Project import
diff --git a/._bluez b/._bluez
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/._bluez
Binary files differ
diff --git a/bluez/.DS_Store b/bluez/.DS_Store
new file mode 100644
index 0000000..e0841c4
--- /dev/null
+++ b/bluez/.DS_Store
Binary files differ
diff --git a/bluez/._.DS_Store b/bluez/._.DS_Store
new file mode 100644
index 0000000..09fa6bd
--- /dev/null
+++ b/bluez/._.DS_Store
Binary files differ
diff --git a/bluez/._Makefile b/bluez/._Makefile
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/._Makefile
Binary files differ
diff --git a/bluez/._repo b/bluez/._repo
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/._repo
Binary files differ
diff --git a/bluez/Makefile b/bluez/Makefile
new file mode 100644
index 0000000..bc6fea3
--- /dev/null
+++ b/bluez/Makefile
@@ -0,0 +1,110 @@
+#
+#    Copyright (c) 2010-2011 Nest, Inc.
+#    All rights reserved.
+#
+#    This document is the property of Nest. It is considered
+#    confidential and proprietary information.
+#
+#    This document may not be reproduced or transmitted in any form,
+#    in whole or in part, without the express written permission of
+#    Nest.
+#
+#    Description:
+#      This file is the makefile for BlueZ, the official Linux
+#      Bluetooth protocol stack.
+#
+
+
+include pre.mak
+
+PackageName		:= bluez
+
+PackageSourceDir	:= repo
+
+PackageBuildMakefile	= $(call GenerateBuildPaths,Makefile)
+
+CleanPaths		+= $(PackageLicenseFile)
+
+SOURCEDIRS                      = $(PackageSourceDir)
+$(PackageSourceDir)_RULE_TARGET = $(BuildDirectory)/configure
+
+all: $(PackageDefaultGoal)
+
+# Generate the package license contents.
+
+$(PackageSourceDir)/LICENSE: $(BuildDirectory)/source
+
+$(PackageLicenseFile): $(PackageSourceDir)/LICENSE
+	$(copy-result)
+
+# Prepare the sources.
+
+.PHONY: source
+source: $(BuildDirectory)/source
+$(BuildDirectory)/source: | $(PackageSourceDir) $(BuildDirectory)
+	$(Verbose)$(call create-links,$(CURDIR)/$(PackageSourceDir),$(BuildDirectory))
+	$(Verbose)touch $@
+
+# Generate the package's build makefile
+
+# Configure the source for building.
+
+.PHONY: configure
+configure: $(BuildDirectory)/configure
+$(BuildDirectory)/configure: $(BuildDirectory)/source | $(PackageSourceDir) $(BuildDirectory)
+	$(Verbose)cd $(BuildDirectory) && ./bootstrap
+	$(Verbose)cd $(BuildDirectory) && ./configure \
+	CC="$(CC) $(CPPOPTFLAGS)" CXX="$(CXX) $(CPPOPTFLAGS)" AR=$(AR) NM=$(NM) RANLIB=$(RANLIB) STRIP=$(STRIP) INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+	CPPFLAGS="$(call ToolGenerateDefineArgument,FIRMWARE_DIR=\\\"/lib/firmware/\\\") \
+	          $(call ToolGenerateIncludeArgument,$(LinuxIncludePath))" \
+	DBUS_CFLAGS="$(call ToolGenerateIncludeArgument,$(DbusIncludePaths))" \
+	GLIB_CFLAGS="$(call ToolGenerateIncludeArgument,$(GlibIncludePaths))" \
+	UDEV_CFLAGS="$(call ToolGenerateIncludeArgument,$(UdevIncludePaths))" \
+	DBUS_LIBS="$(call GenerateLibraryArgument,$(DbusLibraryPath))" \
+	GLIB_LIBS="$(call GenerateLibraryArgument,$(GlibLibraryPath))" \
+	UDEV_LIBS="$(call GenerateLibraryArgument,$(UdevLibraryPath))" \
+	PKG_CONFIG_PATH="$(GlibPkgConfigPath):$(DbusPkgConfigPath):$(UdevPkgConfigPath)" \
+	--build=$(HostTuple) \
+	--host=$(TargetTuple) \
+	--prefix=/usr \
+	--sysconfdir=/etc \
+	--localstatedir=/var \
+	--disable-cups \
+	--disable-monitor \
+	--disable-client \
+	--disable-systemd \
+	--disable-obex \
+	$(if $(BUILD_FEATURE_BLUEZ_LIB), --enable-library) \
+	$(NULL)
+	$(Verbose)touch $@
+
+# Build the source.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+.PHONY: build
+build: $(BuildDirectory)/build
+$(BuildDirectory)/build: $(BuildDirectory)/configure
+	$(Verbose)unset MAKEFLAGS && \
+	$(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+	all
+	$(Verbose)touch $@
+
+# Stage the build to a temporary installation area.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+$(BuildDirectory)/stage: $(BuildDirectory)/build | $(ResultDirectory)
+	$(Verbose)unset MAKEFLAGS && \
+	$(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+	DESTDIR=$(ResultDirectory) \
+	install
+	$(Verbose)touch $@
+
+stage: $(BuildDirectory)/stage
+
+clean:
+	$(Verbose)$(RM) $(RMFLAGS) -r $(BuildDirectory)
+	$(Verbose)$(RM) $(RMFLAGS) -r $(ResultDirectory)
+
+include post.mak
diff --git a/bluez/repo/.DS_Store b/bluez/repo/.DS_Store
new file mode 100644
index 0000000..a0b52f9
--- /dev/null
+++ b/bluez/repo/.DS_Store
Binary files differ
diff --git a/bluez/repo/._.DS_Store b/bluez/repo/._.DS_Store
new file mode 100644
index 0000000..09fa6bd
--- /dev/null
+++ b/bluez/repo/._.DS_Store
Binary files differ
diff --git a/bluez/repo/._.mailmap b/bluez/repo/._.mailmap
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._.mailmap
Binary files differ
diff --git a/bluez/repo/._AUTHORS b/bluez/repo/._AUTHORS
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._AUTHORS
Binary files differ
diff --git a/bluez/repo/._COPYING b/bluez/repo/._COPYING
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._COPYING
Binary files differ
diff --git a/bluez/repo/._COPYING.LIB b/bluez/repo/._COPYING.LIB
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._COPYING.LIB
Binary files differ
diff --git a/bluez/repo/._ChangeLog b/bluez/repo/._ChangeLog
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._ChangeLog
Binary files differ
diff --git a/bluez/repo/._HACKING b/bluez/repo/._HACKING
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._HACKING
Binary files differ
diff --git a/bluez/repo/._INSTALL b/bluez/repo/._INSTALL
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._INSTALL
Binary files differ
diff --git a/bluez/repo/._Makefile.am b/bluez/repo/._Makefile.am
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._Makefile.am
Binary files differ
diff --git a/bluez/repo/._Makefile.obexd b/bluez/repo/._Makefile.obexd
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._Makefile.obexd
Binary files differ
diff --git a/bluez/repo/._Makefile.plugins b/bluez/repo/._Makefile.plugins
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._Makefile.plugins
Binary files differ
diff --git a/bluez/repo/._Makefile.tools b/bluez/repo/._Makefile.tools
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._Makefile.tools
Binary files differ
diff --git a/bluez/repo/._NEWS b/bluez/repo/._NEWS
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._NEWS
Binary files differ
diff --git a/bluez/repo/._README b/bluez/repo/._README
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._README
Binary files differ
diff --git a/bluez/repo/._TODO b/bluez/repo/._TODO
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._TODO
Binary files differ
diff --git a/bluez/repo/._acinclude.m4 b/bluez/repo/._acinclude.m4
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._acinclude.m4
Binary files differ
diff --git a/bluez/repo/._android b/bluez/repo/._android
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._android
Binary files differ
diff --git a/bluez/repo/._attrib b/bluez/repo/._attrib
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._attrib
Binary files differ
diff --git a/bluez/repo/._bootstrap b/bluez/repo/._bootstrap
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._bootstrap
Binary files differ
diff --git a/bluez/repo/._bootstrap-configure b/bluez/repo/._bootstrap-configure
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._bootstrap-configure
Binary files differ
diff --git a/bluez/repo/._btio b/bluez/repo/._btio
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._btio
Binary files differ
diff --git a/bluez/repo/._client b/bluez/repo/._client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._client
Binary files differ
diff --git a/bluez/repo/._configure.ac b/bluez/repo/._configure.ac
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._configure.ac
Binary files differ
diff --git a/bluez/repo/._doc b/bluez/repo/._doc
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._doc
Binary files differ
diff --git a/bluez/repo/._emulator b/bluez/repo/._emulator
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._emulator
Binary files differ
diff --git a/bluez/repo/._gdbus b/bluez/repo/._gdbus
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._gdbus
Binary files differ
diff --git a/bluez/repo/._gobex b/bluez/repo/._gobex
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._gobex
Binary files differ
diff --git a/bluez/repo/._lib b/bluez/repo/._lib
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._lib
Binary files differ
diff --git a/bluez/repo/._monitor b/bluez/repo/._monitor
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._monitor
Binary files differ
diff --git a/bluez/repo/._obexd b/bluez/repo/._obexd
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._obexd
Binary files differ
diff --git a/bluez/repo/._peripheral b/bluez/repo/._peripheral
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._peripheral
Binary files differ
diff --git a/bluez/repo/._plugins b/bluez/repo/._plugins
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._plugins
Binary files differ
diff --git a/bluez/repo/._profiles b/bluez/repo/._profiles
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._profiles
Binary files differ
diff --git a/bluez/repo/._src b/bluez/repo/._src
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._src
Binary files differ
diff --git a/bluez/repo/._test b/bluez/repo/._test
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._test
Binary files differ
diff --git a/bluez/repo/._tools b/bluez/repo/._tools
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._tools
Binary files differ
diff --git a/bluez/repo/._unit b/bluez/repo/._unit
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/._unit
Binary files differ
diff --git a/bluez/repo/.mailmap b/bluez/repo/.mailmap
new file mode 100644
index 0000000..a7e36e3
--- /dev/null
+++ b/bluez/repo/.mailmap
@@ -0,0 +1,11 @@
+Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>	<luiz.dentz-von@nokia.com>
+Vinicius Costa Gomes <vinicius.gomes@openbossa.org>	<vinicius.gomes@openbossa.org>
+Elvis Pfützenreuter <epx@signove.com>			<epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>		<scarot@libresoft.es>
+José Antonio Santos Cadenas <santoscadenas@gmail.com>	<santoscadenas@gmail.com>
+Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>	<Waldemar.Rymarkiewicz@tieto.com>
+Alok Barsode <alokbarsode@gmail.com>			<alok@greatbear.(none)>
+André Dieb Martins <andre.dieb@signove.com>		<andre.dieb@signove.com>
+Tedd Ho-Jeong An <tedd.an@intel.com>			<tedd.an@intel.com>
+Martin Xu <martin.xu@linux.intel.com>			<martin.xu@linux.intel.com>
+Marie Janssen <jamuraa@chromium.org>			<jamuraa@chromium.org>
diff --git a/bluez/repo/AUTHORS b/bluez/repo/AUTHORS
new file mode 100644
index 0000000..16fb14c
--- /dev/null
+++ b/bluez/repo/AUTHORS
@@ -0,0 +1,101 @@
+Maxim Krasnyansky <maxk@qualcomm.com>
+Marcel Holtmann <marcel@holtmann.org>
+Stephen Crane <steve.crane@rococosoft.com>
+Jean Tourrilhes <jt@hpl.hp.com>
+Jan Beutel <j.beutel@ieee.org>
+Ilguiz Latypov <ilatypov@superbt.com>
+Thomas Moser <thomas.moser@tmoser.ch>
+Nils Faerber <nils@kernelconcepts.de>
+Martin Leopold <martin@leopold.dk>
+Wolfgang Heidrich <wolfgang.heidrich@esk.fhg.de>
+Fabrizio Gennari <fabrizio.gennari@philips.com>
+Brad Midgley <bmidgley@xmission.com>
+Henryk Ploetz <henryk@ploetzli.ch>
+Philip Blundell <pb@nexus.co.uk>
+Johan Hedberg <johan.hedberg@intel.com>
+Claudio Takahasi <claudio.takahasi@indt.org.br>
+Eduardo Rocha <eduardo.rocha@indt.org.br>
+Denis Kenzior <denis.kenzior@trolltech.com>
+Frederic Dalleau <frederic.dalleau@access-company.com>
+Frederic Danis <frederic.danis@access-company.com>
+Luiz Augusto von Dentz <luiz.dentz@gmail.com>
+Fabien Chevalier <fabchevalier@free.fr>
+Ohad Ben-Cohen <ohad@bencohen.org>
+Daniel Gollub <dgollub@suse.de>
+Tom Patzig <tpatzig@suse.de>
+Kai Vehmanen <kai.vehmanen@nokia.com>
+Vinicius Gomes <vinicius.gomes@openbossa.org>
+Alok Barsode <alok.barsode@azingo.com>
+Bastien Nocera <hadess@hadess.net>
+Albert Huang <albert@csail.mit.edu>
+Glenn Durfee <gdurfee@google.com>
+David Woodhouse <david.woodhouse@intel.com>
+Christian Hoene <hoene@uni-tuebingen.de>
+Pekka Pessi <pekka.pessi@nokia.com>
+Siarhei Siamashka <siarhei.siamashka@nokia.com>
+Nick Pelly <npelly@google.com>
+Lennart Poettering <lennart@poettering.net>
+Gustavo Padovan <gustavo@padovan.org>
+Marc-Andre Lureau <marc-andre.lureau@nokia.com>
+Bea Lam <bea.lam@nokia.com>
+Zygo Blaxell <zygo.blaxell@xandros.com>
+Forrest Zhao <forrest.zhao@intel.com>
+Scott Talbot <psyc@stalbot.com>
+Ilya Rubtsov <lusyaru@gmail.com>
+Mario Limonciello <mario_limonciello@dell.com>
+Filippo Giunchedi <filippo@esaurito.net>
+Jaikumar Ganesh <jaikumar@google.com>
+Elvis Pfutzenreuter <epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>
+José Antonio Santos Cadenas <jcaden@libresoft.es>
+Francisco Alecrim <francisco.alecrim@openbossa.org>
+Daniel Orstadius <daniel.orstadius@gmail.com>
+Anderson Briglia <anderson.briglia@openbossa.org>
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruna Moreira <bruna.moreira@openbossa.org>
+Brian Gix <bgix@codeaurora.org>
+Andre Guedes <andre.guedes@openbossa.org>
+Sheldon Demario <sheldon.demario@openbossa.org>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Szymon Janc <szymon.janc@codecoup.pl>
+Syam Sidhardhan <s.syam@samsung.com>
+Paulo Alcantara <pcacjr@gmail.com>
+Jefferson Delfes <jefferson.delfes@openbossa.org>
+Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+Eder Ruiz Maria <eder.ruiz@openbossa.org>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Chan-yeol Park <chanyeol.park@samsung.com>
+João Paulo Rechi Vita <jprvita@gmail.com>
+Larry Junior <larry.junior@openbossa.org>
+Raymond Liu <raymond.liu@intel.com>
+Radoslaw Jablonski <ext-jablonski.radoslaw@nokia.com>
+Rafal Michalski <michalski.raf@gmail.com>
+Dmitriy Paliy <dmitriy.paliy@nokia.com>
+Bartosz Szatkowski <bulislaw@linux.com>
+Lukasz Pawlik <lucas.pawlik@gmail.com>
+Slawomir Bochenski <lkslawek@gmail.com>
+Wayne Lee <waynelee@qualcomm.com>
+Ricky Yuen <ryuen@qualcomm.com>
+Takashi Sasai <sasai@sm.sony.co.jp>
+Andre Dieb Martins <andre.dieb@signove.com>
+Cristian Rodríguez <crrodriguez@opensuse.org>
+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>
+Marcin Krąglak <marcin.kraglak@tieto.com>
+Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>
+Jerzy Kasenberg <jerzy.kasenberg@tieto.com>
+Arman Uguray <armansito@chromium.org>
+Artem Rakhov <arakhov@chromium.org>
+Mike Ryan <mikeryan@lacklustre.net>
+David Herrmann <dh.herrmann@gmail.com>
+Jacob Siverskog <jacob@teenageengineering.com>
+Sebastian Chłąd <sebastian.chlad@tieto.com>
+Alex Gal <a.gal@miip.ca>
+Loic Poulain <loic.poulain@intel.com>
+Gowtham Anandha Babu <gowtham.ab@samsung.com>
+Bharat Panda <bharat.panda@samsung.com>
+Marie Janssen <jamuraa@chromium.org>
+Jaganath Kanakkassery <jaganath.k@samsung.com>
diff --git a/bluez/repo/COPYING b/bluez/repo/COPYING
new file mode 100644
index 0000000..6d45519
--- /dev/null
+++ b/bluez/repo/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/bluez/repo/COPYING.LIB b/bluez/repo/COPYING.LIB
new file mode 100644
index 0000000..1f7c8cc
--- /dev/null
+++ b/bluez/repo/COPYING.LIB
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/bluez/repo/ChangeLog b/bluez/repo/ChangeLog
new file mode 100644
index 0000000..c7a4fc0
--- /dev/null
+++ b/bluez/repo/ChangeLog
@@ -0,0 +1,2107 @@
+ver 5.37:
+	Fix issue with registering external profiles.
+	Fix issue with connecting external profiles.
+	Fix issue with GATT service changed handling.
+	Fix issue with not emitting GattServices update.
+	Convert to unified HID over GATT profile support.
+	Convert to KeyboardDisplay as default IO capability.
+	Install btattach utility by default.
+
+ver 5.36:
+	Fix issue with PBAP headers for size query.
+	Fix issue with AVRCP current player handling.
+	Fix issue with device information handling.
+	Fix issue with device disconnect handling.
+	Fix issue with duplicate connect handling.
+	Fix issue with attribute claiming for drivers.
+
+ver 5.35:
+	Fix issue with connected devices after discovery.
+	Fix issue with profile support and LTK loading.
+	Fix issue with AVRCP events for volume control.
+	Fix issue with OBEX session owner handling.
+	Fix issue with HID over GATT setup failures.
+	Fix issue with GATT notification registration.
+	Fix issue with GATT cache validation feature.
+	Add support for persistent GATT database.
+	Add support for controller enabling option.
+
+ver 5.34:
+	Fix issue with GATT profiles and auto-connect.
+	Fix issue with missing GoepL2CapPsm SDP data.
+	Fix issue with suspending AVDTP endpoints.
+	Fix issue with audio service state on disconnect.
+	Add support for AVRCP Set Addressed Player feature.
+	Add support for AVRCP Get Folder Items feature.
+	Add support for Android 5.1 HFP WBS callbacks.
+
+ver 5.33:
+	Fix issue with memory leak in GATT database.
+	Fix issue with AVDTP set configuration handling.
+	Fix issue with AVDTP discover procedure.
+	Fix issue with not emitting Paired property.
+
+ver 5.32:
+	Fix issue with OPP GET request path handling.
+	Fix issue with ATT information request errors.
+	Fix issue with advertising instance numbers.
+	Fix issue with overwriting SDP record cache.
+	Fix issue with new connections during disconnect.
+	Add support for GATT security auto-elevation.
+
+ver 5.31:
+	Fix issue with crash in networking interface.
+	Fix issue with crash when creating endless GATT loops.
+	Fix issue with memory leak when connecting services.
+	Fix issue with memory leak creating new D-Bus proxy.
+	Fix issue with profile connections from remote devices.
+	Fix issue with GATT over BR/EDR and MTU notification.
+	Fix issue with HID and dual mode remote devices.
+	Fix issue with handling A2DP vendor codec setup.
+	Fix issue with AVRCP and syncing player state.
+	Fix issue with GATT secondary discovery handling.
+	Fix issue with wrong characteristic allocation.
+	Add support for handling BNEP setup response.
+	Add support for setting GATT database security flags.
+	Add support for setting discovery filters interface.
+	Add support for user controlled advertising interface.
+	Update Android qualification documentation to PTS 6.1 release.
+
+ver 5.30:
+	Fix compilation error in C++ due to inline function.
+	Fix issue with missing storage of device information.
+	Fix issue with GATT client and gaps in service handles.
+	Fix issue with AVDTP discovery callback crashing.
+	Fix issue with AVCTP channel handling in case of conflicts.
+	Fix issue with AVRCP target and get capabilities command.
+	Add experimental support for LE advertising manager API.
+	Add support for Android 5.1 GATT MTU exchange API.
+
+ver 5.29:
+	Fix issue with AVCTP initial key repeat timeout.
+	Fix issue with Android application disconnect handling.
+	Fix issue with Android support and service notifications.
+	Fix issue with Android support and Exchange MTU Request.
+	Fix issue with Android HFP support and AT+CMER handling.
+	Fix issue with Android HFP support and SLC setup.
+	Fix issue with Android HFP support and call hold status.
+	Fix issue with Android HFP support and indicator handling.
+	Fix issue with Android HFP support and SCO/eSCO disconnection.
+	Fix issue with Android HID over GATT support and battery service.
+	Fix issue with GATT sending Exchange MTU Request for BR/EDR.
+	Fix issue with GATT notification support without CCC.
+	Fix issue with GATT object life-time after disconnects.
+	Fix issue with GATT notification handling API.
+	Add experimental support for GATT client D-Bus API.
+	Add experimental support for GATT server D-Bus API.
+	Add support for Multi Profile Specification.
+	Update Android qualification documentation to PTS 6.0 release.
+
+ver 5.28:
+	Fix issue with GATT device discovery and probing.
+	Fix issue with bearer selection for dual-mode devices.
+	Fix issue with device removal while connected.
+	Fix issue with device name setting from inquiry response.
+	Fix issue with missing termination of name characteristic.
+	Fix issue with UTF-8 length handling for device name.
+	Fix issue with AVCTP key auto release handling.
+	Fix issue with AVCTP key press repetition handling.
+	Fix issue with payload sizes and GATT notifications.
+	Fix issue with memory corruption and GATT notifications.
+	Add support for HID proxy switching and CSR 8510 A10 devices.
+	Add support for Broadcom hex2hcd conversion utility.
+
+ver 5.27:
+	Fix issue with endian handling and management interface.
+	Fix issue with pending GATT operations when disconnecting.
+	Fix issue with 128-bit UUID conversions for HID over GATT.
+	Add support for Android 5.0 SELinux policies.
+
+ver 5.26:
+	Fix issue with handling A2DP XCASE connection state.
+	Fix issue with crash and A2DP configuration failures.
+	Fix issue with crash during OBEX session shutdown.
+	Add support for version 1.2 of Phonebook Access Profile.
+	Add support for HID over GATT get and set report handling.
+	Add support for Low Energy Secure Connections feature.
+	Add support for Bluetooth 4.2 commands and events.
+	Add support for Android 5.0 Bluetooth features.
+
+ver 5.25:
+	Fix issue with SCO connection after codec negotiation.
+	Fix issue with GATT and secondary service discovery.
+	Fix issue with GATT write descriptor callback.
+	Fix issue with MAP supported features bits.
+	Add support for MAP local time and timezone offset.
+	Add support for PBAP speed-dial and favorites folders.
+	Add support for PBAP speed-dial and identifier filters.
+	Add support for controller mode configuration option.
+	Add initial support for Android Lollipop features.
+
+ver 5.24:
+	Fix issue with storing of connection parameters.
+	Add support for Phonebook Access Profile 1.2 features.
+	Add support for Message Access Profile 1.2 event reports.
+	Add support for Android Bluetooth configuration options.
+
+ver 5.23:
+	Fix issue with concurrent authorization requests.
+	Fix issue with HID report identifier mismatch.
+	Fix issue with crash when receiving uHID events.
+	Fix issue with crash and OBEX disconnect handling.
+	Fix issue with OBEX client transfers and suspend.
+	Fix issue with parsing of MAP application parameters.
+	Fix issue with devices rejecting AVRCP GetCapabilities.
+	Add support for kernel whitelist and Android Bluetooth.
+
+ver 5.22:
+	Fix issue with UHID_OUTPUT events mapping.
+	Fix issue with UHID_FEATURE events handling.
+	Fix issue with UINT32_MAX overflow and AVRCP.
+	Fix issue when dirent type DT_UNKNOWN is returned.
+	Add support for kernel whitelist filtering feature.
+	Add support for Android Bluetooth GATT over BR/EDR.
+
+ver 5.21:
+	Fix issue with SDP requests and wrong PDU size.
+	Fix issue with handling passive scanning triggers.
+	Add support for storing and loading connection parameters.
+	Add support for kernel background auto-connection feature.
+	Add support for Android Bluetooth Scan Parameters feature.
+	Add support for Android Bluetooth Device Information feature.
+	Add support for Android Bluetooth Health Device interface.
+
+ver 5.20:
+	Fix issue with LED handling of PS3 controllers.
+	Add support for Android Bluetooth GATT server interface.
+	Add support for Android Bluetooth HID over GATT feature.
+	Add support for Android Bluetooth multi-profile feature.
+	Add support for Android Bluetooth aptX audio integration.
+
+	Note: aptX codec not included
+
+ver 5.19:
+	Fix issue with OBEX Put-Delete and Create-Empty methods.
+	Fix issue with AVRCP browsable/searchable player properties.
+	Fix issue with handling multiple default agents.
+	Fix issue with handling unpair event per bearer.
+	Fix issue with HID over GATT report ID presence.
+	Add support for HID protocol handling in userspace.
+	Add support for Bluetooth reconnection policy framework.
+	Add support for Android Bluetooth SCO over HCI transport.
+	Add support for Android Bluetooth audio quality control.
+	Add support for Android Bluetooth Low Energy only mode.
+
+ver 5.18:
+	Fix issue with identifying LE single mode devices.
+	Fix issue with L2CAP and RFCOMM peer address lookup.
+	Add support for handling OBEX authentication procedure.
+	Add support for Android Bluetooth GATT client interface.
+
+ver 5.17:
+	Fix issue with not resetting OBEX SRM setup.
+	Fix issue with BR/EDR devices and auto-connect list.
+	Fix issue with bonding complete detection as peripheral.
+	Fix issue with not updating bearer timestamp of connections.
+	Fix issue with paired property for multiple bearers.
+	Add support for Android Bluetooth Handsfree interface.
+	Add support for Android Bluetooth Wideband speech.
+
+ver 5.16:
+	Fix issue with HID over GATT physical location.
+	Fix issue with HID over GATT unique identifier.
+	Fix issue with missing paired property notification.
+	Fix issue with endianess of long term key storage.
+	Add support for storing signature resolving keys.
+	Add support for Android Bluetooth AVRCP interface.
+
+ver 5.15:
+	Fix issue with LE enabling and background scanning.
+	Fix issue with HID over GATT input device name.
+	Fix issue with storage of slave long term keys.
+	Add support for handling identity resolving keys.
+	Add support for Android Bluetooth A2DP interface.
+	Add support for Android Bluetooth audio interface.
+
+ver 5.14:
+	Fix issue with marking PS3 controllers as trusted.
+	Fix issue with authorization of PS3 controllers.
+	Add support for DualShock 4 controller detection.
+	Add support for legacy pairing emulation.
+	Add support for secure simple pairing emulation.
+	Add support for automated pairing testing.
+	Add support for RFCOMM protocol testing.
+	Add support for HCI controller testing.
+
+ver 5.13:
+	Fix issue with PS3 controller detection.
+	Add support for data transfers to L2CAP testing tool.
+	Add support for delay reporting to AVDTP testing tool.
+	Add support for Android Bluetooth Core interface.
+	Add support for Android Bluetooth Socket interface.
+	Add support for Android Bluetooth HID Host interface.
+	Add support for Android Bluetooth PAN interface.
+
+ver 5.12:
+	Fix issue with missing reply to DisconnectProfile.
+	Fix issue with icon property and class of device changes.
+	Fix issue with HID devices when SDP record is not available.
+	Fix issue with handling auto-pairing of printers.
+	Fix issue with agent authorization handling.
+	Add support for PS3 controller setup and pairing.
+	Add support for LE L2CAP CoC test capabilities.
+	Add support for AVDTP qualification test cases.
+	Add support for SMP cryptographic test cases.
+
+ver 5.11:
+	Fix issue with connection attempt when not powered.
+	Fix issue with assigning player to AVRCP target role.
+	Fix issue with OBEX default cache directory.
+	Fix issue with SDP search error handling.
+	Fix issue with processing of SDP records.
+	Fix issue with HID to HCI switching utility.
+	Fix issue with mgmt end-to-end testing tool.
+	Fix issue with L2CAP end-to-end testing tool.
+	Add support for SMP end-to-end testing tool.
+	Add support for more Wii controllers.
+
+ver 5.10:
+	Fix issue with discoverable timeout handling.
+	Fix issue with MAP messages and record version.
+	Fix issue with MAP messages and status events.
+	Fix issue with MAP messages and relative folders.
+	Fix issue with MAP messages and type property signals.
+	Fix issue with transfer size for OBEX GET operations.
+	Fix issue with AVRCP service class identifier.
+	Fix issue with AVRCP tracking seeked signal.
+	Add support for OBEX command line client.
+
+ver 5.9:
+	Fix issue with network service and adapter removal.
+	Fix issue with misleading OBEX error messages.
+	Fix issue with OBEX transport reference handling.
+	Fix issue with memory leak with MAP event handler.
+	Fix issue with missing MAP property changed signal.
+	Fix issue with message type property values.
+	Fix issue with empty UUID list for devices.
+	Fix issue with profile agent cancel method.
+	Remove dependency on USB library.
+
+ver 5.8:
+	Fix issue with missing OBEX session properties.
+	Fix issue with missing SDP service refresh.
+	Fix issue with SDP attribute range check.
+	Fix issue with priority for SDP transactions.
+	Fix issue with service discovery after pairing.
+	Fix issue with race condition in service list.
+	Fix issue with input service state transition.
+	Fix issue with default authorization for profiles.
+	Fix issue with AVRCP browsing channel connections.
+	Add support for AVRCP role agnostic sessions.
+
+ver 5.7:
+	Fix issue with missing UUID discovery during pairing.
+	Fix issue with broken patch for SDP range check handling.
+	Fix issue with AVRCP usage of UID=0 for paused/stopped.
+	Add support MAP notification dispatching.
+
+ver 5.6:
+	Fix issue with incoming connections without SDP record.
+	Fix issue with canceling ongoing device connections.
+	Fix issue with handling failed connection attempts.
+	Fix issue with pending resume during A2DP open failures.
+	Fix issue with registering AVRCP unsupported notification.
+	Fix issue with listing available AVRCP target settings.
+	Fix issue with missing error for OBEX SetPath commands.
+	Fix issue with missing OBEX session command queue.
+	Fix issue with retrieving multiple MAP event reports.
+	Add support for command line player utility.
+
+ver 5.5:
+	Fix issue with race condition between SDP and properties.
+	Fix issue with handling storage of private device addresses.
+	Fix issue with NFC out-of-band pairing and power states.
+	Fix issue with short name during device update handling.
+	Fix issue with handling AVRCP without A2DP being present.
+	Add support for handling AVRCP pass-through operations.
+	Add support for automatically reconnecting HID devices.
+	Add support for automatically pairing of devices.
+
+ver 5.4:
+	Fix issue with invalid memory access and SDP service search.
+	Add support for available player changed event for controller.
+	Add support for UIDs changed event for AVRCP controller.
+	Add support for mandatory AVRCP pass-through operations.
+	Add support for Message Notification Service (MNS) server.
+	Add support for agent methods within command line client.
+
+ver 5.3:
+	Fix issue with registering invalid profiles.
+	Fix issue with inconsistent A2DP transport state.
+	Fix issue with A2DP resume while in configured state.
+	Fix issue with buffer overflow when processing SDP response.
+	Fix issue with missing range check for SDP attribute response.
+	Fix issue with missing validation of SDP data elements.
+	Fix issue with missing fallback to static hostname.
+	Fix issue with default adapter assignment.
+
+ver 5.2:
+	Fix issue with connection handling for Low Energy.
+	Fix issue with broken device discovery handling.
+	Fix issue with invalid memory access within A2DP.
+	Fix issue with handling empty path name of SetPath.
+	Fix issue with handling Message Access Profile filters.
+	Fix issue with handling network service unregistration.
+	Fix issue with not handling bogus device pairing results.
+	Fix issue with initial service discovery and profile manager.
+	Add support for AVRCP volume notifications.
+	Add support for AVRCP browsing commands.
+
+ver 5.1:
+	Fix issue with crash when removing OBEX session.
+	Fix issue with HID device disconnected from kernel.
+	Fix issue with buffer overflow when parsing HID SDP record.
+	Fix issue with SDP_TEXT_STR16 and SDP_URL_STR16 parsing.
+	Add support for integration with systemd's hostname daemon.
+	Add support for separate adapter alias property.
+	Add support for adapter and device modalias properties.
+	Add support for official BlueZ device information.
+	Add support for asynchronous management interface handling.
+	Add tool for testing management interface compliance.
+	Add tool for testing SDP qualification requirements.
+	Add tool for testing various EIR and AD data records.
+
+ver 5.0:
+	Introduce D-Bus Properties and ObjectManager interfaces.
+	Add support for generic profile interface.
+	Add support for global agent interface.
+	Add support for integrated OBEX daemon.
+	Add support for integrated hcidump utility.
+	Add support for Bluetooth tracing and monitor utility.
+	Add support for Bluetooth command line client utility.
+	Remove support for Handsfree gateway handling.
+	Remove support for GStreamer A2DP and SBC elements.
+	Disable default installation of Bluetooth library.
+
+ver 4.101:
+	Fix issue with missing BlueZ service file.
+	Fix issue with aborting A2DP setup during AVDTP start.
+	Fix issue with handling of multiple A2DP indication.
+	Fix issue with handling AVDTP abort with invalid SEID.
+	Fix issue with rejecting AVDTP abort commands.
+	Add support for handling AVDTP command collision.
+
+ver 4.100:
+	Fix issue with crashing when SCO connection fails.
+	Fix issue with HFP gateway failing on first GSM connection.
+	Fix issue with AVRCP and handling of vendor commands.
+	Fix issue with handling AVRCP subunit info command.
+	Fix issue with missing capability for AVRCP track reached end.
+	Fix issue with AVDTP signaling and GStreamer SBC NULL check.
+	Fix issue with AVDTP Reconfigure Reject message.
+	Fix issue with incorrect EIR length parsing.
+	Fix issue with SDP disconnect for HIDSDPDisable.
+	Fix issue with SDP interoperability with Mac OS X Lion.
+	Fix issue with reverse SDP discovery with some devices.
+	Fix issue with discovering state during power off operation.
+	Add support for AVRCP Volume Changed notifications.
+	Add support for AVRCP Set Absolute Volume handling.
+	Add support for display legacy PIN code agent method.
+	Add support for multiple media transports per endpoint.
+	Add support for discovering device information characteristics.
+	Add support for vendor source for Device ID setting.
+	Add support for immediate alert server.
+	Add support for link loss server.
+
+	Notes:
+	This version requires D-Bus 1.4 or later.
+	This version requires GLib 2.28 or later.
+
+ver 4.99:
+	Fix issue with missing retries for BNEP connection setup.
+	Fix issue with not showing name if first EIR has no details.
+	Fix issue with running SDP discovery for LE devices.
+	Add support for GATT using 128-bit Bluetooth UUIDs.
+	Add support for retrieving key size information.
+	Add support for storing Long Term Keys.
+	Add support for Proximity Reporter API.
+	Add support for KeyboardDisplay IO capability.
+	Add support for version 1.0 of management API.
+	Add support for monitoring interface.
+
+ver 4.98:
+	Fix issue with adapter list upon initialization failure.
+	Fix issue with missing legacy property for Low Energy.
+	Fix issue with missing EIR information handling.
+	Fix issue with device address type tracking.
+	Fix issue with alert level characteristic.
+	Fix issue with headset shutdown handling.
+	Fix issue with Wiimote address handling.
+	Add support for advanced l2test options.
+	Add support for attribute protocol and multiple adapters.
+
+ver 4.97:
+	Update support for proximity profile.
+	Fix issue with SBC audio decoding quality.
+	Fix multiple issues with HFP support.
+	Fix multiple issues with A2DP support.
+	Fix multiple issues with AVDTP support.
+	Fix multiple issues with AVRCP support.
+	Add support for AVRCP meta-data transfer.
+	Add support for Bluetooth based thermometers.
+
+ver 4.96:
+	Fix issue with race condition in AVDTP stream start.
+	Fix issue with global adapter offline switching.
+	Fix issue with pairing and No Bonding devices.
+	Add support for Nintendo Wii Remote pairing.
+
+ver 4.95:
+	Fix issue with AVCTP replies with invalid PID.
+	Fix issue with AVRCP and unknown packet types.
+	Fix issue with AVRCP not using NOT_IMPLEMENTED correctly.
+	Fix issue with AVDTP discovery if all endpoints are in use.
+	Fix issue with invalid memory writes and media support.
+	Fix issue with not removing device alias and unbonding.
+	Fix issue with device disconnects and offline mode handling.
+	Add support for setting adapter name based on machine-info.
+	Add support for systemd service configuration.
+
+ver 4.94:
+	Fix issue with invalid read of memory in various modules.
+	Fix issue with buffer overflow when sending AVDTP commands.
+	Fix issue with response to vendor dependent AVRCP commands.
+	Fix issue with headset when not able to reply with ERROR.
+	Fix issue with crash when creating a device from storage.
+	Fix issue with handling non UTF-8 devices names.
+	Add support for improved discovery procedure.
+
+ver 4.93:
+	Fix issue with property type and Health Main channel.
+	Fix issue with crash when removing devices.
+	Add support for hid2hci and udev integration.
+
+ver 4.92:
+	Fix issue with handling of A2DP suspend response.
+	Fix issue with crashing when acquiring A2DP stream.
+	Fix issue with missing check for valid SCO before shutdown.
+	Fix issue with waiting for POLLERR when disconnecting SCO.
+	Fix issue with disconnect after primary service discovery.
+	Fix issue with attribute interface registration.
+	Add support for primary services over BR/EDR.
+	Add support for flushable packets of A2DP media.
+
+ver 4.91:
+	Fix issue with LMP version string and hciconfig.
+	Fix issue with missing discovery signal when scanning.
+	Fix issue with wrong state and canceling name resolving.
+	Fix issue with missing check during adapter initialization.
+	Fix issue with missing protocol not supported error and A2DP.
+	Fix issue with crash during driver unregistering and A2DP.
+	Fix issue with crash when receiving AVDTP close command.
+	Fix issue with remote SEP handling when A2DP codec changes.
+	Fix issue with SCO hangup handling and state changes.
+	Fix issue with security level and MCAP instances.
+	Fix issue with memory leak and HDP data channels.
+	Add support for discover characteristics by UUID to gatttool.
+	Add initial support for Out-of-Band association model.
+	Add initial support for SIM Access Profile.
+
+ver 4.90:
+	Fix issue with setting of global mode property.
+	Fix issue with handling of RequestSession responses.
+	Fix issue with TP_BNEP_CTRL_BV_01_C qualification test.
+	Fix issue with too short AVDTP request timeout.
+	Add support for SIM Access Profile manager.
+	Add support for new UUID utility functions.
+	Add support for attribute server notifications.
+	Add support for client characteristic configuration.
+	Update support for interactive GATT utility.
+
+ver 4.89:
+	Fix issue with name resolving when discovery is suspended.
+	Fix issue with parsing flags of advertising report.
+	Fix issue with SEP handling if interface is disabled.
+	Fix issue with device object creation on disconnect event.
+	Fix issue with indicators whenever the driver is initialized.
+	Fix issue with call indicator when parsing call info reply.
+	Fix issue with crash and allowed GATT MTU was too large.
+	Add support for SDP record of Primary GATT services.
+	Add support for interactive mode for GATT utility.
+
+ver 4.88:
+	Fix issue with HID channel reference count handling.
+	Fix issue with daemon exit on badly formatted AT+VTS.
+	Fix issue with crash while parsing of endpoint properties.
+	Fix issue with possible crash on AVDTP Suspend request timeout.
+	Fix issue with stopping inquiry before adapter is initialized.
+	Fix issue with creating device object when connection fails.
+	Fix issue with sending HCIDEVUP when adapter is already up.
+	Fix issue with handling bonding IO channel closing.
+	Fix agent cancellation in security mode 3 situations.
+	Update pairing code to support management interface.
+
+ver 4.87:
+	Fix issue with initialization when adapter is already up.
+	Fix issue with attribute server MTU and incoming connections.
+	Fix issue with duplicate characteristics after discovery.
+
+ver 4.86:
+	Revert wrong fix for SDP PDU size error response.
+	Fix various memory leaks in A2DP and AVDTP support.
+	Add Routing property to MediaTransport interface
+	Add proper tracking mechanism to NREC status.
+	Add READ_BLOB_REQUEST support to attribute server.
+
+ver 4.85:
+	Fix issue with event mask setting for older adapters.
+	Fix issue with device creation and pairing failures.
+	Add support for telephony support via oFono.
+	Add support for characteristic security level.
+	Update support for service registration.
+
+ver 4.84:
+	Fix issue with wrong parameters and device found signals.
+	Fix issue with leaking EIR data if RSSI does not change.
+	Fix issue with adapter initialization state.
+	Fix issue with closing of SDP server sockets.
+
+ver 4.83:
+	Fix issue with already connected HFP/HSP endpoints.
+	Fix missing reply when create device is canceled.
+	Fix memory leak within the attribute server.
+	Fix memory leak with unused extended inquiry name.
+	Fix setting paired state when device->authr is false.
+	Fix clearing authentication request for renewed keys.
+	Add support for storing link keys in runtime memory.
+	Update support for primary service discovery.
+
+ver 4.82:
+	Fix crash with mmap of files with multiples of page size.
+	Fix HFP response and hold (AT+BTRH) command response.
+	Fix device creation error response when powered off.
+	Fix device removal when connecting/browsing fails.
+	Add initial attribute permission implementation.
+	Add AVDTP SRC stream send buffer size verification.
+	Add support for setting link policy based on features.
+
+ver 4.81:
+	Fix issue with telephony driver initialization.
+	Fix issue with adapter services list initialization.
+	Fix crash after simultaneous authentication requests.
+	Add support for primary service search on device creation.
+
+ver 4.80:
+	Fix legacy link key storing for some buggy adapters.
+	Fix invalid memory access when EIR field length is zero.
+	Fix adapter initialization to wait for kernel HCI commands.
+	Fix initialization of adapters which are already up.
+	Fix possible race condition when initializing adapters.
+	Fix possible crashes when attempting to connect AVDTP.
+	Fix not aborting sink stream configuration on disconnect.
+	Fix not indicating disconnected state when connecting to AVDTP.
+	Fix not dropping AVDTP session when canceling stream setup.
+	Fix AVDTP abort not being send when the state is idle.
+	Fix regression with Low Energy and interleave discovery.
+	Add a new configuration option to disable Low Energy support.
+	Add iwmmxt optimization for SBC for ARM PXA series CPUs.
+	Update support for GATT Primary Service Discovery.
+	Update MCAP and HDP support.
+
+ver 4.79:
+	Fix issue with adapter initialization race condition.
+	Update new Bluetooth Management interface support.
+
+ver 4.78:
+	Fix various issues with AVDTP timer handling.
+	Fix various issues with handling of mode changes.
+	Fix issue with audio disconnect watch in connecting state.
+	Fix issue with handling call waiting indicators in telephony.
+	Fix issue with handling UUID parameter and RegisterEndpoint.
+	Add initial support for Bluetooth Management interface.
+	Add support for Application property to HealthChannel.
+
+ver 4.77:
+	Fix issue with device name and accessing already freed memory.
+	Fix issue with handling CHLD=0 command for handsfree.
+	Fix issue with manager properties and no adapters.
+	Fix issue with properties and broken service records.
+	Fix issue with A2DP playback and sample rate changes.
+	Update MCAP and HDP support.
+
+ver 4.76:
+	Fix issue in telephony driver with hanging up held call.
+	Fix issue in telephony driver with notifications when on hold.
+	Fix issue with blocking on setconf confirmation callback.
+	Fix issue with not always signaling new streams as sinks.
+	Fix issue with errors in case of endpoint request timeout.
+	Fix issue with HFP/HSP microphone and speaker gain values.
+	Add source if the device attempt to configure local sink stream.
+	Add PSM option for GATT/ATT over BR/EDR on gatttool.
+	Add support for GATT/ATT Attribute Write Request.
+	Update MCAP and HDP support.
+
+ver 4.75:
+	Fix use of uninitialized variable on legacy pairing.
+	Fix mismatch of attribute protocol opcode.
+
+ver 4.74:
+	Fix regression for Legacy Pairing.
+	Fix wrong PSM value for attribute protocol.
+	Fix issue with RSSI field in advertising reports.
+	Add support for Add BR/EDR and LE interleaved discovery.
+	Add support for GATT write characteristic value option.
+	Add support for specifying download address for AR300x.
+
+ver 4.73:
+	Fix problem with EIR data when setting the name.
+	Fix reading local name from command complete event.
+	Fix registering local endpoints with disabled socket interface.
+	Add support for more HCI operations using ops infrastructure.
+	Add support for GATT characteristic hierarchy.
+	Add support for GATT indications.
+
+ver 4.72:
+	Fix memory leak while connecting BTIO channels.
+	Fix crash with GStreamer plugin if SBC is not supported.
+	Fix issue with GATT server stop sending notifications.
+	Fix issue with GATT and dealing with the minimum MTU size.
+	Fix issue with file descriptor leak in GATT client.
+	Add support for UUID 128-bit handling in attribute client.
+	Add support for encoders/decoders for MTU Exchange.
+	Add support for the MTU Exchange procedure to the server.
+	Add support for a per channel MTU to the ATT server.
+	Add support for Characteristic interface.
+	Add support for new Media API and framework.
+	Add initial support for HDP plugin.
+
+ver 4.71:
+	Fix compilation when SBC support in not enabled.
+	Fix crash with RequestSession and application disconnects.
+	Fix memory leak and possible crash when removing audio device.
+	Fix issue with closing stream of locked sep when reconfiguring.
+	Fix issue where discovery could interfere with bonding.
+	Fix issue with Connected status when PS3 BD remote connects.
+	Fix issue with lifetime of fake input devices.
+	Add support for compile time option of oui.txt path.
+	Add support for printing IEEE1284 device ID for CUPS.
+	Add plugin for setting adapter class via DMI.
+	Add more features for attribute protocol and profile.
+	Add initial support for MCAP.
+
+ver 4.70:
+	Fix incoming call indication handling when in WAITING state.
+	Fix various SDP related qualification test case issues.
+	Fix logic to write EIR when SDP records are changed.
+	Fix UTF-8 validity check for remote names in EIR.
+	Add support for UUID-128 extended inquiry response.
+	Add service UUIDs from EIR to the DeviceFound signal.
+	Add fast connectable feature for Handsfree profile.
+	Add HCI command and event definitions for AMP support.
+	Add firmware download support for Qualcommh devices.
+	Add host level support for Atheros AR300x device.
+	Add initial support of ATT and GATT for basic rate.
+
+ver 4.69:
+	Fix issue with calling g_option_context_free() twice.
+	Fix inconsistencies with initial LE commands and events.
+	Add support for telephony ClearLastNumber method.
+	Add support for network server interface.
+
+ver 4.68:
+	Fix initialization of adapters in RAW mode.
+	Fix signal strength for HFP in Maemo's telephony support.
+	Add support for following the radio state via Maemo's MCE.
+	Add initial set of LE commands and events definitions.
+	Add mode option for L2CAP sockets to the BtIO API.
+
+ver 4.67:
+	Fix issue with authentication reply when bonding already completed.
+	Fix issue with not canceling authentication when bonding fails.
+	Fix issue with changed combination keys and temporary storage.
+	Fix issue with sdp_get_supp_feat library function.
+	Fix issue with missing unblock on device removal.
+	Fix issue with not waiting for mode change completion.
+	Add ARMv6 optimized version of analysis filter for SBC encoder.
+
+ver 4.66:
+	Fix regression with full debug enabling via SIGUSR2.
+	Fix redundant speaker/microphone gains being sent.
+	Fix not emitting PropertyChanged for SpeakerGain/MicrophoneGain.
+	Fix issue with storage usage when a record is not found in memory.
+	Fix issue with DiscoverServices not retrieving any records.
+	Fix audio profile disconnection order to match whitepaper.
+	Fix auto-accept confirmation when local agent has NoInputNoOutput.
+	Fix remote just-works SSP when MITM protection is required.
+	Fix performing dedicated bonding without MITM requirement.
+	Add support for storing debug link keys in runtime memory.
+
+ver 4.65:
+	Fix issues with general bonding being default setting now.
+	Fix driver removal upon device removal.
+	Add new "Blocked" property to device objects.
+	Add hciconfig support for blacklisting.
+	Add support for dynamic debug feature.
+
+ver 4.64:
+	Fix invalid memory access in headset_get_nrec function.
+	Fix issue with disconnect event on higher protocol layers.
+	Fix issue with list parsing in sdp_set_supp_features function.
+	Fix device object reference counting for SDP browse requests.
+	Add missing memory checks whenever memory is allocated for SDP.
+	Add support for exporting local services via D-Bus.
+	Add more L2CAP Enhanced Retransmission test options.
+
+ver 4.63:
+	Fix avdtp_abort not canceling pending requests.
+	Fix stale connection when abort gets rejected.
+
+ver 4.62:
+	Fix accidental symbol breakage with inquiry transmit power.
+	Fix using invalid data from previous headset connection.
+	Fix double free on AVDTP Abort response.
+	Fix possible crash while verifying AVDTP version.
+	Fix missing inuse flag when AVDTP stream is configured.
+	Add support for Bluetooth controller types.
+
+ver 4.61:
+	Fix issues with Read Inquiry Response Transmit Power Level.
+	Fix possible invalid read when removing a temporary device.
+	Fix mode restoration when remember_powered is false.
+	Fix conference call releasing in telephony-maemo.
+	Fix segmentation fault with authorization during headset disconnects.
+	Add support for handling unanswered AVDTP request on disconnect.
+	Add support for handling Inquiry Response Transmit Power Level.
+	Add support for caching of remote host features.
+	Add preliminary voice dialing support for HSP.
+
+ver 4.60:
+	Fix voice mailbox number reading from SIM.
+	Fix some races with D-Bus mainloop integration.
+	Add helpers for D-Bus signal watches.
+
+ver 4.59:
+	Add values for Bluetooth 4.0 specification.
+	Add SDP functions for HDP support.
+	Add test scripts for input and audio.
+	Fix missing close on BtIO create_io function.
+	Fix sending incorrect AVDTP commands after timeout occurs.
+	Fix timer removal when device disconnects unexpectedly.
+	Fix Extended Inquiry Response record for Device ID.
+
+ver 4.58:
+	Fix crash when adapter agent exists during authentication.
+	Fix CK-20W quirks for play and pause events.
+
+ver 4.57:
+	Fix unloading of drivers for uninitialized adapters.
+	Fix debug message to use requested and not opened SEID.
+	Fix codec selection for GStreamer plugin.
+	Fix deleting of SDP records during service updates.
+	Fix deleting of SDP records when a device is removed.
+	Fix handling when the SDP record is modified on remote device.
+	Fix potential buffer overflow by using snprintf instead of sprintf.
+	Fix const declarations for some storage function parameters.
+
+ver 4.56:
+	Add missing values from Bluetooth 3.0 specification.
+	Add proper tracking of device paired status.
+	Fix tracking of devices without permanently stored link key.
+	Fix issue with link key removal after connection failures.
+	Fix legacy pairing information based on remote host features.
+	Fix off-by-one issue with AVDTP capability parsing.
+	Fix AVRCP, AVCTP, AVDTP, A2DP and HFP version numbers.
+	Fix agent canceling before calling agent_destroy.
+	Fix service record parsing with an empty UUID list.
+	Fix various SDP related memory leaks.
+
+ver 4.55:
+	Add support for POSIX capabilities dropping.
+	Add special quirk for the Nokia CK-20W car kit.
+	Fix error code handling for AVDTP SetConfiguration response.
+	Fix updating out of range list when RSSI hasn't changed.
+	Fix various memory leaks and unnecessary error checks.
+
+ver 4.54:
+	Add introspection interface to output of introspection calls.
+	Fix stream handling when media transport disconnects prematurely.
+	Fix command timeout handling when there's no stream.
+	Fix headset_suspend_stream behavior for invalid states
+	Fix issue with AVDTP ABORTING state transition.
+	Fix issue with AVDTP suspend while closing.
+
+ver 4.53:
+	Fix issue with telephony connection state notifications.
+	Fix AVDTP stream leak for invalid media transport config.
+	Fix audio connection authorization handling with timeouts.
+	Fix race condition in authorizing audio connections.
+	Fix device authorized setting for AVRCP-only connections.
+	Fix duplicate attempts from device to connect signal channel.
+
+ver 4.52:
+	Add AVCTP support to test utility.
+	Fix AVDTP Abort when transport closes before response.
+	Fix authorization when the audio profiles are slow to connect.
+	Fix potential AVDTP reference leaks.
+
+ver 4.51:
+	Add utility for basic AVDTP testing.
+	Add support for configuring L2CAP FCS option.
+	Fix discovery mode for CUPS 1.4.x and later.
+	Fix global state tracking of audio service.
+	Fix last issues with the new build system.
+
+ver 4.50:
+	Fix issue with missing manual pages in distribution.
+	Fix issue with the configuration and state directories.
+	Fix issue with creating include directory.
+	Fix dependencies of include file generation.
+
+ver 4.49:
+	Add simple test program for basic GAP testing.
+	Add support for confirmation requests to agent example.
+	Add support for full non-recursive build.
+	Add five millisecond delay for Simple Pairing auto-accept.
+	Fix Class of Device setting when InitiallyPowered=false.
+
+ver 4.48:
+	Add library function for comparing UUID values.
+	Add support for creating all plugins as builtins.
+	Add support for async handling of service class changes.
+	Add support for source interface to audio IPC.
+	Fix device name settings when device is off or down.
+	Fix issue with enabled SCO server when not necessary.
+	Fix missing D-Bus access policy for CUPS backend.
+	Fix discovery results of CUPS backend.
+	Fix initialization handling of Maemo telephony.
+
+ver 4.47:
+	Add support for RFKILL unblock handling.
+	Add support for serial proxy configurations.
+	Add support for caching service class updates.
+	Fix issues with updating SDP service records.
+	Fix usage of limited discoverable mode.
+	Remove deprecated methods and signals for AudioSource.
+
+ver 4.46:
+	Add support for A2DP sink role.
+	Fix clearing svc_cache before the adapter is up.
+	Fix various pointer after free usages.
+	Fix various memory leaks.
+
+ver 4.45:
+	Fix UDEV_DATADIR fallback if pkg-config fails.
+	Fix adapter cleanup and setup prototypes.
+	Fix double-free with out-of-range devices.
+	Fix inband ring setting to be per-headset.
+	Fix handling of Maemo CSD startup.
+
+ver 4.44:
+	Add some missing manual pages.
+	Fix missing number prefix when installing udev rules.
+	Fix program prefix used in Bluetooth udev rules.
+	Fix three-way calling indicator order.
+	Fix downgrade/upgrade of callheld indicator.
+	Fix +CIEV sending when indicator value changes.
+	Fix signal handling for Maemo telephony driver.
+	Fix parsing issues with messages from Maemo CSD.
+	Fix issue with duplicate active calls.
+
+ver 4.43:
+	Add support for udev based on-demand startup.
+	Fix verbose error reporting of CUPS backend.
+	Fix various string length issues.
+	Fix issues with Maemo telephony driver.
+	Fix another device setup and temporary flag issue.
+	Fix and update example agent implementation.
+
+ver 4.42:
+	Add TI WL1271 to Texas Instruments chip list.
+	Add special udev mode to bluetoothd.
+	Fix regression when there is no agent registered.
+	Fix error return when bonding socket hang up.
+	Fix SCO server socket for HFP handsfree role.
+	Fix shutdown on SCO socket before closing.
+	Fix shutdown on A2DP audio stream channel before closing.
+	Fix issue with asserting on AVDTP reference count bugs.
+	Fix authorization denied issue with certain headsets.
+	Fix AVRCP UNITINFO and SUBUNIT INFO responses.
+	Fix discovery cancel issues in case SDP discovery fails.
+
+ver 4.41:
+	Fix pairing even if the ACL gets dropped before successful SDP.
+	Fix regression which caused device to be removed after pairing.
+	Fix HSP record fetching when remote device doesn't support it.
+	Fix SDP discovery canceling when clearing hs->pending.
+	Fix headset never connecting on the first attempt.
+	Fix headset state tracking if bt_search_service() fails.
+	Fix maximum headset connection count check.
+	Fix AVDTP Discover timeout handling.
+	Fix also UI_SET_KEYBIT for the new pause and play key codes.
+
+ver 4.40:
+	Add telephony driver for oFono telephony stack.
+	Add support for Dell specific HID proxy switching.
+	Add support for running hid2hci from udev.
+	Add mapping for AVRCP Play and Pause to dedicated key codes.
+	Fix AVRCP keycodes to better match existing X keymap support.
+	Fix various quoting issues within telephony support.
+	Fix memory allocation issue when generating PDUs for SDP.
+	Fix race condition on device removal.
+	Fix non-cancelable issue with CreateDevice method.
+	Fix non-working CancelDiscovery method call.
+
+ver 4.39:
+	Add workaround for dealing with unknown inquiry complete.
+	Fix discovering when using software scheduler.
+	Fix wrong NoInputNoOutput IO capability string.
+	Fix race condition with agent during pairing.
+	Fix agent cancellation for security mode 3 acceptor failure.
+	Fix temporary flag removal when device creation fails.
+	Fix hciattach to use ppoll instead of poll.
+	Fix service class update when adapter is down.
+	Fix service classes race condition during startup.
+	Fix release of audio client before freeing the device.
+
+ver 4.38:
+	Add support for builtin plugins.
+	Add framework for adapter operations.
+	Add constants for Enhanced Retransmission modes.
+	Fix HCI socket leak in device_remove_bonding.
+	Fix various format string issues.
+	Fix crashes with various free functions.
+	Fix issues with Headset and A2DP drivers to load again.
+	Fix sending AVRCP button released passthrough messages
+	Fix bug which prevent input devices to work after restart.
+	Fix issue with interpretation of UUID-128 as channel.
+
+ver 4.37:
+	Add version value for Bluetooth 3.0 devices.
+	Add additional L2CAP extended feature mask bits.
+	Add support for loading plugins in priority order.
+	Add support for more detailed usage of disconnect watches.
+	Add support for AVRCP volume control.
+	Add saturated clipping of SBC decoder output to 16-bit.
+	Fix potentially infinite recursion of adapter_up.
+	Fix SCO handling in the case of an incoming call.
+	Fix input service to use confirm callback.
+	Fix cleanup of temporary device entries from storage.
+
+ver 4.36:
+	Add proper tracking of AVCTP connect attempts.
+	Add support to channel pattern in Serial interface.
+	Fix A2DP sink crash if removing device while connecting.
+	Fix error handling if HFP indicators aren't initialized.
+	Fix segfault while handling an incoming SCO connection.
+	Fix Serial.Disconnect to abort connection attempt.
+
+ver 4.35:
+	Add support for Handsfree profile headset role.
+	Add additional checks for open SEIDs from clients.
+	Fix device removal while audio IPC client is connected.
+	Fix device removal when an authorization request is pending.
+	Fix incoming AVDTP connect while authorization in progress.
+	Fix disconnection timers for audio support.
+	Fix various potential NULL pointer deferences.
+	Fix callheld indicator value for multiple calls.
+	Fix voice number type usage.
+	Fix GDBus watch handling.
+
+ver 4.34:
+	Add support for version checks of plugins.
+	Add support for class property on adapter interface.
+	Add support for second SDP attempt after connection reset.
+	Add support for more detailed audio states.
+	Add support for HFP+A2DP auto connection feature.
+	Add support for new and improved audio IPC.
+	Add program for testing audio IPC interface.
+	Fix various AVDTP qualification related issues.
+	Fix broken SDP AttributeIdList parsing.
+	Fix invalid memory access of SDP URL handling.
+	Fix local class of device race conditions.
+	Fix issue with periodic inquiry on startup.
+	Fix missing temporary devices in some situations.
+	Fix SBC alignment issue for encoding with four subbands.
+
+ver 4.33:
+	Add Paired property to the DeviceFound signals.
+	Add support for Headset profile 1.2 version.
+	Fix broken network configuration when IPv6 is disabled.
+	Fix network regression that caused disconnection.
+	Fix SDP truncation of strings with NULL values.
+	Fix service discovery handling of CUPS helper.
+
+ver 4.32:
+	Fix broken SDP record handling.
+	Fix SDP data buffer parsing.
+	Fix more SDP memory leaks.
+	Fix read scan enable calls.
+	Fix A2DP stream handling.
+
+ver 4.31:
+	Add support for new BtIO helper library.
+	Fix AVDTP session close issue.
+	Fix SDP memory leaks.
+	Fix various uninitialized memory issues.
+	Fix duplicate signal emissions.
+	Fix property changes request handling.
+	Fix class of device storage handling.
+
+ver 4.30:
+	Add CID field to L2CAP socket address structure.
+	Fix reset of authentication requirements after bonding.
+	Fix storing of link keys when using dedicated bonding.
+	Fix storing of pre-Bluetooth 2.1 link keys.
+	Fix resetting trust settings on every reboot.
+	Fix handling of local name changes.
+	Fix memory leaks in hciconfig and hcitool
+
+ver 4.29:
+	Use AVRCP version 1.0 for now.
+	Decrease AVDTP idle timeout to one second.
+	Delay AVRCP connection when remote device connects A2DP.
+	Add workaround for AVDTP stream setup with broken headsets.
+	Add missing three-way calling feature bit for Handsfree.
+	Fix handsfree callheld indicator updating.
+	Fix parsing of all AT commands within the buffer.
+	Fix authentication replies when disconnected.
+	Fix handling of debug combination keys.
+	Fix handling of changed combination keys.
+	Fix handling of link keys when using no bonding.
+	Fix handling of invalid/unknown authentication requirements.
+	Fix closing of L2CAP raw socket used for dedicated bonding.
+
+ver 4.28:
+	Add AVDTP signal fragmentation support.
+	Add more SBC performance optimizations.
+	Add more SBC audio quality improvements.
+	Use native byte order for audio plugins.
+	Set the adapter alias only after checking the EIR data.
+	Fix auto-disconnect issue with explicit A2DP connections.
+	Fix invalid memory access of ALSA plugin.
+	Fix compilation with -Wsign-compare.
+
+ver 4.27:
+	Add more SBC optimization (MMX and ARM NEON).
+	Add BT_SECURITY and BT_DEFER_SETUP definitions.
+	Add support for deferred connection setup.
+	Add support for fragmentation of data packets.
+	Add option to trigger dedicated bonding.
+	Follow MITM requirements from remote device.
+	Require MITM for dedicated bonding if capabilities allow it.
+	Fix IO capabilities for non-pairing and pairing cases.
+	Fix no-bonding connections in non-bondable mode.
+	Fix new pairing detection with SSP.
+	Fix bonding with pre-2.1 devices and newer kernels.
+	Fix LIAC setting while toggling Pairable property.
+	Fix device creation for incoming security mode 3 connects.
+	Fix crash within A2DP with bogus pointer.
+	Fix issue with sdp_copy_record() function.
+	Fix crash with extract_des() if sdp_uuid_extract() fails.
+
+ver 4.26:
+	Use of constant shift in SBC quantization code.
+	Add possibility to analyze 4 blocks at once in encoder.
+	Fix correct handling of frame sizes in the encoder.
+	Fix for big endian problems in SBC codec.
+	Fix audio client socket to always be non-blocking.
+	Update telephony support for Maemo.
+
+ver 4.25:
+	Fix receiving data over the audio control socket.
+	Fix subbands selection for joint-stereo in SBC encoder.
+	Add new SBC analysis filter function.
+
+ver 4.24:
+	Fix signal emissions when removing adapters.
+	Fix missing adapter signals on exit.
+	Add support for bringing adapters down on exit.
+	Add support for RememberPowered option.
+	Add support for verbose compiler warnings.
+	Add more options to SBC encoder.
+
+ver 4.23:
+	Update audio IPC for better codec handling.
+	Fix bitstream optimization for SBC encoder.
+	Fix length header values of IPC messages.
+	Fix multiple coding style violations.
+	Fix FindDevice to handle temporary devices.
+	Add configuration option for DeviceID.
+	Add support for InitiallyPowered option.
+	Add missing signals for manager properties.
+	Add telephony support for Maemo.
+
+ver 4.22:
+	Add deny statements to D-Bus access policy.
+	Add support for LegacyPairing property.
+	Add support for global properties.
+	Add more commands to telephony testing script.
+	Add sender checks for serial and network interfaces.
+	Remove deprecated methods and signals from input interface.
+	Remove deprecated methods and signals from network interface.
+	Remove OffMode option and always use device down.
+
+ver 4.21:
+	Fix adapter initialization logic.
+	Fix adapter setup and start security manager early.
+	Fix usage issue with first_init variable.
+
+ver 4.20:
+	Cleanup session handling.
+	Cleanup mode setting handling.
+	Fix issue with concurrent audio clients.
+	Fix issue with HFP/HSP suspending.
+	Fix AT result code syntax handling.
+	Add Handsfree support for AT+NREC.
+	Add PairableTimeout adapter property.
+
+ver 4.19:
+	Fix installation of manual pages for old daemons.
+	Fix D-Bus signal emmissions for CreateDevice.
+	Fix issues with UUID probing.
+	Fix +BSRF syntax issue.
+	Add Pairable adapter property.
+	Add sdp_copy_record() library function.
+
+ver 4.18:
+	Fix release before close issue with RFCOMM TTYs.
+	Fix Connected property on input interface.
+	Fix DeviceFound signals during initial name resolving.
+	Fix service discovery handling.
+	Fix duplicate UUID detection.
+	Fix SBC gain mismatch and decoding handling.
+	Add more options to SBC encoder and decoder.
+	Add special any adapter object for service interface.
+	Add variable prefix to adapter and device object paths.
+
+ver 4.17:
+	Fix SBC encoder not writing last frame.
+	Fix missing timer for A2DP suspend.
+	Add more supported devices to hid2hci utility.
+	Add additional functionality to Handsfree support.
+
+ver 4.16:
+	Fix wrong parameter usage of watch callbacks.
+	Fix parameters for callback upon path removal.
+	Fix unloading of adapter drivers.
+
+ver 4.15:
+	Fix various A2DP state machine issues.
+	Fix some issues with the Handsfree error reporting.
+	Fix format string warnings with recent GCC versions.
+	Remove dependency on GModule.
+
+ver 4.14:
+	Fix types of property arrays.
+	Fix potential crash with input devices.
+	Fix PS3 BD remote input event generation.
+	Allow dynamic adapter driver registration.
+	Update udev rules.
+
+ver 4.13:
+	Fix service discovery and UUID handling.
+	Fix bonding issues with Simple Pairing.
+	Fix file descriptor misuse of SCO connections.
+	Fix various memory leaks in the device handling.
+	Fix AVCTP disconnect handling.
+	Fix GStreamer modes for MP3 encoding.
+	Add operator selection to Handsfree support.
+
+ver 4.12:
+	Fix crash with missing icon value.
+	Fix error checks of HAL plugin.
+	Fix SCO server socket cleanup on exit.
+	Fix memory leaks from DBusPendingCall.
+	Fix handling of pending authorization requests.
+	Fix missing protocol UUIDs in record pattern.
+
+ver 4.11:
+	Change SCO server socket into a generic one.
+	Add test script for dummy telephony plugin.
+	Fix uninitialized reply of multiple GetProperties methods.
+
+ver 4.10:
+	Fix memory leaks with HAL messages.
+	Add more advanced handsfree features.
+	Add properties to audio, input and network interfaces.
+	Stop device discovery timer on device removal.
+
+ver 4.9:
+	Fix signals for Powered and Discoverable properties.
+	Fix handling of Alias and Icon properties.
+	Fix duplicate entries for service UUIDs.
+
+ver 4.8:
+	Fix retrieving of formfactor value.
+	Fix retrieving of local and remote extended features.
+	Fix potential NULL pointer dereference during pairing.
+	Fix crash with browsing due to a remotely initated pairing.
+
+ver 4.7:
+	Fix pairing and service discovery logic.
+	Fix crashes during suspend and resume.
+	Fix race condition within devdown mode.
+	Add RequestSession and ReleaseSession methods.
+	Add Powered and Discoverable properties.
+	Add Devices property and deprecate ListDevices.
+	Add workaround for a broken carkit from Nokia.
+
+ver 4.6:
+	Fix Device ID record handling.
+	Fix service browsing and storage.
+	Fix authentication and encryption for input devices.
+	Fix adapter name initialization.
+
+ver 4.5:
+	Fix initialization issue with new adapters.
+	Send HID authentication request without blocking.
+	Hide the verbose SDP debug behind SDP_DEBUG.
+	Add extra UUIDs for service discovery.
+	Add SCO server socket listener.
+	Add authorization support to service plugin.
+
+ver 4.4:
+	Add temporary fix for the CUPS compile issue.
+	Add service-api.txt to distribution.
+	Mention the variable prefix of an object path
+
+ver 4.3:
+	Add dummy driver for telephony support.
+	Add support for discovery sessions.
+	Add service plugin for external services.
+	Various cleanups.
+
+ver 4.2:
+	Avoid memory copies in A2DP write routine.
+	Fix broken logic with Simple Pairing check and old kernels.
+	Allow non-bondable and outgoing SDP without agent.
+	Only remove the bonding for non-temporary devices.
+	Cleanup various unnecessary includes.
+	Make more unexported functions static.
+	Add basic infrastructure for gtk-doc support.
+
+ver 4.1:
+	Add 30 seconds timeout to BNEP connection setup phase.
+	Avoid memory copies in A2DP write routine for ALSA.
+	Make sure to include compat/sdp.h in the distribution.
+
+ver 4.0:
+	Initial public release.
+
+ver 3.36:
+	Add init routines for TI BRF chips.
+	Add extra attributes to the serial port record.
+	Add example record for headset audio gateway record.
+	Use Handsfree version 0x0105 for the gateway role.
+	Fix SDP record registration with specific record handles.
+	Fix BCSP sent/receive handling.
+	Fix various includes for cross-compilation.
+	Allow link mode settings for outgoing connections.
+	Allow bonding during periodic inquiry.
+
+ver 3.35:
+	Add two additional company identifiers.
+	Add UUID-128 support for service discovery.
+	Fix usage of friendly names for service discovery.
+	Fix authorization when experiemental is disabled.
+	Fix uninitialized variable in passkey request handling.
+	Enable output of timestamps for l2test and rctest.
+
+ver 3.34:
+	Replace various SDP functions with safe versions.
+	Add additional length validation for incoming SDP packets.
+	Use safe function versions for SDP client handling.
+	Fix issue with RemoveDevice during discovery procedure.
+	Fix collect for non-persistent service records.
+
+ver 3.33:
+	Add functions for reading and writing the link policy settings.
+	Add definition for authentication requirements.
+	Add support for handling Simple Pairing.
+	Add Simple Pairing support to Agent interface.
+	Add ReleaseMode method to Adapter interface.
+	Add DiscoverServices method to Device interface.
+	Remove obsolete code and cleanup the repository.
+	Move over to use the libgdbus API.
+	Enable PIE by default if supported.
+
+ver 3.32:
+	Add OCF constants for synchronous flow control enabling.
+	Add support for switching HID proxy devices from Dell.
+	Add more Bluetooth client/server helper functions.
+	Add support for input service idle timeout option.
+	Fix BNEP reconnection handling.
+	Fix return value for snd_pcm_hw_params() calls.
+	Use upper-case addresses for object paths.
+	Remove HAL support helpers.
+	Remove inotify support.
+	Remove service daemon activation handling.
+	Remove uneeded D-Bus API extension.
+
+ver 3.31:
+	Create device object for all pairing cases.
+	Convert authorization to internal function calls.
+	Add initial support for Headset Audio Gateway role.
+	Add generic Bluetooth helper functions for GLib.
+	Fix endiannes handling of connection handles.
+	Don't optimize when debug is enabled.
+
+ver 3.30:
+	Convert audio service into a plugin.
+	Convert input service into a plugin.
+	Convert serial service into a plugin.
+	Convert network service into a plugin.
+	Emit old device signals when a property is changed.
+	Fix missing DiscoverDevices and CancelDiscovery methods.
+	Add another company identifier.
+	Add basic support for Bluetooth sessions.
+	Add avinfo utility for AVDTP/A2DP classification.
+	Remove build option for deprecated sdpd binary.
+
+ver 3.29:
+	Introduce new D-Bus based API.
+	Add more SBC optimizations.
+	Add support for PS3 remote devices.
+	Fix alignment trap in SDP server.
+	Fix memory leak in sdp_get_uuidseq_attr function.
+
+ver 3.28:
+	Add support for MCAP UUIDs.
+	Add support for role switch for audio service.
+	Add disconnect timer for audio service.
+	Add disconnect detection to ALSA plugin.
+	Add more SBC optimizations.
+	Fix alignment issue of SDP server.
+	Remove support for SDP parsing via expat.
+
+ver 3.27:
+	Update uinput.h with extra key definitions.
+	Add support for input connect/disconnect callbacks.
+	Add ifdefs around some baud rate definitions.
+	Add another company identifier.
+	Add proper HFP service level connection handling.
+	Add basic headset automatic disconnect support.
+	Add support for new SBC API.
+	Fix SBC decoder noise at high bitpools.
+	Use 32-bit multipliers for further SBC optimization.
+	Check for RFCOMM connection state in SCO connect callback.
+	Make use of parameters selected in ALSA plugin.
+
+ver 3.26:
+	Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX.
+	Improve handling of different audio transports.
+	Enable services by default and keep old daemons disabled.
+
+ver 3.25:
+	Add limited support for Handsfree profile.
+	Add limited support for MPEG12/MP3 codec.
+	Add basic support for UNITINFO and SUBUNITINFO.
+	Add more SBC optimizations.
+	Fix external service (un)registration.
+	Allow GetInfo and GetAddress to fail.
+
+ver 3.24:
+	Add definitions for MDP.
+	Add TCP connection support for serial proxy.
+	Add fix for Logitech HID proxy switching.
+	Add missing macros, MIN, MAX, ABS and CLAMP.
+	Add more SBC encoder optimizations.
+	Add initial mechanism to handle headset commands.
+	Fix connecting to handsfree profile headsets.
+	Use proper function for checking signal name.
+
+ver 3.23:
+	Fix remote name request handling bug.
+	Fix key search function to honor the mmap area size.
+	Fix Avahi integration of network service.
+	Add new plugin communication for audio service.
+	Enable basic AVRCP support by default.
+	More optimizations to the SBC library.
+	Create common error definitions.
+
+ver 3.22:
+	Add missing include file from audio service.
+	Add SBC conformance test utility.
+	Add basic uinput support for AVRCP.
+	Fix L2CAP socket leak in audio service.
+	Fix buffer usage in GStreamer plugin.
+	Fix remote name request event handling.
+
+ver 3.21:
+	Add constant for Bluetooth socket options level.
+	Add initial AVRCP support.
+	Add A2DP sink support to GStreamer plugin.
+	Fix interoperability with A2DP suspend.
+	Fix sign error in 8-subband encoder.
+	Fix handling of service classes length size.
+	Store Extended Inquiry Response data information.
+	Publish device id information through EIR.
+	Support higher baud rates for Ericcson based chips.
+
+ver 3.20:
+	Fix GStreamer plugin file type detection.
+	Fix potential infinite loop in inotify support.
+	Fix D-Bus signatures for dict handling.
+	Fix issues with service activation.
+	Fix SDP failure handling of audio service.
+	Fix various memory leaks in input service.
+	Add secure device creation method to input service.
+	Add service information methods to serial service.
+	Add config file support to network service.
+	Add scripting capability to network service.
+	Add special on-mode handling.
+	Add optimization for SBC encoder.
+	Add tweaks for D-Bus 1.1.x libraries.
+	Add support for inquiry transmit power level.
+
+ver 3.19:
+	Limit range of bitpool announced while in ACP side.
+	Use poll instead of usleep to wait for worker thread.
+	Use default event mask from the specification.
+	Add L2CAP mode constants.
+	Add HID proxy support for Logitech diNovo Edge dongle.
+	Add refresh option to re-request device names.
+	Show correct connection link type.
+
+ver 3.18:
+	Don't allocate memory for the Bluetooth base UUID.
+	Implement proper locking for headsets.
+	Fix various A2DP SEP locking issues.
+	Fix and cleanup audio stream handling.
+	Fix stream starting if suspend request is pending.
+	Fix A2DP and AVDTP endianess problems.
+	Add network timeout and retransmission support.
+	Add more detailed decoding of EIR elements.
+
+ver 3.17:
+	Fix supported commands bit calculation.
+	Fix crashes in audio and network services.
+	Check PAN source and destination roles.
+	Only export the needed symbols for the plugins.
+
+ver 3.16:
+	Update company identifier list.
+	Add support for headsets with SCO audio over HCI.
+	Add support for auto-create through ALSA plugin.
+	Add support for ALSA plugin parameters.
+	Add GStreamer plugin with SBC decoder and encoder.
+	Fix network service NAP, GN and PANU servers.
+	Set EIR information from SDP database.
+
+ver 3.15:
+	Add A2DP support to the audio service.
+	Add proxy support to the serial service.
+	Extract main service class for later use.
+	Set service classes value from SDP database.
+
+ver 3.14:
+	Add missing signals for the adapter interface.
+	Add definitions and functions for Simple Pairing.
+	Add basic commands for Simple Pairing.
+	Add correct Simple Pairing and EIR interaction.
+	Add missing properties for remote information.
+	Add EPoX endian quirk to the input service.
+	Fix HID descriptor import and storage functions.
+	Fix handling of adapters in raw mode.
+	Fix remote device listing methods.
+
+ver 3.13:
+	Fix some issues with the headset support.
+	Fix concurrent pending connection attempts.
+	Fix usage of devname instead of netdev.
+	Add identifier for Nokia SyncML records.
+	Add command for reading the CSR chip revision.
+	Add generic CSR radio test support.
+	Update HCI command table.
+
+ver 3.12:
+	Add missing HCI command text descriptions
+	Add missing HCI commands structures.
+	Add missing HCI event structures.
+	Add common bachk() function.
+	Add support for limited discovery mode.
+	Add support for setting of event mask.
+	Add GetRemoteServiceIdentifiers method.
+	Add skeleton for local D-Bus server.
+	Add headset gain control methods.
+	Fix various headset implementation issues.
+	Fix various serial port service issues.
+	Fix various input service issues.
+	Let CUPS plugin discover printers in range.
+	Improve the BCM2035 UART init routine.
+	Ignore connection events for non-ACL links.
+
+ver 3.11:
+	Update API documentation.
+	Minimize SDP root records and browse groups.
+	Use same decoder for text and URL strings.
+	Fix URL data size handling.
+	Fix SDP pattern extraction for XML.
+	Fix network connection persistent state.
+	Add network connection helper methods.
+	Add initial version of serial port support.
+	Add class of device tracking.
+
+ver 3.10.1:
+	Add option to disable installation of manual pages.
+	Fix input service encryption setup.
+	Fix serial service methods.
+	Fix network service connection handling.
+	Provide a simple init script.
+
+ver 3.10:
+	Add initial version of network service.
+	Add initial version of serial service.
+	Add initial version of input service.
+	Add initial version of audio service.
+	Add authorization framework.
+	Add integer based SBC library.
+	Add version code for Bluetooth 2.1 specification.
+	Add ESCO_LINK connection type constant.
+	Export sdp_uuid32_to_uuid128() function.
+
+ver 3.9:
+	Add RemoteDeviceDisconnectRequested signal.
+	Add updated service framework.
+	Add embedded GLib library.
+	Add support for using system GLib library.
+	Create internal SDP server library.
+
+ver 3.8:
+	Sort discovered devices list based on their RSSI.
+	Send DiscoverableTimeoutChanged signal.
+	Fix local and remote name validity checking.
+	Add ListRemoteDevices and ListRecentRemoteDevices methods.
+	Add basic integration of confirmation concept.
+	Add support for service record description via XML.
+	Add support for external commands to the RFCOMM utility.
+	Add experimental service and authorization API.
+	Add functions for registering binary records.
+
+ver 3.7:
+	Fix class of device handling.
+	Fix error replies with pairing and security mode 3.
+	Fix disconnect method for RFCOMM connections.
+	Add match pattern for service searches.
+	Add support for prioritized watches.
+	Add additional PDU length checks.
+	Fix CSRC value for partial responses.
+
+ver 3.6.1:
+	Fix IO channel race conditions.
+	Fix pairing issues on big endian systems.
+	Fix pairing issues with page timeout errors.
+	Fix pairing state for security mode 3 requests.
+	Switch to user as default security manager mode.
+
+ver 3.6:
+	Update D-Bus based RFCOMM interface support.
+	Use L2CAP raw sockets for HCI connection creation.
+	Add periodic discovery support to the D-Bus interface.
+	Add initial support for device names via EIR.
+	Add proper UTF-8 validation of device names.
+	Add support for the J-Three keyboard.
+	Fix issues with the asynchronous API for SDP.
+
+ver 3.5:
+	Fix and cleanup watch functionality.
+	Add support for periodic inquiry mode.
+	Add support for asynchronous SDP requests.
+	Add more request owner tracking.
+	Add asynchronous API for SDP.
+	Document pageto and discovto options.
+
+ver 3.4:
+	Improve error reporting for failed HCI commands.
+	Improve handling of CancelBonding.
+	Fixed bonding reply message when disconnected.
+	Fix UUID128 string lookup handling.
+	Fix malloc() versus bt_malloc() usage.
+
+ver 3.3:
+	Don't change inquiry mode for Bluetooth 1.1 adapters.
+	Add udev rules for Bluetooth serial PCMCIA cards.
+	Add Cancel and Release methods for passkey agents.
+	Add GetRemoteClass method.
+	Convert to using ppoll() and pselect().
+	Initialize allocated memory to zero.
+	Remove bcm203x firmware loader.
+	Remove kernel specific timeouts.
+	Add additional private data field for SDP sessions.
+	Add host controller to host flow control defines.
+	Add host number of completed packets defines.
+	Initialize various memory to zero before usage.
+
+ver 3.2:
+	Only check for the low-level D-Bus library.
+	Update possible device minor classes.
+	Fix timeout for pending reply.
+	Add more Inquiry with RSSI quirks.
+	Sleep only 100 msecs for device detection.
+	Don't send BondingCreated on link key renewal.
+	Allow storing of all UTF-8 remote device names.
+	Create storage filenames with a generic function.
+	Fix handling of SDP strings.
+	Add adapter type for SDIO cards.
+	Add features bit for link supervision timeout.
+
+ver 3.1:
+	Add missing placeholders for feature bits.
+	Fix handling of raw mode devices.
+	Fix busy loop in UUID extraction routine.
+	Remove inquiry mode setting.
+	Remove auth and encrypt settings.
+
+ver 3.0:
+	Implement the new BlueZ D-Bus API.
+	Fix broken behavior with EVT_CMD_STATUS.
+	Add features bit for pause encryption.
+	Add additional EIR error code.
+	Add more company identifiers.
+	Add another Phonebook Access identifier.
+	Update sniff subrating data structures.
+
+ver 2.25:
+	Use %jx instead of %llx for uint64_t and int64_t.
+	Allow null-terminated text strings.
+	Add UUID for N-Gage games.
+	Add UUID for Apple Macintosh Attributes.
+	Add Apple attributes and iSync records.
+	Add definitions for Apple Agent.
+	Add support for the Handsfree Audio Gateway service.
+	Add support for choosing a specific record handle.
+	Add support for dialup/telephone connections.
+	Add definitions for Apple Agent.
+	Add support for record handle on service registration.
+
+ver 2.24:
+	Fix display of SDP text and data strings.
+	Add support for device scan property.
+	Add support for additional access protocols.
+	Update the D-Bus policy configuration file.
+
+ver 2.23:
+	Update the new D-Bus interface.
+	Make dfutool ready for big endian architectures.
+	Add support for AVRCP specific service records.
+	Add support for writing complex BCCMD commands.
+	Add the new BCCMD interface utility.
+	Add MicroBCSP implementation from CSR.
+	Add constants and definitions for sniff subrating.
+	Add support for allocation of binary text elements.
+	Add HCI emulation tool.
+	Add fake HID support for old EPoX presenters.
+	Reject connections from unknown HID devices.
+	Fix service discovery deadlocks with Samsung D600 phones.
+
+ver 2.22:
+	Remove D-Bus 0.23 support.
+	Add initial version of the new D-Bus interface.
+	Add support for extended inquiry response commands.
+	Add support for the Logitech diNovo Media Desktop Laser.
+	Add compile time buffer checks (FORTIFY SOURCE).
+	Decode reserved LMP feature bits.
+	Fix errno overwrite problems.
+	Fix profile descriptor problem with Samsung phones.
+
+ver 2.21:
+	Move create_dirs() and create_file() into the textfile library.
+	Let textfile_put() also replace the last key value pair.
+	Fix memory leaks with textfile_get() usage.
+	Fix infinite loops and false positive matches.
+	Don't retrieve stored link keys for RAW devices.
+	Document the putkey and delkey commands.
+	Show supported commands also in clear text.
+	Support volatile changes of the BD_ADDR for CSR chips.
+	Add support for identification of supported commands.
+	Add missing OCF declarations for the security filter.
+	Add two new company identifiers.
+
+ver 2.20:
+	Add UUIDs for video distribution profile.
+	Add UUIDs for phonebook access profile.
+	Add attribute identifier for supported repositories.
+	Add definitions for extended inquiry response.
+	Add functions for extended inquiry response.
+	Add support for extended inquiry response.
+	Add support for HotSync service record.
+	Add support for ActiveSync service record.
+	Add ActiveSync networking support.
+	Fix D-Bus crashes with new API versions.
+
+ver 2.19:
+	Fix the GCC 4.0 warnings.
+	Fix the routing for dealing with raw devices.
+	Fix off by one memory allocation error.
+	Fix security problem with escape characters in device name.
+	Add per device service record functions.
+	Send D-Bus signals for inquiry results and remote name resolves.
+	Add support for device specific SDP records.
+
+ver 2.18:
+	Support D-Bus 0.23 and 0.33 API versions.
+	Support reading of complex BCCMD values.
+	Support minimum and maximum encryption key length.
+	Add support for reading and writing the inquiry scan type.
+	Add definitions for connection accept timeout and scan enable.
+	Add support for inquiry scan type.
+	Add tool for the CSR BCCMD interface.
+	Add first draft of the Audio/Video control utility.
+	Add disconnect timer support for the A2DP ALSA plugin.
+	Make SBC parameters configurable.
+	Replace non-printable characters in device names.
+	Remove hci_vhci.h header file.
+	Remove hci_uart.h header file.
+
+ver 2.17:
+	Set the storage directory through ${localstatedir}.
+	Add the textfile library for ASCII based file access.
+	Add support for return link keys event.
+	Add support for voice setting configuration.
+	Add support for page scan timeout configuration.
+	Add support for storing and deleting of stored link keys.
+	Add support for searching for services with UUID-128.
+	Add support for retrieving all possible service records.
+	Add support for a raw mode view of service records.
+	Add support for HID information caching in hidd.
+	Add support for authentication in pand and dund.
+	Add support for changing BD_ADDR of CSR chips.
+	Add pskey utility for changing CSR persistent storage values.
+	Add the firmware upgrade utility.
+	Add connection caching for the A2DP ALSA plugin.
+	Add functions for stored link keys.
+	Add definitions for PIN type and unit key.
+	Add SDP_WAIT_ON_CLOSE flag for sdp_connect().
+	Include stdio.h in bluetooth.h header file.
+	Include sys/socket.h in the header files.
+
+ver 2.16:
+	Store link keys in ASCII based file format.
+	Support device name caching.
+	Support zero length data sizes in l2test.
+	Change default l2ping data size to 44 bytes.
+	Hide the server record and the public browse group root.
+	Read BD_ADDR if not set and if it is a raw device.
+	Add SDP language attributes.
+	Add support for browsing the L2CAP group.
+	Add support for stored pin codes for outgoing connections.
+	Add support for local commands and extended features.
+	Add support for reading CSR panic and fault codes.
+	Add config option for setting the inquiry mode.
+	Add OUI decoding support.
+	Use unlimited inquiry responses as default.
+	Use cached device names for PIN request.
+	Use the clock offset when getting the remote names.
+	Add function for reading local supported commands.
+	Add function for reading local extended features.
+	Add function for reading remote extended features.
+	Add function for getting the remote name with a clock offset.
+	Add function for extracting the OUI from a BD_ADDR.
+	Add inquiry info structure with RSSI and page scan mode.
+	Fix buffer allocation for features to string conversion.
+	Support inquiry with unlimited number of responses.
+
+ver 2.15:
+	Enable the RFCOMM service level security.
+	Add deprecated functions for reading the name.
+	Add command for reading the clock offset.
+	Add command for reading the clock.
+	Add function for reading the clock.
+	Add function for reading the local Bluetooth address.
+	Add function for reading the local supported features.
+	Don't configure raw devices.
+	Don't set inquiry scan or page scan on raw devices.
+	Don't show extended information for raw devices.
+	Support L2CAP signal sizes bigger than 2048 bytes.
+	Cleanup of the socket handling code of the test programs.
+	Use better way for unaligned access.
+	Remove sdp_internal.h and its usage.
+
+ver 2.14:
+	Make use of additional connection information.
+	Use library function for reading the RSSI.
+	Use library function for reading the link quality.
+	Use library function for reading the transmit power level.
+	Use library functions for the link supervision timeout.
+	Add tool for changing the device address.
+	Add function for reading the RSSI.
+	Add function for reading the link quality.
+	Add function for reading the transmit power level.
+	Add functions for the link supervision timeout.
+	Remove deprecated functions.
+	Update AM_PATH_BLUEZ macro.
+
+ver 2.13:
+	Use file permission 0600 for the link key file.
+	Add support for HID attribute descriptions.
+	Add support for Device ID attributes.
+	Add Device ID and HID attribute definitions.
+	Update the UUID constants and its translations.
+	Update L2CAP socket option definitions.
+	Update connection information definitions.
+	Various whitespace cleanups.
+
+ver 2.12:
+	Inherit the device specific options from the default.
+	Use --device for selecting the source device.
+	Add --nosdp option for devices with resource limitation.
+	Add support and parameter option for secure mode.
+	Add a lot of build ids and hardware revisions.
+	Add service classes and profile ids for WAP.
+	Add simple AM_PATH_BLUEZ macro.
+	Update UUID translation tables.
+	Correct kernel interface for CMTP and HIDP support.
+
+ver 2.11:
+	Initial support for the kernel security manager.
+	Various cleanups to avoid inclusion of kernel headers.
+	Fix output when the CUPS backend is called without arguments.
+	Fix problems with a 64 bit userland.
+	Use Bluetooth library functions if available.
+	Use standard numbering scheme of SDP record handles.
+	Use bit zero for vendor packets in the filter type bitmask.
+	Add SIM Access types for service discovery.
+	Add more audio/video profile translations.
+	Add another company identifier.
+	Add the missing HCI error codes.
+	Add RFCOMM socket options.
+	Add definition for the SECURE link mode.
+	Add functions for reading and writing the inquiry mode.
+	Add functions for AFH related settings and information.
+	Add version identifier for the Bluetooth 2.0 specification.
+	Add a master option to the hidd.
+	Add support for changing the link key of a connection.
+	Add support for requesting encryption on keyboards.
+	Add support for revision information of Digianswer devices.
+	Add support for the Zoom, IBM and TDK PCMCIA cards.
+	Add checks for the OpenOBEX and the ALSA libraries.
+	Add experimental mRouter support.
+
+ver 2.10:
+	Use a define for the configuration directory.
+	Fix string initialization for flags translation.
+	Fix and extend the unaligned access macros.
+	Make compiling with debug information optional.
+	Don't override CFLAGS from configure.
+	Check for usb_get_busses() and usb_interrupt_read().
+	Add optional support for compiling with PIE.
+	Make installation of the init scripts optional.
+	Make compiling with debug information optional.
+	Don't override CFLAGS from configure.
+
+ver 2.9:
+	Retry SDP connect if busy in the CUPS backend.
+	Use packet type and allow role switch in hcitool.
+	Use the functions from the USB library for hid2hci.
+	Add Broadcom firmware loader.
+	Add EPoX endian quirk for buggy keyboards.
+	Add L2CAP info type and info result definitions.
+	Add value for L2CAP_CONF_RFC_MODE.
+	Change RSSI value to signed instead of unsigned.
+	Allow UUID32 values as protocol identifiers.
+	Update the autoconf/automake scripts.
+
+ver 2.8:
+	Use LIBS and LDADD instead of LDFLAGS.
+	Use HIDP subclass field for HID boot protocol.
+	Set olen before calling getsockopt() in pand.
+	Restore signals for dev-up script.
+	Add PID file support for pand.
+	Add size parameter to expand_name() in hcid.
+	Add support for audio source and audio sink SDP records.
+	Add support for HID virtual cable unplug.
+	Add support for AmbiCom BT2000C card.
+	Add defines and UUID's for audio/video profiles.
+	Add AVDTP protocol identifier.
+	Add HIDP subclass field.
+	Add PKGConfig support.
+	Fix the event code of inquiry with RSSI.
+	Remove dummy SDP library.
+
+ver 2.7:
+	Fix display of decoded LMP features.
+	Update company identifiers.
+	Add AFH related types.
+	Add first bits from EDR prototyping specification.
+	Add support for inquiry with RSSI.
+	Add HCRP related SDP functions.
+	Add HIDP header file.
+	Add support for getting the AFH channel map.
+	Add support for AFH mode.
+	Add support for inquiry mode.
+	Add Bluetooth backend for CUPS.
+	Add the hid2hci utility.
+	Add the hidd utility.
+	Add the pand utility.
+	Add the dund utility.
+	More endian bug fixes.
+	Give udev some time to create the RFCOMM device nodes.
+	Release the TTY if no device node is found.
+	New startup script for the Bluetooth subsystem.
+	Update to the autoconf stuff.
+
+ver 2.6:
+	Change default prefix to /usr.
+	Add manpages for hcid and hcid.conf.
+	Add the sdpd server daemon.
+	Add the sdptool utility.
+	Add the ciptool utility.
+	Add new company identifiers.
+	Add BNEP and CMTP header files.
+	Add the SDP library.
+	Use R2 for default value of pscan_rep_mode.
+
+ver 2.5:
+	Add decoding of Bluetooth 1.2 features.
+	Add link manager version parameter for Bluetooth 1.2.
+	Add new company identifiers.
+	Add D-Bus support for PIN request.
+	Support for transmit power level.
+	Support for park, sniff and hold mode.
+	Support for role switch.
+	Support for reading the clock offset.
+	Support for requesting authentication.
+	Support for setting connection encryption.
+	Show revision information for Broadcom devices.
+	Replace unprintable characters in device name.
+	Use R1 for default value of pscan_rep_mode.
+	Fix some 64-bit problems.
+	Fix some endian problems.
+	Report an error on PIN helper failure.
+	Update bluepin script for GTK2.
+
+ver 2.4:
+	Increase number of inquiry responses.
+	Support for transmit power level.
+	Display all 8 bytes of the features.
+	Add support for reading and writing of IAC.
+	Correct decoding class of device.
+	Use Ericsson revision command for ST Microelectronics devices.
+	Display AVM firmware version with 'revision' command.
+	New code for CSR specific revision information.
+	Support for ST Microelectronics specific initialization.
+	Support for 3Com card version 3.0.
+	Support for TDK, IBM and Socket cards.
+	Support for initial baud rate.
+	Update man pages.
+	Fixes for some memory leaks.
+
+ver 2.3:
+	Added const qualifiers to appropriate function arguments.
+	Minor fixes.
+	CSR firmware version is now displayed by 'revision' command.
+	Voice command is working properly on big endian machines.
+	Added support for Texas Bluetooth modules.
+	Added support for high UART baud rates on Ericsson modules.
+	BCSP initialization fixes.
+	Support for role switch command (hcitool).
+	RFCOMM config file parser fixes.
+	Update man pages.
+	Removed GLib dependency.
+
+ver 2.2:
+	Updated RFCOMM header file.
+	Additional HCI command and event defines.
+	Support for voice settings (hciconfig).
+	Minor hcitool fixes.
+	Improved configure script.
+	Added Headset testing tool.
+	Updated man pages.
+	RPM package.
+
+ver 2.1.1:
+	Resurrect hci_remote_name.
+
+ver 2.1:
+	Added hci_{read, write}_class_of_dev().
+	Added hci_{read, write}_current_iac_lap().
+	Added hci_write_local_name().
+	Added RFCOMM header file.
+	Minor fixes.
+	Improved BCSP initialization (hciattach).
+	Support for displaying link quality (hcitool).
+	Support for changing link supervision timeout (hcitool).
+	New RFCOMM TTY configuration tool (rfcomm).
+	Minor fixes and updates.
+
+ver 2.0:
+	Additional company IDs.
+	BCSP initialization (hciattach).
+	Minor hciconfig fixes.
+
+ver 2.0-pr13:
+	Support for multiple pairing modes.
+	Link key database handling fixes.
+
+ver 2.0-pre12:
+	Removed max link key limit. Keys never expire.
+	Link key database is always updated. Reread PIN on SIGHUP (hcid).
+	Bluetooth script starts SDPd, if installed.
+	Other minor fixes.
+
+ver 2.0-pre11:
+	Improved link key management and more verbose logging (hcid).
+	Fixed scan command (hcitool).
+
+ver 2.0-pre10:
+	Fix hci_inquiry function to return errors and accept user buffers.
+	New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route.
+	Additional company IDs.
+	Makefile and other minor fixes.
+	Support for reading RSSI, remote name and changing
+	connection type (hcitool). 
+	Device initialization fixes (hcid).
+	Other minor fixes and improvements.
+	Build environment cleanup and fixes.
+
+ver 2.0-pre9:
+	Improved bluepin. Working X authentication.
+	Improved hcitool. New flexible cmd syntax, additional commands.
+	Human readable display of the device features.
+	LMP features to string translation support.
+	Additional HCI command and event defines.
+	Extended hci_filter API.
+
+ver 2.0-pre8:
+	Additional HCI ioctls and defines.
+	All strings and buffers are allocated dynamically.
+	ba2str, str2ba automatically swap bdaddress.
+	Additional hciconfig commands. Support for ACL and SCO MTU ioctls.
+	Support for Inventel and COM1 UART based devices.
+	Minor hcitool fixes.
+	Improved l2test. New L2CAP test modes.
+	Minor fixes and cleanup.
+
+ver 2.0-pre7:
+	Bluetooth libraries and header files is now a separate package.
+	New build environment uses automake and libtool.
+	Massive header files cleanup.
+	Bluetooth utilities is now a separate package.
+	New build environment uses automake.
+	Moved all config files and security data to /etc/bluetooth.
+	Various cleanups.
+
+ver 2.0-pre6:
+	API cleanup and additions.
+	Improved hcitool.
+	l2test minor output fixes.
+	hciattach opt to display list of supported devices.
+
+ver 2.0-pre4:
+	HCI filter enhancements.
+
+ver 2.0-pre3:
+	Cleanup.
+
+ver 2.0-pre2:
+	Additional HCI library functions.
+	Improved CSR baud rate initialization.
+	PCMCIA scripts fixes and enhancements.
+	Documentation update.
+
+ver 2.0-pre1:
+	New UART initialization utility.
+	Hot plugging support for UART based PCMCIA devices.
+	SCO testing utility.
+	New authentication utility (bluepin).
+	Minor fixes and improvements.
diff --git a/bluez/repo/HACKING b/bluez/repo/HACKING
new file mode 100644
index 0000000..a8fb403
--- /dev/null
+++ b/bluez/repo/HACKING
@@ -0,0 +1,130 @@
+Hacking on BlueZ
+****************
+
+Build tools requirements
+========================
+
+When building and testing directly from the repository it is important to
+have at least automake version 1.10 or later installed. All modern
+distributions should default to the latest version, but it seems that
+Debian's default is still an earlier version:
+
+  Check version
+    # dpkg -l '*automake*'
+
+  Install new version
+    # apt-get install automake1.10
+    # update-alternatives --config automake
+
+
+Working with the source code repository
+=======================================
+
+The repository contains two extra scripts that accomplish the bootstrap
+process. One is called "bootstrap" which is the basic scripts that uses the
+autotools scripts to create the needed files for building and installing.
+It makes sure to call the right programs depending on the usage of shared or
+static libraries or translations etc.
+
+The second program is called "bootstrap-configure". This program will make
+sure to properly clean the repository, call the "bootstrap" script and then
+call configure with proper settings for development. It will use the best
+options and pass them over to configure. These options normally include
+the enabling the maintainer mode and the debugging features.
+
+So while in a normal source project the call "./configure ..." is used to
+configure the project with its settings like prefix and extra options. In
+case of bare repositories call "./bootstrap-configure" and it will bootstrap
+the repository and calls configure with all the correct options to make
+development easier.
+
+In case of preparing for a release with "make distcheck", don't use
+bootstrap-configure since it could export development specific settings.
+
+So the normal steps to checkout, build and install such a repository is
+like this:
+
+  Checkout repository
+    # git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git
+    # cd bluez
+
+  Configure and build
+    # ./bootstrap-configure
+    # make
+
+  Configure and build with cgcc (Sparse)
+    # ./bootstrap-configure CC=cgcc
+    # make
+
+  Run unit tests
+    # make check
+
+  Check installation
+    # make install DESTDIR=$PWD/x
+    # find x
+    # rm -rf x
+
+  Check distribution
+    # make distcheck
+
+  Final installation
+    # sudo make install
+
+  Remove autogenerated files
+    # make maintainer-clean
+
+
+Running from within the source code repository
+==============================================
+
+When using "./configure --enable-maintainer-mode" the automake scripts will
+use the plugins directly from within the repository. This removes the need
+to use "make install" when testing "bluetoothd". The "bootstrap-configure"
+automatically includes this option.
+
+  Copy configuration file which specifies the required security policies
+    # sudo cp ./src/bluetooth.conf /etc/dbus-1/system.d/
+
+  Run daemon in foreground with debugging
+    # sudo ./src/bluetoothd -n -d
+
+  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
+
+For production installations or distribution packaging it is important that
+the "--enable-maintainer-mode" option is NOT used.
+
+Note multiple arguments to -d can be specified, colon, comma or space
+separated. The arguments are relative source code filenames for which
+debugging output should be enabled; output shell-style globs are
+accepted (e.g.: 'plugins/*:src/main.c').
+
+Submitting patches
+==================
+
+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.
+
+BlueZ rules for submitting patches follow most of the rules used by Linux kernel
+(https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
+
+1) Do *not* add "Signed-off-by" lines in your commit messages. BlueZ does not
+use them, so including them is actually an error.
+
+2) Be sure to follow the coding style rules of BlueZ. They are listed in
+doc/coding-style.txt.
+
+3) Split your patch according to the top-level directories. E.g.: if you added
+a feature that touches files under 'include/', 'src/' and 'drivers/'
+directories, split in three separated patches, taking care not to
+break compilation.
+
+4) Bug fixes should be sent first as they take priority over new features.
+
+5) The commit message should follow 50/72 formatting which means the header
+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.
diff --git a/bluez/repo/INSTALL b/bluez/repo/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/bluez/repo/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/bluez/repo/Makefile.am b/bluez/repo/Makefile.am
new file mode 100644
index 0000000..3e1d8d0
--- /dev/null
+++ b/bluez/repo/Makefile.am
@@ -0,0 +1,505 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+lib_LTLIBRARIES =
+
+noinst_LIBRARIES =
+
+noinst_LTLIBRARIES =
+
+bin_PROGRAMS =
+
+noinst_PROGRAMS =
+
+dist_man_MANS =
+
+dist_noinst_MANS =
+
+CLEANFILES =
+
+EXTRA_DIST =
+
+libexecdir = @libexecdir@/bluetooth
+
+libexec_PROGRAMS =
+
+includedir = @includedir@/bluetooth
+
+include_HEADERS =
+
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+
+if DATAFILES
+dbusdir = @DBUS_CONFDIR@/dbus-1/system.d
+dbus_DATA = src/bluetooth.conf
+
+confdir = $(sysconfdir)/bluetooth
+conf_DATA =
+
+statedir = $(localstatedir)/lib/bluetooth
+state_DATA =
+endif
+
+if SYSTEMD
+systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
+systemdsystemunit_DATA = src/bluetooth.service
+
+dbussystembusdir = @DBUS_SYSTEMBUSDIR@
+dbussystembus_DATA = src/org.bluez.service
+endif
+
+EXTRA_DIST += src/bluetooth.service.in src/org.bluez.service
+
+plugindir = $(libdir)/bluetooth/plugins
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+
+plugin_LTLIBRARIES =
+
+lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \
+		lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \
+		lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
+
+extra_headers = lib/mgmt.h lib/uuid.h lib/a2mp.h lib/amp.h
+extra_sources = lib/uuid.c
+
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+if LIBRARY
+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:10:18
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+endif
+
+noinst_LTLIBRARIES += lib/libbluetooth-internal.la
+
+lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \
+					$(extra_headers) $(extra_sources)
+
+noinst_LTLIBRARIES += gdbus/libgdbus-internal.la
+
+gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \
+				gdbus/mainloop.c gdbus/watch.c \
+				gdbus/object.c gdbus/client.c gdbus/polkit.c
+
+noinst_LTLIBRARIES += src/libshared-glib.la src/libshared-mainloop.la
+
+shared_sources = src/shared/io.h src/shared/timeout.h \
+			src/shared/queue.h src/shared/queue.c \
+			src/shared/util.h src/shared/util.c \
+			src/shared/mgmt.h src/shared/mgmt.c \
+			src/shared/crypto.h src/shared/crypto.c \
+			src/shared/ecc.h src/shared/ecc.c \
+			src/shared/ringbuf.h src/shared/ringbuf.c \
+			src/shared/tester.h src/shared/tester.c \
+			src/shared/hci.h src/shared/hci.c \
+			src/shared/hci-crypto.h src/shared/hci-crypto.c \
+			src/shared/hfp.h src/shared/hfp.c \
+			src/shared/uhid.h src/shared/uhid.c \
+			src/shared/pcap.h src/shared/pcap.c \
+			src/shared/btsnoop.h src/shared/btsnoop.c \
+			src/shared/ad.h src/shared/ad.c \
+			src/shared/att-types.h \
+			src/shared/att.h src/shared/att.c \
+			src/shared/gatt-helpers.h src/shared/gatt-helpers.c \
+			src/shared/gatt-client.h src/shared/gatt-client.c \
+			src/shared/gatt-server.h src/shared/gatt-server.c \
+			src/shared/gatt-db.h src/shared/gatt-db.c \
+			src/shared/gap.h src/shared/gap.c
+
+src_libshared_glib_la_SOURCES = $(shared_sources) \
+				src/shared/io-glib.c \
+				src/shared/timeout-glib.c
+
+src_libshared_mainloop_la_SOURCES = $(shared_sources) \
+				src/shared/io-mainloop.c \
+				src/shared/timeout-mainloop.c \
+				src/shared/mainloop.h src/shared/mainloop.c
+
+attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
+		attrib/gatt.h attrib/gatt.c \
+		attrib/gattrib.h attrib/gattrib.c \
+		attrib/gatt-service.h attrib/gatt-service.c
+
+btio_sources = btio/btio.h btio/btio.c
+
+gobex_sources = gobex/gobex.h gobex/gobex.c \
+			gobex/gobex-defs.h gobex/gobex-defs.c \
+			gobex/gobex-packet.c gobex/gobex-packet.h \
+			gobex/gobex-header.c gobex/gobex-header.h \
+			gobex/gobex-transfer.c gobex/gobex-debug.h \
+			gobex/gobex-apparam.c gobex/gobex-apparam.h
+
+builtin_modules =
+builtin_sources =
+builtin_nodist =
+
+include Makefile.plugins
+
+if MAINTAINER_MODE
+plugin_LTLIBRARIES += plugins/external-dummy.la
+plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+				    -no-undefined
+plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+endif
+
+libexec_PROGRAMS += src/bluetoothd
+
+src_bluetoothd_SOURCES = $(builtin_sources) \
+			$(attrib_sources) $(btio_sources) \
+			src/bluetooth.ver \
+			src/main.c src/log.h src/log.c \
+			src/backtrace.h src/backtrace.c \
+			src/systemd.h src/systemd.c \
+			src/rfkill.c src/hcid.h src/sdpd.h \
+			src/sdpd-server.c src/sdpd-request.c \
+			src/sdpd-service.c src/sdpd-database.c \
+			src/attrib-server.h src/attrib-server.c \
+			src/gatt-database.h src/gatt-database.c \
+			src/sdp-xml.h src/sdp-xml.c \
+			src/sdp-client.h src/sdp-client.c \
+			src/textfile.h src/textfile.c \
+			src/uuid-helper.h src/uuid-helper.c \
+			src/uinput.h \
+			src/plugin.h src/plugin.c \
+			src/storage.h src/storage.c \
+			src/advertising.h src/advertising.c \
+			src/agent.h src/agent.c \
+			src/error.h src/error.c \
+			src/adapter.h src/adapter.c \
+			src/profile.h src/profile.c \
+			src/service.h src/service.c \
+			src/gatt-client.h src/gatt-client.c \
+			src/device.h src/device.c src/attio.h \
+			src/dbus-common.c src/dbus-common.h \
+			src/eir.h src/eir.c
+src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+			gdbus/libgdbus-internal.la \
+			src/libshared-glib.la \
+			@BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
+				-Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la \
+				src/libshared-glib.la \
+				src/bluetooth.service
+
+src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
+					-DPLUGINDIR=\""$(build_plugindir)"\"
+src_bluetoothd_SHORTNAME = bluetoothd
+
+builtin_files = src/builtin.h $(builtin_nodist)
+
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+
+CLEANFILES += $(builtin_files) src/bluetooth.service
+
+man_MANS = src/bluetoothd.8
+
+EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
+			src/main.conf profiles/network/network.conf \
+			profiles/input/input.conf profiles/proximity/proximity.conf
+
+test_scripts =
+unit_tests =
+
+include Makefile.tools
+include Makefile.obexd
+include android/Makefile.am
+
+if HID2HCI
+rulesdir = @UDEV_DIR@/rules.d
+
+rules_DATA = tools/97-hid2hci.rules
+
+CLEANFILES += $(rules_DATA)
+endif
+
+EXTRA_DIST += tools/hid2hci.rules
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+EXTRA_DIST += $(test_scripts)
+
+EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \
+				doc/test-coverage.txt \
+				doc/test-runner.txt \
+				doc/settings-storage.txt
+
+EXTRA_DIST += doc/mgmt-api.txt \
+		doc/adapter-api.txt doc/device-api.txt \
+		doc/agent-api.txt doc/profile-api.txt \
+		doc/network-api.txt doc/media-api.txt \
+		doc/health-api.txt doc/sap-api.txt \
+		doc/input-api.txt
+
+EXTRA_DIST += doc/alert-api.txt \
+		doc/proximity-api.txt doc/heartrate-api.txt \
+		doc/thermometer-api.txt doc/cyclingspeed-api.txt \
+		doc/gatt-api.txt doc/advertising-api.txt
+
+EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
+
+EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
+		doc/pts-opp.txt
+
+EXTRA_DIST += tools/magic.btsnoop
+
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ @UDEV_CFLAGS@
+
+AM_CPPFLAGS = -I$(builddir)/lib
+
+
+unit_tests += unit/test-eir
+
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
+unit_test_eir_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \
+								@GLIB_LIBS@
+
+unit_tests += unit/test-uuid
+
+unit_test_uuid_SOURCES = unit/test-uuid.c
+unit_test_uuid_LDADD = src/libshared-glib.la lib/libbluetooth-internal.la \
+								@GLIB_LIBS@
+
+unit_tests += unit/test-textfile
+
+unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
+unit_test_textfile_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-crc
+
+unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
+unit_test_crc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-crypto
+
+unit_test_crypto_SOURCES = unit/test-crypto.c
+unit_test_crypto_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-ecc
+
+unit_test_ecc_SOURCES = unit/test-ecc.c
+unit_test_ecc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-ringbuf unit/test-queue
+
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c
+unit_test_ringbuf_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_test_queue_SOURCES = unit/test-queue.c
+unit_test_queue_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-mgmt
+
+unit_test_mgmt_SOURCES = unit/test-mgmt.c
+unit_test_mgmt_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-uhid
+
+unit_test_uhid_SOURCES = unit/test-uhid.c
+unit_test_uhid_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-sdp
+
+unit_test_sdp_SOURCES = unit/test-sdp.c \
+				src/sdpd.h src/sdpd-database.c \
+				src/log.h src/log.c \
+				src/sdpd-service.c src/sdpd-request.c
+unit_test_sdp_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avdtp
+
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+				src/log.h src/log.c \
+				android/avdtp.c android/avdtp.h
+unit_test_avdtp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avctp
+
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+				src/log.h src/log.c \
+				android/avctp.c android/avctp.h
+unit_test_avctp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-avrcp
+
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+				src/log.h src/log.c \
+				android/avctp.c android/avctp.h \
+				android/avrcp-lib.c android/avrcp-lib.h
+unit_test_avrcp_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-hfp
+
+unit_test_hfp_SOURCES = unit/test-hfp.c
+unit_test_hfp_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+unit_tests += unit/test-gdbus-client
+
+unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
+unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@ @DBUS_LIBS@
+
+unit_tests += unit/test-gobex-header unit/test-gobex-packet unit/test-gobex \
+			unit/test-gobex-transfer unit/test-gobex-apparam
+
+unit_test_gobex_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex.c
+unit_test_gobex_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_packet_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-packet.c
+unit_test_gobex_packet_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_header_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-header.c
+unit_test_gobex_header_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_transfer_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-transfer.c
+unit_test_gobex_transfer_LDADD = @GLIB_LIBS@
+
+unit_test_gobex_apparam_SOURCES = $(gobex_sources) unit/util.c unit/util.h \
+						unit/test-gobex-apparam.c
+unit_test_gobex_apparam_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-lib
+
+unit_test_lib_SOURCES = unit/test-lib.c
+unit_test_lib_LDADD = src/libshared-glib.la \
+				lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-gatt
+
+unit_test_gatt_SOURCES = unit/test-gatt.c
+unit_test_gatt_LDADD = src/libshared-glib.la \
+				lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-hog
+
+unit_test_hog_SOURCES = unit/test-hog.c \
+			$(btio_sources) \
+			profiles/input/hog-lib.h profiles/input/hog-lib.c \
+			profiles/scanparam/scpp.h profiles/scanparam/scpp.c \
+			profiles/battery/bas.h profiles/battery/bas.c \
+			profiles/deviceinfo/dis.h profiles/deviceinfo/dis.c \
+			src/log.h src/log.c \
+			attrib/att.h attrib/att.c \
+			attrib/gatt.h attrib/gatt.c \
+			attrib/gattrib.h attrib/gattrib.c
+unit_test_hog_LDADD = src/libshared-glib.la \
+				lib/libbluetooth-internal.la @GLIB_LIBS@
+
+unit_tests += unit/test-gattrib
+
+unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
+unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
+			src/libshared-glib.la \
+			@GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+
+if MAINTAINER_MODE
+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
+
+if LIBRARY
+pkgconfig_DATA = lib/bluez.pc
+endif
+
+manual_pages = doc/btmon.1
+
+if MANPAGES
+dist_noinst_MANS += $(manual_pages)
+endif
+
+EXTRA_DIST += $(manual_pages:.1=.txt)
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
+					--enable-manpages --enable-android \
+					--disable-systemd --disable-udev
+
+DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages)
+
+MAINTAINERCLEANFILES = Makefile.in \
+	aclocal.m4 configure config.h.in config.sub config.guess \
+	ltmain.sh depcomp compile missing install-sh mkinstalldirs test-driver
+
+SED_PROCESS = $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
+		$(SED) -e 's,@libexecdir\@,$(libexecdir),g' \
+		< $< > $@
+
+%.service: %.service.in Makefile
+	$(SED_PROCESS)
+
+%.1: %.txt
+	$(AM_V_GEN)a2x --doctype manpage --format manpage $(srcdir)/$<
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+	$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+tools/%.rules:
+	$(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@
+
+$(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)"/$< $@
+
+if COVERAGE
+clean-coverage:
+	@lcov --directory $(top_builddir) --zerocounters
+	$(RM) -r coverage $(top_builddir)/coverage.info
+
+coverage: check
+	@lcov --compat-libtool --directory $(top_builddir) --capture \
+				--output-file $(top_builddir)/coverage.info
+	$(AM_V_at)$(MKDIR_P) coverage
+	@genhtml -o coverage/ $(top_builddir)/coverage.info
+
+clean-local: clean-coverage
+	-find $(top_builddir) -name "*.gcno" -delete
+	-find $(top_builddir) -name "*.gcda" -delete
+	$(RM) -r lib/bluetooth
+
+else
+clean-local:
+	-find $(top_builddir) -name "*.gcno" -delete
+	-find $(top_builddir) -name "*.gcda" -delete
+	$(RM) -r lib/bluetooth
+endif
diff --git a/bluez/repo/Makefile.obexd b/bluez/repo/Makefile.obexd
new file mode 100644
index 0000000..2e33cbc
--- /dev/null
+++ b/bluez/repo/Makefile.obexd
@@ -0,0 +1,110 @@
+
+if SYSTEMD
+systemduserunitdir = @SYSTEMD_USERUNITDIR@
+systemduserunit_DATA = obexd/src/obex.service
+
+dbussessionbusdir = @DBUS_SESSIONBUSDIR@
+dbussessionbus_DATA = obexd/src/org.bluez.obex.service
+endif
+
+EXTRA_DIST += obexd/src/obex.service.in obexd/src/org.bluez.obex.service
+
+obex_plugindir = $(libdir)/obex/plugins
+
+obexd_builtin_modules =
+obexd_builtin_sources =
+obexd_builtin_nodist =
+
+obexd_builtin_modules += filesystem
+obexd_builtin_sources += obexd/plugins/filesystem.c obexd/plugins/filesystem.h
+
+obexd_builtin_modules += bluetooth
+obexd_builtin_sources += obexd/plugins/bluetooth.c
+
+if EXPERIMENTAL
+obexd_builtin_modules += pcsuite
+obexd_builtin_sources += obexd/plugins/pcsuite.c
+endif
+
+obexd_builtin_modules += opp
+obexd_builtin_sources += obexd/plugins/opp.c
+
+obexd_builtin_modules += ftp
+obexd_builtin_sources += obexd/plugins/ftp.c obexd/plugins/ftp.h
+
+if OBEX
+obexd_builtin_modules += irmc
+obexd_builtin_sources += obexd/plugins/irmc.c
+
+obexd_builtin_modules += pbap
+obexd_builtin_sources += obexd/plugins/pbap.c \
+				obexd/plugins/vcard.h obexd/plugins/vcard.c \
+				obexd/plugins/phonebook.h \
+				obexd/plugins/phonebook-dummy.c
+endif
+
+obexd_builtin_modules += mas
+obexd_builtin_sources += obexd/plugins/mas.c obexd/src/map_ap.h \
+				obexd/plugins/messages.h \
+				obexd/plugins/messages-dummy.c
+
+obexd_builtin_modules += mns
+obexd_builtin_sources += obexd/client/mns.c obexd/src/map_ap.h \
+				obexd/client/map-event.h
+
+libexec_PROGRAMS += obexd/src/obexd
+
+obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \
+			$(obexd_builtin_sources) \
+			obexd/src/main.c obexd/src/obexd.h \
+			obexd/src/plugin.h obexd/src/plugin.c \
+			obexd/src/log.h obexd/src/log.c \
+			obexd/src/manager.h obexd/src/manager.c \
+			obexd/src/obex.h obexd/src/obex.c obexd/src/obex-priv.h \
+			obexd/src/mimetype.h obexd/src/mimetype.c \
+			obexd/src/service.h obexd/src/service.c \
+			obexd/src/transport.h obexd/src/transport.c \
+			obexd/src/server.h obexd/src/server.c \
+			obexd/client/manager.h obexd/client/manager.c \
+			obexd/client/session.h obexd/client/session.c \
+			obexd/client/bluetooth.h obexd/client/bluetooth.c \
+			obexd/client/sync.h obexd/client/sync.c \
+			obexd/client/pbap.h obexd/client/pbap.c \
+			obexd/client/ftp.h obexd/client/ftp.c \
+			obexd/client/opp.h obexd/client/opp.c \
+			obexd/client/map.h obexd/client/map.c \
+			obexd/client/map-event.h obexd/client/map-event.c \
+			obexd/client/transfer.h obexd/client/transfer.c \
+			obexd/client/transport.h obexd/client/transport.c \
+			obexd/client/dbus.h obexd/client/dbus.c \
+			obexd/client/driver.h obexd/client/driver.c \
+			obexd/src/map_ap.h
+obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \
+			gdbus/libgdbus-internal.la \
+			@ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ -ldl
+
+obexd_src_obexd_LDFLAGS = -Wl,--export-dynamic
+
+obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \
+				@ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \
+				-DPLUGINDIR=\""$(obex_plugindir)"\" \
+				-fPIC -D_FILE_OFFSET_BITS=64
+
+obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src
+
+obexd_src_obexd_SHORTNAME = obexd
+
+obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist)
+
+nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
+
+BUILT_SOURCES += obexd/src/builtin.h
+
+obexd/src/plugin.$(OBJEXT): obexd/src/builtin.h
+
+obexd/src/builtin.h: obexd/src/genbuiltin $(obexd_builtin_sources)
+	$(AM_V_GEN)$(srcdir)/obexd/src/genbuiltin $(obexd_builtin_modules) > $@
+
+CLEANFILES += obexd/src/builtin.h $(builtin_files) obexd/src/obex.service
+
+EXTRA_DIST += obexd/src/genbuiltin
diff --git a/bluez/repo/Makefile.plugins b/bluez/repo/Makefile.plugins
new file mode 100644
index 0000000..f85b642
--- /dev/null
+++ b/bluez/repo/Makefile.plugins
@@ -0,0 +1,125 @@
+
+builtin_modules += hostname
+builtin_sources += plugins/hostname.c
+
+builtin_modules += wiimote
+builtin_sources += plugins/wiimote.c
+
+builtin_modules += autopair
+builtin_sources += plugins/autopair.c
+
+builtin_modules += policy
+builtin_sources += plugins/policy.c
+
+if MAINTAINER_MODE
+builtin_modules += gatt_example
+builtin_sources += plugins/gatt-example.c
+endif
+
+if EXPERIMENTAL
+builtin_modules += neard
+builtin_sources += plugins/neard.c
+
+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-dummy.c
+
+noinst_LIBRARIES += profiles/sap/libsap.a
+profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+endif
+
+builtin_modules += a2dp
+builtin_sources += profiles/audio/source.h profiles/audio/source.c \
+			profiles/audio/sink.h profiles/audio/sink.c \
+			profiles/audio/a2dp.h profiles/audio/a2dp.c \
+			profiles/audio/avdtp.h profiles/audio/avdtp.c \
+			profiles/audio/media.h profiles/audio/media.c \
+			profiles/audio/transport.h profiles/audio/transport.c \
+			profiles/audio/a2dp-codecs.h
+
+builtin_modules += avrcp
+builtin_sources += profiles/audio/control.h profiles/audio/control.c \
+			profiles/audio/avctp.h profiles/audio/avctp.c \
+			profiles/audio/avrcp.h profiles/audio/avrcp.c \
+			profiles/audio/player.h profiles/audio/player.c
+
+builtin_modules += network
+builtin_sources += profiles/network/manager.c \
+			profiles/network/bnep.h profiles/network/bnep.c \
+			profiles/network/server.h profiles/network/server.c \
+			profiles/network/connection.h \
+			profiles/network/connection.c
+
+builtin_modules += input
+builtin_sources += profiles/input/manager.c \
+			profiles/input/server.h profiles/input/server.c \
+			profiles/input/device.h profiles/input/device.c \
+			profiles/input/hidp_defs.h
+
+builtin_modules += hog
+builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
+			profiles/input/hog-lib.c profiles/input/hog-lib.h \
+			profiles/deviceinfo/dis.c profiles/deviceinfo/dis.h \
+			profiles/battery/bas.c profiles/battery/bas.h \
+			profiles/scanparam/scpp.c profiles/scanparam/scpp.h \
+			profiles/input/suspend.h profiles/input/suspend-none.c
+
+EXTRA_DIST += profiles/input/suspend-dummy.c
+
+if EXPERIMENTAL
+builtin_modules += health
+builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
+			profiles/health/hdp_main.c profiles/health/hdp_types.h \
+			profiles/health/hdp_manager.h \
+			profiles/health/hdp_manager.c \
+			profiles/health/hdp.h profiles/health/hdp.c \
+			profiles/health/hdp_util.h profiles/health/hdp_util.c
+endif
+
+builtin_modules += gap
+builtin_sources += profiles/gap/gas.c
+
+builtin_modules += scanparam
+builtin_sources += profiles/scanparam/scan.c
+
+builtin_modules += deviceinfo
+builtin_sources += profiles/deviceinfo/deviceinfo.c
+
+if EXPERIMENTAL
+builtin_modules += alert
+builtin_sources += profiles/alert/server.c
+
+builtin_modules += time
+builtin_sources += profiles/time/server.c
+
+builtin_modules += proximity
+builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
+			profiles/proximity/manager.c \
+			profiles/proximity/monitor.h \
+			profiles/proximity/monitor.c \
+			profiles/proximity/reporter.h \
+			profiles/proximity/reporter.c \
+			profiles/proximity/linkloss.h \
+			profiles/proximity/linkloss.c \
+			profiles/proximity/immalert.h \
+			profiles/proximity/immalert.c
+
+builtin_modules += thermometer
+builtin_sources += profiles/thermometer/thermometer.c
+
+builtin_modules += heartrate
+builtin_sources += profiles/heartrate/heartrate.c
+
+builtin_modules += cyclingspeed
+builtin_sources += profiles/cyclingspeed/cyclingspeed.c
+endif
+
+if SIXAXIS
+plugin_LTLIBRARIES += plugins/sixaxis.la
+plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
+plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+						-no-undefined @UDEV_LIBS@
+plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
+endif
diff --git a/bluez/repo/Makefile.tools b/bluez/repo/Makefile.tools
new file mode 100644
index 0000000..e640271
--- /dev/null
+++ b/bluez/repo/Makefile.tools
@@ -0,0 +1,418 @@
+
+if CLIENT
+bin_PROGRAMS += client/bluetoothctl
+
+client_bluetoothctl_SOURCES = client/main.c \
+					client/display.h client/display.c \
+					client/agent.h client/agent.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
+endif
+
+if MONITOR
+bin_PROGRAMS += monitor/btmon
+
+monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+				monitor/display.h monitor/display.c \
+				monitor/hcidump.h monitor/hcidump.c \
+				monitor/ellisys.h monitor/ellisys.c \
+				monitor/control.h monitor/control.c \
+				monitor/packet.h monitor/packet.c \
+				monitor/vendor.h monitor/vendor.c \
+				monitor/lmp.h monitor/lmp.c \
+				monitor/crc.h monitor/crc.c \
+				monitor/ll.h monitor/ll.c \
+				monitor/l2cap.h monitor/l2cap.c \
+				monitor/sdp.h monitor/sdp.c \
+				monitor/avctp.h monitor/avctp.c \
+				monitor/avdtp.h monitor/avdtp.c \
+				monitor/a2dp.h monitor/a2dp.c \
+				monitor/rfcomm.h monitor/rfcomm.c \
+				monitor/bnep.h monitor/bnep.c \
+				monitor/uuid.h monitor/uuid.c \
+				monitor/hwdb.h monitor/hwdb.c \
+				monitor/keys.h monitor/keys.c \
+				monitor/analyze.h monitor/analyze.c \
+				monitor/intel.h monitor/intel.c \
+				monitor/broadcom.h monitor/broadcom.c
+monitor_btmon_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-mainloop.la @UDEV_LIBS@
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \
+					peripheral/btsensor tools/3dsp \
+					tools/mgmt-tester tools/gap-tester \
+					tools/l2cap-tester tools/sco-tester \
+					tools/smp-tester tools/hci-tester \
+					tools/rfcomm-tester tools/bnep-tester \
+					tools/userchan-tester
+
+emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+				emulator/serial.h emulator/serial.c \
+				emulator/server.h emulator/server.c \
+				emulator/vhci.h emulator/vhci.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				emulator/phy.h emulator/phy.c \
+				emulator/amp.h emulator/amp.c \
+				emulator/le.h emulator/le.c
+emulator_btvirt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+emulator_b1ee_SOURCES = emulator/b1ee.c
+emulator_b1ee_LDADD = src/libshared-mainloop.la
+
+emulator_hfp_SOURCES = emulator/hfp.c
+emulator_hfp_LDADD = src/libshared-mainloop.la
+
+peripheral_btsensor_SOURCES = peripheral/main.c \
+				peripheral/efivars.h peripheral/efivars.c \
+				peripheral/attach.h peripheral/attach.c \
+				peripheral/log.h peripheral/log.c \
+				peripheral/gap.h peripheral/gap.c \
+				peripheral/gatt.h peripheral/gatt.c
+peripheral_btsensor_LDADD = src/libshared-mainloop.la \
+				lib/libbluetooth-internal.la
+
+tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h
+tools_3dsp_LDADD = src/libshared-mainloop.la
+
+tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_bnep_tester_SOURCES = tools/bnep-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_bnep_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_smp_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_gap_tester_LDADD =  lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la \
+				src/libshared-glib.la \
+				@GLIB_LIBS@ @DBUS_LIBS@
+
+tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_sco_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h
+tools_hci_tester_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+tools_userchan_tester_SOURCES = tools/userchan-tester.c monitor/bt.h \
+				emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c
+tools_userchan_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+endif
+
+if TOOLS
+bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
+			tools/rfcomm tools/rctest tools/l2test tools/l2ping \
+			tools/sdptool tools/ciptool tools/bccmd \
+			tools/hex2hcd tools/mpris-proxy
+
+tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+						tools/hciattach_st.c \
+						tools/hciattach_ti.c \
+						tools/hciattach_tialt.c \
+						tools/hciattach_ath3k.c \
+						tools/hciattach_qualcomm.c \
+						tools/hciattach_intel.c \
+						tools/hciattach_bcm43xx.c
+tools_hciattach_LDADD = lib/libbluetooth-internal.la
+
+tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
+tools_hciconfig_LDADD = lib/libbluetooth-internal.la
+
+tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
+tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+
+tools_hcidump_SOURCES = tools/hcidump.c \
+				tools/parser/parser.h tools/parser/parser.c \
+				tools/parser/lmp.c \
+				tools/parser/hci.c \
+				tools/parser/l2cap.h tools/parser/l2cap.c \
+				tools/parser/amp.c \
+				tools/parser/smp.c \
+				tools/parser/att.c \
+				tools/parser/sdp.h tools/parser/sdp.c \
+				tools/parser/rfcomm.h tools/parser/rfcomm.c \
+				tools/parser/bnep.c \
+				tools/parser/cmtp.c \
+				tools/parser/hidp.c \
+				tools/parser/hcrp.c \
+				tools/parser/avdtp.c \
+				tools/parser/avctp.c \
+				tools/parser/avrcp.c \
+				tools/parser/sap.c \
+				tools/parser/obex.c \
+				tools/parser/capi.c \
+				tools/parser/ppp.c \
+				tools/parser/tcpip.c \
+				tools/parser/ericsson.c \
+				tools/parser/csr.c \
+				tools/parser/bpa.c
+tools_hcidump_LDADD = lib/libbluetooth-internal.la
+
+tools_rfcomm_LDADD = lib/libbluetooth-internal.la
+
+tools_rctest_LDADD = lib/libbluetooth-internal.la
+
+tools_l2test_LDADD = lib/libbluetooth-internal.la
+
+tools_l2ping_LDADD = lib/libbluetooth-internal.la
+
+tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+tools_sdptool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_ciptool_LDADD = lib/libbluetooth-internal.la
+
+tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
+			tools/csr_hci.c tools/csr_usb.c \
+			tools/csr_h4.c tools/csr_3wire.c \
+			tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
+tools_bccmd_LDADD = lib/libbluetooth-internal.la
+
+tools_hex2hcd_SOURCES = tools/hex2hcd.c
+
+tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
+tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+
+dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
+			tools/hcitool.1 tools/hcidump.1 \
+			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+			tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+else
+EXTRA_DIST += tools/hciattach.1 tools/hciconfig.1 \
+			tools/hcitool.1 tools/hcidump.1 \
+			tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
+			tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
+endif
+
+if HID2HCI
+udevdir = @UDEV_DIR@
+
+udev_PROGRAMS = tools/hid2hci
+
+tools_hid2hci_LDADD = @UDEV_LIBS@
+
+dist_man_MANS += tools/hid2hci.1
+else
+EXTRA_DIST += tools/hid2hci.1
+endif
+
+if EXPERIMENTAL
+bin_PROGRAMS += tools/btattach
+
+noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
+			tools/scotest tools/amptest tools/hwdb \
+			tools/hcieventmask tools/hcisecfilter \
+			tools/btinfo \
+			tools/btsnoop tools/btproxy \
+			tools/btiotest tools/bneptest tools/mcaptest \
+			tools/cltest tools/oobtest tools/seq2bseq \
+			tools/nokfw tools/create-image \
+			tools/eddystone tools/ibeacon \
+			tools/btgatt-client tools/btgatt-server \
+			tools/test-runner tools/check-selftest
+
+tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
+tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
+
+tools_avinfo_LDADD = lib/libbluetooth-internal.la
+
+tools_avtest_LDADD = lib/libbluetooth-internal.la
+
+tools_scotest_LDADD = lib/libbluetooth-internal.la
+
+tools_amptest_LDADD = lib/libbluetooth-internal.la
+
+tools_hwdb_LDADD = lib/libbluetooth-internal.la
+
+tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
+
+tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h
+tools_btinfo_LDADD = src/libshared-mainloop.la
+
+tools_btattach_SOURCES = tools/btattach.c monitor/bt.h
+tools_btattach_LDADD = src/libshared-mainloop.la
+
+tools_btsnoop_SOURCES = tools/btsnoop.c
+tools_btsnoop_LDADD = src/libshared-mainloop.la
+
+tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h
+tools_btproxy_LDADD = src/libshared-mainloop.la
+
+tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
+tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_mcaptest_SOURCES = tools/mcaptest.c \
+				btio/btio.h btio/btio.c \
+				src/log.c src/log.h \
+				profiles/health/mcap.h profiles/health/mcap.c
+tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
+
+tools_bneptest_SOURCES = tools/bneptest.c \
+				btio/btio.h btio/btio.c \
+				src/log.h src/log.c \
+				profiles/network/bnep.h profiles/network/bnep.c
+tools_bneptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_cltest_SOURCES = tools/cltest.c
+tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_oobtest_SOURCES = tools/oobtest.c
+tools_oobtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+
+tools_seq2bseq_SOURCES = tools/seq2bseq.c
+
+tools_nokfw_SOURCES = tools/nokfw.c
+
+tools_create_image_SOURCES = tools/create-image.c
+
+tools_eddystone_SOURCES = tools/eddystone.c monitor/bt.h
+tools_eddystone_LDADD = src/libshared-mainloop.la
+
+tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h
+tools_ibeacon_LDADD = src/libshared-mainloop.la
+
+tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
+tools_btgatt_client_LDADD = src/libshared-mainloop.la \
+						lib/libbluetooth-internal.la
+
+tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
+tools_btgatt_server_LDADD = src/libshared-mainloop.la \
+						lib/libbluetooth-internal.la
+
+dist_man_MANS += tools/btattach.1
+
+EXTRA_DIST += tools/bdaddr.1
+else
+EXTRA_DIST += tools/btattach.1
+endif
+
+if READLINE
+noinst_PROGRAMS += attrib/gatttool tools/btmgmt \
+			tools/obex-client-tool tools/obex-server-tool \
+			tools/bluetooth-player tools/obexctl
+
+attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+				attrib/gattrib.c btio/btio.c \
+				attrib/gatttool.h attrib/interactive.c \
+				attrib/utils.c src/log.c client/display.c \
+				client/display.h
+attrib_gatttool_LDADD = lib/libbluetooth-internal.la \
+			src/libshared-glib.la @GLIB_LIBS@ -lreadline
+
+tools_obex_client_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+						tools/obex-client-tool.c
+tools_obex_client_tool_LDADD = lib/libbluetooth-internal.la \
+						@GLIB_LIBS@ -lreadline
+
+tools_obex_server_tool_SOURCES = $(gobex_sources) $(btio_sources) \
+						tools/obex-server-tool.c
+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 \
+				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+tools_obexctl_SOURCES = tools/obexctl.c \
+				client/display.h client/display.c
+tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
+				@GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \
+				-lreadline
+endif
+
+if EXPERIMENTAL
+noinst_PROGRAMS += tools/gatt-service
+
+tools_gatt_service_SOURCES = tools/gatt-service.c
+tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la
+
+noinst_PROGRAMS += profiles/iap/iapd
+
+profiles_iap_iapd_SOURCES = profiles/iap/main.c
+profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+endif
+
+if CUPS
+cupsdir = $(libdir)/cups/backend
+
+cups_PROGRAMS = profiles/cups/bluetooth
+
+profiles_cups_bluetooth_SOURCES = profiles/cups/main.c \
+					profiles/cups/cups.h \
+					profiles/cups/sdp.c \
+					profiles/cups/spp.c \
+					profiles/cups/hcrp.c
+
+profiles_cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ \
+				lib/libbluetooth-internal.la \
+				gdbus/libgdbus-internal.la
+endif
+
+test_scripts += test/sap_client.py test/bluezutils.py \
+		test/dbusdef.py test/monitor-bluetooth test/list-devices \
+		test/test-discovery test/test-manager test/test-adapter \
+		test/test-device test/simple-agent \
+		test/simple-endpoint test/test-sap-server \
+		test/test-proximity test/test-network \
+		test/test-thermometer test/test-profile test/test-health \
+		test/test-health-sink test/service-record.dtd \
+		test/service-did.xml test/service-spp.xml test/service-opp.xml \
+		test/service-ftp.xml test/simple-player test/test-nap \
+		test/test-heartrate test/test-alert test/test-hfp \
+		test/test-cyclingspeed test/opp-client test/ftp-client \
+		test/pbap-client test/map-client test/example-advertisement \
+		test/example-gatt-server test/example-gatt-client \
+		test/test-gatt-profile
diff --git a/bluez/repo/NEWS b/bluez/repo/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bluez/repo/NEWS
diff --git a/bluez/repo/README b/bluez/repo/README
new file mode 100644
index 0000000..c991ab0
--- /dev/null
+++ b/bluez/repo/README
@@ -0,0 +1,127 @@
+BlueZ - Bluetooth protocol stack for Linux
+******************************************
+
+Copyright (C) 2000-2001  Qualcomm Incorporated
+Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+
+
+Compilation and installation
+============================
+
+In order to compile Bluetooth utilities you need following software packages:
+	- GCC compiler
+	- GLib library
+	- D-Bus library
+	- udev library (optional)
+	- readline (command line clients)
+
+To configure run:
+	./configure --prefix=/usr --mandir=/usr/share/man \
+				--sysconfdir=/etc --localstatedir=/var
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+	make && make install
+
+
+Configuration and options
+=========================
+
+For a working system, certain configuration options need to be enabled:
+
+	--enable-library
+
+		Enable installation of Bluetooth library
+
+		By default the Bluetooth library is no longer installed.
+
+		The user interfaces or command line utilities do not
+		require an installed Bluetooth library anymore. This
+		option is provided for legacy third party applications
+		that still depend on the library.
+
+		When the library installation is enabled, it is a good
+		idea to use a separate bluez-library or libbluetooth
+		package for it.
+
+	--disable-tools
+
+		Disable support for Bluetooth utilities
+
+		By default the Bluetooth utilities are built and also
+		installed. For production systems the tools are not
+		needed and this option allows to disable them to save
+		build time and disk space.
+
+		When the tools are selected, it is a good idea to
+		use a separate bluez-tools package for them.
+
+	--disable-cups
+
+		Disable support for CUPS printer backend
+
+		By default the printer backend for CUPS is build and
+		also installed. For systems that do not require printing
+		over Bluetooth, this options allows to disable it.
+
+		When the CUPS backend is selected, it is a good idea to
+		use a separate bluez-cups package for it.
+
+	--disable-monitor
+
+		Disable support for the Bluetooth monitor utility
+
+		By default the monitor utility is enabled. It provides
+		support for HCI level tracing and debugging. For systems
+		that don't require any kind of tracing or debugging
+		capabilities, this options allows to disable it.
+
+		The monitor utility should be placed in the main package
+		along with the daemons. It is universally useful.
+
+	--disable-client
+
+		Disable support for the command line client
+
+		By default the command line client is enabled and uses the
+		readline library. For specific systems where BlueZ is
+		configured by other means, the command line client can be
+		disabled and the dependency on readline is removed.
+
+		The client should be placed in the main package along
+		with the daemons. It is universally useful.
+
+	--disable-systemd
+
+		Disable integration with systemd
+
+		By default the integration with systemd is enabled and
+		installed. This gives the best integration into all
+		distributions based on systemd.
+
+		This option is provided for distributions that do not
+		support systemd. In that case all integration with the
+		init system is up to the package.
+
+	--enable-experimental
+
+		Enable experimental plugins
+
+		By default all plugins that are still in development
+		are disabled. This option can be used to enable them.
+
+		It is not recommended to enable this option for production
+		systems. The APIs or behavior of the experimental plugins
+		is unstable and might still change.
+
+
+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/bluez/repo/TODO b/bluez/repo/TODO
new file mode 100644
index 0000000..8c710ef
--- /dev/null
+++ b/bluez/repo/TODO
@@ -0,0 +1,288 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.  The complexity scale is exponential,
+  with complexity 1 being the lowest complexity.  Complexity is a function
+  of both task 'complexity' and task 'scope'.
+
+  The general rule of thumb is that a complexity 1 task should take 1-2 weeks
+  for a person very familiar with BlueZ codebase.  Higher complexity tasks
+  require more time and have higher uncertainty.
+
+  Higher complexity tasks should be refined into several lower complexity tasks
+  once the task is better understood.
+
+General
+=======
+
+- UUID handling: Use the new functions created for UUID handling in all parts
+  of BlueZ code.  Currently, the new bt_uuid_* functions are being used by
+  GATT-related code only.
+
+  Priority: high
+  Complexity: C4
+
+- Update PBAP client/server implementation to 1.2 and create necessary APIs for
+  new features it introduces.
+
+  Priority: Medium
+  Complexity: C4
+
+- Create GOEP unit tests based on its test specification:
+
+  https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=230559
+
+  Priority: Medium
+  Complexity: C2
+
+- Function in src/adapter.c to convert old storage files to new ini-file format
+  should be removed 6-8 months after first BlueZ 5 release.
+
+  Priority: Low
+  Complexity: C1
+
+- Remove usage of symlinks for drivers, such as profiles/input/suspend.c and
+  profiles/sap/sap.c. Instead, select drivers at runtime by using config
+  options or probing for running D-Bus services (using e.g.
+  g_dbus_add_service_watch()). Idea first mentioned on
+  http://thread.gmane.org/gmane.linux.bluez.kernel/30175/focus=30190.
+
+- Reuse connection handling code of src/profile.c also for built-in profiles
+  so plugins would only need to register their btd_profile and the core takes
+  care of the rest including listen to the right channel and manages the sdp
+  record. Once btd_profile manages the connection it can also notify about
+  their state, this probably remove the need of having callbacks to
+  .connect/.disconnect since their state can be tracked, it also enables any
+  plugin to track any profile state change which can be useful for e.g.
+  a connection policy plugin in case one is needed.
+
+  Priority: Low
+  Complexity: C2
+
+- Add queueing support for src/agent.c, currently if there is any request
+  pending the code fail with error EBUSY which is very inconvenient.
+
+  Priority: Low
+  Complexity: C2
+
+Low Energy
+==========
+
+- Connection modes. Adapter interface needs to be changed to manage
+  connection modes and adapter type. See Volume 3, Part C, section 9.3.
+  1. Mode management: Peripheral / Central
+
+  Priority: Medium
+  Complexity: C2
+
+- Advertising data. The D-Bus interface needs to be updated to enable setting
+  scan response data, and to read the advertising and scan response data which
+  has been broadcast from other LE devices.
+
+  Priority: Medium
+  Complexity: C2
+
+- Static random address setup and storage. Once this address is written
+  in a given remote, the address can not be changed anymore.
+
+  Priority: Low
+  Complexity: C1
+
+- Device Name Characteristic is a GAP characteristic for Low Energy. This
+  characteristic shall be integrated/used in the discovery procedure. The
+  idea is to report the value of this characteristic using DeviceFound signals.
+  Discussion with the community is needed before to start this task. Other GAP
+  characteristics for LE needs to follow a similar approach. It is not clear
+  if all GAP characteristics can be exposed using properties instead of a primary
+  service characteristics.
+  See Volume 3, Part C, section 12.1 for more information.
+
+  Priority: Low
+  Complexity: C2
+
+ATT/GATT (new shared stack)
+===========================
+
+- Add complete GATT test coverage in unit/test-gatt following the GATT test
+  spec. This could use shared/gatt-client and shared/gatt-server at the same
+  time to test both against eachother. We should definitely have tests for
+  gatt-server and gatt-client simultaneously on one side of the connection.
+
+  Priority: High
+  Complexity: C4
+
+- Write an example using client D-Bus API using C.
+
+  Priority: High
+  Complexity: C2
+
+- Write an example using client D-Bus API using python.
+
+  Priority: High
+  Complexity: C2
+
+- Define packed structs for ATT protocol PDUs in shared/att-types to improve
+  readability. We should probably do this once there are extensive unit tests
+  for gatt-client/gatt-server so that we don't accidentally break working code.
+
+  Priority: Medium
+  Complexity: C2
+
+- Use struct iovec to pass around byte buffers that will be sent over the wire,
+  instead of passing uint8_t and size_t parameters everywhere.
+
+  Priority: Medium
+  Complexity: C1
+
+- Persist client attribute cache across reboots.
+
+  Priority: Medium
+  Complexity: C4
+
+- Move all daemon plugins and profiles that are GATT based to use
+  shared/gatt-client instead of attrib/*. This is a complicated task that
+  potentially needs a new plugin/profile probing interface and a lot of
+  rewriting that can cause regressions in existing functionality.
+
+  Priority: Medium
+  Complexity: C4
+
+- Introduce a way for shared/gatt-server to check security permissions on the
+  current connection through bt_att.
+
+  Priority: Medium
+  Complexity: C2
+
+- Implement other low-priority ATT protocol operations for shared/gatt-server:
+
+      Read Multiple Request
+
+  Priority: Low
+  Complexity: C1
+
+- Implement the server portion of doc/gatt-api.txt using shared/gatt-server once
+  it exists.
+
+  Priority: Medium
+  Complexity: C4
+
+- Send out indications from the "Service Changed" characteristic upon
+  reconnection if a bonded device is not connected when the local database is
+  modified.
+
+  Priority: High
+  Complexity: C2
+
+- Unify the GATT server and client D-Bus implementations into a single module.
+  While these don't share a lot of code, keeping them all in src/gatt-dbus seems
+  to make more sense from an organizational perspective.
+
+  Priority: Low
+  Complexity: C1
+
+- Isolate all GATT code inside the daemon into its own module and perform
+  interaction with other modules (e.g. src/device.c) via callbacks. This
+  includes client/server management, tracking incoming/outgoing connections for
+  ATT, and callbacks to perform profile probing.
+
+  Priority: Low
+  Complexity: C4
+
+- Support included services in the GATT D-Bus client API.
+
+  Priority: Medium
+  Complexity: C1
+
+- The recently added support for ATT signed writes requires the following kernel
+  modules to be enabled:
+
+     CONFIG_CRYPTO_USER_API
+     CONFIG_CRYPTO_USER_API_HASH
+     CONFIG_CRYPTO_USER_API_SKCIPHER
+
+  Currently, if these are not enabled, bt_att_new silently returns NULL. We
+  should handle this more gracefully by not supporting signed writes if we can't
+  initialize bt_crypto while succeeding bt_att initialization regardless.
+
+  This behavior should be documented in the README.
+
+  Priority: High
+  Complexity: C1
+
+
+ATT/GATT (old/outdated)
+=======================
+
+- At the moment authentication and authorization is not supported at the
+  same time, read/write requirements in the attribute server needs to
+  be extended. According to Bluetooth Specification a server shall check
+  authentication and authorization requirements before any other check is
+  performed.
+
+  Priority: Medium
+  Complexity: C1
+
+- Implement ATT PDU validation. Malformed PDUs can cause division by zero
+  when decoding PDUs. A proper error PDU should be returned for this case.
+  See decoding function in att.c file.
+
+  Priority: Medium
+  Complexity: C1
+
+- Refactor read_by_group() and read_by_type() in src/attrib-server.c
+  (they've grown simply too big). First step could be to move out the
+  long for-loops to new functions called e.g. get_groups() and get_types().
+
+  Priority: Low
+  Complexity: C1
+
+- Agent for characteristics: Agent interface should be extended to support
+  authorization per characteristic if the remote is not in the trusted list.
+
+  Priority: Low
+  Complexity: C1
+
+- gatttool should have the ability to wait for req responses before
+  quitting (some servers require a small sleep even with cmd's). Maybe a
+  --delay-exit or --timeout command line switch.
+
+  Priority: Low
+  Complexity: C1
+
+- Client needs to export a property in the Device Characteristic hierarchy
+  to manage characteristic value changes reports in the remote device.
+  Currently, Client Characteristic Configuration attribute is not exposed
+  as an object. The user needs to use gatttool to change the value of the
+  this attribute to receive notification/indications. Export this attribute
+  as a property is a proposal that needs further discussion.
+
+  Priority: Low
+  Complexity: C1
+
+- Attribute server should process queued GATT/ATT commands if the
+  client disconnects. The client can simply send a command and quit,
+  without wait for a response(ex: Write Command). For this scenario
+  that the client disconnects the link quickly the queued received
+  command is ignored.
+
+  Priority: Low
+  Complecity: C1
+
+- Implement Server characteristic Configuration support in the attribute
+  server to manage characteristic value broadcasting. There is a single
+  instance of the Server Characteristic Configuration for all clients.
+  See Volume 3, Part G, section 3.3.3.4 for more information.
+
+  Priority: Low
+  Complexity: C1
+
+- Long write is not implemented. Attribute server, client and command line
+  tool shall be changed to support this feature.
+
+  Priority: Low
+  Complexity: C2
+
+Management Interface
+====================
diff --git a/bluez/repo/acinclude.m4 b/bluez/repo/acinclude.m4
new file mode 100644
index 0000000..bc39c6d
--- /dev/null
+++ b/bluez/repo/acinclude.m4
@@ -0,0 +1,62 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+	AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+		echo 'void f(){}' > conftest.c
+		if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+			ac_cv_prog_cc_pie=yes
+		else
+			ac_cv_prog_cc_pie=no
+		fi
+		rm -rf conftest*
+	])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+	with_cflags=""
+	if (test "$USE_MAINTAINER_MODE" = "yes"); then
+		with_cflags="$with_cflags -Wall -Werror -Wextra"
+		with_cflags="$with_cflags -Wno-unused-parameter"
+		with_cflags="$with_cflags -Wno-missing-field-initializers"
+		with_cflags="$with_cflags -Wdeclaration-after-statement"
+		with_cflags="$with_cflags -Wmissing-declarations"
+		with_cflags="$with_cflags -Wredundant-decls"
+		with_cflags="$with_cflags -Wcast-align"
+		with_cflags="$with_cflags -Wswitch-enum"
+		with_cflags="$with_cflags -Wformat -Wformat-security"
+		with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+		with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
+		with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
+	fi
+	AC_SUBST([WARNING_CFLAGS], $with_cflags)
+])
+
+AC_DEFUN([MISC_FLAGS], [
+	misc_cflags=""
+	misc_ldflags=""
+	AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+			[disable code optimization through compiler]), [
+		if (test "${enableval}" = "no"); then
+			misc_cflags="$misc_cflags -O0"
+		fi
+	])
+	AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+			[enable compiling with debugging information]), [
+		if (test "${enableval}" = "yes" &&
+				test "${ac_cv_prog_cc_g}" = "yes"); then
+			misc_cflags="$misc_cflags -g"
+		fi
+	])
+	AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+			[enable position independent executables flag]), [
+		if (test "${enableval}" = "yes" &&
+				test "${ac_cv_prog_cc_pie}" = "yes"); then
+			misc_cflags="$misc_cflags -fPIC"
+			misc_ldflags="$misc_ldflags -pie"
+		fi
+	])
+	if (test "$enable_coverage" = "yes"); then
+		misc_cflags="$misc_cflags --coverage"
+		misc_ldflags="$misc_ldflags --coverage"
+	fi
+	AC_SUBST([MISC_CFLAGS], $misc_cflags)
+	AC_SUBST([MISC_LDFLAGS], $misc_ldflags)
+])
diff --git a/bluez/repo/android/._Android.mk b/bluez/repo/android/._Android.mk
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._Android.mk
Binary files differ
diff --git a/bluez/repo/android/._Makefile.am b/bluez/repo/android/._Makefile.am
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._Makefile.am
Binary files differ
diff --git a/bluez/repo/android/._README b/bluez/repo/android/._README
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._README
Binary files differ
diff --git a/bluez/repo/android/._a2dp-sink.c b/bluez/repo/android/._a2dp-sink.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._a2dp-sink.c
Binary files differ
diff --git a/bluez/repo/android/._a2dp-sink.h b/bluez/repo/android/._a2dp-sink.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._a2dp-sink.h
Binary files differ
diff --git a/bluez/repo/android/._a2dp.c b/bluez/repo/android/._a2dp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._a2dp.c
Binary files differ
diff --git a/bluez/repo/android/._a2dp.h b/bluez/repo/android/._a2dp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._a2dp.h
Binary files differ
diff --git a/bluez/repo/android/._audio-ipc-api.txt b/bluez/repo/android/._audio-ipc-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._audio-ipc-api.txt
Binary files differ
diff --git a/bluez/repo/android/._audio-msg.h b/bluez/repo/android/._audio-msg.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._audio-msg.h
Binary files differ
diff --git a/bluez/repo/android/._audio_utils b/bluez/repo/android/._audio_utils
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._audio_utils
Binary files differ
diff --git a/bluez/repo/android/._avctp.c b/bluez/repo/android/._avctp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avctp.c
Binary files differ
diff --git a/bluez/repo/android/._avctp.h b/bluez/repo/android/._avctp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avctp.h
Binary files differ
diff --git a/bluez/repo/android/._avdtp.c b/bluez/repo/android/._avdtp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avdtp.c
Binary files differ
diff --git a/bluez/repo/android/._avdtp.h b/bluez/repo/android/._avdtp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avdtp.h
Binary files differ
diff --git a/bluez/repo/android/._avdtptest.c b/bluez/repo/android/._avdtptest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avdtptest.c
Binary files differ
diff --git a/bluez/repo/android/._avrcp-lib.c b/bluez/repo/android/._avrcp-lib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avrcp-lib.c
Binary files differ
diff --git a/bluez/repo/android/._avrcp-lib.h b/bluez/repo/android/._avrcp-lib.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avrcp-lib.h
Binary files differ
diff --git a/bluez/repo/android/._avrcp.c b/bluez/repo/android/._avrcp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avrcp.c
Binary files differ
diff --git a/bluez/repo/android/._avrcp.h b/bluez/repo/android/._avrcp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._avrcp.h
Binary files differ
diff --git a/bluez/repo/android/._bluetooth.c b/bluez/repo/android/._bluetooth.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._bluetooth.c
Binary files differ
diff --git a/bluez/repo/android/._bluetooth.h b/bluez/repo/android/._bluetooth.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._bluetooth.h
Binary files differ
diff --git a/bluez/repo/android/._bluetoothd-snoop.c b/bluez/repo/android/._bluetoothd-snoop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._bluetoothd-snoop.c
Binary files differ
diff --git a/bluez/repo/android/._bluetoothd-wrapper.c b/bluez/repo/android/._bluetoothd-wrapper.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._bluetoothd-wrapper.c
Binary files differ
diff --git a/bluez/repo/android/._bluetoothd.te b/bluez/repo/android/._bluetoothd.te
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._bluetoothd.te
Binary files differ
diff --git a/bluez/repo/android/._bluetoothd_snoop.te b/bluez/repo/android/._bluetoothd_snoop.te
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._bluetoothd_snoop.te
Binary files differ
diff --git a/bluez/repo/android/._client b/bluez/repo/android/._client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._client
Binary files differ
diff --git a/bluez/repo/android/._compat b/bluez/repo/android/._compat
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._compat
Binary files differ
diff --git a/bluez/repo/android/._cts.txt b/bluez/repo/android/._cts.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._cts.txt
Binary files differ
diff --git a/bluez/repo/android/._cutils b/bluez/repo/android/._cutils
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._cutils
Binary files differ
diff --git a/bluez/repo/android/._gatt.c b/bluez/repo/android/._gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._gatt.c
Binary files differ
diff --git a/bluez/repo/android/._gatt.h b/bluez/repo/android/._gatt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._gatt.h
Binary files differ
diff --git a/bluez/repo/android/._hal-a2dp-sink.c b/bluez/repo/android/._hal-a2dp-sink.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-a2dp-sink.c
Binary files differ
diff --git a/bluez/repo/android/._hal-a2dp.c b/bluez/repo/android/._hal-a2dp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-a2dp.c
Binary files differ
diff --git a/bluez/repo/android/._hal-audio-aptx.c b/bluez/repo/android/._hal-audio-aptx.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-audio-aptx.c
Binary files differ
diff --git a/bluez/repo/android/._hal-audio-sbc.c b/bluez/repo/android/._hal-audio-sbc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-audio-sbc.c
Binary files differ
diff --git a/bluez/repo/android/._hal-audio.c b/bluez/repo/android/._hal-audio.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-audio.c
Binary files differ
diff --git a/bluez/repo/android/._hal-audio.h b/bluez/repo/android/._hal-audio.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-audio.h
Binary files differ
diff --git a/bluez/repo/android/._hal-avrcp-ctrl.c b/bluez/repo/android/._hal-avrcp-ctrl.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-avrcp-ctrl.c
Binary files differ
diff --git a/bluez/repo/android/._hal-avrcp.c b/bluez/repo/android/._hal-avrcp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-avrcp.c
Binary files differ
diff --git a/bluez/repo/android/._hal-bluetooth.c b/bluez/repo/android/._hal-bluetooth.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-bluetooth.c
Binary files differ
diff --git a/bluez/repo/android/._hal-gatt.c b/bluez/repo/android/._hal-gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-gatt.c
Binary files differ
diff --git a/bluez/repo/android/._hal-handsfree-client.c b/bluez/repo/android/._hal-handsfree-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-handsfree-client.c
Binary files differ
diff --git a/bluez/repo/android/._hal-handsfree.c b/bluez/repo/android/._hal-handsfree.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-handsfree.c
Binary files differ
diff --git a/bluez/repo/android/._hal-health.c b/bluez/repo/android/._hal-health.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-health.c
Binary files differ
diff --git a/bluez/repo/android/._hal-hidhost.c b/bluez/repo/android/._hal-hidhost.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-hidhost.c
Binary files differ
diff --git a/bluez/repo/android/._hal-ipc-api.txt b/bluez/repo/android/._hal-ipc-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-ipc-api.txt
Binary files differ
diff --git a/bluez/repo/android/._hal-ipc.c b/bluez/repo/android/._hal-ipc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-ipc.c
Binary files differ
diff --git a/bluez/repo/android/._hal-ipc.h b/bluez/repo/android/._hal-ipc.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-ipc.h
Binary files differ
diff --git a/bluez/repo/android/._hal-log.h b/bluez/repo/android/._hal-log.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-log.h
Binary files differ
diff --git a/bluez/repo/android/._hal-map-client.c b/bluez/repo/android/._hal-map-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-map-client.c
Binary files differ
diff --git a/bluez/repo/android/._hal-msg.h b/bluez/repo/android/._hal-msg.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-msg.h
Binary files differ
diff --git a/bluez/repo/android/._hal-pan.c b/bluez/repo/android/._hal-pan.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-pan.c
Binary files differ
diff --git a/bluez/repo/android/._hal-sco.c b/bluez/repo/android/._hal-sco.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-sco.c
Binary files differ
diff --git a/bluez/repo/android/._hal-socket.c b/bluez/repo/android/._hal-socket.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-socket.c
Binary files differ
diff --git a/bluez/repo/android/._hal-utils.c b/bluez/repo/android/._hal-utils.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-utils.c
Binary files differ
diff --git a/bluez/repo/android/._hal-utils.h b/bluez/repo/android/._hal-utils.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal-utils.h
Binary files differ
diff --git a/bluez/repo/android/._hal.h b/bluez/repo/android/._hal.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hal.h
Binary files differ
diff --git a/bluez/repo/android/._handsfree-client.c b/bluez/repo/android/._handsfree-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._handsfree-client.c
Binary files differ
diff --git a/bluez/repo/android/._handsfree-client.h b/bluez/repo/android/._handsfree-client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._handsfree-client.h
Binary files differ
diff --git a/bluez/repo/android/._handsfree.c b/bluez/repo/android/._handsfree.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._handsfree.c
Binary files differ
diff --git a/bluez/repo/android/._handsfree.h b/bluez/repo/android/._handsfree.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._handsfree.h
Binary files differ
diff --git a/bluez/repo/android/._hardware b/bluez/repo/android/._hardware
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hardware
Binary files differ
diff --git a/bluez/repo/android/._health.c b/bluez/repo/android/._health.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._health.c
Binary files differ
diff --git a/bluez/repo/android/._health.h b/bluez/repo/android/._health.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._health.h
Binary files differ
diff --git a/bluez/repo/android/._hidhost.c b/bluez/repo/android/._hidhost.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hidhost.c
Binary files differ
diff --git a/bluez/repo/android/._hidhost.h b/bluez/repo/android/._hidhost.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._hidhost.h
Binary files differ
diff --git a/bluez/repo/android/._init.bluetooth.rc b/bluez/repo/android/._init.bluetooth.rc
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._init.bluetooth.rc
Binary files differ
diff --git a/bluez/repo/android/._ipc-common.h b/bluez/repo/android/._ipc-common.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._ipc-common.h
Binary files differ
diff --git a/bluez/repo/android/._ipc-tester.c b/bluez/repo/android/._ipc-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._ipc-tester.c
Binary files differ
diff --git a/bluez/repo/android/._ipc.c b/bluez/repo/android/._ipc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._ipc.c
Binary files differ
diff --git a/bluez/repo/android/._ipc.h b/bluez/repo/android/._ipc.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._ipc.h
Binary files differ
diff --git a/bluez/repo/android/._log.c b/bluez/repo/android/._log.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._log.c
Binary files differ
diff --git a/bluez/repo/android/._main.c b/bluez/repo/android/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._main.c
Binary files differ
diff --git a/bluez/repo/android/._map-client.c b/bluez/repo/android/._map-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._map-client.c
Binary files differ
diff --git a/bluez/repo/android/._map-client.h b/bluez/repo/android/._map-client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._map-client.h
Binary files differ
diff --git a/bluez/repo/android/._pan.c b/bluez/repo/android/._pan.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pan.c
Binary files differ
diff --git a/bluez/repo/android/._pan.h b/bluez/repo/android/._pan.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pan.h
Binary files differ
diff --git a/bluez/repo/android/._pics-a2dp.txt b/bluez/repo/android/._pics-a2dp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-a2dp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-avctp.txt b/bluez/repo/android/._pics-avctp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-avctp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-avdtp.txt b/bluez/repo/android/._pics-avdtp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-avdtp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-avrcp.txt b/bluez/repo/android/._pics-avrcp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-avrcp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-bnep.txt b/bluez/repo/android/._pics-bnep.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-bnep.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-did.txt b/bluez/repo/android/._pics-did.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-did.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-dis.txt b/bluez/repo/android/._pics-dis.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-dis.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-gap.txt b/bluez/repo/android/._pics-gap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-gap.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-gatt.txt b/bluez/repo/android/._pics-gatt.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-gatt.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-gavdp.txt b/bluez/repo/android/._pics-gavdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-gavdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-hdp.txt b/bluez/repo/android/._pics-hdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-hdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-hfp.txt b/bluez/repo/android/._pics-hfp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-hfp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-hid.txt b/bluez/repo/android/._pics-hid.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-hid.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-hogp.txt b/bluez/repo/android/._pics-hogp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-hogp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-hsp.txt b/bluez/repo/android/._pics-hsp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-hsp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-iopt.txt b/bluez/repo/android/._pics-iopt.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-iopt.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-l2cap.txt b/bluez/repo/android/._pics-l2cap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-l2cap.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-map.txt b/bluez/repo/android/._pics-map.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-map.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-mcap.txt b/bluez/repo/android/._pics-mcap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-mcap.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-mps.txt b/bluez/repo/android/._pics-mps.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-mps.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-opp.txt b/bluez/repo/android/._pics-opp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-opp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-pan.txt b/bluez/repo/android/._pics-pan.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-pan.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-pbap.txt b/bluez/repo/android/._pics-pbap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-pbap.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-rfcomm.txt b/bluez/repo/android/._pics-rfcomm.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-rfcomm.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-scpp.txt b/bluez/repo/android/._pics-scpp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-scpp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-sdp.txt b/bluez/repo/android/._pics-sdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-sdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-sm.txt b/bluez/repo/android/._pics-sm.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-sm.txt
Binary files differ
diff --git a/bluez/repo/android/._pics-spp.txt b/bluez/repo/android/._pics-spp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pics-spp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-a2dp.txt b/bluez/repo/android/._pixit-a2dp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-a2dp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-avctp.txt b/bluez/repo/android/._pixit-avctp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-avctp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-avdtp.txt b/bluez/repo/android/._pixit-avdtp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-avdtp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-avrcp.txt b/bluez/repo/android/._pixit-avrcp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-avrcp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-bnep.txt b/bluez/repo/android/._pixit-bnep.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-bnep.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-did.txt b/bluez/repo/android/._pixit-did.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-did.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-dis.txt b/bluez/repo/android/._pixit-dis.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-dis.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-gap.txt b/bluez/repo/android/._pixit-gap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-gap.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-gatt.txt b/bluez/repo/android/._pixit-gatt.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-gatt.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-gavdp.txt b/bluez/repo/android/._pixit-gavdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-gavdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-hdp.txt b/bluez/repo/android/._pixit-hdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-hdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-hfp.txt b/bluez/repo/android/._pixit-hfp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-hfp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-hid.txt b/bluez/repo/android/._pixit-hid.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-hid.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-hogp.txt b/bluez/repo/android/._pixit-hogp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-hogp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-hsp.txt b/bluez/repo/android/._pixit-hsp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-hsp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-iopt.txt b/bluez/repo/android/._pixit-iopt.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-iopt.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-l2cap.txt b/bluez/repo/android/._pixit-l2cap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-l2cap.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-map.txt b/bluez/repo/android/._pixit-map.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-map.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-mcap.txt b/bluez/repo/android/._pixit-mcap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-mcap.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-mps.txt b/bluez/repo/android/._pixit-mps.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-mps.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-opp.txt b/bluez/repo/android/._pixit-opp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-opp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-pan.txt b/bluez/repo/android/._pixit-pan.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-pan.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-pbap.txt b/bluez/repo/android/._pixit-pbap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-pbap.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-rfcomm.txt b/bluez/repo/android/._pixit-rfcomm.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-rfcomm.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-scpp.txt b/bluez/repo/android/._pixit-scpp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-scpp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-sdp.txt b/bluez/repo/android/._pixit-sdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-sdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-sm.txt b/bluez/repo/android/._pixit-sm.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-sm.txt
Binary files differ
diff --git a/bluez/repo/android/._pixit-spp.txt b/bluez/repo/android/._pixit-spp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pixit-spp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-a2dp.txt b/bluez/repo/android/._pts-a2dp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-a2dp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-avctp.txt b/bluez/repo/android/._pts-avctp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-avctp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-avdtp.txt b/bluez/repo/android/._pts-avdtp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-avdtp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-avrcp.txt b/bluez/repo/android/._pts-avrcp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-avrcp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-bnep.txt b/bluez/repo/android/._pts-bnep.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-bnep.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-did.txt b/bluez/repo/android/._pts-did.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-did.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-dis.txt b/bluez/repo/android/._pts-dis.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-dis.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-gap.txt b/bluez/repo/android/._pts-gap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-gap.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-gatt.txt b/bluez/repo/android/._pts-gatt.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-gatt.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-gavdp.txt b/bluez/repo/android/._pts-gavdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-gavdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-hdp.txt b/bluez/repo/android/._pts-hdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-hdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-hfp.txt b/bluez/repo/android/._pts-hfp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-hfp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-hid.txt b/bluez/repo/android/._pts-hid.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-hid.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-hogp.txt b/bluez/repo/android/._pts-hogp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-hogp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-hsp.txt b/bluez/repo/android/._pts-hsp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-hsp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-iopt.txt b/bluez/repo/android/._pts-iopt.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-iopt.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-l2cap.txt b/bluez/repo/android/._pts-l2cap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-l2cap.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-map.txt b/bluez/repo/android/._pts-map.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-map.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-mcap.txt b/bluez/repo/android/._pts-mcap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-mcap.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-mps.txt b/bluez/repo/android/._pts-mps.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-mps.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-opp.txt b/bluez/repo/android/._pts-opp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-opp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-pan.txt b/bluez/repo/android/._pts-pan.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-pan.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-pbap.txt b/bluez/repo/android/._pts-pbap.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-pbap.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-rfcomm.txt b/bluez/repo/android/._pts-rfcomm.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-rfcomm.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-scpp.txt b/bluez/repo/android/._pts-scpp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-scpp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-sdp.txt b/bluez/repo/android/._pts-sdp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-sdp.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-sm.txt b/bluez/repo/android/._pts-sm.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-sm.txt
Binary files differ
diff --git a/bluez/repo/android/._pts-spp.txt b/bluez/repo/android/._pts-spp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._pts-spp.txt
Binary files differ
diff --git a/bluez/repo/android/._sco-ipc-api.txt b/bluez/repo/android/._sco-ipc-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._sco-ipc-api.txt
Binary files differ
diff --git a/bluez/repo/android/._sco-msg.h b/bluez/repo/android/._sco-msg.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._sco-msg.h
Binary files differ
diff --git a/bluez/repo/android/._sco.c b/bluez/repo/android/._sco.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._sco.c
Binary files differ
diff --git a/bluez/repo/android/._sco.h b/bluez/repo/android/._sco.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._sco.h
Binary files differ
diff --git a/bluez/repo/android/._socket-api.txt b/bluez/repo/android/._socket-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._socket-api.txt
Binary files differ
diff --git a/bluez/repo/android/._socket.c b/bluez/repo/android/._socket.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._socket.c
Binary files differ
diff --git a/bluez/repo/android/._socket.h b/bluez/repo/android/._socket.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._socket.h
Binary files differ
diff --git a/bluez/repo/android/._system b/bluez/repo/android/._system
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._system
Binary files differ
diff --git a/bluez/repo/android/._system-emulator.c b/bluez/repo/android/._system-emulator.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._system-emulator.c
Binary files differ
diff --git a/bluez/repo/android/._test-ipc.c b/bluez/repo/android/._test-ipc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._test-ipc.c
Binary files differ
diff --git a/bluez/repo/android/._tester-a2dp.c b/bluez/repo/android/._tester-a2dp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-a2dp.c
Binary files differ
diff --git a/bluez/repo/android/._tester-avrcp.c b/bluez/repo/android/._tester-avrcp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-avrcp.c
Binary files differ
diff --git a/bluez/repo/android/._tester-bluetooth.c b/bluez/repo/android/._tester-bluetooth.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-bluetooth.c
Binary files differ
diff --git a/bluez/repo/android/._tester-gatt.c b/bluez/repo/android/._tester-gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-gatt.c
Binary files differ
diff --git a/bluez/repo/android/._tester-hdp.c b/bluez/repo/android/._tester-hdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-hdp.c
Binary files differ
diff --git a/bluez/repo/android/._tester-hidhost.c b/bluez/repo/android/._tester-hidhost.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-hidhost.c
Binary files differ
diff --git a/bluez/repo/android/._tester-main.c b/bluez/repo/android/._tester-main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-main.c
Binary files differ
diff --git a/bluez/repo/android/._tester-main.h b/bluez/repo/android/._tester-main.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-main.h
Binary files differ
diff --git a/bluez/repo/android/._tester-map-client.c b/bluez/repo/android/._tester-map-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-map-client.c
Binary files differ
diff --git a/bluez/repo/android/._tester-pan.c b/bluez/repo/android/._tester-pan.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-pan.c
Binary files differ
diff --git a/bluez/repo/android/._tester-socket.c b/bluez/repo/android/._tester-socket.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._tester-socket.c
Binary files differ
diff --git a/bluez/repo/android/._utils.h b/bluez/repo/android/._utils.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/._utils.h
Binary files differ
diff --git a/bluez/repo/android/Android.mk b/bluez/repo/android/Android.mk
new file mode 100644
index 0000000..38ef4aa
--- /dev/null
+++ b/bluez/repo/android/Android.mk
@@ -0,0 +1,855 @@
+LOCAL_PATH := external/bluetooth
+
+# Retrieve BlueZ version from configure.ac file
+BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"`
+
+ANDROID_VERSION := $(shell echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }')
+
+ANDROID_GE_5_0_0 := $(shell test `echo $$(($(ANDROID_VERSION)))` -lt `echo $$((0x050000))`; echo $$?)
+
+# Specify pathmap for glib and sbc
+pathmap_INCL += glib:external/bluetooth/glib \
+		sbc:external/bluetooth/sbc \
+
+# Specify common compiler flags
+BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
+			-DANDROID_VERSION=$(ANDROID_VERSION) \
+			-DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
+			-DHAVE_LINUX_IF_ALG_H \
+			-DHAVE_LINUX_TYPES_H \
+
+# Enable warnings enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wall -Wextra \
+			-Wdeclaration-after-statement \
+			-Wmissing-declarations \
+			-Wredundant-decls \
+			-Wcast-align \
+
+# Disable warnings enabled by Android but not enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \
+			-Wno-missing-field-initializers \
+			-Wno-unused-parameter \
+
+#
+# Android BlueZ daemon (bluetoothd)
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/main.c \
+	bluez/android/bluetooth.c \
+	bluez/profiles/scanparam/scpp.c \
+	bluez/profiles/deviceinfo/dis.c \
+	bluez/profiles/battery/bas.c \
+	bluez/profiles/input/hog-lib.c \
+	bluez/android/hidhost.c \
+	bluez/android/socket.c \
+	bluez/android/ipc.c \
+	bluez/android/avdtp.c \
+	bluez/android/a2dp.c \
+	bluez/android/a2dp-sink.c \
+	bluez/android/avctp.c \
+	bluez/android/avrcp.c \
+	bluez/android/avrcp-lib.c \
+	bluez/android/pan.c \
+	bluez/android/handsfree.c \
+	bluez/android/handsfree-client.c \
+	bluez/android/gatt.c \
+	bluez/android/health.c \
+	bluez/android/sco.c \
+	bluez/profiles/health/mcap.c \
+	bluez/android/map-client.c \
+	bluez/android/log.c \
+	bluez/src/shared/mgmt.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/ringbuf.c \
+	bluez/src/shared/hfp.c \
+	bluez/src/shared/gatt-db.c \
+	bluez/src/shared/io-glib.c \
+	bluez/src/shared/timeout-glib.c \
+	bluez/src/shared/crypto.c \
+	bluez/src/shared/uhid.c \
+	bluez/src/shared/att.c \
+	bluez/src/sdpd-database.c \
+	bluez/src/sdpd-service.c \
+	bluez/src/sdpd-request.c \
+	bluez/src/sdpd-server.c \
+	bluez/src/uuid-helper.c \
+	bluez/src/eir.c \
+	bluez/lib/sdp.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/lib/uuid.c \
+	bluez/btio/btio.c \
+	bluez/src/sdp-client.c \
+	bluez/profiles/network/bnep.c \
+	bluez/attrib/gattrib.c \
+	bluez/attrib/gatt.c \
+	bluez/attrib/att.c
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+
+# for userdebug/eng this module is bluetoothd-main since bluetoothd is used as
+# wrapper to launch bluetooth with Valgrind
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_MODULE := bluetoothd-main
+LOCAL_STRIP_MODULE := false
+else
+LOCAL_MODULE := bluetoothd
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetooth.default.so HAL
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/hal-ipc.c \
+	bluez/android/hal-bluetooth.c \
+	bluez/android/hal-socket.c \
+	bluez/android/hal-hidhost.c \
+	bluez/android/hal-pan.c \
+	bluez/android/hal-a2dp.c \
+	bluez/android/hal-avrcp.c \
+	bluez/android/hal-handsfree.c \
+	bluez/android/hal-gatt.c \
+	bluez/android/hal-utils.c \
+	bluez/android/hal-health.c \
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_SRC_FILES += \
+	bluez/android/hal-handsfree-client.c \
+	bluez/android/hal-map-client.c \
+	bluez/android/hal-a2dp-sink.c \
+	bluez/android/hal-avrcp-ctrl.c
+endif
+
+LOCAL_C_INCLUDES += \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE := bluetooth.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# haltest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/client/haltest.c \
+	bluez/android/client/pollhandler.c \
+	bluez/android/client/terminal.c \
+	bluez/android/client/history.c \
+	bluez/android/client/tabcompletion.c \
+	bluez/android/client/if-audio.c \
+	bluez/android/client/if-sco.c \
+	bluez/android/client/if-av.c \
+	bluez/android/client/if-rc.c \
+	bluez/android/client/if-bt.c \
+	bluez/android/client/if-hf.c \
+	bluez/android/client/if-hh.c \
+	bluez/android/client/if-pan.c \
+	bluez/android/client/if-hl.c \
+	bluez/android/client/if-sock.c \
+	bluez/android/client/if-gatt.c \
+	bluez/android/hal-utils.c \
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_SRC_FILES += \
+	bluez/android/client/if-hf-client.c \
+	bluez/android/client/if-mce.c \
+	bluez/android/client/if-av-sink.c \
+	bluez/android/client/if-rc-ctrl.c
+endif
+
+LOCAL_C_INCLUDES += \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez/android \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+
+LOCAL_SHARED_LIBRARIES := \
+	libhardware \
+	libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := haltest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# mcaptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/src/log.c \
+	bluez/btio/btio.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/profiles/health/mcap.c \
+	bluez/tools/mcaptest.c \
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := mcaptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bneptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/src/log.c \
+	bluez/btio/btio.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/profiles/network/bnep.c \
+	bluez/tools/bneptest.c \
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := bneptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avdtptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/avdtptest.c \
+	bluez/android/avdtp.c \
+	bluez/src/log.c \
+	bluez/btio/btio.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/queue.c \
+
+LOCAL_C_INCLUDES += \
+	$(LOCAL_PATH)/bluez \
+	$(call include-path-for, glib) \
+	$(call include-path-for, glib)/glib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avdtptest
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btmon
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/monitor/main.c \
+	bluez/monitor/display.c \
+	bluez/monitor/hcidump.c \
+	bluez/monitor/control.c \
+	bluez/monitor/packet.c \
+	bluez/monitor/l2cap.c \
+	bluez/monitor/avctp.c \
+	bluez/monitor/avdtp.c \
+	bluez/monitor/a2dp.c \
+	bluez/monitor/rfcomm.c \
+	bluez/monitor/bnep.c \
+	bluez/monitor/uuid.c \
+	bluez/monitor/sdp.c \
+	bluez/monitor/vendor.c \
+	bluez/monitor/lmp.c \
+	bluez/monitor/crc.c \
+	bluez/monitor/ll.c \
+	bluez/monitor/hwdb.c \
+	bluez/monitor/keys.c \
+	bluez/monitor/ellisys.c \
+	bluez/monitor/analyze.c \
+	bluez/monitor/intel.c \
+	bluez/monitor/broadcom.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/crypto.c \
+	bluez/src/shared/btsnoop.c \
+	bluez/src/shared/mainloop.c \
+	bluez/lib/hci.c \
+	bluez/lib/bluetooth.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmon
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btproxy
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/btproxy.c \
+	bluez/src/shared/mainloop.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/ecc.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btproxy
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# A2DP audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/hal-audio.c \
+	bluez/android/hal-audio-sbc.c \
+	bluez/android/hal-audio-aptx.c \
+
+LOCAL_C_INCLUDES = \
+	$(LOCAL_PATH)/bluez \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+	$(call include-path-for, sbc) \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libsbc \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.a2dp.default
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# SCO audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-sco.c \
+	bluez/android/hal-utils.c
+
+LOCAL_C_INCLUDES = \
+	$(call include-path-for, system-core) \
+	$(call include-path-for, libhardware) \
+	$(call include-path-for, audio-utils) \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libaudioutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.sco.default
+
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# l2cap-test
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/l2test.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2test
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetoothd-snoop
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/bluetoothd-snoop.c \
+	bluez/src/shared/mainloop.c \
+	bluez/src/shared/btsnoop.c \
+	bluez/android/log.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd-snoop
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# init.bluetooth.rc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init.bluetooth.rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
+
+#
+# btmgmt
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/btmgmt.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/lib/sdp.c \
+	bluez/src/shared/mainloop.c \
+	bluez/src/shared/io-mainloop.c \
+	bluez/src/shared/mgmt.c \
+	bluez/src/shared/queue.c \
+	bluez/src/shared/util.c \
+	bluez/src/shared/gap.c \
+	bluez/src/uuid-helper.c \
+	bluez/client/display.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+	$(LOCAL_PATH)/bluez/android/compat \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmgmt
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hcitool
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/hcitool.c \
+	bluez/src/oui.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hcitool
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciconfig
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	bluez/tools/hciconfig.c \
+	bluez/tools/csr.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hciconfig
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# l2ping
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/l2ping.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2ping
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/avtest.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avtest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciattach
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/hciattach.c \
+	bluez/tools/hciattach_st.c \
+	bluez/tools/hciattach_ti.c \
+	bluez/tools/hciattach_tialt.c \
+	bluez/tools/hciattach_ath3k.c \
+	bluez/tools/hciattach_qualcomm.c \
+	bluez/tools/hciattach_intel.c \
+	bluez/tools/hciattach_bcm43xx.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hciattach
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# libsbc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	sbc/sbc/sbc.c \
+	sbc/sbc/sbc_primitives.c \
+	sbc/sbc/sbc_primitives_mmx.c \
+	sbc/sbc/sbc_primitives_neon.c \
+	sbc/sbc/sbc_primitives_armv6.c \
+	sbc/sbc/sbc_primitives_iwmmxt.c \
+
+LOCAL_C_INCLUDES:= \
+	$(LOCAL_PATH)/sbc \
+
+LOCAL_CFLAGS:= \
+	-Os \
+	-Wno-sign-compare \
+	-Wno-missing-field-initializers \
+	-Wno-unused-parameter \
+	-Wno-type-limits \
+	-Wno-empty-body \
+
+LOCAL_MODULE := libsbc
+
+include $(BUILD_SHARED_LIBRARY)
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+
+#
+# bluetoothd (debug)
+# this is just a wrapper used in userdebug/eng to launch bluetoothd-main
+# with/without Valgrind
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/android/bluetoothd-wrapper.c \
+	bluez/android/hal-utils.c
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd
+
+LOCAL_REQUIRED_MODULES := \
+	bluetoothd-main \
+	valgrind \
+	memcheck-$(TARGET_ARCH)-linux \
+	vgpreload_core-$(TARGET_ARCH)-linux \
+	vgpreload_memcheck-$(TARGET_ARCH)-linux \
+	default.supp
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+#
+# bluetooth-headers
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bluetooth-headers
+LOCAL_NODULE_TAGS := optional
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+include_path := $(local-intermediates-dir)/include
+include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h)
+$(shell mkdir -p $(include_path)/bluetooth)
+$(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth))
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path)
+
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/avinfo.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avinfo
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# rctest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	bluez/tools/rctest.c \
+	bluez/lib/bluetooth.c \
+	bluez/lib/hci.c \
+	bluez/lib/sdp.c \
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+	bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := rctest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
diff --git a/bluez/repo/android/Makefile.am b/bluez/repo/android/Makefile.am
new file mode 100644
index 0000000..154f8db
--- /dev/null
+++ b/bluez/repo/android/Makefile.am
@@ -0,0 +1,320 @@
+if ANDROID
+
+AM_CFLAGS += -DANDROID_VERSION=0x050100
+
+android_plugindir = $(abs_top_srcdir)/android/.libs
+
+noinst_PROGRAMS += android/system-emulator
+
+android_system_emulator_SOURCES = android/system-emulator.c
+android_system_emulator_LDADD = src/libshared-mainloop.la
+
+noinst_PROGRAMS += android/bluetoothd-snoop
+
+android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c src/log.c
+android_bluetoothd_snoop_LDADD = src/libshared-mainloop.la @GLIB_LIBS@
+
+noinst_PROGRAMS += android/bluetoothd
+
+android_bluetoothd_SOURCES = android/main.c \
+				src/log.c \
+				android/hal-msg.h \
+				android/audio-msg.h \
+				android/sco-msg.h \
+				android/utils.h \
+				src/sdpd-database.c src/sdpd-server.c \
+				src/sdpd-service.c src/sdpd-request.c \
+				src/uuid-helper.h src/uuid-helper.c \
+				src/eir.h src/eir.c \
+				android/bluetooth.h android/bluetooth.c \
+				android/hidhost.h android/hidhost.c \
+				profiles/scanparam/scpp.h \
+				profiles/scanparam/scpp.c \
+				profiles/deviceinfo/dis.h \
+				profiles/deviceinfo/dis.c \
+				profiles/battery/bas.h profiles/battery/bas.c \
+				profiles/input/hog-lib.h \
+				profiles/input/hog-lib.c \
+				android/ipc-common.h \
+				android/ipc.h android/ipc.c \
+				android/avdtp.h android/avdtp.c \
+				android/a2dp.h android/a2dp.c \
+				android/a2dp-sink.h android/a2dp-sink.c \
+				android/avctp.h android/avctp.c \
+				android/avrcp.h android/avrcp.c \
+				android/avrcp-lib.h android/avrcp-lib.c \
+				android/socket.h android/socket.c \
+				android/sco.h android/sco.c \
+				android/pan.h android/pan.c \
+				android/handsfree.h android/handsfree.c \
+				android/handsfree-client.c android/handsfree-client.h \
+				android/gatt.h android/gatt.c \
+				android/health.h android/health.c \
+				profiles/health/mcap.h profiles/health/mcap.c \
+				android/map-client.h android/map-client.c \
+				attrib/att.c attrib/att.h \
+				attrib/gatt.c attrib/gatt.h \
+				attrib/gattrib.c attrib/gattrib.h \
+				btio/btio.h btio/btio.c \
+				src/sdp-client.h src/sdp-client.c \
+				profiles/network/bnep.h profiles/network/bnep.c
+android_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/bluetooth.default.la
+
+android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \
+					android/hal-socket.c \
+					android/hal-hidhost.c \
+					android/hal-health.c \
+					android/hal-pan.c \
+					android/hal-a2dp.c \
+					android/hal-a2dp-sink.c \
+					android/hal-avrcp.c \
+					android/hal-avrcp-ctrl.c \
+					android/hal-handsfree.c \
+					android/hal-handsfree-client.c \
+					android/hal-gatt.c \
+					android/hal-map-client.c \
+					android/hardware/bluetooth.h \
+					android/hardware/bt_av.h \
+					android/hardware/bt_gatt.h \
+					android/hardware/bt_gatt_client.h \
+					android/hardware/bt_gatt_server.h \
+					android/hardware/bt_gatt_types.h \
+					android/hardware/bt_hf.h \
+					android/hardware/bt_hh.h \
+					android/hardware/bt_hl.h \
+					android/hardware/bt_pan.h \
+					android/hardware/bt_rc.h \
+					android/hardware/bt_sock.h \
+					android/hardware/bt_hf_client.h \
+					android/hardware/bt_mce.h \
+					android/hardware/hardware.h \
+					android/cutils/properties.h \
+					android/ipc-common.h \
+					android/hal-log.h \
+					android/hal-ipc.h android/hal-ipc.c \
+					android/hal-utils.h android/hal-utils.c
+android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined
+
+noinst_PROGRAMS += android/avdtptest
+
+android_avdtptest_SOURCES = android/avdtptest.c \
+				src/log.h src/log.c \
+				btio/btio.h btio/btio.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/queue.h src/shared/queue.c \
+				android/avdtp.h android/avdtp.c
+android_avdtptest_CFLAGS = $(AM_CFLAGS)
+android_avdtptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+noinst_PROGRAMS += android/haltest
+
+android_haltest_SOURCES = android/client/haltest.c \
+				android/client/pollhandler.h \
+				android/client/pollhandler.c \
+				android/client/terminal.h \
+				android/client/terminal.c \
+				android/client/history.h \
+				android/client/history.c \
+				android/client/tabcompletion.c \
+				android/client/if-main.h \
+				android/client/if-av.c \
+				android/client/if-av-sink.c \
+				android/client/if-rc.c \
+				android/client/if-rc-ctrl.c \
+				android/client/if-bt.c \
+				android/client/if-gatt.c \
+				android/client/if-hf.c \
+				android/client/if-hf-client.c \
+				android/client/if-hh.c \
+				android/client/if-pan.c \
+				android/client/if-hl.c \
+				android/client/if-sock.c \
+				android/client/if-audio.c \
+				android/client/if-sco.c \
+				android/client/if-mce.c \
+				android/hardware/hardware.c \
+				android/hal-utils.h android/hal-utils.c
+android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+				-DPLUGINDIR=\""$(android_plugindir)"\"
+android_haltest_LDFLAGS = -pthread -ldl -lm
+
+noinst_PROGRAMS += android/android-tester
+
+android_android_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				monitor/rfcomm.h \
+				android/hardware/hardware.c \
+				android/tester-bluetooth.c \
+				android/tester-socket.c \
+				android/tester-hidhost.c \
+				android/tester-pan.c \
+				android/tester-hdp.c \
+				android/tester-a2dp.c \
+				android/tester-avrcp.c \
+				android/tester-gatt.c \
+				android/tester-map-client.c \
+				android/tester-main.h android/tester-main.c
+android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+				-DPLUGINDIR=\""$(android_plugindir)"\"
+android_android_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+android_android_tester_LDFLAGS = -pthread -ldl
+
+noinst_PROGRAMS += android/ipc-tester
+
+android_ipc_tester_SOURCES = emulator/hciemu.h emulator/hciemu.c \
+				emulator/btdev.h emulator/btdev.c \
+				emulator/bthost.h emulator/bthost.c \
+				emulator/smp.c \
+				android/hal-utils.h android/hal-utils.c \
+				android/ipc-common.h android/ipc-tester.c
+android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_ipc_tester_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-glib.la @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/audio.a2dp.default.la
+
+android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+					android/hal-msg.h \
+					android/hal-audio.h \
+					android/hal-audio.c \
+					android/hal-audio-sbc.c \
+					android/hal-audio-aptx.c \
+					android/hardware/audio.h \
+					android/hardware/audio_effect.h \
+					android/hardware/hardware.h \
+					android/system/audio.h
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+					@SBC_CFLAGS@
+android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined -pthread -lrt
+
+plugin_LTLIBRARIES += android/audio.sco.default.la
+
+android_audio_sco_default_la_SOURCES = android/hal-log.h \
+					android/sco-msg.h \
+					android/hal-sco.c \
+					android/hardware/audio.h \
+					android/hardware/audio_effect.h \
+					android/hardware/hardware.h \
+					android/audio_utils/resampler.c \
+					android/audio_utils/resampler.h \
+					android/system/audio.h
+android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@
+android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+					-no-undefined -lrt
+unit_tests += android/test-ipc
+
+android_test_ipc_SOURCES = android/test-ipc.c \
+				src/log.h src/log.c \
+				android/ipc-common.h \
+				android/ipc.c android/ipc.h
+android_test_ipc_LDADD = src/libshared-glib.la @GLIB_LIBS@
+
+endif
+
+EXTRA_DIST += android/Android.mk android/README \
+				android/compat/readline/history.h \
+				android/compat/readline/readline.h \
+				android/compat/wordexp.h \
+				android/bluetoothd-wrapper.c \
+				android/log.c \
+				android/bluetoothd.te \
+				android/bluetoothd_snoop.te \
+				android/init.bluetooth.rc \
+				android/hal-ipc-api.txt \
+				android/audio-ipc-api.txt \
+				android/cts.txt \
+				android/pics-rfcomm.txt \
+				android/pics-spp.txt \
+				android/pics-sdp.txt \
+				android/pics-l2cap.txt \
+				android/pics-gap.txt \
+				android/pics-did.txt \
+				android/pics-hid.txt \
+				android/pics-pan.txt \
+				android/pics-opp.txt \
+				android/pics-map.txt \
+				android/pics-pbap.txt \
+				android/pics-a2dp.txt \
+				android/pics-avctp.txt \
+				android/pics-avrcp.txt \
+				android/pics-hsp.txt \
+				android/pics-hfp.txt \
+				android/pics-gatt.txt \
+				android/pics-mcap.txt \
+				android/pics-hdp.txt \
+				android/pics-iopt.txt \
+				android/pics-sm.txt \
+				android/pics-mps.txt \
+				android/pics-hogp.txt \
+				android/pics-scpp.txt \
+				android/pics-dis.txt \
+				android/pics-avdtp.txt \
+				android/pics-gavdp.txt \
+				android/pics-bnep.txt \
+				android/pixit-l2cap.txt \
+				android/pixit-gap.txt \
+				android/pixit-did.txt \
+				android/pixit-hid.txt \
+				android/pixit-pan.txt \
+				android/pixit-opp.txt \
+				android/pixit-map.txt \
+				android/pixit-pbap.txt \
+				android/pixit-a2dp.txt \
+				android/pixit-avctp.txt \
+				android/pixit-avrcp.txt \
+				android/pixit-hsp.txt \
+				android/pixit-hfp.txt \
+				android/pixit-gatt.txt \
+				android/pixit-mcap.txt \
+				android/pixit-hdp.txt \
+				android/pixit-iopt.txt \
+				android/pixit-sm.txt \
+				android/pixit-mps.txt \
+				android/pixit-hogp.txt \
+				android/pixit-scpp.txt \
+				android/pixit-dis.txt \
+				android/pixit-rfcomm.txt \
+				android/pixit-spp.txt \
+				android/pixit-avdtp.txt \
+				android/pixit-gavdp.txt \
+				android/pixit-sdp.txt \
+				android/pixit-bnep.txt \
+				android/pts-rfcomm.txt \
+				android/pts-spp.txt \
+				android/pts-l2cap.txt \
+				android/pts-gap.txt \
+				android/pts-did.txt \
+				android/pts-hid.txt \
+				android/pts-pan.txt \
+				android/pts-opp.txt \
+				android/pts-map.txt \
+				android/pts-a2dp.txt \
+				android/pts-avrcp.txt \
+				android/pts-avctp.txt \
+				android/pts-pbap.txt \
+				android/pts-hfp.txt \
+				android/pts-gatt.txt \
+				android/pts-hsp.txt \
+				android/pts-iopt.txt \
+				android/pts-hdp.txt \
+				android/pts-mcap.txt \
+				android/pts-mps.txt \
+				android/pts-sm.txt \
+				android/pts-hogp.txt \
+				android/pts-scpp.txt \
+				android/pts-dis.txt \
+				android/pts-avdtp.txt \
+				android/pts-gavdp.txt \
+				android/pts-sdp.txt \
+				android/pts-bnep.txt
diff --git a/bluez/repo/android/README b/bluez/repo/android/README
new file mode 100644
index 0000000..fa4c42a
--- /dev/null
+++ b/bluez/repo/android/README
@@ -0,0 +1,454 @@
+BlueZ for Android
+*****************
+
+Since Android 4.2 there exists a well standardized HAL interface that the
+Bluetooth stack is expected to provide and which enables the easy replacement
+of the stack of choice on Android. Android BlueZ is intended as a drop-in
+replacement to Android provided Bluetooth stack.
+
+More details about BlueZ for Android architecture and components can be found
+in android/hal-ipc-api.txt file.
+
+Supported Android version: 4.4 KitKat and 5.0, 5.1 Lollipop
+
+
+Building and running on Android
+===============================
+
+Steps needed to build and run Android Open Source Project with integrated BlueZ.
+
+
+Build requirements
+------------------
+
+- GLib - Android 4.2 or later don't provide GLib and one must provide it in
+'external/bluetooth/glib' folder of Android tree. Sample Android GLib port
+is available at https://github.com/bluez-android/glib
+
+- SBC - A2DP code requires SBC library (version 1.2 or higher) present in
+'external/bluetooth/sbc' directory. Library is build from Android.mk provided
+by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc
+
+- Bionic support - Android 5.0 provides all required functionality. Running
+BlueZ on Android 4.4 requires backporting missing features (epoll_create1 and
+ppoll calls). Sample Bionic for Android 4.4 with all required features
+backported is available at
+https://github.com/bluez-android/aosp_platform_bionic
+
+Runtime requirements
+--------------------
+
+BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be
+available on Android system. Some permissions settings are also required.
+
+This can be done by importing init.bluetooth.rc file in init.rc file of targeted
+board:
+import init.bluetooth.rc
+
+For convenience examples are provided at:
+https://github.com/bluez-android/aosp_device_lge_mako           (Nexus 4)
+https://github.com/bluez-android/aosp_device_lge_hammerhead     (Nexus 5)
+https://github.com/bluez-android/aosp_device_asus_flo           (Nexus 7 2013)
+
+Security-Enhanced Linux in Android
+----------------------------------
+
+Since 5.0 release Android moved to full enforcement of SELinux. This requires
+proper policy to be provided for all BlueZ for Android services (and services
+interacting with BlueZ). Policies should be placed in external/selinux/ path.
+
+Required policy files are provided at:
+bluetoothd.te
+bluetoothd_snoop.te
+
+For convenience sepolicy.git with all required policies is available at:
+https://github.com/bluez-android/aosp_platform_external_sepolicy
+
+Downloading and building
+------------------------
+
+Building for Android requires full Android AOSP source tree. Sample Android tree
+with all required components present is available at
+https://github.com/bluez-android
+
+This tree provides support for Nexus4 (mako), Nexus 5 (hammerhead) and
+Nexus 7 2013 (flo, deb). Tree does not provide binary blobs needed to run
+Android on supported devices. Those can be obtained from
+https://developers.google.com/android/nexus/drivers. Binary blobs needs to be
+unpacked (EULA acceptance required) into 'vendor' directory of Android tree.
+
+Downloading:
+Android 5.0 - 'lollipop' branch
+Android 4.4 - 'kitkat' branch
+
+repo init -u https://github.com/bluez-android/aosp_platform_manifest \
+	-b lollipop
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch aosp_<target>-userdebug
+make -j8
+
+Flashing:
+adb reboot bootloader
+fastboot flashall -w
+
+After full build is done it is possible to rebuild only BlueZ:
+'cd external/bluetooth/bluez/android/'
+'mm' (or 'mm -B' to force rebuilding of all files)
+'adb sync' to update target device.
+
+Downloading and building for Intel devices
+------------------------------------------
+
+Sample Android tree with all required components for Intel devices based on
+Intel reference image (https://01.org/android-ia) can be reconstructed following
+instructions below.
+
+This tree provides support for Dell XPS12, Minnowboard MAX, Intel NUC,
+Acer Iconia W700 and other devices mentioned in:
+https://01.org/android-ia/guides/devices
+
+Downloading:
+repo init -u https://github.com/01org/android-bluez-manifest.git -b android-ia \
+	-m topic/bluez
+repo sync
+
+Building:
+source build/envsetup.sh
+lunch haswell_generic-eng
+make -j8
+
+Installing:
+Live and Install image is $OUT/live.img
+Flash live.img to USB flash and boot from it. More instructions here:
+https://01.org/android-ia/guides/developers/build-and-install
+
+Linux Kernel requirements
+-------------------------
+
+BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in
+kernel. Minimal required version of management interface is 1.3. This
+corresponds to Linux 3.9 but latest available version is recommended. Other
+requirements include UHID and network bridge support.
+
+Following kernel options should be enabled:
+CONFIG_BT
+CONFIG_BT_RFCOMM
+CONFIG_BT_RFCOMM_TTY
+CONFIG_BT_BNEP
+CONFIG_BT_BNEP_MC_FILTER
+CONFIG_BT_BNEP_PROTO_FILTER
+CONFIG_BRIDGE
+CONFIG_UHID
+CONFIG_CRYPTO_CMAC
+CONFIG_CRYPTO_USER_API
+CONFIG_CRYPTO_USER_API_HASH
+CONFIG_CRYPTO_USER_API_SKCIPHER
+
+Also BT chip driver needs to be enabled e.g:
+CONFIG_BT_HCIBTUSB
+
+If it is not possible to use new enough Linux kernel one can use updated
+bluetooth subsystem from Backports project. More information about Backports can
+be found at https://backports.wiki.kernel.org. Sample kernels using backports
+for running BlueZ on Android are available at https://github.com/bluez-android.
+
+
+Running with Valgrind
+---------------------
+
+BlueZ for Android is preconfigured to be easily run under Valgrind memcheck.
+Appropriate configuration and required modules are automatically included when
+building either userdebug or eng variant of Android platform.
+
+Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind"
+property to either literal "true" or any numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.valgrind true
+
+After changing property value Bluetooth need to be restarted to apply changes
+(this can be done using UI, just disable and enable it again). Property is
+persistent, i.e. there's no need to enable Valgrind again after reboot.
+
+It's recommended to have unstripped libglib.so installed which will enable
+complete backtraces in Valgrind output. Otherwise, in many cases backtrace
+will break at e.g. g_free() function without prior callers. It's possible to
+have proper library installed automatically by appropriate entry in Android.mk,
+see https://github.com/bluez-android/glib for an example.
+
+When running with valgrind SElinux needs to be set into permissive mode. This
+can be done by executing 'setenforce 0' from root shell.
+
+
+Enabling BlueZ debugs
+---------------------
+
+BlueZ debug logs can be enabled in runtime by setting
+"persist.sys.bluetooth.debug" property to either literal "true" or any
+numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.debug 1
+
+After changing property value Bluetooth needs to be restarted to apply changes.
+
+There is also a possibility to enable mgmt debug logs which also enables debugs
+as above. To enable it proceed in the same way as described above but use
+system properties called: persist.sys.bluetooth.mgmtdbg
+
+Note: Debugs are only available on NON USER build variants
+
+
+Customization
+-------------
+
+It is possible to customize BlueZ for Android through Android system properties.
+This may include enabling extra profiles or features inside HALs implementation
+These properties are read on Bluetooth stack startup only and require stack
+restart if changed. All customization properties names start with
+"persist.sys.bluetooth." or "ro.bluetooth." followed by specific HAL name e.g.
+"persist.sys.bluetooth.handsfree". If both are present "persist.sys.bluetooth."
+takes precedence. This allows for read only properties to be set during build
+leaving enough flexibility for developing or debugging purposes.
+This section list available customization options.
+
+Property	Value		Description
+-------------------------------------------
+mode		bredr		Enable BlueZ in BR/EDR mode
+		le		Enable BlueZ in LE mode
+		<none>		Enable BlueZ in default mode - enable BR/EDR/LE
+				if available.
+handsfree	hfp		Enable Handsfree Profile (HFP) with narrowband
+				speech only
+		hfp_wbs		Enable Handsfree Profile (HFP) with narrowband
+				and wideband speech support
+		<none>		Don't enable Handsfree Profile (HFP)
+vendor		<any>		Set vendor name in DIS. If not set fallback to
+				"ro.product.manufacturer".
+model		<any>		Set model name used as default adapter name.
+				If not set fallback to "ro.product.model".
+name		<any>		Set model number in DIS. If not set fallback to
+				"ro.product.name".
+serialno	<any>		Set serial number in DIS. If not set fallback to
+				"ro.serialno".
+systemid	<uint64>	Set system ID in DIS. Hex string encoded uint64.
+pnpid		<any>		PnP information used in DIS and DID profiles.
+				Required format: "Source:VID:PID:Version".
+				Source must be either "bluetooth" or "usb".
+				VID, PID and Version are uint16. Version is
+				optional.
+fwrev		<any>		Firmware revision in DIS. If not set fallback to
+				"ro.build.version.release".
+hwrew		<any>		Hardware revision in DIS. If not set fallback to
+				"ro.board.platform".
+
+
+Building and running on Linux
+-----------------------------
+
+It is possible to build and test BlueZ for Android daemon on Linux (eg. PC).
+Simply follow instructions available at README file in BlueZ top directory.
+Android daemon binary is located at android/bluetoothd. See next section on
+how to test Android daemon on Linux.
+
+
+Testing tool
+------------
+
+BT HAL test tools located in android/haltest is provided for HAL level testing
+of both Android daemon and HAL library. Start it with '-n' parameter and type
+'bluetooth init' in prompt to initialize HAL library. Running without parameter
+will make haltest try to initialize all services after start. On Android
+required bluetoothd service will be started automatically. On Linux it is
+required to start android/bluetoothd manually before init command timeout or
+use provided android/system-emulator, which takes care of launching daemon
+automatically on HAL library initialization. To deinitialize HAL library and
+stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab
+completion is also supported.
+
+
+Implementation status
+=====================
+
+Summary of HALs implementation status.
+
+complete    - implementation is feature complete and Android Framework is able
+              to use it normally
+partial     - implementation is in progress and not all required features are
+              present, Android Framework is able to use some of features
+initial     - only initial implementations is present, Android Framework is
+              able to initialize but most likely not able to use it
+not started - no implementation, Android Framework is not able to initialize it
+
+Profile ID        HAL header         4.4 Status    5.0 status
+-------------------------------------------------------------
+core              bluetooth.h        complete      partial
+a2dp              bt_av.h            complete      complete
+gatt              bt_gatt.h          complete      partial
+                  bt_gatt_client.h   complete      partial
+                  bt_gatt_server.h   complete      partial
+handsfree         bt_hf.h            complete      complete
+hidhost           bt_hh.h            complete      complete
+health            bt_hl.h            complete      complete
+pan               bt_pan.h           complete      complete
+avrcp             bt_rc.h            complete      complete
+socket            bt_sock.h          complete      partial
+handsfree_client  bt_hf_client.h     N/A           complete
+map_client        bt_mce.h           N/A           complete
+a2dp_sink         bt_av.h            N/A           partial
+avrcp_ctrl        bt_rc.h            N/A           partial
+
+
+Implementation shortcomings
+===========================
+
+It is possible that some of HAL functionality (although being marked as
+complete) is missing implementation due to reasons like feature feasibility or
+necessity for latest Android Framework. This sections provides list of such
+deficiencies. Note that HAL library is always expected to fully implement HAL
+API so missing implementation might happen only in daemon.
+
+
+HAL Bluetooth
+-------------
+
+methods:
+dut_mode_send                      never called from Android Framework
+le_test_mode                       never called from Android Framework
+
+callbacks:
+dut_mode_recv_cb                   empty JNI implementation
+le_test_mode_cb                    empty JNI implementation
+
+properties:
+BT_PROPERTY_SERVICE_RECORD         not supported for adapter, for device this
+                                   property is returned as a response to
+                                   get_remote_service_record call
+
+BT_PROPERTY_REMOTE_VERSION_INFO    information required by this property (LMP
+                                   information) are not accessible from mgmt
+                                   interface, also marking this property as
+                                   settable is probably a typo in HAL header
+
+HAL Socket
+----------
+
+Support only for BTSOCK_RFCOMM socket type.
+
+
+HAL AVRCP
+---------
+
+methods:
+list_player_app_attr_rsp           never called from Android Framework
+list_player_app_value_rsp          never called from Android Framework
+get_player_app_value_rsp           never called from Android Framework
+get_player_app_attr_text_rsp       never called from Android Framework
+get_player_app_value_text_rsp      never called from Android Framework
+set_player_app_value_rsp           never called from Android Framework
+
+callbacks:
+list_player_app_attr_cb            NULL JNI implementation
+list_player_app_values_cb          NULL JNI implementation
+get_player_app_value_cb            NULL JNI implementation
+get_player_app_attrs_text_cb       NULL JNI implementation
+get_player_app_values_text_cb      NULL JNI implementation
+set_player_app_value_cb            NULL JNI implementation
+
+
+HAL GATT
+--------
+
+methods:
+client->set_adv_data               missing kernel support for vendor data
+client->connect                    is_direct parameter is ignored
+
+
+Audio SCO HAL
+=============
+
+When Bluetooth chip's audio is not wired directly to device audio, Audio SCO
+HAL is used to enable SCO support. It needs to be loaded by AudioFlinger
+following audio_policy.conf configuration. Example of configuration is shown
+below:
+
+...
+  sco {
+    outputs {
+      sco {
+        sampling_rates 8000|44100
+        channel_masks AUDIO_CHANNEL_OUT_STEREO
+        formats AUDIO_FORMAT_PCM_16_BIT
+        devices AUDIO_DEVICE_OUT_ALL_SCO
+      }
+    }
+    inputs {
+      sco {
+        sampling_rates 8000|44100
+        channel_masks AUDIO_CHANNEL_IN_MONO
+        formats AUDIO_FORMAT_PCM_16_BIT
+        devices AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET
+      }
+    }
+  }
+...
+
+Known Android issues
+====================
+
+It is possible that BlueZ is triggering bugs on Android Framework that could
+affect qualification or user experience. This section provides list of
+recommended Android fixes that are not part of latest AOSP release supported by
+BlueZ.
+
+For Android 5.1 Lollipop:
+https://android-review.googlesource.com/177314
+
+For Android 5.0 Lollipop:
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/132733
+https://android-review.googlesource.com/132763
+https://android-review.googlesource.com/177314
+
+For Android 4.4 KitKat:
+https://android-review.googlesource.com/82757
+https://android-review.googlesource.com/87670
+https://android-review.googlesource.com/88384
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/99850
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/177314
+
+Unimplemented Bluetooth features
+================================
+
+Some Bluetooth functionality require support from outside of BT stack
+eg. telephony stack. This sections describes profiles optional features not
+implemented due to lack of support in other Android subsystems or missing API
+in respective BT HALs.
+
+Profile		Feature				Comments
+--------------------------------------------------------
+HFP		Attach a phone number to	AT+BINP=1
+		a voice tag
+HFP		Enhanced Call Control		AT+CHLD={1x,2x}
+HFP		Explicit Call Transfer		AT+CHLD=4
+HFP		Response and Hold		AT+BTRH, +BTRH
+HFP		In-band Ring Tone		+BSIR
+AVRCP		Player Settings			HAL API present but not used
+AVRCP		Browsing			No HAL API
+GATT		Read multiple characteristics	No HAL API
+
+
+Reporting Bugs
+==============
+
+Bugs should be reported at https://01.org/jira/browse/BA. When reporting
+a bug please attach logs from logcat (logcat -v time) and HCI trace. Daemon
+debug logs should be enabled. When reporting daemon crash please run it under
+valgrind if possible. For details on how to enabled debug logs and valgrind see
+"Enabling BlueZ debugs" section.
diff --git a/bluez/repo/android/a2dp-sink.c b/bluez/repo/android/a2dp-sink.c
new file mode 100644
index 0000000..7c1e1a0
--- /dev/null
+++ b/bluez/repo/android/a2dp-sink.c
@@ -0,0 +1,84 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc.h"
+#include "a2dp-sink.h"
+
+static struct ipc *hal_ipc = NULL;
+
+static void bt_a2dp_sink_connect(const void *buf, uint16_t len)
+{
+	/* TODO */
+
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_a2dp_sink_disconnect(const void *buf, uint16_t len)
+{
+	/* TODO */
+
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_A2DP_CONNECT */
+	{ bt_a2dp_sink_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+	/* HAL_OP_A2DP_DISCONNECT */
+	{ bt_a2dp_sink_disconnect, false,
+				sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
+bool bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	DBG("");
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP_SINK, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_a2dp_sink_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP_SINK);
+	hal_ipc = NULL;
+}
diff --git a/bluez/repo/android/a2dp-sink.h b/bluez/repo/android/a2dp-sink.h
new file mode 100644
index 0000000..d2c5ff4
--- /dev/null
+++ b/bluez/repo/android/a2dp-sink.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_a2dp_sink_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_a2dp_sink_unregister(void);
diff --git a/bluez/repo/android/a2dp.c b/bluez/repo/android/a2dp.c
new file mode 100644
index 0000000..f219042
--- /dev/null
+++ b/bluez/repo/android/a2dp.c
@@ -0,0 +1,1774 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "profiles/audio/a2dp-codecs.h"
+#include "src/shared/queue.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "a2dp.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "avdtp.h"
+#include "avrcp.h"
+#include "audio-msg.h"
+
+#define SVC_HINT_CAPTURING 0x08
+#define IDLE_TIMEOUT 1
+#define AUDIO_RETRY_TIMEOUT 2
+
+static GIOChannel *server = NULL;
+static GSList *devices = NULL;
+static GSList *endpoints = NULL;
+static GSList *setups = NULL;
+static bdaddr_t adapter_addr;
+static uint32_t record_id = 0;
+static guint audio_retry_id = 0;
+static bool audio_retrying = false;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *audio_ipc = NULL;
+
+static struct queue *lseps = NULL;
+
+struct a2dp_preset {
+	void *data;
+	int8_t len;
+};
+
+struct a2dp_endpoint {
+	uint8_t id;
+	uint8_t codec;
+	struct avdtp_local_sep *sep;
+	struct a2dp_preset *caps;
+	GSList *presets;
+};
+
+struct a2dp_device {
+	bdaddr_t	dst;
+	uint8_t		state;
+	GIOChannel	*io;
+	struct avdtp	*session;
+	guint		idle_id;
+};
+
+struct a2dp_setup {
+	struct a2dp_device *dev;
+	struct a2dp_endpoint *endpoint;
+	struct a2dp_preset *preset;
+	struct avdtp_stream *stream;
+	uint8_t state;
+};
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct a2dp_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static void preset_free(void *data)
+{
+	struct a2dp_preset *preset = data;
+
+	g_free(preset->data);
+	g_free(preset);
+}
+
+static void unregister_endpoint(void *data)
+{
+	struct a2dp_endpoint *endpoint = data;
+
+	if (endpoint->sep)
+		avdtp_unregister_sep(lseps, endpoint->sep);
+
+	if (endpoint->caps)
+		preset_free(endpoint->caps);
+
+	g_slist_free_full(endpoint->presets, preset_free);
+
+	g_free(endpoint);
+}
+
+static void setup_free(void *data)
+{
+	struct a2dp_setup *setup = data;
+
+	if (!g_slist_find(setup->endpoint->presets, setup->preset))
+		preset_free(setup->preset);
+
+	g_free(setup);
+}
+
+static void setup_remove(struct a2dp_setup *setup)
+{
+	setups = g_slist_remove(setups, setup);
+	setup_free(setup);
+}
+
+static void setup_remove_all_by_dev(struct a2dp_device *dev)
+{
+	GSList *l = setups;
+
+	while (l) {
+		struct a2dp_setup *setup = l->data;
+		GSList *next = g_slist_next(l);
+
+		if (setup->dev == dev)
+			setup_remove(setup);
+
+		l = next;
+	}
+}
+
+static void a2dp_device_free(void *data)
+{
+	struct a2dp_device *dev = data;
+
+	if (dev->idle_id > 0)
+		g_source_remove(dev->idle_id);
+
+	if (dev->session)
+		avdtp_unref(dev->session);
+
+	if (dev->io) {
+		g_io_channel_shutdown(dev->io, FALSE, NULL);
+		g_io_channel_unref(dev->io);
+	}
+
+	setup_remove_all_by_dev(dev);
+
+	g_free(dev);
+}
+
+static void a2dp_device_remove(struct a2dp_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+	a2dp_device_free(dev);
+}
+
+static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst)
+{
+	struct a2dp_device *dev;
+
+	dev = g_new0(struct a2dp_device, 1);
+	bacpy(&dev->dst, dst);
+	devices = g_slist_prepend(devices, dev);
+
+	return dev;
+}
+
+static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb)
+{
+	GError *err = NULL;
+
+	dev->io = bt_io_connect(cb, dev, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	return true;
+}
+
+static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
+{
+	struct hal_ev_a2dp_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	dev->state = state;
+
+	ba2str(&dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE,
+							sizeof(ev), &ev);
+
+	if (state != HAL_A2DP_STATE_DISCONNECTED)
+		return;
+
+	bt_avrcp_disconnect(&dev->dst);
+
+	a2dp_device_remove(dev);
+}
+
+static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
+{
+	struct hal_ev_a2dp_audio_state ev;
+	char address[18];
+
+	if (setup->state == state)
+		return;
+
+	setup->state = state;
+
+	ba2str(&setup->dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&setup->dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE,
+							sizeof(ev), &ev);
+}
+
+static void disconnect_cb(void *user_data)
+{
+	struct a2dp_device *dev = user_data;
+
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static int sbc_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_sbc_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) {
+		error("SBC: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->frequency & config->frequency)) {
+		error("SBC: Unsupported frequency (%u) by endpoint",
+							config->frequency);
+		return -EINVAL;
+	}
+
+	if (!(cap->channel_mode & config->channel_mode)) {
+		error("SBC: Unsupported channel mode (%u) by endpoint",
+							config->channel_mode);
+		return -EINVAL;
+	}
+
+	if (!(cap->block_length & config->block_length)) {
+		error("SBC: Unsupported block length (%u) by endpoint",
+							config->block_length);
+		return -EINVAL;
+	}
+
+	if (!(cap->allocation_method & config->allocation_method)) {
+		error("SBC: Unsupported allocation method (%u) by endpoint",
+							config->block_length);
+		return -EINVAL;
+	}
+
+	if (config->max_bitpool < cap->min_bitpool) {
+		error("SBC: Invalid maximun bitpool (%u < %u)",
+					config->max_bitpool, cap->min_bitpool);
+		return -EINVAL;
+	}
+
+	if (config->min_bitpool > cap->max_bitpool) {
+		error("SBC: Invalid minimun bitpool (%u > %u)",
+					config->min_bitpool, cap->min_bitpool);
+		return -EINVAL;
+	}
+
+	if (config->max_bitpool > cap->max_bitpool)
+		return -ERANGE;
+
+	if (config->min_bitpool < cap->min_bitpool)
+		return -ERANGE;
+
+	return 0;
+}
+
+static int aac_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_aac_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) {
+		error("AAC: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->object_type & config->object_type)) {
+		error("AAC: Unsupported object type (%u) by endpoint",
+							config->object_type);
+		return -EINVAL;
+	}
+
+	if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) {
+		error("AAC: Unsupported frequency (%u) by endpoint",
+						AAC_GET_FREQUENCY(*config));
+		return -EINVAL;
+	}
+
+	if (!(cap->channels & config->channels)) {
+		error("AAC: Unsupported channels (%u) by endpoint",
+							config->channels);
+		return -EINVAL;
+	}
+
+	/* VBR support in SNK is mandatory but let's make sure we don't try to
+	 * have VBR on remote which for some reason does not support it
+	 */
+	if (!cap->vbr && config->vbr) {
+		error("AAC: Unsupported VBR (%u) by endpoint",
+							config->vbr);
+		return -EINVAL;
+	}
+
+	if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config))
+		return -ERANGE;
+
+	return 0;
+}
+
+static int aptx_check_config(void *caps, uint8_t caps_len, void *conf,
+							uint8_t conf_len)
+{
+	a2dp_aptx_t *cap, *config;
+
+	if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) {
+		error("APTX: Invalid configuration size (%u)", conf_len);
+		return -EINVAL;
+	}
+
+	cap = caps;
+	config = conf;
+
+	if (!(cap->frequency & config->frequency)) {
+		error("APTX: Unsupported frequenct (%u) by endpoint",
+							config->frequency);
+		return -EINVAL;
+	}
+
+	if (!(cap->channel_mode & config->channel_mode)) {
+		error("APTX: Unsupported channel mode (%u) by endpoint",
+							config->channel_mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int check_capabilities(struct a2dp_preset *preset,
+				struct avdtp_media_codec_capability *codec,
+				uint8_t codec_len)
+{
+	a2dp_vendor_codec_t *vndcodec;
+
+	/* Codec specific */
+	switch (codec->media_codec_type) {
+	case A2DP_CODEC_SBC:
+		return sbc_check_config(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_MPEG24:
+		return aac_check_config(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_VENDOR:
+		vndcodec = (void *) codec->data;
+		if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID &&
+				btohs(vndcodec->codec_id) == APTX_CODEC_ID)
+			return aptx_check_config(codec->data, codec_len,
+						preset->data, preset->len);
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len,
+						void *conf, uint8_t conf_len)
+{
+	struct a2dp_preset *p;
+	a2dp_sbc_t *cap, *config;
+
+	cap = caps;
+	config = conf;
+
+	config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool);
+	config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool);
+
+	p = g_new0(struct a2dp_preset, 1);
+	p->len = conf_len;
+	p->data = g_memdup(conf, p->len);
+
+	return p;
+}
+
+static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len,
+						void *conf, uint8_t conf_len)
+{
+	struct a2dp_preset *p;
+	a2dp_aac_t *cap, *config;
+	uint32_t bitrate;
+
+	cap = caps;
+	config = conf;
+
+	bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config));
+	AAC_SET_BITRATE(*config, bitrate);
+
+	p = g_new0(struct a2dp_preset, 1);
+	p->len = conf_len;
+	p->data = g_memdup(conf, p->len);
+
+	return p;
+}
+
+static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
+				struct avdtp_media_codec_capability *codec,
+				uint8_t codec_len)
+{
+	/* Codec specific */
+	switch (codec->media_codec_type) {
+	case A2DP_CODEC_SBC:
+		return sbc_select_range(codec->data, codec_len, preset->data,
+								preset->len);
+	case A2DP_CODEC_MPEG24:
+		return aac_select_range(codec->data, codec_len, preset->data,
+								preset->len);
+	default:
+		return NULL;
+	}
+}
+
+static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint,
+						struct avdtp_remote_sep *rsep)
+{
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+	GSList *l;
+	uint8_t codec_len;
+
+	service = avdtp_get_codec(rsep);
+	codec = (struct avdtp_media_codec_capability *) service->data;
+	codec_len = service->length - sizeof(*codec);
+
+	for (l = endpoint->presets; l; l = g_slist_next(l)) {
+		struct a2dp_preset *preset = l->data;
+		int err;
+
+		err = check_capabilities(preset, codec, codec_len);
+		if (err == 0)
+			return preset;
+
+		if (err == -ERANGE)
+			return select_preset_range(preset, codec, codec_len);
+	}
+
+	return NULL;
+}
+
+static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
+			struct a2dp_preset *preset, struct avdtp_stream *stream)
+{
+	struct a2dp_setup *setup;
+
+	setup = g_new0(struct a2dp_setup, 1);
+	setup->dev = dev;
+	setup->endpoint = endpoint;
+	setup->preset = preset;
+	setup->stream = stream;
+	setups = g_slist_append(setups, setup);
+
+	if (dev->idle_id > 0) {
+		g_source_remove(dev->idle_id);
+		dev->idle_id = 0;
+	}
+}
+
+static int select_configuration(struct a2dp_device *dev,
+				struct a2dp_endpoint *endpoint,
+				struct avdtp_remote_sep *rsep)
+{
+	struct a2dp_preset *preset;
+	struct avdtp_stream *stream;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+	GSList *caps;
+	int err;
+
+	preset = select_preset(endpoint, rsep);
+	if (!preset) {
+		error("Unable to select codec preset");
+		return -EINVAL;
+	}
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	caps = g_slist_append(NULL, service);
+
+	codec = g_malloc0(sizeof(*codec) + preset->len);
+	codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec->media_codec_type = endpoint->codec;
+	memcpy(codec->data, preset->data, preset->len);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+						sizeof(*codec) + preset->len);
+	caps = g_slist_append(caps, service);
+
+	g_free(codec);
+
+	err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps,
+								&stream);
+	g_slist_free_full(caps, g_free);
+	if (err < 0) {
+		error("avdtp_set_configuration: %s", strerror(-err));
+		return err;
+	}
+
+	setup_add(dev, endpoint, preset, stream);
+
+	return 0;
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_device *dev = user_data;
+	struct a2dp_endpoint *endpoint = NULL;
+	struct avdtp_remote_sep *rsep = NULL;
+	GSList *l;
+
+	for (l = endpoints; l; l = g_slist_next(l)) {
+		endpoint = l->data;
+
+		rsep = avdtp_find_remote_sep(session, endpoint->sep);
+		if (rsep)
+			break;
+	}
+
+	if (!rsep) {
+		error("Unable to find matching endpoint");
+		goto failed;
+	}
+
+	if (select_configuration(dev, endpoint, rsep) < 0)
+		goto failed;
+
+	return;
+
+failed:
+	avdtp_shutdown(session);
+}
+
+static gboolean idle_timeout(gpointer user_data)
+{
+	struct a2dp_device *dev = user_data;
+	int err;
+
+	dev->idle_id = 0;
+
+	err = avdtp_discover(dev->session, discover_cb, dev);
+	if (err == 0)
+		return FALSE;
+
+	error("avdtp_discover: %s", strerror(-err));
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+
+	return FALSE;
+}
+
+static void signaling_connect_cb(GIOChannel *chan, GError *err,
+							gpointer user_data)
+{
+	struct a2dp_device *dev = user_data;
+	struct avdtp *session;
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		goto failed;
+	}
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	/* FIXME: Add proper version */
+	session = avdtp_new(fd, imtu, omtu, 0x0100, lseps);
+	if (!session)
+		goto failed;
+
+	dev->session = session;
+
+	avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
+
+	/* Proceed to stream setup if initiator */
+	if (dev->io) {
+		int perr;
+
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+
+		perr = avdtp_discover(dev->session, discover_cb, dev);
+		if (perr < 0) {
+			error("avdtp_discover: %s", strerror(-perr));
+			goto failed;
+		}
+		bt_avrcp_connect(&dev->dst);
+	} else /* Init idle timeout to discover */
+		dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout,
+									dev);
+
+	return;
+
+failed:
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static void bt_a2dp_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_a2dp_connect *cmd = buf;
+	struct a2dp_device *dev;
+	uint8_t status;
+	char addr[18];
+	bdaddr_t dst;
+	GSList *l;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = a2dp_device_new(&dst);
+	if (!a2dp_device_connect(dev, signaling_connect_cb)) {
+		a2dp_device_remove(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
+}
+
+static void bt_a2dp_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_a2dp_connect *cmd = buf;
+	uint8_t status;
+	struct a2dp_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+	status = HAL_STATUS_SUCCESS;
+
+	if (dev->io) {
+		bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+		goto failed;
+	}
+
+	/* Wait AVDTP session to shutdown */
+	avdtp_shutdown(dev->session);
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+									status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_A2DP_CONNECT */
+	{ bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+	/* HAL_OP_A2DP_DISCONNECT */
+	{ bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
+static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev)
+{
+	GSList *l;
+
+	for (l = setups; l; l = g_slist_next(l)) {
+		struct a2dp_setup *setup = l->data;
+
+		if (setup->dev == dev)
+			return setup;
+	}
+
+	return NULL;
+}
+
+static void transport_connect_cb(GIOChannel *chan, GError *err,
+							gpointer user_data)
+{
+	struct a2dp_device *dev = user_data;
+	struct a2dp_setup *setup;
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	setup = find_setup_by_device(dev);
+	if (!setup) {
+		error("Unable to find stream setup");
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) {
+		error("avdtp_stream_set_transport: failed");
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct a2dp_device *dev;
+	bdaddr_t dst;
+	char address[18];
+	GError *gerr = NULL;
+	GSList *l;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s", address);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l) {
+		transport_connect_cb(chan, err, l->data);
+		return;
+	}
+
+	dev = a2dp_device_new(&dst);
+	bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
+	signaling_connect_cb(chan, err, dev);
+}
+
+static sdp_record_t *a2dp_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVDTP_UUID;
+	uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &a2dp_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+	profile[0].version = a2dp_ver;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+	proto[1] = sdp_list_append(NULL, &avdtp_uuid);
+	version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "Audio Source", NULL, NULL);
+
+	sdp_data_free(psm);
+	sdp_data_free(version);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static gboolean sep_getcap_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_preset *cap = endpoint->caps;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+
+	*caps = NULL;
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	*caps = g_slist_append(*caps, service);
+
+	codec = g_malloc0(sizeof(*codec) + cap->len);
+	codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec->media_codec_type = endpoint->codec;
+	memcpy(codec->data, cap->data, cap->len);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+						sizeof(*codec) + cap->len);
+	*caps = g_slist_append(*caps, service);
+	g_free(codec);
+
+	return TRUE;
+}
+
+static int check_config(struct a2dp_endpoint *endpoint,
+						struct a2dp_preset *config)
+{
+	GSList *l;
+	struct a2dp_preset *caps;
+
+	for (l = endpoint->presets; l; l = g_slist_next(l)) {
+		struct a2dp_preset *preset = l->data;
+
+		if (preset->len != config->len)
+			continue;
+
+		if (memcmp(preset->data, config->data, preset->len) == 0)
+			return 0;
+	}
+
+	caps = endpoint->caps;
+
+	/* Codec specific */
+	switch (endpoint->codec) {
+	case A2DP_CODEC_SBC:
+		return sbc_check_config(caps->data, caps->len, config->data,
+								config->len);
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct a2dp_device *find_device_by_session(struct avdtp *session)
+{
+	GSList *l;
+
+	for (l = devices; l; l = g_slist_next(l)) {
+		struct a2dp_device *dev = l->data;
+
+		if (dev->session == session)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static struct a2dp_setup *find_setup(uint8_t id)
+{
+	GSList *l;
+
+	for (l = setups; l; l = g_slist_next(l)) {
+		struct a2dp_setup *setup = l->data;
+
+		if (setup->endpoint->id == id)
+			return setup;
+	}
+
+	return NULL;
+}
+
+static void setup_remove_by_id(uint8_t id)
+{
+	struct a2dp_setup *setup;
+
+	setup = find_setup(id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u", id);
+		return;
+	}
+
+	setup_remove(setup);
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						GSList *caps,
+						avdtp_set_configuration_cb cb,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_device *dev;
+	struct a2dp_preset *preset = NULL;
+
+	DBG("");
+
+	dev = find_device_by_session(session);
+	if (!dev) {
+		error("Unable to find device for session %p", session);
+		return FALSE;
+	}
+
+	for (; caps != NULL; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+		struct avdtp_media_codec_capability *codec;
+
+		if (cap->category == AVDTP_DELAY_REPORTING)
+			return FALSE;
+
+		if (cap->category != AVDTP_MEDIA_CODEC)
+			continue;
+
+		codec = (struct avdtp_media_codec_capability *) cap->data;
+
+		if (codec->media_codec_type != endpoint->codec)
+			return FALSE;
+
+		preset = g_new0(struct a2dp_preset, 1);
+		preset->len = cap->length - sizeof(*codec);
+		preset->data = g_memdup(codec->data, preset->len);
+
+		if (check_config(endpoint, preset) < 0) {
+			preset_free(preset);
+			return FALSE;
+		}
+	}
+
+	if (!preset)
+		return FALSE;
+
+	setup_add(dev, endpoint, preset, stream);
+
+	cb(session, stream, NULL);
+
+	return TRUE;
+}
+
+static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean sep_close_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+	setup_remove(setup);
+
+	return TRUE;
+}
+
+static gboolean sep_start_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+
+	return TRUE;
+}
+
+static gboolean sep_suspend_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		*err = AVDTP_SEP_NOT_IN_USE;
+		return FALSE;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+	.get_capability		= sep_getcap_ind,
+	.set_configuration	= sep_setconf_ind,
+	.open			= sep_open_ind,
+	.close			= sep_close_ind,
+	.start			= sep_start_ind,
+	.suspend		= sep_suspend_ind,
+};
+
+static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+	int ret;
+
+	DBG("");
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for endpoint %u",
+								endpoint->id);
+		return;
+	}
+
+	if (err)
+		goto failed;
+
+	ret = avdtp_open(session, stream);
+	if (ret < 0) {
+		error("avdtp_open: %s", strerror(-ret));
+		goto failed;
+	}
+
+	return;
+
+failed:
+	setup_remove(setup);
+}
+
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_device *dev;
+
+	DBG("");
+
+	if (err)
+		goto failed;
+
+	dev = find_device_by_session(session);
+	if (!dev) {
+		error("Unable to find device for session");
+		goto failed;
+	}
+
+	a2dp_device_connect(dev, transport_connect_cb);
+
+	return;
+
+failed:
+	setup_remove_by_id(endpoint->id);
+}
+
+static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	if (err) {
+		setup_remove_by_id(endpoint->id);
+		return;
+	}
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+}
+
+static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	if (err) {
+		setup_remove_by_id(endpoint->id);
+		return;
+	}
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+}
+
+static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
+
+	DBG("");
+
+	if (err)
+		return;
+
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+	setup_remove(setup);
+}
+
+static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_endpoint *endpoint = user_data;
+
+	DBG("");
+
+	if (err)
+		return;
+
+	setup_remove_by_id(endpoint->id);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+	.set_configuration	= sep_setconf_cfm,
+	.open			= sep_open_cfm,
+	.start			= sep_start_cfm,
+	.suspend		= sep_suspend_cfm,
+	.close			= sep_close_cfm,
+	.abort			= sep_abort_cfm,
+};
+
+static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
+							GSList *presets)
+{
+	struct a2dp_endpoint *endpoint;
+
+	/* FIXME: Add proper check for uuid */
+
+	endpoint = g_new0(struct a2dp_endpoint, 1);
+	endpoint->id = g_slist_length(endpoints) + 1;
+	endpoint->codec = codec;
+	endpoint->sep = avdtp_register_sep(lseps, AVDTP_SEP_TYPE_SOURCE,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						codec, FALSE, &sep_ind,
+						&sep_cfm, endpoint);
+	endpoint->caps = presets->data;
+	endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+
+	if (endpoint->codec == A2DP_CODEC_VENDOR) {
+		a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data;
+
+		avdtp_sep_set_vendor_codec(endpoint->sep,
+						btohl(vndcodec->vendor_id),
+						btohs(vndcodec->codec_id));
+	}
+
+	endpoints = g_slist_append(endpoints, endpoint);
+
+	return endpoint->id;
+}
+
+static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
+								uint16_t len)
+{
+	GSList *l = NULL;
+	uint8_t i;
+
+	for (i = 0; count > i; i++) {
+		const uint8_t *ptr = (const uint8_t *) p;
+		struct a2dp_preset *preset;
+
+		if (len < sizeof(struct audio_preset)) {
+			DBG("Invalid preset index %u", i);
+			g_slist_free_full(l, preset_free);
+			return NULL;
+		}
+
+		len -= sizeof(struct audio_preset);
+		if (len == 0 || len < p->len) {
+			DBG("Invalid preset size of %u for index %u", len, i);
+			g_slist_free_full(l, preset_free);
+			return NULL;
+		}
+
+		preset = g_new0(struct a2dp_preset, 1);
+		preset->len = p->len;
+		preset->data = g_memdup(p->data, preset->len);
+		l = g_slist_append(l, preset);
+
+		len -= preset->len;
+		ptr += sizeof(*p) + preset->len;
+		p = (const struct audio_preset *) ptr;
+	}
+
+	return l;
+}
+
+static void bt_audio_open(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_open *cmd = buf;
+	struct audio_rsp_open rsp;
+	GSList *presets;
+
+	DBG("");
+
+	audio_retrying = false;
+
+	if (cmd->presets == 0) {
+		error("No audio presets found");
+		goto failed;
+	}
+
+	presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
+	if (!presets) {
+		error("No audio presets found");
+		goto failed;
+	}
+
+	rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
+	if (rsp.id == 0) {
+		g_slist_free_full(presets, preset_free);
+		error("Unable to register endpoint");
+		goto failed;
+	}
+
+	g_slist_free(presets);
+
+	ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+							sizeof(rsp), &rsp, -1);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+							AUDIO_STATUS_FAILED);
+}
+
+static struct a2dp_endpoint *find_endpoint(uint8_t id)
+{
+	GSList *l;
+
+	for (l = endpoints; l; l = g_slist_next(l)) {
+		struct a2dp_endpoint *endpoint = l->data;
+
+		if (endpoint->id == id)
+			return endpoint;
+	}
+
+	return NULL;
+}
+
+static void bt_audio_close(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_close *cmd = buf;
+	struct a2dp_endpoint *endpoint;
+
+	DBG("");
+
+	endpoint = find_endpoint(cmd->id);
+	if (!endpoint) {
+		error("Unable to find endpoint %u", cmd->id);
+		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+							AUDIO_STATUS_FAILED);
+		return;
+	}
+
+	endpoints = g_slist_remove(endpoints, endpoint);
+	unregister_endpoint(endpoint);
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+							AUDIO_STATUS_SUCCESS);
+}
+
+static void bt_stream_open(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_open_stream *cmd = buf;
+	struct audio_rsp_open_stream *rsp;
+	struct a2dp_setup *setup;
+	int fd;
+	uint16_t omtu;
+
+	DBG("");
+
+	if (cmd->id)
+		setup = find_setup(cmd->id);
+	else
+		setup = setups ? setups->data : NULL;
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+							AUDIO_STATUS_FAILED);
+		return;
+	}
+
+	if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu,
+								NULL)) {
+		error("avdtp_stream_get_transport: failed");
+		ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+							AUDIO_STATUS_FAILED);
+		return;
+	}
+
+	len = sizeof(struct audio_rsp_open_stream) +
+			sizeof(struct audio_preset) + setup->preset->len;
+	rsp = g_malloc0(len);
+	rsp->id = setup->endpoint->id;
+	rsp->mtu = omtu;
+	rsp->preset->len = setup->preset->len;
+	memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
+
+	ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+								len, rsp, fd);
+
+	g_free(rsp);
+}
+
+static void bt_stream_close(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_close_stream *cmd = buf;
+	struct a2dp_setup *setup;
+	int err;
+
+	DBG("");
+
+	setup = find_setup(cmd->id);
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		goto failed;
+	}
+
+	err = avdtp_close(setup->dev->session, setup->stream, FALSE);
+	if (err < 0) {
+		error("avdtp_close: %s", strerror(-err));
+		goto failed;
+	}
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+							AUDIO_STATUS_SUCCESS);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+							AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_resume(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_resume_stream *cmd = buf;
+	struct a2dp_setup *setup;
+	int err;
+
+	DBG("");
+
+	setup = find_setup(cmd->id);
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		goto failed;
+	}
+
+	if (setup->state != HAL_AUDIO_STARTED) {
+		err = avdtp_start(setup->dev->session, setup->stream);
+		if (err < 0) {
+			error("avdtp_start: %s", strerror(-err));
+			goto failed;
+		}
+	}
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+							AUDIO_STATUS_SUCCESS);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+							AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_suspend(const void *buf, uint16_t len)
+{
+	const struct audio_cmd_suspend_stream *cmd = buf;
+	struct a2dp_setup *setup;
+	int err;
+
+	DBG("");
+
+	setup = find_setup(cmd->id);
+	if (!setup) {
+		error("Unable to find stream for endpoint %u", cmd->id);
+		goto failed;
+	}
+
+	err = avdtp_suspend(setup->dev->session, setup->stream);
+	if (err < 0) {
+		error("avdtp_suspend: %s", strerror(-err));
+		goto failed;
+	}
+
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+							AUDIO_STATUS_SUCCESS);
+
+	return;
+
+failed:
+	ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+							AUDIO_STATUS_FAILED);
+}
+
+static const struct ipc_handler audio_handlers[] = {
+	/* AUDIO_OP_OPEN */
+	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
+	/* AUDIO_OP_CLOSE */
+	{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
+	/* AUDIO_OP_OPEN_STREAM */
+	{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
+	/* AUDIO_OP_CLOSE_STREAM */
+	{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
+	/* AUDIO_OP_RESUME_STREAM */
+	{ bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
+	/* AUDIO_OP_SUSPEND_STREAM */
+	{ bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
+};
+
+static void bt_audio_unregister(void)
+{
+	DBG("");
+
+	if (audio_retry_id > 0)
+		g_source_remove(audio_retry_id);
+
+	g_slist_free_full(endpoints, unregister_endpoint);
+	endpoints = NULL;
+
+	g_slist_free_full(setups, setup_free);
+	setups = NULL;
+
+	ipc_cleanup(audio_ipc);
+	audio_ipc = NULL;
+
+	queue_destroy(lseps, NULL);
+}
+
+static bool bt_audio_register(ipc_disconnect_cb disconnect)
+{
+	DBG("");
+
+	audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
+				AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
+	if (!audio_ipc)
+		return false;
+
+	ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers,
+						G_N_ELEMENTS(audio_handlers));
+
+	return true;
+}
+
+static gboolean audio_retry_register(void *data)
+{
+	ipc_disconnect_cb cb = data;
+
+	audio_retry_id = 0;
+	audio_retrying = true;
+
+	bt_audio_register(cb);
+
+	return FALSE;
+}
+
+static void audio_disconnected(void *data)
+{
+	GSList *l;
+	bool restart;
+
+	DBG("");
+
+	if (audio_retrying)
+		goto retry;
+
+	restart = endpoints != NULL ? true : false;
+
+	bt_audio_unregister();
+
+	for (l = devices; l; l = g_slist_next(l)) {
+		struct a2dp_device *dev = l->data;
+
+		avdtp_shutdown(dev->session);
+	}
+
+	if (!restart)
+		return;
+
+retry:
+	audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT,
+						audio_retry_register,
+						audio_disconnected);
+}
+
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+	sdp_record_t *rec;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	lseps = queue_new();
+
+	server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, AVDTP_PSM,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_MASTER, true,
+				BT_IO_OPT_INVALID);
+	if (!server) {
+		error("Failed to listen on AVDTP channel: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = a2dp_record();
+	if (!rec) {
+		error("Failed to allocate A2DP record");
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) {
+		error("Failed to register A2DP record");
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_id = rec->handle;
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	if (bt_audio_register(audio_disconnected))
+		return true;
+
+fail:
+	g_io_channel_shutdown(server, TRUE, NULL);
+	g_io_channel_unref(server);
+	server = NULL;
+	return false;
+}
+
+void bt_a2dp_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(setups, setup_free);
+	setups = NULL;
+
+	g_slist_free_full(endpoints, unregister_endpoint);
+	endpoints = NULL;
+
+	g_slist_free_full(devices, a2dp_device_free);
+	devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP);
+	hal_ipc = NULL;
+
+	bt_adapter_remove_record(record_id);
+	record_id = 0;
+
+	if (server) {
+		g_io_channel_shutdown(server, TRUE, NULL);
+		g_io_channel_unref(server);
+		server = NULL;
+	}
+
+	if (audio_ipc) {
+		ipc_unregister(audio_ipc, AUDIO_SERVICE_ID);
+		ipc_cleanup(audio_ipc);
+		audio_ipc = NULL;
+	}
+}
diff --git a/bluez/repo/android/a2dp.h b/bluez/repo/android/a2dp.h
new file mode 100644
index 0000000..8a70407
--- /dev/null
+++ b/bluez/repo/android/a2dp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_a2dp_unregister(void);
diff --git a/bluez/repo/android/audio-ipc-api.txt b/bluez/repo/android/audio-ipc-api.txt
new file mode 100644
index 0000000..f4a497d
--- /dev/null
+++ b/bluez/repo/android/audio-ipc-api.txt
@@ -0,0 +1,87 @@
+Bluetooth Audio Plugin
+======================
+
+The audio plugin happen to be in a different socket but all the rules for
+HAL socket apply here as well, the abstract socket name is
+"\0bluez_audio_socket" (tentative):
+
+	.---Audio---.                             .--Android--.
+	|  Plugin   |                             |   Daemon  |
+	|           |          Command            |           |
+	|           | --------------------------> |           |
+	|           |                             |           |
+	|           | <-------------------------- |           |
+	|           |          Response           |           |
+	|           |                             |           |
+	|           |                             |           |
+	|           |                             |           |
+	'-----------'                             '-----------'
+
+
+	Audio HAL                               Daemon
+	----------------------------------------------------
+
+	call dev->open()                    --> command 0x01
+	return dev->open()                  <-- response 0x01
+
+	call dev->open_output_stream()      --> command 0x03
+	return dev->open_output_stream()    <-- response 0x03
+
+	call stream->write()                --> command 0x05
+	return stream->write()              <-- response 0x05
+
+	call stream->common.standby()       --> command 0x06
+	return stream->common.standby()     <-- response 0x06
+
+	call dev->close_output_stream()     --> command 0x04
+	return dev->close_output_stream()   <-- response 0x04
+
+	call dev->close()                   --> command 0x02
+	return dev->close()                 <-- response 0x02
+
+Audio Service (ID 0)
+====================
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+	Opcode 0x01 - Open Audio Endpoint commmand
+
+		Command parameters: Service UUID (16 octets)
+				    Codec ID (1 octet)
+				    Number of codec presets (1 octet)
+				    Codec capabilities length (1 octet)
+				    Codec capabilities (variable)
+				    Codec preset # length (1 octet)
+				    Codec preset # configuration (variable)
+				    ...
+		Response parameters: Endpoint ID (1 octet)
+
+	Opcode 0x02 - Close Audio Endpoint command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
+
+	Opcode 0x03 - Open Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: Outgoing MTU (2 octets)
+				     Codec configuration length (1 octet)
+				     Codec configuration (1 octet)
+				     File descriptor (inline)
+
+	Opcode 0x04 - Close Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
+
+	Opcode 0x05 - Resume Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
+
+	Opcode 0x06 - Suspend Stream command
+
+		Command parameters: Endpoint ID (1 octet)
+		Response parameters: <none>
diff --git a/bluez/repo/android/audio-msg.h b/bluez/repo/android/audio-msg.h
new file mode 100644
index 0000000..7b9553b
--- /dev/null
+++ b/bluez/repo/android/audio-msg.h
@@ -0,0 +1,82 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 BLUEZ_AUDIO_MTU 1024
+
+static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
+
+#define AUDIO_SERVICE_ID		0
+#define AUDIO_SERVICE_ID_MAX		AUDIO_SERVICE_ID
+
+#define AUDIO_STATUS_SUCCESS		IPC_STATUS_SUCCESS
+#define AUDIO_STATUS_FAILED		0x01
+
+#define AUDIO_OP_STATUS			IPC_OP_STATUS
+
+#define AUDIO_OP_OPEN			0x01
+struct audio_preset {
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_cmd_open {
+	uint8_t uuid[16];
+	uint8_t codec;
+	uint8_t presets;
+	struct audio_preset preset[0];
+} __attribute__((packed));
+
+struct audio_rsp_open {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE			0x02
+struct audio_cmd_close {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_OPEN_STREAM		0x03
+struct audio_cmd_open_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+struct audio_rsp_open_stream {
+	uint16_t id;
+	uint16_t mtu;
+	struct audio_preset preset[0];
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE_STREAM		0x04
+struct audio_cmd_close_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_RESUME_STREAM		0x05
+struct audio_cmd_resume_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_SUSPEND_STREAM		0x06
+struct audio_cmd_suspend_stream {
+	uint8_t id;
+} __attribute__((packed));
diff --git a/bluez/repo/android/audio_utils/._resampler.c b/bluez/repo/android/audio_utils/._resampler.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/audio_utils/._resampler.c
Binary files differ
diff --git a/bluez/repo/android/audio_utils/._resampler.h b/bluez/repo/android/audio_utils/._resampler.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/audio_utils/._resampler.h
Binary files differ
diff --git a/bluez/repo/android/audio_utils/resampler.c b/bluez/repo/android/audio_utils/resampler.c
new file mode 100644
index 0000000..ce30375
--- /dev/null
+++ b/bluez/repo/android/audio_utils/resampler.c
@@ -0,0 +1,270 @@
+/*
+** Copyright 2011, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <system/audio.h>
+#include <audio_utils/resampler.h>
+#include <speex/speex_resampler.h>
+
+#include "hal-log.h"
+
+struct resampler {
+    struct resampler_itfe itfe;
+    SpeexResamplerState *speex_resampler;       // handle on speex resampler
+    struct resampler_buffer_provider *provider; // buffer provider installed by client
+    uint32_t in_sample_rate;                    // input sampling rate in Hz
+    uint32_t out_sample_rate;                   // output sampling rate in Hz
+    uint32_t channel_count;                     // number of channels (interleaved)
+    int16_t *in_buf;                            // input buffer
+    size_t in_buf_size;                         // input buffer size
+    size_t frames_in;                           // number of frames in input buffer
+    size_t frames_rq;                           // cached number of output frames
+    size_t frames_needed;                       // minimum number of input frames to produce
+                                                // frames_rq output frames
+    int32_t speex_delay_ns;                     // delay introduced by speex resampler in ns
+};
+
+
+//------------------------------------------------------------------------------
+// speex based resampler
+//------------------------------------------------------------------------------
+
+static void resampler_reset(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    rsmp->frames_in = 0;
+    rsmp->frames_rq = 0;
+
+    if (rsmp != NULL && rsmp->speex_resampler != NULL) {
+        speex_resampler_reset_mem(rsmp->speex_resampler);
+    }
+}
+
+static int32_t resampler_delay_ns(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate);
+    delay += rsmp->speex_delay_ns;
+
+    return delay;
+}
+
+// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount
+// with the actual number of frames produced.
+static int resampler_resample_from_provider(struct resampler_itfe *resampler,
+                       int16_t *out,
+                       size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+    size_t framesRq;
+    size_t framesWr;
+    size_t inFrames;
+
+    if (rsmp == NULL || out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider == NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    framesRq = *outFrameCount;
+    // update and cache the number of frames needed at the input sampling rate to produce
+    // the number of frames requested at the output sampling rate
+    if (framesRq != rsmp->frames_rq) {
+        rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1;
+        rsmp->frames_rq = framesRq;
+    }
+
+    framesWr = 0;
+    inFrames = 0;
+    while (framesWr < framesRq) {
+        size_t outFrames;
+        if (rsmp->frames_in < rsmp->frames_needed) {
+            struct resampler_buffer buf;
+            // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at
+            // least the number of frames needed to produce the number of frames requested at
+            // the output sampling rate
+            if (rsmp->in_buf_size < rsmp->frames_needed) {
+                rsmp->in_buf_size = rsmp->frames_needed;
+                rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf,
+                                        rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t));
+            }
+            buf.frame_count = rsmp->frames_needed - rsmp->frames_in;
+            rsmp->provider->get_next_buffer(rsmp->provider, &buf);
+            if (buf.raw == NULL) {
+                break;
+            }
+            memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count,
+                    buf.raw,
+                    buf.frame_count * rsmp->channel_count * sizeof(int16_t));
+            rsmp->frames_in += buf.frame_count;
+            rsmp->provider->release_buffer(rsmp->provider, &buf);
+        }
+
+        outFrames = framesRq - framesWr;
+        inFrames = rsmp->frames_in;
+        if (rsmp->channel_count == 1) {
+            speex_resampler_process_int(rsmp->speex_resampler,
+                                        0,
+                                        rsmp->in_buf,
+                                        (void *) &inFrames,
+                                        out + framesWr,
+                                        (void *) &outFrames);
+        } else {
+            speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                        rsmp->in_buf,
+                                        (void *) &inFrames,
+                                        out + framesWr * rsmp->channel_count,
+                                        (void *) &outFrames);
+        }
+        framesWr += outFrames;
+        rsmp->frames_in -= inFrames;
+
+        if ((framesWr != framesRq) && (rsmp->frames_in != 0))
+            warn("ReSampler::resample() remaining %zd frames in and %zd out",
+                rsmp->frames_in, (framesRq - framesWr));
+    }
+    if (rsmp->frames_in) {
+        memmove(rsmp->in_buf,
+                rsmp->in_buf + inFrames * rsmp->channel_count,
+                rsmp->frames_in * rsmp->channel_count * sizeof(int16_t));
+    }
+    *outFrameCount = framesWr;
+
+    return 0;
+}
+
+static int resampler_resample_from_input(struct resampler_itfe *resampler,
+                                  int16_t *in,
+                                  size_t *inFrameCount,
+                                  int16_t *out,
+                                  size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL || in == NULL || inFrameCount == NULL ||
+            out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider != NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    if (rsmp->channel_count == 1) {
+        speex_resampler_process_int(rsmp->speex_resampler,
+                                    0,
+                                    in,
+                                    (void *) inFrameCount,
+                                    out,
+                                    (void *) outFrameCount);
+    } else {
+        speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                                in,
+                                                (void *) inFrameCount,
+                                                out,
+                                                (void *) outFrameCount);
+    }
+
+    DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount);
+
+    return 0;
+}
+
+int create_resampler(uint32_t inSampleRate,
+                    uint32_t outSampleRate,
+                    uint32_t channelCount,
+                    uint32_t quality,
+                    struct resampler_buffer_provider* provider,
+                    struct resampler_itfe **resampler)
+{
+    int error;
+    struct resampler *rsmp;
+    int frames;
+
+    DBG("create_resampler() In SR %d Out SR %d channels %d",
+         inSampleRate, outSampleRate, channelCount);
+
+    if (resampler == NULL) {
+        return -EINVAL;
+    }
+
+    *resampler = NULL;
+
+    if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) {
+        return -EINVAL;
+    }
+
+    rsmp = (struct resampler *)calloc(1, sizeof(struct resampler));
+
+    rsmp->speex_resampler = speex_resampler_init(channelCount,
+                                      inSampleRate,
+                                      outSampleRate,
+                                      quality,
+                                      &error);
+    if (rsmp->speex_resampler == NULL) {
+        error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
+        free(rsmp);
+        return -ENODEV;
+    }
+
+    rsmp->itfe.reset = resampler_reset;
+    rsmp->itfe.resample_from_provider = resampler_resample_from_provider;
+    rsmp->itfe.resample_from_input = resampler_resample_from_input;
+    rsmp->itfe.delay_ns = resampler_delay_ns;
+
+    rsmp->provider = provider;
+    rsmp->in_sample_rate = inSampleRate;
+    rsmp->out_sample_rate = outSampleRate;
+    rsmp->channel_count = channelCount;
+    rsmp->in_buf = NULL;
+    rsmp->in_buf_size = 0;
+
+    resampler_reset(&rsmp->itfe);
+
+    frames = speex_resampler_get_input_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate);
+    frames = speex_resampler_get_output_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate);
+
+    *resampler = &rsmp->itfe;
+    DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p",
+         rsmp, &rsmp->itfe, rsmp->speex_resampler);
+    return 0;
+}
+
+void release_resampler(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL) {
+        return;
+    }
+
+    free(rsmp->in_buf);
+
+    if (rsmp->speex_resampler != NULL) {
+        speex_resampler_destroy(rsmp->speex_resampler);
+    }
+    free(rsmp);
+}
diff --git a/bluez/repo/android/audio_utils/resampler.h b/bluez/repo/android/audio_utils/resampler.h
new file mode 100644
index 0000000..0c7046f
--- /dev/null
+++ b/bluez/repo/android/audio_utils/resampler.h
@@ -0,0 +1,109 @@
+/*
+** Copyright 2008, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_RESAMPLER_H
+#define ANDROID_RESAMPLER_H
+
+#include <stdint.h>
+#include <sys/time.h>
+
+__BEGIN_DECLS
+
+
+#define RESAMPLER_QUALITY_MAX 10
+#define RESAMPLER_QUALITY_MIN 0
+#define RESAMPLER_QUALITY_DEFAULT 4
+#define RESAMPLER_QUALITY_VOIP 3
+#define RESAMPLER_QUALITY_DESKTOP 5
+
+struct resampler_buffer {
+    union {
+        void*       raw;
+        short*      i16;
+        int8_t*     i8;
+    };
+    size_t frame_count;
+};
+
+/* call back interface used by the resampler to get new data */
+struct resampler_buffer_provider
+{
+    /**
+     *  get a new buffer of data:
+     *   as input: buffer->frame_count is the number of frames requested
+     *   as output: buffer->frame_count is the number of frames returned
+     *              buffer->raw points to data returned
+     */
+    int (*get_next_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+    /**
+     *  release a consumed buffer of data:
+     *   as input: buffer->frame_count is the number of frames released
+     *             buffer->raw points to data released
+     */
+    void (*release_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+};
+
+/* resampler interface */
+struct resampler_itfe {
+    /**
+     * reset resampler state
+     */
+    void (*reset)(struct resampler_itfe *resampler);
+    /**
+     * resample input from buffer provider and output at most *outFrameCount to out buffer.
+     * *outFrameCount is updated with the actual number of frames produced.
+     */
+    int (*resample_from_provider)(struct resampler_itfe *resampler,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * resample at most *inFrameCount frames from in buffer and output at most
+     * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively
+     * with the number of frames remaining in input and written to output.
+     */
+    int (*resample_from_input)(struct resampler_itfe *resampler,
+                    int16_t *in,
+                    size_t *inFrameCount,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * return the latency introduced by the resampler in ns.
+     */
+    int32_t (*delay_ns)(struct resampler_itfe *resampler);
+};
+
+/**
+ * create a resampler according to input parameters passed.
+ * If resampler_buffer_provider is not NULL only resample_from_provider() can be called.
+ * If resampler_buffer_provider is NULL only resample_from_input() can be called.
+ */
+int create_resampler(uint32_t inSampleRate,
+          uint32_t outSampleRate,
+          uint32_t channelCount,
+          uint32_t quality,
+          struct resampler_buffer_provider *provider,
+          struct resampler_itfe **);
+
+/**
+ * release resampler resources.
+ */
+void release_resampler(struct resampler_itfe *);
+
+__END_DECLS
+
+#endif // ANDROID_RESAMPLER_H
diff --git a/bluez/repo/android/avctp.c b/bluez/repo/android/avctp.c
new file mode 100644
index 0000000..6aa64cf
--- /dev/null
+++ b/bluez/repo/android/avctp.c
@@ -0,0 +1,1653 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/uinput.h"
+
+#include "avctp.h"
+
+/*
+ * AV/C Panel 1.23, page 76:
+ * command with the pressed value is valid for two seconds
+ */
+#define AVC_PRESS_TIMEOUT	2
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND		0
+#define AVCTP_RESPONSE		1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE	0
+#define AVCTP_PACKET_START	1
+#define AVCTP_PACKET_CONTINUE	2
+#define AVCTP_PACKET_END	3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+	uint8_t ipid:1;
+	uint8_t cr:1;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint16_t pid;
+} __attribute__ ((packed));
+
+struct avc_header {
+	uint8_t code:4;
+	uint8_t _hdr0:4;
+	uint8_t subunit_id:3;
+	uint8_t subunit_type:5;
+	uint8_t opcode;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t cr:1;
+	uint8_t ipid:1;
+	uint16_t pid;
+} __attribute__ ((packed));
+
+struct avc_header {
+	uint8_t _hdr0:4;
+	uint8_t code:4;
+	uint8_t subunit_type:5;
+	uint8_t subunit_id:3;
+	uint8_t opcode;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_control_req {
+	struct avctp_pending_req *p;
+	uint8_t code;
+	uint8_t subunit;
+	uint8_t op;
+	struct iovec *iov;
+	int iov_cnt;
+	avctp_rsp_cb func;
+	void *user_data;
+};
+
+struct avctp_browsing_req {
+	struct avctp_pending_req *p;
+	struct iovec *iov;
+	int iov_cnt;
+	avctp_browsing_rsp_cb func;
+	void *user_data;
+};
+
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+	struct avctp_channel *chan;
+	uint8_t transaction;
+	guint timeout;
+	int err;
+	avctp_process_cb process;
+	void *data;
+	avctp_destroy_cb_t destroy;
+};
+
+struct avctp_channel {
+	struct avctp *session;
+	GIOChannel *io;
+	uint8_t transaction;
+	guint watch;
+	uint16_t imtu;
+	uint16_t omtu;
+	uint8_t *buffer;
+	GSList *handlers;
+	struct avctp_pending_req *p;
+	GQueue *queue;
+	GSList *processed;
+	guint process_id;
+	avctp_destroy_cb_t destroy;
+};
+
+struct key_pressed {
+	uint8_t op;
+	uint8_t *params;
+	size_t params_len;
+	guint timer;
+};
+
+struct avctp {
+	unsigned int ref;
+	int uinput;
+
+	unsigned int passthrough_id;
+	unsigned int unit_id;
+	unsigned int subunit_id;
+
+	struct avctp_channel *control;
+	struct avctp_channel *browsing;
+
+	struct avctp_passthrough_handler *handler;
+
+	uint8_t key_quirks[256];
+	struct key_pressed key;
+	uint16_t version;
+
+	avctp_destroy_cb_t destroy;
+	void *data;
+};
+
+struct avctp_passthrough_handler {
+	avctp_passthrough_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_pdu_handler {
+	uint8_t opcode;
+	avctp_control_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+	avctp_destroy_cb_t destroy;
+};
+
+static struct {
+	const char *name;
+	uint8_t avc;
+	uint16_t uinput;
+} key_map[] = {
+	{ "SELECT",		AVC_SELECT,		KEY_SELECT },
+	{ "UP",			AVC_UP,			KEY_UP },
+	{ "DOWN",		AVC_DOWN,		KEY_DOWN },
+	{ "LEFT",		AVC_LEFT,		KEY_LEFT },
+	{ "RIGHT",		AVC_RIGHT,		KEY_RIGHT },
+	{ "ROOT MENU",		AVC_ROOT_MENU,		KEY_MENU },
+	{ "CONTENTS MENU",	AVC_CONTENTS_MENU,	KEY_PROGRAM },
+	{ "FAVORITE MENU",	AVC_FAVORITE_MENU,	KEY_FAVORITES },
+	{ "EXIT",		AVC_EXIT,		KEY_EXIT },
+	{ "ON DEMAND MENU",	AVC_ON_DEMAND_MENU,	KEY_MENU },
+	{ "APPS MENU",		AVC_APPS_MENU,		KEY_MENU },
+	{ "0",			AVC_0,			KEY_0 },
+	{ "1",			AVC_1,			KEY_1 },
+	{ "2",			AVC_2,			KEY_2 },
+	{ "3",			AVC_3,			KEY_3 },
+	{ "4",			AVC_4,			KEY_4 },
+	{ "5",			AVC_5,			KEY_5 },
+	{ "6",			AVC_6,			KEY_6 },
+	{ "7",			AVC_7,			KEY_7 },
+	{ "8",			AVC_8,			KEY_8 },
+	{ "9",			AVC_9,			KEY_9 },
+	{ "DOT",		AVC_DOT,		KEY_DOT },
+	{ "ENTER",		AVC_ENTER,		KEY_ENTER },
+	{ "CHANNEL UP",		AVC_CHANNEL_UP,		KEY_CHANNELUP },
+	{ "CHANNEL DOWN",	AVC_CHANNEL_DOWN,	KEY_CHANNELDOWN },
+	{ "CHANNEL PREVIOUS",	AVC_CHANNEL_PREVIOUS,	KEY_LAST },
+	{ "INPUT SELECT",	AVC_INPUT_SELECT,	KEY_CONFIG },
+	{ "INFO",		AVC_INFO,		KEY_INFO },
+	{ "HELP",		AVC_HELP,		KEY_HELP },
+	{ "POWER",		AVC_POWER,		KEY_POWER2 },
+	{ "VOLUME UP",		AVC_VOLUME_UP,		KEY_VOLUMEUP },
+	{ "VOLUME DOWN",	AVC_VOLUME_DOWN,	KEY_VOLUMEDOWN },
+	{ "MUTE",		AVC_MUTE,		KEY_MUTE },
+	{ "PLAY",		AVC_PLAY,		KEY_PLAYCD },
+	{ "STOP",		AVC_STOP,		KEY_STOPCD },
+	{ "PAUSE",		AVC_PAUSE,		KEY_PAUSECD },
+	{ "FORWARD",		AVC_FORWARD,		KEY_NEXTSONG },
+	{ "BACKWARD",		AVC_BACKWARD,		KEY_PREVIOUSSONG },
+	{ "RECORD",		AVC_RECORD,		KEY_RECORD },
+	{ "REWIND",		AVC_REWIND,		KEY_REWIND },
+	{ "FAST FORWARD",	AVC_FAST_FORWARD,	KEY_FASTFORWARD },
+	{ "LIST",		AVC_LIST,		KEY_LIST },
+	{ "F1",			AVC_F1,			KEY_F1 },
+	{ "F2",			AVC_F2,			KEY_F2 },
+	{ "F3",			AVC_F3,			KEY_F3 },
+	{ "F4",			AVC_F4,			KEY_F4 },
+	{ "F5",			AVC_F5,			KEY_F5 },
+	{ "F6",			AVC_F6,			KEY_F6 },
+	{ "F7",			AVC_F7,			KEY_F7 },
+	{ "F8",			AVC_F8,			KEY_F8 },
+	{ "F9",			AVC_F9,			KEY_F9 },
+	{ "RED",		AVC_RED,		KEY_RED },
+	{ "GREEN",		AVC_GREEN,		KEY_GREEN },
+	{ "BLUE",		AVC_BLUE,		KEY_BLUE },
+	{ "YELLOW",		AVC_YELLOW,		KEY_YELLOW },
+	{ NULL }
+};
+
+static gboolean process_queue(gpointer user_data);
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+	struct uinput_event event;
+	int err;
+
+	memset(&event, 0, sizeof(event));
+	event.type	= type;
+	event.code	= code;
+	event.value	= value;
+
+	do {
+		err = write(fd, &event, sizeof(event));
+	} while (err < 0 && errno == EINTR);
+
+	if (err < 0) {
+		err = -errno;
+		error("send_event: %s (%d)", strerror(-err), -err);
+	}
+
+	return err;
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+	send_event(fd, EV_KEY, key, pressed);
+	send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean auto_release(gpointer user_data)
+{
+	struct avctp *session = user_data;
+
+	session->key.timer = 0;
+
+	DBG("AV/C: key press timeout");
+
+	send_key(session->uinput, session->key.op, 0);
+
+	return FALSE;
+}
+
+static ssize_t handle_panel_passthrough(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avctp_passthrough_handler *handler = session->handler;
+	const char *status;
+	int pressed, i;
+
+	if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+		*code = AVC_CTYPE_REJECTED;
+		return operand_count;
+	}
+
+	if (operand_count == 0)
+		goto done;
+
+	if (operands[0] & 0x80) {
+		status = "released";
+		pressed = 0;
+	} else {
+		status = "pressed";
+		pressed = 1;
+	}
+
+	if (session->key.timer == 0 && handler != NULL) {
+		if (handler->cb(session, operands[0] & 0x7F,
+						pressed, handler->user_data))
+			goto done;
+	}
+
+	if (session->uinput < 0) {
+		DBG("AV/C: uinput not initialized");
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		uint8_t key_quirks;
+
+		if ((operands[0] & 0x7F) != key_map[i].avc)
+			continue;
+
+		DBG("AV/C: %s %s", key_map[i].name, status);
+
+		key_quirks = session->key_quirks[key_map[i].avc];
+
+		if (key_quirks & QUIRK_NO_RELEASE) {
+			if (!pressed) {
+				DBG("AV/C: Ignoring release");
+				break;
+			}
+
+			DBG("AV/C: treating key press as press + release");
+			send_key(session->uinput, key_map[i].uinput, 1);
+			send_key(session->uinput, key_map[i].uinput, 0);
+			break;
+		}
+
+		if (pressed) {
+			if (session->key.timer > 0) {
+				g_source_remove(session->key.timer);
+				send_key(session->uinput, session->key.op, 0);
+			}
+
+			session->key.op = key_map[i].uinput;
+			session->key.timer = g_timeout_add_seconds(
+							AVC_PRESS_TIMEOUT,
+							auto_release,
+							session);
+		} else if (session->key.timer > 0) {
+			g_source_remove(session->key.timer);
+			session->key.timer = 0;
+		}
+
+		send_key(session->uinput, key_map[i].uinput, pressed);
+		break;
+	}
+
+	if (key_map[i].name == NULL) {
+		DBG("AV/C: unknown button 0x%02X %s",
+						operands[0] & 0x7F, status);
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return operand_count;
+	}
+
+done:
+	*code = AVC_CTYPE_ACCEPTED;
+	return operand_count;
+}
+
+static ssize_t handle_unit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/*
+	 * The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it
+	 */
+	if (operand_count >= 1)
+		operands[0] = 0x07;
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_UNITINFO");
+
+	return operand_count;
+}
+
+static ssize_t handle_subunit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/*
+	 * The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it
+	 */
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_SUBUNITINFO");
+
+	return operand_count;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+	for (; list; list = list->next) {
+		struct avctp_pdu_handler *handler = list->data;
+
+		if (handler->opcode == opcode)
+			return handler;
+	}
+
+	return NULL;
+}
+
+static void pending_destroy(gpointer data, gpointer user_data)
+{
+	struct avctp_pending_req *req = data;
+
+	if (req->destroy)
+		req->destroy(req->data);
+
+	if (req->timeout > 0)
+		g_source_remove(req->timeout);
+
+	g_free(req);
+}
+
+static void avctp_channel_destroy(struct avctp_channel *chan)
+{
+	g_io_channel_shutdown(chan->io, TRUE, NULL);
+	g_io_channel_unref(chan->io);
+
+	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_foreach(chan->processed, pending_destroy, NULL);
+	g_slist_free(chan->processed);
+	g_slist_free_full(chan->handlers, g_free);
+	g_free(chan);
+}
+
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+				uint8_t cr, uint8_t code,
+				uint8_t subunit, uint8_t opcode,
+				const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_header avctp;
+	struct avc_header avc;
+	struct msghdr msg;
+	int sk, err = 0;
+	struct iovec pdu[iov_cnt + 2];
+	int i;
+	size_t len = sizeof(avctp) + sizeof(avc);
+
+	DBG("");
+
+	pdu[0].iov_base = &avctp;
+	pdu[0].iov_len  = sizeof(avctp);
+	pdu[1].iov_base = &avc;
+	pdu[1].iov_len  = sizeof(avc);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 2].iov_base = iov[i].iov_base;
+		pdu[i + 2].iov_len  = iov[i].iov_len;
+		len += iov[i].iov_len;
+	}
+
+	if (control->omtu < len)
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(control->io);
+
+	memset(&avctp, 0, sizeof(avctp));
+
+	avctp.transaction = transaction;
+	avctp.packet_type = AVCTP_PACKET_SINGLE;
+	avctp.cr = cr;
+	avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	memset(&avc, 0, sizeof(avc));
+
+	avc.code = code;
+	avc.subunit_type = subunit;
+	avc.opcode = opcode;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = pdu;
+	msg.msg_iovlen = iov_cnt + 2;
+
+	if (sendmsg(sk, &msg, 0) < 0)
+		err = -errno;
+
+	return err;
+}
+
+static int avctp_browsing_send(struct avctp_channel *browsing,
+				uint8_t transaction, uint8_t cr,
+				const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_header avctp;
+	struct msghdr msg;
+	struct iovec pdu[iov_cnt + 1];
+	int sk, err = 0;
+	int i;
+	size_t len = sizeof(avctp);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len  = iov[i].iov_len;
+		len += iov[i].iov_len;
+	}
+
+	pdu[0].iov_base = &avctp;
+	pdu[0].iov_len  = sizeof(avctp);
+
+	if (browsing->omtu < len)
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(browsing->io);
+
+	memset(&avctp, 0, sizeof(avctp));
+
+	avctp.transaction = transaction;
+	avctp.packet_type = AVCTP_PACKET_SINGLE;
+	avctp.cr = cr;
+	avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = pdu;
+	msg.msg_iovlen = iov_cnt + 1;
+
+	if (sendmsg(sk, &msg, 0) < 0)
+		err = -errno;
+
+	return err;
+}
+
+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;
+	int i;
+
+	if (p->err == 0 || req->func == NULL)
+		goto done;
+
+	req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
+							req->user_data);
+
+done:
+	for (i = 0; i < req->iov_cnt; i++)
+		g_free(req->iov[i].iov_base);
+
+	g_free(req->iov);
+	g_free(req);
+}
+
+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;
+	int i;
+
+	if (p->err == 0 || req->func == NULL)
+		goto done;
+
+	req->func(session, NULL, 0, req->user_data);
+
+done:
+	for (i = 0; i < req->iov_cnt; i++)
+		g_free(req->iov[i].iov_base);
+
+	g_free(req->iov);
+	g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+	struct avctp_channel *chan = user_data;
+	struct avctp_pending_req *p = chan->p;
+
+	DBG("transaction %u", p->transaction);
+
+	p->timeout = 0;
+	p->err = -ETIMEDOUT;
+
+	pending_destroy(p, NULL);
+	chan->p = NULL;
+
+	if (chan->process_id == 0)
+		chan->process_id = g_idle_add(process_queue, chan);
+
+	return FALSE;
+}
+
+static int process_control(void *data)
+{
+	struct avctp_control_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+				req->subunit, req->op, req->iov, req->iov_cnt);
+}
+
+static int process_browsing(void *data)
+{
+	struct avctp_browsing_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+						req->iov, req->iov_cnt);
+}
+
+static gboolean process_queue(void *user_data)
+{
+	struct avctp_channel *chan = user_data;
+	struct avctp_pending_req *p = chan->p;
+
+	chan->process_id = 0;
+
+	if (p != NULL)
+		return FALSE;
+
+	while ((p = g_queue_pop_head(chan->queue))) {
+
+		if (p->process(p->data) == 0)
+			break;
+
+		pending_destroy(p, NULL);
+	}
+
+	if (p == NULL)
+		return FALSE;
+
+	chan->p = p;
+	p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+	return FALSE;
+
+}
+
+static struct avctp *avctp_ref(struct avctp *session)
+{
+	__sync_fetch_and_add(&session->ref, 1);
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	return session;
+}
+
+static void avctp_unref(struct avctp *session)
+{
+	DBG("%p: ref=%d", session, session->ref);
+
+	if (__sync_sub_and_fetch(&session->ref, 1))
+		return;
+
+	if (session->browsing)
+		avctp_channel_destroy(session->browsing);
+
+	if (session->control)
+		avctp_channel_destroy(session->control);
+
+	if (session->destroy)
+		session->destroy(session->data);
+
+	g_free(session->handler);
+
+	if (session->key.timer > 0)
+		g_source_remove(session->key.timer);
+
+	if (session->uinput >= 0) {
+		DBG("AVCTP: closing uinput");
+
+		ioctl(session->uinput, UI_DEV_DESTROY);
+		close(session->uinput);
+		session->uinput = -1;
+	}
+
+	g_free(session);
+}
+
+static void control_response(struct avctp_channel *control,
+					struct avctp_header *avctp,
+					struct avc_header *avc,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = control->p;
+	struct avctp_control_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		control->processed = g_slist_prepend(control->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		control->p = NULL;
+
+		if (control->process_id == 0)
+			control->process_id = g_idle_add(process_queue,
+								control);
+	}
+
+	avctp_ref(control->session);
+
+	for (l = control->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(control->session, avc->code,
+						avc->subunit_type,
+						operands, operand_count,
+						req->user_data))
+			break;
+
+		control->processed = g_slist_remove(control->processed, p);
+		pending_destroy(p, NULL);
+
+		break;
+	}
+
+	avctp_unref(control->session);
+}
+
+static void browsing_response(struct avctp_channel *browsing,
+					struct avctp_header *avctp,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = browsing->p;
+	struct avctp_browsing_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		browsing->processed = g_slist_prepend(browsing->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		browsing->p = NULL;
+
+		if (browsing->process_id == 0)
+			browsing->process_id = g_idle_add(process_queue,
+								browsing);
+	}
+
+	avctp_ref(browsing->session);
+
+	for (l = browsing->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(browsing->session, operands,
+						operand_count, req->user_data))
+			break;
+
+		browsing->processed = g_slist_remove(browsing->processed, p);
+		pending_destroy(p, NULL);
+
+		break;
+	}
+
+	avctp_unref(browsing->session);
+}
+
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *browsing = session->browsing;
+	uint8_t *buf = browsing->buffer;
+	uint8_t *operands;
+	struct avctp_header *avctp;
+	int sock, ret, packet_size, operand_count;
+	struct avctp_browsing_pdu_handler *handler;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(chan);
+
+	ret = read(sock, buf, browsing->imtu);
+	if (ret <= 0)
+		goto failed;
+
+	if (ret < AVCTP_HEADER_LENGTH) {
+		error("Too small AVCTP packet");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+		error("Invalid packet type");
+		goto failed;
+	}
+
+	operands = buf + AVCTP_HEADER_LENGTH;
+	ret -= AVCTP_HEADER_LENGTH;
+	operand_count = ret;
+
+	if (avctp->cr == AVCTP_RESPONSE) {
+		browsing_response(browsing, avctp, operands, operand_count);
+		return TRUE;
+	}
+
+	packet_size = AVCTP_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	handler = g_slist_nth_data(browsing->handlers, 0);
+	if (handler == NULL) {
+		DBG("handler not found");
+		/* FIXME: Add general reject */
+		/* packet_size += avrcp_browsing_general_reject(operands); */
+		goto send;
+	}
+
+	ret = handler->cb(session, avctp->transaction, operands, operand_count,
+							handler->user_data);
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return TRUE;
+		goto failed;
+	}
+
+	packet_size += ret;
+
+send:
+	if (packet_size != 0) {
+		ret = write(sock, buf, packet_size);
+		if (ret != packet_size)
+			goto failed;
+	}
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP Browsing: disconnected");
+
+	if (session->browsing) {
+		avctp_channel_destroy(session->browsing);
+		session->browsing = NULL;
+	}
+
+	return FALSE;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *control = session->control;
+	uint8_t *buf = control->buffer;
+	uint8_t *operands, code, subunit;
+	struct avctp_header *avctp;
+	struct avc_header *avc;
+	int packet_size, operand_count, sock;
+	struct avctp_pdu_handler *handler;
+	ssize_t ret;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(chan);
+
+	ret = read(sock, buf, control->imtu);
+	if (ret <= 0)
+		goto failed;
+
+	if (ret < AVCTP_HEADER_LENGTH) {
+		error("Too small AVCTP packet");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	ret -= AVCTP_HEADER_LENGTH;
+	if (ret < AVC_HEADER_LENGTH) {
+		error("Too small AVC packet");
+		goto failed;
+	}
+
+	avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH);
+
+	ret -= AVC_HEADER_LENGTH;
+
+	operands = (uint8_t *) avc + AVC_HEADER_LENGTH;
+	operand_count = ret;
+
+	if (avctp->cr == AVCTP_RESPONSE) {
+		control_response(control, avctp, avc, operands, operand_count);
+		return TRUE;
+	}
+
+	packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+		avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+		goto done;
+	}
+
+	if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+		avctp->ipid = 1;
+		packet_size = AVCTP_HEADER_LENGTH;
+		goto done;
+	}
+
+	handler = find_handler(control->handlers, avc->opcode);
+	if (!handler) {
+		DBG("handler not found for 0x%02x", avc->opcode);
+		avc->code = AVC_CTYPE_REJECTED;
+		goto done;
+	}
+
+	code = avc->code;
+	subunit = avc->subunit_type;
+
+	ret = handler->cb(session, avctp->transaction, &code,
+					&subunit, operands, operand_count,
+					handler->user_data);
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return TRUE;
+		goto failed;
+	}
+
+	packet_size += ret;
+	avc->code = code;
+	avc->subunit_type = subunit;
+
+done:
+	ret = write(sock, buf, packet_size);
+	if (ret != packet_size)
+		goto failed;
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP session %p got disconnected", session);
+	avctp_shutdown(session);
+	return FALSE;
+}
+
+static int uinput_create(const char *name)
+{
+	struct uinput_dev dev;
+	int fd, err, i;
+
+	fd = open("/dev/uinput", O_RDWR);
+	if (fd < 0) {
+		fd = open("/dev/input/uinput", O_RDWR);
+		if (fd < 0) {
+			fd = open("/dev/misc/uinput", O_RDWR);
+			if (fd < 0) {
+				err = -errno;
+				error("Can't open input device: %s (%d)",
+							strerror(-err), -err);
+				return err;
+			}
+		}
+	}
+
+	memset(&dev, 0, sizeof(dev));
+	if (name)
+		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+	dev.id.bustype = BUS_BLUETOOTH;
+	dev.id.vendor  = 0x0000;
+	dev.id.product = 0x0000;
+	dev.id.version = 0x0000;
+
+	if (write(fd, &dev, sizeof(dev)) < 0) {
+		err = -errno;
+		error("Can't write device information: %s (%d)",
+						strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	ioctl(fd, UI_SET_EVBIT, EV_KEY);
+	ioctl(fd, UI_SET_EVBIT, EV_REL);
+	ioctl(fd, UI_SET_EVBIT, EV_REP);
+	ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+	for (i = 0; key_map[i].name != NULL; i++)
+		ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+	if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+		err = -errno;
+		error("Can't create uinput device: %s (%d)",
+						strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	return fd;
+}
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+							const char *address)
+{
+	if (g_str_equal(name, "Nokia CK-20W")) {
+		session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
+	}
+
+	session->uinput = uinput_create(address);
+	if (session->uinput < 0) {
+		error("AVCTP: failed to init uinput for %s", address);
+		return session->uinput;
+	}
+
+	return 0;
+}
+
+static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd,
+						size_t imtu, size_t omtu,
+						avctp_destroy_cb_t destroy)
+{
+	struct avctp_channel *chan;
+
+	chan = g_new0(struct avctp_channel, 1);
+	chan->session = session;
+	chan->io = g_io_channel_unix_new(fd);
+	chan->queue = g_queue_new();
+	chan->imtu = imtu;
+	chan->omtu = omtu;
+	chan->buffer = g_malloc0(MAX(imtu, omtu));
+	chan->destroy = destroy;
+
+	return chan;
+}
+
+static void handler_free(void *data)
+{
+	struct avctp_browsing_pdu_handler *handler = data;
+
+	if (handler->destroy)
+		handler->destroy(handler->user_data);
+
+	g_free(data);
+}
+
+static void avctp_destroy_browsing(void *data)
+{
+	struct avctp_channel *chan = data;
+
+	g_slist_free_full(chan->handlers, handler_free);
+
+	chan->handlers = NULL;
+}
+
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+						avctp_process_cb process,
+						void *data,
+						avctp_destroy_cb_t 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->process = process;
+	p->data = data;
+	p->destroy = destroy;
+
+	chan->transaction++;
+	chan->transaction %= 16;
+
+	return p;
+}
+
+static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit,
+			uint8_t opcode, const struct iovec *iov, int iov_cnt,
+			avctp_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_pending_req *p;
+	struct avctp_control_req *req;
+	struct iovec *pdu;
+	int i;
+
+	if (control == NULL)
+		return -ENOTCONN;
+
+	pdu = g_new0(struct iovec, iov_cnt);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i].iov_len = iov[i].iov_len;
+		pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+	}
+
+	req = g_new0(struct avctp_control_req, 1);
+	req->code = code;
+	req->subunit = subunit;
+	req->op = opcode;
+	req->func = func;
+	req->iov = pdu;
+	req->iov_cnt = iov_cnt;
+	req->user_data = user_data;
+
+	p = pending_create(control, process_control, req, control_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(control->queue, p);
+
+	if (control->process_id == 0)
+		control->process_id = g_idle_add(process_queue, control);
+
+	return 0;
+}
+
+int avctp_send_browsing_req(struct avctp *session,
+				const struct iovec *iov, int iov_cnt,
+				avctp_browsing_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_pending_req *p;
+	struct avctp_browsing_req *req;
+	struct iovec *pdu;
+	int i;
+
+	if (browsing == NULL)
+		return -ENOTCONN;
+
+	pdu = g_new0(struct iovec, iov_cnt);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i].iov_len = iov[i].iov_len;
+		pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+	}
+
+	req = g_new0(struct avctp_browsing_req, 1);
+	req->func = func;
+	req->iov = pdu;
+	req->iov_cnt = iov_cnt;
+	req->user_data = user_data;
+
+	p = pending_create(browsing, process_browsing, req,
+			browsing_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(browsing->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);
+
+	return 0;
+}
+
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+					const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_channel *browsing = session->browsing;
+
+	if (browsing == NULL)
+		return -ENOTCONN;
+
+	return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE,
+								iov, iov_cnt);
+}
+
+static const char *op2str(uint8_t op)
+{
+	int i;
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		if ((op & 0x7F) == key_map[i].avc)
+			return key_map[i].name;
+	}
+
+	return "UNKNOWN";
+}
+
+static int avctp_passthrough_press(struct avctp *session, uint8_t op,
+					uint8_t *params, size_t params_len)
+{
+	struct iovec iov[2];
+	int iov_cnt;
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	iov[0].iov_base = operands;
+	iov[0].iov_len = sizeof(operands);
+
+	/* Button pressed */
+	operands[0] = op & 0x7f;
+
+	if (params_len > 0) {
+		iov[1].iov_base = params;
+		iov[1].iov_len = params_len;
+		iov_cnt = 2;
+		operands[1] = params_len;
+	} else {
+		iov_cnt = 1;
+		operands[1] = 0;
+	}
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				iov, iov_cnt, avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op,
+					uint8_t *params, size_t params_len)
+{
+	struct iovec iov[2];
+	int iov_cnt;
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	iov[0].iov_base = operands;
+	iov[0].iov_len = sizeof(operands);
+
+	/* Button released */
+	operands[0] = op | 0x80;
+
+	if (params_len > 0) {
+		iov[1].iov_base = params;
+		iov[1].iov_len = params_len;
+		iov_cnt = 2;
+		operands[1] = params_len;
+	} else {
+		iov_cnt = 1;
+		operands[1] = 0;
+	}
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				iov, iov_cnt, NULL, NULL);
+}
+
+static gboolean repeat_timeout(gpointer user_data)
+{
+	struct avctp *session = user_data;
+
+	avctp_passthrough_release(session, session->key.op, session->key.params,
+						session->key.params_len);
+	avctp_passthrough_press(session, session->key.op, session->key.params,
+						session->key.params_len);
+
+	return TRUE;
+}
+
+static void release_pressed(struct avctp *session)
+{
+	avctp_passthrough_release(session, session->key.op, session->key.params,
+						session->key.params_len);
+
+	if (session->key.timer > 0)
+		g_source_remove(session->key.timer);
+
+	session->key.timer = 0;
+}
+
+static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params,
+							size_t params_len)
+{
+	if (session->key.timer > 0) {
+		if (session->key.op == op)
+			return TRUE;
+		release_pressed(session);
+	}
+
+	if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+		return FALSE;
+
+	session->key.op = op;
+	session->key.params = params;
+	session->key.params_len = params_len;
+	session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+							repeat_timeout,
+							session);
+
+	return TRUE;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	uint8_t *params;
+	size_t params_len;
+
+	DBG("code 0x%02x operand_count %zd", code, operand_count);
+
+	if (code != AVC_CTYPE_ACCEPTED)
+		return FALSE;
+
+	if (operands[0] == AVC_VENDOR_UNIQUE) {
+		params = &operands[2];
+		params_len = operand_count - 2;
+	} else {
+		params = NULL;
+		params_len = 0;
+	}
+
+	if (set_pressed(session, operands[0], params, params_len))
+		return FALSE;
+
+	avctp_passthrough_release(session, operands[0], params, params_len);
+
+	return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+							size_t params_len)
+{
+	/* Auto release if key pressed */
+	if (session->key.timer > 0)
+		release_pressed(session);
+
+	return avctp_passthrough_press(session, op, params, params_len);
+}
+
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				const struct iovec *iov, int iov_cnt)
+{
+	struct avctp_channel *control = session->control;
+
+	if (control == NULL)
+		return -ENOTCONN;
+
+	return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
+						AVC_OP_VENDORDEP, iov, iov_cnt);
+}
+
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+					const struct iovec *iov, int iov_cnt,
+					avctp_rsp_cb func, void *user_data)
+{
+	return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov,
+						iov_cnt, func, user_data);
+}
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+						avctp_passthrough_cb cb,
+						void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_passthrough_handler *handler;
+	static unsigned int id = 0;
+
+	if (control == NULL || session->handler != NULL)
+		return 0;
+
+	handler = g_new(struct avctp_passthrough_handler, 1);
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	session->handler = handler;
+
+	return handler->id;
+}
+
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+							unsigned int id)
+{
+	if (session->handler == NULL)
+		return false;
+
+	if (session->handler->id != id)
+		return false;
+
+	g_free(session->handler);
+	session->handler = NULL;
+	return true;
+}
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+						avctp_control_pdu_cb cb,
+						void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	if (control == NULL)
+		return 0;
+
+	handler = find_handler(control->handlers, opcode);
+	if (handler)
+		return 0;
+
+	handler = g_new(struct avctp_pdu_handler, 1);
+	handler->opcode = opcode;
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	control->handlers = g_slist_append(control->handlers, handler);
+
+	return handler->id;
+}
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+						avctp_browsing_pdu_cb cb,
+						void *user_data,
+						avctp_destroy_cb_t destroy)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_browsing_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	if (browsing == NULL)
+		return 0;
+
+	if (browsing->handlers != NULL)
+		return 0;
+
+	handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+	handler->destroy = destroy;
+
+	browsing->handlers = g_slist_append(browsing->handlers, handler);
+
+	return handler->id;
+}
+
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id)
+{
+	struct avctp_channel *control = session->control;
+	GSList *l;
+
+	if (!control)
+		return false;
+
+	for (l = control->handlers; l; l = g_slist_next(l)) {
+		struct avctp_pdu_handler *handler = l->data;
+
+		if (handler->id != id)
+			continue;
+
+		control->handlers = g_slist_remove(control->handlers, handler);
+		g_free(handler);
+		return true;
+	}
+
+	return false;
+}
+
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+							unsigned int id)
+{
+	struct avctp_channel *browsing = session->browsing;
+	GSList *l;
+
+	if (browsing == NULL)
+		return false;
+
+	for (l = browsing->handlers; l; l = g_slist_next(l)) {
+		struct avctp_browsing_pdu_handler *handler = l->data;
+
+		if (handler->id != id)
+			continue;
+
+		browsing->handlers = g_slist_remove(browsing->handlers,
+								handler);
+		g_free(handler);
+		return true;
+	}
+
+	return false;
+}
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+	struct avctp *session;
+	struct avctp_channel *control;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	session = g_new0(struct avctp, 1);
+	session->version = version;
+
+	control = avctp_channel_create(session, fd, imtu, omtu, NULL);
+	if (!control) {
+		g_free(session);
+		return NULL;
+	}
+
+	session->uinput = -1;
+	session->control = control;
+	session->passthrough_id = avctp_register_pdu_handler(session,
+						AVC_OP_PASSTHROUGH,
+						handle_panel_passthrough,
+						NULL);
+	session->unit_id = avctp_register_pdu_handler(session,
+						AVC_OP_UNITINFO,
+						handle_unit_info,
+						NULL);
+	session->subunit_id = avctp_register_pdu_handler(session,
+						AVC_OP_SUBUNITINFO,
+						handle_subunit_info,
+						NULL);
+
+	control->watch = g_io_add_watch(session->control->io, cond,
+						(GIOFunc) session_cb, session);
+
+	return avctp_ref(session);
+}
+
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+								size_t omtu)
+{
+	struct avctp_channel *browsing;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	if (session->browsing)
+		return -EISCONN;
+
+	browsing = avctp_channel_create(session, fd, imtu, omtu,
+						avctp_destroy_browsing);
+	if (!browsing)
+		return -EINVAL;
+
+	session->browsing = browsing;
+	browsing->watch = g_io_add_watch(session->browsing->io, cond,
+					(GIOFunc) session_browsing_cb, session);
+
+	return 0;
+}
+
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+							void *user_data)
+{
+	session->destroy = cb;
+	session->data = user_data;
+}
+
+void avctp_shutdown(struct avctp *session)
+{
+	if (!session)
+		return;
+
+	avctp_unref(session);
+}
diff --git a/bluez/repo/android/avctp.h b/bluez/repo/android/avctp.h
new file mode 100644
index 0000000..f0da2b3
--- /dev/null
+++ b/bluez/repo/android/avctp.h
@@ -0,0 +1,183 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+#define AVCTP_CONTROL_PSM		23
+#define AVCTP_BROWSING_PSM		27
+
+#define AVCTP_HEADER_LENGTH		3
+#define AVC_HEADER_LENGTH		3
+
+#define AVC_DATA_OFFSET			AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH
+#define AVC_DATA_MTU			512
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL		0x0
+#define AVC_CTYPE_STATUS		0x1
+#define AVC_CTYPE_NOTIFY		0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED	0x8
+#define AVC_CTYPE_ACCEPTED		0x9
+#define AVC_CTYPE_REJECTED		0xA
+#define AVC_CTYPE_STABLE		0xC
+#define AVC_CTYPE_CHANGED		0xD
+#define AVC_CTYPE_INTERIM		0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP		0x00
+#define AVC_OP_UNITINFO			0x30
+#define AVC_OP_SUBUNITINFO		0x31
+#define AVC_OP_PASSTHROUGH		0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL		0x09
+
+/* operands in passthrough commands */
+#define AVC_SELECT			0x00
+#define AVC_UP				0x01
+#define AVC_DOWN			0x02
+#define AVC_LEFT			0x03
+#define AVC_RIGHT			0x04
+#define AVC_ROOT_MENU			0x09
+#define AVC_CONTENTS_MENU		0x0b
+#define AVC_FAVORITE_MENU		0x0c
+#define AVC_EXIT			0x0d
+#define AVC_ON_DEMAND_MENU		0x0e
+#define AVC_APPS_MENU			0x0f
+#define AVC_0				0x20
+#define AVC_1				0x21
+#define AVC_2				0x22
+#define AVC_3				0x23
+#define AVC_4				0x24
+#define AVC_5				0x25
+#define AVC_6				0x26
+#define AVC_7				0x27
+#define AVC_8				0x28
+#define AVC_9				0x29
+#define AVC_DOT				0x2a
+#define AVC_ENTER			0x2b
+#define AVC_CHANNEL_UP			0x30
+#define AVC_CHANNEL_DOWN		0x31
+#define AVC_CHANNEL_PREVIOUS		0x32
+#define AVC_INPUT_SELECT		0x34
+#define AVC_INFO			0x35
+#define AVC_HELP			0x36
+#define AVC_PAGE_UP			0x37
+#define AVC_PAGE_DOWN			0x38
+#define AVC_LOCK			0x3a
+#define AVC_POWER			0x40
+#define AVC_VOLUME_UP			0x41
+#define AVC_VOLUME_DOWN			0x42
+#define AVC_MUTE			0x43
+#define AVC_PLAY			0x44
+#define AVC_STOP			0x45
+#define AVC_PAUSE			0x46
+#define AVC_RECORD			0x47
+#define AVC_REWIND			0x48
+#define AVC_FAST_FORWARD		0x49
+#define AVC_EJECT			0x4a
+#define AVC_FORWARD			0x4b
+#define AVC_BACKWARD			0x4c
+#define AVC_LIST			0x4d
+#define AVC_F1				0x71
+#define AVC_F2				0x72
+#define AVC_F3				0x73
+#define AVC_F4				0x74
+#define AVC_F5				0x75
+#define AVC_F6				0x76
+#define AVC_F7				0x77
+#define AVC_F8				0x78
+#define AVC_F9				0x79
+#define AVC_RED				0x7a
+#define AVC_GREEN			0x7b
+#define AVC_BLUE			0x7c
+#define AVC_YELLOW			0x7c
+
+#define AVC_VENDOR_UNIQUE		0x7e
+
+#define AVC_VENDOR_NEXT_GROUP		0x00
+#define AVC_VENDOR_PREV_GROUP		0x01
+
+struct avctp;
+
+typedef bool (*avctp_passthrough_cb) (struct avctp *session,
+					uint8_t op, bool pressed,
+					void *user_data);
+typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+
+typedef void (*avctp_destroy_cb_t) (void *user_data);
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+							void *user_data);
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+							const char *address);
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+								size_t omtu);
+
+void avctp_shutdown(struct avctp *session);
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+						avctp_passthrough_cb cb,
+						void *user_data);
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+							unsigned int id);
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+						avctp_control_pdu_cb cb,
+						void *user_data);
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id);
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+						avctp_browsing_pdu_cb cb,
+						void *user_data,
+						avctp_destroy_cb_t destroy);
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+							unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+							size_t params_len);
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				const struct iovec *iov, int iov_cnt);
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+					const struct iovec *iov, int iov_cnt,
+					avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+					const struct iovec *iov, int iov_cnt);
+int avctp_send_browsing_req(struct avctp *session,
+				const struct iovec *iov, int iov_cnt,
+				avctp_browsing_rsp_cb func, void *user_data);
diff --git a/bluez/repo/android/avdtp.c b/bluez/repo/android/avdtp.c
new file mode 100644
index 0000000..bab305b
--- /dev/null
+++ b/bluez/repo/android/avdtp.c
@@ -0,0 +1,3484 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "avdtp.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_SEID 0x3E
+static unsigned int seids;
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER				0x01
+#define AVDTP_GET_CAPABILITIES			0x02
+#define AVDTP_SET_CONFIGURATION			0x03
+#define AVDTP_GET_CONFIGURATION			0x04
+#define AVDTP_RECONFIGURE			0x05
+#define AVDTP_OPEN				0x06
+#define AVDTP_START				0x07
+#define AVDTP_CLOSE				0x08
+#define AVDTP_SUSPEND				0x09
+#define AVDTP_ABORT				0x0A
+#define AVDTP_SECURITY_CONTROL			0x0B
+#define AVDTP_GET_ALL_CAPABILITIES		0x0C
+#define AVDTP_DELAY_REPORT			0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE			0x00
+#define AVDTP_PKT_TYPE_START			0x01
+#define AVDTP_PKT_TYPE_CONTINUE			0x02
+#define AVDTP_PKT_TYPE_END			0x03
+
+#define AVDTP_MSG_TYPE_COMMAND			0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT		0x01
+#define AVDTP_MSG_TYPE_ACCEPT			0x02
+#define AVDTP_MSG_TYPE_REJECT			0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define START_TIMEOUT 1
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t no_of_packets;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t rfa0:1;
+	uint8_t inuse:1;
+	uint8_t seid:6;
+	uint8_t rfa2:3;
+	uint8_t type:1;
+	uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t rfa0:2;
+	uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t no_of_packets;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t seid:6;
+	uint8_t inuse:1;
+	uint8_t rfa0:1;
+	uint8_t media_type:4;
+	uint8_t type:1;
+	uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+	struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+	uint8_t category;
+	uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t rfa1:2;
+	uint8_t int_seid:6;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t int_seid:6;
+	uint8_t rfa1:2;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+	gboolean active;
+	int no_of_packets;
+	uint8_t transaction;
+	uint8_t message_type;
+	uint8_t signal_id;
+	uint8_t buf[1024];
+	uint8_t data_size;
+};
+
+struct pending_req {
+	uint8_t transaction;
+	uint8_t signal_id;
+	void *data;
+	size_t data_size;
+	struct avdtp_stream *stream; /* Set if the request targeted a stream */
+	guint timeout;
+	gboolean collided;
+};
+
+struct avdtp_remote_sep {
+	uint8_t seid;
+	uint8_t type;
+	uint8_t media_type;
+	struct avdtp_service_capability *codec;
+	gboolean delay_reporting;
+	GSList *caps; /* of type struct avdtp_service_capability */
+	struct avdtp_stream *stream;
+};
+
+struct avdtp_local_sep {
+	avdtp_state_t state;
+	struct avdtp_stream *stream;
+	struct seid_info info;
+	uint8_t codec;
+	uint32_t vndcodec_vendor;
+	uint16_t vndcodec_codec;
+	gboolean delay_reporting;
+	GSList *caps;
+	struct avdtp_sep_ind *ind;
+	struct avdtp_sep_cfm *cfm;
+	void *user_data;
+};
+
+struct stream_callback {
+	avdtp_stream_state_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct discover_callback {
+	unsigned int id;
+	avdtp_discover_cb_t cb;
+	void *user_data;
+};
+
+struct disconnect_callback {
+	unsigned int id;
+	avdtp_disconnect_cb_t cb;
+	void *user_data;
+};
+
+struct avdtp_stream {
+	GIOChannel *io;
+	uint16_t imtu;
+	uint16_t omtu;
+	struct avdtp *session;
+	struct avdtp_local_sep *lsep;
+	uint8_t rseid;
+	GSList *caps;
+	GSList *callbacks;
+	struct avdtp_service_capability *codec;
+	guint io_id;		/* Transport GSource ID */
+	guint timer;		/* Waiting for other side to close or open
+				 * the transport channel */
+	gboolean open_acp;	/* If we are in ACT role for Open */
+	gboolean close_int;	/* If we are in INT role for Close */
+	gboolean abort_int;	/* If we are in INT role for Abort */
+	guint start_timer;	/* Wait START command timer */
+	gboolean delay_reporting;
+	uint16_t delay;		/* AVDTP 1.3 Delay Reporting feature */
+	gboolean starting;	/* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+	unsigned int ref;
+
+	uint16_t version;
+
+	guint auth_id;
+
+	GIOChannel *io;
+	guint io_id;
+
+	GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+	struct queue *lseps; /* Elements of type struct avdtp_local_sep * */
+
+	GSList *streams; /* Elements of type struct avdtp_stream * */
+
+	GSList *req_queue; /* Elements of type struct pending_req * */
+	GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+	struct avdtp_stream *pending_open;
+
+	uint16_t imtu;
+	uint16_t omtu;
+
+	struct in_buf in;
+
+	char *buf;
+
+	struct discover_callback *discover;
+	struct pending_req *req;
+
+	GSList *disconnect;
+
+	bool shutdown;
+};
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state);
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+	switch (state) {
+	case AVDTP_STATE_IDLE:
+		return "IDLE";
+	case AVDTP_STATE_CONFIGURED:
+		return "CONFIGURED";
+	case AVDTP_STATE_OPEN:
+		return "OPEN";
+	case AVDTP_STATE_STREAMING:
+		return "STREAMING";
+	case AVDTP_STATE_CLOSING:
+		return "CLOSING";
+	case AVDTP_STATE_ABORTING:
+		return "ABORTING";
+	default:
+		return "<unknown state>";
+	}
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+	int err;
+
+	do {
+		err = send(sk, data, len, 0);
+	} while (err < 0 && errno == EINTR);
+
+	if (err < 0) {
+		error("send: %s (%d)", strerror(errno), errno);
+		return FALSE;
+	} else if ((size_t) err != len) {
+		error("try_send: complete buffer not sent (%d/%zu bytes)",
+								err, len);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+				uint8_t message_type, uint8_t signal_id,
+				void *data, size_t len)
+{
+	unsigned int cont_fragments, sent;
+	struct avdtp_start_header start;
+	struct avdtp_continue_header cont;
+	int sock;
+
+	if (session->io == NULL) {
+		error("avdtp_send: session is closed");
+		return FALSE;
+	}
+
+	sock = g_io_channel_unix_get_fd(session->io);
+
+	/* Single packet - no fragmentation */
+	if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+		struct avdtp_single_header single;
+
+		memset(&single, 0, sizeof(single));
+
+		single.transaction = transaction;
+		single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+		single.message_type = message_type;
+		single.signal_id = signal_id;
+
+		memcpy(session->buf, &single, sizeof(single));
+		memcpy(session->buf + sizeof(single), data, len);
+
+		return try_send(sock, session->buf, sizeof(single) + len);
+	}
+
+	/* Check if there is enough space to start packet */
+	if (session->omtu < sizeof(start)) {
+		error("No enough space to fragment packet");
+		return FALSE;
+	}
+
+	/* Count the number of needed fragments */
+	cont_fragments = (len - (session->omtu - sizeof(start))) /
+					(session->omtu - sizeof(cont)) + 1;
+
+	DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+	/* Send the start packet */
+	memset(&start, 0, sizeof(start));
+	start.transaction = transaction;
+	start.packet_type = AVDTP_PKT_TYPE_START;
+	start.message_type = message_type;
+	start.no_of_packets = cont_fragments + 1;
+	start.signal_id = signal_id;
+
+	memcpy(session->buf, &start, sizeof(start));
+	memcpy(session->buf + sizeof(start), data,
+					session->omtu - sizeof(start));
+
+	if (!try_send(sock, session->buf, session->omtu))
+		return FALSE;
+
+	DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+	sent = session->omtu - sizeof(start);
+
+	/* Send the continue fragments and the end packet */
+	while (sent < len) {
+		int left, to_copy;
+
+		left = len - sent;
+		if (left + sizeof(cont) > session->omtu) {
+			cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+			to_copy = session->omtu - sizeof(cont);
+			DBG("sending continue with %d bytes", to_copy);
+		} else {
+			cont.packet_type = AVDTP_PKT_TYPE_END;
+			to_copy = left;
+			DBG("sending end with %d bytes", to_copy);
+		}
+
+		cont.transaction = transaction;
+		cont.message_type = message_type;
+
+		memcpy(session->buf, &cont, sizeof(cont));
+		memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+		if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+			return FALSE;
+
+		sent += to_copy;
+	}
+
+	return TRUE;
+}
+
+static void pending_req_free(void *data)
+{
+	struct pending_req *req = data;
+
+	if (req->timeout)
+		g_source_remove(req->timeout);
+	g_free(req->data);
+	g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+	int sock;
+
+	if (stream->io == NULL)
+		return;
+
+	sock = g_io_channel_unix_get_fd(stream->io);
+
+	shutdown(sock, SHUT_RDWR);
+
+	g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+	g_io_channel_unref(stream->io);
+	stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to close the transport channel");
+
+	stream->timer = 0;
+
+	close_stream(stream);
+
+	return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to open the transport channel");
+
+	stream->timer = 0;
+
+	stream->session->pending_open = NULL;
+
+	avdtp_abort(stream->session, stream);
+
+	return FALSE;
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+	err->category = category;
+
+	if (category == AVDTP_ERRNO)
+		err->err.posix_errno = id;
+	else
+		err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+	return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+	assert(err->category != AVDTP_ERRNO);
+	return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+	assert(err->category == AVDTP_ERRNO);
+	return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+							uint8_t rseid)
+{
+	GSList *l;
+
+	for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_stream *stream = l->data;
+
+		if (stream->rseid == rseid)
+			return stream;
+	}
+
+	return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+	GSList *l;
+
+	for (l = seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static void stream_free(void *data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_remote_sep *rsep;
+
+	stream->lsep->info.inuse = 0;
+	stream->lsep->stream = NULL;
+
+	rsep = find_remote_sep(stream->session->seps, stream->rseid);
+	if (rsep)
+		rsep->stream = NULL;
+
+	if (stream->timer)
+		g_source_remove(stream->timer);
+
+	if (stream->start_timer > 0)
+		g_source_remove(stream->start_timer);
+
+	if (stream->io)
+		close_stream(stream);
+
+	if (stream->io_id)
+		g_source_remove(stream->io_id);
+
+	g_slist_free_full(stream->callbacks, g_free);
+	g_slist_free_full(stream->caps, g_free);
+
+	g_free(stream);
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (stream->close_int && sep->cfm && sep->cfm->close)
+		sep->cfm->close(stream->session, sep, stream, NULL,
+				sep->user_data);
+
+	if (!(cond & G_IO_NVAL))
+		close_stream(stream);
+
+	stream->io_id = 0;
+
+	if (!stream->abort_int)
+		avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+	return FALSE;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+					uint16_t imtu, uint16_t omtu)
+{
+	struct avdtp_stream *stream = session->pending_open;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	session->pending_open = NULL;
+
+	if (stream->timer) {
+		g_source_remove(stream->timer);
+		stream->timer = 0;
+	}
+
+	if (io == NULL)
+		return;
+
+	if (stream->io == NULL)
+		stream->io = g_io_channel_ref(io);
+
+	stream->omtu = omtu;
+	stream->imtu = imtu;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct pending_req *req = a;
+	const struct avdtp_stream *stream = b;
+
+	if (req->stream == stream)
+		return 0;
+
+	return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+	GSList *l;
+	struct pending_req *req;
+
+	while ((l = g_slist_find_custom(session->prio_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->prio_queue = g_slist_remove(session->prio_queue, req);
+	}
+
+	while ((l = g_slist_find_custom(session->req_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->req_queue = g_slist_remove(session->req_queue, req);
+	}
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+						struct avdtp_stream *stream)
+{
+	struct pending_req *req;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_error err;
+
+	if (!session->req->timeout)
+		/* Request is in process */
+		return;
+
+	if (session->req->signal_id == AVDTP_ABORT) {
+		/* Avoid freeing the Abort request here */
+		DBG("handle_unanswered_req: Abort req, returning");
+		session->req->stream = NULL;
+		return;
+	}
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+	lsep = stream->lsep;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("No reply to Reconfigure request");
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("No reply to Open request");
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &err,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("No reply to Start request");
+		if (lsep && lsep->cfm && lsep->cfm->start)
+			lsep->cfm->start(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SUSPEND:
+		error("No reply to Suspend request");
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("No reply to Close request");
+		if (lsep && lsep->cfm && lsep->cfm->close)
+			lsep->cfm->close(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("No reply to SetConfiguration request");
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+							&err, lsep->user_data);
+	}
+
+	pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state)
+{
+	struct avdtp_stream *stream = sep->stream;
+	avdtp_state_t old_state;
+	struct avdtp_error err, *err_ptr = NULL;
+	GSList *l;
+
+	if (!stream) {
+		error("Error changing sep state: stream not available");
+		return;
+	}
+
+	if (sep->state == state) {
+		avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+		DBG("stream state change failed: %s", avdtp_strerror(&err));
+		err_ptr = &err;
+	} else {
+		err_ptr = NULL;
+		DBG("stream state changed: %s -> %s",
+				avdtp_statestr(sep->state),
+				avdtp_statestr(state));
+	}
+
+	old_state = sep->state;
+	sep->state = state;
+
+	switch (state) {
+	case AVDTP_STATE_CONFIGURED:
+		if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+			avdtp_delay_report(session, stream, stream->delay);
+		break;
+	case AVDTP_STATE_OPEN:
+		stream->starting = FALSE;
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		stream->open_acp = FALSE;
+		break;
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		break;
+	case AVDTP_STATE_IDLE:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		if (session->pending_open == stream)
+			handle_transport_connect(session, NULL, 0, 0);
+		if (session->req && session->req->stream == stream)
+			handle_unanswered_req(session, stream);
+		/* Remove pending commands for this stream from the queue */
+		cleanup_queue(session, stream);
+		break;
+	default:
+		break;
+	}
+
+	l = stream->callbacks;
+	while (l != NULL) {
+		struct stream_callback *cb = l->data;
+		l = g_slist_next(l);
+		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);
+		stream_free(stream);
+	}
+
+	if (session->io && session->shutdown && session->streams == NULL) {
+		int sock = g_io_channel_unix_get_fd(session->io);
+		shutdown(sock, SHUT_RDWR);
+	}
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+	struct discover_callback *discover = session->discover;
+	struct avdtp_error avdtp_err;
+
+	if (!discover)
+		return;
+
+	session->discover = NULL;
+
+	avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
+
+	if (discover->id > 0)
+		g_source_remove(discover->id);
+
+	if (discover->cb)
+		discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+							discover->user_data);
+	g_free(discover);
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->abort &&
+				(sep->state != AVDTP_STATE_ABORTING ||
+							stream->abort_int))
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void sep_free(gpointer data)
+{
+	struct avdtp_remote_sep *sep = data;
+
+	g_slist_free_full(sep->caps, g_free);
+	g_free(sep);
+}
+
+static void avdtp_free(void *data)
+{
+	struct avdtp *session = data;
+
+	DBG("%p", session);
+
+	g_slist_free_full(session->streams, stream_free);
+
+	if (session->io) {
+		g_io_channel_shutdown(session->io, FALSE, NULL);
+		g_io_channel_unref(session->io);
+	}
+
+	if (session->io_id) {
+		g_source_remove(session->io_id);
+		session->io_id = 0;
+	}
+
+	if (session->req)
+		pending_req_free(session->req);
+
+	g_slist_free_full(session->req_queue, pending_req_free);
+	g_slist_free_full(session->prio_queue, pending_req_free);
+	g_slist_free_full(session->seps, sep_free);
+	g_slist_free_full(session->disconnect, g_free);
+
+	/* Free copy of the SEP list */
+	session->lseps = NULL;
+
+	g_free(session->buf);
+
+	g_free(session);
+}
+
+static void process_disconnect(void *data)
+{
+	struct disconnect_callback *callback = data;
+
+	callback->cb(callback->user_data);
+
+	g_free(callback);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+	DBG("Disconnected: %s (%d)", strerror(err), err);
+
+	g_slist_foreach(session->streams, (GFunc) release_stream, session);
+	session->streams = NULL;
+
+	avdtp_ref(session);
+
+	finalize_discovery(session, err);
+
+	g_slist_free_full(session->disconnect, process_disconnect);
+	session->disconnect = NULL;
+
+	avdtp_unref(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+	if (!session)
+		return;
+
+	session->ref--;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	if (session->ref > 0)
+		return;
+
+	finalize_discovery(session, ECONNABORTED);
+
+	avdtp_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+	session->ref++;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	return session;
+}
+
+static bool match_by_seid(const void *data, const void *user_data)
+{
+	const struct avdtp_local_sep *sep = data;
+	uint8_t seid = PTR_TO_UINT(user_data);
+
+	return sep->info.seid == seid;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
+								uint8_t seid)
+{
+	return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep)
+{
+	GSList *l;
+
+	if (lsep->info.inuse)
+		return NULL;
+
+	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+		struct avdtp_service_capability *cap;
+		struct avdtp_media_codec_capability *codec_data;
+
+		/* Type must be different: source <-> sink */
+		if (sep->type == lsep->info.type)
+			continue;
+
+		if (sep->media_type != lsep->info.media_type)
+			continue;
+
+		if (!sep->codec)
+			continue;
+
+		cap = sep->codec;
+		codec_data = (void *) cap->data;
+
+		if (codec_data->media_codec_type != lsep->codec)
+			continue;
+
+		/* FIXME: Add Vendor Specific Codec match to SEP callback */
+		if (lsep->codec == A2DP_CODEC_VENDOR) {
+			a2dp_vendor_codec_t *vndcodec =
+						(void *) codec_data->data;
+
+			if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+				continue;
+
+			if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+				continue;
+		}
+
+		if (sep->stream == NULL)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+				struct avdtp_service_capability **codec,
+				gboolean *delay_reporting)
+{
+	GSList *caps;
+	int processed;
+
+	if (delay_reporting)
+		*delay_reporting = FALSE;
+
+	for (processed = 0, caps = NULL; processed + 2 <= size;) {
+		struct avdtp_service_capability *cap;
+		uint8_t length, category;
+
+		category = data[0];
+		length = data[1];
+
+		if (processed + 2 + length > size) {
+			error("Invalid capability data in getcap resp");
+			break;
+		}
+
+		cap = g_malloc(sizeof(struct avdtp_service_capability) +
+					length);
+		memcpy(cap, data, 2 + length);
+
+		processed += 2 + length;
+		data += 2 + length;
+
+		caps = g_slist_append(caps, cap);
+
+		if (category == AVDTP_MEDIA_CODEC &&
+				length >=
+				sizeof(struct avdtp_media_codec_capability))
+			*codec = cap;
+		else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+			*delay_reporting = TRUE;
+	}
+
+	return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+							uint8_t signal_id)
+{
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+							signal_id, NULL, 0);
+}
+
+static void copy_seps(void *data, void *user_data)
+{
+	struct avdtp_local_sep *sep = data;
+	struct seid_info **p = user_data;
+
+	memcpy(*p, &sep->info, sizeof(struct seid_info));
+	*p = *p + 1;
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+							void *buf, int size)
+{
+	unsigned int rsp_size, sep_count;
+	struct seid_info *seps, *p;
+	gboolean ret;
+
+	sep_count = queue_length(session->lseps);
+
+	if (sep_count == 0) {
+		uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+		return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DISCOVER, &err, sizeof(err));
+	}
+
+	rsp_size = sep_count * sizeof(struct seid_info);
+
+	seps = g_new0(struct seid_info, sep_count);
+	p = seps;
+
+	queue_foreach(session->lseps, copy_seps, &p);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_DISCOVER, seps, rsp_size);
+	g_free(seps);
+
+	return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, unsigned int size,
+					gboolean get_all)
+{
+	GSList *l, *caps;
+	struct avdtp_local_sep *sep = NULL;
+	unsigned int rsp_size;
+	uint8_t err, buf[1024], *ptr = buf;
+	uint8_t cmd;
+
+	cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+	if (size < sizeof(struct seid_req)) {
+		err = AVDTP_BAD_LENGTH;
+		goto failed;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (!sep->ind->get_capability(session, sep, &caps, &err,
+							sep->user_data))
+		goto failed;
+
+	for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+
+		g_free(cap);
+	}
+
+	if (get_all && sep->delay_reporting) {
+		ptr[0] = AVDTP_DELAY_REPORTING;
+		ptr[1] = 0x00;
+		rsp_size += 2;
+	}
+
+	g_slist_free(caps);
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+								buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+							&err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+						struct avdtp_error *err)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+
+	if (err != NULL) {
+		rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+		rej.category = err->err.error_code;
+		avdtp_send(session, session->in.transaction,
+				AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+				&rej, sizeof(rej));
+		return;
+	}
+
+	if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+		stream_free(stream);
+		return;
+	}
+
+	sep = stream->lsep;
+	sep->stream = stream;
+	sep->info.inuse = 1;
+	session->streams = g_slist_append(session->streams, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+				struct setconf_req *req, unsigned int size)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err, category = 0x00;
+	GSList *l;
+
+	if (size < sizeof(struct setconf_req)) {
+		error("Too short getcap request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->stream) {
+		err = AVDTP_SEP_IN_USE;
+		goto failed;
+	}
+
+	stream = g_new0(struct avdtp_stream, 1);
+	stream->session = session;
+	stream->lsep = sep;
+	stream->rseid = req->int_seid;
+	stream->caps = caps_to_list(req->caps,
+					size - sizeof(struct setconf_req),
+					&stream->codec,
+					&stream->delay_reporting);
+
+	/*
+	 * Verify that the Media Transport capability's length = 0.
+	 * Reject otherwise
+	 */
+	for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_TRANSPORT &&
+							cap->length != 0) {
+			err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+			goto failed_stream;
+		}
+	}
+
+	if (stream->delay_reporting && session->version < 0x0103)
+		session->version = 0x0103;
+
+	if (sep->ind && sep->ind->set_configuration) {
+		if (!sep->ind->set_configuration(session, sep, stream,
+							stream->caps,
+							setconf_cb,
+							sep->user_data)) {
+			err = AVDTP_UNSUPPORTED_CONFIGURATION;
+			category = 0x00;
+			goto failed_stream;
+		}
+	} else {
+		if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+			stream_free(stream);
+			return FALSE;
+		}
+
+		sep->stream = stream;
+		sep->info.inuse = 1;
+		session->streams = g_slist_append(session->streams, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+	}
+
+	return TRUE;
+
+failed_stream:
+	stream_free(stream);
+failed:
+	rej.error = err;
+	rej.category = category;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	GSList *l;
+	struct avdtp_local_sep *sep = NULL;
+	int rsp_size;
+	uint8_t err;
+	uint8_t buf[1024];
+	uint8_t *ptr = buf;
+
+	if (size < (int) sizeof(struct seid_req)) {
+		error("Too short getconf request");
+		return FALSE;
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+	if (!sep->stream || !sep->stream->caps) {
+		err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		goto failed;
+	}
+
+	for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	struct conf_rej rej;
+
+	rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+	rej.category = 0x00;
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+	struct seid_req *seid = req->data;
+
+	if (seid->acp_seid == id)
+		req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+	struct start_req *start = req->data;
+	struct seid *seid = &start->first_seid;
+	int count = 1 + req->data_size - sizeof(struct start_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+	struct suspend_req *suspend = req->data;
+	struct seid *seid = &suspend->first_seid;
+	int count = 1 + req->data_size - sizeof(struct suspend_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+					struct avdtp_stream *stream)
+{
+	struct pending_req *req = session->req;
+
+	if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+		return;
+
+	if (cmd == AVDTP_ABORT)
+		cmd = req->signal_id;
+
+	switch (cmd) {
+	case AVDTP_OPEN:
+	case AVDTP_CLOSE:
+		check_seid_collision(req, stream->rseid);
+		break;
+	case AVDTP_START:
+		check_start_collision(req, stream->rseid);
+		break;
+	case AVDTP_SUSPEND:
+		check_suspend_collision(req, stream->rseid);
+		break;
+	}
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_CONFIGURED) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->open) {
+		if (!sep->ind->open(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_OPEN, NULL, 0))
+		return FALSE;
+
+	stream->open_acp = TRUE;
+	session->pending_open = stream;
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+						stream_open_timeout,
+						stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+				struct start_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct start_req)) {
+		error("Too short start request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct start_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(session, seid->seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		/* Also reject start cmd if state is not open */
+		if (sep->state != AVDTP_STATE_OPEN) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+		stream->starting = TRUE;
+
+		if (sep->ind && sep->ind->start) {
+			if (!sep->ind->start(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_START, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_START, NULL, 0);
+
+failed:
+	DBG("Rejecting (%d)", err);
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short close request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_OPEN &&
+			sep->state != AVDTP_STATE_STREAMING) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->close) {
+		if (!sep->ind->close(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_CLOSE, NULL, 0))
+		return FALSE;
+
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+					stream_close_timeout,
+					stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+				struct suspend_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct suspend_req)) {
+		error("Too short suspend request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct suspend_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(session, seid->seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		if (sep->state != AVDTP_STATE_STREAMING) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+
+		if (sep->ind && sep->ind->suspend) {
+			if (!sep->ind->suspend(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_SUSPEND, NULL, 0);
+
+failed:
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t err;
+	gboolean ret;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream)
+		return TRUE;
+
+	if (sep->ind && sep->ind->abort)
+		sep->ind->abort(session, sep, sep->stream, &err,
+							sep->user_data);
+
+	avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_ABORT, NULL, 0);
+	if (ret)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+					uint8_t transaction,
+					struct delay_req *req,
+					unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct delay_req)) {
+		error("Too short delay report request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	switch (sep->state) {
+	case AVDTP_STATE_IDLE:
+	case AVDTP_STATE_ABORTING:
+	case AVDTP_STATE_CLOSING:
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_OPEN:
+	case AVDTP_STATE_STREAMING:
+	default:
+		break;
+	}
+
+	stream->delay = ntohs(req->delay);
+
+	if (sep->ind && sep->ind->delayreport) {
+		if (!sep->ind->delayreport(session, sep, stream->rseid,
+						stream->delay, &err,
+						sep->user_data))
+			goto failed;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+				uint8_t signal_id, void *buf, int size)
+{
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("Received DISCOVER_CMD");
+		return avdtp_discover_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CAPABILITIES:
+		DBG("Received  GET_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size,
+									FALSE);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		DBG("Received  GET_ALL_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+	case AVDTP_SET_CONFIGURATION:
+		DBG("Received SET_CONFIGURATION_CMD");
+		return avdtp_setconf_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CONFIGURATION:
+		DBG("Received GET_CONFIGURATION_CMD");
+		return avdtp_getconf_cmd(session, transaction, buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("Received RECONFIGURE_CMD");
+		return avdtp_reconf_cmd(session, transaction, buf, size);
+	case AVDTP_OPEN:
+		DBG("Received OPEN_CMD");
+		return avdtp_open_cmd(session, transaction, buf, size);
+	case AVDTP_START:
+		DBG("Received START_CMD");
+		return avdtp_start_cmd(session, transaction, buf, size);
+	case AVDTP_CLOSE:
+		DBG("Received CLOSE_CMD");
+		return avdtp_close_cmd(session, transaction, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("Received SUSPEND_CMD");
+		return avdtp_suspend_cmd(session, transaction, buf, size);
+	case AVDTP_ABORT:
+		DBG("Received ABORT_CMD");
+		return avdtp_abort_cmd(session, transaction, buf, size);
+	case AVDTP_SECURITY_CONTROL:
+		DBG("Received SECURITY_CONTROL_CMD");
+		return avdtp_secctl_cmd(session, transaction, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("Received DELAY_REPORT_CMD");
+		return avdtp_delayreport_cmd(session, transaction, buf, size);
+	default:
+		DBG("Received unknown request id %u", signal_id);
+		return avdtp_unknown_cmd(session, transaction, signal_id);
+	}
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+							void *buf, size_t size)
+{
+	struct avdtp_common_header *header = buf;
+	struct avdtp_single_header *single = (void *) session->buf;
+	struct avdtp_start_header *start = (void *) session->buf;
+	void *payload;
+	gsize payload_size;
+
+	switch (header->packet_type) {
+	case AVDTP_PKT_TYPE_SINGLE:
+		if (size < sizeof(*single)) {
+			error("Received too small single packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("SINGLE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(*single);
+		payload_size = size - sizeof(*single);
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.no_of_packets = 1;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.signal_id = single->signal_id;
+
+		break;
+	case AVDTP_PKT_TYPE_START:
+		if (size < sizeof(*start)) {
+			error("Received too small start packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("START: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.no_of_packets = start->no_of_packets;
+		session->in.signal_id = start->signal_id;
+
+		payload = session->buf + sizeof(*start);
+		payload_size = size - sizeof(*start);
+
+		break;
+	case AVDTP_PKT_TYPE_CONTINUE:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small continue packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("CONTINUE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("Continue transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets <= 1) {
+			error("Too few continue packets");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	case AVDTP_PKT_TYPE_END:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small end packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("END: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("End transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets > 1) {
+			error("Got an end packet too early");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	default:
+		error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+		return PARSE_ERROR;
+	}
+
+	if (session->in.data_size + payload_size >
+					sizeof(session->in.buf)) {
+		error("Not enough incoming buffer space!");
+		return PARSE_ERROR;
+	}
+
+	memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+	session->in.data_size += payload_size;
+
+	if (session->in.no_of_packets > 1) {
+		session->in.no_of_packets--;
+		DBG("Received AVDTP fragment. %d to go",
+						session->in.no_of_packets);
+		return PARSE_FRAGMENT;
+	}
+
+	session->in.active = FALSE;
+
+	return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp *session = data;
+	struct avdtp_common_header *header;
+	ssize_t size;
+	int fd;
+
+	DBG("");
+
+	if (cond & G_IO_NVAL) {
+		session->io_id = 0;
+
+		return FALSE;
+	}
+
+	header = (void *) session->buf;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		goto failed;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	size = read(fd, session->buf, session->imtu);
+	if (size < 0) {
+		error("IO Channel read error");
+		goto failed;
+	}
+
+	if ((size_t) size < sizeof(struct avdtp_common_header)) {
+		error("Received too small packet (%zu bytes)", size);
+		goto failed;
+	}
+
+	switch (avdtp_parse_data(session, session->buf, size)) {
+	case PARSE_ERROR:
+		goto failed;
+	case PARSE_FRAGMENT:
+		return TRUE;
+	case PARSE_SUCCESS:
+		break;
+	}
+
+	/* Take a reference to protect against callback destroying session */
+	avdtp_ref(session);
+
+	if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+		if (!avdtp_parse_cmd(session, session->in.transaction,
+					session->in.signal_id,
+					session->in.buf,
+					session->in.data_size)) {
+			error("Unable to handle command. Disconnecting");
+			goto failed;
+		}
+
+		if (session->req && session->req->collided) {
+			DBG("Collision detected");
+			goto next;
+		}
+
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	if (session->req == NULL) {
+		error("No pending request, ignoring message");
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	if (header->transaction != session->req->transaction) {
+		error("Transaction label doesn't match");
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	if (session->in.signal_id != session->req->signal_id) {
+		error("Response signal doesn't match");
+		avdtp_unref(session);
+		return TRUE;
+	}
+
+	g_source_remove(session->req->timeout);
+	session->req->timeout = 0;
+
+	switch (header->message_type) {
+	case AVDTP_MSG_TYPE_ACCEPT:
+		if (!avdtp_parse_resp(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse accept response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_REJECT:
+		if (!avdtp_parse_rej(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse reject response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_GEN_REJECT:
+		error("Received a General Reject message");
+		break;
+	default:
+		error("Unknown message type 0x%02X", header->message_type);
+		break;
+	}
+
+next:
+	pending_req_free(session->req);
+	session->req = NULL;
+
+	if (session->ref > 1)
+		process_queue(session);
+
+	avdtp_unref(session);
+
+	return TRUE;
+
+failed:
+	session->io_id = 0;
+	connection_lost(session, EIO);
+
+	return FALSE;
+}
+
+static int set_priority(int fd, int priority)
+{
+	int err;
+
+	err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority,
+							sizeof(priority));
+	if (err == 0 || errno == ENOTSOCK)
+		return 0;
+
+	err = -errno;
+	error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err);
+
+	return err;
+}
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+							struct queue *lseps)
+{
+	struct avdtp *session;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	int new_fd;
+
+	if (!lseps)
+		return NULL;
+
+	new_fd = dup(fd);
+	if (new_fd < 0) {
+		error("dup(): %s (%d)", strerror(errno), errno);
+		return NULL;
+	}
+
+	if (set_priority(new_fd, 6) < 0)
+		return NULL;
+
+	session = g_new0(struct avdtp, 1);
+	session->io = g_io_channel_unix_new(new_fd);
+	session->version = version;
+	session->imtu = imtu;
+	session->omtu = omtu;
+	session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+
+	/* This watch should be low priority since otherwise the
+	 * connect callback might be dispatched before the session
+	 * callback if the kernel wakes us up at the same time for
+	 * them. This could happen if a headset is very quick in
+	 * sending the Start command after connecting the stream
+	 * transport channel.
+	 */
+	session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond,
+						(GIOFunc) session_cb, session,
+						NULL);
+
+	session->lseps = lseps;
+
+	return avdtp_ref(session);
+}
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+						avdtp_disconnect_cb_t cb,
+						void *user_data)
+{
+	struct disconnect_callback *callback;
+	static unsigned int id = 0;
+
+	callback = g_new0(struct disconnect_callback, 1);
+	callback->id = ++id;
+	callback->cb = cb;
+	callback->user_data = user_data;
+	session->disconnect = g_slist_append(session->disconnect, callback);
+
+	return id;
+}
+
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id)
+{
+	GSList *l;
+
+	for (l = session->disconnect; l; l = g_slist_next(l)) {
+		struct disconnect_callback *callback = l->data;
+
+		if (callback->id != id)
+			continue;
+
+		session->disconnect = g_slist_remove(session->disconnect,
+								callback);
+		g_free(callback);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void avdtp_shutdown(struct avdtp *session)
+{
+	GSList *l;
+	bool aborting = false;
+
+	if (!session->io)
+		return;
+
+	for (l = session->streams; l; l = g_slist_next(l)) {
+		struct avdtp_stream *stream = l->data;
+
+		if (stream->abort_int ||
+					avdtp_close(session, stream, TRUE) == 0)
+			aborting = true;
+	}
+
+	if (aborting) {
+		/* defer shutdown until all streams are aborted properly */
+		session->shutdown = true;
+	} else {
+		int sock = g_io_channel_unix_get_fd(session->io);
+
+		shutdown(sock, SHUT_RDWR);
+	}
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+			gboolean priority)
+{
+	if (priority)
+		session->prio_queue = g_slist_append(session->prio_queue, req);
+	else
+		session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+	if (req->signal_id == AVDTP_DISCOVER)
+		return 0;
+
+	return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+	struct pending_req *req;
+	struct seid_req sreq;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_stream *stream;
+	uint8_t seid;
+	struct avdtp_error averr;
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+	seid = req_get_seid(req);
+	if (seid)
+		stream = find_stream_by_rseid(session, seid);
+	else
+		stream = NULL;
+
+	if (stream) {
+		stream->abort_int = TRUE;
+		lsep = stream->lsep;
+	} else
+		lsep = NULL;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("Reconfigure: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("Open: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &averr,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("Start: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->start) {
+			lsep->cfm->start(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->starting = FALSE;
+		}
+		break;
+	case AVDTP_SUSPEND:
+		error("Suspend: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("Close: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->close) {
+			lsep->cfm->close(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->close_int = FALSE;
+		}
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("SetConfiguration: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+						&averr, lsep->user_data);
+		goto failed;
+	case AVDTP_DISCOVER:
+		error("Discover: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_GET_CAPABILITIES:
+		error("GetCapabilities: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_ABORT:
+		error("Abort: %s (%d)", strerror(err), err);
+		goto failed;
+	}
+
+	if (!stream)
+		goto failed;
+
+	memset(&sreq, 0, sizeof(sreq));
+	sreq.acp_seid = seid;
+
+	err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+				sizeof(sreq));
+	if (err < 0) {
+		error("Unable to send abort request");
+		goto failed;
+	}
+
+	goto done;
+
+failed:
+	connection_lost(session, err);
+done:
+	pending_req_free(req);
+	return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+	struct avdtp *session = user_data;
+
+	cancel_request(session, ETIMEDOUT);
+
+	return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+			struct pending_req *req)
+{
+	static int transaction = 0;
+	int err;
+
+	if (session->req != NULL) {
+		queue_request(session, req, priority);
+		return 0;
+	}
+
+	req->transaction = transaction++;
+	transaction %= 16;
+
+	/* FIXME: Should we retry to send if the buffer
+	was not totally sent or in case of EINTR? */
+	if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+				req->signal_id, req->data, req->data_size)) {
+		err = -EIO;
+		goto failed;
+	}
+
+	session->req = req;
+
+	req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+					ABORT_TIMEOUT : REQ_TIMEOUT,
+					request_timeout,
+					session);
+	return 0;
+
+failed:
+	g_free(req->data);
+	g_free(req);
+	return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size)
+{
+	struct pending_req *req;
+
+	if (size > 0 && !buffer) {
+		DBG("Invalid buffer %p", buffer);
+		return -EINVAL;
+	}
+
+	if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+		DBG("Unable to send requests while aborting");
+		return -EINVAL;
+	}
+
+	req = g_new0(struct pending_req, 1);
+	req->signal_id = signal_id;
+	req->data_size = size;
+	req->stream = stream;
+
+	if (size > 0) {
+		req->data = g_malloc(size);
+		memcpy(req->data, buffer, size);
+	}
+
+	return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+					struct discover_resp *resp, int size)
+{
+	int sep_count, i;
+	uint8_t getcap_cmd;
+	int ret = 0;
+	gboolean getcap_pending = FALSE;
+
+	if (session->version >= 0x0103)
+		getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+	else
+		getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+	sep_count = size / sizeof(struct seid_info);
+
+	for (i = 0; i < sep_count; i++) {
+		struct avdtp_remote_sep *sep;
+		struct avdtp_stream *stream;
+		struct seid_req req;
+
+		DBG("seid %d type %d media %d in use %d",
+				resp->seps[i].seid, resp->seps[i].type,
+				resp->seps[i].media_type, resp->seps[i].inuse);
+
+		stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+		sep = find_remote_sep(session->seps, resp->seps[i].seid);
+		if (!sep) {
+			if (resp->seps[i].inuse && !stream)
+				continue;
+			sep = g_new0(struct avdtp_remote_sep, 1);
+			session->seps = g_slist_append(session->seps, sep);
+		}
+
+		sep->stream = stream;
+		sep->seid = resp->seps[i].seid;
+		sep->type = resp->seps[i].type;
+		sep->media_type = resp->seps[i].media_type;
+
+		memset(&req, 0, sizeof(req));
+		req.acp_seid = sep->seid;
+
+		ret = send_request(session, TRUE, NULL, getcap_cmd,
+							&req, sizeof(req));
+		if (ret < 0)
+			break;
+		getcap_pending = TRUE;
+	}
+
+	if (!getcap_pending)
+		finalize_discovery(session, -ret);
+
+	return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+						struct getcap_resp *resp,
+						unsigned int size)
+{
+	struct avdtp_remote_sep *sep;
+	uint8_t seid;
+
+	/* Check for minimum required packet size includes:
+	 *   1. getcap resp header
+	 *   2. media transport capability (2 bytes)
+	 *   3. media codec capability type + length (2 bytes)
+	 *   4. the actual media codec elements
+	 * */
+	if (size < (sizeof(struct getcap_resp) + 4 +
+				sizeof(struct avdtp_media_codec_capability))) {
+		error("Too short getcap resp packet");
+		return FALSE;
+	}
+
+	seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+	sep = find_remote_sep(session->seps, seid);
+
+	DBG("seid %d type %d media %d", sep->seid,
+					sep->type, sep->media_type);
+
+	if (sep->caps) {
+		g_slist_free_full(sep->caps, g_free);
+		sep->caps = NULL;
+		sep->codec = NULL;
+		sep->delay_reporting = FALSE;
+	}
+
+	sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+					&sep->codec, &sep->delay_reporting);
+
+	return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct avdtp_single_header *resp,
+					int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+	if (sep->cfm && sep->cfm->set_configuration)
+		sep->cfm->set_configuration(session, sep, stream, NULL,
+						sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct avdtp_single_header *resp,
+					int size)
+{
+	return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session,
+				struct avdtp_stream *stream,
+				struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	session->pending_open = stream;
+
+	if (!stream->open_acp && sep->cfm && sep->cfm->open)
+		sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	/* We might be in STREAMING already if both sides send START_CMD at the
+	 * same time and the one in SNK role doesn't reject it as it should */
+	if (sep->state != AVDTP_STATE_STREAMING)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+	if (sep->cfm && sep->cfm->start)
+		sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	close_stream(stream);
+
+	return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	if (sep->cfm && sep->cfm->suspend)
+		sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	if (sep->cfm && sep->cfm->abort)
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+	return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->delay_report)
+		sep->cfm->delay_report(session, sep, stream, NULL,
+							sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct pending_req *next;
+	const char *get_all = "";
+
+	if (session->prio_queue)
+		next = session->prio_queue->data;
+	else if (session->req_queue)
+		next = session->req_queue->data;
+	else
+		next = NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("DISCOVER request succeeded");
+		return avdtp_discover_resp(session, buf, size);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		get_all = "ALL_";
+	case AVDTP_GET_CAPABILITIES:
+		DBG("GET_%sCAPABILITIES request succeeded", get_all);
+		if (!avdtp_get_capabilities_resp(session, buf, size))
+			return FALSE;
+		if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+				next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+			finalize_discovery(session, 0);
+		return TRUE;
+	}
+
+	/* The remaining commands require an existing stream so bail out
+	 * here if the stream got unexpectedly disconnected */
+	if (!stream) {
+		DBG("AVDTP: stream was closed while waiting for reply");
+		return TRUE;
+	}
+
+	switch (signal_id) {
+	case AVDTP_SET_CONFIGURATION:
+		DBG("SET_CONFIGURATION request succeeded");
+		return avdtp_set_configuration_resp(session, stream,
+								buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("RECONFIGURE request succeeded");
+		return avdtp_reconfigure_resp(session, stream, buf, size);
+	case AVDTP_OPEN:
+		DBG("OPEN request succeeded");
+		return avdtp_open_resp(session, stream, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("SUSPEND request succeeded");
+		return avdtp_suspend_resp(session, stream, buf, size);
+	case AVDTP_START:
+		DBG("START request succeeded");
+		return avdtp_start_resp(session, stream, buf, size);
+	case AVDTP_CLOSE:
+		DBG("CLOSE request succeeded");
+		return avdtp_close_resp(session, stream, buf, size);
+	case AVDTP_ABORT:
+		DBG("ABORT request succeeded");
+		return avdtp_abort_resp(session, stream, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("DELAY_REPORT request succeeded");
+		return avdtp_delay_report_resp(session, stream, buf, size);
+	}
+
+	error("Unknown signal id in accept response: %u", signal_id);
+	return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+					struct avdtp_error *err)
+{
+	if (size < sizeof(struct seid_rej)) {
+		error("Too small packet for seid_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+				struct avdtp_error *err)
+{
+	if (size < sizeof(struct conf_rej)) {
+		error("Too small packet for conf_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, rej->category, rej->error);
+
+	return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+					struct avdtp_error *err,
+					uint8_t *acp_seid)
+{
+	if (size < sizeof(struct stream_rej)) {
+		error("Too small packet for stream_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	if (acp_seid)
+		*acp_seid = rej->acp_seid;
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct avdtp_error err;
+	uint8_t acp_seid;
+	struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+	case AVDTP_GET_CAPABILITIES:
+	case AVDTP_GET_ALL_CAPABILITIES:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("%s request rejected: %s (%d)",
+			signal_id == AVDTP_DISCOVER ? "DISCOVER" :
+			signal_id == AVDTP_GET_CAPABILITIES ?
+			"GET_CAPABILITIES" : "GET_ALL_CAPABILITIES",
+			avdtp_strerror(&err), err.err.error_code);
+		if (session->discover) {
+			session->discover->cb(session, session->seps, &err,
+						session->discover->user_data);
+			g_free(session->discover);
+			session->discover = NULL;
+		}
+		return TRUE;
+	case AVDTP_OPEN:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("OPEN request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->open)
+			sep->cfm->open(session, sep, stream, &err,
+					sep->user_data);
+		return TRUE;
+	case AVDTP_SET_CONFIGURATION:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("SET_CONFIGURATION request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->set_configuration)
+			sep->cfm->set_configuration(session, sep, stream,
+							&err, sep->user_data);
+		return TRUE;
+	case AVDTP_GET_CONFIGURATION:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("GET_CONFIGURATION request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->get_configuration)
+			sep->cfm->get_configuration(session, sep, stream, &err,
+								sep->user_data);
+		return TRUE;
+	case AVDTP_RECONFIGURE:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("RECONFIGURE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->reconfigure)
+			sep->cfm->reconfigure(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_START:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("START request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->start) {
+			stream->starting = FALSE;
+			sep->cfm->start(session, sep, stream, &err,
+					sep->user_data);
+		}
+		return TRUE;
+	case AVDTP_SUSPEND:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("SUSPEND request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->suspend)
+			sep->cfm->suspend(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_CLOSE:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("CLOSE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->close) {
+			sep->cfm->close(session, sep, stream, &err,
+					sep->user_data);
+			stream->close_int = FALSE;
+		}
+		return TRUE;
+	case AVDTP_ABORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("ABORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->abort)
+			sep->cfm->abort(session, sep, stream, &err,
+					sep->user_data);
+		return FALSE;
+	case AVDTP_DELAY_REPORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("DELAY_REPORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->delay_report)
+			sep->cfm->delay_report(session, sep, stream, &err,
+							sep->user_data);
+		return TRUE;
+	default:
+		error("Unknown reject response signal id: %u", signal_id);
+		return TRUE;
+	}
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->caps; l; l = l->next) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_CODEC)
+			return cap;
+	}
+
+	return NULL;
+}
+
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+					struct avdtp_service_capability *cap)
+{
+	GSList *l;
+	struct avdtp_service_capability *stream_cap;
+
+	for (l = stream->caps; l; l = g_slist_next(l)) {
+		stream_cap = l->data;
+
+		if (stream_cap->category != cap->category ||
+			stream_cap->length != cap->length)
+			continue;
+
+		if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps)
+{
+	for (; caps; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+
+		if (!avdtp_stream_has_capability(stream, cap))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->session->seps; l; l = l->next) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == stream->rseid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu)
+{
+	GIOChannel *io;
+
+	if (stream != stream->session->pending_open)
+		return FALSE;
+
+	if (set_priority(fd, 5) < 0)
+		return FALSE;
+
+	io = g_io_channel_unix_new(fd);
+
+	handle_transport_connect(stream->session, io, imtu, omtu);
+
+	g_io_channel_unref(io);
+
+	return TRUE;
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps)
+{
+	if (stream->io == NULL)
+		return FALSE;
+
+	if (sock)
+		*sock = g_io_channel_unix_get_fd(stream->io);
+
+	if (omtu)
+		*omtu = stream->omtu;
+
+	if (imtu)
+		*imtu = stream->imtu;
+
+	if (caps)
+		*caps = stream->caps;
+
+	return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+	GSList **queue, *l;
+	struct pending_req *req;
+
+	if (session->req)
+		return 0;
+
+	if (session->prio_queue)
+		queue = &session->prio_queue;
+	else
+		queue = &session->req_queue;
+
+	if (!*queue)
+		return 0;
+
+	l = *queue;
+	req = l->data;
+
+	*queue = g_slist_remove(*queue, req);
+
+	return send_req(session, FALSE, req);
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+	return sep->codec;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							const void *data,
+							int length)
+{
+	struct avdtp_service_capability *cap;
+
+	if (category < AVDTP_MEDIA_TRANSPORT ||
+					category > AVDTP_DELAY_REPORTING)
+		return NULL;
+
+	if (length > 0 && !data)
+		return NULL;
+
+	cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+	cap->category = category;
+	cap->length = length;
+
+	if (length > 0)
+		memcpy(cap->data, data, length);
+
+	return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+	struct avdtp *session = data;
+
+	session->discover->id = 0;
+
+	finalize_discovery(session, 0);
+
+	return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data)
+{
+	int err;
+
+	if (session->discover)
+		return -EBUSY;
+
+	session->discover = g_new0(struct discover_callback, 1);
+
+	if (session->seps) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+		session->discover->id = g_idle_add(process_discover, session);
+		return 0;
+	}
+
+	err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+	if (err == 0) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+	}
+
+	return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id)
+{
+	GSList *l;
+	struct stream_callback *cb;
+
+	if (!stream)
+		return FALSE;
+
+	for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+		struct stream_callback *tmp = l->data;
+		if (tmp && tmp->id == id) {
+			cb = tmp;
+			break;
+		}
+	}
+
+	if (!cb)
+		return FALSE;
+
+	stream->callbacks = g_slist_remove(stream->callbacks, cb);
+	g_free(cb);
+
+	return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data)
+{
+	struct stream_callback *stream_cb;
+	static unsigned int id = 0;
+
+	stream_cb = g_new(struct stream_callback, 1);
+	stream_cb->cb = cb;
+	stream_cb->user_data = data;
+	stream_cb->id = ++id;
+
+	stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+	return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+							&req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+	struct avdtp_service_capability *src_cap = data;
+	struct avdtp_service_capability *dst_cap;
+	GSList **l = user_data;
+
+	dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+					src_cap->length);
+
+	*l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream)
+{
+	struct setconf_req *req;
+	struct avdtp_stream *new_stream;
+	unsigned char *ptr;
+	int err, caps_len;
+	struct avdtp_service_capability *cap;
+	GSList *l;
+
+	if (!(lsep && rsep))
+		return -EINVAL;
+
+	DBG("%p: int_seid=%u, acp_seid=%u", session,
+			lsep->info.seid, rsep->seid);
+
+	new_stream = g_new0(struct avdtp_stream, 1);
+	new_stream->session = session;
+	new_stream->lsep = lsep;
+	new_stream->rseid = rsep->seid;
+
+	if (rsep->delay_reporting && lsep->delay_reporting) {
+		struct avdtp_service_capability *delay_reporting;
+
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		caps = g_slist_append(caps, delay_reporting);
+		new_stream->delay_reporting = TRUE;
+	}
+
+	g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+	/* Calculate total size of request */
+	for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		caps_len += cap->length + 2;
+	}
+
+	req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+	req->int_seid = lsep->info.seid;
+	req->acp_seid = rsep->seid;
+
+	/* Copy the capabilities into the request */
+	for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		memcpy(ptr, cap, cap->length + 2);
+		ptr += cap->length + 2;
+	}
+
+	err = send_request(session, FALSE, new_stream,
+				AVDTP_SET_CONFIGURATION, req,
+				sizeof(struct setconf_req) + caps_len);
+	if (err < 0)
+		stream_free(new_stream);
+	else {
+		lsep->info.inuse = 1;
+		lsep->stream = new_stream;
+		rsep->stream = new_stream;
+		session->streams = g_slist_append(session->streams, new_stream);
+		if (stream)
+			*stream = new_stream;
+	}
+
+	g_free(req);
+
+	return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_OPEN,
+							&req, sizeof(req));
+}
+
+static gboolean start_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+	struct avdtp *session = stream->session;
+
+	stream->open_acp = FALSE;
+
+	if (avdtp_start(session, stream) < 0)
+		error("wait_timeout: avdtp_start failed");
+
+	stream->start_timer = 0;
+
+	return FALSE;
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct start_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_OPEN)
+		return -EINVAL;
+
+	/* Recommendation 12:
+	 *  If the RD has configured and opened a stream it is also responsible
+	 *  to start the streaming via GAVDP_START.
+	 */
+	if (stream->open_acp) {
+		/* If timer already active wait it */
+		if (stream->start_timer)
+			return 0;
+
+		stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
+								start_timeout,
+								stream);
+		return 0;
+	}
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_start: rejecting start since close is initiated");
+		return -EINVAL;
+	}
+
+	if (stream->starting == TRUE) {
+		DBG("stream already started");
+		return -EINPROGRESS;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.first_seid.seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_START,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->starting = TRUE;
+
+	return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_close: rejecting since close is already initiated");
+		return -EINVAL;
+	}
+
+	/* If stream is not yet in the OPEN state, let's use ABORT_CMD */
+	if (stream->lsep->state < AVDTP_STATE_OPEN)
+		return avdtp_abort(session, stream);
+
+	if (immediate && session->req && stream == session->req->stream)
+		return avdtp_abort(session, stream);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->close_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+							&req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state == AVDTP_STATE_ABORTING)
+		return -EINVAL;
+
+	if (session->req && session->req->timeout > 0 &&
+						stream == session->req->stream)
+		return cancel_request(session, ECANCELED);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->abort_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay)
+{
+	struct delay_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+				stream->lsep->state != AVDTP_STATE_STREAMING)
+		return -EINVAL;
+
+	if (!stream->delay_reporting || session->version < 0x0103)
+		return -EINVAL;
+
+	stream->delay = delay;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+	req.delay = htons(delay);
+
+	return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+							&req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+						uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t seid = util_get_uid(&seids, MAX_SEID);
+
+	if (!seid)
+		return NULL;
+
+	sep = g_new0(struct avdtp_local_sep, 1);
+
+	sep->state = AVDTP_STATE_IDLE;
+	sep->info.seid = seid;
+	sep->info.type = type;
+	sep->info.media_type = media_type;
+	sep->codec = codec_type;
+	sep->ind = ind;
+	sep->cfm = cfm;
+	sep->user_data = user_data;
+	sep->delay_reporting = delay_reporting;
+
+	DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	queue_push_tail(lseps, sep);
+
+	return sep;
+}
+
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+							uint16_t codec_id)
+{
+	sep->vndcodec_vendor = vendor_id;
+	sep->vndcodec_codec = codec_id;
+}
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
+{
+	if (!sep)
+		return -EINVAL;
+
+	if (sep->stream)
+		release_stream(sep->stream, sep->stream->session);
+
+	DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	util_clear_uid(&seids, sep->info.seid);
+	queue_remove(lseps, sep);
+	g_free(sep);
+
+	return 0;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+	if (err->category == AVDTP_ERRNO)
+		return strerror(err->err.posix_errno);
+
+	switch (err->err.error_code) {
+	case AVDTP_BAD_HEADER_FORMAT:
+		return "Bad Header Format";
+	case AVDTP_BAD_LENGTH:
+		return "Bad Packet Length";
+	case AVDTP_BAD_ACP_SEID:
+		return "Bad Acceptor SEID";
+	case AVDTP_SEP_IN_USE:
+		return "Stream End Point in Use";
+	case AVDTP_SEP_NOT_IN_USE:
+		return "Stream End Point Not in Use";
+	case AVDTP_BAD_SERV_CATEGORY:
+		return "Bad Service Category";
+	case AVDTP_BAD_PAYLOAD_FORMAT:
+		return "Bad Payload format";
+	case AVDTP_NOT_SUPPORTED_COMMAND:
+		return "Command Not Supported";
+	case AVDTP_INVALID_CAPABILITIES:
+		return "Invalid Capabilities";
+	case AVDTP_BAD_RECOVERY_TYPE:
+		return "Bad Recovery Type";
+	case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+		return "Bad Media Transport Format";
+	case AVDTP_BAD_RECOVERY_FORMAT:
+		return "Bad Recovery Format";
+	case AVDTP_BAD_ROHC_FORMAT:
+		return "Bad Header Compression Format";
+	case AVDTP_BAD_CP_FORMAT:
+		return "Bad Content Protection Format";
+	case AVDTP_BAD_MULTIPLEXING_FORMAT:
+		return "Bad Multiplexing Format";
+	case AVDTP_UNSUPPORTED_CONFIGURATION:
+		return "Configuration not supported";
+	case AVDTP_BAD_STATE:
+		return "Bad State";
+	default:
+		return "Unknown error";
+	}
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+	return sep->state;
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+	return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
diff --git a/bluez/repo/android/avdtp.h b/bluez/repo/android/avdtp.h
new file mode 100644
index 0000000..07516a8
--- /dev/null
+++ b/bluez/repo/android/avdtp.h
@@ -0,0 +1,291 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+	uint8_t category;
+	union {
+		uint8_t error_code;
+		int posix_errno;
+	} err;
+};
+
+#define AVDTP_PSM 25
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT			0x01
+#define AVDTP_REPORTING				0x02
+#define AVDTP_RECOVERY				0x03
+#define AVDTP_CONTENT_PROTECTION		0x04
+#define AVDTP_HEADER_COMPRESSION		0x05
+#define AVDTP_MULTIPLEXING			0x06
+#define AVDTP_MEDIA_CODEC			0x07
+#define AVDTP_DELAY_REPORTING			0x08
+#define AVDTP_ERRNO				0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT			0x01
+#define AVDTP_BAD_LENGTH			0x11
+#define AVDTP_BAD_ACP_SEID			0x12
+#define AVDTP_SEP_IN_USE			0x13
+#define AVDTP_SEP_NOT_IN_USE			0x14
+#define AVDTP_BAD_SERV_CATEGORY			0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT		0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND		0x19
+#define AVDTP_INVALID_CAPABILITIES		0x1A
+#define AVDTP_BAD_RECOVERY_TYPE			0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT	0x23
+#define AVDTP_BAD_RECOVERY_FORMAT		0x25
+#define AVDTP_BAD_ROHC_FORMAT			0x26
+#define AVDTP_BAD_CP_FORMAT			0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT		0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION		0x29
+#define AVDTP_BAD_STATE				0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE			0x00
+#define AVDTP_SEP_TYPE_SINK			0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO			0x00
+#define AVDTP_MEDIA_TYPE_VIDEO			0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA		0x02
+
+typedef enum {
+	AVDTP_STATE_IDLE,
+	AVDTP_STATE_CONFIGURED,
+	AVDTP_STATE_OPEN,
+	AVDTP_STATE_STREAMING,
+	AVDTP_STATE_CLOSING,
+	AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+	uint8_t category;
+	uint8_t length;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t rfa0:4;
+	uint8_t media_type:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t media_type:4;
+	uint8_t rfa0:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+						struct avdtp_stream *stream,
+						struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+	void (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*reconfigure) (struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+};
+
+/*
+ * Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted
+ */
+struct avdtp_sep_ind {
+	gboolean (*get_capability) (struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data);
+	gboolean (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					GSList *caps,
+					avdtp_set_configuration_cb cb,
+					void *user_data);
+	gboolean (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*suspend) (struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*reconfigure) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*delayreport) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t rseid, uint16_t delay,
+					uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+					struct avdtp_error *err, void *user_data);
+typedef void (*avdtp_disconnect_cb_t) (void *user_data);
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+							struct queue *lseps);
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+						avdtp_disconnect_cb_t cb,
+						void *user_data);
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id);
+
+void avdtp_shutdown(struct avdtp *session);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							const void *data,
+							int size);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id);
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu);
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream);
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+				struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+						uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data);
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+							uint16_t codec_id);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
diff --git a/bluez/repo/android/avdtptest.c b/bluez/repo/android/avdtptest.c
new file mode 100644
index 0000000..ce34443
--- /dev/null
+++ b/bluez/repo/android/avdtptest.c
@@ -0,0 +1,909 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "btio/btio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "avdtp.h"
+
+static GMainLoop *mainloop = NULL;
+static int dev_role = AVDTP_SEP_TYPE_SOURCE;
+static bool preconf = false;
+static struct avdtp *avdtp = NULL;
+struct avdtp_stream *avdtp_stream = NULL;
+struct avdtp_local_sep *local_sep = NULL;
+struct avdtp_remote_sep *remote_sep = NULL;
+static GIOChannel *io = NULL;
+static bool reject = false;
+static bdaddr_t src;
+static bdaddr_t dst;
+static uint16_t version = 0x0103;
+static guint media_player = 0;
+static guint media_recorder = 0;
+static guint idle_id = 0;
+static struct queue *lseps = NULL;
+
+static bool fragment = false;
+
+static enum {
+	CMD_GET_CONF,
+	CMD_OPEN,
+	CMD_START,
+	CMD_SUSPEND,
+	CMD_CLOSE,
+	CMD_ABORT,
+	CMD_DELAY,
+	CMD_NONE,
+} command = CMD_NONE;
+
+static const char sbc_codec[] = {0x00, 0x00, 0x11, 0x15, 0x02, 0x40};
+static const char sbc_media_frame[] = {
+	0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x01, 0x9c, 0xfd, 0x40, 0xbd, 0xde, 0xa9, 0x75, 0x43, 0x20, 0x87, 0x64,
+	0x44, 0x32, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe,
+	0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7,
+	0xbb, 0xbb, 0x80, 0x3e, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x83, 0x41,
+	0x07, 0x77, 0x09, 0x07, 0x43, 0xb3, 0x81, 0xbc, 0xf8, 0x77, 0x02, 0xe5,
+	0xa4, 0x3a, 0xa0, 0xcb, 0x38, 0xbb, 0x57, 0x90, 0xd9, 0x08, 0x9c, 0x1d,
+	0x86, 0x59, 0x01, 0x0c, 0x21, 0x44, 0x68, 0x35, 0xa8, 0x57, 0x97, 0x0e,
+	0x9b, 0xbb, 0x62, 0xc4, 0xca, 0x57, 0x04, 0xa1, 0xca, 0x3b, 0xa3, 0x48,
+	0xd2, 0x66, 0x11, 0x33, 0x6a, 0x3b, 0xb4, 0xbb, 0x08, 0x77, 0x17, 0x03,
+	0xb4, 0x3b, 0x79, 0x3b, 0x46, 0x97, 0x0e, 0xf7, 0x3d, 0xbb, 0x3d, 0x49,
+	0x25, 0x86, 0x88, 0xb4, 0xad, 0x3b, 0x62, 0xbb, 0xa4, 0x47, 0x29, 0x99,
+	0x3b, 0x3b, 0xaf, 0xc6, 0xd4, 0x37, 0x68, 0x94, 0x0a, 0xbb
+	};
+
+static void parse_command(const char *cmd)
+{
+	if (!strncmp(cmd, "getconf", sizeof("getconf"))) {
+		command = CMD_GET_CONF;
+	} else if (!strncmp(cmd, "open", sizeof("open"))) {
+		command = CMD_OPEN;
+	} else if (!strncmp(cmd, "start", sizeof("start"))) {
+		command = CMD_START;
+	} else if (!strncmp(cmd, "suspend", sizeof("suspend"))) {
+		command = CMD_SUSPEND;
+	} else if (!strncmp(cmd, "close", sizeof("close"))) {
+		command = CMD_CLOSE;
+	} else if (!strncmp(cmd, "abort", sizeof("abort"))) {
+		command = CMD_ABORT;
+	} else if (!strncmp(cmd, "delay", sizeof("delay"))) {
+		command = CMD_DELAY;
+	} else {
+		printf("Unknown command '%s'\n", cmd);
+		printf("(getconf open start suspend close abort delay)\n");
+		exit(1);
+	}
+}
+
+static void send_command(void)
+{
+	avdtp_state_t state = avdtp_sep_get_state(local_sep);
+
+	switch (command) {
+	case CMD_GET_CONF:
+		avdtp_get_configuration(avdtp, avdtp_stream);
+		break;
+	case CMD_OPEN:
+		if (state == AVDTP_STATE_CONFIGURED)
+			avdtp_open(avdtp, avdtp_stream);
+		break;
+	case CMD_START:
+		if (state == AVDTP_STATE_OPEN)
+			avdtp_start(avdtp, avdtp_stream);
+		break;
+	case CMD_SUSPEND:
+		if (state == AVDTP_STATE_STREAMING)
+			avdtp_suspend(avdtp , avdtp_stream);
+		break;
+	case CMD_CLOSE:
+		if (state == AVDTP_STATE_STREAMING)
+			avdtp_close(avdtp, avdtp_stream, FALSE);
+		break;
+	case CMD_ABORT:
+		avdtp_abort(avdtp , avdtp_stream);
+		break;
+	case CMD_DELAY:
+		avdtp_delay_report(avdtp , avdtp_stream , 250);
+		break;
+	case CMD_NONE:
+	default:
+		break;
+	}
+}
+
+static gboolean media_writer(gpointer user_data)
+{
+	uint16_t omtu;
+	int fd;
+	int to_write;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+		return TRUE;
+
+	if (omtu < sizeof(sbc_media_frame))
+		to_write = omtu;
+	else
+		to_write = sizeof(sbc_media_frame);
+
+	if (write(fd, sbc_media_frame, to_write) < 0)
+		return TRUE;
+
+	send_command();
+
+	return TRUE;
+}
+
+static bool start_media_player(void)
+{
+	int fd;
+	uint16_t omtu;
+
+	printf("Media streaming started\n");
+
+	if (media_player || !avdtp_stream)
+		return false;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+		return false;
+
+	media_player = g_timeout_add(200, media_writer, NULL);
+	if (!media_player)
+		return false;
+
+	return true;
+}
+
+static void stop_media_player(void)
+{
+	if (!media_player)
+		return;
+
+	printf("Media streaming stopped\n");
+
+	g_source_remove(media_player);
+	media_player = 0;
+}
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+	unsigned cc:4;
+	unsigned x:1;
+	unsigned p:1;
+	unsigned v:2;
+
+	unsigned pt:7;
+	unsigned m:1;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+	unsigned v:2;
+	unsigned p:1;
+	unsigned x:1;
+	unsigned cc:4;
+
+	unsigned m:1;
+	unsigned pt:7;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+static gboolean media_reader(GIOChannel *source, GIOCondition condition,
+								gpointer data)
+{
+	char buf[UINT16_MAX];
+	struct rtp_header *rtp = (void *) buf;
+	static bool decode = false;
+	uint16_t imtu;
+	int fd, ret;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, &imtu, NULL, NULL))
+		return TRUE;
+
+	ret = read(fd, buf, imtu);
+	if (ret < 0) {
+		printf("Reading failed (%s)\n", strerror(errno));
+		return TRUE;
+	}
+
+	if (ret < (int) sizeof(*rtp)) {
+		printf("Not enough media data received (%u bytes)", ret);
+		return TRUE;
+	}
+
+	if (!decode) {
+		printf("V=%u P=%u X=%u CC=%u M=%u PT=%u SeqNr=%d\n",
+			rtp->v, rtp->p, rtp->x, rtp->cc, rtp->m, rtp->pt,
+			be16_to_cpu(rtp->sequence_number));
+		decode = true;
+	}
+
+	send_command();
+
+	return TRUE;
+}
+
+static bool start_media_recorder(void)
+{
+	int fd;
+	uint16_t omtu;
+	GIOChannel *chan;
+
+	printf("Media recording started\n");
+
+	if (media_recorder || !avdtp_stream)
+		return false;
+
+	if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL))
+		return false;
+
+	chan = g_io_channel_unix_new(fd);
+
+	media_recorder = g_io_add_watch(chan, G_IO_IN, media_reader, NULL);
+	g_io_channel_unref(chan);
+
+	if (!media_recorder)
+		return false;
+
+	return true;
+}
+
+static void stop_media_recorder(void)
+{
+	if (!media_recorder)
+		return;
+
+	printf("Media recording stopped\n");
+
+	g_source_remove(media_recorder);
+	media_recorder = 0;
+}
+
+static void set_configuration_cfm(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (preconf)
+		avdtp_open(avdtp, avdtp_stream);
+}
+
+static void get_configuration_cfm(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data)
+	{
+	printf("%s\n", __func__);
+}
+
+static void disconnect_cb(void *user_data)
+{
+	printf("Disconnected\n");
+
+	g_main_loop_quit(mainloop);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+	struct avdtp_service_capability *service;
+	GSList *caps = NULL;
+	int ret;
+
+	remote_sep = avdtp_find_remote_sep(avdtp, local_sep);
+	if (!remote_sep) {
+		printf("Unable to find matching endpoint\n");
+		avdtp_shutdown(session);
+		return;
+	}
+
+	printf("Matching endpoint found\n");
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	caps = g_slist_append(caps, service);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec,
+							sizeof(sbc_codec));
+	caps = g_slist_append(caps, service);
+
+	ret = avdtp_set_configuration(avdtp, remote_sep, local_sep, caps,
+								&avdtp_stream);
+
+	g_slist_free_full(caps, g_free);
+
+	if (ret < 0) {
+		printf("Failed to set configuration (%s)\n", strerror(-ret));
+		avdtp_shutdown(session);
+	}
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		printf("%s\n", err->message);
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		printf("%s\n", gerr->message);
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	printf("Connected (imtu=%d omtu=%d)\n", imtu, omtu);
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	if (avdtp && avdtp_stream) {
+		if (!avdtp_stream_set_transport(avdtp_stream, fd, imtu, omtu)) {
+			printf("avdtp_stream_set_transport: failed\n");
+			g_main_loop_quit(mainloop);
+		}
+
+		g_io_channel_set_close_on_unref(chan, FALSE);
+
+		send_command();
+
+		return;
+	}
+
+	avdtp = avdtp_new(fd, imtu, omtu, version, lseps);
+	if (!avdtp) {
+		printf("Failed to create avdtp instance\n");
+		g_main_loop_quit(mainloop);
+		return;
+	}
+
+	avdtp_add_disconnect_cb(avdtp, disconnect_cb, NULL);
+
+	if (preconf) {
+		int ret;
+
+		ret = avdtp_discover(avdtp, discover_cb, NULL);
+		if (ret < 0) {
+			printf("avdtp_discover failed: %s", strerror(-ret));
+			g_main_loop_quit(mainloop);
+		}
+	}
+}
+
+static GIOChannel *do_connect(GError **err)
+{
+	if (fragment)
+		return bt_io_connect(connect_cb, NULL, NULL, err,
+					BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_DEST_BDADDR, &dst,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_MTU, 48,
+					BT_IO_OPT_INVALID);
+
+	return bt_io_connect(connect_cb, NULL, NULL, err,
+				BT_IO_OPT_SOURCE_BDADDR, &src,
+				BT_IO_OPT_DEST_BDADDR, &dst,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_PSM, AVDTP_PSM,
+				BT_IO_OPT_INVALID);
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	GError *gerr = NULL;
+
+	printf("%s\n", __func__);
+
+	do_connect(&gerr);
+	if (gerr) {
+		printf("connect failed: %s\n", gerr->message);
+		g_error_free(gerr);
+		g_main_loop_quit(mainloop);
+	}
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		start_media_player();
+	else
+		start_media_recorder();
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream,
+			struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream,
+			struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream,
+			struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+}
+
+static void reconfigure_cfm(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+}
+
+static void delay_report_cfm(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	printf("%s\n", __func__);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+	.set_configuration	= set_configuration_cfm,
+	.get_configuration	= get_configuration_cfm,
+	.open			= open_cfm,
+	.start			= start_cfm,
+	.suspend		= suspend_cfm,
+	.close			= close_cfm,
+	.abort			= abort_cfm,
+	.reconfigure		= reconfigure_cfm,
+	.delay_report		= delay_report_cfm,
+};
+
+static gboolean get_capability_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data)
+{
+	struct avdtp_service_capability *service;
+	int i;
+
+	printf("%s\n", __func__);
+
+	if (idle_id > 0) {
+		g_source_remove(idle_id);
+		idle_id = 0;
+	}
+
+	if (reject)
+		return FALSE;
+
+	*caps = NULL;
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+	*caps = g_slist_append(*caps, service);
+
+	service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec,
+						sizeof(sbc_codec));
+	*caps = g_slist_append(*caps, service);
+
+	if (fragment)
+		for (i = 0; i < 10; i++) {
+			service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC,
+							sbc_codec,
+							sizeof(sbc_codec));
+			*caps = g_slist_append(*caps, service);
+		}
+
+	return TRUE;
+}
+
+static gboolean set_configuration_ind(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					GSList *caps,
+					avdtp_set_configuration_cb cb,
+					void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (idle_id > 0) {
+		g_source_remove(idle_id);
+		idle_id = 0;
+	}
+
+	avdtp_stream = stream;
+
+	cb(session, stream, NULL);
+
+	send_command();
+
+	return TRUE;
+}
+
+static gboolean get_configuration_ind(struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	send_command();
+
+	return TRUE;
+}
+
+static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		start_media_player();
+	else
+		start_media_recorder();
+
+	send_command();
+
+	return TRUE;
+}
+
+static gboolean suspend_ind(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	return TRUE;
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+
+	return TRUE;
+}
+
+static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, uint8_t *err,
+			void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (dev_role == AVDTP_SEP_TYPE_SOURCE)
+		stop_media_player();
+	else
+		stop_media_recorder();
+
+	avdtp_stream = NULL;
+}
+
+static gboolean reconfigure_ind(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				uint8_t *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean delayreport_ind(struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				uint8_t rseid, uint16_t delay,
+				uint8_t *err, void *user_data)
+{
+	printf("%s\n", __func__);
+
+	if (reject)
+		return FALSE;
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+	.get_capability		= get_capability_ind,
+	.set_configuration	= set_configuration_ind,
+	.get_configuration	= get_configuration_ind,
+	.open			= open_ind,
+	.close			= close_ind,
+	.start			= start_ind,
+	.suspend		= suspend_ind,
+	.abort			= abort_ind,
+	.reconfigure		= reconfigure_ind,
+	.delayreport		= delayreport_ind,
+};
+
+static void usage(void)
+{
+	printf("avdtptest - AVDTP testing ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tavdtptest [options]\n");
+	printf("options:\n"
+		"\t-d <device_role>   SRC (source) or SINK (sink)\n"
+		"\t-i <hcidev>        HCI adapter\n"
+		"\t-c <bdaddr>        connect\n"
+		"\t-l                 listen\n"
+		"\t-r                 reject commands\n"
+		"\t-f                 fragment\n"
+		"\t-p                 configure stream\n"
+		"\t-s <command>       send command\n"
+		"\t-v <version>       set version (0x0100, 0x0102, 0x0103\n");
+}
+
+static struct option main_options[] = {
+	{ "help",		0, 0, 'h' },
+	{ "device_role",	1, 0, 'd' },
+	{ "adapter",		1, 0, 'i' },
+	{ "connect",		1, 0, 'c' },
+	{ "listen",		0, 0, 'l' },
+	{ "reject",		0, 0, 'r' },
+	{ "fragment",		0, 0, 'f' },
+	{ "preconf",		0, 0, 'p' },
+	{ "send",		1, 0, 's' },
+	{ "version",		1, 0, 'v' },
+	{ 0, 0, 0, 0 }
+};
+
+static GIOChannel *do_listen(GError **err)
+{
+	if (fragment)
+		return bt_io_listen(connect_cb, NULL, NULL, NULL, err,
+					BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_MTU, 48,
+					BT_IO_OPT_INVALID);
+
+	return bt_io_listen(connect_cb, NULL, NULL, NULL, err,
+					BT_IO_OPT_SOURCE_BDADDR, &src,
+					BT_IO_OPT_PSM, AVDTP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+}
+
+int main(int argc, char *argv[])
+{
+	GError *err = NULL;
+	int opt;
+
+	bacpy(&src, BDADDR_ANY);
+	bacpy(&dst, BDADDR_ANY);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+	if (!mainloop) {
+		printf("Failed to create main loop\n");
+
+		exit(1);
+	}
+
+	while ((opt = getopt_long(argc, argv, "d:hi:s:c:v:lrfp",
+						main_options, NULL)) != EOF) {
+		switch (opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &src);
+			else
+				str2ba(optarg, &src);
+			break;
+		case 'd':
+			if (!strncasecmp(optarg, "SRC", sizeof("SRC"))) {
+				dev_role = AVDTP_SEP_TYPE_SOURCE;
+			} else if (!strncasecmp(optarg, "SINK",
+							sizeof("SINK"))) {
+				dev_role = AVDTP_SEP_TYPE_SINK;
+			} else {
+				usage();
+				exit(1);
+			}
+			break;
+		case 'c':
+			if (str2ba(optarg, &dst) < 0) {
+				usage();
+				exit(1);
+			}
+			break;
+		case 'l':
+			bacpy(&dst, BDADDR_ANY);
+			break;
+		case 'r':
+			reject = true;
+			break;
+		case 'f':
+			fragment = true;
+			break;
+		case 'p':
+			preconf = true;
+			break;
+		case 's':
+			parse_command(optarg);
+			break;
+		case 'v':
+			version = strtol(optarg, NULL, 0);
+			if (version != 0x0100 && version != 0x0102 &&
+							version != 0x0103) {
+				printf("invalid version\n");
+				exit(1);
+			}
+
+			break;
+		case 'h':
+			usage();
+			exit(0);
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	lseps = queue_new();
+
+	local_sep = avdtp_register_sep(lseps, dev_role, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, &sep_cfm, NULL);
+	if (!local_sep) {
+		printf("Failed to register sep\n");
+		exit(1);
+	}
+
+	queue_push_tail(lseps, local_sep);
+
+	if (!bacmp(&dst, BDADDR_ANY)) {
+		printf("Listening...\n");
+		io = do_listen(&err);
+	} else {
+		printf("Connecting...\n");
+		io = do_connect(&err);
+	}
+
+	if (!io) {
+		printf("Failed: %s\n", err->message);
+		g_error_free(err);
+		exit(1);
+	}
+
+	g_main_loop_run(mainloop);
+
+	printf("Done\n");
+
+	queue_destroy(lseps, NULL);
+
+	avdtp_unref(avdtp);
+	avdtp = NULL;
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	return 0;
+}
diff --git a/bluez/repo/android/avrcp-lib.c b/bluez/repo/android/avrcp-lib.c
new file mode 100644
index 0000000..4edfd0e
--- /dev/null
+++ b/bluez/repo/android/avrcp-lib.c
@@ -0,0 +1,3615 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdbool.h>
+#include <glib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE		0x00
+#define AVRCP_PACKET_TYPE_START			0x01
+#define AVRCP_PACKET_TYPE_CONTINUING		0x02
+#define AVRCP_PACKET_TYPE_END			0x03
+
+#define AVRCP_CHARSET_UTF8	0x006a
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_header {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t packet_type:2;
+	uint8_t rsvd:6;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_header {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t rsvd:6;
+	uint8_t packet_type:2;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avrcp_browsing_header {
+	uint8_t pdu_id;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
+struct get_capabilities_req {
+	uint8_t cap;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct get_capabilities_rsp {
+	uint8_t cap;
+	uint8_t number;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct list_attributes_rsp {
+	uint8_t number;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct list_values_req {
+	uint8_t attr;
+} __attribute__ ((packed));
+
+struct list_values_rsp {
+	uint8_t number;
+	uint8_t params[0];
+} __attribute__ ((packed));
+
+struct get_value_req {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__ ((packed));
+
+struct attr_value {
+	uint8_t attr;
+	uint8_t value;
+} __attribute__ ((packed));
+
+struct value_rsp {
+	uint8_t number;
+	struct attr_value values[0];
+} __attribute__ ((packed));
+
+struct set_value_req {
+	uint8_t number;
+	struct attr_value values[0];
+} __attribute__ ((packed));
+
+struct get_attribute_text_req {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__ ((packed));
+
+struct text_value {
+	uint8_t attr;
+	uint16_t charset;
+	uint8_t len;
+	char data[0];
+} __attribute__ ((packed));
+
+struct get_attribute_text_rsp {
+	uint8_t number;
+	struct text_value values[0];
+} __attribute__ ((packed));
+
+struct get_value_text_req {
+	uint8_t attr;
+	uint8_t number;
+	uint8_t values[0];
+} __attribute__ ((packed));
+
+struct get_value_text_rsp {
+	uint8_t number;
+	struct text_value values[0];
+} __attribute__ ((packed));
+
+struct media_item {
+	uint32_t attr;
+	uint16_t charset;
+	uint16_t len;
+	char data[0];
+} __attribute__ ((packed));
+
+struct get_element_attributes_req {
+	uint64_t id;
+	uint8_t number;
+	uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_element_attributes_rsp {
+	uint8_t number;
+	struct media_item items[0];
+} __attribute__ ((packed));
+
+struct get_play_status_rsp {
+	uint32_t duration;
+	uint32_t position;
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct register_notification_req {
+	uint8_t event;
+	uint32_t interval;
+} __attribute__ ((packed));
+
+struct register_notification_rsp {
+	uint8_t event;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct set_volume_req {
+	uint8_t value;
+} __attribute__ ((packed));
+
+struct set_volume_rsp {
+	uint8_t value;
+} __attribute__ ((packed));
+
+struct set_addressed_req {
+	uint16_t id;
+} __attribute__ ((packed));
+
+struct set_addressed_rsp {
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct set_browsed_req {
+	uint16_t id;
+} __attribute__ ((packed));
+
+struct set_browsed_rsp {
+	uint8_t status;
+	uint16_t counter;
+	uint32_t items;
+	uint16_t charset;
+	uint8_t depth;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct get_folder_items_req {
+	uint8_t scope;
+	uint32_t start;
+	uint32_t end;
+	uint8_t number;
+	uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_folder_items_rsp {
+	uint8_t status;
+	uint16_t counter;
+	uint16_t number;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct change_path_req {
+	uint16_t counter;
+	uint8_t direction;
+	uint64_t uid;
+} __attribute__ ((packed));
+
+struct change_path_rsp {
+	uint8_t status;
+	uint32_t items;
+} __attribute__ ((packed));
+
+struct get_item_attributes_req {
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t counter;
+	uint8_t number;
+	uint32_t attrs[0];
+} __attribute__ ((packed));
+
+struct get_item_attributes_rsp {
+	uint8_t status;
+	uint8_t number;
+	struct media_item items[0];
+} __attribute__ ((packed));
+
+struct play_item_req {
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t counter;
+} __attribute__ ((packed));
+
+struct play_item_rsp {
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct search_req {
+	uint16_t charset;
+	uint16_t len;
+	char string[0];
+} __attribute__ ((packed));
+
+struct search_rsp {
+	uint8_t status;
+	uint16_t counter;
+	uint32_t items;
+} __attribute__ ((packed));
+
+struct add_to_now_playing_req {
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t counter;
+} __attribute__ ((packed));
+
+struct add_to_now_playing_rsp {
+	uint8_t status;
+} __attribute__ ((packed));
+
+struct avrcp_control_handler {
+	uint8_t id;
+	uint8_t code;
+	uint8_t rsp;
+	ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+			uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_browsing_handler {
+	uint8_t id;
+	ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+			uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_continuing {
+	uint8_t pdu_id;
+	struct iovec pdu;
+};
+
+struct avrcp {
+	struct avctp *conn;
+	struct avrcp_player *player;
+
+	const struct avrcp_control_handler *control_handlers;
+	void *control_data;
+	unsigned int control_id;
+	uint16_t control_mtu;
+
+	struct avrcp_continuing *continuing;
+
+	const struct avrcp_passthrough_handler *passthrough_handlers;
+	void *passthrough_data;
+	unsigned int passthrough_id;
+
+	const struct avrcp_browsing_handler *browsing_handlers;
+	void *browsing_data;
+	unsigned int browsing_id;
+
+	avrcp_destroy_cb_t destroy;
+	void *destroy_data;
+};
+
+struct avrcp_player {
+	const struct avrcp_control_ind *ind;
+	const struct avrcp_control_cfm *cfm;
+
+	void *user_data;
+};
+
+static inline uint32_t ntoh24(const uint8_t src[3])
+{
+	return src[0] << 16 | src[1] << 8 | src[2];
+}
+
+static inline void hton24(uint8_t dst[3], uint32_t src)
+{
+	dst[0] = (src & 0xff0000) >> 16;
+	dst[1] = (src & 0x00ff00) >> 8;
+	dst[2] = (src & 0x0000ff);
+}
+
+static void continuing_free(struct avrcp_continuing *continuing)
+{
+	g_free(continuing->pdu.iov_base);
+	g_free(continuing);
+}
+
+void avrcp_shutdown(struct avrcp *session)
+{
+	if (session->conn) {
+		if (session->control_id > 0)
+			avctp_unregister_pdu_handler(session->conn,
+							session->control_id);
+		if (session->passthrough_id > 0)
+			avctp_unregister_passthrough_handler(session->conn,
+						session->passthrough_id);
+
+		if (session->browsing_id > 0)
+			avctp_unregister_browsing_pdu_handler(session->conn,
+							session->browsing_id);
+
+		/* clear destroy callback that would call shutdown again */
+		avctp_set_destroy_cb(session->conn, NULL, NULL);
+		avctp_shutdown(session->conn);
+	}
+
+	if (session->destroy)
+		session->destroy(session->destroy_data);
+
+	if (session->continuing)
+		continuing_free(session->continuing);
+
+	g_free(session->player);
+	g_free(session);
+}
+
+static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count)
+{
+	struct avrcp_header *pdu;
+
+	if (!operands || operand_count < sizeof(*pdu)) {
+		error("AVRCP: packet too small (%zu bytes)", operand_count);
+		return NULL;
+	}
+
+	pdu = (void *) operands;
+	pdu->params_len = ntohs(pdu->params_len);
+
+	if (operand_count != pdu->params_len + sizeof(*pdu)) {
+		error("AVRCP: invalid parameter length (%u bytes)",
+							pdu->params_len);
+		return NULL;
+	}
+
+	return pdu;
+}
+
+static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands,
+							size_t operand_count)
+{
+	struct avrcp_browsing_header *pdu;
+
+	if (!operands || operand_count < sizeof(*pdu)) {
+		error("AVRCP: packet too small (%zu bytes)", operand_count);
+		return NULL;
+	}
+
+	pdu = (void *) operands;
+	pdu->params_len = ntohs(pdu->params_len);
+
+	if (operand_count != pdu->params_len + sizeof(*pdu)) {
+		error("AVRCP: invalid parameter length (%u bytes)",
+							pdu->params_len);
+		return NULL;
+	}
+
+	return pdu;
+}
+
+static uint8_t errno2status(int err)
+{
+	switch (err) {
+	case -ENOSYS:
+		return AVRCP_STATUS_INVALID_COMMAND;
+	case -EINVAL:
+		return AVRCP_STATUS_INVALID_PARAM;
+	case 0:
+		return AVRCP_STATUS_SUCCESS;
+	case -ENOTDIR:
+		return AVRCP_STATUS_NOT_DIRECTORY;
+	case -EBADRQC:
+		return AVRCP_STATUS_INVALID_SCOPE;
+	case -ERANGE:
+		return AVRCP_STATUS_OUT_OF_BOUNDS;
+	case -ENOENT:
+		return AVRCP_STATUS_DOES_NOT_EXIST;
+	default:
+		return AVRCP_STATUS_INTERNAL_ERROR;
+	}
+}
+
+static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
+					uint8_t *code, uint8_t *subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct avrcp_control_handler *handler;
+	struct avrcp_header *pdu;
+	uint32_t company_id;
+	ssize_t ret;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		pdu = (void *) operands;
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	company_id = ntoh24(pdu->company_id);
+	if (company_id != IEEEID_BTSIG) {
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len);
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (!session->control_handlers)
+		goto reject;
+
+	for (handler = session->control_handlers; handler->id; handler++) {
+		if (handler->id == pdu->pdu_id)
+			break;
+	}
+
+	if (handler->id != pdu->pdu_id || handler->code != *code) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	if (!handler->func) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		goto reject;
+	}
+
+	ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+							session->control_data);
+	if (ret == 0)
+		return -EAGAIN;
+
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return ret;
+		pdu->params[0] = errno2status(ret);
+		goto reject;
+	}
+
+	*code = handler->rsp;
+	pdu->params_len = htons(ret);
+
+	return AVRCP_HEADER_LENGTH + ret;
+
+reject:
+	pdu->params_len = htons(1);
+	*code = AVC_CTYPE_REJECTED;
+
+	return AVRCP_HEADER_LENGTH + 1;
+}
+
+static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op,
+						bool pressed, void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct avrcp_passthrough_handler *handler;
+
+	if (!session->passthrough_handlers)
+		return false;
+
+	for (handler = session->passthrough_handlers; handler->func;
+								handler++) {
+		if (handler->op == op)
+			break;
+	}
+
+	if (handler->func == NULL)
+		return false;
+
+	return handler->func(session, pressed, session->passthrough_data);
+}
+
+static void disconnect_cb(void *data)
+{
+	struct avrcp *session = data;
+
+	session->conn = NULL;
+
+	avrcp_shutdown(session);
+}
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+	struct avrcp *session;
+
+	session = g_new0(struct avrcp, 1);
+
+	session->conn = avctp_new(fd, imtu, omtu, version);
+	if (!session->conn) {
+		g_free(session);
+		return NULL;
+	}
+
+	session->passthrough_id = avctp_register_passthrough_handler(
+							session->conn,
+							handle_passthrough_pdu,
+							session);
+	session->control_id = avctp_register_pdu_handler(session->conn,
+							AVC_OP_VENDORDEP,
+							handle_vendordep_pdu,
+							session);
+	session->control_mtu = omtu - AVC_DATA_OFFSET;
+
+	/*
+	 * 27.1.2 AV/C Command Frame
+	 * An AV/C command frame contains up to 512 octets of data
+	 */
+	if (session->control_mtu > AVC_DATA_MTU)
+		session->control_mtu = AVC_DATA_MTU;
+
+	avctp_set_destroy_cb(session->conn, disconnect_cb, session);
+
+	return session;
+}
+
+static ssize_t handle_browsing_pdu(struct avctp *conn,
+					uint8_t transaction, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct avrcp_browsing_handler *handler;
+	struct avrcp_browsing_header *pdu;
+	int ret;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		pdu = (void *) operands;
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
+							pdu->params_len);
+
+	if (!session->browsing_handlers) {
+		pdu->pdu_id = AVRCP_GENERAL_REJECT;
+		pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+		goto reject;
+	}
+
+	for (handler = session->browsing_handlers; handler->id; handler++) {
+		if (handler->id == pdu->pdu_id)
+			break;
+	}
+
+	if (handler->id != pdu->pdu_id) {
+		pdu->pdu_id = AVRCP_GENERAL_REJECT;
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto reject;
+	}
+
+	if (!handler->func) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		goto reject;
+	}
+
+	ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+							session->control_data);
+	if (ret == 0)
+		return -EAGAIN;
+
+	if (ret < 0) {
+		if (ret == -EAGAIN)
+			return ret;
+		pdu->params[0] = errno2status(ret);
+		goto reject;
+	}
+
+	pdu->params_len = htons(ret);
+
+	return AVRCP_BROWSING_HEADER_LENGTH + ret;
+
+reject:
+	pdu->params_len = htons(1);
+
+	return AVRCP_BROWSING_HEADER_LENGTH + 1;
+}
+
+static void browsing_disconnect_cb(void *data)
+{
+	struct avrcp *session = data;
+
+	session->browsing_id = 0;
+}
+
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+								size_t omtu)
+{
+	int err;
+
+	err = avctp_connect_browsing(session->conn, fd, imtu, omtu);
+	if (err < 0)
+		return err;
+
+	session->browsing_id = avctp_register_browsing_pdu_handler(
+							session->conn,
+							handle_browsing_pdu,
+							session,
+							browsing_disconnect_cb);
+
+	return 0;
+}
+
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+							void *user_data)
+{
+	session->destroy = cb;
+	session->destroy_data = user_data;
+}
+
+static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction,
+				uint16_t params_len, uint8_t *params,
+				void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_capabilities_req *req;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	switch (req->cap) {
+	case CAP_COMPANY_ID:
+		req->params[0] = 1;
+		hton24(&req->params[1], IEEEID_BTSIG);
+		return 5;
+	case CAP_EVENTS_SUPPORTED:
+		if (!player->ind || !player->ind->get_capabilities)
+			return -ENOSYS;
+		return player->ind->get_capabilities(session, transaction,
+							player->user_data);
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t list_attributes(struct avrcp *session, uint8_t transaction,
+				uint16_t params_len, uint8_t *params,
+				void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->list_attributes)
+		return -ENOSYS;
+
+	return player->ind->list_attributes(session, transaction,
+							player->user_data);
+}
+
+static bool check_attributes(uint8_t number, const uint8_t *attrs)
+{
+	int i;
+
+	for (i = 0; i < number; i++) {
+		if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+					attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+			return false;
+	}
+
+	return true;
+}
+
+static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_attribute_text_req *req;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_attribute_text)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len != sizeof(*req) + req->number)
+		return -EINVAL;
+
+	if (!check_attributes(req->number, req->attrs))
+		return -EINVAL;
+
+	return player->ind->get_attribute_text(session, transaction,
+						req->number, req->attrs,
+						player->user_data);
+}
+
+static ssize_t list_values(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct list_values_req *req;
+
+	DBG("");
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (req->attr > AVRCP_ATTRIBUTE_LAST ||
+					req->attr == AVRCP_ATTRIBUTE_ILEGAL)
+		return -EINVAL;
+
+	if (!player->ind || !player->ind->list_values)
+		return -ENOSYS;
+
+	return player->ind->list_values(session, transaction, req->attr,
+							player->user_data);
+}
+
+static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values)
+{
+	int i;
+
+	for (i = 0; i < number; i++) {
+		/* Check for invalid value */
+		switch (attr) {
+		case AVRCP_ATTRIBUTE_EQUALIZER:
+			if (values[i] < AVRCP_EQUALIZER_OFF ||
+						values[i] > AVRCP_EQUALIZER_ON)
+				return false;
+			break;
+		case AVRCP_ATTRIBUTE_REPEAT_MODE:
+			if (values[i] < AVRCP_REPEAT_MODE_OFF ||
+					values[i] > AVRCP_REPEAT_MODE_GROUP)
+				return false;
+			break;
+		case AVRCP_ATTRIBUTE_SHUFFLE:
+			if (values[i] < AVRCP_SHUFFLE_OFF ||
+					values[i] > AVRCP_SHUFFLE_GROUP)
+				return false;
+			break;
+		case AVRCP_ATTRIBUTE_SCAN:
+			if (values[i] < AVRCP_SCAN_OFF ||
+					values[i] > AVRCP_SCAN_GROUP)
+				return false;
+			break;
+		}
+	}
+
+	return true;
+}
+
+static ssize_t get_value_text(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_value_text_req *req;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_value_text)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len != sizeof(*req) + req->number)
+		return -EINVAL;
+
+	if (req->number > AVRCP_ATTRIBUTE_LAST ||
+					req->number == AVRCP_ATTRIBUTE_ILEGAL)
+		return -EINVAL;
+
+	if (!check_value(req->attr, req->number, req->values))
+		return -EINVAL;
+
+	return player->ind->get_value_text(session, transaction, params[0],
+						params[1], &params[2],
+						player->user_data);
+}
+
+static ssize_t get_value(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_value_req *req;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_value)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len < sizeof(*req) + req->number)
+		return -EINVAL;
+
+	if (!check_attributes(req->number, req->attrs))
+		return -EINVAL;
+
+	return player->ind->get_value(session, transaction, params[0],
+					&params[1], player->user_data);
+}
+
+static ssize_t set_value(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_value_req *req;
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	uint8_t values[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_value)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+	if (params_len < sizeof(*req) + req->number * sizeof(*req->values))
+		return -EINVAL;
+
+	for (i = 0; i < req->number; i++) {
+		attrs[i] = req->values[i].attr;
+		values[i] = req->values[i].value;
+
+		if (!check_value(attrs[i], 1, &values[i]))
+			return -EINVAL;
+	}
+
+	return player->ind->set_value(session, transaction, req->number,
+					attrs, values, player->user_data);
+}
+
+static ssize_t get_play_status(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_play_status)
+		return -ENOSYS;
+
+	return player->ind->get_play_status(session, transaction,
+							player->user_data);
+}
+
+static bool parse_attributes(uint32_t *params, uint16_t params_len,
+					uint8_t number, uint32_t *attrs)
+{
+	int i;
+
+	for (i = 0; i < number && params_len >= sizeof(*attrs); i++,
+					params_len -= sizeof(*attrs)) {
+		attrs[i] = be32_to_cpu(params[i]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return false;
+	}
+
+	return true;
+}
+
+static ssize_t get_element_attributes(struct avrcp *session,
+						uint8_t transaction,
+						uint16_t params_len,
+						uint8_t *params,
+						void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_element_attributes_req *req;
+	uint64_t uid;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_element_attributes)
+		return -ENOSYS;
+
+	req = (void *) params;
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	if (!parse_attributes(req->attrs, params_len - sizeof(*req),
+							req->number, attrs))
+		return -EINVAL;
+
+	uid = get_be64(params);
+
+	return player->ind->get_element_attributes(session, transaction, uid,
+							req->number, attrs,
+							player->user_data);
+}
+
+static ssize_t register_notification(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct register_notification_req *req;
+	uint32_t interval;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->register_notification)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	interval = be32_to_cpu(req->interval);
+
+	return player->ind->register_notification(session, transaction,
+							req->event, interval,
+							player->user_data);
+}
+
+static ssize_t set_volume(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_volume_req *req;
+	uint8_t volume;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_volume)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(volume))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	volume = req->value & 0x7f;
+
+	return player->ind->set_volume(session, transaction, volume,
+							player->user_data);
+}
+
+static ssize_t set_addressed(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_addressed_req *req;
+	uint16_t id;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_addressed)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	id = be16_to_cpu(req->id);
+
+	return player->ind->set_addressed(session, transaction, id,
+							player->user_data);
+}
+
+static void continuing_new(struct avrcp *session, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt,
+					size_t offset)
+{
+	struct avrcp_continuing *continuing;
+	int i;
+	size_t len = 0;
+
+	continuing = g_new0(struct avrcp_continuing, 1);
+	continuing->pdu_id = pdu_id;
+
+	for (i = 0; i < iov_cnt; i++) {
+		if (i == 0 && offset) {
+			len += iov[i].iov_len - offset;
+			continue;
+		}
+
+		len += iov[i].iov_len;
+	}
+
+	continuing->pdu.iov_base = g_malloc0(len);
+
+	DBG("len %zu", len);
+
+	for (i = 0; i < iov_cnt; i++) {
+		if (i == 0 && offset) {
+			memcpy(continuing->pdu.iov_base,
+						iov[i].iov_base + offset,
+						iov[i].iov_len - offset);
+			continuing->pdu.iov_len += iov[i].iov_len - offset;
+			continue;
+		}
+
+		memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len,
+					iov[i].iov_base, iov[i].iov_len);
+		continuing->pdu.iov_len += iov[i].iov_len;
+	}
+
+	session->continuing = continuing;
+}
+
+static int avrcp_send_internal(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t subunit,
+					uint8_t pdu_id, uint8_t type,
+					const struct iovec *iov, int iov_cnt)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_header hdr;
+	int i;
+
+	/*
+	 * If a receiver receives a start fragment or non-fragmented AVRCP
+	 * Specific AV/C message when it already has an incomplete fragment
+	 * from that sender then the receiver shall consider the first PDU
+	 * aborted.
+	 */
+	if (session->continuing) {
+		continuing_free(session->continuing);
+		session->continuing = NULL;
+	}
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	hdr.packet_type = type;
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+
+		if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <=
+							session->control_mtu) {
+			pdu[i + 1].iov_len = iov[i].iov_len;
+			hdr.params_len += iov[i].iov_len;
+			if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE)
+				hdr.packet_type = AVRCP_PACKET_TYPE_END;
+			continue;
+		}
+
+		/*
+		 * Only send what can fit and store the remaining in the
+		 * continuing iovec
+		 */
+		pdu[i + 1].iov_len = session->control_mtu -
+					(pdu[0].iov_len + hdr.params_len);
+		hdr.params_len += pdu[i + 1].iov_len;
+
+		continuing_new(session, pdu_id, &iov[i], iov_cnt - i,
+							pdu[i + 1].iov_len);
+
+		hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ?
+						AVRCP_PACKET_TYPE_CONTINUING :
+						AVRCP_PACKET_TYPE_START;
+		break;
+	}
+
+	hton24(hdr.company_id, IEEEID_BTSIG);
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	return avctp_send_vendor(session->conn, transaction, code, subunit,
+							pdu, iov_cnt + 1);
+}
+
+static ssize_t request_continuing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct iovec iov;
+	int err;
+
+	DBG("");
+
+	if (!params || params_len != 1 || !session->continuing ||
+				session->continuing->pdu_id != params[0])
+		return -EINVAL;
+
+	iov.iov_base = session->continuing->pdu.iov_base;
+	iov.iov_len = session->continuing->pdu.iov_len;
+
+	DBG("len %zu", iov.iov_len);
+
+	session->continuing->pdu.iov_base = NULL;
+
+	err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE,
+					AVC_SUBUNIT_PANEL, params[0],
+					AVRCP_PACKET_TYPE_CONTINUING, &iov, 1);
+
+	g_free(iov.iov_base);
+
+	if (err < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	DBG("");
+
+	if (!params || params_len != 1 || !session->continuing)
+		return -EINVAL;
+
+	continuing_free(session->continuing);
+	session->continuing = NULL;
+
+	avrcp_send_internal(session, transaction, AVC_CTYPE_ACCEPTED,
+				AVC_SUBUNIT_PANEL, AVRCP_ABORT_CONTINUING,
+				AVRCP_PACKET_TYPE_SINGLE, NULL, 0);
+
+	return 0;
+}
+
+static const struct avrcp_control_handler player_handlers[] = {
+		{ AVRCP_GET_CAPABILITIES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_capabilities },
+		{ AVRCP_LIST_PLAYER_ATTRIBUTES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					list_attributes },
+		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_attribute_text },
+		{ AVRCP_LIST_PLAYER_VALUES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					list_values },
+		{ AVRCP_GET_PLAYER_VALUE_TEXT,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_value_text },
+		{ AVRCP_GET_CURRENT_PLAYER_VALUE,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_value },
+		{ AVRCP_SET_PLAYER_VALUE,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_value },
+		{ AVRCP_GET_PLAY_STATUS,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_play_status },
+		{ AVRCP_GET_ELEMENT_ATTRIBUTES,
+					AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+					get_element_attributes },
+		{ AVRCP_REGISTER_NOTIFICATION,
+					AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM,
+					register_notification },
+		{ AVRCP_SET_ABSOLUTE_VOLUME,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_volume },
+		{ AVRCP_SET_ADDRESSED_PLAYER,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					set_addressed },
+		{ AVRCP_REQUEST_CONTINUING,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+					request_continuing },
+		{ AVRCP_ABORT_CONTINUING,
+					AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED,
+					abort_continuing },
+		{ },
+};
+
+static void avrcp_set_control_handlers(struct avrcp *session,
+				const struct avrcp_control_handler *handlers,
+				void *user_data)
+{
+	session->control_handlers = handlers;
+	session->control_data = user_data;
+}
+
+static ssize_t set_browsed(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct set_browsed_req *req;
+	uint16_t id;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->set_browsed)
+		return -ENOSYS;
+
+	if (!params || params_len != sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	id = be16_to_cpu(req->id);
+
+	return player->ind->set_browsed(session, transaction, id,
+							player->user_data);
+}
+
+static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_folder_items_req *req;
+	uint32_t start, end;
+	uint16_t number;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_folder_items)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	start = be32_to_cpu(req->start);
+	end = be32_to_cpu(req->end);
+
+	if (start > end)
+		return -ERANGE;
+
+	number = be16_to_cpu(req->number);
+
+	for (i = 0; i < number; i++) {
+		attrs[i] = be32_to_cpu(req->attrs[i]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return -EINVAL;
+	}
+
+	return player->ind->get_folder_items(session, transaction, req->scope,
+						start, end, number, attrs,
+						player->user_data);
+}
+
+static ssize_t change_path(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct change_path_req *req;
+	uint16_t counter;
+	uint64_t uid;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->change_path)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	counter = be16_to_cpu(req->counter);
+	uid = be64_to_cpu(req->uid);
+
+	return player->ind->change_path(session, transaction, counter,
+					req->direction, uid, player->user_data);
+}
+
+static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct get_item_attributes_req *req;
+	uint64_t uid;
+	uint16_t counter;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int i;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->get_item_attributes)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	uid = be64_to_cpu(req->uid);
+	counter = be16_to_cpu(req->counter);
+
+	for (i = 0; i < req->number; i++) {
+		attrs[i] = be32_to_cpu(req->attrs[i]);
+
+		if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+				attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+			return -EINVAL;
+	}
+
+	return player->ind->get_item_attributes(session, transaction,
+						req->scope, uid, counter,
+						req->number, attrs,
+						player->user_data);
+}
+
+static ssize_t play_item(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct play_item_req *req;
+	uint64_t uid;
+	uint16_t counter;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->play_item)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	uid = be64_to_cpu(req->uid);
+	counter = be16_to_cpu(req->counter);
+
+	return player->ind->play_item(session, transaction, req->scope, uid,
+						counter, player->user_data);
+}
+
+static ssize_t search(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct search_req *req;
+	char *string;
+	uint16_t len;
+	int ret;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->search)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	len = be16_to_cpu(req->len);
+	if (!len)
+		return -EINVAL;
+
+	string = strndup(req->string, len);
+
+	ret = player->ind->search(session, transaction, string,
+							player->user_data);
+
+	free(string);
+
+	return ret;
+}
+
+static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction,
+					uint16_t params_len, uint8_t *params,
+					void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct add_to_now_playing_req *req;
+	uint64_t uid;
+	uint16_t counter;
+
+	DBG("");
+
+	if (!player->ind || !player->ind->add_to_now_playing)
+		return -ENOSYS;
+
+	if (!params || params_len < sizeof(*req))
+		return -EINVAL;
+
+	req = (void *) params;
+
+	if (req->scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EBADRQC;
+
+	uid = be64_to_cpu(req->uid);
+	counter = be16_to_cpu(req->counter);
+
+	return player->ind->add_to_now_playing(session, transaction, req->scope,
+							uid, counter,
+							player->user_data);
+}
+
+static const struct avrcp_browsing_handler browsing_handlers[] = {
+		{ AVRCP_SET_BROWSED_PLAYER, set_browsed },
+		{ AVRCP_GET_FOLDER_ITEMS, get_folder_items },
+		{ AVRCP_CHANGE_PATH, change_path },
+		{ AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes },
+		{ AVRCP_PLAY_ITEM, play_item },
+		{ AVRCP_SEARCH, search },
+		{ AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing },
+		{ },
+};
+
+static void avrcp_set_browsing_handlers(struct avrcp *session,
+				const struct avrcp_browsing_handler *handlers,
+				void *user_data)
+{
+	session->browsing_handlers = handlers;
+	session->browsing_data = user_data;
+}
+
+void avrcp_register_player(struct avrcp *session,
+				const struct avrcp_control_ind *ind,
+				const struct avrcp_control_cfm *cfm,
+				void *user_data)
+{
+	struct avrcp_player *player;
+
+	player = g_new0(struct avrcp_player, 1);
+	player->ind = ind;
+	player->cfm = cfm;
+	player->user_data = user_data;
+
+	avrcp_set_control_handlers(session, player_handlers, player);
+	avrcp_set_browsing_handlers(session, browsing_handlers, player);
+	session->player = player;
+}
+
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+			const struct avrcp_passthrough_handler *handlers,
+			void *user_data)
+{
+	session->passthrough_handlers = handlers;
+	session->passthrough_data = user_data;
+}
+
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+							const char *address)
+{
+	return avctp_init_uinput(session->conn, name, address);
+}
+
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+					uint8_t subunit, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt)
+{
+	return avrcp_send_internal(session, transaction, code, subunit, pdu_id,
+					AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt);
+}
+
+static int status2errno(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_STATUS_INVALID_COMMAND:
+		return -ENOSYS;
+	case AVRCP_STATUS_INVALID_PARAM:
+		return -EINVAL;
+	case AVRCP_STATUS_SUCCESS:
+		return 0;
+	case AVRCP_STATUS_NOT_DIRECTORY:
+		return -ENOTDIR;
+	case AVRCP_STATUS_INVALID_SCOPE:
+		return -EBADRQC;
+	case AVRCP_STATUS_OUT_OF_BOUNDS:
+		return -ERANGE;
+	case AVRCP_STATUS_INTERNAL_ERROR:
+	case AVRCP_STATUS_INVALID_PLAYER_ID:
+	case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
+	case AVRCP_STATUS_NO_AVAILABLE_PLAYERS:
+	case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED:
+		return -EPERM;
+	default:
+		return -EPROTO;
+	}
+}
+
+static int parse_status(struct avrcp_header *pdu)
+{
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	return status2errno(pdu->params[0]);
+}
+
+static int parse_browsing_status(struct avrcp_browsing_header *pdu)
+{
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	return status2errno(pdu->params[0]);
+}
+
+static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
+					uint8_t pdu_id, const struct iovec *iov,
+					int iov_cnt, avctp_rsp_cb func,
+					void *user_data)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_header hdr;
+	int i;
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len = iov[i].iov_len;
+		hdr.params_len += iov[i].iov_len;
+	}
+
+	hton24(hdr.company_id, IEEEID_BTSIG);
+	hdr.pdu_id = pdu_id;
+	hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	hdr.params_len = htons(hdr.params_len);
+
+	return avctp_send_vendor_req(session->conn, code, subunit, pdu,
+						iov_cnt + 1, func, user_data);
+}
+
+static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt,
+					avctp_browsing_rsp_cb func,
+					void *user_data)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_browsing_header hdr;
+	int i;
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len = iov[i].iov_len;
+		hdr.params_len += iov[i].iov_len;
+	}
+
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1,
+							func, user_data);
+}
+
+static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction,
+				uint8_t pdu_id, const struct iovec *iov,
+				int iov_cnt)
+{
+	struct iovec pdu[iov_cnt + 1];
+	struct avrcp_browsing_header hdr;
+	int i;
+
+	memset(&hdr, 0, sizeof(hdr));
+
+	for (i = 0; i < iov_cnt; i++) {
+		pdu[i + 1].iov_base = iov[i].iov_base;
+		pdu[i + 1].iov_len = iov[i].iov_len;
+		hdr.params_len += iov[i].iov_len;
+	}
+
+	hdr.pdu_id = pdu_id;
+	hdr.params_len = htons(hdr.params_len);
+
+	pdu[0].iov_base = &hdr;
+	pdu[0].iov_len = sizeof(hdr);
+
+	return avctp_send_browsing(session->conn, transaction, pdu,
+								iov_cnt + 1);
+}
+
+static gboolean get_capabilities_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct get_capabilities_rsp *rsp;
+	uint8_t number = 0;
+	uint8_t *params = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_capabilities)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	switch (rsp->cap) {
+	case CAP_COMPANY_ID:
+	case CAP_EVENTS_SUPPORTED:
+		break;
+	default:
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (rsp->number > 0) {
+		number = rsp->number;
+		params = rsp->params;
+	}
+
+	err = 0;
+
+done:
+	player->cfm->get_capabilities(session, err, number, params,
+							player->user_data);
+
+	return FALSE;
+}
+
+
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param)
+{
+	struct iovec iov;
+	struct get_capabilities_req req;
+
+	req.cap = param;
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+					AVRCP_GET_CAPABILITIES, &iov, 1,
+					get_capabilities_rsp, session);
+}
+
+static gboolean register_notification_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct register_notification_rsp *rsp;
+	uint8_t event = 0;
+	uint16_t value16, value16_2[2];
+	uint32_t value32;
+	uint64_t value64;
+	uint8_t *params = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->register_notification)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+	event = rsp->event;
+
+	if (event > AVRCP_EVENT_LAST) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	switch (event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) {
+			err = -EPROTO;
+			goto done;
+		}
+		params = rsp->data;
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(uint8_t)) {
+			err = -EPROTO;
+			goto done;
+		}
+		params = rsp->data;
+		params[0] &= 0x7f;
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(value64)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value64 = get_be64(rsp->data);
+		params = (uint8_t *) &value64;
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		if (pdu->params_len != sizeof(*rsp) + sizeof(value32)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value32 = get_be32(rsp->data);
+		params = (uint8_t *) &value32;
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		if (pdu->params_len < sizeof(*rsp) + sizeof(value16_2)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value16_2[0] = get_be16(rsp->data);
+		value16_2[1] = get_be16(rsp->data + 2);
+		params = (uint8_t *) value16_2;
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		if (pdu->params_len < sizeof(*rsp) + sizeof(uint8_t)) {
+			err = -EPROTO;
+			goto done;
+		}
+		params = rsp->data;
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		if (pdu->params_len < sizeof(*rsp) + sizeof(value16)) {
+			err = -EPROTO;
+			goto done;
+		}
+		value16 = get_be16(rsp->data);
+		params = (uint8_t *) &value16;
+		break;
+	}
+
+	err = 0;
+
+done:
+	return player->cfm->register_notification(session, err, code, event,
+						params, player->user_data);
+}
+
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+							uint32_t interval)
+{
+	struct iovec iov;
+	struct register_notification_req req;
+
+	if (event > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	req.event = event;
+	req.interval = cpu_to_be32(interval);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL,
+				AVRCP_REGISTER_NOTIFICATION, &iov, 1,
+				register_notification_rsp, session);
+}
+
+static gboolean list_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu = (void *) operands;
+	struct list_attributes_rsp *rsp;
+	uint8_t number = 0;
+	uint8_t *attrs = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->list_attributes)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	number = rsp->number;
+	if (number > 0)
+		attrs = rsp->params;
+
+	err = 0;
+
+done:
+	player->cfm->list_attributes(session, err, number, attrs,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_list_player_attributes(struct avrcp *session)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0,
+				list_attributes_rsp, session);
+}
+
+static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number,
+					uint8_t *attrs, char **text)
+{
+	uint8_t *ptr;
+	uint16_t params_len;
+	int i;
+
+	if (pdu->params_len < 1)
+		return -EPROTO;
+
+	*number = pdu->params[0];
+	if (*number > AVRCP_ATTRIBUTE_LAST) {
+		*number = 0;
+		return -EPROTO;
+	}
+
+	params_len = pdu->params_len - 1;
+	for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) {
+		struct text_value *val;
+
+		if (params_len < sizeof(*val))
+			goto fail;
+
+		val = (void *) ptr;
+
+		attrs[i] = val->attr;
+
+		params_len -= sizeof(*val);
+		ptr += sizeof(*val);
+
+		if (val->len > params_len)
+			goto fail;
+
+		if (val->len > 0) {
+			text[i] = g_strndup(val->data, val->len);
+			params_len -= val->len;
+			ptr += val->len;
+		}
+	}
+
+	if (i != *number)
+		goto fail;
+
+	return 0;
+
+fail:
+	for (i -= 1; i >= 0; i--)
+		g_free(text[i]);
+
+	*number = 0;
+
+	return -EPROTO;
+}
+
+static gboolean get_attribute_text_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	char *text[AVRCP_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_attribute_text)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_text_rsp(pdu, &number, attrs, text);
+
+done:
+	player->cfm->get_attribute_text(session, err, number, attrs, text,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+								uint8_t *attrs)
+{
+	struct iovec iov[2];
+
+	if (!number || number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number;
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2,
+				get_attribute_text_rsp, session);
+}
+
+static gboolean list_values_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct list_values_rsp *rsp;
+	uint8_t number = 0;
+	uint8_t *values = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->list_values)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	if (rsp->number > 0) {
+		number = rsp->number;
+		values = rsp->params;
+	}
+
+	err = 0;
+
+done:
+	player->cfm->list_values(session, err, number, values,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr)
+{
+	struct iovec iov;
+
+	iov.iov_base = &attr;
+	iov.iov_len = sizeof(attr);
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+					AVRCP_LIST_PLAYER_VALUES, &iov, 1,
+					list_values_rsp, session);
+}
+
+static gboolean get_value_text_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint8_t values[AVRCP_ATTRIBUTE_LAST];
+	char *text[AVRCP_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_value_text)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_text_rsp(pdu, &number, values, text);
+
+done:
+	player->cfm->get_value_text(session, err, number, values, text,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+					uint8_t number, uint8_t *values)
+{
+	struct iovec iov[2];
+	struct get_value_text_req req;
+
+	if (!number)
+		return -EINVAL;
+
+	req.attr = attr;
+	req.number = number;
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	iov[1].iov_base = values;
+	iov[1].iov_len = number;
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+					AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2,
+					get_value_text_rsp, session);
+}
+
+static int parse_value(struct avrcp_header *pdu, uint8_t *number,
+					uint8_t *attrs, uint8_t *values)
+{
+	int i;
+	struct value_rsp *rsp;
+
+	if (pdu->params_len < sizeof(*rsp))
+		return -EPROTO;
+
+	rsp = (void *) pdu->params;
+
+	/*
+	 * Check if PDU is big enough to hold the number of (attribute, value)
+	 * tuples.
+	 */
+	if (rsp->number > AVRCP_ATTRIBUTE_LAST ||
+			sizeof(*rsp) + rsp->number * 2 != pdu->params_len) {
+		*number = 0;
+		return -EPROTO;
+	}
+
+	for (i = 0; i < rsp->number; i++) {
+		attrs[i] = rsp->values[i].attr;
+		values[i] = rsp->values[i].value;
+	}
+
+	*number = rsp->number;
+
+	return 0;
+}
+
+static gboolean get_value_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	uint8_t values[AVRCP_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_value)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_value(pdu, &number, attrs, values);
+
+done:
+	player->cfm->get_value(session, err, number, attrs, values,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+							uint8_t *attrs)
+
+{
+	struct iovec iov[2];
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number;
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2,
+				get_value_rsp, session);
+}
+
+static gboolean set_value_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_value)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = 0;
+
+done:
+	player->cfm->set_value(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+					uint8_t *attrs, uint8_t *values)
+{
+	struct iovec iov[2];
+	struct attr_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		val[i].attr = attrs[i];
+		val[i].value = values[i];
+	}
+
+	iov[1].iov_base = val;
+	iov[1].iov_len = sizeof(*val) * number;
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+					AVRCP_SET_PLAYER_VALUE, iov, 2,
+					set_value_rsp, session);
+}
+
+static gboolean get_play_status_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct get_play_status_rsp *rsp;
+	uint8_t status = 0;
+	uint32_t position = 0;
+	uint32_t duration = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_play_status)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	duration = be32_to_cpu(rsp->duration);
+	position = be32_to_cpu(rsp->position);
+	status = rsp->status;
+	err = 0;
+
+done:
+	player->cfm->get_play_status(session, err, status, position, duration,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_play_status(struct avrcp *session)
+{
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_PLAY_STATUS, NULL, 0,
+				get_play_status_rsp, session);
+}
+
+static gboolean set_volume_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	struct set_volume_rsp *rsp;
+	uint8_t value = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_volume)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	value = rsp->value & 0x7f;
+	err = 0;
+
+done:
+	player->cfm->set_volume(session, err, value, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_volume(struct avrcp *session, uint8_t volume)
+{
+	struct iovec iov;
+
+	iov.iov_base = &volume;
+	iov.iov_len = sizeof(volume);
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+				AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1,
+				set_volume_rsp, session);
+}
+
+static int parse_attribute_list(uint8_t *params, uint16_t params_len,
+				uint8_t number, uint32_t *attrs, char **text)
+{
+	struct media_item *item;
+	int i;
+
+	if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EPROTO;
+
+	for (i = 0; i < number && params_len >= sizeof(*item); i++) {
+		item = (void *) params;
+
+		item->attr = be32_to_cpu(item->attr);
+		item->charset = be16_to_cpu(item->charset);
+		item->len = be16_to_cpu(item->len);
+
+		params_len -= sizeof(*item);
+		params += sizeof(*item);
+		if (item->len > params_len)
+			goto fail;
+
+		if (item->len > 0) {
+			text[i] = g_strndup(item->data, item->len);
+			attrs[i] = item->attr;
+			params_len -= item->len;
+			params += item->len;
+		} else {
+			text[i] = NULL;
+			attrs[i] = 0;
+		}
+	}
+
+	return 0;
+
+fail:
+	for (i -= 1; i >= 0; i--)
+		g_free(text[i]);
+
+	return -EPROTO;
+}
+
+static void free_attribute_list(uint8_t number, char **text)
+{
+	while(number--)
+		g_free(text[number]);
+}
+
+static int parse_elements(struct avrcp_header *pdu, uint8_t *number,
+						uint32_t *attrs, char **text)
+{
+	struct get_element_attributes_rsp *rsp;
+
+	if (pdu->params_len < sizeof(*rsp))
+		return -EPROTO;
+
+	rsp = (void *) pdu->params;
+	if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EPROTO;
+
+	*number = rsp->number;
+
+	return parse_attribute_list(pdu->params + sizeof(*rsp),
+						pdu->params_len - sizeof(*rsp),
+						*number, attrs, text);
+}
+
+static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number,
+						uint32_t *attrs, char **text)
+{
+	struct get_item_attributes_rsp *rsp;
+
+	if (pdu->params_len < sizeof(*rsp))
+		return -EPROTO;
+
+	rsp = (void *) pdu->params;
+
+	if (rsp->number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EPROTO;
+
+	*number = rsp->number;
+
+	return parse_attribute_list(pdu->params + sizeof(*rsp),
+						pdu->params_len - sizeof(*rsp),
+						*number, attrs, text);
+}
+
+static gboolean get_element_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	uint8_t number = 0;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_element_attributes)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		err = parse_status(pdu);
+		goto done;
+	}
+
+	err = parse_elements(pdu, &number, attrs, text);
+
+done:
+	player->cfm->get_element_attributes(session, err, number, attrs, text,
+							player->user_data);
+
+	if (err == 0)
+		free_attribute_list(number, text);
+
+	return FALSE;
+}
+
+int avrcp_get_element_attributes(struct avrcp *session)
+{
+	struct iovec iov;
+	struct get_element_attributes_req req;
+
+	/* This returns all attributes */
+	memset(&req, 0, sizeof(req));
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+				AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1,
+				get_element_attributes_rsp, session);
+}
+
+static gboolean set_addressed_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_addressed)
+		return FALSE;
+
+	pdu = parse_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_status(pdu);
+
+done:
+	player->cfm->set_addressed(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id)
+{
+	struct iovec iov;
+	struct set_addressed_req req;
+
+	req.id = cpu_to_be16(player_id);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+					AVRCP_SET_ADDRESSED_PLAYER, &iov, 1,
+					set_addressed_rsp, session);
+}
+
+static char *parse_folder_list(uint8_t *params, uint16_t params_len,
+								uint8_t depth)
+{
+	char **folders, *path;
+	uint8_t count;
+	size_t i;
+
+	folders = g_new0(char *, depth + 2);
+	folders[0] = g_strdup("/Filesystem");
+
+	for (i = 0, count = 1; count <= depth && i < params_len; count++) {
+		uint8_t len;
+
+		len = params[i++];
+
+		if (i + len > params_len || len == 0) {
+			g_strfreev(folders);
+			return NULL;
+		}
+
+		folders[count] = g_memdup(&params[i], len);
+		i += len;
+	}
+
+	path = g_build_pathv("/", folders);
+	g_strfreev(folders);
+
+	return path;
+}
+
+static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct set_browsed_rsp *rsp;
+	uint16_t counter = 0;
+	uint32_t items = 0;
+	char *path = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->set_browsed)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	counter = be16_to_cpu(rsp->counter);
+	items = be32_to_cpu(rsp->items);
+
+	path = parse_folder_list(rsp->data, pdu->params_len - sizeof(*rsp),
+								rsp->depth);
+	if (!path)
+		err = -EPROTO;
+
+done:
+	player->cfm->set_browsed(session, err, counter, items, path,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id)
+{
+	struct iovec iov;
+	struct set_browsed_req req;
+
+	req.id = cpu_to_be16(player_id);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER,
+					&iov, 1, set_browsed_rsp, session);
+}
+
+static gboolean get_folder_items_rsp(struct avctp *conn,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct get_folder_items_rsp *rsp;
+	uint16_t counter = 0, number = 0;
+	uint8_t *params = NULL;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_folder_items)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	counter = be16_to_cpu(rsp->counter);
+	number = be16_to_cpu(rsp->number);
+	params = rsp->data;
+
+	/* FIXME: Add proper parsing for each item type */
+
+done:
+	player->cfm->get_folder_items(session, err, counter, number, params,
+							player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+				uint32_t start, uint32_t end, uint8_t number,
+				uint32_t *attrs)
+{
+
+	struct iovec iov[2];
+	struct get_folder_items_req req;
+	int i;
+
+	req.scope = scope;
+	req.start = cpu_to_be32(start);
+	req.end = cpu_to_be32(end);
+	req.number = number;
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	if (!number)
+		return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+						iov, 1, get_folder_items_rsp,
+						session);
+
+	for (i = 0; i < number; i++)
+		attrs[i] = cpu_to_be32(attrs[i]);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number * sizeof(*attrs);
+
+	return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+					iov, 2, get_folder_items_rsp, session);
+}
+
+static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct change_path_rsp *rsp;
+	uint32_t items = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->change_path)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	items = be32_to_cpu(rsp->items);
+
+done:
+	player->cfm->change_path(session, err, items, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+							uint16_t counter)
+{
+	struct iovec iov;
+	struct change_path_req req;
+
+	req.counter = cpu_to_be16(counter);
+	req.direction = direction;
+	req.uid = cpu_to_be64(uid);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH,
+					&iov, 1, change_path_rsp, session);
+}
+
+static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	uint8_t number = 0;
+	uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->get_item_attributes)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	err = parse_items(pdu, &number, attrs, text);
+
+done:
+	player->cfm->get_item_attributes(session, err, number, attrs, text,
+							player->user_data);
+
+	if (err == 0)
+		free_attribute_list(number, text);
+
+	return FALSE;
+}
+
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+				uint64_t uid, uint16_t counter, uint8_t number,
+				uint32_t *attrs)
+{
+	struct iovec iov[2];
+	struct get_item_attributes_req req;
+	int i;
+
+	req.scope = scope;
+	req.uid = cpu_to_be64(uid);
+	req.counter = cpu_to_be16(counter);
+	req.number = number;
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	if (!number)
+		return avrcp_send_browsing_req(session,
+						AVRCP_GET_ITEM_ATTRIBUTES,
+						iov, 1, get_item_attributes_rsp,
+						session);
+
+	if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	for (i = 0; i < number; i++) {
+		if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+				attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+			return -EINVAL;
+		attrs[i] = cpu_to_be32(attrs[i]);
+	}
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number * sizeof(*attrs);
+
+	return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES,
+					iov, 2, get_item_attributes_rsp,
+					session);
+}
+
+static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->play_item)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+
+done:
+	player->cfm->play_item(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter)
+{
+	struct iovec iov;
+	struct play_item_req req;
+
+	if (scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EINVAL;
+
+	req.scope = scope;
+	req.uid = cpu_to_be64(uid);
+	req.counter = cpu_to_be16(counter);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1,
+						play_item_rsp, session);
+}
+
+static gboolean search_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	struct search_rsp *rsp;
+	uint16_t counter = 0;
+	uint32_t items = 0;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->search)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+	if (err < 0)
+		goto done;
+
+	if (pdu->params_len < sizeof(*rsp)) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	rsp = (void *) pdu->params;
+
+	counter = be16_to_cpu(rsp->counter);
+	items = be32_to_cpu(rsp->items);
+
+	err = 0;
+
+done:
+	player->cfm->search(session, err, counter, items, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_search(struct avrcp *session, const char *string)
+{
+	struct iovec iov[2];
+	struct search_req req;
+	size_t len;
+
+	if (!string)
+		return -EINVAL;
+
+	len = strnlen(string, UINT8_MAX);
+
+	req.charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+	req.len = cpu_to_be16(len);
+
+	iov[0].iov_base = &req;
+	iov[0].iov_len = sizeof(req);
+
+	iov[1].iov_base = (void *) string;
+	iov[1].iov_len = len;
+
+	return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2,
+							search_rsp, session);
+}
+
+static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->player;
+	struct avrcp_browsing_header *pdu;
+	int err;
+
+	DBG("");
+
+	if (!player || !player->cfm || !player->cfm->add_to_now_playing)
+		return FALSE;
+
+	pdu = parse_browsing_pdu(operands, operand_count);
+	if (!pdu) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	err = parse_browsing_status(pdu);
+
+done:
+	player->cfm->add_to_now_playing(session, err, player->user_data);
+
+	return FALSE;
+}
+
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter)
+{
+	struct iovec iov;
+	struct add_to_now_playing_req req;
+
+	if (scope > AVRCP_MEDIA_NOW_PLAYING)
+		return -EINVAL;
+
+	req.scope = scope;
+	req.uid = cpu_to_be64(uid);
+	req.counter = cpu_to_be16(counter);
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING,
+					&iov, 1, add_to_now_playing_rsp,
+					session);
+}
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t number, uint8_t *events)
+{
+	struct iovec iov[2];
+	struct get_capabilities_rsp rsp;
+
+	if (number > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	rsp.cap = CAP_EVENTS_SUPPORTED;
+	rsp.number = number;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	iov[1].iov_base = events;
+	iov[1].iov_len = number;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES,
+				iov, 2);
+}
+
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs)
+{
+	struct iovec iov[2];
+	struct list_attributes_rsp rsp;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	rsp.number = number;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	if (!number)
+		return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+				iov, 1);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = number;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+				iov, 2);
+}
+
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, const char **text)
+{
+	struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+	struct text_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		uint8_t len = 0;
+
+		if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+					attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+			return -EINVAL;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		val[i].attr = attrs[i];
+		val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+		val[i].len = len;
+
+		iov[i + 1].iov_base = &val[i];
+		iov[i + 1].iov_len = sizeof(val[i]);
+
+		iov[i + 2].iov_base = (void *) text[i];
+		iov[i + 2].iov_len = len;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+			iov, 1 + i * 2);
+}
+
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *values)
+{
+	struct iovec iov[2];
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	iov[1].iov_base = values;
+	iov[1].iov_len = number;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES,
+				iov, 2);
+}
+
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+				uint32_t position, uint32_t duration,
+				uint8_t status)
+{
+	struct iovec iov;
+	struct get_play_status_rsp rsp;
+
+	rsp.duration = cpu_to_be32(duration);
+	rsp.position = cpu_to_be32(position);
+	rsp.status = status;
+
+	iov.iov_base = &rsp;
+	iov.iov_len = sizeof(rsp);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS,
+				&iov, 1);
+}
+
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *values, const char **text)
+{
+	struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+	struct text_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		uint8_t len = 0;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		val[i].attr = values[i];
+		val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+		val[i].len = len;
+
+		iov[i + 1].iov_base = &val[i];
+		iov[i + 1].iov_len = sizeof(val[i]);
+
+		iov[i + 2].iov_base = (void *) text[i];
+		iov[i + 2].iov_len = len;
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT,
+				iov, 1 + i * 2);
+}
+
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, uint8_t *values)
+{
+	struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST];
+	struct attr_value val[AVRCP_ATTRIBUTE_LAST];
+	int i;
+
+	if (number > AVRCP_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &number;
+	iov[0].iov_len = sizeof(number);
+
+	for (i = 0; i < number; i++) {
+		val[i].attr = attrs[i];
+		val[i].value = values[i];
+
+		iov[i + 1].iov_base = &val[i];
+		iov[i + 1].iov_len = sizeof(val[i]);
+	}
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE,
+			iov, 1 + i);
+}
+
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction)
+{
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+			AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0);
+}
+
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t *params, size_t params_len)
+{
+	struct iovec iov;
+
+	iov.iov_base = params;
+	iov.iov_len = params_len;
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				&iov, 1);
+}
+
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t event,
+					void *data, size_t len)
+{
+	struct iovec iov[2];
+	uint16_t *player;
+	uint8_t *volume;
+
+	if (event > AVRCP_EVENT_LAST)
+		return -EINVAL;
+
+	iov[0].iov_base = &event;
+	iov[0].iov_len = sizeof(event);
+
+	switch (event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+		if (len != sizeof(uint8_t))
+			return -EINVAL;
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		if (len != sizeof(uint8_t))
+			return -EINVAL;
+		volume = data;
+		if (volume[0] > 127)
+			return -EINVAL;
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		if (len != sizeof(uint64_t))
+			return -EINVAL;
+
+		put_be64(*(uint64_t *) data, data);
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		if (len != sizeof(uint32_t))
+			return -EINVAL;
+
+		put_be32(*(uint32_t *) data, data);
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		if (len != 4)
+			return -EINVAL;
+
+		player = data;
+		player[0] = cpu_to_be16(player[0]);
+		player[1] = cpu_to_be16(player[1]);
+
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		if (len < sizeof(uint8_t))
+			return -EINVAL;
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		if (len != sizeof(uint16_t))
+			return -EINVAL;
+
+		put_be16(*(uint16_t *) data, data);
+		break;
+	default:
+		return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL,
+					AVRCP_REGISTER_NOTIFICATION, iov, 1);
+	}
+
+	iov[1].iov_base = data;
+	iov[1].iov_len = len;
+
+	return avrcp_send(session, transaction, code, AVC_SUBUNIT_PANEL,
+					AVRCP_REGISTER_NOTIFICATION, iov, 2);
+}
+
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t volume)
+{
+	struct iovec iov;
+
+	if (volume > 127)
+		return -EINVAL;
+
+	iov.iov_base = &volume;
+	iov.iov_len = sizeof(volume);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME,
+				&iov, 1);
+}
+
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t status)
+{
+	struct iovec iov;
+
+	iov.iov_base = &status;
+	iov.iov_len = sizeof(status);
+
+	return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+				AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER,
+				&iov, 1);
+}
+
+static int avrcp_status_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t pdu_id, uint8_t status)
+{
+	struct iovec iov;
+
+	if (status > AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED)
+		return -EINVAL;
+
+	iov.iov_base = &status;
+	iov.iov_len = sizeof(status);
+
+	return avrcp_send_browsing(session, transaction, pdu_id, &iov, 1);
+}
+
+int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint32_t items, uint8_t depth,
+					const char **folders)
+{
+	struct iovec iov[UINT8_MAX * 2 + 1];
+	struct set_browsed_rsp rsp;
+	uint16_t len[UINT8_MAX];
+	int i;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction,
+					AVRCP_SET_BROWSED_PLAYER, status);
+
+	rsp.status = status;
+	rsp.counter = cpu_to_be16(counter);
+	rsp.items = cpu_to_be32(items);
+	rsp.charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+	rsp.depth = depth;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	if (!depth)
+		return avrcp_send_browsing(session, transaction,
+						AVRCP_SET_BROWSED_PLAYER,
+						iov, 1);
+
+	for (i = 0; i < depth; i++) {
+		if (!folders[i])
+			return -EINVAL;
+
+		len[i] = strlen(folders[i]);
+
+		iov[i * 2 + 2].iov_base = (void *) folders[i];
+		iov[i * 2 + 2].iov_len = len[i];
+
+		len[i] = cpu_to_be16(len[i]);
+
+		iov[i * 2 + 1].iov_base = &len[i];
+		iov[i * 2 + 1].iov_len = sizeof(len[i]);
+	}
+
+	return avrcp_send_browsing(session, transaction,
+					AVRCP_SET_BROWSED_PLAYER, iov,
+					depth * 2 + 1);
+}
+
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint8_t number, uint8_t *type,
+					uint16_t *len, uint8_t **params)
+{
+	struct iovec iov[UINT8_MAX * 2 + 1];
+	struct get_folder_items_rsp rsp;
+	uint8_t item[UINT8_MAX][3];
+	int i;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction,
+					AVRCP_GET_FOLDER_ITEMS, status);
+
+	rsp.status = status;
+	rsp.counter = cpu_to_be16(counter);
+	rsp.number = cpu_to_be16(number);
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	for (i = 0; i < number; i++) {
+		item[i][0] = type[i];
+		put_be16(len[i], &item[i][1]);
+
+		iov[i * 2 + 1].iov_base = item[i];
+		iov[i * 2 + 1].iov_len = sizeof(item[i]);
+
+		iov[i * 2 + 2].iov_base = params[i];
+		iov[i * 2 + 2].iov_len = len[i];
+	}
+
+	return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS,
+							iov, number * 2 + 1);
+}
+
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t status, uint32_t items)
+{
+	struct iovec iov;
+	struct change_path_rsp rsp;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction, AVRCP_CHANGE_PATH,
+									status);
+
+	rsp.status = status;
+	rsp.items = cpu_to_be32(items);
+
+	iov.iov_base = &rsp;
+	iov.iov_len = sizeof(rsp);
+
+	return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH,
+								&iov, 1);
+}
+
+static bool pack_attribute_list(struct iovec *iov, uint8_t number,
+					uint32_t *attrs, const char **text)
+{
+	int i;
+	struct media_item val[AVRCP_MEDIA_ATTRIBUTE_LAST];
+
+	for (i = 0; i < number; i++) {
+		uint16_t len = 0;
+
+		if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+				attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+			return false;
+
+		if (text[i])
+			len = strlen(text[i]);
+
+		val[i].attr = cpu_to_be32(attrs[i]);
+		val[i].charset = cpu_to_be16(AVRCP_CHARSET_UTF8);
+		val[i].len = cpu_to_be16(len);
+
+		iov[i].iov_base = &val[i];
+		iov[i].iov_len = sizeof(val[i]);
+
+		iov[i + 1].iov_base = (void *) text[i];
+		iov[i + 1].iov_len = len;
+	}
+
+	return true;
+}
+
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint8_t number,
+					uint32_t *attrs, const char **text)
+{
+	struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1];
+	struct get_item_attributes_rsp rsp;
+
+	if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+		return -EINVAL;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction,
+					AVRCP_GET_ITEM_ATTRIBUTES, status);
+
+	rsp.status = status;
+	rsp.number = number;
+
+	iov[0].iov_base = &rsp;
+	iov[0].iov_len = sizeof(rsp);
+
+	if (!pack_attribute_list(&iov[1], number, attrs, text))
+		return -EINVAL;
+
+	return avrcp_send_browsing(session, transaction,
+					AVRCP_GET_ITEM_ATTRIBUTES, iov,
+					number * 2 + 1);
+}
+
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction,
+								uint8_t status)
+{
+	return avrcp_status_rsp(session, transaction, AVRCP_PLAY_ITEM,
+								status);
+}
+
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status,
+					uint16_t counter, uint32_t items)
+{
+	struct iovec iov;
+	struct search_rsp rsp;
+
+	if (status != AVRCP_STATUS_SUCCESS)
+		return avrcp_status_rsp(session, transaction, AVRCP_SEARCH,
+								status);
+
+	rsp.status = status;
+	rsp.counter = cpu_to_be16(counter);
+	rsp.items = cpu_to_be32(items);
+
+	iov.iov_base = &rsp;
+	iov.iov_len = sizeof(rsp);
+
+	return avrcp_send_browsing(session, transaction, AVRCP_SEARCH,
+								&iov, 1);
+}
+
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction,
+								uint8_t status)
+{
+	return avrcp_status_rsp(session, transaction, AVRCP_ADD_TO_NOW_PLAYING,
+								status);
+}
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op)
+{
+	uint8_t params[5];
+
+	if (!vendor)
+		return avctp_send_passthrough(session->conn, op, NULL, 0);
+
+	hton24(params, vendor);
+	put_be16(op, &params[3]);
+
+	return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params,
+								sizeof(params));
+}
diff --git a/bluez/repo/android/avrcp-lib.h b/bluez/repo/android/avrcp-lib.h
new file mode 100644
index 0000000..6554b12
--- /dev/null
+++ b/bluez/repo/android/avrcp-lib.h
@@ -0,0 +1,356 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+/* Control PDU ids */
+#define AVRCP_GET_CAPABILITIES		0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
+#define AVRCP_SET_PLAYER_VALUE		0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
+#define AVRCP_DISPLAYABLE_CHARSET	0x17
+#define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+#define AVRCP_REQUEST_CONTINUING	0x40
+#define AVRCP_ABORT_CONTINUING		0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_ADDRESSED_PLAYER	0x60
+#define AVRCP_SET_BROWSED_PLAYER	0x70
+#define AVRCP_GET_FOLDER_ITEMS		0x71
+#define AVRCP_CHANGE_PATH		0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES	0x73
+#define AVRCP_PLAY_ITEM			0x74
+#define AVRCP_SEARCH			0x80
+#define AVRCP_ADD_TO_NOW_PLAYING	0x90
+#define AVRCP_GENERAL_REJECT		0xA0
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED		0x02
+#define AVRCP_EVENT_TRACK_REACHED_END		0x03
+#define AVRCP_EVENT_TRACK_REACHED_START		0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED	0x05
+#define AVRCP_EVENT_SETTINGS_CHANGED		0x08
+#define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED	0x09
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED	0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED	0x0b
+#define AVRCP_EVENT_UIDS_CHANGED		0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED		0x0d
+#define AVRCP_EVENT_LAST			AVRCP_EVENT_VOLUME_CHANGED
+
+/* Status codes */
+#define AVRCP_STATUS_INVALID_COMMAND		0x00
+#define AVRCP_STATUS_INVALID_PARAM		0x01
+#define AVRCP_STATUS_PARAM_NOT_FOUND		0x02
+#define AVRCP_STATUS_INTERNAL_ERROR		0x03
+#define AVRCP_STATUS_SUCCESS			0x04
+#define AVRCP_STATUS_UID_CHANGED		0x05
+#define AVRCP_STATUS_NOT_DIRECTORY		0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST		0x09
+#define AVRCP_STATUS_INVALID_SCOPE		0x0a
+#define AVRCP_STATUS_OUT_OF_BOUNDS		0x0b
+#define AVRCP_STATUS_INVALID_PLAYER_ID		0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE	0x12
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS	0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED	0x16
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID				0x02
+#define CAP_EVENTS_SUPPORTED			0x03
+
+/* Player Attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL			0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER		0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE		0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE			0x03
+#define AVRCP_ATTRIBUTE_SCAN			0x04
+#define AVRCP_ATTRIBUTE_LAST			AVRCP_ATTRIBUTE_SCAN
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_OFF		0x01
+#define AVRCP_EQUALIZER_ON		0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF		0x01
+#define AVRCP_REPEAT_MODE_SINGLE	0x02
+#define AVRCP_REPEAT_MODE_ALL		0x03
+#define AVRCP_REPEAT_MODE_GROUP		0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF		0x01
+#define AVRCP_SHUFFLE_ALL		0x02
+#define AVRCP_SHUFFLE_GROUP		0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF			0x01
+#define AVRCP_SCAN_ALL			0x02
+#define AVRCP_SCAN_GROUP		0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL	0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE	0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST	0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM	0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK	0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS	0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE	0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION	0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST	AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* Media Scope */
+#define AVRCP_MEDIA_PLAYER_LIST			0x00
+#define AVRCP_MEDIA_PLAYER_VFS			0x01
+#define AVRCP_MEDIA_SEARCH			0x02
+#define AVRCP_MEDIA_NOW_PLAYING			0x03
+
+/* SDP features */
+#define AVRCP_FEATURE_CATEGORY_1	0x0001
+#define AVRCP_FEATURE_CATEGORY_2	0x0002
+#define AVRCP_FEATURE_CATEGORY_3	0x0004
+#define AVRCP_FEATURE_CATEGORY_4	0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS	0x0010
+#define AVRCP_FEATURE_GROUP_NAVIGATION	0x0020
+#define AVRCP_FEATURE_BROWSING		0x0040
+#define AVRCP_FEATURE_MULTIPLE_PLAYERS	0x0080
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG		0x001958
+
+struct avrcp;
+
+struct avrcp_control_ind {
+	int (*get_capabilities) (struct avrcp *session, uint8_t transaction,
+							void *user_data);
+	int (*list_attributes) (struct avrcp *session, uint8_t transaction,
+							void *user_data);
+	int (*get_attribute_text) (struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					void *user_data);
+	int (*list_values) (struct avrcp *session, uint8_t transaction,
+					uint8_t attr, void *user_data);
+	int (*get_value_text) (struct avrcp *session, uint8_t transaction,
+					uint8_t attr, uint8_t number,
+					uint8_t *values, void *user_data);
+	int (*get_value) (struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					void *user_data);
+	int (*set_value) (struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					uint8_t *values, void *user_data);
+	int (*get_play_status) (struct avrcp *session, uint8_t transaction,
+					void *user_data);
+	int (*get_element_attributes) (struct avrcp *session,
+					uint8_t transaction, uint64_t uid,
+					uint8_t number, uint32_t *attrs,
+					void *user_data);
+	int (*register_notification) (struct avrcp *session,
+					uint8_t transaction, uint8_t event,
+					uint32_t interval, void *user_data);
+	int (*set_volume) (struct avrcp *session, uint8_t transaction,
+					uint8_t volume, void *user_data);
+	int (*set_addressed) (struct avrcp *session, uint8_t transaction,
+					uint16_t id, void *user_data);
+	int (*set_browsed) (struct avrcp *session, uint8_t transaction,
+					uint16_t id, void *user_data);
+	int (*get_folder_items) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint32_t start,
+					uint32_t end, uint16_t number,
+					uint32_t *attrs, void *user_data);
+	int (*change_path) (struct avrcp *session, uint8_t transaction,
+					uint16_t counter, uint8_t direction,
+					uint64_t uid, void *user_data);
+	int (*get_item_attributes) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, uint8_t number,
+					uint32_t *attrs, void *user_data);
+	int (*play_item) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, void *user_data);
+	int (*search) (struct avrcp *session, uint8_t transaction,
+					const char *string, void *user_data);
+	int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, void *user_data);
+};
+
+struct avrcp_control_cfm {
+	void (*get_capabilities) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *params,
+					void *user_data);
+	void (*list_attributes) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *attrs,
+					void *user_data);
+	void (*get_attribute_text) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *attrs,
+					char **text, void *user_data);
+	void (*list_values) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *values,
+					void *user_data);
+	void (*get_value_text) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *values,
+					char **text, void *user_data);
+	void (*get_value) (struct avrcp *session, int err,
+					uint8_t number, uint8_t *attrs,
+					uint8_t *values, void *user_data);
+	void (*set_value) (struct avrcp *session, int err, void *user_data);
+	void (*get_play_status) (struct avrcp *session, int err,
+					uint8_t status, uint32_t position,
+					uint32_t duration, void *user_data);
+	void (*get_element_attributes) (struct avrcp *session, int err,
+					uint8_t number, uint32_t *attrs,
+					char **text, void *user_data);
+	bool (*register_notification) (struct avrcp *session, int err,
+					uint8_t code, uint8_t event,
+					void *params, void *user_data);
+	void (*set_volume) (struct avrcp *session, int err, uint8_t volume,
+					void *user_data);
+	void (*set_addressed) (struct avrcp *session, int err,
+					void *user_data);
+	void (*set_browsed) (struct avrcp *session, int err,
+					uint16_t counter, uint32_t items,
+					char *path, void *user_data);
+	void (*get_folder_items) (struct avrcp *session, int err,
+					uint16_t counter, uint16_t number,
+					uint8_t *params, void *user_data);
+	void (*change_path) (struct avrcp *session, int err,
+					uint32_t items, void *user_data);
+	void (*get_item_attributes) (struct avrcp *session, int err,
+					uint8_t number, uint32_t *attrs,
+					char **text, void *user_data);
+	void (*play_item) (struct avrcp *session, int err, void *user_data);
+	void (*search) (struct avrcp *session, int err, uint16_t counter,
+					uint32_t items, void *user_data);
+	void (*add_to_now_playing) (struct avrcp *session, int err,
+					void *user_data);
+};
+
+struct avrcp_passthrough_handler {
+	uint8_t op;
+	bool (*func) (struct avrcp *session, bool pressed, void *user_data);
+};
+
+typedef void (*avrcp_destroy_cb_t) (void *user_data);
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avrcp_shutdown(struct avrcp *session);
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+							void *user_data);
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+								size_t omtu);
+
+void avrcp_register_player(struct avrcp *session,
+				const struct avrcp_control_ind *ind,
+				const struct avrcp_control_cfm *cfm,
+				void *user_data);
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+			const struct avrcp_passthrough_handler *handlers,
+			void *user_data);
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+							const char *address);
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+					uint8_t subunit, uint8_t pdu_id,
+					const struct iovec *iov, int iov_cnt);
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param);
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+							uint32_t interval);
+int avrcp_list_player_attributes(struct avrcp *session);
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+							uint8_t *attrs);
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr);
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+					uint8_t number, uint8_t *values);
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+					uint8_t *attrs, uint8_t *values);
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+							uint8_t *attrs);
+int avrcp_get_play_status(struct avrcp *session);
+int avrcp_set_volume(struct avrcp *session, uint8_t volume);
+int avrcp_get_element_attributes(struct avrcp *session);
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+				uint32_t start, uint32_t end, uint8_t number,
+				uint32_t *attrs);
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+							uint16_t counter);
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+				uint64_t uid, uint16_t counter, uint8_t number,
+				uint32_t *attrs);
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter);
+int avrcp_search(struct avrcp *session, const char *string);
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+							uint16_t counter);
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *events);
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs);
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, const char **text);
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *values);
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+				uint32_t position, uint32_t duration,
+				uint8_t status);
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *values, const char **text);
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+					uint8_t transaction, uint8_t number,
+					uint8_t *attrs, uint8_t *values);
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction);
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t *params, size_t params_len);
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t code, uint8_t event,
+					void *data, size_t len);
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t volume);
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+							uint8_t status);
+int avrcp_set_browsed_player_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint32_t items, uint8_t depth,
+					const char **folders);
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint16_t counter,
+					uint8_t number, uint8_t *type,
+					uint16_t *len, uint8_t **params);
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+						uint8_t status, uint32_t items);
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status, uint8_t number,
+					uint32_t *attrs, const char **text);
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction,
+					uint8_t status);
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, uint8_t status,
+					uint16_t counter, uint32_t items);
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction,
+								uint8_t status);
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op);
diff --git a/bluez/repo/android/avrcp.c b/bluez/repo/android/avrcp.c
new file mode 100644
index 0000000..8c3e25a
--- /dev/null
+++ b/bluez/repo/android/avrcp.c
@@ -0,0 +1,1169 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "avrcp.h"
+#include "utils.h"
+
+#define L2CAP_PSM_AVCTP 0x17
+
+static bdaddr_t adapter_addr;
+static uint32_t record_tg_id = 0;
+static uint32_t record_ct_id = 0;
+static GSList *devices = NULL;
+static GIOChannel *server = NULL;
+static struct ipc *hal_ipc = NULL;
+
+struct avrcp_request {
+	struct avrcp_device *dev;
+	uint8_t pdu_id;
+	uint8_t event_id;
+	uint8_t transaction;
+};
+
+struct avrcp_device {
+	bdaddr_t	dst;
+	uint16_t	version;
+	uint16_t	features;
+	struct avrcp	*session;
+	GIOChannel	*io;
+	GQueue		*queue;
+};
+
+static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id,
+								bool peek)
+{
+	GSList *l;
+
+	for (l = devices; l; l = g_slist_next(l)) {
+		struct avrcp_device *dev = l->data;
+		GList *reqs = g_queue_peek_head_link(dev->queue);
+		int i;
+
+		for (i = 0; reqs; reqs = g_list_next(reqs), i++) {
+			struct avrcp_request *req = reqs->data;
+
+			if (req->pdu_id != pdu_id || req->event_id != event_id)
+				continue;
+
+			if (!peek)
+				g_queue_pop_nth(dev->queue, i);
+
+			return req;
+		}
+	}
+
+	return NULL;
+}
+
+static void handle_get_play_status(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_avrcp_get_play_status *cmd = buf;
+	uint8_t status;
+	struct avrcp_request *req;
+	int ret;
+
+	DBG("");
+
+	req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false);
+	if (!req) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction,
+					cmd->position, cmd->duration,
+					cmd->status);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		g_free(req);
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	g_free(req);
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+				HAL_OP_AVRCP_GET_PLAY_STATUS, status);
+}
+
+static void handle_list_player_attrs(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_list_player_values(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs_text(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_values_text(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED);
+}
+
+static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text,
+						uint8_t *pdu)
+{
+	uint16_t charset = 106;
+	size_t len = 0;
+
+	put_be32(id, pdu);
+	pdu += 4;
+	len += 4;
+
+	put_be16(charset, pdu);
+	pdu += 2;
+	len += 2;
+
+	put_be16(text_len, pdu);
+	pdu += 2;
+	len += 2;
+
+	memcpy(pdu, text, text_len);
+	len += text_len;
+
+	return len;
+}
+
+static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu,
+								size_t *len)
+{
+	int i;
+
+	*pdu = number;
+	pdu++;
+	*len += 1;
+
+	for (i = 0; i < number; i++) {
+		struct hal_avrcp_player_setting_text *text = (void *) ptr;
+		size_t ret;
+
+		ret = write_element_text(text->id, text->len, text->text, pdu);
+
+		ptr += sizeof(*text) + text->len;
+		pdu += ret;
+		*len += ret;
+	}
+}
+
+static void handle_get_element_attrs_text(const void *buf, uint16_t len)
+{
+	struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+	uint8_t status;
+	struct avrcp_request *req;
+	uint8_t pdu[IPC_MTU];
+	uint8_t *ptr;
+	size_t pdu_len;
+	int ret;
+
+	DBG("");
+
+	req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false);
+	if (!req) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ptr = (uint8_t *) &cmd->values[0];
+	pdu_len = 0;
+	write_element_attrs(ptr, cmd->number, pdu, &pdu_len);
+
+	ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction,
+								pdu, pdu_len);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		g_free(req);
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	g_free(req);
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status);
+}
+
+static void handle_set_player_attrs_value(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED);
+}
+
+static void handle_register_notification(const void *buf, uint16_t len)
+{
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	uint8_t status;
+	struct avrcp_request *req;
+	uint8_t code;
+	bool peek = false;
+	int ret;
+
+	DBG("");
+
+	switch (cmd->type) {
+	case HAL_AVRCP_EVENT_TYPE_INTERIM:
+		code = AVC_CTYPE_INTERIM;
+		peek = true;
+		break;
+	case HAL_AVRCP_EVENT_TYPE_CHANGED:
+		code = AVC_CTYPE_CHANGED;
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek);
+	if (!req) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	ret = avrcp_register_notification_rsp(req->dev->session,
+						req->transaction, code,
+						cmd->event, cmd->data,
+						cmd->len);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		if (!peek)
+			g_free(req);
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	if (!peek)
+		g_free(req);
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_OP_AVRCP_REGISTER_NOTIFICATION, status);
+}
+
+static void handle_set_volume(const void *buf, uint16_t len)
+{
+	struct hal_cmd_avrcp_set_volume *cmd = (void *) buf;
+	struct avrcp_device *dev;
+	uint8_t status;
+	int ret;
+
+	DBG("");
+
+	if (!devices) {
+		error("AVRCP: No device found to set volume");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/*
+	 * Peek the first device since the HAL cannot really address a specific
+	 * device it might mean there could only be one connected.
+	 */
+	dev = devices->data;
+
+	ret = avrcp_set_volume(dev->session, cmd->value & 0x7f);
+	if (ret < 0) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+								status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_AVRCP_GET_PLAY_STATUS */
+	{ handle_get_play_status, false,
+			sizeof(struct hal_cmd_avrcp_get_play_status) },
+	/* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */
+	{ handle_list_player_attrs, true,
+			sizeof(struct hal_cmd_avrcp_list_player_attrs) },
+	/* HAL_OP_AVRCP_LIST_PLAYER_VALUES */
+	{ handle_list_player_values, true,
+			sizeof(struct hal_cmd_avrcp_list_player_values) },
+	/* HAL_OP_AVRCP_GET_PLAYER_ATTRS */
+	{ handle_get_player_attrs, true,
+			sizeof(struct hal_cmd_avrcp_get_player_attrs) },
+	/* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */
+	{ handle_get_player_attrs_text, true,
+			sizeof(struct hal_cmd_avrcp_get_player_attrs_text) },
+	/* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */
+	{ handle_get_player_values_text, true,
+			sizeof(struct hal_cmd_avrcp_get_player_values_text) },
+	/* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */
+	{ handle_get_element_attrs_text, true,
+			sizeof(struct hal_cmd_avrcp_get_element_attrs_text) },
+	/* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */
+	{ handle_set_player_attrs_value, true,
+			sizeof(struct hal_cmd_avrcp_set_player_attrs_value) },
+	/* HAL_OP_AVRCP_REGISTER_NOTIFICATION */
+	{ handle_register_notification, true,
+			sizeof(struct hal_cmd_avrcp_register_notification) },
+	/* HAL_OP_AVRCP_SET_VOLUME */
+	{ handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) },
+};
+
+static sdp_record_t *avrcp_tg_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrtg;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto_control, *proto_control[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = L2CAP_PSM_AVCTP;
+	uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+	uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 |
+					AVRCP_FEATURE_CATEGORY_2 |
+					AVRCP_FEATURE_CATEGORY_3 |
+					AVRCP_FEATURE_CATEGORY_4);
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &avrtg);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto_control[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto_control[0] = sdp_list_append(proto_control[0], psm);
+	apseq = sdp_list_append(NULL, proto_control[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto_control[1] = sdp_list_append(NULL, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto_control[1] = sdp_list_append(proto_control[1], version);
+	apseq = sdp_list_append(apseq, proto_control[1]);
+
+	aproto_control = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto_control);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = avrcp_ver;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
+
+	sdp_data_free(psm);
+	sdp_data_free(version);
+	sdp_list_free(proto_control[0], NULL);
+	sdp_list_free(proto_control[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto_control, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static sdp_record_t *avrcp_ct_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrct, avrctr;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVCTP_CONTROL_PSM;
+	uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+						AVRCP_FEATURE_CATEGORY_2 |
+						AVRCP_FEATURE_CATEGORY_3 |
+						AVRCP_FEATURE_CATEGORY_4);
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &avrct);
+	sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &avrctr);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(NULL, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = avrcp_ver;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP CT", NULL, NULL);
+
+	free(psm);
+	free(version);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static void avrcp_device_free(void *data)
+{
+	struct avrcp_device *dev = data;
+
+	if (dev->queue) {
+		g_queue_foreach(dev->queue, (GFunc) g_free, NULL);
+		g_queue_free(dev->queue);
+	}
+
+	if (dev->session)
+		avrcp_shutdown(dev->session);
+
+	if (dev->io) {
+		g_io_channel_shutdown(dev->io, FALSE, NULL);
+		g_io_channel_unref(dev->io);
+	}
+
+	g_free(dev);
+}
+
+static void avrcp_device_remove(struct avrcp_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+	avrcp_device_free(dev);
+}
+
+static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst)
+{
+	struct avrcp_device *dev;
+
+	dev = g_new0(struct avrcp_device, 1);
+	bacpy(&dev->dst, dst);
+	devices = g_slist_prepend(devices, dev);
+
+	return dev;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct avrcp_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(devices, dst, device_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static void disconnect_cb(void *data)
+{
+	struct avrcp_device *dev = data;
+
+	DBG("");
+
+	dev->session = NULL;
+
+	avrcp_device_remove(dev);
+}
+
+static bool handle_fast_forward(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	struct hal_ev_avrcp_passthrough_cmd ev;
+
+	DBG("pressed %s", pressed ? "true" : "false");
+
+	ev.id = AVC_FAST_FORWARD;
+	ev.state = pressed;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+	return true;
+}
+
+static bool handle_rewind(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	struct hal_ev_avrcp_passthrough_cmd ev;
+
+	DBG("pressed %s", pressed ? "true" : "false");
+
+	ev.id = AVC_REWIND;
+	ev.state = pressed;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+			HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+	return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+		{ AVC_FAST_FORWARD, handle_fast_forward },
+		{ AVC_REWIND, handle_rewind },
+		{ },
+};
+
+static int handle_get_capabilities_cmd(struct avrcp *session,
+					uint8_t transaction, void *user_data)
+{
+	uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED,
+					AVRCP_EVENT_TRACK_CHANGED,
+					AVRCP_EVENT_PLAYBACK_POS_CHANGED  };
+
+	DBG("");
+
+	/*
+	 * Android do not provide this info via HAL so the list most
+	 * be hardcoded according to what RegisterNotification can
+	 * actually handle
+	 */
+	avrcp_get_capabilities_rsp(session, transaction, sizeof(events),
+								events);
+
+	return 0;
+}
+
+static void push_request(struct avrcp_device *dev, uint8_t pdu_id,
+					uint8_t event_id, uint8_t transaction)
+{
+	struct avrcp_request *req;
+
+	req = g_new0(struct avrcp_request, 1);
+	req->dev = dev;
+	req->pdu_id = pdu_id;
+	req->event_id = event_id;
+	req->transaction = transaction;
+
+	g_queue_push_tail(dev->queue, req);
+}
+
+static int handle_get_play_status_cmd(struct avrcp *session,
+					uint8_t transaction, void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+
+	DBG("");
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL);
+
+	push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction);
+
+	return 0;
+}
+
+static int handle_get_element_attrs_cmd(struct avrcp *session,
+					uint8_t transaction, uint64_t uid,
+					uint8_t number, uint32_t *attrs,
+					void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf;
+	int i;
+
+	DBG("");
+
+	ev->number = number;
+	/* Set everything in case of empty list */
+	if (ev->number == 0) {
+		for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) {
+			/* Skip 0x00 as the attributes start with 0x01 */
+			ev->attrs[i] = i + 1;
+		}
+		ev->number = i;
+		goto done;
+	}
+
+	for (i = 0; i < number; i++)
+		ev->attrs[i] = attrs[i];
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_GET_ELEMENT_ATTRS,
+					sizeof(*ev) + ev->number, ev);
+
+	push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction);
+
+	return 0;
+
+}
+
+static int handle_register_notification_cmd(struct avrcp *session,
+						uint8_t transaction,
+						uint8_t event,
+						uint32_t interval,
+						void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	struct hal_ev_avrcp_register_notification ev;
+
+	DBG("");
+
+	/* TODO: Add any missing events supported by Android */
+	switch (event) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+	case AVRCP_EVENT_TRACK_CHANGED:
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ev.event = event;
+	ev.param = interval;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_REGISTER_NOTIFICATION,
+					sizeof(ev), &ev);
+
+	push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction);
+
+	return 0;
+}
+
+static const struct avrcp_control_ind control_ind = {
+	.get_capabilities = handle_get_capabilities_cmd,
+	.get_play_status = handle_get_play_status_cmd,
+	.get_element_attributes = handle_get_element_attrs_cmd,
+	.register_notification = handle_register_notification_cmd,
+};
+
+static bool handle_register_notification_rsp(struct avrcp *session, int err,
+						uint8_t code, uint8_t event,
+						void *params,
+						void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	struct hal_ev_avrcp_volume_changed ev;
+	uint8_t *volume = params;
+
+	if (err < 0) {
+		error("AVRCP: %s", strerror(-err));
+		return false;
+	}
+
+	if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+		return false;
+
+	if (event != AVRCP_EVENT_VOLUME_CHANGED)
+		return false;
+
+	ev.type = code;
+	ev.volume = volume[0];
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_VOLUME_CHANGED,
+					sizeof(ev), &ev);
+
+	if (code == AVC_CTYPE_INTERIM)
+		return true;
+
+	avrcp_register_notification(dev->session, event, 0);
+	return false;
+}
+
+static void handle_get_capabilities_rsp(struct avrcp *session, int err,
+					uint8_t number, uint8_t *events,
+					void *user_data)
+{
+	struct avrcp_device *dev = user_data;
+	int i;
+
+	if (err < 0) {
+		error("AVRCP: %s", strerror(-err));
+		return;
+	}
+
+	for (i = 0; i < number; i++) {
+		if (events[i] != AVRCP_EVENT_VOLUME_CHANGED)
+			continue;
+
+		avrcp_register_notification(dev->session, events[i], 0);
+		break;
+	}
+
+	return;
+}
+
+static void handle_set_volume_rsp(struct avrcp *session, int err,
+						uint8_t value, void *user_data)
+{
+	struct hal_ev_avrcp_volume_changed ev;
+
+	if (err < 0) {
+		ev.volume = 0;
+		ev.type = AVC_CTYPE_REJECTED;
+		goto done;
+	}
+
+	ev.volume = value;
+	ev.type = AVC_CTYPE_ACCEPTED;
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_VOLUME_CHANGED,
+					sizeof(ev), &ev);
+}
+
+static const struct avrcp_control_cfm control_cfm = {
+	.get_capabilities = handle_get_capabilities_rsp,
+	.register_notification = handle_register_notification_rsp,
+	.set_volume = handle_set_volume_rsp,
+};
+
+static int avrcp_device_add_session(struct avrcp_device *dev, int fd,
+						uint16_t imtu, uint16_t omtu)
+{
+	struct hal_ev_avrcp_remote_features ev;
+	char address[18];
+
+	dev->session = avrcp_new(fd, imtu, omtu, dev->version);
+	if (!dev->session)
+		return -EINVAL;
+
+	avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
+	avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
+									dev);
+	avrcp_register_player(dev->session, &control_ind, &control_cfm, dev);
+
+	dev->queue = g_queue_new();
+
+	ba2str(&dev->dst, address);
+
+	/* FIXME: get the real name of the device */
+	avrcp_init_uinput(dev->session, "bluetooth", address);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.features = HAL_AVRCP_FEATURE_NONE;
+
+	DBG("version 0x%02x", dev->version);
+
+	if (dev->version < 0x0103)
+		goto done;
+
+	ev.features |= HAL_AVRCP_FEATURE_METADATA;
+
+	if (dev->version < 0x0104)
+		goto done;
+
+	ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME;
+
+	avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED);
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+					HAL_EV_AVRCP_REMOTE_FEATURES,
+					sizeof(ev), &ev);
+
+	return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct avrcp_device *dev = user_data;
+	uint16_t imtu, omtu;
+	char address[18];
+	GError *gerr = NULL;
+	int fd;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	fd = g_io_channel_unix_get_fd(chan);
+	if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) {
+		avrcp_device_free(dev);
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	DBG("%s connected", address);
+}
+
+static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb)
+{
+	GError *err = NULL;
+
+	dev->io = bt_io_connect(cb, dev, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	return true;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct avrcp_device *dev = data;
+	sdp_list_t *list;
+
+	DBG("");
+
+	if (!g_slist_find(devices, dev))
+		return;
+
+	if (err < 0) {
+		error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s",
+							strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("No AVRCP records found");
+		goto fail;
+	}
+
+	for (list = recs; list; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_list_t *l;
+		sdp_profile_desc_t *desc;
+		int features;
+
+		if (sdp_get_profile_descs(rec, &l) < 0)
+			continue;
+
+		desc = l->data;
+		dev->version = desc->version;
+
+		if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES,
+							&features) == 0)
+			dev->features = features;
+
+		sdp_list_free(l, free);
+		break;
+	}
+
+	if (dev->io) {
+		GError *gerr = NULL;
+		if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) {
+			error("bt_io_accept: %s", gerr->message);
+			g_error_free(gerr);
+			goto fail;
+		}
+		return;
+	}
+
+	if (!avrcp_device_connect(dev, connect_cb)) {
+		error("Unable to connect to AVRCP");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	avrcp_device_remove(dev);
+}
+
+static int avrcp_device_search(struct avrcp_device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb,
+								dev, NULL, 0);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct avrcp_device *dev;
+	char address[18];
+	bdaddr_t dst;
+	GError *err = NULL;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	DBG("incoming connect from %s", address);
+
+	dev = avrcp_device_find(&dst);
+	if (dev && dev->session) {
+		error("AVRCP: Refusing unexpected connect");
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	dev = avrcp_device_new(&dst);
+	if (avrcp_device_search(dev) < 0) {
+		error("AVRCP: Failed to search SDP details");
+		avrcp_device_free(dev);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+	}
+
+	dev->io = g_io_channel_ref(chan);
+}
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+	sdp_record_t *rec;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_INVALID);
+	if (!server) {
+		error("Failed to listen on AVDTP channel: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = avrcp_tg_record();
+	if (!rec) {
+		error("Failed to allocate AVRCP TG record");
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register AVRCP TG record");
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_tg_id = rec->handle;
+
+	rec = avrcp_ct_record();
+	if (!rec) {
+		error("Failed to allocate AVRCP CT record");
+		bt_adapter_remove_record(record_tg_id);
+		goto fail;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register AVRCP CT record");
+		bt_adapter_remove_record(record_tg_id);
+		sdp_record_free(rec);
+		goto fail;
+	}
+	record_ct_id = rec->handle;
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+fail:
+	g_io_channel_shutdown(server, TRUE, NULL);
+	g_io_channel_unref(server);
+	server = NULL;
+
+	return false;
+}
+
+void bt_avrcp_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(devices, avrcp_device_free);
+	devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP);
+	hal_ipc = NULL;
+
+	bt_adapter_remove_record(record_tg_id);
+	record_tg_id = 0;
+
+	bt_adapter_remove_record(record_ct_id);
+	record_ct_id = 0;
+
+	if (server) {
+		g_io_channel_shutdown(server, TRUE, NULL);
+		g_io_channel_unref(server);
+		server = NULL;
+	}
+}
+
+void bt_avrcp_connect(const bdaddr_t *dst)
+{
+	struct avrcp_device *dev;
+	char addr[18];
+
+	DBG("");
+
+	if (avrcp_device_find(dst))
+		return;
+
+	dev = avrcp_device_new(dst);
+	if (avrcp_device_search(dev) < 0) {
+		error("AVRCP: Failed to search SDP details");
+		avrcp_device_free(dev);
+	}
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+}
+
+void bt_avrcp_disconnect(const bdaddr_t *dst)
+{
+	struct avrcp_device *dev;
+
+	DBG("");
+
+	dev = avrcp_device_find(dst);
+	if (!dev)
+		return;
+
+	if (dev->session) {
+		avrcp_shutdown(dev->session);
+		return;
+	}
+
+	avrcp_device_remove(dev);
+}
diff --git a/bluez/repo/android/avrcp.h b/bluez/repo/android/avrcp.h
new file mode 100644
index 0000000..11e79b7
--- /dev/null
+++ b/bluez/repo/android/avrcp.h
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_avrcp_unregister(void);
+
+void bt_avrcp_connect(const bdaddr_t *dst);
+void bt_avrcp_disconnect(const bdaddr_t *dst);
diff --git a/bluez/repo/android/bluetooth.c b/bluez/repo/android/bluetooth.c
new file mode 100644
index 0000000..51a31fe
--- /dev/null
+++ b/bluez/repo/android/bluetooth.c
@@ -0,0 +1,5343 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdlib.h>
+#include <unistd.h>
+#include <inttypes.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/mgmt.h"
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/queue.h"
+#include "src/eir.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/sdpd.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+
+#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
+
+#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings"
+#define DEVICES_FILE ANDROID_STORAGEDIR"/devices"
+#define CACHE_FILE ANDROID_STORAGEDIR"/cache"
+
+#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */
+#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */
+
+/* Default to DisplayYesNo */
+#define DEFAULT_IO_CAPABILITY 0x01
+
+/* Default discoverable timeout 120sec as in Android */
+#define DEFAULT_DISCOVERABLE_TIMEOUT 120
+
+#define DEVICES_CACHE_MAX 300
+
+#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \
+					+ sizeof(struct hal_property))
+
+#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
+					+ sizeof(struct hal_property))
+
+#define SCAN_TYPE_NONE 0
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
+#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC)
+
+struct device {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+
+	bdaddr_t rpa;
+	uint8_t rpa_type;
+
+	bool le;
+	bool bredr;
+
+	bool pairing;
+
+	bool bredr_paired;
+	bool bredr_bonded;
+	bool le_paired;
+	bool le_bonded;
+
+	bool in_white_list;
+
+	bool connected;
+
+	char *name;
+	char *friendly_name;
+
+	uint32_t class;
+	int32_t rssi;
+
+	time_t bredr_seen;
+	time_t le_seen;
+
+	GSList *uuids;
+
+	bool found; /* if device is found in current discovery session */
+	unsigned int confirm_id; /* mgtm command id if command pending */
+
+	bool valid_remote_csrk;
+	bool remote_csrk_auth;
+	uint8_t remote_csrk[16];
+	uint32_t remote_sign_cnt;
+
+	bool valid_local_csrk;
+	bool local_csrk_auth;
+	uint8_t local_csrk[16];
+	uint32_t local_sign_cnt;
+	uint16_t gatt_ccc;
+};
+
+struct browse_req {
+	bdaddr_t bdaddr;
+	GSList *uuids;
+	int search_uuid;
+	int reconnect_attempt;
+};
+
+static struct {
+	uint16_t index;
+
+	bdaddr_t bdaddr;
+	uint32_t dev_class;
+
+	char *name;
+
+	uint8_t max_advert_instance;
+	uint8_t rpa_offload_supported;
+	uint8_t max_irk_list_size;
+	uint8_t max_scan_filters_supported;
+	uint16_t scan_result_storage_size;
+	uint8_t activity_energy_info_supported;
+
+	uint32_t current_settings;
+	uint32_t supported_settings;
+
+	bool le_scanning;
+	uint8_t cur_discovery_type;
+	uint8_t exp_discovery_type;
+	uint32_t discoverable_timeout;
+
+	GSList *uuids;
+} adapter = {
+	.index = MGMT_INDEX_NONE,
+	.dev_class = 0,
+	.name = NULL,
+	.max_advert_instance = 0,
+	.rpa_offload_supported = 0,
+	.max_irk_list_size = 0,
+	.max_scan_filters_supported = 0,
+	.scan_result_storage_size = 0,
+	.activity_energy_info_supported = 0,
+	.current_settings = 0,
+	.supported_settings = 0,
+	.cur_discovery_type = SCAN_TYPE_NONE,
+	.exp_discovery_type = SCAN_TYPE_NONE,
+	.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
+	.uuids = NULL,
+};
+
+static const uint16_t uuid_list[] = {
+	L2CAP_UUID,
+	PNP_INFO_SVCLASS_ID,
+	PUBLIC_BROWSE_GROUP,
+	0
+};
+
+static uint16_t option_index = MGMT_INDEX_NONE;
+static struct mgmt *mgmt_if = NULL;
+
+static GSList *bonded_devices = NULL;
+static GSList *cached_devices = NULL;
+
+static bt_le_device_found gatt_device_found_cb = NULL;
+static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL;
+
+/* This list contains addresses which are asked for records */
+static GSList *browse_reqs;
+
+static struct ipc *hal_ipc = NULL;
+
+static bool kernel_conn_control = false;
+
+static struct queue *unpaired_cb_list = NULL;
+static struct queue *paired_cb_list = NULL;
+
+static void get_device_android_addr(struct device *dev, uint8_t *addr)
+{
+	/*
+	 * If RPA is set it means that IRK was received and ID address is being
+	 * used. Android Framework is still using old RPA and it needs to be
+	 * used in notifications.
+	 */
+	if (bacmp(&dev->rpa, BDADDR_ANY))
+		bdaddr2android(&dev->rpa, addr);
+	else
+		bdaddr2android(&dev->bdaddr, addr);
+}
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+	info("%s%s", prefix, str);
+}
+
+static void store_adapter_config(void)
+{
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	key_file = g_key_file_new();
+
+	g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+	ba2str(&adapter.bdaddr, addr);
+
+	g_key_file_set_string(key_file, "General", "Address", addr);
+
+	if (adapter.name)
+		g_key_file_set_string(key_file, "General", "Name",
+				adapter.name);
+
+	g_key_file_set_integer(key_file, "General", "DiscoverableTimeout",
+						adapter.discoverable_timeout);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+
+	g_file_set_contents(SETTINGS_FILE, data, length, NULL);
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+static void load_adapter_config(void)
+{
+	GError *gerr = NULL;
+	GKeyFile *key_file;
+	char *str;
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+	str = g_key_file_get_string(key_file, "General", "Address", NULL);
+	if (!str) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	str2ba(str, &adapter.bdaddr);
+	g_free(str);
+
+	adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL);
+
+	adapter.discoverable_timeout = g_key_file_get_integer(key_file,
+				"General", "DiscoverableTimeout", &gerr);
+	if (gerr) {
+		adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
+		g_clear_error(&gerr);
+	}
+
+	g_key_file_free(key_file);
+}
+
+static void store_device_info(struct device *dev, const char *path)
+{
+	GKeyFile *key_file;
+	char addr[18];
+	gsize length = 0;
+	char **uuids = NULL;
+	char *str;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, path, 0, NULL);
+
+	g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr);
+
+	if (dev->le)
+		g_key_file_set_integer(key_file, addr, "AddressType",
+							dev->bdaddr_type);
+
+	g_key_file_set_string(key_file, addr, "Name", dev->name);
+
+	if (dev->friendly_name)
+		g_key_file_set_string(key_file, addr, "FriendlyName",
+							dev->friendly_name);
+	else
+		g_key_file_remove_key(key_file, addr, "FriendlyName", NULL);
+
+	if (dev->class)
+		g_key_file_set_integer(key_file, addr, "Class", dev->class);
+	else
+		g_key_file_remove_key(key_file, addr, "Class", NULL);
+
+	if (dev->bredr_seen > dev->le_seen)
+		g_key_file_set_integer(key_file, addr, "Timestamp",
+							dev->bredr_seen);
+	else
+		g_key_file_set_integer(key_file, addr, "Timestamp",
+								dev->le_seen);
+
+	if (dev->uuids) {
+		GSList *l;
+		int i;
+
+		uuids = g_new0(char *, g_slist_length(dev->uuids) + 1);
+
+		for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) {
+			int j;
+			uint8_t *u = l->data;
+			char *uuid_str = g_malloc0(33);
+
+			for (j = 0; j < 16; j++)
+				sprintf(uuid_str + (j * 2), "%2.2X", u[j]);
+
+			uuids[i] = uuid_str;
+		}
+
+		g_key_file_set_string_list(key_file, addr, "Services",
+						(const char **)uuids, i);
+	} else {
+		g_key_file_remove_key(key_file, addr, "Services", NULL);
+	}
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(path, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+	g_strfreev(uuids);
+}
+
+static void remove_device_info(struct device *dev, const char *path)
+{
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *str;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, path, 0, NULL);
+
+	g_key_file_remove_group(key_file, addr, NULL);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(path, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static int device_match(gconstpointer a, gconstpointer b)
+{
+	const struct device *dev = a;
+	const bdaddr_t *bdaddr = b;
+
+	/* Android is using RPA even if IRK was received and ID addr resolved */
+	if (!bacmp(&dev->rpa, bdaddr))
+		return 0;
+
+	return bacmp(&dev->bdaddr, bdaddr);
+}
+
+static struct device *find_device(const bdaddr_t *bdaddr)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(bonded_devices, bdaddr, device_match);
+	if (l)
+		return l->data;
+
+	l = g_slist_find_custom(cached_devices, bdaddr, device_match);
+	if (l)
+		return l->data;
+
+	return NULL;
+}
+
+static void free_device(struct device *dev)
+{
+	if (dev->confirm_id)
+		mgmt_cancel(mgmt_if, dev->confirm_id);
+
+	g_free(dev->name);
+	g_free(dev->friendly_name);
+	g_slist_free_full(dev->uuids, g_free);
+	g_free(dev);
+}
+
+static void cache_device(struct device *new_dev)
+{
+	struct device *dev;
+	GSList *l;
+
+	l = g_slist_find(cached_devices, new_dev);
+	if (l) {
+		cached_devices = g_slist_remove(cached_devices, new_dev);
+		goto cache;
+	}
+
+	if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX)
+		goto cache;
+
+	l = g_slist_last(cached_devices);
+	dev = l->data;
+
+	cached_devices = g_slist_remove(cached_devices, dev);
+	remove_device_info(dev, CACHE_FILE);
+	free_device(dev);
+
+cache:
+	cached_devices = g_slist_prepend(cached_devices, new_dev);
+	store_device_info(new_dev, CACHE_FILE);
+}
+
+static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct device *dev;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("%s", addr);
+
+	dev = g_new0(struct device, 1);
+
+	bacpy(&dev->bdaddr, bdaddr);
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		dev->bredr = true;
+		dev->bredr_seen = time(NULL);
+	} else {
+		dev->le = true;
+		dev->bdaddr_type = bdaddr_type;
+		dev->le_seen = time(NULL);
+	}
+
+	/*
+	 * Use address for name, will be change if one is present
+	 * eg. in EIR or set by set_property.
+	 */
+	dev->name = g_strdup(addr);
+
+	return dev;
+}
+
+static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type)
+{
+	struct device *dev;
+
+	dev = find_device(bdaddr);
+	if (dev)
+		return dev;
+
+	dev = create_device(bdaddr, type);
+
+	cache_device(dev);
+
+	return dev;
+}
+
+static struct device *find_device_android(const uint8_t *addr)
+{
+	bdaddr_t bdaddr;
+
+	android2bdaddr(addr, &bdaddr);
+
+	return find_device(&bdaddr);
+}
+
+static struct device *get_device_android(const uint8_t *addr)
+{
+	bdaddr_t bdaddr;
+
+	android2bdaddr(addr, &bdaddr);
+
+	return get_device(&bdaddr, BDADDR_BREDR);
+}
+
+static  void send_adapter_property(uint8_t type, uint16_t len, const void *val)
+{
+	uint8_t buf[BASELEN_PROP_CHANGED + len];
+	struct hal_ev_adapter_props_changed *ev = (void *) buf;
+
+	ev->status = HAL_STATUS_SUCCESS;
+	ev->num_props = 1;
+	ev->props[0].type = type;
+	ev->props[0].len = len;
+	memcpy(ev->props[0].val, val, len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf);
+}
+
+static void adapter_name_changed(const uint8_t *name)
+{
+	/* Android expects string value without NULL terminator */
+	send_adapter_property(HAL_PROP_ADAPTER_NAME,
+					strlen((const char *) name), name);
+}
+
+static void adapter_set_name(const uint8_t *name)
+{
+	if (!g_strcmp0(adapter.name, (const char *) name))
+		return;
+
+	DBG("%s", name);
+
+	g_free(adapter.name);
+	adapter.name = g_strdup((const char *) name);
+
+	store_adapter_config();
+
+	adapter_name_changed(name);
+}
+
+static void mgmt_local_name_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_cp_set_local_name *rp = param;
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of local name changed parameters");
+		return;
+	}
+
+	adapter_set_name(rp->name);
+
+	/* TODO Update services if needed */
+}
+
+static void powered_changed(void)
+{
+	struct hal_ev_adapter_state_changed ev;
+
+	ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ?
+						HAL_POWER_ON : HAL_POWER_OFF;
+
+	DBG("%u", ev.state);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static uint8_t settings2scan_mode(void)
+{
+	bool connectable, discoverable;
+
+	connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
+	discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
+
+	if (connectable && discoverable)
+		return HAL_ADAPTER_SCAN_MODE_CONN_DISC;
+
+	if (connectable)
+		return HAL_ADAPTER_SCAN_MODE_CONN;
+
+	return HAL_ADAPTER_SCAN_MODE_NONE;
+}
+
+static void scan_mode_changed(void)
+{
+	uint8_t mode;
+
+	mode = settings2scan_mode();
+
+	DBG("mode %u", mode);
+
+	send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode);
+}
+
+static void adapter_class_changed(void)
+{
+	send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class),
+							&adapter.dev_class);
+}
+
+static void settings_changed(uint32_t settings)
+{
+	uint32_t changed_mask;
+	uint32_t scan_mode_mask;
+
+	changed_mask = adapter.current_settings ^ settings;
+
+	adapter.current_settings = settings;
+
+	DBG("0x%08x", changed_mask);
+
+	if (changed_mask & MGMT_SETTING_POWERED)
+		powered_changed();
+
+	scan_mode_mask = MGMT_SETTING_CONNECTABLE |
+					MGMT_SETTING_DISCOVERABLE;
+
+	/*
+	 * Only when powered, the connectable and discoverable
+	 * state changes should be communicated.
+	 */
+	if (adapter.current_settings & MGMT_SETTING_POWERED)
+		if (changed_mask & scan_mode_mask)
+			scan_mode_changed();
+}
+
+static void new_settings_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	uint32_t settings;
+
+	if (length < sizeof(settings)) {
+		error("Wrong size of new settings parameters");
+		return;
+	}
+
+	settings = get_le32(param);
+
+	DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings,
+								settings);
+
+	if (settings == adapter.current_settings)
+		return;
+
+	settings_changed(settings);
+}
+
+static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_cod *rp = param;
+	uint32_t dev_class;
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of class of device changed parameters");
+		return;
+	}
+
+	dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16);
+
+	if (dev_class == adapter.dev_class)
+		return;
+
+	DBG("Class: 0x%06x", dev_class);
+
+	adapter.dev_class = dev_class;
+
+	adapter_class_changed();
+
+	/* TODO: Gatt attrib set*/
+}
+
+void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value)
+{
+	struct device *dev;
+	GKeyFile *key_file;
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	dev = find_device(dst);
+	if (!dev)
+		return;
+
+	key_file = g_key_file_new();
+
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(&dev->bdaddr, addr);
+
+	DBG("%s Gatt CCC %d", addr, value);
+
+	g_key_file_set_integer(key_file, addr, "GattCCC", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+
+	dev->gatt_ccc = value;
+}
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return 0;
+
+	return dev->gatt_ccc;
+}
+
+static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
+					uint8_t type, uint8_t pin_length)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	char addr[18];
+	char *data;
+	int i;
+
+	key_file = g_key_file_new();
+
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(dst, addr);
+
+	DBG("%s type %u pin_len %u", addr, type, pin_length);
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, addr, "LinkKey", key_str);
+	g_key_file_set_integer(key_file, addr, "LinkKeyType", type);
+	g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void send_bond_state_change(struct device *dev, uint8_t status,
+								uint8_t state)
+{
+	struct hal_ev_bond_state_changed ev;
+
+	ev.status = status;
+	ev.state = state;
+	get_device_android_addr(dev, ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void update_bredr_state(struct device *dev, bool pairing, bool paired,
+								bool bonded)
+{
+	if (pairing == dev->pairing && paired == dev->bredr_paired &&
+						bonded == dev->bredr_bonded)
+		return;
+
+	/* avoid unpairing device on incoming pairing request */
+	if (pairing && dev->bredr_paired)
+		goto done;
+
+	/* avoid unpairing device if pairing failed */
+	if (!pairing && !paired && dev->pairing && dev->bredr_paired)
+		goto done;
+
+	if (paired && !dev->le_paired && !dev->bredr_paired) {
+		cached_devices = g_slist_remove(cached_devices, dev);
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+		remove_device_info(dev, CACHE_FILE);
+		store_device_info(dev, DEVICES_FILE);
+	} else if (!paired && !dev->le_paired) {
+		bonded_devices = g_slist_remove(bonded_devices, dev);
+		remove_device_info(dev, DEVICES_FILE);
+		cache_device(dev);
+	}
+
+	dev->bredr_paired = paired;
+
+	if (dev->bredr_paired)
+		dev->bredr_bonded = dev->bredr_bonded || bonded;
+	else
+		dev->bredr_bonded = false;
+
+done:
+	dev->pairing = pairing;
+}
+
+static void update_le_state(struct device *dev, bool pairing, bool paired,
+								bool bonded)
+{
+	if (pairing == dev->pairing && paired == dev->le_paired &&
+						bonded == dev->le_bonded)
+		return;
+
+	/* avoid unpairing device on incoming pairing request */
+	if (pairing && dev->le_paired)
+		goto done;
+
+	/* avoid unpairing device if pairing failed */
+	if (!pairing && !paired && dev->pairing && dev->le_paired)
+		goto done;
+
+	if (paired && !dev->bredr_paired && !dev->le_paired) {
+		cached_devices = g_slist_remove(cached_devices, dev);
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+		remove_device_info(dev, CACHE_FILE);
+		store_device_info(dev, DEVICES_FILE);
+	} else if (!paired && !dev->bredr_paired) {
+		bonded_devices = g_slist_remove(bonded_devices, dev);
+		remove_device_info(dev, DEVICES_FILE);
+		dev->valid_local_csrk = false;
+		dev->valid_remote_csrk = false;
+		dev->local_sign_cnt = 0;
+		dev->remote_sign_cnt = 0;
+		memset(dev->local_csrk, 0, sizeof(dev->local_csrk));
+		memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk));
+		cache_device(dev);
+	}
+
+	dev->le_paired = paired;
+
+	if (dev->le_paired)
+		dev->le_bonded = dev->le_bonded || bonded;
+	else
+		dev->le_bonded = false;
+
+done:
+	dev->pairing = pairing;
+}
+
+static uint8_t device_bond_state(struct device *dev)
+{
+	if (dev->pairing)
+		return HAL_BOND_STATE_BONDING;
+
+	/*
+	 * We are checking for paired here instead of bonded as HAL API is
+	 * using BOND state also if there was no bonding pairing.
+	 */
+	if (dev->bredr_paired || dev->le_paired)
+		return HAL_BOND_STATE_BONDED;
+
+	return HAL_BOND_STATE_NONE;
+}
+
+static void update_bond_state(struct device *dev, uint8_t status,
+					uint8_t old_bond, uint8_t new_bond)
+{
+	if (old_bond == new_bond)
+		return;
+
+	/*
+	 * When internal bond state changes from bond to non-bond or other way,
+	 * BfA needs to send bonding state to Android in the middle. Otherwise
+	 * Android will not handle it correctly
+	 */
+	if ((old_bond == HAL_BOND_STATE_NONE &&
+				new_bond == HAL_BOND_STATE_BONDED) ||
+				(old_bond == HAL_BOND_STATE_BONDED &&
+				new_bond == HAL_BOND_STATE_NONE))
+		send_bond_state_change(dev, HAL_STATUS_SUCCESS,
+						HAL_BOND_STATE_BONDING);
+
+	send_bond_state_change(dev, status, new_bond);
+}
+
+static void send_paired_notification(void *data, void *user_data)
+{
+	bt_paired_device_cb cb = data;
+	struct device *dev = user_data;
+
+	cb(&dev->bdaddr);
+}
+
+static void update_device_state(struct device *dev, uint8_t addr_type,
+				uint8_t status, bool pairing, bool paired,
+				bool bonded)
+{
+	uint8_t old_bond, new_bond;
+
+	old_bond = device_bond_state(dev);
+
+	if (addr_type == BDADDR_BREDR)
+		update_bredr_state(dev, pairing, paired, bonded);
+	else
+		update_le_state(dev, pairing, paired, bonded);
+
+	new_bond = device_bond_state(dev);
+
+	update_bond_state(dev, status, old_bond, new_bond);
+}
+
+static void send_device_property(struct device *dev, uint8_t type,
+						uint16_t len, const void *val)
+{
+	uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+
+	ev->status = HAL_STATUS_SUCCESS;
+	get_device_android_addr(dev, ev->bdaddr);
+	ev->num_props = 1;
+	ev->props[0].type = type;
+	ev->props[0].len = len;
+	memcpy(ev->props[0].val, val, len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf);
+}
+
+static void send_device_uuids_notif(struct device *dev)
+{
+	uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)];
+	uint8_t *ptr = buf;
+	GSList *l;
+
+	for (l = dev->uuids; l; l = g_slist_next(l)) {
+		memcpy(ptr, l->data, sizeof(uint128_t));
+		ptr += sizeof(uint128_t);
+	}
+
+	send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf);
+}
+
+static void set_device_uuids(struct device *dev, GSList *uuids)
+{
+	g_slist_free_full(dev->uuids, g_free);
+	dev->uuids = uuids;
+
+	if (dev->le_paired || dev->bredr_paired)
+		store_device_info(dev, DEVICES_FILE);
+	else
+		store_device_info(dev, CACHE_FILE);
+
+	send_device_uuids_notif(dev);
+}
+
+static void browse_req_free(struct browse_req *req)
+{
+	g_slist_free_full(req->uuids, g_free);
+	g_free(req);
+}
+
+static int uuid_128_cmp(gconstpointer a, gconstpointer b)
+{
+	return memcmp(a, b, sizeof(uint128_t));
+}
+
+static void update_records(struct browse_req *req, sdp_list_t *recs)
+{
+	for (; recs; recs = recs->next) {
+		sdp_record_t *rec = (sdp_record_t *) recs->data;
+		sdp_list_t *svcclass = NULL;
+		uuid_t uuid128;
+		uuid_t *tmp;
+		uint8_t *new_uuid;
+
+		if (!rec)
+			break;
+
+		if (sdp_get_service_classes(rec, &svcclass) < 0)
+			continue;
+
+		if (!svcclass)
+			continue;
+
+		tmp = svcclass->data;
+
+		switch (tmp->type) {
+		case SDP_UUID16:
+			sdp_uuid16_to_uuid128(&uuid128, tmp);
+			break;
+		case SDP_UUID32:
+			sdp_uuid32_to_uuid128(&uuid128, tmp);
+			break;
+		case SDP_UUID128:
+			memcpy(&uuid128, tmp, sizeof(uuid_t));
+			break;
+		default:
+			sdp_list_free(svcclass, free);
+			continue;
+		}
+
+		new_uuid = g_malloc(16);/* size of 128 bit uuid */
+		memcpy(new_uuid, &uuid128.value.uuid128,
+				sizeof(uuid128.value.uuid128));
+
+		/* Check if uuid is already added */
+		if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp))
+			g_free(new_uuid);
+		else
+			req->uuids = g_slist_append(req->uuids, new_uuid);
+
+		sdp_list_free(svcclass, free);
+	}
+}
+
+static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct browse_req *req = user_data;
+	struct device *dev;
+	uuid_t uuid;
+
+	/*
+	 * If we have a valid response and req->search_uuid == 2, then L2CAP
+	 * UUID & PNP searching was successful -- we are done
+	 */
+	if (err < 0 || req->search_uuid == 2) {
+		if (err == -ECONNRESET && req->reconnect_attempt < 1) {
+			req->search_uuid--;
+			req->reconnect_attempt++;
+		} else {
+			goto done;
+		}
+	}
+
+	update_records(req, recs);
+
+	/* Search for mandatory uuids */
+	if (uuid_list[req->search_uuid]) {
+		sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+		bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid,
+						browse_cb, user_data, NULL, 0);
+		return;
+	}
+
+done:
+	dev = find_device(&req->bdaddr);
+	if (dev) {
+		set_device_uuids(dev, req->uuids);
+		req->uuids = NULL;
+	}
+
+	browse_reqs = g_slist_remove(browse_reqs, req);
+	browse_req_free(req);
+}
+
+static int req_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct browse_req *req = a;
+	const bdaddr_t *bdaddr = b;
+
+	return bacmp(&req->bdaddr, bdaddr);
+}
+
+static uint8_t browse_remote_sdp(const bdaddr_t *addr)
+{
+	struct browse_req *req;
+	uuid_t uuid;
+
+	if (g_slist_find_custom(browse_reqs, addr, req_cmp))
+		return HAL_STATUS_SUCCESS;
+
+	req = g_new0(struct browse_req, 1);
+	bacpy(&req->bdaddr, addr);
+	sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+
+	if (bt_search_service(&adapter.bdaddr,
+			&req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) {
+		browse_req_free(req);
+		return HAL_STATUS_FAILED;
+	}
+
+	browse_reqs = g_slist_append(browse_reqs, req);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void send_remote_sdp_rec_notify(bt_uuid_t *uuid, int channel,
+					char *name, uint8_t name_len,
+					uint8_t status, bdaddr_t *bdaddr)
+{
+	struct hal_prop_device_service_rec *prop;
+	uint8_t buf[BASELEN_REMOTE_DEV_PROP + name_len + sizeof(*prop)];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	size_t prop_len = sizeof(*prop) + name_len;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (uuid && status == HAL_STATUS_SUCCESS) {
+		prop = (void *) &ev->props[0].val;
+		prop->name_len = name_len;
+		prop->channel = (uint16_t)channel;
+		memcpy(prop->name, name, name_len);
+		memcpy(prop->uuid, &uuid->value.u128, sizeof(prop->uuid));
+	}
+
+	ev->num_props = 1;
+	ev->status = status;
+	ev->props[0].len = prop_len;
+	bdaddr2android(bdaddr, ev->bdaddr);
+	ev->props[0].type = HAL_PROP_DEVICE_SERVICE_REC;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+						HAL_EV_REMOTE_DEVICE_PROPS,
+						sizeof(buf), buf);
+}
+
+static void find_remote_sdp_rec_cb(sdp_list_t *recs, int err,
+							gpointer user_data)
+{
+	bdaddr_t *addr = user_data;
+	uint8_t name_len;
+	uint8_t status;
+	char name_buf[256];
+	int channel;
+	bt_uuid_t uuid;
+	uuid_t uuid128_sdp;
+	sdp_list_t *protos;
+	sdp_record_t *sdp_rec;
+
+	if (err < 0) {
+		error("error while search remote sdp records");
+		status = HAL_STATUS_FAILED;
+		send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
+		goto done;
+	}
+
+	if (!recs) {
+		info("No service records found on remote");
+		status = HAL_STATUS_SUCCESS;
+		send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
+		goto done;
+	}
+
+	for ( ; recs; recs = recs->next) {
+		sdp_rec = recs->data;
+
+		switch (sdp_rec->svclass.type) {
+		case SDP_UUID16:
+			sdp_uuid16_to_uuid128(&uuid128_sdp,
+							&sdp_rec->svclass);
+			break;
+		case SDP_UUID32:
+			sdp_uuid32_to_uuid128(&uuid128_sdp,
+							&sdp_rec->svclass);
+			break;
+		case SDP_UUID128:
+			break;
+		default:
+			error("wrong sdp uuid type");
+			goto done;
+		}
+
+		if (!sdp_get_access_protos(sdp_rec, &protos)) {
+			channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+			sdp_list_foreach(protos,
+						(sdp_list_func_t) sdp_list_free,
+						NULL);
+			sdp_list_free(protos, NULL);
+		} else
+			channel = -1;
+
+		if (channel < 0) {
+			error("can't get channel for sdp record");
+			channel = 0;
+		}
+
+		if (!sdp_get_service_name(sdp_rec, name_buf, sizeof(name_buf)))
+			name_len = strlen(name_buf);
+		else
+			name_len = 0;
+
+		uuid.type = BT_UUID128;
+		memcpy(&uuid.value.u128, uuid128_sdp.value.uuid128.data,
+						sizeof(uuid.value.u128));
+		status = HAL_STATUS_SUCCESS;
+
+		send_remote_sdp_rec_notify(&uuid, channel, name_buf, name_len,
+								status, addr);
+	}
+
+done:
+	g_free(addr);
+}
+
+static uint8_t find_remote_sdp_rec(const bdaddr_t *addr,
+						const uint8_t *find_uuid)
+{
+	bdaddr_t *bdaddr;
+	uuid_t uuid;
+
+	/* from android we always get full 128bit length uuid */
+	sdp_uuid128_create(&uuid, find_uuid);
+
+	bdaddr = g_new(bdaddr_t, 1);
+	if (!bdaddr)
+		return HAL_STATUS_NOMEM;
+
+	memcpy(bdaddr, addr, sizeof(*bdaddr));
+
+	if (bt_search_service(&adapter.bdaddr, addr, &uuid,
+				find_remote_sdp_rec_cb, bdaddr, NULL, 0) < 0) {
+		g_free(bdaddr);
+		return HAL_STATUS_FAILED;
+	}
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void new_link_key_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_link_key *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small new link key event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("new key for %s type %u pin_len %u",
+					dst, ev->key.type, ev->key.pin_len);
+
+	if (ev->key.pin_len > 16) {
+		error("Invalid PIN length (%u) in new_key event",
+							ev->key.pin_len);
+		return;
+	}
+
+	dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint) {
+		const struct mgmt_link_key_info *key = &ev->key;
+
+		store_link_key(&addr->bdaddr, key->val, key->type,
+								key->pin_len);
+	}
+
+	browse_remote_sdp(&addr->bdaddr);
+}
+
+static uint8_t get_device_name(struct device *dev)
+{
+	send_device_property(dev, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct hal_ev_pin_request hal_ev;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small PIN code request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+
+	dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR);
+
+	/*
+	 * Workaround for Android Bluetooth.apk issue: send remote
+	 * device property
+	 */
+	get_device_name(dev);
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
+
+	/* Name already sent in remote device prop */
+	memset(&hal_ev, 0, sizeof(hal_ev));
+	get_device_android_addr(dev, hal_ev.bdaddr);
+	hal_ev.class_of_dev = dev->class;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
+						sizeof(hal_ev), &hal_ev);
+}
+
+static void send_ssp_request(struct device *dev, uint8_t variant,
+							uint32_t passkey)
+{
+	struct hal_ev_ssp_request ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	get_device_android_addr(dev, ev.bdaddr);
+	memcpy(ev.name, dev->name, strlen(dev->name));
+	ev.class_of_dev = dev->class;
+
+	ev.pairing_variant = variant;
+	ev.passkey = passkey;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
+							sizeof(ev), &ev);
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_user_confirm_request *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small user confirm request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s confirm_hint %u", dst, ev->confirm_hint);
+
+	dev = get_device(&ev->addr.bdaddr, ev->addr.type);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	if (ev->confirm_hint)
+		send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0);
+	else
+		send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value);
+}
+
+static void user_passkey_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_user_passkey_request *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small passkey request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s", dst);
+
+	dev = get_device(&ev->addr.bdaddr, ev->addr.type);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0);
+}
+
+static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_passkey_notify *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small passkey notify event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+	DBG("%s entered %u", dst, ev->entered);
+
+	/* HAL seems to not support entered characters */
+	if (ev->entered)
+		return;
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+								false, false);
+
+	send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey);
+}
+
+static void clear_device_found(gpointer data, gpointer user_data)
+{
+	struct device *dev = data;
+
+	dev->found = false;
+}
+
+static uint8_t get_supported_discovery_type(void)
+{
+	uint8_t type = SCAN_TYPE_NONE;
+
+	if (adapter.current_settings & MGMT_SETTING_BREDR)
+		type |= SCAN_TYPE_BREDR;
+
+	if (adapter.current_settings & MGMT_SETTING_LE)
+		type |= SCAN_TYPE_LE;
+
+	return type;
+}
+
+static bool start_discovery(uint8_t type)
+{
+	struct mgmt_cp_start_discovery cp;
+
+	cp.type = get_supported_discovery_type() & type;
+
+	DBG("type=0x%x", cp.type);
+
+	if (cp.type == SCAN_TYPE_NONE)
+		return false;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to start discovery");
+
+	return false;
+}
+
+/*
+ * Send discovery state change event only if it is related to dual type
+ * discovery session (triggered by start/cancel discovery commands)
+ */
+static void check_discovery_state(uint8_t new_type, uint8_t old_type)
+{
+	struct hal_ev_discovery_state_changed ev;
+
+	DBG("%u %u", new_type, old_type);
+
+	if (new_type == get_supported_discovery_type()) {
+		g_slist_foreach(bonded_devices, clear_device_found, NULL);
+		g_slist_foreach(cached_devices, clear_device_found, NULL);
+		ev.state = HAL_DISCOVERY_STATE_STARTED;
+		goto done;
+	}
+
+	if (old_type != get_supported_discovery_type())
+		return;
+
+	ev.state = HAL_DISCOVERY_STATE_STOPPED;
+
+done:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev);
+}
+
+static void mgmt_discovering_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_discovering *ev = param;
+	uint8_t type;
+
+	if (length < sizeof(*ev)) {
+		error("Too small discovering event");
+		return;
+	}
+
+	DBG("type %u discovering %u", ev->type, ev->discovering);
+
+	if (!!adapter.cur_discovery_type == !!ev->discovering)
+		return;
+
+	type = ev->discovering ? ev->type : SCAN_TYPE_NONE;
+
+	check_discovery_state(type, adapter.cur_discovery_type);
+
+	adapter.cur_discovery_type = type;
+
+	if (ev->discovering) {
+		adapter.exp_discovery_type = adapter.le_scanning ?
+						SCAN_TYPE_LE : SCAN_TYPE_NONE;
+		return;
+	}
+
+	/* One shot notification about discovery stopped */
+	if (gatt_discovery_stopped_cb) {
+		gatt_discovery_stopped_cb();
+		gatt_discovery_stopped_cb = NULL;
+	}
+
+	type = adapter.exp_discovery_type;
+	adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE :
+								SCAN_TYPE_NONE;
+
+	if (type != SCAN_TYPE_NONE)
+		start_discovery(type);
+}
+
+static void confirm_device_name_cb(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_confirm_name *rp = param;
+	struct device *dev;
+
+	DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status);
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of confirm name response");
+		return;
+	}
+
+	dev = find_device(&rp->addr.bdaddr);
+	if (!dev)
+		return;
+
+	dev->confirm_id = 0;
+}
+
+static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type,
+							bool resolve_name)
+{
+	struct mgmt_cp_confirm_name cp;
+	unsigned int res;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, addr);
+	cp.addr.type = addr_type;
+
+	if (!resolve_name)
+		cp.name_known = 1;
+
+	res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index,
+				sizeof(cp), &cp, confirm_device_name_cb,
+				NULL, NULL);
+	if (!res)
+		error("Failed to send confirm name request");
+
+	return res;
+}
+
+static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
+							const void *val)
+{
+	struct hal_property *prop = buf;
+
+	prop->type = type;
+	prop->len = len;
+
+	if (len)
+		memcpy(prop->val, val, len);
+
+	return sizeof(*prop) + len;
+}
+
+static uint8_t get_device_android_type(struct device *dev)
+{
+	if (dev->bredr && dev->le)
+		return HAL_TYPE_DUAL;
+
+	if (dev->le)
+		return HAL_TYPE_LE;
+
+	return HAL_TYPE_BREDR;
+}
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = get_device(addr, BDADDR_BREDR);
+
+	return get_device_android_type(dev);
+}
+
+bool bt_is_device_le(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	return dev->le;
+}
+
+const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return NULL;
+
+	if (type)
+		*type = dev->bdaddr_type;
+
+	return &dev->bdaddr;
+}
+
+const char *bt_get_adapter_name(void)
+{
+	return adapter.name;
+}
+
+bool bt_device_is_bonded(const bdaddr_t *bdaddr)
+{
+	if (g_slist_find_custom(bonded_devices, bdaddr, device_match))
+		return true;
+
+	return false;
+}
+
+bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	set_device_uuids(dev, uuids);
+
+	return true;
+}
+
+bool bt_kernel_conn_control(void)
+{
+	return kernel_conn_control;
+}
+
+bool bt_auto_connect_add(const bdaddr_t *addr)
+{
+	struct mgmt_cp_add_device cp;
+	struct device *dev;
+
+	if (!kernel_conn_control)
+		return false;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	if (dev->bdaddr_type == BDADDR_BREDR) {
+		DBG("auto-connection feature is not available for BR/EDR");
+		return false;
+	}
+
+	if (dev->in_white_list) {
+		DBG("Device already in white list");
+		return true;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, addr);
+	cp.addr.type = dev->bdaddr_type;
+	cp.action = 0x02;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_ADD_DEVICE, adapter.index, sizeof(cp),
+						&cp, NULL, NULL, NULL) > 0) {
+		dev->in_white_list = true;
+		return true;
+	}
+
+	error("Failed to add device");
+
+	return false;
+}
+
+void bt_auto_connect_remove(const bdaddr_t *addr)
+{
+	struct mgmt_cp_remove_device cp;
+	struct device *dev;
+
+	if (!kernel_conn_control)
+		return;
+
+	dev = find_device(addr);
+	if (!dev)
+		return;
+
+	if (dev->bdaddr_type == BDADDR_BREDR) {
+		DBG("auto-connection feature is not available for BR/EDR");
+		return;
+	}
+
+	if (!dev->in_white_list) {
+		DBG("Device already removed from white list");
+		return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, addr);
+	cp.addr.type = dev->bdaddr_type;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) > 0) {
+		dev->in_white_list = false;
+		return;
+	}
+
+	error("Failed to remove device");
+}
+
+static bool match_by_value(const void *data, const void *user_data)
+{
+	return data == user_data;
+}
+
+bool bt_unpaired_register(bt_unpaired_device_cb cb)
+{
+	if (queue_find(unpaired_cb_list, match_by_value, cb))
+		return false;
+
+	return queue_push_head(unpaired_cb_list, cb);
+}
+
+void bt_unpaired_unregister(bt_unpaired_device_cb cb)
+{
+	queue_remove(unpaired_cb_list, cb);
+}
+
+bool bt_paired_register(bt_paired_device_cb cb)
+{
+	if (queue_find(paired_cb_list, match_by_value, cb))
+		return false;
+
+	return queue_push_head(paired_cb_list, cb);
+}
+
+void bt_paired_unregister(bt_paired_device_cb cb)
+{
+	queue_remove(paired_cb_list, cb);
+}
+
+bool bt_is_pairing(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	return dev->pairing;
+}
+
+static bool rssi_above_threshold(int old, int new)
+{
+	/* only 8 dBm or more */
+	return abs(old - new) >= 8;
+}
+
+static void update_new_device(struct device *dev, int8_t rssi,
+						const struct eir_data *eir)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_device_found *ev = (void *) buf;
+	uint8_t android_bdaddr[6];
+	uint8_t android_type;
+	size_t size;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (adapter.cur_discovery_type)
+		dev->found = true;
+
+	size = sizeof(*ev);
+
+	get_device_android_addr(dev, android_bdaddr);
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR,
+				sizeof(android_bdaddr), android_bdaddr);
+	ev->num_props++;
+
+	android_type = get_device_android_type(dev);
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+				sizeof(android_type), &android_type);
+	ev->num_props++;
+
+	if (eir->class)
+		dev->class = eir->class;
+
+	if (dev->class) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+					sizeof(dev->class), &dev->class);
+		ev->num_props++;
+	}
+
+	if (rssi && rssi_above_threshold(dev->rssi, rssi))
+		dev->rssi = rssi;
+
+	if (dev->rssi) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+						sizeof(dev->rssi), &dev->rssi);
+		ev->num_props++;
+	}
+
+	if (eir->name && strlen(eir->name)) {
+		g_free(dev->name);
+		dev->name = g_strdup(eir->name);
+	}
+
+	if (dev->name) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+		ev->num_props++;
+
+		/* when updating name also send stored friendly name */
+		if (dev->friendly_name) {
+			size += fill_hal_prop(buf + size,
+						HAL_PROP_DEVICE_FRIENDLY_NAME,
+						strlen(dev->friendly_name),
+						dev->friendly_name);
+			ev->num_props++;
+		}
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND,
+								size, buf);
+}
+
+static void update_device(struct device *dev, int8_t rssi,
+						const struct eir_data *eir)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	uint8_t old_type, new_type;
+	size_t size;
+
+	memset(buf, 0, sizeof(buf));
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	get_device_android_addr(dev, ev->bdaddr);
+
+	old_type = get_device_android_type(dev);
+
+	new_type = get_device_android_type(dev);
+
+	if (old_type != new_type) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+						sizeof(new_type), &new_type);
+		ev->num_props++;
+	}
+
+	if (eir->class && dev->class != eir->class) {
+		dev->class = eir->class;
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+					sizeof(dev->class), &dev->class);
+		ev->num_props++;
+	}
+
+	if (rssi && rssi_above_threshold(dev->rssi, rssi)) {
+		dev->rssi = rssi;
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+						sizeof(dev->rssi), &dev->rssi);
+		ev->num_props++;
+	}
+
+	if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) {
+		g_free(dev->name);
+		dev->name = g_strdup(eir->name);
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+		ev->num_props++;
+
+		/* when updating name also send stored friendly name */
+		if (dev->friendly_name) {
+			size += fill_hal_prop(buf + size,
+						HAL_PROP_DEVICE_FRIENDLY_NAME,
+						strlen(dev->friendly_name),
+						dev->friendly_name);
+			ev->num_props++;
+		}
+	}
+
+	if (ev->num_props)
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static bool is_new_device(const struct device *dev, unsigned int flags,
+							uint8_t bdaddr_type)
+{
+	if (dev->found)
+		return false;
+
+	if (dev->bredr_paired || dev->le_paired)
+		return false;
+
+	if (bdaddr_type != BDADDR_BREDR &&
+				!(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
+		return false;
+
+	return true;
+}
+
+static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+					int8_t rssi, bool confirm,
+					bool connectable,
+					const uint8_t *data, uint8_t data_len)
+{
+	struct eir_data eir;
+	struct device *dev;
+
+	memset(&eir, 0, sizeof(eir));
+
+	eir_parse(&eir, data, data_len);
+
+	dev = get_device(bdaddr, bdaddr_type);
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		dev->bredr = true;
+		dev->bredr_seen = time(NULL);
+	} else {
+		dev->le = true;
+		dev->bdaddr_type = bdaddr_type;
+		dev->le_seen = time(NULL);
+	}
+
+	/*
+	 * Device found event needs to be send also for known device if this is
+	 * new discovery session. Otherwise framework will ignore it.
+	 */
+	if (is_new_device(dev, eir.flags, bdaddr_type))
+		update_new_device(dev, rssi, &eir);
+	else
+		update_device(dev, rssi, &eir);
+
+	eir_data_free(&eir);
+
+	/* Notify Gatt if its registered for LE events */
+	if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) {
+		const bdaddr_t *addr;
+
+		/*
+		 * If RPA is set it means that IRK was received and ID address
+		 * is being used. Android Framework is still using old RPA and
+		 * it needs to be used also in GATT notifications. Also GATT
+		 * HAL implementation is using RPA for devices matching.
+		 */
+		if (bacmp(&dev->rpa, BDADDR_ANY))
+			addr = &dev->rpa;
+		else
+			addr = &dev->bdaddr;
+
+		gatt_device_found_cb(addr, rssi, data_len, data, connectable,
+								dev->le_bonded);
+	}
+
+	if (!dev->bredr_paired && !dev->le_paired)
+		cache_device(dev);
+
+	if (confirm) {
+		char addr[18];
+		bool resolve_name = true;
+
+		ba2str(bdaddr, addr);
+
+		/*
+		 * Don't need to confirm name if we have it already in cache
+		 * Just check if device name is different than bdaddr
+		 */
+		if (g_strcmp0(dev->name, addr)) {
+			get_device_name(dev);
+			resolve_name = false;
+		}
+
+		info("Device %s needs name confirmation (resolve_name=%d)",
+							addr, resolve_name);
+		dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type,
+								resolve_name);
+	}
+}
+
+static void mgmt_device_found_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_found *ev = param;
+	const uint8_t *eir;
+	uint16_t eir_len;
+	uint32_t flags;
+	bool confirm_name;
+	bool connectable;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too short device found event (%u bytes)", length);
+		return;
+	}
+
+	eir_len = le16_to_cpu(ev->eir_len);
+	if (length != sizeof(*ev) + eir_len) {
+		error("Device found event size mismatch (%u != %zu)",
+					length, sizeof(*ev) + eir_len);
+		return;
+	}
+
+	if (eir_len == 0)
+		eir = NULL;
+	else
+		eir = ev->eir;
+
+	flags = le32_to_cpu(ev->flags);
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+				index, addr, ev->rssi, flags, eir_len);
+
+	confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME;
+	connectable = !(flags & MGMT_DEV_FOUND_NOT_CONNECTABLE);
+
+	update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi,
+				confirm_name, connectable, eir, eir_len);
+}
+
+static void mgmt_device_connected_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_connected *ev = param;
+	struct hal_ev_acl_state_changed hal_ev;
+	struct device *dev;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too short device connected event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("%s type %u", addr, ev->addr.type);
+
+	update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, false,
+					&ev->eir[0], le16_to_cpu(ev->eir_len));
+
+	hal_ev.status = HAL_STATUS_SUCCESS;
+	hal_ev.state = HAL_ACL_STATE_CONNECTED;
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	dev->connected = true;
+
+	get_device_android_addr(dev, hal_ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+}
+
+static bool device_is_paired(struct device *dev, uint8_t addr_type)
+{
+	if (addr_type == BDADDR_BREDR)
+		return dev->bredr_paired;
+
+	return dev->le_paired;
+}
+
+static bool device_is_bonded(struct device *dev)
+{
+	return dev->bredr_bonded || dev->le_bonded;
+}
+
+static void mgmt_device_disconnected_event(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_device_disconnected *ev = param;
+	struct hal_ev_acl_state_changed hal_ev;
+	struct device *dev;
+	uint8_t type = ev->addr.type;
+
+	if (length < sizeof(*ev)) {
+		error("Too short device disconnected event (%u bytes)", length);
+		return;
+	}
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	hal_ev.status = HAL_STATUS_SUCCESS;
+	hal_ev.state = HAL_ACL_STATE_DISCONNECTED;
+	get_device_android_addr(dev, hal_ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+			HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
+
+	if (device_is_paired(dev, type) && !device_is_bonded(dev))
+		update_device_state(dev, type, HAL_STATUS_SUCCESS, false,
+								false, false);
+
+	dev->connected = false;
+}
+
+static uint8_t status_mgmt2hal(uint8_t mgmt)
+{
+	switch (mgmt) {
+	case MGMT_STATUS_SUCCESS:
+		return HAL_STATUS_SUCCESS;
+	case MGMT_STATUS_NO_RESOURCES:
+		return HAL_STATUS_NOMEM;
+	case MGMT_STATUS_BUSY:
+		return HAL_STATUS_BUSY;
+	case MGMT_STATUS_NOT_SUPPORTED:
+		return HAL_STATUS_UNSUPPORTED;
+	case MGMT_STATUS_INVALID_PARAMS:
+		return HAL_STATUS_INVALID;
+	case MGMT_STATUS_AUTH_FAILED:
+		return HAL_STATUS_AUTH_FAILURE;
+	case MGMT_STATUS_NOT_CONNECTED:
+		return HAL_STATUS_REMOTE_DEVICE_DOWN;
+	default:
+		return HAL_STATUS_FAILED;
+	}
+}
+
+static void mgmt_connect_failed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_connect_failed *ev = param;
+	struct device *dev;
+
+	if (length < sizeof(*ev)) {
+		error("Too short connect failed event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	/*
+	 * In case security mode 3 pairing we will get connect failed event
+	 * in case e.g wrong PIN code entered. Let's check if device is
+	 * bonding, if so update bond state
+	 */
+
+	if (!dev->pairing)
+		return;
+
+	update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+							false, false, false);
+}
+
+static void mgmt_auth_failed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_auth_failed *ev = param;
+	struct device *dev;
+
+	if (length < sizeof(*ev)) {
+		error("Too small auth failed mgmt event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	if (!dev->pairing)
+		return;
+
+	update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+							false, false, false);
+}
+
+static void mgmt_device_unpaired_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_unpaired *ev = param;
+	struct device *dev;
+
+	if (length < sizeof(*ev)) {
+		error("Too small device unpaired event (%u bytes)", length);
+		return;
+	}
+
+	DBG("");
+
+	/* TODO should device be disconnected ? */
+
+	dev = find_device(&ev->addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false,
+								false, false);
+
+	/* Unpaired device is removed from the white list */
+	dev->in_white_list = false;
+}
+
+static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master,
+			const uint8_t *key, uint8_t key_type, uint8_t enc_size,
+			uint16_t ediv, uint64_t rand)
+{
+	const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	char addr[18];
+	char *data;
+	int i;
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(dst, addr);
+
+	key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+	keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+	encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+	ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+	rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, addr, key_s, key_str);
+
+	g_key_file_set_integer(key_file, addr, keytype_s, key_type);
+
+	g_key_file_set_integer(key_file, addr, encsize_s, enc_size);
+
+	g_key_file_set_integer(key_file, addr, ediv_s, ediv);
+
+	g_key_file_set_uint64(key_file, addr, rand_s, rand);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_long_term_key *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small long term key event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+
+	DBG("new LTK for %s type %u enc_size %u store_hint %u",
+			dst, ev->key.type, ev->key.enc_size, ev->store_hint);
+
+	dev = find_device(&ev->key.addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint) {
+		const struct mgmt_ltk_info *key = &ev->key;
+		uint16_t ediv;
+		uint64_t rand;
+
+		ediv = le16_to_cpu(key->ediv);
+		rand = le64_to_cpu(key->rand);
+
+		store_ltk(&key->addr.bdaddr, key->addr.type, key->master,
+				key->val, key->type, key->enc_size, ediv, rand);
+	}
+
+	/* TODO browse services here? */
+}
+
+static void store_csrk(struct device *dev)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	char addr[18];
+	int i;
+	gsize length = 0;
+	char *data;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	if (dev->valid_local_csrk) {
+		for (i = 0; i < 16; i++)
+			sprintf(key_str + (i * 2), "%2.2X",
+							dev->local_csrk[i]);
+
+		g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
+
+		g_key_file_set_boolean(key_file, addr, "LocalCSRKAuthenticated",
+							dev->local_csrk_auth);
+	}
+
+	if (dev->valid_remote_csrk) {
+		for (i = 0; i < 16; i++)
+			sprintf(key_str + (i * 2), "%2.2X",
+							dev->remote_csrk[i]);
+
+		g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
+
+		g_key_file_set_boolean(key_file, addr,
+						"RemoteCSRKAuthenticated",
+						dev->remote_csrk_auth);
+	}
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_csrk *ev = param;
+	struct device *dev;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		error("Too small csrk event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+	dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
+	if (!dev)
+		return;
+
+	switch (ev->key.type) {
+	case 0x00:
+	case 0x02:
+		memcpy(dev->local_csrk, ev->key.val, 16);
+		dev->local_sign_cnt = 0;
+		dev->valid_local_csrk = true;
+		dev->local_csrk_auth = ev->key.type == 0x02;
+		break;
+	case 0x01:
+	case 0x03:
+		memcpy(dev->remote_csrk, ev->key.val, 16);
+		dev->remote_sign_cnt = 0;
+		dev->valid_remote_csrk = true;
+		dev->remote_csrk_auth = ev->key.type == 0x03;
+		break;
+	default:
+		error("Unknown CSRK key type 02%02x", ev->key.type);
+		return;
+	}
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint)
+		store_csrk(dev);
+}
+
+static void store_irk(struct device *dev, const uint8_t *val)
+{
+	GKeyFile *key_file;
+	char key_str[33];
+	char addr[18];
+	int i;
+	gsize length = 0;
+	char *data;
+
+	ba2str(&dev->bdaddr, addr);
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", val[i]);
+
+	g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_irk *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct device *dev;
+	char dst[18], rpa[18];
+
+	if (length < sizeof(*ev)) {
+		error("To small New Irk Event (%u bytes)", length);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, dst);
+	ba2str(&ev->rpa, rpa);
+
+	DBG("new IRK for %s, RPA %s", dst, rpa);
+
+	if (!bacmp(&ev->rpa, BDADDR_ANY)) {
+		dev = get_device(&addr->bdaddr, addr->type);
+		if (!dev)
+			return;
+	} else {
+		dev = find_device(&addr->bdaddr);
+
+		if (dev && dev->bredr_paired) {
+			bacpy(&dev->rpa, &addr->bdaddr);
+			dev->rpa_type = addr->type;
+
+			/* TODO merge properties ie. UUIDs */
+		} else {
+			dev = find_device(&ev->rpa);
+			if (!dev)
+				return;
+
+			/* don't leave garbage in cache file */
+			remove_device_info(dev, CACHE_FILE);
+
+			/*
+			 * RPA resolution is transparent for Android Framework
+			 * ie. device is still access by RPA so it need to be
+			 * keep. After bluetoothd restart device is advertised
+			 * to Android with IDA and RPA is not set.
+			 */
+			bacpy(&dev->rpa, &dev->bdaddr);
+			dev->rpa_type = dev->bdaddr_type;
+
+			bacpy(&dev->bdaddr, &addr->bdaddr);
+			dev->bdaddr_type = addr->type;
+		}
+	}
+
+	update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+							true, !!ev->store_hint);
+
+	if (ev->store_hint)
+		store_irk(dev, ev->key.val);
+}
+
+static void register_mgmt_handlers(void)
+{
+	mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
+					new_settings_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index,
+				mgmt_dev_class_changed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index,
+				mgmt_local_name_changed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index,
+					new_link_key_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index,
+					pin_code_request_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index,
+				user_confirm_request_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index,
+				user_passkey_request_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index,
+				user_passkey_notify_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index,
+					mgmt_discovering_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index,
+					mgmt_device_found_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index,
+				mgmt_device_connected_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index,
+				mgmt_device_disconnected_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index,
+					mgmt_connect_failed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index,
+					mgmt_auth_failed_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index,
+				mgmt_device_unpaired_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
+					new_long_term_key_event, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index,
+						new_csrk_callback, NULL, NULL);
+
+	mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback,
+								NULL, NULL);
+}
+
+static void load_link_keys_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	bt_bluetooth_ready cb = user_data;
+	int err;
+
+	if (status) {
+		error("Failed to load link keys for index %u: %s (0x%02x)",
+				adapter.index, mgmt_errstr(status), status);
+		err = -EIO;
+		goto failed;
+	}
+
+	DBG("status %u", status);
+
+	cb(0, &adapter.bdaddr);
+	return;
+
+failed:
+	cb(err, NULL);
+}
+
+static void load_link_keys(GSList *keys, bt_bluetooth_ready cb)
+{
+	struct mgmt_cp_load_link_keys *cp;
+	struct mgmt_link_key_info *key;
+	size_t key_count, cp_size;
+	unsigned int id;
+
+	key_count = g_slist_length(keys);
+
+	DBG("keys %zu ", key_count);
+
+	cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+	cp = g_malloc0(cp_size);
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to
+	 * load an empty list into the kernel. That way it is ensured
+	 * that no old keys from a previous daemon are present.
+	 */
+	cp->key_count = cpu_to_le16(key_count);
+
+	for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++)
+		memcpy(key, keys->data, sizeof(*key));
+
+	id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index,
+			cp_size, cp, load_link_keys_complete, cb, NULL);
+
+	g_free(cp);
+
+	if (id == 0) {
+		error("Failed to load link keys");
+		cb(-EIO, NULL);
+	}
+}
+
+static void load_ltk_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status == MGMT_STATUS_SUCCESS)
+		return;
+
+	info("Failed to load LTKs: %s (0x%02x)", mgmt_errstr(status), status);
+}
+
+static void load_ltks(GSList *ltks)
+{
+	struct mgmt_cp_load_long_term_keys *cp;
+	struct mgmt_ltk_info *ltk;
+	size_t ltk_count, cp_size;
+	GSList *l;
+
+	ltk_count = g_slist_length(ltks);
+
+	DBG("ltks %zu", ltk_count);
+
+	cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk));
+
+	cp = g_malloc0(cp_size);
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to load
+	 * an empty list into the kernel. That way it is ensured that no old
+	 * keys from a previous daemon are present.
+	 */
+	cp->key_count = cpu_to_le16(ltk_count);
+
+	for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++)
+		memcpy(ltk, l->data, sizeof(*ltk));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index,
+			cp_size, cp, load_ltk_complete, NULL, NULL) == 0)
+		error("Failed to load LTKs");
+
+	g_free(cp);
+}
+
+static void load_irk_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status == MGMT_STATUS_SUCCESS)
+		return;
+
+	info("Failed to load IRKs: %s (0x%02x)", mgmt_errstr(status), status);
+}
+
+static void load_irks(GSList *irks)
+{
+	struct mgmt_cp_load_irks *cp;
+	struct mgmt_irk_info *irk;
+	size_t irk_count, cp_size;
+	GSList *l;
+
+	irk_count = g_slist_length(irks);
+
+	DBG("irks %zu", irk_count);
+
+	cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+	cp = g_malloc0(cp_size);
+
+	cp->irk_count = cpu_to_le16(irk_count);
+
+	for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++)
+		memcpy(irk, irks->data, sizeof(*irk));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp,
+					load_irk_complete, NULL, NULL) == 0)
+		error("Failed to load IRKs");
+
+	g_free(cp);
+}
+
+static uint8_t get_adapter_uuids(void)
+{
+	struct hal_ev_adapter_props_changed *ev;
+	GSList *list = adapter.uuids;
+	size_t uuid_count = g_slist_length(list);
+	size_t len = uuid_count * sizeof(uint128_t);
+	uint8_t buf[BASELEN_PROP_CHANGED + len];
+	uint8_t *p;
+
+	memset(buf, 0, sizeof(buf));
+	ev = (void *) buf;
+
+	ev->num_props = 1;
+	ev->status = HAL_STATUS_SUCCESS;
+
+	ev->props[0].type = HAL_PROP_ADAPTER_UUIDS;
+	ev->props[0].len = len;
+	p = ev->props->val;
+
+	for (; list; list = g_slist_next(list)) {
+		uuid_t *uuid = list->data;
+
+		memcpy(p, &uuid->value.uuid128, sizeof(uint128_t));
+
+		p += sizeof(uint128_t);
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void remove_uuid_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status),
+									status);
+		return;
+	}
+
+	mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
+
+	get_adapter_uuids();
+}
+
+static void remove_uuid(uuid_t *uuid)
+{
+	uint128_t uint128;
+	struct mgmt_cp_remove_uuid cp;
+
+	ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp,
+					remove_uuid_complete, NULL, NULL);
+}
+
+static void add_uuid_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to add UUID: %s (0x%02x)", mgmt_errstr(status),
+									status);
+		return;
+	}
+
+	mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
+
+	get_adapter_uuids();
+}
+
+static void add_uuid(uint8_t svc_hint, uuid_t *uuid)
+{
+	uint128_t uint128;
+	struct mgmt_cp_add_uuid cp;
+
+	ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	cp.svc_hint = svc_hint;
+
+	mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp,
+						add_uuid_complete, NULL, NULL);
+}
+
+int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint)
+{
+	uuid_t *uuid;
+
+	uuid = sdp_uuid_to_uuid128(&rec->svclass);
+
+	if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) {
+		char uuid_str[32];
+
+		sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str));
+		DBG("UUID %s already added", uuid_str);
+
+		bt_free(uuid);
+		return -EALREADY;
+	}
+
+	adapter.uuids = g_slist_prepend(adapter.uuids, uuid);
+
+	add_uuid(svc_hint, uuid);
+
+	return add_record_to_server(&adapter.bdaddr, rec);
+}
+
+void bt_adapter_remove_record(uint32_t handle)
+{
+	sdp_record_t *rec;
+	GSList *uuid_found;
+
+	rec = sdp_record_find(handle);
+	if (!rec)
+		return;
+
+	uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass,
+								sdp_uuid_cmp);
+	if (uuid_found) {
+		uuid_t *uuid = uuid_found->data;
+
+		remove_uuid(uuid);
+
+		adapter.uuids = g_slist_remove(adapter.uuids, uuid);
+
+		free(uuid);
+	}
+
+	remove_record_from_server(handle);
+}
+
+static void set_mode_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to set mode: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	new_settings_callback(adapter.index, length, param, NULL);
+}
+
+static bool set_mode(uint16_t opcode, uint8_t mode)
+{
+	struct mgmt_mode cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+
+	DBG("opcode=0x%x mode=0x%x", opcode, mode);
+
+	if (mgmt_send(mgmt_if, opcode, adapter.index, sizeof(cp), &cp,
+					set_mode_complete, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to set mode");
+
+	return false;
+}
+
+static void set_io_capability(void)
+{
+	struct mgmt_cp_set_io_capability cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.io_capability = DEFAULT_IO_CAPABILITY;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_IO_CAPABILITY, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+		error("Failed to set IO capability");
+}
+
+static void set_device_id(void)
+{
+	struct mgmt_cp_set_device_id cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.source = cpu_to_le16(bt_config_get_pnp_source());
+	cp.vendor = cpu_to_le16(bt_config_get_pnp_vendor());
+	cp.product = cpu_to_le16(bt_config_get_pnp_product());
+	cp.version = cpu_to_le16(bt_config_get_pnp_version());
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_DEVICE_ID, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+		error("Failed to set device id");
+
+	register_device_id(bt_config_get_pnp_source(),
+						bt_config_get_pnp_vendor(),
+						bt_config_get_pnp_product(),
+						bt_config_get_pnp_version());
+
+	bt_adapter_add_record(sdp_record_find(0x10000), 0x00);
+}
+
+static void set_adapter_name_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_cp_set_local_name *rp = param;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to set name: %s (0x%02x)", mgmt_errstr(status),
+									status);
+		return;
+	}
+
+	adapter_set_name(rp->name);
+}
+
+static uint8_t set_adapter_name(const uint8_t *name, uint16_t len)
+{
+	struct mgmt_cp_set_local_name cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(cp.name, name, len);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_LOCAL_NAME, adapter.index,
+				sizeof(cp), &cp, set_adapter_name_complete,
+				NULL, NULL) > 0)
+		return HAL_STATUS_SUCCESS;
+
+	error("Failed to set name");
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len)
+{
+	const uint32_t *timeout = buf;
+
+	if (len != sizeof(*timeout)) {
+		error("Invalid set disc timeout size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return HAL_STATUS_FAILED;
+	}
+
+	/*
+	 * Android handles discoverable timeout in Settings app.
+	 * There is no need to use kernel feature for that.
+	 * Just need to store this value here
+	 */
+
+	memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t));
+
+	store_adapter_config();
+
+	send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+					sizeof(adapter.discoverable_timeout),
+					&adapter.discoverable_timeout);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void clear_uuids(void)
+{
+	struct mgmt_cp_remove_uuid cp;
+
+	memset(&cp, 0, sizeof(cp));
+
+	mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp),
+							&cp, NULL, NULL, NULL);
+}
+
+static struct device *create_device_from_info(GKeyFile *key_file,
+							const char *peer)
+{
+	struct device *dev;
+	uint8_t type;
+	bdaddr_t bdaddr;
+	char **uuids;
+	char *str;
+
+	/* BREDR if not present */
+	type = g_key_file_get_integer(key_file, peer, "AddressType", NULL);
+
+	str2ba(peer, &bdaddr);
+	dev = create_device(&bdaddr, type);
+
+	if (type != BDADDR_BREDR)
+		dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR",
+									NULL);
+
+	str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL);
+	if (str) {
+		int i;
+
+		dev->valid_local_csrk = true;
+		for (i = 0; i < 16; i++)
+			sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]);
+
+		g_free(str);
+
+		dev->local_sign_cnt = g_key_file_get_integer(key_file, peer,
+						"LocalCSRKSignCounter", NULL);
+
+		dev->local_csrk_auth = g_key_file_get_boolean(key_file, peer,
+						"LocalCSRKAuthenticated", NULL);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL);
+	if (str) {
+		int i;
+
+		dev->valid_remote_csrk = true;
+		for (i = 0; i < 16; i++)
+			sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]);
+
+		g_free(str);
+
+		dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer,
+						"RemoteCSRKSignCounter", NULL);
+
+		dev->remote_csrk_auth = g_key_file_get_boolean(key_file, peer,
+						"RemoteCSRKAuthenticated",
+						NULL);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "GattCCC", NULL);
+	if (str) {
+		dev->gatt_ccc = atoi(str);
+		g_free(str);
+	}
+
+	str = g_key_file_get_string(key_file, peer, "Name", NULL);
+	if (str) {
+		g_free(dev->name);
+		dev->name = str;
+	}
+
+	str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL);
+	if (str) {
+		g_free(dev->friendly_name);
+		dev->friendly_name = str;
+	}
+
+	dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL);
+
+	if (dev->bredr)
+		dev->bredr_seen = g_key_file_get_integer(key_file, peer,
+								"Timestamp",
+								NULL);
+	else
+		dev->le_seen = g_key_file_get_integer(key_file, peer,
+							"Timestamp", NULL);
+
+	uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL,
+									NULL);
+	if (uuids) {
+		char **uuid;
+
+		for (uuid = uuids; *uuid; uuid++) {
+			uint8_t *u = g_malloc0(16);
+			int i;
+
+			for (i = 0; i < 16; i++)
+				sscanf((*uuid) + (i * 2), "%02hhX", &u[i]);
+
+			dev->uuids = g_slist_append(dev->uuids, u);
+		}
+
+		g_strfreev(uuids);
+	}
+
+	return dev;
+}
+
+static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file,
+							const char *peer)
+{
+	struct mgmt_link_key_info *info = NULL;
+	char *str;
+	unsigned int i;
+
+	str = g_key_file_get_string(key_file, peer, "LinkKey", NULL);
+	if (!str || strlen(str) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_link_key_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+	info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType",
+									NULL);
+	info->pin_len = g_key_file_get_integer(key_file, peer,
+						"LinkKeyPinLength", NULL);
+
+failed:
+	g_free(str);
+
+	return info;
+}
+
+static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer,
+								bool master)
+{
+	const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+	struct mgmt_ltk_info *info = NULL;
+	char *key;
+	unsigned int i;
+
+	key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+	keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+	encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+	ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+	rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+	key = g_key_file_get_string(key_file, peer, key_s, NULL);
+	if (!key || strlen(key) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_ltk_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+									NULL);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(key + (i * 2), "%02hhX", &info->val[i]);
+
+	info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL);
+
+	info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s,
+									NULL);
+
+	info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL);
+	info->rand = cpu_to_le64(info->rand);
+
+	info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL);
+	info->ediv = cpu_to_le16(info->ediv);
+
+	info->master = master;
+
+failed:
+	g_free(key);
+
+	return info;
+}
+
+static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer)
+{
+	struct mgmt_irk_info *info = NULL;
+	unsigned int i;
+	char *str;
+
+	str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey",
+									NULL);
+	if (!str || strlen(str) != 32)
+		goto failed;
+
+	info = g_new0(struct mgmt_irk_info, 1);
+
+	str2ba(peer, &info->addr.bdaddr);
+
+	info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType",
+									NULL);
+
+	for (i = 0; i < sizeof(info->val); i++)
+		sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+failed:
+	g_free(str);
+
+	return info;
+}
+
+static time_t device_timestamp(const struct device *dev)
+{
+	if (dev->bredr && dev->le) {
+		if (dev->le_seen > dev->bredr_seen)
+			return dev->le_seen;
+
+		return dev->bredr_seen;
+	}
+
+	if (dev->bredr)
+		return dev->bredr_seen;
+
+	return dev->le_seen;
+}
+
+static int device_timestamp_cmp(gconstpointer  a, gconstpointer  b)
+{
+	const struct device *deva = a;
+	const struct device *devb = b;
+
+	return device_timestamp(deva) < device_timestamp(devb);
+}
+
+static void load_devices_cache(void)
+{
+	GKeyFile *key_file;
+	gchar **devs;
+	gsize len = 0;
+	unsigned int i;
+
+	key_file = g_key_file_new();
+
+	g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL);
+
+	devs = g_key_file_get_groups(key_file, &len);
+
+	for (i = 0; i < len; i++) {
+		struct device *dev;
+
+		dev = create_device_from_info(key_file, devs[i]);
+		cached_devices = g_slist_prepend(cached_devices, dev);
+	}
+
+	cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp);
+
+	g_strfreev(devs);
+	g_key_file_free(key_file);
+}
+
+static void load_devices_info(bt_bluetooth_ready cb)
+{
+	GKeyFile *key_file;
+	gchar **devs;
+	gsize len = 0;
+	unsigned int i;
+	GSList *keys = NULL;
+	GSList *ltks = NULL;
+	GSList *irks = NULL;
+
+	key_file = g_key_file_new();
+
+	g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL);
+
+	devs = g_key_file_get_groups(key_file, &len);
+
+	for (i = 0; i < len; i++) {
+		struct mgmt_link_key_info *key_info;
+		struct mgmt_ltk_info *ltk_info;
+		struct mgmt_irk_info *irk_info;
+		struct mgmt_ltk_info *slave_ltk_info;
+		struct device *dev;
+
+		dev = create_device_from_info(key_file, devs[i]);
+
+		key_info = get_key_info(key_file, devs[i]);
+		irk_info = get_irk_info(key_file, devs[i]);
+		ltk_info = get_ltk_info(key_file, devs[i], true);
+		slave_ltk_info = get_ltk_info(key_file, devs[i], false);
+
+		/*
+		 * Skip devices that have no permanent keys
+		 * (CSRKs are loaded by create_device_from_info())
+		 */
+		if (!dev->valid_local_csrk && !dev->valid_remote_csrk &&
+						!key_info && !ltk_info &&
+						!slave_ltk_info && !irk_info) {
+			error("Failed to load keys for %s, skipping", devs[i]);
+			free_device(dev);
+			continue;
+		}
+
+		if (key_info) {
+			keys = g_slist_prepend(keys, key_info);
+			dev->bredr_paired = true;
+			dev->bredr_bonded = true;
+		}
+
+		if (irk_info)
+			irks = g_slist_prepend(irks, irk_info);
+
+		if (ltk_info)
+			ltks = g_slist_prepend(ltks, ltk_info);
+
+		if (slave_ltk_info)
+			ltks = g_slist_prepend(ltks, slave_ltk_info);
+
+		if (dev->valid_local_csrk || dev->valid_remote_csrk ||
+				irk_info || ltk_info || slave_ltk_info) {
+			dev->le_paired = true;
+			dev->le_bonded = true;
+		}
+
+		bonded_devices = g_slist_prepend(bonded_devices, dev);
+	}
+
+	load_ltks(ltks);
+	g_slist_free_full(ltks, g_free);
+
+	load_irks(irks);
+	g_slist_free_full(irks, g_free);
+
+	load_link_keys(keys, cb);
+	g_slist_free_full(keys, g_free);
+
+	g_strfreev(devs);
+	g_key_file_free(key_file);
+}
+
+static void set_adapter_class(void)
+{
+	struct mgmt_cp_set_dev_class cp;
+
+	memset(&cp, 0, sizeof(cp));
+
+	/*
+	 * kernel assign the major and minor numbers straight to dev_class[0]
+	 * and dev_class[1] without considering the proper bit shifting.
+	 */
+	cp.major = ADAPTER_MAJOR_CLASS & 0x1f;
+	cp.minor = ADAPTER_MINOR_CLASS << 2;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index, sizeof(cp),
+						&cp, NULL, NULL, NULL) > 0)
+		return;
+
+	error("Failed to set class of device");
+}
+
+static void enable_mps(void)
+{
+	uuid_t uuid, *uuid128;
+
+	sdp_uuid16_create(&uuid, MPS_SVCLASS_ID);
+	uuid128 = sdp_uuid_to_uuid128(&uuid);
+	if (!uuid128)
+		return;
+
+	register_mps(true);
+	adapter.uuids = g_slist_prepend(adapter.uuids, uuid128);
+	add_uuid(0, uuid128);
+}
+
+static void clear_auto_connect_list_complete(uint8_t status,
+							uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS)
+		error("Failed to clear auto connect list: %s (0x%02x)",
+						mgmt_errstr(status), status);
+}
+
+static void clear_auto_connect_list(void)
+{
+	struct mgmt_cp_remove_device cp;
+
+	if (!kernel_conn_control)
+		return;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, sizeof(cp),
+			&cp, clear_auto_connect_list_complete, NULL, NULL) > 0)
+		return;
+
+	error("Could not clear auto connect list");
+}
+
+static void read_info_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_info *rp = param;
+	bt_bluetooth_ready cb = user_data;
+	uint32_t missing_settings;
+	int err;
+
+	DBG("");
+
+	if (status) {
+		error("Failed to read info for index %u: %s (0x%02x)",
+				adapter.index, mgmt_errstr(status), status);
+		err = -EIO;
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Too small read info complete response");
+		err = -EIO;
+		goto failed;
+	}
+
+	if (!bacmp(&rp->bdaddr, BDADDR_ANY)) {
+		error("No Bluetooth address");
+		err = -ENODEV;
+		goto failed;
+	}
+
+	load_adapter_config();
+
+	if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) {
+		bacpy(&adapter.bdaddr, &rp->bdaddr);
+		store_adapter_config();
+	} else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) {
+		error("Bluetooth address mismatch");
+		err = -ENODEV;
+		goto failed;
+	}
+
+	if (adapter.name && g_strcmp0(adapter.name, (const char *) rp->name))
+		set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+
+	set_adapter_class();
+
+	/* Store adapter information */
+	adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
+						(rp->dev_class[2] << 16);
+
+	adapter.supported_settings = le32_to_cpu(rp->supported_settings);
+	adapter.current_settings = le32_to_cpu(rp->current_settings);
+
+	/* TODO: Register all event notification handlers */
+	register_mgmt_handlers();
+
+	clear_uuids();
+	clear_auto_connect_list();
+
+	set_io_capability();
+	set_device_id();
+	enable_mps();
+
+	missing_settings = adapter.current_settings ^
+						adapter.supported_settings;
+
+	if (missing_settings & MGMT_SETTING_SSP)
+		set_mode(MGMT_OP_SET_SSP, 0x01);
+
+	if (missing_settings & MGMT_SETTING_BONDABLE)
+		set_mode(MGMT_OP_SET_BONDABLE, 0x01);
+
+	load_devices_info(cb);
+	load_devices_cache();
+
+	return;
+
+failed:
+	cb(err, NULL);
+}
+
+static void mgmt_index_added_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	bt_bluetooth_ready cb = user_data;
+
+	DBG("index %u", index);
+
+	if (adapter.index != MGMT_INDEX_NONE) {
+		DBG("skip event for index %u", index);
+		return;
+	}
+
+	if (option_index != MGMT_INDEX_NONE && option_index != index) {
+		DBG("skip event for index %u (option %u)", index, option_index);
+		return;
+	}
+
+	adapter.index = index;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL,
+				read_info_complete, cb, NULL) == 0) {
+		cb(-EIO, NULL);
+		return;
+	}
+}
+
+static void mgmt_index_removed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	DBG("index %u", index);
+
+	if (index != adapter.index)
+		return;
+
+	error("Adapter was removed. Exiting.");
+	raise(SIGTERM);
+}
+
+static void read_index_list_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_index_list *rp = param;
+	bt_bluetooth_ready cb = user_data;
+	uint16_t num;
+	int i;
+
+	DBG("");
+
+	if (status) {
+		error("%s: Failed to read index list: %s (0x%02x)", __func__,
+						mgmt_errstr(status), status);
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("%s: Wrong size of read index list response", __func__);
+		goto failed;
+	}
+
+	num = le16_to_cpu(rp->num_controllers);
+
+	DBG("Number of controllers: %u", num);
+
+	if (num * sizeof(uint16_t) + sizeof(*rp) != length) {
+		error("%s: Incorrect pkt size for index list rsp", __func__);
+		goto failed;
+	}
+
+	if (adapter.index != MGMT_INDEX_NONE)
+		return;
+
+	for (i = 0; i < num; i++) {
+		uint16_t index = le16_to_cpu(rp->index[i]);
+
+		if (option_index != MGMT_INDEX_NONE && option_index != index)
+			continue;
+
+		if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL,
+					read_info_complete, cb, NULL) == 0)
+			goto failed;
+
+		adapter.index = index;
+		return;
+	}
+
+	return;
+
+failed:
+	cb(-EIO, NULL);
+}
+
+static void read_version_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_version *rp = param;
+	uint8_t mgmt_version, mgmt_revision;
+	bt_bluetooth_ready cb = user_data;
+
+	DBG("");
+
+	if (status) {
+		error("Failed to read version information: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size response");
+		goto failed;
+	}
+
+	mgmt_version = rp->version;
+	mgmt_revision = le16_to_cpu(rp->revision);
+
+	info("Bluetooth management interface %u.%u initialized",
+						mgmt_version, mgmt_revision);
+
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 3)) {
+		error("Version 1.3 or later of management interface required");
+		goto failed;
+	}
+
+	/* Starting from mgmt 1.7, kernel can handle connection control */
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 7)) {
+		info("Kernel connection control will be used");
+		kernel_conn_control = true;
+	}
+
+	mgmt_register(mgmt_if, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					mgmt_index_added_event, cb, NULL);
+	mgmt_register(mgmt_if, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					mgmt_index_removed_event, NULL, NULL);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+				NULL, read_index_list_complete, cb, NULL) > 0)
+		return;
+
+	error("Failed to read controller index list");
+
+failed:
+	cb(-EIO, NULL);
+}
+
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb)
+{
+	DBG("index %d", index);
+
+	mgmt_if = mgmt_new_default();
+	if (!mgmt_if) {
+		error("Failed to access management interface");
+		return false;
+	}
+
+	if (mgmt_dbg)
+		mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL,
+				read_version_complete, cb, NULL) == 0) {
+		error("Error sending READ_VERSION mgmt command");
+
+		mgmt_unref(mgmt_if);
+		mgmt_if = NULL;
+
+		return false;
+	}
+
+	if (index >= 0)
+		option_index = index;
+
+	return true;
+}
+
+static void shutdown_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	bt_bluetooth_stopped cb = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS)
+		error("Clean controller shutdown failed");
+
+	cb();
+}
+
+bool bt_bluetooth_stop(bt_bluetooth_stopped cb)
+{
+	struct mgmt_mode cp;
+
+	if (adapter.index == MGMT_INDEX_NONE)
+		return false;
+
+	info("Switching controller off");
+
+	memset(&cp, 0, sizeof(cp));
+
+	return mgmt_send(mgmt_if, MGMT_OP_SET_POWERED, adapter.index,
+				sizeof(cp), &cp, shutdown_complete, (void *)cb,
+				NULL) > 0;
+}
+
+void bt_bluetooth_cleanup(void)
+{
+	g_free(adapter.name);
+	adapter.name = NULL;
+
+	mgmt_unref(mgmt_if);
+	mgmt_if = NULL;
+}
+
+static bool set_discoverable(uint8_t mode, uint16_t timeout)
+{
+	struct mgmt_cp_set_discoverable cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+	cp.timeout = cpu_to_le16(timeout);
+
+	DBG("mode %u timeout %u", mode, timeout);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_DISCOVERABLE, adapter.index,
+			sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to set mode discoverable");
+
+	return false;
+}
+
+static uint8_t get_adapter_address(void)
+{
+	uint8_t buf[6];
+
+	bdaddr2android(&adapter.bdaddr, buf);
+
+	send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_name(void)
+{
+	if (!adapter.name)
+		return HAL_STATUS_FAILED;
+
+	adapter_name_changed((uint8_t *) adapter.name);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_class(void)
+{
+	DBG("");
+
+	adapter_class_changed();
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t settings2type(void)
+{
+	bool bredr, le;
+
+	bredr = adapter.current_settings & MGMT_SETTING_BREDR;
+	le = adapter.current_settings & MGMT_SETTING_LE;
+
+	if (bredr && le)
+		return HAL_TYPE_DUAL;
+
+	if (bredr && !le)
+		return HAL_TYPE_BREDR;
+
+	if (!bredr && le)
+		return HAL_TYPE_LE;
+
+	return 0;
+}
+
+static uint8_t get_adapter_type(void)
+{
+	uint8_t type;
+
+	DBG("");
+
+	type = settings2type();
+
+	if (!type)
+		return HAL_STATUS_FAILED;
+
+	send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_service_rec(void)
+{
+	DBG("Not implemented");
+
+	/* TODO: Add implementation */
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_adapter_scan_mode(void)
+{
+	DBG("");
+
+	scan_mode_changed();
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_bonded_devices(void)
+{
+	uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)];
+	int i = 0;
+	GSList *l;
+
+	DBG("");
+
+	for (l = bonded_devices; l; l = g_slist_next(l)) {
+		struct device *dev = l->data;
+
+		get_device_android_addr(dev, buf + (i * sizeof(bdaddr_t)));
+		i++;
+	}
+
+	send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES,
+						i * sizeof(bdaddr_t), buf);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_discoverable_timeout(void)
+{
+	send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+					sizeof(adapter.discoverable_timeout),
+					&adapter.discoverable_timeout);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void prepare_le_features(uint8_t *le_features)
+{
+	le_features[0] = !!(adapter.current_settings & MGMT_SETTING_PRIVACY);
+	le_features[1] = adapter.max_advert_instance;
+	le_features[2] = adapter.rpa_offload_supported;
+	le_features[3] = adapter.max_irk_list_size;
+	le_features[4] = adapter.max_scan_filters_supported;
+	/* lo byte */
+	le_features[5] = adapter.scan_result_storage_size;
+	/* hi byte */
+	le_features[6] = adapter.scan_result_storage_size >> 8;
+	le_features[7] = adapter.activity_energy_info_supported;
+}
+
+static uint8_t get_adapter_le_features(void)
+{
+	uint8_t le_features[8];
+
+	prepare_le_features(le_features);
+
+	send_adapter_property(HAL_PROP_ADAPTER_LOCAL_LE_FEAT,
+					sizeof(le_features), le_features);
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_adapter_prop *cmd = buf;
+	uint8_t status;
+
+	switch (cmd->type) {
+	case HAL_PROP_ADAPTER_ADDR:
+		status = get_adapter_address();
+		break;
+	case HAL_PROP_ADAPTER_NAME:
+		status = get_adapter_name();
+		break;
+	case HAL_PROP_ADAPTER_UUIDS:
+		status = get_adapter_uuids();
+		break;
+	case HAL_PROP_ADAPTER_CLASS:
+		status = get_adapter_class();
+		break;
+	case HAL_PROP_ADAPTER_TYPE:
+		status = get_adapter_type();
+		break;
+	case HAL_PROP_ADAPTER_SERVICE_REC:
+		status = get_adapter_service_rec();
+		break;
+	case HAL_PROP_ADAPTER_SCAN_MODE:
+		status = get_adapter_scan_mode();
+		break;
+	case HAL_PROP_ADAPTER_BONDED_DEVICES:
+		status = get_adapter_bonded_devices();
+		break;
+	case HAL_PROP_ADAPTER_DISC_TIMEOUT:
+		status = get_adapter_discoverable_timeout();
+		break;
+	case HAL_PROP_ADAPTER_LOCAL_LE_FEAT:
+		status = get_adapter_le_features();
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to get adapter property (type %u status %u)",
+							cmd->type, status);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+									status);
+}
+
+static void get_adapter_properties(void)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_adapter_props_changed *ev = (void *) buf;
+	uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)];
+	uint128_t uuids[g_slist_length(adapter.uuids)];
+	uint8_t android_bdaddr[6];
+	uint8_t le_features[8];
+	uint8_t type, mode;
+	size_t size, i;
+	GSList *l;
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	ev->num_props = 0;
+
+	bdaddr2android(&adapter.bdaddr, &android_bdaddr);
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR,
+					sizeof(android_bdaddr), android_bdaddr);
+	ev->num_props++;
+
+	if (adapter.name) {
+		size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME,
+					strlen(adapter.name), adapter.name);
+		ev->num_props++;
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS,
+				sizeof(adapter.dev_class), &adapter.dev_class);
+	ev->num_props++;
+
+	type = settings2type();
+	if (type) {
+		size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE,
+							sizeof(type), &type);
+		ev->num_props++;
+	}
+
+	mode = settings2scan_mode();
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE,
+							sizeof(mode), &mode);
+	ev->num_props++;
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT,
+					sizeof(adapter.discoverable_timeout),
+					&adapter.discoverable_timeout);
+	ev->num_props++;
+
+	for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) {
+		struct device *dev = l->data;
+
+		get_device_android_addr(dev, bonded + (i * sizeof(bdaddr_t)));
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES,
+						sizeof(bonded), bonded);
+	ev->num_props++;
+
+	for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) {
+		uuid_t *uuid = l->data;
+
+		memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t));
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids),
+									uuids);
+	ev->num_props++;
+
+	prepare_le_features(le_features);
+	size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_LOCAL_LE_FEAT,
+					sizeof(le_features), le_features);
+
+	ev->num_props++;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_EV_ADAPTER_PROPS_CHANGED, size, buf);
+}
+
+static void cancel_pending_confirm_name(gpointer data, gpointer user_data)
+{
+	struct device *dev = data;
+
+	mgmt_cancel(mgmt_if, dev->confirm_id);
+	dev->confirm_id = 0;
+}
+
+static bool stop_discovery(uint8_t type)
+{
+	struct mgmt_cp_stop_discovery cp;
+
+	cp.type = get_supported_discovery_type() & type;
+
+	DBG("type=0x%x", cp.type);
+
+	if (cp.type == SCAN_TYPE_NONE)
+		return false;
+
+	/* Lets drop all confirm name request as we don't need it anymore */
+	g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL);
+
+	if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index,
+					sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to stop discovery");
+	return false;
+}
+
+struct adv_user_data {
+	bt_le_set_advertising_done cb;
+	void *user_data;
+};
+
+static void set_advertising_cb(uint8_t status, uint16_t length,
+			const void *param, void *user_data)
+{
+	struct adv_user_data *data = user_data;
+
+	DBG("");
+
+	if (status)
+		error("Failed to set adverising %s (0x%02x))",
+						mgmt_errstr(status), status);
+
+	data->cb(status, data->user_data);
+}
+
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+							 void *user_data)
+{
+	struct adv_user_data *data;
+	uint8_t adv = advertising ? 0x01 : 0x00;
+
+	data = new0(struct adv_user_data, 1);
+	data->cb = cb;
+	data->user_data = user_data;
+
+	if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index,
+			sizeof(adv), &adv, set_advertising_cb, data, free) > 0)
+		return true;
+
+	error("Failed to set advertising");
+	free(data);
+	return false;
+}
+
+bool bt_le_register(bt_le_device_found cb)
+{
+	if (gatt_device_found_cb)
+		return false;
+
+	gatt_device_found_cb = cb;
+
+	return true;
+}
+
+void bt_le_unregister(void)
+{
+	gatt_device_found_cb = NULL;
+}
+
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
+{
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+		return false;
+
+	adapter.le_scanning = false;
+
+	if (adapter.cur_discovery_type != SCAN_TYPE_LE) {
+		if (cb)
+			cb();
+
+		return true;
+	}
+
+	if (!stop_discovery(SCAN_TYPE_LE))
+		return false;
+
+	gatt_discovery_stopped_cb = cb;
+	adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+	return true;
+}
+
+bool bt_le_discovery_start(void)
+{
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+		return false;
+
+	adapter.le_scanning = true;
+
+	/*
+	 * If core is discovering - just set expected next scan type.
+	 * It will be triggered in case current scan session is almost done
+	 * i.e. we missed LE phase in interleaved scan, or we're trying to
+	 * connect to device that was already discovered.
+	 */
+	if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
+		adapter.exp_discovery_type = SCAN_TYPE_LE;
+		return true;
+	}
+
+	if (start_discovery(SCAN_TYPE_LE))
+		return true;
+
+	return false;
+}
+
+struct read_rssi_user_data {
+	bt_read_device_rssi_done cb;
+	void *user_data;
+};
+
+static void read_device_rssi_cb(uint8_t status, uint16_t length,
+			const void *param, void *user_data)
+{
+	const struct mgmt_rp_get_conn_info *rp = param;
+	struct read_rssi_user_data *data = user_data;
+
+	DBG("");
+
+	if (status)
+		error("Failed to get conn info: %s (0x%02x))",
+						mgmt_errstr(status), status);
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of get conn info response");
+		return;
+	}
+
+	data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data);
+}
+
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+							void *user_data)
+{
+	struct device *dev;
+	struct read_rssi_user_data *data;
+	struct mgmt_cp_get_conn_info cp;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr));
+	cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+
+	data = new0(struct read_rssi_user_data, 1);
+	data->cb = cb;
+	data->user_data = user_data;
+
+	if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index,
+			sizeof(cp), &cp, read_device_rssi_cb, data, free)) {
+		free(data);
+		error("Failed to get conn info");
+		return false;
+	}
+
+	return true;
+}
+
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+					uint32_t *sign_cnt, bool *authenticated)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return false;
+
+	if (local && dev->valid_local_csrk) {
+		if (key)
+			memcpy(key, dev->local_csrk, 16);
+
+		if (sign_cnt)
+			*sign_cnt = dev->local_sign_cnt;
+
+		if (authenticated)
+			*authenticated = dev->local_csrk_auth;
+	} else if (!local && dev->valid_remote_csrk) {
+		if (key)
+			memcpy(key, dev->remote_csrk, 16);
+
+		if (sign_cnt)
+			*sign_cnt = dev->remote_sign_cnt;
+
+		if (authenticated)
+			*authenticated = dev->remote_csrk_auth;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+static void store_sign_counter(struct device *dev, bool local)
+{
+	const char *sign_cnt_s;
+	uint32_t sign_cnt;
+	GKeyFile *key_file;
+
+	gsize length = 0;
+	char addr[18];
+	char *data;
+
+	key_file = g_key_file_new();
+	if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+		g_key_file_free(key_file);
+		return;
+	}
+
+	ba2str(&dev->bdaddr, addr);
+
+	sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter";
+	sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt;
+
+	g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(DEVICES_FILE, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (!dev)
+		return;
+
+	if (local)
+		dev->local_sign_cnt = val;
+	else
+		dev->remote_sign_cnt = val;
+
+	store_sign_counter(dev, local);
+}
+
+static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
+{
+	const uint8_t *mode = buf;
+	bool conn, disc, cur_conn, cur_disc;
+
+	if (len != sizeof(*mode)) {
+		error("Invalid set scan mode size (%u bytes), terminating",
+								len);
+		raise(SIGTERM);
+		return HAL_STATUS_FAILED;
+	}
+
+	cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
+	cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
+
+	DBG("connectable %u discoverable %d mode %u", cur_conn, cur_disc,
+								*mode);
+
+	switch (*mode) {
+	case HAL_ADAPTER_SCAN_MODE_NONE:
+		if (!cur_conn && !cur_disc)
+			goto done;
+
+		conn = false;
+		disc = false;
+		break;
+	case HAL_ADAPTER_SCAN_MODE_CONN:
+		if (cur_conn && !cur_disc)
+			goto done;
+
+		conn = true;
+		disc = false;
+		break;
+	case HAL_ADAPTER_SCAN_MODE_CONN_DISC:
+		if (cur_conn && cur_disc)
+			goto done;
+
+		conn = true;
+		disc = true;
+		break;
+	default:
+		return HAL_STATUS_FAILED;
+	}
+
+	if (cur_conn != conn) {
+		if (!set_mode(MGMT_OP_SET_CONNECTABLE, conn ? 0x01 : 0x00))
+			return HAL_STATUS_FAILED;
+	}
+
+	if (cur_disc != disc && conn) {
+		if (!set_discoverable(disc ? 0x01 : 0x00, 0))
+			return HAL_STATUS_FAILED;
+	}
+
+	return HAL_STATUS_SUCCESS;
+
+done:
+	/* Android expects property changed callback */
+	scan_mode_changed();
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_set_adapter_prop *cmd = buf;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid set adapter prop cmd (0x%x), terminating",
+								cmd->type);
+		raise(SIGTERM);
+		return;
+	}
+
+	switch (cmd->type) {
+	case HAL_PROP_ADAPTER_SCAN_MODE:
+		status = set_adapter_scan_mode(cmd->val, cmd->len);
+		break;
+	case HAL_PROP_ADAPTER_NAME:
+		status = set_adapter_name(cmd->val, cmd->len);
+		break;
+	case HAL_PROP_ADAPTER_DISC_TIMEOUT:
+		status = set_adapter_discoverable_timeout(cmd->val, cmd->len);
+		break;
+	default:
+		DBG("Unhandled property type 0x%x", cmd->type);
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to set adapter property (type %u status %u)",
+							cmd->type, status);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+									status);
+}
+
+static void pair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_pair_device *rp = param;
+	struct device *dev;
+
+	DBG("status %u", status);
+
+	dev = find_device(&rp->addr.bdaddr);
+	if (!dev)
+		return;
+
+	/*
+	 * Update pairing and paired status. Bonded status will be updated once
+	 * any link key come
+	 */
+	update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false,
+								!status, false);
+
+	if (status == MGMT_STATUS_SUCCESS)
+		queue_foreach(paired_cb_list, send_paired_notification, dev);
+}
+
+static uint8_t select_device_bearer(struct device *dev)
+{
+	uint8_t res;
+
+	if (dev->bredr && dev->le) {
+		if (dev->le_seen > dev->bredr_seen)
+			res = dev->bdaddr_type;
+		else
+			res = BDADDR_BREDR;
+	} else {
+		res = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+	}
+
+	DBG("Selected bearer %d", res);
+
+	return res;
+}
+
+uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr)
+{
+	 struct device *dev;
+
+	dev = find_device(bdaddr);
+	if (!dev)
+		return BDADDR_BREDR;
+
+	return select_device_bearer(dev);
+}
+
+static void handle_create_bond_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_create_bond *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	struct mgmt_cp_pair_device cp;
+
+	dev = get_device_android(cmd->bdaddr);
+
+	cp.io_cap = DEFAULT_IO_CAPABILITY;
+	cp.addr.type = select_device_bearer(dev);
+	bacpy(&cp.addr.bdaddr, &dev->bdaddr);
+
+	/* TODO: Handle transport parameter */
+	if (cmd->transport > BT_TRANSPORT_LE) {
+		status = HAL_STATUS_INVALID;
+		goto fail;
+	}
+
+	if (device_is_paired(dev, cp.addr.type)) {
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp),
+				&cp, pair_device_complete, NULL, NULL) == 0) {
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+	update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false,
+									false);
+
+fail:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+									status);
+}
+
+static void handle_cancel_bond_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_cancel_bond *cmd = buf;
+	struct mgmt_addr_info cp;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cp.type = select_device_bearer(dev);
+	bacpy(&cp.bdaddr, &dev->bdaddr);
+
+	if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index,
+				sizeof(cp), &cp, NULL, NULL, NULL) == 0) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+									status);
+}
+
+static void send_unpaired_notification(void *data, void *user_data)
+{
+	bt_unpaired_device_cb cb = data;
+	struct mgmt_addr_info *addr = user_data;
+
+	cb(&addr->bdaddr);
+}
+
+static void unpair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_unpair_device *rp = param;
+	struct device *dev;
+
+	DBG("status %u", status);
+
+	if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED)
+		return;
+
+	dev = find_device(&rp->addr.bdaddr);
+	if (!dev)
+		return;
+
+	update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false,
+								false, false);
+
+	/* Cast rp->addr to (void *) since queue_foreach don't take const */
+
+	if (!dev->le_paired && !dev->bredr_paired)
+		queue_foreach(unpaired_cb_list, send_unpaired_notification,
+							(void *)&rp->addr);
+}
+
+static void handle_remove_bond_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_remove_bond *cmd = buf;
+	struct mgmt_cp_unpair_device cp;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cp.disconnect = 1;
+	bacpy(&cp.addr.bdaddr, &dev->bdaddr);
+
+	if (dev->le_paired) {
+		cp.addr.type = dev->bdaddr_type;
+
+		if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+					sizeof(cp), &cp, unpair_device_complete,
+					NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	if (dev->bredr_paired) {
+		cp.addr.type = BDADDR_BREDR;
+
+		if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+					sizeof(cp), &cp, unpair_device_complete,
+					NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+									status);
+}
+
+static void handle_pin_reply_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pin_reply *cmd = buf;
+	uint8_t status;
+	bdaddr_t bdaddr;
+	char addr[18];
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	ba2str(&bdaddr, addr);
+
+	DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len);
+
+	if (!cmd->accept && cmd->pin_len) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	if (cmd->accept) {
+		struct mgmt_cp_pin_code_reply rp;
+
+		memset(&rp, 0, sizeof(rp));
+
+		bacpy(&rp.addr.bdaddr, &bdaddr);
+		rp.addr.type = BDADDR_BREDR;
+		rp.pin_len = cmd->pin_len;
+		memcpy(rp.pin_code, cmd->pin_code, rp.pin_len);
+
+		if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index,
+				sizeof(rp), &rp, NULL, NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	} else {
+		struct mgmt_cp_pin_code_neg_reply rp;
+
+		bacpy(&rp.addr.bdaddr, &bdaddr);
+		rp.addr.type = BDADDR_BREDR;
+
+		if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY,
+						adapter.index, sizeof(rp), &rp,
+						NULL, NULL, NULL) == 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+									status);
+}
+
+static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, uint8_t type,
+								bool accept)
+{
+	struct mgmt_addr_info cp;
+	uint16_t opcode;
+
+	if (accept)
+		opcode = MGMT_OP_USER_CONFIRM_REPLY;
+	else
+		opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.type = type;
+
+	if (mgmt_reply(mgmt_if, opcode, adapter.index, sizeof(cp), &cp,
+							NULL, NULL, NULL) > 0)
+		return HAL_STATUS_SUCCESS;
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, uint8_t type,
+						bool accept, uint32_t passkey)
+{
+	unsigned int id;
+
+	if (accept) {
+		struct mgmt_cp_user_passkey_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = type;
+		cp.passkey = cpu_to_le32(passkey);
+
+		id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_REPLY,
+						adapter.index, sizeof(cp), &cp,
+						NULL, NULL, NULL);
+	} else {
+		struct mgmt_cp_user_passkey_neg_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = type;
+
+		id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+						adapter.index, sizeof(cp), &cp,
+						NULL, NULL, NULL);
+	}
+
+	if (id == 0)
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_ssp_reply_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_ssp_reply *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	char addr[18];
+
+	/* TODO should parameters sanity be verified here? */
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev)
+		return;
+
+	ba2str(&dev->bdaddr, addr);
+
+	DBG("%s variant %u accept %u", addr, cmd->ssp_variant, cmd->accept);
+
+	switch (cmd->ssp_variant) {
+	case HAL_SSP_VARIANT_CONFIRM:
+	case HAL_SSP_VARIANT_CONSENT:
+		status = user_confirm_reply(&dev->bdaddr,
+						select_device_bearer(dev),
+						cmd->accept);
+		break;
+	case HAL_SSP_VARIANT_ENTRY:
+		status = user_passkey_reply(&dev->bdaddr,
+						select_device_bearer(dev),
+						cmd->accept, cmd->passkey);
+		break;
+	case HAL_SSP_VARIANT_NOTIF:
+		status = HAL_STATUS_SUCCESS;
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		break;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+									status);
+}
+
+static void handle_get_remote_services_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_services *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	status = browse_remote_sdp(&addr);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_SERVICES, status);
+}
+
+static uint8_t get_device_uuids(struct device *dev)
+{
+	send_device_uuids_notif(dev);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_class(struct device *dev)
+{
+	send_device_property(dev, HAL_PROP_DEVICE_CLASS, sizeof(dev->class),
+								&dev->class);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_type(struct device *dev)
+{
+	uint8_t type = get_device_android_type(dev);
+
+	send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_service_rec(struct device *dev)
+{
+	DBG("Not implemented");
+
+	/* TODO */
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_friendly_name(struct device *dev)
+{
+	if (!dev->friendly_name)
+		return HAL_STATUS_FAILED;
+
+	send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME,
+				strlen(dev->friendly_name), dev->friendly_name);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_rssi(struct device *dev)
+{
+	if (!dev->rssi)
+		return HAL_STATUS_FAILED;
+
+	send_device_property(dev, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi),
+								&dev->rssi);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_version_info(struct device *dev)
+{
+	DBG("Not implemented");
+
+	/* TODO */
+
+	return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_timestamp(struct device *dev)
+{
+	uint32_t timestamp;
+
+	timestamp = device_timestamp(dev);
+
+	send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP, sizeof(timestamp),
+								&timestamp);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void get_remote_device_props(struct device *dev)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_remote_device_props *ev = (void *) buf;
+	uint128_t uuids[g_slist_length(dev->uuids)];
+	uint8_t android_type;
+	uint32_t timestamp;
+	size_t size, i;
+	GSList *l;
+
+	memset(buf, 0, sizeof(buf));
+
+	size = sizeof(*ev);
+
+	ev->status = HAL_STATUS_SUCCESS;
+	get_device_android_addr(dev, ev->bdaddr);
+
+	android_type = get_device_android_type(dev);
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+					sizeof(android_type), &android_type);
+	ev->num_props++;
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+					sizeof(dev->class), &dev->class);
+	ev->num_props++;
+
+	if (dev->rssi) {
+		size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+						sizeof(dev->rssi), &dev->rssi);
+		ev->num_props++;
+	}
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+						strlen(dev->name), dev->name);
+	ev->num_props++;
+
+	if (dev->friendly_name) {
+		size += fill_hal_prop(buf + size,
+					HAL_PROP_DEVICE_FRIENDLY_NAME,
+					strlen(dev->friendly_name),
+					dev->friendly_name);
+		ev->num_props++;
+	}
+
+	for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++)
+		memcpy(&uuids[i], l->data, sizeof(uint128_t));
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids),
+									uuids);
+	ev->num_props++;
+
+	timestamp = get_device_timestamp(dev);
+
+	size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP,
+						sizeof(timestamp), &timestamp);
+	ev->num_props++;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static void send_bonded_devices_props(void)
+{
+	GSList *l;
+
+	for (l = bonded_devices; l; l = g_slist_next(l)) {
+		struct device *dev = l->data;
+
+		get_remote_device_props(dev);
+	}
+}
+
+static void handle_enable_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	/*
+	 * Framework expects all properties to be emitted while enabling
+	 * adapter
+	 */
+	get_adapter_properties();
+
+	/* Sent also properties of bonded devices */
+	send_bonded_devices_props();
+
+	if (adapter.current_settings & MGMT_SETTING_POWERED) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status);
+}
+
+static void handle_disable_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Cancel all pending requests. Need it in case of ongoing paring */
+	mgmt_cancel_index(mgmt_if, adapter.index);
+
+	if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status);
+}
+
+static void handle_get_adapter_props_cmd(const void *buf, uint16_t len)
+{
+	get_adapter_properties();
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS);
+}
+
+static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_device_props *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	get_remote_device_props(dev);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROPS, status);
+}
+
+static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_device_prop *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	switch (cmd->type) {
+	case HAL_PROP_DEVICE_NAME:
+		status = get_device_name(dev);
+		break;
+	case HAL_PROP_DEVICE_UUIDS:
+		status = get_device_uuids(dev);
+		break;
+	case HAL_PROP_DEVICE_CLASS:
+		status = get_device_class(dev);
+		break;
+	case HAL_PROP_DEVICE_TYPE:
+		status = get_device_type(dev);
+		break;
+	case HAL_PROP_DEVICE_SERVICE_REC:
+		status = get_device_service_rec(dev);
+		break;
+	case HAL_PROP_DEVICE_FRIENDLY_NAME:
+		status = get_device_friendly_name(dev);
+		break;
+	case HAL_PROP_DEVICE_RSSI:
+		status = get_device_rssi(dev);
+		break;
+	case HAL_PROP_DEVICE_VERSION_INFO:
+		status = get_device_version_info(dev);
+		break;
+	case HAL_PROP_DEVICE_TIMESTAMP:
+		status = get_device_timestamp(dev);
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to get device property (type %u status %u)",
+							cmd->type, status);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROP, status);
+}
+
+static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val,
+								uint16_t len)
+{
+	DBG("");
+
+	g_free(dev->friendly_name);
+	dev->friendly_name = g_strndup((const char *) val, len);
+
+	if (dev->bredr_paired || dev->le_paired)
+		store_device_info(dev, DEVICES_FILE);
+	else
+		store_device_info(dev, CACHE_FILE);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t set_device_version_info(struct device *dev)
+{
+	DBG("Not implemented");
+
+	/* TODO */
+
+	return HAL_STATUS_FAILED;
+}
+
+static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_set_remote_device_prop *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid set remote device prop cmd (0x%x), terminating",
+								cmd->type);
+		raise(SIGTERM);
+		return;
+	}
+
+	dev = find_device_android(cmd->bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	switch (cmd->type) {
+	case HAL_PROP_DEVICE_FRIENDLY_NAME:
+		status = set_device_friendly_name(dev, cmd->val, cmd->len);
+		break;
+	case HAL_PROP_DEVICE_VERSION_INFO:
+		status = set_device_version_info(dev);
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		error("Failed to set device property (type %u status %u)",
+							cmd->type, status);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_SET_REMOTE_DEVICE_PROP, status);
+}
+
+static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_remote_service_rec *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	status = find_remote_sdp_rec(&addr, cmd->uuid);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_SERVICE_REC, status);
+}
+
+static void handle_start_discovery_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_NOT_READY;
+		goto failed;
+	}
+
+	switch (adapter.cur_discovery_type) {
+	case SCAN_TYPE_DUAL:
+	case SCAN_TYPE_BREDR:
+		break;
+	case SCAN_TYPE_NONE:
+		if (!start_discovery(SCAN_TYPE_DUAL)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case SCAN_TYPE_LE:
+		if (get_supported_discovery_type() == SCAN_TYPE_LE)
+			break;
+
+		if (!stop_discovery(SCAN_TYPE_LE)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		adapter.exp_discovery_type = SCAN_TYPE_DUAL;
+		break;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY,
+									status);
+}
+
+static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
+{
+	uint8_t status;
+
+	if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+		status = HAL_STATUS_NOT_READY;
+		goto failed;
+	}
+
+	switch (adapter.cur_discovery_type) {
+	case SCAN_TYPE_NONE:
+		break;
+	case SCAN_TYPE_LE:
+		if (get_supported_discovery_type() != SCAN_TYPE_LE)
+			break;
+
+		if (adapter.exp_discovery_type == SCAN_TYPE_LE) {
+			status = HAL_STATUS_BUSY;
+			goto failed;
+		}
+
+		if (!stop_discovery(SCAN_TYPE_LE)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case SCAN_TYPE_DUAL:
+	case SCAN_TYPE_BREDR:
+		if (!stop_discovery(SCAN_TYPE_DUAL)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		if (adapter.exp_discovery_type != SCAN_TYPE_LE)
+			adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+		break;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY,
+									status);
+}
+
+static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_dut_mode_conf *cmd = buf;
+	char path[FILENAME_MAX];
+	uint8_t status;
+	int fd, ret;
+
+	DBG("enable %u", cmd->enable);
+
+	snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index);
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->enable)
+		ret = write(fd, "1", sizeof("1"));
+	else
+		ret = write(fd, "0", sizeof("0"));
+
+	if (ret < 0)
+		status = HAL_STATUS_FAILED;
+	else
+		status = HAL_STATUS_SUCCESS;
+
+	close(fd);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+									status);
+}
+
+static void handle_dut_mode_send_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_dut_mode_send *cmd = buf;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid dut mode send cmd, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_le_test_mode_cmd(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_le_test_mode *cmd = buf;
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid le test mode cmd, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	error("le_test_mode not supported (cmd opcode %u)", cmd->opcode);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_get_connection_state(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_get_connection_state *cmd = buf;
+	struct hal_rsp_get_connection_state rsp;
+	struct device *dev;
+	char address[18];
+	bdaddr_t bdaddr;
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	ba2str(&bdaddr, address);
+
+	dev = find_device_android(cmd->bdaddr);
+	if (dev && dev->connected)
+		rsp.connection_state = 1;
+	else
+		rsp.connection_state = 0;
+
+	DBG("%s %u", address, rsp.connection_state);
+
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_OP_GET_CONNECTION_STATE, sizeof(rsp), &rsp,
+				-1);
+}
+
+static void handle_read_energy_info(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_ENABLE */
+	{ handle_enable_cmd, false, 0 },
+	/* HAL_OP_DISABLE */
+	{ handle_disable_cmd, false, 0 },
+	/* HAL_OP_GET_ADAPTER_PROPS */
+	{ handle_get_adapter_props_cmd, false, 0 },
+	/* HAL_OP_GET_ADAPTER_PROP */
+	{ handle_get_adapter_prop_cmd, false,
+				sizeof(struct hal_cmd_get_adapter_prop) },
+	/* HAL_OP_SET_ADAPTER_PROP */
+	{ handle_set_adapter_prop_cmd, true,
+				sizeof(struct hal_cmd_set_adapter_prop) },
+	/* HAL_OP_GET_REMOTE_DEVICE_PROPS */
+	{ handle_get_remote_device_props_cmd, false,
+			sizeof(struct hal_cmd_get_remote_device_props) },
+	/* HAL_OP_GET_REMOTE_DEVICE_PROP */
+	{ handle_get_remote_device_prop_cmd, false,
+				sizeof(struct hal_cmd_get_remote_device_prop) },
+	/* HAL_OP_SET_REMOTE_DEVICE_PROP */
+	{ handle_set_remote_device_prop_cmd, true,
+				sizeof(struct hal_cmd_set_remote_device_prop) },
+	/* HAL_OP_GET_REMOTE_SERVICE_REC */
+	{ handle_get_remote_service_rec_cmd, false,
+				sizeof(struct hal_cmd_get_remote_service_rec) },
+	/* HAL_OP_GET_REMOTE_SERVICES */
+	{ handle_get_remote_services_cmd, false,
+				sizeof(struct hal_cmd_get_remote_services) },
+	/* HAL_OP_START_DISCOVERY */
+	{ handle_start_discovery_cmd, false, 0 },
+	/* HAL_OP_CANCEL_DISCOVERY */
+	{ handle_cancel_discovery_cmd, false, 0 },
+	/* HAL_OP_CREATE_BOND */
+	{ handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) },
+	/* HAL_OP_REMOVE_BOND */
+	{ handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) },
+	/* HAL_OP_CANCEL_BOND */
+	{handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) },
+	/* HAL_OP_PIN_REPLY */
+	{ handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) },
+	/* HAL_OP_SSP_REPLY */
+	{ handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) },
+	/* HAL_OP_DUT_MODE_CONF */
+	{ handle_dut_mode_conf_cmd, false,
+					sizeof(struct hal_cmd_dut_mode_conf) },
+	/* HAL_OP_DUT_MODE_SEND */
+	{ handle_dut_mode_send_cmd, true,
+					sizeof(struct hal_cmd_dut_mode_send) },
+	/* HAL_OP_LE_TEST_MODE */
+	{ handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) },
+	/* HAL_OP_GET_CONNECTION_STATE */
+	{ handle_get_connection_state, false,
+				sizeof(struct hal_cmd_get_connection_state) },
+	/* HAL_OP_READ_ENERGY_INFO */
+	{ handle_read_energy_info, false, 0 },
+};
+
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
+{
+	uint32_t missing_settings;
+
+	DBG("mode 0x%x", mode);
+
+	unpaired_cb_list = queue_new();
+	paired_cb_list = queue_new();
+
+	missing_settings = adapter.current_settings ^
+						adapter.supported_settings;
+
+	switch (mode) {
+	case HAL_MODE_DEFAULT:
+		if (missing_settings & MGMT_SETTING_BREDR)
+			set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+		if (missing_settings & MGMT_SETTING_LE)
+			set_mode(MGMT_OP_SET_LE, 0x01);
+		break;
+	case HAL_MODE_LE:
+		/* Fail if controller does not support LE */
+		if (!(adapter.supported_settings & MGMT_SETTING_LE)) {
+			error("LE Mode not supported by controller");
+			goto failed;
+		}
+
+		/* If LE it is not yet enabled then enable it */
+		if (!(adapter.current_settings & MGMT_SETTING_LE))
+			set_mode(MGMT_OP_SET_LE, 0x01);
+
+		/* Disable BR/EDR if it is enabled */
+		if (adapter.current_settings & MGMT_SETTING_BREDR)
+			set_mode(MGMT_OP_SET_BREDR, 0x00);
+		break;
+	case HAL_MODE_BREDR:
+		/* Fail if controller does not support BR/EDR */
+		if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) {
+			error("BR/EDR Mode not supported");
+			goto failed;
+		}
+
+		/* Enable BR/EDR if it is not enabled */
+		if (missing_settings & MGMT_SETTING_BREDR)
+			set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+		/*
+		 * According to Core Spec 4.0 host should not disable LE in
+		 * controller if it was enabled (Vol 2. Part E. 7.3.79).
+		 * Core Spec 4.1 removed this limitation and chips seem to be
+		 * handling this just fine anyway.
+		 */
+		if (adapter.current_settings & MGMT_SETTING_LE)
+			set_mode(MGMT_OP_SET_LE, 0x00);
+		break;
+	default:
+		error("Unknown mode 0x%x", mode);
+		goto failed;
+	}
+
+	/* Requested mode is set now, let's enable secure connection */
+	if (missing_settings & MGMT_SETTING_SECURE_CONN)
+		set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
+
+	/* Set initial default name */
+	if (!adapter.name) {
+		adapter.name = g_strdup(bt_config_get_model());
+		set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+	}
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+
+failed:
+	queue_destroy(unpaired_cb_list, NULL);
+	unpaired_cb_list = NULL;
+	queue_destroy(paired_cb_list, NULL);
+	paired_cb_list = NULL;
+
+	return false;
+}
+
+void bt_bluetooth_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(bonded_devices, (GDestroyNotify) free_device);
+	bonded_devices = NULL;
+
+	g_slist_free_full(cached_devices, (GDestroyNotify) free_device);
+	cached_devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+	hal_ipc = NULL;
+
+	queue_destroy(unpaired_cb_list, NULL);
+	unpaired_cb_list = NULL;
+
+	queue_destroy(paired_cb_list, NULL);
+	paired_cb_list = NULL;
+}
diff --git a/bluez/repo/android/bluetooth.h b/bluez/repo/android/bluetooth.h
new file mode 100644
index 0000000..4b17209
--- /dev/null
+++ b/bluez/repo/android/bluetooth.h
@@ -0,0 +1,90 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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
+ *
+ */
+
+typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
+
+typedef void (*bt_bluetooth_stopped)(void);
+bool bt_bluetooth_stop(bt_bluetooth_stopped cb);
+
+void bt_bluetooth_cleanup(void);
+
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode);
+void bt_bluetooth_unregister(void);
+
+int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
+void bt_adapter_remove_record(uint32_t handle);
+
+typedef void (*bt_le_device_found)(const bdaddr_t *addr, int rssi,
+					uint16_t eir_len, const void *eir,
+					bool connectable, bool bonded);
+bool bt_le_register(bt_le_device_found cb);
+void bt_le_unregister(void);
+
+bool bt_le_discovery_start(void);
+
+typedef void (*bt_le_discovery_stopped)(void);
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
+
+typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data);
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+							void *user_data);
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+bool bt_is_device_le(const bdaddr_t *addr);
+uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr);
+
+const char *bt_get_adapter_name(void);
+bool bt_device_is_bonded(const bdaddr_t *bdaddr);
+bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids);
+
+typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr,
+						int8_t rssi, void *user_data);
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+							void *user_data);
+
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+				uint32_t *sign_cnt, bool *authenticated);
+
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val);
+
+void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value);
+
+uint16_t bt_get_gatt_ccc(const bdaddr_t *addr);
+
+const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type);
+
+bool bt_kernel_conn_control(void);
+
+bool bt_auto_connect_add(const bdaddr_t *addr);
+
+void bt_auto_connect_remove(const bdaddr_t *addr);
+
+typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr);
+bool bt_unpaired_register(bt_unpaired_device_cb cb);
+void bt_unpaired_unregister(bt_unpaired_device_cb cb);
+
+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);
diff --git a/bluez/repo/android/bluetoothd-snoop.c b/bluez/repo/android/bluetoothd-snoop.c
new file mode 100644
index 0000000..7526782
--- /dev/null
+++ b/bluez/repo/android/bluetoothd-snoop.c
@@ -0,0 +1,262 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if defined(ANDROID)
+#include <sys/capability.h>
+#endif
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/btsnoop.h"
+#include "src/log.h"
+
+#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log"
+
+static struct btsnoop *snoop = NULL;
+static uint8_t monitor_buf[BTSNOOP_MAX_PACKET_SIZE];
+static int monitor_fd = -1;
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static uint32_t get_flags_from_opcode(uint16_t opcode)
+{
+	switch (opcode) {
+	case BTSNOOP_OPCODE_NEW_INDEX:
+	case BTSNOOP_OPCODE_DEL_INDEX:
+		break;
+	case BTSNOOP_OPCODE_COMMAND_PKT:
+		return 0x02;
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		return 0x03;
+	case BTSNOOP_OPCODE_ACL_TX_PKT:
+		return 0x00;
+	case BTSNOOP_OPCODE_ACL_RX_PKT:
+		return 0x01;
+	case BTSNOOP_OPCODE_SCO_TX_PKT:
+	case BTSNOOP_OPCODE_SCO_RX_PKT:
+		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		break;
+	}
+
+	return 0xff;
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+	unsigned char control[32];
+	struct mgmt_hdr hdr;
+	struct msghdr msg;
+	struct iovec iov[2];
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(monitor_fd);
+		return;
+	}
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = MGMT_HDR_SIZE;
+	iov[1].iov_base = monitor_buf;
+	iov[1].iov_len = sizeof(monitor_buf);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 2;
+	msg.msg_control = control;
+	msg.msg_controllen = sizeof(control);
+
+	while (true) {
+		struct cmsghdr *cmsg;
+		struct timeval *tv = NULL;
+		struct timeval ctv;
+		uint16_t opcode, index, pktlen;
+		uint32_t flags;
+		ssize_t len;
+
+		len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT);
+		if (len < 0)
+			break;
+
+		if (len < MGMT_HDR_SIZE)
+			break;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level != SOL_SOCKET)
+				continue;
+
+			if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+				memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+				tv = &ctv;
+			}
+		}
+
+		opcode = btohs(hdr.opcode);
+		index  = btohs(hdr.index);
+		pktlen = btohs(hdr.len);
+
+		if (index)
+			continue;
+
+		flags = get_flags_from_opcode(opcode);
+		if (flags != 0xff)
+			btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+	}
+}
+
+static int open_monitor(const char *path)
+{
+	struct sockaddr_hci addr;
+	int opt = 1;
+
+	snoop = btsnoop_create(path, BTSNOOP_FORMAT_HCI);
+	if (!snoop)
+		return -1;
+
+	monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (monitor_fd < 0)
+		goto failed;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	addr.hci_channel = HCI_CHANNEL_MONITOR;
+
+	if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+		goto failed_close;
+
+	if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))
+									< 0)
+		goto failed_close;
+
+	mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL);
+
+	return 0;
+
+failed_close:
+	close(monitor_fd);
+	monitor_fd = -1;
+
+failed:
+	btsnoop_unref(snoop);
+	snoop = NULL;
+
+	return -1;
+}
+
+static void close_monitor(void)
+{
+	btsnoop_unref(snoop);
+	snoop = NULL;
+
+	close(monitor_fd);
+	monitor_fd = -1;
+}
+
+static void set_capabilities(void)
+{
+#if defined(ANDROID)
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+
+	/*
+	 * CAP_NET_RAW: for snooping
+	 * CAP_DAC_READ_SEARCH: override path search permissions
+	 */
+	cap.effective = cap.permitted =
+		CAP_TO_MASK(CAP_NET_RAW) |
+		CAP_TO_MASK(CAP_DAC_READ_SEARCH);
+	cap.inheritable = 0;
+
+	/* TODO: Move to cap_set_proc once bionic support it */
+	if (capset(&header, &cap) < 0)
+		exit(EXIT_FAILURE);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+	const char *path;
+	sigset_t mask;
+
+	__btd_log_init(NULL, 0);
+
+	DBG("");
+
+	set_capabilities();
+
+	if (argc > 1)
+		path = argv[1];
+	else
+		path = DEFAULT_SNOOP_FILE;
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	if (!strcmp(DEFAULT_SNOOP_FILE, path))
+		rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old");
+
+	if (open_monitor(path) < 0) {
+		error("bluetoothd_snoop: start failed");
+		return EXIT_FAILURE;
+	}
+
+	info("bluetoothd_snoop: started");
+
+	mainloop_run();
+
+	close_monitor();
+
+	info("bluetoothd_snoop: stopped");
+
+	__btd_log_cleanup();
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/android/bluetoothd-wrapper.c b/bluez/repo/android/bluetoothd-wrapper.c
new file mode 100644
index 0000000..7f668da
--- /dev/null
+++ b/bluez/repo/android/bluetoothd-wrapper.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <cutils/properties.h>
+
+#include "hal-utils.h"
+
+#define VALGRIND_BIN "/system/bin/valgrind"
+
+#define BLUETOOTHD_BIN "/system/bin/bluetoothd-main"
+
+static void run_valgrind(int debug, int mgmt_dbg)
+{
+	char *prg_argv[7];
+	char *prg_envp[3];
+
+	prg_argv[0] = VALGRIND_BIN;
+	prg_argv[1] = "--leak-check=full";
+	prg_argv[2] = "--track-origins=yes";
+	prg_argv[3] = BLUETOOTHD_BIN;
+	prg_argv[4] = debug ? "-d" : NULL;
+	prg_argv[5] = mgmt_dbg ? "--mgmt-debug" : NULL;
+	prg_argv[6] = NULL;
+
+	prg_envp[0] = "G_SLICE=always-malloc";
+	prg_envp[1] = "G_DEBUG=gc-friendly";
+	prg_envp[2] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void run_bluetoothd(int debug, int mgmt_dbg)
+{
+	char *prg_argv[4];
+	char *prg_envp[1];
+
+	prg_argv[0] = BLUETOOTHD_BIN;
+	prg_argv[1] = debug ? "-d" : NULL;
+	prg_argv[2] = mgmt_dbg ? "--mgmt-debug" : NULL;
+	prg_argv[3] = NULL;
+
+	prg_envp[0] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+int main(int argc, char *argv[])
+{
+	char value[PROPERTY_VALUE_MAX];
+	int debug = 0;
+	int mgmt_dbg = 0;
+
+	if (get_config("debug", value, NULL) > 0 &&
+			(!strcasecmp(value, "true") || atoi(value) > 0))
+		debug = 1;
+
+	if (get_config("mgmtdbg", value, NULL) > 0 &&
+			(!strcasecmp(value, "true") || atoi(value) > 0)) {
+		debug = 1;
+		mgmt_dbg = 1;
+	}
+
+	if (get_config("valgrind", value, NULL) > 0 &&
+			(!strcasecmp(value, "true") || atoi(value) > 0))
+		run_valgrind(debug, mgmt_dbg);
+
+	/*
+	 * In case we failed to execute Valgrind, try to run bluetoothd
+	 * without it
+	 */
+	run_bluetoothd(debug, mgmt_dbg);
+
+	return 0;
+}
diff --git a/bluez/repo/android/bluetoothd.te b/bluez/repo/android/bluetoothd.te
new file mode 100644
index 0000000..532bfbb
--- /dev/null
+++ b/bluez/repo/android/bluetoothd.te
@@ -0,0 +1,47 @@
+type bluetoothd, domain;
+type bluetoothd_exec, exec_type, file_type;
+type bluetoothd_main_exec, exec_type, file_type;
+
+# Start bluetoothd from init
+init_daemon_domain(bluetoothd)
+
+# Data file accesses
+allow bluetoothd bluetooth_data_file:dir w_dir_perms;
+allow bluetoothd bluetooth_data_file:notdevfile_class_set create_file_perms;
+
+allow bluetoothd self:capability { setuid net_admin net_bind_service net_raw };
+allow bluetoothd kernel:system module_request;
+
+# TODO: this may be romoved for userbuild where we don't use bluetoothd_wrapper
+allow bluetoothd bluetoothd_main_exec:file { execute execute_no_trans read open };
+
+# IPC socket communication
+allow bluetoothd self:socket { create_socket_perms accept listen setopt getopt };
+
+# Allow clients to use a socket provided by the bluetooth app.
+allow bluetoothd { bluetooth mediaserver }:unix_stream_socket connectto;
+
+# Allow system app to use sockets and fds
+allow bluetooth bluetoothd:fd use;
+allow bluetooth bluetoothd:unix_stream_socket rw_socket_perms;
+
+# Allow user bluetooth apps to use sockets and fds
+allow bluetoothdomain bluetoothd:fd use;
+allow bluetoothdomain bluetoothd:unix_stream_socket { getopt setopt getattr read write ioctl shutdown };
+
+# Other domains that can create and use bluetooth sockets.
+allow bluetoothdomain self:socket create_socket_perms;
+
+#This we might should put to mediaserver.te ?
+allow mediaserver bluetoothd:fd use;
+allow mediaserver bluetoothd:socket rw_socket_perms;
+
+# needs /system/bin/log access
+allow bluetoothd devpts:chr_file rw_file_perms;
+
+# access to uhid device
+allow bluetoothd uhid_device:chr_file rw_file_perms;
+
+# tethering
+allow bluetoothd self:udp_socket create_socket_perms;
+allow bluetoothd self:tcp_socket { create ioctl };
diff --git a/bluez/repo/android/bluetoothd_snoop.te b/bluez/repo/android/bluetoothd_snoop.te
new file mode 100644
index 0000000..ef817b5
--- /dev/null
+++ b/bluez/repo/android/bluetoothd_snoop.te
@@ -0,0 +1,17 @@
+type bluetoothd_snoop, domain;
+type bluetoothd_snoop_exec, exec_type, file_type;
+
+# Start bluetoothd_snoop from init
+init_daemon_domain(bluetoothd_snoop)
+
+# directory search and read caps
+allow bluetoothd_snoop self:capability dac_read_search;
+# use raw and packet sockets caps
+allow bluetoothd_snoop self:capability net_raw;
+
+# monitor socket access
+allow bluetoothd_snoop self:socket { create bind setopt read };
+
+# sdcard access
+allow bluetoothd_snoop fuse:dir w_dir_perms;
+allow bluetoothd_snoop fuse:file create_file_perms;
diff --git a/bluez/repo/android/client/._haltest.c b/bluez/repo/android/client/._haltest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._haltest.c
Binary files differ
diff --git a/bluez/repo/android/client/._history.c b/bluez/repo/android/client/._history.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._history.c
Binary files differ
diff --git a/bluez/repo/android/client/._history.h b/bluez/repo/android/client/._history.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._history.h
Binary files differ
diff --git a/bluez/repo/android/client/._if-audio.c b/bluez/repo/android/client/._if-audio.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-audio.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-av-sink.c b/bluez/repo/android/client/._if-av-sink.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-av-sink.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-av.c b/bluez/repo/android/client/._if-av.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-av.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-bt.c b/bluez/repo/android/client/._if-bt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-bt.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-gatt.c b/bluez/repo/android/client/._if-gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-gatt.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-hf-client.c b/bluez/repo/android/client/._if-hf-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-hf-client.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-hf.c b/bluez/repo/android/client/._if-hf.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-hf.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-hh.c b/bluez/repo/android/client/._if-hh.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-hh.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-hl.c b/bluez/repo/android/client/._if-hl.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-hl.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-main.h b/bluez/repo/android/client/._if-main.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-main.h
Binary files differ
diff --git a/bluez/repo/android/client/._if-mce.c b/bluez/repo/android/client/._if-mce.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-mce.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-pan.c b/bluez/repo/android/client/._if-pan.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-pan.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-rc-ctrl.c b/bluez/repo/android/client/._if-rc-ctrl.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-rc-ctrl.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-rc.c b/bluez/repo/android/client/._if-rc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-rc.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-sco.c b/bluez/repo/android/client/._if-sco.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-sco.c
Binary files differ
diff --git a/bluez/repo/android/client/._if-sock.c b/bluez/repo/android/client/._if-sock.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._if-sock.c
Binary files differ
diff --git a/bluez/repo/android/client/._pollhandler.c b/bluez/repo/android/client/._pollhandler.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._pollhandler.c
Binary files differ
diff --git a/bluez/repo/android/client/._pollhandler.h b/bluez/repo/android/client/._pollhandler.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._pollhandler.h
Binary files differ
diff --git a/bluez/repo/android/client/._tabcompletion.c b/bluez/repo/android/client/._tabcompletion.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._tabcompletion.c
Binary files differ
diff --git a/bluez/repo/android/client/._terminal.c b/bluez/repo/android/client/._terminal.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._terminal.c
Binary files differ
diff --git a/bluez/repo/android/client/._terminal.h b/bluez/repo/android/client/._terminal.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/client/._terminal.h
Binary files differ
diff --git a/bluez/repo/android/client/haltest.c b/bluez/repo/android/client/haltest.c
new file mode 100644
index 0000000..92cd811
--- /dev/null
+++ b/bluez/repo/android/client/haltest.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <poll.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "if-main.h"
+#include "terminal.h"
+#include "pollhandler.h"
+#include "history.h"
+
+static void process_line(char *line_buffer);
+
+const struct interface *interfaces[] = {
+	&audio_if,
+	&sco_if,
+	&bluetooth_if,
+	&av_if,
+	&rc_if,
+	&gatt_if,
+	&gatt_client_if,
+	&gatt_server_if,
+	&hf_if,
+	&hh_if,
+	&pan_if,
+	&hl_if,
+	&sock_if,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	&hf_client_if,
+	&mce_if,
+	&ctrl_rc_if,
+	&av_sink_if,
+#endif
+	NULL
+};
+
+static struct method commands[];
+
+struct method *get_method(struct method *methods, const char *name)
+{
+	while (strcmp(methods->name, "") != 0) {
+		if (strcmp(methods->name, name) == 0)
+			return methods;
+		methods++;
+	}
+
+	return NULL;
+}
+
+/* function returns interface of given name or NULL if not found */
+const struct interface *get_interface(const char *name)
+{
+	int i;
+
+	for (i = 0; interfaces[i] != NULL; ++i) {
+		if (strcmp(interfaces[i]->name, name) == 0)
+			break;
+	}
+
+	return interfaces[i];
+}
+
+int haltest_error(const char *format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = terminal_vprint(format, args);
+	va_end(args);
+	return ret;
+}
+
+int haltest_info(const char *format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = terminal_vprint(format, args);
+	va_end(args);
+	return ret;
+}
+
+int haltest_warn(const char *format, ...)
+{
+	va_list args;
+	int ret;
+	va_start(args, format);
+	ret = terminal_vprint(format, args);
+	va_end(args);
+	return ret;
+}
+
+static void help_print_interface(const struct interface *i)
+{
+	struct method *m;
+
+	for (m = i->methods; strcmp(m->name, "") != 0; m++)
+		haltest_info("%s %s %s\n", i->name, m->name,
+						(m->help ? m->help : ""));
+}
+
+/* Help completion */
+static void help_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 2)
+		*enum_func = interface_name;
+}
+
+/* Help execution */
+static void help_p(int argc, const char **argv)
+{
+	const struct method *m = commands;
+	const struct interface **ip = interfaces;
+	const struct interface *i;
+
+	if (argc == 1) {
+		terminal_print("haltest allows to call Android HAL methods.\n");
+		terminal_print("\nAvailable commands:\n");
+		while (0 != strcmp(m->name, "")) {
+			terminal_print("\t%s %s\n", m->name,
+						(m->help ? m->help : ""));
+			m++;
+		}
+
+		terminal_print("\nAvailable interfaces to use:\n");
+		while (NULL != *ip) {
+			terminal_print("\t%s\n", (*ip)->name);
+			ip++;
+		}
+
+		terminal_print("\nTo get help on methods for each interface type:\n");
+		terminal_print("\n\thelp <inerface>\n");
+		terminal_print("\nBasic scenario:\n\tbluetooth init\n");
+		terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n");
+		terminal_print("\tbluetooth get_profile_interface handsfree\n");
+		terminal_print("\thandsfree init\n\n");
+		return;
+	}
+
+	i = get_interface(argv[1]);
+	if (i == NULL) {
+		haltest_error("No such interface\n");
+		return;
+	}
+
+	help_print_interface(i);
+}
+
+/* quit/exit execution */
+static void quit_p(int argc, const char **argv)
+{
+	char cleanup_audio[] = "audio cleanup";
+
+	close_hw_bt_dev();
+	process_line(cleanup_audio);
+
+	exit(0);
+}
+
+static int fd_stack[10];
+static int fd_stack_pointer = 0;
+
+static void stdin_handler(struct pollfd *pollfd);
+
+static void process_file(const char *name)
+{
+	int fd = open(name, O_RDONLY);
+
+	if (fd < 0) {
+		haltest_error("Can't open file: %s for reading\n", name);
+		return;
+	}
+
+	if (fd_stack_pointer >= 10) {
+		haltest_error("To many open files\n");
+		close(fd);
+		return;
+	}
+
+	fd_stack[fd_stack_pointer++] = fd;
+	poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler);
+	poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler);
+}
+
+static void source_p(int argc, const char **argv)
+{
+	if (argc < 2) {
+		haltest_error("No file specified");
+		return;
+	}
+
+	process_file(argv[1]);
+}
+
+/* Commands available without interface */
+static struct method commands[] = {
+	STD_METHODCH(help, "[<interface>]"),
+	STD_METHOD(quit),
+	METHOD("exit", quit_p, NULL, NULL),
+	STD_METHODH(source, "<file>"),
+	END_METHOD
+};
+
+/* Gets comman by name */
+struct method *get_command(const char *name)
+{
+	return get_method(commands, name);
+}
+
+/* Function to enumerate interface names */
+const char *interface_name(void *v, int i)
+{
+	return interfaces[i] ? interfaces[i]->name : NULL;
+}
+
+/* Function to enumerate command and interface names */
+const char *command_name(void *v, int i)
+{
+	int cmd_cnt = NELEM(commands);
+
+	if (i >= cmd_cnt)
+		return interface_name(v, i - cmd_cnt);
+	else
+		return commands[i].name;
+}
+
+/*
+ * This function changes input parameter line_buffer so it has
+ * null termination after each token (due to strtok)
+ * Output argv is filled with pointers to arguments
+ * returns number of tokens parsed - argc
+ */
+static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size)
+{
+	static const char *token_breaks = "\r\n\t ";
+	char *token;
+	int argc = 0;
+
+	token = strtok(line_buffer, token_breaks);
+	while (token != NULL && argc < (int) argv_size) {
+		argv[argc++] = token;
+		token = strtok(NULL, token_breaks);
+	}
+
+	return argc;
+}
+
+static void process_line(char *line_buffer)
+{
+	char *argv[50];
+	int argc;
+	int i = 0;
+	struct method *m;
+
+	argc = command_line_to_argv(line_buffer, argv, 50);
+	if (argc < 1)
+		return;
+
+	while (interfaces[i] != NULL) {
+		if (strcmp(interfaces[i]->name, argv[0])) {
+			i++;
+			continue;
+		}
+
+		if (argc < 2 || strcmp(argv[1], "?") == 0) {
+			help_print_interface(interfaces[i]);
+			return;
+		}
+
+		m = get_method(interfaces[i]->methods, argv[1]);
+		if (m != NULL) {
+			m->func(argc, (const char **) argv);
+			return;
+		}
+
+		haltest_error("No function %s found\n", argv[1]);
+		return;
+	}
+	/* No interface, try commands */
+	m = get_command(argv[0]);
+	if (m == NULL)
+		haltest_error("No such command %s\n", argv[0]);
+	else
+		m->func(argc, (const char **) argv);
+}
+
+/* called when there is something on stdin */
+static void stdin_handler(struct pollfd *pollfd)
+{
+	char buf[10];
+
+	if (pollfd->revents & POLLIN) {
+		int count = read(fd_stack[fd_stack_pointer - 1], buf, 10);
+
+		if (count > 0) {
+			int i;
+
+			for (i = 0; i < count; ++i)
+				terminal_process_char(buf[i], process_line);
+			return;
+		}
+	}
+
+	if (fd_stack_pointer > 1)
+		poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN,
+								stdin_handler);
+	if (fd_stack_pointer > 0) {
+		poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler);
+
+		if (fd_stack[fd_stack_pointer])
+			close(fd_stack[fd_stack_pointer]);
+	}
+}
+
+static void usage(void)
+{
+	printf("haltest Android Bluetooth HAL testing tool\n"
+		"Usage:\n");
+	printf("\thaltest [options]\n");
+	printf("options:\n"
+		"\t-i  --ivi              Initialize only IVI interfaces\n"
+		"\t-n, --no-init          Don't initialize any interfaces\n"
+		"\t-v  --version          Print version\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static void print_version(void)
+{
+	printf("haltest version %s\n", VERSION);
+}
+
+static const struct option main_options[] = {
+	{ "no-init", no_argument, NULL, 'n' },
+	{ "ivi",     no_argument, NULL, 'i' },
+	{ "help",    no_argument, NULL, 'h' },
+	{ "version", no_argument, NULL, 'v' },
+	{ NULL }
+};
+
+static bool no_init = false;
+static bool ivi_only = false;
+
+static void parse_command_line(int argc, char *argv[])
+{
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "inhv", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'n':
+			no_init = true;
+			break;
+		case 'i':
+			ivi_only = true;
+			break;
+		case 'h':
+			usage();
+			exit(0);
+		case 'v':
+			print_version();
+			exit(0);
+		default:
+			putchar('\n');
+			exit(-1);
+			break;
+		}
+	}
+}
+
+static const char * const interface_names[] = {
+	BT_PROFILE_HANDSFREE_ID,
+	BT_PROFILE_ADVANCED_AUDIO_ID,
+	BT_PROFILE_AV_RC_ID,
+	BT_PROFILE_HEALTH_ID,
+	BT_PROFILE_HIDHOST_ID,
+	BT_PROFILE_PAN_ID,
+	BT_PROFILE_GATT_ID,
+	BT_PROFILE_SOCKETS_ID,
+	NULL
+};
+
+static const char * const ivi_interface_inames[] = {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	BT_PROFILE_HANDSFREE_CLIENT_ID,
+	BT_PROFILE_MAP_CLIENT_ID,
+	BT_PROFILE_AV_RC_CTRL_ID,
+		BT_PROFILE_ADVANCED_AUDIO_SINK_ID,
+#endif
+	NULL
+};
+
+static void init(const char * const *inames)
+{
+	const struct method *m;
+	const char *argv[4];
+	char init_audio[] = "audio init";
+	char init_sco[] = "sco init";
+	char init_bt[] = "bluetooth init";
+	uint32_t i;
+
+	process_line(init_audio);
+	process_line(init_sco);
+	process_line(init_bt);
+
+	m = get_interface_method("bluetooth", "get_profile_interface");
+
+	while (*inames) {
+		argv[2] = *inames;
+		m->func(3, argv);
+		inames++;
+	}
+
+	/* Init what is available to init */
+	for (i = 3; i < NELEM(interfaces) - 1; ++i) {
+		m = get_interface_method(interfaces[i]->name, "init");
+		if (m != NULL)
+			m->func(2, argv);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	struct stat rcstat;
+
+	parse_command_line(argc, argv);
+
+	terminal_setup();
+
+	if (!no_init) {
+		if (ivi_only)
+			init(ivi_interface_inames);
+		else
+			init(interface_names);
+	}
+
+	history_restore(".haltest_history");
+
+	fd_stack[fd_stack_pointer++] = 0;
+	/* Register command line handler */
+	poll_register_fd(0, POLLIN, stdin_handler);
+
+	if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0)
+		process_file(".haltestrc");
+
+	poll_dispatch_loop();
+
+	return 0;
+}
diff --git a/bluez/repo/android/client/history.c b/bluez/repo/android/client/history.c
new file mode 100644
index 0000000..ca4664c
--- /dev/null
+++ b/bluez/repo/android/client/history.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "history.h"
+
+/* Very simple history storage for easy usage of tool */
+
+#define HISTORY_DEPTH 40
+#define LINE_SIZE 200
+static char lines[HISTORY_DEPTH][LINE_SIZE];
+static int last_line = 0;
+static int history_size = 0;
+
+/* TODO: Storing history not implemented yet */
+void history_store(const char *filename)
+{
+}
+
+/* Restoring history from file */
+void history_restore(const char *filename)
+{
+	char line[1000];
+	FILE *f = fopen(filename, "rt");
+
+	if (f == NULL)
+		return;
+
+	for (;;) {
+		if (fgets(line, 1000, f) != NULL) {
+			int l = strlen(line);
+
+			while (l > 0 && isspace(line[--l]))
+				line[l] = 0;
+
+			if (l > 0)
+				history_add_line(line);
+		} else
+			break;
+	}
+
+	fclose(f);
+}
+
+/* Add new line to history buffer */
+void history_add_line(const char *line)
+{
+	if (line == NULL || strlen(line) == 0)
+		return;
+
+	if (strcmp(line, lines[last_line]) == 0)
+		return;
+
+	last_line = (last_line + 1) % HISTORY_DEPTH;
+	strncpy(&lines[last_line][0], line, LINE_SIZE - 1);
+	if (history_size < HISTORY_DEPTH)
+		history_size++;
+}
+
+/*
+ * Get n-th line from history
+ * 0 - means latest
+ * -1 - means oldest
+ * return -1 if there is no such line
+ */
+int history_get_line(int n, char *buf, int buf_size)
+{
+	if (n == -1)
+		n = history_size - 1;
+
+	if (n >= history_size || buf_size == 0 || n < 0)
+		return -1;
+
+	strncpy(buf,
+		&lines[(HISTORY_DEPTH + last_line - n) % HISTORY_DEPTH][0],
+		buf_size - 1);
+	buf[buf_size - 1] = 0;
+
+	return n;
+}
diff --git a/bluez/repo/android/client/history.h b/bluez/repo/android/client/history.h
new file mode 100644
index 0000000..26085b5
--- /dev/null
+++ b/bluez/repo/android/client/history.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+void history_store(const char *filename);
+void history_restore(const char *filename);
+void history_add_line(const char *line);
+int history_get_line(int n, char *buf, int buf_size);
diff --git a/bluez/repo/android/client/if-audio.c b/bluez/repo/android/client/if-audio.c
new file mode 100644
index 0000000..65e2f2f
--- /dev/null
+++ b/bluez/repo/android/client/if-audio.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+audio_hw_device_t *if_audio = NULL;
+static struct audio_stream_out *stream_out = NULL;
+
+static size_t buffer_size = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+	STATE_STOPPED,
+	STATE_STOPPING,
+	STATE_PLAYING,
+	STATE_SUSPENDED,
+	STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+	DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+#else
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+#endif
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+	DELEMENT(AUDIO_FORMAT_DEFAULT),
+	DELEMENT(AUDIO_FORMAT_PCM),
+	DELEMENT(AUDIO_FORMAT_MP3),
+	DELEMENT(AUDIO_FORMAT_AMR_NB),
+	DELEMENT(AUDIO_FORMAT_AMR_WB),
+	DELEMENT(AUDIO_FORMAT_AAC),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+	DELEMENT(AUDIO_FORMAT_VORBIS),
+	DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+	DELEMENT(AUDIO_FORMAT_SUB_MASK),
+	DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+	audio_hw_device_t *device;
+
+	err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
+					AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
+	if (err) {
+		haltest_error("hw_get_module_by_class returned %d\n", err);
+		return;
+	}
+
+	err = audio_hw_device_open(module, &device);
+	if (err) {
+		haltest_error("audio_hw_device_open returned %d\n", err);
+		return;
+	}
+
+	if_audio = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+	FILE *in = data;
+	return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+	size_t i = 0;
+	float volume = 0.5;
+	float *freq = data;
+	float f = 1;
+
+	if (freq)
+		f = *freq;
+
+	/* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+	for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+		if (sample_pos >= SAMPLERATE)
+			sample_pos = sample_pos % SAMPLERATE;
+
+		/* Use the same sample for both channels */
+		buffer[i++] = sample[sample_pos] * volume;
+		buffer[i++] = sample[sample_pos] * volume;
+
+		sample_pos += f;
+	}
+
+	return buffer_size;
+}
+
+static void prepare_sample(void)
+{
+	int x;
+	double s;
+
+	haltest_info("Preparing audio sample...\n");
+
+	for (x = 0; x < SAMPLERATE; x++) {
+		/* prepare sinusoidal 1Hz sample */
+		s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+		s = sin(s);
+
+		/* remap <-1, 1> to signed 16bit PCM range */
+		sample[x] = s * 32767;
+	}
+
+	sample_pos = 0;
+}
+
+static void *playback_thread(void *data)
+{
+	int (*filbuff_cb) (short*, void*);
+	short buffer[buffer_size / sizeof(short)];
+	size_t len = 0;
+	ssize_t w_len = 0;
+	FILE *in = data;
+	void *cb_data = NULL;
+	float freq = 440.0;
+
+	/* Use file or fall back to generator */
+	if (in) {
+		filbuff_cb = feed_from_file;
+		cb_data = in;
+	} else {
+		prepare_sample();
+		filbuff_cb = feed_from_generator;
+		cb_data = &freq;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+
+	do {
+		pthread_mutex_lock(&state_mutex);
+
+		if (current_state == STATE_STOPPING) {
+			pthread_mutex_unlock(&state_mutex);
+			break;
+		} else if (current_state == STATE_SUSPENDED) {
+			pthread_mutex_unlock(&state_mutex);
+			usleep(500);
+			continue;
+		}
+
+		pthread_mutex_unlock(&state_mutex);
+
+		len = filbuff_cb(buffer, cb_data);
+
+		pthread_mutex_lock(&outstream_mutex);
+		if (!stream_out) {
+			pthread_mutex_unlock(&outstream_mutex);
+			break;
+		}
+
+		w_len = stream_out->write(stream_out, buffer, buffer_size);
+		pthread_mutex_unlock(&outstream_mutex);
+	} while (len && w_len > 0);
+
+	if (in)
+		fclose(in);
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done playing.\n");
+
+	return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+	const char *fname = NULL;
+	FILE *in = NULL;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("Invalid audio file path.\n");
+		haltest_info("Using sound generator.\n");
+	} else {
+		fname = argv[2];
+		in = fopen(fname, "r");
+
+		if (in == NULL) {
+			haltest_error("Cannot open file: %s\n", fname);
+			return;
+		}
+		haltest_info("Playing file: %s\n", fname);
+	}
+
+	if (buffer_size == 0) {
+		haltest_error("Invalid buffer size. Was stream_out opened?\n");
+		goto fail;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		goto fail;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+		haltest_error("Cannot create playback thread!\n");
+		goto fail;
+	}
+
+	return;
+fail:
+	if (in)
+		fclose(in);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+
+	current_state = STATE_STOPPING;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_mutex_lock(&outstream_mutex);
+	stream_out->common.standby(&stream_out->common);
+	pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+	int err;
+
+	RETURN_IF_NULL(if_audio);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_PLAYING) {
+		haltest_error("Already playing!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	err = if_audio->open_output_stream(if_audio,
+						0,
+						AUDIO_DEVICE_OUT_ALL_A2DP,
+						AUDIO_OUTPUT_FLAG_NONE,
+						NULL,
+						&stream_out, NULL);
+#else
+	err = if_audio->open_output_stream(if_audio,
+						0,
+						AUDIO_DEVICE_OUT_ALL_A2DP,
+						AUDIO_OUTPUT_FLAG_NONE,
+						NULL,
+						&stream_out);
+#endif
+	if (err < 0) {
+		haltest_error("open output stream returned %d\n", err);
+		return;
+	}
+
+	buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+	if (buffer_size == 0)
+		haltest_error("Invalid buffer size received!\n");
+	else
+		haltest_info("Using buffer size: %zu\n", buffer_size);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	stop_p(argc, argv);
+
+	haltest_info("Waiting for playback thread...\n");
+	pthread_join(play_thread, NULL);
+
+	if_audio->close_output_stream(if_audio, stream_out);
+
+	stream_out = NULL;
+	buffer_size = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+	int err;
+
+	RETURN_IF_NULL(if_audio);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		pthread_mutex_unlock(&state_mutex);
+		close_output_stream_p(0, NULL);
+	} else {
+		pthread_mutex_unlock(&state_mutex);
+	}
+
+	err = audio_hw_device_close(if_audio);
+	if (err < 0) {
+		haltest_error("audio_hw_device_close returned %d\n", err);
+		return;
+	}
+
+	if_audio = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_PLAYING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	current_state = STATE_SUSPENDED;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_mutex_lock(&outstream_mutex);
+	stream_out->common.standby(&stream_out->common);
+	pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_SUSPENDED)
+		current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Output audio stream latency: %d\n",
+					stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current output buffer size: %zu\n",
+		stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+	audio_channel_mask_t channels;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	channels = stream_out->common.get_channels(&stream_out->common);
+
+	haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+	audio_format_t format;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	format = stream_out->common.get_format(&stream_out->common);
+
+	haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current sample rate: %d\n",
+		stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+	const char *keystr;
+
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_info("No keys given.\n");
+		keystr = "";
+	} else {
+		keystr = argv[2];
+	}
+
+	haltest_info("Current parameters: %s\n",
+			stream_out->common.get_parameters(&stream_out->common,
+								keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("No key=value; pairs given.\n");
+		return;
+	}
+
+	stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3)
+		return;
+
+	stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio);
+
+	haltest_info("Init check result: %d\n", if_audio->init_check(if_audio));
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	STD_METHOD(open_output_stream),
+	STD_METHOD(close_output_stream),
+	STD_METHODH(play, "<path to pcm file>"),
+	STD_METHOD(stop),
+	STD_METHOD(suspend),
+	STD_METHOD(resume),
+	STD_METHOD(get_latency),
+	STD_METHOD(get_buffer_size),
+	STD_METHOD(get_channels),
+	STD_METHOD(get_format),
+	STD_METHOD(get_sample_rate),
+	STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
+	STD_METHODH(set_parameters, "<A2dpSuspended=value;closing=value>"),
+	STD_METHODH(set_sample_rate, "<sample rate>"),
+	STD_METHOD(init_check),
+	END_METHOD
+};
+
+const struct interface audio_if = {
+	.name = "audio",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-av-sink.c b/bluez/repo/android/client/if-av-sink.c
new file mode 100644
index 0000000..3087dcf
--- /dev/null
+++ b/bluez/repo/android/client/if-av-sink.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btav_interface_t *if_av_sink = NULL;
+
+SINTMAP(btav_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btav_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND),
+	DELEMENT(BTAV_AUDIO_STATE_STOPPED),
+	DELEMENT(BTAV_AUDIO_STATE_STARTED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state(btav_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("(sink) %s: connection_state=%s remote_bd_addr=%s\n",
+				__func__, btav_connection_state_t2str(state),
+				bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("(sink) %s: audio_state=%s remote_bd_addr=%s\n", __func__,
+					btav_audio_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate,
+							uint8_t channel_count) {
+	haltest_info("(sink) %s: addr=%s\n sample_rate=%d\n channel_count=%d\n",
+				__func__, bt_bdaddr_t2str(bd_addr, last_addr),
+				sample_rate, channel_count);
+}
+
+static btav_callbacks_t av_cbacks = {
+	.size = sizeof(av_cbacks),
+	.connection_state_cb = connection_state,
+	.audio_state_cb = audio_state,
+	.audio_config_cb = audio_config,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av_sink);
+
+	EXEC(if_av_sink->init, &av_cbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av_sink);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av_sink->connect, &addr);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av_sink);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av_sink->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av_sink);
+
+	EXECV(if_av_sink->cleanup);
+	if_av_sink = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface av_sink_if = {
+	.name = "av-sink",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-av.c b/bluez/repo/android/client/if-av.c
new file mode 100644
index 0000000..85c641b
--- /dev/null
+++ b/bluez/repo/android/client/if-av.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btav_interface_t *if_av = NULL;
+
+SINTMAP(btav_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTAV_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btav_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND),
+	DELEMENT(BTAV_AUDIO_STATE_STOPPED),
+	DELEMENT(BTAV_AUDIO_STATE_STARTED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state(btav_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: connection_state=%s remote_bd_addr=%s\n", __func__,
+					btav_connection_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: audio_state=%s remote_bd_addr=%s\n", __func__,
+					btav_audio_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void audio_config(bt_bdaddr_t *bd_addr, uint32_t sample_rate,
+							uint8_t channel_count) {
+	haltest_info("%s: remote_addr=%s\n sample_rate=%d\n channel_count=%d\n",
+				__func__, bt_bdaddr_t2str(bd_addr, last_addr),
+				sample_rate, channel_count);
+}
+#endif
+
+static btav_callbacks_t av_cbacks = {
+	.size = sizeof(av_cbacks),
+	.connection_state_cb = connection_state,
+	.audio_state_cb = audio_state,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.audio_config_cb = audio_config,
+#endif
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av);
+
+	EXEC(if_av->init, &av_cbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av->connect, &addr);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_av);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_av->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_av);
+
+	EXECV(if_av->cleanup);
+	if_av = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface av_if = {
+	.name = "av",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-bt.c b/bluez/repo/android/client/if-bt.c
new file mode 100644
index 0000000..c9acf6c
--- /dev/null
+++ b/bluez/repo/android/client/if-bt.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string.h>
+#include <inttypes.h>
+
+#include "if-main.h"
+#include "terminal.h"
+#include "../hal-msg.h"
+#include "../hal-utils.h"
+
+static hw_device_t *bt_device;
+const bt_interface_t *if_bluetooth;
+
+#define VERIFY_PROP_TYPE_ARG(n, typ) \
+	do { \
+		if (n < argc) \
+			typ = str2btpropertytype(argv[n]); \
+		else { \
+			haltest_error("No property type specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+static bt_scan_mode_t str2btscanmode(const char *str)
+{
+	bt_scan_mode_t v = str2bt_scan_mode_t(str);
+
+	if ((int) v != -1)
+		return v;
+
+	haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+	return (bt_scan_mode_t) atoi(str);
+}
+
+static bt_ssp_variant_t str2btsspvariant(const char *str)
+{
+	bt_ssp_variant_t v = str2bt_ssp_variant_t(str);
+
+	if ((int) v != -1)
+		return v;
+
+	haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+	return (bt_ssp_variant_t) atoi(str);
+}
+
+static bt_property_type_t str2btpropertytype(const char *str)
+{
+	bt_property_type_t v = str2bt_property_type_t(str);
+
+	if ((int) v != -1)
+		return v;
+
+	haltest_warn("WARN: %s cannot convert %s\n", __func__, str);
+	return (bt_property_type_t) atoi(str);
+}
+
+static void dump_properties(int num_properties, bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++) {
+		/*
+		 * properities sometimes come unaligned hence memcp to
+		 * aligned buffer
+		 */
+		bt_property_t prop;
+		memcpy(&prop, properties + i, sizeof(prop));
+
+		haltest_info("prop: %s\n", btproperty2str(&prop));
+	}
+}
+
+/* Cache for remote devices, stored in sorted array */
+static bt_bdaddr_t *remote_devices = NULL;
+static int remote_devices_cnt = 0;
+static int remote_devices_capacity = 0;
+
+/* Adds address to remote device set so it can be used in tab completion */
+void add_remote_device(const bt_bdaddr_t *addr)
+{
+	int i;
+
+	if (remote_devices == NULL) {
+		remote_devices = malloc(4 * sizeof(bt_bdaddr_t));
+		remote_devices_cnt = 0;
+		if (remote_devices == NULL) {
+			remote_devices_capacity = 0;
+			return;
+		}
+
+		remote_devices_capacity = 4;
+	}
+
+	/* Array is sorted, search for right place */
+	for (i = 0; i < remote_devices_cnt; ++i) {
+		int res = memcmp(&remote_devices[i], addr, sizeof(*addr));
+
+		if (res == 0)
+			return; /* Already added */
+		else if (res > 0)
+			break;
+	}
+
+	/* Realloc space if needed */
+	if (remote_devices_cnt >= remote_devices_capacity) {
+		bt_bdaddr_t *tmp;
+
+		remote_devices_capacity *= 2;
+		/*
+		 * Save reference to previously allocated memory block so that
+		 * it can be freed in case realloc fails.
+		 */
+		tmp = remote_devices;
+
+		remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) *
+						remote_devices_capacity);
+		if (remote_devices == NULL) {
+			free(tmp);
+			remote_devices_capacity = 0;
+			remote_devices_cnt = 0;
+			return;
+		}
+	}
+
+	if (i < remote_devices_cnt)
+		memmove(remote_devices + i + 1, remote_devices + i,
+				(remote_devices_cnt - i) * sizeof(bt_bdaddr_t));
+	remote_devices[i] = *addr;
+	remote_devices_cnt++;
+}
+
+const char *enum_devices(void *v, int i)
+{
+	static char buf[MAX_ADDR_STR_LEN];
+
+	if (i >= remote_devices_cnt)
+		return NULL;
+
+	bt_bdaddr_t2str(&remote_devices[i], buf);
+	return buf;
+}
+
+static void add_remote_device_from_props(int num_properties,
+						const bt_property_t *properties)
+{
+	int i;
+
+	for (i = 0; i < num_properties; i++) {
+		/*
+		 * properities sometimes come unaligned hence memcp to
+		 * aligned buffer
+		 */
+		bt_property_t property;
+
+		memcpy(&property, properties + i, sizeof(property));
+		if (property.type == BT_PROPERTY_BDADDR)
+			add_remote_device((bt_bdaddr_t *) property.val);
+	}
+}
+
+bool close_hw_bt_dev(void)
+{
+	if (!bt_device)
+		return false;
+
+	bt_device->close(bt_device);
+	return true;
+}
+
+static void adapter_state_changed_cb(bt_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state));
+}
+
+static void adapter_properties_cb(bt_status_t status, int num_properties,
+						bt_property_t *properties)
+{
+	haltest_info("%s: status=%s num_properties=%d\n", __func__,
+				bt_status_t2str(status), num_properties);
+
+	dump_properties(num_properties, properties);
+}
+
+static void remote_device_properties_cb(bt_status_t status,
+					bt_bdaddr_t *bd_addr,
+					int num_properties,
+					bt_property_t *properties)
+{
+	haltest_info("%s: status=%s bd_addr=%s num_properties=%d\n", __func__,
+			bt_status_t2str(status), bdaddr2str(bd_addr),
+			num_properties);
+
+	add_remote_device(bd_addr);
+
+	dump_properties(num_properties, properties);
+}
+
+static void device_found_cb(int num_properties, bt_property_t *properties)
+{
+	haltest_info("%s: num_properties=%d\n", __func__, num_properties);
+
+	add_remote_device_from_props(num_properties, properties);
+
+	dump_properties(num_properties, properties);
+}
+
+static void discovery_state_changed_cb(bt_discovery_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__,
+					bt_discovery_state_t2str(state));
+}
+
+/*
+ * Buffer for remote addres that came from one of bind request.
+ * It's stored for command completion.
+ */
+static char last_remote_addr[MAX_ADDR_STR_LEN];
+static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t) -1;
+
+static bt_bdaddr_t pin_request_addr;
+static void pin_request_answer(char *reply)
+{
+	bt_pin_code_t pin;
+	int accept = 0;
+	int pin_len = strlen(reply);
+
+	if (pin_len > 0) {
+		accept = 1;
+		if (pin_len > 16)
+			pin_len = 16;
+		memcpy(&pin.pin, reply, pin_len);
+	}
+
+	EXEC(if_bluetooth->pin_reply, &pin_request_addr, accept, pin_len, &pin);
+}
+
+static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+								uint32_t cod)
+{
+	/* Store for command completion */
+	bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+	pin_request_addr = *remote_bd_addr;
+
+	haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__,
+					last_remote_addr, bd_name->name, cod);
+	terminal_prompt_for("Enter pin: ", pin_request_answer);
+}
+
+/* Variables to store information from ssp_request_cb used for ssp_reply */
+static bt_bdaddr_t ssp_request_addr;
+static bt_ssp_variant_t ssp_request_variant;
+static uint32_t ssp_request_pask_key;
+
+/* Called when user hit enter on prompt for confirmation */
+static void ssp_request_yes_no_answer(char *reply)
+{
+	int accept = *reply == 0 || *reply == 'y' || *reply == 'Y';
+
+	if_bluetooth->ssp_reply(&ssp_request_addr, ssp_request_variant, accept,
+							ssp_request_pask_key);
+}
+
+static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+				uint32_t cod, bt_ssp_variant_t pairing_variant,
+				uint32_t pass_key)
+{
+	static char prompt[50];
+
+	/* Store for command completion */
+	bt_bdaddr_t2str(remote_bd_addr, last_remote_addr);
+	last_ssp_variant = pairing_variant;
+
+	haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n",
+			__func__, last_remote_addr, bd_name->name, cod,
+			bt_ssp_variant_t2str(pairing_variant), pass_key);
+
+	switch (pairing_variant) {
+	case BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
+		sprintf(prompt, "Does other device show %d [Y/n] ?", pass_key);
+
+		ssp_request_addr = *remote_bd_addr;
+		ssp_request_variant = pairing_variant;
+		ssp_request_pask_key = pass_key;
+
+		terminal_prompt_for(prompt, ssp_request_yes_no_answer);
+		break;
+	case BT_SSP_VARIANT_CONSENT:
+		sprintf(prompt, "Consent pairing [Y/n] ?");
+
+		ssp_request_addr = *remote_bd_addr;
+		ssp_request_variant = pairing_variant;
+		ssp_request_pask_key = 0;
+
+		terminal_prompt_for(prompt, ssp_request_yes_no_answer);
+		break;
+	case BT_SSP_VARIANT_PASSKEY_ENTRY:
+	case BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
+	default:
+		haltest_info("Not automatically handled\n");
+		break;
+	}
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+						bt_bdaddr_t *remote_bd_addr,
+						bt_bond_state_t state)
+{
+	haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__,
+			bt_status_t2str(status), bdaddr2str(remote_bd_addr),
+			bt_bond_state_t2str(state));
+}
+
+static void acl_state_changed_cb(bt_status_t status,
+						bt_bdaddr_t *remote_bd_addr,
+						bt_acl_state_t state)
+{
+	haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__,
+			bt_status_t2str(status), bdaddr2str(remote_bd_addr),
+			bt_acl_state_t2str(state));
+}
+
+static void thread_evt_cb(bt_cb_thread_evt evt)
+{
+	haltest_info("%s: evt=%s\n", __func__, bt_cb_thread_evt2str(evt));
+}
+
+static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static void le_test_mode_cb(bt_status_t status, uint16_t num_packets)
+{
+	haltest_info("%s %s %d\n", __func__, bt_status_t2str(status),
+								num_packets);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void energy_info_cb(bt_activity_energy_info *energy_info)
+{
+	haltest_info("%s status=%s, ctrl_state=0x%02X, tx_time=0x%jx,"
+			"rx_time=0x%jx, idle_time=0x%jx, energu_used=0x%jx\n",
+			__func__, bt_status_t2str(energy_info->status),
+			energy_info->ctrl_state, energy_info->tx_time,
+			energy_info->rx_time, energy_info->idle_time,
+			energy_info->energy_used);
+}
+#endif
+
+static bt_callbacks_t bt_callbacks = {
+	.size = sizeof(bt_callbacks),
+	.adapter_state_changed_cb = adapter_state_changed_cb,
+	.adapter_properties_cb = adapter_properties_cb,
+	.remote_device_properties_cb = remote_device_properties_cb,
+	.device_found_cb = device_found_cb,
+	.discovery_state_changed_cb = discovery_state_changed_cb,
+	.pin_request_cb = pin_request_cb,
+	.ssp_request_cb = ssp_request_cb,
+	.bond_state_changed_cb = bond_state_changed_cb,
+	.acl_state_changed_cb = acl_state_changed_cb,
+	.thread_evt_cb = thread_evt_cb,
+	.dut_mode_recv_cb = dut_mode_recv_cb,
+	.le_test_mode_cb = le_test_mode_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.energy_info_cb = energy_info_cb,
+#endif
+};
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static alarm_cb alarm_cb_p = NULL;
+static void *alarm_cb_p_data = NULL;
+
+static bool set_wake_alarm(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+								void *data)
+{
+	haltest_info("%s: delay %"PRIu64" should_wake %u cb %p data %p\n",
+				__func__, delay_millis, should_wake, cb, data);
+
+	/* TODO call alarm callback after specified delay */
+	alarm_cb_p = cb;
+	alarm_cb_p_data = data;
+
+	return true;
+}
+
+static int acquire_wake_lock(const char *lock_name)
+{
+	haltest_info("%s: %s\n", __func__, lock_name);
+
+	return BT_STATUS_SUCCESS;
+}
+
+static int release_wake_lock(const char *lock_name)
+{
+	haltest_info("%s: %s\n", __func__, lock_name);
+
+	return BT_STATUS_SUCCESS;
+}
+
+static bt_os_callouts_t bt_os_callouts = {
+	.size = sizeof(bt_os_callouts),
+	.set_wake_alarm = set_wake_alarm,
+	.acquire_wake_lock = acquire_wake_lock,
+	.release_wake_lock = release_wake_lock,
+};
+#endif
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+
+	err = hw_get_module(BT_HARDWARE_MODULE_ID, &module);
+	if (err) {
+		haltest_error("he_get_module returned %d\n", err);
+		return;
+	}
+
+	err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &bt_device);
+	if (err) {
+		haltest_error("module->methods->open returned %d\n", err);
+		return;
+	}
+
+	if_bluetooth =
+		((bluetooth_device_t *) bt_device)->get_bluetooth_interface();
+	if (!if_bluetooth) {
+		haltest_error("get_bluetooth_interface returned NULL\n");
+		return;
+	}
+
+	EXEC(if_bluetooth->init, &bt_callbacks);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	EXEC(if_bluetooth->set_os_callouts, &bt_os_callouts);
+#endif
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXECV(if_bluetooth->cleanup);
+
+	if_bluetooth = NULL;
+}
+
+static void enable_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->enable);
+}
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void read_energy_info_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->read_energy_info);
+}
+
+#define get_connection_state_c complete_addr_c
+
+static void get_connection_state_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+
+	VERIFY_ADDR_ARG(2, &addr);
+
+	haltest_info("if_bluetooth->get_connection_state : %d\n",
+				if_bluetooth->get_connection_state(&addr));
+}
+#endif
+
+static void disable_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->disable);
+}
+
+static void get_adapter_properties_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->get_adapter_properties);
+}
+
+static void get_adapter_property_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bt_property_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_adapter_property_p(int argc, const char **argv)
+{
+	int type;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_PROP_TYPE_ARG(2, type);
+
+	EXEC(if_bluetooth->get_adapter_property, type);
+}
+
+static const char * const names[] = {
+	"BT_PROPERTY_BDNAME",
+	"BT_PROPERTY_ADAPTER_SCAN_MODE",
+	"BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT",
+	NULL
+};
+
+static void set_adapter_property_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = (void *) names;
+		*enum_func = enum_strings;
+	} else if (argc == 4) {
+		if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) {
+			*user = TYPE_ENUM(bt_scan_mode_t);
+			*enum_func = enum_defines;
+		}
+	}
+}
+
+static void set_adapter_property_p(int argc, const char **argv)
+{
+	bt_property_t property;
+	bt_scan_mode_t mode;
+	int timeout;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_PROP_TYPE_ARG(2, property.type);
+
+	if (argc <= 3) {
+		haltest_error("No property value specified\n");
+		return;
+	}
+	switch (property.type) {
+	case BT_PROPERTY_BDNAME:
+		property.len = strlen(argv[3]) + 1;
+		property.val = (char *) argv[3];
+		break;
+
+	case BT_PROPERTY_ADAPTER_SCAN_MODE:
+		mode = str2btscanmode(argv[3]);
+		property.len = sizeof(bt_scan_mode_t);
+		property.val = &mode;
+		break;
+
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+		timeout = atoi(argv[3]);
+		property.val = &timeout;
+		property.len = sizeof(timeout);
+		break;
+
+	case BT_PROPERTY_BDADDR:
+	case BT_PROPERTY_UUIDS:
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+	case BT_PROPERTY_SERVICE_RECORD:
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+	case BT_PROPERTY_REMOTE_RSSI:
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+	default:
+		haltest_error("Invalid property %s\n", argv[3]);
+		return;
+	}
+
+	EXEC(if_bluetooth->set_adapter_property, &property);
+}
+
+/* This function is to be used for completion methods that need only address */
+static void complete_addr_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_device_properties_c complete_addr_c
+
+static void get_remote_device_properties_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->get_remote_device_properties, &addr);
+}
+
+static void get_remote_device_property_c(int argc, const char **argv,
+							enum_func *enum_func,
+							void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bt_property_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_remote_device_property_p(int argc, const char **argv)
+{
+	bt_property_type_t type;
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+	VERIFY_PROP_TYPE_ARG(3, type);
+
+	EXEC(if_bluetooth->get_remote_device_property, &addr, type);
+}
+
+/*
+ * Same completion as for get_remote_device_property_c can be used for
+ * set_remote_device_property_c. No need to create separate function.
+ */
+#define set_remote_device_property_c get_remote_device_property_c
+
+static void set_remote_device_property_p(int argc, const char **argv)
+{
+	bt_property_t property;
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+	VERIFY_PROP_TYPE_ARG(3, property.type);
+
+	switch (property.type) {
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+		property.len = strlen(argv[4]);
+		property.val = (char *) argv[4];
+		break;
+	case BT_PROPERTY_BDNAME:
+	case BT_PROPERTY_BDADDR:
+	case BT_PROPERTY_UUIDS:
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+	case BT_PROPERTY_SERVICE_RECORD:
+	case BT_PROPERTY_ADAPTER_SCAN_MODE:
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+	case BT_PROPERTY_REMOTE_RSSI:
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+	default:
+		return;
+	}
+
+	EXEC(if_bluetooth->set_remote_device_property, &addr, &property);
+}
+
+/* For now uuid is not autocompleted. Use routine for complete_addr_c */
+#define get_remote_service_record_c complete_addr_c
+
+static void get_remote_service_record_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc <= 3) {
+		haltest_error("No uuid specified\n");
+		return;
+	}
+
+	str2bt_uuid_t(argv[3], &uuid);
+
+	EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define get_remote_services_c complete_addr_c
+
+static void get_remote_services_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->get_remote_services, &addr);
+}
+
+static void start_discovery_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->start_discovery);
+}
+
+static void cancel_discovery_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_bluetooth);
+
+	EXEC(if_bluetooth->cancel_discovery);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define create_bond_c complete_addr_c
+
+static void create_bond_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int transport;
+#endif
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (argc < 4)
+		transport = BT_TRANSPORT_UNKNOWN;
+	else
+		transport = atoi(argv[3]);
+
+	EXEC(if_bluetooth->create_bond, &addr, transport);
+#else
+	EXEC(if_bluetooth->create_bond, &addr);
+#endif
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define remove_bond_c complete_addr_c
+
+static void remove_bond_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->remove_bond, &addr);
+}
+
+/* Just addres to complete, use complete_addr_c */
+#define cancel_bond_c complete_addr_c
+
+static void cancel_bond_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_bluetooth->cancel_bond, &addr);
+}
+
+static void pin_reply_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	static const char *const completions[] = { last_remote_addr, NULL };
+
+	if (argc == 3) {
+		*user = (void *) completions;
+		*enum_func = enum_strings;
+	}
+}
+
+static void pin_reply_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bt_pin_code_t pin;
+	int pin_len = 0;
+	int accept = 0;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc > 3) {
+		accept = 1;
+		pin_len = strlen(argv[3]);
+		memcpy(pin.pin, argv[3], pin_len);
+	}
+
+	EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin);
+}
+
+static void ssp_reply_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_remote_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 5) {
+		*user = "1";
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		if (-1 != (int) last_ssp_variant) {
+			*user = (void *) bt_ssp_variant_t2str(last_ssp_variant);
+			*enum_func = enum_one_string;
+		} else {
+			*user = TYPE_ENUM(bt_ssp_variant_t);
+			*enum_func = enum_defines;
+		}
+	}
+}
+
+static void ssp_reply_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bt_ssp_variant_t var;
+	int accept;
+	int passkey;
+
+	RETURN_IF_NULL(if_bluetooth);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No ssp variant specified\n");
+		return;
+	}
+
+	var = str2btsspvariant(argv[3]);
+	if (argc < 5) {
+		haltest_error("No accept value specified\n");
+		return;
+	}
+
+	accept = atoi(argv[4]);
+	passkey = 0;
+
+	if (accept && var == BT_SSP_VARIANT_PASSKEY_ENTRY && argc >= 5)
+		passkey = atoi(argv[4]);
+
+	EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey);
+}
+
+static void get_profile_interface_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	static const char *const profile_ids[] = {
+		BT_PROFILE_HANDSFREE_ID,
+		BT_PROFILE_ADVANCED_AUDIO_ID,
+		BT_PROFILE_HEALTH_ID,
+		BT_PROFILE_SOCKETS_ID,
+		BT_PROFILE_HIDHOST_ID,
+		BT_PROFILE_PAN_ID,
+		BT_PROFILE_GATT_ID,
+		BT_PROFILE_AV_RC_ID,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		BT_PROFILE_HANDSFREE_CLIENT_ID,
+		BT_PROFILE_MAP_CLIENT_ID,
+		BT_PROFILE_AV_RC_CTRL_ID,
+		BT_PROFILE_ADVANCED_AUDIO_SINK_ID,
+#endif
+		NULL
+	};
+
+	if (argc == 3) {
+		*user = (void *) profile_ids;
+		*enum_func = enum_strings;
+	}
+}
+
+static void get_profile_interface_p(int argc, const char **argv)
+{
+	const char *id;
+	const void **pif = NULL;
+
+	RETURN_IF_NULL(if_bluetooth);
+	if (argc <= 2) {
+		haltest_error("No interface specified\n");
+		return;
+	}
+
+	id = argv[2];
+
+	if (strcmp(BT_PROFILE_HANDSFREE_ID, id) == 0)
+		pif = (const void **) &if_hf;
+	else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0)
+		pif = (const void **) &if_av;
+	else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0)
+		pif = (const void **) &if_hl;
+	else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0)
+		pif = (const void **) &if_sock;
+	else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0)
+		pif = (const void **) &if_hh;
+	else if (strcmp(BT_PROFILE_PAN_ID, id) == 0)
+		pif = (const void **) &if_pan;
+	else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0)
+		pif = (const void **) &if_rc;
+	else if (strcmp(BT_PROFILE_GATT_ID, id) == 0)
+		pif = (const void **) &if_gatt;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	else if (strcmp(BT_PROFILE_AV_RC_CTRL_ID, id) == 0)
+		pif = (const void **) &if_rc_ctrl;
+	else if (strcmp(BT_PROFILE_HANDSFREE_CLIENT_ID, id) == 0)
+		pif = (const void **) &if_hf_client;
+	else if (strcmp(BT_PROFILE_MAP_CLIENT_ID, id) == 0)
+		pif = (const void **) &if_mce;
+	else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_SINK_ID, id) == 0)
+		pif = (const void **) &if_av_sink;
+#endif
+	else
+		haltest_error("%s is not correct for get_profile_interface\n",
+									id);
+
+	if (pif != NULL) {
+		*pif = if_bluetooth->get_profile_interface(id);
+		haltest_info("get_profile_interface(%s) : %p\n", id, *pif);
+	}
+}
+
+static void dut_mode_configure_p(int argc, const char **argv)
+{
+	uint8_t mode;
+
+	RETURN_IF_NULL(if_bluetooth);
+
+	if (argc <= 2) {
+		haltest_error("No dut mode specified\n");
+		return;
+	}
+
+	mode = strtol(argv[2], NULL, 0);
+
+	EXEC(if_bluetooth->dut_mode_configure, mode);
+}
+
+static void dut_mode_send_p(int argc, const char **argv)
+{
+	haltest_error("not implemented\n");
+}
+
+static void le_test_mode_p(int argc, const char **argv)
+{
+	haltest_error("not implemented\n");
+}
+
+static void config_hci_snoop_log_p(int argc, const char **argv)
+{
+	uint8_t mode;
+
+	RETURN_IF_NULL(if_bluetooth);
+
+	if (argc <= 2) {
+		haltest_error("No mode specified\n");
+		return;
+	}
+
+	mode = strtol(argv[2], NULL, 0);
+
+	EXEC(if_bluetooth->config_hci_snoop_log, mode);
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	STD_METHOD(enable),
+	STD_METHOD(disable),
+	STD_METHOD(get_adapter_properties),
+	STD_METHODCH(get_adapter_property, "<prop_type>"),
+	STD_METHODCH(set_adapter_property, "<prop_type> <prop_value>"),
+	STD_METHODCH(get_remote_device_properties, "<addr>"),
+	STD_METHODCH(get_remote_device_property, "<addr> <property_type>"),
+	STD_METHODCH(set_remote_device_property,
+					"<addr> <property_type> <value>"),
+	STD_METHODCH(get_remote_service_record, "<addr> <uuid>"),
+	STD_METHODCH(get_remote_services, "<addr>"),
+	STD_METHOD(start_discovery),
+	STD_METHOD(cancel_discovery),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	STD_METHODCH(create_bond, "<addr> [<transport>]"),
+	STD_METHOD(read_energy_info),
+	STD_METHODCH(get_connection_state, "<addr>"),
+#else
+	STD_METHODCH(create_bond, "<addr>"),
+#endif
+	STD_METHODCH(remove_bond, "<addr>"),
+	STD_METHODCH(cancel_bond, "<addr>"),
+	STD_METHODCH(pin_reply, "<address> [<pin>]"),
+	STD_METHODCH(ssp_reply, "<address> <ssp_veriant> 1|0 [<passkey>]"),
+	STD_METHODCH(get_profile_interface, "<profile id>"),
+	STD_METHODH(dut_mode_configure, "<dut mode>"),
+	STD_METHOD(dut_mode_send),
+	STD_METHOD(le_test_mode),
+	STD_METHODH(config_hci_snoop_log, "<mode>"),
+	END_METHOD
+};
+
+const struct interface bluetooth_if = {
+	.name = "bluetooth",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-gatt.c b/bluez/repo/android/client/if-gatt.c
new file mode 100644
index 0000000..3526783
--- /dev/null
+++ b/bluez/repo/android/client/if-gatt.c
@@ -0,0 +1,2675 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+
+#include "../hal-utils.h"
+#include "if-main.h"
+
+const btgatt_interface_t *if_gatt = NULL;
+
+/*
+ * In version 19 some callback were changed.
+ * btgatt_char_id_t -> btgatt_gatt_id_t
+ * bt_uuid_t        -> btgatt_gatt_id_t
+ */
+#define str2btgatt_descr_id_t str2btgatt_gatt_id_t
+#define btgatt_descr_id_t2str btgatt_gatt_id_t2str
+#define btgatt_descr_id_t btgatt_gatt_id_t
+
+#define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11)
+#define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11)
+/* How man characters print from binary objects (arbitrary) */
+#define MAX_HEX_VAL_STR_LEN 100
+#define MAX_NOTIFY_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+		+ MAX_ADDR_STR_LEN + MAX_HEX_VAL_STR_LEN + 60)
+#define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \
+		+ MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80)
+
+/* Hex arguments must have "0x" or "0X" prefix */
+#define VERIFY_INT_ARG(n, v, err) \
+	do { \
+		if (n < argc) \
+			v = strtol(argv[n], NULL, 0); \
+		else { \
+			haltest_error(err); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_HEX_ARG(n, v, err) \
+	do { \
+		if (n < argc) \
+			v = strtol(argv[n], NULL, 16); \
+		else { \
+			haltest_error(err); \
+			return;\
+		} \
+	} while (0)
+
+/* Helper macros to verify arguments of methods */
+#define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n")
+#define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n")
+#define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n")
+#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n")
+#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n")
+#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n")
+#define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n")
+#define VERIFY_ACTION(n, v) VERIFY_INT_ARG(n, v, "No action specified\n")
+#define VERIFY_FILT_TYPE(n, v) VERIFY_INT_ARG(n, v, "No filter specified\n")
+#define VERIFY_FILT_INDEX(n, v) VERIFY_INT_ARG(n, v, \
+						"No filter index specified\n")
+#define VERIFY_FEAT_SELN(n, v) VERIFY_INT_ARG(n, v, "No feat seln specified\n")
+#define VERIFY_LIST_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+					"No list logic type specified\n")
+#define VERIFY_FILT_LOGIC_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+					"No filt logic type specified\n")
+#define VERIFY_RSSI_HI_THR(n, v) VERIFY_INT_ARG(n, v, \
+					"No rssi hi threshold specified\n")
+#define VERIFY_RSSI_LOW_THR(n, v) VERIFY_INT_ARG(n, v, \
+						"No low threshold specified\n")
+#define VERIFY_DELY_MODE(n, v) VERIFY_INT_ARG(n, v, "No delay mode specified\n")
+#define VERIFY_FND_TIME(n, v) VERIFY_INT_ARG(n, v, "No found time specified\n")
+#define VERIFY_LOST_TIME(n, v) VERIFY_INT_ARG(n, v, "No lost time specified\n")
+#define VERIFY_FND_TIME_CNT(n, v) VERIFY_INT_ARG(n, v, \
+					"No found timer counter specified\n")
+#define VERIFY_COMP_ID(n, v) VERIFY_INT_ARG(n, v, "No company id specified\n")
+#define VERIFY_COMP_ID_MASK(n, v) VERIFY_INT_ARG(n, v, \
+						"No comp. id mask specified\n")
+#define VERIFY_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, "No data len specified\n")
+#define VERIFY_MASK_LEN(n, v) VERIFY_INT_ARG(n, v, "No mask len specified\n")
+#define VERIFY_MIN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+						"No min interval specified\n")
+#define VERIFY_MAX_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+						"No max interval specified\n")
+#define VERIFY_APPEARANCE(n, v) VERIFY_INT_ARG(n, v, "No apperance specified\n")
+#define VERIFY_MANUFACTURER_LEN(n, v) VERIFY_INT_ARG(n, v, \
+					"No manufacturer len specified\n")
+#define VERIFY_SERVICE_DATA_LEN(n, v) VERIFY_INT_ARG(n, v, \
+					"No service data len specified\n")
+#define VERIFY_SERVICE_UUID_LEN(n, v) VERIFY_INT_ARG(n, v, \
+					"No service uuid len specified\n")
+#define VERIFY_MTU(n, v) VERIFY_INT_ARG(n, v, "No mtu specified\n")
+#define VERIFY_LATENCY(n, v) VERIFY_INT_ARG(n, v, \
+						"No latency specified\n")
+#define VERIFY_TIMEOUT(n, v) VERIFY_INT_ARG(n, v, "No timeout specified\n")
+#define VERIFY_SCAN_INTERVAL(n, v) VERIFY_INT_ARG(n, v, \
+						"No scan interval specified\n")
+#define VERIFY_SCAN_WINDOW(n, v) VERIFY_INT_ARG(n, v, \
+						"No scan window specified\n")
+#define VERIFY_ADV_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+					"No advertising type specified\n")
+#define VERIFY_CHNL_MAP(n, v) VERIFY_INT_ARG(n, v, \
+						"No channel map specified\n")
+#define VERIFY_TX_POWER(n, v) VERIFY_INT_ARG(n, v, \
+						"No tx power specified\n")
+#define VERIFY_TIMEOUT_S(n, v) VERIFY_INT_ARG(n, v, \
+						"No timeout in sec specified\n")
+#define VERIFY_BATCH_SCAN_FULL_MAX(n, v) VERIFY_INT_ARG(n, v, \
+					"No batch scan full max specified\n")
+#define VERIFY_BATCH_SCAN_TRUNC_MAX(n, v) VERIFY_INT_ARG(n, v, \
+					"No batch scan trunc max specified\n")
+#define VERIFY_BATCH_SCAN_NOTIFY_THR(n, v) VERIFY_INT_ARG(n, v, \
+				"No batch scan notify threshold specified\n")
+#define VERIFY_SCAN_MODE(n, v) VERIFY_INT_ARG(n, v, \
+						"No scan mode specified\n")
+#define VERIFY_ADDR_TYPE(n, v) VERIFY_INT_ARG(n, v, \
+						"No address type specified\n")
+#define VERIFY_DISCARD_RULE(n, v) VERIFY_INT_ARG(n, v, \
+						"No discard rule specified\n")
+#define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n")
+#define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v)
+
+#define VERIFY_UUID(n, v) \
+	do { \
+		if (n < argc) \
+			gatt_str2bt_uuid_t(argv[n], -1, v); \
+		else { \
+			haltest_error("No uuid specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_SRVC_ID(n, v) \
+	do { \
+		if (n < argc) \
+			str2btgatt_srvc_id_t(argv[n], v); \
+		else { \
+			haltest_error("No srvc_id specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_CHAR_ID(n, v) \
+	do { \
+		if (n < argc) \
+			str2btgatt_gatt_id_t(argv[n], v); \
+		else { \
+			haltest_error("No char_id specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define VERIFY_DESCR_ID(n, v) \
+	do { \
+		if (n < argc) \
+			str2btgatt_descr_id_t(argv[n], v); \
+		else { \
+			haltest_error("No descr_id specified\n"); \
+			return;\
+		} \
+	} while (0)
+
+#define GET_VERIFY_HEX_STRING(i, v, l) \
+	do { \
+		int ll;\
+		const char *n = argv[i]; \
+		if (argc <= i) { \
+			haltest_error("No value specified\n"); \
+			return; \
+		} \
+		if (n[0] != '0' || (n[1] != 'X' && n[1] != 'x')) { \
+			haltest_error("Value must be hex string\n"); \
+			return; \
+		} \
+		ll = fill_buffer(n + 2, (uint8_t *) v, sizeof(v)); \
+		if (ll < 0) { \
+			haltest_error("Value must be byte hex string\n"); \
+			return; \
+		} \
+		l = ll; \
+	} while (0)
+
+/* Gatt uses little endian uuid */
+static const char GATT_BASE_UUID[] = {
+	0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+	0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * converts gatt uuid to string
+ * buf should be at least 39 bytes
+ *
+ * This function formats 16, 32 and 128 bits uuid
+ *
+ * returns string representation of uuid
+ */
+static char *gatt_uuid_t2str(const bt_uuid_t *uuid, char *buf)
+{
+	int shift = 0;
+	int i = 16;
+	int limit = 0;
+	int j = 0;
+
+	/* for bluetooth uuid only 32 bits */
+	if (0 == memcmp(&uuid->uu, &GATT_BASE_UUID,
+						sizeof(bt_uuid_t) - 4)) {
+		limit = 12;
+		/* make it 16 bits */
+		if (uuid->uu[15] == 0 && uuid->uu[14] == 0)
+			i = 14;
+	}
+
+	while (i-- > limit) {
+		if (i == 11 || i == 9 || i == 7 || i == 5) {
+			buf[j * 2 + shift] = '-';
+			shift++;
+		}
+
+		sprintf(buf + j * 2 + shift, "%02x", uuid->uu[i]);
+		++j;
+	}
+
+	return buf;
+}
+
+/*
+ * Tries to convert hex string of given size into out buffer.
+ * Output buffer is little endian.
+ */
+static void scan_field(const char *str, int len, uint8_t *out, int out_size)
+{
+	int i;
+
+	memset(out, 0, out_size);
+	if (out_size * 2 > len + 1)
+		out_size = (len + 1) / 2;
+
+	for (i = 0; i < out_size && len > 0; ++i) {
+		len -= 2;
+		if (len >= 0)
+			sscanf(str + len, "%02hhx", &out[i]);
+		else
+			sscanf(str, "%1hhx", &out[i]);
+	}
+}
+
+/* Like strchr but with upper limit instead of 0 terminated string */
+static const char *strchrlimit(const char *p, const char *e, int c)
+{
+	while (p < e && *p != (char) c)
+		++p;
+
+	return p < e ? p : NULL;
+}
+
+/*
+ * converts string to uuid
+ * it accepts uuid in following forms:
+ *	123
+ *	0000123
+ *	0000123-0014-1234-0000-000056789abc
+ *	0000123001412340000000056789abc
+ *	123-14-1234-0-56789abc
+ */
+static void gatt_str2bt_uuid_t(const char *str, int len, bt_uuid_t *uuid)
+{
+	int dash_cnt = 0;
+	int dashes[6] = {-1}; /* indexes of '-' or \0 */
+	static uint8_t filed_offset[] = { 16, 12, 10, 8, 6, 0 };
+	const char *p = str;
+	const char *e;
+	int i;
+
+	e = str + ((len >= 0) ? len : (int) strlen(str));
+
+	while (p != NULL && dash_cnt < 5) {
+		const char *f = strchrlimit(p, e, '-');
+
+		if (f != NULL)
+			dashes[++dash_cnt] = f++ - str;
+		p = f;
+	}
+
+	/* get index of \0 to dashes table */
+	if (dash_cnt < 5)
+		dashes[++dash_cnt] = e - str;
+
+	memcpy(uuid, GATT_BASE_UUID, sizeof(bt_uuid_t));
+
+	/* whole uuid in one string without dashes */
+	if (dash_cnt == 1 && dashes[1] > 8) {
+		if (dashes[1] > 32)
+			dashes[1] = 32;
+		scan_field(str, dashes[1],
+				&uuid->uu[16 - (dashes[1] + 1) / 2],
+				(dashes[1] + 1) / 2);
+	} else {
+		for (i = 0; i < dash_cnt; ++i) {
+			scan_field(str + dashes[i] + 1,
+					dashes[i + 1] - dashes[i] - 1,
+					&uuid->uu[filed_offset[i + 1]],
+					filed_offset[i] - filed_offset[i + 1]);
+		}
+	}
+}
+
+/* char_id formating function */
+static char *btgatt_gatt_id_t2str(const btgatt_gatt_id_t *char_id, char *buf)
+{
+	char uuid_buf[MAX_UUID_STR_LEN];
+
+	sprintf(buf, "{%s,%d}", gatt_uuid_t2str(&char_id->uuid, uuid_buf),
+							char_id->inst_id);
+	return buf;
+}
+
+/* Parse btgatt_gatt_id_t */
+static void str2btgatt_gatt_id_t(const char *buf, btgatt_gatt_id_t *char_id)
+{
+	const char *e;
+
+	memcpy(&char_id->uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t));
+	char_id->inst_id = 0;
+
+	if (*buf == '{')
+		buf++;
+	e = strpbrk(buf, " ,}");
+	if (e == NULL)
+		e = buf + strlen(buf);
+
+	gatt_str2bt_uuid_t(buf, e - buf, &char_id->uuid);
+	if (*e == ',') {
+		buf = e + 1;
+		e = strpbrk(buf, " ,}");
+		if (e == NULL)
+			e = buf + strlen(buf);
+		if (buf < e)
+			char_id->inst_id = atoi(buf);
+	}
+}
+
+/* service_id formating function */
+static char *btgatt_srvc_id_t2str(const btgatt_srvc_id_t *srvc_id, char *buf)
+{
+	char uuid_buf[MAX_UUID_STR_LEN];
+
+	sprintf(buf, "{%s,%d,%d}", gatt_uuid_t2str(&srvc_id->id.uuid, uuid_buf),
+				srvc_id->id.inst_id, srvc_id->is_primary);
+	return buf;
+}
+
+/* Parse btgatt_srvc_id_t */
+static void str2btgatt_srvc_id_t(const char *buf, btgatt_srvc_id_t *srvc_id)
+{
+	const char *e;
+
+	memcpy(&srvc_id->id.uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t));
+	srvc_id->id.inst_id = 0;
+	srvc_id->is_primary = 1;
+
+	if (*buf == '{')
+		buf++;
+	e = strpbrk(buf, " ,}");
+	if (e == NULL)
+		e = buf + strlen(buf);
+
+	gatt_str2bt_uuid_t(buf, e - buf, &srvc_id->id.uuid);
+	if (*e == ',') {
+		buf = e + 1;
+		e = strpbrk(buf, " ,}");
+		if (e == NULL)
+			e = buf + strlen(buf);
+		if (buf < e)
+			srvc_id->id.inst_id = atoi(buf);
+	}
+
+	if (*e == ',') {
+		buf = e + 1;
+		e = strpbrk(buf, " ,}");
+		if (e == NULL)
+			e = buf + strlen(buf);
+		if (buf < e)
+			srvc_id->is_primary = atoi(buf);
+	}
+}
+
+/* Converts array of uint8_t to string representation */
+static char *array2str(const uint8_t *v, int size, char *buf, int out_size)
+{
+	int limit = size;
+	int i;
+
+	if (out_size > 0) {
+		*buf = '\0';
+		if (size >= 2 * out_size)
+			limit = (out_size - 2) / 2;
+
+		for (i = 0; i < limit; ++i)
+			sprintf(buf + 2 * i, "%02x", v[i]);
+
+		/* output buffer not enough to hold whole field fill with ...*/
+		if (limit < size)
+			sprintf(buf + 2 * i, "...");
+	}
+
+	return buf;
+}
+
+/* Converts btgatt_notify_params_t to string */
+static char *btgatt_notify_params_t2str(const btgatt_notify_params_t *data,
+								char *buf)
+{
+	char addr[MAX_ADDR_STR_LEN];
+	char srvc_id[MAX_SRVC_ID_STR_LEN];
+	char char_id[MAX_CHAR_ID_STR_LEN];
+	char value[MAX_HEX_VAL_STR_LEN];
+
+	sprintf(buf, "{bda=%s, srvc_id=%s, char_id=%s, val=%s, is_notify=%u}",
+		bt_bdaddr_t2str(&data->bda, addr),
+		btgatt_srvc_id_t2str(&data->srvc_id, srvc_id),
+		btgatt_gatt_id_t2str(&data->char_id, char_id),
+		array2str(data->value, data->len, value, sizeof(value)),
+							data->is_notify);
+	return buf;
+}
+
+static char *btgatt_unformatted_value_t2str(const btgatt_unformatted_value_t *v,
+							char *buf, int size)
+{
+	return array2str(v->value, v->len, buf, size);
+}
+
+static char *btgatt_read_params_t2str(const btgatt_read_params_t *data,
+								char *buf)
+{
+	char srvc_id[MAX_SRVC_ID_STR_LEN];
+	char char_id[MAX_CHAR_ID_STR_LEN];
+	char descr_id[MAX_UUID_STR_LEN];
+	char value[MAX_HEX_VAL_STR_LEN];
+
+	sprintf(buf, "{srvc_id=%s, char_id=%s, descr_id=%s, val=%s value_type=%d, status=%d}",
+		btgatt_srvc_id_t2str(&data->srvc_id, srvc_id),
+		btgatt_gatt_id_t2str(&data->char_id, char_id),
+		btgatt_descr_id_t2str(&data->descr_id, descr_id),
+		btgatt_unformatted_value_t2str(&data->value, value, 100),
+		data->value_type, data->status);
+	return buf;
+}
+
+/* BT-GATT Client callbacks. */
+
+/* Cache client_if and conn_id for tab completion */
+static char client_if_str[20];
+static char conn_id_str[20];
+/* Cache address for tab completion */
+static char last_addr[MAX_ADDR_STR_LEN];
+
+/* Callback invoked in response to register_client */
+static void gattc_register_client_cb(int status, int client_if,
+							bt_uuid_t *app_uuid)
+{
+	char buf[MAX_UUID_STR_LEN];
+
+	snprintf(client_if_str, sizeof(client_if_str), "%d", client_if);
+
+	haltest_info("%s: status=%d client_if=%d app_uuid=%s\n", __func__,
+						status, client_if,
+						gatt_uuid_t2str(app_uuid, buf));
+}
+
+/* Callback for scan results */
+static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bda=%s rssi=%d adv_data=%p\n", __func__,
+				bt_bdaddr_t2str(bda, buf), rssi, adv_data);
+}
+
+/* GATT open callback invoked in response to open */
+static void gattc_connect_cb(int conn_id, int status, int client_if,
+							bt_bdaddr_t *bda)
+{
+	haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n",
+					__func__, conn_id, status, client_if,
+					bt_bdaddr_t2str(bda, last_addr));
+}
+
+/* Callback invoked in response to close */
+static void gattc_disconnect_cb(int conn_id, int status, int client_if,
+							bt_bdaddr_t *bda)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n",
+					__func__, conn_id, status, client_if,
+					bt_bdaddr_t2str(bda, buf));
+}
+
+/*
+ * Invoked in response to search_service when the GATT service search
+ * has been completed.
+ */
+static void gattc_search_complete_cb(int conn_id, int status)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Reports GATT services on a remote device */
+static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d srvc_id=%s\n", __func__, conn_id,
+				btgatt_srvc_id_t2str(srvc_id, srvc_id_buf));
+}
+
+/* GATT characteristic enumeration result callback */
+static void gattc_get_characteristic_cb(int conn_id, int status,
+					btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					int char_prop)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n",
+			__func__, conn_id, status,
+			btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+			btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop);
+
+	/* enumerate next characteristic */
+	if (status == 0)
+		EXEC(if_gatt->client->get_characteristic, conn_id, srvc_id,
+								char_id);
+}
+
+/* GATT descriptor enumeration result callback */
+static void gattc_get_descriptor_cb(int conn_id, int status,
+		btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+		btgatt_descr_id_t *descr_id)
+{
+	char buf[MAX_UUID_STR_LEN];
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, descr_id=%s\n",
+				__func__, conn_id, status,
+				btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+				btgatt_gatt_id_t2str(char_id, char_id_buf),
+				btgatt_descr_id_t2str(descr_id, buf));
+
+	if (status == 0)
+		EXEC(if_gatt->client->get_descriptor, conn_id, srvc_id, char_id,
+								descr_id);
+}
+
+/* GATT included service enumeration result callback */
+static void gattc_get_included_service_cb(int conn_id, int status,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_srvc_id_t *incl_srvc_id)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char incl_srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d srvc_id=%s incl_srvc_id=%s)\n",
+			__func__, conn_id, status,
+			btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+			btgatt_srvc_id_t2str(incl_srvc_id, incl_srvc_id_buf));
+
+	if (status == 0)
+		EXEC(if_gatt->client->get_included_service, conn_id, srvc_id,
+								incl_srvc_id);
+}
+
+/* Callback invoked in response to [de]register_for_notification */
+static void gattc_register_for_notification_cb(int conn_id, int registered,
+						int status,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	char srvc_id_buf[MAX_SRVC_ID_STR_LEN];
+	char char_id_buf[MAX_CHAR_ID_STR_LEN];
+
+	haltest_info("%s: conn_id=%d registered=%d status=%d srvc_id=%s char_id=%s\n",
+				__func__, conn_id, registered, status,
+				btgatt_srvc_id_t2str(srvc_id, srvc_id_buf),
+				btgatt_gatt_id_t2str(char_id, char_id_buf));
+}
+
+/*
+ * Remote device notification callback, invoked when a remote device sends
+ * a notification or indication that a client has registered for.
+ */
+static void gattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data)
+{
+	char buf[MAX_NOTIFY_PARAMS_STR_LEN];
+
+	haltest_info("%s: conn_id=%d data=%s\n", __func__, conn_id,
+				btgatt_notify_params_t2str(p_data, buf));
+}
+
+/* Reports result of a GATT read operation */
+static void gattc_read_characteristic_cb(int conn_id, int status,
+						btgatt_read_params_t *p_data)
+{
+	char buf[MAX_READ_PARAMS_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id,
+				status, btgatt_read_params_t2str(p_data, buf));
+}
+
+/* GATT write characteristic operation callback */
+static void gattc_write_characteristic_cb(int conn_id, int status,
+						btgatt_write_params_t *p_data)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* GATT execute prepared write callback */
+static void gattc_execute_write_cb(int conn_id, int status)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Callback invoked in response to read_descriptor */
+static void gattc_read_descriptor_cb(int conn_id, int status,
+						btgatt_read_params_t *p_data)
+{
+	char buf[MAX_READ_PARAMS_STR_LEN];
+
+	haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id,
+				status, btgatt_read_params_t2str(p_data, buf));
+}
+
+/* Callback invoked in response to write_descriptor */
+static void gattc_write_descriptor_cb(int conn_id, int status,
+						btgatt_write_params_t *p_data)
+{
+	haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
+}
+
+/* Callback triggered in response to read_remote_rssi */
+static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi,
+								int status)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: client_if=%d bda=%s rssi=%d satus=%d\n", __func__,
+			client_if, bt_bdaddr_t2str(bda, buf), rssi, status);
+}
+
+/* Callback invoked in response to listen */
+static void gattc_listen_cb(int status, int client_if)
+{
+	haltest_info("%s: client_if=%d status=%d\n", __func__, client_if,
+								status);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/* Callback invoked when the MTU for a given connection changes */
+static void gattc_configure_mtu_cb(int conn_id, int status, int mtu)
+{
+	haltest_info("%s: conn_id=%d, status=%d, mtu=%d\n", __func__, conn_id,
+								status, mtu);
+}
+
+/* Callback invoked when a scan filter configuration command has completed */
+static void gattc_scan_filter_cfg_cb(int action, int client_if, int status,
+						int filt_type, int avbl_space)
+{
+	haltest_info("%s: action=%d, client_if=%d, status=%d, filt_type=%d"
+			", avbl_space=%d", __func__, action, client_if, status,
+			filt_type, avbl_space);
+}
+
+/* Callback invoked when scan param has been added, cleared, or deleted */
+static void gattc_scan_filter_param_cb(int action, int client_if, int status,
+								int avbl_space)
+{
+	haltest_info("%s: action=%d, client_if=%d, status=%d, avbl_space=%d",
+			__func__, action, client_if, status, avbl_space);
+}
+
+/* Callback invoked when a scan filter configuration command has completed */
+static void gattc_scan_filter_status_cb(int enable, int client_if, int status)
+{
+	haltest_info("%s: enable=%d, client_if=%d, status=%d", __func__,
+						enable, client_if, status);
+}
+
+/* Callback invoked when multi-adv enable operation has completed */
+static void gattc_multi_adv_enable_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when multi-adv param update operation has completed */
+static void gattc_multi_adv_update_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when multi-adv instance data set operation has completed */
+static void gattc_multi_adv_data_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when multi-adv disable operation has completed */
+static void gattc_multi_adv_disable_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/*
+ * Callback notifying an application that a remote device connection is
+ * currently congested and cannot receive any more data. An application should
+ * avoid sending more data until a further callback is received indicating the
+ * congestion status has been cleared.
+ */
+static void gattc_congestion_cb(int conn_id, bool congested)
+{
+	haltest_info("%s: conn_id=%d, congested=%d", __func__, conn_id,
+								congested);
+}
+
+/* Callback invoked when batchscan storage config operation has completed */
+static void gattc_batchscan_cfg_storage_cb(int client_if, int status)
+{
+	haltest_info("%s: client_if=%d, status=%d", __func__, client_if,
+									status);
+}
+
+/* Callback invoked when batchscan enable / disable operation has completed */
+static void gattc_batchscan_enable_disable_cb(int action, int client_if,
+								int status)
+{
+	haltest_info("%s: action=%d, client_if=%d, status=%d", __func__, action,
+							client_if, status);
+}
+
+/* Callback invoked when batchscan reports are obtained */
+static void gattc_batchscan_reports_cb(int client_if, int status,
+					int report_format, int num_records,
+					int data_len, uint8_t* rep_data)
+{
+	/* BTGATT_MAX_ATTR_LEN = 600 */
+	char valbuf[600];
+
+	haltest_info("%s: client_if=%d, status=%d, report_format=%d"
+			", num_records=%d, data_len=%d, rep_data=%s", __func__,
+			client_if, status, report_format, num_records, data_len,
+			array2str(rep_data, data_len, valbuf, sizeof(valbuf)));
+}
+
+/* Callback invoked when batchscan storage threshold limit is crossed */
+static void gattc_batchscan_threshold_cb(int client_if)
+{
+	haltest_info("%s: client_if=%d", __func__, client_if);
+}
+
+/* Track ADV VSE callback invoked when tracked device is found or lost */
+static void gattc_track_adv_event_cb(int client_if, int filt_index,
+						int addr_type, bt_bdaddr_t* bda,
+						int adv_state)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s, client_if=%d, filt_index=%d, addr_type=%d, bda=%s"
+			", adv_state=%d", __func__, client_if, filt_index,
+			addr_type, bt_bdaddr_t2str(bda, buf), adv_state);
+}
+#endif
+
+static const btgatt_client_callbacks_t btgatt_client_callbacks = {
+	.register_client_cb = gattc_register_client_cb,
+	.scan_result_cb = gattc_scan_result_cb,
+	.open_cb = gattc_connect_cb,
+	.close_cb = gattc_disconnect_cb,
+	.search_complete_cb = gattc_search_complete_cb,
+	.search_result_cb = gattc_search_result_cb,
+	.get_characteristic_cb = gattc_get_characteristic_cb,
+	.get_descriptor_cb = gattc_get_descriptor_cb,
+	.get_included_service_cb = gattc_get_included_service_cb,
+	.register_for_notification_cb = gattc_register_for_notification_cb,
+	.notify_cb = gattc_notify_cb,
+	.read_characteristic_cb = gattc_read_characteristic_cb,
+	.write_characteristic_cb = gattc_write_characteristic_cb,
+	.read_descriptor_cb = gattc_read_descriptor_cb,
+	.write_descriptor_cb = gattc_write_descriptor_cb,
+	.execute_write_cb = gattc_execute_write_cb,
+	.read_remote_rssi_cb = gattc_read_remote_rssi_cb,
+	.listen_cb = gattc_listen_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.configure_mtu_cb = gattc_configure_mtu_cb,
+	.scan_filter_cfg_cb = gattc_scan_filter_cfg_cb,
+	.scan_filter_param_cb = gattc_scan_filter_param_cb,
+	.scan_filter_status_cb = gattc_scan_filter_status_cb,
+	.multi_adv_enable_cb = gattc_multi_adv_enable_cb,
+	.multi_adv_update_cb = gattc_multi_adv_update_cb,
+	.multi_adv_data_cb = gattc_multi_adv_data_cb,
+	.multi_adv_disable_cb = gattc_multi_adv_disable_cb,
+	.congestion_cb = gattc_congestion_cb,
+	.batchscan_cfg_storage_cb = gattc_batchscan_cfg_storage_cb,
+	.batchscan_enb_disable_cb = gattc_batchscan_enable_disable_cb,
+	.batchscan_reports_cb = gattc_batchscan_reports_cb,
+	.batchscan_threshold_cb = gattc_batchscan_threshold_cb,
+	.track_adv_event_cb = gattc_track_adv_event_cb,
+#endif
+};
+
+/* BT-GATT Server callbacks */
+
+/* Cache server_if and conn_id for tab completion */
+static char server_if_str[20];
+
+/* Callback invoked in response to register_server */
+static void gatts_register_server_cb(int status, int server_if,
+							bt_uuid_t *app_uuid)
+{
+	char buf[MAX_UUID_STR_LEN];
+
+	haltest_info("%s: status=%d server_if=%d app_uuid=%s\n", __func__,
+			status, server_if, gatt_uuid_t2str(app_uuid, buf));
+}
+
+/*
+ * Callback indicating that a remote device has connected
+ * or been disconnected
+ */
+static void gatts_connection_cb(int conn_id, int server_if, int connected,
+							bt_bdaddr_t *bda)
+{
+	haltest_info("%s: conn_id=%d server_if=%d connected=%d bda=%s\n",
+					__func__, conn_id, server_if, connected,
+					bt_bdaddr_t2str(bda, last_addr));
+	snprintf(conn_id_str, sizeof(conn_id_str), "%d", conn_id);
+}
+
+/* Callback invoked in response to create_service */
+static void gatts_service_added_cb(int status, int server_if,
+				btgatt_srvc_id_t *srvc_id, int srvc_handle)
+{
+	char buf[MAX_SRVC_ID_STR_LEN];
+
+	snprintf(server_if_str, sizeof(server_if_str), "%d", server_if);
+
+	haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n",
+			__func__, status, server_if,
+			btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle);
+}
+
+/* Callback indicating that an included service has been added to a service */
+static void gatts_included_service_added_cb(int status, int server_if,
+							int srvc_handle,
+							int incl_srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n",
+						__func__, status, server_if,
+						srvc_handle, incl_srvc_handle);
+}
+
+/* Callback invoked when a characteristic has been added to a service */
+static void gatts_characteristic_added_cb(int status, int server_if,
+								bt_uuid_t *uuid,
+								int srvc_handle,
+								int char_handle)
+{
+	char buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n",
+			__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
+			srvc_handle, char_handle);
+}
+
+/* Callback invoked when a descriptor has been added to a characteristic */
+static void gatts_descriptor_added_cb(int status, int server_if,
+					bt_uuid_t *uuid, int srvc_handle,
+							int descr_handle)
+{
+	char buf[MAX_SRVC_ID_STR_LEN];
+
+	haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n",
+			__func__, status, server_if, gatt_uuid_t2str(uuid, buf),
+			srvc_handle, descr_handle);
+}
+
+/* Callback invoked in response to start_service */
+static void gatts_service_started_cb(int status, int server_if, int srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+				__func__, status, server_if, srvc_handle);
+}
+
+/* Callback invoked in response to stop_service */
+static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+				__func__, status, server_if, srvc_handle);
+}
+
+/* Callback triggered when a service has been deleted */
+static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle)
+{
+	haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n",
+				__func__, status, server_if, srvc_handle);
+}
+
+/*
+ * Callback invoked when a remote device has requested to read a characteristic
+ * or descriptor. The application must respond by calling send_response
+ */
+static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+						int attr_handle, int offset,
+						bool is_long)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n",
+			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+			attr_handle, offset, is_long);
+}
+
+/*
+ * Callback invoked when a remote device has requested to write to a
+ * characteristic or descriptor.
+ */
+static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+					int attr_handle, int offset, int length,
+					bool need_rsp, bool is_prep,
+					uint8_t *value)
+{
+	char buf[MAX_ADDR_STR_LEN];
+	char valbuf[100];
+
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n",
+			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+			attr_handle, offset, length, need_rsp, is_prep,
+			array2str(value, length, valbuf, sizeof(valbuf)));
+}
+
+/* Callback invoked when a previously prepared write is to be executed */
+static void gatts_request_exec_write_cb(int conn_id, int trans_id,
+					bt_bdaddr_t *bda, int exec_write)
+{
+	char buf[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: conn_id=%d trans_id=%d bda=%s exec_write=%d\n",
+			__func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf),
+			exec_write);
+}
+
+/*
+ * Callback triggered in response to send_response if the remote device
+ * sends a confirmation.
+ */
+static void gatts_response_confirmation_cb(int status, int handle)
+{
+	haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/**
+ * Callback confirming that a notification or indication has been sent
+ * to a remote device.
+ */
+static void gatts_indication_sent_cb(int conn_id, int status)
+{
+	haltest_info("%s: status=%d conn_id=%d", __func__, status, conn_id);
+}
+
+/**
+ * Callback notifying an application that a remote device connection is
+ * currently congested and cannot receive any more data. An application
+ * should avoid sending more data until a further callback is received
+ * indicating the congestion status has been cleared.
+ */
+static void gatts_congestion_cb(int conn_id, bool congested)
+{
+	haltest_info("%s: conn_id=%d congested=%d", __func__, conn_id,
+								congested);
+}
+#endif
+
+static const btgatt_server_callbacks_t btgatt_server_callbacks = {
+	.register_server_cb = gatts_register_server_cb,
+	.connection_cb = gatts_connection_cb,
+	.service_added_cb = gatts_service_added_cb,
+	.included_service_added_cb = gatts_included_service_added_cb,
+	.characteristic_added_cb = gatts_characteristic_added_cb,
+	.descriptor_added_cb = gatts_descriptor_added_cb,
+	.service_started_cb = gatts_service_started_cb,
+	.service_stopped_cb = gatts_service_stopped_cb,
+	.service_deleted_cb = gatts_service_deleted_cb,
+	.request_read_cb = gatts_request_read_cb,
+	.request_write_cb = gatts_request_write_cb,
+	.request_exec_write_cb = gatts_request_exec_write_cb,
+	.response_confirmation_cb = gatts_response_confirmation_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.indication_sent_cb = gatts_indication_sent_cb,
+	.congestion_cb = gatts_congestion_cb,
+#endif
+};
+
+static const btgatt_callbacks_t gatt_cbacks = {
+	.size = sizeof(gatt_cbacks),
+	.client = &btgatt_client_callbacks,
+	.server = &btgatt_server_callbacks
+};
+
+/*
+ * convert hex string to uint8_t array
+ */
+static int fill_buffer(const char *str, uint8_t *out, int out_size)
+{
+	int str_len;
+	int i, j;
+	char c;
+	uint8_t b;
+
+	str_len = strlen(str);
+
+	/* Hex string must be byte format */
+	if (str_len % 2)
+		return -1;
+
+	for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) {
+		c = str[j];
+
+		if (c >= 'a' && c <= 'f')
+			c += 'A' - 'a';
+
+		if (c >= '0' && c <= '9')
+			b = c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = 10 + c - 'A';
+		else
+			return 0;
+
+		j++;
+
+		c = str[j];
+
+		if (c >= 'a' && c <= 'f')
+			c += 'A' - 'a';
+
+		if (c >= '0' && c <= '9')
+			b = b * 16 + c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = b * 16 + 10 + c - 'A';
+		else
+			return 0;
+
+		out[i] = b;
+	}
+
+	return i;
+}
+
+/* gatt client methods */
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_gatt);
+
+	EXEC(if_gatt->init, &gatt_cbacks);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_gatt);
+
+	EXECV(if_gatt->cleanup);
+
+	if_gatt = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface gatt_if = {
+	.name = "gatt",
+	.methods = methods
+};
+
+/* register_client */
+
+static void register_client_p(int argc, const char **argv)
+{
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+
+	/* uuid */
+	if (argc <= 2)
+		gatt_str2bt_uuid_t("babe4bed", -1, &uuid);
+	else
+		gatt_str2bt_uuid_t(argv[2], -1, &uuid);
+
+	EXEC(if_gatt->client->register_client, &uuid);
+}
+
+/* unregister_client */
+
+static void unregister_client_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void unregister_client_p(int argc, const char **argv)
+{
+	int client_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	EXEC(if_gatt->client->unregister_client, client_if);
+}
+
+/* scan */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define scan_c unregister_client_c
+
+static void scan_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	int client_if;
+#endif
+	int start = 1;
+
+	RETURN_IF_NULL(if_gatt);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* start */
+	if (argc >= 4)
+		start = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->scan, client_if, start);
+#else
+	/* start */
+	if (argc >= 3)
+		start = strtol(argv[2], NULL, 0);
+
+	EXEC(if_gatt->client->scan, start);
+#endif
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	int is_direct = 1;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int transport = 1;
+#endif
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	/* is_direct */
+	if (argc > 4)
+		is_direct = strtol(argv[4], NULL, 0);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct);
+#else
+	/* transport */
+	if (argc > 5)
+		transport = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct,
+								transport);
+#endif
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 5) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	int conn_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_CONN_ID(4, conn_id);
+
+	EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id);
+}
+
+/* listen */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define listen_c unregister_client_c
+
+static void listen_p(int argc, const char **argv)
+{
+	int client_if;
+	int start = 1;
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* start */
+	if (argc >= 4)
+		start = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->listen, client_if, start);
+}
+
+/* refresh */
+
+static void refresh_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*enum_func = enum_devices;
+	}
+}
+
+static void refresh_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	EXEC(if_gatt->client->refresh, client_if, &bd_addr);
+}
+
+/* search_service */
+
+static void search_service_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void search_service_p(int argc, const char **argv)
+{
+	int conn_id;
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CONN_ID(2, conn_id);
+
+	/* uuid */
+	if (argc <= 3) {
+		EXEC(if_gatt->client->search_service, conn_id, NULL);
+
+	} else {
+		bt_uuid_t filter_uuid;
+
+		gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid);
+		EXEC(if_gatt->client->search_service, conn_id, &filter_uuid);
+	}
+}
+
+/* get_included_service */
+
+static void get_included_service_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void get_included_service_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+
+	EXEC(if_gatt->client->get_included_service, conn_id, &srvc_id, NULL);
+}
+
+/* get_characteristic */
+
+/* Same completion as get_included_service_c */
+#define get_characteristic_c get_included_service_c
+
+static void get_characteristic_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+
+	EXEC(if_gatt->client->get_characteristic, conn_id, &srvc_id, NULL);
+}
+
+/* get_descriptor */
+
+/* Same completion as get_included_service_c */
+#define get_descriptor_c get_included_service_c
+
+static void get_descriptor_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+
+	EXEC(if_gatt->client->get_descriptor, conn_id, &srvc_id, &char_id,
+									NULL);
+}
+
+/* read_characteristic */
+
+/* Same completion as get_included_service_c */
+#define read_characteristic_c get_included_service_c
+
+static void read_characteristic_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	int auth_req = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+
+	/* auth_req */
+	if (argc > 5)
+		auth_req = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->client->read_characteristic, conn_id, &srvc_id, &char_id,
+								auth_req);
+}
+
+/* write_characteristic */
+
+static void write_characteristic_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	/*
+	 * This should be from tGATT_WRITE_TYPE but it's burried
+	 * inside bluedroid guts
+	 */
+	static const char *wrtypes[] = { "1", "2", "3", NULL };
+
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 6) {
+		*user = wrtypes;
+		*enum_func = enum_strings;
+	}
+}
+
+static void write_characteristic_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	int write_type;
+	int len;
+	int auth_req = 0;
+	uint8_t value[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+
+	/* write type */
+	if (argc <= 5) {
+		haltest_error("No write type specified\n");
+		return;
+	}
+	write_type = strtol(argv[5], NULL, 0);
+
+	GET_VERIFY_HEX_STRING(6, value, len);
+
+	/* auth_req */
+	if (argc > 7)
+		auth_req = strtol(argv[7], NULL, 0);
+
+	EXEC(if_gatt->client->write_characteristic, conn_id, &srvc_id, &char_id,
+				write_type, len, auth_req, (char *) value);
+}
+
+/* read_descriptor */
+
+/* Same completion as get_included_service_c */
+#define read_descriptor_c get_included_service_c
+
+static void read_descriptor_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	btgatt_descr_id_t descr_id;
+	int auth_req = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+	VERIFY_DESCR_ID(5, &descr_id);
+
+	/* auth_req */
+	if (argc > 6)
+		auth_req = strtol(argv[6], NULL, 0);
+
+	EXEC(if_gatt->client->read_descriptor, conn_id, &srvc_id, &char_id,
+							&descr_id, auth_req);
+}
+
+/* write_descriptor */
+
+static void write_descriptor_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	/*
+	 * This should be from tGATT_WRITE_TYPE but it's burried
+	 * inside bluedroid guts
+	 */
+	static const char *wrtypes[] = { "1", "2", "3", NULL };
+
+	if (argc == 3) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 7) {
+		*user = wrtypes;
+		*enum_func = enum_strings;
+	}
+}
+
+static void write_descriptor_p(int argc, const char **argv)
+{
+	int conn_id;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+	btgatt_descr_id_t descr_id;
+	int write_type;
+	int len;
+	int auth_req = 0;
+	uint8_t value[200] = {0};
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_SRVC_ID(3, &srvc_id);
+	VERIFY_CHAR_ID(4, &char_id);
+	VERIFY_DESCR_ID(5, &descr_id);
+
+	/* write type */
+	if (argc <= 6) {
+		haltest_error("No write type specified\n");
+		return;
+	}
+	write_type = strtol(argv[6], NULL, 0);
+
+	/* value */
+	if (argc <= 7) {
+		haltest_error("No value specified\n");
+		return;
+	}
+
+	/* len in chars */
+	if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) {
+		haltest_error("Value must be hex string");
+		return;
+	}
+
+	len = fill_buffer(argv[7] + 2, value, sizeof(value));
+
+	/* auth_req */
+	if (argc > 8)
+		auth_req = strtol(argv[8], NULL, 0);
+
+	EXEC(if_gatt->client->write_descriptor, conn_id, &srvc_id, &char_id,
+			&descr_id, write_type, len, auth_req, (char *) value);
+}
+
+/* execute_write */
+
+/* Same completion as search_service */
+#define execute_write_c search_service_c
+
+static void execute_write_p(int argc, const char **argv)
+{
+	int conn_id;
+	int execute;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+
+	/* execute */
+	if (argc <= 3) {
+		haltest_error("No execute specified\n");
+		return;
+	}
+	execute = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->execute_write, conn_id, execute);
+}
+
+/* register_for_notification */
+
+static void register_for_notification_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void register_for_notification_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_SRVC_ID(4, &srvc_id);
+	VERIFY_CHAR_ID(5, &char_id);
+
+	EXEC(if_gatt->client->register_for_notification, client_if, &bd_addr,
+							&srvc_id, &char_id);
+}
+
+/* deregister_for_notification */
+
+/* Same completion as search_service */
+#define deregister_for_notification_c register_for_notification_c
+
+static void deregister_for_notification_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_gatt_id_t char_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_SRVC_ID(4, &srvc_id);
+	VERIFY_CHAR_ID(5, &char_id);
+
+	EXEC(if_gatt->client->deregister_for_notification, client_if, &bd_addr,
+							&srvc_id, &char_id);
+}
+
+/* read_remote_rssi */
+
+static void read_remote_rssi_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*enum_func = enum_devices;
+	}
+}
+
+static void read_remote_rssi_p(int argc, const char **argv)
+{
+	int client_if;
+	bt_bdaddr_t bd_addr;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	EXEC(if_gatt->client->read_remote_rssi, client_if, &bd_addr);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+/* scan filter parameter setup */
+static void scan_filter_param_setup_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_param_setup_p(int argc, const char **argv)
+{
+	int client_if;
+	int action;
+	int filt_index;
+	int feat_seln;
+	int list_logic_type, filt_logic_type;
+	int rssi_high_thres, rssi_low_thres;
+	int dely_mode;
+	int found_timeout, lost_timeout, found_timeout_cnt;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ACTION(3, action);
+	VERIFY_FILT_INDEX(4, filt_index);
+	VERIFY_FEAT_SELN(5, feat_seln);
+	VERIFY_LIST_LOGIC_TYPE(6, list_logic_type);
+	VERIFY_FILT_LOGIC_TYPE(7, filt_logic_type);
+	VERIFY_RSSI_HI_THR(8, rssi_high_thres);
+	VERIFY_RSSI_LOW_THR(9, rssi_low_thres);
+	VERIFY_DELY_MODE(10, dely_mode);
+	VERIFY_FND_TIME(11, found_timeout);
+	VERIFY_LOST_TIME(12, lost_timeout);
+	VERIFY_FND_TIME_CNT(13, found_timeout_cnt);
+
+	EXEC(if_gatt->client->scan_filter_param_setup, client_if, action,
+		filt_index, feat_seln, list_logic_type, filt_logic_type,
+		rssi_high_thres, rssi_low_thres, dely_mode, found_timeout,
+		lost_timeout, found_timeout_cnt);
+}
+
+/* scan filter add remove */
+
+static void scan_filter_add_remove_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 10) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_add_remove_p(int argc, const char **argv)
+{
+	int client_if;
+	int action;
+	int filt_type;
+	int filt_index;
+	int company_id;
+	int company_id_mask;
+	bt_uuid_t p_uuid;
+	bt_uuid_t p_uuid_mask;
+	bt_bdaddr_t bd_addr;
+	char addr_type;
+	int data_len;
+	uint8_t p_data[100];
+	int mask_len;
+	uint8_t p_mask[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_ACTION(3, action);
+	VERIFY_FILT_TYPE(4, filt_type);
+	VERIFY_FILT_INDEX(5, filt_index);
+	VERIFY_COMP_ID(6, company_id);
+	VERIFY_COMP_ID_MASK(7, company_id_mask);
+
+	if (argc <= 9) {
+		haltest_error("No p_uuid, p_uuid_mask specified\n");
+		return;
+	}
+	gatt_str2bt_uuid_t(argv[8], -1, &p_uuid);
+	gatt_str2bt_uuid_t(argv[9], -1, &p_uuid_mask);
+
+	VERIFY_UUID(8, &p_uuid);
+	VERIFY_UUID(9, &p_uuid_mask);
+	VERIFY_ADDR_ARG(10, &bd_addr);
+	VERIFY_ADDR_TYPE(11, addr_type);
+	GET_VERIFY_HEX_STRING(12, p_data, data_len);
+	GET_VERIFY_HEX_STRING(13, p_mask, mask_len);
+
+	EXEC(if_gatt->client->scan_filter_add_remove, client_if, action,
+		filt_type, filt_index, company_id, company_id_mask,
+		&p_uuid, &p_uuid_mask, &bd_addr, addr_type, data_len,
+		(char *) p_data, mask_len, (char *) p_mask);
+}
+
+/* scan filter clean */
+static void scan_filter_clear_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_clear_p(int argc, const char **argv)
+{
+	int client_if;
+	int filt_index;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_FILT_INDEX(3, filt_index);
+
+	EXEC(if_gatt->client->scan_filter_clear, client_if, filt_index);
+}
+
+/* scan filter enable */
+static void scan_filter_enable_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void scan_filter_enable_p(int argc, const char **argv)
+{
+	int client_if;
+	int enable = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* enable */
+	if (argc >= 4)
+		enable = strtol(argv[3], NULL, 0);
+
+	EXEC(if_gatt->client->scan_filter_clear, client_if, enable);
+}
+
+/* set advertising data */
+static void set_adv_data_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void set_adv_data_p(int argc, const char **argv)
+{
+	int client_if;
+	bool set_scan_rsp;
+	bool include_name, include_txpower;
+	int min_interval, max_interval;
+	int appearance;
+	uint16_t manufacturer_len;
+	uint8_t manufacturer_data[100];
+	uint16_t service_data_len;
+	uint8_t service_data[100];
+	uint16_t service_uuid_len;
+	uint8_t service_uuid[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* set scan response */
+	if (argc >= 4)
+		set_scan_rsp = strtol(argv[3], NULL, 0);
+	/* include name */
+	if (argc >= 5)
+		include_name = strtol(argv[4], NULL, 0);
+	/* include txpower */
+	if (argc >= 6)
+		include_txpower = strtol(argv[5], NULL, 0);
+
+	VERIFY_MIN_INTERVAL(6, min_interval);
+	VERIFY_MAX_INTERVAL(7, max_interval);
+	VERIFY_APPEARANCE(8, appearance);
+	GET_VERIFY_HEX_STRING(9, manufacturer_data, manufacturer_len);
+	GET_VERIFY_HEX_STRING(10, service_data, service_data_len);
+	GET_VERIFY_HEX_STRING(11, service_uuid, service_uuid_len);
+
+	EXEC(if_gatt->client->set_adv_data, client_if, set_scan_rsp,
+		include_name, include_txpower, min_interval, max_interval,
+		appearance, manufacturer_len, (char *) manufacturer_data,
+		service_data_len, (char *) service_data, service_uuid_len,
+		(char *) service_uuid);
+}
+
+/* configure mtu */
+static void configure_mtu_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void configure_mtu_p(int argc, const char **argv)
+{
+	int conn_id;
+	int mtu;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_MTU(3, mtu);
+
+	EXEC(if_gatt->client->configure_mtu, conn_id, mtu);
+}
+
+/* con parameter update */
+static void conn_parameter_update_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void conn_parameter_update_p(int argc, const char **argv)
+{
+	bt_bdaddr_t bd_addr;
+	int min_interval, max_interval;
+	int latency;
+	int timeout;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_ADDR_ARG(2, &bd_addr);
+	VERIFY_MIN_INTERVAL(3, min_interval);
+	VERIFY_MAX_INTERVAL(4, max_interval);
+	VERIFY_LATENCY(5, latency);
+	VERIFY_TIMEOUT(6, timeout);
+
+	EXEC(if_gatt->client->conn_parameter_update, &bd_addr, min_interval,
+						max_interval, latency, timeout);
+}
+
+/* set scan parameters */
+static void set_scan_parameters_p(int argc, const char **argv)
+{
+	int scan_interval;
+	int scan_window;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SCAN_INTERVAL(2, scan_interval);
+	VERIFY_SCAN_WINDOW(3, scan_window);
+
+	EXEC(if_gatt->client->set_scan_parameters, scan_interval, scan_window);
+}
+
+/* enable multi advertising */
+static void multi_adv_enable_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_enable_p(int argc, const char **argv)
+{
+	int client_if;
+	int min_interval, max_interval;
+	int adv_type;
+	int chnl_map;
+	int tx_power;
+	int timeout_s;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_MIN_INTERVAL(3, min_interval);
+	VERIFY_MAX_INTERVAL(4, max_interval);
+	VERIFY_ADV_TYPE(5, adv_type);
+	VERIFY_CHNL_MAP(6, chnl_map);
+	VERIFY_TX_POWER(7, tx_power);
+	VERIFY_TIMEOUT_S(8, timeout_s);
+
+	EXEC(if_gatt->client->multi_adv_enable, client_if, min_interval,
+			max_interval, adv_type, chnl_map, tx_power, timeout_s);
+}
+
+/* update multi advertiser */
+static void multi_adv_update_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_update_p(int argc, const char **argv)
+{
+	int client_if;
+	int min_interval, max_interval;
+	int adv_type;
+	int chnl_map;
+	int tx_power;
+	int timeout_s;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_MIN_INTERVAL(3, min_interval);
+	VERIFY_MAX_INTERVAL(4, max_interval);
+	VERIFY_ADV_TYPE(5, adv_type);
+	VERIFY_CHNL_MAP(6, chnl_map);
+	VERIFY_TX_POWER(7, tx_power);
+	VERIFY_TIMEOUT_S(8, timeout_s);
+
+	EXEC(if_gatt->client->multi_adv_update, client_if, min_interval,
+			max_interval, adv_type, chnl_map, tx_power, timeout_s);
+}
+
+/* set advertising data */
+static void multi_adv_set_inst_data_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_set_inst_data_p(int argc, const char **argv)
+{
+	int client_if;
+	bool set_scan_rsp;
+	bool include_name, include_txpower;
+	int appearance;
+	uint16_t manufacturer_len;
+	uint8_t manufacturer_data[100];
+	uint16_t service_data_len;
+	uint8_t service_data[100];
+	uint16_t service_uuid_len;
+	uint8_t service_uuid[100];
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	/* set scan response */
+	if (argc >= 4)
+		set_scan_rsp = strtol(argv[3], NULL, 0);
+	/* include name */
+	if (argc >= 5)
+		include_name = strtol(argv[4], NULL, 0);
+	/* include txpower */
+	if (argc >= 6)
+		include_txpower = strtol(argv[5], NULL, 0);
+
+	VERIFY_APPEARANCE(6, appearance);
+	GET_VERIFY_HEX_STRING(7, manufacturer_data, manufacturer_len);
+	GET_VERIFY_HEX_STRING(8, service_data, service_data_len);
+	GET_VERIFY_HEX_STRING(9, service_uuid, service_uuid_len);
+
+	EXEC(if_gatt->client->multi_adv_set_inst_data, client_if, set_scan_rsp,
+		include_name, include_txpower, appearance, manufacturer_len,
+		(char *) manufacturer_data, service_data_len,
+		(char *) service_data, service_uuid_len, (char *) service_uuid);
+}
+
+/* multi advertising disable */
+static void multi_adv_disable_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void multi_adv_disable_p(int argc, const char **argv)
+{
+	int client_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	EXEC(if_gatt->client->multi_adv_disable, client_if);
+}
+
+/* batch scanconfigure storage */
+static void batchscan_cfg_storage_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_cfg_storage_p(int argc, const char **argv)
+{
+	int client_if;
+	int batch_scan_full_max;
+	int batch_scan_trunc_max;
+	int batch_scan_notify_threshold;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_BATCH_SCAN_FULL_MAX(3, batch_scan_full_max);
+	VERIFY_BATCH_SCAN_TRUNC_MAX(4, batch_scan_trunc_max);
+	VERIFY_BATCH_SCAN_NOTIFY_THR(5, batch_scan_notify_threshold);
+
+	EXEC(if_gatt->client->batchscan_cfg_storage, client_if,
+				batch_scan_full_max, batch_scan_trunc_max,
+				batch_scan_notify_threshold);
+}
+
+/* enable batch scan */
+static void batchscan_enb_batch_scan_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_enb_batch_scan_p(int argc, const char **argv)
+{
+	int client_if;
+	int scan_mode, scan_interval, scan_window;
+	int addr_type;
+	int discard_rule;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_SCAN_MODE(3, scan_mode);
+	VERIFY_SCAN_INTERVAL(4, scan_interval);
+	VERIFY_SCAN_WINDOW(5, scan_window);
+	VERIFY_ADDR_TYPE(6, addr_type);
+	VERIFY_DISCARD_RULE(7, discard_rule);
+
+	EXEC(if_gatt->client->batchscan_enb_batch_scan, client_if, scan_mode,
+			scan_interval, scan_window, addr_type, discard_rule);
+}
+
+/* batchscan disable */
+static void batchscan_dis_batch_scan_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_dis_batch_scan_p(int argc, const char **argv)
+{
+	int client_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+
+	EXEC(if_gatt->client->batchscan_dis_batch_scan, client_if);
+}
+
+/* batchscan read reports */
+static void batchscan_read_reports_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 2) {
+		*user = client_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void batchscan_read_reports_p(int argc, const char **argv)
+{
+	int client_if;
+	int scan_mode;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_CLIENT_IF(2, client_if);
+	VERIFY_SCAN_MODE(3, scan_mode);
+
+	EXEC(if_gatt->client->batchscan_read_reports, client_if, scan_mode);
+}
+#endif
+
+/* get_device_type */
+
+static void get_device_type_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3)
+		*enum_func = enum_devices;
+}
+
+static void get_device_type_p(int argc, const char **argv)
+{
+	bt_bdaddr_t bd_addr;
+	int dev_type;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_ADDR_ARG(2, &bd_addr);
+
+	dev_type = if_gatt->client->get_device_type(&bd_addr);
+	haltest_info("%s: %d\n", "get_device_type", dev_type);
+}
+
+/* test_command */
+
+static void test_command_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 4)
+		*enum_func = enum_devices;
+}
+
+static void test_command_p(int argc, const char **argv)
+{
+	int command;
+	int i;
+	bt_bdaddr_t bd_addr;
+	bt_uuid_t uuid;
+	btgatt_test_params_t params = {
+		.bda1 = &bd_addr,
+		.uuid1 = &uuid
+	};
+	uint16_t *u = &params.u1;
+
+	RETURN_IF_NULL(if_gatt);
+
+	/* command */
+	if (argc <= 2) {
+		haltest_error("No command specified\n");
+		return;
+	}
+	command = strtol(argv[2], NULL, 0);
+
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_UUID(4, &uuid);
+
+	for (i = 5; i < argc; i++)
+		VERIFY_TEST_ARG(i, *u++);
+
+	EXEC(if_gatt->client->test_command, command, &params);
+}
+
+static struct method client_methods[] = {
+	STD_METHODH(register_client, "[<uuid>]"),
+	STD_METHODCH(unregister_client, "<client_if>"),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	STD_METHODCH(scan, "[1|0]"),
+	STD_METHODCH(connect, "<client_if> <addr> [<is_direct>] [<transport]"),
+	STD_METHODCH(scan_filter_param_setup, "<client_if> <action>"
+			" <filt_index> <feat_seln> <list_logic_type>"
+			" <filt_logic_type> <rssi_high_thres> <rssi_low_thres>"
+			" <dely_mode> <found_timeout> <lost_timeout>"
+			" <found_timeout_cnt>"),
+	STD_METHODCH(scan_filter_add_remove, "<client_if> <action> <filt_type>"
+			" <filt_index> <company_id> <company_id_mask>"
+			" [<p_uuid>] <p_uuid_mask> <addr> <addr_type>"
+			" [<p_data>] [<p_mask>]"),
+	STD_METHODCH(scan_filter_clear, "<client_if> <filt_index>"),
+	STD_METHODCH(scan_filter_enable, "<client_if> [<enable>]"),
+	STD_METHODCH(set_adv_data, "<client_if> [<set_scan_rsp>] <include_name>"
+			" [<include_txpower>] <min_interval> <max_interval>"
+			" <appearance> [<manufacturer_data>] [<service_data>]"
+			" [<service_uuid>]"),
+	STD_METHODCH(configure_mtu, "<conn_id> <mtu>"),
+	STD_METHODCH(conn_parameter_update, "<bd_addr> <min_interval>"
+					" <max_interval> <latency> <timeout>"),
+	STD_METHODH(set_scan_parameters, "<scan_inverval> <scan_window>"),
+	STD_METHODCH(multi_adv_enable, "<client_if> <min_interval>"
+			" <max_interval> <adv_type> <chnl_map> <tx_power>"
+			" <timeout_s>"),
+	STD_METHODCH(multi_adv_update, "<client_if> <min_interval>"
+			" <max_interval> <adv_type> <chnl_map> <tx_power>"
+			" <timeout_s>"),
+	STD_METHODCH(multi_adv_set_inst_data, "<client_if> [<set_scan_rsp>]"
+			" <include_name> [<include_txpower>] <appearance>"
+			" [<manufacturer_data>] [<service_data>]"
+			" [<service_uuid>]"),
+	STD_METHODCH(multi_adv_disable, "<client_if>"),
+	STD_METHODCH(batchscan_cfg_storage, "<client_if> <batch_scan_full_max>"
+					" <batch_scan_trunc_max>"
+					" <batch_scan_notify_threshold>"),
+	STD_METHODCH(batchscan_enb_batch_scan, "<client_if> <scan_mode>"
+			" <scan_interval> <scan_window> <addr_type>"
+			" <discard_rule>"),
+	STD_METHODCH(batchscan_dis_batch_scan, "<client_if>"),
+	STD_METHODCH(batchscan_read_reports, "<client_if> <scan_mode>"),
+#else
+	STD_METHODCH(scan, "<client_if> [1|0]"),
+	STD_METHODCH(connect, "<client_if> <addr> [<is_direct>]"),
+#endif
+	STD_METHODCH(disconnect, "<client_if> <addr> <conn_id>"),
+	STD_METHODCH(refresh, "<client_if> <addr>"),
+	STD_METHODCH(search_service, "<conn_id> [<uuid>]"),
+	STD_METHODCH(get_included_service, "<conn_id> <srvc_id>"),
+	STD_METHODCH(get_characteristic, "<conn_id> <srvc_id>"),
+	STD_METHODCH(get_descriptor, "<conn_id> <srvc_id> <char_id>"),
+	STD_METHODCH(read_characteristic,
+			"<conn_id> <srvc_id> <char_id> [<auth_req>]"),
+	STD_METHODCH(write_characteristic,
+			"<conn_id> <srvc_id> <char_id> <write_type> <hex_value> [<auth_req>]"),
+	STD_METHODCH(read_descriptor,
+			"<conn_id> <srvc_id> <char_id> <descr_id> [<auth_req>]"),
+	STD_METHODCH(write_descriptor,
+			"<conn_id> <srvc_id> <char_id> <descr_id> <write_type> <hex_value> [<auth_req>]"),
+	STD_METHODCH(execute_write, "<conn_id> <execute>"),
+	STD_METHODCH(register_for_notification,
+			"<client_if> <addr> <srvc_id> <char_id>"),
+	STD_METHODCH(deregister_for_notification,
+			"<client_if> <addr> <srvc_id> <char_id>"),
+	STD_METHODCH(read_remote_rssi, "<client_if> <addr>"),
+	STD_METHODCH(get_device_type, "<addr>"),
+	STD_METHODCH(test_command,
+			"<cmd> <addr> <uuid> [u1] [u2] [u3] [u4] [u5]"),
+	STD_METHODCH(listen, "<client_if> [1|0]"),
+	END_METHOD
+};
+
+const struct interface gatt_client_if = {
+	.name = "gattc",
+	.methods = client_methods
+};
+
+/* gatt server methods */
+
+/* register_server */
+
+static void gatts_register_server_p(int argc, const char *argv[])
+{
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+
+	/* uuid */
+	if (argc <= 2)
+		gatt_str2bt_uuid_t("bed4babe", -1, &uuid);
+	else
+		gatt_str2bt_uuid_t(argv[2], -1, &uuid);
+
+	EXEC(if_gatt->server->register_server, &uuid);
+}
+
+/* unregister_server */
+
+static void gatts_unregister_server_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = server_if_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void gatts_unregister_server_p(int argc, const char *argv[])
+{
+	int server_if;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+
+	EXEC(if_gatt->server->unregister_server, server_if);
+}
+
+/* connect */
+
+static void gatts_connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = server_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void gatts_connect_p(int argc, const char *argv[])
+{
+	int server_if;
+	bt_bdaddr_t bd_addr;
+	int is_direct = 1;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int transport = 1;
+#endif
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	/* is_direct */
+	if (argc > 4)
+		is_direct = strtol(argv[4], NULL, 0);
+
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct);
+#else
+	/* transport */
+	if (argc > 5)
+		transport = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct,
+								transport);
+#endif
+}
+
+/* disconnect */
+
+static void gatts_disconnect_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = server_if_str;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 5) {
+		*user = conn_id_str;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void gatts_disconnect_p(int argc, const char *argv[])
+{
+	int server_if;
+	bt_bdaddr_t bd_addr;
+	int conn_id;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_ADDR_ARG(3, &bd_addr);
+	VERIFY_CONN_ID(4, conn_id);
+
+	EXEC(if_gatt->server->disconnect, server_if, &bd_addr, conn_id);
+}
+
+/* add_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_service_c gatts_unregister_server_c
+
+static void gatts_add_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	btgatt_srvc_id_t srvc_id;
+	int num_handles;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SRVC_ID(3, &srvc_id);
+
+	/* num handles */
+	if (argc <= 4) {
+		haltest_error("No num_handles specified\n");
+		return;
+	}
+	num_handles = strtol(argv[4], NULL, 0);
+
+	EXEC(if_gatt->server->add_service, server_if, &srvc_id, num_handles);
+}
+
+/* add_included_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_included_service_c gatts_unregister_server_c
+
+static void gatts_add_included_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int included_handle;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+	VERIFY_HANDLE(4, included_handle);
+
+	EXEC(if_gatt->server->add_included_service, server_if, service_handle,
+							included_handle);
+}
+
+/* add_characteristic */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_characteristic_c gatts_unregister_server_c
+
+static void gatts_add_characteristic_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int properties;
+	int permissions;
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+	VERIFY_UUID(4, &uuid);
+
+	/* properties */
+	if (argc <= 5) {
+		haltest_error("No properties specified\n");
+		return;
+	}
+	properties = strtol(argv[5], NULL, 0);
+
+	/* permissions */
+	if (argc <= 6) {
+		haltest_error("No permissions specified\n");
+		return;
+	}
+	permissions = strtol(argv[6], NULL, 0);
+
+	EXEC(if_gatt->server->add_characteristic, server_if, service_handle,
+						&uuid, properties, permissions);
+}
+
+/* add_descriptor */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_add_descriptor_c gatts_unregister_server_c
+
+static void gatts_add_descriptor_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int permissions;
+	bt_uuid_t uuid;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+	VERIFY_UUID(4, &uuid);
+
+	/* permissions */
+	if (argc <= 5) {
+		haltest_error("No permissions specified\n");
+		return;
+	}
+	permissions = strtol(argv[5], NULL, 0);
+
+	EXEC(if_gatt->server->add_descriptor, server_if, service_handle, &uuid,
+								permissions);
+}
+
+/* start_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_start_service_c gatts_unregister_server_c
+
+static void gatts_start_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+	int transport;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+
+	/* transport */
+	if (argc <= 4) {
+		haltest_error("No transport specified\n");
+		return;
+	}
+	transport = strtol(argv[4], NULL, 0);
+
+	EXEC(if_gatt->server->start_service, server_if, service_handle,
+								transport);
+}
+
+/* stop_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_stop_service_c gatts_unregister_server_c
+
+static void gatts_stop_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+
+	EXEC(if_gatt->server->stop_service, server_if, service_handle);
+}
+
+/* delete_service */
+
+/* Same completion as gatts_unregister_server_c */
+#define gatts_delete_service_c gatts_unregister_server_c
+
+static void gatts_delete_service_p(int argc, const char *argv[])
+{
+	int server_if;
+	int service_handle;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_SERVICE_HANDLE(3, service_handle);
+
+	EXEC(if_gatt->server->delete_service, server_if, service_handle);
+}
+
+/* send_indication */
+
+static void gatts_send_indication_p(int argc, const char *argv[])
+{
+	int server_if;
+	int attr_handle;
+	int conn_id;
+	int confirm;
+	char data[200];
+	int len = 0;
+
+	RETURN_IF_NULL(if_gatt);
+	VERIFY_SERVER_IF(2, server_if);
+	VERIFY_HANDLE(3, attr_handle);
+	VERIFY_CONN_ID(4, conn_id);
+
+	/* confirm */
+	if (argc <= 5) {
+		haltest_error("No transport specified\n");
+		return;
+	}
+	confirm = strtol(argv[5], NULL, 0);
+
+	GET_VERIFY_HEX_STRING(6, data, len);
+
+	EXEC(if_gatt->server->send_indication, server_if, attr_handle, conn_id,
+							len, confirm, data);
+}
+
+/* send_response */
+
+static void gatts_send_response_p(int argc, const char *argv[])
+{
+	int conn_id;
+	int trans_id;
+	int status;
+	btgatt_response_t data;
+
+	memset(&data, 0, sizeof(data));
+
+	RETURN_IF_NULL(if_gatt);
+
+	VERIFY_CONN_ID(2, conn_id);
+	VERIFY_TRANS_ID(3, trans_id);
+	VERIFY_STATUS(4, status);
+	VERIFY_HANDLE(5, data.attr_value.handle);
+	VERIFY_OFFSET(6, data.attr_value.offset);
+
+	data.attr_value.auth_req = 0;
+	data.attr_value.len = 0;
+
+	GET_VERIFY_HEX_STRING(7, data.attr_value.value, data.attr_value.len);
+
+	haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id,
+									status);
+
+	EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data);
+}
+
+#define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h)
+#define GATTS_METHODCH(n, h) METHOD(#n, gatts_##n##_p, gatts_##n##_c, h)
+
+static struct method server_methods[] = {
+	GATTS_METHODH(register_server, "[<uuid>]"),
+	GATTS_METHODCH(unregister_server, "<server_if>"),
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>] [<transport>]"),
+#else
+	GATTS_METHODCH(connect, "<server_if> <addr> [<is_direct>]"),
+#endif
+	GATTS_METHODCH(disconnect, "<server_if> <addr> <conn_id>"),
+	GATTS_METHODCH(add_service, "<server_if> <srvc_id> <num_handles>"),
+	GATTS_METHODCH(add_included_service,
+			"<server_if> <service_handle> <included_handle>"),
+	GATTS_METHODCH(add_characteristic,
+		"<server_if> <service_handle> <uuid> <properites> <permissions>"),
+	GATTS_METHODCH(add_descriptor,
+			"<server_if> <service_handle> <uuid> <permissions>"),
+	GATTS_METHODCH(start_service,
+				"<server_if> <service_handle> <transport>"),
+	GATTS_METHODCH(stop_service, "<server_if> <service_handle>"),
+	GATTS_METHODCH(delete_service, "<server_if> <service_handle>"),
+	GATTS_METHODH(send_indication,
+			"<server_if> <attr_handle> <conn_id> <confirm> [<data>]"),
+	GATTS_METHODH(send_response,
+		"<conn_id> <trans_id> <status> <handle> <offset> [<data>]"),
+	END_METHOD
+};
+
+const struct interface gatt_server_if = {
+	.name = "gatts",
+	.methods = server_methods
+};
diff --git a/bluez/repo/android/client/if-hf-client.c b/bluez/repo/android/client/if-hf-client.c
new file mode 100644
index 0000000..3f6f7c2
--- /dev/null
+++ b/bluez/repo/android/client/if-hf-client.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const bthf_client_interface_t *if_hf_client = NULL;
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+SINTMAP(bthf_client_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED),
+	DELEMENT(BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_client_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_DISCONNECTED),
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTING),
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED),
+	DELEMENT(BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC),
+ENDMAP
+
+SINTMAP(bthf_client_vr_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_VR_STATE_STOPPED),
+	DELEMENT(BTHF_CLIENT_VR_STATE_STARTED),
+ENDMAP
+
+SINTMAP(bthf_client_network_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE),
+	DELEMENT(BTHF_CLIENT_NETWORK_STATE_AVAILABLE),
+ENDMAP
+
+SINTMAP(bthf_client_service_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_SERVICE_TYPE_HOME),
+	DELEMENT(BTHF_CLIENT_SERVICE_TYPE_ROAMING),
+ENDMAP
+
+SINTMAP(bthf_client_call_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS),
+	DELEMENT(BTHF_CLIENT_CALL_CALLS_IN_PROGRESS),
+ENDMAP
+
+SINTMAP(bthf_client_callsetup_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALLSETUP_NONE),
+	DELEMENT(BTHF_CLIENT_CALLSETUP_INCOMING),
+	DELEMENT(BTHF_CLIENT_CALLSETUP_OUTGOING),
+	DELEMENT(BTHF_CLIENT_CALLSETUP_ALERTING),
+ENDMAP
+
+SINTMAP(bthf_client_callheld_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALLHELD_NONE),
+	DELEMENT(BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE),
+	DELEMENT(BTHF_CLIENT_CALLHELD_HOLD),
+ENDMAP
+
+SINTMAP(bthf_client_resp_and_hold_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_RESP_AND_HOLD_HELD),
+	DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_ACCEPT),
+	DELEMENT(BTRH_CLIENT_RESP_AND_HOLD_REJECT),
+ENDMAP
+
+SINTMAP(bthf_client_call_direction_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_DIRECTION_OUTGOING),
+	DELEMENT(BTHF_CLIENT_CALL_DIRECTION_INCOMING),
+ENDMAP
+
+SINTMAP(bthf_client_call_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_STATE_ACTIVE),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_HELD),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_DIALING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_ALERTING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_INCOMING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_WAITING),
+	DELEMENT(BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD),
+ENDMAP
+
+SINTMAP(bthf_client_call_mpty_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE),
+	DELEMENT(BTHF_CLIENT_CALL_MPTY_TYPE_MULTI),
+ENDMAP
+
+SINTMAP(bthf_client_volume_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_VOLUME_TYPE_SPK),
+	DELEMENT(BTHF_CLIENT_VOLUME_TYPE_MIC),
+ENDMAP
+
+SINTMAP(bthf_client_cmd_complete_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_OK),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED),
+	DELEMENT(BTHF_CLIENT_CMD_COMPLETE_ERROR_CME),
+ENDMAP
+
+SINTMAP(bthf_client_subscriber_service_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_SERVICE_UNKNOWN),
+	DELEMENT(BTHF_CLIENT_SERVICE_VOICE),
+	DELEMENT(BTHF_CLIENT_SERVICE_FAX),
+ENDMAP
+
+SINTMAP(bthf_client_in_band_ring_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED),
+	DELEMENT(BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED),
+ENDMAP
+
+SINTMAP(bthf_client_call_action_t, -1, "(unknown)")
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_0),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_3),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_4),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_1x),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHLD_2x),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_ATA),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_CHUP),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_0),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_1),
+	DELEMENT(BTHF_CLIENT_CALL_ACTION_BTRH_2),
+ENDMAP
+
+/* Callbacks */
+
+static char features_str[512];
+
+static const char *pear_features_t2str(int feat)
+{
+	memset(features_str, 0, sizeof(features_str));
+
+	sprintf(features_str, "BTHF_CLIENT_PEER_FEAT_3WAY: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_ECNR: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_VREC: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_INBAND: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_VTAG: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_REJECT: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_ECS: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_ECC: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_EXTERR: %s,\n"
+			"BTHF_CLIENT_PEER_FEAT_CODEC: %s,\n",
+			feat & BTHF_CLIENT_PEER_FEAT_3WAY ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_ECNR ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_VREC ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_INBAND ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_VTAG ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_REJECT ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_ECS ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_ECC ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_EXTERR ? "True" : "False",
+			feat & BTHF_CLIENT_PEER_FEAT_CODEC ? "True" : "False");
+
+	return features_str;
+}
+
+static const char *chld_features_t2str(int feat)
+{
+	memset(features_str, 0, sizeof(features_str));
+
+	sprintf(features_str,
+		"BTHF_CLIENT_CHLD_FEAT_REL: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_REL_ACC: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_REL_X: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_HOLD_ACC: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_PRIV_X: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_MERGE: %s,\n"
+		"BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH: %s,\n",
+		feat & BTHF_CLIENT_CHLD_FEAT_REL ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_REL_ACC ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_REL_X ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_HOLD_ACC ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_PRIV_X ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_MERGE ? "True" : "False",
+		feat & BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH ? "True" : "False");
+
+	return features_str;
+}
+
+/* Callback for connection state change. */
+static void hf_client_connection_state_callback(
+					bthf_client_connection_state_t state,
+					unsigned int peer_feat,
+					unsigned int chld_feat,
+					bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+				bthf_client_connection_state_t2str(state),
+				bt_bdaddr_t2str(bd_addr, last_addr));
+
+	if (state != BTHF_CLIENT_CONNECTION_STATE_CONNECTED)
+		return;
+
+	haltest_info("\tpeer_features%s\n", pear_features_t2str(peer_feat));
+	haltest_info("\tchld_feat=%s\n", chld_features_t2str(chld_feat));
+}
+
+/* Callback for audio connection state change. */
+static void hf_client_audio_state_callback(bthf_client_audio_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+				bthf_client_audio_state_t2str(state),
+				bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/* Callback for VR connection state change. */
+static void hf_client_vr_cmd_callback(bthf_client_vr_state_t state)
+{
+	haltest_info("%s: vr_state=%s\n", __func__,
+					bthf_client_vr_state_t2str(state));
+}
+
+/* Callback for network state change */
+static void hf_client_network_state_callback(bthf_client_network_state_t state)
+{
+	haltest_info("%s: network_state=%s\n", __func__,
+					bthf_client_network_state_t2str(state));
+}
+
+/* Callback for network roaming status change */
+static void hf_client_network_roaming_callback(bthf_client_service_type_t type)
+{
+	haltest_info("%s: service_type=%s\n", __func__,
+					bthf_client_service_type_t2str(type));
+}
+
+/* Callback for signal strength indication */
+static void hf_client_network_signal_callback(int signal_strength)
+{
+	haltest_info("%s: signal strength=%d\n", __func__, signal_strength);
+}
+
+/* Callback for battery level indication */
+static void hf_client_battery_level_callback(int battery_level)
+{
+	haltest_info("%s: battery_lvl=%d\n", __func__, battery_level);
+}
+
+/* Callback for current operator name */
+static void hf_client_current_operator_callback(const char *name)
+{
+	haltest_info("%s: operator_name=%s\n", __func__, name);
+}
+
+/* Callback for call indicator */
+static void hf_client_call_callback(bthf_client_call_t call)
+{
+	haltest_info("%s: call_state=%s\n", __func__,
+						bthf_client_call_t2str(call));
+}
+
+/* Callback for callsetup indicator */
+static void hf_client_callsetup_callback(bthf_client_callsetup_t callsetup)
+{
+	haltest_info("%s: callsetup=%s\n", __func__,
+					bthf_client_callsetup_t2str(callsetup));
+}
+
+/* Callback for callheld indicator */
+static void hf_client_callheld_callback(bthf_client_callheld_t callheld)
+{
+	haltest_info("%s: callheld=%s\n", __func__,
+					bthf_client_callheld_t2str(callheld));
+}
+
+/* Callback for response and hold */
+static void hf_client_resp_and_hold_callback(
+				bthf_client_resp_and_hold_t resp_and_hold)
+{
+	haltest_info("%s: resp_and_hold=%s\n", __func__,
+				bthf_client_resp_and_hold_t2str(resp_and_hold));
+}
+
+/* Callback for Calling Line Identification notification */
+static void hf_client_clip_callback(const char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for Call Waiting notification */
+static void hf_client_call_waiting_callback(const char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for listing current calls. Can be called multiple time. */
+static void hf_client_current_calls_callback(int index,
+					bthf_client_call_direction_t dir,
+					bthf_client_call_state_t state,
+					bthf_client_call_mpty_type_t mpty,
+					const char *number)
+{
+	haltest_info("%s: index=%d, direction=%s, state=%s, m_party=%s\n",
+					__func__, index,
+					bthf_client_call_direction_t2str(dir),
+					bthf_client_call_state_t2str(state),
+					bthf_client_call_mpty_type_t2str(mpty));
+
+	if (number)
+		haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for audio volume change */
+static void hf_client_volume_change_callback(bthf_client_volume_type_t type,
+								int volume)
+{
+	haltest_info("%s: vol_type=%s, value=%d\n", __func__,
+				bthf_client_volume_type_t2str(type), volume);
+}
+
+/* Callback for command complete event */
+static void hf_client_cmd_complete_callback(bthf_client_cmd_complete_t type,
+									int cme)
+{
+	haltest_info("%s: type=%s, cme=%d\n", __func__,
+				bthf_client_cmd_complete_t2str(type), cme);
+}
+
+/* Callback for subscriber information */
+static void hf_client_subscriber_info_callback(const char *name,
+				bthf_client_subscriber_service_type_t type)
+{
+	haltest_info("%s: name=%s, type=%s\n", __func__, name,
+			bthf_client_subscriber_service_type_t2str(type));
+}
+
+/* Callback for in-band ring tone settings */
+static void hf_client_in_band_ring_tone_callback(
+				bthf_client_in_band_ring_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__,
+				bthf_client_in_band_ring_state_t2str(state));
+}
+
+/* Callback for requested number from AG */
+static void hf_client_last_voice_tag_number_callback(const char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+
+/* Callback for sending ring indication to app */
+static void hf_client_ring_indication_callback(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static bthf_client_callbacks_t hf_client_cbacks = {
+	.size = sizeof(hf_client_cbacks),
+	.connection_state_cb = hf_client_connection_state_callback,
+	.audio_state_cb = hf_client_audio_state_callback,
+	.vr_cmd_cb = hf_client_vr_cmd_callback,
+	.network_state_cb = hf_client_network_state_callback,
+	.network_roaming_cb = hf_client_network_roaming_callback,
+	.network_signal_cb = hf_client_network_signal_callback,
+	.battery_level_cb = hf_client_battery_level_callback,
+	.current_operator_cb = hf_client_current_operator_callback,
+	.call_cb = hf_client_call_callback,
+	.callsetup_cb = hf_client_callsetup_callback,
+	.callheld_cb = hf_client_callheld_callback,
+	.resp_and_hold_cb = hf_client_resp_and_hold_callback,
+	.clip_cb = hf_client_clip_callback,
+	.call_waiting_cb = hf_client_call_waiting_callback,
+	.current_calls_cb = hf_client_current_calls_callback,
+	.volume_change_cb = hf_client_volume_change_callback,
+	.cmd_complete_cb = hf_client_cmd_complete_callback,
+	.subscriber_info_cb = hf_client_subscriber_info_callback,
+	.in_band_ring_tone_cb = hf_client_in_band_ring_tone_callback,
+	.last_voice_tag_number_callback =
+				hf_client_last_voice_tag_number_callback,
+	.ring_indication_cb = hf_client_ring_indication_callback,
+};
+
+/* init */
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->init, &hf_client_cbacks);
+}
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* connect to audio gateway */
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->connect, &addr);
+}
+
+/*
+ * This completion function will be used for several methods
+ * returning recently connected address
+ */
+static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_c connected_addr_c
+
+/* disconnect from audio gateway */
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->disconnect, &addr);
+}
+
+static void connect_audio_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* create an audio connection */
+static void connect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->connect_audio, &addr);
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_audio_c connected_addr_c
+
+/* close the audio connection */
+static void disconnect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf_client);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf_client->disconnect_audio, &addr);
+}
+
+/* start voice recognition */
+static void start_voice_recognition_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->start_voice_recognition);
+}
+
+/* stop voice recognition */
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->stop_voice_recognition);
+}
+
+static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_client_volume_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+/* volume control */
+static void volume_control_p(int argc, const char **argv)
+{
+	bthf_client_volume_type_t type;
+	int volume;
+
+	RETURN_IF_NULL(if_hf_client);
+
+	/* volume type */
+	if (argc <= 2) {
+		haltest_error("No volume type specified\n");
+		return;
+	}
+	type = str2bthf_client_volume_type_t(argv[2]);
+
+	/* volume */
+	if (argc <= 3) {
+		haltest_error("No volume specified\n");
+		return;
+	}
+	volume = atoi(argv[3]);
+
+	EXEC(if_hf_client->volume_control, type, volume);
+}
+
+/* place a call with number a number */
+static void dial_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	/* number string */
+	if (argc <= 2) {
+		haltest_info("Number not specified. Redial\n");
+		EXEC(if_hf_client->dial, NULL);
+		return;
+	}
+
+	EXEC(if_hf_client->dial, argv[2]);
+}
+
+/* place a call with number specified by location (speed dial) */
+static void dial_memory_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	/* memory index */
+	if (argc <= 2) {
+		haltest_error("No memory index specified\n");
+		return;
+	}
+
+	EXEC(if_hf_client->dial_memory, atoi(argv[2]));
+}
+
+static void handle_call_action_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_client_call_action_t);
+		*enum_func = enum_defines;
+	}
+}
+
+/* perform specified call related action */
+static void handle_call_action_p(int argc, const char **argv)
+{
+	bthf_client_call_action_t action;
+	int index = 0;
+
+	RETURN_IF_NULL(if_hf_client);
+
+	/* action */
+	if (argc <= 2) {
+		haltest_error("No action specified\n");
+		return;
+	}
+	action = str2bthf_client_call_action_t(argv[2]);
+
+	/* call index */
+	if (action == BTHF_CLIENT_CALL_ACTION_CHLD_1x ||
+				action == BTHF_CLIENT_CALL_ACTION_CHLD_2x) {
+		if (argc <= 3) {
+			haltest_error("No call index specified\n");
+			return;
+		}
+		index = atoi(argv[3]);
+	}
+
+	EXEC(if_hf_client->handle_call_action, action, index);
+}
+
+/* query list of current calls */
+static void query_current_calls_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->query_current_calls);
+}
+
+/* query name of current selected operator */
+static void query_current_operator_name_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->query_current_operator_name);
+}
+
+/* Retrieve subscriber information */
+static void retrieve_subscriber_info_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->retrieve_subscriber_info);
+}
+
+/* Send DTMF code*/
+static void send_dtmf_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->send_dtmf, *argv[2]);
+}
+
+/* Request a phone number from AG corresponding to last voice tag recorded */
+static void request_last_voice_tag_number_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXEC(if_hf_client->request_last_voice_tag_number);
+}
+
+/* Closes the interface. */
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf_client);
+
+	EXECV(if_hf_client->cleanup);
+	if_hf_client = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHODCH(connect_audio, "<addr>"),
+	STD_METHODCH(disconnect_audio, "<addr>"),
+	STD_METHOD(start_voice_recognition),
+	STD_METHOD(stop_voice_recognition),
+	STD_METHODCH(volume_control, "<volume_type> <value>"),
+	STD_METHODH(dial, "<destination_number>"),
+	STD_METHODH(dial_memory, "<memory_location>"),
+	STD_METHODCH(handle_call_action, "<call_action> <call_index>"),
+	STD_METHOD(query_current_calls),
+	STD_METHOD(query_current_operator_name),
+	STD_METHOD(retrieve_subscriber_info),
+	STD_METHODH(send_dtmf, "<code>"),
+	STD_METHOD(request_last_voice_tag_number),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hf_client_if = {
+	.name = "handsfree_client",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-hf.c b/bluez/repo/android/client/if-hf.c
new file mode 100644
index 0000000..6b798f8
--- /dev/null
+++ b/bluez/repo/android/client/if-hf.c
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const bthf_interface_t *if_hf = NULL;
+
+SINTMAP(bthf_at_response_t, -1, "(unknown)")
+	DELEMENT(BTHF_AT_RESPONSE_ERROR),
+	DELEMENT(BTHF_AT_RESPONSE_OK),
+ENDMAP
+
+SINTMAP(bthf_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED),
+	DELEMENT(BTHF_CONNECTION_STATE_CONNECTING),
+	DELEMENT(BTHF_CONNECTION_STATE_CONNECTED),
+	DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED),
+	DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_audio_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED),
+	DELEMENT(BTHF_AUDIO_STATE_CONNECTING),
+	DELEMENT(BTHF_AUDIO_STATE_CONNECTED),
+	DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(bthf_vr_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_VR_STATE_STOPPED),
+	DELEMENT(BTHF_VR_STATE_STARTED),
+ENDMAP
+
+SINTMAP(bthf_volume_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_VOLUME_TYPE_SPK),
+	DELEMENT(BTHF_VOLUME_TYPE_MIC),
+ENDMAP
+
+SINTMAP(bthf_nrec_t, -1, "(unknown)")
+	DELEMENT(BTHF_NREC_STOP),
+	DELEMENT(BTHF_NREC_START),
+ENDMAP
+
+SINTMAP(bthf_chld_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD),
+	DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD),
+	DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD),
+	DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF),
+ENDMAP
+
+/* Network Status */
+SINTMAP(bthf_network_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE),
+	DELEMENT(BTHF_NETWORK_STATE_AVAILABLE),
+ENDMAP
+
+/* Service type */
+SINTMAP(bthf_service_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_SERVICE_TYPE_HOME),
+	DELEMENT(BTHF_SERVICE_TYPE_ROAMING),
+ENDMAP
+
+SINTMAP(bthf_call_state_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_STATE_ACTIVE),
+	DELEMENT(BTHF_CALL_STATE_HELD),
+	DELEMENT(BTHF_CALL_STATE_DIALING),
+	DELEMENT(BTHF_CALL_STATE_ALERTING),
+	DELEMENT(BTHF_CALL_STATE_INCOMING),
+	DELEMENT(BTHF_CALL_STATE_WAITING),
+	DELEMENT(BTHF_CALL_STATE_IDLE),
+ENDMAP
+
+SINTMAP(bthf_call_direction_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_DIRECTION_OUTGOING),
+	DELEMENT(BTHF_CALL_DIRECTION_INCOMING),
+ENDMAP
+
+SINTMAP(bthf_call_mode_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_TYPE_VOICE),
+	DELEMENT(BTHF_CALL_TYPE_DATA),
+	DELEMENT(BTHF_CALL_TYPE_FAX),
+ENDMAP
+
+SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE),
+	DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI),
+ENDMAP
+
+SINTMAP(bthf_call_addrtype_t, -1, "(unknown)")
+	DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN),
+	DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+SINTMAP(bthf_wbs_config_t, -1, "(unknown)")
+	DELEMENT(BTHF_WBS_NONE),
+	DELEMENT(BTHF_WBS_NO),
+	DELEMENT(BTHF_WBS_YES),
+ENDMAP
+#endif
+
+/* Callbacks */
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+/*
+ * Callback for connection state change.
+ * state will have one of the values from BtHfConnectionState
+ */
+static void connection_state_cb(bthf_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					bthf_connection_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/*
+ * Callback for audio connection state change.
+ * state will have one of the values from BtHfAudioState
+ */
+static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					bthf_audio_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+/*
+ * Callback for VR connection state change.
+ * state will have one of the values from BtHfVRState
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void vr_cmd_cb(bthf_vr_state_t state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					bthf_vr_state_t2str(state),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void vr_cmd_cb(bthf_vr_state_t state)
+{
+	haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state));
+}
+#endif
+
+/* Callback for answer incoming call (ATA) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void answer_call_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void answer_call_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for disconnect call (AT+CHUP) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void hangup_call_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void hangup_call_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/*
+ * Callback for disconnect call (AT+CHUP)
+ * type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void volume_cmd_cb(bthf_volume_type_t type, int volume,
+							bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: type=%s volume=%d bd_addr=%s\n", __func__,
+					bthf_volume_type_t2str(type), volume,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void volume_cmd_cb(bthf_volume_type_t type, int volume)
+{
+	haltest_info("%s: type=%s volume=%d\n", __func__,
+					bthf_volume_type_t2str(type), volume);
+}
+#endif
+
+/*
+ * Callback for dialing an outgoing call
+ * If number is NULL, redial
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void dial_call_cmd_cb(char *number, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: number=%s bd_addr=%s\n", __func__, number,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void dial_call_cmd_cb(char *number)
+{
+	haltest_info("%s: number=%s\n", __func__, number);
+}
+#endif
+
+/*
+ * Callback for sending DTMF tones
+ * tone contains the dtmf character to be sent
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void dtmf_cmd_cb(char tone, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: tone=%d bd_addr=%s\n", __func__, tone,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void dtmf_cmd_cb(char tone)
+{
+	haltest_info("%s: tone=%d\n", __func__, tone);
+}
+#endif
+
+/*
+ * Callback for enabling/disabling noise reduction/echo cancellation
+ * value will be 1 to enable, 0 to disable
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void nrec_cmd_cb(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: nrec=%s bd_addr=%s\n", __func__,
+					bthf_nrec_t2str(nrec),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void nrec_cmd_cb(bthf_nrec_t nrec)
+{
+	haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec));
+}
+#endif
+
+/*
+ * Callback for call hold handling (AT+CHLD)
+ * value will contain the call hold command (0, 1, 2, 3)
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void chld_cmd_cb(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: chld=%s bd_addr=%s\n", __func__,
+					bthf_chld_type_t2str(chld),
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void chld_cmd_cb(bthf_chld_type_t chld)
+{
+	haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld));
+}
+#endif
+
+/* Callback for CNUM (subscriber number) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cnum_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cnum_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for indicators (CIND) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cind_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cind_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for operator selection (COPS) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void cops_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void cops_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/* Callback for call list (AT+CLCC) */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void clcc_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void clcc_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+/*
+ * Callback for unknown AT command recd from HF
+ * at_string will contain the unparsed AT string
+ */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void unknown_at_cmd_cb(char *at_string, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: at_string=%s bd_addr=%s\n", __func__, at_string,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void unknown_at_cmd_cb(char *at_string)
+{
+	haltest_info("%s: at_string=%s\n", __func__, at_string);
+}
+#endif
+
+/* Callback for keypressed (HSP) event. */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void key_pressed_cmd_cb(bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#else
+static void key_pressed_cmd_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+#endif
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void wbs_cb(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: bd_addr=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+#endif
+
+static bthf_callbacks_t hf_cbacks = {
+	.size = sizeof(hf_cbacks),
+	.connection_state_cb = connection_state_cb,
+	.audio_state_cb = audio_state_cb,
+	.vr_cmd_cb = vr_cmd_cb,
+	.answer_call_cmd_cb = answer_call_cmd_cb,
+	.hangup_call_cmd_cb = hangup_call_cmd_cb,
+	.volume_cmd_cb = volume_cmd_cb,
+	.dial_call_cmd_cb = dial_call_cmd_cb,
+	.dtmf_cmd_cb = dtmf_cmd_cb,
+	.nrec_cmd_cb = nrec_cmd_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.wbs_cb = wbs_cb,
+#endif
+	.chld_cmd_cb = chld_cmd_cb,
+	.cnum_cmd_cb = cnum_cmd_cb,
+	.cind_cmd_cb = cind_cmd_cb,
+	.cops_cmd_cb = cops_cmd_cb,
+	.clcc_cmd_cb = clcc_cmd_cb,
+	.unknown_at_cmd_cb = unknown_at_cmd_cb,
+	.key_pressed_cmd_cb = key_pressed_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	int max_hf_clients;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (argc <= 2)
+		max_hf_clients = 1;
+	else
+		max_hf_clients = atoi(argv[2]);
+
+	EXEC(if_hf->init, &hf_cbacks, max_hf_clients);
+#else
+	EXEC(if_hf->init, &hf_cbacks);
+#endif
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->connect, &addr);
+}
+
+/* disconnect */
+
+/*
+ * This completion function will be used for several methods
+ * returning recently connected address
+ */
+static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+/* Map completion to connected_addr_c */
+#define disconnect_c connected_addr_c
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->disconnect, &addr);
+}
+
+/* create an audio connection */
+
+/* Map completion to connected_addr_c */
+#define connect_audio_c connected_addr_c
+
+static void connect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->connect_audio, &addr);
+}
+
+/* close the audio connection */
+
+/* Map completion to connected_addr_c */
+#define disconnect_audio_c connected_addr_c
+
+static void disconnect_audio_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->disconnect_audio, &addr);
+}
+
+/* start voice recognition */
+
+static void start_voice_recognition_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->start_voice_recognition, &addr);
+#else
+	EXEC(if_hf->start_voice_recognition);
+#endif
+}
+
+/* stop voice recognition */
+
+static void stop_voice_recognition_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hf->stop_voice_recognition, &addr);
+#else
+	EXEC(if_hf->stop_voice_recognition);
+#endif
+}
+
+/* volume control */
+
+static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_volume_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void volume_control_p(int argc, const char **argv)
+{
+	bthf_volume_type_t type;
+	int volume;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* volume type */
+	if (argc <= 2) {
+		haltest_error("No volume type specified\n");
+		return;
+	}
+	type = str2bthf_volume_type_t(argv[2]);
+
+	/* volume */
+	if (argc <= 3) {
+		haltest_error("No volume specified\n");
+		return;
+	}
+	volume = atoi(argv[3]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(4, &addr);
+
+	EXEC(if_hf->volume_control, type, volume, &addr);
+#else
+	EXEC(if_hf->volume_control, type, volume);
+#endif
+}
+
+/* Combined device status change notification */
+
+static void device_status_notification_c(int argc, const char **argv,
+							enum_func *enum_func,
+							void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_network_state_t);
+		*enum_func = enum_defines;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthf_service_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void device_status_notification_p(int argc, const char **argv)
+{
+	bthf_network_state_t ntk_state;
+	bthf_service_type_t svc_type;
+	int signal;
+	int batt_chg;
+
+	RETURN_IF_NULL(if_hf);
+
+	/* network state */
+	if (argc <= 2) {
+		haltest_error("No network state specified\n");
+		return;
+	}
+	ntk_state = str2bthf_network_state_t(argv[2]);
+
+	/* service type */
+	if (argc <= 3) {
+		haltest_error("No service type specified\n");
+		return;
+	}
+	svc_type = str2bthf_service_type_t(argv[3]);
+
+	/* signal */
+	if (argc <= 4) {
+		haltest_error("No signal specified\n");
+		return;
+	}
+	signal = atoi(argv[4]);
+
+	/* batt_chg */
+	if (argc <= 5) {
+		haltest_error("No batt_chg specified\n");
+		return;
+	}
+	batt_chg = atoi(argv[5]);
+
+	EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal,
+								batt_chg);
+}
+
+/* Response for COPS command */
+
+static void cops_response_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* response */
+	if (argc <= 2) {
+		haltest_error("No cops specified\n");
+		return;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(3, &addr);
+
+	EXEC(if_hf->cops_response, argv[2], &addr);
+#else
+	EXEC(if_hf->cops_response, argv[2]);
+#endif
+}
+
+/* Response for CIND command */
+
+static void cind_response_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 6) {
+		*user = TYPE_ENUM(bthf_call_state_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void cind_response_p(int argc, const char **argv)
+{
+	int svc;
+	int num_active;
+	int num_held;
+	bthf_call_state_t call_setup_state;
+	int signal;
+	int roam;
+	int batt_chg;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* svc */
+	if (argc <= 2) {
+		haltest_error("No service specified\n");
+		return;
+	}
+	svc = atoi(argv[2]);
+
+	/* num active */
+	if (argc <= 3) {
+		haltest_error("No num active specified\n");
+		return;
+	}
+	num_active = atoi(argv[3]);
+
+	/* num held */
+	if (argc <= 4) {
+		haltest_error("No num held specified\n");
+		return;
+	}
+	num_held = atoi(argv[4]);
+
+	/* call setup state */
+	if (argc <= 5) {
+		haltest_error("No call setup state specified\n");
+		return;
+	}
+	call_setup_state = str2bthf_call_state_t(argv[5]);
+
+	/* signal */
+	if (argc <= 6) {
+		haltest_error("No signal specified\n");
+		return;
+	}
+	signal = atoi(argv[6]);
+
+	/* roam */
+	if (argc <= 7) {
+		haltest_error("No roam specified\n");
+		return;
+	}
+	roam = atoi(argv[7]);
+
+	/* batt_chg */
+	if (argc <= 8) {
+		haltest_error("No batt_chg specified\n");
+		return;
+	}
+	batt_chg = atoi(argv[8]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(9, &addr);
+
+	EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+						signal, roam, batt_chg, &addr);
+#else
+	EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
+							signal, roam, batt_chg);
+#endif
+}
+
+/* Pre-formatted AT response, typically in response to unknown AT cmd */
+
+static void formatted_at_response_p(int argc, const char **argv)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* response */
+	if (argc <= 2) {
+		haltest_error("No response specified\n");
+		return;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(3, &addr);
+
+	EXEC(if_hf->formatted_at_response, argv[2], &addr);
+#else
+	EXEC(if_hf->formatted_at_response, argv[2]);
+#endif
+}
+
+/* at_response */
+
+static void at_response_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(bthf_at_response_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void at_response_p(int argc, const char **argv)
+{
+	bthf_at_response_t response_code;
+	int error_code;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* response type */
+	if (argc <= 2) {
+		haltest_error("No response specified\n");
+		return;
+	}
+	response_code = str2bthf_at_response_t(argv[2]);
+
+	/* error code */
+	if (argc <= 3)
+		error_code = 0;
+	else
+		error_code = atoi(argv[3]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(4, &addr);
+
+	EXEC(if_hf->at_response, response_code, error_code, &addr);
+#else
+	EXEC(if_hf->at_response, response_code, error_code);
+#endif
+}
+
+/* response for CLCC command */
+
+static void clcc_response_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 4) {
+		*user = TYPE_ENUM(bthf_call_direction_t);
+		*enum_func = enum_defines;
+	} else if (argc == 5) {
+		*user = TYPE_ENUM(bthf_call_state_t);
+		*enum_func = enum_defines;
+	} else if (argc == 6) {
+		*user = TYPE_ENUM(bthf_call_mode_t);
+		*enum_func = enum_defines;
+	} else if (argc == 7) {
+		*user = TYPE_ENUM(bthf_call_mpty_type_t);
+		*enum_func = enum_defines;
+	} else if (argc == 9) {
+		*user = TYPE_ENUM(bthf_call_addrtype_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void clcc_response_p(int argc, const char **argv)
+{
+	int index;
+	bthf_call_direction_t dir;
+	bthf_call_state_t state;
+	bthf_call_mode_t mode;
+	bthf_call_mpty_type_t mpty;
+	const char *number;
+	bthf_call_addrtype_t type;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	bt_bdaddr_t addr;
+#endif
+
+	RETURN_IF_NULL(if_hf);
+
+	/* index */
+	if (argc <= 2) {
+		haltest_error("No index specified\n");
+		return;
+	}
+	index = atoi(argv[2]);
+
+	/* direction */
+	if (argc <= 3) {
+		haltest_error("No direction specified\n");
+		return;
+	}
+	dir = str2bthf_call_direction_t(argv[3]);
+
+	/* call state */
+	if (argc <= 4) {
+		haltest_error("No call state specified\n");
+		return;
+	}
+	state = str2bthf_call_state_t(argv[4]);
+
+	/* call mode */
+	if (argc <= 5) {
+		haltest_error("No mode specified\n");
+		return;
+	}
+	mode = str2bthf_call_mode_t(argv[5]);
+
+	/* call mpty type */
+	if (argc <= 6) {
+		haltest_error("No mpty type specified\n");
+		return;
+	}
+	mpty = str2bthf_call_mpty_type_t(argv[6]);
+
+	/* number */
+	if (argc <= 7) {
+		haltest_error("No number specified\n");
+		return;
+	}
+	number = argv[7];
+
+	/* call mpty type */
+	if (argc <= 8) {
+		haltest_error("No address type specified\n");
+		return;
+	}
+	type = str2bthf_call_addrtype_t(argv[8]);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	VERIFY_ADDR_ARG(9, &addr);
+
+	EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+								type, &addr);
+#else
+	EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
+									type);
+#endif
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void configure_wbs_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 4) {
+		*user = TYPE_ENUM(bthf_wbs_config_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void configure_wbs_p(int argc, const char **argv)
+{
+	bthf_wbs_config_t wbs;
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hf);
+
+	if (argc <= 3) {
+		haltest_error("Too few parameters specified\n");
+		return;
+	}
+
+	VERIFY_ADDR_ARG(2, &addr);
+	wbs = str2bthf_wbs_config_t(argv[3]);
+
+	EXEC(if_hf->configure_wbs, &addr, wbs);
+}
+#endif
+
+/* phone state change */
+
+static void phone_state_change_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 5) {
+		*user = TYPE_ENUM(bthf_call_state_t);
+		*enum_func = enum_defines;
+	} else if (argc == 7) {
+		*user = TYPE_ENUM(bthf_call_addrtype_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void phone_state_change_p(int argc, const char **argv)
+{
+	int num_active;
+	int num_held;
+	bthf_call_state_t call_setup_state;
+	const char *number;
+	bthf_call_addrtype_t type;
+
+	RETURN_IF_NULL(if_hf);
+
+	/* num_active */
+	if (argc <= 2) {
+		haltest_error("No num_active specified\n");
+		return;
+	}
+	num_active = atoi(argv[2]);
+
+	/* num_held */
+	if (argc <= 3) {
+		haltest_error("No num_held specified\n");
+		return;
+	}
+	num_held = atoi(argv[3]);
+
+	/* setup state */
+	if (argc <= 4) {
+		haltest_error("No call setup state specified\n");
+		return;
+	}
+	call_setup_state = str2bthf_call_state_t(argv[4]);
+
+	/* number */
+	if (argc <= 5) {
+		haltest_error("No number specified\n");
+		return;
+	}
+	number = argv[5];
+
+	/* call mpty type */
+	if (argc <= 6) {
+		haltest_error("No address type specified\n");
+		return;
+	}
+	type = str2bthf_call_addrtype_t(argv[6]);
+
+	EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state,
+								number, type);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hf);
+
+	EXECV(if_hf->cleanup);
+	if_hf = NULL;
+}
+
+static struct method methods[] = {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	STD_METHODH(init, "[<max_hf_clients>]"),
+	STD_METHODH(start_voice_recognition, "<addr>"),
+	STD_METHODH(stop_voice_recognition, "<addr>"),
+	STD_METHODCH(volume_control, "<vol_type> <volume> <addr>"),
+	STD_METHODH(cops_response, "<cops string> <addr>"),
+	STD_METHODCH(cind_response,
+			"<svc> <num_active> <num_held> <setup_state> <signal> "
+			"<roam> <batt_chg> <addr>"),
+	STD_METHODH(formatted_at_response, "<at_response> <addr>"),
+	STD_METHODCH(at_response, "<response_code> [<error_code> <bdaddr>]"),
+	STD_METHODCH(clcc_response,
+			"<index> <direction> <state> <mode> <mpty> <number> "
+			"<type> <addr>"),
+	STD_METHODCH(configure_wbs, "<addr> <wbs config>"),
+#else
+	STD_METHOD(init),
+	STD_METHOD(start_voice_recognition),
+	STD_METHOD(stop_voice_recognition),
+	STD_METHODCH(volume_control, "<vol_type> <volume>"),
+	STD_METHODH(cops_response, "<cops string>"),
+	STD_METHODCH(cind_response,
+			"<svc> <num_active> <num_held> <setup_state> <signal> "
+			"<roam> <batt_chg>"),
+	STD_METHODH(formatted_at_response, "<at_response>"),
+	STD_METHODCH(at_response, "<response_code> [<error_code>]"),
+	STD_METHODCH(clcc_response,
+			"<index> <direction> <state> <mode> <mpty> <number> "
+			"<type>"),
+#endif
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHODCH(connect_audio, "<addr>"),
+	STD_METHODCH(disconnect_audio, "<addr>"),
+	STD_METHODCH(device_status_notification,
+			"<ntk_state> <svt_type> <signal> <batt_chg>"),
+	STD_METHODCH(phone_state_change,
+			"<num_active> <num_held> <setup_state> <number> "
+			"<type>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hf_if = {
+	.name = "handsfree",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-hh.c b/bluez/repo/android/client/if-hh.c
new file mode 100644
index 0000000..25519e5
--- /dev/null
+++ b/bluez/repo/android/client/if-hh.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const bthh_interface_t *if_hh = NULL;
+
+SINTMAP(bthh_protocol_mode_t, -1, "(unknown)")
+	DELEMENT(BTHH_REPORT_MODE),
+	DELEMENT(BTHH_BOOT_MODE),
+	DELEMENT(BTHH_UNSUPPORTED_MODE),
+ENDMAP
+
+SINTMAP(bthh_report_type_t, -1, "(unknown)")
+	DELEMENT(BTHH_INPUT_REPORT),
+	DELEMENT(BTHH_OUTPUT_REPORT),
+	DELEMENT(BTHH_FEATURE_REPORT),
+ENDMAP
+
+SINTMAP(bthh_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTHH_CONN_STATE_CONNECTED),
+	DELEMENT(BTHH_CONN_STATE_CONNECTING),
+	DELEMENT(BTHH_CONN_STATE_DISCONNECTED),
+	DELEMENT(BTHH_CONN_STATE_DISCONNECTING),
+	DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST),
+	DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST),
+	DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES),
+	DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER),
+	DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC),
+	DELEMENT(BTHH_CONN_STATE_UNKNOWN),
+ENDMAP
+
+SINTMAP(bthh_status_t, -1, "(unknown)")
+	DELEMENT(BTHH_OK),
+	DELEMENT(BTHH_HS_HID_NOT_READY),
+	DELEMENT(BTHH_HS_INVALID_RPT_ID),
+	DELEMENT(BTHH_HS_TRANS_NOT_SPT),
+	DELEMENT(BTHH_HS_INVALID_PARAM),
+	DELEMENT(BTHH_HS_ERROR),
+	DELEMENT(BTHH_ERR),
+	DELEMENT(BTHH_ERR_SDP),
+	DELEMENT(BTHH_ERR_PROTO),
+	DELEMENT(BTHH_ERR_DB_FULL),
+	DELEMENT(BTHH_ERR_TOD_UNSPT),
+	DELEMENT(BTHH_ERR_NO_RES),
+	DELEMENT(BTHH_ERR_AUTH_FAILED),
+	DELEMENT(BTHH_ERR_HDL),
+ENDMAP
+
+static char connected_device_addr[MAX_ADDR_STR_LEN];
+/*
+ * Callback for connection state change.
+ * state will have one of the values from bthh_connection_state_t
+ */
+static void connection_state_cb(bt_bdaddr_t *bd_addr,
+						bthh_connection_state_t state)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, addr),
+					bthh_connection_state_t2str(state));
+	if (state == BTHH_CONN_STATE_CONNECTED)
+		strcpy(connected_device_addr, addr);
+}
+
+/*
+ * Callback for virtual unplug api.
+ * the status of the virtual unplug
+ */
+static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__,
+						bt_bdaddr_t2str(bd_addr, addr),
+						bthh_status_t2str(hh_status));
+}
+
+/* Callback for Android 5.0 handshake api. */
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void handshake_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__,
+						bt_bdaddr_t2str(bd_addr, addr),
+						bthh_status_t2str(hh_status));
+}
+#endif
+
+/*
+ * Callback for get hid info
+ * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id,
+ * version, ctry_code, len
+ */
+static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	/* TODO: bluedroid does not seem to ever call this callback */
+	haltest_info("%s: bd_addr=%s\n", __func__,
+						bt_bdaddr_t2str(bd_addr, addr));
+}
+
+/*
+ * Callback for get/set protocol api.
+ * the protocol mode is one of the value from bthh_protocol_mode_t
+ */
+static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+						bthh_protocol_mode_t mode)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__,
+					bt_bdaddr_t2str(bd_addr, addr),
+					bthh_status_t2str(hh_status),
+					bthh_protocol_mode_t2str(mode));
+}
+
+/* Callback for get/set_idle_time api. */
+static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+								int idle_rate)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__,
+				bt_bdaddr_t2str(bd_addr, addr),
+				bthh_status_t2str(hh_status), idle_rate);
+}
+
+
+/*
+ * Callback for get report api.
+ * if status is ok rpt_data contains the report data
+ */
+static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
+						uint8_t *rpt_data, int rpt_size)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	/* TODO: print actual report */
+	haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__,
+					bt_bdaddr_t2str(bd_addr, addr),
+					bthh_status_t2str(hh_status), rpt_size);
+}
+
+static bthh_callbacks_t bthh_callbacks = {
+	.size = sizeof(bthh_callbacks),
+	.connection_state_cb = connection_state_cb,
+	.hid_info_cb = hid_info_cb,
+	.protocol_mode_cb = protocol_mode_cb,
+	.idle_time_cb = idle_time_cb,
+	.get_report_cb = get_report_cb,
+	.virtual_unplug_cb = virtual_unplug_cb,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.handshake_cb = handshake_cb
+#endif
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hh);
+
+	EXEC(if_hh->init, &bthh_callbacks);
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = (void *) connected_device_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hh->connect, &addr);
+}
+
+/* disconnect */
+
+/* Same completion as connect_c */
+#define disconnect_c connect_c
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hh->disconnect, &addr);
+}
+
+/* virtual_unplug */
+
+/* Same completion as connect_c */
+#define virtual_unplug_c connect_c
+
+static void virtual_unplug_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_hh->virtual_unplug, &addr);
+}
+
+/* set_info */
+
+/* Same completion as connect_c */
+#define set_info_c connect_c
+
+static void set_info_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_hid_info_t hid_info;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	memset(&hid_info, 0, sizeof(hid_info));
+
+	/*
+	 * This command is intentionally not supported. See comment from
+	 * bt_hid_info() in android/hidhost.c
+	 */
+	EXEC(if_hh->set_info, &addr, hid_info);
+}
+
+/* get_protocol */
+
+static void get_protocol_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthh_protocol_mode_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_protocol_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_protocol_mode_t protocolMode;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No protocol mode specified\n");
+		return;
+	}
+	protocolMode = str2bthh_protocol_mode_t(argv[3]);
+
+	EXEC(if_hh->get_protocol, &addr, protocolMode);
+}
+
+/* set_protocol */
+
+/* Same completion as get_protocol_c */
+#define set_protocol_c get_protocol_c
+
+static void set_protocol_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_protocol_mode_t protocolMode;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No protocol mode specified\n");
+		return;
+	}
+	protocolMode = str2bthh_protocol_mode_t(argv[3]);
+
+	EXEC(if_hh->set_protocol, &addr, protocolMode);
+}
+
+/* get_report */
+
+static void get_report_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthh_report_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_report_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_report_type_t reportType;
+	uint8_t reportId;
+	int bufferSize;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No report type specified\n");
+		return;
+	}
+	reportType = str2bthh_report_type_t(argv[3]);
+
+	if (argc < 5) {
+		haltest_error("No reportId specified\n");
+		return;
+	}
+	reportId = (uint8_t) atoi(argv[4]);
+
+	if (argc < 6) {
+		haltest_error("No bufferSize specified\n");
+		return;
+	}
+	bufferSize = atoi(argv[5]);
+
+	EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize);
+}
+
+/* set_report */
+
+static void set_report_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(bthh_report_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void set_report_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	bthh_report_type_t reportType;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc <= 3) {
+		haltest_error("No report type specified\n");
+		return;
+	}
+	reportType = str2bthh_report_type_t(argv[3]);
+
+	if (argc <= 4) {
+		haltest_error("No report specified\n");
+		return;
+	}
+
+	EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]);
+}
+
+/* send_data */
+
+static void send_data_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = connected_device_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void send_data_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_hh);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc <= 3) {
+		haltest_error("No data to send specified\n");
+		return;
+	}
+
+	EXEC(if_hh->send_data, &addr, (char *) argv[3]);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hh);
+
+	EXECV(if_hh->cleanup);
+}
+
+/* Methods available in bthh_interface_t */
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr>"),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHODCH(virtual_unplug, "<addr>"),
+	STD_METHODCH(set_info, "<addr>"),
+	STD_METHODCH(get_protocol, "<addr> <mode>"),
+	STD_METHODCH(set_protocol, "<addr> <mode>"),
+	STD_METHODCH(get_report, "<addr> <type> <report_id> <size>"),
+	STD_METHODCH(set_report, "<addr> <type> <hex_encoded_report>"),
+	STD_METHODCH(send_data, "<addr> <hex_encoded_data>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hh_if = {
+	.name = "hidhost",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-hl.c b/bluez/repo/android/client/if-hl.c
new file mode 100644
index 0000000..bd05671
--- /dev/null
+++ b/bluez/repo/android/client/if-hl.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hl.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+SINTMAP(bthl_mdep_role_t, -1, "(unknown)")
+	DELEMENT(BTHL_MDEP_ROLE_SOURCE),
+	DELEMENT(BTHL_MDEP_ROLE_SINK),
+ENDMAP
+
+SINTMAP(bthl_channel_type_t, -1, "(unknown)")
+	DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE),
+	DELEMENT(BTHL_CHANNEL_TYPE_STREAMING),
+	DELEMENT(BTHL_CHANNEL_TYPE_ANY),
+ENDMAP
+
+SINTMAP(bthl_app_reg_state_t, -1, "(unknown)")
+	DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS),
+	DELEMENT(BTHL_APP_REG_STATE_REG_FAILED),
+	DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS),
+	DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED),
+ENDMAP
+
+SINTMAP(bthl_channel_state_t, -1, "(unknown)")
+	DELEMENT(BTHL_CONN_STATE_CONNECTING),
+	DELEMENT(BTHL_CONN_STATE_CONNECTED),
+	DELEMENT(BTHL_CONN_STATE_DISCONNECTING),
+	DELEMENT(BTHL_CONN_STATE_DISCONNECTED),
+	DELEMENT(BTHL_CONN_STATE_DESTROYED),
+ENDMAP
+
+#define APP_ID_SIZE 20
+#define MDEP_CFG_SIZE 10
+#define CHANNEL_ID_SIZE 50
+
+struct channel_info {
+	int fd;
+};
+
+struct mdep_cfg {
+	uint8_t role;
+	struct channel_info channel[CHANNEL_ID_SIZE];
+};
+
+struct {
+	struct mdep_cfg mdep[MDEP_CFG_SIZE];
+} app[APP_ID_SIZE];
+
+const bthl_interface_t *if_hl = NULL;
+
+static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state)
+{
+	haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__,
+				app_id, bthl_app_reg_state_t2str(state));
+}
+
+static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr,
+					int index, int channel_id,
+					bthl_channel_state_t state, int fd)
+{
+	char addr[MAX_ADDR_STR_LEN];
+
+	haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n"
+			"channel_id=%d channel_state=%s fd=%d\n", __func__,
+			app_id, bt_bdaddr_t2str(bd_addr, addr), index,
+			channel_id, bthl_channel_state_t2str(state), fd);
+
+	if (app_id >= APP_ID_SIZE || index >= MDEP_CFG_SIZE
+			|| channel_id >= CHANNEL_ID_SIZE) {
+		haltest_error("exceeds maximum limit");
+		return;
+	}
+
+	if (state == BTHL_CONN_STATE_CONNECTED) {
+		app[app_id].mdep[index].channel[channel_id].fd = fd;
+
+		/*
+		 * PTS expects dummy data on fd when it
+		 * connects in source role.
+		 */
+		if (app[app_id].mdep[index].role == BTHL_MDEP_ROLE_SOURCE)
+			if (write(fd, "0", sizeof("0")) < 0)
+				haltest_error("writing data on fd failed\n");
+
+		return;
+	}
+
+	if (state == BTHL_CONN_STATE_DISCONNECTED ||
+			state == BTHL_CONN_STATE_DESTROYED) {
+		if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
+			close(app[app_id].mdep[index].channel[channel_id].fd);
+			app[app_id].mdep[index].channel[channel_id].fd = -1;
+		}
+	}
+}
+
+static bthl_callbacks_t hl_cbacks = {
+	.size = sizeof(hl_cbacks),
+	.app_reg_state_cb = app_reg_state_cb,
+	.channel_state_cb = channel_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	int i, j, k;
+
+	for (i = 0; i < APP_ID_SIZE; i++) {
+		for (j = 0; j < MDEP_CFG_SIZE; j++) {
+			app[i].mdep[j].role = 0;
+			for (k = 0; k < CHANNEL_ID_SIZE; k++)
+				app[i].mdep[j].channel[k].fd = -1;
+		}
+	}
+
+
+	RETURN_IF_NULL(if_hl);
+
+	EXEC(if_hl->init, &hl_cbacks);
+}
+
+/* register_application */
+
+static void register_application_p(int argc, const char **argv)
+{
+	bthl_reg_param_t reg;
+	uint16_t mdep_argc_init, mdep_argc_off;
+	int app_id = -1;
+	int i;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app name is specified\n");
+		return;
+	}
+
+	if (argc <= 3) {
+		haltest_error("No provider is specified\n");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No service name is specified\n");
+		return;
+	}
+
+	if (argc <= 5) {
+		haltest_error("No service description is specified\n");
+		return;
+	}
+
+	if (argc <= 6) {
+		haltest_error("No num of mdeps is specified\n");
+		return;
+	}
+
+	memset(&reg, 0, sizeof(reg));
+
+	if (argc != ((atoi(argv[6]) * 4) + 7)) {
+		haltest_error("mdep cfg argumetns are not proper\n");
+		return;
+	}
+
+	reg.application_name = argv[2];
+
+	if (strcmp("-", argv[3]))
+		reg.provider_name = argv[3];
+
+	if (strcmp("-", argv[4]))
+		reg.srv_name = argv[4];
+
+	if (strcmp("-", argv[5]))
+		reg.srv_desp = argv[5];
+
+	reg.number_of_mdeps = atoi(argv[6]);
+
+	reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t));
+	if (!reg.mdep_cfg) {
+		haltest_error("malloc failed\n");
+		return;
+	}
+	mdep_argc_init = 7;
+
+	for (i = 0; i < reg.number_of_mdeps; i++) {
+		mdep_argc_off = mdep_argc_init + (4 * i);
+		reg.mdep_cfg[i].mdep_role =
+				str2bthl_mdep_role_t(argv[mdep_argc_off]);
+		reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]);
+		reg.mdep_cfg[i].channel_type =
+			str2bthl_channel_type_t(argv[mdep_argc_off + 2]);
+
+		if (!strcmp("-", argv[mdep_argc_off + 3])) {
+			reg.mdep_cfg[i].mdep_description = NULL;
+			continue;
+		}
+
+		reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3];
+	}
+
+	EXEC(if_hl->register_application, &reg, &app_id);
+
+	for (i = 0; i < reg.number_of_mdeps; i++)
+		app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role;
+
+	free(reg.mdep_cfg);
+}
+
+/* unregister_application */
+
+static void unregister_application_p(int argc, const char **argv)
+{
+	uint32_t app_id;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app id is specified");
+		return;
+	}
+
+	app_id = (uint32_t) atoi(argv[2]);
+
+	EXEC(if_hl->unregister_application, app_id);
+}
+
+/* connect_channel */
+
+static void connect_channel_p(int argc, const char **argv)
+{
+	uint32_t app_id, mdep_cfg_index;
+	int channel_id = -1;
+	bt_bdaddr_t bd_addr;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app id is specified");
+		return;
+	}
+
+	VERIFY_ADDR_ARG(3, &bd_addr);
+
+	if (argc <= 4) {
+		haltest_error("No mdep cfg index is specified");
+		return;
+	}
+
+	app_id = (uint32_t) atoi(argv[2]);
+	mdep_cfg_index = (uint32_t) atoi(argv[4]);
+
+	EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index,
+								&channel_id);
+}
+
+/* destroy_channel */
+
+static void destroy_channel_p(int argc, const char **argv)
+{
+	uint32_t channel_id;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No channel id is specified");
+		return;
+	}
+
+	channel_id = (uint32_t) atoi(argv[2]);
+
+	EXEC(if_hl->destroy_channel, channel_id);
+}
+
+/* close_channel */
+
+static void close_channel_p(int argc, const char **argv)
+{
+	uint32_t app_id;
+	uint8_t index;
+	int channel_id;
+
+	RETURN_IF_NULL(if_hl);
+
+	if (argc <= 2) {
+		haltest_error("No app id is specified");
+		return;
+	}
+
+	if (argc <= 3) {
+		haltest_error("No mdep_cfg_index is specified");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No channel_id is specified");
+		return;
+	}
+
+	app_id = (uint32_t) atoi(argv[2]);
+	if (app_id >= APP_ID_SIZE) {
+		haltest_error("Wrong app_id specified: %u\n", app_id);
+		return;
+	}
+
+	index = (uint8_t) atoi(argv[3]);
+	if (index >= MDEP_CFG_SIZE) {
+		haltest_error("Wrong mdep cfg index: %u\n", index);
+		return;
+	}
+
+	channel_id = atoi(argv[4]);
+	if (channel_id >= CHANNEL_ID_SIZE) {
+		haltest_error("Wrong channel id: %u\n", channel_id);
+		return;
+	}
+
+	if (app[app_id].mdep[index].channel[channel_id].fd >= 0) {
+		shutdown(app[app_id].mdep[index].channel[channel_id].fd,
+								SHUT_RDWR);
+		app[app_id].mdep[index].channel[channel_id].fd = -1;
+	}
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_hl);
+
+	EXECV(if_hl->cleanup);
+	if_hl = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODH(register_application,
+		"<app_name> <provider_name> <srv_name> <srv_descr>\n"
+		"<num_of_mdeps>\n"
+		"[[<mdep_role>] [<data_type>] [<channel_type>] [<mdep_descr>]]"
+		"..."),
+	STD_METHODH(unregister_application, "<app_id>"),
+	STD_METHODH(connect_channel, "<app_id> <bd_addr> <mdep_cfg_index>"),
+	STD_METHODH(destroy_channel, "<channel_id>"),
+	STD_METHODH(close_channel, "<app_id> <mdep_cfg_index> <channel_id>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface hl_if = {
+	.name = "hl",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-main.h b/bluez/repo/android/client/if-main.h
new file mode 100644
index 0000000..d239bb2
--- /dev/null
+++ b/bluez/repo/android/client/if-main.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <poll.h>
+
+#include <hardware/audio.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_hl.h>
+
+#include "hal.h"
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+#include <hardware/bt_hf_client.h>
+#include <hardware/bt_mce.h>
+#endif
+
+#include <hardware/bt_rc.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_types.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+
+extern audio_hw_device_t *if_audio;
+
+/* Interfaces from hal that can be populated during application lifetime */
+extern const bt_interface_t *if_bluetooth;
+extern const btav_interface_t *if_av;
+extern const btrc_interface_t *if_rc;
+extern const bthf_interface_t *if_hf;
+extern const bthh_interface_t *if_hh;
+extern const btpan_interface_t *if_pan;
+extern const bthl_interface_t *if_hl;
+extern const btsock_interface_t *if_sock;
+extern const btgatt_interface_t *if_gatt;
+extern const btgatt_server_interface_t *if_gatt_server;
+extern const btgatt_client_interface_t *if_gatt_client;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+extern const btrc_ctrl_interface_t *if_rc_ctrl;
+extern const bthf_client_interface_t *if_hf_client;
+extern const btmce_interface_t *if_mce;
+extern const btav_interface_t *if_av_sink;
+#endif
+
+/*
+ * Structure defines top level interfaces that can be used in test tool
+ * this will contain values as: bluetooth, av, gatt, socket, pan...
+ */
+struct interface {
+	const char *name; /* interface name */
+	struct method *methods; /* methods available for this interface */
+};
+
+extern const struct interface audio_if;
+extern const struct interface sco_if;
+extern const struct interface bluetooth_if;
+extern const struct interface av_if;
+extern const struct interface rc_if;
+extern const struct interface gatt_if;
+extern const struct interface gatt_client_if;
+extern const struct interface gatt_server_if;
+extern const struct interface pan_if;
+extern const struct interface sock_if;
+extern const struct interface hf_if;
+extern const struct interface hh_if;
+extern const struct interface hl_if;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+extern const struct interface ctrl_rc_if;
+extern const struct interface hf_client_if;
+extern const struct interface mce_if;
+extern const struct interface av_sink_if;
+#endif
+
+/* Interfaces that will show up in tool (first part of command line) */
+extern const struct interface *interfaces[];
+
+#define METHOD(name, func, comp, help) {name, func, comp, help}
+#define STD_METHOD(m) {#m, m##_p, NULL, NULL}
+#define STD_METHODC(m) {#m, m##_p, m##_c, NULL}
+#define STD_METHODH(m, h) {#m, m##_p, NULL, h}
+#define STD_METHODCH(m, h) {#m, m##_p, m##_c, h}
+#define END_METHOD {"", NULL, NULL, NULL}
+
+/*
+ * Function to parse argument for function, argv[0] and argv[1] are already
+ * parsed before this function is called and contain interface and method name
+ * up to argc - 1 arguments are finished and should be used to decide which
+ * function enumeration function to return
+ */
+typedef void (*parse_and_call)(int argc, const char **argv);
+
+/*
+ * This is prototype of function that will return string for given number.
+ * Purpose is to enumerate string for auto completion.
+ * Function of this type will always be called in loop.
+ * First time function is called i = 0, then if function returns non-NULL
+ * it will be called again till for some value of i it will return NULL
+ */
+typedef const char *(*enum_func)(void *user, int i);
+
+/*
+ * This is prototype of function that when given argc, argv will
+ * fill enum_func with pointer to function that will enumerate
+ * parameters for argc argument, user will be passed to enum_func.
+ */
+typedef void (*tab_complete)(int argc, const char **argv, enum_func *enum_func,
+								void **user);
+
+/*
+ * For each method there is name and two functions to parse command line
+ * and call proper hal function on.
+ */
+struct method {
+	const char *name;
+	parse_and_call func;
+	tab_complete complete;
+	const char *help;
+};
+
+int haltest_error(const char *format, ...)
+					__attribute__((format(printf, 1, 2)));
+int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2)));
+int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2)));
+
+/* Enumerator for discovered devices, to be used as tab completion enum_func */
+const char *enum_devices(void *v, int i);
+const char *interface_name(void *v, int i);
+const char *command_name(void *v, int i);
+void add_remote_device(const bt_bdaddr_t *addr);
+bool close_hw_bt_dev(void);
+
+const struct interface *get_interface(const char *name);
+struct method *get_method(struct method *methods, const char *name);
+struct method *get_command(const char *name);
+const struct method *get_interface_method(const char *iname,
+							const char *mname);
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/* Helper macro for executing function on interface and printing BT_STATUS */
+#define EXEC(f, ...) \
+	{ \
+		if (f) { \
+			int err = f(__VA_ARGS__); \
+			haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \
+		} else { \
+			haltest_info("%s is NULL\n", #f); \
+		} \
+	}
+
+/* Helper macro for executing void function on interface */
+#define EXECV(f, ...) \
+	{ \
+		(void) f(__VA_ARGS__); \
+		haltest_info("%s: void\n", #f); \
+	}
+
+#define RETURN_IF_NULL(x) \
+	do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0)
+
+#define VERIFY_ADDR_ARG(n, adr) \
+	do { \
+		if (n < argc) {\
+			str2bt_bdaddr_t(argv[n], adr); \
+		} else { \
+			haltest_error("No address specified\n");\
+			return;\
+		} \
+	} while (0)
diff --git a/bluez/repo/android/client/if-mce.c b/bluez/repo/android/client/if-mce.c
new file mode 100644
index 0000000..5f101e0
--- /dev/null
+++ b/bluez/repo/android/client/if-mce.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btmce_interface_t *if_mce = NULL;
+
+/*
+ *  Callback for get_remote_mas_instances
+ */
+static void btmce_remote_mas_instances_cb(bt_status_t status,
+						bt_bdaddr_t *bd_addr,
+						int num_instances,
+						btmce_mas_instance_t *instances)
+{
+	int i;
+
+	haltest_info("%s: status=%s bd_addr=%s num_instance=%d\n", __func__,
+				bt_status_t2str(status), bdaddr2str(bd_addr),
+				num_instances);
+
+	for (i = 0; i < num_instances; i++)
+		haltest_info("id=%d scn=%d msg_types=%d name=%s\n",
+				instances[i].id, instances[i].scn,
+				instances[i].msg_types, instances[i].p_name);
+}
+
+static btmce_callbacks_t mce_cbacks = {
+	.size = sizeof(mce_cbacks),
+	.remote_mas_instances_cb = btmce_remote_mas_instances_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_mce);
+
+	EXEC(if_mce->init, &mce_cbacks);
+}
+
+static void get_remote_mas_instances_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+/* search for MAS instances on remote device */
+
+static void get_remote_mas_instances_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_mce);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_mce->get_remote_mas_instances, &addr);
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(get_remote_mas_instances, "<addr>"),
+	END_METHOD
+};
+
+const struct interface mce_if = {
+	.name = "mce",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-pan.c b/bluez/repo/android/client/if-pan.c
new file mode 100644
index 0000000..b0c4b84
--- /dev/null
+++ b/bluez/repo/android/client/if-pan.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+const btpan_interface_t *if_pan = NULL;
+
+typedef int btpan_role_t;
+
+SINTMAP(btpan_role_t, -1, "(unknown)")
+	DELEMENT(BTPAN_ROLE_NONE),
+	DELEMENT(BTPAN_ROLE_PANNAP),
+	DELEMENT(BTPAN_ROLE_PANU),
+ENDMAP
+
+SINTMAP(btpan_connection_state_t, -1, "(unknown)")
+	DELEMENT(BTPAN_STATE_CONNECTED),
+	DELEMENT(BTPAN_STATE_CONNECTING),
+	DELEMENT(BTPAN_STATE_DISCONNECTED),
+	DELEMENT(BTPAN_STATE_DISCONNECTING),
+ENDMAP
+
+SINTMAP(btpan_control_state_t, -1, "(unknown)")
+	DELEMENT(BTPAN_STATE_ENABLED),
+	DELEMENT(BTPAN_STATE_DISABLED),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void control_state_cb(btpan_control_state_t state, int local_role,
+					bt_status_t error, const char *ifname)
+#else
+static void control_state_cb(btpan_control_state_t state, bt_status_t error,
+					int local_role, const char *ifname)
+#endif
+{
+	haltest_info("%s: state=%s error=%s local_role=%s ifname=%s\n",
+			__func__, btpan_control_state_t2str(state),
+			bt_status_t2str(error), btpan_role_t2str(local_role),
+			ifname);
+}
+
+static char last_used_addr[MAX_ADDR_STR_LEN];
+
+static void connection_state_cb(btpan_connection_state_t state,
+				bt_status_t error, const bt_bdaddr_t *bd_addr,
+				int local_role, int remote_role)
+{
+	haltest_info("%s: state=%s error=%s bd_addr=%s local_role=%s remote_role=%s\n",
+			__func__, btpan_connection_state_t2str(state),
+			bt_status_t2str(error),
+			bt_bdaddr_t2str(bd_addr, last_used_addr),
+			btpan_role_t2str(local_role),
+			btpan_role_t2str(remote_role));
+}
+
+static btpan_callbacks_t pan_cbacks = {
+	.size = sizeof(pan_cbacks),
+	.control_state_cb = control_state_cb,
+	.connection_state_cb = connection_state_cb
+};
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_pan);
+
+	EXEC(if_pan->init, &pan_cbacks);
+}
+
+/* enable */
+
+static void enable_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btpan_role_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void enable_p(int argc, const char **argv)
+{
+	int local_role;
+
+	RETURN_IF_NULL(if_pan);
+
+	/* local role */
+	if (argc < 3) {
+		haltest_error("No local mode specified\n");
+		return;
+	}
+	local_role = str2btpan_role_t(argv[2]);
+	if (local_role == -1)
+		local_role = atoi(argv[2]);
+
+	EXEC(if_pan->enable, local_role);
+}
+
+/* get_local_role */
+
+static void get_local_role_p(int argc, const char **argv)
+{
+	int local_role;
+
+	RETURN_IF_NULL(if_pan);
+
+	local_role = if_pan->get_local_role();
+	haltest_info("local_role: %s\n", btpan_role_t2str(local_role));
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	} else if (argc == 4 || argc == 5) {
+		*user = TYPE_ENUM(btpan_role_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	int local_role;
+	int remote_role;
+
+	RETURN_IF_NULL(if_pan);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	/* local role */
+	if (argc < 4) {
+		haltest_error("No local mode specified\n");
+		return;
+	}
+	local_role = str2btpan_role_t(argv[3]);
+	if (local_role == -1)
+		local_role = atoi(argv[3]);
+
+	/* remote role */
+	if (argc < 5) {
+		haltest_error("No remote mode specified\n");
+		return;
+	}
+	remote_role = str2btpan_role_t(argv[4]);
+	if (remote_role == -1)
+		remote_role = atoi(argv[4]);
+
+	EXEC(if_pan->connect, &addr, local_role, remote_role);
+}
+
+/* disconnect */
+
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = last_used_addr;
+		*enum_func = enum_one_string;
+	}
+}
+
+static void disconnect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+
+	RETURN_IF_NULL(if_pan);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	EXEC(if_pan->disconnect, &addr);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_pan);
+
+	EXECV(if_pan->cleanup);
+	if_pan = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(connect, "<addr> <local_role> <remote_role>"),
+	STD_METHODCH(enable, "<local_role>"),
+	STD_METHOD(get_local_role),
+	STD_METHODCH(disconnect, "<addr>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface pan_if = {
+	.name = "pan",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-rc-ctrl.c b/bluez/repo/android/client/if-rc-ctrl.c
new file mode 100644
index 0000000..3980764
--- /dev/null
+++ b/bluez/repo/android/client/if-rc-ctrl.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_rc.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_ctrl_interface_t *if_rc_ctrl = NULL;
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void passthrough_rsp_cb(int id, int key_state)
+{
+	haltest_info("%s: id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static void connection_state_cb(bool state, bt_bdaddr_t *bd_addr)
+{
+	haltest_info("%s: state=%s bd_addr=%s\n", __func__,
+					state ? "true" : "false",
+					bt_bdaddr_t2str(bd_addr, last_addr));
+}
+
+static btrc_ctrl_callbacks_t rc_ctrl_cbacks = {
+	.size = sizeof(rc_ctrl_cbacks),
+	.passthrough_rsp_cb = passthrough_rsp_cb,
+	.connection_state_cb = connection_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc_ctrl);
+
+	EXEC(if_rc_ctrl->init, &rc_ctrl_cbacks);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc_ctrl);
+
+	EXECV(if_rc_ctrl->cleanup);
+	if_rc_ctrl = NULL;
+}
+
+/* send_pass_through_cmd */
+static void send_pass_through_cmd_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = NULL;
+		*enum_func = enum_devices;
+	}
+}
+
+static void send_pass_through_cmd_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	uint8_t key_code, key_state;
+
+	RETURN_IF_NULL(if_rc);
+	VERIFY_ADDR_ARG(2, &addr);
+
+	if (argc < 4) {
+		haltest_error("No key code specified\n");
+		return;
+	}
+
+	key_code = (uint8_t) atoi(argv[3]);
+
+	if (argc < 5) {
+		haltest_error("No key state specified\n");
+		return;
+	}
+
+	key_state = (uint8_t) atoi(argv[4]);
+
+	EXEC(if_rc_ctrl->send_pass_through_cmd, &addr, key_code, key_state);
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(send_pass_through_cmd, "<bd_addr> <key_code> <key_state>"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface ctrl_rc_if = {
+	.name = "rc-ctrl",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-rc.c b/bluez/repo/android/client/if-rc.c
new file mode 100644
index 0000000..ed65600
--- /dev/null
+++ b/bluez/repo/android/client/if-rc.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_interface_t *if_rc = NULL;
+
+SINTMAP(btrc_play_status_t, -1, "(unknown)")
+	DELEMENT(BTRC_PLAYSTATE_STOPPED),
+	DELEMENT(BTRC_PLAYSTATE_PLAYING),
+	DELEMENT(BTRC_PLAYSTATE_PAUSED),
+	DELEMENT(BTRC_PLAYSTATE_FWD_SEEK),
+	DELEMENT(BTRC_PLAYSTATE_REV_SEEK),
+	DELEMENT(BTRC_PLAYSTATE_ERROR),
+ENDMAP
+
+SINTMAP(btrc_media_attr_t, -1, "(unknown)")
+	DELEMENT(BTRC_MEDIA_ATTR_TITLE),
+	DELEMENT(BTRC_MEDIA_ATTR_ARTIST),
+	DELEMENT(BTRC_MEDIA_ATTR_ALBUM),
+	DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM),
+	DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS),
+	DELEMENT(BTRC_MEDIA_ATTR_GENRE),
+	DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME),
+ENDMAP
+
+SINTMAP(btrc_status_t, -1, "(unknown)")
+	DELEMENT(BTRC_STS_BAD_CMD),
+	DELEMENT(BTRC_STS_BAD_PARAM),
+	DELEMENT(BTRC_STS_NOT_FOUND),
+	DELEMENT(BTRC_STS_INTERNAL_ERR),
+	DELEMENT(BTRC_STS_NO_ERROR),
+ENDMAP
+
+SINTMAP(btrc_event_id_t, -1, "(unknown)")
+	DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED),
+	DELEMENT(BTRC_EVT_TRACK_CHANGE),
+	DELEMENT(BTRC_EVT_TRACK_REACHED_END),
+	DELEMENT(BTRC_EVT_TRACK_REACHED_START),
+	DELEMENT(BTRC_EVT_PLAY_POS_CHANGED),
+	DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED),
+ENDMAP
+
+SINTMAP(btrc_notification_type_t, -1, "(unknown)")
+	DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM),
+	DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void remote_features_cb(bt_bdaddr_t *bd_addr,
+					btrc_remote_features_t features)
+{
+	haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__,
+				bt_bdaddr_t2str(bd_addr, last_addr), features);
+}
+
+static void get_play_status_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_attr_cb(void)
+{
+	haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_values_cb(btrc_player_attr_t attr_id)
+{
+	haltest_info("%s, attr_id=%d\n", __func__, attr_id);
+}
+
+static void get_player_app_value_cb(uint8_t num_attr,
+						btrc_player_attr_t *p_attrs)
+{
+	int i;
+
+	haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+	for (i = 0; i < num_attr; i++)
+		haltest_info("attribute=%u\n", p_attrs[i]);
+}
+
+static void get_player_app_attrs_text_cb(uint8_t num_attr,
+						btrc_player_attr_t *p_attrs)
+{
+	int i;
+
+	haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+	for (i = 0; i < num_attr; i++)
+		haltest_info("attribute=%u\n", p_attrs[i]);
+
+}
+
+static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val,
+								uint8_t *p_vals)
+{
+	haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__,
+						attr_id, num_val, p_vals);
+}
+
+static void set_player_app_value_cb(btrc_player_settings_t *p_vals)
+{
+	int i;
+
+	haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr);
+
+	for (i = 0; i < p_vals->num_attr; i++)
+		haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i],
+							p_vals->attr_values[i]);
+}
+
+static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs)
+{
+	uint8_t i;
+
+	haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr);
+
+	for (i = 0; i < num_attr; i++)
+		haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i]));
+}
+
+static void register_notification_cb(btrc_event_id_t event_id, uint32_t param)
+{
+	haltest_info("%s, event=%u param=%u\n", __func__, event_id, param);
+}
+
+static void volume_change_cb(uint8_t volume, uint8_t ctype)
+{
+	haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype);
+}
+
+static void passthrough_cmd_cb(int id, int key_state)
+{
+	haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static btrc_callbacks_t rc_cbacks = {
+	.size = sizeof(rc_cbacks),
+	.remote_features_cb = remote_features_cb,
+	.get_play_status_cb = get_play_status_cb,
+	.list_player_app_attr_cb = list_player_app_attr_cb,
+	.list_player_app_values_cb = list_player_app_values_cb,
+	.get_player_app_value_cb = get_player_app_value_cb,
+	.get_player_app_attrs_text_cb = get_player_app_attrs_text_cb,
+	.get_player_app_values_text_cb = get_player_app_values_text_cb,
+	.set_player_app_value_cb = set_player_app_value_cb,
+	.get_element_attr_cb = get_element_attr_cb,
+	.register_notification_cb = register_notification_cb,
+	.volume_change_cb = volume_change_cb,
+	.passthrough_cmd_cb = passthrough_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc);
+
+	EXEC(if_rc->init, &rc_cbacks);
+}
+
+/* get_play_status_rsp */
+
+static void get_play_status_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btrc_play_status_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_play_status_rsp_p(int argc, const char **argv)
+{
+	btrc_play_status_t play_status;
+	uint32_t song_len, song_pos;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No play status specified");
+		return;
+	}
+
+	if (argc <= 3) {
+		haltest_error("No song length specified");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No song position specified");
+		return;
+	}
+
+	play_status = str2btrc_play_status_t(argv[2]);
+	song_len = (uint32_t) atoi(argv[3]);
+	song_pos = (uint32_t) atoi(argv[4]);
+
+	EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos);
+}
+
+/* get_element_attr_rsp */
+
+static void get_element_attr_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 4) {
+		*user = TYPE_ENUM(btrc_media_attr_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void get_element_attr_rsp_p(int argc, const char **argv)
+{
+	uint8_t num_attr;
+	btrc_element_attr_val_t attrs;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No number of attributes specified");
+		return;
+	}
+
+	if (argc <= 4) {
+		haltest_error("No attr id and value specified");
+		return;
+	}
+
+	num_attr = (uint8_t) atoi(argv[2]);
+	attrs.attr_id = str2btrc_media_attr_t(argv[3]);
+	strcpy((char *)attrs.text, argv[4]);
+
+	EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs);
+}
+
+/* set_volume */
+
+static void set_volume_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+}
+
+static void set_volume_p(int argc, const char **argv)
+{
+	uint8_t volume;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No volume specified");
+		return;
+	}
+
+	volume = (uint8_t) atoi(argv[2]);
+
+	EXEC(if_rc->set_volume, volume);
+}
+
+/* set_player_app_value_rsp */
+
+static void set_player_app_value_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btrc_status_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void set_player_app_value_rsp_p(int argc, const char **argv)
+{
+	btrc_status_t rsp_status;
+
+	RETURN_IF_NULL(if_rc);
+
+	if (argc <= 2) {
+		haltest_error("No response status specified");
+		return;
+	}
+
+	rsp_status = str2btrc_status_t(argv[2]);
+
+	EXEC(if_rc->set_player_app_value_rsp, rsp_status);
+}
+
+/* register_notification_rsp */
+
+static void register_notification_rsp_c(int argc, const char **argv,
+					enum_func *enum_func, void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btrc_event_id_t);
+		*enum_func = enum_defines;
+	}
+
+	if (argc == 4) {
+		*user = TYPE_ENUM(btrc_notification_type_t);
+		*enum_func = enum_defines;
+	}
+}
+
+static void register_notification_rsp_p(int argc, const char **argv)
+{
+	btrc_event_id_t event_id;
+	btrc_notification_type_t type;
+	btrc_register_notification_t reg;
+	uint32_t song_pos;
+	uint64_t track;
+
+	RETURN_IF_NULL(if_rc);
+
+	memset(&reg, 0, sizeof(reg));
+	event_id = str2btrc_event_id_t(argv[2]);
+	type = str2btrc_notification_type_t(argv[3]);
+
+	switch (event_id) {
+	case BTRC_EVT_PLAY_STATUS_CHANGED:
+		reg.play_status = str2btrc_play_status_t(argv[4]);
+		break;
+
+	case BTRC_EVT_TRACK_CHANGE:
+		track = strtoull(argv[5], NULL, 10);
+		memcpy(reg.track, &track, sizeof(btrc_uid_t));
+		break;
+
+	case BTRC_EVT_TRACK_REACHED_END:
+	case BTRC_EVT_TRACK_REACHED_START:
+		break;
+
+	case BTRC_EVT_PLAY_POS_CHANGED:
+		song_pos = strtoul(argv[4], NULL, 10);
+		memcpy(&reg.song_pos, &song_pos, sizeof(uint32_t));
+		break;
+
+	case BTRC_EVT_APP_SETTINGS_CHANGED:
+		haltest_error("not supported");
+		return;
+	}
+
+	EXEC(if_rc->register_notification_rsp, event_id, type, &reg);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_rc);
+
+	EXECV(if_rc->cleanup);
+	if_rc = NULL;
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHODCH(get_play_status_rsp,
+					"<play_status> <song_len> <song_pos>"),
+	STD_METHODCH(get_element_attr_rsp, "<num_attr> <attrs_id> <value>"),
+	STD_METHODCH(set_player_app_value_rsp, "<rsp_status>"),
+	STD_METHODCH(set_volume, "<volume>"),
+	STD_METHODCH(register_notification_rsp,
+			"<event_id> <type> <respective_data...>\n"
+			"BTRC_EVT_PLAY_STATUS_CHANGED <type> <play_status>\n"
+			"BTRC_EVT_TRACK_CHANGE <type> <track>\n"
+			"BTRC_EVT_TRACK_REACHED_END <type>\n"
+			"BTRC_EVT_TRACK_REACHED_START <type>\n"
+			"BTRC_EVT_PLAY_POS_CHANGED <type> <song_pos>\n"),
+	STD_METHOD(cleanup),
+	END_METHOD
+};
+
+const struct interface rc_if = {
+	.name = "rc",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-sco.c b/bluez/repo/android/client/if-sco.c
new file mode 100644
index 0000000..5a68ed5
--- /dev/null
+++ b/bluez/repo/android/client/if-sco.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <math.h>
+
+#include "if-main.h"
+#include "../hal-utils.h"
+
+audio_hw_device_t *if_audio_sco = NULL;
+static struct audio_stream_out *stream_out = NULL;
+static struct audio_stream_in *stream_in = NULL;
+
+static size_t buffer_size = 0;
+static size_t buffer_size_in = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+	STATE_STOPPED,
+	STATE_STOPPING,
+	STATE_PLAYING,
+	STATE_SUSPENDED,
+	STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+	DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+	DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+	DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+#if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
+	DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+#else
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+#endif
+	DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+	DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+	DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+	DELEMENT(AUDIO_FORMAT_DEFAULT),
+	DELEMENT(AUDIO_FORMAT_PCM),
+	DELEMENT(AUDIO_FORMAT_MP3),
+	DELEMENT(AUDIO_FORMAT_AMR_NB),
+	DELEMENT(AUDIO_FORMAT_AMR_WB),
+	DELEMENT(AUDIO_FORMAT_AAC),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+	DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+	DELEMENT(AUDIO_FORMAT_VORBIS),
+	DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+	DELEMENT(AUDIO_FORMAT_SUB_MASK),
+	DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+	DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+	int err;
+	const hw_module_t *module;
+	audio_hw_device_t *device;
+
+	err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module);
+	if (err) {
+		haltest_error("hw_get_module_by_class returned %d\n", err);
+		return;
+	}
+
+	err = audio_hw_device_open(module, &device);
+	if (err) {
+		haltest_error("audio_hw_device_open returned %d\n", err);
+		return;
+	}
+
+	if_audio_sco = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+	FILE *in = data;
+	return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+	size_t i = 0;
+	float volume = 0.5;
+	float *freq = data;
+	float f = 1;
+
+	if (freq)
+		f = *freq;
+
+	/* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+	for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+		if (sample_pos >= SAMPLERATE)
+			sample_pos = sample_pos % SAMPLERATE;
+
+		/* Use the same sample for both channels */
+		buffer[i++] = sample[sample_pos] * volume;
+		buffer[i++] = sample[sample_pos] * volume;
+
+		sample_pos += f;
+	}
+
+	return buffer_size;
+}
+
+static int feed_from_in(short *buffer, void *data)
+{
+	return stream_in->read(stream_in, buffer, buffer_size_in);
+}
+
+static void prepare_sample(void)
+{
+	int x;
+	double s;
+
+	haltest_info("Preparing audio sample...\n");
+
+	for (x = 0; x < SAMPLERATE; x++) {
+		/* prepare sinusoidal 1Hz sample */
+		s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+		s = sin(s);
+
+		/* remap <-1, 1> to signed 16bit PCM range */
+		sample[x] = s * 32767;
+	}
+
+	sample_pos = 0;
+}
+
+static void mono_to_stereo_pcm16(const int16_t *in, int16_t *out, size_t samples)
+{
+	size_t i;
+
+	for (i = 0; i < samples; i++) {
+		out[2 * i] = in[i];
+		out[2 * i + 1] = in[i];
+	}
+}
+
+static void *playback_thread(void *data)
+{
+	int (*filbuff_cb) (short*, void*);
+	short buffer[buffer_size / sizeof(short)];
+	short buffer_in[buffer_size_in / sizeof(short)];
+	size_t len = 0;
+	ssize_t w_len = 0;
+	FILE *in = data;
+	void *cb_data = NULL;
+	float freq = 440.0;
+
+	/* Use file or fall back to generator */
+	if (in) {
+		if (data == stream_in)
+			filbuff_cb = feed_from_in;
+		else {
+			filbuff_cb = feed_from_file;
+			cb_data = in;
+		}
+	} else {
+		prepare_sample();
+		filbuff_cb = feed_from_generator;
+		cb_data = &freq;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+
+	do {
+		pthread_mutex_lock(&state_mutex);
+
+		if (current_state == STATE_STOPPING) {
+			haltest_info("Detected stopping\n");
+			pthread_mutex_unlock(&state_mutex);
+			break;
+		} else if (current_state == STATE_SUSPENDED) {
+			pthread_mutex_unlock(&state_mutex);
+			usleep(500);
+			continue;
+		}
+
+		pthread_mutex_unlock(&state_mutex);
+
+		if (data && data == stream_in) {
+			int chan_in = popcount(stream_in->common.get_channels(&stream_in->common));
+			int chan_out = popcount(stream_out->common.get_channels(&stream_out->common));
+
+			len = filbuff_cb(buffer_in, cb_data);
+
+			if (chan_in == 1 && chan_out == 2) {
+				mono_to_stereo_pcm16(buffer_in,
+							buffer,
+							buffer_size_in / 2);
+			}
+		} else
+			len = filbuff_cb(buffer, cb_data);
+
+		pthread_mutex_lock(&outstream_mutex);
+		if (!stream_out) {
+			pthread_mutex_unlock(&outstream_mutex);
+			break;
+		}
+
+		w_len = stream_out->write(stream_out, buffer, buffer_size);
+		pthread_mutex_unlock(&outstream_mutex);
+	} while (len && w_len > 0);
+
+	if (in && data != stream_in)
+		fclose(in);
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done playing.\n");
+
+	return NULL;
+}
+
+static void write_stereo_pcm16(const short *input, size_t len, FILE *out)
+{
+	short sample[2];
+	size_t i;
+
+	for (i = 0; i < len / 2; i++) {
+		sample[0] = input[i];
+		sample[1] = input[i];
+
+		fwrite(sample, sizeof(sample), 1, out);
+	}
+}
+
+static void *read_thread(void *data)
+{
+	int (*filbuff_cb) (short*, void*) = feed_from_in;
+	short buffer[buffer_size_in / sizeof(short)];
+	ssize_t len = 0;
+	void *cb_data = NULL;
+	FILE *out = data;
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+
+	do {
+		pthread_mutex_lock(&state_mutex);
+
+		if (current_state == STATE_STOPPING) {
+			haltest_info("Detected stopping\n");
+			pthread_mutex_unlock(&state_mutex);
+			break;
+		} else if (current_state == STATE_SUSPENDED) {
+			pthread_mutex_unlock(&state_mutex);
+			usleep(500);
+			continue;
+		}
+
+		pthread_mutex_unlock(&state_mutex);
+
+		len = filbuff_cb(buffer, cb_data);
+		if (len < 0) {
+			haltest_error("Error receiving SCO data");
+			break;
+		}
+
+		haltest_info("Read %zd bytes\n", len);
+
+		if (out) {
+			write_stereo_pcm16(buffer, len, out);
+			haltest_info("Written %zd bytes\n", len * 2);
+		}
+	} while (len);
+
+	if (out)
+		fclose(out);
+
+	pthread_mutex_lock(&state_mutex);
+	current_state = STATE_STOPPED;
+	pthread_mutex_unlock(&state_mutex);
+
+	haltest_info("Done reading.\n");
+
+	return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+	const char *fname = NULL;
+	FILE *in = NULL;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("Invalid audio file path.\n");
+		haltest_info("Using sound generator.\n");
+	} else {
+		fname = argv[2];
+		in = fopen(fname, "r");
+
+		if (in == NULL) {
+			haltest_error("Cannot open file: %s\n", fname);
+			return;
+		}
+		haltest_info("Playing file: %s\n", fname);
+	}
+
+	if (buffer_size == 0) {
+		haltest_error("Invalid buffer size. Was stream_out opened?\n");
+		goto fail;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		goto fail;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+		haltest_error("Cannot create playback thread!\n");
+		goto fail;
+	}
+
+	return;
+fail:
+	if (in)
+		fclose(in);
+}
+
+static void loop_p(int argc, const char **argv)
+{
+	int chan_out, chan_in;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+	RETURN_IF_NULL(stream_in);
+
+	chan_out = popcount(stream_out->common.get_channels(&stream_out->common));
+	chan_in = popcount(stream_in->common.get_channels(&stream_in->common));
+
+	if (!buffer_size || !buffer_size_in) {
+		haltest_error("Invalid buffer sizes. Streams opened\n");
+		return;
+	}
+
+	if (buffer_size / chan_out != buffer_size_in / chan_in) {
+		haltest_error("read/write buffers differ, not supported\n");
+		return;
+	}
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (pthread_create(&play_thread, NULL, playback_thread,
+							stream_in) != 0)
+		haltest_error("Cannot create playback thread!\n");
+}
+
+static void read_p(int argc, const char **argv)
+{
+	const char *fname = NULL;
+	FILE *out = NULL;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_in);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		haltest_error("Already playing or stream suspended!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (argc < 3) {
+		haltest_error("Invalid audio file path.\n");
+		haltest_info("Using read and through away\n");
+	} else {
+		fname = argv[2];
+		out = fopen(fname, "w");
+		if (!out) {
+			haltest_error("Cannot open file: %s\n", fname);
+			return;
+		}
+
+		haltest_info("Reading to file: %s\n", fname);
+	}
+
+	if (!buffer_size_in) {
+		haltest_error("Invalid buffer size.\n");
+		goto failed;
+	}
+
+	if (pthread_create(&play_thread, NULL, read_thread, out) != 0) {
+		haltest_error("Cannot create playback thread!\n");
+		goto failed;
+	}
+
+	return;
+failed:
+	if (out)
+		fclose(out);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(play_thread);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+
+	if (stream_out) {
+		pthread_mutex_lock(&outstream_mutex);
+		stream_out->common.standby(&stream_out->common);
+		pthread_mutex_unlock(&outstream_mutex);
+	}
+
+	current_state = STATE_STOPPING;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_join(play_thread, NULL);
+	play_thread = 0;
+
+	haltest_info("Ended %s\n", __func__);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+	struct audio_config *config;
+	int err;
+
+	RETURN_IF_NULL(if_audio_sco);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_PLAYING) {
+		haltest_error("Already playing!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (argc < 3) {
+		haltest_info("No sampling rate specified. Use default conf\n");
+		config = NULL;
+	} else {
+		config = calloc(1, sizeof(struct audio_config));
+		if (!config)
+			return;
+
+		config->sample_rate = atoi(argv[2]);
+		config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+		config->format = AUDIO_FORMAT_PCM_16_BIT;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	err = if_audio_sco->open_output_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_OUT_ALL_SCO,
+						AUDIO_OUTPUT_FLAG_NONE,
+						config,
+						&stream_out, NULL);
+#else
+	err = if_audio_sco->open_output_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_OUT_ALL_SCO,
+						AUDIO_OUTPUT_FLAG_NONE,
+						config,
+						&stream_out);
+#endif
+	if (err < 0) {
+		haltest_error("open output stream returned %d\n", err);
+		goto failed;
+	}
+
+	buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+	if (buffer_size == 0)
+		haltest_error("Invalid buffer size received!\n");
+	else
+		haltest_info("Using buffer size: %zu\n", buffer_size);
+failed:
+	if (config)
+		free(config);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (play_thread)
+		stop_p(argc, argv);
+
+	if_audio_sco->close_output_stream(if_audio_sco, stream_out);
+
+	stream_out = NULL;
+	buffer_size = 0;
+}
+
+static void open_input_stream_p(int argc, const char **argv)
+{
+	struct audio_config *config;
+	int err;
+
+	RETURN_IF_NULL(if_audio_sco);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_PLAYING) {
+		haltest_error("Already playing!\n");
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	pthread_mutex_unlock(&state_mutex);
+
+	if (argc < 3) {
+		haltest_info("No sampling rate specified. Use default conf\n");
+		config = NULL;
+	} else {
+		config = calloc(1, sizeof(struct audio_config));
+		if (!config)
+			return;
+
+		config->sample_rate = atoi(argv[2]);
+		config->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+		config->format = AUDIO_FORMAT_PCM_16_BIT;
+	}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	err = if_audio_sco->open_input_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+						config,
+						&stream_in, 0, NULL, 0);
+#else
+	err = if_audio_sco->open_input_stream(if_audio_sco,
+						0,
+						AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+						config,
+						&stream_in);
+#endif
+	if (err < 0) {
+		haltest_error("open output stream returned %d\n", err);
+		goto failed;
+	}
+
+	buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common);
+	if (buffer_size_in == 0)
+		haltest_error("Invalid buffer size received!\n");
+	else
+		haltest_info("Using buffer size: %zu\n", buffer_size_in);
+failed:
+	if (config)
+		free(config);
+}
+
+static void close_input_stream_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_in);
+
+	if (play_thread)
+		stop_p(argc, argv);
+
+	if_audio_sco->close_input_stream(if_audio_sco, stream_in);
+
+	stream_in = NULL;
+	buffer_size_in = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+	int err;
+
+	RETURN_IF_NULL(if_audio_sco);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_STOPPED) {
+		pthread_mutex_unlock(&state_mutex);
+		close_output_stream_p(0, NULL);
+	} else {
+		pthread_mutex_unlock(&state_mutex);
+	}
+
+	err = audio_hw_device_close(if_audio_sco);
+	if (err < 0) {
+		haltest_error("audio_hw_device_close returned %d\n", err);
+		return;
+	}
+
+	if_audio_sco = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state != STATE_PLAYING) {
+		pthread_mutex_unlock(&state_mutex);
+		return;
+	}
+	current_state = STATE_SUSPENDED;
+	pthread_mutex_unlock(&state_mutex);
+
+	pthread_mutex_lock(&outstream_mutex);
+	stream_out->common.standby(&stream_out->common);
+	pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	pthread_mutex_lock(&state_mutex);
+	if (current_state == STATE_SUSPENDED)
+		current_state = STATE_PLAYING;
+	pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Output audio stream latency: %d\n",
+					stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current output buffer size: %zu\n",
+		stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+	audio_channel_mask_t channels;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	channels = stream_out->common.get_channels(&stream_out->common);
+
+	haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+	audio_format_t format;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	format = stream_out->common.get_format(&stream_out->common);
+
+	haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	haltest_info("Current sample rate: %d\n",
+		stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+	const char *keystr;
+
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_info("No keys given.\n");
+		keystr = "";
+	} else {
+		keystr = argv[2];
+	}
+
+	haltest_info("Current parameters: %s\n",
+			stream_out->common.get_parameters(&stream_out->common,
+								keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3) {
+		haltest_error("No key=value; pairs given.\n");
+		return;
+	}
+
+	stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+	RETURN_IF_NULL(stream_out);
+
+	if (argc < 3)
+		return;
+
+	stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+	RETURN_IF_NULL(if_audio_sco);
+
+	haltest_info("Init check result: %d\n",
+					if_audio_sco->init_check(if_audio_sco));
+}
+
+static struct method methods[] = {
+	STD_METHOD(init),
+	STD_METHOD(cleanup),
+	STD_METHODH(open_output_stream, "sample_rate"),
+	STD_METHOD(close_output_stream),
+	STD_METHODH(open_input_stream, "sampling rate"),
+	STD_METHOD(close_input_stream),
+	STD_METHODH(play, "<path to pcm file>"),
+	STD_METHOD(read),
+	STD_METHOD(loop),
+	STD_METHOD(stop),
+	STD_METHOD(suspend),
+	STD_METHOD(resume),
+	STD_METHOD(get_latency),
+	STD_METHOD(get_buffer_size),
+	STD_METHOD(get_channels),
+	STD_METHOD(get_format),
+	STD_METHOD(get_sample_rate),
+	STD_METHODH(get_parameters, "<closing>"),
+	STD_METHODH(set_parameters, "<closing=value>"),
+	STD_METHODH(set_sample_rate, "<sample rate>"),
+	STD_METHOD(init_check),
+	END_METHOD
+};
+
+const struct interface sco_if = {
+	.name = "sco",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/if-sock.c b/bluez/repo/android/client/if-sock.c
new file mode 100644
index 0000000..ee2c1e8
--- /dev/null
+++ b/bluez/repo/android/client/if-sock.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btsock_interface_t *if_sock = NULL;
+
+SINTMAP(btsock_type_t, -1, "(unknown)")
+	DELEMENT(BTSOCK_RFCOMM),
+	DELEMENT(BTSOCK_SCO),
+	DELEMENT(BTSOCK_L2CAP),
+ENDMAP
+
+#define MAX_LISTEN_FD 15
+static int listen_fd[MAX_LISTEN_FD];
+static int listen_fd_count;
+
+static const char * const uuids[] = {
+	"00001101", "00001105", "0000112f", NULL
+};
+
+/*
+ * This function reads data from file descriptor and
+ * prints it to the user
+ */
+static void receive_from_client(struct pollfd *pollfd)
+{
+	char buf[16];
+	/*
+	 * Buffer for lines:
+	 * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC  .12.....
+	 */
+	char outbuf[sizeof(buf) * 4 + 2];
+	int i;
+	int ret;
+
+	if (pollfd->revents & POLLHUP) {
+		haltest_error("Disconnected fd=%d\n", pollfd->fd);
+		poll_unregister_fd(pollfd->fd, receive_from_client);
+	} else if (pollfd->revents & POLLIN) {
+		haltest_info("receiving from client fd=%d\n", pollfd->fd);
+
+		do {
+			memset(outbuf, ' ', sizeof(outbuf));
+			outbuf[sizeof(outbuf) - 1] = 0;
+			ret = recv(pollfd->fd, buf, sizeof(buf), MSG_DONTWAIT);
+
+			for (i = 0; i < ret; ++i)
+				sprintf(outbuf + i * 3, "%02X ",
+							(unsigned) buf[i]);
+			outbuf[i * 3] = ' ';
+			for (i = 0; i < ret; ++i)
+				sprintf(outbuf + 48 + i, "%c",
+					(isprint(buf[i]) ? buf[i] : '.'));
+			if (ret > 0)
+				haltest_info("%s\n", outbuf);
+		} while (ret > 0);
+	} else {
+		/* For now disconnect on all other events */
+		haltest_error("Poll event %x\n", pollfd->revents);
+		poll_unregister_fd(pollfd->fd, receive_from_client);
+	}
+}
+
+/*
+ * This function read from fd socket information about
+ * connected socket
+ */
+static void receive_sock_connect_signal(struct pollfd *pollfd)
+{
+	sock_connect_signal_t cs;
+	char addr_str[MAX_ADDR_STR_LEN];
+
+	if (pollfd->revents & POLLIN) {
+		int ret;
+
+		poll_unregister_fd(pollfd->fd, receive_sock_connect_signal);
+		ret = read(pollfd->fd, &cs, sizeof(cs));
+		if (ret != sizeof(cs)) {
+			haltest_info("Read on connect return %d\n", ret);
+			return;
+		}
+
+		haltest_info("Connection to %s channel %d status=%d\n",
+				bt_bdaddr_t2str(&cs.bd_addr, addr_str),
+				cs.channel, cs.status);
+
+		if (cs.status == 0)
+			poll_register_fd(pollfd->fd, POLLIN,
+							receive_from_client);
+	}
+
+	if (pollfd->revents & POLLHUP) {
+		haltest_error("Disconnected fd=%d revents=0x%X\n", pollfd->fd,
+				pollfd->revents);
+		poll_unregister_fd(pollfd->fd, receive_sock_connect_signal);
+	}
+}
+
+/*
+ * This function read from fd socket information about
+ * incoming connection and starts monitoring new connection
+ * on file descriptor read from fd.
+ */
+static void read_accepted(int fd)
+{
+	int ret;
+	struct msghdr msg;
+	struct iovec iv;
+	char cmsgbuf[CMSG_SPACE(1)];
+	struct cmsghdr *cmsgptr;
+	sock_connect_signal_t cs;
+	int accepted_fd = -1;
+	char addr_str[MAX_ADDR_STR_LEN];
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&iv, 0, sizeof(iv));
+	memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+	iv.iov_base = &cs;
+	iv.iov_len = sizeof(cs);
+
+	msg.msg_iov = &iv;
+	msg.msg_iovlen = 1;
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+
+	do {
+		ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret < 16 ||
+		(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0)
+		haltest_error("Failed to accept connection\n");
+
+	for (cmsgptr = CMSG_FIRSTHDR(&msg);
+		cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+		int count;
+
+		if (cmsgptr->cmsg_level != SOL_SOCKET ||
+			cmsgptr->cmsg_type != SCM_RIGHTS)
+			continue;
+
+		memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd));
+		count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+
+		if (count != 1)
+			haltest_error("Failed to accept descriptors count=%d\n",
+									count);
+
+		break;
+	}
+
+	haltest_info("Incoming connection from %s channel %d status=%d fd=%d\n",
+					bt_bdaddr_t2str(&cs.bd_addr, addr_str),
+					cs.channel, cs.status, accepted_fd);
+	poll_register_fd(accepted_fd, POLLIN, receive_from_client);
+}
+
+/* handles incoming connections on socket */
+static void client_connected(struct pollfd *pollfd)
+{
+	haltest_info("client connected %x\n", pollfd->revents);
+
+	if (pollfd->revents & POLLHUP)
+		poll_unregister_fd(pollfd->fd, client_connected);
+	else if (pollfd->revents & POLLIN)
+		read_accepted(pollfd->fd);
+}
+
+/* listen */
+
+static void listen_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*user = TYPE_ENUM(btsock_type_t);
+		*enum_func = enum_defines;
+	} else if (argc == 5) {
+		*user = (void *) uuids;
+		*enum_func = enum_strings;
+	}
+}
+
+static void listen_p(int argc, const char **argv)
+{
+	btsock_type_t type;
+	const char *service_name;
+	bt_uuid_t service_uuid;
+	int channel;
+	int sock_fd = -1;
+	int flags;
+
+	RETURN_IF_NULL(if_sock);
+
+	/* Socket type */
+	if (argc < 3) {
+		haltest_error("No socket type specified\n");
+		return;
+	}
+	type = str2btsock_type_t(argv[2]);
+	if ((int) type == -1)
+		type = atoi(argv[2]);
+
+	/* service name */
+	if (argc < 4) {
+		haltest_error("No service name specified\n");
+		return;
+	}
+	service_name = argv[3];
+
+	/* uuid */
+	if (argc < 5) {
+		haltest_error("No uuid specified\n");
+		return;
+	}
+	str2bt_uuid_t(argv[4], &service_uuid);
+
+	/* channel */
+	channel = argc > 5 ? atoi(argv[5]) : 0;
+
+	/* flags */
+	flags = argc > 6 ? atoi(argv[6]) : 0;
+
+	if (listen_fd_count >= MAX_LISTEN_FD) {
+		haltest_error("Max (%d) listening sockets exceeded\n",
+							listen_fd_count);
+		return;
+	}
+	EXEC(if_sock->listen, type, service_name,
+				&service_uuid.uu[0], channel, &sock_fd, flags);
+	if (sock_fd > 0) {
+		int channel = 0;
+		int ret = read(sock_fd, &channel, 4);
+		if (ret != 4)
+			haltest_info("Read channel failed\n");
+		haltest_info("Channel returned from first read %d\n", channel);
+		listen_fd[listen_fd_count++] = sock_fd;
+		poll_register_fd(sock_fd, POLLIN, client_connected);
+	}
+}
+
+/* connect */
+
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
+								void **user)
+{
+	if (argc == 3) {
+		*enum_func = enum_devices;
+	} else if (argc == 4) {
+		*user = TYPE_ENUM(btsock_type_t);
+		*enum_func = enum_defines;
+	} else if (argc == 5) {
+		*user = (void *) uuids;
+		*enum_func = enum_strings;
+	}
+}
+
+static void connect_p(int argc, const char **argv)
+{
+	bt_bdaddr_t addr;
+	btsock_type_t type;
+	bt_uuid_t uuid;
+	int channel;
+	int sock_fd = -1;
+	int flags;
+
+	/* Address */
+	if (argc <= 2) {
+		haltest_error("No address specified\n");
+		return;
+	}
+	str2bt_bdaddr_t(argv[2], &addr);
+
+	/* Socket type */
+	if (argc <= 3) {
+		haltest_error("No socket type specified\n");
+		return;
+	}
+	type = str2btsock_type_t(argv[3]);
+	if ((int) type == -1)
+		type = atoi(argv[3]);
+
+	/* uuid */
+	if (argc <= 4) {
+		haltest_error("No uuid specified\n");
+		return;
+	}
+	str2bt_uuid_t(argv[4], &uuid);
+
+	/* channel */
+	if (argc <= 5) {
+		haltest_error("No channel specified\n");
+		return;
+	}
+	channel = atoi(argv[5]);
+
+	/* flags */
+	flags = argc <= 6 ? 0 : atoi(argv[6]);
+
+	RETURN_IF_NULL(if_sock);
+
+	EXEC(if_sock->connect, &addr, type, &uuid.uu[0], channel, &sock_fd,
+									flags);
+	if (sock_fd > 0) {
+		int channel = 0;
+		int ret = read(sock_fd, &channel, 4);
+
+		if (ret != 4)
+			haltest_info("Read channel failed\n");
+		haltest_info("Channel returned from first read %d\n", channel);
+		listen_fd[listen_fd_count++] = sock_fd;
+		poll_register_fd(sock_fd, POLLIN, receive_sock_connect_signal);
+	}
+}
+
+/* Methods available in btsock_interface_t */
+static struct method methods[] = {
+	STD_METHODCH(listen,
+			"<sock_type> <srvc_name> <uuid> [<channel>] [<flags>]"),
+	STD_METHODCH(connect,
+			"<addr> <sock_type> <uuid> <channel> [<flags>]"),
+	END_METHOD
+};
+
+const struct interface sock_if = {
+	.name = "socket",
+	.methods = methods
+};
diff --git a/bluez/repo/android/client/pollhandler.c b/bluez/repo/android/client/pollhandler.c
new file mode 100644
index 0000000..6160921
--- /dev/null
+++ b/bluez/repo/android/client/pollhandler.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "pollhandler.h"
+
+/*
+ * Code that allows to poll multiply file descriptors for events
+ * File descriptors can be added and removed at runtime
+ *
+ * Call poll_register_fd function first to add file descriptors to monitor
+ * Then call poll_dispatch_loop that will poll all registered file descriptors
+ * as long as they are not unregistered.
+ *
+ * When event happen on given fd appropriate user supplied handler is called
+ */
+
+/* Maximum number of files to monitor */
+#define MAX_OPEN_FD 10
+
+/* Storage for pollfd structures for monitored file descriptors */
+static struct pollfd fds[MAX_OPEN_FD];
+static poll_handler fds_handler[MAX_OPEN_FD];
+/* Number of registered file descriptors */
+static int fds_count = 0;
+
+/*
+ * Function polls file descriptor in loop and calls appropriate handler
+ * on event. Function returns when there is no more file descriptor to
+ * monitor
+ */
+void poll_dispatch_loop(void)
+{
+	while (fds_count > 0) {
+		int i;
+		int cur_fds_count = fds_count;
+		int ready = poll(fds, fds_count, 1000);
+
+		for (i = 0; i < fds_count && ready > 0; ++i) {
+			if (fds[i].revents == 0)
+				continue;
+
+			fds_handler[i](fds + i);
+			ready--;
+			/*
+			 * If handler was remove from table
+			 * just skip the rest and poll again
+			 * This is due to reordering of tables in
+			 * register/unregister functions
+			 */
+			if (cur_fds_count != fds_count)
+				break;
+		}
+	}
+}
+
+/*
+ * Registers file descriptor to be monitored for events (see man poll(2))
+ * for events.
+ *
+ * return non negative value on success
+ * -EMFILE when there are to much descriptors
+ */
+int poll_register_fd(int fd, short events, poll_handler ph)
+{
+	if (fds_count >= MAX_OPEN_FD)
+		return -EMFILE;
+
+	fds_handler[fds_count] = ph;
+	fds[fds_count].fd = fd;
+	fds[fds_count].events = events;
+	fds_count++;
+
+	return fds_count;
+}
+
+/*
+ * Unregisters file descriptor
+ * Both fd and ph must match previously registered data
+ *
+ * return 0 if unregister succeeded
+ * -EBADF if arguments do not match any register handler
+ */
+int poll_unregister_fd(int fd, poll_handler ph)
+{
+	int i;
+
+	for (i = 0; i < fds_count; ++i) {
+		if (fds_handler[i] == ph && fds[i].fd == fd) {
+			fds_count--;
+			if (i < fds_count) {
+				fds[i].fd = fds[fds_count].fd;
+				fds[i].events = fds[fds_count].events;
+				fds_handler[i] = fds_handler[fds_count];
+			}
+			return 0;
+		}
+	}
+	return -EBADF;
+}
diff --git a/bluez/repo/android/client/pollhandler.h b/bluez/repo/android/client/pollhandler.h
new file mode 100644
index 0000000..e2f22df
--- /dev/null
+++ b/bluez/repo/android/client/pollhandler.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <poll.h>
+
+/* Function to be called when there are event for some descriptor */
+typedef void (*poll_handler)(struct pollfd *pollfd);
+
+int poll_register_fd(int fd, short events, poll_handler ph);
+int poll_unregister_fd(int fd, poll_handler ph);
+
+void poll_dispatch_loop(void);
diff --git a/bluez/repo/android/client/tabcompletion.c b/bluez/repo/android/client/tabcompletion.c
new file mode 100644
index 0000000..bcca5fa
--- /dev/null
+++ b/bluez/repo/android/client/tabcompletion.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "if-main.h"
+#include "terminal.h"
+
+/* how many times tab was hit */
+static int tab_hit_count;
+
+typedef struct split_arg {
+	struct split_arg *next; /* next argument in buffer */
+	const char *origin; /* pointer to original argument */
+	char ntcopy[1]; /* null terminated copy of argument */
+} split_arg_t;
+
+/* function returns method of given name or NULL if not found */
+const struct method *get_interface_method(const char *iname,
+							const char *mname)
+{
+	const struct interface *iface = get_interface(iname);
+
+	if (iface == NULL)
+		return NULL;
+
+	return get_method(iface->methods, mname);
+}
+
+/* prints matching elements */
+static void print_matches(enum_func f, void *user, const char *prefix, int len)
+{
+	int i;
+	const char *enum_name;
+
+	putchar('\n');
+	for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
+		if (strncmp(enum_name, prefix, len) == 0)
+			printf("%s\t", enum_name);
+	}
+	putchar('\n');
+	terminal_draw_command_line();
+}
+
+/*
+ * This function splits command line into linked list of arguments.
+ * line_buffer - pointer to input command line
+ * size - size of command line to parse
+ * buf - output buffer to keep split arguments list
+ * buf_size_in_bytes - size of buf
+ */
+static int split_command(const char *line_buffer, int size, split_arg_t *buf,
+							int buf_size_in_bytes)
+{
+	split_arg_t *prev = NULL;
+	split_arg_t *arg = buf;
+	int argc = 0;
+	const char *p = line_buffer;
+	const char *e = p + (size > 0 ? size : (int) strlen(p));
+	int len;
+
+	do {
+		while (p < e && isspace(*p))
+			p++;
+		arg->origin = p;
+		arg->next = NULL;
+		while (p < e && !isspace(*p))
+			p++;
+		len = p - arg->origin;
+		if (&arg->ntcopy[0] + len + 1 >
+			(const char *) buf + buf_size_in_bytes)
+			break;
+		strncpy(arg->ntcopy, arg->origin, len);
+		arg->ntcopy[len] = 0;
+		if (prev != NULL)
+			prev->next = arg;
+		prev = arg;
+		arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
+		argc++;
+	} while (p < e);
+
+	return argc;
+}
+
+/* Function to enumerate method names */
+static const char *methods_name(void *v, int i)
+{
+	const struct interface *iface = v;
+
+	return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
+}
+
+struct command_completion_args;
+typedef void (*short_help)(struct command_completion_args *args);
+
+struct command_completion_args {
+	const split_arg_t *arg; /* list of arguments */
+	const char *typed; /* last typed element */
+	enum_func func; /* enumerating function */
+	void *user; /* argument to enumerating function */
+	short_help help; /* help function */
+	const char *user_help; /* additional data (used by short_help) */
+};
+
+/* complete command line */
+static void tab_completion(struct command_completion_args *args)
+{
+	const char *name = args->typed;
+	const int len = strlen(name);
+	int i;
+	int j;
+	char prefix[128] = {0};
+	int prefix_len = 0;
+	int count = 0;
+	const char *enum_name;
+
+	for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
+		/* prefix does not match */
+		if (strncmp(enum_name, name, len) != 0)
+			continue;
+
+		/* prefix matches first time */
+		if (count++ == 0) {
+			strcpy(prefix, enum_name);
+			prefix_len = strlen(prefix);
+			continue;
+		}
+
+		/* Prefix matches next time reduce prefix to common part */
+		for (j = 0; prefix[j] != 0
+			&& prefix[j] == enum_name[j];)
+			++j;
+		prefix_len = j;
+		prefix[j] = 0;
+	}
+
+	if (count == 0) {
+		/* no matches */
+		if (args->help != NULL)
+			args->help(args);
+		tab_hit_count = 0;
+		return;
+	}
+
+	/* len == prefix_len => nothing new was added */
+	if (len == prefix_len) {
+		if (count != 1) {
+			if (tab_hit_count == 1) {
+				putchar('\a');
+			} else if (tab_hit_count == 2 ||
+					args->help == NULL) {
+				print_matches(args->func,
+						args->user, name, len);
+			} else {
+				args->help(args);
+				tab_hit_count = 1;
+			}
+		} else if (count == 1) {
+			/* nothing to add, exact match add space */
+			terminal_insert_into_command_line(" ");
+		}
+	} else {
+		/* new chars can be added from some interface name(s) */
+		if (count == 1) {
+			/* exact match, add space */
+			prefix[prefix_len++] = ' ';
+			prefix[prefix_len] = '\0';
+		}
+
+		terminal_insert_into_command_line(prefix + len);
+		tab_hit_count = 0;
+	}
+}
+
+/* interface completion */
+static void command_completion(split_arg_t *arg)
+{
+	struct command_completion_args args = {
+		.arg = arg,
+		.typed = arg->ntcopy,
+		.func = command_name
+	};
+
+	tab_completion(&args);
+}
+
+/* method completion */
+static void method_completion(const struct interface *iface, split_arg_t *arg)
+{
+	struct command_completion_args args = {
+		.arg = arg,
+		.typed = arg->next->ntcopy,
+		.func = methods_name,
+		.user = (void *) iface
+	};
+
+	if (iface == NULL)
+		return;
+
+	tab_completion(&args);
+}
+
+static const char *bold = "\x1b[1m";
+static const char *normal = "\x1b[0m";
+
+static bool find_nth_argument(const char *str, int n, const char **s,
+								const char **e)
+{
+	const char *p = str;
+	int argc = 0;
+	*e = NULL;
+
+	while (p != NULL && *p != 0) {
+
+		while (isspace(*p))
+			++p;
+
+		if (n == argc)
+			*s = p;
+
+		if (*p == '[') {
+			p = strchr(p, ']');
+			if (p != NULL)
+				*e = ++p;
+		} else if (*p == '<') {
+			p = strchr(p, '>');
+			if (p != NULL)
+				*e = ++p;
+		} else {
+			*e = strchr(p, ' ');
+			if (*e == NULL)
+				*e = p + strlen(p);
+			p = *e;
+		}
+
+		if (n == argc)
+			break;
+
+		argc++;
+		*e = NULL;
+	}
+	return *e != NULL;
+}
+
+/* prints short help on method for interface */
+static void method_help(struct command_completion_args *args)
+{
+	int argc;
+	const split_arg_t *arg = args->arg;
+	const char *sb = NULL;
+	const char *eb = NULL;
+	const char *arg1 = "";
+	int arg1_size = 0; /* size of method field (for methods > 0) */
+
+	if (args->user_help == NULL)
+		return;
+
+	for (argc = 0; arg != NULL; argc++)
+		arg = arg->next;
+
+	/* Check if this is method from interface */
+	if (get_command(args->arg->ntcopy) == NULL) {
+		/* if so help is missing interface and method name */
+		arg1 = args->arg->next->ntcopy;
+		arg1_size = strlen(arg1) + 1;
+	}
+
+	find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
+								&sb, &eb);
+
+	if (eb != NULL)
+		haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
+				arg1_size, arg1, (int) (sb - args->user_help),
+				args->user_help, bold, (int) (eb - sb),
+				sb, normal, eb);
+	else
+		haltest_info("%s %-*s%s\n", args->arg->ntcopy,
+			arg1_size, arg1, args->user_help);
+}
+
+/* So we have empty enumeration */
+static const char *return_null(void *user, int i)
+{
+	return NULL;
+}
+
+/*
+ * parameter completion function
+ * argc - number of elements in arg list
+ * arg - list of arguments
+ * method - method to get completion from (can be NULL)
+ */
+static void param_completion(int argc, const split_arg_t *arg,
+					const struct method *method, int hlpix)
+{
+	int i;
+	const char *argv[argc];
+	const split_arg_t *tmp = arg;
+	struct command_completion_args args = {
+		.arg = arg,
+		.func = return_null
+	};
+
+	/* prepare standard argv from arg */
+	for (i = 0; i < argc; ++i) {
+		argv[i] = tmp->ntcopy;
+		tmp = tmp->next;
+	}
+
+	if (method != NULL && method->complete != NULL) {
+		/* ask method for completion function */
+		method->complete(argc, argv, &args.func, &args.user);
+	}
+
+	/* If method provided enumeration function call try to complete */
+	if (args.func != NULL) {
+		args.typed = argv[argc - 1];
+		args.help = method_help;
+		args.user_help = method ? method->help : NULL;
+
+		tab_completion(&args);
+	}
+}
+
+/*
+ * This method gets called when user tapped tab key.
+ * line - points to command line
+ * len - size of line that should be used for completions. This should be
+ *   cursor position during tab hit.
+ */
+void process_tab(const char *line, int len)
+{
+	int argc;
+	static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
+	const struct method *method;
+
+	argc = split_command(line, len, buf, sizeof(buf));
+	tab_hit_count++;
+
+	if (argc == 0)
+		return;
+
+	if (argc == 1) {
+		command_completion(buf);
+		return;
+	}
+
+	method = get_command(buf[0].ntcopy);
+	if (method != NULL) {
+		param_completion(argc, buf, method, 1);
+	} else if (argc == 2) {
+		method_completion(get_interface(buf[0].ntcopy), buf);
+	} else {
+		/* Find method for <interface, name> pair */
+		method = get_interface_method(buf[0].ntcopy,
+							buf[0].next->ntcopy);
+		param_completion(argc, buf, method, 2);
+	}
+}
diff --git a/bluez/repo/android/client/terminal.c b/bluez/repo/android/client/terminal.c
new file mode 100644
index 0000000..f7b56de
--- /dev/null
+++ b/bluez/repo/android/client/terminal.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <stdlib.h>
+
+#include "terminal.h"
+#include "history.h"
+
+/*
+ * Character sequences recognized by code in this file
+ * Leading ESC 0x1B is not included
+ */
+#define SEQ_INSERT "[2~"
+#define SEQ_DELETE "[3~"
+#define SEQ_HOME   "OH"
+#define SEQ_END    "OF"
+#define SEQ_PGUP   "[5~"
+#define SEQ_PGDOWN "[6~"
+#define SEQ_LEFT   "[D"
+#define SEQ_RIGHT  "[C"
+#define SEQ_UP     "[A"
+#define SEQ_DOWN   "[B"
+#define SEQ_STAB   "[Z"
+#define SEQ_M_n    "n"
+#define SEQ_M_p    "p"
+#define SEQ_CLEFT  "[1;5D"
+#define SEQ_CRIGHT "[1;5C"
+#define SEQ_CUP    "[1;5A"
+#define SEQ_CDOWN  "[1;5B"
+#define SEQ_SLEFT  "[1;2D"
+#define SEQ_SRIGHT "[1;2C"
+#define SEQ_SUP    "[1;2A"
+#define SEQ_SDOWN  "[1;2B"
+#define SEQ_MLEFT  "[1;3D"
+#define SEQ_MRIGHT "[1;3C"
+#define SEQ_MUP    "[1;3A"
+#define SEQ_MDOWN  "[1;3B"
+
+#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k }
+struct ansii_sequence {
+	int code;
+	const char *sequence;
+};
+
+/* Table connects single int key codes with character sequences */
+static const struct ansii_sequence ansii_sequnces[] = {
+	KEY_SEQUENCE(INSERT),
+	KEY_SEQUENCE(DELETE),
+	KEY_SEQUENCE(HOME),
+	KEY_SEQUENCE(END),
+	KEY_SEQUENCE(PGUP),
+	KEY_SEQUENCE(PGDOWN),
+	KEY_SEQUENCE(LEFT),
+	KEY_SEQUENCE(RIGHT),
+	KEY_SEQUENCE(UP),
+	KEY_SEQUENCE(DOWN),
+	KEY_SEQUENCE(CLEFT),
+	KEY_SEQUENCE(CRIGHT),
+	KEY_SEQUENCE(CUP),
+	KEY_SEQUENCE(CDOWN),
+	KEY_SEQUENCE(SLEFT),
+	KEY_SEQUENCE(SRIGHT),
+	KEY_SEQUENCE(SUP),
+	KEY_SEQUENCE(SDOWN),
+	KEY_SEQUENCE(MLEFT),
+	KEY_SEQUENCE(MRIGHT),
+	KEY_SEQUENCE(MUP),
+	KEY_SEQUENCE(MDOWN),
+	KEY_SEQUENCE(STAB),
+	KEY_SEQUENCE(M_p),
+	KEY_SEQUENCE(M_n),
+	{ 0, NULL }
+};
+
+#define KEY_SEQUNCE_NOT_FINISHED -1
+#define KEY_C_C 3
+#define KEY_C_D 4
+#define KEY_C_L 12
+
+#define isseqence(c) ((c) == 0x1B)
+
+/*
+ * Number of characters that consist of ANSI sequence
+ * Should not be less then longest string in ansi_sequences
+ */
+#define MAX_ASCII_SEQUENCE 10
+
+static char current_sequence[MAX_ASCII_SEQUENCE];
+static int current_sequence_len = -1;
+
+/* single line typed by user goes here */
+static char line_buf[LINE_BUF_MAX];
+/* index of cursor in input line */
+static int line_buf_ix = 0;
+/* current length of input line */
+static int line_len = 0;
+
+/* line index used for fetching lines from history */
+static int line_index = 0;
+
+static char prompt_buf[10] = "> ";
+static const char *const noprompt = "";
+static const char *current_prompt = prompt_buf;
+static const char *prompt = prompt_buf;
+/*
+ * Moves cursor to right or left
+ *
+ * n - positive - moves cursor right
+ * n - negative - moves cursor left
+ */
+static void terminal_move_cursor(int n)
+{
+	if (n < 0) {
+		for (; n < 0; n++)
+			putchar('\b');
+	} else if (n > 0) {
+		printf("%*s", n, line_buf + line_buf_ix);
+	}
+}
+
+/* Draw command line */
+void terminal_draw_command_line(void)
+{
+	/*
+	 * this needs to be checked here since line_buf is not cleared
+	 * before parsing event though line_len and line_buf_ix are
+	 */
+	if (line_len > 0)
+		printf("%s%s", prompt, line_buf);
+	else
+		printf("%s", prompt);
+
+	/* move cursor to it's place */
+	terminal_move_cursor(line_buf_ix - line_len);
+}
+
+/* inserts string into command line at cursor position */
+void terminal_insert_into_command_line(const char *p)
+{
+	int len = strlen(p);
+
+	if (line_len == line_buf_ix) {
+		strcat(line_buf, p);
+		printf("%s", p);
+		line_len = line_len + len;
+		line_buf_ix = line_len;
+	} else {
+		memmove(line_buf + line_buf_ix + len,
+			line_buf + line_buf_ix, line_len - line_buf_ix + 1);
+		memmove(line_buf + line_buf_ix, p, len);
+		printf("%s", line_buf + line_buf_ix);
+		line_buf_ix += len;
+		line_len += len;
+		terminal_move_cursor(line_buf_ix - line_len);
+	}
+}
+
+/* Prints string and redraws command line */
+int terminal_print(const char *format, ...)
+{
+	va_list args;
+	int ret;
+
+	va_start(args, format);
+
+	ret = terminal_vprint(format, args);
+
+	va_end(args);
+	return ret;
+}
+
+/* Prints string and redraws command line */
+int terminal_vprint(const char *format, va_list args)
+{
+	int ret;
+
+	printf("\r%*s\r", (int) line_len + 1, " ");
+
+	ret = vprintf(format, args);
+
+	terminal_draw_command_line();
+
+	fflush(stdout);
+
+	return ret;
+}
+
+/*
+ * Call this when text in line_buf was changed
+ * and line needs to be redrawn
+ */
+static void terminal_line_replaced(void)
+{
+	int len = strlen(line_buf);
+
+	/* line is shorter that previous */
+	if (len < line_len) {
+		/* if new line is shorter move cursor to end of new end */
+		while (line_buf_ix > len) {
+			putchar('\b');
+			line_buf_ix--;
+		}
+
+		/* If cursor was not at the end, move it to the end */
+		if (line_buf_ix < line_len)
+			printf("%.*s", line_len - line_buf_ix,
+					line_buf + line_buf_ix);
+		/* over write end of previous line */
+		while (line_len >= len++)
+			putchar(' ');
+	}
+
+	/* draw new line */
+	printf("\r%s%s", prompt, line_buf);
+	/* set up indexes to new line */
+	line_len = strlen(line_buf);
+	line_buf_ix = line_len;
+	fflush(stdout);
+}
+
+static void terminal_clear_line(void)
+{
+	line_buf[0] = '\0';
+	terminal_line_replaced();
+}
+
+static void terminal_clear_screen(void)
+{
+	line_buf[0] = '\0';
+	line_buf_ix = 0;
+	line_len = 0;
+
+	printf("\x1b[2J\x1b[1;1H%s", prompt);
+}
+
+static void terminal_delete_char(void)
+{
+	/* delete character under cursor if not at the very end */
+	if (line_buf_ix >= line_len)
+		return;
+	/*
+	 * Prepare buffer with one character missing
+	 * trailing 0 is moved
+	 */
+	line_len--;
+	memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1,
+						line_len - line_buf_ix + 1);
+	/* print rest of line from current cursor position */
+	printf("%s \b", line_buf + line_buf_ix);
+	/* move back cursor */
+	terminal_move_cursor(line_buf_ix - line_len);
+}
+
+/*
+ * Function tries to replace current line with specified line in history
+ * new_line_index - new line to show, -1 to show oldest
+ */
+static void terminal_get_line_from_history(int new_line_index)
+{
+	new_line_index = history_get_line(new_line_index,
+						line_buf, LINE_BUF_MAX);
+
+	if (new_line_index >= 0) {
+		terminal_line_replaced();
+		line_index = new_line_index;
+	}
+}
+
+/*
+ * Function searches history back or forward for command line that starts
+ * with characters up to cursor position
+ *
+ * back - true - searches backward
+ * back - false - searches forward (more recent commands)
+ */
+static void terminal_match_hitory(bool back)
+{
+	char buf[line_buf_ix + 1];
+	int line;
+	int matching_line = -1;
+	int dir = back ? 1 : -1;
+
+	line = line_index + dir;
+	while (matching_line == -1 && line >= 0) {
+		int new_line_index;
+
+		new_line_index = history_get_line(line, buf, line_buf_ix + 1);
+		if (new_line_index < 0)
+			break;
+
+		if (0 == strncmp(line_buf, buf, line_buf_ix))
+			matching_line = line;
+		line += dir;
+	}
+
+	if (matching_line >= 0) {
+		int pos = line_buf_ix;
+		terminal_get_line_from_history(matching_line);
+		/* move back to cursor position to original place */
+		line_buf_ix = pos;
+		terminal_move_cursor(pos - line_len);
+	}
+}
+
+/*
+ * Converts terminal character sequences to single value representing
+ * keyboard keys
+ */
+static int terminal_convert_sequence(int c)
+{
+	int i;
+
+	/* Not in sequence yet? */
+	if (current_sequence_len == -1) {
+		/* Is ansi sequence detected by 0x1B ? */
+		if (isseqence(c)) {
+			current_sequence_len++;
+			return KEY_SEQUNCE_NOT_FINISHED;
+		}
+
+		return c;
+	}
+
+	/* Inside sequence */
+	current_sequence[current_sequence_len++] = c;
+	current_sequence[current_sequence_len] = '\0';
+	for (i = 0; ansii_sequnces[i].code; ++i) {
+		/* Matches so far? */
+		if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence,
+							current_sequence_len))
+			continue;
+
+		/* Matches as a whole? */
+		if (ansii_sequnces[i].sequence[current_sequence_len] == 0) {
+			current_sequence_len = -1;
+			return ansii_sequnces[i].code;
+		}
+
+		/* partial match (not whole sequence yet) */
+		return KEY_SEQUNCE_NOT_FINISHED;
+	}
+
+	terminal_print("ansi char 0x%X %c\n", c);
+	/*
+	 * Sequence does not match
+	 * mark that no in sequence any more, return char
+	 */
+	current_sequence_len = -1;
+	return c;
+}
+
+typedef void (*terminal_action)(int c, line_callback process_line);
+
+#define TERMINAL_ACTION(n) \
+	static void n(int c, void (*process_line)(char *line))
+
+TERMINAL_ACTION(terminal_action_null)
+{
+}
+
+/* Mapping between keys and function */
+typedef struct {
+	int key;
+	terminal_action func;
+} KeyAction;
+
+int action_keys[] = {
+	KEY_SEQUNCE_NOT_FINISHED,
+	KEY_LEFT,
+	KEY_RIGHT,
+	KEY_HOME,
+	KEY_END,
+	KEY_DELETE,
+	KEY_CLEFT,
+	KEY_CRIGHT,
+	KEY_SUP,
+	KEY_SDOWN,
+	KEY_UP,
+	KEY_DOWN,
+	KEY_BACKSPACE,
+	KEY_INSERT,
+	KEY_PGUP,
+	KEY_PGDOWN,
+	KEY_CUP,
+	KEY_CDOWN,
+	KEY_SLEFT,
+	KEY_SRIGHT,
+	KEY_MLEFT,
+	KEY_MRIGHT,
+	KEY_MUP,
+	KEY_MDOWN,
+	KEY_STAB,
+	KEY_M_n,
+	KEY_M_p,
+	KEY_C_C,
+	KEY_C_D,
+	KEY_C_L,
+	'\t',
+	'\r',
+	'\n',
+};
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/*
+ * current_actions holds all recognizable kes and actions for them
+ * additional element (index 0) is used for default action
+ */
+static KeyAction current_actions[NELEM(action_keys) + 1];
+
+/* KeyAction comparator by key, for qsort and bsearch */
+static int KeyActionKeyCompare(const void *a, const void *b)
+{
+	return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key;
+}
+
+/* Find action by key, NULL if no action for this key */
+static KeyAction *terminal_get_action(int key)
+{
+	KeyAction a = { .key = key };
+
+	return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a),
+							KeyActionKeyCompare);
+}
+
+/* Sets new set of actions to use */
+static void terminal_set_actions(const KeyAction *actions)
+{
+	int i;
+
+	/* Make map with empty function for every key */
+	for (i = 0; i < NELEM(action_keys); ++i) {
+		/*
+		 * + 1 due to 0 index reserved for default action that is
+		 * called for non mapped key
+		 */
+		current_actions[i + 1].key = action_keys[i];
+		current_actions[i + 1].func = terminal_action_null;
+	}
+
+	/* Sort action from 1 (index 0 - default action) */
+	qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction),
+							KeyActionKeyCompare);
+	/* Set default action (first in array) */
+	current_actions[0] = *actions++;
+
+	/* Copy rest of actions into their places */
+	for (; actions->key; ++actions) {
+		KeyAction *place = terminal_get_action(actions->key);
+
+		if (place)
+			place->func = actions->func;
+	}
+}
+
+TERMINAL_ACTION(terminal_action_left)
+{
+	/* if not at the beginning move to previous character */
+	if (line_buf_ix <= 0)
+		return;
+	line_buf_ix--;
+	terminal_move_cursor(-1);
+}
+
+TERMINAL_ACTION(terminal_action_right)
+{
+	/*
+	 * If not at the end, just print current character
+	 * and modify position
+	 */
+	if (line_buf_ix < line_len)
+		putchar(line_buf[line_buf_ix++]);
+}
+
+TERMINAL_ACTION(terminal_action_home)
+{
+	/* move to beginning of line and update position */
+	printf("\r%s", prompt);
+	line_buf_ix = 0;
+}
+
+TERMINAL_ACTION(terminal_action_end)
+{
+	/* if not at the end of line */
+	if (line_buf_ix < line_len) {
+		/* print everything from cursor */
+		printf("%s", line_buf + line_buf_ix);
+		/* just modify current position */
+		line_buf_ix = line_len;
+	}
+}
+
+TERMINAL_ACTION(terminal_action_del)
+{
+	terminal_delete_char();
+}
+
+TERMINAL_ACTION(terminal_action_word_left)
+{
+	int old_pos;
+	/*
+	 * Move by word left
+	 *
+	 * Are we at the beginning of line?
+	 */
+	if (line_buf_ix <= 0)
+		return;
+
+	old_pos = line_buf_ix;
+	line_buf_ix--;
+	/* skip spaces left */
+	while (line_buf_ix && isspace(line_buf[line_buf_ix]))
+		line_buf_ix--;
+
+	/* skip all non spaces to the left */
+	while (line_buf_ix > 0 &&
+			!isspace(line_buf[line_buf_ix - 1]))
+		line_buf_ix--;
+
+	/* move cursor to new position */
+	terminal_move_cursor(line_buf_ix - old_pos);
+}
+
+TERMINAL_ACTION(terminal_action_word_right)
+{
+	int old_pos;
+	/*
+	 * Move by word right
+	 *
+	 * are we at the end of line?
+	 */
+	if (line_buf_ix >= line_len)
+		return;
+
+	old_pos = line_buf_ix;
+	/* skip all spaces */
+	while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix]))
+		line_buf_ix++;
+
+	/* skip all non spaces */
+	while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix]))
+		line_buf_ix++;
+	/*
+	 * Move cursor to right by printing text
+	 * between old cursor and new
+	 */
+	if (line_buf_ix > old_pos)
+		printf("%.*s", (int) (line_buf_ix - old_pos),
+							line_buf + old_pos);
+}
+
+TERMINAL_ACTION(terminal_action_history_begin)
+{
+	terminal_get_line_from_history(-1);
+}
+
+TERMINAL_ACTION(terminal_action_history_end)
+{
+	if (line_index > 0)
+		terminal_get_line_from_history(0);
+}
+
+TERMINAL_ACTION(terminal_action_history_up)
+{
+	terminal_get_line_from_history(line_index + 1);
+}
+
+TERMINAL_ACTION(terminal_action_history_down)
+{
+	if (line_index > 0)
+		terminal_get_line_from_history(line_index - 1);
+}
+
+TERMINAL_ACTION(terminal_action_tab)
+{
+	/* tab processing */
+	process_tab(line_buf, line_buf_ix);
+}
+
+
+TERMINAL_ACTION(terminal_action_backspace)
+{
+	if (line_buf_ix <= 0)
+		return;
+
+	if (line_buf_ix == line_len) {
+		printf("\b \b");
+		line_len = --line_buf_ix;
+		line_buf[line_len] = 0;
+	} else {
+		putchar('\b');
+		line_buf_ix--;
+		line_len--;
+		memmove(line_buf + line_buf_ix,
+				line_buf + line_buf_ix + 1,
+				line_len - line_buf_ix + 1);
+		printf("%s \b", line_buf + line_buf_ix);
+		terminal_move_cursor(line_buf_ix - line_len);
+	}
+}
+
+TERMINAL_ACTION(terminal_action_find_history_forward)
+{
+	/* Search history forward */
+	terminal_match_hitory(false);
+}
+
+TERMINAL_ACTION(terminal_action_find_history_backward)
+{
+	/* Search history forward */
+	terminal_match_hitory(true);
+}
+
+TERMINAL_ACTION(terminal_action_ctrl_c)
+{
+	terminal_clear_line();
+}
+
+TERMINAL_ACTION(terminal_action_ctrl_d)
+{
+	if (line_len > 0) {
+		terminal_delete_char();
+	} else  {
+		puts("");
+		exit(0);
+	}
+}
+
+TERMINAL_ACTION(terminal_action_clear_screen)
+{
+	terminal_clear_screen();
+}
+
+TERMINAL_ACTION(terminal_action_enter)
+{
+	/*
+	 * On new line add line to history
+	 * forget history position
+	 */
+	history_add_line(line_buf);
+	line_len = 0;
+	line_buf_ix = 0;
+	line_index = -1;
+	/* print new line */
+	putchar(c);
+	prompt = noprompt;
+	process_line(line_buf);
+	/* clear current line */
+	line_buf[0] = '\0';
+	prompt = current_prompt;
+	printf("%s", prompt);
+}
+
+TERMINAL_ACTION(terminal_action_default)
+{
+	char str[2] = { c, 0 };
+
+	if (!isprint(c))
+		/*
+		 * TODO: remove this print once all meaningful sequences
+		 * are identified
+		 */
+		printf("char-0x%02x\n", c);
+	else if (line_buf_ix < LINE_BUF_MAX - 1)
+		terminal_insert_into_command_line(str);
+}
+
+/* Callback to call when user hit enter during prompt for */
+static line_callback prompt_callback;
+
+static KeyAction normal_actions[] = {
+	{ 0, terminal_action_default },
+	{ KEY_LEFT, terminal_action_left },
+	{ KEY_RIGHT, terminal_action_right },
+	{ KEY_HOME, terminal_action_home },
+	{ KEY_END, terminal_action_end },
+	{ KEY_DELETE, terminal_action_del },
+	{ KEY_CLEFT, terminal_action_word_left },
+	{ KEY_CRIGHT, terminal_action_word_right },
+	{ KEY_SUP, terminal_action_history_begin },
+	{ KEY_SDOWN, terminal_action_history_end },
+	{ KEY_UP, terminal_action_history_up },
+	{ KEY_DOWN, terminal_action_history_down },
+	{ '\t', terminal_action_tab },
+	{ KEY_BACKSPACE, terminal_action_backspace },
+	{ KEY_M_n, terminal_action_find_history_forward },
+	{ KEY_M_p, terminal_action_find_history_backward },
+	{ KEY_C_C, terminal_action_ctrl_c },
+	{ KEY_C_D, terminal_action_ctrl_d },
+	{ KEY_C_L, terminal_action_clear_screen },
+	{ '\r', terminal_action_enter },
+	{ '\n', terminal_action_enter },
+	{ 0, NULL },
+};
+
+TERMINAL_ACTION(terminal_action_answer)
+{
+	putchar(c);
+
+	terminal_set_actions(normal_actions);
+	/* Restore default prompt */
+	current_prompt = prompt_buf;
+
+	/* No prompt for prints */
+	prompt = noprompt;
+	line_buf_ix = 0;
+	line_len = 0;
+	/* Call user function with what was typed */
+	prompt_callback(line_buf);
+
+	line_buf[0] = 0;
+	/* promot_callback could change current_prompt */
+	prompt = current_prompt;
+
+	printf("%s", prompt);
+}
+
+TERMINAL_ACTION(terminal_action_prompt_ctrl_c)
+{
+	printf("^C\n");
+	line_buf_ix = 0;
+	line_len = 0;
+	line_buf[0] = 0;
+
+	current_prompt = prompt_buf;
+	prompt = current_prompt;
+	terminal_set_actions(normal_actions);
+
+	printf("%s", prompt);
+}
+
+static KeyAction prompt_actions[] = {
+	{ 0, terminal_action_default },
+	{ KEY_LEFT, terminal_action_left },
+	{ KEY_RIGHT, terminal_action_right },
+	{ KEY_HOME, terminal_action_home },
+	{ KEY_END, terminal_action_end },
+	{ KEY_DELETE, terminal_action_del },
+	{ KEY_CLEFT, terminal_action_word_left },
+	{ KEY_CRIGHT, terminal_action_word_right },
+	{ KEY_BACKSPACE, terminal_action_backspace },
+	{ KEY_C_C, terminal_action_prompt_ctrl_c },
+	{ KEY_C_D, terminal_action_ctrl_d },
+	{ '\r', terminal_action_answer },
+	{ '\n', terminal_action_answer },
+	{ 0, NULL },
+};
+
+void terminal_process_char(int c, line_callback process_line)
+{
+	KeyAction *a;
+
+	c = terminal_convert_sequence(c);
+
+	/* Get action for this key */
+	a = terminal_get_action(c);
+
+	/* No action found, get default one */
+	if (a == NULL)
+		a = &current_actions[0];
+
+	a->func(c, process_line);
+	fflush(stdout);
+}
+
+void terminal_prompt_for(const char *s, line_callback process_line)
+{
+	current_prompt = s;
+	if (prompt != noprompt) {
+		prompt = s;
+		terminal_clear_line();
+	}
+	prompt_callback = process_line;
+	terminal_set_actions(prompt_actions);
+}
+
+static struct termios origianl_tios;
+
+static void terminal_cleanup(void)
+{
+	tcsetattr(0, TCSANOW, &origianl_tios);
+}
+
+void terminal_setup(void)
+{
+	struct termios tios;
+
+	terminal_set_actions(normal_actions);
+
+	tcgetattr(0, &origianl_tios);
+	tios = origianl_tios;
+
+	/*
+	 * Turn off echo since all editing is done by hand,
+	 * Ctrl-c handled internally
+	 */
+	tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK);
+	tcsetattr(0, TCSANOW, &tios);
+
+	/* Restore terminal at exit */
+	atexit(terminal_cleanup);
+
+	printf("%s", prompt);
+	fflush(stdout);
+}
diff --git a/bluez/repo/android/client/terminal.h b/bluez/repo/android/client/terminal.h
new file mode 100644
index 0000000..0e63936
--- /dev/null
+++ b/bluez/repo/android/client/terminal.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdarg.h>
+
+/* size of supported line */
+#define LINE_BUF_MAX 1024
+
+enum key_codes {
+	KEY_BACKSPACE = 0x7F,
+	KEY_INSERT = 1000, /* arbitrary value */
+	KEY_DELETE,
+	KEY_HOME,
+	KEY_END,
+	KEY_PGUP,
+	KEY_PGDOWN,
+	KEY_LEFT,
+	KEY_RIGHT,
+	KEY_UP,
+	KEY_DOWN,
+	KEY_CLEFT,
+	KEY_CRIGHT,
+	KEY_CUP,
+	KEY_CDOWN,
+	KEY_SLEFT,
+	KEY_SRIGHT,
+	KEY_SUP,
+	KEY_SDOWN,
+	KEY_MLEFT,
+	KEY_MRIGHT,
+	KEY_MUP,
+	KEY_MDOWN,
+	KEY_STAB,
+	KEY_M_p,
+	KEY_M_n
+};
+
+typedef void (*line_callback)(char *);
+
+void terminal_setup(void);
+int terminal_print(const char *format, ...);
+int terminal_vprint(const char *format, va_list args);
+void terminal_process_char(int c, line_callback process_line);
+void terminal_insert_into_command_line(const char *p);
+void terminal_draw_command_line(void);
+void terminal_prompt_for(const char *s, line_callback process_line);
+
+void process_tab(const char *line, int len);
diff --git a/bluez/repo/android/compat/._readline b/bluez/repo/android/compat/._readline
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/compat/._readline
Binary files differ
diff --git a/bluez/repo/android/compat/._wordexp.h b/bluez/repo/android/compat/._wordexp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/compat/._wordexp.h
Binary files differ
diff --git a/bluez/repo/android/compat/readline/._history.h b/bluez/repo/android/compat/readline/._history.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/compat/readline/._history.h
Binary files differ
diff --git a/bluez/repo/android/compat/readline/._readline.h b/bluez/repo/android/compat/readline/._readline.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/compat/readline/._readline.h
Binary files differ
diff --git a/bluez/repo/android/compat/readline/history.h b/bluez/repo/android/compat/readline/history.h
new file mode 100644
index 0000000..decc2f4
--- /dev/null
+++ b/bluez/repo/android/compat/readline/history.h
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 1987-2011 Free Software Foundation, 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
+ *
+ */
+
+#ifndef _HISTORY_H_
+#define _HISTORY_H_
+
+static inline void add_history(const char *c)
+{
+}
+
+#endif
diff --git a/bluez/repo/android/compat/readline/readline.h b/bluez/repo/android/compat/readline/readline.h
new file mode 100644
index 0000000..aaf6f31
--- /dev/null
+++ b/bluez/repo/android/compat/readline/readline.h
@@ -0,0 +1,110 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 1987-2011 Free Software Foundation, 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
+ *
+ */
+
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+typedef void (*rl_vcpfunc_t)(char *c);
+typedef void (*rl_compdisp_func_t)(char **c, int i, int j);
+typedef char *(*rl_compentry_func_t)(const char *c, int i);
+typedef char **(*rl_completion_func_t)(const char *c, int i, int j);
+
+#define RL_STATE_DONE 0x1000000
+#define RL_ISSTATE(x) (rl_readline_state & (x))
+
+static int rl_end;
+static int rl_point;
+static int rl_readline_state;
+static int rl_erase_empty_line;
+static int rl_attempted_completion_over;
+static char *rl_prompt;
+static char *rl_line_buffer;
+static rl_compdisp_func_t rl_completion_display_matches_hook;
+static rl_completion_func_t rl_attempted_completion_function;
+
+static inline void rl_callback_handler_install(const char *c, rl_vcpfunc_t f)
+{
+	printf("readline not available\n");
+	exit(1);
+}
+
+static inline int rl_set_prompt(const char *c)
+{
+	return -1;
+}
+
+static inline void rl_replace_line(const char *c, int i)
+{
+}
+
+static inline void rl_redisplay(void)
+{
+}
+
+static inline char **rl_completion_matches(const char *c, rl_compentry_func_t f)
+{
+	return NULL;
+}
+
+static inline int rl_insert_text(const char *c)
+{
+	return -1;
+}
+
+static inline int rl_crlf(void)
+{
+	return -1;
+}
+
+static inline void rl_callback_read_char(void)
+{
+}
+
+static inline int rl_message(const char *c, ...)
+{
+	return -1;
+}
+
+static inline void rl_callback_handler_remove(void)
+{
+}
+
+static inline char *rl_copy_text(int i, int j)
+{
+	return NULL;
+}
+
+static inline void rl_save_prompt(void)
+{
+}
+
+static inline void rl_restore_prompt(void)
+{
+}
+
+static inline int rl_forced_update_display(void)
+{
+	return -1;
+}
+
+#endif
diff --git a/bluez/repo/android/compat/wordexp.h b/bluez/repo/android/compat/wordexp.h
new file mode 100644
index 0000000..ff1f21c
--- /dev/null
+++ b/bluez/repo/android/compat/wordexp.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 1991-2013 Free Software Foundation, Inc.
+ *
+ *
+ *  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
+ *
+ */
+
+#ifndef _WORDEXP_H_
+#define _WORDEXP_H_
+
+#define WRDE_NOCMD 0
+
+typedef struct {
+	size_t we_wordc;
+	char **we_wordv;
+	size_t we_offs;
+} wordexp_t;
+
+static inline int wordexp(const char *c, wordexp_t *w, int _i)
+{
+	return -1;
+}
+
+static inline void wordfree(wordexp_t *__wordexp)
+{
+}
+
+#endif
diff --git a/bluez/repo/android/cts.txt b/bluez/repo/android/cts.txt
new file mode 100644
index 0000000..f679263
--- /dev/null
+++ b/bluez/repo/android/cts.txt
@@ -0,0 +1,58 @@
+Android Compatibility Test Suite results
+
+Tested: 27-Nov-2014
+Android version: 5.0
+Kernel Version: 3.18
+CTS version: 5.0 R1 (android-5.0.0_r7)
+
+Note:
+CTS reliable write GATT tests require using CTS from master branch
+or https://android-review.googlesource.com/99354 applied.
+
+(*) Tests are disabled due to CTS quality issues. Link for reference:
+https://android.googlesource.com/platform/cts/+/0a62e4a0a9910101ccf2ccc43f6%5E!/
+
+-------------------------------------------------------------------------------
+android.bluetooth.cts.BasicAdapterTest (automated tests)
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+testAndroidTestCaseSetupProperly	PASS
+test_checkBluetoothAddress		PASS
+test_enableDisable			PASS
+test_getAddress				PASS
+test_getBondedDevices			PASS
+test_getDefaultAdapter			PASS
+test_getName				PASS
+test_getRemoteDevice			PASS
+test_listenUsingRfcommWithServiceRecord	PASS
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+com.android.cts.verifier (manual tests)
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+Toggle Bluetooth			PASS
+BLE Client Test:
+	connect				N/A	(*)
+	discover service		N/A	(*)
+	read/write characteristic	N/A	(*)
+	reliable write			N/A	(*)
+	notify characteristic		N/A	(*)
+	read/write descriptor		N/A	(*)
+	read RSSI			N/A	(*)
+	disconnect			N/A	(*)
+BLE Server Test:
+	add service			N/A	(*)
+	connection			N/A	(*)
+	read characteristic request	N/A	(*)
+	write characteristic request	N/A	(*)
+	read descriptor request		N/A	(*)
+	write descriptor request	N/A	(*)
+	reliable write			N/A	(*)
+	disconnection			N/A	(*)
+Insecure Client				PASS
+Insecure Server				PASS
+Secure Client				PASS
+Secure Server				PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/cutils/._properties.h b/bluez/repo/android/cutils/._properties.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/cutils/._properties.h
Binary files differ
diff --git a/bluez/repo/android/cutils/properties.h b/bluez/repo/android/cutils/properties.h
new file mode 100644
index 0000000..0163eb5
--- /dev/null
+++ b/bluez/repo/android/cutils/properties.h
@@ -0,0 +1,95 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define PROPERTY_VALUE_MAX 32
+#define PROPERTY_KEY_MAX 32
+
+#define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode"
+#define BLUETOOTH_MODE_PROPERTY_HANDSFREE "persist.sys.bluetooth.handsfree"
+
+static inline int property_get(const char *key, char *value,
+						const char *default_value)
+{
+	const char *prop = NULL;
+
+	if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME))
+		prop = getenv("BLUETOOTH_MODE");
+
+	if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_HANDSFREE))
+		prop = getenv("BLUETOOTH_HANDSFREE_MODE");
+
+	if (!prop)
+		prop = default_value;
+
+	if (prop) {
+		strncpy(value, prop, PROPERTY_VALUE_MAX);
+
+		value[PROPERTY_VALUE_MAX - 1] = '\0';
+
+		return strlen(value);
+	}
+
+	return 0;
+}
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+static inline int property_set(const char *key, const char *value)
+{
+	static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+
+	struct sockaddr_un addr;
+	char msg[256];
+	int fd, len;
+
+	fd = socket(PF_LOCAL, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return 0;
+	}
+
+	len = snprintf(msg, sizeof(msg), "%s=%s", key, value);
+
+	if (send(fd, msg, len + 1, 0) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+
+	return 0;
+}
diff --git a/bluez/repo/android/gatt.c b/bluez/repo/android/gatt.c
new file mode 100644
index 0000000..28635ed
--- /dev/null
+++ b/bluez/repo/android/gatt.c
@@ -0,0 +1,7193 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "ipc.h"
+#include "ipc-common.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "utils.h"
+#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"
+#include "attrib/gatt.h"
+#include "btio/btio.h"
+
+/* set according to Android bt_gatt_client.h */
+#define GATT_MAX_ATTR_LEN 600
+
+#define GATT_SUCCESS	0x00000000
+#define GATT_FAILURE	0x00000101
+
+#define BASE_UUID16_OFFSET     12
+
+#define GATT_PERM_READ			0x00000001
+#define GATT_PERM_READ_ENCRYPTED	0x00000002
+#define GATT_PERM_READ_MITM		0x00000004
+#define GATT_PERM_READ_AUTHORIZATION	0x00000008
+#define GATT_PERM_WRITE			0x00000100
+#define GATT_PERM_WRITE_ENCRYPTED	0x00000200
+#define GATT_PERM_WRITE_MITM		0x00000400
+#define GATT_PERM_WRITE_AUTHORIZATION	0x00000800
+#define GATT_PERM_WRITE_SIGNED		0x00010000
+#define GATT_PERM_WRITE_SIGNED_MITM	0x00020000
+#define GATT_PERM_NONE			0x10000000
+
+#define GATT_PAIR_CONN_TIMEOUT 30
+#define GATT_CONN_TIMEOUT 2
+
+static const uint8_t BLUETOOTH_UUID[] = {
+	0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+	0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+typedef enum {
+	DEVICE_DISCONNECTED = 0,
+	DEVICE_CONNECT_INIT,		/* connection procedure initiated */
+	DEVICE_CONNECT_READY,		/* dev found during LE scan */
+	DEVICE_CONNECTED,		/* connection has been established */
+} gatt_device_state_t;
+
+static const char *device_state_str[] = {
+	"DISCONNECTED",
+	"CONNECT INIT",
+	"CONNECT READY",
+	"CONNECTED",
+};
+
+struct pending_trans_data {
+	unsigned int id;
+	uint8_t opcode;
+	struct gatt_db_attribute *attrib;
+	unsigned int serial_id;
+};
+
+struct gatt_app {
+	int32_t id;
+	uint8_t uuid[16];
+
+	gatt_type_t type;
+
+	/* Valid for client applications */
+	struct queue *notifications;
+
+	gatt_conn_cb_t func;
+};
+
+struct element_id {
+	bt_uuid_t uuid;
+	uint8_t instance;
+};
+
+struct descriptor {
+	struct element_id id;
+	uint16_t handle;
+};
+
+struct characteristic {
+	struct element_id id;
+	struct gatt_char ch;
+	uint16_t end_handle;
+
+	struct queue *descriptors;
+};
+
+struct service {
+	struct element_id id;
+	struct gatt_primary prim;
+	struct gatt_included incl;
+
+	bool primary;
+
+	struct queue *chars;
+	struct queue *included;	/* Valid only for primary services */
+	bool incl_search_done;
+};
+
+struct notification_data {
+	struct hal_gatt_srvc_id service;
+	struct hal_gatt_gatt_id ch;
+	struct app_connection *conn;
+	guint notif_id;
+	guint ind_id;
+	int ref;
+};
+
+struct gatt_device {
+	bdaddr_t bdaddr;
+
+	gatt_device_state_t state;
+
+	GAttrib *attrib;
+	GIOChannel *att_io;
+	struct queue *services;
+	bool partial_srvc_search;
+
+	guint watch_id;
+	guint server_id;
+	guint ind_id;
+
+	int ref;
+
+	struct queue *autoconnect_apps;
+
+	struct queue *pending_requests;
+};
+
+struct app_connection {
+	struct gatt_device *device;
+	struct gatt_app *app;
+	struct queue *transactions;
+	int32_t id;
+
+	guint timeout_id;
+
+	bool wait_execute_write;
+};
+
+struct service_sdp {
+	int32_t service_handle;
+	uint32_t sdp_handle;
+};
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+static bool scanning = false;
+static unsigned int advertising_cnt = 0;
+
+static struct queue *gatt_apps = NULL;
+static struct queue *gatt_devices = NULL;
+static struct queue *app_connections = NULL;
+
+static struct queue *services_sdp = NULL;
+
+static struct queue *listen_apps = NULL;
+static struct gatt_db *gatt_db = NULL;
+
+static struct gatt_db_attribute *service_changed_attrib = NULL;
+
+static GIOChannel *le_io = NULL;
+static GIOChannel *bredr_io = NULL;
+
+static uint32_t gatt_sdp_handle = 0;
+static uint32_t gap_sdp_handle = 0;
+static uint32_t dis_sdp_handle = 0;
+
+static struct bt_crypto *crypto = NULL;
+
+static int test_client_if = 0;
+static const uint8_t TEST_UUID[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04
+};
+
+static bool is_bluetooth_uuid(const uint8_t *uuid)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		/* ignore minimal uuid (16) value */
+		if (i == 12 || i == 13)
+			continue;
+
+		if (uuid[i] != BLUETOOTH_UUID[i])
+			return false;
+	}
+
+	return true;
+}
+
+static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst)
+{
+	if (is_bluetooth_uuid(uuid)) {
+		/* copy 16 bit uuid value from full android 128bit uuid */
+		dst->type = BT_UUID16;
+		dst->value.u16 = (uuid[13] << 8) + uuid[12];
+	} else {
+		int i;
+
+		dst->type = BT_UUID128;
+		for (i = 0; i < 16; i++)
+			dst->value.u128.data[i] = uuid[15 - i];
+	}
+}
+
+static void uuid2android(const bt_uuid_t *src, uint8_t *uuid)
+{
+	bt_uuid_t uu128;
+	uint8_t i;
+
+	if (src->type != BT_UUID128) {
+		bt_uuid_to_uuid128(src, &uu128);
+		src = &uu128;
+	}
+
+	for (i = 0; i < 16; i++)
+		uuid[15 - i] = src->value.u128.data[i];
+}
+
+static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from,
+							struct element_id *to)
+{
+	to->instance = from->inst_id;
+	android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_srvc_id(const struct element_id *from,
+						uint8_t primary,
+						struct hal_gatt_srvc_id *to)
+{
+	to->is_primary = primary;
+	to->inst_id = from->instance;
+	uuid2android(&from->uuid, to->uuid);
+}
+
+static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from,
+							struct element_id *to)
+{
+	to->instance = from->inst_id;
+	android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_gatt_id(const struct element_id *from,
+						struct hal_gatt_gatt_id *to)
+{
+	to->inst_id = from->instance;
+	uuid2android(&from->uuid, to->uuid);
+}
+
+static void destroy_characteristic(void *data)
+{
+	struct characteristic *chars = data;
+
+	if (!chars)
+		return;
+
+	queue_destroy(chars->descriptors, free);
+	free(chars);
+}
+
+static void destroy_service(void *data)
+{
+	struct service *srvc = data;
+
+	if (!srvc)
+		return;
+
+	queue_destroy(srvc->chars, destroy_characteristic);
+
+	/*
+	 * Included services we keep on two queues.
+	 * 1. On the same queue with primary services.
+	 * 2. On the queue inside primary service.
+	 * So we need to free service memory only once but we need to destroy
+	 * two queues
+	 */
+	queue_destroy(srvc->included, NULL);
+
+	free(srvc);
+}
+
+static bool match_app_by_uuid(const void *data, const void *user_data)
+{
+	const uint8_t *exp_uuid = user_data;
+	const struct gatt_app *client = data;
+
+	return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid));
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+	int32_t exp_id = PTR_TO_INT(user_data);
+	const struct gatt_app *client = data;
+
+	return client->id == exp_id;
+}
+
+static struct gatt_app *find_app_by_id(int32_t id)
+{
+	return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id));
+}
+
+static bool match_device_by_bdaddr(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+	const bdaddr_t *addr = user_data;
+
+	return !bacmp(&dev->bdaddr, addr);
+}
+
+static bool match_device_by_state(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+
+	if (dev->state != PTR_TO_UINT(user_data))
+		return false;
+
+	return true;
+}
+
+static bool match_pending_device(const void *data, const void *user_data)
+{
+	const struct gatt_device *dev = data;
+
+	if ((dev->state == DEVICE_CONNECT_INIT) ||
+					(dev->state == DEVICE_CONNECT_READY))
+		return true;
+
+	return false;
+}
+
+static bool match_connection_by_id(const void *data, const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const int32_t id = PTR_TO_INT(user_data);
+
+	return conn->id == id;
+}
+
+static bool match_connection_by_device_and_app(const void *data,
+							const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const struct app_connection *match = user_data;
+
+	return conn->device == match->device && conn->app == match->app;
+}
+
+static struct app_connection *find_connection_by_id(int32_t conn_id)
+{
+	struct app_connection *conn;
+
+	conn = queue_find(app_connections, match_connection_by_id,
+							INT_TO_PTR(conn_id));
+	if (conn && conn->device->state == DEVICE_CONNECTED)
+		return conn;
+
+	return NULL;
+}
+
+static bool match_connection_by_device(const void *data, const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const struct gatt_device *dev = user_data;
+
+	return conn->device == dev;
+}
+
+static bool match_connection_by_app(const void *data, const void *user_data)
+{
+	const struct app_connection *conn = data;
+	const struct gatt_app *app = user_data;
+
+	return conn->app == app;
+}
+
+static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
+{
+	return queue_find(gatt_devices, match_device_by_bdaddr, addr);
+}
+
+static struct gatt_device *find_pending_device(void)
+{
+	return queue_find(gatt_devices, match_pending_device, NULL);
+}
+
+static struct gatt_device *find_device_by_state(uint32_t state)
+{
+	return queue_find(gatt_devices, match_device_by_state,
+							UINT_TO_PTR(state));
+}
+
+static bool match_srvc_by_element_id(const void *data, const void *user_data)
+{
+	const struct element_id *exp_id = user_data;
+	const struct service *service = data;
+
+	if (service->id.instance == exp_id->instance)
+		return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static bool match_srvc_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct service *s = data;
+	uint8_t inst_id = PTR_TO_INT(user_data);
+
+	/* For now we match inst_id as it is unique */
+	return inst_id < s->id.instance;
+}
+
+static bool match_srvc_by_bt_uuid(const void *data, const void *user_data)
+{
+	const bt_uuid_t *exp_uuid = user_data;
+	const struct service *service = data;
+
+	return !bt_uuid_cmp(exp_uuid, &service->id.uuid);
+}
+
+static bool match_srvc_by_range(const void *data, const void *user_data)
+{
+	const struct service *srvc = data;
+	const struct att_range *range = user_data;
+
+	return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range));
+}
+
+static bool match_char_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct characteristic *ch = data;
+	uint8_t inst_id = PTR_TO_INT(user_data);
+
+	/* For now we match inst_id as it is unique, we'll match uuids later */
+	return inst_id < ch->id.instance;
+}
+
+static bool match_descr_by_element_id(const void *data, const void *user_data)
+{
+	const struct element_id *exp_id = user_data;
+	const struct descriptor *descr = data;
+
+	if (exp_id->instance == descr->id.instance)
+		return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static bool match_descr_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct descriptor *descr = data;
+	uint8_t instance = PTR_TO_INT(user_data);
+
+	/* For now we match instance as it is unique */
+	return instance < descr->id.instance;
+}
+
+static bool match_notification(const void *a, const void *b)
+{
+	const struct notification_data *a1 = a;
+	const struct notification_data *b1 = b;
+
+	if (a1->conn != b1->conn)
+		return false;
+
+	if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch)))
+		return false;
+
+	if (memcmp(&a1->service, &b1->service, sizeof(a1->service)))
+		return false;
+
+	return true;
+}
+
+static bool match_char_by_element_id(const void *data, const void *user_data)
+{
+	const struct element_id *exp_id = user_data;
+	const struct characteristic *chars = data;
+
+	if (exp_id->instance == chars->id.instance)
+		return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static void destroy_notification(void *data)
+{
+	struct notification_data *notification = data;
+	struct gatt_app *app;
+
+	if (!notification)
+		return;
+
+	if (--notification->ref)
+		return;
+
+	app = notification->conn->app;
+	queue_remove_if(app->notifications, match_notification, notification);
+	free(notification);
+}
+
+static void unregister_notification(void *data)
+{
+	struct notification_data *notification = data;
+	struct gatt_device *dev = notification->conn->device;
+
+	/*
+	 * No device means it was already disconnected and client cleanup was
+	 * triggered afterwards, but once client unregisters, device stays if
+	 * used by others. Then just unregister single handle.
+	 */
+	if (!queue_find(gatt_devices, NULL, dev))
+		return;
+
+	if (notification->notif_id && dev)
+		g_attrib_unregister(dev->attrib, notification->notif_id);
+
+	if (notification->ind_id && dev)
+		g_attrib_unregister(dev->attrib, notification->ind_id);
+}
+
+static void device_set_state(struct gatt_device *dev, uint32_t state)
+{
+	char bda[18];
+
+	if (dev->state == state)
+		return;
+
+	ba2str(&dev->bdaddr, bda);
+	DBG("gatt: Device %s state changed %s -> %s", bda,
+			device_state_str[dev->state], device_state_str[state]);
+
+	dev->state = state;
+}
+
+static bool auto_connect_le(struct gatt_device *dev)
+{
+	/*  For LE devices use auto connect feature if possible */
+	if (bt_kernel_conn_control()) {
+		if (!bt_auto_connect_add(bt_get_id_addr(&dev->bdaddr, NULL)))
+			return false;
+	} else {
+		/* Trigger discovery if not already started */
+		if (!scanning && !bt_le_discovery_start()) {
+			error("gatt: Could not start scan");
+			return false;
+		}
+	}
+
+	device_set_state(dev, DEVICE_CONNECT_INIT);
+	return true;
+}
+
+static void connection_cleanup(struct gatt_device *device)
+{
+	if (device->watch_id) {
+		g_source_remove(device->watch_id);
+		device->watch_id = 0;
+	}
+
+	if (device->att_io) {
+		g_io_channel_shutdown(device->att_io, FALSE, NULL);
+		g_io_channel_unref(device->att_io);
+		device->att_io = NULL;
+	}
+
+	if (device->attrib) {
+		GAttrib *attrib = device->attrib;
+
+		if (device->server_id > 0)
+			g_attrib_unregister(device->attrib, device->server_id);
+
+		if (device->ind_id > 0)
+			g_attrib_unregister(device->attrib, device->ind_id);
+
+		device->attrib = NULL;
+		g_attrib_cancel_all(attrib);
+		g_attrib_unref(attrib);
+	}
+
+	/*
+	 * If device was in connection_pending or connectable state we
+	 * search device list if we should stop the scan.
+	 */
+	if (!scanning && (device->state == DEVICE_CONNECT_INIT ||
+				device->state == DEVICE_CONNECT_READY)) {
+		if (!find_pending_device())
+			bt_le_discovery_stop(NULL);
+	}
+
+	/* If device is not bonded service cache should be refreshed */
+	if (!bt_device_is_bonded(&device->bdaddr))
+		queue_remove_all(device->services, NULL, NULL, destroy_service);
+
+	device_set_state(device, DEVICE_DISCONNECTED);
+
+	if (!queue_isempty(device->autoconnect_apps))
+		auto_connect_le(device);
+	else
+		bt_auto_connect_remove(&device->bdaddr);
+}
+
+static void destroy_gatt_app(void *data)
+{
+	struct gatt_app *app = data;
+
+	if (!app)
+		return;
+
+	/*
+	 * First we want to get all notifications and unregister them.
+	 * We don't pass unregister_notification to queue_destroy,
+	 * because destroy notification performs operations on queue
+	 * too. So remove all elements and then destroy queue.
+	 */
+
+	if (app->type == GATT_CLIENT)
+		while (queue_peek_head(app->notifications)) {
+			struct notification_data *notification;
+
+			notification = queue_pop_head(app->notifications);
+			unregister_notification(notification);
+		}
+
+	queue_destroy(app->notifications, free);
+
+	free(app);
+}
+
+struct pending_request {
+	struct gatt_db_attribute *attrib;
+	int length;
+	uint8_t *value;
+	uint16_t offset;
+
+	uint8_t *filter_value;
+	uint16_t filter_vlen;
+
+	bool completed;
+	uint8_t error;
+};
+
+static void destroy_pending_request(void *data)
+{
+	struct pending_request *entry = data;
+
+	if (!entry)
+		return;
+
+	free(entry->value);
+	free(entry->filter_value);
+	free(entry);
+}
+
+static void destroy_device(void *data)
+{
+	struct gatt_device *dev = data;
+
+	if (!dev)
+		return;
+
+	queue_destroy(dev->services, destroy_service);
+	queue_destroy(dev->pending_requests, destroy_pending_request);
+	queue_destroy(dev->autoconnect_apps, NULL);
+
+	bt_auto_connect_remove(&dev->bdaddr);
+
+	free(dev);
+}
+
+static struct gatt_device *device_ref(struct gatt_device *device)
+{
+	if (!device)
+		return NULL;
+
+	device->ref++;
+
+	return device;
+}
+
+static void device_unref(struct gatt_device *device)
+{
+	if (!device)
+		return;
+
+	if (--device->ref)
+		return;
+
+	destroy_device(device);
+}
+
+static struct gatt_device *create_device(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+
+	dev = new0(struct gatt_device, 1);
+
+	bacpy(&dev->bdaddr, addr);
+
+	dev->services = queue_new();
+	dev->autoconnect_apps = queue_new();
+	dev->pending_requests = queue_new();
+
+	queue_push_head(gatt_devices, dev);
+
+	return device_ref(dev);
+}
+
+static void send_client_connect_status_notify(struct app_connection *conn,
+								int32_t status)
+{
+	struct hal_ev_gatt_client_connect ev;
+
+	if (conn->app->func) {
+		conn->app->func(&conn->device->bdaddr,
+					status == GATT_SUCCESS ? 0 : -ENOTCONN,
+					conn->device->attrib);
+		return;
+	}
+
+	ev.client_if = conn->app->id;
+	ev.conn_id = conn->id;
+	ev.status = status;
+
+	bdaddr2android(&conn->device->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
+							sizeof(ev), &ev);
+}
+
+static void send_server_connection_state_notify(struct app_connection *conn,
+								bool connected)
+{
+	struct hal_ev_gatt_server_connection ev;
+
+	if (conn->app->func) {
+		conn->app->func(&conn->device->bdaddr,
+					connected ? 0 : -ENOTCONN,
+					conn->device->attrib);
+		return;
+	}
+
+	ev.server_if = conn->app->id;
+	ev.conn_id = conn->id;
+	ev.connected = connected;
+
+	bdaddr2android(&conn->device->bdaddr, &ev.bdaddr);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
+}
+
+static void send_client_disconnect_status_notify(struct app_connection *conn,
+								int32_t status)
+{
+	struct hal_ev_gatt_client_disconnect ev;
+
+	if (conn->app->func) {
+		conn->app->func(&conn->device->bdaddr, -ENOTCONN,
+						conn->device->attrib);
+		return;
+	}
+
+	ev.client_if = conn->app->id;
+	ev.conn_id = conn->id;
+	ev.status = status;
+
+	bdaddr2android(&conn->device->bdaddr, &ev.bda);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+
+}
+
+static void notify_app_disconnect_status(struct app_connection *conn,
+								int32_t status)
+{
+	if (!conn->app)
+		return;
+
+	if (conn->app->type == GATT_CLIENT)
+		send_client_disconnect_status_notify(conn, status);
+	else
+		send_server_connection_state_notify(conn, !!status);
+}
+
+static void notify_app_connect_status(struct app_connection *conn,
+								int32_t status)
+{
+	if (!conn->app)
+		return;
+
+	if (conn->app->type == GATT_CLIENT)
+		send_client_connect_status_notify(conn, status);
+	else
+		send_server_connection_state_notify(conn, !status);
+}
+
+static void destroy_connection(void *data)
+{
+	struct app_connection *conn = data;
+
+	if (!conn)
+		return;
+
+	if (conn->timeout_id > 0)
+		g_source_remove(conn->timeout_id);
+
+	switch (conn->device->state) {
+	case DEVICE_CONNECTED:
+		notify_app_disconnect_status(conn, GATT_SUCCESS);
+		break;
+	case DEVICE_CONNECT_INIT:
+	case DEVICE_CONNECT_READY:
+		notify_app_connect_status(conn, GATT_FAILURE);
+		break;
+	case DEVICE_DISCONNECTED:
+		break;
+	}
+
+	if (!queue_find(app_connections, match_connection_by_device,
+							conn->device))
+		connection_cleanup(conn->device);
+
+	queue_destroy(conn->transactions, free);
+	device_unref(conn->device);
+	free(conn);
+}
+
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	int sock, err = 0;
+	socklen_t len;
+
+	sock = g_io_channel_unix_get_fd(io);
+	len = sizeof(err);
+	if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
+		DBG("%s (%d)", strerror(err), err);
+
+	queue_remove_all(app_connections, match_connection_by_device, dev,
+							destroy_connection);
+
+	return FALSE;
+}
+
+static bool get_local_mtu(struct gatt_device *dev, uint16_t *mtu)
+{
+	GIOChannel *io;
+	uint16_t imtu, omtu;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get local MTU");
+		return false;
+	}
+
+	/*
+	 * Limit MTU to  MIN(IMTU, OMTU). This is to avoid situation where
+	 * local OMTU < MIN(remote MTU, IMTU)
+	 */
+	if (mtu)
+		*mtu = MIN(imtu, omtu);
+
+	return true;
+}
+
+static void notify_client_mtu_change(struct app_connection *conn, bool success)
+{
+	struct hal_ev_gatt_client_configure_mtu ev;
+	size_t mtu;
+
+	g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+	ev.conn_id = conn->id;
+	ev.status = success ? GATT_SUCCESS : GATT_FAILURE;
+	ev.mtu = mtu;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_CONFIGURE_MTU, sizeof(ev), &ev);
+}
+
+static void notify_server_mtu(struct app_connection *conn)
+{
+	struct hal_ev_gatt_server_mtu_changed ev;
+	size_t mtu;
+
+	g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+	ev.conn_id = conn->id;
+	ev.mtu = mtu;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_MTU_CHANGED, sizeof(ev), &ev);
+}
+
+static void notify_mtu_change(void *data, void *user_data)
+{
+	struct gatt_device *device = user_data;
+	struct app_connection *conn = data;
+
+	if (conn->device != device)
+		return;
+
+	if (!conn->app) {
+		error("gatt: can't notify mtu - no app registered for conn");
+		return;
+	}
+
+	switch (conn->app->type) {
+	case GATT_CLIENT:
+		notify_client_mtu_change(conn, true);
+		break;
+	case GATT_SERVER:
+		notify_server_mtu(conn);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
+{
+	uint16_t mtu, lmtu;
+
+	if (!get_local_mtu(device, &lmtu))
+		return false;
+
+	DBG("remote_mtu:%d local_mtu:%d", rmtu, lmtu);
+
+	if (rmtu < ATT_DEFAULT_LE_MTU) {
+		error("gatt: remote MTU invalid (%u bytes)", rmtu);
+		return false;
+	}
+
+	mtu = MIN(lmtu, rmtu);
+
+	if (mtu == ATT_DEFAULT_LE_MTU)
+		return true;
+
+	if (!g_attrib_set_mtu(device->attrib, mtu)) {
+		error("gatt: Failed to set MTU");
+		return false;
+	}
+
+	queue_foreach(app_connections, notify_mtu_change, device);
+
+	return true;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct gatt_device *device = user_data;
+	uint16_t rmtu;
+
+	DBG("");
+
+	if (status) {
+		error("gatt: MTU exchange: %s", att_ecode2str(status));
+		goto failed;
+	}
+
+	if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+		error("gatt: MTU exchange: protocol error");
+		goto failed;
+	}
+
+	update_mtu(device, rmtu);
+
+failed:
+	device_unref(device);
+}
+
+static void send_exchange_mtu_request(struct gatt_device *device)
+{
+	uint16_t mtu;
+
+	if (!get_local_mtu(device, &mtu))
+		return;
+
+	DBG("mtu %u", mtu);
+
+	if (!gatt_exchange_mtu(device->attrib, mtu, exchange_mtu_cb,
+							device_ref(device)))
+		device_unref(device);
+}
+
+static void ignore_confirmation_cb(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	/* Ignored. */
+}
+
+static void notify_att_range_change(struct gatt_device *dev,
+							struct att_range *range)
+{
+	uint16_t handle;
+	uint16_t length = 0;
+	uint16_t ccc;
+	uint8_t *pdu;
+	size_t mtu;
+	GAttribResultFunc confirmation_cb = NULL;
+
+	handle = gatt_db_attribute_get_handle(service_changed_attrib);
+	if (!handle)
+		return;
+
+	ccc = bt_get_gatt_ccc(&dev->bdaddr);
+	if (!ccc)
+		return;
+
+	pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+
+	switch (ccc) {
+	case 0x0001:
+		length = enc_notification(handle, (uint8_t *) range,
+						sizeof(*range), pdu, mtu);
+		break;
+	case 0x0002:
+		length = enc_indication(handle, (uint8_t *) range,
+						sizeof(*range), pdu, mtu);
+		confirmation_cb = ignore_confirmation_cb;
+		break;
+	default:
+		/* 0xfff4 reserved for future use */
+		break;
+	}
+
+	g_attrib_send(dev->attrib, 0, pdu, length, confirmation_cb, NULL, NULL);
+}
+
+static struct app_connection *create_connection(struct gatt_device *device,
+						struct gatt_app *app)
+{
+	struct app_connection *new_conn;
+	static int32_t last_conn_id = 1;
+
+	/* Check if already connected */
+	new_conn = new0(struct app_connection, 1);
+
+	/* Make connection id unique to connection record (app, device) pair */
+	new_conn->app = app;
+	new_conn->id = last_conn_id++;
+	new_conn->transactions = queue_new();
+
+	queue_push_head(app_connections, new_conn);
+
+	new_conn->device = device_ref(device);
+
+	return new_conn;
+}
+
+static struct service *create_service(uint8_t id, bool primary, char *uuid,
+								void *data)
+{
+	struct service *s;
+
+	s = new0(struct service, 1);
+
+	if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) {
+		error("gatt: Cannot convert string to uuid");
+		free(s);
+		return NULL;
+	}
+
+	s->chars = queue_new();
+	s->included = queue_new();
+	s->id.instance = id;
+
+	/* Put primary service to our local list */
+	s->primary = primary;
+	if (s->primary)
+		memcpy(&s->prim, data, sizeof(s->prim));
+	else
+		memcpy(&s->incl, data, sizeof(s->incl));
+
+	return s;
+}
+
+static void send_client_primary_notify(void *data, void *user_data)
+{
+	struct hal_ev_gatt_client_search_result ev;
+	struct service *p = data;
+	int32_t conn_id = PTR_TO_INT(user_data);
+
+	/* In service queue we will have also included services */
+	if (!p->primary)
+		return;
+
+	ev.conn_id  = conn_id;
+	element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
+
+	uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
+}
+
+static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
+{
+	struct hal_ev_gatt_client_search_complete ev;
+
+	ev.status = status;
+	ev.conn_id = conn_id;
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
+
+struct discover_srvc_data {
+	bt_uuid_t uuid;
+	struct app_connection *conn;
+};
+
+static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct gatt_primary prim;
+	struct service *s;
+	int32_t gatt_status;
+	struct gatt_device *dev = cb_data->conn->device;
+	uint8_t instance_id = queue_length(dev->services);
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover pri srvc filtered by uuid failed: %s",
+							att_ecode2str(status));
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!ranges) {
+		info("gatt: No primary services searched by uuid found");
+		gatt_status = GATT_SUCCESS;
+		goto reply;
+	}
+
+	bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
+
+	for (; ranges; ranges = ranges->next) {
+		memcpy(&prim.range, ranges->data, sizeof(prim.range));
+
+		s = create_service(instance_id++, true, prim.uuid, &prim);
+		if (!s) {
+			gatt_status = GATT_FAILURE;
+			goto reply;
+		}
+
+		queue_push_tail(dev->services, s);
+
+		send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
+
+		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+				prim.range.start, prim.range.end, prim.uuid);
+	}
+
+	/* Partial search service scanning was performed */
+	dev->partial_srvc_search = true;
+	gatt_status = GATT_SUCCESS;
+
+reply:
+	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+	free(cb_data);
+}
+
+static void discover_srvc_all_cb(uint8_t status, GSList *services,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct gatt_device *dev = cb_data->conn->device;
+	int32_t gatt_status;
+	GSList *l;
+	/*
+	 * There might be multiply services with same uuid. Therefore make sure
+	 * each primary service one has unique instance_id
+	 */
+	uint8_t instance_id = queue_length(dev->services);
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		gatt_status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		gatt_status = GATT_SUCCESS;
+		goto reply;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		struct service *p;
+
+		if (queue_find(dev->services, match_srvc_by_range,
+								&prim->range))
+			continue;
+
+		p = create_service(instance_id++, true, prim->uuid, prim);
+		if (!p)
+			continue;
+
+		queue_push_tail(dev->services, p);
+
+		DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+			prim->range.start, prim->range.end, prim->uuid);
+	}
+
+	/*
+	 * Send all found services notifications - first cache,
+	 * then send notifies
+	 */
+	queue_foreach(dev->services, send_client_primary_notify,
+						INT_TO_PTR(cb_data->conn->id));
+
+	/* Full search service scanning was performed */
+	dev->partial_srvc_search = false;
+	gatt_status = GATT_SUCCESS;
+
+reply:
+	send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+	free(cb_data);
+}
+
+static gboolean connection_timeout(void *user_data)
+{
+	struct app_connection *conn = user_data;
+
+	conn->timeout_id = 0;
+
+	queue_remove(app_connections, conn);
+	destroy_connection(conn);
+
+	return FALSE;
+}
+
+static void discover_primary_cb(uint8_t status, GSList *services,
+								void *user_data)
+{
+	struct discover_srvc_data *cb_data = user_data;
+	struct app_connection *conn = cb_data->conn;
+	struct gatt_device *dev = conn->device;
+	GSList *l, *uuids = NULL;
+
+	DBG("Status %d", status);
+
+	if (status) {
+		error("gatt: Discover all primary services failed: %s",
+							att_ecode2str(status));
+		free(cb_data);
+
+		return;
+	}
+
+	if (!services) {
+		info("gatt: No primary services found");
+		free(cb_data);
+
+		return;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		uint8_t *new_uuid;
+		bt_uuid_t uuid, u128;
+
+		DBG("uuid: %s", prim->uuid);
+
+		if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+			error("gatt: Cannot convert string to uuid");
+			continue;
+		}
+
+		bt_uuid_to_uuid128(&uuid, &u128);
+		new_uuid = g_memdup(&u128.value.u128, sizeof(u128.value.u128));
+
+		uuids = g_slist_prepend(uuids, new_uuid);
+	}
+
+	bt_device_set_uuids(&dev->bdaddr, uuids);
+
+	free(cb_data);
+
+	conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT,
+						connection_timeout, conn);
+}
+
+static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
+{
+	struct discover_srvc_data *cb_data;
+
+	cb_data = new0(struct discover_srvc_data, 1);
+	cb_data->conn = conn;
+
+	if (uuid) {
+		memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
+		return gatt_discover_primary(conn->device->attrib, uuid,
+					discover_srvc_by_uuid_cb, cb_data);
+	}
+
+	if (conn->app)
+		return gatt_discover_primary(conn->device->attrib, NULL,
+						discover_srvc_all_cb, cb_data);
+
+	return gatt_discover_primary(conn->device->attrib, NULL,
+						discover_primary_cb, cb_data);
+}
+
+struct connect_data {
+	struct gatt_device *dev;
+	int32_t status;
+};
+
+static void notify_app_connect_status_by_device(void *data, void *user_data)
+{
+	struct app_connection *conn = data;
+	struct connect_data *con_data = user_data;
+
+	if (conn->device == con_data->dev)
+		notify_app_connect_status(conn, con_data->status);
+}
+
+static struct app_connection *find_conn_without_app(struct gatt_device *dev)
+{
+	struct app_connection conn_match;
+
+	conn_match.device = dev;
+	conn_match.app = NULL;
+
+	return queue_find(app_connections, match_connection_by_device_and_app,
+								&conn_match);
+}
+
+static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
+{
+	struct app_connection conn_match;
+	struct gatt_device *dev;
+	struct gatt_app *app;
+
+	/* Check if app is registered */
+	app = find_app_by_id(app_id);
+	if (!app) {
+		error("gatt: Client id %d not found", app_id);
+		return NULL;
+	}
+
+	/* Check if device is known */
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		error("gatt: Client id %d not found", app_id);
+		return NULL;
+	}
+
+	conn_match.device = dev;
+	conn_match.app = app;
+
+	return queue_find(app_connections, match_connection_by_device_and_app,
+								&conn_match);
+}
+
+static void create_app_connection(void *data, void *user_data)
+{
+	struct gatt_device *dev = user_data;
+	struct gatt_app *app;
+
+	app = find_app_by_id(PTR_TO_INT(data));
+	if (!app)
+		return;
+
+	DBG("Autoconnect application id=%d", app->id);
+
+	if (!find_conn(&dev->bdaddr, PTR_TO_INT(data)))
+		create_connection(dev, app);
+}
+
+static void ind_handler(const uint8_t *cmd, uint16_t cmd_len,
+							gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	uint16_t resp_length = 0;
+	size_t length;
+	uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
+
+	/*
+	 * We have to send confirmation here. If some client is
+	 * registered for this indication, event will be send in
+	 * handle_notification
+	 */
+
+	resp_length = enc_confirmation(opdu, length);
+	g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	struct connect_data data;
+	struct att_range range;
+	uint32_t status;
+	GError *err = NULL;
+	GAttrib *attrib;
+	uint16_t mtu, cid;
+
+	if (dev->state != DEVICE_CONNECT_READY) {
+		error("gatt: Device not in a connecting state!?");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	if (dev->att_io) {
+		g_io_channel_unref(dev->att_io);
+		dev->att_io = NULL;
+	}
+
+	if (gerr) {
+		error("gatt: connection failed %s", gerr->message);
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		goto reply;
+	}
+
+	if (!bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Could not get imtu or cid: %s", err->message);
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		g_error_free(err);
+		goto reply;
+	}
+
+	/* on BR/EDR MTU must not be less then minimal allowed MTU */
+	if (cid != ATT_CID && mtu < ATT_DEFAULT_L2CAP_MTU) {
+		error("gatt: MTU too small (%u bytes)", mtu);
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		goto reply;
+	}
+
+	DBG("mtu %u cid %u", mtu, cid);
+
+	/* on LE we always start with default MTU */
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(io, mtu, true);
+	if (!attrib) {
+		error("gatt: unable to create new GAttrib instance");
+		device_set_state(dev, DEVICE_DISCONNECTED);
+		status = GATT_FAILURE;
+		goto reply;
+	}
+
+	dev->attrib = attrib;
+	dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							disconnected_cb, dev);
+
+	dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+						GATTRIB_ALL_HANDLES,
+						att_handler, dev, NULL);
+	dev->ind_id = g_attrib_register(attrib, ATT_OP_HANDLE_IND,
+						GATTRIB_ALL_HANDLES,
+						ind_handler, dev, NULL);
+	if ((dev->server_id && dev->ind_id) == 0)
+		error("gatt: Could not attach to server");
+
+	device_set_state(dev, DEVICE_CONNECTED);
+
+	/* Send exchange mtu request as we assume being client and server */
+	/* TODO: Dont exchange mtu if no client apps */
+
+	/* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+	if (cid == ATT_CID)
+		send_exchange_mtu_request(dev);
+
+	/*
+	 * Service Changed Characteristic and CCC Descriptor handles
+	 * should not change if there are bonded devices. We have them
+	 * constant all the time, thus they should be excluded from
+	 * range indicating changes.
+	 */
+	range.start = gatt_db_attribute_get_handle(service_changed_attrib) + 2;
+	range.end = 0xffff;
+
+	/*
+	 * If there is ccc stored for that device we were acting as server for
+	 * it, and as we dont have last connect and last services (de)activation
+	 * timestamps we should always assume something has changed.
+	 */
+	notify_att_range_change(dev, &range);
+
+	status = GATT_SUCCESS;
+
+reply:
+	/*
+	 * Make sure there are app_connections for all apps interested in auto
+	 * connect to that device
+	 */
+	queue_foreach(dev->autoconnect_apps, create_app_connection, dev);
+
+	if (!queue_find(app_connections, match_connection_by_device, dev)) {
+		struct app_connection *conn;
+
+		if (!dev->attrib)
+			return;
+
+		conn = create_connection(dev, NULL);
+		if (!conn)
+			return;
+
+		if (bt_is_pairing(&dev->bdaddr))
+			/*
+			 * If there is bonding ongoing lets wait for paired
+			 * callback. Once we get that we can start search
+			 * services
+			 */
+			conn->timeout_id = g_timeout_add_seconds(
+						GATT_PAIR_CONN_TIMEOUT,
+						connection_timeout, conn);
+		else
+			/*
+			 * There is no ongoing bonding, lets search for primary
+			 * services
+			 */
+			search_dev_for_srvc(conn, NULL);
+	}
+
+	data.dev = dev;
+	data.status = status;
+	queue_foreach(app_connections, notify_app_connect_status_by_device,
+									&data);
+
+	/* For BR/EDR notify about MTU since it is not negotiable*/
+	if (cid != ATT_CID && status == GATT_SUCCESS)
+		queue_foreach(app_connections, notify_mtu_change, dev);
+
+	device_unref(dev);
+
+	/* Check if we should restart scan */
+	if (scanning)
+		bt_le_discovery_start();
+
+	/* FIXME: What to do if discovery won't start here. */
+}
+
+static int connect_le(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	GError *gerr = NULL;
+	char addr[18];
+	const bdaddr_t *bdaddr;
+	uint8_t bdaddr_type;
+
+	ba2str(&dev->bdaddr, addr);
+
+	/* There is one connection attempt going on */
+	if (dev->att_io) {
+		info("gatt: connection to dev %s is ongoing", addr);
+		return -EALREADY;
+	}
+
+	DBG("Connection attempt to: %s", addr);
+
+	bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type);
+
+	/*
+	 * This connection will help us catch any PDUs that comes before
+	 * pairing finishes
+	 */
+	io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+					BT_IO_OPT_DEST_BDADDR, bdaddr,
+					BT_IO_OPT_DEST_TYPE, bdaddr_type,
+					BT_IO_OPT_CID, ATT_CID,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+	if (!io) {
+		error("gatt: Failed bt_io_connect(%s): %s", addr,
+							gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	/* Keep this, so we can cancel the connection */
+	dev->att_io = io;
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	return 0;
+}
+
+static int connect_next_dev(void)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	dev = find_device_by_state(DEVICE_CONNECT_READY);
+	if (!dev)
+		return -ENODEV;
+
+	return connect_le(dev);
+}
+
+static void bt_le_discovery_stop_cb(void)
+{
+	DBG("");
+
+	/* Check now if there is any device ready to connect */
+	if (connect_next_dev() < 0)
+		bt_le_discovery_start();
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, int rssi,
+					uint16_t eir_len, const void *eir,
+					bool connectable, bool bonded)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+	struct gatt_device *dev;
+	char bda[18];
+
+	if (!scanning)
+		goto done;
+
+	ba2str(addr, bda);
+	DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+	bdaddr2android(addr, ev->bda);
+	ev->rssi = rssi;
+	ev->len = eir_len;
+
+	memcpy(ev->adv_data, eir, ev->len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+						HAL_EV_GATT_CLIENT_SCAN_RESULT,
+						sizeof(*ev) + ev->len, ev);
+
+done:
+	if (!connectable)
+		return;
+
+	/* We use auto connect feature from kernel if possible */
+	if (bt_kernel_conn_control())
+		return;
+
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		if (!bonded)
+			return;
+
+		dev = create_device(addr);
+	}
+
+	if (dev->state != DEVICE_CONNECT_INIT)
+		return;
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	/*
+	 * We are ok to perform connect now. Stop discovery
+	 * and once it is stopped continue with creating ACL
+	 */
+	bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type)
+{
+	static int32_t application_id = 1;
+	struct gatt_app *app;
+
+	if (queue_find(gatt_apps, match_app_by_uuid, uuid)) {
+		error("gatt: app uuid is already on list");
+		return NULL;
+	}
+
+	app = new0(struct gatt_app, 1);
+
+	app->type = type;
+
+	if (app->type == GATT_CLIENT)
+		app->notifications = queue_new();
+
+	memcpy(app->uuid, uuid, sizeof(app->uuid));
+
+	app->id = application_id++;
+
+	queue_push_head(gatt_apps, app);
+
+	if (app->type == GATT_SERVER)
+		queue_push_tail(listen_apps, INT_TO_PTR(app->id));
+
+	return app;
+}
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_register *cmd = buf;
+	struct hal_ev_gatt_client_register_client ev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	app = register_app(cmd->uuid, GATT_CLIENT);
+
+	if (app) {
+		ev.client_if = app->id;
+		ev.status = GATT_SUCCESS;
+	} else {
+		ev.status = GATT_FAILURE;
+	}
+
+	/* We should send notification with given in cmd UUID */
+	memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+							HAL_STATUS_SUCCESS);
+}
+
+static void handle_client_scan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan *cmd = buf;
+	uint8_t status;
+
+	DBG("new state %d", cmd->start);
+
+	if (cmd->client_if != 0) {
+		void *registered = find_app_by_id(cmd->client_if);
+
+		if (!registered) {
+			error("gatt: Client not registered");
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+	}
+
+	/* Turn off scan */
+	if (!cmd->start) {
+		DBG("Stopping LE SCAN");
+
+		if (scanning) {
+			bt_le_discovery_stop(NULL);
+			scanning = false;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Reply success if we already do scan */
+	if (scanning) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Turn on scan */
+	if (!bt_le_discovery_start()) {
+		error("gatt: LE scan switch failed");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	scanning = true;
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+									status);
+}
+
+static int connect_bredr(struct gatt_device *dev)
+{
+	BtIOSecLevel sec_level;
+	GIOChannel *io;
+	GError *gerr = NULL;
+	char addr[18];
+
+	ba2str(&dev->bdaddr, addr);
+
+	/* There is one connection attempt going on */
+	if (dev->att_io) {
+		info("gatt: connection to dev %s is ongoing", addr);
+		return -EALREADY;
+	}
+
+	DBG("Connection attempt to: %s", addr);
+
+	sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM :
+								BT_IO_SEC_LOW;
+
+	io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+					BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+					BT_IO_OPT_DEST_TYPE, BDADDR_BREDR,
+					BT_IO_OPT_PSM, ATT_PSM,
+					BT_IO_OPT_SEC_LEVEL, sec_level,
+					BT_IO_OPT_INVALID);
+	if (!io) {
+		error("gatt: Failed bt_io_connect(%s): %s", addr,
+							gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	/* Keep this, so we can cancel the connection */
+	dev->att_io = io;
+
+	return 0;
+}
+
+static bool trigger_connection(struct app_connection *conn, bool direct)
+{
+	switch (conn->device->state) {
+	case DEVICE_DISCONNECTED:
+		/*
+		 *  If device was last seen over BR/EDR connect over it.
+		 *  Note: Connection state is handled in connect_bredr() func
+		 */
+		if (bt_device_last_seen_bearer(&conn->device->bdaddr) ==
+								BDADDR_BREDR)
+			return connect_bredr(conn->device) == 0;
+
+		if (direct)
+			return connect_le(conn->device) == 0;
+
+		bt_gatt_add_autoconnect(conn->app->id, &conn->device->bdaddr);
+		return auto_connect_le(conn->device);
+	case DEVICE_CONNECTED:
+		notify_app_connect_status(conn, GATT_SUCCESS);
+		return true;
+	case DEVICE_CONNECT_READY:
+	case DEVICE_CONNECT_INIT:
+	default:
+		/* In those cases connection is already triggered. */
+		return true;
+	}
+}
+
+static void remove_autoconnect_device(struct gatt_device *dev)
+{
+	bt_auto_connect_remove(&dev->bdaddr);
+
+	if (dev->state == DEVICE_CONNECT_INIT)
+		device_set_state(dev, DEVICE_DISCONNECTED);
+
+	device_unref(dev);
+}
+
+static void clear_autoconnect_devices(void *data, void *user_data)
+{
+	struct gatt_device *dev = data;
+
+	if (queue_remove(dev->autoconnect_apps, user_data))
+		if (queue_isempty(dev->autoconnect_apps))
+			remove_autoconnect_device(dev);
+}
+
+static uint8_t unregister_app(int client_if)
+{
+	struct gatt_app *cl;
+
+	/*
+	 * Make sure that there is no devices in auto connect list for this
+	 * application
+	 */
+	queue_foreach(gatt_devices, clear_autoconnect_devices,
+							INT_TO_PTR(client_if));
+
+	cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if));
+	if (!cl) {
+		error("gatt: client_if=%d not found", client_if);
+
+		return HAL_STATUS_FAILED;
+	}
+
+	/* Destroy app connections with proper notifications for this app. */
+	queue_remove_all(app_connections, match_connection_by_app, cl,
+							destroy_connection);
+	destroy_gatt_app(cl);
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void send_client_listen_notify(int32_t id, int32_t status)
+{
+	struct hal_ev_gatt_client_listen ev;
+
+	/* Server if because of typo in android headers */
+	ev.server_if = id;
+	ev.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN,
+							sizeof(ev), &ev);
+}
+
+struct listen_data {
+	int32_t client_id;
+	bool start;
+};
+
+static struct listen_data *create_listen_data(int32_t client_id, bool start)
+{
+	struct listen_data *d;
+
+	d = new0(struct listen_data, 1);
+	d->client_id = client_id;
+	d->start = start;
+
+	return d;
+}
+
+static void set_advertising_cb(uint8_t status, void *user_data)
+{
+	struct listen_data *l = user_data;
+
+	send_client_listen_notify(l->client_id, status);
+
+	/* In case of success update advertising state*/
+	if (!status)
+		advertising_cnt = l->start ? 1 : 0;
+
+	/*
+	 * Let's remove client from the list in two cases
+	 * 1. Start failed
+	 * 2. Stop succeed
+	 */
+	if ((l->start && status) || (!l->start && !status))
+		queue_remove(listen_apps, INT_TO_PTR(l->client_id));
+
+	free(l);
+}
+
+static void handle_client_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_unregister *cmd = buf;
+	uint8_t status;
+	void *listening_client;
+	struct listen_data *data;
+
+	DBG("");
+
+	listening_client = queue_find(listen_apps, NULL,
+						INT_TO_PTR(cmd->client_if));
+
+	if (listening_client) {
+		advertising_cnt--;
+		queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
+	} else {
+		status = unregister_app(cmd->client_if);
+		goto reply;
+	}
+
+	if (!advertising_cnt) {
+		data = create_listen_data(cmd->client_if, false);
+
+		if (!bt_le_set_advertising(data->start, set_advertising_cb,
+								data)) {
+			error("gatt: Could not set advertising");
+			status = HAL_STATUS_FAILED;
+			free(data);
+			goto reply;
+		}
+	}
+
+	status = unregister_app(cmd->client_if);
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UNREGISTER, status);
+}
+
+static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr, bool direct)
+{
+	struct app_connection conn_match;
+	struct app_connection *conn;
+	struct gatt_device *device;
+	struct gatt_app *app;
+
+	DBG("");
+
+	app = find_app_by_id(app_id);
+	if (!app)
+		return HAL_STATUS_FAILED;
+
+	device = find_device_by_addr(addr);
+	if (!device)
+		device = create_device(addr);
+
+	conn_match.device = device;
+	conn_match.app = app;
+
+	conn = queue_find(app_connections, match_connection_by_device_and_app,
+								&conn_match);
+	if (!conn) {
+		conn = create_connection(device, app);
+		if (!conn)
+			return HAL_STATUS_NOMEM;
+	}
+
+	if (!trigger_connection(conn, direct))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_connect *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	DBG("is_direct:%u transport:%u", cmd->is_direct, cmd->transport);
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	/* TODO handle transport flag */
+
+	status = handle_connect(cmd->client_if, &addr, cmd->is_direct);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+								status);
+}
+
+static void handle_client_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_disconnect *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+
+	DBG("");
+
+	/* TODO: should we care to match also bdaddr when conn_id is unique? */
+	conn = queue_remove_if(app_connections, match_connection_by_id,
+						INT_TO_PTR(cmd->conn_id));
+	destroy_connection(conn);
+
+	status = HAL_STATUS_SUCCESS;
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_DISCONNECT, status);
+}
+
+static void handle_client_listen(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_listen *cmd = buf;
+	uint8_t status;
+	struct listen_data *data;
+	bool req_sent = false;
+	void *listening_client;
+
+	DBG("");
+
+	if (!find_app_by_id(cmd->client_if)) {
+		error("gatt: Client not registered");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	listening_client = queue_find(listen_apps, NULL,
+						INT_TO_PTR(cmd->client_if));
+	/* Start listening */
+	if (cmd->start) {
+		if (listening_client) {
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+
+		queue_push_tail(listen_apps, INT_TO_PTR(cmd->client_if));
+
+		/* If listen is already on just return success*/
+		if (advertising_cnt > 0) {
+			advertising_cnt++;
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+	} else {
+		/* Stop listening. Check if client was listening */
+		if (!listening_client) {
+			error("gatt: This client %d does not listen",
+							cmd->client_if);
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+
+		/*
+		 * In case there is more listening clients don't stop
+		 * advertising
+		 */
+		if (advertising_cnt > 1) {
+			advertising_cnt--;
+			queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+	}
+
+	data = create_listen_data(cmd->client_if, cmd->start);
+
+	if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) {
+		error("gatt: Could not set advertising");
+		status = HAL_STATUS_FAILED;
+		free(data);
+		goto reply;
+	}
+
+	/*
+	 * Use this flag to keep in mind that we are waiting for callback with
+	 * result
+	 */
+	req_sent = true;
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+							status);
+
+	/* In case of early success or error, just send notification up */
+	if (!req_sent) {
+		int32_t gatt_status = status == HAL_STATUS_SUCCESS ?
+						GATT_SUCCESS : GATT_FAILURE;
+		send_client_listen_notify(cmd->client_if, gatt_status);
+	}
+}
+
+static void handle_client_refresh(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_refresh *cmd = buf;
+	struct gatt_device *dev;
+	uint8_t status;
+	bdaddr_t bda;
+
+	/*
+	 * This is Android's framework hidden API call. It seams that no
+	 * notification is expected and Bluedroid silently updates device's
+	 * cache under the hood. As we use lazy caching ,we can just clear the
+	 * cache and we're done.
+	 */
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bda);
+	dev = find_device_by_addr(&bda);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	queue_remove_all(dev->services, NULL, NULL, destroy_service);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+									status);
+}
+
+static void handle_client_search_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_search_service *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+	struct service *s;
+	bt_uuid_t uuid;
+	guint srvc_search_success;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) {
+		error("Invalid search service size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		error("gatt: dev with conn_id=%d not found", cmd->conn_id);
+
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (conn->device->state != DEVICE_CONNECTED) {
+		char bda[18];
+
+		ba2str(&conn->device->bdaddr, bda);
+		error("gatt: device %s not connected", bda);
+
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (cmd->filtered)
+		android2uuid(cmd->filter_uuid, &uuid);
+
+	/* Services not cached yet */
+	if (queue_isempty(conn->device->services)) {
+		if (cmd->filtered)
+			srvc_search_success = search_dev_for_srvc(conn, &uuid);
+		else
+			srvc_search_success = search_dev_for_srvc(conn, NULL);
+
+		if (!srvc_search_success) {
+			status = HAL_STATUS_FAILED;
+			goto reply;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Search in cached services for given service */
+	if (cmd->filtered) {
+		/* Search in cache for service by uuid */
+		s = queue_find(conn->device->services, match_srvc_by_bt_uuid,
+									&uuid);
+
+		if (s) {
+			send_client_primary_notify(s, INT_TO_PTR(conn->id));
+		} else {
+			if (!search_dev_for_srvc(conn, &uuid)) {
+				status = HAL_STATUS_FAILED;
+				goto reply;
+			}
+
+			status = HAL_STATUS_SUCCESS;
+			goto reply;
+		}
+	} else {
+		/* Refresh service cache if only partial search was performed */
+		if (conn->device->partial_srvc_search) {
+			srvc_search_success = search_dev_for_srvc(conn, NULL);
+			if (!srvc_search_success) {
+				status = HAL_STATUS_FAILED;
+				goto reply;
+			}
+		} else
+			queue_foreach(conn->device->services,
+						send_client_primary_notify,
+						INT_TO_PTR(cmd->conn_id));
+	}
+
+	send_client_search_complete_notify(GATT_SUCCESS, conn->id);
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
+}
+
+static void send_client_incl_service_notify(const struct element_id *srvc_id,
+						const struct service *incl,
+						int32_t conn_id)
+{
+	struct hal_ev_gatt_client_get_inc_service ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.conn_id = conn_id;
+
+	element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id);
+
+	if (incl) {
+		element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id);
+		ev.status = GATT_SUCCESS;
+	} else {
+		ev.status = GATT_FAILURE;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
+					HAL_EV_GATT_CLIENT_GET_INC_SERVICE,
+					sizeof(ev), &ev);
+}
+
+struct get_included_data {
+	struct service *prim;
+	struct app_connection *conn;
+};
+
+static int get_inst_id_of_prim_services(const struct gatt_device *dev)
+{
+	struct service *s = queue_peek_tail(dev->services);
+
+	if (s)
+		return s->id.instance;
+
+	return -1;
+}
+
+static void get_included_cb(uint8_t status, GSList *included, void *user_data)
+{
+	struct get_included_data *data = user_data;
+	struct app_connection *conn = data->conn;
+	struct service *service = data->prim;
+	struct service *incl = NULL;
+	int instance_id;
+
+	DBG("");
+
+	free(data);
+
+	if (status) {
+		error("gatt: no included services found");
+		goto failed;
+	}
+
+	/* Remember that we already search included services.*/
+	service->incl_search_done = true;
+
+	/*
+	 * There might be multiply services with same uuid. Therefore make sure
+	 * each service has unique instance id. Let's take the latest instance
+	 * id of primary service and start iterate included services from this
+	 * point.
+	 */
+	instance_id = get_inst_id_of_prim_services(conn->device);
+	if (instance_id < 0)
+		goto failed;
+
+	for (; included; included = included->next) {
+		struct gatt_included *included_service = included->data;
+
+		incl = create_service(++instance_id, false,
+							included_service->uuid,
+							included_service);
+		if (!incl)
+			continue;
+
+		/*
+		 * Lets keep included service on two queues.
+		 * 1. on services queue together with primary service
+		 * 2. on special queue inside primary service
+		 */
+		queue_push_tail(service->included, incl);
+		queue_push_tail(conn->device->services, incl);
+	}
+
+	/*
+	 * Notify upper layer about first included service.
+	 * Android framework will iterate for next one.
+	 */
+	incl = queue_peek_head(service->included);
+
+failed:
+	send_client_incl_service_notify(&service->id, incl, conn->id);
+}
+
+static void search_included_services(struct app_connection *conn,
+							struct service *service)
+{
+	struct get_included_data *data;
+	uint16_t start, end;
+
+	data = new0(struct get_included_data, 1);
+	data->prim = service;
+	data->conn = conn;
+
+	if (service->primary) {
+		start = service->prim.range.start;
+		end = service->prim.range.end;
+	} else {
+		start = service->incl.range.start;
+		end = service->incl.range.end;
+	}
+
+	gatt_find_included(conn->device->attrib, start, end, get_included_cb,
+									data);
+}
+
+static bool find_service(int32_t conn_id, struct element_id *service_id,
+					struct app_connection **connection,
+					struct service **service)
+{
+	struct service *srvc;
+	struct app_connection *conn;
+
+	conn = find_connection_by_id(conn_id);
+	if (!conn) {
+		error("gatt: conn_id=%d not found", conn_id);
+		return false;
+	}
+
+	srvc = queue_find(conn->device->services, match_srvc_by_element_id,
+								service_id);
+	if (!srvc) {
+		error("gatt: Service with inst_id: %d not found",
+							service_id->instance);
+		return false;
+	}
+
+	*connection = conn;
+	*service = srvc;
+
+	return true;
+}
+
+static void handle_client_get_included_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_included_service *cmd = buf;
+	struct app_connection *conn;
+	struct service *prim_service;
+	struct service *incl_service = NULL;
+	struct element_id match_id;
+	struct element_id srvc_id;
+	uint8_t status;
+
+	DBG("");
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+
+	if (len != sizeof(*cmd) +
+			(cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) {
+		error("Invalid get incl services size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+	if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) {
+		status = HAL_STATUS_FAILED;
+		goto notify;
+	}
+
+	if (!prim_service->incl_search_done) {
+		search_included_services(conn, prim_service);
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* Try to use cache here */
+	if (!cmd->continuation) {
+		incl_service = queue_peek_head(prim_service->included);
+	} else {
+		uint8_t inst_id = cmd->incl_srvc_id[0].inst_id;
+
+		incl_service = queue_find(prim_service->included,
+						match_srvc_by_higher_inst_id,
+						INT_TO_PTR(inst_id));
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+notify:
+	/*
+	 * In case of error in handling request we need to send event with
+	 * service id of cmd and gatt failure status.
+	 */
+	send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id);
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status);
+}
+
+static void send_client_char_notify(const struct hal_gatt_srvc_id *service,
+					const struct hal_gatt_gatt_id *charac,
+					int32_t char_prop, int32_t conn_id)
+{
+	struct hal_ev_gatt_client_get_characteristic ev;
+
+	ev.conn_id = conn_id;
+
+	if (charac) {
+		memcpy(&ev.char_id, charac, sizeof(struct hal_gatt_gatt_id));
+		ev.char_prop = char_prop;
+		ev.status = GATT_SUCCESS;
+	} else {
+		memset(&ev.char_id, 0, sizeof(struct hal_gatt_gatt_id));
+		ev.char_prop = 0;
+		ev.status = GATT_FAILURE;
+	}
+
+	memcpy(&ev.srvc_id, service, sizeof(struct hal_gatt_srvc_id));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC,
+					sizeof(ev), &ev);
+}
+
+static void convert_send_client_char_notify(const struct characteristic *ch,
+						int32_t conn_id,
+						const struct service *service)
+{
+	struct hal_gatt_srvc_id srvc;
+	struct hal_gatt_gatt_id charac;
+
+	element_id_to_hal_srvc_id(&service->id, service->primary, &srvc);
+
+	if (ch) {
+		element_id_to_hal_gatt_id(&ch->id, &charac);
+		send_client_char_notify(&srvc, &charac, ch->ch.properties,
+								conn_id);
+	} else {
+		send_client_char_notify(&srvc, NULL, 0, conn_id);
+	}
+}
+
+static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics)
+{
+	uint16_t inst_id = 0;
+	bt_uuid_t uuid;
+
+	for (; characteristics; characteristics = characteristics->next) {
+		struct characteristic *ch;
+
+		ch = new0(struct characteristic, 1);
+		ch->descriptors = queue_new();
+
+		memcpy(&ch->ch, characteristics->data, sizeof(ch->ch));
+
+		bt_string_to_uuid(&uuid, ch->ch.uuid);
+		bt_uuid_to_uuid128(&uuid, &ch->id.uuid);
+
+		/*
+		 * For now we increment inst_id and use it as characteristic
+		 * handle
+		 */
+		ch->id.instance = ++inst_id;
+
+		/* Store end handle to use later for descriptors discovery */
+		if (characteristics->next) {
+			struct gatt_char *next = characteristics->next->data;
+
+			ch->end_handle = next->handle - 1;
+		} else {
+			ch->end_handle = srvc->primary ? srvc->prim.range.end :
+							srvc->incl.range.end;
+		}
+
+		DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s",
+				ch->ch.handle, ch->end_handle, ch->ch.uuid);
+
+		queue_push_tail(srvc->chars, ch);
+	}
+}
+
+struct discover_char_data {
+	int32_t conn_id;
+	struct service *service;
+};
+
+static void discover_char_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct discover_char_data *data = user_data;
+	struct service *srvc = data->service;
+
+	if (status) {
+		error("gatt: Failed to get characteristics: %s",
+							att_ecode2str(status));
+		convert_send_client_char_notify(NULL, data->conn_id, srvc);
+		goto done;
+	}
+
+	if (queue_isempty(srvc->chars))
+		cache_all_srvc_chars(srvc, characteristics);
+
+	convert_send_client_char_notify(queue_peek_head(srvc->chars),
+							data->conn_id, srvc);
+
+done:
+	free(data);
+}
+
+static void handle_client_get_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_characteristic *cmd = buf;
+	struct characteristic *ch;
+	struct element_id match_id;
+	struct app_connection *conn;
+	struct service *srvc;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) {
+		error("Invalid get characteristic size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+	if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* Discover all characteristics for services if not cached yet */
+	if (queue_isempty(srvc->chars)) {
+		struct discover_char_data *cb_data;
+		struct att_range range;
+
+		cb_data = new0(struct discover_char_data, 1);
+		cb_data->service = srvc;
+		cb_data->conn_id = conn->id;
+
+		range = srvc->primary ? srvc->prim.range : srvc->incl.range;
+
+		if (!gatt_discover_char(conn->device->attrib, range.start,
+						range.end, NULL,
+						discover_char_cb, cb_data)) {
+			free(cb_data);
+
+			status = HAL_STATUS_FAILED;
+			goto done;
+		}
+
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	if (cmd->continuation)
+		ch = queue_find(srvc->chars, match_char_by_higher_inst_id,
+					INT_TO_PTR(cmd->char_id[0].inst_id));
+	else
+		ch = queue_peek_head(srvc->chars);
+
+	convert_send_client_char_notify(ch, conn->id, srvc);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_char_notify(&cmd->srvc_id, NULL, 0, cmd->conn_id);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
+}
+
+static void send_client_descr_notify(int32_t status, int32_t conn_id,
+					bool primary,
+					const struct element_id *srvc,
+					const struct element_id *ch,
+					const struct element_id *opt_descr)
+{
+	struct hal_ev_gatt_client_get_descriptor ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.status = status;
+	ev.conn_id = conn_id;
+
+	element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id);
+	element_id_to_hal_gatt_id(ch, &ev.char_id);
+
+	if (opt_descr)
+		element_id_to_hal_gatt_id(opt_descr, &ev.descr_id);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev);
+}
+
+struct discover_desc_data {
+	struct app_connection *conn;
+	struct service *srvc;
+	struct characteristic *ch;
+};
+
+static void gatt_discover_desc_cb(guint8 status, GSList *descs,
+							gpointer user_data)
+{
+	struct discover_desc_data *data = user_data;
+	struct app_connection *conn = data->conn;
+	struct service *srvc = data->srvc;
+	struct characteristic *ch = data->ch;
+	struct descriptor *descr;
+	int i = 0;
+
+	if (status != 0) {
+		error("Discover all characteristic descriptors failed [%s]: %s",
+					ch->ch.uuid, att_ecode2str(status));
+		goto reply;
+	}
+
+	for ( ; descs; descs = descs->next) {
+		struct gatt_desc *desc = descs->data;
+		bt_uuid_t uuid;
+
+		descr = new0(struct descriptor, 1);
+
+		bt_string_to_uuid(&uuid, desc->uuid);
+		bt_uuid_to_uuid128(&uuid, &descr->id.uuid);
+
+		descr->id.instance = ++i;
+		descr->handle = desc->handle;
+
+		DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid);
+
+		queue_push_tail(ch->descriptors, descr);
+	}
+
+reply:
+	descr = queue_peek_head(ch->descriptors);
+
+	send_client_descr_notify(status ? GATT_FAILURE : GATT_SUCCESS, conn->id,
+					srvc->primary, &srvc->id, &ch->id,
+					descr ? &descr->id : NULL);
+
+	free(data);
+}
+
+static bool build_descr_cache(struct app_connection *conn, struct service *srvc,
+						struct characteristic *ch)
+{
+	struct discover_desc_data *cb_data;
+	uint16_t start, end;
+
+	/* Clip range to given characteristic */
+	start = ch->ch.value_handle + 1;
+	end = ch->end_handle;
+
+	/* If there are no descriptors, notify with fail status. */
+	if (start > end)
+		return false;
+
+	cb_data = new0(struct discover_desc_data, 1);
+	cb_data->conn = conn;
+	cb_data->srvc = srvc;
+	cb_data->ch = ch;
+
+	if (!gatt_discover_desc(conn->device->attrib, start, end, NULL,
+					gatt_discover_desc_cb, cb_data)) {
+		free(cb_data);
+		return false;
+	}
+
+	return true;
+}
+
+static void handle_client_get_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
+	struct descriptor *descr = NULL;
+	struct characteristic *ch;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	struct app_connection *conn;
+	int32_t conn_id;
+	uint8_t primary;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) +
+			(cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) {
+		error("gatt: Invalid get descr command (%u bytes), terminating",
+									len);
+
+		raise(SIGTERM);
+		return;
+	}
+
+	conn_id = cmd->conn_id;
+	primary = cmd->srvc_id.is_primary;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+	if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+		error("gatt: Get descr. could not find service");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Get descr. could not find characteristic");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (queue_isempty(ch->descriptors)) {
+		if (build_descr_cache(conn, srvc, ch)) {
+			ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+					HAL_STATUS_SUCCESS);
+			return;
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+	/* Send from cache */
+	if (cmd->continuation)
+		descr = queue_find(ch->descriptors,
+					match_descr_by_higher_inst_id,
+					INT_TO_PTR(cmd->descr_id[0].inst_id));
+	else
+		descr = queue_peek_head(ch->descriptors);
+
+failed:
+	send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id,
+						primary, &srvc_id, &char_id,
+						&descr->id);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status);
+}
+
+struct char_op_data {
+	int32_t conn_id;
+	const struct element_id *srvc_id;
+	const struct element_id *char_id;
+	uint8_t primary;
+};
+
+static struct char_op_data *create_char_op_data(int32_t conn_id,
+						const struct element_id *s_id,
+						const struct element_id *ch_id,
+						bool primary)
+{
+	struct char_op_data *d;
+
+	d = new0(struct char_op_data, 1);
+	d->conn_id = conn_id;
+	d->srvc_id = s_id;
+	d->char_id = ch_id;
+	d->primary = primary;
+
+	return d;
+}
+
+static void send_client_read_char_notify(int32_t status, const uint8_t *pdu,
+						uint16_t len, int32_t conn_id,
+						const struct element_id *s_id,
+						const struct element_id *ch_id,
+						uint8_t primary)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf;
+	ssize_t vlen;
+
+	memset(buf, 0, sizeof(buf));
+
+	ev->conn_id = conn_id;
+	ev->status = status;
+	ev->data.status = status;
+
+	element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id);
+	element_id_to_hal_gatt_id(ch_id, &ev->data.char_id);
+
+	if (status == 0 && pdu) {
+		vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf));
+		if (vlen < 0) {
+			error("gatt: Protocol error");
+			ev->status = GATT_FAILURE;
+		} else {
+			ev->data.len = vlen;
+		}
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC,
+					sizeof(*ev) + ev->data.len, ev);
+}
+
+static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct char_op_data *data = user_data;
+
+	send_client_read_char_notify(status, pdu, len, data->conn_id,
+						data->srvc_id, data->char_id,
+						data->primary);
+
+	free(data);
+}
+
+static int get_cid(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	uint16_t cid;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get CID");
+		return -1;
+	}
+
+	return cid;
+}
+
+static int get_sec_level(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get sec_level");
+		return -1;
+	}
+
+	return sec_level;
+}
+
+static bool set_security(struct gatt_device *device, int req_sec_level)
+{
+	int sec_level;
+	GError *gerr = NULL;
+	GIOChannel *io;
+
+	sec_level = get_sec_level(device);
+	if (sec_level < 0)
+		return false;
+
+	if (req_sec_level <= sec_level)
+		return true;
+
+	io = g_attrib_get_channel(device->attrib);
+	if (!io)
+		return false;
+
+	bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level,
+							BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: Failed to set security level: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	return true;
+}
+
+bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level)
+{
+	struct gatt_device *device;
+
+	device = find_device_by_addr(bdaddr);
+	if (!device)
+		return false;
+
+	return set_security(device, sec_level);
+}
+
+static bool set_auth_type(struct gatt_device *device, int auth_type)
+{
+	int sec_level;
+
+	switch (auth_type) {
+	case HAL_GATT_AUTHENTICATION_MITM:
+		sec_level = BT_SECURITY_HIGH;
+		break;
+	case HAL_GATT_AUTHENTICATION_NO_MITM:
+		sec_level = BT_SECURITY_MEDIUM;
+		break;
+	case HAL_GATT_AUTHENTICATION_NONE:
+		sec_level = BT_SECURITY_LOW;
+		break;
+	default:
+		error("gatt: Invalid auth_type value: %d", auth_type);
+		return false;
+	}
+
+	return set_security(device, sec_level);
+}
+
+static void handle_client_read_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
+	struct char_op_data *cb_data;
+	struct characteristic *ch;
+	struct app_connection *conn;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	uint8_t status;
+
+	DBG("");
+
+	/* TODO authorization needs to be handled */
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+	if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* search characteristics by element id */
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Characteristic with inst_id: %d not found",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+						cmd->srvc_id.is_primary);
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
+	if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle,
+						read_char_cb, cb_data)) {
+		error("gatt: Cannot read characteristic with inst_id: %d",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status);
+
+	/*
+	 * We should send notification with service, characteristic id in case
+	 * of errors.
+	 */
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_read_char_notify(GATT_FAILURE, NULL, 0,
+						cmd->conn_id, &srvc_id,
+						&char_id,
+						cmd->srvc_id.is_primary);
+}
+
+static void send_client_write_char_notify(int32_t status, int32_t conn_id,
+					const struct element_id *srvc_id,
+					const struct element_id *char_id,
+					uint8_t primary)
+{
+	struct hal_ev_gatt_client_write_characteristic ev;
+
+	memset(&ev, 0, sizeof(ev));
+
+	ev.conn_id = conn_id;
+	ev.status = status;
+	ev.data.status = status;
+
+	element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id);
+	element_id_to_hal_gatt_id(char_id, &ev.data.char_id);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC,
+					sizeof(ev), &ev);
+}
+
+static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct char_op_data *data = user_data;
+
+	send_client_write_char_notify(status, data->conn_id, data->srvc_id,
+						data->char_id, data->primary);
+
+	free(data);
+}
+
+static guint signed_write_cmd(struct gatt_device *dev, uint16_t handle,
+					const uint8_t *value, uint16_t vlen)
+{
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+	guint res;
+
+	memset(csrk, 0, 16);
+
+	if (!bt_get_csrk(&dev->bdaddr, true, csrk, &sign_cnt, NULL)) {
+		error("gatt: Could not get csrk key");
+		return 0;
+	}
+
+	res = gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto,
+						csrk, sign_cnt, NULL, NULL);
+	if (!res) {
+		error("gatt: Signed write command failed");
+		return 0;
+	}
+
+	bt_update_sign_counter(&dev->bdaddr, true, ++sign_cnt);
+
+	return res;
+}
+
+static void handle_client_write_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
+	struct char_op_data *cb_data = NULL;
+	struct characteristic *ch;
+	struct app_connection *conn;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	uint8_t status;
+	guint res;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid write char size (%u bytes), terminating", len);
+		raise(SIGTERM);
+		return;
+	}
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+	if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* search characteristics by instance id */
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Characteristic with inst_id: %d not found",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->write_type == GATT_WRITE_TYPE_PREPARE ||
+				cmd->write_type == GATT_WRITE_TYPE_DEFAULT) {
+		cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+						cmd->srvc_id.is_primary);
+	}
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	switch (cmd->write_type) {
+	case GATT_WRITE_TYPE_NO_RESPONSE:
+		res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle,
+							cmd->value, cmd->len,
+							NULL, NULL);
+		break;
+	case GATT_WRITE_TYPE_PREPARE:
+		res = gatt_reliable_write_char(conn->device->attrib,
+							ch->ch.value_handle,
+							cmd->value, cmd->len,
+							write_char_cb, cb_data);
+		break;
+	case GATT_WRITE_TYPE_DEFAULT:
+		res = gatt_write_char(conn->device->attrib, ch->ch.value_handle,
+							cmd->value, cmd->len,
+							write_char_cb, cb_data);
+		break;
+	case GATT_WRITE_TYPE_SIGNED:
+		if (get_cid(conn->device) != ATT_CID) {
+			error("gatt: Cannot write signed on BR/EDR bearer");
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		if (get_sec_level(conn->device) > BT_SECURITY_LOW)
+			res = gatt_write_cmd(conn->device->attrib,
+						ch->ch.value_handle, cmd->value,
+						cmd->len, NULL, NULL);
+		else
+			res = signed_write_cmd(conn->device,
+						ch->ch.value_handle, cmd->value,
+						cmd->len);
+		break;
+	default:
+		error("gatt: Write type %d unsupported", cmd->write_type);
+		status = HAL_STATUS_UNSUPPORTED;
+		goto failed;
+	}
+
+	if (!res) {
+		error("gatt: Cannot write char. with inst_id: %d",
+							cmd->char_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status);
+
+	/*
+	 * We should send notification with service, characteristic id in case
+	 * of error and write with no response
+	 */
+	if (status != HAL_STATUS_SUCCESS ||
+			cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE ||
+			cmd->write_type == GATT_WRITE_TYPE_SIGNED) {
+		int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+						GATT_SUCCESS : GATT_FAILURE;
+
+		send_client_write_char_notify(gatt_status, cmd->conn_id,
+						&srvc_id, &char_id,
+						cmd->srvc_id.is_primary);
+		free(cb_data);
+	}
+}
+
+static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu,
+						guint16 len, int32_t conn_id,
+						const struct element_id *srvc,
+						const struct element_id *ch,
+						const struct element_id *descr,
+						uint8_t primary)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	ev->status = status;
+	ev->conn_id = conn_id;
+	ev->data.status = ev->status;
+
+	element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+	element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+	element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+
+	if (status == 0 && pdu) {
+		ssize_t ret;
+
+		ret = dec_read_resp(pdu, len, ev->data.value,
+							GATT_MAX_ATTR_LEN);
+		if (ret < 0) {
+			error("gatt: Protocol error");
+			ev->status = GATT_FAILURE;
+		} else {
+			ev->data.len = ret;
+		}
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_READ_DESCRIPTOR,
+					sizeof(*ev) + ev->data.len, ev);
+}
+
+struct desc_data {
+	int32_t conn_id;
+	const struct element_id *srvc_id;
+	const struct element_id *char_id;
+	const struct element_id *descr_id;
+	uint8_t primary;
+};
+
+static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct desc_data *cb_data = user_data;
+
+	if (status != 0)
+		error("gatt: Discover all char descriptors failed: %s",
+							att_ecode2str(status));
+
+	send_client_descr_read_notify(status, pdu, len, cb_data->conn_id,
+					cb_data->srvc_id, cb_data->char_id,
+					cb_data->descr_id, cb_data->primary);
+
+	free(cb_data);
+}
+
+static struct desc_data *create_desc_data(int32_t conn_id,
+						const struct element_id *s_id,
+						const struct element_id *ch_id,
+						const struct element_id *d_id,
+						uint8_t primary)
+{
+	struct desc_data *d;
+
+	d = new0(struct desc_data, 1);
+	d->conn_id = conn_id;
+	d->srvc_id = s_id;
+	d->char_id = ch_id;
+	d->descr_id = d_id;
+	d->primary = primary;
+
+	return d;
+}
+
+static void handle_client_read_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_descriptor *cmd = buf;
+	struct desc_data *cb_data;
+	struct characteristic *ch;
+	struct descriptor *descr;
+	struct service *srvc;
+	struct element_id char_id;
+	struct element_id descr_id;
+	struct element_id srvc_id;
+	struct app_connection *conn;
+	int32_t conn_id = 0;
+	uint8_t primary;
+	uint8_t status;
+
+	DBG("");
+
+	conn_id = cmd->conn_id;
+	primary = cmd->srvc_id.is_primary;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+	hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+	if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+		error("gatt: Read descr. could not find service");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Read descr. could not find characteristic");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	descr = queue_find(ch->descriptors, match_descr_by_element_id,
+								&descr_id);
+	if (!descr) {
+		error("gatt: Read descr. could not find descriptor");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id,
+								primary);
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		free(cb_data);
+		goto failed;
+	}
+
+	if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb,
+								cb_data)) {
+		free(cb_data);
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id,
+						&srvc_id, &char_id, &descr_id,
+						primary);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status);
+}
+
+static void send_client_descr_write_notify(int32_t status, int32_t conn_id,
+						const struct element_id *srvc,
+						const struct element_id *ch,
+						const struct element_id *descr,
+						uint8_t primary) {
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	ev->status = status;
+	ev->conn_id = conn_id;
+
+	element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+	element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+	element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+	ev->data.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR,
+					sizeof(*ev), ev);
+}
+
+static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct desc_data *cb_data = user_data;
+
+	if (status)
+		error("gatt: Write descriptors failed: %s",
+							att_ecode2str(status));
+
+	send_client_descr_write_notify(status, cb_data->conn_id,
+					cb_data->srvc_id, cb_data->char_id,
+					cb_data->descr_id, cb_data->primary);
+
+	free(cb_data);
+}
+
+static void handle_client_write_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_write_descriptor *cmd = buf;
+	struct desc_data *cb_data = NULL;
+	struct characteristic *ch;
+	struct descriptor *descr;
+	struct service *srvc;
+	struct element_id srvc_id;
+	struct element_id char_id;
+	struct element_id descr_id;
+	struct app_connection *conn;
+	int32_t conn_id;
+	uint8_t primary;
+	uint8_t status;
+	guint res;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid write desriptor command (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	primary = cmd->srvc_id.is_primary;
+	conn_id = cmd->conn_id;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+	hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+	hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+	if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+		error("gatt: Write descr. could not find service");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+	if (!ch) {
+		error("gatt: Write descr. could not find characteristic");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	descr = queue_find(ch->descriptors, match_descr_by_element_id,
+								&descr_id);
+	if (!descr) {
+		error("gatt: Write descr. could not find descriptor");
+
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE)
+		cb_data = create_desc_data(conn_id, &srvc->id, &ch->id,
+							&descr->id, primary);
+
+	if (!set_auth_type(conn->device, cmd->auth_req)) {
+		error("gatt: Failed to set security %d", cmd->auth_req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	switch (cmd->write_type) {
+	case GATT_WRITE_TYPE_NO_RESPONSE:
+		res = gatt_write_cmd(conn->device->attrib, descr->handle,
+					cmd->value, cmd->len, NULL , NULL);
+		break;
+	case GATT_WRITE_TYPE_PREPARE:
+		res = gatt_reliable_write_char(conn->device->attrib,
+						descr->handle, cmd->value,
+						cmd->len, write_descr_cb,
+						cb_data);
+		break;
+	case GATT_WRITE_TYPE_DEFAULT:
+		res = gatt_write_char(conn->device->attrib, descr->handle,
+						cmd->value, cmd->len,
+						write_descr_cb, cb_data);
+		break;
+	default:
+		error("gatt: Write type %d unsupported", cmd->write_type);
+		status = HAL_STATUS_UNSUPPORTED;
+		goto failed;
+	}
+
+	if (!res) {
+		error("gatt: Write desc, could not write desc");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	if (status != HAL_STATUS_SUCCESS ||
+			cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) {
+		int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+						GATT_SUCCESS : GATT_FAILURE;
+
+		send_client_descr_write_notify(gatt_status, conn_id, &srvc_id,
+						&char_id, &descr_id, primary);
+		free(cb_data);
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status);
+}
+
+static void send_client_write_execute_notify(int32_t id, int32_t status)
+{
+	struct hal_ev_gatt_client_exec_write ev;
+
+	ev.conn_id = id;
+	ev.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_EXEC_WRITE,
+					sizeof(ev), &ev);
+}
+
+static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	send_client_write_execute_notify(PTR_TO_INT(user_data), status);
+}
+
+static void handle_client_execute_write(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_execute_write *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+	uint8_t flags;
+
+	DBG("");
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES :
+						ATT_CANCEL_ALL_PREP_WRITES;
+
+	if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb,
+						INT_TO_PTR(cmd->conn_id))) {
+		error("gatt: Could not send execute write");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status);
+
+	/* In case of early error send also notification.*/
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE);
+}
+
+static void handle_notification(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_client_notify *ev = (void *) buf;
+	struct notification_data *notification = user_data;
+	uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t);
+
+	if (len < data_offset)
+		return;
+
+	memcpy(&ev->char_id, &notification->ch, sizeof(ev->char_id));
+	memcpy(&ev->srvc_id, &notification->service, sizeof(ev->srvc_id));
+	bdaddr2android(&notification->conn->device->bdaddr, &ev->bda);
+	ev->conn_id = notification->conn->id;
+	ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY;
+
+	/* We have to cut opcode and handle from data */
+	ev->len = len - data_offset;
+	memcpy(ev->value, pdu + data_offset, len - data_offset);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY,
+						sizeof(*ev) + ev->len, ev);
+}
+
+static void send_register_for_notification_ev(int32_t id, int32_t registered,
+					int32_t status,
+					const struct hal_gatt_srvc_id *srvc,
+					const struct hal_gatt_gatt_id *ch)
+{
+	struct hal_ev_gatt_client_reg_for_notif ev;
+
+	ev.conn_id = id;
+	ev.status = status;
+	ev.registered = registered;
+	memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id));
+	memcpy(&ev.char_id, ch, sizeof(ev.char_id));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev);
+}
+
+static void handle_client_register_for_notification(const void *buf,
+								uint16_t len)
+{
+	const struct hal_cmd_gatt_client_register_for_notification *cmd = buf;
+	struct notification_data *notification;
+	struct characteristic *c;
+	struct element_id match_id;
+	struct app_connection *conn;
+	int32_t conn_id = 0;
+	struct service *service;
+	uint8_t status;
+	int32_t gatt_status;
+	bdaddr_t addr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	conn = find_conn(&addr, cmd->client_if);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	conn_id = conn->id;
+
+	hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+	service = queue_find(conn->device->services, match_srvc_by_element_id,
+								&match_id);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	hal_gatt_id_to_element_id(&cmd->char_id, &match_id);
+	c = queue_find(service->chars, match_char_by_element_id, &match_id);
+	if (!c) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	notification = new0(struct notification_data, 1);
+
+	memcpy(&notification->ch, &cmd->char_id, sizeof(notification->ch));
+	memcpy(&notification->service, &cmd->srvc_id,
+						sizeof(notification->service));
+	notification->conn = conn;
+
+	if (queue_find(conn->app->notifications, match_notification,
+								notification)) {
+		free(notification);
+		status = HAL_STATUS_SUCCESS;
+		goto failed;
+	}
+
+	notification->notif_id = g_attrib_register(conn->device->attrib,
+							ATT_OP_HANDLE_NOTIFY,
+							c->ch.value_handle,
+							handle_notification,
+							notification,
+							destroy_notification);
+	if (!notification->notif_id) {
+		free(notification);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	notification->ind_id = g_attrib_register(conn->device->attrib,
+							ATT_OP_HANDLE_IND,
+							c->ch.value_handle,
+							handle_notification,
+							notification,
+							destroy_notification);
+	if (!notification->ind_id) {
+		g_attrib_unregister(conn->device->attrib,
+							notification->notif_id);
+		free(notification);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/*
+	 * Because same data - notification - is shared by two handlers, we
+	 * introduce ref counter to be sure that data can be freed with no risk.
+	 * Counter is decremented in destroy_notification.
+	 */
+	notification->ref = 2;
+
+	queue_push_tail(conn->app->notifications, notification);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+	send_register_for_notification_ev(conn_id, 1, gatt_status,
+						&cmd->srvc_id, &cmd->char_id);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status);
+}
+
+static void handle_client_deregister_for_notification(const void *buf,
+								uint16_t len)
+{
+	const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf;
+	struct notification_data *notification, notif;
+	struct app_connection *conn;
+	int32_t conn_id = 0;
+	uint8_t status;
+	int32_t gatt_status;
+	bdaddr_t addr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	conn = find_conn(&addr, cmd->client_if);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	conn_id = conn->id;
+
+	memcpy(&notif.ch, &cmd->char_id, sizeof(notif.ch));
+	memcpy(&notif.service, &cmd->srvc_id, sizeof(notif.service));
+	notif.conn = conn;
+
+	notification = queue_find(conn->app->notifications,
+						match_notification, &notif);
+	if (!notification) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	unregister_notification(notification);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+	send_register_for_notification_ev(conn_id, 0, gatt_status,
+						&cmd->srvc_id, &cmd->char_id);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status);
+}
+
+static void send_client_remote_rssi_notify(int32_t client_if,
+						const bdaddr_t *addr,
+						int32_t rssi, int32_t status)
+{
+	struct hal_ev_gatt_client_read_remote_rssi ev;
+
+	ev.client_if = client_if;
+	bdaddr2android(addr, &ev.address);
+	ev.rssi = rssi;
+	ev.status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev);
+}
+
+static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr,
+						int8_t rssi, void *user_data)
+{
+	int32_t client_if = PTR_TO_INT(user_data);
+	int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+
+	send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status);
+}
+
+static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf;
+	uint8_t status;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	if (!find_app_by_id(cmd->client_if)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+	if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb,
+						INT_TO_PTR(cmd->client_if))) {
+		error("gatt: Could not read RSSI");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status);
+
+	if (status != HAL_STATUS_SUCCESS)
+		send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0,
+								GATT_FAILURE);
+}
+
+static void handle_client_get_device_type(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_get_device_type *cmd = buf;
+	struct hal_rsp_gatt_client_get_device_type rsp;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	rsp.type = bt_get_device_android_type(&bdaddr);
+
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+					sizeof(rsp), &rsp, -1);
+}
+
+static void handle_client_set_adv_data(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_set_adv_data *cmd = buf;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->manufacturer_len) {
+		error("Invalid set adv data command (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d",
+		cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower,
+		cmd->min_interval, cmd->max_interval, cmd->appearance);
+
+	DBG("manufacturer=%u service_data=%u service_uuid=%u",
+				cmd->manufacturer_len, cmd->service_data_len,
+				cmd->service_uuid_len);
+
+	/* TODO This should be implemented when kernel supports it */
+	if (cmd->manufacturer_len || cmd->service_data_len ||
+							cmd->service_uuid_len) {
+		error("gatt: Extra advertising data not supported");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SET_ADV_DATA, status);
+}
+
+static void test_command_result(guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data)
+{
+	DBG("status: %d", status);
+}
+
+static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op,
+						uint16_t u2, uint16_t u3,
+						uint16_t u4, uint16_t u5)
+{
+	guint16 length = 0;
+	struct gatt_device *dev;
+	uint8_t *pdu;
+	size_t mtu;
+
+	dev = find_device_by_addr(bdaddr);
+	if (!dev || dev->state != DEVICE_CONNECTED)
+		return HAL_STATUS_FAILED;
+
+	pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+	if (!pdu)
+		return HAL_STATUS_FAILED;
+
+	switch (op) {
+	case ATT_OP_READ_REQ:
+		length = enc_read_req(u2, pdu, mtu);
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+		length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu);
+		break;
+	case ATT_OP_READ_BLOB_REQ:
+		length = enc_read_blob_req(u2, u3, pdu, mtu);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu);
+		break;
+	case ATT_OP_READ_MULTI_REQ:
+		return HAL_STATUS_UNSUPPORTED;
+	case ATT_OP_WRITE_REQ:
+		length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+									mtu);
+		break;
+	case ATT_OP_WRITE_CMD:
+		length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu,
+									mtu);
+		break;
+	case ATT_OP_PREP_WRITE_REQ:
+		length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4),
+								pdu, mtu);
+		break;
+	case ATT_OP_EXEC_WRITE_REQ:
+		length = enc_exec_write_req(u2, pdu, mtu);
+		break;
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3)))
+			return HAL_STATUS_SUCCESS;
+		else
+			return HAL_STATUS_FAILED;
+	default:
+		error("gatt: Unknown operation type");
+
+		return HAL_STATUS_UNSUPPORTED;
+	}
+
+	if (!g_attrib_send(dev->attrib, 0, pdu, length, test_command_result,
+								NULL, NULL))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1)
+{
+	struct gatt_device *device;
+
+	device = find_device_by_addr(bdaddr);
+	if (!device)
+		return HAL_STATUS_FAILED;
+
+	if (!set_auth_type(device, u1))
+		return HAL_STATUS_FAILED;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_test_command(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_test_command *cmd = buf;
+	struct gatt_app *app;
+	bdaddr_t bdaddr;
+	bt_uuid_t uuid;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bda1, &bdaddr);
+	android2uuid(cmd->uuid1, &uuid);
+
+	switch (cmd->command) {
+	case GATT_CLIENT_TEST_CMD_ENABLE:
+		if (cmd->u1) {
+			if (!test_client_if) {
+				app = register_app(TEST_UUID, GATT_CLIENT);
+				if (app)
+					test_client_if = app->id;
+			}
+
+			if (test_client_if)
+				status = HAL_STATUS_SUCCESS;
+			else
+				status = HAL_STATUS_FAILED;
+		} else {
+			status = unregister_app(test_client_if);
+			test_client_if = 0;
+		}
+		break;
+	case GATT_CLIENT_TEST_CMD_CONNECT:
+		/* TODO u1 holds device type, for now assume BLE */
+		status = handle_connect(test_client_if, &bdaddr, false);
+		break;
+	case GATT_CLIENT_TEST_CMD_DISCONNECT:
+		app = queue_find(gatt_apps, match_app_by_id,
+						INT_TO_PTR(test_client_if));
+		queue_remove_all(app_connections, match_connection_by_app, app,
+							destroy_connection);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case GATT_CLIENT_TEST_CMD_DISCOVER:
+		status = HAL_STATUS_FAILED;
+		break;
+	case GATT_CLIENT_TEST_CMD_READ:
+	case GATT_CLIENT_TEST_CMD_WRITE:
+		status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2,
+						cmd->u3, cmd->u4, cmd->u5);
+		break;
+	case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY:
+		status = test_increase_security(&bdaddr, cmd->u1);
+		break;
+	case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG:
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_TEST_COMMAND, status);
+}
+
+static void handle_server_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_register *cmd = buf;
+	struct hal_ev_gatt_server_register ev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	app = register_app(cmd->uuid, GATT_SERVER);
+
+	if (app) {
+		ev.server_if = app->id;
+		ev.status = GATT_SUCCESS;
+	} else {
+		ev.status = GATT_FAILURE;
+	}
+
+	memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+							HAL_STATUS_SUCCESS);
+}
+
+static void handle_server_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_unregister *cmd = buf;
+	uint8_t status;
+
+	DBG("");
+
+	status = unregister_app(cmd->server_if);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_UNREGISTER, status);
+}
+
+static void handle_server_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_connect *cmd = buf;
+	uint8_t status;
+	bdaddr_t addr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &addr);
+
+	/* TODO: Handle transport flag */
+
+	status = handle_connect(cmd->server_if, &addr, cmd->is_direct);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+								status);
+}
+
+static void handle_server_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_disconnect *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+
+	DBG("");
+
+	/* TODO: should we care to match also bdaddr when conn_id is unique? */
+	conn = queue_remove_if(app_connections, match_connection_by_id,
+						INT_TO_PTR(cmd->conn_id));
+	destroy_connection(conn);
+
+	status = HAL_STATUS_SUCCESS;
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_DISCONNECT, status);
+}
+
+static void handle_server_add_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_service *cmd = buf;
+	struct hal_ev_gatt_server_service_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *service;
+	uint8_t status;
+	bt_uuid_t uuid;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2uuid(cmd->srvc_id.uuid, &uuid);
+
+	service = gatt_db_add_service(gatt_db, &uuid, cmd->srvc_id.is_primary,
+							cmd->num_handles);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.srvc_handle = gatt_db_attribute_get_handle(service);
+	if (!ev.srvc_handle) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.srvc_id = cmd->srvc_id;
+	ev.server_if = cmd->server_if;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_SERVICE, status);
+}
+
+static void handle_server_add_included_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_inc_service *cmd = buf;
+	struct hal_ev_gatt_server_inc_srvc_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *service, *include;
+	uint8_t status;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	service = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	include = gatt_db_get_attribute(gatt_db, cmd->included_handle);
+	if (!include) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	service = gatt_db_service_add_included(service, include);
+	if (!service) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.incl_srvc_handle = gatt_db_attribute_get_handle(service);
+	status = HAL_STATUS_SUCCESS;
+failed:
+	ev.srvc_handle = cmd->service_handle;
+	ev.status = status;
+	ev.server_if = cmd->server_if;
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
+}
+
+static bool is_service(const bt_uuid_t *type)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	if (!bt_uuid_cmp(&uuid, type))
+		return true;
+
+	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+	if (!bt_uuid_cmp(&uuid, type))
+		return true;
+
+	return false;
+}
+
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+	const struct pending_request *pending_request = data;
+
+	return !pending_request->completed;
+}
+
+static void send_dev_complete_response(struct gatt_device *device,
+								uint8_t opcode)
+{
+	size_t mtu;
+	uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
+	struct pending_request *val;
+	uint16_t len = 0;
+	uint8_t error = 0;
+
+	if (queue_isempty(device->pending_requests))
+		return;
+
+	if (queue_find(device->pending_requests, match_pending_dev_request,
+									NULL)) {
+		DBG("Still pending requests");
+		return;
+	}
+
+	val = queue_peek_head(device->pending_requests);
+	if (!val) {
+		error = ATT_ECODE_ATTR_NOT_FOUND;
+		goto done;
+	}
+
+	if (val->error) {
+		error = val->error;
+		goto done;
+	}
+
+	switch (opcode) {
+	case ATT_OP_READ_BY_TYPE_REQ: {
+		struct att_data_list *adl;
+		int iterator = 0;
+		int length;
+		struct queue *temp;
+
+		temp = queue_new();
+
+		val = queue_pop_head(device->pending_requests);
+		if (!val) {
+			queue_destroy(temp, NULL);
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+			goto done;
+		}
+
+		if (val->error) {
+			queue_destroy(temp, NULL);
+			error = val->error;
+			destroy_pending_request(val);
+			goto done;
+		}
+
+		length = val->length;
+
+		while (val && val->length == length && val->error == 0) {
+			queue_push_tail(temp, val);
+			val = queue_pop_head(device->pending_requests);
+		}
+
+		adl = att_data_list_alloc(queue_length(temp),
+						sizeof(uint16_t) + length);
+
+		destroy_pending_request(val);
+
+		val = queue_pop_head(temp);
+		while (val) {
+			uint8_t *value = adl->data[iterator++];
+			uint16_t handle;
+
+			handle = gatt_db_attribute_get_handle(val->attrib);
+
+			put_le16(handle, value);
+			memcpy(&value[2], val->value, val->length);
+
+			destroy_pending_request(val);
+			val = queue_pop_head(temp);
+		}
+
+		len = enc_read_by_type_resp(adl, rsp, mtu);
+
+		att_data_list_free(adl);
+		queue_destroy(temp, destroy_pending_request);
+
+		break;
+	}
+	case ATT_OP_READ_BLOB_REQ:
+		len = enc_read_blob_resp(val->value, val->length, val->offset,
+								rsp, mtu);
+		break;
+	case ATT_OP_READ_REQ:
+		len = enc_read_resp(val->value, val->length, rsp, mtu);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ: {
+		struct att_data_list *adl;
+		int iterator = 0;
+		int length;
+		struct queue *temp;
+
+		temp = queue_new();
+
+		val = queue_pop_head(device->pending_requests);
+		if (!val) {
+			queue_destroy(temp, NULL);
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+			goto done;
+		}
+
+		length = val->length;
+
+		while (val && val->length == length) {
+			queue_push_tail(temp, val);
+			val = queue_pop_head(device->pending_requests);
+		}
+
+		adl = att_data_list_alloc(queue_length(temp),
+						2 * sizeof(uint16_t) + length);
+
+		val = queue_pop_head(temp);
+		while (val) {
+			uint8_t *value = adl->data[iterator++];
+			uint16_t start_handle, end_handle;
+
+			gatt_db_attribute_get_service_handles(val->attrib,
+								&start_handle,
+								&end_handle);
+
+			put_le16(start_handle, value);
+			put_le16(end_handle, &value[2]);
+			memcpy(&value[4], val->value, val->length);
+
+			destroy_pending_request(val);
+			val = queue_pop_head(temp);
+		}
+
+		len = enc_read_by_grp_resp(adl, rsp, mtu);
+
+		att_data_list_free(adl);
+		queue_destroy(temp, destroy_pending_request);
+
+		break;
+	}
+	case ATT_OP_FIND_BY_TYPE_REQ: {
+		GSList *list = NULL;
+
+		val = queue_pop_head(device->pending_requests);
+		while (val) {
+			struct att_range *range;
+			const bt_uuid_t *type;
+
+			/* Its find by type and value - filter by value here */
+			if ((val->length != val->filter_vlen) ||
+				memcmp(val->value, val->filter_value,
+								val->length)) {
+
+				destroy_pending_request(val);
+				val = queue_pop_head(device->pending_requests);
+				continue;
+			}
+
+			range = new0(struct att_range, 1);
+			range->start = gatt_db_attribute_get_handle(
+								val->attrib);
+
+			type = gatt_db_attribute_get_type(val->attrib);
+			if (is_service(type))
+				gatt_db_attribute_get_service_handles(
+								val->attrib,
+								NULL,
+								&range->end);
+			else
+				range->end = range->start;
+
+			list = g_slist_append(list, range);
+
+			destroy_pending_request(val);
+			val = queue_pop_head(device->pending_requests);
+		}
+
+		if (list && !error)
+			len = enc_find_by_type_resp(list, rsp, mtu);
+		else
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+
+		g_slist_free_full(list, free);
+
+		break;
+	}
+	case ATT_OP_EXEC_WRITE_REQ:
+		len = enc_exec_write_resp(rsp);
+		break;
+	case ATT_OP_WRITE_REQ:
+		len = enc_write_resp(rsp);
+		break;
+	case ATT_OP_PREP_WRITE_REQ: {
+		uint16_t handle;
+
+		handle = gatt_db_attribute_get_handle(val->attrib);
+		len = enc_prep_write_resp(handle, val->offset, val->value,
+							val->length, rsp, mtu);
+		break;
+	}
+	default:
+		break;
+	}
+
+done:
+	if (!len)
+		len = enc_error_resp(opcode, 0x0000, error, rsp, mtu);
+
+	g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+	queue_remove_all(device->pending_requests, NULL, NULL,
+						destroy_pending_request);
+}
+
+struct request_processing_data {
+	uint8_t opcode;
+	struct gatt_device *device;
+};
+
+static uint8_t check_device_permissions(struct gatt_device *device,
+					uint8_t opcode, uint32_t permissions)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(device->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID))
+		return ATT_ECODE_UNLIKELY;
+
+	DBG("opcode 0x%02x permissions %u sec_level %u", opcode, permissions,
+								sec_level);
+
+	switch (opcode) {
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (!(permissions & GATT_PERM_WRITE_SIGNED))
+				return ATT_ECODE_WRITE_NOT_PERM;
+
+		if (permissions & GATT_PERM_WRITE_SIGNED_MITM) {
+			bool auth;
+
+			if (bt_get_csrk(&device->bdaddr, true, NULL, NULL,
+					&auth) && auth)
+				break;
+
+			return ATT_ECODE_AUTHENTICATION;
+		}
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_FIND_BY_TYPE_REQ:
+	case ATT_OP_FIND_INFO_REQ:
+		if (!(permissions & GATT_PERM_READ))
+			return ATT_ECODE_READ_NOT_PERM;
+
+		if ((permissions & GATT_PERM_READ_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_READ_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_EXEC_WRITE_REQ:
+		if (!(permissions & GATT_PERM_WRITE))
+			return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((permissions & GATT_PERM_WRITE_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	default:
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	return 0;
+}
+
+static uint8_t err_to_att(int err)
+{
+	if (!err || (err > 0 && err < UINT8_MAX))
+		return err;
+
+	switch (err) {
+	case -ENOENT:
+		return ATT_ECODE_INVALID_HANDLE;
+	case -ENOMEM:
+		return ATT_ECODE_INSUFF_RESOURCES;
+	default:
+		return ATT_ECODE_UNLIKELY;
+	}
+}
+
+static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
+					const uint8_t *value, size_t length,
+					void *user_data)
+{
+	struct pending_request *resp_data = user_data;
+	uint8_t error = err_to_att(err);
+
+	resp_data->attrib = attrib;
+	resp_data->length = length;
+	resp_data->error = error;
+
+	resp_data->completed = true;
+
+	if (!length)
+		return;
+
+	resp_data->value = malloc0(length);
+	if (!resp_data->value) {
+		resp_data->error = ATT_ECODE_INSUFF_RESOURCES;
+
+		return;
+	}
+
+	memcpy(resp_data->value, value, length);
+}
+
+static void read_requested_attributes(void *data, void *user_data)
+{
+	struct pending_request *resp_data = data;
+	struct request_processing_data *process_data = user_data;
+	struct bt_att *att = g_attrib_get_att(process_data->device->attrib);
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint8_t error;
+
+	attrib = resp_data->attrib;
+	if (!attrib) {
+		resp_data->error = ATT_ECODE_ATTR_NOT_FOUND;
+		resp_data->completed = true;
+		return;
+	}
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	/*
+	 * Check if it is attribute we didn't declare permissions, like service
+	 * declaration or included service. Set permissions to read only
+	 */
+	if (permissions == 0)
+		permissions = GATT_PERM_READ;
+
+	error = check_device_permissions(process_data->device,
+							process_data->opcode,
+							permissions);
+	if (error != 0) {
+		resp_data->error = error;
+		resp_data->completed = true;
+		return;
+	}
+
+	gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode,
+					att, attribute_read_cb, resp_data);
+}
+
+static void process_dev_pending_requests(struct gatt_device *device,
+							uint8_t att_opcode)
+{
+	struct request_processing_data process_data;
+
+	if (queue_isempty(device->pending_requests))
+		return;
+
+	process_data.device = device;
+	process_data.opcode = att_opcode;
+
+	/* Process pending requests and prepare response */
+	queue_foreach(device->pending_requests, read_requested_attributes,
+								&process_data);
+
+	send_dev_complete_response(device, att_opcode);
+}
+
+static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
+					uint8_t opcode,
+					struct gatt_db_attribute *attrib,
+					unsigned int serial_id)
+{
+	struct pending_trans_data *transaction;
+	static int32_t trans_id = 1;
+
+	transaction = new0(struct pending_trans_data, 1);
+	transaction->id = trans_id++;
+	transaction->opcode = opcode;
+	transaction->attrib = attrib;
+	transaction->serial_id = serial_id;
+
+	queue_push_tail(conn->transactions, transaction);
+
+	return transaction;
+}
+
+static bool get_dst_addr(struct bt_att *att, bdaddr_t *dst)
+{
+	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_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;
+}
+
+static void read_cb(struct gatt_db_attribute *attrib, unsigned int id,
+			uint16_t offset, uint8_t opcode, struct bt_att *att,
+			void *user_data)
+{
+	struct pending_trans_data *transaction;
+	struct hal_ev_gatt_server_request_read ev;
+	struct gatt_app *app;
+	struct app_connection *conn;
+	int32_t app_id = PTR_TO_INT(user_data);
+	bdaddr_t bdaddr;
+
+	DBG("id %u", id);
+
+	app = find_app_by_id(app_id);
+	if (!app) {
+		error("gatt: read_cb, cound not found app id");
+		goto failed;
+	}
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: read_cb, could not obtain dst BDADDR");
+		goto failed;
+	}
+
+	conn = find_conn(&bdaddr, app->id);
+	if (!conn) {
+		error("gatt: read_cb, cound not found connection");
+		goto failed;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+
+	/* Store the request data, complete callback and transaction id */
+	transaction = conn_add_transact(conn, opcode, attrib, id);
+
+	bdaddr2android(&bdaddr, ev.bdaddr);
+	ev.conn_id = conn->id;
+	ev.attr_handle = gatt_db_attribute_get_handle(attrib);
+	ev.offset = offset;
+	ev.is_long = opcode == ATT_OP_READ_BLOB_REQ;
+	ev.trans_id = transaction->id;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_SERVER_REQUEST_READ,
+					sizeof(ev), &ev);
+
+	return;
+
+failed:
+	gatt_db_attribute_read_result(attrib, id, -ENOENT, NULL, 0);
+}
+
+static void write_cb(struct gatt_db_attribute *attrib, unsigned int id,
+			uint16_t offset, const uint8_t *value, size_t len,
+			uint8_t opcode, struct bt_att *att, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_gatt_server_request_write *ev = (void *) buf;
+	struct pending_trans_data *transaction;
+	struct gatt_app *app;
+	int32_t app_id = PTR_TO_INT(user_data);
+	struct app_connection *conn;
+	bdaddr_t bdaddr;
+
+	DBG("id %u", id);
+
+	app = find_app_by_id(app_id);
+	if (!app) {
+		error("gatt: write_cb could not found app id");
+		goto failed;
+	}
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: write_cb, could not obtain dst BDADDR");
+		goto failed;
+	}
+
+	conn = find_conn(&bdaddr, app->id);
+	if (!conn) {
+		error("gatt: write_cb could not found connection");
+		goto failed;
+	}
+
+	/*
+	 * Remember that this application has ongoing prep write
+	 * Need it later to find out where to send execute write
+	 */
+	if (opcode == ATT_OP_PREP_WRITE_REQ)
+		conn->wait_execute_write = true;
+
+	/* Store the request data, complete callback and transaction id */
+	transaction = conn_add_transact(conn, opcode, attrib, id);
+
+	memset(ev, 0, sizeof(*ev));
+
+	bdaddr2android(&bdaddr, &ev->bdaddr);
+	ev->attr_handle = gatt_db_attribute_get_handle(attrib);
+	ev->offset = offset;
+
+	ev->conn_id = conn->id;
+	ev->trans_id = transaction->id;
+
+	ev->is_prep = opcode == ATT_OP_PREP_WRITE_REQ;
+
+	if (opcode == ATT_OP_WRITE_REQ || opcode == ATT_OP_PREP_WRITE_REQ)
+		ev->need_rsp = 0x01;
+	else
+		gatt_db_attribute_write_result(attrib, id, 0);
+
+	ev->length = len;
+	memcpy(ev->value, value, len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_REQUEST_WRITE,
+						sizeof(*ev) + ev->length , ev);
+	return;
+
+failed:
+	gatt_db_attribute_write_result(attrib, id, ATT_ECODE_UNLIKELY);
+}
+
+static uint32_t android_to_gatt_permissions(int32_t hal_permissions)
+{
+	uint32_t permissions = 0;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ)
+		permissions |= GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED)
+		permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM)
+		permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED |
+								GATT_PERM_READ;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE)
+		permissions |= GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED)
+		permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM)
+		permissions |= GATT_PERM_WRITE_MITM |
+				GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED)
+		permissions |= GATT_PERM_WRITE_SIGNED;
+
+	if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM)
+		permissions |= GATT_PERM_WRITE_SIGNED_MITM |
+							GATT_PERM_WRITE_SIGNED;
+
+	return permissions;
+}
+
+static void handle_server_add_characteristic(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
+	struct hal_ev_gatt_server_characteristic_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	bt_uuid_t uuid;
+	uint8_t status;
+	uint32_t permissions;
+	int32_t app_id = cmd->server_if;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(app_id);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2uuid(cmd->uuid, &uuid);
+	permissions = android_to_gatt_permissions(cmd->permissions);
+
+	attrib = gatt_db_service_add_characteristic(attrib,
+							&uuid, permissions,
+							cmd->properties,
+							read_cb, write_cb,
+							INT_TO_PTR(app_id));
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.char_handle = gatt_db_attribute_get_handle(attrib);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.srvc_handle = cmd->service_handle;
+	ev.status = status;
+	ev.server_if = app_id;
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status);
+}
+
+static void handle_server_add_descriptor(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_add_descriptor *cmd = buf;
+	struct hal_ev_gatt_server_descriptor_added ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	bt_uuid_t uuid;
+	uint8_t status;
+	uint32_t permissions;
+	int32_t app_id = cmd->server_if;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(app_id);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2uuid(cmd->uuid, &uuid);
+	permissions = android_to_gatt_permissions(cmd->permissions);
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_service_add_descriptor(attrib, &uuid, permissions,
+							read_cb, write_cb,
+							INT_TO_PTR(app_id));
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ev.descr_handle = gatt_db_attribute_get_handle(attrib);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.server_if = app_id;
+	ev.srvc_handle = cmd->service_handle;
+	memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
+}
+
+static void notify_service_change(void *data, void *user_data)
+{
+	struct att_range range;
+	struct gatt_db_attribute *attrib = user_data;
+
+	gatt_db_attribute_get_service_handles(attrib, &range.start, &range.end);
+
+	/* In case of db error */
+	if (!range.end)
+		return;
+
+	notify_att_range_change(data, &range);
+}
+
+static sdp_record_t *get_sdp_record(uuid_t *uuid, uint16_t start, uint16_t end,
+							const char *name)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+	uuid_t root_uuid, proto_uuid, l2cap;
+	sdp_record_t *record;
+	sdp_data_t *psm, *sh, *eh;
+	uint16_t lp = ATT_PSM;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	svclass_id = sdp_list_append(NULL, uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&proto_uuid, ATT_UUID);
+	proto[1] = sdp_list_append(NULL, &proto_uuid);
+	sh = sdp_data_alloc(SDP_UINT16, &start);
+	proto[1] = sdp_list_append(proto[1], sh);
+	eh = sdp_data_alloc(SDP_UINT16, &end);
+	proto[1] = sdp_list_append(proto[1], eh);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	if (name)
+		sdp_set_info_attr(record, name, "BlueZ for Android", NULL);
+
+	sdp_data_free(psm);
+	sdp_data_free(sh);
+	sdp_data_free(eh);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return record;
+}
+
+static uint32_t add_sdp_record(const bt_uuid_t *uuid, uint16_t start,
+						uint16_t end, const char *name)
+{
+	sdp_record_t *rec;
+	uuid_t u, u32;
+
+	switch (uuid->type) {
+	case BT_UUID16:
+		sdp_uuid16_create(&u, uuid->value.u16);
+		break;
+	case BT_UUID32:
+		sdp_uuid32_create(&u32, uuid->value.u32);
+		sdp_uuid32_to_uuid128(&u, &u32);
+		break;
+	case BT_UUID128:
+		sdp_uuid128_create(&u, &uuid->value.u128);
+		break;
+	case BT_UUID_UNSPEC:
+	default:
+		return 0;
+	}
+
+	rec = get_sdp_record(&u, start, end, name);
+	if (!rec)
+		return 0;
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("gatt: Failed to register SDP record");
+		sdp_record_free(rec);
+		return 0;
+	}
+
+	return rec->handle;
+}
+
+static bool match_service_sdp(const void *data, const void *user_data)
+{
+	const struct service_sdp *s = data;
+
+	return s->service_handle == PTR_TO_INT(user_data);
+}
+
+static struct service_sdp *new_service_sdp_record(int32_t service_handle)
+{
+	bt_uuid_t uuid;
+	struct service_sdp *s;
+	struct gatt_db_attribute *attrib;
+	uint16_t end_handle;
+
+	attrib = gatt_db_get_attribute(gatt_db, service_handle);
+	if (!attrib)
+		return NULL;
+
+	gatt_db_attribute_get_service_handles(attrib, NULL, &end_handle);
+	if (!end_handle)
+		return NULL;
+
+	if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
+		return NULL;
+
+	s = new0(struct service_sdp, 1);
+	s->service_handle = service_handle;
+	s->sdp_handle = add_sdp_record(&uuid, service_handle, end_handle, NULL);
+	if (!s->sdp_handle) {
+		free(s);
+		return NULL;
+	}
+
+	return s;
+}
+
+static void free_service_sdp_record(void *data)
+{
+	struct service_sdp *s = data;
+
+	if (!s)
+		return;
+
+	bt_adapter_remove_record(s->sdp_handle);
+	free(s);
+}
+
+static bool add_service_sdp_record(int32_t service_handle)
+{
+	struct service_sdp *s;
+
+	s = queue_find(services_sdp, match_service_sdp,
+						INT_TO_PTR(service_handle));
+	if (s)
+		return true;
+
+	s = new_service_sdp_record(service_handle);
+	if (!s)
+		return false;
+
+	queue_push_tail(services_sdp, s);
+
+	return true;
+}
+
+static void remove_service_sdp_record(int32_t service_handle)
+{
+	struct service_sdp *s;
+
+	s = queue_remove_if(services_sdp, match_service_sdp,
+						INT_TO_PTR(service_handle));
+	if (!s)
+		return;
+
+	free_service_sdp_record(s);
+}
+
+static void handle_server_start_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_start_service *cmd = buf;
+	struct hal_ev_gatt_server_service_started ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	uint8_t status;
+
+	DBG("transport 0x%02x", cmd->transport);
+
+	memset(&ev, 0, sizeof(ev));
+
+	if (cmd->transport == 0) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (cmd->transport & GATT_SERVER_TRANSPORT_BREDR_BIT) {
+		if (!add_service_sdp_record(cmd->service_handle)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+	}
+	/* TODO: Handle BREDR only */
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!gatt_db_service_set_active(attrib, true)) {
+		/*
+		 * no need to clean SDP since this can fail only if service
+		 * handle is invalid in which case add_sdp_record() also fails
+		 */
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	queue_foreach(gatt_devices, notify_service_change, attrib);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.server_if = cmd->server_if;
+	ev.srvc_handle = cmd->service_handle;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_START_SERVICE, status);
+}
+
+static void handle_server_stop_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_stop_service *cmd = buf;
+	struct hal_ev_gatt_server_service_stopped ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	uint8_t status;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!gatt_db_service_set_active(attrib, false)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	remove_service_sdp_record(cmd->service_handle);
+
+	status = HAL_STATUS_SUCCESS;
+
+	queue_foreach(gatt_devices, notify_service_change, attrib);
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.server_if = cmd->server_if;
+	ev.srvc_handle = cmd->service_handle;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_STOP_SERVICE, status);
+}
+
+static void handle_server_delete_service(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_delete_service *cmd = buf;
+	struct hal_ev_gatt_server_service_deleted ev;
+	struct gatt_app *server;
+	struct gatt_db_attribute *attrib;
+	uint8_t status;
+
+	DBG("");
+
+	memset(&ev, 0, sizeof(ev));
+
+	server = find_app_by_id(cmd->server_if);
+	if (!server) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle);
+	if (!attrib) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!gatt_db_remove_service(gatt_db, attrib)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	remove_service_sdp_record(cmd->service_handle);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+	ev.srvc_handle = cmd->service_handle;
+	ev.server_if = cmd->server_if;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
+}
+
+static void indication_confirmation_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct hal_ev_gatt_server_indication_sent ev;
+
+	ev.status = status;
+	ev.conn_id = PTR_TO_UINT(user_data);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_INDICATION_SENT, sizeof(ev), &ev);
+}
+
+static void handle_server_send_indication(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_send_indication *cmd = buf;
+	struct app_connection *conn;
+	uint8_t status;
+	uint16_t length;
+	uint8_t *pdu;
+	size_t mtu;
+	GAttribResultFunc confirmation_cb = NULL;
+
+	DBG("");
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		error("gatt: Could not find connection");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+	if (cmd->confirm) {
+		length = enc_indication(cmd->attribute_handle,
+					(uint8_t *) cmd->value, cmd->len, pdu,
+					mtu);
+		confirmation_cb = indication_confirmation_cb;
+	} else {
+		length = enc_notification(cmd->attribute_handle,
+						(uint8_t *) cmd->value,
+						cmd->len, pdu, mtu);
+	}
+
+	if (!g_attrib_send(conn->device->attrib, 0, pdu, length,
+				confirmation_cb, UINT_TO_PTR(conn->id), NULL)) {
+		error("gatt: Failed to send indication");
+		status = HAL_STATUS_FAILED;
+	} else {
+		status = HAL_STATUS_SUCCESS;
+	}
+
+	/* Here we confirm failed indications and all notifications */
+	if (status || !confirmation_cb)
+		indication_confirmation_cb(status, NULL, 0,
+							UINT_TO_PTR(conn->id));
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_SERVER_SEND_INDICATION, status);
+}
+
+static bool match_trans_id(const void *data, const void *user_data)
+{
+	const struct pending_trans_data *transaction = data;
+
+	return transaction->id == PTR_TO_UINT(user_data);
+}
+
+static bool find_conn_waiting_exec_write(const void *data,
+							const void *user_data)
+{
+	const struct app_connection *conn = data;
+
+	return conn->wait_execute_write;
+}
+
+static bool pending_execute_write(void)
+{
+	return queue_find(app_connections, find_conn_waiting_exec_write, NULL);
+}
+
+static void handle_server_send_response(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_server_send_response *cmd = buf;
+	struct pending_trans_data *transaction;
+	struct app_connection *conn;
+	uint8_t status;
+
+	DBG("");
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		error("gatt: could not found connection");
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	transaction = queue_remove_if(conn->transactions, match_trans_id,
+						UINT_TO_PTR(cmd->trans_id));
+	if (!transaction) {
+		error("gatt: transaction ID = %d not found", cmd->trans_id);
+		status = HAL_STATUS_FAILED;
+		goto reply;
+	}
+
+	if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) {
+		struct pending_request *req;
+
+		conn->wait_execute_write = false;
+
+		/* Check for execute response from all server applications */
+		if (pending_execute_write())
+			goto done;
+
+		/*
+		 * This is usually done through db write callback but for
+		 * execute write we dont have the attribute or handle to call
+		 * gatt_db_attribute_write().
+		 */
+		req = queue_peek_head(conn->device->pending_requests);
+		if (!req)
+			goto done;
+
+		/* Cast status to uint8_t, due to (byte) cast in java layer. */
+		req->error = err_to_att((uint8_t) cmd->status);
+		req->completed = true;
+
+		/*
+		 * FIXME: Handle situation when not all server applications
+		 * respond with a success.
+		 */
+	}
+
+	/* Cast status to uint8_t, due to (byte) cast in java layer. */
+	if (transaction->opcode < ATT_OP_WRITE_REQ)
+		gatt_db_attribute_read_result(transaction->attrib,
+					transaction->serial_id,
+					err_to_att((uint8_t) cmd->status),
+					cmd->data, cmd->len);
+	else
+		gatt_db_attribute_write_result(transaction->attrib,
+					transaction->serial_id,
+					err_to_att((uint8_t) cmd->status));
+
+	send_dev_complete_response(conn->device, transaction->opcode);
+
+done:
+	/* Clean request data */
+	free(transaction);
+
+	status = HAL_STATUS_SUCCESS;
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_SEND_RESPONSE, status);
+}
+
+static void handle_client_scan_filter_setup(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_setup *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_add_remove(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE,
+				HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_clear(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_clear *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_scan_filter_enable(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_scan_filter_enable *cmd = buf;
+
+	DBG("client_if %u", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_configure_mtu(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_configure_mtu *cmd = buf;
+	static struct app_connection *conn;
+	uint8_t status;
+
+	DBG("conn_id %u mtu %d", cmd->conn_id, cmd->mtu);
+
+	conn = find_connection_by_id(cmd->conn_id);
+	if (!conn) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/*
+	 * currently MTU is always exchanged on connection, just report current
+	 * value
+	 *
+	 * TODO figure out when send failed status in notification
+	 * TODO should we fail for BR/EDR?
+	 */
+	notify_client_mtu_change(conn, false);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
+					status);
+}
+
+static void handle_client_conn_param_update(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_conn_param_update *cmd = buf;
+	char address[18];
+	bdaddr_t bdaddr;
+
+	android2bdaddr(cmd->address, &bdaddr);
+	ba2str(&bdaddr, address);
+
+	DBG("%s", address);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_set_scan_param(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_set_scan_param *cmd = buf;
+
+	DBG("interval %d window %d", cmd->interval, cmd->window);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SET_SCAN_PARAM,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_setup_multi_adv(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_setup_multi_adv *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_update_multi_adv(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_update_multi_adv *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+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;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+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;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+				HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_configure_batchscan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_configure_batchscan *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_enable_batchscan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_enable_batchscan *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_disable_batchscan(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_disable_batchscan *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN,
+					HAL_STATUS_UNSUPPORTED);
+}
+
+static void handle_client_read_batchscan_reports(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_gatt_client_read_batchscan_reports *cmd = buf;
+
+	DBG("client_if %d", cmd->client_if);
+
+	/* TODO */
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS,
+				HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_GATT_CLIENT_REGISTER */
+	{ handle_client_register, false,
+		sizeof(struct hal_cmd_gatt_client_register) },
+	/* HAL_OP_GATT_CLIENT_UNREGISTER */
+	{ handle_client_unregister, false,
+		sizeof(struct hal_cmd_gatt_client_unregister) },
+	/* HAL_OP_GATT_CLIENT_SCAN */
+	{ handle_client_scan, false,
+		sizeof(struct hal_cmd_gatt_client_scan) },
+	/* HAL_OP_GATT_CLIENT_CONNECT */
+	{ handle_client_connect, false,
+		sizeof(struct hal_cmd_gatt_client_connect) },
+	/* HAL_OP_GATT_CLIENT_DISCONNECT */
+	{ handle_client_disconnect, false,
+		sizeof(struct hal_cmd_gatt_client_disconnect) },
+	/* HAL_OP_GATT_CLIENT_LISTEN */
+	{ handle_client_listen, false,
+		sizeof(struct hal_cmd_gatt_client_listen) },
+	/* HAL_OP_GATT_CLIENT_REFRESH */
+	{ handle_client_refresh, false,
+		sizeof(struct hal_cmd_gatt_client_refresh) },
+	/* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */
+	{ handle_client_search_service, true,
+		sizeof(struct hal_cmd_gatt_client_search_service) },
+	/* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */
+	{ handle_client_get_included_service, true,
+		sizeof(struct hal_cmd_gatt_client_get_included_service) },
+	/* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */
+	{ handle_client_get_characteristic, true,
+		sizeof(struct hal_cmd_gatt_client_get_characteristic) },
+	/* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */
+	{ handle_client_get_descriptor, true,
+		sizeof(struct hal_cmd_gatt_client_get_descriptor) },
+	/* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */
+	{ handle_client_read_characteristic, false,
+		sizeof(struct hal_cmd_gatt_client_read_characteristic) },
+	/* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */
+	{ handle_client_write_characteristic, true,
+		sizeof(struct hal_cmd_gatt_client_write_characteristic) },
+	/* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */
+	{ handle_client_read_descriptor, false,
+		sizeof(struct hal_cmd_gatt_client_read_descriptor) },
+	/* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */
+	{ handle_client_write_descriptor, true,
+		sizeof(struct hal_cmd_gatt_client_write_descriptor) },
+	/* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */
+	{ handle_client_execute_write, false,
+		sizeof(struct hal_cmd_gatt_client_execute_write)},
+	/* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */
+	{ handle_client_register_for_notification, false,
+		sizeof(struct hal_cmd_gatt_client_register_for_notification) },
+	/* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */
+	{ handle_client_deregister_for_notification, false,
+		sizeof(struct hal_cmd_gatt_client_deregister_for_notification) },
+	/* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */
+	{ handle_client_read_remote_rssi, false,
+		sizeof(struct hal_cmd_gatt_client_read_remote_rssi) },
+	/* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */
+	{ handle_client_get_device_type, false,
+		sizeof(struct hal_cmd_gatt_client_get_device_type) },
+	/* HAL_OP_GATT_CLIENT_SET_ADV_DATA */
+	{ handle_client_set_adv_data, true,
+		sizeof(struct hal_cmd_gatt_client_set_adv_data) },
+	/* HAL_OP_GATT_CLIENT_TEST_COMMAND */
+	{ handle_client_test_command, false,
+		sizeof(struct hal_cmd_gatt_client_test_command) },
+	/* HAL_OP_GATT_SERVER_REGISTER */
+	{ handle_server_register, false,
+		sizeof(struct hal_cmd_gatt_server_register) },
+	/* HAL_OP_GATT_SERVER_UNREGISTER */
+	{ handle_server_unregister, false,
+		sizeof(struct hal_cmd_gatt_server_unregister) },
+	/* HAL_OP_GATT_SERVER_CONNECT */
+	{ handle_server_connect, false,
+		sizeof(struct hal_cmd_gatt_server_connect) },
+	/* HAL_OP_GATT_SERVER_DISCONNECT */
+	{ handle_server_disconnect, false,
+		sizeof(struct hal_cmd_gatt_server_disconnect) },
+	/* HAL_OP_GATT_SERVER_ADD_SERVICE */
+	{ handle_server_add_service, false,
+		sizeof(struct hal_cmd_gatt_server_add_service) },
+	/* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */
+	{ handle_server_add_included_service, false,
+		sizeof(struct hal_cmd_gatt_server_add_inc_service) },
+	/* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */
+	{ handle_server_add_characteristic, false,
+		sizeof(struct hal_cmd_gatt_server_add_characteristic) },
+	/* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */
+	{ handle_server_add_descriptor, false,
+		sizeof(struct hal_cmd_gatt_server_add_descriptor) },
+	/* HAL_OP_GATT_SERVER_START_SERVICE */
+	{ handle_server_start_service, false,
+		sizeof(struct hal_cmd_gatt_server_start_service) },
+	/* HAL_OP_GATT_SERVER_STOP_SERVICE */
+	{ handle_server_stop_service, false,
+		sizeof(struct hal_cmd_gatt_server_stop_service) },
+	/* HAL_OP_GATT_SERVER_DELETE_SERVICE */
+	{ handle_server_delete_service, false,
+		sizeof(struct hal_cmd_gatt_server_delete_service) },
+	/* HAL_OP_GATT_SERVER_SEND_INDICATION */
+	{ handle_server_send_indication, true,
+		sizeof(struct hal_cmd_gatt_server_send_indication) },
+	/* HAL_OP_GATT_SERVER_SEND_RESPONSE */
+	{ handle_server_send_response, true,
+		sizeof(struct hal_cmd_gatt_server_send_response) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP */
+	{ handle_client_scan_filter_setup, false,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_setup) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE */
+	{ handle_client_scan_filter_add_remove, true,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_add_remove) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR */
+	{ handle_client_scan_filter_clear, false,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_clear) },
+	/* HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE */
+	{ handle_client_scan_filter_enable, false,
+		sizeof(struct hal_cmd_gatt_client_scan_filter_enable) },
+	/* HAL_OP_GATT_CLIENT_CONFIGURE_MTU */
+	{ handle_client_configure_mtu, false,
+		sizeof(struct hal_cmd_gatt_client_configure_mtu) },
+	/* HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE */
+	{ handle_client_conn_param_update, false,
+		sizeof(struct hal_cmd_gatt_client_conn_param_update) },
+	/* HAL_OP_GATT_CLIENT_SET_SCAN_PARAM */
+	{ handle_client_set_scan_param, false,
+		sizeof(struct hal_cmd_gatt_client_set_scan_param) },
+	/* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV */
+	{ handle_client_setup_multi_adv, false,
+		sizeof(struct hal_cmd_gatt_client_setup_multi_adv) },
+	/* HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV */
+	{ 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,
+		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,
+		sizeof(struct hal_cmd_gatt_client_disable_multi_adv_inst) },
+	/* HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN */
+	{ handle_client_configure_batchscan, false,
+		sizeof(struct hal_cmd_gatt_client_configure_batchscan) },
+	/* HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN */
+	{ handle_client_enable_batchscan, false,
+		sizeof(struct hal_cmd_gatt_client_enable_batchscan) },
+	/* HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN */
+	{ handle_client_disable_batchscan, false,
+		sizeof(struct hal_cmd_gatt_client_disable_batchscan) },
+	/* HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS */
+	{ handle_client_read_batchscan_reports, false,
+		sizeof(struct hal_cmd_gatt_client_read_batchscan_reports) },
+};
+
+static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *device)
+{
+	uint16_t start, end;
+	uint16_t len = 0;
+	bt_uuid_t uuid;
+	struct queue *q;
+
+	DBG("");
+
+	switch (cmd[0]) {
+	case ATT_OP_READ_BY_TYPE_REQ:
+		len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid);
+		break;
+	default:
+		break;
+	}
+
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	q = queue_new();
+
+	switch (cmd[0]) {
+	case ATT_OP_READ_BY_TYPE_REQ:
+		gatt_db_read_by_type(gatt_db, start, end, uuid, q);
+		break;
+	case ATT_OP_READ_BY_GROUP_REQ:
+		gatt_db_read_by_group_type(gatt_db, start, end, uuid, q);
+		break;
+	default:
+		break;
+	}
+
+	if (queue_isempty(q)) {
+		queue_destroy(q, NULL);
+		return ATT_ECODE_ATTR_NOT_FOUND;
+	}
+
+	while (queue_peek_head(q)) {
+		struct pending_request *data;
+		struct gatt_db_attribute *attrib = queue_pop_head(q);
+
+		data = new0(struct pending_request, 1);
+		data->attrib = attrib;
+		queue_push_tail(device->pending_requests, data);
+	}
+
+	queue_destroy(q, NULL);
+	process_dev_pending_requests(device, cmd[0]);
+
+	return 0;
+}
+
+static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
+							struct gatt_device *dev)
+{
+	struct gatt_db_attribute *attrib;
+	uint16_t handle;
+	uint16_t len;
+	uint16_t offset;
+	struct pending_request *data;
+
+	DBG("");
+
+	switch (cmd[0]) {
+	case ATT_OP_READ_BLOB_REQ:
+		len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
+		if (!len)
+			return ATT_ECODE_INVALID_PDU;
+		break;
+	case ATT_OP_READ_REQ:
+		len = dec_read_req(cmd, cmd_len, &handle);
+		if (!len)
+			return ATT_ECODE_INVALID_PDU;
+		offset = 0;
+		break;
+	default:
+		error("gatt: Unexpected read type 0x%02x", cmd[0]);
+		return ATT_ECODE_REQ_NOT_SUPP;
+	}
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (attrib == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	data = new0(struct pending_request, 1);
+	data->offset = offset;
+	data->attrib = attrib;
+	queue_push_tail(dev->pending_requests, data);
+
+	process_dev_pending_requests(dev, cmd[0]);
+
+	return 0;
+}
+
+static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
+							struct gatt_device *dev)
+{
+	uint16_t rmtu, mtu, len;
+	size_t length;
+	uint8_t *rsp;
+
+	DBG("");
+
+	len = dec_mtu_req(cmd, cmd_len, &rmtu);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	/* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+	if (get_cid(dev) != ATT_CID)
+		return ATT_ECODE_UNLIKELY;
+
+	if (!get_local_mtu(dev, &mtu))
+		return ATT_ECODE_UNLIKELY;
+
+	if (!update_mtu(dev, rmtu))
+		return ATT_ECODE_UNLIKELY;
+
+	rsp = g_attrib_get_buffer(dev->attrib, &length);
+
+	/* Respond with our MTU */
+	len = enc_mtu_resp(mtu, rsp, length);
+	if (!g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL))
+		return ATT_ECODE_UNLIKELY;
+
+	return 0;
+}
+
+static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
+				uint8_t *rsp, size_t rsp_size, uint16_t *length)
+{
+	struct gatt_db_attribute *attrib;
+	struct queue *q, *temp;
+	struct att_data_list *adl;
+	int iterator = 0;
+	uint16_t start, end;
+	uint16_t len, queue_len;
+	uint8_t format;
+	uint8_t ret = 0;
+
+	DBG("");
+
+	len = dec_find_info_req(cmd, cmd_len, &start, &end);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	q = queue_new();
+
+	gatt_db_find_information(gatt_db, start, end, q);
+
+	if (queue_isempty(q)) {
+		queue_destroy(q, NULL);
+		return ATT_ECODE_ATTR_NOT_FOUND;
+	}
+
+	temp = queue_new();
+
+	attrib = queue_peek_head(q);
+	/* UUIDS can be only 128 bit and 16 bit */
+	len = bt_uuid_len(gatt_db_attribute_get_type(attrib));
+	if (len != 2 && len != 16) {
+		queue_destroy(q, NULL);
+		queue_destroy(temp, NULL);
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	while (attrib) {
+		const bt_uuid_t *type;
+
+		type = gatt_db_attribute_get_type(attrib);
+		if (bt_uuid_len(type) != len)
+			break;
+
+		queue_push_tail(temp, queue_pop_head(q));
+		attrib = queue_peek_head(q);
+	}
+
+	queue_destroy(q, NULL);
+
+	queue_len = queue_length(temp);
+	adl = att_data_list_alloc(queue_len, len + sizeof(uint16_t));
+	if (!adl) {
+		queue_destroy(temp, NULL);
+		return ATT_ECODE_INSUFF_RESOURCES;
+	}
+
+	while (queue_peek_head(temp)) {
+		uint8_t *value;
+		const bt_uuid_t *type;
+		struct gatt_db_attribute *attrib = queue_pop_head(temp);
+		uint16_t handle;
+
+		type = gatt_db_attribute_get_type(attrib);
+		if (!type)
+			break;
+
+		value = adl->data[iterator++];
+
+		handle = gatt_db_attribute_get_handle(attrib);
+		put_le16(handle, value);
+		memcpy(&value[2], &type->value, len);
+	}
+
+	if (len == 2)
+		format = ATT_FIND_INFO_RESP_FMT_16BIT;
+	else
+		format = ATT_FIND_INFO_RESP_FMT_128BIT;
+
+	len = enc_find_info_resp(format, adl, rsp, rsp_size);
+	if (!len)
+		ret = ATT_ECODE_UNLIKELY;
+
+	*length = len;
+	att_data_list_free(adl);
+	queue_destroy(temp, NULL);
+
+	return ret;
+}
+
+struct find_by_type_request_data {
+	struct gatt_device *device;
+	uint8_t *search_value;
+	size_t search_vlen;
+	uint8_t error;
+};
+
+static void find_by_type_request_cb(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	struct find_by_type_request_data *find_data = user_data;
+	struct pending_request *request_data;
+
+	if (find_data->error)
+		return;
+
+	request_data = new0(struct pending_request, 1);
+	request_data->filter_value = malloc0(find_data->search_vlen);
+	if (!request_data->filter_value) {
+		destroy_pending_request(request_data);
+		find_data->error = ATT_ECODE_INSUFF_RESOURCES;
+		return;
+	}
+
+	request_data->attrib = attrib;
+	request_data->filter_vlen = find_data->search_vlen;
+	memcpy(request_data->filter_value, find_data->search_value,
+							find_data->search_vlen);
+
+	queue_push_tail(find_data->device->pending_requests, request_data);
+}
+
+static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *device)
+{
+	uint8_t search_value[cmd_len];
+	size_t search_vlen;
+	uint16_t start, end;
+	bt_uuid_t uuid;
+	uint16_t len;
+	struct find_by_type_request_data data;
+
+	DBG("");
+
+	len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid,
+						search_value, &search_vlen);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (start > end || start == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	data.error = 0;
+	data.search_vlen = search_vlen;
+	data.search_value = search_value;
+	data.device = device;
+
+	if (gatt_db_find_by_type(gatt_db, start, end, &uuid,
+					find_by_type_request_cb, &data) == 0) {
+		size_t mtu;
+		uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
+
+		len = enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+					ATT_ECODE_ATTR_NOT_FOUND, rsp, mtu);
+		g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+		return 0;
+	}
+
+	if (!data.error)
+		process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
+
+	return data.error;
+}
+
+static void write_confirm(struct gatt_db_attribute *attrib,
+						int err, void *user_data)
+{
+	if (!err)
+		return;
+
+	error("Error writting attribute %p", attrib);
+}
+
+static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+
+	len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
+	if (!len)
+		return;
+
+	if (handle == 0)
+		return;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						write_confirm, NULL);
+}
+
+static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	uint8_t s[ATT_SIGNATURE_LEN];
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+
+	if (get_cid(dev) != ATT_CID) {
+		error("gatt: Remote tries write signed on BR/EDR bearer");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (get_sec_level(dev) != BT_SECURITY_LOW) {
+		error("gatt: Remote tries write signed on encrypted link");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (!bt_get_csrk(&dev->bdaddr, false, csrk, &sign_cnt, NULL)) {
+		error("gatt: No valid csrk from remote device");
+		return;
+	}
+
+	len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
+
+	if (handle == 0)
+		return;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	if (len) {
+		uint8_t t[ATT_SIGNATURE_LEN];
+		uint32_t r_sign_cnt = get_le32(s);
+
+		if (r_sign_cnt < sign_cnt) {
+			error("gatt: Invalid sign counter (%d<%d)",
+							r_sign_cnt, sign_cnt);
+			return;
+		}
+
+		/* Generate signature and verify it */
+		if (!bt_crypto_sign_att(crypto, csrk, cmd,
+						cmd_len - ATT_SIGNATURE_LEN,
+						r_sign_cnt, t)) {
+			error("gatt: Error when generating att signature");
+			return;
+		}
+
+		if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
+			error("gatt: signature does not match");
+			return;
+		}
+		/* Signature OK, proceed with write */
+		bt_update_sign_counter(&dev->bdaddr, false, r_sign_cnt);
+		gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						write_confirm, NULL);
+	}
+}
+
+static void attribute_write_cb(struct gatt_db_attribute *attrib, int err,
+								void *user_data)
+{
+	struct pending_request *data = user_data;
+	uint8_t error = err_to_att(err);
+
+	DBG("");
+
+	data->attrib = attrib;
+	data->error = error;
+	data->completed = true;
+}
+
+static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	struct pending_request *data;
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	uint8_t error;
+	size_t vlen;
+
+	len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return ATT_ECODE_ATTR_NOT_FOUND;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	error = check_device_permissions(dev, cmd[0], permissions);
+	if (error)
+		return error;
+
+	data = new0(struct pending_request, 1);
+	data->attrib = attrib;
+
+	queue_push_tail(dev->pending_requests, data);
+
+	if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						attribute_write_cb, data)) {
+		queue_remove(dev->pending_requests, data);
+		free(data);
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	send_dev_complete_response(dev, cmd[0]);
+
+	return 0;
+}
+
+static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	uint8_t value[cmd_len];
+	struct pending_request *data;
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t offset;
+	uint8_t error;
+	uint16_t len;
+	size_t vlen;
+
+	len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
+						value, &vlen);
+	if (!len)
+		return ATT_ECODE_INVALID_PDU;
+
+	if (handle == 0)
+		return ATT_ECODE_INVALID_HANDLE;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return ATT_ECODE_ATTR_NOT_FOUND;
+
+	permissions = gatt_db_attribute_get_permissions(attrib);
+
+	error = check_device_permissions(dev, cmd[0], permissions);
+	if (error)
+		return error;
+
+	data = new0(struct pending_request, 1);
+	data->attrib = attrib;
+	data->offset = offset;
+
+	queue_push_tail(dev->pending_requests, data);
+
+	data->value = g_memdup(value, vlen);
+	data->length = vlen;
+
+	if (!gatt_db_attribute_write(attrib, offset, value, vlen, cmd[0],
+						g_attrib_get_att(dev->attrib),
+						attribute_write_cb, data)) {
+		queue_remove(dev->pending_requests, data);
+		g_free(data->value);
+		free(data);
+
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	send_dev_complete_response(dev, cmd[0]);
+
+	return 0;
+}
+
+static void send_server_write_execute_notify(void *data, void *user_data)
+{
+	struct hal_ev_gatt_server_request_exec_write *ev = user_data;
+	struct pending_trans_data *transaction;
+	struct app_connection *conn = data;
+
+	if (!conn->wait_execute_write)
+		return;
+
+	ev->conn_id = conn->id;
+
+	transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ, NULL, 0);
+
+	ev->trans_id = transaction->id;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE, sizeof(*ev), ev);
+}
+
+static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
+						struct gatt_device *dev)
+{
+	struct hal_ev_gatt_server_request_exec_write ev;
+	uint8_t value;
+	struct pending_request *data;
+
+	/*
+	 * Check if there was any write prep before.
+	 * TODO: Try to find better error code if possible
+	 */
+	if (!pending_execute_write())
+		return ATT_ECODE_UNLIKELY;
+
+	if (!dec_exec_write_req(cmd, cmd_len, &value))
+		return ATT_ECODE_INVALID_PDU;
+
+	memset(&ev, 0, sizeof(ev));
+	bdaddr2android(&dev->bdaddr, &ev.bdaddr);
+	ev.exec_write = value;
+
+	data = new0(struct pending_request, 1);
+
+	queue_push_tail(dev->pending_requests, data);
+
+	queue_foreach(app_connections, send_server_write_execute_notify, &ev);
+	send_dev_complete_response(dev, cmd[0]);
+
+	return 0;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	uint8_t status;
+	uint16_t resp_length = 0;
+	size_t length;
+	uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
+
+	DBG("op 0x%02x", ipdu[0]);
+
+	if (len > length) {
+		error("gatt: Too much data on ATT socket %p", opdu);
+		status = ATT_ECODE_INVALID_PDU;
+		goto done;
+	}
+
+	switch (ipdu[0]) {
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_READ_BY_TYPE_REQ:
+		status = read_by_type(ipdu, len, dev);
+		break;
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+		status = read_request(ipdu, len, dev);
+		break;
+	case ATT_OP_MTU_REQ:
+		status = mtu_att_handle(ipdu, len, dev);
+		break;
+	case ATT_OP_FIND_INFO_REQ:
+		status = find_info_handle(ipdu, len, opdu, length,
+								&resp_length);
+		break;
+	case ATT_OP_WRITE_REQ:
+		status = write_req_request(ipdu, len, dev);
+		break;
+	case ATT_OP_WRITE_CMD:
+		write_cmd_request(ipdu, len, dev);
+		/* No response on write cmd */
+		return;
+	case ATT_OP_SIGNED_WRITE_CMD:
+		write_signed_cmd_request(ipdu, len, dev);
+		/* No response on write signed cmd */
+		return;
+	case ATT_OP_PREP_WRITE_REQ:
+		status = write_prep_request(ipdu, len, dev);
+		break;
+	case ATT_OP_FIND_BY_TYPE_REQ:
+		status = find_by_type_request(ipdu, len, dev);
+		break;
+	case ATT_OP_EXEC_WRITE_REQ:
+		status = write_execute_request(ipdu, len, dev);
+		break;
+	case ATT_OP_READ_MULTI_REQ:
+	default:
+		DBG("Unsupported request 0x%02x", ipdu[0]);
+		status = ATT_ECODE_REQ_NOT_SUPP;
+		break;
+	}
+
+done:
+	if (status)
+		resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+									length);
+
+	g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
+}
+
+static void connect_confirm(GIOChannel *io, void *user_data)
+{
+	struct gatt_device *dev;
+	bdaddr_t dst;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("gatt: bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	/* TODO Handle collision */
+	dev = find_device_by_addr(&dst);
+	if (!dev) {
+		dev = create_device(&dst);
+	} else {
+		if ((dev->state != DEVICE_DISCONNECTED) &&
+					!(dev->state == DEVICE_CONNECT_INIT &&
+					bt_kernel_conn_control())) {
+			char addr[18];
+
+			ba2str(&dst, addr);
+			info("gatt: Rejecting incoming connection from %s",
+									addr);
+			goto drop;
+		}
+	}
+
+	if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) {
+		error("gatt: failed to accept connection");
+		device_unref(dev);
+		goto drop;
+	}
+
+	queue_foreach(listen_apps, create_app_connection, dev);
+	device_set_state(dev, DEVICE_CONNECT_READY);
+
+	return;
+
+drop:
+	g_io_channel_shutdown(io, TRUE, NULL);
+}
+
+struct gap_srvc_handles {
+	struct gatt_db_attribute *srvc;
+
+	/* Characteristics */
+	struct gatt_db_attribute *dev_name;
+	struct gatt_db_attribute *appear;
+	struct gatt_db_attribute *priv;
+};
+
+static struct gap_srvc_handles gap_srvc_data;
+
+#define APPEARANCE_GENERIC_PHONE 0x0040
+#define PERIPHERAL_PRIVACY_DISABLE 0x00
+
+static void device_name_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	const char *name = bt_get_adapter_name();
+
+	gatt_db_attribute_read_result(attrib, id, 0, (void *) name,
+								strlen(name));
+}
+
+static void register_gap_service(void)
+{
+	uint16_t start, end;
+	bt_uuid_t uuid;
+
+	/* GAP UUID */
+	bt_uuid16_create(&uuid, 0x1800);
+	gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7);
+
+	/* Device name characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+	gap_srvc_data.dev_name =
+			gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+							&uuid, GATT_PERM_READ,
+							GATT_CHR_PROP_READ,
+							device_name_read_cb,
+							NULL, NULL);
+
+	/* Appearance */
+	bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+
+	gap_srvc_data.appear =
+			gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+							&uuid, GATT_PERM_READ,
+							GATT_CHR_PROP_READ,
+							NULL, NULL, NULL);
+	if (gap_srvc_data.appear) {
+		uint16_t value;
+		/* Store appearance into db */
+		value = cpu_to_le16(APPEARANCE_GENERIC_PHONE);
+		gatt_db_attribute_write(gap_srvc_data.appear, 0,
+						(void *) &value, sizeof(value),
+						ATT_OP_WRITE_REQ, NULL,
+						write_confirm, NULL);
+	}
+
+	/* Pripheral privacy flag */
+	bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
+	gap_srvc_data.priv =
+			gatt_db_service_add_characteristic(gap_srvc_data.srvc,
+							&uuid, GATT_PERM_READ,
+							GATT_CHR_PROP_READ,
+							NULL, NULL, NULL);
+	if (gap_srvc_data.priv) {
+		uint8_t value;
+
+		/* Store privacy into db */
+		value = PERIPHERAL_PRIVACY_DISABLE;
+		gatt_db_attribute_write(gap_srvc_data.priv, 0,
+						&value, sizeof(value),
+						ATT_OP_WRITE_REQ, NULL,
+						write_confirm, NULL);
+	}
+
+	gatt_db_service_set_active(gap_srvc_data.srvc , true);
+
+	/* SDP */
+	bt_uuid16_create(&uuid, 0x1800);
+	gatt_db_attribute_get_service_handles(gap_srvc_data.srvc, &start, &end);
+	gap_sdp_handle = add_sdp_record(&uuid, start, end,
+						"Generic Access Profile");
+	if (!gap_sdp_handle)
+		error("gatt: Failed to register GAP SDP record");
+}
+
+static void device_info_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	char *buf = user_data;
+
+	gatt_db_attribute_read_result(attrib, id, 0, user_data, strlen(buf));
+}
+
+static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	uint8_t pdu[8];
+
+	put_le64(bt_config_get_system_id(), pdu);
+
+	gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	uint8_t pdu[7];
+
+	pdu[0] = bt_config_get_pnp_source();
+	put_le16(bt_config_get_pnp_vendor(), &pdu[1]);
+	put_le16(bt_config_get_pnp_product(), &pdu[3]);
+	put_le16(bt_config_get_pnp_version(), &pdu[5]);
+
+	gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void register_device_info_service(void)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service;
+	uint16_t start_handle, end_handle;
+	const char *data;
+	uint32_t enc_perm = GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED;
+
+	DBG("");
+
+	/* Device Information Service */
+	bt_uuid16_create(&uuid, 0x180a);
+	service = gatt_db_add_service(gatt_db, &uuid, true, 17);
+
+	/* User data are not const hence (void *) cast is used */
+	data = bt_config_get_name();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	data = bt_config_get_serial();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						enc_perm, GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	if (bt_config_get_system_id()) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
+		gatt_db_service_add_characteristic(service, &uuid,
+						enc_perm, GATT_CHR_PROP_READ,
+						device_info_read_system_id_cb,
+						NULL, NULL);
+	}
+
+	data = bt_config_get_fw_rev();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	data = bt_config_get_hw_rev();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
+	gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ,
+					GATT_CHR_PROP_READ, device_info_read_cb,
+					NULL, VERSION);
+
+	data = bt_config_get_vendor();
+	if (data) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_cb, NULL,
+						(void *) data);
+	}
+
+	if (bt_config_get_pnp_source()) {
+		bt_uuid16_create(&uuid, GATT_CHARAC_PNP_ID);
+		gatt_db_service_add_characteristic(service, &uuid,
+						GATT_PERM_READ,
+						GATT_CHR_PROP_READ,
+						device_info_read_pnp_id_cb,
+						NULL, NULL);
+	}
+
+	gatt_db_service_set_active(service, true);
+
+	/* SDP */
+	bt_uuid16_create(&uuid, 0x180a);
+	gatt_db_attribute_get_service_handles(service, &start_handle,
+								&end_handle);
+	dis_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle,
+						"Device Information Service");
+	if (!dis_sdp_handle)
+		error("gatt: Failed to register DIS SDP record");
+}
+
+static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct gatt_device *dev;
+	bdaddr_t bdaddr;
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: srvc_change_write_cb, could not obtain BDADDR");
+		return;
+	}
+
+	dev = find_device_by_addr(&bdaddr);
+	if (!dev) {
+		error("gatt: Could not find device ?!");
+		return;
+	}
+
+	if (!bt_device_is_bonded(&bdaddr)) {
+		gatt_db_attribute_write_result(attrib, id,
+						ATT_ECODE_AUTHORIZATION);
+		return;
+	}
+
+	/* 2 octets are expected as CCC value */
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+						ATT_ECODE_INVAL_ATTR_VALUE_LEN);
+		return;
+	}
+
+	/* Set services changed indication value */
+	bt_store_gatt_ccc(&bdaddr, get_le16(value));
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct gatt_device *dev;
+	uint8_t pdu[2];
+	bdaddr_t bdaddr;
+
+	if (!get_dst_addr(att, &bdaddr)) {
+		error("gatt: srvc_change_read_cb, could not obtain BDADDR");
+		return;
+	}
+
+	dev = find_device_by_addr(&bdaddr);
+	if (!dev) {
+		error("gatt: Could not find device ?!");
+		return;
+	}
+
+	put_le16(bt_get_gatt_ccc(&dev->bdaddr), pdu);
+
+	gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu));
+}
+
+static void register_gatt_service(void)
+{
+	struct gatt_db_attribute *service;
+	uint16_t start_handle, end_handle;
+	bt_uuid_t uuid;
+
+	DBG("");
+
+	bt_uuid16_create(&uuid, 0x1801);
+	service = gatt_db_add_service(gatt_db, &uuid, true, 4);
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+	service_changed_attrib = gatt_db_service_add_characteristic(service,
+							&uuid, GATT_PERM_NONE,
+							GATT_CHR_PROP_INDICATE,
+							NULL, NULL, NULL);
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+	gatt_db_service_add_descriptor(service, &uuid,
+					GATT_PERM_READ | GATT_PERM_WRITE,
+					gatt_srvc_change_read_cb,
+					gatt_srvc_change_write_cb, NULL);
+
+	gatt_db_service_set_active(service, true);
+
+	/* SDP */
+	bt_uuid16_create(&uuid, 0x1801);
+	gatt_db_attribute_get_service_handles(service, &start_handle,
+								&end_handle);
+	gatt_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle,
+						"Generic Attribute Profile");
+
+	if (!gatt_sdp_handle)
+		error("gatt: Failed to register GATT SDP record");
+}
+
+static bool start_listening(void)
+{
+	/* BR/EDR socket */
+	bredr_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+					BT_IO_OPT_PSM, ATT_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	/* LE socket */
+	le_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+					BT_IO_OPT_CID, ATT_CID,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	if (!le_io && !bredr_io) {
+		error("gatt: Failed to start listening IO");
+		return false;
+	}
+
+	return true;
+}
+
+static void gatt_paired_cb(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+	char address[18];
+	struct app_connection *conn;
+
+	dev = find_device_by_addr(addr);
+	if (!dev)
+		return;
+
+	ba2str(addr, address);
+	DBG("Paired device %s", address);
+
+	/* conn without app is internal one used for search primary services */
+	conn = find_conn_without_app(dev);
+	if (!conn)
+		return;
+
+	if (conn->timeout_id > 0) {
+		g_source_remove(conn->timeout_id);
+		conn->timeout_id = 0;
+	}
+
+	search_dev_for_srvc(conn, NULL);
+}
+
+static void gatt_unpaired_cb(const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+	char address[18];
+
+	dev = find_device_by_addr(addr);
+	if (!dev)
+		return;
+
+	ba2str(addr, address);
+	DBG("Unpaired device %s", address);
+
+	queue_remove(gatt_devices, dev);
+	destroy_device(dev);
+}
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+	DBG("");
+
+	if (!bt_paired_register(gatt_paired_cb)) {
+		error("gatt: Could not register paired callback");
+		return false;
+	}
+
+	if (!bt_unpaired_register(gatt_unpaired_cb)) {
+		error("gatt: Could not register unpaired callback");
+		return false;
+	}
+
+	if (!start_listening())
+		return false;
+
+	crypto = bt_crypto_new();
+	if (!crypto) {
+		error("gatt: Failed to setup crypto");
+		goto failed;
+	}
+
+	gatt_devices = queue_new();
+	gatt_apps = queue_new();
+	app_connections = queue_new();
+	listen_apps = queue_new();
+	services_sdp = queue_new();
+	gatt_db = gatt_db_new();
+
+	if (!gatt_db) {
+		error("gatt: Failed to allocate memory for database");
+		goto failed;
+	}
+
+	if (!bt_le_register(le_device_found_handler)) {
+		error("gatt: bt_le_register failed");
+		goto failed;
+	}
+
+	bacpy(&adapter_addr, addr);
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	register_gap_service();
+	register_device_info_service();
+	register_gatt_service();
+
+	info("gatt: LE: %s BR/EDR: %s", le_io ? "enabled" : "disabled",
+					bredr_io ? "enabled" : "disabled");
+
+	return true;
+
+failed:
+
+	bt_paired_unregister(gatt_paired_cb);
+	bt_unpaired_unregister(gatt_unpaired_cb);
+
+	queue_destroy(gatt_apps, NULL);
+	gatt_apps = NULL;
+
+	queue_destroy(gatt_devices, NULL);
+	gatt_devices = NULL;
+
+	queue_destroy(app_connections, NULL);
+	app_connections = NULL;
+
+	queue_destroy(listen_apps, NULL);
+	listen_apps = NULL;
+
+	queue_destroy(services_sdp, NULL);
+	services_sdp = NULL;
+
+	gatt_db_unref(gatt_db);
+	gatt_db = NULL;
+
+	bt_crypto_unref(crypto);
+	crypto = NULL;
+
+	if (le_io) {
+		g_io_channel_unref(le_io);
+		le_io = NULL;
+	}
+
+	if (bredr_io) {
+		g_io_channel_unref(bredr_io);
+		bredr_io = NULL;
+	}
+
+	return false;
+}
+
+void bt_gatt_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
+	hal_ipc = NULL;
+
+	queue_destroy(app_connections, destroy_connection);
+	app_connections = NULL;
+
+	queue_destroy(gatt_apps, destroy_gatt_app);
+	gatt_apps = NULL;
+
+	queue_destroy(gatt_devices, destroy_device);
+	gatt_devices = NULL;
+
+	queue_destroy(services_sdp, free_service_sdp_record);
+	services_sdp = NULL;
+
+	queue_destroy(listen_apps, NULL);
+	listen_apps = NULL;
+
+	gatt_db_unref(gatt_db);
+	gatt_db = NULL;
+
+	if (le_io) {
+		g_io_channel_unref(le_io);
+		le_io = NULL;
+	}
+
+	if (bredr_io) {
+		g_io_channel_unref(bredr_io);
+		bredr_io = NULL;
+	}
+
+	if (gap_sdp_handle) {
+		bt_adapter_remove_record(gap_sdp_handle);
+		gap_sdp_handle = 0;
+	}
+
+	if (gatt_sdp_handle) {
+		bt_adapter_remove_record(gatt_sdp_handle);
+		gatt_sdp_handle = 0;
+	}
+
+	if (dis_sdp_handle) {
+		bt_adapter_remove_record(dis_sdp_handle);
+		dis_sdp_handle = 0;
+	}
+
+	bt_crypto_unref(crypto);
+	crypto = NULL;
+
+	bt_le_unregister();
+	bt_unpaired_unregister(gatt_unpaired_cb);
+}
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+							gatt_conn_cb_t func)
+{
+	struct gatt_app *app;
+	bt_uuid_t u, u128;
+
+	bt_string_to_uuid(&u, uuid);
+	bt_uuid_to_uuid128(&u, &u128);
+	app = register_app((void *) &u128.value.u128, type);
+	if (!app)
+		return 0;
+
+	app->func = func;
+
+	return app->id;
+}
+
+bool bt_gatt_unregister_app(unsigned int id)
+{
+	uint8_t status;
+
+	status = unregister_app(id);
+
+	return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr)
+{
+	uint8_t status;
+
+	status = handle_connect(id, addr, false);
+
+	return status != HAL_STATUS_FAILED;
+}
+
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr)
+{
+	struct app_connection match;
+	struct app_connection *conn;
+	struct gatt_device *device;
+	struct gatt_app *app;
+
+	app = find_app_by_id(id);
+	if (!app)
+		return false;
+
+	device = find_device_by_addr(addr);
+	if (!device)
+		return false;
+
+	match.device = device;
+	match.app = app;
+
+	conn = queue_remove_if(app_connections,
+				match_connection_by_device_and_app, &match);
+	if (!conn)
+		return false;
+
+	destroy_connection(conn);
+
+	return true;
+}
+
+bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+	struct gatt_app *app;
+
+	DBG("");
+
+	app = find_app_by_id(id);
+	if (!app) {
+		error("gatt: App ID=%d not found", id);
+		return false;
+	}
+
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		error("gatt: Device not found");
+		return false;
+	}
+
+	/* Take reference of device for auto connect purpose */
+	if (queue_isempty(dev->autoconnect_apps))
+		device_ref(dev);
+
+	if (!queue_find(dev->autoconnect_apps, NULL, INT_TO_PTR(id)))
+		return queue_push_head(dev->autoconnect_apps, INT_TO_PTR(id));
+
+	return true;
+}
+
+void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr)
+{
+	struct gatt_device *dev;
+
+	DBG("");
+
+	dev = find_device_by_addr(addr);
+	if (!dev) {
+		error("gatt: Device not found");
+		return;
+	}
+
+	queue_remove(dev->autoconnect_apps, INT_TO_PTR(id));
+
+	if (queue_isempty(dev->autoconnect_apps))
+		remove_autoconnect_device(dev);
+}
diff --git a/bluez/repo/android/gatt.h b/bluez/repo/android/gatt.h
new file mode 100644
index 0000000..3382df9
--- /dev/null
+++ b/bluez/repo/android/gatt.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_gatt_unregister(void);
+
+
+typedef enum {
+	GATT_CLIENT,
+	GATT_SERVER,
+} gatt_type_t;
+
+typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib);
+
+unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type,
+							gatt_conn_cb_t func);
+bool bt_gatt_unregister_app(unsigned int id);
+
+bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr);
+bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level);
+bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr);
+void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr);
diff --git a/bluez/repo/android/hal-a2dp-sink.c b/bluez/repo/android/hal-a2dp-sink.c
new file mode 100644
index 0000000..a0b7ed1
--- /dev/null
+++ b/bluez/repo/android/hal-a2dp-sink.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btav_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+static void handle_audio_config(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_audio_config *ev = buf;
+
+	if (cbs->audio_config_cb)
+		cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr),
+					ev->sample_rate, ev->channel_count);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_A2DP_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+	/* HAL_EV_A2DP_AUDIO_STATE */
+	{ handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
+	/* HAL_EV_A2DP_AUDIO_CONFIG */
+	{ handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) },
+};
+
+static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP_SINK, HAL_OP_A2DP_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btav_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_A2DP_SINK, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP_SINK;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP_SINK;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_A2DP_SINK);
+
+	cbs = NULL;
+}
+
+static btav_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = a2dp_connect,
+	.disconnect = disconnect,
+	.cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_sink_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-a2dp.c b/bluez/repo/android/hal-a2dp.c
new file mode 100644
index 0000000..f572875
--- /dev/null
+++ b/bluez/repo/android/hal-a2dp.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btav_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_a2dp_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
+}
+
+static void handle_audio_config(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_a2dp_audio_config *ev = buf;
+
+	if (cbs->audio_config_cb)
+		cbs->audio_config_cb((bt_bdaddr_t *)(ev->bdaddr),
+					ev->sample_rate, ev->channel_count);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_A2DP_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) },
+	/* HAL_EV_A2DP_AUDIO_STATE */
+	{ handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) },
+	/* HAL_EV_A2DP_AUDIO_CONFIG */
+	{ handle_audio_config, false, sizeof(struct hal_ev_a2dp_audio_config) },
+};
+
+static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_a2dp_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btav_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_A2DP;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
+
+	cbs = NULL;
+}
+
+static btav_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = a2dp_connect,
+	.disconnect = disconnect,
+	.cleanup = cleanup
+};
+
+btav_interface_t *bt_get_a2dp_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-audio-aptx.c b/bluez/repo/android/hal-audio-aptx.c
new file mode 100644
index 0000000..a800075
--- /dev/null
+++ b/bluez/repo/android/hal-audio-aptx.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 Tieto Poland
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "profiles/audio/a2dp-codecs.h"
+
+#define APTX_SO_NAME	"libbt-aptx.so"
+
+struct aptx_data {
+	a2dp_aptx_t aptx;
+
+	void *enc;
+};
+
+static const a2dp_aptx_t aptx_presets[] = {
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_44100 |
+						APTX_SAMPLING_FREQ_48000,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_48000,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+	{
+		.info = {
+			.vendor_id = APTX_VENDOR_ID,
+			.codec_id = APTX_CODEC_ID,
+		},
+		.frequency = APTX_SAMPLING_FREQ_44100,
+		.channel_mode = APTX_CHANNEL_MODE_STEREO,
+	},
+};
+
+static void *aptx_handle;
+static int aptx_btencsize;
+static int (*aptx_init)(void *, short);
+static int (*aptx_encode)(void *, void *, void *, void *);
+
+static bool aptx_load(void)
+{
+	const char * (*aptx_version)(void);
+	const char * (*aptx_build)(void);
+	int (*aptx_sizeofenc)(void);
+
+	aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY);
+	if (!aptx_handle) {
+		error("APTX: failed to open library %s (%s)", APTX_SO_NAME,
+								dlerror());
+		return false;
+	}
+
+	aptx_version = dlsym(aptx_handle, "aptxbtenc_version");
+	aptx_build = dlsym(aptx_handle, "aptxbtenc_build");
+
+	if (aptx_version && aptx_build)
+		info("APTX: using library version %s build %s", aptx_version(),
+								aptx_build());
+	else
+		warn("APTX: cannot retrieve library version");
+
+	aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc");
+	aptx_init = dlsym(aptx_handle, "aptxbtenc_init");
+	aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo");
+	if (!aptx_sizeofenc || !aptx_init || !aptx_encode) {
+		error("APTX: failed initialize library");
+		dlclose(aptx_handle);
+		aptx_handle = NULL;
+		return false;
+	}
+	aptx_btencsize = aptx_sizeofenc();
+
+	info("APTX: codec library initialized (size=%d)", aptx_btencsize);
+
+	return true;
+}
+
+static void aptx_unload(void)
+{
+	if (!aptx_handle)
+		return;
+
+	dlclose(aptx_handle);
+	aptx_handle = NULL;
+}
+
+static int aptx_get_presets(struct audio_preset *preset, size_t *len)
+{
+	int i;
+	int count;
+	size_t new_len = 0;
+	uint8_t *ptr = (uint8_t *) preset;
+	size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t);
+
+	DBG("");
+
+	count = sizeof(aptx_presets) / sizeof(aptx_presets[0]);
+
+	for (i = 0; i < count; i++) {
+		preset = (struct audio_preset *) ptr;
+
+		if (new_len + preset_size > *len)
+			break;
+
+		preset->len = sizeof(a2dp_aptx_t);
+		memcpy(preset->data, &aptx_presets[i], preset->len);
+
+		new_len += preset_size;
+		ptr += preset_size;
+	}
+
+	*len = new_len;
+
+	return i;
+}
+
+static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct aptx_data *aptx_data;
+
+	DBG("");
+
+	if (preset->len != sizeof(a2dp_aptx_t)) {
+		error("APTX: preset size mismatch");
+		return false;
+	}
+
+	aptx_data = malloc(sizeof(*aptx_data));
+	if (!aptx_data)
+		return false;
+
+	memset(aptx_data, 0, sizeof(*aptx_data));
+	memcpy(&aptx_data->aptx, preset->data, preset->len);
+
+	aptx_data->enc = calloc(1, aptx_btencsize);
+	if (!aptx_data->enc) {
+		error("APTX: failed to create encoder");
+		free(aptx_data);
+		return false;
+	}
+
+	/* 1 = big-endian, this is what devices are using */
+	aptx_init(aptx_data->enc, 1);
+
+	*codec_data = aptx_data;
+
+	return true;
+}
+
+static bool aptx_cleanup(void *codec_data)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+	free(aptx_data->enc);
+	free(codec_data);
+
+	return true;
+}
+
+static bool aptx_get_config(void *codec_data, struct audio_input_config *config)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+	config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ?
+								48000 : 44100;
+	config->channels = AUDIO_CHANNEL_OUT_STEREO;
+	config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	return true;
+}
+
+static size_t aptx_get_buffer_size(void *codec_data)
+{
+	/* TODO: return actual value */
+	return 0;
+}
+
+static size_t aptx_get_mediapacket_duration(void *codec_data)
+{
+	/* TODO: return actual value */
+	return 0;
+}
+
+static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+	const int16_t *ptr = (const void *) buffer;
+	size_t bytes_in = 0;
+	size_t bytes_out = 0;
+
+	while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) {
+		int pcm_l[4], pcm_r[4];
+		int i;
+
+		for (i = 0; i < 4; i++) {
+			pcm_l[i] = ptr[0];
+			pcm_r[i] = ptr[1];
+			ptr += 2;
+		}
+
+		aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]);
+
+		bytes_in += 16;
+		bytes_out += 4;
+	}
+
+	*written = bytes_out;
+
+	return bytes_in;
+}
+
+static bool aptx_update_qos(void *codec_data, uint8_t op)
+{
+	/*
+	 * aptX has constant bitrate of 352kbps (with constant 4:1 compression
+	 * ratio) thus QoS is not possible here.
+	 */
+
+	return false;
+}
+
+static const struct audio_codec codec = {
+	.type = A2DP_CODEC_VENDOR,
+	.use_rtp = false,
+
+	.load = aptx_load,
+	.unload = aptx_unload,
+
+	.get_presets = aptx_get_presets,
+
+	.init = aptx_codec_init,
+	.cleanup = aptx_cleanup,
+	.get_config = aptx_get_config,
+	.get_buffer_size = aptx_get_buffer_size,
+	.get_mediapacket_duration = aptx_get_mediapacket_duration,
+	.encode_mediapacket = aptx_encode_mediapacket,
+	.update_qos = aptx_update_qos,
+};
+
+const struct audio_codec *codec_aptx(void)
+{
+	return &codec;
+}
diff --git a/bluez/repo/android/hal-audio-sbc.c b/bluez/repo/android/hal-audio-sbc.c
new file mode 100644
index 0000000..7ad3a6a
--- /dev/null
+++ b/bluez/repo/android/hal-audio-sbc.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+#include <sbc/sbc.h>
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+#define SBC_QUALITY_MIN_BITPOOL	33
+#define SBC_QUALITY_STEP	5
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+	unsigned frame_count:4;
+	unsigned rfa0:1;
+	unsigned is_last_fragment:1;
+	unsigned is_first_fragment:1;
+	unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+	unsigned is_fragmented:1;
+	unsigned is_first_fragment:1;
+	unsigned is_last_fragment:1;
+	unsigned rfa0:1;
+	unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet_sbc {
+	struct media_packet_rtp hdr;
+	struct rtp_payload payload;
+	uint8_t data[0];
+};
+
+struct sbc_data {
+	a2dp_sbc_t sbc;
+
+	sbc_t enc;
+
+	uint16_t payload_len;
+
+	size_t in_frame_len;
+	size_t in_buf_size;
+
+	size_t out_frame_len;
+
+	unsigned frame_duration;
+	unsigned frames_per_packet;
+};
+
+static const a2dp_sbc_t sbc_presets[] = {
+	{
+		.frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+		.channel_mode = SBC_CHANNEL_MODE_MONO |
+				SBC_CHANNEL_MODE_DUAL_CHANNEL |
+				SBC_CHANNEL_MODE_STEREO |
+				SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_SNR |
+					SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+				SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+	{
+		.frequency = SBC_SAMPLING_FREQ_44100,
+		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+	{
+		.frequency = SBC_SAMPLING_FREQ_48000,
+		.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+		.subbands = SBC_SUBBANDS_8,
+		.allocation_method = SBC_ALLOCATION_LOUDNESS,
+		.block_length = SBC_BLOCK_LENGTH_16,
+		.min_bitpool = MIN_BITPOOL,
+		.max_bitpool = MAX_BITPOOL
+	},
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+	int i;
+	int count;
+	size_t new_len = 0;
+	uint8_t *ptr = (uint8_t *) preset;
+	size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+	count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+	for (i = 0; i < count; i++) {
+		preset = (struct audio_preset *) ptr;
+
+		if (new_len + preset_size > *len)
+			break;
+
+		preset->len = sizeof(a2dp_sbc_t);
+		memcpy(preset->data, &sbc_presets[i], preset->len);
+
+		new_len += preset_size;
+		ptr += preset_size;
+	}
+
+	*len = new_len;
+
+	return i;
+}
+
+static int sbc_freq2int(uint8_t freq)
+{
+	switch (freq) {
+	case SBC_SAMPLING_FREQ_16000:
+		return 16000;
+	case SBC_SAMPLING_FREQ_32000:
+		return 32000;
+	case SBC_SAMPLING_FREQ_44100:
+		return 44100;
+	case SBC_SAMPLING_FREQ_48000:
+		return 48000;
+	default:
+		return 0;
+	}
+}
+
+static const char *sbc_mode2str(uint8_t mode)
+{
+	switch (mode) {
+	case SBC_CHANNEL_MODE_MONO:
+		return "Mono";
+	case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+		return "DualChannel";
+	case SBC_CHANNEL_MODE_STEREO:
+		return "Stereo";
+	case SBC_CHANNEL_MODE_JOINT_STEREO:
+		return "JointStereo";
+	default:
+		return "(unknown)";
+	}
+}
+
+static int sbc_blocks2int(uint8_t blocks)
+{
+	switch (blocks) {
+	case SBC_BLOCK_LENGTH_4:
+		return 4;
+	case SBC_BLOCK_LENGTH_8:
+		return 8;
+	case SBC_BLOCK_LENGTH_12:
+		return 12;
+	case SBC_BLOCK_LENGTH_16:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static int sbc_subbands2int(uint8_t subbands)
+{
+	switch (subbands) {
+	case SBC_SUBBANDS_4:
+		return 4;
+	case SBC_SUBBANDS_8:
+		return 8;
+	default:
+		return 0;
+	}
+}
+
+static const char *sbc_allocation2str(uint8_t allocation)
+{
+	switch (allocation) {
+	case SBC_ALLOCATION_SNR:
+		return "SNR";
+	case SBC_ALLOCATION_LOUDNESS:
+		return "Loudness";
+	default:
+		return "(unknown)";
+	}
+}
+
+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+	a2dp_sbc_t *in = &sbc_data->sbc;
+	sbc_t *out = &sbc_data->enc;
+
+	sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+	out->endian = SBC_LE;
+	out->bitpool = in->max_bitpool;
+
+	DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d",
+			sbc_freq2int(in->frequency),
+			sbc_mode2str(in->channel_mode),
+			sbc_blocks2int(in->block_length),
+			sbc_subbands2int(in->subbands),
+			sbc_allocation2str(in->allocation_method),
+			in->min_bitpool, in->max_bitpool);
+}
+
+static void sbc_codec_calculate(struct sbc_data *sbc_data)
+{
+	size_t in_frame_len;
+	size_t out_frame_len;
+	size_t num_frames;
+
+	in_frame_len = sbc_get_codesize(&sbc_data->enc);
+	out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+	num_frames = sbc_data->payload_len / out_frame_len;
+
+	if (num_frames > MAX_FRAMES_IN_PAYLOAD)
+		num_frames = MAX_FRAMES_IN_PAYLOAD;
+
+	sbc_data->in_frame_len = in_frame_len;
+	sbc_data->in_buf_size = num_frames * in_frame_len;
+
+	sbc_data->out_frame_len = out_frame_len;
+
+	sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+	sbc_data->frames_per_packet = num_frames;
+
+	DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+				in_frame_len, out_frame_len, num_frames);
+}
+
+static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+							void **codec_data)
+{
+	struct sbc_data *sbc_data;
+
+	if (preset->len != sizeof(a2dp_sbc_t)) {
+		error("SBC: preset size mismatch");
+		return false;
+	}
+
+	sbc_data = calloc(sizeof(struct sbc_data), 1);
+	if (!sbc_data)
+		return false;
+
+	memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+	sbc_init_encoder(sbc_data);
+
+	sbc_data->payload_len = payload_len;
+
+	sbc_codec_calculate(sbc_data);
+
+	*codec_data = sbc_data;
+
+	return true;
+}
+
+static bool sbc_cleanup(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	sbc_finish(&sbc_data->enc);
+	free(codec_data);
+
+	return true;
+}
+
+static bool sbc_get_config(void *codec_data, struct audio_input_config *config)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	switch (sbc_data->sbc.frequency) {
+	case SBC_SAMPLING_FREQ_16000:
+		config->rate = 16000;
+		break;
+	case SBC_SAMPLING_FREQ_32000:
+		config->rate = 32000;
+		break;
+	case SBC_SAMPLING_FREQ_44100:
+		config->rate = 44100;
+		break;
+	case SBC_SAMPLING_FREQ_48000:
+		config->rate = 48000;
+		break;
+	default:
+		return false;
+	}
+	config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+				AUDIO_CHANNEL_OUT_MONO :
+				AUDIO_CHANNEL_OUT_STEREO;
+	config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+	return true;
+}
+
+static size_t sbc_get_buffer_size(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	return sbc_data->in_buf_size;
+}
+
+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+	return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp;
+	size_t consumed = 0;
+	size_t encoded = 0;
+	uint8_t frame_count = 0;
+
+	mp_data_len -= sizeof(mp_sbc->payload);
+
+	while (len - consumed >= sbc_data->in_frame_len &&
+			mp_data_len - encoded >= sbc_data->out_frame_len &&
+			frame_count < sbc_data->frames_per_packet) {
+		ssize_t read;
+		ssize_t written = 0;
+
+		read = sbc_encode(&sbc_data->enc, buffer + consumed,
+				sbc_data->in_frame_len, mp_sbc->data + encoded,
+				mp_data_len - encoded, &written);
+
+		if (read < 0) {
+			error("SBC: failed to encode block at frame %d (%zd)",
+							frame_count, read);
+			break;
+		}
+
+		frame_count++;
+		consumed += read;
+		encoded += written;
+	}
+
+	*written = encoded + sizeof(mp_sbc->payload);
+	mp_sbc->payload.frame_count = frame_count;
+
+	return consumed;
+}
+
+static bool sbc_update_qos(void *codec_data, uint8_t op)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	uint8_t curr_bitpool = sbc_data->enc.bitpool;
+	uint8_t new_bitpool = curr_bitpool;
+
+	switch (op) {
+	case QOS_POLICY_DEFAULT:
+		new_bitpool = sbc_data->sbc.max_bitpool;
+		break;
+
+	case QOS_POLICY_DECREASE:
+		if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
+			new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
+			if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
+				new_bitpool = SBC_QUALITY_MIN_BITPOOL;
+		}
+		break;
+	}
+
+	if (new_bitpool == curr_bitpool)
+		return false;
+
+	sbc_data->enc.bitpool = new_bitpool;
+
+	sbc_codec_calculate(sbc_data);
+
+	info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
+
+	return true;
+}
+
+static const struct audio_codec codec = {
+	.type = A2DP_CODEC_SBC,
+	.use_rtp = true,
+
+	.get_presets = sbc_get_presets,
+
+	.init = sbc_codec_init,
+	.cleanup = sbc_cleanup,
+	.get_config = sbc_get_config,
+	.get_buffer_size = sbc_get_buffer_size,
+	.get_mediapacket_duration = sbc_get_mediapacket_duration,
+	.encode_mediapacket = sbc_encode_mediapacket,
+	.update_qos = sbc_update_qos,
+};
+
+const struct audio_codec *codec_sbc(void)
+{
+	return &codec;
+}
diff --git a/bluez/repo/android/hal-audio.c b/bluez/repo/android/hal-audio.c
new file mode 100644
index 0000000..207101f
--- /dev/null
+++ b/bluez/repo/android/hal-audio.c
@@ -0,0 +1,1641 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include "audio-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "hal-audio.h"
+#include "hal-utils.h"
+#include "hal.h"
+
+#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
+
+#define FIXED_BUFFER_SIZE (20 * 512)
+
+#define MAX_DELAY	100000 /* 100ms */
+
+static const uint8_t a2dp_src_uuid[] = {
+		0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
+		0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static int listen_sk = -1;
+static int audio_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void timespec_add(struct timespec *base, uint64_t time_us,
+							struct timespec *res)
+{
+	res->tv_sec = base->tv_sec + time_us / 1000000;
+	res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000;
+
+	if (res->tv_nsec >= 1000000000) {
+		res->tv_sec++;
+		res->tv_nsec -= 1000000000;
+	}
+}
+
+static void timespec_diff(struct timespec *a, struct timespec *b,
+							struct timespec *res)
+{
+	res->tv_sec = a->tv_sec - b->tv_sec;
+	res->tv_nsec = a->tv_nsec - b->tv_nsec;
+
+	if (res->tv_nsec < 0) {
+		res->tv_sec--;
+		res->tv_nsec += 1000000000; /* 1sec */
+	}
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+	struct timespec res;
+
+	timespec_diff(a, b, &res);
+
+	return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+#if defined(ANDROID)
+/*
+ * Bionic does not have clock_nanosleep() prototype in time.h even though
+ * it provides its implementation.
+ */
+extern int clock_nanosleep(clockid_t clock_id, int flags,
+					const struct timespec *request,
+					struct timespec *remain);
+#endif
+
+static struct {
+	const audio_codec_get_t get_codec;
+	bool loaded;
+} audio_codecs[] = {
+		{ .get_codec = codec_aptx, .loaded = false },
+		{ .get_codec = codec_sbc, .loaded = false },
+};
+
+#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
+
+#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+
+struct audio_endpoint {
+	uint8_t id;
+	const struct audio_codec *codec;
+	void *codec_data;
+	int fd;
+
+	struct media_packet *mp;
+	size_t mp_data_len;
+
+	uint16_t seq;
+	uint32_t samples;
+	struct timespec start;
+
+	bool resync;
+};
+
+static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];
+
+enum a2dp_state_t {
+	AUDIO_A2DP_STATE_NONE,
+	AUDIO_A2DP_STATE_STANDBY,
+	AUDIO_A2DP_STATE_SUSPENDED,
+	AUDIO_A2DP_STATE_STARTED
+};
+
+struct a2dp_stream_out {
+	struct audio_stream_out stream;
+
+	struct audio_endpoint *ep;
+	enum a2dp_state_t audio_state;
+	struct audio_input_config cfg;
+
+	uint8_t *downmix_buf;
+};
+
+struct a2dp_audio_dev {
+	struct audio_hw_device dev;
+	struct a2dp_stream_out *out;
+};
+
+static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+			void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr cmd;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct ipc_status s;
+	size_t s_len = sizeof(s);
+
+	pthread_mutex_lock(&sk_mutex);
+
+	if (audio_sk < 0) {
+		error("audio: Invalid cmd socket passed to audio_ipc_cmd");
+		goto failed;
+	}
+
+	if (!rsp || !rsp_len) {
+		memset(&s, 0, s_len);
+		rsp_len = &s_len;
+		rsp = &s;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.service_id = service_id;
+	cmd.opcode = opcode;
+	cmd.len = len;
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	ret = sendmsg(audio_sk, &msg, 0);
+	if (ret < 0) {
+		error("audio: Sending command failed:%s", strerror(errno));
+		goto failed;
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("audio: Command socket closed");
+		goto failed;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = rsp;
+	iv[1].iov_len = *rsp_len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd) {
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+	}
+
+	ret = recvmsg(audio_sk, &msg, 0);
+	if (ret < 0) {
+		error("audio: Receiving command response failed:%s",
+							strerror(errno));
+		goto failed;
+	}
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("audio: Too small response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.service_id != service_id) {
+		error("audio: Invalid service id (%u vs %u)", cmd.service_id,
+								service_id);
+		goto failed;
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("audio: Malformed response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) {
+		error("audio: Invalid opcode received (%u vs %u)",
+						cmd.opcode, opcode);
+		goto failed;
+	}
+
+	if (cmd.opcode == AUDIO_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("audio: Invalid status length");
+			goto failed;
+		}
+
+		if (s->code == AUDIO_STATUS_SUCCESS) {
+			error("audio: Invalid success status response");
+			goto failed;
+		}
+
+		pthread_mutex_unlock(&sk_mutex);
+
+		return s->code;
+	}
+
+	pthread_mutex_unlock(&sk_mutex);
+
+	/* Receive auxiliary data in msg */
+	if (fd) {
+		struct cmsghdr *cmsg;
+
+		*fd = -1;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+
+		if (*fd < 0)
+			goto failed;
+	}
+
+	*rsp_len = cmd.len;
+
+	return AUDIO_STATUS_SUCCESS;
+
+failed:
+	/* Some serious issue happen on IPC - recover */
+	shutdown(audio_sk, SHUT_RDWR);
+	pthread_mutex_unlock(&sk_mutex);
+
+	return AUDIO_STATUS_FAILED;
+}
+
+static int ipc_open_cmd(const struct audio_codec *codec)
+{
+	uint8_t buf[BLUEZ_AUDIO_MTU];
+	struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf;
+	struct audio_rsp_open rsp;
+	size_t cmd_len = sizeof(buf) - sizeof(*cmd);
+	size_t rsp_len = sizeof(rsp);
+	int result;
+
+	DBG("");
+
+	memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid));
+
+	cmd->codec = codec->type;
+	cmd->presets = codec->get_presets(cmd->preset, &cmd_len);
+
+	cmd_len += sizeof(*cmd);
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd,
+				&rsp_len, &rsp, NULL);
+
+	if (result != AUDIO_STATUS_SUCCESS)
+		return 0;
+
+	return rsp.id;
+}
+
+static int ipc_close_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_close cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd,
+						struct audio_preset **caps)
+{
+	char buf[BLUEZ_AUDIO_MTU];
+	struct audio_cmd_open_stream cmd;
+	struct audio_rsp_open_stream *rsp =
+					(struct audio_rsp_open_stream *) &buf;
+	size_t rsp_len = sizeof(buf);
+	int result;
+
+	DBG("");
+
+	if (!caps)
+		return AUDIO_STATUS_FAILED;
+
+	cmd.id = *endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+				sizeof(cmd), &cmd, &rsp_len, rsp, fd);
+	if (result == AUDIO_STATUS_SUCCESS) {
+		size_t buf_len = sizeof(struct audio_preset) +
+					rsp->preset[0].len;
+		*endpoint_id = rsp->id;
+		*mtu = rsp->mtu;
+		*caps = malloc(buf_len);
+		memcpy(*caps, &rsp->preset, buf_len);
+	} else {
+		*caps = NULL;
+	}
+
+	return result;
+}
+
+static int ipc_close_stream_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_close_stream cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+static int ipc_resume_stream_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_resume_stream cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+static int ipc_suspend_stream_cmd(uint8_t endpoint_id)
+{
+	struct audio_cmd_suspend_stream cmd;
+	int result;
+
+	DBG("");
+
+	cmd.id = endpoint_id;
+
+	result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	return result;
+}
+
+struct register_state {
+	struct audio_endpoint *ep;
+	bool error;
+};
+
+static void register_endpoint(const struct audio_codec *codec,
+						struct register_state *state)
+{
+	struct audio_endpoint *ep = state->ep;
+
+	/* don't even try to register more endpoints if one failed */
+	if (state->error)
+		return;
+
+	ep->id = ipc_open_cmd(codec);
+
+	if (!ep->id) {
+		state->error = true;
+		error("Failed to register endpoint");
+		return;
+	}
+
+	ep->codec = codec;
+	ep->codec_data = NULL;
+	ep->fd = -1;
+
+	state->ep++;
+}
+
+static int register_endpoints(void)
+{
+	struct register_state state;
+	unsigned int i;
+
+	state.ep = &audio_endpoints[0];
+	state.error = false;
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+		if (!audio_codecs[i].loaded)
+			continue;
+
+		register_endpoint(codec, &state);
+	}
+
+	return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS;
+}
+
+static void unregister_endpoints(void)
+{
+	size_t i;
+
+	for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) {
+		struct audio_endpoint *ep = &audio_endpoints[i];
+
+		if (ep->id) {
+			ipc_close_cmd(ep->id);
+			memset(ep, 0, sizeof(*ep));
+		}
+	}
+}
+
+static bool open_endpoint(struct audio_endpoint **epp,
+						struct audio_input_config *cfg)
+{
+	struct audio_preset *preset;
+	struct audio_endpoint *ep = *epp;
+	const struct audio_codec *codec;
+	uint16_t mtu;
+	uint16_t payload_len;
+	int fd;
+	size_t i;
+	uint8_t ep_id = 0;
+
+	if (ep)
+		ep_id = ep->id;
+
+	if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) !=
+							AUDIO_STATUS_SUCCESS)
+		return false;
+
+	DBG("ep_id=%d mtu=%u", ep_id, mtu);
+
+	for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++)
+		if (audio_endpoints[i].id == ep_id) {
+			ep = &audio_endpoints[i];
+			break;
+		}
+
+	if (!ep) {
+		error("Cound not find opened endpoint");
+		goto failed;
+	}
+
+	*epp = ep;
+
+	payload_len = mtu;
+	if (ep->codec->use_rtp)
+		payload_len -= sizeof(struct rtp_header);
+
+	ep->fd = fd;
+
+	codec = ep->codec;
+	codec->init(preset, payload_len, &ep->codec_data);
+	codec->get_config(ep->codec_data, cfg);
+
+	ep->mp = calloc(mtu, 1);
+	if (!ep->mp)
+		goto failed;
+
+	if (ep->codec->use_rtp) {
+		struct media_packet_rtp *mp_rtp =
+					(struct media_packet_rtp *) ep->mp;
+		mp_rtp->hdr.v = 2;
+		mp_rtp->hdr.pt = 0x60;
+		mp_rtp->hdr.ssrc = htonl(1);
+	}
+
+	ep->mp_data_len = payload_len;
+
+	free(preset);
+
+	return true;
+
+failed:
+	close(fd);
+	free(preset);
+
+	return false;
+}
+
+static void close_endpoint(struct audio_endpoint *ep)
+{
+	ipc_close_stream_cmd(ep->id);
+	if (ep->fd >= 0) {
+		close(ep->fd);
+		ep->fd = -1;
+	}
+
+	free(ep->mp);
+
+	ep->codec->cleanup(ep->codec_data);
+	ep->codec_data = NULL;
+}
+
+static bool resume_endpoint(struct audio_endpoint *ep)
+{
+	if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS)
+		return false;
+
+	ep->samples = 0;
+	ep->resync = false;
+
+	ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT);
+
+	return true;
+}
+
+static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
+								size_t bytes)
+{
+	const int16_t *input = (const void *) buffer;
+	int16_t *output = (void *) out->downmix_buf;
+	size_t i, frames;
+
+	/* PCM 16bit stereo */
+	frames = bytes / (2 * sizeof(int16_t));
+
+	for (i = 0; i < frames; i++) {
+		int16_t l = get_le16(&input[i * 2]);
+		int16_t r = get_le16(&input[i * 2 + 1]);
+
+		put_le16((l + r) / 2, &output[i]);
+	}
+}
+
+static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable)
+{
+	int ret;
+
+	while (true) {
+		struct pollfd pollfd;
+
+		pollfd.fd = ep->fd;
+		pollfd.events = POLLOUT;
+		pollfd.revents = 0;
+
+		ret = poll(&pollfd, 1, 500);
+
+		if (ret >= 0) {
+			*writable = !!(pollfd.revents & POLLOUT);
+			break;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("poll failed (%d)", ret);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes)
+{
+	struct media_packet *mp = (struct media_packet *) ep->mp;
+	int ret;
+
+	while (true) {
+		ret = write(ep->fd, mp, bytes);
+
+		if (ret >= 0)
+			break;
+
+		/*
+		 * this should not happen so let's issue warning, but do not
+		 * fail, we can try to write next packet
+		 */
+		if (errno == EAGAIN) {
+			ret = errno;
+			warn("write failed (%d)", ret);
+			break;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("write failed (%d)", ret);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool write_data(struct a2dp_stream_out *out, const void *buffer,
+								size_t bytes)
+{
+	struct audio_endpoint *ep = out->ep;
+	struct media_packet *mp = (struct media_packet *) ep->mp;
+	struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp;
+	size_t free_space = ep->mp_data_len;
+	size_t consumed = 0;
+
+	while (consumed < bytes) {
+		size_t written = 0;
+		ssize_t read;
+		uint32_t samples;
+		int ret;
+		struct timespec current;
+		uint64_t audio_sent, audio_passed;
+		bool do_write = false;
+
+		/*
+		 * prepare media packet in advance so we don't waste time after
+		 * wakeup
+		 */
+		if (ep->codec->use_rtp) {
+			mp_rtp->hdr.sequence_number = htons(ep->seq++);
+			mp_rtp->hdr.timestamp = htonl(ep->samples);
+		}
+		read = ep->codec->encode_mediapacket(ep->codec_data,
+						buffer + consumed,
+						bytes - consumed, mp,
+						free_space, &written);
+
+		/*
+		 * not much we can do here, let's just ignore remaining
+		 * data and continue
+		 */
+		if (read <= 0)
+			return true;
+
+		/* calculate where are we and where we should be */
+		clock_gettime(CLOCK_MONOTONIC, &current);
+		if (!ep->samples)
+			memcpy(&ep->start, &current, sizeof(ep->start));
+		audio_sent = ep->samples * 1000000ll / out->cfg.rate;
+		audio_passed = timespec_diff_us(&current, &ep->start);
+
+		/*
+		 * if we're ahead of stream then wait for next write point,
+		 * if we're lagging more than 100ms then stop writing and just
+		 * skip data until we're back in sync
+		 */
+		if (audio_sent > audio_passed) {
+			struct timespec anchor;
+
+			ep->resync = false;
+
+			timespec_add(&ep->start, audio_sent, &anchor);
+
+			while (true) {
+				ret = clock_nanosleep(CLOCK_MONOTONIC,
+							TIMER_ABSTIME, &anchor,
+							NULL);
+
+				if (!ret)
+					break;
+
+				if (ret != EINTR) {
+					error("clock_nanosleep failed (%d)",
+									ret);
+					return false;
+				}
+			}
+		} else if (!ep->resync) {
+			uint64_t diff = audio_passed - audio_sent;
+
+			if (diff > MAX_DELAY) {
+				warn("lag is %jums, resyncing", diff / 1000);
+
+				ep->codec->update_qos(ep->codec_data,
+							QOS_POLICY_DECREASE);
+				ep->resync = true;
+			}
+		}
+
+		/* we send data only in case codec encoded some data, i.e. some
+		 * codecs do internal buffering and output data only if full
+		 * frame can be encoded
+		 * in resync mode we'll just drop mediapackets
+		 */
+		if (written > 0 && !ep->resync) {
+			/* wait some time for socket to be ready for write,
+			 * but we'll just skip writing data if timeout occurs
+			 */
+			if (!wait_for_endpoint(ep, &do_write))
+				return false;
+
+			if (do_write) {
+				if (ep->codec->use_rtp)
+					written += sizeof(struct rtp_header);
+
+				if (!write_to_endpoint(ep, written))
+					return false;
+			}
+		}
+
+		/*
+		 * AudioFlinger provides 16bit PCM, so sample size is 2 bytes
+		 * multiplied by number of channels. Number of channels is
+		 * simply number of bits set in channels mask.
+		 */
+		samples = read / (2 * popcount(out->cfg.channels));
+		ep->samples += samples;
+		consumed += read;
+	}
+
+	return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+								size_t bytes)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	const void *in_buf = buffer;
+	size_t in_len = bytes;
+
+	/* just return in case we're closing */
+	if (out->audio_state == AUDIO_A2DP_STATE_NONE)
+		return -1;
+
+	/* We can auto-start only from standby */
+	if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) {
+		DBG("stream in standby, auto-start");
+
+		if (!resume_endpoint(out->ep))
+			return -1;
+
+		out->audio_state = AUDIO_A2DP_STATE_STARTED;
+	}
+
+	if (out->audio_state != AUDIO_A2DP_STATE_STARTED) {
+		error("audio: stream not started");
+		return -1;
+	}
+
+	if (out->ep->fd < 0) {
+		error("audio: no transport socket");
+		return -1;
+	}
+
+	/*
+	 * currently Android audioflinger is not able to provide mono stream on
+	 * A2DP output so down mixing needs to be done in hal-audio plugin.
+	 *
+	 * for reference see
+	 * AudioFlinger::PlaybackThread::readOutputParameters()
+	 * frameworks/av/services/audioflinger/Threads.cpp:1631
+	 */
+	if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+		if (!out->downmix_buf) {
+			error("audio: downmix buffer not initialized");
+			return -1;
+		}
+
+		downmix_to_mono(out, buffer, bytes);
+
+		in_buf = out->downmix_buf;
+		in_len = bytes / 2;
+	}
+
+	if (!write_data(out, in_buf, in_len))
+		return -1;
+
+	return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	if (rate != out->cfg.rate) {
+		warn("audio: cannot set sample rate to %d", rate);
+		return -1;
+	}
+
+	return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+	DBG("");
+
+	/*
+	 * We should return proper buffer size calculated by codec (so each
+	 * input buffer is encoded into single media packed) but this does not
+	 * work well with AudioFlinger and causes problems. For this reason we
+	 * use magic value here and out_write code takes care of splitting
+	 * input buffer into multiple media packets.
+	 */
+	return FIXED_BUFFER_SIZE;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+	DBG("");
+
+	/*
+	 * AudioFlinger can only provide stereo stream, so we return it here and
+	 * later we'll downmix this to mono in case codec requires it
+	 */
+
+	return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	if (out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+		if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+			return -1;
+		out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+	}
+
+	return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	char *kvpair;
+	char *str;
+	char *saveptr;
+	bool enter_suspend = false;
+	bool exit_suspend = false;
+
+	DBG("%s", kvpairs);
+
+	str = strdup(kvpairs);
+	if (!str)
+		return -ENOMEM;
+
+	kvpair = strtok_r(str, ";", &saveptr);
+
+	for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) {
+		char *keyval;
+
+		keyval = strchr(kvpair, '=');
+		if (!keyval)
+			continue;
+
+		*keyval = '\0';
+		keyval++;
+
+		if (!strcmp(kvpair, "closing")) {
+			if (!strcmp(keyval, "true"))
+				out->audio_state = AUDIO_A2DP_STATE_NONE;
+		} else if (!strcmp(kvpair, "A2dpSuspended")) {
+			if (!strcmp(keyval, "true"))
+				enter_suspend = true;
+			else
+				exit_suspend = true;
+		}
+	}
+
+	free(str);
+
+	if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+		if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+			return -1;
+		out->audio_state = AUDIO_A2DP_STATE_SUSPENDED;
+	}
+
+	if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED)
+		out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+	return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+	return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	struct audio_endpoint *ep = out->ep;
+	size_t pkt_duration;
+
+	DBG("");
+
+	pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data);
+
+	return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+								float right)
+{
+	DBG("");
+	/* volume controlled in audioflinger mixer (digital) */
+	return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+							uint32_t *dsp_frames)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+	return strdup("");
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+								size_t bytes)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_open_output_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+	struct a2dp_stream_out *out;
+
+	out = calloc(1, sizeof(struct a2dp_stream_out));
+	if (!out)
+		return -ENOMEM;
+
+	DBG("");
+
+	out->stream.common.get_sample_rate = out_get_sample_rate;
+	out->stream.common.set_sample_rate = out_set_sample_rate;
+	out->stream.common.get_buffer_size = out_get_buffer_size;
+	out->stream.common.get_channels = out_get_channels;
+	out->stream.common.get_format = out_get_format;
+	out->stream.common.set_format = out_set_format;
+	out->stream.common.standby = out_standby;
+	out->stream.common.dump = out_dump;
+	out->stream.common.set_parameters = out_set_parameters;
+	out->stream.common.get_parameters = out_get_parameters;
+	out->stream.common.add_audio_effect = out_add_audio_effect;
+	out->stream.common.remove_audio_effect = out_remove_audio_effect;
+	out->stream.get_latency = out_get_latency;
+	out->stream.set_volume = out_set_volume;
+	out->stream.write = out_write;
+	out->stream.get_render_position = out_get_render_position;
+
+	/* We want to autoselect opened endpoint */
+	out->ep = NULL;
+
+	if (!open_endpoint(&out->ep, &out->cfg))
+		goto fail;
+
+	DBG("rate=%d channels=%d format=%d", out->cfg.rate,
+					out->cfg.channels, out->cfg.format);
+
+	if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+		out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2);
+		if (!out->downmix_buf)
+			goto fail;
+	}
+
+	*stream_out = &out->stream;
+	a2dp_dev->out = out;
+
+	out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+	return 0;
+
+fail:
+	error("audio: cannot open output stream");
+	free(out);
+	*stream_out = NULL;
+	return -EIO;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int audio_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	return audio_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, address);
+}
+#else
+static int audio_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out)
+{
+	return audio_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, NULL);
+}
+#endif
+
+static void audio_close_output_stream(struct audio_hw_device *dev,
+					struct audio_stream_out *stream)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+	DBG("");
+
+	close_endpoint(a2dp_dev->out->ep);
+
+	free(out->downmix_buf);
+
+	free(stream);
+	a2dp_dev->out = NULL;
+}
+
+static int audio_set_parameters(struct audio_hw_device *dev,
+							const char *kvpairs)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+	struct a2dp_stream_out *out = a2dp_dev->out;
+
+	DBG("");
+
+	if (!out)
+		return 0;
+
+	return out->stream.common.set_parameters((struct audio_stream *) out,
+								kvpairs);
+}
+
+static char *audio_get_parameters(const struct audio_hw_device *dev,
+							const char *keys)
+{
+	DBG("");
+	return strdup("");
+}
+
+static int audio_init_check(const struct audio_hw_device *dev)
+{
+	DBG("");
+	return 0;
+}
+
+static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_set_mode(struct audio_hw_device *dev, int mode)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev,
+					const struct audio_config *config)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int audio_open_input_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	struct audio_stream_in *in;
+
+	DBG("");
+
+	in = calloc(1, sizeof(struct audio_stream_in));
+	if (!in)
+		return -ENOMEM;
+
+	in->common.get_sample_rate = in_get_sample_rate;
+	in->common.set_sample_rate = in_set_sample_rate;
+	in->common.get_buffer_size = in_get_buffer_size;
+	in->common.get_channels = in_get_channels;
+	in->common.get_format = in_get_format;
+	in->common.set_format = in_set_format;
+	in->common.standby = in_standby;
+	in->common.dump = in_dump;
+	in->common.set_parameters = in_set_parameters;
+	in->common.get_parameters = in_get_parameters;
+	in->common.add_audio_effect = in_add_audio_effect;
+	in->common.remove_audio_effect = in_remove_audio_effect;
+	in->set_gain = in_set_gain;
+	in->read = in_read;
+	in->get_input_frames_lost = in_get_input_frames_lost;
+
+	*stream_in = in;
+
+	return 0;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int audio_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	return audio_open_input_stream_real(dev, handle, devices, config,
+						stream_in, flags, address,
+						source);
+}
+#else
+static int audio_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in)
+{
+	return audio_open_input_stream_real(dev, handle, devices, config,
+						stream_in, 0, NULL, 0);
+}
+#endif
+
+static void audio_close_input_stream(struct audio_hw_device *dev,
+					struct audio_stream_in *stream_in)
+{
+	DBG("");
+	free(stream_in);
+}
+
+static int audio_dump(const audio_hw_device_t *device, int fd)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int set_master_mute(struct audio_hw_device *dev, bool mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_master_mute(struct audio_hw_device *dev, bool *mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int create_audio_patch(struct audio_hw_device *dev,
+					unsigned int num_sources,
+					const struct audio_port_config *sources,
+					unsigned int num_sinks,
+					const struct audio_port_config *sinks,
+					audio_patch_handle_t *handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int release_audio_patch(struct audio_hw_device *dev,
+					audio_patch_handle_t handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int set_audio_port_config(struct audio_hw_device *dev,
+					const struct audio_port_config *config)
+{
+	DBG("");
+	return -ENOSYS;
+}
+#endif
+
+static int audio_close(hw_device_t *device)
+{
+	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
+	unsigned int i;
+
+	DBG("");
+
+	unregister_endpoints();
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+		if (!audio_codecs[i].loaded)
+			continue;
+
+		if (codec->unload)
+			codec->unload();
+
+		audio_codecs[i].loaded = false;
+	}
+
+	shutdown(listen_sk, SHUT_RDWR);
+	shutdown(audio_sk, SHUT_RDWR);
+
+	pthread_join(ipc_th, NULL);
+
+	close(listen_sk);
+	listen_sk = -1;
+
+	free(a2dp_dev);
+	return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+	bool done = false;
+	struct pollfd pfd;
+	int sk;
+
+	DBG("");
+
+	while (!done) {
+		DBG("Waiting for connection ...");
+
+		sk = accept(listen_sk, NULL, NULL);
+		if (sk < 0) {
+			int err = errno;
+
+			if (err == EINTR)
+				continue;
+
+			if (err != ECONNABORTED && err != EINVAL)
+				error("audio: Failed to accept socket: %d (%s)",
+							err, strerror(err));
+
+			break;
+		}
+
+		pthread_mutex_lock(&sk_mutex);
+		audio_sk = sk;
+		pthread_mutex_unlock(&sk_mutex);
+
+		DBG("Audio IPC: Connected");
+
+		if (register_endpoints() != AUDIO_STATUS_SUCCESS) {
+			error("audio: Failed to register endpoints");
+
+			unregister_endpoints();
+
+			pthread_mutex_lock(&sk_mutex);
+			shutdown(audio_sk, SHUT_RDWR);
+			close(audio_sk);
+			audio_sk = -1;
+			pthread_mutex_unlock(&sk_mutex);
+
+			continue;
+		}
+
+		memset(&pfd, 0, sizeof(pfd));
+		pfd.fd = audio_sk;
+		pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+		/* Check if socket is still alive. Empty while loop.*/
+		while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+		info("Audio HAL: Socket closed");
+
+		pthread_mutex_lock(&sk_mutex);
+		close(audio_sk);
+		audio_sk = -1;
+		pthread_mutex_unlock(&sk_mutex);
+	}
+
+	/* audio_sk is closed at this point, just cleanup endpoints states */
+	memset(audio_endpoints, 0, sizeof(audio_endpoints));
+
+	info("Closing Audio IPC thread");
+	return NULL;
+}
+
+static int audio_ipc_init(void)
+{
+	struct sockaddr_un addr;
+	int err;
+	int sk;
+
+	DBG("");
+
+	sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = -errno;
+		error("audio: Failed to create socket: %d (%s)", -err,
+								strerror(-err));
+		return err;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH,
+					sizeof(BLUEZ_AUDIO_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = -errno;
+		error("audio: Failed to bind socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	if (listen(sk, 1) < 0) {
+		err = -errno;
+		error("audio: Failed to listen on the socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	listen_sk = sk;
+
+	err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+	if (err) {
+		err = -err;
+		ipc_th = 0;
+		error("audio: Failed to start Audio IPC thread: %d (%s)",
+							-err, strerror(-err));
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	close(sk);
+	return err;
+}
+
+static int audio_open(const hw_module_t *module, const char *name,
+							hw_device_t **device)
+{
+	struct a2dp_audio_dev *a2dp_dev;
+	size_t i;
+	int err;
+
+	DBG("");
+
+	if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+		error("audio: interface %s not matching [%s]", name,
+						AUDIO_HARDWARE_INTERFACE);
+		return -EINVAL;
+	}
+
+	err = audio_ipc_init();
+	if (err < 0)
+		return err;
+
+	a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
+	if (!a2dp_dev)
+		return -ENOMEM;
+
+	a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+	a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+	a2dp_dev->dev.common.module = (struct hw_module_t *) module;
+	a2dp_dev->dev.common.close = audio_close;
+
+	a2dp_dev->dev.init_check = audio_init_check;
+	a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
+	a2dp_dev->dev.set_master_volume = audio_set_master_volume;
+	a2dp_dev->dev.set_mode = audio_set_mode;
+	a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
+	a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
+	a2dp_dev->dev.set_parameters = audio_set_parameters;
+	a2dp_dev->dev.get_parameters = audio_get_parameters;
+	a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
+	a2dp_dev->dev.open_output_stream = audio_open_output_stream;
+	a2dp_dev->dev.close_output_stream = audio_close_output_stream;
+	a2dp_dev->dev.open_input_stream = audio_open_input_stream;
+	a2dp_dev->dev.close_input_stream = audio_close_input_stream;
+	a2dp_dev->dev.dump = audio_dump;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	a2dp_dev->dev.set_master_mute = set_master_mute;
+	a2dp_dev->dev.get_master_mute = get_master_mute;
+	a2dp_dev->dev.create_audio_patch = create_audio_patch;
+	a2dp_dev->dev.release_audio_patch = release_audio_patch;
+	a2dp_dev->dev.get_audio_port = get_audio_port;
+	a2dp_dev->dev.set_audio_port_config = set_audio_port_config;
+#endif
+
+	for (i = 0; i < NUM_CODECS; i++) {
+		const struct audio_codec *codec = audio_codecs[i].get_codec();
+
+		if (codec->load && !codec->load())
+			continue;
+
+		audio_codecs[i].loaded = true;
+	}
+
+	/*
+	 * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
+	 * This results from the structure of following structs:a2dp_audio_dev,
+	 * audio_hw_device. We will rely on this later in the code.
+	 */
+	*device = &a2dp_dev->dev.common;
+
+	return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+	.open = audio_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+	.common = {
+		.tag = HARDWARE_MODULE_TAG,
+		.version_major = 1,
+		.version_minor = 0,
+		.id = AUDIO_HARDWARE_MODULE_ID,
+		.name = "A2DP Bluez HW HAL",
+		.author = "Intel Corporation",
+		.methods = &hal_module_methods,
+	},
+};
diff --git a/bluez/repo/android/hal-audio.h b/bluez/repo/android/hal-audio.h
new file mode 100644
index 0000000..2b47412
--- /dev/null
+++ b/bluez/repo/android/hal-audio.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <time.h>
+#include <hardware/audio.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+	unsigned cc:4;
+	unsigned x:1;
+	unsigned p:1;
+	unsigned v:2;
+
+	unsigned pt:7;
+	unsigned m:1;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+	unsigned v:2;
+	unsigned p:1;
+	unsigned x:1;
+	unsigned cc:4;
+
+	unsigned m:1;
+	unsigned pt:7;
+
+	uint16_t sequence_number;
+	uint32_t timestamp;
+	uint32_t ssrc;
+	uint32_t csrc[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet {
+	uint8_t data[0];
+};
+
+struct media_packet_rtp {
+	struct rtp_header hdr;
+	uint8_t data[0];
+};
+
+struct audio_input_config {
+	uint32_t rate;
+	uint32_t channels;
+	audio_format_t format;
+};
+
+struct audio_codec {
+	uint8_t type;
+	bool use_rtp;
+
+	bool (*load) (void);
+	void (*unload) (void);
+
+	int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+	bool (*init) (struct audio_preset *preset, uint16_t mtu,
+				void **codec_data);
+	bool (*cleanup) (void *codec_data);
+	bool (*get_config) (void *codec_data,
+					struct audio_input_config *config);
+	size_t (*get_buffer_size) (void *codec_data);
+	size_t (*get_mediapacket_duration) (void *codec_data);
+	ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written);
+	bool (*update_qos) (void *codec_data, uint8_t op);
+};
+
+#define QOS_POLICY_DEFAULT	0x00
+#define QOS_POLICY_DECREASE	0x01
+
+typedef const struct audio_codec * (*audio_codec_get_t) (void);
+
+const struct audio_codec *codec_sbc(void);
+const struct audio_codec *codec_aptx(void);
diff --git a/bluez/repo/android/hal-avrcp-ctrl.c b/bluez/repo/android/hal-avrcp-ctrl.c
new file mode 100644
index 0000000..46b77fd
--- /dev/null
+++ b/bluez/repo/android/hal-avrcp-ctrl.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_ctrl_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_connection_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_ctrl_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_passthrough_rsp(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_ctrl_passthrough_rsp *ev = buf;
+
+	if (cbs->passthrough_rsp_cb)
+		cbs->passthrough_rsp_cb(ev->id, ev->key_state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_AVRCP_CTRL_CONN_STATE */
+	{ handle_connection_state, false,
+			sizeof(struct hal_ev_avrcp_ctrl_conn_state) },
+	/* HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP */
+	{ handle_passthrough_rsp, false,
+			sizeof(struct hal_ev_avrcp_ctrl_passthrough_rsp) },
+};
+
+static bt_status_t init(btrc_ctrl_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_AVRCP_CTRL, ev_handlers,
+				sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL);
+	}
+
+	return ret;
+}
+
+static bt_status_t send_pass_through_cmd(bt_bdaddr_t *bd_addr, uint8_t key_code,
+							uint8_t key_state)
+{
+	struct hal_cmd_avrcp_ctrl_send_passthrough cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.key_code = key_code;
+	cmd.key_state = key_state;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP_CTRL,
+					HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP_CTRL;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_AVRCP_CTRL);
+
+	cbs = NULL;
+}
+
+static btrc_ctrl_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.send_pass_through_cmd = send_pass_through_cmd,
+	.cleanup = cleanup
+};
+
+btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-avrcp.c b/bluez/repo/android/hal-avrcp.c
new file mode 100644
index 0000000..f935eda
--- /dev/null
+++ b/bluez/repo/android/hal-avrcp.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_remote_features(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_remote_features *ev = buf;
+
+	if (cbs->remote_features_cb)
+		cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr),
+								ev->features);
+}
+
+static void handle_get_play_status(void *buf, uint16_t len, int fd)
+{
+	if (cbs->get_play_status_cb)
+		cbs->get_play_status_cb();
+}
+
+static void handle_list_player_attrs(void *buf, uint16_t len, int fd)
+{
+	if (cbs->list_player_app_attr_cb)
+		cbs->list_player_app_attr_cb();
+}
+
+static void handle_list_player_values(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_list_player_values *ev = buf;
+
+	if (cbs->list_player_app_values_cb)
+		cbs->list_player_app_values_cb(ev->attr);
+}
+
+static void handle_get_player_values(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_player_values *ev = buf;
+	btrc_player_attr_t attrs[4];
+	int i;
+
+	if (!cbs->get_player_app_value_cb)
+		return;
+
+	/* Convert uint8_t array to btrc_player_attr_t array */
+	for (i = 0; i < ev->number; i++)
+		attrs[i] = ev->attrs[i];
+
+	cbs->get_player_app_value_cb(ev->number, attrs);
+}
+
+static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_player_attrs_text *ev = buf;
+	btrc_player_attr_t attrs[4];
+	int i;
+
+	if (!cbs->get_player_app_attrs_text_cb)
+		return;
+
+	/* Convert uint8_t array to btrc_player_attr_t array */
+	for (i = 0; i < ev->number; i++)
+		attrs[i] = ev->attrs[i];
+
+	cbs->get_player_app_attrs_text_cb(ev->number, attrs);
+}
+
+static void handle_get_player_values_text(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_player_values_text *ev = buf;
+
+	if (cbs->get_player_app_values_text_cb)
+		cbs->get_player_app_values_text_cb(ev->attr, ev->number,
+								ev->values);
+}
+
+static void handle_set_player_value(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_set_player_values *ev = buf;
+	struct hal_avrcp_player_attr_value *attrs;
+	btrc_player_settings_t values;
+	int i;
+
+	if (!cbs->set_player_app_value_cb)
+		return;
+
+	attrs = (struct hal_avrcp_player_attr_value *) ev->attrs;
+
+	/* Convert to btrc_player_settings_t */
+	values.num_attr = ev->number;
+	for (i = 0; i < ev->number; i++) {
+		values.attr_ids[i] = attrs[i].attr;
+		values.attr_values[i] = attrs[i].value;
+	}
+
+	cbs->set_player_app_value_cb(&values);
+}
+
+static void handle_get_element_attrs(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_get_element_attrs *ev = buf;
+	btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS];
+	int i;
+
+	if (!cbs->get_element_attr_cb)
+		return;
+
+	/* Convert uint8_t array to btrc_media_attr_t array */
+	for (i = 0; i < ev->number; i++)
+		attrs[i] = ev->attrs[i];
+
+	cbs->get_element_attr_cb(ev->number, attrs);
+}
+
+static void handle_register_notification(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_register_notification *ev = buf;
+
+	if (cbs->register_notification_cb)
+		cbs->register_notification_cb(ev->event, ev->param);
+}
+
+static void handle_volume_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_volume_changed *ev = buf;
+
+	if (cbs->volume_change_cb)
+		cbs->volume_change_cb(ev->volume, ev->type);
+}
+
+static void handle_passthrough_cmd(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_avrcp_passthrough_cmd *ev = buf;
+
+	if (cbs->passthrough_cmd_cb)
+		cbs->passthrough_cmd_cb(ev->id, ev->state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_AVRCP_REMOTE_FEATURES */
+	{ handle_remote_features, false,
+			sizeof(struct hal_ev_avrcp_remote_features) },
+	/* HAL_EV_AVRCP_GET_PLAY_STATUS */
+	{ handle_get_play_status, false, 0 },
+	/* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */
+	{ handle_list_player_attrs, false, 0 },
+	/* HAL_EV_AVRCP_LIST_PLAYER_VALUES */
+	{ handle_list_player_values, false,
+			sizeof(struct hal_ev_avrcp_list_player_values) },
+	/* HAL_EV_AVRCP_GET_PLAYER_VALUES */
+	{ handle_get_player_values, true,
+			sizeof(struct hal_ev_avrcp_get_player_values) },
+	/* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */
+	{ handle_get_player_attrs_text, true,
+			sizeof(struct hal_ev_avrcp_get_player_attrs_text) },
+	/* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */
+	{ handle_get_player_values_text, true,
+			sizeof(struct hal_ev_avrcp_get_player_values_text) },
+	/* HAL_EV_AVRCP_SET_PLAYER_VALUES */
+	{ handle_set_player_value, true,
+			sizeof(struct hal_ev_avrcp_set_player_values) },
+	/* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */
+	{ handle_get_element_attrs, true,
+			sizeof(struct hal_ev_avrcp_get_element_attrs) },
+	/* HAL_EV_AVRCP_REGISTER_NOTIFICATION */
+	{ handle_register_notification, false,
+			sizeof(struct hal_ev_avrcp_register_notification) },
+	/* HAL_EV_AVRCP_VOLUME_CHANGED */
+	{ handle_volume_changed, false,
+			sizeof(struct hal_ev_avrcp_volume_changed) },
+	/* HAL_EV_AVRCP_PASSTHROUGH_CMD */
+	{ handle_passthrough_cmd, false,
+			sizeof(struct hal_ev_avrcp_passthrough_cmd) },
+};
+
+static bt_status_t init(btrc_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers,
+				sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+	}
+
+	return ret;
+}
+
+static bt_status_t get_play_status_rsp(btrc_play_status_t status,
+					uint32_t song_len, uint32_t song_pos)
+{
+	struct hal_cmd_avrcp_get_play_status cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.status = status;
+	cmd.duration = song_len;
+	cmd.position = song_pos;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t list_player_app_attr_rsp(int num_attr,
+						btrc_player_attr_t *p_attrs)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_attr < 0)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd) + num_attr;
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->number = num_attr;
+	memcpy(cmd->attrs, p_attrs, num_attr);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_LIST_PLAYER_ATTRS,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_val < 0)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd) + num_val;
+
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->number = num_val;
+	memcpy(cmd->values, p_vals, num_val);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_LIST_PLAYER_VALUES,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf;
+	size_t len, attrs_len;
+	int i;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!p_vals)
+		return BT_STATUS_PARM_INVALID;
+
+	attrs_len = p_vals->num_attr *
+				sizeof(struct hal_avrcp_player_attr_value);
+	len = sizeof(*cmd) + attrs_len;
+
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->number = p_vals->num_attr;
+
+	for (i = 0; i < p_vals->num_attr; i++) {
+		cmd->attrs[i].attr = p_vals->attr_ids[i];
+		cmd->attrs[i].value = p_vals->attr_values[i];
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_PLAYER_ATTRS,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len)
+{
+	struct hal_avrcp_player_setting_text *value = (void *) ptr;
+	size_t attr_len = sizeof(*value);
+
+	if (attr_len + *len > IPC_MTU)
+		return 0;
+
+	value->id = id;
+	value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN);
+
+	*len += attr_len;
+
+	if (value->len + *len > IPC_MTU)
+		value->len = IPC_MTU - *len;
+
+	memcpy(value->text, text, value->len);
+
+	*len += value->len;
+
+	return attr_len + value->len;
+}
+
+static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr,
+					btrc_player_setting_text_t *p_attrs,
+					size_t *len)
+{
+	int i;
+
+	for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+		int ret;
+
+		ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len);
+		if (ret == 0)
+			break;
+
+		ptr += ret;
+	}
+
+	return i;
+}
+
+static bt_status_t get_player_app_attr_text_rsp(int num_attr,
+					btrc_player_setting_text_t *p_attrs)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf;
+	uint8_t *ptr;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd);
+	ptr = (uint8_t *) &cmd->attrs[0];
+	cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_text_rsp(int num_val,
+					btrc_player_setting_text_t *p_vals)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf;
+	uint8_t *ptr;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (num_val < 0)
+		return BT_STATUS_PARM_INVALID;
+
+	len = sizeof(*cmd);
+	ptr = (uint8_t *) &cmd->values[0];
+	cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr,
+					btrc_element_attr_val_t *p_attrs,
+					size_t *len)
+{
+	int i;
+
+	for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+		int ret;
+
+		ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len);
+		if (ret == 0)
+			break;
+
+		ptr += ret;
+	}
+
+	return i;
+}
+
+static bt_status_t get_element_attr_rsp(uint8_t num_attr,
+					btrc_element_attr_val_t *p_attrs)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+	size_t len;
+	uint8_t *ptr;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	len = sizeof(*cmd);
+	ptr = (uint8_t *) &cmd->values[0];
+	cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status)
+{
+	struct hal_cmd_avrcp_set_player_attrs_value cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.status = rsp_status;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t play_status_changed_rsp(btrc_notification_type_t type,
+						btrc_play_status_t *play_status)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	size_t len;
+
+	cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED;
+	cmd->type = type;
+	cmd->len = 1;
+	memcpy(cmd->data, play_status, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_change_rsp(btrc_notification_type_t type,
+							btrc_uid_t *track)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	size_t len;
+
+	cmd->event = BTRC_EVT_TRACK_CHANGE;
+	cmd->type = type;
+	cmd->len = sizeof(*track);
+	memcpy(cmd->data, track, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_reached_end_rsp(btrc_notification_type_t type)
+{
+	struct hal_cmd_avrcp_register_notification cmd;
+
+	cmd.event = BTRC_EVT_TRACK_REACHED_END;
+	cmd.type = type;
+	cmd.len = 0;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t track_reached_start_rsp(btrc_notification_type_t type)
+{
+	struct hal_cmd_avrcp_register_notification cmd;
+
+	cmd.event = BTRC_EVT_TRACK_REACHED_START;
+	cmd.type = type;
+	cmd.len = 0;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type,
+							uint32_t *song_pos)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	size_t len;
+
+	cmd->event = BTRC_EVT_PLAY_POS_CHANGED;
+	cmd->type = type;
+	cmd->len = sizeof(*song_pos);
+	memcpy(cmd->data, song_pos, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t settings_changed_rsp(btrc_notification_type_t type,
+					btrc_player_settings_t *player_setting)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+	struct hal_avrcp_player_attr_value *attrs;
+	size_t len, param_len;
+	int i;
+
+	param_len = player_setting->num_attr * sizeof(*attrs);
+	len = sizeof(*cmd) + param_len;
+
+	if (len > IPC_MTU)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED;
+	cmd->type = type;
+	cmd->len = param_len;
+
+	attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0];
+	for (i = 0; i < player_setting->num_attr; i++) {
+		attrs[i].attr = player_setting->attr_ids[i];
+		attrs[i].value = player_setting->attr_values[i];
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+					HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t register_notification_rsp(btrc_event_id_t event_id,
+					btrc_notification_type_t type,
+					btrc_register_notification_t *p_param)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	switch (event_id) {
+	case BTRC_EVT_PLAY_STATUS_CHANGED:
+		return play_status_changed_rsp(type, &p_param->play_status);
+	case BTRC_EVT_TRACK_CHANGE:
+		return track_change_rsp(type, &p_param->track);
+	case BTRC_EVT_TRACK_REACHED_END:
+		return track_reached_end_rsp(type);
+	case BTRC_EVT_TRACK_REACHED_START:
+		return track_reached_start_rsp(type);
+	case BTRC_EVT_PLAY_POS_CHANGED:
+		return play_pos_changed_rsp(type, &p_param->song_pos);
+	case BTRC_EVT_APP_SETTINGS_CHANGED:
+		return settings_changed_rsp(type, &p_param->player_setting);
+	default:
+		return BT_STATUS_PARM_INVALID;
+	}
+}
+
+static bt_status_t set_volume(uint8_t volume)
+{
+	struct hal_cmd_avrcp_set_volume cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.value = volume;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_AVRCP;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+
+	cbs = NULL;
+}
+
+static btrc_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.get_play_status_rsp = get_play_status_rsp,
+	.list_player_app_attr_rsp = list_player_app_attr_rsp,
+	.list_player_app_value_rsp = list_player_app_value_rsp,
+	.get_player_app_value_rsp = get_player_app_value_rsp,
+	.get_player_app_attr_text_rsp = get_player_app_attr_text_rsp,
+	.get_player_app_value_text_rsp = get_player_app_value_text_rsp,
+	.get_element_attr_rsp = get_element_attr_rsp,
+	.set_player_app_value_rsp = set_player_app_value_rsp,
+	.register_notification_rsp = register_notification_rsp,
+	.set_volume = set_volume,
+	.cleanup = cleanup
+};
+
+btrc_interface_t *bt_get_avrcp_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-bluetooth.c b/bluez/repo/android/hal-bluetooth.c
new file mode 100644
index 0000000..66f4a37
--- /dev/null
+++ b/bluez/repo/android/hal-bluetooth.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const bt_callbacks_t *bt_hal_cbacks = NULL;
+
+#define enum_prop_to_hal(prop, hal_prop, type) do { \
+	static type e; \
+	prop.val = &e; \
+	prop.len = sizeof(e); \
+	e = *((uint8_t *) (hal_prop->val)); \
+} while (0)
+
+#define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \
+	enum_type e; \
+	if (prop->len != sizeof(e)) { \
+		error("invalid HAL property %u (%u vs %zu), aborting ", \
+					prop->type, prop->len, sizeof(e)); \
+		exit(EXIT_FAILURE); \
+	} \
+	memcpy(&e, prop->val, sizeof(e)); \
+	*((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \
+	*hal_len = 1; \
+} while (0)
+
+static void handle_adapter_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_adapter_state_changed *ev = buf;
+
+	DBG("state: %s", bt_state_t2str(ev->state));
+
+	if (bt_hal_cbacks->adapter_state_changed_cb)
+		bt_hal_cbacks->adapter_state_changed_cb(ev->state);
+}
+
+static void adapter_props_to_hal(bt_property_t *send_props,
+					struct hal_property *prop,
+					uint8_t num_props, uint16_t len)
+{
+	void *buf = prop;
+	uint8_t i;
+
+	for (i = 0; i < num_props; i++) {
+		if (sizeof(*prop) + prop->len > len) {
+			error("invalid adapter properties(%zu > %u), aborting",
+					sizeof(*prop) + prop->len, len);
+			exit(EXIT_FAILURE);
+		}
+
+		send_props[i].type = prop->type;
+
+		switch (prop->type) {
+		case HAL_PROP_ADAPTER_TYPE:
+			enum_prop_to_hal(send_props[i], prop,
+							bt_device_type_t);
+			break;
+		case HAL_PROP_ADAPTER_SCAN_MODE:
+			enum_prop_to_hal(send_props[i], prop,
+							bt_scan_mode_t);
+			break;
+		case HAL_PROP_ADAPTER_SERVICE_REC:
+		default:
+			send_props[i].len = prop->len;
+			send_props[i].val = prop->val;
+			break;
+		}
+
+		DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+
+		len -= sizeof(*prop) + prop->len;
+		buf += sizeof(*prop) + prop->len;
+		prop = buf;
+	}
+
+	if (!len)
+		return;
+
+	error("invalid adapter properties (%u bytes left), aborting", len);
+	exit(EXIT_FAILURE);
+}
+
+static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type,
+						uint16_t *len, void *val)
+{
+	/* type match IPC type */
+	*type = property->type;
+
+	switch (property->type) {
+	case HAL_PROP_ADAPTER_SCAN_MODE:
+		enum_prop_from_hal(property, len, val, bt_scan_mode_t);
+		break;
+	case BT_PROPERTY_BDNAME:
+	case BT_PROPERTY_BDADDR:
+	case BT_PROPERTY_UUIDS:
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+	case BT_PROPERTY_SERVICE_RECORD:
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+	case BT_PROPERTY_REMOTE_RSSI:
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+#endif
+	default:
+		*len = property->len;
+		memcpy(val, property->val, property->len);
+		break;
+	}
+}
+
+static void device_props_to_hal(bt_property_t *send_props,
+				struct hal_property *prop, uint8_t num_props,
+				uint16_t len)
+{
+	void *buf = prop;
+	uint8_t i;
+
+	for (i = 0; i < num_props; i++) {
+		if (sizeof(*prop) + prop->len > len) {
+			error("invalid device properties (%zu > %u), aborting",
+					sizeof(*prop) + prop->len, len);
+			exit(EXIT_FAILURE);
+		}
+
+		send_props[i].type = prop->type;
+
+		switch (prop->type) {
+		case HAL_PROP_DEVICE_TYPE:
+			enum_prop_to_hal(send_props[i], prop,
+							bt_device_type_t);
+			break;
+		case HAL_PROP_DEVICE_VERSION_INFO:
+		{
+			static bt_remote_version_t e;
+			const struct hal_prop_device_info *p;
+
+			send_props[i].val = &e;
+			send_props[i].len = sizeof(e);
+
+			p = (struct hal_prop_device_info *) prop->val;
+
+			e.manufacturer = p->manufacturer;
+			e.sub_ver = p->sub_version;
+			e.version = p->version;
+		}
+			break;
+		case HAL_PROP_DEVICE_SERVICE_REC:
+		{
+			static bt_service_record_t e;
+			const struct hal_prop_device_service_rec *p;
+
+			send_props[i].val = &e;
+			send_props[i].len = sizeof(e);
+
+			p = (struct hal_prop_device_service_rec *) prop->val;
+
+			memset(&e, 0, sizeof(e));
+			memcpy(&e.channel, &p->channel, sizeof(e.channel));
+			memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu));
+			memcpy(e.name, p->name, p->name_len);
+		}
+			break;
+		default:
+			send_props[i].len = prop->len;
+			send_props[i].val = prop->val;
+			break;
+		}
+
+		len -= sizeof(*prop) + prop->len;
+		buf += sizeof(*prop) + prop->len;
+		prop = buf;
+
+		DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+	}
+
+	if (!len)
+		return;
+
+	error("invalid device properties (%u bytes left), aborting", len);
+	exit(EXIT_FAILURE);
+}
+
+static void handle_adapter_props_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_adapter_props_changed *ev = buf;
+	bt_property_t props[ev->num_props];
+
+	DBG("");
+
+	if (!bt_hal_cbacks->adapter_properties_cb)
+		return;
+
+	len -= sizeof(*ev);
+	adapter_props_to_hal(props, ev->props, ev->num_props, len);
+
+	bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props);
+}
+
+static void handle_bond_state_change(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_bond_state_changed *ev = buf;
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+
+	DBG("state %u", ev->state);
+
+	if (bt_hal_cbacks->bond_state_changed_cb)
+		bt_hal_cbacks->bond_state_changed_cb(ev->status, addr,
+								ev->state);
+}
+
+static void handle_pin_request(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_pin_request *ev = buf;
+	/* Those are declared as packed, so it's safe to assign pointers */
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+	bt_bdname_t *name = (bt_bdname_t *) ev->name;
+
+	DBG("");
+
+	if (bt_hal_cbacks->pin_request_cb)
+		bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev);
+}
+
+static void handle_ssp_request(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_ssp_request *ev = buf;
+	/* Those are declared as packed, so it's safe to assign pointers */
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+	bt_bdname_t *name = (bt_bdname_t *) ev->name;
+
+	DBG("");
+
+	if (bt_hal_cbacks->ssp_request_cb)
+		bt_hal_cbacks->ssp_request_cb(addr, name, ev->class_of_dev,
+							ev->pairing_variant,
+							ev->passkey);
+}
+
+void bt_thread_associate(void)
+{
+	if (bt_hal_cbacks->thread_evt_cb)
+		bt_hal_cbacks->thread_evt_cb(ASSOCIATE_JVM);
+}
+
+void bt_thread_disassociate(void)
+{
+	if (bt_hal_cbacks->thread_evt_cb)
+		bt_hal_cbacks->thread_evt_cb(DISASSOCIATE_JVM);
+}
+
+static bool interface_ready(void)
+{
+	return bt_hal_cbacks != NULL;
+}
+
+static void handle_discovery_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_discovery_state_changed *ev = buf;
+
+	DBG("");
+
+	if (bt_hal_cbacks->discovery_state_changed_cb)
+		bt_hal_cbacks->discovery_state_changed_cb(ev->state);
+}
+
+static void handle_device_found(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_device_found *ev = buf;
+	bt_property_t props[ev->num_props];
+
+	DBG("");
+
+	if (!bt_hal_cbacks->device_found_cb)
+		return;
+
+	len -= sizeof(*ev);
+	device_props_to_hal(props, ev->props, ev->num_props, len);
+
+	bt_hal_cbacks->device_found_cb(ev->num_props, props);
+}
+
+static void handle_device_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_remote_device_props *ev = buf;
+	bt_property_t props[ev->num_props];
+
+	DBG("");
+
+	if (!bt_hal_cbacks->remote_device_properties_cb)
+		return;
+
+	len -= sizeof(*ev);
+	device_props_to_hal(props, ev->props, ev->num_props, len);
+
+	bt_hal_cbacks->remote_device_properties_cb(ev->status,
+						(bt_bdaddr_t *)ev->bdaddr,
+						ev->num_props, props);
+}
+
+static void handle_acl_state_changed(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_acl_state_changed *ev = buf;
+	bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
+
+	DBG("state %u", ev->state);
+
+	if (bt_hal_cbacks->acl_state_changed_cb)
+		bt_hal_cbacks->acl_state_changed_cb(ev->status, addr,
+								ev->state);
+}
+
+static void handle_dut_mode_receive(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_dut_mode_receive *ev = buf;
+
+	DBG("");
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("invalid dut mode receive event (%u), aborting", len);
+		exit(EXIT_FAILURE);
+	}
+
+	if (bt_hal_cbacks->dut_mode_recv_cb)
+		bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len);
+}
+
+static void handle_le_test_mode(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_le_test_mode *ev = buf;
+
+	DBG("");
+
+	if (bt_hal_cbacks->le_test_mode_cb)
+		bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets);
+}
+
+static void handle_energy_info(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_energy_info *ev = buf;
+	bt_activity_energy_info info;
+
+	DBG("");
+
+	info.ctrl_state = ev->ctrl_state;
+	info.energy_used = ev->energy_used;
+	info.idle_time = ev->idle_time;
+	info.rx_time = ev->rx_time;
+	info.status = ev->status;
+	info.tx_time = ev->status;
+
+	if (bt_hal_cbacks->energy_info_cb)
+		bt_hal_cbacks->energy_info_cb(&info);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_ADAPTER_STATE_CHANGED */
+	{ handle_adapter_state_changed, false,
+				sizeof(struct hal_ev_adapter_state_changed) },
+	/* HAL_EV_ADAPTER_PROPS_CHANGED */
+	{ handle_adapter_props_changed, true,
+				sizeof(struct hal_ev_adapter_props_changed) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_REMOTE_DEVICE_PROPS */
+	{ handle_device_state_changed, true,
+				sizeof(struct hal_ev_remote_device_props) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_DEVICE_FOUND */
+	{ handle_device_found, true, sizeof(struct hal_ev_device_found) +
+				sizeof(struct hal_property) },
+	/* HAL_EV_DISCOVERY_STATE_CHANGED */
+	{ handle_discovery_state_changed, false,
+				sizeof(struct hal_ev_discovery_state_changed) },
+	/* HAL_EV_PIN_REQUEST */
+	{ handle_pin_request, false, sizeof(struct hal_ev_pin_request) },
+	/* HAL_EV_SSP_REQUEST */
+	{ handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) },
+	/* HAL_EV_BOND_STATE_CHANGED */
+	{ handle_bond_state_change, false,
+				sizeof(struct hal_ev_bond_state_changed) },
+	/* HAL_EV_ACL_STATE_CHANGED */
+	{ handle_acl_state_changed, false,
+				sizeof(struct hal_ev_acl_state_changed) },
+	/* HAL_EV_DUT_MODE_RECEIVE */
+	{ handle_dut_mode_receive, true,
+				sizeof(struct hal_ev_dut_mode_receive) },
+	/* HAL_EV_LE_TEST_MODE */
+	{ handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) },
+	/* HAL_EV_ENERGY_INFO */
+	{ handle_energy_info, false, sizeof(struct hal_ev_energy_info) },
+};
+
+static uint8_t get_mode(void)
+{
+	char value[PROPERTY_VALUE_MAX];
+
+	if (get_config("mode", value, NULL) > 0) {
+		if (!strcasecmp(value, "bredr"))
+			return HAL_MODE_BREDR;
+
+		if (!strcasecmp(value, "le"))
+			return HAL_MODE_LE;
+	}
+
+	return HAL_MODE_DEFAULT;
+}
+
+static uint16_t add_prop(const char *prop, uint8_t type, void *buf)
+{
+	struct hal_config_prop *hal_prop = buf;
+
+	hal_prop->type = type;
+	hal_prop->len = strlen(prop) + 1;
+	memcpy(hal_prop->val, prop, hal_prop->len);
+
+	return sizeof(*hal_prop) + hal_prop->len;
+}
+
+static int send_configuration(void)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_configuration *cmd = (void *) buf;
+	char prop[PROPERTY_VALUE_MAX];
+	uint16_t len = sizeof(*cmd);
+
+	cmd->num = 0;
+
+	if (get_config("vendor", prop, "ro.product.manufacturer") > 0) {
+		len += add_prop(prop, HAL_CONFIG_VENDOR, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("name", prop, "ro.product.name") > 0) {
+		len += add_prop(prop, HAL_CONFIG_NAME, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("model", prop, "ro.product.model") > 0) {
+		len += add_prop(prop, HAL_CONFIG_MODEL, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("serialno", prop, "ro.serialno") > 0) {
+		len += add_prop(prop, HAL_CONFIG_SERIAL_NUMBER, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("systemid", prop, NULL) > 0) {
+		len += add_prop(prop, HAL_CONFIG_SYSTEM_ID, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("pnpid", prop, NULL) > 0) {
+		len += add_prop(prop, HAL_CONFIG_PNP_ID, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("fwrev", prop, "ro.build.version.release") > 0) {
+		len += add_prop(prop, HAL_CONFIG_FW_REV, buf + len);
+		cmd->num++;
+	}
+
+	if (get_config("hwrev", prop, "ro.board.platform") > 0) {
+		len += add_prop(prop, HAL_CONFIG_HW_REV, buf + len);
+		cmd->num++;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION, len, cmd,
+							NULL, NULL, NULL);
+}
+
+static int init(bt_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int status;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)))
+		return BT_STATUS_FAIL;
+
+	bt_hal_cbacks = callbacks;
+
+	/* Start Android Bluetooth daemon service */
+	if (property_set("bluetooth.start", "daemon") < 0) {
+		error("Failed to set bluetooth.start=daemon");
+		hal_ipc_cleanup();
+		bt_hal_cbacks = NULL;
+		return BT_STATUS_FAIL;
+	}
+
+	if (!hal_ipc_accept()) {
+		hal_ipc_cleanup();
+		bt_hal_cbacks = NULL;
+		return BT_STATUS_FAIL;
+	}
+
+	status = send_configuration();
+	if (status != BT_STATUS_SUCCESS) {
+		error("Failed to send configuration");
+		goto fail;
+	}
+
+	cmd.service_id = HAL_SERVICE_ID_BLUETOOTH;
+	cmd.mode = get_mode();
+	cmd.max_clients = 1;
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+	if (status != BT_STATUS_SUCCESS) {
+		error("Failed to register 'bluetooth' service");
+		goto fail;
+	}
+
+	cmd.service_id = HAL_SERVICE_ID_SOCKET;
+	cmd.max_clients = 1;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	cmd.mode = HAL_MODE_SOCKET_DYNAMIC_MAP;
+#else
+	cmd.mode = HAL_MODE_DEFAULT;
+#endif
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+	if (status != BT_STATUS_SUCCESS) {
+		error("Failed to register 'socket' service");
+		goto fail;
+	}
+
+	return status;
+
+fail:
+	hal_ipc_cleanup();
+	bt_hal_cbacks = NULL;
+
+	hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
+	return status;
+}
+
+static int enable(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, NULL,
+							NULL, NULL, NULL);
+}
+
+static int disable(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, NULL,
+							NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	hal_ipc_cleanup();
+
+	hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
+	bt_hal_cbacks = NULL;
+}
+
+static int get_adapter_properties(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS,
+						0, NULL, NULL, NULL, NULL);
+}
+
+static int get_adapter_property(bt_property_type_t type)
+{
+	struct hal_cmd_get_adapter_prop cmd;
+
+	DBG("prop: %s (%d)", bt_property_type_t2str(type), type);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	/* type match IPC type */
+	cmd.type = type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int set_adapter_property(const bt_property_t *property)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_set_adapter_prop *cmd = (void *) buf;
+	size_t len;
+
+	DBG("prop: %s", btproperty2str(property));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+						len, cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_device_properties(bt_bdaddr_t *remote_addr)
+{
+	struct hal_cmd_get_remote_device_props cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROPS,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_device_property(bt_bdaddr_t *remote_addr,
+						bt_property_type_t type)
+{
+	struct hal_cmd_get_remote_device_prop cmd;
+
+	DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
+						bt_property_type_t2str(type));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+	/* type match IPC type */
+	cmd.type = type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_DEVICE_PROP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int set_remote_device_property(bt_bdaddr_t *remote_addr,
+						const bt_property_t *property)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_set_remote_device_prop *cmd = (void *) buf;
+	size_t len;
+
+	DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
+				bt_property_type_t2str(property->type));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr));
+
+	/* type match IPC type */
+	cmd->type = property->type;
+	cmd->len = property->len;
+	memcpy(cmd->val, property->val, property->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_SET_REMOTE_DEVICE_PROP,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid)
+{
+	struct hal_cmd_get_remote_service_rec cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+	memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+					HAL_OP_GET_REMOTE_SERVICE_REC,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_remote_services(bt_bdaddr_t *remote_addr)
+{
+	struct hal_cmd_get_remote_services cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(remote_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int start_discovery(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, 0,
+						NULL, NULL, NULL, NULL);
+}
+
+static int cancel_discovery(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, 0,
+						NULL, NULL, NULL, NULL);
+}
+
+static int create_bond_real(const bt_bdaddr_t *bd_addr, int transport)
+{
+	struct hal_cmd_create_bond cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.transport = transport;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int create_bond(const bt_bdaddr_t *bd_addr, int transport)
+{
+	return create_bond_real(bd_addr, transport);
+}
+#else
+static int create_bond(const bt_bdaddr_t *bd_addr)
+{
+	return create_bond_real(bd_addr, BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static int cancel_bond(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_cancel_bond cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int remove_bond(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_remove_bond cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int pin_reply(const bt_bdaddr_t *bd_addr, uint8_t accept,
+				uint8_t pin_len, bt_pin_code_t *pin_code)
+{
+	struct hal_cmd_pin_reply cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.accept = accept;
+	cmd.pin_len = pin_len;
+	memcpy(cmd.pin_code, pin_code, sizeof(cmd.pin_code));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
+					uint8_t accept, uint32_t passkey)
+{
+	struct hal_cmd_ssp_reply cmd;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	/* type match IPC type */
+	cmd.ssp_variant = variant;
+	cmd.accept = accept;
+	cmd.passkey = passkey;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static const void *get_profile_interface(const char *profile_id)
+{
+	DBG("%s", profile_id);
+
+	if (!interface_ready())
+		return NULL;
+
+	if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID))
+		return bt_get_socket_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID))
+		return bt_get_hidhost_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_PAN_ID))
+		return bt_get_pan_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
+		return bt_get_a2dp_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID))
+		return bt_get_avrcp_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID))
+		return bt_get_handsfree_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_GATT_ID))
+		return bt_get_gatt_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID))
+		return bt_get_health_interface();
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (!strcmp(profile_id, BT_PROFILE_AV_RC_CTRL_ID))
+		return bt_get_avrcp_ctrl_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID))
+		return bt_get_hf_client_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_MAP_CLIENT_ID))
+		return bt_get_map_client_interface();
+
+	if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID))
+		return bt_get_a2dp_sink_interface();
+#endif
+
+	return NULL;
+}
+
+static int dut_mode_configure(uint8_t enable)
+{
+	struct hal_cmd_dut_mode_conf cmd;
+
+	DBG("enable %u", enable);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.enable = enable;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+	char cmd_buf[IPC_MTU];
+	struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf;
+	size_t len;
+
+	DBG("opcode %u len %u", opcode, buf_len);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->opcode = opcode;
+	cmd->len = buf_len;
+	memcpy(cmd->data, buf, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+						len, cmd, NULL, NULL, NULL);
+}
+
+static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+	char cmd_buf[IPC_MTU];
+	struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf;
+	size_t len;
+
+	DBG("opcode %u len %u", opcode, buf_len);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->opcode = opcode;
+	cmd->len = buf_len;
+	memcpy(cmd->data, buf, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+						len, cmd, NULL, NULL, NULL);
+}
+
+static int config_hci_snoop_log(uint8_t enable)
+{
+	const char *property;
+
+	DBG("enable %u", enable);
+
+	property = enable ? "bluetooth.start" : "bluetooth.stop";
+
+	if (property_set(property, "snoop") < 0) {
+		error("Failed to set %s=snoop", property);
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int get_connection_state(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_get_connection_state cmd;
+	struct hal_rsp_get_connection_state rsp;
+	size_t rsp_len = sizeof(rsp);
+	bt_status_t status;
+
+	DBG("bdaddr: %s", bdaddr2str(bd_addr));
+
+	if (!interface_ready())
+		return 0;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_CONNECTION_STATE, sizeof(cmd), &cmd,
+			&rsp_len, &rsp, NULL);
+
+	if (status != BT_STATUS_SUCCESS)
+		return 0;
+
+	return rsp.connection_state;
+}
+
+static int set_os_callouts(bt_os_callouts_t *callouts)
+{
+	DBG("callouts: %p", callouts);
+
+	/* TODO: implement */
+
+	return BT_STATUS_SUCCESS;
+}
+
+static int read_energy_info(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_READ_ENERGY_INFO, 0,
+							NULL, NULL, NULL, NULL);
+}
+#endif
+
+static const bt_interface_t bluetooth_if = {
+	.size = sizeof(bt_interface_t),
+	.init = init,
+	.enable = enable,
+	.disable = disable,
+	.cleanup = cleanup,
+	.get_adapter_properties = get_adapter_properties,
+	.get_adapter_property = get_adapter_property,
+	.set_adapter_property = set_adapter_property,
+	.get_remote_device_properties = get_remote_device_properties,
+	.get_remote_device_property = get_remote_device_property,
+	.set_remote_device_property = set_remote_device_property,
+	.get_remote_service_record = get_remote_service_record,
+	.get_remote_services = get_remote_services,
+	.start_discovery = start_discovery,
+	.cancel_discovery = cancel_discovery,
+	.create_bond = create_bond,
+	.remove_bond = remove_bond,
+	.cancel_bond = cancel_bond,
+	.pin_reply = pin_reply,
+	.ssp_reply = ssp_reply,
+	.get_profile_interface = get_profile_interface,
+	.dut_mode_configure = dut_mode_configure,
+	.dut_mode_send = dut_mode_send,
+	.le_test_mode = le_test_mode,
+	.config_hci_snoop_log = config_hci_snoop_log,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.get_connection_state = get_connection_state,
+	.set_os_callouts = set_os_callouts,
+	.read_energy_info = read_energy_info,
+#endif
+};
+
+static const bt_interface_t *get_bluetooth_interface(void)
+{
+	DBG("");
+
+	return &bluetooth_if;
+}
+
+static int close_bluetooth(struct hw_device_t *device)
+{
+	DBG("");
+
+	cleanup();
+
+	free(device);
+
+	return 0;
+}
+
+static int open_bluetooth(const struct hw_module_t *module, char const *name,
+					struct hw_device_t **device)
+{
+	bluetooth_device_t *dev = malloc(sizeof(bluetooth_device_t));
+
+	DBG("");
+
+	if (!dev) {
+		error("Failed to allocate memory for device");
+		return -ENOMEM;
+	}
+
+	memset(dev, 0, sizeof(bluetooth_device_t));
+	dev->common.tag = HARDWARE_DEVICE_TAG;
+	dev->common.version = 0;
+	dev->common.module = (struct hw_module_t *) module;
+	dev->common.close = close_bluetooth;
+	dev->get_bluetooth_interface = get_bluetooth_interface;
+
+	*device = (struct hw_device_t *) dev;
+
+	return 0;
+}
+
+static struct hw_module_methods_t bluetooth_module_methods = {
+	.open = open_bluetooth,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+	.tag = HARDWARE_MODULE_TAG,
+	.version_major = 1,
+	.version_minor = 0,
+	.id = BT_HARDWARE_MODULE_ID,
+	.name = "BlueZ Bluetooth stack",
+	.author = "Intel Corporation",
+	.methods = &bluetooth_module_methods
+};
diff --git a/bluez/repo/android/hal-gatt.c b/bluez/repo/android/hal-gatt.c
new file mode 100644
index 0000000..f7217c7
--- /dev/null
+++ b/bluez/repo/android/hal-gatt.c
@@ -0,0 +1,2103 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const btgatt_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void gatt_id_from_hal(btgatt_gatt_id_t *to,
+						struct hal_gatt_gatt_id *from)
+{
+	memcpy(&to->uuid, from->uuid, sizeof(to->uuid));
+	to->inst_id = from->inst_id;
+}
+
+static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from)
+{
+	memcpy(to->uuid, &from->uuid, sizeof(from->uuid));
+	to->inst_id = from->inst_id;
+}
+
+static void srvc_id_from_hal(btgatt_srvc_id_t *to,
+						struct hal_gatt_srvc_id *from)
+{
+	memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid));
+	to->id.inst_id = from->inst_id;
+	to->is_primary = from->is_primary;
+}
+
+static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from)
+{
+	memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid));
+	to->inst_id = from->id.inst_id;
+	to->is_primary = from->is_primary;
+}
+
+/* Client Event Handlers */
+
+static void handle_register_client(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_register_client *ev = buf;
+
+	if (cbs->client->register_client_cb)
+		cbs->client->register_client_cb(ev->status, ev->client_if,
+						(bt_uuid_t *) ev->app_uuid);
+}
+
+static void handle_scan_result(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_scan_result *ev = buf;
+	uint8_t ad[62];
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("gatt: invalid scan result event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Java assumes that passed data has 62 bytes */
+	memset(ad, 0, sizeof(ad));
+	memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len);
+
+	if (cbs->client->scan_result_cb)
+		cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi,
+									ad);
+}
+
+static void handle_connect(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_connect *ev = buf;
+
+	if (cbs->client->open_cb)
+		cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if,
+						(bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_disconnect(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_disconnect *ev = buf;
+
+	if (cbs->client->close_cb)
+		cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if,
+						(bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_search_complete(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_search_complete *ev = buf;
+
+	if (cbs->client->search_complete_cb)
+		cbs->client->search_complete_cb(ev->conn_id, ev->status);
+}
+
+static void handle_search_result(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_search_result *ev = buf;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+	if (cbs->client->search_result_cb)
+		cbs->client->search_result_cb(ev->conn_id, &srvc_id);
+}
+
+static void handle_get_characteristic(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_get_characteristic *ev = buf;
+	btgatt_gatt_id_t char_id;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&char_id, &ev->char_id);
+
+	if (cbs->client->get_characteristic_cb)
+		cbs->client->get_characteristic_cb(ev->conn_id, ev->status,
+							&srvc_id, &char_id,
+							ev->char_prop);
+}
+
+static void handle_get_descriptor(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_get_descriptor *ev = buf;
+	btgatt_gatt_id_t descr_id;
+	btgatt_gatt_id_t char_id;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&char_id, &ev->char_id);
+	gatt_id_from_hal(&descr_id, &ev->descr_id);
+
+	if (cbs->client->get_descriptor_cb)
+		cbs->client->get_descriptor_cb(ev->conn_id, ev->status,
+						&srvc_id, &char_id, &descr_id);
+}
+
+static void handle_get_included_service(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_get_inc_service *ev = buf;
+	btgatt_srvc_id_t srvc_id;
+	btgatt_srvc_id_t incl_srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id);
+
+	if (cbs->client->get_included_service_cb)
+		cbs->client->get_included_service_cb(ev->conn_id, ev->status,
+								&srvc_id,
+								&incl_srvc_id);
+}
+
+static void handle_register_for_notification(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_reg_for_notif *ev = buf;
+	btgatt_gatt_id_t char_id;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&char_id, &ev->char_id);
+
+	if (cbs->client->register_for_notification_cb)
+		cbs->client->register_for_notification_cb(ev->conn_id,
+								ev->registered,
+								ev->status,
+								&srvc_id,
+								&char_id);
+}
+
+static void handle_notify(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_notify *ev = buf;
+	btgatt_notify_params_t params;
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("gatt: invalid notify event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&params, 0, sizeof(params));
+	memcpy(params.value, ev->value, ev->len);
+	memcpy(&params.bda, ev->bda, sizeof(params.bda));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->char_id);
+
+	params.len = ev->len;
+	params.is_notify = ev->is_notify;
+
+	if (cbs->client->notify_cb)
+		cbs->client->notify_cb(ev->conn_id, &params);
+}
+
+static void handle_read_characteristic(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_read_characteristic *ev = buf;
+	btgatt_read_params_t params;
+
+	if (len != sizeof(*ev) + ev->data.len) {
+		error("gatt: invalid read characteristic event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	memcpy(&params.value.value, ev->data.value, ev->data.len);
+
+	params.value_type = ev->data.value_type;
+	params.value.len = ev->data.len;
+	params.status = ev->data.status;
+
+	if (cbs->client->read_characteristic_cb)
+		cbs->client->read_characteristic_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_write_characteristic(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_write_characteristic *ev = buf;
+	btgatt_write_params_t params;
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	params.status = ev->data.status;
+
+	if (cbs->client->write_characteristic_cb)
+		cbs->client->write_characteristic_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_read_descriptor(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_read_descriptor *ev = buf;
+	btgatt_read_params_t params;
+
+	if (len != sizeof(*ev) + ev->data.len) {
+		error("gatt: invalid read descriptor event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	memcpy(&params.value.value, ev->data.value, ev->data.len);
+
+	params.value_type = ev->data.value_type;
+	params.value.len = ev->data.len;
+	params.status = ev->data.status;
+
+	if (cbs->client->read_descriptor_cb)
+		cbs->client->read_descriptor_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_write_descriptor(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_write_descriptor *ev = buf;
+	btgatt_write_params_t params;
+
+	memset(&params, 0, sizeof(params));
+
+	srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+	gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+	gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+	params.status = ev->data.status;
+
+	if (cbs->client->write_descriptor_cb)
+		cbs->client->write_descriptor_cb(ev->conn_id, ev->status,
+								&params);
+}
+
+static void handle_execute_write(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_exec_write *ev = buf;
+
+	if (cbs->client->execute_write_cb)
+		cbs->client->execute_write_cb(ev->conn_id, ev->status);
+}
+
+static void handle_read_remote_rssi(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_read_remote_rssi *ev = buf;
+
+	if (cbs->client->read_remote_rssi_cb)
+		cbs->client->read_remote_rssi_cb(ev->client_if,
+						(bt_bdaddr_t *) ev->address,
+						ev->rssi, ev->status);
+}
+
+static void handle_listen(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_client_listen *ev = buf;
+
+	if (cbs->client->listen_cb)
+		cbs->client->listen_cb(ev->status, ev->server_if);
+}
+
+/* Server Event Handlers */
+
+static void handle_register_server(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_register *ev = buf;
+
+	if (cbs->server->register_server_cb)
+		cbs->server->register_server_cb(ev->status, ev->server_if,
+						(bt_uuid_t *) &ev->uuid);
+}
+
+static void handle_connection(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_connection *ev = buf;
+
+	if (cbs->server->connection_cb)
+		cbs->server->connection_cb(ev->conn_id, ev->server_if,
+						ev->connected,
+						(bt_bdaddr_t *) &ev->bdaddr);
+}
+
+static void handle_service_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_added *ev = buf;
+	btgatt_srvc_id_t srvc_id;
+
+	srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+	if (cbs->server->service_added_cb)
+		cbs->server->service_added_cb(ev->status, ev->server_if,
+						&srvc_id, ev->srvc_handle);
+}
+
+static void handle_included_service_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_inc_srvc_added *ev = buf;
+
+	if (cbs->server->included_service_added_cb)
+		cbs->server->included_service_added_cb(ev->status,
+							ev->server_if,
+							ev->srvc_handle,
+							ev->incl_srvc_handle);
+}
+
+static void handle_characteristic_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_characteristic_added *ev = buf;
+
+	if (cbs->server->characteristic_added_cb)
+		cbs->server->characteristic_added_cb(ev->status, ev->server_if,
+							(bt_uuid_t *) &ev->uuid,
+							ev->srvc_handle,
+							ev->char_handle);
+}
+
+static void handle_descriptor_added(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_descriptor_added *ev = buf;
+
+	if (cbs->server->descriptor_added_cb)
+		cbs->server->descriptor_added_cb(ev->status, ev->server_if,
+							(bt_uuid_t *) &ev->uuid,
+							ev->srvc_handle,
+							ev->descr_handle);
+}
+
+static void handle_service_started(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_started *ev = buf;
+
+	if (cbs->server->service_started_cb)
+		cbs->server->service_started_cb(ev->status, ev->server_if,
+							ev->srvc_handle);
+}
+
+static void handle_service_stopped(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_stopped *ev = buf;
+
+	if (cbs->server->service_stopped_cb)
+		cbs->server->service_stopped_cb(ev->status, ev->server_if,
+							ev->srvc_handle);
+}
+
+static void handle_service_deleted(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_service_deleted *ev = buf;
+
+	if (cbs->server->service_deleted_cb)
+		cbs->server->service_deleted_cb(ev->status, ev->server_if,
+							ev->srvc_handle);
+}
+
+static void handle_request_read(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_request_read *ev = buf;
+
+	if (cbs->server->request_read_cb)
+		cbs->server->request_read_cb(ev->conn_id, ev->trans_id,
+						(bt_bdaddr_t *) &ev->bdaddr,
+						ev->attr_handle, ev->offset,
+						ev->is_long);
+}
+
+static void handle_request_write(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_request_write *ev = buf;
+
+	if (len != sizeof(*ev) + ev->length) {
+		error("gatt: invalid request write event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (cbs->server->request_write_cb)
+		cbs->server->request_write_cb(ev->conn_id, ev->trans_id,
+						(bt_bdaddr_t *) ev->bdaddr,
+						ev->attr_handle, ev->offset,
+						ev->length, ev->need_rsp,
+						ev->is_prep, ev->value);
+}
+
+static void handle_request_exec_write(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_request_exec_write *ev = buf;
+
+	if (cbs->server->request_exec_write_cb)
+		cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id,
+						(bt_bdaddr_t *) ev->bdaddr,
+						ev->exec_write);
+}
+
+static void handle_response_confirmation(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
+
+	if (cbs->server->response_confirmation_cb)
+		cbs->server->response_confirmation_cb(ev->status, ev->handle);
+}
+
+static void handle_configure_mtu(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_configure_mtu *ev = buf;
+
+	if (cbs->client->configure_mtu_cb)
+		cbs->client->configure_mtu_cb(ev->conn_id, ev->status, ev->mtu);
+#endif
+}
+
+static void handle_filter_config(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_filter_config *ev = buf;
+
+	if (cbs->client->scan_filter_cfg_cb)
+		cbs->client->scan_filter_cfg_cb(ev->action, ev->client_if,
+						ev->status, ev->type,
+						ev->space);
+#endif
+}
+
+static void handle_filter_params(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_filter_params *ev = buf;
+
+	if (cbs->client->scan_filter_param_cb)
+		cbs->client->scan_filter_param_cb(ev->action, ev->client_if,
+							ev->status, ev->space);
+#endif
+}
+
+static void handle_filter_status(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_filter_status *ev = buf;
+
+	if (cbs->client->scan_filter_status_cb)
+		cbs->client->scan_filter_status_cb(ev->enable, ev->client_if,
+								ev->status);
+#endif
+}
+
+static void handle__multi_adv_enable(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_enable *ev = buf;
+
+	if (cbs->client->multi_adv_enable_cb)
+		cbs->client->multi_adv_enable_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_update(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_update *ev = buf;
+
+	if (cbs->client->multi_adv_update_cb)
+		cbs->client->multi_adv_update_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_data(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_data *ev = buf;
+
+	if (cbs->client->multi_adv_data_cb)
+		cbs->client->multi_adv_data_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_multi_adv_disable(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_multi_adv_disable *ev = buf;
+
+	if (cbs->client->multi_adv_disable_cb)
+		cbs->client->multi_adv_disable_cb(ev->client_if, ev->status);
+#endif
+}
+
+static void handle_client_congestion(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_congestion *ev = buf;
+
+	if (cbs->client->congestion_cb)
+		cbs->client->congestion_cb(ev->conn_id, ev->congested);
+#endif
+}
+
+static void handle_config_batchscan(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_config_batchscan *ev = buf;
+
+	if (cbs->client->batchscan_cfg_storage_cb)
+		cbs->client->batchscan_cfg_storage_cb(ev->client_if,
+								ev->status);
+#endif
+}
+
+static void handle_enable_batchscan(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_enable_batchscan *ev = buf;
+
+	if (cbs->client->batchscan_enb_disable_cb)
+		cbs->client->batchscan_enb_disable_cb(ev->action, ev->client_if,
+								ev->status);
+#endif
+}
+
+static void handle_client_batchscan_reports(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_batchscan_reports *ev = buf;
+
+	if (cbs->client->batchscan_reports_cb)
+		cbs->client->batchscan_reports_cb(ev->client_if, ev->status,
+							ev->format, ev->num,
+							ev->data_len, ev->data);
+#endif
+}
+
+static void handle_batchscan_threshold(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_batchscan_threshold *ev = buf;
+
+	if (cbs->client->batchscan_threshold_cb)
+		cbs->client->batchscan_threshold_cb(ev->client_if);
+#endif
+}
+
+static void handle_track_adv(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_client_track_adv *ev = buf;
+
+	if (cbs->client->track_adv_event_cb)
+		cbs->client->track_adv_event_cb(ev->client_if, ev->filetr_index,
+						ev->address_type,
+						(bt_bdaddr_t *) ev->address,
+						ev->state);
+#endif
+}
+
+static void handle_indication_send(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_server_indication_sent *ev = buf;
+
+	if (cbs->server->indication_sent_cb)
+		cbs->server->indication_sent_cb(ev->conn_id, ev->status);
+#endif
+}
+
+static void handle_server_congestion(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_gatt_server_congestion *ev = buf;
+
+	if (cbs->server->congestion_cb)
+		cbs->server->congestion_cb(ev->conn_id, ev->congested);
+#endif
+}
+
+static void handle_server_mtu_changed(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 1, 0)
+	struct hal_ev_gatt_server_mtu_changed *ev = buf;
+
+	if (cbs->server->mtu_changed_cb)
+		cbs->server->mtu_changed_cb(ev->conn_id, ev->mtu);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */
+	{ handle_register_client, false,
+		sizeof(struct hal_ev_gatt_client_register_client) },
+	/* HAL_EV_GATT_CLIENT_SCAN_RESULT */
+	{ handle_scan_result, true,
+		sizeof(struct hal_ev_gatt_client_scan_result) },
+	/* HAL_EV_GATT_CLIENT_CONNECT */
+	{ handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) },
+	/* HAL_EV_GATT_CLIENT_DISCONNECT */
+	{ handle_disconnect, false,
+		sizeof(struct hal_ev_gatt_client_disconnect) },
+	/* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */
+	{ handle_search_complete, false,
+		sizeof(struct hal_ev_gatt_client_search_complete) },
+	/* HAL_EV_GATT_CLIENT_SEARCH_RESULT */
+	{ handle_search_result, false,
+		sizeof(struct hal_ev_gatt_client_search_result) },
+	/* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */
+	{ handle_get_characteristic, false,
+		sizeof(struct hal_ev_gatt_client_get_characteristic) },
+	/* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */
+	{ handle_get_descriptor, false,
+		sizeof(struct hal_ev_gatt_client_get_descriptor) },
+	/* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */
+	{ handle_get_included_service, false,
+		sizeof(struct hal_ev_gatt_client_get_inc_service) },
+	/* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */
+	{ handle_register_for_notification, false,
+		sizeof(struct hal_ev_gatt_client_reg_for_notif) },
+	/* HAL_EV_GATT_CLIENT_NOTIFY */
+	{ handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) },
+	/* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */
+	{ handle_read_characteristic, true,
+		sizeof(struct hal_ev_gatt_client_read_characteristic) },
+	/* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */
+	{ handle_write_characteristic, false,
+		sizeof(struct hal_ev_gatt_client_write_characteristic) },
+	/* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */
+	{ handle_read_descriptor, true,
+		sizeof(struct hal_ev_gatt_client_read_descriptor) },
+	/* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */
+	{ handle_write_descriptor, false,
+		sizeof(struct hal_ev_gatt_client_write_descriptor) },
+	/* HAL_EV_GATT_CLIENT_EXEC_WRITE */
+	{ handle_execute_write, false,
+		sizeof(struct hal_ev_gatt_client_exec_write) },
+	/* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */
+	{ handle_read_remote_rssi, false,
+		sizeof(struct hal_ev_gatt_client_read_remote_rssi) },
+	/* HAL_EV_GATT_CLIENT_LISTEN */
+	{ handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) },
+	/* HAL_EV_GATT_SERVER_REGISTER */
+	{ handle_register_server, false,
+		sizeof(struct hal_ev_gatt_server_register) },
+	/* HAL_EV_GATT_SERVER_CONNECTION */
+	{ handle_connection, false,
+		sizeof(struct hal_ev_gatt_server_connection) },
+	/* HAL_EV_GATT_SERVER_SERVICE_ADDED */
+	{ handle_service_added, false,
+		sizeof(struct hal_ev_gatt_server_service_added) },
+	/* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */
+	{ handle_included_service_added, false,
+		sizeof(struct hal_ev_gatt_server_inc_srvc_added) },
+	/* HAL_EV_GATT_SERVER_CHAR_ADDED */
+	{ handle_characteristic_added, false,
+		sizeof(struct hal_ev_gatt_server_characteristic_added) },
+	/* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */
+	{ handle_descriptor_added, false,
+		sizeof(struct hal_ev_gatt_server_descriptor_added) },
+	/* HAL_EV_GATT_SERVER_SERVICE_STARTED */
+	{ handle_service_started, false,
+		sizeof(struct hal_ev_gatt_server_service_started) },
+	/* HAL_EV_GATT_SERVER_SERVICE_STOPPED */
+	{ handle_service_stopped, false,
+		sizeof(struct hal_ev_gatt_server_service_stopped) },
+	/* HAL_EV_GATT_SERVER_SERVICE_DELETED */
+	{ handle_service_deleted, false,
+		sizeof(struct hal_ev_gatt_server_service_deleted) },
+	/* HAL_EV_GATT_SERVER_REQUEST_READ */
+	{ handle_request_read, false,
+		sizeof(struct hal_ev_gatt_server_request_read) },
+	/* HAL_EV_GATT_SERVER_REQUEST_WRITE */
+	{ handle_request_write, true,
+		sizeof(struct hal_ev_gatt_server_request_write) },
+	/* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */
+	{ handle_request_exec_write, false,
+		sizeof(struct hal_ev_gatt_server_request_exec_write) },
+	/* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */
+	{ handle_response_confirmation, false,
+		sizeof(struct hal_ev_gatt_server_rsp_confirmation) },
+	/* HAL_EV_GATT_CLIENT_CONFIGURE_MTU */
+	{ handle_configure_mtu, false,
+		sizeof(struct hal_ev_gatt_client_configure_mtu) },
+	/* HAL_EV_GATT_CLIENT_FILTER_CONFIG */
+	{ handle_filter_config, false,
+		sizeof(struct hal_ev_gatt_client_filter_config) },
+	/* HAL_EV_GATT_CLIENT_FILTER_PARAMS */
+	{ handle_filter_params, false,
+		sizeof(struct hal_ev_gatt_client_filter_params) },
+	/* HAL_EV_GATT_CLIENT_FILTER_STATUS */
+	{ handle_filter_status, false,
+		sizeof(struct hal_ev_gatt_client_filter_status) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE */
+	{ handle__multi_adv_enable, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_enable) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE */
+	{ handle_multi_adv_update, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_update) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_DATA */
+	{ handle_multi_adv_data, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_data) },
+	/* HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE */
+	{ handle_multi_adv_disable, false,
+		sizeof(struct hal_ev_gatt_client_multi_adv_disable) },
+	/* HAL_EV_GATT_CLIENT_CONGESTION */
+	{ handle_client_congestion, false,
+		sizeof(struct hal_ev_gatt_client_congestion) },
+	/* HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN */
+	{ handle_config_batchscan, false,
+		sizeof(struct hal_ev_gatt_client_config_batchscan) },
+	/* HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN */
+	{ handle_enable_batchscan, false,
+		sizeof(struct hal_ev_gatt_client_enable_batchscan) },
+	/* HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS */
+	{ handle_client_batchscan_reports, true,
+		sizeof(struct hal_ev_gatt_client_batchscan_reports) },
+	/* HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD */
+	{ handle_batchscan_threshold, false,
+		sizeof(struct hal_ev_gatt_client_batchscan_threshold) },
+	/* HAL_EV_GATT_CLIENT_TRACK_ADV */
+	{ handle_track_adv, false,
+		sizeof(struct hal_ev_gatt_client_track_adv) },
+	/* HAL_EV_GATT_SERVER_INDICATION_SENT */
+	{ handle_indication_send, false,
+		sizeof(struct hal_ev_gatt_server_indication_sent) },
+	/* HAL_EV_GATT_SERVER_CONGESTION */
+	{ handle_server_congestion, false,
+		sizeof(struct hal_ev_gatt_server_congestion) },
+	/* HAL_EV_GATT_SERVER_MTU_CHANGED */
+	{ handle_server_mtu_changed, false,
+		sizeof(struct hal_ev_gatt_server_mtu_changed) },
+	};
+
+/* Client API */
+
+static bt_status_t register_client(bt_uuid_t *uuid)
+{
+	struct hal_cmd_gatt_client_register cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t unregister_client(int client_if)
+{
+	struct hal_cmd_gatt_client_unregister cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_real(int client_if, bool start)
+{
+	struct hal_cmd_gatt_client_scan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.start = start;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t scan(bool start)
+{
+	return scan_real(0, start);
+}
+#else
+static bt_status_t scan(int client_if, bool start)
+{
+	return scan_real(client_if, start);
+}
+#endif
+
+static bt_status_t connect_real(int client_if, const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	struct hal_cmd_gatt_client_connect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.is_direct = is_direct;
+	cmd.transport = transport;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	return connect_real(client_if, bd_addr, is_direct, transport);
+}
+#else
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+								bool is_direct)
+{
+	return connect_real(client_if, bd_addr, is_direct,
+							BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr,
+								int conn_id)
+{
+	struct hal_cmd_gatt_client_disconnect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.conn_id = conn_id;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t listen(int client_if, bool start)
+{
+	struct hal_cmd_gatt_client_listen cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.start = start;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_refresh cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_search_service *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	cmd->conn_id = conn_id;
+
+	if (filter_uuid) {
+		memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid));
+		len += sizeof(*filter_uuid);
+		cmd->filtered = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SEARCH_SERVICE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_srvc_id_t *start_incl_srvc_id)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	cmd->continuation = 0;
+
+	if (start_incl_srvc_id) {
+		srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id);
+		len += sizeof(cmd->incl_srvc_id[0]);
+		cmd->continuation = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *start_char_id)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	cmd->continuation = 0;
+
+	if (start_char_id) {
+		gatt_id_to_hal(&cmd->char_id[0], start_char_id);
+		len += sizeof(cmd->char_id[0]);
+		cmd->continuation = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					btgatt_gatt_id_t *start_descr_id)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf;
+	size_t len = sizeof(*cmd);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd->char_id, char_id);
+	cmd->continuation = 0;
+
+	if (start_descr_id) {
+		gatt_id_to_hal(&cmd->descr_id[0], start_descr_id);
+		len += sizeof(cmd->descr_id[0]);
+		cmd->continuation = 1;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+					len, cmd, NULL , NULL, NULL);
+}
+
+static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					int auth_req)
+{
+	struct hal_cmd_gatt_client_read_characteristic cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					int write_type, int len, int auth_req,
+					char *p_value)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+	cmd->write_type = write_type;
+	cmd->len = len;
+	cmd->auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd->char_id, char_id);
+
+	memcpy(cmd->value, p_value, len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id,
+						btgatt_gatt_id_t *descr_id,
+						int auth_req)
+{
+	struct hal_cmd_gatt_client_read_descriptor cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+	gatt_id_to_hal(&cmd.descr_id, descr_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_DESCRIPTOR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+					btgatt_gatt_id_t *char_id,
+					btgatt_gatt_id_t *descr_id,
+					int write_type, int len, int auth_req,
+					char *p_value)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+	cmd->write_type = write_type;
+	cmd->len = len;
+	cmd->auth_req = auth_req;
+
+	srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd->char_id, char_id);
+	gatt_id_to_hal(&cmd->descr_id, descr_id);
+
+	memcpy(cmd->value, p_value, len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t execute_write(int conn_id, int execute)
+{
+	struct hal_cmd_gatt_client_execute_write cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.execute = execute;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_EXECUTE_WRITE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t register_for_notification(int client_if,
+						const bt_bdaddr_t *bd_addr,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	struct hal_cmd_gatt_client_register_for_notification cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t deregister_for_notification(int client_if,
+						const bt_bdaddr_t *bd_addr,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	struct hal_cmd_gatt_client_deregister_for_notification cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+	gatt_id_to_hal(&cmd.char_id, char_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_read_remote_rssi cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int get_device_type(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_gatt_client_get_device_type cmd;
+	uint8_t dev_type;
+	size_t resp_len = sizeof(dev_type);
+	bt_status_t status;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+				sizeof(cmd), &cmd, &resp_len, &dev_type, NULL);
+
+	if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type))
+		return 0;
+
+	return dev_type;
+}
+
+static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data,
+				uint16_t service_data_len, char *service_data,
+				uint16_t service_uuid_len, char *service_uuid)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
+	size_t cmd_len;
+	uint8_t *data;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len +
+							service_uuid_len;
+
+	if (cmd_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
+	cmd->server_if = server_if;
+	cmd->set_scan_rsp = set_scan_rsp;
+	cmd->include_name = include_name;
+	cmd->include_txpower = include_txpower;
+	cmd->min_interval = min_interval;
+	cmd->max_interval = max_interval;
+	cmd->appearance = appearance;
+	cmd->manufacturer_len = manufacturer_len;
+	cmd->service_data_len = service_data_len;
+	cmd->service_uuid_len = service_uuid_len;
+
+	data = cmd->data;
+
+	if (manufacturer_data && manufacturer_len) {
+		memcpy(data, manufacturer_data, manufacturer_len);
+		data += manufacturer_len;
+	}
+
+	if (service_data && service_data_len) {
+		memcpy(data, service_data, service_data_len);
+		data += service_data_len;
+	}
+
+	if (service_uuid && service_uuid_len)
+		memcpy(data, service_uuid, service_uuid_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
+						cmd_len, cmd, NULL, NULL, NULL);
+}
+
+/*
+ * This is temporary solution and support for older Android versions might
+ * be removed at any time.
+ */
+#if ANDROID_VERSION < PLATFORM_VER(4, 4, 3)
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data)
+{
+	return set_adv_data_real(server_if, set_scan_rsp, include_name,
+					include_txpower, min_interval,
+					max_interval, appearance,
+					manufacturer_len, manufacturer_data,
+					0, NULL, 0, NULL);
+}
+#else
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+				bool include_name, bool include_txpower,
+				int min_interval, int max_interval,
+				int appearance, uint16_t manufacturer_len,
+				char *manufacturer_data,
+				uint16_t service_data_len, char *service_data,
+				uint16_t service_uuid_len, char *service_uuid)
+{
+	return set_adv_data_real(server_if, set_scan_rsp, include_name,
+					include_txpower, min_interval,
+					max_interval, appearance,
+					manufacturer_len, manufacturer_data,
+					service_data_len, service_data,
+					service_uuid_len, service_uuid);
+}
+#endif
+
+static bt_status_t test_command(int command, btgatt_test_params_t *params)
+{
+	struct hal_cmd_gatt_client_test_command cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.command = command;
+
+	memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1));
+	memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1));
+
+	cmd.u1 = params->u1;
+	cmd.u2 = params->u2;
+	cmd.u3 = params->u3;
+	cmd.u4 = params->u4;
+	cmd.u5 = params->u5;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t scan_filter_param_setup(int client_if, int action,
+						int filt_index, int feat_seln,
+						int list_logic_type,
+						int filt_logic_type,
+						int rssi_high_thres,
+						int rssi_low_thres,
+						int dely_mode,
+						int found_timeout,
+						int lost_timeout,
+						int found_timeout_cnt)
+{
+	struct hal_cmd_gatt_client_scan_filter_setup cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.action = action;
+	cmd.filter_index = filt_index;
+	cmd.features = feat_seln;
+	cmd.list_type = list_logic_type;
+	cmd.filter_type = filt_logic_type;
+	cmd.rssi_hi = rssi_high_thres;
+	cmd.rssi_lo = rssi_low_thres;
+	cmd.delivery_mode = dely_mode;
+	cmd.found_timeout = found_timeout;
+	cmd.lost_timeout = lost_timeout;
+	cmd.found_timeout_cnt = found_timeout_cnt;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_add_remove(int client_if, int action,
+						int filt_type, int filt_index,
+						int company_id,
+						int company_id_mask,
+						const bt_uuid_t *p_uuid,
+						const bt_uuid_t *p_uuid_mask,
+						const bt_bdaddr_t *bd_addr,
+						char addr_type,
+						int data_len, char *p_data,
+						int mask_len, char *p_mask)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_scan_filter_add_remove *cmd = (void *) buf;
+	size_t cmd_len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!p_uuid || !p_uuid_mask || !bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	cmd_len = sizeof(*cmd) + data_len + mask_len;
+	if (cmd_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
+	cmd->client_if = client_if;
+	cmd->action = action;
+	cmd->filter_type = filt_type;
+	cmd->filter_index = filt_index;
+	cmd->company_id = company_id;
+	cmd->company_id_mask = company_id_mask;
+	memcpy(cmd->uuid, p_uuid, sizeof(*p_uuid));
+	memcpy(cmd->uuid_mask, p_uuid_mask, sizeof(*p_uuid_mask));
+	memcpy(cmd->address, bd_addr, sizeof(*bd_addr));
+	cmd->address_type = addr_type;
+
+	cmd->data_len = data_len;
+	memcpy(cmd->data_mask, p_data, data_len);
+
+	cmd->mask_len = mask_len;
+	memcpy(cmd->data_mask + data_len, p_mask, mask_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE,
+				cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_clear(int client_if, int filt_index)
+{
+	struct hal_cmd_gatt_client_scan_filter_clear cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.index = filt_index;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t scan_filter_enable(int client_if, bool enable)
+{
+	struct hal_cmd_gatt_client_scan_filter_enable cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.enable = enable;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t configure_mtu(int conn_id, int mtu)
+{
+	struct hal_cmd_gatt_client_configure_mtu cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.conn_id = conn_id;
+	cmd.mtu = mtu;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t conn_parameter_update(const bt_bdaddr_t *bd_addr,
+						int min_interval,
+						int max_interval, int latency,
+						int timeout)
+{
+	struct hal_cmd_gatt_client_conn_param_update cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.address, bd_addr, sizeof(*bd_addr));
+	cmd.min_interval = min_interval;
+	cmd.max_interval = max_interval;
+	cmd.latency = latency;
+	cmd.timeout = timeout;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_scan_parameters(int scan_interval, int scan_window)
+{
+	struct hal_cmd_gatt_client_set_scan_param cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.interval = scan_interval;
+	cmd.window = scan_window;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SET_SCAN_PARAM,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_enable(int client_if, int min_interval,
+					int max_interval, int adv_type,
+					int chnl_map, int tx_power,
+					int timeout_s)
+{
+	struct hal_cmd_gatt_client_setup_multi_adv cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.min_interval = min_interval;
+	cmd.max_interval = max_interval;
+	cmd.type = adv_type;
+	cmd.channel_map = chnl_map;
+	cmd.tx_power = tx_power;
+	cmd.timeout = timeout_s;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_update(int client_if, int min_interval,
+					int max_interval, int adv_type,
+					int chnl_map, int tx_power,
+					int timeout_s)
+{
+	struct hal_cmd_gatt_client_update_multi_adv cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.min_interval = min_interval;
+	cmd.max_interval = max_interval;
+	cmd.type = adv_type;
+	cmd.channel_map = chnl_map;
+	cmd.tx_power = tx_power;
+	cmd.timeout = timeout_s;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_set_inst_data(int client_if, bool set_scan_rsp,
+						bool include_name,
+						bool incl_txpower,
+						int appearance,
+						int manufacturer_len,
+						char *manufacturer_data,
+						int service_data_len,
+						char *service_data,
+						int service_uuid_len,
+						char *service_uuid)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = (void *) buf;
+	int off = 0;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (manufacturer_len > 0 && !manufacturer_data)
+		return BT_STATUS_PARM_INVALID;
+
+	if (service_data_len > 0 && !service_data)
+		return BT_STATUS_PARM_INVALID;
+
+	if (service_uuid_len > 0 && !service_uuid)
+		return BT_STATUS_PARM_INVALID;
+
+	if (sizeof(*cmd) + manufacturer_len + service_data_len
+						+ service_uuid_len > IPC_MTU)
+		return BT_STATUS_FAIL;
+
+	cmd->client_if = client_if;
+	cmd->set_scan_rsp = set_scan_rsp;
+	cmd->include_name = include_name;
+	cmd->include_tx_power = incl_txpower;
+	cmd->appearance = appearance;
+	cmd->manufacturer_data_len = manufacturer_len;
+	cmd->service_data_len = service_data_len;
+	cmd->service_uuid_len = service_uuid_len;
+
+	if (manufacturer_len > 0) {
+		memcpy(cmd->data_service_uuid, manufacturer_data,
+							manufacturer_len);
+		off += manufacturer_len;
+	}
+
+	if (service_data_len > 0) {
+		memcpy(cmd->data_service_uuid + off, service_data,
+							service_data_len);
+		off += service_data_len;
+	}
+
+	if (service_uuid_len > 0) {
+		memcpy(cmd->data_service_uuid + off, service_uuid,
+							service_uuid_len);
+		off += service_uuid_len;
+	}
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+				sizeof(*cmd) + off, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t multi_adv_disable(int client_if)
+{
+	struct hal_cmd_gatt_client_disable_multi_adv_inst cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_cfg_storage(int client_if, int batch_scan_full_max,
+						int batch_scan_trunc_max,
+						int batch_scan_notify_threshold)
+{
+	struct hal_cmd_gatt_client_configure_batchscan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.full_max = batch_scan_full_max;
+	cmd.trunc_max = batch_scan_trunc_max;
+	cmd.notify_threshold = batch_scan_notify_threshold;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_enb_batch_scan(int client_if, int scan_mode,
+						int scan_interval,
+						int scan_window, int addr_type,
+						int discard_rule)
+{
+	struct hal_cmd_gatt_client_enable_batchscan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.mode = scan_mode;
+	cmd.interval = scan_interval;
+	cmd.window = scan_window;
+	cmd.address_type = addr_type;
+	cmd.discard_rule = discard_rule;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_dis_batch_scan(int client_if)
+{
+	struct hal_cmd_gatt_client_disable_batchscan cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t batchscan_read_reports(int client_if, int scan_mode)
+{
+	struct hal_cmd_gatt_client_read_batchscan_reports cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.client_if = client_if;
+	cmd.scan_mode = scan_mode;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+#endif
+
+/* Server API */
+
+static bt_status_t register_server(bt_uuid_t *uuid)
+{
+	struct hal_cmd_gatt_server_register cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t unregister_server(int server_if)
+{
+	struct hal_cmd_gatt_server_unregister cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t server_connect_real(int server_if,
+						const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	struct hal_cmd_gatt_server_connect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.is_direct = is_direct;
+	cmd.transport = transport;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+						bool is_direct, int transport)
+{
+	return server_connect_real(server_if, bd_addr, is_direct, transport);
+}
+#else
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+								bool is_direct)
+{
+	return server_connect_real(server_if, bd_addr, is_direct,
+							BT_TRANSPORT_UNKNOWN);
+}
+#endif
+
+static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr,
+								int conn_id)
+{
+	struct hal_cmd_gatt_server_disconnect cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.conn_id = conn_id;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id,
+								int num_handles)
+{
+	struct hal_cmd_gatt_server_add_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.num_handles = num_handles;
+
+	srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_included_service(int server_if, int service_handle,
+						int included_handle)
+{
+	struct hal_cmd_gatt_server_add_inc_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.included_handle = included_handle;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_INC_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_characteristic(int server_if, int service_handle,
+						bt_uuid_t *uuid, int properties,
+						int permissions)
+{
+	struct hal_cmd_gatt_server_add_characteristic cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.properties = properties;
+	cmd.permissions = permissions;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t add_descriptor(int server_if, int service_handle,
+					bt_uuid_t *uuid, int permissions)
+{
+	struct hal_cmd_gatt_server_add_descriptor cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.permissions = permissions;
+
+	memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_DESCRIPTOR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_service_real(int server_if, int service_handle,
+								int transport)
+{
+	struct hal_cmd_gatt_server_start_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+	cmd.transport = transport;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_START_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t start_service(int server_if, int service_handle,
+								int transport)
+{
+	return start_service_real(server_if, service_handle, transport);
+}
+#else
+static bt_status_t start_service(int server_if, int service_handle,
+								int transport)
+{
+	int transport_mask = 0;
+
+	/* Android 5 changes transport enum to bit mask. */
+	switch (transport) {
+	case 0:
+		transport_mask = GATT_SERVER_TRANSPORT_LE_BIT;
+		break;
+	case 1:
+		transport_mask = GATT_SERVER_TRANSPORT_BREDR_BIT;
+		break;
+	case 2:
+		transport_mask = GATT_SERVER_TRANSPORT_LE_BIT |
+						GATT_SERVER_TRANSPORT_BREDR_BIT;
+		break;
+	}
+
+	return start_service_real(server_if, service_handle, transport_mask);
+}
+#endif
+
+static bt_status_t stop_service(int server_if, int service_handle)
+{
+	struct hal_cmd_gatt_server_stop_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t delete_service(int server_if, int service_handle)
+{
+	struct hal_cmd_gatt_server_delete_service cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.server_if = server_if;
+	cmd.service_handle = service_handle;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_DELETE_SERVICE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t send_indication(int server_if, int attribute_handle,
+					int conn_id, int len, int confirm,
+					char *p_value)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + len;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->server_if = server_if;
+	cmd->attribute_handle = attribute_handle;
+	cmd->conn_id = conn_id;
+	cmd->len = len;
+	cmd->confirm = confirm;
+
+	memcpy(cmd->value, p_value, len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_SEND_INDICATION,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t send_response(int conn_id, int trans_id, int status,
+						btgatt_response_t *response)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_gatt_server_send_response *cmd = (void *) buf;
+	size_t cmd_len = sizeof(*cmd) + sizeof(*response);
+
+	memset(buf, 0 , IPC_MTU);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->conn_id = conn_id;
+	cmd->trans_id = trans_id;
+	cmd->status = status;
+	cmd->handle = response->attr_value.handle;
+	cmd->offset = response->attr_value.offset;
+	cmd->auth_req = response->attr_value.auth_req;
+	cmd->len = response->attr_value.len;
+
+	memcpy(cmd->data, response->attr_value.value, cmd->len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_SEND_RESPONSE,
+					cmd_len, cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(const btgatt_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_GATT;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_GATT;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+
+	cbs = NULL;
+}
+
+static btgatt_client_interface_t client_iface = {
+	.register_client = register_client,
+	.unregister_client = unregister_client,
+	.scan = scan,
+	.connect = connect,
+	.disconnect = disconnect,
+	.listen = listen,
+	.refresh = refresh,
+	.search_service = search_service,
+	.get_included_service = get_included_service,
+	.get_characteristic = get_characteristic,
+	.get_descriptor = get_descriptor,
+	.read_characteristic = read_characteristic,
+	.write_characteristic = write_characteristic,
+	.read_descriptor = read_descriptor,
+	.write_descriptor = write_descriptor,
+	.execute_write = execute_write,
+	.register_for_notification = register_for_notification,
+	.deregister_for_notification = deregister_for_notification,
+	.read_remote_rssi = read_remote_rssi,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.scan_filter_param_setup = scan_filter_param_setup,
+	.scan_filter_add_remove = scan_filter_add_remove,
+	.scan_filter_clear = scan_filter_clear,
+	.scan_filter_enable = scan_filter_enable,
+#endif
+	.get_device_type = get_device_type,
+	.set_adv_data = set_adv_data,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.configure_mtu = configure_mtu,
+	.conn_parameter_update = conn_parameter_update,
+	.set_scan_parameters = set_scan_parameters,
+	.multi_adv_enable = multi_adv_enable,
+	.multi_adv_update = multi_adv_update,
+	.multi_adv_set_inst_data = multi_adv_set_inst_data,
+	.multi_adv_disable = multi_adv_disable,
+	.batchscan_cfg_storage = batchscan_cfg_storage,
+	.batchscan_enb_batch_scan = batchscan_enb_batch_scan,
+	.batchscan_dis_batch_scan = batchscan_dis_batch_scan,
+	.batchscan_read_reports = batchscan_read_reports,
+#endif
+	.test_command = test_command,
+};
+
+static btgatt_server_interface_t server_iface = {
+	.register_server = register_server,
+	.unregister_server = unregister_server,
+	.connect = server_connect,
+	.disconnect = server_disconnect,
+	.add_service = add_service,
+	.add_included_service = add_included_service,
+	.add_characteristic = add_characteristic,
+	.add_descriptor = add_descriptor,
+	.start_service = start_service,
+	.stop_service = stop_service,
+	.delete_service = delete_service,
+	.send_indication = send_indication,
+	.send_response = send_response,
+};
+
+static btgatt_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.cleanup = cleanup,
+	.client = &client_iface,
+	.server = &server_iface,
+};
+
+btgatt_interface_t *bt_get_gatt_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-handsfree-client.c b/bluez/repo/android/hal-handsfree-client.c
new file mode 100644
index 0000000..93b5746
--- /dev/null
+++ b/bluez/repo/android/hal-handsfree-client.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthf_client_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state, ev->peer_feat,
+						ev->chld_feat,
+						(bt_bdaddr_t *) ev->bdaddr);
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_vr_state *ev = buf;
+
+	if (cbs->vr_cmd_cb)
+		cbs->vr_cmd_cb(ev->state);
+}
+
+static void handle_network_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_net_state *ev = buf;
+
+	if (cbs->network_state_cb)
+		cbs->network_state_cb(ev->state);
+}
+
+static void handle_network_roaming(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_net_roaming_type *ev = buf;
+
+	if (cbs->network_roaming_cb)
+		cbs->network_roaming_cb(ev->state);
+}
+
+static void handle_network_signal(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_net_signal_strength *ev = buf;
+
+	if (cbs->network_signal_cb)
+		cbs->network_signal_cb(ev->signal_strength);
+}
+
+static void handle_battery_level(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_battery_level *ev = buf;
+
+	if (cbs->battery_level_cb)
+		cbs->battery_level_cb(ev->battery_level);
+}
+
+static void handle_operator_name(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_operator_name *ev = buf;
+	uint16_t name_len = ev->name_len;
+	char *name = NULL;
+
+	if (len != sizeof(*ev) + name_len ||
+		(name_len != 0 && ev->name[name_len - 1] != '\0')) {
+		error("invalid operator name, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (name_len)
+		name = (char *) ev->name;
+
+	if (cbs->current_operator_cb)
+		cbs->current_operator_cb(name);
+}
+
+static void handle_call(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_indicator *ev = buf;
+
+	if (cbs->call_cb)
+		cbs->call_cb(ev->call);
+}
+
+static void handle_call_setup(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_setup_indicator *ev = buf;
+
+	if (cbs->callsetup_cb)
+		cbs->callsetup_cb(ev->call_setup);
+}
+
+static void handle_call_held(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_held_indicator *ev = buf;
+
+	if (cbs->callheld_cb)
+		cbs->callheld_cb(ev->call_held);
+}
+
+static void handle_response_and_hold(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_response_and_hold_status *ev = buf;
+
+	if (cbs->resp_and_hold_cb)
+		cbs->resp_and_hold_cb(ev->status);
+}
+
+static void handle_clip(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_calling_line_ident *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid  clip, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->clip_cb)
+		cbs->clip_cb(number);
+}
+
+static void handle_call_waiting(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_call_waiting *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid call waiting, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->call_waiting_cb)
+		cbs->call_waiting_cb(number);
+}
+
+static void handle_current_calls(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_current_call *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid current calls, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->current_calls_cb)
+		cbs->current_calls_cb(ev->index, ev->direction, ev->call_state,
+							ev->multiparty, number);
+}
+
+static void handle_volume_change(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_volume_changed *ev = buf;
+
+	if (cbs->volume_change_cb)
+		cbs->volume_change_cb(ev->type, ev->volume);
+}
+
+static void handle_command_cmp(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_command_complete *ev = buf;
+
+	if (cbs->cmd_complete_cb)
+		cbs->cmd_complete_cb(ev->type, ev->cme);
+}
+
+static void handle_subscriber_info(void *buf, uint16_t len, int fd)
+{
+	const struct hal_ev_hf_client_subscriber_service_info *ev = buf;
+	uint16_t name_len = ev->name_len;
+	char *name = NULL;
+
+	if (len != sizeof(*ev) + name_len ||
+		(name_len != 0 && ev->name[name_len - 1] != '\0')) {
+		error("invalid sunscriber info, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (name_len)
+		name = (char *) ev->name;
+
+	if (cbs->subscriber_info_cb)
+		cbs->subscriber_info_cb(name, ev->type);
+}
+
+static void handle_in_band_ringtone(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hf_client_inband_settings *ev = buf;
+
+	if (cbs->in_band_ring_tone_cb)
+		cbs->in_band_ring_tone_cb(ev->state);
+}
+
+static void handle_last_voice_tag_number(void *buf, uint16_t len, int fd)
+{
+	const struct hal_ev_hf_client_last_void_call_tag_num *ev = buf;
+	char *number = NULL;
+	uint16_t num_len = ev->number_len;
+
+	if (len != sizeof(*ev) + num_len ||
+		(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid voice tag, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (num_len)
+		number = (char *) ev->number;
+
+	if (cbs->last_voice_tag_number_callback)
+		cbs->last_voice_tag_number_callback(number);
+}
+
+static void handle_ring_indication(void *buf, uint16_t len, int fd)
+{
+	if (cbs->ring_indication_cb)
+		cbs->ring_indication_cb();
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HF_CLIENT_CONN_STATE */
+	{ handle_conn_state, false,
+				sizeof(struct hal_ev_hf_client_conn_state) },
+	/* HAL_EV_HF_CLIENT_AUDIO_STATE */
+	{ handle_audio_state, false,
+				sizeof(struct hal_ev_hf_client_audio_state) },
+	/* HAL_EV_HF_CLIENT_VR_STATE */
+	{ handle_vr_state, false, sizeof(struct hal_ev_hf_client_vr_state) },
+	/*HAL_EV_HF_CLIENT_NET_STATE */
+	{ handle_network_state, false,
+				sizeof(struct hal_ev_hf_client_net_state)},
+	/*HAL_EV_HF_CLIENT_NET_ROAMING_TYPE */
+	{ handle_network_roaming, false,
+			sizeof(struct hal_ev_hf_client_net_roaming_type) },
+	/* HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH */
+	{ handle_network_signal, false,
+			sizeof(struct hal_ev_hf_client_net_signal_strength) },
+	/* HAL_EV_HF_CLIENT_BATTERY_LEVEL */
+	{ handle_battery_level, false,
+			sizeof(struct hal_ev_hf_client_battery_level) },
+	/* HAL_EV_HF_CLIENT_OPERATOR_NAME */
+	{ handle_operator_name, true,
+			sizeof(struct hal_ev_hf_client_operator_name) },
+	/* HAL_EV_HF_CLIENT_CALL_INDICATOR */
+	{ handle_call, false,
+			sizeof(struct hal_ev_hf_client_call_indicator) },
+	/* HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR */
+	{ handle_call_setup, false,
+		sizeof(struct hal_ev_hf_client_call_setup_indicator) },
+	/* HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR */
+	{ handle_call_held, false,
+			sizeof(struct hal_ev_hf_client_call_held_indicator) },
+	/* HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS */
+	{ handle_response_and_hold, false,
+		sizeof(struct hal_ev_hf_client_response_and_hold_status) },
+	/* HAL_EV_HF_CLIENT_CALLING_LINE_IDENT */
+	{ handle_clip, true,
+			sizeof(struct hal_ev_hf_client_calling_line_ident) },
+	/* HAL_EV_HF_CLIENT_CALL_WAITING */
+	{ handle_call_waiting, true,
+			sizeof(struct hal_ev_hf_client_call_waiting) },
+	/* HAL_EV_HF_CLIENT_CURRENT_CALL */
+	{ handle_current_calls, true,
+			sizeof(struct hal_ev_hf_client_current_call) },
+	/* HAL_EV_CLIENT_VOLUME_CHANGED */
+	{ handle_volume_change, false,
+			sizeof(struct hal_ev_hf_client_volume_changed) },
+	/* HAL_EV_CLIENT_COMMAND_COMPLETE */
+	{ handle_command_cmp, false,
+			sizeof(struct hal_ev_hf_client_command_complete) },
+	/* HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO */
+	{ handle_subscriber_info, true,
+		sizeof(struct hal_ev_hf_client_subscriber_service_info) },
+	/* HAL_EV_CLIENT_INBAND_SETTINGS */
+	{ handle_in_band_ringtone, false,
+		sizeof(struct hal_ev_hf_client_inband_settings) },
+	/* HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM */
+	{ handle_last_voice_tag_number, true,
+		sizeof(struct hal_ev_hf_client_last_void_call_tag_num) },
+	/* HAL_EV_CLIENT_RING_INDICATION */
+	{ handle_ring_indication, false, 0 },
+};
+
+static bt_status_t init(bthf_client_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HANDSFREE_CLIENT, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	}
+
+	return ret;
+}
+
+static bt_status_t hf_client_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_CONNECT, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DISCONNECT, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_connect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_CONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hf_client_disconnect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_START_VR, 0, NULL, NULL, NULL,
+				NULL);
+}
+
+static bt_status_t stop_voice_recognition(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_STOP_VR, 0, NULL, NULL, NULL,
+				NULL);
+}
+
+static bt_status_t volume_control(bthf_client_volume_type_t type,
+								int volume)
+{
+	struct hal_cmd_hf_client_volume_control cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.type = type;
+	cmd.volume = volume;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_VOLUME_CONTROL, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t dial(const char *number)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_hf_client_dial *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (number) {
+		cmd->number_len = strlen(number) + 1;
+		memcpy(cmd->number, number, cmd->number_len);
+	} else {
+		cmd->number_len = 0;
+	}
+
+	len = sizeof(*cmd) + cmd->number_len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DIAL, len, cmd, NULL, NULL,
+				NULL);
+}
+
+static bt_status_t dial_memory(int location)
+{
+	struct hal_cmd_hf_client_dial_memory cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.location = location;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_DIAL_MEMORY,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t call_action(bthf_client_call_action_t action, int index)
+{
+	struct hal_cmd_hf_client_call_action cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.action = action;
+	cmd.index = index;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_CALL_ACTION, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t query_current_calls(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS, 0, NULL,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t query_operator_name(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME, 0, NULL,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t retrieve_subsr_info(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO, 0, NULL,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t send_dtmf(char tone)
+{
+	struct hal_cmd_hf_client_send_dtmf cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.tone = tone;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_SEND_DTMF, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t request_last_voice_tag_number(void)
+{
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+					0, NULL, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+	cbs = NULL;
+}
+
+static bthf_client_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = hf_client_connect,
+	.disconnect = disconnect,
+	.connect_audio = connect_audio,
+	.disconnect_audio = disconnect_audio,
+	.start_voice_recognition = start_voice_recognition,
+	.stop_voice_recognition = stop_voice_recognition,
+	.volume_control = volume_control,
+	.dial = dial,
+	.dial_memory = dial_memory,
+	.handle_call_action = call_action,
+	.query_current_calls = query_current_calls,
+	.query_current_operator_name = query_operator_name,
+	.retrieve_subscriber_info = retrieve_subsr_info,
+	.send_dtmf = send_dtmf,
+	.request_last_voice_tag_number = request_last_voice_tag_number,
+	.cleanup = cleanup
+};
+
+bthf_client_interface_t *bt_get_hf_client_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-handsfree.c b/bluez/repo/android/hal-handsfree.c
new file mode 100644
index 0000000..3847901
--- /dev/null
+++ b/bluez/repo/android/hal-handsfree.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const bthf_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state,
+						(bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_audio_state *ev = buf;
+
+	if (cbs->audio_state_cb)
+		cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_vr_state *ev = buf;
+
+	if (cbs->vr_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->vr_cmd_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->vr_cmd_cb(ev->state);
+#endif
+}
+
+static void handle_answer(void *buf, uint16_t len, int fd)
+{
+	if (cbs->answer_call_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_answer *ev = buf;
+
+		cbs->answer_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->answer_call_cmd_cb();
+#endif
+	}
+}
+
+static void handle_hangup(void *buf, uint16_t len, int fd)
+{
+	if (cbs->hangup_call_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_hangup *ev = buf;
+
+		cbs->hangup_call_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->hangup_call_cmd_cb();
+#endif
+	}
+}
+
+static void handle_volume(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_volume *ev = buf;
+
+	if (cbs->volume_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->volume_cmd_cb(ev->type, ev->volume,
+						(bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->volume_cmd_cb(ev->type, ev->volume);
+#endif
+}
+
+static void handle_dial(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_dial *ev = buf;
+	uint16_t num_len = ev->number_len;
+	char *number = NULL;
+
+	if (len != sizeof(*ev) + num_len ||
+			(num_len != 0 && ev->number[num_len - 1] != '\0')) {
+		error("invalid dial event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (!cbs->dial_call_cmd_cb)
+		return;
+
+	if (ev->number_len)
+		number = (char *) ev->number;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	cbs->dial_call_cmd_cb(number, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+	cbs->dial_call_cmd_cb(number);
+#endif
+}
+
+static void handle_dtmf(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_dtmf *ev = buf;
+
+	if (cbs->dtmf_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->dtmf_cmd_cb(ev->tone, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->dtmf_cmd_cb(ev->tone);
+#endif
+}
+
+static void handle_nrec(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_nrec *ev = buf;
+
+	if (cbs->nrec_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->nrec_cmd_cb(ev->nrec, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->nrec_cmd_cb(ev->nrec);
+#endif
+}
+
+static void handle_wbs(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_handsfree_wbs *ev = buf;
+
+	if (cbs->wbs_cb)
+		cbs->wbs_cb(ev->wbs, (bt_bdaddr_t *) (ev->bdaddr));
+#endif
+}
+
+static void handle_chld(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_chld *ev = buf;
+
+	if (cbs->chld_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->chld_cmd_cb(ev->chld, (bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->chld_cmd_cb(ev->chld);
+#endif
+}
+
+static void handle_cnum(void *buf, uint16_t len, int fd)
+{
+	if (cbs->cnum_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_cnum *ev = buf;
+
+		cbs->cnum_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->cnum_cmd_cb(NULL);
+#endif
+	}
+}
+
+static void handle_cind(void *buf, uint16_t len, int fd)
+{
+	if (cbs->cind_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_cind *ev = buf;
+
+		cbs->cind_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->cind_cmd_cb();
+#endif
+	}
+}
+
+static void handle_cops(void *buf, uint16_t len, int fd)
+{
+	if (cbs->cops_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_cops *ev = buf;
+
+		cbs->cops_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->cops_cmd_cb();
+#endif
+	}
+}
+
+static void handle_clcc(void *buf, uint16_t len, int fd)
+{
+	if (cbs->clcc_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_clcc *ev = buf;
+
+		cbs->clcc_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->clcc_cmd_cb();
+#endif
+	}
+}
+
+static void handle_unknown_at(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_handsfree_unknown_at *ev = buf;
+
+	if (len != sizeof(*ev) + ev->len ||
+			(ev->len != 0 && ev->buf[ev->len - 1] != '\0')) {
+		error("invalid unknown command event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (cbs->unknown_at_cmd_cb)
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		cbs->unknown_at_cmd_cb((char *) ev->buf,
+						(bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->unknown_at_cmd_cb((char *) ev->buf);
+#endif
+}
+
+static void handle_hsp_key_press(void *buf, uint16_t len, int fd)
+{
+	if (cbs->key_pressed_cmd_cb) {
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+		struct hal_ev_handsfree_hsp_key_press *ev = buf;
+
+		cbs->key_pressed_cmd_cb((bt_bdaddr_t *) (ev->bdaddr));
+#else
+		cbs->key_pressed_cmd_cb();
+#endif
+	}
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HANDSFREE_CONN_STATE */
+	{ handle_conn_state, false,
+				sizeof(struct hal_ev_handsfree_conn_state) },
+	/* HAL_EV_HANDSFREE_AUDIO_STATE */
+	{ handle_audio_state, false,
+				sizeof(struct hal_ev_handsfree_audio_state) },
+	/* HAL_EV_HANDSFREE_VR */
+	{ handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) },
+	/* HAL_EV_HANDSFREE_ANSWER */
+	{ handle_answer, false, sizeof(struct hal_ev_handsfree_answer) },
+	/* HAL_EV_HANDSFREE_HANGUP */
+	{ handle_hangup, false, sizeof(struct hal_ev_handsfree_hangup) },
+	/* HAL_EV_HANDSFREE_VOLUME */
+	{ handle_volume, false, sizeof(struct hal_ev_handsfree_volume) },
+	/* HAL_EV_HANDSFREE_DIAL */
+	{ handle_dial, true, sizeof(struct hal_ev_handsfree_dial) },
+	/* HAL_EV_HANDSFREE_DTMF */
+	{ handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) },
+	/* HAL_EV_HANDSFREE_NREC */
+	{ handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) },
+	/* HAL_EV_HANDSFREE_CHLD */
+	{ handle_chld, false, sizeof(struct hal_ev_handsfree_chld) },
+	/* HAL_EV_HANDSFREE_CNUM */
+	{ handle_cnum, false, sizeof(struct hal_ev_handsfree_cnum) },
+	/* HAL_EV_HANDSFREE_CIND */
+	{ handle_cind, false, sizeof(struct hal_ev_handsfree_cind) },
+	/* HAL_EV_HANDSFREE_COPS */
+	{ handle_cops, false, sizeof(struct hal_ev_handsfree_cops) },
+	/* HAL_EV_HANDSFREE_CLCC */
+	{ handle_clcc, false, sizeof(struct hal_ev_handsfree_clcc) },
+	/* HAL_EV_HANDSFREE_UNKNOWN_AT */
+	{ handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) },
+	/* HAL_EV_HANDSFREE_HSP_KEY_PRESS */
+	{ handle_hsp_key_press, false,
+				sizeof(struct hal_ev_handsfree_hsp_key_press) },
+	/* HAL_EV_HANDSFREE_WBS */
+	{ handle_wbs, false, sizeof(struct hal_ev_handsfree_wbs) },
+};
+
+static uint8_t get_mode(void)
+{
+	char value[PROPERTY_VALUE_MAX];
+
+	if (get_config("handsfree", value, NULL) > 0) {
+		if (!strcasecmp(value, "hfp"))
+			return HAL_MODE_HANDSFREE_HFP;
+
+		if (!strcasecmp(value, "hfp_wbs"))
+			return HAL_MODE_HANDSFREE_HFP_WBS;
+	}
+
+	return HAL_MODE_HANDSFREE_HSP_ONLY;
+}
+
+static bt_status_t init_real(bthf_callbacks_t *callbacks, int max_hf_clients)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+	cmd.mode = get_mode();
+	cmd.max_clients = max_hf_clients;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+	}
+
+	return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t init(bthf_callbacks_t *callbacks, int max_hf_clients)
+{
+	return init_real(callbacks, max_hf_clients);
+}
+#else
+static bt_status_t init(bthf_callbacks_t *callbacks)
+{
+	return init_real(callbacks, 1);
+}
+#endif
+
+static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd,
+				NULL, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_connect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_disconnect_audio cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition_real(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_start_vr cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t start_voice_recognition(bt_bdaddr_t *bd_addr)
+{
+	return start_voice_recognition_real(bd_addr);
+}
+#else
+static bt_status_t start_voice_recognition(void)
+{
+	return start_voice_recognition_real(NULL);
+}
+#endif
+
+static bt_status_t stop_voice_recognition_real(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_stop_vr cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t stop_voice_recognition(bt_bdaddr_t *bd_addr)
+{
+	return stop_voice_recognition_real(bd_addr);
+}
+#else
+static bt_status_t stop_voice_recognition(void)
+{
+	return stop_voice_recognition_real(NULL);
+}
+#endif
+
+static bt_status_t volume_control_real(bthf_volume_type_t type, int volume,
+							bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_volume_control cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.type = type;
+	cmd.volume = volume;
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t volume_control(bthf_volume_type_t type, int volume,
+							bt_bdaddr_t *bd_addr)
+{
+	return volume_control_real(type, volume, bd_addr);
+}
+#else
+static bt_status_t volume_control(bthf_volume_type_t type, int volume)
+{
+	return volume_control_real(type, volume, NULL);
+}
+#endif
+
+static bt_status_t device_status_notification(bthf_network_state_t state,
+						bthf_service_type_t type,
+						int signal, int battery)
+{
+	struct hal_cmd_handsfree_device_status_notif cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.state = state;
+	cmd.type = type;
+	cmd.signal = signal;
+	cmd.battery = battery;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t cops_response_real(const char *cops, bt_bdaddr_t *bd_addr)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_cops_response *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!cops)
+		return BT_STATUS_PARM_INVALID;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	if (bd_addr)
+		memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+	/* Size of cmd.buf */
+	cmd->len = strlen(cops) + 1;
+	memcpy(cmd->buf, cops, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+						HAL_OP_HANDSFREE_COPS_RESPONSE,
+						len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t cops_response(const char *cops, bt_bdaddr_t *bd_addr)
+{
+	return cops_response_real(cops, bd_addr);
+}
+#else
+static bt_status_t cops_response(const char *cops)
+{
+	return cops_response_real(cops, NULL);
+}
+#endif
+
+static bt_status_t cind_response_real(int svc, int num_active, int num_held,
+					bthf_call_state_t state, int signal,
+					int roam, int batt_chg,
+					bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_cind_response cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	cmd.svc = svc;
+	cmd.num_active = num_active;
+	cmd.num_held = num_held;
+	cmd.state = state;
+	cmd.signal = signal;
+	cmd.roam = roam;
+	cmd.batt_chg = batt_chg;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CIND_RESPONSE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+					bthf_call_state_t state, int signal,
+					int roam, int batt_chg,
+					bt_bdaddr_t *bd_addr)
+{
+	return cind_response_real(svc, num_active, num_held, state, signal,
+						roam, batt_chg, bd_addr);
+}
+#else
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+					bthf_call_state_t state, int signal,
+					int roam, int batt_chg)
+{
+	return cind_response_real(svc, num_active, num_held, state, signal,
+						roam, batt_chg, NULL);
+}
+#endif
+
+static bt_status_t formatted_at_response_real(const char *rsp,
+							bt_bdaddr_t *bd_addr)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!rsp)
+		return BT_STATUS_PARM_INVALID;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	if (bd_addr)
+		memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+	cmd->len = strlen(rsp) + 1;
+	memcpy(cmd->buf, rsp, cmd->len);
+
+	len = sizeof(*cmd) + cmd->len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t formatted_at_response(const char *rsp, bt_bdaddr_t *bd_addr)
+{
+	return formatted_at_response_real(rsp, bd_addr);
+}
+#else
+static bt_status_t formatted_at_response(const char *rsp)
+{
+	return formatted_at_response_real(rsp, NULL);
+}
+#endif
+
+static bt_status_t at_response_real(bthf_at_response_t response, int error,
+							bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_handsfree_at_response cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (bd_addr)
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.response = response;
+	cmd.error = error;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_AT_RESPONSE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t at_response(bthf_at_response_t response, int error,
+							bt_bdaddr_t *bd_addr)
+{
+	return at_response_real(response, error, bd_addr);
+}
+#else
+static bt_status_t at_response(bthf_at_response_t response, int error)
+{
+	return at_response_real(response, error, NULL);
+}
+#endif
+
+static bt_status_t clcc_response_real(int index, bthf_call_direction_t dir,
+					bthf_call_state_t state,
+					bthf_call_mode_t mode,
+					bthf_call_mpty_type_t mpty,
+					const char *number,
+					bthf_call_addrtype_t type,
+					bt_bdaddr_t *bd_addr)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	if (bd_addr)
+		memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+
+	cmd->index = index;
+	cmd->dir = dir;
+	cmd->state = state;
+	cmd->mode = mode;
+	cmd->mpty = mpty;
+	cmd->type = type;
+
+	if (number) {
+		cmd->number_len = strlen(number) + 1;
+		memcpy(cmd->number, number, cmd->number_len);
+	} else {
+		cmd->number_len = 0;
+	}
+
+	len = sizeof(*cmd) + cmd->number_len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+						HAL_OP_HANDSFREE_CLCC_RESPONSE,
+						len, cmd, NULL, NULL, NULL);
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+					bthf_call_state_t state,
+					bthf_call_mode_t mode,
+					bthf_call_mpty_type_t mpty,
+					const char *number,
+					bthf_call_addrtype_t type,
+					bt_bdaddr_t *bd_addr)
+{
+	return clcc_response_real(index, dir, state, mode, mpty, number, type,
+								bd_addr);
+}
+#else
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+					bthf_call_state_t state,
+					bthf_call_mode_t mode,
+					bthf_call_mpty_type_t mpty,
+					const char *number,
+					bthf_call_addrtype_t type)
+{
+	return clcc_response_real(index, dir, state, mode, mpty, number, type,
+									NULL);
+}
+#endif
+
+static bt_status_t phone_state_change(int num_active, int num_held,
+					bthf_call_state_t state,
+					const char *number,
+					bthf_call_addrtype_t type)
+{
+	char buf[IPC_MTU];
+	struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf;
+	size_t len;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd->num_active = num_active;
+	cmd->num_held = num_held;
+	cmd->state = state;
+	cmd->type = type;
+
+	if (number) {
+		cmd->number_len = strlen(number) + 1;
+		memcpy(cmd->number, number, cmd->number_len);
+	} else {
+		cmd->number_len = 0;
+	}
+
+	len = sizeof(*cmd) + cmd->number_len;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_PHONE_STATE_CHANGE,
+					len, cmd, NULL, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+
+	cbs = NULL;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static bt_status_t configure_wbs(bt_bdaddr_t *bd_addr, bthf_wbs_config_t config)
+{
+	struct hal_cmd_handsfree_configure_wbs cmd;
+
+	DBG("%u", config);
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.config = config;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CONFIGURE_WBS,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+#endif
+
+static bthf_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.connect = handsfree_connect,
+	.disconnect = disconnect,
+	.connect_audio = connect_audio,
+	.disconnect_audio = disconnect_audio,
+	.start_voice_recognition = start_voice_recognition,
+	.stop_voice_recognition = stop_voice_recognition,
+	.volume_control = volume_control,
+	.device_status_notification = device_status_notification,
+	.cops_response = cops_response,
+	.cind_response = cind_response,
+	.formatted_at_response = formatted_at_response,
+	.at_response = at_response,
+	.clcc_response = clcc_response,
+	.phone_state_change = phone_state_change,
+	.cleanup = cleanup,
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	.configure_wbs = configure_wbs,
+#endif
+};
+
+bthf_interface_t *bt_get_handsfree_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-health.c b/bluez/repo/android/hal-health.c
new file mode 100644
index 0000000..5d5b111
--- /dev/null
+++ b/bluez/repo/android/hal-health.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthl_callbacks_t *cbacks = NULL;
+
+static bool interface_ready(void)
+{
+	return cbacks != NULL;
+}
+
+static void handle_app_registration_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_health_app_reg_state *ev = buf;
+
+	if (cbacks->app_reg_state_cb)
+		cbacks->app_reg_state_cb(ev->id, ev->state);
+}
+
+static void handle_channel_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_health_channel_state *ev = buf;
+	int flags;
+
+	if (fd < 0)
+		goto end;
+
+	flags = fcntl(fd, F_GETFL, 0);
+	if (flags < 0) {
+		error("health: fcntl GETFL error: %s", strerror(errno));
+		return;
+	}
+
+	/* Clean O_NONBLOCK fd flag as Android Java layer expects */
+	if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
+		error("health: fcntl SETFL error: %s", strerror(errno));
+		return;
+	}
+
+end:
+	if (cbacks->channel_state_cb)
+		cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr,
+						ev->mdep_index, ev->channel_id,
+						ev->channel_state, fd);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HEALTH_APP_REG_STATE */
+	{ handle_app_registration_state, false,
+				sizeof(struct hal_ev_health_app_reg_state) },
+	/* HAL_EV_HEALTH_CHANNEL_STATE */
+	{ handle_channel_state, false,
+				sizeof(struct hal_ev_health_channel_state) },
+};
+
+static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_cmd_health_reg_app *cmd = (void *) buf;
+	struct hal_rsp_health_reg_app rsp;
+	size_t rsp_len = sizeof(rsp);
+	bt_status_t status;
+	uint16_t off, len;
+	int i;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!reg || !app_id || !reg->application_name)
+		return BT_STATUS_PARM_INVALID;
+
+	*app_id = -1;
+	memset(buf, 0, IPC_MTU);
+
+	cmd->num_of_mdep = reg->number_of_mdeps;
+
+	off = 0;
+	cmd->app_name_off = off;
+	len = strlen(reg->application_name) + 1;
+	memcpy(cmd->data, reg->application_name, len);
+	off += len;
+
+	cmd->provider_name_off = off;
+	if (reg->provider_name) {
+		len = strlen(reg->provider_name) + 1;
+		memcpy(cmd->data + off, reg->provider_name, len);
+		off += len;
+	}
+
+	cmd->service_name_off = off;
+	if (reg->srv_name) {
+		len = strlen(reg->srv_name) + 1;
+		memcpy(cmd->data + off, reg->srv_name, len);
+		off += len;
+	}
+
+	cmd->service_descr_off = off;
+	if (reg->srv_desp) {
+		len = strlen(reg->srv_desp) + 1;
+		memcpy(cmd->data + off, reg->srv_desp, len);
+		off += len;
+	}
+
+	cmd->len = off;
+	status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+						sizeof(*cmd) + cmd->len, buf,
+						&rsp_len, &rsp, NULL);
+	if (status != BT_STATUS_SUCCESS)
+		return status;
+
+	for (i = 0; i < reg->number_of_mdeps; i++) {
+		struct hal_cmd_health_mdep *mdep = (void *) buf;
+
+		memset(buf, 0, IPC_MTU);
+		mdep->app_id = rsp.app_id;
+		mdep->role = reg->mdep_cfg[i].mdep_role;
+		mdep->data_type = reg->mdep_cfg[i].data_type;
+		mdep->channel_type = reg->mdep_cfg[i].channel_type;
+
+		if (reg->mdep_cfg[i].mdep_description) {
+			mdep->descr_len =
+				strlen(reg->mdep_cfg[i].mdep_description) + 1;
+			memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description,
+							mdep->descr_len);
+		}
+
+		status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+						sizeof(*mdep) + mdep->descr_len,
+						buf, NULL, NULL, NULL);
+
+		if (status != BT_STATUS_SUCCESS)
+			return status;
+	}
+
+	*app_id = rsp.app_id;
+
+	return status;
+}
+
+static bt_status_t unregister_application(int app_id)
+{
+	struct hal_cmd_health_unreg_app cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.app_id = app_id;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr,
+					int mdep_cfg_index, int *channel_id)
+{
+	struct hal_cmd_health_connect_channel cmd;
+	struct hal_rsp_health_connect_channel rsp;
+	size_t len = sizeof(rsp);
+	bt_status_t status;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr || !channel_id)
+		return BT_STATUS_PARM_INVALID;
+
+	*channel_id = -1;
+	cmd.app_id = app_id;
+	cmd.mdep_index = mdep_cfg_index;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH,
+					HAL_OP_HEALTH_CONNECT_CHANNEL,
+					sizeof(cmd), &cmd, &len, &rsp, NULL);
+
+	if (status == BT_STATUS_SUCCESS)
+		*channel_id = rsp.channel_id;
+
+	return status;
+}
+
+static bt_status_t destroy_channel(int channel_id)
+{
+	struct hal_cmd_health_destroy_channel cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.channel_id = channel_id;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(bthl_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	/* store reference to user callbacks */
+	cbacks = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HEALTH;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbacks = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HEALTH;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+
+	cbacks = NULL;
+}
+
+static bthl_interface_t health_if = {
+	.size = sizeof(health_if),
+	.init = init,
+	.register_application = register_application,
+	.unregister_application = unregister_application,
+	.connect_channel = connect_channel,
+	.destroy_channel = destroy_channel,
+	.cleanup = cleanup
+};
+
+bthl_interface_t *bt_get_health_interface(void)
+{
+	return &health_if;
+}
diff --git a/bluez/repo/android/hal-hidhost.c b/bluez/repo/android/hal-hidhost.c
new file mode 100644
index 0000000..1a60326
--- /dev/null
+++ b/bluez/repo/android/hal-hidhost.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthh_callbacks_t *cbacks;
+
+static bool interface_ready(void)
+{
+	return cbacks != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_conn_state *ev = buf;
+
+	if (cbacks->connection_state_cb)
+		cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr,
+								ev->state);
+}
+
+static void handle_info(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_info *ev = buf;
+	bthh_hid_info_t info;
+
+	info.attr_mask = ev->attr;
+	info.sub_class = ev->subclass;
+	info.app_id = ev->app_id;
+	info.vendor_id = ev->vendor;
+	info.product_id = ev->product;
+	info.version = ev->version;
+	info.ctry_code = ev->country;
+	info.dl_len = ev->descr_len;
+	memcpy(info.dsc_list, ev->descr, info.dl_len);
+
+	if (cbacks->hid_info_cb)
+		cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
+}
+
+static void handle_proto_mode(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_proto_mode *ev = buf;
+
+	if (cbacks->protocol_mode_cb)
+		cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr,
+							ev->status, ev->mode);
+}
+
+static void handle_idle_time(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_idle_time *ev = buf;
+
+	if (cbacks->idle_time_cb)
+		cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+								ev->idle_rate);
+}
+
+static void handle_get_report(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_get_report *ev = buf;
+
+	if (len != sizeof(*ev) + ev->len) {
+		error("invalid get report event, aborting");
+		exit(EXIT_FAILURE);
+	}
+
+	if (cbacks->get_report_cb)
+		cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+							ev->data, ev->len);
+}
+
+static void handle_virtual_unplug(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_hidhost_virtual_unplug *ev = buf;
+
+	if (cbacks->virtual_unplug_cb)
+		cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr,
+								ev->status);
+}
+
+static void handle_handshake(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	struct hal_ev_hidhost_handshake *ev = buf;
+
+	if (cbacks->handshake_cb)
+		cbacks->handshake_cb((bt_bdaddr_t *) ev->bdaddr, ev->status);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_HIDHOST_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) },
+	/* HAL_EV_HIDHOST_INFO */
+	{ handle_info, false, sizeof(struct hal_ev_hidhost_info) },
+	/* HAL_EV_HIDHOST_PROTO_MODE */
+	{ handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) },
+	/* HAL_EV_HIDHOST_IDLE_TIME */
+	{ handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) },
+	/* HAL_EV_HIDHOST_GET_REPORT */
+	{ handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) },
+	/* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+	{ handle_virtual_unplug, false,
+				sizeof(struct hal_ev_hidhost_virtual_unplug) },
+	{ handle_handshake, false, sizeof(struct hal_ev_hidhost_handshake) },
+};
+
+static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hidhost_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hidhost_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_hidhost_virtual_unplug cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
+{
+	struct hal_cmd_hidhost_set_info cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.attr = hid_info.attr_mask;
+	cmd.subclass = hid_info.sub_class;
+	cmd.app_id = hid_info.app_id;
+	cmd.vendor = hid_info.vendor_id;
+	cmd.product = hid_info.product_id;
+	cmd.country = hid_info.ctry_code;
+	cmd.descr_len = hid_info.dl_len;
+	memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_protocol(bt_bdaddr_t *bd_addr,
+					bthh_protocol_mode_t protocol_mode)
+{
+	struct hal_cmd_hidhost_get_protocol cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	/* type match IPC type */
+	cmd.mode = protocol_mode;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+				HAL_OP_HIDHOST_GET_PROTOCOL,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_protocol(bt_bdaddr_t *bd_addr,
+					bthh_protocol_mode_t protocol_mode)
+{
+	struct hal_cmd_hidhost_set_protocol cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	/* type match IPC type */
+	cmd.mode = protocol_mode;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
+				HAL_OP_HIDHOST_SET_PROTOCOL,
+				sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t get_report(bt_bdaddr_t *bd_addr,
+						bthh_report_type_t report_type,
+						uint8_t report_id,
+						int buffer_size)
+{
+	struct hal_cmd_hidhost_get_report cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.id = report_id;
+	cmd.buf_size = buffer_size;
+
+	/* type match IPC type */
+	cmd.type = report_type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t set_report(bt_bdaddr_t *bd_addr,
+						bthh_report_type_t report_type,
+						char *report)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_cmd_hidhost_set_report *cmd = (void *) buf;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr || !report)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+	cmd->len = strlen(report);
+	memcpy(cmd->data, report, cmd->len);
+
+	/* type match IPC type */
+	cmd->type = report_type;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+				sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
+}
+
+static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_cmd_hidhost_send_data *cmd = (void *) buf;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	if (!bd_addr || !data)
+		return BT_STATUS_PARM_INVALID;
+
+	memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
+	cmd->len = strlen(data);
+	memcpy(cmd->data, data, cmd->len);
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+			sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
+}
+
+static bt_status_t init(bthh_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	/* store reference to user callbacks */
+	cbacks = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbacks = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+	}
+
+	return ret;
+}
+
+static void cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+
+	cbacks = NULL;
+}
+
+static bthh_interface_t hidhost_if = {
+	.size = sizeof(hidhost_if),
+	.init = init,
+	.connect = hidhost_connect,
+	.disconnect = disconnect,
+	.virtual_unplug = virtual_unplug,
+	.set_info = set_info,
+	.get_protocol = get_protocol,
+	.set_protocol = set_protocol,
+	.get_report = get_report,
+	.set_report = set_report,
+	.send_data = send_data,
+	.cleanup = cleanup
+};
+
+bthh_interface_t *bt_get_hidhost_interface(void)
+{
+	return &hidhost_if;
+}
diff --git a/bluez/repo/android/hal-ipc-api.txt b/bluez/repo/android/hal-ipc-api.txt
new file mode 100644
index 0000000..e3b7798
--- /dev/null
+++ b/bluez/repo/android/hal-ipc-api.txt
@@ -0,0 +1,2737 @@
+Android HAL protocol for Bluetooth
+==================================
+
+The Android HAL daemon for Bluetooth functionality implements the Unix socket
+server protocol around /run/bluetooth/daemon (tentative location) or Linux
+abstract sockets (tentative name).
+
+The daemon is single threaded and uses a mainloop for scheduling and general
+operation.
+
+The protocol is SOCK_SEQPACKET based and follows a strict PDU specification
+with a generic header and initial registration exchange. The communication
+is driven from the HAL with command/response exchanges. The daemon will use
+notification to signal events. The protocol is single PDU exchanged based,
+meaning every command requires a response. Notification does not require
+any confirmation. Not handling this PDU exchange leads to a disconnection of
+the socket.
+
+Command/response and notification use separate sockets. First connected socket
+is used for command/response, second for notification.  All services are
+multi-plexed over same pair of sockets. Separation is done to ease
+implementation of simple HAL library with dedicated thread for handling
+notification.
+
+This strict protocol requirement is done to match C based callbacks and
+callout functions that are running in a thread inside the HAL and might
+block.
+
+	.--Android--.                             .--Android--.
+	|  daemon   |                             |  HAL      |
+	|           |          Command            |           |
+	|           | <-------------------------- |           |
+	|           |                             |           |
+	|           | --------------------------> |           |
+	|           |          Response           |           |
+	|           |                             |           |
+	|           |                             |           |
+	|           |        Notification         |           |
+	|           | --------------------------> |           |
+	|           |                             |           |
+	'-----------'                             '-----------'
+
+Every packet will follow the basic header to support simple multi-plexing
+over the same socket. It will also support a basic control channel with service
+id 0.
+
+	0              8              16             24            31
+	+--------------+--------------+--------------+--------------+
+	| Service ID   | Opcode       | Data Length                 |
+	+--------------+--------------+-----------------------------+
+	|                                                           |
+
+The unique service ID is assigned by this specification for each HAL.
+
+As general rule of thumb, the opcode for command matches the opcode for a
+response. Or the opcode 0x00 for an error is returned.
+
+Notification opcodes start from 0x81.
+
+Opcode 0x80 is reserved and shall not be used.
+
+All command/response opcodes have the least significant bit not set. And all
+notifications have the least significant bit set.
+
+The HAL modules only have the job to map the callback and event functions
+to the protocol. They do not need to do anything else. Below is an example
+of a sample transaction for the Bluetooth Core HAL and enabling of an
+adapter.
+
+	HAL                                Daemon
+	----------------------------------------------------
+
+	call enable()                  --> command 0x01
+	return enable()                <-- response 0x01
+
+	call adapter_state_changed()   <-- notification 0x81
+	return adapter_state_changed()
+
+When the Android hardware framework calls into the Bluetooth Core HAL
+and executes the enable() callback, the HAL module sends the enable
+command with opcode 0x01 to the daemon. As soon as the daemon responds,
+the callback will return with the appropriate result.
+
+After the daemon switched on the adapter, it will send a notification
+with opcode 0x81 to the HAL module.
+
+The Bluetooth Core HAL and Bluetooth Socket HAL are guaranteed to be
+available from the daemon. All other HAL modules are optional.
+
+When the Bluetooth Core HAL init() function is called, it should open
+the socket and register both "bluetooth" and "socket" service modules. It is
+required to register "socket" service at the same time since the HAL module
+does not have its own init() function.
+
+It is possible to send Configure command before registering any services to
+customize stack. This step is optional.
+
+When new profiles are initiated, the get_profile_interface() callback
+will load the profile and during init() of the profile, it should register the
+specific service.
+
+	Bluetooth main thread       Daemon
+	-------------------------------------------------------
+
+	init()                  --> open command socket
+	                        --> open notification socket
+	                        --> register module "bluetooth"
+	                        --> register module "socket"
+
+	get_profile_interface() --> return profile struct
+	                        --> continue on Handsfree thread
+
+
+	Handsfree thread            Daemon
+	--------------------------------------------------------
+
+	init()                  --> register module handsfree
+
+
+Error response is common for all services and has fixed structure:
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+		Valid status values: 0x01 = Fail
+		                     0x02 = Not ready
+		                     0x03 = No memory
+		                     0x04 = Busy
+		                     0x05 = Done (already completed)
+		                     0x06 = Unsupported
+		                     0x07 = Parameter invalid
+		                     0x08 = Unhandled
+		                     0x09 = Authentication failure
+		                     0x0a = Remote device down
+		                     0x0b = Authentication rejected
+
+
+Core Service (ID 0)
+===================
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Register module command/response
+
+		Command parameters: Service id (1 octet)
+		                    Mode (1 octet)
+		                    Max Clients (4 octets)
+		Response parameters: <none>
+
+		In case a command is sent for an undeclared service ID, it will
+		be rejected. Also there will be no notifications for undeclared
+		service ID.
+
+		Valid Mode values: 0x00 = Default Mode
+		                   0xXX = as defined by service
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Unregister module command/response
+
+		Command parameters: Service id (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Configuration
+
+		Command parameters: Num options (1 octet)
+		                    Option Type # (1 octet)
+		                    Option Length # (2 octets)
+		                    Option Value # (variable)
+
+		Response parameters: <none>
+
+		Valid configure option types: 0x00 = Vendor
+		                              0x01 = Model
+		                              0x02 = Name
+		                              0x03 = Serial Number
+		                              0x04 = System ID
+		                              0x05 = PnP ID
+		                              0x06 = Firmware Rev
+		                              0x07 = Hardware Rev
+
+		In case of an error, the error response will be returned.
+
+Bluetooth Core HAL (ID 1)
+=========================
+
+Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID)
+
+	Service modes: 0x00 = Enable BR/EDR/LE if supported (default)
+	               0x01 = Enable BR/EDR only
+	               0x02 = Enable LE only
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Enable command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disable command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Get Adapter Properties command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Get Adapter Property command/response
+
+		Command parameters: Property type (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Set Adapter Property command/response
+
+		Command parameters: Property type (1 octet)
+		                    Property length (2 octets)
+		                    Property value (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Get Remote Device Properties command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Get Remote Device Property command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Property type (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Set Remote Device Property command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Property type (1 octet)
+		                    Property length (2 octets)
+		                    Property value (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Get Remote Service Record command/response
+
+		Command parameters: Remote address (6 octets)
+		                    UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - Get Remote Services command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Start Discovery command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - Cancel Discovery command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - Create Bond command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Transport (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Remove Bond command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Cancel Bond command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - PIN Reply command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Accept (1 octet)
+		                    PIN length (1 octet)
+		                    PIN code (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x11 - SSP Reply command/response
+
+		Command parameters: Remote address (6 octets)
+		                    SSP variant (1 octet)
+		                    Accept (1 octet)
+		                    Passkey (4 octets)
+		Response parameters: <none>
+
+		Valid SSP variant values: 0x00 = Passkey Confirmation
+		                          0x01 = Passkey Entry
+		                          0x02 = Consent (for Just Works)
+		                          0x03 = Passkey Notification
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - DUT Mode Configure command/response
+
+		Command parameters: Enable (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - DUT Mode Send command/response
+
+		Command parameters: Opcode (2 octets)
+		                    Length (1 octet)
+		                    Data (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x14 - LE Test Mode command/response
+
+		Command parameters: Opcode (2 octets)
+		                    Length (1 octet)
+		                    Data (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Adapter State Changed notification
+
+		Notifications parameters: State (1 octet)
+
+		Valid state values: 0x00 = Off
+		                    0x01 = On
+
+	Opcode 0x82 - Adapter Properties Changed notification
+
+		Notification parameters: Status (1 octet)
+		                         Num properties (1 octet)
+		                         Type # (1 octet)
+		                         Length # (2 octets)
+		                         Value # (variable)
+		                         ...
+
+	Opcode 0x83 - Remote Device Properties notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         Num properties (1 octet)
+		                         Type # (1 octet)
+		                         Length # (2 octets)
+		                         Value # (variable)
+		                         ...
+
+	Opcode 0x84 - Device Found notification
+
+		Notification parameters: Num properties (1 octet)
+		                         Type # (1 octet)
+		                         Length # (2 octets)
+		                         Value # (variable)
+		                         ...
+
+	Opcode 0x85 - Discovery State Changed notification
+
+		Notifications parameters: State (1 octet)
+
+	Opcode 0x86 - PIN Request notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Remote name (249 octets)
+		                         Class of device (4 octets)
+
+	Opcode 0x87 - SSP Request notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Remote name (249 octets)
+		                         Class of device (4 octets)
+		                         Pairing variant (1 octet)
+		                         Passkey (4 octets)
+
+	Opcode 0x88 - Bond State Changed notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         Bond state (1 octet)
+
+		Valid bond state values: 0x00 = None
+		                         0x01 = Bonding
+		                         0x02 = Bonded
+
+	Opcode 0x89 - ACL State Changed notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         ACL state (1 octet)
+
+	Opcode 0x8a - DUT Mode Receive notification
+
+		Notification parameters: Opcode (2 octets)
+		                         Length  (1 octet)
+		                         Data (variable)
+
+	Opcode 0x8b - LE Test Mode notification
+
+		Notification parameters: Status (1 octet)
+		                         Num packets (2 octets)
+
+
+Bluetooth Socket HAL (ID 2)
+===========================
+
+Android HAL name:: "socket" (BT_PROFILE_SOCKETS_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Listen command/response
+
+		Command parameters: Socket type (1 octet)
+		                    Service name (256 octets)
+		                    Service UUID (16 octets)
+		                    Channel (4 octets)
+		                    Socket flags (1 octet)
+		Response parameters: File descriptor (inline)
+
+		Valid socket types: 0x01 = RFCOMM
+		                    0x02 = SCO
+		                    0x03 = L2CAP
+
+		Valid socket flags: 0x01 = Encrypt
+		                    0x02 = Auth
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Socket type (1 octet)
+		                    Service UUID (16 octets)
+		                    Channel (4 octets)
+		                    Socket flags (1 octet)
+		Response parameters: File descriptor (inline)
+
+		Valid socket types: 0x01 = RFCOMM
+		                    0x02 = SCO
+		                    0x03 = L2CAP
+
+		Valid socket flags: 0x01 = Encrypt
+		                    0x02 = Auth
+
+		In case of an error, the error response will be returned.
+
+
+Bluetooth HID Host HAL (ID 3)
+============================
+
+Android HAL name: "hidhost" (BT_PROFILE_HIDHOST_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Virtual Unplug command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Set Info command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Attribute mask (2 octets)
+		                    Subclass (1 octet)
+		                    Application ID (1 octet)
+		                    Vendor ID (2 octets)
+		                    Product ID (2 octets)
+		                    Version (2 octets)
+		                    Country code (1 octet)
+		                    Descriptor length (2 octet)
+		                    Descriptor value (884 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Get Protocol command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Protocol mode (1 octet)
+		Response parameters: <none>
+
+		Valid protocol modes: 0x00 = Report
+		                      0x01 = Boot
+		                      0xff = Unsupported
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Set Protocol command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Protocol mode (1 octet)
+		Response parameters: <none>
+
+		Valid protocol modes: 0x00 = Report
+		                      0x01 = Boot
+		                      0xff = Unsupported
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Get Report command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Report type (1 octet)
+		                    Report ID (1 octet)
+		                    Buffer size (2 octet)
+		Response parameters: <none>
+
+		Valid report types: 0x01 = Input
+		                    0x02 = Output
+		                    0x03 = Feature
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Set Report command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Report type (1 octet)
+		                    Report length (2 octets)
+		                    Report data (Report length)
+
+		Response parameters: <none>
+
+		Valid report types: 0x01 = Input
+		                    0x02 = Output
+		                    0x03 = Feature
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Send Data command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Data length (2 octets)
+		                    Data (Data length)
+
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Status is common for many notifications and has fixed value range:
+
+		Status values: 0x00 = Ok
+		               0x01 = Handshake - Device not ready
+		               0x02 = Handshake - Invalid report ID
+		               0x03 = Handshake - Transaction not SPT
+		               0x04 = Handshake - Invalid parameter
+		               0x05 = Handshake - Generic error
+		               0x06 = General error
+		               0x07 = SDP error
+		               0x08 = Set protocol error
+		               0x09 = Device database full
+		               0x0a = Device type not supported
+		               0x0b = No resources
+		               0x0c = Authentication failed
+		               0x0d = HDL
+
+	Opcode 0x81 - Connection State notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Connection State (1 octets)
+
+		Valid connection states: 0x00 = Connected
+		                         0x01 = Connecting
+		                         0x02 = Disconnected
+		                         0x03 = Disconnecting
+		                         0x04 = Failed - Mouse from host
+		                         0x05 = Failed - Keyboard from host
+		                         0x06 = Failed - Too many devices
+		                         0x07 = Failed - No HID driver
+		                         0x08 = Failed - generic
+		                         0x09 = Unknown
+
+	Opcode 0x82 - HID Info notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Attribute mask (2 octets)
+		                         Subclass (1 octet)
+		                         Application ID (1 octet)
+		                         Vendor ID (2 octets)
+		                         Product ID (2 octets)
+		                         Version (2 octets)
+		                         Country code (1 octet)
+		                         Descriptor length (2 octet)
+		                         Descriptor value (884 octets)
+
+	Opcode 0x83 - Protocol Mode notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+		                         Protocol mode (1 octet)
+
+		Valid protocol modes: 0x00 = Report
+		                      0x01 = Boot
+		                      0xff = Unsupported
+
+	Opcode 0x84 - Idle Time notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+		                         Idle time (2 octets)
+
+	Opcode 0x85 - Get Report notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+		                         Report length (2 octets)
+		                         Report data (variable)
+
+	Opcode 0x86 - Virtual Unplug notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+
+	Opcode 0x87 - Handshake notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Status (1 octet)
+
+		Only status values from 0x00 to 0x05 are valid as handshake
+		status.
+
+
+Bluetooth PAN HAL (ID 4)
+========================
+
+Android HAL name: "pan" (BT_PROFILE_PAN_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Enable command/response
+
+		Command parameters: Local role (1 octet)
+		Response parameters: <none>
+
+		Valid role values: 0x00 = None
+		                   0x01 = NAP
+		                   0x02 = PANU
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Get Local Role command/response
+
+		Command parameters: <none>
+		Response parameters: Local role (1 octet)
+
+		Valid role values: 0x00 = None
+		                   0x01 = NAP
+		                   0x02 = PANU
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Local role (1 octet)
+		                    Remote role (1 octet)
+		Response parameters: <none>
+
+		Valid role values: 0x01 = NAP
+		                   0x02 = PANU
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Control State notification
+
+		Notification parameters: Control state (1 octet)
+		                         Status (1 octet)
+		                         Local role (1 octet)
+		                         Interface name (17 octet)
+
+		Valid control states: 0x00 = Enabled
+		                      0x01 = Disabled
+
+		Valid role values: 0x00 = None
+		                   0x01 = NAP
+		                   0x02 = PANU
+
+	Opcode 0x82 - Connection State notification
+
+		Notification parameters: Connection state (1 octet)
+		                         Status (1 octet)
+		                         Remote address (6 octets)
+		                         Local role (1 octet)
+		                         Remote role (1 octet)
+
+		Valid connection states: 0x00 = Connected
+		                         0x01 = Connecting
+		                         0x02 = Disconnected
+		                         0x03 = Disconnecting
+
+		Valid role values: 0x01 = NAP
+		                   0x02 = PANU
+
+
+Bluetooth Handsfree HAL (ID 5)
+==============================
+
+Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID)
+
+	Service modes: 0x00 = Headset Profile only mode (default)
+	               0x01 = Handsfree Profile (narrowband speech)
+	               0x02 = Handsfree Profile (narrowband and wideband speech)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Connect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Disconnect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Start Voice Recognition command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Stop Voice Recognition command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Volume Control command/response
+
+		Command parameters: Volume type (1 octet)
+		                    Volume (1 octet)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Device Status Notification command/response
+
+		Command parameters: Network state (1 octet)
+		                    Service type (1 octet)
+		                    Signal strength (1 octet)
+		                    Battery level (1 octet)
+		Response parameters: <none>
+
+		Valid network states: 0x00 = Not available
+		                      0x01 = Available
+
+		Valid service types: 0x00 = Home network
+		                     0x01 = Roaming network
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - COPS Response command/response
+
+		Command parameters: COPS command response (string)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - CIND Response command/response
+
+		Command parameters: Service (1 octet)
+		                    Number of active calls (1 octet)
+		                    Number of held calls (1 octet)
+		                    Call setup state (1 octet)
+		                    Signal strength (1 octet)
+		                    Roaming indicator (1 octet)
+		                    Battery level (1 octet)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid call setup states: 0x00 = Active
+		                         0x01 = Held
+		                         0x02 = Dialing
+		                         0x03 = Alerting
+		                         0x04 = Incoming
+		                         0x05 = Waiting
+		                         0x06 = Idle
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Formatted AT Response command/response
+
+		Command parameters: Pre-formatted AT response (string)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - AT Response command/response
+
+		Command parameters: Response code (1 octet)
+		                    Error code (1 octet)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid response codes: 0x00 = ERROR
+		                      0x01 = OK
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - CLCC Response command/response
+
+		Command parameters: Call index (1 octet)
+		                    Call direction (1 octet)
+		                    Call state (1 octet)
+		                    Call mode (1 octet)
+		                    Call multiparty type (1 octet)
+		                    Call number type (1 octet)
+		                    Call number (string)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		Valid call directions: 0x00 = Outgoing
+		                       0x01 = Incoming
+
+		Valid call states: 0x00 = Active
+		                   0x01 = Held
+		                   0x02 = Dialing
+		                   0x03 = Alerting
+		                   0x04 = Incoming
+		                   0x05 = Waiting
+		                   0x06 = Idle
+
+		Valid call modes: 0x00 = Voice
+		                  0x01 = Data
+		                  0x02 = Fax
+
+		Valid multiparty types: 0x00 = Single call
+		                        0x01 = Multiparty call
+
+		Valid number types: 0x81 = Unknown
+		                    0x91 = International
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Phone Status Change command/response
+
+		Command parameters: Number of active calls (1 octet)
+		                    Number of held calls (1 octet)
+		                    Call setup state (1 octet)
+		                    Call number type (1 octet)
+		                    Call number (string)
+		Response parameters: <none>
+
+		Valid call setup states: 0x00 = Active
+		                         0x01 = Held
+		                         0x02 = Dialing
+		                         0x03 = Alerting
+		                         0x04 = Incoming
+		                         0x05 = Waiting
+		                         0x06 = Idle
+
+		Valid number types: 0x81 = Unknown
+		                    0x91 = International
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Configure WBS command/response
+
+		Command parameters: Remote address (6 octets)
+		                    Config (1 octet)
+		Response parameters: <none>
+
+		Valid config values: 0x00 = None
+		                     0x01 = No
+		                     0x02 = Yes
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Connection State notification
+
+		Notification parameters: Connection state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid connection states: 0x00 = Disconnected
+		                         0x01 = Connecting
+		                         0x02 = Connected
+		                         0x03 = SLC connected
+		                         0x04 = Disconnecting
+
+	Opcode 0x82 - Audio State notification
+
+		Notification parameters: Audio state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid audio states: 0x00 = Disconnected
+		                    0x01 = Connecting
+		                    0x02 = Connected
+		                    0x03 = Disconnecting
+
+	Opcode 0x83 - Voice Recognition Command notification
+
+		Notification parameters: Voice recognition state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid voice recognition states: 0x00 = Stopped
+		                                0x01 = Started
+
+	Opcode 0x84 - Answer Call Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x85 - Hangup Call Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x86 - Volume Command notification
+
+		Notification parameters: Volume type (1 octet)
+		                         Volume (1 octet)
+		                         Remote address (6 octets)
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+	Opcode 0x87 - Dial Call Command notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Number (string)
+
+	Opcode 0x88 - DTMF Command notification
+
+		Notification parameters: Tone (1 octet)
+		                         Remote address (6 octets)
+
+	Opcode 0x89 - NREC Command notification
+
+		Notification parameters: NREC types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid NREC types: 0x00 = Stop
+		                  0x01 = Start
+
+	Opcode 0x8a - CHLD Command notification
+
+		Notification parameters: NREC types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid CHLD types: 0x00 = Release and hold
+		                  0x01 = Release active and accept held
+		                  0x02 = Hold active and accept held
+		                  0x03 = Add held call to conference
+
+	Opcode 0x8b - CNUM Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8c - CIND Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8d - COPS Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8e - CLCC Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x8f - Unknown AT Command notification
+
+		Notification parameters: Remote address (6 octets)
+		                         AT command (string)
+
+	Opcode 0x90 - Key Pressed Command notification
+
+		Notification parameters: Remote address (6 octets)
+
+	Opcode 0x91 - WBS Command notification
+
+		Notification parameters: WBS types (1 octet)
+		                         Remote address (6 octets)
+
+		Valid WBS types: 0x00 = None
+		                 0x01 = No
+		                 0x02 = Yes
+
+Bluetooth Advanced Audio HAL (ID 6)
+Bluetooth Advanced Audio Sink HAL (ID 13)
+=========================================
+
+Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID)
+Android HAL name: "a2dp_sink" (BT_PROFILE_ADVANCED_AUDIO__SINK_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disconnect command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Connection State notification
+
+		Notification parameters: Connection state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid connection states: 0x00 = Disconnected
+		                         0x01 = Connecting
+		                         0x02 = Connected
+		                         0x03 = Disconnecting
+
+	Opcode 0x82 - Audio State notification
+
+		Notification parameters: Audio state (1 octet)
+		                         Remote address (6 octets)
+
+		Valid connection states: 0x00 = Remote suspend
+		                         0x01 = Stopped
+		                         0x02 = Started
+
+	Opcode 0x83 - Audio Configuration notification
+
+		Notification parameters: Remote address (6 octets)
+					 Sample Rate in Hz (4 octets)
+					 Channel Count (1 octet)
+
+		Valid channel count: 0x01 = Mono
+		                     0x02 = Stereo
+
+Bluetooth Health HAL (ID 7)
+===========================
+
+Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Register Application command/response
+
+		Command parameters: Number of MDEP (1 octet)
+		                    Application name offset (2 octets)
+		                    Provider name offset (2 octets)
+		                    Service name offset (2 octets)
+		                    Service description offset (2 octets)
+		                    Data length (2 octets)
+		                    Data (data length)
+		Response parameters: Application ID (2 octets)
+
+		Strings are null terminated.
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Register Application MDEP data command/response
+
+		Command parameters: Application ID (2 octets)
+                                    MDEP Role (1 octet)
+		                    Data type (2 octets)
+		                    Channel type (1 octet)
+		                    MDEP description length (2 octets)
+		                    MDEP description (MDEP desciption length)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Unregister Application command/response
+
+		Command parameters: Application ID (2 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Connect Channel command/response
+
+		Command parameters: Application ID (2 octets)
+		                    Remote address (6 octets)
+		                    MDEP index (1 octet)
+		Response parameters: Channel ID (2 octets)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Destroy Channel command/response
+
+		Command parameters: Channel ID (2 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Application Registration State notification
+
+		Notification parameters: Application ID (2 octets)
+		                         Application state (1 octet)
+
+		Valid application states: 0x00 = Registration success
+		                          0x01 = Registration failed
+		                          0x02 = Deregistration success
+		                          0x03 = Deregistration failed
+
+	Opcode 0x82 - Channel State notification
+
+		Notification parameters: Application ID (2 octets)
+		                         Remote address (6 octets)
+		                         MDEP index (1 octet)
+		                         Channel ID (2 octets)
+		                         Channel state (1 octet)
+		                         File descriptor (inline)
+
+		Valid channel states: 0x00 = Connecting
+		                      0x01 = Connected
+		                      0x02 = Disconnecting
+		                      0x03 = Disconnected
+		                      0x04 = Destroyed
+
+
+Bluetooth Remote Control Target HAL (ID 8)
+===================================
+
+Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Get Play Status Response command/response
+
+		Command parameters: Status (1 octet)
+		                    Duration (4 octets)
+		                    Position (4 octets)
+
+		In case of an error, the error response will be returned.
+
+		Valid status values: 0x00 = Stopped
+		                     0x01 = Playing
+		                     0x02 = Paused
+		                     0x03 = Fwd seek
+		                     0x04 = Rev seek
+		                     0xff = Error
+
+	Opcode 0x02 - List Player Attributes Response command/response
+
+		Command parameters: Number of attributes (1 octet)
+		                    Attribute # (1 octet)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid attributes: 0x01 = Equalizer
+		                  0x02 = Repead
+		                  0x03 = Shuffle
+		                  0x04 = Scan
+
+	Opcode 0x03 - List Player Values Response command/response
+
+		Command parameters: Number of values (1 octet)
+		                    Value # (1 octet)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Get Player Values Response command/response
+
+		Command parameters: Number of attributes (1 octet)
+		                    Attribute # (1 octet)
+		                    Value # (1 octet)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid attributes: Same as in List Player Attributes
+
+	Opcode 0x05 - Get Player Attributes Text Response command/response
+
+		Command parameters: Number of attributes (1 octet)
+		                    Attribute # (1 octet)
+		                    Attribute # text length (1 octet)
+		                    Attribute # text (variable)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid attributes: Same as in List Player Attributes
+
+	Opcode 0x06 - Get Player Values Text Response command/response
+
+		Command parameters: Number of values (1 octet)
+		                    Value # (1 octet)
+		                    Value # text length (1 octet)
+		                    Value # text (variable)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Get Element Attributes Text Response command/response
+
+		Command parameters: Number of elements (1 octet)
+		                    Element # (1 octet)
+		                    Element # text length (1 octet)
+		                    Element # text (variable)
+		                    ...
+
+		In case of an error, the error response will be returned.
+
+		Valid elements: 0x01 = Title
+		                0x02 = Artist
+		                0x03 = Album
+		                0x04 = Track Number
+		                0x05 = Number of Tracks
+		                0x06 = Genre
+		                0x06 = Duration
+
+	Opcode 0x08 - Set Player Attributes Value Response command/response
+
+		Command parameters: Status (1 octet)
+
+		In case of an error, the error response will be returned.
+
+		Valid status values: Same as in Get Play Status Response
+
+	Opcode 0x09 - Register Notification Response command/response
+
+		Command parameters: Event (1 octet)
+		                    Type (1 octet)
+		                    Data length (1 octet)
+		                    Data (variable)
+
+		In case of an error, the error response will be returned.
+
+		Valid event values: 0x01 = Status Changed
+		                    0x02 = Track Changed
+		                    0x03 = Track Reached End
+		                    0x04 = Track Reached Start
+		                    0x05 = Position Changed
+		                    0x08 = Setting Changed
+
+		Valid type values : 0x00 = Interim
+		                    0x01 = Changed
+
+	Opcode 0x0a - Set Volume command/response
+
+		Command parameters: Value (1 octet)
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Remote Features notification
+
+		Notification parameters: Remote address (6 octets)
+		                         Features (1 octet)
+
+		Valid features values : 0x00 = None
+		                        0x01 = Metadata
+		                        0x02 = Absolute Volume
+		                        0x03 = Browse
+
+	Opcode 0x82 - Get Play Status notification
+
+		Notification parameters: <none>
+
+	Opcode 0x83 - List Player Attributes notification
+
+		Notification parameters: <none>
+
+	Opcode 0x84 - List Player Values notification
+
+		Notification parameters: Attribute (1 octet)
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x85 - Get Player Values notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x86 - Get Player Attributes Text notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x87 - Get Player Values Text notification
+
+		Notification parameters: Attribute (1 octet)
+		                         Number of values (1 octet)
+		                         Value # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x88 - Set Player Values notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         Value # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in List Player Attributes
+
+	Opcode 0x89 - Get Element Attributes notification
+
+		Notification parameters: Number of attributes (1 octet)
+		                         Attribute # (1 octet)
+		                         ...
+
+		Valid attribute values: Same as in Get Element Attribute
+
+	Opcode 0x8a - Register Notification notification
+
+		Notification parameters: Event (1 octet)
+		                         Parameter (4 octets)
+
+		Valid event values: Same as in Register Notification
+
+	Opcode 0x8b - Volume Changed notification
+
+		Notification parameters: Volume (1 octet)
+		                         Type (1 octet)
+
+		Valid type values: Same as in Register Notification
+
+	Opcode 0x8c - Passthrough Command notification
+
+		Notification parameters: ID (1 octet)
+		                         State (1 octet)
+
+Bluetooth GATT HAL (ID 9)
+=========================
+
+Android HAL name: "gatt" (BT_PROFILE_GATT_ID)
+
+Structures:
+
+	GATT Service ID: UUID (16 octets)
+	                 Instance ID (1 octet)
+	                 Is Primary (1 octet)
+
+	GATT Included Service ID: UUID (16 octets)
+	                          Instance ID (1 octet)
+	                          Is Primary (1 octet)
+
+	GATT Characteristic ID: UUID (16 octets)
+	                        Instance ID (1 octet)
+
+	GATT Descriptor ID: UUID (16 octets)
+	                    Instance ID (1 octet)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Client Register command/response
+
+		Command parameters: Service UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Client Unregister command/response
+
+		Command parameters: Client Interface (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Client Scan command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Start (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Client Connect Device command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    Is Direct (1 octet)
+		                    Transport (4 octets)
+		Response parameters: <none>
+
+		Valid transport value: 0x00 = Auto
+		                       0x01 = BR/EDR
+		                       0x02 = LE
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Client Disconnect Device command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    Connection ID (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Client Listen command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Start (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Client Refresh command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Client Search Service command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Filtered (1 octet)
+		                    Filter UUID (16 octets)
+		Response parameters: <none>
+
+		Filter UUID shall only be present when Filtered is non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Client Get Included Service command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Continuation (1 octet)
+		                    GATT Included Service ID (18 octets)
+		                    ...
+		Response parameters: <none>
+
+		GATT Included Service ID shall only be present when Continuation
+		is non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0a - Client Get Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Continuation (1 octet)
+		                    GATT Characteristic ID (17 octets)
+		                    ...
+		Response parameters: <none>
+
+		GATT Characteristic ID shall only be present when Continuation
+		is non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0b - Client Get Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    Continuation (1 octet)
+		                    GATT Descriptor ID (17 octets)
+		                    ...
+		Response parameters: <none>
+
+		GATT Descriptor ID shall only be present when Continuation is
+		non-zero.
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0c - Client Read Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    Authorization (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0d - Client Write Characteristic command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    Write Type (4 octets)
+		                    Length (4 octets)
+		                    Authorization Req. (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		Valid Write Type: 0x01 = No response
+		                  0x02 = Default
+		                  0x03 = Prepare
+		                  0x04 = Signed
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0e - Client Read Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    GATT Descriptor ID (17 octets)
+		                    Authorization Req. (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x0f - Client Write Descriptor command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		                    GATT Descriptor ID (17 octets)
+		                    Write Type (4 octets)
+		                    Length (4 octets)
+		                    Authorization Req. (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		Valid Write Type: 0x01 = No response
+		                  0x02 = Default
+		                  0x03 = Prepare
+		                  0x04 = Signed
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - Client Execute Write command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Execute (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x11 - Client Register For Notification command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - Client Deregister For Notification command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		                    GATT Service ID (18 octets)
+		                    GATT Characteristic ID (17 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - Client Read Remote RSSI command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x14 - Client Get Device Type command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: Device Type
+
+		Valid Device Type: 0x01 = BREDR
+		                   0x02 = BLE
+		                   0x03 = DUAL
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x15 - Client Set Advertising data command/response
+
+		Command parameters: Server Interface (4 octets)
+		                    Set Scan Resp. (1 octet)
+		                    Include Name (1 octet)
+		                    Include TX Power (1 octet)
+		                    Min. Interval (4 octets)
+		                    Max. Interval (4 octets)
+		                    Appearance (4 octets)
+		                    Manufacturer Len. (2 octets)
+		                    Manufacturer Data (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x16 - Client Test Command command/response
+
+		Command parameters: Command (4 octets)
+		                    Address (6 octets)
+		                    UUID (16 octets)
+		                    U1 (2 octets)
+		                    U2 (2 octets)
+		                    U3 (2 octets)
+		                    U4 (2 octets)
+		                    U5 (2 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x17 - Server Register command/response
+
+		Command parameters: UUID (16 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x18 - Server Unregister command/response
+
+		Command parameters: Server (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x19 - Server Connect Peripheral command/response
+
+		Command parameters: Server (4 octets)
+		                    Remote address (6 octets)
+		                    Is Direct (1 octet)
+		                    Transport (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1a - Server Disconnect Peripheral command/response
+
+		Command parameters: Server (4 octets)
+		                    Remote address (6 octets)
+		                    Connection ID (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1b - Server Add Service command/response
+
+		Command parameters: Server (4 octets)
+		                    GATT Service ID (18 octets)
+		                    Number of Handles (4 octet)
+		Response parameters: <none>
+
+		Valid GATT Service ID: UUID (16 octets)
+		                       Instance ID (1 octet)
+		                       Is Primary (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1c - Server Add Included Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    Included handle (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1d - Server Add Characteristic command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    UUID (16 octets)
+		                    Properties (4 octets)
+		                    Permissions (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1e - Server Add Descriptor command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    UUID (16 octets)
+		                    Permissions (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x1f - Server Start Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		                    Transport (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x20 - Server Stop Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x21 - Server Delete Service command/response
+
+		Command parameters: Server (4 octets)
+		                    Service handle (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x22 - Server Send Indication command/response
+
+		Command parameters: Server (4 octets)
+		                    Attribute handle (4 octets)
+		                    Connection ID (4 octets)
+		                    Length (4 octets)
+		                    Confirmation (4 octets)
+		                    Value (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x23 - Server Send Response command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    Transaction ID (4 octets)
+		                    Handle (2 octets)
+		                    Offset (2 octets)
+		                    Auth Request (1 octect)
+		                    Status (4 octets)
+		                    GATT Response (4 octets)
+		Response parameters: <none>
+
+		Valid GATT Response: GATT Value (607 octets)
+		                     Handle (2 octets)
+
+		Valid GATT Value: Value (600 octets)
+		                  Handle (2 octets)
+		                  Offset (2 octets)
+		                  Length (2 octets)
+		                  Authentication Request (1 octet)
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x24 - Client Scan Filter Params Setup command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Action (4 octets)
+		                    Filter Index (4 octets)
+		                    Features (4 octets)
+		                    List Type (4 octets)
+		                    Filter Type (4 octets)
+		                    RSSI High Threshold (4 octets)
+		                    RSSI Low Threshold (4 octets)
+		                    Delivery Mode (4 octets)
+		                    Found Timeout (4 octets)
+		                    Lost Timeout (4 octets)
+		                    Found Timeout Count (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x25 - Client Scan Filter Add Remove command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Action (4 octets)
+		                    Filter Type (4 octets)
+		                    Filter Index (4 octets)
+		                    Company ID (4 octets)
+		                    Company ID Mask (4 octets)
+		                    UUID (16 octets)
+		                    UUID Mask (16 octets)
+		                    Address (6 octets)
+		                    Address Type (1 octet)
+		                    Data Length (4 octets)
+		                    Data (variable)
+		                    Mask Length (4 octets)
+		                    Mask (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x26 - Client Scan Filter Clear command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Filter Index (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x27 - Client Scan Filter Enable command/response
+
+		Command parameters: Client Interface (4 octets)
+		                    Enable (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x28 - Client Configure MTU command/response
+
+		Command parameters: Connection ID (4 octets)
+		                    MTU (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x29 - Client Connection Parameter Update command/response
+
+		Command parameters: Address (6 octets)
+		                    Min Interval (4 octets)
+		                    Max Interval (4 octets)
+		                    Latency (4 octets)
+		                    Timeoutl (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2a - Client Set Scan Parameters command/response
+
+		Command parameters: Scan Interval (4 octets)
+		                    Scan Window (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2b - Client Setup Multi Advertising command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Min Interval (4 octets)
+		                    Max Interval (4 octets)
+		                    ADV Type (4 octets)
+		                    Channel Map (4 octets)
+		                    TX Power (4 octets)
+		                    Timeout (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2c - Client Update Multi Advertising command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Min Interval (4 octets)
+		                    Max Interval (4 octets)
+		                    ADV Type (4 octets)
+		                    Channel Map (4 octets)
+		                    TX Power (4 octets)
+		                    Timeout (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2d - Client Setup Multi Advertising Instance command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Set Scan Response (1 octet)
+		                    Include Name (1 octet)
+		                    Include TX Power (1 octet)
+		                    Appearance (4 octets)
+		                    Manufacturer Data Length (4 octets)
+		                    Manufacturer Data (variable)
+		                    Service Data Length (4 octets)
+		                    Service Data (variable)
+		                    Service UUID Length (4 octets)
+		                    Service UUID (variable)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2e - Client Disable Multi Advertising Instance command/response
+
+		Command parameters: Client ID (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x2f - Client Configure Batchscan command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Full Max (4 octets)
+		                    Trunc Max (4 octets)
+		                    Notify Threshold (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x30 - Client Enable Batchscan command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Scan Mode (4 octets)
+		                    Scan Interval (4 octets)
+		                    Scan Window (4 octets)
+		                    Address Type (4 octets)
+		                    Discard Rule (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x31 - Client Disable Batchscan command/response
+
+		Command parameters: Client ID (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x32 - Client Read Batchscan Resports command/response
+
+		Command parameters: Client ID (4 octets)
+		                    Scan Mode (4 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Client Register notification
+
+		Notification parameters: Status (4 octets)
+		                         Client Interface (4 octets)
+		                         UUID (16 octets)
+
+	Opcode 0x82 - Client Scan Result notification
+
+		Notification parameters: Address (6 octets)
+		                         RSSI (4 octets)
+		                         Length (2 octets)
+		                         Data (variable)
+
+	Opcode 0x83 - Client Connect Device notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         Client Interface (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x84 - Client Disconnect Device notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         Client Interface (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x85 - Client Search Complete notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x86 - Client Search Result notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         GATT Service ID (18 octets)
+
+	Opcode 0x87 - Client Get Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+		                         Char Prop. (4 octets)
+
+	Opcode 0x88 - Client Get Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+		                         GATT Descriptor ID (17 octets)
+
+	Opcode 0x89 - Client Get Included Service notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Included Service ID (18 octets)
+
+	Opcode 0x8a - Client Register For Notification notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Registered (4 octets)
+		                         Status (4 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+
+	Opcode 0x8b - Client Notify notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Address (6 octets)
+		                         GATT Service ID (18 octets)
+		                         GATT Characteristic ID (17 octets)
+		                         Is Notify (1 octet)
+		                         Length (2 octets)
+		                         Value (variable)
+
+	Opcode 0x8c - Client Read Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Read Parameters (variable)
+
+		Valid GATT Read Parameters: GATT Service ID (18 octets)
+		                            GATT Characteristic ID (17 octets)
+		                            GATT Descriptor ID (17 octets)
+		                            Value Type (4 octets)
+		                            Status (1 octet)
+		                            Length (2 octets)
+		                            Value (variable)
+
+	Opcode 0x8d - Client Write Characteristic notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Write Parameters (53 octets)
+
+		Valid GATT Write Parameters: GATT Service ID (18 octets)
+		                             GATT Characteristic ID (17 octets)
+		                             GATT Description ID (17 octets)
+		                             Status (1 octet)
+
+	Opcode 0x8e - Client Read Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Read Parameters (variable)
+
+		Valid GATT Read Parameters: As described in Read Characteristic
+
+	Opcode 0x8f - Client Write Descriptor notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         GATT Write Parameters (53 octets)
+
+		Valid GATT Write Parameters: As described in Write Characteristic
+
+	Opcode 0x90 - Client Execute Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x91 - Client Read Remote RSSI notification
+
+		Notification parameters: Client (4 octets)
+		                         Address (6 octets)
+		                         RSSI (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0x92 - Client Listen notification
+
+		Notification parameters: Status (4 octets)
+		                         Server Interface (4 octets)
+
+	Opcode 0x93 - Server Register notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (16 octets)
+
+	Opcode 0x94 - Server Connection notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Server (4 octets)
+		                         Connected (4 octets)
+		                         Address (6 octets)
+
+	Opcode 0x95 - Server Service Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         GATT Service ID (18 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x96 - Server Included Service Added notification
+
+		Notification patemeters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+		                         Included Service Handle (4 octets)
+
+	Opcode 0x97 - Server Characteristic Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (16 octets)
+		                         Service Handle (4 octets)
+		                         Characteristic Handle (4 octets)
+
+	Opcode 0x98 - Server Descriptor Added notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         UUID (6 octets)
+		                         Service Handle (4 octets)
+		                         Descriptor Handle (4 octets)
+
+	Opcode 0x99 - Server Service Started notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9a - Server Service Stopped notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9b - Server Service Deleted notification
+
+		Notification parameters: Status (4 octets)
+		                         Server (4 octets)
+		                         Service Handle (4 octets)
+
+	Opcode 0x9c - Server Request Read notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Attribute Handle (4 octets)
+		                         Offset (4 octets)
+		                         Is Long (1 octet)
+
+	Opcode 0x9d - Server Request Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Attribute Handle (4 octets)
+		                         Offset (4 octets)
+		                         Length (4 octets)
+		                         Need Response (4 octets)
+		                         Is Prepare (1 octet)
+		                         Value (variable)
+
+	Opcode 0x9e - Server Request Execute Write notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Trans ID (4 octets)
+		                         Address (6 octets)
+		                         Execute Write (4 octets)
+
+	Opcode 0x9f - Server Response Confirmation notification
+
+		Notification parameters: Status (4 octets)
+		                         Handle (4 octets)
+
+	Opcode 0xa0 - Client Configure MTU notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+		                         MTU (4 octets)
+
+	Opcode 0xa1 - Client Filter Configuration notification
+
+		Notification parameters: Action (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+		                         Filter Type (4 octets)
+		                         Available Space (4 octets)
+
+	Opcode 0xa2 - Client Filter Parameters notification
+
+		Notification parameters: Action (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+		                         Available Space (4 octets)
+
+	Opcode 0xa3 - Client Filter Status notification
+
+		Notification parameters: Enable (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa4 - Client Multi Advertising Enable notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa5 - Client Multi Advertising Update notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa6 - Client Multi Advertising Data notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa7 - Client Multi Advertising Disable notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xa8 - Client Congestion notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Congested (1 octet)
+
+	Opcode 0xa9 - Client Configure Batchscan notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xaa - Client Enable Batchscan notification
+
+		Notification parameters: Action (4 octets)
+		                         Client ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xab - Client Batchscan Reports notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Status (4 octets)
+		                         Report Format (4 octets)
+		                         Num Reports (4 octets)
+		                         Data Length (4 octets)
+		                         Data (variable)
+
+	Opcode 0xac - Client Batchscan Threshold notification
+
+		Notification parameters: Client ID (4 octets)
+
+	Opcode 0xad - Client Track ADV notification
+
+		Notification parameters: Client ID (4 octets)
+		                         Filter Index (4 octets)
+		                         Address Type (4 octets)
+		                         Address (6 octets)
+		                         State (4 octets)
+
+	Opcode 0xae - Server Indication Sent notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Status (4 octets)
+
+	Opcode 0xaf - Server Congestion notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         Congested (1 octet)
+
+	Opcode 0xb0 - Server MTU Changed notification
+
+		Notification parameters: Connection ID (4 octets)
+		                         MTU (4 octets)
+
+
+Bluetooth Handsfree Client HAL (ID 10)
+======================================
+
+Android HAL name: "hf_client" (BT_PROFILE_HANDSFREE_CLIENT_ID)
+
+Commands and response:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Connect command/respose
+
+		Command parameters: Remote address (6 octects)
+		Response parameters: <none>
+
+		 In case of an error, the error response will be returned.
+
+	Opcode 0x02 - Disonnect command/response
+
+		Command parameters: Remote address (6 octetcs)
+		Response parameters: <none>
+
+		 In case of an error, the error response will be returned.
+
+	Opcode 0x03 - Connect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x04 - Disconnect Audio command/response
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x05 - Start Voice Recognition command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x06 - Stop Voice Recognition command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x07 - Volume Control command/response
+
+		Command parameters: Volume type (1 octet)
+		                    Volume (1 octet)
+		Response parameters: <none>
+
+		Valid volume types: 0x00 = Speaker
+		                    0x01 = Microphone
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x08 - Dial command/response
+
+		Command parameters: Number (string)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x09 - Dial Memory command/response
+
+		Command parameters: Location (4 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x10 - Handle Call Action command/response
+
+		Command parameters: Action (1 octet)
+		                    Call Index (1 octet)
+		Response parameters: <none>
+
+		Valid actions: 0x00 = CHLD_0
+		               0x01 = CHLD_1
+		               0x02 = CHLD_2
+		               0x03 = CHLD_3
+		               0x04 = CHLD_4
+		               0x05 = CHLD_1x
+		               0x06 = CHLD_2x
+		               0x07 = ATA
+		               0x08 = CHUP
+		               0x09 = BTRH_0
+		               0x10 = BTRH_1
+		               0x11 = BTRH_2
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x11 - Query Current Calls commad/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x12 - Query Current Operator Name
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x13 - Retrieve Subscriber Info command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x14 - Send DTMF Tone command/response
+
+		Command parameters: Tone (1 octet)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+	Opcode 0x15 - Request Last Voice Tag Number command/response
+
+		Command parameters: <none>
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Connection State Changed notification
+
+		Notification parameters: State (1 octet)
+		                         Peer Features (4 octets)
+		                         CHLD Features (4 octets)
+		                         Address (6 octets)
+
+		Valid State values: 0x00 = Disconnected
+		                    0x01 = Connecting
+		                    0x02 = Connected
+		                    0x03 = SLC Connected
+		                    0x04 = Disconnecting
+
+		Peer Features is a bitmask of the supported features. Currently
+		available bits:
+
+			0	Three way calling
+			1	Echo cancellation and/or noise reduction
+			2	Voice recognition
+			3	In band ring tone
+			4	Attach a number to a voice tag
+			5	Ability to reject a call
+			6	Enhanced call status
+			7	Enhanced call control
+			8	Extended Error Result Codes
+			9	Codec negotiations
+			10-31	Reserved for future use
+
+		CHLD Features is a bitmask of the supported features. Currently
+		available bits:
+
+			0	Release waiting call or held calls
+			1	Release active calls and accept other call
+			2	Release specified active call only
+			3	Place all active calls on hold and accept other call
+			4	Request private mode with secified call
+			5	Add a held call to the multiparty
+			6	Connect two calls and leave multiparty
+			7-31	Reserved for future use
+
+		Note: Peer and CHLD Features are valid only in SCL Connected state
+
+
+	Opcode 0x82 - Audio State Changed notification
+
+		Notification parameters: State (1 octet)
+		                         Address (6 octets)
+
+		Valid State values: 0x00 = Disconnected
+		                    0x01 = Connecting
+		                    0x02 = Connected
+		                    0x03 = Connected mSBC
+
+	Opcode 0x83 - Voice Recognition State Changed notification
+
+		Notification parameters: State (1 octet)
+
+		Valid State values: 0x00 = VR Stopped
+		                    0x01 = VR Started
+
+	Opcode 0x84 - Network State Changed notification
+
+		Notification parameters: State (1 octet)
+
+		Valid State values: 0x00 = Network Not Available
+		                    0x01 = Network Available
+
+	Opcode 0x85 - Network Roaming Type Changed notification
+
+		Notification parameters: Type (1 octet)
+
+		Valid Type values: 0x00 = Home
+		                   0x01 = Roaming
+
+	Opcode 0x86 - Network Signal Strength notification
+
+		Notification parameters: Signal Strength (1 octet)
+
+	Opcode 0x87 - Battery Level notification
+
+		Notification parameters: Battery Level (1 octet)
+
+	Opcode 0x88 - Current Operator Name notification
+
+		Notification parameters: Name (string)
+
+	Opcode 0x89 - Call Indicatior notification
+
+		Notification parameters: Call (1 octet)
+
+		Valid Call values: 0x00 = No Call In Progress
+		                   0x01 = Call In Progress
+
+	Opcode 0x8a - Call Setup Indicator notification
+
+		Notification parameters: Call Setup (1 octet)
+
+		Valid Call Setup values: 0x00 = None
+		                         0x01 = Incoming
+		                         0x02 = Outgoing
+		                         0x03 = Alerting
+
+	Opcode 0x8b - Call Held Indicator notification
+
+		Notification parameters: Call Held (1 octet)
+
+		Valid Call Held values: 0x00 = None
+		                        0x01 = Hold and Active
+					0x02 = Hold
+
+	Opcode 0x8c - Resposne and Hold Status notification
+
+		Notification parameters: Status (1 octet)
+
+		Valid Status values: 0x00 = Held
+		                     0x01 = Accept
+		                     0x02 = Reject
+
+	Opcode 0x8d - Calling Line Identification notification
+
+		Notification parameters: Number (string)
+
+		Note: This will be called only on incoming call if number is
+		provided.
+
+	Opcode 0x8e - Call Waiting notification
+
+		Notification parameters: Nunmber (string)
+
+	Opcode 0x8f - Current Calls List notification
+
+		Notification parameters: Index (1 octet)
+		                         Direction (1 octet)
+		                         Call State (1 octet)
+		                         Multiparty (1 octet)
+		                         Number (string)
+
+		Valid Direction values: 0x00 = Outgoing
+		                        0x01 = Incoming
+
+		Valid Call Sate values: 0x00 = Active
+		                        0x01 = Held
+		                        0x02 = Dialing
+		                        0x03 = Alerting
+		                        0x04 = Incoming
+		                        0x05 = Waiting
+		                        0x06 = Call held by Response and Hold
+
+		Valid Multiparty values: 0x00 = Single Call
+		                         0x01 = Multiparty (conference) Call
+
+		Note: Number might be empty
+
+	Opcode 0x90 - Volume Changed notification
+
+		Notification parameters: Type (1 octet)
+		                         Volume (1 octet)
+
+		Valid Type values: 0x00 = Speaker
+		                   0x01 = Microphone
+
+	Opcode 0x91 - Command Complete Callback notification
+
+		Notification parameters: Type (1 octet)
+		                         CME (1 octet)
+
+		Valid Type values: 0x00 = OK
+		                   0x01 = Error
+		                   0x02 = Error no carrier
+		                   0x03 = Error busy
+		                   0x04 = Error no answer
+		                   0x05 = Error delayed
+		                   0x06 = Error blacklisted
+		                   0x07 = Error CME
+
+		Note: CME parameter is valid only for Error CME type
+
+	Opcode 0x92 - Subscriber Service Info Callback notification
+
+		Notification parameters: Name (string)
+		                         Type (1 octet)
+
+		Valid Type values: 0x00 = Service unknown
+		                   0x01 = Service voice
+		                   0x02 = Service fax
+
+	Opcode 0x93 - In Band Ring Settings Callback notification
+
+		Notification parameters: State (1 octet)
+
+		Valid State values: 0x00 = In band ringtone not provided
+		                    0x01 = In band ringtone provided
+
+	Opcode 0x94 - Last Voice Call Tag Number Callback notification
+
+		Notification parameters: Number (string)
+
+	Opcode 0x95 - Ring Indication notification
+
+		Notification parameters: <none>
+
+
+Bluetooth Map Client HAL (ID 11)
+=========================
+
+Android HAL name: "map_client" (BT_PROFILE_MAP_CLIENT_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Get Remote MAS Instances
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: <none>
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Remote MAS Instances notification
+
+		Notification parameters: Status (1 octet)
+		                         Remote address (6 octets)
+		                         Number of instances (4 octets)
+		                         Instance ID # (4 octets)
+		                         Channel # (4 octets)
+		                         Message types (4 octets)
+		                         Name # (string)
+
+Bluetooth Remote Control Controller HAL (ID 12)
+===================================
+
+Android HAL name: "avrcp-ctrl" (BT_PROFILE_AV_RC_CTRL_ID)
+
+Commands and responses:
+
+	Opcode 0x00 - Error response
+
+	Opcode 0x01 - Send Pass Through command/response
+
+		Command parameters: Remote Address (6 octets)
+		                    Key Code (1 octet)
+		                    Key State (1 octet)
+
+		In case of an error, the error response will be returned.
+
+Notifications:
+
+	Opcode 0x81 - Passthrough Response Notification
+
+		Notification parameters: ID (1 octet)
+		                         Key State (1 octet)
+
+	Opcode 0x82 - Connection State Notification
+
+		Notification parameters: State (1 octet)
+		                         Remote Address (6 octets)
diff --git a/bluez/repo/android/hal-ipc.c b/bluez/repo/android/hal-ipc.c
new file mode 100644
index 0000000..363072c
--- /dev/null
+++ b/bluez/repo/android/hal-ipc.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <pthread.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-log.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+#define CONNECT_TIMEOUT (10 * 1000)
+
+static int listen_sk = -1;
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t notif_th = 0;
+
+struct service_handler {
+	const struct hal_ipc_handler *handler;
+	uint8_t size;
+};
+
+static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+								uint8_t size)
+{
+	services[service].handler = handlers;
+	services[service].size = size;
+}
+
+void hal_ipc_unregister(uint8_t service)
+{
+	services[service].handler = NULL;
+	services[service].size = 0;
+}
+
+static bool handle_msg(void *buf, ssize_t len, int fd)
+{
+	struct ipc_hdr *msg = buf;
+	const struct hal_ipc_handler *handler;
+	uint8_t opcode;
+
+	if (len < (ssize_t) sizeof(*msg)) {
+		error("IPC: message too small (%zd bytes)", len);
+		return false;
+	}
+
+	if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+		error("IPC: message malformed (%zd bytes)", len);
+		return false;
+	}
+
+	/* if service is valid */
+	if (msg->service_id > HAL_SERVICE_ID_MAX) {
+		error("IPC: unknown service (0x%x)", msg->service_id);
+		return false;
+	}
+
+	/* if service is registered */
+	if (!services[msg->service_id].handler) {
+		error("IPC: unregistered service (0x%x)", msg->service_id);
+		return false;
+	}
+
+	/* if opcode fit valid range */
+	if (msg->opcode < HAL_MINIMUM_EVENT) {
+		error("IPC: invalid opcode for service 0x%x (0x%x)",
+						msg->service_id, msg->opcode);
+		return false;
+	}
+
+	/*
+	 * opcode is used as table offset and must be adjusted as events start
+	 * with HAL_MINIMUM_EVENT offset
+	 */
+	opcode = msg->opcode - HAL_MINIMUM_EVENT;
+
+	/* if opcode is valid */
+	if (opcode >= services[msg->service_id].size) {
+		error("IPC: invalid opcode for service 0x%x (0x%x)",
+						msg->service_id, msg->opcode);
+		return false;
+	}
+
+	handler = &services[msg->service_id].handler[opcode];
+
+	/* if payload size is valid */
+	if ((handler->var_len && handler->data_len > msg->len) ||
+			(!handler->var_len && handler->data_len != msg->len)) {
+		error("IPC: message size invalid for service 0x%x opcode 0x%x "
+				"(%u bytes)",
+				msg->service_id, msg->opcode, msg->len);
+		return false;
+	}
+
+	handler->handler(msg->payload, msg->len, fd);
+
+	return true;
+}
+
+static void *notification_handler(void *data)
+{
+	struct msghdr msg;
+	struct iovec iv;
+	struct cmsghdr *cmsg;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	char buf[IPC_MTU];
+	ssize_t ret;
+	int fd;
+
+	bt_thread_associate();
+
+	while (true) {
+		memset(&msg, 0, sizeof(msg));
+		memset(buf, 0, sizeof(buf));
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+		iv.iov_base = buf;
+		iv.iov_len = sizeof(buf);
+
+		msg.msg_iov = &iv;
+		msg.msg_iovlen = 1;
+
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+
+		ret = recvmsg(notif_sk, &msg, 0);
+		if (ret < 0) {
+			error("Receiving notifications failed: %s",
+							strerror(errno));
+			goto failed;
+		}
+
+		/* socket was shutdown */
+		if (ret == 0) {
+			pthread_mutex_lock(&cmd_sk_mutex);
+			if (cmd_sk == -1) {
+				pthread_mutex_unlock(&cmd_sk_mutex);
+				break;
+			}
+			pthread_mutex_unlock(&cmd_sk_mutex);
+
+			error("Notification socket closed");
+			goto failed;
+		}
+
+		fd = -1;
+
+		/* Receive auxiliary data in msg */
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+
+		if (!handle_msg(buf, ret, fd))
+			goto failed;
+	}
+
+	close(notif_sk);
+	notif_sk = -1;
+
+	bt_thread_disassociate();
+
+	DBG("exit");
+
+	return NULL;
+
+failed:
+	exit(EXIT_FAILURE);
+}
+
+static int accept_connection(int sk)
+{
+	int err;
+	struct pollfd pfd;
+	int new_sk;
+
+	memset(&pfd, 0 , sizeof(pfd));
+	pfd.fd = sk;
+	pfd.events = POLLIN;
+
+	err = poll(&pfd, 1, CONNECT_TIMEOUT);
+	if (err < 0) {
+		err = errno;
+		error("Failed to poll: %d (%s)", err, strerror(err));
+		return -1;
+	}
+
+	if (err == 0) {
+		error("bluetoothd connect timeout");
+		return -1;
+	}
+
+	new_sk = accept(sk, NULL, NULL);
+	if (new_sk < 0) {
+		err = errno;
+		error("Failed to accept socket: %d (%s)", err, strerror(err));
+		return -1;
+	}
+
+	return new_sk;
+}
+
+bool hal_ipc_accept(void)
+{
+	int err;
+
+	cmd_sk = accept_connection(listen_sk);
+	if (cmd_sk < 0)
+		return false;
+
+	notif_sk = accept_connection(listen_sk);
+	if (notif_sk < 0) {
+		close(cmd_sk);
+		cmd_sk = -1;
+		return false;
+	}
+
+	err = pthread_create(&notif_th, NULL, notification_handler, NULL);
+	if (err) {
+		notif_th = 0;
+		error("Failed to start notification thread: %d (%s)", err,
+							strerror(err));
+		close(cmd_sk);
+		cmd_sk = -1;
+		close(notif_sk);
+		notif_sk = -1;
+		return false;
+	}
+
+	info("IPC connected");
+
+	return true;
+}
+
+bool hal_ipc_init(const char *path, size_t size)
+{
+	struct sockaddr_un addr;
+	int sk;
+	int err;
+
+	sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = errno;
+		error("Failed to create socket: %d (%s)", err,
+							strerror(err));
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, path, size);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = errno;
+		error("Failed to bind socket: %d (%s)", err, strerror(err));
+		close(sk);
+		return false;
+	}
+
+	if (listen(sk, 2) < 0) {
+		err = errno;
+		error("Failed to listen on socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	listen_sk = sk;
+
+	return true;
+}
+
+void hal_ipc_cleanup(void)
+{
+	close(listen_sk);
+	listen_sk = -1;
+
+	pthread_mutex_lock(&cmd_sk_mutex);
+	if (cmd_sk >= 0) {
+		close(cmd_sk);
+		cmd_sk = -1;
+	}
+	pthread_mutex_unlock(&cmd_sk_mutex);
+
+	if (notif_sk < 0)
+		return;
+
+	shutdown(notif_sk, SHUT_RD);
+
+	pthread_join(notif_th, NULL);
+	notif_th = 0;
+}
+
+int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
+					size_t *rsp_len, void *rsp, int *fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr cmd;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct ipc_status s;
+	size_t s_len = sizeof(s);
+
+	if (cmd_sk < 0) {
+		error("Invalid cmd socket passed to hal_ipc_cmd");
+		goto failed;
+	}
+
+	if (!rsp || !rsp_len) {
+		memset(&s, 0, s_len);
+		rsp_len = &s_len;
+		rsp = &s;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.service_id = service_id;
+	cmd.opcode = opcode;
+	cmd.len = len;
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	pthread_mutex_lock(&cmd_sk_mutex);
+
+	ret = sendmsg(cmd_sk, &msg, 0);
+	if (ret < 0) {
+		error("Sending command failed:%s", strerror(errno));
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		goto failed;
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("Command socket closed");
+		pthread_mutex_unlock(&cmd_sk_mutex);
+		goto failed;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = rsp;
+	iv[1].iov_len = *rsp_len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd) {
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+	}
+
+	ret = recvmsg(cmd_sk, &msg, 0);
+
+	pthread_mutex_unlock(&cmd_sk_mutex);
+
+	if (ret < 0) {
+		error("Receiving command response failed: %s", strerror(errno));
+		goto failed;
+	}
+
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("Too small response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.service_id != service_id) {
+		error("Invalid service id (0x%x vs 0x%x)",
+						cmd.service_id, service_id);
+		goto failed;
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("Malformed response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
+		error("Invalid opcode received (0x%x vs 0x%x)",
+						cmd.opcode, opcode);
+		goto failed;
+	}
+
+	if (cmd.opcode == HAL_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("Invalid status length");
+			goto failed;
+		}
+
+		if (s->code == HAL_STATUS_SUCCESS) {
+			error("Invalid success status response");
+			goto failed;
+		}
+
+		return s->code;
+	}
+
+	/* Receive auxiliary data in msg */
+	if (fd) {
+		struct cmsghdr *cmsg;
+
+		*fd = -1;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+	}
+
+	*rsp_len = cmd.len;
+
+	return BT_STATUS_SUCCESS;
+
+failed:
+	exit(EXIT_FAILURE);
+}
diff --git a/bluez/repo/android/hal-ipc.h b/bluez/repo/android/hal-ipc.h
new file mode 100644
index 0000000..08ed7cc
--- /dev/null
+++ b/bluez/repo/android/hal-ipc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+struct hal_ipc_handler {
+	void (*handler) (void *buf, uint16_t len, int fd);
+	bool var_len;
+	size_t data_len;
+};
+
+bool hal_ipc_init(const char *path, size_t size);
+bool hal_ipc_accept(void);
+void hal_ipc_cleanup(void);
+
+int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
+					size_t *rsp_len, void *rsp, int *fd);
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+								uint8_t size);
+void hal_ipc_unregister(uint8_t service);
diff --git a/bluez/repo/android/hal-log.h b/bluez/repo/android/hal-log.h
new file mode 100644
index 0000000..63ff61b
--- /dev/null
+++ b/bluez/repo/android/hal-log.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "BlueZ"
+
+#ifdef __BIONIC__
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+#endif
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+#define DBG(fmt, arg...) ALOG(LOG_DEBUG, LOG_TAG, "%s:%s() "fmt, __FILE__, \
+							__func__, ##arg)
diff --git a/bluez/repo/android/hal-map-client.c b/bluez/repo/android/hal-map-client.c
new file mode 100644
index 0000000..adf04fc
--- /dev/null
+++ b/bluez/repo/android/hal-map-client.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btmce_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+/* Event Handlers */
+
+static void remote_mas_instances_to_hal(btmce_mas_instance_t *send_instance,
+				struct hal_map_client_mas_instance *instance,
+				int num_instances, uint16_t len)
+{
+	void *buf = instance;
+	char *name;
+	int i;
+
+	DBG("");
+
+	for (i = 0; i < num_instances; i++) {
+		name = (char *) instance->name;
+		if (sizeof(*instance) + instance->name_len > len ||
+					(instance->name_len != 0 &&
+					name[instance->name_len - 1] != '\0')) {
+			error("invalid remote mas instance %d, aborting", i);
+			exit(EXIT_FAILURE);
+		}
+
+		send_instance[i].id = instance->id;
+		send_instance[i].msg_types = instance->msg_types;
+		send_instance[i].scn = instance->scn;
+		send_instance[i].p_name = name;
+
+		len -= sizeof(*instance) + instance->name_len;
+		buf += sizeof(*instance) + instance->name_len;
+		instance = buf;
+	}
+
+	if (!len)
+		return;
+
+	error("invalid remote mas instances (%u bytes left), aborting", len);
+	exit(EXIT_FAILURE);
+}
+
+static void handle_remote_mas_instances(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_map_client_remote_mas_instances *ev = buf;
+	btmce_mas_instance_t instances[ev->num_instances];
+
+	DBG("");
+
+	len -= sizeof(*ev);
+	remote_mas_instances_to_hal(instances, ev->instances, ev->num_instances,
+									len);
+
+	if (cbs->remote_mas_instances_cb)
+		cbs->remote_mas_instances_cb(ev->status,
+						(bt_bdaddr_t *) ev->bdaddr,
+						ev->num_instances, instances);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_MCE_REMOTE_MAS_INSTANCES */
+	{ handle_remote_mas_instances, true,
+			sizeof(struct hal_ev_map_client_remote_mas_instances) }
+};
+
+/* API */
+
+static bt_status_t get_remote_mas_instances(bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_map_client_get_instances cmd;
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES, sizeof(cmd),
+				&cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t init(btmce_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	/*
+	 * Interface ready check was removed because there is no cleanup
+	 * function to unregister and clear callbacks. MAP client testers may
+	 * restart bluetooth, unregister this profile and try to reuse it.
+	 * This situation make service unregistered but callbacks are still
+	 * set - interface is ready. On android devices there is no need to
+	 * re-init MAP client profile while bluetooth is loaded.
+	 */
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_MAP_CLIENT, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_MAP_CLIENT;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, 0, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_MAP_CLIENT);
+	}
+
+	return ret;
+}
+
+static btmce_interface_t iface = {
+	.size = sizeof(iface),
+	.init = init,
+	.get_remote_mas_instances = get_remote_mas_instances
+};
+
+btmce_interface_t *bt_get_map_client_interface(void)
+{
+	return &iface;
+}
diff --git a/bluez/repo/android/hal-msg.h b/bluez/repo/android/hal-msg.h
new file mode 100644
index 0000000..ea79fa7
--- /dev/null
+++ b/bluez/repo/android/hal-msg.h
@@ -0,0 +1,2335 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  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
+ *
+ */
+
+static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
+
+#define HAL_MINIMUM_EVENT		0x81
+
+#define HAL_SERVICE_ID_CORE		0
+#define HAL_SERVICE_ID_BLUETOOTH	1
+#define HAL_SERVICE_ID_SOCKET		2
+#define HAL_SERVICE_ID_HIDHOST		3
+#define HAL_SERVICE_ID_PAN		4
+#define HAL_SERVICE_ID_HANDSFREE	5
+#define HAL_SERVICE_ID_A2DP		6
+#define HAL_SERVICE_ID_HEALTH		7
+#define HAL_SERVICE_ID_AVRCP		8
+#define HAL_SERVICE_ID_GATT		9
+#define HAL_SERVICE_ID_HANDSFREE_CLIENT	10
+#define HAL_SERVICE_ID_MAP_CLIENT	11
+#define HAL_SERVICE_ID_AVRCP_CTRL	12
+#define HAL_SERVICE_ID_A2DP_SINK	13
+
+#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_A2DP_SINK
+
+/* Core Service */
+
+#define HAL_STATUS_SUCCESS		IPC_STATUS_SUCCESS
+#define HAL_STATUS_FAILED		0x01
+#define HAL_STATUS_NOT_READY		0x02
+#define HAL_STATUS_NOMEM		0x03
+#define HAL_STATUS_BUSY			0x04
+#define HAL_STATUS_DONE			0x05
+#define HAL_STATUS_UNSUPPORTED		0x06
+#define HAL_STATUS_INVALID		0x07
+#define HAL_STATUS_UNHANDLED		0x08
+#define HAL_STATUS_AUTH_FAILURE		0x09
+#define HAL_STATUS_REMOTE_DEVICE_DOWN	0x0a
+
+#define HAL_OP_STATUS			IPC_OP_STATUS
+
+#define HAL_MODE_DEFAULT		0x00
+#define HAL_MODE_BREDR			0x01
+#define HAL_MODE_LE			0x02
+
+#define HAL_OP_REGISTER_MODULE		0x01
+struct hal_cmd_register_module {
+	uint8_t service_id;
+	uint8_t mode;
+	int32_t max_clients;
+} __attribute__((packed));
+
+#define HAL_OP_UNREGISTER_MODULE	0x02
+struct hal_cmd_unregister_module {
+	uint8_t service_id;
+} __attribute__((packed));
+
+#define HAL_CONFIG_VENDOR		0x00
+#define HAL_CONFIG_MODEL		0x01
+#define HAL_CONFIG_NAME			0x02
+#define HAL_CONFIG_SERIAL_NUMBER	0x03
+#define HAL_CONFIG_SYSTEM_ID		0x04
+#define HAL_CONFIG_PNP_ID		0x05
+#define HAL_CONFIG_FW_REV		0x06
+#define HAL_CONFIG_HW_REV		0x07
+
+struct hal_config_prop {
+	uint8_t type;
+	uint16_t len;
+	uint8_t val[0];
+} __attribute__((packed));
+
+#define HAL_OP_CONFIGURATION		0x03
+struct hal_cmd_configuration {
+	uint8_t num;
+	struct hal_config_prop props[0];
+} __attribute__((packed));
+
+/* Bluetooth Core HAL API */
+
+#define HAL_OP_ENABLE			0x01
+
+#define HAL_OP_DISABLE			0x02
+
+#define HAL_OP_GET_ADAPTER_PROPS	0x03
+
+#define HAL_OP_GET_ADAPTER_PROP		0x04
+struct hal_cmd_get_adapter_prop {
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_MAX_NAME_LENGTH		249
+
+#define HAL_PROP_ADAPTER_NAME			0x01
+#define HAL_PROP_ADAPTER_ADDR			0x02
+#define HAL_PROP_ADAPTER_UUIDS			0x03
+#define HAL_PROP_ADAPTER_CLASS			0x04
+#define HAL_PROP_ADAPTER_TYPE			0x05
+#define HAL_PROP_ADAPTER_SERVICE_REC		0x06
+#define HAL_PROP_ADAPTER_SCAN_MODE		0x07
+#define HAL_PROP_ADAPTER_BONDED_DEVICES		0x08
+#define HAL_PROP_ADAPTER_DISC_TIMEOUT		0x09
+
+#define HAL_PROP_DEVICE_NAME			0x01
+#define HAL_PROP_DEVICE_ADDR			0x02
+#define HAL_PROP_DEVICE_UUIDS			0x03
+#define HAL_PROP_DEVICE_CLASS			0x04
+#define HAL_PROP_DEVICE_TYPE			0x05
+#define HAL_PROP_DEVICE_SERVICE_REC		0x06
+struct hal_prop_device_service_rec {
+	uint8_t uuid[16];
+	uint16_t channel;
+	uint8_t name_len;
+	uint8_t name[];
+} __attribute__((packed));
+
+#define HAL_PROP_DEVICE_FRIENDLY_NAME		0x0a
+#define HAL_PROP_DEVICE_RSSI			0x0b
+#define HAL_PROP_DEVICE_VERSION_INFO		0x0c
+struct hal_prop_device_info {
+	uint8_t version;
+	uint16_t sub_version;
+	uint16_t manufacturer;
+} __attribute__((packed));
+
+#define HAL_PROP_ADAPTER_LOCAL_LE_FEAT		0x0d
+#define HAL_PROP_DEVICE_TIMESTAMP		0xFF
+
+#define HAL_ADAPTER_SCAN_MODE_NONE		0x00
+#define HAL_ADAPTER_SCAN_MODE_CONN		0x01
+#define HAL_ADAPTER_SCAN_MODE_CONN_DISC	0x02
+
+#define HAL_TYPE_BREDR				0x01
+#define HAL_TYPE_LE				0x02
+#define HAL_TYPE_DUAL				0x03
+
+#define HAL_OP_SET_ADAPTER_PROP		0x05
+struct hal_cmd_set_adapter_prop {
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  val[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_DEVICE_PROPS	0x06
+struct hal_cmd_get_remote_device_props {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_DEVICE_PROP	0x07
+struct hal_cmd_get_remote_device_prop {
+	uint8_t bdaddr[6];
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_SET_REMOTE_DEVICE_PROP	0x08
+struct hal_cmd_set_remote_device_prop {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  val[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_SERVICE_REC	0x09
+struct hal_cmd_get_remote_service_rec {
+	uint8_t bdaddr[6];
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GET_REMOTE_SERVICES	0x0a
+struct hal_cmd_get_remote_services {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_START_DISCOVERY		0x0b
+
+#define HAL_OP_CANCEL_DISCOVERY		0x0c
+
+#define BT_TRANSPORT_UNKNOWN		0x00
+#define BT_TRANSPORT_BR_EDR		0x01
+#define BT_TRANSPORT_LE			0x02
+
+#define HAL_OP_CREATE_BOND		0x0d
+struct hal_cmd_create_bond {
+	uint8_t bdaddr[6];
+	uint8_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_REMOVE_BOND		0x0e
+struct hal_cmd_remove_bond {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_CANCEL_BOND		0x0f
+struct hal_cmd_cancel_bond {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_PIN_REPLY		0x10
+struct hal_cmd_pin_reply {
+	uint8_t bdaddr[6];
+	uint8_t accept;
+	uint8_t pin_len;
+	uint8_t pin_code[16];
+} __attribute__((packed));
+
+#define HAL_SSP_VARIANT_CONFIRM		0x00
+#define HAL_SSP_VARIANT_ENTRY		0x01
+#define HAL_SSP_VARIANT_CONSENT		0x02
+#define HAL_SSP_VARIANT_NOTIF		0x03
+
+#define HAL_OP_SSP_REPLY		0x11
+struct hal_cmd_ssp_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  ssp_variant;
+	uint8_t  accept;
+	uint32_t passkey;
+} __attribute__((packed));
+
+#define HAL_OP_DUT_MODE_CONF		0x12
+struct hal_cmd_dut_mode_conf {
+	uint8_t enable;
+} __attribute__((packed));
+
+#define HAL_OP_DUT_MODE_SEND		0x13
+struct hal_cmd_dut_mode_send {
+	uint16_t opcode;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_LE_TEST_MODE		0x14
+struct hal_cmd_le_test_mode {
+	uint16_t opcode;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GET_CONNECTION_STATE	0x15
+struct hal_cmd_get_connection_state {
+	uint8_t  bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_get_connection_state {
+	int32_t connection_state;
+} __attribute__((packed));
+
+#define HAL_OP_READ_ENERGY_INFO		0x16
+
+/* Bluetooth Socket HAL api */
+
+#define HAL_MODE_SOCKET_DEFAULT		HAL_MODE_DEFAULT
+#define HAL_MODE_SOCKET_DYNAMIC_MAP	0x01
+
+#define HAL_SOCK_RFCOMM		0x01
+#define HAL_SOCK_SCO		0x02
+#define HAL_SOCK_L2CAP		0x03
+
+#define HAL_SOCK_FLAG_ENCRYPT	0x01
+#define HAL_SOCK_FLAG_AUTH	0x02
+
+#define HAL_OP_SOCKET_LISTEN		0x01
+struct hal_cmd_socket_listen {
+	uint8_t type;
+	uint8_t name[256];
+	uint8_t uuid[16];
+	int32_t channel;
+	uint8_t flags;
+} __attribute__((packed));
+
+#define HAL_OP_SOCKET_CONNECT		0x02
+struct hal_cmd_socket_connect {
+	uint8_t bdaddr[6];
+	uint8_t type;
+	uint8_t uuid[16];
+	int32_t channel;
+	uint8_t flags;
+} __attribute__((packed));
+
+/* Bluetooth HID Host HAL API */
+
+#define HAL_OP_HIDHOST_CONNECT		0x01
+struct hal_cmd_hidhost_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_DISCONNECT		0x02
+struct hal_cmd_hidhost_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_VIRTUAL_UNPLUG		0x03
+struct hal_cmd_hidhost_virtual_unplug {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_INFO		0x04
+struct hal_cmd_hidhost_set_info {
+	uint8_t  bdaddr[6];
+	uint8_t  attr;
+	uint8_t  subclass;
+	uint8_t  app_id;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t country;
+	uint16_t descr_len;
+	uint8_t  descr[0];
+} __attribute__((packed));
+
+#define HAL_HIDHOST_REPORT_PROTOCOL		0x00
+#define HAL_HIDHOST_BOOT_PROTOCOL		0x01
+#define HAL_HIDHOST_UNSUPPORTED_PROTOCOL	0xff
+
+#define HAL_OP_HIDHOST_GET_PROTOCOL	0x05
+struct hal_cmd_hidhost_get_protocol {
+	uint8_t bdaddr[6];
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_PROTOCOL	0x06
+struct hal_cmd_hidhost_set_protocol {
+	uint8_t bdaddr[6];
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_INPUT_REPORT		0x01
+#define HAL_HIDHOST_OUTPUT_REPORT		0x02
+#define HAL_HIDHOST_FEATURE_REPORT		0x03
+
+#define HAL_OP_HIDHOST_GET_REPORT		0x07
+struct hal_cmd_hidhost_get_report {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+	uint8_t  id;
+	uint16_t buf_size;
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SET_REPORT		0x08
+struct hal_cmd_hidhost_set_report {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_HIDHOST_SEND_DATA		0x09
+struct hal_cmd_hidhost_send_data {
+	uint8_t  bdaddr[6];
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+/* a2dp source and sink HAL API */
+
+#define HAL_OP_A2DP_CONNECT	0x01
+struct hal_cmd_a2dp_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_A2DP_DISCONNECT	0x02
+struct hal_cmd_a2dp_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+/* PAN HAL API */
+
+/* PAN Roles */
+#define HAL_PAN_ROLE_NONE	0x00
+#define HAL_PAN_ROLE_NAP	0x01
+#define HAL_PAN_ROLE_PANU	0x02
+
+/* PAN Control states */
+#define HAL_PAN_CTRL_ENABLED	0x00
+#define HAL_PAN_CTRL_DISABLED	0x01
+
+/* PAN Connection states */
+#define HAL_PAN_STATE_CONNECTED		0x00
+#define HAL_PAN_STATE_CONNECTING	0x01
+#define HAL_PAN_STATE_DISCONNECTED	0x02
+#define HAL_PAN_STATE_DISCONNECTING	0x03
+
+/* PAN status values */
+#define HAL_PAN_STATUS_FAIL		0x01
+#define HAL_PAN_STATUS_NOT_READY	0x02
+#define HAL_PAN_STATUS_NO_MEMORY	0x03
+#define HAL_PAN_STATUS_BUSY		0x04
+#define HAL_PAN_STATUS_DONE		0x05
+#define HAL_PAN_STATUS_UNSUPORTED	0x06
+#define HAL_PAN_STATUS_INVAL		0x07
+#define HAL_PAN_STATUS_UNHANDLED	0x08
+#define HAL_PAN_STATUS_AUTH_FAILED	0x09
+#define HAL_PAN_STATUS_DEVICE_DOWN	0x0A
+
+#define HAL_OP_PAN_ENABLE	0x01
+struct hal_cmd_pan_enable {
+	uint8_t local_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_GET_ROLE	0x02
+struct hal_rsp_pan_get_role {
+	uint8_t local_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_CONNECT	0x03
+struct hal_cmd_pan_connect {
+	uint8_t bdaddr[6];
+	uint8_t local_role;
+	uint8_t remote_role;
+} __attribute__((packed));
+
+#define HAL_OP_PAN_DISCONNECT	0x04
+struct hal_cmd_pan_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HEALTH_MDEP_ROLE_SOURCE	0x00
+#define HAL_HEALTH_MDEP_ROLE_SINK	0x01
+
+#define HAL_HEALTH_CHANNEL_TYPE_RELIABLE	0x00
+#define HAL_HEALTH_CHANNEL_TYPE_STREAMING	0x01
+#define HAL_HEALTH_CHANNEL_TYPE_ANY		0x02
+
+#define HAL_OP_HEALTH_REG_APP		0x01
+struct hal_cmd_health_reg_app {
+	uint8_t  num_of_mdep;
+	uint16_t app_name_off;
+	uint16_t provider_name_off;
+	uint16_t service_name_off;
+	uint16_t service_descr_off;
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+struct hal_rsp_health_reg_app {
+	uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_MDEP		0x02
+struct hal_cmd_health_mdep {
+	uint16_t app_id;
+	uint8_t  role;
+	uint16_t data_type;
+	uint8_t  channel_type;
+	uint16_t descr_len;
+	uint8_t  descr[0];
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_UNREG_APP		0x03
+struct hal_cmd_health_unreg_app {
+	uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_CONNECT_CHANNEL	0x04
+struct hal_cmd_health_connect_channel {
+	uint16_t app_id;
+	uint8_t  bdaddr[6];
+	uint8_t  mdep_index;
+} __attribute__((packed));
+
+struct hal_rsp_health_connect_channel {
+	uint16_t  channel_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_DESTROY_CHANNEL	0x05
+struct hal_cmd_health_destroy_channel {
+	uint16_t channel_id;
+} __attribute__((packed));
+
+/* Handsfree HAL API */
+
+#define HAL_MODE_HANDSFREE_HSP_ONLY		HAL_MODE_DEFAULT
+#define HAL_MODE_HANDSFREE_HFP			0x01
+#define HAL_MODE_HANDSFREE_HFP_WBS		0x02
+
+#define HAL_OP_HANDSFREE_CONNECT		0x01
+struct hal_cmd_handsfree_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT		0x02
+struct hal_cmd_handsfree_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_CONNECT_AUDIO		0x03
+struct hal_cmd_handsfree_connect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT_AUDIO	0x04
+struct hal_cmd_handsfree_disconnect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_START_VR		0x05
+struct hal_cmd_handsfree_start_vr {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_STOP_VR		0x06
+struct hal_cmd_handsfree_stop_vr {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER	0x00
+#define HAL_HANDSFREE_VOLUME_TYPE_MIC		0x01
+
+#define HAL_OP_HANDSFREE_VOLUME_CONTROL		0x07
+struct hal_cmd_handsfree_volume_control {
+	uint8_t type;
+	uint8_t volume;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE	0x00
+#define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE		0x01
+
+#define HAL_HANDSFREE_SERVICE_TYPE_HOME		0x00
+#define HAL_HANDSFREE_SERVICE_TYPE_ROAMING	0x01
+
+#define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF	0x08
+struct hal_cmd_handsfree_device_status_notif {
+	uint8_t state;
+	uint8_t type;
+	uint8_t signal;
+	uint8_t battery;
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_COPS_RESPONSE		0x09
+struct hal_cmd_handsfree_cops_response {
+	uint16_t len;
+	uint8_t bdaddr[6];
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_STATE_ACTIVE		0x00
+#define HAL_HANDSFREE_CALL_STATE_HELD		0x01
+#define HAL_HANDSFREE_CALL_STATE_DIALING	0x02
+#define HAL_HANDSFREE_CALL_STATE_ALERTING	0x03
+#define HAL_HANDSFREE_CALL_STATE_INCOMING	0x04
+#define HAL_HANDSFREE_CALL_STATE_WAITING	0x05
+#define HAL_HANDSFREE_CALL_STATE_IDLE		0x06
+
+#define HAL_OP_HANDSFREE_CIND_RESPONSE		0x0A
+struct hal_cmd_handsfree_cind_response {
+	uint8_t svc;
+	uint8_t num_active;
+	uint8_t num_held;
+	uint8_t state;
+	uint8_t signal;
+	uint8_t roam;
+	uint8_t batt_chg;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE	0x0B
+struct hal_cmd_handsfree_formatted_at_response {
+	uint16_t len;
+	uint8_t bdaddr[6];
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_AT_RESPONSE_ERROR		0x00
+#define HAL_HANDSFREE_AT_RESPONSE_OK		0x01
+
+#define HAL_OP_HANDSFREE_AT_RESPONSE		0x0C
+struct hal_cmd_handsfree_at_response {
+	uint8_t response;
+	uint8_t error;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING	0x00
+#define HAL_HANDSFREE_CALL_DIRECTION_INCOMING	0x01
+
+#define HAL_HANDSFREE_CALL_TYPE_VOICE		0x00
+#define HAL_HANDSFREE_CALL_TYPE_DATA		0x01
+#define HAL_HANDSFREE_CALL_TYPE_FAX		0x02
+
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE	0x00
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI	0x01
+
+#define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN	0x81
+#define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL	0x91
+
+#define HAL_OP_HANDSFREE_CLCC_RESPONSE		0x0D
+struct hal_cmd_handsfree_clcc_response {
+	uint8_t index;
+	uint8_t dir;
+	uint8_t state;
+	uint8_t mode;
+	uint8_t mpty;
+	uint8_t type;
+	uint8_t bdaddr[6];
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE	0x0E
+struct hal_cmd_handsfree_phone_state_change {
+	uint8_t num_active;
+	uint8_t num_held;
+	uint8_t state;
+	uint8_t type;
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_WBS_NONE			0x00
+#define HAL_HANDSFREE_WBS_NO			0x01
+#define HAL_HANDSFREE_WBS_YES			0x02
+
+#define HAL_OP_HANDSFREE_CONFIGURE_WBS		0x0F
+struct hal_cmd_handsfree_configure_wbs {
+	uint8_t bdaddr[6];
+	uint8_t config;
+} __attribute__((packed));
+
+/* AVRCP TARGET HAL API */
+
+#define HAL_AVRCP_PLAY_STATUS_STOPPED	0x00
+#define HAL_AVRCP_PLAY_STATUS_PLAYING	0x01
+#define HAL_AVRCP_PLAY_STATUS_PAUSED	0x02
+#define HAL_AVRCP_PLAY_STATUS_FWD_SEEK	0x03
+#define HAL_AVRCP_PLAY_STATUS_REV_SEEK	0x04
+#define HAL_AVRCP_PLAY_STATUS_ERROR	0xff
+
+#define HAL_OP_AVRCP_GET_PLAY_STATUS	0x01
+struct hal_cmd_avrcp_get_play_status {
+	uint8_t status;
+	uint32_t duration;
+	uint32_t position;
+} __attribute__((packed));
+
+#define HAL_AVRCP_PLAYER_ATTR_EQUALIZER	0x01
+#define HAL_AVRCP_PLAYER_ATTR_REPEAT	0x02
+#define HAL_AVRCP_PLAYER_ATTR_SHUFFLE	0x03
+#define HAL_AVRCP_PLAYER_ATTR_SCAN	0x04
+
+#define HAL_OP_AVRCP_LIST_PLAYER_ATTRS	0x02
+struct hal_cmd_avrcp_list_player_attrs {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_LIST_PLAYER_VALUES	0x03
+struct hal_cmd_avrcp_list_player_values {
+	uint8_t number;
+	uint8_t values[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_attr_value {
+	uint8_t attr;
+	uint8_t value;
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS	0x04
+struct hal_cmd_avrcp_get_player_attrs {
+	uint8_t number;
+	struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_setting_text {
+	uint8_t id;
+	uint8_t len;
+	uint8_t text[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT	0x05
+struct hal_cmd_avrcp_get_player_attrs_text {
+	uint8_t number;
+	struct hal_avrcp_player_setting_text attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT	0x06
+struct hal_cmd_avrcp_get_player_values_text {
+	uint8_t number;
+	struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_AVRCP_MEDIA_ATTR_TITLE		0x01
+#define HAL_AVRCP_MEDIA_ATTR_ARTIST		0x02
+#define HAL_AVRCP_MEDIA_ATTR_ALBUM		0x03
+#define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM		0x04
+#define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS		0x05
+#define HAL_AVRCP_MEDIA_ATTR_GENRE		0x06
+#define HAL_AVRCP_MEDIA_ATTR_DURATION		0x07
+
+#define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT	0x07
+struct hal_cmd_avrcp_get_element_attrs_text {
+	uint8_t number;
+	struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE	0x08
+struct hal_cmd_avrcp_set_player_attrs_value {
+	uint8_t status;
+} __attribute__((packed));
+
+#define HAL_AVRCP_EVENT_STATUS_CHANGED		0x01
+#define HAL_AVRCP_EVENT_TRACK_CHANGED		0x02
+#define HAL_AVRCP_EVENT_TRACK_REACHED_END	0x03
+#define HAL_AVRCP_EVENT_TRACK_REACHED_START	0x04
+#define HAL_AVRCP_EVENT_POSITION_CHANGED	0x05
+#define HAL_AVRCP_EVENT_SETTING_CHANGED		0x08
+
+#define HAL_AVRCP_EVENT_TYPE_INTERIM		0x00
+#define HAL_AVRCP_EVENT_TYPE_CHANGED		0x01
+
+#define HAL_OP_AVRCP_REGISTER_NOTIFICATION	0x09
+struct hal_cmd_avrcp_register_notification {
+	uint8_t event;
+	uint8_t type;
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_VOLUME			0x0a
+struct hal_cmd_avrcp_set_volume {
+	uint8_t value;
+} __attribute__((packed));
+
+/* AVRCP CTRL HAL API */
+
+#define HAL_OP_AVRCP_CTRL_SEND_PASSTHROUGH	0x01
+struct hal_cmd_avrcp_ctrl_send_passthrough {
+	uint8_t bdaddr[6];
+	uint8_t key_code;
+	uint8_t key_state;
+} __attribute__((packed));
+
+/* GATT HAL API */
+
+#define HAL_OP_GATT_CLIENT_REGISTER		0x01
+struct hal_cmd_gatt_client_register {
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UNREGISTER		0x02
+struct hal_cmd_gatt_client_unregister {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN			0x03
+struct hal_cmd_gatt_client_scan {
+	int32_t client_if;
+	uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONNECT		0x04
+struct hal_cmd_gatt_client_connect {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	uint8_t is_direct;
+	int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISCONNECT		0x05
+struct hal_cmd_gatt_client_disconnect {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_LISTEN		0x06
+struct hal_cmd_gatt_client_listen {
+	int32_t client_if;
+	uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REFRESH		0x07
+struct hal_cmd_gatt_client_refresh {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SEARCH_SERVICE	0x08
+struct hal_cmd_gatt_client_search_service {
+	int32_t conn_id;
+	uint8_t filtered;
+	uint8_t filter_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE	0x09
+struct hal_gatt_srvc_id {
+	uint8_t uuid[16];
+	uint8_t inst_id;
+	uint8_t is_primary;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_included_service {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	uint8_t continuation;
+	struct hal_gatt_srvc_id incl_srvc_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC	0x0a
+struct hal_gatt_gatt_id {
+	uint8_t uuid[16];
+	uint8_t inst_id;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_characteristic {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	uint8_t continuation;
+	struct hal_gatt_gatt_id char_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR	0x0b
+struct hal_cmd_gatt_client_get_descriptor {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	uint8_t continuation;
+	struct hal_gatt_gatt_id descr_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC	0x0c
+struct hal_cmd_gatt_client_read_characteristic {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	int32_t auth_req;
+} __attribute__((packed));
+
+#define GATT_WRITE_TYPE_NO_RESPONSE	0x01
+#define GATT_WRITE_TYPE_DEFAULT		0x02
+#define GATT_WRITE_TYPE_PREPARE		0x03
+#define GATT_WRITE_TYPE_SIGNED		0x04
+
+#define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC	0x0d
+struct hal_cmd_gatt_client_write_characteristic {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	int32_t write_type;
+	int32_t len;
+	int32_t auth_req;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR	0x0e
+struct hal_cmd_gatt_client_read_descriptor {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	int32_t auth_req;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR	0x0f
+struct hal_cmd_gatt_client_write_descriptor {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	int32_t write_type;
+	int32_t len;
+	int32_t auth_req;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_EXECUTE_WRITE	0x10
+struct hal_cmd_gatt_client_execute_write {
+	int32_t conn_id;
+	int32_t execute;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION	0x11
+struct hal_cmd_gatt_client_register_for_notification {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION	0x12
+struct hal_cmd_gatt_client_deregister_for_notification {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI	0x13
+struct hal_cmd_gatt_client_read_remote_rssi {
+	int32_t client_if;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE	0x14
+struct hal_cmd_gatt_client_get_device_type {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_gatt_client_get_device_type {
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_ADV_DATA		0x015
+struct hal_cmd_gatt_client_set_adv_data {
+	int32_t  server_if;
+	uint8_t  set_scan_rsp;
+	uint8_t  include_name;
+	uint8_t  include_txpower;
+	int32_t  min_interval;
+	int32_t  max_interval;
+	int32_t  appearance;
+	uint16_t manufacturer_len;
+	uint16_t service_data_len;
+	uint16_t service_uuid_len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define GATT_CLIENT_TEST_CMD_ENABLE		0x01
+#define GATT_CLIENT_TEST_CMD_CONNECT		0x02
+#define GATT_CLIENT_TEST_CMD_DISCONNECT		0x03
+#define GATT_CLIENT_TEST_CMD_DISCOVER		0x04
+#define GATT_CLIENT_TEST_CMD_READ		0xe0
+#define GATT_CLIENT_TEST_CMD_WRITE		0xe1
+#define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY	0xe2
+#define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG	0xf0
+
+#define HAL_OP_GATT_CLIENT_TEST_COMMAND		0x16
+struct hal_cmd_gatt_client_test_command {
+	int32_t command;
+	uint8_t  bda1[6];
+	uint8_t  uuid1[16];
+	uint16_t u1;
+	uint16_t u2;
+	uint16_t u3;
+	uint16_t u4;
+	uint16_t u5;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_REGISTER		0x17
+struct hal_cmd_gatt_server_register {
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_UNREGISTER		0x18
+struct hal_cmd_gatt_server_unregister {
+	int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_CONNECT		0x19
+struct hal_cmd_gatt_server_connect {
+	int32_t server_if;
+	uint8_t bdaddr[6];
+	uint8_t is_direct;
+	int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DISCONNECT		0x1a
+struct hal_cmd_gatt_server_disconnect {
+	int32_t server_if;
+	uint8_t bdaddr[6];
+	int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_SERVICE		0x1b
+struct hal_cmd_gatt_server_add_service {
+	int32_t server_if;
+	struct hal_gatt_srvc_id srvc_id;
+	int32_t num_handles;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_INC_SERVICE	0x1c
+struct hal_cmd_gatt_server_add_inc_service {
+	int32_t server_if;
+	int32_t service_handle;
+	int32_t included_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC	0x1d
+struct hal_cmd_gatt_server_add_characteristic {
+	int32_t server_if;
+	int32_t service_handle;
+	uint8_t uuid[16];
+	int32_t properties;
+	int32_t permissions;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR	0x1e
+struct hal_cmd_gatt_server_add_descriptor {
+	int32_t server_if;
+	int32_t service_handle;
+	uint8_t uuid[16];
+	int32_t permissions;
+} __attribute__((packed));
+
+#define GATT_SERVER_TRANSPORT_LE_BIT		0x01
+#define GATT_SERVER_TRANSPORT_BREDR_BIT		0x02
+
+#define HAL_OP_GATT_SERVER_START_SERVICE	0x1f
+struct hal_cmd_gatt_server_start_service {
+	int32_t server_if;
+	int32_t service_handle;
+	int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_STOP_SERVICE		0x20
+struct hal_cmd_gatt_server_stop_service {
+	int32_t server_if;
+	int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DELETE_SERVICE	0x21
+struct hal_cmd_gatt_server_delete_service {
+	int32_t server_if;
+	int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_INDICATION	0x22
+struct hal_cmd_gatt_server_send_indication {
+	int32_t server_if;
+	int32_t attribute_handle;
+	int32_t conn_id;
+	int32_t len;
+	int32_t confirm;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_RESPONSE	0x23
+struct hal_cmd_gatt_server_send_response {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint16_t handle;
+	uint16_t offset;
+	uint8_t auth_req;
+	int32_t status;
+	uint16_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_SETUP	0x024
+struct hal_cmd_gatt_client_scan_filter_setup {
+	int32_t client_if;
+	int32_t action;
+	int32_t filter_index;
+	int32_t features;
+	int32_t list_type;
+	int32_t filter_type;
+	int32_t rssi_hi;
+	int32_t rssi_lo;
+	int32_t delivery_mode;
+	int32_t found_timeout;
+	int32_t lost_timeout;
+	int32_t found_timeout_cnt;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_ADD_REMOVE	0x025
+struct hal_cmd_gatt_client_scan_filter_add_remove {
+	int32_t client_if;
+	int32_t action;
+	int32_t filter_type;
+	int32_t filter_index;
+	int32_t company_id;
+	int32_t company_id_mask;
+	uint8_t uuid[16];
+	uint8_t uuid_mask[16];
+	uint8_t address[6];
+	uint8_t address_type;
+	int32_t data_len;
+	int32_t mask_len;
+	uint8_t data_mask[0]; /* common buffer for data and mask */
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_CLEAR		0x26
+struct hal_cmd_gatt_client_scan_filter_clear {
+	int32_t client_if;
+	int32_t index;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN_FILTER_ENABLE		0x27
+struct hal_cmd_gatt_client_scan_filter_enable {
+	int32_t client_if;
+	uint8_t enable;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONFIGURE_MTU		0x28
+struct hal_cmd_gatt_client_configure_mtu {
+	int32_t conn_id;
+	int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONN_PARAM_UPDATE		0x29
+struct hal_cmd_gatt_client_conn_param_update {
+	uint8_t address[6];
+	int32_t min_interval;
+	int32_t max_interval;
+	int32_t latency;
+	int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_SCAN_PARAM		0x2a
+struct hal_cmd_gatt_client_set_scan_param {
+	int32_t interval;
+	int32_t window;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV		0x2b
+struct hal_cmd_gatt_client_setup_multi_adv {
+	int32_t client_if;
+	int32_t min_interval;
+	int32_t max_interval;
+	int32_t type;
+	int32_t channel_map;
+	int32_t tx_power;
+	int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UPDATE_MULTI_ADV		0x2c
+struct hal_cmd_gatt_client_update_multi_adv {
+	int32_t client_if;
+	int32_t min_interval;
+	int32_t max_interval;
+	int32_t type;
+	int32_t channel_map;
+	int32_t tx_power;
+	int32_t timeout;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST		0x2d
+struct hal_cmd_gatt_client_setup_multi_adv_inst {
+	int32_t client_if;
+	uint8_t set_scan_rsp;
+	uint8_t include_name;
+	uint8_t include_tx_power;
+	int32_t appearance;
+	int32_t manufacturer_data_len;
+	int32_t service_data_len;
+	int32_t service_uuid_len;
+	uint8_t data_service_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST	0x2e
+struct hal_cmd_gatt_client_disable_multi_adv_inst {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONFIGURE_BATCHSCAN		0x2f
+struct hal_cmd_gatt_client_configure_batchscan {
+	int32_t client_if;
+	int32_t full_max;
+	int32_t trunc_max;
+	int32_t notify_threshold;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_ENABLE_BATCHSCAN		0x30
+struct hal_cmd_gatt_client_enable_batchscan {
+	int32_t client_if;
+	int32_t mode;
+	int32_t interval;
+	int32_t window;
+	int32_t address_type;
+	int32_t discard_rule;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISABLE_BATCHSCAN		0x31
+struct hal_cmd_gatt_client_disable_batchscan {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_BATCHSCAN_REPORTS	0x32
+struct hal_cmd_gatt_client_read_batchscan_reports {
+	int32_t client_if;
+	int32_t scan_mode;
+} __attribute__((packed));
+
+/* Handsfree client HAL API */
+
+#define HAL_OP_HF_CLIENT_CONNECT		0x01
+struct hal_cmd_hf_client_connect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DISCONNECT		0x02
+struct hal_cmd_hf_client_disconnect {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_CONNECT_AUDIO		0x03
+struct hal_cmd_hf_client_connect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DISCONNECT_AUDIO	0x04
+struct hal_cmd_hf_client_disconnect_audio {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_START_VR		0x05
+#define HAL_OP_HF_CLIENT_STOP_VR		0x06
+
+#define HF_CLIENT_VOLUME_TYPE_SPEAKER	0x00
+#define HF_CLIENT_VOLUME_TYPE_MIC	0x01
+
+#define HAL_OP_HF_CLIENT_VOLUME_CONTROL		0x07
+struct hal_cmd_hf_client_volume_control {
+	uint8_t type;
+	uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DIAL			0x08
+struct hal_cmd_hf_client_dial {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_DIAL_MEMORY		0x09
+struct hal_cmd_hf_client_dial_memory {
+	int32_t location;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_ACTION_CHLD_0		0x00
+#define HAL_HF_CLIENT_ACTION_CHLD_1		0x01
+#define HAL_HF_CLIENT_ACTION_CHLD_2		0x02
+#define HAL_HF_CLIENT_ACTION_CHLD_3		0x03
+#define HAL_HF_CLIENT_ACTION_CHLD_4		0x04
+#define HAL_HF_CLIENT_ACTION_CHLD_1x		0x05
+#define HAL_HF_CLIENT_ACTION_CHLD_2x		0x06
+#define HAL_HF_CLIENT_ACTION_ATA		0x07
+#define HAL_HF_CLIENT_ACTION_CHUP		0x08
+#define HAL_HF_CLIENT_ACTION_BRTH_0		0x09
+#define HAL_HF_CLIENT_ACTION_BRTH_1		0x0a
+#define HAL_HF_CLIENT_ACTION_BRTH_2		0x0b
+
+#define HAL_OP_HF_CLIENT_CALL_ACTION		0x0a
+struct hal_cmd_hf_client_call_action {
+	uint8_t action;
+	int32_t index;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS	0x0b
+#define HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME	0x0c
+#define HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO	0x0d
+
+#define HAL_OP_HF_CLIENT_SEND_DTMF		0x0e
+struct hal_cmd_hf_client_send_dtmf {
+	uint8_t tone;
+} __attribute__((packed));
+
+#define HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM	0x0f
+
+/* MAP CLIENT HAL API */
+
+#define HAL_OP_MAP_CLIENT_GET_INSTANCES	0x01
+struct hal_cmd_map_client_get_instances {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+/* Notifications and confirmations */
+
+#define HAL_POWER_OFF			0x00
+#define HAL_POWER_ON			0x01
+
+#define HAL_EV_ADAPTER_STATE_CHANGED	0x81
+struct hal_ev_adapter_state_changed {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_ADAPTER_PROPS_CHANGED	0x82
+struct hal_property {
+	uint8_t  type;
+	uint16_t len;
+	uint8_t  val[0];
+} __attribute__((packed));
+struct hal_ev_adapter_props_changed {
+	uint8_t              status;
+	uint8_t              num_props;
+	struct  hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_EV_REMOTE_DEVICE_PROPS	0x83
+struct hal_ev_remote_device_props {
+	uint8_t             status;
+	uint8_t             bdaddr[6];
+	uint8_t             num_props;
+	struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_EV_DEVICE_FOUND		0x84
+struct hal_ev_device_found {
+	uint8_t             num_props;
+	struct hal_property props[0];
+} __attribute__((packed));
+
+#define HAL_DISCOVERY_STATE_STOPPED	0x00
+#define HAL_DISCOVERY_STATE_STARTED	0x01
+
+#define HAL_EV_DISCOVERY_STATE_CHANGED	0x85
+struct hal_ev_discovery_state_changed {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_PIN_REQUEST		0x86
+struct hal_ev_pin_request {
+	uint8_t  bdaddr[6];
+	uint8_t  name[249];
+	uint32_t class_of_dev;
+} __attribute__((packed));
+
+#define HAL_EV_SSP_REQUEST		0x87
+struct hal_ev_ssp_request {
+	uint8_t  bdaddr[6];
+	uint8_t  name[249];
+	uint32_t class_of_dev;
+	uint8_t  pairing_variant;
+	uint32_t passkey;
+} __attribute__((packed));
+
+#define HAL_BOND_STATE_NONE 0
+#define HAL_BOND_STATE_BONDING 1
+#define HAL_BOND_STATE_BONDED 2
+
+#define HAL_EV_BOND_STATE_CHANGED	0x88
+struct hal_ev_bond_state_changed {
+	uint8_t status;
+	uint8_t bdaddr[6];
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_ACL_STATE_CONNECTED		0x00
+#define HAL_ACL_STATE_DISCONNECTED	0x01
+
+#define HAL_EV_ACL_STATE_CHANGED	0x89
+struct hal_ev_acl_state_changed {
+	uint8_t status;
+	uint8_t bdaddr[6];
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_DUT_MODE_RECEIVE		0x8a
+struct hal_ev_dut_mode_receive {
+	uint16_t opcode;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_EV_LE_TEST_MODE		0x8b
+struct hal_ev_le_test_mode {
+	uint8_t  status;
+	uint16_t num_packets;
+} __attribute__((packed));
+
+#define HAL_EV_ENERGY_INFO		0x8c
+struct hal_ev_energy_info {
+	uint8_t status;
+	uint8_t ctrl_state;
+	uint64_t tx_time;
+	uint64_t rx_time;
+	uint64_t idle_time;
+	uint64_t energy_used;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_STATE_CONNECTED		0x00
+#define HAL_HIDHOST_STATE_CONNECTING	0x01
+#define HAL_HIDHOST_STATE_DISCONNECTED	0x02
+#define HAL_HIDHOST_STATE_DISCONNECTING	0x03
+#define HAL_HIDHOST_STATE_NO_HID		0x07
+#define HAL_HIDHOST_STATE_FAILED		0x08
+#define HAL_HIDHOST_STATE_UNKNOWN		0x09
+
+#define HAL_EV_HIDHOST_CONN_STATE		0x81
+struct hal_ev_hidhost_conn_state {
+	uint8_t bdaddr[6];
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HIDHOST_STATUS_OK			0x00
+
+#define HAL_HIDHOST_HS_NOT_READY		0x01
+#define HAL_HIDHOST_HS_INVALID_RAPORT_ID	0x02
+#define HAL_HIDHOST_HS_TRANS_NOT_SUPPORTED	0x03
+#define HAL_HIDHOST_HS_INVALID_PARAM		0x04
+#define HAL_HIDHOST_HS_ERROR			0x05
+
+#define HAL_HIDHOST_GENERAL_ERROR		0x06
+#define HAL_HIDHOST_SDP_ERROR			0x07
+#define HAL_HIDHOST_PROTOCOL_ERROR		0x08
+#define HAL_HIDHOST_DB_ERROR			0x09
+#define HAL_HIDHOST_TOD_UNSUPPORTED_ERROR	0x0a
+#define HAL_HIDHOST_NO_RESOURCES_ERROR		0x0b
+#define HAL_HIDHOST_AUTH_FAILED_ERROR		0x0c
+#define HAL_HIDHOST_HDL_ERROR			0x0d
+
+#define HAL_EV_HIDHOST_INFO			0x82
+struct hal_ev_hidhost_info {
+	uint8_t  bdaddr[6];
+	uint8_t  attr;
+	uint8_t  subclass;
+	uint8_t  app_id;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t version;
+	uint8_t  country;
+	uint16_t descr_len;
+	uint8_t  descr[884];
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_PROTO_MODE		0x83
+struct hal_ev_hidhost_proto_mode {
+	uint8_t bdaddr[6];
+	uint8_t status;
+	uint8_t mode;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_IDLE_TIME		0x84
+struct hal_ev_hidhost_idle_time {
+	uint8_t bdaddr[6];
+	uint8_t status;
+	uint32_t idle_rate;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_GET_REPORT		0x85
+struct hal_ev_hidhost_get_report {
+	uint8_t  bdaddr[6];
+	uint8_t  status;
+	uint16_t len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG		0x86
+struct hal_ev_hidhost_virtual_unplug {
+	uint8_t  bdaddr[6];
+	uint8_t  status;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_HANDSHAKE		0x87
+struct hal_ev_hidhost_handshake {
+	uint8_t  bdaddr[6];
+	uint8_t  status;
+} __attribute__((packed));
+
+#define HAL_EV_PAN_CTRL_STATE			0x81
+struct hal_ev_pan_ctrl_state {
+	uint8_t  state;
+	uint8_t  status;
+	uint8_t  local_role;
+	uint8_t  name[17];
+} __attribute__((packed));
+
+#define HAL_EV_PAN_CONN_STATE			0x82
+struct hal_ev_pan_conn_state {
+	uint8_t  state;
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint8_t  local_role;
+	uint8_t  remote_role;
+} __attribute__((packed));
+
+#define HAL_HEALTH_APP_REG_SUCCESS		0x00
+#define HAL_HEALTH_APP_REG_FAILED		0x01
+#define HAL_HEALTH_APP_DEREG_SUCCESS		0x02
+#define HAL_HEALTH_APP_DEREG_FAILED		0x03
+
+#define HAL_HEALTH_CHANNEL_CONNECTING		0x00
+#define HAL_HEALTH_CHANNEL_CONNECTED		0x01
+#define HAL_HEALTH_CHANNEL_DISCONNECTING	0x02
+#define HAL_HEALTH_CHANNEL_DISCONNECTED		0x03
+#define HAL_HEALTH_CHANNEL_DESTROYED		0x04
+
+#define HAL_EV_HEALTH_APP_REG_STATE		0x81
+struct hal_ev_health_app_reg_state {
+	uint16_t id;
+	uint8_t  state;
+} __attribute__((packed));
+
+#define HAL_EV_HEALTH_CHANNEL_STATE		0x82
+struct hal_ev_health_channel_state {
+	uint16_t app_id;
+	uint8_t  bdaddr[6];
+	uint8_t  mdep_index;
+	uint16_t channel_id;
+	uint8_t  channel_state;
+} __attribute__((packed));
+
+#define HAL_A2DP_STATE_DISCONNECTED		0x00
+#define HAL_A2DP_STATE_CONNECTING		0x01
+#define HAL_A2DP_STATE_CONNECTED		0x02
+#define HAL_A2DP_STATE_DISCONNECTING		0x03
+
+#define HAL_EV_A2DP_CONN_STATE			0x81
+struct hal_ev_a2dp_conn_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_AUDIO_SUSPEND			0x00
+#define HAL_AUDIO_STOPPED			0x01
+#define HAL_AUDIO_STARTED			0x02
+
+#define HAL_EV_A2DP_AUDIO_STATE			0x82
+struct hal_ev_a2dp_audio_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_A2DP_AUDIO_CONFIG		0x83
+struct hal_ev_a2dp_audio_config {
+	uint8_t  bdaddr[6];
+	uint32_t sample_rate;
+	uint8_t  channel_count;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED	0x00
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING		0x01
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED		0x02
+#define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED	0x03
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING	0x04
+
+#define HAL_EV_HANDSFREE_CONN_STATE		0x81
+struct hal_ev_handsfree_conn_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED	0x00
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING		0x01
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED		0x02
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING	0x03
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE		0x82
+struct hal_ev_handsfree_audio_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VR_STOPPED	0x00
+#define HAL_HANDSFREE_VR_STARTED	0x01
+
+#define HAL_EV_HANDSFREE_VR		0x83
+struct hal_ev_handsfree_vr_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_ANSWER		0x84
+struct hal_ev_handsfree_answer {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HANGUP		0x85
+struct hal_ev_handsfree_hangup {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_VOLUME		0x86
+struct hal_ev_handsfree_volume {
+	uint8_t type;
+	uint8_t volume;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DIAL		0x87
+struct hal_ev_handsfree_dial {
+	uint8_t bdaddr[6];
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DTMF		0x88
+struct hal_ev_handsfree_dtmf {
+	uint8_t tone;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NREC_STOP		0x00
+#define HAL_HANDSFREE_NREC_START	0x01
+
+#define HAL_EV_HANDSFREE_NREC		0x89
+struct hal_ev_handsfree_nrec {
+	uint8_t nrec;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD			0x00
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD	0x01
+#define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD		0x02
+#define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF			0x03
+
+#define HAL_EV_HANDSFREE_CHLD		0x8A
+struct hal_ev_handsfree_chld {
+	uint8_t chld;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CNUM		0x8B
+struct hal_ev_handsfree_cnum {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CIND		0x8C
+struct hal_ev_handsfree_cind {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_COPS		0x8D
+struct hal_ev_handsfree_cops {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CLCC		0x8E
+struct hal_ev_handsfree_clcc {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_UNKNOWN_AT	0x8F
+struct hal_ev_handsfree_unknown_at {
+	uint8_t bdaddr[6];
+	uint16_t len;
+	uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HSP_KEY_PRESS	0x90
+struct hal_ev_handsfree_hsp_key_press {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_WBS		0x91
+struct hal_ev_handsfree_wbs {
+	uint8_t wbs;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_AVRCP_FEATURE_NONE			0x00
+#define HAL_AVRCP_FEATURE_METADATA		0x01
+#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME	0x02
+#define HAL_AVRCP_FEATURE_BROWSE		0x04
+
+#define HAL_EV_AVRCP_REMOTE_FEATURES		0x81
+struct hal_ev_avrcp_remote_features {
+	uint8_t bdaddr[6];
+	uint8_t features;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAY_STATUS		0x82
+#define HAL_EV_AVRCP_LIST_PLAYER_ATTRS		0x83
+
+#define HAL_EV_AVRCP_LIST_PLAYER_VALUES		0x84
+struct hal_ev_avrcp_list_player_values {
+	uint8_t attr;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES		0x85
+struct hal_ev_avrcp_get_player_values {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT	0x86
+struct hal_ev_avrcp_get_player_attrs_text {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT	0x87
+struct hal_ev_avrcp_get_player_values_text {
+	uint8_t attr;
+	uint8_t number;
+	uint8_t values[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_SET_PLAYER_VALUES		0x88
+struct hal_ev_avrcp_set_player_values {
+	uint8_t number;
+	struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_ELEMENT_ATTRS		0x89
+struct hal_ev_avrcp_get_element_attrs {
+	uint8_t number;
+	uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_REGISTER_NOTIFICATION	0x8a
+struct hal_ev_avrcp_register_notification {
+	uint8_t event;
+	uint32_t param;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_VOLUME_CHANGED		0x8b
+struct hal_ev_avrcp_volume_changed {
+	uint8_t volume;
+	uint8_t type;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_PASSTHROUGH_CMD		0x8c
+struct hal_ev_avrcp_passthrough_cmd {
+	uint8_t id;
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_CTRL_CONN_STATE		0x81
+struct hal_ev_avrcp_ctrl_conn_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_CTRL_PASSTHROUGH_RSP	0x82
+struct hal_ev_avrcp_ctrl_passthrough_rsp {
+	uint8_t id;
+	uint8_t key_state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_CLIENT	0x81
+struct hal_ev_gatt_client_register_client {
+	int32_t status;
+	int32_t client_if;
+	uint8_t app_uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SCAN_RESULT	0x82
+struct hal_ev_gatt_client_scan_result {
+	uint8_t  bda[6];
+	int32_t  rssi;
+	uint16_t len;
+	uint8_t  adv_data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONNECT	0x83
+struct hal_ev_gatt_client_connect {
+	int32_t conn_id;
+	int32_t status;
+	int32_t client_if;
+	uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_DISCONNECT	0x84
+struct hal_ev_gatt_client_disconnect {
+	int32_t conn_id;
+	int32_t status;
+	int32_t client_if;
+	uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE	0x85
+struct hal_ev_gatt_client_search_complete {
+	int32_t conn_id;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_RESULT	0x86
+struct hal_ev_gatt_client_search_result {
+	int32_t conn_id;
+	struct hal_gatt_srvc_id srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC	0x87
+struct hal_ev_gatt_client_get_characteristic {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	int32_t char_prop;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR	0x88
+struct hal_ev_gatt_client_get_descriptor {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_INC_SERVICE	0X89
+struct hal_ev_gatt_client_get_inc_service {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_srvc_id incl_srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF	0x8a
+struct hal_ev_gatt_client_reg_for_notif {
+	int32_t conn_id;
+	int32_t registered;
+	int32_t status;
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_NOTIFY		0x8b
+struct hal_ev_gatt_client_notify {
+	int32_t conn_id;
+	uint8_t bda[6];
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	uint8_t  is_notify;
+	uint16_t len;
+	uint8_t  value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC	0x8c
+struct hal_gatt_read_params {
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	uint8_t  status;
+	uint16_t value_type;
+	uint16_t len;
+	uint8_t  value[0];
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_read_characteristic {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC	0x8d
+struct hal_gatt_write_params {
+	struct hal_gatt_srvc_id srvc_id;
+	struct hal_gatt_gatt_id char_id;
+	struct hal_gatt_gatt_id descr_id;
+	uint8_t status;
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_write_characteristic {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR	0x8e
+struct hal_ev_gatt_client_read_descriptor {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR	0x8f
+struct hal_ev_gatt_client_write_descriptor {
+	int32_t conn_id;
+	int32_t status;
+	struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_EXEC_WRITE		0x90
+struct hal_ev_gatt_client_exec_write {
+	int32_t conn_id;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI	0x91
+struct hal_ev_gatt_client_read_remote_rssi {
+	int32_t client_if;
+	uint8_t address[6];
+	int32_t rssi;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_LISTEN		0x92
+struct hal_ev_gatt_client_listen {
+	int32_t status;
+	int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REGISTER		0x93
+struct hal_ev_gatt_server_register {
+	int32_t status;
+	int32_t server_if;
+	uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONNECTION		0x94
+struct hal_ev_gatt_server_connection {
+	int32_t conn_id;
+	int32_t server_if;
+	int32_t connected;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_ADDED	0x95
+struct hal_ev_gatt_server_service_added {
+	int32_t status;
+	int32_t server_if;
+	struct hal_gatt_srvc_id srvc_id;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INC_SRVC_ADDED	0x96
+struct hal_ev_gatt_server_inc_srvc_added {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+	int32_t incl_srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CHAR_ADDED		0x97
+struct hal_ev_gatt_server_characteristic_added {
+	int32_t status;
+	int32_t server_if;
+	uint8_t uuid[16];
+	int32_t srvc_handle;
+	int32_t char_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED	0x98
+struct hal_ev_gatt_server_descriptor_added {
+	int32_t status;
+	int32_t server_if;
+	uint8_t uuid[16];
+	int32_t srvc_handle;
+	int32_t descr_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STARTED	0x99
+struct hal_ev_gatt_server_service_started {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STOPPED	0x9a
+struct hal_ev_gatt_server_service_stopped {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_DELETED	0x9b
+struct hal_ev_gatt_server_service_deleted {
+	int32_t status;
+	int32_t server_if;
+	int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_READ		0x9c
+struct hal_ev_gatt_server_request_read {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint8_t bdaddr[6];
+	int32_t attr_handle;
+	int32_t offset;
+	uint8_t is_long;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_WRITE	0x9d
+struct hal_ev_gatt_server_request_write {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint8_t bdaddr[6];
+	int32_t attr_handle;
+	int32_t offset;
+	int32_t length;
+	uint8_t need_rsp;
+	uint8_t is_prep;
+	uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE	0x9e
+struct hal_ev_gatt_server_request_exec_write {
+	int32_t conn_id;
+	int32_t trans_id;
+	uint8_t bdaddr[6];
+	int32_t exec_write;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_RSP_CONFIRMATION	0x9f
+struct hal_ev_gatt_server_rsp_confirmation {
+	int32_t status;
+	int32_t handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONFIGURE_MTU	0xa0
+struct hal_ev_gatt_client_configure_mtu {
+	int32_t conn_id;
+	int32_t status;
+	int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_CONFIG	0xa1
+struct hal_ev_gatt_client_filter_config {
+	int32_t action;
+	int32_t client_if;
+	int32_t status;
+	int32_t type;
+	int32_t space;
+}  __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_PARAMS	0xa2
+struct hal_ev_gatt_client_filter_params {
+	int32_t action;
+	int32_t client_if;
+	int32_t status;
+	int32_t space;
+}  __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_FILTER_STATUS	0xa3
+struct hal_ev_gatt_client_filter_status {
+	int32_t enable;
+	int32_t client_if;
+	int32_t status;
+}  __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE	0xa4
+struct hal_ev_gatt_client_multi_adv_enable {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_UPDATE	0xa5
+struct hal_ev_gatt_client_multi_adv_update {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_DATA	0xa6
+struct hal_ev_gatt_client_multi_adv_data {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+
+#define HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE	0xa7
+struct hal_ev_gatt_client_multi_adv_disable {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONGESTION		0xa8
+struct hal_ev_gatt_client_congestion {
+	int32_t conn_id;
+	uint8_t congested;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONFIG_BATCHSCAN	0xa9
+struct hal_ev_gatt_client_config_batchscan {
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_ENABLE_BATCHSCAN	0xaa
+struct hal_ev_gatt_client_enable_batchscan {
+	int32_t action;
+	int32_t client_if;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_BATCHSCAN_REPORTS	0xab
+struct hal_ev_gatt_client_batchscan_reports {
+	int32_t client_if;
+	int32_t status;
+	int32_t format;
+	int32_t num;
+	int32_t data_len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_BATCHSCAN_THRESHOLD	0xac
+struct hal_ev_gatt_client_batchscan_threshold {
+	int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_TRACK_ADV		0xad
+struct hal_ev_gatt_client_track_adv {
+	int32_t client_if;
+	int32_t filetr_index;
+	int32_t address_type;
+	uint8_t address[6];
+	int32_t state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INDICATION_SENT	0xae
+struct hal_ev_gatt_server_indication_sent {
+	int32_t conn_id;
+	int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONGESTION		0xaf
+struct hal_ev_gatt_server_congestion {
+	int32_t conn_id;
+	uint8_t congested;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_MTU_CHANGED		0xb0
+struct hal_ev_gatt_server_mtu_changed {
+	int32_t conn_id;
+	int32_t mtu;
+} __attribute__((packed));
+
+#define HAL_GATT_PERMISSION_READ			0x0001
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED		0x0002
+#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM		0x0004
+#define HAL_GATT_PERMISSION_WRITE			0x0010
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED		0x0020
+#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM	0x0040
+#define HAL_GATT_PERMISSION_WRITE_SIGNED		0x0080
+#define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM		0x0100
+
+#define HAL_GATT_AUTHENTICATION_NONE		0
+#define HAL_GATT_AUTHENTICATION_NO_MITM		1
+#define HAL_GATT_AUTHENTICATION_MITM		2
+
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTED		0x00
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTING		0x01
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTED		0x02
+#define HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED		0x03
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTING		0x04
+
+#define HAL_HF_CLIENT_PEER_FEAT_3WAY		0x00000001
+#define HAL_HF_CLIENT_PEER_FEAT_ECNR		0x00000002
+#define HAL_HF_CLIENT_PEER_FEAT_VREC		0x00000004
+#define HAL_HF_CLIENT_PEER_FEAT_INBAND		0x00000008
+#define HAL_HF_CLIENT_PEER_FEAT_VTAG		0x00000010
+#define HAL_HF_CLIENT_PEER_FEAT_REJECT		0x00000020
+#define HAL_HF_CLIENT_PEER_FEAT_ECS		0x00000040
+#define HAL_HF_CLIENT_PEER_FEAT_ECC		0x00000080
+#define HAL_HF_CLIENT_PEER_FEAT_EXTERR		0x00000100
+#define HAL_HF_CLIENT_PEER_FEAT_CODEC		0x00000200
+
+#define HAL_HF_CLIENT_CHLD_FEAT_REL		0x00000001
+#define HAL_HF_CLIENT_CHLD_FEAT_REL_ACC		0x00000002
+#define HAL_HF_CLIENT_CHLD_FEAT_REL_X		0x00000004
+#define HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC	0x00000008
+#define HAL_HF_CLIENT_CHLD_FEAT_PRIV_X		0x00000010
+#define HAL_HF_CLIENT_CHLD_FEAT_MERGE		0x00000020
+#define HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH	0x00000040
+
+#define HAL_EV_HF_CLIENT_CONN_STATE			0x81
+struct hal_ev_hf_client_conn_state {
+	uint8_t state;
+	uint32_t peer_feat;
+	uint32_t chld_feat;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED		0x00
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTING		0x01
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED		0x02
+#define HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC	0x03
+
+#define HAL_EV_HF_CLIENT_AUDIO_STATE			0x82
+struct hal_ev_hf_client_audio_state {
+	uint8_t state;
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_VR_STOPPED	0x00
+#define HAL_HF_CLIENT_VR_STARTED	0x01
+
+#define HAL_EV_HF_CLIENT_VR_STATE			0x83
+struct hal_ev_hf_client_vr_state {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_NET_NOT_AVAILABLE		0x00
+#define HAL_HF_CLIENT_NET_AVAILABLE		0x01
+
+#define HAL_EV_HF_CLIENT_NET_STATE			0x84
+struct hal_ev_hf_client_net_state {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_NET_ROAMING_TYPE_HOME		0x00
+#define HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING		0x01
+
+#define HAL_EV_HF_CLIENT_NET_ROAMING_TYPE		0x85
+struct hal_ev_hf_client_net_roaming_type {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH		0x86
+struct hal_ev_hf_client_net_signal_strength {
+	uint8_t signal_strength;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_BATTERY_LEVEL			0x87
+struct hal_ev_hf_client_battery_level {
+	uint8_t battery_level;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_OPERATOR_NAME			0x88
+struct hal_ev_hf_client_operator_name {
+	uint16_t name_len;
+	uint8_t name[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_IND_NO_CALL_IN_PROGERSS	0x00
+#define HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS		0x01
+
+#define HAL_EV_HF_CLIENT_CALL_INDICATOR			0x89
+struct hal_ev_hf_client_call_indicator {
+	uint8_t call;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_SETUP_NONE			0x00
+#define HAL_HF_CLIENT_CALL_SETUP_INCOMING		0x01
+#define HAL_HF_CLIENT_CALL_SETUP_OUTGOING		0x02
+#define HAL_HF_CLIENT_CALL_SETUP_ALERTING		0x03
+
+#define HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR		0x8a
+struct hal_ev_hf_client_call_setup_indicator {
+	uint8_t call_setup;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CALL_HELD_IND_NONE		0x00
+#define HAL_HF_CLIENT_CALL_HELD_IND_HOLD_AND_ACTIVE	0x01
+#define HAL_HF_CLIENT_CALL_SETUP_IND_HOLD		0x02
+
+#define HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR		0x8b
+struct hal_ev_hf_client_call_held_indicator {
+	uint8_t call_held;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_HELD		0x00
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_ACCEPT	0x01
+#define HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT	0x02
+
+#define HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS	0x8c
+struct hal_ev_hf_client_response_and_hold_status {
+	uint8_t status;
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_CALLING_LINE_IDENT		0x8d
+struct hal_ev_hf_client_calling_line_ident {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HF_CLIENT_CALL_WAITING			0x8e
+struct hal_ev_hf_client_call_waiting {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_DIRECTION_OUTGOING	0x00
+#define HAL_HF_CLIENT_DIRECTION_INCOMING	0x01
+
+#define HAL_HF_CLIENT_CALL_STATE_ACTIVE			0x00
+#define HAL_HF_CLIENT_CALL_STATE_HELD			0x01
+#define HAL_HF_CLIENT_CALL_STATE_DIALING		0x02
+#define HAL_HF_CLIENT_CALL_STATE_ALERTING		0x03
+#define HAL_HF_CLIENT_CALL_STATE_INCOMING		0x04
+#define HAL_HF_CLIENT_CALL_STATE_WAITING		0x05
+#define HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD	0x06
+
+#define HAL_EV_HF_CLIENT_CURRENT_CALL			0x8f
+struct hal_ev_hf_client_current_call {
+	uint8_t index;
+	uint8_t direction;
+	uint8_t call_state;
+	uint8_t multiparty;
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_VOLUME_CHANGED			0x90
+struct hal_ev_hf_client_volume_changed {
+	uint8_t type;
+	uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_CMD_COMP_OK			0x00
+#define HAL_HF_CLIENT_CMD_COMP_ERR			0x01
+#define HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER		0x02
+#define HAL_HF_CLIENT_CMD_COMP_ERR_BUSY			0x03
+#define HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER		0x04
+#define HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED		0x05
+#define HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED		0x06
+#define HAL_HF_CLIENT_CMD_COMP_ERR_CME			0x07
+
+#define HAL_EV_CLIENT_COMMAND_COMPLETE			0x91
+struct hal_ev_hf_client_command_complete {
+	uint8_t type;
+	uint8_t cme;
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN	0x00
+#define HAL_HF_CLIENT_SUBSCR_TYPE_VOICE		0x01
+#define HAL_HF_CLIENT_SUBSCR_TYPE_FAX		0x02
+
+#define HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO		0x92
+struct hal_ev_hf_client_subscriber_service_info {
+	uint8_t type;
+	uint16_t name_len;
+	uint8_t name[0];
+} __attribute__((packed));
+
+#define HAL_HF_CLIENT_INBAND_RINGTONE_NOT_PROVIDED	0x00
+#define HAL_HF_CLIENT_INBAND_RINGTONE_PROVIDED		0x01
+
+#define HAL_EV_CLIENT_INBAND_SETTINGS			0x93
+struct hal_ev_hf_client_inband_settings {
+	uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM		0x94
+struct hal_ev_hf_client_last_void_call_tag_num {
+	uint16_t number_len;
+	uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_CLIENT_RING_INDICATION			0x95
+
+#define HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES	0x81
+struct hal_map_client_mas_instance {
+	int32_t id;
+	int32_t scn;
+	int32_t msg_types;
+	int32_t name_len;
+	uint8_t name[0];
+} __attribute__((packed));
+
+struct hal_ev_map_client_remote_mas_instances {
+	int8_t status;
+	uint8_t bdaddr[6];
+	int32_t num_instances;
+	struct hal_map_client_mas_instance instances[0];
+} __attribute__((packed));
diff --git a/bluez/repo/android/hal-pan.c b/bluez/repo/android/hal-pan.c
new file mode 100644
index 0000000..61d44a9
--- /dev/null
+++ b/bluez/repo/android/hal-pan.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "hal-utils.h"
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "hal-ipc.h"
+
+static const btpan_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+	return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_pan_conn_state *ev = buf;
+
+	if (cbs->connection_state_cb)
+		cbs->connection_state_cb(ev->state, ev->status,
+					(bt_bdaddr_t *) ev->bdaddr,
+					ev->local_role, ev->remote_role);
+}
+
+static void handle_ctrl_state(void *buf, uint16_t len, int fd)
+{
+	struct hal_ev_pan_ctrl_state *ev = buf;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (cbs->control_state_cb)
+		cbs->control_state_cb(ev->state, ev->local_role, ev->status,
+							(char *)ev->name);
+#else
+	/*
+	 * Callback declared in bt_pan.h is 'typedef void
+	 * (*btpan_control_state_callback)(btpan_control_state_t state,
+	 * bt_status_t error, int local_role, const char* ifname);
+	 * But PanService.Java defined it wrong way.
+	 * private void onControlStateChanged(int local_role, int state,
+	 * int error, String ifname).
+	 * First and third parameters are misplaced, so sending data according
+	 * to PanService.Java.
+	 */
+	if (cbs->control_state_cb)
+		cbs->control_state_cb(ev->local_role, ev->state, ev->status,
+							(char *)ev->name);
+#endif
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+	/* HAL_EV_PAN_CTRL_STATE */
+	{ handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) },
+	/* HAL_EV_PAN_CONN_STATE */
+	{ handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) },
+};
+
+static bt_status_t pan_enable(int local_role)
+{
+	struct hal_cmd_pan_enable cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	cmd.local_role = local_role;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static int pan_get_local_role(void)
+{
+	struct hal_rsp_pan_get_role rsp;
+	size_t len = sizeof(rsp);
+	bt_status_t status;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BTPAN_ROLE_NONE;
+
+	status = hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, NULL,
+							&len, &rsp, NULL);
+	if (status != BT_STATUS_SUCCESS)
+		return BTPAN_ROLE_NONE;
+
+	return rsp.local_role;
+}
+
+static bt_status_t pan_connect(const bt_bdaddr_t *bd_addr, int local_role,
+					int remote_role)
+{
+	struct hal_cmd_pan_connect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.local_role = local_role;
+	cmd.remote_role = remote_role;
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr)
+{
+	struct hal_cmd_pan_disconnect cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return BT_STATUS_NOT_READY;
+
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+}
+
+static bt_status_t pan_init(const btpan_callbacks_t *callbacks)
+{
+	struct hal_cmd_register_module cmd;
+	int ret;
+
+	DBG("");
+
+	if (interface_ready())
+		return BT_STATUS_DONE;
+
+	cbs = callbacks;
+
+	hal_ipc_register(HAL_SERVICE_ID_PAN, ev_handlers,
+				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+	cmd.service_id = HAL_SERVICE_ID_PAN;
+	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.max_clients = 1;
+
+	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	if (ret != BT_STATUS_SUCCESS) {
+		cbs = NULL;
+		hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+	}
+
+	return ret;
+}
+
+static void pan_cleanup(void)
+{
+	struct hal_cmd_unregister_module cmd;
+
+	DBG("");
+
+	if (!interface_ready())
+		return;
+
+	cmd.service_id = HAL_SERVICE_ID_PAN;
+
+	hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+					sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+	hal_ipc_unregister(HAL_SERVICE_ID_PAN);
+
+	cbs = NULL;
+}
+
+static btpan_interface_t pan_if = {
+	.size = sizeof(pan_if),
+	.init = pan_init,
+	.enable = pan_enable,
+	.get_local_role = pan_get_local_role,
+	.connect = pan_connect,
+	.disconnect = pan_disconnect,
+	.cleanup = pan_cleanup
+};
+
+btpan_interface_t *bt_get_pan_interface(void)
+{
+	return &pan_if;
+}
diff --git a/bluez/repo/android/hal-sco.c b/bluez/repo/android/hal-sco.c
new file mode 100644
index 0000000..2c95866
--- /dev/null
+++ b/bluez/repo/android/hal-sco.c
@@ -0,0 +1,1530 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+#include <audio_utils/resampler.h>
+
+#include "hal-utils.h"
+#include "sco-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal.h"
+
+#define AUDIO_STREAM_DEFAULT_RATE	44100
+#define AUDIO_STREAM_SCO_RATE		8000
+#define AUDIO_STREAM_DEFAULT_FORMAT	AUDIO_FORMAT_PCM_16_BIT
+
+#define OUT_BUFFER_SIZE			2560
+#define OUT_STREAM_FRAMES		2560
+#define IN_STREAM_FRAMES		5292
+
+#define SOCKET_POLL_TIMEOUT_MS		500
+
+static int listen_sk = -1;
+static int ipc_sk = -1;
+
+static int sco_fd = -1;
+static uint16_t sco_mtu = 0;
+static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static struct sco_stream_in *sco_stream_in = NULL;
+static struct sco_stream_out *sco_stream_out = NULL;
+
+struct sco_audio_config {
+	uint32_t rate;
+	uint32_t channels;
+	uint32_t frame_num;
+	audio_format_t format;
+};
+
+struct sco_stream_out {
+	struct audio_stream_out stream;
+
+	struct sco_audio_config cfg;
+
+	uint8_t *downmix_buf;
+	uint8_t *cache;
+	size_t cache_len;
+
+	size_t samples;
+	struct timespec start;
+
+	struct resampler_itfe *resampler;
+	int16_t *resample_buf;
+	uint32_t resample_frame_num;
+
+	bt_bdaddr_t bd_addr;
+};
+
+static void sco_close_socket(void)
+{
+	DBG("sco fd %d", sco_fd);
+
+	if (sco_fd < 0)
+		return;
+
+	shutdown(sco_fd, SHUT_RDWR);
+	close(sco_fd);
+	sco_fd = -1;
+}
+
+struct sco_stream_in {
+	struct audio_stream_in stream;
+
+	struct sco_audio_config cfg;
+
+	struct resampler_itfe *resampler;
+	int16_t *resample_buf;
+	uint32_t resample_frame_num;
+
+	bt_bdaddr_t bd_addr;
+};
+
+struct sco_dev {
+	struct audio_hw_device dev;
+	struct sco_stream_out *out;
+	struct sco_stream_in *in;
+};
+
+/*
+ * return the minimum frame numbers from resampling between BT stack's rate
+ * and audio flinger's. For output stream, 'output' shall be true, otherwise
+ * false for input streams at audio flinger side.
+ */
+static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate,
+						size_t frame_num, bool output)
+{
+	size_t resample_frames_num = frame_num * sco_rate / rate + output;
+
+	DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd",
+				sco_rate, frame_num, rate, resample_frames_num);
+
+	return resample_frames_num;
+}
+
+/* SCO IPC functions */
+
+static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+			void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr cmd;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct ipc_status s;
+	size_t s_len = sizeof(s);
+
+	pthread_mutex_lock(&sk_mutex);
+
+	if (ipc_sk < 0) {
+		error("sco: Invalid cmd socket passed to sco_ipc_cmd");
+		goto failed;
+	}
+
+	if (!rsp || !rsp_len) {
+		memset(&s, 0, s_len);
+		rsp_len = &s_len;
+		rsp = &s;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.service_id = service_id;
+	cmd.opcode = opcode;
+	cmd.len = len;
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	ret = sendmsg(ipc_sk, &msg, 0);
+	if (ret < 0) {
+		error("sco: Sending command failed:%s", strerror(errno));
+		goto failed;
+	}
+
+	/* socket was shutdown */
+	if (ret == 0) {
+		error("sco: Command socket closed");
+		goto failed;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmd, 0, sizeof(cmd));
+
+	iv[0].iov_base = &cmd;
+	iv[0].iov_len = sizeof(cmd);
+
+	iv[1].iov_base = rsp;
+	iv[1].iov_len = *rsp_len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd) {
+		memset(cmsgbuf, 0, sizeof(cmsgbuf));
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+	}
+
+	ret = recvmsg(ipc_sk, &msg, 0);
+	if (ret < 0) {
+		error("sco: Receiving command response failed:%s",
+							strerror(errno));
+		goto failed;
+	}
+
+	if (ret < (ssize_t) sizeof(cmd)) {
+		error("sco: Too small response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.service_id != service_id) {
+		error("sco: Invalid service id (%u vs %u)", cmd.service_id,
+								service_id);
+		goto failed;
+	}
+
+	if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+		error("sco: Malformed response received(%zd bytes)", ret);
+		goto failed;
+	}
+
+	if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) {
+		error("sco: Invalid opcode received (%u vs %u)",
+						cmd.opcode, opcode);
+		goto failed;
+	}
+
+	if (cmd.opcode == SCO_OP_STATUS) {
+		struct ipc_status *s = rsp;
+
+		if (sizeof(*s) != cmd.len) {
+			error("sco: Invalid status length");
+			goto failed;
+		}
+
+		if (s->code == SCO_STATUS_SUCCESS) {
+			error("sco: Invalid success status response");
+			goto failed;
+		}
+
+		pthread_mutex_unlock(&sk_mutex);
+
+		return s->code;
+	}
+
+	pthread_mutex_unlock(&sk_mutex);
+
+	/* Receive auxiliary data in msg */
+	if (fd) {
+		struct cmsghdr *cmsg;
+
+		*fd = -1;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level == SOL_SOCKET
+					&& cmsg->cmsg_type == SCM_RIGHTS) {
+				memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+				break;
+			}
+		}
+
+		if (*fd < 0)
+			goto failed;
+	}
+
+	*rsp_len = cmd.len;
+
+	return SCO_STATUS_SUCCESS;
+
+failed:
+	/* Some serious issue happen on IPC - recover */
+	shutdown(ipc_sk, SHUT_RDWR);
+	pthread_mutex_unlock(&sk_mutex);
+
+	return SCO_STATUS_FAILED;
+}
+
+static int ipc_get_sco_fd(bt_bdaddr_t *bd_addr)
+{
+	int ret = SCO_STATUS_SUCCESS;
+
+	pthread_mutex_lock(&sco_mutex);
+
+	if (sco_fd < 0) {
+		struct sco_cmd_get_fd cmd;
+		struct sco_rsp_get_fd rsp;
+		size_t rsp_len = sizeof(rsp);
+
+		DBG("Getting SCO fd");
+
+		memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+		ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_GET_FD, sizeof(cmd),
+						&cmd, &rsp_len, &rsp, &sco_fd);
+
+		/* Sometimes mtu returned is wrong */
+		sco_mtu = /* rsp.mtu */ 48;
+	}
+
+	pthread_mutex_unlock(&sco_mutex);
+
+	return ret;
+}
+
+/* Audio stream functions */
+
+static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer,
+							size_t frame_num)
+{
+	const int16_t *input = (const void *) buffer;
+	int16_t *output = (void *) out->downmix_buf;
+	size_t i;
+
+	for (i = 0; i < frame_num; i++) {
+		int16_t l = get_le16(&input[i * 2]);
+		int16_t r = get_le16(&input[i * 2 + 1]);
+
+		put_le16((l + r) / 2, &output[i]);
+	}
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+	struct timespec res;
+
+	res.tv_sec = a->tv_sec - b->tv_sec;
+	res.tv_nsec = a->tv_nsec - b->tv_nsec;
+
+	if (res.tv_nsec < 0) {
+		res.tv_sec--;
+		res.tv_nsec += 1000000000ll; /* 1sec */
+	}
+
+	return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
+								size_t bytes)
+{
+	struct pollfd pfd;
+	size_t len, written = 0;
+	int ret;
+	uint8_t *p;
+	uint64_t audio_sent_us, audio_passed_us;
+
+	pfd.fd = sco_fd;
+	pfd.events = POLLOUT | POLLHUP | POLLNVAL;
+
+	while (bytes > written) {
+		struct timespec now;
+
+		/* poll for sending */
+		if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+			DBG("timeout fd %d", sco_fd);
+			return false;
+		}
+
+		if (pfd.revents & (POLLHUP | POLLNVAL)) {
+			error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+			return false;
+		}
+
+		len = bytes - written > sco_mtu ? sco_mtu : bytes - written;
+
+		clock_gettime(CLOCK_REALTIME, &now);
+		/* Mark start of the stream */
+		if (!out->samples)
+			memcpy(&out->start, &now, sizeof(out->start));
+
+		audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE;
+		audio_passed_us = timespec_diff_us(&now, &out->start);
+		if ((int) (audio_sent_us - audio_passed_us) > 1500) {
+			struct timespec timeout = {0,
+						(audio_sent_us -
+						audio_passed_us) * 1000};
+			DBG("Sleeping for %d ms",
+					(int) (audio_sent_us - audio_passed_us));
+			nanosleep(&timeout, NULL);
+		} else if ((int)(audio_passed_us - audio_sent_us) > 50000) {
+			DBG("\n\nResync\n\n");
+			out->samples = 0;
+			memcpy(&out->start, &now, sizeof(out->start));
+		}
+
+		if (out->cache_len) {
+			DBG("First packet cache_len %zd", out->cache_len);
+			memcpy(out->cache + out->cache_len, buffer,
+						sco_mtu - out->cache_len);
+			p = out->cache;
+		} else {
+			if (bytes - written >= sco_mtu)
+				p = (void *) buffer + written;
+			else {
+				memcpy(out->cache, buffer + written,
+							bytes - written);
+				out->cache_len = bytes - written;
+				DBG("Last packet, cache %zd bytes",
+							bytes - written);
+				written += bytes - written;
+				continue;
+			}
+		}
+
+		ret = write(sco_fd, p, len);
+		if (ret > 0) {
+			if (out->cache_len) {
+				written = sco_mtu - out->cache_len;
+				out->cache_len = 0;
+			} else
+				written += ret;
+
+			out->samples += ret / 2;
+
+			DBG("written %d samples %zd total %zd bytes",
+					ret, out->samples, written);
+			continue;
+		}
+
+		if (errno == EAGAIN) {
+			ret = errno;
+			warn("write failed (%d)", ret);
+			continue;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("write failed (%d) fd %d bytes %zd", ret, sco_fd,
+									bytes);
+			return false;
+		}
+	}
+
+	DBG("written %zd bytes", bytes);
+
+	return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+								size_t bytes)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	size_t frame_num = bytes / audio_stream_out_frame_size(stream);
+#else
+	size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common);
+#endif
+	size_t output_frame_num = frame_num;
+	void *send_buf = out->downmix_buf;
+	size_t total;
+
+	DBG("write to fd %d bytes %zu", sco_fd, bytes);
+
+	if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS)
+		return -1;
+
+	if (!out->downmix_buf) {
+		error("sco: downmix buffer not initialized");
+		return -1;
+	}
+
+	downmix_to_mono(out, buffer, frame_num);
+
+	if (out->resampler) {
+		int ret;
+
+		/* limit resampler's output within what resample buf can hold */
+		output_frame_num = out->resample_frame_num;
+
+		ret = out->resampler->resample_from_input(out->resampler,
+							send_buf,
+							&frame_num,
+							out->resample_buf,
+							&output_frame_num);
+		if (ret) {
+			error("Failed to resample frames: %zd input %zd (%s)",
+				frame_num, output_frame_num, strerror(ret));
+			return -1;
+		}
+
+		send_buf = out->resample_buf;
+
+		DBG("Resampled: frame_num %zd, output_frame_num %zd",
+						frame_num, output_frame_num);
+	}
+
+	total = output_frame_num * sizeof(int16_t) * 1;
+
+	DBG("total %zd", total);
+
+	if (!write_data(out, send_buf, total))
+		return -1;
+
+	return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+	DBG("rate %u", out->cfg.rate);
+
+	return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	DBG("rate %u", rate);
+
+	return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	size_t size = audio_stream_out_frame_size(&out->stream) *
+							out->cfg.frame_num;
+#else
+	size_t size = audio_stream_frame_size(&out->stream.common) *
+							out->cfg.frame_num;
+#endif
+
+	/* buffer size without resampling */
+	if (out->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		size = 576 * 2;
+
+	DBG("buf size %zd", size);
+
+	return size;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+	DBG("channels num: %u", popcount(out->cfg.channels));
+
+	return out->cfg.channels;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+	struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+	DBG("format: %u", out->cfg.format);
+
+	return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	DBG("%s", kvpairs);
+
+	return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+
+	return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+								float right)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+							uint32_t *dsp_frames)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_open_output_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	struct sco_dev *adev = (struct sco_dev *) dev;
+	struct sco_stream_out *out;
+	int chan_num, ret;
+	size_t resample_size;
+
+	DBG("config %p device flags 0x%02x", config, devices);
+
+	if (sco_stream_out) {
+		DBG("stream_out already open");
+		return -EIO;
+	}
+
+	out = calloc(1, sizeof(struct sco_stream_out));
+	if (!out)
+		return -ENOMEM;
+
+	DBG("stream %p sco fd %d mtu %u", out, sco_fd, sco_mtu);
+
+	out->stream.common.get_sample_rate = out_get_sample_rate;
+	out->stream.common.set_sample_rate = out_set_sample_rate;
+	out->stream.common.get_buffer_size = out_get_buffer_size;
+	out->stream.common.get_channels = out_get_channels;
+	out->stream.common.get_format = out_get_format;
+	out->stream.common.set_format = out_set_format;
+	out->stream.common.standby = out_standby;
+	out->stream.common.dump = out_dump;
+	out->stream.common.set_parameters = out_set_parameters;
+	out->stream.common.get_parameters = out_get_parameters;
+	out->stream.common.add_audio_effect = out_add_audio_effect;
+	out->stream.common.remove_audio_effect = out_remove_audio_effect;
+	out->stream.get_latency = out_get_latency;
+	out->stream.set_volume = out_set_volume;
+	out->stream.write = out_write;
+	out->stream.get_render_position = out_get_render_position;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (address) {
+		DBG("address %s", address);
+
+		str2bt_bdaddr_t(address, &out->bd_addr);
+	}
+#endif
+
+	if (ipc_get_sco_fd(&out->bd_addr) != SCO_STATUS_SUCCESS)
+		DBG("SCO is not connected yet; get fd on write()");
+
+	if (config) {
+		DBG("config: rate %u chan mask %x format %d offload %p",
+				config->sample_rate, config->channel_mask,
+				config->format, &config->offload_info);
+
+		out->cfg.format = config->format;
+		out->cfg.channels = config->channel_mask;
+		out->cfg.rate = config->sample_rate;
+	} else {
+		out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+		out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+		out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+	}
+
+	out->cfg.frame_num = OUT_STREAM_FRAMES;
+
+	out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
+	if (!out->downmix_buf) {
+		free(out);
+		return -ENOMEM;
+	}
+
+	out->cache = malloc(sco_mtu);
+	if (!out->cache) {
+		free(out->downmix_buf);
+		free(out);
+		return -ENOMEM;
+	}
+
+	if (out->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		goto skip_resampler;
+
+	/* Channel numbers for resampler */
+	chan_num = 1;
+
+	ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+						RESAMPLER_QUALITY_DEFAULT, NULL,
+						&out->resampler);
+	if (ret) {
+		error("Failed to create resampler (%s)", strerror(-ret));
+		goto failed;
+	}
+
+	out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+							out->cfg.rate,
+							out->cfg.frame_num, 1);
+
+	if (!out->resample_frame_num) {
+		error("frame num is too small to resample, discard it");
+		goto failed;
+	}
+
+	resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num;
+
+	out->resample_buf = malloc(resample_size);
+	if (!out->resample_buf) {
+		error("failed to allocate resample buffer for %u frames",
+						out->resample_frame_num);
+		goto failed;
+	}
+
+	DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+				out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+				out->resample_frame_num, resample_size);
+skip_resampler:
+	*stream_out = &out->stream;
+	adev->out = out;
+	sco_stream_out = out;
+
+	return 0;
+failed:
+	if (out->resampler)
+		release_resampler(out->resampler);
+
+	free(out->cache);
+	free(out->downmix_buf);
+	free(out);
+	*stream_out = NULL;
+	adev->out = NULL;
+	sco_stream_out = NULL;
+
+	return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int sco_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out,
+					const char *address)
+{
+	return  sco_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, address);
+}
+#else
+static int sco_open_output_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					audio_output_flags_t flags,
+					struct audio_config *config,
+					struct audio_stream_out **stream_out)
+{
+	return sco_open_output_stream_real(dev, handle, devices, flags,
+						config, stream_out, NULL);
+}
+#endif
+
+static void sco_close_output_stream(struct audio_hw_device *dev,
+					struct audio_stream_out *stream_out)
+{
+	struct sco_dev *sco_dev = (struct sco_dev *) dev;
+	struct sco_stream_out *out = (struct sco_stream_out *) stream_out;
+
+	DBG("dev %p stream %p fd %d", dev, out, sco_fd);
+
+	if (out->resampler) {
+		release_resampler(out->resampler);
+		free(out->resample_buf);
+	}
+
+	free(out->cache);
+	free(out->downmix_buf);
+	free(out);
+	sco_dev->out = NULL;
+
+	pthread_mutex_lock(&sco_mutex);
+
+	sco_stream_out = NULL;
+
+	if (!sco_stream_in)
+		sco_close_socket();
+
+	pthread_mutex_unlock(&sco_mutex);
+}
+
+static int sco_set_parameters(struct audio_hw_device *dev,
+							const char *kvpairs)
+{
+	DBG("%s", kvpairs);
+
+	return 0;
+}
+
+static char *sco_get_parameters(const struct audio_hw_device *dev,
+							const char *keys)
+{
+	DBG("");
+
+	return strdup("");
+}
+
+static int sco_init_check(const struct audio_hw_device *dev)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int sco_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("%f", volume);
+
+	return 0;
+}
+
+static int sco_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+	DBG("%f", volume);
+
+	return 0;
+}
+
+static int sco_set_mode(struct audio_hw_device *dev, int mode)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev,
+					const struct audio_config *config)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+	DBG("rate %u", in->cfg.rate);
+
+	return in->cfg.rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+	DBG("rate %u", rate);
+
+	return 0;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	size_t size = audio_stream_in_frame_size(&in->stream) *
+							in->cfg.frame_num;
+#else
+	size_t size = audio_stream_frame_size(&in->stream.common) *
+							in->cfg.frame_num;
+#endif
+
+	/* buffer size without resampling */
+	if (in->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		size = 576;
+
+	DBG("buf size %zd", size);
+
+	return size;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+	DBG("channels num: %u", popcount(in->cfg.channels));
+
+	return in->cfg.channels;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+	DBG("format: %u", in->cfg.format);
+
+	return in->cfg.format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+	DBG("");
+
+	return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+	DBG("%s", kvpairs);
+
+	return 0;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+							const char *keys)
+{
+	DBG("");
+
+	return strdup("");
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+							effect_handle_t effect)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static bool read_data(struct sco_stream_in *in, char *buffer, size_t bytes)
+{
+	struct pollfd pfd;
+	size_t len, read_bytes = 0;
+
+	pfd.fd = sco_fd;
+	pfd.events = POLLIN | POLLHUP | POLLNVAL;
+
+	while (bytes > read_bytes) {
+		int ret;
+
+		/* poll for reading */
+		if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+			DBG("timeout fd %d", sco_fd);
+			return false;
+		}
+
+		if (pfd.revents & (POLLHUP | POLLNVAL)) {
+			error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+			return false;
+		}
+
+		len = bytes - read_bytes > sco_mtu ? sco_mtu :
+							bytes - read_bytes;
+
+		ret = read(sco_fd, buffer + read_bytes, len);
+		if (ret > 0) {
+			read_bytes += ret;
+			DBG("read %d total %zd", ret, read_bytes);
+			continue;
+		}
+
+		if (errno == EAGAIN) {
+			ret = errno;
+			warn("read failed (%d)", ret);
+			continue;
+		}
+
+		if (errno != EINTR) {
+			ret = errno;
+			error("read failed (%d) fd %d bytes %zd", ret, sco_fd,
+									bytes);
+			return false;
+		}
+	}
+
+	DBG("read %zd bytes", read_bytes);
+
+	return true;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+								size_t bytes)
+{
+	struct sco_stream_in *in = (struct sco_stream_in *) stream;
+	size_t frame_size, frame_num, input_frame_num;
+	void *read_buf = buffer;
+	size_t total = bytes;
+	int ret;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	frame_size = audio_stream_in_frame_size(&in->stream);
+#else
+	frame_size = audio_stream_frame_size(&stream->common);
+#endif
+
+	if (!frame_size)
+		return -1;
+
+	frame_num = bytes / frame_size;
+	input_frame_num = frame_num;
+
+	DBG("Read from fd %d bytes %zu", sco_fd, bytes);
+
+	if (ipc_get_sco_fd(&in->bd_addr) != SCO_STATUS_SUCCESS)
+		return -1;
+
+	if (!in->resampler && in->cfg.rate != AUDIO_STREAM_SCO_RATE) {
+		error("Cannot find resampler");
+		return -1;
+	}
+
+	if (in->resampler) {
+		input_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+							in->cfg.rate,
+							frame_num, 0);
+		if (input_frame_num > in->resample_frame_num) {
+			DBG("resize input frames from %zd to %d",
+				input_frame_num, in->resample_frame_num);
+			input_frame_num = in->resample_frame_num;
+		}
+
+		read_buf = in->resample_buf;
+
+		total = input_frame_num * sizeof(int16_t) * 1;
+	}
+
+	if(!read_data(in, read_buf, total))
+		return -1;
+
+	if (in->resampler) {
+		ret = in->resampler->resample_from_input(in->resampler,
+							in->resample_buf,
+							&input_frame_num,
+							(int16_t *) buffer,
+							&frame_num);
+		if (ret) {
+			error("Failed to resample frames: %zd input %zd (%s)",
+					frame_num, input_frame_num,
+					strerror(ret));
+			return -1;
+		}
+
+		DBG("resampler: remain %zd output %zd frames", input_frame_num,
+								frame_num);
+	}
+
+	return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+	DBG("");
+
+	return -ENOSYS;
+}
+
+static int sco_open_input_stream_real(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	struct sco_dev *sco_dev = (struct sco_dev *) dev;
+	struct sco_stream_in *in;
+	int chan_num, ret;
+	size_t resample_size;
+
+	DBG("config %p device flags 0x%02x", config, devices);
+
+	if (sco_stream_in) {
+		DBG("stream_in already open");
+		ret = -EIO;
+		goto failed2;
+	}
+
+	in = calloc(1, sizeof(struct sco_stream_in));
+	if (!in)
+		return -ENOMEM;
+
+	DBG("stream %p sco fd %d mtu %u", in, sco_fd, sco_mtu);
+
+	in->stream.common.get_sample_rate = in_get_sample_rate;
+	in->stream.common.set_sample_rate = in_set_sample_rate;
+	in->stream.common.get_buffer_size = in_get_buffer_size;
+	in->stream.common.get_channels = in_get_channels;
+	in->stream.common.get_format = in_get_format;
+	in->stream.common.set_format = in_set_format;
+	in->stream.common.standby = in_standby;
+	in->stream.common.dump = in_dump;
+	in->stream.common.set_parameters = in_set_parameters;
+	in->stream.common.get_parameters = in_get_parameters;
+	in->stream.common.add_audio_effect = in_add_audio_effect;
+	in->stream.common.remove_audio_effect = in_remove_audio_effect;
+	in->stream.set_gain = in_set_gain;
+	in->stream.read = in_read;
+	in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	if (address) {
+		DBG("address %s", address);
+
+		str2bt_bdaddr_t(address, &in->bd_addr);
+	}
+#endif
+
+	if (config) {
+		DBG("config: rate %u chan mask %x format %d offload %p",
+				config->sample_rate, config->channel_mask,
+				config->format, &config->offload_info);
+
+		in->cfg.format = config->format;
+		in->cfg.channels = config->channel_mask;
+		in->cfg.rate = config->sample_rate;
+	} else {
+		in->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+		in->cfg.channels = AUDIO_CHANNEL_OUT_MONO;
+		in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+	}
+
+	in->cfg.frame_num = IN_STREAM_FRAMES;
+
+	if (in->cfg.rate == AUDIO_STREAM_SCO_RATE)
+		goto skip_resampler;
+
+	/* Channel numbers for resampler */
+	chan_num = 1;
+
+	ret = create_resampler(AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+						RESAMPLER_QUALITY_DEFAULT, NULL,
+						&in->resampler);
+	if (ret) {
+		error("Failed to create resampler (%s)", strerror(-ret));
+		goto failed;
+	}
+
+	in->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+							in->cfg.rate,
+							in->cfg.frame_num, 0);
+
+	resample_size = sizeof(int16_t) * chan_num * in->resample_frame_num;
+
+	in->resample_buf = malloc(resample_size);
+	if (!in->resample_buf) {
+		error("failed to allocate resample buffer for %d frames",
+							in->resample_frame_num);
+		goto failed;
+	}
+
+	DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+				AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+				in->resample_frame_num, resample_size);
+skip_resampler:
+	*stream_in = &in->stream;
+	sco_dev->in = in;
+	sco_stream_in = in;
+
+	return 0;
+failed:
+	if (in->resampler)
+		release_resampler(in->resampler);
+	free(in);
+failed2:
+	*stream_in = NULL;
+	sco_dev->in = NULL;
+	sco_stream_in = NULL;
+
+	return ret;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int sco_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in,
+					audio_input_flags_t flags,
+					const char *address,
+					audio_source_t source)
+{
+	return sco_open_input_stream_real(dev, handle, devices, config,
+						stream_in, flags, address,
+						source);
+}
+#else
+static int sco_open_input_stream(struct audio_hw_device *dev,
+					audio_io_handle_t handle,
+					audio_devices_t devices,
+					struct audio_config *config,
+					struct audio_stream_in **stream_in)
+{
+	return sco_open_input_stream_real(dev, handle, devices, config,
+						stream_in, 0, NULL, 0);
+}
+#endif
+
+static void sco_close_input_stream(struct audio_hw_device *dev,
+					struct audio_stream_in *stream_in)
+{
+	struct sco_dev *sco_dev = (struct sco_dev *) dev;
+	struct sco_stream_in *in = (struct sco_stream_in *) stream_in;
+
+	DBG("dev %p stream %p fd %d", dev, in, sco_fd);
+
+	if (in->resampler) {
+		release_resampler(in->resampler);
+		free(in->resample_buf);
+	}
+
+	free(in);
+	sco_dev->in = NULL;
+
+	pthread_mutex_lock(&sco_mutex);
+
+	sco_stream_in = NULL;
+
+	if (!sco_stream_out)
+		sco_close_socket();
+
+	pthread_mutex_unlock(&sco_mutex);
+}
+
+static int sco_dump(const audio_hw_device_t *device, int fd)
+{
+	DBG("");
+
+	return 0;
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static int set_master_mute(struct audio_hw_device *dev, bool mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_master_mute(struct audio_hw_device *dev, bool *mute)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int create_audio_patch(struct audio_hw_device *dev,
+					unsigned int num_sources,
+					const struct audio_port_config *sources,
+					unsigned int num_sinks,
+					const struct audio_port_config *sinks,
+					audio_patch_handle_t *handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int release_audio_patch(struct audio_hw_device *dev,
+					audio_patch_handle_t handle)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+	DBG("");
+	return -ENOSYS;
+}
+
+static int set_audio_port_config(struct audio_hw_device *dev,
+					const struct audio_port_config *config)
+{
+	DBG("");
+	return -ENOSYS;
+}
+#endif
+
+static int sco_close(hw_device_t *device)
+{
+	DBG("");
+
+	free(device);
+
+	return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+	bool done = false;
+	struct pollfd pfd;
+	int sk;
+
+	DBG("");
+
+	while (!done) {
+		DBG("Waiting for connection ...");
+
+		sk = accept(listen_sk, NULL, NULL);
+		if (sk < 0) {
+			int err = errno;
+
+			if (err == EINTR)
+				continue;
+
+			if (err != ECONNABORTED && err != EINVAL)
+				error("sco: Failed to accept socket: %d (%s)",
+							err, strerror(err));
+
+			break;
+		}
+
+		pthread_mutex_lock(&sk_mutex);
+		ipc_sk = sk;
+		pthread_mutex_unlock(&sk_mutex);
+
+		DBG("SCO IPC: Connected");
+
+		memset(&pfd, 0, sizeof(pfd));
+		pfd.fd = ipc_sk;
+		pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+		/* Check if socket is still alive. Empty while loop.*/
+		while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+		info("SCO HAL: Socket closed");
+
+		pthread_mutex_lock(&sk_mutex);
+		close(ipc_sk);
+		ipc_sk = -1;
+		pthread_mutex_unlock(&sk_mutex);
+	}
+
+	info("Closing SCO IPC thread");
+	return NULL;
+}
+
+static int sco_ipc_init(void)
+{
+	struct sockaddr_un addr;
+	int err;
+	int sk;
+
+	DBG("");
+
+	sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = -errno;
+		error("sco: Failed to create socket: %d (%s)", -err,
+								strerror(-err));
+		return err;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = -errno;
+		error("sco: Failed to bind socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	if (listen(sk, 1) < 0) {
+		err = -errno;
+		error("sco: Failed to listen on the socket: %d (%s)", -err,
+								strerror(-err));
+		goto failed;
+	}
+
+	listen_sk = sk;
+
+	err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+	if (err) {
+		err = -err;
+		ipc_th = 0;
+		error("sco: Failed to start IPC thread: %d (%s)",
+							-err, strerror(-err));
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	close(sk);
+	return err;
+}
+
+static int sco_open(const hw_module_t *module, const char *name,
+							hw_device_t **device)
+{
+	struct sco_dev *dev;
+	int err;
+
+	DBG("");
+
+	if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+		error("SCO: interface %s not matching [%s]", name,
+						AUDIO_HARDWARE_INTERFACE);
+		return -EINVAL;
+	}
+
+	err = sco_ipc_init();
+	if (err < 0)
+		return err;
+
+	dev = calloc(1, sizeof(struct sco_dev));
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+	dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+	dev->dev.common.module = (struct hw_module_t *) module;
+	dev->dev.common.close = sco_close;
+
+	dev->dev.init_check = sco_init_check;
+	dev->dev.set_voice_volume = sco_set_voice_volume;
+	dev->dev.set_master_volume = sco_set_master_volume;
+	dev->dev.set_mode = sco_set_mode;
+	dev->dev.set_mic_mute = sco_set_mic_mute;
+	dev->dev.get_mic_mute = sco_get_mic_mute;
+	dev->dev.set_parameters = sco_set_parameters;
+	dev->dev.get_parameters = sco_get_parameters;
+	dev->dev.get_input_buffer_size = sco_get_input_buffer_size;
+	dev->dev.open_output_stream = sco_open_output_stream;
+	dev->dev.close_output_stream = sco_close_output_stream;
+	dev->dev.open_input_stream = sco_open_input_stream;
+	dev->dev.close_input_stream = sco_close_input_stream;
+	dev->dev.dump = sco_dump;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	dev->dev.set_master_mute = set_master_mute;
+	dev->dev.get_master_mute = get_master_mute;
+	dev->dev.create_audio_patch = create_audio_patch;
+	dev->dev.release_audio_patch = release_audio_patch;
+	dev->dev.get_audio_port = get_audio_port;
+	dev->dev.set_audio_port_config = set_audio_port_config;
+#endif
+
+	*device = &dev->dev.common;
+
+	return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+	.open = sco_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+	.common = {
+		.tag = HARDWARE_MODULE_TAG,
+		.version_major = 1,
+		.version_minor = 0,
+		.id = AUDIO_HARDWARE_MODULE_ID,
+		.name = "SCO Audio HW HAL",
+		.author = "Intel Corporation",
+		.methods = &hal_module_methods,
+	},
+};
diff --git a/bluez/repo/android/hal-socket.c b/bluez/repo/android/hal-socket.c
new file mode 100644
index 0000000..cfd50d1
--- /dev/null
+++ b/bluez/repo/android/hal-socket.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hal-ipc.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "hal-utils.h"
+#include "hal.h"
+
+static bt_status_t socket_listen(btsock_type_t type, const char *service_name,
+					const uint8_t *uuid, int chan,
+					int *sock, int flags)
+{
+	struct hal_cmd_socket_listen cmd;
+
+	if (!sock)
+		return BT_STATUS_PARM_INVALID;
+
+	DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x",
+		btuuid2str(uuid), chan, sock, type, service_name, flags);
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	/* type match IPC type */
+	cmd.type = type;
+	cmd.flags = flags;
+	cmd.channel = chan;
+
+	if (uuid)
+		memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+	if (service_name)
+		memcpy(cmd.name, service_name, strlen(service_name));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+				sizeof(cmd), &cmd, NULL, NULL, sock);
+}
+
+static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
+					const uint8_t *uuid, int chan,
+					int *sock, int flags)
+{
+	struct hal_cmd_socket_connect cmd;
+
+	if (!sock)
+		return BT_STATUS_PARM_INVALID;
+
+	DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x",
+		bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags);
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	/* type match IPC type */
+	cmd.type = type;
+	cmd.flags = flags;
+	cmd.channel = chan;
+
+	if (uuid)
+		memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+	if (bdaddr)
+		memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr));
+
+	return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+					sizeof(cmd), &cmd, NULL, NULL, sock);
+}
+
+static btsock_interface_t socket_if = {
+	sizeof(socket_if),
+	socket_listen,
+	socket_connect
+};
+
+btsock_interface_t *bt_get_socket_interface(void)
+{
+	return &socket_if;
+}
diff --git a/bluez/repo/android/hal-utils.c b/bluez/repo/android/hal-utils.c
new file mode 100644
index 0000000..e45f6e4
--- /dev/null
+++ b/bluez/repo/android/hal-utils.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <cutils/properties.h>
+
+#include "hal.h"
+#include "hal-utils.h"
+
+/*
+ * converts uuid to string
+ * buf should be at least 39 bytes
+ *
+ * returns string representation of uuid
+ */
+const char *bt_uuid_t2str(const uint8_t *uuid, char *buf)
+{
+	int shift = 0;
+	unsigned int i;
+	int is_bt;
+
+	if (!uuid)
+		return strcpy(buf, "NULL");
+
+	is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4);
+
+	for (i = 0; i < HAL_UUID_LEN; i++) {
+		if (i == 4 && is_bt)
+			break;
+
+		if (i == 4 || i == 6 || i == 8 || i == 10) {
+			buf[i * 2 + shift] = '-';
+			shift++;
+		}
+		sprintf(buf + i * 2 + shift, "%02x", uuid[i]);
+	}
+
+	return buf;
+}
+
+const char *btuuid2str(const uint8_t *uuid)
+{
+	static char buf[MAX_UUID_STR_LEN];
+
+	return bt_uuid_t2str(uuid, buf);
+}
+
+INTMAP(bt_status_t, -1, "(unknown)")
+	DELEMENT(BT_STATUS_SUCCESS),
+	DELEMENT(BT_STATUS_FAIL),
+	DELEMENT(BT_STATUS_NOT_READY),
+	DELEMENT(BT_STATUS_NOMEM),
+	DELEMENT(BT_STATUS_BUSY),
+	DELEMENT(BT_STATUS_DONE),
+	DELEMENT(BT_STATUS_UNSUPPORTED),
+	DELEMENT(BT_STATUS_PARM_INVALID),
+	DELEMENT(BT_STATUS_UNHANDLED),
+	DELEMENT(BT_STATUS_AUTH_FAILURE),
+	DELEMENT(BT_STATUS_RMT_DEV_DOWN),
+ENDMAP
+
+INTMAP(bt_state_t, -1, "(unknown)")
+	DELEMENT(BT_STATE_OFF),
+	DELEMENT(BT_STATE_ON),
+ENDMAP
+
+INTMAP(bt_device_type_t, -1, "(unknown)")
+	DELEMENT(BT_DEVICE_DEVTYPE_BREDR),
+	DELEMENT(BT_DEVICE_DEVTYPE_BLE),
+	DELEMENT(BT_DEVICE_DEVTYPE_DUAL),
+ENDMAP
+
+INTMAP(bt_scan_mode_t, -1, "(unknown)")
+	DELEMENT(BT_SCAN_MODE_NONE),
+	DELEMENT(BT_SCAN_MODE_CONNECTABLE),
+	DELEMENT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE),
+ENDMAP
+
+INTMAP(bt_discovery_state_t, -1, "(unknown)")
+	DELEMENT(BT_DISCOVERY_STOPPED),
+	DELEMENT(BT_DISCOVERY_STARTED),
+ENDMAP
+
+INTMAP(bt_acl_state_t, -1, "(unknown)")
+	DELEMENT(BT_ACL_STATE_CONNECTED),
+	DELEMENT(BT_ACL_STATE_DISCONNECTED),
+ENDMAP
+
+INTMAP(bt_bond_state_t, -1, "(unknown)")
+	DELEMENT(BT_BOND_STATE_NONE),
+	DELEMENT(BT_BOND_STATE_BONDING),
+	DELEMENT(BT_BOND_STATE_BONDED),
+ENDMAP
+
+INTMAP(bt_ssp_variant_t, -1, "(unknown)")
+	DELEMENT(BT_SSP_VARIANT_PASSKEY_CONFIRMATION),
+	DELEMENT(BT_SSP_VARIANT_PASSKEY_ENTRY),
+	DELEMENT(BT_SSP_VARIANT_CONSENT),
+	DELEMENT(BT_SSP_VARIANT_PASSKEY_NOTIFICATION),
+ENDMAP
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+INTMAP(bt_property_type_t, -1, "(unknown)")
+	DELEMENT(BT_PROPERTY_BDNAME),
+	DELEMENT(BT_PROPERTY_BDADDR),
+	DELEMENT(BT_PROPERTY_UUIDS),
+	DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_SERVICE_RECORD),
+	DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE),
+	DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES),
+	DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
+	DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
+	DELEMENT(BT_PROPERTY_REMOTE_RSSI),
+	DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
+	DELEMENT(BT_PROPERTY_LOCAL_LE_FEATURES),
+	DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
+ENDMAP
+#else
+INTMAP(bt_property_type_t, -1, "(unknown)")
+	DELEMENT(BT_PROPERTY_BDNAME),
+	DELEMENT(BT_PROPERTY_BDADDR),
+	DELEMENT(BT_PROPERTY_UUIDS),
+	DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE),
+	DELEMENT(BT_PROPERTY_SERVICE_RECORD),
+	DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE),
+	DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES),
+	DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
+	DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
+	DELEMENT(BT_PROPERTY_REMOTE_RSSI),
+	DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
+	DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
+ENDMAP
+#endif
+
+INTMAP(bt_cb_thread_evt, -1, "(unknown)")
+	DELEMENT(ASSOCIATE_JVM),
+	DELEMENT(DISASSOCIATE_JVM),
+ENDMAP
+
+/* Find first index of given value in table m */
+int int2str_findint(int v, const struct int2str m[])
+{
+	int i;
+
+	for (i = 0; m[i].str; ++i) {
+		if (m[i].val == v)
+			return i;
+	}
+	return -1;
+}
+
+/* Find first index of given string in table m */
+int int2str_findstr(const char *str, const struct int2str m[])
+{
+	int i;
+
+	for (i = 0; m[i].str; ++i) {
+		if (strcmp(m[i].str, str) == 0)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * convert bd_addr to string
+ * buf must be at least 18 char long
+ *
+ * returns buf
+ */
+const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf)
+{
+	const uint8_t *p;
+
+	if (!bd_addr)
+		return strcpy(buf, "NULL");
+
+	p = bd_addr->address;
+
+	snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x",
+					p[0], p[1], p[2], p[3], p[4], p[5]);
+
+	return buf;
+}
+
+/* converts string to bt_bdaddr_t */
+void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr)
+{
+	uint8_t *p = bd_addr->address;
+
+	sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+				&p[0], &p[1], &p[2], &p[3], &p[4], &p[5]);
+}
+
+/* converts string to uuid */
+void str2bt_uuid_t(const char *str, bt_uuid_t *uuid)
+{
+	int i = 0;
+
+	memcpy(uuid, BT_BASE_UUID, sizeof(bt_uuid_t));
+
+	while (*str && i < (int) sizeof(bt_uuid_t)) {
+		while (*str == '-')
+			str++;
+
+		if (sscanf(str, "%02hhx", &uuid->uu[i]) != 1)
+			break;
+
+		i++;
+		str += 2;
+	}
+}
+
+const char *enum_defines(void *v, int i)
+{
+	const struct int2str *m = v;
+
+	return m[i].str != NULL ? m[i].str : NULL;
+}
+
+const char *enum_strings(void *v, int i)
+{
+	const char **m = v;
+
+	return m[i] != NULL ? m[i] : NULL;
+}
+
+const char *enum_one_string(void *v, int i)
+{
+	const char *m = v;
+
+	return (i == 0) && (m[0] != 0) ? m : NULL;
+}
+
+const char *bdaddr2str(const bt_bdaddr_t *bd_addr)
+{
+	static char buf[MAX_ADDR_STR_LEN];
+
+	return bt_bdaddr_t2str(bd_addr, buf);
+}
+
+static void bonded_devices2string(char *str, void *prop, int prop_len)
+{
+	int count = prop_len / sizeof(bt_bdaddr_t);
+	bt_bdaddr_t *addr = prop;
+
+	strcat(str, "{");
+
+	while (count--) {
+		strcat(str, bdaddr2str(addr));
+		if (count)
+			strcat(str, ", ");
+		addr++;
+	}
+
+	strcat(str, "}");
+}
+
+static void uuids2string(char *str, void *prop, int prop_len)
+{
+	int count = prop_len / sizeof(bt_uuid_t);
+	bt_uuid_t *uuid = prop;
+
+	strcat(str, "{");
+
+	while (count--) {
+		strcat(str, btuuid2str(uuid->uu));
+		if (count)
+			strcat(str, ", ");
+		uuid++;
+	}
+
+	strcat(str, "}");
+}
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+static void local_le_feat2string(char *str, const bt_local_le_features_t *f)
+{
+	uint16_t scan_num;
+
+	str += sprintf(str, "{\n");
+
+	str += sprintf(str, "Privacy supported: %s,\n",
+				f->local_privacy_enabled ? "TRUE" : "FALSE");
+
+	str += sprintf(str, "Num of advertising instances: %u,\n",
+							f->max_adv_instance);
+
+	str += sprintf(str, "PRA offloading support: %s,\n",
+				f->rpa_offload_supported ? "TRUE" : "FALSE");
+
+	str += sprintf(str, "Num of offloaded IRKs: %u,\n",
+							f->max_irk_list_size);
+
+	str += sprintf(str, "Num of offloaded scan filters: %u,\n",
+						f->max_adv_filter_supported);
+
+	scan_num = (f->scan_result_storage_size_hibyte << 8) +
+					f->scan_result_storage_size_lobyte;
+
+	str += sprintf(str, "Num of offloaded scan results: %u,\n", scan_num);
+
+	str += sprintf(str, "Activity & energy report support: %s\n",
+			f->activity_energy_info_supported ? "TRUE" : "FALSE");
+
+	sprintf(str, "}");
+}
+#endif
+
+const char *btproperty2str(const bt_property_t *property)
+{
+	bt_service_record_t *rec;
+	static char buf[4096];
+	char *p;
+
+	p = buf + sprintf(buf, "type=%s len=%d val=",
+					bt_property_type_t2str(property->type),
+					property->len);
+
+	switch (property->type) {
+	case BT_PROPERTY_BDNAME:
+	case BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+		snprintf(p, property->len + 1, "%s",
+					((bt_bdname_t *) property->val)->name);
+		break;
+	case BT_PROPERTY_BDADDR:
+		sprintf(p, "%s", bdaddr2str((bt_bdaddr_t *) property->val));
+		break;
+	case BT_PROPERTY_CLASS_OF_DEVICE:
+		sprintf(p, "%06x", *((int *) property->val));
+		break;
+	case BT_PROPERTY_TYPE_OF_DEVICE:
+		sprintf(p, "%s", bt_device_type_t2str(
+					*((bt_device_type_t *) property->val)));
+		break;
+	case BT_PROPERTY_REMOTE_RSSI:
+		sprintf(p, "%d", *((char *) property->val));
+		break;
+	case BT_PROPERTY_ADAPTER_SCAN_MODE:
+		sprintf(p, "%s",
+			bt_scan_mode_t2str(*((bt_scan_mode_t *) property->val)));
+		break;
+	case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
+		sprintf(p, "%d", *((int *) property->val));
+		break;
+	case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
+		bonded_devices2string(p, property->val, property->len);
+		break;
+	case BT_PROPERTY_UUIDS:
+		uuids2string(p, property->val, property->len);
+		break;
+	case BT_PROPERTY_SERVICE_RECORD:
+		rec = property->val;
+		sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu),
+						rec->channel, rec->name);
+		break;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+	case BT_PROPERTY_LOCAL_LE_FEATURES:
+		local_le_feat2string(p, property->val);
+		break;
+#endif
+	case BT_PROPERTY_REMOTE_VERSION_INFO:
+	case BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP:
+	default:
+		sprintf(p, "%p", property->val);
+		break;
+	}
+
+	return buf;
+}
+
+#define PROP_PREFIX "persist.sys.bluetooth."
+#define PROP_PREFIX_RO "ro.bluetooth."
+
+int get_config(const char *config_key, char *value, const char *fallback)
+{
+	char key[PROPERTY_KEY_MAX];
+	int ret;
+
+	if (strlen(config_key) + sizeof(PROP_PREFIX) > sizeof(key))
+		return 0;
+
+	snprintf(key, sizeof(key), PROP_PREFIX"%s", config_key);
+
+	ret = property_get(key, value, "");
+	if (ret > 0)
+		return ret;
+
+	snprintf(key, sizeof(key), PROP_PREFIX_RO"%s", config_key);
+
+	ret = property_get(key, value, "");
+	if (ret > 0)
+		return ret;
+
+	if (!fallback)
+		return 0;
+
+	return property_get(fallback, value, "");
+}
diff --git a/bluez/repo/android/hal-utils.h b/bluez/repo/android/hal-utils.h
new file mode 100644
index 0000000..9c59948
--- /dev/null
+++ b/bluez/repo/android/hal-utils.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <endian.h>
+
+#include <hardware/bluetooth.h>
+
+#define MAX_UUID_STR_LEN	37
+#define HAL_UUID_LEN		16
+#define MAX_ADDR_STR_LEN	18
+
+static const char BT_BASE_UUID[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+};
+
+const char *bt_uuid_t2str(const uint8_t *uuid, char *buf);
+const char *btuuid2str(const uint8_t *uuid);
+const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf);
+void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr);
+void str2bt_uuid_t(const char *str, bt_uuid_t *uuid);
+const char *btproperty2str(const bt_property_t *property);
+const char *bdaddr2str(const bt_bdaddr_t *bd_addr);
+
+int get_config(const char *config_key, char *value, const char *fallback);
+
+/*
+ * Begin mapping section
+ *
+ * There are some mappings between integer values (enums) and strings
+ * to be presented to user. To make it easier to convert between those two
+ * set of macros is given. It is specially useful when we want to have
+ * strings that match constants from header files like:
+ *  BT_STATUS_SUCCESS (0) and corresponding "BT_STATUS_SUCCESS"
+ * Example of usage:
+ *
+ * INTMAP(int, -1, "invalid")
+ *   DELEMENT(BT_STATUS_SUCCESS)
+ *   DELEMENT(BT_STATUS_FAIL)
+ *   MELEMENT(123, "Some strange value")
+ * ENDMAP
+ *
+ * Just by doing this we have mapping table plus two functions:
+ *  int str2int(const char *str);
+ *  const char *int2str(int v);
+ *
+ * second argument to INTMAP specifies value to be returned from
+ * str2int function when there is not mapping for such number
+ * third argument specifies default value to be returned from int2str
+ *
+ * If same mapping is to be used in several source files put
+ * INTMAP in c file and DECINTMAP in h file.
+ *
+ * For mappings that are to be used in single file only
+ * use SINTMAP which will create the same but everything will be marked
+ * as static.
+ */
+
+struct int2str {
+	int val;		/* int value */
+	const char *str;	/* corresponding string */
+};
+
+int int2str_findint(int v, const struct int2str m[]);
+int int2str_findstr(const char *str, const struct int2str m[]);
+const char *enum_defines(void *v, int i);
+const char *enum_strings(void *v, int i);
+const char *enum_one_string(void *v, int i);
+
+#define TYPE_ENUM(type) ((void *) &__##type##2str[0])
+#define DECINTMAP(type) \
+extern struct int2str __##type##2str[]; \
+const char *type##2##str(type v); \
+type str##2##type(const char *str); \
+
+#define INTMAP(type, deft, defs) \
+const char *type##2##str(type v) \
+{ \
+	int i = int2str_findint((int) v, __##type##2str); \
+	return (i < 0) ? defs : __##type##2str[i].str; \
+} \
+type str##2##type(const char *str) \
+{ \
+	int i = int2str_findstr(str, __##type##2str); \
+	return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \
+} \
+struct int2str __##type##2str[] = {
+
+#define SINTMAP(type, deft, defs) \
+static struct int2str __##type##2str[]; \
+static inline const char *type##2##str(type v) \
+{ \
+	int i = int2str_findint((int) v, __##type##2str); \
+	return (i < 0) ? defs : __##type##2str[i].str; \
+} \
+static inline type str##2##type(const char *str) \
+{ \
+	int i = int2str_findstr(str, __##type##2str); \
+	return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \
+} \
+static struct int2str __##type##2str[] = {
+
+#define ENDMAP {0, NULL} };
+
+/* use this to generate string from header file constant */
+#define MELEMENT(v, s) {v, s}
+/* use this to have arbitrary mapping from int to string */
+#define DELEMENT(s) {s, #s}
+/* End of mapping section */
+
+DECINTMAP(bt_status_t);
+DECINTMAP(bt_state_t);
+DECINTMAP(bt_device_type_t);
+DECINTMAP(bt_scan_mode_t);
+DECINTMAP(bt_discovery_state_t);
+DECINTMAP(bt_acl_state_t);
+DECINTMAP(bt_bond_state_t);
+DECINTMAP(bt_ssp_variant_t);
+DECINTMAP(bt_property_type_t);
+DECINTMAP(bt_cb_thread_evt);
+
+static inline uint16_t get_le16(const void *src)
+{
+	const struct __attribute__((packed)) {
+		uint16_t le16;
+	} *p = src;
+
+	return le16toh(p->le16);
+}
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+	struct __attribute__((packed)) {
+		uint16_t le16;
+	} *p = dst;
+
+	p->le16 = htole16(val);
+}
diff --git a/bluez/repo/android/hal.h b/bluez/repo/android/hal.h
new file mode 100644
index 0000000..709c197
--- /dev/null
+++ b/bluez/repo/android/hal.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_rc.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+#include <hardware/bt_hl.h>
+
+#define PLATFORM_VER(a, b, c) ((a << 16) | ( b << 8) | (c))
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+#include <hardware/bt_hf_client.h>
+#include <hardware/bt_mce.h>
+#endif
+
+btsock_interface_t *bt_get_socket_interface(void);
+bthh_interface_t *bt_get_hidhost_interface(void);
+btpan_interface_t *bt_get_pan_interface(void);
+btav_interface_t *bt_get_a2dp_interface(void);
+btrc_interface_t *bt_get_avrcp_interface(void);
+bthf_interface_t *bt_get_handsfree_interface(void);
+btgatt_interface_t *bt_get_gatt_interface(void);
+bthl_interface_t *bt_get_health_interface(void);
+
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+btrc_ctrl_interface_t *bt_get_avrcp_ctrl_interface(void);
+bthf_client_interface_t *bt_get_hf_client_interface(void);
+btmce_interface_t *bt_get_map_client_interface(void);
+btav_interface_t *bt_get_a2dp_sink_interface(void);
+#endif
+
+void bt_thread_associate(void);
+void bt_thread_disassociate(void);
diff --git a/bluez/repo/android/handsfree-client.c b/bluez/repo/android/handsfree-client.c
new file mode 100644
index 0000000..65659b8
--- /dev/null
+++ b/bluez/repo/android/handsfree-client.c
@@ -0,0 +1,2204 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/hfp.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "ipc.h"
+#include "ipc-common.h"
+#include "src/log.h"
+#include "utils.h"
+
+#include "bluetooth.h"
+#include "hal-msg.h"
+#include "handsfree-client.h"
+#include "sco.h"
+
+#define HFP_HF_CHANNEL 7
+
+#define HFP_HF_FEAT_ECNR	0x00000001
+#define HFP_HF_FEAT_3WAY	0x00000002
+#define HFP_HF_FEAT_CLI		0x00000004
+#define HFP_HF_FEAT_VR		0x00000008
+#define HFP_HF_FEAT_RVC		0x00000010
+#define HFP_HF_FEAT_ECS		0x00000020
+#define HFP_HF_FEAT_ECC		0x00000040
+#define HFP_HF_FEAT_CODEC	0x00000080
+#define HFP_HF_FEAT_HF_IND	0x00000100
+#define HFP_HF_FEAT_ESCO_S4_T2	0x00000200
+
+#define HFP_AG_FEAT_3WAY	0x00000001
+#define HFP_AG_FEAT_ECNR	0x00000002
+#define HFP_AG_FEAT_VR		0x00000004
+#define HFP_AG_FEAT_INBAND	0x00000008
+#define HFP_AG_FEAT_VTAG	0x00000010
+#define HFP_AG_FEAT_REJ_CALL	0x00000020
+#define HFP_AG_FEAT_ECS		0x00000040
+#define HFP_AG_FEAT_ECC		0x00000080
+#define HFP_AG_FEAT_EXT_ERR	0x00000100
+#define HFP_AG_FEAT_CODEC	0x00000200
+
+#define HFP_HF_FEATURES (HFP_HF_FEAT_ECNR | HFP_HF_FEAT_3WAY |\
+				HFP_HF_FEAT_CLI | HFP_HF_FEAT_VR |\
+				HFP_HF_FEAT_RVC | HFP_HF_FEAT_ECS |\
+				HFP_HF_FEAT_ECC)
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+#define MAX_NUMBER_LEN 33
+#define MAX_OPERATOR_NAME_LEN 17
+
+enum hfp_indicator {
+	HFP_INDICATOR_SERVICE = 0,
+	HFP_INDICATOR_CALL,
+	HFP_INDICATOR_CALLSETUP,
+	HFP_INDICATOR_CALLHELD,
+	HFP_INDICATOR_SIGNAL,
+	HFP_INDICATOR_ROAM,
+	HFP_INDICATOR_BATTCHG,
+	HFP_INDICATOR_LAST
+};
+
+typedef void (*ciev_func_t)(uint8_t val);
+
+struct indicator {
+	uint8_t index;
+	uint32_t min;
+	uint32_t max;
+	uint32_t val;
+	ciev_func_t cb;
+};
+
+struct hfp_codec {
+	uint8_t type;
+	bool local_supported;
+	bool remote_supported;
+};
+
+struct device {
+	bdaddr_t bdaddr;
+	struct hfp_hf *hf;
+	uint8_t state;
+	uint8_t audio_state;
+
+	uint8_t negotiated_codec;
+	uint32_t features;
+	struct hfp_codec codecs[2];
+
+	struct indicator ag_ind[HFP_INDICATOR_LAST];
+
+	uint32_t chld_features;
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+	{ CODEC_ID_CVSD, true, false},
+	{ CODEC_ID_MSBC, false, false},
+};
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+
+static uint32_t hfp_hf_features = 0;
+static uint32_t hfp_hf_record_id = 0;
+static struct queue *devices = NULL;
+static GIOChannel *hfp_hf_server = NULL;
+
+static struct bt_sco *sco = NULL;
+
+static struct device *find_default_device(void)
+{
+	return queue_peek_head(devices);
+}
+
+static bool match_by_bdaddr(const void *data, const void *user_data)
+{
+	const bdaddr_t *addr1 = data;
+	const bdaddr_t *addr2 = user_data;
+
+	return !bacmp(addr1, addr2);
+}
+
+static struct device *find_device(const bdaddr_t *addr)
+{
+	return queue_find(devices, match_by_bdaddr, addr);
+}
+
+static void init_codecs(struct device *dev)
+{
+	memcpy(&dev->codecs, codecs_defaults, sizeof(dev->codecs));
+
+	if (hfp_hf_features & HFP_HF_FEAT_CODEC)
+		dev->codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static struct device *device_create(const bdaddr_t *bdaddr)
+{
+	struct device *dev;
+
+	dev = new0(struct device, 1);
+
+	bacpy(&dev->bdaddr, bdaddr);
+	dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED;
+	dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+
+	init_codecs(dev);
+
+	queue_push_tail(devices, dev);
+
+	return dev;
+}
+
+static struct device *get_device(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	dev = find_device(addr);
+	if (dev)
+		return dev;
+
+	/* We do support only one device as for now */
+	if (queue_isempty(devices))
+		return device_create(addr);
+
+	return NULL;
+}
+
+static void device_set_state(struct device *dev, uint8_t state)
+{
+	struct hal_ev_hf_client_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	memset(&ev, 0, sizeof(ev));
+
+	dev->state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ev.chld_feat = dev->chld_features;
+	ev.peer_feat = dev->features;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void device_destroy(struct device *dev)
+{
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTED);
+	queue_remove(devices, dev);
+
+	if (dev->hf)
+		hfp_hf_unref(dev->hf);
+
+	free(dev);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_disconnect *cmd = buf;
+	struct device *dev;
+	uint32_t status;
+	bdaddr_t bdaddr;
+	char addr[18];
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	ba2str(&bdaddr, addr);
+	DBG("Disconnect %s", addr);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->state == HAL_HF_CLIENT_CONN_STATE_DISCONNECTING) {
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	if (dev->state == HAL_HF_CLIENT_CONN_STATE_CONNECTING) {
+		device_destroy(dev);
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	status = hfp_hf_disconnect(dev->hf) ? HAL_STATUS_SUCCESS :
+							HAL_STATUS_FAILED;
+
+	if (status)
+		device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_DISCONNECTING);
+
+done:
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT, status);
+}
+
+static void set_audio_state(struct device *dev, uint8_t state)
+{
+	struct hal_ev_hf_client_audio_state ev;
+	char address[18];
+
+	if (dev->audio_state == state)
+		return;
+
+	dev->audio_state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s audio state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void bcc_cb(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	if (result != HFP_RESULT_OK)
+		set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
+static bool codec_negotiation_supported(struct device *dev)
+{
+	return (dev->features & HFP_AG_FEAT_CODEC) &&
+			(hfp_hf_features & HFP_HF_FEAT_CODEC);
+}
+
+static bool connect_sco(struct device *dev)
+{
+	if (codec_negotiation_supported(dev))
+		return hfp_hf_send_command(dev->hf, bcc_cb, dev,
+								"AT+BCC");
+
+	return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT);
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_connect_audio *cmd = (void *) buf;
+	struct device *dev;
+	uint8_t status;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED ||
+		dev->audio_state != HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+		error("hf-client: Cannot create SCO, check SLC or audio state");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (connect_sco(dev)) {
+		status = HAL_STATUS_SUCCESS;
+		set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_disconnect_audio *cmd = (void *) buf;
+	struct device *dev;
+	uint8_t status;
+	bdaddr_t bdaddr;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev ||
+		dev->audio_state == HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+		error("hf-client: Device not found or audio not connected");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	bt_sco_disconnect(sco);
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
+}
+
+static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct hal_ev_hf_client_command_complete ev;
+
+	DBG("");
+	memset(&ev, 0, sizeof(ev));
+
+	switch (result) {
+	case HFP_RESULT_OK:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
+		break;
+	case HFP_RESULT_NO_CARRIER:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
+		break;
+	case HFP_RESULT_ERROR:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+		break;
+	case HFP_RESULT_BUSY:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
+		break;
+	case HFP_RESULT_NO_ANSWER:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
+		break;
+	case HFP_RESULT_DELAYED:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
+		break;
+	case HFP_RESULT_BLACKLISTED:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
+		break;
+	case HFP_RESULT_CME_ERROR:
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
+		ev.cme = cme_err;
+		break;
+	case HFP_RESULT_CONNECT:
+	case HFP_RESULT_RING:
+	case HFP_RESULT_NO_DIALTONE:
+	default:
+		error("hf-client: Unknown error code %d", result);
+		ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+		break;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=1"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BVRA=0"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_volume_control *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	uint8_t vol;
+	bool ret;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/*
+	 * Volume is in the range 0-15. Make sure we send correct value
+	 * to remote device
+	 */
+	vol = cmd->volume > 15 ? 15 : cmd->volume;
+
+	switch (cmd->type) {
+	case HF_CLIENT_VOLUME_TYPE_SPEAKER:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+							"AT+VGS=%u", vol);
+		break;
+	case HF_CLIENT_VOLUME_TYPE_MIC:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+							"AT+VGM=%u", vol);
+		break;
+	default:
+		ret = false;
+		break;
+	}
+
+	status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+					status);
+}
+
+static void handle_dial(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_dial *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	bool ret;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->number_len)
+		goto failed;
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (cmd->number_len > 0) {
+		if (cmd->number[cmd->number_len - 1] != '\0')
+			goto failed;
+
+		DBG("Dialing %s", cmd->number);
+
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+							"ATD%s;", cmd->number);
+	} else {
+		DBG("Redialing");
+
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BLDN");
+	}
+
+	status =  ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+						HAL_OP_HF_CLIENT_DIAL, status);
+
+	return;
+
+failed:
+	error("Malformed number data, size (%u bytes), terminating", len);
+	raise(SIGTERM);
+}
+
+static void handle_dial_memory(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_dial_memory *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* For some reason location in BT HAL is int. Therefore that check */
+	if (cmd->location < 0) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL , "ATD>%d;",
+								cmd->location))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_DIAL_MEMORY, status);
+}
+
+static void handle_call_action(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_call_action *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+	bool ret;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	switch (cmd->action) {
+	case HAL_HF_CLIENT_ACTION_CHLD_0:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=0");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_1:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=1");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_2:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+							NULL, "AT+CHLD=2");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_3:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=3");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_4:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHLD=4");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_1x:
+		/* Index is int in BT HAL. Let's be paranoid here */
+		if (cmd->index <= 0)
+			ret = false;
+		else
+			ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+					NULL, "AT+CHLD=1%d", cmd->index);
+		break;
+	case HAL_HF_CLIENT_ACTION_CHLD_2x:
+		/* Index is int in BT HAL. Let's be paranoid here */
+		if (cmd->index <= 0)
+			ret = false;
+		else
+			ret = hfp_hf_send_command(dev->hf, cmd_complete_cb,
+					NULL, "AT+CHLD=2%d", cmd->index);
+		break;
+	case HAL_HF_CLIENT_ACTION_ATA:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+									"ATA");
+		break;
+	case HAL_HF_CLIENT_ACTION_CHUP:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+CHUP");
+		break;
+	case HAL_HF_CLIENT_ACTION_BRTH_0:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BTRH=0");
+		break;
+	case HAL_HF_CLIENT_ACTION_BRTH_1:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BTRH=1");
+		break;
+	case HAL_HF_CLIENT_ACTION_BRTH_2:
+		ret = hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+								"AT+BTRH=2");
+		break;
+	default:
+		error("hf-client: Unknown action %d", cmd->action);
+		ret = false;
+		break;
+	}
+
+	status = ret ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_CALL_ACTION, status);
+}
+
+static void handle_query_current_calls(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CLCC"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+					status);
+}
+
+static void handle_query_operator_name(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS?"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+					status);
+}
+
+static void handle_retrieve_subscr_info(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	DBG("");
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+CNUM"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+					status);
+}
+
+static void handle_send_dtmf(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hf_client_send_dtmf *cmd = buf;
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+VTS=%c",
+							(char) cmd->tone))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_SEND_DTMF, status);
+}
+
+static void handle_get_last_vc_tag_num(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	uint8_t status;
+
+	dev = find_default_device();
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+BINP=1"))
+		status = HAL_STATUS_SUCCESS;
+	else
+		status = HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM, status);
+}
+
+static void disconnect_watch(void *user_data)
+{
+	DBG("");
+
+	device_destroy(user_data);
+}
+
+static void slc_error(struct device *dev)
+{
+	error("hf-client: Could not create SLC - dropping connection");
+	hfp_hf_disconnect(dev->hf);
+}
+
+static void set_chld_feat(struct device *dev, char *feat)
+{
+	DBG(" %s", feat);
+
+	if (strcmp(feat, "0") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL;
+	else if (strcmp(feat, "1") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_ACC;
+	else if (strcmp(feat, "1x") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_REL_X;
+	else if (strcmp(feat, "2") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_HOLD_ACC;
+	else if (strcmp(feat, "2x") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_PRIV_X;
+	else if (strcmp(feat, "3") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE;
+	else if (strcmp(feat, "4") == 0)
+		dev->chld_features |= HAL_HF_CLIENT_CHLD_FEAT_MERGE_DETACH;
+}
+
+static void get_local_codecs_string(struct device *dev, char *buf,
+								uint8_t len)
+{
+	int i;
+	uint8_t offset;
+
+	memset(buf, 0, len);
+	offset = 0;
+
+	for (i = 0; i < CODECS_COUNT; i++) {
+		char c[8];
+		int l;
+
+		if (!dev->codecs[i].local_supported)
+			continue;
+
+		memset(c, 0, sizeof(c));
+
+		l = sprintf(c, "%d,", dev->codecs[i].type);
+
+		if (l > (len - offset - 1)) {
+			error("hf-client: Codecs cannot fit into buffer");
+			return;
+		}
+
+		strcat(&buf[offset], c);
+		offset += l;
+	}
+}
+
+static void bvra_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_vr_state ev;
+	unsigned int val;
+
+	if (!hfp_context_get_number(context, &val) || val > 1)
+		return;
+
+	ev.state = val ? HAL_HF_CLIENT_VR_STARTED : HAL_HF_CLIENT_VR_STOPPED;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev);
+}
+
+static void vgm_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_volume_changed ev;
+	unsigned int val;
+
+	if (!hfp_context_get_number(context, &val) || val > 15)
+		return;
+
+	ev.type = HF_CLIENT_VOLUME_TYPE_MIC;
+	ev.volume = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_VR_STATE, sizeof(ev), &ev);
+}
+
+static void vgs_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_volume_changed ev;
+	unsigned int val;
+
+	if (!hfp_context_get_number(context, &val) || val > 15)
+		return;
+
+	ev.type = HF_CLIENT_VOLUME_TYPE_SPEAKER;
+	ev.volume = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_CLIENT_VOLUME_CHANGED, sizeof(ev), &ev);
+}
+
+static void brth_cb(struct hfp_context *context, void *user_data)
+{
+	struct hal_ev_hf_client_response_and_hold_status ev;
+	unsigned int val;
+
+	DBG("");
+
+	if (!hfp_context_get_number(context, &val) ||
+			val > HAL_HF_CLIENT_RESP_AND_HOLD_STATUS_REJECT) {
+		error("hf-client: incorrect BTRH response ");
+		return;
+	}
+
+	ev.status = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_RESPONSE_AND_HOLD_STATUS,
+				sizeof(ev), &ev);
+}
+
+static void clcc_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_current_call *ev = (void *) buf;
+	unsigned int val;
+
+	DBG("");
+
+	memset(buf, 0, sizeof(buf));
+
+	if (!hfp_context_get_number(context, &val)) {
+		error("hf-client: Could not get index");
+		return;
+	}
+
+	ev->index = val;
+
+	if (!hfp_context_get_number(context, &val) ||
+				val > HAL_HF_CLIENT_DIRECTION_INCOMING) {
+		error("hf-client: Could not get direction");
+		return;
+	}
+
+	ev->direction = val;
+
+	if (!hfp_context_get_number(context, &val) ||
+			val > HAL_HF_CLIENT_CALL_STATE_HELD_BY_RESP_AND_HOLD) {
+		error("hf-client: Could not get callstate");
+		return;
+	}
+
+	ev->call_state = val;
+
+	/* Next field is MODE but Android is not interested in this. Skip it */
+	if (!hfp_context_get_number(context, &val)) {
+		error("hf-client: Could not get mode");
+		return;
+	}
+
+	if (!hfp_context_get_number(context, &val) || val > 1) {
+		error("hf-client: Could not get multiparty");
+		return;
+	}
+
+	ev->multiparty = val;
+
+	if (hfp_context_get_string(context, (char *) &ev->number[0],
+								MAX_NUMBER_LEN))
+		ev->number_len = strlen((char *) ev->number) + 1;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_CURRENT_CALL,
+					sizeof(*ev) + ev->number_len, ev);
+}
+
+static void ciev_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	unsigned int index, val;
+	int i;
+
+	DBG("");
+
+	if (!hfp_context_get_number(context, &index))
+		return;
+
+	if (!hfp_context_get_number(context, &val))
+		return;
+
+	for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+		if (dev->ag_ind[i].index != index)
+			continue;
+
+		if (dev->ag_ind[i].cb) {
+			dev->ag_ind[i].val = val;
+			dev->ag_ind[i].cb(val);
+			return;
+		}
+	}
+}
+
+static void cnum_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_subscriber_service_info *ev = (void *) buf;
+	unsigned int service;
+
+	DBG("");
+
+	/* Alpha field is empty string, just skip it */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_string(context, (char *) &ev->name[0],
+							MAX_NUMBER_LEN)) {
+		error("hf-client: Could not get number");
+		return;
+	}
+
+	ev->name_len = strlen((char *) &ev->name[0]) + 1;
+
+	/* Type is not used in Android */
+	hfp_context_skip_field(context);
+
+	/* Speed field is empty string, just skip it */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_number(context, &service))
+		return;
+
+	switch (service) {
+	case 4:
+		ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_VOICE;
+		break;
+	case 5:
+		ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_FAX;
+		break;
+	default:
+		ev->type = HAL_HF_CLIENT_SUBSCR_TYPE_UNKNOWN;
+		break;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_CLIENT_SUBSCRIBER_SERVICE_INFO,
+					sizeof(*ev) + ev->name_len, ev);
+}
+
+static void cops_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_operator_name *ev = (void *) buf;
+	unsigned int format;
+
+	DBG("");
+
+	/* Not interested in mode */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_number(context, &format))
+		return;
+
+	if (format != 0)
+		info("hf-client: Not correct string format in +COSP");
+
+	if (!hfp_context_get_string(context, (char *) &ev->name[0],
+						MAX_OPERATOR_NAME_LEN)) {
+		error("hf-client: incorrect COPS response");
+		return;
+	}
+
+	ev->name_len = strlen((char *) &ev->name[0]) + 1;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_OPERATOR_NAME,
+					sizeof(*ev) + ev->name_len, ev);
+}
+
+static void binp_cb(struct hfp_context *context, void *user_data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_hf_client_last_void_call_tag_num *ev = (void *) buf;
+	char number[33];
+
+	DBG("");
+
+	if (!hfp_context_get_string(context, number, sizeof(number))) {
+		error("hf-client: incorrect COPS response");
+		return;
+	}
+
+	ev->number_len = strlen(number) + 1;
+	memcpy(ev->number, number, ev->number_len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_CLIENT_LAST_VOICE_CALL_TAG_NUM,
+					sizeof(*ev) + ev->number_len, ev);
+}
+
+static bool is_codec_supported_localy(struct device *dev, uint8_t codec)
+{
+	int i;
+
+	for (i = 0; i < CODECS_COUNT; i++) {
+		if (dev->codecs[i].type != codec)
+			continue;
+
+		return dev->codecs[i].local_supported;
+	}
+
+	return false;
+}
+
+static void bcs_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	if (result != HFP_RESULT_OK)
+		error("hf-client: Error on AT+BCS (err=%u)", result);
+}
+
+static void bcs_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	unsigned int codec;
+	char codecs_string[8];
+
+	DBG("");
+
+	if (!hfp_context_get_number(context, &codec))
+		goto failed;
+
+	if (!is_codec_supported_localy(dev, codec))
+		goto failed;
+
+	dev->negotiated_codec = codec;
+
+	hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%u", codec);
+
+	return;
+
+failed:
+	error("hf-client: Could not get codec");
+
+	get_local_codecs_string(dev, codecs_string, sizeof(codecs_string));
+
+	hfp_hf_send_command(dev->hf, bcs_resp, dev, "AT+BCS=%s", codecs_string);
+}
+
+static void slc_completed(struct device *dev)
+{
+	int i;
+	struct indicator *ag_ind;
+
+	DBG("");
+
+	ag_ind = dev->ag_ind;
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED);
+
+	/* Notify Android with indicators */
+	for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+		if (!ag_ind[i].cb)
+			continue;
+
+		ag_ind[i].cb(ag_ind[i].val);
+	}
+
+	/* TODO: register unsolicited results handlers */
+
+	hfp_hf_register(dev->hf, bvra_cb, "+BRVA", dev, NULL);
+	hfp_hf_register(dev->hf, vgm_cb, "+VGM", dev, NULL);
+	hfp_hf_register(dev->hf, vgs_cb, "+VGS", dev, NULL);
+	hfp_hf_register(dev->hf, brth_cb, "+BTRH", dev, NULL);
+	hfp_hf_register(dev->hf, clcc_cb, "+CLCC", dev, NULL);
+	hfp_hf_register(dev->hf, ciev_cb, "+CIEV", dev, NULL);
+	hfp_hf_register(dev->hf, cops_cb, "+COPS", dev, NULL);
+	hfp_hf_register(dev->hf, cnum_cb, "+CNUM", dev, NULL);
+	hfp_hf_register(dev->hf, binp_cb, "+BINP", dev, NULL);
+	hfp_hf_register(dev->hf, bcs_cb, "+BCS", dev, NULL);
+
+	if (!hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL, "AT+COPS=3,0"))
+		info("hf-client: Could not send AT+COPS=3,0");
+}
+
+static void slc_chld_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	char feat[3];
+
+	if (!hfp_context_open_container(context))
+		goto failed;
+
+	while (hfp_context_get_unquoted_string(context, feat, sizeof(feat)))
+		set_chld_feat(dev, feat);
+
+	if (!hfp_context_close_container(context))
+		goto failed;
+
+	return;
+
+failed:
+	error("hf-client: Error on CHLD response");
+	slc_error(dev);
+}
+
+static void slc_chld_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	hfp_hf_unregister(dev->hf, "+CHLD");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CHLD error: %d", result);
+		slc_error(dev);
+		return;
+	}
+
+	slc_completed(dev);
+}
+
+static void slc_cmer_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CMER error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (!(dev->features & HFP_AG_FEAT_3WAY)) {
+		slc_completed(dev);
+		return;
+	}
+
+	if (!hfp_hf_register(dev->hf, slc_chld_cb, "+CHLD", dev, NULL)) {
+		error("hf-client: Could not register +CHLD");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_chld_resp, dev, "AT+CHLD=?")) {
+		error("hf-client: Could not send AT+CHLD");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static void set_indicator_value(uint8_t index, unsigned int val,
+						struct indicator *ag_ind)
+{
+	int i;
+
+	for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+		if (index != ag_ind[i].index)
+			continue;
+
+		ag_ind[i].val = val;
+		ag_ind[i].cb(val);
+		return;
+	}
+}
+
+static void slc_cind_status_cb(struct hfp_context *context,
+							void *user_data)
+{
+	struct device *dev = user_data;
+	uint8_t index = 1;
+
+	DBG("");
+
+	while (hfp_context_has_next(context)) {
+		uint32_t val;
+
+		if (!hfp_context_get_number(context, &val)) {
+			error("hf-client: Error on CIND status response");
+			return;
+		}
+
+		set_indicator_value(index++, val, dev->ag_ind);
+	}
+}
+
+static void slc_cind_status_resp(enum hfp_result result,
+							enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	hfp_hf_unregister(dev->hf, "+CIND");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CIND error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (!hfp_hf_send_command(dev->hf, slc_cmer_resp, dev,
+							"AT+CMER=3,0,0,1")) {
+		error("hf-client: Counld not send AT+CMER");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static void slc_cind_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	hfp_hf_unregister(dev->hf, "+CIND");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: CIND error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (!hfp_hf_register(dev->hf, slc_cind_status_cb, "+CIND", dev,
+								NULL)) {
+		error("hf-client: Counld not register +CIND");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_cind_status_resp, dev,
+								"AT+CIND?")) {
+		error("hf-client: Counld not send AT+CIND?");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static void ciev_service_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_net_state ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) {
+		error("hf-client: Incorrect state %u:", val);
+		return;
+	}
+
+	ev.state = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+				HAL_EV_HF_CLIENT_NET_STATE, sizeof(ev), &ev);
+}
+
+static void ciev_call_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_call_indicator ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_CALL_IND_CALL_IN_PROGERSS) {
+		error("hf-client: Incorrect call state %u:", val);
+		return;
+	}
+
+	ev.call = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_HF_CLIENT_CALL_INDICATOR, sizeof(ev), &ev);
+}
+
+static void ciev_callsetup_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_call_setup_indicator ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_CALL_SETUP_ALERTING) {
+		error("hf-client: Incorrect call setup state %u:", val);
+		return;
+	}
+
+	ev.call_setup = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_CALL_SETUP_INDICATOR,
+					sizeof(ev), &ev);
+}
+
+static void ciev_callheld_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_call_held_indicator ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_CALL_SETUP_IND_HOLD) {
+		error("hf-client: Incorrect call held state %u:", val);
+		return;
+	}
+
+	ev.call_held = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_CALL_HELD_INDICATOR,
+					sizeof(ev), &ev);
+}
+
+static void ciev_signal_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_net_signal_strength ev;
+
+	DBG("");
+
+	if (val > 5) {
+		error("hf-client: Incorrect signal value %u:", val);
+		return;
+	}
+
+	ev.signal_strength = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_NET_SIGNAL_STRENGTH,
+					sizeof(ev), &ev);
+}
+
+static void ciev_roam_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_net_roaming_type ev;
+
+	DBG("");
+
+	if (val > HAL_HF_CLIENT_NET_ROAMING_TYPE_ROAMING) {
+		error("hf-client: Incorrect roaming state %u:", val);
+		return;
+	}
+
+	ev.state = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_EV_HF_CLIENT_NET_ROAMING_TYPE,
+					sizeof(ev), &ev);
+}
+
+static void ciev_battchg_cb(uint8_t val)
+{
+	struct hal_ev_hf_client_battery_level ev;
+
+	DBG("");
+
+	if (val > 5) {
+		error("hf-client: Incorrect battery charge value %u:", val);
+		return;
+	}
+
+	ev.battery_level = val;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_EV_HF_CLIENT_BATTERY_LEVEL, sizeof(ev), &ev);
+}
+
+static void set_indicator_parameters(uint8_t index, const char *indicator,
+						unsigned int min,
+						unsigned int max,
+						struct indicator *ag_ind)
+{
+	DBG("%s, %i", indicator, index);
+
+	/* TODO: Verify min/max values ? */
+
+	if (strcmp("service", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_SERVICE].index = index;
+		ag_ind[HFP_INDICATOR_SERVICE].min = min;
+		ag_ind[HFP_INDICATOR_SERVICE].max = max;
+		ag_ind[HFP_INDICATOR_SERVICE].cb = ciev_service_cb;
+		return;
+	}
+
+	if (strcmp("call", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_CALL].index = index;
+		ag_ind[HFP_INDICATOR_CALL].min = min;
+		ag_ind[HFP_INDICATOR_CALL].max = max;
+		ag_ind[HFP_INDICATOR_CALL].cb = ciev_call_cb;
+		return;
+	}
+
+	if (strcmp("callsetup", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_CALLSETUP].index = index;
+		ag_ind[HFP_INDICATOR_CALLSETUP].min = min;
+		ag_ind[HFP_INDICATOR_CALLSETUP].max = max;
+		ag_ind[HFP_INDICATOR_CALLSETUP].cb = ciev_callsetup_cb;
+		return;
+	}
+
+	if (strcmp("callheld", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_CALLHELD].index = index;
+		ag_ind[HFP_INDICATOR_CALLHELD].min = min;
+		ag_ind[HFP_INDICATOR_CALLHELD].max = max;
+		ag_ind[HFP_INDICATOR_CALLHELD].cb = ciev_callheld_cb;
+		return;
+	}
+
+	if (strcmp("signal", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_SIGNAL].index = index;
+		ag_ind[HFP_INDICATOR_SIGNAL].min = min;
+		ag_ind[HFP_INDICATOR_SIGNAL].max = max;
+		ag_ind[HFP_INDICATOR_SIGNAL].cb = ciev_signal_cb;
+		return;
+	}
+
+	if (strcmp("roam", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_ROAM].index = index;
+		ag_ind[HFP_INDICATOR_ROAM].min = min;
+		ag_ind[HFP_INDICATOR_ROAM].max = max;
+		ag_ind[HFP_INDICATOR_ROAM].cb = ciev_roam_cb;
+		return;
+	}
+
+	if (strcmp("battchg", indicator) == 0) {
+		ag_ind[HFP_INDICATOR_BATTCHG].index = index;
+		ag_ind[HFP_INDICATOR_BATTCHG].min = min;
+		ag_ind[HFP_INDICATOR_BATTCHG].max = max;
+		ag_ind[HFP_INDICATOR_BATTCHG].cb = ciev_battchg_cb;
+		return;
+	}
+
+	error("hf-client: Unknown indicator: %s", indicator);
+}
+
+static void slc_cind_cb(struct hfp_context *context, void *user_data)
+{
+	struct device *dev = user_data;
+	int index = 1;
+
+	DBG("");
+
+	while (hfp_context_has_next(context)) {
+		char name[255];
+		unsigned int min, max;
+
+		/* e.g ("callsetup",(0-3)) */
+		if (!hfp_context_open_container(context))
+			break;
+
+		if (!hfp_context_get_string(context, name, sizeof(name))) {
+			error("hf-client: Could not get string");
+			goto failed;
+		}
+
+		if (!hfp_context_open_container(context)) {
+			error("hf-client: Could not open container");
+			goto failed;
+		}
+
+		if (!hfp_context_get_range(context, &min, &max)) {
+			if (!hfp_context_get_number(context, &min)) {
+				error("hf-client: Could not get number");
+				goto failed;
+			}
+
+			if (!hfp_context_get_number(context, &max)) {
+				error("hf-client: Could not get number");
+				goto failed;
+			}
+		}
+
+		if (!hfp_context_close_container(context)) {
+			error("hf-client: Could not close container");
+			goto failed;
+		}
+
+		if (!hfp_context_close_container(context)) {
+			error("hf-client: Could not close container");
+			goto failed;
+		}
+
+		set_indicator_parameters(index, name, min, max, dev->ag_ind);
+		index++;
+	}
+
+	return;
+
+failed:
+	error("hf-client: Error on CIND response");
+	slc_error(dev);
+}
+
+static void slc_bac_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (result != HFP_RESULT_OK)
+		goto failed;
+
+	/* Continue with SLC creation */
+	if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) {
+		error("hf-client: Could not register for +CIND");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?"))
+		goto failed;
+
+	return;
+
+failed:
+	error("hf-client: Error on BAC response");
+	slc_error(dev);
+}
+
+static bool send_supported_codecs(struct device *dev)
+{
+	char codecs_string[8];
+	char bac[16];
+
+	memset(bac, 0, sizeof(bac));
+
+	strcpy(bac, "AT+BAC=");
+
+	get_local_codecs_string(dev, codecs_string, sizeof(codecs_string));
+	strcat(bac, codecs_string);
+
+	return hfp_hf_send_command(dev->hf, slc_bac_resp, dev, bac);
+}
+
+static void slc_brsf_cb(struct hfp_context *context, void *user_data)
+{
+	unsigned int feat;
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (hfp_context_get_number(context, &feat))
+		dev->features = feat;
+}
+
+static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct device *dev = user_data;
+
+	hfp_hf_unregister(dev->hf, "+BRSF");
+
+	if (result != HFP_RESULT_OK) {
+		error("hf-client: BRSF error: %d", result);
+		goto failed;
+	}
+
+	/* Continue with SLC creation */
+	if (codec_negotiation_supported(dev)) {
+		if (send_supported_codecs(dev))
+			return;
+
+		error("hf-client: Could not send BAC command");
+		goto failed;
+	}
+
+	/* No WBS on remote side. Continue with indicators */
+	if (!hfp_hf_register(dev->hf, slc_cind_cb, "+CIND", dev, NULL)) {
+		error("hf-client: Could not register for +CIND");
+		goto failed;
+	}
+
+	if (!hfp_hf_send_command(dev->hf, slc_cind_resp, dev, "AT+CIND=?")) {
+		error("hf-client: Could not send AT+CIND command");
+		goto failed;
+	}
+
+	return;
+
+failed:
+	slc_error(dev);
+}
+
+static bool create_slc(struct device *dev)
+{
+	DBG("");
+
+	if (!hfp_hf_register(dev->hf, slc_brsf_cb, "+BRSF", dev, NULL))
+		return false;
+
+	return hfp_hf_send_command(dev->hf, slc_brsf_resp, dev, "AT+BRSF=%u",
+							hfp_hf_features);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct device *dev = user_data;
+
+	DBG("");
+
+	if (err) {
+		error("hf-client: connect failed (%s)", err->message);
+		goto failed;
+	}
+
+	dev->hf = hfp_hf_new(g_io_channel_unix_get_fd(chan));
+	if (!dev->hf) {
+		error("hf-client: Could not create hfp io");
+		goto failed;
+	}
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	hfp_hf_set_close_on_unref(dev->hf, true);
+	hfp_hf_set_disconnect_handler(dev->hf, disconnect_watch, dev, NULL);
+
+	if (!create_slc(dev)) {
+		error("hf-client: Could not start SLC creation");
+		hfp_hf_disconnect(dev->hf);
+		goto failed;
+	}
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTED);
+
+	return;
+
+failed:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+	device_destroy(dev);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	sdp_list_t *protos, *classes;
+	struct device *dev = data;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t uuid;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("hf-client: unable to get SDP record: %s",
+							strerror(-err));
+		goto failed;
+	}
+
+	if (!recs || !recs->data) {
+		info("hf-client: no HFP SDP records found");
+		goto failed;
+	}
+
+	if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
+		error("hf-client: unable to get service classes from record");
+		goto failed;
+	}
+
+	/* TODO read remote version? */
+
+	memcpy(&uuid, classes->data, sizeof(uuid));
+	sdp_list_free(classes, free);
+
+	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
+		error("hf-client: invalid service record or not HFP");
+		goto failed;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("hf-client: unable to get access protocols from record");
+		sdp_list_free(classes, free);
+		goto failed;
+	}
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("hf-client: unable to get RFCOMM channel from record");
+		goto failed;
+	}
+
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("hf-client: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto failed;
+	}
+
+	g_io_channel_unref(io);
+	return;
+
+failed:
+	device_destroy(dev);
+}
+
+static int sdp_search_hfp(struct device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+					sdp_hfp_search_cb, dev, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+	struct device *dev;
+	const struct hal_cmd_hf_client_connect *cmd = buf;
+	uint32_t status;
+	bdaddr_t bdaddr;
+	char addr[18];
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	ba2str(&bdaddr, addr);
+	DBG("connecting to %s", addr);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (sdp_search_hfp(dev) < 0) {
+		status = HAL_STATUS_FAILED;
+		device_destroy(dev);
+		goto done;
+	}
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+					HAL_OP_HF_CLIENT_CONNECT, status);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct device *dev;
+	char address[18];
+	bdaddr_t bdaddr;
+	GError *err = NULL;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_DEST_BDADDR, &bdaddr,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("hf-client: confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("Incoming connection from %s", address);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		error("hf-client: There is other AG connected");
+		goto drop;
+	}
+
+	if (dev->state != HAL_HF_CLIENT_CONN_STATE_DISCONNECTED) {
+		/* TODO: Handle colision */
+		error("hf-client: Connections is up or ongoing ?");
+		goto drop;
+	}
+
+	device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTING);
+
+	if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) {
+		error("hf-client: failed to accept connection");
+		device_destroy(dev);
+		goto drop;
+	}
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HF_CLIENT_CONNECT */
+	{ handle_connect, false,
+				sizeof(struct hal_cmd_hf_client_connect) },
+	/* HAL_OP_HF_CLIENT_DISCONNECT */
+	{ handle_disconnect, false,
+				sizeof(struct hal_cmd_hf_client_disconnect) },
+	/* HAL_OP_HF_CLIENT_CONNECT_AUDIO */
+	{ handle_connect_audio, false,
+			sizeof(struct hal_cmd_hf_client_connect_audio) },
+	/* HAL_OP_HF_CLIENT_DISCONNECT_AUDIO */
+	{ handle_disconnect_audio, false,
+			sizeof(struct hal_cmd_hf_client_disconnect_audio) },
+	/* define HAL_OP_HF_CLIENT_START_VR */
+	{ handle_start_vr, false, 0 },
+	/* define HAL_OP_HF_CLIENT_STOP_VR */
+	{ handle_stop_vr, false, 0 },
+	/* HAL_OP_HF_CLIENT_VOLUME_CONTROL */
+	{ handle_volume_control, false,
+			sizeof(struct hal_cmd_hf_client_volume_control) },
+	/* HAL_OP_HF_CLIENT_DIAL */
+	{ handle_dial, true, sizeof(struct hal_cmd_hf_client_dial) },
+	/* HAL_OP_HF_CLIENT_DIAL_MEMORY */
+	{ handle_dial_memory, false,
+				sizeof(struct hal_cmd_hf_client_dial_memory) },
+	/* HAL_OP_HF_CLIENT_CALL_ACTION */
+	{ handle_call_action, false,
+				sizeof(struct hal_cmd_hf_client_call_action) },
+	/* HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS */
+	{ handle_query_current_calls, false, 0 },
+	/* HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME */
+	{ handle_query_operator_name, false, 0 },
+	/* HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO */
+	{ handle_retrieve_subscr_info, false, 0 },
+	/* HAL_OP_HF_CLIENT_SEND_DTMF */
+	{ handle_send_dtmf, false,
+				sizeof(struct hal_cmd_hf_client_send_dtmf) },
+	/* HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM */
+	{ handle_get_last_vc_tag_num, false, 0 },
+};
+
+static sdp_record_t *hfp_hf_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel, *features;
+	uint16_t sdpfeat;
+	uint8_t ch = HFP_HF_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+	profile.version = 0x0106;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	/* Codec Negotiation bit in SDP feature is different then in BRSF */
+	sdpfeat = hfp_hf_features & 0x0000003F;
+	if (hfp_hf_features & HFP_HF_FEAT_CODEC)
+		sdpfeat |= 0x00000020;
+	else
+		sdpfeat &= ~0x00000020;
+
+	features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Hands-Free unit", NULL, NULL);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool enable_hf_client(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	hfp_hf_server =  bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HFP_HF_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hfp_hf_server) {
+		error("hf-client: Failed to listen on Handsfree rfcomm: %s",
+								err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	hfp_hf_features = HFP_HF_FEATURES;
+
+	rec = hfp_hf_record();
+	if (!rec) {
+		error("hf-client: Could not create service record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("hf-client: Failed to register service record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hfp_hf_record_id = rec->handle;
+
+	return true;
+
+failed:
+	g_io_channel_shutdown(hfp_hf_server, TRUE, NULL);
+	g_io_channel_unref(hfp_hf_server);
+	hfp_hf_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hfp_hf(void)
+{
+	if (hfp_hf_server) {
+		g_io_channel_shutdown(hfp_hf_server, TRUE, NULL);
+		g_io_channel_unref(hfp_hf_server);
+		hfp_hf_server = NULL;
+	}
+
+	if (hfp_hf_record_id > 0) {
+		bt_adapter_remove_record(hfp_hf_record_id);
+		hfp_hf_record_id = 0;
+	}
+
+	if (sco) {
+		bt_sco_unref(sco);
+		sco = NULL;
+	}
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+	struct device *dev;
+
+	DBG("");
+
+	dev = find_device(addr);
+	if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) {
+		error("hf-client: No device or SLC not ready");
+		return false;
+	}
+
+	set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+
+	if (codec_negotiation_supported(dev) &&
+			dev->negotiated_codec != CODEC_ID_CVSD)
+		*voice_settings = BT_VOICE_TRANSPARENT;
+	else
+		*voice_settings = BT_VOICE_CVSD_16BIT;
+
+	return true;
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+	struct device *dev;
+	uint8_t audio_state;
+
+	DBG("SCO Status %u", status);
+
+	/* Device shall be there, just sanity check */
+	dev = find_device(addr);
+	if (!dev) {
+		error("hf-client: There is no device?");
+		return;
+	}
+
+	if (status != SCO_STATUS_OK) {
+		audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+		goto done;
+	}
+
+	if (dev->negotiated_codec == CODEC_ID_MSBC)
+		audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
+	else
+		audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED;
+
+done:
+	set_audio_state(dev, audio_state);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+	struct device *dev;
+
+	DBG("");
+
+	dev = find_device(addr);
+	if (!dev) {
+		error("hf-client: No device");
+		return;
+	}
+
+	set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
+bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+	DBG("");
+
+	devices = queue_new();
+
+	bacpy(&adapter_addr, addr);
+
+	if (!enable_hf_client())
+		goto failed;
+
+	sco = bt_sco_new(addr);
+	if (!sco) {
+		error("hf-client: Cannot create SCO. HFP AG is in use ?");
+		goto failed;
+	}
+
+	bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+	bt_sco_set_connect_cb(sco, connect_sco_cb);
+	bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+
+failed:
+	cleanup_hfp_hf();
+	queue_destroy(devices, free);
+	devices = NULL;
+
+	return false;
+}
+
+void bt_hf_client_unregister(void)
+{
+	DBG("");
+
+	cleanup_hfp_hf();
+
+	queue_destroy(devices, (void *) device_destroy);
+	devices = NULL;
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+	hal_ipc = NULL;
+}
diff --git a/bluez/repo/android/handsfree-client.h b/bluez/repo/android/handsfree-client.h
new file mode 100644
index 0000000..1eb69ff
--- /dev/null
+++ b/bluez/repo/android/handsfree-client.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_hf_client_unregister(void);
diff --git a/bluez/repo/android/handsfree.c b/bluez/repo/android/handsfree.c
new file mode 100644
index 0000000..cb348ab
--- /dev/null
+++ b/bluez/repo/android/handsfree.c
@@ -0,0 +1,3031 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/shared/hfp.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "handsfree.h"
+#include "bluetooth.h"
+#include "src/log.h"
+#include "utils.h"
+#include "sco-msg.h"
+#include "sco.h"
+
+#define HSP_AG_CHANNEL 12
+#define HFP_AG_CHANNEL 13
+
+#define HFP_AG_FEAT_3WAY	0x00000001
+#define HFP_AG_FEAT_ECNR	0x00000002
+#define HFP_AG_FEAT_VR		0x00000004
+#define HFP_AG_FEAT_INBAND	0x00000008
+#define HFP_AG_FEAT_VTAG	0x00000010
+#define HFP_AG_FEAT_REJ_CALL	0x00000020
+#define HFP_AG_FEAT_ECS		0x00000040
+#define HFP_AG_FEAT_ECC		0x00000080
+#define HFP_AG_FEAT_EXT_ERR	0x00000100
+#define HFP_AG_FEAT_CODEC	0x00000200
+
+#define HFP_HF_FEAT_ECNR	0x00000001
+#define HFP_HF_FEAT_3WAY	0x00000002
+#define HFP_HF_FEAT_CLI		0x00000004
+#define HFP_HF_FEAT_VR		0x00000008
+#define HFP_HF_FEAT_RVC		0x00000010
+#define HFP_HF_FEAT_ECS		0x00000020
+#define HFP_HF_FEAT_ECC		0x00000040
+#define HFP_HF_FEAT_CODEC	0x00000080
+
+#define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\
+				HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\
+				HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR)
+
+#define HFP_AG_CHLD "0,1,2,3"
+
+/* offsets in indicators table, should be incremented when sending CIEV */
+#define IND_SERVICE	0
+#define IND_CALL	1
+#define IND_CALLSETUP	2
+#define IND_CALLHELD	3
+#define IND_SIGNAL	4
+#define IND_ROAM	5
+#define IND_BATTCHG	6
+#define IND_COUNT	(IND_BATTCHG + 1)
+
+#define RING_TIMEOUT 2
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+struct indicator {
+	const char *name;
+	int min;
+	int max;
+	int val;
+	bool always_active;
+	bool active;
+};
+
+struct hfp_codec {
+	uint8_t type;
+	bool local_supported;
+	bool remote_supported;
+};
+
+struct hf_device {
+	bdaddr_t bdaddr;
+	uint8_t state;
+	uint8_t audio_state;
+	uint32_t features;
+
+	bool clip_enabled;
+	bool cmee_enabled;
+	bool ccwa_enabled;
+	bool indicators_enabled;
+	struct indicator inds[IND_COUNT];
+	int num_active;
+	int num_held;
+	int setup_state;
+	guint call_hanging_up;
+
+	uint8_t negotiated_codec;
+	uint8_t proposed_codec;
+	struct hfp_codec codecs[CODECS_COUNT];
+
+	guint ring;
+	char *clip;
+	bool hsp;
+
+	struct hfp_gw *gw;
+	guint delay_sco;
+};
+
+static const struct indicator inds_defaults[] = {
+		{ "service",   0, 1, 0, false, true },
+		{ "call",      0, 1, 0, true, true },
+		{ "callsetup", 0, 3, 0, true, true },
+		{ "callheld",  0, 2, 0, true, true },
+		{ "signal",    0, 5, 0, false, true },
+		{ "roam",      0, 1, 0, false, true },
+		{ "battchg",   0, 5, 0, false, true },
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+	{ CODEC_ID_CVSD, true, false},
+	{ CODEC_ID_MSBC, false, false},
+};
+
+static struct queue *devices = NULL;
+
+static uint32_t hfp_ag_features = 0;
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *sco_ipc = NULL;
+
+static uint32_t hfp_record_id = 0;
+static GIOChannel *hfp_server = NULL;
+
+static uint32_t hsp_record_id = 0;
+static GIOChannel *hsp_server = NULL;
+
+static struct bt_sco *sco = NULL;
+
+static unsigned int max_hfp_clients = 0;
+
+static void set_state(struct hf_device *dev, uint8_t state)
+{
+	struct hal_ev_handsfree_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	dev->state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void set_audio_state(struct hf_device *dev, uint8_t state)
+{
+	struct hal_ev_handsfree_audio_state ev;
+	char address[18];
+
+	if (dev->audio_state == state)
+		return;
+
+	dev->audio_state = state;
+
+	ba2str(&dev->bdaddr, address);
+	DBG("device %s audio state %u", address, state);
+
+	bdaddr2android(&dev->bdaddr, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void init_codecs(struct hf_device *dev)
+{
+	memcpy(dev->codecs, codecs_defaults, sizeof(dev->codecs));
+
+	if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+		dev->codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static struct hf_device *device_create(const bdaddr_t *bdaddr)
+{
+	struct hf_device *dev;
+
+	dev = new0(struct hf_device, 1);
+
+	bacpy(&dev->bdaddr, bdaddr);
+	dev->setup_state = HAL_HANDSFREE_CALL_STATE_IDLE;
+	dev->state = HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED;
+	dev->audio_state = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED;
+
+	memcpy(dev->inds, inds_defaults, sizeof(dev->inds));
+
+	init_codecs(dev);
+
+	queue_push_head(devices, dev);
+
+	return dev;
+}
+
+static void device_destroy(struct hf_device *dev)
+{
+	hfp_gw_unref(dev->gw);
+
+	if (dev->delay_sco)
+		g_source_remove(dev->delay_sco);
+
+	if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED)
+		bt_sco_disconnect(sco);
+
+	if (dev->ring)
+		g_source_remove(dev->ring);
+
+	g_free(dev->clip);
+
+	if (dev->call_hanging_up)
+		g_source_remove(dev->call_hanging_up);
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED);
+
+	queue_remove(devices, dev);
+	free(dev);
+}
+
+static bool match_by_bdaddr(const void *data, const void *match_data)
+{
+	const struct hf_device *dev = data;
+	const bdaddr_t *addr = match_data;
+
+	return !bacmp(&dev->bdaddr, addr);
+}
+
+static struct hf_device *find_device(const bdaddr_t *bdaddr)
+{
+	if (!bacmp(bdaddr, BDADDR_ANY))
+		return queue_peek_head(devices);
+
+	return queue_find(devices, match_by_bdaddr, bdaddr);
+}
+
+static struct hf_device *get_device(const bdaddr_t *bdaddr)
+{
+	struct hf_device *dev;
+
+	dev = find_device(bdaddr);
+	if (dev)
+		return dev;
+
+	if (queue_length(devices) == max_hfp_clients)
+		return NULL;
+
+	return device_create(bdaddr);
+}
+
+static void disconnect_watch(void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	device_destroy(dev);
+}
+
+static void at_cmd_unknown(const char *command, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_handsfree_unknown_at *ev = (void *) buf;
+
+	bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+	/* copy while string including terminating NULL */
+	ev->len = strlen(command) + 1;
+
+	if (ev->len > IPC_MTU - sizeof(*ev)) {
+		hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+		return;
+	}
+
+	memcpy(ev->buf, command, ev->len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev);
+}
+
+static void at_cmd_vgm(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_volume ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 15)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC;
+		ev.volume = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+VGM */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vgs(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_volume ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 15)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER;
+		ev.volume = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+VGS */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cops(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_cops ev;
+	unsigned int val;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val != 3)
+			break;
+
+		if (!hfp_context_get_number(context, &val) || val != 0)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_COPS, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bia(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val, i, def;
+	bool tmp[IND_COUNT];
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		for (i = 0; i < IND_COUNT; i++)
+			tmp[i] = dev->inds[i].active;
+
+		i = 0;
+
+		do {
+			def = (i < IND_COUNT) ? dev->inds[i].active : 0;
+
+			if (!hfp_context_get_number_default(context, &val, def))
+				goto failed;
+
+			if (val > 1)
+				goto failed;
+
+			if (i < IND_COUNT) {
+				tmp[i] = val || dev->inds[i].always_active;
+				i++;
+			}
+		} while (hfp_context_has_next(context));
+
+		for (i = 0; i < IND_COUNT; i++)
+			dev->inds[i].active = tmp[i];
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+failed:
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_a(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_answer ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_ANSWER, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for ATA */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_d(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	char buf[IPC_MTU];
+	struct hal_ev_handsfree_dial *ev = (void *) buf;
+	int cnt;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_unquoted_string(context,
+						(char *) ev->number, 255))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+		ev->number_len = strlen((char *) ev->number);
+
+		if (ev->number[ev->number_len - 1] != ';')
+			break;
+
+		if (ev->number[0] == '>')
+			cnt = strspn((char *) ev->number + 1, "0123456789") + 1;
+		else
+			cnt = strspn((char *) ev->number, "0123456789ABC*#+");
+
+		if (cnt != ev->number_len - 1)
+			break;
+
+		ev->number_len++;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_DIAL,
+					sizeof(*ev) + ev->number_len, ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ccwa(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		dev->ccwa_enabled = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chup(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_hangup ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_EV_HANDSFREE_HANGUP, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+CHUP */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clcc(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_clcc ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CLCC, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cmee(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		dev->cmee_enabled = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clip(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		dev->clip_enabled = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vts(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_dtmf ev;
+	char str[2];
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_unquoted_string(context, str, 2))
+			break;
+
+		if (!((str[0] >= '0' && str[0] <= '9') ||
+				(str[0] >= 'A' && str[0] <= 'D') ||
+				str[0] == '*' || str[0] == '#'))
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+		ev.tone = str[0];
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev);
+
+		/* Framework is not replying with result for AT+VTS */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cnum(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_cnum ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CNUM, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_binp(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bldn(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_dial ev;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (hfp_context_has_next(context))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+		ev.number_len = 0;
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bvra(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_vr_state ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(context, &val) || val > 1)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		if (val)
+			ev.state = HAL_HANDSFREE_VR_STARTED;
+		else
+			ev.state = HAL_HANDSFREE_VR_STOPPED;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_VR, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_nrec(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_nrec ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		/*
+		 * Android HAL defines start and stop parameter for NREC
+		 * callback, but spec allows HF to only disable AG's NREC
+		 * feature for SLC duration. Follow spec here.
+		 */
+		if (!hfp_context_get_number(context, &val) || val != 0)
+			break;
+
+		if (hfp_context_has_next(context))
+			break;
+
+		ev.nrec = HAL_HANDSFREE_NREC_STOP;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev);
+
+		/* Framework is not replying with context for AT+NREC */
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bsir(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_btrh(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	/* TODO */
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+	struct hf_device *dev;
+
+	DBG("");
+
+	dev = find_device(addr);
+	if (!dev) {
+		error("handsfree: Could not find device");
+		return;
+	}
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+}
+
+static void select_codec(struct hf_device *dev, uint8_t codec_type)
+{
+	uint8_t type = CODEC_ID_CVSD;
+	int i;
+
+	if (codec_type > 0) {
+		type = codec_type;
+		goto done;
+	}
+
+	for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) {
+		if (!dev->codecs[i].local_supported)
+			continue;
+
+		if (!dev->codecs[i].remote_supported)
+			continue;
+
+		type = dev->codecs[i].type;
+		break;
+	}
+
+done:
+	dev->proposed_codec = type;
+
+	hfp_gw_send_info(dev->gw, "+BCS: %u", type);
+}
+
+static bool codec_negotiation_supported(struct hf_device *dev)
+{
+	return (dev->features & HFP_HF_FEAT_CODEC) &&
+			(hfp_ag_features & HFP_AG_FEAT_CODEC);
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+	struct hf_device *dev;
+
+	dev = find_device(addr);
+	if (!dev) {
+		error("handsfree: Connect sco failed, no device?");
+		return;
+	}
+
+	if (status == SCO_STATUS_OK) {
+		set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+		return;
+	}
+
+	/* Try fallback to CVSD first */
+	if (codec_negotiation_supported(dev) &&
+				dev->negotiated_codec != CODEC_ID_CVSD) {
+		info("handsfree: trying fallback with CVSD");
+		select_codec(dev, CODEC_ID_CVSD);
+		return;
+	}
+
+	error("handsfree: audio connect failed");
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+}
+
+static bool connect_sco(struct hf_device *dev)
+{
+	uint16_t voice_settings;
+
+	if (codec_negotiation_supported(dev) &&
+			dev->negotiated_codec != CODEC_ID_CVSD)
+		voice_settings = BT_VOICE_TRANSPARENT;
+	else
+		voice_settings = BT_VOICE_CVSD_16BIT;
+
+	if (!bt_sco_connect(sco, &dev->bdaddr, voice_settings))
+		return false;
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+
+	return true;
+}
+
+static gboolean connect_sco_delayed(void *data)
+{
+	struct hf_device *dev = data;
+
+	DBG("");
+
+	dev->delay_sco = 0;
+
+	if (connect_sco(dev))
+		return FALSE;
+
+	/*
+	 * we try connect to negotiated codec. If it fails, and it isn't
+	 * CVSD codec, try connect CVSD
+	 */
+	if (dev->negotiated_codec != CODEC_ID_CVSD)
+		select_codec(dev, CODEC_ID_CVSD);
+
+	return FALSE;
+}
+
+static void at_cmd_bcc(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_COMMAND:
+		if (!codec_negotiation_supported(dev))
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		/* we haven't negotiated codec, start selection */
+		if (!dev->negotiated_codec) {
+			select_codec(dev, 0);
+			return;
+		}
+
+		/* Delay SCO connection so that OK response is send first */
+		if (dev->delay_sco == 0)
+			dev->delay_sco = g_idle_add(connect_sco_delayed, dev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_SET:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bcs(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_wbs ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &val))
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		/* Remote replied with other codec. Reply with error */
+		if (dev->proposed_codec != val) {
+			dev->proposed_codec = 0;
+			break;
+		}
+
+		ev.wbs = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_WBS, sizeof(ev), &ev);
+
+		dev->proposed_codec = 0;
+		dev->negotiated_codec = val;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		/*
+		 * Delay SCO connection so that OK response is send first,
+		 * then connect with negotiated parameters.
+		 */
+		if (dev->delay_sco == 0)
+			dev->delay_sco = g_idle_add(connect_sco_delayed, dev);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ckpd(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_hsp_key_press ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &val) || val != 200)
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+						HAL_EV_HANDSFREE_HSP_KEY_PRESS,
+						sizeof(ev), &ev);
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+}
+
+static void register_post_slc_at(struct hf_device *dev)
+{
+	hfp_gw_set_command_handler(dev->gw, at_cmd_unknown, dev, NULL);
+
+	if (dev->hsp) {
+		hfp_gw_register(dev->gw, at_cmd_ckpd, "+CKPD", dev, NULL);
+		hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
+		hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL);
+		return;
+	}
+
+	hfp_gw_register(dev->gw, at_cmd_a, "A", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_d, "D", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_ccwa, "+CCWA", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_chup, "+CHUP", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_clcc, "+CLCC", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cops, "+COPS", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cmee, "+CMEE", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_clip, "+CLIP", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_vts, "+VTS", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cnum, "+CNUM", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bia, "+BIA", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_binp, "+BINP", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bldn, "+BLDN", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bvra, "+BVRA", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_nrec, "+NREC", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_vgm, "+VGM", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bsir, "+BSIR", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_btrh, "+BTRH", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bcc, "+BCC", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bcs, "+BCS", dev, NULL);
+}
+
+static void at_cmd_cmer(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		/* mode must be =3 */
+		if (!hfp_context_get_number(result, &val) || val != 3)
+			break;
+
+		/* keyp is don't care */
+		if (!hfp_context_get_number(result, &val))
+			break;
+
+		/* disp is don't care */
+		if (!hfp_context_get_number(result, &val))
+			break;
+
+		/* ind must be 0 or 1 */
+		if (!hfp_context_get_number(result, &val) || val > 1)
+			break;
+
+		dev->indicators_enabled = val;
+
+		/* skip bfr if present */
+		hfp_context_get_number(result, &val);
+
+		if (hfp_context_has_next(result))
+			break;
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		if (dev->features & HFP_HF_FEAT_3WAY)
+			return;
+
+		register_post_slc_at(dev);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_cind(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_cind ev;
+	char *buf, *ptr;
+	int len;
+	unsigned int i;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_TEST:
+
+		/*
+		 * If device supports Codec Negotiation, AT+BAC should be
+		 * received first
+		 */
+		if (codec_negotiation_supported(dev) &&
+				!dev->codecs[CVSD_OFFSET].remote_supported)
+			break;
+
+		len = strlen("+CIND:") + 1;
+
+		for (i = 0; i < IND_COUNT; i++) {
+			len += strlen("(\"\",(X,X)),");
+			len += strlen(dev->inds[i].name);
+		}
+
+		buf = g_malloc(len);
+
+		ptr = buf + sprintf(buf, "+CIND:");
+
+		for (i = 0; i < IND_COUNT; i++) {
+			ptr += sprintf(ptr, "(\"%s\",(%d%c%d)),",
+					dev->inds[i].name,
+					dev->inds[i].min,
+					dev->inds[i].max == 1 ? ',' : '-',
+					dev->inds[i].max);
+		}
+
+		ptr--;
+		*ptr = '\0';
+
+		hfp_gw_send_info(dev->gw, "%s", buf);
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		g_free(buf);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CIND, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_SET:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_brsf(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int feat;
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &feat))
+			break;
+
+		if (hfp_context_has_next(result))
+			break;
+
+		/* TODO verify features */
+		dev->features = feat;
+
+		hfp_gw_send_info(dev->gw, "+BRSF: %u", hfp_ag_features);
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void at_cmd_chld(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	struct hal_ev_handsfree_chld ev;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!hfp_context_get_number(result, &val) || val > 3)
+			break;
+
+		/* No ECC support */
+		if (hfp_context_has_next(result))
+			break;
+
+		/* value match HAL type */
+		ev.chld = val;
+		bdaddr2android(&dev->bdaddr, ev.bdaddr);
+
+		ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+		hfp_gw_send_info(dev->gw, "+CHLD: (%s)", HFP_AG_CHLD);
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		register_post_slc_at(dev);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static struct hfp_codec *find_codec_by_type(struct hf_device *dev, uint8_t type)
+{
+	int i;
+
+	for (i = 0; i < CODECS_COUNT; i++)
+		if (type == dev->codecs[i].type)
+			return &dev->codecs[i];
+
+	return NULL;
+}
+
+static void at_cmd_bac(struct hfp_context *result, enum hfp_gw_cmd_type type,
+								void *user_data)
+{
+	struct hf_device *dev = user_data;
+	unsigned int val;
+
+	DBG("");
+
+	switch (type) {
+	case HFP_GW_CMD_TYPE_SET:
+		if (!codec_negotiation_supported(dev))
+			goto failed;
+
+		/* set codecs to defaults */
+		init_codecs(dev);
+		dev->negotiated_codec = 0;
+
+		/*
+		 * At least CVSD mandatory codec must exist
+		 * HFP V1.6 4.34.1
+		 */
+		if (!hfp_context_get_number(result, &val) ||
+							val != CODEC_ID_CVSD)
+			goto failed;
+
+		dev->codecs[CVSD_OFFSET].remote_supported = true;
+
+		if (hfp_context_get_number(result, &val)) {
+			if (val != CODEC_ID_MSBC)
+				goto failed;
+
+			dev->codecs[MSBC_OFFSET].remote_supported = true;
+		}
+
+		while (hfp_context_has_next(result)) {
+			struct hfp_codec *codec;
+
+			if (!hfp_context_get_number(result, &val))
+				goto failed;
+
+			codec = find_codec_by_type(dev, val);
+			if (!codec)
+				continue;
+
+			codec->remote_supported = true;
+		}
+
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		if (dev->proposed_codec)
+			select_codec(dev, 0);
+		return;
+	case HFP_GW_CMD_TYPE_TEST:
+	case HFP_GW_CMD_TYPE_READ:
+	case HFP_GW_CMD_TYPE_COMMAND:
+		break;
+	}
+
+failed:
+	hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED)
+		hfp_gw_disconnect(dev->gw);
+}
+
+static void register_slc_at(struct hf_device *dev)
+{
+	hfp_gw_register(dev->gw, at_cmd_brsf, "+BRSF", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cind, "+CIND", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_cmer, "+CMER", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_chld, "+CHLD", dev, NULL);
+	hfp_gw_register(dev->gw, at_cmd_bac, "+BAC", dev, NULL);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	if (err) {
+		error("handsfree: connect failed (%s)", err->message);
+		goto failed;
+	}
+
+	dev->gw = hfp_gw_new(g_io_channel_unix_get_fd(chan));
+	if (!dev->gw)
+		goto failed;
+
+	g_io_channel_set_close_on_unref(chan, FALSE);
+
+	hfp_gw_set_close_on_unref(dev->gw, true);
+	hfp_gw_set_disconnect_handler(dev->gw, disconnect_watch, dev, NULL);
+
+	if (dev->hsp) {
+		register_post_slc_at(dev);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+		return;
+	}
+
+	register_slc_at(dev);
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+	return;
+
+failed:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+	device_destroy(dev);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	char address[18];
+	bdaddr_t bdaddr;
+	GError *err = NULL;
+	struct hf_device *dev;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_DEST_BDADDR, &bdaddr,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("handsfree: confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("incoming connect from %s", address);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		error("handsfree: Failed to get device object for %s", address);
+		goto drop;
+	}
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		info("handsfree: refusing connection from %s", address);
+		goto drop;
+	}
+
+	if (!bt_io_accept(chan, connect_cb, dev, NULL, NULL)) {
+		error("handsfree: failed to accept connection");
+		device_destroy(dev);
+		goto drop;
+	}
+
+	dev->hsp = GPOINTER_TO_INT(data);
+
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hf_device *dev = data;
+	sdp_list_t *protos;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t class;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	sdp_uuid16_create(&class, HEADSET_SVCLASS_ID);
+
+	/* Find record with proper service class */
+	for (; recs; recs = recs->next) {
+		sdp_record_t *rec = recs->data;
+
+		if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+			break;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no valid HSP SDP records found");
+		goto fail;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		goto fail;
+	}
+
+	/* TODO read remote version? */
+	/* TODO read volume control support */
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("handsfree: unable to get RFCOMM channel from record");
+		goto fail;
+	}
+
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("handsfree: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	dev->hsp = true;
+
+	g_io_channel_unref(io);
+	return;
+
+fail:
+	device_destroy(dev);
+}
+
+static int sdp_search_hsp(struct hf_device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+					sdp_hsp_search_cb, dev, NULL, 0);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hf_device *dev = data;
+	sdp_list_t *protos;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t class;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	sdp_uuid16_create(&class, HANDSFREE_SVCLASS_ID);
+
+	/* Find record with proper service class */
+	for (; recs; recs = recs->next) {
+		sdp_record_t *rec = recs->data;
+
+		if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+			break;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no HFP SDP records found, trying HSP");
+
+		if (sdp_search_hsp(dev) < 0) {
+			error("handsfree: HSP SDP search failed");
+			goto fail;
+		}
+
+		return;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		goto fail;
+	}
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("handsfree: unable to get RFCOMM channel from record");
+		goto fail;
+	}
+
+	/* TODO read remote version? */
+
+	io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("handsfree: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	g_io_channel_unref(io);
+	return;
+
+fail:
+	device_destroy(dev);
+}
+
+static int sdp_search_hfp(struct hf_device *dev)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &dev->bdaddr, &uuid,
+					sdp_hfp_search_cb, dev, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_connect *cmd = buf;
+	struct hf_device *dev;
+	char addr[18];
+	uint8_t status;
+	bdaddr_t bdaddr;
+	int ret;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+	dev = get_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	ba2str(&bdaddr, addr);
+	DBG("connecting to %s", addr);
+
+	/* prefer HFP over HSP */
+	ret = hfp_server ? sdp_search_hfp(dev) : sdp_search_hsp(dev);
+	if (ret < 0) {
+		error("handsfree: SDP search failed");
+		device_destroy(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CONNECT, status);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_disconnect *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) {
+		status = HAL_STATUS_SUCCESS;
+		goto failed;
+	}
+
+	if (dev->state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) {
+		device_destroy(dev);
+	} else {
+		set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING);
+		hfp_gw_disconnect(dev->gw);
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_DISCONNECT, status);
+}
+
+static bool disconnect_sco(struct hf_device *dev)
+{
+	if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED ||
+		dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING)
+		return false;
+
+	bt_sco_disconnect(sco);
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING);
+
+	return true;
+}
+
+static bool connect_audio(struct hf_device *dev)
+{
+	if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED)
+		return false;
+
+	/* we haven't negotiated codec, start selection */
+	if (codec_negotiation_supported(dev) && !dev->negotiated_codec) {
+		select_codec(dev, 0);
+		return true;
+	}
+
+	return connect_sco(dev);
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_connect_audio *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = connect_audio(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_disconnect_audio *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	status = disconnect_sco(dev) ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_start_vr *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->features & HFP_HF_FEAT_VR) {
+		hfp_gw_send_info(dev->gw, "+BVRA: 1");
+		status = HAL_STATUS_SUCCESS;
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_stop_vr *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->features & HFP_HF_FEAT_VR) {
+		hfp_gw_send_info(dev->gw, "+BVRA: 0");
+		status = HAL_STATUS_SUCCESS;
+	} else {
+		status = HAL_STATUS_FAILED;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_volume_control *cmd = buf;
+	struct hf_device *dev;
+	uint8_t status, volume;
+	bdaddr_t bdaddr;
+
+	DBG("type=%u volume=%u", cmd->type, cmd->volume);
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	volume = cmd->volume > 15 ? 15 : cmd->volume;
+
+	switch (cmd->type) {
+	case HAL_HANDSFREE_VOLUME_TYPE_MIC:
+		hfp_gw_send_info(dev->gw, "+VGM: %u", volume);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER:
+		hfp_gw_send_info(dev->gw, "+VGS: %u", volume);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_VOLUME_CONTROL, status);
+}
+
+static void update_indicator(struct hf_device *dev, int ind, uint8_t val)
+{
+	DBG("ind=%u new=%u old=%u", ind, val, dev->inds[ind].val);
+
+	if (dev->inds[ind].val == val)
+		return;
+
+	dev->inds[ind].val = val;
+
+	if (!dev->indicators_enabled)
+		return;
+
+	if (!dev->inds[ind].active)
+		return;
+
+	/* indicator numbers in CIEV start from 1 */
+	hfp_gw_send_info(dev->gw, "+CIEV: %u,%u", ind + 1, val);
+}
+
+static void device_status_notif(void *data, void *user_data)
+{
+	struct hf_device *dev = data;
+	struct hal_cmd_handsfree_device_status_notif *cmd = user_data;
+
+	update_indicator(dev, IND_SERVICE, cmd->state);
+	update_indicator(dev, IND_ROAM, cmd->type);
+	update_indicator(dev, IND_SIGNAL, cmd->signal);
+	update_indicator(dev, IND_BATTCHG, cmd->battery);
+}
+
+static void handle_device_status_notif(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_device_status_notif *cmd = buf;
+	uint8_t status;
+
+	DBG("");
+
+	if (queue_isempty(devices)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* Cast cmd to void as queue api needs that */
+	queue_foreach(devices, device_status_notif, (void *) cmd);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, status);
+}
+
+static void handle_cops(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_cops_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			(cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+		error("Invalid cops response command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	hfp_gw_send_info(dev->gw, "+COPS: 0,0,\"%.16s\"",
+					cmd->len ? (char *) cmd->buf : "");
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_COPS_RESPONSE, status);
+}
+
+static unsigned int get_callsetup(uint8_t state)
+{
+	switch (state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		return 1;
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+		return 2;
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		return 3;
+	default:
+		return 0;
+	}
+}
+
+static void handle_cind(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_cind_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* HAL doesn't provide indicators values so need to convert here */
+	dev->inds[IND_SERVICE].val = cmd->svc;
+	dev->inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held);
+	dev->inds[IND_CALLSETUP].val = get_callsetup(cmd->state);
+	dev->inds[IND_CALLHELD].val = cmd->num_held ?
+						(cmd->num_active ? 1 : 2) : 0;
+	dev->inds[IND_SIGNAL].val = cmd->signal;
+	dev->inds[IND_ROAM].val = cmd->roam;
+	dev->inds[IND_BATTCHG].val = cmd->batt_chg;
+
+	/* Order must match indicators_defaults table */
+	hfp_gw_send_info(dev->gw, "+CIND: %u,%u,%u,%u,%u,%u,%u",
+						dev->inds[IND_SERVICE].val,
+						dev->inds[IND_CALL].val,
+						dev->inds[IND_CALLSETUP].val,
+						dev->inds[IND_CALLHELD].val,
+						dev->inds[IND_SIGNAL].val,
+						dev->inds[IND_ROAM].val,
+						dev->inds[IND_BATTCHG].val);
+
+	hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CIND_RESPONSE, status);
+}
+
+static void handle_formatted_at_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_formatted_at_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			(cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+		error("Invalid formatted AT response command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	hfp_gw_send_info(dev->gw, "%s", cmd->len ? (char *) cmd->buf : "");
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+			HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, status);
+}
+
+static void handle_at_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_at_response *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK)
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+	else if (dev->cmee_enabled)
+		hfp_gw_send_error(dev->gw, cmd->error);
+	else
+		hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_AT_RESPONSE, status);
+}
+
+static void handle_clcc_resp(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_clcc_response *cmd = buf;
+	struct hf_device *dev;
+	uint8_t status;
+	bdaddr_t bdaddr;
+	char *number;
+
+	if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+				cmd->number[cmd->number_len - 1] != '\0')) {
+		error("Invalid CLCC response command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (!cmd->index) {
+		hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
+
+		status = HAL_STATUS_SUCCESS;
+		goto done;
+	}
+
+	number = cmd->number_len ? (char *) cmd->number : "";
+
+	switch (cmd->state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+	case HAL_HANDSFREE_CALL_STATE_WAITING:
+	case HAL_HANDSFREE_CALL_STATE_ACTIVE:
+	case HAL_HANDSFREE_CALL_STATE_HELD:
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL &&
+							number[0] != '+')
+			hfp_gw_send_info(dev->gw,
+					"+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u",
+					cmd->index, cmd->dir, cmd->state,
+					cmd->mode, cmd->mpty, number,
+					cmd->type);
+		else
+			hfp_gw_send_info(dev->gw,
+					"+CLCC: %u,%u,%u,%u,%u,\"%s\",%u",
+					cmd->index, cmd->dir, cmd->state,
+					cmd->mode, cmd->mpty, number,
+					cmd->type);
+
+		status = HAL_STATUS_SUCCESS;
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+	default:
+		status = HAL_STATUS_FAILED;
+		break;
+	}
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_CLCC_RESPONSE, status);
+}
+
+static gboolean ring_cb(gpointer user_data)
+{
+	struct hf_device *dev = user_data;
+
+	hfp_gw_send_info(dev->gw, "RING");
+
+	if (dev->clip_enabled && dev->clip)
+		hfp_gw_send_info(dev->gw, "%s", dev->clip);
+
+	return TRUE;
+}
+
+static void phone_state_dialing(struct hf_device *dev, int num_active,
+								int num_held)
+{
+	if (dev->call_hanging_up) {
+		g_source_remove(dev->call_hanging_up);
+		dev->call_hanging_up = 0;
+	}
+
+	update_indicator(dev, IND_CALLSETUP, 2);
+
+	if (num_active == 0 && num_held > 0)
+		update_indicator(dev, IND_CALLHELD, 2);
+
+	if (dev->num_active == 0 && dev->num_held == 0)
+		connect_audio(dev);
+}
+
+static void phone_state_alerting(struct hf_device *dev, int num_active,
+								int num_held)
+{
+	if (dev->call_hanging_up) {
+		g_source_remove(dev->call_hanging_up);
+		dev->call_hanging_up = 0;
+	}
+
+	update_indicator(dev, IND_CALLSETUP, 3);
+}
+
+static void phone_state_waiting(struct hf_device *dev, int num_active,
+					int num_held, uint8_t type,
+					const uint8_t *number, int number_len)
+{
+	char *num;
+
+	if (!dev->ccwa_enabled)
+		return;
+
+	num = number_len ? (char *) number : "";
+
+	if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+		hfp_gw_send_info(dev->gw, "+CCWA: \"+%s\",%u", num, type);
+	else
+		hfp_gw_send_info(dev->gw, "+CCWA: \"%s\",%u", num, type);
+
+	update_indicator(dev, IND_CALLSETUP, 1);
+}
+
+static void phone_state_incoming(struct hf_device *dev, int num_active,
+					int num_held, uint8_t type,
+					const uint8_t *number, int number_len)
+{
+	char *num;
+
+	if (dev->setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) {
+		if (dev->num_active != num_active ||
+						dev->num_held != num_held) {
+			if (dev->num_active == num_held &&
+						dev->num_held == num_active)
+				return;
+			/*
+			 * calls changed while waiting call ie. due to
+			 * termination of active call
+			 */
+			update_indicator(dev, IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+			update_indicator(dev, IND_CALL,
+						!!(num_active + num_held));
+		}
+
+		return;
+	}
+
+	if (dev->call_hanging_up)
+		return;
+
+	if (num_active > 0 || num_held > 0) {
+		phone_state_waiting(dev, num_active, num_held, type, number,
+								number_len);
+		return;
+	}
+
+	update_indicator(dev, IND_CALLSETUP, 1);
+
+	num = number_len ? (char *) number : "";
+
+	if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+		dev->clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type);
+	else
+		dev->clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type);
+
+	/* send first RING */
+	ring_cb(dev);
+
+	dev->ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+							RING_TIMEOUT, ring_cb,
+							dev, NULL);
+	if (!dev->ring) {
+		g_free(dev->clip);
+		dev->clip = NULL;
+	}
+}
+
+static gboolean hang_up_cb(gpointer user_data)
+{
+	struct hf_device *dev = user_data;
+
+	DBG("");
+
+	dev->call_hanging_up = 0;
+
+	return FALSE;
+}
+
+static void phone_state_idle(struct hf_device *dev, int num_active,
+								int num_held)
+{
+	if (dev->ring) {
+		g_source_remove(dev->ring);
+		dev->ring = 0;
+
+		if (dev->clip) {
+			g_free(dev->clip);
+			dev->clip = NULL;
+		}
+	}
+
+	switch (dev->setup_state) {
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		if (num_active > dev->num_active) {
+			update_indicator(dev, IND_CALL, 1);
+
+			if (dev->num_active == 0 && dev->num_held == 0)
+				connect_audio(dev);
+		}
+
+		if (num_held >= dev->num_held && num_held != 0)
+			update_indicator(dev, IND_CALLHELD, 1);
+
+		update_indicator(dev, IND_CALLSETUP, 0);
+
+		if (num_active == 0 && num_held == 0 &&
+				num_active == dev->num_active &&
+				num_held == dev->num_held)
+			dev->call_hanging_up = g_timeout_add(800, hang_up_cb,
+									dev);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		if (num_active > dev->num_active)
+			update_indicator(dev, IND_CALL, 1);
+
+		update_indicator(dev, IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+
+		update_indicator(dev, IND_CALLSETUP, 0);
+
+		/* disconnect SCO if we hang up while dialing or alerting */
+		if (num_active == 0 && num_held == 0)
+			disconnect_sco(dev);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+		if (dev->call_hanging_up) {
+			g_source_remove(dev->call_hanging_up);
+			dev->call_hanging_up = 0;
+			return;
+		}
+
+		/* check if calls swapped */
+		if (num_held != 0 && num_active != 0 &&
+				dev->num_active == num_held &&
+				dev->num_held == num_active) {
+			/* TODO better way for forcing indicator */
+			dev->inds[IND_CALLHELD].val = 0;
+		} else if ((num_active > 0 || num_held > 0) &&
+						dev->num_active == 0 &&
+						dev->num_held == 0) {
+			/*
+			 * If number of active or held calls change but there
+			 * was no call setup change this means that there were
+			 * calls present when headset was connected.
+			 */
+			connect_audio(dev);
+		} else if (num_active == 0 && num_held == 0) {
+			disconnect_sco(dev);
+		}
+
+		update_indicator(dev, IND_CALLHELD,
+					num_held ? (num_active ? 1 : 2) : 0);
+		update_indicator(dev, IND_CALL, !!(num_active + num_held));
+		update_indicator(dev, IND_CALLSETUP, 0);
+
+		/* If call was terminated due to carrier lost send NO CARRIER */
+		if (num_active == 0 && num_held == 0 &&
+				dev->inds[IND_SERVICE].val == 0 &&
+				(dev->num_active > 0 || dev->num_held > 0))
+			hfp_gw_send_info(dev->gw, "NO CARRIER");
+
+		break;
+	default:
+		DBG("unhandled state %u", dev->setup_state);
+		break;
+	}
+}
+
+static void phone_state_change(void *data, void *user_data)
+{
+	struct hf_device *dev = data;
+	struct hal_cmd_handsfree_phone_state_change *cmd = user_data;
+
+	switch (cmd->state) {
+	case HAL_HANDSFREE_CALL_STATE_DIALING:
+		phone_state_dialing(dev, cmd->num_active, cmd->num_held);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_ALERTING:
+		phone_state_alerting(dev, cmd->num_active, cmd->num_held);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_INCOMING:
+		phone_state_incoming(dev, cmd->num_active, cmd->num_held,
+						cmd->type, cmd->number,
+						cmd->number_len);
+		break;
+	case HAL_HANDSFREE_CALL_STATE_IDLE:
+		phone_state_idle(dev, cmd->num_active, cmd->num_held);
+		break;
+	default:
+		DBG("unhandled new state %u (current state %u)", cmd->state,
+							dev->setup_state);
+
+		return;
+	}
+
+	dev->num_active = cmd->num_active;
+	dev->num_held = cmd->num_held;
+	dev->setup_state = cmd->state;
+
+}
+
+static void handle_phone_state_change(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_phone_state_change *cmd = buf;
+	uint8_t status;
+
+	if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+				cmd->number[cmd->number_len - 1] != '\0')) {
+		error("Invalid phone state change command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held,
+								cmd->state);
+
+	if (queue_isempty(devices)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* Cast cmd to void as queue api needs that */
+	queue_foreach(devices, phone_state_change, (void *) cmd);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+				HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status);
+}
+
+static void handle_configure_wbs(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_handsfree_configure_wbs *cmd = buf;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	uint8_t status;
+
+	if (!(hfp_ag_features & HFP_AG_FEAT_CODEC)) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	if (dev->audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	switch (cmd->config) {
+	case HAL_HANDSFREE_WBS_NO:
+		dev->codecs[MSBC_OFFSET].local_supported = false;
+		break;
+	case HAL_HANDSFREE_WBS_YES:
+		dev->codecs[MSBC_OFFSET].local_supported = true;
+		break;
+	case HAL_HANDSFREE_WBS_NONE:
+		/* TODO */
+	default:
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/*
+	 * cleanup negotiated codec if WBS support was changed, it will be
+	 * renegotiated on next audio connection based on currently supported
+	 * codecs
+	 */
+	dev->negotiated_codec = 0;
+	status = HAL_STATUS_SUCCESS;
+
+done:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+					HAL_OP_HANDSFREE_CONFIGURE_WBS, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HANDSFREE_CONNECT */
+	{ handle_connect, false,
+		sizeof(struct hal_cmd_handsfree_connect) },
+	/* HAL_OP_HANDSFREE_DISCONNECT */
+	{ handle_disconnect, false,
+		sizeof(struct hal_cmd_handsfree_disconnect) },
+	/* HAL_OP_HANDSFREE_CONNECT_AUDIO */
+	{ handle_connect_audio, false,
+		sizeof(struct hal_cmd_handsfree_connect_audio) },
+	/* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */
+	{ handle_disconnect_audio, false,
+		sizeof(struct hal_cmd_handsfree_disconnect_audio) },
+	/* define HAL_OP_HANDSFREE_START_VR */
+	{ handle_start_vr, false, sizeof(struct hal_cmd_handsfree_start_vr) },
+	/* define HAL_OP_HANDSFREE_STOP_VR */
+	{ handle_stop_vr, false, sizeof(struct hal_cmd_handsfree_stop_vr) },
+	/* HAL_OP_HANDSFREE_VOLUME_CONTROL */
+	{ handle_volume_control, false,
+		sizeof(struct hal_cmd_handsfree_volume_control) },
+	/* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */
+	{ handle_device_status_notif, false,
+		sizeof(struct hal_cmd_handsfree_device_status_notif) },
+	/* HAL_OP_HANDSFREE_COPS_RESPONSE */
+	{ handle_cops, true,
+		sizeof(struct hal_cmd_handsfree_cops_response) },
+	/* HAL_OP_HANDSFREE_CIND_RESPONSE */
+	{ handle_cind, false,
+		sizeof(struct hal_cmd_handsfree_cind_response) },
+	/* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */
+	{ handle_formatted_at_resp, true,
+		sizeof(struct hal_cmd_handsfree_formatted_at_response) },
+	/* HAL_OP_HANDSFREE_AT_RESPONSE */
+	{ handle_at_resp, false,
+		sizeof(struct hal_cmd_handsfree_at_response) },
+	/* HAL_OP_HANDSFREE_CLCC_RESPONSE */
+	{ handle_clcc_resp, true,
+		sizeof(struct hal_cmd_handsfree_clcc_response) },
+	/* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */
+	{ handle_phone_state_change, true,
+		sizeof(struct hal_cmd_handsfree_phone_state_change) },
+	/* HAL_OP_HANDSFREE_CONFIGURE_WBS */
+	{ handle_configure_wbs, false,
+		sizeof(struct hal_cmd_handsfree_configure_wbs) },
+};
+
+static sdp_record_t *headset_ag_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel;
+	uint8_t netid = 0x01;
+	sdp_data_t *network;
+	uint8_t ch = HSP_AG_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	network = sdp_data_alloc(SDP_UINT8, &netid);
+	if (!network) {
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+	profile.version = 0x0102;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Voice Gateway", NULL, NULL);
+
+	sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+	char address[18];
+	struct hf_device *dev;
+
+	ba2str(addr, address);
+
+	DBG("incoming SCO connection from %s", address);
+
+	dev = find_device(addr);
+	if (!dev || dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) {
+		error("handsfree: audio connection from %s rejected", address);
+		return false;
+	}
+
+	/* If HF initiate SCO there must be no WBS used */
+	*voice_settings = 0;
+
+	set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+	return true;
+}
+
+static bool enable_hsp_ag(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	DBG("");
+
+	hsp_server =  bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true),
+					NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hsp_server) {
+		error("Failed to listen on Headset rfcomm: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = headset_ag_record();
+	if (!rec) {
+		error("Failed to allocate Headset record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register Headset record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hsp_record_id = rec->handle;
+	return true;
+
+failed:
+	g_io_channel_shutdown(hsp_server, TRUE, NULL);
+	g_io_channel_unref(hsp_server);
+	hsp_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hsp_ag(void)
+{
+	if (hsp_server) {
+		g_io_channel_shutdown(hsp_server, TRUE, NULL);
+		g_io_channel_unref(hsp_server);
+		hsp_server = NULL;
+	}
+
+	if (hsp_record_id > 0) {
+		bt_adapter_remove_record(hsp_record_id);
+		hsp_record_id = 0;
+	}
+}
+
+static sdp_record_t *hfp_ag_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel, *features;
+	uint8_t netid = 0x01;
+	uint16_t sdpfeat;
+	sdp_data_t *network;
+	uint8_t ch = HFP_AG_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	network = sdp_data_alloc(SDP_UINT8, &netid);
+	if (!network) {
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+	profile.version = 0x0106;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	/* Codec Negotiation bit in SDP feature is different then in BRSF */
+	sdpfeat = hfp_ag_features & 0x0000003F;
+	if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+		sdpfeat |= 0x00000020;
+	else
+		sdpfeat &= ~0x00000020;
+
+	features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL);
+
+	sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool enable_hfp_ag(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	DBG("");
+
+	if (hfp_server)
+		return false;
+
+	hfp_server =  bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false),
+					NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hfp_server) {
+		error("Failed to listen on Handsfree rfcomm: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = hfp_ag_record();
+	if (!rec) {
+		error("Failed to allocate Handsfree record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register Handsfree record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hfp_record_id = rec->handle;
+	return true;
+
+failed:
+	g_io_channel_shutdown(hfp_server, TRUE, NULL);
+	g_io_channel_unref(hfp_server);
+	hfp_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hfp_ag(void)
+{
+	if (hfp_server) {
+		g_io_channel_shutdown(hfp_server, TRUE, NULL);
+		g_io_channel_unref(hfp_server);
+		hfp_server = NULL;
+	}
+
+	if (hfp_record_id > 0) {
+		bt_adapter_remove_record(hfp_record_id);
+		hfp_record_id = 0;
+	}
+}
+
+static void bt_sco_get_fd(const void *buf, uint16_t len)
+{
+	const struct sco_cmd_get_fd *cmd = buf;
+	struct sco_rsp_get_fd rsp;
+	struct hf_device *dev;
+	bdaddr_t bdaddr;
+	int fd;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	dev = find_device(&bdaddr);
+	if (!dev || !bt_sco_get_fd_and_mtu(sco, &fd, &rsp.mtu))
+		goto failed;
+
+	DBG("fd %d mtu %u", fd, rsp.mtu);
+
+	ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_GET_FD,
+							sizeof(rsp), &rsp, fd);
+
+	return;
+
+failed:
+	ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED);
+}
+
+static const struct ipc_handler sco_handlers[] = {
+	/* SCO_OP_GET_FD */
+	{ bt_sco_get_fd, false, sizeof(struct sco_cmd_get_fd) }
+};
+
+static void bt_sco_unregister(void)
+{
+	DBG("");
+
+	ipc_cleanup(sco_ipc);
+	sco_ipc = NULL;
+}
+
+static bool bt_sco_register(ipc_disconnect_cb disconnect)
+{
+	DBG("");
+
+	sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH),
+				SCO_SERVICE_ID, false, disconnect, NULL);
+	if (!sco_ipc)
+		return false;
+
+	ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers,
+						G_N_ELEMENTS(sco_handlers));
+
+	return true;
+}
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode,
+								int max_clients)
+{
+	DBG("mode 0x%x max_clients %d", mode, max_clients);
+
+	bacpy(&adapter_addr, addr);
+
+	if (max_clients < 1)
+		return false;
+
+	devices = queue_new();
+
+	if (!enable_hsp_ag())
+		goto failed_queue;
+
+	sco = bt_sco_new(addr);
+	if (!sco)
+		goto failed_hsp;
+
+	bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+	bt_sco_set_connect_cb(sco, connect_sco_cb);
+	bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
+	if (mode == HAL_MODE_HANDSFREE_HSP_ONLY)
+		goto done;
+
+	hfp_ag_features = HFP_AG_FEATURES;
+
+	if (mode == HAL_MODE_HANDSFREE_HFP_WBS)
+		hfp_ag_features |= HFP_AG_FEAT_CODEC;
+
+	if (enable_hfp_ag())
+		goto done;
+
+	bt_sco_unref(sco);
+	sco = NULL;
+	hfp_ag_features = 0;
+failed_hsp:
+	cleanup_hsp_ag();
+failed_queue:
+	queue_destroy(devices, NULL);
+	devices = NULL;
+
+	return false;
+
+done:
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	bt_sco_register(NULL);
+
+	max_hfp_clients = max_clients;
+
+	return true;
+}
+
+void bt_handsfree_unregister(void)
+{
+	DBG("");
+
+	bt_sco_unregister();
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+	hal_ipc = NULL;
+
+	cleanup_hfp_ag();
+	cleanup_hsp_ag();
+	bt_sco_unref(sco);
+	sco = NULL;
+
+	hfp_ag_features = 0;
+
+	queue_destroy(devices, (queue_destroy_func_t) device_destroy);
+	devices = NULL;
+
+	max_hfp_clients = 0;
+}
diff --git a/bluez/repo/android/handsfree.h b/bluez/repo/android/handsfree.h
new file mode 100644
index 0000000..d4fd649
--- /dev/null
+++ b/bluez/repo/android/handsfree.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode,
+							int max_clients);
+void bt_handsfree_unregister(void);
diff --git a/bluez/repo/android/hardware/._audio.h b/bluez/repo/android/hardware/._audio.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._audio.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._audio_effect.h b/bluez/repo/android/hardware/._audio_effect.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._audio_effect.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bluetooth.h b/bluez/repo/android/hardware/._bluetooth.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bluetooth.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_av.h b/bluez/repo/android/hardware/._bt_av.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_av.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_gatt.h b/bluez/repo/android/hardware/._bt_gatt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_gatt.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_gatt_client.h b/bluez/repo/android/hardware/._bt_gatt_client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_gatt_client.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_gatt_server.h b/bluez/repo/android/hardware/._bt_gatt_server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_gatt_server.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_gatt_types.h b/bluez/repo/android/hardware/._bt_gatt_types.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_gatt_types.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_hf.h b/bluez/repo/android/hardware/._bt_hf.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_hf.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_hf_client.h b/bluez/repo/android/hardware/._bt_hf_client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_hf_client.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_hh.h b/bluez/repo/android/hardware/._bt_hh.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_hh.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_hl.h b/bluez/repo/android/hardware/._bt_hl.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_hl.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_mce.h b/bluez/repo/android/hardware/._bt_mce.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_mce.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_pan.h b/bluez/repo/android/hardware/._bt_pan.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_pan.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_rc.h b/bluez/repo/android/hardware/._bt_rc.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_rc.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._bt_sock.h b/bluez/repo/android/hardware/._bt_sock.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._bt_sock.h
Binary files differ
diff --git a/bluez/repo/android/hardware/._hardware.c b/bluez/repo/android/hardware/._hardware.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._hardware.c
Binary files differ
diff --git a/bluez/repo/android/hardware/._hardware.h b/bluez/repo/android/hardware/._hardware.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/hardware/._hardware.h
Binary files differ
diff --git a/bluez/repo/android/hardware/audio.h b/bluez/repo/android/hardware/audio.h
new file mode 100644
index 0000000..3cc2be5
--- /dev/null
+++ b/bluez/repo/android/hardware/audio.h
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_HAL_INTERFACE_H
+#define ANDROID_AUDIO_HAL_INTERFACE_H
+
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio_effect.h>
+
+__BEGIN_DECLS
+
+/**
+ * The id of this module
+ */
+#define AUDIO_HARDWARE_MODULE_ID "audio"
+
+/**
+ * Name of the audio devices to open
+ */
+#define AUDIO_HARDWARE_INTERFACE "audio_hw_if"
+
+
+/* Use version 0.1 to be compatible with first generation of audio hw module with version_major
+ * hardcoded to 1. No audio module API change.
+ */
+#define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
+#define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1
+
+/* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0
+ * will be considered of first generation API.
+ */
+#define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0)
+#define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0)
+#define AUDIO_DEVICE_API_VERSION_3_0 HARDWARE_DEVICE_API_VERSION(3, 0)
+#define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_3_0
+/* Minimal audio HAL version supported by the audio framework */
+#define AUDIO_DEVICE_API_VERSION_MIN AUDIO_DEVICE_API_VERSION_2_0
+
+/**
+ * List of known audio HAL modules. This is the base name of the audio HAL
+ * library composed of the "audio." prefix, one of the base names below and
+ * a suffix specific to the device.
+ * e.g: audio.primary.goldfish.so or audio.a2dp.default.so
+ */
+
+#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary"
+#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp"
+#define AUDIO_HARDWARE_MODULE_ID_USB "usb"
+#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix"
+#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload"
+
+/**************************************/
+
+/**
+ *  standard audio parameters that the HAL may need to handle
+ */
+
+/**
+ *  audio device parameters
+ */
+
+/* BT SCO Noise Reduction + Echo Cancellation parameters */
+#define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec"
+#define AUDIO_PARAMETER_VALUE_ON "on"
+#define AUDIO_PARAMETER_VALUE_OFF "off"
+
+/* TTY mode selection */
+#define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode"
+#define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off"
+#define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco"
+#define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco"
+#define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full"
+
+/* Hearing Aid Compatibility - Telecoil (HAC-T) mode on/off
+   Strings must be in sync with CallFeaturesSetting.java */
+#define AUDIO_PARAMETER_KEY_HAC "HACSetting"
+#define AUDIO_PARAMETER_VALUE_HAC_ON "ON"
+#define AUDIO_PARAMETER_VALUE_HAC_OFF "OFF"
+
+/* A2DP sink address set by framework */
+#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address"
+
+/* A2DP source address set by framework */
+#define AUDIO_PARAMETER_A2DP_SOURCE_ADDRESS "a2dp_source_address"
+
+/* Screen state */
+#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state"
+
+/* Bluetooth SCO wideband */
+#define AUDIO_PARAMETER_KEY_BT_SCO_WB "bt_wbs"
+
+
+/**
+ *  audio stream parameters
+ */
+
+#define AUDIO_PARAMETER_STREAM_ROUTING "routing"             /* audio_devices_t */
+#define AUDIO_PARAMETER_STREAM_FORMAT "format"               /* audio_format_t */
+#define AUDIO_PARAMETER_STREAM_CHANNELS "channels"           /* audio_channel_mask_t */
+#define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count"     /* size_t */
+#define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source"   /* audio_source_t */
+#define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" /* uint32_t */
+
+#define AUDIO_PARAMETER_DEVICE_DISCONNECT "disconnect"      /* audio_devices_t */
+
+/* Query supported formats. The response is a '|' separated list of strings from
+ * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */
+#define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats"
+/* Query supported channel masks. The response is a '|' separated list of strings from
+ * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */
+#define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels"
+/* Query supported sampling rates. The response is a '|' separated list of integer values e.g:
+ * "sup_sampling_rates=44100|48000" */
+#define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates"
+
+/* Get the HW synchronization source used for an output stream.
+ * Return a valid source (positive integer) or AUDIO_HW_SYNC_INVALID if an error occurs
+ * or no HW sync source is used. */
+#define AUDIO_PARAMETER_STREAM_HW_AV_SYNC "hw_av_sync"
+
+/**
+ * audio codec parameters
+ */
+
+#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param"
+#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample"
+#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id"
+#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align"
+#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate"
+#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option"
+#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL  "music_offload_num_channels"
+#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING  "music_offload_down_sampling"
+#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES  "delay_samples"
+#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES  "padding_samples"
+
+/**************************************/
+
+/* common audio stream parameters and operations */
+struct audio_stream {
+
+    /**
+     * Return the sampling rate in Hz - eg. 44100.
+     */
+    uint32_t (*get_sample_rate)(const struct audio_stream *stream);
+
+    /* currently unused - use set_parameters with key
+     *    AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+     */
+    int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
+
+    /**
+     * Return size of input/output buffer in bytes for this stream - eg. 4800.
+     * It should be a multiple of the frame size.  See also get_input_buffer_size.
+     */
+    size_t (*get_buffer_size)(const struct audio_stream *stream);
+
+    /**
+     * Return the channel mask -
+     *  e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+     */
+    audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
+
+    /**
+     * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+     */
+    audio_format_t (*get_format)(const struct audio_stream *stream);
+
+    /* currently unused - use set_parameters with key
+     *     AUDIO_PARAMETER_STREAM_FORMAT
+     */
+    int (*set_format)(struct audio_stream *stream, audio_format_t format);
+
+    /**
+     * Put the audio hardware input/output into standby mode.
+     * Driver should exit from standby mode at the next I/O operation.
+     * Returns 0 on success and <0 on failure.
+     */
+    int (*standby)(struct audio_stream *stream);
+
+    /** dump the state of the audio input/output device */
+    int (*dump)(const struct audio_stream *stream, int fd);
+
+    /** Return the set of device(s) which this stream is connected to */
+    audio_devices_t (*get_device)(const struct audio_stream *stream);
+
+    /**
+     * Currently unused - set_device() corresponds to set_parameters() with key
+     * AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+     * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+     * input streams only.
+     */
+    int (*set_device)(struct audio_stream *stream, audio_devices_t device);
+
+    /**
+     * set/get audio stream parameters. The function accepts a list of
+     * parameter key value pairs in the form: key1=value1;key2=value2;...
+     *
+     * Some keys are reserved for standard parameters (See AudioParameter class)
+     *
+     * If the implementation does not accept a parameter change while
+     * the output is active but the parameter is acceptable otherwise, it must
+     * return -ENOSYS.
+     *
+     * The audio flinger will put the stream in standby and then change the
+     * parameter value.
+     */
+    int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs);
+
+    /*
+     * Returns a pointer to a heap allocated string. The caller is responsible
+     * for freeing the memory for it using free().
+     */
+    char * (*get_parameters)(const struct audio_stream *stream,
+                             const char *keys);
+    int (*add_audio_effect)(const struct audio_stream *stream,
+                             effect_handle_t effect);
+    int (*remove_audio_effect)(const struct audio_stream *stream,
+                             effect_handle_t effect);
+};
+typedef struct audio_stream audio_stream_t;
+
+/* type of asynchronous write callback events. Mutually exclusive */
+typedef enum {
+    STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */
+    STREAM_CBK_EVENT_DRAIN_READY  /* drain completed */
+} stream_callback_event_t;
+
+typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie);
+
+/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */
+typedef enum {
+    AUDIO_DRAIN_ALL,            /* drain() returns when all data has been played */
+    AUDIO_DRAIN_EARLY_NOTIFY    /* drain() returns a short time before all data
+                                   from the current track has been played to
+                                   give time for gapless track switch */
+} audio_drain_type_t;
+
+/**
+ * audio_stream_out is the abstraction interface for the audio output hardware.
+ *
+ * It provides information about various properties of the audio output
+ * hardware driver.
+ */
+
+struct audio_stream_out {
+    /**
+     * Common methods of the audio stream out.  This *must* be the first member of audio_stream_out
+     * as users of this structure will cast a audio_stream to audio_stream_out pointer in contexts
+     * where it's known the audio_stream references an audio_stream_out.
+     */
+    struct audio_stream common;
+
+    /**
+     * Return the audio hardware driver estimated latency in milliseconds.
+     */
+    uint32_t (*get_latency)(const struct audio_stream_out *stream);
+
+    /**
+     * Use this method in situations where audio mixing is done in the
+     * hardware. This method serves as a direct interface with hardware,
+     * allowing you to directly set the volume as apposed to via the framework.
+     * This method might produce multiple PCM outputs or hardware accelerated
+     * codecs, such as MP3 or AAC.
+     */
+    int (*set_volume)(struct audio_stream_out *stream, float left, float right);
+
+    /**
+     * Write audio buffer to driver. Returns number of bytes written, or a
+     * negative status_t. If at least one frame was written successfully prior to the error,
+     * it is suggested that the driver return that successful (short) byte count
+     * and then return an error in the subsequent call.
+     *
+     * If set_callback() has previously been called to enable non-blocking mode
+     * the write() is not allowed to block. It must write only the number of
+     * bytes that currently fit in the driver/hardware buffer and then return
+     * this byte count. If this is less than the requested write size the
+     * callback function must be called when more space is available in the
+     * driver/hardware buffer.
+     */
+    ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,
+                     size_t bytes);
+
+    /* return the number of audio frames written by the audio dsp to DAC since
+     * the output has exited standby
+     */
+    int (*get_render_position)(const struct audio_stream_out *stream,
+                               uint32_t *dsp_frames);
+
+    /**
+     * get the local time at which the next write to the audio driver will be presented.
+     * The units are microseconds, where the epoch is decided by the local audio HAL.
+     */
+    int (*get_next_write_timestamp)(const struct audio_stream_out *stream,
+                                    int64_t *timestamp);
+
+    /**
+     * set the callback function for notifying completion of non-blocking
+     * write and drain.
+     * Calling this function implies that all future write() and drain()
+     * must be non-blocking and use the callback to signal completion.
+     */
+    int (*set_callback)(struct audio_stream_out *stream,
+            stream_callback_t callback, void *cookie);
+
+    /**
+     * Notifies to the audio driver to stop playback however the queued buffers are
+     * retained by the hardware. Useful for implementing pause/resume. Empty implementation
+     * if not supported however should be implemented for hardware with non-trivial
+     * latency. In the pause state audio hardware could still be using power. User may
+     * consider calling suspend after a timeout.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*pause)(struct audio_stream_out* stream);
+
+    /**
+     * Notifies to the audio driver to resume playback following a pause.
+     * Returns error if called without matching pause.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*resume)(struct audio_stream_out* stream);
+
+    /**
+     * Requests notification when data buffered by the driver/hardware has
+     * been played. If set_callback() has previously been called to enable
+     * non-blocking mode, the drain() must not block, instead it should return
+     * quickly and completion of the drain is notified through the callback.
+     * If set_callback() has not been called, the drain() must block until
+     * completion.
+     * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+     * data has been played.
+     * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+     * data for the current track has played to allow time for the framework
+     * to perform a gapless track switch.
+     *
+     * Drain must return immediately on stop() and flush() call
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type );
+
+    /**
+     * Notifies to the audio driver to flush the queued data. Stream must already
+     * be paused before calling flush().
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+   int (*flush)(struct audio_stream_out* stream);
+
+    /**
+     * Return a recent count of the number of audio frames presented to an external observer.
+     * This excludes frames which have been written but are still in the pipeline.
+     * The count is not reset to zero when output enters standby.
+     * Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+     * The returned count is expected to be 'recent',
+     * but does not need to be the most recent possible value.
+     * However, the associated time should correspond to whatever count is returned.
+     * Example:  assume that N+M frames have been presented, where M is a 'small' number.
+     * Then it is permissible to return N instead of N+M,
+     * and the timestamp should correspond to N rather than N+M.
+     * The terms 'recent' and 'small' are not defined.
+     * They reflect the quality of the implementation.
+     *
+     * 3.0 and higher only.
+     */
+    int (*get_presentation_position)(const struct audio_stream_out *stream,
+                               uint64_t *frames, struct timespec *timestamp);
+
+};
+typedef struct audio_stream_out audio_stream_out_t;
+
+struct audio_stream_in {
+    /**
+     * Common methods of the audio stream in.  This *must* be the first member of audio_stream_in
+     * as users of this structure will cast a audio_stream to audio_stream_in pointer in contexts
+     * where it's known the audio_stream references an audio_stream_in.
+     */
+    struct audio_stream common;
+
+    /** set the input gain for the audio driver. This method is for
+     *  for future use */
+    int (*set_gain)(struct audio_stream_in *stream, float gain);
+
+    /** Read audio buffer in from audio driver. Returns number of bytes read, or a
+     *  negative status_t. If at least one frame was read prior to the error,
+     *  read should return that byte count and then return an error in the subsequent call.
+     */
+    ssize_t (*read)(struct audio_stream_in *stream, void* buffer,
+                    size_t bytes);
+
+    /**
+     * Return the amount of input frames lost in the audio driver since the
+     * last call of this function.
+     * Audio driver is expected to reset the value to 0 and restart counting
+     * upon returning the current value by this function call.
+     * Such loss typically occurs when the user space process is blocked
+     * longer than the capacity of audio driver buffers.
+     *
+     * Unit: the number of input audio frames
+     */
+    uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream);
+};
+typedef struct audio_stream_in audio_stream_in_t;
+
+/**
+ * return the frame size (number of bytes per sample).
+ *
+ * Deprecated: use audio_stream_out_frame_size() or audio_stream_in_frame_size() instead.
+ */
+__attribute__((__deprecated__))
+static inline size_t audio_stream_frame_size(const struct audio_stream *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->get_format(s);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return popcount(s->get_channels(s)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+/**
+ * return the frame size (number of bytes per sample) of an output stream.
+ */
+static inline size_t audio_stream_out_frame_size(const struct audio_stream_out *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->common.get_format(&s->common);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return audio_channel_count_from_out_mask(s->common.get_channels(&s->common)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+/**
+ * return the frame size (number of bytes per sample) of an input stream.
+ */
+static inline size_t audio_stream_in_frame_size(const struct audio_stream_in *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->common.get_format(&s->common);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return audio_channel_count_from_in_mask(s->common.get_channels(&s->common)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+/**********************************************************************/
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+struct audio_module {
+    struct hw_module_t common;
+};
+
+struct audio_hw_device {
+    /**
+     * Common methods of the audio device.  This *must* be the first member of audio_hw_device
+     * as users of this structure will cast a hw_device_t to audio_hw_device pointer in contexts
+     * where it's known the hw_device_t references an audio_hw_device.
+     */
+    struct hw_device_t common;
+
+    /**
+     * used by audio flinger to enumerate what devices are supported by
+     * each audio_hw_device implementation.
+     *
+     * Return value is a bitmask of 1 or more values of audio_devices_t
+     *
+     * NOTE: audio HAL implementations starting with
+     * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+     * All supported devices should be listed in audio_policy.conf
+     * file and the audio policy manager must choose the appropriate
+     * audio module based on information in this file.
+     */
+    uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
+
+    /**
+     * check to see if the audio hardware interface has been initialized.
+     * returns 0 on success, -ENODEV on failure.
+     */
+    int (*init_check)(const struct audio_hw_device *dev);
+
+    /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
+    int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
+
+    /**
+     * set the audio volume for all audio activities other than voice call.
+     * Range between 0.0 and 1.0. If any value other than 0 is returned,
+     * the software mixer will emulate this capability.
+     */
+    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
+
+    /**
+     * Get the current master volume value for the HAL, if the HAL supports
+     * master volume control.  AudioFlinger will query this value from the
+     * primary audio HAL when the service starts and use the value for setting
+     * the initial master volume across all HALs.  HALs which do not support
+     * this method may leave it set to NULL.
+     */
+    int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
+
+    /**
+     * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+     * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+     * playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+     */
+    int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
+
+    /* mic mute */
+    int (*set_mic_mute)(struct audio_hw_device *dev, bool state);
+    int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state);
+
+    /* set/get global audio parameters */
+    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
+
+    /*
+     * Returns a pointer to a heap allocated string. The caller is responsible
+     * for freeing the memory for it using free().
+     */
+    char * (*get_parameters)(const struct audio_hw_device *dev,
+                             const char *keys);
+
+    /* Returns audio input buffer size according to parameters passed or
+     * 0 if one of the parameters is not supported.
+     * See also get_buffer_size which is for a particular stream.
+     */
+    size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
+                                    const struct audio_config *config);
+
+    /** This method creates and opens the audio hardware output stream.
+     * The "address" parameter qualifies the "devices" audio device type if needed.
+     * The format format depends on the device type:
+     * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC"
+     * - USB devices use the ALSA card and device numbers in the form  "card=X;device=Y"
+     * - Other devices may use a number or any other string.
+     */
+
+    int (*open_output_stream)(struct audio_hw_device *dev,
+                              audio_io_handle_t handle,
+                              audio_devices_t devices,
+                              audio_output_flags_t flags,
+                              struct audio_config *config,
+                              struct audio_stream_out **stream_out,
+                              const char *address);
+
+    void (*close_output_stream)(struct audio_hw_device *dev,
+                                struct audio_stream_out* stream_out);
+
+    /** This method creates and opens the audio hardware input stream */
+    int (*open_input_stream)(struct audio_hw_device *dev,
+                             audio_io_handle_t handle,
+                             audio_devices_t devices,
+                             struct audio_config *config,
+                             struct audio_stream_in **stream_in,
+                             audio_input_flags_t flags,
+                             const char *address,
+                             audio_source_t source);
+
+    void (*close_input_stream)(struct audio_hw_device *dev,
+                               struct audio_stream_in *stream_in);
+
+    /** This method dumps the state of the audio hardware */
+    int (*dump)(const struct audio_hw_device *dev, int fd);
+
+    /**
+     * set the audio mute status for all audio activities.  If any value other
+     * than 0 is returned, the software mixer will emulate this capability.
+     */
+    int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
+
+    /**
+     * Get the current master mute status for the HAL, if the HAL supports
+     * master mute control.  AudioFlinger will query this value from the primary
+     * audio HAL when the service starts and use the value for setting the
+     * initial master mute across all HALs.  HALs which do not support this
+     * method may leave it set to NULL.
+     */
+    int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);
+
+    /**
+     * Routing control
+     */
+
+    /* Creates an audio patch between several source and sink ports.
+     * The handle is allocated by the HAL and should be unique for this
+     * audio HAL module. */
+    int (*create_audio_patch)(struct audio_hw_device *dev,
+                               unsigned int num_sources,
+                               const struct audio_port_config *sources,
+                               unsigned int num_sinks,
+                               const struct audio_port_config *sinks,
+                               audio_patch_handle_t *handle);
+
+    /* Release an audio patch */
+    int (*release_audio_patch)(struct audio_hw_device *dev,
+                               audio_patch_handle_t handle);
+
+    /* Fills the list of supported attributes for a given audio port.
+     * As input, "port" contains the information (type, role, address etc...)
+     * needed by the HAL to identify the port.
+     * As output, "port" contains possible attributes (sampling rates, formats,
+     * channel masks, gain controllers...) for this port.
+     */
+    int (*get_audio_port)(struct audio_hw_device *dev,
+                          struct audio_port *port);
+
+    /* Set audio port configuration */
+    int (*set_audio_port_config)(struct audio_hw_device *dev,
+                         const struct audio_port_config *config);
+
+};
+typedef struct audio_hw_device audio_hw_device_t;
+
+/** convenience API for opening and closing a supported device */
+
+static inline int audio_hw_device_open(const struct hw_module_t* module,
+                                       struct audio_hw_device** device)
+{
+    return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
+                                 (struct hw_device_t**)device);
+}
+
+static inline int audio_hw_device_close(struct audio_hw_device* device)
+{
+    return device->common.close(&device->common);
+}
+
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_INTERFACE_H
diff --git a/bluez/repo/android/hardware/audio_effect.h b/bluez/repo/android/hardware/audio_effect.h
new file mode 100644
index 0000000..69ea896
--- /dev/null
+++ b/bluez/repo/android/hardware/audio_effect.h
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_EFFECT_H
+#define ANDROID_AUDIO_EFFECT_H
+
+#include <errno.h>
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+
+__BEGIN_DECLS
+
+
+/////////////////////////////////////////////////
+//      Common Definitions
+/////////////////////////////////////////////////
+
+//
+//--- Effect descriptor structure effect_descriptor_t
+//
+
+// Unique effect ID (can be generated from the following site:
+//  http://www.itu.int/ITU-T/asn1/uuid.html)
+// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
+// - When used for effect type and the engine is implementing and effect corresponding to a standard
+// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
+// - When used as uuid, it should be a unique UUID for this particular implementation.
+typedef struct effect_uuid_s {
+    uint32_t timeLow;
+    uint16_t timeMid;
+    uint16_t timeHiAndVersion;
+    uint16_t clockSeq;
+    uint8_t node[6];
+} effect_uuid_t;
+
+// Maximum length of character strings in structures defines by this API.
+#define EFFECT_STRING_LEN_MAX 64
+
+// NULL UUID definition (matches SL_IID_NULL_)
+#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
+                                  { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
+static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
+static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
+static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
+
+
+// The effect descriptor contains necessary information to facilitate the enumeration of the effect
+// engines present in a library.
+typedef struct effect_descriptor_s {
+    effect_uuid_t type;     // UUID of to the OpenSL ES interface implemented by this effect
+    effect_uuid_t uuid;     // UUID for this particular implementation
+    uint32_t apiVersion;    // Version of the effect control API implemented
+    uint32_t flags;         // effect engine capabilities/requirements flags (see below)
+    uint16_t cpuLoad;       // CPU load indication (see below)
+    uint16_t memoryUsage;   // Data Memory usage (see below)
+    char    name[EFFECT_STRING_LEN_MAX];   // human readable effect name
+    char    implementor[EFFECT_STRING_LEN_MAX];    // human readable effect implementor name
+} effect_descriptor_t;
+
+// CPU load and memory usage indication: each effect implementation must provide an indication of
+// its CPU and memory usage for the audio effect framework to limit the number of effects
+// instantiated at a given time on a given platform.
+// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
+// The memory usage is expressed in KB and includes only dynamically allocated memory
+
+// Definitions for flags field of effect descriptor.
+//  +---------------------------+-----------+-----------------------------------
+//  | description               | bits      | values
+//  +---------------------------+-----------+-----------------------------------
+//  | connection mode           | 0..2      | 0 insert: after track process
+//  |                           |           | 1 auxiliary: connect to track auxiliary
+//  |                           |           |  output and use send level
+//  |                           |           | 2 replace: replaces track process function;
+//  |                           |           |   must implement SRC, volume and mono to stereo.
+//  |                           |           | 3 pre processing: applied below audio HAL on input
+//  |                           |           | 4 post processing: applied below audio HAL on output
+//  |                           |           | 5 - 7 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | insertion preference      | 3..5      | 0 none
+//  |                           |           | 1 first of the chain
+//  |                           |           | 2 last of the chain
+//  |                           |           | 3 exclusive (only effect in the insert chain)
+//  |                           |           | 4..7 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Volume management         | 6..8      | 0 none
+//  |                           |           | 1 implements volume control
+//  |                           |           | 2 requires volume indication
+//  |                           |           | 4 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Device indication         | 9..11     | 0 none
+//  |                           |           | 1 requires device updates
+//  |                           |           | 2, 4 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Sample input mode         | 12..13    | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+//  |                           |           |   command must specify a buffer descriptor
+//  |                           |           | 2 provider: process() function uses the
+//  |                           |           |   bufferProvider indicated by the
+//  |                           |           |   EFFECT_CMD_SET_CONFIG command to request input.
+//  |                           |           |   buffers.
+//  |                           |           | 3 both: both input modes are supported
+//  +---------------------------+-----------+-----------------------------------
+//  | Sample output mode        | 14..15    | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+//  |                           |           |   command must specify a buffer descriptor
+//  |                           |           | 2 provider: process() function uses the
+//  |                           |           |   bufferProvider indicated by the
+//  |                           |           |   EFFECT_CMD_SET_CONFIG command to request output
+//  |                           |           |   buffers.
+//  |                           |           | 3 both: both output modes are supported
+//  +---------------------------+-----------+-----------------------------------
+//  | Hardware acceleration     | 16..17    | 0 No hardware acceleration
+//  |                           |           | 1 non tunneled hw acceleration: the process() function
+//  |                           |           |   reads the samples, send them to HW accelerated
+//  |                           |           |   effect processor, reads back the processed samples
+//  |                           |           |   and returns them to the output buffer.
+//  |                           |           | 2 tunneled hw acceleration: the process() function is
+//  |                           |           |   transparent. The effect interface is only used to
+//  |                           |           |   control the effect engine. This mode is relevant for
+//  |                           |           |   global effects actually applied by the audio
+//  |                           |           |   hardware on the output stream.
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio Mode indication     | 18..19    | 0 none
+//  |                           |           | 1 requires audio mode updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio source indication   | 20..21    | 0 none
+//  |                           |           | 1 requires audio source updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Effect offload supported  | 22        | 0 The effect cannot be offloaded to an audio DSP
+//  |                           |           | 1 The effect can be offloaded to an audio DSP
+//  +---------------------------+-----------+-----------------------------------
+
+// Insert mode
+#define EFFECT_FLAG_TYPE_SHIFT          0
+#define EFFECT_FLAG_TYPE_SIZE           3
+#define EFFECT_FLAG_TYPE_MASK           (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \
+                                            << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_INSERT         (0 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_AUXILIARY      (1 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_REPLACE        (2 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_PRE_PROC       (3 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_POST_PROC      (4 << EFFECT_FLAG_TYPE_SHIFT)
+
+// Insert preference
+#define EFFECT_FLAG_INSERT_SHIFT        (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE)
+#define EFFECT_FLAG_INSERT_SIZE         3
+#define EFFECT_FLAG_INSERT_MASK         (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \
+                                            << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_ANY          (0 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_FIRST        (1 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_LAST         (2 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_EXCLUSIVE    (3 << EFFECT_FLAG_INSERT_SHIFT)
+
+
+// Volume control
+#define EFFECT_FLAG_VOLUME_SHIFT        (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE)
+#define EFFECT_FLAG_VOLUME_SIZE         3
+#define EFFECT_FLAG_VOLUME_MASK         (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \
+                                            << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_CTRL         (1 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_IND          (2 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_NONE         (0 << EFFECT_FLAG_VOLUME_SHIFT)
+
+// Device indication
+#define EFFECT_FLAG_DEVICE_SHIFT        (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE)
+#define EFFECT_FLAG_DEVICE_SIZE         3
+#define EFFECT_FLAG_DEVICE_MASK         (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \
+                                            << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_IND          (1 << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_NONE         (0 << EFFECT_FLAG_DEVICE_SHIFT)
+
+// Sample input modes
+#define EFFECT_FLAG_INPUT_SHIFT         (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE)
+#define EFFECT_FLAG_INPUT_SIZE          2
+#define EFFECT_FLAG_INPUT_MASK          (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \
+                                            << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_DIRECT        (1 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_PROVIDER      (2 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_BOTH          (3 << EFFECT_FLAG_INPUT_SHIFT)
+
+// Sample output modes
+#define EFFECT_FLAG_OUTPUT_SHIFT        (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE)
+#define EFFECT_FLAG_OUTPUT_SIZE         2
+#define EFFECT_FLAG_OUTPUT_MASK         (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \
+                                            << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_DIRECT       (1 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_PROVIDER     (2 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_BOTH         (3 << EFFECT_FLAG_OUTPUT_SHIFT)
+
+// Hardware acceleration mode
+#define EFFECT_FLAG_HW_ACC_SHIFT        (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE)
+#define EFFECT_FLAG_HW_ACC_SIZE         2
+#define EFFECT_FLAG_HW_ACC_MASK         (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \
+                                            << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_SIMPLE       (1 << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_TUNNEL       (2 << EFFECT_FLAG_HW_ACC_SHIFT)
+
+// Audio mode indication
+#define EFFECT_FLAG_AUDIO_MODE_SHIFT    (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE)
+#define EFFECT_FLAG_AUDIO_MODE_SIZE     2
+#define EFFECT_FLAG_AUDIO_MODE_MASK     (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \
+                                            << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_IND      (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_NONE     (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+
+// Audio source indication
+#define EFFECT_FLAG_AUDIO_SOURCE_SHIFT  (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE)
+#define EFFECT_FLAG_AUDIO_SOURCE_SIZE   2
+#define EFFECT_FLAG_AUDIO_SOURCE_MASK   (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \
+                                          << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_IND    (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_NONE   (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+
+// Effect offload indication
+#define EFFECT_FLAG_OFFLOAD_SHIFT       (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \
+                                                    EFFECT_FLAG_AUDIO_SOURCE_SIZE)
+#define EFFECT_FLAG_OFFLOAD_SIZE        1
+#define EFFECT_FLAG_OFFLOAD_MASK        (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \
+                                          << EFFECT_FLAG_OFFLOAD_SHIFT)
+#define EFFECT_FLAG_OFFLOAD_SUPPORTED   (1 << EFFECT_FLAG_OFFLOAD_SHIFT)
+
+#define EFFECT_MAKE_API_VERSION(M, m)  (((M)<<16) | ((m) & 0xFFFF))
+#define EFFECT_API_VERSION_MAJOR(v)    ((v)>>16)
+#define EFFECT_API_VERSION_MINOR(v)    ((m) & 0xFFFF)
+
+
+
+/////////////////////////////////////////////////
+//      Effect control interface
+/////////////////////////////////////////////////
+
+// Effect control interface version 2.0
+#define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0)
+
+// Effect control interface structure: effect_interface_s
+// The effect control interface is exposed by each effect engine implementation. It consists of
+// a set of functions controlling the configuration, activation and process of the engine.
+// The functions are grouped in a structure of type effect_interface_s.
+//
+// Effect control interface handle: effect_handle_t
+// The effect_handle_t serves two purposes regarding the implementation of the effect engine:
+// - 1 it is the address of a pointer to an effect_interface_s structure where the functions
+// of the effect control API for a particular effect are located.
+// - 2 it is the address of the context of a particular effect instance.
+// A typical implementation in the effect library would define a structure as follows:
+// struct effect_module_s {
+//        const struct effect_interface_s *itfe;
+//        effect_config_t config;
+//        effect_context_t context;
+// }
+// The implementation of EffectCreate() function would then allocate a structure of this
+// type and return its address as effect_handle_t
+typedef struct effect_interface_s **effect_handle_t;
+
+
+// Forward definition of type audio_buffer_t
+typedef struct audio_buffer_s audio_buffer_t;
+
+
+
+
+
+
+// Effect control interface definition
+struct effect_interface_s {
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       process
+    //
+    //    Description:    Effect process function. Takes input samples as specified
+    //          (count and location) in input buffer descriptor and output processed
+    //          samples as specified in output buffer descriptor. If the buffer descriptor
+    //          is not specified the function must use either the buffer or the
+    //          buffer provider function installed by the EFFECT_CMD_SET_CONFIG command.
+    //          The effect framework will call the process() function after the EFFECT_CMD_ENABLE
+    //          command is received and until the EFFECT_CMD_DISABLE is received. When the engine
+    //          receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
+    //          and when done indicate that it is OK to stop calling the process() function by
+    //          returning the -ENODATA status.
+    //
+    //    NOTE: the process() function implementation should be "real-time safe" that is
+    //      it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
+    //      pthread_cond_wait/pthread_mutex_lock...
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          inBuffer:   buffer descriptor indicating where to read samples to process.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+    //
+    //          outBuffer:   buffer descriptor indicating where to write processed samples.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+    //
+    //    Output:
+    //        returned value:    0 successful operation
+    //                          -ENODATA the engine has finished the disable phase and the framework
+    //                                  can stop calling process()
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid input/output buffer description
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*process)(effect_handle_t self,
+                       audio_buffer_t *inBuffer,
+                       audio_buffer_t *outBuffer);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       command
+    //
+    //    Description:    Send a command and receive a response to/from effect engine.
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          cmdCode:    command code: the command can be a standardized command defined in
+    //              effect_command_e (see below) or a proprietary command.
+    //          cmdSize:    size of command in bytes
+    //          pCmdData:   pointer to command data
+    //          pReplyData: pointer to reply data
+    //
+    //    Input/Output:
+    //          replySize: maximum size of reply data as input
+    //                      actual size of reply data as output
+    //
+    //    Output:
+    //          returned value: 0       successful operation
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid command/reply size or format according to command code
+    //              The return code should be restricted to indicate problems related to the this
+    //              API specification. Status related to the execution of a particular command should be
+    //              indicated as part of the reply field.
+    //
+    //          *pReplyData updated with command response
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*command)(effect_handle_t self,
+                       uint32_t cmdCode,
+                       uint32_t cmdSize,
+                       void *pCmdData,
+                       uint32_t *replySize,
+                       void *pReplyData);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        get_descriptor
+    //
+    //    Description:    Returns the effect descriptor
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //
+    //    Input/Output:
+    //          pDescriptor:    address where to return the effect descriptor.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -EINVAL     invalid interface handle or invalid pDescriptor
+    //        *pDescriptor:     updated with the effect descriptor.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*get_descriptor)(effect_handle_t self,
+                              effect_descriptor_t *pDescriptor);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       process_reverse
+    //
+    //    Description:    Process reverse stream function. This function is used to pass
+    //          a reference stream to the effect engine. If the engine does not need a reference
+    //          stream, this function pointer can be set to NULL.
+    //          This function would typically implemented by an Echo Canceler.
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          inBuffer:   buffer descriptor indicating where to read samples to process.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+    //
+    //          outBuffer:   buffer descriptor indicating where to write processed samples.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+    //              If the buffer and buffer provider in the configuration received by
+    //              EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse
+    //              stream data
+    //
+    //    Output:
+    //        returned value:    0 successful operation
+    //                          -ENODATA the engine has finished the disable phase and the framework
+    //                                  can stop calling process_reverse()
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid input/output buffer description
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*process_reverse)(effect_handle_t self,
+                               audio_buffer_t *inBuffer,
+                               audio_buffer_t *outBuffer);
+};
+
+
+//
+//--- Standardized command codes for command() function
+//
+enum effect_command_e {
+   EFFECT_CMD_INIT,                 // initialize effect engine
+   EFFECT_CMD_SET_CONFIG,           // configure effect engine (see effect_config_t)
+   EFFECT_CMD_RESET,                // reset effect engine
+   EFFECT_CMD_ENABLE,               // enable effect process
+   EFFECT_CMD_DISABLE,              // disable effect process
+   EFFECT_CMD_SET_PARAM,            // set parameter immediately (see effect_param_t)
+   EFFECT_CMD_SET_PARAM_DEFERRED,   // set parameter deferred
+   EFFECT_CMD_SET_PARAM_COMMIT,     // commit previous set parameter deferred
+   EFFECT_CMD_GET_PARAM,            // get parameter
+   EFFECT_CMD_SET_DEVICE,           // set audio device (see audio.h, audio_devices_t)
+   EFFECT_CMD_SET_VOLUME,           // set volume
+   EFFECT_CMD_SET_AUDIO_MODE,       // set the audio mode (normal, ring, ...)
+   EFFECT_CMD_SET_CONFIG_REVERSE,   // configure effect engine reverse stream(see effect_config_t)
+   EFFECT_CMD_SET_INPUT_DEVICE,     // set capture device (see audio.h, audio_devices_t)
+   EFFECT_CMD_GET_CONFIG,           // read effect engine configuration
+   EFFECT_CMD_GET_CONFIG_REVERSE,   // read configure effect engine reverse stream configuration
+   EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature.
+   EFFECT_CMD_GET_FEATURE_CONFIG,   // get current feature configuration
+   EFFECT_CMD_SET_FEATURE_CONFIG,   // set current feature configuration
+   EFFECT_CMD_SET_AUDIO_SOURCE,     // set the audio source (see audio.h, audio_source_t)
+   EFFECT_CMD_OFFLOAD,              // set if effect thread is an offload one,
+                                    // send the ioHandle of the effect thread
+   EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
+};
+
+//==================================================================================================
+// command: EFFECT_CMD_INIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Initialize effect engine: All configurations return to default
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_RESET
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Reset the effect engine. Keep configuration but resets state and buffer content
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_ENABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Enable the process. Called by the framework before the first call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_DISABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Disable the process. Called by the framework after the last call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter and apply it immediately
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_DEFERRED
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_COMMIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_GET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Get a parameter value
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param
+//  data: effect_param_t + param
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//==================================================================================================
+// command: EFFECT_CMD_SET_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t
+//  for device values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_VOLUME
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set and get volume. Used by audio framework to delegate volume control to effect engine.
+//  The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in
+//  its descriptor to receive this command before every call to process() function
+//  If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
+//  the volume that should be applied before the effect is processed. The overall volume (the volume
+//  actually applied by the effect engine multiplied by the returned value) should match the value
+//  indicated in the command.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: n * sizeof(uint32_t)
+//  data: volume for each channel defined in effect_config_t for output buffer expressed in
+//      8.24 fixed point format
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: n * sizeof(uint32_t) / 0
+//  data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
+//              volume for each channel defined in effect_config_t for output buffer expressed in
+//              8.24 fixed point format
+//        - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
+//              N/A
+//  It is legal to receive a null pointer as pReplyData in which case the effect framework has
+//  delegated volume control to another effect
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_MODE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
+//  descriptor to receive this command when the audio mode changes.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: audio_mode_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers of reverse stream.
+//  An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_INPUT_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the capture device the audio input path is connected to. See audio.h, audio_devices_t
+//  for device values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Read audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Read audio parameters configurations for input and output buffers of reverse stream
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Queries for supported configurations for a particular feature (e.g. get the supported
+// combinations of main and auxiliary channels for a noise suppressor).
+// The command parameter is the feature identifier (See effect_feature_e for a list of defined
+// features) followed by the maximum number of configuration descriptor to return.
+// The reply is composed of:
+//  - status (uint32_t):
+//          - 0 if feature is supported
+//          - -ENOSYS if the feature is not supported,
+//          - -ENOMEM if the feature is supported but the total number of supported configurations
+//          exceeds the maximum number indicated by the caller.
+//  - total number of supported configurations (uint32_t)
+//  - an array of configuration descriptors.
+// The actual number of descriptors returned must not exceed the maximum number indicated by
+// the caller.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 2 x sizeof(uint32_t)
+//  data: effect_feature_e + maximum number of configurations to return
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 2 x sizeof(uint32_t) + n x sizeof (<config descriptor>)
+//  data: status + total number of configurations supported + array of n config descriptors
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Retrieves current configuration for a given feature.
+// The reply status is:
+//      - 0 if feature is supported
+//      - -ENOSYS if the feature is not supported,
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: effect_feature_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t) + sizeof (<config descriptor>)
+//  data: status + config descriptor
+//==================================================================================================
+// command: EFFECT_CMD_SET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Sets current configuration for a given feature.
+// The reply status is:
+//      - 0 if feature is supported
+//      - -ENOSYS if the feature is not supported,
+//      - -EINVAL if the configuration is invalid
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t) + sizeof (<config descriptor>)
+//  data: effect_feature_e + config descriptor
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_SOURCE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio source the capture path is configured for (Camcorder, voice recognition...).
+//  See audio.h, audio_source_t for values.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_OFFLOAD
+//--------------------------------------------------------------------------------------------------
+// description:
+//  1.indicate if the playback thread the effect is attached to is offloaded or not
+//  2.update the io handle of the playback thread the effect is attached to
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_offload_param_t)
+//  data: effect_offload_param_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// command: EFFECT_CMD_FIRST_PROPRIETARY
+//--------------------------------------------------------------------------------------------------
+// description:
+//  All proprietary effect commands must use command codes above this value. The size and format of
+//  command and response fields is free in this case
+//==================================================================================================
+
+
+// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
+// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
+// regard to the channel mask definition in audio.h, audio_channel_mask_t e.g :
+// Stereo: left, right
+// 5 point 1: front left, front right, front center, low frequency, back left, back right
+// The buffer size is expressed in frame count, a frame being composed of samples for all
+// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
+// definition
+struct audio_buffer_s {
+    size_t   frameCount;        // number of frames in buffer
+    union {
+        void*       raw;        // raw pointer to start of buffer
+        int32_t*    s32;        // pointer to signed 32 bit data at start of buffer
+        int16_t*    s16;        // pointer to signed 16 bit data at start of buffer
+        uint8_t*    u8;         // pointer to unsigned 8 bit data at start of buffer
+    };
+};
+
+// The buffer_provider_s structure contains functions that can be used
+// by the effect engine process() function to query and release input
+// or output audio buffer.
+// The getBuffer() function is called to retrieve a buffer where data
+// should read from or written to by process() function.
+// The releaseBuffer() function MUST be called when the buffer retrieved
+// with getBuffer() is not needed anymore.
+// The process function should use the buffer provider mechanism to retrieve
+// input or output buffer if the inBuffer or outBuffer passed as argument is NULL
+// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG
+// command did not specify an audio buffer.
+
+typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer);
+
+typedef struct buffer_provider_s {
+    buffer_function_t getBuffer;       // retrieve next buffer
+    buffer_function_t releaseBuffer;   // release used buffer
+    void       *cookie;                // for use by client of buffer provider functions
+} buffer_provider_t;
+
+
+// The buffer_config_s structure specifies the input or output audio format
+// to be used by the effect engine. It is part of the effect_config_t
+// structure that defines both input and output buffer configurations and is
+// passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command.
+typedef struct buffer_config_s {
+    audio_buffer_t  buffer;     // buffer for use by process() function if not passed explicitly
+    uint32_t   samplingRate;    // sampling rate
+    uint32_t   channels;        // channel mask (see audio_channel_mask_t in audio.h)
+    buffer_provider_t bufferProvider;   // buffer provider
+    uint8_t    format;          // Audio format (see audio_format_t in audio.h)
+    uint8_t    accessMode;      // read/write or accumulate in buffer (effect_buffer_access_e)
+    uint16_t   mask;            // indicates which of the above fields is valid
+} buffer_config_t;
+
+// Values for "accessMode" field of buffer_config_t:
+//   overwrite, read only, accumulate (read/modify/write)
+enum effect_buffer_access_e {
+    EFFECT_BUFFER_ACCESS_WRITE,
+    EFFECT_BUFFER_ACCESS_READ,
+    EFFECT_BUFFER_ACCESS_ACCUMULATE
+
+};
+
+// feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command
+enum effect_feature_e {
+    EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor)
+    EFFECT_FEATURE_CNT
+};
+
+// EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination
+// of main and auxiliary channels supported
+typedef struct channel_config_s {
+    audio_channel_mask_t main_channels; // channel mask for main channels
+    audio_channel_mask_t aux_channels;  // channel mask for auxiliary channels
+} channel_config_t;
+
+
+// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
+// in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command
+#define EFFECT_CONFIG_BUFFER    0x0001  // buffer field must be taken into account
+#define EFFECT_CONFIG_SMP_RATE  0x0002  // samplingRate field must be taken into account
+#define EFFECT_CONFIG_CHANNELS  0x0004  // channels field must be taken into account
+#define EFFECT_CONFIG_FORMAT    0x0008  // format field must be taken into account
+#define EFFECT_CONFIG_ACC_MODE  0x0010  // accessMode field must be taken into account
+#define EFFECT_CONFIG_PROVIDER  0x0020  // bufferProvider field must be taken into account
+#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \
+                           EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
+                           EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
+
+
+// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG
+// command to configure audio parameters and buffers for effect engine input and output.
+typedef struct effect_config_s {
+    buffer_config_t   inputCfg;
+    buffer_config_t   outputCfg;
+} effect_config_t;
+
+
+// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
+// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
+// psize and vsize represent the actual size of parameter and value.
+//
+// NOTE: the start of value field inside the data field is always on a 32 bit boundary:
+//
+//  +-----------+
+//  | status    | sizeof(int)
+//  +-----------+
+//  | psize     | sizeof(int)
+//  +-----------+
+//  | vsize     | sizeof(int)
+//  +-----------+
+//  |           |   |           |
+//  ~ parameter ~   > psize     |
+//  |           |   |           >  ((psize - 1)/sizeof(int) + 1) * sizeof(int)
+//  +-----------+               |
+//  | padding   |               |
+//  +-----------+
+//  |           |   |
+//  ~ value     ~   > vsize
+//  |           |   |
+//  +-----------+
+
+typedef struct effect_param_s {
+    int32_t     status;     // Transaction status (unused for command, used for reply)
+    uint32_t    psize;      // Parameter size
+    uint32_t    vsize;      // Value size
+    char        data[];     // Start of Parameter + Value data
+} effect_param_t;
+
+// structure used by EFFECT_CMD_OFFLOAD command
+typedef struct effect_offload_param_s {
+    bool isOffload;         // true if the playback thread the effect is attached to is offloaded
+    int ioHandle;           // io handle of the playback thread the effect is attached to
+} effect_offload_param_t;
+
+
+/////////////////////////////////////////////////
+//      Effect library interface
+/////////////////////////////////////////////////
+
+// Effect library interface version 3.0
+// Note that EffectsFactory.c only checks the major version component, so changes to the minor
+// number can only be used for fully backwards compatible changes
+#define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0)
+
+#define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T'))
+
+// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM
+// and the fields of this data structure must begin with audio_effect_library_t
+
+typedef struct audio_effect_library_s {
+    // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
+    uint32_t tag;
+    // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor
+    uint32_t version;
+    // Name of this library
+    const char *name;
+    // Author/owner/implementor of the library
+    const char *implementor;
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        create_effect
+    //
+    //    Description:    Creates an effect engine of the specified implementation uuid and
+    //          returns an effect control interface on this engine. The function will allocate the
+    //          resources for an instance of the requested effect engine and return
+    //          a handle on the effect control interface.
+    //
+    //    Input:
+    //          uuid:    pointer to the effect uuid.
+    //          sessionId:  audio session to which this effect instance will be attached. All effects
+    //              created with the same session ID are connected in series and process the same signal
+    //              stream. Knowing that two effects are part of the same effect chain can help the
+    //              library implement some kind of optimizations.
+    //          ioId:   identifies the output or input stream this effect is directed to at audio HAL.
+    //              For future use especially with tunneled HW accelerated effects
+    //
+    //    Input/Output:
+    //          pHandle:        address where to return the effect interface handle.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid pEffectUuid or pHandle
+    //                          -ENOENT     no effect with this uuid found
+    //        *pHandle:         updated with the effect interface handle.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*create_effect)(const effect_uuid_t *uuid,
+                             int32_t sessionId,
+                             int32_t ioId,
+                             effect_handle_t *pHandle);
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        release_effect
+    //
+    //    Description:    Releases the effect engine whose handle is given as argument.
+    //          All resources allocated to this particular instance of the effect are
+    //          released.
+    //
+    //    Input:
+    //          handle:         handle on the effect interface to be released.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid interface handle
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*release_effect)(effect_handle_t handle);
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        get_descriptor
+    //
+    //    Description:    Returns the descriptor of the effect engine which implementation UUID is
+    //          given as argument.
+    //
+    //    Input/Output:
+    //          uuid:           pointer to the effect uuid.
+    //          pDescriptor:    address where to return the effect descriptor.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid pDescriptor or uuid
+    //        *pDescriptor:     updated with the effect descriptor.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*get_descriptor)(const effect_uuid_t *uuid,
+                              effect_descriptor_t *pDescriptor);
+} audio_effect_library_t;
+
+// Name of the hal_module_info
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM         AELI
+
+// Name of the hal_module_info as a string
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR  "AELI"
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_EFFECT_H
diff --git a/bluez/repo/android/hardware/bluetooth.h b/bluez/repo/android/hardware/bluetooth.h
new file mode 100644
index 0000000..74cd1fc
--- /dev/null
+++ b/bluez/repo/android/hardware/bluetooth.h
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BLUETOOTH_H
+#define ANDROID_INCLUDE_BLUETOOTH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+/**
+ * The Bluetooth Hardware Module ID
+ */
+
+#define BT_HARDWARE_MODULE_ID "bluetooth"
+#define BT_STACK_MODULE_ID "bluetooth"
+#define BT_STACK_TEST_MODULE_ID "bluetooth_test"
+
+
+/* Bluetooth profile interface IDs */
+
+#define BT_PROFILE_HANDSFREE_ID "handsfree"
+#define BT_PROFILE_HANDSFREE_CLIENT_ID "handsfree_client"
+#define BT_PROFILE_ADVANCED_AUDIO_ID "a2dp"
+#define BT_PROFILE_ADVANCED_AUDIO_SINK_ID "a2dp_sink"
+#define BT_PROFILE_HEALTH_ID "health"
+#define BT_PROFILE_SOCKETS_ID "socket"
+#define BT_PROFILE_HIDHOST_ID "hidhost"
+#define BT_PROFILE_PAN_ID "pan"
+#define BT_PROFILE_MAP_CLIENT_ID "map_client"
+
+#define BT_PROFILE_GATT_ID "gatt"
+#define BT_PROFILE_AV_RC_ID "avrcp"
+#define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl"
+
+/** Bluetooth Address */
+typedef struct {
+    uint8_t address[6];
+} __attribute__((packed))bt_bdaddr_t;
+
+/** Bluetooth Device Name */
+typedef struct {
+    uint8_t name[249];
+} __attribute__((packed))bt_bdname_t;
+
+/** Bluetooth Adapter Visibility Modes*/
+typedef enum {
+    BT_SCAN_MODE_NONE,
+    BT_SCAN_MODE_CONNECTABLE,
+    BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+} bt_scan_mode_t;
+
+/** Bluetooth Adapter State */
+typedef enum {
+    BT_STATE_OFF,
+    BT_STATE_ON
+}   bt_state_t;
+
+/** Bluetooth Error Status */
+/** We need to build on this */
+
+typedef enum {
+    BT_STATUS_SUCCESS,
+    BT_STATUS_FAIL,
+    BT_STATUS_NOT_READY,
+    BT_STATUS_NOMEM,
+    BT_STATUS_BUSY,
+    BT_STATUS_DONE,        /* request already completed */
+    BT_STATUS_UNSUPPORTED,
+    BT_STATUS_PARM_INVALID,
+    BT_STATUS_UNHANDLED,
+    BT_STATUS_AUTH_FAILURE,
+    BT_STATUS_RMT_DEV_DOWN,
+    BT_STATUS_AUTH_REJECTED
+
+} bt_status_t;
+
+/** Bluetooth PinKey Code */
+typedef struct {
+    uint8_t pin[16];
+} __attribute__((packed))bt_pin_code_t;
+
+typedef struct {
+    uint8_t status;
+    uint8_t ctrl_state;     /* stack reported state */
+    uint64_t tx_time;       /* in ms */
+    uint64_t rx_time;       /* in ms */
+    uint64_t idle_time;     /* in ms */
+    uint64_t energy_used;   /* a product of mA, V and ms */
+} __attribute__((packed))bt_activity_energy_info;
+
+/** Bluetooth Adapter Discovery state */
+typedef enum {
+    BT_DISCOVERY_STOPPED,
+    BT_DISCOVERY_STARTED
+} bt_discovery_state_t;
+
+/** Bluetooth ACL connection state */
+typedef enum {
+    BT_ACL_STATE_CONNECTED,
+    BT_ACL_STATE_DISCONNECTED
+} bt_acl_state_t;
+
+/** Bluetooth 128-bit UUID */
+typedef struct {
+   uint8_t uu[16];
+} bt_uuid_t;
+
+/** Bluetooth SDP service record */
+typedef struct
+{
+   bt_uuid_t uuid;
+   uint16_t channel;
+   char name[256]; // what's the maximum length
+} bt_service_record_t;
+
+
+/** Bluetooth Remote Version info */
+typedef struct
+{
+   int version;
+   int sub_ver;
+   int manufacturer;
+} bt_remote_version_t;
+
+typedef struct
+{
+    uint8_t local_privacy_enabled;
+    uint8_t max_adv_instance;
+    uint8_t rpa_offload_supported;
+    uint8_t max_irk_list_size;
+    uint8_t max_adv_filter_supported;
+    uint8_t scan_result_storage_size_lobyte;
+    uint8_t scan_result_storage_size_hibyte;
+    uint8_t activity_energy_info_supported;
+}bt_local_le_features_t;
+
+/* Bluetooth Adapter and Remote Device property types */
+typedef enum {
+    /* Properties common to both adapter and remote device */
+    /**
+     * Description - Bluetooth Device Name
+     * Access mode - Adapter name can be GET/SET. Remote device can be GET
+     * Data type   - bt_bdname_t
+     */
+    BT_PROPERTY_BDNAME = 0x1,
+    /**
+     * Description - Bluetooth Device Address
+     * Access mode - Only GET.
+     * Data type   - bt_bdaddr_t
+     */
+    BT_PROPERTY_BDADDR,
+    /**
+     * Description - Bluetooth Service 128-bit UUIDs
+     * Access mode - Only GET.
+     * Data type   - Array of bt_uuid_t (Array size inferred from property length).
+     */
+    BT_PROPERTY_UUIDS,
+    /**
+     * Description - Bluetooth Class of Device as found in Assigned Numbers
+     * Access mode - Only GET.
+     * Data type   - uint32_t.
+     */
+    BT_PROPERTY_CLASS_OF_DEVICE,
+    /**
+     * Description - Device Type - BREDR, BLE or DUAL Mode
+     * Access mode - Only GET.
+     * Data type   - bt_device_type_t
+     */
+    BT_PROPERTY_TYPE_OF_DEVICE,
+    /**
+     * Description - Bluetooth Service Record
+     * Access mode - Only GET.
+     * Data type   - bt_service_record_t
+     */
+    BT_PROPERTY_SERVICE_RECORD,
+
+    /* Properties unique to adapter */
+    /**
+     * Description - Bluetooth Adapter scan mode
+     * Access mode - GET and SET
+     * Data type   - bt_scan_mode_t.
+     */
+    BT_PROPERTY_ADAPTER_SCAN_MODE,
+    /**
+     * Description - List of bonded devices
+     * Access mode - Only GET.
+     * Data type   - Array of bt_bdaddr_t of the bonded remote devices
+     *               (Array size inferred from property length).
+     */
+    BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+    /**
+     * Description - Bluetooth Adapter Discovery timeout (in seconds)
+     * Access mode - GET and SET
+     * Data type   - uint32_t
+     */
+    BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+
+    /* Properties unique to remote device */
+    /**
+     * Description - User defined friendly name of the remote device
+     * Access mode - GET and SET
+     * Data type   - bt_bdname_t.
+     */
+    BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+    /**
+     * Description - RSSI value of the inquired remote device
+     * Access mode - Only GET.
+     * Data type   - int32_t.
+     */
+    BT_PROPERTY_REMOTE_RSSI,
+    /**
+     * Description - Remote version info
+     * Access mode - SET/GET.
+     * Data type   - bt_remote_version_t.
+     */
+
+    BT_PROPERTY_REMOTE_VERSION_INFO,
+
+    /**
+     * Description - Local LE features
+     * Access mode - GET.
+     * Data type   - bt_local_le_features_t.
+     */
+    BT_PROPERTY_LOCAL_LE_FEATURES,
+
+    BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
+} bt_property_type_t;
+
+/** Bluetooth Adapter Property data structure */
+typedef struct
+{
+    bt_property_type_t type;
+    int len;
+    void *val;
+} bt_property_t;
+
+
+/** Bluetooth Device Type */
+typedef enum {
+    BT_DEVICE_DEVTYPE_BREDR = 0x1,
+    BT_DEVICE_DEVTYPE_BLE,
+    BT_DEVICE_DEVTYPE_DUAL
+} bt_device_type_t;
+/** Bluetooth Bond state */
+typedef enum {
+    BT_BOND_STATE_NONE,
+    BT_BOND_STATE_BONDING,
+    BT_BOND_STATE_BONDED
+} bt_bond_state_t;
+
+/** Bluetooth SSP Bonding Variant */
+typedef enum {
+    BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+    BT_SSP_VARIANT_PASSKEY_ENTRY,
+    BT_SSP_VARIANT_CONSENT,
+    BT_SSP_VARIANT_PASSKEY_NOTIFICATION
+} bt_ssp_variant_t;
+
+#define BT_MAX_NUM_UUIDS 32
+
+/** Bluetooth Interface callbacks */
+
+/** Bluetooth Enable/Disable Callback. */
+typedef void (*adapter_state_changed_callback)(bt_state_t state);
+
+/** GET/SET Adapter Properties callback */
+/* TODO: For the GET/SET property APIs/callbacks, we may need a session
+ * identifier to associate the call with the callback. This would be needed
+ * whenever more than one simultaneous instance of the same adapter_type
+ * is get/set.
+ *
+ * If this is going to be handled in the Java framework, then we do not need
+ * to manage sessions here.
+ */
+typedef void (*adapter_properties_callback)(bt_status_t status,
+                                               int num_properties,
+                                               bt_property_t *properties);
+
+/** GET/SET Remote Device Properties callback */
+/** TODO: For remote device properties, do not see a need to get/set
+ * multiple properties - num_properties shall be 1
+ */
+typedef void (*remote_device_properties_callback)(bt_status_t status,
+                                                       bt_bdaddr_t *bd_addr,
+                                                       int num_properties,
+                                                       bt_property_t *properties);
+
+/** New device discovered callback */
+/** If EIR data is not present, then BD_NAME and RSSI shall be NULL and -1
+ * respectively */
+typedef void (*device_found_callback)(int num_properties,
+                                         bt_property_t *properties);
+
+/** Discovery state changed callback */
+typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state);
+
+/** Bluetooth Legacy PinKey Request callback */
+typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr,
+                                        bt_bdname_t *bd_name, uint32_t cod);
+
+/** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/
+/** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT &
+ *  BT_SSP_PAIRING_PASSKEY_ENTRY */
+/* TODO: Passkey request callback shall not be needed for devices with display
+ * capability. We still need support this in the stack for completeness */
+typedef void (*ssp_request_callback)(bt_bdaddr_t *remote_bd_addr,
+                                        bt_bdname_t *bd_name,
+                                        uint32_t cod,
+                                        bt_ssp_variant_t pairing_variant,
+                                     uint32_t pass_key);
+
+/** Bluetooth Bond state changed callback */
+/* Invoked in response to create_bond, cancel_bond or remove_bond */
+typedef void (*bond_state_changed_callback)(bt_status_t status,
+                                               bt_bdaddr_t *remote_bd_addr,
+                                               bt_bond_state_t state);
+
+/** Bluetooth ACL connection state changed callback */
+typedef void (*acl_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr,
+                                            bt_acl_state_t state);
+
+typedef enum {
+    ASSOCIATE_JVM,
+    DISASSOCIATE_JVM
+} bt_cb_thread_evt;
+
+/** Thread Associate/Disassociate JVM Callback */
+/* Callback that is invoked by the callback thread to allow upper layer to attach/detach to/from
+ * the JVM */
+typedef void (*callback_thread_event)(bt_cb_thread_evt evt);
+
+/** Bluetooth Test Mode Callback */
+/* Receive any HCI event from controller. Must be in DUT Mode for this callback to be received */
+typedef void (*dut_mode_recv_callback)(uint16_t opcode, uint8_t *buf, uint8_t len);
+
+/* LE Test mode callbacks
+* This callback shall be invoked whenever the le_tx_test, le_rx_test or le_test_end is invoked
+* The num_packets is valid only for le_test_end command */
+typedef void (*le_test_mode_callback)(bt_status_t status, uint16_t num_packets);
+
+/** Callback invoked when energy details are obtained */
+/* Ctrl_state-Current controller state-Active-1,scan-2,or idle-3 state as defined by HCI spec.
+ * If the ctrl_state value is 0, it means the API call failed
+ * Time values-In milliseconds as returned by the controller
+ * Energy used-Value as returned by the controller
+ * Status-Provides the status of the read_energy_info API call */
+typedef void (*energy_info_callback)(bt_activity_energy_info *energy_info);
+
+/** TODO: Add callbacks for Link Up/Down and other generic
+  *  notifications/callbacks */
+
+/** Bluetooth DM callback structure. */
+typedef struct {
+    /** set to sizeof(bt_callbacks_t) */
+    size_t size;
+    adapter_state_changed_callback adapter_state_changed_cb;
+    adapter_properties_callback adapter_properties_cb;
+    remote_device_properties_callback remote_device_properties_cb;
+    device_found_callback device_found_cb;
+    discovery_state_changed_callback discovery_state_changed_cb;
+    pin_request_callback pin_request_cb;
+    ssp_request_callback ssp_request_cb;
+    bond_state_changed_callback bond_state_changed_cb;
+    acl_state_changed_callback acl_state_changed_cb;
+    callback_thread_event thread_evt_cb;
+    dut_mode_recv_callback dut_mode_recv_cb;
+    le_test_mode_callback le_test_mode_cb;
+    energy_info_callback energy_info_cb;
+} bt_callbacks_t;
+
+typedef void (*alarm_cb)(void *data);
+typedef bool (*set_wake_alarm_callout)(uint64_t delay_millis, bool should_wake, alarm_cb cb, void *data);
+typedef int (*acquire_wake_lock_callout)(const char *lock_name);
+typedef int (*release_wake_lock_callout)(const char *lock_name);
+
+/** The set of functions required by bluedroid to set wake alarms and
+  * grab wake locks. This struct is passed into the stack through the
+  * |set_os_callouts| function on |bt_interface_t|.
+  */
+typedef struct {
+  /* set to sizeof(bt_os_callouts_t) */
+  size_t size;
+
+  set_wake_alarm_callout set_wake_alarm;
+  acquire_wake_lock_callout acquire_wake_lock;
+  release_wake_lock_callout release_wake_lock;
+} bt_os_callouts_t;
+
+/** NOTE: By default, no profiles are initialized at the time of init/enable.
+ *  Whenever the application invokes the 'init' API of a profile, then one of
+ *  the following shall occur:
+ *
+ *    1.) If Bluetooth is not enabled, then the Bluetooth core shall mark the
+ *        profile as enabled. Subsequently, when the application invokes the
+ *        Bluetooth 'enable', as part of the enable sequence the profile that were
+ *        marked shall be enabled by calling appropriate stack APIs. The
+ *        'adapter_properties_cb' shall return the list of UUIDs of the
+ *        enabled profiles.
+ *
+ *    2.) If Bluetooth is enabled, then the Bluetooth core shall invoke the stack
+ *        profile API to initialize the profile and trigger a
+ *        'adapter_properties_cb' with the current list of UUIDs including the
+ *        newly added profile's UUID.
+ *
+ *   The reverse shall occur whenever the profile 'cleanup' APIs are invoked
+ */
+
+/** Represents the standard Bluetooth DM interface. */
+typedef struct {
+    /** set to sizeof(bt_interface_t) */
+    size_t size;
+    /**
+     * Opens the interface and provides the callback routines
+     * to the implemenation of this interface.
+     */
+    int (*init)(bt_callbacks_t* callbacks );
+
+    /** Enable Bluetooth. */
+    int (*enable)(void);
+
+    /** Disable Bluetooth. */
+    int (*disable)(void);
+
+    /** Closes the interface. */
+    void (*cleanup)(void);
+
+    /** Get all Bluetooth Adapter properties at init */
+    int (*get_adapter_properties)(void);
+
+    /** Get Bluetooth Adapter property of 'type' */
+    int (*get_adapter_property)(bt_property_type_t type);
+
+    /** Set Bluetooth Adapter property of 'type' */
+    /* Based on the type, val shall be one of
+     * bt_bdaddr_t or bt_bdname_t or bt_scanmode_t etc
+     */
+    int (*set_adapter_property)(const bt_property_t *property);
+
+    /** Get all Remote Device properties */
+    int (*get_remote_device_properties)(bt_bdaddr_t *remote_addr);
+
+    /** Get Remote Device property of 'type' */
+    int (*get_remote_device_property)(bt_bdaddr_t *remote_addr,
+                                      bt_property_type_t type);
+
+    /** Set Remote Device property of 'type' */
+    int (*set_remote_device_property)(bt_bdaddr_t *remote_addr,
+                                      const bt_property_t *property);
+
+    /** Get Remote Device's service record  for the given UUID */
+    int (*get_remote_service_record)(bt_bdaddr_t *remote_addr,
+                                     bt_uuid_t *uuid);
+
+    /** Start SDP to get remote services */
+    int (*get_remote_services)(bt_bdaddr_t *remote_addr);
+
+    /** Start Discovery */
+    int (*start_discovery)(void);
+
+    /** Cancel Discovery */
+    int (*cancel_discovery)(void);
+
+    /** Create Bluetooth Bonding */
+    int (*create_bond)(const bt_bdaddr_t *bd_addr, int transport);
+
+    /** Remove Bond */
+    int (*remove_bond)(const bt_bdaddr_t *bd_addr);
+
+    /** Cancel Bond */
+    int (*cancel_bond)(const bt_bdaddr_t *bd_addr);
+
+    /**
+     * Get the connection status for a given remote device.
+     * return value of 0 means the device is not connected,
+     * non-zero return status indicates an active connection.
+     */
+    int (*get_connection_state)(const bt_bdaddr_t *bd_addr);
+
+    /** BT Legacy PinKey Reply */
+    /** If accept==FALSE, then pin_len and pin_code shall be 0x0 */
+    int (*pin_reply)(const bt_bdaddr_t *bd_addr, uint8_t accept,
+                     uint8_t pin_len, bt_pin_code_t *pin_code);
+
+    /** BT SSP Reply - Just Works, Numeric Comparison and Passkey
+     * passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON &
+     * BT_SSP_VARIANT_CONSENT
+     * For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey
+     * shall be zero */
+    int (*ssp_reply)(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
+                     uint8_t accept, uint32_t passkey);
+
+    /** Get Bluetooth profile interface */
+    const void* (*get_profile_interface) (const char *profile_id);
+
+    /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */
+    /* Configure DUT Mode - Use this mode to enter/exit DUT mode */
+    int (*dut_mode_configure)(uint8_t enable);
+
+    /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */
+    int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len);
+    /** BLE Test Mode APIs */
+    /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */
+    int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len);
+
+    /* enable or disable bluetooth HCI snoop log */
+    int (*config_hci_snoop_log)(uint8_t enable);
+
+    /** Sets the OS call-out functions that bluedroid needs for alarms and wake locks.
+      * This should be called immediately after a successful |init|.
+      */
+    int (*set_os_callouts)(bt_os_callouts_t *callouts);
+
+    /** Read Energy info details - return value indicates BT_STATUS_SUCCESS or BT_STATUS_NOT_READY
+      * Success indicates that the VSC command was sent to controller
+      */
+    int (*read_energy_info)();
+} bt_interface_t;
+
+/** TODO: Need to add APIs for Service Discovery, Service authorization and
+  *       connection management. Also need to add APIs for configuring
+  *       properties of remote bonded devices such as name, UUID etc. */
+
+typedef struct {
+    struct hw_device_t common;
+    const bt_interface_t* (*get_bluetooth_interface)();
+} bluetooth_device_t;
+
+typedef bluetooth_device_t bluetooth_module_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BLUETOOTH_H */
diff --git a/bluez/repo/android/hardware/bt_av.h b/bluez/repo/android/hardware/bt_av.h
new file mode 100644
index 0000000..5252a17
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_av.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_AV_H
+#define ANDROID_INCLUDE_BT_AV_H
+
+__BEGIN_DECLS
+
+/* Bluetooth AV connection states */
+typedef enum {
+    BTAV_CONNECTION_STATE_DISCONNECTED = 0,
+    BTAV_CONNECTION_STATE_CONNECTING,
+    BTAV_CONNECTION_STATE_CONNECTED,
+    BTAV_CONNECTION_STATE_DISCONNECTING
+} btav_connection_state_t;
+
+/* Bluetooth AV datapath states */
+typedef enum {
+    BTAV_AUDIO_STATE_REMOTE_SUSPEND = 0,
+    BTAV_AUDIO_STATE_STOPPED,
+    BTAV_AUDIO_STATE_STARTED,
+} btav_audio_state_t;
+
+
+/** Callback for connection state change.
+ *  state will have one of the values from btav_connection_state_t
+ */
+typedef void (* btav_connection_state_callback)(btav_connection_state_t state, 
+                                                    bt_bdaddr_t *bd_addr);
+
+/** Callback for audiopath state change.
+ *  state will have one of the values from btav_audio_state_t
+ */
+typedef void (* btav_audio_state_callback)(btav_audio_state_t state, 
+                                               bt_bdaddr_t *bd_addr);
+
+/** Callback for audio configuration change.
+ *  Used only for the A2DP sink interface.
+ *  state will have one of the values from btav_audio_state_t
+ *  sample_rate: sample rate in Hz
+ *  channel_count: number of channels (1 for mono, 2 for stereo)
+ */
+typedef void (* btav_audio_config_callback)(bt_bdaddr_t *bd_addr,
+                                                uint32_t sample_rate,
+                                                uint8_t channel_count);
+
+/** BT-AV callback structure. */
+typedef struct {
+    /** set to sizeof(btav_callbacks_t) */
+    size_t      size;
+    btav_connection_state_callback  connection_state_cb;
+    btav_audio_state_callback audio_state_cb;
+    btav_audio_config_callback audio_config_cb;
+} btav_callbacks_t;
+
+/** 
+ * NOTE:
+ *
+ * 1. AVRCP 1.0 shall be supported initially. AVRCP passthrough commands
+ *    shall be handled internally via uinput 
+ *
+ * 2. A2DP data path shall be handled via a socket pipe between the AudioFlinger
+ *    android_audio_hw library and the Bluetooth stack.
+ * 
+ */
+/** Represents the standard BT-AV interface.
+ *  Used for both the A2DP source and sink interfaces.
+ */
+typedef struct {
+
+    /** set to sizeof(btav_interface_t) */
+    size_t          size;
+    /**
+     * Register the BtAv callbacks
+     */
+    bt_status_t (*init)( btav_callbacks_t* callbacks );
+
+    /** connect to headset */
+    bt_status_t (*connect)( bt_bdaddr_t *bd_addr );
+
+    /** dis-connect from headset */
+    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+} btav_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_AV_H */
diff --git a/bluez/repo/android/hardware/bt_gatt.h b/bluez/repo/android/hardware/bt_gatt.h
new file mode 100644
index 0000000..42e14c2
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_gatt.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_H
+#define ANDROID_INCLUDE_BT_GATT_H
+
+#include <stdint.h>
+#include "bt_gatt_client.h"
+#include "bt_gatt_server.h"
+
+__BEGIN_DECLS
+
+/** BT-GATT callbacks */
+typedef struct {
+    /** Set to sizeof(btgatt_callbacks_t) */
+    size_t size;
+
+    /** GATT Client callbacks */
+    const btgatt_client_callbacks_t* client;
+
+    /** GATT Server callbacks */
+    const btgatt_server_callbacks_t* server;
+} btgatt_callbacks_t;
+
+/** Represents the standard Bluetooth GATT interface. */
+typedef struct {
+    /** Set to sizeof(btgatt_interface_t) */
+    size_t          size;
+
+    /**
+     * Initializes the interface and provides callback routines
+     */
+    bt_status_t (*init)( const btgatt_callbacks_t* callbacks );
+
+    /** Closes the interface */
+    void (*cleanup)( void );
+
+    /** Pointer to the GATT client interface methods.*/
+    const btgatt_client_interface_t* client;
+
+    /** Pointer to the GATT server interface methods.*/
+    const btgatt_server_interface_t* server;
+} btgatt_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_H */
diff --git a/bluez/repo/android/hardware/bt_gatt_client.h b/bluez/repo/android/hardware/bt_gatt_client.h
new file mode 100644
index 0000000..8073dd1
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_gatt_client.h
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_CLIENT_H
+#define ANDROID_INCLUDE_BT_GATT_CLIENT_H
+
+#include <stdint.h>
+#include "bt_gatt_types.h"
+
+__BEGIN_DECLS
+
+/**
+ * Buffer sizes for maximum attribute length and maximum read/write
+ * operation buffer size.
+ */
+#define BTGATT_MAX_ATTR_LEN 600
+
+/** Buffer type for unformatted reads/writes */
+typedef struct
+{
+    uint8_t             value[BTGATT_MAX_ATTR_LEN];
+    uint16_t            len;
+} btgatt_unformatted_value_t;
+
+/** Parameters for GATT read operations */
+typedef struct
+{
+    btgatt_srvc_id_t    srvc_id;
+    btgatt_gatt_id_t    char_id;
+    btgatt_gatt_id_t    descr_id;
+    btgatt_unformatted_value_t value;
+    uint16_t            value_type;
+    uint8_t             status;
+} btgatt_read_params_t;
+
+/** Parameters for GATT write operations */
+typedef struct
+{
+    btgatt_srvc_id_t    srvc_id;
+    btgatt_gatt_id_t    char_id;
+    btgatt_gatt_id_t    descr_id;
+    uint8_t             status;
+} btgatt_write_params_t;
+
+/** Attribute change notification parameters */
+typedef struct
+{
+    uint8_t             value[BTGATT_MAX_ATTR_LEN];
+    bt_bdaddr_t         bda;
+    btgatt_srvc_id_t    srvc_id;
+    btgatt_gatt_id_t    char_id;
+    uint16_t            len;
+    uint8_t             is_notify;
+} btgatt_notify_params_t;
+
+typedef struct
+{
+    bt_bdaddr_t        *bda1;
+    bt_uuid_t          *uuid1;
+    uint16_t            u1;
+    uint16_t            u2;
+    uint16_t            u3;
+    uint16_t            u4;
+    uint16_t            u5;
+} btgatt_test_params_t;
+
+/** BT-GATT Client callback structure. */
+
+/** Callback invoked in response to register_client */
+typedef void (*register_client_callback)(int status, int client_if,
+                bt_uuid_t *app_uuid);
+
+/** Callback for scan results */
+typedef void (*scan_result_callback)(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data);
+
+/** GATT open callback invoked in response to open */
+typedef void (*connect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda);
+
+/** Callback invoked in response to close */
+typedef void (*disconnect_callback)(int conn_id, int status,
+                int client_if, bt_bdaddr_t* bda);
+
+/**
+ * Invoked in response to search_service when the GATT service search
+ * has been completed.
+ */
+typedef void (*search_complete_callback)(int conn_id, int status);
+
+/** Reports GATT services on a remote device */
+typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id);
+
+/** GATT characteristic enumeration result callback */
+typedef void (*get_characteristic_callback)(int conn_id, int status,
+                btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                int char_prop);
+
+/** GATT descriptor enumeration result callback */
+typedef void (*get_descriptor_callback)(int conn_id, int status,
+                btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                btgatt_gatt_id_t *descr_id);
+
+/** GATT included service enumeration result callback */
+typedef void (*get_included_service_callback)(int conn_id, int status,
+                btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id);
+
+/** Callback invoked in response to [de]register_for_notification */
+typedef void (*register_for_notification_callback)(int conn_id,
+                int registered, int status, btgatt_srvc_id_t *srvc_id,
+                btgatt_gatt_id_t *char_id);
+
+/**
+ * Remote device notification callback, invoked when a remote device sends
+ * a notification or indication that a client has registered for.
+ */
+typedef void (*notify_callback)(int conn_id, btgatt_notify_params_t *p_data);
+
+/** Reports result of a GATT read operation */
+typedef void (*read_characteristic_callback)(int conn_id, int status,
+                btgatt_read_params_t *p_data);
+
+/** GATT write characteristic operation callback */
+typedef void (*write_characteristic_callback)(int conn_id, int status,
+                btgatt_write_params_t *p_data);
+
+/** GATT execute prepared write callback */
+typedef void (*execute_write_callback)(int conn_id, int status);
+
+/** Callback invoked in response to read_descriptor */
+typedef void (*read_descriptor_callback)(int conn_id, int status,
+                btgatt_read_params_t *p_data);
+
+/** Callback invoked in response to write_descriptor */
+typedef void (*write_descriptor_callback)(int conn_id, int status,
+                btgatt_write_params_t *p_data);
+
+/** Callback triggered in response to read_remote_rssi */
+typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda,
+                                          int rssi, int status);
+
+/**
+ * Callback indicating the status of a listen() operation
+ */
+typedef void (*listen_callback)(int status, int server_if);
+
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*configure_mtu_callback)(int conn_id, int status, int mtu);
+
+/** Callback invoked when a scan filter configuration command has completed */
+typedef void (*scan_filter_cfg_callback)(int action, int client_if, int status, int filt_type,
+                                         int avbl_space);
+
+/** Callback invoked when scan param has been added, cleared, or deleted */
+typedef void (*scan_filter_param_callback)(int action, int client_if, int status,
+                                         int avbl_space);
+
+/** Callback invoked when a scan filter configuration command has completed */
+typedef void (*scan_filter_status_callback)(int enable, int client_if, int status);
+
+/** Callback invoked when multi-adv enable operation has completed */
+typedef void (*multi_adv_enable_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv param update operation has completed */
+typedef void (*multi_adv_update_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv instance data set operation has completed */
+typedef void (*multi_adv_data_callback)(int client_if, int status);
+
+/** Callback invoked when multi-adv disable operation has completed */
+typedef void (*multi_adv_disable_callback)(int client_if, int status);
+
+/**
+ * Callback notifying an application that a remote device connection is currently congested
+ * and cannot receive any more data. An application should avoid sending more data until
+ * a further callback is received indicating the congestion status has been cleared.
+ */
+typedef void (*congestion_callback)(int conn_id, bool congested);
+/** Callback invoked when batchscan storage config operation has completed */
+typedef void (*batchscan_cfg_storage_callback)(int client_if, int status);
+
+/** Callback invoked when batchscan enable / disable operation has completed */
+typedef void (*batchscan_enable_disable_callback)(int action, int client_if, int status);
+
+/** Callback invoked when batchscan reports are obtained */
+typedef void (*batchscan_reports_callback)(int client_if, int status, int report_format,
+                                           int num_records, int data_len, uint8_t* rep_data);
+
+/** Callback invoked when batchscan storage threshold limit is crossed */
+typedef void (*batchscan_threshold_callback)(int client_if);
+
+/** Track ADV VSE callback invoked when tracked device is found or lost */
+typedef void (*track_adv_event_callback)(int client_if, int filt_index, int addr_type,
+                                             bt_bdaddr_t* bda, int adv_state);
+
+typedef struct {
+    register_client_callback            register_client_cb;
+    scan_result_callback                scan_result_cb;
+    connect_callback                    open_cb;
+    disconnect_callback                 close_cb;
+    search_complete_callback            search_complete_cb;
+    search_result_callback              search_result_cb;
+    get_characteristic_callback         get_characteristic_cb;
+    get_descriptor_callback             get_descriptor_cb;
+    get_included_service_callback       get_included_service_cb;
+    register_for_notification_callback  register_for_notification_cb;
+    notify_callback                     notify_cb;
+    read_characteristic_callback        read_characteristic_cb;
+    write_characteristic_callback       write_characteristic_cb;
+    read_descriptor_callback            read_descriptor_cb;
+    write_descriptor_callback           write_descriptor_cb;
+    execute_write_callback              execute_write_cb;
+    read_remote_rssi_callback           read_remote_rssi_cb;
+    listen_callback                     listen_cb;
+    configure_mtu_callback              configure_mtu_cb;
+    scan_filter_cfg_callback            scan_filter_cfg_cb;
+    scan_filter_param_callback          scan_filter_param_cb;
+    scan_filter_status_callback         scan_filter_status_cb;
+    multi_adv_enable_callback           multi_adv_enable_cb;
+    multi_adv_update_callback           multi_adv_update_cb;
+    multi_adv_data_callback             multi_adv_data_cb;
+    multi_adv_disable_callback          multi_adv_disable_cb;
+    congestion_callback                 congestion_cb;
+    batchscan_cfg_storage_callback      batchscan_cfg_storage_cb;
+    batchscan_enable_disable_callback   batchscan_enb_disable_cb;
+    batchscan_reports_callback          batchscan_reports_cb;
+    batchscan_threshold_callback        batchscan_threshold_cb;
+    track_adv_event_callback            track_adv_event_cb;
+} btgatt_client_callbacks_t;
+
+/** Represents the standard BT-GATT client interface. */
+
+typedef struct {
+    /** Registers a GATT client application with the stack */
+    bt_status_t (*register_client)( bt_uuid_t *uuid );
+
+    /** Unregister a client application from the stack */
+    bt_status_t (*unregister_client)(int client_if );
+
+    /** Start or stop LE device scanning */
+    bt_status_t (*scan)( bool start );
+
+    /** Create a connection to a remote LE or dual-mode device */
+    bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr,
+                         bool is_direct, int transport );
+
+    /** Disconnect a remote device or cancel a pending connection */
+    bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr,
+                    int conn_id);
+
+    /** Start or stop advertisements to listen for incoming connections */
+    bt_status_t (*listen)(int client_if, bool start);
+
+    /** Clear the attribute cache for a given device */
+    bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr );
+
+    /**
+     * Enumerate all GATT services on a connected device.
+     * Optionally, the results can be filtered for a given UUID.
+     */
+    bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid );
+
+    /**
+     * Enumerate included services for a given service.
+     * Set start_incl_srvc_id to NULL to get the first included service.
+     */
+    bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id,
+                                         btgatt_srvc_id_t *start_incl_srvc_id);
+
+    /**
+     * Enumerate characteristics for a given service.
+     * Set start_char_id to NULL to get the first characteristic.
+     */
+    bt_status_t (*get_characteristic)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id);
+
+    /**
+     * Enumerate descriptors for a given characteristic.
+     * Set start_descr_id to NULL to get the first descriptor.
+     */
+    bt_status_t (*get_descriptor)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    btgatt_gatt_id_t *start_descr_id);
+
+    /** Read a characteristic on a remote device */
+    bt_status_t (*read_characteristic)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    int auth_req );
+
+    /** Write a remote characteristic */
+    bt_status_t (*write_characteristic)(int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    int write_type, int len, int auth_req,
+                    char* p_value);
+
+    /** Read the descriptor for a given characteristic */
+    bt_status_t (*read_descriptor)(int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    btgatt_gatt_id_t *descr_id, int auth_req);
+
+    /** Write a remote descriptor for a given characteristic */
+    bt_status_t (*write_descriptor)( int conn_id,
+                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+                    btgatt_gatt_id_t *descr_id, int write_type, int len,
+                    int auth_req, char* p_value);
+
+    /** Execute a prepared write operation */
+    bt_status_t (*execute_write)(int conn_id, int execute);
+
+    /**
+     * Register to receive notifications or indications for a given
+     * characteristic
+     */
+    bt_status_t (*register_for_notification)( int client_if,
+                    const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
+                    btgatt_gatt_id_t *char_id);
+
+    /** Deregister a previous request for notifications/indications */
+    bt_status_t (*deregister_for_notification)( int client_if,
+                    const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
+                    btgatt_gatt_id_t *char_id);
+
+    /** Request RSSI for a given remote device */
+    bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr);
+
+    /** Setup scan filter params */
+    bt_status_t (*scan_filter_param_setup)(int client_if, int action, int filt_index, int feat_seln,
+                                      int list_logic_type, int filt_logic_type, int rssi_high_thres,
+                                      int rssi_low_thres, int dely_mode, int found_timeout,
+                                      int lost_timeout, int found_timeout_cnt);
+
+
+    /** Configure a scan filter condition  */
+    bt_status_t (*scan_filter_add_remove)(int client_if, int action, int filt_type,
+                                   int filt_index, int company_id,
+                                   int company_id_mask, const bt_uuid_t *p_uuid,
+                                   const bt_uuid_t *p_uuid_mask, const bt_bdaddr_t *bd_addr,
+                                   char addr_type, int data_len, char* p_data, int mask_len,
+                                   char* p_mask);
+
+    /** Clear all scan filter conditions for specific filter index*/
+    bt_status_t (*scan_filter_clear)(int client_if, int filt_index);
+
+    /** Enable / disable scan filter feature*/
+    bt_status_t (*scan_filter_enable)(int client_if, bool enable);
+
+    /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */
+    int (*get_device_type)( const bt_bdaddr_t *bd_addr );
+
+    /** Set the advertising data or scan response data */
+    bt_status_t (*set_adv_data)(int client_if, bool set_scan_rsp, bool include_name,
+                    bool include_txpower, int min_interval, int max_interval, int appearance,
+                    uint16_t manufacturer_len, char* manufacturer_data,
+                    uint16_t service_data_len, char* service_data,
+                    uint16_t service_uuid_len, char* service_uuid);
+
+    /** Configure the MTU for a given connection */
+    bt_status_t (*configure_mtu)(int conn_id, int mtu);
+
+    /** Request a connection parameter update */
+    bt_status_t (*conn_parameter_update)(const bt_bdaddr_t *bd_addr, int min_interval,
+                    int max_interval, int latency, int timeout);
+
+    /** Sets the LE scan interval and window in units of N*0.625 msec */
+    bt_status_t (*set_scan_parameters)(int scan_interval, int scan_window);
+
+    /* Setup the parameters as per spec, user manual specified values and enable multi ADV */
+    bt_status_t (*multi_adv_enable)(int client_if, int min_interval,int max_interval,int adv_type,
+                 int chnl_map, int tx_power, int timeout_s);
+
+    /* Update the parameters as per spec, user manual specified values and restart multi ADV */
+    bt_status_t (*multi_adv_update)(int client_if, int min_interval,int max_interval,int adv_type,
+                 int chnl_map, int tx_power, int timeout_s);
+
+    /* Setup the data for the specified instance */
+    bt_status_t (*multi_adv_set_inst_data)(int client_if, bool set_scan_rsp, bool include_name,
+                    bool incl_txpower, int appearance, int manufacturer_len,
+                    char* manufacturer_data, int service_data_len,
+                    char* service_data, int service_uuid_len, char* service_uuid);
+
+    /* Disable the multi adv instance */
+    bt_status_t (*multi_adv_disable)(int client_if);
+
+    /* Configure the batchscan storage */
+    bt_status_t (*batchscan_cfg_storage)(int client_if, int batch_scan_full_max,
+        int batch_scan_trunc_max, int batch_scan_notify_threshold);
+
+    /* Enable batchscan */
+    bt_status_t (*batchscan_enb_batch_scan)(int client_if, int scan_mode,
+        int scan_interval, int scan_window, int addr_type, int discard_rule);
+
+    /* Disable batchscan */
+    bt_status_t (*batchscan_dis_batch_scan)(int client_if);
+
+    /* Read out batchscan reports */
+    bt_status_t (*batchscan_read_reports)(int client_if, int scan_mode);
+
+    /** Test mode interface */
+    bt_status_t (*test_command)( int command, btgatt_test_params_t* params);
+
+} btgatt_client_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */
diff --git a/bluez/repo/android/hardware/bt_gatt_server.h b/bluez/repo/android/hardware/bt_gatt_server.h
new file mode 100644
index 0000000..0d6cc1e
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_gatt_server.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_SERVER_H
+#define ANDROID_INCLUDE_BT_GATT_SERVER_H
+
+#include <stdint.h>
+
+#include "bt_gatt_types.h"
+
+__BEGIN_DECLS
+
+/** GATT value type used in response to remote read requests */
+typedef struct
+{
+    uint8_t           value[BTGATT_MAX_ATTR_LEN];
+    uint16_t          handle;
+    uint16_t          offset;
+    uint16_t          len;
+    uint8_t           auth_req;
+} btgatt_value_t;
+
+/** GATT remote read request response type */
+typedef union
+{
+    btgatt_value_t attr_value;
+    uint16_t            handle;
+} btgatt_response_t;
+
+/** BT-GATT Server callback structure. */
+
+/** Callback invoked in response to register_server */
+typedef void (*register_server_callback)(int status, int server_if,
+                bt_uuid_t *app_uuid);
+
+/** Callback indicating that a remote device has connected or been disconnected */
+typedef void (*connection_callback)(int conn_id, int server_if, int connected,
+                                    bt_bdaddr_t *bda);
+
+/** Callback invoked in response to create_service */
+typedef void (*service_added_callback)(int status, int server_if,
+                btgatt_srvc_id_t *srvc_id, int srvc_handle);
+
+/** Callback indicating that an included service has been added to a service */
+typedef void (*included_service_added_callback)(int status, int server_if,
+                int srvc_handle, int incl_srvc_handle);
+
+/** Callback invoked when a characteristic has been added to a service */
+typedef void (*characteristic_added_callback)(int status, int server_if,
+                bt_uuid_t *uuid, int srvc_handle, int char_handle);
+
+/** Callback invoked when a descriptor has been added to a characteristic */
+typedef void (*descriptor_added_callback)(int status, int server_if,
+                bt_uuid_t *uuid, int srvc_handle, int descr_handle);
+
+/** Callback invoked in response to start_service */
+typedef void (*service_started_callback)(int status, int server_if,
+                                         int srvc_handle);
+
+/** Callback invoked in response to stop_service */
+typedef void (*service_stopped_callback)(int status, int server_if,
+                                         int srvc_handle);
+
+/** Callback triggered when a service has been deleted */
+typedef void (*service_deleted_callback)(int status, int server_if,
+                                         int srvc_handle);
+
+/**
+ * Callback invoked when a remote device has requested to read a characteristic
+ * or descriptor. The application must respond by calling send_response
+ */
+typedef void (*request_read_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda,
+                                      int attr_handle, int offset, bool is_long);
+
+/**
+ * Callback invoked when a remote device has requested to write to a
+ * characteristic or descriptor.
+ */
+typedef void (*request_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda,
+                                       int attr_handle, int offset, int length,
+                                       bool need_rsp, bool is_prep, uint8_t* value);
+
+/** Callback invoked when a previously prepared write is to be executed */
+typedef void (*request_exec_write_callback)(int conn_id, int trans_id,
+                                            bt_bdaddr_t *bda, int exec_write);
+
+/**
+ * Callback triggered in response to send_response if the remote device
+ * sends a confirmation.
+ */
+typedef void (*response_confirmation_callback)(int status, int handle);
+
+/**
+ * Callback confirming that a notification or indication has been sent
+ * to a remote device.
+ */
+typedef void (*indication_sent_callback)(int conn_id, int status);
+
+/**
+ * Callback notifying an application that a remote device connection is currently congested
+ * and cannot receive any more data. An application should avoid sending more data until
+ * a further callback is received indicating the congestion status has been cleared.
+ */
+typedef void (*congestion_callback)(int conn_id, bool congested);
+
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*mtu_changed_callback)(int conn_id, int mtu);
+
+typedef struct {
+    register_server_callback        register_server_cb;
+    connection_callback             connection_cb;
+    service_added_callback          service_added_cb;
+    included_service_added_callback included_service_added_cb;
+    characteristic_added_callback   characteristic_added_cb;
+    descriptor_added_callback       descriptor_added_cb;
+    service_started_callback        service_started_cb;
+    service_stopped_callback        service_stopped_cb;
+    service_deleted_callback        service_deleted_cb;
+    request_read_callback           request_read_cb;
+    request_write_callback          request_write_cb;
+    request_exec_write_callback     request_exec_write_cb;
+    response_confirmation_callback  response_confirmation_cb;
+    indication_sent_callback        indication_sent_cb;
+    congestion_callback             congestion_cb;
+    mtu_changed_callback            mtu_changed_cb;
+} btgatt_server_callbacks_t;
+
+/** Represents the standard BT-GATT server interface. */
+typedef struct {
+    /** Registers a GATT server application with the stack */
+    bt_status_t (*register_server)( bt_uuid_t *uuid );
+
+    /** Unregister a server application from the stack */
+    bt_status_t (*unregister_server)(int server_if );
+
+    /** Create a connection to a remote peripheral */
+    bt_status_t (*connect)(int server_if, const bt_bdaddr_t *bd_addr,
+                            bool is_direct, int transport);
+
+    /** Disconnect an established connection or cancel a pending one */
+    bt_status_t (*disconnect)(int server_if, const bt_bdaddr_t *bd_addr,
+                    int conn_id );
+
+    /** Create a new service */
+    bt_status_t (*add_service)( int server_if, btgatt_srvc_id_t *srvc_id, int num_handles);
+
+    /** Assign an included service to it's parent service */
+    bt_status_t (*add_included_service)( int server_if, int service_handle, int included_handle);
+
+    /** Add a characteristic to a service */
+    bt_status_t (*add_characteristic)( int server_if,
+                    int service_handle, bt_uuid_t *uuid,
+                    int properties, int permissions);
+
+    /** Add a descriptor to a given service */
+    bt_status_t (*add_descriptor)(int server_if, int service_handle,
+                                  bt_uuid_t *uuid, int permissions);
+
+    /** Starts a local service */
+    bt_status_t (*start_service)(int server_if, int service_handle,
+                                 int transport);
+
+    /** Stops a local service */
+    bt_status_t (*stop_service)(int server_if, int service_handle);
+
+    /** Delete a local service */
+    bt_status_t (*delete_service)(int server_if, int service_handle);
+
+    /** Send value indication to a remote device */
+    bt_status_t (*send_indication)(int server_if, int attribute_handle,
+                                   int conn_id, int len, int confirm,
+                                   char* p_value);
+
+    /** Send a response to a read/write operation */
+    bt_status_t (*send_response)(int conn_id, int trans_id,
+                                 int status, btgatt_response_t *response);
+
+} btgatt_server_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */
diff --git a/bluez/repo/android/hardware/bt_gatt_types.h b/bluez/repo/android/hardware/bt_gatt_types.h
new file mode 100644
index 0000000..e037ddc
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_gatt_types.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_INCLUDE_BT_GATT_TYPES_H
+#define ANDROID_INCLUDE_BT_GATT_TYPES_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+/**
+ * GATT Service types
+ */
+#define BTGATT_SERVICE_TYPE_PRIMARY 0
+#define BTGATT_SERVICE_TYPE_SECONDARY 1
+
+/** GATT ID adding instance id tracking to the UUID */
+typedef struct
+{
+    bt_uuid_t           uuid;
+    uint8_t             inst_id;
+} btgatt_gatt_id_t;
+
+/** GATT Service ID also identifies the service type (primary/secondary) */
+typedef struct
+{
+    btgatt_gatt_id_t    id;
+    uint8_t             is_primary;
+} btgatt_srvc_id_t;
+
+/** Preferred physical Transport for GATT connection */
+typedef enum
+{
+    GATT_TRANSPORT_AUTO,
+    GATT_TRANSPORT_BREDR,
+    GATT_TRANSPORT_LE
+} btgatt_transport_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */
diff --git a/bluez/repo/android/hardware/bt_hf.h b/bluez/repo/android/hardware/bt_hf.h
new file mode 100644
index 0000000..7dcb40a
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_hf.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_H
+#define ANDROID_INCLUDE_BT_HF_H
+
+__BEGIN_DECLS
+
+/* AT response code - OK/Error */
+typedef enum {
+    BTHF_AT_RESPONSE_ERROR = 0,
+    BTHF_AT_RESPONSE_OK
+} bthf_at_response_t;
+
+typedef enum {
+    BTHF_CONNECTION_STATE_DISCONNECTED = 0,
+    BTHF_CONNECTION_STATE_CONNECTING,
+    BTHF_CONNECTION_STATE_CONNECTED,
+    BTHF_CONNECTION_STATE_SLC_CONNECTED,
+    BTHF_CONNECTION_STATE_DISCONNECTING
+} bthf_connection_state_t;
+
+typedef enum {
+    BTHF_AUDIO_STATE_DISCONNECTED = 0,
+    BTHF_AUDIO_STATE_CONNECTING,
+    BTHF_AUDIO_STATE_CONNECTED,
+    BTHF_AUDIO_STATE_DISCONNECTING
+} bthf_audio_state_t;
+
+typedef enum {
+    BTHF_VR_STATE_STOPPED = 0,
+    BTHF_VR_STATE_STARTED
+} bthf_vr_state_t;
+
+typedef enum {
+    BTHF_VOLUME_TYPE_SPK = 0,
+    BTHF_VOLUME_TYPE_MIC
+} bthf_volume_type_t;
+
+/* Noise Reduction and Echo Cancellation */
+typedef enum
+{
+    BTHF_NREC_STOP,
+    BTHF_NREC_START
+} bthf_nrec_t;
+
+/* WBS codec setting */
+typedef enum
+{
+   BTHF_WBS_NONE,
+   BTHF_WBS_NO,
+   BTHF_WBS_YES
+}bthf_wbs_config_t;
+
+/* CHLD - Call held handling */
+typedef enum
+{
+    BTHF_CHLD_TYPE_RELEASEHELD,              // Terminate all held or set UDUB("busy") to a waiting call
+    BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD, // Terminate all active calls and accepts a waiting/held call
+    BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD,    // Hold all active calls and accepts a waiting/held call
+    BTHF_CHLD_TYPE_ADDHELDTOCONF,            // Add all held calls to a conference
+} bthf_chld_type_t;
+
+/** Callback for connection state change.
+ *  state will have one of the values from BtHfConnectionState
+ */
+typedef void (* bthf_connection_state_callback)(bthf_connection_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for audio connection state change.
+ *  state will have one of the values from BtHfAudioState
+ */
+typedef void (* bthf_audio_state_callback)(bthf_audio_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for VR connection state change.
+ *  state will have one of the values from BtHfVRState
+ */
+typedef void (* bthf_vr_cmd_callback)(bthf_vr_state_t state, bt_bdaddr_t *bd_addr);
+
+/** Callback for answer incoming call (ATA)
+ */
+typedef void (* bthf_answer_call_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for disconnect call (AT+CHUP)
+ */
+typedef void (* bthf_hangup_call_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for disconnect call (AT+CHUP)
+ *  type will denote Speaker/Mic gain (BtHfVolumeControl).
+ */
+typedef void (* bthf_volume_cmd_callback)(bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr);
+
+/** Callback for dialing an outgoing call
+ *  If number is NULL, redial
+ */
+typedef void (* bthf_dial_call_cmd_callback)(char *number, bt_bdaddr_t *bd_addr);
+
+/** Callback for sending DTMF tones
+ *  tone contains the dtmf character to be sent
+ */
+typedef void (* bthf_dtmf_cmd_callback)(char tone, bt_bdaddr_t *bd_addr);
+
+/** Callback for enabling/disabling noise reduction/echo cancellation
+ *  value will be 1 to enable, 0 to disable
+ */
+typedef void (* bthf_nrec_cmd_callback)(bthf_nrec_t nrec, bt_bdaddr_t *bd_addr);
+
+/** Callback for AT+BCS and event from BAC
+ *  WBS enable, WBS disable
+ */
+typedef void (* bthf_wbs_callback)(bthf_wbs_config_t wbs, bt_bdaddr_t *bd_addr);
+
+/** Callback for call hold handling (AT+CHLD)
+ *  value will contain the call hold command (0, 1, 2, 3)
+ */
+typedef void (* bthf_chld_cmd_callback)(bthf_chld_type_t chld, bt_bdaddr_t *bd_addr);
+
+/** Callback for CNUM (subscriber number)
+ */
+typedef void (* bthf_cnum_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for indicators (CIND)
+ */
+typedef void (* bthf_cind_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for operator selection (COPS)
+ */
+typedef void (* bthf_cops_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** Callback for call list (AT+CLCC)
+ */
+typedef void (* bthf_clcc_cmd_callback) (bt_bdaddr_t *bd_addr);
+
+/** Callback for unknown AT command recd from HF
+ *  at_string will contain the unparsed AT string
+ */
+typedef void (* bthf_unknown_at_cmd_callback)(char *at_string, bt_bdaddr_t *bd_addr);
+
+/** Callback for keypressed (HSP) event.
+ */
+typedef void (* bthf_key_pressed_cmd_callback)(bt_bdaddr_t *bd_addr);
+
+/** BT-HF callback structure. */
+typedef struct {
+    /** set to sizeof(BtHfCallbacks) */
+    size_t      size;
+    bthf_connection_state_callback  connection_state_cb;
+    bthf_audio_state_callback       audio_state_cb;
+    bthf_vr_cmd_callback            vr_cmd_cb;
+    bthf_answer_call_cmd_callback   answer_call_cmd_cb;
+    bthf_hangup_call_cmd_callback   hangup_call_cmd_cb;
+    bthf_volume_cmd_callback        volume_cmd_cb;
+    bthf_dial_call_cmd_callback     dial_call_cmd_cb;
+    bthf_dtmf_cmd_callback          dtmf_cmd_cb;
+    bthf_nrec_cmd_callback          nrec_cmd_cb;
+    bthf_wbs_callback               wbs_cb;
+    bthf_chld_cmd_callback          chld_cmd_cb;
+    bthf_cnum_cmd_callback          cnum_cmd_cb;
+    bthf_cind_cmd_callback          cind_cmd_cb;
+    bthf_cops_cmd_callback          cops_cmd_cb;
+    bthf_clcc_cmd_callback          clcc_cmd_cb;
+    bthf_unknown_at_cmd_callback    unknown_at_cmd_cb;
+    bthf_key_pressed_cmd_callback   key_pressed_cmd_cb;
+} bthf_callbacks_t;
+
+/** Network Status */
+typedef enum
+{
+    BTHF_NETWORK_STATE_NOT_AVAILABLE = 0,
+    BTHF_NETWORK_STATE_AVAILABLE
+} bthf_network_state_t;
+
+/** Service type */
+typedef enum
+{
+    BTHF_SERVICE_TYPE_HOME = 0,
+    BTHF_SERVICE_TYPE_ROAMING
+} bthf_service_type_t;
+
+typedef enum {
+    BTHF_CALL_STATE_ACTIVE = 0,
+    BTHF_CALL_STATE_HELD,
+    BTHF_CALL_STATE_DIALING,
+    BTHF_CALL_STATE_ALERTING,
+    BTHF_CALL_STATE_INCOMING,
+    BTHF_CALL_STATE_WAITING,
+    BTHF_CALL_STATE_IDLE
+} bthf_call_state_t;
+
+typedef enum {
+    BTHF_CALL_DIRECTION_OUTGOING = 0,
+    BTHF_CALL_DIRECTION_INCOMING
+} bthf_call_direction_t;
+
+typedef enum {
+    BTHF_CALL_TYPE_VOICE = 0,
+    BTHF_CALL_TYPE_DATA,
+    BTHF_CALL_TYPE_FAX
+} bthf_call_mode_t;
+
+typedef enum {
+    BTHF_CALL_MPTY_TYPE_SINGLE = 0,
+    BTHF_CALL_MPTY_TYPE_MULTI
+} bthf_call_mpty_type_t;
+
+typedef enum {
+    BTHF_CALL_ADDRTYPE_UNKNOWN = 0x81,
+    BTHF_CALL_ADDRTYPE_INTERNATIONAL = 0x91
+} bthf_call_addrtype_t;
+/** Represents the standard BT-HF interface. */
+typedef struct {
+
+    /** set to sizeof(BtHfInterface) */
+    size_t          size;
+    /**
+     * Register the BtHf callbacks
+     */
+    bt_status_t (*init)( bthf_callbacks_t* callbacks, int max_hf_clients);
+
+    /** connect to headset */
+    bt_status_t (*connect)( bt_bdaddr_t *bd_addr );
+
+    /** dis-connect from headset */
+    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+    /** create an audio connection */
+    bt_status_t (*connect_audio)( bt_bdaddr_t *bd_addr );
+
+    /** close the audio connection */
+    bt_status_t (*disconnect_audio)( bt_bdaddr_t *bd_addr );
+
+    /** start voice recognition */
+    bt_status_t (*start_voice_recognition)( bt_bdaddr_t *bd_addr );
+
+    /** stop voice recognition */
+    bt_status_t (*stop_voice_recognition)( bt_bdaddr_t *bd_addr );
+
+    /** volume control */
+    bt_status_t (*volume_control) (bthf_volume_type_t type, int volume, bt_bdaddr_t *bd_addr );
+
+    /** Combined device status change notification */
+    bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, int signal,
+                           int batt_chg);
+
+    /** Response for COPS command */
+    bt_status_t (*cops_response)(const char *cops, bt_bdaddr_t *bd_addr );
+
+    /** Response for CIND command */
+    bt_status_t (*cind_response)(int svc, int num_active, int num_held, bthf_call_state_t call_setup_state,
+                                 int signal, int roam, int batt_chg, bt_bdaddr_t *bd_addr );
+
+    /** Pre-formatted AT response, typically in response to unknown AT cmd */
+    bt_status_t (*formatted_at_response)(const char *rsp, bt_bdaddr_t *bd_addr );
+
+    /** ok/error response
+     *  ERROR (0)
+     *  OK    (1)
+     */
+    bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code, bt_bdaddr_t *bd_addr );
+
+    /** response for CLCC command 
+     *  Can be iteratively called for each call index
+     *  Call index of 0 will be treated as NULL termination (Completes response)
+     */
+    bt_status_t (*clcc_response) (int index, bthf_call_direction_t dir,
+                                bthf_call_state_t state, bthf_call_mode_t mode,
+                                bthf_call_mpty_type_t mpty, const char *number,
+                                bthf_call_addrtype_t type, bt_bdaddr_t *bd_addr );
+
+    /** notify of a call state change
+     *  Each update notifies 
+     *    1. Number of active/held/ringing calls
+     *    2. call_state: This denotes the state change that triggered this msg
+     *                   This will take one of the values from BtHfCallState
+     *    3. number & type: valid only for incoming & waiting call
+    */
+    bt_status_t (*phone_state_change) (int num_active, int num_held, bthf_call_state_t call_setup_state,
+                                       const char *number, bthf_call_addrtype_t type);
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+
+    /** configureation for the SCO codec */
+    bt_status_t (*configure_wbs)( bt_bdaddr_t *bd_addr ,bthf_wbs_config_t config );
+} bthf_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_H */
diff --git a/bluez/repo/android/hardware/bt_hf_client.h b/bluez/repo/android/hardware/bt_hf_client.h
new file mode 100644
index 0000000..8acf1b2
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_hf_client.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_CLIENT_H
+#define ANDROID_INCLUDE_BT_HF_CLIENT_H
+
+__BEGIN_DECLS
+
+typedef enum {
+    BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED = 0,
+    BTHF_CLIENT_CONNECTION_STATE_CONNECTING,
+    BTHF_CLIENT_CONNECTION_STATE_CONNECTED,
+    BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED,
+    BTHF_CLIENT_CONNECTION_STATE_DISCONNECTING
+} bthf_client_connection_state_t;
+
+typedef enum {
+    BTHF_CLIENT_AUDIO_STATE_DISCONNECTED = 0,
+    BTHF_CLIENT_AUDIO_STATE_CONNECTING,
+    BTHF_CLIENT_AUDIO_STATE_CONNECTED,
+    BTHF_CLIENT_AUDIO_STATE_CONNECTED_MSBC,
+} bthf_client_audio_state_t;
+
+typedef enum {
+    BTHF_CLIENT_VR_STATE_STOPPED = 0,
+    BTHF_CLIENT_VR_STATE_STARTED
+} bthf_client_vr_state_t;
+
+typedef enum {
+    BTHF_CLIENT_VOLUME_TYPE_SPK = 0,
+    BTHF_CLIENT_VOLUME_TYPE_MIC
+} bthf_client_volume_type_t;
+
+typedef enum
+{
+    BTHF_CLIENT_NETWORK_STATE_NOT_AVAILABLE = 0,
+    BTHF_CLIENT_NETWORK_STATE_AVAILABLE
+} bthf_client_network_state_t;
+
+typedef enum
+{
+    BTHF_CLIENT_SERVICE_TYPE_HOME = 0,
+    BTHF_CLIENT_SERVICE_TYPE_ROAMING
+} bthf_client_service_type_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_STATE_ACTIVE = 0,
+    BTHF_CLIENT_CALL_STATE_HELD,
+    BTHF_CLIENT_CALL_STATE_DIALING,
+    BTHF_CLIENT_CALL_STATE_ALERTING,
+    BTHF_CLIENT_CALL_STATE_INCOMING,
+    BTHF_CLIENT_CALL_STATE_WAITING,
+    BTHF_CLIENT_CALL_STATE_HELD_BY_RESP_HOLD,
+} bthf_client_call_state_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_NO_CALLS_IN_PROGRESS = 0,
+    BTHF_CLIENT_CALL_CALLS_IN_PROGRESS
+} bthf_client_call_t;
+
+typedef enum {
+    BTHF_CLIENT_CALLSETUP_NONE = 0,
+    BTHF_CLIENT_CALLSETUP_INCOMING,
+    BTHF_CLIENT_CALLSETUP_OUTGOING,
+    BTHF_CLIENT_CALLSETUP_ALERTING
+
+} bthf_client_callsetup_t;
+
+typedef enum {
+    BTHF_CLIENT_CALLHELD_NONE = 0,
+    BTHF_CLIENT_CALLHELD_HOLD_AND_ACTIVE,
+    BTHF_CLIENT_CALLHELD_HOLD,
+} bthf_client_callheld_t;
+
+typedef enum {
+    BTHF_CLIENT_RESP_AND_HOLD_HELD = 0,
+    BTRH_CLIENT_RESP_AND_HOLD_ACCEPT,
+    BTRH_CLIENT_RESP_AND_HOLD_REJECT,
+} bthf_client_resp_and_hold_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_DIRECTION_OUTGOING = 0,
+    BTHF_CLIENT_CALL_DIRECTION_INCOMING
+} bthf_client_call_direction_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_MPTY_TYPE_SINGLE = 0,
+    BTHF_CLIENT_CALL_MPTY_TYPE_MULTI
+} bthf_client_call_mpty_type_t;
+
+typedef enum {
+    BTHF_CLIENT_CMD_COMPLETE_OK = 0,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_CARRIER,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_BUSY,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_NO_ANSWER,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_DELAYED,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_BLACKLISTED,
+    BTHF_CLIENT_CMD_COMPLETE_ERROR_CME
+} bthf_client_cmd_complete_t;
+
+typedef enum {
+    BTHF_CLIENT_CALL_ACTION_CHLD_0 = 0,
+    BTHF_CLIENT_CALL_ACTION_CHLD_1,
+    BTHF_CLIENT_CALL_ACTION_CHLD_2,
+    BTHF_CLIENT_CALL_ACTION_CHLD_3,
+    BTHF_CLIENT_CALL_ACTION_CHLD_4,
+    BTHF_CLIENT_CALL_ACTION_CHLD_1x,
+    BTHF_CLIENT_CALL_ACTION_CHLD_2x,
+    BTHF_CLIENT_CALL_ACTION_ATA,
+    BTHF_CLIENT_CALL_ACTION_CHUP,
+    BTHF_CLIENT_CALL_ACTION_BTRH_0,
+    BTHF_CLIENT_CALL_ACTION_BTRH_1,
+    BTHF_CLIENT_CALL_ACTION_BTRH_2,
+} bthf_client_call_action_t;
+
+typedef enum {
+    BTHF_CLIENT_SERVICE_UNKNOWN = 0,
+    BTHF_CLIENT_SERVICE_VOICE,
+    BTHF_CLIENT_SERVICE_FAX
+} bthf_client_subscriber_service_type_t;
+
+typedef enum {
+    BTHF_CLIENT_IN_BAND_RINGTONE_NOT_PROVIDED = 0,
+    BTHF_CLIENT_IN_BAND_RINGTONE_PROVIDED,
+} bthf_client_in_band_ring_state_t;
+
+/* Peer features masks */
+#define BTHF_CLIENT_PEER_FEAT_3WAY   0x00000001  /* Three-way calling */
+#define BTHF_CLIENT_PEER_FEAT_ECNR   0x00000002  /* Echo cancellation and/or noise reduction */
+#define BTHF_CLIENT_PEER_FEAT_VREC   0x00000004  /* Voice recognition */
+#define BTHF_CLIENT_PEER_FEAT_INBAND 0x00000008  /* In-band ring tone */
+#define BTHF_CLIENT_PEER_FEAT_VTAG   0x00000010  /* Attach a phone number to a voice tag */
+#define BTHF_CLIENT_PEER_FEAT_REJECT 0x00000020  /* Ability to reject incoming call */
+#define BTHF_CLIENT_PEER_FEAT_ECS    0x00000040  /* Enhanced Call Status */
+#define BTHF_CLIENT_PEER_FEAT_ECC    0x00000080  /* Enhanced Call Control */
+#define BTHF_CLIENT_PEER_FEAT_EXTERR 0x00000100  /* Extended error codes */
+#define BTHF_CLIENT_PEER_FEAT_CODEC  0x00000200  /* Codec Negotiation */
+
+/* Peer call handling features masks */
+#define BTHF_CLIENT_CHLD_FEAT_REL           0x00000001  /* 0  Release waiting call or held calls */
+#define BTHF_CLIENT_CHLD_FEAT_REL_ACC       0x00000002  /* 1  Release active calls and accept other
+                                                              (waiting or held) cal */
+#define BTHF_CLIENT_CHLD_FEAT_REL_X         0x00000004  /* 1x Release specified active call only */
+#define BTHF_CLIENT_CHLD_FEAT_HOLD_ACC      0x00000008  /* 2  Active calls on hold and accept other
+                                                              (waiting or held) call */
+#define BTHF_CLIENT_CHLD_FEAT_PRIV_X        0x00000010  /* 2x Request private mode with specified
+                                                              call (put the rest on hold) */
+#define BTHF_CLIENT_CHLD_FEAT_MERGE         0x00000020  /* 3  Add held call to multiparty */
+#define BTHF_CLIENT_CHLD_FEAT_MERGE_DETACH  0x00000040  /* 4  Connect two calls and leave
+                                                              (disconnect from) multiparty */
+
+/** Callback for connection state change.
+ *  state will have one of the values from BtHfConnectionState
+ *  peer/chld_features are valid only for BTHF_CLIENT_CONNECTION_STATE_SLC_CONNECTED state
+ */
+typedef void (* bthf_client_connection_state_callback)(bthf_client_connection_state_t state,
+                                                       unsigned int peer_feat,
+                                                       unsigned int chld_feat,
+                                                       bt_bdaddr_t *bd_addr);
+
+/** Callback for audio connection state change.
+ *  state will have one of the values from BtHfAudioState
+ */
+typedef void (* bthf_client_audio_state_callback)(bthf_client_audio_state_t state,
+                                                  bt_bdaddr_t *bd_addr);
+
+/** Callback for VR connection state change.
+ *  state will have one of the values from BtHfVRState
+ */
+typedef void (* bthf_client_vr_cmd_callback)(bthf_client_vr_state_t state);
+
+/** Callback for network state change
+ */
+typedef void (* bthf_client_network_state_callback) (bthf_client_network_state_t state);
+
+/** Callback for network roaming status change
+ */
+typedef void (* bthf_client_network_roaming_callback) (bthf_client_service_type_t type);
+
+/** Callback for signal strength indication
+ */
+typedef void (* bthf_client_network_signal_callback) (int signal_strength);
+
+/** Callback for battery level indication
+ */
+typedef void (* bthf_client_battery_level_callback) (int battery_level);
+
+/** Callback for current operator name
+ */
+typedef void (* bthf_client_current_operator_callback) (const char *name);
+
+/** Callback for call indicator
+ */
+typedef void (* bthf_client_call_callback) (bthf_client_call_t call);
+
+/** Callback for callsetup indicator
+ */
+typedef void (* bthf_client_callsetup_callback) (bthf_client_callsetup_t callsetup);
+
+/** Callback for callheld indicator
+ */
+typedef void (* bthf_client_callheld_callback) (bthf_client_callheld_t callheld);
+
+/** Callback for response and hold
+ */
+typedef void (* bthf_client_resp_and_hold_callback) (bthf_client_resp_and_hold_t resp_and_hold);
+
+/** Callback for Calling Line Identification notification
+ *  Will be called only when there is an incoming call and number is provided.
+ */
+typedef void (* bthf_client_clip_callback) (const char *number);
+
+/**
+ * Callback for Call Waiting notification
+ */
+typedef void (* bthf_client_call_waiting_callback) (const char *number);
+
+/**
+ *  Callback for listing current calls. Can be called multiple time.
+ *  If number is unknown NULL is passed.
+ */
+typedef void (*bthf_client_current_calls) (int index, bthf_client_call_direction_t dir,
+                                           bthf_client_call_state_t state,
+                                           bthf_client_call_mpty_type_t mpty,
+                                           const char *number);
+
+/** Callback for audio volume change
+ */
+typedef void (*bthf_client_volume_change_callback) (bthf_client_volume_type_t type, int volume);
+
+/** Callback for command complete event
+ *  cme is valid only for BTHF_CLIENT_CMD_COMPLETE_ERROR_CME type
+ */
+typedef void (*bthf_client_cmd_complete_callback) (bthf_client_cmd_complete_t type, int cme);
+
+/** Callback for subscriber information
+ */
+typedef void (* bthf_client_subscriber_info_callback) (const char *name,
+                                                       bthf_client_subscriber_service_type_t type);
+
+/** Callback for in-band ring tone settings
+ */
+typedef void (* bthf_client_in_band_ring_tone_callback) (bthf_client_in_band_ring_state_t state);
+
+/**
+ * Callback for requested number from AG
+ */
+typedef void (* bthf_client_last_voice_tag_number_callback) (const char *number);
+
+/**
+ * Callback for sending ring indication to app
+ */
+typedef void (* bthf_client_ring_indication_callback) (void);
+
+/** BT-HF callback structure. */
+typedef struct {
+    /** set to sizeof(BtHfClientCallbacks) */
+    size_t      size;
+    bthf_client_connection_state_callback  connection_state_cb;
+    bthf_client_audio_state_callback       audio_state_cb;
+    bthf_client_vr_cmd_callback            vr_cmd_cb;
+    bthf_client_network_state_callback     network_state_cb;
+    bthf_client_network_roaming_callback   network_roaming_cb;
+    bthf_client_network_signal_callback    network_signal_cb;
+    bthf_client_battery_level_callback     battery_level_cb;
+    bthf_client_current_operator_callback  current_operator_cb;
+    bthf_client_call_callback              call_cb;
+    bthf_client_callsetup_callback         callsetup_cb;
+    bthf_client_callheld_callback          callheld_cb;
+    bthf_client_resp_and_hold_callback     resp_and_hold_cb;
+    bthf_client_clip_callback              clip_cb;
+    bthf_client_call_waiting_callback      call_waiting_cb;
+    bthf_client_current_calls              current_calls_cb;
+    bthf_client_volume_change_callback     volume_change_cb;
+    bthf_client_cmd_complete_callback      cmd_complete_cb;
+    bthf_client_subscriber_info_callback   subscriber_info_cb;
+    bthf_client_in_band_ring_tone_callback in_band_ring_tone_cb;
+    bthf_client_last_voice_tag_number_callback last_voice_tag_number_callback;
+    bthf_client_ring_indication_callback   ring_indication_cb;
+} bthf_client_callbacks_t;
+
+/** Represents the standard BT-HF interface. */
+typedef struct {
+
+    /** set to sizeof(BtHfClientInterface) */
+    size_t size;
+    /**
+     * Register the BtHf callbacks
+     */
+    bt_status_t (*init)(bthf_client_callbacks_t* callbacks);
+
+    /** connect to audio gateway */
+    bt_status_t (*connect)(bt_bdaddr_t *bd_addr);
+
+    /** disconnect from audio gateway */
+    bt_status_t (*disconnect)(bt_bdaddr_t *bd_addr);
+
+    /** create an audio connection */
+    bt_status_t (*connect_audio)(bt_bdaddr_t *bd_addr);
+
+    /** close the audio connection */
+    bt_status_t (*disconnect_audio)(bt_bdaddr_t *bd_addr);
+
+    /** start voice recognition */
+    bt_status_t (*start_voice_recognition)(void);
+
+    /** stop voice recognition */
+    bt_status_t (*stop_voice_recognition)(void);
+
+    /** volume control */
+    bt_status_t (*volume_control) (bthf_client_volume_type_t type, int volume);
+
+    /** place a call with number a number
+     * if number is NULL last called number is called (aka re-dial)*/
+    bt_status_t (*dial) (const char *number);
+
+    /** place a call with number specified by location (speed dial) */
+    bt_status_t (*dial_memory) (int location);
+
+    /** perform specified call related action
+     * idx is limited only for enhanced call control related action
+     */
+    bt_status_t (*handle_call_action) (bthf_client_call_action_t action, int idx);
+
+    /** query list of current calls */
+    bt_status_t (*query_current_calls) (void);
+
+    /** query name of current selected operator */
+    bt_status_t (*query_current_operator_name) (void);
+
+    /** Retrieve subscriber information */
+    bt_status_t (*retrieve_subscriber_info) (void);
+
+    /** Send DTMF code*/
+    bt_status_t (*send_dtmf) (char code);
+
+    /** Request a phone number from AG corresponding to last voice tag recorded */
+    bt_status_t (*request_last_voice_tag_number) (void);
+
+    /** Closes the interface. */
+    void (*cleanup)(void);
+
+    /** Send AT Command. */
+    bt_status_t (*send_at_cmd) (int cmd, int val1, int val2, const char *arg);
+} bthf_client_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HF_CLIENT_H */
diff --git a/bluez/repo/android/hardware/bt_hh.h b/bluez/repo/android/hardware/bt_hh.h
new file mode 100644
index 0000000..dad9586
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_hh.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HH_H
+#define ANDROID_INCLUDE_BT_HH_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+#define BTHH_MAX_DSC_LEN   884
+
+/* HH connection states */
+typedef enum
+{
+    BTHH_CONN_STATE_CONNECTED              = 0,
+    BTHH_CONN_STATE_CONNECTING,
+    BTHH_CONN_STATE_DISCONNECTED,
+    BTHH_CONN_STATE_DISCONNECTING,
+    BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST,
+    BTHH_CONN_STATE_FAILED_KBD_FROM_HOST,
+    BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES,
+    BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER,
+    BTHH_CONN_STATE_FAILED_GENERIC,
+    BTHH_CONN_STATE_UNKNOWN
+} bthh_connection_state_t;
+
+typedef enum
+{
+    BTHH_OK                = 0,
+    BTHH_HS_HID_NOT_READY,        /* handshake error : device not ready */
+    BTHH_HS_INVALID_RPT_ID,       /* handshake error : invalid report ID */
+    BTHH_HS_TRANS_NOT_SPT,        /* handshake error : transaction not spt */
+    BTHH_HS_INVALID_PARAM,        /* handshake error : invalid paremter */
+    BTHH_HS_ERROR,                /* handshake error : unspecified HS error */
+    BTHH_ERR,                     /* general BTA HH error */
+    BTHH_ERR_SDP,                 /* SDP error */
+    BTHH_ERR_PROTO,               /* SET_Protocol error,
+                                                                only used in BTA_HH_OPEN_EVT callback */
+    BTHH_ERR_DB_FULL,             /* device database full error, used  */
+    BTHH_ERR_TOD_UNSPT,           /* type of device not supported */
+    BTHH_ERR_NO_RES,              /* out of system resources */
+    BTHH_ERR_AUTH_FAILED,         /* authentication fail */
+    BTHH_ERR_HDL
+}bthh_status_t;
+
+/* Protocol modes */
+typedef enum {
+    BTHH_REPORT_MODE       = 0x00,
+    BTHH_BOOT_MODE         = 0x01,
+    BTHH_UNSUPPORTED_MODE  = 0xff
+}bthh_protocol_mode_t;
+
+/* Report types */
+typedef enum {
+    BTHH_INPUT_REPORT      = 1,
+    BTHH_OUTPUT_REPORT,
+    BTHH_FEATURE_REPORT
+}bthh_report_type_t;
+
+typedef struct
+{
+    int         attr_mask;
+    uint8_t     sub_class;
+    uint8_t     app_id;
+    int         vendor_id;
+    int         product_id;
+    int         version;
+    uint8_t     ctry_code;
+    int         dl_len;
+    uint8_t     dsc_list[BTHH_MAX_DSC_LEN];
+} bthh_hid_info_t;
+
+/** Callback for connection state change.
+ *  state will have one of the values from bthh_connection_state_t
+ */
+typedef void (* bthh_connection_state_callback)(bt_bdaddr_t *bd_addr, bthh_connection_state_t state);
+
+/** Callback for vitual unplug api.
+ *  the status of the vitual unplug
+ */
+typedef void (* bthh_virtual_unplug_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status);
+
+/** Callback for get hid info
+ *  hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, len
+ */
+typedef void (* bthh_hid_info_callback)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info);
+
+/** Callback for get protocol api.
+ *  the protocol mode is one of the value from bthh_protocol_mode_t
+ */
+typedef void (* bthh_protocol_mode_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t mode);
+
+/** Callback for get/set_idle_time api.
+ */
+typedef void (* bthh_idle_time_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate);
+
+
+/** Callback for get report api.
+ *  if staus is ok rpt_data contains the report data
+ */
+typedef void (* bthh_get_report_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size);
+
+/** Callback for set_report/set_protocol api and if error
+ *  occurs for get_report/get_protocol api.
+ */
+typedef void (* bthh_handshake_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status);
+
+
+/** BT-HH callback structure. */
+typedef struct {
+    /** set to sizeof(BtHfCallbacks) */
+    size_t      size;
+    bthh_connection_state_callback  connection_state_cb;
+    bthh_hid_info_callback          hid_info_cb;
+    bthh_protocol_mode_callback     protocol_mode_cb;
+    bthh_idle_time_callback         idle_time_cb;
+    bthh_get_report_callback        get_report_cb;
+    bthh_virtual_unplug_callback    virtual_unplug_cb;
+    bthh_handshake_callback         handshake_cb;
+
+} bthh_callbacks_t;
+
+
+
+/** Represents the standard BT-HH interface. */
+typedef struct {
+
+    /** set to sizeof(BtHhInterface) */
+    size_t          size;
+
+    /**
+     * Register the BtHh callbacks
+     */
+    bt_status_t (*init)( bthh_callbacks_t* callbacks );
+
+    /** connect to hid device */
+    bt_status_t (*connect)( bt_bdaddr_t *bd_addr);
+
+    /** dis-connect from hid device */
+    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );
+
+    /** Virtual UnPlug (VUP) the specified HID device */
+    bt_status_t (*virtual_unplug)(bt_bdaddr_t *bd_addr);
+
+    /** Set the HID device descriptor for the specified HID device. */
+    bt_status_t (*set_info)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info );
+
+    /** Get the HID proto mode. */
+    bt_status_t (*get_protocol) (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode);
+
+    /** Set the HID proto mode. */
+    bt_status_t (*set_protocol)(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode);
+
+    /** Send a GET_REPORT to HID device. */
+    bt_status_t (*get_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize);
+
+    /** Send a SET_REPORT to HID device. */
+    bt_status_t (*set_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report);
+
+    /** Send data to HID device. */
+    bt_status_t (*send_data)(bt_bdaddr_t *bd_addr, char* data);
+
+	/** Closes the interface. */
+    void  (*cleanup)( void );
+
+} bthh_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HH_H */
+
+
diff --git a/bluez/repo/android/hardware/bt_hl.h b/bluez/repo/android/hardware/bt_hl.h
new file mode 100644
index 0000000..bd29e3a
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_hl.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HL_H
+#define ANDROID_INCLUDE_BT_HL_H
+
+__BEGIN_DECLS
+
+/* HL connection states */
+
+typedef enum
+{
+    BTHL_MDEP_ROLE_SOURCE,
+    BTHL_MDEP_ROLE_SINK
+} bthl_mdep_role_t;
+
+typedef enum {
+    BTHL_APP_REG_STATE_REG_SUCCESS,
+    BTHL_APP_REG_STATE_REG_FAILED,
+    BTHL_APP_REG_STATE_DEREG_SUCCESS,
+    BTHL_APP_REG_STATE_DEREG_FAILED
+} bthl_app_reg_state_t;
+
+typedef enum
+{
+    BTHL_CHANNEL_TYPE_RELIABLE,
+    BTHL_CHANNEL_TYPE_STREAMING,
+    BTHL_CHANNEL_TYPE_ANY
+} bthl_channel_type_t;
+
+
+/* HL connection states */
+typedef enum {
+    BTHL_CONN_STATE_CONNECTING,
+    BTHL_CONN_STATE_CONNECTED,
+    BTHL_CONN_STATE_DISCONNECTING,
+    BTHL_CONN_STATE_DISCONNECTED,
+    BTHL_CONN_STATE_DESTROYED
+} bthl_channel_state_t;
+
+typedef struct
+{
+    bthl_mdep_role_t        mdep_role;
+    int                     data_type;
+    bthl_channel_type_t     channel_type;
+    const char                   *mdep_description; /* MDEP description to be used in the SDP (optional); null terminated */
+} bthl_mdep_cfg_t;
+
+typedef struct
+{
+    const char      *application_name;
+    const char      *provider_name;   /* provider name to be used in the SDP (optional); null terminated */
+    const char      *srv_name;        /* service name to be used in the SDP (optional); null terminated*/
+    const char      *srv_desp;        /* service description to be used in the SDP (optional); null terminated */
+    int             number_of_mdeps;
+    bthl_mdep_cfg_t *mdep_cfg;  /* Dynamic array */
+} bthl_reg_param_t;
+
+/** Callback for application registration status.
+ *  state will have one of the values from  bthl_app_reg_state_t
+ */
+typedef void (* bthl_app_reg_state_callback)(int app_id, bthl_app_reg_state_t state);
+
+/** Callback for channel connection state change.
+ *  state will have one of the values from
+ *  bthl_connection_state_t and fd (file descriptor)
+ */
+typedef void (* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd);
+
+/** BT-HL callback structure. */
+typedef struct {
+    /** set to sizeof(bthl_callbacks_t) */
+    size_t      size;
+    bthl_app_reg_state_callback     app_reg_state_cb;
+    bthl_channel_state_callback     channel_state_cb;
+} bthl_callbacks_t;
+
+
+/** Represents the standard BT-HL interface. */
+typedef struct {
+
+    /** set to sizeof(bthl_interface_t)  */
+    size_t          size;
+
+    /**
+     * Register the Bthl callbacks
+     */
+    bt_status_t (*init)( bthl_callbacks_t* callbacks );
+
+    /** Register HL application */
+    bt_status_t (*register_application) ( bthl_reg_param_t *p_reg_param, int *app_id);
+
+    /** Unregister HL application */
+    bt_status_t (*unregister_application) (int app_id);
+
+    /** connect channel */
+    bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id);
+
+    /** destroy channel */
+    bt_status_t (*destroy_channel)(int channel_id);
+
+    /** Close the  Bthl callback **/
+    void (*cleanup)(void);
+
+} bthl_interface_t;
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_HL_H */
+
+
diff --git a/bluez/repo/android/hardware/bt_mce.h b/bluez/repo/android/hardware/bt_mce.h
new file mode 100644
index 0000000..5d159b3
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_mce.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_MCE_H
+#define ANDROID_INCLUDE_BT_MCE_H
+
+__BEGIN_DECLS
+
+/** MAS instance description */
+typedef struct
+{
+    int  id;
+    int  scn;
+    int  msg_types;
+    char *p_name;
+} btmce_mas_instance_t;
+
+/** callback for get_remote_mas_instances */
+typedef void (*btmce_remote_mas_instances_callback)(bt_status_t status, bt_bdaddr_t *bd_addr,
+                                                    int num_instances, btmce_mas_instance_t *instances);
+
+typedef struct {
+    /** set to sizeof(btmce_callbacks_t) */
+    size_t      size;
+    btmce_remote_mas_instances_callback  remote_mas_instances_cb;
+} btmce_callbacks_t;
+
+typedef struct {
+    /** set to size of this struct */
+    size_t size;
+
+    /** register BT MCE callbacks */
+    bt_status_t (*init)(btmce_callbacks_t *callbacks);
+
+    /** search for MAS instances on remote device */
+    bt_status_t (*get_remote_mas_instances)(bt_bdaddr_t *bd_addr);
+} btmce_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_MCE_H */
diff --git a/bluez/repo/android/hardware/bt_pan.h b/bluez/repo/android/hardware/bt_pan.h
new file mode 100644
index 0000000..83e7949
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_pan.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_PAN_H
+#define ANDROID_INCLUDE_BT_PAN_H
+
+__BEGIN_DECLS
+
+#define BTPAN_ROLE_NONE      0
+#define BTPAN_ROLE_PANNAP    1
+#define BTPAN_ROLE_PANU      2
+
+typedef enum {
+    BTPAN_STATE_CONNECTED       = 0,
+    BTPAN_STATE_CONNECTING      = 1,
+    BTPAN_STATE_DISCONNECTED    = 2,
+    BTPAN_STATE_DISCONNECTING   = 3
+} btpan_connection_state_t;
+
+typedef enum {
+    BTPAN_STATE_ENABLED = 0,
+    BTPAN_STATE_DISABLED = 1
+} btpan_control_state_t;
+
+/**
+* Callback for pan connection state
+*/
+typedef void (*btpan_connection_state_callback)(btpan_connection_state_t state, bt_status_t error,
+                                                const bt_bdaddr_t *bd_addr, int local_role, int remote_role);
+typedef void (*btpan_control_state_callback)(btpan_control_state_t state, int local_role,
+                                            bt_status_t error, const char* ifname);
+
+typedef struct {
+    size_t size;
+    btpan_control_state_callback control_state_cb;
+    btpan_connection_state_callback connection_state_cb;
+} btpan_callbacks_t;
+typedef struct {
+    /** set to size of this struct*/
+    size_t          size;
+    /**
+     * Initialize the pan interface and register the btpan callbacks
+     */
+    bt_status_t (*init)(const btpan_callbacks_t* callbacks);
+    /*
+     * enable the pan service by specified role. The result state of
+     * enabl will be returned by btpan_control_state_callback. when pan-nap is enabled,
+     * the state of connecting panu device will be notified by btpan_connection_state_callback
+     */
+    bt_status_t (*enable)(int local_role);
+    /*
+     * get current pan local role
+     */
+    int (*get_local_role)(void);
+    /**
+     * start bluetooth pan connection to the remote device by specified pan role. The result state will be
+     * returned by btpan_connection_state_callback
+     */
+    bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, int local_role, int remote_role);
+    /**
+     * stop bluetooth pan connection. The result state will be returned by btpan_connection_state_callback
+     */
+    bt_status_t (*disconnect)(const bt_bdaddr_t *bd_addr);
+
+    /**
+     * Cleanup the pan interface
+     */
+    void (*cleanup)(void);
+
+} btpan_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_PAN_H */
diff --git a/bluez/repo/android/hardware/bt_rc.h b/bluez/repo/android/hardware/bt_rc.h
new file mode 100644
index 0000000..c565c48
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_rc.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_RC_H
+#define ANDROID_INCLUDE_BT_RC_H
+
+__BEGIN_DECLS
+
+/* Macros */
+#define BTRC_MAX_ATTR_STR_LEN       255
+#define BTRC_UID_SIZE               8
+#define BTRC_MAX_APP_SETTINGS       8
+#define BTRC_MAX_FOLDER_DEPTH       4
+#define BTRC_MAX_APP_ATTR_SIZE      16
+#define BTRC_MAX_ELEM_ATTR_SIZE     7
+
+typedef uint8_t btrc_uid_t[BTRC_UID_SIZE];
+
+typedef enum {
+    BTRC_FEAT_NONE = 0x00,    /* AVRCP 1.0 */
+    BTRC_FEAT_METADATA = 0x01,    /* AVRCP 1.3 */
+    BTRC_FEAT_ABSOLUTE_VOLUME = 0x02,    /* Supports TG role and volume sync */
+    BTRC_FEAT_BROWSE = 0x04,    /* AVRCP 1.4 and up, with Browsing support */
+} btrc_remote_features_t;
+
+typedef enum {
+    BTRC_PLAYSTATE_STOPPED = 0x00,    /* Stopped */
+    BTRC_PLAYSTATE_PLAYING = 0x01,    /* Playing */
+    BTRC_PLAYSTATE_PAUSED = 0x02,    /* Paused  */
+    BTRC_PLAYSTATE_FWD_SEEK = 0x03,    /* Fwd Seek*/
+    BTRC_PLAYSTATE_REV_SEEK = 0x04,    /* Rev Seek*/
+    BTRC_PLAYSTATE_ERROR = 0xFF,    /* Error   */
+} btrc_play_status_t;
+
+typedef enum {
+    BTRC_EVT_PLAY_STATUS_CHANGED = 0x01,
+    BTRC_EVT_TRACK_CHANGE = 0x02,
+    BTRC_EVT_TRACK_REACHED_END = 0x03,
+    BTRC_EVT_TRACK_REACHED_START = 0x04,
+    BTRC_EVT_PLAY_POS_CHANGED = 0x05,
+    BTRC_EVT_APP_SETTINGS_CHANGED = 0x08,
+} btrc_event_id_t;
+
+typedef enum {
+    BTRC_NOTIFICATION_TYPE_INTERIM = 0,
+    BTRC_NOTIFICATION_TYPE_CHANGED = 1,
+} btrc_notification_type_t;
+
+typedef enum {
+    BTRC_PLAYER_ATTR_EQUALIZER = 0x01,
+    BTRC_PLAYER_ATTR_REPEAT = 0x02,
+    BTRC_PLAYER_ATTR_SHUFFLE = 0x03,
+    BTRC_PLAYER_ATTR_SCAN = 0x04,
+} btrc_player_attr_t;
+
+typedef enum {
+    BTRC_MEDIA_ATTR_TITLE = 0x01,
+    BTRC_MEDIA_ATTR_ARTIST = 0x02,
+    BTRC_MEDIA_ATTR_ALBUM = 0x03,
+    BTRC_MEDIA_ATTR_TRACK_NUM = 0x04,
+    BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05,
+    BTRC_MEDIA_ATTR_GENRE = 0x06,
+    BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07,
+} btrc_media_attr_t;
+
+typedef enum {
+    BTRC_PLAYER_VAL_OFF_REPEAT = 0x01,
+    BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02,
+    BTRC_PLAYER_VAL_ALL_REPEAT = 0x03,
+    BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04
+} btrc_player_repeat_val_t;
+
+typedef enum {
+    BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01,
+    BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02,
+    BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03
+} btrc_player_shuffle_val_t;
+
+typedef enum {
+    BTRC_STS_BAD_CMD        = 0x00, /* Invalid command */
+    BTRC_STS_BAD_PARAM      = 0x01, /* Invalid parameter */
+    BTRC_STS_NOT_FOUND      = 0x02, /* Specified parameter is wrong or not found */
+    BTRC_STS_INTERNAL_ERR   = 0x03, /* Internal Error */
+    BTRC_STS_NO_ERROR       = 0x04  /* Operation Success */
+} btrc_status_t;
+
+typedef struct {
+    uint8_t num_attr;
+    uint8_t attr_ids[BTRC_MAX_APP_SETTINGS];
+    uint8_t attr_values[BTRC_MAX_APP_SETTINGS];
+} btrc_player_settings_t;
+
+typedef union
+{
+    btrc_play_status_t play_status;
+    btrc_uid_t track; /* queue position in NowPlaying */
+    uint32_t song_pos;
+    btrc_player_settings_t player_setting;
+} btrc_register_notification_t;
+
+typedef struct {
+    uint8_t id; /* can be attr_id or value_id */
+    uint8_t text[BTRC_MAX_ATTR_STR_LEN];
+} btrc_player_setting_text_t;
+
+typedef struct {
+    uint32_t attr_id;
+    uint8_t text[BTRC_MAX_ATTR_STR_LEN];
+} btrc_element_attr_val_t;
+
+/** Callback for the controller's supported feautres */
+typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr,
+                                                      btrc_remote_features_t features);
+
+/** Callback for play status request */
+typedef void (* btrc_get_play_status_callback)();
+
+/** Callback for list player application attributes (Shuffle, Repeat,...) */
+typedef void (* btrc_list_player_app_attr_callback)();
+
+/** Callback for list player application attributes (Shuffle, Repeat,...) */
+typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id);
+
+/** Callback for getting the current player application settings value
+**  num_attr: specifies the number of attribute ids contained in p_attrs
+*/
+typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
+
+/** Callback for getting the player application settings attributes' text
+**  num_attr: specifies the number of attribute ids contained in p_attrs
+*/
+typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs);
+
+/** Callback for getting the player application settings values' text
+**  num_attr: specifies the number of value ids contained in p_vals
+*/
+typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals);
+
+/** Callback for setting the player application settings values */
+typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals);
+
+/** Callback to fetch the get element attributes of the current song
+**  num_attr: specifies the number of attributes requested in p_attrs
+*/
+typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs);
+
+/** Callback for register notification (Play state change/track change/...)
+**  param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED
+*/
+typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param);
+
+/* AVRCP 1.4 Enhancements */
+/** Callback for volume change on CT
+**  volume: Current volume setting on the CT (0-127)
+*/
+typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype);
+
+/** Callback for passthrough commands */
+typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state);
+
+/** BT-RC Target callback structure. */
+typedef struct {
+    /** set to sizeof(BtRcCallbacks) */
+    size_t      size;
+    btrc_remote_features_callback               remote_features_cb;
+    btrc_get_play_status_callback               get_play_status_cb;
+    btrc_list_player_app_attr_callback          list_player_app_attr_cb;
+    btrc_list_player_app_values_callback        list_player_app_values_cb;
+    btrc_get_player_app_value_callback          get_player_app_value_cb;
+    btrc_get_player_app_attrs_text_callback     get_player_app_attrs_text_cb;
+    btrc_get_player_app_values_text_callback    get_player_app_values_text_cb;
+    btrc_set_player_app_value_callback          set_player_app_value_cb;
+    btrc_get_element_attr_callback              get_element_attr_cb;
+    btrc_register_notification_callback         register_notification_cb;
+    btrc_volume_change_callback                 volume_change_cb;
+    btrc_passthrough_cmd_callback               passthrough_cmd_cb;
+} btrc_callbacks_t;
+
+/** Represents the standard BT-RC AVRCP Target interface. */
+typedef struct {
+
+    /** set to sizeof(BtRcInterface) */
+    size_t          size;
+    /**
+     * Register the BtRc callbacks
+     */
+    bt_status_t (*init)( btrc_callbacks_t* callbacks );
+
+    /** Respose to GetPlayStatus request. Contains the current
+    **  1. Play status
+    **  2. Song duration/length
+    **  3. Song position
+    */
+    bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos);
+
+    /** Lists the support player application attributes (Shuffle/Repeat/...)
+    **  num_attr: Specifies the number of attributes contained in the pointer p_attrs
+    */
+    bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs);
+
+    /** Lists the support player application attributes (Shuffle Off/On/Group)
+    **  num_val: Specifies the number of values contained in the pointer p_vals
+    */
+    bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals);
+
+    /** Returns the current application attribute values for each of the specified attr_id */
+    bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals);
+
+    /** Returns the application attributes text ("Shuffle"/"Repeat"/...)
+    **  num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
+    */
+    bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs);
+
+    /** Returns the application attributes text ("Shuffle"/"Repeat"/...)
+    **  num_attr: Specifies the number of attribute values' text contained in the pointer p_vals
+    */
+    bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals);
+
+    /** Returns the current songs' element attributes text ("Title"/"Album"/"Artist")
+    **  num_attr: Specifies the number of attributes' text contained in the pointer p_attrs
+    */
+    bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs);
+
+    /** Response to set player attribute request ("Shuffle"/"Repeat")
+    **  rsp_status: Status of setting the player attributes for the current media player
+    */
+    bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status);
+
+    /* Response to the register notification request (Play state change/track change/...).
+    ** event_id: Refers to the event_id this notification change corresponds too
+    ** type: Response type - interim/changed
+    ** p_params: Based on the event_id, this parameter should be populated
+    */
+    bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id,
+                                             btrc_notification_type_t type,
+                                             btrc_register_notification_t *p_param);
+
+    /* AVRCP 1.4 enhancements */
+
+    /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume
+    ** This can be enhanced to support Relative Volume (AVRCP 1.0).
+    ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level
+    ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set
+    */
+    bt_status_t (*set_volume)(uint8_t volume);
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+} btrc_interface_t;
+
+
+typedef void (* btrc_passthrough_rsp_callback) (int id, int key_state);
+
+typedef void (* btrc_connection_state_callback) (bool state, bt_bdaddr_t *bd_addr);
+
+/** BT-RC Controller callback structure. */
+typedef struct {
+    /** set to sizeof(BtRcCallbacks) */
+    size_t      size;
+    btrc_passthrough_rsp_callback               passthrough_rsp_cb;
+    btrc_connection_state_callback              connection_state_cb;
+} btrc_ctrl_callbacks_t;
+
+/** Represents the standard BT-RC AVRCP Controller interface. */
+typedef struct {
+
+    /** set to sizeof(BtRcInterface) */
+    size_t          size;
+    /**
+     * Register the BtRc callbacks
+     */
+    bt_status_t (*init)( btrc_ctrl_callbacks_t* callbacks );
+
+    /** send pass through command to target */
+    bt_status_t (*send_pass_through_cmd) ( bt_bdaddr_t *bd_addr, uint8_t key_code, uint8_t key_state );
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+} btrc_ctrl_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_RC_H */
diff --git a/bluez/repo/android/hardware/bt_sock.h b/bluez/repo/android/hardware/bt_sock.h
new file mode 100644
index 0000000..a4aa046
--- /dev/null
+++ b/bluez/repo/android/hardware/bt_sock.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_SOCK_H
+#define ANDROID_INCLUDE_BT_SOCK_H
+
+__BEGIN_DECLS
+
+#define BTSOCK_FLAG_ENCRYPT 1
+#define BTSOCK_FLAG_AUTH (1 << 1)
+
+typedef enum {
+    BTSOCK_RFCOMM = 1,
+    BTSOCK_SCO = 2,
+    BTSOCK_L2CAP = 3
+} btsock_type_t;
+
+/** Represents the standard BT SOCKET interface. */
+typedef struct {
+    short size;
+    bt_bdaddr_t bd_addr;
+    int channel;
+    int status;
+} __attribute__((packed)) sock_connect_signal_t;
+
+typedef struct {
+
+    /** set to size of this struct*/
+    size_t          size;
+    /**
+     * listen to a rfcomm uuid or channel. It returns the socket fd from which
+     * btsock_connect_signal can be read out when a remote device connected
+     */
+    bt_status_t (*listen)(btsock_type_t type, const char* service_name, const uint8_t* service_uuid, int channel, int* sock_fd, int flags);
+    /*
+     * connect to a rfcomm uuid channel of remote device, It returns the socket fd from which
+     * the btsock_connect_signal and a new socket fd to be accepted can be read out when connected
+     */
+    bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, btsock_type_t type, const uint8_t* uuid, int channel, int* sock_fd, int flags);
+
+} btsock_interface_t;
+
+__END_DECLS
+
+#endif /* ANDROID_INCLUDE_BT_SOCK_H */
diff --git a/bluez/repo/android/hardware/hardware.c b/bluez/repo/android/hardware/hardware.c
new file mode 100644
index 0000000..4bd5eba
--- /dev/null
+++ b/bluez/repo/android/hardware/hardware.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hardware.h>
+
+#include <dlfcn.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#define LOG_TAG "HAL"
+
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+
+/**
+ * Load the file defined by the variant and if successful
+ * return the dlopen handle and the hmi.
+ * @return 0 = success, !0 = failure.
+ */
+static int load(const char *id,
+        const char *path,
+        const struct hw_module_t **pHmi)
+{
+    int status;
+    void *handle;
+    struct hw_module_t *hmi;
+    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
+
+    /*
+     * load the symbols resolving undefined symbols before
+     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
+     * RTLD_NOW the external symbols will not be global
+     */
+    handle = dlopen(path, RTLD_NOW);
+    if (handle == NULL) {
+        char const *err_str = dlerror();
+        error("load: module=%s\n%s", path, err_str?err_str:"unknown");
+        status = -EINVAL;
+        goto done;
+    }
+
+    /* Get the address of the struct hal_module_info. */
+    hmi = (struct hw_module_t *)dlsym(handle, sym);
+    if (hmi == NULL) {
+        error("load: couldn't find symbol %s", sym);
+        status = -EINVAL;
+        goto done;
+    }
+
+    /* Check that the id matches */
+    if (strcmp(id, hmi->id) != 0) {
+        error("load: id=%s != hmi->id=%s", id, hmi->id);
+        status = -EINVAL;
+        goto done;
+    }
+
+    hmi->dso = handle;
+
+    *pHmi = hmi;
+
+    info("loaded HAL id=%s path=%s hmi=%p handle=%p",
+                id, path, *pHmi, handle);
+
+    return 0;
+
+done:
+    hmi = NULL;
+    if (handle != NULL) {
+        dlclose(handle);
+        handle = NULL;
+    }
+
+    return status;
+}
+
+int hw_get_module_by_class(const char *class_id, const char *inst,
+                           const struct hw_module_t **module)
+{
+    char path[PATH_MAX];
+    char name[PATH_MAX];
+
+    if (inst)
+        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
+    else
+        snprintf(name, PATH_MAX, "%s", class_id);
+
+    /*
+     * Here we rely on the fact that calling dlopen multiple times on
+     * the same .so will simply increment a refcount (and not load
+     * a new copy of the library).
+     * We also assume that dlopen() is thread-safe.
+     */
+    snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name);
+
+    return load(class_id, path, module);
+}
+
+int hw_get_module(const char *id, const struct hw_module_t **module)
+{
+    return hw_get_module_by_class(id, NULL, module);
+}
diff --git a/bluez/repo/android/hardware/hardware.h b/bluez/repo/android/hardware/hardware.h
new file mode 100644
index 0000000..c7e8cc7
--- /dev/null
+++ b/bluez/repo/android/hardware/hardware.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H
+#define ANDROID_INCLUDE_HARDWARE_HARDWARE_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/*
+ * Value for the hw_module_t.tag field
+ */
+
+#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
+
+#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
+#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
+
+#define HARDWARE_MAKE_API_VERSION(maj,min) \
+            ((((maj) & 0xff) << 8) | ((min) & 0xff))
+
+#define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \
+            ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff))
+#define HARDWARE_API_VERSION_2_MAJ_MIN_MASK 0xffff0000
+#define HARDWARE_API_VERSION_2_HEADER_MASK  0x0000ffff
+
+
+/*
+ * The current HAL API version.
+ *
+ * All module implementations must set the hw_module_t.hal_api_version field
+ * to this value when declaring the module with HAL_MODULE_INFO_SYM.
+ *
+ * Note that previous implementations have always set this field to 0.
+ * Therefore, libhardware HAL API will always consider versions 0.0 and 1.0
+ * to be 100% binary compatible.
+ *
+ */
+#define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0)
+
+/*
+ * Helper macros for module implementors.
+ *
+ * The derived modules should provide convenience macros for supported
+ * versions so that implementations can explicitly specify module/device
+ * versions at definition time.
+ *
+ * Use this macro to set the hw_module_t.module_api_version field.
+ */
+#define HARDWARE_MODULE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min)
+#define HARDWARE_MODULE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr)
+
+/*
+ * Use this macro to set the hw_device_t.version field
+ */
+#define HARDWARE_DEVICE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min)
+#define HARDWARE_DEVICE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr)
+
+struct hw_module_t;
+struct hw_module_methods_t;
+struct hw_device_t;
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+typedef struct hw_module_t {
+    /** tag must be initialized to HARDWARE_MODULE_TAG */
+    uint32_t tag;
+
+    /**
+     * The API version of the implemented module. The module owner is
+     * responsible for updating the version when a module interface has
+     * changed.
+     *
+     * The derived modules such as gralloc and audio own and manage this field.
+     * The module user must interpret the version field to decide whether or
+     * not to inter-operate with the supplied module implementation.
+     * For example, SurfaceFlinger is responsible for making sure that
+     * it knows how to manage different versions of the gralloc-module API,
+     * and AudioFlinger must know how to do the same for audio-module API.
+     *
+     * The module API version should include a major and a minor component.
+     * For example, version 1.0 could be represented as 0x0100. This format
+     * implies that versions 0x0100-0x01ff are all API-compatible.
+     *
+     * In the future, libhardware will expose a hw_get_module_version()
+     * (or equivalent) function that will take minimum/maximum supported
+     * versions as arguments and would be able to reject modules with
+     * versions outside of the supplied range.
+     */
+    uint16_t module_api_version;
+#define version_major module_api_version
+    /**
+     * version_major/version_minor defines are supplied here for temporary
+     * source code compatibility. They will be removed in the next version.
+     * ALL clients must convert to the new version format.
+     */
+
+    /**
+     * The API version of the HAL module interface. This is meant to
+     * version the hw_module_t, hw_module_methods_t, and hw_device_t
+     * structures and definitions.
+     *
+     * The HAL interface owns this field. Module users/implementations
+     * must NOT rely on this value for version information.
+     *
+     * Presently, 0 is the only valid value.
+     */
+    uint16_t hal_api_version;
+#define version_minor hal_api_version
+
+    /** Identifier of module */
+    const char *id;
+
+    /** Name of this module */
+    const char *name;
+
+    /** Author/owner/implementor of the module */
+    const char *author;
+
+    /** Modules methods */
+    struct hw_module_methods_t* methods;
+
+    /** module's dso */
+    void* dso;
+
+    /** padding to 128 bytes, reserved for future use */
+    uint32_t reserved[32-7];
+
+} hw_module_t;
+
+typedef struct hw_module_methods_t {
+    /** Open a specific device */
+    int (*open)(const struct hw_module_t* module, const char* id,
+            struct hw_device_t** device);
+
+} hw_module_methods_t;
+
+/**
+ * Every device data structure must begin with hw_device_t
+ * followed by module specific public methods and attributes.
+ */
+typedef struct hw_device_t {
+    /** tag must be initialized to HARDWARE_DEVICE_TAG */
+    uint32_t tag;
+
+    /**
+     * Version of the module-specific device API. This value is used by
+     * the derived-module user to manage different device implementations.
+     *
+     * The module user is responsible for checking the module_api_version
+     * and device version fields to ensure that the user is capable of
+     * communicating with the specific module implementation.
+     *
+     * One module can support multiple devices with different versions. This
+     * can be useful when a device interface changes in an incompatible way
+     * but it is still necessary to support older implementations at the same
+     * time. One such example is the Camera 2.0 API.
+     *
+     * This field is interpreted by the module user and is ignored by the
+     * HAL interface itself.
+     */
+    uint32_t version;
+
+    /** reference to the module this device belongs to */
+    struct hw_module_t* module;
+
+    /** padding reserved for future use */
+    uint32_t reserved[12];
+
+    /** Close this device */
+    int (*close)(struct hw_device_t* device);
+
+} hw_device_t;
+
+/**
+ * Name of the hal_module_info
+ */
+#define HAL_MODULE_INFO_SYM         HMI
+
+/**
+ * Name of the hal_module_info as a string
+ */
+#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
+
+/**
+ * Get the module info associated with a module by id.
+ *
+ * @return: 0 == success, <0 == error and *module == NULL
+ */
+int hw_get_module(const char *id, const struct hw_module_t **module);
+
+/**
+ * Get the module info associated with a module instance by class 'class_id'
+ * and instance 'inst'.
+ *
+ * Some modules types necessitate multiple instances. For example audio supports
+ * multiple concurrent interfaces and thus 'audio' is the module class
+ * and 'primary' or 'a2dp' are module interfaces. This implies that the files
+ * providing these modules would be named audio.primary.<variant>.so and
+ * audio.a2dp.<variant>.so
+ *
+ * @return: 0 == success, <0 == error and *module == NULL
+ */
+int hw_get_module_by_class(const char *class_id, const char *inst,
+                           const struct hw_module_t **module);
+
+__END_DECLS
+
+#endif  /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */
diff --git a/bluez/repo/android/health.c b/bluez/repo/android/health.c
new file mode 100644
index 0000000..eb02a64
--- /dev/null
+++ b/bluez/repo/android/health.c
@@ -0,0 +1,2047 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/l2cap.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/uuid-helper.h"
+#include "src/sdp-client.h"
+#include "profiles/health/mcap.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "health.h"
+
+#define SVC_HINT_HEALTH			0x00
+#define HDP_VERSION			0x0101
+#define DATA_EXCHANGE_SPEC_11073	0x01
+
+#define CHANNEL_TYPE_ANY       0x00
+#define CHANNEL_TYPE_RELIABLE  0x01
+#define CHANNEL_TYPE_STREAM    0x02
+
+#define MDEP_ECHO		0x00
+#define MDEP_INITIAL		0x01
+#define MDEP_FINAL		0x7F
+
+static bdaddr_t adapter_addr;
+static struct ipc *hal_ipc = NULL;
+static struct queue *apps = NULL;
+static struct mcap_instance *mcap = NULL;
+static uint32_t record_id = 0;
+static uint32_t record_state = 0;
+
+struct mdep_cfg {
+	uint8_t role;
+	uint16_t data_type;
+	uint8_t channel_type;
+	char *descr;
+
+	uint8_t id; /* mdep id */
+};
+
+struct health_device {
+	bdaddr_t dst;
+	uint16_t app_id;
+
+	struct mcap_mcl *mcl;
+
+	struct queue *channels;     /* data channels */
+
+	uint16_t ccpsm;
+	uint16_t dcpsm;
+};
+
+struct health_channel {
+	uint8_t mdep_id;
+	uint8_t type;
+
+	struct health_device *dev;
+
+	uint8_t remote_mdep;
+	struct mcap_mdl *mdl;
+	bool mdl_conn;
+	uint16_t mdl_id; /* MDL ID */
+
+	uint16_t id; /* channel id */
+};
+
+struct health_app {
+	char *app_name;
+	char *provider_name;
+	char *service_name;
+	char *service_descr;
+	uint8_t num_of_mdep;
+	struct queue *mdeps;
+
+	uint16_t id; /* app id */
+	struct queue *devices;
+};
+
+static void send_app_reg_notify(struct health_app *app, uint8_t state)
+{
+	struct hal_ev_health_app_reg_state ev;
+
+	DBG("");
+
+	ev.id = app->id;
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev);
+}
+
+static void send_channel_state_notify(struct health_channel *channel,
+						uint8_t state, int fd)
+{
+	struct hal_ev_health_channel_state ev;
+
+	DBG("");
+
+	bdaddr2android(&channel->dev->dst, ev.bdaddr);
+	ev.app_id = channel->dev->app_id;
+	ev.mdep_index = channel->mdep_id - 1;
+	ev.channel_id = channel->id;
+	ev.channel_state = state;
+
+	ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH,
+					HAL_EV_HEALTH_CHANNEL_STATE,
+					sizeof(ev), &ev, fd);
+}
+
+static void unref_mdl(struct health_channel *channel)
+{
+	if (!channel || !channel->mdl)
+		return;
+
+	mcap_mdl_unref(channel->mdl);
+	channel->mdl = NULL;
+	channel->mdl_conn = false;
+}
+
+static void free_health_channel(void *data)
+{
+	struct health_channel *channel = data;
+	int fd;
+
+	DBG("channel %p", channel);
+
+	if (!channel)
+		return;
+
+	fd = mcap_mdl_get_fd(channel->mdl);
+	if (fd >= 0)
+		shutdown(fd, SHUT_RDWR);
+
+	unref_mdl(channel);
+	free(channel);
+}
+
+static void destroy_channel(void *data)
+{
+	struct health_channel *channel = data;
+
+	if (!channel)
+		return;
+
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1);
+	queue_remove(channel->dev->channels, channel);
+	free_health_channel(channel);
+}
+
+static void unref_mcl(struct health_device *dev)
+{
+	if (!dev || !dev->mcl)
+		return;
+
+	mcap_close_mcl(dev->mcl, FALSE);
+	mcap_mcl_unref(dev->mcl);
+	dev->mcl = NULL;
+}
+
+static void free_health_device(void *data)
+{
+	struct health_device *dev = data;
+
+	if (!dev)
+		return;
+
+	unref_mcl(dev);
+	queue_destroy(dev->channels, free_health_channel);
+	free(dev);
+}
+
+static void free_mdep_cfg(void *data)
+{
+	struct mdep_cfg *cfg = data;
+
+	if (!cfg)
+		return;
+
+	free(cfg->descr);
+	free(cfg);
+}
+
+static void free_health_app(void *data)
+{
+	struct health_app *app = data;
+
+	if (!app)
+		return;
+
+	free(app->app_name);
+	free(app->provider_name);
+	free(app->service_name);
+	free(app->service_descr);
+	queue_destroy(app->mdeps, free_mdep_cfg);
+	queue_destroy(app->devices, free_health_device);
+	free(app);
+}
+
+static bool match_channel_by_mdl(const void *data, const void *user_data)
+{
+	const struct health_channel *channel = data;
+	const struct mcap_mdl *mdl = user_data;
+
+	return channel->mdl == mdl;
+}
+
+static bool match_channel_by_id(const void *data, const void *user_data)
+{
+	const struct health_channel *channel = data;
+	uint16_t channel_id = PTR_TO_INT(user_data);
+
+	return channel->id == channel_id;
+}
+
+static bool match_dev_by_mcl(const void *data, const void *user_data)
+{
+	const struct health_device *dev = data;
+	const struct mcap_mcl *mcl = user_data;
+
+	return dev->mcl == mcl;
+}
+
+static bool match_dev_by_addr(const void *data, const void *user_data)
+{
+	const struct health_device *dev = data;
+	const bdaddr_t *addr = user_data;
+
+	return !bacmp(&dev->dst, addr);
+}
+
+static bool match_channel_by_mdep_id(const void *data, const void *user_data)
+{
+	const struct health_channel *channel = data;
+	uint16_t mdep_id = PTR_TO_INT(user_data);
+
+	return channel->mdep_id == mdep_id;
+}
+
+static bool match_mdep_by_role(const void *data, const void *user_data)
+{
+	const struct mdep_cfg *mdep = data;
+	uint16_t role = PTR_TO_INT(user_data);
+
+	return mdep->role == role;
+}
+
+static bool match_mdep_by_id(const void *data, const void *user_data)
+{
+	const struct mdep_cfg *mdep = data;
+	uint16_t mdep_id = PTR_TO_INT(user_data);
+
+	return mdep->id == mdep_id;
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+	const struct health_app *app = data;
+	uint16_t app_id = PTR_TO_INT(user_data);
+
+	return app->id == app_id;
+}
+
+static struct health_channel *search_channel_by_id(uint16_t id)
+{
+	const struct queue_entry *apps_entry, *devices_entry;
+	struct health_app *app;
+	struct health_channel *channel;
+	struct health_device *dev;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+		devices_entry = queue_get_entries(app->devices);
+		while (devices_entry) {
+			dev = devices_entry->data;
+			channel = queue_find(dev->channels, match_channel_by_id,
+								INT_TO_PTR(id));
+
+			if (channel)
+				return channel;
+
+			devices_entry = devices_entry->next;
+		}
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static struct health_channel *search_channel_by_mdl(struct mcap_mdl *mdl)
+{
+	const struct queue_entry *apps_entry, *devices_entry;
+	struct health_app *app;
+	struct health_channel *channel;
+	struct health_device *dev;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+		devices_entry = queue_get_entries(app->devices);
+		while (devices_entry) {
+			dev = devices_entry->data;
+			channel = queue_find(dev->channels,
+						match_channel_by_mdl, mdl);
+
+			if (channel)
+				return channel;
+
+			devices_entry = devices_entry->next;
+		}
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static struct health_device *search_dev_by_mcl(struct mcap_mcl *mcl)
+{
+	const struct queue_entry *apps_entry;
+	struct health_app *app;
+	struct health_device *dev;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+
+		dev = queue_find(app->devices, match_dev_by_mcl, mcl);
+
+		if (dev)
+			return dev;
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static struct health_app *search_app_by_mdepid(uint8_t mdepid)
+{
+	const struct queue_entry *apps_entry;
+	struct health_app *app;
+
+	DBG("");
+
+	apps_entry = queue_get_entries(apps);
+	while (apps_entry) {
+		app = apps_entry->data;
+
+		if (queue_find(app->mdeps, match_mdep_by_id,
+							INT_TO_PTR(mdepid)))
+			return app;
+
+		apps_entry = apps_entry->next;
+	}
+
+	return NULL;
+}
+
+static int register_service_protocols(sdp_record_t *rec,
+					struct health_app *app)
+{
+	uuid_t l2cap_uuid, mcap_c_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL, *mcap_ver = NULL;
+	uint32_t ccpsm;
+	uint16_t version = MCAP_VERSION;
+	GError *err = NULL;
+	int ret = -1;
+
+	DBG("");
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (!l2cap_list)
+		goto fail;
+
+	ccpsm = mcap_get_ctrl_psm(mcap, &err);
+	if (err)
+		goto fail;
+
+	psm = sdp_data_alloc(SDP_UINT16, &ccpsm);
+	if (!psm)
+		goto fail;
+
+	if (!sdp_list_append(l2cap_list, psm))
+		goto fail;
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (!proto_list)
+		goto fail;
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+	if (!mcap_list)
+		goto fail;
+
+	mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+	if (!mcap_ver)
+		goto fail;
+
+	if (!sdp_list_append(mcap_list, mcap_ver))
+		goto fail;
+
+	if (!sdp_list_append(proto_list, mcap_list))
+		goto fail;
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (!access_proto_list)
+		goto fail;
+
+	sdp_set_access_protos(rec, access_proto_list);
+	ret = 0;
+
+fail:
+	sdp_list_free(l2cap_list, NULL);
+	sdp_list_free(mcap_list, NULL);
+	sdp_list_free(proto_list, NULL);
+	sdp_list_free(access_proto_list, NULL);
+
+	if (psm)
+		sdp_data_free(psm);
+
+	if (mcap_ver)
+		sdp_data_free(mcap_ver);
+
+	if (err)
+		g_error_free(err);
+
+	return ret;
+}
+
+static int register_service_profiles(sdp_record_t *rec)
+{
+	int ret;
+	sdp_list_t *profile_list;
+	sdp_profile_desc_t hdp_profile;
+
+	DBG("");
+
+	/* set hdp information */
+	sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+	hdp_profile.version = HDP_VERSION;
+	profile_list = sdp_list_append(NULL, &hdp_profile);
+	if (!profile_list)
+		return -1;
+
+	/* set profile descriptor list */
+	ret = sdp_set_profile_descs(rec, profile_list);
+	sdp_list_free(profile_list, NULL);
+
+	return ret;
+}
+
+static int register_service_additional_protocols(sdp_record_t *rec,
+						struct health_app *app)
+{
+	int ret = -1;
+	uuid_t l2cap_uuid, mcap_d_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL;
+	uint32_t dcpsm;
+	GError *err = NULL;
+
+	DBG("");
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (!l2cap_list)
+		goto fail;
+
+	dcpsm = mcap_get_data_psm(mcap, &err);
+	if (err)
+		goto fail;
+
+	psm = sdp_data_alloc(SDP_UINT16, &dcpsm);
+	if (!psm)
+		goto fail;
+
+	if (!sdp_list_append(l2cap_list, psm))
+		goto fail;
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (!proto_list)
+		goto fail;
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+	if (!mcap_list)
+		goto fail;
+
+	if (!sdp_list_append(proto_list, mcap_list))
+		goto fail;
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (!access_proto_list)
+		goto fail;
+
+	sdp_set_add_access_protos(rec, access_proto_list);
+	ret = 0;
+
+fail:
+	sdp_list_free(l2cap_list, NULL);
+	sdp_list_free(mcap_list, NULL);
+	sdp_list_free(proto_list, NULL);
+	sdp_list_free(access_proto_list, NULL);
+
+	if (psm)
+		sdp_data_free(psm);
+
+	if (err)
+		g_error_free(err);
+
+	return ret;
+}
+
+static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep)
+{
+	sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL;
+	sdp_list_t *f_list = NULL;
+
+	DBG("");
+
+	mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id);
+	if (!mdepid)
+		return NULL;
+
+	dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type);
+	if (!dtype)
+		goto fail;
+
+	role = sdp_data_alloc(SDP_UINT8, &mdep->role);
+	if (!role)
+		goto fail;
+
+	if (mdep->descr) {
+		descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr);
+		if (!descr)
+			goto fail;
+	}
+
+	f_list = sdp_list_append(NULL, mdepid);
+	if (!f_list)
+		goto fail;
+
+	if (!sdp_list_append(f_list, dtype))
+		goto fail;
+
+	if (!sdp_list_append(f_list, role))
+		goto fail;
+
+	if (descr && !sdp_list_append(f_list, descr))
+		goto fail;
+
+	return f_list;
+
+fail:
+	sdp_list_free(f_list, NULL);
+
+	if (mdepid)
+		sdp_data_free(mdepid);
+
+	if (dtype)
+		sdp_data_free(dtype);
+
+	if (role)
+		sdp_data_free(role);
+
+	if (descr)
+		sdp_data_free(descr);
+
+	return NULL;
+}
+
+static void free_hdp_list(void *list)
+{
+	sdp_list_t *hdp_list = list;
+
+	sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static void register_features(void *data, void *user_data)
+{
+	struct mdep_cfg *mdep = data;
+	sdp_list_t **sup_features = user_data;
+	sdp_list_t *hdp_feature;
+
+	DBG("");
+
+	hdp_feature = mdeps_to_sdp_features(mdep);
+	if (!hdp_feature)
+		return;
+
+	if (!*sup_features) {
+		*sup_features = sdp_list_append(NULL, hdp_feature);
+		if (!*sup_features)
+			sdp_list_free(hdp_feature,
+					(sdp_free_func_t)sdp_data_free);
+	} else if (!sdp_list_append(*sup_features, hdp_feature)) {
+		sdp_list_free(hdp_feature,
+					(sdp_free_func_t)sdp_data_free);
+	}
+}
+
+static int register_service_sup_features(sdp_record_t *rec,
+						struct health_app *app)
+{
+	sdp_list_t *sup_features = NULL;
+
+	DBG("");
+
+	queue_foreach(app->mdeps, register_features, &sup_features);
+	if (!sup_features)
+		return -1;
+
+	if (sdp_set_supp_feat(rec, sup_features) < 0) {
+		sdp_list_free(sup_features, free_hdp_list);
+		return -1;
+	}
+
+	sdp_list_free(sup_features, free_hdp_list);
+	return 0;
+}
+
+static int register_data_exchange_spec(sdp_record_t *rec)
+{
+	sdp_data_t *spec;
+	uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+	/* As of now only 11073 is supported, so we set it as default */
+
+	DBG("");
+
+	spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+	if (!spec)
+		return -1;
+
+	if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+		sdp_data_free(spec);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int register_mcap_features(sdp_record_t *rec)
+{
+	sdp_data_t *mcap_proc;
+	uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+	DBG("");
+
+	mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+	if (!mcap_proc)
+		return -1;
+
+	if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+							mcap_proc) < 0) {
+		sdp_data_free(mcap_proc);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role)
+{
+	uuid_t source, sink;
+	sdp_list_t *list = NULL;
+
+	sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID);
+	sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID);
+	sdp_get_service_classes(rec, &list);
+
+	switch (role) {
+	case HAL_HEALTH_MDEP_ROLE_SOURCE:
+		if (!sdp_list_find(list, &source, sdp_uuid_cmp))
+			list = sdp_list_append(list, &source);
+		break;
+	case HAL_HEALTH_MDEP_ROLE_SINK:
+		if (!sdp_list_find(list, &sink, sdp_uuid_cmp))
+			list = sdp_list_append(list, &sink);
+		break;
+	}
+
+	if (sdp_set_service_classes(rec, list) < 0) {
+		sdp_list_free(list, NULL);
+		return -1;
+	}
+
+	sdp_list_free(list, NULL);
+
+	return 0;
+}
+
+static int update_sdp_record(struct health_app *app)
+{
+	sdp_record_t *rec;
+	uint8_t role;
+
+	DBG("");
+
+	if (record_id > 0) {
+		bt_adapter_remove_record(record_id);
+		record_id = 0;
+	}
+
+	rec = sdp_record_alloc();
+	if (!rec)
+		return -1;
+
+	role = HAL_HEALTH_MDEP_ROLE_SOURCE;
+	if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role)))
+		set_sdp_services_uuid(rec, role);
+
+	role = HAL_HEALTH_MDEP_ROLE_SINK;
+	if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role)))
+		set_sdp_services_uuid(rec, role);
+
+	sdp_set_info_attr(rec, app->service_name, app->provider_name,
+							app->service_descr);
+
+	if (register_service_protocols(rec, app) < 0)
+		goto fail;
+
+	if (register_service_profiles(rec) < 0)
+		goto fail;
+
+	if (register_service_additional_protocols(rec, app) < 0)
+		goto fail;
+
+	if (register_service_sup_features(rec, app) < 0)
+		goto fail;
+
+	if (register_data_exchange_spec(rec) < 0)
+		goto fail;
+
+	if (register_mcap_features(rec) < 0)
+		goto fail;
+
+	if (sdp_set_record_state(rec, record_state++) < 0)
+		goto fail;
+
+	if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) {
+		error("health: Failed to register HEALTH record");
+		goto fail;
+	}
+
+	record_id = rec->handle;
+
+	return 0;
+
+fail:
+	sdp_record_free(rec);
+
+	return -1;
+}
+
+static struct health_app *create_health_app(const char *app_name,
+				const char *provider, const char *srv_name,
+				const char *srv_descr, uint8_t mdeps)
+{
+	struct health_app *app;
+	static unsigned int app_id = 1;
+
+	DBG("");
+
+	app = new0(struct health_app, 1);
+	app->id = app_id++;
+	app->num_of_mdep = mdeps;
+	app->app_name = strdup(app_name);
+
+	if (provider) {
+		app->provider_name = strdup(provider);
+		if (!app->provider_name)
+			goto fail;
+	}
+
+	if (srv_name) {
+		app->service_name = strdup(srv_name);
+		if (!app->service_name)
+			goto fail;
+	}
+
+	if (srv_descr) {
+		app->service_descr = strdup(srv_descr);
+		if (!app->service_descr)
+			goto fail;
+	}
+
+	app->mdeps = queue_new();
+	app->devices = queue_new();
+
+	return app;
+
+fail:
+	free_health_app(app);
+	return NULL;
+}
+
+static void bt_health_register_app(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_reg_app *cmd = buf;
+	struct hal_rsp_health_reg_app rsp;
+	struct health_app *app;
+	uint16_t off;
+	uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len;
+	char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len ||
+			cmd->app_name_off > cmd->provider_name_off ||
+			cmd->provider_name_off > cmd->service_name_off ||
+			cmd->service_name_off > cmd->service_descr_off ||
+			cmd->service_descr_off > cmd->len) {
+		error("health: Invalid register app command, terminating");
+		raise(SIGTERM);
+		return;
+	}
+
+	app_name = (char *) cmd->data;
+	app_name_len = cmd->provider_name_off - cmd->app_name_off;
+
+	off = app_name_len;
+	provider_len = cmd->service_name_off - off;
+	if (provider_len > 0)
+		provider = (char *) cmd->data + off;
+
+	off += provider_len;
+	srv_name_len = cmd->service_descr_off - off;
+	if (srv_name_len > 0)
+		srv_name = (char *) cmd->data + off;
+
+	off += srv_name_len;
+	srv_descr_len = cmd->len - off;
+	if (srv_descr_len > 0)
+		srv_descr = (char *) cmd->data + off;
+
+	app = create_health_app(app_name, provider, srv_name, srv_descr,
+							cmd->num_of_mdep);
+	if (!app)
+		goto fail;
+
+	queue_push_tail(apps, app);
+
+	rsp.app_id = app->id;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+							sizeof(rsp), &rsp, -1);
+	return;
+
+fail:
+	free_health_app(app);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+							HAL_STATUS_FAILED);
+}
+
+static uint8_t android2channel_type(uint8_t type)
+{
+	switch (type) {
+	case HAL_HEALTH_CHANNEL_TYPE_RELIABLE:
+		return CHANNEL_TYPE_RELIABLE;
+	case HAL_HEALTH_CHANNEL_TYPE_STREAMING:
+		return CHANNEL_TYPE_STREAM;
+	default:
+		return CHANNEL_TYPE_ANY;
+	}
+}
+
+static void bt_health_mdep_cfg_data(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_mdep *cmd = buf;
+	struct health_app *app;
+	struct mdep_cfg *mdep = NULL;
+	uint8_t status;
+
+	DBG("");
+
+	app = queue_find(apps, match_app_by_id, INT_TO_PTR(cmd->app_id));
+	if (!app) {
+		status = HAL_STATUS_INVALID;
+		goto fail;
+	}
+
+	mdep = new0(struct mdep_cfg, 1);
+	mdep->role = cmd->role;
+	mdep->data_type = cmd->data_type;
+	mdep->channel_type = android2channel_type(cmd->channel_type);
+	mdep->id = queue_length(app->mdeps) + 1;
+
+	if (cmd->descr_len > 0) {
+		mdep->descr = malloc0(cmd->descr_len);
+		memcpy(mdep->descr, cmd->descr, cmd->descr_len);
+	}
+
+	queue_push_tail(app->mdeps, mdep);
+
+	if (app->num_of_mdep != queue_length(app->mdeps))
+		goto send_rsp;
+
+	/* add sdp record from app configuration data */
+	/*
+	 * TODO: Check what to be done if mupltple applications are trying to
+	 * register with different role and different configurations.
+	 * 1) Does device supports SOURCE and SINK at the same time ?
+	 * 2) Does it require different SDP records or one record with
+	 *    multile MDEP configurations ?
+	 */
+	if (update_sdp_record(app) < 0) {
+		error("health: HDP SDP record preparation failed");
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS);
+
+send_rsp:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+							HAL_STATUS_SUCCESS);
+	return;
+
+fail:
+	if (status != HAL_STATUS_SUCCESS) {
+		free_mdep_cfg(mdep);
+		queue_remove(apps, app);
+		free_health_app(app);
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+								status);
+}
+
+static void bt_health_unregister_app(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_unreg_app *cmd = buf;
+	struct health_app *app;
+
+	DBG("");
+
+	app = queue_remove_if(apps, match_app_by_id, INT_TO_PTR(cmd->app_id));
+	if (!app) {
+		ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID);
+		return;
+	}
+
+	send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS);
+
+	if (record_id > 0) {
+		bt_adapter_remove_record(record_id);
+		record_id = 0;
+	}
+
+	free_health_app(app);
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS);
+}
+
+static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+	sdp_data_t *iter;
+	int proto;
+
+	if (!entry || !SDP_IS_SEQ(entry->dtd))
+		return -1;
+
+	iter = entry->val.dataseq;
+	if (!(iter->dtd & SDP_UUID_UNSPEC))
+		return -1;
+
+	proto = sdp_uuid_to_proto(&iter->val.uuid);
+	if (proto != type)
+		return -1;
+
+	if (!val)
+		return 0;
+
+	iter = iter->next;
+	if (iter->dtd != SDP_UINT16)
+		return -1;
+
+	*val = iter->val.uint16;
+
+	return 0;
+}
+
+static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm,
+							uint16_t *version)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (!psm && !version)
+		return -1;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+	if (!pdl || !SDP_IS_SEQ(pdl->dtd))
+		return -1;
+
+	p0 = pdl->val.dataseq;
+	if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+		return -1;
+
+	p1 = p0->next;
+	if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (!get_prot_desc_list(rec, ccpsm, NULL))
+			return 0;
+	}
+
+	return -1;
+}
+
+static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (!psm)
+		return -1;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+	if (!pdl || pdl->dtd != SDP_SEQ8)
+		return -1;
+
+	pdl = pdl->val.dataseq;
+	if (pdl->dtd != SDP_SEQ8)
+		return -1;
+
+	p0 = pdl->val.dataseq;
+
+	if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0)
+		return -1;
+
+	p1 = p0->next;
+	if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (!get_add_prot_desc_list(rec, dcpsm))
+			return 0;
+	}
+
+	return -1;
+}
+
+static int send_echo_data(int sock, const void *buf, uint32_t size)
+{
+	const uint8_t *buf_b = buf;
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = write(sock, buf_b + sent, size - sent);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+
+	return 0;
+}
+
+static gboolean serve_echo(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+	struct health_channel *channel = data;
+	uint8_t buf[MCAP_DC_MTU];
+	int fd, len, ret;
+
+	DBG("channel %p", channel);
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		DBG("Error condition on channel");
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(io);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0) {
+		DBG("Error reading ECHO");
+		return FALSE;
+	}
+
+	ret = send_echo_data(fd, buf, len);
+	if (ret != len)
+		DBG("Error sending ECHO back");
+
+	return FALSE;
+}
+
+static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel = data;
+	int fd;
+
+	DBG("Data channel connected: mdl %p channel %p", mdl, channel);
+
+	if (!channel) {
+		channel = search_channel_by_mdl(mdl);
+		if (!channel) {
+			error("health: channel data does not exist");
+			return;
+		}
+	}
+
+	if (!channel->mdl)
+		channel->mdl = mcap_mdl_ref(mdl);
+
+	fd = mcap_mdl_get_fd(channel->mdl);
+	if (fd < 0) {
+		error("health: error retrieving fd");
+		goto fail;
+	}
+
+	if (channel->mdep_id == MDEP_ECHO) {
+		GIOChannel *io;
+
+		io = g_io_channel_unix_new(fd);
+		g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+							serve_echo, channel);
+		g_io_channel_unref(io);
+
+		return;
+	}
+
+	info("health: MDL connected");
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd);
+
+	return;
+fail:
+	/* TODO: mcap_mdl_abort */
+	destroy_channel(channel);
+}
+
+static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel = data;
+
+	info("health: MDL closed");
+
+	if (!channel)
+		return;
+
+	channel->mdl_conn = false;
+}
+
+static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel;
+
+	info("health: MDL deleted");
+
+	channel = search_channel_by_mdl(mdl);
+	if (!channel)
+		return;
+
+	DBG("channel %p mdl %p", channel, mdl);
+	destroy_channel(channel);
+}
+
+static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+	DBG("Not Implemeneted");
+}
+
+static struct health_device *create_device(struct health_app *app,
+							const uint8_t *addr)
+{
+	struct health_device *dev;
+
+	/* create device and push it to devices queue */
+	dev = new0(struct health_device, 1);
+
+	android2bdaddr(addr, &dev->dst);
+	dev->channels = queue_new();
+	dev->app_id = app->id;
+
+	queue_push_tail(app->devices, dev);
+
+	return dev;
+}
+
+static struct health_device *get_device(struct health_app *app,
+							const uint8_t *addr)
+{
+	struct health_device *dev;
+	bdaddr_t bdaddr;
+
+	android2bdaddr(addr, &bdaddr);
+	dev = queue_find(app->devices, match_dev_by_addr, &bdaddr);
+	if (dev)
+		return dev;
+
+	return create_device(app, addr);
+}
+
+static struct health_channel *create_channel(struct health_app *app,
+						uint8_t mdep_index,
+						struct health_device *dev)
+{
+	struct mdep_cfg *mdep;
+	struct health_channel *channel;
+	static unsigned int channel_id = 1;
+
+	DBG("mdep %u", mdep_index);
+
+	if (!dev || !app)
+		return NULL;
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdep_index));
+	if (!mdep) {
+		if (mdep_index == MDEP_ECHO) {
+			mdep = new0(struct mdep_cfg, 1);
+
+			/* Leave other configuration zeroes */
+			mdep->id = MDEP_ECHO;
+
+			queue_push_tail(app->mdeps, mdep);
+		} else {
+			return NULL;
+		}
+	}
+
+	/* create channel and push it to device */
+	channel = new0(struct health_channel, 1);
+	channel->mdep_id = mdep->id;
+	channel->type = mdep->channel_type;
+	channel->id = channel_id++;
+	channel->dev = dev;
+
+	queue_push_tail(dev->channels, channel);
+
+	return channel;
+}
+
+static struct health_channel *connect_channel(struct health_app *app,
+							struct mcap_mcl *mcl,
+							uint8_t mdepid)
+{
+	struct health_device *device;
+	bdaddr_t addr;
+
+	DBG("app %p mdepid %u", app, mdepid);
+
+	mcap_mcl_get_addr(mcl, &addr);
+
+	if (!app) {
+		DBG("No app found for mdepid %u", mdepid);
+		return NULL;
+	}
+
+	device = get_device(app, (uint8_t *) &addr);
+
+	return create_channel(app, mdepid, device);
+}
+
+static uint8_t conf_to_l2cap(uint8_t conf)
+{
+	return conf == CHANNEL_TYPE_STREAM ? L2CAP_MODE_STREAMING :
+								L2CAP_MODE_ERTM;
+}
+
+static uint8_t mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+				uint16_t mdlid, uint8_t *conf, void *data)
+{
+	GError *gerr = NULL;
+	struct health_channel *channel;
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+
+	DBG("Data channel request: mdepid %u mdlid %u conf %u",
+							mdepid, mdlid, *conf);
+
+	if (mdepid == MDEP_ECHO)
+		/* For echo service take last app */
+		app = queue_peek_tail(apps);
+	else
+		app = search_app_by_mdepid(mdepid);
+
+	if (!app)
+		return MCAP_MDL_BUSY;
+
+	channel = connect_channel(app, mcl, mdepid);
+	if (!channel)
+		return MCAP_MDL_BUSY;
+
+	/* Channel is assigned here after creation */
+	mcl->cb->user_data = channel;
+
+	if (mdepid == MDEP_ECHO) {
+		switch (*conf) {
+		case CHANNEL_TYPE_ANY:
+			*conf = CHANNEL_TYPE_RELIABLE;
+			break;
+		case CHANNEL_TYPE_RELIABLE:
+			break;
+		case CHANNEL_TYPE_STREAM:
+			return MCAP_CONFIGURATION_REJECTED;
+		default:
+			/*
+			 * Special case defined in HDP spec 3.4.
+			 * When an invalid configuration is received we shall
+			 * close the MCL when we are still processing the
+			 * callback.
+			 */
+			/* TODO close device */
+			return MCAP_CONFIGURATION_REJECTED; /* not processed */
+		}
+
+		if (!mcap_set_data_chan_mode(mcap, L2CAP_MODE_ERTM, &gerr)) {
+			error("Error: %s", gerr->message);
+			g_error_free(gerr);
+			return MCAP_MDL_BUSY;
+		}
+
+		/* TODO: Create channel */
+
+		return MCAP_SUCCESS;
+	}
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdepid));
+	if (!mdep)
+		return MCAP_MDL_BUSY;
+
+	switch (*conf) {
+	case CHANNEL_TYPE_ANY:
+		if (mdep->role == HAL_HEALTH_MDEP_ROLE_SINK) {
+			return MCAP_CONFIGURATION_REJECTED;
+		} else {
+			if (queue_length(channel->dev->channels) <= 1)
+				*conf = CHANNEL_TYPE_RELIABLE;
+			else
+				*conf = CHANNEL_TYPE_STREAM;
+		}
+		break;
+	case CHANNEL_TYPE_STREAM:
+		if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+			return MCAP_CONFIGURATION_REJECTED;
+		break;
+	case CHANNEL_TYPE_RELIABLE:
+		if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+			return MCAP_CONFIGURATION_REJECTED;
+		break;
+	default:
+		/*
+		 * Special case defined in HDP spec 3.4. When an invalid
+		 * configuration is received we shall close the MCL when
+		 * we are still processing the callback.
+		 */
+		/* TODO: close device */
+		return MCAP_CONFIGURATION_REJECTED; /* not processed */
+	}
+
+	if (!mcap_set_data_chan_mode(mcap, conf_to_l2cap(*conf), &gerr)) {
+		error("health: error setting L2CAP mode: %s", gerr->message);
+		g_error_free(gerr);
+		return MCAP_MDL_BUSY;
+	}
+
+	return MCAP_SUCCESS;
+}
+
+static uint8_t mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct health_channel *channel;
+	GError *err = NULL;
+
+	DBG("");
+
+	channel = search_channel_by_mdl(mdl);
+	if (!channel) {
+		error("health: channel data does not exist");
+		return MCAP_UNSPECIFIED_ERROR;
+	}
+
+	if (!mcap_set_data_chan_mode(mcap,
+			conf_to_l2cap(channel->type), &err)) {
+		error("health: %s", err->message);
+		g_error_free(err);
+		return MCAP_MDL_BUSY;
+	}
+
+	return MCAP_SUCCESS;
+}
+
+static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+	struct health_channel *channel = data;
+	int fd;
+
+	DBG("");
+
+	if (gerr) {
+		error("health: error connecting to MDL %s", gerr->message);
+		goto fail;
+	}
+
+	fd = mcap_mdl_get_fd(channel->mdl);
+	if (fd < 0) {
+		error("health: error retrieving fd");
+		goto fail;
+	}
+
+	info("health: MDL connected");
+	channel->mdl_conn = true;
+
+	/* first data channel should be reliable data channel */
+	if (!queue_length(channel->dev->channels))
+		if (channel->type != CHANNEL_TYPE_RELIABLE)
+			goto fail;
+
+	send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd);
+
+	return;
+
+fail:
+	/* TODO: mcap_mdl_abort */
+	destroy_channel(channel);
+}
+
+static void reconnect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+	struct health_channel *channel = data;
+	uint8_t mode;
+	GError *err = NULL;
+
+	DBG("");
+
+	if (gerr) {
+		error("health: error reconnecting to MDL %s", gerr->message);
+		goto fail;
+	}
+
+	channel->mdl_id = mcap_mdl_get_mdlid(mdl);
+
+	if (channel->type == CHANNEL_TYPE_RELIABLE)
+		mode = L2CAP_MODE_ERTM;
+	else
+		mode = L2CAP_MODE_STREAMING;
+
+	if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm,
+						connect_mdl_cb, channel,
+						NULL, &err)) {
+		error("health: error connecting to mdl");
+		g_error_free(err);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	/* TODO: mcap_mdl_abort */
+	destroy_channel(channel);
+}
+
+static int reconnect_mdl(struct health_channel *channel)
+{
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (!channel)
+		return -1;
+
+	if (!mcap_reconnect_mdl(channel->mdl, reconnect_mdl_cb, channel,
+								NULL, &gerr)){
+		error("health: reconnect failed %s", gerr->message);
+		destroy_channel(channel);
+	}
+
+	return 0;
+}
+
+static void create_mdl_cb(struct mcap_mdl *mdl, uint8_t type, GError *gerr,
+								gpointer data)
+{
+	struct health_channel *channel = data;
+	uint8_t mode;
+	GError *err = NULL;
+
+	DBG("");
+	if (gerr) {
+		error("health: error creating MDL %s", gerr->message);
+		goto fail;
+	}
+
+	if (channel->type == CHANNEL_TYPE_ANY && type != CHANNEL_TYPE_ANY)
+		channel->type = type;
+
+	/*
+	 * if requested channel type is not same as preferred
+	 * channel type from remote device, then abort the connection.
+	 */
+	if (channel->type != type) {
+		/* TODO: abort mdl */
+		error("health: channel type requested %d preferred %d not same",
+							channel->type, type);
+		goto fail;
+	}
+
+	if (!channel->mdl)
+		channel->mdl = mcap_mdl_ref(mdl);
+
+	channel->type = type;
+	channel->mdl_id = mcap_mdl_get_mdlid(mdl);
+
+	if (channel->type == CHANNEL_TYPE_RELIABLE)
+		mode = L2CAP_MODE_ERTM;
+	else
+		mode = L2CAP_MODE_STREAMING;
+
+	if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm,
+						connect_mdl_cb, channel,
+						NULL, &err)) {
+		error("health: error connecting to mdl");
+		g_error_free(err);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static bool check_role(uint8_t rec_role, uint8_t app_role)
+{
+	if ((rec_role == HAL_HEALTH_MDEP_ROLE_SINK &&
+			app_role == HAL_HEALTH_MDEP_ROLE_SOURCE) ||
+			(rec_role == HAL_HEALTH_MDEP_ROLE_SOURCE &&
+			app_role == HAL_HEALTH_MDEP_ROLE_SINK))
+		return true;
+
+	return false;
+}
+
+static bool get_mdep_from_rec(const sdp_record_t *rec, uint8_t role,
+						uint16_t d_type, uint8_t *mdep)
+{
+	sdp_data_t *list, *feat;
+
+	if (!mdep)
+		return false;
+
+	list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+	if (!list || !SDP_IS_SEQ(list->dtd))
+		return false;
+
+	for (feat = list->val.dataseq; feat; feat = feat->next) {
+		sdp_data_t *data_type, *mdepid, *role_t;
+
+		if (!SDP_IS_SEQ(feat->dtd))
+			continue;
+
+		mdepid = feat->val.dataseq;
+		if (!mdepid)
+			continue;
+
+		data_type = mdepid->next;
+		if (!data_type)
+			continue;
+
+		role_t = data_type->next;
+		if (!role_t)
+			continue;
+
+		if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 ||
+						role_t->dtd != SDP_UINT8)
+			continue;
+
+		if (data_type->val.uint16 != d_type ||
+					!check_role(role_t->val.uint8, role))
+			continue;
+
+		*mdep = mdepid->val.uint8;
+
+		return true;
+	}
+
+	return false;
+}
+
+static bool get_remote_mdep(sdp_list_t *recs, struct health_channel *channel)
+{
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+	uint8_t mdep_id;
+
+	app = queue_find(apps, match_app_by_id,
+					INT_TO_PTR(channel->dev->app_id));
+	if (!app)
+		return false;
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id,
+						INT_TO_PTR(channel->mdep_id));
+	if (!mdep)
+		return false;
+
+	if (!get_mdep_from_rec(recs->data, mdep->role, mdep->data_type,
+								&mdep_id)) {
+		error("health: no matching MDEP: %u", channel->mdep_id);
+		return false;
+	}
+
+	channel->remote_mdep = mdep_id;
+	return true;
+}
+
+static bool create_mdl(struct health_channel *channel)
+{
+	struct health_app *app;
+	struct mdep_cfg *mdep;
+	uint8_t type;
+	GError *gerr = NULL;
+
+	app = queue_find(apps, match_app_by_id,
+					INT_TO_PTR(channel->dev->app_id));
+	if (!app)
+		return false;
+
+	mdep = queue_find(app->mdeps, match_mdep_by_id,
+						INT_TO_PTR(channel->mdep_id));
+	if (!mdep)
+		return false;
+
+	if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE)
+		type = channel->type;
+	else
+		type = CHANNEL_TYPE_ANY;
+
+	if (!mcap_create_mdl(channel->dev->mcl, channel->remote_mdep,
+				type, create_mdl_cb, channel, NULL, &gerr)) {
+		error("health: error creating mdl %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	return true;
+}
+
+static bool set_mcl_cb(struct mcap_mcl *mcl, gpointer user_data, GError **err)
+{
+	return mcap_mcl_set_cb(mcl, user_data, err,
+			MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb,
+			MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb,
+			MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb,
+			MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb,
+			MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb,
+			MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb,
+			MCAP_MDL_CB_INVALID);
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+	struct health_channel *channel = data;
+	gboolean ret;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err) {
+		error("health: error creating MCL : %s", err->message);
+		goto fail;
+	}
+
+	if (!channel->dev->mcl)
+		channel->dev->mcl = mcap_mcl_ref(mcl);
+
+	info("health: MCL connected");
+
+	ret = set_mcl_cb(channel->dev->mcl, channel, &gerr);
+	if (!ret) {
+		error("health: error setting mdl callbacks: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	if (!create_mdl(channel))
+		goto fail;
+
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct health_channel *channel = data;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err < 0 || !recs) {
+		error("health: Error getting remote SDP records");
+		goto fail;
+	}
+
+	if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) {
+		error("health: Can't get remote PSM for control channel");
+		goto fail;
+	}
+
+	if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) {
+		error("health: Can't get remote PSM for data channel");
+		goto fail;
+	}
+
+	if (!get_remote_mdep(recs, channel)) {
+		error("health: Can't get remote MDEP data");
+		goto fail;
+	}
+
+	if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm,
+					create_mcl_cb, channel, NULL, &gerr)) {
+		error("health: error creating mcl %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	destroy_channel(channel);
+}
+
+static int connect_mcl(struct health_channel *channel)
+{
+	uuid_t uuid;
+	int err;
+
+	DBG("");
+
+	bt_string2uuid(&uuid, HDP_UUID);
+
+	err = bt_search_service(&adapter_addr, &channel->dev->dst, &uuid,
+						search_cb, channel, NULL, 0);
+	if (!err)
+		send_channel_state_notify(channel,
+					HAL_HEALTH_CHANNEL_CONNECTING, -1);
+
+	return err;
+}
+
+static struct health_app *get_app(uint16_t app_id)
+{
+	return queue_find(apps, match_app_by_id, INT_TO_PTR(app_id));
+}
+
+static struct health_channel *get_channel(struct health_app *app,
+						uint8_t mdep_index,
+						struct health_device *dev)
+{
+	struct health_channel *channel;
+	uint8_t index;
+
+	if (!dev)
+		return NULL;
+
+	index = mdep_index + 1;
+	channel = queue_find(dev->channels, match_channel_by_mdep_id,
+							INT_TO_PTR(index));
+	if (channel)
+		return channel;
+
+	return create_channel(app, index, dev);
+}
+
+static void bt_health_connect_channel(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_connect_channel *cmd = buf;
+	struct hal_rsp_health_connect_channel rsp;
+	struct health_device *dev = NULL;
+	struct health_channel *channel = NULL;
+	struct health_app *app;
+
+	DBG("");
+
+	app = get_app(cmd->app_id);
+	if (!app)
+		goto send_rsp;
+
+	dev = get_device(app, cmd->bdaddr);
+
+	channel = get_channel(app, cmd->mdep_index, dev);
+	if (!channel)
+		goto send_rsp;
+
+	if (!queue_length(dev->channels)) {
+		if (channel->type != CHANNEL_TYPE_RELIABLE) {
+			error("health: first data shannel should be reliable");
+			goto fail;
+		}
+	}
+
+	if (!dev->mcl) {
+		if (connect_mcl(channel) < 0) {
+			error("health: error retrieving HDP SDP record");
+			goto fail;
+		}
+	} else {
+		/* data channel is already connected */
+		if (channel->mdl && channel->mdl_conn)
+			goto fail;
+
+		/* create mdl if it does not exists */
+		if (!channel->mdl && !create_mdl(channel))
+			goto fail;
+
+		/* reconnect mdl if it exists */
+		if (channel->mdl && !channel->mdl_conn) {
+			if (reconnect_mdl(channel) < 0)
+				goto fail;
+		}
+
+	}
+
+	rsp.channel_id = channel->id;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH,
+				HAL_OP_HEALTH_CONNECT_CHANNEL,
+				sizeof(rsp), &rsp, -1);
+	return;
+
+fail:
+	queue_remove(channel->dev->channels, channel);
+	free_health_channel(channel);
+
+send_rsp:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED);
+}
+
+static void channel_delete_cb(GError *gerr, gpointer data)
+{
+	struct health_channel *channel = data;
+
+	DBG("");
+
+	if (gerr) {
+		error("health: channel delete failed %s", gerr->message);
+		return;
+	}
+
+	destroy_channel(channel);
+}
+
+static void bt_health_destroy_channel(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_health_destroy_channel *cmd = buf;
+	struct health_channel *channel;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	channel = search_channel_by_id(cmd->channel_id);
+	if (!channel)
+		goto fail;
+
+	if (!mcap_delete_mdl(channel->mdl, channel_delete_cb, channel,
+							NULL, &gerr)) {
+		error("health: channel delete failed %s", gerr->message);
+		goto fail;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_SUCCESS);
+
+	return;
+
+fail:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+			HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_INVALID);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HEALTH_REG_APP */
+	{ bt_health_register_app, true,
+				sizeof(struct hal_cmd_health_reg_app) },
+	/* HAL_OP_HEALTH_MDEP */
+	{ bt_health_mdep_cfg_data, true,
+				sizeof(struct hal_cmd_health_mdep) },
+	/* HAL_OP_HEALTH_UNREG_APP */
+	{ bt_health_unregister_app, false,
+				sizeof(struct hal_cmd_health_unreg_app) },
+	/* HAL_OP_HEALTH_CONNECT_CHANNEL */
+	{ bt_health_connect_channel, false,
+				sizeof(struct hal_cmd_health_connect_channel) },
+	/* HAL_OP_HEALTH_DESTROY_CHANNEL */
+	{ bt_health_destroy_channel, false,
+				sizeof(struct hal_cmd_health_destroy_channel) },
+};
+
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+	GError *gerr = NULL;
+	bool ret;
+
+	DBG("");
+
+	info("health: MCL connected");
+	ret = set_mcl_cb(mcl, NULL, &gerr);
+	if (!ret) {
+		error("health: error setting mcl callbacks: %s", gerr->message);
+		g_error_free(gerr);
+	}
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct health_device *dev;
+
+	DBG("");
+
+	info("health: MCL reconnected");
+	dev = search_dev_by_mcl(mcl);
+	if (!dev) {
+		error("device data does not exists");
+		return;
+	}
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct health_device *dev;
+
+	DBG("");
+
+	info("health: MCL disconnected");
+	dev = search_dev_by_mcl(mcl);
+	unref_mcl(dev);
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+	/* mcap library maintains cache of mcls, not required here */
+}
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0,
+					mcl_connected, mcl_reconnected,
+					mcl_disconnected, mcl_uncached,
+					NULL, /* CSP is not used right now */
+					NULL, &err);
+	if (!mcap) {
+		error("health: MCAP instance creation failed %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	hal_ipc = ipc;
+	apps = queue_new();
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_health_unregister(void)
+{
+	DBG("");
+
+	mcap_instance_unref(mcap);
+	queue_destroy(apps, free_health_app);
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
+	hal_ipc = NULL;
+}
diff --git a/bluez/repo/android/health.h b/bluez/repo/android/health.h
new file mode 100644
index 0000000..0b32fd3
--- /dev/null
+++ b/bluez/repo/android/health.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_health_unregister(void);
diff --git a/bluez/repo/android/hidhost.c b/bluez/repo/android/hidhost.c
new file mode 100644
index 0000000..591ca95
--- /dev/null
+++ b/bluez/repo/android/hidhost.c
@@ -0,0 +1,1595 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
+#include "profiles/input/hog-lib.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "hidhost.h"
+#include "utils.h"
+
+#define L2CAP_PSM_HIDP_CTRL	0x11
+#define L2CAP_PSM_HIDP_INTR	0x13
+
+/* HID message types */
+#define HID_MSG_HANDSHAKE	0x00
+#define HID_MSG_CONTROL		0x10
+#define HID_MSG_GET_REPORT	0x40
+#define HID_MSG_SET_REPORT	0x50
+#define HID_MSG_GET_PROTOCOL	0x60
+#define HID_MSG_SET_PROTOCOL	0x70
+#define HID_MSG_DATA		0xa0
+
+#define HID_MSG_TYPE_MASK	0xf0
+
+/* HID data types */
+#define HID_DATA_TYPE_INPUT	0x01
+#define HID_DATA_TYPE_OUTPUT	0x02
+#define HID_DATA_TYPE_FEATURE	0x03
+
+/* HID protocol header parameters */
+#define HID_PROTO_BOOT		0x00
+#define HID_PROTO_REPORT	0x01
+
+/* HID GET REPORT Size Field */
+#define HID_GET_REPORT_SIZE_FIELD	0x08
+
+/* HID Virtual Cable Unplug */
+#define HID_VIRTUAL_CABLE_UNPLUG	0x05
+
+#define HOG_UUID		"00001812-0000-1000-8000-00805f9b34fb"
+
+static bdaddr_t adapter_addr;
+
+static GIOChannel *ctrl_io = NULL;
+static GIOChannel *intr_io = NULL;
+static GSList *devices = NULL;
+static unsigned int hog_app = 0;
+
+static struct ipc *hal_ipc = NULL;
+
+struct hid_device {
+	bdaddr_t	dst;
+	uint8_t		state;
+	uint8_t		subclass;
+	uint16_t	vendor;
+	uint16_t	product;
+	uint16_t	version;
+	uint8_t		country;
+	int		rd_size;
+	void		*rd_data;
+	uint8_t		boot_dev;
+	GIOChannel	*ctrl_io;
+	GIOChannel	*intr_io;
+	guint		ctrl_watch;
+	guint		intr_watch;
+	struct bt_uhid	*uhid;
+	uint8_t		last_hid_msg;
+	struct bt_hog	*hog;
+	int		sec_level;
+};
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct hid_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static void hid_device_free(void *data)
+{
+	struct hid_device *dev = data;
+
+	if (dev->ctrl_watch > 0)
+		g_source_remove(dev->ctrl_watch);
+
+	if (dev->intr_watch > 0)
+		g_source_remove(dev->intr_watch);
+
+	if (dev->intr_io)
+		g_io_channel_unref(dev->intr_io);
+
+	if (dev->ctrl_io)
+		g_io_channel_unref(dev->ctrl_io);
+
+	if (dev->uhid)
+		bt_uhid_unref(dev->uhid);
+
+	if (dev->hog)
+		bt_hog_unref(dev->hog);
+
+	g_free(dev->rd_data);
+	g_free(dev);
+}
+
+static void hid_device_remove(struct hid_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+	hid_device_free(dev);
+}
+
+static struct hid_device *hid_device_new(const bdaddr_t *addr)
+{
+	struct hid_device *dev;
+
+	dev = g_new0(struct hid_device, 1);
+	bacpy(&dev->dst, addr);
+	dev->state = HAL_HIDHOST_STATE_DISCONNECTED;
+	dev->sec_level = BT_IO_SEC_LOW;
+
+	devices = g_slist_append(devices, dev);
+
+	return dev;
+}
+
+static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size)
+{
+	int i, j;
+	char c;
+	uint8_t b;
+
+	for (i = 0, j = 0; i < buf_size; i++, j++) {
+		c = toupper(hex[j]);
+
+		if (c >= '0' && c <= '9')
+			b = c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = 10 + c - 'A';
+		else
+			return false;
+
+		j++;
+
+		c = toupper(hex[j]);
+
+		if (c >= '0' && c <= '9')
+			b = b * 16 + c - '0';
+		else if (c >= 'A' && c <= 'F')
+			b = b * 16 + 10 + c - 'A';
+		else
+			return false;
+
+		buf[i] = b;
+	}
+
+	return true;
+}
+
+static void handle_uhid_output(struct uhid_event *event, void *user_data)
+{
+	struct uhid_output_req *output = &event->u.output;
+	struct hid_device *dev = user_data;
+	int fd, req_size;
+	uint8_t *req;
+
+	if (!dev->ctrl_io)
+		return;
+
+	req_size = 1 + output->size;
+	req = malloc0(req_size);
+	if (!req)
+		return;
+
+	req[0] = HID_MSG_SET_REPORT | output->rtype;
+	memcpy(req + 1, output->data, req_size - 1);
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0)
+		error("hidhost: error writing set_report: %s (%d)",
+							strerror(errno), errno);
+
+	free(req);
+}
+
+static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data)
+{
+	struct hid_device *dev = data;
+	uint8_t buf[UHID_DATA_MAX];
+	struct uhid_event ev;
+	int fd, bread, err;
+
+	/* Wait uHID if not ready */
+	if (!dev->uhid)
+		return TRUE;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	bread = read(fd, buf, sizeof(buf));
+	if (bread < 0) {
+		error("hidhost: read from interrupt failed: %s(%d)",
+						strerror(errno), -errno);
+		return TRUE;
+	}
+
+	/* Discard non-data packets */
+	if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT))
+		return TRUE;
+
+	/* send data to uHID device skipping HIDP header byte */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+	ev.u.input.size = bread - 1;
+	memcpy(ev.u.input.data, &buf[1], ev.u.input.size);
+
+	err = bt_uhid_send(dev->uhid, &ev);
+	if (err < 0)
+		DBG("bt_uhid_send: %s (%d)", strerror(-err), -err);
+
+	return TRUE;
+}
+
+static void bt_hid_notify_state(struct hid_device *dev, uint8_t state)
+{
+	struct hal_ev_hidhost_conn_state ev;
+	char address[18];
+
+	if (dev->state == state)
+		return;
+
+	dev->state = state;
+
+	ba2str(&dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev);
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	struct hid_device *dev = data;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto error;
+
+	if (cond & G_IO_IN)
+		return intr_io_watch_cb(chan, data);
+
+error:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+
+	/*
+	 * Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+	 * it's likely that ctrl_watch_cb has been queued for dispatching in
+	 * this mainloop iteration
+	 */
+	if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	/* Close control channel */
+	if (dev->ctrl_io && !(cond & G_IO_NVAL))
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+	hid_device_remove(dev);
+
+	return FALSE;
+}
+
+static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf,
+									int len)
+{
+	struct hal_ev_hidhost_proto_mode ev;
+	char address[18];
+
+	ba2str(&dev->dst, address);
+	DBG("device %s", address);
+
+	memset(&ev, 0, sizeof(ev));
+	bdaddr2android(&dev->dst, ev.bdaddr);
+
+	if (buf[0] == HID_MSG_DATA) {
+		ev.status = HAL_HIDHOST_STATUS_OK;
+		if (buf[1] == HID_PROTO_REPORT)
+			ev.mode = HAL_HIDHOST_REPORT_PROTOCOL;
+		else if (buf[1] == HID_PROTO_BOOT)
+			ev.mode = HAL_HIDHOST_BOOT_PROTOCOL;
+		else
+			ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
+
+	} else {
+		ev.status = buf[0];
+		ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev);
+}
+
+static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
+									int len)
+{
+	struct hal_ev_hidhost_get_report *ev;
+	int ev_len;
+	char address[18];
+
+	ba2str(&dev->dst, address);
+	DBG("device %s", address);
+
+	ev_len = sizeof(*ev);
+
+	if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) ||
+			(buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) ||
+			(buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) {
+		ev = g_malloc0(ev_len);
+		ev->status = buf[0];
+		bdaddr2android(&dev->dst, ev->bdaddr);
+		goto send;
+	}
+
+	/*
+	 * Report porotocol mode reply contains id after hdr, in boot
+	 * protocol mode id doesn't exist
+	 */
+	ev_len += (dev->boot_dev) ? (len - 1) : (len - 2);
+	ev = g_malloc0(ev_len);
+	ev->status = HAL_HIDHOST_STATUS_OK;
+	bdaddr2android(&dev->dst, ev->bdaddr);
+
+	/*
+	 * Report porotocol mode reply contains id after hdr, in boot
+	 * protocol mode id doesn't exist
+	 */
+	if (dev->boot_dev) {
+		ev->len = len - 1;
+		memcpy(ev->data, buf + 1, ev->len);
+	} else {
+		ev->len = len - 2;
+		memcpy(ev->data, buf + 2, ev->len);
+	}
+
+send:
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_GET_REPORT, ev_len, ev);
+	g_free(ev);
+}
+
+static void bt_hid_notify_handshake(struct hid_device *dev, uint8_t *buf,
+									int len)
+{
+	struct hal_ev_hidhost_handshake ev;
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+
+	/* crop result code to handshake status range from HAL */
+	ev.status = buf[0];
+	if (ev.status > HAL_HIDHOST_HS_ERROR)
+		ev.status = HAL_HIDHOST_HS_ERROR;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_HANDSHAKE, sizeof(ev), &ev);
+}
+
+static void bt_hid_notify_virtual_unplug(struct hid_device *dev,
+							uint8_t *buf, int len)
+{
+	struct hal_ev_hidhost_virtual_unplug ev;
+	char address[18];
+
+	ba2str(&dev->dst, address);
+	DBG("device %s", address);
+	bdaddr2android(&dev->dst, ev.bdaddr);
+
+	ev.status = HAL_HIDHOST_GENERAL_ERROR;
+
+	/* Wait either channels to HUP */
+	if (dev->intr_io && dev->ctrl_io) {
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+		ev.status = HAL_HIDHOST_STATUS_OK;
+	}
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+				HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev);
+}
+
+static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data)
+{
+	struct hid_device *dev = data;
+	int fd, bread;
+	uint8_t buf[UHID_DATA_MAX];
+
+	DBG("");
+
+	fd = g_io_channel_unix_get_fd(chan);
+	bread = read(fd, buf, sizeof(buf));
+	if (bread < 0) {
+		error("hidhost: read from control failed: %s(%d)",
+						strerror(errno), -errno);
+		return TRUE;
+	}
+
+	switch (dev->last_hid_msg) {
+	case HID_MSG_GET_PROTOCOL:
+	case HID_MSG_SET_PROTOCOL:
+		bt_hid_notify_proto_mode(dev, buf, bread);
+		break;
+	case HID_MSG_GET_REPORT:
+		bt_hid_notify_get_report(dev, buf, bread);
+		break;
+	}
+
+	switch (buf[0] & HID_MSG_TYPE_MASK) {
+	case HID_MSG_HANDSHAKE:
+		bt_hid_notify_handshake(dev, buf, bread);
+		break;
+	case HID_MSG_CONTROL:
+		if ((buf[0] & ~HID_MSG_TYPE_MASK) == HID_VIRTUAL_CABLE_UNPLUG)
+			bt_hid_notify_virtual_unplug(dev, buf, bread);
+		break;
+	default:
+		break;
+	}
+
+	/* reset msg type request */
+	dev->last_hid_msg = 0;
+
+	return TRUE;
+}
+
+static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	struct hid_device *dev = data;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto error;
+
+	if (cond & G_IO_IN)
+		return ctrl_io_watch_cb(chan, data);
+
+error:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+
+	/*
+	 * Checking for intr_watch avoids a double g_io_channel_shutdown since
+	 * it's likely that intr_watch_cb has been queued for dispatching in
+	 * this mainloop iteration
+	 */
+	if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	if (dev->intr_io && !(cond & G_IO_NVAL))
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+	hid_device_remove(dev);
+
+	return FALSE;
+}
+
+static void bt_hid_set_info(struct hid_device *dev)
+{
+	struct hal_ev_hidhost_info ev;
+
+	DBG("");
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.attr = 0; /* TODO: Check what is this field */
+	ev.subclass = dev->subclass;
+	ev.app_id = 0; /* TODO: Check what is this field */
+	ev.vendor = dev->vendor;
+	ev.product = dev->product;
+	ev.version = dev->version;
+	ev.country = dev->country;
+	ev.descr_len = dev->rd_size;
+	memset(ev.descr, 0, sizeof(ev.descr));
+	memcpy(ev.descr, dev->rd_data, ev.descr_len);
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO,
+							sizeof(ev), &ev);
+}
+
+static int uhid_create(struct hid_device *dev)
+{
+	struct uhid_event ev;
+	int err;
+
+	dev->uhid = bt_uhid_new_default();
+	if (!dev->uhid) {
+		err = -errno;
+		error("hidhost: Failed to create bt_uhid instance");
+		return err;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	strcpy((char *) ev.u.create.name, "bluez-input-device");
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.vendor = dev->vendor;
+	ev.u.create.product = dev->product;
+	ev.u.create.version = dev->version;
+	ev.u.create.country = dev->country;
+	ev.u.create.rd_size = dev->rd_size;
+	ev.u.create.rd_data = dev->rd_data;
+
+	err = bt_uhid_send(dev->uhid, &ev);
+	if (err < 0) {
+		error("hidhost: Failed to create uHID device: %s",
+							strerror(-err));
+		bt_uhid_unref(dev->uhid);
+		dev->uhid = NULL;
+		return err;
+	}
+
+	bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev);
+	bt_hid_set_info(dev);
+
+	return 0;
+}
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	struct hid_device *dev = user_data;
+	uint8_t state;
+
+	DBG("");
+
+	if (conn_err) {
+		error("hidhost: Failed to connect interrupt channel (%s)",
+							conn_err->message);
+		state = HAL_HIDHOST_STATE_FAILED;
+		goto failed;
+	}
+
+	if (uhid_create(dev) < 0) {
+		state = HAL_HIDHOST_STATE_NO_HID;
+		goto failed;
+	}
+
+	dev->intr_watch = g_io_add_watch(dev->intr_io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				intr_watch_cb, dev);
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+	return;
+
+failed:
+	bt_hid_notify_state(dev, state);
+	hid_device_remove(dev);
+}
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	struct hid_device *dev = user_data;
+	GError *err = NULL;
+
+	DBG("");
+
+	if (conn_err) {
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+		error("hidhost: Failed to connect control channel (%s)",
+							conn_err->message);
+		goto failed;
+	}
+
+	/* Connect to the HID interrupt channel */
+	dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+					BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+					BT_IO_OPT_INVALID);
+	if (!dev->intr_io) {
+		error("hidhost: Failed to connect interrupt channel (%s)",
+								err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	dev->ctrl_watch = g_io_add_watch(dev->ctrl_io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				ctrl_watch_cb, dev);
+
+	return;
+
+failed:
+	hid_device_remove(dev);
+}
+
+static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hid_device *dev = data;
+	sdp_list_t *list;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (err < 0) {
+		error("hidhost: Unable to get SDP record: %s", strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("hidhost: No SDP records found");
+		goto fail;
+	}
+
+	for (list = recs; list != NULL; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_data_t *data;
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
+		if (data)
+			dev->country = data->val.uint8;
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS);
+		if (data) {
+			dev->subclass = data->val.uint8;
+
+			/* Encryption is mandatory for keyboards */
+			if (dev->subclass & 0x40)
+				dev->sec_level = BT_IO_SEC_MEDIUM;
+		}
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE);
+		if (data)
+			dev->boot_dev = data->val.uint8;
+
+		data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST);
+		if (data) {
+			if (!SDP_IS_SEQ(data->dtd))
+				goto fail;
+
+			/* First HIDDescriptor */
+			data = data->val.dataseq;
+			if (!SDP_IS_SEQ(data->dtd))
+				goto fail;
+
+			/* ClassDescriptorType */
+			data = data->val.dataseq;
+			if (data->dtd != SDP_UINT8)
+				goto fail;
+
+			/* ClassDescriptorData */
+			data = data->next;
+			if (!data || !SDP_IS_TEXT_STR(data->dtd))
+				goto fail;
+
+			dev->rd_size = data->unitSize;
+			dev->rd_data = g_memdup(data->val.str, data->unitSize);
+		}
+	}
+
+	if (dev->ctrl_io) {
+		/* Raise the security level for this device if needed. */
+		if ((dev->sec_level > BT_IO_SEC_LOW) &&
+			!bt_io_set(dev->ctrl_io, &gerr,
+					BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+					BT_IO_OPT_INVALID)) {
+			error("hidhost: Cannot raise security level: %s",
+								gerr->message);
+			g_error_free(gerr);
+
+			goto fail;
+		}
+
+		if (uhid_create(dev) < 0)
+			goto fail;
+		return;
+	}
+
+	dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+					BT_IO_OPT_SEC_LEVEL, dev->sec_level,
+					BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("hidhost: Failed to connect control channel (%s)",
+								gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct hid_device *dev = data;
+	sdp_list_t *list;
+	uuid_t uuid;
+
+	DBG("");
+
+	if (err < 0) {
+		error("hidhost: Unable to get Device ID SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("hidhost: No Device ID SDP records found");
+		goto fail;
+	}
+
+	for (list = recs; list; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_data_t *data;
+
+		data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+		if (data)
+			dev->vendor = data->val.uint16;
+
+		data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+		if (data)
+			dev->product = data->val.uint16;
+
+		data = sdp_data_get(rec, SDP_ATTR_VERSION);
+		if (data)
+			dev->version = data->val.uint16;
+	}
+
+	sdp_uuid16_create(&uuid, HID_SVCLASS_ID);
+	if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+				hid_sdp_search_cb, dev, NULL, 0) < 0) {
+		error("hidhost: Failed to search SDP details");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib)
+{
+	GSList *l;
+	struct hid_device *dev;
+
+	l = g_slist_find_custom(devices, addr, device_cmp);
+	dev = l ? l->data : NULL;
+
+	if (err < 0) {
+		if (!dev)
+			return;
+		if (dev->hog) {
+			bt_hid_notify_state(dev,
+						HAL_HIDHOST_STATE_DISCONNECTED);
+			bt_hog_detach(dev->hog);
+			return;
+		}
+		goto fail;
+	}
+
+	if (!dev)
+		dev = hid_device_new(addr);
+
+	if (!dev->hog) {
+		/* TODO: Get device details and primary */
+		dev->hog = bt_hog_new_default("bluez-input-device", dev->vendor,
+					dev->product, dev->version, NULL);
+		if (!dev->hog) {
+			error("HoG: unable to create session");
+			goto fail;
+		}
+	}
+
+	if (!bt_hog_attach(dev->hog, attrib)) {
+		error("HoG: unable to attach");
+		goto fail;
+	}
+
+	if (!bt_gatt_set_security(addr, BT_IO_SEC_MEDIUM)) {
+		error("Failed to set security level");
+		goto fail;
+	}
+
+	DBG("");
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+
+	if (!bt_gatt_add_autoconnect(hog_app, &dev->dst))
+		error("hidhost: Could not add to autoconnect list");
+
+	return;
+
+fail:
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+	hid_device_remove(dev);
+}
+
+static bool hog_connect(struct hid_device *dev)
+{
+	DBG("");
+
+	if (hog_app)
+		return bt_gatt_connect_app(hog_app, &dev->dst);
+
+	hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb);
+	if (!hog_app) {
+		error("hidhost: bt_gatt_register_app failed");
+		return false;
+	}
+
+	return bt_gatt_connect_app(hog_app, &dev->dst);
+}
+
+static void bt_hid_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_connect *cmd = buf;
+	struct hid_device *dev;
+	uint8_t status;
+	char addr[18];
+	bdaddr_t dst;
+	GSList *l;
+	uuid_t uuid;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l)
+		dev = l->data;
+	else
+		dev = hid_device_new(&dst);
+
+	if (dev->state != HAL_HIDHOST_STATE_DISCONNECTED)
+		goto done;
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s", addr);
+
+	if (bt_device_last_seen_bearer(&dev->dst) != BDADDR_BREDR) {
+		if (!hog_connect(dev)) {
+			status = HAL_STATUS_FAILED;
+			hid_device_remove(dev);
+			goto failed;
+		}
+		goto done;
+	}
+
+	sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+	if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+				hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+		error("hidhost: Failed to search DeviceID SDP details");
+		hid_device_remove(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+done:
+	if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+									status);
+}
+
+static bool hog_disconnect(struct hid_device *dev)
+{
+	DBG("");
+
+	if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED)
+		return false;
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+	if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) {
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+		hid_device_remove(dev);
+	}
+
+	return true;
+}
+
+static void bt_hid_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_disconnect *cmd = buf;
+	struct hid_device *dev;
+	uint8_t status;
+	GSList *l;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+	if (bt_is_device_le(&dst)) {
+		if (!hog_disconnect(dev)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+		goto done;
+	}
+
+	/* Wait either channels to HUP */
+	if (dev->intr_io)
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+	if (dev->ctrl_io)
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+
+done:
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+									status);
+}
+
+static bool bt_hid_write_virtual_unplug(GIOChannel *chan)
+{
+	uint8_t hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG;
+	int fd = g_io_channel_unix_get_fd(chan);
+
+	if (write(fd, &hdr, sizeof(hdr)) == sizeof(hdr))
+		return true;
+
+	error("hidhost: Error writing virtual unplug command: %s (%d)",
+							strerror(errno), errno);
+	return false;
+}
+
+static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_virtual_unplug *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	uint8_t status;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (!(dev->ctrl_io)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	if (!bt_hid_write_virtual_unplug(dev->ctrl_io)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	/* Wait either channels to HUP */
+	if (dev->intr_io)
+		g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
+
+	if (dev->ctrl_io)
+		g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
+
+	bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status);
+}
+
+static void bt_hid_info(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_set_info *cmd = buf;
+
+	if (len != sizeof(*cmd) + cmd->descr_len) {
+		error("Invalid hid set info size (%u bytes), terminating", len);
+		raise(SIGTERM);
+		return;
+	}
+
+	/*
+	 * Data from hal_cmd_hidhost_set_info is usefull only when we create
+	 * UHID device. Once device is created all the transactions will be
+	 * done through the fd. There is no way to use this information
+	 * once device is created with HID internals.
+	 */
+	DBG("Not supported");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+							HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_hid_get_protocol(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_get_protocol *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t hdr;
+	uint8_t status;
+
+	DBG("");
+
+	switch (cmd->mode) {
+	case HAL_HIDHOST_REPORT_PROTOCOL:
+	case HAL_HIDHOST_BOOT_PROTOCOL:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	hdr = HID_MSG_GET_PROTOCOL | cmd->mode;
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, &hdr, sizeof(hdr)) < 0) {
+		error("hidhost: Error writing device_get_protocol: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_GET_PROTOCOL;
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_GET_PROTOCOL, status);
+}
+
+static void bt_hid_set_protocol(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_set_protocol *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t hdr;
+	uint8_t status;
+
+	DBG("");
+
+	switch (cmd->mode) {
+	case HAL_HIDHOST_REPORT_PROTOCOL:
+	case HAL_HIDHOST_BOOT_PROTOCOL:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	hdr = HID_MSG_SET_PROTOCOL | cmd->mode;
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, &hdr, sizeof(hdr)) < 0) {
+		error("hidhost: error writing device_set_protocol: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_SET_PROTOCOL;
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+					HAL_OP_HIDHOST_SET_PROTOCOL, status);
+}
+
+static void bt_hid_get_report(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_get_report *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t *req;
+	uint8_t req_size;
+	uint8_t status;
+
+	DBG("");
+
+	switch (cmd->type) {
+	case HAL_HIDHOST_INPUT_REPORT:
+	case HAL_HIDHOST_OUTPUT_REPORT:
+	case HAL_HIDHOST_FEATURE_REPORT:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+	req_size = (cmd->buf_size > 0) ? 4 : 2;
+	req = g_try_malloc0(req_size);
+	if (!req) {
+		status = HAL_STATUS_NOMEM;
+		goto failed;
+	}
+
+	req[0] = HID_MSG_GET_REPORT | cmd->type;
+	req[1] = cmd->id;
+
+	if (cmd->buf_size > 0) {
+		req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD;
+		put_le16(cmd->buf_size, &req[2]);
+	}
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("hidhost: error writing hid_get_report: %s (%d)",
+						strerror(errno), errno);
+		g_free(req);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_GET_REPORT;
+	g_free(req);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+									status);
+}
+
+static void bt_hid_set_report(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_set_report *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t *req = NULL;
+	uint8_t req_size;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid hid set report size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	switch (cmd->type) {
+	case HAL_HIDHOST_INPUT_REPORT:
+	case HAL_HIDHOST_OUTPUT_REPORT:
+	case HAL_HIDHOST_FEATURE_REPORT:
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (!dev->ctrl_io && !dev->hog) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	req_size = 1 + (cmd->len / 2);
+	req = g_try_malloc0(req_size);
+	if (!req) {
+		status = HAL_STATUS_NOMEM;
+		goto failed;
+	}
+
+	req[0] = HID_MSG_SET_REPORT | cmd->type;
+	/*
+	 * Report data coming to HAL is in ascii format, HAL sends
+	 * data in hex to daemon, so convert to binary.
+	 */
+	if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	if (dev->hog) {
+		if (bt_hog_send_report(dev->hog, req + 1, req_size - 1,
+							cmd->type) < 0) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		goto done;
+	}
+
+	fd = g_io_channel_unix_get_fd(dev->ctrl_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("hidhost: error writing hid_set_report: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev->last_hid_msg = HID_MSG_SET_REPORT;
+
+done:
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	g_free(req);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+									status);
+}
+
+static void bt_hid_send_data(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_hidhost_send_data *cmd = buf;
+	struct hid_device *dev;
+	GSList *l;
+	bdaddr_t dst;
+	int fd;
+	uint8_t *req = NULL;
+	uint8_t req_size;
+	uint8_t status;
+
+	DBG("");
+
+	if (len != sizeof(*cmd) + cmd->len) {
+		error("Invalid hid send data size (%u bytes), terminating",
+									len);
+		raise(SIGTERM);
+		return;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (!(dev->intr_io)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	req_size = 1 + (cmd->len / 2);
+	req = g_try_malloc0(req_size);
+	if (!req) {
+		status = HAL_STATUS_NOMEM;
+		goto failed;
+	}
+
+	req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT;
+	/*
+	 * Report data coming to HAL is in ascii format, HAL sends
+	 * data in hex to daemon, so convert to binary.
+	 */
+	if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+		status = HAL_STATUS_INVALID;
+		goto failed;
+	}
+
+	fd = g_io_channel_unix_get_fd(dev->intr_io);
+
+	if (write(fd, req, req_size) < 0) {
+		error("hidhost: error writing data to HID device: %s (%d)",
+						strerror(errno), errno);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	g_free(req);
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+									status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_HIDHOST_CONNECT */
+	{ bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) },
+	/* HAL_OP_HIDHOST_DISCONNECT */
+	{ bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) },
+	/* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */
+	{ bt_hid_virtual_unplug, false,
+				sizeof(struct hal_cmd_hidhost_virtual_unplug) },
+	/* HAL_OP_HIDHOST_SET_INFO */
+	{ bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) },
+	/* HAL_OP_HIDHOST_GET_PROTOCOL */
+	{ bt_hid_get_protocol, false,
+				sizeof(struct hal_cmd_hidhost_get_protocol) },
+	/* HAL_OP_HIDHOST_SET_PROTOCOL */
+	{ bt_hid_set_protocol, false,
+				sizeof(struct hal_cmd_hidhost_get_protocol) },
+	/* HAL_OP_HIDHOST_GET_REPORT */
+	{ bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) },
+	/* HAL_OP_HIDHOST_SET_REPORT */
+	{ bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) },
+	/* HAL_OP_HIDHOST_SEND_DATA */
+	{ bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data)  },
+};
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct hid_device *dev;
+	bdaddr_t dst;
+	char address[18];
+	uint16_t psm;
+	GError *gerr = NULL;
+	GSList *l;
+	uuid_t uuid;
+
+	if (err) {
+		error("hidhost: Connect failed (%s)", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_PSM, &psm,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("hidhost: Failed to read remote address (%s)",
+								gerr->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_error_free(gerr);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s on PSM %d", address, psm);
+
+	if (!bt_device_is_bonded(&dst)) {
+		warn("hidhost: Rejecting connection from unknown device %s",
+								address);
+		if (psm == L2CAP_PSM_HIDP_CTRL)
+			bt_hid_write_virtual_unplug(chan);
+
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	switch (psm) {
+	case L2CAP_PSM_HIDP_CTRL:
+		l = g_slist_find_custom(devices, &dst, device_cmp);
+		if (l)
+			return;
+
+		dev = hid_device_new(&dst);
+		dev->ctrl_io = g_io_channel_ref(chan);
+
+		sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+		if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+				hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+			error("hidhost: Failed to search DID SDP details");
+			hid_device_remove(dev);
+			return;
+		}
+
+		dev->ctrl_watch = g_io_add_watch(dev->ctrl_io,
+					G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+					ctrl_watch_cb, dev);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
+		break;
+
+	case L2CAP_PSM_HIDP_INTR:
+		l = g_slist_find_custom(devices, &dst, device_cmp);
+		if (!l)
+			return;
+
+		dev = l->data;
+		dev->intr_io = g_io_channel_ref(chan);
+		dev->intr_watch = g_io_add_watch(dev->intr_io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				intr_watch_cb, dev);
+		bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED);
+		break;
+	}
+}
+
+static void hid_unpaired_cb(const bdaddr_t *addr)
+{
+	GSList *l;
+	struct hid_device *dev;
+	char address[18];
+
+	l = g_slist_find_custom(devices, addr, device_cmp);
+	if (!l)
+		return;
+
+	dev = l->data;
+
+	ba2str(addr, address);
+	DBG("Unpaired device %s", address);
+
+	if (hog_app)
+		bt_gatt_remove_autoconnect(hog_app, addr);
+
+	hid_device_remove(dev);
+}
+
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	GError *err = NULL;
+
+	DBG("");
+
+	if (!bt_unpaired_register(hid_unpaired_cb)) {
+		error("hidhost: Could not register unpaired callback");
+		return false;
+	}
+
+	bacpy(&adapter_addr, addr);
+
+	ctrl_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!ctrl_io) {
+		error("hidhost: Failed to listen on control channel: %s",
+								err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	intr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!intr_io) {
+		error("hidhost: Failed to listen on interrupt channel: %s",
+								err->message);
+		g_error_free(err);
+
+		g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+		g_io_channel_unref(ctrl_io);
+		ctrl_io = NULL;
+
+		return false;
+	}
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_hid_unregister(void)
+{
+	DBG("");
+
+	if (hog_app > 0)
+		bt_gatt_unregister_app(hog_app);
+
+	g_slist_free_full(devices, hid_device_free);
+	devices = NULL;
+
+	if (ctrl_io) {
+		g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+		g_io_channel_unref(ctrl_io);
+		ctrl_io = NULL;
+	}
+
+	if (intr_io) {
+		g_io_channel_shutdown(intr_io, TRUE, NULL);
+		g_io_channel_unref(intr_io);
+		intr_io = NULL;
+	}
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST);
+	hal_ipc = NULL;
+
+	bt_unpaired_unregister(hid_unpaired_cb);
+}
diff --git a/bluez/repo/android/hidhost.h b/bluez/repo/android/hidhost.h
new file mode 100644
index 0000000..e6b87ed
--- /dev/null
+++ b/bluez/repo/android/hidhost.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_hid_unregister(void);
diff --git a/bluez/repo/android/init.bluetooth.rc b/bluez/repo/android/init.bluetooth.rc
new file mode 100644
index 0000000..2d43f73
--- /dev/null
+++ b/bluez/repo/android/init.bluetooth.rc
@@ -0,0 +1,38 @@
+# required permissions
+on boot
+    chown bluetooth bluetooth /data/misc/bluetooth
+    chown bluetooth bluetooth /dev/uhid
+    chown system    bluetooth /dev/uinput
+
+# services
+on property:bluetooth.start=daemon
+    setprop bluetooth.start none
+    start bluetoothd
+
+on property:bluetooth.stop=daemon
+    setprop bluetooth.stop none
+    stop bluetoothd
+
+on property:bluetooth.start=snoop
+    setprop bluetooth.start none
+    start bluetoothd-snoop
+
+on property:bluetooth.stop=snoop
+    setprop bluetooth.stop none
+    stop bluetoothd-snoop
+
+service bluetoothd /system/bin/bluetoothd
+    class main
+    # init does not yet support setting capabilities so run as root,
+    # bluetoothd drop uid to bluetooth with the right linux capabilities
+    group bluetooth
+    disabled
+    oneshot
+
+service bluetoothd-snoop /system/bin/bluetoothd-snoop
+    class main
+    # init does not yet support setting capabilities so run as root,
+    # bluetoothd-snoop drops unneeded linux capabilities
+    group nobody
+    disabled
+    oneshot
diff --git a/bluez/repo/android/ipc-common.h b/bluez/repo/android/ipc-common.h
new file mode 100644
index 0000000..27736e4
--- /dev/null
+++ b/bluez/repo/android/ipc-common.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 IPC_MTU 1024
+
+#define IPC_STATUS_SUCCESS	0x00
+
+struct ipc_hdr {
+	uint8_t  service_id;
+	uint8_t  opcode;
+	uint16_t len;
+	uint8_t  payload[0];
+} __attribute__((packed));
+
+#define IPC_OP_STATUS		0x00
+struct ipc_status {
+	uint8_t code;
+} __attribute__((packed));
diff --git a/bluez/repo/android/ipc-tester.c b/bluez/repo/android/ipc-tester.c
new file mode 100644
index 0000000..1aa17d2
--- /dev/null
+++ b/bluez/repo/android/ipc-tester.c
@@ -0,0 +1,1512 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <libgen.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "emulator/hciemu.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+
+#include <cutils/properties.h>
+
+#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+struct test_data {
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	pid_t bluetoothd_pid;
+	bool setup_done;
+};
+
+struct ipc_data {
+	void *buffer;
+	size_t len;
+};
+
+struct generic_data {
+	struct ipc_data ipc_data;
+
+	unsigned int num_services;
+	int init_services[];
+};
+
+struct regmod_msg {
+	struct ipc_hdr header;
+	struct hal_cmd_register_module cmd;
+} __attribute__((packed));
+
+#define CONNECT_TIMEOUT (5 * 1000)
+#define SERVICE_NAME "bluetoothd"
+
+static char exec_dir[PATH_MAX];
+
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *data)
+{
+	struct test_data *test_data = tester_get_data();
+
+	if (!tester_use_debug())
+		fclose(stderr);
+
+	test_data->mgmt = mgmt_new_default();
+	if (!test_data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+				NULL, read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *data)
+{
+	struct test_data *test_data = tester_get_data();
+
+	if (test_data->hciemu) {
+		hciemu_unref(test_data->hciemu);
+		test_data->hciemu = NULL;
+	}
+}
+
+static void bluetoothd_start(int hci_index)
+{
+	char prg_name[PATH_MAX];
+	char index[8];
+	char *prg_argv[4];
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+	snprintf(index, sizeof(index), "%d", hci_index);
+
+	prg_argv[0] = prg_name;
+	prg_argv[1] = "-i";
+	prg_argv[2] = index;
+	prg_argv[3] = NULL;
+
+	if (!tester_use_debug())
+		fclose(stderr);
+
+	execve(prg_argv[0], prg_argv, NULL);
+}
+
+static void emulator(int pipe, int hci_index)
+{
+	static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+	char buf[1024];
+	struct sockaddr_un addr;
+	struct timeval tv;
+	int fd;
+	ssize_t len;
+
+	fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		goto failed;
+
+	tv.tv_sec = WAIT_FOR_SIGNAL_TIME;
+	tv.tv_usec = 0;
+	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind system socket");
+		goto failed;
+	}
+
+	len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL));
+
+	if (len != sizeof(EMULATOR_SIGNAL))
+		goto failed;
+
+	memset(buf, 0, sizeof(buf));
+
+	len = read(fd, buf, sizeof(buf));
+	if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd"))
+		goto failed;
+
+	close(pipe);
+	close(fd);
+	return bluetoothd_start(hci_index);
+
+failed:
+	close(pipe);
+	if (fd >= 0)
+		close(fd);
+}
+
+static int accept_connection(int sk)
+{
+	int err;
+	struct pollfd pfd;
+	int new_sk;
+
+	memset(&pfd, 0 , sizeof(pfd));
+	pfd.fd = sk;
+	pfd.events = POLLIN;
+
+	err = poll(&pfd, 1, CONNECT_TIMEOUT);
+	if (err < 0) {
+		err = errno;
+		tester_warn("Failed to poll: %d (%s)", err, strerror(err));
+		return -errno;
+	}
+
+	if (err == 0) {
+		tester_warn("bluetoothd connect timeout");
+		return -errno;
+	}
+
+	new_sk = accept(sk, NULL, NULL);
+	if (new_sk < 0) {
+		err = errno;
+		tester_warn("Failed to accept socket: %d (%s)",
+							err, strerror(err));
+		return -errno;
+	}
+
+	return new_sk;
+}
+
+static bool init_ipc(void)
+{
+	struct sockaddr_un addr;
+
+	int sk;
+	int err;
+
+	sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = errno;
+		tester_warn("Failed to create socket: %d (%s)", err,
+							strerror(err));
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = errno;
+		tester_warn("Failed to bind socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	if (listen(sk, 2) < 0) {
+		err = errno;
+		tester_warn("Failed to listen on socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	/* Start Android Bluetooth daemon service */
+	if (property_set("ctl.start", SERVICE_NAME) < 0) {
+		tester_warn("Failed to start service %s", SERVICE_NAME);
+		close(sk);
+		return false;
+	}
+
+	cmd_sk = accept_connection(sk);
+	if (cmd_sk < 0) {
+		close(sk);
+		return false;
+	}
+
+	notif_sk = accept_connection(sk);
+	if (notif_sk < 0) {
+		close(sk);
+		close(cmd_sk);
+		cmd_sk = -1;
+		return false;
+	}
+
+	tester_print("bluetoothd connected");
+
+	close(sk);
+
+	return true;
+}
+
+static void cleanup_ipc(void)
+{
+	if (cmd_sk < 0)
+		return;
+
+	close(cmd_sk);
+	cmd_sk = -1;
+}
+
+static gboolean check_for_daemon(gpointer user_data)
+{
+	int status;
+	struct test_data *data = user_data;
+
+	if ((waitpid(data->bluetoothd_pid, &status, WNOHANG))
+							!= data->bluetoothd_pid)
+		return true;
+
+	if (data->setup_done) {
+		if (WIFEXITED(status) &&
+				(WEXITSTATUS(status) == EXIT_SUCCESS)) {
+			tester_test_passed();
+			return false;
+		}
+		tester_test_failed();
+	} else {
+		tester_setup_failed();
+		test_post_teardown(data);
+	}
+
+	tester_warn("Unexpected Daemon shutdown with status %d", status);
+	return false;
+}
+
+static bool setup_module(int service_id)
+{
+	struct ipc_hdr response;
+	struct ipc_hdr expected_response;
+
+	struct regmod_msg btmodule_msg = {
+		.header = {
+			.service_id = HAL_SERVICE_ID_CORE,
+			.opcode = HAL_OP_REGISTER_MODULE,
+			.len = sizeof(struct hal_cmd_register_module),
+			},
+		.cmd = {
+			.service_id = service_id,
+			.mode = HAL_MODE_DEFAULT,
+			.max_clients = 1,
+			},
+	};
+
+	if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0)
+		goto fail;
+
+	if (read(cmd_sk, &response, sizeof(response)) < 0)
+		goto fail;
+
+	expected_response = btmodule_msg.header;
+	expected_response.len = 0;
+
+	if (memcmp(&response, &expected_response, sizeof(response)) == 0)
+		return true;
+
+fail:
+	tester_warn("Module registration failed.");
+	return false;
+}
+
+static void setup(const void *data)
+{
+	const struct generic_data *generic_data = data;
+	struct test_data *test_data = tester_get_data();
+	int signal_fd[2];
+	char buf[1024];
+	pid_t pid;
+	int len;
+	unsigned int i;
+
+	if (pipe(signal_fd))
+		goto failed;
+
+	pid = fork();
+
+	if (pid < 0) {
+		close(signal_fd[0]);
+		close(signal_fd[1]);
+		goto failed;
+	}
+
+	if (pid == 0) {
+		if (!tester_use_debug())
+			fclose(stderr);
+
+		close(signal_fd[0]);
+		emulator(signal_fd[1], test_data->mgmt_index);
+		exit(0);
+	}
+
+	close(signal_fd[1]);
+	test_data->bluetoothd_pid = pid;
+
+	len = read(signal_fd[0], buf, sizeof(buf));
+	if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) {
+		close(signal_fd[0]);
+		goto failed;
+	}
+
+	g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data,
+									NULL);
+
+	if (!init_ipc()) {
+		tester_warn("Cannot initialize IPC mechanism!");
+		goto failed;
+	}
+	tester_print("Will init %d services.", generic_data->num_services);
+
+	for (i = 0; i < generic_data->num_services; i++)
+		if (!setup_module(generic_data->init_services[i])) {
+			cleanup_ipc();
+			goto failed;
+		}
+
+	test_data->setup_done = true;
+
+	tester_setup_complete();
+	return;
+
+failed:
+	g_idle_remove_by_data(test_data);
+	tester_setup_failed();
+	test_post_teardown(data);
+}
+
+static void teardown(const void *data)
+{
+	struct test_data *test_data = tester_get_data();
+
+	g_idle_remove_by_data(test_data);
+	cleanup_ipc();
+
+	if (test_data->bluetoothd_pid)
+		waitpid(test_data->bluetoothd_pid, NULL, 0);
+
+	tester_teardown_complete();
+}
+
+static void ipc_send_tc(const void *data)
+{
+	const struct generic_data *generic_data = data;
+	const struct ipc_data *ipc_data = &generic_data->ipc_data;
+
+	if (ipc_data->len) {
+		if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0)
+			tester_test_failed();
+	}
+}
+
+#define service_data(args...) { args }
+
+#define gen_data(writelen, writebuf, servicelist...) \
+	{								\
+		.ipc_data = {						\
+			.buffer = writebuf,				\
+			.len = writelen,				\
+		},							\
+		.init_services = service_data(servicelist),		\
+		.num_services = sizeof((const int[])			\
+					service_data(servicelist)) /	\
+					sizeof(int),			\
+	}
+
+#define test_generic(name, test, setup, teardown, buffer, writelen, \
+							services...) \
+	do {								\
+		struct test_data *user;					\
+		static const struct generic_data data =			\
+				gen_data(writelen, buffer, services);	\
+		user = g_malloc0(sizeof(struct test_data));		\
+		if (!user)						\
+			break;						\
+		user->hciemu_type = HCIEMU_TYPE_BREDRLE;		\
+		tester_add_full(name, &data, test_pre_setup, setup,	\
+				test, teardown, test_post_teardown,	\
+				3, user, g_free);			\
+	} while (0)
+
+#define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \
+	do {								\
+		static struct ipc_hdr hdr = {				\
+			.service_id = _service,				\
+			.opcode = _opcode,				\
+			.len = _len,					\
+		};							\
+									\
+		test_generic("Opcode out of range: "_name,		\
+				ipc_send_tc, setup, teardown,		\
+				&hdr,					\
+				sizeof(hdr),				\
+				_servicelist);				\
+	} while (0)
+
+struct vardata {
+	struct ipc_hdr hdr;
+	uint8_t buf[IPC_MTU];
+} __attribute__((packed));
+
+#define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \
+							_servicelist...) \
+	do {								\
+		static struct vardata vdata = {				\
+			.hdr.service_id = _service,			\
+			.hdr.opcode = _opcode,				\
+			.hdr.len = (_hlen) + (_addatasize),		\
+			.buf = {},					\
+		};							\
+		test_generic("Data size "_name,				\
+				ipc_send_tc, setup, teardown,		\
+				&vdata,					\
+				sizeof(vdata.hdr) + (_hlen) + (_addatasize),\
+				_servicelist);				\
+	} while (0)
+
+static struct regmod_msg register_bt_msg = {
+	.header = {
+		.service_id = HAL_SERVICE_ID_CORE,
+		.opcode = HAL_OP_REGISTER_MODULE,
+		.len = sizeof(struct hal_cmd_register_module),
+		},
+	.cmd = {
+		.service_id = HAL_SERVICE_ID_BLUETOOTH,
+		},
+};
+
+static struct regmod_msg register_bt_malformed_size_msg = {
+	.header = {
+		.service_id = HAL_SERVICE_ID_CORE,
+		.opcode = HAL_OP_REGISTER_MODULE,
+		/* wrong payload size declared */
+		.len = sizeof(struct hal_cmd_register_module) - 1,
+		},
+	.cmd = {
+		.service_id = HAL_SERVICE_ID_CORE,
+		},
+};
+
+struct malformed_data3_struct {
+	struct regmod_msg valid_msg;
+	int redundant_data;
+}  __attribute__((packed));
+
+static struct malformed_data3_struct malformed_data3_msg = {
+	/* valid register service message */
+	.valid_msg = {
+		.header = {
+			.service_id = HAL_SERVICE_ID_CORE,
+			.opcode = HAL_OP_REGISTER_MODULE,
+			.len = sizeof(struct hal_cmd_register_module),
+			},
+		.cmd = {
+			.service_id = HAL_SERVICE_ID_CORE,
+			},
+	},
+	/* plus redundant data */
+	. redundant_data = 666,
+};
+
+static struct ipc_hdr enable_unknown_service_hdr = {
+	.service_id = HAL_SERVICE_ID_MAX + 1,
+	.opcode = HAL_OP_REGISTER_MODULE,
+	.len = 0,
+};
+
+static struct ipc_hdr enable_bt_service_hdr = {
+	.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.opcode = HAL_OP_ENABLE,
+	.len = 0,
+};
+
+struct bt_set_adapter_prop_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_set_adapter_prop prop;
+
+	/* data placeholder for hal_cmd_set_adapter_prop.val[0] */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_set_adapter_prop)];
+} __attribute__((packed));
+
+#define set_name "new name"
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name),
+
+	.prop.type = HAL_PROP_ADAPTER_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) + 1,
+	/* init prop.val[0] */
+	.buf = set_name,
+};
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + sizeof(set_name),
+
+	.prop.type = HAL_PROP_ADAPTER_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) - 1,
+	/* init prop.val[0] */
+	.buf = set_name,
+};
+
+struct bt_set_remote_prop_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_set_remote_device_prop prop;
+
+	/* data placeholder for hal_cmd_set_remote_device_prop.val[0] */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_set_remote_device_prop)];
+} __attribute__((packed));
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+							sizeof(set_name),
+
+	.prop.bdaddr = {},
+	.prop.type = HAL_PROP_DEVICE_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) + 1,
+	.buf = set_name,
+};
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+	.hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+	.hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+						sizeof(set_name),
+
+	.prop.bdaddr = {},
+	.prop.type = HAL_PROP_DEVICE_NAME,
+	/* declare wrong descriptor length */
+	.prop.len = sizeof(set_name) - 1,
+	.buf = set_name,
+};
+
+struct hidhost_set_info_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hidhost_set_info info;
+
+	/* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hidhost_set_info)];
+} __attribute__((packed));
+
+#define set_info_data "some descriptor"
+
+static struct hidhost_set_info_data hidhost_set_info_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+							sizeof(set_info_data),
+
+	/* declare wrong descriptor length */
+	.info.descr_len = sizeof(set_info_data) + 1,
+	/* init .info.descr[0] */
+	.buf = set_info_data,
+};
+
+static struct hidhost_set_info_data hidhost_set_info_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+							sizeof(set_info_data),
+
+	/* declare wrong descriptor length */
+	.info.descr_len = sizeof(set_info_data) - 1,
+	/* init .info.descr[0] */
+	.buf = set_info_data,
+};
+
+struct hidhost_set_report_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hidhost_set_report report;
+
+	/* data placeholder for hal_cmd_hidhost_set_report.data[0] field */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hidhost_set_report)];
+} __attribute__((packed));
+
+#define set_rep_data "1234567890"
+
+static struct hidhost_set_report_data hidhost_set_report_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+							sizeof(set_rep_data),
+
+	/* declare wrong descriptor length */
+	.report.len = sizeof(set_rep_data) + 1,
+	/* init report.data[0] */
+	.buf = set_rep_data,
+};
+
+static struct hidhost_set_report_data hidhost_set_report_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+							sizeof(set_rep_data),
+
+	/* declare wrong descriptor length */
+	.report.len = sizeof(set_rep_data) - 1,
+	/* init report.data[0] */
+	.buf = set_rep_data,
+};
+
+struct hidhost_send_data_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hidhost_send_data hiddata;
+
+	/* data placeholder for hal_cmd_hidhost_send_data.data[0] field */
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hidhost_send_data)];
+} __attribute__((packed));
+
+#define send_data_data "1234567890"
+
+static struct hidhost_send_data_data hidhost_send_data_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+							sizeof(send_data_data),
+
+	/* declare wrong descriptor length */
+	.hiddata.len = sizeof(send_data_data) + 1,
+	/* init .hiddata.data[0] */
+	.buf = send_data_data,
+};
+
+static struct hidhost_send_data_data hidhost_send_data_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+	.hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+	.hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+							sizeof(send_data_data),
+
+	/* declare wrong descriptor length */
+	.hiddata.len = sizeof(send_data_data) - 1,
+	/* init .hiddata.data[0] */
+	.buf = send_data_data,
+};
+
+#define hfp_number "#1234567890"
+
+struct hfp_dial_data {
+	struct ipc_hdr hdr;
+	struct hal_cmd_hf_client_dial data;
+
+	uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+				sizeof(struct hal_cmd_hf_client_dial)];
+} __attribute__((packed));
+
+static struct hfp_dial_data hfp_dial_overs = {
+	.hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT,
+	.hdr.opcode = HAL_OP_HF_CLIENT_DIAL,
+	.hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number),
+
+	.data.number_len = sizeof(hfp_number) + 1,
+	.buf = hfp_number,
+};
+
+static struct hfp_dial_data hfp_dial_unders = {
+	.hdr.service_id = HAL_SERVICE_ID_HANDSFREE_CLIENT,
+	.hdr.opcode = HAL_OP_HF_CLIENT_DIAL,
+	.hdr.len = sizeof(struct hal_cmd_hf_client_dial) + sizeof(hfp_number),
+
+	.data.number_len = sizeof(hfp_number) - 1,
+	.buf = hfp_number,
+};
+
+int main(int argc, char *argv[])
+{
+	snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+	tester_init(&argc, &argv);
+
+	/* check general IPC errors */
+	test_generic("Too small data",
+				ipc_send_tc, setup, teardown,
+				&register_bt_msg, 1);
+
+	test_generic("Malformed data (wrong payload declared)",
+				ipc_send_tc, setup, teardown,
+				&register_bt_malformed_size_msg,
+				sizeof(register_bt_malformed_size_msg),
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Malformed data2 (undersized msg)",
+				ipc_send_tc, setup, teardown,
+				&register_bt_msg,
+				sizeof(register_bt_msg) - 1,
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Malformed data3 (oversized msg)",
+				ipc_send_tc, setup, teardown,
+				&malformed_data3_msg,
+				sizeof(malformed_data3_msg),
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Invalid service",
+				ipc_send_tc, setup, teardown,
+				&enable_unknown_service_hdr,
+				sizeof(enable_unknown_service_hdr),
+				HAL_SERVICE_ID_BLUETOOTH);
+
+	test_generic("Enable unregistered service",
+				ipc_send_tc, setup, teardown,
+				&enable_bt_service_hdr,
+				sizeof(enable_bt_service_hdr));
+
+	/* check service handler's max opcode value */
+	test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0);
+
+	test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0,
+			HAL_SERVICE_ID_BLUETOOTH);
+
+	test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+	test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+	test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+	test_opcode_valid("HANDSFREE", HAL_SERVICE_ID_HANDSFREE, 0x10, 0,
+						HAL_SERVICE_ID_BLUETOOTH,
+						HAL_SERVICE_ID_HANDSFREE);
+
+	test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+	test_opcode_valid("HEALTH", HAL_SERVICE_ID_HEALTH, 0x06, 0,
+						HAL_SERVICE_ID_BLUETOOTH,
+						HAL_SERVICE_ID_HEALTH);
+
+	test_opcode_valid("AVRCP", HAL_SERVICE_ID_AVRCP, 0x0b, 0,
+				HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_AVRCP);
+
+	test_opcode_valid("GATT", HAL_SERVICE_ID_GATT, 0x24, 0,
+				HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_GATT);
+
+	test_opcode_valid("HF_CLIENT", HAL_SERVICE_ID_HANDSFREE_CLIENT, 0x10, 0,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+	test_opcode_valid("MAP_CLIENT", HAL_SERVICE_ID_MAP_CLIENT, 0x01, 0,
+						HAL_SERVICE_ID_BLUETOOTH,
+						HAL_SERVICE_ID_MAP_CLIENT);
+
+	/* check for valid data size */
+	test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE,
+			HAL_OP_REGISTER_MODULE,
+			sizeof(struct hal_cmd_register_module), 1);
+	test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE,
+			HAL_OP_REGISTER_MODULE,
+			sizeof(struct hal_cmd_register_module), -1);
+	test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE,
+			HAL_OP_UNREGISTER_MODULE,
+			sizeof(struct hal_cmd_unregister_module), 1);
+	test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE,
+			HAL_OP_UNREGISTER_MODULE,
+			sizeof(struct hal_cmd_unregister_module), -1);
+
+	/* check for valid data size for BLUETOOTH */
+	test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_ENABLE,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DISABLE,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_ADAPTER_PROPS,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_get_adapter_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_get_adapter_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_set_adapter_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_ADAPTER_PROP,
+			sizeof(struct hal_cmd_set_adapter_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Adapter Prop Vardata+",
+			ipc_send_tc, setup, teardown,
+			&bt_set_adapter_prop_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_adapter_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Adapter Prop Vardata+",
+			ipc_send_tc, setup, teardown,
+			&bt_set_adapter_prop_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_adapter_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROPS,
+			sizeof(struct hal_cmd_get_remote_device_props), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROPS,
+			sizeof(struct hal_cmd_get_remote_device_props), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_get_remote_device_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_get_remote_device_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_set_remote_device_prop), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SET_REMOTE_DEVICE_PROP,
+			sizeof(struct hal_cmd_set_remote_device_prop), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Remote Prop Vardata+",
+			ipc_send_tc, setup, teardown,
+			&bt_set_remote_prop_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_remote_device_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_generic("Data size BT Set Remote Prop Vardata-",
+			ipc_send_tc, setup, teardown,
+			&bt_set_remote_prop_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_set_remote_device_prop) +
+				sizeof(set_name)),
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICE_REC,
+			sizeof(struct hal_cmd_get_remote_service_rec), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICE_REC,
+			sizeof(struct hal_cmd_get_remote_service_rec), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICES,
+			sizeof(struct hal_cmd_get_remote_services), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_GET_REMOTE_SERVICES,
+			sizeof(struct hal_cmd_get_remote_services), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_START_DISCOVERY,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CANCEL_DISCOVERY,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CREATE_BOND,
+			sizeof(struct hal_cmd_create_bond), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CREATE_BOND,
+			sizeof(struct hal_cmd_create_bond), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_REMOVE_BOND,
+			sizeof(struct hal_cmd_remove_bond), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_REMOVE_BOND,
+			sizeof(struct hal_cmd_remove_bond), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CANCEL_BOND,
+			sizeof(struct hal_cmd_cancel_bond), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_CANCEL_BOND,
+			sizeof(struct hal_cmd_cancel_bond), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_PIN_REPLY,
+			sizeof(struct hal_cmd_pin_reply), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_PIN_REPLY,
+			sizeof(struct hal_cmd_pin_reply), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SSP_REPLY,
+			sizeof(struct hal_cmd_ssp_reply), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_SSP_REPLY,
+			sizeof(struct hal_cmd_ssp_reply), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_CONF,
+			sizeof(struct hal_cmd_dut_mode_conf), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_CONF,
+			sizeof(struct hal_cmd_dut_mode_conf), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_SEND,
+			sizeof(struct hal_cmd_dut_mode_send), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_DUT_MODE_SEND,
+			sizeof(struct hal_cmd_dut_mode_send), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_LE_TEST_MODE,
+			sizeof(struct hal_cmd_le_test_mode), 1,
+			HAL_SERVICE_ID_BLUETOOTH);
+	test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_LE_TEST_MODE,
+			sizeof(struct hal_cmd_le_test_mode), -1,
+			HAL_SERVICE_ID_BLUETOOTH);
+
+	/* check for valid data size for SOCK */
+	test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_LISTEN,
+			sizeof(struct hal_cmd_socket_listen), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+	test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_LISTEN,
+			sizeof(struct hal_cmd_socket_listen), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+	test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_CONNECT,
+			sizeof(struct hal_cmd_socket_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+	test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET,
+			HAL_OP_SOCKET_CONNECT,
+			sizeof(struct hal_cmd_socket_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+	/* check for valid data size for HID Host */
+	test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_CONNECT,
+			sizeof(struct hal_cmd_hidhost_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_CONNECT,
+			sizeof(struct hal_cmd_hidhost_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_DISCONNECT,
+			sizeof(struct hal_cmd_hidhost_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_DISCONNECT,
+			sizeof(struct hal_cmd_hidhost_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+			sizeof(struct hal_cmd_hidhost_virtual_unplug), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+			sizeof(struct hal_cmd_hidhost_virtual_unplug), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_INFO,
+			sizeof(struct hal_cmd_hidhost_set_info), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_INFO,
+			sizeof(struct hal_cmd_hidhost_set_info), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Info Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_info_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_info) +
+				sizeof(set_info_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Info Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_info_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_info) +
+				sizeof(set_info_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_get_protocol), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_get_protocol), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_set_protocol), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_PROTOCOL,
+			sizeof(struct hal_cmd_hidhost_set_protocol), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_REPORT,
+			sizeof(struct hal_cmd_hidhost_get_report), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_GET_REPORT,
+			sizeof(struct hal_cmd_hidhost_get_report), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_REPORT,
+			sizeof(struct hal_cmd_hidhost_set_report), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SET_REPORT,
+			sizeof(struct hal_cmd_hidhost_set_report), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Report Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_report_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_report) +
+				sizeof(set_rep_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Set Report Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hidhost_set_report_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_set_report) +
+				sizeof(set_rep_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SEND_DATA,
+			sizeof(struct hal_cmd_hidhost_send_data), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST,
+			HAL_OP_HIDHOST_SEND_DATA,
+			sizeof(struct hal_cmd_hidhost_send_data), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Send Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hidhost_send_data_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_send_data) +
+				sizeof(send_data_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+	test_generic("Data size HIDHOST Send Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hidhost_send_data_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hidhost_send_data) +
+				sizeof(send_data_data)),
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+	/* check for valid data size for PAN */
+	test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_ENABLE,
+			sizeof(struct hal_cmd_pan_enable), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_ENABLE,
+			sizeof(struct hal_cmd_pan_enable), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_GET_ROLE,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_CONNECT,
+			sizeof(struct hal_cmd_pan_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_CONNECT,
+			sizeof(struct hal_cmd_pan_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_DISCONNECT,
+			sizeof(struct hal_cmd_pan_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+	test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN,
+			HAL_OP_PAN_DISCONNECT,
+			sizeof(struct hal_cmd_pan_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+	/* check for valid data size for A2DP */
+	test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_CONNECT,
+			sizeof(struct hal_cmd_a2dp_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+	test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_CONNECT,
+			sizeof(struct hal_cmd_a2dp_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+	test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_DISCONNECT,
+			sizeof(struct hal_cmd_a2dp_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+	test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP,
+			HAL_OP_A2DP_DISCONNECT,
+			sizeof(struct hal_cmd_a2dp_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+	/* Check for valid data size for Handsfree Client */
+	test_datasize_valid("HF_CLIENT Connect+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT,
+			sizeof(struct hal_cmd_hf_client_connect), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Connect-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT,
+			sizeof(struct hal_cmd_hf_client_connect), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT,
+			sizeof(struct hal_cmd_hf_client_disconnect), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT,
+			sizeof(struct hal_cmd_hf_client_disconnect), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Connect Audio+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_connect_audio), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Connect Audio-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_connect_audio), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect Audio+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_disconnect_audio), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Disconnect Audio-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
+			sizeof(struct hal_cmd_hf_client_disconnect_audio), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Start VR+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_START_VR,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Start VR-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_START_VR,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Stop VR+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_STOP_VR,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Stop VR-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_STOP_VR,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Vol Contr.+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+			sizeof(struct hal_cmd_hf_client_volume_control), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Vol Contr.-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_VOLUME_CONTROL,
+			sizeof(struct hal_cmd_hf_client_volume_control), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_generic("Data size HF_CLIENT Dial Vardata+",
+			ipc_send_tc, setup, teardown,
+			&hfp_dial_overs,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hf_client_dial) +
+				sizeof(hfp_number)),
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_generic("Data size HF_CLIENT Dial Vardata-",
+			ipc_send_tc, setup, teardown,
+			&hfp_dial_unders,
+			(sizeof(struct ipc_hdr) +
+				sizeof(struct hal_cmd_hf_client_dial) +
+				sizeof(hfp_number)),
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Dial Memory+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DIAL_MEMORY,
+			sizeof(struct hal_cmd_hf_client_dial_memory), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Dial Memory-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_DIAL_MEMORY,
+			sizeof(struct hal_cmd_hf_client_dial_memory), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Call Action+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CALL_ACTION,
+			sizeof(struct hal_cmd_hf_client_call_action), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Call Action-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_CALL_ACTION,
+			sizeof(struct hal_cmd_hf_client_call_action), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Current Calls+",
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Current Calls-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_QUERY_CURRENT_CALLS,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Operator Name+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Query Operator Name-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_QUERY_OPERATOR_NAME,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Retrieve Subscrb. Info-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_RETRIEVE_SUBSCR_INFO,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Send DTMF+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_SEND_DTMF,
+			sizeof(struct hal_cmd_hf_client_send_dtmf), 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Send DTMF-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_SEND_DTMF,
+			sizeof(struct hal_cmd_hf_client_send_dtmf), -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Get Last Voice Tag+",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+			0, 1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+	test_datasize_valid("HF_CLIENT Get Last Voice Tag-",
+			HAL_SERVICE_ID_HANDSFREE_CLIENT,
+			HAL_OP_HF_CLIENT_GET_LAST_VOICE_TAG_NUM,
+			0, -1,
+			HAL_SERVICE_ID_BLUETOOTH,
+			HAL_SERVICE_ID_HANDSFREE_CLIENT);
+
+	/* check for valid data size for MAP CLIENT */
+	test_datasize_valid("MAP CLIENT Get instances+",
+				HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES,
+				sizeof(struct hal_cmd_map_client_get_instances),
+				1, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_SERVICE_ID_MAP_CLIENT);
+	test_datasize_valid("MAP CLIENT Get instances-",
+				HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES,
+				sizeof(struct hal_cmd_map_client_get_instances),
+				-1, HAL_SERVICE_ID_BLUETOOTH,
+				HAL_SERVICE_ID_MAP_CLIENT);
+
+	return tester_run();
+}
diff --git a/bluez/repo/android/ipc.c b/bluez/repo/android/ipc.c
new file mode 100644
index 0000000..2e67428
--- /dev/null
+++ b/bluez/repo/android/ipc.c
@@ -0,0 +1,437 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stddef.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "ipc-common.h"
+#include "ipc.h"
+#include "src/log.h"
+
+struct service_handler {
+	const struct ipc_handler *handler;
+	uint8_t size;
+};
+
+struct ipc {
+	struct service_handler *services;
+	int service_max;
+
+	const char *path;
+	size_t size;
+
+	GIOChannel *cmd_io;
+	guint cmd_watch;
+
+	bool notifications;
+	GIOChannel *notif_io;
+	guint notif_watch;
+
+	ipc_disconnect_cb disconnect_cb;
+	void *disconnect_cb_data;
+};
+
+static void ipc_disconnect(struct ipc *ipc, bool in_cleanup)
+{
+	if (ipc->cmd_watch) {
+		g_source_remove(ipc->cmd_watch);
+		ipc->cmd_watch = 0;
+	}
+
+	if (ipc->cmd_io) {
+		g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL);
+		g_io_channel_unref(ipc->cmd_io);
+		ipc->cmd_io = NULL;
+	}
+
+	if (ipc->notif_watch) {
+		g_source_remove(ipc->notif_watch);
+		ipc->notif_watch = 0;
+	}
+
+	if (ipc->notif_io) {
+		g_io_channel_shutdown(ipc->notif_io, TRUE, NULL);
+		g_io_channel_unref(ipc->notif_io);
+		ipc->notif_io = NULL;
+	}
+
+	if (in_cleanup)
+		return;
+
+	if (ipc->disconnect_cb)
+		ipc->disconnect_cb(ipc->disconnect_cb_data);
+}
+
+static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
+						const void *buf, ssize_t len)
+{
+	const struct ipc_hdr *msg = buf;
+	const struct ipc_handler *handler;
+
+	if (len < (ssize_t) sizeof(*msg)) {
+		DBG("message too small (%zd bytes)", len);
+		return -EBADMSG;
+	}
+
+	if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+		DBG("message malformed (%zd bytes)", len);
+		return -EBADMSG;
+	}
+
+	/* if service is valid */
+	if (msg->service_id > max_index) {
+		DBG("unknown service (0x%x)", msg->service_id);
+		return -EOPNOTSUPP;
+	}
+
+	/* if service is registered */
+	if (!handlers[msg->service_id].handler) {
+		DBG("service not registered (0x%x)", msg->service_id);
+		return -EOPNOTSUPP;
+	}
+
+	/* if opcode is valid */
+	if (msg->opcode == IPC_OP_STATUS ||
+			msg->opcode > handlers[msg->service_id].size) {
+		DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
+							msg->service_id);
+		return -EOPNOTSUPP;
+	}
+
+	/* opcode is table offset + 1 */
+	handler = &handlers[msg->service_id].handler[msg->opcode - 1];
+
+	/* if payload size is valid */
+	if ((handler->var_len && handler->data_len > msg->len) ||
+			(!handler->var_len && handler->data_len != msg->len)) {
+		DBG("invalid size for opcode 0x%x service 0x%x",
+						msg->opcode, msg->service_id);
+		return -EMSGSIZE;
+	}
+
+	handler->handler(msg->payload, msg->len);
+
+	return 0;
+}
+
+static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	char buf[IPC_MTU];
+	ssize_t ret;
+	int fd, err;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		info("IPC: command socket closed");
+
+		ipc->cmd_watch = 0;
+		goto fail;
+	}
+
+	fd = g_io_channel_unix_get_fd(io);
+
+	ret = read(fd, buf, sizeof(buf));
+	if (ret < 0) {
+		error("IPC: command read failed (%s)", strerror(errno));
+		goto fail;
+	}
+
+	err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret);
+	if (err < 0) {
+		error("IPC: failed to handle message (%s)", strerror(-err));
+		goto fail;
+	}
+
+	return TRUE;
+
+fail:
+	ipc_disconnect(ipc, false);
+
+	return FALSE;
+}
+
+static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	info("IPC: notification socket closed");
+
+	ipc->notif_watch = 0;
+
+	ipc_disconnect(ipc, false);
+
+	return FALSE;
+}
+
+static GIOChannel *ipc_connect(const char *path, size_t size,
+					GIOFunc connect_cb, void *user_data)
+{
+	struct sockaddr_un addr;
+	GIOCondition cond;
+	GIOChannel *io;
+	int sk;
+
+	sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		error("IPC: failed to create socket: %d (%s)", errno,
+							strerror(errno));
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sk);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, path, size);
+
+	connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	g_io_add_watch(io, cond, connect_cb, user_data);
+
+	return io;
+}
+
+static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	DBG("");
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		error("IPC: notification socket connect failed");
+
+		ipc_disconnect(ipc, false);
+
+		return FALSE;
+	}
+
+	cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc);
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+	info("IPC: successfully connected (with notifications)");
+
+	return FALSE;
+}
+
+static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ipc *ipc = user_data;
+
+	DBG("");
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		error("IPC: command socket connect failed");
+		ipc_disconnect(ipc, false);
+
+		return FALSE;
+	}
+
+	if (ipc->notifications) {
+		ipc->notif_io = ipc_connect(ipc->path, ipc->size,
+							notif_connect_cb, ipc);
+		if (!ipc->notif_io)
+			ipc_disconnect(ipc, false);
+
+		return FALSE;
+	}
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+	info("IPC: successfully connected (without notifications)");
+
+	return FALSE;
+}
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+					bool notifications,
+					ipc_disconnect_cb cb, void *cb_data)
+{
+	struct ipc *ipc;
+
+	ipc = g_new0(struct ipc, 1);
+
+	ipc->services = g_new0(struct service_handler, max_service_id + 1);
+	ipc->service_max = max_service_id;
+
+	ipc->path = path;
+	ipc->size = size;
+
+	ipc->notifications = notifications;
+
+	ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc);
+	if (!ipc->cmd_io) {
+		g_free(ipc->services);
+		g_free(ipc);
+		return NULL;
+	}
+
+	ipc->disconnect_cb = cb;
+	ipc->disconnect_cb_data = cb_data;
+
+	return ipc;
+}
+
+void ipc_cleanup(struct ipc *ipc)
+{
+	ipc_disconnect(ipc, true);
+
+	g_free(ipc->services);
+	g_free(ipc);
+}
+
+static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
+							void *param, int fd)
+{
+	struct msghdr msg;
+	struct iovec iv[2];
+	struct ipc_hdr m;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+	struct cmsghdr *cmsg;
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&m, 0, sizeof(m));
+	memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+	m.service_id = service_id;
+	m.opcode = opcode;
+	m.len = len;
+
+	iv[0].iov_base = &m;
+	iv[0].iov_len = sizeof(m);
+
+	iv[1].iov_base = param;
+	iv[1].iov_len = len;
+
+	msg.msg_iov = iv;
+	msg.msg_iovlen = 2;
+
+	if (fd >= 0) {
+		msg.msg_control = cmsgbuf;
+		msg.msg_controllen = sizeof(cmsgbuf);
+
+		cmsg = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_RIGHTS;
+		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+		/* Initialize the payload */
+		memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+	}
+
+	if (sendmsg(sk, &msg, 0) < 0) {
+		error("IPC send failed :%s", strerror(errno));
+
+		/* TODO disconnect IPC here when this function becomes static */
+		raise(SIGTERM);
+	}
+}
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+								uint8_t status)
+{
+	struct ipc_status s;
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(ipc->cmd_io);
+
+	if (status == IPC_STATUS_SUCCESS) {
+		ipc_send(sk, service_id, opcode, 0, NULL, -1);
+		return;
+	}
+
+	s.code = status;
+
+	ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1);
+}
+
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd)
+{
+	ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len,
+								param, fd);
+}
+
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+						uint16_t len, void *param)
+{
+	return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1);
+}
+
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd)
+{
+	if (!ipc || !ipc->notif_io)
+		return;
+
+	ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
+								len, param, fd);
+}
+
+void ipc_register(struct ipc *ipc, uint8_t service,
+			const struct ipc_handler *handlers, uint8_t size)
+{
+	if (service > ipc->service_max)
+		return;
+
+	ipc->services[service].handler = handlers;
+	ipc->services[service].size = size;
+}
+
+void ipc_unregister(struct ipc *ipc, uint8_t service)
+{
+	if (service > ipc->service_max)
+		return;
+
+	ipc->services[service].handler = NULL;
+	ipc->services[service].size = 0;
+}
diff --git a/bluez/repo/android/ipc.h b/bluez/repo/android/ipc.h
new file mode 100644
index 0000000..fd2b985
--- /dev/null
+++ b/bluez/repo/android/ipc.h
@@ -0,0 +1,50 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 ipc_handler {
+	void (*handler) (const void *buf, uint16_t len);
+	bool var_len;
+	size_t data_len;
+};
+
+struct ipc;
+
+typedef void (*ipc_disconnect_cb) (void *data);
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+					bool notifications,
+					ipc_disconnect_cb cb, void *cb_data);
+void ipc_cleanup(struct ipc *ipc);
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+								uint8_t status);
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd);
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+						uint16_t len, void *param);
+void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+					uint16_t len, void *param, int fd);
+
+void ipc_register(struct ipc *ipc, uint8_t service,
+			const struct ipc_handler *handlers, uint8_t size);
+void ipc_unregister(struct ipc *ipc, uint8_t service);
diff --git a/bluez/repo/android/log.c b/bluez/repo/android/log.c
new file mode 100644
index 0000000..35917c6
--- /dev/null
+++ b/bluez/repo/android/log.c
@@ -0,0 +1,216 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "src/log.h"
+
+#define LOG_TAG "bluetoothd"
+
+#define LOG_DEBUG 3
+#define LOG_INFO 4
+#define LOG_WARN 5
+#define LOG_ERR 6
+
+#define LOG_ID_SYSTEM 3
+
+struct logd_header {
+	uint8_t id;
+	uint16_t pid; /* Android logd expects only 2 bytes for PID */
+	uint32_t sec;
+	uint32_t nsec;
+} __attribute__ ((packed));
+
+static int log_fd = -1;
+static bool legacy_log = false;
+
+static void android_log(unsigned char level, const char *fmt, va_list ap)
+{
+	struct logd_header header;
+	struct iovec vec[4];
+	int cnt = 0;
+	char *msg;
+	static pid_t pid = 0;
+
+	if (log_fd < 0)
+		return;
+
+	/* no need to call getpid all the time since we don't fork */
+	if (!pid)
+		pid = getpid();
+
+	if (vasprintf(&msg, fmt, ap) < 0)
+		return;
+
+	if (!legacy_log) {
+		struct timespec ts;
+
+		clock_gettime(CLOCK_REALTIME, &ts);
+
+		header.id = LOG_ID_SYSTEM;
+		header.pid = pid;
+		header.sec = ts.tv_sec;
+		header.nsec = ts.tv_nsec;
+
+		vec[0].iov_base = &header;
+		vec[0].iov_len = sizeof(header);
+
+		cnt += 1;
+	}
+
+	vec[cnt + 0].iov_base = &level;
+	vec[cnt + 0].iov_len = sizeof(level);
+	vec[cnt + 1].iov_base = LOG_TAG;
+	vec[cnt + 1].iov_len = sizeof(LOG_TAG);
+	vec[cnt + 2].iov_base  = msg;
+	vec[cnt + 2].iov_len  = strlen(msg) + 1;
+
+	cnt += 3;
+
+	writev(log_fd, vec, cnt);
+
+	free(msg);
+}
+
+void info(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_INFO, format, ap);
+
+	va_end(ap);
+}
+
+void warn(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_WARN, format, ap);
+
+	va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_ERR, format, ap);
+
+	va_end(ap);
+}
+
+void btd_debug(uint16_t index, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	android_log(LOG_DEBUG, format, ap);
+
+	va_end(ap);
+}
+
+static bool init_legacy_log(void)
+{
+	log_fd = open("/dev/log/system", O_WRONLY);
+	if (log_fd < 0)
+		return false;
+
+	legacy_log = true;
+
+	return true;
+}
+
+static bool init_logd(void)
+{
+	struct sockaddr_un addr;
+
+	log_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (log_fd < 0)
+		return false;
+
+	if (fcntl(log_fd, F_SETFL, O_NONBLOCK) < 0)
+		goto failed;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strcpy(addr.sun_path, "/dev/socket/logdw");
+
+	if (connect(log_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+		goto failed;
+
+	return true;
+
+failed:
+	close(log_fd);
+	log_fd = -1;
+
+	return false;
+}
+
+extern struct btd_debug_desc __start___debug[];
+extern struct btd_debug_desc __stop___debug[];
+
+void __btd_log_init(const char *debug, int detach)
+{
+	if (!init_logd() && !init_legacy_log())
+		return;
+
+	if (debug) {
+		struct btd_debug_desc *desc;
+
+		for (desc = __start___debug; desc < __stop___debug; desc++)
+			desc->flags |= BTD_DEBUG_FLAG_PRINT;
+	}
+
+	info("Bluetooth daemon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+	if (log_fd < 0)
+		return;
+
+	close(log_fd);
+	log_fd = -1;
+}
diff --git a/bluez/repo/android/main.c b/bluez/repo/android/main.c
new file mode 100644
index 0000000..03c8760
--- /dev/null
+++ b/bluez/repo/android/main.c
@@ -0,0 +1,805 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdbool.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/signalfd.h>
+#if defined(ANDROID)
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/sdpd.h"
+#include "src/shared/util.h"
+
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "socket.h"
+#include "hidhost.h"
+#include "hal-msg.h"
+#include "a2dp.h"
+#include "pan.h"
+#include "avrcp.h"
+#include "handsfree.h"
+#include "gatt.h"
+#include "health.h"
+#include "handsfree-client.h"
+#include "map-client.h"
+#include "utils.h"
+
+#define DEFAULT_VENDOR "BlueZ"
+#define DEFAULT_MODEL "BlueZ for Android"
+#define DEFAULT_NAME "BlueZ for Android"
+
+#define STARTUP_GRACE_SECONDS 5
+#define SHUTDOWN_GRACE_SECONDS 5
+
+static char *config_vendor = NULL;
+static char *config_model = NULL;
+static char *config_name = NULL;
+static char *config_serial = NULL;
+static char *config_fw_rev = NULL;
+static char *config_hw_rev = NULL;
+static uint64_t config_system_id = 0;
+static uint16_t config_pnp_source = 0x0002;	/* USB */
+static uint16_t config_pnp_vendor = 0x1d6b;	/* Linux Foundation */
+static uint16_t config_pnp_product = 0x0247;	/* BlueZ for Android */
+static uint16_t config_pnp_version = 0x0000;
+
+static guint quit_timeout = 0;
+
+static bdaddr_t adapter_bdaddr;
+
+static GMainLoop *event_loop;
+
+static struct ipc *hal_ipc = NULL;
+
+static bool services[HAL_SERVICE_ID_MAX + 1] = { false };
+
+const char *bt_config_get_vendor(void)
+{
+	if (config_vendor)
+		return config_vendor;
+
+	return DEFAULT_VENDOR;
+}
+
+const char *bt_config_get_name(void)
+{
+	if (config_name)
+		return config_name;
+
+	return DEFAULT_NAME;
+}
+
+const char *bt_config_get_model(void)
+{
+	if (config_model)
+		return config_model;
+
+	return DEFAULT_MODEL;
+}
+
+const char *bt_config_get_serial(void)
+{
+	return config_serial;
+}
+
+const char *bt_config_get_fw_rev(void)
+{
+	return config_fw_rev;
+}
+
+const char *bt_config_get_hw_rev(void)
+{
+	return config_hw_rev;
+}
+
+uint64_t bt_config_get_system_id(void)
+{
+	return config_system_id;
+}
+
+uint16_t bt_config_get_pnp_source(void)
+{
+	return config_pnp_source;
+}
+
+uint16_t bt_config_get_pnp_vendor(void)
+{
+	return config_pnp_vendor;
+}
+
+uint16_t bt_config_get_pnp_product(void)
+{
+	return config_pnp_product;
+}
+
+uint16_t bt_config_get_pnp_version(void)
+{
+	return config_pnp_version;
+}
+
+static void service_register(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_register_module *m = buf;
+	uint8_t status;
+
+	if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	switch (m->service_id) {
+	case HAL_SERVICE_ID_BLUETOOTH:
+		if (!bt_bluetooth_register(hal_ipc, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_SOCKET:
+		bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode);
+
+		break;
+	case HAL_SERVICE_ID_HIDHOST:
+		if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_A2DP:
+		if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_PAN:
+		if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_AVRCP:
+		if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_HANDSFREE:
+		if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode,
+							m->max_clients)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_GATT:
+		if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_HEALTH:
+		if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_HANDSFREE_CLIENT:
+		if (!bt_hf_client_register(hal_ipc, &adapter_bdaddr)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	case HAL_SERVICE_ID_MAP_CLIENT:
+		if (!bt_map_client_register(hal_ipc, &adapter_bdaddr,
+								m->mode)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
+		break;
+	default:
+		DBG("service %u not supported", m->service_id);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	services[m->service_id] = true;
+
+	status = HAL_STATUS_SUCCESS;
+
+	info("Service ID=%u registered", m->service_id);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+								status);
+}
+
+static bool unregister_service(uint8_t id)
+{
+	if (id > HAL_SERVICE_ID_MAX || !services[id])
+		return false;
+
+	switch (id) {
+	case HAL_SERVICE_ID_BLUETOOTH:
+		bt_bluetooth_unregister();
+		break;
+	case HAL_SERVICE_ID_SOCKET:
+		bt_socket_unregister();
+		break;
+	case HAL_SERVICE_ID_HIDHOST:
+		bt_hid_unregister();
+		break;
+	case HAL_SERVICE_ID_A2DP:
+		bt_a2dp_unregister();
+		break;
+	case HAL_SERVICE_ID_PAN:
+		bt_pan_unregister();
+		break;
+	case HAL_SERVICE_ID_AVRCP:
+		bt_avrcp_unregister();
+		break;
+	case HAL_SERVICE_ID_HANDSFREE:
+		bt_handsfree_unregister();
+		break;
+	case HAL_SERVICE_ID_GATT:
+		bt_gatt_unregister();
+		break;
+	case HAL_SERVICE_ID_HEALTH:
+		bt_health_unregister();
+		break;
+	case HAL_SERVICE_ID_HANDSFREE_CLIENT:
+		bt_hf_client_unregister();
+		break;
+	case HAL_SERVICE_ID_MAP_CLIENT:
+		bt_map_client_unregister();
+		break;
+	default:
+		DBG("service %u not supported", id);
+		return false;
+	}
+
+	services[id] = false;
+
+	return true;
+}
+
+static void service_unregister(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_unregister_module *m = buf;
+	uint8_t status;
+
+	if (!unregister_service(m->service_id)) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+	info("Service ID=%u unregistered", m->service_id);
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+								status);
+}
+
+static char *get_prop(char *prop, uint16_t len, const uint8_t *val)
+{
+	/* TODO should fail if set more than once ? */
+	free(prop);
+
+	prop = malloc0(len);
+	if (!prop)
+		return NULL;
+
+	memcpy(prop, val, len);
+	prop[len - 1] = '\0';
+
+	return prop;
+}
+
+static void parse_pnp_id(uint16_t len, const uint8_t *val)
+{
+	int result;
+	uint16_t vendor, product, version , source;
+	char *pnp;
+
+	/* version is optional */
+	version = config_pnp_version;
+
+	pnp = get_prop(NULL, len, val);
+	if (!pnp)
+		return;
+
+	DBG("pnp_id %s", pnp);
+
+	result = sscanf(pnp, "bluetooth:%4hx:%4hx:%4hx",
+						&vendor, &product, &version);
+	if (result != EOF && result >= 2) {
+		source = 0x0001;
+		goto done;
+	}
+
+	result = sscanf(pnp, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version);
+	if (result != EOF && result >= 2) {
+		source = 0x0002;
+		goto done;
+	}
+
+	free(pnp);
+	return;
+done:
+	free(pnp);
+
+	config_pnp_source = source;
+	config_pnp_vendor = vendor;
+	config_pnp_product = product;
+	config_pnp_version = version;
+}
+
+static void parse_system_id(uint16_t len, const uint8_t *val)
+{
+	uint64_t res;
+	char *id;
+
+	id = get_prop(NULL, len, val);
+	if (!id)
+		return;
+
+	res = strtoull(id, NULL, 16);
+	if (res == ULLONG_MAX && errno == ERANGE)
+		goto done;
+
+	config_system_id = res;
+done:
+	free(id);
+}
+
+static void configuration(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_configuration *cmd = buf;
+	const struct hal_config_prop *prop;
+	unsigned int i;
+
+	buf += sizeof(*cmd);
+	len -= sizeof(*cmd);
+
+	for (i = 0; i < cmd->num; i++) {
+		prop = buf;
+
+		if (len < sizeof(*prop) || len < sizeof(*prop) + prop->len) {
+			error("Invalid configuration command, terminating");
+			raise(SIGTERM);
+			return;
+		}
+
+		switch (prop->type) {
+		case HAL_CONFIG_VENDOR:
+			config_vendor = get_prop(config_vendor, prop->len,
+								prop->val);
+			DBG("vendor %s", config_vendor);
+			break;
+		case HAL_CONFIG_NAME:
+			config_name = get_prop(config_name, prop->len,
+								prop->val);
+			DBG("name %s", config_name);
+			break;
+		case HAL_CONFIG_MODEL:
+			config_model = get_prop(config_model, prop->len,
+								prop->val);
+			DBG("model %s", config_model);
+			break;
+		case HAL_CONFIG_SERIAL_NUMBER:
+			config_serial = get_prop(config_serial, prop->len,
+								prop->val);
+			DBG("serial %s", config_serial);
+			break;
+		case HAL_CONFIG_SYSTEM_ID:
+			parse_system_id(prop->len, prop->val);
+			break;
+		case HAL_CONFIG_PNP_ID:
+			parse_pnp_id(prop->len, prop->val);
+			break;
+		case HAL_CONFIG_FW_REV:
+			config_fw_rev = get_prop(config_fw_rev, prop->len,
+								prop->val);
+			DBG("fw_rev %s", config_fw_rev);
+			break;
+		case HAL_CONFIG_HW_REV:
+			config_hw_rev = get_prop(config_hw_rev, prop->len,
+								prop->val);
+			DBG("hw_rev %s", config_hw_rev);
+			break;
+		default:
+			error("Invalid configuration option (%u), terminating",
+								prop->type);
+			raise(SIGTERM);
+			return;
+		}
+
+		buf += sizeof(*prop) + prop->len;
+		len -= sizeof(*prop) + prop->len;
+	}
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_CONFIGURATION,
+							HAL_STATUS_SUCCESS);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_REGISTER_MODULE */
+	{ service_register, false, sizeof(struct hal_cmd_register_module) },
+	/* HAL_OP_UNREGISTER_MODULE */
+	{ service_unregister, false, sizeof(struct hal_cmd_unregister_module) },
+	/* HAL_OP_CONFIGURATION */
+	{ configuration, true, sizeof(struct hal_cmd_configuration) },
+};
+
+static void bluetooth_stopped(void)
+{
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+	g_main_loop_quit(event_loop);
+
+	quit_timeout = 0;
+
+	return FALSE;
+}
+
+static void stop_bluetooth(void)
+{
+	static bool __stop = false;
+
+	if (__stop)
+		return;
+
+	__stop = true;
+
+	if (!bt_bluetooth_stop(bluetooth_stopped)) {
+		g_main_loop_quit(event_loop);
+		return;
+	}
+
+	quit_timeout = g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+							quit_eventloop, NULL);
+}
+
+static void ipc_disconnected(void *data)
+{
+	stop_bluetooth();
+}
+
+static void adapter_ready(int err, const bdaddr_t *addr)
+{
+	if (err < 0) {
+		error("Adapter initialization failed: %s", strerror(-err));
+		exit(EXIT_FAILURE);
+	}
+
+	bacpy(&adapter_bdaddr, addr);
+
+	if (quit_timeout > 0) {
+		g_source_remove(quit_timeout);
+		quit_timeout = 0;
+	}
+
+	info("Adapter initialized");
+
+	hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+						HAL_SERVICE_ID_MAX, true,
+						ipc_disconnected, NULL);
+	if (!hal_ipc) {
+		error("Failed to initialize IPC");
+		exit(EXIT_FAILURE);
+	}
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	static bool __terminated = false;
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		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:
+	case SIGTERM:
+		if (!__terminated) {
+			info("Terminating");
+			stop_bluetooth();
+		}
+
+		__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 gboolean option_version = FALSE;
+static gint option_index = -1;
+static gboolean option_dbg = FALSE;
+static gboolean option_mgmt_dbg = FALSE;
+
+static GOptionEntry options[] = {
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit", NULL },
+	{ "index", 'i', 0, G_OPTION_ARG_INT, &option_index,
+				"Use specified controller", "INDEX"},
+	{ "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg,
+				"Enable debug logs", NULL},
+	{ "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg,
+				"Enable mgmt debug logs", NULL},
+
+	{ NULL }
+};
+
+static void cleanup_services(void)
+{
+	int i;
+
+	DBG("");
+
+	for (i = HAL_SERVICE_ID_MAX; i > HAL_SERVICE_ID_CORE; i--)
+		unregister_service(i);
+}
+
+static bool set_capabilities(void)
+{
+#if defined(ANDROID)
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+
+	/*
+	 * CAP_NET_ADMIN: Allow use of MGMT interface
+	 * CAP_NET_BIND_SERVICE: Allow use of privileged PSM
+	 * CAP_NET_RAW: Allow use of bnep ioctl calls
+	 */
+	cap.effective = cap.permitted =
+		CAP_TO_MASK(CAP_NET_RAW) |
+		CAP_TO_MASK(CAP_NET_ADMIN) |
+		CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+	cap.inheritable = 0;
+
+	/* don't clear capabilities when dropping root */
+	if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+		error("%s: prctl(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	/* Android bluetooth user UID=1002 */
+	if (setuid(1002) < 0) {
+		error("%s: setuid(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	/* TODO: Move to cap_set_proc once bionic support it */
+	if (capset(&header, &cap) < 0) {
+		error("%s: capset(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	/* TODO: Move to cap_get_proc once bionic support it */
+	if (capget(&header, &cap) < 0) {
+		error("%s: capget(): %s", __func__, strerror(errno));
+		return false;
+	}
+
+	DBG("Caps: eff: 0x%x, perm: 0x%x, inh: 0x%x", cap.effective,
+					cap.permitted, cap.inheritable);
+
+#endif
+	return true;
+}
+
+static void set_version(void)
+{
+	uint8_t major, minor;
+
+	if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
+		return;
+
+	config_pnp_version = major << 8 | minor;
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	guint signal;
+
+	set_version();
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+		if (err != NULL) {
+			g_printerr("%s\n", err->message);
+			g_error_free(err);
+		} else
+			g_printerr("An unknown error occurred\n");
+
+		exit(EXIT_FAILURE);
+	}
+
+	g_option_context_free(context);
+
+	if (option_version == TRUE) {
+		printf("%s\n", VERSION);
+		exit(EXIT_SUCCESS);
+	}
+
+	signal = setup_signalfd();
+	if (!signal)
+		return EXIT_FAILURE;
+
+	if (option_dbg || option_mgmt_dbg)
+		__btd_log_init("*", 0);
+	else
+		__btd_log_init(NULL, 0);
+
+	if (!set_capabilities()) {
+		__btd_log_cleanup();
+		g_source_remove(signal);
+		return EXIT_FAILURE;
+	}
+
+	quit_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS,
+							quit_eventloop, NULL);
+	if (quit_timeout == 0) {
+		error("Failed to init startup timeout");
+		__btd_log_cleanup();
+		g_source_remove(signal);
+		return EXIT_FAILURE;
+	}
+
+	if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) {
+		__btd_log_cleanup();
+		g_source_remove(quit_timeout);
+		g_source_remove(signal);
+		return EXIT_FAILURE;
+	}
+
+	/* Use params: mtu = 0, flags = 0 */
+	start_sdp_server(0, 0);
+
+	DBG("Entering main loop");
+
+	event_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(event_loop);
+
+	g_source_remove(signal);
+
+	if (quit_timeout > 0)
+		g_source_remove(quit_timeout);
+
+	cleanup_services();
+
+	stop_sdp_server();
+	bt_bluetooth_cleanup();
+	g_main_loop_unref(event_loop);
+
+	/* If no adapter was initialized, hal_ipc is NULL */
+	if (hal_ipc) {
+		ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+		ipc_cleanup(hal_ipc);
+	}
+
+	info("Exit");
+
+	__btd_log_cleanup();
+
+	free(config_vendor);
+	free(config_model);
+	free(config_name);
+	free(config_serial);
+	free(config_fw_rev);
+	free(config_hw_rev);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/android/map-client.c b/bluez/repo/android/map-client.c
new file mode 100644
index 0000000..e3ad148
--- /dev/null
+++ b/bluez/repo/android/map-client.c
@@ -0,0 +1,203 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+
+#include "ipc.h"
+#include "lib/bluetooth.h"
+#include "map-client.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "utils.h"
+#include "src/shared/util.h"
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+
+static int fill_mce_inst(void *buf, int32_t id, int32_t scn, int32_t msg_type,
+					const void *name, uint8_t name_len)
+{
+	struct hal_map_client_mas_instance *inst = buf;
+
+	inst->id = id;
+	inst->scn = scn;
+	inst->msg_types = msg_type;
+	inst->name_len = name_len;
+
+	if (name_len)
+		memcpy(inst->name, name, name_len);
+
+	return sizeof(*inst) + name_len;
+}
+
+static void map_client_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	uint8_t buf[IPC_MTU];
+	struct hal_ev_map_client_remote_mas_instances *ev = (void *) buf;
+	bdaddr_t *dst = data;
+	sdp_list_t *list, *protos;
+	uint8_t status;
+	int32_t id, scn, msg_type, name_len, num_instances = 0;
+	char *name;
+	size_t size;
+
+	size = sizeof(*ev);
+	bdaddr2android(dst, &ev->bdaddr);
+
+	if (err < 0) {
+		error("mce: Unable to get SDP record: %s", strerror(-err));
+		status = HAL_STATUS_FAILED;
+		goto fail;
+	}
+
+	for (list = recs; list != NULL; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_data_t *data;
+
+		data = sdp_data_get(rec, SDP_ATTR_MAS_INSTANCE_ID);
+		if (!data) {
+			error("mce: cannot get mas instance id");
+			continue;
+		}
+
+		id = data->val.uint8;
+
+		data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+		if (!data) {
+			error("mce: cannot get mas instance name");
+			continue;
+		}
+
+		name = data->val.str;
+		name_len = data->unitSize;
+
+		data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_MESSAGE_TYPES);
+		if (!data) {
+			error("mce: cannot get mas instance msg type");
+			continue;
+		}
+
+		msg_type = data->val.uint8;
+
+		if (sdp_get_access_protos(rec, &protos) < 0) {
+			error("mce: cannot get mas instance sdp protocol list");
+			continue;
+		}
+
+		scn = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+		sdp_list_free(protos, NULL);
+
+		if (!scn) {
+			error("mce: cannot get mas instance rfcomm channel");
+			continue;
+		}
+
+		size += fill_mce_inst(buf + size, id, scn, msg_type, name,
+								name_len);
+		num_instances++;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+fail:
+	ev->num_instances = num_instances;
+	ev->status = status;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT,
+			HAL_EV_MAP_CLIENT_REMOTE_MAS_INSTANCES, size, buf);
+}
+
+static void handle_get_instances(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_map_client_get_instances *cmd = buf;
+	uint8_t status;
+	bdaddr_t *dst;
+	uuid_t uuid;
+
+	DBG("");
+
+	dst = new0(bdaddr_t, 1);
+	if (!dst) {
+		error("mce: Fail to allocate cb data");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, dst);
+	sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID);
+
+	if (bt_search_service(&adapter_addr, dst, &uuid,
+				map_client_sdp_search_cb, dst, free, 0)) {
+		error("mce: Failed to search SDP details");
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT,
+				HAL_OP_MAP_CLIENT_GET_INSTANCES, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_MAP_CLIENT_GET_INSTANCES */
+	{ handle_get_instances, false,
+			sizeof(struct hal_cmd_map_client_get_instances) },
+};
+
+bool bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	hal_ipc = ipc;
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_map_client_unregister(void)
+{
+	DBG("");
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_MAP_CLIENT);
+	hal_ipc = NULL;
+}
diff --git a/bluez/repo/android/map-client.h b/bluez/repo/android/map-client.h
new file mode 100644
index 0000000..0e63072
--- /dev/null
+++ b/bluez/repo/android/map-client.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_map_client_register(struct ipc *ipc, const bdaddr_t *addr,
+								uint8_t mode);
+void bt_map_client_unregister(void);
diff --git a/bluez/repo/android/pan.c b/bluez/repo/android/pan.c
new file mode 100644
index 0000000..c40a6d3
--- /dev/null
+++ b/bluez/repo/android/pan.c
@@ -0,0 +1,903 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <linux/if_bridge.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/uuid-helper.h"
+#include "profiles/network/bnep.h"
+#include "src/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "pan.h"
+
+#define SVC_HINT_NETWORKING 0x02
+
+#define BNEP_BRIDGE "bt-pan"
+#define BNEP_PANU_INTERFACE "bt-pan"
+#define BNEP_NAP_INTERFACE "bt-pan%d"
+
+struct pan_device {
+	char		iface[16];
+	bdaddr_t	dst;
+	uint8_t		conn_state;
+	uint8_t		role;
+	GIOChannel	*io;
+	struct bnep	*session;
+	guint		watch;
+};
+
+static bdaddr_t adapter_addr;
+static GSList *devices = NULL;
+static uint8_t local_role = HAL_PAN_ROLE_NONE;
+static uint32_t nap_rec_id = 0;
+static uint32_t panu_rec_id = 0;
+static GIOChannel *nap_io = NULL;
+static bool nap_bridge_mode = false;
+static struct ipc *hal_ipc = NULL;
+
+static int set_forward_delay(int sk)
+{
+	unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 };
+	struct ifreq ifr;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ - 1);
+	ifr.ifr_data = (char *) args;
+
+	if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+		error("pan: setting forward delay failed: %d (%s)",
+							errno, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int nap_create_bridge(void)
+{
+	int sk, err;
+
+	DBG("%s", BNEP_BRIDGE);
+
+	if (nap_bridge_mode)
+		return 0;
+
+	sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (sk < 0)
+		return -EOPNOTSUPP;
+
+	if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) {
+		err = -errno;
+		if (err != -EEXIST) {
+			close(sk);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	err = set_forward_delay(sk);
+	if (err < 0)
+		ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+
+	close(sk);
+
+	nap_bridge_mode = err == 0;
+
+	return err;
+}
+
+static int bridge_if_down(void)
+{
+	struct ifreq ifr;
+	int sk, err;
+
+	sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1);
+
+	ifr.ifr_flags &= ~IFF_UP;
+
+	/* Bring down the interface */
+	err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+	close(sk);
+
+	if (err < 0) {
+		error("pan: Could not bring down %s", BNEP_BRIDGE);
+		return err;
+	}
+
+	return 0;
+}
+
+static int nap_remove_bridge(void)
+{
+	int sk, err;
+
+	DBG("%s", BNEP_BRIDGE);
+
+	if (!nap_bridge_mode)
+		return 0;
+
+	bridge_if_down();
+
+	sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (sk < 0)
+		return -EOPNOTSUPP;
+
+	err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+	if (err < 0)
+		err = -errno;
+
+	close(sk);
+
+	if (err < 0)
+		return err;
+
+	nap_bridge_mode = false;
+
+	return 0;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct pan_device *dev = s;
+	const bdaddr_t *dst = user_data;
+
+	return bacmp(&dev->dst, dst);
+}
+
+static void pan_device_free(void *data)
+{
+	struct pan_device *dev = data;
+
+	if (dev->watch > 0) {
+		bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst);
+		g_source_remove(dev->watch);
+	}
+
+	if (dev->io) {
+		g_io_channel_shutdown(dev->io, FALSE, NULL);
+		g_io_channel_unref(dev->io);
+	}
+
+	if (dev->session)
+		bnep_free(dev->session);
+
+	g_free(dev);
+}
+
+static void pan_device_remove(struct pan_device *dev)
+{
+	devices = g_slist_remove(devices, dev);
+
+	if (g_slist_length(devices) == 0) {
+		local_role = HAL_PAN_ROLE_NONE;
+		nap_remove_bridge();
+	}
+
+	pan_device_free(dev);
+}
+
+static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state)
+{
+	struct hal_ev_pan_conn_state ev;
+	char addr[18];
+
+	if (dev->conn_state == state)
+		return;
+
+	dev->conn_state = state;
+	ba2str(&dev->dst, addr);
+	DBG("device %s state %u", addr, state);
+
+	bdaddr2android(&dev->dst, ev.bdaddr);
+	ev.state = state;
+	ev.local_role = local_role;
+	ev.remote_role = dev->role;
+	ev.status = HAL_STATUS_SUCCESS;
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE,
+							sizeof(ev), &ev);
+	if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED)
+		pan_device_remove(dev);
+}
+
+static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state,
+								uint8_t status)
+{
+	struct hal_ev_pan_ctrl_state ev;
+
+	DBG("");
+
+	ev.state = state;
+	ev.local_role = local_role;
+	ev.status = status;
+
+	memset(ev.name, 0, sizeof(ev.name));
+
+	if (local_role == HAL_PAN_ROLE_NAP)
+		memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE));
+	else if (local_role == HAL_PAN_ROLE_PANU)
+		memcpy(ev.name, dev->iface, sizeof(dev->iface));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE,
+							sizeof(ev), &ev);
+}
+
+static void bnep_disconn_cb(void *data)
+{
+	struct pan_device *dev = data;
+
+	DBG("%s disconnected", dev->iface);
+
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void bnep_conn_cb(char *iface, int err, void *data)
+{
+	struct pan_device *dev = data;
+
+	DBG("");
+
+	if (err < 0) {
+		error("bnep connect req failed: %s", strerror(-err));
+		bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+		return;
+	}
+
+	memcpy(dev->iface, iface, sizeof(dev->iface));
+
+	DBG("%s connected", dev->iface);
+
+	bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	struct pan_device *dev = data;
+	uint16_t l_role, r_role;
+	int perr, sk;
+
+	DBG("");
+
+	if (err) {
+		error("%s", err->message);
+		goto fail;
+	}
+
+	l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP :
+								BNEP_SVC_PANU;
+	r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU;
+
+	sk = g_io_channel_unix_get_fd(dev->io);
+
+	dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE);
+	if (!dev->session)
+		goto fail;
+
+	perr = bnep_connect(dev->session, bnep_conn_cb, bnep_disconn_cb, dev,
+									dev);
+	if (perr < 0) {
+		error("bnep connect req failed: %s", strerror(-perr));
+		goto fail;
+	}
+
+	if (dev->io) {
+		g_io_channel_unref(dev->io);
+		dev->io = NULL;
+	}
+
+	return;
+
+fail:
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void bt_pan_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_connect *cmd = buf;
+	struct pan_device *dev;
+	uint8_t status;
+	bdaddr_t dst;
+	char addr[18];
+	GSList *l;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	switch (cmd->local_role) {
+	case HAL_PAN_ROLE_NAP:
+		if (cmd->remote_role != HAL_PAN_ROLE_PANU) {
+			status = HAL_STATUS_UNSUPPORTED;
+			goto failed;
+		}
+		break;
+	case HAL_PAN_ROLE_PANU:
+		if (cmd->remote_role != HAL_PAN_ROLE_NAP &&
+					cmd->remote_role != HAL_PAN_ROLE_PANU) {
+			status = HAL_STATUS_UNSUPPORTED;
+			goto failed;
+		}
+		break;
+	default:
+		status = HAL_STATUS_UNSUPPORTED;
+		goto failed;
+	}
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = g_new0(struct pan_device, 1);
+	bacpy(&dev->dst, &dst);
+	local_role = cmd->local_role;
+	dev->role = cmd->remote_role;
+
+	ba2str(&dev->dst, addr);
+	DBG("connecting to %s %s", addr, dev->iface);
+
+	dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_DEST_BDADDR, &dev->dst,
+					BT_IO_OPT_PSM, BNEP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_OMTU, BNEP_MTU,
+					BT_IO_OPT_IMTU, BNEP_MTU,
+					BT_IO_OPT_INVALID);
+	if (!dev->io) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_free(dev);
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	devices = g_slist_append(devices, dev);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status);
+}
+
+static void bt_pan_disconnect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_disconnect *cmd = buf;
+	struct pan_device *dev;
+	uint8_t status;
+	GSList *l;
+	bdaddr_t dst;
+
+	DBG("");
+
+	android2bdaddr(&cmd->bdaddr, &dst);
+
+	l = g_slist_find_custom(devices, &dst, device_cmp);
+	if (!l) {
+		status = HAL_STATUS_FAILED;
+		goto failed;
+	}
+
+	dev = l->data;
+
+	if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session)
+		bnep_disconnect(dev->session);
+
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+	status = HAL_STATUS_SUCCESS;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+									status);
+}
+
+static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct pan_device *dev = user_data;
+
+	DBG("disconnected");
+
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+
+	return FALSE;
+}
+
+static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct pan_device *dev = user_data;
+	uint8_t packet[BNEP_MTU];
+	int sk, n, err;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		error("Hangup or error or inval on BNEP socket");
+		return FALSE;
+	}
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	/*
+	 * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+	 * of kernel setup connection msg handling.
+	 */
+	n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+	if (n  < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		goto failed;
+	}
+
+	if (n < 3) {
+		error("pan: to few setup connection request data received");
+		goto failed;
+	}
+
+	err = nap_create_bridge();
+	if (err < 0)
+		error("pan: Failed to create bridge: %s (%d)", strerror(-err),
+									-err);
+
+	if (bnep_server_add(sk, (err < 0) ? NULL : BNEP_BRIDGE, dev->iface,
+						&dev->dst, packet, n) < 0) {
+		error("pan: server_connadd failed");
+		goto failed;
+	}
+
+	dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							nap_watchdog_cb, dev);
+	g_io_channel_unref(dev->io);
+	dev->io = NULL;
+
+	bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+
+	return FALSE;
+
+failed:
+	pan_device_remove(dev);
+
+	return FALSE;
+}
+
+static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct pan_device *dev = user_data;
+
+	DBG("");
+
+	if (err) {
+		error("%s", err->message);
+		bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, TRUE);
+	dev->watch = g_io_add_watch(chan,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				nap_setup_cb, dev);
+}
+
+static void nap_confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct pan_device *dev;
+	bdaddr_t dst;
+	char address[18];
+	GError *err = NULL;
+
+	DBG("");
+
+	bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return;
+	}
+
+	DBG("incoming connect request from %s", address);
+	dev = g_new0(struct pan_device, 1);
+	bacpy(&dev->dst, &dst);
+	local_role = HAL_PAN_ROLE_NAP;
+	dev->role = HAL_PAN_ROLE_PANU;
+
+	strncpy(dev->iface, BNEP_NAP_INTERFACE, 16);
+	dev->iface[15] = '\0';
+
+	dev->io = g_io_channel_ref(chan);
+	g_io_channel_set_close_on_unref(dev->io, TRUE);
+
+	if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	devices = g_slist_append(devices, dev);
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+	return;
+
+failed:
+	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void destroy_nap_device(void)
+{
+	DBG("");
+
+	nap_remove_bridge();
+
+	if (nap_io) {
+		g_io_channel_shutdown(nap_io, FALSE, NULL);
+		g_io_channel_unref(nap_io);
+		nap_io = NULL;
+	}
+}
+
+static int register_nap_server(void)
+{
+	GError *gerr = NULL;
+
+	DBG("");
+
+	nap_io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_PSM, BNEP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_OMTU, BNEP_MTU,
+					BT_IO_OPT_IMTU, BNEP_MTU,
+					BT_IO_OPT_INVALID);
+
+	if (!nap_io) {
+		destroy_nap_device();
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void bt_pan_enable(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_pan_enable *cmd = buf;
+	uint8_t status, state;
+	int err;
+
+	DBG("");
+
+	if (local_role == cmd->local_role) {
+		status = HAL_STATUS_SUCCESS;
+		goto reply;
+	}
+
+	/* destroy existing server */
+	destroy_nap_device();
+
+	switch (cmd->local_role) {
+	case HAL_PAN_ROLE_NAP:
+		break;
+	case HAL_PAN_ROLE_NONE:
+		local_role = HAL_PAN_ROLE_NONE;
+		status = HAL_STATUS_SUCCESS;
+		state = HAL_PAN_CTRL_DISABLED;
+		goto notify;
+	default:
+		status = HAL_STATUS_UNSUPPORTED;
+		goto reply;
+	}
+
+	local_role = cmd->local_role;
+	err = register_nap_server();
+	if (err < 0) {
+		status = HAL_STATUS_FAILED;
+		destroy_nap_device();
+		goto reply;
+	}
+
+	status = HAL_STATUS_SUCCESS;
+	state = HAL_PAN_CTRL_ENABLED;
+
+notify:
+	bt_pan_notify_ctrl_state(NULL, state, status);
+
+reply:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status);
+}
+
+static void bt_pan_get_role(const void *buf, uint16_t len)
+{
+	struct hal_rsp_pan_get_role rsp;
+
+	DBG("");
+
+	rsp.local_role = local_role;
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE,
+							sizeof(rsp), &rsp, -1);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_PAN_ENABLE */
+	{ bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) },
+	/* HAL_OP_PAN_GET_ROLE */
+	{ bt_pan_get_role, false, 0 },
+	/* HAL_OP_PAN_CONNECT */
+	{ bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) },
+	/* HAL_OP_PAN_DISCONNECT */
+	{ bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) },
+};
+
+static sdp_record_t *nap_record(void)
+{
+	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+	uuid_t root_uuid, nap, l2cap, bnep;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *proto[2];
+	sdp_data_t *v, *p;
+	uint16_t psm = BNEP_PSM, version = 0x0100;
+	uint16_t security = 0x0001, type = 0xfffe;
+	uint32_t rate = 0;
+	const char *desc = "Network Access Point", *name = "Network Service";
+	sdp_record_t *record;
+	uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806,  /* ARP */ };
+	sdp_data_t *head, *pseq, *data;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->attrlist = NULL;
+	record->pattern = NULL;
+
+	sdp_uuid16_create(&nap, NAP_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &nap);
+	sdp_set_service_classes(record, svclass);
+
+	sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+	sdp_set_info_attr(record, name, NULL, desc);
+	sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+	sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+							SDP_UINT32, &rate);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	p = sdp_data_alloc(SDP_UINT16, &psm);
+	proto[0] = sdp_list_append(proto[0], p);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep);
+	v = sdp_data_alloc(SDP_UINT16, &version);
+	proto[1] = sdp_list_append(proto[1], v);
+
+	head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+	data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+	sdp_seq_append(head, data);
+
+	pseq = sdp_data_alloc(SDP_SEQ16, head);
+	proto[1] = sdp_list_append(proto[1], pseq);
+	apseq = sdp_list_append(apseq, proto[1]);
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+	sdp_add_lang_attr(record);
+	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+	sdp_data_free(p);
+	sdp_data_free(v);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(svclass, NULL);
+	sdp_list_free(pfseq, NULL);
+
+	return record;
+}
+
+static sdp_record_t *panu_record(void)
+{
+	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+	uuid_t root_uuid, panu, l2cap, bnep;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *proto[2];
+	sdp_data_t *v, *p;
+	uint16_t psm = BNEP_PSM, version = 0x0100;
+	uint16_t security = 0x0001, type = 0xfffe;
+	uint32_t rate = 0;
+	const char *desc = "PAN User", *name = "Network Service";
+	sdp_record_t *record;
+	uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806,  /* ARP */ };
+	sdp_data_t *head, *pseq, *data;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->attrlist = NULL;
+	record->pattern = NULL;
+
+	sdp_uuid16_create(&panu, PANU_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &panu);
+	sdp_set_service_classes(record, svclass);
+
+	sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+	sdp_set_info_attr(record, name, NULL, desc);
+	sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+	sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+							SDP_UINT32, &rate);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	p = sdp_data_alloc(SDP_UINT16, &psm);
+	proto[0] = sdp_list_append(proto[0], p);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep);
+	v = sdp_data_alloc(SDP_UINT16, &version);
+	proto[1] = sdp_list_append(proto[1], v);
+
+	head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+	data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+	sdp_seq_append(head, data);
+
+	pseq = sdp_data_alloc(SDP_SEQ16, head);
+	proto[1] = sdp_list_append(proto[1], pseq);
+	apseq = sdp_list_append(apseq, proto[1]);
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+	sdp_add_lang_attr(record);
+	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+	sdp_data_free(p);
+	sdp_data_free(v);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(svclass, NULL);
+	sdp_list_free(pfseq, NULL);
+
+	return record;
+}
+
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	sdp_record_t *nap_rec, *panu_rec;
+	int err;
+
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	nap_rec = nap_record();
+	if (bt_adapter_add_record(nap_rec, SVC_HINT_NETWORKING) < 0) {
+		sdp_record_free(nap_rec);
+		error("Failed to allocate PAN-NAP sdp record");
+		return false;
+	}
+
+	panu_rec = panu_record();
+	if (bt_adapter_add_record(panu_rec, SVC_HINT_NETWORKING) < 0) {
+		sdp_record_free(nap_rec);
+		sdp_record_free(panu_rec);
+		error("Failed to allocate PAN-PANU sdp record");
+		return false;
+	}
+
+	err = bnep_init();
+	if (err < 0) {
+		error("Failed to init BNEP");
+		bt_adapter_remove_record(nap_rec->handle);
+		bt_adapter_remove_record(panu_rec->handle);
+		return false;
+	}
+
+	err = register_nap_server();
+	if (err < 0) {
+		error("Failed to register NAP server");
+		bt_adapter_remove_record(nap_rec->handle);
+		bt_adapter_remove_record(panu_rec->handle);
+		bnep_cleanup();
+		return false;
+	}
+
+	nap_rec_id = nap_rec->handle;
+	panu_rec_id = panu_rec->handle;
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
+}
+
+void bt_pan_unregister(void)
+{
+	DBG("");
+
+	g_slist_free_full(devices, pan_device_free);
+	devices = NULL;
+	local_role = HAL_PAN_ROLE_NONE;
+
+	bnep_cleanup();
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN);
+	hal_ipc = NULL;
+
+	bt_adapter_remove_record(nap_rec_id);
+	nap_rec_id = 0;
+	bt_adapter_remove_record(panu_rec_id);
+	panu_rec_id = 0;
+	destroy_nap_device();
+}
diff --git a/bluez/repo/android/pan.h b/bluez/repo/android/pan.h
new file mode 100644
index 0000000..cfbea96
--- /dev/null
+++ b/bluez/repo/android/pan.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_pan_unregister(void);
diff --git a/bluez/repo/android/pics-a2dp.txt b/bluez/repo/android/pics-a2dp.txt
new file mode 100644
index 0000000..2e87104
--- /dev/null
+++ b/bluez/repo/android/pics-a2dp.txt
@@ -0,0 +1,162 @@
+A2DP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_0_1	False		A2DP 1.0 (C.1)
+TSPC_A2DP_0_2	False		A2DP 1.2 (C.1)
+TSPC_A2DP_0_3	True (*)	A2DP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_1_1	True (*)	Role: Source (C.1)
+TSPC_A2DP_1_2	False		Role: Sink (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		A2DP SRC Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_2_1	True		SRC: Initiate connection establishment (M)
+TSPC_A2DP_2_2	True		SRC: Accept connection establishment (M)
+TSPC_A2DP_2_3	True		SRC: Initiate start streaming (M)
+TSPC_A2DP_2_4	True		SRC: Accept start streaming (M)
+TSPC_A2DP_2_5	True		SRC: Send audio stream (M)
+TSPC_A2DP_2_6	True		SRC: Initiate connection release (M)
+TSPC_A2DP_2_7	True		SRC: Accept connection release (M)
+TSPC_A2DP_2_8	True (*)	SRC: Initiate suspend (O)
+TSPC_A2DP_2_9	True (*)	SRC: Accept suspend (O)
+TSPC_A2DP_2_10	True		SRC: SBC encoder (M)
+TSPC_A2DP_2_10a	False		SRC: Encode and Forward Audio Stream (O)
+TSPC_A2DP_2_11	False		SRC: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_2_12	False		SRC: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_2_13	True (*)	SRC: SBC Configurations in 44.1 KHz sampling
+					(C.1)
+TSPC_A2DP_2_14	True (*)	SRC: SBC Configurations in 48 KHz sampling (C.1)
+TSPC_A2DP_2_15	False		SRC: Delay Reporting (C.2)
+TSPC_A2DP_2_16	False		SRC: SRC video playback via Bluetooth VDP (C.3)
+TSPC_A2DP_2_17	False		SRC: SRC video playback on a local video
+					display (C.3)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+C.2: Mandatory if A2DP 0/3 AND (2/16 OR 2/17) is supported, otherwise excluded.
+C.3: Optional to support if A2DP 0/3 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Codecs in SRC
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3_1	True		SRC: SBC encoder (M)
+TSPC_A2DP_3_1a	False		SRC: Encode and Forward SBC Audio Stream (O)
+TSPC_A2DP_3_2	False		SRC: Optional codec (O)
+TSPC_A2DP_3_3	False		SRC: MPEG-1,2 Audio decoder (C.1)
+TSPC_A2DP_3_4	False		SRC: MPEG-1,2 Audio encoder (C.1)
+TSPC_A2DP_3_5	False		SRC: MPEG-2,4 AAC decoder (C.1)
+TSPC_A2DP_3_6	False		SRC: MPEG-2,4 AAC encoder (C.1)
+TSPC_A2DP_3_7	False		SRC: ATRAC family decoder (C.1)
+TSPC_A2DP_3_8	False		SRC: ATRAC family encoder (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of the implementations shall be supported if 3/2
+	is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Codec Features in SRC
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3a_1	True 		SRC: Channel Mode - Mono (M)
+TSPC_A2DP_3a_2	True (*)	SRC: Channel Mode - Dual Channel (C.1)
+TSPC_A2DP_3a_3	True (*)	SRC: Channel Mode - Stereo (C.1)
+TSPC_A2DP_3a_4	True (*)	SRC: Channel Mode - Joint Stereo (C.1)
+TSPC_A2DP_3a_5	True		SRC: Block Length 4 (M)
+TSPC_A2DP_3a_6	True		SRC: Block Length 8 (M)
+TSPC_A2DP_3a_7	True		SRC: Block Length 12 (M)
+TSPC_A2DP_3a_8	True		SRC: Block Length 16 (M)
+TSPC_A2DP_3a_9	True (*)	SRC: Subbands - 4 (O)
+TSPC_A2DP_3a_10	True		SRC: Subbands - 8 (M)
+TSPC_A2DP_3a_11	True (*)	SRC: Allocation - SNR (O)
+TSPC_A2DP_3a_12	True		SRC: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+-------------------------------------------------------------------------------
+
+
+		A2DP Sink Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_4_1	False		SNK: Initiate connection establishment (O)
+TSPC_A2DP_4_2	False (*)	SNK: Accept connection establishment (M)
+TSPC_A2DP_4_3	False		SNK: Initiate start streaming (O)
+TSPC_A2DP_4_4	False (*)	SNK: Accept start streaming (M)
+TSPC_A2DP_4_5	False (*)	SNK: Receive audio stream (M)
+TSPC_A2DP_4_6	False		SNK: Initiate connection release (O)
+TSPC_A2DP_4_7	False (*)	SNK: Accept connection release (M)
+TSPC_A2DP_4_8	False		SNK: Initiate suspend (O)
+TSPC_A2DP_4_9	False		SNK: Accept suspend (O)
+TSPC_A2DP_4_10	False (*)	SNK: SBC decoder (M)
+TSPC_A2DP_4_10a	False		SNK: Decode and Forward Audio Stream (O)
+TSPC_A2DP_4_11	False		SNK: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_4_12	False		SNK: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_4_13	False (*)	SNK: SBC Configurations in 44.1 KHz sampling (M)
+TSPC_A2DP_4_14	False (*)	SNK: SBC Configurations in 48 KHz sampling (M)
+TSPC_A2DP_4_15	False		SNK: Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if A2DP 0/3 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported codecs in SNK
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5_1	False (*)	SNK: SBC decoder (M)
+TSPC_A2DP_5_1a	False		SNK: Decode and Forward SBC Audio Stream (O)
+TSPC_A2DP_5_2	False		SNK: Optional codec decoder (O)
+TSPC_A2DP_5_3	False		SNK: MPEG-1,2 Audio (C.1)
+TSPC_A2DP_5_4	False		SNK: MPEG-2,4 AAC (C.1)
+TSPC_A2DP_5_5	False		SNK: ATRAC family (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one codec shall be supported if Table 5/2 is supported, otherwise
+	excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Codec Features in SNK
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5a_1	False (*)	SNK: Channel Mode - Mono (M)
+TSPC_A2DP_5a_2	False (*)	SNK: Channel Mode - Dual Channel (M)
+TSPC_A2DP_5a_3	False (*)	SNK: Channel Mode - Stereo (M)
+TSPC_A2DP_5a_4	False (*)	SNK: Channel Mode - Joint Stereo (M)
+TSPC_A2DP_5a_5	False (*)	SNK: Block Length 4 (M)
+TSPC_A2DP_5a_6	False (*)	SNK: Block Length 8 (M)
+TSPC_A2DP_5a_7	False (*)	SNK: Block Length 12 (M)
+TSPC_A2DP_5a_8	False (*)	SNK: Block Length 16 (M)
+TSPC_A2DP_5a_9	False (*)	SNK: Subbands - 4 (M)
+TSPC_A2DP_5a_10	False (*)	SNK: Subbands - 8 (M)
+TSPC_A2DP_5a_11	False (*)	SNK: Allocation - SNR (M)
+TSPC_A2DP_5a_12	False (*)	SNK: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-avctp.txt b/bluez/repo/android/pics-avctp.txt
new file mode 100644
index 0000000..3447962
--- /dev/null
+++ b/bluez/repo/android/pics-avctp.txt
@@ -0,0 +1,75 @@
+AVCTP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Protocol Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_0_1	False		AVCTP 1.0 (C.1)
+TSPC_AVCTP_0_2	False		AVCTP 1.2 (C.1)
+TSPC_AVCTP_0_3	False		AVCTP 1.3 (C.1)
+TSPC_AVCTP_0_4	True (*)	AVCTP 1.4 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Protocol Version.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_1_1	True (*)	Controller (C.1)
+TSPC_AVCTP_1_2	True (*)	Target (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Controller Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_2_1	False		Message fragmentation (O)
+TSPC_AVCTP_2_2	True		Transaction label management (M)
+TSPC_AVCTP_2_3	True		Packet type field management (M)
+TSPC_AVCTP_2_4	True		Message type field management (M)
+TSPC_AVCTP_2_5	True		PID field management (M)
+TSPC_AVCTP_2_6	True		IPID field mangement (M)
+TSPC_AVCTP_2_7	True		Message information management (M)
+TSPC_AVCTP_2_8	False		Event registration for message reception (O)
+TSPC_AVCTP_2_9	False		Event registration for connection request (O)
+TSPC_AVCTP_2_10	False		Event registration for disconnection (O)
+TSPC_AVCTP_2_11	False		Connect request (O)
+TSPC_AVCTP_2_12	False		Disconnect request (O)
+TSPC_AVCTP_2_13	False		Send message (O)
+TSPC_AVCTP_2_14	False		Support for multiple AVCTP channel establishment
+					(O)
+-------------------------------------------------------------------------------
+
+
+		Target Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_3_1	False		Message fragmentation (O)
+TSPC_AVCTP_3_2	True		Transaction label management (M)
+TSPC_AVCTP_3_3	True		Packet type field management (M)
+TSPC_AVCTP_3_4	True		Message type field management (M)
+TSPC_AVCTP_3_5	True		PID field management (M)
+TSPC_AVCTP_3_6	True		IPID field management (M)
+TSPC_AVCTP_3_7	True		Message information management (M)
+TSPC_AVCTP_3_8	True (*)	Event registration for message reception (O)
+TSPC_AVCTP_3_9	True (*)	Event registration for connection request (O)
+TSPC_AVCTP_3_10	True (*)	Event registration for disconnection request (O)
+TSPC_AVCTP_3_11	True (*)	Connect request (O)
+TSPC_AVCTP_3_12	True (*)	Disconnect request (O)
+TSPC_AVCTP_3_13	True (*)	Send message (O)
+TSPC_AVCTP_ALL	False		Enables all test cases when set to TRUE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-avdtp.txt b/bluez/repo/android/pics-avdtp.txt
new file mode 100644
index 0000000..c0c5529
--- /dev/null
+++ b/bluez/repo/android/pics-avdtp.txt
@@ -0,0 +1,236 @@
+AVDTP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Versions
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_0_1	False		AVDTP 1.0 (C.1)
+TSPC_AVDTP_0_2	False		AVDTP 1.2 (C.1)
+TSPC_AVDTP_0_3	True (*)	AVDTP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select only one of the protocol versions.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_1_1	True (*)	Source (C.1)
+TSPC_AVDTP_1_2	True (*)	Sink (C.1)
+TSPC_AVDTP_1_3	True (*)	Initiator (C.2)
+TSPC_AVDTP_1_4	True (*)	Acceptor (C.2)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+C.2: It is within the scope of profiles using the AVDTP specification to
+	mandate Initiator/Acceptor capabilities. It is mandatory to support at
+	least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Signaling Message Format (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_2_1	True		Transaction label (M)
+TSPC_AVDTP_2_2	True		Packet type (M)
+TSPC_AVDTP_2_3	True		Message type (M)
+TSPC_AVDTP_2_4	True		Signal identifier (M)
+-------------------------------------------------------------------------------
+
+
+		Signaling Channel Establishment/Disconnection (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_3_1	True (*)	Establish signaling channel (O)
+TSPC_AVDTP_3_2	True (*)	Disconnect signaling channel (O)
+-------------------------------------------------------------------------------
+
+
+		Stream Discovery and Configuration (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_4_1	True (*)	Stream discover command (O)
+TSPC_AVDTP_4_2	True (*)	Stream get capabilities command (C.2)
+TSPC_AVDTP_4_3	True (*)	Set configuration command (O)
+TSPC_AVDTP_4_4	True (*)	Get configuration command (O)
+TSPC_AVDTP_4_5	False		Reconfigure command (O)
+TSPC_AVDTP_4_6	True (*)	Stream get all capabilities command (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise
+	excluded.
+C.2: Mandatory to support if TSPC_AVDTP_4_6 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Stream Establishment, Suspension and Release (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_5_1	True (*)	Open stream command (O)
+TSPC_AVDTP_5_2	True (*)	Start stream command (O)
+TSPC_AVDTP_5_3	True (*)	Close stream command (O)
+TSPC_AVDTP_5_4	True (*)	Suspend command (O)
+TSPC_AVDTP_5_5	True (*)	Abort stream command (O)
+-------------------------------------------------------------------------------
+
+
+		Security Signaling (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_6_1	False		Content security control command (O)
+-------------------------------------------------------------------------------
+
+
+		Message Fragmentation (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_7_1	True		Signaling message fragmentation (M)
+-------------------------------------------------------------------------------
+
+
+		Signaling Message Format (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_8_1	True		Transaction label (M)
+TSPC_AVDTP_8_2	True		Packet type (M)
+TSPC_AVDTP_8_3	True		Message type (M)
+TSPC_AVDTP_8_4	True		Signal identifier (M)
+-------------------------------------------------------------------------------
+
+
+		Signaling Channel Establishment/Disconnection (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_9_1	True (*)	Establish signaling channel (O)
+TSPC_AVDTP_9_2	True (*)	Disconnect signaling channel (O)
+-------------------------------------------------------------------------------
+
+
+		Stream Discovery and Configuration (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_10_1	True (*)	Stream discover response (O)
+TSPC_AVDTP_10_2	True (*)	Stream get capabilities response (C.2)
+TSPC_AVDTP_10_3	True (*)	Set configuration response (O)
+TSPC_AVDTP_10_4	True (*)	Get configuration response (O)
+TSPC_AVDTP_10_5 False		Reconfigure response (O)
+TSPC_AVDTP_10_6	True (*)	Stream get all capabilities response (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, otherwise
+	excluded.
+C.2: It is Mandatory to support if TSPC_AVDTP_10_6 is supported, otherwise
+	Optional.
+-------------------------------------------------------------------------------
+
+
+		Stream Establishment, Suspension and Release (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_11_1	True (*)	Open stream response (O)
+TSPC_AVDTP_11_2	True (*)	Start stream response (O)
+TSPC_AVDTP_11_3	True (*)	Close stream response (O)
+TSPC_AVDTP_11_4	True (*)	Suspend response (O)
+TSPC_AVDTP_11_5	True (*)	Abort stream response (O)
+TSPC_AVDTP_11_6	True (*)	General reject message (O)
+-------------------------------------------------------------------------------
+
+
+		Security Signaling (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_12_1	False		Content security control response (O)
+-------------------------------------------------------------------------------
+
+
+		Message Fragmentation (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_13_1	True		Signaling message fragmentation (M)
+-------------------------------------------------------------------------------
+
+
+		Source Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_14_1	True		Basic transport service support (M)
+TSPC_AVDTP_14_2	False		Reporting service support (O)
+TSPC_AVDTP_14_3	False		Recovery service support (O)
+TSPC_AVDTP_14_4	False		Multiplexing service support (O)
+TSPC_AVDTP_14_5	False		Robust header compression service support (O)
+TSPC_AVDTP_14_6	True (*)	Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Sink Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_15_1	True		Basic transport service support (M)
+TSPC_AVDTP_15_2	False		Reporting service support (O)
+TSPC_AVDTP_15_3	False		Recovery service support (O)
+TSPC_AVDTP_15_4	False		Multiplexing service support (O)
+TSPC_AVDTP_15_5	False		Robust header compression service support (O)
+TSPC_AVDTP_15_6	True (*)	Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: It is optional to support if TSPC_AVDTP_0_3 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Message Error Handling Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_16_1	False		Reporting Capability Error (C.1)
+TSPC_AVDTP_16_2	False		Reject Corrupted Messages (C.2)
+TSPC_AVDTP_16_3	True (*)	General Reject Response Includes Signal ID (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_AVDTP_0_2 or TSPC_AVDTP_0_3 supported, excluded
+	otherwise.
+C.2: Optional, excluded if TSPC_AVDTP_16_3 (General Reject Response Includes
+	Signal ID) is supported.
+C.3: Mandatory if TSPC_AVDTP_0_3 supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Upper Test Interface
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_17_1	False		Upper Test Interface provided (O)
+-------------------------------------------------------------------------------
+
+
+		L2CAP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVDTP_18_1	False		Enhanced Retransmission Mode preferred for
+				signaling channel (O)
+TSPC_AVDTP_18_2	False		Streaming Mode preferred for Media Transport
+				channel (O)
+TSPC_AVDTP_18_3	False		FCS Option (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_AVDTP_18_1 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-avrcp.txt b/bluez/repo/android/pics-avrcp.txt
new file mode 100644
index 0000000..7bd68fa
--- /dev/null
+++ b/bluez/repo/android/pics-avrcp.txt
@@ -0,0 +1,644 @@
+AVRCP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_1_1    True (*)	Role: Controller (CT) (C.1)
+TSPC_AVRCP_1_2    True (*)	Role: Target (TG) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Controller Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2_1    False (*)	CT: Initiating connection establishment (M)
+TSPC_AVRCP_2_2    False (*)	CT: Accepting connection establishment for
+						control initiated by TG (M)
+TSPC_AVRCP_2_3    False (*)	CT: Initiating connection release (M)
+TSPC_AVRCP_2_4    False (*)	CT: Accepting connection release for control
+							initiated by TG (M)
+TSPC_AVRCP_2_5    False		CT: Sending UNIT INFO (O)
+TSPC_AVRCP_2_6    False		CT: Sending SUBUNIT INFO (O)
+TSPC_AVRCP_2_7    False		CT: Sending PASS THROUGH command category 1
+					(C.1)
+TSPC_AVRCP_2_8    False		CT: Sending PASS THROUGH command category 2
+					(C.1)
+TSPC_AVRCP_2_9    False		CT: Sending PASS THROUGH command category 3
+					(C.1)
+TSPC_AVRCP_2_10   False		CT: Sending PASS THROUGH command category 4
+					(C.1)
+TSPC_AVRCP_2_11   False		CT: Get Capabilities (O)
+TSPC_AVRCP_2_12   False		CT: List Player Application Setting
+					Attributes (C.9)
+TSPC_AVRCP_2_13   False		CT: List Player Application Setting Values (O)
+TSPC_AVRCP_2_14   False		CT: Get Current Player Application Setting
+					(C.10)
+TSPC_AVRCP_2_15   False		CT: Set Player Application Setting Value (C.10)
+TSPC_AVRCP_2_16   False		CT: Get Player Application Setting
+						Attribute Text (O)
+TSPC_AVRCP_2_17   False		CT: Get Player Application Setting Value Text
+					(O)
+TSPC_AVRCP_2_18   False		CT: Inform Displayable Character Set (O)
+TSPC_AVRCP_2_19   False		CT: Inform Battery Status of CT (O)
+TSPC_AVRCP_2_20   False		CT: Get Element Attributes (O)
+TSPC_AVRCP_2_21   False		CT: Get Play Status (O)
+TSPC_AVRCP_2_22   False		CT: Register Notification (C.11)
+TSPC_AVRCP_2_23   False		CT: Request Continuing Response (C.2)
+TSPC_AVRCP_2_24   False		CT: Abort Continuing Response (C.2)
+TSPC_AVRCP_2_25   False		CT: Next Group (C.12)
+TSPC_AVRCP_2_26   False		CT: Previous Group (C.12)
+TSPC_AVRCP_2_27   False		CT: Media Player Selection (O)
+TSPC_AVRCP_2_28   False		CT: SetAddressedPlayer (O)
+TSPC_AVRCP_2_29   False		CT: GetFolderItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_29b  False		CT: GetTotalNumberOfItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_30   False		CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O)
+TSPC_AVRCP_2_31   False		CT: EVENT_ADDRESSED_PLAYER_CHANGED (O)
+TSPC_AVRCP_2_32   False		CT: Browsing (O)
+TSPC_AVRCP_2_33   False		CT: SetBrowsedPlayer (C.4)
+TSPC_AVRCP_2_34   False		CT: ChangePath (C.4)
+TSPC_AVRCP_2_35   False		CT: GetFolderItems(Filesystem) (C.4)
+TSPC_AVRCP_2_35b  False		CT: GetTotalNumberOfItems(Filesystem) (C.4)
+TSPC_AVRCP_2_36   False		CT: GetItemAttributes (O)
+TSPC_AVRCP_2_37   False		CT: PlayItem(Filesystem) (C.4)
+TSPC_AVRCP_2_38   False		CT: EVENT_UIDS_CHANGED (O)
+TSPC_AVRCP_2_39   False		CT: Searching (O)
+TSPC_AVRCP_2_40   False		CT: Search (C.7)
+TSPC_AVRCP_2_41   False		CT: GetFolderItems(Search Results) (C.7)
+TSPC_AVRCP_2_41b  False		CT: GetTotalNumberOfItems(Search Results) (C.7)
+TSPC_AVRCP_2_42   False		CT: PlayItem(SearchResultList) (C.7)
+TSPC_AVRCP_2_43   False		CT: NowPlaying (C.8)
+TSPC_AVRCP_2_44   False		CT: GetFolderItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_44b  False		CT: GetTotalNumberOfItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_45   False		CT: PlayItem(NowPlayingList) (C.8)
+TSPC_AVRCP_2_46   False		CT: AddToNowPlaying (O)
+TSPC_AVRCP_2_47   False		CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O)
+TSPC_AVRCP_2_48   False		CT: Playable Folders (O)
+TSPC_AVRCP_2_49   True (*)	CT: Absolute Volume (C.3)
+TSPC_AVRCP_2_50   True (*)	CT: SetAbsoluteVolume (C.3)
+TSPC_AVRCP_2_51   True (*)	CT: NotifyVolumeChange (C.3)
+TSPC_AVRCP_2_52   False (*)	CT: Discoverable Mode (M)
+TSPC_AVRCP_2_53   False		CT: PASSTHROUGH operation supporting press
+					and hold (O)
+TSPC_AVRCP_2_54   False		CT: Cover Art (O)
+TSPC_AVRCP_2_55   False		CT: GetImageProperties (C.14)
+TSPC_AVRCP_2_56   False		CT: GetImage (C.13)
+TSPC_AVRCP_2_57   False		CT: GetLinkedThumbnail (C.13)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined categories
+	(TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10).
+C.2: Mandatory to support at least one of TSPC_AVRCP_2_23 or TSPC_AVRC_2_24
+			if TSPC_AVRCP_2_20 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded.
+C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded.
+C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise.
+C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.9: Mandatory to support if Player Application Settings feature is supported.
+	If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is
+	required to claim support for this feature in accordance with Player
+	Application Settings support requirements, otherwise Optional.
+C.10: Mandatory to support either Get or Set Player Application Settings
+	(TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting
+	Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14
+	or TSPC_AVRCP_2_15 must be supported if Player Application Settings
+	feature is supported, in accordance with Player Application Settings
+	support requirements.
+C.11: Mandatory if TSPC_AVRCP_2_20 or TSPC_AVRCP_2_49 is supported, otherwise
+	Optional.
+C.12: Mandatory if Basic Group Navigation Feature supported. If any item
+	TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to
+	support both, in accordance with Basic Group Navigation support
+	requirements, otherwise Excluded.
+C.13: Mandatory to support at least one of the functions if TSPC_AVRCP_2_54
+	(Cover Art) is support, otherwise Excluded.
+C.14: Optional if TSPC_AVRCP_2_54 (Cover Art) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Controller Profile Version
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2b_1   False		CT: AVRCP v1.0 (C.1)
+TSPC_AVRCP_2b_2   False		CT: AVRCP v1.3 (C.1)
+TSPC_AVRCP_2b_3   False		CT: AVRCP v1.4 (C.1)
+TSPC_AVRCP_2b_4   False		CT: AVRCP v1.5 (C.1)
+TSPC_AVRCP_2b_5   False		CT: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions if
+	Controller role supported (SPC_AVRCP_1_1).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of Category 1 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_3_1    False		CT: category 1 - Operation id: 0 (C.1)
+TSPC_AVRCP_3_2    False		CT: category 1 - Operation id: 1 (C.1)
+TSPC_AVRCP_3_3    False		CT: category 1 - Operation id: 2 (C.1)
+TSPC_AVRCP_3_4    False		CT: category 1 - Operation id: 3 (C.1)
+TSPC_AVRCP_3_5    False		CT: category 1 - Operation id: 4 (C.1)
+TSPC_AVRCP_3_6    False		CT: category 1 - Operation id: 5 (C.1)
+TSPC_AVRCP_3_7    False		CT: category 1 - Operation id: 6 (C.1)
+TSPC_AVRCP_3_8    False		CT: category 1 - Operation id: 7 (C.1)
+TSPC_AVRCP_3_9    False		CT: category 1 - Operation id: 8 (C.1)
+TSPC_AVRCP_3_10   False		CT: category 1 - Operation id: 9 (C.1)
+TSPC_AVRCP_3_11   False		CT: category 1 - Operation id: dot (C.1)
+TSPC_AVRCP_3_12   False		CT: category 1 - Operation id: enter (C.1)
+TSPC_AVRCP_3_13   False		CT: category 1 - Operation id: clear (C.1)
+TSPC_AVRCP_3_14   False		CT: category 1 - Operation id: sound_select
+					(C.1)
+TSPC_AVRCP_3_15   False		CT: category 1 - Operation id: input_select
+					(C.1)
+TSPC_AVRCP_3_16   False		CT: category 1 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_3_17   False		CT: category 1 - Operation id: help (C.1)
+TSPC_AVRCP_3_18   False		CT: category 1 - Operation id: power (C.1)
+TSPC_AVRCP_3_19   False		CT: category 1 - Operation id: play (C.1)
+TSPC_AVRCP_3_20   False		CT: category 1 - Operation id: stop (C.1)
+TSPC_AVRCP_3_21   False		CT: category 1 - Operation id: pause (C.1)
+TSPC_AVRCP_3_22   False		CT: category 1 - Operation id: record (C.1)
+TSPC_AVRCP_3_23   False		CT: category 1 - Operation id: rewind (C.1)
+TSPC_AVRCP_3_24   False		CT: category 1 - Operation id: fast_forward
+					(C.1)
+TSPC_AVRCP_3_25   False		CT: category 1 - Operation id: eject (C.1)
+TSPC_AVRCP_3_26   False		CT: category 1 - Operation id: forward (C.1)
+TSPC_AVRCP_3_27   False		CT: category 1 - Operation id: backward (C.1)
+TSPC_AVRCP_3_28   False		CT: category 1 - Operation id: angle (C.1)
+TSPC_AVRCP_3_29   False		CT: category 1 - Operation id: subpicture (C.1)
+TSPC_AVRCP_3_30   False		CT: category 1 - Operation id: F1 (C.1)
+TSPC_AVRCP_3_31   False		CT: category 1 - Operation id: F2 (C.1)
+TSPC_AVRCP_3_32   False		CT: category 1 - Operation id: F3 (C.1)
+TSPC_AVRCP_3_33   False		CT: category 1 - Operation id: F4 (C.1)
+TSPC_AVRCP_3_34   False		CT: category 1 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 1 (TSPC_AVRCP_2_7).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of category 2 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_4_1    False		CT: category 2 - Operation id: 0 (C.1)
+TSPC_AVRCP_4_2    False		CT: category 2 - Operation id: 1 (C.1)
+TSPC_AVRCP_4_3    False		CT: category 2 - Operation id: 2 (C.1)
+TSPC_AVRCP_4_4    False		CT: category 2 - Operation id: 3 (C.1)
+TSPC_AVRCP_4_5    False		CT: category 2 - Operation id: 4 (C.1)
+TSPC_AVRCP_4_6    False		CT: category 2 - Operation id: 5 (C.1)
+TSPC_AVRCP_4_7    False		CT: category 2 - Operation id: 6 (C.1)
+TSPC_AVRCP_4_8    False		CT: category 2 - Operation id: 7 (C.1)
+TSPC_AVRCP_4_9    False		CT: category 2 - Operation id: 8 (C.1)
+TSPC_AVRCP_4_10   False		CT: category 2 - Operation id: 9 (C.1)
+TSPC_AVRCP_4_11   False		CT: category 2 - Operation id: dot (C.1)
+TSPC_AVRCP_4_12   False		CT: category 2 - Operation id: enter (C.1)
+TSPC_AVRCP_4_13   False		CT: category 2 - Operation id: clear (C.1)
+TSPC_AVRCP_4_14   False		CT: category 2 - Operation id: sound_select
+					(C.1)
+TSPC_AVRCP_4_15   False		CT: category 2 - Operation id: input_select
+					(C.1)
+TSPC_AVRCP_4_16   False		CT: category 2 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_4_17   False		CT: category 2 - Operation id: help (C.1)
+TSPC_AVRCP_4_18   False		CT: category 2 - Operation id: power (C.1)
+TSPC_AVRCP_4_19   False		CT: category 2 - Operation id: volume_up (C.1)
+TSPC_AVRCP_4_20   False		CT: category 2 - Operation id: volume_down (C.1)
+TSPC_AVRCP_4_21   False		CT: category 2 - Operation id: mute (C.1)
+TSPC_AVRCP_4_22   False		CT: category 2 - Operation id: F1 (C.1)
+TSPC_AVRCP_4_23   False		CT: category 2 - Operation id: F2 (C.1)
+TSPC_AVRCP_4_24   False		CT: category 2 - Operation id: F3 (C.1)
+TSPC_AVRCP_4_25   False		CT: category 2 - Operation id: F4 (C.1)
+TSPC_AVRCP_4_26   False		CT: category 2 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 2 (TSPC_AVRCP_2_8).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of category 3 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_5_1    False		CT: category 3 - Operation id: 0 (C.1)
+TSPC_AVRCP_5_2    False		CT: category 3 - Operation id: 1 (C.1)
+TSPC_AVRCP_5_3    False		CT: category 3 - Operation id: 2 (C.1)
+TSPC_AVRCP_5_4    False		CT: category 3 - Operation id: 3 (C.1)
+TSPC_AVRCP_5_5    False		CT: category 3 - Operation id: 4 (C.1)
+TSPC_AVRCP_5_6    False		CT: category 3 - Operation id: 5 (C.1)
+TSPC_AVRCP_5_7    False		CT: category 3 - Operation id: 6 (C.1)
+TSPC_AVRCP_5_8    False		CT: category 3 - Operation id: 7 (C.1)
+TSPC_AVRCP_5_9    False		CT: category 3 - Operation id: 8 (C.1)
+TSPC_AVRCP_5_10   False		CT: category 3 - Operation id: 9 (C.1)
+TSPC_AVRCP_5_11   False		CT: category 3 - Operation id: dot (C.1)
+TSPC_AVRCP_5_12   False		CT: category 3 - Operation id: enter (C.1)
+TSPC_AVRCP_5_13   False		CT: category 3 - Operation id: clear (C.1)
+TSPC_AVRCP_5_14   False		CT: category 3 - Operation id: channel up (C.1)
+TSPC_AVRCP_5_15   False		CT: category 3 - Operation id: channel down
+					(C.1)
+TSPC_AVRCP_5_16   False		CT: category 3 - Operation id: previous channel
+					(C.1)
+TSPC_AVRCP_5_17   False		CT: category 3 - Operation id: sound_select
+					(C.1)
+TSPC_AVRCP_5_18   False		CT: category 3 - Operation id: input_select
+					(C.1)
+TSPC_AVRCP_5_19   False		CT: category 3 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_5_20   False		CT: category 3 - Operation id: help (C.1)
+TSPC_AVRCP_5_21   False		CT: category 3 - Operation id: power (C.1)
+TSPC_AVRCP_5_22   False		CT: category 3 - Operation id: angle (C.1)
+TSPC_AVRCP_5_23   False		CT: category 3 - Operation id: subpicture(C.1)
+TSPC_AVRCP_5_24   False		CT: category 3 - Operation id: F1 (C.1)
+TSPC_AVRCP_5_25   False		CT: category 3 - Operation id: F2 (C.1)
+TSPC_AVRCP_5_26   False		CT: category 3 - Operation id: F3 (C.1)
+TSPC_AVRCP_5_27   False		CT: category 3 - Operation id: F4 (C.1)
+TSPC_AVRCP_5_28   False		CT: category 3 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 3 (TSPC_AVRCP_2_9).
+-------------------------------------------------------------------------------
+
+
+		Operation_id of category 4 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_6_1    False		CT: category 4 - Operation id: select (C.1)
+TSPC_AVRCP_6_2    False		CT: category 4 - Operation id: up (C.1)
+TSPC_AVRCP_6_3    False		CT: category 4 - Operation id: down (C.1)
+TSPC_AVRCP_6_4    False		CT: category 4 - Operation id: left (C.1)
+TSPC_AVRCP_6_5    False		CT: category 4 - Operation id: right (C.1)
+TSPC_AVRCP_6_6    False		CT: category 4 - Operation id: right up (C.1)
+TSPC_AVRCP_6_7    False		CT: category 4 - Operation id: right down (C.1)
+TSPC_AVRCP_6_8    False		CT: category 4 - Operation id: left up (C.1)
+TSPC_AVRCP_6_9    False		CT: category 4 - Operation id: left down (C.1)
+TSPC_AVRCP_6_10   False		CT: category 4 - Operation id: root menu (C.1)
+TSPC_AVRCP_6_11   False		CT: category 4 - Operation id: setup menu (C.1)
+TSPC_AVRCP_6_12   False		CT: category 4 - Operation id: contents menu
+					(C.1)
+TSPC_AVRCP_6_13   False		CT: category 4 - Operation id: favorite menu
+					(C.1)
+TSPC_AVRCP_6_14   False		CT: category 4 - Operation id: exit (C.1)
+TSPC_AVRCP_6_15   False		CT: category 4 - Operation id: 0 (C.1)
+TSPC_AVRCP_6_16   False		CT: category 4 - Operation id: 1 (C.1)
+TSPC_AVRCP_6_17   False		CT: category 4 - Operation id: 2 (C.1)
+TSPC_AVRCP_6_18   False		CT: category 4 - Operation id: 3 (C.1)
+TSPC_AVRCP_6_19   False		CT: category 4 - Operation id: 4 (C.1)
+TSPC_AVRCP_6_20   False		CT: category 4 - Operation id: 5 (C.1)
+TSPC_AVRCP_6_21   False		CT: category 4 - Operation id: 6 (C.1)
+TSPC_AVRCP_6_22   False		CT: category 4 - Operation id: 7 (C.1)
+TSPC_AVRCP_6_23   False		CT: category 4 - Operation id: 8 (C.1)
+TSPC_AVRCP_6_24   False		CT: category 4 - Operation id: 9 (C.1)
+TSPC_AVRCP_6_25   False		CT: category 4 - Operation id: dot (C.1)
+TSPC_AVRCP_6_26   False		CT: category 4 - Operation id: enter (C.1)
+TSPC_AVRCP_6_27   False		CT: category 4 - Operation id: clear (C.1)
+TSPC_AVRCP_6_28   False		CT: category 4 - Operation id:
+					display_information (C.1)
+TSPC_AVRCP_6_29   False		CT: category 4 - Operation id: help (C.1)
+TSPC_AVRCP_6_30   False		CT: category 4 - Operation id: page up (C.1)
+TSPC_AVRCP_6_31   False		CT: category 4 - Operation id: page down (C.1)
+TSPC_AVRCP_6_32   False		CT: category 4 - Operation id: power (C.1)
+TSPC_AVRCP_6_33   False		CT: category 4 - Operation id: F1 (C.1)
+TSPC_AVRCP_6_34   False		CT: category 4 - Operation id: F2 (C.1)
+TSPC_AVRCP_6_35   False		CT: category 4 - Operation id: F3 (C.1)
+TSPC_AVRCP_6_36   False		CT: category 4 - Operation id: F4 (C.1)
+TSPC_AVRCP_6_37   False		CT: category 4 - Operation id: vendor_unique
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+	supports category 4 (TSPC_AVRCP_2_10).
+-------------------------------------------------------------------------------
+
+
+		Target Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7_1    True (*)	TG: Initiating connection establishment for
+					Control (O)
+TSPC_AVRCP_7_2    True		TG: Accept connection establishment for Control
+					initiated by CT (M)
+TSPC_AVRCP_7_3    True (*)	TG: Initiating connection release (M)
+TSPC_AVRCP_7_4    True		TG: Accepting connection release (M)
+TSPC_AVRCP_7_5    True		TG: Receiving UNIT INFO (M)
+TSPC_AVRCP_7_6    True		TG: Receiving SUBUNIT INFO (M)
+TSPC_AVRCP_7_7    True (*)	TG: Receiving PASS THROUGH command category 1
+					(C.1)
+TSPC_AVRCP_7_8    True (*)	TG: Receiving PASS THROUGH command category 2
+					(C.1)
+TSPC_AVRCP_7_9    False		TG: Receiving PASS THROUGH command category 3
+					(C.1)
+TSPC_AVRCP_7_10   False		TG: Receiving PASS THROUGH command category 4
+					(C.1)
+TSPC_AVRCP_7_11   True (*)	TG: Get Capabilities Response (C.3)
+TSPC_AVRCP_7_12   False		TG: List Player Application Settings Attributes
+					Response (C.14)
+TSPC_AVRCP_7_13   False		TG: List Player Application Setting Values
+					Response (C.14)
+TSPC_AVRCP_7_14   False		TG: Get Current Player Application Settings
+					Value Response (C.14)
+TSPC_AVRCP_7_15   False		TG: Set Player Application Setting Value
+					Response (C.14)
+TSPC_AVRCP_7_16   False		TG: Get Player Application Setting Attribute
+					Text Response (O)
+TSPC_AVRCP_7_17   False		TG: Get Player Application Setting Value Text
+					Response (O)
+TSPC_AVRCP_7_18   False		TG: Inform Displayable Character Set Response
+					(O)
+TSPC_AVRCP_7_19   False		TG: Inform Battery Status Of CT Response (O)
+TSPC_AVRCP_7_20   True (*)	TG: Get Element Attributes Response (C.3)
+TSPC_AVRCP_7_21   True (*)	TG: Get Play Status Response (C.2)
+TSPC_AVRCP_7_22   True (*)	TG: Register Notification Response (C.12)
+TSPC_AVRCP_7_23   True (*)	TG: Notify Event Response:
+					PLAYBACK_STATUS_CHANGED (C.4)
+TSPC_AVRCP_7_24   True (*)	TG: Notify Event Response: TRACK_CHANGED (C.4)
+TSPC_AVRCP_7_25   False		TG: Notify Event Response: TRACK_REACHED_END (O)
+TSPC_AVRCP_7_26   False		TG: Notify Event Response: TRACK_REACHED_START
+					(O)
+TSPC_AVRCP_7_27   False		TG: Notify Event Response: PLAYBACK_POS_CHANGED
+					(O)
+TSPC_AVRCP_7_28   False		TG: Notify Event Response: BATT_STATUS_CHANGED
+					(O)
+TSPC_AVRCP_7_29   False		TG: Notify Event Response: SYSTEM_STATUS_CHANGED
+					(O)
+TSPC_AVRCP_7_30   False		TG: Notify Event Response:
+					PLAYER_APPLICATION_SETTING_CHANGED (O)
+TSPC_AVRCP_7_31   True (*)	TG: Request Continuing Response (C.2)
+TSPC_AVRCP_7_32   True (*)	TG: Abort ContinuingResponse Response (C.2)
+TSPC_AVRCP_7_34   False		TG: Next Group (C.15)
+TSPC_AVRCP_7_35   False		TG: Previous Group (C.15)
+TSPC_AVRCP_7_36   False		TG: Media Player Selection (C.8)
+TSPC_AVRCP_7_37   False		TG: SetAddressedPlayer (C.8)
+TSPC_AVRCP_7_38   False		TG: GetFolderItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_38b  False		TG: GetTotalNumberOfItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_39   False		TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8)
+TSPC_AVRCP_7_40   False		TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8)
+TSPC_AVRCP_7_41   False		TG: Supports Multiple Players (O)
+TSPC_AVRCP_7_42   False		TG: Browsing (O)
+TSPC_AVRCP_7_42a  False		TG: Initiating connection establishment for
+					browsing channel (O)
+TSPC_AVRCP_7_43   False		TG: SetBrowsedPlayer (C.6)
+TSPC_AVRCP_7_44   False		TG: ChangePath (C.6)
+TSPC_AVRCP_7_45   False		TG: GetFolderItems(Filesystem) (C.6)
+TSPC_AVRCP_7_45b  False		TG: GetTotalNumberOfItems(Filesystem) (C.6)
+TSPC_AVRCP_7_46   False		TG: GetItemAttributes (C.6)
+TSPC_AVRCP_7_47   False		TG: PlayItem(Filesystem) (C.6)
+TSPC_AVRCP_7_48   False		TG: EVENT_UIDS_CHANGED (C.9)
+TSPC_AVRCP_7_49   False		TG: Database Aware Players (O)
+TSPC_AVRCP_7_50   False		TG: Searching (O)
+TSPC_AVRCP_7_51   False		TG: Search (C.10)
+TSPC_AVRCP_7_52   False		TG: GetFolderItems(Search Results) (C.10)
+TSPC_AVRCP_7_52b  False		TG: GetTotalNumberOfItems(Search Results) (C.10)
+TSPC_AVRCP_7_53   False		TG: PlayItem(SearchResultList) (C.10)
+TSPC_AVRCP_7_54   False		TG: NowPlaying (C.11)
+TSPC_AVRCP_7_55   False		TG: GetFolderItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_55b  False		TG: GetTotalNumberOfItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_56   False		TG: PlayItem(NowPlayingList) (C.11)
+TSPC_AVRCP_7_57   False		TG: AddToNowPlaying (O)
+TSPC_AVRCP_7_58   False		TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11)
+TSPC_AVRCP_7_59   False		TG: Playable Folders (O)
+TSPC_AVRCP_7_60   False		TG: Absolute Volume (C.5)
+TSPC_AVRCP_7_61   False		TG: SetAbsoluteVolume (C.5)
+TSPC_AVRCP_7_62   False		TG: NotifyVolumeChange (C.5)
+TSPC_AVRCP_7_63   False		TG: Error Response (O)
+TSPC_AVRCP_7_64   False		TG: General Reject (C.13)
+TSPC_AVRCP_7_65   True		TG: Discoverable Mode (M)
+TSPC_AVRCP_7_66   False		TG: PASSTHROUGH operation supporting press
+					and hold (O)
+TSPC_AVRCP_7_67   False		TG: Cover Art (O)
+TSPC_AVRCP_7_68   False		TG: GetImageProperties (C.16)
+TSPC_AVRCP_7_69   False		TG: GetImage (C.16)
+TSPC_AVRCP_7_70   False		TG: GetLinkedThumbnail (C.16)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the categories. Supported
+	operation_id's are shown in Table 8 to Table 11.
+C.2: Mandatory if 7/20 is supported, otherwise Optional.
+C.3: Mandatory if 7/7 is supported, otherwise Optional.
+C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional.
+C.5: Mandatory if 7/8 is supported, otherwise Excluded.
+C.6: Mandatory if 7/42 is supported, otherwise Excluded.
+C.7: Mandatory if 7/36 is supported, otherwise Excluded.
+C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded.
+C.9: Mandatory if 7/49 is supported, otherwise Optional.
+C.10: Mandatory if 7/50 is supported, otherwise Excluded.
+C.11: Mandatory if 7/42 is supported, otherwise Optional.
+C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional
+C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional.
+C.14: Mandatory if Player Application Settings Feature supported. If any item
+	7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported,
+	in accordance with Player Application Settings Feature support
+	requirements, otherwise Excluded.
+C.15: Mandatory if Basic Group Navigation Feature supported. If any item
+	7/34 or 7/35 is supported it is mandatory to support both,
+	in accordance with Basic Group Navigation support requirements,
+	otherwise Excluded.
+C.16: Mandatory if 7/67 (Cover Art) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+		Target Profile Version
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7b_1   False		TG: AVRCP v1.0 (C.1)
+TSPC_AVRCP_7b_2   False		TG: AVRCP v1.3 (C.1)
+TSPC_AVRCP_7b_3   False		TG: AVRCP v1.4 (C.1)
+TSPC_AVRCP_7b_4   True (*)	TG: AVRCP v1.5 (C.1)
+TSPC_AVRCP_7b_5   False		TG: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 1 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_8_1    False		TG: category 1 - Operation id: 0 (O)
+TSPC_AVRCP_8_2    False		TG: category 1 - Operation id: 1 (O)
+TSPC_AVRCP_8_3    False		TG: category 1 - Operation id: 2 (O)
+TSPC_AVRCP_8_4    False		TG: category 1 - Operation id: 3 (O)
+TSPC_AVRCP_8_5    False		TG: category 1 - Operation id: 4 (O)
+TSPC_AVRCP_8_6    False		TG: category 1 - Operation id: 5 (O)
+TSPC_AVRCP_8_7    False		TG: category 1 - Operation id: 6 (O)
+TSPC_AVRCP_8_8    False		TG: category 1 - Operation id: 7 (O)
+TSPC_AVRCP_8_9    False		TG: category 1 - Operation id: 8 (O)
+TSPC_AVRCP_8_10   False		TG: category 1 - Operation id: 9 (O)
+TSPC_AVRCP_8_11   False		TG: category 1 - Operation id: dot (O)
+TSPC_AVRCP_8_12   False		TG: category 1 - Operation id: enter (O)
+TSPC_AVRCP_8_13   False		TG: category 1 - Operation id: clear (O)
+TSPC_AVRCP_8_14   False		TG: category 1 - Operation id: sound select (O)
+TSPC_AVRCP_8_15   False		TG: category 1 - Operation id: input select (O)
+TSPC_AVRCP_8_16   False		TG: category 1 - Operation id: display
+					information (O)
+TSPC_AVRCP_8_17   False		TG: category 1 - Operation id: help (O)
+TSPC_AVRCP_8_18   False		TG: category 1 - Operation id: power (O)
+TSPC_AVRCP_8_19   True		TG: category 1 - Operation id: play (M)
+TSPC_AVRCP_8_20   True		TG: category 1 - Operation id: stop (M)
+TSPC_AVRCP_8_21   True (*)	TG: category 1 - Operation id: pause (O)
+TSPC_AVRCP_8_22   False		TG: category 1 - Operation id: record (O)
+TSPC_AVRCP_8_23   True (*)	TG: category 1 - Operation id: rewind (O)
+TSPC_AVRCP_8_24   True (*)	TG: category 1 - Operation id: fast forward (O)
+TSPC_AVRCP_8_25   False		TG: category 1 - Operation id: eject (O)
+TSPC_AVRCP_8_26   True (*)	TG: category 1 - Operation id: forward (O)
+TSPC_AVRCP_8_27   True (*)	TG: category 1 - Operation id: backward (O)
+TSPC_AVRCP_8_28   False		TG: category 1 - Operation id: angle (O)
+TSPC_AVRCP_8_29   False		TG: category 1 - Operation id: subpicture (O)
+TSPC_AVRCP_8_30   False		TG: category 1 - Operation id: F1 (O)
+TSPC_AVRCP_8_31   False		TG: category 1 - Operation id: F2 (O)
+TSPC_AVRCP_8_32   False		TG: category 1 - Operation id: F3 (O)
+TSPC_AVRCP_8_33   False		TG: category 1 - Operation id: F4 (O)
+TSPC_AVRCP_8_33a  False		TG: category 1 - Operation id: F5 (O)
+TSPC_AVRCP_8_34   False		TG: category 1 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 2 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_9_1    False		TG: category 2 - Operation id: 0 (O)
+TSPC_AVRCP_9_2    False		TG: category 2 - Operation id: 1 (O)
+TSPC_AVRCP_9_3    False		TG: category 2 - Operation id: 2 (O)
+TSPC_AVRCP_9_4    False		TG: category 2 - Operation id: 3 (O)
+TSPC_AVRCP_9_5    False		TG: category 2 - Operation id: 4 (O)
+TSPC_AVRCP_9_6    False		TG: category 2 - Operation id: 5 (O)
+TSPC_AVRCP_9_7    False		TG: category 2 - Operation id: 6 (O)
+TSPC_AVRCP_9_8    False		TG: category 2 - Operation id: 7 (O)
+TSPC_AVRCP_9_9    False		TG: category 2 - Operation id: 8 (O)
+TSPC_AVRCP_9_10   False		TG: category 2 - Operation id: 9 (O)
+TSPC_AVRCP_9_11   False		TG: category 2 - Operation id: dot (O)
+TSPC_AVRCP_9_12   False		TG: category 2 - Operation id: enter (O)
+TSPC_AVRCP_9_13   False		TG: category 2 - Operation id: clear (O)
+TSPC_AVRCP_9_14   False		TG: category 2 - Operation id: sound select (O)
+TSPC_AVRCP_9_15   False		TG: category 2 - Operation id: input select (O)
+TSPC_AVRCP_9_16   False		TG: category 2 - Operation id: display
+					information (O)
+TSPC_AVRCP_9_17	  False		TG: category 2 - Operation id: help (O)
+TSPC_AVRCP_9_18	  False		TG: category 2 - Operation id: power (O)
+TSPC_AVRCP_9_19   True		TG: category 2 - Operation id: volume up (C.2)
+TSPC_AVRCP_9_20   True		TG: category 2 - Operation id: volume down (C.2)
+TSPC_AVRCP_9_21   False		TG: category 2 - Operation id: mute (O)
+TSPC_AVRCP_9_24   False		TG: category 2 - Operation id: F1 (O)
+TSPC_AVRCP_9_25   False		TG: category 2 - Operation id: F2 (O)
+TSPC_AVRCP_9_26   False		TG: category 2 - Operation id: F3 (O)
+TSPC_AVRCP_9_27   False		TG: category 2 - Operation id: F4 (O)
+TSPC_AVRCP_9_27a  False		TG: category 2 - Operation id: F5 (O)
+TSPC_AVRCP_9_28   False		TG: category 2 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8).
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 3 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_10_1   False		TG: category 3 - Operation id: 0 (O)
+TSPC_AVRCP_10_2   False		TG: category 3 - Operation id: 1 (O)
+TSPC_AVRCP_10_3   False		TG: category 3 - Operation id: 2 (O)
+TSPC_AVRCP_10_4   False		TG: category 3 - Operation id: 3 (O)
+TSPC_AVRCP_10_5   False		TG: category 3 - Operation id: 4 (O)
+TSPC_AVRCP_10_6   False		TG: category 3 - Operation id: 5 (O)
+TSPC_AVRCP_10_7   False		TG: category 3 - Operation id: 6 (O)
+TSPC_AVRCP_10_8   False		TG: category 3 - Operation id: 7 (O)
+TSPC_AVRCP_10_9   False		TG: category 3 - Operation id: 8 (O)
+TSPC_AVRCP_10_10  False		TG: category 3 - Operation id: 9 (O)
+TSPC_AVRCP_10_11  False		TG: category 3 - Operation id: dot (O)
+TSPC_AVRCP_10_12  False		TG: category 3 - Operation id: enter (O)
+TSPC_AVRCP_10_13  False		TG: category 3 - Operation id: clear (O)
+TSPC_AVRCP_10_14  False (*)	TG: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_10_15  False (*)	TG: category 3 - Operation id: channel down
+					(C.3)
+TSPC_AVRCP_10_16  False		TG: category 3 - Operation id: previous channel
+					(O)
+TSPC_AVRCP_10_17  False		TG: category 3 - Operation id: sound select (O)
+TSPC_AVRCP_10_18  False		TG: category 3 - Operation id: input select (O)
+TSPC_AVRCP_10_19  False		TG: category 3 - Operation id: display
+					information (O)
+TSPC_AVRCP_10_20  False		TG: category 3 - Operation id: help (O)
+TSPC_AVRCP_10_21  False		TG: category 3 - Operation id: power (O)
+TSPC_AVRCP_10_21a False		TG: category 3 - Operation id: angle (O)
+TSPC_AVRCP_10_21b False		TG: category 3 - Operation id: subpicture (O)
+TSPC_AVRCP_10_22  False		TG: category 3 - Operation id: F1 (O)
+TSPC_AVRCP_10_23  False		TG: category 3 - Operation id: F2 (O)
+TSPC_AVRCP_10_24  False		TG: category 3 - Operation id: F3 (O)
+TSPC_AVRCP_10_25  False		TG: category 3 - Operation id: F4 (O)
+TSPC_AVRCP_10_25a False		TG: category 3 - Operation id: F5 (O)
+TSPC_AVRCP_10_26  False		TG: category 3 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9).
+-------------------------------------------------------------------------------
+
+
+		operation_id of category 4 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected	Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_11_1   False (*)	TG: category 4 - Operation id: select (C.4)
+TSPC_AVRCP_11_2   False (*)	TG: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_11_3   False (*)	TG: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_11_4   False (*)	TG: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_11_5   False (*)	TG: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_11_6   False		TG: category 4 - Operation id: right up (O)
+TSPC_AVRCP_11_7   False		TG: category 4 - Operation id: right down (O)
+TSPC_AVRCP_11_8   False		TG: category 4 - Operation id: left up (O)
+TSPC_AVRCP_11_9   False		TG: category 4 - Operation id: left down (O)
+TSPC_AVRCP_11_10  False (*)	TG: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_11_11  False		TG: category 4 - Operation id: setup menu (O)
+TSPC_AVRCP_11_12  False		TG: category 4 - Operation id: contents menu (O)
+TSPC_AVRCP_11_13  False		TG: category 4 - Operation id: favorite menu (O)
+TSPC_AVRCP_11_14  False		TG: category 4 - Operation id: exit (O)
+TSPC_AVRCP_11_15  False		TG: category 4 - Operation id: 0 (O)
+TSPC_AVRCP_11_16  False		TG: category 4 - Operation id: 1 (O)
+TSPC_AVRCP_11_17  False		TG: category 4 - Operation id: 2 (O)
+TSPC_AVRCP_11_18  False		TG: category 4 - Operation id: 3 (O)
+TSPC_AVRCP_11_19  False		TG: category 4 - Operation id: 4 (O)
+TSPC_AVRCP_11_20  False		TG: category 4 - Operation id: 5 (O)
+TSPC_AVRCP_11_21  False		TG: category 4 - Operation id: 6 (O)
+TSPC_AVRCP_11_22  False		TG: category 4 - Operation id: 7 (O)
+TSPC_AVRCP_11_23  False		TG: category 4 - Operation id: 8 (O)
+TSPC_AVRCP_11_24  False		TG: category 4 - Operation id: 9 (O)
+TSPC_AVRCP_11_25  False		TG: category 4 - Operation id: dot (O)
+TSPC_AVRCP_11_26  False		TG: category 4 - Operation id: enter (O)
+TSPC_AVRCP_11_27  False		TG: category 4 - Operation id: clear (O)
+TSPC_AVRCP_11_28  False		TG: category 4 - Operation id: disply (O)
+TSPC_AVRCP_11_29  False		TG: category 4 - Operation id: help (O)
+TSPC_AVRCP_11_30  False		TG: category 4 - Operation id: page up (O)
+TSPC_AVRCP_11_31  False		TG: category 4 - Operation id: page down (O)
+TSPC_AVRCP_11_32  False		TG: category 4 - Operation id: power (O)
+TSPC_AVRCP_11_33  False		TG: category 4 - Operation id: F1 (O)
+TSPC_AVRCP_11_34  False		TG: category 4 - Operation id: F2 (O)
+TSPC_AVRCP_11_35  False		TG: category 4 - Operation id: F3 (O)
+TSPC_AVRCP_11_36  False		TG: category 4 - Operation id: F4 (O)
+TSPC_AVRCP_11_36a False		TG: category 4 - Operation id: F5 (O)
+TSPC_AVRCP_11_37  False		TG: category 4 - Operation id: vendor unique (O)
+
+TSPC_AVRCP_12_1   True		General discoverable mode (M)
+TSPC_AVRCP_13_1   True		General discoverable mode (M)
+TSPC_AVRCP_14_1   False		OBEX Connect operation (C.1)
+TSPC_AVRCP_14_2   False		OBEX Get operation (C.1)
+TSPC_AVRCP_14_3   False		OBEX Disconnect operation (C.1)
+TSPC_AVRCP_15_1   False		OBEX Connect operation (C.1)
+TSPC_AVRCP_15_2   False		OBEX Get operation (C.1)
+TSPC_AVRCP_15_3   False		OBEX Disconnect operation (C.1)
+
+TSPC_ALL          False		Enables all test cases when set to TRUE.
+-------------------------------------------------------------------------------
+C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10).
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-bnep.txt b/bluez/repo/android/pics-bnep.txt
new file mode 100644
index 0000000..289e470
--- /dev/null
+++ b/bluez/repo/android/pics-bnep.txt
@@ -0,0 +1,26 @@
+BNEP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+		Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_BNEP_1_1	True		BNEP Connection Setup (M)
+TSPC_BNEP_1_2	True (*)	BNEP Data Packet Reception (M)
+TSPC_BNEP_1_3	True (*)	BNEP Data Packet Transmission (M)
+TSPC_BNEP_1_3a	True (*)	BNEP Compressed Packet Transmission (O)
+TSPC_BNEP_1_3b	True (*)	BNEP Compressed Packet Transmission Source Only
+				(O)
+TSPC_BNEP_1_4	True		BNEP Control Message Processing (M)
+TSPC_BNEP_1_5	True		BNEP Extension Header Processing (M)
+TSPC_BNEP_1_6	True		Network Protocol Filter Message Transmission (O)
+TSPC_BNEP_1_7	True		Multicast Address Filter Message Transmission
+				(O)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-did.txt b/bluez/repo/android/pics-did.txt
new file mode 100644
index 0000000..9ecb86d
--- /dev/null
+++ b/bluez/repo/android/pics-did.txt
@@ -0,0 +1,23 @@
+DID PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+		SDP Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DID_1_1	True		Specification ID (M)
+TSPC_DID_1_2	True		Vendor ID (M)
+TSPC_DID_1_3	True		Product ID (M)
+TSPC_DID_1_4	True		Version (M)
+TSPC_DID_1_5	True		Primary Record (M)
+TSPC_DID_1_6	True		Vendor ID Source (M)
+TSPC_ALL	False		Turns on all the test cases
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-dis.txt b/bluez/repo/android/pics-dis.txt
new file mode 100644
index 0000000..83f25ce
--- /dev/null
+++ b/bluez/repo/android/pics-dis.txt
@@ -0,0 +1,59 @@
+DIS PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Transport Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_1_1	True		Service supported over BR/EDR (C.1)
+TSPC_DIS_1_2	True		Service supported over LE (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_DIS_1_1 or TSPC_DIS_1_2.
+-------------------------------------------------------------------------------
+
+
+		Service Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_2_1	True		Device Information Service (M)
+TSPC_DIS_2_2	True		Manufacturer Name String Characteristic (O)
+TSPC_DIS_2_3	True		Model Number String Characteristic (O)
+TSPC_DIS_2_4	True		Serial Number String Characteristic (O)
+TSPC_DIS_2_5	True		Hardware Revision String Characteristic (O)
+TSPC_DIS_2_6	True		Firmware Revision String Characteristic (O)
+TSPC_DIS_2_7	True		Software Revision String Characteristic (O)
+TSPC_DIS_2_8	True		System ID Characteristic (O)
+TSPC_DIS_2_9	False (*)	IEEE 11073-20601 Regulatory Certification
+				Data List Characteristic (O)
+TSPC_DIS_2_10	True		SDP Interoperability (C.1)
+TSPC_DIS_2_11	True		PnP ID (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_DIS_1_1 (BR/EDR) is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_3_1	True		Generic Attribute Profile Server (M)
+-------------------------------------------------------------------------------
+
+
+		SDP Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_DIS_4_1	True		Support for server role (M)
+TSPC_DIS_4_2	True		ProtocolDescriptorList (M)
+TSPC_DIS_4_3	True		BrowseGroupList (M)
+-------------------------------------------------------------------------------
+Note: Marked as False as TSPC_DIS_1_1 is set to False
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-gap.txt b/bluez/repo/android/pics-gap.txt
new file mode 100644
index 0000000..3775995
--- /dev/null
+++ b/bluez/repo/android/pics-gap.txt
@@ -0,0 +1,788 @@
+GAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Device Configuration
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0_1	False (*)	BR/EDR (C.1)
+TSPC_GAP_0_2	False (*)	LE (C.2)
+TSPC_GAP_0_3	True		BR/EDR/LE (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or
+	'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR'
+	or 'BR/HS Host' CC), otherwise excluded. Optional for
+	'Component (Tested)' or 'Component (Non-Tested)'.
+C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are
+	Supported (End Product or Host Subsystem with LE Host CC),
+	otherwise excluded.  Optional for 'Component (Tested)' or
+	'Component (Non-Tested)'.
+C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or
+	'BR/HS/LE Host') are Supported (End Product or Host Subsystem with
+	BR/LE or BR/HS/LE Host CC), otherwise excluded.
+	Optional for 'Component (Tested)' or 'Component (Non-tested)'.
+Note - Only one transport shall be supported.
+-------------------------------------------------------------------------------
+
+
+		Version Configuration
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0A_1	True		Core Specification Addendum 3 (CSA3), GAP
+					Connection Parameters Changes,
+					Authentication and Lost Bond Changes,
+					Private Addressing Changes, Dual Mode
+					Addressing Changes,
+					Adopted 24 July 2012 (C.1)
+TSPC_GAP_0A_2	True		Core Specification Addendum 4 (CSA4)
+TSPC_GAP_0A_3	True		Core Spec version 4.1 (Core v4.1) GAP Connection
+					Parameters Changes, Authentication and
+					Lost Bond Changes, Private Addressing
+					Changes, Dual Mode Addressing Changes,
+					Adopted 03 December 2013
+-------------------------------------------------------------------------------
+C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_1_1	True		Non-discoverable mode (C.1)
+TSPC_GAP_1_2	True		Limited-discoverable Mode (O)
+TSPC_GAP_1_3	True		General-discoverable mode (O)
+TSPC_GAP_1_4	True		Non-connectable mode (O)
+TSPC_GAP_1_5	True		Connectable mode (M)
+TSPC_GAP_1_6	True		Non-bondable mode (O)
+TSPC_GAP_1_7	True		Bondable mode (C.2)
+TSPC_GAP_1_8	False (*)	Non-Synchronizable Mode (C.3)
+TSPC_GAP_1_9	False (*)	Synchronizable Mode (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional.
+C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_GAP_0A_2 or TSPC_GAP_0A_3 and is supported, otherwise
+	Excluded.
+C.4: Optional if TSPC_GAP_0A_2 or later is supported; Mandatory if TSPC_GAP_0A_2
+	or later and BB 3a/1 (Connectionless Slave Broadcast Transmitter) are
+	supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_2_1	True		Authentication procedure (C.1)
+TSPC_GAP_2_2	True		Support of LMP-Authentication (M)
+TSPC_GAP_2_3	True		Initiate LMP-Authentication (C.5)
+TSPC_GAP_2_4	False (*)	Security mode 1 (C.2)
+TSPC_GAP_2_5	True		Security mode 2 (O)
+TSPC_GAP_2_6	False (*)	Security mode 3 (C.7)
+TSPC_GAP_2_7	True		Security mode 4 (C.4)
+TSPC_GAP_2_8	True		Support of Authenticated link key (C.6)
+TSPC_GAP_2_9	True		Support of Unauthenticated link key (C.6)
+TSPC_GAP_2_10	True		No security (C.6)
+TSPC_GAP_2_11	False (*)	Secure Connections Only Mode (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise
+	Optional.
+Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one
+	described in Fig. 5.1 on page 198 in the GAP Profile Specification and
+	not the LMP-Authenticaion.
+C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported,
+	otherwise Optional.
+C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded.
+Note 2. If a Core 2.0 and earlier design claims to support secure communcation
+	it should support either Security mode 2 or 3.
+Note 3. A Core 2.1 or later device shall always support secure communication
+	in Security Mode 4, and shall use that mode to connect with another
+	Core 2.1 or later device. It shall use Security Mode 2 only for
+	backward compatibility purposes with Core 2.0 and earlier devices.
+	Security Mode 1 is excluded for Core 2.1 or later devices based on
+	condition C.2.
+C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or
+	TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded.
+C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Idle Mode Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_3_1	True		Initiation of general inquiry (C.1)
+TSPC_GAP_3_2	True		Initiation of limited inquiry (C.1)
+TSPC_GAP_3_3	True		Initiation of name discover (O)
+TSPC_GAP_3_4	True		Initiation of device discovery (O)
+TSPC_GAP_3_5	True		Initiation of general bonding (O)
+TSPC_GAP_3_6	True		Initiation of dedicated bonding (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if
+	TSPC_GAP_3_5 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Establishment Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_4_1	True		Support link establishment as initiator (M)
+TSPC_GAP_4_2	True		Support link establishment as acceptor (M)
+TSPC_GAP_4_3	True		Support channel establishment as initiator (O)
+TSPC_GAP_4_4	True		Support channel establishment as acceptor (M)
+TSPC_GAP_4_5	True		Support connection establishment as initiator
+					(O)
+TSPC_GAP_4_6	True		Support connection establishment as acceptor
+					(O)
+TSPC_GAP_4_7	True		Support synchronization establishment
+					as receiver (O)
+-------------------------------------------------------------------------------
+
+
+		LE Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_5_1	False (*)	Broadcaster (C.1)
+TSPC_GAP_5_2	False (*)	Observer (C.1)
+TSPC_GAP_5_3	True		Peripheral (C.1)
+TSPC_GAP_5_4	True		Central (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+Note: 'LE Roles' is applicable for LE-only configurations, but it appears that
+	PTS is checking this precondition also in some BR/EDR/LE tests.
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_6_1	False (*)	Broadcaster: Transmitter (M)
+TSPC_GAP_6_2	False (*)	Broadcaster: Receiver (O)
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_7_1	False (*)	Broadcaster: Standby (M)
+TSPC_GAP_7_2	False (*)	Broadcaster: Advertising (M)
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8_1	False (*)	Broadcaster: Non-Connectable Undirected Event
+					(M)
+TSPC_GAP_8_2	False (*)	Broadcaster: Scannable Undirected Event (O)
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8A_1	False (*)	AD Type-Service UUID (O)
+TSPC_GAP_8A_2	False (*)	AD Type-Local Name (O)
+TSPC_GAP_8A_3	False (*)	AD Type-Flags (O)
+TSPC_GAP_8A_4	False (*)	AD Type-Manufacturer Specific Data (O)
+TSPC_GAP_8A_5	False (*)	AD Type-TX Power Level (O)
+TSPC_GAP_8A_6	False (*)	AD Type-Security Manager Out of Band (OOB) (C.1)
+TSPC_GAP_8A_7	False (*)	AD Type-Security manager TK Value (O)
+TSPC_GAP_8A_8	False (*)	AD Type-Slave Connection Interval Range (O)
+TSPC_GAP_8A_9	False (*)	AD Type-Service Solicitation (O)
+TSPC_GAP_8A_10	False (*)	AD Type-Service Data (O)
+TSPC_GAP_8A_11	False (*)	AD Type-Appearance (O)
+TSPC_GAP_8A_12	False (*)	AD Type-Public Target Address (O)
+TSPC_GAP_8A_13	False (*)	AD Type-Random Target Address (O)
+TSPC_GAP_8A_14	False (*)	AD Type-Advertising Interval (O)
+TSPC_GAP_8A_15	False (*)	AD Type-LE Bluetooth Device Address (O)
+TSPC_GAP_8A_16	False (*)	AD Type –LE Role (O)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_9_1	False (*)	Broadcaster: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+		Broadcaster Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_10_1	False (*)	Broadcaster: Broadcast Mode
+TSPC_GAP_11_1	False (*)	Broadcaster: Privacy Feature v.1.0
+TSPC_GAP_11_1A	False (*)	Broadcaster: Privacy Feature v1.1 (O)
+TSPC_GAP_11_2	False (*)	Broadcaster: Resolvable Private Address
+					Generation Procedure
+TSPC_GAP_11_3	False (*)	Broadcaster: Non-Resolvable Private Address
+					Generation Procedure (O)
+-------------------------------------------------------------------------------
+
+
+		Observer Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_12_1	False (*)	Observer: Receiver
+TSPC_GAP_12_2	False (*)	Observer: Transmitter
+-------------------------------------------------------------------------------
+
+
+		Observer Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_13_1	False (*)	Observer: Standby
+TSPC_GAP_13_2	False (*)	Observer: Scanning
+-------------------------------------------------------------------------------
+
+
+		Observer Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_14_1	False (*)	Observer: Passive Scanning
+TSPC_GAP_14_2	False (*)	Observer: Active Scanning
+-------------------------------------------------------------------------------
+
+
+		Observer Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_15_1	False (*)	Observer: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+		Observer Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_16_1	False (*)	Observer: Observation Procedure
+-------------------------------------------------------------------------------
+
+
+		Observer Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_17_1	False (*)	Observer: Privacy Feature v1.0 (O)
+TSPC_GAP_17_1A	False (*)	Observer: Privacy Feature v1.1 (O)
+TSPC_GAP_17_2	False (*)	Observer: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_17_3	False (*)	Observer: Resolvable Private Address Resolution
+					Procedure (C.2)
+TSPC_GAP_17_4	False (*)	Observer: Resolvable Private Address Generation
+					Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are
+	supported and TSPC_GAP_17_4 (Resolvable Private Address Generation
+	Procedure) is Not Supported; Optional if CSA3 or later and
+	TSPC_GAP_17_4 are supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded.
+C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2
+	(Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable
+	Private	Address Generation Procedure) is not supported; Optional if
+	CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address
+	Generation Procedure) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_18_1	True		Peripheral: Transmitter
+TSPC_GAP_18_2	True		Peripheral: Receiver
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_19_1	True		Peripheral: Standby
+TSPC_GAP_19_2	True		Peripheral: Advertising
+TSPC_GAP_19_3	True		Peripheral: Connection, Slave Role
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20_1	True		Peripheral: Connectable Undirected Event (C.1)
+TSPC_GAP_20_2	True		Peripheral: Connectable Directed Event (C.2)
+TSPC_GAP_20_2A	True		Peripheral: Low Duty Directed Advertising (C.3)
+TSPC_GAP_20_3	True		Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4	True		Peripheral: Scannable Undirected Event
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20A_1	False (*)	AD Type-Service UUID (C.1)
+TSPC_GAP_20A_2	True		AD Type-Local Name (C.1)
+TSPC_GAP_20A_3	True		AD Type-Flags (C.2)
+TSPC_GAP_20A_4	False (*)	AD Type-Manufacturer Specific Data (C.1)
+TSPC_GAP_20A_5	True		AD Type-TX Power Level (C.1)
+TSPC_GAP_20A_6	False (*)	AD Type-Security Manager Out of Band (OOB) (C.3)
+TSPC_GAP_20A_7	False (*)	AD Type-Security manager TK Value (C.1)
+TSPC_GAP_20A_8	False (*)	AD Type-Slave Connection Interval Range (C.1)
+TSPC_GAP_20A_9	False (*)	AD Type-Service Solicitation (C.1)
+TSPC_GAP_20A_10	False (*)	AD Type-Service Data (C.1)
+TSPC_GAP_20A_11	False (*)	AD Type –Appearance (C.1)
+TSPC_GAP_20A_12	False (*)	AD Type-Public Target Address (C.1)
+TSPC_GAP_20A_13	False (*)	AD Type-Random Target Address (C.1)
+TSPC_GAP_20A_14	False (*)	AD Type-Advertising Interval (C.1)
+TSPC_GAP_20A_15	False (*)	AD Type-LE Bluetooth Device Address (C.1)
+TSPC_GAP_20A_16	False (*)	AD Type – LE Role (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is
+	supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3
+	(General Discoverable Mode) is supported, otherwise Optional.
+C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3
+	(Non-Connectable Undirected Event) or TSPC_GAP_20_4
+	(Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are
+	supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_21_1	True		Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2	True		Peripheral: Channel Map Update Procedure (M)
+TSPC_GAP_21_3	True		Peripheral: Encryption Procedure (O)
+TSPC_GAP_21_4	True		Peripheral: Feature Exchange Procedure (M)
+TSPC_GAP_21_5	True		Peripheral: Version Exchange Procedure (M)
+TSPC_GAP_21_6	True		Peripheral: Termination Procedure (M)
+TSPC_GAP_21_7	True		Peripheral: LE Ping Procedure (C.3)
+TSPC_GAP_21_8	True		Peripheral: Slave Initiated Feature Exchange
+					Procedure (C.4)
+TSPC_GAP_21_9	True		Peripheral: Connection Parameter Request
+					Procedure (C.5)
+-------------------------------------------------------------------------------
+
+
+		Peripheral Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_22_1	True		Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2	True		Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3	True		Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4	True		Peripheral: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported,
+	otherwise Excluded.
+C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_23_1	True		Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2	True		Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3	True		Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4	True		Peripheral: Connection Parameter Update
+					Procedure (O)
+TSPC_GAP_23_5	True		Peripheral: Terminate Connection Procedure (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3
+	(BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4
+	(BR/EDR/LE – Connectable Mode) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_24_1	True		Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2	True		Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3	True		Peripheral: Bonding Procedure  (C.2)
+TSPC_GAP_24_4	True		Peripheral: Multiple Bonds (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3
+	(BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE -
+	Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6
+	(BR/EDR/LE – Bondable Mode) is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Peripheral Security Aspects Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_25_1	True		Peripheral: Security Mode (O)
+TSPC_GAP_25_2	True		Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3	True		Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4	True		Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5	True		Peripheral: Connection Data Signing Procedure
+				(O)
+TSPC_GAP_25_6	True		Peripheral: Authenticate Signed Data Procedure
+				(O)
+TSPC_GAP_25_7	True		Peripheral: Authenticated Pairing
+				(LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8	True		Peripheral: Unauthenticated Pairing
+				(LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
+	otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Peripheral Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_26_1	False (*)	Peripheral: Privacy Feature v1.0 (O)
+TSPC_GAP_26_1A	True		Peripheral: Privacy Feature v1.1 (O)
+TSPC_GAP_26_2	True		Peripheral: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_26_3	True		Peripheral: Resolvable Private Address
+					Generation Procedure (C.2)
+TSPC_GAP_26_4	True		Peripheral: Resolvable Private Address
+					Generation Procedure (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_27_1	True		Peripheral: Device Name (M)
+TSPC_GAP_27_2	True		Peripheral: Appearance (M)
+TSPC_GAP_27_3	False (*)	Peripheral: Peripheral Privacy Flag (C.1)
+TSPC_GAP_27_4	False (*)	Peripheral: Reconnection Address (C.2)
+TSPC_GAP_27_5	False (*)	Peripheral: Peripheral Preferred Connection
+					Parameters (O)
+TSPC_GAP_27_6	False (*)	Peripheral: Writeable Device Name (O)
+TSPC_GAP_27_7	False (*)	Peripheral: Writeable Appearance (O)
+TSPC_GAP_27_8	False (*)	Peripheral: Writeable Peripheral Privacy Flag
+				(O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_28_1	True		Central: Transmitter (M)
+TSPC_GAP_28_2	True		Central: Receiver (M)
+-------------------------------------------------------------------------------
+
+
+		Central Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_29_1	True		Central: Standby (M)
+TSPC_GAP_29_2	True		Central: Scanning (M)
+TSPC_GAP_29_3	True		Central: Initiating (M)
+TSPC_GAP_29_4	True		Central: Connection, Master Role (M)
+-------------------------------------------------------------------------------
+
+
+		Central Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_30_1	True		Central: Passive Scanning (O)
+TSPC_GAP_30_2	True		Central: Active Scanning (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported.
+	Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4)
+	is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_31_1	True		Central: Connection Update Procedure (M)
+TSPC_GAP_31_2	True		Central: Channel Map Update Procedure (M)
+TSPC_GAP_31_3	True		Central: Encryption Procedure (O)
+TSPC_GAP_31_4	True		Central: Feature Exchange Procedure (M)
+TSPC_GAP_31_5	True		Central: Version Exchange Procedure (M)
+TSPC_GAP_31_6	True		Central: Termination Procedure (M)
+TSPC_GAP_31_7	True		Central: LE Ping Procedure (C.1)
+TSPC_GAP_31_8	True		Central: Slave Initiated Feature Exchange
+					Procedure (C.2)
+TSPC_GAP_31_9	True		Central: Connection Parameter Request Procedure
+					(C.3)
+-------------------------------------------------------------------------------
+
+
+		Central Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_32_1	True		Central: Limited Discovery Procedure (C.2)
+TSPC_GAP_32_2	True		Central: General Discovery Procedure (C.1)
+TSPC_GAP_32_3	True		Central: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded.
+C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported,
+	otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_33_1	True		Central: Auto Connection Establishment
+					Procedure (C.3)
+TSPC_GAP_33_2	True		Central: General Connection Establishment
+					Procedure (C.1)
+TSPC_GAP_33_3	True		Central: Selective Connection Establishment
+					Procedure (C.3)
+TSPC_GAP_33_4	True		Central: Direct Connection Establishment
+					Procedure (C.2)
+TSPC_GAP_33_5	True		Central: Connection Parameter Update Procedure
+					(C.2)
+TSPC_GAP_33_6	True		Central: Terminate Connection Procedure
+					(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is
+	supported, otherwise Optional.
+C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+	otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_34_1	True		Central: Non-Bondable Mode (C.1)
+TSPC_GAP_34_2	True		Central: Bondable Mode (C.2)
+TSPC_GAP_34_3	True		Central: Bonding Procedure (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded.
+C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Security Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_35_1	True		Central: Security Mode 1 (O)
+TSPC_GAP_35_2	True		Central: Security Mode 2 (O)
+TSPC_GAP_35_3	True		Central: Authentication Procedure (O)
+TSPC_GAP_35_4	True		Central: Authorization Procedure (O)
+TSPC_GAP_35_5	True		Central: Connection Data Signing Procedure (O)
+TSPC_GAP_35_6	True		Central: Authenticate Signed Data Procedure (O)
+TSPC_GAP_35_7	True		Central: Authenticated Pairing
+					(LE security mode 1 level 3) (C.1)
+TSPC_GAP_35_8	True		Central: Unauthenticated Pairing
+					(LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_36_1	False (*)	Central: Privacy Feature v1.0 (C.2)
+TSPC_GAP_36_1A	True		Central: Privacy Feature v1.1 (C.4)
+TSPC_GAP_36_2	True		Central: Non-Resolvable Private Address
+					Generation Procedure (C.1)
+TSPC_GAP_36_3	True		Central: Resolvable Private Address Resolution
+					Procedure (C.2)
+TSPC_GAP_36_4	False (*)	Central: Write to Privacy Characteristic
+					(Enable/Disable Privacy) (O)
+TSPC_GAP_36_5	True		Central: Resolvable Private Address Generation
+					Procedure (C.6)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported,
+	otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_37_1	True		Central: Device Name (M)
+TSPC_GAP_37_2	True		Central: Appearance (M)
+-------------------------------------------------------------------------------
+
+
+		BR/EDR/LE Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_38_1	False (*)	BR/EDR/LE: Broadcaster (C.1)
+TSPC_GAP_38_2	False (*)	BR/EDR/LE: Observer (C.1)
+TSPC_GAP_38_3	True		BR/EDR/LE: Peripheral (C.1)
+TSPC_GAP_38_4	True		BR/EDR/LE: Central (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+This table is applicable for BR/EDR/LE configurations. For LE-only
+configurations, see 'LE Roles' table for role declarations.
+-------------------------------------------------------------------------------
+
+
+		Central BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_39_1	True		Central BR/EDR/LE: Non-Discoverable Mode (C.1)
+TSPC_GAP_39_2	True		Central BR/EDR/LE: Discoverable Mode (C.2)
+TSPC_GAP_39_3	True		Central BR/EDR/LE: Non-Connectable Mode (C.3)
+TSPC_GAP_39_4	True		Central BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_39_5	True		Central BR/EDR/LE: Non-Bondable Mode (C.4)
+TSPC_GAP_39_6	True		Central BR/EDR/LE: Bondable Mode (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
+	otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central BR/EDR/LE Idle Mode Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_40_1	True		Central BR/EDR/LE: General Discovery (C.1)
+TSPC_GAP_40_2	True		Central BR/EDR/LE: Limited Discovery (C.2)
+TSPC_GAP_40_3	True		Central BR/EDR/LE: Device Type Discovery (C.3)
+TSPC_GAP_40_4	True		Central BR/EDR/LE: Name Discovery (C.4)
+TSPC_GAP_40_5	True		Central BR/EDR/LE: Link Establishment (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded.
+C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR,
+	otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Central BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_41_1	True		Central BR/EDR/LE: Security Aspects (M)
+-------------------------------------------------------------------------------
+
+
+		Peripheral BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_42_1	True		Peripheral BR/EDR/LE: Non-Discoverable Mode
+				(See Spec)
+TSPC_GAP_42_2	True		Peripheral BR/EDR/LE: Discoverable Mode
+				(See Spec)
+TSPC_GAP_42_3	True		Peripheral BR/EDR/LE: Non-Connectable Mode
+				(See Spec)
+TSPC_GAP_42_4	True		Peripheral BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_42_5	True		Peripheral BR/EDR/LE: Non-Bondable Mode
+				(See Spec)
+TSPC_GAP_42_6	True		Peripheral BR/EDR/LE: Bondable Mode (See Spec)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
+	otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Peripheral BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_43_1	True		Peripheral BR/EDR/LE: Non-Discoverable Mode
+-------------------------------------------------------------------------------
+
+
+		Central Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_44_1	True		Central BR/EDR/LE: Simultaneous BR/EDR and LE
+					Transports – BR/EDR Slave to the same
+					device (O)
+TSPC_GAP_44_2	True		Central BR/EDR/LE: Simultaneous BR/EDR and LE
+					Transports – BR/EDR Master to the same
+					device (O)
+-------------------------------------------------------------------------------
+
+
+		Peripheral Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_45_1	True		Simultaneous BR/EDR and LE Transports – BR/EDR
+					Slave to the same device (C.1)
+TSPC_GAP_45_2	True		Simultaneous BR/EDR and LE Transports – BR/EDR
+					Master to the same device (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15
+(Core Spec Version 4.1+HS)) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1	True		GATT Client Role (O)
+TSPC_GATT_1_2	True		GATT Server Role (O)
+TSPC_SM_1_1	True		Master Role (Initiator)
+TSPC_SM_1_2	True		Slave Role (Responder)
+TSPC_SM_2_4	True		OOB supported (O)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-gatt.txt b/bluez/repo/android/pics-gatt.txt
new file mode 100644
index 0000000..90585f5
--- /dev/null
+++ b/bluez/repo/android/pics-gatt.txt
@@ -0,0 +1,326 @@
+GATT PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Generic Attribute Profile Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1	True		Generic Attribute Profile Client (C.1)
+TSPC_GATT_1_2	True		Generic Attribute Profile Server (C.2)
+TSPC_GATT_1A_1	True		Complete GATT client (C.3)
+TSPC_GATT_1A_2	True		Complete GATT server (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
+	to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is
+	mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		ATT Bearer Transport
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_2_1	True		Attribute Protocol Supported over BR/EDR
+					(L2CAP fixed channel support) (C.1)
+TSPC_GATT_2_2	True		Attribute Protocol Supported over LE (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise
+	Excluded
+C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise
+	Excluded
+-------------------------------------------------------------------------------
+
+
+
+		Generic Attribute Profile Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_3_1	True		Client: Exchange MTU (C.2)
+TSPC_GATT_3_2	True		Client: Discover All Primary Services (C.1)
+TSPC_GATT_3_3	True		Client: Discover Primary Services Service
+					UUID (C.1)
+TSPC_GATT_3_4	True		Client: Find Included Services (C.1)
+TSPC_GATT_3_5	True		Client: Discover All characteristics of a
+					Service (C.1)
+TSPC_GATT_3_6	True		Client: Discover Characteristics by UUID (C.1)
+TSPC_GATT_3_7	True		Client: Discover All Characteristic Descriptors
+					(C.1)
+TSPC_GATT_3_8	True		Client: Read Characteristic Value (C.1)
+TSPC_GATT_3_9	True		Client: Read using Characteristic UUID (C.1)
+TSPC_GATT_3_10	True		Client: Read Long Characteristic Values (C.1)
+TSPC_GATT_3_11	False (*)	Client: Read Multiple Characteristic
+					Values (C.1)
+TSPC_GATT_3_12	True		Client: Write without Response (C.1)
+TSPC_GATT_3_13	True		Client: Signed Write Without Response (C.1)
+TSPC_GATT_3_14	True		Client: Write Characteristic Value (C.1)
+TSPC_GATT_3_15	True		Client: Write Long Characteristic Values (C.1)
+TSPC_GATT_3_16	True		Client: Characteristic Value Reliable
+					Writes (C.1)
+TSPC_GATT_3_17	True		Client: Notifications (C.1)
+TSPC_GATT_3_18	True		Client: Indications (M)
+TSPC_GATT_3_19	True		Client: Read Characteristic Descriptors (C.1)
+TSPC_GATT_3_20	True		Client: Read long Characteristic Descriptors
+					(C.1)
+TSPC_GATT_3_21	True		Client: Write Characteristic Descriptors (C.1)
+TSPC_GATT_3_22	True		Client: Write Long Characteristic Descriptors
+					(C.1)
+TSPC_GATT_3_23	True		Client: Service Changed Characteristic (M)
+TSPC_GATT_3B_1	True		Client: Primary Service Declaration (M)
+TSPC_GATT_3B_2	True		Client: Secondary Service Declaration (M)
+TSPC_GATT_3B_3	True		Client: Include Declaration (M)
+TSPC_GATT_3B_4	True		Client: Characteristic Declaration (M)
+TSPC_GATT_3B_5	True		Client: Characteristic Value Declaration (M)
+TSPC_GATT_3B_6	True		Client: Characteristic Extended Properties (M)
+TSPC_GATT_3B_7	True		Client: Characteristic User Description
+					Descriptor (M)
+TSPC_GATT_3B_8	True		Client: Client Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_3B_9	True		Client: Server Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_3B_10	True		Client: Characteristic Format Descriptor (M)
+TSPC_GATT_3B_11	True		Client: Characteristic Aggregate Format
+					Descriptor (M)
+TSPC_GATT_3B_12	True		Client: Characteristic Format: Boolean (M)
+TSPC_GATT_3B_13	True		Client: Characteristic Format: 2Bit (M)
+TSPC_GATT_3B_14	True		Client: Characteristic Format: nibble (M)
+TSPC_GATT_3B_15	True		Client: Characteristic Format: Uint8 (M)
+TSPC_GATT_3B_16	True		Client: Characteristic Format: Uint12 (M)
+TSPC_GATT_3B_17	True		Client: Characteristic Format: Uint16 (M)
+TSPC_GATT_3B_18	True		Client: Characteristic Format: Uint24 (M)
+TSPC_GATT_3B_19	True		Client: Characteristic Format: Uint32 (M)
+TSPC_GATT_3B_20	True		Client: Characteristic Format: Uint48 (M)
+TSPC_GATT_3B_21	True		Client: Characteristic Format: Uint64 (M)
+TSPC_GATT_3B_22	True		Client: Characteristic Format: Uint128 (M)
+TSPC_GATT_3B_23	True		Client: Characteristic Format: Sint8 (M)
+TSPC_GATT_3B_24	True		Client: Characteristic Format: Sint12 (M)
+TSPC_GATT_3B_25	True		Client: Characteristic Format: Sint16 (M)
+TSPC_GATT_3B_26	True		Client: Characteristic Format: Sint24 (M)
+TSPC_GATT_3B_27	True		Client: Characteristic Format: Sint32 (M)
+TSPC_GATT_3B_28	True		Client: Characteristic Format: Sint48 (M)
+TSPC_GATT_3B_29	True		Client: Characteristic Format: Sint64 (M)
+TSPC_GATT_3B_30	True		Client: Characteristic Format: Sint128 (M)
+TSPC_GATT_3B_31	True		Client: Characteristic Format: Float32 (M)
+TSPC_GATT_3B_32	True		Client: Characteristic Format: Float64 (M)
+TSPC_GATT_3B_33	True		Client: Characteristic Format: SFLOAT (M)
+TSPC_GATT_3B_34	True		Client: Characteristic Format: FLOAT (M)
+TSPC_GATT_3B_35	True		Client: Characteristic Format: Duint16 (M)
+TSPC_GATT_3B_36	True		Client: Characteristic Format: utf8s (M)
+TSPC_GATT_3B_37	True		Client: Characteristic Format: utf16s (M)
+TSPC_GATT_3B_38	True		Client: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_3 is supported, otherwise Optional
+C.2: Mandatory IF TSPC_GATT_1_3 AND TSPC_GATT_2_2 is supported, otherwise
+	Excluded
+-------------------------------------------------------------------------------
+
+
+
+		Generic Attribute Profile Support, by Server
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4_1	True		Server: Exchange MTU (C.4)
+TSPC_GATT_4_2	True		Server: Discover All Primary Services (M)
+TSPC_GATT_4_3	True		Server: Discover Primary Services Service
+					UUID (M)
+TSPC_GATT_4_4	True		Server: Find Included Services (M)
+TSPC_GATT_4_5	True		Server: Discover All characteristics of
+					a Service (M)
+TSPC_GATT_4_6	True		Server: Discover Characteristics by UUID (M)
+TSPC_GATT_4_7	True		Server: Discover All Characteristic
+					Descriptors (M)
+TSPC_GATT_4_8	True		Server: Read Characteristic Value (M)
+TSPC_GATT_4_9	True		Server: Read using Characteristic UUID (M)
+TSPC_GATT_4_10	True		Server: Read Long Characteristic Values (C.4)
+TSPC_GATT_4_11	False (*)	Server: Read Multiple Characteristic
+					Values (C.4)
+TSPC_GATT_4_12	True		Server: Write without Response (C.2)
+TSPC_GATT_4_13	True		Server: Signed Write Without Response (C.4)
+TSPC_GATT_4_14	True		Server: Write Characteristic Value (C.3)
+TSPC_GATT_4_15	True		Server: Write Long Characteristic Values (C.4)
+TSPC_GATT_4_16	True		Server: Characteristic Value Reliable
+					Writes (C.4)
+TSPC_GATT_4_17	True		Server: Notifications (C.4)
+TSPC_GATT_4_18	True		Server: Indications (C.1)
+TSPC_GATT_4_19	True		Server: Read Characteristic Descriptors (C.4)
+TSPC_GATT_4_20	True		Server: Read long Characteristic
+					Descriptors (C.4)
+TSPC_GATT_4_21	True		Server: Write Characteristic Descriptors (C.4)
+TSPC_GATT_4_22	True		Server: Write Long Characteristic
+					Descriptors (C.4)
+TSPC_GATT_4_23	True		Server: Service Changed Characteristic (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF service definitions on the server can be added, changed, or
+	removed, otherwise Optional
+C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional
+C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional
+C.4: Mandatory IF GATT TSPC_GATT_1_4 is supported, otherwise Optional
+-------------------------------------------------------------------------------
+
+
+		Profile Attribute Types and Characteristic Formats
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4B_1	True		Server: Primary Service Declaration (M)
+TSPC_GATT_4B_2	True		Server: Secondary Service Declaration (M)
+TSPC_GATT_4B_3	True		Server: Include Declaration (M)
+TSPC_GATT_4B_4	True		Server: Characteristic Declaration (M)
+TSPC_GATT_4B_5	True		Server: Characteristic Value Declaration (M)
+TSPC_GATT_4B_6	True		Server: Characteristic Extended Properties (M)
+TSPC_GATT_4B_7	True		Server: Characteristic User Description
+					Descriptor (M)
+TSPC_GATT_4B_8	True		Server: Client Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_4B_9	True		Server: Server Characteristic Configuration
+					Descriptor (M)
+TSPC_GATT_4B_10	True		Server: Characteristic Format Descriptor (M)
+TSPC_GATT_4B_11	True		Server: Characteristic Aggregate Format
+					Descriptor (M)
+TSPC_GATT_4B_12	True		Server: Characteristic Format: Boolean (M)
+TSPC_GATT_4B_13	True		Server: Characteristic Format: 2Bit (M)
+TSPC_GATT_4B_14	True		Server: Characteristic Format: nibble (M)
+TSPC_GATT_4B_15	True		Server: Characteristic Format: Uint8 (M)
+TSPC_GATT_4B_16	True		Server: Characteristic Format: Uint12 (M)
+TSPC_GATT_4B_17	True		Server: Characteristic Format: Uint16 (M)
+TSPC_GATT_4B_18	True		Server: Characteristic Format: Uint24 (M)
+TSPC_GATT_4B_19	True		Server: Characteristic Format: Uint32 (M)
+TSPC_GATT_4B_20	True		Server: Characteristic Format: Uint48 (M)
+TSPC_GATT_4B_21	True		Server: Characteristic Format: Uint64 (M)
+TSPC_GATT_4B_22	True		Server: Characteristic Format: Uint128 (M)
+TSPC_GATT_4B_23	True		Server: Characteristic Format: Sint8 (M)
+TSPC_GATT_4B_24	True		Server: Characteristic Format: Sint12 (M)
+TSPC_GATT_4B_25	True		Server: Characteristic Format: Sint16 (M)
+TSPC_GATT_4B_26	True		Server: Characteristic Format: Sint24 (M)
+TSPC_GATT_4B_27	True		Server: Characteristic Format: Sint32 (M)
+TSPC_GATT_4B_28	True		Server: Characteristic Format: Sint48 (M)
+TSPC_GATT_4B_29	True		Server: Characteristic Format: Sint64 (M)
+TSPC_GATT_4B_30	True		Server: Characteristic Format: Sint128 (M)
+TSPC_GATT_4B_31	True		Server: Characteristic Format: Float32 (M)
+TSPC_GATT_4B_32	True		Server: Characteristic Format: Float64 (M)
+TSPC_GATT_4B_33	True		Server: Characteristic Format: SFLOAT (M)
+TSPC_GATT_4B_34	True		Server: Characteristic Format: FLOAT (M)
+TSPC_GATT_4B_35	True		Server: Characteristic Format: Duint16 (M)
+TSPC_GATT_4B_36	True		Server: Characteristic Format: utf8s (M)
+TSPC_GATT_4B_37	True		Server: Characteristic Format: utf16s (M)
+TSPC_GATT_4B_38	True		Server: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+
+
+		Generic Attribute Profile Service
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_6_2	True		Discover GATT Services using Service Discovery
+					Profile (C.1)
+TSPC_GATT_6_3	True		Publish SDP record for GATT services support
+					via BR/EDR (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Attribute Protocol Transport Security
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GATT_7_1	True		Security Mode 4 (C.1)
+TSPC_GATT_7_2	True		LE Security Mode 1 (C.2)
+TSPC_GATT_7_3	True		LE Security Mode 2 (C.2)
+TSPC_GATT_7_4	True		LE Authentication Procedure (C.2)
+TSPC_GATT_7_5	True		LE connection data signing procedure (C.2)
+TSPC_GATT_7_6	True		LE Authenticate signed data procedure (C.2)
+TSPC_GATT_7_7	True		LE Authorization Procedure (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded
+C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Attribute Protocol Client Messages
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ATT_3_1	True		Attribute Error Response (M)
+TSPC_ATT_3_2	True		Exchange MTU Request (O)
+TSPC_ATT_3_4	True		Find Information Request (O)
+TSPC_ATT_3_6	True		Find by Type Value Request (O)
+TSPC_ATT_3_8	True		Read by Type Request (O)
+TSPC_ATT_3_10	True		Read Request (O)
+TSPC_ATT_3_12	True		Read Blob Request (O)
+TSPC_ATT_3_14	False (*)	Read Multiple Request (O)
+TSPC_ATT_3_16	True		Read by Group Type Request (O)
+TSPC_ATT_3_17	True		Read by Group Type Response (C.6)
+TSPC_ATT_3_18	True		Write Request (O)
+TSPC_ATT_3_20	True		Write Command (O)
+TSPC_ATT_3_21	True		Signed Write Command (O)
+TSPC_ATT_3_22	True		Prepare Write Request (O)
+TSPC_ATT_3_24	True		Execute Write Request (C.8)
+TSPC_ATT_3_26	True		Handle Value Notification (M)
+TSPC_ATT_3_28	True		Handle Value Confirmation (M)
+-------------------------------------------------------------------------------
+C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
+C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+		Attribute Protocol Server Messages
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ATT_4_1	True		Attribute Error Response (M)
+TSPC_ATT_4_3	True		Exchange MTU Response (M)
+TSPC_ATT_4_5	True		Find Information Response (M)
+TSPC_ATT_4_7	True		Find by Type Value Response (M)
+TSPC_ATT_4_8	True		Read by Type Request (M)
+TSPC_ATT_4_9	True		Read by Type Response (M)
+TSPC_ATT_4_11	True		Read Response (M)
+TSPC_ATT_4_15	False (*)	Read Multiple Response (C.2)
+TSPC_ATT_4_17	True		Read by Group Type Response (M)
+TSPC_ATT_4_19	True		Write Response (C.3)
+TSPC_ATT_4_20	True		Write Command (O)
+TSPC_ATT_4_21	True		Signed Write Command (O)
+TSPC_ATT_4_23	True		Prepare Write Response (C.4)
+TSPC_ATT_4_25	True		Execute Write Response (C.4)
+TSPC_ATT_4_26	True		Handle Value Notification (O)
+TSPC_ATT_4_27	True		Handle Value Indication	(O)
+-------------------------------------------------------------------------------
+C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
+C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
+C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded
+C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Attribute Protocol Transport
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ATT_5_2	True		LE Security Mode 1 (C.2)
+TSPC_ATT_5_4	True		LE Authentication Procedure (C.2)
+TSPC_ATT_5_7	True		LE Authorization Procedure (C.2)
+-------------------------------------------------------------------------------
+C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE),
+	otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Device Configuration
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0_2	True		LE (C.2)
+-------------------------------------------------------------------------------
+C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP))
+	is supported, otherwise Excluded
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-gavdp.txt b/bluez/repo/android/pics-gavdp.txt
new file mode 100644
index 0000000..8fa5ac1
--- /dev/null
+++ b/bluez/repo/android/pics-gavdp.txt
@@ -0,0 +1,38 @@
+GAVDP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAVDP_1_1	True (*)	Initiator (C.1)
+TSPC_GAVDP_1_2	True (*)	Initiator (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if Acceptor/Initiator is not supported
+-------------------------------------------------------------------------------
+
+		GAVDP Procedures (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAVDP_2_1	True		Connection Establishment (M)
+TSPC_GAVDP_2_2	True (*)	Transfer Control -Suspend (O)
+TSPC_GAVDP_2_3	False		Transfer Control – Change Parameters (O)
+-------------------------------------------------------------------------------
+
+
+		GAVDP Procedures (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_GAVDP_3_1	True		Connection Establishment (M)
+TSPC_GAVDP_3_2	True (*)	Transfer Control -Suspend (O)
+TSPC_GAVDP_3_3	False		Transfer Control – Change Parameters (O)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-hdp.txt b/bluez/repo/android/pics-hdp.txt
new file mode 100644
index 0000000..a613c97
--- /dev/null
+++ b/bluez/repo/android/pics-hdp.txt
@@ -0,0 +1,307 @@
+HDP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+		Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_0_1	False		HDP 1.0 (C.1)
+TSPC_HDP_0_2	True		HDP 1.1 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Profile version.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_1_1	True		Supports Source Role (C.1, C.2)
+TSPC_HDP_1_2	True (*)	Supports Sink Role (C.1, C.3)
+-------------------------------------------------------------------------------
+C.1: At least one of the defined roles is Mandatory.
+C.2: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded.
+C.3: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		GAP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_2_1	True		Supports General-discoverable Mode (M)
+TSPC_HDP_2_2	True		Supports bondable Mode (M) (C.1)
+TSPC_HDP_2_3	True		Supports Response to Authentication requests (M)
+TSPC_HDP_2_4	True		Supports Initiation of Authentication (M) (C.2)
+TSPC_HDP_2_5	True		Supports Acceptance of Encryption request (M)
+TSPC_HDP_2_6	True		Supports Initiation of Encryption (M) (C.3)
+TSPC_HDP_2_7	True (*)	Supports General Inquiry (C.5) (C.4)
+TSPC_HDP_2_8	True		Supports Acceptance of Bonding requests (M)
+TSPC_HDP_2_9	True (*)	Supports Initiation of Bonding (O)
+TSPC_HDP_2_10	True (*)	Supports Extended Inquiry Response (C.7)
+TSPC_HDP_2_11	False		Supports use of Health Class of Device (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
+C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
+C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed, otherwise Optional.
+C.4: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
+C.5: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
+C.6: Mandatory if Bluetooth Core Specification 2.1 + EDR or later
+	(Not SUM ICS 31/4) and Table 2/1 (Supports General-discoverable Mode)
+	is supported, otherwise Optional if Bluetooth Core Specification 2.1
+	+ EDR or later (Not SUM ICS 31/4) is supported, otherwise Excluded.
+C.7: Mandatory if Bluetooth Core specification 2.1 + EDR or later is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		L2CAP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_3_1	True		Supports Reliable Control Channel (C.1)
+TSPC_HDP_3_2	True		Uses FCS for Control Channel (M)
+TSPC_HDP_3_3	True		Supports Reliable Data Channel (C.1)
+TSPC_HDP_3_4	True		Can send data using SAR in ERTM (C.2)
+TSPC_HDP_3_5	True (*)	Uses FCS for Reliable Data Channel (O)
+TSPC_HDP_3_6	True (*)	Supports FCS option of "No FCS" for Reliable
+				Data Channel (C.3)
+TSPC_HDP_3_7	True		Supports Streaming Data Channel (C.4)
+TSPC_HDP_3_8	True (*)	Can send data using SAR in SM (C.5)
+TSPC_HDP_3_9	True (*)	Uses FCS for Steaming Data Channel (C.6)
+TSPC_HDP_3_10	True (*)	Supports FCS option of "No FCS" for Streaming
+				(C.7)
+TSPC_HDP_3_11	True		Maximum number of simultaneous Data Channels
+				supported (DCmax) per MCL (C.8)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded.
+C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded.
+C.4: Optional if TSPC_L2CAP_2_13 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_HDP_3_7 and TSPC_L2CAP_2_23 are supported, otherwise
+	Excluded.
+C.6: Optional if TSPC_HDP_3_7 is supported, otherwise Excluded.
+C.7: Optional if TSPC_HDP_3_7 and TSPC_L2CAP_2_14 are supported, otherwise
+	Excluded.
+C.8: >=2 if Table TSPC_HDP_3_7 is claimed, otherwise >=1.
+-------------------------------------------------------------------------------
+
+
+		SDP Attributes - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_4_1	True		Supports advertisement of HDP Service Record
+				(C.1) (C.4)
+TSPC_HDP_4_2	True		Service Class ID List (C.2)
+TSPC_HDP_4_3	True		Protocol Descriptor List (C.2)
+TSPC_HDP_4_4	True		Bluetooth Profile Descriptor List (C.2)
+TSPC_HDP_4_5	True (*)	Additional Protocol Descriptor Lists (C.2)
+TSPC_HDP_4_6	True (*)	Service Name (O)
+TSPC_HDP_4_7	True (*)	Service Description (O)
+TSPC_HDP_4_8	True (*)	Provider Name (O)
+TSPC_HDP_4_9	True		HDP Supported Features (MDEP List) (C.3)
+TSPC_HDP_4_10	True		MCAP Data Exchange Specification (C.3)
+TSPC_HDP_4_11	True		MCAP Supported Procedures (C.3)
+TSPC_HDP_4_12	True (*)	Service Record State (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_HDP_4_1 is supported, otherwise Excluded.
+C.4: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported.
+-------------------------------------------------------------------------------
+
+
+		Device Identification - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_5_1	True		Device Identification Profile v1.3 or later
+				(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		HDP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_6_1	True		Supports Standard Op Codes (M)
+TSPC_HDP_6_2	True (*)	Supports Initiate creation of Control and Data
+				Channels (C.3) (C.7) (C.1 - MCAP Status)
+TSPC_HDP_6_3	True		Supports Accept creation of Control and Data
+				Channels (C.3) (C.8) (C.1 - MCAP Status)
+TSPC_HDP_6_4	False		Supports Initiate Reconnection of MDL (O)
+				(C.2 - MCAP Status)
+TSPC_HDP_6_5	True		Supports Accept Reconnection of MDL (C.4)
+TSPC_HDP_6_6	False		Supports Clock Synchronization Protocol (O)
+TSPC_HDP_6_7	False (*)	Supports Sync-Slave (C.5)
+TSPC_HDP_6_8	False		Supports Sync-Master (C.6)
+-------------------------------------------------------------------------------
+C.1: If TSPC_HDP_6_1 is supported, at least one is Mandatory, otherwise
+	Excluded.
+C.2: Optional if TSPC_HDP_6_1 is supported, otherwise Excluded.
+C.3: Mandatory to support at least one.
+C.4: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded.
+
+C.5: Mandatory if TSPC_HDP_6_6 is supported, otherwise Excluded.
+C.6: Optional if TSPC_HDP_6_6 is supported, otherwise Excluded.
+C.7: Mandatory to support SDP Client Role (SDP 1b/2) if this item is supported.
+C.8: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported.
+-------------------------------------------------------------------------------
+
+
+		Data Exchange Features - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_7_1	False		Supports Initiation of Echo Test (O)
+TSPC_HDP_7_2	True		Supports Acceptance of Echo Test (M)
+TSPC_HDP_7_3	True		Supports IEEE 11073-20601 (M)
+TSPC_HDP_7_4	False (*)	Supports IEEE 11073-20601 Agent Role (C.1)
+TSPC_HDP_7_5	True (*)	Supports IEEE 11073-20601 Manager Role (C.1)
+TSPC_HDP_7_6	False		Supports Initiation of Association Release (O)
+-------------------------------------------------------------------------------
+C.1: If TSPC_HDP_7_3 is supported, at least one is Mandatory, otherwise
+	Excluded.
+-------------------------------------------------------------------------------
+
+
+		GAP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_8_1	True		Supports General-discoverable Mode (M)
+TSPC_HDP_8_2	True		Supports Bondable Mode (M) (C.1)
+TSPC_HDP_8_3	True		Supports Response to Authentitaction requests
+				(M)
+TSPC_HDP_8_4	True		Supports Initiation of Authentication (M) (C.2)
+TSPC_HDP_8_5	True		Supports Acceptance of Encryption request (M)
+TSPC_HDP_8_6	True		Supports Initiation of Encryption (M) (C.3)
+TSPC_HDP_8_7	True		Supports General Inquiry (M) C.4)
+TSPC_HDP_8_8	True		Supports Acceptance of Bonding requests (M)
+TSPC_HDP_8_9	True (*)	Supports Initiation of Bonding (O)
+TSPC_HDP_8_10	True (*)	Supports Extended Inquiry Response (C.5) (C.6)
+TSPC_HDP_8_11	False		Supports use of Health Class of Device (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional.
+C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
+C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed (Not SUM ICS 31/4),
+	otherwise Optional.
+C.4: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional.
+C.5: Mandatory if Bluetooth Core Specification 2.1 + EDR or later
+	(Not SUM ICS 31/4) and TSPC_HDP_8_1 is supported, otherwise Optional
+	if Bluetooth Core Specification 2.1 + EDR or later is supported,
+	otherwise Excluded.
+C.6: Mandatory if Bluetooth Core specification 2.1 + EDR or later
+	(Not SUM ICS 31/4) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+
+		L2CAP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_9_1	True		Supports Reliable Control Channel (C.1)
+TSPC_HDP_9_2	True		Uses FCS for Control Channel (M)
+TSPC_HDP_9_3	True		Supports Reliable Data Channel (C.1)
+TSPC_HDP_9_4	True		Can send data using SAR in ERTM (C.2)
+TSPC_HDP_9_5	True (*)	Uses FCS for Reliable Data Channel (O)
+TSPC_HDP_9_6	True (*)	Supports FCS option of "No FCS" for Reliable
+				Data Channel (C.3)
+TSPC_HDP_9_7	True		Supports Streaming Data Channel (C.4)
+TSPC_HDP_9_8	True		Can send data using SAR in SM (C.5)
+TSPC_HDP_9_9	True (*)	Uses FCS for Steaming Data Channel (O)
+TSPC_HDP_9_10	True (*)	Supports FCS option of "No FCS" for Streaming
+				Data Channel (C.3)
+TSPC_HDP_9_11	True		Maximum number of simultaneous Data Channels
+				supported (DCmax) per MCL (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded.
+C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded.
+C.4: Mandatory if TSPC_L2CAP_2_13 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_L2CAP_2_23 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		SDP Attributes - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_10_1	True		Supports advertisement of HDP Service Record (C.1)
+TSPC_HDP_10_2	True		Service Class ID List (M)
+TSPC_HDP_10_3	True		Protocol Descriptor List (M)
+TSPC_HDP_10_4	True		Bluetooth Profile Descriptor List (M)
+TSPC_HDP_10_5	True		Additional Protocol Descriptor Lists (M)
+TSPC_HDP_10_6	True (*)	Service Name (O)
+TSPC_HDP_10_7	True (*)	Service Description (O)
+TSPC_HDP_10_8	True (*)	Provider Name (O)
+TSPC_HDP_10_9	True		HDP Supported Features (MDEP List) (M)
+TSPC_HDP_10_10	True		MCAP Data Exchange Specification (M)
+TSPC_HDP_10_11	True		MCAP Supported Procedures (M)
+TSPC_HDP_10_12	True (*)	Service Record State (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support 10/1 and SDP Server Role (SDP 1b/1).
+-------------------------------------------------------------------------------
+
+
+		Device Identification - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_11_1	True		Device Identification Profile v1.3 or later
+				(M) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 1/2 is supported.
+-------------------------------------------------------------------------------
+
+
+		HDP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_12_1	True		Supports Standard Op Codes (M)
+TSPC_HDP_12_2	True		Supports Initiate creation of Control and Data
+				Channels (C.1) (C.5)
+TSPC_HDP_12_3	True		Supports Accept creation of Control and Data
+				Channels (C.1) (C.6)
+TSPC_HDP_12_4	False		Supports Initiate Reconnection of MDL (O) (C.2)
+TSPC_HDP_12_5	True		Supports Accept Reconnection of MDL (M)
+TSPC_HDP_12_6	False		Supports Clock Synchronization Protocol (O)
+TSPC_HDP_12_7	False		Supports Sync-Slave (C.3)
+TSPC_HDP_12_8	False		Supports Sync-Master (C.6)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_12_1 is supported, otherwise Excluded.
+C.2: Optional if TSPC_HDP_12_1 is supported, otherwise Excluded.
+C.3: Mandatory if TSPC_HDP_12_6 is supported, otherwise Excluded.
+C.4: Optional if TSPC_HDP_12_6 is supported, otherwise Excluded.
+C.5: Mandatory to support 12/2 and SDP Client Role (SDP 1b/2).
+C.6: Mandatory to support 12/3 and SDP Server Role (SDP 1b/1).
+-------------------------------------------------------------------------------
+
+
+		Data Exchange Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HDP_13_1	False		Supports Initiation of Echo Test (O)
+TSPC_HDP_13_2	True		Supports Acceptance of Echo Test (M)
+TSPC_HDP_13_3	True		Supports IEEE 11073-20601 (M)
+TSPC_HDP_13_4	True (*)	Supports IEEE 11073-20601 Agent Role (C.1)
+TSPC_HDP_13_5	False		Supports IEEE 11073-20601 Manager Role (C.1)
+TSPC_HDP_13_6	False		Supports Initiation of Association Release (O)
+-------------------------------------------------------------------------------
+C.1: If TSPC_HDP_13_3 is supported, at least one is Mandatory, otherwise
+	Excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-hfp.txt b/bluez/repo/android/pics-hfp.txt
new file mode 100644
index 0000000..c658a25
--- /dev/null
+++ b/bluez/repo/android/pics-hfp.txt
@@ -0,0 +1,232 @@
+HFP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HFP_0_1	False		Version: Hands-Free Profile v1.5 (O.1)
+TSPC_HFP_0_2	True (*)	Version: Hands-Free Profile v1.6 (O.1)
+TSPC_HFP_0_3	False		Version: Hands-Free Profile v1.7 (O.1)
+-------------------------------------------------------------------------------
+O.1: It is mandatory to support only one of the adopted versions.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HFP_1_1	True (*)	Role: Audio Gateway (AG) (O.1)
+TSPC_HFP_1_2	False		Role: Hands-Free (HF) (O.1)
+-------------------------------------------------------------------------------
+O.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Audio Gateway Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HFP_2_1	True		Connection management (M)
+TSPC_HFP_2_1a	True (*)	SLC initiation during active ongoing call (O)
+TSPC_HFP_2_2	True		Phone Status Information (M)
+TSPC_HFP_2_3	True		Audio connection handling (M)
+TSPC_HFP_2_3a	False		Audio connection establishment independent of
+					a call processing (O)
+TSPC_HFP_2_3b	True (*)	eSCO support in Audio Connection (C.10)
+TSPC_HFP_2_3c	True (*)	Codec negotiation (C.7)
+TSPC_HFP_2_4a	False		Accept an incoming voice call
+					(in-band ring) (C.1)
+TSPC_HFP_2_4b	True (*)	Accept an incoming voice call
+					(no in-band ring) (C.1)
+TSPC_HFP_2_4c	False		Capability to change the "in-band ring"
+					settings (O)
+TSPC_HFP_2_5	True (*)	Reject an incoming voice call (O)
+TSPC_HFP_2_6	True		Terminate a call (M)
+TSPC_HFP_2_7	True		Audio connection transfer during an ongoing
+					call (M)
+TSPC_HFP_2_7a	True (*)	HF-initiated Audio transfer to AG during
+					ongoing call (O)
+TSPC_HFP_2_8	True		Place a call with a phone number supplied by
+					the HF (M)
+TSPC_HFP_2_9	True		Place a call using memory dialing (M)
+TSPC_HFP_2_10	True		Place a call to the last number dialed (M)
+TSPC_HFP_2_11	True		Call waiting notification (M)
+TSPC_HFP_2_12	True (*)	Three Way Calling (O)
+TSPC_HFP_2_12a	True (*)	User Busy (AT+CHLD value 0) (C.3)
+TSPC_HFP_2_12b	True (*)	Call Hold Handling (AT+CHLD value 1,2) (C.2)
+TSPC_HFP_2_12c	True (*)	Three Way Call (AT+CHLD value 3) (C.3)
+TSPC_HFP_2_12d	False		Explicit Call Transfer (AT+CHLD value 4) (C.3)
+TSPC_HFP_2_13	True		Calling Line Identification (CLI) (M)
+TSPC_HFP_2_14	True (*)	Echo canceling (EC) and Noise reduction (NR) (O)
+TSPC_HFP_2_15	True (*)	Voice recognition activation (O)
+TSPC_HFP_2_15a	True (*)	Initiate voice recognition from AG (C.6)
+TSPC_HFP_2_15b	True (*)	Autonomous voice deactivation (C.6)
+TSPC_HFP_2_16	False		Attach a phone number to a voice tag (O)
+TSPC_HFP_2_17	True		Ability to transmit DTMF codes (M)
+TSPC_HFP_2_18a	True (*)	Remote audio volume control – speaker (O)
+TSPC_HFP_2_18b	False		Remote audio volume control – microphone (O)
+TSPC_HFP_2_18c	True (*)	Volume Level Synchronization – speaker and
+					microphone (C.5)
+TSPC_HFP_2_19	False		Response and hold (O)
+TSPC_HFP_2_20	True		Subscriber Number Information (M)
+TSPC_HFP_2_21a	True		Enhanced Call Status (C.4)
+TSPC_HFP_2_21b	False		Enhanced Call Control (C.3)
+TSPC_HFP_2_21c	True (*)	Enhanced Call Status with limited network
+					notification (C.4)
+TSPC_HFP_2_22	False		Support for automatic link loss recovery (O)
+TSPC_HFP_2_23	True		Individual Indicator Activation (C.9)
+TSPC_HFP_2_24	True (*)	Wide Band Speech service (C.8)
+TSPC_HFP_2_25	False		Support roaming function (O)
+TSPC_HFP_2_26	False		HF Indicators (C.11)
+TSPC_HFP_2_27	False		Support CVSD eSCO s4 setting (C.12)
+-------------------------------------------------------------------------------
+C.1:  The AG must support one of item TSPC_HFP_2_4a or TSPC_HFP_2_4b
+C.2:  Mandatory if TSPC_HFP_2_12 is TRUE; otherwise excluded
+C.3:  Optional if TSPC_HFP_2_12 is TRUE; otherwise excluded
+C.4:  The AG must support one of item TSPC_HFP_2_21a or TSPC_HFP_2_21c
+C.5:  Mandatory if TSPC_HFP_2_18a or TSPC_HFP_2_18b; otherwise optional
+C.6:  Optional if TSPC_HFP_2_15 is supported, otherwise excluded
+C.7:  Mandatory if TSPC_HFP_2_24 otherwise excluded
+C.8:  Excluded if TSPC_HFP_0_1 otherwise optional
+C.9:  Excluded if TSPC_HFP_0_1 otherwise mandatory
+C.10: Mandatory if TSPC_HFP_2_27 or TSPC_HFP_2_24 otherwise optional
+C.11: Optional IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is NOT
+	supported, otherwise Excluded.
+C.12: Excluded IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is
+	supported, otherwise Mandatory.
+-------------------------------------------------------------------------------
+
+
+		Hands-Free Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HFP_3_1	False (*)	Connection Management (M)
+TSPC_HFP_3_2a	False (*)	Phone Status Information ("service" and "call"
+					indicators) (M)
+TSPC_HFP_3_2b	False		Phone Status Information ("callsetup"
+					indicators) (O)
+TSPC_HFP_3_2c	False		Accept indicator of signal strength (O)
+TSPC_HFP_3_2d	False		Accept indicator of roaming state ("roam:") (O)
+TSPC_HFP_3_2e	False		Accept indicator of battery level ("battchg") (O)
+TSPC_HFP_3_2f	False		Accept indicator of operator selection (O)
+TSPC_HFP_3_3	False (*)	Audio connection handling (M)
+TSPC_HFP_3_3a	False		Audio connection establishment independent
+					of call processing (O)
+TSPC_HFP_3_3b	False		eSCO support in Audio Connection (C.7)
+TSPC_HFP_3_3c	False		Codec negotiation (C.5)
+TSPC_HFP_3_4a	False (*)	Accept an incoming voice call (in-band ring) (M)
+TSPC_HFP_3_4b	False (*)	Accept an incoming voice call (no in-band
+					ring) (M)
+TSPC_HFP_3_4c	False		Accept an incoming voice call (in-band ring
+					muting) (O)
+TSPC_HFP_3_5	False (*)	Reject an incoming voice call (M)
+TSPC_HFP_3_6	False (*)	Terminate a call (M)
+TSPC_HFP_3_7	False (*)	Audio connection transfer during an ongoing
+					call (M)
+TSPC_HFP_3_7a	False		HF-initiated Audio transfer to AG during
+					ongoing call (O)
+TSPC_HFP_3_8	False		Place a call with a phone number supplied by
+					the HF (O)
+TSPC_HFP_3_9	False		Place a call using memory dialing (O)
+TSPC_HFP_3_10	False		Place a call to the last number dialed (O)
+TSPC_HFP_3_11	False		Call waiting notification (O)
+TSPC_HFP_3_12	False		Three Way Calling (O)
+TSPC_HFP_3_12a	False		Three way calling (AT+CHLD values 0) (C.2)
+TSPC_HFP_3_12b	False		Three way calling (AT+CHLD values 1 and 2) (C.1)
+TSPC_HFP_3_12c	False		Three way calling (AT+CHLD value 3) (C.2)
+TSPC_HFP_3_12d	False		Three way calling (AT+CHLD value 4) (C.2)
+TSPC_HFP_3_12e	False		Originate new call with established call in
+					progress (C.2)
+TSPC_HFP_3_13	False		Calling Line Identification (CLI) (O)
+TSPC_HFP_3_14	False		Echo cancelling (EC) and Noise reduction (NR) (O)
+TSPC_HFP_3_15	False		Voice recognition activation/deactivation (O)
+TSPC_HFP_3_16	False		Attach a phone number to a voice tag (O)
+TSPC_HFP_3_17	False		Ability to transmit DTMF codes (O)
+TSPC_HFP_3_18a	False		Remote audio volume control – speaker (O)
+TSPC_HFP_3_18b	False		Remote audio volume control – microphone (O)
+TSPC_HFP_3_18c	False		Volume Level Synchronization – speaker (C.3)
+TSPC_HFP_3_18d	False		Volume Level Synchronization – microphone (C.4)
+TSPC_HFP_3_18e	False		HF informs AG about local changes of audio
+					volume (O)
+TSPC_HFP_3_18f	False		HF informs AG about local changes of
+					microphone gain (O)
+TSPC_HFP_3_19	False		Response and hold (O)
+TSPC_HFP_3_20	False		Subscriber Number Information (O)
+TSPC_HFP_3_21a	False		Enhanced Call Status (O)
+TSPC_HFP_3_21b	False		Enhanced Call Control (C.2)
+TSPC_HFP_3_22	False		Support for automatic link loss recovery (O)
+TSPC_HFP_3_23	False (*)	Individual Indicator Activation (C.6)
+TSPC_HFP_3_24	False		Wide Band Speech service (C.6)
+TSPC_HFP_3_25	False		HF Indicators (C.8)
+TSPC_HFP_3_26	False		Support CVSD eSCO S4 setting (C.9)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HFP_3_12; otherwise excluded
+C.2: Optional if TSPC_HFP_3_12; otherwise excluded
+C.3: Mandatory if TSPC_HFP_3_18a or TSPC_HFP_3_18b, otherwise optional
+C.4: Mandatory if TSPC_HFP_3_18b, otherwise optional
+C.5: Mandatory if TSPC_HFP_3_24 otherwise excluded
+C.6: Excluded if TSPC_HFP_0_1 otherwise optional
+C.7: Mandatory if TSPC_HFP_3_26 or TSPC_HFP_3_24 otherwise optional
+C.8: Optional IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is NOT
+	supported, otherwise Excluded.
+C.9: Excluded IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is
+	supported, otherwise Mandatory.
+-------------------------------------------------------------------------------
+
+
+		Audio Coding Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HFP_4_1	True		CVSD audio coding over SCO (M)
+TSPC_HFP_4_2	True (*)	mSBC audio coding over eSCO (C.1)
+TSPC_HFP_4_3	True (*)	CVSD audio coding over eSCO (Initiating) (C.2)
+TSPC_HFP_4_2	True (*)	CVSD audio coding over eSCO (Accepting) (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or
+	TSPC_HFP_3_24, otherwise excluded
+C.2: Mandatory IF TPSC_HFP_2_3b OR TSPC_HFP_3_3b; otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Supplementary Interoperability Verification
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HFP_8_1	True (*)	Multiple audio transfers during call –
+					AG and HF initiated (C.1)
+TSPC_HFP_8_2	True (*)	Audio transfer by SLC release during
+					an active call (C.1)
+TSPC_HFP_8_3	True (*)	Audio transfer by powering ON HF (O)
+TSPC_HFP_8_4	True (*)	SLC during SDP response (O)
+TSPC_HFP_8_5	True (*)	Handle dynamic server channel number for HFP
+					service (O)
+TSPC_HFP_8_6	False		HF disallows connections in non-discoverable
+					mode (C.2)
+TSPC_HFP_8_7	True (*)	HF connects to AG during incoming call (O)
+TSPC_HFP_8_8	True (*)	Link loss during incoming call (C.3)
+TSPC_HFP_8_9	True (*)	SLC release during incoming call (C.3)
+TSPC_HFP_8_10	True (*)	Voice Recognition Activation (C.4)
+TSPC_HFP_8_11	True (*)	Place outgoing call by dialing number on
+					the AG (O)
+TSPC_HFP_8_12	True (*)	Active call termination – NO CARRIER signal
+					(C.5)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_HFP_2_7a or TSPC_HFP_3_7a is supported,
+	otherwise excluded
+C.2: Optional if TSPC_HFP_1_2 is supported, otherwise excluded
+C.3: Optional if TSPC_HFP_1_1 is supported, otherwise excluded
+C.4: Optional if TSPC_HFP_2_15 or TSPC_HFP_3_15 is supported,
+	otherwise excluded
+C.5: Optional if TSPC_HFP_2_6 is supported, otherwise excluded
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-hid.txt b/bluez/repo/android/pics-hid.txt
new file mode 100644
index 0000000..875f9b7
--- /dev/null
+++ b/bluez/repo/android/pics-hid.txt
@@ -0,0 +1,291 @@
+HID PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_1_1	True (*)	Role: Host, Report protocol (O.1)
+TSPC_HID_1_2	False		Role: HID Role (O.1)
+TSPC_HID_1_3	False		Role: Host, Boot protocol (O.1)
+-------------------------------------------------------------------------------
+O.1: It is Mandatory to support One of these roles.
+-------------------------------------------------------------------------------
+
+
+		Application Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_2_1	True (*)	Host: Establish HID connection (C.4)
+TSPC_HID_2_2	True (*)	Host: Accept HID connection (C.4)
+TSPC_HID_2_3	True (*)	Host: Terminate HID connection (C.4)
+TSPC_HID_2_4	True (*)	Host: Accept termination of HID connection (C.4)
+TSPC_HID_2_5	True (*)	Host: Support for virtual cables (C.4)
+TSPC_HID_2_6	True (*)	Host: HID initiated connection (C.4)
+TSPC_HID_2_7	True (*)	Host: Host initiated connection (C.4)
+TSPC_HID_2_8	True (*)	Host: Host data transfer to HID (C.1)
+TSPC_HID_2_9	True (*)	Host: HID data transfer to Host (C.1)
+TSPC_HID_2_10	False		Host: Boot mode data transfer to Host (C.2)
+TSPC_HID_2_11	False		Host : Boot mode data transfer to HID (C.2)
+TSPC_HID_2_12	False		Host : Support for Application to send
+					GET_Report (O)
+TSPC_HID_2_13	False		Host : Support for Application to send
+					SET_REPORT (O)
+TSPC_HID_2_14	False		Host : Support for sending HCI_CONTROL with
+					VIRTUAL_CABLE_UNPLUG (C.3)
+TSPC_HID_2_15	False		Host : Support for receiving HCI_CONTROL with
+					VIRTUAL_CABLE_UNPLUG (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional for Boot Mode Only Hosts (TSPC_HID_1_3); Mandatory for Host Role
+	(TSPC_HID_1_1); OTHERWISE Excluded.
+C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional.
+C.3: Optional IF (TSPC_HID_2_5) supported, otherwise excluded.
+C.4: Mandatory IF TSPC_HID_1_1 (Host, Report protocol) is supported, otherwise
+	Optional.
+-------------------------------------------------------------------------------
+
+
+		Device to Host Transfers
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_3_1	False		Host : Data reports larger than host MTU on
+					Control channel (O)
+TSPC_HID_3_2	True (*)	Host : Data reports larger than host MTU on
+					Interrupt channel (C.1)
+TSPC_HID_3_3	True (*)	Host : Data reports to host (C.1)
+TSPC_HID_3_4	False		Host : Boot mode reports to host (C.2)
+-------------------------------------------------------------------------------
+C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); Mandatory IF
+	TSPC_HID_2_12 is supported, otherwise Optional.
+C.2: Mandatory IF TSPC_HID_1_3 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Host to Device Transfers
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_4_1	False		Host : Data reports larger than device MTU on
+					Control channel (C.1)
+TSPC_HID_4_2	False		Host : Data reports larger than device MTU on
+					Interrupt channel (C.1)
+TSPC_HID_4_3	True (*)	Host : Data reports to device (C.2)
+TSPC_HID_4_4	False		Host : Boot mode reports to device (O)
+-------------------------------------------------------------------------------
+C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional
+C.2: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory for
+	Host Role (TSPC_HID_1_1).
+-------------------------------------------------------------------------------
+
+
+		HID Control Commands
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_5_1	False		Host : Set_Protocol command (C.1, C.4)
+TSPC_HID_5_2	False		Host : Get_Protocol command (C.1, C.4)
+TSPC_HID_5_3	False		Host : Set_Idle command (O)
+TSPC_HID_5_4	False		Host : Get_Idle command (O)
+TSPC_HID_5_5	False		Host : Set_Report command (C.2)
+TSPC_HID_5_6	False		Host : Get_Report command (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional.
+C.2: Mandatory IF (TSPC_HID_1_1) supported AND (TSPC_HID_2_13) supported.
+C.3: Mandatory IF (TSPC_HID_1_1) Supported AND (TSPC_HID_2_12) Supported
+C.4: Mandatory to support TSPC_HID_5_1 (Set_Protocol command) AND TSPC_HID_5_2
+	(Get_Protocol command) IF one of TSPC_HID_5_1 (Set_Protocol command)
+	OR TSPC_HID_5_2 (Get_Protocol command) is supported, otherwise
+	Excluded.
+-------------------------------------------------------------------------------
+
+
+		Host Link Manager Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_6_1	False		Host : Initiate Authentication before
+					connection completed (C.1)
+TSPC_HID_6_2	False		Host : Initiate Authentication after connection
+					completed (C.1)
+TSPC_HID_6_3	False		Host : Initiate pairing before connection
+					completed (C.2)
+TSPC_HID_6_4	False		Host : Initiate pairing after connection
+					completed (C.2)
+TSPC_HID_6_5	False		Host : Encryption (O)
+TSPC_HID_6_6	False		Host : Initiate encryption (C.3)
+TSPC_HID_6_7	False		Host : Accept encryption requests (C.3)
+TSPC_HID_6_8	True (*)	Host : Role switch (Master/Slave) (C.4)
+TSPC_HID_6_9	True (*)	Host : Request Master Slave switch (C.4)
+TSPC_HID_6_10	True (*)	Host : Accept Master Slave switch requests (C.4)
+TSPC_HID_6_11	False		Host : Hold mode (O)
+TSPC_HID_6_12	True (*)	Host : Sniff mode (C.4)
+TSPC_HID_6_13	False		Host : Park mode (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support TSPC_HID_6_1 AND TSPC_HID_6_2 IF GAP 2/3
+	(Initiate LMP-Authentication) is supported, otherwise Excluded.
+C.2: If Pairing supported both (TSPC_HID_6_3) AND (TSPC_HID_6_4) must
+	be supported.
+C.3: Mandatory IF (TSPC_HID_6_5) encryption supported.
+C.4: Mandatory IF (TSPC_HID_1_1) supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Host Link Control Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_7_1	True (*)	Host : Supports inquiry, 79 channel (C.1)
+TSPC_HID_7_2	False		Host : Supports inquiry scan, 79 channel (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support IF (TSPC_HID_1_1) supported, otherwise Excluded.
+C.2: Feature should not be used by a Host, but can be supported in LM.
+-------------------------------------------------------------------------------
+
+
+		HID Device Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_8_1	False		Hid : Pointing HID (O.1)
+TSPC_HID_8_2	False		Hid : Keyboard HID (O.1)
+TSPC_HID_8_3	False		Hid : Identification HID (O.1)
+TSPC_HID_8_4	False		Hid : Other HID (O.1)
+-------------------------------------------------------------------------------
+O.1: It is Mandatory to support One of these roles IF (TSPC_HID_1_2)
+	is selected
+-------------------------------------------------------------------------------
+
+
+		HID Application Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_9_1	False		Hid : Establish HID connection (O)
+TSPC_HID_9_2	False (*)	Hid : Accept HID connection (M)
+TSPC_HID_9_3	False		Hid : Terminate HID connection (O)
+TSPC_HID_9_4	False (*)	Hid : Accept Termination of HID connection (M)
+TSPC_HID_9_5	False		Hid : Support for virtual cables (O)
+TSPC_HID_9_6	False		Hid : HID initiated reconnection (C.1)
+TSPC_HID_9_7	False		Hid : Host initiated reconnection (C.1)
+TSPC_HID_9_8	False		Hid : Host data transfer to HID (C.2)
+TSPC_HID_9_9	False		Hid : HID data transfer to Host (C.2)
+TSPC_HID_9_10	False		Hid : HID Boot mode data transfer to Host (C.3)
+TSPC_HID_9_11	False		Hid : Host Boot mode data transfer to HID (C.4)
+TSPC_HID_9_12	False		Hid : Output reports declared (C.4)
+TSPC_HID_9_13	False		Hid : Input reports declared (C.3)
+TSPC_HID_9_14	False		Hid : Feature reports declared (O)
+TSPC_HID_9_15	False		Hid : Support for sending HCI_CONTROL with
+					VIRTUAL_CABLE_UNPLUG (C.5)
+TSPC_HID_9_16	False		Hid : Support for receiving HCI_CONTROL with
+					VIRTUAL_CABLE_UNPLUG (C.5)
+-------------------------------------------------------------------------------
+C.1: One of these is Mandatory IF (TSPC_HID_9_5) is supported
+	(SDP attribute 0x204=True)
+C.2: One of these is Mandatory if TSPC_HID_1_2 (HID Role) is supported.
+C.3: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is selected
+C.4: Mandatory IF (TSPC_HID_8_2) is supported (for status indicators)
+C.5: Optional IF (TSPC_HID_9_5) supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Device to Host Transfers
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_10_1	False		Hid : Data reports larger than host MTU on
+					Control channel (O)
+TSPC_HID_10_2	False		Hid : Data reports larger than host MTU on
+					Interrupt channel (O)
+TSPC_HID_10_3	False		Hid : Data reports to host (O)
+TSPC_HID_10_4	False		Hid : Boot mode reports to host (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported.
+	Optional for other HIDs.
+-------------------------------------------------------------------------------
+
+
+		Host to Device Transfers
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_11_1	False		Hid : Data reports larger than device MTU on
+					Control channel (O)
+TSPC_HID_11_2	False		Hid : Data reports larger than device MTU on
+					Interrupt channel (O)
+TSPC_HID_11_3	False		Hid : Data reports to device (O)
+TSPC_HID_11_4	False		Hid : Boot mode reports to device (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_2) is supported. Optional for other HIDs.
+-------------------------------------------------------------------------------
+
+
+		HID Control Commands
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_12_1	False		Hid : Set_Protocol command (C.1, C.5)
+TSPC_HID_12_2	False		Hid : Get_Protocol command (C.1, C.5)
+TSPC_HID_12_3	False		Hid : Set_Idle command (C.2)
+TSPC_HID_12_4	False		Hid : Get_Idle command (C.2)
+TSPC_HID_12_5	False		Hid : Set_Report command (C.3)
+TSPC_HID_12_6	False		Hid : Get_Report command (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported.
+	Optional for other HIDs. If either Set_Protocol or Get_Protocol
+	supported, both are Mandatory.
+C.2: Mandatory IF (TSPC_HID_8_2) Keyboard is selected. Optional for other HIDs.
+C.3: Mandatory IF (TSPC_HID_9_12) or (TSPC_HID_9_14) supported.
+C.4: Mandatory IF (TSPC_HID_9_13) or (TSPC_HID_9_14) supported
+C.5: If either TSPC_HID_12_1 (Set_Protocol command) OR TSPC_HID_12_2
+	(Get_Protocol command) is supported, both TSPC_HID_12_1
+	(Set_Protocol command) AND TSPC_HID_12_2 (Get_Protocol command) are
+	Mandatory to support
+-------------------------------------------------------------------------------
+
+
+		HID Link Manager Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_13_1	False		Hid : Host initiated Authentication before
+					connection completed (C.1)
+TSPC_HID_13_2	False		Hid : Host initiated Authentication after
+					connection completed (C.1)
+TSPC_HID_13_3	False		Hid : Item no longer used (N/A)
+TSPC_HID_13_4	False		Hid : Item no longer used (N/A)
+TSPC_HID_13_5	False		Hid : Encryption (C.1)
+TSPC_HID_13_6	False		Hid : Initiate encryption (O)
+TSPC_HID_13_7	False		Hid : Accept encryption requests (C.2)
+TSPC_HID_13_8	False		Hid : Role switch (Master/Slave) (C.3)
+TSPC_HID_13_9	False		Hid : Request Master Slave switch (O)
+TSPC_HID_13_10	False		Hid : Accept Master Slave switch requests (C.3)
+TSPC_HID_13_11	False		Hid : Hold mode (O)
+TSPC_HID_13_12	False		Hid : Sniff mode (O)
+TSPC_HID_13_13	False		Hid : Park mode (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_2) OR (TSPC_HID_8_3) is selected. Optional
+	for other HIDs.
+C.2: Mandatory IF (TSPC_HID_13_5) supported.
+C.3: Mandatory IF (TSPC_HID_9_6) is supported.
+-------------------------------------------------------------------------------
+
+
+		HID Link Control Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HID_14_1	False		Hid : Supports inquiry, 79 channel (O)
+TSPC_HID_14_2	False		Hid : Supports inquiry scan, 79 channel (M.1)
+TSPC_ALL	False		Enables all test cases when set to true.
+-------------------------------------------------------------------------------
+M.1: Mandatory IF (TSPC_HID_1_2) is supported.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-hogp.txt b/bluez/repo/android/pics-hogp.txt
new file mode 100644
index 0000000..bd9c9f9
--- /dev/null
+++ b/bluez/repo/android/pics-hogp.txt
@@ -0,0 +1,409 @@
+HOGP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Profile Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_1_1	False (*)	HID Device (Server) (C.1)
+TSPC_HOGP_1_2	True		Report Host (Client) (C.1)
+TSPC_HOGP_1_3	False (*)	Boot Host (Client) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_HOGP_1_1 or TSPC_HOGP_1_2
+	or TSPC_HOGP_1_3.
+-------------------------------------------------------------------------------
+
+
+		Transport
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_2_1	False (*)	Profile supported over BR/EDR (C.1)
+TSPC_HOGP_2_2	True		Profile supported over LE (M)
+-------------------------------------------------------------------------------
+C.1: Excluded for this profile.
+-------------------------------------------------------------------------------
+
+
+		Services - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_3_1	False (*)	Implements HID Service (M.1)
+TSPC_HOGP_3_2	False (*)	Multiple Service instances - HID Service (O)
+TSPC_HOGP_3_3	False (*)	Implements Battery Service (M.1)
+TSPC_HOGP_3_4	False (*)	Implements Device Information Service (M.1)
+TSPC_HOGP_3_5	False (*)	Implements Scan Parameters Service (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+		Features - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_4_1	False (*)	Include HID Service UUID in AD in GAP
+					Discoverable Mode (O)
+TSPC_HOGP_4_2	False (*)	Include Local Name in AD or Scan Response Data
+					(O)
+TSPC_HOGP_4_3	False (*)	Include Appearance in AD or Scan Response Data
+					(O)
+TSPC_HOGP_4_4	False (*)	Support Device Information Service
+					characteristic: PnP ID (M)
+TSPC_HOGP_4_5	False (*)	Report characteristic (C.1)
+TSPC_HOGP_4_6	False (*)	Non-HID Service characteristic described within
+					Report Map characteristic (C.1)
+TSPC_HOGP_4_7	False (*)	External Report Reference characteristic
+					descriptor for Report Map characteristic
+					(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory if TSPC_HOGP_4_6 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		GAP Requirements - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_5_1	False (*)	Peripheral (M.1)
+TSPC_HOGP_5_2	False (*)	Directed Connectable Mode (O)
+TSPC_HOGP_5_3	False (*)	Undirected Connectable Mode (M.1)
+TSPC_HOGP_5_4	False (*)	Bondable mode (peripheral) (M.1)
+TSPC_HOGP_5_5	False (*)	Bonding procedure (peripheral) (M.1)
+TSPC_HOGP_5_6	False (*)	LE Security Mode 1 (peripheral) (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+		SM Requirements - HID Device
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_6_1	False (*)	No security
+					(LE Security Level 1) (M.1)
+TSPC_HOGP_6_2	False (*)	Unauthenticated no MITM protection
+					(LE Security Level 2, Just Works) (M.1)
+TSPC_HOGP_6_3	False (*)	Authenticated MITM protection
+					(LE Security Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_1 selected
+-------------------------------------------------------------------------------
+
+
+		Client Services Support - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_7_1	True		HID Service (M.1)
+TSPC_HOGP_7_2	True		Battery Service (M.1)
+TSPC_HOGP_7_3	True		Device Information Service (M.1)
+TSPC_HOGP_7_4	True		Scan Parameters Service (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 selected
+-------------------------------------------------------------------------------
+
+
+		GATT based Profile Support - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_7a_1	True		Scan Parameters Profile (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 selected
+-------------------------------------------------------------------------------
+
+
+		Client Service Support - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_8_1	False (*)	HID Service (M.1)
+TSPC_HOGP_8_2	False (*)	Battery Service (O)
+TSPC_HOGP_8_3	False (*)	Device Information Service (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+-------------------------------------------------------------------------------
+
+
+		Discover Services & Characteristics - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_9_1	True		Discover HID Service (M.1)
+TSPC_HOGP_9_2	True		Discover Battery Service (M.1)
+TSPC_HOGP_9_3	True		Discover Device Information Service (M.1)
+TSPC_HOGP_9_4	True		Discover Scan Parameters Service (M.1)
+TSPC_HOGP_9_5	True		Discover HID Service characteristic: Report Map
+					(M.1)
+TSPC_HOGP_9_6	True		Discover HID Service characteristic: Report Map
+					- External Report Reference
+					characteristic descriptor (M.1)
+TSPC_HOGP_9_7	True		Discover HID Service characteristic: Report
+					(M.1)
+TSPC_HOGP_9_8	True		Discover HID Service characteristic: Report
+					- Client Characteristic Configuration
+					characteristic descriptor (M.1)
+TSPC_HOGP_9_9	True		Discover HID Service characteristic: Report
+					- Report Reference characteristic
+					descriptor (M.1)
+TSPC_HOGP_9_10	True		Discover HID Service characteristic: HID
+					Information (M.1)
+TSPC_HOGP_9_11	True		Discover HID Service characteristic: HID
+					Control Point (M.1)
+TSPC_HOGP_9_12	True		Discover HID Service characteristic: Protocol
+					Mode (O)
+TSPC_HOGP_9_13	True		Discover Battery Service characteristic: Battery
+					Level (M.1)
+TSPC_HOGP_9_14	True		Discover Battery Service characteristic: Battery
+					Level - Client Characteristic
+					Configuration characteristic descriptor
+					(M.1)
+TSPC_HOGP_9_15	True		Discover Device Information Service
+					characteristic: PnP ID (M.1)
+TSPC_HOGP_9_16	True		Discover non-HID Service characteristic: Report
+					Reference characteristic descriptor
+					(M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 selected
+-------------------------------------------------------------------------------
+
+
+		Discover Services & Characteristics - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_10_1	False (*)	Discover HID Service (M.1)
+TSPC_HOGP_10_2	False (*)	Discover Battery Service (O)
+TSPC_HOGP_10_3	False (*)	Discover Device Information Service (O)
+TSPC_HOGP_10_4	False (*)	Discover HID Service characteristic: Protocol
+					Mode (M.1)
+TSPC_HOGP_10_5	False (*)	Discover HID Service characteristic: Boot
+					Keyboard Input Report (C.1, C.2)
+TSPC_HOGP_10_6	False (*)	Discover HID Service characteristic: Boot
+					Keyboard Input Report - Client
+					Characteristic Configuration
+					characteristic descriptor (C.3)
+TSPC_HOGP_10_7	False (*)	Discover HID Service characteristic: Boot
+					Keyboard Output Report (C.1, C.2)
+TSPC_HOGP_10_8	False (*)	Discover HID Service characteristic: Boot
+					Mouse Input Report (C.1)
+TSPC_HOGP_10_9	False (*)	Discover HID Service characteristic: Boot
+					Mouse Input Report - Client
+					Characteristic Configuration
+					characteristic descriptor (C.4)
+TSPC_HOGP_10_10	False (*)	Discover Battery Service characteristic:
+					Battery Level (O)
+TSPC_HOGP_10_11	False (*)	Discover Battery Service characteristic:
+					Battery Level - Client Characteristic
+					Configuration characteristic descriptor
+					(O)
+TSPC_HOGP_10_12	False (*)	Discover Device Information Service
+					characteristic: PnP ID (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support at least one of TSPC_HOGP_10_5, TSPC_HOGP_10_7, or
+	TSPC_HOGP_10_8.
+C.2: If one of TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, both shall be
+	supported.
+C.3: Mandatory to support if TSPC_HOGP_10_5 is supported, else excluded.
+C.4: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Features - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_11_1	True		Read Report Map characteristic (M.1)
+TSPC_HOGP_11_2	True		Read Report Map characteristic: External
+					Report Reference characteristic
+					descriptor (M.1)
+TSPC_HOGP_11_3	True		Read Report characteristic: Report Type:
+					Input Report (M.1)
+TSPC_HOGP_11_4	True		Write Report characteristic: Report Type:
+					Input Report (M.1)
+TSPC_HOGP_11_5	True		Read Report characteristic: Report Type:
+					Output Report (M.1)
+TSPC_HOGP_11_6	True		Write HID Report characteristic: Report Type:
+					Output Report (M.1)
+TSPC_HOGP_11_7	True		Read HID Report characteristic: Report Type:
+					Feature Report (M.1)
+TSPC_HOGP_11_8	True		Write HID Report characteristic: Report Type:
+					Feature Report (M.1)
+TSPC_HOGP_11_9	True		Read Report characteristic: Report Reference
+					characteristic descriptor (M.1)
+TSPC_HOGP_11_10	True		Read Report characteristic: Input Report:
+					Client Characteristic Configuration
+					characteristic descriptor (M.1)
+TSPC_HOGP_11_11	True		Report characteristic configuration with 0x0001
+					(M.1)
+TSPC_HOGP_11_11a True		Report characteristic configuration with 0x0000
+					(O)
+TSPC_HOGP_11_12	True		Read HID Information characteristic (M.1)
+TSPC_HOGP_11_13	False (*)	Suspend State (O)
+TSPC_HOGP_11_14	False (*)	Exit Suspend State (C.1)
+TSPC_HOGP_11_15	False (*)	Write HID Control Point characteristic: Suspend
+					command (C.1)
+TSPC_HOGP_11_16	False (*)	Write HID Control Point characteristic: Exit
+					Suspend command (C.1)
+TSPC_HOGP_11_17	False (*)	Read Protocol Mode characteristic: Get Protocol
+					command (O)
+TSPC_HOGP_11_18	False (*)	Write Protocol Mode characteristic: Set Report
+					Protocol Mode command (O)
+TSPC_HOGP_11_19	True		Read Battery Level characteristic (M.1)
+TSPC_HOGP_11_20	True		Read Battery Level characteristic: Client
+					Characteristic Configuration
+					characteristic descriptor (M.1)
+TSPC_HOGP_11_21	True		Battery Level characteristic configuration with
+					0x0000 0r 0x0001 (M.1)
+TSPC_HOGP_11_22	True		Read non-HID Service characteristic: Report
+					Reference characteristic descriptor
+					(M.1)
+TSPC_HOGP_11_23	True		Read PnP ID characteristic (M.1)
+TSPC_HOGP_11_24	True		Notify Report characteristic (M.1)
+TSPC_HOGP_11_25	True		Notify Battery Level characteristic (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 selected
+C.1: Mandatory to support if TSPC_HOGP_11_13 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		Features - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_12_1	False (*)	Read Protocol Mode characteristic: Get Protocol
+					Mode command (M.1)
+TSPC_HOGP_12_2	False (*)	Write Protocol Mode characteristic: Set Boot
+					Protocol Mode command (M.1)
+TSPC_HOGP_12_3	False (*)	Read HID Service characteristic: Boot Keyboard
+					Input Report (C.1)
+TSPC_HOGP_12_4	False (*)	Write HID Service characteristic: Boot Keyboard
+					Input Report (C.1)
+TSPC_HOGP_12_5	False (*)	Read Client Characteristic Configuration
+					characteristic descriptor for Boot
+					Keyboard Input Report (C.1)
+TSPC_HOGP_12_6	False (*)	Boot Keyboard Input Report characteristic:
+					configuration with 0x0000 or 0x0001
+					(C.1)
+TSPC_HOGP_12_7	False (*)	Read HID Service characteristic: Boot Keyboard
+					Output Report (C.1)
+TSPC_HOGP_12_8	False (*)	Write HID Service characteristic: Boot Keyboard
+					Output Report (C.1)
+TSPC_HOGP_12_9	False (*)	Read HID Service characteristic: Boot Mouse
+					Input Report (C.2)
+TSPC_HOGP_12_10	False (*)	Write HID Service characteristic: Boot Mouse
+					Input Report (C.2)
+TSPC_HOGP_12_11	False (*)	Read Client Characteristic Configuration
+					characteristic descriptor for Boot
+					Mouse Input Report (C.2)
+TSPC_HOGP_12_12	False (*)	Boot Mouse Input Report characteristic:
+					configuration with 0x0000 or 0x0001
+					(C.2)
+TSPC_HOGP_12_13	False (*)	Notify Boot Keyboard Input Report characteristic
+					(C.1)
+TSPC_HOGP_12_14	False (*)	Notify Boot Mouse Input Report characteristic
+					(C.2)
+TSPC_HOGP_12_15	False (*)	Read Battery Level characteristic (O)
+TSPC_HOGP_12_16	False (*)	Read Battery Level characteristic: Client
+					Characteristic Configuration
+					characteristic descriptor (O)
+TSPC_HOGP_12_17	False (*)	Battery Level characteristic: configuration with
+					0x0000 or 0x0001 (O)
+TSPC_HOGP_12_18	False (*)	Notify Battery Level characteristic (O)
+TSPC_HOGP_12_19	False (*)	Read PnP ID characteristic (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+C.1: Mandatory to support if TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported,
+	else excluded.
+C.2: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements - Report Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_13_1	True		Attribute Protocol supported over LE Transport
+					(M.1)
+TSPC_HOGP_13_2	True		Generic Attribute Profile Client (M.1)
+TSPC_HOGP_13_3	True		Discover All Primary Services (C.1)
+TSPC_HOGP_13_4	False (*)	Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_13_5	True		Find Included Services (M.1)
+TSPC_HOGP_13_6	True		Discover All Characteristics of a Service (C.2)
+TSPC_HOGP_13_7	False (*)	Discover Characteristics by UUID (C.2)
+TSPC_HOGP_13_8	True		Discover All Characteristic Descriptors (M.1)
+TSPC_HOGP_13_9	True		Read Characteristic Value (M.1)
+TSPC_HOGP_13_10	True		Read using Characteristic UUID (O)
+TSPC_HOGP_13_11	True		Read Long Characteristic Value (M.1)
+TSPC_HOGP_13_12	True		Read Characteristic Descriptors (M.1)
+TSPC_HOGP_13_13	True		Write without Response (M.1)
+TSPC_HOGP_13_14	True		Write Characteristic Value (M.1)
+TSPC_HOGP_13_15	True		Write Characteristic Descriptors (M.1)
+TSPC_HOGP_13_16	True		Notifications (M.1)
+TSPC_HOGP_13_17	True		Exchange MTU (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 selected
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory to support at least one of these features.
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements - Boot Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_14_1	False (*)	Attribute Protocol supported over LE Transport
+					(M.1)
+TSPC_HOGP_14_2	False (*)	Generic Attribute Profile Client (M.1)
+TSPC_HOGP_14_3	False (*)	Discover All Primary Services (C.1)
+TSPC_HOGP_14_4	False (*)	Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_14_5	False (*)	Discover All Characteristics of a Service (O)
+TSPC_HOGP_14_6	False (*)	Discover Characteristics by UUID (O)
+TSPC_HOGP_14_7	False (*)	Discover All Characteristic Descriptors (M.1)
+TSPC_HOGP_14_8	False (*)	Read Characteristic Value (M.1)
+TSPC_HOGP_14_9	False (*)	Read using Characteristic UUID (M.1)
+TSPC_HOGP_14_10	False (*)	Read Characteristic Descriptors (M.1)
+TSPC_HOGP_14_11	False (*)	Write without Response (M.1)
+TSPC_HOGP_14_12	False (*)	Write Characteristic Value (M.1)
+TSPC_HOGP_14_13	False (*)	Write Characteristic Descriptors (M.1)
+TSPC_HOGP_14_14	False (*)	Notifications (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_3 selected
+-------------------------------------------------------------------------------
+
+
+		GAP Requirements - HID Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_15_1	True		Central (M.1)
+TSPC_HOGP_15_2	True		LE Security Mode 1 (central) (M.1)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 or TSPC_HOGP_1_3 is selected
+-------------------------------------------------------------------------------
+
+
+		SM Requirements - HID Host
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HOGP_16_1	True		No Security Requirements (LE Security Level 1,
+					No Security) (M.1)
+TSPC_HOGP_16_2	True		Unauthenticated no MITM protection (LE Security
+					Level 2, Just Works) (M.1)
+TSPC_HOGP_16_3	True		Authenticated MITM protection (LE Security
+					Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
+M.1: Mandatory if TSPC_HOGP_1_2 or TSPC_HOGP_1_3 is selected
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-hsp.txt b/bluez/repo/android/pics-hsp.txt
new file mode 100644
index 0000000..e55b986
--- /dev/null
+++ b/bluez/repo/android/pics-hsp.txt
@@ -0,0 +1,103 @@
+HSP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HSP_0_1	False		Version: Headset Profile v1.1 (C.1)
+TSPC_HSP_0_2	True (*)	Version: Headset Profile v1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one of these versions.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HSP_1_1	True (*)	Role: Audio Gateway (AG) (C.1)
+TSPC_HSP_1_2	False		Role: Headset (HS) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Audio Gateway Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HSP_2_1	True		Incoming audio connection establishment (M)
+TSPC_HSP_2_2	True (*)	Ring (AT command) (C.3)
+TSPC_HSP_2_3	False 		Inband ring tone (O)
+TSPC_HSP_2_4	True (*)	Outgoing audio connection establishment (O)
+TSPC_HSP_2_5	True (*)	Audio connection release from HS (C.5)
+TSPC_HSP_2_6	True 		Audio connection release from AG (M)
+TSPC_HSP_2_7	True		Audio connection transfer: AG to HS (M)
+TSPC_HSP_2_8	True		Audio connection transfer: HS to AG (M)
+TSPC_HSP_2_9	True (*)	Remote audio volume control (C.1)
+TSPC_HSP_2_10	True (*)	HS informs AG about local changes of audio
+					volume (O)
+TSPC_HSP_2_11	True (*)	Audio volume setting storage by HS (O)
+TSPC_HSP_2_12	False		Remote microphone gain control (C.2)
+TSPC_HSP_2_13	False		HS informs AG about local changes of microphone
+					gain (O)
+TSPC_HSP_2_14	False		Microphone gain setting storage by HS (O)
+TSPC_HSP_2_15	True		Connection handling with Detach/Page (M)
+TSPC_HSP_2_16	False		Connection handling with Park Mode (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HSP_2_10 is supported, otherwise optional
+C:2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional
+C.3: Excluded if TSPC_HSP_2_3 and TSPC_HSP_4_1 ("Show that in-band
+	ringing and RING are mutually exclusive") are supported,
+	otherwise optional
+C.4: Excluded if TSPC_HSP_0_2 is supported, otherwise optional
+C.5: Mandatory if TSPC_HSP_0_1 is supported, otherwise optional
+-------------------------------------------------------------------------------
+
+
+		Headset Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HSP_3_1	False (*)	Incoming audio connection establishment (M)
+TSPC_HSP_3_2	False (*)	Ring (AT command) (M)
+TSPC_HSP_3_3	False (*)	Inband ring tone (M)
+TSPC_HSP_3_4	False (*)	Outgoing audio connection establishment (M)
+TSPC_HSP_3_5	False (*)	Audio connection release from HS (M)
+TSPC_HSP_3_6	False (*)	Audio connection release from AG (M)
+TSPC_HSP_3_7	False (*)	Audio connection transfer: AG to HS (M)
+TSPC_HSP_3_8	False (*)	Audio connection transfer: HS to AG (M)
+TSPC_HSP_3_9	False		Remote audio volume control (C.1)
+TSPC_HSP_3_10	False		HS informs AG about local changes of audio
+                                        volume (O)
+TSPC_HSP_3_11	False		Audio volume setting storage by HS (O)
+TSPC_HSP_3_12	False		Remote microphone gain control (C.2)
+TSPC_HSP_3_13	False		HS informs AG about local changes of microphone
+                                        gain (O)
+TSPC_HSP_3_14	False		Microphone gain setting storage by HS (O)
+TSPC_HSP_3_15	False (*)	Connection handling with Detach/Page (M)
+TSPC_HSP_3_16	False		Connection handling with Park Mode (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HSP_3_10 is supported, otherwise optional
+C.2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional
+C.3: Excluded if TSPC_HSP_0_2 is supported, otherwise optional
+-------------------------------------------------------------------------------
+
+
+		Errata Service Releases
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_HSP_4_1	False		Show that in-band ringing and RING are
+					mutually exclusive (C.1)
+-------------------------------------------------------------------------------
+C.1: Excluded if TSPC_HSP_0_2 is supported, otherwise optional
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-iopt.txt b/bluez/repo/android/pics-iopt.txt
new file mode 100644
index 0000000..7277c4c
--- /dev/null
+++ b/bluez/repo/android/pics-iopt.txt
@@ -0,0 +1,223 @@
+IOPT PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Profiles
+-------------------------------------------------------------------------------
+Parameter Name				Selected	Description
+-------------------------------------------------------------------------------
+TSPC_support_						Support for: Advanced
+AdvancedAudioDistributionProfile_Sink	False		Audio Distribution
+							Profile. Role: Sink
+
+TSPC_support_						Support for: Advanced
+AdvancedAudioDistributionProfile_Source	True (*)	Audio Distribution
+							Profile. Role: Source
+
+TSPC_support_AVRemoteControlProfile_CT	True (*)	Support for: Audio\Video
+							Remote Control Profile.
+							Role: Controller
+
+TSPC_support_AVRemoteControlProfile_TG	True (*)	Support for: Audio\Video
+							Remote Control Profile.
+							Role: Target
+
+TSPC_support_BasicImagingProfile_CLIENT	False		Support for: Basic
+							Imaging Profile.
+							Role: Client
+
+TSPC_support_BasicImagingProfile_			Support for: Basic
+SERVER_ImagingAutomaticArchive		False		Imaging Profile. Role:
+							Server Functionality:
+							Imaging autoarchive
+
+TSPC_support_BasicImagingProfile_	False		Support for: Basic
+SERVER_ImagingReferencedObjects				Imaging Profile. Role:
+							Server Functionality:
+							Imaging ref. objects
+
+TSPC_support_BasicImagingProfile_	False		Support for: Basic
+SERVER_ImagingResponder					Imaging Profile. Role:
+							Server Functionality:
+							Imaging responder
+
+TSPC_support_				False		Support for: Basic
+BasicPrintingProfile_PRINTER				Printing Profile. Role:
+							Printer
+
+TSPC_support_						Support for: Basic
+BasicPriProfile_PRINTER_ReflectedUI	False		Printing Profile. Role:
+							Printer Functionality:
+							Reflected UI
+
+TSPC_support_BasicPrintingProfile_			Support for: Basic
+SENDER_Referenced_objects_Service	False		Printing Profile. Role:
+							Sender Functionality:
+							Refe. objects service
+
+TSPC_support_DialUpNetworkingProfile_DT	False		Support for: Dial-Up
+							Networking Profile.
+							Role: Data Terminal
+
+TSPC_support_DialUpNetworkingProfile_GW	False		Support for: Dial-Up
+							Networking Profile.
+							Role: Gateway
+
+TSPC_support_				False		Support for: Extended
+ExtendedServiceDiscoveryProfile_IP_LAP			SDP. Version: IP-LAP
+
+TSPC_support_				False		Support for: Extended
+ExtendedServiceDiscoveryProfile_IP_PAN			SDP. Version: IP-PAN
+
+TSPC_support_				False		Support for: Extended
+ExtendedServiceDiscoveryProfile_L2CAP			SDP. Version: L2CAP
+
+TSPC_support_FAXProfile_DT		False		Support for: FAX Profile
+							Role: Data Terminal
+
+TSPC_support_FAXProfile_GW		False		Support for: FAX Profile
+							Role: Gateway
+
+TSPC_support_FileTransferProfile_CLIENT	False		Support for: FTP
+							Role: Client
+
+TSPC_support_FileTransferProfile_SERVER	False		Support for: FTP
+							Role: Server
+
+TSPC_support_HealthDeviceProfile_Sink	True (*)	Support for: HDP
+							Role: Sink
+
+TSPC_support_HealthDeviceProfile_Source	False		Support for: HDP
+							Role: Source
+
+TSPC_support_NewHandsFreeProfile_AG	True (*)	Support for: HFP
+							Role: Audio gateway
+
+TSPC_support_NewHandsFreeProfile_HF	False		Support for: HFP
+							Role: Hands-Free unit
+
+TSPC_support_				False		Support for: Hard Copy
+HardCopyReplacementProfile_				cable Repl. Profile
+CLIENT_CR_RegisterNotofication_support			Role: Client
+							Functionality: CR
+							register notification
+							support
+
+TSPC_support_				False		Support for: Hard Copy
+HardCopyReplacementProfile_CLIENT_print			cable Repl. Profile.
+							Role: Client
+							Functionality: Print
+
+TSPC_support_				False		Support for: Hard Copy
+HardCopyReplacementProfile_CLIENT_scan			cable Repl. Profile.
+							Role: Client
+							Functionality: Scan
+
+TSPC_support_				False		Support for: Hard Copy
+HardCopyReplacementProfile_SERVER_print			cable Repl. Profile.
+							Role: Server
+							Functionality: Print
+
+TSPC_support_				False		Support for: Hard Copy
+HardCopyReplacementProfile_SERVER_scan			cable Repl. Profile.
+							Role: Server
+							Functionality: Scan
+
+TSPC_support_HeadsetProfile_AG		True (*)	Support for: HSP
+							Role: Audio Gateway
+
+TSPC_support_HeadsetProfile_HS		False		Support for: HSP
+							Role: Headset
+
+TSPC_support_				False		Support for: HID
+HumanInterfaceDeviceProfile				Role: Device
+
+TSPC_support_HID_Host			True (*)	Support for: HID
+							Role: Host
+
+TSPC_support_LANAccessProfile_DT	False		Support for: LAN Access
+							Profile. Role: Data
+							Terminal
+
+TSPC_support_LANAccessProfile_LAP	False		Support for: LAN Access
+							Profile. Role: LAN
+							Access Point
+
+TSPC_support_MessaeAccessProfile_MCE	False		Support for: MAP
+							Role: MCE
+
+TSPC_support_MessageAccessProfile_MSE	True (*)	Support for: MAP
+							Role: MSE
+
+TSPC_support_ObjectPushProfile_CLIENT	True (*)	Support for: OPP
+							Role: Client
+
+TSPC_support_ObjectPushProfile_SERVER	True (*)	Support for: OPP
+							Role: Server
+
+TSPC_support_				False		Support for: PAN
+PersonalAreaNetworkProfile_GN				Role: GN
+
+TSPC_support_				True (*)	Support for: PAN
+PersonalAreaNetworkProfile_NAP				Role: NAP
+
+TSPC_support_				True (*)	Support for: PAN
+PersonalAreaNetworkProfile_PANU				Role: PANU
+
+TSPC_support_PhonebookAccessProfile_PCE	False		Support for: PBAP
+							Role: PCE
+
+TSPC_support_PhonebookAccessProfile_PSE	True (*)	Support for: PBAP
+							Role: PSE
+
+TSPC_support_SerialPortProfile_Service	False		Support for: SPP
+							Role: Dev B
+
+TSPC_support_				False		Support for: Service
+ServiceDiscoveryApplicationProfile			Discovery Application
+							Profile
+
+TSPC_support_SIMAccessProfile_CLIENT	False		Support for: SIM access
+							Profile. Role: Client
+
+TSPC_support_SIMAccessProfile_SERVER	False		Support for: SIM access
+							Profile. Role: Server
+
+TSPC_support_				False		Support for:
+SynchronizationProfile_CLIENT				Synchronization Profile
+							Role: Client
+
+TSPC_support_				False		Support for:
+SynchronizationProfile_SERVER				Synchronization Profile
+							Role: Server
+
+TSPC_support_UDIProfile_MT		False		Support for: UDI Profile
+							Role: MT
+
+TSPC_support_UDIProfile_TA		False		Support for: UDI Profile
+							Role: TA
+
+TSPC_support_				False		Support for: Video
+VideoDistributionProfile_Sink				distribution Profile
+							Role: Sink
+
+TSPC_support_				False		Support for: Video
+VideoDistributionProfile_Source				distribution Profile
+							Role: Source
+
+TSPC_support_WAPOverBluetooth_CLIENT	False		Support for: WAP over
+							Bluetooth Profile
+							Role: Client
+
+TSPC_support_WAPOverBluetooth_PROXY	False		Support for: WAP over
+							Bluetooth Profile
+							Role: PROXY
+
+TSPC_support_GNSS_SERVER		False		Support for: GNSS
+							Role: Server
diff --git a/bluez/repo/android/pics-l2cap.txt b/bluez/repo/android/pics-l2cap.txt
new file mode 100644
index 0000000..ce50c98
--- /dev/null
+++ b/bluez/repo/android/pics-l2cap.txt
@@ -0,0 +1,178 @@
+L2CAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_L2CAP_1_1	True		Data Channel Initiator (C.3)
+TSPC_L2CAP_1_2	True		Data Channel Acceptor (C.1)
+TSPC_L2CAP_1_3	True		LE Master (C.2)
+TSPC_L2CAP_1_4	True		LE Slave (C.2)
+TSPC_L2CAP_1_5	True		LE Data Channel Initiator (C.4)
+TSPC_L2CAP_1_6	True		LE Data Channel Acceptor (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF BR/EDR or BR/EDR/LE is supported, otherwise Excluded.
+C.2: Mandatory to support (at least one of TSPC_L2CAP_1_3 or TSPC_L2CAP_1_4)
+	IF LE or BR/EDR/LE is supported, otherwise Excluded.
+C.3: Optional IF BR/EDR or BR/EDR/LE is supported, otherwise Excluded.
+C.4: Optional IF LE or BR/EDR/LE and Core Spec v4.1 or Core Spec v4.1+HS and
+	TSPC_L2CAP_2_46 is supported, otherwise Excluded.
+C.5: Mandatory IF LE or BR/EDR/LE and Core Spec v4.1 or Core Spec v4.1+HS and
+	TSPC_L2CAP_2_46 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		General Operation
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_L2CAP_2_1	True		Support of L2CAP signaling channel (C.20)
+TSPC_L2CAP_2_2	True		Support of configuration process (C.20)
+TSPC_L2CAP_2_3  True		Support of connection oriented data
+					channel (C.20)
+TSPC_L2CAP_2_4	True		Support of command echo request (C.21)
+TSPC_L2CAP_2_5	True		Support of command echo response (C.20)
+TSPC_L2CAP_2_6	True		Support of command information request (C.21)
+TSPC_L2CAP_2_7	True		Support of command information response (C.20)
+TSPC_L2CAP_2_8	False (*)	Support of a channel group (C.21)
+TSPC_L2CAP_2_9	False (*)	Support of packet for connectionless
+					channel (C.21)
+TSPC_L2CAP_2_10	False (*)	Support retransmission mode (C.21)
+TSPC_L2CAP_2_11	False (*)	Support flow control mode(C.21)
+TSPC_L2CAP_2_12	True		Enhanced Retransmission Mode (C.1, C.13)
+TSPC_L2CAP_2_13	True		Streaming Mode (C.1, C.14)
+TSPC_L2CAP_2_14	True		FCS Option (C.2)
+TSPC_L2CAP_2_15	True		Generate Local Busy Condition (C.3)
+TSPC_L2CAP_2_16	False (*)	Send Reject (C.3)
+TSPC_L2CAP_2_17	True		Send Selective Reject (C.3)
+TSPC_L2CAP_2_18	True		Mandatory use of ERTM (C.4)
+TSPC_L2CAP_2_19	True		Mandatory use of Streaming Mode (C.5)
+TSPC_L2CAP_2_20	True		Optional use of ERTM (C.4)
+TSPC_L2CAP_2_21	True		Optional use of Streaming Mode (C.5)
+TSPC_L2CAP_2_22	True		Send data using SAR in ERTM (C.6)
+TSPC_L2CAP_2_23	True		Send data using SAR in Streaming Mode (C.7)
+TSPC_L2CAP_2_24	True		Actively request Basic Mode for a PSM that
+					supports the use of ERTM or Streaming
+					Mode (C.8)
+TSPC_L2CAP_2_25	True		Supports performing L2CAP channel mode
+					configuration fallback from SM
+					to ERTM (C.9)
+TSPC_L2CAP_2_26	True		Supports sending more than one unacknowledged
+					I-Frame when operating in ERTM (C.10)
+TSPC_L2CAP_2_27	True		Supports sending more than three unacknowledged
+					I-Frame when operating in ERTM (C.10)
+TSPC_L2CAP_2_28	True		Supports configuring the peer TxWindow
+					greater than 1 (C.11)
+TSPC_L2CAP_2_29	False (*)	AMP Support (C.12)
+TSPC_L2CAP_2_30	True		Fixed Channel Support (C.12)
+TSPC_L2CAP_2_31	False (*)	AMP Manager Support (C.12)
+TSPC_L2CAP_2_32	False (*)	ERTM over AMP (C.12)
+TSPC_L2CAP_2_33	False (*)	Streaming Mode Source over AMP Support (C.15)
+TSPC_L2CAP_2_34	False (*)	Streaming Mode Sink over AMP Support (C.15)
+TSPC_L2CAP_2_35	True		Unicast Connectionless Data, Reception
+					(C.1, C.16)
+TSPC_L2CAP_2_36	True		Ability to transmit an unencrypted packet over
+					a Unicast connectionless L2CAP
+					channel (C.16)
+TSPC_L2CAP_2_37	True		Ability to transmit an encrypted packet over
+					a Unicast connectionless L2CAP
+					channel (C.16)
+TSPC_L2CAP_2_38	False (*)	Extended Flow Specification for BR/EDR (C.8)
+TSPC_L2CAP_2_39	False (*)	Extended Window Size (C.8)
+TSPC_L2CAP_2_40	True		Support of Low Energy signaling channel (C.17)
+TSPC_L2CAP_2_41	True		Support of command reject (C.17)
+TSPC_L2CAP_2_42	True		Send Connection Parameter Update Request (C.18)
+TSPC_L2CAP_2_43	True		Send Connection Parameter Update Response (C.19)
+TSPC_L2CAP_2_44	False (*)	Extended Flow Specification for AMP (C.22)
+TSPC_L2CAP_2_45	True		Send disconnect request command (C.21)
+TSCP_L2CAP_2_46	True		Support LE Credit Based Flow Control
+					Mode (C.23)
+TSCP_L2CAP_2_47	True		Support for LE Data Channel (C.24)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR
+	TSPC_L2CAP_2_35 IF BR/EDR OR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR
+	Core Spec v3.0 or later is supported, ELSE Excluded
+C.2: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded.
+C.3: Optional IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_28 is claimed, ELSE Excluded.
+C.4: IF TSPC_L2CAP_2_12 is claimed THEN either TSPC_L2CAP_2_18
+	OR TSPC_L2CAP_2_20 is Mandatory, ELSE Excluded.
+C.5: IF TSPC_L2CAP_2_13 is claimed THEN either TSPC_L2CAP_2_19
+	OR TSPC_L2CAP_2_21 are Mandatory, ELSE Excluded.
+C.6: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded.
+C.7: Optional IF TSPC_L2CAP_2_13 is claimed, ELSE Excluded.
+C.8: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded.
+C.9: Mandatory IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_13 AND TSPC_L2CAP_2_21
+       is claimed, ELSE Excluded.
+C.10: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded.
+C.11: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded.
+C.12: Mandatory IF Core Spec v3.0+HS OR Core Spec v4.0+HS OR
+    Core Spec v4.1+HS OR Core Spec v4.2+HS is claimed, ELSE Optional.
+C.13: Mandatory IF Core Spec v3.0+HS OR Core Spec v4.0+HS OR
+    Core Spec v4.1+HS OR Core Spec v4.2+HS is claimed, ELSE Optional.
+C.14: Optional IF Core Spec v3.0 OR or later is claimed, ELSE Excluded.
+C.15: Optional IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded.
+C.16: Optional IF Core Spec v3.0 or later is claimed, ELSE Excluded.
+C.17: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded.
+C.18: Optional IF Core Spec 4.0 OR TSPC_L2CAP_1_4 is claimed, ELSE Excluded.
+C.19: Mandatory IF Core Spec 4.0 AND TSPC_L2CAP_1_3 is claimed, ELSE Excluded.
+C.20: Mandatory IF BR/EDR OR BR/EDR/LE, is claimed, ELSE Excluded
+C.21: Optional IF BR/EDR OR BR/EDR/LE, is claimed, ELSE Excluded.
+C.22: Mandatory IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded.
+C.23: Optional LE OR BR/EDR/LE AND Core Spec v4.1 OR Core Spec v4.1+HS OR
+    Core Spec v4.2 OR Core Spec v4.2+HS is supported, otherwise Excluded.
+C.24: Mandatory IF TSPC_L2CAP_2_46 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Configurable Parameters
+-------------------------------------------------------------------------------
+Parameter	Name Selected	Description
+-------------------------------------------------------------------------------
+TSPC_L2CAP_3_1	True		Support of RTX timer (M)
+TSPC_L2CAP_3_2	True		Support of ERTX timer (C.4)
+TSPC_L2CAP_3_3	True		Support minimum MTU size 48 octets (C.4)
+TSPC_L2CAP_3_4	True		Support MTU size larger than 48 octets (C.5)
+TSPC_L2CAP_3_5	True		Support of flush timeout value for reliable
+					channel (C.4)
+TSPC_L2CAP_3_6	False (*)	Support of flush timeout value for unreliable
+					channel (C.5)
+TSPC_L2CAP_3_7	False (*)	Support of bi-directional quality of service
+					(QoS) option field (C.1)
+TSPC_L2CAP_3_8	False (*)	Negotiate QoS service type (C.5)
+TSPC_L2CAP_3_9	False (*)	Negotiate and support service type ‘No
+					traffic’ (C.2)
+TSPC_L2CAP_3_10	False (*)	Negotiate and support service type ‘Best
+					effort’ (C.3)
+TSPC_L2CAP_3_11	False (*)	Negotiate and support service type
+					‘Guaranteed’ (C.2)
+TSPC_L2CAP_3_12	True		Support minimum MTU size 23 octets (C.6)
+TSPC_L2CAP_3_13	False (*)	Negotiate and support service type ‘No traffic’
+					for Extended Flow Specification (C.7)
+TSPC_L2CAP_3_14	False (*)	Negotiate and support service type ‘Best Effort'
+					for Extended Flow Specification (C.8)
+TSPC_L2CAP_3_15	False (*)	Negotiate and support service type ‘Guaranteed’
+					for Extended Flow Specification (C.9)
+TSPC_L2CAP_3_16	True		Support Multiple Simultaneous LE Data
+					Channels (C.10)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Optional.
+C.2: Optional if TSPC_L2CAP_3_8 is supported, ELSE Excluded.
+C.3: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Excluded.
+C.4: Mandatory IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded.
+C.5: Optional IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded.
+C.6: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded.
+C.7: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded.
+C.8: Mandatory if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported,
+	ELSE Excluded.
+C.9: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded.
+C.10: Optional IF TSPC_L2CAP_2_47 AND Core Spec 4.1 is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-map.txt b/bluez/repo/android/pics-map.txt
new file mode 100644
index 0000000..b1346b6
--- /dev/null
+++ b/bluez/repo/android/pics-map.txt
@@ -0,0 +1,175 @@
+MAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+	Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_0_1	False		Role: Map 1.0 (C1)
+TSPC_MAP_0_2	True (*)	Role: Map 1.1 (C1)
+TSPC_MAP_0_3	False		Role: Map 1.2 (C1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Profile version.
+-------------------------------------------------------------------------------
+
+
+	Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_1_1	True (*)	Role: Messaging Server Equipment (C1)
+TSPC_MAP_1_2	False		Role: Messaging Client Equipment (C1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+	Supported features MCE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_2_1	False		MCE: Message Notification (C1)
+TSPC_MAP_2_1a	False		MCE: SendEvent (C4)
+TSPC_MAP_2_2	False		MCE: Message Browsing (C1)
+TSPC_MAP_2_2a	False		MCE: SetFolder (C5)
+TSPC_MAP_2_2b	False		MCE: GetFoldersListing (C5)
+TSPC_MAP_2_2c	False		MCE: GetMessagesListing (C5)
+TSPC_MAP_2_2d	False		MCE: GetMessage (O)
+TSPC_MAP_2_2e	False		MCE: SetMessageStatus (O)
+TSPC_MAP_2_2f	False		MCE: UpdateInbox (O)
+TSPC_MAP_2_2g	False		MCE: Filtering (O)
+TSPC_MAP_2_2h	False		MCE: Multiple simultaneous MAS instances (O)
+TSPC_MAP_2_3	False		MCE: Message Uploading (O)
+TSPC_MAP_2_3a	False		MCE: SetFolder (C6)
+TSPC_MAP_2_3b	False		MCE: GetFoldersListing (C6)
+TSPC_MAP_2_3c	False		MCE: PushMessage (C6)
+TSPC_MAP_2_4	False		MCE: Message Delete (O)
+TSPC_MAP_2_4a	False		MCE: SetMessageStatus (C7)
+TSPC_MAP_2_5	False		MCE: Notification Registration (C2)
+TSPC_MAP_2_5a	False		MCE: SetNotificationRegistration off (O)
+TSPC_MAP_2_5b	False		MCE: SetNotificationRegistration on (C8)
+TSPC_MAP_2_6	False		MCE: Supported Message Types
+TSPC_MAP_2_6a	False (*)	MCE: EMAIL (C3)
+TSPC_MAP_2_6b	False (*)	MCE: SMS_GSM (C3)
+TSPC_MAP_2_6c	False (*)	MCE: SMS_CDMA (C3)
+TSPC_MAP_2_6d	False (*)	MCE: MMS (C3)
+TSPC_MAP_2_7	False		MCE: Instance Information (Not Supported)
+TSPC_MAP_2_7a	False (*)	MCE: GetMASInstanceInformation (Not Supported)
+TSPC_MAP_2_8	False		MCE: Extended MAP-Event-Report (Not Supported)
+TSPC_MAP_2_8a	False (*)	MCE: MAP-Event-Report: Version 1.1
+					(Not Supported)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined features TSPC_MAP_2_1 or
+	TSPC_MAP_2_2.
+C.2: Mandatory to support TSPC_MAP_2_5 if TSPC_MAP_2_1 is supported.
+C.3: Mandatory to support at least one of the defined message types
+	TSPC_MAP_2_6a to TSPC_MAP_2_6d IF TSPC_MAP_2_2 or TSPC_MAP_2_3 is
+	supported.
+C.4: Support of functionality TSPC_MAP_2_1a mandatory IF related feature
+	TSPC_MAP_2_1 supported.
+C.5: Support of functionality mandatory IF TSPC_MAP_2_2 supported.
+C.6: Support of functionality mandatory IF TSPC_MAP_2_3 supported.
+C.7: Support of functionality mandatory IF TSPC_MAP_2_4 supported.
+C.8: Mandatory to support IF TSPC_MAP_2_5 (Notification Registration) is
+	supported, otherwise excluded.
+C.9: Optional to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, otherwise
+	excluded.
+C.10: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) and TSPC_MAP_2_1
+	(Message Notification) is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+	Supported features MSE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_3_1	True		MSE: Message Notification (M)
+TSPC_MAP_3_1a	True		MSE: SendEvent (M)
+TSPC_MAP_3_2	True		MSE: Message Browsing (M)
+TSPC_MAP_3_2a	True		MSE: SetFolder (M)
+TSPC_MAP_3_2b	True		MSE: GetFoldersListing (M)
+TSPC_MAP_3_2c	True		MSE: GetMessagesListing (M)
+TSPC_MAP_3_2d	True		MSE: GetMessage (M)
+TSPC_MAP_3_2e	True		MSE: SetMessageStatus (M)
+TSPC_MAP_3_2f	True		MSE: UpdateInbox (M)
+TSPC_MAP_3_2g	False		MSE: Multiple simultaneous MAS instances (O)
+TSPC_MAP_3_3	True		MSE: Message Uploading (M)
+TSPC_MAP_3_3a	True		MSE: SetFolder (M)
+TSPC_MAP_3_3b	True		MSE: GetFoldersListing (M)
+TSPC_MAP_3_3c	True		MSE: PushMessage (M)
+TSPC_MAP_3_4	True		MSE: Message Delete (M)
+TSPC_MAP_3_4a	True		MSE: SetMessageStatus (M)
+TSPC_MAP_3_5	True		MSE: Notification Registration (M)
+TSPC_MAP_3_5a	True		MSE: SetNotificationRegistration (M)
+TSPC_MAP_3_6	False		MSE: Supported Message Types
+TSPC_MAP_3_6a	False		MSE: EMAIL (C1)
+TSPC_MAP_3_6b	True		MSE: SMS_GSM (C1)
+TSPC_MAP_3_6c	True (*)	MSE: SMS_CDMA (C1)
+TSPC_MAP_3_6d	True		MSE: MMS (C1)
+TSPC_MAP_3_7	False		MSE: Instance Information (Not Supported)
+TSPC_MAP_3_7a	False (*)	MSE: GetMASInstanceInformation (Not Supported)
+TSPC_MAP_3_8	False		MSE: Extended MAP-Event-Report (Not Supported)
+TSPC_MAP_3_8a	False (*)	MSE: MAP-Event-Report: Version 1.1
+					(Not Supported)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined message types
+	TSPC_MAP_3_6a to TSPC_MAP_3_6d IF TSPC_MAP_3_2 or TSPC_MAP_3_3
+	is supported.
+C.2: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) is supported,
+	otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+	GOEP v2.0 or later Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_7b_1	False		GOEP v2.0 or later (C1)
+TSPC_MAP_7b_2	False		GOEP v2 Backwards Compatibility (C1)
+TSPC_MAP_7b_3	False		OBEX over L2CAP (C1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded.
+-------------------------------------------------------------------------------
+
+
+	MCE OBEX Header Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_10_1	False (*)	Name (M)
+TSPC_MAP_10_2	False (*)	Typr (M)
+TSPC_MAP_10_3	False (*)	Body (M)
+TSPC_MAP_10_4	False (*)	End of Body (M)
+TSPC_MAP_10_5	False (*)	Target (M)
+TSPC_MAP_10_6	False (*)	Who (M)
+TSPC_MAP_10_7	False (*)	Connection ID (M)
+TSPC_MAP_10_8	False (*)	Application Parameters (M)
+TSPC_MAP_10_9	False		SRM (C2)
+TSPC_MAP_10_10	False		Receive SRMP (C2)
+TSPC_MAP_10_11	False		Send SRMP (C2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded.
+C.2: Optional if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded.
+-------------------------------------------------------------------------------
+
+
+	GetMessagesListing Filtering Parameter Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MAP_20_1	False (*)	MCE: FilterMessageType (O)
+TSPC_MAP_20_2	False (*)	MCE: FilterPeriodBegin (O)
+TSPC_MAP_20_3	False (*)	MCE: FilterPeriodEnd (O)
+TSPC_MAP_20_4	False (*)	MCE: FilterReadStatus (O)
+TSPC_MAP_20_5	False (*)	MCE: FilterRecipient (O)
+TSPC_MAP_20_6	False (*)	MCE: FilterOriginator (O)
+TSPC_MAP_20_7	False (*)	MCE: FilterPriority (O)
+TSPC_ALL	False (*)	Turns on all the test cases
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-mcap.txt b/bluez/repo/android/pics-mcap.txt
new file mode 100644
index 0000000..218ee80
--- /dev/null
+++ b/bluez/repo/android/pics-mcap.txt
@@ -0,0 +1,141 @@
+MCAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Protocols
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_1_A_1	True (*)	Supports Standard Op Codes (C.1)
+TSPC_MCAP_1_A_2	True (*)	Supports Clock Synchronization Protocol (C.1)
+-------------------------------------------------------------------------------
+C.1: Support for at least one of the defined protocols is Mandatory.
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_1_1	True (*)	Supports Source Role (C.1)
+TSPC_MCAP_1_2	True (*)	Supports Sink Role (C.1)
+TSPC_MCAP_1_3	False		Supports Sync-Slave Role (C.2)
+TSPC_MCAP_1_4	False		Supports Sync-Master Role (C.3)
+-------------------------------------------------------------------------------
+C.1: If support for TSPC_MCAP_1_A_1 is supported, at least one of the
+	defined roles is Mandatory otherwise Excluded.
+C.2: Mandatory if TSPC_MCAP_1_A_2 is supported, otherwise Excluded.
+C.3: Optional if TSPC_MCAP_1_A_2 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		L2CAP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_2_1	True (*)	Supports L2CAP Control Channel (M)
+TSPC_MCAP_2_2	True (*)	Supports at least one L2CAP Data Channel (M)
+TSPC_MCAP_2_3	True (*)	Maximum number of simultaneous L2CAP Data
+				Channels supported (DCmax) per MCL (M)
+TSPC_MCAP_2_4	False		Can support multiple simultaneous MCLs with
+				Standard Op Codes (O)
+-------------------------------------------------------------------------------
+
+
+		Connection Management - Source
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_3_1			This row intentionally left blank
+TSPC_MCAP_3_2	True (*)	Initiate creation of Control and Data Channels
+				(C.1)
+TSPC_MCAP_3_3	True (*)	Accept creation of Control and Data Channels
+				(C.1)
+TSPC_MCAP_3_4	True (*)	Initiate Disconnection of MCL (M)
+TSPC_MCAP_3_5	True (*)	Accept Disconnection of MCL (M)
+TSPC_MCAP_3_6	True (*)	Initiate Disconnection of MDL (M)
+TSPC_MCAP_3_7	True (*)	Accept Disconnection of MDL (M)
+TSPC_MCAP_3_8	False		Initiate Reconnection of MDL (O)
+TSPC_MCAP_3_9	False		Accept Reconnection of MDL (C.2)
+TSPC_MCAP_3_10	False		Initiate Deletion of MDL (O)
+TSPC_MCAP_3_11	True (*)	Accept Deletion of MDL (M)
+TSPC_MCAP_3_12	False		Initiate Delete of All MDLs using 0xFFFF (O)
+TSPC_MCAP_3_13	True (*)	Accept Delete of All MDLs using 0xFFFF (M)
+TSPC_MCAP_3_14	False		Send MDL Abort request (O)
+TSPC_MCAP_3_15	True (*)	Accept MDL Abort request (M)
+-------------------------------------------------------------------------------
+C.1: Support for at least one of TSPC_MCAP_3_2 or TSPC_MCAP_3_3 is Mandatory.
+C.2: Mandatory if TSPC_MCAP_3_3 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		L2CAP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_4_1	True (*)	Supports L2CAP Control Channel (M)
+TSPC_MCAP_4_2	True (*)	Supports at least one L2CAP Data Channel (M)
+TSPC_MCAP_4_3	True (*)	Maximum number of simultaneous L2CAP Data
+				Channels supported (DCmax) per MCL (M)
+TSPC_MCAP_4_4	False		Can support multiple simultaneous MCLs with
+				Standard Op Codes (O)
+-------------------------------------------------------------------------------
+
+
+		Connection Management - Sink
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_5_1			This row intentionally left blank
+TSPC_MCAP_5_2	True (*)	Initiate creation of Control and Data Channels
+				(M)
+TSPC_MCAP_5_3	True (*)	Accept creation of Control and Data Channels
+				(M)
+TSPC_MCAP_5_4	True (*)	Initiate Disconnection of MCL (M)
+TSPC_MCAP_5_5	True (*)	Accept Disconnection of MCL (M)
+TSPC_MCAP_5_6	True (*)	Initiate Disconnection of MDL (M)
+TSPC_MCAP_5_7	True (*)	Accept Disconnection of MDL (M)
+TSPC_MCAP_5_8	False		Initiate Reconnection of MDL (O)
+TSPC_MCAP_5_9	True (*)	Accept Reconnection of MDL (M)
+TSPC_MCAP_5_10	False		Initiate Deletion of MDL (O)
+TSPC_MCAP_5_11	True (*)	Accept Deletion of MDL (M)
+TSPC_MCAP_5_12	False		Initiate Delete of All MDLs using 0xFFFF (O)
+TSPC_MCAP_5_13	True (*)	Accept Delete of All MDLs using 0xFFFF (M)
+TSPC_MCAP_5_14	False		Send MDL Abort request (O)
+TSPC_MCAP_5_15	True (*)	Accept MDL Abort request (M)
+-------------------------------------------------------------------------------
+
+
+		Clock Synchronization Features - Sync-Slave
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_6_1	False (*)	Accept MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ and
+				Initiate MD_SYNC_INFO_IND (C.1)
+TSPC_MCAP_6_2			This row intentionally left blank
+TSPC_MCAP_6_3	False		Can support multiple simultaneous MCLs with CSP
+				(O)
+TSPC_MCAP_6_4	False		Can access Bluetooth Clock (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if MCAP TSPC_MCAP_1_A_2 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Clock Synchronization Features - Sync-Master
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_7_1			This row intentionally left blank
+TSPC_MCAP_7_2	False (*)	Initiate MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ
+				and Accept MD_SYNC_INFO_IND (C.1)
+TSPC_MCAP_7_3	False		Can support multiple simultaneous MCLs with CSP (O)
+TSPC_MCAP_7_4	False (*)	Can access Bluetooth Clock (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support IF TSPC_MCAP_1_A_2 is supported.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-mps.txt b/bluez/repo/android/pics-mps.txt
new file mode 100644
index 0000000..5aad7c1
--- /dev/null
+++ b/bluez/repo/android/pics-mps.txt
@@ -0,0 +1,337 @@
+MPS PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_0_1	True		MPS v1.0 (M)
+-------------------------------------------------------------------------------
+
+
+		Profile Version Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_1_1	True (*)	A2DP 1.2 or later (O)
+TSPC_MPS_1_2	True (*)	AVRCP 1.3 or later (O)
+TSPC_MPS_1_3	False		DUN 1.1 or later (O)
+TSPC_MPS_1_4	True (*)	HFP 1.5 or later (O)
+TSPC_MPS_1_5	True (*)	PAN 1.0 or later (O)
+TSPC_MPS_1_6	True (*)	PBAP 1.1 or later (O)
+-------------------------------------------------------------------------------
+
+
+		Profile Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_2_1	True (*)	A2DP Source (SRC) (C.1)
+TSPC_MPS_2_2	False		A2DP Sink (SNK) (C.1)
+TSPC_MPS_2_3	True (*)	AVRCP Controller (CT) (C.1)
+TSPC_MPS_2_4	True (*)	AVRCP Target (TG) (C.1)
+TSPC_MPS_2_5	False		DUN Gateway (GW) (C.1)
+TSPC_MPS_2_6	False		DUN Data Terminal (DT) (C.1)
+TSPC_MPS_2_7	True (*)	HFP Audio Gateway (AG) (C.1)
+TSPC_MPS_2_8	False		HFP Hands-Free (HF) (C.1)
+TSPC_MPS_2_9	True (*)	PAN Network Access Point (NAP) (C.1)
+TSPC_MPS_2_10	False		PAN Group Ad-hoc Network (GN) (C.1)
+TSPC_MPS_2_11	True (*)	PAN User (PANU) (C.1)
+TSPC_MPS_2_12	False		PBAP PCE (C.1)
+TSPC_MPS_2_13	True (*)	PBAP PSE (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to declare each role as supported within the represented Profile
+	otherwise Excluded. The roles declared shall match that of the roles
+	supported within the Profile.
+-------------------------------------------------------------------------------
+
+
+		Profile Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_3_1	True (*)	Receiving PASS THROUGH command in Category 1
+				(AVRCP - TG)  (C.1)
+TSPC_MPS_3_2	True (*)	Receiving PASS THROUGH command in Category 1
+				(AVRCP - TG) - PAUSE (C.1)
+TSPC_MPS_3_3	False		Sending PASS THROUGH command in Category 1
+				(AVRCP - CT) - PLAY (C.2)
+TSPC_MPS_3_4	False		Sending PASS THROUGH command in Category 1
+				(AVRCP - CT) - PAUSE (C.2)
+TSPC_MPS_3_5	True (*)	Transfer Control - Suspend (GAVDP - Initiator)
+				(C.3)
+TSPC_MPS_3_6	True (*)	Transfer Control - Suspend (GAVDP - Acceptor)
+				(C.4)
+TSPC_MPS_3_7	False		Accept an incoming voice call (in-band ring)
+				(C.5)
+TSPC_MPS_3_8	True (*)	Accept an incoming voice call (no in-band ring)
+				(C.5)
+TSPC_MPS_3_9	False		Place a call with a phone number supplied by
+				the HF (C.6)
+TSPC_MPS_3_10	True (*)	Register Notification: PLAYBACK_STATUS_CHANGED
+				(C.7)
+TSPC_MPS_3_11	True (*)	Ability to support parallel data and call
+				operation (O)
+TSPC_MPS_3_12	True (*)	PBAP Phone Book Download (C.8)
+TSPC_MPS_3_13	True (*)	Ability to support multiple concurrent device
+				connections (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP
+	Target role) are supported, otherwise Excluded.
+C.2: Mandatory if TSPC_MPS_2_2 (A2DP Sink role) and TSPC_MPS_2_3 (AVRCP
+	Controller role) are supported, otherwise Excluded.
+C.3: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source
+	role) are supported; Optional if TSPC_MPS_1_4 (HFP 1.5 or later) and
+	TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise Excluded.
+C.4: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source
+	role) or TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise
+	Excluded.
+C.5: Mandatory to support at least one if TSPC_MPS_1_4 (HFP 1.5 or later) is
+	supported, otherwise Excluded.
+C.6: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and HFP 3/8 (Place a call with
+	a phone number supplied by the HF) are supported, otherwise Excluded.
+C.7: Mandatory if TSPC_MPS_2_3 (AVRCP Controller role) is supported, otherwise
+	Excluded.
+C.8: Mandatory if TSPC_MPS_1_6 (PBAP 1.1 or later) and PBAP 2/1 (Phone Book
+	Download) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Device Capability Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_4_1	True		Multiple Profiles Single Device (MPSD) (M)
+TSPC_MPS_4_2	True (*)	Multiple Profiles Multiple Devices (MPMD) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device
+	connections), otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		MPSD scenarios
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_6_1	True (*)	HFP-AG and A2DP-SRC Implementation Answer
+				Incoming Call during Audio Streaming (C.1)
+TSPC_MPS_6_2	False		HFP-HF and A2DP-SNK Implementation Answer
+				Incoming Call during Audio Streaming (C.2)
+TSPC_MPS_6_3	True (*)	HFP-AG and A2DP-SRC Implementation Outgoing
+				Call during Audio Streaming (C.1)
+TSPC_MPS_6_4	False		HFP-HF and A2DP-SNK Implementation Outgoing
+				Call during Audio Streaming (C.2)
+TSPC_MPS_6_5	True (*)	HFP-AG and A2DP-SRC Implementation Reject/Ignore
+				Incoming Call during Audio Streaming (C.1)
+TSPC_MPS_6_6	False		HFP-HF and A2DP-SNK Implementation Reject/Ignore
+				Incoming Call during Audio Streaming (C.2)
+TSPC_MPS_6_7	True (*)	HFP-AG and A2DP-SRC Implementation HFP Call
+				Termination during AVP Connection (C.1)
+TSPC_MPS_6_8	False		HFP-HF and A2DP-SNK Implementation HFP Call
+				Termination during AVP Connection (C.2)
+TSPC_MPS_6_9	True (*)	HFP-AG and A2DP-SRC Implementation Press Play
+				on Audio Player during Active Call (C.1)
+TSPC_MPS_6_10	False		HFP-HF and A2DP-SNK Implementation Press Play
+				on Audio Player during Active Call (C.2)
+TSPC_MPS_6_11	True (*)	HFP-AG and A2DP-SRC Implementation Start Audio
+				Streaming after AVRCP Play Command (C.1)
+TSPC_MPS_6_12	False		HFP-HF and A2DP-SNK Implementation Start Audio
+				Streaming after AVRCP Play Command (C.2)
+TSPC_MPS_6_13	True (*)	HFP-AG and A2DP-SRC Implementation Suspend Audio
+				Streaming after AVRCP Pause/Stop (C.1)
+TSPC_MPS_6_14	False		HFP-HF and A2DP-SNK Implementation Suspend Audio
+				Streaming after AVRCP Pause/Stop (C.2)
+TSPC_MPS_6_15	False		HFP-AG and DUN-GW Implementation Data
+				Communication under PSDM (DUN) during Active
+				Voice Call (C.3)
+TSPC_MPS_6_16	False		HFP-HF and DUN-DT Implementation Data
+				Communication under PSDM (DUN) during Active
+				Voice call (C.4)
+TSPC_MPS_6_17	False		HFP-AG and DUN-GW Implementation Outgoing Voice
+				Call during Data Communication under PSDM (DUN)
+				(C.3)
+TSPC_MPS_6_18	False		HFP-HF and DUN-DT Implementation Outgoing Voice
+				Call during Data Communication under PSDM (DUN)
+				(C.4)
+TSPC_MPS_6_19	False		HFP-AG and DUN-GW Implementation Incoming Voice
+				Call during Data Communication under PSDM (DUN)
+				(C.3)
+TSPC_MPS_6_20	False		HFP-HF and DUN-DT Implementation Incoming Voice
+				Call during Data Communication under PSDM (DUN)
+				(C.4)
+TSPC_MPS_6_21	False		A2DP-SRC and DUN-GW Implementation Start Audio
+				Streaming during Data Communication under PSDM
+				(DUN) (C.5)
+TSPC_MPS_6_22	False		A2DP-SNK and DUN-DT Implementation Start Audio
+				Streaming during Data Communication under PSDM
+				(DUN) (C.6)
+TSPC_MPS_6_23	False		A2DP-SRC and DUN-GW Implementation Data
+				Communication Establishment under PSDM (DUN)
+				during Audio Streaming (C.5)
+TSPC_MPS_6_24	False		A2DP-SNK and DUN-DT Implementation Data
+				Communication Establishment under PSDM (DUN)
+				during Audio Streaming (C.6)
+TSPC_MPS_6_25	False		HFP-AG and DUN-GW Implementation Terminate
+				Voice Call/Data Call during Data Communication
+				and Voice Call (C.5)
+TSPC_MPS_6_26	False		HFP-HF and DUN-DT Implementation Terminate
+				Voice Call/Data Call during Data Communication
+				and Voice Call (C.6)
+TSPC_MPS_6_27	True (*)	HFP-AG and PAN-NAP Implementation Data
+				Communication in Personal Area Network during
+				Active Voice Call (C.7)
+TSPC_MPS_6_28	False		HFP-HF and PAN-PANU Implementation Data
+				Communication in Personal Area Network during
+				Active Voice Call (C.8)
+TSPC_MPS_6_29	True (*)	HFP-AG and PAN-NAP Implementation Outgoing
+				Voice Call during Data Communication in Personal
+				Area Network (C.7)
+TSPC_MPS_6_30	False		HFP-HF and PAN-PANU Implementation Outgoing
+				Voice Call during Data Communication in Personal
+				Area Network (C.8)
+TSPC_MPS_6_31	True (*)	HFP-AG and PAN-NAP Implementation Incoming Voice
+				Call during Data Communication in Personal Area
+				Network (C.7)
+TSPC_MPS_6_32	False		HFP-HF and PAN-PANU Implementation Incoming
+				Voice Call during Data Communication in Personal
+				Area Network (C.8)
+TSPC_MPS_6_33	True (*)	A2DP-SRC and PAN-NAP Implementation Start Audio
+				Streaming during Data Communication in Personal
+				Area Network (C.9)
+TSPC_MPS_6_34	False		A2DP-SNK and PAN-PANU Implementation Start Audio
+				Streaming during Data Communication in Personal
+				Area Network (C.10)
+TSPC_MPS_6_35	True (*)	A2DP-SRC and PAN-NAP Implementation Data
+				Communication Establishment in Personal Area
+				Network during Audio Streaming (C.9)
+TSPC_MPS_6_36	False		A2DP-SNK and PAN_PANU Implementation Data
+				Communication Establishment in Personal Area
+				Network during Audio Streaming (C.10)
+TSPC_MPS_6_37	True (*)	A2DP-SRC_PBAP-Server Implementation Phonebook
+				Download during Audio Streaming (C.11)
+TSPC_MPS_6_38	False		A2DP-SNK and PBAP-Client Implementation
+				Phonebook Download during Audio Streaming (C.12)
+TSPC_MPS_6_39	True (*)	HFP-AG and PBAP-Server Implementation PBAP and
+				HFP Connection Behaviour (C.13)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_2_1, TSPC_MPS_2_4 and TSPC_MPS_2_7 are supported,
+	otherwise Excluded.
+C.2: Mandatory if TSPC_MPS_2_2, TSPC_MPS_2_3 and TSPC_MPS_2_8 are supported,
+	otherwise Excluded.
+C.3: Mandatory if TSPC_MPS_2_5 and TSPC_MPS_2_7 are supported and TSPC_MPS_3_9,
+	otherwise Excluded.
+C.4: Mandatory if TSPC_MPS_2_6 and TSPC_MPS_2_8 are supported, otherwise
+	Excluded.
+C.5: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_5 are supported, otherwise
+	Excluded.
+C.6: Mandatory if TSPC_MPS_2_2 and TSPC_MPS_2_6 are supported, otherwise
+	Excluded.
+C.7: Mandatory if TSPC_MPS_2_7 and TSPC_MPS_2_9 and TSPC_MPS_3_11 are
+	supported, otherwise Excluded.
+C.8: Mandatory if TSPC_MPS_2_8 and TSPC_MPS_2_11 are supported and
+	TSPC_MPS_3_11, otherwise Excluded.
+C.9: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_9 are supported, otherwise
+	Excluded.
+C.10: Mandatory if TSPC_MPS_2_2 and TSPC_MPS_2_11 are supported, otherwise
+	Excluded.
+C.11: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_13 are supported, otherwise
+	Excluded.
+C.12: Mandatory if TSPC_MPS_2_2 and TSPC_MPS_2_12 are supported, otherwise
+	Excluded.
+C.13: Mandatory if TSPC_MPS_2_7 and TSPC_MPS_2_13 are supported, otherwise
+	Excluded.
+-------------------------------------------------------------------------------
+
+
+		MPMD Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_7_1	False		HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+				Answer Incoming Call during Audio Streaming
+				(C.1)
+TSPC_MPS_7_2	True (*)	A2DP-SRC and AVRCP-TG Implementation Answer
+				Incoming Call during Audio Streaming (C.2)
+TSPC_MPS_7_3	False		HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+				Outgoing Call during Audio Streaming (C.1)
+TSPC_MPS_7_4	True (*)	A2DP-SRC and AVRCP-TG Implementation Outgoing
+				Call during Audio Streaming (C.2)
+TSPC_MPS_7_5	False		HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+				Reject/Ignore Incoming Call during Audio
+				Streaming (C.1)
+TSPC_MPS_7_6	True (*)	A2DP-SRC and AVRCP-TG Implementation
+				Reject/Ignore Incoming Call during Audio
+				Streaming (C.2)
+TSPC_MPS_7_7	False		HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+				HFP Call Termination during AVP Connection (C.1)
+TSPC_MPS_7_8	True (*)	A2DP-SRC and AVRCP-TG Implementation HFP Call
+				Termination during AVP Connection (C.2)
+TSPC_MPS_7_9	False		HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+				Press Play on Audio Player during Active Call
+				(C.1)
+TSPC_MPS_7_10	True (*)	A2DP-SRC and AVRCP-TG Implementation Press Play
+				on Audio Player during Active Call (C.2)
+TSPC_MPS_7_11	True (*)	A2DP-SRC and AVRCP-TG Implementation Start Audio
+				Streaming during Data Communication under PSDM
+				(C.2)
+TSPC_MPS_7_12	False		A2DP-SNK and AVRCP-CT and DUN-DT Implementation
+				Start Audio Streaming during Data Communication
+				under PSDM (C.3)
+TSPC_MPS_7_13	True (*)	A2DP-SRC and AVRCP-TG Implementation Start
+				Packet Data Communication during Audio Streaming
+				(C.2)
+TSPC_MPS_7_14	False		A2DP-SNK and AVRCP-CT and DUN-DT Implementation
+				Start Packet Data Communication during Audio
+				Streaming (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_2_2, TSPC_MPS_2_3 and TSPC_MPS_2_8 are supported,
+	otherwise Excluded.
+C.2: Mandatory if TSPC_MPS_2_1 and TSPC_MPS_2_4 are supported, otherwise
+	Excluded.
+C.3: Mandatory if TSPC_MPS_2_2, TSPC_MPS_2_3 and 2/6TSPC_MPS_2_6 supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		MPS Procedures
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_8_1	True (*)	AVP Suspension (C.1)
+TSPC_MPS_8_2	True (*)	Profile (Dis-)Connection behaviour (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_1_1 and TSPC_MPS_1_2 are supported, otherwise
+	Excluded.
+C.2: Mandatory if TSPC_MPS_1_1, TSPC_MPS_1_2 and TSPC_MPS_1_4 are supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		MPS Dependencies
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_9_1	True		Implements Bluetooth Core Specification v2.1
+				+ EDR or later (M)
+-------------------------------------------------------------------------------
+
+
+		MPS Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_MPS_10_1	True		SDP Record (M)
+TSPC_MPS_10_2	True (*)	Media Stream Suspension (C.1)
+TSPC_MPS_10_3	True (*)	Sniff Mode during Streaming (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_1_1 and TSPC_MPS_1_4 are supported, otherwise
+	Excluded.
+C.2: Mandatory if TSPC_MPS_1_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-opp.txt b/bluez/repo/android/pics-opp.txt
new file mode 100644
index 0000000..145198d
--- /dev/null
+++ b/bluez/repo/android/pics-opp.txt
@@ -0,0 +1,187 @@
+OPP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1_1	True (*)	Role: Object Push Client (C.1)
+TSPC_OPP_1_2	True (*)	Role: Object Push Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Client Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1b_1	True (*)	Client supports OPP version 1.1. (C.1)
+TSPC_OPP_1b_2	False		Client supports OPP version 1.2. (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Client Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2_1	True		Client: Perform Service Discovery request (M)
+TSPC_OPP_2_2	True		Client: Authentication/PIN exchange supported.
+					(M)
+TSPC_OPP_2_2a	True (*)	Client: Require Authentication/PIN by default.
+					(O)
+TSPC_OPP_2_3	True		Client: Object Push (M)
+TSPC_OPP_2_4	True (*)	Client: vCard 2.1 (C.3)
+TSPC_OPP_2_5	False		Client: vCalender 1.0 (O)
+TSPC_OPP_2_6	False		Client: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_2_7	False		Client: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_2_8	True (*)	Client: Support content formats other than those
+					declared in TSPC_OPP_2_4 through
+					TSPC_OPP_2_7. (O)
+TSPC_OPP_2_8a	False		Client: Support specific set of other content
+					formats. (C.4)
+TSPC_OPP_2_8b	True (*)	Client: Support all content formats. (C.4)
+TSPC_OPP_2_9	True (*)	Client: Push multiple vCard objects. (O)
+TSPC_OPP_2_9a	False		Client: Push multiple vCard objects using
+					different PUT operations. (C.5)
+TSPC_OPP_2_9b	True (*)	Client: Push multiple vCard objects using the
+					same PUT operation. (C.5)
+TSPC_OPP_2_10	False		Client: Push multiple vCalender objects. (O)
+TSPC_OPP_2_10a	False		Client: Push multiple vCalendar objects using
+					different PUT operations. (C.6)
+TSPC_OPP_2_10b	False		Client: Push multiple vCalendar objects using
+					the same PUT operation. (C.6)
+TSPC_OPP_2_11	False		Client: Push multiple vMsg objects. (O)
+TSPC_OPP_2_11a	False		Client: Push multiple vMsg objects using
+					different PUT operations. (C.7)
+TSPC_OPP_2_11b	False		Client: Push multiple vMsg objects using the
+					same PUT operation. (C.7)
+TSPC_OPP_2_12	False		Client: Push multiple vNote objects. (O)
+TSPC_OPP_2_12a	False		Client: Push multiple vNote objects using
+					different PUT operations. (C.8)
+TSPC_OPP_2_12b	False		Client: Push multiple vNote objects using the
+					same PUT operation. (C.8)
+TSPC_OPP_2_13	False		Client: Pull business card (O)
+TSPC_OPP_2_14	False		Client: vCard 2.1 (C.1)
+TSPC_OPP_2_15	False		Client: Exchange business card (O)
+TSPC_OPP_2_16	False		Client: vCard 2.1 (C.2)
+TSPC_OPP_2_17	False		GOEP v2 (C.9)
+TSPC_OPP_2_18	False		GOEP v2 Backward Compability (C.9)
+TSPC_OPP_2_19	False		OBEX over L2CAP (C.9)
+TSPC_OPP_2_20	False		OBEX Reliable Session (C.10)
+TSPC_OPP_2_21	False		OBEX SRM (C.10)
+TSPC_OPP_2_22	False		Send OBEX SRMP header (C.10)
+TSPC_OPP_2_23	False		Receive OBEX SRMP header (C.11)
+-------------------------------------------------------------------------------
+C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is
+	supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+	applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8
+	is supported. Otherwise, both items are excluded.
+C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if
+	TSPC_OPP_2_9 is supported. Otherwise, both items are excluded.
+C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if
+	TSPC_OPP_2_10 is supported. Otherwise, both items are excluded.
+C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if
+	TSPC_OPP_2_11 is supported. Otherwise, both items are excluded.
+C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if
+	TSPC_OPP_2_12 is supported. Otherwise, both items are excluded.
+C.9: Mandatory if TSPC_OPP_1b_2 supported.
+C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded.
+C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+		Server Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2b_1	True (*)	Server supports OPP version 1.1.
+TSPC_OPP_2b_2	False		Server supports OPP version 1.2.
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Server Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_3_1	True		Server: Provide information on supported
+					contents type on service discovery
+					request. (M)
+TSPC_OPP_3_2	True		Server: Authentication/PIN exchange supported.
+					(M)
+TSPC_OPP_3_3	True		Server: Object Push (M)
+TSPC_OPP_3_3a	True (*)	Server: Receive multiple objects in the same
+					PUT operation. (O)
+TSPC_OPP_3_4	True (*)	Server: vCard 2.1 (C.3)
+TSPC_OPP_3_5	False		Server: vCalender 1.0 format (O)
+TSPC_OPP_3_6	False		Server: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_3_7	False		Server: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_3_8	True (*)	Server: Support content formats other than those
+					declared in TSPC_OPP_3_4 through
+					TSPC_OPP_3_7. (O)
+TSPC_OPP_3_8a	False		Server: Support specific set of other content
+					formats. (C.4)
+TSPC_OPP_3_8b	True (*)	Server: Support all content formats. (C.4)
+TSPC_OPP_3_9	True (*)	Server: Object Push vCard reject. (O)
+TSPC_OPP_3_10	False		Server: Object Push vCal reject. (O)
+TSPC_OPP_3_11	False		Server: Object Push vMsg reject. (O)
+TSPC_OPP_3_12	False		Server: Object Push vNote reject. (O)
+TSPC_OPP_3_13	False		Server: Business card pull (O.1)
+TSPC_OPP_3_14	False		Server: vCard 2.1 (C.1)
+TSPC_OPP_3_15	False		Server: Business card pull reject. (O)
+TSPC_OPP_3_16	False		Server: Business card exchange (O.2)
+TSPC_OPP_3_17	False		Server: vCard 2.1 (C.2)
+TSPC_OPP_3_18	False		Server: Business card exchange reject. (O)
+TSPC_OPP_3_19	False		GOEP v2 (C.5)
+TSPC_OPP_3_20	False		GOEP v2 Backward Compability (C.5)
+TSPC_OPP_3_21	False		OBEX over L2CAP (C.5)
+TSPC_OPP_3_22	False		OBEX Reliable Session (C.16)
+TSPC_OPP_3_23	False		OBEX SRM (C.6)
+TSPC_OPP_3_24	False		Send OBEX SRMP header (C.6)
+TSPC_OPP_3_25	False		Receive OBEX SRMP header (C.7)
+-------------------------------------------------------------------------------
+O.1: IF NOT Supported, an error message must be sent on request for Business
+	Card Pull.
+O.2: IF NOT Supported, an error message must be sent on request for Business
+	Card Exchange.
+C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
+	supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+	applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8
+	is supported. Otherwise, both items are excluded.
+C.5: Mandatory if TSPC_OPP_2b_2 supported.
+C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded.
+C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+		Additional OPP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_4_1	False		Abort-Push Operation (O)
+TSPC_OPP_4_2	False		Intentionally Left Blank (N/A)
+TSPC_OPP_4_3	False		Multiple vCards transferred as a single vObject
+					(C.1)
+TSPC_OPP_4_4	False		Multiple vCards transfer (C.1)
+TSPC_OPP_4_5	False		vCards with multiple Phone Number Fields (C.1)
+TSPC_OPP_4_6	False		Push vCal to Different Time Zone Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-pan.txt b/bluez/repo/android/pics-pan.txt
new file mode 100644
index 0000000..57863c0
--- /dev/null
+++ b/bluez/repo/android/pics-pan.txt
@@ -0,0 +1,152 @@
+PAN PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PAN_1_1	True (*)	Role: Network Access Point (O.1)
+TSPC_PAN_1_2	False		Role: Group Ad-hoc Network (O.1)
+TSPC_PAN_1_3	True (*)	Role: PAN User (O.1)
+TSPC_PAN_1a_1	True		BNEP: BNEP Connection Setup (M)
+TSPC_PAN_1a_2	True		BNEP: BNEP Data Packet Reception (M)
+TSPC_PAN_1a_3	True		BNEP: BNEP Data Packet Transmission (M)
+TSPC_PAN_1a_3a	True		BNEP: BNEP Compressed Packet Transmission (O)
+TSPC_PAN_1a_3b	True		BNEP: BNEP Compressed Packet Transmission
+								Source Only (O)
+TSPC_PAN_1a_4	True		BNEP: BNEP Control Message Processing (M)
+TSPC_PAN_1a_5	True		BNEP: BNEP Extension Header Processing (M)
+TSPC_PAN_1a_6	False		BNEP: Network Protocol Filter Message
+					Transmission (O)
+TSPC_PAN_1a_7	False		BNEP: Multicast Address Filter Message
+					Transmission (O)
+-------------------------------------------------------------------------------
+O.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Network Access Point Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PAN_2_1	True		NAP: Support BNEP (M)
+TSPC_PAN_2_2	True		NAP: Support BNEP Forwarding (M)
+TSPC_PAN_2_3	False		NAP: Support Layer 2-Bridging between PAN and
+					External Network (C.1)
+TSPC_PAN_2_4	True (*)	NAP: Support IP forwarding between PAN and
+					External Network (C.1)
+TSPC_PAN_2_5	False		NAP: Support BNEP Packet Filtering (O)
+TSPC_PAN_2_6	False		NAP: Support IPv4 (C.2)
+TSPC_PAN_2_6a	False		NAP: Supports operable routable IPv4 address (O)
+TSPC_PAN_2_6b	False		NAP: Support link-local address configuration
+					for IPv4 (C.4)
+TSPC_PAN_2_7	False		NAP: Support ping client for IPv4 (O)
+TSPC_PAN_2_8	False		NAP: Support DHCP Client for IPv4 (O)
+TSPC_PAN_2_9	False		NAP: Support DNS/LLMNR Resolver for IPv4 (O)
+TSPC_PAN_2_9a	False (*)	NAP: Support LLMNR Sender for IPv4 (C.5)
+TSPC_PAN_2_9b	False		NAP: Support LLMNR Responder for IPv4 (O)
+TSPC_PAN_2_10	False		NAP: Support HTTP Client for IPv4 (O)
+TSPC_PAN_2_11	False		NAP: Support WAP Client for IPv4 (O)
+TSPC_PAN_2_12	False		NAP: Support IPv6 (C.3)
+TSPC_PAN_2_13	False		NAP: Support ping client for IPv6 (O)
+TSPC_PAN_2_14	False		NAP: Support DNS/LLMNR Resolver for IPv6 (O)
+TSPC_PAN_2_14a	False (*)	NAP: Support LLMNR Sender for IPv6 (C.6)
+TSPC_PAN_2_14b	False		NAP: Support LLMNR Responder for IPv6 (O)
+TSPC_PAN_2_15	False		NAP: Support HTTP Client for IPv6 (O)
+TSPC_PAN_2_16	False		NAP: Support WAP Client for IPv6 (O)
+TSPC_PAN_2_17	True		NAP: Supports Connectable Mode (M)
+TSPC_PAN_2_18	True		NAP: NAP Service Record (M)
+TSPC_PAN_2_19	False		NAP: Support at least three PANUs (O)
+TSPC_PAN_2_20	False		NAP: Support at least two PANUs (O)
+-------------------------------------------------------------------------------
+Note that support for IP-related features only applies to the PAN interface of
+	the NAP (i.e. If the IP stack is accessible by PANUs).
+C.1: Network Access Point devices MUST support either (TSPC_PAN_2_3)
+	OR (TSPC_PAN_2_4).
+C.2: Mandatory to support IF any IPv4-based transport protocol OR
+	(TSPC_PAN_2_7-11) is supported, ELSE Optional.
+C.3: Mandatory to support IF any IPv6-based transport protocol OR
+	(TSPC_PAN_2_13-16) is supported, ELSE Optional.
+C.4: Mandatory if TSPC_PAN_2_6 is supported and TSPC_PAN_2_6a is not supported,
+	otherwise optional.
+C.5: Mandatory if item (TSPC_PAN_2_6) supported.
+C.6: Mandatory if item (TSPC_PAN_2_12) supported
+-------------------------------------------------------------------------------
+
+
+		Group Ad-hoc Network Application Features
+			(GN Application Features)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PAN_3_1	False (*)	GN: Support BNEP (M)
+TSPC_PAN_3_2	False (*)	GN: Support BNEP Forwarding (M)
+TSPC_PAN_3_3	False		GN: Support BNEP Packet Filtering (O)
+TSPC_PAN_3_4	False		GN: Support IPv4 (C.1)
+TSPC_PAN_3_5	False		GN: Support ping client for IPv4 (O)
+TSPC_PAN_3_6	False		GN: Support DHCP Client for IPv4 (O)
+TSPC_PAN_3_7	False		GN: Support DNS/LLMNR Resolver for IPv4 (O)
+TSPC_PAN_3_7a	False (*)	GN: Support LLMNR Sender for IPv4 (C.3)
+TSPC_PAN_3_7b	False		GN: Support LLMNR Responder for IPv4 (O)
+TSPC_PAN_3_8	False		GN: Support HTTP Client for IPv4 (O)
+TSPC_PAN_3_9	False		GN: Support WAP Client for IPv4 (O)
+TSPC_PAN_3_10	False		GN: Support IPv6 (C.2)
+TSPC_PAN_3_11	False		GN: Support ping client for IPv6 (O)
+TSPC_PAN_3_12	False		GN: Support DNS/LLMNR Resolver for IPv6 (O)
+TSPC_PAN_3_12a	False (*)	GN: Support LLMNR Sender for IPv6 (C.4)
+TSPC_PAN_3_12b	False		GN: Support LLMNR Responder for IPv6 (O)
+TSPC_PAN_3_13	False		GN: Support HTTP Client for IPv6 (O)
+TSPC_PAN_3_14	False		GN: Support WAP Client for IPv6 (O)
+TSPC_PAN_3_15	False (*)	GN: Supports Connectable Mode (M)
+TSPC_PAN_3_16	False (*)	GN: GN Service Record (M)
+TSPC_PAN_3_17	False		GN: Support at least three PANUs (O)
+TSPC_PAN_3_18	False		GN: Support at least two PANUs (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support IF any IPv4-based transport protocol OR
+	(TSPC_PAN_3_5-9) is supported, ELSE Optional.
+C.2: Mandatory to support IF any IPv6-based transport protocol OR
+	(TSPC_PAN_3_11-14) is supported, ELSE Optional.
+C.3: Mandatory to support IF (TSPC_PAN_3_4) is supported.
+C.4: Mandatory to support if (TSPC_PAN_3_10) is supported.
+-------------------------------------------------------------------------------
+
+
+		PAN User Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PAN_4_1	True		PANU: Support BNEP (M)
+TSPC_PAN_4_2	True (*)	PANU: Support IPv4 (C.1)
+TSPC_PAN_4_3	False		PANU: Support ping client for IPv4 (O)
+TSPC_PAN_4_4	False		PANU: Support DHCP client for  IPv4 (O)
+TSPC_PAN_4_5	False		PANU: Support DNS/LLMNR Resolver for IPv4 (O)
+TSPC_PAN_4_5a	False (*)	PANU: Support LLMNR Sender for IPv4 (C.2)
+				Reference: SE #3558, TSE #4382, TCW #448
+TSPC_PAN_4_5b	False		PANU: Support LLMNR Responder for IPv4 (O)
+TSPC_PAN_4_6	False		PANU: Support HTTP Client for IPv4 (O)
+TSPC_PAN_4_7	False		PANU: Support WAP Client for IPv4 (O)
+TSPC_PAN_4_8	False		PANU: Support IPv6 (C.1)
+TSPC_PAN_4_9	False		PANU: Support ping client for IPv6 (O)
+TSPC_PAN_4_10	False		PANU: Support DNS/LLMNR Resolver for IPv6 (O)
+TSPC_PAN_4_10a	False (*)	PANU: Support LLMNR Sender for IPv6 (C.3)
+TSPC_PAN_4_10b	False		PANU: Support LLMNR Responder for IPv6 (O)
+TSPC_PAN_4_11	False		PANU: Support HTTP Client for IPv6 (O)
+TSPC_PAN_4_12	False		PANU: Support WAP Client for IPv6 (O)
+TSPC_PAN_4_13	False		PANU: Support connections to multi-user
+					NAPs/GNs (O)
+TSPC_PAN_4_14	False		PANU: Supports Connectable Mode (O)
+TSPC_PAN_4_15	False		PANU: PANU Service Record (O)
+TSPC_ALL	False		Turns on all the test cases
+-------------------------------------------------------------------------------
+C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or
+	(TSPC_PAN_4_8).
+C.2: Mandatory to support if (TSPC_PAN_4_2) is supported.
+C.3: Mandatory to support if (TSPC_PAN_4_8) is supported.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-pbap.txt b/bluez/repo/android/pics-pbap.txt
new file mode 100644
index 0000000..bc01d8a
--- /dev/null
+++ b/bluez/repo/android/pics-pbap.txt
@@ -0,0 +1,475 @@
+PBAP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_1_1	False		Role: PCE (C.1)
+TSPC_PBAP_1_2	True (*)	Role: PSE (C.1)
+-------------------------------------------------------------------------------
+C1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Client Major Profile Version (X.Y)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_2a_1	False		PBAP 1.0 (C.1)
+TSPC_PBAP_2a_2	False		PBAP 1.1 (C.1)
+TSPC_PBAP_2a_3	False		PBAP 1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one major profile version.
+-------------------------------------------------------------------------------
+
+
+		Client Minor Profile Version (X.Y)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_2b_1	False		PBAP 1.1.1 (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if 2a/2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported features (PCE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_2_1	False (*)	PCE: Phone Book Download (C.1)
+TSPC_PBAP_2_2	False (*)	PCE: Phone Book Browsing (C.1)
+TSPC_PBAP_2_3	False (*)	PCE: Session Management (M)
+TSPC_PBAP_2_4	False		PCE: Able to Request Size of the Phonebook (O)
+TSPC_PBAP_2_5	False		PCE: Database Identifier (C.2)
+TSPC_PBAP_2_6	False		PCE: Folder Version Counters (C.2)
+TSPC_PBAP_2_7	False		PCE: vCard Selecting (C.2)
+TSPC_PBAP_2_7a	False		PCE: Able to send vCardSelector (C.2)
+TSPC_PBAP_2_7b	False		PCE: Able to send vCardSelectorOperator (C.2)
+TSPC_PBAP_2_8	False (*)	PCE: Enhanced Missed Calls (C.4)
+TSPC_PBAP_2_8a	False (*)	PCE: Able to reset the missed Calls (C.2)
+TSPC_PBAP_2_9	False		PCE: X-BT-UCI vCard Field (C.2)
+TSPC_PBAP_2_9a	False		PCE: Able to request X-BT-UCI Field (C.2)
+TSPC_PBAP_2_10	False		PCE: X-BT-UID vCard Field (C.2)
+TSPC_PBAP_2_10a	False		PCE: Referencing Contacts (C.2)
+TSPC_PBAP_2_12	False		PCE: Contact Image Default Format (C.2)
+TSPC_PBAP_2_12a	False		PCE: Able to request Contact Images (C.2)
+TSPC_PBAP_2_13	False		PCE: Supported Phonebook Objects (C.3)
+TSPC_PBAP_2_13a	False (*)	PCE: Telecom/pb (C.3)
+TSPC_PBAP_2_13b	False		PCE: Telecom/ich (C.3)
+TSPC_PBAP_2_13c	False		PCE: Telecom/och (C.3)
+TSPC_PBAP_2_13d	False (*)	PCE: Telecom/mch (C.3)
+TSPC_PBAP_2_13e	False (*)	PCE: Telecom/cch (C.3)
+TSPC_PBAP_2_13f	False		PCE: Telecom/spd (C.3)
+TSPC_PBAP_2_13g	False		PCE: Telecom/fav (C.3)
+TSPC_PBAP_2_13h	False		PCE: SIM1/Telecom/pb (C.3)
+TSPC_PBAP_2_13i	False		PCE: SIM1/Telecom/ich (C.3)
+TSPC_PBAP_2_13j	False		PCE: SIM1/Telecom/och (C.3)
+TSPC_PBAP_2_13k	False		PCE: SIM1/Telecom/mch (C.3)
+TSPC_PBAP_2_13l	False		PCE: SIM1/Telecom/cch (C.3)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined features.
+C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.3: Mandatory to support at least one of the listed phonebook objects .
+C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders
+	(13d,13e,13k,13l) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Phone Book Download functions (PCE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_3_1	False (*)	PCE: Pull Phone Book (C.1)
+-------------------------------------------------------------------------------
+C1: Mandatory for PCE if Phone Book Download (TSPC_PBAP_2_1) is supported,
+	otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported Phone Book Browsing functions (PCE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_4_1	False (*)	PCE: Set Phone Book (C.1)
+TSPC_PBAP_4_2	False (*)	PCE: Pull vCard Listing (C.1)
+TSPC_PBAP_4_3	False (*)	PCE: Pull vCard Entry (C.1)
+-------------------------------------------------------------------------------
+C1: Mandatory for PCE if Phone Book Browsing TSPC_PBAP_2_2 is supported,
+	otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		Used vCard formats (PCE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_5_1	False (*)	PCE: vCard 2.1 (C.1)
+TSPC_PBAP_5_2	False (*)	PCE: vCard 3.0 (C.1)
+-------------------------------------------------------------------------------
+C1: It is mandatory to support at least one of the defined versions if PCE
+	supported.
+-------------------------------------------------------------------------------
+
+
+		OBEX Functions for PCE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_6_1	False (*)	PCE: Connect (M)
+TSPC_PBAP_6_2	False (*)	PCE: Disconnect (M)
+TSPC_PBAP_6_3	False (*)	PCE: Get (M)
+TSPC_PBAP_6_4	False		PCE: Abort (O)
+TSPC_PBAP_6_5	False (*)	PCE: SetPath (C.1)
+TSPC_PBAP_6_6	False		PCE: Support for OBEX authentication initiation
+					(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_2_2 (Phone Book Browsing) is supported,
+	otherwise Excluded.
+C.2: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or
+	TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		PCE OBEX Header Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_7_1	False (*)	PCE: Name (M)
+TSPC_PBAP_7_2	False (*)	PCE: Type (M)
+TSPC_PBAP_7_3	False (*)	PCE: Body (M)
+TSPC_PBAP_7_4	False (*)	PCE: End of Body (M)
+TSPC_PBAP_7_5	False (*)	PCE: Target (M)
+TSPC_PBAP_7_6	False (*)	PCE: Who (M)
+TSPC_PBAP_7_7	False (*)	PCE: Connection ID (M)
+TSPC_PBAP_7_8	False (*)	PCE: Authentication Challenge (M)
+TSPC_PBAP_7_9	False (*)	PCE: Authentication Response (M)
+TSPC_PBAP_7_10	False (*)	PCE: Application Parameters (M)
+TSPC_PBAP_7_11	False		PCE: Single Response Mode (C.1)
+TSPC_PBAP_7_12	False		PCE: Single Response Mode Parameter
+					(ability to parse) (C.1)
+TSPC_PBAP_7_13	False		PCE: Single Response Mode Parameter
+					(ability to send) (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.2: Optional if TSPC_PBAP_2a_3 (PBAP 1.2) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		OBEX Error Codes for PCE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_8_1	False (*)	PCE: Bad Request (M)
+TSPC_PBAP_8_2	False (*)	PCE: Not Implemented (M)
+TSPC_PBAP_8_3	False (*)	PCE: Unauthorized (M)
+TSPC_PBAP_8_4	False (*)	PCE: Precondition Failed (M)
+TSPC_PBAP_8_5	False (*)	PCE: Not Found (M)
+TSPC_PBAP_8_6	False (*)	PCE: Not Acceptable (M)
+TSPC_PBAP_8_7	False (*)	PCE: Service Unavailable (M)
+TSPC_PBAP_8_8	False (*)	PCE: Forbidden (M)
+-------------------------------------------------------------------------------
+
+
+		Server Major Profile Version (X.Y)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_9a_1	False		PBAP 1.0 (C.1)
+TSPC_PBAP_9a_2	True (*)	PBAP 1.1 (C.1)
+TSPC_PBAP_9a_3	False		PBAP 1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one major profile version.
+-------------------------------------------------------------------------------
+
+
+		Server Minor Profile Version (X.Y.Z)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_9b_1	False		PBAP 1.1.1 (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if 9a/2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Supported features (PSE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_9_1	True		PSE: Phone Book Download (M)
+TSPC_PBAP_9_2	True		PSE: Phone Book Browsing (M)
+TSPC_PBAP_9_3	True		PSE: Session Management (M)
+TSPC_PBAP_9_4	True		PSE: Able to request the size of the Phonebook
+					(M)
+TSPC_PBAP_9_5	False		PSE: Database Identifier (C.1)
+TSPC_PBAP_9_5a	False		PSE: Able to keep a persistent Database
+					Identifier (C.2)
+TSPC_PBAP_9_5b	False		PSE: Able to regenerate a Database Identifier
+					(C.2)
+TSPC_PBAP_9_6	False		PSE: Folder Version Counters (C.1)
+TSPC_PBAP_9_6a	False		PSE: Able to Insert or Remove Entries (C.2)
+TSPC_PBAP_9_6b	False		PSE: Able to Modify contact primary Fields (C.2)
+TSPC_PBAP_9_6c	False		PSE: Able to Modify contact secondary Fields
+					(C.2)
+TSPC_PBAP_9_7	False (*)	PSE: vCard Selecting (C.1)
+TSPC_PBAP_9_8	False (*)	PSE: Enhanced Missed Calls (C.4)
+TSPC_PBAP_9_9	False		PSE: X-BT-UCI vCard Field (C.2)
+TSPC_PBAP_9_10	False		PSE: X-BT-UID vCard Field (C.2)
+TSPC_PBAP_9_10a	False		PSE: Referencing Contacts (C.3)
+TSPC_PBAP_9_12	False		PSE: Contact Image Default Format (C.1)
+TSPC_PBAP_9_12a	False		PSE: Able to request Contact Images (C.2)
+TSPC_PBAP_9_13	False		PSE: Supported Phonebook Objects
+TSPC_PBAP_9_13a	True 		PSE: Telecom/pb (M)
+TSPC_PBAP_9_13b	True  (*)	PSE: Telecom/ich (O)
+TSPC_PBAP_9_13c	True  (*)	PSE: Telecom/och (O)
+TSPC_PBAP_9_13d	True  (*)	PSE: Telecom/mch (O)
+TSPC_PBAP_9_13e	True		PSE: Telecom/cch (O)
+TSPC_PBAP_9_13f	False		PSE: Telecom/spd (C.2)
+TSPC_PBAP_9_13g	False		PSE: Telecom/fav (C.2)
+TSPC_PBAP_9_13h	False (*)	PSE: SIM1/Telecom/pb (O)
+TSPC_PBAP_9_13i	False		PSE: SIM1/Telecom/ich (O)
+TSPC_PBAP_9_13j	False		PSE: SIM1/Telecom/och (O)
+TSPC_PBAP_9_13k	False (*)	PSE: SIM1/Telecom/mch (O)
+TSPC_PBAP_9_13l	False		PSE: SIM1/Telecom/cch (O)
+TSPC_PBAP_9_14	False		PSE: Deleted Handles Behavior
+TSPC_PBAP_9_14a	True		PSE: Error reporting (C.5)
+TSPC_PBAP_9_14b	False		PSE: Change tracking (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.3: Optional if TSPC_PBAP_9_10 (X-BT-UID vCard Property) is supported,
+	otherwise Excluded.
+C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders
+	(13d,13e,13k,13l) are supported, otherwise Excluded.
+C.5: It is mandatory to support at least one of the defined deleted handles
+	behaviors.
+-------------------------------------------------------------------------------
+
+
+		Supported Phone Book Download functions ( PSE )
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_10_1	True		PSE: Pull Phone Book (M)
+TSPC_PBAP_10_2	False		PSE: Call History Function (O)
+-------------------------------------------------------------------------------
+
+
+		Supported Phone Book Browsing functions ( PSE )
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_11_1	True		PSE: Set Phone Book (M)
+TSPC_PBAP_11_2	True		PSE: Pull vCard Listing (M)
+TSPC_PBAP_11_3	True		PSE: Pull vCard Entry (M)
+-------------------------------------------------------------------------------
+
+
+		Used vCard formats (PSE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_12_1	True		PSE: vCard 2.1 (M)
+TSPC_PBAP_12_2	True		PSE: vCard 3.0 (M)
+-------------------------------------------------------------------------------
+
+
+		OBEX Functions for PSE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_13_1	True		PSE: Connect (M)
+TSPC_PBAP_13_2	True		PSE: Disconnect (M)
+TSPC_PBAP_13_3	True		PSE: Get (M)
+TSPC_PBAP_13_4	True		PSE: Abort (M)
+TSPC_PBAP_13_5	True		PSE: SetPath (M)
+TSPC_PBAP_13_6	False		PSE: Support for OBEX authentication initiation
+					(C.1)
+-------------------------------------------------------------------------------
+C.1: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or
+	TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		PSE OBEX Header Support
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_14_1	True		PSE: Name (M)
+TSPC_PBAP_14_2	True		PSE: Type (M)
+TSPC_PBAP_14_3	True		PSE: Body (M)
+TSPC_PBAP_14_4	True		PSE: End of Body (M)
+TSPC_PBAP_14_5	True		PSE: Target (M)
+TSPC_PBAP_14_6	True		PSE: Who (M)
+TSPC_PBAP_14_7	True		PSE: Connection ID (M)
+TSPC_PBAP_14_8	True		PSE: Authentication Challenge (M)
+TSPC_PBAP_14_9	True		PSE: Authentication Response (M)
+TSPC_PBAP_14_10	True		PSE: Application Parameters (M)
+TSPC_PBAP_14_11	False		PSE: Single Response Mode (C.1)
+TSPC_PBAP_14_12	False		PSE: Single Response Mode Parameter
+					(ability to parse) (C.1)
+TSPC_PBAP_14_13	False		PSE: Single Response Mode Parameter
+					(ability to send) (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.2: Optional if TSPC_PBAP_9a_3 (PBAP 1.2) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		OBEX Error Codes for PSE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_15_1	True		PSE: Bad Request (M)
+TSPC_PBAP_15_2	True		PSE: Not Implemented (M)
+TSPC_PBAP_15_3	True (*)	PSE: Unauthorized (O)
+TSPC_PBAP_15_4	True (*)	PSE: Precondition Failed (C.1)
+TSPC_PBAP_15_5	True		PSE: Not Found (M)
+TSPC_PBAP_15_6	True (*)	PSE: Not Acceptable (O)
+TSPC_PBAP_15_7	True		PSE: Service Unavailable (M)
+TSPC_PBAP_15_8	True (*)	PSE: Forbidden (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_9_14a (Error reporting) is supported, otherwise
+	Optional.
+-------------------------------------------------------------------------------
+
+
+		GAP Modes for PCE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_16_1	False (*)	PCE: General discoverable mode (M)
+TSPC_PBAP_16_2	False (*)	PCE: Pairable mode (M)
+-------------------------------------------------------------------------------
+
+
+		GAP Modes for PSE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_17_1	True		PSE: General discoverable mode (M)
+TSPC_PBAP_17_2	True		PSE: Pairable mode (M)
+-------------------------------------------------------------------------------
+
+
+		GAP Security Modes for PCE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_18_1	False (*)	PCE: Authentication Procedure (M)
+TSPC_PBAP_18_2	False (*)	PCE: Initiate LMP-Authentication (M)
+TSPC_PBAP_18_3	False		PCE: Security mode 1 (C.1)
+TSPC_PBAP_18_4	False		PCE: Security mode 2 (C.1)
+TSPC_PBAP_18_5	False		PCE: Security mode 3 (C.1)
+TSPC_PBAP_18_6	False		PCE: Security mode 4 (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of TSPC_PBAP_18_4, TSPC_PBAP_18_5 or TSPC_PBAP_18_6
+	(security mode 2, 3, or 4) shall be supported.
+-------------------------------------------------------------------------------
+
+
+		GAP Security Modes for PSE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_19_1	True		PSE: Authentication Procedure (M)
+TSPC_PBAP_19_2	True		PSE: Initiate LMP-Authentication (M)
+TSPC_PBAP_19_3	False		PSE: Security mode 1 (C.2)
+TSPC_PBAP_19_4	False		PSE: Security mode 2 (C.1)
+TSPC_PBAP_19_5	False		PSE: Security mode 3 (C.1)
+TSPC_PBAP_19_6	True (*)	PSE: Security mode 4 (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of TSPC_PBAP_19_3, TSPC_PBAP_19_4, TSPC_PBAP_19_5 or
+	TSPC_PBAP_19_6 (security mode 2, 3, or 4) shall be supported.
+C.2: Excluded in PSE.
+-------------------------------------------------------------------------------
+
+
+		GAP Idle Modes for PSE
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_21_1	True		PSE: Initiation of General Inquiry (M)
+TSPC_PBAP_21_2	False		PSE: Initiation of Limited Inquiry (O)
+-------------------------------------------------------------------------------
+
+
+		SDP Attributes (PCE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_22_1	False (*)	PCE: BluetoothProfileDescriptorList (M)
+-------------------------------------------------------------------------------
+
+		SDP Attributes (PSE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_23_1	True		PSE: ProtocolDescriptorList (M)
+TSPC_PBAP_23_2	True		PSE: BluetoothProfileDescriptorList (M)
+-------------------------------------------------------------------------------
+
+
+		Additional PBAP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_24_1	False		PCE: Retrieve Large Phone Book (C.1)
+TSPC_PBAP_24_2	False		PSE: Transfer Large Phone Book (C.2)
+TSPC_PBAP_24_3	False		PCE: Retrieve Empty Phone Book (C.1)
+TSPC_PBAP_24_4	False		PSE: Transfer Empty Phone Book (C.2)
+TSPC_PBAP_24_5	False		PSE: Return Phonebook – Limit number of entries
+					(C.2)
+TSPC_PBAP_24_6	False		PSE: Return vCard listing – Limit number of
+					entries  (C.2)
+TSPC_PBAP_24_7	False		PSE: Phone Book Order (C.2)
+TSPC_PBAP_24_8	False		PSE: Call stack timestamps (C.3)
+TSPC_PBAP_24_9	False		PSE: No User Interaction (C.2)
+TSPC_PBAP_24_10	False		PSE: Special Character Handling  (C.2)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_PBAP_2_1 is supported, otherwise excluded.
+C.2: Optional if TSPC_PBAP_1_2 is supported, otherwise excluded.
+C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+		GOEP 2.0 or later Features (PCE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_25_1	False		PCE: GOEP v2.0 or later (M)
+TSPC_PBAP_25_2	False (*)	PCE: GOEP v2 Backwards Compatibility (M)
+TSPC_PBAP_25_3	False		PCE: OBEX over L2CAP (M)
+TSPC_PBAP_25_4	False		PCE: OBEX SRM (M)
+TSPC_PBAP_25_5	False (*)	PCE: Send OBEX SRMP header (C.1)
+TSPC_PBAP_25_6	False		PCE: Receive OBEX SRMP header (M)
+-------------------------------------------------------------------------------
+C.1: Optional to support if TSPC_PBAP_25_4 (OBEX SRM) is supported,
+	otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		GOEP 2.0 or later Features (PSE)
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_26_1	False		PSE: GOEP v2.0 or later (M)
+TSPC_PBAP_26_2	False (*)	PSE: GOEP v2 Backwards Compatibility (M)
+TSPC_PBAP_26_3	False		PSE: OBEX over L2CAP (M)
+TSPC_PBAP_26_4	False		PSE: OBEX SRM (M)
+TSPC_PBAP_26_5	False (*)	PSE: Send OBEX SRMP header (C.1)
+TSPC_PBAP_26_6	False		PSE: Receive OBEX SRMP header (M)
+TSPC_ALL	False		Turns on all the test cases
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_PBAP_26_4 (OBEX SRM) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-rfcomm.txt b/bluez/repo/android/pics-rfcomm.txt
new file mode 100644
index 0000000..032ee73
--- /dev/null
+++ b/bluez/repo/android/pics-rfcomm.txt
@@ -0,0 +1,65 @@
+RFCOMM PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Protocol Version
+-------------------------------------------------------------------------------
+Parameter Name		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_RFCOMM_0_1		False		RFCOMM 1.1 with TS 07.10 (C.1)
+TSPC_RFCOMM_0_2		True (*)	RFCOMM 1.2 with TS 07.10 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one of the protocol versions.
+-------------------------------------------------------------------------------
+
+
+		Supported Procedures
+-------------------------------------------------------------------------------
+Parameter Name		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_RFCOMM_1_1		True (*)	Initialize RFCOMM Session (C.2)
+TSPC_RFCOMM_1_2		True (*)	Respond to Initialization of an RFCOMM
+					Session (C.1)
+TSPC_RFCOMM_1_3		True		Shutdown RFCOMM Session (M)
+TSPC_RFCOMM_1_4		True		Respond to a Shutdown of an RFCOMM
+					Session (M)
+TSPC_RFCOMM_1_5		True (*)	Establish DLC (C.2)
+TSPC_RFCOMM_1_6		True (*)	Respond to Establishment of a DLC (C.1)
+TSPC_RFCOMM_1_7		True		Disconnect DLC (M)
+TSPC_RFCOMM_1_8		True		Respond to Disconnection of a DLC (M)
+TSPC_RFCOMM_1_9		True		Respond to and send MSC Command (M)
+TSPC_RFCOMM_1_10	True		Initiate Transfer Information (M)
+TSPC_RFCOMM_1_11	True		Respond to Test Command (M)
+TSPC_RFCOMM_1_12	True (*)	Send Test Command (O)
+TSPC_RFCOMM_1_13	True		React to Aggregate Flow Control (M)
+TSPC_RFCOMM_1_14	True		Respond to RLS Command (M)
+TSPC_RFCOMM_1_15	False		Send RLS Command (O)
+TSPC_RFCOMM_1_16	True		Respond to PN Command (M)
+TSPC_RFCOMM_1_17	True (*)	Send PN Command (C.3)
+TSPC_RFCOMM_1_18	True (*)	Send Non-Supported Command (NSC)
+					response (C.4)
+TSPC_RFCOMM_1_19	True		Respond to RPN Command (M)
+TSPC_RFCOMM_1_20	True (*)	Send RPN Command (O)
+TSPC_RFCOMM_1_21	True (*)	Closing Multiplexer by First Sending
+					a DISC Command (O)
+TSPC_RFCOMM_1_22	True		Support of Credit Based Flow Control (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if SPP 1/2 (Device B) is supported, otherwise Excluded.
+C.2: Mandatory if SPP 1/1 (Device A) is supported, otherwise Excluded.
+C.3: Mandatory if SPP 1/1 (Device A) is supported, otherwise Optional.
+C.4: Mandatory to support if TSPC_RFCOMM_0_2 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+Parameter Name		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_2_1		False		Channel the tester should use when
+					connecting to the IUT (Default "")
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-scpp.txt b/bluez/repo/android/pics-scpp.txt
new file mode 100644
index 0000000..601ac32
--- /dev/null
+++ b/bluez/repo/android/pics-scpp.txt
@@ -0,0 +1,143 @@
+ScPP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+		Connection Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_1_1	False (*)	Scan Server (C.1)
+TSPC_ScPP_1_2	True		Scan Client (C.1)
+-------------------------------------------------------------------------------
+Note: Mandatory to support at least one of TSPC_ScPP_1_1 or TSPC_ScPP_1_2.
+-------------------------------------------------------------------------------
+
+
+		Transport Requirements
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_2_1	False (*)	Profile supported over BR/EDR (C.1)
+TSPC_ScPP_2_2	True		Profile supported over LE (M)
+-------------------------------------------------------------------------------
+C.1: Excluded for this profile.
+-------------------------------------------------------------------------------
+
+
+		Services - Scan Server
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_3_1	False (*)	Implements Scan Parameters Service (M)
+-------------------------------------------------------------------------------
+
+
+		GAP Requirements - Scan Server Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_4_1	False (*)	Peripheral (M)
+TSPC_ScPP_4_2	False (*)	Directed Connectable Mode (O)
+TSPC_ScPP_4_3	False (*)	Undirected Connectable Mode (M)
+TSPC_ScPP_4_4	False (*)	Bondable mode (peripheral) (O)
+TSPC_ScPP_4_5	False (*)	Bonding procedure (peripheral) (O)
+TSPC_ScPP_4_6	False (*)	LE Security Mode 1 (peripheral) (M)
+-------------------------------------------------------------------------------
+
+
+		Scan Server
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_5_1	False (*)	SM 2.3.1-
+TSPC_ScPP_5_2	False (*)	Unauthenticated no MITM protection. (LE Security
+				Level 2, Just Works) (O)
+TSPC_ScPP_5_3	False (*)	Authenticated MITM protection (LE Security
+				Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
+
+
+		Client Services Support - Scan Client Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_6_1	True		Scan Parameters Service (M)
+-------------------------------------------------------------------------------
+
+
+		Discover Services and Characteristics - Scan Client Role
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_7_1	True		Discover Scan Parameters Service (M)
+TSPC_ScPP_7_2	True		Discover Scan Parameters characteristic:
+				Scan interval Window (M)
+TSPC_ScPP_7_3	True		Discover Scan Parameters characteristic:
+				Scan Refresh (M)
+TSPC_ScPP_7_4	True		Discover Scan Parameters characteristic:
+				Scan Refresh – Client Characteristic
+				Configuration Descriptor (M)
+-------------------------------------------------------------------------------
+
+
+		Features - Client
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_8_1	True		Write Scan Interval Window characteristic (M)
+TSPC_ScPP_8_2	True		Configure Scan Refresh characteristic:
+				Client Characteristic Configuration
+				characteristic descriptor with 0x0001 (M)
+TSPC_ScPP_8_3	True		Notify Scan Refresh characteristic (M)
+-------------------------------------------------------------------------------
+
+
+		GATT Requirements - Scan Client
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_9_1	True		Attribute Protocol supported over LE Transport
+				(M)
+TSPC_ScPP_9_2	True		Generic Attribute Profile Client (M)
+TSPC_ScPP_9_3	True		Discover All Primary Services (C.1)
+TSPC_ScPP_9_4	True		Discover Primary Services by Service UUID (C.1)
+TSPC_ScPP_9_5	True		Discover All Characteristics of a Service (C.2)
+TSPC_ScPP_9_6	True		Discover Characteristics by UUID (C.2)
+TSPC_ScPP_9_7	True		Discover All Characteristic Descriptors (M)
+TSPC_ScPP_9_8	True		Write without Response (M)
+TSPC_ScPP_9_9	True		Write Characteristic Descriptors (M)
+TSPC_ScPP_9_10	True		Notifications (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one of these sub-procedures for service discovery
+C.2: Mandatory to support one of these sub-procedures for characteristic
+	discovery
+-------------------------------------------------------------------------------
+
+
+		GAP Requirements - Scan Client
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_10_1	True		Central (M)
+TSPC_ScPP_10_2	True		Bondable mode (central) (O)
+TSPC_ScPP_10_3	True		Bonding procedure (central) (O)
+TSPC_ScPP_10_4	True		Central (M)
+-------------------------------------------------------------------------------
+
+
+		SM Requirements - Scan Client
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_ScPP_11_1	True		No Security Requirements (LE Security Level 1,
+				No Security) (M)
+TSPC_ScPP_11_2	True		Unauthenticated no MITM protection (LE Security
+				Level 2, Just Works) (O)
+TSPC_ScPP_11_3	True		Authenticated MITM protection (LE Security
+				Level 3, Passkey) (O)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-sdp.txt b/bluez/repo/android/pics-sdp.txt
new file mode 100644
index 0000000..508b80d
--- /dev/null
+++ b/bluez/repo/android/pics-sdp.txt
@@ -0,0 +1,140 @@
+SDP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+		Support Different Size Capabilities on UUID
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_1_1	True		Support for 128 bit UUID (M)
+TSPC_SDP_1_2	True		Support for 32 bit UUID	(M)
+TSPC_SDP_1_3	True		Support for 16 bit UUID (M)
+-------------------------------------------------------------------------------
+
+
+		Roles
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_1b_1	True (*)	Support for server role (C.1)
+TSPC_SDP_1b_2	True (*)	Support for client role (C.1)
+-------------------------------------------------------------------------------
+C.1 Mandatory to support at least one of the roles
+-------------------------------------------------------------------------------
+
+
+		Valid Service Search Request
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_2_1	True (*)	Support for respond on search of single
+				Service, using ServiceSearchRequest (C.2)
+TSPC_SDP_2_2	True (*)	Support for respond on search of Service,
+				using continuation state (O)
+TSPC_SDP_2_3	True (*)	Search for services using the continuation
+				state (C.1)
+-------------------------------------------------------------------------------
+C.1 Mandatory to support if the client role is supported TSPC_SDP_1b_2
+C.2 Mandatory to support if the server role is supported TSPC_SDP_1b_1
+-------------------------------------------------------------------------------
+
+
+		Invalid Service Search Request
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_3_1	True		Support for error response on Service search
+				request (M)
+-------------------------------------------------------------------------------
+
+
+		Valid Service Attribute Request
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_4_1	True		Support for respond on search of
+				Attribute(s) (M)
+TSPC_SDP_4_2	True (*)	Support for respond on search of
+				Attribute, using continuation state (O)
+TSPC_SDP_4_3	True (*)	Support for respond on search on
+				attribute AdditionalProtocolDescriptorList (O)
+-------------------------------------------------------------------------------
+
+
+		Invalid Service Attribute Request
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_5_1	True		Support for error response on Attribute
+				search request (M)
+-------------------------------------------------------------------------------
+
+
+		Valid Service Search Attribute Request
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_6_1	True		Support for respond on search for Service(s)
+				and Attribute(s) (M)
+TSPC_SDP_6_2	True (*)	Support for respond on search of Attribute,
+				using continuation state (O)
+TSPC_SDP_6_3	True (*)	Support for respond on search on attribute
+				AdditionalProtocolDescriptorList on existing
+				service (O)
+-------------------------------------------------------------------------------
+
+
+		Invalid Service Search Attribute Request
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_7_1	True		Support for error response on Service and
+				Attribute request (M)
+-------------------------------------------------------------------------------
+
+
+		Service Browsing
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_8_1	True (*)	Support for browsing, using
+				SDP_ServiceSearchRequest and
+				SDP_ServiceAttributeRequest (O)
+TSPC_SDP_8_2	True (*)	Support for browsing, using
+				SDP_ServiceSearchAttributeRequest (O)
+-------------------------------------------------------------------------------
+
+
+		Attributes Present in IUT
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SDP_9_1	True (*)	ServiceID (O)
+TSPC_SDP_9_2	True (*)	ProtocolDescriptorList (O)
+TSPC_SDP_9_3	True (*)	ServiceRecordState (O)
+TSPC_SDP_9_4	True (*)	ServiceInfoTimeToLive (O)
+TSPC_SDP_9_5	True (*)	BrowseGroupList (O)
+TSPC_SDP_9_6	True (*)	LanguageBaseAttributeIdList (O)
+TSPC_SDP_9_7	True (*)	ServiceAvailability (O)
+TSPC_SDP_9_8	True (*)	IconURL (O)
+TSPC_SDP_9_9	True (*)	ServiceName (O)
+TSPC_SDP_9_10	True (*)	ServiceDescription (O)
+TSPC_SDP_9_11	True (*)	ProviderName (O)
+TSPC_SDP_9_12	True (*)	VersionNumberList (O)
+TSPC_SDP_9_13	True (*)	ServiceDataBaseState (O)
+TSPC_SDP_9_14	True (*)	BluetoothProfileDescriptorList (O)
+TSPC_SDP_9_15	True (*)	DocumentationURL (O)
+TSPC_SDP_9_16	True (*)	ClientExecutableURL (O)
+TSPC_SDP_9_17	True (*)	AdditionalProtocolDescriptorList (C.1)
+TSPC_SDP_9_18	True		ServiceRecordHandle (M)
+TSPC_SDP_9_19	True		ServiceClassIDList (M)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_SDP_9_2 is supported, otherwise excluded
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-sm.txt b/bluez/repo/android/pics-sm.txt
new file mode 100644
index 0000000..c31fe76
--- /dev/null
+++ b/bluez/repo/android/pics-sm.txt
@@ -0,0 +1,96 @@
+SM PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+^ - field not available on PTS
+
+M - mandatory
+O - optional
+
+		Connection Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_1_1	True		Master Role (Initiator) (C.1)
+TSPC_SM_1_2	True		Slave Role (Responder) (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if TSPC_SM_1_2 is NOT supported, otherwise Optional
+C.2: Optional IF ((4.0 OR 4.0+HS) AND TSPC_GAP_5_3) OR ((4.1 OR 4.1+HS OR 4.2
+	OR 4.2+HS) AND (TSPC_GAP_5_3 OR TSPC_GAP_38_3)))
+-------------------------------------------------------------------------------
+
+
+		Security Properties
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_2_1	True		Authenticated MITM protection (O)
+TSPC_SM_2_2	True		Unauthenticated no MITM protection (C.1)
+TSPC_SM_2_3	True		No security requirements (M)
+TSPC_SM_2_4	False (*)	OOB supported (O)
+TSPC_SM_2_5	(^)		LE Secure Connections (C.2)
+-------------------------------------------------------------------------------
+C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional
+C.2: Optional IF Core 4.2 OR Core 4.2+HS are supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Encryption Key Size
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_3_1	True		Encryption Key Size Negotiation (M)
+-------------------------------------------------------------------------------
+
+
+		Pairing Method
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_4_1	True		Just Works (O)
+TSPC_SM_4_2	True		Passkey Entry (C.1)
+TSPC_SM_4_3	False (*)	Out of Band (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined methods IF TSPC_SM_2_1 is
+	supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+		Security Initiation
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_5_1	True		Encryption Setup using STK (C.3)
+TSPC_SM_5_2	True		Encryption Setup using LTK (O)
+TSPC_SM_5_3	True		Slave Initiated Security (C.1)
+TSPC_SM_5_4	True		Slave Initiated Security – Master response(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_SM_1_2 is supported, otherwise Excluded
+C.2: Mandatory if TSPC_SM_1_1 is supported, otherwise Excluded
+C.3: Mandatory IF TSPC_SM_2_1 OR TSPC_SM_2_2 OR TSPC_SM_2_4 is supported,
+	otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+		Signing Algorithm
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_6_1	True		Signing Algorithm - Generation (O)
+TSPC_SM_6_2	True		Signing Algorithm - Resolving (O)
+-------------------------------------------------------------------------------
+
+
+		Key Distribution
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SM_7_1	True		Encryption Key (C.1)
+TSPC_SM_7_2	True		Identity Key (C.2)
+TSPC_SM_7_3	True		Signing Key (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_24_2 OR TSPC_GAP_42_6 is supported, ELSE Optional
+C.2: Mandatory if TSPC_GAP_26_3 is supported, ELSE Optional
+C.3: Mandatory if TSPC_GAP_25_6 OR TSPC_GAP_35_6 is supported, ELSE Optional
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pics-spp.txt b/bluez/repo/android/pics-spp.txt
new file mode 100644
index 0000000..d6bf97a
--- /dev/null
+++ b/bluez/repo/android/pics-spp.txt
@@ -0,0 +1,99 @@
+SPP PICS for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+		Profile Version
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_0_1	False		SPP v1.1 (C.1)
+TSPC_SPP_0_2	True (*)	SPP v1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Profile version.
+-------------------------------------------------------------------------------
+
+
+		Device Role
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_1_1	True (*)	Device A (DevA) (C.1)
+TSPC_SPP_1_2	True (*)	Device B (DevB) (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Support of SPP Service
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_2_1	True (*)	Support of Serial Profile Service (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory for devices that support Serial Profile for serial cable
+     emulation as a Bluetooth service. Irrelevant of devices that only
+     support Serial Profile for usage by another application profile
+     e.g. Fax Profile, Dun Profile, Hands free Profile, etc.
+-------------------------------------------------------------------------------
+
+
+		Application Procedures
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_3_1	True (*)	Establish link and set up virtual serial
+				connection (C.1)
+TSPC_SPP_3_2	True (*)	Accept link and virtual serial connection
+				establishment (C.2)
+TSPC_SPP_3_3	True (*)	Register Service record for application in
+				local SDP database (C.2)
+TSPC_SPP_3_4	True (*)	No release in Sniff mode. Sniff mode enabled
+				in the Link Manager (O)
+TSPC_SPP_3_5	True (*)	No release in Hold mode. Hold mode enabled
+				in the Link Manager (O)
+TSPC_SPP_3_6	True (*)	No release in Park mode. Park mode enabled
+				in the Link Manager (O)
+TSPC_SPP_3_7	True (*)	No release after Master/Slave switch. M/S
+				switch enabled in the Link manager (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory for Device A, Irrelevant for Device B.
+C.2: Mandatory for Device B, Irrelevant for Device A.
+-------------------------------------------------------------------------------
+
+
+		Service Port Profile Record Content (SerialPort UUID)
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_4_1	True (*)	SerialPort service class (UUID16: 0x1101) (C.1)
+TSPC_SPP_4_2	True (*)	Protocol0, L2CAP (C.1)
+TSPC_SPP_4_3	True (*)	Protocol1, RFCOMM (C.1)
+TSPC_SPP_4_4	True (*)	Server Channel number (C.1)
+TSPC_SPP_4_5	True (*)	Displayable text name (C.2)
+TSPC_SPP_4_6	True (*)	BluetoothProfileDescriptorList (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory for role B, if capability 'Support of Serial Profile Service'
+	(TSPC_SPP_2_1) supported. Irrelevant for role A.
+C.2: Mandatory to support if TSPC_SPP_2_1 AND TSPC_SPP_0_1 are supported,
+	otherwise Optional.
+C.3: Mandatory to support if TSPC_SPP_2_1 AND TSPC_SPP_0_2 are supported,
+	otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+		Encryption
+-------------------------------------------------------------------------------
+Item		Selected	Description
+-------------------------------------------------------------------------------
+TSPC_SPP_5_1	True (*)	Initiate encryption (O)
+TSPC_SPP_5_2	True		Accept encryption request (M)
+TSPC_SPP_5_3	True		Point to point encryption (M)
+TSPC_SPP_5_4	True		Stop encryption (M)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-a2dp.txt b/bluez/repo/android/pixit-a2dp.txt
new file mode 100644
index 0000000..c7f9762
--- /dev/null
+++ b/bluez/repo/android/pixit-a2dp.txt
@@ -0,0 +1,30 @@
+A2DP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name			Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled		TRUE (*)
+TSPX_bd_addr_iut		112233445566 (*&)
+TSPX_SRC_class_of_device	080418
+TSPX_SNK_class_of_device	04041C
+TSPX_pin_code			0000
+TSPX_delete_link_key		FALSE
+TSPX_time_guard			300000
+TSPX_use_implicit_send		TRUE
+TSPX_media_directory		C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+					bin\audio (#)
+TSPX_no_avrcp			FALSE (*)
+TSPX_auth_password		0000
+TSPX_auth_user_id		PTS
+TSPX_rfcomm_channel		8
+TSPX_l2cap_psm			1011
+TSPX_no_confirmations		FALSE
+TSPX_cover_art_uuid		3EEE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-avctp.txt b/bluez/repo/android/pixit-avctp.txt
new file mode 100644
index 0000000..ef85510
--- /dev/null
+++ b/bluez/repo/android/pixit-avctp.txt
@@ -0,0 +1,39 @@
+AVCTP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name			Value
+-------------------------------------------------------------------------------
+TSPX_avctp_psm			0017
+TSPX_avctp_profile_id		110E
+TSPX_connect_avdtp		TRUE
+TSPX_avctp_tester_command_data
+TSPX_avctp_tester_response_data
+TSPX_avctp_iut_command_data
+TSPX_avctp_iut_response_data
+TSPX_bd_addr_iut		11223344556677 (*&)
+TSPX_pin_code			0000
+TSPX_delete_link_key		FALSE
+TSPX_security_enabled		FALSE
+TSPX_class_of_device		20050C
+TSPX_player_feature_bitmask	0000000000000007FFF00070000000000
+TSPX_avrcp_version
+TSPX_establish_avdtp_stream	TRUE
+TSPX_tester_av_role		SNK (*)
+TSPX_time_guard			300000
+TSPX_avrcp_only			FALSE
+TSPX_use_implicit_send		TRUE
+TSPX_media_directory		C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+					bin\audio (#)
+TSPX_no_confirmations		FALSE
+TSPX_auth_password		0000
+TSPX_auth_user_id		PTS
+TSPX_rfcomm_channel		8
+TSPX_l2cap_psm			1011
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-avdtp.txt b/bluez/repo/android/pixit-avdtp.txt
new file mode 100644
index 0000000..f73b676
--- /dev/null
+++ b/bluez/repo/android/pixit-avdtp.txt
@@ -0,0 +1,31 @@
+AVDTP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name			Value
+-------------------------------------------------------------------------------
+TSPX_SNK_class_of_device	04041C
+TSPX_SRC_class_of_device	080418
+TSPX_security_control_data
+TSPX_content_protection_data
+TSPX_content_protection_type
+TSPX_no_avrcp			TRUE
+TSPX_media_directory
+TSPX_bd_addr_iut		11223344556677 (*&)
+TSPX_delete_link_key		FALSE
+TSPX_pin_code			1234
+TSPX_security_enabled		TRUE (*)
+TSPX_time_guard			300000
+TSPX_use_implicit_send		TRUE
+TSPX_auth_password		0000
+TSPX_auth_user_id		PTS
+TSPX_l2cap_psm			1003
+TSPX_rfcomm_channel		8
+TSPX_no_confirmations		FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-avrcp.txt b/bluez/repo/android/pixit-avrcp.txt
new file mode 100644
index 0000000..9d5b87e
--- /dev/null
+++ b/bluez/repo/android/pixit-avrcp.txt
@@ -0,0 +1,36 @@
+AVRCP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name			Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled		FALSE
+TSPX_bd_addr_iut		112233445566 (*&)
+TSPX_class_of_device		20050C
+TSPX_player_feature_bitmask	0000000000000007FFF00070000000000
+TSPX_pin_code			0000
+TSPX_delete_link_key		FALSE
+TSPX_time_guard			300000
+TSPX_avrcp_only			FALSE
+TSPX_search_string		3
+TSPX_max_avc_fragments		10
+TSPX_establish_avdtp_stream	TRUE
+TSPX_use_implicit_send		TRUE
+TSPX_avrcp_version
+TSPX_tester_av_role
+TSPX_media_directory		C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+					bin\audio (#)
+TSPX_auth_password		0000
+TSPX_auth_user_id		PTS
+TSPX_rfcomm_channel		8
+TSPX_l2cap_psm			1011
+TSPX_no_confirmations		FALSE
+TSPX_no_cover_art_folder
+TSPX_cover_art_folder
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-bnep.txt b/bluez/repo/android/pixit-bnep.txt
new file mode 100644
index 0000000..1366535
--- /dev/null
+++ b/bluez/repo/android/pixit-bnep.txt
@@ -0,0 +1,30 @@
+BNEP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name			Value
+-------------------------------------------------------------------------------
+TSPX_class_of_device		04041C
+TSPX_security_control_data
+TSPX_content_protection_data
+TSPX_bd_addr_iut		112233445566 (*&)
+TSPX_delete_link_key		FALSE
+TSPX_pin_code			1234
+TSPX_security_enabled		FALSE
+TSPX_time_guard			300000
+TSPX_use_implicit_send		TRUE
+TSPX_auth_password		0000
+TSPX_auth_user_id		PTS
+TSPX_l2cap_psm			000F
+TSPX_rfcomm_channel		8
+TSPX_no_confirmations		FALSE
+TSPX_UUID_dest_address		1116
+TSPX_UUID_source_address	1115
+TSPX_MAC_dest_address		000000000000 (*&)
+TSPX_MAC_source_address		000000000000 (*&)
diff --git a/bluez/repo/android/pixit-did.txt b/bluez/repo/android/pixit-did.txt
new file mode 100644
index 0000000..0a0f1cc
--- /dev/null
+++ b/bluez/repo/android/pixit-did.txt
@@ -0,0 +1,24 @@
+DID PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled					False
+TSPX_ClientExecutableURL				False (*)
+TSPX_ServiceDescription					False (*)
+TSPX_DocumentationURL					False (*)
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_class_of_device_pts				200404
+TSPX_device_search_time					30
+TSPX_delete_link_key					False
+TSPX_pin_code						0000
+TSPX_time_guard						200000
+TSPX_use_implicit_send					True
+TSPX_secure_simple_pairing_pass_key_confirmation	False
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-dis.txt b/bluez/repo/android/pixit-dis.txt
new file mode 100644
index 0000000..e53532e
--- /dev/null
+++ b/bluez/repo/android/pixit-dis.txt
@@ -0,0 +1,26 @@
+DIS PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_time_guard						180000
+TSPX_use_implicit_send					TRUE
+TSPX_tester_database_file				C:/Program Files/...
+TSPX_mtu_size						23
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin					FALSE
+TSPX_delete_ltk						FALSE
+TSPX_security_enabled					TRUE (*)
+TSPX_iut_setup_att_over_br_edr				FALSE
+TSPX_tester_appearance					0000
+TSPX_iut_use_resolvable_random_address			FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-gap.txt b/bluez/repo/android/pixit-gap.txt
new file mode 100644
index 0000000..92a8135
--- /dev/null
+++ b/bluez/repo/android/pixit-gap.txt
@@ -0,0 +1,60 @@
+GAP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to IUT name
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_bd_addr_PTS					C000DEADBEEF
+TSPX_broadcaster_class_of_device			100104
+TSPX_observer_class_of_device				100104
+TSPX_peripheral_class_of_device				100104
+TSPX_central_class_of_device				100104
+TSPX_security_enabled					True
+TSPX_delete_link_key					True
+TSPX_pin_code						0000
+TSPX_time_guard						300000
+TSPX_use_implicit_send					True
+TSPX_use_dynamic_pin					False
+TSPX_secure_simple_pairing_pass_key_confirmation	False
+TSPX_using_public_device_address			True
+TSPX_using_random_device_address			False
+TSPX_lim_adv_timeout					30720
+TSPX_gen_disc_adv_min					30720
+TSPX_lim_disc_scan_min					10240
+TSPX_gen_disc_scan_min					10240
+TSPX_database_file					Database-GAP.sig
+TSPX_iut_rx_mtu						23
+TSPX_iut_private_address_interval			30000 (*)
+TSPX_iut_privacy_enabled				False
+TSPX_psm						1001
+TSPX_iut_valid_connection_interval_min			00C8
+TSPX_iut_valid_conneciton_interval_max			0960
+TSPX_iut_valid_connection_latency			0006
+TSPX_iut_valid_timeout_multiplier			0962
+TSPX_iut_connection_parameter_timeout			30000
+TSPX_iut_invalid_connection_interval_min		0000
+TSPX_iut_invalid_conneciton_interval_max		0000
+TSPX_iut_invalid_connection_latency			0000
+TSPX_iut_invalid_timeout_multiplier			0000
+TSPX_LE_scan_interval					0010
+TSPX_LE_scan_window					0010
+TSPX_con_interval_min					0032
+TSPX_con_interval_max					0046
+TSPX_con_latency					0000
+TSPX_supervision_timeout				07D0
+TSPX_minimum_ce_length					0000
+TSPX_maximum_ce_length					0000
+TSPX_pairing_before_service_request			False
+TSPX_iut_mandates_mitm					False
+TSPX_encryption_before_service_request			False
+TSPX_tester_appearance					0000
+TSPX_iut_advertising_data_in_broadcasting_mode		[set to default value]
+TSPX_iut_device_name_in_adv_packet_for_random_address	PTS-66DE (#)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-gatt.txt b/bluez/repo/android/pixit-gatt.txt
new file mode 100644
index 0000000..c6cfaa7
--- /dev/null
+++ b/bluez/repo/android/pixit-gatt.txt
@@ -0,0 +1,32 @@
+GATT PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_security_enabled					FALSE
+TSPX_delete_link_key					TRUE
+TSPX_time_guard						180000
+TSPX_selected_handle					0012
+TSPX_use_implicit_send					TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_iut_use_dynamic_bd_addr				FALSE
+TSPX_iut_setup_att_over_br_edr				FALSE
+TSPX_tester_database_file				[Path to GATT Test
+								Database]
+TSPX_iut_is_client_periphral				FALSE
+TSPX_iut_is_server_central				FALSE
+TSPX_mtu_size						23
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin					FALSE
+TSPX_delete_ltk						FALSE
+TSPX_characteristic_readable				FALSE
+TSPX_tester_appearance					0000
+TSPX_iut_use_resolvable_random_access			FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-gavdp.txt b/bluez/repo/android/pixit-gavdp.txt
new file mode 100644
index 0000000..0ee9eca
--- /dev/null
+++ b/bluez/repo/android/pixit-gavdp.txt
@@ -0,0 +1,32 @@
+GAVDP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+^ - should be set accordingly
+# - should be set to PTS's bin/audio folder
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_SNK_class_of_device	04041C
+TSPX_SRC_class_of_device	080418
+TSPX_bd_addr_iut		112233445566 (*&)
+TSPX_delete_link_key		FALSE
+TSPX_media_directory		C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+					bin\audio (#)
+TSPX_no_avrcp			FALSE
+TSPX_pin_code			0000
+TSPX_security_enabled		FALSE
+TSPX_tester_av_role
+TSPX_time_guard			300000
+TSPX_use_implicit_send		TRUE
+TSPX_auth_password		0000
+TSPX_auth_user_id		PTS
+TSPX_rfcomm_channel		8
+TSPX_l2cap_psm			1011
+TSPX_no_confirmations		FALSE
+TSPX_cover_art_uuid
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-hdp.txt b/bluez/repo/android/pixit-hdp.txt
new file mode 100644
index 0000000..ca9b8a8
--- /dev/null
+++ b/bluez/repo/android/pixit-hdp.txt
@@ -0,0 +1,32 @@
+HDP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled					TRUE
+TSPX_delete_link_key					FALSE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_sink_device_class_of_device			000900
+TSPX_source_device_class_of_device			000900
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin					FALSE
+TSPX_use_implicit_send					TRUE
+TSPX_MCAP_l2cap_psm_control				1001
+TSPX_MCAP_l2cap_psm_data				1003
+TSPX_MCAP_sync_lead_time				0BB8
+TSPX_MCAP_timestamp_native_resolution			2233
+TSPX_MCAP_timestamp_native_accuracy			1400
+TSPX_MCAP_timestamp_required_accuracy			6400
+TSPX_DC_max						1
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_time_guard						60000000
+TSPX_ieee_device_specialization				10417
+TSPX_ieee_standard_configuration			TRUE
+TSPX_MCAP_bluetooth_clock_access_resolution		55
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-hfp.txt b/bluez/repo/android/pixit-hfp.txt
new file mode 100644
index 0000000..896744e
--- /dev/null
+++ b/bluez/repo/android/pixit-hfp.txt
@@ -0,0 +1,41 @@
+HFP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to the respective phone numbers
+^ - should be set according to the reported phone number's type
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled					TRUE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_hf_class_of_device					200408
+TSPX_ag_class_of_device					400204
+TSPX_packet_type_sco					00A0
+TSPX_phone_number					1234567 (#)
+TSPX_second_phone_number				7654321 (#)
+TSPX_phone_number_type					145 (*^)
+TSPX_second_phone_number_type				145 (*^)
+TSPX_phone_number_memory				1
+TSPX_phone_number_memory_invalid_index			9999
+TSPX_scan_all_memory_dial_locations			FALSE
+TSPX_pin_code						0000
+TSPX_time_guard						300000
+TSPX_use_implicit_send					TRUE
+TSPX_verbose_implicit_send				FALSE
+TSPX_delete_link_key					FALSE
+TSPX_server_channel_tester				01
+TSPX_server_channel_iut					00
+TSPX_verify_CLIP_information				TRUE
+TSPX_no_fail_verdict					FALSE
+TSPX_network_supports_correct_call_and_callstatus	TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_AG_match_tester_BRSF_codec_negotiation_bit		FALSE
+TSPX_HFP_Unsupported_HF_Indicator_1			99
+TSPX_HFP_Supported_HF_Indiccator_1			1
+TSPX_Automation						FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-hid.txt b/bluez/repo/android/pixit-hid.txt
new file mode 100644
index 0000000..511957b
--- /dev/null
+++ b/bluez/repo/android/pixit-hid.txt
@@ -0,0 +1,31 @@
+HID PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled					True
+TSPX_delete_link_key					True
+TSPX_query_iut_sdp					True
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_pointing_device_class_of_device			002580
+TSPX_keyboard_device_class_of_device			002540
+TSPX_host_class_of_device				100108
+TSPX_pts_device_role					MOUSE
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin_code				False
+TSPX_time_guard						6000000
+TSPX_hid_data_interval					1000
+TSPX_use_implicit_send					True
+TSPX_verbose_implicit_send				False
+TSPX_no_fail_verdicts					False
+TSPX_time_reconnect					30000
+TSPX_secure_simple_pairing_pass_key_confirmation	False
+TSPX_hid_report_id					1
+TSPX_hid_report_data					ff00 (*)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-hogp.txt b/bluez/repo/android/pixit-hogp.txt
new file mode 100644
index 0000000..6a38d19
--- /dev/null
+++ b/bluez/repo/android/pixit-hogp.txt
@@ -0,0 +1,29 @@
+HOGP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_time_guard						180000
+TSPX_use_implicit_send					TRUE
+TSPX_tester_database_file				[Path to HOGP Test
+								Database]
+TSPX_mtu_size						23
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin					FALSE
+TSPX_delete_ltk						FALSE
+TSPX_security_enabled					TRUE
+TSPX_input_report_data					CDA6F8B3AA
+TSPX_output_report_data					001234567890EF
+TSPX_feature_report_data				872D3F45EA
+TSPX_tester_appearance					03C0
+TSPX_iut_use_resolvable_random_address			FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-hsp.txt b/bluez/repo/android/pixit-hsp.txt
new file mode 100644
index 0000000..9ba74d2
--- /dev/null
+++ b/bluez/repo/android/pixit-hsp.txt
@@ -0,0 +1,30 @@
+HSP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled					TRUE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_hs_class_of_device					200404
+TSPX_ag_class_of_device					400204
+TSPX_packet_type_sco					00A0
+TSPX_pin_code						0000
+TSPX_time_guard						20000000
+TSPX_use_implicit_send					TRUE
+TSPX_verbose_implicit_send				FALSE
+TSPX_delete_link_key					FALSE
+TSPX_server_channel_tester				01
+TSPX_server_channel_iut					00
+TSPX_no_fail_verdict					FALSE
+TSPX_remote_audio_volume_control			TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_inband_ring_only					FALSE
+TSPX_no_ring_or_inband_ring_tone			FALSE
+TSPX_iut_establish_audio_before_RING			FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-iopt.txt b/bluez/repo/android/pixit-iopt.txt
new file mode 100644
index 0000000..181a91d
--- /dev/null
+++ b/bluez/repo/android/pixit-iopt.txt
@@ -0,0 +1,23 @@
+IOPT PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled					FALSE
+TSPX_delete_link_key					FALSE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_class_of_device_pts				200404
+TSPX_class_of_device_test_pts_initiator			TRUE
+TSPX_limited_inquiry_used				FALSE
+TSPX_pin_code						0000
+TSPX_time_guard						200000
+TSPX_device_search_time					20
+TSPX_use_implicit_send					TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-l2cap.txt b/bluez/repo/android/pixit-l2cap.txt
new file mode 100644
index 0000000..23fad19
--- /dev/null
+++ b/bluez/repo/android/pixit-l2cap.txt
@@ -0,0 +1,59 @@
+L2CAP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_client_class_of_device                            100104
+TSPX_server_class_of_device                            100104
+TSPX_security_enabled                                  TRUE (*)
+TSPX_delete_link_key                                   FALSE
+TSPX_pin_code                                          0000
+TSPX_flushto                                           FFFF
+TSPX_inmtu                                             02A0
+TSPX_no_fail_verditcs                                  FALSE
+TSPX_oumtu                                             02A0
+TSPX_tester_mps                                        0017
+TSPX_tester_mtu                                        02A0
+TSPX_iut_role_initiator                                TRUE (*)
+TSPX_le_psm                                            0080 (*)
+TSPX_psm                                               1011 (*)
+TSPX_psm_unsupported                                   00F1
+TSPX_psm_authentication_required                       00F2
+TSPX_psm_authorization_required                        00F3
+TSPX_psm_encryption_key_size_required                  00F4
+TSPX_time_guard                                        180000
+TSPX_timer_ertx                                        120000
+TSPX_timer_ertx_max                                    300000
+TSPX_timer_ertx_min                                    60000
+TSPX_timer_rtx                                         10000
+TSPX_timer_rtx_max                                     60000
+TSPX_timer_rtx_min                                     1000
+TSPX_rfc_mode_tx_window_size                           08
+TSPX_rfc_mode_max_transmit                             03
+TSPX_rfc_mode_retransmission_timeout                   07D0
+TSPX_rfc_mode_monitor_timeout                          2EE0
+TSPX_rfc_mode_maximum_pdu_size                         02A0
+TSPX_extended_window_size                              0012
+TSPX_use_implicit_send                                 TRUE
+TSPX_use_dynamic_pin                                   FALSE
+TSPX_iut_SDU_size_in_bytes                             144
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_iut_address_type_random                           FALSE
+TSPX_tester_adv_interval_min                           0030
+TSPX_tester_adv_interval_max                           0050
+TSPX_tester_le_scan_interval                           0C80
+TSPX_tester_le_scan_window                             0C80
+TSPX_tester_conn_interval_min                          0028
+TSPX_tester_conn_interval_max                          0050
+TSPX_tester_conn_latency                               0000
+TSPX_tester_supervision_timeout                        0C80
+TSPX_tester_min_CE_length                              0080
+TSPX_tester_max_CE_length                              0C80
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-map.txt b/bluez/repo/android/pixit-map.txt
new file mode 100644
index 0000000..90272cb
--- /dev/null
+++ b/bluez/repo/android/pixit-map.txt
@@ -0,0 +1,44 @@
+MAP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to tester's phone number
+$ - should be set to IUT e-mail address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_client_class_of_device				100204
+TSPX_delete_link_key					FALSE
+TSPX_get_object_name					put.gif
+TSPX_initial_path
+TSPX_l2cap_psm						1001
+TSPX_no_confirmations					FALSE
+TSPX_pin_code						0000
+TSPX_rfcomm_channel					8
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_security_enabled					TRUE
+TSPX_server_class_of_device				100204
+TSPX_time_guard						300000
+TSPX_use_implicit_send					TRUE
+TSPX_Message_Access_rfcomm_channel			1
+TSPX_Message_Notification_rfcomm_channel		2
+TSPX_SPP_rfcomm_channel					03
+TSPX_filter_period_begin				20100101T000000
+TSPX_filter_period_end					20111231T125959
+TSPX_filter_recipient					PTS
+TSPX_filter_originator					PTS
+TSPX_default_message_upload_folder_in_msg		draft
+TSPX_default_test_folder_in_msg				inbox
+TSPX_message_notification_l2cap_psm			1003
+TSPX_message_notification_rfcomm_channel		9
+TSPX_upload_msg_phonenumber				123456789 (#)
+TSPX_upload_msg_emailaddress				IUT-email ($)
+TSPX_Automation						FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-mcap.txt b/bluez/repo/android/pixit-mcap.txt
new file mode 100644
index 0000000..a8bbe52
--- /dev/null
+++ b/bluez/repo/android/pixit-mcap.txt
@@ -0,0 +1,37 @@
+MCAP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_delete_link_key					FALSE
+TSPX_MCAP_DC_max					1 (*)
+TSPX_MCAP_l2cap_psm_control				1003
+TSPX_MCAP_l2cap_psm_control_B
+TSPX_MCAP_l2cap_psm_data				1005
+TSPX_MCAP_l2cap_psm_data_B
+TSPX_MCAP_bluetooth_clock_access_resolution		55
+TSPX_MCAP_create_mdl_configuration
+TSPX_MCAP_data_channel_setup_mode
+TSPX_MCAP_iut_initiate_connection			FALSE
+TSPX_MCAP_mdep_id					12
+TSPX_MCAP_profile_name
+TSPX_MCAP_sync_lead_time				0BB8
+TSPX_tester_role
+TSPX_MCAP_timestamp_native_accuracy			1400
+TSPX_MCAP_timestamp_native_resolution			2233
+TSPX_MCAP_timestamp_required_accuracy			6400
+TSPX_host_class_of_device				100108
+TSPX_pin_code						0000
+TSPX_security_enabled					TRUE (*)
+TSPX_time_guard						6000000
+TSPX_use_dynamic_pin					FALSE
+TSPX_use_implicit_send					TRUE
+TSPX_verbose_implicit_send				TRUE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-mps.txt b/bluez/repo/android/pixit-mps.txt
new file mode 100644
index 0000000..116a8c0
--- /dev/null
+++ b/bluez/repo/android/pixit-mps.txt
@@ -0,0 +1,47 @@
+MPS PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+^ - should be set accordingly
+# - should be set according to the reported phone number's type
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_avrcp_revision					1.5 (^)
+TSPX_class_of_device					20050C
+TSPX_establish_avdtp_stream				TRUE
+TSPX_iut_establishes_initial_condition			FALSE
+TSPX_tester_device					A (*)
+TSPX_media_directory
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_security_enabled					FALSE
+TSPX_time_guard						300000
+TSPX_use_implicit_send					TRUE
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_l2cap_psm						1003
+TSPX_rfcomm_channel					8
+TSPX_no_confirmations					FALSE
+TSPX_AG_match_tester_BRSF_codec_negotiation_bit		FALSE
+TSPX_network_supports_correct_call_and_callstatus	TRUE
+TSPX_no_fail_verdicts					FALSE
+TSPX_packet_type_sco					00A0
+TSPX_phone_number					1234567 (^)
+TSPX_phone_number_memory				1
+TSPX_phone_number_memory_invalid_index			9999
+TSPX_phone_number_type					145 (*#)
+TSPX_scan_all_memory_dial_locations			FALSE
+TSPX_second_phone_number				1234567 (^)
+TSPX_second_phone_type					129
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_server_channel_iut					00
+TSPX_server_channel_tester				01
+TSPX_verbose_implicit_send				FALSE
+TSPX_verify_CLIP_information				TRUE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-opp.txt b/bluez/repo/android/pixit-opp.txt
new file mode 100644
index 0000000..f6651a1
--- /dev/null
+++ b/bluez/repo/android/pixit-opp.txt
@@ -0,0 +1,27 @@
+OPP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_supported_extension				jpg (*)
+TSPX_unsupported_extension				pts
+TSPX_client_class_of_device				100104
+TSPX_server_class_of_device				100104
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_l2cap_psm						1003
+TSPX_rfcomm_channel					8
+TSPX_no_confirmations					FALSE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_security_enabled					FALSE
+TSPX_time_guard						300000
+TSPX_use_implicit_send					TRUE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-pan.txt b/bluez/repo/android/pixit-pan.txt
new file mode 100644
index 0000000..713646e
--- /dev/null
+++ b/bluez/repo/android/pixit-pan.txt
@@ -0,0 +1,39 @@
+PAN PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT or PTS Bluetooth/MAC address respectively
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_GN_class_of_device					020104
+TSPX_NAP_class_of_device				020300
+TSPX_PANU_class_of_device				020104
+TSPX_time_guard						300000
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_security_enabled					False
+TSPX_pin_code						0000
+TSPX_delete_link_key					False
+TSPX_use_implicit_send					True
+TSPX_iut_ip_address					192.168.1.100 (*&)
+TSPX_iut_port_number					4242
+TSPX_PTS_ip_address					192.168.168.100
+TSPX_PTS_port_number					4242
+TSPX_bd_addr_PTS					112233445566 (*&)
+TSPX_checksum						E851
+TSPX_secure_simple_pairing_pass_key_confirmation	False
+TSPX_iut_friendly_bt_name				gprs-pc
+TSPX_PTS_role_when_iut_is_PANU				default
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_l2cap_psm						000F
+TSPX_rfcomm_channel					8
+TSPX_no_confirmations					FALSE
+TSPX_UUID_dest_address					0000
+TSPX_UUID_source_address				0000
+TSPX_MAC_dest_address					112233445566 (*&)
+TSPX_MAC_source_address					112233445566 (*&)
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-pbap.txt b/bluez/repo/android/pixit-pbap.txt
new file mode 100644
index 0000000..9bf6c06
--- /dev/null
+++ b/bluez/repo/android/pixit-pbap.txt
@@ -0,0 +1,37 @@
+PBAP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_security_enabled					True
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_pin_code						0000
+TSPX_time_guard						100000
+TSPX_use_implicit_send					True
+TSPX_client_class_of_device				100204
+TSPX_server_class_of_device				100204
+TSPX_PSE_vCardSelector					0000000000000001
+TSPX_delete_link_key					False
+TSPX_PBAP_profile_version				1
+TSPX_PBAP_supported_repositories			1
+TSPX_PBAP_rfcomm_channel				1
+TSPX_telecom_folder_path				telecom
+TSPX_secure_simple_pairing_pass_key_confirmation	False
+TSPX_check_downloaded_contents_after_disconnect		False
+TSPX_SPP_rfcomm_channel					03
+TSPX_l2cap_psm						1005
+TSPX_rfcomm_channel					2
+TSPX_obex_auth_password					0000
+TSPX_no_confirmations					False
+TSPX_PSE_vCardSelector					0000000000000001
+TSPX_Automation						False
+TSPX_search_criteria					PTS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-rfcomm.txt b/bluez/repo/android/pixit-rfcomm.txt
new file mode 100644
index 0000000..88402a5
--- /dev/null
+++ b/bluez/repo/android/pixit-rfcomm.txt
@@ -0,0 +1,28 @@
+RFCOMM PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name				Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut			11223344556677 (*&)
+TSPX_delete_link_key			FALSE
+TSPX_pin_code				1234
+TSPX_security_enabled			TRUE
+TSPX_time_guard				300000
+TSPX_use_implicit_send			TRUE
+TSPX_service_name_tester		COM5
+TSPX_class_of_device_tester		200408
+TSPX_server_channel_iut			01 (*)
+TSPX_verification_pattern_length	4
+TSPX_T1_Acknowledgement_Timer		20000
+TSPX_T1_Acknowledgement_Timer_Dlc	300000
+TSPX_T2_Response_Timer			20000
+TSPX_max_frame_size_iut			127
+TSPX_RPN_parameters_iut			0302001113
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-scpp.txt b/bluez/repo/android/pixit-scpp.txt
new file mode 100644
index 0000000..c8d9f37
--- /dev/null
+++ b/bluez/repo/android/pixit-scpp.txt
@@ -0,0 +1,25 @@
+ScPP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_time_guard						180000
+TSPX_use_implicit_send					TRUE
+TSPX_tester_database_file				C:/Program Files/...
+TSPX_mtu_size						23
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_use_dynamic_pin					FALSE
+TSPX_delete_ltk						FALSE
+TSPX_security_enabled					FALSE
+TSPX_tester_appearance					0000
+TSPX_iut_use_resolvable_random_address			FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-sdp.txt b/bluez/repo/android/pixit-sdp.txt
new file mode 100644
index 0000000..b430f84
--- /dev/null
+++ b/bluez/repo/android/pixit-sdp.txt
@@ -0,0 +1,45 @@
+SDP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+^ - should be set accordingly
+# - should be set according to the reported phone number's type
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name							Value
+-------------------------------------------------------------------------------
+TSPX_sdp_service_search_pattern					0100
+TSPX_sdp_service_search_pattern_no_results			EEEE
+TSPX_sdp_service_search_additional_protocol_descriptor_list
+TSPX_sdp_service_search_bluetooth_profile_descriptor_list
+TSPX_sdp_service_search_pattern_browse_group_list
+TSPX_sdp_service_search_pattern_client_exe_url
+TSPX_sdp_service_search_pattern_documentation_url
+TSPX_sdp_service_search_pattern_icon_url
+TSPX_sdp_service_search_pattern_language_base_attribute_id_list
+TSPX_sdp_service_search_pattern_protocol_descriptor_list
+TSPX_sdp_service_search_pattern_provider_name
+TSPX_sdp_service_search_pattern_service_availability
+TSPX_sdp_service_search_pattern_service_data_base_state		1000(*)
+TSPX_sdp_service_search_pattern_service_description
+TSPX_sdp_service_search_pattern_service_id
+TSPX_sdp_service_search_pattern_service_info_time_to_live
+TSPX_sdp_service_search_pattern_version_number_list		1000(*)
+TSPX_sdp_service_search_pattern_service_name
+TSPX_sdp_service_search_pattern_service_record_state
+TSPX_sdp_unsupported_attribute_id				EEEE
+TSPX_security_enabled						FALSE
+TSPX_delete_link_key						FALSE
+TSPX_bd_addr_iut						112233445566(*&)
+TSPX_class_of_device_pts					200404
+TSPX_class_of_device_test_pts_initiator				TRUE
+TSPX_limited_inquiry_used					FALSE
+TSPX_pin_code							0000
+TSPX_time_guard							200000
+TSPX_device_search_time						20
+TSPX_use_implicit_send						TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation		FALSE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-sm.txt b/bluez/repo/android/pixit-sm.txt
new file mode 100644
index 0000000..6facbb8
--- /dev/null
+++ b/bluez/repo/android/pixit-sm.txt
@@ -0,0 +1,72 @@
+SM PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_SMP_pin_code					111111
+TSPX_OOB_Data						000000000000FE12036E5A
+							889F4D
+TSPX_peer_addr_type					00
+TSPX_own_addr_type					00
+TSPX_conn_interval_min					0190
+TSPX_conn_interval_max					0190
+TSPX_con_latency					0000
+TSPX_client_class_of_device				100104
+TSPX_server_class_of_device				100104
+TSPX_security_enabled					TRUE
+TSPX_delete_link_key					TRUE
+TSPX_pin_code						1234
+TSPX_ATTR_HANDLE					0000
+TSPX_ATTR_VALUE						000000000000000
+TSPX_delay_variation_in					FFFFFFFF
+TSPX_delay_variation_out				FFFFFFFF
+TSPX_flushto						FFFF
+TSPX_inmtu						02A0
+TSPX_inquiry_length					17
+TSPX_latency_in						FFFFFFFF
+TSPX_latency_out					FFFFFFFF
+TSPX_linkto						3000
+TSPX_max_nbr_retransmission				10
+TSPX_no_fail_verdicts					FALSE
+TSPX_outmtu						02A0
+TSPX_tester_role_optional				L2CAP_ROLE_INITIATOR
+TSPX_page_scan_mode					00
+TSPX_page_scan_repetition_mode				00
+TSPX_peak_bandwidth_in					00000000
+TSPX_peak_bandwidth_out					00000000
+TSPX_psm						0011
+TSPX_service_type_in					01
+TSPX_service_type_out					01
+TSPX_support_retransmissions				TRUE
+TSPX_time_guard						180000
+TSPX_timer_ertx						120000
+TSPX_timer_ertx_max					300000
+TSPX_timer_ertx_min					60000
+TSPX_timer_rtx						10000
+TSPX_timer_rtx_max					60000
+TSPX_timer_rtx_min					1000
+TSPX_token_bucket_size_in				00000000
+TSPX_token_bucket_size_out				00000000
+TSPX_token_rate_in					00000000
+TSPX_token_rate_out					00000000
+TSPX_rfc_mode_mode					03
+TSPX_rfc_mode_tx_window_size				08
+TSPX_rfc_mode_max_transmit				03
+TSPX_rfc_mode_retransmission_timeout			07D0
+TSPX_rfc_mode_monitor_timeout				2EE0
+TSPX_rfc_mode_maximum_pdu_size				02A0
+TSPX_extended_window_size				0012
+TSPX_use_implicit_send					TRUE
+TSPX_use_dynamic_pin					FALSE
+TSPX_iut_SDU_size_in_bytes				144
+TSPX_secure_simple_pairing_pass_key_confirmation	FALSE
+TSPX_Min_Encryption_Key_Length				07
+TSPX_Bonding_Flags					00
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pixit-spp.txt b/bluez/repo/android/pixit-spp.txt
new file mode 100644
index 0000000..4bd54d0
--- /dev/null
+++ b/bluez/repo/android/pixit-spp.txt
@@ -0,0 +1,19 @@
+SPP PIXIT for the PTS tool.
+
+PTS version: 6.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_security_enabled					TRUE
+TSPX_pin_code						0000
+TSPX_time_guard						300000
+TSPX_delete_link_key					FALSE
+TSPX_stop_immediately_when_fail				TRUE
+TSPX_class_of_device_tester				200408
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-a2dp.txt b/bluez/repo/android/pts-a2dp.txt
new file mode 100644
index 0000000..7131a73
--- /dev/null
+++ b/bluez/repo/android/pts-a2dp.txt
@@ -0,0 +1,70 @@
+PTS test results for A2DP
+
+PTS version: 6.1
+Tested 21-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+		Source (SRC)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_SRC_CC_BV_09_I	PASS	Start streaming
+TC_SRC_CC_BV_10_I	PASS	Start streaming
+TC_SRC_REL_BV_01_I	PASS	When requested disconnect from IUT
+TC_SRC_REL_BV_02_I	PASS	Start streaming
+TC_SRC_SET_BV_01_I	PASS
+TC_SRC_SET_BV_02_I	PASS
+TC_SRC_SET_BV_03_I	PASS	Start streaming
+TC_SRC_SET_BV_04_I	PASS	Start streaming
+TC_SRC_SET_BV_05_I	PASS	Start streaming
+				IUT must be moved out of range
+				Initiate connection
+TC_SRC_SET_BV_06_I	PASS	PTS issue#13469
+				IUT must be moved out of range
+				To pass set TSPX_no_avrcp to TRUE
+TC_SRC_SUS_BV_01_I	PASS	Start streaming
+				Stop streaming
+				Start streaming
+TC_SRC_SUS_BV_02_I	PASS	Start streaming
+TC_SRC_SDP_BV_01_I	PASS
+TC_SRC_AS_BV_01_I	PASS	Requires checking if the output on the IUT is
+				correct
+TC_SRC_AS_BV_02_I	N/A
+TC_SRC_AS_BV_03_I	N/A
+TC_SRC_SYN_BV_02_I	N/A
+-------------------------------------------------------------------------------
+
+
+		Sink (SNK)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_SNK_CC_BV_01_I	N/A
+TC_SNK_CC_BV_02_I	N/A
+TC_SNK_CC_BV_03_I	N/A
+TC_SNK_CC_BV_04_I	N/A
+TC_SNK_CC_BV_05_I	N/A
+TC_SNK_CC_BV_06_I	N/A
+TC_SNK_CC_BV_07_I	N/A
+TC_SNK_CC_BV_08_I	N/A
+TC_SNK_REL_BV_01_I	N/A
+TC_SNK_REL_BV_02_I	N/A
+TC_SNK_SET_BV_01_I	N/A
+TC_SNK_SET_BV_02_I	N/A
+TC_SNK_SET_BV_03_I	N/A
+TC_SNK_SET_BV_04_I	N/A
+TC_SNK_SET_BV_05_I	N/A
+TC_SNK_SET_BV_06_I	N/A
+TC_SNK_SUS_BV_01_I	N/A
+TC_SNK_SUS_BV_02_I	N/A
+TC_SNK_SDP_BV_02_I	N/A
+TC_SNK_AS_BV_01_I	N/A
+TC_SNK_AS_BV_02_I	N/A
+TC_SNK_SYN_BV_01_I	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-avctp.txt b/bluez/repo/android/pts-avctp.txt
new file mode 100644
index 0000000..ec48322
--- /dev/null
+++ b/bluez/repo/android/pts-avctp.txt
@@ -0,0 +1,43 @@
+PTS test results for AVCTP
+
+PTS version: 6.1
+Tested: 21-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+		Controller (CT)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_CT_CCM_BV_01_C	N/A
+TC_CT_CCM_BV_02_C	N/A
+TC_CT_CCM_BV_03_C	N/A
+TC_CT_CCM_BV_04_C	N/A
+TC_CT_CCM_BI_01_C	N/A
+TC_CT_NFR_BV_01_C	N/A
+TC_CT_NFR_BV_04_C	PASS	haltest: rc set_volume 5
+				Note: IUT must be connectable and discoverable
+TC_CT_FRA_BV_01_C	N/A
+TC_CT_FRA_BV_04_C	N/A
+-------------------------------------------------------------------------------
+
+
+		Target (TG)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_TG_CCM_BV_01_C	PASS
+TC_TG_CCM_BV_02_C	PASS
+TC_TG_CCM_BV_03_C	PASS
+TC_TG_CCM_BV_04_C	PASS
+TC_TG_NFR_BV_02_C	PASS
+TC_TG_NFR_BV_03_C	PASS
+TC_TG_NFR_BI_01_C	PASS
+TC_TG_FRA_BV_02_C	N/A	Fragmentation not supported
+TC_TG_FRA_BV_03_C	N/A	Fragmentation not supported
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-avdtp.txt b/bluez/repo/android/pts-avdtp.txt
new file mode 100644
index 0000000..1a5699d
--- /dev/null
+++ b/bluez/repo/android/pts-avdtp.txt
@@ -0,0 +1,237 @@
+PTS test results for AVDTP
+
+PTS version: 6.1
+Tested: 22-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+TC_ACP_SNK_L2C_EM_BV_02_C		N/A
+TC_ACP_SNK_SIG_FRA_BV_01_C		N/A
+TC_ACP_SNK_SIG_FRA_BV_02_C		N/A
+TC_ACP_SNK_SIG_SEC_BI_01_C		N/A
+TC_ACP_SNK_SIG_SEC_BV_02_C		N/A
+TC_ACP_SNK_SIG_SMG_BV_06_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_08_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_10_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_12_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_14_C		N/A
+TC_ACP_SNK_SIG_SMG_BV_16_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_18_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_20_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_22_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_24_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_26_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BV_27_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_ESR05_BV_14_C	N/A
+TC_ACP_SNK_SIG_SMG_BI_02_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_03_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_05_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_06_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_08_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_09_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_11_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_12_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_14_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_15_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_17_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_18_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_20_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_21_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_23_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_24_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_26_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_27_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_28_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_29_C		N/A
+TC_ACP_SNK_SIG_SMG_BI_33_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_BI_34_C		N/A
+TC_ACP_SNK_SIG_SMG_ESR04_BI_28_C	PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SMG_ESR05_BI_15_C	N/A
+TC_ACP_SNK_SIG_SYN_BV_01_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_SIG_SYN_BV_03_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_TRA_BTR_BI_01_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_TRA_BTR_BV_02_C		PASS	avdtptest -d SINK -l
+TC_ACP_SNK_TRA_MUX_BI_01_C		N/A
+TC_ACP_SNK_TRA_MUX_BV_05_C		N/A
+TC_ACP_SNK_TRA_MUX_BV_06_C		N/A
+TC_ACP_SNK_TRA_REC_BI_01_C		N/A
+TC_ACP_SNK_TRA_REC_BV_01_C		N/A
+TC_ACP_SNK_TRA_REC_BV_02_C		N/A
+TC_ACP_SNK_TRA_REP_BI_01_C		N/A
+TC_ACP_SNK_TRA_REP_BV_01_C		N/A
+TC_ACP_SNK_TRA_REP_BV_02_C		N/A
+TC_ACP_SNK_TRA_REP_ESR02_BI_01_C	N/A
+TC_ACP_SNK_TRA_RHC_BI_01_C		N/A
+TC_ACP_SNK_TRA_RHC_BV_01_C		N/A
+TC_ACP_SNK_TRA_RHC_BV_02_C		N/A
+TC_ACP_SRC_L2C_EM_BV_02_C		N/A
+TC_ACP_SRC_SIG_FRA_BV_01_C		N/A
+TC_ACP_SRC_SIG_FRA_BV_02_C		N/A
+TC_ACP_SRC_SIG_SEC_BI_01_C		N/A
+TC_ACP_SRC_SIG_SEC_BV_02_C		N/A
+TC_ACP_SRC_SIG_SMG_BV_06_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_08_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_10_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_12_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_14_C		N/A
+TC_ACP_SRC_SIG_SMG_BV_16_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_18_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_20_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_22_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_24_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_26_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BV_27_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_ESR05_BV_14_C	N/A
+TC_ACP_SRC_SIG_SMG_BI_02_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_03_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_05_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_06_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_08_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_09_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_11_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_12_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_14_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_15_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_17_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_18_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_20_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_21_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_23_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_24_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_26_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_27_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_28_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_29_C		N/A
+TC_ACP_SRC_SIG_SMG_BI_33_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_BI_34_C		N/A
+TC_ACP_SRC_SIG_SMG_ESR04_BI_28_C	PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SMG_ESR05_BI_15_C	N/A
+TC_ACP_SRC_SIG_SYN_BV_05_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_SIG_SYN_BV_06_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_TRA_BTR_BI_01_C		PASS	avdtptest -d SRC -l
+TC_ACP_SRC_TRA_BTR_BV_01_C		PASS	avdtptest -d SRC -l -p -s start
+TC_ACP_SRC_TRA_MUX_BI_01_C		N/A
+TC_ACP_SRC_TRA_MUX_BV_05_C		N/A
+TC_ACP_SRC_TRA_MUX_BV_06_C		N/A
+TC_ACP_SRC_TRA_REC_BI_01_C		N/A
+TC_ACP_SRC_TRA_REC_BV_01_C		N/A
+TC_ACP_SRC_TRA_REC_BV_02_C		N/A
+TC_ACP_SRC_TRA_REP_BI_01_C		N/A
+TC_ACP_SRC_TRA_REP_BV_01_C		N/A
+TC_ACP_SRC_TRA_REP_BV_02_C		N/A
+TC_ACP_SRC_TRA_REP_ESR02_BI_01_C	N/A
+TC_ACP_SRC_TRA_RHC_BI_01_C		N/A
+TC_ACP_SRC_TRA_RHC_BV_01_C		N/A
+TC_ACP_SRC_TRA_RHC_BV_02_C		N/A
+TC_INT_SNK_L2C_BM_BV_03_C		N/A
+TC_INT_SNK_L2C_BM_BV_06_C		N/A
+TC_INT_SNK_SIG_FRA_BV_01_C		N/A
+TC_INT_SNK_SIG_FRA_BV_02_C		N/A
+TC_INT_SNK_SIG_SEC_BV_01_C		N/A
+TC_INT_SNK_SIG_SMG_BV_05_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_BV_07_C		PASS	avdtptest -d SINK -l -p
+						-v 0x0100
+TC_INT_SNK_SIG_SMG_BV_09_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_BV_11_C		PASS	avdtptest -d SINK -l -s getconf
+TC_INT_SNK_SIG_SMG_BV_13_C		N/A
+TC_INT_SNK_SIG_SMG_BV_15_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_BV_19_C		PASS	avdtptest -d SINK -l -s close
+TC_INT_SNK_SIG_SMG_BV_23_C		PASS	avdtptest -d SINK -l -p -s abort
+TC_INT_SNK_SIG_SMG_BV_25_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_BV_28_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_BV_31_C		PASS	avdtptest -d SINK -l -p
+						-v 0x0100
+TC_INT_SNK_SIG_SMG_ESR05_BV_13_C	N/A
+TC_INT_SNK_SIG_SMG_BI_01_C		N/A
+TC_INT_SNK_SIG_SMG_BI_04_C		N/A
+TC_INT_SNK_SIG_SMG_BI_07_C		N/A
+TC_INT_SNK_SIG_SMG_BI_10_C		N/A
+TC_INT_SNK_SIG_SMG_BI_13_C		N/A
+TC_INT_SNK_SIG_SMG_BI_16_C		N/A
+TC_INT_SNK_SIG_SMG_BI_19_C		N/A
+TC_INT_SNK_SIG_SMG_BI_22_C		N/A
+TC_INT_SNK_SIG_SMG_BI_25_C		N/A
+TC_INT_SNK_SIG_SMG_BI_30_C		PASS	avdtptest -d SINK -l -p
+						-v 0x0100
+TC_INT_SNK_SIG_SMG_BI_32_C		N/A
+TC_INT_SNK_SIG_SMG_BI_35_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_BI_36_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SMG_ESR05_BI_13_C	N/A
+TC_INT_SNK_SIG_SYN_BV_01_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SYN_BV_02_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_SIG_SYN_BV_03_C		PASS	avdtptest -d SINK -l
+TC_INT_SNK_SIG_SYN_BV_04_C		PASS	avdtptest -d SINK -l -p
+TC_INT_SNK_TRA_BTR_BI_01_C		PASS	avdtptest -d SINK -l
+TC_INT_SNK_TRA_BTR_BV_02_C		PASS	avdtptest -d SINK -l
+TC_INT_SNK_TRA_MUX_BI_01_C		N/A
+TC_INT_SNK_TRA_MUX_BV_05_C		N/A
+TC_INT_SNK_TRA_MUX_BV_06_C		N/A
+TC_INT_SNK_TRA_REC_BI_01_C		N/A
+TC_INT_SNK_TRA_REC_BV_01_C		N/A
+TC_INT_SNK_TRA_REC_BV_02_C		N/A
+TC_INT_SNK_TRA_REP_BI_01_C		N/A
+TC_INT_SNK_TRA_REP_BV_01_C		N/A
+TC_INT_SNK_TRA_REP_BV_02_C		N/A
+TC_INT_SNK_TRA_REP_ESR02_BI_01_C	N/A
+TC_INT_SNK_TRA_RHC_BI_01_C		N/A
+TC_INT_SNK_TRA_RHC_BV_01_C		N/A
+TC_INT_SNK_TRA_RHC_BV_02_C		N/A
+TC_INT_SRC_L2C_BM_BV_03_C		N/A
+TC_INT_SRC_L2C_BM_BV_06_C		N/A
+TC_INT_SRC_SIG_FRA_BV_01_C		N/A
+TC_INT_SRC_SIG_FRA_BV_02_C		N/A
+TC_INT_SRC_SIG_SEC_BV_01_C		N/A
+TC_INT_SRC_SIG_SMG_BV_05_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_BV_07_C		PASS	avdtptest -d SRC -l -p -v 0x0100
+TC_INT_SRC_SIG_SMG_BV_09_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_BV_11_C		PASS	avdtptest -d SRC -l -s getconf
+TC_INT_SRC_SIG_SMG_BV_13_C		N/A
+TC_INT_SRC_SIG_SMG_BV_15_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_BV_17_C		PASS	avdtptest -d SRC -l -p -s start
+TC_INT_SRC_SIG_SMG_BV_19_C		PASS	avdtptest -d SRC -l -s close
+TC_INT_SRC_SIG_SMG_BV_21_C		PASS	avdtptest -d SRC -l -s suspend
+TC_INT_SRC_SIG_SMG_BV_23_C		PASS	avdtptest -d SRC -l -p -s abort
+TC_INT_SRC_SIG_SMG_BV_25_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_BV_28_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_BV_31_C		PASS	avdtptest -d SRC -l -p -v 0x0100
+TC_INT_SRC_SIG_SMG_ESR05_BV_13_C	N/A
+TC_INT_SRC_SIG_SMG_BI_01_C		N/A
+TC_INT_SRC_SIG_SMG_BI_04_C		N/A
+TC_INT_SRC_SIG_SMG_BI_07_C		N/A
+TC_INT_SRC_SIG_SMG_BI_10_C		N/A
+TC_INT_SRC_SIG_SMG_BI_13_C		N/A
+TC_INT_SRC_SIG_SMG_BI_16_C		N/A
+TC_INT_SRC_SIG_SMG_BI_19_C		N/A
+TC_INT_SRC_SIG_SMG_BI_22_C		N/A
+TC_INT_SRC_SIG_SMG_BI_25_C		N/A
+TC_INT_SRC_SIG_SMG_BI_30_C		PASS	avdtptest -d SRC -l -p -v 0x0100
+TC_INT_SRC_SIG_SMG_BI_32_C		N/A
+TC_INT_SRC_SIG_SMG_BI_35_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_BI_36_C		PASS	avdtptest -d SRC -l -p
+TC_INT_SRC_SIG_SMG_ESR05_BI_13_C	N/A
+TC_INT_SRC_SIG_SYN_BV_05_C		PASS	avdtptest -d SRC -l
+TC_INT_SRC_SIG_SYN_BV_06_C		PASS	avdtptest -d SRC -l
+TC_INT_SRC_TRA_BTR_BI_01_C		PASS	avdtptest -d SRC -l
+TC_INT_SRC_TRA_BTR_BV_01_C		PASS	avdtptest -d SRC -l -p -s start
+TC_INT_SRC_TRA_MUX_BI_01_C		N/A
+TC_INT_SRC_TRA_MUX_BV_05_C		N/A
+TC_INT_SRC_TRA_MUX_BV_06_C		N/A
+TC_INT_SRC_TRA_REC_BI_01_C		N/A
+TC_INT_SRC_TRA_REC_BV_01_C		N/A
+TC_INT_SRC_TRA_REC_BV_02_C		N/A
+TC_INT_SRC_TRA_REP_BI_01_C		N/A
+TC_INT_SRC_TRA_REP_BV_01_C		N/A
+TC_INT_SRC_TRA_REP_BV_02_C		N/A
+TC_INT_SRC_TRA_REP_ESR02_BI_01_C	N/A
+TC_INT_SRC_TRA_RHC_BI_01_C		N/A
+TC_INT_SRC_TRA_RHC_BV_01_C		N/A
+TC_INT_SRC_TRA_RHC_BV_02_C		N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-avrcp.txt b/bluez/repo/android/pts-avrcp.txt
new file mode 100644
index 0000000..b7e1386
--- /dev/null
+++ b/bluez/repo/android/pts-avrcp.txt
@@ -0,0 +1,242 @@
+PTS test results for AVRCP
+
+PTS version: 6.1
+Tested: 21-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+		Controller (CT)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_CT_BGN_BV_01_I	N/A
+TC_CT_BGN_BV_02_I	N/A
+TC_CT_CA_BV_01_C	N/A
+TC_CT_CA_BV_03_C	N/A
+TC_CT_CA_BV_05_C	N/A
+TC_CT_CA_BV_07_C	N/A
+TC_CT_CA_BV_09_C	N/A
+TC_CT_CA_BV_11_C	N/A
+TC_CT_CA_BV_13_C	N/A
+TC_CT_CA_BV_15_C	N/A
+TC_CT_CA_BV_17_C	N/A
+TC_CT_CA_BV_18_C	N/A
+TC_CT_CA_BV_01_I	N/A
+TC_CT_CA_BV_02_I	N/A
+TC_CT_CA_BV_03_I	N/A
+TC_CT_CEC_BV_01_I	N/A
+TC_CT_CEC_BV_02_I	N/A
+TC_CT_CFG_BV_01_C	N/A
+TC_CT_CON_BV_01_C	N/A
+TC_CT_CON_BV_03_C	N/A
+TC_CT_CRC_BV_01_I	N/A
+TC_CT_CRC_BV_02_I	N/A
+TC_CT_ICC_BV_01_I	N/A
+TC_CT_ICC_BV_02_I	N/A
+TC_CT_MCN_CB_BV_01_C	N/A
+TC_CT_MCN_CB_BV_04_C	N/A
+TC_CT_MCN_CB_BV_07_C	N/A
+TC_CT_MCN_CB_BV_12_C	N/A
+TC_CT_MCN_CB_BV_01_I	N/A
+TC_CT_MCN_CB_BV_02_I	N/A
+TC_CT_MCN_CB_BV_03_I	N/A
+TC_CT_MCN_CB_BV_04_I	N/A
+TC_CT_MCN_CB_BV_05_I	N/A
+TC_CT_MCN_CB_BV_06_I	N/A
+TC_CT_MCN_NP_BV_01_C	N/A
+TC_CT_MCN_NP_BV_03_C	N/A
+TC_CT_MCN_NP_BV_05_C	N/A
+TC_CT_MCN_NP_BV_08_C	N/A
+TC_CT_MCN_NP_BV_10_C	N/A
+TC_CT_MCN_NP_BV_01_I	N/A
+TC_CT_MCN_NP_BV_02_I	N/A
+TC_CT_MCN_NP_BV_03_I	N/A
+TC_CT_MCN_NP_BV_04_I	N/A
+TC_CT_MCN_NP_BV_05_I	N/A
+TC_CT_MCN_NP_BV_06_I	N/A
+TC_CT_MCN_NP_BV_07_I	N/A
+TC_CT_MCN_SRC_BV_01_C	N/A
+TC_CT_MCN_SRC_BV_03_C	N/A
+TC_CT_MCN_SRC_BV_05_C	N/A
+TC_CT_MCN_SRC_BV_07_C	N/A
+TC_CT_MCN_SRC_BV_01_I	N/A
+TC_CT_MCN_SRC_BV_02_I	N/A
+TC_CT_MCN_SRC_BV_03_I	N/A
+TC_CT_MCN_SRC_BV_04_I	N/A
+TC_CT_MDI_BV_01_C	N/A
+TC_CT_MDI_BV_03_C	N/A
+TC_CT_MPS_BV_01_C	N/A
+TC_CT_MPS_BV_03_C	N/A
+TC_CT_MPS_BV_08_C	N/A
+TC_CT_MPS_BV_11_C	N/A
+TC_CT_MPS_BV_01_I	N/A
+TC_CT_MPS_BV_02_I	N/A
+TC_CT_MPS_BV_03_I	N/A
+TC_CT_NFY_BV_01_C	N/A
+TC_CT_PAS_BV_01_C	N/A
+TC_CT_PAS_BV_03_C	N/A
+TC_CT_PAS_BV_05_C	N/A
+TC_CT_PAS_BV_07_C	N/A
+TC_CT_PAS_BV_09_C	N/A
+TC_CT_PAS_BV_11_C	N/A
+TC_CT_PTH_BV_01_C	N/A
+TC_CT_PTH_BV_02_C	N/A
+TC_CT_PTT_BV_01_I	N/A
+TC_CT_PTT_BV_02_I	N/A
+TC_CT_PTT_BV_03_I	N/A
+TC_CT_PTT_BV_04_I	N/A
+TC_CT_PTT_BV_05_I	N/A
+TC_CT_RCR_BV_01_C	N/A
+TC_CT_RCR_BV_03_C	N/A
+TC_CT_VLH_BI_03_C	PASS	Send SetAbsolute Volume command by pressing
+				volume up or down buttons then adb logcat and
+				check VOLUME_CHANGED value
+TC_CT_VLH_BI_04_C	PASS	adb logcat: check VOLUME_CHANGED value
+TC_CT_VLH_BV_01_C	PASS	Send SetAbsolute Volume command by pressing
+				volume up or down buttons
+TC_CT_VLH_BV_03_C	PASS	adb logcat: check VOLUME_CHANGED value
+TC_CT_VLH_BV_01_I	PASS	Send SetAbsolute Volume command by pressing
+				volume up or down buttons
+TC_CT_VLH_BV_02_I	PASS 	Send SetAbsolute Volume command by pressing
+				volume up or down buttons
+-------------------------------------------------------------------------------
+
+
+		Target (TG)
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_TG_BGN_BV_01_I	N/A
+TC_TG_BGN_BV_02_I	N/A
+TC_TG_CA_BI_01_C	N/A
+TC_TG_CA_BI_02_C	N/A
+TC_TG_CA_BI_03_C	N/A
+TC_TG_CA_BI_04_C	N/A
+TC_TG_CA_BI_05_C	N/A
+TC_TG_CA_BI_06_C	N/A
+TC_TG_CA_BI_07_C	N/A
+TC_TG_CA_BI_08_C	N/A
+TC_TG_CA_BI_09_C	N/A
+TC_TG_CA_BI_10_C	N/A
+TC_TG_CA_BV_02_C	N/A
+TC_TG_CA_BV_04_C	N/A
+TC_TG_CA_BV_06_C	N/A
+TC_TG_CA_BV_08_C	N/A
+TC_TG_CA_BV_10_C	N/A
+TC_TG_CA_BV_12_C	N/A
+TC_TG_CA_BV_14_C	N/A
+TC_TG_CA_BV_16_C	N/A
+TC_TG_CA_BV_01_I	N/A
+TC_TG_CA_BV_02_I	N/A
+TC_TG_CA_BV_03_I	N/A
+TC_TG_CEC_BV_01_I	PASS
+TC_TG_CEC_BV_02_I	PASS
+TC_TG_CFG_BI_01_C	PASS
+TC_TG_CFG_BV_02_C	PASS
+TC_TG_CON_BV_02_C	N/A
+TC_TG_CON_BV_04_C	N/A
+TC_TG_CON_BV_05_C	N/A
+TC_TG_CRC_BV_01_I	PASS
+TC_TG_CRC_BV_02_I	PASS	Disconnect from PTS
+TC_TG_ICC_BV_01_I	PASS
+TC_TG_ICC_BV_02_I	PASS
+TC_TG_INV_BI_01_C	PASS
+TC_TG_INV_BI_02_C	N/A
+TC_TG_MCN_CB_BI_01_C	N/A
+TC_TG_MCN_CB_BI_02_C	N/A
+TC_TG_MCN_CB_BI_03_C	N/A
+TC_TG_MCN_CB_BI_04_C	N/A
+TC_TG_MCN_CB_BI_05_C	N/A
+TC_TG_MCN_CB_BV_02_C	N/A
+TC_TG_MCN_CB_BV_02_I	N/A
+TC_TG_MCN_CB_BV_03_C	N/A
+TC_TG_MCN_CB_BV_03_I	N/A
+TC_TG_MCN_CB_BV_04_I	N/A
+TC_TG_MCN_CB_BV_05_C	N/A
+TC_TG_MCN_CB_BV_05_I	N/A
+TC_TG_MCN_CB_BV_06_C	N/A
+TC_TG_MCN_CB_BV_06_I	N/A
+TC_TG_MCN_CB_BV_07_I	N/A
+TC_TG_MCN_CB_BV_08_C	N/A
+TC_TG_MCN_CB_BV_09_C	N/A
+TC_TG_MCN_CB_BV_10_C	N/A
+TC_TG_MCN_CB_BV_11_C	N/A
+TC_TG_MCN_CB_BV_13_C	N/A
+TC_TG_MCN_NP_BI_01_C	N/A
+TC_TG_MCN_NP_BI_02_C	N/A
+TC_TG_MCN_NP_BV_01_I	N/A
+TC_TG_MCN_NP_BV_02_C	N/A
+TC_TG_MCN_NP_BV_02_I	N/A
+TC_TG_MCN_NP_BV_03_I	N/A
+TC_TG_MCN_NP_BV_04_C	N/A
+TC_TG_MCN_NP_BV_04_I	N/A
+TC_TG_MCN_NP_BV_05_I	N/A
+TC_TG_MCN_NP_BV_06_C	N/A
+TC_TG_MCN_NP_BV_06_I	N/A
+TC_TG_MCN_NP_BV_07_C	N/A
+TC_TG_MCN_NP_BV_07_I	N/A
+TC_TG_MCN_NP_BV_09_C	N/A
+TC_TG_MCN_NP_BV_11_C	N/A
+TC_TG_MCN_SRC_BV_01_I	N/A
+TC_TG_MCN_SRC_BV_02_C	N/A
+TC_TG_MCN_SRC_BV_02_I	N/A
+TC_TG_MCN_SRC_BV_03_I	N/A
+TC_TG_MCN_SRC_BV_04_C	N/A
+TC_TG_MCN_SRC_BV_04_I	N/A
+TC_TG_MCN_SRC_BV_06_C	N/A
+TC_TG_MCN_SRC_BV_08_C	N/A
+TC_TG_MDI_BV_02_C	PASS
+TC_TG_MDI_BV_04_C	PASS
+TC_TG_MDI_BV_05_C	PASS
+TC_TG_MPS_BI_01_C	N/A
+TC_TG_MPS_BI_02_C	N/A
+TC_TG_MPS_BV_01_I	N/A
+TC_TG_MPS_BV_02_C	N/A
+TC_TG_MPS_BV_02_I	N/A
+TC_TG_MPS_BV_03_I	N/A
+TC_TG_MPS_BV_04_C	N/A
+TC_TG_MPS_BV_05_C	N/A
+TC_TG_MPS_BV_06_C	N/A
+TC_TG_MPS_BV_07_C	N/A
+TC_TG_MPS_BV_09_C	N/A
+TC_TG_MPS_BV_10_C	N/A
+TC_TG_MPS_BV_12_C	N/A
+TC_TG_NFY_BI_01_C	PASS
+TC_TG_NFY_BV_02_C	PASS	Change track when requested
+TC_TG_NFY_BV_03_C	N/A
+TC_TG_NFY_BV_04_C	PASS
+TC_TG_NFY_BV_05_C	PASS
+TC_TG_NFY_BV_06_C	N/A
+TC_TG_NFY_BV_07_C	N/A
+TC_TG_NFY_BV_08_C	PASS
+TC_TG_PAS_BI_01_C	N/A
+TC_TG_PAS_BI_02_C	N/A
+TC_TG_PAS_BI_03_C	N/A
+TC_TG_PAS_BI_04_C	N/A
+TC_TG_PAS_BI_05_C	N/A
+TC_TG_PAS_BV_02_C	N/A
+TC_TG_PAS_BV_04_C	N/A
+TC_TG_PAS_BV_06_C	N/A
+TC_TG_PAS_BV_08_C	N/A
+TC_TG_PAS_BV_10_C	N/A
+TC_TG_PTT_BV_01_I	PASS
+TC_TG_PTT_BV_02_I	PASS
+TC_TG_PTT_BV_03_I	N/A
+TC_TG_PTT_BV_04_I	N/A
+TC_TG_PTT_BV_05_I	N/A
+TC_TG_RCR_BV_02_C	PASS	Use modified media metadata (artist, title,
+					album etc.) to be larger than 512 byte.
+TC_TG_RCR_BV_04_C	PASS	Use modified media metadata (artist, title,
+					album etc.) to be larger than 512 byte.
+TC_TG_VLH_BI_01_C	N/A
+TC_TG_VLH_BI_02_C	N/A
+TC_TG_VLH_BV_01_I	N/A
+TC_TG_VLH_BV_02_C	N/A
+TC_TG_VLH_BV_02_I	N/A
+TC_TG_VLH_BV_04_C	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-bnep.txt b/bluez/repo/android/pts-bnep.txt
new file mode 100644
index 0000000..8b6986a
--- /dev/null
+++ b/bluez/repo/android/pts-bnep.txt
@@ -0,0 +1,60 @@
+PTS test results for BNEP
+
+PTS version: 6.1
+Tested: 11-May-2015
+Android version: 5.1
+Kernel version: 4.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+--------------------------------------------------------------------------------
+Test Name		Result	Notes
+--------------------------------------------------------------------------------
+TC_CTRL_BV_01_C		PASS	bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_02_C		PASS	bneptest -c <PTS addr> -b <bridge> -n <iface>
+TC_CTRL_BV_03_C		PASS	bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_04_C		PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_05_C		PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_06_C		PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_07_C		PASS	PTS issue #13169
+				bneptest -c <PTS addr> -b <bridge> -n <iface>
+					-t 3 -d 0 -e 1500 -y 1
+TC_CTRL_BV_08_C		PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_09_C		PASS	bneptest -c <PTS addr> -b <bridge> -n <iface>
+					-t 5 -g 00:00:00:00:00:00
+					-j ff:ff:ff:ff:ff:ff -y 1
+TC_CTRL_BV_10_C		PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_19_C		PASS	bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_11_C	PASS	PTS issue #13171
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_C_BV_12_C		PASS	PTS issue #13171
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_C_S_BV_13_C	PASS	PTS issue #13171
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_C_S_BV_14_C	PASS	PTS issue #13171
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_15_C	PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_16_C	PASS	PTS issue #13171
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_17_C	PASS	PTS issue #13169
+				bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_18_C	PASS	PTS issue #13171
+				bneptest -s -b <bridge> -n <iface>
+TC_TX_TYPE_0_BV_20_C	PASS	bneptest -c <PTS addr> -b <bridge> -n <iface>
+					-w 0 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_BV_21_C		PASS	bneptest -c <PTS addr> -b <bridge> -n <iface>
+					-w 2 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_S_BV_22_C	PASS	bneptest -c <PTS addr> -b <bridge> -n <iface>
+					-w 3 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_D_BV_23_C	PASS	bneptest -c <PTS addr> -b <bridge> -n <iface>
+					-w 4 -k <src hw addr> -f <dst hw addr>
diff --git a/bluez/repo/android/pts-did.txt b/bluez/repo/android/pts-did.txt
new file mode 100644
index 0000000..f44bf8f
--- /dev/null
+++ b/bluez/repo/android/pts-did.txt
@@ -0,0 +1,20 @@
+PTS test results for DID
+
+PTS version: 6.1
+Tested: 04-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name	Result	Notes
+-------------------------------------------------------------------------------
+TC_SDI_BV_1_I	PASS	IUT must be discoverable
+TC_SDI_BV_2_I	PASS	IUT must be discoverable
+TC_SDI_BV_3_I	PASS	IUT must be discoverable
+TC_SDI_BV_4_I	PASS	IUT must be discoverable
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-dis.txt b/bluez/repo/android/pts-dis.txt
new file mode 100644
index 0000000..c7900fd
--- /dev/null
+++ b/bluez/repo/android/pts-dis.txt
@@ -0,0 +1,40 @@
+PTS test results for DIS
+
+PTS version: 6.1
+Tested: 11-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+NOTE: DIS testing might require some extra properties to be set. Please refer
+to the README file in android folder. Section: Customization.
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_SD_BV_01_C		PASS
+TC_DEC_BV_01_C		PASS
+TC_DEC_BV_02_C		PASS
+TC_DEC_BV_03_C		PASS
+TC_DEC_BV_04_C		PASS
+TC_DEC_BV_05_C		PASS
+TC_DEC_BV_06_C		PASS
+TC_DEC_BV_07_C		PASS
+TC_DEC_BV_08_C		N/A
+TC_DEC_BV_09_C		PASS
+TC_CR_BV_01_C		PASS
+TC_CR_BV_02_C		PASS
+TC_CR_BV_03_C		PASS
+TC_CR_BV_04_C		PASS
+TC_CR_BV_05_C		PASS
+TC_CR_BV_06_C		PASS
+TC_CR_BV_07_C		PASS
+TC_CR_BV_08_C		N/A
+TC_CR_BV_09_C		PASS
+TC_SDP_BV_01_C		PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-gap.txt b/bluez/repo/android/pts-gap.txt
new file mode 100644
index 0000000..fe42d86
--- /dev/null
+++ b/bluez/repo/android/pts-gap.txt
@@ -0,0 +1,432 @@
+PTS test results for GAP
+
+PTS version: 6.1
+Tested: 11-May-2015
+Android version: 5.1
+Kernel version: 4.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_MOD_NDIS_BV_01_C	PASS	IUT must be non-discoverable
+TC_MOD_LDIS_BV_01_C	PASS	btmgmt discov limited 30
+TC_MOD_LDIS_BV_02_C	PASS	btmgmt discov limited 30
+TC_MOD_LDIS_BV_03_C	PASS	btmgmt discov limited 30
+TC_MOD_GDIS_BV_01_C	PASS	IUT must be discoverable
+TC_MOD_GDIS_BV_02_C	PASS	IUT must be discoverable
+TC_MOD_NCON_BV_01_C	PASS	btmgmt connectable off
+TC_MOD_CON_BV_01_C	PASS	btmgmt connectable on
+TC_BROB_BCST_BV_01_C	N/A
+TC_BROB_BCST_BV_02_C	N/A
+TC_BROB_BCST_BV_03_C	N/A
+TC_BROB_OBSV_BV_01_C	N/A
+TC_BROB_OBSV_BV_02_C	N/A
+TC_BROB_OBSV_BV_03_C	N/A
+TC_BROB_OBSV_BV_04_C	N/A
+TC_BROB_OBSV_BV_05_C	N/A
+TC_DISC_NONM_BV_01_C	PASS	btmgmt connectable off
+				btmgmt advertising on
+TC_DISC_NONM_BV_02_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				btmgmt advertising on
+TC_DISC_LIMM_BV_01_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				<answer NO to non-connectable adv question>
+				btmgmt discov limited 30
+TC_DISC_LIMM_BV_02_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+				btmgmt discov limited 30
+TC_DISC_LIMM_BV_03_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				<answer NO to non-connectable adv question>
+				btmgmt discov limited 30
+				btmgmt advertising on
+TC_DISC_LIMM_BV_04_C	PASS	btmgmt connectable on
+				btmgmt discov off
+				btmgmt power off
+				btmgmt bredr off
+				btmgmt power on
+				btmgmt discov limited 30
+				btmgmt advertising on
+TC_DISC_GENM_BV_01_C	PASS	btmgmt connectable on
+				btmgmt discov on
+				btmgmt advertising on
+				<answer NO to non-connectable adv question>
+TC_DISC_GENM_BV_02_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+				btmgmt discov on
+TC_DISC_GENM_BV_03_C	PASS	btmgmt connectable on
+				btmgmt discov on
+				btmgmt advertising on
+				<answer NO to non-connectable adv question>
+TC_DISC_GENM_BV_04_C	PASS	btmgmt connectable on
+				btmgmt power off
+				btmgmt le on
+				btmgmt bredr off
+				btmgmt power on
+				btmgmt discov on
+				btmgmt advertising on
+TC_DISC_LIMP_BV_01_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 unset and bit 0 set
+TC_DISC_LIMP_BV_02_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 set and bit 0 unset
+TC_DISC_LIMP_BV_03_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_LIMP_BV_04_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_LIMP_BV_05_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_GENP_BV_01_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 set and bit 0 unset
+TC_DISC_GENP_BV_02_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 unset and bit 0 set
+TC_DISC_GENP_BV_03_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_GENP_BV_04_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_GENP_BV_05_C	PASS	btmgmt find -l
+				PTS AD flags must have bit 1 and bit 0 unset
+TC_IDLE_GIN_BV_01_C	PASS	Start discovery from IUT
+TC_IDLE_LIN_BV_01_C	PASS	hcitool scan --iac=liac
+TC_IDLE_NAMP_BV_01_C	PASS	haltest: gattc register_client
+				gattc listen 1
+				gattc search_service 1 1800
+				gattc get_characteristic 1 {1800,0,1}
+				gattc read_characteristic 1 {1800,0,1} {2a00,1}
+TC_IDLE_NAMP_BV_02_C	PASS	btmgmt advertising on
+TC_CONN_NCON_BV_01_C	PASS	btmgmt connectable off
+				btmgmt advertising on
+				<answer NO to non-connectable adv question>
+TC_CONN_NCON_BV_02_C	PASS	<answer NO to non-connectable adv question>
+				Note: non-connectable and discoverable ?
+TC_CONN_NCON_BV_03_C	PASS	<answer NO to non-connectable adv question>
+				Note: non-connectable and discoverable ?
+TC_CONN_DCON_BV_01_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+TC_CONN_DCON_BV_02_C	N/A
+TC_CONN_DCON_BV_03_C	N/A
+TC_CONN_UCON_BV_01_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+TC_CONN_UCON_BV_02_C	PASS	btmgmt connectable on
+				btmgmt discov on
+				btmgmt advertising on
+TC_CONN_UCON_BV_03_C	PASS	btmgmt connectable on
+				btmgmt advertising on
+				btmgmt discov limited 30
+TC_CONN_UCON_BV_04_C	N/A
+TC_CONN_UCON_BV_05_C	N/A
+TC_CONN_ACEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
+TC_CONN_ACEP_BV_02_C	N/A
+TC_CONN_GCEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
+TC_CONN_GCEP_BV_02_C	PASS	'gattc connect' prior to pressing OK on PTS
+TC_CONN_GCEP_BV_03_C	N/A
+TC_CONN_GCEP_BV_04_C	N/A
+TC_CONN_SCEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
+TC_CONN_SCEP_BV_02_C	N/A
+TC_CONN_DCEP_BV_01_C	PASS	'gattc connect' prior to pressing OK on PTS
+TC_CONN_DCEP_BV_02_C	N/A
+TC_CONN_DCEP_BV_03_C	PASS	gattc connect
+TC_CONN_DCEP_BV_04_C	N/A
+TC_CONN_CPUP_BV_01_C	PASS	btmgmt advertising on
+TC_CONN_CPUP_BV_02_C	PASS	btmgmt advertising on
+TC_CONN_CPUP_BV_03_C	PASS	btmgmt advertising on
+TC_CONN_CPUP_BV_04_C	PASS	gattc register_client
+				gattc connect
+				gattc disconnect
+TC_CONN_CPUP_BV_05_C	PASS	gattc register_client
+				gattc connect
+				gattc disconnect
+TC_CONN_CPUP_BV_06_C	PASS	gattc register_client
+				gattc connect 1 <pts_bdaddr>
+				hcitool lecup <handle> 0x00C8 0x0960 0x0007
+					0x0960
+				gattc disconnect <client_if> <pts_bdaddr>
+					<conn_id>
+TC_CONN_TERM_BV_01_C	PASS	gattc register_client
+				gattc listen
+				gattc disconnect
+TC_CONN_PRDA_BV_01_C	PASS	gattc register_client
+				gattc listen
+				gattc disconnect
+TC_CONN_PRDA_BV_02_C	PASS	PTS issue #12950
+				gattc register_client
+				gattc connect <pts_bdaddr>
+				bluetooth create_bond <pts_bdaddr>
+				gattc connect <pts_bdaddr>
+				gattc test_command 226 <pts_bdaddr> 0 2
+TC_BOND_NBON_BV_01_C	PASS	haltest:
+				gattc register_client
+				gattc connect
+				gatt disconnect
+				gattc connect
+				gatt disconnect
+TC_BOND_NBON_BV_02_C	PASS	haltest: gattc register_client
+				gattc connect <client_id> <address>
+				bluetooth create_bond <address>
+				gattc connect <client_id> <address>
+				bluetooth create_bond <address>
+TC_BOND_NBON_BV_03_C	PASS	haltest: gattc listen
+TC_BOND_BON_BV_01_C	PASS	PTS issue #12503
+				haltest:
+				bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE
+				gattc register_client
+				gattc listen 1
+				bluetooth create_bond <pts_address>
+TC_BOND_BON_BV_02_C	PASS	gattc regicter_client
+				gattc scan
+				gattc connect
+				bluetooth create_bond
+				gattc connect
+				gattc test_command 226 <addr> <uuid> 1
+TC_BOND_BON_BV_03_C	PASS	gattc register_client
+				gattc listen 1
+TC_BOND_BON_BV_04_C	PASS	haltest: gattc_register_client
+				gattc connect <client_id> <address>
+				gattc disconnect
+				gattc connect <client_id> <address>
+				gattc test_command 226 <addr> 0 2
+TC_SEC_AUT_BV_11_C	PASS	haltest: gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 10 68
+				gatts start_service 2 1b 1
+				gattc listen 1
+				PTS asks for handle with Insufficient auth
+				gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_12_C	PASS	haltest: gatts register_server
+				gatts add_service 1 <uuid> 3
+				gatts add_characteristic 1 1b <uuid> 10 68
+				gatts start_service 1 1b 1
+				gatts connect 1 <addr>
+				PTS asks for handle with Insufficient auth
+				gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_13_C	PASS	haltest: gatts register_server
+				gatts add_service 1 <uuid> 3
+				gatts add_characteristic 1 1b <uuid> 10 68
+				gatts start_service 1 1b 1
+				gatts connect 1 <addr>
+				PTS asks for handle with Insufficient auth
+				gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_14_C	PASS	haltest: gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 10 68
+				gatts start_service 2 1b 1
+				gattc listen 1
+				PTS asks for handle with Insufficient auth
+				gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_15_C	N/A
+TC_SEC_AUT_BV_16_C	N/A
+TC_SEC_AUT_BV_17_C	PASS	haltest: gattc register_client
+				gattc connect
+				gattc search_service
+				gattc get_characteristic
+				gattc read_characteristic
+				bluetooth create_bond
+TC_SEC_AUT_BV_18_C	PASS	haltest: gattc register_client
+				gattc listen
+				gattc search_service
+				gattc get_characteristic
+				gattc read_characteristic
+				bluetooth create_bond
+				gattc read_characteristic
+TC_SEC_AUT_BV_19_C	PASS
+TC_SEC_AUT_BV_20_C	PASS	haltest: gattc register_client
+				gattc listen 1 1
+				gattc search_service 2
+				gattc get_characteristic 2 {1801,1,1}
+				gattc read_characteristic 2 {1801,1,1} {2a05,1}
+				gattc read_characteristic 2 {1801,1,1} {2a05,1}
+					1
+TC_SEC_AUT_BV_21_C	PASS	haltest: gattc register_client
+				gattc connect
+				bluetooth create_bond
+				gattc connect
+				gattc test_command 226 <addr> 0 1
+TC_SEC_AUT_BV_22_C	PASS	btmgmt io-cap 3
+				haltest: gattc register_client
+				gattc listen
+				gattc test_command 226 <addr> <u1> 1
+TC_SEC_AUT_BV_23_C	PASS	haltest: gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 10 34
+				gatts start_service 2 1b 1
+				gattc listen 1
+				PTS asks for handle with insufficient encryption
+				gatts send_response 3 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_24_C	PASS	haltest: gatts register_server
+				gatts add_service 1 <uuid> 3
+				gatts add_characteristic 1 1d <uuid> 10 34
+				gatts start_service 1 1d 1
+				gatts connect
+				gatts disconnect
+				gatts connect
+				PTS asks for handle with insufficient encryption
+				gatts send_response 2 1 0 1f 0 0x1234
+TC_SEC_CSIGN_BV_01_C	PASS	haltest:
+				gattc connect
+				bluetooth create_bond
+				gattc connect
+				gattc write_characteristic: <write_type> 4
+				gattc disconnect
+TC_SEC_CSIGN_BV_02_C	PASS	haltest: gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1d <uuid> 66 129
+				gatts start_service 2 1d 1
+				gattc listen 1
+				gatts disconnect
+TC_SEC_CSIGN_BI_01_C	PASS	gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1d <uuid> 66 129
+				gatts start_service 2 1d 1
+				gattc listen 1
+				gatts disconnect
+				gattc disconnect
+TC_SEC_CSIGN_BI_02_C	PASS	gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 66 129
+				gatts start_service 2 1b 1
+				gattc listen 1
+				gatts disconnect
+				gattc disconnect
+TC_SEC_CSIGN_BI_03_C	PASS	gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 66 129
+				gatts start_service 2 1b 1
+				gattc listen 1
+				gatts disconnect
+				gattc disconnect
+				bluetooth remove_bond
+TC_SEC_CSIGN_BI_04_C	PASS	gattc register_client
+				gatts register_server
+				gatts add_service 2 <uuid> 3
+				gatts add_characteristic 2 1b <uuid> 64 256
+				gatts start_service 2 1b 1
+				gattc listen 1
+				gatts disconnect
+				gattc disconnect
+TC_PRIV_CONN_BV_01_C	N/A
+TC_PRIV_CONN_BV_02_C	N/A
+TC_PRIV_CONN_BV_03_C	N/A
+TC_PRIV_CONN_BV_04_C	N/A
+TC_PRIV_CONN_BV_05_C	N/A
+TC_PRIV_CONN_BV_06_C	N/A
+TC_PRIV_CONN_BV_07_C	N/A
+TC_PRIV_CONN_BV_08_C	N/A
+TC_PRIV_CONN_BV_09_C	N/A
+TC_PRIV_CONN_BV_10_C	PASS	PTS issue #12951
+				Note: PIXITs required to be changed:
+				TSPX_using_public_device_address: FALSE
+				TSPX_using_random_device_address: TRUE
+				echo 30 > /sys/kernel/debug/bluetooth/hci0/
+								rpa_timeout
+				btmgmt power off
+				btmgmt privacy on
+				btmgmt power on
+TC_PRIV_CONN_BV_11_C	INC	PTS issue #12952
+				JIRA #BA-186
+TC_ADV_BV_01_C		N/A
+TC_ADV_BV_02_C		PASS	gattc register_client
+				gattc listen 1 1
+TC_ADV_BV_03_C		PASS	gattc register_client
+				gattc listen 1 1
+TC_ADV_BV_04_C		N/A
+TC_ADV_BV_05_C		PASS	gattc register_client
+				gattc listen 1 1
+TC_ADV_BV_06_C		N/A
+TC_ADV_BV_07_C		N/A
+TC_ADV_BV_08_C		N/A
+TC_ADV_BV_09_C		N/A
+TC_ADV_BV_10_C		N/A
+TC_ADV_BV_11_C		N/A
+TC_ADV_BV_12_C		N/A
+TC_ADV_BV_13_C		N/A
+TC_ADV_BV_14_C		N/A
+TC_ADV_BV_15_C		N/A
+TC_ADV_BV_16_C		N/A
+TC_GAT_BV_01_C		PASS	haltest:
+				gattc register_client
+				gattc listen
+TC_GAT_BV_02_C		N/A
+TC_GAT_BV_03_C		N/A
+TC_GAT_BV_04_C		N/A
+TC_GAT_BV_05_C		N/A
+TC_GAT_BV_06_C		N/A
+TC_GAT_BV_07_C		N/A
+TC_GAT_BV_08_C		N/A
+TC_DM_NCON_BV_01_C	PASS	bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_NONE
+				gattc register_client
+				gattc listen 1
+TC_DM_CON_BV_01_C	PASS	bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+				gattc register_client
+				gattc listen 1
+TC_DM_NBON_BV_01_C	PASS	btmgmt pairable off
+				btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_DM_BON_BV_01_C	PASS	btmgmt pairable on
+				btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_DM_GIN_BV_01_C	PASS
+TC_DM_LIN_BV_01_C	PASS
+TC_DM_NAD_BV_01_C	PASS	btmgmt find
+TC_DM_NAD_BV_02_C	PASS
+TC_DM_LEP_BV_01_C	PASS	bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+				gattc register_client
+				gattc listen 1 1
+TC_DM_LEP_BV_02_C	PASS	Use basic rate PTS dongle
+				haltest:
+				bluetooth set_adapter_property
+TC_DM_LEP_BV_04_C	PASS	haltest:
+				gattc connect <PTS bdaddr>
+TC_DM_LEP_BV_05_C	PASS	Use basic rate PTS dongle
+				btmgmt find -b
+				l2test -n <PTS bdaddr>
+TC_DM_LEP_BV_06_C	PASS	gattc connect
+TC_DM_LEP_BV_07_C	PASS	bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+				gattc register_client
+				gattc listen 1 1
+TC_DM_LEP_BV_08_C	PASS	bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+				gattc register_client
+				gattc listen 1 1
+TC_DM_LEP_BV_09_C	PASS	haltest:
+				bluetooth enable
+				bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+				gattc register_client
+				gattc scan 1
+				gattc connect <PTS addr>
+				l2test -n -P 31 <PTS addr>
+				disconnect
+TC_DM_LEP_BV_10_C	PASS	btmgmt find
+				l2test -n -P 31 <PTS addr>
+TC_DM_LEP_BV_11_C	PASS	haltest:
+				bluetooth enable
+				bluetooth set_adapter_property
+					BT_PROPERTY_ADAPTER_SCAN_MODE
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+				gattc register_client
+				gattc connect
+				gattc disconnect
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-gatt.txt b/bluez/repo/android/pts-gatt.txt
new file mode 100644
index 0000000..3531cca
--- /dev/null
+++ b/bluez/repo/android/pts-gatt.txt
@@ -0,0 +1,1422 @@
+PTS test results for GATT
+
+PTS version: 6.1
+Tested: 24-April-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_GAC_CL_BV_01_C	PASS	haltest:
+				gattc scan
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc_uuid>
+				gattc write_characteristic: type 3
+TC_GAC_SR_BV_01_C	PASS	PTS issue #13073
+				TSE #6271
+				haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+					<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+					<data> value greater than MTU
+					repeat with correct offset
+				gatts send_response:
+					<data> value greater than MTU
+					repeat with correct offset
+TC_GAD_CL_BV_01_C	PASS	haltest:
+				NOTE: Repeat following steps if asked
+				gattc connect <client_id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc disconnect <client_if> <PTS bdaddr>
+					<conn_id>
+TC_GAD_CL_BV_02_C	PASS	haltest:
+				NOTE: Repeat following steps if asked
+				gattc connect <client_id> <PTS addr>
+				gattc search_service <conn_id> <uuid>
+				gattc disconnect <client_if> <PTS bdaddr>
+					<conn_id>
+TC_GAD_CL_BV_03_C	PASS	haltest:
+				NOTE: Repeat following steps if asked
+				gattc connect <client_id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x2802 0x08
+					0x0001 0xffff
+				NOTE: Keep on mind MTU size
+					(some att rsp could not fit)
+				gattc_disconnect <client_if> <PTS bdaddr>
+					<conn_id>
+TC_GAD_CL_BV_04_C	PASS	haltest:
+				NOTE: Repeat following steps if asked
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+TC_GAD_CL_BV_05_C	PASS	haltest:
+				NOTE: Repeat following steps if asked
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x2803 0x08
+					<start hdl> <end hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAD_CL_BV_06_C	PASS	haltest:
+				NOTE: Repeat following steps if asked
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <conn_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAD_CL_BV_07_C	PASS	haltest:
+				NOTE: Repeat following step if asked
+				bluetooth get_remote_services
+TC_GAD_CL_BV_08_C	PASS	haltest:
+				NOTE: Repear following step if asked
+				bluetooth get_remote_services
+TC_GAD_SR_BV_01_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+TC_GAD_SR_BV_02_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+TC_GAD_SR_BV_03_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+				gatts register_server
+				gatts add_service
+				gatts start_service
+				gatts add_service
+				gatts add_included_service
+				gatts start_service
+TC_GAD_SR_BV_04_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+TC_GAD_SR_BV_05_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+TC_GAD_SR_BV_06_C	PASS	haltest:
+				gattc register_client
+				gattc listen
+TC_GAD_SR_BV_07_C	PASS	haltest:
+				when requested:
+				bluetooth get_remote_services
+				NOTE: check if found requested service
+TC_GAD_SR_BV_08_C	PASS	haltest:
+				when requested:
+				bluetooth get_remote_services
+				NOTE: check if found requested service
+TC_GAR_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0a <invalid char hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_02_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_03_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0a <inf. auth. att hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_04_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0a <inf. auth. att hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_05_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BV_03_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 0x0001 0xffff
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_06_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_07_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_09_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_10_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_11_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BV_04_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				NOTE: Repeat following steps if asked
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				NOTE: After reading all characteristics
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_12_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_13_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0c <handle> <offset>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_14_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0a <char_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_15_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_16_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_17_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BV_05_C	N/A
+TC_GAR_CL_BI_18_C	N/A
+TC_GAR_CL_BI_19_C	N/A
+TC_GAR_CL_BI_20_C	N/A
+TC_GAR_CL_BI_21_C	N/A
+TC_GAR_CL_BI_22_C	N/A
+TC_GAR_CL_BV_06_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_23_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_24_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0a <desc_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_25_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_26_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_27_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BV_07_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				NOTE: Repeat following step if asked
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				NOTE: After reading all characteristics
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_28_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_29_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0c <handle> <offset>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_30_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> 0x0000
+					0x0a <desc_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_31_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_32_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_33_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc read_descriptor <client_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_CL_BI_34_C	PASS	haltest:
+				gattc connect
+				gattc test_command 224 <addr> 0 0x0a <handle>
+				gattc disconnect
+TC_GAR_CL_BI_35_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <client_id> <svc_id>
+					<char_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAR_SR_BV_01_C	PASS
+TC_GAR_SR_BI_01_C	PASS
+TC_GAR_SR_BI_02_C	PASS
+TC_GAR_SR_BI_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 3
+				gatts start_service
+				gatts send_response
+TC_GAR_SR_BI_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_03_C	PASS
+TC_GAR_SR_BI_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 16
+				gatts start_service
+TC_GAR_SR_BI_07_C	PASS
+TC_GAR_SR_BI_08_C	PASS
+TC_GAR_SR_BI_09_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_10_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_11_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+TC_GAR_SR_BI_12_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 8 <permissions> 16
+				gatts start_service
+				gatts send_response
+TC_GAR_SR_BI_13_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAR_SR_BI_14_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAR_SR_BI_15_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_16_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_17_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						 <properties> 2 <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_05_C	N/A
+TC_GAR_SR_BI_18_C	N/A
+TC_GAR_SR_BI_19_C	N/A
+TC_GAR_SR_BI_20_C	N/A
+TC_GAR_SR_BI_21_C	N/A
+TC_GAR_SR_BI_22_C	N/A
+TC_GAR_SR_BV_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor
+				gatts start_service
+				gatts send_response
+TC_GAR_SR_BI_23_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 16
+				gatts start_service
+TC_GAR_SR_BI_24_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAR_SR_BI_25_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_26_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_27_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BV_07_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+TC_GAR_SR_BV_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+TC_GAR_SR_BI_28_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 16
+				gatts start_service
+TC_GAR_SR_BI_29_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAR_SR_BI_30_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAR_SR_BI_31_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAR_SR_BI_32_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAR_SR_BI_33_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permissions> 1
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAR_SR_BI_34_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts send_response <status> 0x80-0x9F
+TC_GAR_SR_BI_35_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic
+				gatts start_service
+				gatts send_response <status> 0x80-0x9F
+TC_GAW_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 1 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BV_02_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 4 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BV_03_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_02_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x12
+					<char_hdl> <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_03_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_04_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_05_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_06_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BV_05_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc execute_write <conn_id> 1
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_07_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x12
+					<char_hdl> <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_08_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc execute_write <conn_id> 1
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_09_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x16
+					<char_hdl> <offset> <data>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x18 1
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_11_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_12_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_13_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BV_06_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc execute_write <conn_id> 1
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_14_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x16
+					<char_hdl> <offset> <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_15_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_17_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_18_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_19_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 3 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BV_08_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_20_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x12
+					<char_hdl> <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_21_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_22_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_23_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_24_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BV_09_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 3 <data>
+				gattc execute_write <conn_id> 1
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_25_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x16
+					<char_hdl> <offset> <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_26_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 3 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_27_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x16
+					<char_hdl> <offset> <data>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x18 1
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_29_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 3 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_30_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 3 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_31_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x16
+					<desc_hdl> 0x0000 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_32_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x16
+					<desc_hdl> <offset> <data>
+				gattc test_command 0xe1 <PTS addr> 0x0000 0x18 0
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_33_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_34_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_35_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_CL_BI_36_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 <data>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAW_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 4 <permissions> 17
+				gatts start_service
+TC_GAW_SR_BV_02_C	PASS	haltest:
+				gatts add service
+				gatts add_characteristics:
+					<properties> 66 <permisions> 145
+				gatts start_service
+				gattc listen
+				gatts send_response: (twice)
+				NOTE: gatts_request_write_cb shall be called
+								 (verify it)
+TC_GAW_SR_BI_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 68
+						<permissions> 129
+				gatts start_service
+				gatts send_response: repeat with <data> 1
+TC_GAW_SR_BV_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+TC_GAW_SR_BI_02_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 1
+				gatts start_service
+TC_GAW_SR_BI_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BI_07_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response
+TC_GAW_SR_BI_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts start_service
+TC_GAW_SR_BI_09_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAW_SR_BI_11_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_12_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_13_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BV_10_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BI_14_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_15_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 3
+TC_GAW_SR_BI_17_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_18_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_19_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_07_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						repeat with correct value
+TC_GAW_CL_BV_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response
+TC_GAW_SR_BI_20_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_21_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 2 <permissions> 1
+				gatts add_descriptor: <permmisions> 1
+				gatts start_service
+TC_GAW_SR_BI_22_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+
+TC_GAW_SR_BI_23_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_24_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BV_09_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response:
+						repeat with correct value
+TC_GAW_SR_BI_25_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 1
+TC_GAW_SR_BI_26_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 1
+				gatts start_service
+TC_GAW_SR_BI_27_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 1
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 7
+TC_GAW_SR_BI_29_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 8
+TC_GAW_SR_BI_30_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 5
+TC_GAW_SR_BI_31_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response: <status> 12
+TC_GAW_SR_BI_32_C	PASS	PTS issue #12823
+				haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response
+				gatts send_response: <status> 13
+TC_GAW_SR_BI_33_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 13
+TC_GAW_SR_BI_34_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response
+				gatts send_response: <status> 13
+TC_GAW_SR_BI_35_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor: <permmisions> 17
+				gatts start_service
+				gatts send_response:
+						<data> value greater than MTU
+						repeat with correct offset
+				gatts send_response: <status> 13
+TC_GAN_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 0x0100
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAN_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 26 <permissions> 17
+				gatts add_descriptor: <uuid> 2902
+					<permission> 11
+				gatts start_service
+				gatts send_response
+				gatts send_response
+				gatts send_indication:
+						<attr_handle> char value handle
+						<confirm> 0
+TC_GAI_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc get_descriptor <client_id> <svc_id>
+					<char_id>
+				gattc write_descriptor <client_id> <svc_id>
+					<desc_id> 2 0x0200
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAI_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 42 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts add_service
+				gatts start_service
+TC_GAS_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GAS_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 42 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts add_service
+				gatts start_service
+TC_GAT_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_characteristic <conn_id> <svc_id>
+					<char_id>
+				wait for 30 sec timeout
+TC_GAT_CL_BV_02_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc write_characteristic <client_id> <svc_id>
+					<char_id> 2 <value>
+				wait for 30 sec timeout
+TC_GAT_SR_BV_01_C	PASS	haltest:
+				gatts add_service
+				gatts add_characteristic:
+						<properties> 42 <permissions> 17
+				gatts add_descriptor: <permissions> 17
+				gatts start_service
+				gatts add_service
+				gatts start_service
+TC_GPA_CL_BV_01_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_02_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_03_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_04_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_05_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_06_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc connect <client id> <PTS addr>
+				gattc search_service <conn_id>
+				gattc get_characteristic <conn_id> <svc uuid>
+				gattc read_descriptor <conn_id> <svc_id>
+					<char_id> <desc_id>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_07_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_08_C	PASS	haltest:
+				gattc connect <client id> <PTS addr>
+				gattc test_command 0xe0 <PTS addr> <char_uuid>
+					0x08 <start_hdl> <end_hdl>
+				gattc disconnect <client_id> <PTS addr>
+					<conn_id>
+TC_GPA_CL_BV_11_C	PASS	haltest:
+				gattc connect
+				Repeat following steps 5 times:
+				1.Find Characteristic Aggregate Format
+				gattc test_command <cmd> 224 [u1] 8
+				2.Read aggregate descriptor
+				gattc test_command <cmd> 224 [u1] 10
+				3.Read 3 handles from aggregate descriptor
+									value
+				gattc test_command <cmd> 224 [u1] 10
+				4.Compare descriptors values
+				gattc disconnect
+
+TC_GPA_CL_BV_12_C	PASS	haltest:
+				gattc connect
+				Repeat following steps 5 times:
+				1.Find Characteristic Presentation Format
+				gattc test_command <cmd> 224 [u1] 8
+				2.Find characteristic in this range
+				gattc test_command <cmd> 224 <uuid> 2803 [u1] 8
+				3.Read characteristic declaration
+				gattc test_command <cmd> 224 [u1] 10
+				4.Read characteristic value
+				gattc test_command <cmd> 224 [u1] 10
+				5.Compare characteristic value and
+							presentation format
+				gattc disconnect
+TC_GPA_SR_BV_01_C	PASS
+TC_GPA_SR_BV_02_C	PASS	haltest:
+				gatts add_service
+				gatts start_service
+TC_GPA_SR_BV_03_C	PASS	haltest:
+				gatts add_service
+				gatts add_service
+				add_included_service
+				gatts start_service
+				gatts start_service
+TC_GPA_SR_BV_04_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 10 <permissions> 17
+				gatts start_service
+TC_GPA_SR_BV_05_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2900
+				gatts start_service
+TC_GPA_SR_BV_06_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2901
+				gatts start_service
+TC_GPA_SR_BV_07_C	PASS
+TC_GPA_SR_BV_08_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2903
+				gatts start_service
+				gatts send_response
+TC_GPA_SR_BV_11_C	INC	PTS issue #13392
+				haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 138 <permissions> 17
+				gatts add_descriptor <UUID> 2905
+				gatts start_service
+				gatts send_response: repeat with correct offset
+								and data
+TC_GPA_SR_BV_12_C	PASS	haltest:
+				gatts add_service
+				gatts add_chaaracteristic:
+						<properties> 10 <permissions> 17
+				gatts add_descriptor <UUID> 2904
+				gatts start_service
+				gatts send_response: repeat with correct data
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-gavdp.txt b/bluez/repo/android/pts-gavdp.txt
new file mode 100644
index 0000000..30b59ea
--- /dev/null
+++ b/bluez/repo/android/pts-gavdp.txt
@@ -0,0 +1,23 @@
+PTS test results for GAVDP
+
+PTS version: 6.1
+Tested: 08-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+TC_ACP_APP_CON_BV_01_C			PASS
+TC_ACP_APP_TRC_BV_01_C			N/A
+TC_ACP_APP_TRC_BV_02_C			PASS
+TC_INT_APP_CON_BV_01_C			PASS
+TC_INT_APP_TRC_BV_01_C			N/A
+TC_INT_APP_TRC_BV_02_C			PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-hdp.txt b/bluez/repo/android/pts-hdp.txt
new file mode 100644
index 0000000..cbe1b3a
--- /dev/null
+++ b/bluez/repo/android/pts-hdp.txt
@@ -0,0 +1,296 @@
+PTS test results for HDP
+
+PTS version: 6.1
+Tested: 08-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_SRC_CON_BV_01_I	PASS	haltest:
+				hl register_application <args>
+				for instance:
+				hl register_application health intel heartrate
+				heartrate-monitor 1 BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE testing
+
+				when prompted:
+				bluetooth ssp_reply <args>
+				for instance:
+				bluetooth ssp_reply <bdaddr>
+				BT_SSP_VARIANT_CONSENT 1
+				Note: IUT must be discoverable, connectable
+TC_SRC_CON_BV_02_I	PASS	Note: IUT must be in discoverable mode
+TC_SRC_CON_BV_03_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_04_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_05_I	PASS	when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be in connectable mode
+TC_SRC_CON_BV_06_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_07_I	PASS	bluetooth start_discovery
+				Note: PTS HDP device must be discovered
+TC_SRC_CON_BV_08_I	PASS	bluetooth remove_bond <PTS addr>
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_09_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_10_I	N/A
+TC_SRC_CC_BV_01_C	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CC_BV_02_C	PASS	when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SRC_CC_BV_03_C	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_CC_BV_05_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SRC_CC_BV_07_C	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 2
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter
+
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+
+				when prompted:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+TC_SRC_CC_BV_09_C	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 2
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter
+
+				when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SRC_CC_BI_12_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SRC_HCT_BV_01_I	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SRC_HCT_BV_02_I	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SRC_HCT_BV_03_I	N/A
+TC_SRC_HCT_BV_04_I	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SRC_HCT_BV_05_C	N/A
+TC_SRC_HCT_BV_06_C	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SRC_HCT_BV_07_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+TC_SRC_DE_BV_01_I	N/A
+TC_SRC_DE_BV_02_I	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SOURCE 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SRC_DEP_BV_01_I	N/A
+TC_SRC_DEP_BV_02_I	N/A
+TC_SNK_CON_BV_01_I	PASS	haltest:
+				hl register_application <args>
+				for instance:
+				hl register_application health intel heartrate
+				heartrate-monitor 1 BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE testing
+
+				when prompted:
+				bluetooth ssp_reply <args>
+				for instance:
+				bluetooth ssp_reply <bdaddr>
+				BT_SSP_VARIANT_CONSENT 1
+				Note: IUT must be discoverable, connectable
+TC_SNK_CON_BV_02_I	PASS	Note: IUT must be discoverable, connectable
+TC_SNK_CON_BV_03_I	PASS	when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SNK_CON_BV_04_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SNK_CON_BV_05_I	PASS	when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_06_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_07_I	PASS	bluetooth start_discovery
+TC_SNK_CON_BV_08_I	PASS	bluetooth remove_bond <PTS addr>
+
+				when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SNK_CON_BV_09_I	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_10_I	N/A
+TC_SNK_CC_BV_01_C	PASS	haltest:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CC_BV_02_C	PASS	when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SNK_CC_BV_04_C	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_CC_BV_06_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 2
+				BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE
+				pulse-oximeter
+				BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING
+				pulse-oximeter
+TC_SNK_CC_BV_08_C	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 2
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter
+
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+
+				when prompted:
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+TC_SNK_CC_BV_10_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 2
+				BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE
+				pulse-oximeter
+				BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING
+				pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SNK_CC_BI_11_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SNK_HCT_BV_01_I	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				hl connect_channel <app_id> <bd_addr>
+				<mdep_cfg_index>
+
+				when prompted: bluetooth ssp_reply <args>
+TC_SNK_HCT_BV_02_I	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SNK_HCT_BV_03_I	N/A
+TC_SNK_HCT_BV_04_I	PASS    haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+
+				when prompted: bluetooth ssp_reply <args>
+				Note: IUT must be discoverable, connectable
+TC_SNK_HCT_BV_05_C	N/A
+TC_SNK_HCT_BV_06_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SNK_HCT_BV_07_C	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SNK_DE_BV_01_I	N/A
+TC_SNK_DE_BV_02_I	PASS	haltest:
+				hl register_application bluez-android Bluez
+				bluez-hdp health-device-profile 1
+				BTHL_MDEP_ROLE_SINK 4100
+				BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+				Note: IUT must be discoverable, connectable
+TC_SNK_DEP_BV_03_I	N/A
+TC_SNK_DEP_BV_04_I	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-hfp.txt b/bluez/repo/android/pts-hfp.txt
new file mode 100644
index 0000000..05fccd8
--- /dev/null
+++ b/bluez/repo/android/pts-hfp.txt
@@ -0,0 +1,250 @@
+PTS test results for HFP
+
+PTS version: 6.1
+Tested: 14-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_AG_OOR_BV_01_I	PASS
+TC_AG_OOR_BV_02_I	PASS
+TC_AG_TRS_BV_01_I	PASS
+TC_AG_PSI_BV_01_I	PASS
+TC_AG_PSI_BV_02_I	N/A
+TC_AG_PSI_BV_03_I	PASS
+TC_AG_PSI_BV_04_I	PASS
+TC_AG_PSI_BV_05_I	PASS
+TC_AG_ACS_BV_02_I	N/A
+TC_AG_ACS_BV_04_I	PASS
+TC_AG_ACS_BV_06_I	N/A
+TC_AG_ACS_BV_08_I	PASS
+TC_AG_ACS_BV_10_I	N/A
+TC_AG_ACS_BV_11_I	PASS
+TC_AG_ACS_BI_14_I	PASS
+TC_AG_ACS_BI_16_I	N/A
+TC_AG_ACR_BV_01_I	PASS
+TC_AG_ACR_BV_02_I	PASS
+TC_AG_CLI_BV_01_I	PASS
+TC_AG_ICA_BV_01_I	N/A
+TC_AG_ICA_BV_02_I	N/A
+TC_AG_ICA_BV_04_I	PASS
+TC_AG_ICA_BV_05_I	N/A
+TC_AG_ICA_BV_06_I	PASS
+TC_AG_ICR_BV_01_I	PASS
+TC_AG_ICR_BV_02_I	PASS
+TC_AG_TCA_BV_01_I	PASS
+TC_AG_TCA_BV_02_I	PASS
+TC_AG_TCA_BV_03_I	PASS
+TC_AG_TCA_BV_04_I	PASS
+TC_AG_TCA_BV_05_I	PASS
+TC_AG_ATH_BV_03_I	PASS
+TC_AG_ATH_BV_04_I	PASS
+TC_AG_ATH_BV_05_I	PASS
+TC_AG_ATH_BV_06_I	PASS
+TC_AG_ATA_BV_01_I	PASS
+TC_AG_ATA_BV_02_I	PASS
+TC_AG_OCN_BV_01_I	PASS
+TC_AG_OCM_BV_01_I	PASS
+TC_AG_OCM_BV_02_I	PASS
+TC_AG_OCL_BV_01_I	PASS
+TC_AG_OCL_BV_02_I	PASS
+TC_AG_TWC_BV_01_I	PASS
+TC_AG_TWC_BV_02_I	PASS
+TC_AG_TWC_BV_03_I	PASS
+TC_AG_TWC_BV_04_I	PASS
+TC_AG_TWC_BV_05_I	PASS
+TC_AG_TWC_BV_06_I	N/A
+TC_AG_CIT_BV_01_I	PASS
+TC_AG_ENO_BV_01_I	PASS
+TC_AG_ENO_BV_02_I	N/A
+TC_AG_VRA_BV_01_I	PASS
+TC_AG_VRA_BV_02_I	PASS
+TC_AG_VRA_BI_01_I	PASS
+TC_AG_VRD_BV_01_I	N/A
+TC_AG_VTG_BV_01_I	N/A
+TC_AG_TDC_BV_01_I	PASS
+TC_AG_RSV_BV_01_I	PASS
+TC_AG_RSV_BV_02_I	PASS
+TC_AG_RSV_BV_03_I	PASS
+TC_AG_RMV_BV_01_I	N/A
+TC_AG_RMV_BV_02_I	N/A
+TC_AG_RMV_BV_03_I	N/A
+TC_AG_ECS_BV_01_I	PASS
+TC_AG_ECS_BV_02_I	PASS
+TC_AG_ECS_BV_03_I	PASS
+TC_AG_ECC_BV_01_I	N/A
+TC_AG_ECC_BV_02_I	N/A
+TC_AG_ECC_BI_03_I	PASS
+TC_AG_ECC_BI_04_I	PASS
+TC_AG_RHH_BV_01_I	N/A
+TC_AG_RHH_BV_02_I	N/A
+TC_AG_RHH_BV_03_I	N/A
+TC_AG_RHH_BV_04_I	N/A
+TC_AG_RHH_BV_05_I	N/A
+TC_AG_RHH_BV_06_I	N/A
+TC_AG_RHH_BV_07_I	N/A
+TC_AG_RHH_BV_08_I	N/A
+TC_AG_NUM_BV_01_I	PASS
+TC_AG_SLC_BV_01_C	PASS
+TC_AG_SLC_BV_02_C	PASS
+TC_AG_SLC_BV_03_C	PASS
+TC_AG_SLC_BV_04_C	PASS
+TC_AG_SLC_BV_05_I	PASS
+TC_AG_SLC_BV_06_I	PASS
+TC_AG_SLC_BV_07_I	PASS
+TC_AG_SLC_BV_09_I	N/A
+TC_AG_SLC_BV_10_I	N/A
+TC_AG_ACC_BV_08_I	PASS
+TC_AG_ACC_BV_09_I	PASS
+TC_AG_ACC_BV_10_I	PASS
+TC_AG_ACC_BV_11_I	PASS
+TC_AG_ACC_BI_12_I	PASS
+TC_AG_ACC_BI_13_I	PASS
+TC_AG_ACC_BI_14_I	PASS
+TC_AG_ACC_BV_15_I	PASS
+TC_AG_WBS_BV_01_I	PASS
+TC_AG_DIS_BV_01_I	PASS
+TC_AG_SDP_BV_01_I	PASS
+TC_AG_IIA_BV_01_I	PASS
+TC_AG_IIA_BV_02_I	PASS
+TC_AG_IIA_BV_03_I	N/A
+TC_AG_IIA_BV_05_I	PASS
+TC_AG_IID_BV_01_I	PASS
+TC_AG_IID_BV_02_I	N/A
+TC_AG_IID_BV_03_I	PASS
+TC_AG_IID_BV_04_I	PASS
+TC_AG_IIC_BV_01_I	PASS
+TC_AG_IIC_BV_02_I	PASS
+TC_AG_IIC_BV_03_I	PASS
+TC_AG_HFI_BV_02_I	N/A
+TC_AG_HFI_BV_03_I	N/A
+TC_HF_OOR_BV_01_I	N/A
+TC_HF_OOR_BV_02_I	N/A
+TC_HF_TRS_BV_01_I	N/A
+TC_HF_PSI_BV_01_I	N/A
+TC_HF_PSI_BV_02_I	N/A
+TC_HF_PSI_BV_03_I	N/A
+TC_HF_PSI_BV_04_I	N/A
+TC_HF_ACS_BV_01_I	N/A
+TC_HF_ACS_BV_03_I	N/A
+TC_HF_ACS_BV_05_I	N/A
+TC_HF_ACS_BV_07_I	N/A
+TC_HF_ACS_BV_09_I	N/A
+TC_HF_ACS_BV_12_I	N/A
+TC_HF_ACS_BI_13_I	N/A
+TC_HF_ACR_BV_01_I	N/A
+TC_HF_ACR_BV_02_I	N/A
+TC_HF_CLI_BV_01_I	N/A
+TC_HF_ICA_BV_01_I	N/A
+TC_HF_ICA_BV_02_I	N/A
+TC_HF_ICA_BV_03_I	N/A
+TC_HF_ICA_BV_04_I	N/A
+TC_HF_ICA_BV_05_I	N/A
+TC_HF_ICA_BV_06_I	N/A
+TC_HF_ICA_BV_07_I	N/A
+TC_HF_ICR_BV_01_I	N/A
+TC_HF_ICR_BV_02_I	N/A
+TC_HF_TCA_BV_01_I	N/A
+TC_HF_TCA_BV_02_I	N/A
+TC_HF_TCA_BV_03_I	N/A
+TC_HF_TCA_BV_04_I	N/A
+TC_HF_ATH_BV_03_I	N/A
+TC_HF_ATH_BV_04_I	N/A
+TC_HF_ATH_BV_05_I	N/A
+TC_HF_ATH_BV_06_I	N/A
+TC_HF_ATH_BV_09_I	N/A
+TC_HF_ATA_BV_01_I	N/A
+TC_HF_ATA_BV_02_I	N/A
+TC_HF_ATA_BV_03_I	N/A
+TC_HF_OCN_BV_01_I	N/A
+TC_HF_OCM_BV_01_I	N/A
+TC_HF_OCM_BV_02_I	N/A
+TC_HF_OCL_BV_01_I	N/A
+TC_HF_OCL_BV_02_I	N/A
+TC_HF_TWC_BV_01_I	N/A
+TC_HF_TWC_BV_02_I	N/A
+TC_HF_TWC_BV_03_I	N/A
+TC_HF_TWC_BV_04_I	N/A
+TC_HF_TWC_BV_05_I	N/A
+TC_HF_TWC_BV_06_I	N/A
+TC_HF_CIT_BV_01_I	N/A
+TC_HF_ENO_BV_01_I	N/A
+TC_HF_VRA_BV_01_I	N/A
+TC_HF_VRA_BV_02_I	N/A
+TC_HF_VRA_BV_03_I	N/A
+TC_HF_VRD_BV_01_I	N/A
+TC_HF_VTG_BV_01_I	N/A
+TC_HF_TDC_BV_01_I	N/A
+TC_HF_RSV_BV_01_I	N/A
+TC_HF_RSV_BV_02_I	N/A
+TC_HF_RSV_BV_03_I	N/A
+TC_HF_RMV_BV_01_I	N/A
+TC_HF_RMV_BV_02_I	N/A
+TC_HF_RMV_BV_03_I	N/A
+TC_HF_ECS_BV_01_I	N/A
+TC_HF_ECS_BV_02_I	N/A
+TC_HF_ECS_BV_03_I	N/A
+TC_HF_ECC_BV_01_I	N/A
+TC_HF_ECC_BV_02_I	N/A
+TC_HF_RHH_BV_01_I	N/A
+TC_HF_RHH_BV_02_I	N/A
+TC_HF_RHH_BV_03_I	N/A
+TC_HF_RHH_BV_04_I	N/A
+TC_HF_RHH_BV_05_I	N/A
+TC_HF_RHH_BV_06_I	N/A
+TC_HF_RHH_BV_07_I	N/A
+TC_HF_RHH_BV_08_I	N/A
+TC_HF_NUM_BV_01_I	N/A
+TC_HF_NUM_BI_01_I	N/A
+TC_HF_SLC_BV_01_C	N/A
+TC_HF_SLC_BV_02_C	N/A
+TC_HF_SLC_BV_03_C	N/A
+TC_HF_SLC_BV_04_C	N/A
+TC_HF_SLC_BV_05_I	N/A
+TC_HF_SLC_BV_06_I	N/A
+TC_HF_SLC_BV_08_I	N/A
+TC_HF_ACC_BV_01_I	N/A
+TC_HF_ACC_BV_02_I	N/A
+TC_HF_ACC_BV_03_I	N/A
+TC_HF_ACC_BV_04_I	N/A
+TC_HF_ACC_BV_05_I	N/A
+TC_HF_ACC_BV_06_I	N/A
+TC_HF_ACC_BV_07_I	N/A
+TC_HF_WBS_BV_02_I	N/A
+TC_HF_WBS_BV_03_I	N/A
+TC_HF_DIS_BV_01_I	N/A
+TC_HF_DIS_BV_02_I	N/A
+TC_HF_SDP_BV_01_I	N/A
+TC_HF_SDP_BV_02_C	N/A
+TC_HF_SDP_BV_03_C	N/A
+TC_HF_ATAH_BV_01_I	N/A
+TC_HF_OCA_BV_01_I	N/A
+TC_HF_IIA_BV_04_I	N/A
+TC_AG_COD_BV_02_I	PASS
+TC_AG_ATAH_BV_01_I	PASS
+TC_AG_ATA_BV_03_I	PASS
+TC_AG_ATH_BV_09_I	PASS
+TC_AG_SDP_BV_02_C	PASS
+TC_AG_SDP_BV_03_C	PASS
+TC_AG_ICA_BV_07_I	PASS
+TC_AG_ICA_BV_08_I	PASS
+TC_AG_ICA_BV_09_I	PASS
+TC_AG_VRA_BV_03_I	PASS
+TC_AG_OCA_BV_01_I	PASS
+TC_AG_TCA_BV_06_I	PASS
+TC_HF_ATAH_BV_03_I	N/A
+TC_HF_ATA_BV_03_I	N/A
+TC_HF_ATH_BV_09_I	N/A
+TC_HF_SDP_BV_02_C	N/A
+TC_HF_SDP_BV_03_C	N/A
+TC_HF_DIS_BV_02_I	N/A
+TC_HF_ICA_BV_07_I	N/A
+TC_HF_VRA_BV_03_I	N/A
+TC_HF_OCA_BV_01_I	N/A
diff --git a/bluez/repo/android/pts-hid.txt b/bluez/repo/android/pts-hid.txt
new file mode 100644
index 0000000..80f11e8
--- /dev/null
+++ b/bluez/repo/android/pts-hid.txt
@@ -0,0 +1,74 @@
+PTS test results for HID
+
+PTS version: 6.1
+Tested: 19-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_HOS_HCE_BV_01_I	PASS
+TC_HOS_HCE_BV_03_I	PASS
+TC_HOS_HCE_BV_04_I	PASS
+TC_HOS_HCR_BV_01_I	PASS
+TC_HOS_HCR_BV_02_I	PASS
+TC_HOS_HCR_BV_03_I	N/A
+TC_HOS_HCR_BV_04_I	N/A
+TC_HOS_HDT_BV_01_I	PASS
+TC_HOS_HDT_BV_02_I	PASS	haltest: hidhost connect <addr>
+				hidhost send_data <addr> ff00
+				NOTE: PTS displays wrong report data on popup
+					PTS issue #13021
+TC_HOS_HDT_BV_03_I	N/A
+TC_HOS_HDT_BV_04_I	N/A
+TC_HOS_HID_BV_01_C	N/A
+TC_HOS_HID_BV_02_C	N/A
+TC_HOS_HID_BV_03_C	N/A
+TC_HOS_HID_BV_04_C	N/A
+TC_HOS_HID_BV_05_C	N/A
+TC_HOS_HID_BV_06_C	N/A
+TC_HOS_HID_BV_08_C	N/A
+TC_HOS_HID_BV_09_C	N/A
+TC_HOS_HID_BV_10_C	N/A
+TC_HOS_DAT_BV_01_C	PASS	haltest: hidhost connect <addr>
+				hidhost send_data <addr> ff00
+				NOTE: PTS displays wrong report data on popup
+					PTS issue #13021
+TC_HOS_DAT_BV_02_C	N/A
+TC_HOS_DAT_BI_01_C	N/A
+TC_HOS_DAT_BI_02_C	N/A
+TC_DEV_HCE_BV_01_I	N/A
+TC_DEV_HCE_BV_02_I	N/A
+TC_DEV_HCE_BV_03_I	N/A
+TC_DEV_HCE_BV_04_I	N/A
+TC_DEV_HCE_BV_05_I	N/A
+TC_DEV_HCR_BV_01_I	N/A
+TC_DEV_HCR_BV_02_I	N/A
+TC_DEV_HCR_BV_03_I	N/A
+TC_DEV_HCR_BV_04_I	N/A
+TC_DEV_HDT_BV_01_I	N/A
+TC_DEV_HDT_BV_02_I	N/A
+TC_DEV_HDT_BV_03_I	N/A
+TC_DEV_HDT_BV_04_I	N/A
+TC_DEV_HID_BV_01_C	N/A
+TC_DEV_HID_BV_03_C	N/A
+TC_DEV_HID_BV_04_C	N/A
+TC_DEV_HID_BV_05_C	N/A
+TC_DEV_HID_BV_06_C	N/A
+TC_DEV_HID_BV_08_C	N/A
+TC_DEV_HID_BV_09_C	N/A
+TC_DEV_HID_BV_10_C	N/A
+TC_DEV_HID_BI_01_C	N/A
+TC_DEV_HID_BI_02_C	N/A
+TC_DEV_DAT_BV_01_C	N/A
+TC_DEV_SDD_BV_01_C	N/A
+TC_DEV_SDD_BV_02_C	N/A
+TC_DEV_SDD_BV_03_C	N/A
+TC_DEV_SDD_BV_04_I	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-hogp.txt b/bluez/repo/android/pts-hogp.txt
new file mode 100644
index 0000000..a6b8dc1
--- /dev/null
+++ b/bluez/repo/android/pts-hogp.txt
@@ -0,0 +1,102 @@
+PTS test results for HoG
+
+PTS version: 6.1
+Tested: 20-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_HGDS_HH_BV_01_I	PASS
+TC_HGDS_HH_BV_02_I	PASS
+TC_HGDS_HH_BV_03_I	PASS
+TC_HGDS_HD_BV_01_I	N/A
+TC_HGDS_HD_BV_02_I	N/A
+TC_HGDR_RH_BV_01_I	PASS
+TC_HGDC_RH_BV_01_I	PASS
+TC_HGDC_RH_BV_02_I	PASS
+TC_HGDC_RH_BV_03_I	PASS
+TC_HGDC_RH_BV_04_I	PASS
+TC_HGDC_RH_BV_05_I	PASS
+TC_HGDC_RH_BV_06_I	PASS
+TC_HGDC_RH_BV_07_I	PASS
+TC_HGDC_HH_BV_08_I	PASS
+TC_HGDC_HH_BV_14_I	PASS
+TC_HGDC_HH_BV_15_I	PASS
+TC_HGDC_HH_BV_16_I	PASS
+TC_HGDC_BH_BV_09_I	N/A
+TC_HGDC_BH_BV_10_I	N/A
+TC_HGDC_BH_BV_11_I	N/A
+TC_HGDC_BH_BV_12_I	N/A
+TC_HGDC_BH_BV_13_I	N/A
+TC_HGRF_RH_BV_01_I	PASS
+TC_HGRF_RH_BV_02_I	PASS
+TC_HGRF_RH_BV_03_I	PASS
+TC_HGRF_RH_BV_04_I	PASS
+TC_HGRF_RH_BV_05_I	PASS
+TC_HGRF_RH_BV_19_I	PASS
+TC_HGRF_RH_BV_06_I	PASS
+TC_HGRF_RH_BV_07_I	PASS
+TC_HGRF_RH_BV_08_I	PASS
+TC_HGRF_RH_BV_09_I	PASS
+TC_HGRF_HH_BV_10_I	PASS
+TC_HGRF_HH_BV_11_I	PASS
+TC_HGRF_HH_BV_12_I	PASS
+TC_HGRF_BH_BV_13_I	N/A
+TC_HGRF_BH_BV_14_I	N/A
+TC_HGRF_BH_BV_15_I	N/A
+TC_HGRF_BH_BV_16_I	N/A
+TC_HGRF_BH_BV_17_I	N/A
+TC_HGRF_HH_BV_18_I	N/A
+TC_HGWF_RH_BV_01_I	PASS	haltest: hidhost connect <addr>
+				hidhost set_report <addr> BTHH_INPUT_REPORT
+					AAB3F8A6CD
+				hidhost disconnect <addr>
+TC_HGWF_RH_BV_02_I	PASS	haltest: hidhost connect <addr>
+				hidhost set_report <addr> BTHH_OUTPUT_REPORT
+					EF907856341200
+				hidhost disconnect <addr>
+TC_HGWF_RH_BV_03_I	PASS	haltest: hidhost connect <addr>
+				hidhost set_report <addr> BTHH_OUTPUT_REPORT
+					EF907856341200
+				hidhost disconnect <addr>
+TC_HGWF_RH_BV_04_I	PASS	haltest: hidhost connect <addr>
+				hidhost set_report <addr> BTHH_FEATURE_REPORT
+					EA453F2D87
+				hidhost disconnect <addr>
+TC_HGWF_RH_BV_05_I	N/A
+TC_HGWF_RH_BV_06_I	N/A
+TC_HGWF_RH_BV_07_I	N/A
+TC_HGWF_BH_BV_08_I	N/A
+TC_HGWF_BH_BV_09_I	N/A
+TC_HGWF_BH_BV_10_I	N/A
+TC_HGWF_BH_BV_11_I	N/A
+TC_HGCF_RH_BV_01_I	PASS
+TC_HGCF_RH_BV_02_I	PASS	haltest: hidhost connect <addr>
+				gattc search_service 1
+				gattc get_characteristic 1 {1812,2,1}
+				gattc get_descriptor 1 {1812,2,1} {2a4d,5}
+				gattc write_descriptor 1 {1812,2,1} {2a4d,5}
+					{2902,1} 2 0x0000 0
+				gattc get_characteristic 1 {1812,5,1}
+				gattc get_descriptor 1 {1812,5,1} {2a4d,4}
+				gattc write_descriptor 1 {1812,5,1}
+					{2a4d,4} {2902,1} 2 0x0000 0
+				hidhost disconnect <addr>
+TC_HGCF_BH_BV_03_I	N/A
+TC_HGCF_BH_BV_04_I	N/A
+TC_HGCF_BH_BV_05_I	N/A
+TC_HGCF_BH_BV_06_I	N/A
+TC_HGNF_RH_BV_01_I	PASS
+TC_HGNF_RH_BI_01_I	PASS
+TC_HGNF_RH_BI_01_I	PASS
+TC_HGNF_BH_BV_02_I	N/A
+TC_HGNF_BH_BV_03_I	N/A
+TC_HGNF_BH_BI_01_I	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-hsp.txt b/bluez/repo/android/pts-hsp.txt
new file mode 100644
index 0000000..8a48dd8
--- /dev/null
+++ b/bluez/repo/android/pts-hsp.txt
@@ -0,0 +1,41 @@
+PTS test results for HSP
+
+PTS version: 6.1
+Tested: 13-May-2015
+Android version: 5.1
+
+Results:
+PASS    test passed
+FAIL    test failed
+INC     test is inconclusive
+N/A     test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name               Result  Notes
+-------------------------------------------------------------------------------
+TC_AG_IAC_BV_01_I	PASS
+TC_AG_IAC_BV_02_I	N/A
+TC_AG_OAC_BV_01_I	PASS
+TC_AG_ACR_BV_01_I	PASS
+TC_AG_ACR_BV_02_I	PASS
+TC_AG_ACT_BV_01_I	PASS
+TC_AG_ACT_BV_02_I	PASS
+TC_AG_RAV_BV_01_I	PASS
+TC_AG_RAV_BV_02_I	PASS
+TC_AG_RAV_BV_03_I	PASS
+TC_AG_RAV_BV_04_I	N/A
+TC_AG_RAV_BV_05_I	N/A
+TC_AG_RAV_BV_06_I	N/A
+TC_HS_IAC_BV_01_I	N/A
+TC_HS_IAC_BV_02_I	N/A
+TC_HS_OAC_BV_01_I	N/A
+TC_HS_ACR_BV_01_I	N/A
+TC_HS_ACR_BV_02_I	N/A
+TC_HS_ACT_BV_01_I	N/A
+TC_HS_ACT_BV_02_I	N/A
+TC_HS_RAV_BV_01_I	N/A
+TC_HS_RAV_BV_02_I	N/A
+TC_HS_RAV_BV_03_I	N/A
+TC_HS_RAV_BV_04_I	N/A
+TC_HS_RAV_BV_05_I	N/A
+TC_HS_RAV_BV_06_I	N/A
diff --git a/bluez/repo/android/pts-iopt.txt b/bluez/repo/android/pts-iopt.txt
new file mode 100644
index 0000000..cd7ad32
--- /dev/null
+++ b/bluez/repo/android/pts-iopt.txt
@@ -0,0 +1,26 @@
+PTS test results for IOPT
+
+PTS version: 6.1
+Tested: 21-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name	Result	Notes
+-------------------------------------------------------------------------------
+TC_COD_BV_01_I	PASS	IUT must be discoverable
+TC_COD_BV_02_I	N/A	PTS issue#13473
+TC_SDSS_BV_02_I	PASS	Note: HDP sink record should be registered before test
+				run, e.g. register health app via HDPSample.apk
+TC_SDAS_BV_03_I	PASS	Note: HDP sink record should be registered before test
+				run, e.g. register health app via HDPSample.apk
+TC_SDR_BV_04_I	PASS	For every asked to check PTS bt profile:
+			haltest: bluetooth get_remote_service_record <PTS addr>
+				<profile uuid>
+			Note: 0000xxxx - acceptable 16bit uuid format
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-l2cap.txt b/bluez/repo/android/pts-l2cap.txt
new file mode 100644
index 0000000..81552dd
--- /dev/null
+++ b/bluez/repo/android/pts-l2cap.txt
@@ -0,0 +1,191 @@
+PTS test results for L2CAP
+
+PTS version: 6.1
+Tested: 28-May-2015
+Android version: 5.1
+Kernel version: 4.1
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+				For all tests daemon should be stopped then:
+					setprop ctl.start hciattach
+TC_COS_CED_BV_01_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_03_C	PASS	l2test -y -N 1 -P 4113 <bdaddr>
+TC_COS_CED_BV_04_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_05_C	PASS	l2test -r -P 4113
+TC_COS_CED_BV_07_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_08_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_09_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_10_C	N/A
+TC_COS_CED_BV_11_C	PASS	l2test -u -P 4113 <bdaddr>
+TC_COS_CED_BI_01_C	PASS
+TC_COS_CFD_BV_01_C	PASS	l2test -r -P 4113
+TC_COS_CFD_BV_02_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_03_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_08_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_09_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_10_C	N/A
+TC_COS_CFD_BV_11_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_12_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_13_C	N/A
+TC_COS_IEX_BV_01_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_IEX_BV_02_C	PASS
+TC_COS_ECH_BV_01_C	PASS
+TC_COS_ECH_BV_02_C	PASS	l2ping -c 1 <bdaddr>
+TC_COS_CFC_BV_01_C	PASS	l2test -y -N 1 -b 40 -V le_public -P 37 <braddr>
+TC_COS_CFC_BV_02_C	PASS	l2test -y -N 1 -b 1 -V le_public -P 37 <bdaddr>
+TC_COS_CFC_BV_03_C	PASS	l2test -u -V le_public -P 37 <bdaddr>
+TC_COS_CFC_BV_04_C	PASS	l2test -u -V le_public -P 37 <bdaddr>
+TC_COS_CFC_BV_05_C	PASS	l2test -u -V le_public <bdaddr>
+				l2test -u -V le_public <bdaddr>
+TC_CLS_CLR_BV_01_C	N/A
+TC_CLS_UCD_BV_01_C	PASS
+TC_CLS_UCD_BV_02_C	PASS	l2test -s -G -N 1 -P 4113 <bdaddr>
+TC_CLS_UCD_BV_03_C	PASS	l2test -s -E -G -N 1 -P 4113 <bdaddr>
+TC_EXF_BV_01_C		PASS
+TC_EXF_BV_02_C		PASS
+TC_EXF_BV_03_C		PASS
+TC_EXF_BV_04_C		N/A
+TC_EXF_BV_05_C		PASS
+TC_EXF_BV_06_C		N/A
+TC_CMC_BV_01_C		PASS	l2test -r -X ertm -P 4113
+TC_CMC_BV_02_C		PASS	l2test -r -X ertm -P 4113
+TC_CMC_BV_03_C		PASS	l2test -r -X ertm -P 4113
+TC_CMC_BV_04_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BV_05_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BV_06_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BV_07_C		PASS	l2test -r -X ertm -P 4113
+TC_CMC_BV_08_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BV_09_C		PASS	l2test -r -X basic -P 4113
+TC_CMC_BV_10_C		PASS	l2test -n -P 4113 <bdaddr>
+TC_CMC_BV_11_C		PASS	l2test -n -P 4113 <bdaddr>
+TC_CMC_BV_12_C		PASS	l2test -z -X ertm <bdaddr>
+TC_CMC_BV_13_C		PASS	l2test -z -X streaming <bdaddr>
+TC_CMC_BV_14_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BV_15_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BI_01_C		PASS	l2test -r -X ertm -P 4113
+TC_CMC_BI_02_C		PASS	l2test -r -X ertm -P 4113
+TC_CMC_BI_03_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BI_04_C		PASS	l2test -r -X streaming -P 4113
+TC_CMC_BI_05_C		PASS	l2test -r -X basic -P 4113
+TC_CMC_BI_06_C		PASS	l2test -r -X basic -P 4113
+TC_FOC_BV_01_C		PASS	l2test -r -X ertm -P 4113 -F 0
+TC_FOC_BV_02_C		PASS	l2test -r -X ertm -P 4113 -F 0
+TC_FOC_BV_03_C		PASS	l2test -r -X ertm -P 4113 -F 0
+TC_OFS_BV_01_C		PASS	l2test -x -X ertm -P 4113 -F 0 -N 1
+TC_OFS_BV_02_C		PASS	l2test -r -X ertm -P 4113 -F 0
+TC_OFS_BV_03_C		PASS	l2test -x -X streaming -P 4113 -F 0 -N 1
+TC_OFS_BV_04_C		PASS	l2test -d -X streaming -P 4113 -F 0
+TC_OFS_BV_05_C		PASS	l2test -x -X ertm -P 4113 -N 1
+TC_OFS_BV_06_C		PASS	l2test -r -X ertm -P 4113
+TC_OFS_BV_07_C		PASS	l2test -x -X streaming -P 4113 -F 0 -N 1
+TC_OFS_BV_08_C		PASS	l2test -d -X streaming -P 4113
+TC_ERM_BV_01_C		PASS	l2test -x -X ertm -P 4113 -N 3 -Y 3
+TC_ERM_BV_02_C		PASS	l2test -r -X ertm -P 4113
+TC_ERM_BV_03_C		PASS	l2test -r -X ertm -P 4113
+TC_ERM_BV_05_C		PASS	l2test -x -X ertm -P 4113 -N 2 -Y 2
+TC_ERM_BV_06_C		PASS	l2test -x -X ertm -P 4113 -N 2 -Y 2
+TC_ERM_BV_07_C		PASS	l2test -r -H 1000 -K 10000 -X ertm -P 4113
+TC_ERM_BV_08_C		PASS	l2test -x -X ertm -P 4113 -N 1
+TC_ERM_BV_09_C		PASS	l2test -X ertm -P 4113
+TC_ERM_BV_10_C		PASS	l2test -x -X ertm -P 4113 -N 1
+TC_ERM_BV_11_C		PASS	l2test -x -X ertm -P 4113 -N 1 -Q 1
+TC_ERM_BV_12_C		PASS	l2test -x -X ertm -P 4113 -R -N 1 -Q 1
+TC_ERM_BV_13_C		PASS	l2test -x -X ertm -P 4113 -N 2
+TC_ERM_BV_14_C		PASS	l2test -x -X ertm -P 4113 -N 4
+TC_ERM_BV_15_C		PASS	l2test -x -X ertm -P 4113 -N 4
+TC_ERM_BV_16_C		N/A
+TC_ERM_BV_17_C		PASS	l2test -X ertm -P 4113
+TC_ERM_BV_18_C		PASS	l2test -x -X ertm -P 4113 -N 1
+TC_ERM_BV_19_C		PASS	l2test -x -X ertm -P 4113 -N 1
+TC_ERM_BV_20_C		PASS	l2test -x -X ertm -P 4113 -N 1
+TC_ERM_BV_21_C		PASS	l2test -x -X ertm -P 4113 -D 2000 -N 2
+TC_ERM_BV_22_C		PASS	l2test -r -H 1000 -K 10000 -X ertm -P 4113
+TC_ERM_BV_23_C		PASS	l2test -x -X ertm -P 4113 -N 2
+TC_ERM_BI_01_C		N/A
+TC_ERM_BI_02_C		PASS	l2test -X ertm -P 4113
+TC_ERM_BI_03_C		PASS	l2test -x -X ertm -P 4113 -N 2
+TC_ERM_BI_04_C		PASS	l2test -x -X ertm -P 4113 -N 2
+TC_ERM_BI_05_C		PASS	l2test -x -X ertm -P 4113 -N 2
+TC_STM_BV_01_C		PASS	l2test -x -X streaming -P 4113 -N 3 -Y 3
+TC_STM_BV_02_C		PASS	l2test -d -X streaming -P 4113
+TC_STM_BV_03_C		PASS	l2test -x -X streaming -P 4113 -N 2
+TC_STM_BV_11_C		N/A
+TC_STM_BV_12_C		N/A
+TC_STM_BV_13_C		N/A
+TC_FIX_BV_01_C		PASS	l2test -z -P 4113 <bdaddr>
+TC_FIX_BV_02_C		N/A
+TC_EWC_BV_01_C		N/A
+TC_EWC_BV_02_C		N/A
+TC_EWC_BV_03_C		N/A
+TC_LSC_BV_01_C		N/A
+TC_LSC_BV_02_C		N/A
+TC_LSC_BV_03_C		N/A
+TC_LSC_BI_04_C		N/A
+TC_LSC_BI_05_C		N/A
+TC_LSC_BV_06_C		N/A
+TC_LSC_BV_07_C		N/A
+TC_LSC_BV_08_C		N/A
+TC_LSC_BV_09_C		N/A
+TC_LSC_BI_10_C		N/A
+TC_LSC_BI_11_C		N/A
+TC_LSC_BV_12_C		N/A
+TC_CCH_BV_01_C		N/A
+TC_CCH_BV_02_C		N/A
+TC_CCH_BV_03_C		N/A
+TC_CCH_BV_04_C		N/A
+TC_ECF_BV_01_C		N/A
+TC_ECF_BV_02_C		N/A
+TC_ECF_BV_03_C		N/A
+TC_ECF_BV_04_C		N/A
+TC_ECF_BV_05_C		N/A
+TC_ECF_BV_06_C		N/A
+TC_ECF_BV_07_C		N/A
+TC_ECF_BV_08_C		N/A
+TC_LE_CPU_BV_01_C	PASS	btmgmt advertising on
+				l2test -r -V le_public -J 4
+TC_LE_CPU_BV_02_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_CPU_BI_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_CPU_BI_02_C	PASS	btmgmt advertising on
+				l2test -r -V le_public -J 4
+TC_LE_REJ_BI_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_REJ_BI_02_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_CFC_BV_01_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_02_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_03_C	PASS	l2test -x -N 1 -V le_public
+				hcitool lecc <braddr>
+				hcitool ledc <handle>
+TC_LE_CFC_BV_04_C	PASS	l2test -n -V le_public -P 241 <braddr>
+TC_LE_CFC_BV_05_C	PASS	l2test -r -V le_public -J 4
+				hcitool lecc <braddr>
+				hcitool ledc <handle>
+TC_LE_CFC_BV_06_C	PASS	l2test -s -N 10 -V le_public <braddr>
+TC_LE_CFC_BV_07_C	PASS	l2test -u -V le_public <braddr>
+TC_LE_CFC_BI_01_C	PASS	l2test -u -V le_public <bdaddr>
+TC_LE_CFC_BV_08_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_09_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_16_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_17_C	N/A
+TC_LE_CID_BV_01_C	PASS	PTS issue #12730
+				l2test -r -J 2
+				l2test -r -J 4 -V le_public
+				hcitool cc <braddr>
+				hcitool lecc --static <braddr>
+				l2test -s -N 1 -C 0 -e 5 -D 10000 <braddr>
+				l2test -s -N 1 -C 0 -D 10000 -g 10000
+					-V le_public <braddr>
+TC_LE_CID_BV_02_I	PASS	PTS issue #12730
+				l2test -r -J 2
+				l2test -r -J 4 -V le_public
+				l2test -w -N 1 -C 0 -D 5000 -g 10000
+				l2test -w -N 1 -C 0 -D 5000 -e 5 -g 10000
+					-V le_public
+				hcitool cc <braddr>
+				hcitool lecc --static <braddr>
diff --git a/bluez/repo/android/pts-map.txt b/bluez/repo/android/pts-map.txt
new file mode 100644
index 0000000..2be8db2
--- /dev/null
+++ b/bluez/repo/android/pts-map.txt
@@ -0,0 +1,95 @@
+PTS test results for MAP
+
+PTS version: 6.1
+Tested: 29-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_MCE_MSM_BV_01_I	N/A
+TC_MCE_MSM_BV_02_I	N/A
+TC_MCE_MSM_BV_03_I	N/A
+TC_MCE_MSM_BV_04_I	N/A
+TC_MCE_MSM_BV_13_I	N/A
+TC_MCE_MSM_BV_14_I	N/A
+TC_MCE_MNR_BV_01_I	N/A
+TC_MCE_MNR_BV_02_I	N/A
+TC_MCE_MMB_BV_01_I	N/A
+TC_MCE_MMB_BV_02_I	N/A
+TC_MCE_MMB_BV_03_I	N/A
+TC_MCE_MMB_BV_19_I	N/A
+TC_MCE_MMB_BV_04_I	N/A
+TC_MCE_MMB_BV_17_I	N/A
+TC_MCE_MMB_BV_06_I	N/A
+TC_MCE_MMB_BV_07_I	N/A
+TC_MCE_MMB_BV_08_I	N/A
+TC_MCE_MMD_BV_01_I	N/A
+TC_MCE_MMU_BV_01_I	N/A
+TC_MCE_MMN_BV_01_I	N/A
+TC_MCE_MMN_BV_03_I	N/A
+TC_MCE_MMI_BV_01_I	N/A
+TC_MCE_MFB_BV_01_I	N/A
+TC_MCE_MFB_BV_03_I	N/A
+TC_MCE_MFB_BV_04_I	N/A
+TC_MCE_BC_BV_02_I	N/A
+TC_MCE_BC_BV_04_I	N/A
+TC_MCE_CON_BV_01_I	N/A
+TC_MCE_CON_BV_02_I	N/A
+TC_MCE_ROB_BV_01_I	N/A
+TC_MCE_SRM_BV_03_I	N/A
+TC_MCE_SRM_BV_07_I	N/A
+TC_MCE_SRMP_BI_01_I	N/A
+TC_MCE_SRMP_BV_01_I	N/A
+TC_MCE_SRMP_BV_04_I	N/A
+TC_MCE_SRMP_BV_05_I	N/A
+TC_MCE_SRMP_BV_06_I	N/A
+TC_MSE_MSM_BV_05_I	PASS
+TC_MSE_MSM_BV_06_I	PASS
+TC_MSE_MSM_BV_07_I	PASS
+TC_MSE_MSM_BV_08_I	PASS
+TC_MSE_MSM_BV_09_I	N/A
+TC_MSE_MSM_BV_10_I	N/A
+TC_MSE_MSM_BV_11_I	N/A
+TC_MSE_MSM_BV_12_I	N/A
+TC_MSE_MNR_BV_03_I	PASS
+TC_MSE_MNR_BV_04_I	PASS
+TC_MSE_MMB_BV_09_I	PASS
+TC_MSE_MMB_BV_10_I	PASS
+TC_MSE_MMB_BV_11_I	PASS
+TC_MSE_MMB_BV_20_I	PASS
+TC_MSE_MMB_BV_12_I	N/A
+TC_MSE_MMB_BV_18_I	PASS
+TC_MSE_MMB_BV_13_I	PASS
+TC_MSE_MMB_BV_14_I	PASS
+TC_MSE_MMB_BV_15_I	PASS
+TC_MSE_MMB_BV_16_I	PASS
+TC_MSE_MMD_BV_02_I	PASS
+TC_MSE_MMU_BV_02_I	PASS
+TC_MSE_MMU_BV_03_I	PASS
+TC_MSE_MMN_BV_02_I	INC	JIRA #BA-380
+TC_MSE_MMN_BV_04_I	N/A
+TC_MSE_MMI_BV_02_I	N/A
+TC_MSE_MFB_BV_02_I	N/A
+TC_MSE_MFB_BV_05_I	N/A
+TC_MSE_BC_BV_01_I	N/A
+TC_MSE_BC_BV_03_I	N/A
+TC_MSE_CON_BV_01_I	N/A
+TC_MSE_CON_BV_02_I	N/A
+TC_MSE_ROB_BV_01_I	N/A
+TC_MSE_ROB_BV_02_I	N/A
+TC_MSE_SRM_BI_02_I	N/A
+TC_MSE_SRM_BI_03_I	N/A
+TC_MSE_SRM_BI_05_I	N/A
+TC_MSE_SRM_BV_04_I	N/A
+TC_MSE_SRM_BV_08_I	N/A
+TC_MSE_SRMP_BI_02_I	N/A
+TC_MSE_SRMP_BV_02_I	N/A
+TC_MSE_SRMP_BV_03_I	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-mcap.txt b/bluez/repo/android/pts-mcap.txt
new file mode 100644
index 0000000..8e224a1
--- /dev/null
+++ b/bluez/repo/android/pts-mcap.txt
@@ -0,0 +1,80 @@
+PTS test results for MCAP
+
+PTS version: 6.1
+Tested: 19-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+Note: Test were done with ssp enabled and in most of the cases requires pairing
+	confirmation. This can be done easily by using 'btmgmt monitor'.
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_MCAP_CE_BV_01_C	PASS	mcaptest -C 4099 -D 4101 -f 2 -dc <PTS addr>
+TC_MCAP_CE_BV_02_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CE_BV_03_C	PASS	mcaptest -C 4099 -D 4101 -f 2 -c <PTS addr>
+TC_MCAP_CE_BV_04_C	PASS	mcaptest -C 4099 -D 4101 -f 2 -d
+TC_MCAP_CM_ABT_BV_01_C	N/A
+TC_MCAP_CM_ABT_BV_02_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CM_ABT_BV_03_C	N/A
+TC_MCAP_CM_DEL_BV_01_C	N/A
+TC_MCAP_CM_DEL_BV_02_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CM_DEL_BV_03_C	N/A
+TC_MCAP_CM_DEL_BV_04_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CM_DIS_BV_01_C	PASS	mcaptest -C 4099 -D 4101 -ab -e 2 -f 2
+TC_MCAP_CM_DIS_BV_02_C	PASS	mcaptest -C 4099 -D 4101
+TC_MCAP_CM_DIS_BV_03_C	PASS	mcaptest -C 4099 -D 4101 -n -f 2
+TC_MCAP_CM_DIS_BV_04_C	PASS	mcaptest -C 4099 -D 4101 -ab -e 2 -f 2
+TC_MCAP_CM_DIS_BV_05_C	PASS	mcaptest -C 4099 -D 4101
+TC_MCAP_CM_REC_BV_01_C	N/A
+TC_MCAP_CM_REC_BV_02_C	PASS	mcaptest -C 4099 -D 4101 -n -f 2
+TC_MCAP_CM_REC_BV_03_C	N/A
+TC_MCAP_CM_REC_BV_04_C	PASS	mcaptest -C 4099 -D 4101 -n -f 2
+TC_MCAP_CM_REC_BV_05_C	N/A
+TC_MCAP_CM_REC_BV_06_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CS_ERR_BI_01_C	N/A
+TC_MCAP_CS_ERR_BI_02_C	N/A
+TC_MCAP_CS_ERR_BI_03_C	N/A
+TC_MCAP_CS_ERR_BI_04_C	N/A
+TC_MCAP_CS_I_BV_01_I	N/A
+TC_MCAP_CS_I_BV_02_I	N/A
+TC_MCAP_CS_I_BV_03_C	N/A
+TC_MCAP_CS_I_BV_04_C	N/A
+TC_MCAP_CS_R_BV_01_I	N/A
+TC_MCAP_CS_R_BV_02_I	N/A
+TC_MCAP_CS_R_BV_03_C	N/A
+TC_MCAP_CS_T_BV_04_C	N/A
+TC_MCAP_ERR_BI_01_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_02_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_03_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_04_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_05_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_06_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_07_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_08_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_09_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_10_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_11_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_12_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_13_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_14_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_15_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_16_C	PASS	mcaptest -C 4099 -D 4101 -u -f 2
+TC_MCAP_ERR_BI_17_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_18_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_19_C	N/A
+TC_MCAP_ERR_BI_20_C	PASS	mcaptest -C 4099 -D 4101 -g -f 2
+TC_MCAP_INV_BI_01_C	PASS	mcaptest -C 4099 -D 4101 -dc <PTS addr>
+TC_MCAP_INV_BI_02_C	PASS	mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_INV_BI_03_C	PASS	mcaptest -C 4099 -D 4101 -d -f 2 -c <PTS addr>
+TC_MCAP_INV_BI_04_C	PASS	mcaptest -C 4099 -D 4101 -f 2 -c <PTS addr>
+TC_MCAP_INV_BI_05_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_INV_BI_06_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_INV_BI_07_C	PASS	mcaptest -C 4099 -D 4101 -f 2
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-mps.txt b/bluez/repo/android/pts-mps.txt
new file mode 100644
index 0000000..87a0e45
--- /dev/null
+++ b/bluez/repo/android/pts-mps.txt
@@ -0,0 +1,60 @@
+PTS test results for MPS
+
+PTS version: 6.1
+Tested: 20-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+Note: Do not use AOSP Music player. Use e.g. MortPlayer or Poweramp.
+For full tests conformance, "Touch sounds" on IUT should be disabled
+(Settings > Sound&notification > Other sounds)
+
+-------------------------------------------------------------------------------
+Test Name				Result	Notes
+-------------------------------------------------------------------------------
+TC_AG_PSE_HFPB_CTH_SD_BV_01_I		PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_01_I		PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_02_I		PASS
+TC_AG_SRC_HFAV_ACT_SD_BV_03_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_01_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_02_I		N/A
+TC_AG_SRC_HFAV_CLH_SD_BV_03_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_04_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_05_I		PASS
+TC_AG_SRC_HFAV_CLH_SD_BV_06_I		PASS	PTS issue #13466
+TC_AVP_CTH_SD_BI_01_I			PASS
+TC_AVP_CTH_SD_BI_02_I			PASS
+TC_HF_SNK_HFAV_ACT_SD_BV_01_I		N/A
+TC_HF_SNK_HFAV_ACT_SD_BV_02_I		N/A
+TC_HF_SNK_HFAV_ACT_SD_BV_03_I		N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_01_I		N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_02_I		N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_03_I		N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_04_I		N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_05_I		N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_06_I		N/A
+TC_HF_SNK_CT_HFAV_ACT_MD_BV_01_I	N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_01_I	N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_02_I	N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_03_I	N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I	N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I	N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I	N/A
+TC_SDP_CTH_SD_BV_01_I			PASS
+TC_SNK_PCE_AVO_ACT_SD_BV_01_I		N/A
+TC_SRC_PSE_AVO_ACT_SD_BV_01_I		PASS
+TC_SRC_TG_HFAV_ACT_MD_BV_01_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_01_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_02_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_03_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_04_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_05_I		PASS
+TC_SRC_TG_HFAV_CLH_MD_BV_06_I		PASS
+TC_PAIRING_HF_SNK_CT			N/A	Pairing helper for MD tests
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-opp.txt b/bluez/repo/android/pts-opp.txt
new file mode 100644
index 0000000..8496700
--- /dev/null
+++ b/bluez/repo/android/pts-opp.txt
@@ -0,0 +1,119 @@
+PTS test results for OPP
+
+PTS version: 6.1
+Tested: 20-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_CLIENT_BC_BV_02_I	N/A
+TC_CLIENT_BC_BV_04_I	N/A
+TC_CLIENT_BCE_BV_01_I   N/A
+TC_CLIENT_BCE_BV_03_I   N/A
+TC_CLIENT_BCE_BV_04_I   N/A
+TC_CLIENT_BCE_BV_05_I   N/A
+TC_CLIENT_BCE_BV_06_I   N/A
+TC_CLIENT_BCE_BV_07_I   N/A
+TC_CLIENT_BCP_BV_01_I   N/A
+TC_CLIENT_BCP_BV_02_I   N/A
+TC_CLIENT_BCP_BV_03_I   N/A
+TC_CLIENT_BCP_BV_04_I   N/A
+TC_CLIENT_BCP_BV_05_I   N/A
+TC_CLIENT_CON_BV_01_C	N/A
+TC_CLIENT_OPH_BI_01_C	PASS
+TC_CLIENT_OPH_BV_01_I	PASS
+TC_CLIENT_OPH_BV_02_I	N/A
+TC_CLIENT_OPH_BV_03_I	PASS
+TC_CLIENT_OPH_BV_04_I	N/A
+TC_CLIENT_OPH_BV_05_I	PASS
+TC_CLIENT_OPH_BV_07_I	N/A
+TC_CLIENT_OPH_BV_08_I	N/A
+TC_CLIENT_OPH_BV_09_I	N/A
+TC_CLIENT_OPH_BV_10_I	N/A
+TC_CLIENT_OPH_BV_11_I	N/A
+TC_CLIENT_OPH_BV_12_I	N/A
+TC_CLIENT_OPH_BV_13_I	N/A
+TC_CLIENT_OPH_BV_14_I	N/A
+TC_CLIENT_OPH_BV_15_I	N/A
+TC_CLIENT_OPH_BV_16_I	N/A
+TC_CLIENT_OPH_BV_17_I	N/A
+TC_CLIENT_OPH_BV_18_I	N/A
+TC_CLIENT_OPH_BV_19_I	PASS	Send file other than vCard
+TC_CLIENT_OPH_BV_20_I	PASS
+TC_CLIENT_OPH_BV_22_I	PASS	Send file greater than 2 MB
+TC_CLIENT_OPH_BV_23_I	PASS	Send two vCards in a single operation
+TC_CLIENT_OPH_BV_24_I	N/A
+TC_CLIENT_OPH_BV_25_I	N/A
+TC_CLIENT_OPH_BV_26_I	N/A
+TC_CLIENT_SRM_BV_01_C	N/A
+TC_CLIENT_SRM_BV_03_C	N/A
+TC_CLIENT_SRM_BV_05_C	N/A
+TC_CLIENT_SRM_BV_07_C	N/A
+TC_CLIENT_SRMP_BI_01_C	N/A
+TC_CLIENT_SRMP_BV_01_C	N/A
+TC_CLIENT_SRMP_BV_04_C	N/A
+TC_CLIENT_SRMP_BV_05_C	N/A
+TC_CLIENT_SRMP_BV_06_C	N/A
+TC_SERVER_BC_BV_01_I	N/A
+TC_SERVER_BC_BV_03_I	N/A
+TC_SERVER_BCE_BV_01_I	N/A
+TC_SERVER_BCE_BV_03_I	N/A
+TC_SERVER_BCE_BV_04_I	N/A
+TC_SERVER_BCE_BV_05_I	N/A
+TC_SERVER_BCE_BV_06_I	N/A
+TC_SERVER_BCE_BV_07_I	N/A
+TC_SERVER_BCP_BV_01_I	N/A
+TC_SERVER_BCP_BV_02_I	PASS
+TC_SERVER_BCP_BV_03_I	N/A
+TC_SERVER_BCP_BV_04_I	N/A
+TC_SERVER_BCP_BV_05_I	N/A
+TC_SERVER_CON_BV_02_C	N/A
+TC_SERVER_OPH_BV_01_I	PASS
+TC_SERVER_OPH_BV_02_I	PASS
+TC_SERVER_OPH_BV_03_I	PASS
+TC_SERVER_OPH_BV_04_I	PASS
+TC_SERVER_OPH_BV_05_I	PASS
+TC_SERVER_OPH_BV_07_I	N/A
+TC_SERVER_OPH_BV_08_I	N/A
+TC_SERVER_OPH_BV_09_I	N/A
+TC_SERVER_OPH_BV_10_I	PASS
+TC_SERVER_OPH_BV_11_I	N/A
+TC_SERVER_OPH_BV_12_I	N/A
+TC_SERVER_OPH_BV_13_I	N/A
+TC_SERVER_OPH_BV_14_I	PASS
+TC_SERVER_OPH_BV_15_I	N/A
+TC_SERVER_OPH_BV_16_I	N/A
+TC_SERVER_OPH_BV_17_I	N/A
+TC_SERVER_OPH_BV_18_I	PASS
+TC_SERVER_OPH_BV_19_I	PASS
+TC_SERVER_OPH_BV_21_I	N/A
+TC_SERVER_OPH_BV_22_I	PASS
+TC_SERVER_OPH_BV_23_I	PASS
+TC_SERVER_OPH_BV_24_I	N/A
+TC_SERVER_OPH_BV_25_I	N/A
+TC_SERVER_OPH_BV_26_I	N/A
+TC_SERVER_ROB_BV_01_I	N/A
+TC_SERVER_ROB_BV_02_I	N/A
+TC_SERVER_SRM_BI_03_I	N/A
+TC_SERVER_SRM_BI_05_I	N/A
+TC_SERVER_SRM_BV_04_I	N/A
+TC_SERVER_SRM_BV_08_I	N/A
+TC_SERVER_SRMP_BV_02_I	N/A
+TC_SERVER_SRMP_BV_03_I	N/A
+TC_CLIENT_OPH_BV_27_I	N/A
+TC_CLIENT_OPH_BV_34_I	PASS
+TC_SERVER_OPH_BV_27_I	N/A
+TC_SERVER_OPH_BV_30_I	N/A
+TC_SERVER_OPH_BV_31_I	N/A
+TC_SERVER_OPH_BV_32_I	N/A
+TC_SERVER_OPH_BV_33_I	N/A
+TC_SERVER_OPH_BV_34_I	PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-pan.txt b/bluez/repo/android/pts-pan.txt
new file mode 100644
index 0000000..350b84e
--- /dev/null
+++ b/bluez/repo/android/pts-pan.txt
@@ -0,0 +1,71 @@
+PTS test results for PAN
+
+PTS version: 6.1
+Tested: 11-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+--------------------------------------------------------------------------------
+Test Name					Result	Notes
+--------------------------------------------------------------------------------
+TC_BNEP_BROADCAST_0_BV_01_C			N/A
+TC_BNEP_BROADCAST_0_BV_02_C			N/A
+TC_BNEP_MULTICAST_0_BV_03_C			N/A
+TC_BNEP_MULTICAST_0_BV_04_C			N/A
+TC_BNEP_FORWARD_UNICAST_BV_05_C			N/A
+TC_BNEP_FORWARD_UNICAST_BV_06_C			N/A
+TC_BNEP_EXTENSION_0_BV_07_C_TESTER_1		N/A
+TC_BNEP_EXTENSION_0_BV_07_C_TESTER_2		N/A
+TC_BNEP_FORWARD_BV_08_C_TESTER_1		N/A
+TC_BNEP_FORWARD_BV_08_C_TESTER_2		N/A
+TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_1	N/A
+TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_2	N/A
+TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_3	N/A
+TC_BNEP_FILTER_BV_10_C_TESTER_1			N/A
+TC_BNEP_FILTER_BV_10_C_TESTER_2			N/A
+TC_BNEP_FILTER_BV_11_C_TESTER_1			N/A
+TC_BNEP_FILTER_BV_11_C_TESTER_2			N/A
+TC_BNEP_FILTER_BV_12_C_TESTER_1			N/A
+TC_BNEP_FILTER_BV_12_C_TESTER_2			N/A
+TC_BNEP_FILTER_BV_13_C_TESTER_1			N/A
+TC_BNEP_FILTER_BV_13_C_TESTER_2			N/A
+TC_BNEP_FILTER_BV_14_C_TESTER_1			N/A
+TC_BNEP_FILTER_BV_14_C_TESTER_2			N/A
+TC_BNEP_FILTER_BV_15_C_TESTER_1			N/A
+TC_BNEP_FILTER_BV_15_C_TESTER_2			N/A
+TC_BRIDGE_TX_BV_01_I				PASS
+TC_BRIDGE_RX_BV_02_I				PASS	To initiate general
+							ethernet use for e.g.
+							ping.
+TC_IPv4_AUTONET_BV_01_I				PASS	To initiate general
+							ethernet use for e.g.
+							ping.
+TC_IPv6_AUTONET_BV_02_I				N/A
+TC_IP_DHCP_BV_03_I				PASS
+TC_IP_LLMNR_BV_01_I				N/A
+TC_IP_LLMNR_BV_02_I				N/A
+TC_IP_DNS_BV_01_I				N/A
+TC_IP_APP_BV_03_I				N/A
+TC_IP_APP_BV_05_I				INC	PTS issue #13436
+							ip neighbour add
+							<PTS IP addr> lladdr
+							<PTS HW addr> dev bt-pan
+							Note: ARP record should
+							be add immediately after
+							establish of bt-pan,
+							because PTS treat other
+							than ICMP frames as
+							error.
+TC_MISC_ROLE_BV_01_C				N/A
+TC_MISC_ROLE_BV_02_C				N/A
+TC_MISC_UUID_BV_01_C				PASS
+TC_MISC_UUID_BV_02_C				PASS
+TC_SDP_NAP_BV_01_C				PASS
+TC_SDP_GN_BV_01_C				N/A
+TC_SDP_PANU_BV_01_C				N/A
+--------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-pbap.txt b/bluez/repo/android/pts-pbap.txt
new file mode 100644
index 0000000..0c597eb
--- /dev/null
+++ b/bluez/repo/android/pts-pbap.txt
@@ -0,0 +1,145 @@
+PTS test results for PBAP
+
+PTS version: 6.1
+Tested: 19-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_PCE_SSM_BV_01_C	N/A
+TC_PCE_SSM_BV_02_C	N/A
+TC_PCE_SSM_BV_06_C	N/A
+TC_PCE_SSM_BV_08_C	N/A
+TC_PCE_SSM_BI_01_C	N/A
+TC_PCE_SSM_BV_09_C	N/A
+TC_PCE_SSM_BV_10_C	N/A
+TC_PCE_PBD_BV_01_C	N/A
+TC_PCE_PBD_BV_04_C	N/A
+TC_PCE_PBD_BV_38_C	N/A
+TC_PCE_PBD_BV_29_C	N/A
+TC_PCE_PBD_BV_40_C	N/A
+TC_PCE_PBD_BV_41_C	N/A
+TC_PCE_PBD_BV_42_C	N/A
+TC_PCE_PBD_BV_43_C	N/A
+TC_PCE_PBD_BV_44_C	N/A
+TC_PCE_PBD_BV_45_C	N/A
+TC_PCE_PBD_BV_46_C	N/A
+TC_PCE_PBD_BV_47_C	N/A
+TC_PCE_PBD_BV_48_C	N/A
+TC_PCE_PBB_BV_01_C	N/A
+TC_PCE_PBB_BV_02_C	N/A
+TC_PCE_PBB_BV_03_C	N/A
+TC_PCE_PBB_BV_05_C	N/A
+TC_PCE_PBB_BV_39_C	N/A
+TC_PCE_PBB_BV_40_C	N/A
+TC_PCE_PBB_BV_41_C	N/A
+TC_PCE_PBB_BV_42_C	N/A
+TC_PCE_PBB_BV_33_C	N/A
+TC_PCE_PBB_BV_34_C	N/A
+TC_PCE_PBB_BV_35_C	N/A
+TC_PCE_PBB_BV_36_C	N/A
+TC_PCE_PBB_BV_43_C	N/A
+TC_PCE_PBB_BV_37_C	N/A
+TC_PCE_PBB_BV_38_C	N/A
+TC_PCE_PBF_BV_01_I	N/A
+TC_PCE_PBF_BV_02_I	N/A
+TC_PCE_PBF_BV_03_I	N/A
+TC_PCE_PDF_BV_01_I	N/A
+TC_PCE_PDF_BV_06_I	N/A
+TC_PSE_SSM_BV_03_C	PASS	Tester must accept obex request
+TC_PSE_SSM_BV_05_C	PASS
+TC_PSE_SSM_BV_07_C	PASS	Tester must accept obex request with
+				TSPX_auth_password set in PIXITs
+TC_PSE_SSM_BI_02_C	PASS
+TC_PSE_SSM_BI_03_C	N/A
+TC_PSE_SSM_BV_08_I	PASS	Tester must compare passkey on IUT and PTS
+TC_PSE_SSM_BV_11_C	N/A
+
+TC_PSE_PBD_BV_02_C	PASS	Tester must compare phone book size with the
+				value given by PTS
+TC_PSE_PBD_BV_03_C	PASS	Tester must compare phone book size with the
+				value given by PTS
+TC_PSE_PBD_BV_05_C	N/A
+TC_PSE_PBD_BI_01_C	PASS
+TC_PSE_PBD_BV_06_C	N/A
+TC_PSE_PBD_BV_07_C	N/A
+TC_PSE_PBD_BV_08_C	N/A
+TC_PSE_PBD_BV_09_C	N/A
+TC_PSE_PBD_BV_10_C	N/A
+TC_PSE_PBD_BV_17_C	PASS
+TC_PSE_PBD_BV_18_C	N/A
+TC_PSE_PBD_BV_19_C	N/A
+TC_PSE_PBD_BV_20_C	N/A
+TC_PSE_PBD_BV_21_C	N/A
+TC_PSE_PBD_BV_22_C	N/A
+TC_PSE_PBD_BV_23_C	N/A
+TC_PSE_PBD_BV_24_C	N/A
+TC_PSE_PBD_BV_25_C	N/A
+TC_PSE_PBD_BV_26_C	N/A
+TC_PSE_PBD_BV_27_C	N/A
+TC_PSE_PBD_BV_28_C	N/A
+TC_PSE_PBD_BV_29_C	N/A
+TC_PSE_PBD_BV_30_C	N/A
+TC_PSE_PBD_BV_31_C	N/A
+TC_PSE_PBD_BV_32_C	N/A
+TC_PSE_PBD_BV_33_C	N/A
+TC_PSE_PBD_BV_34_C	N/A
+TC_PSE_PBD_BV_35_C	N/A
+TC_PSE_PBD_BV_36_C	PASS
+TC_PSE_PBD_BV_37_C	N/A
+TC_PSE_PBB_BV_06_C	PASS
+TC_PSE_PBB_BV_07_C	PASS
+TC_PSE_PBB_BV_08_C	PASS	Tester must compare phone book size with the
+				value given by PTS
+TC_PSE_PBB_BV_09_C	PASS
+TC_PSE_PBB_BV_10_C	PASS	Tester must verify vcard content received by PTS
+TC_PSE_PBB_BV_11_C	PASS	Tester must verify number of new missed calls
+				with value given by PTS
+TC_PSE_PBB_BI_01_C	PASS
+TC_PSE_PBB_BI_07_C	PASS
+TC_PSE_PBB_BV_12_C	PASS
+TC_PSE_PBB_BV_13_C	N/A
+TC_PSE_PBB_BV_14_C	N/A
+TC_PSE_PBB_BV_15_C	N/A
+TC_PSE_PBB_BV_16_C	N/A
+TC_PSE_PBB_BV_17_C	N/A
+TC_PSE_PBB_BV_18_C	N/A
+TC_PSE_PBB_BV_19_C	N/A
+TC_PSE_PBB_BV_20_C	N/A
+TC_PSE_PBB_BV_21_C	N/A
+TC_PSE_PBB_BV_22_C	N/A
+TC_PSE_PBB_BV_23_C	N/A
+TC_PSE_PBB_BV_24_C	N/A
+TC_PSE_PBB_BV_25_C	N/A
+TC_PSE_PBB_BV_26_C	N/A
+TC_PSE_PBB_BV_27_C	N/A
+TC_PSE_PBB_BV_44_C	N/A
+TC_PSE_PBB_BV_45_C	N/A
+TC_PSE_PBB_BV_46_C	N/A
+TC_PSE_PBB_BV_28_C	N/A
+TC_PSE_PBB_BV_29_C	N/A
+TC_PSE_PBB_BV_30_C	N/A
+TC_PSE_PBB_BV_31_C	PASS	Requires entries in IUT's /och and /ich folders
+TC_PSE_PBB_BV_32_C	N/A
+TC_PSE_PBF_BV_01_I	PASS
+TC_PSE_PBF_BV_02_I	PASS	Tester must verify vcard content received by PTS
+TC_PSE_PBF_BV_03_I	PASS
+TC_PSE_PDF_BV_01_I	PASS	Tester must compare phone book size with the
+				value given by PTS
+TC_PSE_PDF_BV_06_I	PASS
+TC_PSE_BC_BV_03_I	N/A
+TC_PSE_CON_BV_02_I	N/A
+TC_PSE_ROB_BV_01_I	N/A
+TC_PSE_SRM_BI_03_I	N/A
+TC_PSE_SRM_BI_05_I	N/A
+TC_PSE_SRM_BV_08_I	N/A
+TC_PSE_SRMP_BI_02_I	N/A
+TC_PSE_SRMP_BV_02_I	N/A
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-rfcomm.txt b/bluez/repo/android/pts-rfcomm.txt
new file mode 100644
index 0000000..b1fce28
--- /dev/null
+++ b/bluez/repo/android/pts-rfcomm.txt
@@ -0,0 +1,38 @@
+PTS test results for RFCOMM
+
+PTS version: 6.1
+Tested: 12-May-2015
+Android version: 5.1
+Kernel version: 4.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_RFC_BV_01_C		PASS	rctest -n -P 1 <btaddr>
+TC_RFC_BV_02_C		PASS	rctest -r -P 1
+TC_RFC_BV_03_C		PASS	rctest -r -P 1
+TC_RFC_BV_04_C		PASS	rctest -r -P 1
+TC_RFC_BV_05_C		PASS	rctest -n -P 4 <btaddr>
+				Note: test requires IUT to connect on the given
+				channel. sdptool browse <btaddr> to check the
+				channel.
+TC_RFC_BV_06_C		PASS	rctest -r -P 1
+TC_RFC_BV_07_C		PASS	rctest -r -P 1
+TC_RFC_BV_08_C		PASS	rctest -r -P 1
+TC_RFC_BV_11_C		PASS	rctest -r -P 1
+TC_RFC_BV_13_C		PASS	rctest -r -P 1
+TC_RFC_BV_14_C		N/A
+TC_RFC_BV_15_C		PASS	rctest -r -P 1
+TC_RFC_BV_17_C		PASS	rctest -d -P 1
+TC_RFC_BV_19_C		PASS
+TC_RFC_BV_21_C		PASS	rctest -w -N 10 -P 1
+TC_RFC_BV_22_C		PASS	rctest -w -N 10 -P 1
+TC_RFC_BV_25_C		PASS	rctest -r -P 1
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-scpp.txt b/bluez/repo/android/pts-scpp.txt
new file mode 100644
index 0000000..3fab984
--- /dev/null
+++ b/bluez/repo/android/pts-scpp.txt
@@ -0,0 +1,24 @@
+PTS test results for ScPP
+
+PTS version: 6.1
+Tested: 12-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_SPDS_SC_BV_01_I	PASS
+TC_SPDC_SC_BV_01_I	PASS
+TC_SPDC_SC_BV_02_I	PASS
+TC_SPDC_SC_BV_03_I	PASS
+TC_SPWF_SC_BV_01_I	PASS
+TC_SPCF_SC_BV_01_I	PASS
+TC_SPNF_SC_BV_01_I	PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-sdp.txt b/bluez/repo/android/pts-sdp.txt
new file mode 100644
index 0000000..8a3e889
--- /dev/null
+++ b/bluez/repo/android/pts-sdp.txt
@@ -0,0 +1,77 @@
+PTS test results for SDP
+
+PTS version: 6.1
+Tested: 13-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+Note: from haltest:
+bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE
+						BT_SCAN_MODE_CONNECTABLE
+bluetooth enable
+socket listen BTSOCK_L2CAP BlueZ 0
+
+-------------------------------------------------------------------------------
+Test Name			Result	Notes
+-------------------------------------------------------------------------------
+TC_SERVER_BRW_BV_01_C		PASS
+TC_SERVER_BRW_BV_01_C		PASS
+TC_SERVER_SA_BI_01_C		PASS
+TC_SERVER_SA_BI_02_C		PASS
+TC_SERVER_SA_BI_03_C		PASS
+TC_SERVER_SA_BV_01_C		PASS
+TC_SERVER_SA_BV_03_C		PASS
+TC_SERVER_SA_BV_04_C		PASS
+TC_SERVER_SA_BV_05_C		PASS
+TC_SERVER_SA_BV_06_C		PASS
+TC_SERVER_SA_BV_07_C		PASS
+TC_SERVER_SA_BV_08_C		PASS
+TC_SERVER_SA_BV_09_C		PASS
+TC_SERVER_SA_BV_10_C		PASS
+TC_SERVER_SA_BV_11_C		PASS
+TC_SERVER_SA_BV_12_C		PASS
+TC_SERVER_SA_BV_13_C		PASS
+TC_SERVER_SA_BV_14_C		PASS
+TC_SERVER_SA_BV_15_C		PASS
+TC_SERVER_SA_BV_16_C		PASS
+TC_SERVER_SA_BV_17_C		PASS
+TC_SERVER_SA_BV_18_C		PASS
+TC_SERVER_SA_BV_19_C		PASS
+TC_SERVER_SA_BV_20_C		PASS
+TC_SERVER_SA_BV_21_C		PASS
+TC_SERVER_SS_BI_01_C		PASS
+TC_SERVER_SS_BI_02_C		PASS
+TC_SERVER_SS_BV_01_C		PASS
+TC_SERVER_SS_BV_03_C		PASS
+TC_SERVER_SS_BV_04_C		PASS
+TC_SERVER_SSA_BI_01_C		PASS
+TC_SERVER_SSA_BI_02_C		PASS
+TC_SERVER_SSA_BV_01_C		PASS
+TC_SERVER_SSA_BV_02_C		PASS
+TC_SERVER_SSA_BV_03_C		PASS
+TC_SERVER_SSA_BV_04_C		PASS
+TC_SERVER_SSA_BV_06_C		PASS
+TC_SERVER_SSA_BV_07_C		PASS
+TC_SERVER_SSA_BV_08_C		PASS
+TC_SERVER_SSA_BV_09_C		PASS
+TC_SERVER_SSA_BV_10_C		PASS
+TC_SERVER_SSA_BV_11_C		PASS
+TC_SERVER_SSA_BV_12_C		PASS
+TC_SERVER_SSA_BV_13_C		PASS
+TC_SERVER_SSA_BV_14_C		PASS
+TC_SERVER_SSA_BV_15_C		PASS
+TC_SERVER_SSA_BV_16_C		PASS
+TC_SERVER_SSA_BV_17_C		PASS
+TC_SERVER_SSA_BV_18_C		PASS
+TC_SERVER_SSA_BV_19_C		PASS
+TC_SERVER_SSA_BV_20_C		PASS
+TC_SERVER_SSA_BV_21_C		PASS
+TC_SERVER_SSA_BV_22_C		PASS
+TC_SERVER_SSA_BV_23_C		PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-sm.txt b/bluez/repo/android/pts-sm.txt
new file mode 100644
index 0000000..1fad305
--- /dev/null
+++ b/bluez/repo/android/pts-sm.txt
@@ -0,0 +1,102 @@
+PTS test results for SM
+
+PTS version: 6.1
+Tested: 04-May-2015
+Android version: 5.1
+kernel version: 4.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_PROT_BV_01_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_PROT_BV_02_C		PASS	btmgmt advertising on
+				btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BV_01_C		PASS	btmgmt pairable off
+				btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BV_02_C		PASS	btmgmt advertising on
+TC_JW_BV_05_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BI_01_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BI_02_C		PASS	btmgmt pairable on
+TC_JW_BI_03_C		PASS	btmgmt pairable on
+				btmgmt advertising on
+TC_JW_BI_04_C		PASS	btmgmt pairable off
+				btmgmg pair -c 0x03 -t 0x01 <addr>
+TC_PKE_BV_01_C		PASS	btmgmt pairable off
+				btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: provide passkey to PTS
+TC_PKE_BV_02_C		PASS	btmgmt pairable off
+				btmgmt io-cap 0x04
+				Note: provide passkey
+TC_PKE_BV_04_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_PKE_BV_05_C		PASS	btmgmt io-cap 0x04
+				l2test -r -J4 -AES -V le_public
+TC_PKE_BI_01_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: Enter invalid passkey in PTS
+TC_PKE_BI_02_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: provide passkey
+TC_PKE_BI_03_C		PASS	btmgmt io-cap 0x04
+				btmgmt advertising on
+				Note: Enter invalid passkey in PTS
+TC_OOB_BV_01_C		N/A
+TC_OOB_BV_02_C		N/A
+TC_OOB_BV_03_C		N/A
+TC_OOB_BV_04_C		N/A
+TC_OOB_BV_05_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: Enter valid passkey in PTS
+TC_OOB_BV_06_C		PASS	btmgmt advertising on
+				Note: Enter valid passkey in PTS
+TC_OOB_BV_07_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_OOB_BV_08_C		PASS	btmgmt advertising on
+				Note: Accept pairing in btmgmt
+TC_OOB_BV_09_C		N/A
+TC_OOB_BV_10_C		N/A
+TC_OOB_BI_01_C		N/A
+TC_OOB_BI_02_C		N/A
+TC_EKS_BV_01_C		PASS	btmgmt pair -c 0x04 -t 0x01 <addr>
+				Note: Enter valid passkey in PTS
+TC_EKS_BV_02_C		PASS	btmgmt advertising on
+				Note: Accept pairing in btmgmt
+TC_EKS_BI_01_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_EKS_BI_02_C		PASS	btmgmt advertising on
+TC_SIGN_BV_01_C		INC	PTS issue #12305
+TC_SIGN_BV_03_C		PASS	haltest:
+				gattc register_client 1234
+				gattc listen 1 1
+				Note: IUT must be connectable and discoverable
+TC_SIGN_BI_01_C		PASS	haltest:
+				gattc register client 1234
+				gattc listen 1 1
+				Note: IUT must be connectable and discoverable
+TC_KDU_BV_01_C		PASS	btmgmt pairable on
+				btmgmt advertising on
+				btmgmt connectable on
+TC_KDU_BV_02_C		PASS	PTS issue #12302
+				Note: Can pass it with following instructions:
+				btmgmt privacy on
+				btmgmt advertising on
+				Check our random address (valid for 15 min)
+				Set PIXIT TSPX_bd_addr_iut to random address
+				Set PIXIT TSPX_peer_type to 01
+TC_KDU_BV_03_C		PASS	btmgmt pairable on
+				btmgmt advertising on
+TC_KDU_BV_04_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_KDU_BV_05_C		PASS	PTS issue #12302
+				Note: Can pass it with following instructions:
+				btmgmt privacy on
+				Check our random address (valid for 15 min)
+				Set PIXIT TSPX_bd_addr_iut to random address
+				Set PIXIT TSPX_peer_type to 01
+TC_KDU_BV_06_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_KDU_BV_07_C		PASS	btmgmt pairable on
+TC_SIP_BV_01_C		PASS	btmgmt advertising on
+TC_SIP_BV_02_C		PASS	btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_SIE_BV_01_C		PASS	btmgmt io-cap 0x03
+				btmgmt advertising on
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/pts-spp.txt b/bluez/repo/android/pts-spp.txt
new file mode 100644
index 0000000..0845825
--- /dev/null
+++ b/bluez/repo/android/pts-spp.txt
@@ -0,0 +1,22 @@
+PTS test results for SPP
+
+PTS version: 6.1
+Tested: 19-May-2015
+Android version: 5.1
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_DevA_APP_BV_01_C	PASS	haltest: socket connect <PTS addr>
+					BTSOCK_RFCOMM 00001101 0
+TC_DevB_APP_BV_02_C	PASS	haltest: socket listen BTSOCK_RFCOMM SerialPort
+					00001101
+				Note: IUT must be in connectable, discoverable
+					mode.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/android/sco-ipc-api.txt b/bluez/repo/android/sco-ipc-api.txt
new file mode 100644
index 0000000..27d5ef2
--- /dev/null
+++ b/bluez/repo/android/sco-ipc-api.txt
@@ -0,0 +1,37 @@
+Bluetooth SCO Audio Plugin
+==========================
+
+The SCO Audio Plugin communicate through abstract socket name
+"\0bluez_sco_socket".
+
+	.----SCO----.                             .--Android--.
+	|   Plugin  |                             |   Daemon  |
+	|           |          Command            |           |
+	|           | --------------------------> |           |
+	|           |                             |           |
+	|           | <-------------------------- |           |
+	|           |          Response           |           |
+	|           |                             |           |
+	|           |                             |           |
+	|           |                             |           |
+	'-----------'                             '-----------'
+
+
+	SCO HAL                               Daemon
+	----------------------------------------------------
+
+	call get_fd()                    --> Get SCO socket fd
+	return get_fd()                  <-- Return SCO socket fd and mtu
+
+SCO Audio Service (ID 0)
+========================
+
+	Opcode 0x00 - Error response
+
+		Response parameters: Status (1 octet)
+
+	Opcode 0x01 - Get SCO fd command
+
+		Command parameters: Remote address (6 octets)
+		Response parameters: MTU (2 octets)
+				     File descriptor (inline)
diff --git a/bluez/repo/android/sco-msg.h b/bluez/repo/android/sco-msg.h
new file mode 100644
index 0000000..d1b13d7
--- /dev/null
+++ b/bluez/repo/android/sco-msg.h
@@ -0,0 +1,40 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+static const char BLUEZ_SCO_SK_PATH[] = "\0bluez_sco_socket";
+
+#define SCO_SERVICE_ID			0
+
+#define SCO_STATUS_SUCCESS		IPC_STATUS_SUCCESS
+#define SCO_STATUS_FAILED		0x01
+
+#define SCO_OP_STATUS			IPC_OP_STATUS
+
+#define SCO_OP_GET_FD			0x01
+struct sco_cmd_get_fd {
+	uint8_t bdaddr[6];
+} __attribute__((packed));
+
+struct sco_rsp_get_fd {
+	uint16_t mtu;
+} __attribute__((packed));
diff --git a/bluez/repo/android/sco.c b/bluez/repo/android/sco.c
new file mode 100644
index 0000000..e8ac685
--- /dev/null
+++ b/bluez/repo/android/sco.c
@@ -0,0 +1,351 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <glib.h>
+
+#include "lib/bluetooth.h"
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+
+#include "sco.h"
+
+struct bt_sco {
+	int ref_count;
+
+	GIOChannel *server_io;
+
+	GIOChannel *io;
+	guint watch;
+
+	bdaddr_t local_addr;
+	bdaddr_t remote_addr;
+
+	bt_sco_confirm_func_t confirm_cb;
+	bt_sco_conn_func_t connect_cb;
+	bt_sco_disconn_func_t disconnect_cb;
+};
+
+/* We support only one sco for the moment */
+static bool sco_in_use = false;
+
+static void clear_remote_address(struct bt_sco *sco)
+{
+	memset(&sco->remote_addr, 0, sizeof(bdaddr_t));
+}
+
+static gboolean disconnect_watch(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct bt_sco *sco = user_data;
+
+	g_io_channel_shutdown(sco->io, TRUE, NULL);
+	g_io_channel_unref(sco->io);
+	sco->io = NULL;
+
+	DBG("");
+
+	sco->watch = 0;
+
+	if (sco->disconnect_cb)
+		sco->disconnect_cb(&sco->remote_addr);
+
+	clear_remote_address(sco);
+
+	return FALSE;
+}
+
+static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct bt_sco *sco = user_data;
+
+	DBG("");
+
+	/* Lets unref connecting io */
+	if (sco->io) {
+		g_io_channel_unref(sco->io);
+		sco->io = NULL;
+	}
+
+	if (err) {
+		error("sco: Audio connect failed (%s)", err->message);
+
+		/*
+		 * Connect_sco_cb is called only when connect_cb is in place
+		 * Therefore it is safe to call it
+		 */
+		sco->connect_cb(SCO_STATUS_ERROR, &sco->remote_addr);
+
+		clear_remote_address(sco);
+
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, TRUE);
+
+	sco->io = g_io_channel_ref(chan);
+	sco->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+							disconnect_watch, sco);
+
+	/* It is safe to call it here */
+	sco->connect_cb(SCO_STATUS_OK, &sco->remote_addr);
+}
+
+static void confirm_sco_cb(GIOChannel *chan, gpointer user_data)
+{
+	char address[18];
+	bdaddr_t bdaddr;
+	GError *err = NULL;
+	struct bt_sco *sco = user_data;
+	uint16_t voice_settings;
+
+	DBG("");
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_DEST_BDADDR, &bdaddr,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("sco: audio confirm failed (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	if (!sco->confirm_cb || !sco->connect_cb) {
+		error("sco: Connect and/or confirm callback not registered ");
+		goto drop;
+	}
+
+	/* Check if there is SCO */
+	if (sco->io) {
+		error("sco: SCO is in progress");
+		goto drop;
+	}
+
+	if (!sco->confirm_cb(&bdaddr, &voice_settings)) {
+		error("sco: Audio connection from %s rejected", address);
+		goto drop;
+	}
+
+	bacpy(&sco->remote_addr, &bdaddr);
+
+	DBG("Incoming SCO connection from %s, voice settings 0x%x", address,
+								voice_settings);
+
+	err = NULL;
+	bt_io_set(chan, &err, BT_IO_OPT_VOICE, voice_settings,
+							BT_IO_OPT_INVALID);
+	if (err) {
+		error("sco: Could not set voice settings (%s)", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	if (!bt_io_accept(chan, connect_sco_cb, sco, NULL, NULL)) {
+		error("sco: Failed to accept audio connection");
+		goto drop;
+	}
+
+	sco->io = g_io_channel_ref(chan);
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static bool sco_listen(struct bt_sco *sco)
+{
+	GError *err = NULL;
+
+	if (!sco)
+		return false;
+
+	sco->server_io = bt_io_listen(NULL, confirm_sco_cb, sco, NULL, &err,
+							BT_IO_OPT_SOURCE_BDADDR,
+							&sco->local_addr,
+							BT_IO_OPT_INVALID);
+	if (!sco->server_io) {
+		error("sco: Failed to listen on SCO: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	return true;
+}
+
+struct bt_sco *bt_sco_new(const bdaddr_t *local_bdaddr)
+{
+	struct bt_sco *sco;
+
+	if (!local_bdaddr)
+		return NULL;
+
+	/* For now we support only one SCO connection per time */
+	if (sco_in_use)
+		return NULL;
+
+	sco = new0(struct bt_sco, 1);
+	if (!sco)
+		return NULL;
+
+	bacpy(&sco->local_addr, local_bdaddr);
+
+	if (!sco_listen(sco)) {
+		free(sco);
+		return NULL;
+	}
+
+	sco_in_use  = true;
+
+	return bt_sco_ref(sco);
+}
+
+struct bt_sco *bt_sco_ref(struct bt_sco *sco)
+{
+	if (!sco)
+		return NULL;
+
+	__sync_fetch_and_add(&sco->ref_count, 1);
+
+	return sco;
+}
+
+static void sco_free(struct bt_sco *sco)
+{
+	if (sco->server_io) {
+		g_io_channel_shutdown(sco->server_io, TRUE, NULL);
+		g_io_channel_unref(sco->server_io);
+	}
+
+	if (sco->io) {
+		g_io_channel_shutdown(sco->io, TRUE, NULL);
+		g_io_channel_unref(sco->io);
+	}
+
+	g_free(sco);
+	sco_in_use  = false;
+}
+
+void bt_sco_unref(struct bt_sco *sco)
+{
+	DBG("");
+
+	if (!sco)
+		return;
+
+	if (__sync_sub_and_fetch(&sco->ref_count, 1))
+		return;
+
+	sco_free(sco);
+}
+
+bool bt_sco_connect(struct bt_sco *sco, const bdaddr_t *addr,
+							uint16_t voice_settings)
+{
+	GIOChannel *io;
+	GError *gerr = NULL;
+
+	DBG("");
+
+	if (!sco || !sco->connect_cb || !addr) {
+		error("sco: Incorrect parameters or missing connect_cb");
+		return false;
+	}
+
+	/* Check if we have connection in progress */
+	if (sco->io) {
+		error("sco: Connection already in progress");
+		return false;
+	}
+
+	io = bt_io_connect(connect_sco_cb, sco, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &sco->local_addr,
+				BT_IO_OPT_DEST_BDADDR, addr,
+				BT_IO_OPT_VOICE, voice_settings,
+				BT_IO_OPT_INVALID);
+
+	if (!io) {
+		error("sco: unable to connect audio: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	sco->io = io;
+
+	bacpy(&sco->remote_addr, addr);
+
+	return true;
+}
+
+void bt_sco_disconnect(struct bt_sco *sco)
+{
+	if (!sco)
+		return;
+
+	if (sco->io)
+		g_io_channel_shutdown(sco->io, TRUE, NULL);
+}
+
+bool bt_sco_get_fd_and_mtu(struct bt_sco *sco, int *fd, uint16_t *mtu)
+{
+	GError *err;
+
+	if (!sco->io || !fd || !mtu)
+		return false;
+
+	err = NULL;
+	if (!bt_io_get(sco->io, &err, BT_IO_OPT_MTU, mtu, BT_IO_OPT_INVALID)) {
+			error("Unable to get MTU: %s\n", err->message);
+			g_clear_error(&err);
+			return false;
+		}
+
+	*fd = g_io_channel_unix_get_fd(sco->io);
+
+	return true;
+}
+
+void bt_sco_set_confirm_cb(struct bt_sco *sco,
+					bt_sco_confirm_func_t func)
+{
+	sco->confirm_cb = func;
+}
+
+void bt_sco_set_connect_cb(struct bt_sco *sco, bt_sco_conn_func_t func)
+{
+	sco->connect_cb = func;
+}
+
+void bt_sco_set_disconnect_cb(struct bt_sco *sco,
+						bt_sco_disconn_func_t func)
+{
+	sco->disconnect_cb = func;
+}
diff --git a/bluez/repo/android/sco.h b/bluez/repo/android/sco.h
new file mode 100644
index 0000000..4e1a2b3
--- /dev/null
+++ b/bluez/repo/android/sco.h
@@ -0,0 +1,51 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+enum sco_status {
+	SCO_STATUS_OK,
+	SCO_STATUS_ERROR,
+};
+
+struct bt_sco;
+
+struct bt_sco *bt_sco_new(const bdaddr_t *local_bdaddr);
+
+struct bt_sco *bt_sco_ref(struct bt_sco *sco);
+void bt_sco_unref(struct bt_sco *sco);
+
+bool bt_sco_connect(struct bt_sco *sco, const bdaddr_t *remote_addr,
+						uint16_t voice_settings);
+void bt_sco_disconnect(struct bt_sco *sco);
+bool bt_sco_get_fd_and_mtu(struct bt_sco *sco, int *fd, uint16_t *mtu);
+
+typedef bool (*bt_sco_confirm_func_t) (const bdaddr_t *remote_addr,
+						uint16_t *voice_settings);
+typedef void (*bt_sco_conn_func_t) (enum sco_status status,
+							const bdaddr_t *addr);
+typedef void (*bt_sco_disconn_func_t) (const bdaddr_t *addr);
+
+void bt_sco_set_confirm_cb(struct bt_sco *sco,
+					bt_sco_confirm_func_t func);
+void bt_sco_set_connect_cb(struct bt_sco *sco, bt_sco_conn_func_t func);
+void bt_sco_set_disconnect_cb(struct bt_sco *sco,
+						bt_sco_disconn_func_t func);
diff --git a/bluez/repo/android/socket-api.txt b/bluez/repo/android/socket-api.txt
new file mode 100644
index 0000000..9f622f9
--- /dev/null
+++ b/bluez/repo/android/socket-api.txt
@@ -0,0 +1,61 @@
+Android Socket protocol for Bluetooth
+=====================================
+
+Since Android switched from BlueZ (where sockets where nicely implemented) to
+Bluedroid user space stack there is a need to emulate bluetooth sockets.
+
+Android Bluetooth Socket Hardware Abstraction Layer (HAL) bt_sock.h has
+only 2 functions:
+
+static btsock_interface_t sock_if = {
+	sizeof(sock_if),
+	sock_listen,
+	sock_connect
+};
+
+with following parameters:
+
+sock_listen(btsock_type_t type, const char *service_name,
+		const uint8_t *uuid, int chan, int *sock_fd, int flags)
+sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
+		const uint8_t *uuid, int chan, int *sock_fd, int flags)
+
+socket type RFCOMM is only supported at the moment. uuid and channel used
+to decide where to connect.
+
+sockfd is used to return socket fd to Android framework. It is used to inform
+framework when remote device is connected.
+
+listen()
+========
+
+Listens on RFCOMM socket, socket channel is either found based on uuid or
+channel parameter used directly. Returns sock_fd to Android framework.
+
+Through this sock_fd channel number as (int) needs to be written right after
+listen() succeeds.
+
+When remote device is connected to this socket we shall send accept signal
+through sock_fd
+
+connect()
+=========
+
+Connects to remote device specified in bd_addr parameter. Socket channel is
+found by SDP search of remote device by supplied uuid. Returns sock_fd to
+Android framework.
+
+Through this sock_fd channel number as (int) needs to be written right after
+connects() succeeds.
+
+When remote device is connected to this socket we shall send connect signal
+through sock_fd
+
+The format of connect/accept signal is shown below:
+
+struct hal_sock_connect_signal {
+	short   size;
+	uint8_t bdaddr[6];
+	int     channel;
+	int     status;
+} __attribute__((packed));
diff --git a/bluez/repo/android/socket.c b/bluez/repo/android/socket.c
new file mode 100644
index 0000000..15e1bfc
--- /dev/null
+++ b/bluez/repo/android/socket.c
@@ -0,0 +1,1322 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <glib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "btio/btio.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/sdpd.h"
+#include "src/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "socket.h"
+
+#define RFCOMM_CHANNEL_MAX 30
+
+#define OPP_DEFAULT_CHANNEL	9
+#define HSP_AG_DEFAULT_CHANNEL	12
+#define HFP_AG_DEFAULT_CHANNEL	13
+#define PBAP_DEFAULT_CHANNEL	15
+#define MAP_MAS_DEFAULT_CHANNEL	16
+
+#define SVC_HINT_OBEX 0x10
+
+/* Hardcoded MAP stuff needed for MAS SMS Instance.*/
+#define DEFAULT_MAS_INSTANCE	0x00
+
+#define MAP_MSG_TYPE_SMS_GSM	0x02
+#define MAP_MSG_TYPE_SMS_CDMA	0x04
+#define DEFAULT_MAS_MSG_TYPE	(MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA)
+
+static struct ipc *hal_ipc = NULL;
+struct rfcomm_sock {
+	int channel;	/* RFCOMM channel */
+	BtIOSecLevel sec_level;
+
+	/* for socket to BT */
+	int bt_sock;
+	guint bt_watch;
+
+	/* for socket to HAL */
+	int jv_sock;
+	guint jv_watch;
+
+	bdaddr_t dst;
+	uint32_t service_handle;
+
+	uint8_t *buf;
+	int buf_size;
+};
+
+struct rfcomm_channel {
+	bool reserved;
+	struct rfcomm_sock *rfsock;
+};
+
+static bdaddr_t adapter_addr;
+
+static uint8_t hal_mode = HAL_MODE_SOCKET_DEFAULT;
+
+static const uint8_t zero_uuid[16] = { 0 };
+
+/* Simple list of RFCOMM connected sockets */
+static GList *connections = NULL;
+
+static struct rfcomm_channel servers[RFCOMM_CHANNEL_MAX + 1];
+
+static uint32_t test_sdp_record_uuid16 = 0;
+static uint32_t test_sdp_record_uuid32 = 0;
+static uint32_t test_sdp_record_uuid128 = 0;
+
+static int rfsock_set_buffer(struct rfcomm_sock *rfsock)
+{
+	socklen_t len = sizeof(int);
+	int rcv, snd, size, err;
+
+	err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len);
+	if (err < 0) {
+		int err = -errno;
+		error("getsockopt(SO_RCVBUF): %s", strerror(-err));
+		return err;
+	}
+
+	err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_SNDBUF, &snd, &len);
+	if (err < 0) {
+		int err = -errno;
+		error("getsockopt(SO_SNDBUF): %s", strerror(-err));
+		return err;
+	}
+
+	size = MAX(rcv, snd);
+
+	DBG("Set buffer size %d", size);
+
+	rfsock->buf = g_malloc(size);
+	rfsock->buf_size = size;
+
+	return 0;
+}
+
+static void cleanup_rfsock(gpointer data)
+{
+	struct rfcomm_sock *rfsock = data;
+
+	DBG("rfsock %p bt_sock %d jv_sock %d", rfsock, rfsock->bt_sock,
+							rfsock->jv_sock);
+
+	if (rfsock->jv_sock >= 0)
+		if (close(rfsock->jv_sock) < 0)
+			error("close() fd %d failed: %s", rfsock->jv_sock,
+							strerror(errno));
+
+	if (rfsock->bt_sock >= 0)
+		if (close(rfsock->bt_sock) < 0)
+			error("close() fd %d: failed: %s", rfsock->bt_sock,
+							strerror(errno));
+
+	if (rfsock->bt_watch > 0)
+		if (!g_source_remove(rfsock->bt_watch))
+			error("bt_watch source was not found");
+
+	if (rfsock->jv_watch > 0)
+		if (!g_source_remove(rfsock->jv_watch))
+			error("stack_watch source was not found");
+
+	if (rfsock->service_handle)
+		bt_adapter_remove_record(rfsock->service_handle);
+
+	if (rfsock->buf)
+		g_free(rfsock->buf);
+
+	g_free(rfsock);
+}
+
+static struct rfcomm_sock *create_rfsock(int bt_sock, int *hal_sock)
+{
+	int fds[2] = {-1, -1};
+	struct rfcomm_sock *rfsock;
+
+	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+		error("socketpair(): %s", strerror(errno));
+		*hal_sock = -1;
+		return NULL;
+	}
+
+	rfsock = g_new0(struct rfcomm_sock, 1);
+	rfsock->jv_sock = fds[0];
+	*hal_sock = fds[1];
+	rfsock->bt_sock = bt_sock;
+
+	DBG("rfsock %p", rfsock);
+
+	if (bt_sock < 0)
+		return rfsock;
+
+	if (rfsock_set_buffer(rfsock) < 0) {
+		cleanup_rfsock(rfsock);
+		return NULL;
+	}
+
+	return rfsock;
+}
+
+static sdp_record_t *create_rfcomm_record(uint8_t chan, uuid_t *uuid,
+						const char *svc_name,
+						bool has_obex)
+{
+	sdp_list_t *svclass_id;
+	sdp_list_t *seq, *proto_seq, *pbg_seq;
+	sdp_list_t *proto[3];
+	uuid_t l2cap_uuid, rfcomm_uuid, obex_uuid, pbg_uuid;
+	sdp_data_t *channel;
+	sdp_record_t *record;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->handle =  sdp_next_handle();
+
+	svclass_id = sdp_list_append(NULL, uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	seq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &chan);
+	proto[1] = sdp_list_append(proto[1], channel);
+	seq = sdp_list_append(seq, proto[1]);
+
+	if (has_obex) {
+		sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+		proto[2] = sdp_list_append(NULL, &obex_uuid);
+		seq = sdp_list_append(seq, proto[2]);
+	}
+
+	proto_seq = sdp_list_append(NULL, seq);
+	sdp_set_access_protos(record, proto_seq);
+
+	sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP);
+	pbg_seq = sdp_list_append(NULL, &pbg_uuid);
+	sdp_set_browse_groups(record, pbg_seq);
+
+	if (svc_name)
+		sdp_set_info_attr(record, svc_name, NULL, NULL);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	if (has_obex)
+		sdp_list_free(proto[2], NULL);
+	sdp_list_free(seq, NULL);
+	sdp_list_free(proto_seq, NULL);
+	sdp_list_free(pbg_seq, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name)
+{
+	uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+	uint8_t dtd = SDP_UINT8;
+	uuid_t uuid;
+	sdp_list_t *seq;
+	sdp_profile_desc_t profile[1];
+	void *dtds[sizeof(formats)], *values[sizeof(formats)];
+	sdp_data_t *formats_list;
+	sdp_record_t *record;
+	size_t i;
+
+	sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID);
+
+	record = create_rfcomm_record(chan, &uuid, svc_name, true);
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+	profile[0].version = 0x0100;
+	seq = sdp_list_append(NULL, profile);
+	sdp_set_profile_descs(record, seq);
+
+	for (i = 0; i < sizeof(formats); i++) {
+		dtds[i] = &dtd;
+		values[i] = &formats[i];
+	}
+	formats_list = sdp_seq_alloc(dtds, values, sizeof(formats));
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, formats_list);
+
+	sdp_list_free(seq, NULL);
+
+	return record;
+}
+
+static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
+{
+	sdp_list_t *seq;
+	sdp_profile_desc_t profile[1];
+	uint8_t formats = 0x01;
+	sdp_record_t *record;
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, PBAP_PSE_SVCLASS_ID);
+
+	record = create_rfcomm_record(chan, &uuid, svc_name, true);
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+	profile[0].version = 0x0101;
+	seq = sdp_list_append(NULL, profile);
+	sdp_set_profile_descs(record, seq);
+
+	sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_REPOSITORIES, SDP_UINT8,
+								&formats);
+
+	sdp_list_free(seq, NULL);
+
+	return record;
+}
+
+static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name)
+{
+	sdp_list_t *seq;
+	sdp_profile_desc_t profile[1];
+	uint8_t minst, mtype;
+	sdp_record_t *record;
+	uuid_t uuid;
+	int cnt, ret;
+
+	switch (hal_mode) {
+	case HAL_MODE_SOCKET_DYNAMIC_MAP:
+		/*
+		 * Service name for MAP is passed as XXYYname
+		 * XX - instance
+		 * YY - message type
+		 */
+		ret = sscanf(svc_name, "%02hhx%02hhx%n", &minst, &mtype, &cnt);
+		if (ret != 2 || cnt != 4)
+			return NULL;
+
+		svc_name += 4;
+		break;
+	case HAL_MODE_SOCKET_DEFAULT:
+		minst = DEFAULT_MAS_INSTANCE;
+		mtype = DEFAULT_MAS_MSG_TYPE;
+		break;
+	default:
+		return NULL;
+	}
+
+	sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID);
+
+	record = create_rfcomm_record(chan, &uuid, svc_name, true);
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID);
+	profile[0].version = 0x0101;
+	seq = sdp_list_append(NULL, profile);
+	sdp_set_profile_descs(record, seq);
+
+	sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst);
+	sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8,
+									&mtype);
+
+	sdp_list_free(seq, NULL);
+
+	return record;
+}
+
+static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
+{
+	sdp_record_t *record;
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, SERIAL_PORT_SVCLASS_ID);
+
+	record = create_rfcomm_record(chan, &uuid, svc_name, false);
+	if (!record)
+		return NULL;
+
+	return record;
+}
+
+static sdp_record_t *create_app_record(uint8_t chan,
+						const uint8_t *app_uuid,
+						const char *svc_name)
+{
+	sdp_record_t *record;
+	uuid_t uuid;
+
+	sdp_uuid128_create(&uuid, app_uuid);
+	sdp_uuid128_to_uuid(&uuid);
+
+	record = create_rfcomm_record(chan, &uuid, svc_name, false);
+	if (!record)
+		return NULL;
+
+	return record;
+}
+
+static const struct profile_info {
+	uint8_t		uuid[16];
+	uint8_t		channel;
+	uint8_t		svc_hint;
+	BtIOSecLevel	sec_level;
+	sdp_record_t *	(*create_record)(uint8_t chan, const char *svc_name);
+} profiles[] = {
+	{
+		.uuid = {
+			0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+		},
+		.channel = HSP_AG_DEFAULT_CHANNEL,
+		.svc_hint = 0,
+		.sec_level = BT_IO_SEC_MEDIUM,
+		.create_record = NULL
+	}, {
+		.uuid = {
+			0x00, 0x00, 0x11, 0x1F, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+		},
+		.channel = HFP_AG_DEFAULT_CHANNEL,
+		.svc_hint = 0,
+		.sec_level = BT_IO_SEC_MEDIUM,
+		.create_record = NULL
+	}, {
+		.uuid = {
+			0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+		},
+		.channel = PBAP_DEFAULT_CHANNEL,
+		.svc_hint = SVC_HINT_OBEX,
+		.sec_level = BT_IO_SEC_MEDIUM,
+		.create_record = create_pbap_record
+	}, {
+		.uuid = {
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+		  },
+		.channel = OPP_DEFAULT_CHANNEL,
+		.svc_hint = SVC_HINT_OBEX,
+		.sec_level = BT_IO_SEC_LOW,
+		.create_record = create_opp_record
+	}, {
+		.uuid = {
+			0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+		},
+		.channel = MAP_MAS_DEFAULT_CHANNEL,
+		.svc_hint = SVC_HINT_OBEX,
+		.sec_level = BT_IO_SEC_MEDIUM,
+		.create_record = create_mas_record
+	}, {
+		.uuid = {
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+		},
+		.channel = 0,
+		.svc_hint = 0,
+		.sec_level = BT_IO_SEC_MEDIUM,
+		.create_record = create_spp_record
+	},
+};
+
+static uint32_t sdp_service_register(uint8_t channel, const uint8_t *uuid,
+					const struct profile_info *profile,
+					const void *svc_name)
+{
+	sdp_record_t *record = NULL;
+	uint8_t svc_hint = 0;
+
+	if (profile && profile->create_record) {
+		record = profile->create_record(channel, svc_name);
+		svc_hint = profile->svc_hint;
+	} else if (uuid) {
+		record = create_app_record(channel, uuid, svc_name);
+	}
+
+	if (!record)
+		return 0;
+
+	if (bt_adapter_add_record(record, svc_hint) < 0) {
+		error("Failed to register on SDP record");
+		sdp_record_free(record);
+		return 0;
+	}
+
+	return record->handle;
+}
+
+static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd)
+{
+	ssize_t ret;
+	struct msghdr msg;
+	struct cmsghdr *cmsg;
+	struct iovec iv;
+	char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+	DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd);
+
+	if (sock_fd == -1 || send_fd == -1)
+		return -1;
+
+	memset(&msg, 0, sizeof(msg));
+	memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_RIGHTS;
+	cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
+
+	memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd));
+
+	iv.iov_base = (unsigned char *) buf;
+	iv.iov_len = len;
+
+	msg.msg_iov = &iv;
+	msg.msg_iovlen = 1;
+
+	ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL);
+	if (ret < 0) {
+		error("sendmsg(): sock_fd %d send_fd %d: %s",
+					sock_fd, send_fd, strerror(errno));
+		return ret;
+	}
+
+	return ret;
+}
+
+static const struct profile_info *get_profile_by_uuid(const uint8_t *uuid)
+{
+	unsigned int i;
+
+	for (i = 0; i < G_N_ELEMENTS(profiles); i++) {
+		if (!memcmp(profiles[i].uuid, uuid, 16))
+			return &profiles[i];
+	}
+
+	return NULL;
+}
+
+static int try_write_all(int fd, unsigned char *buf, int len)
+{
+	int sent = 0;
+
+	while (len > 0) {
+		int written;
+
+		written = write(fd, buf, len);
+		if (written < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+
+		if (!written)
+			return 0;
+
+		len -= written; buf += written; sent += written;
+	}
+
+	return sent;
+}
+
+static gboolean jv_sock_client_event_cb(GIOChannel *io, GIOCondition cond,
+								gpointer data)
+{
+	struct rfcomm_sock *rfsock = data;
+	int len, sent;
+
+	if (cond & G_IO_HUP) {
+		DBG("Socket %d hang up", g_io_channel_unix_get_fd(io));
+		goto fail;
+	}
+
+	if (cond & (G_IO_ERR | G_IO_NVAL)) {
+		error("Socket %d error", g_io_channel_unix_get_fd(io));
+		goto fail;
+	}
+
+	len = read(rfsock->jv_sock, rfsock->buf, rfsock->buf_size);
+	if (len <= 0) {
+		error("read(): %s", strerror(errno));
+		/* Read again */
+		return TRUE;
+	}
+
+	sent = try_write_all(rfsock->bt_sock, rfsock->buf, len);
+	if (sent < 0) {
+		error("write(): %s", strerror(errno));
+		goto fail;
+	}
+
+	return TRUE;
+fail:
+	DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond);
+
+	connections = g_list_remove(connections, rfsock);
+	cleanup_rfsock(rfsock);
+
+	return FALSE;
+}
+
+static gboolean bt_sock_event_cb(GIOChannel *io, GIOCondition cond,
+								gpointer data)
+{
+	struct rfcomm_sock *rfsock = data;
+	int len, sent;
+
+	if (cond & G_IO_HUP) {
+		DBG("Socket %d hang up", g_io_channel_unix_get_fd(io));
+		goto fail;
+	}
+
+	if (cond & (G_IO_ERR | G_IO_NVAL)) {
+		error("Socket %d error", g_io_channel_unix_get_fd(io));
+		goto fail;
+	}
+
+	len = read(rfsock->bt_sock, rfsock->buf, rfsock->buf_size);
+	if (len <= 0) {
+		error("read(): %s", strerror(errno));
+		/* Read again */
+		return TRUE;
+	}
+
+	sent = try_write_all(rfsock->jv_sock, rfsock->buf, len);
+	if (sent < 0) {
+		error("write(): %s", strerror(errno));
+		goto fail;
+	}
+
+	return TRUE;
+fail:
+	DBG("rfsock %p bt_sock %d cond %d", rfsock, rfsock->bt_sock, cond);
+
+	connections = g_list_remove(connections, rfsock);
+	cleanup_rfsock(rfsock);
+
+	return FALSE;
+}
+
+static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr,
+							int fd_accepted)
+{
+	struct hal_sock_connect_signal cmd;
+	int len;
+
+	DBG("");
+
+	cmd.size = sizeof(cmd);
+	bdaddr2android(bdaddr, cmd.bdaddr);
+	cmd.channel = rfsock->channel;
+	cmd.status = 0;
+
+	len = bt_sock_send_fd(rfsock->jv_sock, &cmd, sizeof(cmd), fd_accepted);
+	if (len != sizeof(cmd)) {
+		error("Error sending accept signal");
+		return false;
+	}
+
+	return true;
+}
+
+static gboolean jv_sock_server_event_cb(GIOChannel *io, GIOCondition cond,
+								gpointer data)
+{
+	struct rfcomm_sock *rfsock = data;
+
+	DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond);
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & (G_IO_ERR | G_IO_HUP)) {
+		servers[rfsock->channel].rfsock = NULL;
+		cleanup_rfsock(rfsock);
+	}
+
+	return FALSE;
+}
+
+static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct rfcomm_sock *rfsock = user_data;
+	struct rfcomm_sock *new_rfsock;
+	GIOChannel *jv_io;
+	GError *gerr = NULL;
+	bdaddr_t dst;
+	char address[18];
+	int new_sock;
+	int hal_sock;
+	guint id;
+	GIOCondition cond;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(io, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s on channel %d (rfsock %p)", address,
+						rfsock->channel, rfsock);
+
+	new_sock = g_io_channel_unix_get_fd(io);
+	new_rfsock = create_rfsock(new_sock, &hal_sock);
+	if (!new_rfsock) {
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	DBG("new rfsock %p bt_sock %d jv_sock %d hal_sock %d", new_rfsock,
+			new_rfsock->bt_sock, new_rfsock->jv_sock, hal_sock);
+
+	if (!sock_send_accept(rfsock, &dst, hal_sock)) {
+		cleanup_rfsock(new_rfsock);
+		return;
+	}
+
+	connections = g_list_append(connections, new_rfsock);
+
+	/* Handle events from Android */
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	jv_io = g_io_channel_unix_new(new_rfsock->jv_sock);
+	id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, new_rfsock);
+	g_io_channel_unref(jv_io);
+
+	new_rfsock->jv_watch = id;
+
+	/* Handle rfcomm events */
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	id = g_io_add_watch(io, cond, bt_sock_event_cb, new_rfsock);
+	g_io_channel_set_close_on_unref(io, FALSE);
+
+	new_rfsock->bt_watch = id;
+}
+
+static int find_free_channel(void)
+{
+	int ch;
+
+	/* channel 0 is reserver so we don't use it */
+	for (ch = 1; ch <= RFCOMM_CHANNEL_MAX; ch++) {
+		struct rfcomm_channel *srv = &servers[ch];
+
+		if (!srv->reserved && srv->rfsock == NULL)
+			return ch;
+	}
+
+	return 0;
+}
+
+static BtIOSecLevel get_sec_level(uint8_t flags)
+{
+	/*
+	 * HAL_SOCK_FLAG_AUTH should require MITM but in our case setting
+	 * security to BT_IO_SEC_HIGH would also require 16-digits PIN code
+	 * for pre-2.1 devices which is not what Android expects. For this
+	 * reason we ignore this flag to not break apps which use "secure"
+	 * sockets (have both auth and encrypt flags set, there is no public
+	 * API in Android which should provide proper high security socket).
+	 */
+	return flags & HAL_SOCK_FLAG_ENCRYPT ? BT_IO_SEC_MEDIUM :
+							BT_IO_SEC_LOW;
+}
+
+static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid,
+						uint8_t flags, int *hal_sock)
+{
+	const struct profile_info *profile;
+	struct rfcomm_sock *rfsock = NULL;
+	BtIOSecLevel sec_level;
+	GIOChannel *io, *jv_io;
+	GIOCondition cond;
+	GError *err = NULL;
+	guint id;
+	uuid_t uu;
+	char uuid_str[32];
+
+	sdp_uuid128_create(&uu, uuid);
+	sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str));
+
+	DBG("chan %d flags 0x%02x uuid %s name %s", chan, flags, uuid_str,
+									name);
+
+	if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) ||
+			(chan > RFCOMM_CHANNEL_MAX)) {
+		error("Invalid rfcomm listen params");
+		return HAL_STATUS_INVALID;
+	}
+
+	profile = get_profile_by_uuid(uuid);
+	if (!profile) {
+		sec_level = get_sec_level(flags);
+	} else {
+		if (!profile->create_record)
+			return HAL_STATUS_INVALID;
+
+		chan = profile->channel;
+		sec_level = profile->sec_level;
+	}
+
+	if (chan <= 0)
+		chan = find_free_channel();
+
+	if (!chan) {
+		error("No free channels");
+		return HAL_STATUS_BUSY;
+	}
+
+	if (servers[chan].rfsock != NULL) {
+		error("Channel already registered (%d)", chan);
+		return HAL_STATUS_BUSY;
+	}
+
+	DBG("chan %d sec_level %d", chan, sec_level);
+
+	rfsock = create_rfsock(-1, hal_sock);
+	if (!rfsock)
+		return HAL_STATUS_FAILED;
+
+	rfsock->channel = chan;
+
+	io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_CHANNEL, chan,
+				BT_IO_OPT_SEC_LEVEL, sec_level,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("Failed listen: %s", err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	rfsock->bt_sock = g_io_channel_unix_get_fd(io);
+
+	g_io_channel_set_close_on_unref(io, FALSE);
+	g_io_channel_unref(io);
+
+	/* Handle events from Android */
+	cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	jv_io = g_io_channel_unix_new(rfsock->jv_sock);
+	id = g_io_add_watch_full(jv_io, G_PRIORITY_HIGH, cond,
+					jv_sock_server_event_cb, rfsock,
+					NULL);
+	g_io_channel_unref(jv_io);
+
+	rfsock->jv_watch = id;
+
+	DBG("rfsock %p bt_sock %d jv_sock %d hal_sock %d", rfsock,
+								rfsock->bt_sock,
+								rfsock->jv_sock,
+								*hal_sock);
+
+	if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) {
+		error("Error sending RFCOMM channel");
+		goto failed;
+	}
+
+	rfsock->service_handle = sdp_service_register(chan, uuid, profile,
+									name);
+
+	servers[chan].rfsock = rfsock;
+
+	return HAL_STATUS_SUCCESS;
+
+failed:
+
+	cleanup_rfsock(rfsock);
+	close(*hal_sock);
+	return HAL_STATUS_FAILED;
+}
+
+static uint32_t add_test_record(uuid_t *uuid)
+{
+	sdp_record_t *record;
+	sdp_list_t *svclass_id;
+	sdp_list_t *seq, *pbg_seq, *proto_seq, *ap_seq;
+	sdp_list_t *proto, *proto1, *aproto;
+	uuid_t l2cap_uuid, pbg_uuid, ap_uuid;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return 0;
+
+	record->handle =  sdp_next_handle();
+
+	svclass_id = sdp_list_append(NULL, uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, &l2cap_uuid);
+	seq = sdp_list_append(NULL, proto);
+
+	proto_seq = sdp_list_append(NULL, seq);
+	sdp_set_access_protos(record, proto_seq);
+
+	sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP);
+	pbg_seq = sdp_list_append(NULL, &pbg_uuid);
+	sdp_set_browse_groups(record, pbg_seq);
+
+	/* Additional Protocol Descriptor List */
+	sdp_uuid16_create(&ap_uuid, L2CAP_UUID);
+	proto1 = sdp_list_append(NULL, &ap_uuid);
+	ap_seq = sdp_list_append(NULL, proto1);
+	aproto = sdp_list_append(NULL, ap_seq);
+	sdp_set_add_access_protos(record, aproto);
+
+	sdp_set_service_id(record, *uuid);
+	sdp_set_record_state(record, 0);
+	sdp_set_service_ttl(record, 0);
+	sdp_set_service_avail(record, 0);
+	sdp_set_url_attr(record, "http://www.bluez.org",
+				"http://www.bluez.org", "http://www.bluez.org");
+
+	sdp_list_free(proto, NULL);
+	sdp_list_free(seq, NULL);
+	sdp_list_free(proto_seq, NULL);
+	sdp_list_free(pbg_seq, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	if (bt_adapter_add_record(record, 0) < 0) {
+		sdp_record_free(record);
+		return 0;
+	}
+
+	return record->handle;
+}
+
+static void test_sdp_cleanup(void)
+{
+	if (test_sdp_record_uuid16) {
+		bt_adapter_remove_record(test_sdp_record_uuid16);
+		test_sdp_record_uuid16 = 0;
+	}
+
+	if (test_sdp_record_uuid32) {
+		bt_adapter_remove_record(test_sdp_record_uuid32);
+		test_sdp_record_uuid32 = 0;
+	}
+
+	if (test_sdp_record_uuid128) {
+		bt_adapter_remove_record(test_sdp_record_uuid128);
+		test_sdp_record_uuid128 = 0;
+	}
+}
+
+static void test_sdp_init(void)
+{
+	char uuid128[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+	uuid_t u;
+
+	sdp_uuid16_create(&u, 0xffff);
+	test_sdp_record_uuid16 = add_test_record(&u);
+
+	sdp_uuid32_create(&u, 0xffffffff);
+	test_sdp_record_uuid32 = add_test_record(&u);
+
+	sdp_uuid128_create(&u, uuid128);
+	test_sdp_record_uuid128 = add_test_record(&u);
+}
+
+static uint8_t l2cap_listen(int chan, const uint8_t *name, const uint8_t *uuid,
+						uint8_t flags, int *hal_sock)
+{
+	/* TODO be more strict here? */
+	if (strcmp("BlueZ", (const char *) name)) {
+		error("socket: Only SDP test supported on L2CAP");
+		return HAL_STATUS_UNSUPPORTED;
+	}
+
+	test_sdp_cleanup();
+	test_sdp_init();
+
+	*hal_sock = -1;
+
+	return HAL_STATUS_SUCCESS;
+}
+
+static void handle_listen(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_socket_listen *cmd = buf;
+	uint8_t status;
+	int hal_sock;
+
+	switch (cmd->type) {
+	case HAL_SOCK_RFCOMM:
+		status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid,
+							cmd->flags, &hal_sock);
+		break;
+	case HAL_SOCK_L2CAP:
+		status = l2cap_listen(cmd->channel, cmd->name, cmd->uuid,
+							cmd->flags, &hal_sock);
+		break;
+	case HAL_SOCK_SCO:
+		status = HAL_STATUS_UNSUPPORTED;
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		goto failed;
+
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+							0, NULL, hal_sock);
+	close(hal_sock);
+	return;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+									status);
+}
+
+static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr)
+{
+	struct hal_sock_connect_signal cmd;
+	int len;
+
+	DBG("");
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.size = sizeof(cmd);
+	bdaddr2android(bdaddr, cmd.bdaddr);
+	cmd.channel = rfsock->channel;
+	cmd.status = 0;
+
+	len = write(rfsock->jv_sock, &cmd, sizeof(cmd));
+	if (len < 0) {
+		error("%s", strerror(errno));
+		return false;
+	}
+
+	if (len != sizeof(cmd)) {
+		error("Error sending connect signal");
+		return false;
+	}
+
+	return true;
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct rfcomm_sock *rfsock = user_data;
+	bdaddr_t *dst = &rfsock->dst;
+	GIOChannel *jv_io;
+	char address[18];
+	guint id;
+	GIOCondition cond;
+
+	if (err) {
+		error("%s", err->message);
+		goto fail;
+	}
+
+	ba2str(dst, address);
+	DBG("Connected to %s on channel %d (rfsock %p)", address,
+						rfsock->channel, rfsock);
+
+	if (!sock_send_connect(rfsock, dst))
+		goto fail;
+
+	/* Handle events from Android */
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	jv_io = g_io_channel_unix_new(rfsock->jv_sock);
+	id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, rfsock);
+	g_io_channel_unref(jv_io);
+
+	rfsock->jv_watch = id;
+
+	/* Handle rfcomm events */
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	id = g_io_add_watch(io, cond, bt_sock_event_cb, rfsock);
+	g_io_channel_set_close_on_unref(io, FALSE);
+
+	rfsock->bt_watch = id;
+
+	return;
+fail:
+	connections = g_list_remove(connections, rfsock);
+	cleanup_rfsock(rfsock);
+}
+
+static bool do_rfcomm_connect(struct rfcomm_sock *rfsock, int chan)
+{
+	GIOChannel *io;
+	GError *gerr = NULL;
+
+	DBG("rfsock %p sec_level %d chan %d", rfsock, rfsock->sec_level, chan);
+
+	io = bt_io_connect(connect_cb, rfsock, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &rfsock->dst,
+				BT_IO_OPT_CHANNEL, chan,
+				BT_IO_OPT_SEC_LEVEL, rfsock->sec_level,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("Failed connect: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	g_io_channel_set_close_on_unref(io, FALSE);
+	g_io_channel_unref(io);
+
+	if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) {
+		error("Error sending RFCOMM channel");
+		return false;
+	}
+
+	rfsock->bt_sock = g_io_channel_unix_get_fd(io);
+	rfsock_set_buffer(rfsock);
+	rfsock->channel = chan;
+	connections = g_list_append(connections, rfsock);
+
+	return true;
+}
+
+static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct rfcomm_sock *rfsock = data;
+	sdp_list_t *list;
+	int chan;
+
+	DBG("");
+
+	if (err < 0) {
+		error("Unable to get SDP record: %s", strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		error("No SDP records found");
+		goto fail;
+	}
+
+	for (list = recs; list != NULL; list = list->next) {
+		sdp_record_t *rec = list->data;
+		sdp_list_t *protos;
+
+		if (sdp_get_access_protos(rec, &protos) < 0) {
+			error("Unable to get proto list");
+			goto fail;
+		}
+
+		chan = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
+									NULL);
+		sdp_list_free(protos, NULL);
+
+		if (chan)
+			break;
+	}
+
+	if (chan <= 0) {
+		error("Could not get RFCOMM channel %d", chan);
+		goto fail;
+	}
+
+	DBG("Got RFCOMM channel %d", chan);
+
+	if (do_rfcomm_connect(rfsock, chan))
+		return;
+fail:
+	cleanup_rfsock(rfsock);
+}
+
+static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan,
+					const uint8_t *uuid, uint8_t flags,
+					int *hal_sock)
+{
+	struct rfcomm_sock *rfsock;
+	char address[18];
+	uuid_t uu;
+	char uuid_str[32];
+
+	sdp_uuid128_create(&uu, uuid);
+	sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str));
+	ba2str(addr, address);
+
+	DBG("addr %s chan %d flags 0x%02x uuid %s", address, chan, flags,
+								uuid_str);
+
+	if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) ||
+						!bacmp(addr, BDADDR_ANY)) {
+		error("Invalid rfcomm connect params");
+		return HAL_STATUS_INVALID;
+	}
+
+	rfsock = create_rfsock(-1, hal_sock);
+	if (!rfsock)
+		return HAL_STATUS_FAILED;
+
+	DBG("rfsock %p jv_sock %d hal_sock %d", rfsock, rfsock->jv_sock,
+							*hal_sock);
+
+	rfsock->sec_level = get_sec_level(flags);
+
+	bacpy(&rfsock->dst, addr);
+
+	if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) {
+		if (!do_rfcomm_connect(rfsock, chan))
+			goto failed;
+	} else {
+
+		if (bt_search_service(&adapter_addr, &rfsock->dst, &uu,
+					sdp_search_cb, rfsock, NULL, 0) < 0) {
+			error("Failed to search SDP records");
+			goto failed;
+		}
+	}
+
+	return HAL_STATUS_SUCCESS;
+
+failed:
+	cleanup_rfsock(rfsock);
+	close(*hal_sock);
+	return HAL_STATUS_FAILED;
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+	const struct hal_cmd_socket_connect *cmd = buf;
+	bdaddr_t bdaddr;
+	uint8_t status;
+	int hal_sock;
+
+	DBG("");
+
+	android2bdaddr(cmd->bdaddr, &bdaddr);
+
+	switch (cmd->type) {
+	case HAL_SOCK_RFCOMM:
+		status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid,
+							cmd->flags, &hal_sock);
+		break;
+	case HAL_SOCK_SCO:
+	case HAL_SOCK_L2CAP:
+		status = HAL_STATUS_UNSUPPORTED;
+		break;
+	default:
+		status = HAL_STATUS_INVALID;
+		break;
+	}
+
+	if (status != HAL_STATUS_SUCCESS)
+		goto failed;
+
+	ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+							0, NULL, hal_sock);
+	close(hal_sock);
+	return;
+
+failed:
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+									status);
+
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_SOCKET_LISTEN */
+	{ handle_listen, false, sizeof(struct hal_cmd_socket_listen) },
+	/* HAL_OP_SOCKET_CONNECT */
+	{ handle_connect, false, sizeof(struct hal_cmd_socket_connect) },
+};
+
+void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+	size_t i;
+
+	DBG("");
+
+	hal_mode = mode;
+
+	/*
+	 * make sure channels assigned for profiles are reserved and not used
+	 * for app services
+	 */
+	for (i = 0; i < G_N_ELEMENTS(profiles); i++)
+		if (profiles[i].channel)
+			servers[profiles[i].channel].reserved = true;
+
+	bacpy(&adapter_addr, addr);
+
+	hal_ipc = ipc;
+	ipc_register(hal_ipc, HAL_SERVICE_ID_SOCKET, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+}
+
+void bt_socket_unregister(void)
+{
+	int ch;
+
+	DBG("");
+
+	test_sdp_cleanup();
+
+	g_list_free_full(connections, cleanup_rfsock);
+
+	for (ch = 0; ch <= RFCOMM_CHANNEL_MAX; ch++)
+		if (servers[ch].rfsock)
+			cleanup_rfsock(servers[ch].rfsock);
+
+	memset(servers, 0, sizeof(servers));
+
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_SOCKET);
+	hal_ipc = NULL;
+}
diff --git a/bluez/repo/android/socket.h b/bluez/repo/android/socket.h
new file mode 100644
index 0000000..b0e78c6
--- /dev/null
+++ b/bluez/repo/android/socket.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 hal_sock_connect_signal {
+	short   size;
+	uint8_t bdaddr[6];
+	int     channel;
+	int     status;
+} __attribute__((packed));
+
+void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_socket_unregister(void);
diff --git a/bluez/repo/android/system-emulator.c b/bluez/repo/android/system-emulator.c
new file mode 100644
index 0000000..61fb496
--- /dev/null
+++ b/bluez/repo/android/system-emulator.c
@@ -0,0 +1,258 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <signal.h>
+#include <string.h>
+#include <libgen.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+#include "src/shared/mainloop.h"
+
+static char exec_dir[PATH_MAX];
+
+static pid_t daemon_pid = -1;
+static pid_t snoop_pid = -1;
+
+static void run_valgrind(char *prg_name)
+{
+	char *prg_argv[6];
+	char *prg_envp[3];
+
+	prg_argv[0] = "/usr/bin/valgrind";
+	prg_argv[1] = "--leak-check=full";
+	prg_argv[2] = "--track-origins=yes";
+	prg_argv[3] = prg_name;
+	prg_argv[4] = "-d";
+	prg_argv[5] = NULL;
+
+	prg_envp[0] = "G_SLICE=always-malloc";
+	prg_envp[1] = "G_DEBUG=gc-friendly";
+	prg_envp[2] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void run_bluetoothd(char *prg_name)
+{
+	char *prg_argv[3];
+	char *prg_envp[1];
+
+	prg_argv[0] = prg_name;
+	prg_argv[1] = "-d";
+	prg_argv[2] = NULL;
+
+	prg_envp[0] = NULL;
+
+	execve(prg_argv[0], prg_argv, prg_envp);
+}
+
+static void ctl_start(void)
+{
+	char prg_name[PATH_MAX];
+	pid_t pid;
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+
+	printf("Starting %s\n", prg_name);
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return;
+	}
+
+	if (pid == 0) {
+		run_valgrind(prg_name);
+
+		/* Fallback to no valgrind if running with valgind failed */
+		run_bluetoothd(prg_name);
+		exit(0);
+	}
+
+	printf("New process %d created\n", pid);
+
+	daemon_pid = pid;
+}
+
+static void snoop_start(void)
+{
+	char prg_name[PATH_MAX];
+	char *prg_argv[3];
+	char *prg_envp[1];
+	pid_t pid;
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir,
+							"bluetoothd-snoop");
+
+	prg_argv[0] = prg_name;
+	prg_argv[1] = "/tmp/btsnoop_hci.log";
+	prg_argv[2] = NULL;
+
+	prg_envp[0] = NULL;
+
+	printf("Starting %s\n", prg_name);
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return;
+	}
+
+	if (pid == 0) {
+		execve(prg_argv[0], prg_argv, prg_envp);
+		exit(0);
+	}
+
+	printf("New process %d created\n", pid);
+
+	snoop_pid = pid;
+}
+
+static void snoop_stop(void)
+{
+	printf("Stoping %s/%s\n", exec_dir, "bluetoothd-snoop");
+
+	kill(snoop_pid, SIGTERM);
+}
+
+static void system_socket_callback(int fd, uint32_t events, void *user_data)
+{
+	char buf[4096];
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		return;
+
+	printf("Received %s\n", buf);
+
+	if (!strcmp(buf, "bluetooth.start=daemon")) {
+		if (daemon_pid > 0)
+			return;
+
+		ctl_start();
+	} else if (!strcmp(buf, "bluetooth.start=snoop")) {
+		if (snoop_pid > 0)
+			return;
+
+		snoop_start();
+	} else if (!strcmp(buf, "bluetooth.stop=snoop")) {
+		if (snoop_pid > 0)
+			snoop_stop();
+	}
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	case SIGCHLD:
+		while (1) {
+			pid_t pid;
+			int status;
+
+			pid = waitpid(WAIT_ANY, &status, WNOHANG);
+			if (pid < 0 || pid == 0)
+				break;
+
+			printf("Process %d terminated with status=%d\n",
+								pid, status);
+
+			if (pid == daemon_pid)
+				daemon_pid = -1;
+			else if (pid == snoop_pid)
+				snoop_pid = -1;
+		}
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+	sigset_t mask;
+	struct sockaddr_un addr;
+	int fd;
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+	sigaddset(&mask, SIGCHLD);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Android system emulator ver %s\n", VERSION);
+
+	snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+	fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to create system socket");
+		return EXIT_FAILURE;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind system socket");
+		close(fd);
+		return EXIT_FAILURE;
+	}
+
+	mainloop_add_fd(fd, EPOLLIN, system_socket_callback, NULL, NULL);
+
+	/* Make sure bluetoothd creates files with proper permissions */
+	umask(0177);
+
+	return mainloop_run();
+}
diff --git a/bluez/repo/android/system/._audio.h b/bluez/repo/android/system/._audio.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/android/system/._audio.h
Binary files differ
diff --git a/bluez/repo/android/system/audio.h b/bluez/repo/android/system/audio.h
new file mode 100644
index 0000000..d2da76d
--- /dev/null
+++ b/bluez/repo/android/system/audio.h
@@ -0,0 +1,1418 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_CORE_H
+#define ANDROID_AUDIO_CORE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+#define popcount __builtin_popcount
+
+/* The enums were moved here mostly from
+ * frameworks/base/include/media/AudioSystem.h
+ */
+
+/* device address used to refer to the standard remote submix */
+#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0"
+
+/* AudioFlinger and AudioPolicy services use I/O handles to identify audio sources and sinks */
+typedef int audio_io_handle_t;
+#define AUDIO_IO_HANDLE_NONE    0
+
+/* Audio stream types */
+typedef enum {
+    /* These values must kept in sync with
+     * frameworks/base/media/java/android/media/AudioSystem.java
+     */
+    AUDIO_STREAM_DEFAULT          = -1,
+    AUDIO_STREAM_MIN              = 0,
+    AUDIO_STREAM_VOICE_CALL       = 0,
+    AUDIO_STREAM_SYSTEM           = 1,
+    AUDIO_STREAM_RING             = 2,
+    AUDIO_STREAM_MUSIC            = 3,
+    AUDIO_STREAM_ALARM            = 4,
+    AUDIO_STREAM_NOTIFICATION     = 5,
+    AUDIO_STREAM_BLUETOOTH_SCO    = 6,
+    AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user
+                                        * and must be routed to speaker
+                                        */
+    AUDIO_STREAM_DTMF             = 8,
+    AUDIO_STREAM_TTS              = 9,
+
+    AUDIO_STREAM_CNT,
+    AUDIO_STREAM_MAX              = AUDIO_STREAM_CNT - 1,
+} audio_stream_type_t;
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/AudioAttributes.java
+ */
+typedef enum {
+    AUDIO_CONTENT_TYPE_UNKNOWN      = 0,
+    AUDIO_CONTENT_TYPE_SPEECH       = 1,
+    AUDIO_CONTENT_TYPE_MUSIC        = 2,
+    AUDIO_CONTENT_TYPE_MOVIE        = 3,
+    AUDIO_CONTENT_TYPE_SONIFICATION = 4,
+
+    AUDIO_CONTENT_TYPE_CNT,
+    AUDIO_CONTENT_TYPE_MAX          = AUDIO_CONTENT_TYPE_CNT - 1,
+} audio_content_type_t;
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/AudioAttributes.java
+ */
+typedef enum {
+    AUDIO_USAGE_UNKNOWN                            = 0,
+    AUDIO_USAGE_MEDIA                              = 1,
+    AUDIO_USAGE_VOICE_COMMUNICATION                = 2,
+    AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING     = 3,
+    AUDIO_USAGE_ALARM                              = 4,
+    AUDIO_USAGE_NOTIFICATION                       = 5,
+    AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE    = 6,
+    AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7,
+    AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8,
+    AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9,
+    AUDIO_USAGE_NOTIFICATION_EVENT                 = 10,
+    AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY           = 11,
+    AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE     = 12,
+    AUDIO_USAGE_ASSISTANCE_SONIFICATION            = 13,
+    AUDIO_USAGE_GAME                               = 14,
+
+    AUDIO_USAGE_CNT,
+    AUDIO_USAGE_MAX                                = AUDIO_USAGE_CNT - 1,
+} audio_usage_t;
+
+typedef uint32_t audio_flags_mask_t;
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/AudioAttributes.java
+ */
+enum {
+    AUDIO_FLAG_AUDIBILITY_ENFORCED = 0x1,
+    AUDIO_FLAG_SECURE              = 0x2,
+    AUDIO_FLAG_SCO                 = 0x4,
+    AUDIO_FLAG_BEACON              = 0x8,
+    AUDIO_FLAG_HW_AV_SYNC          = 0x10,
+    AUDIO_FLAG_HW_HOTWORD          = 0x20,
+};
+
+/* Do not change these values without updating their counterparts
+ * in frameworks/base/media/java/android/media/MediaRecorder.java,
+ * frameworks/av/services/audiopolicy/AudioPolicyService.cpp,
+ * and system/media/audio_effects/include/audio_effects/audio_effects_conf.h!
+ */
+typedef enum {
+    AUDIO_SOURCE_DEFAULT             = 0,
+    AUDIO_SOURCE_MIC                 = 1,
+    AUDIO_SOURCE_VOICE_UPLINK        = 2,
+    AUDIO_SOURCE_VOICE_DOWNLINK      = 3,
+    AUDIO_SOURCE_VOICE_CALL          = 4,
+    AUDIO_SOURCE_CAMCORDER           = 5,
+    AUDIO_SOURCE_VOICE_RECOGNITION   = 6,
+    AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
+    AUDIO_SOURCE_REMOTE_SUBMIX       = 8, /* Source for the mix to be presented remotely.      */
+                                          /* An example of remote presentation is Wifi Display */
+                                          /*  where a dongle attached to a TV can be used to   */
+                                          /*  play the mix captured by this audio source.      */
+    AUDIO_SOURCE_CNT,
+    AUDIO_SOURCE_MAX                 = AUDIO_SOURCE_CNT - 1,
+    AUDIO_SOURCE_HOTWORD             = 1999, /* A low-priority, preemptible audio source for
+                                                for background software hotword detection.
+                                                Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
+                                                Used only internally to the framework. Not exposed
+                                                at the audio HAL. */
+} audio_source_t;
+
+/* Audio attributes */
+#define AUDIO_ATTRIBUTES_TAGS_MAX_SIZE 256
+typedef struct {
+    audio_content_type_t content_type;
+    audio_usage_t        usage;
+    audio_source_t       source;
+    audio_flags_mask_t   flags;
+    char                 tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */
+} audio_attributes_t;
+
+/* special audio session values
+ * (XXX: should this be living in the audio effects land?)
+ */
+typedef enum {
+    /* session for effects attached to a particular output stream
+     * (value must be less than 0)
+     */
+    AUDIO_SESSION_OUTPUT_STAGE = -1,
+
+    /* session for effects applied to output mix. These effects can
+     * be moved by audio policy manager to another output stream
+     * (value must be 0)
+     */
+    AUDIO_SESSION_OUTPUT_MIX = 0,
+
+    /* application does not specify an explicit session ID to be used,
+     * and requests a new session ID to be allocated
+     * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE,
+     * after all uses have been updated from 0 to the appropriate symbol, and have been tested.
+     */
+    AUDIO_SESSION_ALLOCATE = 0,
+} audio_session_t;
+
+/* a unique ID allocated by AudioFlinger for use as a audio_io_handle_t or audio_session_t */
+typedef int audio_unique_id_t;
+
+#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE
+
+/* Audio sub formats (see enum audio_format). */
+
+/* PCM sub formats */
+typedef enum {
+    /* All of these are in native byte order */
+    AUDIO_FORMAT_PCM_SUB_16_BIT          = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */
+    AUDIO_FORMAT_PCM_SUB_8_BIT           = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */
+    AUDIO_FORMAT_PCM_SUB_32_BIT          = 0x3, /* PCM signed .31 fixed point */
+    AUDIO_FORMAT_PCM_SUB_8_24_BIT        = 0x4, /* PCM signed 7.24 fixed point */
+    AUDIO_FORMAT_PCM_SUB_FLOAT           = 0x5, /* PCM single-precision floating point */
+    AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED   = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */
+} audio_format_pcm_sub_fmt_t;
+
+/* The audio_format_*_sub_fmt_t declarations are not currently used */
+
+/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3
+ * frame header to specify bit rate, stereo mode, version...
+ */
+typedef enum {
+    AUDIO_FORMAT_MP3_SUB_NONE            = 0x0,
+} audio_format_mp3_sub_fmt_t;
+
+/* AMR NB/WB sub format field definition: specify frame block interleaving,
+ * bandwidth efficient or octet aligned, encoding mode for recording...
+ */
+typedef enum {
+    AUDIO_FORMAT_AMR_SUB_NONE            = 0x0,
+} audio_format_amr_sub_fmt_t;
+
+/* AAC sub format field definition: specify profile or bitrate for recording... */
+typedef enum {
+    AUDIO_FORMAT_AAC_SUB_MAIN            = 0x1,
+    AUDIO_FORMAT_AAC_SUB_LC              = 0x2,
+    AUDIO_FORMAT_AAC_SUB_SSR             = 0x4,
+    AUDIO_FORMAT_AAC_SUB_LTP             = 0x8,
+    AUDIO_FORMAT_AAC_SUB_HE_V1           = 0x10,
+    AUDIO_FORMAT_AAC_SUB_SCALABLE        = 0x20,
+    AUDIO_FORMAT_AAC_SUB_ERLC            = 0x40,
+    AUDIO_FORMAT_AAC_SUB_LD              = 0x80,
+    AUDIO_FORMAT_AAC_SUB_HE_V2           = 0x100,
+    AUDIO_FORMAT_AAC_SUB_ELD             = 0x200,
+} audio_format_aac_sub_fmt_t;
+
+/* VORBIS sub format field definition: specify quality for recording... */
+typedef enum {
+    AUDIO_FORMAT_VORBIS_SUB_NONE         = 0x0,
+} audio_format_vorbis_sub_fmt_t;
+
+/* Audio format consists of a main format field (upper 8 bits) and a sub format
+ * field (lower 24 bits).
+ *
+ * The main format indicates the main codec type. The sub format field
+ * indicates options and parameters for each format. The sub format is mainly
+ * used for record to indicate for instance the requested bitrate or profile.
+ * It can also be used for certain formats to give informations not present in
+ * the encoded audio stream (e.g. octet alignement for AMR).
+ */
+typedef enum {
+    AUDIO_FORMAT_INVALID             = 0xFFFFFFFFUL,
+    AUDIO_FORMAT_DEFAULT             = 0,
+    AUDIO_FORMAT_PCM                 = 0x00000000UL, /* DO NOT CHANGE */
+    AUDIO_FORMAT_MP3                 = 0x01000000UL,
+    AUDIO_FORMAT_AMR_NB              = 0x02000000UL,
+    AUDIO_FORMAT_AMR_WB              = 0x03000000UL,
+    AUDIO_FORMAT_AAC                 = 0x04000000UL,
+    AUDIO_FORMAT_HE_AAC_V1           = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/
+    AUDIO_FORMAT_HE_AAC_V2           = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/
+    AUDIO_FORMAT_VORBIS              = 0x07000000UL,
+    AUDIO_FORMAT_OPUS                = 0x08000000UL,
+    AUDIO_FORMAT_AC3                 = 0x09000000UL,
+    AUDIO_FORMAT_E_AC3               = 0x0A000000UL,
+    AUDIO_FORMAT_MAIN_MASK           = 0xFF000000UL,
+    AUDIO_FORMAT_SUB_MASK            = 0x00FFFFFFUL,
+
+    /* Aliases */
+    /* note != AudioFormat.ENCODING_PCM_16BIT */
+    AUDIO_FORMAT_PCM_16_BIT          = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_16_BIT),
+    /* note != AudioFormat.ENCODING_PCM_8BIT */
+    AUDIO_FORMAT_PCM_8_BIT           = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_8_BIT),
+    AUDIO_FORMAT_PCM_32_BIT          = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_32_BIT),
+    AUDIO_FORMAT_PCM_8_24_BIT        = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_8_24_BIT),
+    AUDIO_FORMAT_PCM_FLOAT           = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_FLOAT),
+    AUDIO_FORMAT_PCM_24_BIT_PACKED   = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED),
+    AUDIO_FORMAT_AAC_MAIN            = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_MAIN),
+    AUDIO_FORMAT_AAC_LC              = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_LC),
+    AUDIO_FORMAT_AAC_SSR             = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_SSR),
+    AUDIO_FORMAT_AAC_LTP             = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_LTP),
+    AUDIO_FORMAT_AAC_HE_V1           = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_HE_V1),
+    AUDIO_FORMAT_AAC_SCALABLE        = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_SCALABLE),
+    AUDIO_FORMAT_AAC_ERLC            = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_ERLC),
+    AUDIO_FORMAT_AAC_LD              = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_LD),
+    AUDIO_FORMAT_AAC_HE_V2           = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_HE_V2),
+    AUDIO_FORMAT_AAC_ELD             = (AUDIO_FORMAT_AAC |
+                                        AUDIO_FORMAT_AAC_SUB_ELD),
+} audio_format_t;
+
+/* For the channel mask for position assignment representation */
+enum {
+
+/* These can be a complete audio_channel_mask_t. */
+
+    AUDIO_CHANNEL_NONE                      = 0x0,
+    AUDIO_CHANNEL_INVALID                   = 0xC0000000,
+
+/* These can be the bits portion of an audio_channel_mask_t
+ * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION.
+ * Using these bits as a complete audio_channel_mask_t is deprecated.
+ */
+
+    /* output channels */
+    AUDIO_CHANNEL_OUT_FRONT_LEFT            = 0x1,
+    AUDIO_CHANNEL_OUT_FRONT_RIGHT           = 0x2,
+    AUDIO_CHANNEL_OUT_FRONT_CENTER          = 0x4,
+    AUDIO_CHANNEL_OUT_LOW_FREQUENCY         = 0x8,
+    AUDIO_CHANNEL_OUT_BACK_LEFT             = 0x10,
+    AUDIO_CHANNEL_OUT_BACK_RIGHT            = 0x20,
+    AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x40,
+    AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80,
+    AUDIO_CHANNEL_OUT_BACK_CENTER           = 0x100,
+    AUDIO_CHANNEL_OUT_SIDE_LEFT             = 0x200,
+    AUDIO_CHANNEL_OUT_SIDE_RIGHT            = 0x400,
+    AUDIO_CHANNEL_OUT_TOP_CENTER            = 0x800,
+    AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT        = 0x1000,
+    AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER      = 0x2000,
+    AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT       = 0x4000,
+    AUDIO_CHANNEL_OUT_TOP_BACK_LEFT         = 0x8000,
+    AUDIO_CHANNEL_OUT_TOP_BACK_CENTER       = 0x10000,
+    AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT        = 0x20000,
+
+/* TODO: should these be considered complete channel masks, or only bits? */
+
+    AUDIO_CHANNEL_OUT_MONO     = AUDIO_CHANNEL_OUT_FRONT_LEFT,
+    AUDIO_CHANNEL_OUT_STEREO   = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+    AUDIO_CHANNEL_OUT_QUAD     = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT),
+    AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD,
+    /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */
+    AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_SIDE_LEFT |
+                                  AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+    AUDIO_CHANNEL_OUT_5POINT1  = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT),
+    AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1,
+    /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */
+    AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_SIDE_LEFT |
+                                  AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+    // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1
+    AUDIO_CHANNEL_OUT_7POINT1  = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT |
+                                  AUDIO_CHANNEL_OUT_SIDE_LEFT |
+                                  AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+    AUDIO_CHANNEL_OUT_ALL      = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
+                                  AUDIO_CHANNEL_OUT_BACK_CENTER|
+                                  AUDIO_CHANNEL_OUT_SIDE_LEFT|
+                                  AUDIO_CHANNEL_OUT_SIDE_RIGHT|
+                                  AUDIO_CHANNEL_OUT_TOP_CENTER|
+                                  AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT|
+                                  AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER|
+                                  AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT|
+                                  AUDIO_CHANNEL_OUT_TOP_BACK_LEFT|
+                                  AUDIO_CHANNEL_OUT_TOP_BACK_CENTER|
+                                  AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+
+/* These are bits only, not complete values */
+
+    /* input channels */
+    AUDIO_CHANNEL_IN_LEFT            = 0x4,
+    AUDIO_CHANNEL_IN_RIGHT           = 0x8,
+    AUDIO_CHANNEL_IN_FRONT           = 0x10,
+    AUDIO_CHANNEL_IN_BACK            = 0x20,
+    AUDIO_CHANNEL_IN_LEFT_PROCESSED  = 0x40,
+    AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80,
+    AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100,
+    AUDIO_CHANNEL_IN_BACK_PROCESSED  = 0x200,
+    AUDIO_CHANNEL_IN_PRESSURE        = 0x400,
+    AUDIO_CHANNEL_IN_X_AXIS          = 0x800,
+    AUDIO_CHANNEL_IN_Y_AXIS          = 0x1000,
+    AUDIO_CHANNEL_IN_Z_AXIS          = 0x2000,
+    AUDIO_CHANNEL_IN_VOICE_UPLINK    = 0x4000,
+    AUDIO_CHANNEL_IN_VOICE_DNLINK    = 0x8000,
+
+/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */
+
+    AUDIO_CHANNEL_IN_MONO   = AUDIO_CHANNEL_IN_FRONT,
+    AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT),
+    AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK),
+    AUDIO_CHANNEL_IN_ALL    = (AUDIO_CHANNEL_IN_LEFT |
+                               AUDIO_CHANNEL_IN_RIGHT |
+                               AUDIO_CHANNEL_IN_FRONT |
+                               AUDIO_CHANNEL_IN_BACK|
+                               AUDIO_CHANNEL_IN_LEFT_PROCESSED |
+                               AUDIO_CHANNEL_IN_RIGHT_PROCESSED |
+                               AUDIO_CHANNEL_IN_FRONT_PROCESSED |
+                               AUDIO_CHANNEL_IN_BACK_PROCESSED|
+                               AUDIO_CHANNEL_IN_PRESSURE |
+                               AUDIO_CHANNEL_IN_X_AXIS |
+                               AUDIO_CHANNEL_IN_Y_AXIS |
+                               AUDIO_CHANNEL_IN_Z_AXIS |
+                               AUDIO_CHANNEL_IN_VOICE_UPLINK |
+                               AUDIO_CHANNEL_IN_VOICE_DNLINK),
+};
+
+/* A channel mask per se only defines the presence or absence of a channel, not the order.
+ * But see AUDIO_INTERLEAVE_* below for the platform convention of order.
+ *
+ * audio_channel_mask_t is an opaque type and its internal layout should not
+ * be assumed as it may change in the future.
+ * Instead, always use the functions declared in this header to examine.
+ *
+ * These are the current representations:
+ *
+ *   AUDIO_CHANNEL_REPRESENTATION_POSITION
+ *     is a channel mask representation for position assignment.
+ *     Each low-order bit corresponds to the spatial position of a transducer (output),
+ *     or interpretation of channel (input).
+ *     The user of a channel mask needs to know the context of whether it is for output or input.
+ *     The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion.
+ *     It is not permitted for no bits to be set.
+ *
+ *   AUDIO_CHANNEL_REPRESENTATION_INDEX
+ *     is a channel mask representation for index assignment.
+ *     Each low-order bit corresponds to a selected channel.
+ *     There is no platform interpretation of the various bits.
+ *     There is no concept of output or input.
+ *     It is not permitted for no bits to be set.
+ *
+ * All other representations are reserved for future use.
+ *
+ * Warning: current representation distinguishes between input and output, but this will not the be
+ * case in future revisions of the platform. Wherever there is an ambiguity between input and output
+ * that is currently resolved by checking the channel mask, the implementer should look for ways to
+ * fix it with additional information outside of the mask.
+ */
+typedef uint32_t audio_channel_mask_t;
+
+/* Maximum number of channels for all representations */
+#define AUDIO_CHANNEL_COUNT_MAX             30
+
+/* log(2) of maximum number of representations, not part of public API */
+#define AUDIO_CHANNEL_REPRESENTATION_LOG2   2
+
+/* Representations */
+typedef enum {
+    AUDIO_CHANNEL_REPRESENTATION_POSITION    = 0,    // must be zero for compatibility
+    // 1 is reserved for future use
+    AUDIO_CHANNEL_REPRESENTATION_INDEX       = 2,
+    // 3 is reserved for future use
+} audio_channel_representation_t;
+
+/* The return value is undefined if the channel mask is invalid. */
+static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel)
+{
+    return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1);
+}
+
+/* The return value is undefined if the channel mask is invalid. */
+static inline audio_channel_representation_t audio_channel_mask_get_representation(
+        audio_channel_mask_t channel)
+{
+    // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits
+    return (audio_channel_representation_t)
+            ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1));
+}
+
+/* Returns true if the channel mask is valid,
+ * or returns false for AUDIO_CHANNEL_NONE, AUDIO_CHANNEL_INVALID, and other invalid values.
+ * This function is unable to determine whether a channel mask for position assignment
+ * is invalid because an output mask has an invalid output bit set,
+ * or because an input mask has an invalid input bit set.
+ * All other APIs that take a channel mask assume that it is valid.
+ */
+static inline bool audio_channel_mask_is_valid(audio_channel_mask_t channel)
+{
+    uint32_t bits = audio_channel_mask_get_bits(channel);
+    audio_channel_representation_t representation = audio_channel_mask_get_representation(channel);
+    switch (representation) {
+    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+        break;
+    default:
+        bits = 0;
+        break;
+    }
+    return bits != 0;
+}
+
+/* Not part of public API */
+static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits(
+        audio_channel_representation_t representation, uint32_t bits)
+{
+    return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits);
+}
+
+/* Expresses the convention when stereo audio samples are stored interleaved
+ * in an array.  This should improve readability by allowing code to use
+ * symbolic indices instead of hard-coded [0] and [1].
+ *
+ * For multi-channel beyond stereo, the platform convention is that channels
+ * are interleaved in order from least significant channel mask bit
+ * to most significant channel mask bit, with unused bits skipped.
+ * Any exceptions to this convention will be noted at the appropriate API.
+ */
+enum {
+    AUDIO_INTERLEAVE_LEFT   = 0,
+    AUDIO_INTERLEAVE_RIGHT  = 1,
+};
+
+typedef enum {
+    AUDIO_MODE_INVALID          = -2,
+    AUDIO_MODE_CURRENT          = -1,
+    AUDIO_MODE_NORMAL           = 0,
+    AUDIO_MODE_RINGTONE         = 1,
+    AUDIO_MODE_IN_CALL          = 2,
+    AUDIO_MODE_IN_COMMUNICATION = 3,
+
+    AUDIO_MODE_CNT,
+    AUDIO_MODE_MAX              = AUDIO_MODE_CNT - 1,
+} audio_mode_t;
+
+/* This enum is deprecated */
+typedef enum {
+    AUDIO_IN_ACOUSTICS_NONE          = 0,
+    AUDIO_IN_ACOUSTICS_AGC_ENABLE    = 0x0001,
+    AUDIO_IN_ACOUSTICS_AGC_DISABLE   = 0,
+    AUDIO_IN_ACOUSTICS_NS_ENABLE     = 0x0002,
+    AUDIO_IN_ACOUSTICS_NS_DISABLE    = 0,
+    AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004,
+    AUDIO_IN_ACOUSTICS_TX_DISABLE    = 0,
+} audio_in_acoustics_t;
+
+enum {
+    AUDIO_DEVICE_NONE                          = 0x0,
+    /* reserved bits */
+    AUDIO_DEVICE_BIT_IN                        = 0x80000000,
+    AUDIO_DEVICE_BIT_DEFAULT                   = 0x40000000,
+    /* output devices */
+    AUDIO_DEVICE_OUT_EARPIECE                  = 0x1,
+    AUDIO_DEVICE_OUT_SPEAKER                   = 0x2,
+    AUDIO_DEVICE_OUT_WIRED_HEADSET             = 0x4,
+    AUDIO_DEVICE_OUT_WIRED_HEADPHONE           = 0x8,
+    AUDIO_DEVICE_OUT_BLUETOOTH_SCO             = 0x10,
+    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET     = 0x20,
+    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT      = 0x40,
+    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP            = 0x80,
+    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
+    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER    = 0x200,
+    AUDIO_DEVICE_OUT_AUX_DIGITAL               = 0x400,
+    AUDIO_DEVICE_OUT_HDMI                      = AUDIO_DEVICE_OUT_AUX_DIGITAL,
+    /* uses an analog connection (multiplexed over the USB connector pins for instance) */
+    AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET         = 0x800,
+    AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET         = 0x1000,
+    /* USB accessory mode: your Android device is a USB device and the dock is a USB host */
+    AUDIO_DEVICE_OUT_USB_ACCESSORY             = 0x2000,
+    /* USB host mode: your Android device is a USB host and the dock is a USB device */
+    AUDIO_DEVICE_OUT_USB_DEVICE                = 0x4000,
+    AUDIO_DEVICE_OUT_REMOTE_SUBMIX             = 0x8000,
+    /* Telephony voice TX path */
+    AUDIO_DEVICE_OUT_TELEPHONY_TX              = 0x10000,
+    /* Analog jack with line impedance detected */
+    AUDIO_DEVICE_OUT_LINE                      = 0x20000,
+    /* HDMI Audio Return Channel */
+    AUDIO_DEVICE_OUT_HDMI_ARC                  = 0x40000,
+    /* S/PDIF out */
+    AUDIO_DEVICE_OUT_SPDIF                     = 0x80000,
+    /* FM transmitter out */
+    AUDIO_DEVICE_OUT_FM                        = 0x100000,
+    /* Line out for av devices */
+    AUDIO_DEVICE_OUT_AUX_LINE                  = 0x200000,
+    /* limited-output speaker device for acoustic safety */
+    AUDIO_DEVICE_OUT_SPEAKER_SAFE              = 0x400000,
+    AUDIO_DEVICE_OUT_DEFAULT                   = AUDIO_DEVICE_BIT_DEFAULT,
+    AUDIO_DEVICE_OUT_ALL      = (AUDIO_DEVICE_OUT_EARPIECE |
+                                 AUDIO_DEVICE_OUT_SPEAKER |
+                                 AUDIO_DEVICE_OUT_WIRED_HEADSET |
+                                 AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
+                                 AUDIO_DEVICE_OUT_HDMI |
+                                 AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+                                 AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
+                                 AUDIO_DEVICE_OUT_USB_ACCESSORY |
+                                 AUDIO_DEVICE_OUT_USB_DEVICE |
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX |
+                                 AUDIO_DEVICE_OUT_TELEPHONY_TX |
+                                 AUDIO_DEVICE_OUT_LINE |
+                                 AUDIO_DEVICE_OUT_HDMI_ARC |
+                                 AUDIO_DEVICE_OUT_SPDIF |
+                                 AUDIO_DEVICE_OUT_FM |
+                                 AUDIO_DEVICE_OUT_AUX_LINE |
+                                 AUDIO_DEVICE_OUT_SPEAKER_SAFE |
+                                 AUDIO_DEVICE_OUT_DEFAULT),
+    AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
+    AUDIO_DEVICE_OUT_ALL_SCO  = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
+    AUDIO_DEVICE_OUT_ALL_USB  = (AUDIO_DEVICE_OUT_USB_ACCESSORY |
+                                 AUDIO_DEVICE_OUT_USB_DEVICE),
+
+    /* input devices */
+    AUDIO_DEVICE_IN_COMMUNICATION         = AUDIO_DEVICE_BIT_IN | 0x1,
+    AUDIO_DEVICE_IN_AMBIENT               = AUDIO_DEVICE_BIT_IN | 0x2,
+    AUDIO_DEVICE_IN_BUILTIN_MIC           = AUDIO_DEVICE_BIT_IN | 0x4,
+    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8,
+    AUDIO_DEVICE_IN_WIRED_HEADSET         = AUDIO_DEVICE_BIT_IN | 0x10,
+    AUDIO_DEVICE_IN_AUX_DIGITAL           = AUDIO_DEVICE_BIT_IN | 0x20,
+    AUDIO_DEVICE_IN_HDMI                  = AUDIO_DEVICE_IN_AUX_DIGITAL,
+    /* Telephony voice RX path */
+    AUDIO_DEVICE_IN_VOICE_CALL            = AUDIO_DEVICE_BIT_IN | 0x40,
+    AUDIO_DEVICE_IN_TELEPHONY_RX          = AUDIO_DEVICE_IN_VOICE_CALL,
+    AUDIO_DEVICE_IN_BACK_MIC              = AUDIO_DEVICE_BIT_IN | 0x80,
+    AUDIO_DEVICE_IN_REMOTE_SUBMIX         = AUDIO_DEVICE_BIT_IN | 0x100,
+    AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET     = AUDIO_DEVICE_BIT_IN | 0x200,
+    AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET     = AUDIO_DEVICE_BIT_IN | 0x400,
+    AUDIO_DEVICE_IN_USB_ACCESSORY         = AUDIO_DEVICE_BIT_IN | 0x800,
+    AUDIO_DEVICE_IN_USB_DEVICE            = AUDIO_DEVICE_BIT_IN | 0x1000,
+    /* FM tuner input */
+    AUDIO_DEVICE_IN_FM_TUNER              = AUDIO_DEVICE_BIT_IN | 0x2000,
+    /* TV tuner input */
+    AUDIO_DEVICE_IN_TV_TUNER              = AUDIO_DEVICE_BIT_IN | 0x4000,
+    /* Analog jack with line impedance detected */
+    AUDIO_DEVICE_IN_LINE                  = AUDIO_DEVICE_BIT_IN | 0x8000,
+    /* S/PDIF in */
+    AUDIO_DEVICE_IN_SPDIF                 = AUDIO_DEVICE_BIT_IN | 0x10000,
+    AUDIO_DEVICE_IN_BLUETOOTH_A2DP        = AUDIO_DEVICE_BIT_IN | 0x20000,
+    AUDIO_DEVICE_IN_LOOPBACK              = AUDIO_DEVICE_BIT_IN | 0x40000,
+    AUDIO_DEVICE_IN_DEFAULT               = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT,
+
+    AUDIO_DEVICE_IN_ALL     = (AUDIO_DEVICE_IN_COMMUNICATION |
+                               AUDIO_DEVICE_IN_AMBIENT |
+                               AUDIO_DEVICE_IN_BUILTIN_MIC |
+                               AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET |
+                               AUDIO_DEVICE_IN_WIRED_HEADSET |
+                               AUDIO_DEVICE_IN_HDMI |
+                               AUDIO_DEVICE_IN_TELEPHONY_RX |
+                               AUDIO_DEVICE_IN_BACK_MIC |
+                               AUDIO_DEVICE_IN_REMOTE_SUBMIX |
+                               AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET |
+                               AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET |
+                               AUDIO_DEVICE_IN_USB_ACCESSORY |
+                               AUDIO_DEVICE_IN_USB_DEVICE |
+                               AUDIO_DEVICE_IN_FM_TUNER |
+                               AUDIO_DEVICE_IN_TV_TUNER |
+                               AUDIO_DEVICE_IN_LINE |
+                               AUDIO_DEVICE_IN_SPDIF |
+                               AUDIO_DEVICE_IN_BLUETOOTH_A2DP |
+                               AUDIO_DEVICE_IN_LOOPBACK |
+                               AUDIO_DEVICE_IN_DEFAULT),
+    AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+    AUDIO_DEVICE_IN_ALL_USB  = (AUDIO_DEVICE_IN_USB_ACCESSORY |
+                                AUDIO_DEVICE_IN_USB_DEVICE),
+};
+
+typedef uint32_t audio_devices_t;
+
+/* the audio output flags serve two purposes:
+ * - when an AudioTrack is created they indicate a "wish" to be connected to an
+ * output stream with attributes corresponding to the specified flags
+ * - when present in an output profile descriptor listed for a particular audio
+ * hardware module, they indicate that an output stream can be opened that
+ * supports the attributes indicated by the flags.
+ * the audio policy manager will try to match the flags in the request
+ * (when getOuput() is called) to an available output stream.
+ */
+typedef enum {
+    AUDIO_OUTPUT_FLAG_NONE = 0x0,       // no attributes
+    AUDIO_OUTPUT_FLAG_DIRECT = 0x1,     // this output directly connects a track
+                                        // to one output stream: no software mixer
+    AUDIO_OUTPUT_FLAG_PRIMARY = 0x2,    // this output is the primary output of
+                                        // the device. It is unique and must be
+                                        // present. It is opened by default and
+                                        // receives routing, audio mode and volume
+                                        // controls related to voice calls.
+    AUDIO_OUTPUT_FLAG_FAST = 0x4,       // output supports "fast tracks",
+                                        // defined elsewhere
+    AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers
+    AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10,  // offload playback of compressed
+                                                // streams to hardware codec
+    AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20, // use non-blocking write
+    AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 0x40 // output uses a hardware A/V synchronization source
+} audio_output_flags_t;
+
+/* The audio input flags are analogous to audio output flags.
+ * Currently they are used only when an AudioRecord is created,
+ * to indicate a preference to be connected to an input stream with
+ * attributes corresponding to the specified flags.
+ */
+typedef enum {
+    AUDIO_INPUT_FLAG_NONE       = 0x0,  // no attributes
+    AUDIO_INPUT_FLAG_FAST       = 0x1,  // prefer an input that supports "fast tracks"
+    AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2,  // prefer an input that captures from hw hotword source
+} audio_input_flags_t;
+
+/* Additional information about compressed streams offloaded to
+ * hardware playback
+ * The version and size fields must be initialized by the caller by using
+ * one of the constants defined here.
+ */
+typedef struct {
+    uint16_t version;                   // version of the info structure
+    uint16_t size;                      // total size of the structure including version and size
+    uint32_t sample_rate;               // sample rate in Hz
+    audio_channel_mask_t channel_mask;  // channel mask
+    audio_format_t format;              // audio format
+    audio_stream_type_t stream_type;    // stream type
+    uint32_t bit_rate;                  // bit rate in bits per second
+    int64_t duration_us;                // duration in microseconds, -1 if unknown
+    bool has_video;                     // true if stream is tied to a video stream
+    bool is_streaming;                  // true if streaming, false if local playback
+} audio_offload_info_t;
+
+#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \
+            ((((maj) & 0xff) << 8) | ((min) & 0xff))
+
+#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1)
+#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1
+
+static const audio_offload_info_t AUDIO_INFO_INITIALIZER = {
+    version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
+    size: sizeof(audio_offload_info_t),
+    sample_rate: 0,
+    channel_mask: 0,
+    format: AUDIO_FORMAT_DEFAULT,
+    stream_type: AUDIO_STREAM_VOICE_CALL,
+    bit_rate: 0,
+    duration_us: 0,
+    has_video: false,
+    is_streaming: false
+};
+
+/* common audio stream configuration parameters
+ * You should memset() the entire structure to zero before use to
+ * ensure forward compatibility
+ */
+struct audio_config {
+    uint32_t sample_rate;
+    audio_channel_mask_t channel_mask;
+    audio_format_t  format;
+    audio_offload_info_t offload_info;
+    size_t frame_count;
+};
+typedef struct audio_config audio_config_t;
+
+static const audio_config_t AUDIO_CONFIG_INITIALIZER = {
+    sample_rate: 0,
+    channel_mask: AUDIO_CHANNEL_NONE,
+    format: AUDIO_FORMAT_DEFAULT,
+    offload_info: {
+        version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
+        size: sizeof(audio_offload_info_t),
+        sample_rate: 0,
+        channel_mask: 0,
+        format: AUDIO_FORMAT_DEFAULT,
+        stream_type: AUDIO_STREAM_VOICE_CALL,
+        bit_rate: 0,
+        duration_us: 0,
+        has_video: false,
+        is_streaming: false
+    },
+    frame_count: 0,
+};
+
+
+/* audio hw module handle functions or structures referencing a module */
+typedef int audio_module_handle_t;
+
+/******************************
+ *  Volume control
+ *****************************/
+
+/* If the audio hardware supports gain control on some audio paths,
+ * the platform can expose them in the audio_policy.conf file. The audio HAL
+ * will then implement gain control functions that will use the following data
+ * structures. */
+
+/* Type of gain control exposed by an audio port */
+#define AUDIO_GAIN_MODE_JOINT     0x1 /* supports joint channel gain control */
+#define AUDIO_GAIN_MODE_CHANNELS  0x2 /* supports separate channel gain control */
+#define AUDIO_GAIN_MODE_RAMP      0x4 /* supports gain ramps */
+
+typedef uint32_t audio_gain_mode_t;
+
+
+/* An audio_gain struct is a representation of a gain stage.
+ * A gain stage is always attached to an audio port. */
+struct audio_gain  {
+    audio_gain_mode_t    mode;          /* e.g. AUDIO_GAIN_MODE_JOINT */
+    audio_channel_mask_t channel_mask;  /* channels which gain an be controlled.
+                                           N/A if AUDIO_GAIN_MODE_CHANNELS is not supported */
+    int                  min_value;     /* minimum gain value in millibels */
+    int                  max_value;     /* maximum gain value in millibels */
+    int                  default_value; /* default gain value in millibels */
+    unsigned int         step_value;    /* gain step in millibels */
+    unsigned int         min_ramp_ms;   /* minimum ramp duration in ms */
+    unsigned int         max_ramp_ms;   /* maximum ramp duration in ms */
+};
+
+/* The gain configuration structure is used to get or set the gain values of a
+ * given port */
+struct audio_gain_config  {
+    int                  index;             /* index of the corresponding audio_gain in the
+                                               audio_port gains[] table */
+    audio_gain_mode_t    mode;              /* mode requested for this command */
+    audio_channel_mask_t channel_mask;      /* channels which gain value follows.
+                                               N/A in joint mode */
+    int                  values[sizeof(audio_channel_mask_t) * 8]; /* gain values in millibels
+                                               for each channel ordered from LSb to MSb in
+                                               channel mask. The number of values is 1 in joint
+                                               mode or popcount(channel_mask) */
+    unsigned int         ramp_duration_ms; /* ramp duration in ms */
+};
+
+/******************************
+ *  Routing control
+ *****************************/
+
+/* Types defined here are used to describe an audio source or sink at internal
+ * framework interfaces (audio policy, patch panel) or at the audio HAL.
+ * Sink and sources are grouped in a concept of “audio port” representing an
+ * audio end point at the edge of the system managed by the module exposing
+ * the interface. */
+
+/* Audio port role: either source or sink */
+typedef enum {
+    AUDIO_PORT_ROLE_NONE,
+    AUDIO_PORT_ROLE_SOURCE,
+    AUDIO_PORT_ROLE_SINK,
+} audio_port_role_t;
+
+/* Audio port type indicates if it is a session (e.g AudioTrack),
+ * a mix (e.g PlaybackThread output) or a physical device
+ * (e.g AUDIO_DEVICE_OUT_SPEAKER) */
+typedef enum {
+    AUDIO_PORT_TYPE_NONE,
+    AUDIO_PORT_TYPE_DEVICE,
+    AUDIO_PORT_TYPE_MIX,
+    AUDIO_PORT_TYPE_SESSION,
+} audio_port_type_t;
+
+/* Each port has a unique ID or handle allocated by policy manager */
+typedef int audio_port_handle_t;
+#define AUDIO_PORT_HANDLE_NONE 0
+
+
+/* maximum audio device address length */
+#define AUDIO_DEVICE_MAX_ADDRESS_LEN 32
+
+/* extension for audio port configuration structure when the audio port is a
+ * hardware device */
+struct audio_port_config_device_ext {
+    audio_module_handle_t hw_module;                /* module the device is attached to */
+    audio_devices_t       type;                     /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */
+    char                  address[AUDIO_DEVICE_MAX_ADDRESS_LEN]; /* device address. "" if N/A */
+};
+
+/* extension for audio port configuration structure when the audio port is a
+ * sub mix */
+struct audio_port_config_mix_ext {
+    audio_module_handle_t hw_module;    /* module the stream is attached to */
+    audio_io_handle_t handle;           /* I/O handle of the input/output stream */
+    union {
+        //TODO: change use case for output streams: use strategy and mixer attributes
+        audio_stream_type_t stream;
+        audio_source_t      source;
+    } usecase;
+};
+
+/* extension for audio port configuration structure when the audio port is an
+ * audio session */
+struct audio_port_config_session_ext {
+    audio_session_t   session; /* audio session */
+};
+
+/* Flags indicating which fields are to be considered in struct audio_port_config */
+#define AUDIO_PORT_CONFIG_SAMPLE_RATE  0x1
+#define AUDIO_PORT_CONFIG_CHANNEL_MASK 0x2
+#define AUDIO_PORT_CONFIG_FORMAT       0x4
+#define AUDIO_PORT_CONFIG_GAIN         0x8
+#define AUDIO_PORT_CONFIG_ALL (AUDIO_PORT_CONFIG_SAMPLE_RATE | \
+                               AUDIO_PORT_CONFIG_CHANNEL_MASK | \
+                               AUDIO_PORT_CONFIG_FORMAT | \
+                               AUDIO_PORT_CONFIG_GAIN)
+
+/* audio port configuration structure used to specify a particular configuration of
+ * an audio port */
+struct audio_port_config {
+    audio_port_handle_t      id;           /* port unique ID */
+    audio_port_role_t        role;         /* sink or source */
+    audio_port_type_t        type;         /* device, mix ... */
+    unsigned int             config_mask;  /* e.g AUDIO_PORT_CONFIG_ALL */
+    unsigned int             sample_rate;  /* sampling rate in Hz */
+    audio_channel_mask_t     channel_mask; /* channel mask if applicable */
+    audio_format_t           format;       /* format if applicable */
+    struct audio_gain_config gain;         /* gain to apply if applicable */
+    union {
+        struct audio_port_config_device_ext  device;  /* device specific info */
+        struct audio_port_config_mix_ext     mix;     /* mix specific info */
+        struct audio_port_config_session_ext session; /* session specific info */
+    } ext;
+};
+
+
+/* max number of sampling rates in audio port */
+#define AUDIO_PORT_MAX_SAMPLING_RATES 16
+/* max number of channel masks in audio port */
+#define AUDIO_PORT_MAX_CHANNEL_MASKS 16
+/* max number of audio formats in audio port */
+#define AUDIO_PORT_MAX_FORMATS 16
+/* max number of gain controls in audio port */
+#define AUDIO_PORT_MAX_GAINS 16
+
+/* extension for audio port structure when the audio port is a hardware device */
+struct audio_port_device_ext {
+    audio_module_handle_t hw_module;    /* module the device is attached to */
+    audio_devices_t       type;         /* device type (e.g AUDIO_DEVICE_OUT_SPEAKER) */
+    char                  address[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+};
+
+/* Latency class of the audio mix */
+typedef enum {
+    AUDIO_LATENCY_LOW,
+    AUDIO_LATENCY_NORMAL,
+} audio_mix_latency_class_t;
+
+/* extension for audio port structure when the audio port is a sub mix */
+struct audio_port_mix_ext {
+    audio_module_handle_t     hw_module;     /* module the stream is attached to */
+    audio_io_handle_t         handle;        /* I/O handle of the input.output stream */
+    audio_mix_latency_class_t latency_class; /* latency class */
+    // other attributes: routing strategies
+};
+
+/* extension for audio port structure when the audio port is an audio session */
+struct audio_port_session_ext {
+    audio_session_t   session; /* audio session */
+};
+
+
+struct audio_port {
+    audio_port_handle_t      id;                /* port unique ID */
+    audio_port_role_t        role;              /* sink or source */
+    audio_port_type_t        type;              /* device, mix ... */
+    unsigned int             num_sample_rates;  /* number of sampling rates in following array */
+    unsigned int             sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES];
+    unsigned int             num_channel_masks; /* number of channel masks in following array */
+    audio_channel_mask_t     channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS];
+    unsigned int             num_formats;       /* number of formats in following array */
+    audio_format_t           formats[AUDIO_PORT_MAX_FORMATS];
+    unsigned int             num_gains;         /* number of gains in following array */
+    struct audio_gain        gains[AUDIO_PORT_MAX_GAINS];
+    struct audio_port_config active_config;     /* current audio port configuration */
+    union {
+        struct audio_port_device_ext  device;
+        struct audio_port_mix_ext     mix;
+        struct audio_port_session_ext session;
+    } ext;
+};
+
+/* An audio patch represents a connection between one or more source ports and
+ * one or more sink ports. Patches are connected and disconnected by audio policy manager or by
+ * applications via framework APIs.
+ * Each patch is identified by a handle at the interface used to create that patch. For instance,
+ * when a patch is created by the audio HAL, the HAL allocates and returns a handle.
+ * This handle is unique to a given audio HAL hardware module.
+ * But the same patch receives another system wide unique handle allocated by the framework.
+ * This unique handle is used for all transactions inside the framework.
+ */
+typedef int audio_patch_handle_t;
+#define AUDIO_PATCH_HANDLE_NONE 0
+
+#define AUDIO_PATCH_PORTS_MAX   16
+
+struct audio_patch {
+    audio_patch_handle_t id;            /* patch unique ID */
+    unsigned int      num_sources;      /* number of sources in following array */
+    struct audio_port_config sources[AUDIO_PATCH_PORTS_MAX];
+    unsigned int      num_sinks;        /* number of sinks in following array */
+    struct audio_port_config sinks[AUDIO_PATCH_PORTS_MAX];
+};
+
+
+
+/* a HW synchronization source returned by the audio HAL */
+typedef uint32_t audio_hw_sync_t;
+
+/* an invalid HW synchronization source indicating an error */
+#define AUDIO_HW_SYNC_INVALID 0
+
+static inline bool audio_is_output_device(audio_devices_t device)
+{
+    if (((device & AUDIO_DEVICE_BIT_IN) == 0) &&
+            (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_input_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+        device &= ~AUDIO_DEVICE_BIT_IN;
+        if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0))
+            return true;
+    }
+    return false;
+}
+
+static inline bool audio_is_output_devices(audio_devices_t device)
+{
+    return (device & AUDIO_DEVICE_BIT_IN) == 0;
+}
+
+static inline bool audio_is_a2dp_in_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+        device &= ~AUDIO_DEVICE_BIT_IN;
+        if ((popcount(device) == 1) && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP))
+            return true;
+    }
+    return false;
+}
+
+static inline bool audio_is_a2dp_out_device(audio_devices_t device)
+{
+    if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP))
+        return true;
+    else
+        return false;
+}
+
+// Deprecated - use audio_is_a2dp_out_device() instead
+static inline bool audio_is_a2dp_device(audio_devices_t device)
+{
+    return audio_is_a2dp_out_device(device);
+}
+
+static inline bool audio_is_bluetooth_sco_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_BIT_IN) == 0) {
+        if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0))
+            return true;
+    } else {
+        device &= ~AUDIO_DEVICE_BIT_IN;
+        if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0))
+            return true;
+    }
+
+    return false;
+}
+
+static inline bool audio_is_usb_out_device(audio_devices_t device)
+{
+    return ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB));
+}
+
+static inline bool audio_is_usb_in_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+        device &= ~AUDIO_DEVICE_BIT_IN;
+        if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_ALL_USB) != 0)
+            return true;
+    }
+    return false;
+}
+
+/* OBSOLETE - use audio_is_usb_out_device() instead. */
+static inline bool audio_is_usb_device(audio_devices_t device)
+{
+    return audio_is_usb_out_device(device);
+}
+
+static inline bool audio_is_remote_submix_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX
+            || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)
+        return true;
+    else
+        return false;
+}
+
+/* Returns true if:
+ *  representation is valid, and
+ *  there is at least one channel bit set which _could_ correspond to an input channel, and
+ *  there are no channel bits set which could _not_ correspond to an input channel.
+ * Otherwise returns false.
+ */
+static inline bool audio_is_input_channel(audio_channel_mask_t channel)
+{
+    uint32_t bits = audio_channel_mask_get_bits(channel);
+    switch (audio_channel_mask_get_representation(channel)) {
+    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+        if (bits & ~AUDIO_CHANNEL_IN_ALL) {
+            bits = 0;
+        }
+        // fall through
+    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+        return bits != 0;
+    default:
+        return false;
+    }
+}
+
+/* Returns true if:
+ *  representation is valid, and
+ *  there is at least one channel bit set which _could_ correspond to an output channel, and
+ *  there are no channel bits set which could _not_ correspond to an output channel.
+ * Otherwise returns false.
+ */
+static inline bool audio_is_output_channel(audio_channel_mask_t channel)
+{
+    uint32_t bits = audio_channel_mask_get_bits(channel);
+    switch (audio_channel_mask_get_representation(channel)) {
+    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+        if (bits & ~AUDIO_CHANNEL_OUT_ALL) {
+            bits = 0;
+        }
+        // fall through
+    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+        return bits != 0;
+    default:
+        return false;
+    }
+}
+
+/* Returns the number of channels from an input channel mask,
+ * used in the context of audio input or recording.
+ * If a channel bit is set which could _not_ correspond to an input channel,
+ * it is excluded from the count.
+ * Returns zero if the representation is invalid.
+ */
+static inline uint32_t audio_channel_count_from_in_mask(audio_channel_mask_t channel)
+{
+    uint32_t bits = audio_channel_mask_get_bits(channel);
+    switch (audio_channel_mask_get_representation(channel)) {
+    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+        // TODO: We can now merge with from_out_mask and remove anding
+        bits &= AUDIO_CHANNEL_IN_ALL;
+        // fall through
+    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+        return popcount(bits);
+    default:
+        return 0;
+    }
+}
+
+/* Returns the number of channels from an output channel mask,
+ * used in the context of audio output or playback.
+ * If a channel bit is set which could _not_ correspond to an output channel,
+ * it is excluded from the count.
+ * Returns zero if the representation is invalid.
+ */
+static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel)
+{
+    uint32_t bits = audio_channel_mask_get_bits(channel);
+    switch (audio_channel_mask_get_representation(channel)) {
+    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+        // TODO: We can now merge with from_in_mask and remove anding
+        bits &= AUDIO_CHANNEL_OUT_ALL;
+        // fall through
+    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+        return popcount(bits);
+    default:
+        return 0;
+    }
+}
+
+/* Derive an output channel mask for position assignment from a channel count.
+ * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel
+ * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad,
+ * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC
+ * for continuity with stereo.
+ * Returns the matching channel mask,
+ * or AUDIO_CHANNEL_NONE if the channel count is zero,
+ * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the
+ * configurations for which a default output channel mask is defined.
+ */
+static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count)
+{
+    uint32_t bits;
+    switch (channel_count) {
+    case 0:
+        return AUDIO_CHANNEL_NONE;
+    case 1:
+        bits = AUDIO_CHANNEL_OUT_MONO;
+        break;
+    case 2:
+        bits = AUDIO_CHANNEL_OUT_STEREO;
+        break;
+    case 3:
+        bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER;
+        break;
+    case 4: // 4.0
+        bits = AUDIO_CHANNEL_OUT_QUAD;
+        break;
+    case 5: // 5.0
+        bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER;
+        break;
+    case 6: // 5.1
+        bits = AUDIO_CHANNEL_OUT_5POINT1;
+        break;
+    case 7: // 6.1
+        bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER;
+        break;
+    case 8:
+        bits = AUDIO_CHANNEL_OUT_7POINT1;
+        break;
+    default:
+        return AUDIO_CHANNEL_INVALID;
+    }
+    return audio_channel_mask_from_representation_and_bits(
+            AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
+}
+
+/* Derive an input channel mask for position assignment from a channel count.
+ * Currently handles only mono and stereo.
+ * Returns the matching channel mask,
+ * or AUDIO_CHANNEL_NONE if the channel count is zero,
+ * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the
+ * configurations for which a default input channel mask is defined.
+ */
+static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count)
+{
+    uint32_t bits;
+    switch (channel_count) {
+    case 0:
+        return AUDIO_CHANNEL_NONE;
+    case 1:
+        bits = AUDIO_CHANNEL_IN_MONO;
+        break;
+    case 2:
+        bits = AUDIO_CHANNEL_IN_STEREO;
+        break;
+    default:
+        return AUDIO_CHANNEL_INVALID;
+    }
+    return audio_channel_mask_from_representation_and_bits(
+            AUDIO_CHANNEL_REPRESENTATION_POSITION, bits);
+}
+
+/* Derive a channel mask for index assignment from a channel count.
+ * Returns the matching channel mask,
+ * or AUDIO_CHANNEL_NONE if the channel count is zero,
+ * or AUDIO_CHANNEL_INVALID if the channel count exceeds AUDIO_CHANNEL_COUNT_MAX.
+ */
+static inline audio_channel_mask_t audio_channel_mask_for_index_assignment_from_count(
+        uint32_t channel_count)
+{
+    uint32_t bits;
+
+    if (channel_count == 0) {
+        return AUDIO_CHANNEL_NONE;
+    }
+    if (channel_count > AUDIO_CHANNEL_COUNT_MAX) {
+        return AUDIO_CHANNEL_INVALID;
+    }
+    bits = (1 << channel_count) - 1;
+    return audio_channel_mask_from_representation_and_bits(
+            AUDIO_CHANNEL_REPRESENTATION_INDEX, bits);
+}
+
+static inline bool audio_is_valid_format(audio_format_t format)
+{
+    switch (format & AUDIO_FORMAT_MAIN_MASK) {
+    case AUDIO_FORMAT_PCM:
+        switch (format) {
+        case AUDIO_FORMAT_PCM_16_BIT:
+        case AUDIO_FORMAT_PCM_8_BIT:
+        case AUDIO_FORMAT_PCM_32_BIT:
+        case AUDIO_FORMAT_PCM_8_24_BIT:
+        case AUDIO_FORMAT_PCM_FLOAT:
+        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+            return true;
+        case AUDIO_FORMAT_INVALID:
+        case AUDIO_FORMAT_DEFAULT:
+        case AUDIO_FORMAT_MP3:
+        case AUDIO_FORMAT_AMR_NB:
+        case AUDIO_FORMAT_AMR_WB:
+        case AUDIO_FORMAT_AAC:
+        case AUDIO_FORMAT_HE_AAC_V1:
+        case AUDIO_FORMAT_HE_AAC_V2:
+        case AUDIO_FORMAT_VORBIS:
+        case AUDIO_FORMAT_OPUS:
+        case AUDIO_FORMAT_AC3:
+        case AUDIO_FORMAT_E_AC3:
+        case AUDIO_FORMAT_MAIN_MASK:
+        case AUDIO_FORMAT_SUB_MASK:
+        case AUDIO_FORMAT_AAC_MAIN:
+        case AUDIO_FORMAT_AAC_LC:
+        case AUDIO_FORMAT_AAC_SSR:
+        case AUDIO_FORMAT_AAC_LTP:
+        case AUDIO_FORMAT_AAC_HE_V1:
+        case AUDIO_FORMAT_AAC_SCALABLE:
+        case AUDIO_FORMAT_AAC_ERLC:
+        case AUDIO_FORMAT_AAC_LD:
+        case AUDIO_FORMAT_AAC_HE_V2:
+        case AUDIO_FORMAT_AAC_ELD:
+        default:
+            return false;
+        }
+        /* not reached */
+    case AUDIO_FORMAT_MP3:
+    case AUDIO_FORMAT_AMR_NB:
+    case AUDIO_FORMAT_AMR_WB:
+    case AUDIO_FORMAT_AAC:
+    case AUDIO_FORMAT_HE_AAC_V1:
+    case AUDIO_FORMAT_HE_AAC_V2:
+    case AUDIO_FORMAT_VORBIS:
+    case AUDIO_FORMAT_OPUS:
+    case AUDIO_FORMAT_AC3:
+    case AUDIO_FORMAT_E_AC3:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static inline bool audio_is_linear_pcm(audio_format_t format)
+{
+    return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM);
+}
+
+static inline size_t audio_bytes_per_sample(audio_format_t format)
+{
+    size_t size = 0;
+
+    switch (format) {
+    case AUDIO_FORMAT_PCM_32_BIT:
+    case AUDIO_FORMAT_PCM_8_24_BIT:
+        size = sizeof(int32_t);
+        break;
+    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+        size = sizeof(uint8_t) * 3;
+        break;
+    case AUDIO_FORMAT_PCM_16_BIT:
+        size = sizeof(int16_t);
+        break;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        size = sizeof(uint8_t);
+        break;
+    case AUDIO_FORMAT_PCM_FLOAT:
+        size = sizeof(float);
+        break;
+    case AUDIO_FORMAT_INVALID:
+    case AUDIO_FORMAT_DEFAULT:
+    case AUDIO_FORMAT_MP3:
+    case AUDIO_FORMAT_AMR_NB:
+    case AUDIO_FORMAT_AMR_WB:
+    case AUDIO_FORMAT_AAC:
+    case AUDIO_FORMAT_HE_AAC_V1:
+    case AUDIO_FORMAT_HE_AAC_V2:
+    case AUDIO_FORMAT_VORBIS:
+    case AUDIO_FORMAT_OPUS:
+    case AUDIO_FORMAT_AC3:
+    case AUDIO_FORMAT_E_AC3:
+    case AUDIO_FORMAT_MAIN_MASK:
+    case AUDIO_FORMAT_SUB_MASK:
+    case AUDIO_FORMAT_AAC_MAIN:
+    case AUDIO_FORMAT_AAC_LC:
+    case AUDIO_FORMAT_AAC_SSR:
+    case AUDIO_FORMAT_AAC_LTP:
+    case AUDIO_FORMAT_AAC_HE_V1:
+    case AUDIO_FORMAT_AAC_SCALABLE:
+    case AUDIO_FORMAT_AAC_ERLC:
+    case AUDIO_FORMAT_AAC_LD:
+    case AUDIO_FORMAT_AAC_HE_V2:
+    case AUDIO_FORMAT_AAC_ELD:
+    default:
+        break;
+    }
+    return size;
+}
+
+/* converts device address to string sent to audio HAL via set_parameters */
+#if 0 /* never used error */
+static char *audio_device_address_to_parameter(audio_devices_t device, const char *address)
+{
+    const size_t kSize = AUDIO_DEVICE_MAX_ADDRESS_LEN + sizeof("a2dp_sink_address=");
+    char param[kSize];
+
+    if (device & AUDIO_DEVICE_OUT_ALL_A2DP)
+        snprintf(param, kSize, "%s=%s", "a2dp_sink_address", address);
+    else if (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+        snprintf(param, kSize, "%s=%s", "mix", address);
+    else
+        snprintf(param, kSize, "%s", address);
+
+    return strdup(param);
+}
+#endif
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_CORE_H
diff --git a/bluez/repo/android/test-ipc.c b/bluez/repo/android/test-ipc.c
new file mode 100644
index 0000000..bb7d15f
--- /dev/null
+++ b/bluez/repo/android/test-ipc.c
@@ -0,0 +1,577 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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 <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "android/ipc-common.h"
+#include "android/ipc.h"
+
+static const char HAL_SK_PATH[] = "\0test_hal_socket";
+
+#define SERVICE_ID_MAX 10
+
+struct test_data {
+	bool disconnect;
+	const void *cmd;
+	uint16_t cmd_size;
+	uint8_t service;
+	const struct ipc_handler *handlers;
+	uint8_t handlers_size;
+};
+
+struct context {
+	GMainLoop *main_loop;
+
+	int sk;
+
+	guint source;
+	guint cmd_source;
+	guint notif_source;
+
+	GIOChannel *cmd_io;
+	GIOChannel *notif_io;
+
+	const struct test_data *data;
+};
+
+
+static struct ipc *ipc = NULL;
+
+static void context_quit(struct context *context)
+{
+	g_main_loop_quit(context->main_loop);
+}
+
+static gboolean cmd_watch(GIOChannel *io, GIOCondition cond,
+						gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_data *test_data = context->data;
+	const struct ipc_hdr *sent_msg = test_data->cmd;
+	uint8_t buf[128];
+	int sk;
+
+	struct ipc_hdr success_resp = {
+		.service_id = sent_msg->service_id,
+		.opcode = sent_msg->opcode,
+		.len = 0,
+	};
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_assert(test_data->disconnect);
+		return FALSE;
+	}
+
+	g_assert(!test_data->disconnect);
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	g_assert(read(sk, buf, sizeof(buf)) == sizeof(struct ipc_hdr));
+	g_assert(!memcmp(&success_resp, buf, sizeof(struct ipc_hdr)));
+
+	context_quit(context);
+
+	return TRUE;
+}
+
+static gboolean notif_watch(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_data *test_data = context->data;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_assert(test_data->disconnect);
+		return FALSE;
+	}
+
+	g_assert(!test_data->disconnect);
+
+	return TRUE;
+}
+
+static gboolean connect_handler(GIOChannel *io, GIOCondition cond,
+						gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_data *test_data = context->data;
+	GIOChannel *new_io;
+	GIOCondition watch_cond;
+	int sk;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_assert(FALSE);
+		return FALSE;
+	}
+
+	g_assert(!context->cmd_source || !context->notif_source);
+
+	sk = accept(context->sk, NULL, NULL);
+	g_assert(sk >= 0);
+
+	new_io = g_io_channel_unix_new(sk);
+
+	watch_cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+
+	if (context->cmd_source && !context->notif_source) {
+		context->notif_source = g_io_add_watch(new_io, watch_cond,
+							notif_watch, context);
+		g_assert(context->notif_source > 0);
+		context->notif_io = new_io;
+	}
+
+	if (!context->cmd_source) {
+		context->cmd_source = g_io_add_watch(new_io, watch_cond,
+							cmd_watch, context);
+		context->cmd_io = new_io;
+	}
+
+	if (context->cmd_source && context->notif_source && !test_data->cmd)
+		context_quit(context);
+
+	return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	struct sockaddr_un addr;
+	GIOChannel *io;
+	int ret, sk;
+
+	context->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(context->main_loop);
+
+	sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+	g_assert(sk >= 0);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, HAL_SK_PATH, sizeof(HAL_SK_PATH));
+
+	ret = bind(sk, (struct sockaddr *) &addr, sizeof(addr));
+	g_assert(ret == 0);
+
+	ret = listen(sk, 5);
+	g_assert(ret == 0);
+
+	io = g_io_channel_unix_new(sk);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	context->source = g_io_add_watch(io,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				connect_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(io);
+
+	context->sk = sk;
+	context->data = data;
+
+	return context;
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	g_io_channel_shutdown(context->notif_io, TRUE, NULL);
+	g_io_channel_shutdown(context->cmd_io, TRUE, NULL);
+	g_io_channel_unref(context->cmd_io);
+	g_io_channel_unref(context->notif_io);
+
+	g_source_remove(context->notif_source);
+	g_source_remove(context->cmd_source);
+	g_source_remove(context->source);
+
+	g_main_loop_unref(context->main_loop);
+
+	g_free(context);
+}
+
+static void disconnected(void *data)
+{
+	struct context *context = data;
+
+	g_assert(context->data->disconnect);
+
+	context_quit(context);
+}
+
+static void test_init(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+						true, NULL, NULL);
+
+	g_assert(ipc);
+
+	execute_context(context);
+
+	ipc_cleanup(ipc);
+	ipc = NULL;
+}
+
+static gboolean send_cmd(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_data *test_data = context->data;
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(context->cmd_io);
+	g_assert(sk >= 0);
+
+	g_assert(write(sk, test_data->cmd, test_data->cmd_size) ==
+						test_data->cmd_size);
+
+	return FALSE;
+}
+
+static gboolean register_service(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_data *test_data = context->data;
+
+	ipc_register(ipc, test_data->service, test_data->handlers,
+						test_data->handlers_size);
+
+	return FALSE;
+}
+
+static gboolean unregister_service(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_data *test_data = context->data;
+
+	ipc_unregister(ipc, test_data->service);
+
+	return FALSE;
+}
+
+static void test_cmd(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+					true, disconnected, context);
+
+	g_assert(ipc);
+
+	g_idle_add(send_cmd, context);
+
+	execute_context(context);
+
+	ipc_cleanup(ipc);
+	ipc = NULL;
+}
+
+static void test_cmd_reg(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	const struct test_data *test_data = context->data;
+
+	ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+					true, disconnected, context);
+
+	g_assert(ipc);
+
+	g_idle_add(register_service, context);
+	g_idle_add(send_cmd, context);
+
+	execute_context(context);
+
+	ipc_unregister(ipc, test_data->service);
+
+	ipc_cleanup(ipc);
+	ipc = NULL;
+}
+
+static void test_cmd_reg_1(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+					true, disconnected, context);
+
+	g_assert(ipc);
+
+	g_idle_add(register_service, context);
+	g_idle_add(unregister_service, context);
+	g_idle_add(send_cmd, context);
+
+	execute_context(context);
+
+	ipc_cleanup(ipc);
+	ipc = NULL;
+}
+
+static void test_cmd_handler_1(const void *buf, uint16_t len)
+{
+	ipc_send_rsp(ipc, 0, 1, 0);
+}
+
+static void test_cmd_handler_2(const void *buf, uint16_t len)
+{
+	ipc_send_rsp(ipc, 0, 2, 0);
+}
+
+static void test_cmd_handler_invalid(const void *buf, uint16_t len)
+{
+	g_assert(false);
+}
+
+static const struct test_data test_init_1 = {};
+
+static const struct ipc_hdr test_cmd_1_hdr = {
+	.service_id = 0,
+	.opcode = 1,
+	.len = 0
+};
+
+static const struct ipc_hdr test_cmd_2_hdr = {
+	.service_id = 0,
+	.opcode = 2,
+	.len = 0
+};
+
+static const struct test_data test_cmd_service_invalid_1 = {
+	.cmd = &test_cmd_1_hdr,
+	.cmd_size = sizeof(test_cmd_1_hdr),
+	.disconnect = true,
+};
+
+static const struct ipc_handler cmd_handlers[] = {
+	{ test_cmd_handler_1, false, 0 }
+};
+
+static const struct test_data test_cmd_service_valid_1 = {
+	.cmd = &test_cmd_1_hdr,
+	.cmd_size = sizeof(test_cmd_1_hdr),
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1
+};
+
+static const struct test_data test_cmd_service_invalid_2 = {
+	.cmd = &test_cmd_1_hdr,
+	.cmd_size = sizeof(test_cmd_1_hdr),
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+static const struct ipc_handler cmd_handlers_invalid_2[] = {
+	{ test_cmd_handler_1, false, 0 },
+	{ test_cmd_handler_invalid, false, 0 }
+};
+
+static const struct ipc_handler cmd_handlers_invalid_1[] = {
+	{ test_cmd_handler_invalid, false, 0 },
+	{ test_cmd_handler_2, false, 0 },
+};
+
+static const struct test_data test_cmd_opcode_valid_1 = {
+	.cmd = &test_cmd_1_hdr,
+	.cmd_size = sizeof(test_cmd_1_hdr),
+	.service = 0,
+	.handlers = cmd_handlers_invalid_2,
+	.handlers_size = 2,
+};
+
+static const struct test_data test_cmd_opcode_valid_2 = {
+	.cmd = &test_cmd_2_hdr,
+	.cmd_size = sizeof(test_cmd_2_hdr),
+	.service = 0,
+	.handlers = cmd_handlers_invalid_1,
+	.handlers_size = 2,
+};
+
+static const struct test_data test_cmd_opcode_invalid_1 = {
+	.cmd = &test_cmd_2_hdr,
+	.cmd_size = sizeof(test_cmd_2_hdr),
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+static const struct test_data test_cmd_hdr_invalid = {
+	.cmd = &test_cmd_1_hdr,
+	.cmd_size = sizeof(test_cmd_1_hdr) - 1,
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+#define VARDATA_EX1 "some data example"
+
+struct vardata {
+	struct ipc_hdr hdr;
+	uint8_t data[IPC_MTU - sizeof(struct ipc_hdr)];
+} __attribute__((packed));
+
+static const struct vardata test_cmd_vardata = {
+	.hdr.service_id = 0,
+	.hdr.opcode = 1,
+	.hdr.len = sizeof(VARDATA_EX1),
+	.data = VARDATA_EX1,
+};
+
+static const struct ipc_handler cmd_vardata_handlers[] = {
+	{ test_cmd_handler_1, true, sizeof(VARDATA_EX1) }
+};
+
+static const struct test_data test_cmd_vardata_valid = {
+	.cmd = &test_cmd_vardata,
+	.cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1),
+	.service = 0,
+	.handlers = cmd_vardata_handlers,
+	.handlers_size = 1,
+};
+
+static const struct ipc_handler cmd_vardata_handlers_valid2[] = {
+	{ test_cmd_handler_1, true, sizeof(VARDATA_EX1) - 1 }
+};
+
+static const struct test_data test_cmd_vardata_valid_2 = {
+	.cmd = &test_cmd_vardata,
+	.cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1),
+	.service = 0,
+	.handlers = cmd_vardata_handlers_valid2,
+	.handlers_size = 1,
+};
+
+static const struct test_data test_cmd_vardata_invalid_1 = {
+	.cmd = &test_cmd_vardata,
+	.cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1,
+	.service = 0,
+	.handlers = cmd_vardata_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+static const struct ipc_hdr test_cmd_service_offrange_hdr = {
+	.service_id = SERVICE_ID_MAX + 1,
+	.opcode = 1,
+	.len = 0
+};
+
+static const struct test_data test_cmd_service_offrange = {
+	.cmd = &test_cmd_service_offrange_hdr,
+	.cmd_size = sizeof(struct ipc_hdr),
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+static const struct vardata test_cmd_invalid_data_1 = {
+	.hdr.service_id = 0,
+	.hdr.opcode = 1,
+	.hdr.len = sizeof(VARDATA_EX1),
+	.data = VARDATA_EX1,
+};
+
+static const struct test_data test_cmd_msg_invalid_1 = {
+	.cmd = &test_cmd_invalid_data_1,
+	.cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1,
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+static const struct vardata test_cmd_invalid_data_2 = {
+	.hdr.service_id = 0,
+	.hdr.opcode = 1,
+	.hdr.len = sizeof(VARDATA_EX1) - 1,
+	.data = VARDATA_EX1,
+};
+
+static const struct test_data test_cmd_msg_invalid_2 = {
+	.cmd = &test_cmd_invalid_data_2,
+	.cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1),
+	.service = 0,
+	.handlers = cmd_handlers,
+	.handlers_size = 1,
+	.disconnect = true,
+};
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__btd_log_init("*", 0);
+
+	g_test_add_data_func("/android_ipc/init", &test_init_1, test_init);
+	g_test_add_data_func("/android_ipc/service_invalid_1",
+				&test_cmd_service_invalid_1, test_cmd);
+	g_test_add_data_func("/android_ipc/service_valid_1",
+				&test_cmd_service_valid_1, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/service_invalid_2",
+				&test_cmd_service_invalid_2, test_cmd_reg_1);
+	g_test_add_data_func("/android_ipc/opcode_valid_1",
+				&test_cmd_opcode_valid_1, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/opcode_valid_2",
+				&test_cmd_opcode_valid_2, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/opcode_invalid_1",
+				&test_cmd_opcode_invalid_1, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/vardata_valid",
+				&test_cmd_vardata_valid, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/vardata_valid_2",
+				&test_cmd_vardata_valid_2, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/vardata_invalid_1",
+				&test_cmd_vardata_invalid_1, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/service_offrange",
+				&test_cmd_service_offrange, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/hdr_invalid",
+				&test_cmd_hdr_invalid, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/msg_invalid_1",
+				&test_cmd_msg_invalid_1, test_cmd_reg);
+	g_test_add_data_func("/android_ipc/msg_invalid_2",
+				&test_cmd_msg_invalid_2, test_cmd_reg);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/android/tester-a2dp.c b/bluez/repo/android/tester-a2dp.c
new file mode 100644
index 0000000..8397eef
--- /dev/null
+++ b/bluez/repo/android/tester-a2dp.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "lib/bluetooth.h"
+#include "android/utils.h"
+#include "tester-main.h"
+
+static struct queue *list;
+
+#define req_dsc 0x00, 0x01
+#define rsp_dsc 0x02, 0x01, 0x04, 0x08
+#define req_get 0x10, 0x02, 0x04
+#define rsp_get 0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, \
+						0x00, 0xff, 0xff, 0x02, 0x40
+#define req_cfg 0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, \
+					0x06, 0x00, 0x00, 0x21, 0x15, 0x02, \
+					0x40
+#define rsp_cfg 0x22, 0x03
+#define req_open 0x30, 0x06, 0x04
+#define rsp_open 0x32, 0x06
+#define req_close 0x40, 0x08, 0x04
+#define rsp_close 0x42, 0x08
+#define req_start 0x40, 0x07, 0x04
+#define rsp_start 0x42, 0x07
+#define req_suspend 0x50, 0x09, 0x04
+#define rsp_suspend 0x52, 0x09
+
+static const struct pdu_set pdus[] = {
+	{ raw_pdu(req_dsc), raw_pdu(rsp_dsc) },
+	{ raw_pdu(req_get), raw_pdu(rsp_get) },
+	{ raw_pdu(req_cfg), raw_pdu(rsp_cfg) },
+	{ raw_pdu(req_open), raw_pdu(rsp_open) },
+	{ raw_pdu(req_close), raw_pdu(rsp_close) },
+	{ raw_pdu(req_start), raw_pdu(rsp_start) },
+	{ raw_pdu(req_suspend), raw_pdu(rsp_suspend) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data cid_data = {
+	.pdu = pdus,
+};
+
+static void a2dp_connect_request_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
+{
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	if (cid_data->handle)
+		return;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+
+	tester_handle_l2cap_data_exchange(cid_data);
+}
+
+static struct emu_set_l2cap_data l2cap_setup_data = {
+	.psm = 25,
+	.func = a2dp_connect_request_cb,
+	.user_data = &cid_data,
+};
+
+static void a2dp_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	cid_data.handle = 0;
+	cid_data.cid = 0;
+
+	bdaddr2android((const bdaddr_t *) addr, &bdaddr);
+
+	step->action_status = data->if_a2dp->connect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void a2dp_disconnect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) addr, &bdaddr);
+
+	step->action_status = data->if_a2dp->disconnect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void audio_resume_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	int err;
+
+	err = data->audio->open_output_stream(data->audio,
+						0,
+						AUDIO_DEVICE_OUT_ALL_A2DP,
+						AUDIO_OUTPUT_FLAG_NONE,
+						NULL,
+						&data->if_stream, NULL);
+	if (err < 0) {
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	/* Write something to force resume */
+	data->if_stream->write(data->if_stream, &err, sizeof(err));
+
+done:
+	schedule_action_verification(step);
+}
+
+static void audio_suspend_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	data->if_stream->common.standby(&data->if_stream->common);
+
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("A2DP Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("A2DP Connect - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(a2dp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTED),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("A2DP Disconnect - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(a2dp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(a2dp_disconnect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("A2DP Resume - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(a2dp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(audio_resume_action, NULL),
+		CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE,
+					BTAV_AUDIO_STATE_STARTED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTED),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("A2DP Suspend - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(a2dp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(audio_resume_action, NULL),
+		CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE,
+					BTAV_AUDIO_STATE_STARTED),
+		ACTION_SUCCESS(audio_suspend_action, NULL),
+		CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE,
+					BTAV_AUDIO_STATE_STOPPED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTED),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+};
+
+struct queue *get_a2dp_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_a2dp_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-avrcp.c b/bluez/repo/android/tester-avrcp.c
new file mode 100644
index 0000000..737602e
--- /dev/null
+++ b/bluez/repo/android/tester-avrcp.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "lib/bluetooth.h"
+#include "android/utils.h"
+#include "tester-main.h"
+
+static struct queue *list;
+
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+
+#define sdp_rsp_pdu	0x07, \
+			0x00, 0x00, \
+			0x00, 0x7f, \
+			0x00, 0x7c, \
+			0x36, 0x00, 0x79, 0x36, 0x00, 0x3b, 0x09, 0x00, 0x00, \
+			0x0a, 0x00, 0x01, 0x00, 0x04, 0x09, 0x00, 0x01, 0x35, \
+			0x06, 0x19, 0x11, 0x0e, 0x19, 0x11, 0x0f, 0x09, 0x00, \
+			0x04, 0x35, 0x10, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, \
+			0x00, 0x17, 0x35, 0x06, 0x19, 0x00, 0x17, 0x09, 0x01, \
+			0x03, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, \
+			0x11, 0x0e, 0x09, 0x01, 0x00, 0x09, 0x03, 0x11, 0x09, \
+			0x00, 0x01, 0x36, 0x00, 0x38, 0x09, 0x00, 0x00, 0x0a, \
+			0x00, 0x01, 0x00, 0x05, 0x09, 0x00, 0x01, 0x35, 0x03, \
+			0x19, 0x11, 0x0c, 0x09, 0x00, 0x04, 0x35, 0x10, 0x35, \
+			0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x17, 0x35, 0x06, \
+			0x19, 0x00, 0x17, 0x09, 0x01, 0x03, 0x09, 0x00, 0x09, \
+			0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x0e, 0x09, 0x01, \
+			0x04, 0x09, 0x03, 0x11, 0x09, 0x00, 0x02, \
+			0x00
+
+static const struct pdu_set sdp_pdus[] = {
+	{ end_pdu, raw_pdu(sdp_rsp_pdu) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data sdp_data = {
+	.pdu = sdp_pdus,
+	.is_sdp = TRUE,
+};
+
+#define req_dsc 0x00, 0x01
+#define rsp_dsc 0x02, 0x01, 0x04, 0x08
+#define req_get 0x10, 0x02, 0x04
+#define rsp_get 0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, \
+						0x00, 0xff, 0xff, 0x02, 0x40
+#define req_cfg 0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, \
+					0x06, 0x00, 0x00, 0x21, 0x15, 0x02, \
+					0x40
+#define rsp_cfg 0x22, 0x03
+#define req_open 0x30, 0x06, 0x04
+#define rsp_open 0x32, 0x06
+#define req_close 0x40, 0x08, 0x04
+#define rsp_close 0x42, 0x08
+#define req_start 0x40, 0x07, 0x04
+#define rsp_start 0x42, 0x07
+#define req_suspend 0x50, 0x09, 0x04
+#define rsp_suspend 0x52, 0x09
+
+#define req_play_status 0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, 0x00, 0x19, 0x58, \
+							0x30, 0x00, 0x00, 0x00
+#define rsp_play_status 0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, 0x00, 0x19, 0x58, \
+			0x30, 0x00, 0x00, 0x09, 0xbb, 0xbb, 0xbb, 0xbb, 0xaa, \
+							0xaa, 0xaa, 0xaa, 0x00
+
+#define req_track_notif 0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, 0x00, 0x19, 0x58, \
+			0x31, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00
+
+#define rsp_track_notif 0x00, 0x11, 0x0e, 0x0F, 0x48, 0x00, 0x00, 0x19, 0x58, \
+			0x31, 0x00, 0x00, 0x09, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, \
+							0xFF, 0xFF, 0xFF, 0xFF
+
+#define req_position_notif 0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, 0x00, 0x19, \
+				0x58, 0x31, 0x00, 0x00, 0x05, 0x05, 0x00, \
+							0x00, 0x00, 0x00
+
+#define rsp_position_notif 0x00, 0x11, 0x0e, 0x0F, 0x48, 0x00, 0x00, 0x19, \
+				0x58, 0x31, 0x00, 0x00, 0x04, 0x05, 0xFF, \
+							0xFF, 0xFF, 0xFF
+
+#define req_status_notif 0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, 0x00, 0x19, \
+				0x58, 0x31, 0x00, 0x00, 0x05, 0x01, 0x00, \
+							0x00, 0x00, 0x00
+
+#define rsp_status_notif 0x00, 0x11, 0x0e, 0x0D, 0x48, 0x00, 0x00, 0x19, \
+				0x58, 0x31, 0x00, 0x00, 0x01, 0x01, 0x00
+
+#define req_ele_attr 0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, 0x00, 0x19, 0x58, \
+			0x20, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, \
+			0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07
+
+#define rsp_ele_attr 0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, 0x00, 0x19, 0x58, \
+			0x20, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x00, 0x01, \
+			0x00, 0x6a, 0x00, 0x13, 0x47, 0x69, 0x76, 0x65, 0x20, \
+			0x50, 0x65, 0x61, 0x63, 0x65, 0x20, 0x61, 0x20, 0x43, \
+			0x68, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x07, \
+			0x00, 0x6a, 0x00, 0x06, 0x31, 0x30, 0x33, 0x30, 0x30, \
+									0x30
+
+static const struct pdu_set pdus[] = {
+	{ raw_pdu(req_dsc), raw_pdu(rsp_dsc) },
+	{ raw_pdu(req_get), raw_pdu(rsp_get) },
+	{ raw_pdu(req_cfg), raw_pdu(rsp_cfg) },
+	{ raw_pdu(req_open), raw_pdu(rsp_open) },
+	{ raw_pdu(req_close), raw_pdu(rsp_close) },
+	{ raw_pdu(req_start), raw_pdu(rsp_start) },
+	{ raw_pdu(req_suspend), raw_pdu(rsp_suspend) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data a2dp_data = {
+	.pdu = pdus,
+};
+
+static struct emu_l2cap_cid_data avrcp_data;
+
+static btrc_element_attr_val_t ele_attrs[2] = {
+	{
+	.attr_id = BTRC_MEDIA_ATTR_TITLE,
+	.text = {0x47, 0x69, 0x76, 0x65, 0x20, 0x50, 0x65, 0x61, 0x63, 0x65,
+			 0x20, 0x61, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65}
+	},
+	{
+	.attr_id = BTRC_MEDIA_ATTR_PLAYING_TIME,
+	.text = {0x31, 0x30, 0x33, 0x30, 0x30, 0x30}
+	}
+};
+
+static btrc_element_attr_val_t exp_attrs[2];
+
+static void print_avrcp(const char *str, void *user_data)
+{
+	tester_debug("avrcp: %s", str);
+}
+
+static void avrcp_cid_hook_cb(const void *data, uint16_t len, void *user_data)
+{
+	struct step *step;
+	uint8_t pdu, event;
+
+	util_hexdump('>', data, len, print_avrcp, NULL);
+
+	pdu = ((uint8_t *) data)[9];
+	switch (pdu) {
+	case AVRCP_GET_PLAY_STATUS:
+		step = g_new0(struct step, 1);
+		step->callback = CB_AVRCP_PLAY_STATUS_RSP;
+		step->callback_result.song_length = get_be32(data + 13);
+		step->callback_result.song_position = get_be32(data + 17);
+		step->callback_result.play_status = ((uint8_t *) data)[21];
+		schedule_callback_verification(step);
+		break;
+	case AVRCP_REGISTER_NOTIFICATION:
+		event = ((uint8_t *) data)[13];
+		switch (event) {
+		case 0x01:
+			step = g_new0(struct step, 1);
+			step->callback = CB_AVRCP_REG_NOTIF_RSP;
+			step->callback_result.play_status =
+							((uint8_t *) data)[14];
+			schedule_callback_verification(step);
+			break;
+
+		case 0x02:
+			step = g_new0(struct step, 1);
+			step->callback = CB_AVRCP_REG_NOTIF_RSP;
+			step->callback_result.rc_index = get_be64(data + 14);
+			schedule_callback_verification(step);
+			break;
+
+		case 0x05:
+			step = g_new0(struct step, 1);
+			step->callback = CB_AVRCP_REG_NOTIF_RSP;
+			step->callback_result.song_position =
+							get_be32(data + 14);
+			schedule_callback_verification(step);
+			break;
+		}
+		break;
+	case AVRCP_GET_ELEMENT_ATTRIBUTES:
+		step = g_new0(struct step, 1);
+		step->callback = CB_AVRCP_GET_ATTR_RSP;
+		step->callback_result.num_of_attrs = ((uint8_t *) data)[13];
+
+		memset(exp_attrs, 0, 2 * sizeof(btrc_element_attr_val_t));
+		exp_attrs[0].attr_id = get_be16(data + 16);
+		memcpy(exp_attrs[0].text, data + 22, 19);
+		exp_attrs[1].attr_id = get_be16(data + 43);
+		memcpy(exp_attrs[1].text, data + 49, 6);
+		step->callback_result.attrs = exp_attrs;
+		schedule_callback_verification(step);
+		break;
+	}
+}
+
+static void avrcp_connect_request_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, avrcp_cid_hook_cb, cid_data);
+}
+
+static struct emu_set_l2cap_data avrcp_setup_data = {
+	.psm = 23,
+	.func = avrcp_connect_request_cb,
+	.user_data = &avrcp_data,
+};
+
+static void a2dp_connect_request_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
+{
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	if (cid_data->handle)
+		return;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+	avrcp_data.handle = handle;
+	avrcp_data.cid = cid;
+
+	tester_handle_l2cap_data_exchange(cid_data);
+}
+
+static struct emu_set_l2cap_data a2dp_setup_data = {
+	.psm = 25,
+	.func = a2dp_connect_request_cb,
+	.user_data = &a2dp_data,
+};
+
+static struct emu_set_l2cap_data sdp_setup_data = {
+	.psm = 1,
+	.func = tester_generic_connect_cb,
+	.user_data = &sdp_data,
+};
+
+static void avrcp_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	sdp_data.handle = 0;
+	sdp_data.cid = 0;
+
+	a2dp_data.handle = 0;
+	a2dp_data.cid = 0;
+
+	avrcp_data.handle = 0;
+	avrcp_data.cid = 0;
+
+	bdaddr2android((const bdaddr_t *) addr, &bdaddr);
+
+	step->action_status = data->if_a2dp->connect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void avrcp_disconnect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) addr, &bdaddr);
+
+	step->action_status = data->if_a2dp->disconnect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void avrcp_get_play_status_req(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct iovec pdu = raw_pdu(req_play_status);
+	struct step *step = g_new0(struct step, 1);
+
+	bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1);
+	step->action_status = BT_STATUS_SUCCESS;
+	schedule_action_verification(step);
+}
+
+static void avrcp_get_play_status_rsp(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_avrcp->get_play_status_rsp(0x00,
+						0xbbbbbbbb, 0xaaaaaaaa);
+	schedule_action_verification(step);
+}
+
+static void avrcp_reg_notif_track_changed_req(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct iovec pdu = raw_pdu(req_track_notif);
+	struct step *step = g_new0(struct step, 1);
+
+	bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1);
+	step->action_status = BT_STATUS_SUCCESS;
+	schedule_action_verification(step);
+}
+
+static void avrcp_reg_notif_track_changed_rsp(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	uint64_t track;
+	btrc_register_notification_t reg;
+
+	track = 0xffffffffffffffff;
+	memcpy(reg.track, &track, sizeof(btrc_uid_t));
+	step->action_status = data->if_avrcp->register_notification_rsp(
+							BTRC_EVT_TRACK_CHANGE,
+					BTRC_NOTIFICATION_TYPE_INTERIM, &reg);
+
+	schedule_action_verification(step);
+}
+
+static void avrcp_reg_notif_play_position_changed_req(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct iovec pdu = raw_pdu(req_position_notif);
+	struct step *step = g_new0(struct step, 1);
+
+	bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1);
+	step->action_status = BT_STATUS_SUCCESS;
+	schedule_action_verification(step);
+}
+
+static void avrcp_reg_notif_play_position_changed_rsp(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	btrc_register_notification_t reg;
+
+	reg.song_pos = 0xffffffff;
+	step->action_status = data->if_avrcp->register_notification_rsp(
+						BTRC_EVT_PLAY_POS_CHANGED,
+					BTRC_NOTIFICATION_TYPE_INTERIM, &reg);
+
+	schedule_action_verification(step);
+}
+
+static void avrcp_reg_notif_play_status_changed_req(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct iovec pdu = raw_pdu(req_status_notif);
+	struct step *step = g_new0(struct step, 1);
+
+	bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1);
+	step->action_status = BT_STATUS_SUCCESS;
+	schedule_action_verification(step);
+}
+
+static void avrcp_reg_notif_play_status_changed_rsp(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	btrc_register_notification_t reg;
+
+	reg.play_status = BTRC_PLAYSTATE_STOPPED;
+	step->action_status = data->if_avrcp->register_notification_rsp(
+						BTRC_EVT_PLAY_STATUS_CHANGED,
+					BTRC_NOTIFICATION_TYPE_CHANGED, &reg);
+
+	schedule_action_verification(step);
+}
+
+static void avrcp_get_element_attributes_req(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct iovec pdu = raw_pdu(req_ele_attr);
+	struct step *step = g_new0(struct step, 1);
+
+	bthost_send_cid_v(bthost, avrcp_data.handle, avrcp_data.cid, &pdu, 1);
+	step->action_status = BT_STATUS_SUCCESS;
+	schedule_action_verification(step);
+}
+
+static void avrcp_get_element_attributes_rsp(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_avrcp->get_element_attr_rsp(2,
+								ele_attrs);
+
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("AVRCP Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("AVRCP Connect - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("AVRCP Disconnect - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(avrcp_disconnect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_DISCONNECTED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("AVRCP GetPlayStatus - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(avrcp_get_play_status_req, NULL),
+		CALLBACK(CB_AVRCP_PLAY_STATUS_REQ),
+		ACTION_SUCCESS(avrcp_get_play_status_rsp, NULL),
+		CALLBACK_RC_PLAY_STATUS(CB_AVRCP_PLAY_STATUS_RSP, 0xbbbbbbbb,
+							0xaaaaaaaa, 0x00),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("AVRCP RegNotifTrackChanged - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(avrcp_reg_notif_track_changed_req, NULL),
+		CALLBACK(CB_AVRCP_REG_NOTIF_REQ),
+		ACTION_SUCCESS(avrcp_reg_notif_track_changed_rsp, NULL),
+		CALLBACK_RC_REG_NOTIF_TRACK_CHANGED(CB_AVRCP_REG_NOTIF_RSP,
+							0xffffffffffffffff),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("AVRCP RegNotifPlayPositionChanged - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(avrcp_reg_notif_play_position_changed_req, NULL),
+		CALLBACK(CB_AVRCP_REG_NOTIF_REQ),
+		ACTION_SUCCESS(avrcp_reg_notif_play_position_changed_rsp, NULL),
+		CALLBACK_RC_REG_NOTIF_POSITION_CHANGED(CB_AVRCP_REG_NOTIF_RSP,
+								0xffffffff),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("AVRCP RegNotifPlayStatusChanged - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(avrcp_reg_notif_play_status_changed_req, NULL),
+		CALLBACK(CB_AVRCP_REG_NOTIF_REQ),
+		ACTION_SUCCESS(avrcp_reg_notif_play_status_changed_rsp, NULL),
+		CALLBACK_RC_REG_NOTIF_STATUS_CHANGED(CB_AVRCP_REG_NOTIF_RSP,
+									0x00),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("AVRCP GetElementAttributes - 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data),
+		ACTION_SUCCESS(avrcp_connect_action, NULL),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTING),
+		CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE,
+					BTAV_CONNECTION_STATE_CONNECTED),
+		ACTION_SUCCESS(avrcp_get_element_attributes_req, NULL),
+		CALLBACK(CB_AVRCP_GET_ATTR_REQ),
+		ACTION_SUCCESS(avrcp_get_element_attributes_rsp, NULL),
+		CALLBACK_RC_GET_ELEMENT_ATTRIBUTES(CB_AVRCP_GET_ATTR_RSP, 2,
+								ele_attrs),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+};
+
+struct queue *get_avrcp_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_avrcp_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-bluetooth.c b/bluez/repo/android/tester-bluetooth.c
new file mode 100644
index 0000000..22077a0
--- /dev/null
+++ b/bluez/repo/android/tester-bluetooth.c
@@ -0,0 +1,1237 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "tester-main.h"
+
+static struct queue *list; /* List of bluetooth test cases */
+
+static bt_bdaddr_t emu_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 },
+};
+static bt_property_t prop_emu_bdaddr = {
+	.type = BT_PROPERTY_BDADDR,
+	.val = &emu_bdaddr_val,
+	.len = sizeof(emu_bdaddr_val),
+};
+
+static char emu_bdname_val[] = "BlueZ for Android";
+static bt_property_t prop_emu_bdname = {
+	.type = BT_PROPERTY_BDNAME,
+	.val = &emu_bdname_val,
+	.len = sizeof(emu_bdname_val) - 1,
+};
+
+static char emu_uuids_val[] = {
+	/* Multi profile UUID */
+	0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+	/* Device identification profile UUID */
+	0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+};
+static bt_property_t prop_emu_uuids = {
+	.type = BT_PROPERTY_UUIDS,
+	.val = &emu_uuids_val,
+	.len = sizeof(emu_uuids_val),
+};
+
+static uint32_t emu_cod_val = 0x00020c;
+static bt_property_t prop_emu_cod = {
+	.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.val = &emu_cod_val,
+	.len = sizeof(emu_cod_val),
+};
+
+static bt_device_type_t emu_tod_dual_val = BT_DEVICE_DEVTYPE_DUAL;
+static bt_property_t prop_emu_dual_tod = {
+	.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.val = &emu_tod_dual_val,
+	.len = sizeof(emu_tod_dual_val),
+};
+
+static bt_scan_mode_t emu_scan_mode_val = BT_SCAN_MODE_NONE;
+static bt_property_t prop_emu_scan_mode = {
+	.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.val = &emu_scan_mode_val,
+	.len = sizeof(emu_scan_mode_val),
+};
+
+static uint32_t emu_disc_timeout_val = 120;
+static bt_property_t prop_emu_disc_timeout = {
+	.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.val = &emu_disc_timeout_val,
+	.len = sizeof(emu_disc_timeout_val),
+};
+
+static bt_property_t prop_emu_bonded_devs = {
+	.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.val = NULL,
+	.len = 0,
+};
+
+static bt_bdaddr_t emu_remote_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 },
+};
+static bt_property_t prop_emu_remote_bdadr = {
+	.type = BT_PROPERTY_BDADDR,
+	.val = &emu_remote_bdaddr_val,
+	.len = sizeof(emu_remote_bdaddr_val),
+};
+static struct bt_action_data prop_emu_remote_ble_bdaddr_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDADDR,
+};
+
+static uint32_t emu_remote_type_val = BT_DEVICE_DEVTYPE_BREDR;
+
+static uint32_t emu_remote_tod_ble_val = BT_DEVICE_DEVTYPE_BLE;
+static bt_property_t prop_emu_remote_ble_tod_prop = {
+	.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.val = &emu_remote_tod_ble_val,
+	.len = sizeof(emu_remote_tod_ble_val),
+};
+static struct bt_action_data prop_emu_remote_ble_tod_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_TYPE_OF_DEVICE,
+};
+
+static int32_t emu_remote_rssi_val = -60;
+
+static int32_t emu_remote_ble_rssi_val = 127;
+static bt_property_t prop_emu_remote_ble_rssi_prop = {
+	.type = BT_PROPERTY_REMOTE_RSSI,
+	.val = &emu_remote_ble_rssi_val,
+	.len = sizeof(emu_remote_ble_rssi_val),
+};
+static struct bt_action_data prop_emu_remote_ble_rssi_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_REMOTE_RSSI,
+};
+
+static char emu_remote_bdname_val[] = "00:AA:01:01:00:00";
+static bt_property_t prop_emu_remote_ble_bdname_prop = {
+	.type = BT_PROPERTY_BDNAME,
+	.val = &emu_remote_bdname_val,
+	.len = sizeof(emu_remote_bdname_val) - 1,
+};
+static struct bt_action_data prop_emu_remote_ble_bdname_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDNAME,
+};
+
+static uint32_t emu_remote_cod_val = 0;
+static bt_property_t prop_emu_remote_ble_cod_prop = {
+	.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.val = &emu_remote_cod_val,
+	.len = sizeof(emu_remote_cod_val),
+};
+static struct bt_action_data prop_emu_remote_ble_cod_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_CLASS_OF_DEVICE,
+};
+
+static bt_property_t prop_emu_remote_ble_uuids_prop = {
+	.type = BT_PROPERTY_UUIDS,
+	.val = NULL,
+	.len = 0,
+};
+static struct bt_action_data prop_emu_remote_ble_uuids_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_UUIDS,
+};
+
+static bt_property_t prop_emu_remote_ble_timestamp_prop = {
+	.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+	.val = NULL,
+	.len = 4,
+};
+static struct bt_action_data prop_emu_remote_ble_timestamp_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+};
+
+static struct bt_action_data prop_emu_remote_ble_scan_mode_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+};
+
+static struct bt_action_data prop_emu_remote_ble_bondeddev_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+};
+
+static struct bt_action_data prop_emu_remote_ble_disctimeout_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+};
+
+static struct bt_action_data prop_emu_remote_ble_verinfo_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_REMOTE_VERSION_INFO,
+};
+
+static char prop_test_fname_val[] = "FriendlyTestName";
+static bt_property_t prop_emu_remote_ble_fname_prop = {
+	.type = BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+	.val = &prop_test_fname_val,
+	.len = sizeof(prop_test_fname_val) - 1,
+};
+static struct bt_action_data prop_emu_remote_ble_fname_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+	.prop = &prop_emu_remote_ble_fname_prop,
+};
+
+static bt_pin_code_t emu_pin_value = {
+	.pin = { 0x30, 0x30, 0x30, 0x30 },
+};
+static bt_pin_code_t emu_pin_invalid_value = {
+	.pin = { 0x30, 0x10, 0x30, 0x30 },
+};
+static struct bt_action_data emu_pin_set_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.pin = &emu_pin_value,
+	.pin_len = 4,
+};
+static struct bt_action_data emu_pin_set_invalid_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.pin = &emu_pin_invalid_value,
+	.pin_len = 4,
+};
+
+static bt_property_t prop_emu_default_set[] = {
+	{ BT_PROPERTY_BDADDR, sizeof(emu_bdaddr_val), NULL },
+	{ BT_PROPERTY_BDNAME, sizeof(emu_bdname_val) - 1, &emu_bdname_val },
+	{ BT_PROPERTY_CLASS_OF_DEVICE, sizeof(uint32_t), NULL },
+	{ BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_tod_dual_val),
+							&emu_tod_dual_val },
+	{ BT_PROPERTY_ADAPTER_SCAN_MODE, sizeof(emu_scan_mode_val),
+							&emu_scan_mode_val },
+	{ BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, sizeof(emu_disc_timeout_val),
+							&emu_disc_timeout_val},
+	{ BT_PROPERTY_ADAPTER_BONDED_DEVICES, 0, NULL },
+	{ BT_PROPERTY_UUIDS, sizeof(emu_uuids_val), &emu_uuids_val },
+};
+
+static bt_property_t prop_emu_remote_bles_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),
+						&emu_remote_tod_ble_val },
+	{ BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_ble_rssi_val),
+						&emu_remote_ble_rssi_val },
+};
+
+static bt_property_t prop_emu_remotes_default_set[] = {
+	{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+						&emu_remote_bdaddr_val },
+	{ BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_type_val),
+						&emu_remote_type_val },
+	{ BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_rssi_val),
+						&emu_remote_rssi_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 },
+	{ BT_PROPERTY_CLASS_OF_DEVICE, sizeof(emu_remote_cod_val),
+							&emu_remote_cod_val },
+	{ BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_ble_rssi_val),
+						&emu_remote_ble_rssi_val },
+	{ BT_PROPERTY_BDNAME, sizeof(emu_remote_bdname_val) - 1,
+						&emu_remote_bdname_val },
+	{ BT_PROPERTY_UUIDS, 0, NULL },
+	{ BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, 4, NULL },
+};
+
+static bt_property_t prop_emu_remotes_pin_req_set[] = {
+	{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+						&emu_remote_bdaddr_val },
+	{ BT_PROPERTY_CLASS_OF_DEVICE, sizeof(emu_remote_cod_val),
+						&emu_remote_cod_val },
+	{ BT_PROPERTY_BDNAME, sizeof(emu_remote_bdname_val) - 1,
+						&emu_remote_bdname_val },
+};
+
+static char test_bdname[] = "test_bdname";
+static bt_property_t prop_test_bdname = {
+	.type = BT_PROPERTY_BDNAME,
+	.val = test_bdname,
+	.len = sizeof(test_bdname) - 1,
+};
+static struct bt_action_data prop_test_remote_ble_bdname_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDNAME,
+	.prop = &prop_test_bdname,
+};
+
+static bt_scan_mode_t test_scan_mode_connectable_discoverable =
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
+static bt_property_t prop_test_scanmode_conn_discov = {
+	.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.val = &test_scan_mode_connectable_discoverable,
+	.len = sizeof(bt_scan_mode_t),
+};
+
+static uint32_t test_disctimeout_val = 600;
+static bt_property_t prop_test_disctimeout = {
+	.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.val = &test_disctimeout_val,
+	.len = sizeof(test_disctimeout_val),
+};
+static struct bt_action_data prop_test_remote_ble_disc_timeout_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+	.prop = &prop_test_disctimeout,
+};
+
+static unsigned char test_uuids_val[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00,
+			0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00 };
+static bt_property_t prop_test_uuid = {
+	.type = BT_PROPERTY_UUIDS,
+	.val = &test_uuids_val,
+	.len = sizeof(test_uuids_val),
+};
+static struct bt_action_data prop_test_remote_ble_uuids_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_UUIDS,
+	.prop = &prop_test_uuid,
+};
+
+static uint32_t test_cod_val = 0;
+static bt_property_t prop_test_cod = {
+	.type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.val = &test_cod_val,
+	.len = sizeof(test_cod_val),
+};
+static struct bt_action_data prop_test_remote_ble_cod_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_CLASS_OF_DEVICE,
+	.prop = &prop_test_cod,
+};
+
+static uint32_t test_tod_val = BT_DEVICE_DEVTYPE_BLE;
+static bt_property_t prop_test_tod = {
+	.type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.val = &test_tod_val,
+	.len = sizeof(test_tod_val),
+};
+static struct bt_action_data prop_test_remote_ble_tod_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_TYPE_OF_DEVICE,
+	.prop = &prop_test_tod,
+};
+
+static int32_t test_remote_rssi_val = -9;
+static bt_property_t prop_test_remote_rssi = {
+	.type = BT_PROPERTY_REMOTE_RSSI,
+	.val = &test_remote_rssi_val,
+	.len = sizeof(test_remote_rssi_val),
+};
+static struct bt_action_data prop_test_remote_ble_rssi_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_REMOTE_RSSI,
+	.prop = &prop_test_remote_rssi,
+};
+
+static bt_service_record_t test_srvc_record_val =  {
+	.uuid = { {0x00} },
+	.channel = 12,
+	.name = "bt_name",
+};
+static bt_property_t prop_test_srvc_record = {
+	.type = BT_PROPERTY_SERVICE_RECORD,
+	.val = &test_srvc_record_val,
+	.len = sizeof(test_srvc_record_val),
+};
+static struct bt_action_data prop_test_remote_ble_srvc_record_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_SERVICE_RECORD,
+	.prop = &prop_test_srvc_record,
+};
+
+static bt_bdaddr_t test_bdaddr_val = {
+	.address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+};
+static bt_property_t prop_test_bdaddr = {
+	.type = BT_PROPERTY_BDADDR,
+	.val = &test_bdaddr_val,
+	.len = sizeof(test_bdaddr_val),
+};
+static struct bt_action_data prop_test_remote_ble_bdaddr_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDADDR,
+	.prop = &prop_test_bdaddr,
+};
+static struct bt_action_data prop_test_bdaddr_req = {
+	.addr = &test_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDADDR,
+	.prop = &prop_test_bdaddr,
+};
+
+static bt_scan_mode_t setprop_scan_mode_conn_val = BT_SCAN_MODE_CONNECTABLE;
+
+static bt_property_t prop_test_scan_mode_conn = {
+	.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.val = &setprop_scan_mode_conn_val,
+	.len = sizeof(setprop_scan_mode_conn_val),
+};
+
+static bt_scan_mode_t test_scan_mode_none_val = BT_SCAN_MODE_NONE;
+static bt_property_t prop_test_scan_mode_none = {
+	.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.val = &test_scan_mode_none_val,
+	.len = sizeof(test_scan_mode_none_val),
+};
+static struct bt_action_data prop_test_remote_ble_scanmode_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.prop = &prop_test_scan_mode_none,
+};
+
+static bt_bdaddr_t test_bonded_dev_addr_val = {
+	.address = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 },
+};
+static bt_property_t prop_test_bonded_dev_addr = {
+	.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.val = &test_bonded_dev_addr_val,
+	.len = sizeof(test_bonded_dev_addr_val),
+};
+static struct bt_action_data prop_test_ble_bonded_dev_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+	.prop = &prop_test_bonded_dev_addr,
+};
+
+static uint32_t test_remote_timestamp_val = 42;
+static bt_property_t prop_test_remote_ble_timestamp_prop = {
+	.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+	.val = &test_remote_timestamp_val,
+	.len = sizeof(test_remote_timestamp_val),
+};
+static struct bt_action_data prop_test_remote_ble_timestamp_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+	.prop = &prop_test_remote_ble_timestamp_prop,
+};
+
+static struct bt_action_data ssp_confirm_accept_reply = {
+	.addr = &emu_remote_bdaddr_val,
+	.ssp_variant = BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+	.accept = TRUE,
+};
+
+static struct bt_action_data ssp_confirm_reject_reply = {
+	.addr = &emu_remote_bdaddr_val,
+	.ssp_variant = BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+	.accept = FALSE,
+};
+
+static  struct bt_action_data no_input_no_output_io_cap = {
+	.io_cap = 0x03,
+};
+
+static  struct bt_action_data display_yes_no_io_cap = {
+	.io_cap = 0x01,
+};
+
+static uint16_t test_conn_handle = 0;
+
+static void conn_cb(uint16_t handle, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	tester_print("New connection with handle 0x%04x", handle);
+
+	test_conn_handle = handle;
+
+	bthost_request_auth(bthost, handle);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("Bluetooth Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Enable - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_ADAPTER_PROPS(prop_emu_default_set, 8),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Enable - Success 2",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_ADAPTER_PROPS(prop_emu_default_set, 8),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Disable - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set BDNAME - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action, &prop_test_bdname),
+		CALLBACK_ADAPTER_PROPS(&prop_test_bdname, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action,
+					&prop_test_scanmode_conn_discov),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set DISCOVERY_TIMEOUT - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action, &prop_test_disctimeout),
+		CALLBACK_ADAPTER_PROPS(&prop_test_disctimeout, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get BDADDR - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_bdaddr),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_bdaddr, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get BDNAME - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_bdname),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_bdname, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set UUID - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_uuid),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set CLASS_OF_DEVICE - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_cod),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set TYPE_OF_DEVICE - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_tod),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set REMOTE_RSSI - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_remote_rssi),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set SERVICE_RECORD - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_srvc_record),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set BDADDR - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_bdaddr),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE_CONNECTABLE - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action,
+						&prop_test_scan_mode_conn),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set BONDED_DEVICES - Fail",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_FAIL(bt_set_property_action, &prop_test_bonded_dev_addr),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get CLASS_OF_DEVICE - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_cod),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_cod, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get TYPE_OF_DEVICE - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_dual_tod),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_dual_tod, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get SCAN_MODE - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_scan_mode),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_scan_mode, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get DISCOVERY_TIMEOUT - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_disc_timeout),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_disc_timeout, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get UUIDS - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_uuids),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_uuids, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Get BONDED_DEVICES - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_get_property_action, &prop_emu_bonded_devs),
+		CALLBACK_ADAPTER_PROPS(&prop_emu_bonded_devs, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE - Success 2",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action,
+						&prop_test_scan_mode_none),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_none, 1),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Discovery Start - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Discovery Start - Done",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Discovery Stop - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+	),
+	TEST_CASE_BREDRLE("Bluetooth Discovery Stop - Done",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_props_action,
+							&emu_remote_bdaddr_val),
+		CALLBACK_DEVICE_PROPS(prop_emu_remote_bles_query_set, 6),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+					&prop_emu_remote_ble_bdname_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_bdname_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+						&prop_emu_remote_ble_uuids_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_uuids_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+						&prop_emu_remote_ble_cod_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_cod_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+						&prop_emu_remote_ble_tod_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_tod_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+						&prop_emu_remote_ble_rssi_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_rssi_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+					&prop_emu_remote_ble_timestamp_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_timestamp_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_get_device_prop_action,
+					&prop_emu_remote_ble_bdaddr_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_get_device_prop_action,
+					&prop_emu_remote_ble_scan_mode_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_get_device_prop_action,
+					&prop_emu_remote_ble_bondeddev_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_get_device_prop_action,
+					&prop_emu_remote_ble_disctimeout_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_get_device_prop_action,
+					&prop_emu_remote_ble_verinfo_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_get_device_prop_action,
+						&prop_emu_remote_ble_fname_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_set_device_prop_action,
+						&prop_emu_remote_ble_fname_req),
+		ACTION_SUCCESS(bt_get_device_prop_action,
+						&prop_emu_remote_ble_fname_req),
+		CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_fname_prop, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_bdname_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_uuids_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+						&prop_test_remote_ble_cod_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+						&prop_test_remote_ble_tod_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+						&prop_test_remote_ble_rssi_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_timestamp_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_bdaddr_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_srvc_record_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_scanmode_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+						&prop_test_ble_bonded_dev_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_FAIL(bt_set_device_prop_action,
+					&prop_test_remote_ble_disc_timeout_req),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_PROPS(CB_BT_PIN_REQUEST, prop_emu_remotes_pin_req_set,
+									2),
+		ACTION_SUCCESS(bt_pin_reply_accept_action,
+							&emu_pin_set_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_PROPS(CB_BT_PIN_REQUEST, prop_emu_remotes_pin_req_set,
+									2),
+		ACTION_SUCCESS(bt_pin_reply_accept_action,
+						&emu_pin_set_invalid_req),
+		CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE,
+						&prop_emu_remote_bdadr, 1,
+						BT_STATUS_AUTH_FAILURE),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+					prop_emu_remotes_pin_req_set, 2),
+		ACTION_SUCCESS(bt_ssp_reply_accept_action,
+						&ssp_confirm_accept_reply),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+					prop_emu_remotes_pin_req_set, 2),
+		ACTION_SUCCESS(bt_ssp_reply_accept_action,
+						&ssp_confirm_reject_reply),
+		CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE,
+						&prop_emu_remote_bdadr, 1,
+						BT_STATUS_AUTH_FAILURE),
+	),
+	TEST_CASE_BREDR("Bluetooth Create Bond - No Discovery",
+		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_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+					prop_emu_remotes_pin_req_set, 2),
+		ACTION_SUCCESS(bt_ssp_reply_accept_action,
+						&ssp_confirm_accept_reply),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDR("Bluetooth Create Bond - Bad Address",
+		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(bt_create_bond_action, &prop_test_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+							&prop_test_bdaddr, 1),
+		CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE,
+							&prop_test_bdaddr, 1,
+							BT_STATUS_FAIL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+					prop_emu_remotes_pin_req_set, 2),
+		ACTION_SUCCESS(bt_cancel_bond_action, &emu_remote_bdaddr_val),
+		CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE,
+						&prop_emu_remote_bdadr, 1,
+						BT_STATUS_FAIL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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(bt_cancel_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STOPPED),
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+					prop_emu_remotes_pin_req_set, 2),
+		ACTION_SUCCESS(bt_ssp_reply_accept_action,
+						&ssp_confirm_accept_reply),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+		ACTION_SUCCESS(bt_remove_bond_action, &emu_remote_bdaddr_val),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_NONE,
+						&prop_emu_remote_bdadr, 1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDR("Bluetooth Accept Bond - Just Works - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action,
+					&prop_test_scanmode_conn_discov),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1),
+		ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+		ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+		ACTION_SUCCESS(emu_set_io_cap, &no_input_no_output_io_cap),
+		ACTION_SUCCESS(emu_set_connect_cb_action, conn_cb),
+		ACTION_SUCCESS(emu_remote_connect_hci_action, NULL),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+		ACTION_SUCCESS(bt_remove_bond_action, &emu_remote_bdaddr_val),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_NONE,
+						&prop_emu_remote_bdadr, 1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDR("Bluetooth Accept Bond - No Bond - Success",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action,
+					&prop_test_scanmode_conn_discov),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1),
+		ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+		ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+		ACTION_SUCCESS(emu_set_io_cap, &no_input_no_output_io_cap),
+		ACTION_SUCCESS(emu_set_connect_cb_action, conn_cb),
+		ACTION_SUCCESS(emu_remote_connect_hci_action, NULL),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+		ACTION_SUCCESS(emu_remote_disconnect_hci_action,
+							&test_conn_handle),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_NONE,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+};
+
+struct queue *get_bluetooth_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_bluetooth_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-gatt.c b/bluez/repo/android/tester-gatt.c
new file mode 100644
index 0000000..88be3d8
--- /dev/null
+++ b/bluez/repo/android/tester-gatt.c
@@ -0,0 +1,3678 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "lib/bluetooth.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "tester-main.h"
+
+#define ATT_HANDLE_SIZE	2
+
+#define L2CAP_ATT_ERROR			0x01
+#define L2CAP_ATT_EXCHANGE_MTU_REQ	0x02
+#define L2CAP_ATT_EXCHANGE_MTU_RSP	0x03
+#define L2CAP_ATT_FIND_BY_TYPE_REQ	0x06
+#define L2CAP_ATT_READ_REQ		0x0a
+#define L2CAP_ATT_READ_RSP		0x0b
+#define L2CAP_ATT_WRITE_REQ		0x12
+#define L2CAP_ATT_WRITE_RSP		0x13
+#define L2CAP_ATT_HANDLE_VALUE_NOTIFY	0x1b
+#define L2CAP_ATT_HANDLE_VALUE_IND	0x1d
+
+#define GATT_STATUS_SUCCESS	0x00000000
+#define GATT_STATUS_FAILURE	0x00000101
+#define GATT_STATUS_INS_AUTH	0x08
+
+#define GATT_ERR_INVAL_ATTR_VALUE_LEN	0x0D
+
+#define GATT_SERVER_DISCONNECTED	0
+#define GATT_SERVER_CONNECTED		1
+
+#define APP1_ID	1
+#define APP2_ID	2
+
+#define CONN1_ID	1
+#define CONN2_ID	2
+
+#define TRANS1_ID	1
+
+#define BT_TRANSPORT_UNKNOWN		0x00
+
+#define GATT_SERVER_TRANSPORT_LE		0x01
+#define GATT_SERVER_TRANSPORT_BREDR		0x02
+#define GATT_SERVER_TRANSPORT_LE_BREDR		(0x01 | 0x02)
+
+#define GATT_WRITE_TYPE_NO_RESPONSE	0x01
+#define GATT_WRITE_TYPE_DEFAULT		0x02
+#define GATT_WRITE_TYPE_PREPARE		0x03
+#define GATT_WRITE_TYPE_SIGNED		0x04
+
+#define CHAR_PROP_BROADCAST			0x01
+#define CHAR_PROP_READ				0x02
+#define CHAR_PROP_WRITE_WITHOUT_RESPONSE	0x04
+#define CHAR_PROP_WRITE				0x08
+#define CHAR_PROP_NOTIFY			0x10
+#define CHAR_PROP_INDICATE			0x20
+#define CHAR_PROP_AUTHENTICATED_SIGNED_WRITES	0x40
+#define CHAR_PROP_EXTENDED_PROPERTIES		0x80
+
+#define CHAR_PERM_READ			0x0001
+#define CHAR_PERM_READ_ENCRYPTED	0x0002
+#define CHAR_PERM_READ_ENCRYPTED_MITM	0x0004
+#define CHAR_PERM_WRITE			0x0010
+#define CHAR_PERM_WRITE_ENCRYPTED	0x0020
+#define CHAR_PERM_WRITE_ENCRYPTED_MITM	0x0040
+#define CHAR_PERM_WRITE_SIGNED		0x0080
+#define CHAR_PERM_WRITE_SIGNED_MITM	0x0100
+
+static struct queue *list; /* List of gatt test cases */
+
+static uint16_t srvc1_handle;
+static uint16_t inc_srvc1_handle;
+static uint16_t char1_handle;
+
+static struct iovec char1_handle_v = {
+	.iov_base = &char1_handle,
+	.iov_len = sizeof(char1_handle),
+};
+
+struct set_att_data {
+	char *to;
+	char *from;
+	int len;
+};
+
+struct att_write_req_data {
+	uint16_t *attr_handle;
+	uint8_t *value;
+};
+
+static bt_uuid_t app1_uuid = {
+	.uu = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+				0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
+};
+
+static bt_uuid_t app2_uuid = {
+	.uu = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+				0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 },
+};
+
+static uint8_t value_1[] = {0x01};
+
+static uint8_t att_write_req_value_1[] = {0x00, 0x01, 0x02, 0x03};
+static struct iovec att_write_req_value_1_v = {
+	.iov_base = att_write_req_value_1,
+	.iov_len = sizeof(att_write_req_value_1),
+};
+
+struct gatt_connect_data {
+	const int app_id;
+	const int conn_id;
+};
+
+struct gatt_search_service_data {
+	const int conn_id;
+	bt_uuid_t *filter_uuid;
+};
+
+struct get_char_data {
+	const int conn_id;
+	btgatt_srvc_id_t *service;
+};
+
+struct get_desc_data {
+	const int conn_id;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *characteristic;
+	btgatt_gatt_id_t *desc;
+};
+
+struct get_incl_data {
+	const int conn_id;
+	btgatt_srvc_id_t *service;
+	btgatt_srvc_id_t *start_service;
+};
+
+struct read_char_data {
+	const int conn_id;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *characteristic;
+	int auth_req;
+};
+
+struct read_desc_data {
+	const int conn_id;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *characteristic;
+	btgatt_gatt_id_t *descriptor;
+	int auth_req;
+};
+
+struct write_char_data {
+	int conn_id;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *characteristic;
+	int write_type;
+	int len;
+	int auth_req;
+	char *p_value;
+};
+
+struct write_desc_data {
+	int conn_id;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *characteristic;
+	btgatt_gatt_id_t *descriptor;
+	int write_type;
+	int len;
+	int auth_req;
+	char *p_value;
+};
+
+struct notif_data {
+	int conn_id;
+	const bt_bdaddr_t *bdaddr;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *charac;
+};
+
+struct add_service_data {
+	int app_id;
+	btgatt_srvc_id_t *service;
+	int num_handles;
+};
+
+struct add_included_service_data {
+	int app_id;
+	uint16_t *inc_srvc_handle;
+	uint16_t *srvc_handle;
+};
+struct add_char_data {
+	int app_id;
+	uint16_t *srvc_handle;
+	bt_uuid_t *uuid;
+	int properties;
+	int permissions;
+};
+
+struct add_desc_data {
+	int app_id;
+	uint16_t *srvc_handle;
+	bt_uuid_t *uuid;
+	int permissions;
+};
+
+struct start_srvc_data {
+	int app_id;
+	uint16_t *srvc_handle;
+	int transport;
+};
+
+struct stop_srvc_data {
+	int app_id;
+	uint16_t *srvc_handle;
+};
+
+struct delete_srvc_data {
+	int app_id;
+	uint16_t *srvc_handle;
+};
+
+struct send_indication_data {
+	int app_id;
+	uint16_t *attr_handle;
+	int conn_id;
+	int len;
+	int confirm;
+	char *p_value;
+};
+
+struct send_resp_data {
+	int conn_id;
+	int trans_id;
+	int status;
+	btgatt_response_t *response;
+};
+
+static bt_bdaddr_t emu_remote_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 },
+};
+static bt_device_type_t emu_remote_ble_device_type = BT_DEVICE_DEVTYPE_BLE;
+
+static bt_property_t prop_emu_remotes_default_set[] = {
+	{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+						&emu_remote_bdaddr_val },
+};
+static bt_property_t prop_emu_remotes_default_le_set[] = {
+	{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+						&emu_remote_bdaddr_val },
+	{ BT_PROPERTY_TYPE_OF_DEVICE, sizeof(bt_device_type_t),
+						&emu_remote_ble_device_type },
+};
+
+static struct bt_action_data prop_test_remote_ble_bdaddr_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDADDR,
+	.prop = &prop_emu_remotes_default_set[0],
+};
+
+static bt_scan_mode_t setprop_scan_mode_conn_val =
+					BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
+
+static bt_property_t prop_test_scan_mode_conn = {
+	.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.val = &setprop_scan_mode_conn_val,
+	.len = sizeof(setprop_scan_mode_conn_val),
+};
+
+static struct emu_l2cap_cid_data cid_data;
+
+static struct gatt_connect_data app1_conn_req = {
+	.app_id = APP1_ID,
+	.conn_id = CONN1_ID,
+};
+
+static struct gatt_connect_data app1_conn2_req = {
+	.app_id = APP1_ID,
+	.conn_id = CONN2_ID,
+};
+
+static struct gatt_connect_data app2_conn_req = {
+	.app_id = APP2_ID,
+	.conn_id = CONN2_ID,
+};
+
+static struct gatt_search_service_data search_services_1 = {
+	.conn_id = CONN1_ID,
+	.filter_uuid = NULL,
+};
+
+static const struct iovec exchange_mtu_req_pdu = raw_pdu(0x02, 0xa0, 0x02);
+static const struct iovec exchange_mtu_resp_pdu = raw_pdu(0x03, 0xa0, 0x02);
+
+static struct bt_action_data bearer_type = {
+	.bearer_type = BDADDR_LE_PUBLIC,
+};
+
+static btgatt_srvc_id_t service_1 = {
+	.is_primary = true,
+	.id = {
+		.inst_id = 0,
+		.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00,  0x00, 0x18, 0x00, 0x00}
+	}
+};
+
+static btgatt_srvc_id_t service_2 = {
+	.is_primary = true,
+	.id = {
+		.inst_id = 1,
+		.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00,  0x01, 0x18, 0x00, 0x00},
+	}
+};
+
+static btgatt_srvc_id_t service_add_1 = {
+	.is_primary = true,
+	.id = {
+		.inst_id = 0,
+		.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0xFF, 0xEF, 0x00, 0x00},
+	}
+};
+
+static btgatt_srvc_id_t service_add_2 = {
+	.is_primary = true,
+	.id = {
+		.inst_id = 1,
+		.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0xFF, 0xDF, 0x00, 0x00},
+	}
+};
+
+static btgatt_srvc_id_t service_add_3 = {
+	.is_primary = true,
+	.id = {
+		.inst_id = 2,
+		.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0xFF, 0xCF, 0x00, 0x00},
+	}
+};
+
+static btgatt_srvc_id_t included_1 = {
+	.is_primary = false,
+	.id = {
+		.inst_id = 1,
+		.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00,  0xff, 0xfe, 0x00, 0x00},
+	}
+};
+
+static btgatt_srvc_id_t included_2 = {
+	.is_primary = false,
+	.id = {
+		.inst_id = 1,
+		.uuid.uu = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+				0x08, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
+	}
+};
+
+static btgatt_gatt_id_t characteristic_1 = {
+	.inst_id = 1,
+	.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00,  0x19, 0x00, 0x00, 0x00}
+};
+
+static btgatt_gatt_id_t desc_1 = {
+	.inst_id = 1,
+	.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00,  0x00, 0x29, 0x00, 0x00}
+};
+
+static btgatt_gatt_id_t desc_2 = {
+	.inst_id = 2,
+	.uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00,  0x01, 0x29, 0x00, 0x00}
+};
+
+static btgatt_read_params_t read_params_1;
+static btgatt_write_params_t write_params_1;
+static btgatt_notify_params_t notify_params_1;
+
+static struct get_char_data get_char_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1
+};
+
+static struct get_char_data get_char_data_2 = {
+	.conn_id = CONN1_ID,
+	.service = &service_2
+};
+
+static struct get_desc_data get_desc_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+};
+
+static struct get_desc_data get_desc_data_2 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.desc = &desc_1,
+};
+
+static struct read_char_data read_char_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+};
+
+static struct read_char_data read_char_data_2 = {
+	.conn_id = CONN1_ID,
+	.service = &service_2,
+	.characteristic = &characteristic_1,
+};
+
+static struct read_desc_data read_desc_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.descriptor = &desc_1,
+};
+
+static struct read_desc_data read_desc_data_2 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.descriptor = &desc_2,
+};
+
+static struct get_incl_data get_incl_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1
+};
+
+static char value_2[] = {0x00, 0x01, 0x02, 0x03};
+
+static struct write_char_data write_char_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.write_type = GATT_WRITE_TYPE_NO_RESPONSE,
+	.len = sizeof(value_2),
+	.p_value = value_2,
+	.auth_req = 0
+};
+
+static struct write_char_data write_char_data_2 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.write_type = GATT_WRITE_TYPE_DEFAULT,
+	.len = sizeof(value_2),
+	.p_value = value_2,
+	.auth_req = 0
+};
+
+static struct write_desc_data write_desc_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.descriptor = &desc_1,
+	.write_type = 2,
+	.len = sizeof(value_2),
+	.auth_req = 0,
+	.p_value = value_2,
+};
+
+static struct write_desc_data write_desc_data_2 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.characteristic = &characteristic_1,
+	.descriptor = &desc_2,
+	.write_type = 2,
+	.len = sizeof(value_2),
+	.auth_req = 0,
+	.p_value = value_2,
+};
+
+static struct notif_data notif_data_1 = {
+	.conn_id = CONN1_ID,
+	.service = &service_1,
+	.charac = &characteristic_1,
+	.bdaddr = &emu_remote_bdaddr_val,
+};
+
+static struct add_service_data add_service_data_1 = {
+	.app_id = APP1_ID,
+	.service = &service_add_1,
+	.num_handles = 1
+};
+
+static struct add_service_data add_service_data_2 = {
+	.app_id = APP1_ID,
+	.service = &service_add_2,
+	.num_handles = 1
+};
+
+static struct add_service_data add_service_data_3 = {
+	.app_id = APP1_ID,
+	.service = &service_add_3,
+	.num_handles = 1
+};
+
+static struct add_service_data add_service_data_4 = {
+	.app_id = APP1_ID,
+	.service = &service_add_1,
+	.num_handles = 2
+};
+
+static struct add_service_data add_service_data_5 = {
+	.app_id = APP1_ID,
+	.service = &service_add_1,
+	.num_handles = 3
+};
+
+static struct add_service_data add_service_data_6 = {
+	.app_id = APP1_ID,
+	.service = &service_add_1,
+	.num_handles = 4
+};
+
+static struct add_service_data add_bad_service_data_1 = {
+	.app_id = APP1_ID,
+	.service = &service_add_1,
+	.num_handles = 0
+};
+
+static struct add_service_data add_sec_service_data_1 = {
+	.app_id = APP1_ID,
+	.service = &included_1,
+	.num_handles = 1
+};
+
+static uint16_t srvc_bad_handle = 0xffff;
+
+static struct add_included_service_data add_inc_service_data_1 = {
+	.app_id = APP1_ID,
+	.inc_srvc_handle = &inc_srvc1_handle,
+	.srvc_handle = &srvc1_handle
+};
+
+static struct add_included_service_data add_bad_inc_service_data_1 = {
+	.app_id = APP1_ID,
+	.inc_srvc_handle = &srvc_bad_handle,
+	.srvc_handle = &srvc1_handle
+};
+
+static struct add_char_data add_char_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle,
+	.uuid = &app1_uuid,
+	.properties = 0,
+	.permissions = 0
+};
+
+static struct add_char_data add_char_data_2 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle,
+	.uuid = &app1_uuid,
+	.properties = CHAR_PROP_WRITE,
+	.permissions = CHAR_PERM_WRITE
+};
+
+static struct add_char_data add_bad_char_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc_bad_handle,
+	.uuid = &app1_uuid,
+	.properties = 0,
+	.permissions = 0
+};
+
+static struct add_desc_data add_bad_desc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc_bad_handle,
+	.uuid = &app2_uuid,
+	.permissions = 0
+};
+
+static struct add_desc_data add_bad_desc_data_2 = {
+	.app_id = APP2_ID,
+	.srvc_handle = &srvc1_handle,
+	.uuid = &app2_uuid,
+	.permissions = 0
+};
+
+static struct add_desc_data add_desc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle,
+	.uuid = &app2_uuid,
+	.permissions = 0
+};
+
+static struct start_srvc_data start_srvc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle,
+	.transport = GATT_SERVER_TRANSPORT_LE_BREDR
+};
+
+static struct start_srvc_data start_srvc_data_2 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle,
+	.transport = GATT_SERVER_TRANSPORT_LE
+};
+
+static struct start_srvc_data start_bad_srvc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc_bad_handle,
+	.transport = GATT_SERVER_TRANSPORT_LE
+};
+
+static struct start_srvc_data start_bad_srvc_data_2 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle,
+	.transport = 0
+};
+
+static struct stop_srvc_data stop_srvc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle
+};
+
+static struct stop_srvc_data stop_bad_srvc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc_bad_handle
+};
+
+static struct delete_srvc_data delete_srvc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc1_handle
+};
+
+static struct delete_srvc_data delete_bad_srvc_data_1 = {
+	.app_id = APP1_ID,
+	.srvc_handle = &srvc_bad_handle
+};
+
+static uint16_t srvc_indication_handle_1 = 0x01;
+
+static struct send_indication_data send_indication_data_1 = {
+	.app_id = APP1_ID,
+	.attr_handle = &srvc_indication_handle_1,
+	.conn_id = CONN1_ID,
+	.len = sizeof(value_2),
+	.p_value = value_2,
+	.confirm = 1
+};
+
+static struct send_indication_data send_indication_data_2 = {
+	.app_id = APP1_ID,
+	.attr_handle = &srvc_indication_handle_1,
+	.conn_id = CONN1_ID,
+	.len = sizeof(value_2),
+	.p_value = value_2,
+	.confirm = 0
+};
+
+static struct send_indication_data send_bad_indication_data_1 = {
+	.app_id = APP1_ID,
+	.attr_handle = &srvc_indication_handle_1,
+	.conn_id = CONN2_ID,
+	.len = sizeof(value_2),
+	.p_value = value_2,
+	.confirm = 0
+};
+
+struct set_read_params {
+	btgatt_read_params_t *params;
+	btgatt_srvc_id_t *srvc_id;
+	btgatt_gatt_id_t *char_id;
+	btgatt_gatt_id_t *descr_id;
+	uint8_t *value;
+	uint16_t len;
+	uint16_t value_type;
+	uint8_t status;
+};
+
+struct set_write_params {
+	btgatt_write_params_t *params;
+	btgatt_srvc_id_t *srvc_id;
+	btgatt_gatt_id_t *char_id;
+	btgatt_gatt_id_t *descr_id;
+	uint8_t status;
+};
+
+struct set_notify_params {
+	btgatt_notify_params_t *params;
+	uint8_t *value;
+	uint16_t len;
+	uint8_t is_notify;
+	btgatt_srvc_id_t *srvc_id;
+	btgatt_gatt_id_t *char_id;
+	bt_bdaddr_t *bdaddr;
+};
+
+static struct set_read_params set_read_param_1 = {
+	.params = &read_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.value = value_1,
+	.len = sizeof(value_1),
+	.status = BT_STATUS_SUCCESS
+};
+
+static struct set_read_params set_read_param_2 = {
+	.params = &read_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.status = GATT_STATUS_INS_AUTH
+};
+
+static struct set_read_params set_read_param_3 = {
+	.params = &read_params_1,
+	.srvc_id = &service_2,
+	.char_id = &characteristic_1,
+	.status = BT_STATUS_FAIL
+};
+
+static struct set_read_params set_read_param_4 = {
+	.params = &read_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.descr_id = &desc_1,
+	.value = value_1,
+	.len = sizeof(value_1),
+	.status = BT_STATUS_SUCCESS
+};
+
+static struct set_read_params set_read_param_5 = {
+	.params = &read_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.descr_id = &desc_1,
+	.status = GATT_STATUS_INS_AUTH
+};
+
+static struct set_read_params set_read_param_6 = {
+	.params = &read_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.descr_id = &desc_2,
+	.status = BT_STATUS_FAIL
+};
+
+static struct set_write_params set_write_param_1 = {
+	.params = &write_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.status = BT_STATUS_SUCCESS
+};
+
+static struct set_write_params set_write_param_2 = {
+	.params = &write_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.status = GATT_STATUS_INS_AUTH
+};
+
+static struct set_write_params set_write_param_3 = {
+	.params = &write_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.status = BT_STATUS_FAIL
+};
+
+static struct set_write_params set_write_param_4 = {
+	.params = &write_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.descr_id = &desc_1,
+	.status = BT_STATUS_SUCCESS
+};
+
+static struct set_write_params set_write_param_5 = {
+	.params = &write_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.descr_id = &desc_2,
+	.status = BT_STATUS_FAIL
+};
+
+static struct set_write_params set_write_param_6 = {
+	.params = &write_params_1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.descr_id = &desc_1,
+	.status = GATT_STATUS_INS_AUTH
+};
+
+static struct set_notify_params set_notify_param_1 = {
+	.params = &notify_params_1,
+	.value = value_1,
+	.len = sizeof(value_1),
+	.is_notify = 0,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.bdaddr = &emu_remote_bdaddr_val
+};
+
+static struct set_notify_params set_notify_param_2 = {
+	.params = &notify_params_1,
+	.value = value_1,
+	.len = sizeof(value_1),
+	.is_notify = 1,
+	.srvc_id = &service_1,
+	.char_id = &characteristic_1,
+	.bdaddr = &emu_remote_bdaddr_val
+};
+
+static btgatt_response_t response_1 = {
+	.handle = 0x1c,
+	.attr_value.auth_req = 0,
+	.attr_value.handle = 0x1d,
+	.attr_value.len = 0,
+	.attr_value.offset = 0,
+};
+
+static btgatt_response_t response_2 = {
+	.handle = 0x1c,
+	.attr_value.auth_req = 0,
+	.attr_value.handle = 0x1d,
+	.attr_value.len = sizeof(att_write_req_value_1),
+	.attr_value.offset = 0,
+};
+
+static struct send_resp_data send_resp_data_1 = {
+	.conn_id = CONN1_ID,
+	.trans_id = TRANS1_ID,
+	.status = BT_STATUS_SUCCESS,
+	.response = &response_1,
+};
+
+static struct send_resp_data send_resp_data_2 = {
+	.conn_id = CONN1_ID,
+	.trans_id = TRANS1_ID,
+	.status = BT_STATUS_SUCCESS,
+	.response = &response_2,
+};
+
+static struct send_resp_data send_resp_data_2_error = {
+	.conn_id = CONN1_ID,
+	.trans_id = TRANS1_ID,
+	.status = GATT_ERR_INVAL_ATTR_VALUE_LEN,
+	.response = &response_2,
+};
+
+#define SEARCH_SERVICE_SINGLE_SUCCESS_PDUS				\
+	raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),		\
+	raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18),	\
+	raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28),		\
+	raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a)
+
+#define READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS				\
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),		\
+	raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x19, 0x00),	\
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),		\
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)
+
+static struct iovec search_service[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	end_pdu
+};
+
+static struct iovec search_service_2[] = {
+	raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+	raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18),
+	raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28),
+	raw_pdu(0x11, 0x06, 0x11, 0x00, 0x20, 0x00, 0x01, 0x18),
+	raw_pdu(0x10, 0x21, 0x00, 0xff, 0xff, 0x00, 0x28),
+	raw_pdu(0x01, 0x10, 0x21, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec search_service_3[] = {
+	raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+	raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec search_service_4[] = {
+	raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+	raw_pdu(0x11, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18),
+	end_pdu
+};
+
+static struct iovec get_characteristic_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	end_pdu
+};
+
+static struct iovec get_characteristic_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x09, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x19, 0x00),
+	end_pdu
+};
+
+static struct iovec get_descriptor_0[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x00, 0x00, 0x00, 0x29),
+	end_pdu
+};
+
+static struct iovec get_descriptor_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29),
+	raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec get_descriptor_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29, 0x05, 0x00, 0x01, 0x29),
+	raw_pdu(0x04, 0x06, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x06, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec get_descriptor_3[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x01, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec get_included_0[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),
+	raw_pdu(0x09, 0x08, 0x00, 0x00, 0x15, 0x00, 0x19, 0x00, 0xff, 0xfe),
+	end_pdu
+};
+
+static struct iovec get_included_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),
+	raw_pdu(0x09, 0x08, 0x02, 0x00, 0x15, 0x00, 0x19, 0x00, 0xff, 0xfe),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x02, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec get_included_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),
+	raw_pdu(0x09, 0x06, 0x02, 0x00, 0x15, 0x00, 0x19, 0x00),
+	raw_pdu(0x0a, 0x15, 0x00),
+	raw_pdu(0x0b, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+				0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x02, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec get_included_3[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),
+	raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+	end_pdu
+};
+
+static struct iovec read_characteristic_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	raw_pdu(0x0a, 0x03, 0x00),
+	raw_pdu(0x0b, 0x01),
+	end_pdu
+};
+
+static struct iovec read_characteristic_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	raw_pdu(0x0a, 0x03, 0x00),
+	raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08),
+	end_pdu
+};
+
+static struct iovec read_descriptor_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29),
+	raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a),
+	raw_pdu(0x0a, 0x04, 0x00),
+	raw_pdu(0x0b, 0x01),
+	end_pdu
+};
+
+static struct iovec read_descriptor_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29),
+	raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a),
+	raw_pdu(0x0a, 0x04, 0x00),
+	raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x08),
+	end_pdu
+};
+
+static struct iovec write_characteristic_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	raw_pdu(0x52, 0x03, 0x00, 0x00, 0x01, 0x02, 0x03),
+	end_pdu
+};
+
+static struct iovec write_characteristic_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	raw_pdu(0x12, 0x03, 0x00, 0x00, 0x01, 0x02, 0x03),
+	raw_pdu(0x13),
+	end_pdu
+};
+
+static struct iovec write_characteristic_3[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x03, 0x00, 0x19, 0x00),
+	raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),
+	raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
+	raw_pdu(0x12, 0x03, 0x00, 0x00, 0x01, 0x02, 0x03),
+	raw_pdu(0x01, 0x12, 0x03, 0x00, 0x08),
+	end_pdu
+};
+
+static struct iovec write_descriptor_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29),
+	raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a),
+	raw_pdu(0x12, 0x04, 0x00, 0x00, 0x01, 0x02, 0x03),
+	raw_pdu(0x13),
+	end_pdu
+};
+
+static struct iovec write_descriptor_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+	raw_pdu(0x05, 0x01, 0x04, 0x00, 0x00, 0x29),
+	raw_pdu(0x04, 0x05, 0x00, 0x10, 0x00),
+	raw_pdu(0x01, 0x04, 0x05, 0x00, 0x0a),
+	raw_pdu(0x12, 0x04, 0x00, 0x00, 0x01, 0x02, 0x03),
+	raw_pdu(0x01, 0x12, 0x04, 0x00, 0x08),
+	end_pdu
+};
+
+static struct iovec notification_1[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	end_pdu
+};
+
+static struct iovec notification_2[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x1d, 0x03, 0x00, 0x01),
+	raw_pdu(0x1e),
+	end_pdu
+};
+
+static struct iovec notification_3[] = {
+	SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+	READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+	raw_pdu(0x1b, 0x03, 0x00, 0x01),
+	end_pdu
+};
+
+static struct iovec send_indication_1[] = {
+	raw_pdu(0x1d, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03),
+	raw_pdu(0x1e),
+	end_pdu
+};
+
+static struct iovec send_notification_1[] = {
+	raw_pdu(0x1b, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03),
+	end_pdu
+};
+
+static struct iovec search_range_1[] = {
+	raw_pdu(0x01, 0xff, 0xff, 0xff),
+	end_pdu
+};
+
+static struct iovec primary_type = raw_pdu(0x00, 0x28);
+
+/* att commands define raw pdus */
+static struct iovec att_read_req_op_v = raw_pdu(L2CAP_ATT_READ_REQ);
+static struct iovec att_write_req_op_v = raw_pdu(L2CAP_ATT_WRITE_REQ);
+static struct iovec att_find_by_type_req_op_v =
+					raw_pdu(L2CAP_ATT_FIND_BY_TYPE_REQ);
+
+static struct iovec svc_change_ccc_handle_v = raw_pdu(0x1c, 0x00);
+static struct iovec svc_change_ccc_value_v = raw_pdu(0x00, 0x01);
+
+static void gatt_client_register_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_uuid_t *app_uuid = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	if (!app_uuid) {
+		tester_warn("No app uuid provided for register action.");
+		return;
+	}
+
+	step->action_status = data->if_gatt->client->register_client(app_uuid);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_unregister_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	int32_t cl_id = PTR_TO_INT(current_data_step->set_data);
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->unregister_client(cl_id);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_start_scan_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->scan(TRUE);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_stop_scan_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->scan(FALSE);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct gatt_connect_data *conn_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->connect(
+						conn_data->app_id,
+						&emu_remote_bdaddr_val, 0,
+						BT_TRANSPORT_UNKNOWN);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_disconnect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct gatt_connect_data *conn_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->disconnect(
+							conn_data->app_id,
+							&emu_remote_bdaddr_val,
+							conn_data->conn_id);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_do_listen_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct gatt_connect_data *conn_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->listen(
+							conn_data->app_id,
+							1);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_stop_listen_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct gatt_connect_data *conn_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->client->listen(
+							conn_data->app_id,
+							0);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_get_characteristic_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct get_char_data *get_char = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->get_characteristic(get_char->conn_id,
+						get_char->service, NULL);
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_get_descriptor_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct get_desc_data *get_desc = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->get_descriptor(get_desc->conn_id, get_desc->service,
+						get_desc->characteristic,
+						get_desc->desc);
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_get_included_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct get_incl_data *get_incl = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->get_included_service(get_incl->conn_id,
+				get_incl->service, get_incl->start_service);
+
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_read_characteristic_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct read_char_data *read_char_data = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->read_characteristic(read_char_data->conn_id,
+			read_char_data->service, read_char_data->characteristic,
+			read_char_data->auth_req);
+
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_read_descriptor_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct read_desc_data *read_desc_data = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->read_descriptor(read_desc_data->conn_id,
+			read_desc_data->service, read_desc_data->characteristic,
+			read_desc_data->descriptor,
+			read_desc_data->auth_req);
+
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_write_characteristic_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct write_char_data *write_char_data = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->write_characteristic(write_char_data->conn_id,
+						write_char_data->service,
+						write_char_data->characteristic,
+						write_char_data->write_type,
+						write_char_data->len,
+						write_char_data->auth_req,
+						write_char_data->p_value);
+
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_register_for_notification_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct notif_data *notif_data = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->register_for_notification(notif_data->conn_id,
+							notif_data->bdaddr,
+							notif_data->service,
+							notif_data->charac);
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_deregister_for_notification_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct notif_data *notif_data = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->deregister_for_notification(notif_data->conn_id,
+							notif_data->bdaddr,
+							notif_data->service,
+							notif_data->charac);
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_register_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_uuid_t *app_uuid = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	if (!app_uuid) {
+		tester_warn("No app uuid provided for register action.");
+		return;
+	}
+
+	step->action_status = data->if_gatt->server->register_server(app_uuid);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_unregister_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	int32_t sr_id = PTR_TO_INT(current_data_step->set_data);
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->unregister_server(sr_id);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct gatt_connect_data *conn_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->connect(
+						conn_data->app_id,
+						&emu_remote_bdaddr_val, 0,
+						BT_TRANSPORT_UNKNOWN);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_disconnect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct gatt_connect_data *conn_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->disconnect(
+							conn_data->app_id,
+							&emu_remote_bdaddr_val,
+							conn_data->conn_id);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_add_service_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct add_service_data *add_srvc_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->add_service(
+						add_srvc_data->app_id,
+						add_srvc_data->service,
+						add_srvc_data->num_handles);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_add_inc_service_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct add_included_service_data *add_inc_srvc_data =
+						current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->add_included_service(
+					add_inc_srvc_data->app_id,
+					*add_inc_srvc_data->srvc_handle,
+					*add_inc_srvc_data->inc_srvc_handle);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_add_char_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct add_char_data *add_char_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->add_characteristic(
+						add_char_data->app_id,
+						*add_char_data->srvc_handle,
+						add_char_data->uuid,
+						add_char_data->properties,
+						add_char_data->permissions);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_add_desc_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct add_desc_data *add_desc_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->add_descriptor(
+						add_desc_data->app_id,
+						*add_desc_data->srvc_handle,
+						add_desc_data->uuid,
+						add_desc_data->permissions);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_client_write_descriptor_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct write_desc_data *write_desc_data = current_data_step->set_data;
+	const btgatt_client_interface_t *client = data->if_gatt->client;
+	struct step *step = g_new0(struct step, 1);
+	int status;
+
+	status = client->write_descriptor(write_desc_data->conn_id,
+						write_desc_data->service,
+						write_desc_data->characteristic,
+						write_desc_data->descriptor,
+						write_desc_data->write_type,
+						write_desc_data->len,
+						write_desc_data->auth_req,
+						write_desc_data->p_value);
+
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_start_srvc_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct start_srvc_data *start_srvc_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->start_service(
+						start_srvc_data->app_id,
+						*start_srvc_data->srvc_handle,
+						start_srvc_data->transport);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_stop_srvc_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct stop_srvc_data *stop_srvc_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->stop_service(
+						stop_srvc_data->app_id,
+						*stop_srvc_data->srvc_handle);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_delete_srvc_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct delete_srvc_data *delete_srvc_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->delete_service(
+						delete_srvc_data->app_id,
+						*delete_srvc_data->srvc_handle);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_send_indication_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct send_indication_data *send_indication_data =
+						current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->send_indication(
+					send_indication_data->app_id,
+					*send_indication_data->attr_handle,
+					send_indication_data->conn_id,
+					send_indication_data->len,
+					send_indication_data->confirm,
+					send_indication_data->p_value);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_server_send_response_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct send_resp_data *send_resp_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_gatt->server->send_response(
+						send_resp_data->conn_id,
+						send_resp_data->trans_id,
+						send_resp_data->status,
+						send_resp_data->response);
+
+	schedule_action_verification(step);
+}
+
+static void gatt_cid_hook_cb(const void *data, uint16_t len, void *user_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+	const uint8_t *pdu = data;
+	struct iovec *gatt_pdu = queue_peek_head(t_data->pdus);
+	struct step *step;
+
+	tester_debug("Received att pdu with opcode 0x%02x", pdu[0]);
+
+	switch (pdu[0]) {
+	case L2CAP_ATT_ERROR:
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_ATT_ERROR;
+		step->callback_result.error = pdu[4];
+
+		schedule_callback_verification(step);
+		break;
+	case L2CAP_ATT_EXCHANGE_MTU_REQ:
+		tester_print("Exchange MTU request received.");
+
+		if (!memcmp(exchange_mtu_req_pdu.iov_base, pdu, len))
+			bthost_send_cid_v(bthost, cid_data->handle,
+						cid_data->cid,
+						&exchange_mtu_resp_pdu, 1);
+
+		break;
+	case L2CAP_ATT_EXCHANGE_MTU_RSP:
+		tester_print("Exchange MTU response received.");
+
+		break;
+	case L2CAP_ATT_HANDLE_VALUE_IND:
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_VALUE_INDICATION;
+
+		schedule_callback_verification(step);
+		goto respond;
+	case L2CAP_ATT_HANDLE_VALUE_NOTIFY:
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_VALUE_NOTIFICATION;
+
+		schedule_callback_verification(step);
+		break;
+	case L2CAP_ATT_READ_RSP:
+		/* TODO - More complicated cases should also verify pdu data */
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_READ_RESPONSE;
+
+		schedule_callback_verification(step);
+		break;
+	case L2CAP_ATT_WRITE_RSP:
+		/* TODO - More complicated cases should also verify pdu data */
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_WRITE_RESPONSE;
+
+		schedule_callback_verification(step);
+		break;
+	default:
+		if (!gatt_pdu || !gatt_pdu->iov_base) {
+			tester_print("Unknown ATT packet.");
+			break;
+		}
+
+		if (gatt_pdu->iov_len != len) {
+			tester_print("Size of incoming frame is not valid");
+			tester_print("Expected size = %zd incoming size = %d",
+							gatt_pdu->iov_len, len);
+			break;
+		}
+
+respond:
+		if (memcmp(gatt_pdu->iov_base, data, len)) {
+			tester_print("Incoming data mismatch");
+			break;
+		}
+		queue_pop_head(t_data->pdus);
+		gatt_pdu = queue_pop_head(t_data->pdus);
+		if (!gatt_pdu || !gatt_pdu->iov_base)
+			break;
+
+		bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid,
+								gatt_pdu, 1);
+
+		break;
+	}
+}
+
+static void gatt_remote_send_frame_action(void)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	struct iovec *gatt_pdu = queue_pop_head(t_data->pdus);
+	struct step *step = g_new0(struct step, 1);
+
+	if (!gatt_pdu) {
+		tester_print("No frame to send");
+		step->action_status = BT_STATUS_FAIL;
+	} else {
+		bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid,
+								gatt_pdu, 1);
+		step->action_status = BT_STATUS_SUCCESS;
+	}
+
+	schedule_action_verification(step);
+}
+
+static void gatt_remote_send_raw_pdu_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct iovec *pdu = current_data_step->set_data;
+	struct iovec *pdu2 = current_data_step->set_data_2;
+	struct iovec *pdu3 = current_data_step->set_data_3;
+	struct step *step = g_new0(struct step, 1);
+
+	if (cid_data.handle && cid_data.cid) {
+		struct iovec rsp[3];
+		size_t len = 0;
+
+		if (!pdu) {
+			step->action_status = BT_STATUS_FAIL;
+			goto done;
+		}
+
+		rsp[0].iov_base = pdu->iov_base;
+		rsp[0].iov_len = pdu->iov_len;
+		len++;
+
+		if (pdu2) {
+			rsp[1].iov_base = pdu2->iov_base;
+			rsp[1].iov_len = pdu2->iov_len;
+			len++;
+		}
+
+		if (pdu3) {
+			rsp[2].iov_base = pdu3->iov_base;
+			rsp[2].iov_len = pdu3->iov_len;
+			len++;
+		}
+
+		bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid, rsp,
+									len);
+		step->action_status = BT_STATUS_SUCCESS;
+	} else {
+		tester_debug("No connection set up");
+		step->action_status = BT_STATUS_FAIL;
+	}
+
+done:
+	schedule_action_verification(step);
+}
+
+static void gatt_conn_cb(uint16_t handle, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	tester_print("New connection with handle 0x%04x", handle);
+
+	if (data->hciemu_type == HCIEMU_TYPE_BREDR) {
+		tester_warn("Not handled device type.");
+		return;
+	}
+
+	cid_data.cid = 0x0004;
+	cid_data.handle = handle;
+
+	bthost_add_cid_hook(bthost, handle, cid_data.cid, gatt_cid_hook_cb,
+								&cid_data);
+}
+
+static void gatt_client_search_services(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct step *step = g_new0(struct step, 1);
+	struct gatt_search_service_data *search_data;
+	int status;
+
+	search_data = current_data_step->set_data;
+
+	status = data->if_gatt->client->search_service(search_data->conn_id,
+						search_data->filter_uuid);
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static void init_pdus(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct step *step = g_new0(struct step, 1);
+	struct iovec *pdu = current_data_step->set_data;
+
+	while (pdu->iov_base) {
+		queue_push_tail(data->pdus, pdu);
+		pdu++;
+	}
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+static void init_read_params_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct step *step = g_new0(struct step, 1);
+	struct set_read_params *set_param_data = current_data_step->set_data;
+	btgatt_read_params_t *param = set_param_data->params;
+
+	memset(param, 0, sizeof(*param));
+
+	if (set_param_data->srvc_id)
+		memcpy(&param->srvc_id, set_param_data->srvc_id,
+						sizeof(btgatt_srvc_id_t));
+
+	if (set_param_data->char_id)
+		memcpy(&param->char_id, set_param_data->char_id,
+						sizeof(btgatt_gatt_id_t));
+
+	if (set_param_data->descr_id)
+		memcpy(&param->descr_id, set_param_data->descr_id,
+						sizeof(btgatt_gatt_id_t));
+
+	param->value_type = set_param_data->value_type;
+	param->status = set_param_data->status;
+	param->value.len = set_param_data->len;
+
+	if (param->value.len != 0)
+		memcpy(&param->value.value, set_param_data->value,
+							param->value.len);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+static void init_write_params_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct step *step = g_new0(struct step, 1);
+	struct set_write_params *set_param_data = current_data_step->set_data;
+	btgatt_write_params_t *param = set_param_data->params;
+
+	memset(param, 0, sizeof(*param));
+
+	if (set_param_data->srvc_id)
+		memcpy(&param->srvc_id, set_param_data->srvc_id,
+						sizeof(btgatt_srvc_id_t));
+
+	if (set_param_data->char_id)
+		memcpy(&param->char_id, set_param_data->char_id,
+						sizeof(btgatt_gatt_id_t));
+
+	if (set_param_data->descr_id)
+		memcpy(&param->descr_id, set_param_data->descr_id,
+						sizeof(btgatt_gatt_id_t));
+
+	param->status = set_param_data->status;
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+static void init_notify_params_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct step *step = g_new0(struct step, 1);
+	struct set_notify_params *set_param_data = current_data_step->set_data;
+	btgatt_notify_params_t *param = set_param_data->params;
+
+	memset(param, 0, sizeof(*param));
+
+	if (set_param_data->srvc_id)
+		memcpy(&param->srvc_id, set_param_data->srvc_id,
+						sizeof(btgatt_srvc_id_t));
+
+	if (set_param_data->char_id)
+		memcpy(&param->char_id, set_param_data->char_id,
+						sizeof(btgatt_gatt_id_t));
+
+	param->len = set_param_data->len;
+	param->is_notify = set_param_data->is_notify;
+
+	memcpy(&param->bda, set_param_data->bdaddr, sizeof(bt_bdaddr_t));
+	if (param->len != 0)
+		memcpy(&param->value, set_param_data->value, param->len);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("Gatt Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Register",
+		ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Unregister",
+		ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_client_unregister_action,
+							INT_TO_PTR(APP1_ID)),
+		ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+	),
+	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),
+		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),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		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),
+		ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		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),
+		ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_disconnect_action,
+							&app1_conn_req),
+		CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_connect_action, &app2_conn_req),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN2_ID, APP2_ID),
+		ACTION_SUCCESS(gatt_client_disconnect_action,
+							&app2_conn_req),
+		CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN2_ID, APP2_ID),
+		ACTION_SUCCESS(gatt_client_disconnect_action,
+							&app1_conn_req),
+		CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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(emu_set_ssp_mode_action, NULL),
+		ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+		ACTION_SUCCESS(bt_set_property_action,
+						&prop_test_scan_mode_conn),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1),
+		ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_client_do_listen_action, &app1_conn_req),
+		CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_stop_listen_action,
+							&app1_conn_req),
+		CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_client_disconnect_action,
+							&app1_conn_req),
+		CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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(emu_set_ssp_mode_action, NULL),
+		ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+		ACTION_SUCCESS(bt_set_property_action,
+						&prop_test_scan_mode_conn),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1),
+		ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_client_do_listen_action, &app1_conn_req),
+		CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_stop_listen_action,
+							&app1_conn_req),
+		CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_client_disconnect_action,
+							&app1_conn_req),
+		CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		/* Close ACL on emulated remotes side so it can reconnect */
+		ACTION_SUCCESS(emu_remote_disconnect_hci_action,
+							&cid_data.handle),
+		CALLBACK_STATE(CB_BT_ACL_STATE_CHANGED,
+						BT_ACL_STATE_DISCONNECTED),
+		ACTION_SUCCESS(gatt_client_do_listen_action, &app1_conn_req),
+		CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN2_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_disconnect_action,
+							&app1_conn2_req),
+		CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN2_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_stop_listen_action,
+							&app1_conn_req),
+		CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Search Service - Single",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Search Service - Multiple",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_1),
+		CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_2),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Search Service - None",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Search Service - Incorrect rsp",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - Single",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - Incorrect rsp",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_FAILURE,
+				CONN1_ID, &service_1, NULL, 0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - None",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_FAIL(gatt_client_get_characteristic_action,
+							&get_char_data_2),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_FAILURE,
+							CONN1_ID, &service_2,
+							NULL, 0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Incorrect rsp",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID,
+				&service_1, &characteristic_1, NULL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Single",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Multiple",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+							CONN1_ID, &service_1,
+							&characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+						&service_1, &characteristic_1,
+						&desc_1),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_2),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+						&service_1, &characteristic_1,
+						&desc_2),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - None",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID,
+				&service_1, &characteristic_1, NULL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Included Services - Incorrect rsp",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_included_action,
+							&get_incl_data_1),
+		CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_FAILURE, CONN1_ID,
+							&service_1, NULL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+		),
+	TEST_CASE_BREDRLE("Gatt Client - Get Included Service - 16 UUID",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_included_action,
+							&get_incl_data_1),
+		CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_SUCCESS, CONN1_ID,
+						&service_1, &included_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Included Service - 128 UUID",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_included_action,
+							&get_incl_data_1),
+		CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_SUCCESS, CONN1_ID,
+						&service_1, &included_2),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Get Included Service - None",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_included_action,
+							&get_incl_data_1),
+		CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_FAILURE, CONN1_ID,
+							&service_1, NULL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Read Characteristic - Success",
+		ACTION_SUCCESS(init_pdus, read_characteristic_1),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_read_characteristic_action,
+							&read_char_data_1),
+		CALLBACK_GATTC_READ_CHARACTERISTIC(GATT_STATUS_SUCCESS,
+						CONN1_ID, &read_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+
+	TEST_CASE_BREDRLE("Gatt Client - Read Characteristic - Insuf. Auth.",
+		ACTION_SUCCESS(init_pdus, read_characteristic_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_read_characteristic_action,
+							&read_char_data_1),
+		CALLBACK_GATTC_READ_CHARACTERISTIC(GATT_STATUS_INS_AUTH,
+						CONN1_ID, &read_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Read Characteristic - Wrong params",
+		ACTION_SUCCESS(init_pdus, read_characteristic_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_FAIL(gatt_client_read_characteristic_action,
+							&read_char_data_2),
+		CALLBACK_GATTC_READ_CHARACTERISTIC(GATT_STATUS_FAILURE,
+						CONN1_ID, &read_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Read Descriptor - Success",
+		ACTION_SUCCESS(init_pdus, read_descriptor_1),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_SUCCESS(gatt_client_read_descriptor_action,
+							&read_desc_data_1),
+		CALLBACK_GATTC_READ_DESCRIPTOR(GATT_STATUS_SUCCESS,
+						CONN1_ID, &read_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Read Descriptor - Insuf. Auth.",
+		ACTION_SUCCESS(init_pdus, read_descriptor_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_SUCCESS(gatt_client_read_descriptor_action,
+							&read_desc_data_1),
+		CALLBACK_GATTC_READ_DESCRIPTOR(GATT_STATUS_INS_AUTH,
+						CONN1_ID, &read_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Read Descriptor - Wrong params",
+		ACTION_SUCCESS(init_pdus, read_descriptor_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_FAIL(gatt_client_read_descriptor_action,
+							&read_desc_data_2),
+		CALLBACK_GATTC_READ_DESCRIPTOR(GATT_STATUS_FAILURE,
+						CONN1_ID, &read_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Characteristic Cmd - Success",
+		ACTION_SUCCESS(init_pdus, write_characteristic_1),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_write_characteristic_action,
+							&write_char_data_1),
+		CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_SUCCESS,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Characteristic Req - Success",
+		ACTION_SUCCESS(init_pdus, write_characteristic_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_write_characteristic_action,
+							&write_char_data_2),
+		CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_SUCCESS,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Characteristic - Insuf. Auth.",
+		ACTION_SUCCESS(init_pdus, write_characteristic_3),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_write_characteristic_action,
+							&write_char_data_2),
+		CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_INS_AUTH,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Characteristic - Wrong Params",
+		ACTION_SUCCESS(init_pdus, write_characteristic_3),
+		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),
+		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,
+							&write_char_data_2),
+		CALLBACK_GATTC_WRITE_CHARACTERISTIC(GATT_STATUS_FAILURE,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Register For Notification - Success",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_register_for_notification_action,
+								&notif_data_1),
+		CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID,
+							&characteristic_1,
+							&service_1, 1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Deregister For Notification - Success",
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_register_for_notification_action,
+								&notif_data_1),
+		CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID,
+							&characteristic_1,
+							&service_1, 1),
+		ACTION_SUCCESS(gatt_client_deregister_for_notification_action,
+								&notif_data_1),
+		CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID,
+							&characteristic_1,
+							&service_1, 0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Register For Notification - Indicate",
+		ACTION_SUCCESS(init_pdus, notification_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+							CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_register_for_notification_action,
+								&notif_data_1),
+		CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID,
+							&characteristic_1,
+							&service_1, 1),
+		ACTION_SUCCESS(gatt_remote_send_frame_action, NULL),
+		CALLBACK_GATTC_NOTIFY(CONN1_ID, &notify_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Register For Notification - Notify",
+		ACTION_SUCCESS(init_pdus, notification_3),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_register_for_notification_action,
+								&notif_data_1),
+		CALLBACK_GATTC_REGISTER_FOR_NOTIF(GATT_STATUS_SUCCESS, CONN1_ID,
+							&characteristic_1,
+							&service_1, 1),
+		ACTION_SUCCESS(gatt_remote_send_frame_action, NULL),
+		CALLBACK_GATTC_NOTIFY(CONN1_ID, &notify_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Descriptor - Success",
+		ACTION_SUCCESS(init_pdus, write_descriptor_1),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_SUCCESS(gatt_client_write_descriptor_action,
+							&write_desc_data_1),
+		CALLBACK_GATTC_WRITE_DESCRIPTOR(GATT_STATUS_SUCCESS,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Descriptor - Insuf. Auth.",
+		ACTION_SUCCESS(init_pdus, write_descriptor_2),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_SUCCESS(gatt_client_write_descriptor_action,
+							&write_desc_data_1),
+		CALLBACK_GATTC_WRITE_DESCRIPTOR(GATT_STATUS_INS_AUTH,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Client - Write Descriptor - Wrong Param",
+		ACTION_SUCCESS(init_pdus, write_descriptor_1),
+		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),
+		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),
+		CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+		CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+		ACTION_SUCCESS(gatt_client_get_characteristic_action,
+							&get_char_data_1),
+		CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+				CONN1_ID, &service_1, &characteristic_1, 4),
+		ACTION_SUCCESS(gatt_client_get_descriptor_action,
+							&get_desc_data_1),
+		CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_SUCCESS, CONN1_ID,
+				&service_1, &characteristic_1, &desc_1),
+		ACTION_FAIL(gatt_client_write_descriptor_action,
+							&write_desc_data_2),
+		CALLBACK_GATTC_WRITE_DESCRIPTOR(GATT_STATUS_FAILURE,
+						CONN1_ID, &write_params_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Register",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Unregister",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_unregister_action,
+							INT_TO_PTR(APP1_ID)),
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_server_disconnect_action,
+							&app1_conn_req),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_DISCONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_register_action, &app2_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_server_connect_action, &app2_conn_req),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN2_ID, APP2_ID),
+		ACTION_SUCCESS(gatt_server_disconnect_action, &app2_conn_req),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_DISCONNECTED,
+						prop_emu_remotes_default_set,
+						CONN2_ID, APP2_ID),
+		ACTION_SUCCESS(gatt_server_disconnect_action, &app1_conn_req),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_DISCONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Single Service Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&service_add_1, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Multiple Services Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&service_add_1, NULL, NULL),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_2),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&service_add_2, NULL, NULL),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_3),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&service_add_3, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Service with 0 handles",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_FAIL(gatt_server_add_service_action,
+						&add_bad_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_FAILURE, APP1_ID,
+						&service_add_1, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Secondary Service",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+						&add_sec_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&included_1, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Included Service Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_4),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_4),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&inc_srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_inc_service_action,
+						&add_inc_service_data_1),
+		CALLBACK_GATTS_INC_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&srvc1_handle, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Inc. Service with wrong handle",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_4),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_4),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&service_add_1, NULL, NULL),
+		ACTION_FAIL(gatt_server_add_inc_service_action,
+						&add_bad_inc_service_data_1),
+		CALLBACK_GATTS_INC_SERVICE_ADDED(GATT_STATUS_FAILURE, APP1_ID,
+							&srvc1_handle, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Single Characteristic Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_5),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Char. wrong service handle",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_5),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_FAIL(gatt_server_add_char_action, &add_bad_char_data_1),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_FAILURE,
+							APP1_ID, &app1_uuid,
+							NULL, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Single Descriptor Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_6),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							NULL),
+		ACTION_SUCCESS(gatt_server_add_desc_action, &add_desc_data_1),
+		CALLBACK_GATTS_DESCRIPTOR_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&app2_uuid, &srvc1_handle,
+						NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Desc. wrong service handle",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_6),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							NULL),
+		ACTION_FAIL(gatt_server_add_desc_action, &add_bad_desc_data_1),
+		CALLBACK_GATTS_DESCRIPTOR_ADDED(GATT_STATUS_FAILURE, APP1_ID,
+						&app2_uuid, NULL, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Add Desc. wrong app ID",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_6),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							NULL),
+		ACTION_FAIL(gatt_server_add_desc_action, &add_bad_desc_data_2),
+		CALLBACK_GATTS_DESCRIPTOR_ADDED(GATT_STATUS_FAILURE, APP2_ID,
+						&app2_uuid, NULL, NULL, NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Start Service Successful BREDRLE",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Start Service Successful LE",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_2),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Start Service wrong service handle",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+						&service_add_1, NULL, NULL),
+		ACTION_FAIL(gatt_server_start_srvc_action,
+							&start_bad_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_FAILURE, APP1_ID,
+									NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Start Service wrong server transport",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_FAIL(gatt_server_start_srvc_action,
+							&start_bad_srvc_data_2),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_FAILURE, APP1_ID,
+								&srvc1_handle),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Stop Service Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_stop_srvc_action, &stop_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_STOPPED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Stop Service wrong service handle",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+		ACTION_FAIL(gatt_server_stop_srvc_action,
+							&stop_bad_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_STOPPED(GATT_STATUS_FAILURE, APP1_ID,
+									NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Delete Service Successful",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_delete_srvc_action,
+							&delete_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_DELETED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Delete Service wrong handle",
+		ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_1),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_FAIL(gatt_server_delete_srvc_action,
+						&delete_bad_srvc_data_1),
+		CALLBACK_GATTS_SERVICE_DELETED(GATT_STATUS_FAILURE, APP1_ID,
+									NULL),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Send Indication",
+		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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_server_send_indication_action,
+						&send_indication_data_1),
+		CALLBACK(CB_EMU_VALUE_INDICATION),
+		CALLBACK_GATTS_NOTIF_CONF(CONN1_ID, GATT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Gatt Server - Send Notification",
+		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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_SUCCESS(gatt_server_send_indication_action,
+						&send_indication_data_2),
+		CALLBACK_GATTS_NOTIF_CONF(CONN1_ID, GATT_STATUS_SUCCESS),
+		CALLBACK(CB_EMU_VALUE_NOTIFICATION),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		ACTION_FAIL(gatt_server_send_indication_action,
+						&send_bad_indication_data_1),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_5),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							&char1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_2),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		PROCESS_DATA(GATT_STATUS_SUCCESS,
+				gatt_remote_send_raw_pdu_action,
+				&att_read_req_op_v, &char1_handle_v, NULL),
+		CALLBACK_GATTS_REQUEST_READ(CONN1_ID, TRANS1_ID,
+						prop_emu_remotes_default_set,
+						&char1_handle, 0, false),
+		ACTION_SUCCESS(gatt_server_send_response_action,
+							&send_resp_data_1),
+		CALLBACK(CB_EMU_READ_RESPONSE),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_5),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							&char1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_2),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		PROCESS_DATA(GATT_STATUS_SUCCESS,
+					gatt_remote_send_raw_pdu_action,
+					&att_write_req_op_v, &char1_handle_v,
+					&att_write_req_value_1_v),
+		CALLBACK_GATTS_REQUEST_WRITE(CONN1_ID, TRANS1_ID,
+						prop_emu_remotes_default_set,
+						&char1_handle, 0,
+						sizeof(att_write_req_value_1),
+						true, false,
+						att_write_req_value_1),
+		ACTION_SUCCESS(gatt_server_send_response_action,
+							&send_resp_data_2),
+		CALLBACK(CB_EMU_WRITE_RESPONSE),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_5),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							&char1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_2),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		PROCESS_DATA(GATT_STATUS_SUCCESS,
+						gatt_remote_send_raw_pdu_action,
+						&att_find_by_type_req_op_v,
+						&search_range_1,
+						&primary_type),
+		CALLBACK_ERROR(CB_EMU_ATT_ERROR, 0x0a),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	/* This tests embeded ccc */
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		/* For CCC we need to be bonded */
+		ACTION_SUCCESS(bt_create_bond_action,
+					&prop_test_remote_ble_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+					&prop_emu_remotes_default_set[0], 1),
+		/* Write and receive confirmation */
+		PROCESS_DATA(GATT_STATUS_SUCCESS,
+				gatt_remote_send_raw_pdu_action,
+				&att_write_req_op_v, &svc_change_ccc_handle_v,
+				&svc_change_ccc_value_v),
+		CALLBACK(CB_EMU_WRITE_RESPONSE),
+		/* Shutdown */
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	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),
+		CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+		ACTION_SUCCESS(gatt_server_add_service_action,
+							&add_service_data_5),
+		CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+							&service_add_1, NULL,
+							&srvc1_handle),
+		ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2),
+		CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+							APP1_ID, &app1_uuid,
+							&srvc1_handle, NULL,
+							&char1_handle),
+		ACTION_SUCCESS(gatt_server_start_srvc_action,
+							&start_srvc_data_2),
+		CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+								&srvc1_handle),
+		ACTION_SUCCESS(bt_start_discovery_action, NULL),
+		CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+							BT_DISCOVERY_STARTED),
+		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),
+		CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+						prop_emu_remotes_default_set,
+						CONN1_ID, APP1_ID),
+		PROCESS_DATA(GATT_STATUS_SUCCESS,
+					gatt_remote_send_raw_pdu_action,
+					&att_write_req_op_v, &char1_handle_v,
+					&att_write_req_value_1_v),
+		CALLBACK_GATTS_REQUEST_WRITE(CONN1_ID, TRANS1_ID,
+						prop_emu_remotes_default_set,
+						&char1_handle, 0,
+						sizeof(att_write_req_value_1),
+						true, false,
+						att_write_req_value_1),
+		ACTION_SUCCESS(gatt_server_send_response_action,
+						&send_resp_data_2_error),
+		CALLBACK_ERROR(CB_EMU_ATT_ERROR, GATT_ERR_INVAL_ATTR_VALUE_LEN),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+		),
+};
+
+struct queue *get_gatt_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_gatt_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-hdp.c b/bluez/repo/android/tester-hdp.c
new file mode 100644
index 0000000..e849820
--- /dev/null
+++ b/bluez/repo/android/tester-hdp.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "lib/bluetooth.h"
+#include "android/utils.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "tester-main.h"
+
+typedef enum {
+	HDP_APP_SINK_RELIABLE,
+	HDP_APP_SINK_STREAM,
+	HDP_APP_SOURCE_RELIABLE,
+	HDP_APP_SOURCE_STREAM,
+} hdp_app_reg_type;
+
+#define hdp_rsp_pdu	0x07, \
+			0x00, 0x00, \
+			0x01, 0xc8, \
+			0x01, 0xc5, \
+			0x36, 0x01, 0xc2, 0x36, 0x01, 0xbf, 0x09, 0x00, 0x00, \
+			0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, \
+			0x03, 0x19, 0x14, 0x01, 0x09, 0x00, 0x04, 0x35, 0x10, \
+			0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x10, 0x01, 0x35, \
+			0x06, 0x19, 0x00, 0x1e, 0x09, 0x01, 0x00, 0x09, 0x00, \
+			0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x14, 0x00, 0x09, \
+			0x01, 0x01, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, 0x0d, \
+			0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x10, 0x03, 0x35, \
+			0x03, 0x19, 0x00, 0x1f, 0x09, 0x01, 0x00, 0x25, 0x03, \
+			0x48, 0x44, 0x50, 0x09, 0x01, 0x01, 0x25, 0x28, 0x43, \
+			0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x64, \
+			0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2c, 0x20, 0x61, \
+			0x6e, 0x64, 0x20, 0x72, 0x65, 0x8b, 0x6c, 0x61, 0x79, \
+			0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x64, \
+			0x61, 0x74, 0x61, 0x09, 0x01, 0x02, 0x25, 0x0d, 0x42, \
+			0x4c, 0x55, 0x45, 0x54, 0x4f, 0x4f, 0x54, 0x48, 0x20, \
+			0x53, 0x49, 0x47, 0x09, 0x02, 0x00, 0x36, 0x01, 0x22, \
+			0x35, 0x18, 0x08, 0x01, 0x09, 0x10, 0x04, 0x08, 0x00, \
+			0x25, 0x0f, 0x50, 0x75, 0x6c, 0x73, 0x65, 0x20, 0x4f, \
+			0x78, 0x69, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, \
+			0x20, 0x08, 0x02, 0x09, 0x10, 0x07, 0x08, 0x00, 0x25, \
+			0x17, 0x42, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 0x50, 0x72, \
+			0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x20, 0x4d, 0x6f, \
+			0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0d, 0x35, 0x1a, 0x08, \
+			0x03, 0x09, 0x10, 0x08, 0x08, 0x00, 0x25, 0x11, 0x42, \
+			0x6f, 0x64, 0x79, 0x20, 0x54, 0x68, 0x65, 0x72, 0x6d, \
+			0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, 0x1e, \
+			0x08, 0x04, 0x09, 0x10, 0x0f, 0x08, 0x00, 0x25, 0x15, \
+			0x42, 0x6f, 0x64, 0x79, 0x20, 0x57, 0x65, 0x69, 0x67, \
+			0x68, 0x74, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x09, \
+			0x09, 0x09, 0x0d, 0x35, 0x17, 0x08, 0x05, 0x09, 0x10, \
+			0x11, 0x08, 0x00, 0x25, 0x0e, 0x47, 0x6c, 0x75, 0x63, \
+			0x6f, 0x73, 0x65, 0x20, 0x4d, 0x65, 0x74, 0x65, 0x72, \
+			0x0d, 0x35, 0x18, 0x08, 0x06, 0x09, 0x10, 0x04, 0x08, \
+			0x01, 0x25, 0x0f, 0x50, 0x75, 0x6c, 0x73, 0x65, 0x20, \
+			0x4f, 0x78, 0x69, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, \
+			0x35, 0x20, 0x08, 0x07, 0x09, 0x10, 0x07, 0x08, 0x01, \
+			0x25, 0x17, 0x42, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 0x50, \
+			0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x20, 0x4d, \
+			0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0d, 0x35, 0x1a, \
+			0x08, 0x08, 0x09, 0x10, 0x08, 0x08, 0x01, 0x25, 0x11, \
+			0x42, 0x6f, 0x64, 0x79, 0x20, 0x54, 0x68, 0x65, 0x72, \
+			0x6d, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, \
+			0x1e, 0x08, 0x09, 0x09, 0x10, 0x0f, 0x08, 0x01, 0x25, \
+			0x15, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x57, 0x65, 0x69, \
+			0x67, 0x68, 0x74, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, \
+			0x09, 0x09, 0x09, 0x0d, 0x35, 0x17, 0x08, 0x0a, 0x09, \
+			0x10, 0x11, 0x08, 0x01, 0x25, 0x0e, 0x47, 0x6c, 0x75, \
+			0x63, 0x6f, 0x73, 0x65, 0x20, 0x4d, 0x65, 0x74, 0x65, \
+			0x72, 0x0d, 0x09, 0x03, 0x01, 0x08, 0x01, 0x09, 0x03, \
+			0x02, 0x08, 0x00, \
+			0x00
+
+static const struct pdu_set sdp_pdus[] = {
+	{ end_pdu, raw_pdu(hdp_rsp_pdu) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data sdp_cid_data = {
+	.pdu = sdp_pdus,
+	.is_sdp = TRUE,
+};
+
+static struct emu_l2cap_cid_data ctrl_cid_data;
+static struct emu_l2cap_cid_data data_cid_data;
+
+static struct queue *list; /* List of hdp test cases */
+
+static bthl_reg_param_t *create_app(hdp_app_reg_type type)
+{
+	bthl_reg_param_t *reg;
+	bthl_mdep_cfg_t mdep1, mdep2;
+
+	reg = malloc(sizeof(bthl_reg_param_t));
+	reg->application_name = "bluez-android";
+	reg->provider_name = "Bluez";
+	reg->srv_name = "bluez-hdp";
+	reg->srv_desp = "health-device-profile";
+
+	mdep1.data_type = 4100;
+	mdep1.mdep_description = "pulse-oximeter";
+
+	mdep2.data_type = 4100;
+	mdep2.mdep_description = "pulse-oximeter";
+
+	switch (type) {
+	case HDP_APP_SINK_RELIABLE:
+		reg->number_of_mdeps = 1;
+		mdep1.mdep_role = BTHL_MDEP_ROLE_SINK;
+		mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE;
+		reg->mdep_cfg = malloc(reg->number_of_mdeps *
+						sizeof(bthl_mdep_cfg_t));
+		reg->mdep_cfg[0] = mdep1;
+		break;
+
+	case HDP_APP_SINK_STREAM:
+		reg->number_of_mdeps = 2;
+
+		mdep1.mdep_role = BTHL_MDEP_ROLE_SINK;
+		mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE;
+
+		mdep2.mdep_role = BTHL_MDEP_ROLE_SINK;
+		mdep2.channel_type = BTHL_CHANNEL_TYPE_STREAMING;
+
+		reg->mdep_cfg = malloc(reg->number_of_mdeps *
+						sizeof(bthl_mdep_cfg_t));
+		reg->mdep_cfg[0] = mdep1;
+		reg->mdep_cfg[1] = mdep2;
+		break;
+
+	case HDP_APP_SOURCE_RELIABLE:
+		reg->number_of_mdeps = 1;
+
+		mdep1.mdep_role = BTHL_MDEP_ROLE_SOURCE;
+		mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE;
+
+		reg->mdep_cfg = malloc(reg->number_of_mdeps *
+						sizeof(bthl_mdep_cfg_t));
+		reg->mdep_cfg[0] = mdep1;
+		break;
+
+	case HDP_APP_SOURCE_STREAM:
+		reg->number_of_mdeps = 2;
+
+		mdep1.mdep_role = BTHL_MDEP_ROLE_SOURCE;
+		mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE;
+
+		mdep2.mdep_role = BTHL_MDEP_ROLE_SOURCE;
+		mdep2.channel_type = BTHL_CHANNEL_TYPE_STREAMING;
+
+		reg->mdep_cfg = malloc(reg->number_of_mdeps *
+						sizeof(bthl_mdep_cfg_t));
+		reg->mdep_cfg[0] = mdep1;
+		reg->mdep_cfg[1] = mdep2;
+		break;
+	}
+
+
+	return reg;
+}
+
+static void hdp_register_sink_reliable_app_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	int app_id = 0;
+	bthl_reg_param_t *reg;
+
+	reg = create_app(HDP_APP_SINK_RELIABLE);
+	step->action_status = data->if_hdp->register_application(reg, &app_id);
+
+	schedule_action_verification(step);
+	free(reg->mdep_cfg);
+	free(reg);
+}
+
+static void hdp_register_sink_stream_app_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	int app_id = 0;
+	bthl_reg_param_t *reg;
+
+	reg = create_app(HDP_APP_SINK_STREAM);
+	step->action_status = data->if_hdp->register_application(reg, &app_id);
+
+	schedule_action_verification(step);
+	free(reg->mdep_cfg);
+	free(reg);
+}
+
+static void hdp_register_source_reliable_app_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	int app_id = 0;
+	bthl_reg_param_t *reg;
+
+	reg = create_app(HDP_APP_SOURCE_RELIABLE);
+	step->action_status = data->if_hdp->register_application(reg, &app_id);
+
+	schedule_action_verification(step);
+	free(reg->mdep_cfg);
+	free(reg);
+}
+
+static void hdp_register_source_stream_app_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+	int app_id = 0;
+	bthl_reg_param_t *reg;
+
+	reg = create_app(HDP_APP_SOURCE_STREAM);
+	step->action_status = data->if_hdp->register_application(reg, &app_id);
+
+	schedule_action_verification(step);
+	free(reg->mdep_cfg);
+	free(reg);
+}
+
+static void hdp_unregister_app_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_hdp->unregister_application(1);
+
+	schedule_action_verification(step);
+}
+
+static void mcap_ctrl_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+	uint8_t crt_rsp[5], del_rsp[4], config;
+	uint8_t opcode = ((uint8_t *) data)[0];
+	static bool reliable = false;
+
+	switch (opcode) {
+	case 0x01: /* MD_CREATE_MDL_REQ */
+		crt_rsp[0] = 0x02; /* MD_CREATE_MDL_RSP */
+		crt_rsp[1] = 0x00; /* Response code - Success */
+		crt_rsp[2] = ((uint8_t *) data)[1]; /* mdlid */
+		crt_rsp[3] = ((uint8_t *) data)[2];
+		config = ((uint8_t *) data)[4];
+
+		if (config == 0x00) {
+			if (!reliable) {
+				crt_rsp[4] = 0x01;
+				reliable = true;
+			} else {
+				crt_rsp[4] = 0x02;
+				reliable = false;
+			}
+		} else {
+			crt_rsp[4] = config;
+		}
+
+		bthost_send_cid(bthost, cid_data->handle,
+					cid_data->cid,
+					crt_rsp, sizeof(crt_rsp));
+		break;
+	case 0x03: /* MD_RECONNECT_MDL_REQ */
+	case 0x05: /* MD_ABORT_MDL_REQ */
+		break;
+	case 0x07: /* MD_DELETE_MDL_REQ */
+		del_rsp[0] = 0x08; /* MD_DELETE_MDL_RSP */
+		del_rsp[1] = 0x00; /* Response code - Success */
+		del_rsp[2] = ((uint8_t *) data)[1]; /* mdlid */
+		del_rsp[3] = ((uint8_t *) data)[2];
+		bthost_send_cid(bthost, cid_data->handle,
+					cid_data->cid,
+					del_rsp, sizeof(del_rsp));
+		break;
+	}
+}
+
+static void mcap_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, mcap_ctrl_cid_hook_cb,
+								cid_data);
+}
+
+/* Emulate SDP (PSM = 1) */
+static struct emu_set_l2cap_data l2cap_setup_sdp_data = {
+	.psm = 1,
+	.func = tester_generic_connect_cb,
+	.user_data = &sdp_cid_data,
+};
+
+/* Emulate Control Channel (PSM = 0x1001) */
+static struct emu_set_l2cap_data l2cap_setup_cc_data = {
+	.psm = 0x1001,
+	.func = mcap_ctrl_connect_cb,
+	.user_data = &ctrl_cid_data,
+};
+
+/* Emulate Data Channel (PSM = 0x1003) */
+static struct emu_set_l2cap_data l2cap_setup_dc_data = {
+	.psm = 0x1003,
+	.func = tester_generic_connect_cb,
+	.user_data = &data_cid_data,
+};
+
+static void hdp_connect_source_reliable_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+	int app_id, channel_id, mdep_cfg_index;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	app_id = 1;
+	mdep_cfg_index = 0;
+	channel_id = 0;
+	step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr,
+						mdep_cfg_index, &channel_id);
+
+	schedule_action_verification(step);
+}
+
+static void hdp_destroy_source_reliable_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_hdp->destroy_channel(1);
+	schedule_action_verification(step);
+}
+
+static void hdp_connect_sink_reliable_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+	int app_id, channel_id, mdep_cfg_index;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	app_id = 1;
+	mdep_cfg_index = 0;
+	channel_id = 0;
+	step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr,
+						mdep_cfg_index, &channel_id);
+
+	schedule_action_verification(step);
+}
+
+static void hdp_connect_sink_stream_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+	int app_id, channel_id, mdep_cfg_index;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	app_id = 1;
+	mdep_cfg_index = 1;
+	channel_id = 0;
+	step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr,
+						mdep_cfg_index, &channel_id);
+
+	schedule_action_verification(step);
+}
+
+static void hdp_destroy_sink_reliable_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_hdp->destroy_channel(1);
+	schedule_action_verification(step);
+}
+
+static void hdp_destroy_sink_stream_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_hdp->destroy_channel(2);
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("HDP Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("HDP Register Sink Reliable Application",
+		ACTION_SUCCESS(hdp_register_sink_reliable_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("HDP Register Sink Stream Application",
+		ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("HDP Register Source Reliable Application",
+		ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("HDP Register Source Stream Application",
+		ACTION_SUCCESS(hdp_register_source_stream_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("HDP Unegister Application",
+		ACTION_SUCCESS(hdp_register_source_stream_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+		ACTION_SUCCESS(hdp_unregister_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_DEREG_SUCCESS),
+	),
+	TEST_CASE_BREDRLE("HDP Connect Source Reliable Channel",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_dc_data),
+		ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+		ACTION_SUCCESS(hdp_connect_source_reliable_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTING),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTED),
+	),
+	TEST_CASE_BREDRLE("HDP Destroy Source Reliable Channel",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_dc_data),
+		ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+		ACTION_SUCCESS(hdp_connect_source_reliable_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTING),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hdp_destroy_source_reliable_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_DESTROYED),
+	),
+	TEST_CASE_BREDRLE("HDP Connect Sink Streaming Channel",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_dc_data),
+		ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+		ACTION_SUCCESS(hdp_connect_sink_reliable_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTING),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hdp_connect_sink_stream_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1,
+						BTHL_CONN_STATE_CONNECTED),
+	),
+	TEST_CASE_BREDRLE("HDP Destroy Sink Streaming Channel",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_dc_data),
+		ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL),
+		CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1,
+					BTHL_APP_REG_STATE_REG_SUCCESS),
+		ACTION_SUCCESS(hdp_connect_sink_reliable_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTING),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hdp_connect_sink_stream_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1,
+						BTHL_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hdp_destroy_sink_reliable_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0,
+						BTHL_CONN_STATE_DESTROYED),
+		ACTION_SUCCESS(hdp_destroy_sink_stream_action, NULL),
+		CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1,
+						BTHL_CONN_STATE_DESTROYED),
+	),
+};
+
+struct queue *get_hdp_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_hdp_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-hidhost.c b/bluez/repo/android/tester-hidhost.c
new file mode 100644
index 0000000..221b122
--- /dev/null
+++ b/bluez/repo/android/tester-hidhost.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "lib/bluetooth.h"
+#include "android/utils.h"
+#include "tester-main.h"
+
+#define HID_GET_REPORT_PROTOCOL		0x60
+#define HID_GET_BOOT_PROTOCOL		0x61
+#define HID_SET_REPORT_PROTOCOL		0x70
+#define HID_SET_BOOT_PROTOCOL		0x71
+
+#define HID_SET_INPUT_REPORT		0x51
+#define HID_SET_OUTPUT_REPORT		0x52
+#define HID_SET_FEATURE_REPORT		0x53
+
+#define HID_SEND_DATA			0xa2
+
+#define HID_GET_INPUT_REPORT		0x49
+#define HID_GET_OUTPUT_REPORT		0x4a
+#define HID_GET_FEATURE_REPORT		0x4b
+
+#define HID_MODE_DEFAULT		0x00
+#define HID_MODE_BREDR			0x01
+#define HID_MODE_LE			0x02
+
+#define HID_EXPECTED_REPORT_SIZE	0x02
+
+#define HID_VIRTUAL_CABLE_UNPLUG	0x15
+
+static struct queue *list; /* List of hidhost test cases */
+
+#define did_req_pdu	0x06, \
+			0x00, 0x00, \
+			0x00, 0x0f, \
+			0x35, 0x03, \
+			0x19, 0x12, 0x00, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00, \
+			0x00, 0xff, 0xff, 0x00
+
+#define did_rsp_pdu	0x07, \
+			0x00, 0x00, \
+			0x00, 0x4f, \
+			0x00, 0x4c, \
+			0x35, 0x4a, 0x35, 0x48, 0x09, 0x00, 0x00, 0x0a, 0x00, \
+			0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, \
+			0x12, 0x00, 0x09, 0x00, 0x05, 0x35, 0x03, 0x19, 0x10, \
+			0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, \
+			0x12, 0x00, 0x09, 0x01, 0x03, 0x09, 0x02, 0x00, 0x09, \
+			0x01, 0x03, 0x09, 0x02, 0x01, 0x09, 0x1d, 0x6b, 0x09, \
+			0x02, 0x02, 0x09, 0x02, 0x46, 0x09, 0x02, 0x03, 0x09, \
+			0x05, 0x0e, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, \
+			0x05, 0x09, 0x00, 0x02, \
+			0x00
+
+#define hid_req_pdu	0x06, \
+			0x00, 0x01, \
+			0x00, 0x0f, \
+			0x35, 0x03, \
+			0x19, 0x11, 0x24, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00, \
+			0x00, 0xff, 0xff, 0x00
+
+#define hid_rsp_pdu	0x07, \
+			0x00, 0x01, \
+			0x01, 0x71, \
+			0x01, 0x6E, \
+			0x36, 0x01, 0x6b, 0x36, 0x01, 0x68, 0x09, 0x00, 0x00, \
+			0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, \
+			0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, \
+			0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, \
+			0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, 0x03, \
+			0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, \
+			0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, \
+			0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24, \
+			0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, \
+			0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13, \
+			0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25, \
+			0x1e, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, 0x68, \
+			0x20, 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, \
+			0x68, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x4d, \
+			0x35, 0x35, 0x35, 0x62, 0x09, 0x01, 0x01, 0x25, 0x0f, \
+			0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, \
+			0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x09, 0x01, 0x02, \
+			0x25, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, \
+			0x68, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, \
+			0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x80, \
+			0x09, 0x02, 0x03, 0x08, 0x21, 0x09, 0x02, 0x04, 0x28, \
+			0x01, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06, \
+			0x35, 0x74, 0x35, 0x72, 0x08, 0x22, 0x25, 0x6e, 0x05, \
+			0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x02, 0x09, 0x01, \
+			0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, \
+			0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, \
+			0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xf8, \
+			0x26, 0xff, 0x07, 0x75, 0x0c, 0x95, 0x02, 0x81, 0x06, \
+			0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, \
+			0x01, 0x81, 0x06, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x81, \
+			0x06, 0x05, 0x09, 0x19, 0x09, 0x29, 0x10, 0x15, 0x00, \
+			0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0xc0, \
+			0xc0, 0x06, 0x00, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85, \
+			0x10, 0x75, 0x08, 0x95, 0x06, 0x15, 0x00, 0x26, 0xff, \
+			0x00, 0x09, 0x01, 0x81, 0x00, 0x09, 0x01, 0x91, 0x00, \
+			0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, \
+			0x04, 0x09, 0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28, \
+			0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, \
+			0x28, 0x01, 0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09, \
+			0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28, \
+			0x00, 0x09, 0x02, 0x0e, 0x28, 0x01, \
+			0x00
+
+static const struct pdu_set sdp_pdus[] = {
+	{ raw_pdu(did_req_pdu), raw_pdu(did_rsp_pdu) },
+	{ raw_pdu(hid_req_pdu), raw_pdu(hid_rsp_pdu) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data sdp_cid_data = {
+	.pdu = sdp_pdus,
+	.is_sdp = TRUE,
+};
+
+#define hid_keyboard_rsp_pdu	0x07, \
+			0x00, 0x01, \
+			0x02, 0x04, \
+			0x02, 0x01, \
+			0x36, 0x01, 0xfe, 0x36, 0x01, 0x93, 0x09, 0x00, 0x00, \
+			0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, \
+			0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, \
+			0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, \
+			0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x06, 0x35, 0x09, \
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, \
+			0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, \
+			0x24, 0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, \
+			0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, \
+			0x13, 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, \
+			0x25, 0x10, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, \
+			0x20, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, \
+			0x09, 0x01, 0x01, 0x25, 0x08, 0x4b, 0x65, 0x79, 0x62, \
+			0x6f, 0x61, 0x72, 0x64, 0x09, 0x01, 0x02, 0x25, 0x0d, \
+			0x43, 0x53, 0x52, 0x20, 0x48, 0x49, 0x44, 0x45, 0x6e, \
+			0x67, 0x69, 0x6e, 0x65, 0x09, 0x02, 0x00, 0x09, 0x01, \
+			0x00, 0x09, 0x02, 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, \
+			0x02, 0x08, 0x40, 0x09, 0x02, 0x03, 0x08, 0x23, 0x09, \
+			0x02, 0x04, 0x28, 0x01, 0x09, 0x02, 0x05, 0x28, 0x01, \
+			0x09, 0x02, 0x06, 0x35, 0xb7, 0x35, 0xb5, 0x08, 0x22, \
+			0x25, 0xb1, 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, \
+			0x07, 0x85, 0x01, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, \
+			0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, \
+			0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, \
+			0x05, 0x08, 0x85, 0x01, 0x19, 0x01, 0x29, 0x05, 0x91, \
+			0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x06, \
+			0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, \
+			0x00, 0x29, 0x6f, 0x81, 0x00, 0xc0, 0x05, 0x0c, 0x09, \
+			0x01, 0xa1, 0x01, 0x85, 0x02, 0x05, 0x0c, 0x15, 0x00, \
+			0x25, 0x01, 0x75, 0x01, 0x95, 0x18, 0x09, 0xe2, 0x09, \
+			0xea, 0x09, 0xe9, 0x09, 0xb7, 0x09, 0xcd, 0x0a, 0x23, \
+			0x02, 0x0a, 0x8a, 0x01, 0x0a, 0x21, 0x02, 0x75, 0x01, \
+			0x95, 0x03, 0x81, 0x02, 0x75, 0x01, 0x95, 0x05, 0x81, \
+			0x01, 0x05, 0x08, 0x85, 0xff, 0x95, 0x01, 0x75, 0x02, \
+			0x09, 0x24, 0x09, 0x26, 0x81, 0x02, 0x75, 0x06, 0x81, \
+			0x01, 0xc0, 0x06, 0x7f, 0xff, 0x09, 0x01, 0xa1, 0x01, \
+			0x85, 0x03, 0x15, 0x00, 0x25, 0x01, 0x09, 0xb9, 0x09, \
+			0xb5, 0x09, 0xba, 0x09, 0xbb, 0x09, 0xbc, 0x09, 0xbd, \
+			0x09, 0xb6, 0x09, 0xb7, 0x75, 0x01, 0x95, 0x06, 0x81, \
+			0x02, 0x75, 0x01, 0x95, 0x02, 0x81, 0x01, 0xc0, 0x09, \
+			0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, 0x04, 0x09, \
+			0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28, 0x00, 0x09, \
+			0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, 0x28, 0x01, \
+			0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09, 0x02, 0x0c, \
+			0x09, 0x1f, 0x40, 0x09, 0x02, 0x0d, 0x28, 0x00, 0x09, \
+			0x02, 0x0e, 0x28, 0x01, 0x36, 0x00, 0x65, 0x09, 0x00, \
+			0x00, 0x0a, 0x00, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, \
+			0x35, 0x03, 0x19, 0x12, 0x00, 0x09, 0x00, 0x04, 0x35, \
+			0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x01, \
+			0x35, 0x03, 0x19, 0x00, 0x01, 0x09, 0x00, 0x06, 0x35, \
+			0x09, 0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, \
+			0x00, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, \
+			0x12, 0x00, 0x09, 0x01, 0x00, 0x09, 0x01, 0x01, 0x25, \
+			0x00, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, \
+			0x01, 0x09, 0x23, 0x3d, 0x09, 0x02, 0x02, 0x09, 0x01, \
+			0x3d, 0x09, 0x02, 0x03, 0x09, 0x00, 0x00, 0x09, 0x02, \
+			0x04, 0x28, 0x01, 0x09, 0x02, 0x05, 0x09, 0x00,	0x02, \
+			0x00
+
+static const struct pdu_set sdp_kb_pdus[] = {
+	{ raw_pdu(did_req_pdu), raw_pdu(did_rsp_pdu) },
+	{ raw_pdu(hid_req_pdu), raw_pdu(hid_keyboard_rsp_pdu) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data sdp_kb_cid_data = {
+	.pdu = sdp_kb_pdus,
+	.is_sdp = TRUE,
+};
+
+static struct emu_l2cap_cid_data ctrl_cid_data;
+static struct emu_l2cap_cid_data intr_cid_data;
+
+static void hid_prepare_reply_protocol_mode(struct emu_l2cap_cid_data *cid_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	const struct iovec pdu = raw_pdu(0xa0, 0x00);
+
+	bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdu, 1);
+}
+
+static void hid_prepare_reply_report(struct emu_l2cap_cid_data *cid_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	const struct iovec pdu = raw_pdu(0xa2, 0x01, 0x00);
+
+	bthost_send_cid_v(bthost, cid_data->handle, cid_data->cid, &pdu, 1);
+}
+
+static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+	struct emu_l2cap_cid_data *cid_data = user_data;
+	uint8_t header = ((uint8_t *) data)[0];
+	struct step *step;
+
+	switch (header) {
+	case HID_GET_REPORT_PROTOCOL:
+	case HID_GET_BOOT_PROTOCOL:
+	case HID_SET_REPORT_PROTOCOL:
+	case HID_SET_BOOT_PROTOCOL:
+		hid_prepare_reply_protocol_mode(cid_data);
+		break;
+	case HID_GET_INPUT_REPORT:
+	case HID_GET_OUTPUT_REPORT:
+	case HID_GET_FEATURE_REPORT:
+		hid_prepare_reply_report(cid_data);
+		break;
+	/*
+	 * HID device doesnot reply for this commads, so reaching pdu's
+	 * to hid device means assuming test passed
+	 */
+	case HID_SET_INPUT_REPORT:
+	case HID_SET_OUTPUT_REPORT:
+	case HID_SET_FEATURE_REPORT:
+	case HID_SEND_DATA:
+		/* Successfully verify sending data step */
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_CONFIRM_SEND_DATA;
+
+		schedule_callback_verification(step);
+		break;
+	case HID_VIRTUAL_CABLE_UNPLUG:
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_CONNECTION_REJECTED;
+
+		schedule_callback_verification(step);
+		break;
+	}
+}
+static void hid_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb,
+								cid_data);
+}
+
+static void hid_intr_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+	uint8_t header = ((uint8_t *) data)[0];
+	struct step *step;
+
+	switch (header) {
+	case HID_SEND_DATA:
+		/* Successfully verify sending data step */
+		step = g_new0(struct step, 1);
+
+		step->callback = CB_EMU_CONFIRM_SEND_DATA;
+
+		schedule_callback_verification(step);
+		break;
+	}
+}
+static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb,
+								cid_data);
+}
+
+static bt_scan_mode_t setprop_scan_mode_conn_val = BT_SCAN_MODE_CONNECTABLE;
+
+static bt_property_t prop_test_scan_mode_conn = {
+	.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+	.val = &setprop_scan_mode_conn_val,
+	.len = sizeof(setprop_scan_mode_conn_val),
+};
+
+/* Emulate SDP (PSM = 1) */
+static struct emu_set_l2cap_data l2cap_setup_sdp_data = {
+	.psm = 1,
+	.func = tester_generic_connect_cb,
+	.user_data = &sdp_cid_data,
+};
+
+static struct emu_set_l2cap_data l2cap_setup_kb_sdp_data = {
+	.psm = 1,
+	.func = tester_generic_connect_cb,
+	.user_data = &sdp_kb_cid_data,
+};
+
+/* Emulate Control Channel (PSM = 17) */
+static struct emu_set_l2cap_data l2cap_setup_cc_data = {
+	.psm = 17,
+	.func = hid_ctrl_connect_cb,
+	.user_data = &ctrl_cid_data,
+};
+
+/* Emulate Interrupt Channel (PSM = 19) */
+static struct emu_set_l2cap_data l2cap_setup_ic_data = {
+	.psm = 19,
+	.func = hid_intr_connect_cb,
+	.user_data = &intr_cid_data,
+};
+
+static void hidhost_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->connect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void hidhost_disconnect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->disconnect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void hidhost_virtual_unplug_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->virtual_unplug(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void hidhost_get_protocol_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->get_protocol(&bdaddr,
+							BTHH_REPORT_MODE);
+
+	schedule_action_verification(step);
+}
+
+static void hidhost_set_protocol_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->set_protocol(&bdaddr,
+							BTHH_REPORT_MODE);
+
+	schedule_action_verification(step);
+}
+
+static void hidhost_get_report_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->get_report(&bdaddr,
+							BTHH_INPUT_REPORT, 1,
+							20);
+
+	schedule_action_verification(step);
+}
+
+static void hidhost_set_report_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	char *buf = "fe0201";
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->send_data(&bdaddr, buf);
+	schedule_action_verification(step);
+}
+
+static void hidhost_send_data_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	char *buf = "010101";
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+
+	step->action_status = data->if_hid->set_report(&bdaddr,
+							BTHH_INPUT_REPORT, buf);
+	schedule_action_verification(step);
+}
+
+static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
+							void *user_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	struct emu_l2cap_cid_data *cid_data = user_data;
+	const uint16_t *psm = data;
+	const struct iovec con_req = raw_pdu(0x13, 0x00,	/* PSM */
+						0x41, 0x00);	/* Source CID */
+
+	if (len < sizeof(*psm)) {
+		tester_warn("Invalid l2cap response.");
+		return;
+	}
+
+	switch (*psm) {
+	case 0x40:
+		bthost_add_cid_hook(bthost, cid_data->handle, 0x40,
+						hid_ctrl_cid_hook_cb, cid_data);
+
+		bthost_l2cap_req(bthost, cid_data->handle, 0x02,
+					con_req.iov_base, con_req.iov_len,
+					client_l2cap_rsp, cid_data);
+		break;
+	case 0x41:
+		bthost_add_cid_hook(bthost, cid_data->handle, 0x41,
+						hid_intr_cid_hook_cb, cid_data);
+		break;
+	default:
+		break;
+	}
+}
+
+static void hidhost_conn_cb(uint16_t handle, void *user_data)
+{
+	const struct iovec con_req = raw_pdu(0x11, 0x00,	/* PSM */
+						0x40, 0x00);	/* Source CID */
+
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	if (data->hciemu_type == HCIEMU_TYPE_BREDR) {
+		tester_warn("Not handled device type.");
+		return;
+	}
+
+	ctrl_cid_data.cid = 0x40;
+	ctrl_cid_data.handle = handle;
+
+	tester_print("Sending L2CAP Request from remote");
+
+	bthost_l2cap_req(bthost, handle, 0x02, con_req.iov_base,
+					con_req.iov_len, client_l2cap_rsp,
+					&ctrl_cid_data);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("HidHost Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("HidHost Connect 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_DISCONNECTED),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("HidHost Disconnect 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_disconnect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_DISCONNECTED),
+	),
+	TEST_CASE_BREDRLE("HidHost VirtualUnplug 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_virtual_unplug_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_DISCONNECTED),
+	),
+	TEST_CASE_BREDRLE("HidHost GetProtocol 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_get_protocol_action, NULL),
+		CALLBACK_HH_MODE(CB_HH_PROTOCOL_MODE, BTHH_OK, HID_MODE_BREDR),
+	),
+	TEST_CASE_BREDRLE("HidHost SetProtocol 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_set_protocol_action, NULL),
+		CALLBACK_HH_MODE(CB_HH_PROTOCOL_MODE, BTHH_OK, HID_MODE_BREDR),
+	),
+	TEST_CASE_BREDRLE("HidHost GetReport 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_get_report_action, NULL),
+		CALLBACK_HHREPORT(CB_HH_GET_REPORT, BTHH_OK,
+						HID_EXPECTED_REPORT_SIZE),
+	),
+	TEST_CASE_BREDRLE("HidHost SetReport 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTING),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_set_report_action, NULL),
+		CALLBACK(CB_EMU_CONFIRM_SEND_DATA),
+	),
+	TEST_CASE_BREDRLE("HidHost SendData 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_add_l2cap_server_action,
+							&l2cap_setup_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_send_data_action, NULL),
+		CALLBACK(CB_EMU_CONFIRM_SEND_DATA),
+	),
+	TEST_CASE_BREDRLE("HidHost Connect Encrypted 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(set_default_ssp_request_handler, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+						&l2cap_setup_kb_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		ACTION_SUCCESS(hidhost_connect_action, NULL),
+		CALLBACK(CB_EMU_ENCRYPTION_ENABLED),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_CONNECTED),
+		ACTION_SUCCESS(hidhost_send_data_action, NULL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_HH_CONNECTION_STATE,
+						BTHH_CONN_STATE_DISCONNECTED),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("HidHost Reject Unknown Remote Connection",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(bt_set_property_action,
+						&prop_test_scan_mode_conn),
+		CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1),
+		ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+						&l2cap_setup_kb_sdp_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_cc_data),
+		ACTION_SUCCESS(emu_add_l2cap_server_action,
+							&l2cap_setup_ic_data),
+		/* Trigger incoming connection */
+		ACTION_SUCCESS(emu_set_connect_cb_action, hidhost_conn_cb),
+		ACTION_SUCCESS(emu_remote_connect_hci_action, NULL),
+		CALLBACK(CB_EMU_CONNECTION_REJECTED),
+	),
+};
+
+struct queue *get_hidhost_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_hidhost_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-main.c b/bluez/repo/android/tester-main.c
new file mode 100644
index 0000000..3a55792
--- /dev/null
+++ b/bluez/repo/android/tester-main.c
@@ -0,0 +1,3377 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <stdbool.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/signalfd.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/queue.h"
+#include "emulator/bthost.h"
+#include "monitor/bt.h"
+#include "tester-main.h"
+
+static char exec_dir[PATH_MAX + 1];
+
+static gint scheduled_cbacks_num;
+
+#define EMULATOR_SIGNAL_TIMEOUT 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+#define BT_TRANSPORT_UNKNOWN	0x00
+
+static struct {
+	uint16_t cb_num;
+	const char *str;
+} cb_table[] = {
+	DBG_CB(CB_BT_NONE),
+	DBG_CB(CB_BT_ADAPTER_STATE_CHANGED),
+	DBG_CB(CB_BT_ADAPTER_PROPERTIES),
+	DBG_CB(CB_BT_REMOTE_DEVICE_PROPERTIES),
+	DBG_CB(CB_BT_DEVICE_FOUND),
+	DBG_CB(CB_BT_DISCOVERY_STATE_CHANGED),
+	DBG_CB(CB_BT_PIN_REQUEST),
+	DBG_CB(CB_BT_SSP_REQUEST),
+	DBG_CB(CB_BT_BOND_STATE_CHANGED),
+	DBG_CB(CB_BT_ACL_STATE_CHANGED),
+	DBG_CB(CB_BT_THREAD_EVT),
+	DBG_CB(CB_BT_DUT_MODE_RECV),
+	DBG_CB(CB_BT_LE_TEST_MODE),
+
+	/* Hidhost cb */
+	DBG_CB(CB_HH_CONNECTION_STATE),
+	DBG_CB(CB_HH_HID_INFO),
+	DBG_CB(CB_HH_PROTOCOL_MODE),
+	DBG_CB(CB_HH_IDLE_TIME),
+	DBG_CB(CB_HH_GET_REPORT),
+	DBG_CB(CB_HH_VIRTUAL_UNPLUG),
+
+	/* PAN cb */
+	DBG_CB(CB_PAN_CONTROL_STATE),
+	DBG_CB(CB_PAN_CONNECTION_STATE),
+
+	/* HDP cb */
+	DBG_CB(CB_HDP_APP_REG_STATE),
+	DBG_CB(CB_HDP_CHANNEL_STATE),
+
+	/* A2DP cb */
+	DBG_CB(CB_A2DP_CONN_STATE),
+	DBG_CB(CB_A2DP_AUDIO_STATE),
+
+	/* AVRCP */
+	DBG_CB(CB_AVRCP_PLAY_STATUS_REQ),
+	DBG_CB(CB_AVRCP_PLAY_STATUS_RSP),
+	DBG_CB(CB_AVRCP_REG_NOTIF_REQ),
+	DBG_CB(CB_AVRCP_REG_NOTIF_RSP),
+	DBG_CB(CB_AVRCP_GET_ATTR_REQ),
+	DBG_CB(CB_AVRCP_GET_ATTR_RSP),
+
+	/* Gatt client */
+	DBG_CB(CB_GATTC_REGISTER_CLIENT),
+	DBG_CB(CB_GATTC_SCAN_RESULT),
+	DBG_CB(CB_GATTC_OPEN),
+	DBG_CB(CB_GATTC_CLOSE),
+	DBG_CB(CB_GATTC_SEARCH_COMPLETE),
+	DBG_CB(CB_GATTC_SEARCH_RESULT),
+	DBG_CB(CB_GATTC_GET_CHARACTERISTIC),
+	DBG_CB(CB_GATTC_GET_DESCRIPTOR),
+	DBG_CB(CB_GATTC_GET_INCLUDED_SERVICE),
+	DBG_CB(CB_GATTC_REGISTER_FOR_NOTIFICATION),
+	DBG_CB(CB_GATTC_NOTIFY),
+	DBG_CB(CB_GATTC_READ_CHARACTERISTIC),
+	DBG_CB(CB_GATTC_WRITE_CHARACTERISTIC),
+	DBG_CB(CB_GATTC_READ_DESCRIPTOR),
+	DBG_CB(CB_GATTC_WRITE_DESCRIPTOR),
+	DBG_CB(CB_GATTC_EXECUTE_WRITE),
+	DBG_CB(CB_GATTC_READ_REMOTE_RSSI),
+	DBG_CB(CB_GATTC_LISTEN),
+
+	/* Gatt server */
+	DBG_CB(CB_GATTS_REGISTER_SERVER),
+	DBG_CB(CB_GATTS_CONNECTION),
+	DBG_CB(CB_GATTS_SERVICE_ADDED),
+	DBG_CB(CB_GATTS_INCLUDED_SERVICE_ADDED),
+	DBG_CB(CB_GATTS_CHARACTERISTIC_ADDED),
+	DBG_CB(CB_GATTS_DESCRIPTOR_ADDED),
+	DBG_CB(CB_GATTS_SERVICE_STARTED),
+	DBG_CB(CB_GATTS_SERVICE_STOPPED),
+	DBG_CB(CB_GATTS_SERVICE_DELETED),
+	DBG_CB(CB_GATTS_REQUEST_READ),
+	DBG_CB(CB_GATTS_REQUEST_WRITE),
+	DBG_CB(CB_GATTS_REQUEST_EXEC_WRITE),
+	DBG_CB(CB_GATTS_RESPONSE_CONFIRMATION),
+	DBG_CB(CB_GATTS_INDICATION_SEND),
+
+	/* Map client */
+	DBG_CB(CB_MAP_CLIENT_REMOTE_MAS_INSTANCES),
+
+	/* Emulator callbacks */
+	DBG_CB(CB_EMU_CONFIRM_SEND_DATA),
+	DBG_CB(CB_EMU_ENCRYPTION_ENABLED),
+	DBG_CB(CB_EMU_ENCRYPTION_DISABLED),
+	DBG_CB(CB_EMU_CONNECTION_REJECTED),
+	DBG_CB(CB_EMU_VALUE_INDICATION),
+	DBG_CB(CB_EMU_VALUE_NOTIFICATION),
+	DBG_CB(CB_EMU_READ_RESPONSE),
+	DBG_CB(CB_EMU_WRITE_RESPONSE),
+	DBG_CB(CB_EMU_ATT_ERROR),
+};
+
+static gboolean check_callbacks_called(gpointer user_data)
+{
+	/*
+	 * Wait for all callbacks scheduled in current test context to execute
+	 * in main loop. This will avoid late callback calls after test case has
+	 * already failed or timed out.
+	 */
+
+	if (g_atomic_int_get(&scheduled_cbacks_num) == 0) {
+		tester_teardown_complete();
+		return FALSE;
+	} else if (scheduled_cbacks_num < 0) {
+		tester_warn("Unscheduled callback called!");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void check_daemon_term(void)
+{
+	int status;
+	pid_t pid;
+	struct test_data *data = tester_get_data();
+
+	if (!data)
+		return;
+
+	pid = waitpid(data->bluetoothd_pid, &status, WNOHANG);
+	if (pid != data->bluetoothd_pid)
+		return;
+
+	data->bluetoothd_pid = 0;
+
+	if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) {
+		g_idle_add(check_callbacks_called, NULL);
+		return;
+	}
+
+	tester_warn("Unexpected Daemon shutdown with status %d", status);
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		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 SIGCHLD:
+		check_daemon_term();
+		break;
+	}
+
+	return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+	GIOChannel *channel;
+	guint source;
+	sigset_t mask;
+	int fd;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGCHLD);
+
+	if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
+		return 0;
+
+	fd = signalfd(-1, &mask, 0);
+	if (fd < 0)
+		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 void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	/* remove hook for encryption change */
+	hciemu_del_hook(data->hciemu, HCIEMU_HOOK_POST_EVT, 0x08);
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+
+	g_source_remove(data->signalfd);
+	data->signalfd = 0;
+}
+
+static void bluetoothd_start(int hci_index)
+{
+	char prg_name[PATH_MAX + 1];
+	char index[8];
+	char *prg_argv[5];
+
+	snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+	snprintf(index, sizeof(index), "%d", hci_index);
+
+	prg_argv[0] = prg_name;
+	prg_argv[1] = "-i";
+	prg_argv[2] = index;
+	prg_argv[3] = "-d";
+	prg_argv[4] = NULL;
+
+	if (!tester_use_debug())
+		fclose(stderr);
+
+	execve(prg_argv[0], prg_argv, NULL);
+}
+
+static void emulator(int pipe, int hci_index)
+{
+	static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+	char buf[1024];
+	struct sockaddr_un addr;
+	struct timeval tv;
+	int fd;
+	ssize_t len;
+
+	fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		goto failed;
+
+	tv.tv_sec = EMULATOR_SIGNAL_TIMEOUT;
+	tv.tv_usec = 0;
+	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind system socket");
+		goto failed;
+	}
+
+	len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL));
+	if (len != sizeof(EMULATOR_SIGNAL))
+		goto failed;
+
+	memset(buf, 0, sizeof(buf));
+
+	len = read(fd, buf, sizeof(buf));
+	if (len <= 0 || strcmp(buf, "bluetooth.start=daemon"))
+		goto failed;
+
+	close(pipe);
+	close(fd);
+	return bluetoothd_start(hci_index);
+
+failed:
+	close(pipe);
+
+	if (fd >= 0)
+		close(fd);
+}
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static bool hciemu_post_encr_hook(const void *data, uint16_t len,
+							void *user_data)
+{
+	struct step *step;
+
+	/*
+	 * Expected data: status (1 octet) + conn. handle (2 octets) +
+	 * encryption flag (1 octet)
+	 */
+	if (len < 4)
+		return true;
+
+	step = g_new0(struct step, 1);
+
+	step->callback = ((uint8_t *)data)[3] ? CB_EMU_ENCRYPTION_ENABLED :
+						CB_EMU_ENCRYPTION_DISABLED;
+
+	schedule_callback_verification(step);
+	return true;
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	/* set hook for encryption change */
+	hciemu_add_hook(data->hciemu, HCIEMU_HOOK_POST_EVT, 0x08,
+						hciemu_post_encr_hook, NULL);
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->signalfd = setup_signalfd();
+	if (!data->signalfd) {
+		tester_warn("Failed to setup signalfd");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (!tester_use_debug())
+		fclose(stderr);
+	else
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+				NULL, read_index_list_callback, NULL, NULL);
+}
+
+static bool match_property(bt_property_t *exp_prop, bt_property_t *rec_prop,
+								int prop_num)
+{
+	if (exp_prop->type && (exp_prop->type != rec_prop->type))
+		return 0;
+
+	if (exp_prop->len && (exp_prop->len != rec_prop->len)) {
+		tester_debug("Property [%d] len don't match! received=%d, "
+					"expected=%d", prop_num, rec_prop->len,
+					exp_prop->len);
+		return 0;
+	}
+
+	if (exp_prop->val && memcmp(exp_prop->val, rec_prop->val,
+							exp_prop->len)) {
+		tester_debug("Property [%d] value don't match!", prop_num);
+		return 0;
+	}
+
+	return 1;
+}
+
+static bool match_mas_inst(btmce_mas_instance_t *exp_inst,
+				btmce_mas_instance_t *rec_inst, int inst_num)
+{
+	if (exp_inst->id && (exp_inst->id != rec_inst->id)) {
+		tester_debug("MAS inst. [%d] id missmatch %d vs %d", inst_num,
+						rec_inst->id, exp_inst->id);
+		return 0;
+	}
+
+	if (exp_inst->scn && (exp_inst->scn != rec_inst->scn)) {
+		tester_debug("MAS inst. [%d] scn missmatch %d vs %d", inst_num,
+						rec_inst->scn, exp_inst->scn);
+		return 0;
+	}
+
+	if (exp_inst->msg_types &&
+			(exp_inst->msg_types != rec_inst->msg_types)) {
+		tester_debug("Mas inst. [%d] mesg type missmatch %d vs %d",
+					inst_num, rec_inst->scn, exp_inst->scn);
+		return 0;
+	}
+
+	if (exp_inst->p_name && memcmp(exp_inst->p_name, rec_inst->p_name,
+						strlen(exp_inst->p_name))) {
+		tester_debug("Mas inst. [%d] name don't match!", inst_num);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int verify_property(bt_property_t *exp_props, int exp_num_props,
+				bt_property_t *rec_props, int rec_num_props)
+{
+	int i, j;
+	int exp_prop_to_find = exp_num_props;
+
+	if (rec_num_props == 0)
+		return 1;
+
+	if (exp_num_props == 0) {
+		tester_debug("Wrong number of expected properties given");
+		tester_test_failed();
+		return 1;
+	}
+
+	/* Get first exp prop to match and search for it */
+	for (i = 0; i < exp_num_props; i++) {
+		for (j = 0; j < rec_num_props; j++) {
+			if (match_property(&exp_props[i], &rec_props[j], i)) {
+				exp_prop_to_find--;
+				break;
+			}
+		}
+	}
+
+	return exp_prop_to_find;
+}
+
+static int verify_mas_inst(btmce_mas_instance_t *exp_inst, int exp_num_inst,
+						btmce_mas_instance_t *rec_inst,
+						int rec_num_inst)
+{
+	int i, j;
+	int exp_inst_to_find = exp_num_inst;
+
+	if (rec_num_inst == 0)
+		return 1;
+
+	if (exp_num_inst == 0) {
+		tester_debug("Wrong number of expected MAS instances given");
+		tester_test_failed();
+		return 1;
+	}
+
+	for (i = 0; i < exp_num_inst; i++) {
+		for (j = 0; j < rec_num_inst; j++) {
+			if (match_mas_inst(&exp_inst[i], &rec_inst[i], i)) {
+				exp_inst_to_find--;
+				break;
+			}
+		}
+	}
+
+	return exp_inst_to_find;
+}
+
+/*
+ * Check each test case step if test case expected
+ * data is set and match it with expected result.
+ */
+
+static bool verify_gatt_ids(btgatt_gatt_id_t *a, btgatt_gatt_id_t *b)
+{
+
+	if (memcmp(&a->uuid, &b->uuid, sizeof(bt_uuid_t)))
+		return false;
+
+	if (a->inst_id != b->inst_id)
+		return false;
+
+	return true;
+}
+
+static bool verify_services(btgatt_srvc_id_t *a, btgatt_srvc_id_t *b)
+{
+	if (a->is_primary != b->is_primary)
+		return false;
+
+	return verify_gatt_ids(&a->id, &b->id);
+}
+
+static bool match_data(struct step *step)
+{
+	struct test_data *data = tester_get_data();
+	const struct step *exp;
+
+	exp = queue_peek_head(data->steps);
+
+	if (!exp) {
+		/* Can occure while test passed already */
+		tester_debug("Cannot get step to match");
+		return false;
+	}
+
+	if (exp->action_status != step->action_status) {
+		tester_debug("Action status don't match");
+		return false;
+	}
+
+	if (!exp->callback && !step->callback)
+		return true;
+
+	if (exp->callback != step->callback) {
+		tester_debug("Callback type mismatch: %s vs %s",
+						cb_table[step->callback].str,
+						cb_table[exp->callback].str);
+		return false;
+	}
+
+	if (exp->callback_result.state != step->callback_result.state) {
+		tester_debug("Callback state mismatch: %d vs %d",
+						step->callback_result.state,
+						exp->callback_result.state);
+		return false;
+	}
+
+	if (exp->callback_result.status != step->callback_result.status) {
+		tester_debug("Callback status mismatch: %d vs %d",
+						step->callback_result.status,
+						exp->callback_result.status);
+		return false;
+	}
+
+	if (exp->callback_result.mode != step->callback_result.mode) {
+		tester_debug("Callback mode mismatch: %02x vs %02x",
+						step->callback_result.mode,
+						exp->callback_result.mode);
+		return false;
+	}
+
+	if (exp->callback_result.report_size !=
+					step->callback_result.report_size) {
+		tester_debug("Callback report size mismatch: %d vs %d",
+					step->callback_result.report_size,
+					exp->callback_result.report_size);
+		return false;
+	}
+
+	if (exp->callback_result.ctrl_state !=
+					step->callback_result.ctrl_state) {
+		tester_debug("Callback ctrl state mismatch: %d vs %d",
+					step->callback_result.ctrl_state,
+					exp->callback_result.ctrl_state);
+		return false;
+	}
+
+	if (exp->callback_result.conn_state !=
+					step->callback_result.conn_state) {
+		tester_debug("Callback connection state mismatch: %d vs %d",
+					step->callback_result.conn_state,
+					exp->callback_result.conn_state);
+		return false;
+	}
+
+	if (exp->callback_result.local_role !=
+					step->callback_result.local_role) {
+		tester_debug("Callback local_role mismatch: %d vs %d",
+					step->callback_result.local_role,
+					exp->callback_result.local_role);
+		return false;
+	}
+
+	if (exp->callback_result.remote_role !=
+					step->callback_result.remote_role) {
+		tester_debug("Callback remote_role mismatch: %d vs %d",
+					step->callback_result.remote_role,
+					exp->callback_result.remote_role);
+		return false;
+	}
+
+	if (exp->callback_result.app_id != step->callback_result.app_id) {
+		tester_debug("Callback app_id mismatch: %d vs %d",
+						step->callback_result.app_id,
+						exp->callback_result.app_id);
+		return false;
+	}
+
+	if (exp->callback_result.channel_id !=
+					step->callback_result.channel_id) {
+		tester_debug("Callback channel_id mismatch: %d vs %d",
+					step->callback_result.channel_id,
+					exp->callback_result.channel_id);
+		return false;
+	}
+
+	if (exp->callback_result.mdep_cfg_index !=
+					step->callback_result.mdep_cfg_index) {
+		tester_debug("Callback mdep_cfg_index mismatch: %d vs %d",
+					step->callback_result.mdep_cfg_index,
+					exp->callback_result.mdep_cfg_index);
+		return false;
+	}
+
+	if (exp->callback_result.app_state != step->callback_result.app_state) {
+		tester_debug("Callback app_state mismatch: %d vs %d",
+						step->callback_result.app_state,
+						exp->callback_result.app_state);
+		return false;
+	}
+
+	if (exp->callback_result.channel_state !=
+					step->callback_result.channel_state) {
+		tester_debug("Callback channel_state mismatch: %d vs %d",
+					step->callback_result.channel_state,
+					exp->callback_result.channel_state);
+		return false;
+	}
+
+	if (exp->callback_result.av_conn_state !=
+					step->callback_result.av_conn_state) {
+		tester_debug("Callback av conn state mismatch: 0x%x vs 0x%x",
+					step->callback_result.av_conn_state,
+					exp->callback_result.av_conn_state);
+		return false;
+	}
+
+	if (exp->callback_result.av_audio_state !=
+					step->callback_result.av_audio_state) {
+		tester_debug("Callback av audio state mismatch: 0x%x vs 0x%x",
+					step->callback_result.av_audio_state,
+					exp->callback_result.av_audio_state);
+		return false;
+	}
+
+	if (exp->callback_result.song_length !=
+					step->callback_result.song_length) {
+		tester_debug("Callback song_length mismatch: 0x%x vs 0x%x",
+					step->callback_result.song_length,
+					exp->callback_result.song_length);
+		return false;
+	}
+
+	if (exp->callback_result.song_position !=
+					step->callback_result.song_position) {
+		tester_debug("Callback song_position mismatch: 0x%x vs 0x%x",
+					step->callback_result.song_position,
+					exp->callback_result.song_position);
+		return false;
+	}
+
+	if (exp->callback_result.play_status !=
+					step->callback_result.play_status) {
+		tester_debug("Callback play_status mismatch: 0x%x vs 0x%x",
+					step->callback_result.play_status,
+					exp->callback_result.play_status);
+		return false;
+	}
+
+	if (exp->callback_result.rc_index !=
+					step->callback_result.rc_index) {
+		tester_debug("Callback rc_index mismatch");
+		return false;
+	}
+
+	if (exp->callback_result.num_of_attrs !=
+					step->callback_result.num_of_attrs) {
+		tester_debug("Callback rc num of attrs mismatch");
+		return false;
+	}
+
+	if (exp->callback_result.attrs) {
+		if (memcmp(step->callback_result.attrs,
+				exp->callback_result.attrs,
+				exp->callback_result.num_of_attrs *
+				sizeof(btrc_element_attr_val_t))) {
+			tester_debug("Callback rc element attributes doesn't match");
+			return false;
+		}
+	}
+
+	if (exp->callback_result.pairing_variant !=
+					step->callback_result.pairing_variant) {
+		tester_debug("Callback pairing result mismatch: %d vs %d",
+					step->callback_result.pairing_variant,
+					exp->callback_result.pairing_variant);
+		return false;
+	}
+
+	if (exp->callback_result.adv_data != step->callback_result.adv_data) {
+		tester_debug("Callback adv. data status mismatch: %d vs %d",
+						step->callback_result.adv_data,
+						exp->callback_result.adv_data);
+		return false;
+	}
+
+	if (exp->callback_result.conn_id != step->callback_result.conn_id) {
+		tester_debug("Callback conn_id mismatch: %d vs %d",
+						step->callback_result.conn_id,
+						exp->callback_result.conn_id);
+		return false;
+	}
+
+	if (exp->callback_result.gatt_app_id !=
+					step->callback_result.gatt_app_id) {
+		tester_debug("Callback gatt_app_id mismatch: %d vs %d",
+					step->callback_result.gatt_app_id,
+					exp->callback_result.gatt_app_id);
+		return false;
+	}
+
+	if (exp->callback_result.properties &&
+			verify_property(exp->callback_result.properties,
+					exp->callback_result.num_properties,
+					step->callback_result.properties,
+					step->callback_result.num_properties)) {
+		tester_debug("Gatt properties don't match");
+		return false;
+	}
+
+	if (exp->callback_result.service &&
+			!verify_services(step->callback_result.service,
+						exp->callback_result.service)) {
+		tester_debug("Gatt service doesn't match");
+		return false;
+	}
+
+	if (exp->callback_result.characteristic) {
+		btgatt_gatt_id_t *a;
+		btgatt_gatt_id_t *b;
+		a = step->callback_result.characteristic;
+		b = exp->callback_result.characteristic;
+
+		if (!verify_gatt_ids(a, b)) {
+			tester_debug("Gatt char doesn't match");
+			return false;
+		}
+	}
+
+	if (exp->callback_result.char_prop != step->callback_result.char_prop) {
+		tester_debug("Gatt char prop mismatch: %d vs %d",
+						step->callback_result.char_prop,
+						exp->callback_result.char_prop);
+		return false;
+	}
+
+	if (exp->callback_result.descriptor) {
+		btgatt_gatt_id_t *a;
+		btgatt_gatt_id_t *b;
+		a = step->callback_result.descriptor;
+		b = exp->callback_result.descriptor;
+
+		if (!verify_gatt_ids(a, b)) {
+			tester_debug("Gatt desc doesn't match");
+			return false;
+		}
+	}
+
+	if (exp->callback_result.included) {
+		if (!verify_services(step->callback_result.included,
+					exp->callback_result.included)) {
+			tester_debug("Gatt include srvc doesn't match");
+			return false;
+		}
+	}
+
+	if (exp->callback_result.read_params) {
+		if (memcmp(step->callback_result.read_params,
+				exp->callback_result.read_params,
+				sizeof(btgatt_read_params_t))) {
+			tester_debug("Gatt read_param doesn't match");
+			return false;
+		}
+	}
+
+	if (exp->callback_result.write_params) {
+		if (memcmp(step->callback_result.write_params,
+				exp->callback_result.write_params,
+				sizeof(btgatt_write_params_t))) {
+			tester_debug("Gatt write_param doesn't match");
+			return false;
+		}
+
+		if (exp->callback_result.notification_registered !=
+				step->callback_result.notification_registered) {
+			tester_debug("Gatt registered flag mismatch");
+			return false;
+		}
+
+		if (exp->callback_result.notify_params) {
+			if (memcmp(step->callback_result.notify_params,
+					exp->callback_result.notify_params,
+					sizeof(btgatt_notify_params_t))) {
+				tester_debug("Gatt notify_param doesn't match");
+				return false;
+			}
+		}
+	}
+
+	if (exp->callback_result.connected !=
+				step->callback_result.connected) {
+		tester_debug("Gatt server conn status mismatch: %d vs %d",
+						step->callback_result.connected,
+						exp->callback_result.connected);
+		return false;
+	}
+
+	if (exp->callback_result.attr_handle &&
+					step->callback_result.attr_handle)
+		if (*exp->callback_result.attr_handle !=
+					*step->callback_result.attr_handle) {
+			tester_debug("Gatt attribute handle mismatch: %d vs %d",
+					*step->callback_result.attr_handle,
+					*exp->callback_result.attr_handle);
+			return false;
+		}
+
+	if (exp->callback_result.srvc_handle &&
+					step->callback_result.srvc_handle)
+		if (*exp->callback_result.srvc_handle !=
+					*step->callback_result.srvc_handle) {
+			tester_debug("Gatt service handle mismatch: %d vs %d",
+					*step->callback_result.srvc_handle,
+					*exp->callback_result.srvc_handle);
+			return false;
+		}
+
+	if (exp->callback_result.inc_srvc_handle &&
+					step->callback_result.inc_srvc_handle)
+		if (*exp->callback_result.inc_srvc_handle !=
+				*step->callback_result.inc_srvc_handle) {
+			tester_debug("Gatt inc. srvc handle mismatch: %d vs %d",
+					*step->callback_result.inc_srvc_handle,
+					*exp->callback_result.inc_srvc_handle);
+			return false;
+		}
+
+	if (exp->callback_result.uuid && step->callback_result.uuid)
+		if (memcmp(exp->callback_result.uuid,
+					step->callback_result.uuid,
+					sizeof(*exp->callback_result.uuid))) {
+			tester_debug("Uuid mismatch");
+			return false;
+		}
+
+	if (exp->callback_result.trans_id != step->callback_result.trans_id) {
+		tester_debug("Gatt trans id mismatch: %d vs %d",
+						exp->callback_result.trans_id,
+						step->callback_result.trans_id);
+		return false;
+	}
+
+	if (exp->callback_result.offset != step->callback_result.offset) {
+		tester_debug("Gatt offset mismatch: %d vs %d",
+						exp->callback_result.offset,
+						step->callback_result.offset);
+		return false;
+	}
+
+	if (exp->callback_result.is_long != step->callback_result.is_long) {
+		tester_debug("Gatt is long attr value flag mismatch: %d vs %d",
+						exp->callback_result.is_long,
+						step->callback_result.is_long);
+		return false;
+	}
+
+	if (exp->callback_result.length > 0) {
+		if (exp->callback_result.length !=
+						step->callback_result.length) {
+			tester_debug("Gatt attr length mismatch: %d vs %d",
+						exp->callback_result.length,
+						step->callback_result.length);
+			return false;
+		}
+		if (!exp->callback_result.value ||
+						!step->callback_result.value) {
+			tester_debug("Gatt attr values are wrong set");
+			return false;
+		}
+		if (!memcmp(exp->callback_result.value,
+						step->callback_result.value,
+						exp->callback_result.length)) {
+			tester_debug("Gatt attr value mismatch");
+			return false;
+		}
+	}
+
+	if (exp->callback_result.need_rsp != step->callback_result.need_rsp) {
+		tester_debug("Gatt need response value flag mismatch: %d vs %d",
+						exp->callback_result.need_rsp,
+						step->callback_result.need_rsp);
+		return false;
+	}
+
+	if (exp->callback_result.is_prep != step->callback_result.is_prep) {
+		tester_debug("Gatt is prepared value flag mismatch: %d vs %d",
+						exp->callback_result.is_prep,
+						step->callback_result.is_prep);
+		return false;
+	}
+
+	if (exp->callback_result.num_mas_instances !=
+				step->callback_result.num_mas_instances) {
+		tester_debug("Mas instance count mismatch: %d vs %d",
+				exp->callback_result.num_mas_instances,
+				step->callback_result.num_mas_instances);
+		return false;
+	}
+
+	if (exp->callback_result.mas_instances &&
+		verify_mas_inst(exp->callback_result.mas_instances,
+				exp->callback_result.num_mas_instances,
+				step->callback_result.mas_instances,
+				step->callback_result.num_mas_instances)) {
+		tester_debug("Mas instances don't match");
+		return false;
+	}
+
+	if (exp->callback_result.error != step->callback_result.error) {
+		tester_debug("Err mismatch: %d vs %d",
+				exp->callback_result.error,
+				step->callback_result.error);
+		return false;
+	}
+
+	if (exp->store_srvc_handle)
+		memcpy(exp->store_srvc_handle,
+					step->callback_result.srvc_handle,
+					sizeof(*exp->store_srvc_handle));
+
+	if (exp->store_char_handle)
+		memcpy(exp->store_char_handle,
+					step->callback_result.char_handle,
+					sizeof(*exp->store_char_handle));
+
+	return true;
+}
+
+static void init_test_steps(struct test_data *data)
+{
+	const struct test_case *test_steps = data->test_data;
+	int i = 0;
+
+	for (i = 0; i < test_steps->step_num; i++)
+		queue_push_tail(data->steps, (void *) &(test_steps->step[i]));
+
+	tester_print("tester: Number of test steps=%d",
+						queue_length(data->steps));
+}
+
+/*
+ * Each test case step should be verified, if match with
+ * expected result tester should go to next test step.
+ */
+static void verify_step(struct step *step, queue_destroy_func_t cleanup_cb)
+{
+	struct test_data *data = tester_get_data();
+	const struct test_case *test_steps = data->test_data;
+	struct step *next_step;
+
+	tester_debug("tester: STEP[%d] check",
+			test_steps->step_num-queue_length(data->steps) + 1);
+
+	if (step && !match_data(step)) {
+		if (cleanup_cb)
+			cleanup_cb(step);
+
+		return;
+	}
+
+	queue_pop_head(data->steps);
+
+	if (cleanup_cb)
+		cleanup_cb(step);
+
+	tester_debug("tester: STEP[%d] pass",
+			test_steps->step_num-queue_length(data->steps));
+
+	if (queue_isempty(data->steps)) {
+		tester_print("tester: All steps done, passing");
+		tester_test_passed();
+
+		return;
+	}
+
+	/* goto next step action if declared in step */
+	next_step = queue_peek_head(data->steps);
+
+	if (next_step->action)
+		next_step->action();
+}
+
+/*
+ * NOTICE:
+ * Its mandatory for callback to set proper step.callback value so that
+ * step verification could pass and move to next test step
+ */
+
+static void free_properties(struct step *step)
+{
+	bt_property_t *properties = step->callback_result.properties;
+	int num_properties = step->callback_result.num_properties;
+	int i;
+
+	for (i = 0; i < num_properties; i++)
+		g_free(properties[i].val);
+
+	g_free(properties);
+}
+
+static void free_mas_instances(struct step *step)
+{
+	btmce_mas_instance_t *mas_instances;
+	int num_instances;
+	int i;
+
+	mas_instances = step->callback_result.mas_instances;
+	num_instances = step->callback_result.num_mas_instances;
+
+	for (i = 0; i < num_instances; i++)
+		g_free(mas_instances[i].p_name);
+
+	g_free(mas_instances);
+}
+
+static void destroy_callback_step(void *data)
+{
+	struct step *step = data;
+
+	if (step->callback_result.properties)
+		free_properties(step);
+
+	if (step->callback_result.service)
+		free(step->callback_result.service);
+
+	if (step->callback_result.characteristic)
+		free(step->callback_result.characteristic);
+
+	if (step->callback_result.descriptor)
+		free(step->callback_result.descriptor);
+
+	if (step->callback_result.included)
+		free(step->callback_result.included);
+
+	if (step->callback_result.read_params)
+		free(step->callback_result.read_params);
+
+	if (step->callback_result.write_params)
+		free(step->callback_result.write_params);
+
+	if (step->callback_result.notify_params)
+		free(step->callback_result.notify_params);
+
+	if (step->callback_result.srvc_handle)
+		free(step->callback_result.srvc_handle);
+
+	if (step->callback_result.inc_srvc_handle)
+		free(step->callback_result.inc_srvc_handle);
+
+	if (step->callback_result.uuid)
+		free(step->callback_result.uuid);
+
+	if (step->callback_result.char_handle)
+		free(step->callback_result.char_handle);
+
+	if (step->callback_result.desc_handle)
+		free(step->callback_result.desc_handle);
+
+	if (step->callback_result.attr_handle)
+		free(step->callback_result.attr_handle);
+
+	if (step->callback_result.value)
+		free(step->callback_result.value);
+
+	if (step->callback_result.mas_instances)
+		free_mas_instances(step);
+
+	g_free(step);
+	g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+}
+
+static gboolean verify_action(gpointer user_data)
+{
+	struct step *step = user_data;
+
+	verify_step(step, g_free);
+
+	return FALSE;
+}
+
+static gboolean verify_callback(gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = user_data;
+
+	/* Return if callback came when all steps are already verified */
+	if (queue_isempty(data->steps)) {
+		destroy_callback_step(step);
+		return FALSE;
+	}
+
+	/*
+	 * TODO: This may call action from next step before callback data
+	 * from previous step was freed.
+	 */
+	verify_step(step, destroy_callback_step);
+
+	return FALSE;
+}
+
+void schedule_callback_verification(struct step *step)
+{
+	g_atomic_int_inc(&scheduled_cbacks_num);
+	g_idle_add(verify_callback, step);
+}
+
+void schedule_action_verification(struct step *step)
+{
+	g_idle_add_full(G_PRIORITY_HIGH_IDLE, verify_action, step, NULL);
+}
+
+static void adapter_state_changed_cb(bt_state_t state)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback_result.state = state;
+	step->callback = CB_BT_ADAPTER_STATE_CHANGED;
+
+	schedule_callback_verification(step);
+}
+
+static bt_property_t *copy_properties(int num_properties,
+						bt_property_t *properties)
+{
+	int i;
+	bt_property_t *props = g_new0(bt_property_t, num_properties);
+
+	for (i = 0; i < num_properties; i++) {
+		props[i].type = properties[i].type;
+		props[i].len = properties[i].len;
+		props[i].val = g_memdup(properties[i].val, properties[i].len);
+	}
+
+	return props;
+}
+
+static bt_property_t *repack_properties(int num_properties,
+						bt_property_t **properties)
+{
+	int i;
+	bt_property_t *props = g_new0(bt_property_t, num_properties);
+
+	for (i = 0; i < num_properties; i++) {
+		props[i].type = properties[i]->type;
+		props[i].len = properties[i]->len;
+		props[i].val = g_memdup(properties[i]->val, properties[i]->len);
+	}
+
+	return props;
+}
+
+static bt_property_t *create_property(bt_property_type_t type, void *val,
+								int len)
+{
+	bt_property_t *prop = g_new0(bt_property_t, 1);
+
+	prop->type = type;
+	prop->len = len;
+	prop->val = g_memdup(val, len);
+
+	return prop;
+}
+
+static void adapter_properties_cb(bt_status_t status, int num_properties,
+						bt_property_t *properties)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback_result.status = status;
+	step->callback_result.num_properties = num_properties;
+	step->callback_result.properties = copy_properties(num_properties,
+								properties);
+	step->callback = CB_BT_ADAPTER_PROPERTIES;
+
+	schedule_callback_verification(step);
+}
+
+static void discovery_state_changed_cb(bt_discovery_state_t state)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_BT_DISCOVERY_STATE_CHANGED;
+	step->callback_result.state = state;
+
+	schedule_callback_verification(step);
+}
+
+static void device_found_cb(int num_properties, bt_property_t *properties)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback_result.num_properties = num_properties;
+	step->callback_result.properties = copy_properties(num_properties,
+								properties);
+
+	step->callback = CB_BT_DEVICE_FOUND;
+
+	schedule_callback_verification(step);
+}
+
+static void remote_device_properties_cb(bt_status_t status,
+				bt_bdaddr_t *bd_addr, int num_properties,
+				bt_property_t *properties)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback_result.num_properties = num_properties;
+	step->callback_result.properties = copy_properties(num_properties,
+								properties);
+
+	step->callback = CB_BT_REMOTE_DEVICE_PROPERTIES;
+
+	schedule_callback_verification(step);
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+						bt_bdaddr_t *remote_bd_addr,
+						bt_bond_state_t state)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback_result.status = status;
+	step->callback_result.state = state;
+
+	/* Utilize property verification mechanism for bdaddr */
+	step->callback_result.num_properties = 1;
+	step->callback_result.properties =
+			create_property(BT_PROPERTY_BDADDR, remote_bd_addr,
+						sizeof(*remote_bd_addr));
+
+	step->callback = CB_BT_BOND_STATE_CHANGED;
+
+	schedule_callback_verification(step);
+}
+
+static void pin_request_cb(bt_bdaddr_t *remote_bd_addr,
+					bt_bdname_t *bd_name, uint32_t cod)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[3];
+
+	step->callback = CB_BT_PIN_REQUEST;
+
+	/* Utilize property verification mechanism for those */
+	props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr,
+						sizeof(*remote_bd_addr));
+	props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name,
+						strlen((char *) bd_name->name));
+	props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod,
+								sizeof(cod));
+
+	step->callback_result.num_properties = 3;
+	step->callback_result.properties = repack_properties(3, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+	g_free(props[1]->val);
+	g_free(props[1]);
+	g_free(props[2]->val);
+	g_free(props[2]);
+
+	schedule_callback_verification(step);
+}
+
+static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr,
+					bt_bdname_t *bd_name, uint32_t cod,
+					bt_ssp_variant_t pairing_variant,
+					uint32_t pass_key)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[3];
+
+	step->callback = CB_BT_SSP_REQUEST;
+
+	/* Utilize property verification mechanism for those */
+	props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr,
+						sizeof(*remote_bd_addr));
+	props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name,
+						strlen((char *) bd_name->name));
+	props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod,
+								sizeof(cod));
+
+	step->callback_result.num_properties = 3;
+	step->callback_result.properties = repack_properties(3, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+	g_free(props[1]->val);
+	g_free(props[1]);
+	g_free(props[2]->val);
+	g_free(props[2]);
+
+	schedule_callback_verification(step);
+}
+
+static void acl_state_changed_cb(bt_status_t status,
+					bt_bdaddr_t *remote_bd_addr,
+					bt_acl_state_t state) {
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_BT_ACL_STATE_CHANGED;
+
+	step->callback_result.status = status;
+	step->callback_result.state = state;
+
+	schedule_callback_verification(step);
+}
+
+static bt_callbacks_t bt_callbacks = {
+	.size = sizeof(bt_callbacks),
+	.adapter_state_changed_cb = adapter_state_changed_cb,
+	.adapter_properties_cb = adapter_properties_cb,
+	.remote_device_properties_cb = remote_device_properties_cb,
+	.device_found_cb = device_found_cb,
+	.discovery_state_changed_cb = discovery_state_changed_cb,
+	.pin_request_cb = pin_request_cb,
+	.ssp_request_cb = ssp_request_cb,
+	.bond_state_changed_cb = bond_state_changed_cb,
+	.acl_state_changed_cb = acl_state_changed_cb,
+	.thread_evt_cb = NULL,
+	.dut_mode_recv_cb = NULL,
+	.le_test_mode_cb = NULL,
+	.energy_info_cb = NULL,
+};
+
+static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr,
+						bthh_connection_state_t state)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HH_CONNECTION_STATE;
+	step->callback_result.state = state;
+
+	schedule_callback_verification(step);
+}
+
+static void hidhost_virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HH_VIRTUAL_UNPLUG;
+	step->callback_result.status = status;
+
+	schedule_callback_verification(step);
+}
+
+static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr,
+						bthh_status_t status,
+						bthh_protocol_mode_t mode)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HH_PROTOCOL_MODE;
+	step->callback_result.status = status;
+	step->callback_result.mode = mode;
+
+	/* TODO: add bdaddr to verify? */
+
+	schedule_callback_verification(step);
+}
+
+static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HH_HID_INFO;
+
+	schedule_callback_verification(step);
+}
+
+static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+						uint8_t *report, int size)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HH_GET_REPORT;
+
+	step->callback_result.status = status;
+	step->callback_result.report_size = size;
+
+	schedule_callback_verification(step);
+}
+
+static bthh_callbacks_t bthh_callbacks = {
+	.size = sizeof(bthh_callbacks),
+	.connection_state_cb = hidhost_connection_state_cb,
+	.hid_info_cb = hidhost_hid_info_cb,
+	.protocol_mode_cb = hidhost_protocol_mode_cb,
+	.idle_time_cb = NULL,
+	.get_report_cb = hidhost_get_report_cb,
+	.virtual_unplug_cb = hidhost_virtual_unplug_cb,
+	.handshake_cb = NULL,
+};
+
+static void gattc_register_client_cb(int status, int client_if,
+							bt_uuid_t *app_uuid)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_REGISTER_CLIENT;
+
+	step->callback_result.status = status;
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[2];
+
+	step->callback = CB_GATTC_SCAN_RESULT;
+	step->callback_result.adv_data = adv_data ? TRUE : FALSE;
+
+	/* Utilize property verification mechanism for those */
+	props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+	props[1] = create_property(BT_PROPERTY_REMOTE_RSSI, &rssi,
+								sizeof(rssi));
+
+	step->callback_result.num_properties = 2;
+	step->callback_result.properties = repack_properties(2, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+	g_free(props[1]->val);
+	g_free(props[1]);
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_connect_cb(int conn_id, int status, int client_if,
+							bt_bdaddr_t *bda)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[1];
+
+	step->callback = CB_GATTC_OPEN;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.gatt_app_id = client_if;
+
+	/* Utilize property verification mechanism for bdaddr */
+	props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+
+	step->callback_result.num_properties = 1;
+	step->callback_result.properties = repack_properties(1, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_disconnect_cb(int conn_id, int status, int client_if,
+							bt_bdaddr_t *bda)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[1];
+
+	step->callback = CB_GATTC_CLOSE;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.gatt_app_id = client_if;
+
+	/* Utilize property verification mechanism for bdaddr */
+	props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+
+	step->callback_result.num_properties = 1;
+	step->callback_result.properties = repack_properties(1, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_listen_cb(int status, int server_if)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_LISTEN;
+	step->callback_result.status = status;
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_SEARCH_RESULT;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_search_complete_cb(int conn_id, int status)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_SEARCH_COMPLETE;
+	step->callback_result.conn_id = conn_id;
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_get_characteristic_cb(int conn_id, int status,
+			btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+			int char_prop)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_GET_CHARACTERISTIC;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id));
+	step->callback_result.characteristic = g_memdup(char_id,
+							sizeof(*char_id));
+	step->callback_result.char_prop = char_prop;
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_get_descriptor_cb(int conn_id, int status,
+			btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
+			btgatt_gatt_id_t *descr_id)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_GET_DESCRIPTOR;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id));
+	step->callback_result.characteristic = g_memdup(char_id,
+							sizeof(*char_id));
+	step->callback_result.descriptor = g_memdup(descr_id,
+							sizeof(*descr_id));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_get_included_service_cb(int conn_id, int status,
+		btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_GET_INCLUDED_SERVICE;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id));
+	step->callback_result.included = g_memdup(incl_srvc_id,
+							sizeof(*srvc_id));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_read_characteristic_cb(int conn_id, int status,
+						btgatt_read_params_t *p_data)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_READ_CHARACTERISTIC;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.read_params = g_memdup(p_data, sizeof(*p_data));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_read_descriptor_cb(int conn_id, int status,
+						btgatt_read_params_t *p_data)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_READ_DESCRIPTOR;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.read_params = g_memdup(p_data, sizeof(*p_data));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_write_characteristic_cb(int conn_id, int status,
+						btgatt_write_params_t *p_data)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_WRITE_CHARACTERISTIC;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.write_params = g_memdup(p_data, sizeof(*p_data));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_write_descriptor_cb(int conn_id, int status,
+						btgatt_write_params_t *p_data)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_WRITE_DESCRIPTOR;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.write_params = g_memdup(p_data, sizeof(*p_data));
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_register_for_notification_cb(int conn_id, int registered,
+						int status,
+						btgatt_srvc_id_t *srvc_id,
+						btgatt_gatt_id_t *char_id)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_REGISTER_FOR_NOTIFICATION;
+	step->callback_result.status = status;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id));
+	step->callback_result.characteristic = g_memdup(char_id,
+							sizeof(*char_id));
+	step->callback_result.notification_registered = registered;
+
+	schedule_callback_verification(step);
+}
+
+static void gattc_notif_cb(int conn_id, btgatt_notify_params_t *p_data)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTC_NOTIFY;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.notify_params = g_memdup(p_data, sizeof(*p_data));
+
+	schedule_callback_verification(step);
+}
+
+static const btgatt_client_callbacks_t btgatt_client_callbacks = {
+	.register_client_cb = gattc_register_client_cb,
+	.scan_result_cb = gattc_scan_result_cb,
+	.open_cb = gattc_connect_cb,
+	.close_cb = gattc_disconnect_cb,
+	.search_complete_cb = gattc_search_complete_cb,
+	.search_result_cb = gattc_search_result_cb,
+	.get_characteristic_cb = gattc_get_characteristic_cb,
+	.get_descriptor_cb = gattc_get_descriptor_cb,
+	.get_included_service_cb = gattc_get_included_service_cb,
+	.register_for_notification_cb = gattc_register_for_notification_cb,
+	.notify_cb = gattc_notif_cb,
+	.read_characteristic_cb = gattc_read_characteristic_cb,
+	.write_characteristic_cb = gattc_write_characteristic_cb,
+	.read_descriptor_cb = gattc_read_descriptor_cb,
+	.write_descriptor_cb = gattc_write_descriptor_cb,
+	.execute_write_cb = NULL,
+	.read_remote_rssi_cb = NULL,
+	.listen_cb = gattc_listen_cb
+};
+
+static void gatts_register_server_cb(int status, int server_if,
+							bt_uuid_t *app_uuid)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_REGISTER_SERVER;
+
+	step->callback_result.status = status;
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_connection_cb(int conn_id, int server_if, int connected,
+							bt_bdaddr_t *bda)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[1];
+
+	step->callback = CB_GATTS_CONNECTION;
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.connected = connected;
+
+	/* Utilize property verification mechanism for bdaddr */
+	props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+
+	step->callback_result.num_properties = 1;
+	step->callback_result.properties = repack_properties(1, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_service_added_cb(int status, int server_if,
+						btgatt_srvc_id_t *srvc_id,
+						int srvc_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_SERVICE_ADDED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id));
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_included_service_added_cb(int status, int server_if,
+							int srvc_handle,
+							int inc_srvc_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_INCLUDED_SERVICE_ADDED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+	step->callback_result.inc_srvc_handle = g_memdup(&inc_srvc_handle,
+						sizeof(inc_srvc_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_characteristic_added_cb(int status, int server_if,
+								bt_uuid_t *uuid,
+								int srvc_handle,
+								int char_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_CHARACTERISTIC_ADDED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+	step->callback_result.uuid = g_memdup(uuid, sizeof(*uuid));
+	step->callback_result.char_handle = g_memdup(&char_handle,
+							sizeof(char_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_descriptor_added_cb(int status, int server_if,
+								bt_uuid_t *uuid,
+								int srvc_handle,
+								int desc_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_DESCRIPTOR_ADDED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+	step->callback_result.uuid = g_memdup(uuid, sizeof(*uuid));
+	step->callback_result.desc_handle = g_memdup(&desc_handle,
+							sizeof(desc_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_service_started_cb(int status, int server_if, int srvc_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_SERVICE_STARTED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_SERVICE_STOPPED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_SERVICE_DELETED;
+
+	step->callback_result.status = status;
+	step->callback_result.gatt_app_id = server_if;
+	step->callback_result.srvc_handle = g_memdup(&srvc_handle,
+							sizeof(srvc_handle));
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+						int attr_handle, int offset,
+						bool is_long)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[1];
+
+	step->callback = CB_GATTS_REQUEST_READ;
+
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.trans_id = trans_id;
+	step->callback_result.attr_handle = g_memdup(&attr_handle,
+							sizeof(attr_handle));
+	step->callback_result.offset = offset;
+	step->callback_result.is_long = is_long;
+
+	/* Utilize property verification mechanism for bdaddr */
+	props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+
+	step->callback_result.num_properties = 1;
+	step->callback_result.properties = repack_properties(1, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+						int attr_handle, int offset,
+						int length, bool need_rsp,
+						bool is_prep, uint8_t *value)
+{
+	struct step *step = g_new0(struct step, 1);
+	bt_property_t *props[1];
+
+	step->callback = CB_GATTS_REQUEST_WRITE;
+
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.trans_id = trans_id;
+	step->callback_result.attr_handle = g_memdup(&attr_handle,
+							sizeof(attr_handle));
+	step->callback_result.offset = offset;
+	step->callback_result.length = length;
+	step->callback_result.need_rsp = need_rsp;
+	step->callback_result.is_prep = is_prep;
+	step->callback_result.value = g_memdup(&value, length);
+
+	/* Utilize property verification mechanism for bdaddr */
+	props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+
+	step->callback_result.num_properties = 1;
+	step->callback_result.properties = repack_properties(1, props);
+
+	g_free(props[0]->val);
+	g_free(props[0]);
+
+	schedule_callback_verification(step);
+}
+
+static void gatts_indication_send_cb(int conn_id, int status)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_GATTS_INDICATION_SEND;
+
+	step->callback_result.conn_id = conn_id;
+	step->callback_result.status = status;
+
+	schedule_callback_verification(step);
+}
+
+static const btgatt_server_callbacks_t btgatt_server_callbacks = {
+	.register_server_cb = gatts_register_server_cb,
+	.connection_cb = gatts_connection_cb,
+	.service_added_cb = gatts_service_added_cb,
+	.included_service_added_cb = gatts_included_service_added_cb,
+	.characteristic_added_cb = gatts_characteristic_added_cb,
+	.descriptor_added_cb = gatts_descriptor_added_cb,
+	.service_started_cb = gatts_service_started_cb,
+	.service_stopped_cb = gatts_service_stopped_cb,
+	.service_deleted_cb = gatts_service_deleted_cb,
+	.request_read_cb = gatts_request_read_cb,
+	.request_write_cb = gatts_request_write_cb,
+	.request_exec_write_cb = NULL,
+	.response_confirmation_cb = NULL,
+	.indication_sent_cb = gatts_indication_send_cb,
+	.congestion_cb = NULL,
+};
+
+static const btgatt_callbacks_t btgatt_callbacks = {
+	.size = sizeof(btgatt_callbacks),
+	.client = &btgatt_client_callbacks,
+	.server = &btgatt_server_callbacks
+};
+
+static void pan_control_state_cb(btpan_control_state_t state, int local_role,
+					bt_status_t error, const char *ifname)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_PAN_CONTROL_STATE;
+	step->callback_result.state = error;
+	step->callback_result.ctrl_state = state;
+	step->callback_result.local_role = local_role;
+
+	schedule_callback_verification(step);
+}
+
+static void pan_connection_state_cb(btpan_connection_state_t state,
+					bt_status_t error,
+					const bt_bdaddr_t *bd_addr,
+					int local_role, int remote_role)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_PAN_CONNECTION_STATE;
+	step->callback_result.state = error;
+	step->callback_result.conn_state = state;
+	step->callback_result.local_role = local_role;
+	step->callback_result.remote_role = remote_role;
+
+	schedule_callback_verification(step);
+}
+
+static btpan_callbacks_t btpan_callbacks = {
+	.size = sizeof(btpan_callbacks),
+	.control_state_cb = pan_control_state_cb,
+	.connection_state_cb = pan_connection_state_cb,
+};
+
+static void hdp_app_reg_state_cb(int app_id, bthl_app_reg_state_t state)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HDP_APP_REG_STATE;
+	step->callback_result.app_id = app_id;
+	step->callback_result.app_state = state;
+
+	schedule_callback_verification(step);
+}
+
+static void hdp_channel_state_cb(int app_id, bt_bdaddr_t *bd_addr,
+				int mdep_cfg_index, int channel_id,
+				bthl_channel_state_t state, int fd)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_HDP_CHANNEL_STATE;
+	step->callback_result.app_id = app_id;
+	step->callback_result.channel_id = channel_id;
+	step->callback_result.mdep_cfg_index = mdep_cfg_index;
+	step->callback_result.channel_state = state;
+
+	schedule_callback_verification(step);
+}
+
+static bthl_callbacks_t bthl_callbacks = {
+	.size = sizeof(bthl_callbacks),
+	.app_reg_state_cb = hdp_app_reg_state_cb,
+	.channel_state_cb = hdp_channel_state_cb,
+};
+
+static void a2dp_connection_state_cb(btav_connection_state_t state,
+							bt_bdaddr_t *bd_addr)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_A2DP_CONN_STATE;
+	step->callback_result.av_conn_state = state;
+
+	schedule_callback_verification(step);
+}
+
+static void a2dp_audio_state_cb(btav_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_A2DP_AUDIO_STATE;
+	step->callback_result.av_audio_state = state;
+
+	schedule_callback_verification(step);
+}
+
+static btav_callbacks_t bta2dp_callbacks = {
+	.size = sizeof(bta2dp_callbacks),
+	.connection_state_cb = a2dp_connection_state_cb,
+	.audio_state_cb = a2dp_audio_state_cb,
+};
+
+static void avrcp_get_play_status_cb(void)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_AVRCP_PLAY_STATUS_REQ;
+	schedule_callback_verification(step);
+}
+
+static void avrcp_register_notification_cb(btrc_event_id_t event_id,
+								uint32_t param)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_AVRCP_REG_NOTIF_REQ;
+	schedule_callback_verification(step);
+}
+
+static void avrcp_get_element_attr_cb(uint8_t num_attr,
+						btrc_media_attr_t *p_attrs)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback = CB_AVRCP_GET_ATTR_REQ;
+	schedule_callback_verification(step);
+}
+
+static btrc_callbacks_t btavrcp_callbacks = {
+	.size = sizeof(btavrcp_callbacks),
+	.get_play_status_cb = avrcp_get_play_status_cb,
+	.register_notification_cb = avrcp_register_notification_cb,
+	.get_element_attr_cb = avrcp_get_element_attr_cb,
+};
+
+static btmce_mas_instance_t *copy_mas_instances(int num_instances,
+						btmce_mas_instance_t *instances)
+{
+	int i;
+	btmce_mas_instance_t *inst;
+
+	inst = g_new0(btmce_mas_instance_t, num_instances);
+
+	for (i = 0; i < num_instances; i++) {
+		inst[i].id = instances[i].id;
+		inst[i].scn = instances[i].scn;
+		inst[i].msg_types = instances[i].msg_types;
+		inst[i].p_name = g_memdup(instances[i].p_name,
+						strlen(instances[i].p_name));
+	}
+
+	return inst;
+}
+
+static void map_client_get_remote_mas_instances_cb(bt_status_t status,
+							bt_bdaddr_t *bd_addr,
+							int num_instances,
+							btmce_mas_instance_t
+							*instances)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->callback_result.status = status;
+	step->callback_result.num_mas_instances = num_instances;
+	step->callback_result.mas_instances = copy_mas_instances(num_instances,
+								instances);
+
+	step->callback = CB_MAP_CLIENT_REMOTE_MAS_INSTANCES;
+
+	schedule_callback_verification(step);
+}
+
+static btmce_callbacks_t btmap_client_callbacks = {
+	.size = sizeof(btmap_client_callbacks),
+	.remote_mas_instances_cb = map_client_get_remote_mas_instances_cb,
+};
+
+static bool setup_base(struct test_data *data)
+{
+	const hw_module_t *module;
+	hw_device_t *device;
+	int signal_fd[2];
+	char buf[1024];
+	pid_t pid;
+	int len;
+	int err;
+
+	if (pipe(signal_fd))
+		return false;
+
+	pid = fork();
+
+	if (pid < 0) {
+		close(signal_fd[0]);
+		close(signal_fd[1]);
+		return false;
+	}
+
+	if (pid == 0) {
+		if (!tester_use_debug())
+			fclose(stderr);
+
+		close(signal_fd[0]);
+		emulator(signal_fd[1], data->mgmt_index);
+		exit(0);
+	}
+
+	close(signal_fd[1]);
+	data->bluetoothd_pid = pid;
+
+	len = read(signal_fd[0], buf, sizeof(buf));
+	if (len <= 0 || strcmp(buf, EMULATOR_SIGNAL)) {
+		close(signal_fd[0]);
+		return false;
+	}
+
+	close(signal_fd[0]);
+
+	err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
+					AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
+	if (err)
+		return false;
+
+	err = audio_hw_device_open(module, &data->audio);
+	if (err)
+		return false;
+
+	err = hw_get_module(BT_HARDWARE_MODULE_ID, &module);
+	if (err)
+		return false;
+
+	err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
+	if (err)
+		return false;
+
+	data->device = device;
+
+	data->if_bluetooth = ((bluetooth_device_t *)
+					device)->get_bluetooth_interface();
+	if (!data->if_bluetooth)
+		return false;
+
+	if (!(data->steps = queue_new()))
+		return false;
+
+	data->pdus = queue_new();
+	if (!data->pdus) {
+		queue_destroy(data->steps, NULL);
+		return false;
+	}
+
+	return true;
+}
+
+static void setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_socket(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *sock;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID);
+	if (!sock) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_sock = sock;
+
+	tester_setup_complete();
+}
+
+static void setup_hidhost(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *hid;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID);
+	if (!hid) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_hid = hid;
+
+	status = data->if_hid->init(&bthh_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_hid = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_pan(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *pan;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	pan = data->if_bluetooth->get_profile_interface(BT_PROFILE_PAN_ID);
+	if (!pan) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_pan = pan;
+
+	status = data->if_pan->init(&btpan_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_pan = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_hdp(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *hdp;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	hdp = data->if_bluetooth->get_profile_interface(BT_PROFILE_HEALTH_ID);
+	if (!hdp) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_hdp = hdp;
+
+	status = data->if_hdp->init(&bthl_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_hdp = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_a2dp(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_interface_t *if_bt;
+	bt_status_t status;
+	const void *a2dp;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	if_bt = data->if_bluetooth;
+
+	status = if_bt->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
+	if (!a2dp) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_a2dp = a2dp;
+
+	status = data->if_a2dp->init(&bta2dp_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_a2dp = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_avrcp(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const bt_interface_t *if_bt;
+	bt_status_t status;
+	const void *a2dp, *avrcp;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	if_bt = data->if_bluetooth;
+
+	status = if_bt->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
+	if (!a2dp) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_a2dp = a2dp;
+
+	status = data->if_a2dp->init(&bta2dp_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_a2dp = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	avrcp = if_bt->get_profile_interface(BT_PROFILE_AV_RC_ID);
+	if (!avrcp) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_avrcp = avrcp;
+
+	status = data->if_avrcp->init(&btavrcp_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_avrcp = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_gatt(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *gatt;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	gatt = data->if_bluetooth->get_profile_interface(BT_PROFILE_GATT_ID);
+	if (!gatt) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_gatt = gatt;
+
+	status = data->if_gatt->init(&btgatt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_gatt = NULL;
+
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_map_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bt_status_t status;
+	const void *map_client;
+
+	if (!setup_base(data)) {
+		tester_setup_failed();
+		return;
+	}
+
+	status = data->if_bluetooth->init(&bt_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_bluetooth = NULL;
+		tester_setup_failed();
+		return;
+	}
+
+	map_client = data->if_bluetooth->get_profile_interface(
+						BT_PROFILE_MAP_CLIENT_ID);
+	if (!map_client) {
+		tester_setup_failed();
+		return;
+	}
+
+	data->if_map_client = map_client;
+
+	status = data->if_map_client->init(&btmap_client_callbacks);
+	if (status != BT_STATUS_SUCCESS) {
+		data->if_map_client = NULL;
+
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	queue_destroy(data->steps, NULL);
+	data->steps = NULL;
+
+	queue_destroy(data->pdus, NULL);
+	data->pdus = NULL;
+
+	/* no cleanup for map_client */
+	data->if_map_client = NULL;
+
+	if (data->if_gatt) {
+		data->if_gatt->cleanup();
+		data->if_gatt = NULL;
+	}
+
+	if (data->if_hid) {
+		data->if_hid->cleanup();
+		data->if_hid = NULL;
+	}
+
+	if (data->if_pan) {
+		data->if_pan->cleanup();
+		data->if_pan = NULL;
+	}
+
+	if (data->if_hdp) {
+		data->if_hdp->cleanup();
+		data->if_hdp = NULL;
+	}
+
+	if (data->if_stream) {
+		data->audio->close_output_stream(data->audio, data->if_stream);
+		data->if_stream = NULL;
+	}
+
+	if (data->if_a2dp) {
+		data->if_a2dp->cleanup();
+		data->if_a2dp = NULL;
+	}
+
+	if (data->if_avrcp) {
+		data->if_avrcp->cleanup();
+		data->if_avrcp = NULL;
+	}
+
+	if (data->if_bluetooth) {
+		data->if_bluetooth->cleanup();
+		data->if_bluetooth = NULL;
+	}
+
+	data->device->close(data->device);
+	audio_hw_device_close(data->audio);
+
+	/*
+	 * Ssp_request_cb pointer can be set do default_ssp_req_cb.
+	 * Set it back to ssp_request_cb
+	 */
+	bt_callbacks.ssp_request_cb = ssp_request_cb;
+
+	if (!data->bluetoothd_pid)
+		tester_teardown_complete();
+}
+
+static void emu_connectable_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	struct step *step;
+	struct test_data *data = user_data;
+
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+		break;
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		/*
+		 * For BREDRLE emulator we want to verify step after scan
+		 * enable and not after le_set_adv_enable
+		 */
+		if (data->hciemu_type == HCIEMU_TYPE_BREDRLE)
+			return;
+
+		break;
+	default:
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	if (status) {
+		tester_warn("Emulated remote setup failed.");
+		step->action_status = BT_STATUS_FAIL;
+	} else {
+		tester_debug("Emulated remote setup done.");
+		step->action_status = BT_STATUS_SUCCESS;
+	}
+
+	schedule_action_verification(step);
+}
+
+void emu_setup_powered_remote_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data);
+
+	if ((data->hciemu_type == HCIEMU_TYPE_LE) ||
+				(data->hciemu_type == HCIEMU_TYPE_BREDRLE)) {
+		uint8_t adv[4];
+
+		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);
+	}
+
+	if (data->hciemu_type != HCIEMU_TYPE_LE)
+		bthost_write_scan_enable(bthost, 0x03);
+}
+
+void emu_set_pin_code_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct bthost *bthost;
+	struct step *step = g_new0(struct step, 1);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	bthost_set_pin_code(bthost, action_data->pin->pin,
+							action_data->pin_len);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	tester_print("Setting emu pin done.");
+
+	schedule_action_verification(step);
+}
+
+void emu_set_ssp_mode_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+	struct step *step = g_new0(struct step, 1);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	bthost_write_ssp_mode(bthost, 0x01);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+void emu_set_connect_cb_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct step *current_data_step = queue_peek_head(data->steps);
+	void *cb = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	bthost_set_connect_cb(bthost, cb, data);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+void emu_remote_connect_hci_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+	const uint8_t *master_addr;
+
+	master_addr = hciemu_get_master_bdaddr(data->hciemu);
+
+	tester_print("Trying to connect hci");
+
+	if (action_data)
+		bthost_hci_connect(bthost, master_addr,
+						action_data->bearer_type);
+	else
+		bthost_hci_connect(bthost, master_addr, BDADDR_BREDR);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+void emu_remote_disconnect_hci_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	struct step *current_data_step = queue_peek_head(data->steps);
+	uint16_t *handle = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	if (handle) {
+		bthost_hci_disconnect(bthost, *handle, 0x13);
+		step->action_status = BT_STATUS_SUCCESS;
+	} else {
+		step->action_status = BT_STATUS_FAIL;
+	}
+
+	schedule_action_verification(step);
+}
+
+void emu_set_io_cap(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	if (action_data)
+		bthost_set_io_capability(bthost, action_data->io_cap);
+	else
+		bthost_set_io_capability(bthost, 0x01);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+void emu_add_l2cap_server_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct emu_set_l2cap_data *l2cap_data = current_data_step->set_data;
+	struct bthost *bthost;
+	struct step *step = g_new0(struct step, 1);
+
+	if (!l2cap_data) {
+		tester_warn("Invalid l2cap_data params");
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	bthost_add_l2cap_server(bthost, l2cap_data->psm, l2cap_data->func,
+							l2cap_data->user_data);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+done:
+	schedule_action_verification(step);
+}
+
+static void print_data(const char *str, void *user_data)
+{
+	tester_debug("tester: %s", str);
+}
+
+static void emu_generic_cid_hook_cb(const void *data, uint16_t len,
+								void *user_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct emu_l2cap_cid_data *cid_data = user_data;
+	const struct pdu_set *pdus = cid_data->pdu;
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	int i;
+
+	for (i = 0; pdus[i].rsp.iov_base; i++) {
+		if (pdus[i].req.iov_base) {
+			if (pdus[i].req.iov_len != len)
+				continue;
+
+			if (memcmp(pdus[i].req.iov_base, data, len))
+				continue;
+		}
+
+		if (pdus[i].rsp.iov_base) {
+			util_hexdump('>', pdus[i].rsp.iov_base,
+					pdus[i].rsp.iov_len, print_data, NULL);
+
+			/* if its sdp pdu use transaction ID from request */
+			if (cid_data->is_sdp) {
+				struct iovec rsp[3];
+
+				rsp[0].iov_base = pdus[i].rsp.iov_base;
+				rsp[0].iov_len = 1;
+
+				rsp[1].iov_base = ((uint8_t *) data) + 1;
+				rsp[1].iov_len = 2;
+
+				rsp[2].iov_base = pdus[i].rsp.iov_base + 3;
+				rsp[2].iov_len = pdus[i].rsp.iov_len - 3;
+
+				bthost_send_cid_v(bthost, cid_data->handle,
+							cid_data->cid, rsp, 3);
+			} else {
+				bthost_send_cid_v(bthost, cid_data->handle,
+						cid_data->cid, &pdus[i].rsp, 1);
+			}
+
+		}
+	}
+}
+
+void tester_handle_l2cap_data_exchange(struct emu_l2cap_cid_data *cid_data)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+
+	bthost_add_cid_hook(bthost, cid_data->handle, cid_data->cid,
+					emu_generic_cid_hook_cb, cid_data);
+}
+
+void tester_generic_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+	struct emu_l2cap_cid_data *cid_data = user_data;
+
+	cid_data->handle = handle;
+	cid_data->cid = cid;
+
+	tester_handle_l2cap_data_exchange(cid_data);
+}
+
+static void rfcomm_connect_cb(uint16_t handle, uint16_t cid, void *user_data,
+								bool status)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	tester_print("Connect handle %d, cid %d cb status: %d", handle, cid,
+									status);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+void emu_add_rfcomm_server_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *rfcomm_data = current_data_step->set_data;
+	struct bthost *bthost;
+	struct step *step;
+
+	if (!rfcomm_data) {
+		tester_warn("Invalid l2cap_data params");
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	bthost_add_rfcomm_server(bthost, rfcomm_data->channel,
+						rfcomm_connect_cb, data);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+void dummy_action(void)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	step->action = dummy_action;
+
+	schedule_action_verification(step);
+}
+
+void bluetooth_enable_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->enable();
+
+	schedule_action_verification(step);
+}
+
+void bluetooth_disable_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->disable();
+
+	schedule_action_verification(step);
+}
+
+void bt_set_property_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step;
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_property_t *prop;
+
+	if (!current_data_step->set_data) {
+		tester_debug("BT property not set for step");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	prop = (bt_property_t *)current_data_step->set_data;
+
+	step->action_status = data->if_bluetooth->set_adapter_property(prop);
+
+	schedule_action_verification(step);
+}
+
+void bt_get_property_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step;
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_property_t *prop;
+
+	if (!current_data_step->set_data) {
+		tester_debug("BT property to get not defined");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	prop = (bt_property_t *)current_data_step->set_data;
+
+	step->action_status = data->if_bluetooth->get_adapter_property(
+								prop->type);
+
+	schedule_action_verification(step);
+}
+
+void bt_start_discovery_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->start_discovery();
+
+	schedule_action_verification(step);
+}
+
+void bt_cancel_discovery_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->cancel_discovery();
+
+	schedule_action_verification(step);
+}
+
+void bt_get_device_props_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct step *step;
+
+	if (!current_data_step->set_data) {
+		tester_debug("bdaddr not defined");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	step->action_status =
+		data->if_bluetooth->get_remote_device_properties(
+						current_data_step->set_data);
+
+	schedule_action_verification(step);
+}
+
+void bt_get_device_prop_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step;
+
+	if (!action_data) {
+		tester_warn("No arguments for 'get remote device prop' req.");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->get_remote_device_property(
+							action_data->addr,
+							action_data->prop_type);
+
+	schedule_action_verification(step);
+}
+
+void bt_set_device_prop_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step;
+
+	if (!action_data) {
+		tester_warn("No arguments for 'set remote device prop' req.");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->set_remote_device_property(
+							action_data->addr,
+							action_data->prop);
+
+	schedule_action_verification(step);
+}
+
+void bt_create_bond_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step;
+
+	if (!action_data || !action_data->addr) {
+		tester_warn("Bad arguments for 'create bond' req.");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	step->action_status =
+			data->if_bluetooth->create_bond(action_data->addr,
+						action_data->transport_type ?
+						action_data->transport_type :
+						BT_TRANSPORT_UNKNOWN);
+
+	schedule_action_verification(step);
+}
+
+void bt_pin_reply_accept_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step;
+
+	if (!action_data || !action_data->addr || !action_data->pin) {
+		tester_warn("Bad arguments for 'pin reply' req.");
+		tester_test_failed();
+		return;
+	}
+
+	step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->pin_reply(action_data->addr,
+							TRUE,
+							action_data->pin_len,
+							action_data->pin);
+
+	schedule_action_verification(step);
+}
+
+void bt_ssp_reply_accept_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->ssp_reply(action_data->addr,
+						action_data->ssp_variant,
+						action_data->accept, 0);
+
+	schedule_action_verification(step);
+}
+
+void bt_cancel_bond_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_bdaddr_t *addr = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->cancel_bond(addr);
+
+	schedule_action_verification(step);
+}
+
+void bt_remove_bond_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_bdaddr_t *addr = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_bluetooth->remove_bond(addr);
+
+	schedule_action_verification(step);
+}
+
+static void default_ssp_req_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *name,
+				uint32_t cod, bt_ssp_variant_t pairing_variant,
+				uint32_t pass_key)
+{
+	struct test_data *t_data = tester_get_data();
+
+	t_data->if_bluetooth->ssp_reply(remote_bd_addr, pairing_variant, true,
+								pass_key);
+}
+
+void set_default_ssp_request_handler(void)
+{
+	struct step *step = g_new0(struct step, 1);
+
+	bt_callbacks.ssp_request_cb = default_ssp_req_cb;
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+	schedule_action_verification(step);
+}
+
+static void generic_test_function(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct step *first_step;
+
+	init_test_steps(data);
+
+	/* first step action */
+	first_step = queue_peek_head(data->steps);
+	if (!first_step->action) {
+		tester_print("tester: No initial action declared");
+		tester_test_failed();
+		return;
+	}
+	first_step->action();
+}
+
+#define test(data, test_setup, test, test_teardown) \
+	do { \
+		struct test_data *user; \
+		user = g_malloc0(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = data->emu_type; \
+		user->test_data = data; \
+		tester_add_full(data->title, data, test_pre_setup, \
+					test_setup, test, test_teardown, \
+					test_post_teardown, 3, user, g_free); \
+	} while (0)
+
+static void tester_testcases_cleanup(void)
+{
+	remove_bluetooth_tests();
+	remove_socket_tests();
+	remove_hidhost_tests();
+	remove_gatt_tests();
+	remove_a2dp_tests();
+	remove_avrcp_tests();
+	remove_hdp_tests();
+	remove_pan_tests();
+}
+
+static void add_bluetooth_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup, generic_test_function, teardown);
+}
+
+static void add_socket_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_socket, generic_test_function, teardown);
+}
+
+static void add_hidhost_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_hidhost, generic_test_function, teardown);
+}
+
+static void add_pan_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_pan, generic_test_function, teardown);
+}
+
+static void add_hdp_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_hdp, generic_test_function, teardown);
+}
+
+static void add_a2dp_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_a2dp, generic_test_function, teardown);
+}
+
+static void add_avrcp_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_avrcp, generic_test_function, teardown);
+}
+
+static void add_gatt_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_gatt, generic_test_function, teardown);
+}
+
+static void add_map_client_tests(void *data, void *user_data)
+{
+	struct test_case *tc = data;
+
+	test(tc, setup_map_client, generic_test_function, teardown);
+}
+
+int main(int argc, char *argv[])
+{
+	snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+	tester_init(&argc, &argv);
+
+	queue_foreach(get_bluetooth_tests(), add_bluetooth_tests, NULL);
+	queue_foreach(get_socket_tests(), add_socket_tests, NULL);
+	queue_foreach(get_hidhost_tests(), add_hidhost_tests, NULL);
+	queue_foreach(get_pan_tests(), add_pan_tests, NULL);
+	queue_foreach(get_hdp_tests(), add_hdp_tests, NULL);
+	queue_foreach(get_a2dp_tests(), add_a2dp_tests, NULL);
+	queue_foreach(get_avrcp_tests(), add_avrcp_tests, NULL);
+	queue_foreach(get_gatt_tests(), add_gatt_tests, NULL);
+	queue_foreach(get_map_client_tests(), add_map_client_tests, NULL);
+
+	if (tester_run())
+		return 1;
+
+	tester_testcases_cleanup();
+
+	return 0;
+}
diff --git a/bluez/repo/android/tester-main.h b/bluez/repo/android/tester-main.h
new file mode 100644
index 0000000..8a7384c
--- /dev/null
+++ b/bluez/repo/android/tester-main.h
@@ -0,0 +1,801 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <glib.h>
+#include <hardware/audio.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hh.h>
+#include <hardware/bt_pan.h>
+#include <hardware/bt_hl.h>
+#include <hardware/bt_av.h>
+#include <hardware/bt_rc.h>
+#include <hardware/bt_gatt.h>
+
+#include "emulator/hciemu.h"
+#include <hardware/bt_mce.h>
+
+struct pdu_set {
+	struct iovec req;
+	struct iovec rsp;
+};
+
+#define raw_data(args...) ((unsigned char[]) { args })
+
+#define raw_pdu(args...)					\
+	{							\
+		.iov_base = raw_data(args),			\
+		.iov_len = sizeof(raw_data(args)),		\
+	}
+
+#define end_pdu { .iov_base = NULL }
+
+#define TEST_CASE_BREDR(text, ...) { \
+		HCIEMU_TYPE_BREDR, \
+		text, \
+		sizeof((struct step[]) {__VA_ARGS__}) / sizeof(struct step), \
+		(struct step[]) {__VA_ARGS__}, \
+	}
+
+#define TEST_CASE_BREDRLE(text, ...) { \
+		HCIEMU_TYPE_BREDRLE, \
+		text, \
+		sizeof((struct step[]) {__VA_ARGS__}) / sizeof(struct step), \
+		(struct step[]) {__VA_ARGS__}, \
+	}
+
+#define MODIFY_DATA(status, modif_fun, from, to, len) { \
+		.action_status = status, \
+		.action = modif_fun, \
+		.set_data = from, \
+		.set_data_2 = to, \
+		.set_data_len = len, \
+	}
+
+#define PROCESS_DATA(status, proc_fun, data1, data2, data3) { \
+		.action_status = status, \
+		.action = proc_fun, \
+		.set_data = data1, \
+		.set_data_2 = data2, \
+		.set_data_3 = data3, \
+	}
+
+#define ACTION(status, act_fun, data_set) { \
+		.action_status = status, \
+		.action = act_fun, \
+		.set_data = data_set, \
+	}
+
+#define ACTION_FAIL(act_fun, data_set) \
+		ACTION(BT_STATUS_FAIL, act_fun, data_set)
+
+#define ACTION_SUCCESS(act_fun, data_set) \
+		ACTION(BT_STATUS_SUCCESS, act_fun, data_set)
+
+#define CALLBACK(cb) { \
+		.callback = cb, \
+	}
+
+#define CALLBACK_STATE(cb, cb_res) { \
+		.callback = cb, \
+		.callback_result.state = cb_res, \
+	}
+
+#define CALLBACK_STATUS(cb, cb_res) { \
+		.callback = cb, \
+		.callback_result.status = cb_res, \
+	}
+
+#define CALLBACK_ERROR(cb, cb_err) { \
+		.callback = cb, \
+		.callback_result.error = cb_err, \
+	}
+
+#define CALLBACK_ADAPTER_PROPS(props, prop_cnt) { \
+		.callback = CB_BT_ADAPTER_PROPERTIES, \
+		.callback_result.properties = props, \
+		.callback_result.num_properties = prop_cnt, \
+	}
+
+#define CALLBACK_PROPS(cb, props, prop_cnt) { \
+		.callback = cb, \
+		.callback_result.properties = props, \
+		.callback_result.num_properties = prop_cnt, \
+	}
+
+#define CALLBACK_HH_MODE(cb, cb_res, cb_mode) { \
+		.callback = cb, \
+		.callback_result.status = cb_res, \
+		.callback_result.mode = cb_mode, \
+	}
+
+#define CALLBACK_HHREPORT(cb, cb_res, cb_rep_size) { \
+		.callback = cb, \
+		.callback_result.status = cb_res, \
+		.callback_result.report_size = cb_rep_size, \
+	}
+
+#define CLLBACK_GATTC_SCAN_RES(props, prop_cnt, cb_adv_data) {\
+		.callback = CB_GATTC_SCAN_RESULT, \
+		.callback_result.properties = props, \
+		.callback_result.num_properties = prop_cnt, \
+		.callback_result.adv_data = cb_adv_data, \
+	}
+
+#define CALLBACK_GATTC_CONNECT(cb_res, cb_prop, cb_conn_id, cb_client_id) { \
+		.callback = CB_GATTC_OPEN, \
+		.callback_result.status = cb_res, \
+		.callback_result.properties = cb_prop, \
+		.callback_result.num_properties = 1, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.gatt_app_id = cb_client_id, \
+	}
+
+#define CALLBACK_GATTC_SEARCH_RESULT(cb_conn_id, cb_service) { \
+		.callback = CB_GATTC_SEARCH_RESULT, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.service = cb_service \
+	}
+
+#define CALLBACK_GATTC_SEARCH_COMPLETE(cb_res, cb_conn_id) { \
+		.callback = CB_GATTC_SEARCH_COMPLETE, \
+		.callback_result.conn_id = cb_conn_id \
+	}
+#define CALLBACK_GATTC_GET_CHARACTERISTIC_CB(cb_res, cb_conn_id, cb_service, \
+						cb_char, cb_char_prop) { \
+		.callback = CB_GATTC_GET_CHARACTERISTIC, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.service = cb_service, \
+		.callback_result.characteristic = cb_char, \
+		.callback_result.char_prop = cb_char_prop \
+	}
+
+#define CALLBACK_GATTC_GET_DESCRIPTOR(cb_res, cb_conn_id, cb_service, \
+						cb_char, cb_desc) { \
+		.callback = CB_GATTC_GET_DESCRIPTOR, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.service = cb_service, \
+		.callback_result.characteristic = cb_char, \
+		.callback_result.descriptor = cb_desc \
+	}
+
+#define CALLBACK_GATTC_GET_INCLUDED(cb_res, cb_conn_id, cb_service, \
+							cb_incl) { \
+		.callback = CB_GATTC_GET_INCLUDED_SERVICE, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.service = cb_service, \
+		.callback_result.included = cb_incl, \
+	}
+
+#define CALLBACK_GATTC_READ_CHARACTERISTIC(cb_res, cb_conn_id, cb_read_data) { \
+		.callback = CB_GATTC_READ_CHARACTERISTIC, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.read_params = cb_read_data, \
+	}
+
+#define CALLBACK_GATTC_READ_DESCRIPTOR(cb_res, cb_conn_id, cb_read_data) { \
+		.callback = CB_GATTC_READ_DESCRIPTOR, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.read_params = cb_read_data, \
+	}
+
+#define CALLBACK_GATTC_WRITE_DESCRIPTOR(cb_res, cb_conn_id, cb_write_data) { \
+		.callback = CB_GATTC_WRITE_DESCRIPTOR, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.write_params = cb_write_data, \
+	}
+
+#define CALLBACK_GATTC_WRITE_CHARACTERISTIC(cb_res, cb_conn_id, \
+							cb_write_data) { \
+		.callback = CB_GATTC_WRITE_CHARACTERISTIC, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.write_params = cb_write_data, \
+	}
+
+#define CALLBACK_GATTC_REGISTER_FOR_NOTIF(cb_res, cb_conn_id, cb_char,\
+						cb_service, cb_registered) { \
+		.callback = CB_GATTC_REGISTER_FOR_NOTIFICATION, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_res, \
+		.callback_result.service = cb_service, \
+		.callback_result.characteristic = cb_char, \
+		.callback_result.notification_registered = cb_registered \
+	}
+
+#define CALLBACK_GATTC_NOTIFY(cb_conn_id, cb_notify) { \
+		.callback = CB_GATTC_NOTIFY, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.notify_params = cb_notify \
+	}
+
+#define CALLBACK_GATTC_DISCONNECT(cb_res, cb_prop, cb_conn_id, cb_client_id) { \
+		.callback = CB_GATTC_CLOSE, \
+		.callback_result.status = cb_res, \
+		.callback_result.properties = cb_prop, \
+		.callback_result.num_properties = 1, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.gatt_app_id = cb_client_id, \
+	}
+
+#define CALLBACK_GATTS_CONNECTION(cb_res, cb_prop, cb_conn_id, cb_server_id) { \
+		.callback = CB_GATTS_CONNECTION, \
+		.callback_result.connected = cb_res, \
+		.callback_result.properties = cb_prop, \
+		.callback_result.num_properties = 1, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.gatt_app_id = cb_server_id, \
+	}
+
+#define CALLBACK_GATTS_NOTIF_CONF(cb_conn_id, cb_status) { \
+		.callback = CB_GATTS_INDICATION_SEND, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.status = cb_status, \
+	}
+
+#define CALLBACK_GATTS_SERVICE_ADDED(cb_res, cb_server_id, cb_service, \
+						cb_srvc_handle, \
+						cb_store_srvc_handle) { \
+		.callback = CB_GATTS_SERVICE_ADDED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.service = cb_service, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+		.store_srvc_handle = cb_store_srvc_handle, \
+	}
+
+#define CALLBACK_GATTS_INC_SERVICE_ADDED(cb_res, cb_server_id, cb_srvc_handle, \
+							cb_inc_srvc_handle) { \
+		.callback = CB_GATTS_INCLUDED_SERVICE_ADDED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+		.callback_result.inc_srvc_handle = cb_inc_srvc_handle, \
+	}
+
+#define CALLBACK_GATTS_CHARACTERISTIC_ADDED(cb_res, cb_server_id, cb_uuid, \
+						cb_srvc_handle, \
+						cb_char_handle, \
+						cb_store_char_handle) { \
+		.callback = CB_GATTS_CHARACTERISTIC_ADDED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.uuid = cb_uuid, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+		.callback_result.char_handle = cb_char_handle, \
+		.store_char_handle = cb_store_char_handle, \
+	}
+
+#define CALLBACK_GATTS_DESCRIPTOR_ADDED(cb_res, cb_server_id, cb_uuid, \
+					cb_srvc_handle, cb_desc_handle, \
+					cb_store_desc_handle) { \
+		.callback = CB_GATTS_DESCRIPTOR_ADDED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.uuid = cb_uuid, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+		.callback_result.desc_handle = cb_desc_handle, \
+		.store_desc_handle = cb_store_desc_handle, \
+	}
+
+#define CALLBACK_GATTS_SERVICE_STARTED(cb_res, cb_server_id, cb_srvc_handle) { \
+		.callback = CB_GATTS_SERVICE_STARTED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+	}
+
+#define CALLBACK_GATTS_SERVICE_STOPPED(cb_res, cb_server_id, cb_srvc_handle) { \
+		.callback = CB_GATTS_SERVICE_STOPPED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+	}
+
+#define CALLBACK_GATTS_SERVICE_DELETED(cb_res, cb_server_id, cb_srvc_handle) { \
+		.callback = CB_GATTS_SERVICE_DELETED, \
+		.callback_result.status = cb_res, \
+		.callback_result.gatt_app_id = cb_server_id, \
+		.callback_result.srvc_handle = cb_srvc_handle, \
+	}
+
+#define CALLBACK_GATTS_REQUEST_READ(cb_conn_id, cb_trans_id, cb_prop, \
+						cb_attr_handle, cb_offset, \
+						cb_is_long) { \
+		.callback = CB_GATTS_REQUEST_READ, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.trans_id = cb_trans_id, \
+		.callback_result.properties = cb_prop, \
+		.callback_result.num_properties = 1, \
+		.callback_result.attr_handle = cb_attr_handle, \
+		.callback_result.offset = cb_offset, \
+		.callback_result.is_long = cb_is_long, \
+	}
+
+#define CALLBACK_GATTS_REQUEST_WRITE(cb_conn_id, cb_trans_id, cb_prop, \
+						cb_attr_handle, cb_offset, \
+						cb_length, cb_need_rsp, \
+						cb_is_prep, cb_value) { \
+		.callback = CB_GATTS_REQUEST_WRITE, \
+		.callback_result.conn_id = cb_conn_id, \
+		.callback_result.trans_id = cb_trans_id, \
+		.callback_result.properties = cb_prop, \
+		.callback_result.num_properties = 1, \
+		.callback_result.attr_handle = cb_attr_handle, \
+		.callback_result.offset = cb_offset, \
+		.callback_result.length = cb_length, \
+		.callback_result.need_rsp = cb_need_rsp, \
+		.callback_result.is_prep = cb_is_prep, \
+		.callback_result.value = cb_value, \
+	}
+
+#define CALLBACK_MAP_CLIENT_REMOTE_MAS_INSTANCE(cb_status, cb_prop, \
+						cb_num_inst, cb_instances) { \
+		.callback = CB_MAP_CLIENT_REMOTE_MAS_INSTANCES, \
+		.callback_result.properties = cb_prop, \
+		.callback_result.num_properties = 1, \
+		.callback_result.status = cb_status, \
+		.callback_result.num_mas_instances = cb_num_inst, \
+		.callback_result.mas_instances = cb_instances, \
+	}
+
+#define CALLBACK_PAN_CTRL_STATE(cb, cb_res, cb_state, cb_local_role) { \
+		.callback = cb, \
+		.callback_result.status = cb_res, \
+		.callback_result.ctrl_state = cb_state, \
+		.callback_result.local_role = cb_local_role, \
+	}
+
+#define CALLBACK_PAN_CONN_STATE(cb, cb_res, cb_state, cb_local_role, \
+							cb_remote_role) { \
+		.callback = cb, \
+		.callback_result.status = cb_res, \
+		.callback_result.conn_state = cb_state, \
+		.callback_result.local_role = cb_local_role, \
+		.callback_result.remote_role = cb_remote_role, \
+	}
+
+#define CALLBACK_HDP_APP_REG_STATE(cb, cb_app_id, cb_state) { \
+		.callback = cb, \
+		.callback_result.app_id = cb_app_id, \
+		.callback_result.app_state = cb_state, \
+	}
+
+#define CALLBACK_HDP_CHANNEL_STATE(cb, cb_app_id, cb_channel_id, \
+					cb_mdep_cfg_index, cb_state) { \
+		.callback = cb, \
+		.callback_result.app_id = cb_app_id, \
+		.callback_result.channel_id = cb_channel_id, \
+		.callback_result.mdep_cfg_index = cb_mdep_cfg_index, \
+		.callback_result.channel_state = cb_state, \
+	}
+
+#define CALLBACK_AV_CONN_STATE(cb, cb_av_conn_state) { \
+		.callback = cb, \
+		.callback_result.av_conn_state = cb_av_conn_state, \
+	}
+
+#define CALLBACK_AV_AUDIO_STATE(cb, cb_av_audio_state) { \
+		.callback = cb, \
+		.callback_result.av_audio_state = cb_av_audio_state, \
+	}
+
+#define CALLBACK_RC_PLAY_STATUS(cb, cb_length, cb_position, cb_status) { \
+		.callback = cb, \
+		.callback_result.song_length = cb_length, \
+		.callback_result.song_position = cb_position, \
+		.callback_result.play_status = cb_status, \
+	}
+
+#define CALLBACK_RC_REG_NOTIF_TRACK_CHANGED(cb, cb_index) { \
+		.callback = cb, \
+		.callback_result.rc_index = cb_index, \
+	}
+
+#define CALLBACK_RC_REG_NOTIF_POSITION_CHANGED(cb, cb_position) { \
+		.callback = cb, \
+		.callback_result.song_position = cb_position, \
+	}
+
+#define CALLBACK_RC_REG_NOTIF_STATUS_CHANGED(cb, cb_status) { \
+		.callback = cb, \
+		.callback_result.play_status = cb_status, \
+	}
+
+#define CALLBACK_RC_GET_ELEMENT_ATTRIBUTES(cb, cb_num_of_attrs, cb_attrs) { \
+		.callback = cb, \
+		.callback_result.num_of_attrs = cb_num_of_attrs, \
+		.callback_result.attrs = cb_attrs, \
+	}
+
+#define CALLBACK_DEVICE_PROPS(props, prop_cnt) \
+	CALLBACK_PROPS(CB_BT_REMOTE_DEVICE_PROPERTIES, props, prop_cnt)
+
+#define CALLBACK_DEVICE_FOUND(props, prop_cnt) \
+	CALLBACK_PROPS(CB_BT_DEVICE_FOUND, props, prop_cnt)
+
+#define CALLBACK_BOND_STATE(cb_res, props, prop_cnt) { \
+		.callback = CB_BT_BOND_STATE_CHANGED, \
+		.callback_result.state = cb_res, \
+		.callback_result.properties = props, \
+		.callback_result.num_properties = prop_cnt, \
+	}
+
+#define CALLBACK_BOND_STATE_FAILED(cb_res, props, prop_cnt, reason) { \
+		.callback = CB_BT_BOND_STATE_CHANGED, \
+		.callback_result.state = cb_res, \
+		.callback_result.status = reason, \
+		.callback_result.properties = props, \
+		.callback_result.num_properties = prop_cnt, \
+	}
+
+#define CALLBACK_SSP_REQ(pair_var, props, prop_cnt) { \
+		.callback = CB_BT_SSP_REQUEST, \
+		.callback_result.pairing_variant = pair_var, \
+		.callback_result.properties = props, \
+		.callback_result.num_properties = prop_cnt, \
+	}
+
+#define DBG_CB(cb) { cb, #cb }
+
+/*
+ * NOTICE:
+ * Callback enum sections should be
+ * updated while adding new HAL to tester.
+ */
+typedef enum {
+	CB_BT_NONE,
+	CB_BT_ADAPTER_STATE_CHANGED,
+	CB_BT_ADAPTER_PROPERTIES,
+	CB_BT_REMOTE_DEVICE_PROPERTIES,
+	CB_BT_DEVICE_FOUND,
+	CB_BT_DISCOVERY_STATE_CHANGED,
+	CB_BT_PIN_REQUEST,
+	CB_BT_SSP_REQUEST,
+	CB_BT_BOND_STATE_CHANGED,
+	CB_BT_ACL_STATE_CHANGED,
+	CB_BT_THREAD_EVT,
+	CB_BT_DUT_MODE_RECV,
+	CB_BT_LE_TEST_MODE,
+
+	/* Hidhost cb */
+	CB_HH_CONNECTION_STATE,
+	CB_HH_HID_INFO,
+	CB_HH_PROTOCOL_MODE,
+	CB_HH_IDLE_TIME,
+	CB_HH_GET_REPORT,
+	CB_HH_VIRTUAL_UNPLUG,
+
+	/* PAN cb */
+	CB_PAN_CONTROL_STATE,
+	CB_PAN_CONNECTION_STATE,
+
+	/* HDP cb */
+	CB_HDP_APP_REG_STATE,
+	CB_HDP_CHANNEL_STATE,
+
+	/* A2DP cb */
+	CB_A2DP_CONN_STATE,
+	CB_A2DP_AUDIO_STATE,
+
+	/* AVRCP */
+	CB_AVRCP_PLAY_STATUS_REQ,
+	CB_AVRCP_PLAY_STATUS_RSP,
+	CB_AVRCP_REG_NOTIF_REQ,
+	CB_AVRCP_REG_NOTIF_RSP,
+	CB_AVRCP_GET_ATTR_REQ,
+	CB_AVRCP_GET_ATTR_RSP,
+
+	/* Gatt client */
+	CB_GATTC_REGISTER_CLIENT,
+	CB_GATTC_SCAN_RESULT,
+	CB_GATTC_OPEN,
+	CB_GATTC_CLOSE,
+	CB_GATTC_SEARCH_COMPLETE,
+	CB_GATTC_SEARCH_RESULT,
+	CB_GATTC_GET_CHARACTERISTIC,
+	CB_GATTC_GET_DESCRIPTOR,
+	CB_GATTC_GET_INCLUDED_SERVICE,
+	CB_GATTC_REGISTER_FOR_NOTIFICATION,
+	CB_GATTC_NOTIFY,
+	CB_GATTC_READ_CHARACTERISTIC,
+	CB_GATTC_WRITE_CHARACTERISTIC,
+	CB_GATTC_READ_DESCRIPTOR,
+	CB_GATTC_WRITE_DESCRIPTOR,
+	CB_GATTC_EXECUTE_WRITE,
+	CB_GATTC_READ_REMOTE_RSSI,
+	CB_GATTC_LISTEN,
+
+	/* Gatt server */
+	CB_GATTS_REGISTER_SERVER,
+	CB_GATTS_CONNECTION,
+	CB_GATTS_SERVICE_ADDED,
+	CB_GATTS_INCLUDED_SERVICE_ADDED,
+	CB_GATTS_CHARACTERISTIC_ADDED,
+	CB_GATTS_DESCRIPTOR_ADDED,
+	CB_GATTS_SERVICE_STARTED,
+	CB_GATTS_SERVICE_STOPPED,
+	CB_GATTS_SERVICE_DELETED,
+	CB_GATTS_REQUEST_READ,
+	CB_GATTS_REQUEST_WRITE,
+	CB_GATTS_REQUEST_EXEC_WRITE,
+	CB_GATTS_RESPONSE_CONFIRMATION,
+	CB_GATTS_INDICATION_SEND,
+
+	/* Map client */
+	CB_MAP_CLIENT_REMOTE_MAS_INSTANCES,
+
+	/* Emulator callbacks */
+	CB_EMU_CONFIRM_SEND_DATA,
+	CB_EMU_ENCRYPTION_ENABLED,
+	CB_EMU_ENCRYPTION_DISABLED,
+	CB_EMU_CONNECTION_REJECTED,
+	CB_EMU_VALUE_INDICATION,
+	CB_EMU_VALUE_NOTIFICATION,
+	CB_EMU_READ_RESPONSE,
+	CB_EMU_WRITE_RESPONSE,
+	CB_EMU_ATT_ERROR,
+} expected_bt_callback_t;
+
+struct test_data {
+	struct mgmt *mgmt;
+	audio_hw_device_t *audio;
+	struct hw_device_t *device;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+
+	const bt_interface_t *if_bluetooth;
+	const btsock_interface_t *if_sock;
+	const bthh_interface_t *if_hid;
+	const btpan_interface_t *if_pan;
+	const bthl_interface_t *if_hdp;
+	const btav_interface_t *if_a2dp;
+	struct audio_stream_out *if_stream;
+	const btrc_interface_t *if_avrcp;
+	const btgatt_interface_t *if_gatt;
+	const btmce_interface_t *if_map_client;
+
+	const void *test_data;
+	struct queue *steps;
+
+	guint signalfd;
+	uint16_t mgmt_index;
+	pid_t bluetoothd_pid;
+
+	struct queue *pdus;
+};
+
+/*
+ * Struct holding bluetooth HAL action parameters
+ */
+struct bt_action_data {
+	bt_bdaddr_t *addr;
+
+	/* Remote props action arguments */
+	const int prop_type;
+	const bt_property_t *prop;
+
+	/* Bonding requests parameters */
+	bt_pin_code_t *pin;
+	const uint8_t pin_len;
+	const uint8_t ssp_variant;
+	const bool accept;
+	const uint16_t io_cap;
+
+	/* Socket HAL specific params */
+	const btsock_type_t sock_type;
+	const int channel;
+	const uint8_t *service_uuid;
+	const char *service_name;
+	const int flags;
+	int *fd;
+
+	/* HidHost params */
+	const int report_size;
+
+	/*Connection params*/
+	const uint8_t bearer_type;
+	const uint8_t transport_type;
+};
+
+/* bthost's l2cap server setup parameters */
+struct emu_set_l2cap_data {
+	const uint16_t psm;
+	const bthost_l2cap_connect_cb func;
+	void *user_data;
+};
+
+struct emu_l2cap_cid_data {
+	const struct pdu_set *pdu;
+
+	uint16_t handle;
+	uint16_t cid;
+	bool is_sdp;
+};
+
+struct map_inst_data {
+	int32_t id;
+	int32_t scn;
+	int32_t msg_types;
+	int32_t name_len;
+	uint8_t *name;
+};
+
+/*
+ * Callback data structure should be enhanced with data
+ * returned by callbacks. It's used for test case step
+ * matching with expected step data.
+ */
+struct bt_callback_data {
+	bt_state_t state;
+	bt_status_t status;
+	int num_properties;
+	bt_property_t *properties;
+	bt_uuid_t *uuid;
+
+	bt_ssp_variant_t pairing_variant;
+
+	bthh_protocol_mode_t mode;
+	int report_size;
+
+	bool adv_data;
+
+	int gatt_app_id;
+	int conn_id;
+	int trans_id;
+	int offset;
+	bool is_long;
+	int connected;
+	uint16_t *attr_handle;
+	uint16_t *srvc_handle;
+	uint16_t *inc_srvc_handle;
+	uint16_t *char_handle;
+	uint16_t *desc_handle;
+	btgatt_srvc_id_t *service;
+	btgatt_gatt_id_t *characteristic;
+	btgatt_gatt_id_t *descriptor;
+	btgatt_srvc_id_t *included;
+	btgatt_read_params_t *read_params;
+	btgatt_write_params_t *write_params;
+	btgatt_notify_params_t *notify_params;
+	int notification_registered;
+	int char_prop;
+	int length;
+	uint8_t *value;
+	bool need_rsp;
+	bool is_prep;
+	uint8_t error;
+
+	btpan_control_state_t ctrl_state;
+	btpan_connection_state_t conn_state;
+	int local_role;
+	int remote_role;
+
+	int app_id;
+	int channel_id;
+	int mdep_cfg_index;
+	bthl_app_reg_state_t app_state;
+	bthl_channel_state_t channel_state;
+
+	btav_connection_state_t av_conn_state;
+	btav_audio_state_t av_audio_state;
+	uint32_t song_length;
+	uint32_t song_position;
+	btrc_play_status_t play_status;
+	uint64_t rc_index;
+	uint8_t num_of_attrs;
+	btrc_element_attr_val_t *attrs;
+
+	int num_mas_instances;
+	btmce_mas_instance_t *mas_instances;
+};
+
+/*
+ * Step structure contains expected step data and step
+ * action, which should be performed before step check.
+ */
+struct step {
+	void (*action)(void);
+	int action_status;
+
+	expected_bt_callback_t callback;
+	struct bt_callback_data callback_result;
+
+	void *set_data;
+	void *set_data_2;
+	void *set_data_3;
+	int set_data_len;
+
+	uint16_t *store_srvc_handle;
+	uint16_t *store_char_handle;
+	uint16_t *store_desc_handle;
+};
+
+struct test_case {
+	const uint8_t emu_type;
+	const char *title;
+	const uint16_t step_num;
+	const struct step *step;
+};
+
+void tester_handle_l2cap_data_exchange(struct emu_l2cap_cid_data *cid_data);
+void tester_generic_connect_cb(uint16_t handle, uint16_t cid, void *user_data);
+
+/* Get, remove test cases API */
+struct queue *get_bluetooth_tests(void);
+void remove_bluetooth_tests(void);
+struct queue *get_socket_tests(void);
+void remove_socket_tests(void);
+struct queue *get_hidhost_tests(void);
+void remove_hidhost_tests(void);
+struct queue *get_pan_tests(void);
+void remove_pan_tests(void);
+struct queue *get_hdp_tests(void);
+void remove_hdp_tests(void);
+struct queue *get_a2dp_tests(void);
+void remove_a2dp_tests(void);
+struct queue *get_avrcp_tests(void);
+void remove_avrcp_tests(void);
+struct queue *get_gatt_tests(void);
+void remove_gatt_tests(void);
+struct queue *get_map_client_tests(void);
+void remove_map_client_tests(void);
+
+/* Generic tester API */
+void schedule_action_verification(struct step *step);
+void schedule_callback_verification(struct step *step);
+
+/* Emulator actions */
+void emu_setup_powered_remote_action(void);
+void emu_set_pin_code_action(void);
+void emu_set_ssp_mode_action(void);
+void emu_set_connect_cb_action(void);
+void emu_remote_connect_hci_action(void);
+void emu_remote_disconnect_hci_action(void);
+void emu_set_io_cap(void);
+void emu_add_l2cap_server_action(void);
+void emu_add_rfcomm_server_action(void);
+
+/* Actions */
+void dummy_action(void);
+void bluetooth_enable_action(void);
+void bluetooth_disable_action(void);
+void bt_set_property_action(void);
+void bt_get_property_action(void);
+void bt_start_discovery_action(void);
+void bt_cancel_discovery_action(void);
+void bt_get_device_props_action(void);
+void bt_get_device_prop_action(void);
+void bt_set_device_prop_action(void);
+void bt_create_bond_action(void);
+void bt_pin_reply_accept_action(void);
+void bt_ssp_reply_accept_action(void);
+void bt_cancel_bond_action(void);
+void bt_remove_bond_action(void);
+void set_default_ssp_request_handler(void);
diff --git a/bluez/repo/android/tester-map-client.c b/bluez/repo/android/tester-map-client.c
new file mode 100644
index 0000000..695c797
--- /dev/null
+++ b/bluez/repo/android/tester-map-client.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "tester-main.h"
+
+static struct queue *list = NULL; /* List of map client test cases */
+
+#define INST0_ID 0
+#define INST1_ID 1
+
+#define sdp_rsp_pdu	0x07, \
+			0x00, 0x00, \
+			0x00, 0xb5, \
+			0x00, 0xb2, \
+			0x35, 0xb0, 0x36, 0x00, 0x56, 0x09, 0x00, 0x00, 0x0a, \
+			0x00, 0x01, 0x00, 0x09, 0x09, 0x00, 0x01, 0x35, 0x03, \
+			0x19, 0x11, 0x32, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35, \
+			0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00, 0x03, \
+			0x08, 0x04, 0x35, 0x03, 0x19, 0x00, 0x08, 0x09, 0x00, \
+			0x05, 0x35, 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x09, \
+			0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x34, 0x09, 0x01, \
+			0x01, 0x09, 0x01, 0x00, 0x25, 0x0c, 0x4d, 0x41, 0x50, \
+			0x20, 0x53, 0x4d, 0x53, 0x2f, 0x4d, 0x4d, 0x53, 0x00, \
+			0x09, 0x03, 0x15, 0x08, 0x00, 0x09, 0x03, 0x16, 0x08, \
+			0x0e, 0x36, 0x00, 0x54, 0x09, 0x00, 0x00, 0x0a, 0x00, \
+			0x01, 0x00, 0x0a, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, \
+			0x11, 0x32, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35, 0x03, \
+			0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00, 0x03, 0x08, \
+			0x05, 0x35, 0x03, 0x19, 0x00, 0x08, 0x09, 0x00, 0x05, \
+			0x35, 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x09, 0x35, \
+			0x08, 0x35, 0x06, 0x19, 0x11, 0x34, 0x09, 0x01, 0x01, \
+			0x09, 0x01, 0x00, 0x25, 0x0a, 0x4d, 0x41, 0x50, 0x20, \
+			0x45, 0x4d, 0x41, 0x49, 0x4c, 0x00, 0x09, 0x03, 0x15, \
+			0x08, 0x01, 0x09, 0x03, 0x16, 0x08, 0x01, \
+			0x00
+
+static const struct pdu_set pdus[] = {
+	{ end_pdu, raw_pdu(sdp_rsp_pdu) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data cid_data = {
+	.pdu = pdus,
+};
+
+static bt_bdaddr_t emu_remote_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 },
+};
+
+static struct emu_set_l2cap_data l2cap_sdp_setup_data = {
+	.psm = 1,
+	.func = tester_generic_connect_cb,
+	.user_data = &cid_data,
+};
+
+/* TODO define all parameters according to specification document */
+static btmce_mas_instance_t remote_map_inst_sms_mms_email_val[] = {
+	{ INST0_ID, 4, 14, "MAP SMS/MMS" },
+	{ INST1_ID, 5, 1, "MAP EMAIL" },
+};
+
+static void map_client_cid_hook_cb(const void *data, uint16_t len,
+								void *user_data)
+{
+	/* TODO extend if needed */
+}
+
+static void map_client_conn_cb(uint16_t handle, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	tester_print("New connection with handle 0x%04x", handle);
+
+	if (data->hciemu_type == HCIEMU_TYPE_BREDR) {
+		tester_warn("Not handled device type.");
+		return;
+	}
+
+	cid_data.cid = 0x0040;
+	cid_data.handle = handle;
+
+	bthost_add_cid_hook(bthost, handle, cid_data.cid,
+					map_client_cid_hook_cb, &cid_data);
+}
+
+static void map_client_get_instances_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	bt_bdaddr_t *bd_addr = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status =
+		data->if_map_client->get_remote_mas_instances(bd_addr);
+
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("MAP Client Init", ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("MAP Client - Get mas instances 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_add_l2cap_server_action,
+							&l2cap_sdp_setup_data),
+		ACTION_SUCCESS(emu_set_connect_cb_action, map_client_conn_cb),
+		ACTION_SUCCESS(map_client_get_instances_action,
+							&emu_remote_bdaddr_val),
+		CALLBACK_MAP_CLIENT_REMOTE_MAS_INSTANCE(BT_STATUS_SUCCESS, NULL,
+					2, remote_map_inst_sms_mms_email_val),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+};
+
+struct queue *get_map_client_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_map_client_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-pan.c b/bluez/repo/android/tester-pan.c
new file mode 100644
index 0000000..9da2488
--- /dev/null
+++ b/bluez/repo/android/tester-pan.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "lib/bluetooth.h"
+#include "android/utils.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "tester-main.h"
+
+static struct queue *list; /* List of pan test cases */
+
+#define pan_conn_req_pdu 0x01, 0x01, 0x02, 0x11, 0x16, 0x11, 0x15
+#define pan_conn_rsp_pdu 0x01, 0x02, 0x00, 0x00
+
+static const struct pdu_set pdus[] = {
+	{ raw_pdu(pan_conn_req_pdu), raw_pdu(pan_conn_rsp_pdu) },
+	{ end_pdu, end_pdu },
+};
+
+static struct emu_l2cap_cid_data cid_data = {
+	.pdu = pdus,
+};
+
+static struct emu_set_l2cap_data l2cap_setup_data = {
+	.psm = 15,
+	.func = tester_generic_connect_cb,
+	.user_data = &cid_data,
+};
+
+static void pan_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr);
+
+	step->action_status = data->if_pan->connect(&bdaddr,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP);
+
+	schedule_action_verification(step);
+}
+
+static void pan_disconnect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+
+	bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr);
+
+	step->action_status = data->if_pan->disconnect(&bdaddr);
+
+	schedule_action_verification(step);
+}
+
+static void pan_get_local_role_action(void)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu);
+	struct step *step = g_new0(struct step, 1);
+	bt_bdaddr_t bdaddr;
+	int role;
+
+	bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr);
+
+	role = data->if_pan->get_local_role();
+	if (role == BTPAN_ROLE_PANU)
+		step->action_status = BT_STATUS_SUCCESS;
+	else
+		step->action_status = BT_STATUS_FAIL;
+
+	schedule_action_verification(step);
+}
+
+static void pan_enable_nap_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_pan->enable(BTPAN_ROLE_PANNAP);
+
+	schedule_action_verification(step);
+}
+
+static void pan_enable_panu_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_pan->enable(BTPAN_ROLE_PANU);
+
+	schedule_action_verification(step);
+}
+
+static void pan_enable_none_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *step = g_new0(struct step, 1);
+
+	step->action_status = data->if_pan->enable(BTPAN_ROLE_NONE);
+
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("PAN Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("PAN Connect - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(pan_connect_action, NULL),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_CONNECTING,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS,
+					BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_CONNECTED,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_DISCONNECTED,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("PAN Disconnect - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(pan_connect_action, NULL),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_CONNECTING,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS,
+					BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_CONNECTED,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		ACTION_SUCCESS(pan_disconnect_action, NULL),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_DISCONNECTED,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("PAN GetLocalRole - Success",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(pan_connect_action, NULL),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_CONNECTING,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS,
+					BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_CONNECTED,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		ACTION_SUCCESS(pan_get_local_role_action, NULL),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE,
+					BT_STATUS_SUCCESS,
+					BTPAN_STATE_DISCONNECTED,
+					BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("PAN Enable NAP - Success",
+		ACTION_SUCCESS(pan_enable_nap_action, NULL),
+		CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS,
+					BTPAN_STATE_ENABLED, BTPAN_ROLE_PANNAP),
+	),
+	TEST_CASE_BREDRLE("PAN Enable PANU - Success",
+		ACTION(BT_STATUS_UNSUPPORTED, pan_enable_panu_action, NULL),
+	),
+	TEST_CASE_BREDRLE("PAN Enable NONE - Success",
+		ACTION_SUCCESS(pan_enable_nap_action, NULL),
+		CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS,
+					BTPAN_STATE_ENABLED, BTPAN_ROLE_PANNAP),
+		ACTION_SUCCESS(pan_enable_none_action, NULL),
+		CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS,
+					BTPAN_STATE_DISABLED, BTPAN_ROLE_NONE),
+	),
+};
+
+struct queue *get_pan_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_pan_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/tester-socket.c b/bluez/repo/android/tester-socket.c
new file mode 100644
index 0000000..2264a1f
--- /dev/null
+++ b/bluez/repo/android/tester-socket.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include "emulator/bthost.h"
+#include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "tester-main.h"
+
+static struct queue *list; /* List of socket test cases */
+
+static bt_bdaddr_t bdaddr_dummy = {
+	.address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
+};
+
+static int got_fd_result = -1;
+
+static struct bt_action_data btsock_param_socktype_0 = {
+	.addr = &bdaddr_dummy,
+	.sock_type = 0,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.fd = &got_fd_result,
+};
+
+static struct bt_action_data btsock_param_socktype_l2cap = {
+	.addr = &bdaddr_dummy,
+	.sock_type = BTSOCK_L2CAP,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.fd = &got_fd_result,
+};
+
+static struct bt_action_data btsock_param_channel_0 = {
+	.addr = &bdaddr_dummy,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 0,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.fd = &got_fd_result,
+};
+
+static struct bt_action_data btsock_param = {
+	.addr = &bdaddr_dummy,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.fd = &got_fd_result,
+};
+
+static struct bt_action_data btsock_param_inv_bdaddr = {
+	.addr = NULL,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.fd = &got_fd_result,
+};
+
+static bt_bdaddr_t emu_remote_bdaddr_val = {
+	.address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 },
+};
+static bt_property_t prop_emu_remote_bdadr = {
+	.type = BT_PROPERTY_BDADDR,
+	.val = &emu_remote_bdaddr_val,
+	.len = sizeof(emu_remote_bdaddr_val),
+};
+static bt_property_t prop_emu_remotes_default_set[] = {
+	{ BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+						&emu_remote_bdaddr_val },
+};
+
+static struct bt_action_data btsock_param_emu_bdaddr = {
+	.addr = &emu_remote_bdaddr_val,
+	.sock_type = BTSOCK_RFCOMM,
+	.channel = 1,
+	.service_uuid = NULL,
+	.service_name = "Test service",
+	.flags = 0,
+	.fd = &got_fd_result,
+};
+
+static struct emu_set_l2cap_data l2cap_setup_data = {
+	.psm = 0x0003,
+	.func = NULL,
+	.user_data = NULL,
+};
+
+static struct bt_action_data prop_emu_remote_bdaddr_req = {
+	.addr = &emu_remote_bdaddr_val,
+	.prop_type = BT_PROPERTY_BDADDR,
+	.prop = &prop_emu_remote_bdadr,
+};
+
+static void socket_listen_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	*action_data->fd = -1;
+
+	step->action_status = data->if_sock->listen(action_data->sock_type,
+						action_data->service_name,
+						action_data->service_uuid,
+						action_data->channel,
+						action_data->fd,
+						action_data->flags);
+
+	schedule_action_verification(step);
+}
+
+static void socket_connect_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step;
+	int status;
+
+	*action_data->fd = -1;
+
+	status = data->if_sock->connect(action_data->addr,
+						action_data->sock_type,
+						action_data->service_uuid,
+						action_data->channel,
+						action_data->fd,
+						action_data->flags);
+
+	tester_print("status %d sock_fd %d", status, *action_data->fd);
+
+	if (!status)
+		return;
+
+	step = g_new0(struct step, 1);
+	step->action_status = status;
+
+	schedule_action_verification(step);
+}
+
+static gboolean socket_chan_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	int sock_fd = g_io_channel_unix_get_fd(io);
+	struct step *step = g_new0(struct step, 1);
+	int channel, len;
+
+	tester_print("%s", __func__);
+
+	if (cond & G_IO_HUP) {
+		tester_warn("Socket %d hang up", sock_fd);
+
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	if (cond & (G_IO_ERR | G_IO_NVAL)) {
+		tester_warn("Socket error: sock %d cond %d", sock_fd, cond);
+
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	len = read(sock_fd, &channel, sizeof(channel));
+	if (len != sizeof(channel)) {
+		tester_warn("Socket read failed");
+
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	tester_print("read correct channel: %d", channel);
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+done:
+	schedule_action_verification(step);
+	return FALSE;
+}
+
+static void socket_read_fd_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	GIOChannel *io;
+
+	io = g_io_channel_unix_new(*action_data->fd);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							socket_chan_cb, NULL);
+
+	g_io_channel_unref(io);
+}
+
+static void socket_verify_fd_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	if (!*action_data->fd) {
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	step->action_status = (fcntl(*action_data->fd, F_GETFD) < 0) ?
+					BT_STATUS_FAIL : BT_STATUS_SUCCESS;
+
+done:
+	schedule_action_verification(step);
+}
+
+static void socket_verify_channel_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	int channel, len;
+	struct step *step = g_new0(struct step, 1);
+
+	if (!*action_data->fd) {
+		tester_warn("Ups no action_data->fd");
+
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	len = read(*action_data->fd, &channel, sizeof(channel));
+	if (len != sizeof(channel) || channel != action_data->channel) {
+		tester_warn("Ups bad channel");
+
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+done:
+	schedule_action_verification(step);
+}
+
+static void socket_close_channel_action(void)
+{
+	struct test_data *data = tester_get_data();
+	struct step *current_data_step = queue_peek_head(data->steps);
+	struct bt_action_data *action_data = current_data_step->set_data;
+	struct step *step = g_new0(struct step, 1);
+
+	if (!*action_data->fd) {
+		tester_warn("Ups no action_data->fd");
+
+		step->action_status = BT_STATUS_FAIL;
+		goto done;
+	}
+
+	close(*action_data->fd);
+	*action_data->fd = -1;
+
+	step->action_status = BT_STATUS_SUCCESS;
+
+done:
+	schedule_action_verification(step);
+}
+
+static struct test_case test_cases[] = {
+	TEST_CASE_BREDRLE("Socket Init",
+		ACTION_SUCCESS(dummy_action, NULL),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Invalid: sock_type 0",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_PARM_INVALID, socket_listen_action,
+						&btsock_param_socktype_0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Invalid: sock_type L2CAP",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_UNSUPPORTED, socket_listen_action,
+						&btsock_param_socktype_l2cap),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Invalid: chan, uuid",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_PARM_INVALID, socket_listen_action,
+						&btsock_param_channel_0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Check returned fd valid",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(socket_listen_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_fd_action, &btsock_param),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Check returned channel",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(socket_listen_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_fd_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_channel_action, &btsock_param),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Close and Listen again",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(socket_listen_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_fd_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_channel_action, &btsock_param),
+		ACTION_SUCCESS(socket_close_channel_action, &btsock_param),
+		ACTION_SUCCESS(socket_listen_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_fd_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_channel_action, &btsock_param),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Listen - Invalid: double Listen",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION_SUCCESS(socket_listen_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_fd_action, &btsock_param),
+		ACTION_SUCCESS(socket_verify_channel_action, &btsock_param),
+		ACTION(BT_STATUS_BUSY, socket_listen_action, &btsock_param),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Connect - Invalid: sock_type 0",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_PARM_INVALID, socket_connect_action,
+						&btsock_param_socktype_0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Connect - Invalid: sock_type L2CAP",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_UNSUPPORTED, socket_connect_action,
+						&btsock_param_socktype_l2cap),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Connect - Invalid: chan, uuid",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_PARM_INVALID, socket_connect_action,
+						&btsock_param_channel_0),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Connect - Invalid: bdaddr",
+		ACTION_SUCCESS(bluetooth_enable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+		ACTION(BT_STATUS_PARM_INVALID, socket_connect_action,
+						&btsock_param_inv_bdaddr),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Connect - Check returned fd valid",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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(bt_create_bond_action,
+						&prop_emu_remote_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 1),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_DEVICE_PROPS(NULL, 0),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(emu_add_rfcomm_server_action,
+						&btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(socket_connect_action, &btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(socket_verify_fd_action,
+						&btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+	TEST_CASE_BREDRLE("Socket Connect - Check returned chann",
+		ACTION_SUCCESS(set_default_ssp_request_handler, NULL),
+		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(bt_create_bond_action,
+						&prop_emu_remote_bdaddr_req),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 1),
+		CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+						&prop_emu_remote_bdadr, 1),
+		CALLBACK_DEVICE_PROPS(NULL, 0),
+		ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data),
+		ACTION_SUCCESS(emu_add_rfcomm_server_action,
+						&btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(socket_connect_action, &btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(socket_verify_fd_action,
+						&btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(socket_verify_channel_action,
+						&btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(socket_read_fd_action, &btsock_param_emu_bdaddr),
+		ACTION_SUCCESS(bluetooth_disable_action, NULL),
+		CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+	),
+};
+
+struct queue *get_socket_tests(void)
+{
+	uint16_t i = 0;
+
+	list = queue_new();
+
+	for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i)
+		queue_push_tail(list, &test_cases[i]);
+
+	return list;
+}
+
+void remove_socket_tests(void)
+{
+	queue_destroy(list, NULL);
+}
diff --git a/bluez/repo/android/utils.h b/bluez/repo/android/utils.h
new file mode 100644
index 0000000..7adc2da
--- /dev/null
+++ b/bluez/repo/android/utils.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  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
+ *
+ */
+
+static inline void android2bdaddr(const void *buf, bdaddr_t *dst)
+{
+	baswap(dst, buf);
+}
+
+static inline void bdaddr2android(const bdaddr_t *src, void *buf)
+{
+	baswap(buf, src);
+}
+
+const char *bt_config_get_vendor(void);
+const char *bt_config_get_model(void);
+const char *bt_config_get_name(void);
+const char *bt_config_get_serial(void);
+const char *bt_config_get_fw_rev(void);
+const char *bt_config_get_hw_rev(void);
+uint64_t bt_config_get_system_id(void);
+uint16_t bt_config_get_pnp_source(void);
+uint16_t bt_config_get_pnp_vendor(void);
+uint16_t bt_config_get_pnp_product(void);
+uint16_t bt_config_get_pnp_version(void);
diff --git a/bluez/repo/attrib/._att-database.h b/bluez/repo/attrib/._att-database.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._att-database.h
Binary files differ
diff --git a/bluez/repo/attrib/._att.c b/bluez/repo/attrib/._att.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._att.c
Binary files differ
diff --git a/bluez/repo/attrib/._att.h b/bluez/repo/attrib/._att.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._att.h
Binary files differ
diff --git a/bluez/repo/attrib/._gatt-service.c b/bluez/repo/attrib/._gatt-service.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gatt-service.c
Binary files differ
diff --git a/bluez/repo/attrib/._gatt-service.h b/bluez/repo/attrib/._gatt-service.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gatt-service.h
Binary files differ
diff --git a/bluez/repo/attrib/._gatt.c b/bluez/repo/attrib/._gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gatt.c
Binary files differ
diff --git a/bluez/repo/attrib/._gatt.h b/bluez/repo/attrib/._gatt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gatt.h
Binary files differ
diff --git a/bluez/repo/attrib/._gattrib.c b/bluez/repo/attrib/._gattrib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gattrib.c
Binary files differ
diff --git a/bluez/repo/attrib/._gattrib.h b/bluez/repo/attrib/._gattrib.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gattrib.h
Binary files differ
diff --git a/bluez/repo/attrib/._gatttool.c b/bluez/repo/attrib/._gatttool.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gatttool.c
Binary files differ
diff --git a/bluez/repo/attrib/._gatttool.h b/bluez/repo/attrib/._gatttool.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._gatttool.h
Binary files differ
diff --git a/bluez/repo/attrib/._interactive.c b/bluez/repo/attrib/._interactive.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._interactive.c
Binary files differ
diff --git a/bluez/repo/attrib/._utils.c b/bluez/repo/attrib/._utils.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/attrib/._utils.c
Binary files differ
diff --git a/bluez/repo/attrib/att-database.h b/bluez/repo/attrib/att-database.h
new file mode 100644
index 0000000..48c50e3
--- /dev/null
+++ b/bluez/repo/attrib/att-database.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  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
+ *
+ */
+
+/* Requirements for read/write operations */
+enum {
+	ATT_NONE,		/* No restrictions */
+	ATT_AUTHENTICATION,	/* Authentication required */
+	ATT_AUTHORIZATION,	/* Authorization required */
+	ATT_NOT_PERMITTED,	/* Operation not permitted */
+};
+
+struct attribute {
+	uint16_t handle;
+	bt_uuid_t uuid;
+	int read_req;		/* Read requirement */
+	int write_req;		/* Write requirement */
+	uint8_t (*read_cb)(struct attribute *a, struct btd_device *device,
+							gpointer user_data);
+	uint8_t (*write_cb)(struct attribute *a, struct btd_device *device,
+							gpointer user_data);
+	gpointer cb_user_data;
+	size_t len;
+	uint8_t *data;
+};
diff --git a/bluez/repo/attrib/att.c b/bluez/repo/attrib/att.c
new file mode 100644
index 0000000..826b3c1
--- /dev/null
+++ b/bluez/repo/attrib/att.c
@@ -0,0 +1,1251 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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 <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "att.h"
+
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+	if (src->type == BT_UUID16)
+		put_le16(src->value.u16, dst);
+	else
+		/* Convert from 128-bit BE to LE */
+		bswap_128(&src->value.u128, dst);
+}
+
+const char *att_ecode2str(uint8_t status)
+{
+	switch (status)  {
+	case ATT_ECODE_INVALID_HANDLE:
+		return "Invalid handle";
+	case ATT_ECODE_READ_NOT_PERM:
+		return "Attribute can't be read";
+	case ATT_ECODE_WRITE_NOT_PERM:
+		return "Attribute can't be written";
+	case ATT_ECODE_INVALID_PDU:
+		return "Attribute PDU was invalid";
+	case ATT_ECODE_AUTHENTICATION:
+		return "Attribute requires authentication before read/write";
+	case ATT_ECODE_REQ_NOT_SUPP:
+		return "Server doesn't support the request received";
+	case ATT_ECODE_INVALID_OFFSET:
+		return "Offset past the end of the attribute";
+	case ATT_ECODE_AUTHORIZATION:
+		return "Attribute requires authorization before read/write";
+	case ATT_ECODE_PREP_QUEUE_FULL:
+		return "Too many prepare writes have been queued";
+	case ATT_ECODE_ATTR_NOT_FOUND:
+		return "No attribute found within the given range";
+	case ATT_ECODE_ATTR_NOT_LONG:
+		return "Attribute can't be read/written using Read Blob Req";
+	case ATT_ECODE_INSUFF_ENCR_KEY_SIZE:
+		return "Encryption Key Size is insufficient";
+	case ATT_ECODE_INVAL_ATTR_VALUE_LEN:
+		return "Attribute value length is invalid";
+	case ATT_ECODE_UNLIKELY:
+		return "Request attribute has encountered an unlikely error";
+	case ATT_ECODE_INSUFF_ENC:
+		return "Encryption required before read/write";
+	case ATT_ECODE_UNSUPP_GRP_TYPE:
+		return "Attribute type is not a supported grouping attribute";
+	case ATT_ECODE_INSUFF_RESOURCES:
+		return "Insufficient Resources to complete the request";
+	case ATT_ECODE_IO:
+		return "Internal application error: I/O";
+	case ATT_ECODE_TIMEOUT:
+		return "A timeout occured";
+	case ATT_ECODE_ABORTED:
+		return "The operation was aborted";
+	default:
+		return "Unexpected error code";
+	}
+}
+
+void att_data_list_free(struct att_data_list *list)
+{
+	if (list == NULL)
+		return;
+
+	if (list->data) {
+		int i;
+		for (i = 0; i < list->num; i++)
+			g_free(list->data[i]);
+	}
+
+	g_free(list->data);
+	g_free(list);
+}
+
+struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
+{
+	struct att_data_list *list;
+	int i;
+
+	if (len > UINT8_MAX)
+		return NULL;
+
+	list = g_new0(struct att_data_list, 1);
+	list->len = len;
+	list->num = num;
+
+	list->data = g_malloc0(sizeof(uint8_t *) * num);
+
+	for (i = 0; i < num; i++)
+		list->data[i] = g_malloc0(sizeof(uint8_t) * len);
+
+	return list;
+}
+
+static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid)
+{
+	if (type == BT_UUID16)
+		bt_uuid16_create(uuid, get_le16(val));
+	else {
+		uint128_t u128;
+
+		/* Convert from 128-bit LE to BE */
+		bswap_128(val, &u128);
+		bt_uuid128_create(uuid, u128);
+	}
+}
+
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+						uint8_t *pdu, size_t len)
+{
+	uint16_t uuid_len;
+
+	if (!uuid)
+		return 0;
+
+	if (uuid->type == BT_UUID16)
+		uuid_len = 2;
+	else if (uuid->type == BT_UUID128)
+		uuid_len = 16;
+	else
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_READ_BY_GROUP_REQ;
+	/* Starting Handle (2 octets) */
+	put_le16(start, &pdu[1]);
+	/* Ending Handle (2 octets) */
+	put_le16(end, &pdu[3]);
+	/* Attribute Group Type (2 or 16 octet UUID) */
+	put_uuid_le(uuid, &pdu[5]);
+
+	return 5 + uuid_len;
+}
+
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
+						uint16_t *end, bt_uuid_t *uuid)
+{
+	const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+	uint8_t type;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (start == NULL || end == NULL || uuid == NULL)
+		return 0;
+
+	if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
+		return 0;
+
+	if (len == (min_len + 2))
+		type = BT_UUID16;
+	else if (len == (min_len + 16))
+		type = BT_UUID128;
+	else
+		return 0;
+
+	*start = get_le16(&pdu[1]);
+	*end = get_le16(&pdu[3]);
+
+	get_uuid(type, &pdu[5], uuid);
+
+	return len;
+}
+
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu,
+								size_t len)
+{
+	int i;
+	uint16_t w;
+	uint8_t *ptr;
+
+	if (list == NULL)
+		return 0;
+
+	if (len < list->len + sizeof(uint8_t) * 2)
+		return 0;
+
+	pdu[0] = ATT_OP_READ_BY_GROUP_RESP;
+	pdu[1] = list->len;
+
+	ptr = &pdu[2];
+
+	for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+		memcpy(ptr, list->data[i], list->len);
+		ptr += list->len;
+		w += list->len;
+	}
+
+	return w;
+}
+
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len)
+{
+	struct att_data_list *list;
+	const uint8_t *ptr;
+	uint16_t elen, num;
+	int i;
+
+	if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
+		return NULL;
+
+	/* PDU must contain at least:
+	 * - Attribute Opcode (1 octet)
+	 * - Length (1 octet)
+	 * - Attribute Data List (at least one entry):
+	 *   - Attribute Handle (2 octets)
+	 *   - End Group Handle (2 octets)
+	 *   - Attribute Value (at least 1 octet) */
+	if (len < 7)
+		return NULL;
+
+	elen = pdu[1];
+	/* Minimum Attribute Data List size */
+	if (elen < 5)
+		return NULL;
+
+	/* Reject incomplete Attribute Data List */
+	if ((len - 2) % elen)
+		return NULL;
+
+	num = (len - 2) / elen;
+	list = att_data_list_alloc(num, elen);
+	if (list == NULL)
+		return NULL;
+
+	ptr = &pdu[2];
+
+	for (i = 0; i < num; i++) {
+		memcpy(list->data[i], ptr, list->len);
+		ptr += list->len;
+	}
+
+	return list;
+}
+
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+					const uint8_t *value, size_t vlen,
+					uint8_t *pdu, size_t len)
+{
+	uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end) +
+							sizeof(uint16_t);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (!uuid)
+		return 0;
+
+	if (uuid->type != BT_UUID16)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_FIND_BY_TYPE_REQ;
+	put_le16(start, &pdu[1]);
+	put_le16(end, &pdu[3]);
+	put_le16(uuid->value.u16, &pdu[5]);
+
+	if (vlen > 0) {
+		memcpy(&pdu[7], value, vlen);
+		return min_len + vlen;
+	}
+
+	return min_len;
+}
+
+uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
+						uint16_t *end, bt_uuid_t *uuid,
+						uint8_t *value, size_t *vlen)
+{
+	if (pdu == NULL)
+		return 0;
+
+	if (len < 7)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ)
+		return 0;
+
+	/* First requested handle number (2 octets) */
+	*start = get_le16(&pdu[1]);
+	/* Last requested handle number (2 octets) */
+	*end = get_le16(&pdu[3]);
+	/* 16-bit UUID to find (2 octets) */
+	bt_uuid16_create(uuid, get_le16(&pdu[5]));
+
+	/* Attribute value to find */
+	*vlen = len - 7;
+	if (*vlen > 0)
+		memcpy(value, pdu + 7, *vlen);
+
+	return len;
+}
+
+uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len)
+{
+	GSList *l;
+	uint16_t offset;
+
+	if (!pdu)
+		return 0;
+
+	pdu[0] = ATT_OP_FIND_BY_TYPE_RESP;
+
+	for (l = matches, offset = 1;
+				l && len >= (offset + sizeof(uint16_t) * 2);
+				l = l->next, offset += sizeof(uint16_t) * 2) {
+		struct att_range *range = l->data;
+
+		put_le16(range->start, &pdu[offset]);
+		put_le16(range->end, &pdu[offset + 2]);
+	}
+
+	return offset;
+}
+
+GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len)
+{
+	struct att_range *range;
+	GSList *matches;
+	off_t offset;
+
+	/* PDU should contain at least:
+	 * - Attribute Opcode (1 octet)
+	 * - Handles Information List (at least one entry):
+	 *   - Found Attribute Handle (2 octets)
+	 *   - Group End Handle (2 octets) */
+	if (pdu == NULL || len < 5)
+		return NULL;
+
+	if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP)
+		return NULL;
+
+	/* Reject incomplete Handles Information List */
+	if ((len - 1) % 4)
+		return NULL;
+
+	for (offset = 1, matches = NULL;
+				len >= (offset + sizeof(uint16_t) * 2);
+				offset += sizeof(uint16_t) * 2) {
+		range = g_new0(struct att_range, 1);
+		range->start = get_le16(&pdu[offset]);
+		range->end = get_le16(&pdu[offset + 2]);
+
+		matches = g_slist_append(matches, range);
+	}
+
+	return matches;
+}
+
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+						uint8_t *pdu, size_t len)
+{
+	uint16_t uuid_len;
+
+	if (!uuid)
+		return 0;
+
+	if (uuid->type == BT_UUID16)
+		uuid_len = 2;
+	else if (uuid->type == BT_UUID128)
+		uuid_len = 16;
+	else
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_READ_BY_TYPE_REQ;
+	/* Starting Handle (2 octets) */
+	put_le16(start, &pdu[1]);
+	/* Ending Handle (2 octets) */
+	put_le16(end, &pdu[3]);
+	/* Attribute Type (2 or 16 octet UUID) */
+	put_uuid_le(uuid, &pdu[5]);
+
+	return 5 + uuid_len;
+}
+
+uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
+						uint16_t *end, bt_uuid_t *uuid)
+{
+	const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+	uint8_t type;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (start == NULL || end == NULL || uuid == NULL)
+		return 0;
+
+	if (len == (min_len + 2))
+		type = BT_UUID16;
+	else if (len == (min_len + 16))
+		type = BT_UUID128;
+	else
+		return 0;
+
+	if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ)
+		return 0;
+
+	*start = get_le16(&pdu[1]);
+	*end = get_le16(&pdu[3]);
+
+	get_uuid(type, &pdu[5], uuid);
+
+	return len;
+}
+
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu,
+								size_t len)
+{
+	uint8_t *ptr;
+	size_t i, w, l;
+
+	if (list == NULL)
+		return 0;
+
+	if (pdu == NULL)
+		return 0;
+
+	l = MIN(len - 2, list->len);
+
+	pdu[0] = ATT_OP_READ_BY_TYPE_RESP;
+	pdu[1] = l;
+	ptr = &pdu[2];
+
+	for (i = 0, w = 2; i < list->num && w + l <= len; i++) {
+		memcpy(ptr, list->data[i], l);
+		ptr += l;
+		w += l;
+	}
+
+	return w;
+}
+
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len)
+{
+	struct att_data_list *list;
+	const uint8_t *ptr;
+	uint16_t elen, num;
+	int i;
+
+	if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
+		return NULL;
+
+	/* PDU must contain at least:
+	 * - Attribute Opcode (1 octet)
+	 * - Length (1 octet)
+	 * - Attribute Data List (at least one entry):
+	 *   - Attribute Handle (2 octets)
+	 *   - Attribute Value (at least 1 octet) */
+	if (len < 5)
+		return NULL;
+
+	elen = pdu[1];
+	/* Minimum Attribute Data List size */
+	if (elen < 3)
+		return NULL;
+
+	/* Reject incomplete Attribute Data List */
+	if ((len - 2) % elen)
+		return NULL;
+
+	num = (len - 2) / elen;
+	list = att_data_list_alloc(num, elen);
+	if (list == NULL)
+		return NULL;
+
+	ptr = &pdu[2];
+
+	for (i = 0; i < num; i++) {
+		memcpy(list->data[i], ptr, list->len);
+		ptr += list->len;
+	}
+
+	return list;
+}
+
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_WRITE_CMD;
+	put_le16(handle, &pdu[1]);
+
+	if (vlen > 0) {
+		memcpy(&pdu[3], value, vlen);
+		return min_len + vlen;
+	}
+
+	return min_len;
+}
+
+uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint8_t *value, size_t *vlen)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (value == NULL || vlen == NULL || handle == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_WRITE_CMD)
+		return 0;
+
+	*handle = get_le16(&pdu[1]);
+	memcpy(value, pdu + min_len, len - min_len);
+	*vlen = len - min_len;
+
+	return len;
+}
+
+uint16_t enc_signed_write_cmd(uint16_t handle, const uint8_t *value,
+					size_t vlen, struct bt_crypto *crypto,
+					const uint8_t csrk[16],
+					uint32_t sign_cnt,
+					uint8_t *pdu, size_t len)
+{
+	const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(handle);
+	const uint16_t min_len =  hdr_len + ATT_SIGNATURE_LEN;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_SIGNED_WRITE_CMD;
+	put_le16(handle, &pdu[1]);
+
+	if (vlen > 0)
+		memcpy(&pdu[hdr_len], value, vlen);
+
+	if (!bt_crypto_sign_att(crypto, csrk, pdu, hdr_len + vlen, sign_cnt,
+							&pdu[hdr_len + vlen]))
+		return 0;
+
+	return min_len + vlen;
+}
+
+uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len,
+						uint16_t *handle,
+						uint8_t *value, size_t *vlen,
+						uint8_t signature[12])
+{
+	const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(*handle);
+	const uint16_t min_len =  hdr_len + ATT_SIGNATURE_LEN;
+
+
+	if (pdu == NULL)
+		return 0;
+
+	if (value == NULL || vlen == NULL || handle == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_SIGNED_WRITE_CMD)
+		return 0;
+
+	*vlen = len - min_len;
+	*handle = get_le16(&pdu[1]);
+	memcpy(value, pdu + hdr_len, *vlen);
+
+	memcpy(signature, pdu + hdr_len + *vlen, ATT_SIGNATURE_LEN);
+
+	return len;
+}
+
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_WRITE_REQ;
+	put_le16(handle, &pdu[1]);
+
+	if (vlen > 0) {
+		memcpy(&pdu[3], value, vlen);
+		return min_len + vlen;
+	}
+
+	return min_len;
+}
+
+uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint8_t *value, size_t *vlen)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (value == NULL || vlen == NULL || handle == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_WRITE_REQ)
+		return 0;
+
+	*handle = get_le16(&pdu[1]);
+	*vlen = len - min_len;
+	if (*vlen > 0)
+		memcpy(value, pdu + min_len, *vlen);
+
+	return len;
+}
+
+uint16_t enc_write_resp(uint8_t *pdu)
+{
+	if (pdu == NULL)
+		return 0;
+
+	pdu[0] = ATT_OP_WRITE_RESP;
+
+	return sizeof(pdu[0]);
+}
+
+uint16_t dec_write_resp(const uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	if (pdu[0] != ATT_OP_WRITE_RESP)
+		return 0;
+
+	return len;
+}
+
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_READ_REQ;
+	/* Attribute Handle (2 octets) */
+	put_le16(handle, &pdu[1]);
+
+	return 3;
+}
+
+uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
+								size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_READ_BLOB_REQ;
+	/* Attribute Handle (2 octets) */
+	put_le16(handle, &pdu[1]);
+	/* Value Offset (2 octets) */
+	put_le16(offset, &pdu[3]);
+
+	return 5;
+}
+
+uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (handle == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_READ_REQ)
+		return 0;
+
+	*handle = get_le16(&pdu[1]);
+
+	return min_len;
+}
+
+uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle,
+							uint16_t *offset)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) +
+							sizeof(*offset);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (handle == NULL)
+		return 0;
+
+	if (offset == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_READ_BLOB_REQ)
+		return 0;
+
+	*handle = get_le16(&pdu[1]);
+	*offset = get_le16(&pdu[3]);
+
+	return min_len;
+}
+
+uint16_t enc_read_resp(uint8_t *value, size_t vlen, uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* If the attribute value length is longer than the allowed PDU size,
+	 * send only the octets that fit on the PDU. The remaining octets can
+	 * be requested using the Read Blob Request. */
+	if (vlen > len - 1)
+		vlen = len - 1;
+
+	pdu[0] = ATT_OP_READ_RESP;
+
+	memcpy(pdu + 1, value, vlen);
+
+	return vlen + 1;
+}
+
+uint16_t enc_read_blob_resp(uint8_t *value, size_t vlen, uint16_t offset,
+						uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	vlen -= offset;
+	if (vlen > len - 1)
+		vlen = len - 1;
+
+	pdu[0] = ATT_OP_READ_BLOB_RESP;
+
+	memcpy(pdu + 1, &value[offset], vlen);
+
+	return vlen + 1;
+}
+
+ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value,
+								size_t vlen)
+{
+	if (pdu == NULL)
+		return -EINVAL;
+
+	if (pdu[0] != ATT_OP_READ_RESP)
+		return -EINVAL;
+
+	if (value == NULL)
+		return len - 1;
+
+	if (vlen < (len - 1))
+		return -ENOBUFS;
+
+	memcpy(value, pdu + 1, len - 1);
+
+	return len - 1;
+}
+
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+						uint8_t *pdu, size_t len)
+{
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_ERROR;
+	/* Request Opcode In Error (1 octet) */
+	pdu[1] = opcode;
+	/* Attribute Handle In Error (2 octets) */
+	put_le16(handle, &pdu[2]);
+	/* Error Code (1 octet) */
+	pdu[4] = status;
+
+	return 5;
+}
+
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu,
+								size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_FIND_INFO_REQ;
+	/* Starting Handle (2 octets) */
+	put_le16(start, &pdu[1]);
+	/* Ending Handle (2 octets) */
+	put_le16(end, &pdu[3]);
+
+	return 5;
+}
+
+uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start,
+								uint16_t *end)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (start == NULL || end == NULL)
+		return 0;
+
+	if (pdu[0] != ATT_OP_FIND_INFO_REQ)
+		return 0;
+
+	*start = get_le16(&pdu[1]);
+	*end = get_le16(&pdu[3]);
+
+	return min_len;
+}
+
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+						uint8_t *pdu, size_t len)
+{
+	uint8_t *ptr;
+	size_t i, w;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (list == NULL)
+		return 0;
+
+	if (len < list->len + sizeof(uint8_t) * 2)
+		return 0;
+
+	pdu[0] = ATT_OP_FIND_INFO_RESP;
+	pdu[1] = format;
+	ptr = (void *) &pdu[2];
+
+	for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+		memcpy(ptr, list->data[i], list->len);
+		ptr += list->len;
+		w += list->len;
+	}
+
+	return w;
+}
+
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, size_t len,
+							uint8_t *format)
+{
+	struct att_data_list *list;
+	uint8_t *ptr;
+	uint16_t elen, num;
+	int i;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (format == NULL)
+		return 0;
+
+	if (pdu[0] != ATT_OP_FIND_INFO_RESP)
+		return 0;
+
+	*format = pdu[1];
+	elen = sizeof(pdu[0]) + sizeof(*format);
+	if (*format == 0x01)
+		elen += 2;
+	else if (*format == 0x02)
+		elen += 16;
+
+	num = (len - 2) / elen;
+
+	ptr = (void *) &pdu[2];
+
+	list = att_data_list_alloc(num, elen);
+	if (list == NULL)
+		return NULL;
+
+	for (i = 0; i < num; i++) {
+		memcpy(list->data[i], ptr, list->len);
+		ptr += list->len;
+	}
+
+	return list;
+}
+
+uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (len < (vlen + min_len))
+		return 0;
+
+	pdu[0] = ATT_OP_HANDLE_NOTIFY;
+	put_le16(handle, &pdu[1]);
+	memcpy(&pdu[3], value, vlen);
+
+	return vlen + min_len;
+}
+
+uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (len < (vlen + min_len))
+		return 0;
+
+	pdu[0] = ATT_OP_HANDLE_IND;
+	put_le16(handle, &pdu[1]);
+	memcpy(&pdu[3], value, vlen);
+
+	return vlen + min_len;
+}
+
+uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint8_t *value, size_t vlen)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+	uint16_t dlen;
+
+	if (pdu == NULL)
+		return 0;
+
+	if (pdu[0] != ATT_OP_HANDLE_IND)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	dlen = MIN(len - min_len, vlen);
+
+	if (handle)
+		*handle = get_le16(&pdu[1]);
+
+	memcpy(value, &pdu[3], dlen);
+
+	return dlen;
+}
+
+uint16_t enc_confirmation(uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_HANDLE_CNF;
+
+	return 1;
+}
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_MTU_REQ;
+	/* Client Rx MTU (2 octets) */
+	put_le16(mtu, &pdu[1]);
+
+	return 3;
+}
+
+uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (mtu == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_MTU_REQ)
+		return 0;
+
+	*mtu = get_le16(&pdu[1]);
+
+	return min_len;
+}
+
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_MTU_RESP;
+	/* Server Rx MTU (2 octets) */
+	put_le16(mtu, &pdu[1]);
+
+	return 3;
+}
+
+uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (mtu == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_MTU_RESP)
+		return 0;
+
+	*mtu = get_le16(&pdu[1]);
+
+	return min_len;
+}
+
+uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset,
+					const uint8_t *value, size_t vlen,
+					uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) +
+								sizeof(offset);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_PREP_WRITE_REQ;
+	put_le16(handle, &pdu[1]);
+	put_le16(offset, &pdu[3]);
+
+	if (vlen > 0) {
+		memcpy(&pdu[5], value, vlen);
+		return min_len + vlen;
+	}
+
+	return min_len;
+}
+
+uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle,
+				uint16_t *offset, uint8_t *value, size_t *vlen)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) +
+							sizeof(*offset);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (handle == NULL || offset == NULL || value == NULL || vlen == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_PREP_WRITE_REQ)
+		return 0;
+
+	*handle = get_le16(&pdu[1]);
+	*offset = get_le16(&pdu[3]);
+
+	*vlen = len - min_len;
+	if (*vlen > 0)
+		memcpy(value, pdu + min_len, *vlen);
+
+	return len;
+}
+
+uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset,
+					const uint8_t *value, size_t vlen,
+					uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) +
+								sizeof(offset);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (vlen > len - min_len)
+		vlen = len - min_len;
+
+	pdu[0] = ATT_OP_PREP_WRITE_RESP;
+	put_le16(handle, &pdu[1]);
+	put_le16(offset, &pdu[3]);
+
+	if (vlen > 0) {
+		memcpy(&pdu[5], value, vlen);
+		return min_len + vlen;
+	}
+
+	return min_len;
+}
+
+uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle,
+				uint16_t *offset, uint8_t *value, size_t *vlen)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) +
+								sizeof(*offset);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (handle == NULL || offset == NULL || value == NULL || vlen == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_PREP_WRITE_REQ)
+		return 0;
+
+	*handle = get_le16(&pdu[1]);
+	*offset = get_le16(&pdu[3]);
+	*vlen = len - min_len;
+	if (*vlen > 0)
+		memcpy(value, pdu + min_len, *vlen);
+
+	return len;
+}
+
+uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len)
+{
+	if (pdu == NULL)
+		return 0;
+
+	if (flags > 1)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_EXEC_WRITE_REQ;
+	/* Flags (1 octet) */
+	pdu[1] = flags;
+
+	return 2;
+}
+
+uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags)
+{
+	const uint16_t min_len = sizeof(pdu[0]) + sizeof(*flags);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (flags == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_EXEC_WRITE_REQ)
+		return 0;
+
+	*flags = pdu[1];
+
+	return min_len;
+}
+
+uint16_t enc_exec_write_resp(uint8_t *pdu)
+{
+	if (pdu == NULL)
+		return 0;
+
+	/* Attribute Opcode (1 octet) */
+	pdu[0] = ATT_OP_EXEC_WRITE_RESP;
+
+	return 1;
+}
+
+uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len)
+{
+	const uint16_t min_len = sizeof(pdu[0]);
+
+	if (pdu == NULL)
+		return 0;
+
+	if (len < min_len)
+		return 0;
+
+	if (pdu[0] != ATT_OP_EXEC_WRITE_RESP)
+		return 0;
+
+	return len;
+}
diff --git a/bluez/repo/attrib/att.h b/bluez/repo/attrib/att.h
new file mode 100644
index 0000000..2311aaf
--- /dev/null
+++ b/bluez/repo/attrib/att.h
@@ -0,0 +1,202 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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
+ *
+ */
+
+#include "src/shared/crypto.h"
+
+/* Len of signature in write signed packet */
+#define ATT_SIGNATURE_LEN		12
+
+/* Attribute Protocol Opcodes */
+#define ATT_OP_ERROR			0x01
+#define ATT_OP_MTU_REQ			0x02
+#define ATT_OP_MTU_RESP			0x03
+#define ATT_OP_FIND_INFO_REQ		0x04
+#define ATT_OP_FIND_INFO_RESP		0x05
+#define ATT_OP_FIND_BY_TYPE_REQ		0x06
+#define ATT_OP_FIND_BY_TYPE_RESP	0x07
+#define ATT_OP_READ_BY_TYPE_REQ		0x08
+#define ATT_OP_READ_BY_TYPE_RESP	0x09
+#define ATT_OP_READ_REQ			0x0A
+#define ATT_OP_READ_RESP		0x0B
+#define ATT_OP_READ_BLOB_REQ		0x0C
+#define ATT_OP_READ_BLOB_RESP		0x0D
+#define ATT_OP_READ_MULTI_REQ		0x0E
+#define ATT_OP_READ_MULTI_RESP		0x0F
+#define ATT_OP_READ_BY_GROUP_REQ	0x10
+#define ATT_OP_READ_BY_GROUP_RESP	0x11
+#define ATT_OP_WRITE_REQ		0x12
+#define ATT_OP_WRITE_RESP		0x13
+#define ATT_OP_WRITE_CMD		0x52
+#define ATT_OP_PREP_WRITE_REQ		0x16
+#define ATT_OP_PREP_WRITE_RESP		0x17
+#define ATT_OP_EXEC_WRITE_REQ		0x18
+#define ATT_OP_EXEC_WRITE_RESP		0x19
+#define ATT_OP_HANDLE_NOTIFY		0x1B
+#define ATT_OP_HANDLE_IND		0x1D
+#define ATT_OP_HANDLE_CNF		0x1E
+#define ATT_OP_SIGNED_WRITE_CMD		0xD2
+
+/* Error codes for Error response PDU */
+#define ATT_ECODE_INVALID_HANDLE		0x01
+#define ATT_ECODE_READ_NOT_PERM			0x02
+#define ATT_ECODE_WRITE_NOT_PERM		0x03
+#define ATT_ECODE_INVALID_PDU			0x04
+#define ATT_ECODE_AUTHENTICATION		0x05
+#define ATT_ECODE_REQ_NOT_SUPP			0x06
+#define ATT_ECODE_INVALID_OFFSET		0x07
+#define ATT_ECODE_AUTHORIZATION			0x08
+#define ATT_ECODE_PREP_QUEUE_FULL		0x09
+#define ATT_ECODE_ATTR_NOT_FOUND		0x0A
+#define ATT_ECODE_ATTR_NOT_LONG			0x0B
+#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE		0x0C
+#define ATT_ECODE_INVAL_ATTR_VALUE_LEN		0x0D
+#define ATT_ECODE_UNLIKELY			0x0E
+#define ATT_ECODE_INSUFF_ENC			0x0F
+#define ATT_ECODE_UNSUPP_GRP_TYPE		0x10
+#define ATT_ECODE_INSUFF_RESOURCES		0x11
+/* Application error */
+#define ATT_ECODE_IO				0x80
+#define ATT_ECODE_TIMEOUT			0x81
+#define ATT_ECODE_ABORTED			0x82
+
+#define ATT_MAX_VALUE_LEN			512
+#define ATT_DEFAULT_L2CAP_MTU			48
+#define ATT_DEFAULT_LE_MTU			23
+
+#define ATT_CID					4
+#define ATT_PSM					31
+
+/* Flags for Execute Write Request Operation */
+#define ATT_CANCEL_ALL_PREP_WRITES		0x00
+#define ATT_WRITE_ALL_PREP_WRITES		0x01
+
+/* Find Information Response Formats */
+#define ATT_FIND_INFO_RESP_FMT_16BIT		0x01
+#define ATT_FIND_INFO_RESP_FMT_128BIT		0x02
+
+struct att_data_list {
+	uint16_t num;
+	uint16_t len;
+	uint8_t **data;
+};
+
+struct att_range {
+	uint16_t start;
+	uint16_t end;
+};
+
+struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len);
+void att_data_list_free(struct att_data_list *list);
+
+const char *att_ecode2str(uint8_t status);
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+						uint8_t *pdu, size_t len);
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
+					uint16_t *end, bt_uuid_t *uuid);
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu,
+								size_t len);
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+				const uint8_t *value, size_t vlen, uint8_t *pdu,
+				size_t len);
+uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
+		uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen);
+uint16_t enc_find_by_type_resp(GSList *ranges, uint8_t *pdu, size_t len);
+GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len);
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len);
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+						uint8_t *pdu, size_t len);
+uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
+					uint16_t *end, bt_uuid_t *uuid);
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu,
+								size_t len);
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len);
+uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint8_t *value, size_t *vlen);
+uint16_t enc_signed_write_cmd(uint16_t handle,
+					const uint8_t *value, size_t vlen,
+					struct bt_crypto *crypto,
+					const uint8_t csrk[16],
+					uint32_t sign_cnt,
+					uint8_t *pdu, size_t len);
+uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len,
+						uint16_t *handle,
+						uint8_t *value, size_t *vlen,
+						uint8_t signature[12]);
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len);
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len);
+uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint8_t *value, size_t *vlen);
+uint16_t enc_write_resp(uint8_t *pdu);
+uint16_t dec_write_resp(const uint8_t *pdu, size_t len);
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len);
+uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
+								size_t len);
+uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle);
+uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle,
+							uint16_t *offset);
+uint16_t enc_read_resp(uint8_t *value, size_t vlen, uint8_t *pdu, size_t len);
+uint16_t enc_read_blob_resp(uint8_t *value, size_t vlen, uint16_t offset,
+						uint8_t *pdu, size_t len);
+ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value,
+								size_t vlen);
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+						uint8_t *pdu, size_t len);
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu,
+								size_t len);
+uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start,
+								uint16_t *end);
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+						uint8_t *pdu, size_t len);
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, size_t len,
+							uint8_t *format);
+uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len);
+uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen,
+						uint8_t *pdu, size_t len);
+uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint8_t *value, size_t vlen);
+uint16_t enc_confirmation(uint8_t *pdu, size_t len);
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len);
+uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu);
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len);
+uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu);
+
+uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset,
+					const uint8_t *value, size_t vlen,
+					uint8_t *pdu, size_t len);
+uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle,
+				uint16_t *offset, uint8_t *value, size_t *vlen);
+uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset,
+					const uint8_t *value, size_t vlen,
+					uint8_t *pdu, size_t len);
+uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle,
+						uint16_t *offset, uint8_t *value,
+						size_t *vlen);
+uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len);
+uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags);
+uint16_t enc_exec_write_resp(uint8_t *pdu);
+uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len);
diff --git a/bluez/repo/attrib/gatt-service.c b/bluez/repo/attrib/gatt-service.c
new file mode 100644
index 0000000..629d9cf
--- /dev/null
+++ b/bluez/repo/attrib/gatt-service.c
@@ -0,0 +1,375 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/adapter.h"
+#include "src/shared/util.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt-service.h"
+#include "src/log.h"
+
+struct gatt_info {
+	bt_uuid_t uuid;
+	uint8_t props;
+	int authentication;
+	int authorization;
+	GSList *callbacks;
+	unsigned int num_attrs;
+	uint16_t *value_handle;
+	uint16_t *ccc_handle;
+};
+
+struct attrib_cb {
+	attrib_event_t event;
+	void *fn;
+	void *user_data;
+};
+
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+	if (src->type == BT_UUID16)
+		put_le16(src->value.u16, dst);
+	else
+		/* Convert from 128-bit BE to LE */
+		bswap_128(&src->value.u128, dst);
+}
+
+static GSList *parse_opts(gatt_option opt1, va_list args)
+{
+	gatt_option opt = opt1;
+	struct gatt_info *info;
+	struct attrib_cb *cb;
+	GSList *l = NULL;
+
+	info = g_new0(struct gatt_info, 1);
+	l = g_slist_append(l, info);
+
+	while (opt != GATT_OPT_INVALID) {
+		switch (opt) {
+		case GATT_OPT_CHR_UUID16:
+			bt_uuid16_create(&info->uuid, va_arg(args, int));
+			/* characteristic declaration and value */
+			info->num_attrs += 2;
+			break;
+		case GATT_OPT_CHR_UUID:
+			memcpy(&info->uuid, va_arg(args, bt_uuid_t *),
+							sizeof(bt_uuid_t));
+			/* characteristic declaration and value */
+			info->num_attrs += 2;
+			break;
+		case GATT_OPT_CHR_PROPS:
+			info->props = va_arg(args, int);
+
+			if (info->props & (GATT_CHR_PROP_NOTIFY |
+						GATT_CHR_PROP_INDICATE))
+				/* client characteristic configuration */
+				info->num_attrs += 1;
+
+			/* TODO: "Extended Properties" property requires a
+			 * descriptor, but it is not supported yet. */
+			break;
+		case GATT_OPT_CHR_VALUE_CB:
+			cb = g_new0(struct attrib_cb, 1);
+			cb->event = va_arg(args, attrib_event_t);
+			cb->fn = va_arg(args, void *);
+			cb->user_data = va_arg(args, void *);
+			info->callbacks = g_slist_append(info->callbacks, cb);
+			break;
+		case GATT_OPT_CHR_VALUE_GET_HANDLE:
+			info->value_handle = va_arg(args, void *);
+			break;
+		case GATT_OPT_CCC_GET_HANDLE:
+			info->ccc_handle = va_arg(args, void *);
+			break;
+		case GATT_OPT_CHR_AUTHENTICATION:
+			info->authentication = va_arg(args, gatt_option);
+			break;
+		case GATT_OPT_CHR_AUTHORIZATION:
+			info->authorization = va_arg(args, gatt_option);
+			break;
+		case GATT_CHR_VALUE_READ:
+		case GATT_CHR_VALUE_WRITE:
+		case GATT_CHR_VALUE_BOTH:
+		case GATT_OPT_INVALID:
+		default:
+			error("Invalid option: %d", opt);
+		}
+
+		opt = va_arg(args, gatt_option);
+		if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) {
+			info = g_new0(struct gatt_info, 1);
+			l = g_slist_append(l, info);
+		}
+	}
+
+	return l;
+}
+
+static struct attribute *add_service_declaration(struct btd_adapter *adapter,
+				uint16_t handle, uint16_t svc, bt_uuid_t *uuid)
+{
+	bt_uuid_t bt_uuid;
+	uint8_t atval[16];
+	int len;
+
+	put_uuid_le(uuid, &atval[0]);
+	len = bt_uuid_len(uuid);
+
+	bt_uuid16_create(&bt_uuid, svc);
+
+	return attrib_db_add(adapter, handle, &bt_uuid, ATT_NONE,
+						ATT_NOT_PERMITTED, atval, len);
+}
+
+static int att_read_req(int authorization, int authentication, uint8_t props)
+{
+	if (authorization == GATT_CHR_VALUE_READ ||
+				authorization == GATT_CHR_VALUE_BOTH)
+		return ATT_AUTHORIZATION;
+	else if (authentication == GATT_CHR_VALUE_READ ||
+				authentication == GATT_CHR_VALUE_BOTH)
+		return ATT_AUTHENTICATION;
+	else if (!(props & GATT_CHR_PROP_READ))
+		return ATT_NOT_PERMITTED;
+
+	return ATT_NONE;
+}
+
+static int att_write_req(int authorization, int authentication, uint8_t props)
+{
+	if (authorization == GATT_CHR_VALUE_WRITE ||
+				authorization == GATT_CHR_VALUE_BOTH)
+		return ATT_AUTHORIZATION;
+	else if (authentication == GATT_CHR_VALUE_WRITE ||
+				authentication == GATT_CHR_VALUE_BOTH)
+		return ATT_AUTHENTICATION;
+	else if (!(props & (GATT_CHR_PROP_WRITE |
+					GATT_CHR_PROP_WRITE_WITHOUT_RESP)))
+		return ATT_NOT_PERMITTED;
+
+	return ATT_NONE;
+}
+
+static int find_callback(gconstpointer a, gconstpointer b)
+{
+	const struct attrib_cb *cb = a;
+	unsigned int event = GPOINTER_TO_UINT(b);
+
+	return cb->event - event;
+}
+
+static gboolean add_characteristic(struct btd_adapter *adapter,
+				uint16_t *handle, struct gatt_info *info)
+{
+	int read_req, write_req;
+	uint16_t h = *handle;
+	struct attribute *a;
+	bt_uuid_t bt_uuid;
+	uint8_t atval[ATT_MAX_VALUE_LEN];
+	GSList *l;
+
+	if ((info->uuid.type != BT_UUID16 && info->uuid.type != BT_UUID128) ||
+								!info->props) {
+		error("Characteristic UUID or properties are missing");
+		return FALSE;
+	}
+
+	read_req = att_read_req(info->authorization, info->authentication,
+								info->props);
+	write_req = att_write_req(info->authorization, info->authentication,
+								info->props);
+
+	/* TODO: static characteristic values are not supported, therefore a
+	 * callback must be always provided if a read/write property is set */
+	if (read_req != ATT_NOT_PERMITTED) {
+		gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ);
+
+		if (!g_slist_find_custom(info->callbacks, reqs,
+							find_callback)) {
+			error("Callback for read required");
+			return FALSE;
+		}
+	}
+
+	if (write_req != ATT_NOT_PERMITTED) {
+		gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE);
+
+		if (!g_slist_find_custom(info->callbacks, reqs,
+							find_callback)) {
+			error("Callback for write required");
+			return FALSE;
+		}
+	}
+
+	/* characteristic declaration */
+	bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID);
+	atval[0] = info->props;
+	put_le16(h + 1, &atval[1]);
+	put_uuid_le(&info->uuid, &atval[3]);
+	if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
+				atval, 3 + info->uuid.type / 8) == NULL)
+		return FALSE;
+
+	/* characteristic value */
+	a = attrib_db_add(adapter, h++, &info->uuid, read_req, write_req,
+								NULL, 0);
+	if (a == NULL)
+		return FALSE;
+
+	for (l = info->callbacks; l != NULL; l = l->next) {
+		struct attrib_cb *cb = l->data;
+
+		switch (cb->event) {
+		case ATTRIB_READ:
+			a->read_cb = cb->fn;
+			break;
+		case ATTRIB_WRITE:
+			a->write_cb = cb->fn;
+			break;
+		}
+
+		a->cb_user_data = cb->user_data;
+	}
+
+	if (info->value_handle != NULL)
+		*info->value_handle = a->handle;
+
+	/* client characteristic configuration descriptor */
+	if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) {
+		uint8_t cfg_val[2];
+
+		bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+		cfg_val[0] = 0x00;
+		cfg_val[1] = 0x00;
+		a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE,
+				ATT_AUTHENTICATION, cfg_val, sizeof(cfg_val));
+		if (a == NULL)
+			return FALSE;
+
+		if (info->ccc_handle != NULL)
+			*info->ccc_handle = a->handle;
+	}
+
+	*handle = h;
+
+	return TRUE;
+}
+
+static void free_gatt_info(void *data)
+{
+	struct gatt_info *info = data;
+
+	g_slist_free_full(info->callbacks, g_free);
+	g_free(info);
+}
+
+static void service_attr_del(struct btd_adapter *adapter, uint16_t start_handle,
+							uint16_t end_handle)
+{
+	uint16_t handle;
+
+	/* For a 128-bit category primary service below handle should be checked
+	 * for both non-zero as well as >= 0xffff. As on last iteration the
+	 * handle will turn to 0 from 0xffff and loop will be infinite.
+	 */
+	for (handle = start_handle; (handle != 0 && handle <= end_handle);
+								handle++) {
+		if (attrib_db_del(adapter, handle) < 0)
+			error("Can't delete handle 0x%04x", handle);
+	}
+}
+
+gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
+				bt_uuid_t *svc_uuid, gatt_option opt1, ...)
+{
+	char uuidstr[MAX_LEN_UUID_STR];
+	uint16_t start_handle, h;
+	unsigned int size;
+	va_list args;
+	GSList *chrs, *l;
+
+	bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
+
+	if (svc_uuid->type != BT_UUID16 && svc_uuid->type != BT_UUID128) {
+		error("Invalid service uuid: %s", uuidstr);
+		return FALSE;
+	}
+
+	va_start(args, opt1);
+	chrs = parse_opts(opt1, args);
+	va_end(args);
+
+	/* calculate how many attributes are necessary for this service */
+	for (l = chrs, size = 1; l != NULL; l = l->next) {
+		struct gatt_info *info = l->data;
+		size += info->num_attrs;
+	}
+
+	start_handle = attrib_db_find_avail(adapter, svc_uuid, size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		goto fail;
+	}
+
+	DBG("New service: handle 0x%04x, UUID %s, %d attributes",
+						start_handle, uuidstr, size);
+
+	/* service declaration */
+	h = start_handle;
+	if (add_service_declaration(adapter, h++, uuid, svc_uuid) == NULL)
+		goto fail;
+
+	for (l = chrs; l != NULL; l = l->next) {
+		struct gatt_info *info = l->data;
+
+		DBG("New characteristic: handle 0x%04x", h);
+		if (!add_characteristic(adapter, &h, info)) {
+			service_attr_del(adapter, start_handle, h - 1);
+			goto fail;
+		}
+	}
+
+	g_assert(size < USHRT_MAX);
+	g_assert(h == 0 || (h - start_handle == (uint16_t) size));
+	g_slist_free_full(chrs, free_gatt_info);
+
+	return TRUE;
+
+fail:
+	g_slist_free_full(chrs, free_gatt_info);
+	return FALSE;
+}
diff --git a/bluez/repo/attrib/gatt-service.h b/bluez/repo/attrib/gatt-service.h
new file mode 100644
index 0000000..728d3a8
--- /dev/null
+++ b/bluez/repo/attrib/gatt-service.h
@@ -0,0 +1,57 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef enum {
+	GATT_OPT_INVALID = 0,
+
+	/* bt_uuid_t* value */
+	GATT_OPT_CHR_UUID,
+
+	/* a uint16 value */
+	GATT_OPT_CHR_UUID16,
+
+	GATT_OPT_CHR_PROPS,
+	GATT_OPT_CHR_VALUE_CB,
+	GATT_OPT_CHR_AUTHENTICATION,
+	GATT_OPT_CHR_AUTHORIZATION,
+
+	/* Get attribute handle for characteristic value */
+	GATT_OPT_CHR_VALUE_GET_HANDLE,
+
+	/* Get handle for ccc attribute */
+	GATT_OPT_CCC_GET_HANDLE,
+
+	/* arguments for authentication/authorization */
+	GATT_CHR_VALUE_READ,
+	GATT_CHR_VALUE_WRITE,
+	GATT_CHR_VALUE_BOTH,
+} gatt_option;
+
+typedef enum {
+	ATTRIB_READ,
+	ATTRIB_WRITE,
+} attrib_event_t;
+
+gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
+					bt_uuid_t *svc_uuid, gatt_option opt1, ...);
diff --git a/bluez/repo/attrib/gatt.c b/bluez/repo/attrib/gatt.c
new file mode 100644
index 0000000..480f874
--- /dev/null
+++ b/bluez/repo/attrib/gatt.c
@@ -0,0 +1,1260 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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 <stdint.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+
+struct discover_primary {
+	int ref;
+	GAttrib *attrib;
+	unsigned int id;
+	bt_uuid_t uuid;
+	uint16_t start;
+	GSList *primaries;
+	gatt_cb_t cb;
+	void *user_data;
+};
+
+/* Used for the Included Services Discovery (ISD) procedure */
+struct included_discovery {
+	GAttrib		*attrib;
+	unsigned int	id;
+	int		refs;
+	int		err;
+	uint16_t	start_handle;
+	uint16_t	end_handle;
+	GSList		*includes;
+	gatt_cb_t	cb;
+	void		*user_data;
+};
+
+struct included_uuid_query {
+	struct included_discovery	*isd;
+	struct gatt_included		*included;
+};
+
+struct discover_char {
+	int ref;
+	GAttrib *attrib;
+	unsigned int id;
+	bt_uuid_t *uuid;
+	uint16_t end;
+	uint16_t start;
+	GSList *characteristics;
+	gatt_cb_t cb;
+	void *user_data;
+};
+
+struct discover_desc {
+	int ref;
+	GAttrib *attrib;
+	unsigned int id;
+	bt_uuid_t *uuid;
+	uint16_t start;
+	uint16_t end;
+	GSList *descriptors;
+	gatt_cb_t cb;
+	void *user_data;
+};
+
+static void discover_primary_unref(void *data)
+{
+	struct discover_primary *dp = data;
+
+	dp->ref--;
+
+	if (dp->ref > 0)
+		return;
+
+	g_slist_free_full(dp->primaries, g_free);
+	g_attrib_unref(dp->attrib);
+	g_free(dp);
+}
+
+static struct discover_primary *discover_primary_ref(
+						struct discover_primary *dp)
+{
+	dp->ref++;
+
+	return dp;
+}
+
+static struct included_discovery *isd_ref(struct included_discovery *isd)
+{
+	__sync_fetch_and_add(&isd->refs, 1);
+
+	return isd;
+}
+
+static void isd_unref(struct included_discovery *isd)
+{
+	if (__sync_sub_and_fetch(&isd->refs, 1) > 0)
+		return;
+
+	if (isd->err)
+		isd->cb(isd->err, NULL, isd->user_data);
+	else
+		isd->cb(isd->err, isd->includes, isd->user_data);
+
+	g_slist_free_full(isd->includes, g_free);
+	g_attrib_unref(isd->attrib);
+	g_free(isd);
+}
+
+static void discover_char_unref(void *data)
+{
+	struct discover_char *dc = data;
+
+	dc->ref--;
+
+	if (dc->ref > 0)
+		return;
+
+	g_slist_free_full(dc->characteristics, g_free);
+	g_attrib_unref(dc->attrib);
+	g_free(dc->uuid);
+	g_free(dc);
+}
+
+static struct discover_char *discover_char_ref(struct discover_char *dc)
+{
+	dc->ref++;
+
+	return dc;
+}
+
+static void discover_desc_unref(void *data)
+{
+	struct discover_desc *dd = data;
+
+	dd->ref--;
+
+	if (dd->ref > 0)
+		return;
+
+	g_slist_free_full(dd->descriptors, g_free);
+	g_attrib_unref(dd->attrib);
+	g_free(dd->uuid);
+	g_free(dd);
+}
+
+static struct discover_desc *discover_desc_ref(struct discover_desc *dd)
+{
+	dd->ref++;
+
+	return dd;
+}
+
+static void put_uuid_le(const bt_uuid_t *uuid, void *dst)
+{
+	if (uuid->type == BT_UUID16)
+		put_le16(uuid->value.u16, dst);
+	else
+		/* Convert from 128-bit BE to LE */
+		bswap_128(&uuid->value.u128, dst);
+}
+
+static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid)
+{
+	if (type == BT_UUID16) {
+		bt_uuid_t uuid16;
+
+		bt_uuid16_create(&uuid16, get_le16(val));
+		bt_uuid_to_uuid128(&uuid16, uuid);
+	} else {
+		uint128_t u128;
+
+		/* Convert from 128-bit LE to BE */
+		bswap_128(val, &u128);
+		bt_uuid128_create(uuid, u128);
+	}
+}
+
+static guint16 encode_discover_primary(uint16_t start, uint16_t end,
+				bt_uuid_t *uuid, uint8_t *pdu, size_t len)
+{
+	bt_uuid_t prim;
+	guint16 plen;
+
+	bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
+
+	if (uuid == NULL) {
+		/* Discover all primary services */
+		plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
+	} else {
+		uint8_t value[16];
+		size_t vlen;
+
+		/* Discover primary service by service UUID */
+		put_uuid_le(uuid, value);
+		vlen = bt_uuid_len(uuid);
+
+		plen = enc_find_by_type_req(start, end, &prim, value, vlen,
+								pdu, len);
+	}
+
+	return plen;
+}
+
+static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
+					guint16 iplen, gpointer user_data)
+
+{
+	struct discover_primary *dp = user_data;
+	GSList *ranges, *last;
+	struct att_range *range;
+	uint8_t *buf;
+	guint16 oplen;
+	int err = 0;
+	size_t buflen;
+
+	if (status) {
+		err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+		goto done;
+	}
+
+	ranges = dec_find_by_type_resp(ipdu, iplen);
+	if (ranges == NULL)
+		goto done;
+
+	dp->primaries = g_slist_concat(dp->primaries, ranges);
+
+	last = g_slist_last(ranges);
+	range = last->data;
+
+	if (range->end == 0xffff)
+		goto done;
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (range->end < dp->start) {
+		err = ATT_ECODE_UNLIKELY;
+		goto done;
+	}
+
+	dp->start = range->end + 1;
+
+	buf = g_attrib_get_buffer(dp->attrib, &buflen);
+	oplen = encode_discover_primary(dp->start, 0xffff, &dp->uuid,
+								buf, buflen);
+
+	if (oplen == 0)
+		goto done;
+
+	g_attrib_send(dp->attrib, dp->id, buf, oplen, primary_by_uuid_cb,
+			discover_primary_ref(dp), discover_primary_unref);
+	return;
+
+done:
+	dp->cb(err, dp->primaries, dp->user_data);
+}
+
+static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+							gpointer user_data)
+{
+	struct discover_primary *dp = user_data;
+	struct att_data_list *list;
+	unsigned int i, err;
+	uint16_t start, end;
+	uint8_t type;
+
+	if (status) {
+		err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+		goto done;
+	}
+
+	list = dec_read_by_grp_resp(ipdu, iplen);
+	if (list == NULL) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	if (list->len == 6)
+		type = BT_UUID16;
+	else if (list->len == 20)
+		type = BT_UUID128;
+	else {
+		att_data_list_free(list);
+		err = ATT_ECODE_INVALID_PDU;
+		goto done;
+	}
+
+	for (i = 0, end = 0; i < list->num; i++) {
+		const uint8_t *data = list->data[i];
+		struct gatt_primary *primary;
+		bt_uuid_t uuid128;
+
+		start = get_le16(&data[0]);
+		end = get_le16(&data[2]);
+
+		get_uuid128(type, &data[4], &uuid128);
+
+		primary = g_try_new0(struct gatt_primary, 1);
+		if (!primary) {
+			att_data_list_free(list);
+			err = ATT_ECODE_INSUFF_RESOURCES;
+			goto done;
+		}
+		primary->range.start = start;
+		primary->range.end = end;
+		bt_uuid_to_string(&uuid128, primary->uuid, sizeof(primary->uuid));
+		dp->primaries = g_slist_append(dp->primaries, primary);
+	}
+
+	att_data_list_free(list);
+	err = 0;
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (end < dp->start) {
+		err = ATT_ECODE_UNLIKELY;
+		goto done;
+	}
+
+	dp->start = end + 1;
+
+	if (end != 0xffff) {
+		size_t buflen;
+		uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
+		guint16 oplen = encode_discover_primary(dp->start, 0xffff, NULL,
+								buf, buflen);
+
+
+		g_attrib_send(dp->attrib, dp->id, buf, oplen, primary_all_cb,
+						discover_primary_ref(dp),
+						discover_primary_unref);
+
+		return;
+	}
+
+done:
+	dp->cb(err, dp->primaries, dp->user_data);
+}
+
+guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
+							gpointer user_data)
+{
+	struct discover_primary *dp;
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+	GAttribResultFunc cb;
+	guint16 plen;
+
+	plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	dp = g_try_new0(struct discover_primary, 1);
+	if (dp == NULL)
+		return 0;
+
+	dp->attrib = g_attrib_ref(attrib);
+	dp->cb = func;
+	dp->user_data = user_data;
+	dp->start = 0x0001;
+
+	if (uuid) {
+		dp->uuid = *uuid;
+		cb = primary_by_uuid_cb;
+	} else
+		cb = primary_all_cb;
+
+	dp->id = g_attrib_send(attrib, 0, buf, plen, cb,
+					discover_primary_ref(dp),
+					discover_primary_unref);
+
+	return dp->id;
+}
+
+static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
+					uint16_t len, gpointer user_data)
+{
+	struct included_uuid_query *query = user_data;
+	struct included_discovery *isd = query->isd;
+	struct gatt_included *incl = query->included;
+	unsigned int err = status;
+	bt_uuid_t uuid128;
+	size_t buflen;
+	uint8_t *buf;
+
+	if (err)
+		goto done;
+
+	buf = g_attrib_get_buffer(isd->attrib, &buflen);
+	if (dec_read_resp(pdu, len, buf, buflen) != 16) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	get_uuid128(BT_UUID128, buf, &uuid128);
+
+	bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid));
+	isd->includes = g_slist_append(isd->includes, incl);
+	query->included = NULL;
+
+done:
+	if (isd->err == 0)
+		isd->err = err;
+}
+
+static void inc_query_free(void *data)
+{
+	struct included_uuid_query *query = data;
+
+	isd_unref(query->isd);
+	g_free(query->included);
+	g_free(query);
+}
+
+static guint resolve_included_uuid(struct included_discovery *isd,
+					struct gatt_included *incl)
+{
+	struct included_uuid_query *query;
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen);
+	guint16 oplen = enc_read_req(incl->range.start, buf, buflen);
+
+	query = g_new0(struct included_uuid_query, 1);
+	query->isd = isd_ref(isd);
+	query->included = incl;
+
+	return g_attrib_send(isd->attrib, query->isd->id, buf, oplen,
+				resolve_included_uuid_cb, query,
+				inc_query_free);
+}
+
+static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len)
+{
+	struct gatt_included *incl = g_new0(struct gatt_included, 1);
+
+	incl->handle = get_le16(&buf[0]);
+	incl->range.start = get_le16(&buf[2]);
+	incl->range.end = get_le16(&buf[4]);
+
+	if (len == 8) {
+		bt_uuid_t uuid128;
+
+		get_uuid128(BT_UUID16, &buf[6], &uuid128);
+		bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid));
+	}
+
+	return incl;
+}
+
+static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+							gpointer user_data);
+
+static guint find_included(struct included_discovery *isd, uint16_t start)
+{
+	bt_uuid_t uuid;
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen);
+	guint16 oplen;
+
+	bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+	oplen = enc_read_by_type_req(start, isd->end_handle, &uuid,
+							buf, buflen);
+
+	/* If id != 0 it means we are in the middle of include search */
+	if (isd->id)
+		return g_attrib_send(isd->attrib, isd->id, buf, oplen,
+				find_included_cb, isd_ref(isd),
+				(GDestroyNotify) isd_unref);
+
+	/* This is first call from the gattrib user */
+	isd->id = g_attrib_send(isd->attrib, 0, buf, oplen, find_included_cb,
+				isd_ref(isd), (GDestroyNotify) isd_unref);
+
+	return isd->id;
+}
+
+static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct included_discovery *isd = user_data;
+	uint16_t last_handle = isd->end_handle;
+	unsigned int err = status;
+	struct att_data_list *list;
+	int i;
+
+	if (err == ATT_ECODE_ATTR_NOT_FOUND)
+		err = 0;
+
+	if (status)
+		goto done;
+
+	list = dec_read_by_type_resp(pdu, len);
+	if (list == NULL) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	if (list->len != 6 && list->len != 8) {
+		err = ATT_ECODE_IO;
+		att_data_list_free(list);
+		goto done;
+	}
+
+	for (i = 0; i < list->num; i++) {
+		struct gatt_included *incl;
+
+		incl = included_from_buf(list->data[i], list->len);
+		last_handle = incl->handle;
+
+		/* 128 bit UUID, needs resolving */
+		if (list->len == 6) {
+			resolve_included_uuid(isd, incl);
+			continue;
+		}
+
+		isd->includes = g_slist_append(isd->includes, incl);
+	}
+
+	att_data_list_free(list);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last_handle < isd->start_handle) {
+		isd->err = ATT_ECODE_UNLIKELY;
+		goto done;
+	}
+
+	isd->start_handle = last_handle + 1;
+
+	if (last_handle < isd->end_handle)
+		find_included(isd, isd->start_handle);
+
+done:
+	if (isd->err == 0)
+		isd->err = err;
+}
+
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+					gatt_cb_t func, gpointer user_data)
+{
+	struct included_discovery *isd;
+
+	isd = g_new0(struct included_discovery, 1);
+	isd->attrib = g_attrib_ref(attrib);
+	isd->start_handle = start;
+	isd->end_handle = end;
+	isd->cb = func;
+	isd->user_data = user_data;
+
+	return find_included(isd, start);
+}
+
+static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+							gpointer user_data)
+{
+	struct discover_char *dc = user_data;
+	struct att_data_list *list;
+	unsigned int i, err = 0;
+	uint16_t last = 0;
+	uint8_t type;
+
+	/* We have all the characteristic now, lets send it up */
+	if (status == ATT_ECODE_ATTR_NOT_FOUND) {
+		err = dc->characteristics ? 0 : status;
+		goto done;
+	}
+
+	if (status) {
+		err = status;
+		goto done;
+	}
+
+	list = dec_read_by_type_resp(ipdu, iplen);
+	if (list == NULL) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	if (list->len == 7)
+		type = BT_UUID16;
+	else
+		type = BT_UUID128;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value = list->data[i];
+		struct gatt_char *chars;
+		bt_uuid_t uuid128;
+
+		last = get_le16(value);
+
+		get_uuid128(type, &value[5], &uuid128);
+
+		if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid128))
+			continue;
+
+		chars = g_try_new0(struct gatt_char, 1);
+		if (!chars) {
+			att_data_list_free(list);
+			err = ATT_ECODE_INSUFF_RESOURCES;
+			goto done;
+		}
+
+		chars->handle = last;
+		chars->properties = value[2];
+		chars->value_handle = get_le16(&value[3]);
+		bt_uuid_to_string(&uuid128, chars->uuid, sizeof(chars->uuid));
+		dc->characteristics = g_slist_append(dc->characteristics,
+									chars);
+	}
+
+	att_data_list_free(list);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last < dc->start) {
+		err = ATT_ECODE_UNLIKELY;
+		goto done;
+	}
+
+	dc->start = last + 1;
+
+	if (last != 0 && (dc->start < dc->end)) {
+		bt_uuid_t uuid;
+		guint16 oplen;
+		size_t buflen;
+		uint8_t *buf;
+
+		buf = g_attrib_get_buffer(dc->attrib, &buflen);
+
+		bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+
+		oplen = enc_read_by_type_req(dc->start, dc->end, &uuid, buf,
+									buflen);
+
+		if (oplen == 0)
+			return;
+
+		g_attrib_send(dc->attrib, dc->id, buf, oplen,
+				char_discovered_cb, discover_char_ref(dc),
+				discover_char_unref);
+
+		return;
+	}
+
+done:
+	dc->cb(err, dc->characteristics, dc->user_data);
+}
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+						bt_uuid_t *uuid, gatt_cb_t func,
+						gpointer user_data)
+{
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+	struct discover_char *dc;
+	bt_uuid_t type_uuid;
+	guint16 plen;
+
+	bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID);
+
+	plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	dc = g_try_new0(struct discover_char, 1);
+	if (dc == NULL)
+		return 0;
+
+	dc->attrib = g_attrib_ref(attrib);
+	dc->cb = func;
+	dc->user_data = user_data;
+	dc->end = end;
+	dc->start = start;
+	dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
+
+	dc->id = g_attrib_send(attrib, 0, buf, plen, char_discovered_cb,
+				discover_char_ref(dc), discover_char_unref);
+
+	return dc->id;
+}
+
+guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
+					bt_uuid_t *uuid, GAttribResultFunc func,
+					gpointer user_data)
+{
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+	guint16 plen;
+
+	plen = enc_read_by_type_req(start, end, uuid, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
+}
+
+struct read_long_data {
+	GAttrib *attrib;
+	GAttribResultFunc func;
+	gpointer user_data;
+	guint8 *buffer;
+	guint16 size;
+	guint16 handle;
+	guint id;
+	int ref;
+};
+
+static void read_long_destroy(gpointer user_data)
+{
+	struct read_long_data *long_read = user_data;
+
+	if (__sync_sub_and_fetch(&long_read->ref, 1) > 0)
+		return;
+
+	g_attrib_unref(long_read->attrib);
+
+	if (long_read->buffer != NULL)
+		g_free(long_read->buffer);
+
+	g_free(long_read);
+}
+
+static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
+							gpointer user_data)
+{
+	struct read_long_data *long_read = user_data;
+	uint8_t *buf;
+	size_t buflen;
+	guint8 *tmp;
+	guint16 plen;
+	guint id;
+
+	if (status != 0 || rlen == 1) {
+		status = 0;
+		goto done;
+	}
+
+	tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
+
+	if (tmp == NULL) {
+		status = ATT_ECODE_INSUFF_RESOURCES;
+		goto done;
+	}
+
+	memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
+	long_read->buffer = tmp;
+	long_read->size += rlen - 1;
+
+	buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+	if (rlen < buflen)
+		goto done;
+
+	plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
+								buf, buflen);
+	id = g_attrib_send(long_read->attrib, long_read->id, buf, plen,
+				read_blob_helper, long_read, read_long_destroy);
+
+	if (id != 0) {
+		__sync_fetch_and_add(&long_read->ref, 1);
+		return;
+	}
+
+	status = ATT_ECODE_IO;
+
+done:
+	long_read->func(status, long_read->buffer, long_read->size,
+							long_read->user_data);
+}
+
+static void read_char_helper(guint8 status, const guint8 *rpdu,
+					guint16 rlen, gpointer user_data)
+{
+	struct read_long_data *long_read = user_data;
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+	guint16 plen;
+	guint id;
+
+	if (status != 0 || rlen < buflen)
+		goto done;
+
+	long_read->buffer = g_malloc(rlen);
+	if (long_read->buffer == NULL) {
+		status = ATT_ECODE_INSUFF_RESOURCES;
+		goto done;
+	}
+
+	memcpy(long_read->buffer, rpdu, rlen);
+	long_read->size = rlen;
+
+	plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
+
+	id = g_attrib_send(long_read->attrib, long_read->id, buf, plen,
+				read_blob_helper, long_read, read_long_destroy);
+	if (id != 0) {
+		__sync_fetch_and_add(&long_read->ref, 1);
+		return;
+	}
+
+	status = ATT_ECODE_IO;
+
+done:
+	long_read->func(status, rpdu, rlen, long_read->user_data);
+}
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
+							gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+	guint id;
+	struct read_long_data *long_read;
+
+	long_read = g_try_new0(struct read_long_data, 1);
+
+	if (long_read == NULL)
+		return 0;
+
+	long_read->attrib = g_attrib_ref(attrib);
+	long_read->func = func;
+	long_read->user_data = user_data;
+	long_read->handle = handle;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_read_req(handle, buf, buflen);
+	id = g_attrib_send(attrib, 0, buf, plen, read_char_helper,
+						long_read, read_long_destroy);
+	if (id == 0) {
+		g_attrib_unref(long_read->attrib);
+		g_free(long_read);
+	} else {
+		__sync_fetch_and_add(&long_read->ref, 1);
+		long_read->id = id;
+	}
+
+	return id;
+}
+
+struct write_long_data {
+	GAttrib *attrib;
+	GAttribResultFunc func;
+	gpointer user_data;
+	guint16 handle;
+	uint16_t offset;
+	uint8_t *value;
+	size_t vlen;
+};
+
+static guint execute_write(GAttrib *attrib, uint8_t flags,
+				GAttribResultFunc func, gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_exec_write_req(flags, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
+}
+
+static guint prepare_write(struct write_long_data *long_write);
+
+static void prepare_write_cb(guint8 status, const guint8 *rpdu, guint16 rlen,
+							gpointer user_data)
+{
+	struct write_long_data *long_write = user_data;
+
+	if (status != 0) {
+		long_write->func(status, rpdu, rlen, long_write->user_data);
+		return;
+	}
+
+	/* Skip Prepare Write Response PDU header (5 bytes) */
+	long_write->offset += rlen - 5;
+
+	if (long_write->offset == long_write->vlen) {
+		execute_write(long_write->attrib, ATT_WRITE_ALL_PREP_WRITES,
+				long_write->func, long_write->user_data);
+		g_free(long_write->value);
+		g_free(long_write);
+
+		return;
+	}
+
+	prepare_write(long_write);
+}
+
+static guint prepare_write(struct write_long_data *long_write)
+{
+	GAttrib *attrib = long_write->attrib;
+	uint16_t handle = long_write->handle;
+	uint16_t offset = long_write->offset;
+	uint8_t *buf, *value = long_write->value + offset;
+	size_t buflen, vlen = long_write->vlen - offset;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+
+	plen = enc_prep_write_req(handle, offset, value, vlen, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, prepare_write_cb, long_write,
+									NULL);
+}
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value,
+			size_t vlen, GAttribResultFunc func, gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	struct write_long_data *long_write;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+
+	/* Use Write Request if payload fits on a single transfer, including 3
+	 * bytes for the header. */
+	if (vlen <= buflen - 3) {
+		uint16_t plen;
+
+		plen = enc_write_req(handle, value, vlen, buf, buflen);
+		if (plen == 0)
+			return 0;
+
+		return g_attrib_send(attrib, 0, buf, plen, func, user_data,
+									NULL);
+	}
+
+	/* Write Long Characteristic Values */
+	long_write = g_try_new0(struct write_long_data, 1);
+	if (long_write == NULL)
+		return 0;
+
+	long_write->attrib = attrib;
+	long_write->func = func;
+	long_write->user_data = user_data;
+	long_write->handle = handle;
+	long_write->value = g_memdup(value, vlen);
+	long_write->vlen = vlen;
+
+	return prepare_write(long_write);
+}
+
+guint gatt_execute_write(GAttrib *attrib, uint8_t flags,
+				GAttribResultFunc func, gpointer user_data)
+{
+	return execute_write(attrib, flags, func, user_data);
+}
+
+guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle,
+					const uint8_t *value, size_t vlen,
+					GAttribResultFunc func,
+					gpointer user_data)
+{
+	uint8_t *buf;
+	guint16 plen;
+	size_t buflen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+
+	plen = enc_prep_write_req(handle, 0, value, vlen, buf, buflen);
+	if (!plen)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
+}
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+							gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_mtu_req(mtu, buf, buflen);
+	return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
+}
+
+static void desc_discovered_cb(guint8 status, const guint8 *ipdu,
+					guint16 iplen, gpointer user_data)
+{
+	struct discover_desc *dd = user_data;
+	struct att_data_list *list;
+	unsigned int i, err = 0;
+	guint8 format;
+	uint16_t last = 0xffff;
+	uint8_t type;
+	gboolean uuid_found = FALSE;
+
+	if (status == ATT_ECODE_ATTR_NOT_FOUND) {
+		err = dd->descriptors ? 0 : status;
+		goto done;
+	}
+
+	if (status) {
+		err = status;
+		goto done;
+	}
+
+	list = dec_find_info_resp(ipdu, iplen, &format);
+	if (!list) {
+		err = ATT_ECODE_IO;
+		goto done;
+	}
+
+	if (format == ATT_FIND_INFO_RESP_FMT_16BIT)
+		type = BT_UUID16;
+	else
+		type = BT_UUID128;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value = list->data[i];
+		struct gatt_desc *desc;
+		bt_uuid_t uuid128;
+
+		last = get_le16(value);
+
+		get_uuid128(type, &value[2], &uuid128);
+
+		if (dd->uuid) {
+			if (bt_uuid_cmp(dd->uuid, &uuid128))
+				continue;
+			else
+				uuid_found = TRUE;
+		}
+
+		desc = g_try_new0(struct gatt_desc, 1);
+		if (!desc) {
+			att_data_list_free(list);
+			err = ATT_ECODE_INSUFF_RESOURCES;
+			goto done;
+		}
+
+		bt_uuid_to_string(&uuid128, desc->uuid, sizeof(desc->uuid));
+		desc->handle = last;
+
+		if (type == BT_UUID16)
+			desc->uuid16 = get_le16(&value[2]);
+
+		dd->descriptors = g_slist_append(dd->descriptors, desc);
+
+		if (uuid_found)
+			break;
+	}
+
+	att_data_list_free(list);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last < dd->start) {
+		err = ATT_ECODE_UNLIKELY;
+		goto done;
+	}
+
+	dd->start = last + 1;
+
+	if (last < dd->end && !uuid_found) {
+		guint16 oplen;
+		size_t buflen;
+		uint8_t *buf;
+
+		buf = g_attrib_get_buffer(dd->attrib, &buflen);
+
+		oplen = enc_find_info_req(dd->start, dd->end, buf, buflen);
+		if (oplen == 0)
+			return;
+
+		g_attrib_send(dd->attrib, dd->id, buf, oplen,
+				desc_discovered_cb, discover_desc_ref(dd),
+				discover_desc_unref);
+
+		return;
+	}
+
+done:
+	dd->cb(err, dd->descriptors, dd->user_data);
+}
+
+guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+						bt_uuid_t *uuid, gatt_cb_t func,
+						gpointer user_data)
+{
+	size_t buflen;
+	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+	struct discover_desc *dd;
+	guint16 plen;
+
+	plen = enc_find_info_req(start, end, buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	dd = g_try_new0(struct discover_desc, 1);
+	if (dd == NULL)
+		return 0;
+
+	dd->attrib = g_attrib_ref(attrib);
+	dd->cb = func;
+	dd->user_data = user_data;
+	dd->start = start;
+	dd->end = end;
+	dd->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
+
+	dd->id = g_attrib_send(attrib, 0, buf, plen, desc_discovered_cb,
+				discover_desc_ref(dd), discover_desc_unref);
+
+	return dd->id;
+}
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value,
+			int vlen, GDestroyNotify notify, gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+	return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
+}
+
+guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle,
+						const uint8_t *value, int vlen,
+						struct bt_crypto *crypto,
+						const uint8_t csrk[16],
+						uint32_t sign_cnt,
+						GDestroyNotify notify,
+						gpointer user_data)
+{
+	uint8_t *buf;
+	size_t buflen;
+	guint16 plen;
+
+	buf = g_attrib_get_buffer(attrib, &buflen);
+	plen = enc_signed_write_cmd(handle, value, vlen, crypto, csrk, sign_cnt,
+								buf, buflen);
+	if (plen == 0)
+		return 0;
+
+	return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
+}
+
+static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
+{
+	sdp_list_t *list;
+	uuid_t proto;
+
+	sdp_uuid16_create(&proto, ATT_UUID);
+
+	for (list = proto_list; list; list = list->next) {
+		sdp_list_t *p;
+		for (p = list->data; p; p = p->next) {
+			sdp_data_t *seq = p->data;
+			if (seq && seq->dtd == SDP_UUID16 &&
+				sdp_uuid16_cmp(&proto, &seq->val.uuid) == 0)
+				return seq->next;
+		}
+	}
+
+	return NULL;
+}
+
+static gboolean parse_proto_params(sdp_list_t *proto_list, uint16_t *psm,
+						uint16_t *start, uint16_t *end)
+{
+	sdp_data_t *seq1, *seq2;
+
+	if (psm)
+		*psm = sdp_get_proto_port(proto_list, L2CAP_UUID);
+
+	/* Getting start and end handle */
+	seq1 = proto_seq_find(proto_list);
+	if (!seq1 || seq1->dtd != SDP_UINT16)
+		return FALSE;
+
+	seq2 = seq1->next;
+	if (!seq2 || seq2->dtd != SDP_UINT16)
+		return FALSE;
+
+	if (start)
+		*start = seq1->val.uint16;
+
+	if (end)
+		*end = seq2->val.uint16;
+
+	return TRUE;
+}
+
+gboolean gatt_parse_record(const sdp_record_t *rec,
+					uuid_t *prim_uuid, uint16_t *psm,
+					uint16_t *start, uint16_t *end)
+{
+	sdp_list_t *list;
+	uuid_t uuid;
+	gboolean ret;
+
+	if (sdp_get_service_classes(rec, &list) < 0)
+		return FALSE;
+
+	memcpy(&uuid, list->data, sizeof(uuid));
+	sdp_list_free(list, free);
+
+	if (sdp_get_access_protos(rec, &list) < 0)
+		return FALSE;
+
+	ret = parse_proto_params(list, psm, start, end);
+
+	sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(list, NULL);
+
+	/* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */
+	if (ret && prim_uuid)
+		memcpy(prim_uuid, &uuid, sizeof(uuid_t));
+
+	return ret;
+}
diff --git a/bluez/repo/attrib/gatt.h b/bluez/repo/attrib/gatt.h
new file mode 100644
index 0000000..63b2940
--- /dev/null
+++ b/bluez/repo/attrib/gatt.h
@@ -0,0 +1,122 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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
+ *
+ */
+
+/*
+ * GATT Characteristic Property bit field
+ * Reference: Core SPEC 4.1 page 2183 (Table 3.5: Characteristic Properties
+ * bit field) defines how the Characteristic Value can be used, or how the
+ * characteristic descriptors (see Section 3.3.3 - page 2184) can be accessed.
+ * In the core spec, regular properties are included in the characteristic
+ * declaration, and the extended properties are defined as descriptor.
+ */
+
+#define GATT_CHR_PROP_BROADCAST				0x01
+#define GATT_CHR_PROP_READ				0x02
+#define GATT_CHR_PROP_WRITE_WITHOUT_RESP		0x04
+#define GATT_CHR_PROP_WRITE				0x08
+#define GATT_CHR_PROP_NOTIFY				0x10
+#define GATT_CHR_PROP_INDICATE				0x20
+#define GATT_CHR_PROP_AUTH				0x40
+#define GATT_CHR_PROP_EXT_PROP				0x80
+
+/* Client Characteristic Configuration bit field */
+#define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT	0x0001
+#define GATT_CLIENT_CHARAC_CFG_IND_BIT		0x0002
+
+typedef void (*gatt_cb_t) (uint8_t status, GSList *l, void *user_data);
+
+struct gatt_primary {
+	char uuid[MAX_LEN_UUID_STR + 1];
+	gboolean changed;
+	struct att_range range;
+};
+
+struct gatt_included {
+	char uuid[MAX_LEN_UUID_STR + 1];
+	uint16_t handle;
+	struct att_range range;
+};
+
+struct gatt_char {
+	char uuid[MAX_LEN_UUID_STR + 1];
+	uint16_t handle;
+	uint8_t properties;
+	uint16_t value_handle;
+};
+
+struct gatt_desc {
+	char uuid[MAX_LEN_UUID_STR + 1];
+	uint16_t handle;
+	uint16_t uuid16;
+};
+
+guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
+							gpointer user_data);
+
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+					gatt_cb_t func, gpointer user_data);
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+					bt_uuid_t *uuid, gatt_cb_t func,
+					gpointer user_data);
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
+							gpointer user_data);
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value,
+					size_t vlen, GAttribResultFunc func,
+					gpointer user_data);
+
+guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+						bt_uuid_t *uuid, gatt_cb_t func,
+						gpointer user_data);
+
+guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle,
+					const uint8_t *value, size_t vlen,
+					GAttribResultFunc func,
+					gpointer user_data);
+
+guint gatt_execute_write(GAttrib *attrib, uint8_t flags,
+				GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value,
+			int vlen, GDestroyNotify notify, gpointer user_data);
+
+guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle,
+						const uint8_t *value, int vlen,
+						struct bt_crypto *crypto,
+						const uint8_t csrk[16],
+						uint32_t sign_cnt,
+						GDestroyNotify notify,
+						gpointer user_data);
+guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
+				bt_uuid_t *uuid, GAttribResultFunc func,
+				gpointer user_data);
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+							gpointer user_data);
+
+gboolean gatt_parse_record(const sdp_record_t *rec,
+					uuid_t *prim_uuid, uint16_t *psm,
+					uint16_t *start, uint16_t *end);
diff --git a/bluez/repo/attrib/gattrib.c b/bluez/repo/attrib/gattrib.c
new file mode 100644
index 0000000..2e1e39a
--- /dev/null
+++ b/bluez/repo/attrib/gattrib.c
@@ -0,0 +1,488 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "attrib/gattrib.h"
+
+struct _GAttrib {
+	int ref_count;
+	struct bt_att *att;
+	GIOChannel *io;
+	GDestroyNotify destroy;
+	gpointer destroy_user_data;
+	struct queue *callbacks;
+	uint8_t *buf;
+	int buflen;
+	struct queue *track_ids;
+};
+
+struct id_pair {
+	unsigned int org_id;
+	unsigned int pend_id;
+};
+
+struct attrib_callbacks {
+	struct id_pair *id;
+	GAttribResultFunc result_func;
+	GAttribNotifyFunc notify_func;
+	GDestroyNotify destroy_func;
+	gpointer user_data;
+	GAttrib *parent;
+	uint16_t notify_handle;
+};
+
+static bool find_with_org_id(const void *data, const void *user_data)
+{
+	const struct id_pair *p = data;
+	unsigned int orig_id = PTR_TO_UINT(user_data);
+
+	return (p->org_id == orig_id);
+}
+
+static struct id_pair *store_id(GAttrib *attrib, unsigned int org_id,
+							unsigned int pend_id)
+{
+	struct id_pair *t;
+
+	t = new0(struct id_pair, 1);
+	if (!t)
+		return NULL;
+
+	t->org_id = org_id;
+	t->pend_id = pend_id;
+
+	if (queue_push_tail(attrib->track_ids, t))
+		return t;
+
+	return NULL;
+}
+
+GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed)
+{
+	gint fd;
+	GAttrib *attr;
+
+	if (!io)
+		return NULL;
+
+	fd = g_io_channel_unix_get_fd(io);
+	attr = new0(GAttrib, 1);
+	if (!attr)
+		return NULL;
+
+	g_io_channel_ref(io);
+	attr->io = io;
+
+	attr->att = bt_att_new(fd, ext_signed);
+	if (!attr->att)
+		goto fail;
+
+	bt_att_set_close_on_unref(attr->att, true);
+	g_io_channel_set_close_on_unref(io, FALSE);
+
+	if (!bt_att_set_mtu(attr->att, mtu))
+		goto fail;
+
+	attr->buf = malloc0(mtu);
+	attr->buflen = mtu;
+	if (!attr->buf)
+		goto fail;
+
+	attr->callbacks = queue_new();
+	if (!attr->callbacks)
+		goto fail;
+
+	attr->track_ids = queue_new();
+	if (!attr->track_ids)
+		goto fail;
+
+	return g_attrib_ref(attr);
+
+fail:
+	free(attr->buf);
+	bt_att_unref(attr->att);
+	g_io_channel_unref(io);
+	free(attr);
+	return NULL;
+}
+
+GAttrib *g_attrib_ref(GAttrib *attrib)
+{
+	if (!attrib)
+		return NULL;
+
+	__sync_fetch_and_add(&attrib->ref_count, 1);
+
+	DBG("%p: g_attrib_ref=%d ", attrib, attrib->ref_count);
+
+	return attrib;
+}
+
+static void attrib_callbacks_destroy(void *data)
+{
+	struct attrib_callbacks *cb = data;
+
+	if (cb->destroy_func)
+		cb->destroy_func(cb->user_data);
+
+	if (queue_remove(cb->parent->track_ids, cb->id))
+		free(cb->id);
+
+	free(data);
+}
+
+static void attrib_callbacks_remove(void *data)
+{
+	struct attrib_callbacks *cb = data;
+
+	if (!data || !queue_remove(cb->parent->callbacks, data))
+		return;
+
+	attrib_callbacks_destroy(data);
+}
+
+void g_attrib_unref(GAttrib *attrib)
+{
+	if (!attrib)
+		return;
+
+	DBG("%p: g_attrib_unref=%d ", attrib, attrib->ref_count - 1);
+
+	if (__sync_sub_and_fetch(&attrib->ref_count, 1))
+		return;
+
+	if (attrib->destroy)
+		attrib->destroy(attrib->destroy_user_data);
+
+	bt_att_unref(attrib->att);
+
+	queue_destroy(attrib->callbacks, attrib_callbacks_destroy);
+	queue_destroy(attrib->track_ids, free);
+
+	free(attrib->buf);
+
+	g_io_channel_unref(attrib->io);
+
+	free(attrib);
+}
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib)
+{
+	if (!attrib)
+		return NULL;
+
+	return attrib->io;
+}
+
+struct bt_att *g_attrib_get_att(GAttrib *attrib)
+{
+	if (!attrib)
+		return NULL;
+
+	return attrib->att;
+}
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy,
+							gpointer user_data)
+{
+	if (!attrib)
+		return FALSE;
+
+	attrib->destroy = destroy;
+	attrib->destroy_user_data = user_data;
+
+	return TRUE;
+}
+
+
+static uint8_t *construct_full_pdu(uint8_t opcode, const void *pdu,
+								uint16_t length)
+{
+	uint8_t *buf = malloc0(length + 1);
+
+	if (!buf)
+		return NULL;
+
+	buf[0] = opcode;
+	memcpy(buf + 1, pdu, length);
+
+	return buf;
+}
+
+static void attrib_callback_result(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	uint8_t *buf;
+	struct attrib_callbacks *cb = user_data;
+	guint8 status = 0;
+
+	if (!cb)
+		return;
+
+	buf = construct_full_pdu(opcode, pdu, length);
+	if (!buf)
+		return;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		/* Error code is the third byte of the PDU data */
+		if (length < 4)
+			status = BT_ATT_ERROR_UNLIKELY;
+		else
+			status = ((guint8 *)pdu)[3];
+	}
+
+	if (cb->result_func)
+		cb->result_func(status, buf, length + 1, cb->user_data);
+
+	free(buf);
+}
+
+static void attrib_callback_notify(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	uint8_t *buf;
+	struct attrib_callbacks *cb = user_data;
+
+	if (!cb || !cb->notify_func)
+		return;
+
+	if (cb->notify_handle != GATTRIB_ALL_HANDLES && length < 2)
+		return;
+
+	if (cb->notify_handle != GATTRIB_ALL_HANDLES &&
+					cb->notify_handle != get_le16(pdu))
+		return;
+
+	buf = construct_full_pdu(opcode, pdu, length);
+	if (!buf)
+		return;
+
+	cb->notify_func(buf, length + 1, cb->user_data);
+
+	free(buf);
+}
+
+guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
+				GAttribResultFunc func, gpointer user_data,
+				GDestroyNotify notify)
+{
+	struct attrib_callbacks *cb = NULL;
+	bt_att_response_func_t response_cb = NULL;
+	bt_att_destroy_func_t destroy_cb = NULL;
+	unsigned int pend_id;
+
+	if (!attrib)
+		return 0;
+
+	if (!pdu || !len)
+		return 0;
+
+	if (func || notify) {
+		cb = new0(struct attrib_callbacks, 1);
+		if (!cb)
+			return 0;
+		cb->result_func = func;
+		cb->user_data = user_data;
+		cb->destroy_func = notify;
+		cb->parent = attrib;
+		queue_push_head(attrib->callbacks, cb);
+		response_cb = attrib_callback_result;
+		destroy_cb = attrib_callbacks_remove;
+
+	}
+
+	pend_id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1, len - 1,
+						response_cb, cb, destroy_cb);
+
+	/*
+	 * We store here pair as it is easier to handle it in response and in
+	 * case where user request us to use specific id request - see below.
+	 */
+	if (id == 0)
+		id = pend_id;
+
+	/*
+	 * If user what us to use given id, lets keep track on that so we give
+	 * user a possibility to cancel ongoing request.
+	 */
+	if (cb)
+		cb->id = store_id(attrib, id, pend_id);
+
+	return id;
+}
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id)
+{
+	struct id_pair *p;
+
+	if (!attrib)
+		return FALSE;
+
+	/*
+	 * If request belongs to gattrib and is not yet done it has to be on
+	 * the tracking id queue
+	 *
+	 * FIXME: It can happen that on the queue there is id_pair with
+	 * given id which was provided by the user. In the same time it might
+	 * happen that other attrib user got dynamic allocated req_id with same
+	 * value as the one provided by the other user.
+	 * In such case there are two clients having same request id and in
+	 * this point of time we don't know which one calls cancel. For
+	 * now we cancel request in which id was specified by the user.
+	 */
+	p = queue_remove_if(attrib->track_ids, find_with_org_id,
+							UINT_TO_PTR(id));
+	if (!p)
+		return FALSE;
+
+	id = p->pend_id;
+	free(p);
+
+	return bt_att_cancel(attrib->att, id);
+}
+
+static void cancel_request(void *data, void *user_data)
+{
+	struct id_pair *p = data;
+	GAttrib *attrib = user_data;
+
+	bt_att_cancel(attrib->att, p->pend_id);
+}
+
+gboolean g_attrib_cancel_all(GAttrib *attrib)
+{
+	if (!attrib)
+		return FALSE;
+
+	/* Cancel only request which belongs to gattrib */
+	queue_foreach(attrib->track_ids, cancel_request, attrib);
+	queue_remove_all(attrib->track_ids, NULL, NULL, free);
+
+	return TRUE;
+}
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
+				GAttribNotifyFunc func, gpointer user_data,
+				GDestroyNotify notify)
+{
+	struct attrib_callbacks *cb = NULL;
+
+	if (!attrib)
+		return 0;
+
+	if (func || notify) {
+		cb = new0(struct attrib_callbacks, 1);
+		if (!cb)
+			return 0;
+		cb->notify_func = func;
+		cb->notify_handle = handle;
+		cb->user_data = user_data;
+		cb->destroy_func = notify;
+		cb->parent = attrib;
+		queue_push_head(attrib->callbacks, cb);
+	}
+
+	if (opcode == GATTRIB_ALL_REQS)
+		opcode = BT_ATT_ALL_REQUESTS;
+
+	return bt_att_register(attrib->att, opcode, attrib_callback_notify,
+						cb, attrib_callbacks_remove);
+}
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
+{
+	uint16_t mtu;
+
+	if (!attrib || !len)
+		return NULL;
+
+	mtu = bt_att_get_mtu(attrib->att);
+
+	/*
+	 * Clients of this expect a buffer to use.
+	 *
+	 * Pdu encoding in shared/att verifies if whole buffer fits the mtu,
+	 * thus we should set the buflen also when mtu is reduced. But we
+	 * need to reallocate the buffer only if mtu is larger.
+	 */
+	if (mtu > attrib->buflen)
+		attrib->buf = g_realloc(attrib->buf, mtu);
+
+	attrib->buflen = mtu;
+	*len = attrib->buflen;
+	return attrib->buf;
+}
+
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
+{
+	if (!attrib)
+		return FALSE;
+
+	/*
+	 * Clients of this expect a buffer to use.
+	 *
+	 * Pdu encoding in sharred/att verifies if whole buffer fits the mtu,
+	 * thus we should set the buflen also when mtu is reduced. But we
+	 * need to reallocate the buffer only if mtu is larger.
+	 */
+	if (mtu > attrib->buflen)
+		attrib->buf = g_realloc(attrib->buf, mtu);
+
+	attrib->buflen = mtu;
+
+	return bt_att_set_mtu(attrib->att, mtu);
+}
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id)
+{
+	if (!attrib)
+		return FALSE;
+
+	return bt_att_unregister(attrib->att, id);
+}
+
+gboolean g_attrib_unregister_all(GAttrib *attrib)
+{
+	if (!attrib)
+		return false;
+
+	return bt_att_unregister_all(attrib->att);
+}
diff --git a/bluez/repo/attrib/gattrib.h b/bluez/repo/attrib/gattrib.h
new file mode 100644
index 0000000..611f952
--- /dev/null
+++ b/bluez/repo/attrib/gattrib.h
@@ -0,0 +1,76 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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
+ *
+ */
+#ifndef __GATTRIB_H
+#define __GATTRIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GATTRIB_ALL_REQS 0xFE
+#define GATTRIB_ALL_HANDLES 0x0000
+
+struct bt_att;  /* Forward declaration for compatibility */
+struct _GAttrib;
+typedef struct _GAttrib GAttrib;
+
+typedef void (*GAttribResultFunc) (guint8 status, const guint8 *pdu,
+					guint16 len, gpointer user_data);
+typedef void (*GAttribDisconnectFunc)(gpointer user_data);
+typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
+typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
+							gpointer user_data);
+
+GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed);
+GAttrib *g_attrib_ref(GAttrib *attrib);
+void g_attrib_unref(GAttrib *attrib);
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib);
+
+struct bt_att *g_attrib_get_att(GAttrib *attrib);
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+		GDestroyNotify destroy, gpointer user_data);
+
+guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
+			GAttribResultFunc func, gpointer user_data,
+			GDestroyNotify notify);
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id);
+gboolean g_attrib_cancel_all(GAttrib *attrib);
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
+				GAttribNotifyFunc func, gpointer user_data,
+				GDestroyNotify notify);
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len);
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu);
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id);
+gboolean g_attrib_unregister_all(GAttrib *attrib);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/bluez/repo/attrib/gatttool.c b/bluez/repo/attrib/gatttool.c
new file mode 100644
index 0000000..95bd20a
--- /dev/null
+++ b/bluez/repo/attrib/gatttool.c
@@ -0,0 +1,624 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "att.h"
+#include "btio/btio.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+
+static char *opt_src = NULL;
+static char *opt_dst = NULL;
+static char *opt_dst_type = NULL;
+static char *opt_value = NULL;
+static char *opt_sec_level = NULL;
+static bt_uuid_t *opt_uuid = NULL;
+static int opt_start = 0x0001;
+static int opt_end = 0xffff;
+static int opt_handle = -1;
+static int opt_mtu = 0;
+static int opt_psm = 0;
+static gboolean opt_primary = FALSE;
+static gboolean opt_characteristics = FALSE;
+static gboolean opt_char_read = FALSE;
+static gboolean opt_listen = FALSE;
+static gboolean opt_char_desc = FALSE;
+static gboolean opt_char_write = FALSE;
+static gboolean opt_char_write_req = FALSE;
+static gboolean opt_interactive = FALSE;
+static GMainLoop *event_loop;
+static gboolean got_error = FALSE;
+static GSourceFunc operation;
+
+struct characteristic_data {
+	GAttrib *attrib;
+	uint16_t start;
+	uint16_t end;
+};
+
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+	uint8_t *opdu;
+	uint16_t handle, i, olen = 0;
+	size_t plen;
+
+	handle = get_le16(&pdu[1]);
+
+	switch (pdu[0]) {
+	case ATT_OP_HANDLE_NOTIFY:
+		g_print("Notification handle = 0x%04x value: ", handle);
+		break;
+	case ATT_OP_HANDLE_IND:
+		g_print("Indication   handle = 0x%04x value: ", handle);
+		break;
+	default:
+		g_print("Invalid opcode\n");
+		return;
+	}
+
+	for (i = 3; i < len; i++)
+		g_print("%02x ", pdu[i]);
+
+	g_print("\n");
+
+	if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+		return;
+
+	opdu = g_attrib_get_buffer(attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+
+	if (olen > 0)
+		g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static gboolean listen_start(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+
+	g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
+						events_handler, attrib, NULL);
+	g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
+						events_handler, attrib, NULL);
+
+	return FALSE;
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	GAttrib *attrib;
+	uint16_t mtu;
+	uint16_t cid;
+	GError *gerr = NULL;
+
+	if (err) {
+		g_printerr("%s\n", err->message);
+		got_error = TRUE;
+		g_main_loop_quit(event_loop);
+	}
+
+	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
+				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		g_printerr("Can't detect MTU, using default: %s",
+								gerr->message);
+		g_error_free(gerr);
+		mtu = ATT_DEFAULT_LE_MTU;
+	}
+
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(io, mtu, false);
+
+	if (opt_listen)
+		g_idle_add(listen_start, attrib);
+
+	operation(attrib);
+}
+
+static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		g_printerr("Discover all primary services failed: %s\n",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		g_print("attr handle = 0x%04x, end grp handle = 0x%04x "
+			"uuid: %s\n", prim->range.start, prim->range.end, prim->uuid);
+	}
+
+done:
+	g_main_loop_quit(event_loop);
+}
+
+static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
+{
+	GSList *l;
+
+	if (status != 0) {
+		g_printerr("Discover primary services by UUID failed: %s\n",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	for (l = ranges; l; l = l->next) {
+		struct att_range *range = l->data;
+		g_print("Starting handle: %04x Ending handle: %04x\n",
+						range->start, range->end);
+	}
+
+done:
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean primary(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+
+	if (opt_uuid)
+		gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb,
+									NULL);
+	else
+		gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+
+	return FALSE;
+}
+
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		g_printerr("Discover all characteristics failed: %s\n",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	for (l = characteristics; l; l = l->next) {
+		struct gatt_char *chars = l->data;
+
+		g_print("handle = 0x%04x, char properties = 0x%02x, char value "
+			"handle = 0x%04x, uuid = %s\n", chars->handle,
+			chars->properties, chars->value_handle, chars->uuid);
+	}
+
+done:
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+
+	gatt_discover_char(attrib, opt_start, opt_end, opt_uuid,
+						char_discovered_cb, NULL);
+
+	return FALSE;
+}
+
+static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	uint8_t value[plen];
+	ssize_t vlen;
+	int i;
+
+	if (status != 0) {
+		g_printerr("Characteristic value/descriptor read failed: %s\n",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+	if (vlen < 0) {
+		g_printerr("Protocol error\n");
+		goto done;
+	}
+	g_print("Characteristic value/descriptor: ");
+	for (i = 0; i < vlen; i++)
+		g_print("%02x ", value[i]);
+	g_print("\n");
+
+done:
+	if (!opt_listen)
+		g_main_loop_quit(event_loop);
+}
+
+static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct att_data_list *list;
+	int i;
+
+	if (status != 0) {
+		g_printerr("Read characteristics by UUID failed: %s\n",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	list = dec_read_by_type_resp(pdu, plen);
+	if (list == NULL)
+		goto done;
+
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value = list->data[i];
+		int j;
+
+		g_print("handle: 0x%04x \t value: ", get_le16(value));
+		value += 2;
+		for (j = 0; j < list->len - 2; j++, value++)
+			g_print("%02x ", *value);
+		g_print("\n");
+	}
+
+	att_data_list_free(list);
+
+done:
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_read(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+
+	if (opt_uuid != NULL) {
+
+		gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid,
+						char_read_by_uuid_cb, NULL);
+
+		return FALSE;
+	}
+
+	if (opt_handle <= 0) {
+		g_printerr("A valid handle is required\n");
+		g_main_loop_quit(event_loop);
+		return FALSE;
+	}
+
+	gatt_read_char(attrib, opt_handle, char_read_cb, attrib);
+
+	return FALSE;
+}
+
+static void mainloop_quit(gpointer user_data)
+{
+	uint8_t *value = user_data;
+
+	g_free(value);
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_write(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+	uint8_t *value;
+	size_t len;
+
+	if (opt_handle <= 0) {
+		g_printerr("A valid handle is required\n");
+		goto error;
+	}
+
+	if (opt_value == NULL || opt_value[0] == '\0') {
+		g_printerr("A value is required\n");
+		goto error;
+	}
+
+	len = gatt_attr_data_from_string(opt_value, &value);
+	if (len == 0) {
+		g_printerr("Invalid value\n");
+		goto error;
+	}
+
+	gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value);
+
+	g_free(value);
+	return FALSE;
+
+error:
+	g_main_loop_quit(event_loop);
+	return FALSE;
+}
+
+static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	if (status != 0) {
+		g_printerr("Characteristic Write Request failed: "
+						"%s\n", att_ecode2str(status));
+		goto done;
+	}
+
+	if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) {
+		g_printerr("Protocol error\n");
+		goto done;
+	}
+
+	g_print("Characteristic value was written successfully\n");
+
+done:
+	if (!opt_listen)
+		g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_write_req(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+	uint8_t *value;
+	size_t len;
+
+	if (opt_handle <= 0) {
+		g_printerr("A valid handle is required\n");
+		goto error;
+	}
+
+	if (opt_value == NULL || opt_value[0] == '\0') {
+		g_printerr("A value is required\n");
+		goto error;
+	}
+
+	len = gatt_attr_data_from_string(opt_value, &value);
+	if (len == 0) {
+		g_printerr("Invalid value\n");
+		goto error;
+	}
+
+	gatt_write_char(attrib, opt_handle, value, len, char_write_req_cb,
+									NULL);
+
+	g_free(value);
+	return FALSE;
+
+error:
+	g_main_loop_quit(event_loop);
+	return FALSE;
+}
+
+static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		g_printerr("Discover descriptors failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (l = descriptors; l; l = l->next) {
+		struct gatt_desc *desc = l->data;
+
+		g_print("handle = 0x%04x, uuid = %s\n", desc->handle,
+								desc->uuid);
+	}
+
+	if (!opt_listen)
+		g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_desc(gpointer user_data)
+{
+	GAttrib *attrib = user_data;
+
+	gatt_discover_desc(attrib, opt_start, opt_end, NULL, char_desc_cb,
+									NULL);
+
+	return FALSE;
+}
+
+static gboolean parse_uuid(const char *key, const char *value,
+				gpointer user_data, GError **error)
+{
+	if (!value)
+		return FALSE;
+
+	opt_uuid = g_try_malloc(sizeof(bt_uuid_t));
+	if (opt_uuid == NULL)
+		return FALSE;
+
+	if (bt_string_to_uuid(opt_uuid, value) < 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static GOptionEntry primary_char_options[] = {
+	{ "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
+		"Starting handle(optional)", "0x0001" },
+	{ "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
+		"Ending handle(optional)", "0xffff" },
+	{ "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+		parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
+	{ NULL },
+};
+
+static GOptionEntry char_rw_options[] = {
+	{ "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
+		"Read/Write characteristic by handle(required)", "0x0001" },
+	{ "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
+		"Write characteristic value (required for write operation)",
+		"0x0001" },
+	{NULL},
+};
+
+static GOptionEntry gatt_options[] = {
+	{ "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
+		"Primary Service Discovery", NULL },
+	{ "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
+		"Characteristics Discovery", NULL },
+	{ "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
+		"Characteristics Value/Descriptor Read", NULL },
+	{ "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
+		"Characteristics Value Write Without Response (Write Command)",
+		NULL },
+	{ "char-write-req", 0, 0, G_OPTION_ARG_NONE, &opt_char_write_req,
+		"Characteristics Value Write (Write Request)", NULL },
+	{ "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
+		"Characteristics Descriptor Discovery", NULL },
+	{ "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
+		"Listen for notifications and indications", NULL },
+	{ "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+		&opt_interactive, "Use interactive mode", NULL },
+	{ NULL },
+};
+
+static GOptionEntry options[] = {
+	{ "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
+		"Specify local adapter interface", "hciX" },
+	{ "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
+		"Specify remote Bluetooth address", "MAC" },
+	{ "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
+		"Set LE address type. Default: public", "[public | random]"},
+	{ "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
+		"Specify the MTU size", "MTU" },
+	{ "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+		"Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
+	{ "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
+		"Set security level. Default: low", "[low | medium | high]"},
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GOptionGroup *gatt_group, *params_group, *char_rw_group;
+	GError *gerr = NULL;
+	GIOChannel *chan;
+
+	opt_dst_type = g_strdup("public");
+	opt_sec_level = g_strdup("low");
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	/* GATT commands */
+	gatt_group = g_option_group_new("gatt", "GATT commands",
+					"Show all GATT commands", NULL, NULL);
+	g_option_context_add_group(context, gatt_group);
+	g_option_group_add_entries(gatt_group, gatt_options);
+
+	/* Primary Services and Characteristics arguments */
+	params_group = g_option_group_new("params",
+			"Primary Services/Characteristics arguments",
+			"Show all Primary Services/Characteristics arguments",
+			NULL, NULL);
+	g_option_context_add_group(context, params_group);
+	g_option_group_add_entries(params_group, primary_char_options);
+
+	/* Characteristics value/descriptor read/write arguments */
+	char_rw_group = g_option_group_new("char-read-write",
+		"Characteristics Value/Descriptor Read/Write arguments",
+		"Show all Characteristics Value/Descriptor Read/Write "
+		"arguments",
+		NULL, NULL);
+	g_option_context_add_group(context, char_rw_group);
+	g_option_group_add_entries(char_rw_group, char_rw_options);
+
+	if (!g_option_context_parse(context, &argc, &argv, &gerr)) {
+		g_printerr("%s\n", gerr->message);
+		g_clear_error(&gerr);
+	}
+
+	if (opt_interactive) {
+		interactive(opt_src, opt_dst, opt_dst_type, opt_psm);
+		goto done;
+	}
+
+	if (opt_primary)
+		operation = primary;
+	else if (opt_characteristics)
+		operation = characteristics;
+	else if (opt_char_read)
+		operation = characteristics_read;
+	else if (opt_char_write)
+		operation = characteristics_write;
+	else if (opt_char_write_req)
+		operation = characteristics_write_req;
+	else if (opt_char_desc)
+		operation = characteristics_desc;
+	else {
+		char *help = g_option_context_get_help(context, TRUE, NULL);
+		g_print("%s\n", help);
+		g_free(help);
+		got_error = TRUE;
+		goto done;
+	}
+
+	if (opt_dst == NULL) {
+		g_print("Remote Bluetooth address required\n");
+		got_error = TRUE;
+		goto done;
+	}
+
+	chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
+					opt_psm, opt_mtu, connect_cb, &gerr);
+	if (chan == NULL) {
+		g_printerr("%s\n", gerr->message);
+		g_clear_error(&gerr);
+		got_error = TRUE;
+		goto done;
+	}
+
+	event_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(event_loop);
+
+	g_main_loop_unref(event_loop);
+
+done:
+	g_option_context_free(context);
+	g_free(opt_src);
+	g_free(opt_dst);
+	g_free(opt_uuid);
+	g_free(opt_sec_level);
+
+	if (got_error)
+		exit(EXIT_FAILURE);
+	else
+		exit(EXIT_SUCCESS);
+}
diff --git a/bluez/repo/attrib/gatttool.h b/bluez/repo/attrib/gatttool.h
new file mode 100644
index 0000000..8f0913c
--- /dev/null
+++ b/bluez/repo/attrib/gatttool.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+int interactive(const char *src, const char *dst, const char *dst_type,
+								int psm);
+GIOChannel *gatt_connect(const char *src, const char *dst,
+			const char *dst_type, const char *sec_level,
+			int psm, int mtu, BtIOConnect connect_cb,
+			GError **gerr);
+size_t gatt_attr_data_from_string(const char *str, uint8_t **data);
diff --git a/bluez/repo/attrib/interactive.c b/bluez/repo/attrib/interactive.c
new file mode 100644
index 0000000..7d4786a
--- /dev/null
+++ b/bluez/repo/attrib/interactive.c
@@ -0,0 +1,1031 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *
+ *
+ *  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 <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <glib.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+#include "client/display.h"
+
+static GIOChannel *iochannel = NULL;
+static GAttrib *attrib = NULL;
+static GMainLoop *event_loop;
+static GString *prompt;
+
+static char *opt_src = NULL;
+static char *opt_dst = NULL;
+static char *opt_dst_type = NULL;
+static char *opt_sec_level = NULL;
+static int opt_psm = 0;
+static int opt_mtu = 0;
+static int start;
+static int end;
+
+static void cmd_help(int argcp, char **argvp);
+
+static enum state {
+	STATE_DISCONNECTED,
+	STATE_CONNECTING,
+	STATE_CONNECTED
+} conn_state;
+
+#define error(fmt, arg...) \
+	rl_printf(COLOR_RED "Error: " COLOR_OFF fmt, ## arg)
+
+#define failed(fmt, arg...) \
+	rl_printf(COLOR_RED "Command Failed: " COLOR_OFF fmt, ## arg)
+
+static char *get_prompt(void)
+{
+	if (conn_state == STATE_CONNECTED)
+		g_string_assign(prompt, COLOR_BLUE);
+	else
+		g_string_assign(prompt, "");
+
+	if (opt_dst)
+		g_string_append_printf(prompt, "[%17s]", opt_dst);
+	else
+		g_string_append_printf(prompt, "[%17s]", "");
+
+	if (conn_state == STATE_CONNECTED)
+		g_string_append(prompt, COLOR_OFF);
+
+	if (opt_psm)
+		g_string_append(prompt, "[BR]");
+	else
+		g_string_append(prompt, "[LE]");
+
+	g_string_append(prompt, "> ");
+
+	return prompt->str;
+}
+
+
+static void set_state(enum state st)
+{
+	conn_state = st;
+	rl_set_prompt(get_prompt());
+}
+
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+	uint8_t *opdu;
+	uint16_t handle, i, olen;
+	size_t plen;
+	GString *s;
+
+	handle = get_le16(&pdu[1]);
+
+	switch (pdu[0]) {
+	case ATT_OP_HANDLE_NOTIFY:
+		s = g_string_new(NULL);
+		g_string_printf(s, "Notification handle = 0x%04x value: ",
+									handle);
+		break;
+	case ATT_OP_HANDLE_IND:
+		s = g_string_new(NULL);
+		g_string_printf(s, "Indication   handle = 0x%04x value: ",
+									handle);
+		break;
+	default:
+		error("Invalid opcode\n");
+		return;
+	}
+
+	for (i = 3; i < len; i++)
+		g_string_append_printf(s, "%02x ", pdu[i]);
+
+	rl_printf("%s\n", s->str);
+	g_string_free(s, TRUE);
+
+	if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+		return;
+
+	opdu = g_attrib_get_buffer(attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+
+	if (olen > 0)
+		g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	uint16_t mtu;
+	uint16_t cid;
+
+	if (err) {
+		set_state(STATE_DISCONNECTED);
+		error("%s\n", err->message);
+		return;
+	}
+
+	bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu,
+				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+
+	if (err) {
+		g_printerr("Can't detect MTU, using default: %s", err->message);
+		g_error_free(err);
+		mtu = ATT_DEFAULT_LE_MTU;
+	}
+
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(iochannel, mtu, false);
+	g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
+						events_handler, attrib, NULL);
+	g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
+						events_handler, attrib, NULL);
+	set_state(STATE_CONNECTED);
+	rl_printf("Connection successful\n");
+}
+
+static void disconnect_io()
+{
+	if (conn_state == STATE_DISCONNECTED)
+		return;
+
+	g_attrib_unref(attrib);
+	attrib = NULL;
+	opt_mtu = 0;
+
+	g_io_channel_shutdown(iochannel, FALSE, NULL);
+	g_io_channel_unref(iochannel);
+	iochannel = NULL;
+
+	set_state(STATE_DISCONNECTED);
+}
+
+static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		error("Discover all primary services failed: %s\n",
+						att_ecode2str(status));
+		return;
+	}
+
+	if (services == NULL) {
+		error("No primary service found\n");
+		return;
+	}
+
+	for (l = services; l; l = l->next) {
+		struct gatt_primary *prim = l->data;
+		rl_printf("attr handle: 0x%04x, end grp handle: 0x%04x uuid: %s\n",
+				prim->range.start, prim->range.end, prim->uuid);
+	}
+}
+
+static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		error("Discover primary services by UUID failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	if (ranges == NULL) {
+		error("No service UUID found\n");
+		return;
+	}
+
+	for (l = ranges; l; l = l->next) {
+		struct att_range *range = l->data;
+		rl_printf("Starting handle: 0x%04x Ending handle: 0x%04x\n",
+						range->start, range->end);
+	}
+}
+
+static void included_cb(uint8_t status, GSList *includes, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		error("Find included services failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	if (includes == NULL) {
+		rl_printf("No included services found for this range\n");
+		return;
+	}
+
+	for (l = includes; l; l = l->next) {
+		struct gatt_included *incl = l->data;
+		rl_printf("handle: 0x%04x, start handle: 0x%04x, "
+					"end handle: 0x%04x uuid: %s\n",
+					incl->handle, incl->range.start,
+					incl->range.end, incl->uuid);
+	}
+}
+
+static void char_cb(uint8_t status, GSList *characteristics, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		error("Discover all characteristics failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (l = characteristics; l; l = l->next) {
+		struct gatt_char *chars = l->data;
+
+		rl_printf("handle: 0x%04x, char properties: 0x%02x, char value "
+				"handle: 0x%04x, uuid: %s\n", chars->handle,
+				chars->properties, chars->value_handle,
+				chars->uuid);
+	}
+}
+
+static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data)
+{
+	GSList *l;
+
+	if (status) {
+		error("Discover descriptors failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (l = descriptors; l; l = l->next) {
+		struct gatt_desc *desc = l->data;
+
+		rl_printf("handle: 0x%04x, uuid: %s\n", desc->handle,
+								desc->uuid);
+	}
+}
+
+static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	uint8_t value[plen];
+	ssize_t vlen;
+	int i;
+	GString *s;
+
+	if (status != 0) {
+		error("Characteristic value/descriptor read failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+	if (vlen < 0) {
+		error("Protocol error\n");
+		return;
+	}
+
+	s = g_string_new("Characteristic value/descriptor: ");
+	for (i = 0; i < vlen; i++)
+		g_string_append_printf(s, "%02x ", value[i]);
+
+	rl_printf("%s\n", s->str);
+	g_string_free(s, TRUE);
+}
+
+static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
+					guint16 plen, gpointer user_data)
+{
+	struct att_data_list *list;
+	int i;
+	GString *s;
+
+	if (status != 0) {
+		error("Read characteristics by UUID failed: %s\n",
+							att_ecode2str(status));
+		return;
+	}
+
+	list = dec_read_by_type_resp(pdu, plen);
+	if (list == NULL)
+		return;
+
+	s = g_string_new(NULL);
+	for (i = 0; i < list->num; i++) {
+		uint8_t *value = list->data[i];
+		int j;
+
+		g_string_printf(s, "handle: 0x%04x \t value: ",
+							get_le16(value));
+		value += 2;
+		for (j = 0; j < list->len - 2; j++, value++)
+			g_string_append_printf(s, "%02x ", *value);
+
+		rl_printf("%s\n", s->str);
+	}
+
+	att_data_list_free(list);
+	g_string_free(s, TRUE);
+}
+
+static void cmd_exit(int argcp, char **argvp)
+{
+	rl_callback_handler_remove();
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
+				gpointer user_data)
+{
+	disconnect_io();
+
+	return FALSE;
+}
+
+static void cmd_connect(int argcp, char **argvp)
+{
+	GError *gerr = NULL;
+
+	if (conn_state != STATE_DISCONNECTED)
+		return;
+
+	if (argcp > 1) {
+		g_free(opt_dst);
+		opt_dst = g_strdup(argvp[1]);
+
+		g_free(opt_dst_type);
+		if (argcp > 2)
+			opt_dst_type = g_strdup(argvp[2]);
+		else
+			opt_dst_type = g_strdup("public");
+	}
+
+	if (opt_dst == NULL) {
+		error("Remote Bluetooth address required\n");
+		return;
+	}
+
+	rl_printf("Attempting to connect to %s\n", opt_dst);
+	set_state(STATE_CONNECTING);
+	iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
+					opt_psm, opt_mtu, connect_cb, &gerr);
+	if (iochannel == NULL) {
+		set_state(STATE_DISCONNECTED);
+		error("%s\n", gerr->message);
+		g_error_free(gerr);
+	} else
+		g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL);
+}
+
+static void cmd_disconnect(int argcp, char **argvp)
+{
+	disconnect_io();
+}
+
+static void cmd_primary(int argcp, char **argvp)
+{
+	bt_uuid_t uuid;
+
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp == 1) {
+		gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+		return;
+	}
+
+	if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
+		error("Invalid UUID\n");
+		return;
+	}
+
+	gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL);
+}
+
+static int strtohandle(const char *src)
+{
+	char *e;
+	int dst;
+
+	errno = 0;
+	dst = strtoll(src, &e, 16);
+	if (errno != 0 || *e != '\0')
+		return -EINVAL;
+
+	return dst;
+}
+
+static void cmd_included(int argcp, char **argvp)
+{
+	int start = 0x0001;
+	int end = 0xffff;
+
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp > 1) {
+		start = strtohandle(argvp[1]);
+		if (start < 0) {
+			error("Invalid start handle: %s\n", argvp[1]);
+			return;
+		}
+		end = start;
+	}
+
+	if (argcp > 2) {
+		end = strtohandle(argvp[2]);
+		if (end < 0) {
+			error("Invalid end handle: %s\n", argvp[2]);
+			return;
+		}
+	}
+
+	gatt_find_included(attrib, start, end, included_cb, NULL);
+}
+
+static void cmd_char(int argcp, char **argvp)
+{
+	int start = 0x0001;
+	int end = 0xffff;
+
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp > 1) {
+		start = strtohandle(argvp[1]);
+		if (start < 0) {
+			error("Invalid start handle: %s\n", argvp[1]);
+			return;
+		}
+	}
+
+	if (argcp > 2) {
+		end = strtohandle(argvp[2]);
+		if (end < 0) {
+			error("Invalid end handle: %s\n", argvp[2]);
+			return;
+		}
+	}
+
+	if (argcp > 3) {
+		bt_uuid_t uuid;
+
+		if (bt_string_to_uuid(&uuid, argvp[3]) < 0) {
+			error("Invalid UUID\n");
+			return;
+		}
+
+		gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
+		return;
+	}
+
+	gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
+}
+
+static void cmd_char_desc(int argcp, char **argvp)
+{
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp > 1) {
+		start = strtohandle(argvp[1]);
+		if (start < 0) {
+			error("Invalid start handle: %s\n", argvp[1]);
+			return;
+		}
+	} else
+		start = 0x0001;
+
+	if (argcp > 2) {
+		end = strtohandle(argvp[2]);
+		if (end < 0) {
+			error("Invalid end handle: %s\n", argvp[2]);
+			return;
+		}
+	} else
+		end = 0xffff;
+
+	gatt_discover_desc(attrib, start, end, NULL, char_desc_cb, NULL);
+}
+
+static void cmd_read_hnd(int argcp, char **argvp)
+{
+	int handle;
+
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp < 2) {
+		error("Missing argument: handle\n");
+		return;
+	}
+
+	handle = strtohandle(argvp[1]);
+	if (handle < 0) {
+		error("Invalid handle: %s\n", argvp[1]);
+		return;
+	}
+
+	gatt_read_char(attrib, handle, char_read_cb, attrib);
+}
+
+static void cmd_read_uuid(int argcp, char **argvp)
+{
+	int start = 0x0001;
+	int end = 0xffff;
+	bt_uuid_t uuid;
+
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp < 2) {
+		error("Missing argument: UUID\n");
+		return;
+	}
+
+	if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
+		error("Invalid UUID\n");
+		return;
+	}
+
+	if (argcp > 2) {
+		start = strtohandle(argvp[2]);
+		if (start < 0) {
+			error("Invalid start handle: %s\n", argvp[1]);
+			return;
+		}
+	}
+
+	if (argcp > 3) {
+		end = strtohandle(argvp[3]);
+		if (end < 0) {
+			error("Invalid end handle: %s\n", argvp[2]);
+			return;
+		}
+	}
+
+	gatt_read_char_by_uuid(attrib, start, end, &uuid, char_read_by_uuid_cb,
+									NULL);
+}
+
+static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	if (status != 0) {
+		error("Characteristic Write Request failed: "
+						"%s\n", att_ecode2str(status));
+		return;
+	}
+
+	if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) {
+		error("Protocol error\n");
+		return;
+	}
+
+	rl_printf("Characteristic value was written successfully\n");
+}
+
+static void cmd_char_write(int argcp, char **argvp)
+{
+	uint8_t *value;
+	size_t plen;
+	int handle;
+
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (argcp < 3) {
+		rl_printf("Usage: %s <handle> <new value>\n", argvp[0]);
+		return;
+	}
+
+	handle = strtohandle(argvp[1]);
+	if (handle <= 0) {
+		error("A valid handle is required\n");
+		return;
+	}
+
+	plen = gatt_attr_data_from_string(argvp[2], &value);
+	if (plen == 0) {
+		error("Invalid value\n");
+		return;
+	}
+
+	if (g_strcmp0("char-write-req", argvp[0]) == 0)
+		gatt_write_char(attrib, handle, value, plen,
+					char_write_req_cb, NULL);
+	else
+		gatt_write_cmd(attrib, handle, value, plen, NULL, NULL);
+
+	g_free(value);
+}
+
+static void cmd_sec_level(int argcp, char **argvp)
+{
+	GError *gerr = NULL;
+	BtIOSecLevel sec_level;
+
+	if (argcp < 2) {
+		rl_printf("sec-level: %s\n", opt_sec_level);
+		return;
+	}
+
+	if (strcasecmp(argvp[1], "medium") == 0)
+		sec_level = BT_IO_SEC_MEDIUM;
+	else if (strcasecmp(argvp[1], "high") == 0)
+		sec_level = BT_IO_SEC_HIGH;
+	else if (strcasecmp(argvp[1], "low") == 0)
+		sec_level = BT_IO_SEC_LOW;
+	else {
+		rl_printf("Allowed values: low | medium | high\n");
+		return;
+	}
+
+	g_free(opt_sec_level);
+	opt_sec_level = g_strdup(argvp[1]);
+
+	if (conn_state != STATE_CONNECTED)
+		return;
+
+	if (opt_psm) {
+		rl_printf("Change will take effect on reconnection\n");
+		return;
+	}
+
+	bt_io_set(iochannel, &gerr,
+			BT_IO_OPT_SEC_LEVEL, sec_level,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s\n", gerr->message);
+		g_error_free(gerr);
+	}
+}
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	uint16_t mtu;
+
+	if (status != 0) {
+		error("Exchange MTU Request failed: %s\n",
+						att_ecode2str(status));
+		return;
+	}
+
+	if (!dec_mtu_resp(pdu, plen, &mtu)) {
+		error("Protocol error\n");
+		return;
+	}
+
+	mtu = MIN(mtu, opt_mtu);
+	/* Set new value for MTU in client */
+	if (g_attrib_set_mtu(attrib, mtu))
+		rl_printf("MTU was exchanged successfully: %d\n", mtu);
+	else
+		error("Error exchanging MTU\n");
+}
+
+static void cmd_mtu(int argcp, char **argvp)
+{
+	if (conn_state != STATE_CONNECTED) {
+		failed("Disconnected\n");
+		return;
+	}
+
+	if (opt_psm) {
+		failed("Operation is only available for LE transport.\n");
+		return;
+	}
+
+	if (argcp < 2) {
+		rl_printf("Usage: mtu <value>\n");
+		return;
+	}
+
+	if (opt_mtu) {
+		failed("MTU exchange can only occur once per connection.\n");
+		return;
+	}
+
+	errno = 0;
+	opt_mtu = strtoll(argvp[1], NULL, 0);
+	if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) {
+		error("Invalid value. Minimum MTU size is %d\n",
+							ATT_DEFAULT_LE_MTU);
+		return;
+	}
+
+	gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
+}
+
+static struct {
+	const char *cmd;
+	void (*func)(int argcp, char **argvp);
+	const char *params;
+	const char *desc;
+} commands[] = {
+	{ "help",		cmd_help,	"",
+		"Show this help"},
+	{ "exit",		cmd_exit,	"",
+		"Exit interactive mode" },
+	{ "quit",		cmd_exit,	"",
+		"Exit interactive mode" },
+	{ "connect",		cmd_connect,	"[address [address type]]",
+		"Connect to a remote device" },
+	{ "disconnect",		cmd_disconnect,	"",
+		"Disconnect from a remote device" },
+	{ "primary",		cmd_primary,	"[UUID]",
+		"Primary Service Discovery" },
+	{ "included",		cmd_included,	"[start hnd [end hnd]]",
+		"Find Included Services" },
+	{ "characteristics",	cmd_char,	"[start hnd [end hnd [UUID]]]",
+		"Characteristics Discovery" },
+	{ "char-desc",		cmd_char_desc,	"[start hnd] [end hnd]",
+		"Characteristics Descriptor Discovery" },
+	{ "char-read-hnd",	cmd_read_hnd,	"<handle>",
+		"Characteristics Value/Descriptor Read by handle" },
+	{ "char-read-uuid",	cmd_read_uuid,	"<UUID> [start hnd] [end hnd]",
+		"Characteristics Value/Descriptor Read by UUID" },
+	{ "char-write-req",	cmd_char_write,	"<handle> <new value>",
+		"Characteristic Value Write (Write Request)" },
+	{ "char-write-cmd",	cmd_char_write,	"<handle> <new value>",
+		"Characteristic Value Write (No response)" },
+	{ "sec-level",		cmd_sec_level,	"[low | medium | high]",
+		"Set security level. Default: low" },
+	{ "mtu",		cmd_mtu,	"<value>",
+		"Exchange MTU for GATT/ATT" },
+	{ NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+	int i;
+
+	for (i = 0; commands[i].cmd; i++)
+		rl_printf("%-15s %-30s %s\n", commands[i].cmd,
+				commands[i].params, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+	char **argvp;
+	int argcp;
+	int i;
+
+	if (line_read == NULL) {
+		rl_printf("\n");
+		cmd_exit(0, NULL);
+		return;
+	}
+
+	line_read = g_strstrip(line_read);
+
+	if (*line_read == '\0')
+		goto done;
+
+	add_history(line_read);
+
+	if (g_shell_parse_argv(line_read, &argcp, &argvp, NULL) == FALSE)
+		goto done;
+
+	for (i = 0; commands[i].cmd; i++)
+		if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+			break;
+
+	if (commands[i].cmd)
+		commands[i].func(argcp, argvp);
+	else
+		error("%s: command not found\n", argvp[0]);
+
+	g_strfreev(argvp);
+
+done:
+	free(line_read);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_io_channel_unref(chan);
+		return FALSE;
+	}
+
+	rl_callback_read_char();
+
+	return TRUE;
+}
+
+static char *completion_generator(const char *text, int state)
+{
+	static int index = 0, len = 0;
+	const char *cmd = NULL;
+
+	if (state == 0) {
+		index = 0;
+		len = strlen(text);
+	}
+
+	while ((cmd = commands[index].cmd) != NULL) {
+		index++;
+		if (strncmp(cmd, text, len) == 0)
+			return strdup(cmd);
+	}
+
+	return NULL;
+}
+
+static char **commands_completion(const char *text, int start, int end)
+{
+	if (start == 0)
+		return rl_completion_matches(text, &completion_generator);
+	else
+		return NULL;
+}
+
+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,
+				prompt_read, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+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(event_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(event_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;
+}
+
+int interactive(const char *src, const char *dst,
+		const char *dst_type, int psm)
+{
+	guint input;
+	guint signal;
+
+	opt_sec_level = g_strdup("low");
+
+	opt_src = g_strdup(src);
+	opt_dst = g_strdup(dst);
+	opt_dst_type = g_strdup(dst_type);
+	opt_psm = psm;
+
+	prompt = g_string_new(NULL);
+
+	event_loop = g_main_loop_new(NULL, FALSE);
+
+	input = setup_standard_input();
+	signal = setup_signalfd();
+
+	rl_attempted_completion_function = commands_completion;
+	rl_erase_empty_line = 1;
+	rl_callback_handler_install(get_prompt(), parse_line);
+
+	g_main_loop_run(event_loop);
+
+	rl_callback_handler_remove();
+	cmd_disconnect(0, NULL);
+	g_source_remove(input);
+	g_source_remove(signal);
+	g_main_loop_unref(event_loop);
+	g_string_free(prompt, TRUE);
+
+	g_free(opt_src);
+	g_free(opt_dst);
+	g_free(opt_sec_level);
+
+	return 0;
+}
diff --git a/bluez/repo/attrib/utils.c b/bluez/repo/attrib/utils.c
new file mode 100644
index 0000000..8e2fc1a
--- /dev/null
+++ b/bluez/repo/attrib/utils.c
@@ -0,0 +1,122 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *
+ *
+ *  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 <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "btio/btio.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+
+GIOChannel *gatt_connect(const char *src, const char *dst,
+				const char *dst_type, const char *sec_level,
+				int psm, int mtu, BtIOConnect connect_cb,
+				GError **gerr)
+{
+	GIOChannel *chan;
+	bdaddr_t sba, dba;
+	uint8_t dest_type;
+	GError *tmp_err = NULL;
+	BtIOSecLevel sec;
+
+	str2ba(dst, &dba);
+
+	/* Local adapter */
+	if (src != NULL) {
+		if (!strncmp(src, "hci", 3))
+			hci_devba(atoi(src + 3), &sba);
+		else
+			str2ba(src, &sba);
+	} else
+		bacpy(&sba, BDADDR_ANY);
+
+	/* Not used for BR/EDR */
+	if (strcmp(dst_type, "random") == 0)
+		dest_type = BDADDR_LE_RANDOM;
+	else
+		dest_type = BDADDR_LE_PUBLIC;
+
+	if (strcmp(sec_level, "medium") == 0)
+		sec = BT_IO_SEC_MEDIUM;
+	else if (strcmp(sec_level, "high") == 0)
+		sec = BT_IO_SEC_HIGH;
+	else
+		sec = BT_IO_SEC_LOW;
+
+	if (psm == 0)
+		chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err,
+				BT_IO_OPT_SOURCE_BDADDR, &sba,
+				BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+				BT_IO_OPT_DEST_BDADDR, &dba,
+				BT_IO_OPT_DEST_TYPE, dest_type,
+				BT_IO_OPT_CID, ATT_CID,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+	else
+		chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err,
+				BT_IO_OPT_SOURCE_BDADDR, &sba,
+				BT_IO_OPT_DEST_BDADDR, &dba,
+				BT_IO_OPT_PSM, psm,
+				BT_IO_OPT_IMTU, mtu,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+
+	if (tmp_err) {
+		g_propagate_error(gerr, tmp_err);
+		return NULL;
+	}
+
+	return chan;
+}
+
+size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
+{
+	char tmp[3];
+	size_t size, i;
+
+	size = strlen(str) / 2;
+	*data = g_try_malloc0(size);
+	if (*data == NULL)
+		return 0;
+
+	tmp[2] = '\0';
+	for (i = 0; i < size; i++) {
+		memcpy(tmp, str + (i * 2), 2);
+		(*data)[i] = (uint8_t) strtol(tmp, NULL, 16);
+	}
+
+	return size;
+}
diff --git a/bluez/repo/bootstrap b/bluez/repo/bootstrap
new file mode 100755
index 0000000..91756f9
--- /dev/null
+++ b/bluez/repo/bootstrap
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+aclocal && \
+    autoheader && \
+	libtoolize --automake --copy --force && \
+	    automake --add-missing --copy && \
+		autoconf
diff --git a/bluez/repo/bootstrap-configure b/bluez/repo/bootstrap-configure
new file mode 100755
index 0000000..87766b1
--- /dev/null
+++ b/bluez/repo/bootstrap-configure
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+if [ -f config.status ]; then
+	make maintainer-clean
+fi
+
+./bootstrap && \
+    ./configure --enable-maintainer-mode \
+		--enable-debug \
+		--prefix=/usr \
+		--mandir=/usr/share/man \
+		--sysconfdir=/etc \
+		--localstatedir=/var \
+		--enable-manpages \
+		--enable-backtrace \
+		--enable-experimental \
+		--enable-android \
+		--enable-sixaxis \
+		--disable-datafiles $*
diff --git a/bluez/repo/btio/._btio.c b/bluez/repo/btio/._btio.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/btio/._btio.c
Binary files differ
diff --git a/bluez/repo/btio/._btio.h b/bluez/repo/btio/._btio.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/btio/._btio.h
Binary files differ
diff --git a/bluez/repo/btio/btio.c b/bluez/repo/btio/btio.c
new file mode 100644
index 0000000..969cd3a
--- /dev/null
+++ b/bluez/repo/btio/btio.c
@@ -0,0 +1,1682 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  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 <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/rfcomm.h"
+#include "lib/sco.h"
+
+#include "btio.h"
+
+#ifndef BT_FLUSHABLE
+#define BT_FLUSHABLE	8
+#endif
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, err, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+typedef enum {
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+	BT_IO_INVALID,
+} BtIOType;
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	BtIOType type;
+	uint8_t src_type;
+	uint8_t dst_type;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t cid;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+	int flushable;
+	uint32_t priority;
+	uint16_t voice;
+};
+
+struct connect {
+	BtIOConnect connect;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+struct accept {
+	BtIOConnect connect;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+struct server {
+	BtIOConnect connect;
+	BtIOConfirm confirm;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+static BtIOType bt_io_get_type(GIOChannel *io, GError **gerr)
+{
+	int sk = g_io_channel_unix_get_fd(io);
+	int domain, proto, err;
+	socklen_t len;
+
+	domain = 0;
+	len = sizeof(domain);
+	err = getsockopt(sk, SOL_SOCKET, SO_DOMAIN, &domain, &len);
+	if (err < 0) {
+		ERROR_FAILED(gerr, "getsockopt(SO_DOMAIN)", errno);
+		return BT_IO_INVALID;
+	}
+
+	if (domain != AF_BLUETOOTH) {
+		g_set_error(gerr, BT_IO_ERROR, EINVAL,
+				"BtIO socket domain not AF_BLUETOOTH");
+		return BT_IO_INVALID;
+	}
+
+	proto = 0;
+	len = sizeof(proto);
+	err = getsockopt(sk, SOL_SOCKET, SO_PROTOCOL, &proto, &len);
+	if (err < 0) {
+		ERROR_FAILED(gerr, "getsockopt(SO_PROTOCOL)", errno);
+		return BT_IO_INVALID;
+	}
+
+	switch (proto) {
+	case BTPROTO_RFCOMM:
+		return BT_IO_RFCOMM;
+	case BTPROTO_SCO:
+		return BT_IO_SCO;
+	case BTPROTO_L2CAP:
+		return BT_IO_L2CAP;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, EINVAL,
+					"Unknown BtIO socket type");
+		return BT_IO_INVALID;
+	}
+}
+
+static void server_remove(struct server *server)
+{
+	if (server->destroy)
+		server->destroy(server->user_data);
+	g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+	if (conn->destroy)
+		conn->destroy(conn->user_data);
+	g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+	if (accept->destroy)
+		accept->destroy(accept->user_data);
+	g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct accept *accept = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR)) {
+		int err, sk_err, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(sk_err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+			err = -errno;
+		else
+			err = -sk_err;
+
+		if (err < 0)
+			ERROR_FAILED(&gerr, "HUP or ERR on socket", -err);
+	}
+
+	accept->connect(io, gerr, accept->user_data);
+
+	g_clear_error(&gerr);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+	int err, sk_err, sock;
+	socklen_t len = sizeof(sk_err);
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+		err = -errno;
+	else
+		err = -sk_err;
+
+	if (err < 0)
+		ERROR_FAILED(&gerr, "connect error", -err);
+
+	conn->connect(io, gerr, conn->user_data);
+
+	g_clear_error(&gerr);
+
+	return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct server *server = user_data;
+	int srv_sock, cli_sock;
+	GIOChannel *cli_io;
+
+	/* If the user closed the server */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, server->user_data);
+
+	g_io_channel_unref(cli_io);
+
+	return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy)
+{
+	struct server *server;
+	GIOCondition cond;
+
+	server = g_new0(struct server, 1);
+	server->connect = connect;
+	server->confirm = confirm;
+	server->user_data = user_data;
+	server->destroy = destroy;
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+					(GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy)
+{
+	struct connect *conn;
+	GIOCondition cond;
+
+	conn = g_new0(struct connect, 1);
+	conn->connect = connect;
+	conn->user_data = user_data;
+	conn->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+					(GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+							GDestroyNotify destroy)
+{
+	struct accept *accept;
+	GIOCondition cond;
+
+	accept = g_new0(struct accept, 1);
+	accept->connect = connect;
+	accept->user_data = user_data;
+	accept->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+					(GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint8_t src_type,
+				uint16_t psm, uint16_t cid, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+
+	if (cid)
+		addr.l2_cid = htobs(cid);
+	else
+		addr.l2_psm = htobs(psm);
+
+	addr.l2_bdaddr_type = src_type;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		int error = -errno;
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return error;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst, uint8_t dst_type,
+						uint16_t psm, uint16_t cid)
+{
+	int err;
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	if (cid)
+		addr.l2_cid = htobs(cid);
+	else
+		addr.l2_psm = htobs(psm);
+
+	addr.l2_bdaddr_type = dst_type;
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, EINVAL,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_set_flushable(int sock, gboolean flushable)
+{
+	int f;
+
+	f = flushable;
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, sizeof(f)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int set_priority(int sock, uint32_t prio)
+{
+	if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean get_key_size(int sock, int *size, GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*size = sec.key_size;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean set_l2opts(int sock, uint16_t imtu, uint16_t omtu,
+						uint8_t mode, GError **err)
+{
+	struct l2cap_options l2o;
+	socklen_t len;
+
+	memset(&l2o, 0, sizeof(l2o));
+	len = sizeof(l2o);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (imtu)
+		l2o.imtu = imtu;
+	if (omtu)
+		l2o.omtu = omtu;
+	if (mode)
+		l2o.mode = mode;
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+		ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err)
+{
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, &imtu,
+							sizeof(imtu)) < 0) {
+		ERROR_FAILED(err, "setsockopt(BT_RCVMTU)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level,
+				uint16_t imtu, uint16_t omtu, uint8_t mode,
+				int master, int flushable, uint32_t priority,
+				GError **err)
+{
+	if (imtu || omtu || mode) {
+		gboolean ret;
+
+		if (src_type == BDADDR_BREDR)
+			ret = set_l2opts(sock, imtu, omtu, mode, err);
+		else
+			ret = set_le_imtu(sock, imtu, err);
+
+		if (!ret)
+			return ret;
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (flushable >= 0 && l2cap_set_flushable(sock, flushable) < 0) {
+		ERROR_FAILED(err, "l2cap_set_flushable", errno);
+		return FALSE;
+	}
+
+	if (priority > 0 && set_priority(sock, priority) < 0) {
+		ERROR_FAILED(err, "set_priority", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		int error = -errno;
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return error;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, dst);
+	addr.rc_channel = channel;
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return -errno;
+
+	return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		int error = -errno;
+		ERROR_FAILED(err, "sco_bind", errno);
+		return error;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+	struct sockaddr_sco addr;
+	int err;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, dst);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return -errno;
+
+	return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, uint16_t voice, GError **err)
+{
+	struct sco_options sco_opt;
+	struct bt_voice bt_voice;
+	socklen_t len;
+
+	if (!mtu)
+		goto voice;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+voice:
+	if (!voice)
+		return TRUE;
+
+	memset(&bt_voice, 0, sizeof(bt_voice));
+	bt_voice.setting = voice;
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_VOICE, &bt_voice,
+						sizeof(bt_voice)) < 0) {
+		ERROR_FAILED(err, "setsockopt(BT_VOICE)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->type = BT_IO_SCO;
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->mode = L2CAP_MODE_BASIC;
+	opts->flushable = -1;
+	opts->priority = 0;
+	opts->src_type = BDADDR_BREDR;
+	opts->dst_type = BDADDR_BREDR;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			str = va_arg(args, const char *);
+			str2ba(str, &opts->src);
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_SOURCE_TYPE:
+			opts->src_type = va_arg(args, int);
+			break;
+		case BT_IO_OPT_DEST:
+			str2ba(va_arg(args, const char *), &opts->dst);
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEST_TYPE:
+			opts->dst_type = va_arg(args, int);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = va_arg(args, int);
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			opts->sec_level = va_arg(args, int);
+			break;
+		case BT_IO_OPT_CHANNEL:
+			opts->type = BT_IO_RFCOMM;
+			opts->channel = va_arg(args, int);
+			break;
+		case BT_IO_OPT_PSM:
+			opts->type = BT_IO_L2CAP;
+			opts->psm = va_arg(args, int);
+			break;
+		case BT_IO_OPT_CID:
+			opts->type = BT_IO_L2CAP;
+			opts->cid = va_arg(args, int);
+			break;
+		case BT_IO_OPT_MTU:
+			opts->mtu = va_arg(args, int);
+			opts->imtu = opts->mtu;
+			opts->omtu = opts->mtu;
+			break;
+		case BT_IO_OPT_OMTU:
+			opts->omtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			opts->imtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		case BT_IO_OPT_FLUSHABLE:
+			opts->flushable = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_PRIORITY:
+			opts->priority = va_arg(args, int);
+			break;
+		case BT_IO_OPT_VOICE:
+			opts->voice = va_arg(args, int);
+			break;
+		case BT_IO_OPT_INVALID:
+		case BT_IO_OPT_KEY_SIZE:
+		case BT_IO_OPT_SOURCE_CHANNEL:
+		case BT_IO_OPT_DEST_CHANNEL:
+		case BT_IO_OPT_HANDLE:
+		case BT_IO_OPT_CLASS:
+		default:
+			g_set_error(err, BT_IO_ERROR, EINVAL,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_src(int sock, void *src, socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean get_dst(int sock, void *dst, socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static int l2cap_get_flushable(int sock, gboolean *flushable)
+{
+	int f;
+	socklen_t len;
+
+	f = 0;
+	len = sizeof(f);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, &len) < 0)
+		return -errno;
+
+	if (f)
+		*flushable = TRUE;
+	else
+		*flushable = FALSE;
+
+	return 0;
+}
+
+static int get_priority(int sock, uint32_t *prio)
+{
+	socklen_t len;
+
+	len = sizeof(*prio);
+	if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle = 0;
+	socklen_t len;
+	gboolean flushable = FALSE, have_dst = FALSE;
+	uint32_t priority;
+
+	if (!get_src(sock, &src, sizeof(src), err))
+		return FALSE;
+
+	memset(&l2o, 0, sizeof(l2o));
+
+	if (src.l2_bdaddr_type != BDADDR_BREDR) {
+		len = sizeof(l2o.imtu);
+		if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU,
+						&l2o.imtu, &len) == 0)
+			goto parse_opts;
+
+		/* Non-LE CoC enabled kernels will return one of these
+		 * in which case we need to fall back to L2CAP_OPTIONS.
+		 */
+		if (errno != EPROTONOSUPPORT && errno != ENOPROTOOPT) {
+			ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", errno);
+			return FALSE;
+		}
+	}
+
+	len = sizeof(l2o);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+parse_opts:
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEST_TYPE:
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+			*(va_arg(args, uint8_t *)) = dst.l2_bdaddr_type;
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_KEY_SIZE:
+			if (!get_key_size(sock, va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			if (src.l2_psm) {
+				*(va_arg(args, uint16_t *)) = btohs(src.l2_psm);
+				break;
+			}
+
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+
+			*(va_arg(args, uint16_t *)) = btohs(dst.l2_psm);
+			break;
+		case BT_IO_OPT_CID:
+			if (src.l2_cid) {
+				*(va_arg(args, uint16_t *)) = btohs(src.l2_cid);
+				break;
+			}
+
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+
+			*(va_arg(args, uint16_t *)) = btohs(dst.l2_cid);
+			break;
+		case BT_IO_OPT_OMTU:
+			if (src.l2_bdaddr_type == BDADDR_BREDR) {
+				*(va_arg(args, uint16_t *)) = l2o.omtu;
+				break;
+			}
+
+			len = sizeof(l2o.omtu);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU,
+							&l2o.omtu, &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(BT_SNDMTU)",
+									errno);
+				return FALSE;
+			}
+
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		case BT_IO_OPT_MODE:
+			*(va_arg(args, uint8_t *)) = l2o.mode;
+			break;
+		case BT_IO_OPT_FLUSHABLE:
+			if (l2cap_get_flushable(sock, &flushable) < 0) {
+				ERROR_FAILED(err, "get_flushable", errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) = flushable;
+			break;
+		case BT_IO_OPT_PRIORITY:
+			if (get_priority(sock, &priority) < 0) {
+				ERROR_FAILED(err, "get_priority", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint32_t *)) = priority;
+			break;
+		case BT_IO_OPT_INVALID:
+		case BT_IO_OPT_SOURCE_TYPE:
+		case BT_IO_OPT_CHANNEL:
+		case BT_IO_OPT_SOURCE_CHANNEL:
+		case BT_IO_OPT_DEST_CHANNEL:
+		case BT_IO_OPT_MTU:
+		case BT_IO_OPT_VOICE:
+		default:
+			g_set_error(err, BT_IO_ERROR, EINVAL,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	gboolean have_dst = FALSE;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle = 0;
+
+	if (!get_src(sock, &src, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			if (src.rc_channel) {
+				*(va_arg(args, uint8_t *)) = src.rc_channel;
+				break;
+			}
+
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			if (!have_dst)
+				have_dst = get_dst(sock, &dst, sizeof(dst),
+									err);
+			if (!have_dst)
+				return FALSE;
+
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		case BT_IO_OPT_SOURCE_TYPE:
+		case BT_IO_OPT_DEST_TYPE:
+		case BT_IO_OPT_KEY_SIZE:
+		case BT_IO_OPT_PSM:
+		case BT_IO_OPT_CID:
+		case BT_IO_OPT_MTU:
+		case BT_IO_OPT_OMTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_MODE:
+		case BT_IO_OPT_FLUSHABLE:
+		case BT_IO_OPT_PRIORITY:
+		case BT_IO_OPT_VOICE:
+		case BT_IO_OPT_INVALID:
+		default:
+			g_set_error(err, BT_IO_ERROR, EINVAL,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle = 0;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_src(sock, &src, sizeof(src), err))
+		return FALSE;
+
+	if (!get_dst(sock, &dst, sizeof(dst), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+			break;
+		case BT_IO_OPT_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "SCO_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "SCO_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		case BT_IO_OPT_SOURCE_TYPE:
+		case BT_IO_OPT_DEST_TYPE:
+		case BT_IO_OPT_DEFER_TIMEOUT:
+		case BT_IO_OPT_SEC_LEVEL:
+		case BT_IO_OPT_KEY_SIZE:
+		case BT_IO_OPT_CHANNEL:
+		case BT_IO_OPT_SOURCE_CHANNEL:
+		case BT_IO_OPT_DEST_CHANNEL:
+		case BT_IO_OPT_PSM:
+		case BT_IO_OPT_CID:
+		case BT_IO_OPT_MASTER:
+		case BT_IO_OPT_MODE:
+		case BT_IO_OPT_FLUSHABLE:
+		case BT_IO_OPT_PRIORITY:
+		case BT_IO_OPT_VOICE:
+		case BT_IO_OPT_INVALID:
+		default:
+			g_set_error(err, BT_IO_ERROR, EINVAL,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	int sock;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2CAP:
+		return l2cap_get(sock, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	case BT_IO_INVALID:
+	default:
+		g_set_error(err, BT_IO_ERROR, EINVAL,
+				"Unknown BtIO type %d", type);
+		return FALSE;
+	}
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	int sock;
+	char c;
+	struct pollfd pfd;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	memset(&pfd, 0, sizeof(pfd));
+	pfd.fd = sock;
+	pfd.events = POLLOUT;
+
+	if (poll(&pfd, 1, 0) < 0) {
+		ERROR_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		if (read(sock, &c, 1) < 0) {
+			ERROR_FAILED(err, "read", errno);
+			return FALSE;
+		}
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+	BtIOType type;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (!ret)
+		return ret;
+
+	type = bt_io_get_type(io, err);
+	if (type == BT_IO_INVALID)
+		return FALSE;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2CAP:
+		return l2cap_set(sock, opts.src_type, opts.sec_level, opts.imtu,
+					opts.omtu, opts.mode, opts.master,
+					opts.flushable, opts.priority, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, opts.voice, err);
+	case BT_IO_INVALID:
+	default:
+		g_set_error(err, BT_IO_ERROR, EINVAL,
+				"Unknown BtIO type %d", type);
+		return FALSE;
+	}
+
+}
+
+gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	BtIOType type;
+
+	type = bt_io_get_type(io, err);
+	if (type == BT_IO_INVALID)
+		return FALSE;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(gboolean server, struct set_opts *opts,
+								GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (opts->type) {
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src, opts->src_type,
+				server ? opts->psm : 0, opts->cid, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->src_type, opts->sec_level,
+				opts->imtu, opts->omtu, opts->mode,
+				opts->master, opts->flushable, opts->priority,
+				err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, opts->voice, err))
+			goto failed;
+		break;
+	case BT_IO_INVALID:
+	default:
+		g_set_error(err, BT_IO_ERROR, EINVAL,
+				"Unknown BtIO type %d", opts->type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data,
+				GDestroyNotify destroy, GError **gerr,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(FALSE, &opts, gerr);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (opts.type) {
+	case BT_IO_L2CAP:
+		err = l2cap_connect(sock, &opts.dst, opts.dst_type,
+							opts.psm, opts.cid);
+		break;
+	case BT_IO_RFCOMM:
+		err = rfcomm_connect(sock, &opts.dst, opts.channel);
+		break;
+	case BT_IO_SCO:
+		err = sco_connect(sock, &opts.dst);
+		break;
+	case BT_IO_INVALID:
+	default:
+		g_set_error(gerr, BT_IO_ERROR, EINVAL,
+					"Unknown BtIO type %d", opts.type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		ERROR_FAILED(gerr, "connect", -err);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	connect_add(io, connect, user_data, destroy);
+
+	return io;
+}
+
+GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(TRUE, &opts, err);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	if (confirm)
+		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+							sizeof(opts.defer));
+
+	if (listen(sock, 5) < 0) {
+		ERROR_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/bluez/repo/btio/btio.h b/bluez/repo/btio/btio.h
new file mode 100644
index 0000000..2dce9f0
--- /dev/null
+++ b/bluez/repo/btio/btio.h
@@ -0,0 +1,95 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  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 BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	BT_IO_OPT_SOURCE,
+	BT_IO_OPT_SOURCE_BDADDR,
+	BT_IO_OPT_SOURCE_TYPE,
+	BT_IO_OPT_DEST,
+	BT_IO_OPT_DEST_BDADDR,
+	BT_IO_OPT_DEST_TYPE,
+	BT_IO_OPT_DEFER_TIMEOUT,
+	BT_IO_OPT_SEC_LEVEL,
+	BT_IO_OPT_KEY_SIZE,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_CID,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+	BT_IO_OPT_FLUSHABLE,
+	BT_IO_OPT_PRIORITY,
+	BT_IO_OPT_VOICE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef enum {
+	BT_IO_MODE_BASIC = 0,
+	BT_IO_MODE_RETRANS,
+	BT_IO_MODE_FLOWCTL,
+	BT_IO_MODE_ERTM,
+	BT_IO_MODE_STREAMING
+} BtIOMode;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data,
+				GDestroyNotify destroy, GError **gerr,
+				BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+#endif
diff --git a/bluez/repo/client/._agent.c b/bluez/repo/client/._agent.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._agent.c
Binary files differ
diff --git a/bluez/repo/client/._agent.h b/bluez/repo/client/._agent.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._agent.h
Binary files differ
diff --git a/bluez/repo/client/._display.c b/bluez/repo/client/._display.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._display.c
Binary files differ
diff --git a/bluez/repo/client/._display.h b/bluez/repo/client/._display.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._display.h
Binary files differ
diff --git a/bluez/repo/client/._gatt.c b/bluez/repo/client/._gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._gatt.c
Binary files differ
diff --git a/bluez/repo/client/._gatt.h b/bluez/repo/client/._gatt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._gatt.h
Binary files differ
diff --git a/bluez/repo/client/._main.c b/bluez/repo/client/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/client/._main.c
Binary files differ
diff --git a/bluez/repo/client/agent.c b/bluez/repo/client/agent.c
new file mode 100644
index 0000000..2cbc292
--- /dev/null
+++ b/bluez/repo/client/agent.c
@@ -0,0 +1,492 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <readline/readline.h>
+
+#include "gdbus/gdbus.h"
+#include "display.h"
+#include "agent.h"
+
+#define AGENT_PATH "/org/bluez/agent"
+#define AGENT_INTERFACE "org.bluez.Agent1"
+
+#define AGENT_PROMPT	COLOR_RED "[agent]" COLOR_OFF " "
+
+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)
+		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;
+}
+
+dbus_bool_t agent_completion(void)
+{
+	if (!pending_message)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void pincode_response(DBusConnection *conn, const char *input)
+{
+	g_dbus_send_reply(conn, pending_message, DBUS_TYPE_STRING, &input,
+							DBUS_TYPE_INVALID);
+}
+
+static void passkey_response(DBusConnection *conn, const char *input)
+{
+	dbus_uint32_t passkey;
+	if (sscanf(input, "%u", &passkey) == 1)
+		g_dbus_send_reply(conn, pending_message, DBUS_TYPE_UINT32,
+						&passkey, DBUS_TYPE_INVALID);
+	else if (!strcmp(input, "no"))
+		g_dbus_send_error(conn, pending_message,
+					"org.bluez.Error.Rejected", NULL);
+	else
+		g_dbus_send_error(conn, pending_message,
+					"org.bluez.Error.Canceled", NULL);
+}
+
+static void confirm_response(DBusConnection *conn, const char *input)
+{
+	if (!strcmp(input, "yes"))
+		g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID);
+	else if (!strcmp(input, "no"))
+		g_dbus_send_error(conn, pending_message,
+					"org.bluez.Error.Rejected", NULL);
+	else
+		g_dbus_send_error(conn, pending_message,
+					"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;
+	agent_capability = NULL;
+
+	if (pending_message) {
+		dbus_message_unref(pending_message);
+		pending_message = NULL;
+	}
+
+	agent_release_prompt();
+
+	g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE);
+}
+
+static DBusMessage *release_agent(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	rl_printf("Agent released\n");
+
+	agent_release(conn);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *request_pincode(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+
+	rl_printf("Request PIN code\n");
+
+	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
+							DBUS_TYPE_INVALID);
+
+	agent_prompt("Enter PIN code: ");
+
+	pending_message = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *display_pincode(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+	const char *pincode;
+
+	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);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *request_passkey(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+
+	rl_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): ");
+
+	pending_message = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *display_passkey(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+	dbus_uint32_t passkey;
+	dbus_uint16_t entered;
+	char passkey_full[7];
+
+	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
+			DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_UINT16, &entered,
+							DBUS_TYPE_INVALID);
+
+	snprintf(passkey_full, sizeof(passkey_full), "%.6u", passkey);
+	passkey_full[6] = '\0';
+
+	if (entered > strlen(passkey_full))
+		entered = strlen(passkey_full);
+
+	rl_printf(AGENT_PROMPT "Passkey: "
+			COLOR_BOLDGRAY "%.*s" COLOR_BOLDWHITE "%s\n" COLOR_OFF,
+				entered, passkey_full, passkey_full + entered);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *request_confirmation(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+	dbus_uint32_t passkey;
+	char *str;
+
+	rl_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);
+	g_free(str);
+
+	pending_message = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *request_authorization(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device;
+
+	rl_printf("Request authorization\n");
+
+	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
+							DBUS_TYPE_INVALID);
+
+	agent_prompt("Accept pairing (yes/no): ");
+
+	pending_message = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *authorize_service(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *device, *uuid;
+	char *str;
+
+	rl_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);
+	g_free(str);
+
+	pending_message = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *cancel_request(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	rl_printf("Request canceled\n");
+
+	agent_release_prompt();
+	dbus_message_unref(pending_message);
+	pending_message = NULL;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_METHOD("Release", NULL, NULL, release_agent) },
+	{ GDBUS_ASYNC_METHOD("RequestPinCode",
+			GDBUS_ARGS({ "device", "o" }),
+			GDBUS_ARGS({ "pincode", "s" }), request_pincode) },
+	{ GDBUS_METHOD("DisplayPinCode",
+			GDBUS_ARGS({ "device", "o" }, { "pincode", "s" }),
+			NULL, display_pincode) },
+	{ GDBUS_ASYNC_METHOD("RequestPasskey",
+			GDBUS_ARGS({ "device", "o" }),
+			GDBUS_ARGS({ "passkey", "u" }), request_passkey) },
+	{ GDBUS_METHOD("DisplayPasskey",
+			GDBUS_ARGS({ "device", "o" }, { "passkey", "u" },
+							{ "entered", "q" }),
+			NULL, display_passkey) },
+	{ GDBUS_ASYNC_METHOD("RequestConfirmation",
+			GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }),
+			NULL, request_confirmation) },
+	{ GDBUS_ASYNC_METHOD("RequestAuthorization",
+			GDBUS_ARGS({ "device", "o" }),
+			NULL, request_authorization) },
+	{ GDBUS_ASYNC_METHOD("AuthorizeService",
+			GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
+			NULL,  authorize_service) },
+	{ GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
+	{ }
+};
+
+static void register_agent_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = AGENT_PATH;
+	const char *capability = agent_capability;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &capability);
+}
+
+static void register_agent_reply(DBusMessage *message, void *user_data)
+{
+	DBusConnection *conn = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == FALSE) {
+		agent_registered = TRUE;
+		rl_printf("Agent registered\n");
+	} else {
+		rl_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");
+	}
+}
+
+void agent_register(DBusConnection *conn, GDBusProxy *manager,
+						const char *capability)
+
+{
+	if (agent_registered == TRUE) {
+		rl_printf("Agent is already registered\n");
+		return;
+	}
+
+	agent_capability = capability;
+
+	if (g_dbus_register_interface(conn, AGENT_PATH,
+					AGENT_INTERFACE, methods,
+					NULL, NULL, NULL, NULL) == FALSE) {
+		rl_printf("Failed to register agent object\n");
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(manager, "RegisterAgent",
+						register_agent_setup,
+						register_agent_reply,
+						conn, NULL) == FALSE) {
+		rl_printf("Failed to call register agent method\n");
+		return;
+	}
+
+	agent_capability = NULL;
+}
+
+static void unregister_agent_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = AGENT_PATH;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void unregister_agent_reply(DBusMessage *message, void *user_data)
+{
+	DBusConnection *conn = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == FALSE) {
+		rl_printf("Agent unregistered\n");
+		agent_release(conn);
+	} else {
+		rl_printf("Failed to unregister agent: %s\n", error.name);
+		dbus_error_free(&error);
+	}
+}
+
+void agent_unregister(DBusConnection *conn, GDBusProxy *manager)
+{
+	if (agent_registered == FALSE) {
+		rl_printf("No agent is registered\n");
+		return;
+	}
+
+	if (!manager) {
+		rl_printf("Agent unregistered\n");
+		agent_release(conn);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(manager, "UnregisterAgent",
+						unregister_agent_setup,
+						unregister_agent_reply,
+						conn, NULL) == FALSE) {
+		rl_printf("Failed to call unregister agent method\n");
+		return;
+	}
+}
+
+static void request_default_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = AGENT_PATH;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void request_default_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 request default agent: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Default agent request successful\n");
+}
+
+void agent_default(DBusConnection *conn, GDBusProxy *manager)
+{
+	if (agent_registered == FALSE) {
+		rl_printf("No agent is registered\n");
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(manager, "RequestDefaultAgent",
+						request_default_setup,
+						request_default_reply,
+						NULL, NULL) == FALSE) {
+		rl_printf("Failed to call request default agent method\n");
+		return;
+	}
+}
diff --git a/bluez/repo/client/agent.h b/bluez/repo/client/agent.h
new file mode 100644
index 0000000..0fbe8e5
--- /dev/null
+++ b/bluez/repo/client/agent.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void agent_register(DBusConnection *conn, GDBusProxy *manager,
+						const char *capability);
+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);
diff --git a/bluez/repo/client/display.c b/bluez/repo/client/display.c
new file mode 100644
index 0000000..d85b9d0
--- /dev/null
+++ b/bluez/repo/client/display.c
@@ -0,0 +1,106 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <readline/readline.h>
+
+#include "display.h"
+
+void rl_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);
+		rl_save_prompt();
+		rl_replace_line("", 0);
+		rl_redisplay();
+	}
+
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+
+	if (save_input) {
+		rl_restore_prompt();
+		rl_replace_line(saved_line, 0);
+		rl_point = saved_point;
+		rl_forced_update_display();
+		free(saved_line);
+	}
+}
+
+void rl_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';
+			rl_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';
+		rl_printf("%s\n", str);
+	}
+}
diff --git a/bluez/repo/client/display.h b/bluez/repo/client/display.h
new file mode 100644
index 0000000..88dbbd0
--- /dev/null
+++ b/bluez/repo/client/display.h
@@ -0,0 +1,33 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#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"
+
+void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void rl_hexdump(const unsigned char *buf, size_t len);
diff --git a/bluez/repo/client/gatt.c b/bluez/repo/client/gatt.c
new file mode 100644
index 0000000..7dd3c94
--- /dev/null
+++ b/bluez/repo/client/gatt.c
@@ -0,0 +1,675 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <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 "gdbus/gdbus.h"
+#include "monitor/uuid.h"
+#include "display.h"
+#include "gatt.h"
+
+#define PROFILE_PATH "/org/bluez/profile"
+#define PROFILE_INTERFACE "org.bluez.GattProfile1"
+
+/* 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
+
+static GList *services;
+static GList *characteristics;
+static GList *descriptors;
+static GList *managers;
+
+static void print_service(GDBusProxy *proxy, const char *description)
+{
+	DBusMessageIter iter;
+	const char *uuid, *text;
+	dbus_bool_t primary;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &primary);
+
+	text = uuidstr_to_str(uuid);
+	if (!text)
+		text = uuid;
+
+	rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
+				description ? "[" : "",
+				description ? : "",
+				description ? "] " : "",
+				primary ? "Primary" : "Secondary",
+				g_dbus_proxy_get_path(proxy),
+				text);
+}
+
+void gatt_add_service(GDBusProxy *proxy)
+{
+	services = g_list_append(services, proxy);
+
+	print_service(proxy, COLORED_NEW);
+}
+
+void gatt_remove_service(GDBusProxy *proxy)
+{
+	GList *l;
+
+	l = g_list_find(services, proxy);
+	if (!l)
+		return;
+
+	services = g_list_delete_link(services, l);
+
+	print_service(proxy, COLORED_DEL);
+}
+
+static void print_characteristic(GDBusProxy *proxy, 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);
+
+	text = uuidstr_to_str(uuid);
+	if (!text)
+		text = uuid;
+
+	rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
+				description ? "[" : "",
+				description ? : "",
+				description ? "] " : "",
+				g_dbus_proxy_get_path(proxy),
+				text);
+}
+
+static gboolean characteristic_is_child(GDBusProxy *characteristic)
+{
+	GList *l;
+	DBusMessageIter iter;
+	const char *service, *path;
+
+	if (!g_dbus_proxy_get_property(characteristic, "Service", &iter))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&iter, &service);
+
+	for (l = services; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		path = g_dbus_proxy_get_path(proxy);
+
+		if (!strcmp(path, service))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+void gatt_add_characteristic(GDBusProxy *proxy)
+{
+	if (!characteristic_is_child(proxy))
+		return;
+
+	characteristics = g_list_append(characteristics, proxy);
+
+	print_characteristic(proxy, COLORED_NEW);
+}
+
+void gatt_remove_characteristic(GDBusProxy *proxy)
+{
+	GList *l;
+
+	l = g_list_find(characteristics, proxy);
+	if (!l)
+		return;
+
+	characteristics = g_list_delete_link(characteristics, l);
+
+	print_characteristic(proxy, COLORED_DEL);
+}
+
+static void print_descriptor(GDBusProxy *proxy, 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);
+
+	text = uuidstr_to_str(uuid);
+	if (!text)
+		text = uuid;
+
+	rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
+				description ? "[" : "",
+				description ? : "",
+				description ? "] " : "",
+				g_dbus_proxy_get_path(proxy),
+				text);
+}
+
+static gboolean descriptor_is_child(GDBusProxy *characteristic)
+{
+	GList *l;
+	DBusMessageIter iter;
+	const char *service, *path;
+
+	if (!g_dbus_proxy_get_property(characteristic, "Characteristic", &iter))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&iter, &service);
+
+	for (l = characteristics; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		path = g_dbus_proxy_get_path(proxy);
+
+		if (!strcmp(path, service))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+void gatt_add_descriptor(GDBusProxy *proxy)
+{
+	if (!descriptor_is_child(proxy))
+		return;
+
+	descriptors = g_list_append(descriptors, proxy);
+
+	print_descriptor(proxy, COLORED_NEW);
+}
+
+void gatt_remove_descriptor(GDBusProxy *proxy)
+{
+	GList *l;
+
+	l = g_list_find(descriptors, proxy);
+	if (!l)
+		return;
+
+	descriptors = g_list_delete_link(descriptors, l);
+
+	print_descriptor(proxy, COLORED_DEL);
+}
+
+static void list_attributes(const char *path, GList *source)
+{
+	GList *l;
+
+	for (l = source; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+		const char *proxy_path;
+
+		proxy_path = g_dbus_proxy_get_path(proxy);
+
+		if (!g_str_has_prefix(proxy_path, path))
+			continue;
+
+		if (source == services) {
+			print_service(proxy, NULL);
+			list_attributes(proxy_path, characteristics);
+		} else if (source == characteristics) {
+			print_characteristic(proxy, NULL);
+			list_attributes(proxy_path, descriptors);
+		} else if (source == descriptors)
+			print_descriptor(proxy, NULL);
+	}
+}
+
+void gatt_list_attributes(const char *path)
+{
+	list_attributes(path, services);
+}
+
+static GDBusProxy *select_proxy(const char *path, GList *source)
+{
+	GList *l;
+
+	for (l = source; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+GDBusProxy *gatt_select_attribute(const char *path)
+{
+	GDBusProxy *proxy;
+
+	proxy = select_proxy(path, services);
+	if (proxy)
+		return proxy;
+
+	proxy = select_proxy(path, characteristics);
+	if (proxy)
+		return proxy;
+
+	return select_proxy(path, descriptors);
+}
+
+static char *attribute_generator(const char *text, int state, GList *source)
+{
+	static int index, len;
+	GList *list;
+
+	if (!state) {
+		index = 0;
+		len = strlen(text);
+	}
+
+	for (list = g_list_nth(source, index); list;
+						list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+		const char *path;
+
+		index++;
+
+		path = g_dbus_proxy_get_path(proxy);
+
+		if (!strncmp(path, text, len))
+			return strdup(path);
+        }
+
+	return NULL;
+}
+
+char *gatt_attribute_generator(const char *text, int state)
+{
+	static GList *list = NULL;
+
+	if (!state) {
+		GList *list1;
+
+		if (list) {
+			g_list_free(list);
+			list = NULL;
+		}
+
+		list1 = g_list_copy(characteristics);
+		list1 = g_list_concat(list1, g_list_copy(descriptors));
+
+		list = g_list_copy(services);
+		list = g_list_concat(list, list1);
+	}
+
+	return attribute_generator(text, state, list);
+}
+
+static void read_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+	DBusMessageIter iter, array;
+	uint8_t *value;
+	int len;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to read: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+		rl_printf("Invalid response to read\n");
+		return;
+	}
+
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+	if (len < 0) {
+		rl_printf("Unable to parse value\n");
+		return;
+	}
+
+	rl_hexdump(value, len);
+}
+
+static void read_attribute(GDBusProxy *proxy)
+{
+	if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+							NULL, NULL) == FALSE) {
+		rl_printf("Failed to read\n");
+		return;
+	}
+
+	rl_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
+}
+
+void gatt_read_attribute(GDBusProxy *proxy)
+{
+	const char *iface;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
+				!strcmp(iface, "org.bluez.GattDescriptor1")) {
+		read_attribute(proxy);
+		return;
+	}
+
+	rl_printf("Unable to read attribute %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void write_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 write: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+}
+
+static void write_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct iovec *iov = user_data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&iov->iov_base, iov->iov_len);
+	dbus_message_iter_close_container(iter, &array);
+}
+
+static void write_attribute(GDBusProxy *proxy, char *arg)
+{
+	struct iovec iov;
+	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)) {
+			rl_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);
+			return;
+		}
+
+		value[i] = val;
+	}
+
+	iov.iov_base = value;
+	iov.iov_len = i;
+
+	if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
+					write_reply, &iov, NULL) == FALSE) {
+		rl_printf("Failed to write\n");
+		return;
+	}
+
+	rl_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
+}
+
+void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
+{
+	const char *iface;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
+				!strcmp(iface, "org.bluez.GattDescriptor1")) {
+		write_attribute(proxy, (char *) arg);
+		return;
+	}
+
+	rl_printf("Unable to write attribute %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void notify_reply(DBusMessage *message, void *user_data)
+{
+	bool enable = GPOINTER_TO_UINT(user_data);
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_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");
+}
+
+static void notify_attribute(GDBusProxy *proxy, bool enable)
+{
+	const char *method;
+
+	if (enable == TRUE)
+		method = "StartNotify";
+	else
+		method = "StopNotify";
+
+	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");
+		return;
+	}
+}
+
+void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
+{
+	const char *iface;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
+		notify_attribute(proxy, enable);
+		return;
+	}
+
+	rl_printf("Unable to notify attribute %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void register_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+	wordexp_t *w = user_data;
+	DBusMessageIter uuids, opt;
+	const char *path = PROFILE_PATH;
+	size_t i;
+
+	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
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&opt);
+	dbus_message_iter_close_container(iter, &opt);
+
+}
+
+static void register_profile_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);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Profile registered\n");
+}
+
+void gatt_add_manager(GDBusProxy *proxy)
+{
+	managers = g_list_append(managers, proxy);
+}
+
+void gatt_remove_manager(GDBusProxy *proxy)
+{
+	managers = g_list_remove(managers, proxy);
+}
+
+static int match_proxy(const void *a, const void *b)
+{
+	GDBusProxy *proxy1 = (void *) a;
+	GDBusProxy *proxy2 = (void *) b;
+
+	return strcmp(g_dbus_proxy_get_path(proxy1),
+						g_dbus_proxy_get_path(proxy2));
+}
+
+static DBusMessage *release_profile(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_METHOD("Release", NULL, NULL, release_profile) },
+	{ }
+};
+
+void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
+								wordexp_t *w)
+{
+	GList *l;
+
+	l = g_list_find_custom(managers, proxy, match_proxy);
+	if (!l) {
+		rl_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;
+	}
+
+	if (g_dbus_proxy_method_call(l->data, "RegisterProfile",
+						register_profile_setup,
+						register_profile_reply, w,
+						NULL) == FALSE) {
+		rl_printf("Failed register profile\n");
+		return;
+	}
+}
+
+static void unregister_profile_reply(DBusMessage *message, void *user_data)
+{
+	DBusConnection *conn = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to unregister profile: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Profile unregistered\n");
+
+	g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+}
+
+static void unregister_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = PROFILE_PATH;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+void gatt_unregister_profile(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");
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(l->data, "UnregisterProfile",
+						unregister_profile_setup,
+						unregister_profile_reply, conn,
+						NULL) == FALSE) {
+		rl_printf("Failed unregister profile\n");
+		return;
+	}
+}
diff --git a/bluez/repo/client/gatt.h b/bluez/repo/client/gatt.h
new file mode 100644
index 0000000..689bb4d
--- /dev/null
+++ b/bluez/repo/client/gatt.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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
+ *
+ */
+
+void gatt_add_service(GDBusProxy *proxy);
+void gatt_remove_service(GDBusProxy *proxy);
+
+void gatt_add_characteristic(GDBusProxy *proxy);
+void gatt_remove_characteristic(GDBusProxy *proxy);
+
+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);
+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_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);
diff --git a/bluez/repo/client/main.c b/bluez/repo/client/main.c
new file mode 100644
index 0000000..b695744
--- /dev/null
+++ b/bluez/repo/client/main.c
@@ -0,0 +1,2084 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#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 "gdbus/gdbus.h"
+#include "monitor/uuid.h"
+#include "agent.h"
+#include "display.h"
+#include "gatt.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 "[bluetooth]" COLOR_OFF "# "
+#define PROMPT_OFF	"[bluetooth]# "
+
+static GMainLoop *main_loop;
+static DBusConnection *dbus_conn;
+
+static GDBusProxy *agent_manager;
+static char *auto_register_agent = NULL;
+
+static GDBusProxy *default_ctrl;
+static GDBusProxy *default_dev;
+static GDBusProxy *default_attr;
+static GList *ctrl_list;
+static GList *dev_list;
+
+static guint input = 0;
+
+static const char * const agent_arguments[] = {
+	"on",
+	"off",
+	"DisplayOnly",
+	"DisplayYesNo",
+	"KeyboardDisplay",
+	"KeyboardOnly",
+	"NoInputNoOutput",
+	NULL
+};
+
+static void proxy_leak(gpointer data)
+{
+	printf("Leaking proxy %p\n", data);
+}
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+	rl_set_prompt(PROMPT_ON);
+	printf("\r");
+	rl_on_new_line();
+	rl_redisplay();
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	rl_set_prompt(PROMPT_OFF);
+	printf("\r");
+	rl_on_new_line();
+	rl_redisplay();
+
+	g_list_free(ctrl_list);
+	ctrl_list = NULL;
+
+	default_ctrl = NULL;
+
+	g_list_free(dev_list);
+	dev_list = 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>";
+
+	rl_printf("%s%s%sController %s %s %s\n",
+				description ? "[" : "",
+				description ? : "",
+				description ? "] " : "",
+				address, name,
+				default_ctrl == 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>";
+
+	rl_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) {
+		rl_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);
+		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);
+		break;
+	case DBUS_TYPE_BOOLEAN:
+		dbus_message_iter_get_basic(iter, &valbool);
+		rl_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);
+		break;
+	case DBUS_TYPE_UINT16:
+		dbus_message_iter_get_basic(iter, &valu16);
+		rl_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);
+		break;
+	case DBUS_TYPE_BYTE:
+		dbus_message_iter_get_basic(iter, &byte);
+		rl_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:
+		rl_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 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 = 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);
+
+		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 gboolean service_is_child(GDBusProxy *service)
+{
+	GList *l;
+	DBusMessageIter iter;
+	const char *device, *path;
+
+	if (g_dbus_proxy_get_property(service, "Device", &iter) == FALSE)
+		return FALSE;
+
+	dbus_message_iter_get_basic(&iter, &device);
+
+	for (l = dev_list; l; l = g_list_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		path = g_dbus_proxy_get_path(proxy);
+
+		if (!strcmp(path, device))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+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")) {
+		if (device_is_child(proxy, default_ctrl) == TRUE) {
+			dev_list = g_list_append(dev_list, proxy);
+
+			print_device(proxy, COLORED_NEW);
+		}
+	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
+		ctrl_list = g_list_append(ctrl_list, proxy);
+
+		if (!default_ctrl)
+			default_ctrl = proxy;
+
+		print_adapter(proxy, COLORED_NEW);
+	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
+		if (!agent_manager) {
+			agent_manager = proxy;
+
+			if (auto_register_agent)
+				agent_register(dbus_conn, agent_manager,
+							auto_register_agent);
+		}
+	} else if (!strcmp(interface, "org.bluez.GattService1")) {
+		if (service_is_child(proxy))
+			gatt_add_service(proxy);
+	} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+		gatt_add_characteristic(proxy);
+	} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+		gatt_add_descriptor(proxy);
+	} else if (!strcmp(interface, "org.bluez.GattManager1")) {
+		gatt_add_manager(proxy);
+	}
+}
+
+static void set_default_device(GDBusProxy *proxy, const char *attribute)
+{
+	char *desc = NULL;
+	DBusMessageIter iter;
+	const char *path;
+
+	default_dev = proxy;
+
+	if (proxy == NULL) {
+		default_attr = NULL;
+		goto done;
+	}
+
+	if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) {
+		if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
+			goto done;
+	}
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	dbus_message_iter_get_basic(&iter, &desc);
+	desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc,
+				attribute ? ":" : "",
+				attribute ? attribute + strlen(path) : "");
+
+done:
+	rl_set_prompt(desc ? desc : PROMPT_ON);
+	printf("\r");
+	rl_on_new_line();
+	rl_redisplay();
+	g_free(desc);
+}
+
+static void set_default_attribute(GDBusProxy *proxy)
+{
+	const char *path;
+
+	default_attr = proxy;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	set_default_device(default_dev, path);
+}
+
+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")) {
+		if (device_is_child(proxy, default_ctrl) == TRUE) {
+			dev_list = g_list_remove(dev_list, proxy);
+
+			print_device(proxy, COLORED_DEL);
+
+			if (default_dev == proxy)
+				set_default_device(NULL, NULL);
+		}
+	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
+		ctrl_list = g_list_remove(ctrl_list, proxy);
+
+		print_adapter(proxy, COLORED_DEL);
+
+		if (default_ctrl == proxy) {
+			default_ctrl = NULL;
+			set_default_device(NULL, NULL);
+
+			g_list_free(dev_list);
+			dev_list = NULL;
+		}
+	} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
+		if (agent_manager == proxy) {
+			agent_manager = NULL;
+			if (auto_register_agent)
+				agent_unregister(dbus_conn, NULL);
+		}
+	} else if (!strcmp(interface, "org.bluez.GattService1")) {
+		gatt_remove_service(proxy);
+
+		if (default_attr == proxy)
+			set_default_attribute(NULL);
+	} else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+		gatt_remove_characteristic(proxy);
+
+		if (default_attr == proxy)
+			set_default_attribute(NULL);
+	} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+		gatt_remove_descriptor(proxy);
+
+		if (default_attr == proxy)
+			set_default_attribute(NULL);
+	} else if (!strcmp(interface, "org.bluez.GattManager1")) {
+		gatt_remove_manager(proxy);
+	}
+}
+
+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 (device_is_child(proxy, default_ctrl) == TRUE) {
+			DBusMessageIter addr_iter;
+			char *str;
+
+			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
+						"] Device %s ", address);
+			} else
+				str = g_strdup("");
+
+			if (strcmp(name, "Connected") == 0) {
+				dbus_bool_t connected;
+
+				dbus_message_iter_get_basic(iter, &connected);
+
+				if (connected && default_dev == NULL)
+					set_default_device(proxy, NULL);
+				else if (!connected && default_dev == proxy)
+					set_default_device(NULL, NULL);
+			}
+
+			print_iter(str, name, iter);
+			g_free(str);
+		}
+	} else if (!strcmp(interface, "org.bluez.Adapter1")) {
+		DBusMessageIter addr_iter;
+		char *str;
+
+		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("");
+
+		print_iter(str, name, iter);
+		g_free(str);
+	} else if (proxy == default_attr) {
+		char *str;
+
+		str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
+						g_dbus_proxy_get_path(proxy));
+
+		print_iter(str, name, iter);
+		g_free(str);
+	}
+}
+
+static void message_handler(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	rl_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
+					dbus_message_get_member(message));
+}
+
+static GDBusProxy *find_proxy_by_address(GList *source, const char *address)
+{
+	GList *list;
+
+	for (list = g_list_first(source); list; list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+		DBusMessageIter iter;
+		const char *str;
+
+		if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &str);
+
+		if (!strcmp(str, address))
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static gboolean check_default_ctrl(void)
+{
+	if (!default_ctrl) {
+		rl_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)
+{
+	const char * const *opt;
+
+	if (arg == NULL || strlen(arg) == 0) {
+		rl_printf("Missing on/off/capability argument\n");
+		return FALSE;
+	}
+
+	if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+		*value = TRUE;
+		*capability = "";
+		return TRUE;
+	}
+
+	if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+		*value = FALSE;
+		return TRUE;
+	}
+
+	for (opt = agent_arguments; *opt; opt++) {
+		if (strcmp(arg, *opt) == 0) {
+			*value = TRUE;
+			*capability = *opt;
+			return TRUE;
+		}
+	}
+
+	rl_printf("Invalid argument %s\n", arg);
+	return FALSE;
+}
+
+static void cmd_list(const char *arg)
+{
+	GList *list;
+
+	for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+		print_adapter(proxy, NULL);
+	}
+}
+
+static void cmd_show(const char *arg)
+{
+	GDBusProxy *proxy;
+	DBusMessageIter iter;
+	const char *address;
+
+	if (!arg || !strlen(arg)) {
+		if (check_default_ctrl() == FALSE)
+			return;
+
+		proxy = default_ctrl;
+	} else {
+		proxy = find_proxy_by_address(ctrl_list, arg);
+		if (!proxy) {
+			rl_printf("Controller %s not available\n", arg);
+			return;
+		}
+	}
+
+	if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &address);
+	rl_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_property(proxy, "Pairable");
+	print_uuids(proxy);
+	print_property(proxy, "Modalias");
+	print_property(proxy, "Discovering");
+}
+
+static void cmd_select(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing controller address argument\n");
+		return;
+	}
+
+	proxy = find_proxy_by_address(ctrl_list, arg);
+	if (!proxy) {
+		rl_printf("Controller %s not available\n", arg);
+		return;
+	}
+
+	if (default_ctrl == proxy)
+		return;
+
+	default_ctrl = proxy;
+	print_adapter(proxy, NULL);
+
+	g_list_free(dev_list);
+	dev_list = NULL;
+}
+
+static void cmd_devices(const char *arg)
+{
+	GList *list;
+
+	for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+		print_device(proxy, NULL);
+	}
+}
+
+static void cmd_paired_devices(const char *arg)
+{
+	GList *list;
+
+	for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+		DBusMessageIter iter;
+		dbus_bool_t paired;
+
+		if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE)
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &paired);
+		if (!paired)
+			continue;
+
+		print_device(proxy, NULL);
+	}
+}
+
+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);
+	else
+		rl_printf("Changing %s succeeded\n", str);
+}
+
+static void cmd_system_alias(const char *arg)
+{
+	char *name;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing name argument\n");
+		return;
+	}
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	name = g_strdup(arg);
+
+	if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias",
+					DBUS_TYPE_STRING, &name,
+					generic_callback, name, g_free) == TRUE)
+		return;
+
+	g_free(name);
+}
+
+static void cmd_reset_alias(const char *arg)
+{
+	char *name;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	name = g_strdup("");
+
+	if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias",
+					DBUS_TYPE_STRING, &name,
+					generic_callback, name, g_free) == TRUE)
+		return;
+
+	g_free(name);
+}
+
+static void cmd_power(const char *arg)
+{
+	dbus_bool_t powered;
+	char *str;
+
+	if (parse_argument_on_off(arg, &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, "Powered",
+					DBUS_TYPE_BOOLEAN, &powered,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void cmd_pairable(const char *arg)
+{
+	dbus_bool_t pairable;
+	char *str;
+
+	if (parse_argument_on_off(arg, &pairable) == FALSE)
+		return;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	str = g_strdup_printf("pairable %s", pairable == TRUE ? "on" : "off");
+
+	if (g_dbus_proxy_set_property_basic(default_ctrl, "Pairable",
+					DBUS_TYPE_BOOLEAN, &pairable,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void cmd_discoverable(const char *arg)
+{
+	dbus_bool_t discoverable;
+	char *str;
+
+	if (parse_argument_on_off(arg, &discoverable) == FALSE)
+		return;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	str = g_strdup_printf("discoverable %s",
+				discoverable == TRUE ? "on" : "off");
+
+	if (g_dbus_proxy_set_property_basic(default_ctrl, "Discoverable",
+					DBUS_TYPE_BOOLEAN, &discoverable,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void cmd_agent(const char *arg)
+{
+	dbus_bool_t enable;
+	const char *capability;
+
+	if (parse_argument_agent(arg, &enable, &capability) == FALSE)
+		return;
+
+	if (enable == TRUE) {
+		g_free(auto_register_agent);
+		auto_register_agent = g_strdup(capability);
+
+		if (agent_manager)
+			agent_register(dbus_conn, agent_manager,
+						auto_register_agent);
+		else
+			rl_printf("Agent registration enabled\n");
+	} else {
+		g_free(auto_register_agent);
+		auto_register_agent = NULL;
+
+		if (agent_manager)
+			agent_unregister(dbus_conn, agent_manager);
+		else
+			rl_printf("Agent registration disabled\n");
+	}
+}
+
+static void cmd_default_agent(const char *arg)
+{
+	agent_default(dbus_conn, agent_manager);
+}
+
+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) {
+		rl_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, method,
+				NULL, start_discovery_reply,
+				GUINT_TO_POINTER(enable), NULL) == FALSE) {
+		rl_printf("Failed to %s discovery\n",
+					enable == TRUE ? "start" : "stop");
+		return;
+	}
+}
+
+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 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);
+}
+
+#define	DISTANCE_VAL_INVALID	0x7FFF
+
+struct set_discovery_filter_args {
+	char *transport;
+	dbus_uint16_t rssi;
+	dbus_int16_t pathloss;
+	GSList *uuids;
+};
+
+static void set_discovery_filter_setup(DBusMessageIter *iter,
+					   void *user_data)
+{
+	struct set_discovery_filter_args *args = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+			    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			    DBUS_TYPE_STRING_AS_STRING
+			    DBUS_TYPE_VARIANT_AS_STRING
+			    DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	if (args->uuids != NULL) {
+		DBusMessageIter entry, value, arrayIter;
+		char *uuids = "UUIDs";
+		GSList *l;
+
+		dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+						 NULL, &entry);
+		/* dict key */
+		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+					       &uuids);
+
+		dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+						 "as", &value);
+
+		dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, "s",
+						 &arrayIter);
+
+		for (l = args->uuids; l != NULL; l = g_slist_next(l))
+			/* list->data contains string representation of uuid */
+			dbus_message_iter_append_basic(&arrayIter,
+						       DBUS_TYPE_STRING,
+						       &l->data);
+
+		dbus_message_iter_close_container(&value, &arrayIter);
+
+		/* close vararg*/
+		dbus_message_iter_close_container(&entry, &value);
+
+		/* close entry */
+		dbus_message_iter_close_container(&dict, &entry);
+	}
+
+	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);
+
+	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) {
+		rl_printf("SetDiscoveryFilter failed: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("SetDiscoveryFilter success\n");
+}
+
+static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;
+static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+static GSList *filtered_scan_uuids;
+static char *filtered_scan_transport;
+
+static void cmd_set_scan_filter_commit(void)
+{
+	struct set_discovery_filter_args args;
+
+	args.uuids = NULL;
+	args.pathloss = filtered_scan_pathloss;
+	args.rssi = filtered_scan_rssi;
+	args.transport = filtered_scan_transport;
+	args.uuids = filtered_scan_uuids;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter",
+		set_discovery_filter_setup, set_discovery_filter_reply,
+		&args, NULL) == FALSE) {
+		rl_printf("Failed to set discovery filter\n");
+		return;
+	}
+}
+
+static void cmd_set_scan_filter_uuids(const char *arg)
+{
+	char *uuid_str, *saveptr, *uuids, *uuidstmp;
+
+	g_slist_free_full(filtered_scan_uuids, g_free);
+	filtered_scan_uuids = NULL;
+
+	if (!arg || !strlen(arg))
+		return;
+
+	uuids = g_strdup(arg);
+	for (uuidstmp = uuids; ; uuidstmp = NULL) {
+		uuid_str = strtok_r(uuidstmp, " \t", &saveptr);
+		if (uuid_str == NULL)
+			break;
+	    filtered_scan_uuids = g_slist_append(filtered_scan_uuids,
+							strdup(uuid_str));
+	}
+
+	g_free(uuids);
+
+	cmd_set_scan_filter_commit();
+}
+
+static void cmd_set_scan_filter_rssi(const char *arg)
+{
+	filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+
+	if (!arg || !strlen(arg))
+		filtered_scan_rssi = DISTANCE_VAL_INVALID;
+	else
+		filtered_scan_rssi = atoi(arg);
+
+	cmd_set_scan_filter_commit();
+}
+
+static void cmd_set_scan_filter_pathloss(const char *arg)
+{
+	filtered_scan_rssi = DISTANCE_VAL_INVALID;
+
+	if (!arg || !strlen(arg))
+		filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+	else
+		filtered_scan_pathloss = atoi(arg);
+
+	cmd_set_scan_filter_commit();
+}
+
+static void cmd_set_scan_filter_transport(const char *arg)
+{
+	g_free(filtered_scan_transport);
+
+	if (!arg || !strlen(arg))
+		filtered_scan_transport = NULL;
+	else
+		filtered_scan_transport = g_strdup(arg);
+
+	cmd_set_scan_filter_commit();
+}
+
+static void cmd_set_scan_filter_clear(const char *arg)
+{
+	/* set default values for all options */
+	filtered_scan_rssi = DISTANCE_VAL_INVALID;
+	filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+	g_slist_free_full(filtered_scan_uuids, g_free);
+	filtered_scan_uuids = NULL;
+	g_free(filtered_scan_transport);
+	filtered_scan_transport = NULL;
+
+	cmd_set_scan_filter_commit();
+}
+
+static struct GDBusProxy *find_device(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	if (!arg || !strlen(arg)) {
+		if (default_dev)
+			return default_dev;
+		rl_printf("Missing device address argument\n");
+		return NULL;
+	}
+
+	proxy = find_proxy_by_address(dev_list, arg);
+	if (!proxy) {
+		rl_printf("Device %s not available\n", arg);
+		return NULL;
+	}
+
+	return proxy;
+}
+
+static void cmd_info(const char *arg)
+{
+	GDBusProxy *proxy;
+	DBusMessageIter iter;
+	const char *address;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &address);
+	rl_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, "Paired");
+	print_property(proxy, "Trusted");
+	print_property(proxy, "Blocked");
+	print_property(proxy, "Connected");
+	print_property(proxy, "LegacyPairing");
+	print_uuids(proxy);
+	print_property(proxy, "Modalias");
+	print_property(proxy, "ManufacturerData");
+	print_property(proxy, "ServiceData");
+	print_property(proxy, "RSSI");
+	print_property(proxy, "TxPower");
+}
+
+static void pair_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 pair: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Pairing successful\n");
+}
+
+static void cmd_pair(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
+							NULL, NULL) == FALSE) {
+		rl_printf("Failed to pair\n");
+		return;
+	}
+
+	rl_printf("Attempting to pair with %s\n", arg);
+}
+
+static void cmd_trust(const char *arg)
+{
+	GDBusProxy *proxy;
+	dbus_bool_t trusted;
+	char *str;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	trusted = TRUE;
+
+	str = g_strdup_printf("%s trust", arg);
+
+	if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
+					DBUS_TYPE_BOOLEAN, &trusted,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void cmd_untrust(const char *arg)
+{
+	GDBusProxy *proxy;
+	dbus_bool_t trusted;
+	char *str;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	trusted = FALSE;
+
+	str = g_strdup_printf("%s untrust", arg);
+
+	if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
+					DBUS_TYPE_BOOLEAN, &trusted,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void cmd_block(const char *arg)
+{
+	GDBusProxy *proxy;
+	dbus_bool_t blocked;
+	char *str;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	blocked = TRUE;
+
+	str = g_strdup_printf("%s block", arg);
+
+	if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
+					DBUS_TYPE_BOOLEAN, &blocked,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void cmd_unblock(const char *arg)
+{
+	GDBusProxy *proxy;
+	dbus_bool_t blocked;
+	char *str;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	blocked = FALSE;
+
+	str = g_strdup_printf("%s unblock", arg);
+
+	if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
+					DBUS_TYPE_BOOLEAN, &blocked,
+					generic_callback, str, g_free) == TRUE)
+		return;
+
+	g_free(str);
+}
+
+static void remove_device_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 remove device: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Device has been removed\n");
+}
+
+static void remove_device_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void cmd_remove(const char *arg)
+{
+	GDBusProxy *proxy;
+	char *path;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing device address argument\n");
+		return;
+	}
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	proxy = find_proxy_by_address(dev_list, arg);
+	if (!proxy) {
+		rl_printf("Device %s not available\n", arg);
+		return;
+	}
+
+	path = g_strdup(g_dbus_proxy_get_path(proxy));
+
+	if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice",
+						remove_device_setup,
+						remove_device_reply,
+						path, g_free) == FALSE) {
+		rl_printf("Failed to remove device\n");
+		g_free(path);
+		return;
+	}
+}
+
+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) {
+		rl_printf("Failed to connect: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Connection successful\n");
+
+	set_default_device(proxy, NULL);
+}
+
+static void cmd_connect(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing device address argument\n");
+		return;
+	}
+
+	proxy = find_proxy_by_address(dev_list, arg);
+	if (!proxy) {
+		rl_printf("Device %s not available\n", arg);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
+							proxy, NULL) == FALSE) {
+		rl_printf("Failed to connect\n");
+		return;
+	}
+
+	rl_printf("Attempting to connect to %s\n", arg);
+}
+
+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) {
+		rl_printf("Failed to disconnect: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Successful disconnected\n");
+
+	if (proxy != default_dev)
+		return;
+
+	set_default_device(NULL, NULL);
+}
+
+static void cmd_disconn(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
+							proxy, NULL) == FALSE) {
+		rl_printf("Failed to disconnect\n");
+		return;
+	}
+	if (strlen(arg) == 0) {
+		DBusMessageIter iter;
+
+		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);
+}
+
+static void cmd_list_attributes(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	proxy = find_device(arg);
+	if (!proxy)
+		return;
+
+	gatt_list_attributes(g_dbus_proxy_get_path(proxy));
+}
+
+static void cmd_select_attribute(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing attribute argument\n");
+		return;
+	}
+
+	if (!default_dev) {
+		rl_printf("No device connected\n");
+		return;
+	}
+
+	proxy = gatt_select_attribute(arg);
+	if (proxy)
+		set_default_attribute(proxy);
+}
+
+static struct GDBusProxy *find_attribute(const char *arg)
+{
+	GDBusProxy *proxy;
+
+	if (!arg || !strlen(arg)) {
+		if (default_attr)
+			return default_attr;
+		rl_printf("Missing attribute argument\n");
+		return NULL;
+	}
+
+	proxy = gatt_select_attribute(arg);
+	if (!proxy) {
+		rl_printf("Attribute %s not available\n", arg);
+		return NULL;
+	}
+
+	return proxy;
+}
+
+static void cmd_attribute_info(const char *arg)
+{
+	GDBusProxy *proxy;
+	DBusMessageIter iter;
+	const char *iface, *uuid, *text;
+
+	proxy = find_attribute(arg);
+	if (!proxy)
+		return;
+
+	if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	text = 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);
+
+		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);
+
+		print_property(proxy, "UUID");
+		print_property(proxy, "Service");
+		print_property(proxy, "Value");
+		print_property(proxy, "Notifying");
+		print_property(proxy, "Flags");
+		print_property(proxy, "Descriptors");
+	} else if (!strcmp(iface, "org.bluez.GattDescriptor1")) {
+		rl_printf("Descriptor - %s\n", text);
+
+		print_property(proxy, "UUID");
+		print_property(proxy, "Characteristic");
+		print_property(proxy, "Value");
+	}
+}
+
+static void cmd_read(const char *arg)
+{
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_read_attribute(default_attr);
+}
+
+static void cmd_write(const char *arg)
+{
+	if (!arg || !strlen(arg)) {
+		rl_printf("Missing data argument\n");
+		return;
+	}
+
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_write_attribute(default_attr, arg);
+}
+
+static void cmd_notify(const char *arg)
+{
+	dbus_bool_t enable;
+
+	if (parse_argument_on_off(arg, &enable) == FALSE)
+		return;
+
+	if (!default_attr) {
+		rl_printf("No attribute selected\n");
+		return;
+	}
+
+	gatt_notify_attribute(default_attr, enable ? true : false);
+}
+
+static void cmd_register_profile(const char *arg)
+{
+	wordexp_t w;
+
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	if (wordexp(arg, &w, WRDE_NOCMD)) {
+		rl_printf("Invalid argument\n");
+		return;
+	}
+
+	if (w.we_wordc == 0) {
+		rl_printf("Missing argument\n");
+		return;
+	}
+
+	gatt_register_profile(dbus_conn, default_ctrl, &w);
+
+	wordfree(&w);
+}
+
+static void cmd_unregister_profile(const char *arg)
+{
+	if (check_default_ctrl() == FALSE)
+		return;
+
+	gatt_unregister_profile(dbus_conn, default_ctrl);
+}
+
+static void cmd_version(const char *arg)
+{
+	rl_printf("Version %s\n", VERSION);
+}
+
+static void cmd_quit(const char *arg)
+{
+	g_main_loop_quit(main_loop);
+}
+
+static char *generic_generator(const char *text, int state,
+					GList *source, const char *property)
+{
+	static int index, len;
+	GList *list;
+
+	if (!state) {
+		index = 0;
+		len = strlen(text);
+	}
+
+	for (list = g_list_nth(source, index); list;
+						list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+		DBusMessageIter iter;
+		const char *str;
+
+		index++;
+
+		if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE)
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &str);
+
+		if (!strncmp(str, text, len))
+			return strdup(str);
+        }
+
+	return NULL;
+}
+
+static char *ctrl_generator(const char *text, int state)
+{
+	return generic_generator(text, state, ctrl_list, "Address");
+}
+
+static char *dev_generator(const char *text, int state)
+{
+	return generic_generator(text, state, dev_list, "Address");
+}
+
+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 int index, len;
+	const char *arg;
+
+	if (!state) {
+		index = 0;
+		len = strlen(text);
+	}
+
+	while ((arg = agent_arguments[index])) {
+		index++;
+
+		if (!strncmp(arg, text, len))
+			return strdup(arg);
+	}
+
+	return NULL;
+}
+
+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[] = {
+	{ "list",         NULL,       cmd_list, "List available controllers" },
+	{ "show",         "[ctrl]",   cmd_show, "Controller information",
+							ctrl_generator },
+	{ "select",       "<ctrl>",   cmd_select, "Select default controller",
+							ctrl_generator },
+	{ "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" },
+	{ "pairable",     "<on/off>", cmd_pairable,
+					"Set controller pairable mode" },
+	{ "discoverable", "<on/off>", cmd_discoverable,
+					"Set controller discoverable mode" },
+	{ "agent",        "<on/off/capability>", cmd_agent,
+				"Enable/disable agent with given capability",
+							capability_generator},
+	{ "default-agent",NULL,       cmd_default_agent,
+				"Set agent as the default one" },
+	{ "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" },
+	{ "info",         "[dev]",    cmd_info, "Device information",
+							dev_generator },
+	{ "pair",         "[dev]",    cmd_pair, "Pair with device",
+							dev_generator },
+	{ "trust",        "[dev]",    cmd_trust, "Trust device",
+							dev_generator },
+	{ "untrust",      "[dev]",    cmd_untrust, "Untrust device",
+							dev_generator },
+	{ "block",        "[dev]",    cmd_block, "Block device",
+								dev_generator },
+	{ "unblock",      "[dev]",    cmd_unblock, "Unblock device",
+								dev_generator },
+	{ "remove",       "<dev>",    cmd_remove, "Remove device",
+							dev_generator },
+	{ "connect",      "<dev>",    cmd_connect, "Connect device",
+							dev_generator },
+	{ "disconnect",   "[dev]",    cmd_disconn, "Disconnect device",
+							dev_generator },
+	{ "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
+							dev_generator },
+	{ "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 input_handler(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	if (condition & G_IO_IN) {
+		rl_callback_read_char();
+		return TRUE;
+	}
+
+	if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_main_loop_quit(main_loop);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static guint setup_standard_input(void)
+{
+	GIOChannel *channel;
+	guint source;
+
+	channel = g_io_channel_unix_new(fileno(stdin));
+
+	source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				input_handler, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	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 gboolean option_version = FALSE;
+
+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("");
+
+	return TRUE;
+}
+
+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 void client_ready(GDBusClient *client, void *user_data)
+{
+	guint *input = user_data;
+
+	*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);
+
+	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);
+	}
+
+	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();
+
+	signal = setup_signalfd();
+	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);
+
+	input = 0;
+	g_dbus_client_set_ready_watch(client, client_ready, &input);
+
+	g_main_loop_run(main_loop);
+
+	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);
+	g_list_free_full(dev_list, proxy_leak);
+
+	g_free(auto_register_agent);
+
+	return 0;
+}
diff --git a/bluez/repo/configure.ac b/bluez/repo/configure.ac
new file mode 100644
index 0000000..7ec7c32
--- /dev/null
+++ b/bluez/repo/configure.ac
@@ -0,0 +1,303 @@
+AC_PREREQ(2.60)
+AC_INIT(bluez, 5.37)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
+					tar-pax no-dist-gzip dist-xz])
+AC_CONFIG_HEADERS(config.h)
+AC_USE_SYSTEM_EXTENSIONS
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+PKG_PROG_PKG_CONFIG
+
+COMPILER_FLAGS
+
+AC_LANG_C
+
+AC_C_RESTRICT
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AC_PROG_MKDIR_P
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+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
+
+AC_ARG_ENABLE(threads, AC_HELP_STRING([--enable-threads],
+		[enable threading support]), [enable_threads=${enableval}])
+
+AC_CHECK_FUNC(signalfd, dummy=yes,
+			AC_MSG_ERROR(signalfd support is required))
+
+AC_CHECK_LIB(rt, clock_gettime, dummy=yes,
+			AC_MSG_ERROR(realtime clock support is required))
+
+AC_CHECK_LIB(pthread, pthread_create, dummy=yes,
+			AC_MSG_ERROR(posix thread support is required))
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+			AC_MSG_ERROR(dynamic linking loader is required))
+
+AC_CHECK_HEADERS(linux/types.h linux/if_alg.h)
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+				AC_MSG_ERROR(GLib >= 2.28 is required))
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+if (test "${enable_threads}" = "yes"); then
+	AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
+	PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
+				AC_MSG_ERROR(GThread >= 2.16 is required))
+	GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
+	GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
+fi
+
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes,
+				AC_MSG_ERROR(D-Bus >= 1.6 is required))
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+AC_ARG_WITH([dbusconfdir], AC_HELP_STRING([--with-dbusconfdir=DIR],
+				[path to D-Bus configuration directory]),
+					[path_dbusconfdir=${withval}])
+if (test -z "${path_dbusconfdir}"); then
+	AC_MSG_CHECKING([D-Bus configuration directory])
+	path_dbusconfdir="`$PKG_CONFIG --variable=sysconfdir dbus-1`"
+	if (test -z "${path_dbusconfdir}"); then
+		AC_MSG_ERROR([D-Bus configuration directory is required])
+	fi
+	AC_MSG_RESULT([${path_dbusconfdir}])
+fi
+AC_SUBST(DBUS_CONFDIR, [${path_dbusconfdir}])
+
+AC_ARG_WITH([dbussystembusdir], AC_HELP_STRING([--with-dbussystembusdir=DIR],
+				[path to D-Bus system bus services directory]),
+					[path_dbussystembusdir=${withval}])
+if (test -z "${path_dbussystembusdir}"); then
+	AC_MSG_CHECKING([D-Bus system bus services dir])
+	path_dbussystembusdir="`$PKG_CONFIG --variable=system_bus_services_dir dbus-1`"
+	if (test -z "${path_dbussystembusdir}"); then
+		AC_MSG_ERROR([D-Bus system bus services directory is required])
+	fi
+	AC_MSG_RESULT([${path_dbussystembusdir}])
+fi
+AC_SUBST(DBUS_SYSTEMBUSDIR, [${path_dbussystembusdir}])
+
+AC_ARG_WITH([dbussessionbusdir], AC_HELP_STRING([--with-dbussessionbusdir=DIR],
+				[path to D-Bus session bus services directory]),
+					[path_dbussessionbusdir=${withval}])
+if (test -z "${path_dbussessionbusdir}"); then
+	AC_MSG_CHECKING([D-Bus session bus services dir])
+	path_dbussessionbusdir="`$PKG_CONFIG --variable=session_bus_services_dir dbus-1`"
+	if (test -z "${path_dbussessionbusdir}"); then
+		AC_MSG_ERROR([D-Bus session bus services directory is required])
+	fi
+	AC_MSG_RESULT([${path_dbussessionbusdir}])
+fi
+AC_SUBST(DBUS_SESSIONBUSDIR, [${path_dbussessionbusdir}])
+
+AC_ARG_ENABLE(backtrace, AC_HELP_STRING([--enable-backtrace],
+		[compile backtrace support]), [enable_backtrace=${enableval}])
+
+if (test "${enable_backtrace}" = "yes"); then
+	AC_CHECK_HEADER(elfutils/libdwfl.h, dummy=yes,
+			AC_MSG_ERROR(elfutils support is required))
+	AC_DEFINE(HAVE_BACKTRACE_SUPPORT, 1,
+			[Define to 1 if you have the backtrace support.])
+	BACKTRACE_CFLAGS=""
+	BACKTRACE_LIBS="-ldw"
+	AC_SUBST(BACKTRACE_CFLAGS)
+	AC_SUBST(BACKTRACE_LIBS)
+fi
+
+AC_ARG_ENABLE(library, AC_HELP_STRING([--enable-library],
+		[install Bluetooth library]), [enable_library=${enableval}])
+AM_CONDITIONAL(LIBRARY, test "${enable_library}" = "yes")
+
+AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
+		[enable test/example scripts]), [enable_test=${enableval}])
+AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--disable-tools],
+		[disable Bluetooth tools]), [enable_tools=${enableval}])
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
+
+AC_ARG_ENABLE(monitor, AC_HELP_STRING([--disable-monitor],
+		[disable Bluetooth monitor]), [enable_monitor=${enableval}])
+AM_CONDITIONAL(MONITOR, test "${enable_monitor}" != "no")
+
+AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev],
+		[disable udev device support]), [enable_udev=${enableval}])
+if (test "${enable_tools}" != "no" && test "${enable_udev}" != "no"); then
+	PKG_CHECK_MODULES(UDEV, libudev >= 172, dummy=yes,
+				AC_MSG_ERROR(libudev >= 172 is required))
+	AC_SUBST(UDEV_CFLAGS)
+	AC_SUBST(UDEV_LIBS)
+	AC_CHECK_LIB(udev, udev_hwdb_new,
+		AC_DEFINE(HAVE_UDEV_HWDB_NEW, 1,
+			[Define to 1 if you have the udev_hwdb_new() function.]))
+fi
+AM_CONDITIONAL(UDEV, test "${enable_udev}" != "no")
+
+AC_ARG_WITH([udevdir], AC_HELP_STRING([--with-udevdir=DIR],
+			[path to udev directory]), [path_udevdir=${withval}])
+if (test "${enable_udev}" != "no" && test -z "${path_udevdir}"); then
+	AC_MSG_CHECKING([udev directory])
+	path_udevdir="`$PKG_CONFIG --variable=udevdir udev`"
+	if (test -z "${path_udevdir}"); then
+		AC_MSG_ERROR([udev directory is required])
+	fi
+	AC_MSG_RESULT([${path_udevdir}])
+fi
+AC_SUBST(UDEV_DIR, [${path_udevdir}])
+
+AM_CONDITIONAL(HID2HCI, test "${enable_tools}" != "no" &&
+						test "${enable_udev}" != "no")
+
+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(obex, AC_HELP_STRING([--disable-obex],
+		[disable OBEX profile support]), [enable_obex=${enableval}])
+if (test "${enable_obex}" != "no"); then
+	PKG_CHECK_MODULES(ICAL, libical, dummy=yes,
+					AC_MSG_ERROR(libical is required))
+	AC_SUBST(ICAL_CFLAGS)
+	AC_SUBST(ICAL_LIBS)
+fi
+AM_CONDITIONAL(OBEX, test "${enable_obex}" != "no")
+
+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
+        AC_CHECK_HEADERS(readline/readline.h, enable_readline=yes,
+                AC_MSG_ERROR(readline header files are required))
+fi
+AM_CONDITIONAL(READLINE, test "${enable_readline}" = "yes")
+
+AC_ARG_ENABLE(systemd, AC_HELP_STRING([--disable-systemd],
+		[disable systemd integration]), [enable_systemd=${enableval}])
+AM_CONDITIONAL(SYSTEMD, test "${enable_systemd}" != "no")
+
+AC_ARG_WITH([systemdsystemunitdir],
+			AC_HELP_STRING([--with-systemdsystemunitdir=DIR],
+			[path to systemd system unit directory]),
+					[path_systemunitdir=${withval}])
+if (test "${enable_systemd}" != "no" && test -z "${path_systemunitdir}"); then
+	AC_MSG_CHECKING([systemd system unit dir])
+	path_systemunitdir="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"
+	if (test -z "${path_systemunitdir}"); then
+		AC_MSG_ERROR([systemd system unit directory is required])
+	fi
+	AC_MSG_RESULT([${path_systemunitdir}])
+fi
+AC_SUBST(SYSTEMD_SYSTEMUNITDIR, [${path_systemunitdir}])
+
+AC_ARG_WITH([systemduserunitdir],
+			AC_HELP_STRING([--with-systemduserunitdir=DIR],
+			[path to systemd user unit directory]),
+					[path_userunitdir=${withval}])
+if (test "${enable_systemd}" != "no" && test -z "${path_userunitdir}"); then
+	AC_MSG_CHECKING([systemd user unit dir])
+	path_userunitdir="`$PKG_CONFIG --variable=systemduserunitdir systemd`"
+	if (test -z "${path_userunitdir}"); then
+		AC_MSG_ERROR([systemd user unit directory is required])
+	fi
+	AC_MSG_RESULT([${path_userunitdir}])
+fi
+AC_SUBST(SYSTEMD_USERUNITDIR, [${path_userunitdir}])
+
+AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
+			[do not install configuration and data files]),
+					[enable_datafiles=${enableval}])
+AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
+
+AC_ARG_ENABLE(manpages, AC_HELP_STRING([--enable-manpages],
+			[enable building of manual pages]),
+					[enable_manpages=${enableval}])
+AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" = "yes")
+
+AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
+			[enable experimental plugins (SAP, NFC, ...)]),
+					[enable_experimental=${enableval}])
+AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
+
+AC_ARG_ENABLE(sixaxis, AC_HELP_STRING([--enable-sixaxis],
+		[enable sixaxis plugin]), [enable_sixaxis=${enableval}])
+AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" &&
+					 test "${enable_udev}" != "no")
+
+if (test "${prefix}" = "NONE"); then
+	dnl no prefix and no localstatedir, so default to /var
+	if (test "$localstatedir" = '${prefix}/var'); then
+		AC_SUBST([localstatedir], ['/var'])
+	fi
+
+	prefix="${ac_default_prefix}"
+fi
+
+if (test "$localstatedir" = '${prefix}/var'); then
+	storagedir="${prefix}/var/lib/bluetooth"
+else
+	storagedir="${localstatedir}/lib/bluetooth"
+fi
+AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
+			[Directory for the storage files])
+
+if (test "$sysconfdir" = '${prefix}/etc'); then
+	configdir="${prefix}/etc/bluetooth"
+else
+	configdir="${sysconfdir}/bluetooth"
+fi
+AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}",
+			[Directory for the configuration files])
+AC_SUBST(CONFIGDIR, "${configdir}")
+
+AC_ARG_ENABLE(android, AC_HELP_STRING([--enable-android],
+			[enable BlueZ for Android]),
+					[enable_android=${enableval}])
+AM_CONDITIONAL(ANDROID, test "${enable_android}" = "yes")
+
+if (test "${enable_android}" = "yes"); then
+	PKG_CHECK_MODULES(SBC, sbc >= 1.2, dummy=yes,
+					AC_MSG_ERROR(SBC library >= 1.2 is required))
+	AC_SUBST(SBC_CFLAGS)
+	AC_SUBST(SBC_LIBS)
+fi
+
+if (test "${enable_android}" = "yes"); then
+	PKG_CHECK_MODULES(SPEEXDSP, speexdsp >= 1.2, dummy=yes,
+					AC_MSG_ERROR(SPEEXDSP library >= 1.2 is required))
+	AC_SUBST(SPEEXDSP_CFLAGS)
+	AC_SUBST(SPEEXDSP_LIBS)
+fi
+
+AC_DEFINE_UNQUOTED(ANDROID_STORAGEDIR, "${storagedir}/android",
+			[Directory for the Android daemon storage files])
+
+AC_OUTPUT(Makefile src/bluetoothd.8 lib/bluez.pc)
diff --git a/bluez/repo/doc/._adapter-api.txt b/bluez/repo/doc/._adapter-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._adapter-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._advertising-api.txt b/bluez/repo/doc/._advertising-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._advertising-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._agent-api.txt b/bluez/repo/doc/._agent-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._agent-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._alert-api.txt b/bluez/repo/doc/._alert-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._alert-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._assigned-numbers.txt b/bluez/repo/doc/._assigned-numbers.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._assigned-numbers.txt
Binary files differ
diff --git a/bluez/repo/doc/._btmon.txt b/bluez/repo/doc/._btmon.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._btmon.txt
Binary files differ
diff --git a/bluez/repo/doc/._coding-style.txt b/bluez/repo/doc/._coding-style.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._coding-style.txt
Binary files differ
diff --git a/bluez/repo/doc/._cyclingspeed-api.txt b/bluez/repo/doc/._cyclingspeed-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._cyclingspeed-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._device-api.txt b/bluez/repo/doc/._device-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._device-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._gatt-api.txt b/bluez/repo/doc/._gatt-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._gatt-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._health-api.txt b/bluez/repo/doc/._health-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._health-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._heartrate-api.txt b/bluez/repo/doc/._heartrate-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._heartrate-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._input-api.txt b/bluez/repo/doc/._input-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._input-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._maintainer-guidelines.txt b/bluez/repo/doc/._maintainer-guidelines.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._maintainer-guidelines.txt
Binary files differ
diff --git a/bluez/repo/doc/._media-api.txt b/bluez/repo/doc/._media-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._media-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._mgmt-api.txt b/bluez/repo/doc/._mgmt-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._mgmt-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._network-api.txt b/bluez/repo/doc/._network-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._network-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._obex-agent-api.txt b/bluez/repo/doc/._obex-agent-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._obex-agent-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._obex-api.txt b/bluez/repo/doc/._obex-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._obex-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._pics-opp.txt b/bluez/repo/doc/._pics-opp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._pics-opp.txt
Binary files differ
diff --git a/bluez/repo/doc/._pixit-opp.txt b/bluez/repo/doc/._pixit-opp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._pixit-opp.txt
Binary files differ
diff --git a/bluez/repo/doc/._profile-api.txt b/bluez/repo/doc/._profile-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._profile-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._proximity-api.txt b/bluez/repo/doc/._proximity-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._proximity-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._pts-opp.txt b/bluez/repo/doc/._pts-opp.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._pts-opp.txt
Binary files differ
diff --git a/bluez/repo/doc/._sap-api.txt b/bluez/repo/doc/._sap-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._sap-api.txt
Binary files differ
diff --git a/bluez/repo/doc/._settings-storage.txt b/bluez/repo/doc/._settings-storage.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._settings-storage.txt
Binary files differ
diff --git a/bluez/repo/doc/._supported-features.txt b/bluez/repo/doc/._supported-features.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._supported-features.txt
Binary files differ
diff --git a/bluez/repo/doc/._test-coverage.txt b/bluez/repo/doc/._test-coverage.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._test-coverage.txt
Binary files differ
diff --git a/bluez/repo/doc/._test-runner.txt b/bluez/repo/doc/._test-runner.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._test-runner.txt
Binary files differ
diff --git a/bluez/repo/doc/._thermometer-api.txt b/bluez/repo/doc/._thermometer-api.txt
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/doc/._thermometer-api.txt
Binary files differ
diff --git a/bluez/repo/doc/adapter-api.txt b/bluez/repo/doc/adapter-api.txt
new file mode 100644
index 0000000..284214e
--- /dev/null
+++ b/bluez/repo/doc/adapter-api.txt
@@ -0,0 +1,216 @@
+BlueZ D-Bus Adapter API description
+***********************************
+
+
+Adapter hierarchy
+=================
+
+Service		org.bluez
+Interface	org.bluez.Adapter1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void StartDiscovery()
+
+			This method starts the device discovery session. This
+			includes an inquiry procedure and remote device name
+			resolving. Use StopDiscovery to release the sessions
+			acquired.
+
+			This process will start creating Device objects as
+			new devices are discovered.
+
+			During discovery RSSI delta-threshold is imposed.
+
+			Possible errors: org.bluez.Error.NotReady
+					 org.bluez.Error.Failed
+
+		void StopDiscovery()
+
+			This method will cancel any previous StartDiscovery
+			transaction.
+
+			Note that a discovery procedure is shared between all
+			discovery sessions thus calling StopDiscovery will only
+			release a single session.
+
+			Possible errors: org.bluez.Error.NotReady
+					 org.bluez.Error.Failed
+					 org.bluez.Error.NotAuthorized
+
+		void RemoveDevice(object device)
+
+			This removes the remote device object at the given
+			path. It will remove also the pairing information.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void SetDiscoveryFilter(dict filter) [Experimental]
+
+			This method sets the device discovery filter for the
+			caller. When this method is called with no filter
+			parameter, filter is removed.
+
+			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.
+
+			When discovery filter is set, Device objects will be
+			created as new devices with matching criteria are
+			discovered. 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
+			client must check that device updates actually match
+			its filter.
+
+			When SetDiscoveryFilter is called multiple times by the
+			same client, last filter passed will be active for
+			given client.
+
+			SetDiscoveryFilter can be called before StartDiscovery.
+			It is useful when client will create first discovery
+			session, to ensure that proper scan will be started
+			right after call to StartDiscovery.
+
+			Possible errors: org.bluez.Error.NotReady
+					 org.bluez.Error.Failed
+
+Properties	string Address [readonly]
+
+			The Bluetooth device address.
+
+		string Name [readonly]
+
+			The Bluetooth system name (pretty hostname).
+
+			This property is either a static system default
+			or controlled by an external daemon providing
+			access to the pretty hostname configuration.
+
+		string Alias [readwrite]
+
+			The Bluetooth friendly name. This value can be
+			changed.
+
+			In case no alias is set, it will return the system
+			provided name. Setting an empty string as alias will
+			convert it back to the system provided name.
+
+			When resetting the alias with an empty string, the
+			property will default back to system name.
+
+			On a well configured system, this property never
+			needs to be changed since it defaults to the system
+			name and provides the pretty hostname. Only if the
+			local name needs to be different from the pretty
+			hostname, this property should be used as last
+			resort.
+
+		uint32 Class [readonly]
+
+			The Bluetooth class of device.
+
+			This property represents the value that is either
+			automatically configured by DMI/ACPI information
+			or provided as static configuration.
+
+		boolean Powered [readwrite]
+
+			Switch an adapter on or off. This will also set the
+			appropriate connectable state of the controller.
+
+			The value of this property is not persistent. After
+			restart or unplugging of the adapter it will reset
+			back to false.
+
+		boolean Discoverable [readwrite]
+
+			Switch an adapter to discoverable or non-discoverable
+			to either make it visible or hide it. This is a global
+			setting and should only be used by the settings
+			application.
+
+			If the DiscoverableTimeout is set to a non-zero
+			value then the system will set this value back to
+			false after the timer expired.
+
+			In case the adapter is switched off, setting this
+			value will fail.
+
+			When changing the Powered property the new state of
+			this property will be updated via a PropertiesChanged
+			signal.
+
+			For any new adapter this settings defaults to false.
+
+		boolean Pairable [readwrite]
+
+			Switch an adapter to pairable or non-pairable. This is
+			a global setting and should only be used by the
+			settings application.
+
+			Note that this property only affects incoming pairing
+			requests.
+
+			For any new adapter this settings defaults to true.
+
+		uint32 PairableTimeout [readwrite]
+
+			The pairable timeout in seconds. A value of zero
+			means that the timeout is disabled and it will stay in
+			pairable mode forever.
+
+			The default value for pairable timeout should be
+			disabled (value 0).
+
+		uint32 DiscoverableTimeout [readwrite]
+
+			The discoverable timeout in seconds. A value of zero
+			means that the timeout is disabled and it will stay in
+			discoverable/limited mode forever.
+
+			The default value for the discoverable timeout should
+			be 180 seconds (3 minutes).
+
+		boolean Discovering [readonly]
+
+			Indicates that a device discovery procedure is active.
+
+		array{string} UUIDs [readonly]
+
+			List of 128-bit UUIDs that represents the available
+			local services.
+
+		string Modalias [readonly, optional]
+
+			Local Device ID information in modalias format
+			used by the kernel and udev.
diff --git a/bluez/repo/doc/advertising-api.txt b/bluez/repo/doc/advertising-api.txt
new file mode 100644
index 0000000..4ee37bb
--- /dev/null
+++ b/bluez/repo/doc/advertising-api.txt
@@ -0,0 +1,106 @@
+BlueZ D-Bus LE Advertising API Description
+******************************************
+
+Advertising packets are structured data which is broadcast on the LE Advertising
+channels and available for all devices in range.  Because of the limited space
+available in LE Advertising packets (32 bytes), each packet's contents must be
+carefully controlled.
+
+BlueZ acts as a store for the Advertisement Data which is meant to be sent.
+It constructs the correct Advertisement Data from the structured
+data and configured the kernel to send the correct advertisement.
+
+Advertisement Data objects are registered freely and then referenced by BlueZ
+when constructing the data sent to the kernel.
+
+LE Advertisement Data hierarchy
+===============================
+
+Specifies the Advertisement Data to be broadcast and some advertising
+parameters.  Properties which are not present will not be included in the
+data.  Required advertisement data types will always be included.
+All UUIDs are 128-bit versions in the API, and 16 or 32-bit
+versions of the same UUID will be used in the advertising data as appropriate.
+
+Service		org.bluez
+Interface	org.bluez.LEAdvertisement1
+Object path	freely definable
+
+Methods		void Release() [noreply]
+
+			This method gets called when the service daemon
+			removes the Advertisement. A client can use it to do
+			cleanup tasks. There is no need to call
+			UnregisterAdvertisement because when this method gets
+			called it has already been unregistered.
+
+Properties	string Type
+
+			Determines the type of advertising packet requested.
+
+			Possible values: "broadcast" or "peripheral"
+
+		array{string} ServiceUUIDs
+
+			List of UUIDs to include in the "Service UUID" field of
+			the Advertising Data.
+
+		dict ManufacturerData
+
+			Manufactuer Data fields to include in
+			the Advertising Data.  Keys are the Manufacturer ID
+			to associate with the data.
+
+		array{string} SolicitUUIDs
+
+			Array of UUIDs to include in "Service Solicitation"
+			Advertisement Data.
+
+		dict ServiceData
+
+			Service Data elements to include. The keys are the
+			UUID to associate with the data.
+
+		bool IncludeTxPower
+
+			Includes the Tx Power in the advertising packet.
+			If missing, the Tx Power is not included.
+
+
+LE Advertising Manager hierarchy
+================================
+
+The Advertising Manager allows external applications to register Advertisement
+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]
+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
+			provided generates a data packet which is too long.
+			The properties of this object are parser 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
+			an AlreadyExists error.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.AlreadyExists
+					 org.bluez.Error.InvalidLength
+
+		UnregisterAdvertisement(object advertisement)
+
+			This unregisters an advertisement that has been
+			prevously 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
diff --git a/bluez/repo/doc/agent-api.txt b/bluez/repo/doc/agent-api.txt
new file mode 100644
index 0000000..801ccb6
--- /dev/null
+++ b/bluez/repo/doc/agent-api.txt
@@ -0,0 +1,182 @@
+BlueZ D-Bus Agent API description
+**********************************
+
+
+Agent Manager hierarchy
+=======================
+
+Service		org.bluez
+Interface	org.bluez.AgentManager1
+Object path	/org/bluez
+
+		void RegisterAgent(object agent, string capability)
+
+			This registers an agent handler.
+
+			The object path defines the path of the agent
+			that will be called when user input is needed.
+
+			Every application can register its own agent and
+			for all actions triggered by that application its
+			agent is used.
+
+			It is not required by an application to register
+			an agent. If an application does chooses to not
+			register an agent, the default agent is used. This
+			is on most cases a good idea. Only application
+			like a pairing wizard should register their own
+			agent.
+
+			An application can only register one agent. Multiple
+			agents per application is not supported.
+
+			The capability parameter can have the values
+			"DisplayOnly", "DisplayYesNo", "KeyboardOnly",
+			"NoInputNoOutput" and "KeyboardDisplay" which
+			reflects the input and output capabilities of the
+			agent.
+
+			If an empty string is used it will fallback to
+			"KeyboardDisplay".
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.AlreadyExists
+
+		void UnregisterAgent(object agent)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+			Possible errors: org.bluez.Error.DoesNotExist
+
+		void RequestDefaultAgent(object agent)
+
+			This requests is to make the application agent
+			the default agent. The application is required
+			to register an agent.
+
+			Special permission might be required to become
+			the default agent.
+
+			Possible errors: org.bluez.Error.DoesNotExist
+
+
+Agent hierarchy
+===============
+
+Service		unique name
+Interface	org.bluez.Agent1
+Object path	freely definable
+
+Methods		void Release()
+
+			This method gets called when the service daemon
+			unregisters the agent. An agent can use it to do
+			cleanup tasks. There is no need to unregister the
+			agent, because when this method gets called it has
+			already been unregistered.
+
+		string RequestPinCode(object device)
+
+			This method gets called when the service daemon
+			needs to get the passkey for an authentication.
+
+			The return value should be a string of 1-16 characters
+			length. The string can be alphanumeric.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		void DisplayPinCode(object device, string pincode)
+
+			This method gets called when the service daemon
+			needs to display a pincode for an authentication.
+
+			An empty reply should be returned. When the pincode
+			needs no longer to be displayed, the Cancel method
+			of the agent will be called.
+
+			This is used during the pairing process of keyboards
+			that don't support Bluetooth 2.1 Secure Simple Pairing,
+			in contrast to DisplayPasskey which is used for those
+			that do.
+
+			This method will only ever be called once since
+			older keyboards do not support typing notification.
+
+			Note that the PIN will always be a 6-digit number,
+			zero-padded to 6 digits. This is for harmony with
+			the later specification.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		uint32 RequestPasskey(object device)
+
+			This method gets called when the service daemon
+			needs to get the passkey for an authentication.
+
+			The return value should be a numeric value
+			between 0-999999.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		void DisplayPasskey(object device, uint32 passkey,
+								uint16 entered)
+
+			This method gets called when the service daemon
+			needs to display a passkey for an authentication.
+
+			The entered parameter indicates the number of already
+			typed keys on the remote side.
+
+			An empty reply should be returned. When the passkey
+			needs no longer to be displayed, the Cancel method
+			of the agent will be called.
+
+			During the pairing process this method might be
+			called multiple times to update the entered value.
+
+			Note that the passkey will always be a 6-digit number,
+			so the display should be zero-padded at the start if
+			the value contains less than 6 digits.
+
+		void RequestConfirmation(object device, uint32 passkey)
+
+			This method gets called when the service daemon
+			needs to confirm a passkey for an authentication.
+
+			To confirm the value it should return an empty reply
+			or an error in case the passkey is invalid.
+
+			Note that the passkey will always be a 6-digit number,
+			so the display should be zero-padded at the start if
+			the value contains less than 6 digits.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		void RequestAuthorization(object device)
+
+			This method gets called to request the user to
+			authorize an incoming pairing attempt which
+			would in other circumstances trigger the just-works
+			model.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		void AuthorizeService(object device, string uuid)
+
+			This method gets called when the service daemon
+			needs to authorize a connection/service request.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		void Cancel()
+
+			This method gets called to indicate that the agent
+			request failed before a reply was returned.
diff --git a/bluez/repo/doc/alert-api.txt b/bluez/repo/doc/alert-api.txt
new file mode 100644
index 0000000..fc427c8
--- /dev/null
+++ b/bluez/repo/doc/alert-api.txt
@@ -0,0 +1,108 @@
+BlueZ D-Bus Alert API description
+*********************************
+
+
+Introduction
+------------
+
+Currently, there are two different GATT server profiles that depend on
+receiving alerts or notifications from the platform: Phone Alert Status (PASP)
+and Alert Notification (ANP). PASP is very specific to mobile phones, and also
+allows limited control to alerts (i.e. mute once or switch to a silent mode).
+
+This document presents a unified API that allows to register and notify alerts,
+and to control some alerts (using the provided agent object).
+
+
+Alert hierarchy
+===============
+
+Service		org.bluez
+Interface	org.bluez.Alert1
+Object path	/org/bluez
+
+Methods		void RegisterAlert(string category, object agent)
+
+			Register a new alert category and an agent for it. This
+			means the application will be responsible for notifying
+			BlueZ of any alerts of that category, using the
+			NewAlert() method.
+
+			Supported ANP categories: simple, email, news, call,
+				missed-call, sms-mms, voice-mail, schedule,
+				high-priority, instant-message
+			Supported PASP categories: ringer, vibrate, display
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		void NewAlert(string category, uint16 count, string description)
+
+			Notify BlueZ of new alert(s) for the given category. The
+			description is relative to the last received alert and
+			can be sender name, caller ID, title, or other
+			information specific to the category.
+
+			For ringer, vibrate and display categories, valid
+			descriptions are "active" and "not active". Alerts from
+			ringer category also accept "enabled" and "disabled",
+			depending on whether ringer is in silent mode or not.
+
+			Description must not exceed 18 bytes when encoded in
+			UTF-8 format, otherwise an error is returned. If there
+			is no description, an empty string should be used.
+
+			The count argument contains the number of alerts not
+			yet acknowledged by the user on the UI. To save D-Bus
+			traffic, events that may generate multiple alerts at
+			the same time (like email, sms, news) should trigger a
+			single NewAlert().
+
+			If there are more than 254 new alerts, count must be
+			set to 255. PASP alerts should always set count to 1.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		void UnreadAlert(string category, uint16 count)
+
+			Some services (like SMS and e-mail) keep track of
+			number of unread items. This method allows to update
+			this counter, so peer devices can be notified using
+			Alert Notification Profile.
+
+			If there are more than 254 unread alerts, count must be
+			set to 255.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+Alert Agent hierarchy
+=====================
+
+Service		org.bluez
+Interface	org.bluez.AlertAgent1
+Object path	freely definable
+
+Methods		void MuteOnce()
+
+			This method is only called if "ringer" alert category
+			is specified when registering the agent.
+
+			Mute the ringer once (e.g. during a incoming call). If
+			ringer is not active, does nothing.
+
+		void SetRinger(string mode)
+
+			This method is only called if "ringer" alert category
+			is specified when registering the agent.
+
+			Set ringer to the specified mode. If mode is "enabled",
+			ringer is set to the default mode, as defined by the
+			current active profile. If mode is "disabled", ringer
+			will not activate on incoming calls, until it is set
+			back to "enabled" mode.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		void Release()
+
+			Release this agent. At this point, it will not be used
+			by BlueZ anymore and can be destroyed by the owner.
diff --git a/bluez/repo/doc/assigned-numbers.txt b/bluez/repo/doc/assigned-numbers.txt
new file mode 100644
index 0000000..ca171c4
--- /dev/null
+++ b/bluez/repo/doc/assigned-numbers.txt
@@ -0,0 +1,24 @@
+RFCOMM Channels
+===============
+
+Since there are a limited amount of possible RFCOMM channels (1-31)
+they've been pre-allocated for currently known profiles in order to
+avoid conflicts.
+
+Profile		Channel
+-----------------------
+DUN		1
+HFP HF		7
+OPP		9
+FTP		10
+BIP		11
+HSP AG		12
+HFP AG		13
+SYNCH (IrMC)	14
+PBAP		15
+MAP MAS		16
+MAP MNS		17
+SyncEvolution	19
+PC/Ovi Suite	24
+SyncML Client	25
+SyncML Server	26
diff --git a/bluez/repo/doc/btmon.txt b/bluez/repo/doc/btmon.txt
new file mode 100644
index 0000000..7a7fc53
--- /dev/null
+++ b/bluez/repo/doc/btmon.txt
@@ -0,0 +1,35 @@
+BTMON(1)
+========
+:doctype: manpage
+
+
+NAME
+----
+btmon - Bluetooth monitor
+
+
+SYNOPSIS
+--------
+*btmon* ['OPTIONS']
+
+
+DESCRIPTION
+-----------
+The btmon(1) command provides access to the Bluetooth subsystem monitor
+infrastructure for reading HCI traces.
+
+
+AUTHOR
+------
+btmon was originally written by Marcel Holtmann.
+
+
+RESOURCES
+---------
+See <http://www.bluez.org/>
+
+
+COPYING
+-------
+Free use of this software is granted under ther terms of the GNU Lesser
+General Public Licenses (LGPL).
diff --git a/bluez/repo/doc/coding-style.txt b/bluez/repo/doc/coding-style.txt
new file mode 100644
index 0000000..f0bf880
--- /dev/null
+++ b/bluez/repo/doc/coding-style.txt
@@ -0,0 +1,279 @@
+BlueZ coding style
+******************
+
+Every project has its coding style, and BlueZ is not an exception. This
+document describes the preferred coding style for BlueZ code, in order to keep
+some level of consistency among developers so that code can be easily
+understood and maintained.
+
+First of all, BlueZ coding style must follow every rule for Linux kernel
+(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
+named checkpatch.pl to help you check the compliance with it. Just type
+"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
+to clean up all the warnings and errors except this one: "ERROR: Missing
+Signed-off-by: line(s)". BlueZ does not used Signed-Off lines, so including
+them is actually an error.  In certain circumstances one can ignore the 80
+character per line limit.  This is generally only allowed if the alternative
+would make the code even less readable.
+
+Besides the kernel coding style above, BlueZ has special flavors for its own.
+Some of them are mandatory (marked as 'M'), while some others are optional
+(marked as 'O'), but generally preferred.
+
+M1: Blank line before and after an if/while/do/for statement
+============================================================
+
+There should be a blank line before if statement unless the if is nested and
+not preceded by an expression or variable declaration.
+
+Example:
+1)
+a = 1;
+if (b) {  // wrong
+
+2)
+a = 1
+
+if (b) {
+}
+a = 2;	// wrong
+
+3)
+if (a) {
+	if (b)  // correct
+
+4)
+b = 2;
+
+if (a) {	// correct
+
+}
+
+b = 3;
+
+The only exception to this rule applies when a variable is being checked for
+errors as such:
+
+err = stat(filename, &st);
+if (err || !S_ISDIR(st.st_mode))
+	return;
+
+M2: Multiple line comment
+=========================
+
+If your comment has more than one line, please start it from the second line.
+
+Example:
+/*
+ * first line comment	// correct
+ * ...
+ * last line comment
+ */
+
+
+M3: Space before and after operator
+===================================
+
+There should be a space before and after each operator.
+
+Example:
+a + b;  // correct
+
+
+M4: Wrap long lines
+===================
+
+If your condition in if, while, for statement or a function declaration is too
+long to fit in one line, the new line needs to be indented not aligned with the
+body.
+
+Example:
+1)
+if ((adapter->supported_settings & MGMT_SETTING_SSP) &&
+	!(adapter->current_settings & MGMT_SETTING_SSP)) // wrong
+
+2)
+if ((adapter->supported_settings & MGMT_SETTING_SSP) &&
+				!(adapter->current_settings & MGMT_SETTING_SSP))
+
+3)
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+				 btd_adapter_pin_cb_t cb) // wrong
+
+4)
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+							btd_adapter_pin_cb_t cb)
+
+The referred style for line wrapping is to indent as far as possible to the
+right without hitting the 80 columns limit.
+
+M5: Space when doing type casting
+=================================
+
+There should be a space between new type and variable.
+
+Example:
+1)
+a = (int *)b;  // wrong
+2)
+a = (int *) b;  // correct
+
+
+M6: Don't initialize variable unnecessarily
+===========================================
+
+When declaring a variable, try not to initialize it unless necessary.
+
+Example:
+int i = 1;  // wrong
+
+for (i = 0; i < 3; i++) {
+}
+
+M7: Follow the order of include header elements
+===============================================
+
+When writing an include header the various elements should be in the following
+order:
+	- #includes
+	- forward declarations
+	- #defines
+	- enums
+	- typedefs
+	- function declarations and inline function definitions
+
+M8: Internal headers must not use include guards
+================================================
+
+Any time when creating a new header file with non-public API, that header
+must not contain include guards.
+
+M9: Naming of enums
+===================
+
+Enums must have a descriptive name.  The enum type should be small caps and
+it should not be typedef-ed.  Enum contents should be in CAPITAL letters and
+prefixed by the enum type name.
+
+Example:
+
+enum animal_type {
+	ANIMAL_TYPE_FOUR_LEGS,
+	ANIMAL_TYPE_EIGHT_LEGS,
+	ANIMAL_TYPE_TWO_LEGS,
+};
+
+If the enum contents have values (e.g. from specification) the formatting
+should be as follows:
+
+enum animal_type {
+	ANIMAL_TYPE_FOUR_LEGS =		4,
+	ANIMAL_TYPE_EIGHT_LEGS =	8,
+	ANIMAL_TYPE_TWO_LEGS =		2,
+};
+
+M10: Enum as switch variable
+============================
+
+If the variable of a switch is an enum, you must include all values in
+switch body even if providing default. This is enforced by compiler option
+enabling extra warning in such case. The reason for this is to ensure that if
+later on enum is modified and one forget to change the switch accordingly, the
+compiler will complain the new added type hasn't been handled.
+
+Example:
+
+enum animal_type {
+	ANIMAL_TYPE_FOUR_LEGS =		4,
+	ANIMAL_TYPE_EIGHT_LEGS =	8,
+	ANIMAL_TYPE_TWO_LEGS =		2,
+};
+
+enum animal_type t;
+
+switch (t) { // OK
+case ANIMAL_TYPE_FOUR_LEGS:
+	...
+	break;
+case ANIMAL_TYPE_EIGHT_LEGS:
+	...
+	break;
+case ANIMAL_TYPE_TWO_LEGS:
+	...
+	break;
+default:
+	break;
+}
+
+switch (t) { // Wrong
+case ANIMAL_TYPE_FOUR_LEGS:
+	...
+	break;
+case ANIMAL_TYPE_TWO_LEGS:
+	...
+	break;
+default:
+	break;
+}
+
+However if the enum comes from an external header file outside BlueZ, such as
+Android headers, we cannot make any assumption of how the enum is defined and
+this rule might not apply.
+
+M11: Always use parenthesis with sizeof
+=======================================
+
+The expression argument to the sizeof operator should always be in
+parenthesis, too.
+
+Example:
+1)
+memset(stuff, 0, sizeof(*stuff));
+
+2)
+memset(stuff, 0, sizeof *stuff); // Wrong
+
+M12: Use void if function has no parameters
+===========================================
+
+A function with no parameters must use void in the parameter list.
+
+Example:
+1)
+void foo(void)
+{
+}
+
+2)
+void foo()	// Wrong
+{
+}
+
+O1: Try to avoid complex if body
+================================
+
+It's better not to have a complicated statement for if. You may judge its
+contrary condition and return | break | continue | goto ASAP.
+
+Example:
+1)
+if (device) {  // worse
+	memset(&eir_data, 0, sizeof(eir_data));
+	if (eir_len > 0)
+		eir_parse(&eir_data, ev->eir, eir_len);
+	...
+} else {
+	error("Unable to get device object for %s", addr);
+	return;
+}
+
+2)
+if (!device) {
+	error("Unable to get device object for %s", addr);
+	return;
+}
+
+memset(&eir_data, 0, sizeof(eir_data));
+if (eir_len > 0)
+	eir_parse(&eir_data, ev->eir, eir_len);
+...
diff --git a/bluez/repo/doc/cyclingspeed-api.txt b/bluez/repo/doc/cyclingspeed-api.txt
new file mode 100644
index 0000000..a1f1a93
--- /dev/null
+++ b/bluez/repo/doc/cyclingspeed-api.txt
@@ -0,0 +1,99 @@
+Cycling Speed and Cadence API description
+*****************************************
+
+
+Cycling Speed and Cadence Manager hierarchy
+===========================================
+
+Service		org.bluez
+Interface	org.bluez.CyclingSpeedManager1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		RegisterWatcher(object agent)
+
+			Registers a watcher to monitor cycling speed and
+			cadence measurements.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		UnregisterWatcher(object agent)
+
+			Unregisters a watcher.
+
+Cycling Speed and Cadence Profile hierarchy
+===========================================
+
+Service		org.bluez
+Interface	org.bluez.CyclingSpeed1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		SetCumulativeWheelRevolutions(uint32 value)
+
+			Sets cumulative wheel revolutions value if
+			Cumulative Wheel Revolutions feature is supported.
+
+			Possible Errors: org.bluez.Error.NotSupported
+
+Properties	string Location (optional) [readwrite]
+
+			Current sensor location, if supported.
+			If Multiple Sensor Locations feature is supported,
+			this property can be set to one of values read from
+			SupportedLocations property.
+
+			Possible values: "other", "top-of-shoe", "in-shoe",
+					"hip", "front-wheel", "left-crank",
+					"right-crank", "left-pedal",
+					"right-pedal", "front-hub",
+					"rear-dropout", "chainstay",
+					"rear-wheel", "rear-hub"
+
+		array{string} SupportedLocations (optional) [readonly]
+
+			List of locations supported by sensor, only present
+			if Multiple Sensor Locations feature is supported.
+
+		boolean WheelRevolutionDataSupported [readonly]
+
+			true if sensor can read and set Cumulative Wheel
+			Revolutions value, false otherwise.
+
+		boolean MultipleLocationsSupported [readonly]
+
+			true if sensor supports Multiple Sensor Locations
+			feature and can set Location, false otherwise.
+
+Cycling Speed and Cadence Watcher hierarchy
+===========================================
+
+Service		unique name
+Interface	org.bluez.CyclingSpeedWatcher1
+Object path	freely definable
+
+Methods		void MeasurementReceived(object device, dict measurement)
+
+			This callback is called whenever wheel and/or crank
+			revolutions measurement is received from sensor.
+
+			Measurement:
+
+				uint32 WheelRevolutions (optional):
+
+					Cumulative number of wheel revolutions.
+
+				uint16 LastWheelEventTime (optional):
+
+					Time of last event from wheel sensor.
+					Value is expressed in 1/1024 second
+					units and can roll over during a ride.
+
+				uint16 CrankRevolutions (optional):
+
+					Cumulative number of crank revolutions.
+					This value can occasionally roll over.
+
+				uint16 LastCrankEventTime (optional):
+
+					Time of last event from crank sensor.
+					Value is expressed in 1/1024 second
+					units and can roll over during a ride.
diff --git a/bluez/repo/doc/device-api.txt b/bluez/repo/doc/device-api.txt
new file mode 100644
index 0000000..a8076a2
--- /dev/null
+++ b/bluez/repo/doc/device-api.txt
@@ -0,0 +1,221 @@
+BlueZ D-Bus Device API description
+**********************************
+
+
+Device hierarchy
+================
+
+Service		org.bluez
+Interface	org.bluez.Device1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		void Connect()
+
+			This is a generic method to connect any profiles
+			the remote device supports that can be connected
+			to and have been flagged as auto-connectable on
+			our side. If only subset of profiles is already
+			connected it will try to connect currently disconnected
+			ones.
+
+			If at least one profile was connected successfully this
+			method will indicate success.
+
+			Possible errors: org.bluez.Error.NotReady
+					 org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.AlreadyConnected
+
+		void Disconnect()
+
+			This method gracefully disconnects all connected
+			profiles and then terminates low-level ACL connection.
+
+			ACL connection will be terminated even if some profiles
+			were not disconnected properly e.g. due to misbehaving
+			device.
+
+			This method can be also used to cancel a preceding
+			Connect call before a reply to it has been received.
+
+			Possible errors: org.bluez.Error.NotConnected
+
+		void ConnectProfile(string uuid)
+
+			This method connects a specific profile of this
+			device. The UUID provided is the remote service
+			UUID for the profile.
+
+			Possible errors: org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotAvailable
+					 org.bluez.Error.NotReady
+
+		void DisconnectProfile(string uuid)
+
+			This method disconnects a specific profile of
+			this device. The profile needs to be registered
+			client profile.
+
+			There is no connection tracking for a profile, so
+			as long as the profile is registered this will always
+			succeed.
+
+			Possible errors: org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotSupported
+
+		void Pair()
+
+			This method will connect to the remote device,
+			initiate pairing and then retrieve all SDP records
+			(or GATT primary services).
+
+			If the application has registered its own agent,
+			then that specific agent will be used. Otherwise
+			it will use the default agent.
+
+			Only for applications like a pairing wizard it
+			would make sense to have its own agent. In almost
+			all other cases the default agent will handle
+			this just fine.
+
+			In case there is no application agent and also
+			no default agent present, this method will fail.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+					 org.bluez.Error.AlreadyExists
+					 org.bluez.Error.AuthenticationCanceled
+					 org.bluez.Error.AuthenticationFailed
+					 org.bluez.Error.AuthenticationRejected
+					 org.bluez.Error.AuthenticationTimeout
+					 org.bluez.Error.ConnectionAttemptFailed
+
+		void CancelPairing()
+
+			This method can be used to cancel a pairing
+			operation initiated by the Pair method.
+
+			Possible errors: org.bluez.Error.DoesNotExist
+					 org.bluez.Error.Failed
+
+Properties	string Address [readonly]
+
+			The Bluetooth device address of the remote device.
+
+		string Name [readonly, optional]
+
+			The Bluetooth remote name. This value can not be
+			changed. Use the Alias property instead.
+
+			This value is only present for completeness. It is
+			better to always use the Alias property when
+			displaying the devices name.
+
+			If the Alias property is unset, it will reflect
+			this value which makes it more convenient.
+
+		string Icon [readonly, optional]
+
+			Proposed icon name according to the freedesktop.org
+			icon naming specification.
+
+		uint32 Class [readonly, optional]
+
+			The Bluetooth class of device of the remote device.
+
+		uint16 Appearance [readonly, optional]
+
+			External appearance of device, as found on GAP service.
+
+		array{string} UUIDs [readonly, optional]
+
+			List of 128-bit UUIDs that represents the available
+			remote services.
+
+		boolean Paired [readonly]
+
+			Indicates if the remote device is paired.
+
+		boolean Connected [readonly]
+
+			Indicates if the remote device is currently connected.
+			A PropertiesChanged signal indicate changes to this
+			status.
+
+		boolean Trusted [readwrite]
+
+			Indicates if the remote is seen as trusted. This
+			setting can be changed by the application.
+
+		boolean Blocked [readwrite]
+
+			If set to true any incoming connections from the
+			device will be immediately rejected. Any device
+			drivers will also be removed and no new ones will
+			be probed as long as the device is blocked.
+
+		string Alias [readwrite]
+
+			The name alias for the remote device. The alias can
+			be used to have a different friendly name for the
+			remote device.
+
+			In case no alias is set, it will return the remote
+			device name. Setting an empty string as alias will
+			convert it back to the remote device name.
+
+			When resetting the alias with an empty string, the
+			property will default back to the remote name.
+
+		object Adapter [readonly]
+
+			The object path of the adapter the device belongs to.
+
+		boolean LegacyPairing [readonly]
+
+			Set to true if the device only supports the pre-2.1
+			pairing mechanism. This property is useful during
+			device discovery to anticipate whether legacy or
+			simple pairing will occur if pairing is initiated.
+
+			Note that this property can exhibit false-positives
+			in the case of Bluetooth 2.1 (or newer) devices that
+			have disabled Extended Inquiry Response support.
+
+		string Modalias [readonly, optional]
+
+			Remote Device ID information in modalias format
+			used by the kernel and udev.
+
+		int16 RSSI [readonly, optional]
+
+			Received Signal Strength Indicator of the remote
+			device (inquiry or advertising).
+
+		int16 TxPower [readonly, optional, experimental]
+
+			Advertised transmitted power level (inquiry or
+			advertising).
+
+		dict ManufacturerData [readonly, optional]
+
+			Manufacturer specific advertisement data. Keys are
+			16 bits Manufacturer ID followed by its byte array
+			value.
+
+		dict ServiceData [readonly, optional]
+
+			Service advertisement data. Keys are the UUIDs in
+			string format followed by its byte array value.
+
+		array{object} GattServices [readonly, optional]
+
+			List of GATT service object paths. Each referenced
+			object exports the org.bluez.GattService1 interface and
+			represents a remote GATT service. This property will be
+			updated once all remote GATT services of this device
+			have been discovered and exported over D-Bus.
diff --git a/bluez/repo/doc/gatt-api.txt b/bluez/repo/doc/gatt-api.txt
new file mode 100644
index 0000000..d832c73
--- /dev/null
+++ b/bluez/repo/doc/gatt-api.txt
@@ -0,0 +1,356 @@
+BlueZ D-Bus GATT API description
+********************************
+
+GATT local and remote services share the same high-level D-Bus API. Local
+refers to GATT based service exported by a BlueZ plugin or an external
+application. Remote refers to GATT services exported by the peer.
+
+BlueZ acts as a proxy, translating ATT operations to D-Bus method calls and
+Properties (or the opposite). Support for D-Bus Object Manager is mandatory for
+external services to allow seamless GATT declarations (Service, Characteristic
+and Descriptors) discovery. Each GATT service tree is required to export a D-Bus
+Object Manager at its root that is solely responsible for the objects that
+belong to that service.
+
+Releasing a registered GATT service is not defined yet. Any API extension
+should avoid breaking the defined API, and if possible keep an unified GATT
+remote and local services representation.
+
+Service hierarchy
+=================
+
+GATT remote and local service representation. Object path for local services
+is freely definable.
+
+External applications implementing local services must register the services
+using GattManager1 registration method and must implement the methods and
+properties defined in GattService1 interface.
+
+Service		org.bluez
+Interface	org.bluez.GattService1 [Experimental]
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX
+
+Properties	string UUID [read-only]
+
+			128-bit service UUID.
+
+		boolean Primary [read-only]
+
+			Indicates whether or not this GATT service is a
+			primary service. If false, the service is secondary.
+
+		object Device [read-only, optional]
+
+			Object path of the Bluetooth device the service
+			belongs to. Only present on services from remote
+			devices.
+
+		array{object} Characteristics [read-only]
+
+			Array of object paths representing the characteristics
+			of this service. This property is set only when the
+			characteristic discovery has been completed, however the
+			characteristic objects will become available via
+			ObjectManager as soon as they get discovered.
+
+		array{object} Includes [read-only]: Not implemented
+
+			Array of object paths representing the included
+			services of this service.
+
+
+Characteristic hierarchy
+========================
+
+For local GATT defined services, the object paths need to follow the service
+path hierarchy and are freely definable.
+
+Service		org.bluez
+Interface	org.bluez.GattCharacteristic1 [Experimental]
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY
+
+Methods		array{byte} ReadValue()
+
+			Issues a request to read the value of the
+			characteristic and returns the value if the
+			operation was successful.
+
+			Possible Errors: org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.NotPermitted
+					 org.bluez.Error.NotAuthorized
+					 org.bluez.Error.NotSupported
+
+		void WriteValue(array{byte} value)
+
+			Issues a request to write the value of the
+			characteristic.
+
+			Possible Errors: org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.NotPermitted
+					 org.bluez.Error.InvalidValueLength
+					 org.bluez.Error.NotAuthorized
+					 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.InProgress
+					 org.bluez.Error.NotSupported
+
+		void StopNotify()
+
+			This method will cancel any previous StartNotify
+			transaction. Note that notifications from a
+			characteristic are shared between sessions thus
+			calling StopNotify will release a single session.
+
+			Possible Errors: org.bluez.Error.Failed
+
+Properties	string UUID [read-only]
+
+			128-bit characteristic UUID.
+
+		object Service [read-only]
+
+			Object path of the GATT service the characteristc
+			belongs to.
+
+		array{byte} Value [read-only, optional]
+
+			The cached value of the characteristic. This property
+			gets updated only after a successful read request and
+			when a notification or indication is received, upon
+			which a PropertiesChanged signal will be emitted.
+
+		boolean Notifying [read-only]
+
+			True, if notifications or indications on this
+			characteristic are currently enabled.
+
+		array{string} Flags [read-only]
+
+			Defines how the characteristic value can be used. See
+			Core spec "Table 3.5: Characteristic Properties bit
+			field", and "Table 3.8: Characteristic Extended
+			Properties bit field". Allowed values:
+
+				"broadcast"
+				"read"
+				"write-without-response"
+				"write"
+				"notify"
+				"indicate"
+				"authenticated-signed-writes"
+				"reliable-write"
+				"writable-auxiliaries"
+				"encrypt-read"
+				"encrypt-write"
+				"encrypt-authenticated-read"
+				"encrypt-authenticated-write"
+
+		array{object} Descriptors [read-only]
+
+			Array of object paths representing the descriptors
+			of this service. This property is set only when the
+			descriptor discovery has been completed, however the
+			descriptor objects will become available via
+			ObjectManager as soon as they get discovered.
+
+
+Characteristic Descriptors hierarchy
+====================================
+
+Local or remote GATT characteristic descriptors hierarchy.
+
+Service		org.bluez
+Interface	org.bluez.GattDescriptor1 [Experimental]
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ
+
+Methods		array{byte} ReadValue()
+
+			Issues a request to read the value of the
+			characteristic and returns the value if the
+			operation was successful.
+
+			Possible Errors: org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.NotPermitted
+					 org.bluez.Error.NotAuthorized
+					 org.bluez.Error.NotSupported
+
+		void WriteValue(array{byte} value)
+
+			Issues a request to write the value of the
+			characteristic.
+
+			Possible Errors: org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.NotPermitted
+					 org.bluez.Error.InvalidValueLength
+					 org.bluez.Error.NotAuthorized
+					 org.bluez.Error.NotSupported
+
+Properties	string UUID [read-only]
+
+			128-bit descriptor UUID.
+
+		object Characteristic [read-only]
+
+			Object path of the GATT characteristc the descriptor
+			belongs to.
+
+		array{byte} Value [read-only, optional]
+
+			The cached value of the descriptor. This property
+			gets updated only after a successful read request, upon
+			which a PropertiesChanged signal will be emitted.
+
+		array{string} Flags [read-only]
+
+			Defines how the descriptor value can be used.
+
+			Possible values:
+
+				"read"
+				"write"
+				"encrypt-read"
+				"encrypt-write"
+				"encrypt-authenticated-read"
+				"encrypt-authenticated-write"
+
+Profile hierarcy
+================
+
+Local profile (GATT client) instance. By registering this type of object
+an application effectively indicates support for a specific GATT profile
+and requests automatic connections to be established to devices
+supporting it.
+
+Service		<application dependent>
+Interface	org.bluez.GattProfile1 [Experimental]
+Object path	<application dependent>
+
+Methods		void Release()
+
+			This method gets called when the service daemon
+			unregisters the profile. The profile can use it to
+			do cleanup tasks. There is no need to unregister the
+			profile, because when this method gets called it has
+			already been unregistered.
+
+
+GATT Manager hierarchy
+======================
+
+GATT Manager allows external applications to register GATT services and
+profiles.
+
+Registering a profile allows applications to subscribe to *remote* services.
+These must implement the GattProfile1 interface defined above.
+
+Registering a service allows applications to publish a *local* GATT service,
+which then becomes available to remote devices. A GATT service is represented by
+a D-Bus object hierarchy where the root node corresponds to a service and the
+child nodes represent characteristics and descriptors that belong to that
+service. Each node must implement one of GattService1, GattCharacteristic1,
+or GattDescriptor1 interfaces described above, based on the attribute it
+represents. Each node must also implement the standard D-Bus Properties
+interface to expose their properties. These objects collectively represent a
+GATT service definition.
+
+To make service registration simple, BlueZ requires that all objects that belong
+to a GATT service be grouped under a D-Bus Object Manager that solely manages
+the objects of that service. Hence, the standard DBus.ObjectManager interface
+must be available on the root service path. An example application hierarchy
+containing two separate GATT services may look like this:
+
+-> /com/example
+  |
+  -> /com/example/service0
+  | |   - org.freedesktop.DBus.ObjectManager
+  | |   - org.freedesktop.DBus.Properties
+  | |   - org.bluez.GattService1
+  | |
+  | -> /com/example/service0/char0
+  | |     - org.freedesktop.DBus.Properties
+  | |     - org.bluez.GattCharacteristic1
+  | |
+  | -> /com/example/service0/char1
+  |   |   - org.freedesktop.DBus.Properties
+  |   |   - org.bluez.GattCharacteristic1
+  |   |
+  |   -> /com/example/service0/char1/desc0
+  |       - org.freedesktop.DBus.Properties
+  |       - org.bluez.GattDescriptor1
+  |
+  -> /com/example/service1
+    |   - org.freedesktop.DBus.ObjectManager
+    |   - org.freedesktop.DBus.Properties
+    |   - org.bluez.GattService1
+    |
+    -> /com/example/service1/char0
+        - org.freedesktop.DBus.Properties
+        - org.bluez.GattCharacteristic1
+
+When a service is registered, BlueZ will automatically obtain information about
+all objects using the service's Object Manager. Once a service has been
+registered, the objects of a service should not be removed. If BlueZ receives an
+InterfacesRemoved signal from a service's Object Manager, it will immediately
+unregister the service. Similarly, if the application disconnects from the bus,
+all of its registered services will be automatically unregistered.
+InterfacesAdded signals will be ignored.
+
+Service		org.bluez
+Interface	org.bluez.GattManager1 [Experimental]
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void RegisterService(object service, dict options)
+
+			Registers a local GATT service hierarchy as described
+			above.
+
+			"service" object path together with the D-Bus system
+			bus connection ID define the identification of the
+			application registering a GATT based service.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.AlreadyExists
+
+		void UnregisterService(object service)
+
+			This unregisters the service that has been
+			previously registered. The object path parameter
+			must match the same value that has been used
+			on registration.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.DoesNotExist
+
+		void RegisterProfile(object profile, array{string} UUIDs,
+				     dict options)
+
+			Registers a GATT (client role) profile exported
+			under interface GattProfile1. The array of UUIDs
+			specifies the mandatory set of remote service
+			UUIDs that should all be available for the
+			remote device to match this profile. Matching
+			devices will be added to the auto-connection
+			list and connected whenever available.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.AlreadyExists
+
+		void UnregisterProfile(object profile)
+
+			This unregisters the profile that has been
+			previously registered. The object path parameter
+			must match the same value that has been used
+			on registration.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.DoesNotExist
diff --git a/bluez/repo/doc/health-api.txt b/bluez/repo/doc/health-api.txt
new file mode 100644
index 0000000..2c48ff2
--- /dev/null
+++ b/bluez/repo/doc/health-api.txt
@@ -0,0 +1,152 @@
+BlueZ D-Bus Health API description
+**********************************
+
+
+HealthManager hierarchy
+=======================
+
+Service		org.bluez
+Interface	org.bluez.HealthManager1
+Object path	/org/bluez/
+
+Methods		object CreateApplication(dict config)
+
+			Returns the path of the new registered application.
+			Application will be closed by the call or implicitly
+			when the programs leaves the bus.
+
+			config:
+				uint16 DataType:
+
+					Mandatory
+
+				string Role:
+
+					Mandatory. Possible values: "source",
+									"sink"
+
+				string Description:
+
+					Optional
+
+				ChannelType:
+
+					Optional, just for sources. Possible
+					values: "reliable", "streaming"
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		void DestroyApplication(object application)
+
+			Closes the HDP application identified by the object
+			path. Also application will be closed if the process
+			that started it leaves the bus. Only the creator of the
+			application will be able to destroy it.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotFound
+					 org.bluez.Error.NotAllowed
+
+
+HealthDevice hierarchy
+======================
+
+Service		org.bluez
+Interface	org.bluez.HealthDevice1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		boolean Echo()
+
+			Sends an echo petition to the remote service. Returns
+			True if response matches with the buffer sent. If some
+			error is detected False value is returned.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.OutOfRange
+
+		object CreateChannel(object application, string configuration)
+
+			Creates a new data channel.  The configuration should
+			indicate the channel quality of service using one of
+			this values "reliable", "streaming", "any".
+
+			Returns the object path that identifies the data
+			channel that is already connected.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.HealthError
+
+		void DestroyChannel(object channel)
+
+			Destroys the data channel object. Only the creator of
+			the channel or the creator of the HealthApplication
+			that received the data channel will be able to destroy
+			it.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotFound
+				         org.bluez.Error.NotAllowed
+
+Signals		void ChannelConnected(object channel)
+
+			This signal is launched when a new data channel is
+			created or when a known data channel is reconnected.
+
+		void ChannelDeleted(object channel)
+
+			This signal is launched when a data channel is deleted.
+
+			After this signal the data channel path will not be
+			valid and its path can be reused for future data
+			channels.
+
+Properties	object MainChannel [readonly]
+
+			The first reliable channel opened. It is needed by
+			upper applications in order to send specific protocol
+			data units. The first reliable can change after a
+			reconnection.
+
+
+HealthChannel hierarchy
+=======================
+
+Service		org.bluez
+Interface	org.bluez.HealthChannel1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/chanZZZ
+
+Only the process that created the data channel or the creator of the
+HealthApplication that received it will be able to call these methods.
+
+Methods		fd Acquire()
+
+			Returns the file descriptor for this data channel. If
+			the data channel is not connected it will also
+			reconnect.
+
+			Possible Errors: org.bluez.Error.NotConnected
+					 org.bluez.Error.NotAllowed
+
+		void Release()
+
+			Releases the fd. Application should also need to
+			close() it.
+
+			Possible Errors: org.bluez.Error.NotAcquired
+					 org.bluez.Error.NotAllowed
+
+Properties	string Type [readonly]
+
+			The quality of service of the data channel. ("reliable"
+			or "streaming")
+
+		object Device [readonly]
+
+			Identifies the Remote Device that is connected with.
+			Maps with a HealthDevice object.
+
+		object Application [readonly]
+
+			Identifies the HealthApplication to which this channel
+			is related to (which indirectly defines its role and
+			data type).
diff --git a/bluez/repo/doc/heartrate-api.txt b/bluez/repo/doc/heartrate-api.txt
new file mode 100644
index 0000000..665db12
--- /dev/null
+++ b/bluez/repo/doc/heartrate-api.txt
@@ -0,0 +1,77 @@
+Heart Rate API description
+**************************
+
+
+Heart Rate Manager hierarchy
+============================
+
+Service		org.bluez
+Interface	org.bluez.HeartRateManager1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		RegisterWatcher(object agent)
+
+			Registers a watcher to monitor heart rate measurements.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		UnregisterWatcher(object agent)
+
+			Unregisters a watcher.
+
+Heart Rate Profile hierarchy
+============================
+
+Service		org.bluez
+Interface	org.bluez.HeartRate1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		Reset()
+
+			Restart the accumulation of energy expended from zero.
+
+			Possible Errors: org.bluez.Error.NotSupported
+
+Properties	string Location (optional) [readonly]
+
+			Possible values: "other", "chest", "wrist","winger",
+					"hand", "earlobe", "foot"
+
+		boolean ResetSupported [readonly]
+
+			True if energy expended is supported.
+
+Heart Rate Watcher hierarchy
+============================
+
+Service		unique name
+Interface	org.bluez.HeartRateWatcher1
+Object path	freely definable
+
+Methods		void MeasurementReceived(object device, dict measurement)
+
+			This callback is called whenever a heart rate
+			measurement is received from the heart rate device.
+
+			Measurement:
+
+				uint16 Value:
+
+					Measurement value expressed in beats per
+					minute (bpm)
+
+				uint16 Energy (optional):
+
+					Accumulated energy expended in kilo Joules
+
+				boolean Contact (optional):
+
+					true if skin contact is detected by sensor,
+					false otherwise
+
+				array{uint16} Interval (optional):
+
+					RR-Interval values which represent the time
+					between two consecutive R waves in an ECG.
+					Values are ordered starting from oldest to
+					most recent.
diff --git a/bluez/repo/doc/input-api.txt b/bluez/repo/doc/input-api.txt
new file mode 100644
index 0000000..67da08b
--- /dev/null
+++ b/bluez/repo/doc/input-api.txt
@@ -0,0 +1,32 @@
+BlueZ D-Bus Input API description
+*********************************
+
+Input hierarchy
+===============
+
+Service		org.bluez
+Interface	org.bluez.Input1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Properties	string ReconnectMode [readonly]
+
+			Determines the Connectability mode of the HID device as
+			defined by the HID Profile specification, Section 5.4.2.
+
+			This mode is based in the two properties
+			HIDReconnectInitiate (see Section 5.3.4.6) and
+			HIDNormallyConnectable (see Section 5.3.4.14) which
+			define the following four possible values:
+
+			"none"		Device and host are not required to
+					automatically restore the connection.
+
+			"host"		Bluetooth HID host restores connection.
+
+			"device"	Bluetooth HID device restores
+					connection.
+
+			"any"		Bluetooth HID device shall attempt to
+					restore the lost connection, but
+					Bluetooth HID Host may also restore the
+					connection.
diff --git a/bluez/repo/doc/maintainer-guidelines.txt b/bluez/repo/doc/maintainer-guidelines.txt
new file mode 100644
index 0000000..21162d4
--- /dev/null
+++ b/bluez/repo/doc/maintainer-guidelines.txt
@@ -0,0 +1,114 @@
+Maintainer guidelines
+*********************
+
+This document is intended for the maintainers of the BlueZ project. It
+serves as basic guidelines for handling patch review and commit access.
+
+
+Rule 1: Keep the GIT tree clean and linear
+==========================================
+
+The bluetooth.git, bluetooth-next.git and bluez.git trees are not your
+private playground. The history is meant to be clean and linear.
+
+	- NO merges
+	- NO branches
+	- NO tags
+
+If anyone needs testing or work on a feature, clone the tree and do
+it in your own copy. The master trees are off limits.
+
+One advise to avoid any accidental errors in this area to set proper
+options in global ~/.gitconfig or local .git/config files.
+
+	[merge]
+		ff = only
+
+Violations of this rule are not acceptable. This rule is enforced. If
+in doubt ask one of the seasoned maintainers.
+
+
+Rule 2: Enforce clean commit messages
+=====================================
+
+The commit messages are required to be clean and follow style guidelines
+to be consistent.
+
+Commit messages should adhere to a 72 characters by line limit. That
+makes it easy to read them via git log in a terminal window. Exceptions
+to this rule are logs, trace or other verbatim copied information.
+
+Every commit requires full names and email addresses. No synonyms or
+nicknames are allowed. It is also important that the Outlook style
+names with lastname, firstname are not allowed. It is the maintainers
+job to ensure we get proper firstname lastname <email> authorship.
+
+It is also important that the committer itself uses a valid name and
+email address when committing patches. So ensure that either the
+global ~/.gitconfig or local .git/config provides proper values.
+
+	[user]
+		name = Peter Mustermann
+		email = peter@mustermann.de
+
+Commit messages for bluez.git shall not contain Signed-off-by
+signatures. They are not used in userspace and with that it is the
+maintainers job to ensure they do not get committed to the repository.
+
+For bluetooth.git and bluetooth-next.git The Signed-off-by process is
+used and the signatures are required.
+
+Tags like Change-Id generated from Gerrit are never acceptable. It is
+the maintainers job to ensure that these are not committed into the
+repositories.
+
+Violations of this rule create a mess in the tree that can not be
+reversed. If in doubt ask one of the seasoned maintainers.
+
+
+Rule 3: Enforce correct coding style
+====================================
+
+The coding style follows roughly the kernel coding style with any
+exceptions documented in doc/coding-style.txt.
+
+To ensure trivial white-space errors don't get committed, have the
+following in your .gitconfig:
+
+	[apply]
+		whitespace = error
+
+It can also be helpful to use the checkpatch.pl script coming with the
+Linux kernel to do some automated checking. Adding the following to your
+.git/hooks/pre-commit and .git/hooks/pre-applypatch is a simple way to
+do this:
+
+	exec git diff --cached | ~/src/linux/scripts/checkpatch.pl -q \
+		--no-tree --no-signoff --show-types \
+		--ignore CAMELCASE,NEW_TYPEDEFS,INITIALISED_STATIC -
+
+The above assumes that a kernel tree resides in ~/src/linux/.
+
+
+Rule 4: Pay extra attention to adding new files to the tree
+===========================================================
+
+New files that are added to the tree require several things to be
+verified first:
+
+	- Check that the names are acceptible with other maintainers
+	- Ensure that the file modes are correct
+	- Verify that the license & copyright headers are correct
+	- If the file is supposed to be part of the release tarball,
+	  make sure that it gets picked up by 'make dist' (particularly
+	  important for documentation or other files that are not code)
+
+
+Rule 5: Keep the mailing list in sync with the commit process
+=============================================================
+
+When applying patches, be sure to send a response to the mailing list as
+soon as the code has been pushed to the upstream tree. Usually this
+means one email per patch, however patch-sets may only have one response
+covering the entire set. If applying a subset of a patch-set clearly
+state what was applied in your response.
diff --git a/bluez/repo/doc/media-api.txt b/bluez/repo/doc/media-api.txt
new file mode 100644
index 0000000..b5ad2db
--- /dev/null
+++ b/bluez/repo/doc/media-api.txt
@@ -0,0 +1,606 @@
+BlueZ D-Bus Media API description
+*********************************
+
+
+Media hierarchy
+===============
+
+Service		org.bluez
+Interface	org.bluez.Media1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void RegisterEndpoint(object endpoint, dict properties)
+
+			Register a local end point to sender, the sender can
+			register as many end points as it likes.
+
+			Note: If the sender disconnects the end points are
+			automatically unregistered.
+
+			possible properties:
+
+				string UUID:
+
+					UUID of the profile which the endpoint
+					is for.
+
+				byte Codec:
+
+					Assigned number of codec that the
+					endpoint implements. The values should
+					match the profile specification which
+					is indicated by the UUID.
+
+				array{byte} Capabilities:
+
+					Capabilities blob, it is used as it is
+					so the size and byte order must match.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotSupported - emitted
+					 when interface for the end-point is
+					 disabled.
+
+		void UnregisterEndpoint(object endpoint)
+
+			Unregister sender end point.
+
+		void RegisterPlayer(object player, dict properties)
+
+			Register a media player object to sender, the sender
+			can register as many objects as it likes.
+
+			Object must implement at least
+			org.mpris.MediaPlayer2.Player as defined in MPRIS 2.2
+			spec:
+
+			http://specifications.freedesktop.org/mpris-spec/latest/
+
+			Note: If the sender disconnects its objects are
+			automatically unregistered.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotSupported
+
+		void UnregisterPlayer(object player)
+
+			Unregister sender media player.
+
+
+Media Control hierarchy
+=======================
+
+Service		org.bluez
+Interface	org.bluez.MediaControl1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		void Play() [Deprecated]
+
+			Resume playback.
+
+		void Pause() [Deprecated]
+
+			Pause playback.
+
+		void Stop() [Deprecated]
+
+			Stop playback.
+
+		void Next() [Deprecated]
+
+			Next item.
+
+		void Previous() [Deprecated]
+
+			Previous item.
+
+		void VolumeUp() [Deprecated]
+
+			Adjust remote volume one step up
+
+		void VolumeDown() [Deprecated]
+
+			Adjust remote volume one step down
+
+		void FastForward() [Deprecated]
+
+			Fast forward playback, this action is only stopped
+			when another method in this interface is called.
+
+		void Rewind() [Deprecated]
+
+			Rewind playback, this action is only stopped
+			when another method in this interface is called.
+
+Properties
+
+		boolean Connected [readonly]
+
+		object Player [readonly, optional]
+
+			Addressed Player object path.
+
+
+MediaPlayer1 hierarchy
+======================
+
+Service		org.bluez (Controller role)
+Interface	org.bluez.MediaPlayer1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
+
+Methods		void Play()
+
+			Resume playback.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void Pause()
+
+			Pause playback.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void Stop()
+
+			Stop playback.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void Next()
+
+			Next item.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void Previous()
+
+			Previous item.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void FastForward()
+
+			Fast forward playback, this action is only stopped
+			when another method in this interface is called.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void Rewind()
+
+			Rewind playback, this action is only stopped
+			when another method in this interface is called.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+Properties	string Equalizer [readwrite]
+
+			Possible values: "off" or "on"
+
+		string Repeat [readwrite]
+
+			Possible values: "off", "singletrack", "alltracks" or
+					"group"
+
+		string Shuffle [readwrite]
+
+			Possible values: "off", "alltracks" or "group"
+
+		string Scan [readwrite]
+
+			Possible values: "off", "alltracks" or "group"
+
+		string Status [readonly]
+
+			Possible status: "playing", "stopped", "paused",
+					"forward-seek", "reverse-seek"
+					or "error"
+
+		uint32 Position [readonly]
+
+			Playback position in milliseconds. Changing the
+			position may generate additional events that will be
+			sent to the remote device. When position is 0 it means
+			the track is starting and when it's greater than or
+			equal to track's duration the track has ended. Note
+			that even if duration is not available in metadata it's
+			possible to signal its end by setting position to the
+			maximum uint32 value.
+
+		dict Track [readonly]
+
+			Track metadata.
+
+			Possible values:
+
+				string Title:
+
+					Track title name
+
+				string Artist:
+
+					Track artist name
+
+				string Album:
+
+					Track album name
+
+				string Genre:
+
+					Track genre name
+
+				uint32 NumberOfTracks:
+
+					Number of tracks in total
+
+				uint32 TrackNumber:
+
+					Track number
+
+				uint32 Duration:
+
+					Track duration in milliseconds
+
+		object Device [readonly]
+
+			Device object path.
+
+		string Name [readonly]
+
+			Player name
+
+		string Type [readonly]
+
+			Player type
+
+			Possible values:
+
+				"Audio"
+				"Video"
+				"Audio Broadcasting"
+				"Video Broadcasting"
+
+		string Subtype [readonly]
+
+			Player subtype
+
+			Possible values:
+
+				"Audio Book"
+				"Podcast"
+
+		boolean Browsable [readonly]
+
+			If present indicates the player can be browsed using
+			MediaFolder interface.
+
+			Possible values:
+
+				True: Supported and active
+				False: Supported but inactive
+
+			Note: If supported but inactive clients can enable it
+			by using MediaFolder interface but it might interfere
+			in the playback of other players.
+
+
+		boolean Searchable [readonly]
+
+			If present indicates the player can be searched using
+			MediaFolder interface.
+
+			Possible values:
+
+				True: Supported and active
+				False: Supported but inactive
+
+			Note: If supported but inactive clients can enable it
+			by using MediaFolder interface but it might interfere
+			in the playback of other players.
+
+		object Playlist
+
+			Playlist object path.
+
+MediaFolder1 hierarchy
+======================
+
+Service		unique name (Target role)
+		org.bluez (Controller role)
+Interface	org.bluez.MediaFolder1
+Object path	freely definable (Target role)
+		[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
+		(Controller role)
+
+Methods		object Search(string value, dict filter)
+
+			Return a folder object containing the search result.
+
+			To list the items found use the folder object returned
+			and pass to ChangeFolder.
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		array{objects, properties} ListItems(dict filter)
+
+			Return a list of items found
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void ChangeFolder(object folder)
+
+			Change current folder.
+
+			Note: By changing folder the items of previous folder
+			might be destroyed and have to be listed again, the
+			exception is NowPlaying folder which should be always
+			present while the player is active.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+Properties	uint32 NumberOfItems [readonly]
+
+			Number of items in the folder
+
+		string Name [readonly]
+
+			Folder name:
+
+			Possible values:
+				"/Filesystem/...": Filesystem scope
+				"/NowPlaying/...": NowPlaying scope
+
+			Note: /NowPlaying folder might not be listed if player
+			is stopped, folders created by Search are virtual so
+			once another Search is perform or the folder is
+			changed using ChangeFolder it will no longer be listed.
+
+Filters		uint32 Start:
+
+			Offset of the first item.
+
+			Default value: 0
+
+		uint32 End:
+
+			Offset of the last item.
+
+			Default value: NumbeOfItems
+
+		array{string} Attributes
+
+			Item properties that should be included in the list.
+
+			Possible Values:
+
+				"title", "artist", "album", "genre",
+				"number-of-tracks", "number", "duration"
+
+			Default Value: All
+
+MediaItem1 hierarchy
+====================
+
+Service		unique name (Target role)
+		org.bluez (Controller role)
+Interface	org.bluez.MediaItem1
+Object path	freely definable (Target role)
+		[variable
+		prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX/itemX
+		(Controller role)
+
+Methods		void Play()
+
+			Play item
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+		void AddtoNowPlaying()
+
+			Add item to now playing list
+
+			Possible Errors: org.bluez.Error.NotSupported
+					 org.bluez.Error.Failed
+
+Properties	object Player [readonly]
+
+			Player object path the item belongs to
+
+		string Name [readonly]
+
+			Item displayable name
+
+		string Type [readonly]
+
+			Item type
+
+			Possible values: "video", "audio", "folder"
+
+		string FolderType [readonly, optional]
+
+			Folder type.
+
+			Possible values: "mixed", "titles", "albums", "artists"
+
+			Available if property Type is "Folder"
+
+		boolean Playable [readonly, optional]
+
+			Indicates if the item can be played
+
+			Available if property Type is "folder"
+
+		dict Metadata [readonly]
+
+			Item metadata.
+
+			Possible values:
+
+				string Title
+
+					Item title name
+
+					Available if property Type is "audio"
+					or "video"
+
+				string Artist
+
+					Item artist name
+
+					Available if property Type is "audio"
+					or "video"
+
+				string Album
+
+					Item album name
+
+					Available if property Type is "audio"
+					or "video"
+
+				string Genre
+
+					Item genre name
+
+					Available if property Type is "audio"
+					or "video"
+
+				uint32 NumberOfTracks
+
+					Item album number of tracks in total
+
+					Available if property Type is "audio"
+					or "video"
+
+				uint32 Number
+
+					Item album number
+
+					Available if property Type is "audio"
+					or "video"
+
+				uint32 Duration
+
+					Item duration in milliseconds
+
+					Available if property Type is "audio"
+					or "video"
+
+MediaEndpoint1 hierarchy
+========================
+
+Service		unique name
+Interface	org.bluez.MediaEndpoint1
+Object path	freely definable
+
+Methods		void SetConfiguration(object transport, dict properties)
+
+			Set configuration for the transport.
+
+		array{byte} SelectConfiguration(array{byte} capabilities)
+
+			Select preferable configuration from the supported
+			capabilities.
+
+			Returns a configuration which can be used to setup
+			a transport.
+
+			Note: There is no need to cache the selected
+			configuration since on success the configuration is
+			send back as parameter of SetConfiguration.
+
+		void ClearConfiguration(object transport)
+
+			Clear transport configuration.
+
+		void Release()
+
+			This method gets called when the service daemon
+			unregisters the endpoint. An endpoint can use it to do
+			cleanup tasks. There is no need to unregister the
+			endpoint, because when this method gets called it has
+			already been unregistered.
+
+
+MediaTransport1 hierarchy
+=========================
+
+Service		org.bluez
+Interface	org.bluez.MediaTransport1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX
+
+Methods		fd, uint16, uint16 Acquire()
+
+			Acquire transport file descriptor and the MTU for read
+			and write respectively.
+
+			Possible Errors: org.bluez.Error.NotAuthorized
+					 org.bluez.Error.Failed
+
+		fd, uint16, uint16 TryAcquire()
+
+			Acquire transport file descriptor only if the transport
+			is in "pending" state at the time the message is
+			received by BlueZ. Otherwise no request will be sent
+			to the remote device and the function will just fail
+			with org.bluez.Error.NotAvailable.
+
+			Possible Errors: org.bluez.Error.NotAuthorized
+					 org.bluez.Error.Failed
+					 org.bluez.Error.NotAvailable
+
+		void Release()
+
+			Releases file descriptor.
+
+Properties	object Device [readonly]
+
+			Device object which the transport is connected to.
+
+		string UUID [readonly]
+
+			UUID of the profile which the transport is for.
+
+		byte Codec [readonly]
+
+			Assigned number of codec that the transport support.
+			The values should match the profile specification which
+			is indicated by the UUID.
+
+		array{byte} Configuration [readonly]
+
+			Configuration blob, it is used as it is so the size and
+			byte order must match.
+
+		string State [readonly]
+
+			Indicates the state of the transport. Possible
+			values are:
+				"idle": not streaming
+				"pending": streaming but not acquired
+				"active": streaming and acquired
+
+		uint16 Delay [readwrite]
+
+			Optional. Transport delay in 1/10 of millisecond, this
+			property is only writeable when the transport was
+			acquired by the sender.
+
+		uint16 Volume [readwrite]
+
+			Optional. Indicates volume level of the transport,
+			this property is only writeable when the transport was
+			acquired by the sender.
+
+			Possible Values: 0-127
diff --git a/bluez/repo/doc/mgmt-api.txt b/bluez/repo/doc/mgmt-api.txt
new file mode 100644
index 0000000..d5b6c90
--- /dev/null
+++ b/bluez/repo/doc/mgmt-api.txt
@@ -0,0 +1,3646 @@
+Bluetooth Management API
+*************************
+
+Copyright (C) 2008-2009  Marcel Holtmann <marcel@holtmann.org>
+
+
+Overview
+========
+
+This document describes the format of data used for communicating with
+the kernel using a so-called Bluetooth Management sockets. These sockets
+are available starting with Linux kernel version 3.4
+
+The following kernel versions introduced new commands, new events or
+important fixes to the Bluetooth Management API:
+
+Linux kernel v3.4	Version 1.0
+Linux kernel v3.5	Version 1.1
+Linux kernel v3.7	Version 1.2
+Linux kernel v3.9	Version 1.3
+Linux kernel v3.13	Version 1.4
+Linux kernel v3.15	Version 1.5
+Linux kernel v3.16	Version 1.6
+Linux kernel v3.17	Version 1.7
+Linux kernel v3.19	Version 1.8
+Linux kernel v4.1	Version 1.9
+Linux kernel v4.2	Version 1.10
+Linux kernel v4.5	Version 1.11 (not yet released)
+
+Version 1.1 introduces Set Device ID command.
+
+Version 1.2 introduces Passkey Notify event.
+
+Version 1.3 does not introduce any new command or event.
+
+Version 1.4 introduces Set Advertising, Set BR/EDR, Set Static Address
+and Set Scan Parameters commands. The existing Set Discoverable command
+gained an extra setting for limited discoverable mode. The device name
+is now provided in the scan response data for Low Energy.
+
+Version 1.5 introduces Set Secure Connections, Set Debug Keys, Set Privacy
+and Load Identity Resolving Keys commands. It also introduces New Identity
+Resolving Key and New Signature Resolving Key events.
+
+Version 1.6 introduces Get Connection Information command. It also updates
+the Device Found event to combine advertising data and scan response data
+into a single event.
+
+Version 1.7 introduces Get Clock Information, Add Device, Remove Device,
+Load Connection Parameters, Read Unconfigured Index List, Read Controller
+Configuration Information, Set External Configuration and Set Public Address
+commands. It also introduces Device Added, Device Removed, New Connection
+Parameter, Unconfigured Index Added, Unconfigured Index Removed and New
+Configuration Options events. The existing Set Debug Keys command gained
+an extra setting for enabling SSP debug mode.
+
+Version 1.8 introduces Start Service Discovery command. It also adds new
+Long Term Key types for LE Secure Connection feature.
+
+Version 1.9 introduces Read Local Out Of Band Extended, Data, Read Extended
+Controller Index List, Read Advertising Features, Add Advertising and Remove
+Advertising commands. It also introduces Extended Index Added, Extended Index
+Removed, Local Out Of Band Extended Data Updated, Advertising Added and
+Advertising Removed events. The existing Set Advertising command gained an
+extra setting for enabling undirected connectable advertising. It provides
+support for a new static address setting and allows the usage of Set Fast
+Connectable when controller is powered off.
+
+Version 1.10 does not introduce any new command or event. It extends the
+advertising feature to support 5 parallel advertising instances.
+
+Version 1.11 introduces Get Advertising Size Information command.
+
+
+Example
+=======
+
+The Bluetooth management sockets can be created by setting the hci_channel
+member of struct sockaddr_hci to HCI_CHANNEL_CONTROL (3) when creating a
+raw HCI socket. In C the needed code would look something like the following:
+
+int mgmt_create(void)
+{
+	struct sockaddr_hci addr;
+	int fd;
+
+	fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                                                BTPROTO_HCI);
+	if (fd < 0)
+		return -errno;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	addr.hci_channel = HCI_CHANNEL_CONTROL;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		int err = -errno;
+		close(fd);
+		return err;
+	}
+
+	return fd;
+}
+
+The process creating the mgmt socket is required to have the
+CAP_NET_ADMIN capability (e.g. root would have this).
+
+
+Packet Structures
+=================
+
+	Commands:
+
+	0    4    8   12   16   22   24   28   31   35   39   43   47
+	+-------------------+-------------------+-------------------+
+	|  Command Code     |  Controller Index |  Parameter Length |
+	+-------------------+-------------------+-------------------+
+	|                                                           |
+
+	Events:
+
+	0    4    8   12   16   22   24   28   31   35   39   43   47
+	+-------------------+-------------------+-------------------+
+	|  Event Code       |  Controller Index |  Parameter Length |
+	+-------------------+-------------------+-------------------+
+	|                                                           |
+
+All fields are in little-endian byte order (least significant byte first).
+
+Controller Index can have a special value <non-controller> to indicate that
+command or event is not related to any controller. Possible values:
+
+	<controller id>		0x0000 to 0xFFFE
+	<non-controller>	0xFFFF
+
+
+Error Codes
+===========
+
+The following values have been defined for use with the Command Status
+and Command Complete events:
+
+0x00	Success
+0x01	Unknown Command
+0x02	Not Connected
+0x03	Failed
+0x04	Connect Failed
+0x05	Authentication Failed
+0x06	Not Paired
+0x07	No Resources
+0x08	Timeout
+0x09	Already Connected
+0x0A	Busy
+0x0B	Rejected
+0x0C	Not Supported
+0x0D	Invalid Parameters
+0x0E	Disconnected
+0x0F	Not Powered
+0x10	Cancelled
+0x11	Invalid Index
+0x12	RFKilled
+0x13	Already Paired
+0x14	Permission Denied
+
+As a general rule all commands generate the events as specified below,
+however invalid lengths or unknown commands will always generate a
+Command Status response (with Unknown Command or Invalid Parameters
+status). Sending a command with an invalid Controller Index value will
+also always generate a Command Status event with the Invalid Index
+status code.
+
+
+Read Management Version Information Command
+===========================================
+
+	Command Code:		0x0001
+	Controller Index:	<non-controller>
+	Command Parameters:
+	Return Parameters:	Version (1 Octets)
+				Revision (2 Octets)
+
+	This command returns the Management version and revision.
+	Besides, being informational the information can be used to
+	determine whether certain behavior has changed or bugs fixed
+	when interacting with the kernel.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+
+Read Management Supported Commands Command
+==========================================
+
+	Command Code:		0x0002
+	Controller Index:	<non-controller>
+	Command Parameters:
+	Return Parameters:	Num_Of_Commands (2 Octets)
+				Num_Of_Events (2 Octets)
+				Command1 (2 Octets)
+				Command2 (2 Octets)
+				...
+				Event1 (2 Octets)
+				Event2 (2 Octets)
+				...
+
+	This command returns the list of supported Management commands
+	and events.
+
+	The commands Read Management Version Information and Read
+	management Supported Commands are not included in this list.
+	Both commands are always supported and mandatory.
+
+	The events Command Status and Command Complete are not included
+	in this list. Both are implicit and mandatory.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+
+Read Controller Index List Command
+==================================
+
+	Command Code:		0x0003
+	Controller Index:	<non-controller>
+	Command Parameters:
+	Return Parameters:	Num_Controllers (2 Octets)
+				Controller_Index[i] (2 Octets)
+
+	This command returns the list of currently known controllers.
+	Controllers added or removed after calling this command can be
+	monitored using the Index Added and Index Removed events.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+
+Read Controller Information Command
+===================================
+
+	Command Code:		0x0004
+	Controller Index:	<controller id>
+	Command Parameters:
+	Return Parameters:	Address (6 Octets)
+				Bluetooth_Version (1 Octet)
+				Manufacturer (2 Octets)
+				Supported_Settings (4 Octets)
+				Current_Settings (4 Octets)
+				Class_Of_Device (3 Octets)
+				Name (249 Octets)
+				Short_Name (11 Octets)
+
+	This command is used to retreive the current state and basic
+	information of a controller. It is typically used right after
+	getting the response to the Read Controller Index List command
+	or an Index Added event.
+
+	The Address parameter describes the controllers public address
+	and it can be expected that it is set. However in case of single
+	mode Low Energy only controllers it can be 00:00:00:00:00:00. To
+	power on the controller in this case, it is required to configure
+	a static address using Set Static Address command first.
+
+	If the public address is set, then it will be used as identity
+	address for the controller. If no public address is available,
+	then the configured static address will be used as identity
+	address.
+
+	In the case of a dual-mode controller with public address that
+	is configured as Low Energy only device (BR/EDR switched off),
+	the static address is used when set and public address otherwise.
+
+	If no short name is set the Short_Name parameter will be empty
+	(begin with a nul byte).
+
+	Current_Settings and Supported_Settings is a bitmask with
+	currently the following available bits:
+
+		0	Powered
+		1	Connectable
+		2	Fast Connectable
+		3	Discoverable
+		4	Bondable
+		5	Link Level Security (Sec. mode 3)
+		6	Secure Simple Pairing
+		7	Basic Rate/Enhanced Data Rate
+		8	High Speed
+		9	Low Energy
+		10	Advertising
+		11	Secure Connections
+		12	Debug Keys
+		13	Privacy
+		14	Controller Configuration
+		15	Static Address
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Set Powered Command
+===================
+
+	Command Code:		0x0005
+	Controller Index:	<controller id>
+	Command Parameters:	Powered (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to power on or off a controller. The
+	allowed Powered command parameter values are 0x00 and 0x01. All
+	other values will return Invalid Parameters.
+
+	If discoverable setting is activated with a timeout, then
+	switching the controller off will expire this timeout and
+	disable discoverable.
+
+	Settings programmed via Set Advertising and Add/Remove
+	Advertising while the controller was powered off will be activated
+	when powering the controller on.
+
+	Switching the controller off will permanently cancel and remove
+	all advertising instances with a timeout set, i.e. time limited
+	advertising instances are not being remembered across power cycles.
+	Advertising Removed events will be issued accordingly.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Invalid Parameters
+				Invalid Index
+
+
+Set Discoverable Command
+========================
+
+	Command Code:		0x0006
+	Controller Index:	<controller id>
+	Command Parameters:	Discoverable (1 Octet)
+				Timeout (2 Octets)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to set the discoverable property of a
+	controller. The allowed Discoverable command parameter values
+	are 0x00, 0x01 and 0x02. All other values will return Invalid
+	Parameters.
+
+	Timeout is the time in seconds and is only meaningful when
+	Discoverable is set to 0x01 or 0x02. Providing a timeout
+	with 0x00 return Invalid Parameters. For 0x02, the timeout
+	value is required.
+
+	The value 0x00 disables discoverable, the value 0x01 enables
+	general discoverable and the value 0x02 enables limited
+	discoverable.
+
+	This command is only available for BR/EDR capable controllers
+	(e.g. not for single-mode LE ones). It will return Not Supported
+	otherwise.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	However using a timeout when the controller is not powered will
+	return Not Powered error.
+
+	When switching discoverable on and the connectable setting is
+	off it will return Rejected error.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Rejected
+				Not Supported
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Set Connectable Command
+=======================
+
+	Command Code:		0x0007
+	Controller Index:	<controller id>
+	Command Parameters:	Connectable (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to set the connectable property of a
+	controller. The allowed Connectable command parameter values are
+	0x00 and 0x01. All other values will return Invalid Parameters.
+
+	This command is available for BR/EDR, LE-only and also dual
+	mode controllers. For BR/EDR is changes the page scan setting
+	and for LE controllers it changes the advertising type. For
+	dual mode controllers it affects both settings.
+
+	For LE capable controllers the connectable setting takes effect
+	when advertising is enabled (peripheral) or when directed
+	advertising events are received (central).
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	When switching connectable off, it will also switch off the
+	discoverable setting. Switching connectable back on will not
+	restore a previous discoverable. It will stay off and needs
+	to be manually switched back on.
+
+	When switching connectable off, it will expire a discoverable
+	setting with a timeout.
+
+	This setting does not affect known devices from Add Device
+	command. These devices are always allowed to connect.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Fast Connectable Command
+============================
+
+	Command Code:		0x0008
+	Controller Index:	<controller id>
+	Command Parameters:	Enable (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to set the controller into a connectable
+	state where the page scan parameters have been set in a way to
+	favor faster connect times with the expense of higher power
+	consumption.
+
+	The allowed values of the Enable command parameter are 0x00 and
+	0x01. All other values will return Invalid Parameters.
+
+	This command is only available for BR/EDR capable controllers
+	(e.g. not for single-mode LE ones). It will return Not Supported
+	otherwise.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	The setting will be remembered during power down/up toggles.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Failed
+				Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Bondable Command
+====================
+
+	Command Code:		0x0009
+	Controller Index:	<controller id>
+	Command Parameters:	Bondable (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to set the bondable property of an
+	controller. The allowed values for the Bondable command
+	parameter are 0x00 and 0x01. All other values will return
+	Invalid Parameters.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	Turning bondable on will not automatically switch the controller
+	into connectable mode. That needs to be done separately.
+
+	The setting will be remembered during power down/up toggles.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Set Link Security Command
+=========================
+
+	Command Code:		0x000A
+	Controller Index:	<controller id>
+	Command Parameters:	Link_Security (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to either enable or disable link level
+	security for an controller (also known as Security Mode 3). The
+	allowed values for the Link_Security command parameter are 0x00
+	and 0x01. All other values will return Invalid Parameters.
+
+	This command is only available for BR/EDR capable controllers
+	(e.g. not for single-mode LE ones). It will return Not Supported
+	otherwise.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Secure Simple Pairing Command
+=================================
+
+	Command Code:		0x000B
+	Controller Index:	<controller id>
+	Command Parameters:	Secure_Simple_Pairing (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable/disable Secure Simple Pairing
+	support for a controller. The allowed values for the
+	Secure_Simple_Pairing command parameter are 0x00 and 0x01. All
+	other values will return Invalid Parameters.
+
+	This command is only available for BR/EDR capable controllers
+	supporting the core specification version 2.1 or greater
+	(e.g. not for single-mode LE controllers or pre-2.1 ones).
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	In case the controller does not support Secure Simple Pairing,
+	the command will fail regardless with Not Supported error.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+Set High Speed Command
+======================
+
+	Command Code:		0x000C
+	Controller Index:	<controller id>
+	Command Parameters:	High_Speed (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable/disable Bluetooth High Speed
+	support for a controller. The allowed values for the High_Speed
+	command parameter are 0x00 and 0x01. All other values will
+	return Invalid Parameters.
+
+	This command is only available for BR/EDR capable controllers
+	(e.g. not for single-mode LE ones).
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	To enable High Speed support, it is required that Secure Simple
+	Pairing support is enabled first. High Speed support is not
+	possible for connections without Secure Simple Pairing.
+
+	When switching Secure Simple Pairing off, the support for High
+	Speed will be switched off as well. Switching Secure Simple
+	Pairing back on, will not re-enable High Speed support. That
+	needs to be done manually.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Low Energy Command
+======================
+
+	Command Code:		0x000D
+	Controller Index:	<controller id>
+	Command Parameters:	Low_Energy (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable/disable Low Energy support for a
+	controller. The allowed values of the Low_Energy command
+	parameter are 0x00 and 0x01. All other values will return
+	Invalid Parameters.
+
+	This command is only available for LE capable controllers and
+	will yield in a Not Supported error otherwise.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	In case the kernel subsystem does not support Low Energy or the
+	controller does not either, the command will fail regardless.
+
+	Disabling LE support will permanently disable and remove all
+	advertising instances configured with the Add Advertising
+	command. Advertising Removed events will be issued accordingly.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Device Class
+================
+
+	Command Code:		0x000E
+	Controller Index:	<controller id>
+	Command Parameters:	Major_Class (1 Octet)
+				Minor_Class (1 Octet)
+	Return Parameters:	Class_Of_Device (3 Octets)
+
+	This command is used to set the major and minor device class for
+	BR/EDR capable controllers.
+
+	This command will also implicitly disable caching of pending CoD
+	and EIR updates.
+
+	This command is only available for BR/EDR capable controllers
+	(e.g. not for single-mode LE ones).
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	In case the controller is powered off, 0x000000 will be returned
+	for the class of device parameter. And after power on the new
+	value will be announced via class of device changed event.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Local Name Command
+======================
+
+	Command Code:		0x000F
+	Controller Index:	<controller id>
+	Command Parameters:	Name (249 Octets)
+				Short_Name (11 Octets)
+	Return Parameters:	Name (249 Octets)
+				Short_Name (11 Octets)
+
+	This command is used to set the local name of a controller. The
+	command parameters also include a short name which will be used
+	in case the full name doesn't fit within EIR/AD data.
+
+	The name parameters need to always end with a null byte (failure
+	to do so will cause the command to fail).
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	The values of name and short name will be remembered when
+	switching the controller off and back on again. So the name
+	and short name only have to be set once when a new controller
+	is found and will stay until removed.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Add UUID Command
+================
+
+	Command Code:		0x0010
+	Controller Index:	<controller id>
+	Command Parameters:	UUID (16 Octets)
+				SVC_Hint (1 Octet)
+	Return Parameters:	Class_Of_Device (3 Octets)
+
+	This command is used to add a UUID to be published in EIR data.
+	The accompanied SVC_Hint parameter is used to tell the kernel
+	whether the service class bits of the Class of Device value need
+	modifying due to this UUID.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	In case the controller is powered off, 0x000000 will be returned
+	for the class of device parameter. And after power on the new
+	value will be announced via class of device changed event.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Invalid Parameters
+				Invalid Index
+
+
+Remove UUID Command
+===================
+
+	Command Code:		0x0011
+	Controller Index:	<controller id>
+	Command Parameters:	UUID (16 Octets)
+	Return Parameters:	Class_Of_Device (3 Octets)
+
+	This command is used to remove a UUID previously added using the
+	Add UUID command.
+
+	When the UUID parameter is an empty UUID (16 x 0x00), then all
+	previously loaded UUIDs will be removed.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	In case the controller is powered off, 0x000000 will be returned
+	for the class of device parameter. And after power on the new
+	value will be announced via class of device changed event.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Invalid Parameters
+				Invalid Index
+
+
+Load Link Keys Command
+======================
+
+	Command Code:		0x0012
+	Controller Index:	<controller id>
+	Command Parameters:	Debug_Keys (1 Octet)
+				Key_Count (2 Octets)
+				Key1 {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Key_Type (1 Octet)
+					Value (16 Octets)
+					PIN_Length (1 Octet)
+				}
+				Key2 { }
+				...
+	Return Parameters:
+
+	This command is used to feed the kernel with currently known
+	link keys. The command does not need to be called again upon the
+	receiption of New Link Key events since the kernel updates its
+	list automatically.
+
+	The Debug_Keys parameter is used to tell the kernel whether to
+	accept the usage of debug keys or not. The allowed values for
+	this parameter are 0x00 and 0x01. All other values will return
+	an Invalid Parameters response.
+
+	Usage of the Debug_Keys parameter is deprecated and has been
+	replaced with the Set Debug Keys command. When setting the
+	Debug_Keys option via Load Link Keys command it has the same
+	affect as setting it via Set Debug Keys and applies to all
+	keys in the system.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	Reserved (not in use)
+		2	Reserved (not in use)
+
+	Public and random LE addresses are not valid and will be rejected.
+
+	Currently defined Key_Type values are:
+
+		0x00	Combination key
+		0x01	Local Unit key
+		0x02	Remote Unit key
+		0x03	Debug Combination key
+		0x04	Unauthenticated Combination key from P-192
+		0x05	Authenticated Combination key from P-192
+		0x06	Changed Combination key
+		0x07	Unauthenticated Combination key from P-256
+		0x08	Authenticated Combination key from P-256
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Load Long Term Keys Command
+===========================
+
+	Command Code:		0x0013
+	Controller Index:	<controller id>
+	Command Parameters:	Key_Count (2 Octets)
+				Key1 {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Key_Type (1 Octet)
+					Master (1 Octet)
+					Encryption_Size (1 Octet)
+					Encryption_Diversifier (2 Octets)
+					Random_Number (8 Octets)
+					Value (16 Octets)
+				}
+				Key2 {  }
+				...
+	Return Parameters:
+
+	This command is used to feed the kernel with currently known
+	(SMP) Long Term Keys. The command does not need to be called
+	again upon the receiption of New Long Term Key events since the
+	kernel updates its list automatically.
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The provided Address and Address_Type are the identity of
+	a device. So either its public address or static random address.
+
+	Unresolvable random addresses and resolvable random addresses are
+	not valid and will be rejected.
+
+	Currently defined Key_Type values are:
+
+		0x00	Unauthenticated key
+		0x01	Authenticated key
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Disconnect Command
+==================
+
+	Command Code:		0x0014
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to force the disconnection of a currently
+	connected device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Busy
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Get Connections Command
+=======================
+
+	Command Code:		0x0015
+	Controller Index:	<controller id>
+	Command Parameters:
+	Return Parameters:	Connection_Count (2 Octets)
+				Address1 {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+				}
+				Address2 { }
+				...
+
+	This command is used to retrieve a list of currently connected
+	devices.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	For devices using resolvable random addresses with a known
+	identity resolving key, the Address and Address_Type will
+	contain the identity information.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+PIN Code Reply Command
+=======================
+
+	Command Code:		0x0016
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				PIN_Length (1 Octet)
+				PIN_Code (16 Octets)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to respond to a PIN Code request event.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+PIN Code Negative Reply Command
+===============================
+
+	Command Code:		0x0017
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to return a negative response to a PIN Code
+	Request event.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Set IO Capability Command
+=========================
+
+	Command Code:		0x0018
+	Controller Index:	<controller id>
+	Command Parameters:	IO_Capability (1 Octet)
+	Return Parameters:
+
+	This command is used to set the IO Capability used for pairing.
+	The command accepts both SSP and SMP values.
+
+	Possible values for the IO_Capability parameter:
+		0	DisplayOnly
+		1	DisplayYesNo
+		2	KeyboardOnly
+		3	NoInputNoOutput
+		4	KeyboardDisplay
+
+	Passing a value 4 (KeyboardDisplay) will cause the kernel to
+	convert it to 1 (DisplayYesNo) in the case of a BR/EDR
+	connection (as KeyboardDisplay is specific to SMP).
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Pair Device Command
+===================
+
+	Command Code:		0x0019
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				IO_Capability (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to trigger pairing with a remote device.
+	The IO_Capability command parameter is used to temporarily (for
+	this pairing event only) override the global IO Capaility (set
+	using the Set IO Capability command).
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	Possible values for the IO_Capability parameter:
+		0	DisplayOnly
+		1	DisplayYesNo
+		2	KeyboardOnly
+		3	NoInputNoOutput
+		4	KeyboardDisplay
+
+	Passing a value 4 (KeyboardDisplay) will cause the kernel to
+	convert it to 1 (DisplayYesNo) in the case of a BR/EDR
+	connection (as KeyboardDisplay is specific to SMP).
+
+	The Address and Address_Type of the return parameters will
+	return the identity address if known. In case of resolvable
+	random address given as command parameters and the remote
+	provides an identity resolving key, the return parameters
+	will provide the resolved address.
+
+	To allow tracking of which resolvable random address changed
+	into which identity address, the New Identity Resolving Key
+	event will be sent before receiving Command Complete event
+	for this command.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Reject status is used when requested transport is not enabled.
+
+	Not Supported status is used if controller is not capable with
+	requested transport.
+
+	Possible errors:	Rejected
+				Not Supported
+				Connect Failed
+				Busy
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+				Already Paired
+
+
+Cancel Pair Device Command
+==========================
+
+	Command Code:		0x001A
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	The Address and Address_Type parameters should match what was
+	given to a preceding Pair Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Unpair Device Command
+=====================
+
+	Command Code:		0x001B
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Disconnect (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	Removes all keys associated with the remote device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The Disconnect parameter tells the kernel whether to forcefully
+	disconnect any existing connections to the device. It should in
+	practice always be 1 except for some special GAP qualification
+	test-cases where a key removal without disconnecting is needed.
+
+	When unpairing a device its link key, long term key and if
+	provided identity resolving key will be purged.
+
+	For devices using resolvable random addresses where the identity
+	resolving key was available, after this command they will now no
+	longer be resolved. The device will essentially become private
+	again.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Paired
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+User Confirmation Reply Command
+===============================
+
+	Command Code:		0x001C
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to respond to a User Confirmation Request
+	event.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+User Confirmation Negative Reply Command
+========================================
+
+	Command Code:		0x001D
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to return a negative response to a User
+	Confirmation Request event.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+User Passkey Reply Command
+==========================
+
+	Command Code:		0x001E
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Passkey (4 Octets)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to respond to a User Confirmation Passkey
+	Request event.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+User Passkey Negative Reply Command
+===================================
+
+	Command Code:		0x001F
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to return a negative response to a User
+	Passkey Request event.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Not Connected
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Read Local Out Of Band Data Command
+===================================
+
+	Command Code:		0x0020
+	Controller Index:	<controller id>
+	Command Parameters:
+	Return Parameters:	Hash_192 (16 Octets)
+				Randomizer_192 (16 Octets)
+				Hash_256 (16 Octets, Optional)
+				Randomizer_256 (16 Octets, Optional)
+
+	This command is used to read the local Out of Band data.
+
+	This command can only be used when the controller is powered.
+
+	If Secure Connections support is enabled, then this command
+	will return P-192 versions of hash and randomizer as well as
+	P-256 versions of both.
+
+	Values returned by this command become invalid when the controller
+	is powered down. After each power-cycle it is required to call
+	this command again to get updated values.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Not Supported
+				Busy
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Add Remote Out Of Band Data Command
+===================================
+
+	Command Code:		0x0021
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Hash_192 (16 Octets)
+				Randomizer_192 (16 Octets)
+				Hash_256 (16 Octets, Optional)
+				Randomizer_256 (16 Octets, Optional)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to provide Out of Band data for a remote
+	device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	Provided Out Of Band data is persistent over power down/up toggles.
+
+	This command also accept optional P-256 versions of hash and
+	randomizer. If they are not provided, then they are set to
+	zero value.
+
+	The P-256 versions of both can also be provided when the
+	support for Secure Connections is not enabled. However in
+	that case they will never be used.
+
+	To only provide the P-256 versions of hash and randomizer,
+	it is valid to leave both P-192 fields as zero values. If
+	Secure Connections is disabled, then of course this is the
+	same as not providing any data at all.
+
+	When providing data for remote LE devices, then the Hash_192 and
+	and Randomizer_192 fields are not used and shell be set to zero.
+
+	The Hash_256 and Randomizer_256 fields can be used for LE secure
+	connections Out Of Band data. If only LE secure connections data
+	is provided the Hash_P192 and Randomizer_P192 fields can be set
+	to zero. Currently there is no support for providing the Security
+	Manager TK Value for LE legacy pairing.
+
+	If Secure Connections Only mode has been enabled, then providing
+	Hash_P192 and Randomizer_P192 is not allowed. They are required
+	to be set to zero values.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Failed
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Remove Remote Out Of Band Data Command
+======================================
+
+	Command Code:		0x0022
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to remove data added using the Add Remote
+	Out Of Band Data command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	When the Address parameter is 00:00:00:00:00:00, then all
+	previously added data will be removed.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Start Discovery Command
+=======================
+
+	Command Code:		0x0023
+	Controller Index:	<controller id>
+	Command Parameters:	Address_Type (1 Octet)
+	Return Parameters:	Address_Type (1 Octet)
+
+	This command is used to start the process of discovering remote
+	devices. A Device Found event will be sent for each discovered
+	device.
+
+	Possible values for the Address_Type parameter are a bit-wise or
+	of the following bits:
+
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	By combining these e.g. the following values are possible:
+
+		1	BR/EDR
+		6	LE (public & random)
+		7	BR/EDR/LE (interleaved discovery)
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Stop Discovery Command
+======================
+
+	Command Code:		0x0024
+	Controller Index:	<controller id>
+	Command Parameters:	Address_Type (1 Octet)
+	Return Parameters:	Address_Type (1 Octet)
+
+	This command is used to stop the discovery process started using
+	the Start Discovery command.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Rejected
+				Invalid Parameters
+				Invalid Index
+
+
+Confirm Name Command
+====================
+
+	Command Code:		0x0025
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Name_Known (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is only valid during device discovery and is
+	expected for each Device Found event with the Confirm Name
+	flag set.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The Name_Known parameter should be set to 0x01 if user space
+	knows the name for the device and 0x00 if it doesn't. If set to
+	0x00 the kernel will perform a name resolving procedure for the
+	device in question.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Failed
+				Invalid Parameters
+				Invalid Index
+
+
+Block Device Command
+====================
+
+	Command Code:		0x0026
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to add a device to the list of devices
+	which should be blocked from being connected to the local
+	controller.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	For Low Energy devices, the blocking of a device takes precedence
+	over auto-connection actions provided by Add Device. Blocked
+	devices will not be auto-connected or even reported when found
+	during background scanning. If the controller is connectable
+	direct advertising from blocked devices will also be ignored.
+
+	Connections created from advertising of the controller will
+	be dropped if the device is blocked.
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Failed
+				Invalid Parameters
+				Invalid Index
+
+
+Unblock Device Command
+======================
+
+	Command Code:		0x0027
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to remove a device from the list of blocked
+	devices (where it was added to using the Block Device command).
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	When the Address parameter is 00:00:00:00:00:00, then all
+	previously blocked devices will be unblocked.
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Set Device ID Command
+=====================
+
+	Command Code:		0x0028
+	Controller Index:	<controller id>
+	Command Parameters:	Source (2 Octets)
+				Vendor (2 Octets)
+				Product (2 Octets)
+				Version (2 Octets)
+	Return Parameters:
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	The Source parameter selects the organization that assigned the
+	Vendor parameter:
+
+		0x0000	Disable Device ID
+		0x0001	Bluetooth SIG
+		0x0002	USB Implementer’s Forum
+
+	The information is put into the EIR data. If the controller does
+	not support EIR or if SSP is disabled, this command will still
+	succeed. The information is stored for later use and will survive
+	toggling SSP on and off.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Set Advertising Command
+=======================
+
+	Command Code:		0x0029
+	Controller Index:	<controller id>
+	Command Parameters:	Advertising (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable LE advertising on a controller
+	that supports it. The allowed values for the Advertising command
+	parameter are 0x00, 0x01 and 0x02. All other values will return
+	Invalid Parameters.
+
+	The value 0x00 disables advertising, the value 0x01 enables
+	advertising with considering of connectable setting and the
+	value 0x02 enables advertising in connectable mode.
+
+	Using value 0x01 means that when connectable setting is disabled,
+	the advertising happens with undirected non-connectable advertising
+	packets and a non-resovable random address is used. If connectable
+	setting is enabled, then undirected connectable advertising packets
+	and the identity address or resolvable private address are used.
+
+	LE Devices configured via Add Device command with Action 0x01
+	have no effect when using Advertising value 0x01 since only the
+	connectable setting is taken into account.
+
+	To utilize undirected connectable advertising without changing the
+	connectable setting, the value 0x02 can be utilized. It makes the
+	device connectable via LE without the requirement for being
+	connectable on BR/EDR (and/or LE).
+
+	The value 0x02 should be the preferred mode of operation when
+	implementing peripheral mode.
+
+	Using this command will temporarily deactivate any configuration
+	made by the Add Advertising command. This command takes precedence.
+	Once a Set Advertising command with value 0x00 is issued any
+	previously made configurations via Add/Remove Advertising, including
+	such changes made while Set Advertising was active, will be re-
+	enabled.
+
+	A pre-requisite is that LE is already enabled, otherwise this
+	command will return a "rejected" response.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Busy
+				Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set BR/EDR Command
+==================
+
+	Command Code:		0x002A
+	Controller Index:	<controller id>
+	Command Parameters:	BR/EDR (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable or disable BR/EDR support
+	on a dual-mode controller. The allowed values for the Advertising
+	command parameter are 0x00 and 0x01. All other values will
+	return Invalid Parameters.
+
+	A pre-requisite is that LE is already enabled, otherwise
+	this command will return a "rejected" response. Enabling BR/EDR
+	can be done both when powered on and powered off, however
+	disabling it can only be done when powered off (otherwise the
+	command will again return "rejected"). Disabling BR/EDR will
+	automatically disable all other BR/EDR related settings.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Busy
+				Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Static Address Command
+==========================
+
+	Command Code:		0x002B
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command allows for setting the static random address. It is
+	only supported on controllers with LE support. The static random
+	address is suppose to be valid for the lifetime of the
+	controller or at least until the next power cycle. To ensure
+	such behavior, setting of the address is limited to when the
+	controller is powered off.
+
+	The special BDADDR_ANY address (00:00:00:00:00:00) can be used
+	to disable the static address.
+
+	When a controller has a public address (which is required for
+	all dual-mode controllers), this address is not used. If a dual-mode
+	controller is configured as Low Energy only devices (BR/EDR has
+	been switched off), then the static address is used. Only when
+	the controller information reports BDADDR_ANY (00:00:00:00:00:00),
+	it is required to configure a static address first.
+
+	If privacy mode is enabled and the controller is single mode
+	LE only without a public address, the static random address is
+	used as identity address.
+
+	The Static Address flag from the current settings can also be used
+	to determine if the configured static address is in use or not.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Scan Parameters Command
+===========================
+
+	Command Code:		0x002C
+	Controller Index:	<controller id>
+	Command Parameters:	Interval (2 Octets)
+				Window (2 Octets)
+	Return Parameters:
+
+	This command allows for setting the Low Energy scan parameters
+	used for connection establishment and passive scanning. It is
+	only supported on controllers with LE support.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Secure Connections Command
+==============================
+
+	Command Code:		0x002D
+	Controller Index:	<controller id>
+	Command Parameters:	Secure_Connections (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable/disable Secure Connections
+	support for a controller. The allowed values for the
+	Secure_Connections command parameter are 0x00, 0x01 and 0x02.
+	All other values will return Invalid Parameters.
+
+	The value 0x00 disables Secure Connections, the value 0x01
+	enables Secure Connections and the value 0x02 enables Secure
+	Connections Only mode.
+
+	This command is only available for LE capable controllers as
+	well as controllers supporting the core specification version
+	4.1 or greater.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	In case the controller does not support Secure Connections
+	the command will fail regardless with Not Supported error.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Debug Keys Command
+======================
+
+	Command Code:		0x002E
+	Controller Index:	<controller id>
+	Command Parameters:	Debug_Keys (1 Octet)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to tell the kernel whether to accept the
+	usage of debug keys or not. The allowed values for this parameter
+	are 0x00, 0x01 and 0x02. All other values will return an Invalid
+	Parameters response.
+
+	With a value of 0x00 any generated debug key will be discarded
+	as soon as the connection terminates.
+
+	With a value of 0x01 generated debug keys will be kept and can
+	be used for future connections. However debug keys are always
+	marked as non persistent and should not be stored. This means
+	a reboot or changing the value back to 0x00 will delete them.
+
+	With a value of 0x02 generated debug keys will be kept and can
+	be used for future connections. This has the same affect as
+	with value 0x01. However in addition this value will also
+	enter the controller mode to generate debug keys for each
+	new pairing. Changing the value back to 0x01 or 0x00 will
+	disable the controller mode for generating debug keys.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Privacy Command
+===================
+
+	Command Code:		0x002F
+	Controller Index:	<controller id>
+	Command Parameters:	Privacy (1 Octet)
+				Identity_Resolving_Key (16 Octets)
+	Return Parameters:	Current_Settings (4 Octets)
+
+	This command is used to enable Low Energy Privacy feature using
+	resolvable private addresses.
+
+	The value 0x00 disables privacy mode, the values 0x01 and 0x02
+	enable privacy mode.
+
+	With value 0x01 the kernel will always use the privacy mode. This
+	means resolvable private address is used when the controller is
+	discoverable and also when pairing is initiated.
+
+	With value 0x02 the kernel will use privacy mode with resolvable
+	private address. In case the conroller is bondable and discoverable
+	the identity address is used. Also when pairing is initiated, the
+	connection will be established with the identity address.
+
+	Exposing the identity address when bondable and discoverable or
+	during initated pairing can be a privacy issue. For dual-mode
+	controllers this can be neglected since its public address will
+	be exposed over BR/EDR anyway. The benefit of exposing the
+	identity address for pairing purposes is that it makes matching
+	up devices with dual-mode topology during device discovery now
+	possible.
+
+	If the privacy value 0x02 is used, then also the GATT database
+	should expose the Privacy Characteristic so that remote devices
+	can determine if the privacy feature is in use or not.
+
+	When the controller has a public address (mandatory for dual-mode
+	controllers) it is used as identity address. In case the controller
+	is single mode LE only without a public address, it is required
+	to configure a static random andress first. The privacy mode can
+	only be enabled when an identity address is available.
+
+	The Identity_Resolving_Key is the local key assigned for the local
+	resolvable private address.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Load Identity Resolving Keys Command
+====================================
+
+	Command Code:		0x0030
+	Controller Index:	<controller id>
+	Command Parameters:	Key_Count (2 Octets)
+				Key1 {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Value (16 Octets)
+				}
+				Key2 {  }
+				...
+	Return Parameters:
+
+	This command is used to feed the kernel with currently known
+	identity resolving keys. The command does not need to be called
+	again upon the receiption of New Identity Resolving Key events
+	since the kernel updates its list automatically.
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The provided Address and Address_Type are the identity of
+	a device. So either its public address or static random address.
+
+	Unresolvable random addresses and resolvable random addresses are
+	not valid and will be rejected.
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Get Connection Information Command
+==================================
+
+	Command Code:		0x0031
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				RSSI (1 Octet)
+				TX_Power (1 Octet)
+				Max_TX_Power (1 Octet)
+
+	This command is used to get connection information.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	TX_Power and Max_TX_Power can be set to 127 if values are invalid or
+	unknown. A value of 127 for RSSI indicates that it is not available.
+
+	This command generates a Command Complete event on success and
+	on failure. In case of failure only Address and Address_Type fields
+	are valid and values of remaining parameters are considered invalid
+	and shall be ignored.
+
+	Possible errors:	Not Connected
+				Not Powered
+				Invalid Parameters
+				Invalid Index
+
+
+Get Clock Information Command
+=============================
+
+	Command Code:		0x0032
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Local_Clock (4 Octets)
+				Piconet_Clock (4 Octets)
+				Accuracy (2 Octets)
+
+	This command is used to get local and piconet clock information.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	Reserved (not in use)
+		2	Reserved (not in use)
+
+	The Accuracy can be set to 0xffff which means the value is unknown.
+
+	If the Address is set to 00:00:00:00:00:00, then only the
+	Local_Clock field has a valid value. The Piconet_Clock and
+	Accuracy fields are invalid and shall be ignored.
+
+	This command generates a Command Complete event on success and
+	on failure. In case of failure only Address and Address_Type fields
+	are valid and values of remaining parameters are considered invalid
+	and shall be ignored.
+
+	Possible errors:	Not Connected
+				Not Powered
+				Invalid Parameters
+				Invalid Index
+
+
+Add Device Command
+==================
+
+	Command Code:		0x0033
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Action (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to add a device to the action list. The
+	action list allows scanning for devices and enables incoming
+	connections from known devices.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	Possible values for the Action parameter:
+		0	Background scan for device
+		1	Allow incoming connection
+		2	Auto-connect remote device
+
+	With the Action 0, when the device is found, a new Device Found
+	event will be sent indicating this device is available. This
+	action is only valid for LE Public and LE Random address types.
+
+	With the Action 1, the device is allowed to connect. For BR/EDR
+	address type this means an incoming connection. For LE Public
+	and LE Random address types, a connection will be established
+	to devices using directed advertising. If successful a Device
+	Connected event will be sent.
+
+	With the Action 2, when the device is found, it will be connected
+	and if successful a Device Connected event will be sent. This
+	action is only valid for LE Public and LE Random address types.
+
+	When a device is blocked using Block Device command, then it is
+	valid to add the device here, but all actions will be ignored
+	until the device is unblocked.
+
+	Devices added with Action 1 are allowed to connect even if the
+	connectable setting is off. This acts as list of known trusted
+	devices.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Failed
+				Invalid Parameters
+				Invalid Index
+
+
+Remove Device Command
+=====================
+
+	Command Code:		0x0034
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+	Return Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This command is used to remove a device from the action list
+	previously added by using the Add Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	When the Address parameter is 00:00:00:00:00:00, then all
+	previously added devices will be removed.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Load Connection Parameters Command
+==================================
+
+	Command Code:		0x0035
+	Controller Index:	<controller id>
+	Command Parameters:	Param_Count (2 Octets)
+				Param1 {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Min_Connection_Interval (2 Octets)
+					Max_Connection_Interval (2 Octes)
+					Connection_Latency (2 Octets)
+					Supervision_Timeout (2 Octets)
+				}
+				Param2 {  }
+				...
+	Return Parameters:
+
+	This command is used to load connection parameters from several
+	devices into kernel. Currently this is only supported on controllers
+	with Low Energy support.
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The provided Address and Address_Type are the identity of
+	a device. So either its public address or static random address.
+
+	The Min_Connection_Interval, Max_Connection_Interval,
+	Connection_Latency and Supervision_Timeout parameters should
+	be configured as described in Core 4.1 spec, Vol 2, 7.8.12.
+
+	This command can be used when the controller is not powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+				Not Supported
+
+
+Read Unconfigured Controller Index List Command
+===============================================
+
+	Command Code:		0x0036
+	Controller Index:	<non-controller>
+	Command Parameters:
+	Return Parameters:	Num_Controllers (2 Octets)
+				Controller_Index[i] (2 Octets)
+
+	This command returns the list of currently unconfigured controllers.
+	Unconfigured controllers added after calling this command can be
+	monitored using the Unconfigured Index Added event.
+
+	An unconfigured controller can either move to a configured state
+	by indicating Unconfigured Index Removed event followed by an
+	Index Added event; or it can be removed from the system which
+	would be indicated by the Unconfigured Index Removed event.
+
+	Only controllers that require configuration will be listed with
+	this command. A controller that is fully configured will not
+	be listed even if it supports configuration changes.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+
+Read Controller Configuration Information Command
+=================================================
+
+	Command Code:		0x0037
+	Controller Index:	<controller id>
+	Command Parameters:
+	Return Parameters:	Manufacturer (2 Octets)
+				Supported_Options (4 Octets)
+				Missing_Options (4 Octets)
+
+	This command is used to retreive the supported configuration
+	options of a controller and the missing configuration options.
+
+	The missing options are required to be configured before the
+	controller is considered fully configured and ready for standard
+	operation. The command is typically used right after getting the
+	response to Read Unconfigured Controller Index List command or
+	Unconfigured Index Added event.
+
+	Supported_Options and Missing_Options is a bitmask with currently
+	the following available bits:
+
+		0	External configuration
+		1	Bluetooth public address configuration
+
+	It is valid to call this command on controllers that do not
+	require any configuration. It is possible that a fully configured
+	controller offers additional support for configuration.
+
+	For example a controller may contain a valid Bluetooth public
+	device address, but also allows to configure it from the host
+	stack. In this case the general support for configurations will
+	be indicated by the Controller Configuration settings. For
+	controllers where no configuration options are available that
+	setting option will not be present.
+
+	When all configurations have been completed and as a result the
+	Missing_Options mask would become empty, then the now ready
+	controller will be announced via Index Added event.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Set External Configuration Command
+==================================
+
+	Command Code:		0x0038
+	Controller Index:	<controller id>
+	Command Parameters:	Configuration (1 Octet)
+	Return Parameters:	Missing_Options (4 Octets)
+
+	This command allows to change external configuration option to
+	indicate that a controller is now configured or unconfigured.
+
+	The value 0x00 sets unconfigured state and the value 0x01 sets
+	configured state of the controller.
+
+	It is not mandatory that this configuration option is provided
+	by a controller. If it is provided, the configuration has to
+	happen externally using user channel operation or via vendor
+	specific methods.
+
+	Setting this option and when Missing_Options returns zero, this
+	means that the controller will switch to configured state and it
+	can be expected that it will be announced via Index Added event.
+
+	Wrongly configured controllers might still cause an error when
+	trying to power them via Set Powered commmand.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Set Public Address Command
+==========================
+
+	Command Code:		0x0039
+	Controller Index:	<controller id>
+	Command Parameters:	Address (6 Octets)
+	Return Parameters:	Missing_Options (4 Octets)
+
+	This command allows configuration of public address. Since a vendor
+	specific procedure is required, this command might not be supported
+	by all controllers. Actually most likely only a handful embedded
+	controllers will offer support for this command.
+
+	When the support for Bluetooth public address configuration is
+	indicated in the supported options mask, then this command
+	can be used to configure the public address.
+
+	It is only possible to configure the public address when the
+	controller is powered off.
+
+	For an unconfigured controller and when Missing_Options returns
+	an empty mask, this means that a Index Added event for the now
+	fully configured controller can be expected.
+
+	For a fully configured controller, the current controller index
+	will become invalid and an Unconfigured Index Removed event will
+	be sent. Once the address has been successfully changed an Index
+	Added event will be sent. There is no guarantee that the controller
+	index stays the same.
+
+	All previous configured parameters and settings are lost when
+	this command succeeds. The controller has to be treated as new
+	one. Use this command for a fully configured controller only when
+	you really know what you are doing.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Start Service Discovery Command
+===============================
+
+	Command Code:		0x003a
+	Controller Index:	<controller id>
+	Command Parameters:	Address_Type (1 Octet)
+				RSSI_Threshold (1 Octet)
+				UUID_Count (1 Octet)
+				UUID[i] (16 Octets)
+	Return Parameters:	Address_Type (1 Octet)
+
+	This command is used to start the process of discovering remote
+	devices with a specific UUID. A Device Found event will be sent
+	for each discovered device.
+
+	Possible values for the Address_Type parameter are a bit-wise or
+	of the following bits:
+
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	By combining these e.g. the following values are possible:
+
+		1	BR/EDR
+		6	LE (public & random)
+		7	BR/EDR/LE (interleaved discovery)
+
+	The service discovery uses active scanning for Low Energy scanning
+	and will search for UUID in both advertising data and scan response
+	data.
+
+	Found devices that have a RSSI value smaller than RSSI_Threshold
+	are not reported via DeviceFound event. Setting a value of 127
+	will cause all devices to be reported.
+
+	The list of UUIDs identifies a logical OR. Only one of the UUIDs
+	have to match to cause a DeviceFound event. Providing an empty
+	list of UUIDs with Num_UUID set to 0 which means that DeviceFound
+	events are send out for all devices above the RSSI_Threshold.
+
+	In case RSSI_Threshold is set to 127 and UUID_Count is 0, then
+	this command behaves exactly the same as Start Discovery.
+
+	When the discovery procedure starts the Discovery event will
+	notify this similar to Start Discovery.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Read Local Out Of Band Extended Data Command
+============================================
+
+	Command Code:		0x003b
+	Controller Index:	<controller id>
+	Command Parameters:	Address_Type (1 Octet)
+	Return Parameters:	Address_Type (1 Octet)
+				EIR_Data_Length (2 Octets)
+				EIR_Data (0-65535 Octets)
+
+	This command is used to read the local Out of Band data
+	information and provide them encoded as extended inquiry
+	response information or advertising data.
+
+	Possible values for the Address_Type parameter are a bit-wise or
+	of the following bits:
+
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	By combining these e.g. the following values are possible:
+
+		1	BR/EDR
+		6	LE (public & random)
+		7	Reserved (not in use)
+
+	For BR/EDR controller (Address_Type 1) the returned information
+	will contain the following information:
+
+		Class of Device
+		Simple Pairing Hash C-192 (optional)
+		Simple Pairing Randomizer R-192 (optional)
+		Simple Pairing Hash C-256 (optional)
+		Simple Pairing Randomizer R-256 (optional)
+		Service Class UUID (optional)
+		Bluetooth Local Name (optional)
+
+	The Simple Pairing Hash C-256 and Simple Pairing Randomizer R-256
+	fields are only included when secure connections has been enabled.
+
+	The Device Address (BD_ADDR) is not included in the EIR_Data and
+	needs to be taken from controller information.
+
+	For LE controller (Address_Type 6) the returned information
+	will contain the following information:
+
+		LE Bluetooth Device Address
+		LE Role
+		LE Secure Connections Confirmation Value (optional)
+		LE Secure Connections Random Value (optional)
+		Appearance (optional)
+		Local Name (optional)
+		Flags
+
+	The LE Secure Connections Confirmation Value and LE Secure Connections
+	Random Value fields are only included when secure connections has been
+	enabled.
+
+	The Security Manager TK Value from the Bluetooth specification can
+	not be provided by this command. The Out Of Band information here are
+	for asymmetric exchanges based on Diffie-Hellman key exchange. The
+	Security Manager TK Value is a symmetric random number that has to
+	be acquired and agreed upon differently.
+
+	The returned information from BR/EDR controller and LE controller
+	types are not related to each other. Once they have been used
+	over an Out Of Band link, a new set of information shall be
+	requested.
+
+	When Secure Connections Only mode has been enabled, then the fields
+	for Simple Pairing Hash C-192 and Simple Pairing Randomizer R-192
+	are not returned. Only the fields for the strong secure connections
+	pairing are included.
+
+	This command can only be used when the controller is powered.
+
+	Values returned by this command become invalid when the controller
+	is powered down. After each power-cycle it is required to call
+	this command again to get updated information.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Not Supported
+				Busy
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Read Extended Controller Index List Command
+===========================================
+
+	Command Code:		0x003c
+	Controller Index:	<non-controller>
+	Command Parameters:
+	Return Parameters:	Num_Controllers (2 Octets)
+				Controller_Index[i] (2 Octets)
+				Controller_Type[i] (1 Octet)
+				Controller_Bus[i] (1 Octet)
+
+	This command returns the list of currently known controllers. It
+	includes configured, unconfigured and alternate controllers.
+
+	Controllers added or removed after calling this command can be
+	be monitored using the Extended Index Added and Extended Index
+	Removed events.
+
+	The existing Index Added, Index Removed, Unconfigured Index Added
+	and Unconfigured Index Removed are no longer sent after this command
+	has been used at least once.
+
+	Instead of calling Read Controller Index List and Read Unconfigured
+	Controller Index List, this command combines all the information
+	and can be used to retrieve the controller list.
+
+	The Controller_Type parameter has these values:
+
+		0x00	Primary Controller (BR/EDR and/or LE)
+		0x01	Unconfigured Controller (BR/EDR and/or LE)
+		0x02	Alternate MAC/PHY Controller (AMP)
+
+	The 0x00 and 0x01 types indiciate a primary BR/EDR and/or LE
+	controller. The difference is just if they need extra configuration
+	or if they are fully configured.
+
+	Controllers in configured state will be listed as 0x00 and controllers
+	in unconfigured state will be listed as 0x01. A controller that is
+	fully configured and supports configuration changes will be listed
+	as 0x00.
+
+	Alternate MAC/PHY controllers will be listed as 0x02. They do not
+	support the difference between configured and unconfigured state.
+
+	The Controller_Bus parameter has these values:
+
+		0x00	Virtual
+		0x01	USB
+		0x02	PCMCIA
+		0x03	UART
+		0x04	RS232
+		0x05	PCI
+		0x06	SDIO
+
+	Controllers marked as RAW only operation are currently not listed
+	by this command.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+
+Read Advertising Features Command
+=================================
+
+	Command Code:		0x003d
+	Controller Index:	<controller id>
+	Command Parameters:
+	Return Parameters:	Supported_Flags (4 Octets)
+				Max_Adv_Data_Len (1 Octet)
+				Max_Scan_Rsp_Len (1 Octet)
+				Max_Instances (1 Octet)
+				Num_Instances (1 Octet)
+				Instance[i] (1 Octet)
+
+	This command is used to read the advertising features supported
+	by the controller and stack.
+
+	With the Supported_Flags field the possible values for the Flags
+	field in Add Advertising command provided:
+
+		0	Switch into Connectable mode
+		1	Advertise as Discoverable
+		2	Advertise as Limited Discoverable
+		3	Add Flags field to Adv_Data
+		4	Add TX Power field to Adv_Data
+		5	Add Appearance field to Scan_Rsp
+		6	Add Local Name in Scan_Rsp
+
+	The Flags bit 0 indicates support for connectable advertising
+	and for switching to connectable advertising independent of the
+	connectable global setting. When this flag is not supported, then
+	the global connectable setting determines if undirected connectable,
+	undirected scannable or undirected non-connectable advertising is
+	used. It also determines the use of non-resolvable random address
+	versus identity address or resolvable private address.
+
+	The Flags bit 1 indicates support for advertising with discoverable
+	mode enabled. Users of this flag will decrease the Max_Adv_Data_Len
+	by 3 octets. In this case the advertising data flags are managed
+	and added in front of the provided advertising data.
+
+	The Flags bit 2 indicates support for advertising with limited
+	discoverable mode enabled. Users of this flag will decrease the
+	Max_Adv_Data_Len by 3 octets. In this case the advertising data
+	flags are managed and added in front of the provided advertising
+	data.
+
+	The Flags bit 3 indicates support for automatically keeping the
+	Flags field of the advertising data updated. Users of this flag
+	will decrease the Max_Adv_Data_Len by 3 octets and need to keep
+	that in mind. The Flags field will be added in front of the
+	advertising data provided by the user. Note that with Flags bit 1
+	and Flags bit 2, this one will be implicitly used even if it is
+	not marked as supported.
+
+	The Flags bit 4 indicates support for automatically adding the
+	TX Power value to the advertising data. Users of this flag will
+	decrease the Max_Adv_Data_Len by 3 octets. The TX Power field will
+	be added at the end of the user provided advertising data. If the
+	controller does not support TX Power information, then this bit will
+	not be set.
+
+	The Flags bit 5 indicates support for automatically adding the
+	Apperance value to the scan response data. Users of this flag
+	will decrease the Max_Scan_Rsp_len by 4 octets. The Appearance
+	field will be added in front of the scan response data provided
+	by the user. If the appearance value is not supported, then this
+	bit will not be set.
+
+	The Flags bit 6 indicates support for automatically adding the
+	Local Name value to the scan response data. This flag indicates
+	an opportunistic approach for the Local Name. If enough space
+	in the scan response data is available, it will be added. If the
+	space is limited a short version or no name information. The
+	Local Name will be added at the end of the scan response data.
+
+	The valid range for Instance identifiers is 1-254. The value 0
+	is reserved for internal use and the value 255 is reserved for
+	future extensions. However the Max_Instances value for indicating
+	the number of supported Instances can be also 0 if the controller
+	does not support any advertising.
+
+	The Max_Adv_Data_Len and Max_Scan_Rsp_Len provides extra
+	information about the maximum length of the data fields. For
+	now this will always return the value 31. Different flags
+	however might decrease the actual available length in these
+	data fields.
+
+	With Num_Instances and Instance array the currently occupied
+	Instance identifiers can be retrieved.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Add Advertising Command
+=======================
+
+	Command Code:		0x003e
+	Controller Index:	<controller id>
+	Command Parameters:	Instance (1 Octet)
+				Flags (4 Octets)
+				Duration (2 Octets)
+				Timeout (2 Octets)
+				Adv_Data_Len (1 Octet)
+				Scan_Rsp_len (1 Octet)
+				Adv_Data (0-255 Octets)
+				Scan_Rsp (0-255 Octets)
+	Return Parameters:	Instance (1 Octet)
+
+	This command is used to configure an advertising instance that
+	can be used to switch a Bluetooth Low Energy controller into
+	advertising mode.
+
+	Added advertising information with this command will not be visible
+	immediately if advertising is enabled via the Set Advertising
+	command. The usage of the Set Advertising command takes precedence
+	over this command. Instance information is stored and will be
+	advertised once advertising via Set Advertising has been disabled.
+
+	The Instance identifier is a value between 1 and the number of
+	supported instances. The value 0 is reserved.
+
+	With the Flags value the type of advertising is controlled and
+	the following flags are defined:
+
+		0	Switch into Connectable mode
+		1	Advertise as Discoverable
+		2	Advertise as Limited Discoverable
+		3	Add Flags field to Adv_Data
+		4	Add TX Power field to Adv_Data
+		5	Add Appearance field to Scan_Rsp
+		6	Add Local Name in Scan_Rsp
+
+	When the connectable flag is set, then the controller will use
+	undirected connectable advertising. The value of the connectable
+	setting can be overwritten this way. This is useful to switch a
+	controller into connectable mode only for LE operation. This is
+	similar to the mode 0x02 from the Set Advertising command.
+
+	When the connectable flag is not set, then the controller will
+	use advertising based on the connectable setting. When using
+	non-connectable or scannable advertising, the controller will
+	be programmed with a non-resolvable random address. When the
+	system is connectable, then the identity address or resolvable
+	private address will be used.
+
+	Using the connectable flag is useful for peripheral mode support
+	where BR/EDR (and/or LE) is controlled by Add Device. This allows
+	making the peripheral connectable without having to interfere
+	with the global connectable setting.
+
+	If Scan_Rsp_Len is zero and connectable flag is not set and
+	the global connectable setting is off, then non-connectable
+	advertising is used. If Scan_Rsp_Len is larger than zero and
+	connectable flag is not set and the global advertising is off,
+	then scannable advertising is used. This small difference is
+	supported to provide less air traffic for devices implementing
+	broadcaster role.
+
+	The Duration parameter configures the length of an Instance. The
+	value is in seconds.
+
+	A value of 0 indicates a default value is chosen for the
+	Duration. The default is 2 seconds.
+
+	If only one advertising Instance has been added, then the Duration
+	value will be ignored. It only applies for the case where multiple
+	Instances are configured. In that case every Instance will be
+	available for the Duration time and after that it switches to
+	the next one. This is a simple round-robin based approach.
+
+	The Timeout parameter configures the life-time of an Instance. In
+	case the value 0 is used it indicates no expiration time. If a
+	timeout value is provided, then the advertising Instance will be
+	automatically removed when the timeout passes. The value for the
+	timeout is in seconds. Powering down a controller will invalidate
+	all advertising Instances and it is not possible to add a new
+	Instance with a timeout when the controller is powered down.
+
+	When a Timeout is provided, then the Duration subtracts from
+	the actual Timeout value of that Instance. For example an Instance
+	with Timeout of 5 and Duration of 2 will be scheduled exactly 3
+	times, twice with 2 seconds and once with one second. Other
+	Instances have no influence on the Timeout.
+
+	Re-adding an already existing instance (i.e. issuing the Add
+	Advertising command with an Instance identifier of an existing
+	instance) will update that instance's configuration.
+
+	An instance being added or changed while another instance is
+	being advertised will not be visible immediately but only when
+	the new/changed instance is being scheduled by the round robin
+	advertising algorithm.
+
+	Changes to an instance that is currently being advertised will
+	cancel that instance and switch to the next instance. The changes
+	will be visible the next time the instance is scheduled for
+	advertising. In case a single instance is active, this means
+	that changes will be visible right away.
+
+	A pre-requisite is that LE is already enabled, otherwise this
+	command will return a "rejected" response.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success or a
+	Command Status event on failure.
+
+	Possible errors:	Failed
+				Rejected
+				Not Supported
+				Invalid Parameters
+				Invalid Index
+
+
+Remove Advertising Command
+==========================
+
+	Command Code:		0x003f
+	Controller Index:	<controller id>
+	Command Parameters:	Instance (1 Octet)
+	Return Parameters:	Instance (1 Octet)
+
+	This command is used to remove an advertising instance that
+	can be used to switch a Bluetooth Low Energy controller into
+	advertising mode.
+
+	When the Instance parameter is zero, then all previously added
+	advertising Instances will be removed.
+
+	Removing advertising information with this command will not be
+	visible as long as advertising is enabled via the Set Advertising
+	command. The usage of the Set Advertising command takes precedence
+	over this command. Changes to Instance information are stored and
+	will be advertised once advertising via Set Advertising has been
+	disabled.
+
+	Removing an instance while it is being advertised will immediately
+	cancel the instance, even when it has been advertised less then its
+	configured Timeout or Duration.
+
+	This command can be used when the controller is not powered and
+	all settings will be programmed once powered.
+
+	This command generates a Command Complete event on success or
+	a Command Status event on failure.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Get Advertising Size Information Command
+========================================
+
+	Command Code:		0x0040
+	Controller Index:	<controller id>
+	Command Parameters:	Instance (1 Octet)
+				Flags (4 Octets)
+	Return Parameters:	Instance (1 Octet)
+				Flags (4 Octets)
+				Max_Adv_Data_Len (1 Octet)
+				Max_Scan_Rsp_Len (1 Octet)
+
+	The Read Advertising Features command returns the overall maximum
+	size of advertising data and scan respone data fields. That size is
+	valid when no Flags are used. However when certain Flags are used,
+	then the size might decrease. This command can be used to request
+	detailed information about the maximum available size.
+
+	The following Flags values are defined:
+
+		0	Switch into Connectable mode
+		1	Advertise as Discoverable
+		2	Advertise as Limited Discoverable
+		3	Add Flags field to Adv_Data
+		4	Add TX Power field to Adv_Data
+		5	Add Appearance field to Scan_Rsp
+		6	Add Local Name in Scan_Rsp
+
+	To get accurate information about the available size, the same Flags
+	values should be used with the Add Advertising command.
+
+	The Max_Adv_Data_Len and Max_Scan_Rsp_Len fields provide information
+	about the maximum length of the data fields for the given Flags
+	values. When the Flags field is zero, then these fields would contain
+	the same values as Read Advertising Features.
+
+	Possible errors:	Invalid Parameters
+				Invalid Index
+
+
+Start Limited Discovery Command
+===============================
+
+	Command Code:		0x0041
+	Controller Index:	<controller id>
+	Command Parameters:	Address_Type (1 Octet)
+	Return Parameters:	Address_Type (1 Octet)
+
+	This command is used to start the process of discovering remote
+	devices using the limited discovery procedure. A Device Found event
+	will be sent for each discovered device.
+
+	Possible values for the Address_Type parameter are a bit-wise or
+	of the following bits:
+
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	By combining these e.g. the following values are possible:
+
+		1	BR/EDR
+		6	LE (public & random)
+		7	BR/EDR/LE (interleaved discovery)
+
+	The limited discovery uses active scanning for Low Energy scanning
+	and will search for devices with the limited discoverabilty flag
+	configured. On BR/EDR it uses LIAC and filters on the limited
+	discoverabilty flag of the class of device.
+
+	When the discovery procedure starts the Discovery event will
+	notify this similar to Start Discovery.
+
+	This command can only be used when the controller is powered.
+
+	This command generates a Command Complete event on success
+	or failure.
+
+	Possible errors:	Busy
+				Not Supported
+				Invalid Parameters
+				Not Powered
+				Invalid Index
+
+
+Command Complete Event
+======================
+
+	Event Code:		0x0001
+	Controller Index:	<controller id> or <non-controller>
+	Event Parameters:	Command_Opcode (2 Octets)
+				Status (1 Octet)
+				Return_Parameters
+
+	This event is an indication that a command has completed. The
+	fixed set of parameters includes the opcode to identify the
+	command that completed as well as a status value to indicate
+	success or failure. The rest of the parameters are command
+	specific and documented in the section for each command
+	separately.
+
+
+Command Status Event
+====================
+
+	Event Code:		0x0002
+	Controller Index:	<controller id> or <non-controller>
+	Event Parameters:	Command_Opcode (2 Octets)
+				Status (1 Octet)
+
+	The command status event is used to indicate an early status for
+	a pending command. In the case that the status indicates failure
+	(anything else except success status) this also means that the
+	command has finished executing.
+
+
+Controller Error Event
+======================
+
+	Event Code:		0x0003
+	Controller Index:	<controller id>
+	Event Parameters:	Error_Code (1 Octet)
+
+	This event maps straight to the HCI Hardware Error event and is
+	used to indicate something wrong with the controller hardware.
+
+
+Index Added Event
+=================
+
+	Event Code:		0x0004
+	Controller Index:	<controller id>
+	Event Parameters:
+
+	This event indicates that a new controller has been added to the
+	system. It is usually followed by a Read Controller Information
+	command.
+
+	Once the Read Extended Controller Index List command has been
+	used at least once, the Extended Index Added event will be
+	send instead of this one.
+
+
+Index Removed Event
+===================
+
+	Event Code:		0x0005
+	Controller Index:	<controller id>
+	Event Parameters:
+
+	This event indicates that a controller has been removed from the
+	system.
+
+	Once the Read Extended Controller Index List command has been
+	used at least once, the Extended Index Removed event will be
+	send instead of this one.
+
+
+New Settings Event
+==================
+
+	Event Code:		0x0006
+	Controller Index:	<controller id>
+	Event Parameters:	Current_Settings (4 Octets)
+
+	This event indicates that one or more of the settings for a
+	controller has changed.
+
+
+Class Of Device Changed Event
+=============================
+
+	Event Code:		0x0007
+	Controller Index:	<controller id>
+	Event Parameters:	Class_Of_Device (3 Octets)
+
+	This event indicates that the Class of Device value for the
+	controller has changed. When the controller is powered off the
+	Class of Device value will always be reported as zero.
+
+
+Local Name Changed Event
+========================
+
+	Event Code:		0x0008
+	Controller Index:	<controller id>
+	Event Parameters:	Name (249 Octets)
+				Short_Name (11 Octets)
+
+	This event indicates that the local name of the controller has
+	changed.
+
+
+New Link Key Event
+==================
+
+	Event Code:		0x0009
+	Controller Index:	<controller id>
+	Event Parameters:	Store_Hint (1 Octet)
+				Key {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Key_Type (1 Octet)
+					Value (16 Octets)
+					PIN_Length (1 Octet)
+				}
+
+	This event indicates that a new link key has bee generated for a
+	remote device.
+
+	The Store_Hint parameter indicates whether the host is expected
+	to store the key persistently or not (e.g. this would not be set
+	if the authentication requirement was "No Bonding").
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	Reserved (not in use)
+		2	Reserved (not in use)
+
+	Public and random LE addresses are not valid and will be rejected.
+
+	Currently defined Key_Type values are:
+
+		0x00	Combination key
+		0x01	Local Unit key
+		0x02	Remote Unit key
+		0x03	Debug Combination key
+		0x04	Unauthenticated Combination key from P-192
+		0x05	Authenticated Combination key from P-192
+		0x06	Changed Combination key
+		0x07	Unauthenticated Combination key from P-256
+		0x08	Authenticated Combination key from P-256
+
+	Receiving this event indicates that a pairing procecure has
+	been completed.
+
+
+New Long Term Key Event
+=======================
+
+	Event Code:		0x000A
+	Controller Index:	<controller id>
+	Event Parameters:	Store_Hint (1 Octet)
+				Key {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Key_Type (1 Octet)
+					Master (1 Octet)
+					Encryption Size (1 Octet)
+					Enc. Diversifier (2 Octets)
+					Random Number (8 Octets)
+					Value (16 Octets)
+				}
+
+	This event indicates that a new long term key has been generated
+	for a remote device.
+
+	The Store_Hint parameter indicates whether the host is expected
+	to store the key persistently or not (e.g. this would not be set
+	if the authentication requirement was "No Bonding").
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The provided Address and Address_Type are the identity of
+	a device. So either its public address or static random address.
+
+	For unresolvable random addresses and resolvable random addresses
+	without identity information and identity resolving key, the
+	Store_Hint will be set to not store the long term key.
+
+	Currently defined Key_Type values are:
+
+		0x00	Unauthenticated legacy key
+		0x01	Authenticated legacy key
+		0x02	Unauthenticated key from P-256
+		0x03	Authenticated key from P-256
+		0x04	Debug key from P-256
+
+	Receiving this event indicates that a pairing procecure has
+	been completed.
+
+
+Device Connected Event
+======================
+
+	Event Code:		0x000B
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Flags (4 Octets)
+				EIR_Data_Length (2 Octets)
+				EIR_Data (0-65535 Octets)
+
+	This event indicates that a successful baseband connection has
+	been created to the remote device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	For devices using resolvable random addresses with a known
+	identity resolving key, the Address and Address_Type will
+	contain the identity information.
+
+	It is possible that devices get connected via its resolvable
+	random address and after New Identity Resolving Key event
+	start using its identity.
+
+	The following bits are defined for the Flags parameter:
+		0	Reserved (not in use)
+		1	Legacy Pairing
+		2	Reserved (not in use)
+
+
+Device Disconnected Event
+=========================
+
+	Event Code:		0x000C
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Reason (1 Octet)
+
+	This event indicates that the baseband connection was lost to a
+	remote device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	For devices using resolvable random addresses with a known
+	identity resolving key, the Address and Address_Type will
+	contain the identity information.
+
+	Possible values for the Reason parameter:
+		0	Unspecified
+		1	Connection timeout
+		2	Connection terminated by local host
+		3	Connection terminated by remote host
+
+	Note that the local/remote distinction just determines which side
+	terminated the low-level connection, regardless of the
+	disconnection of the higher-level profiles.
+
+	This can sometimes be misleading and thus must be used with care.
+	For example, some hardware combinations would report a locally
+	initiated disconnection even if the user turned Bluetooth off in
+	the remote side.
+
+
+Connect Failed Event
+====================
+
+	Event Code:		0x000D
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Status (1 Octet)
+
+	This event indicates that a connection attempt failed to a
+	remote device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	For devices using resolvable random addresses with a known
+	identity resolving key, the Address and Address_Type will
+	contain the identity information.
+
+
+PIN Code Request Event
+======================
+
+	Event Code:		0x000E
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Secure (1 Octet)
+
+	This event is used to request a PIN Code reply from user space.
+	The reply should either be returned using the PIN Code Reply or
+	the PIN Code Negative Reply command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	Secure: 0x01  secure PIN code required
+		0x00  secure PIN code not required
+
+
+User Confirmation Request Event
+===============================
+
+	Event Code:		0x000F
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Confirm_Hint (1 Octet)
+				Value (4 Octets)
+
+	This event is used to request a user confirmation request from
+	user space.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	If the Confirm_Hint parameter value is 0x01 this means that
+	a simple "Yes/No" confirmation should be presented to the user
+	instead of a full numerical confirmation (in which case the
+	parameter value will be 0x00).
+
+	User space should respond to this command either using the User
+	Confirmation Reply or the User Confirmation Negative Reply
+	command.
+
+
+User Passkey Request Event
+==========================
+
+	Event Code:		0x0010
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This event is used to request a passkey from user space. The
+	response to this event should either be the User Passkey Reply
+	command or the User Passkey Negative Reply command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+
+Authentication Failed Event
+===========================
+
+	Event Code:		0x0011
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Status (1 Octet)
+
+	This event indicates that there was an authentication failure
+	with a remote device.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+
+Device Found Event
+==================
+
+	Event Code:		0x0012
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				RSSI (1 Octet)
+				Flags (4 Octets)
+				EIR_Data_Length (2 Octets)
+				EIR_Data (0-65535 Octets)
+
+	This event indicates that a device was found during device
+	discovery.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The following bits are defined for the Flags parameter:
+		0	Confirm name
+		1	Legacy Pairing
+		2	Not Connectable
+
+	For the RSSI field a value of 127 indicates that the RSSI is
+	not available. That can happen with Bluetooth 1.1 and earlier
+	controllers or with bad radio conditions.
+
+	The Confirm name flag indicates that the kernel wants to know
+	whether user space knows the name for this device or not. If
+	this flag is set user space should respond to it using the
+	Confirm Name command.
+
+	The Legacy Pairing flag indicates that Legacy Pairing is likely
+	to occur when pairing with this device. An application could use
+	this information to optimize the pairing process by locally
+	pre-generating a PIN code and thereby eliminate the risk of
+	local input timeout when pairing. Note that there is a risk of
+	false-positives for this flag so user space should be able to
+	handle getting something else as a PIN Request when pairing.
+
+	The Not Connectable flag indicates that the device will not
+	accept any connections. This can be indicated by Low Energy
+	devices that are in broadcaster role.
+
+
+Discovering Event
+=================
+
+	Event Code:		0x0013
+	Controller Index:	<controller id>
+	Event Parameters:	Address_Type (1 Octet)
+				Discovering (1 Octet)
+
+	This event indicates that the controller has started discovering
+	devices. This discovering state can come and go multiple times
+	between a Start Discovery and a Stop Discovery commands.
+
+	The Start Service Discovery command will also trigger this event.
+
+	The valid values for the Discovering parameter are 0x01
+	(enabled) and 0x00 (disabled).
+
+
+Device Blocked Event
+====================
+
+	Event Code:		0x0014
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This event indicates that a device has been blocked using the
+	Block Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The event will only be sent to Management sockets other than the
+	one through which the command was sent.
+
+
+Device Unblocked Event
+======================
+
+	Event Code:		0x0015
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This event indicates that a device has been unblocked using the
+	Unblock Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The event will only be sent to Management sockets other than the
+	one through which the command was sent.
+
+
+Device Unpaired Event
+=====================
+
+	Event Code:		0x0016
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This event indicates that a device has been unpaired (i.e. all
+	its keys have been removed from the kernel) using the Unpair
+	Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	For devices using resolvable random addresses with a known
+	identity resolving key, the event paramters will contain
+	the identity. After receiving this event, the device will
+	become essentially private again.
+
+	The event will only be sent to Management sockets other than the
+	one through which the Unpair Device command was sent.
+
+
+Passkey Notify Event
+====================
+
+	Event Code:		0x0017
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Passkey (4 Octets)
+				Entered (1 Octet)
+
+	This event is used to request passkey notification to the user.
+	Unlike the other authentication events it does not need
+	responding to using any Management command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The Passkey parameter indicates the passkey to be shown to the
+	user whereas the Entered parameter indicates how many characters
+	the user has entered on the remote side.
+
+
+New Identity Resolving Key Event
+================================
+
+	Event Code:		0x0018
+	Controller Index:	<controller id>
+	Event Parameters:	Store_Hint (1 Octet)
+				Random_Address (6 Octets)
+				Key {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Value (16 Octets)
+				}
+
+	This event indicates that a new identity resolving key has been
+	generated for a remote device.
+
+	The Store_Hint parameter indicates whether the host is expected
+	to store the key persistently or not.
+
+	The Random_Address provides the resolvable random address that
+	was resolved into an identity. A value of 00:00:00:00:00:00
+	indicates that the identity resolving key was provided for
+	a public address or static random address.
+
+	Once this event has been send for a resolvable random address,
+	all further events mapping this device will send out using the
+	identity address information.
+
+	This event also indicates that now the identity address should
+	be used for commands instead of the resolvable random address.
+
+	It is possible that some devices allow discovering via its
+	identity address, but after pairing using resolvable private
+	address only. In such a case Store_Hint will be 0x00 and the
+	Random_Address will indicate 00:00:00:00:00:00. For these devices,
+	the Privacy Characteristic of the remote GATT database should
+	be consulted to decide if the identity resolving key must be
+	stored persistently or not.
+
+	Devices using Set Privacy command with the option 0x02 would
+	be such type of device.
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The provided Address and Address_Type are the identity of
+	a device. So either its public address or static random address.
+
+
+New Signature Resolving Key Event
+=================================
+
+	Event Code:		0x0019
+	Controller Index:	<controller id>
+	Event Parameters:	Store_Hint (1 Octet)
+				Key {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Type (1 Octet)
+					Value (16 Octets)
+				}
+
+	This event indicates that a new signature resolving key has been
+	generated for either the master or slave device.
+
+	The Store_Hint parameter indicates whether the host is expected
+	to store the key persistently or not.
+
+	The Type parameter has the following possible values:
+
+		0x00	Unauthenticated local CSRK
+		0x01	Unauthenticated remote CSRK
+		0x02	Authenticated local CSRK
+		0x03	Authenticated remote CSRK
+
+	The local keys are used for signing data to be sent to the
+	remote device, whereas the remote keys are used to verify
+	signatures received from the remote device.
+
+	The local signature resolving key will be generated with each
+	pairing request. Only after receiving this event with the Type
+	indicating a local key is it possible to use ATT Signed Write
+	procedures.
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The provided Address and Address_Type are the identity of
+	a device. So either its public address or static random address.
+
+
+Device Added Event
+==================
+
+	Event Code:		0x001a
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+				Action (1 Octet)
+
+	This event indicates that a device has been added using the
+	Add Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The event will only be sent to management sockets other than the
+	one through which the command was sent.
+
+
+Device Removed Event
+====================
+
+	Event Code:		0x001b
+	Controller Index:	<controller id>
+	Event Parameters:	Address (6 Octets)
+				Address_Type (1 Octet)
+
+	This event indicates that a device has been removed using the
+	Remove Device command.
+
+	Possible values for the Address_Type parameter:
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	The event will only be sent to management sockets other than the
+	one through which the command was sent.
+
+
+New Connection Parameter Event
+==============================
+
+	Event Code:		0x001c
+	Controller Index:	<controller id>
+	Event Parameters:	Store_Hint (1 Octet)
+				Param {
+					Address (6 Octets)
+					Address_Type (1 Octet)
+					Min_Connection_Interval (2 Octets)
+					Max_Connection_Interval (2 Octes)
+					Connection_Latency (2 Octets)
+					Supervision_Timeout (2 Octets)
+				}
+
+	This event indicates a new set of connection parameters from
+	a peripheral device.
+
+	The Store_Hint parameter indicates whether the host is expected
+	to store this information persistently or not.
+
+	Possible values for the Address_Type parameter:
+		0	Reserved (not in use)
+		1	LE Public
+		2	LE Random
+
+	The Min_Connection_Interval, Max_Connection_Interval,
+	Connection_Latency and Supervision_Timeout parameters are
+	encoded as described in Core 4.1 spec, Vol 2, 7.7.65.3.
+
+
+Unconfigured Index Added Event
+==============================
+
+	Event Code:		0x001d
+	Controller Index:	<controller id>
+	Event Parameters:
+
+	This event indicates that a new unconfigured controller has been
+	added to the system. It is usually followed by a Read Controller
+	Configuration Information command.
+
+	Only when a controller requires further configuration, it will
+	be announced with this event. If it supports configuration, but
+	does not require it, then an Index Added event will be used.
+
+	Once the Read Extended Controller Index List command has been
+	used at least once, the Extended Index Added event will be
+	send instead of this one.
+
+
+Unconfigured Index Removed Event
+================================
+
+	Event Code:		0x001e
+	Controller Index:	<controller id>
+	Event Parameters:
+
+	This event indicates that an unconfigured controller has been
+	removed from the system.
+
+	Once the Read Extended Controller Index List command has been
+	used at least once, the Extended Index Removed event will be
+	send instead of this one.
+
+
+New Configuration Options Event
+===============================
+
+	Event Code:		0x001f
+	Controller Index:	<controller id>
+	Event Parameters:	Missing_Options (4 Octets)
+
+	This event indicates that one or more of the options for the
+	controller configuration has changed.
+
+
+Extended Index Added Event
+==========================
+
+	Event Code:		0x0020
+	Controller Index:	<controller id>
+	Event Parameters:	Controller_Type (1 Octet)
+				Controller_Bus (1 Octet)
+
+	This event indicates that a new controller index has been
+	added to the system.
+
+	This event will only be used after Read Extended Controller Index
+	List has been used at least once. If it has not been used, then
+	Index Added and Unconfigured Index Added are sent instead.
+
+
+Extended Index Removed Event
+============================
+
+	Event Code:		0x0021
+	Controller Index:	<controller id>
+	Event Parameters:	Controller_Type (1 Octet)
+				Controller_Bus (1 Octet)
+
+	This event indicates that an existing controller index has been
+	removed from the system.
+
+	This event will only be used after Read Extended Controller Index
+	List has been used at least once. If it has not been used, then
+	Index Removed and Unconfigured Index Removed are sent instead.
+
+
+Local Out Of Band Extended Data Updated Event
+=============================================
+
+	Event Code:		0x0022
+	Controller Index:	<controller id>
+	Event Parameters:	Address_Type (1 Octet)
+				EIR_Data_Length (2 Octets)
+				EIR_Data (0-65535 Octets)
+
+	This event is used when the Read Local Out Of Band Extended Data
+	command has been used and some other user requested a new set
+	of local out-of-band data. This allows for the original caller
+	to adjust the data.
+
+	Possible values for the Address_Type parameter are a bit-wise or
+	of the following bits:
+
+		0	BR/EDR
+		1	LE Public
+		2	LE Random
+
+	By combining these e.g. the following values are possible:
+
+		1	BR/EDR
+		6	LE (public & random)
+		7	Reserved (not in use)
+
+	The value for EIR_Data_Length and content for EIR_Data is the
+	same as described in Read Local Out Of Band Extended Data command.
+
+	When LE Privacy is used and LE Secure Connections out-of-band
+	data has been requested, then this event will be emitted every
+	time the Resolvable Private Address (RPA) gets changed. The new
+	RPA will be included in the EIR_Data.
+
+	The event will only be sent to management sockets other than the
+	one through which the command was sent. It will additionally also
+	only be sent to sockets that have used the command at least once.
+
+
+Advertising Added Event
+=======================
+
+	Event Code:		0x0023
+	Controller Index:	<controller id>
+	Event Parameters:	Instance (1 Octet)
+
+	This event indicates that an advertising instance has been added
+	using the Add Advertising command.
+
+	The event will only be sent to management sockets other than the
+	one through which the command was sent.
+
+
+Advertising Removed Event
+=========================
+
+	Event Code:		0x0024
+	Controller Index:	<controller id>
+	Event Parameters:	Instance (1 Octet)
+
+	This event indicates that an advertising instance has been removed
+	using the Remove Advertising command.
+
+	The event will only be sent to management sockets other than the
+	one through which the command was sent.
diff --git a/bluez/repo/doc/network-api.txt b/bluez/repo/doc/network-api.txt
new file mode 100644
index 0000000..109da28
--- /dev/null
+++ b/bluez/repo/doc/network-api.txt
@@ -0,0 +1,76 @@
+BlueZ D-Bus Network API description
+***********************************
+
+
+Network hierarchy
+=================
+
+Service		org.bluez
+Interface	org.bluez.Network1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods		string Connect(string uuid)
+
+			Connect to the network device and return the network
+			interface name. Examples of the interface name are
+			bnep0, bnep1 etc.
+
+			uuid can be either one of "gn", "panu" or "nap" (case
+			insensitive) or a traditional string representation of
+			UUID or a hexadecimal number.
+
+			The connection will be closed and network device
+			released either upon calling Disconnect() or when
+			the client disappears from the message bus.
+
+			Possible errors: org.bluez.Error.AlreadyConnected
+					 org.bluez.Error.ConnectionAttemptFailed
+
+		void Disconnect()
+
+			Disconnect from the network device.
+
+			To abort a connection attempt in case of errors or
+			timeouts in the client it is fine to call this method.
+
+			Possible errors: org.bluez.Error.Failed
+
+Properties	boolean Connected [readonly]
+
+			Indicates if the device is connected.
+
+		string Interface [readonly]
+
+			Indicates the network interface name when available.
+
+		string UUID [readonly]
+
+			Indicates the connection role when available.
+
+
+Network server hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.NetworkServer1
+Object path	/org/bluez/{hci0,hci1,...}
+
+Methods		void Register(string uuid, string bridge)
+
+			Register server for the provided UUID. Every new
+			connection to this server will be added the bridge
+			interface.
+
+			Valid UUIDs are "gn", "panu" or "nap".
+
+			Initially no network server SDP is provided. Only
+			after this method a SDP record will be available
+			and the BNEP server will be ready for incoming
+			connections.
+
+		void Unregister(string uuid)
+
+			Unregister the server for provided UUID.
+
+			All servers will be automatically unregistered when
+			the calling application terminates.
diff --git a/bluez/repo/doc/obex-agent-api.txt b/bluez/repo/doc/obex-agent-api.txt
new file mode 100644
index 0000000..3923da6
--- /dev/null
+++ b/bluez/repo/doc/obex-agent-api.txt
@@ -0,0 +1,61 @@
+OBEX D-Bus Agent API description
+********************************
+
+
+Agent Manager hierarchy
+=======================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.AgentManager1
+Object path	/org/bluez/obex
+
+Methods		void RegisterAgent(object agent)
+
+			Register an agent to request authorization of
+			the user to accept/reject objects. Object push
+			service needs to authorize each received object.
+
+			Possible errors: org.bluez.obex.Error.AlreadyExists
+
+		void UnregisterAgent(object agent)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+			Possible errors: org.bluez.obex.Error.DoesNotExist
+
+
+Agent hierarchy
+===============
+
+Service		unique name
+Interface	org.bluez.obex.Agent1
+Object path	freely definable
+
+Methods		void Release()
+
+			This method gets called when the service daemon
+			unregisters the agent. An agent can use it to do
+			cleanup tasks. There is no need to unregister the
+			agent, because when this method gets called it has
+			already been unregistered.
+
+		string AuthorizePush(object transfer)
+
+			This method gets called when the service daemon
+			needs to accept/reject a Bluetooth object push request.
+
+			Returns the full path (including the filename) where
+			the object shall be stored. The tranfer object will
+			contain a Filename property that contains the default
+			location and name that can be returned.
+
+			Possible errors: org.bluez.obex.Error.Rejected
+			                 org.bluez.obex.Error.Canceled
+
+		void Cancel()
+
+			This method gets called to indicate that the agent
+			request failed before a reply was returned. It cancels
+			the previous request.
diff --git a/bluez/repo/doc/obex-api.txt b/bluez/repo/doc/obex-api.txt
new file mode 100644
index 0000000..f39355a
--- /dev/null
+++ b/bluez/repo/doc/obex-api.txt
@@ -0,0 +1,894 @@
+OBEX D-Bus API description
+**************************
+
+
+Client hierarchy
+================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.Client1
+Object path	/org/bluez/obex
+
+Methods		object CreateSession(string destination, dict args)
+
+			Create a new OBEX session for the given remote address.
+
+			The last parameter is a dictionary to hold optional or
+			type-specific parameters. Typical parameters that can
+			be set in this dictionary include the following:
+
+				string "Target" : type of session to be created
+				string "Source" : local address to be used
+				byte "Channel"
+
+			The currently supported targets are the following:
+
+				"ftp"
+				"map"
+				"opp"
+				"pbap"
+				"sync"
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		void RemoveSession(object session)
+
+			Unregister session and abort pending transfers.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.NotAuthorized
+
+Session hierarchy
+=================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.Session1
+Object path	/org/bluez/obex/server/session{0, 1, 2, ...} or
+		/org/bluez/obex/client/session{0, 1, 2, ...}
+
+Methods		string GetCapabilities()
+
+			Get remote device capabilities.
+
+			Possible errors: org.bluez.obex.Error.NotSupported
+					 org.bluez.obex.Error.Failed
+
+Properties	string Source [readonly]
+
+			Bluetooth adapter address
+
+		string Destination [readonly]
+
+			Bluetooth device address
+
+		byte Channel [readonly]
+
+			Bluetooth channel
+
+		string Target [readonly]
+
+			Target UUID
+
+		string Root [readonly]
+
+			Root path
+
+
+Transfer hierarchy
+==================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.Transfer1
+Object path	[Session object path]/transfer{0, 1, 2, ...}
+
+Methods		void Cancel()
+
+			Stops the current transference.
+
+			Possible errors: org.bluez.obex.Error.NotAuthorized
+					 org.bluez.obex.Error.InProgress
+					 org.bluez.obex.Error.Failed
+
+		void Suspend()
+
+			Suspend transference.
+
+			Possible errors: org.bluez.obex.Error.NotAuthorized
+					 org.bluez.obex.Error.NotInProgress
+
+			Note that it is not possible to suspend transfers
+			which are queued which is why NotInProgress is listed
+			as possible error.
+
+		void Resume()
+
+			Resume transference.
+
+			Possible errors: org.bluez.obex.Error.NotAuthorized
+					 org.bluez.obex.Error.NotInProgress
+
+			Note that it is not possible to resume transfers
+			which are queued which is why NotInProgress is listed
+			as possible error.
+
+Properties	string Status [readonly]
+
+			Inform the current status of the transfer.
+
+			Possible values: "queued", "active", "suspended",
+					"complete" or "error"
+
+		object Session [readonly]
+
+			The object path of the session the transfer belongs
+			to.
+
+		string Name [readonly]
+
+			Name of the transferred object. Either Name or Type
+			or both will be present.
+
+		string Type [readonly]
+
+			Type of the transferred object. Either Name or Type
+			or both will be present.
+
+		uint64 Time [readonly, optional]
+
+			Time of the transferred object if this is
+			provided by the remote party.
+
+		uint64 Size [readonly, optional]
+
+			Size of the transferred object. If the size is
+			unknown, then this property will not be present.
+
+		uint64 Transferred [readonly, optional]
+
+			Number of bytes transferred. For queued transfers, this
+			value will not be present.
+
+		string Filename [readonly, optional]
+
+			Complete name of the file being received or sent.
+
+			For incoming object push transaction, this will be
+			the proposed default location and name. It can be
+			overwritten by the AuthorizePush agent callback
+			and will be then updated accordingly.
+
+
+Object Push hierarchy
+=====================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.ObjectPush1
+Object path	[Session object path]
+
+Methods		object, dict SendFile(string sourcefile)
+
+			Send one local file to the remote device.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		object, dict PullBusinessCard(string targetfile)
+
+			Request the business card from a remote device and
+			store it in the local file.
+
+			If an empty target file is given, a name will be
+			automatically calculated for the temporary file.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		object, dict ExchangeBusinessCards(string clientfile,
+							string targetfile)
+
+			Push the client's business card to the remote device
+			and then retrieve the remote business card and store
+			it in a local file.
+
+			If an empty target file is given, a name will be
+			automatically calculated for the temporary file.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+
+File Transfer hierarchy
+=======================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.FileTransfer
+Object path	[Session object path]
+
+Methods		void ChangeFolder(string folder)
+
+			Change the current folder of the remote device.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		void CreateFolder(string folder)
+
+			Create a new folder in the remote device.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		array{dict} ListFolder()
+
+			Returns a dictionary containing information about
+			the current folder content.
+
+			The following keys are defined:
+
+				string Name : Object name in UTF-8 format
+				string Type : Either "folder" or "file"
+				uint64 Size : Object size or number of items in
+						folder
+				string Permission : Group, owner and other
+							permission
+				uint64 Modified : Last change
+				uint64 Accessed : Last access
+				uint64 Created : Creation date
+
+			Possible errors: org.bluez.obex.Error.Failed
+
+		object, dict GetFile(string targetfile, string sourcefile)
+
+			Copy the source file (from remote device) to the
+			target file (on local filesystem).
+
+			If an empty target file is given, a name will be
+			automatically calculated for the temporary file.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		object, dict PutFile(string sourcefile, string targetfile)
+
+			Copy the source file (from local filesystem) to the
+			target file (on remote device).
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		void CopyFile(string sourcefile, string targetfile)
+
+			Copy a file within the remote device from source file
+			to target file.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		void MoveFile(string sourcefile, string targetfile)
+
+			Move a file within the remote device from source file
+			to the target file.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		void Delete(string file)
+
+			Deletes the specified file/folder.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+
+Phonebook Access hierarchy
+==========================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.PhonebookAccess1
+Object path	[Session object path]
+
+Methods		void Select(string location, string phonebook)
+
+			Select the phonebook object for other operations. Should
+			be call before all the other operations.
+
+			location : Where the phonebook is stored, possible
+			inputs :
+				"int" ( "internal" which is default )
+				"sim" ( "sim1" )
+				"sim2"
+				...
+
+			phonebook : Possible inputs :
+				"pb" :	phonebook for the saved contacts
+				"ich":	incoming call history
+				"och":	outgoing call history
+				"mch":	missing call history
+				"cch":	combination of ich och mch
+				"spd":	speed dials entry ( only for "internal" )
+				"fav":	favorites entry ( only for "internal" )
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		object, dict PullAll(string targetfile, dict filters)
+
+			Return the entire phonebook object from the PSE server
+			in plain string with vcard format, and store it in
+			a local file.
+
+			If an empty target file is given, a name will be
+			automatically calculated for the temporary file.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible filters: Format, Order, Offset, MaxCount and
+			Fields
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					org.bluez.obex.Forbidden
+
+		array{string vcard, string name} List(dict filters)
+
+			Return an array of vcard-listing data where every entry
+			consists of a pair of strings containing the vcard
+			handle and the contact name. For example:
+				"1.vcf" : "John"
+
+			Possible filters: Order, Offset and MaxCount
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Forbidden
+
+		object, dict
+		Pull(string vcard, string targetfile, dict filters)
+
+			Given a vcard handle, retrieve the vcard in the current
+			phonebook object and store it in a local file.
+
+			If an empty target file is given, a name will be
+			automatically calculated for the temporary file.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possbile filters: Format and Fields
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Forbidden
+					 org.bluez.obex.Error.Failed
+
+		array{string vcard, string name}
+		Search(string field, string value, dict filters)
+
+			Search for entries matching the given condition and
+			return an array of vcard-listing data where every entry
+			consists of a pair of strings containing the vcard
+			handle and the contact name.
+
+			vcard : name paired string match the search condition.
+
+			field : the field in the vcard to search with
+				{ "name" (default) | "number" | "sound" }
+			value : the string value to search for
+
+
+			Possible filters: Order, Offset and MaxCount
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Forbidden
+					 org.bluez.obex.Error.Failed
+
+		uint16 GetSize()
+
+			Return the number of entries in the selected phonebook
+			object that are actually used (i.e. indexes that
+			correspond to non-NULL entries).
+
+			Possible errors: org.bluez.obex.Error.Forbidden
+					 org.bluez.obex.Error.Failed
+
+		void UpdateVersion()
+
+			Attempt to update PrimaryCounter and SecondaryCounter.
+
+			Possible errors: org.bluez.obex.Error.NotSupported
+					 org.bluez.obex.Error.Forbidden
+					 org.bluez.obex.Error.Failed
+
+		array{string} ListFilterFields()
+
+			Return All Available fields that can be used in Fields
+			filter.
+
+			Possible errors: None
+
+Filter:		string Format:
+
+			Items vcard format
+
+			Possible values: "vcard21" (default) or "vcard30"
+
+		string Order:
+
+			Items order
+
+			Possible values: "indexed" (default), "alphanumeric" or
+			"phonetic"
+
+		uint16 Offset:
+
+			Offset of the first item, default is 0
+
+		uint16 MaxCount:
+
+			Maximum number of items, default is unlimited (65535)
+
+		array{string} Fields:
+
+			Item vcard fields, default is all values.
+
+			Possible values can be query with ListFilterFields.
+
+		array{string} FilterAll:
+
+			Filter items by fields using AND logic, cannot be used
+			together with FilterAny.
+
+			Possible values can be query with ListFilterFields.
+
+		array{string} FilterAny:
+
+			Filter items by fields using OR logic, cannot be used
+			together with FilterAll.
+
+			Possible values can be query with ListFilterFields.
+
+		bool ResetNewMissedCalls
+
+			Reset new the missed calls items, shall only be used
+			for folders mch and cch.
+
+Properties	string Folder [readonly]
+
+			Current folder.
+
+		string DatabaseIdentifier [readonly, optional]
+
+			128 bits persistent database identifier.
+
+			Possible values: 32-character hexadecimal such
+			as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6
+
+		string PrimaryCounter [readonly, optional]
+
+			128 bits primary version counter.
+
+			Possible values: 32-character hexadecimal such
+			as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6
+
+		string SecondaryCounter [readonly, optional]
+
+			128 bits secondary version counter.
+
+			Possible values: 32-character hexadecimal such
+			as A1A2A3A4B1B2C1C2D1D2E1E2E3E4E5E6
+
+		bool FixedImageSize [readonly, optional]
+
+			Indicate support for fixed image size.
+
+			Possible values: True if image is JPEG 300x300 pixels
+			otherwise False.
+
+Synchronization hierarchy
+=========================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.Synchronization1
+Object path	[Session object path]
+
+Methods		void SetLocation(string location)
+
+			Set the phonebook object store location for other
+			operations. Should be called before all the other
+			operations.
+
+			location: Where the phonebook is stored, possible
+			values:
+				"int" ( "internal" which is default )
+				"sim1"
+				"sim2"
+				......
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+
+		object, dict GetPhonebook(string targetfile)
+
+			Retrieve an entire Phonebook Object store from remote
+			device, and stores it in a local file.
+
+			If an empty target file is given, a name will be
+			automatically calculated for the temporary file.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		object, dict PutPhonebook(string sourcefile)
+
+			Send an entire Phonebook Object store to remote device.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+
+Message Access hierarchy
+=========================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.MessageAccess1
+Object path	[Session object path]
+
+Methods		void SetFolder(string name)
+
+			Set working directory for current session, *name* may
+			be the directory name or '..[/dir]'.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		array{dict} ListFolders(dict filter)
+
+			Returns a dictionary containing information about
+			the current folder content.
+
+			The following keys are defined:
+
+				string Name : Folder name
+
+			Possible filters: Offset and MaxCount
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		array{string} ListFilterFields()
+
+			Return all available fields that can be used in Fields
+			filter.
+
+			Possible errors: None
+
+		array{object, dict} ListMessages(string folder, dict filter)
+
+			Returns an array containing the messages found in the
+			given subfolder of the current folder, or in the
+			current folder if folder is empty.
+
+			Possible Filters: Offset, MaxCount, SubjectLength, Fields,
+			Type, PeriodStart, PeriodEnd, Status, Recipient, Sender,
+			Priority
+
+			Each message is represented by an object path followed
+			by a dictionary of the properties.
+
+			Properties:
+
+				string Subject:
+
+					Message subject
+
+				string Timestamp:
+
+					Message timestamp
+
+				string Sender:
+
+					Message sender name
+
+				string SenderAddress:
+
+					Message sender address
+
+				string ReplyTo:
+
+					Message Reply-To address
+
+				string Recipient:
+
+					Message recipient name
+
+				string RecipientAddress:
+
+					Message recipient address
+
+				string Type:
+
+					Message type
+
+					Possible values: "email", "sms-gsm",
+					"sms-cdma" and "mms"
+
+				uint64 Size:
+
+					Message size in bytes
+
+				boolean Text:
+
+					Message text flag
+
+					Specifies whether message has textual
+					content or is binary only
+
+				string Status:
+
+					Message status
+
+					Possible values for received messages:
+					"complete", "fractioned", "notification"
+
+					Possible values for sent messages:
+					"delivery-success", "sending-success",
+					"delivery-failure", "sending-failure"
+
+				uint64 AttachmentSize:
+
+					Message overall attachment size in bytes
+
+				boolean Priority:
+
+					Message priority flag
+
+				boolean Read:
+
+					Message read flag
+
+				boolean Sent:
+
+					Message sent flag
+
+				boolean Protected:
+
+					Message protected flag
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+		void UpdateInbox(void)
+
+			Request remote to update its inbox.
+
+			Possible errors: org.bluez.obex.Error.Failed
+
+		object, dict
+		PushMessage(string sourcefile, string folder, dict args)
+
+			Transfer a message (in bMessage format) to the
+			remote device.
+
+			The message is transferred either to the given
+			subfolder of the current folder, or to the current
+			folder if folder is empty.
+
+			Possible args: Transparent, Retry, Charset
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetAll.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+
+Filter:		uint16 Offset:
+
+			Offset of the first item, default is 0
+
+		uint16 MaxCount:
+
+			Maximum number of items, default is 1024
+
+		byte SubjectLength:
+
+			Maximum length of the Subject property in the
+			message, default is 256
+
+		array{string} Fields:
+
+			Message fields, default is all values.
+
+			Possible values can be query with ListFilterFields.
+
+		array{string} Types:
+
+			Filter messages by type.
+
+			Possible values: "sms", "email", "mms".
+
+		string PeriodBegin:
+
+			Filter messages by starting period.
+
+			Possible values: Date in "YYYYMMDDTHHMMSS" format.
+
+		string PeriodEnd:
+
+			Filter messages by ending period.
+
+			Possible values: Date in "YYYYMMDDTHHMMSS" format.
+
+		boolean Read:
+
+			Filter messages by read flag.
+
+			Possible values: True for read or False for unread
+
+		string Recipient:
+
+			Filter messages by recipient address.
+
+		string Sender:
+
+			Filter messages by sender address.
+
+		boolean Priority:
+
+			Filter messages by priority flag.
+
+			Possible values: True for high priority or False for
+			non-high priority
+
+Message hierarchy
+=================
+
+Service		org.bluez.obex
+Interface	org.bluez.obex.Message1
+Object path	[Session object path]/{message0,...}
+
+Methods		object, dict Get(string targetfile, boolean attachment)
+
+			Download message and store it in the target file.
+
+			If an empty target file is given, a temporary file
+			will be automatically generated.
+
+			The returned path represents the newly created transfer,
+			which should be used to find out if the content has been
+			successfully transferred or if the operation fails.
+
+			The properties of this transfer are also returned along
+			with the object path, to avoid a call to GetProperties.
+
+			Possible errors: org.bluez.obex.Error.InvalidArguments
+					 org.bluez.obex.Error.Failed
+
+Properties	string Folder [readonly]
+
+			Folder which the message belongs to
+
+		string Subject [readonly]
+
+			Message subject
+
+		string Timestamp [readonly]
+
+			Message timestamp
+
+		string Sender [readonly]
+
+			Message sender name
+
+		string SenderAddress [readonly]
+
+			Message sender address
+
+		string ReplyTo [readonly]
+
+			Message Reply-To address
+
+		string Recipient [readonly]
+
+			Message recipient name
+
+		string RecipientAddress [readonly]
+
+			Message recipient address
+
+		string Type [readonly]
+
+			Message type
+
+			Possible values: "email", "sms-gsm",
+			"sms-cdma" and "mms"
+
+		uint64 Size [readonly]
+
+			Message size in bytes
+
+		string Status [readonly]
+
+			Message reception status
+
+			Possible values: "complete",
+			"fractioned" and "notification"
+
+		boolean Priority [readonly]
+
+			Message priority flag
+
+		boolean Read [read/write]
+
+			Message read flag
+
+		boolean Deleted [writeonly]
+
+			Message deleted flag
+
+		boolean Sent [readonly]
+
+			Message sent flag
+
+		boolean Protected [readonly]
+
+			Message protected flag
diff --git a/bluez/repo/doc/pics-opp.txt b/bluez/repo/doc/pics-opp.txt
new file mode 100644
index 0000000..fb4746d
--- /dev/null
+++ b/bluez/repo/doc/pics-opp.txt
@@ -0,0 +1,187 @@
+OPP PICS for the PTS tool.
+
+PTS version: 6.2.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+		Roles
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1_1	True (*)	Role: Object Push Client (C.1)
+TSPC_OPP_1_2	True (*)	Role: Object Push Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+		Client Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1b_1	False		Client supports OPP version 1.1. (C.1)
+TSPC_OPP_1b_2	True (*)	Client supports OPP version 1.2. (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Client Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2_1	True		Client: Perform Service Discovery request (M)
+TSPC_OPP_2_2	True		Client: Authentication/PIN exchange supported.
+					(M)
+TSPC_OPP_2_2a	True (*)	Client: Require Authentication/PIN by default.
+					(O)
+TSPC_OPP_2_3	True		Client: Object Push (M)
+TSPC_OPP_2_4	True (*)	Client: vCard 2.1 (C.3)
+TSPC_OPP_2_5	False		Client: vCalender 1.0 (O)
+TSPC_OPP_2_6	False		Client: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_2_7	False		Client: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_2_8	True (*)	Client: Support content formats other than those
+					declared in TSPC_OPP_2_4 through
+					TSPC_OPP_2_7. (O)
+TSPC_OPP_2_8a	False		Client: Support specific set of other content
+					formats. (C.4)
+TSPC_OPP_2_8b	True (*)	Client: Support all content formats. (C.4)
+TSPC_OPP_2_9	True (*)	Client: Push multiple vCard objects. (O)
+TSPC_OPP_2_9a	True (*)	Client: Push multiple vCard objects using
+					different PUT operations. (C.5)
+TSPC_OPP_2_9b	False		Client: Push multiple vCard objects using the
+					same PUT operation. (C.5)
+TSPC_OPP_2_10	True (*)	Client: Push multiple vCalender objects. (O)
+TSPC_OPP_2_10a	True		Client: Push multiple vCalendar objects using
+					different PUT operations. (C.6)
+TSPC_OPP_2_10b	False		Client: Push multiple vCalendar objects using
+					the same PUT operation. (C.6)
+TSPC_OPP_2_11	True (*)	Client: Push multiple vMsg objects. (O)
+TSPC_OPP_2_11a	True (*)	Client: Push multiple vMsg objects using
+					different PUT operations. (C.7)
+TSPC_OPP_2_11b	False		Client: Push multiple vMsg objects using the
+					same PUT operation. (C.7)
+TSPC_OPP_2_12	True (*)	Client: Push multiple vNote objects. (O)
+TSPC_OPP_2_12a	True (*)	Client: Push multiple vNote objects using
+					different PUT operations. (C.8)
+TSPC_OPP_2_12b	False		Client: Push multiple vNote objects using the
+					same PUT operation. (C.8)
+TSPC_OPP_2_13	True (*)	Client: Pull business card (O)
+TSPC_OPP_2_14	True (*)	Client: vCard 2.1 (C.1)
+TSPC_OPP_2_15	True (*)	Client: Exchange business card (O)
+TSPC_OPP_2_16	False		Client: vCard 2.1 (C.2)
+TSPC_OPP_2_17	True (*)	GOEP v2 (C.9)
+TSPC_OPP_2_18	True (*)	GOEP v2 Backward Compatibility (C.9)
+TSPC_OPP_2_19	True (*)	OBEX over L2CAP (C.9)
+TSPC_OPP_2_20	False		OBEX Reliable Session (C.10)
+TSPC_OPP_2_21	False		OBEX SRM (C.10)
+TSPC_OPP_2_22	False		Send OBEX SRMP header (C.10)
+TSPC_OPP_2_23	False		Receive OBEX SRMP header (C.11)
+-------------------------------------------------------------------------------
+C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is
+	supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+	applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8
+	is supported. Otherwise, both items are excluded.
+C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if
+	TSPC_OPP_2_9 is supported. Otherwise, both items are excluded.
+C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if
+	TSPC_OPP_2_10 is supported. Otherwise, both items are excluded.
+C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if
+	TSPC_OPP_2_11 is supported. Otherwise, both items are excluded.
+C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if
+	TSPC_OPP_2_12 is supported. Otherwise, both items are excluded.
+C.9: Mandatory if TSPC_OPP_1b_2 supported.
+C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded.
+C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+		Server Profile Version
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2b_1	False		Server supports OPP version 1.1.
+TSPC_OPP_2b_2	True (*)	Server supports OPP version 1.2.
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+		Server Application Features
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_3_1	True		Server: Provide information on supported
+					contents type on service discovery
+					request. (M)
+TSPC_OPP_3_2	True		Server: Authentication/PIN exchange supported.
+					(M)
+TSPC_OPP_3_3	True		Server: Object Push (M)
+TSPC_OPP_3_3a	True (*)	Server: Receive multiple objects in the same
+					PUT operation. (O)
+TSPC_OPP_3_4	True (*)	Server: vCard 2.1 (C.3)
+TSPC_OPP_3_5	False		Server: vCalender 1.0 format (O)
+TSPC_OPP_3_6	False		Server: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_3_7	False		Server: vNote as defined in IrMC 1.1 (O)
+TSPC_OPP_3_8	True (*)	Server: Support content formats other than those
+					declared in TSPC_OPP_3_4 through
+					TSPC_OPP_3_7. (O)
+TSPC_OPP_3_8a	False		Server: Support specific set of other content
+					formats. (C.4)
+TSPC_OPP_3_8b	True (*)	Server: Support all content formats. (C.4)
+TSPC_OPP_3_9	True (*)	Server: Object Push vCard reject. (O)
+TSPC_OPP_3_10	True (*)	Server: Object Push vCal reject. (O)
+TSPC_OPP_3_11	True (*)	Server: Object Push vMsg reject. (O)
+TSPC_OPP_3_12	True (*)	Server: Object Push vNote reject. (O)
+TSPC_OPP_3_13	True (*)	Server: Business card pull (O.1)
+TSPC_OPP_3_14	True (*)	Server: vCard 2.1 (C.1)
+TSPC_OPP_3_15	True (*)	Server: Business card pull reject. (O)
+TSPC_OPP_3_16	True (*)	Server: Business card exchange (O.2)
+TSPC_OPP_3_17	True (*)	Server: vCard 2.1 (C.2)
+TSPC_OPP_3_18	True (*)	Server: Business card exchange reject. (O)
+TSPC_OPP_3_19	True (*)	GOEP v2 (C.5)
+TSPC_OPP_3_20	True (*)	GOEP v2 Backward Compatibility (C.5)
+TSPC_OPP_3_21	True (*)	OBEX over L2CAP (C.5)
+TSPC_OPP_3_22	False		OBEX Reliable Session (C.16)
+TSPC_OPP_3_23	False		OBEX SRM (C.6)
+TSPC_OPP_3_24	False		Send OBEX SRMP header (C.6)
+TSPC_OPP_3_25	False		Receive OBEX SRMP header (C.7)
+-------------------------------------------------------------------------------
+O.1: IF NOT Supported, an error message must be sent on request for Business
+	Card Pull.
+O.2: IF NOT Supported, an error message must be sent on request for Business
+	Card Exchange.
+C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
+	supported.
+C.3: vCard 2.1 support is required for devices containing phonebook
+	applications. vCard 2.1 support optional for other devices.
+C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8
+	is supported. Otherwise, both items are excluded.
+C.5: Mandatory if TSPC_OPP_2b_2 supported.
+C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded.
+C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded.
+-------------------------------------------------------------------------------
+
+
+		Additional OPP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name	Selected	Description
+-------------------------------------------------------------------------------
+TSPC_OPP_4_1	False		Abort-Push Operation (O)
+TSPC_OPP_4_2	False		Intentionally Left Blank (N/A)
+TSPC_OPP_4_3	True (*)	Multiple vCards transferred as a single vObject
+					(C.1)
+TSPC_OPP_4_4	True (*)	Multiple vCards transfer (C.1)
+TSPC_OPP_4_5	True (*)	vCards with multiple Phone Number Fields (C.1)
+TSPC_OPP_4_6	True (*)	Push vCal to Different Time Zone Server (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/doc/pixit-opp.txt b/bluez/repo/doc/pixit-opp.txt
new file mode 100644
index 0000000..b7461d6
--- /dev/null
+++ b/bluez/repo/doc/pixit-opp.txt
@@ -0,0 +1,27 @@
+OPP PIXIT for the PTS tool.
+
+PTS version: 6.2.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+		Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name						Value
+-------------------------------------------------------------------------------
+TSPX_supported_extension				bmp
+TSPX_unsupported_extension				pts
+TSPX_client_class_of_device				100104
+TSPX_server_class_of_device				100104
+TSPX_auth_password					0000
+TSPX_auth_user_id					PTS
+TSPX_l2cap_psm						1003
+TSPX_rfcomm_channel					8
+TSPX_no_confirmations					FALSE
+TSPX_bd_addr_iut					112233445566 (*&)
+TSPX_delete_link_key					FALSE
+TSPX_pin_code						0000
+TSPX_security_enabled					FALSE
+TSPX_time_guard						300000
+TSPX_use_implicit_send					TRUE
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/doc/profile-api.txt b/bluez/repo/doc/profile-api.txt
new file mode 100644
index 0000000..ec18034
--- /dev/null
+++ b/bluez/repo/doc/profile-api.txt
@@ -0,0 +1,147 @@
+BlueZ D-Bus Profile API description
+***********************************
+
+
+Profile Manager hierarchy
+=========================
+
+Service		org.bluez
+Interface	org.bluez.ProfileManager1
+Object path	/org/bluez
+
+		void RegisterProfile(object profile, string uuid, dict options)
+
+			This registers a profile implementation.
+
+			If an application disconnects from the bus all
+			its registered profiles will be removed.
+
+			HFP HS UUID: 0000111e-0000-1000-8000-00805f9b34fb
+
+				Default RFCOMM channel is 6. And this requires
+				authentication.
+
+			Available options:
+
+				string Name
+
+					Human readable name for the profile
+
+				string Service
+
+					The primary service class UUID
+					(if different from the actual
+					 profile UUID)
+
+				string Role
+
+					For asymmetric profiles that do not
+					have UUIDs available to uniquely
+					identify each side this
+					parameter allows specifying the
+					precise local role.
+
+					Possible values: "client", "server"
+
+				uint16 Channel
+
+					RFCOMM channel number that is used
+					for client and server UUIDs.
+
+					If applicable it will be used in the
+					SDP record as well.
+
+				uint16 PSM
+
+					PSM number that is used for client
+					and server UUIDs.
+
+					If applicable it will be used in the
+					SDP record as well.
+
+				boolean RequireAuthentication
+
+					Pairing is required before connections
+					will be established. No devices will
+					be connected if not paired.
+
+				boolean RequireAuthorization
+
+					Request authorization before any
+					connection will be established.
+
+				boolean AutoConnect
+
+					In case of a client UUID this will
+					force connection of the RFCOMM or
+					L2CAP channels when a remote device
+					is connected.
+
+				string ServiceRecord
+
+					Provide a manual SDP record.
+
+				uint16 Version
+
+					Profile version (for SDP record)
+
+				uint16 Features
+
+					Profile features (for SDP record)
+
+			Possible errors: org.bluez.Error.InvalidArguments
+			                 org.bluez.Error.AlreadyExists
+
+		void UnregisterProfile(object profile)
+
+			This unregisters the profile that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+			Possible errors: org.bluez.Error.DoesNotExist
+
+
+Profile hierarchy
+=================
+
+Service		unique name
+Interface	org.bluez.Profile1
+Object path	freely definable
+
+Methods		void Release() [noreply]
+
+			This method gets called when the service daemon
+			unregisters the profile. A profile can use it to do
+			cleanup tasks. There is no need to unregister the
+			profile, because when this method gets called it has
+			already been unregistered.
+
+		void NewConnection(object device, fd, dict fd_properties)
+
+			This method gets called when a new service level
+			connection has been made and authorized.
+
+			Common fd_properties:
+
+			uint16 Version		Profile version (optional)
+			uint16 Features		Profile features (optional)
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
+
+		void RequestDisconnection(object device)
+
+			This method gets called when a profile gets
+			disconnected.
+
+			The file descriptor is no longer owned by the service
+			daemon and the profile implementation needs to take
+			care of cleaning up all connections.
+
+			If multiple file descriptors are indicated via
+			NewConnection, it is expected that all of them
+			are disconnected before returning from this
+			method call.
+
+			Possible errors: org.bluez.Error.Rejected
+			                 org.bluez.Error.Canceled
diff --git a/bluez/repo/doc/proximity-api.txt b/bluez/repo/doc/proximity-api.txt
new file mode 100644
index 0000000..5322544
--- /dev/null
+++ b/bluez/repo/doc/proximity-api.txt
@@ -0,0 +1,56 @@
+BlueZ D-Bus Proximity API description
+***********************************
+
+
+Proximity Monitor hierarchy
+===========================
+
+Service		org.bluez
+Interface	org.bluez.ProximityMonitor1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Properties	string SignalLevel [readonly]
+
+			Alert indicating that a threshold has been reached.
+			Possible values: "unknown", "good", "regular", "weak"
+
+		string LinkLossAlertLevel [readwrite]
+
+			Persistent property. Sets the alert level in the
+			proximity reporter for link loss scenario. Values:
+			"none", "mild", "high".
+
+		string ImmediateAlertLevel [readwrite]
+
+			Alert level to be written in the Immediate Alert Level.
+			Property shared between Path Loss and Find Me.
+			Values: "none", "mild", "high". Default value is
+			"none". Applications can disable the alert setting
+			the value to "none". If the "Target" is not found,
+			"none" will be emitted after the configured timeout.
+			When changing the level, signal is the confirmation
+			that the value was written in the remote.
+
+Proximity Reporter hierarchy
+===========================
+
+Shared service used by Proximity Path Loss and Find Me. Allows per device
+alert level handling.
+
+Service		org.bluez
+Interface	org.bluez.ProximityReporter1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Properties	string ImmediateAlertLevel [readonly]
+
+			This property indicates that Immediate Alert Level
+			characteristic in the local Immediate Alert Service
+			was changed by the remote device. Property shared
+			between Path Loss and Find Me. Values: "none", "mild",
+			"high".
+
+		string LinkLossAlertLevel [readonly]
+
+			This property indicates that Alert Level characteristic
+			in the local Link Loss Service was changed by the
+			remote device. Values: "none", "mild", "high".
diff --git a/bluez/repo/doc/pts-opp.txt b/bluez/repo/doc/pts-opp.txt
new file mode 100644
index 0000000..832028c
--- /dev/null
+++ b/bluez/repo/doc/pts-opp.txt
@@ -0,0 +1,119 @@
+PTS test results for OPP
+
+PTS version: 6.2.0
+Tested: 26-Aug-2015
+
+Results:
+PASS	test passed
+FAIL	test failed
+INC	test is inconclusive
+N/A	test is disabled due to PICS setup
+NONE	test result is none
+
+-------------------------------------------------------------------------------
+Test Name		Result	Notes
+-------------------------------------------------------------------------------
+TC_CLIENT_BC_BV_02_I	PASS
+TC_CLIENT_BC_BV_04_I	PASS
+TC_CLIENT_BCE_BV_01_I   PASS
+TC_CLIENT_BCE_BV_03_I   N/A
+TC_CLIENT_BCE_BV_04_I   PASS
+TC_CLIENT_BCE_BV_05_I   PASS
+TC_CLIENT_BCE_BV_06_I   PASS
+TC_CLIENT_BCE_BV_07_I   PASS
+TC_CLIENT_BCP_BV_01_I   PASS
+TC_CLIENT_BCP_BV_02_I   PASS
+TC_CLIENT_BCP_BV_03_I   N/A
+TC_CLIENT_BCP_BV_04_I   PASS
+TC_CLIENT_BCP_BV_05_I   PASS
+TC_CLIENT_CON_BV_01_C	PASS
+TC_CLIENT_OPH_BI_01_C	PASS
+TC_CLIENT_OPH_BV_01_I	PASS
+TC_CLIENT_OPH_BV_02_I	N/A
+TC_CLIENT_OPH_BV_03_I	PASS
+TC_CLIENT_OPH_BV_04_I	PASS
+TC_CLIENT_OPH_BV_05_I	PASS
+TC_CLIENT_OPH_BV_07_I	N/A
+TC_CLIENT_OPH_BV_08_I	PASS
+TC_CLIENT_OPH_BV_09_I	N/A
+TC_CLIENT_OPH_BV_10_I	N/A
+TC_CLIENT_OPH_BV_11_I	N/A
+TC_CLIENT_OPH_BV_12_I	PASS
+TC_CLIENT_OPH_BV_13_I	N/A
+TC_CLIENT_OPH_BV_14_I	N/A
+TC_CLIENT_OPH_BV_15_I	N/A
+TC_CLIENT_OPH_BV_16_I	PASS
+TC_CLIENT_OPH_BV_17_I	N/A
+TC_CLIENT_OPH_BV_18_I	N/A
+TC_CLIENT_OPH_BV_19_I	PASS	Send file other than vCard
+TC_CLIENT_OPH_BV_20_I	PASS
+TC_CLIENT_OPH_BV_22_I	PASS	Send file greater than 2 MB
+TC_CLIENT_OPH_BV_23_I	N/A
+TC_CLIENT_OPH_BV_24_I	N/A
+TC_CLIENT_OPH_BV_25_I	N/A
+TC_CLIENT_OPH_BV_26_I	N/A
+TC_CLIENT_SRM_BV_01_C	N/A
+TC_CLIENT_SRM_BV_03_C	N/A
+TC_CLIENT_SRM_BV_05_C	N/A
+TC_CLIENT_SRM_BV_07_C	N/A
+TC_CLIENT_SRMP_BI_01_C	N/A
+TC_CLIENT_SRMP_BV_01_C	N/A
+TC_CLIENT_SRMP_BV_04_C	N/A
+TC_CLIENT_SRMP_BV_05_C	N/A
+TC_CLIENT_SRMP_BV_06_C	N/A
+TC_SERVER_BC_BV_01_I	PASS
+TC_SERVER_BC_BV_03_I	PASS
+TC_SERVER_BCE_BV_01_I	PASS
+TC_SERVER_BCE_BV_03_I	PASS
+TC_SERVER_BCE_BV_04_I	PASS
+TC_SERVER_BCE_BV_05_I	PASS
+TC_SERVER_BCE_BV_06_I	PASS
+TC_SERVER_BCE_BV_07_I	PASS
+TC_SERVER_BCP_BV_01_I	PASS
+TC_SERVER_BCP_BV_02_I	N/A
+TC_SERVER_BCP_BV_03_I	PASS
+TC_SERVER_BCP_BV_04_I	PASS
+TC_SERVER_BCP_BV_05_I	PASS
+TC_SERVER_CON_BV_02_C	PASS
+TC_SERVER_OPH_BV_01_I	PASS
+TC_SERVER_OPH_BV_02_I	PASS
+TC_SERVER_OPH_BV_03_I	PASS
+TC_SERVER_OPH_BV_04_I	PASS
+TC_SERVER_OPH_BV_05_I	PASS
+TC_SERVER_OPH_BV_07_I	N/A
+TC_SERVER_OPH_BV_08_I	N/A
+TC_SERVER_OPH_BV_09_I	PASS
+TC_SERVER_OPH_BV_10_I	PASS
+TC_SERVER_OPH_BV_11_I	N/A
+TC_SERVER_OPH_BV_12_I	N/A
+TC_SERVER_OPH_BV_13_I	PASS
+TC_SERVER_OPH_BV_14_I	PASS
+TC_SERVER_OPH_BV_15_I	N/A
+TC_SERVER_OPH_BV_16_I	N/A
+TC_SERVER_OPH_BV_17_I	PASS
+TC_SERVER_OPH_BV_18_I	PASS
+TC_SERVER_OPH_BV_19_I	PASS
+TC_SERVER_OPH_BV_21_I	N/A
+TC_SERVER_OPH_BV_22_I	PASS
+TC_SERVER_OPH_BV_23_I	PASS
+TC_SERVER_OPH_BV_24_I	N/A
+TC_SERVER_OPH_BV_25_I	N/A
+TC_SERVER_OPH_BV_26_I	N/A
+TC_SERVER_ROB_BV_01_C	PASS
+TC_SERVER_ROB_BV_02_C	PASS
+TC_SERVER_SRM_BI_02_C	N/A
+TC_SERVER_SRM_BI_03_C	PASS
+TC_SERVER_SRM_BI_05_C	N/A
+TC_SERVER_SRM_BV_04_C	N/A
+TC_SERVER_SRM_BV_08_C	N/A
+TC_SERVER_SRMP_BV_02_C	N/A
+TC_SERVER_SRMP_BV_03_C	N/A
+TC_CLIENT_OPH_BV_27_I	N/A
+TC_CLIENT_OPH_BV_34_I	PASS
+TC_SERVER_OPH_BV_27_I	N/A
+TC_SERVER_OPH_BV_30_I	PASS
+TC_SERVER_OPH_BV_31_I	PASS
+TC_SERVER_OPH_BV_32_I	PASS
+TC_SERVER_OPH_BV_33_I	N/A
+TC_SERVER_OPH_BV_34_I	PASS
+-------------------------------------------------------------------------------
diff --git a/bluez/repo/doc/sap-api.txt b/bluez/repo/doc/sap-api.txt
new file mode 100644
index 0000000..b28c4e3
--- /dev/null
+++ b/bluez/repo/doc/sap-api.txt
@@ -0,0 +1,20 @@
+BlueZ D-Bus Sim Access API description
+**************************************
+
+
+Sim Access Profile hierarchy
+============================
+
+Service		org.bluez
+Interface	org.bluez.SimAccess1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void Disconnect()
+
+			Disconnects SAP client from the server.
+
+			Possible errors: org.bluez.Error.Failed
+
+Properties	boolean Connected [readonly]
+
+			Indicates if SAP client is connected to the server.
diff --git a/bluez/repo/doc/settings-storage.txt b/bluez/repo/doc/settings-storage.txt
new file mode 100644
index 0000000..aea50a6
--- /dev/null
+++ b/bluez/repo/doc/settings-storage.txt
@@ -0,0 +1,283 @@
+BlueZ settings storage
+**********************
+
+Purpose
+=======
+
+The purpose of this document is to describe the directory structure of
+BlueZ settings storage. In effect, this document will serve as the primary,
+up to date source of BlueZ storage information.
+
+It is intended as reference for developers. Direct access to the storage
+outside from bluetoothd is highly discouraged.
+
+Adapter and remote device info are read form the storage during object
+initialization. Write to storage is performed immediately on every value
+change.
+
+Default storage directory is /var/lib/bluetooth. This can be adjusted
+by the --localstatedir configure switch. Default is --localstatedir=/var.
+
+All files are in ini-file format.
+
+
+Storage directory structure
+===========================
+
+There is one directory per adapter, named by its Bluetooth address, which
+contains:
+ - a settings file for the local adapter
+ - an attributes file containing attributes of supported LE services
+ - a cache directory containing:
+    - one file per device, named by remote device address, which contains
+    device name
+ - one directory per remote device, named by remote device address, which
+   contains:
+    - an info file
+    - an attributes file containing attributes of remote LE services
+    - a ccc file containing persistent Client Characteristic Configuration
+      (CCC) descriptor information for GATT characteristics
+
+So the directory structure is:
+    /var/lib/bluetooth/<adapter address>/
+        ./settings
+        ./attributes
+        ./cache/
+            ./<remote device address>
+            ./<remote device address>
+            ...
+        ./<remote device address>/
+            ./info
+            ./attributes
+            ./ccc
+        ./<remote device address>/
+            ./info
+            ./attributes
+        ...
+
+
+Settings file format
+====================
+
+Settings file contains one [General] group with adapter info like:
+
+  Alias			String		Friendly user provided name advertised
+					for this adapter
+
+					This value overwrites the system
+					name (pretty hostname)
+
+  Discoverable		Boolean		Discoverability of the adapter
+
+  PairableTimeout	Integer		How long to stay in pairable mode
+					before going back to non-pairable.
+					The value is in seconds.
+					0 = disable timer, i.e. stay
+					pairable forever
+
+  DiscoverableTimeout	Integer		How long to stay in discoverable mode
+					before going back to non-discoverable.
+					The value is in seconds.
+					0 = disable timer, i.e. stay
+					discoverable forever
+
+Sample:
+  [General]
+  Name=My PC
+  Discoverable=false
+  Pairable=true
+  DiscoverableTimeout=0
+
+
+Attributes file format
+======================
+
+The attributes file lists all attributes supported by the local adapter or
+remote device.
+
+Attributes are stored using their handle as group name (decimal format).
+
+Each group contains:
+
+  UUID			String		128-bit UUID of the attribute
+
+  Value			String		Value of the attribute as hexadecimal encoded
+					string
+
+  EndGroupHandle	Integer		End group handle in decimal format
+
+Sample:
+  [1]
+  UUID=00002800-0000-1000-8000-00805f9b34fb
+  Value=0018
+
+  [4]
+  UUID=00002803-0000-1000-8000-00805f9b34fb
+  Value=020600002A
+
+  [6]
+  UUID=00002a00-0000-1000-8000-00805f9b34fb
+  Value=4578616D706C6520446576696365
+
+
+CCC file format
+======================
+
+The ccc file stores the current CCC descriptor values for GATT characteristics
+which have notification/indication enabled by the remote device.
+
+Information is stored using CCC attribute handle as group name (in decimal
+format).
+
+Each group contains:
+
+  Value			String		CCC descriptor value encoded in
+					hexadecimal
+
+
+Cache directory file format
+============================
+
+Each file, named by remote device address, may includes multiple groups
+(General, ServiceRecords, Attributes).
+
+In ServiceRecords, SDP records are stored using their handle as key
+(hexadecimal format).
+
+In "Attributes" group GATT database is stored using attribute handle as key
+(hexadecimal format). Value associated with this handle is serialized form of
+all data required to re-create given attribute. ":" is used to separate fields.
+
+[General] group contains:
+
+  Name		String		Remote device friendly name
+
+  ShortName	String		Remote device shortened name
+
+[ServiceRecords] group contains
+
+  <0x...>	String		SDP record as hexadecimal encoded
+				string
+
+In [Attributes] group value always starts with attribute type, that determines
+how to interpret rest of value:
+
+  Primary service:
+    2800:end_handle:uuid
+
+  Secondary service:
+    2801:end_handle:uuid
+
+  Included service:
+    2802:start_handle:end_handle:uuid
+
+  Characteristic:
+    2803:value_handle:properties:uuid
+
+  Descriptor:
+    uuid
+
+Sample Attributes section:
+  [Attributes]
+  0001=2800:0005:1801
+  0002=2803:0003:20:2a05
+  0014=2800:001c:1800
+  0015=2803:0016:02:2a00
+  0017=2803:0018:02:2a01
+  0019=2803:001a:02:2aa6
+  0028=2800:ffff:0000180d-0000-1000-8000-00805f9b34fb
+  0029=2803:002a:10:00002a37-0000-1000-8000-00805f9b34fb
+  002b=2803:002c:02:00002a38-0000-1000-8000-00805f9b34fb
+  002d=2803:002e:08:00002a39-0000-1000-8000-00805f9b34fb
+
+
+Info file format
+================
+
+Info file may includes multiple groups (General, Device ID, Link key and
+Long term key) related to a remote device.
+
+[General] group contains:
+
+  Name			String		Remote device friendly name
+
+  Alias			String		Alias name
+
+  Class			String		Device class in hexadecimal,
+					i.e. 0x000000
+
+  Appearance		String		Device appearance in hexadecimal,
+					i.e. 0x0000
+
+  SupportedTechnologies	List of		List of technologies supported by
+			strings		device, separated by ";"
+					Technologies can be BR/EDR or LE
+
+  AddressType		String		An address can be "static" or "public"
+
+  Trusted		Boolean		True if the remote device is trusted
+
+  Blocked		Boolean		True if the remote device is blocked
+
+  Services		List of		List of service UUIDs advertised by
+			strings		remote in 128-bits UUID format,
+					separated by ";"
+
+
+[DeviceID] group contains:
+
+  Source		Integer		Assigner of Device ID
+
+  Vendor		Integer		Device vendor
+
+  Product		Integer		Device product
+
+  Version		Integer		Device version
+
+
+[LinkKey] group contains:
+
+  Key			String		Key in hexadecimal format
+
+  Type			Integer		Type of link key
+
+  PINLength		Integer		Length of PIN
+
+
+[LongTermKey] group contains:
+
+  Key			String		Long term key in hexadecimal format
+
+  Authenticated		Boolean		True if remote device has been
+					authenticated
+
+  EncSize		Integer		Encrypted size
+
+  EDiv			Integer		Encrypted diversifier
+
+  Rand			Integer		Randomizer
+
+
+[SlaveLongTermKey] group contains:
+
+  Same as the [LongTermKey] group, except for slave keys.
+
+
+[ConnectionParameters] group contains:
+
+  MinInterval		Integer		Minimum Connection Interval
+
+  MaxInterval		Integer		Maximum Connection Interval
+
+  Latency		Integer		Connection Latency
+
+  Timeout		Integer		Supervision Timeout
+
+
+[LocalSignatureKey] and [RemoteSignatureKey] groups contain:
+
+  Key			String		Key in hexadecimal format
+
+  Counter		Integer		Signing counter
+
+  Authenticated		Boolean		True if the key is authenticated
diff --git a/bluez/repo/doc/supported-features.txt b/bluez/repo/doc/supported-features.txt
new file mode 100644
index 0000000..33685d5
--- /dev/null
+++ b/bluez/repo/doc/supported-features.txt
@@ -0,0 +1,53 @@
+Supported features in BlueZ
+===========================
+
+Note that some profiles/roles will depend on external components such as
+oFono or ConnMan.
+
+Profile/protocol	Version		Role(s)
+---------------------------------------------------------------------------
+
+GAP			4.2		(LE) Central, Peripheral,
+						Observer, Broadcaster
+L2CAP			4.2		Server, Client
+SDP			4.2		Server, Client
+GATT			4.2		Server, Client
+SDAP			1.1		Server, Client
+RFCOMM			1.1		Server, Client
+SPP			1.1		Server, Client
+
+PXP			1.0		Reporter, Monitor
+HOGP			1.0		Host
+HTP			1.0
+TIP			1.0
+CSCP			1.0		Collector
+
+SAP			1.1		Server
+DUN			1.1		Server, Client
+
+DID			1.3		Server, Client
+
+HFP			1.6		AG, HF
+HSP			1.2		AG, HS
+GAVDTP			1.2		Source, Sink
+AVDTP			1.3		Source, Sink
+A2DP			1.3		Source, Sink
+AVCTP			1.3		CT, TG
+AVRCP			1.5		CT, TG
+
+GOEP			2.0		Client, Server
+FTP			1.2		Client, Server
+OPP			1.2		Client, Server
+SYNCH			1.1		Client
+PBAP			1.1		Client, Server
+MAP			1.0		Client, Server
+
+HID			1.1		Host
+
+BNEP			1.0
+PAN			1.0		PANU, NAP, GN
+
+HCRP			1.2
+
+MCAP			1.0
+HDP			1.0
diff --git a/bluez/repo/doc/test-coverage.txt b/bluez/repo/doc/test-coverage.txt
new file mode 100644
index 0000000..5612ff0
--- /dev/null
+++ b/bluez/repo/doc/test-coverage.txt
@@ -0,0 +1,73 @@
+BlueZ test coverage
+*******************
+
+
+Automated unit testing
+======================
+
+Application		Count	Description
+-------------------------------------------
+test-crc		   9	Link Layer CRC-24 checksum
+test-eir		  14	EIR and AD parsing
+test-lib		  14	SDP library functions
+test-sdp		 133	SDP qualification test cases
+test-uuid		  30	UUID conversion handling
+test-mgmt		   9	Management interface handling
+test-crypto		   4	Cryptographic toolbox helpers
+test-textfile		   4	Old textfile storage format
+test-ringbuf		   3	Ring buffer functionality
+test-queue		   6	Queue handling functionality
+test-uhid		   6	Userspace HID functionality
+test-hfp		  29	HFP Audio Gateway functionality
+test-avdtp		  60	AVDTP qualification test cases
+test-avctp		   9	AVCTP qualification test cases
+test-avrcp		 110	AVRCP qualification test cases
+test-gobex		  31	Generic OBEX functionality
+test-gobex-packet	   9	OBEX packet handling
+test-gobex-header	  28	OBEX header handling
+test-gobex-apparam	  18	OBEX apparam handling
+test-gobex-transfer	  36	OBEX transfer handling
+test-gdbus-client	  13	D-Bus client handling
+test-gatt		 180	GATT qualification test cases
+test-hog		   6	HID Over GATT qualification test cases
+			-----
+			 761
+
+
+Automated end-to-end testing
+============================
+
+Application		Count	Description
+-------------------------------------------
+mgmt-tester		 305	Kernel management interface testing
+l2cap-tester		  33	Kernel L2CAP implementation testing
+rfcomm-tester		   9	Kernel RFCOMM implementation testing
+bnep-tester		   1	Kernel BNEP implementation testing
+smp-tester		   8	Kernel SMP implementation testing
+sco-tester		   8	Kernel SCO implementation testing
+gap-tester		   1	Daemon D-Bus API testing
+hci-tester		  14	Controller hardware testing
+userchan-tester		   3	Kernel HCI User Channel testting
+			-----
+			 382
+
+
+Android end-to-end testing
+==========================
+
+Application		Count	Description
+-------------------------------------------
+android-tester		 194	Android HAL interface testing
+ipc-tester		 132	Android IPC resistance testing
+			-----
+			 326
+
+
+Android automated unit testing
+==============================
+
+Application		Count	Description
+-------------------------------------------
+test-ipc		  14	Android IPC library functions
+			-----
+			  14
diff --git a/bluez/repo/doc/test-runner.txt b/bluez/repo/doc/test-runner.txt
new file mode 100644
index 0000000..72e17ce
--- /dev/null
+++ b/bluez/repo/doc/test-runner.txt
@@ -0,0 +1,54 @@
+Notes for test-runner usage
+***************************
+
+
+Kernel configuration
+====================
+
+The test-runner tool requires a kernel that is at least build with these
+minimal options for a successful boot.
+
+	CONFIG_VIRTIO=y
+	CONFIG_VIRTIO_PCI=y
+
+	CONFIG_NET=y
+	CONFIG_INET=y
+
+	CONFIG_NET_9P=y
+	CONFIG_NET_9P_VIRTIO=y
+
+	CONFIG_9P_FS=y
+	CONFIG_9P_FS_POSIX_ACL=y
+
+	CONFIG_SERIAL_8250=y
+	CONFIG_SERIAL_8250_CONSOLE=y
+	CONFIG_SERIAL_8250_PCI=y
+	CONFIG_SERIAL_8250_NR_UARTS=4
+
+	CONFIG_TMPFS=y
+	CONFIG_TMPFS_POSIX_ACL=y
+	CONFIG_TMPFS_XATTR=y
+
+	CONFIG_DEVTMPFS=y
+	CONFIG_DEBUG_FS=y
+
+These options should be installed as .config in the kernel source directory
+followed by this command.
+
+	make olddefconfig
+
+After that a default kernel with the required options can be built. More
+option (like the Bluetooth subsystem) can be enabled on top of this.
+
+Lock debuging
+-------------
+
+To catch locking related issues the following set of kernel config
+options may be useful:
+
+	CONFIG_LOCKDEP_SUPPORT=y
+	CONFIG_DEBUG_SPINLOCK=y
+	CONFIG_DEBUG_LOCK_ALLOC=y
+	CONFIG_PROVE_LOCKING=y
+	CONFIG_LOCKDEP=y
+	CONFIG_DEBUG_MUTEXES=y
diff --git a/bluez/repo/doc/thermometer-api.txt b/bluez/repo/doc/thermometer-api.txt
new file mode 100644
index 0000000..c7c8a5d
--- /dev/null
+++ b/bluez/repo/doc/thermometer-api.txt
@@ -0,0 +1,134 @@
+BlueZ D-Bus Thermometer API description
+***************************************
+
+	Santiago Carot-Nemesio <sancane@gmail.com>
+
+Health Thermometer Manager hierarchy
+====================================
+
+Service		org.bluez
+Interface	org.bluez.ThermometerManager1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		RegisterWatcher(object agent)
+
+			Registers a watcher to monitor scanned measurements.
+			This agent will be notified about final temperature
+			measurements.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		UnregisterWatcher(object agent)
+
+			Unregisters a watcher.
+
+		EnableIntermediateMeasurement(object agent)
+
+			Enables intermediate measurement notifications
+			for this agent. Intermediate measurements will
+			be enabled only for thermometers which support it.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+		DisableIntermediateMeasurement(object agent)
+
+			Disables intermediate measurement notifications
+			for this agent. It will disable notifications in
+			thermometers when the last agent removes the
+			watcher for intermediate measurements.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					org.bluez.Error.NotFound
+
+Health Thermometer Profile hierarchy
+====================================
+
+Service		org.bluez
+Interface	org.bluez.Thermometer1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+
+Properties	boolean Intermediate [readonly]
+
+			True if the thermometer supports intermediate
+			measurement notifications.
+
+		uint16 Interval (optional) [readwrite]
+
+			The Measurement Interval defines the time (in
+			seconds) between measurements. This interval is
+			not related to the intermediate measurements and
+			must be defined into a valid range. Setting it
+			to zero means that no periodic measurements will
+			be taken.
+
+		uint16 Maximum (optional) [readonly]
+
+			Defines the maximum value allowed for the interval
+			between periodic measurements.
+
+		uint16 Minimum (optional) [readonly]
+
+			Defines the minimum value allowed for the interval
+			between periodic measurements.
+
+
+Health Thermometer Watcher hierarchy
+====================================
+
+Service		unique name
+Interface	org.bluez.ThermometerWatcher1
+Object path	freely definable
+
+Methods		void MeasurementReceived(dict measurement)
+
+			This callback gets called when a measurement has been
+			scanned in the thermometer.
+
+			Measurement:
+
+				int16 Exponent:
+				int32 Mantissa:
+
+					Exponent and Mantissa values as
+					extracted from float value defined by
+					IEEE-11073-20601.
+
+					Measurement value is calculated as
+					(Mantissa) * (10^Exponent)
+
+					For special cases Exponent is
+					set to 0 and Mantissa is set to
+					one of following values:
+
+					+(2^23 - 1)	NaN (invalid or
+							missing data)
+					-(2^23)		NRes
+					+(2^23 - 2)	+Infinity
+					-(2^23 - 2)	-Infinity
+
+				string Unit:
+
+					Possible values: "celsius" or
+							"fahrenheit"
+
+				uint64 Time (optional):
+
+					Time of measurement, if
+					supported by device.
+					Expressed in seconds since epoch.
+
+				string Type (optional):
+
+					Only present if measurement type
+					is known.
+
+					Possible values: "armpit", "body",
+						"ear", "finger", "intestines",
+						"mouth", "rectum", "toe",
+						"tympanum"
+
+				string Measurement:
+
+					Possible values: "final" or
+							"intermediate"
diff --git a/bluez/repo/emulator/._amp.c b/bluez/repo/emulator/._amp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._amp.c
Binary files differ
diff --git a/bluez/repo/emulator/._amp.h b/bluez/repo/emulator/._amp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._amp.h
Binary files differ
diff --git a/bluez/repo/emulator/._b1ee.c b/bluez/repo/emulator/._b1ee.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._b1ee.c
Binary files differ
diff --git a/bluez/repo/emulator/._btdev.c b/bluez/repo/emulator/._btdev.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._btdev.c
Binary files differ
diff --git a/bluez/repo/emulator/._btdev.h b/bluez/repo/emulator/._btdev.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._btdev.h
Binary files differ
diff --git a/bluez/repo/emulator/._bthost.c b/bluez/repo/emulator/._bthost.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._bthost.c
Binary files differ
diff --git a/bluez/repo/emulator/._bthost.h b/bluez/repo/emulator/._bthost.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._bthost.h
Binary files differ
diff --git a/bluez/repo/emulator/._hciemu.c b/bluez/repo/emulator/._hciemu.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._hciemu.c
Binary files differ
diff --git a/bluez/repo/emulator/._hciemu.h b/bluez/repo/emulator/._hciemu.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._hciemu.h
Binary files differ
diff --git a/bluez/repo/emulator/._hfp.c b/bluez/repo/emulator/._hfp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._hfp.c
Binary files differ
diff --git a/bluez/repo/emulator/._le.c b/bluez/repo/emulator/._le.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._le.c
Binary files differ
diff --git a/bluez/repo/emulator/._le.h b/bluez/repo/emulator/._le.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._le.h
Binary files differ
diff --git a/bluez/repo/emulator/._main.c b/bluez/repo/emulator/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._main.c
Binary files differ
diff --git a/bluez/repo/emulator/._phy.c b/bluez/repo/emulator/._phy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._phy.c
Binary files differ
diff --git a/bluez/repo/emulator/._phy.h b/bluez/repo/emulator/._phy.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._phy.h
Binary files differ
diff --git a/bluez/repo/emulator/._serial.c b/bluez/repo/emulator/._serial.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._serial.c
Binary files differ
diff --git a/bluez/repo/emulator/._serial.h b/bluez/repo/emulator/._serial.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._serial.h
Binary files differ
diff --git a/bluez/repo/emulator/._server.c b/bluez/repo/emulator/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._server.c
Binary files differ
diff --git a/bluez/repo/emulator/._server.h b/bluez/repo/emulator/._server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._server.h
Binary files differ
diff --git a/bluez/repo/emulator/._smp.c b/bluez/repo/emulator/._smp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._smp.c
Binary files differ
diff --git a/bluez/repo/emulator/._vhci.c b/bluez/repo/emulator/._vhci.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._vhci.c
Binary files differ
diff --git a/bluez/repo/emulator/._vhci.h b/bluez/repo/emulator/._vhci.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/emulator/._vhci.h
Binary files differ
diff --git a/bluez/repo/emulator/amp.c b/bluez/repo/emulator/amp.c
new file mode 100644
index 0000000..c5c6b81
--- /dev/null
+++ b/bluez/repo/emulator/amp.c
@@ -0,0 +1,1052 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "src/shared/mainloop.h"
+#include "monitor/bt.h"
+
+#include "amp.h"
+
+#define PHY_MODE_IDLE		0x00
+#define PHY_MODE_INITIATOR	0x01
+#define PHY_MODE_ACCEPTOR	0x02
+
+#define MAX_ASSOC_LEN	672
+
+struct bt_amp {
+	volatile int ref_count;
+	int vhci_fd;
+
+	char phylink_path[32];
+	int phylink_fd;
+
+	uint8_t  event_mask[16];
+	uint16_t manufacturer;
+	uint8_t  commands[64];
+	uint8_t  features[8];
+
+	uint8_t  amp_status;
+	uint8_t  amp_type;
+	uint8_t  local_assoc[MAX_ASSOC_LEN];
+	uint16_t local_assoc_len;
+	uint8_t  remote_assoc[MAX_ASSOC_LEN];
+	uint16_t remote_assoc_len;
+
+	uint8_t  phy_mode;
+	uint8_t  phy_handle;
+	uint16_t logic_handle;
+};
+
+static void reset_defaults(struct bt_amp *amp)
+{
+	memset(amp->event_mask, 0, sizeof(amp->event_mask));
+	amp->event_mask[1] |= 0x20;	/* Command Complete */
+	amp->event_mask[1] |= 0x40;	/* Command Status */
+	amp->event_mask[1] |= 0x80;	/* Hardware Error */
+	amp->event_mask[2] |= 0x01;	/* Flush Occurred */
+	amp->event_mask[2] |= 0x04;	/* Number of Completed Packets */
+	amp->event_mask[3] |= 0x02;	/* Data Buffer Overflow */
+	amp->event_mask[3] |= 0x20;	/* QoS Violation */
+	amp->event_mask[7] |= 0x01;	/* Enhanced Flush Complete */
+
+	amp->event_mask[8] |= 0x01;	/* Physical Link Complete */
+	amp->event_mask[8] |= 0x02;	/* Channel Selected */
+	amp->event_mask[8] |= 0x04;	/* Disconnection Physical Link Complete */
+	amp->event_mask[8] |= 0x08;	/* Physical Link Loss Early Warning */
+	amp->event_mask[8] |= 0x10;	/* Physical Link Recovery */
+	amp->event_mask[8] |= 0x20;	/* Logical Link Complete */
+	amp->event_mask[8] |= 0x40;	/* Disconection Logical Link Complete */
+	amp->event_mask[8] |= 0x80;	/* Flow Specification Modify Complete */
+	amp->event_mask[9] |= 0x01;	/* Number of Completed Data Blocks */
+	amp->event_mask[9] |= 0x02;	/* AMP Start Test */
+	amp->event_mask[9] |= 0x04;	/* AMP Test End */
+	amp->event_mask[9] |= 0x08;	/* AMP Receiver Report */
+	amp->event_mask[9] |= 0x10;	/* Short Range Mode Change Complete */
+	amp->event_mask[9] |= 0x20;	/* AMP Status Change */
+
+	amp->manufacturer = 0x003f;	/* Bluetooth SIG (63) */
+
+	memset(amp->commands, 0, sizeof(amp->commands));
+	amp->commands[5]  |= 0x40;	/* Set Event Mask */
+	amp->commands[5]  |= 0x80;	/* Reset */
+	//amp->commands[6]  |= 0x01;	/* Set Event Filter */
+	//amp->commands[7]  |= 0x04;	/* Read Connection Accept Timeout */
+	//amp->commands[7]  |= 0x08;	/* Write Connection Accept Timeout */
+	//amp->commands[10] |= 0x80;	/* Host Number of Completed Packets */
+	//amp->commands[11] |= 0x01;	/* Read Link Supervision Timeout */
+	//amp->commands[11] |= 0x02;	/* Write Link Supervision Timeout */
+	amp->commands[14] |= 0x08;	/* Read Local Version Information */
+	amp->commands[14] |= 0x10;	/* Read Local Supported Commands */
+	amp->commands[14] |= 0x20;	/* Read Local Supported Features */
+	amp->commands[14] |= 0x80;	/* Read Buffer Size */
+	//amp->commands[15] |= 0x04;	/* Read Failed Contact Counter */
+	//amp->commands[15] |= 0x08;	/* Reset Failed Contact Counter */
+	//amp->commands[15] |= 0x10;	/* Read Link Quality */
+	//amp->commands[15] |= 0x20;	/* Read RSSI */
+	//amp->commands[16] |= 0x04;	/* Enable Device Under Test Mode */
+	//amp->commands[19] |= 0x40;	/* Enhanced Flush */
+
+	amp->commands[21] |= 0x01;	/* Create Physical Link */
+	amp->commands[21] |= 0x02;	/* Accept Physical Link */
+	amp->commands[21] |= 0x04;	/* Disconnect Phyiscal Link */
+	amp->commands[21] |= 0x08;	/* Create Logical Link */
+	amp->commands[21] |= 0x10;	/* Accept Logical Link */
+	amp->commands[21] |= 0x20;	/* Disconnect Logical Link */
+	amp->commands[21] |= 0x40;	/* Logical Link Cancel */
+	//amp->commands[21] |= 0x80;	/* Flow Specification Modify */
+	//amp->commands[22] |= 0x01;	/* Read Logical Link Accept Timeout */
+	//amp->commands[22] |= 0x02;	/* Write Logical Link Accept Timeout */
+	amp->commands[22] |= 0x04;	/* Set Event Mask Page 2 */
+	amp->commands[22] |= 0x08;	/* Read Location Data */
+	amp->commands[22] |= 0x10;	/* Write Location Data */
+	amp->commands[22] |= 0x20;	/* Read Local AMP Info */
+	amp->commands[22] |= 0x40;	/* Read Local AMP ASSOC */
+	amp->commands[22] |= 0x80;	/* Write Remote AMP ASSOC */
+	amp->commands[23] |= 0x01;	/* Read Flow Control Mode */
+	amp->commands[23] |= 0x02;	/* Write Flow Control Mode */
+	amp->commands[23] |= 0x04;	/* Read Data Block Size */
+	//amp->commands[23] |= 0x20;	/* Enable AMP Receiver Reports */
+	//amp->commands[23] |= 0x40;	/* AMP Test End */
+	//amp->commands[23] |= 0x80;	/* AMP Test */
+	//amp->commands[24] |= 0x04;	/* Read Best Effort Flush Timeout */
+	//amp->commands[24] |= 0x08;	/* Write Best Effort Flush Timeout */
+	//amp->commands[24] |= 0x10;	/* Short Range Mode */
+
+	memset(amp->features, 0, sizeof(amp->features));
+
+	amp->amp_status = 0x01;		/* Used for Bluetooth only */
+	amp->amp_type = 0x42;		/* Fake virtual AMP type */
+
+	memset(amp->local_assoc, 0, sizeof(amp->local_assoc));
+	amp->local_assoc_len = 0;
+
+	memset(amp->remote_assoc, 0, sizeof(amp->remote_assoc));
+	amp->remote_assoc_len = 0;
+
+	amp->phy_mode = PHY_MODE_IDLE;
+	amp->phy_handle = 0x00;		/* Invalid physical link handle */
+	amp->logic_handle = 0x0000;
+}
+
+static void send_packet(struct bt_amp *amp, const void *data, uint16_t len)
+{
+	if (write(amp->vhci_fd, data, len) < 0)
+		fprintf(stderr, "Write to /dev/vhci failed\n");
+}
+
+static void send_event(struct bt_amp *amp, uint8_t event,
+						const void *data, uint8_t len)
+{
+	struct bt_hci_evt_hdr *hdr;
+	uint16_t pkt_len;
+	void *pkt_data;
+
+	pkt_len = 1 + sizeof(*hdr) + len;
+
+	pkt_data = alloca(pkt_len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+	hdr = pkt_data + 1;
+	hdr->evt = event;
+	hdr->plen = len;
+
+	if (len > 0)
+		memcpy(pkt_data + 1 + sizeof(*hdr), data, len);
+
+	send_packet(amp, pkt_data, pkt_len);
+}
+
+static void cmd_complete(struct bt_amp *amp, uint16_t opcode,
+						const void *data, uint8_t len)
+{
+	struct bt_hci_evt_hdr *hdr;
+	struct bt_hci_evt_cmd_complete *cc;
+	uint16_t pkt_len;
+	void *pkt_data;
+
+	pkt_len = 1 + sizeof(*hdr) + sizeof(*cc) + len;
+
+	pkt_data = alloca(pkt_len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+	hdr = pkt_data + 1;
+	hdr->evt = BT_HCI_EVT_CMD_COMPLETE;
+	hdr->plen = sizeof(*cc) + len;
+
+	cc = pkt_data + 1 + sizeof(*hdr);
+	cc->ncmd = 0x01;
+	cc->opcode = cpu_to_le16(opcode);
+
+	if (len > 0)
+		memcpy(pkt_data + 1 + sizeof(*hdr) + sizeof(*cc), data, len);
+
+	send_packet(amp, pkt_data, pkt_len);
+}
+
+static void cmd_status(struct bt_amp *amp, uint8_t status, uint16_t opcode)
+{
+	struct bt_hci_evt_hdr *hdr;
+	struct bt_hci_evt_cmd_status *cs;
+	uint16_t pkt_len;
+	void *pkt_data;
+
+	pkt_len = 1 + sizeof(*hdr) + sizeof(*cs);
+
+	pkt_data = alloca(pkt_len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+	hdr = pkt_data + 1;
+	hdr->evt = BT_HCI_EVT_CMD_STATUS;
+	hdr->plen = sizeof(*cs);
+
+	cs = pkt_data + 1 + sizeof(*hdr);
+	cs->status = status;
+	cs->ncmd = 0x01;
+	cs->opcode = cpu_to_le16(opcode);
+
+	send_packet(amp, pkt_data, pkt_len);
+}
+
+static void cmd_set_event_mask(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_event_mask *cmd = data;
+	uint8_t status;
+
+	memcpy(amp->event_mask, cmd->mask, 8);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(amp, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status));
+}
+
+static void cmd_reset(struct bt_amp *amp, const void *data, uint8_t size)
+{
+	uint8_t status;
+
+	reset_defaults(amp);
+
+	amp->local_assoc[0] = 0x00;
+	amp->local_assoc_len = 1;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(amp, BT_HCI_CMD_RESET, &status, sizeof(status));
+}
+
+static void cmd_read_local_version(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_version rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.hci_ver = 0x05;
+	rsp.hci_rev = cpu_to_le16(0x0000);
+	rsp.lmp_ver = 0x01;
+	rsp.manufacturer = cpu_to_le16(amp->manufacturer);
+	rsp.lmp_subver = cpu_to_le16(0x0000);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_commands(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_commands rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.commands, amp->commands, 64);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_features(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_features rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.features, amp->features, 8);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_buffer_size(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_buffer_size rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.acl_mtu = cpu_to_le16(0x0000);
+	rsp.sco_mtu = 0x00;
+	rsp.acl_max_pkt = cpu_to_le16(0x0000);
+	rsp.sco_max_pkt = cpu_to_le16(0x0000);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp));
+}
+
+static void evt_phy_link_complete(struct bt_amp *amp)
+{
+	struct bt_hci_evt_phy_link_complete evt;
+
+	evt.status = BT_HCI_ERR_SUCCESS;
+	evt.phy_handle = amp->phy_handle;
+
+	send_event(amp, BT_HCI_EVT_PHY_LINK_COMPLETE, &evt, sizeof(evt));
+}
+
+static void evt_disconn_phy_link_complete(struct bt_amp *amp, uint8_t reason)
+{
+	struct bt_hci_evt_disconn_phy_link_complete evt;
+
+	evt.status = BT_HCI_ERR_SUCCESS;
+	evt.phy_handle = amp->phy_handle;
+	evt.reason = reason;
+
+	send_event(amp, BT_HCI_EVT_DISCONN_PHY_LINK_COMPLETE,
+						&evt, sizeof(evt));
+}
+
+static void link_callback(int fd, uint32_t events, void *user_data)
+{
+	struct bt_amp *amp = user_data;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		close(fd);
+		mainloop_remove_fd(fd);
+
+		evt_disconn_phy_link_complete(amp, 0x13);
+
+		amp->phy_mode = PHY_MODE_IDLE;
+		amp->phy_handle = 0x00;
+		return;
+	}
+}
+
+static void cmd_create_phy_link(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_create_phy_link *cmd = data;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_CREATE_PHY_LINK);
+		return;
+	}
+
+	if (amp->phy_mode != PHY_MODE_IDLE) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_CREATE_PHY_LINK);
+		return;
+	}
+
+	amp->phy_mode = PHY_MODE_INITIATOR;
+	amp->phy_handle = cmd->phy_handle;
+
+	cmd_status(amp, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_CREATE_PHY_LINK);
+}
+
+static void cmd_accept_phy_link(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_accept_phy_link *cmd = data;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_ACCEPT_PHY_LINK);
+		return;
+	}
+
+	if (amp->phy_mode != PHY_MODE_IDLE) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_ACCEPT_PHY_LINK);
+		return;
+	}
+
+	amp->phy_mode = PHY_MODE_ACCEPTOR;
+	amp->phy_handle = cmd->phy_handle;
+
+	cmd_status(amp, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_ACCEPT_PHY_LINK);
+}
+
+static void cmd_disconn_phy_link(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_disconn_phy_link *cmd = data;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_DISCONN_PHY_LINK);
+		return;
+	}
+
+	if (amp->phy_mode == PHY_MODE_IDLE) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_DISCONN_PHY_LINK);
+		return;
+	}
+
+	if (cmd->phy_handle != amp->phy_handle) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_DISCONN_PHY_LINK);
+		return;
+	}
+
+	cmd_status(amp, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_DISCONN_PHY_LINK);
+
+	mainloop_remove_fd(amp->phylink_fd);
+	close(amp->phylink_fd);
+
+	evt_disconn_phy_link_complete(amp, cmd->reason);
+
+	amp->phy_mode = PHY_MODE_IDLE;
+	amp->phy_handle = 0x00;
+}
+
+static void evt_logic_link_complete(struct bt_amp *amp)
+{
+	struct bt_hci_evt_logic_link_complete evt;
+
+	evt.status = BT_HCI_ERR_SUCCESS;
+	evt.handle = htobs(amp->logic_handle);
+	evt.phy_handle = amp->phy_handle;
+	evt.flow_spec = 0x00;
+
+	send_event(amp, BT_HCI_EVT_LOGIC_LINK_COMPLETE, &evt, sizeof(evt));
+}
+
+static void evt_disconn_logic_link_complete(struct bt_amp *amp, uint8_t reason)
+{
+	struct bt_hci_evt_disconn_logic_link_complete evt;
+
+	evt.status = BT_HCI_ERR_SUCCESS;
+	evt.handle = htobs(amp->logic_handle);
+	evt.reason = reason;
+
+	send_event(amp, BT_HCI_EVT_DISCONN_LOGIC_LINK_COMPLETE,
+						&evt, sizeof(evt));
+}
+
+static void cmd_create_logic_link(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_create_logic_link *cmd = data;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_CREATE_LOGIC_LINK);
+		return;
+	}
+
+	if (amp->phy_mode != PHY_MODE_IDLE) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_CREATE_LOGIC_LINK);
+		return;
+	}
+
+	if (amp->logic_handle != 0x00) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_CREATE_LOGIC_LINK);
+		return;
+	}
+
+	cmd_status(amp, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_CREATE_LOGIC_LINK);
+
+	amp->logic_handle = 0x0042;
+
+	evt_logic_link_complete(amp);
+}
+
+static void cmd_accept_logic_link(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_accept_logic_link *cmd = data;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_ACCEPT_LOGIC_LINK);
+		return;
+	}
+
+	if (amp->phy_mode != PHY_MODE_IDLE) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_ACCEPT_LOGIC_LINK);
+		return;
+	}
+
+	if (amp->logic_handle != 0x00) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_ACCEPT_LOGIC_LINK);
+		return;
+	}
+
+	cmd_status(amp, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_ACCEPT_LOGIC_LINK);
+
+	amp->logic_handle = 0x0023;
+
+	evt_logic_link_complete(amp);
+}
+
+static void cmd_disconn_logic_link(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_disconn_logic_link *cmd = data;
+
+	if (cmd->handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_DISCONN_LOGIC_LINK);
+		return;
+	}
+
+	if (cmd->handle != amp->logic_handle) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_DISCONN_LOGIC_LINK);
+		return;
+	}
+
+	cmd_status(amp, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_DISCONN_LOGIC_LINK);
+
+	evt_disconn_logic_link_complete(amp, 0x13);
+
+	amp->logic_handle = 0x0000;
+}
+
+static void cmd_logic_link_cancel(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_logic_link_cancel *cmd = data;
+	struct bt_hci_rsp_logic_link_cancel rsp;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LOGIC_LINK_CANCEL);
+		return;
+	}
+
+	if (amp->phy_mode != PHY_MODE_IDLE) {
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LOGIC_LINK_CANCEL);
+		return;
+	}
+
+	amp->logic_handle = 0x0000;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.phy_handle = amp->phy_handle;
+	rsp.flow_spec = 0x00;
+
+	cmd_complete(amp, BT_HCI_CMD_LOGIC_LINK_CANCEL, &rsp, sizeof(rsp));
+}
+
+static void cmd_set_event_mask_page2(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_event_mask_page2 *cmd = data;
+	uint8_t status;
+
+	memcpy(amp->event_mask + 8, cmd->mask, 8);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(amp, BT_HCI_CMD_SET_EVENT_MASK_PAGE2,
+						&status, sizeof(status));
+}
+
+static void cmd_read_location_data(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_location_data rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.domain_aware = 0x00;
+	rsp.domain[0] = 0x58;
+	rsp.domain[1] = 0x58;
+	rsp.domain_options = 0x58;
+	rsp.options = 0x00;
+
+	cmd_complete(amp, BT_HCI_CMD_READ_LOCATION_DATA, &rsp, sizeof(rsp));
+}
+
+static void cmd_write_location_data(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_location_data *cmd = data;
+	uint8_t status;
+
+	if (cmd->domain_aware > 0x01) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_WRITE_LOCATION_DATA);
+		return;
+	}
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(amp, BT_HCI_CMD_WRITE_LOCATION_DATA,
+						&status, sizeof(status));
+}
+
+static void cmd_read_flow_control_mode(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_flow_control_mode rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.mode = 0x01;
+
+	cmd_complete(amp, BT_HCI_CMD_READ_FLOW_CONTROL_MODE,
+						&rsp, sizeof(rsp));
+}
+
+static void cmd_write_flow_control_mode(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_flow_control_mode *cmd = data;
+	uint8_t status;
+
+	if (cmd->mode != 0x01) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_WRITE_FLOW_CONTROL_MODE);
+		return;
+	}
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(amp, BT_HCI_CMD_WRITE_FLOW_CONTROL_MODE,
+						&status, sizeof(status));
+}
+
+static void cmd_read_data_block_size(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_data_block_size rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.max_acl_len = cpu_to_le16(1492);
+	rsp.block_len = cpu_to_le16(1492);
+	rsp.num_blocks = cpu_to_le16(1);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_DATA_BLOCK_SIZE, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_amp_info(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_amp_info rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.amp_status = amp->amp_status;
+	rsp.total_bw = cpu_to_le32(24000);
+	rsp.max_bw = cpu_to_le32(24000);
+	rsp.min_latency = cpu_to_le32(100);
+	rsp.max_pdu = cpu_to_le32(1492);
+	rsp.amp_type = amp->amp_type;
+	rsp.pal_cap = cpu_to_le16(0x0001);
+	rsp.max_assoc_len = cpu_to_le16(MAX_ASSOC_LEN);
+	rsp.max_flush_to = cpu_to_le32(20000);
+	rsp.be_flush_to = cpu_to_le32(20000);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_LOCAL_AMP_INFO, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_amp_assoc(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_local_amp_assoc *cmd = data;
+	struct bt_hci_rsp_read_local_amp_assoc rsp;
+	uint16_t len_so_far, remain_assoc_len, fragment_len;
+
+	if (cmd->phy_handle != amp->phy_handle) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_READ_LOCAL_AMP_ASSOC);
+		return;
+	}
+
+	len_so_far = le16_to_cpu(cmd->len_so_far);
+	remain_assoc_len = amp->local_assoc_len - len_so_far;
+	fragment_len = remain_assoc_len > 248 ? 248 : remain_assoc_len;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.phy_handle = cmd->phy_handle;
+	rsp.remain_assoc_len = cpu_to_le16(remain_assoc_len);
+	memcpy(rsp.assoc_fragment, amp->local_assoc + len_so_far,
+							fragment_len);
+
+	cmd_complete(amp, BT_HCI_CMD_READ_LOCAL_AMP_ASSOC,
+						&rsp, 4 + fragment_len);
+}
+
+static int create_unix_server(const char *path)
+{
+	struct sockaddr_un addr;
+	int fd;
+
+	fd = socket(PF_UNIX, SOCK_SEQPACKET, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	addr.sun_path[0] = '\0';
+	strcpy(addr.sun_path + 1, path);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, 1) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int connect_unix_client(const char *path)
+{
+	struct sockaddr_un addr;
+	int fd;
+
+	fd = socket(PF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	addr.sun_path[0] = '\0';
+	strcpy(addr.sun_path + 1, path);
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static void accept_callback(int fd, uint32_t events, void *user_data)
+{
+	struct bt_amp *amp = user_data;
+	struct sockaddr_un addr;
+	socklen_t len;
+	int new_fd;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	len = sizeof(addr);
+
+	new_fd = accept4(fd, (struct sockaddr *) &addr, &len,
+						SOCK_CLOEXEC | SOCK_NONBLOCK);
+	if (new_fd < 0)
+		return;
+
+	mainloop_remove_fd(fd);
+	close(fd);
+
+	amp->phylink_fd = new_fd;
+
+	evt_phy_link_complete(amp);
+
+	mainloop_add_fd(new_fd, EPOLLIN, link_callback, amp, NULL);
+}
+
+static void connect_callback(int fd, uint32_t events, void *user_data)
+{
+	struct bt_amp *amp = user_data;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	mainloop_remove_fd(fd);
+
+	evt_phy_link_complete(amp);
+
+	mainloop_add_fd(fd, EPOLLIN, link_callback, amp, NULL);
+}
+
+static void cmd_write_remote_amp_assoc(struct bt_amp *amp,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_remote_amp_assoc *cmd = data;
+	struct bt_hci_rsp_write_remote_amp_assoc rsp;
+	int fd;
+
+	if (cmd->phy_handle == 0x00) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC);
+		return;
+	}
+
+	if (cmd->phy_handle != amp->phy_handle) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC);
+		return;
+	}
+
+	switch (amp->phy_mode) {
+	case PHY_MODE_INITIATOR:
+		strcpy(amp->phylink_path, "amp");
+
+		fd = create_unix_server(amp->phylink_path);
+		if (fd < 0) {
+			cmd_status(amp, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC);
+			return;
+		}
+
+		amp->local_assoc[0] = 0x01;
+		memcpy(amp->local_assoc + 1, amp->phylink_path,
+					strlen(amp->phylink_path) + 1);
+		amp->local_assoc_len = strlen(amp->phylink_path) + 2;
+
+		mainloop_add_fd(fd, EPOLLIN, accept_callback, amp, NULL);
+
+		amp->phylink_fd = fd;
+		break;
+
+	case PHY_MODE_ACCEPTOR:
+		if (cmd->assoc_fragment[0] != 0x01) {
+			cmd_status(amp, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC);
+			return;
+		}
+
+		memcpy(amp->phylink_path, cmd->assoc_fragment + 1,
+						cmd->remain_assoc_len - 1);
+
+		fd = connect_unix_client(amp->phylink_path);
+		if (fd < 0) {
+			cmd_status(amp, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC);
+			return;
+		}
+
+		mainloop_add_fd(fd, EPOLLOUT, connect_callback, amp, NULL);
+
+		amp->phylink_fd = fd;
+		break;
+
+	default:
+		cmd_status(amp, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC);
+		return;
+	}
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.phy_handle = amp->phy_handle;
+
+	cmd_complete(amp, BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC, &rsp, sizeof(rsp));
+
+	if (amp->phy_mode == PHY_MODE_INITIATOR) {
+		struct bt_hci_evt_channel_selected evt;
+
+		evt.phy_handle = amp->phy_handle;
+
+		send_event(amp, BT_HCI_EVT_CHANNEL_SELECTED, &evt, sizeof(evt));
+	}
+}
+
+static const struct {
+	uint16_t opcode;
+	void (*func) (struct bt_amp *amp, const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+} cmd_table[] = {
+	{ BT_HCI_CMD_SET_EVENT_MASK,       cmd_set_event_mask,      8, true },
+	{ BT_HCI_CMD_RESET,                cmd_reset,               0, true },
+	{ BT_HCI_CMD_READ_LOCAL_VERSION,   cmd_read_local_version,  0, true },
+	{ BT_HCI_CMD_READ_LOCAL_COMMANDS,  cmd_read_local_commands, 0, true },
+	{ BT_HCI_CMD_READ_LOCAL_FEATURES,  cmd_read_local_features, 0, true },
+	{ BT_HCI_CMD_READ_BUFFER_SIZE,     cmd_read_buffer_size,    0, true },
+
+	{ BT_HCI_CMD_CREATE_PHY_LINK,
+				cmd_create_phy_link, 3, false },
+	{ BT_HCI_CMD_ACCEPT_PHY_LINK,
+				cmd_accept_phy_link, 3, false },
+	{ BT_HCI_CMD_DISCONN_PHY_LINK,
+				cmd_disconn_phy_link, 2, true },
+	{ BT_HCI_CMD_CREATE_LOGIC_LINK,
+				cmd_create_logic_link, 33, true },
+	{ BT_HCI_CMD_ACCEPT_LOGIC_LINK,
+				cmd_accept_logic_link, 33, true },
+	{ BT_HCI_CMD_DISCONN_LOGIC_LINK,
+				cmd_disconn_logic_link, 2, true },
+	{ BT_HCI_CMD_LOGIC_LINK_CANCEL,
+				cmd_logic_link_cancel, 2, true },
+	{ BT_HCI_CMD_SET_EVENT_MASK_PAGE2,
+				cmd_set_event_mask_page2, 8, true },
+	{ BT_HCI_CMD_READ_LOCATION_DATA,
+				cmd_read_location_data, 0, true },
+	{ BT_HCI_CMD_WRITE_LOCATION_DATA,
+				cmd_write_location_data, 5, true },
+	{ BT_HCI_CMD_READ_FLOW_CONTROL_MODE,
+				cmd_read_flow_control_mode, 0, true },
+	{ BT_HCI_CMD_WRITE_FLOW_CONTROL_MODE,
+				cmd_write_flow_control_mode, 1, true },
+	{ BT_HCI_CMD_READ_DATA_BLOCK_SIZE,
+				cmd_read_data_block_size, 0, true },
+	{ BT_HCI_CMD_READ_LOCAL_AMP_INFO,
+				cmd_read_local_amp_info, 0, true },
+	{ BT_HCI_CMD_READ_LOCAL_AMP_ASSOC,
+				cmd_read_local_amp_assoc, 5, true },
+	{ BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC,
+				cmd_write_remote_amp_assoc, 6, false },
+	{ }
+};
+
+static void process_command(struct bt_amp *amp, const void *data, size_t size)
+{
+	const struct bt_hci_cmd_hdr *hdr = data;
+	uint16_t opcode;
+	unsigned int i;
+
+	if (size < sizeof(*hdr))
+		return;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	opcode = le16_to_cpu(hdr->opcode);
+
+	if (hdr->plen != size) {
+		cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS, opcode);
+		return;
+	}
+
+	for (i = 0; cmd_table[i].func; i++) {
+		if (cmd_table[i].opcode != opcode)
+			continue;
+
+		if ((cmd_table[i].fixed && size != cmd_table[i].size) ||
+						size < cmd_table[i].size) {
+			cmd_status(amp, BT_HCI_ERR_INVALID_PARAMETERS, opcode);
+			return;
+		}
+
+		cmd_table[i].func(amp, data, size);
+		return;
+	}
+
+	cmd_status(amp, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct bt_amp *amp = user_data;
+	unsigned char buf[4096];
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+	len = read(amp->vhci_fd, buf, sizeof(buf));
+	if (len < 1)
+		return;
+
+	switch (buf[0]) {
+	case BT_H4_CMD_PKT:
+		process_command(amp, buf + 1, len - 1);
+		break;
+	}
+}
+
+struct bt_amp *bt_amp_new(void)
+{
+	unsigned char setup_cmd[2];
+	struct bt_amp *amp;
+
+	amp = calloc(1, sizeof(*amp));
+	if (!amp)
+		return NULL;
+
+	reset_defaults(amp);
+
+	amp->vhci_fd = open("/dev/vhci", O_RDWR);
+	if (amp->vhci_fd < 0) {
+		free(amp);
+		return NULL;
+	}
+
+	setup_cmd[0] = HCI_VENDOR_PKT;
+	setup_cmd[1] = HCI_AMP;
+
+	if (write(amp->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) {
+		close(amp->vhci_fd);
+		free(amp);
+		return NULL;
+	}
+
+	mainloop_add_fd(amp->vhci_fd, EPOLLIN, vhci_read_callback, amp, NULL);
+
+	return bt_amp_ref(amp);
+}
+
+struct bt_amp *bt_amp_ref(struct bt_amp *amp)
+{
+	if (!amp)
+		return NULL;
+
+	__sync_fetch_and_add(&amp->ref_count, 1);
+
+	return amp;
+}
+
+void bt_amp_unref(struct bt_amp *amp)
+{
+	if (!amp)
+		return;
+
+	if (__sync_sub_and_fetch(&amp->ref_count, 1))
+		return;
+
+	mainloop_remove_fd(amp->vhci_fd);
+
+	close(amp->vhci_fd);
+
+	free(amp);
+}
diff --git a/bluez/repo/emulator/amp.h b/bluez/repo/emulator/amp.h
new file mode 100644
index 0000000..189dfb7
--- /dev/null
+++ b/bluez/repo/emulator/amp.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 bt_amp;
+
+struct bt_amp *bt_amp_new(void);
+
+struct bt_amp *bt_amp_ref(struct bt_amp *amp);
+void bt_amp_unref(struct bt_amp *amp);
diff --git a/bluez/repo/emulator/b1ee.c b/bluez/repo/emulator/b1ee.c
new file mode 100644
index 0000000..1fe4684
--- /dev/null
+++ b/bluez/repo/emulator/b1ee.c
@@ -0,0 +1,257 @@
+/*
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/mainloop.h"
+
+#define DEFAULT_SERVER		"b1ee.com"
+#define DEFAULT_HOST_PORT	"45550"		/* 0xb1ee */
+#define DEFAULT_SNIFFER_PORT	"45551"		/* 0xb1ef */
+
+static int sniffer_fd;
+static int server_fd;
+static int vhci_fd;
+
+static void sniffer_read_callback(int fd, uint32_t events, void *user_data)
+{
+	static uint8_t buf[4096];
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+again:
+	len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
+	if (len < 0) {
+		if (errno == EAGAIN)
+			goto again;
+		return;
+	}
+
+	printf("Sniffer received: %zi bytes\n", len);
+}
+
+static uint8_t *server_pkt_data;
+static uint8_t server_pkt_type;
+static uint16_t server_pkt_expect;
+static uint16_t server_pkt_len;
+static uint16_t server_pkt_offset;
+
+static void server_read_callback(int fd, uint32_t events, void *user_data)
+{
+	static uint8_t buf[4096];
+	uint8_t *ptr = buf;
+	ssize_t len;
+	uint16_t count;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+again:
+	len = recv(fd, buf + server_pkt_offset,
+			sizeof(buf) - server_pkt_offset, MSG_DONTWAIT);
+	if (len < 0) {
+		if (errno == EAGAIN)
+			goto again;
+		return;
+	}
+
+	count = server_pkt_offset + len;
+
+	while (count > 0) {
+		hci_event_hdr *evt_hdr;
+
+		if (!server_pkt_data) {
+			server_pkt_type = ptr[0];
+
+			switch (server_pkt_type) {
+			case HCI_EVENT_PKT:
+				if (count < HCI_EVENT_HDR_SIZE + 1) {
+					server_pkt_offset += len;
+					return;
+				}
+				evt_hdr = (hci_event_hdr *) (ptr + 1);
+				server_pkt_expect = HCI_EVENT_HDR_SIZE +
+							evt_hdr->plen + 1;
+				server_pkt_data = malloc(server_pkt_expect);
+				server_pkt_len = 0;
+				break;
+			default:
+				fprintf(stderr, "Unknown packet from server\n");
+				return;
+			}
+
+			server_pkt_offset = 0;
+		}
+
+		if (count >= server_pkt_expect) {
+			ssize_t written;
+
+			memcpy(server_pkt_data + server_pkt_len,
+						ptr, server_pkt_expect);
+			ptr += server_pkt_expect;
+			count -= server_pkt_expect;
+
+			written = write(vhci_fd, server_pkt_data,
+					server_pkt_len + server_pkt_expect);
+			if (written != server_pkt_len + server_pkt_expect)
+				fprintf(stderr, "Write to /dev/vhci failed\n");
+
+			free(server_pkt_data);
+			server_pkt_data = NULL;
+		} else {
+			memcpy(server_pkt_data + server_pkt_len, ptr, count);
+			server_pkt_len += count;
+			server_pkt_expect -= count;
+			count = 0;
+		}
+	}
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+	unsigned char buf[4096];
+	ssize_t len, written;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		return;
+
+	written = write(server_fd, buf, len);
+	if (written != len)
+		fprintf(stderr, "Write to server failed\n");
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static int do_connect(const char *node, const char *service)
+{
+	struct addrinfo hints;
+	struct addrinfo *info, *res;
+	int err, fd = -1;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+
+	err = getaddrinfo(DEFAULT_SERVER, DEFAULT_HOST_PORT, &hints, &res);
+	if (err) {
+		perror(gai_strerror(err));
+		exit(1);
+	}
+
+	for (info = res; info; info = info->ai_next) {
+		char str[INET6_ADDRSTRLEN];
+
+		inet_ntop(info->ai_family, info->ai_addr->sa_data,
+							str, sizeof(str));
+
+		fd = socket(info->ai_family, info->ai_socktype,
+						info->ai_protocol);
+		if (fd < 0)
+			continue;
+
+		printf("Trying to connect to %s on port %s\n", str, service);
+
+		if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
+			perror("Failed to connect");
+			close(fd);
+			continue;
+		}
+
+		printf("Successfully connected to %s on port %s\n",
+							str, service);
+		break;
+	}
+
+	freeaddrinfo(res);
+
+	if (res == NULL)
+		exit(1);
+
+	return fd;
+}
+
+int main(int argc, char *argv[])
+{
+	const char sniff_cmd[] = { 0x01, 0x00,
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	ssize_t written;
+	sigset_t mask;
+
+	server_fd = do_connect(DEFAULT_SERVER, DEFAULT_HOST_PORT);
+	sniffer_fd = do_connect(DEFAULT_SERVER, DEFAULT_SNIFFER_PORT);
+
+	written = write(sniffer_fd, sniff_cmd, sizeof(sniff_cmd));
+	if (written < 0)
+		perror("Failed to enable sniffer");
+
+	vhci_fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+	if (vhci_fd < 0) {
+		perror("Failed to /dev/vhci");
+		close(server_fd);
+		exit(1);
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	mainloop_add_fd(sniffer_fd, EPOLLIN, sniffer_read_callback, NULL, NULL);
+	mainloop_add_fd(server_fd, EPOLLIN, server_read_callback, NULL, NULL);
+	mainloop_add_fd(vhci_fd, EPOLLIN, vhci_read_callback, NULL, NULL);
+
+	return mainloop_run();
+}
diff --git a/bluez/repo/emulator/btdev.c b/bluez/repo/emulator/btdev.c
new file mode 100644
index 0000000..38769d8
--- /dev/null
+++ b/bluez/repo/emulator/btdev.c
@@ -0,0 +1,3648 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+#include <sys/uio.h>
+#include <stdint.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "src/shared/timeout.h"
+#include "src/shared/crypto.h"
+#include "src/shared/ecc.h"
+#include "monitor/bt.h"
+#include "btdev.h"
+
+#define has_bredr(btdev)	(!((btdev)->features[4] & 0x20))
+#define has_le(btdev)		(!!((btdev)->features[4] & 0x40))
+
+struct hook {
+	btdev_hook_func handler;
+	void *user_data;
+	enum btdev_hook_type type;
+	uint16_t opcode;
+};
+
+#define MAX_HOOK_ENTRIES 16
+
+struct btdev {
+	enum btdev_type type;
+
+	struct btdev *conn;
+
+	bool auth_init;
+	uint8_t link_key[16];
+	uint16_t pin[16];
+	uint8_t pin_len;
+	uint8_t io_cap;
+	uint8_t auth_req;
+	bool ssp_auth_complete;
+	uint8_t ssp_status;
+
+	btdev_command_func command_handler;
+	void *command_data;
+
+	btdev_send_func send_handler;
+	void *send_data;
+
+	unsigned int inquiry_id;
+	unsigned int inquiry_timeout_id;
+
+	struct hook *hook_list[MAX_HOOK_ENTRIES];
+
+	struct bt_crypto *crypto;
+
+        uint16_t manufacturer;
+        uint8_t  version;
+	uint16_t revision;
+	uint8_t  commands[64];
+	uint8_t  max_page;
+	uint8_t  features[8];
+	uint8_t  feat_page_2[8];
+	uint16_t acl_mtu;
+	uint16_t acl_max_pkt;
+	uint8_t  country_code;
+	uint8_t  bdaddr[6];
+	uint8_t  random_addr[6];
+	uint8_t  le_features[8];
+	uint8_t  le_states[8];
+
+	uint16_t default_link_policy;
+	uint8_t  event_mask[8];
+	uint8_t  event_mask_page2[8];
+	uint8_t  event_filter;
+	uint8_t  name[248];
+	uint8_t  dev_class[3];
+	uint16_t voice_setting;
+	uint16_t conn_accept_timeout;
+	uint16_t page_timeout;
+	uint8_t  scan_enable;
+	uint16_t page_scan_interval;
+	uint16_t page_scan_window;
+	uint16_t page_scan_type;
+	uint8_t  auth_enable;
+	uint16_t inquiry_scan_interval;
+	uint16_t inquiry_scan_window;
+	uint8_t  inquiry_mode;
+	uint8_t  afh_assessment_mode;
+	uint8_t  ext_inquiry_fec;
+	uint8_t  ext_inquiry_rsp[240];
+	uint8_t  simple_pairing_mode;
+	uint8_t  ssp_debug_mode;
+	uint8_t  secure_conn_support;
+	uint8_t  host_flow_control;
+	uint8_t  le_supported;
+	uint8_t  le_simultaneous;
+	uint8_t  le_event_mask[8];
+	uint8_t  le_adv_data[31];
+	uint8_t  le_adv_data_len;
+	uint8_t  le_adv_type;
+	uint8_t  le_adv_own_addr;
+	uint8_t  le_adv_direct_addr_type;
+	uint8_t  le_adv_direct_addr[6];
+	uint8_t  le_scan_data[31];
+	uint8_t  le_scan_data_len;
+	uint8_t  le_scan_enable;
+	uint8_t  le_scan_type;
+	uint8_t  le_scan_own_addr_type;
+	uint8_t  le_filter_dup;
+	uint8_t  le_adv_enable;
+	uint8_t  le_ltk[16];
+
+	uint8_t le_local_sk256[32];
+
+	uint16_t sync_train_interval;
+	uint32_t sync_train_timeout;
+	uint8_t  sync_train_service_data;
+};
+
+struct inquiry_data {
+	struct btdev *btdev;
+	int num_resp;
+
+	int sent_count;
+	int iter;
+};
+
+#define DEFAULT_INQUIRY_INTERVAL 100 /* 100 miliseconds */
+
+#define MAX_BTDEV_ENTRIES 16
+
+static const uint8_t LINK_KEY_NONE[16] = { 0 };
+static const uint8_t LINK_KEY_DUMMY[16] = {	0, 1, 2, 3, 4, 5, 6, 7,
+						8, 9, 0, 1, 2, 3, 4, 5 };
+
+static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { };
+
+static int get_hook_index(struct btdev *btdev, enum btdev_hook_type type,
+								uint16_t opcode)
+{
+	int i;
+
+	for (i = 0; i < MAX_HOOK_ENTRIES; i++) {
+		if (btdev->hook_list[i] == NULL)
+			continue;
+
+		if (btdev->hook_list[i]->type == type &&
+					btdev->hook_list[i]->opcode == opcode)
+			return i;
+	}
+
+	return -1;
+}
+
+static bool run_hooks(struct btdev *btdev, enum btdev_hook_type type,
+				uint16_t opcode, const void *data, uint16_t len)
+{
+	int index = get_hook_index(btdev, type, opcode);
+	if (index < 0)
+		return true;
+
+	return btdev->hook_list[index]->handler(data, len,
+					btdev->hook_list[index]->user_data);
+}
+
+static inline int add_btdev(struct btdev *btdev)
+{
+	int i, index = -1;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		if (btdev_list[i] == NULL) {
+			index = i;
+			btdev_list[index] = btdev;
+			break;
+		}
+	}
+
+	return index;
+}
+
+static inline int del_btdev(struct btdev *btdev)
+{
+	int i, index = -1;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		if (btdev_list[i] == btdev) {
+			index = i;
+			btdev_list[index] = NULL;
+			break;
+		}
+	}
+
+	return index;
+}
+
+static inline struct btdev *find_btdev_by_bdaddr(const uint8_t *bdaddr)
+{
+	int i;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		if (btdev_list[i] && !memcmp(btdev_list[i]->bdaddr, bdaddr, 6))
+			return btdev_list[i];
+	}
+
+	return NULL;
+}
+
+static inline struct btdev *find_btdev_by_bdaddr_type(const uint8_t *bdaddr,
+							uint8_t bdaddr_type)
+{
+	int i;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		int cmp;
+
+		if (!btdev_list[i])
+			continue;
+
+		if (bdaddr_type == 0x01)
+			cmp = memcmp(btdev_list[i]->random_addr, bdaddr, 6);
+		else
+			cmp = memcmp(btdev_list[i]->bdaddr, bdaddr, 6);
+
+		if (!cmp)
+			return btdev_list[i];
+	}
+
+	return NULL;
+}
+
+static void hexdump(const unsigned char *buf, uint16_t len)
+{
+	static const char hexdigits[] = "0123456789abcdef";
+	char str[68];
+	uint16_t i;
+
+	if (!len)
+		return;
+
+	for (i = 0; i < len; i++) {
+		str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+		str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+		str[((i % 16) * 3) + 2] = ' ';
+		str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+		if ((i + 1) % 16 == 0) {
+			str[47] = ' ';
+			str[48] = ' ';
+			str[65] = '\0';
+			printf("%-12c%s\n", ' ', str);
+			str[0] = ' ';
+		}
+	}
+
+	if (i % 16 > 0) {
+		uint16_t j;
+		for (j = (i % 16); j < 16; j++) {
+			str[(j * 3) + 0] = ' ';
+			str[(j * 3) + 1] = ' ';
+			str[(j * 3) + 2] = ' ';
+			str[j + 49] = ' ';
+		}
+		str[47] = ' ';
+		str[48] = ' ';
+		str[65] = '\0';
+		printf("%-12c%s\n", ' ', str);
+	}
+}
+
+static void get_bdaddr(uint16_t id, uint8_t index, uint8_t *bdaddr)
+{
+	bdaddr[0] = id & 0xff;
+	bdaddr[1] = id >> 8;
+	bdaddr[2] = index;
+	bdaddr[3] = 0x01;
+	bdaddr[4] = 0xaa;
+	bdaddr[5] = 0x00;
+}
+
+static void set_common_commands_all(struct btdev *btdev)
+{
+	btdev->commands[5]  |= 0x40;	/* Set Event Mask */
+	btdev->commands[5]  |= 0x80;	/* Reset */
+	btdev->commands[14] |= 0x08;	/* Read Local Version */
+	btdev->commands[14] |= 0x10;	/* Read Local Supported Commands */
+	btdev->commands[14] |= 0x20;	/* Read Local Supported Features */
+	btdev->commands[14] |= 0x80;	/* Read Buffer Size */
+}
+
+static void set_common_commands_bredrle(struct btdev *btdev)
+{
+	btdev->commands[0]  |= 0x20;	/* Disconnect */
+	btdev->commands[2]  |= 0x80;	/* Read Remote Version Information */
+	btdev->commands[10] |= 0x20;    /* Set Host Flow Control */
+	btdev->commands[10] |= 0x40;	/* Host Buffer Size */
+	btdev->commands[15] |= 0x02;	/* Read BD ADDR */
+}
+
+static void set_common_commands_bredr20(struct btdev *btdev)
+{
+	btdev->commands[0]  |= 0x01;	/* Inquiry */
+	btdev->commands[0]  |= 0x02;	/* Inquiry Cancel */
+	btdev->commands[0]  |= 0x10;	/* Create Connection */
+	btdev->commands[0]  |= 0x40;	/* Add SCO Connection */
+	btdev->commands[0]  |= 0x80;	/* Cancel Create Connection */
+	btdev->commands[1]  |= 0x01;	/* Accept Connection Request */
+	btdev->commands[1]  |= 0x02;	/* Reject Connection Request */
+	btdev->commands[1]  |= 0x04;	/* Link Key Request Reply */
+	btdev->commands[1]  |= 0x08;	/* Link Key Request Negative Reply */
+	btdev->commands[1]  |= 0x10;	/* PIN Code Request Reply */
+	btdev->commands[1]  |= 0x20;	/* PIN Code Request Negative Reply */
+	btdev->commands[1]  |= 0x80;	/* Authentication Requested */
+	btdev->commands[2]  |= 0x01;	/* Set Connection Encryption */
+	btdev->commands[2]  |= 0x08;	/* Remote Name Request */
+	btdev->commands[2]  |= 0x10;	/* Cancel Remote Name Request */
+	btdev->commands[2]  |= 0x20;	/* Read Remote Supported Features */
+	btdev->commands[2]  |= 0x40;	/* Read Remote Extended Features */
+	btdev->commands[3]  |= 0x01;	/* Read Clock Offset */
+	btdev->commands[5]  |= 0x08;	/* Read Default Link Policy */
+	btdev->commands[5]  |= 0x10;	/* Write Default Link Policy */
+	btdev->commands[6]  |= 0x01;	/* Set Event Filter */
+	btdev->commands[6]  |= 0x20;	/* Read Stored Link Key */
+	btdev->commands[6]  |= 0x40;	/* Write Stored Link Key */
+	btdev->commands[6]  |= 0x80;	/* Delete Stored Link Key */
+	btdev->commands[7]  |= 0x01;	/* Write Local Name */
+	btdev->commands[7]  |= 0x02;	/* Read Local Name */
+	btdev->commands[7]  |= 0x04;	/* Read Connection Accept Timeout */
+	btdev->commands[7]  |= 0x08;	/* Write Connection Accept Timeout */
+	btdev->commands[7]  |= 0x10;	/* Read Page Timeout */
+	btdev->commands[7]  |= 0x20;	/* Write Page Timeout */
+	btdev->commands[7]  |= 0x40;	/* Read Scan Enable */
+	btdev->commands[7]  |= 0x80;	/* Write Scan Enable */
+	btdev->commands[8]  |= 0x01;	/* Read Page Scan Activity */
+	btdev->commands[8]  |= 0x02;	/* Write Page Scan Activity */
+	btdev->commands[8]  |= 0x04;	/* Read Inquiry Scan Activity */
+	btdev->commands[8]  |= 0x08;	/* Write Inquiry Scan Activity */
+	btdev->commands[8]  |= 0x10;	/* Read Authentication Enable */
+	btdev->commands[8]  |= 0x20;	/* Write Authentication Enable */
+	btdev->commands[9]  |= 0x01;	/* Read Class Of Device */
+	btdev->commands[9]  |= 0x02;	/* Write Class Of Device */
+	btdev->commands[9]  |= 0x04;	/* Read Voice Setting */
+	btdev->commands[9]  |= 0x08;	/* Write Voice Setting */
+	btdev->commands[11] |= 0x10;	/* Write Current IAC LAP */
+	btdev->commands[12] |= 0x40;	/* Read Inquiry Mode */
+	btdev->commands[12] |= 0x80;	/* Write Inquiry Mode */
+	btdev->commands[13] |= 0x01;	/* Read Page Scan Type */
+	btdev->commands[13] |= 0x02;	/* Write Page Scan Type */
+	btdev->commands[13] |= 0x04;	/* Read AFH Assess Mode */
+	btdev->commands[13] |= 0x08;	/* Write AFH Assess Mode */
+	btdev->commands[14] |= 0x40;	/* Read Local Extended Features */
+	btdev->commands[15] |= 0x01;	/* Read Country Code */
+	btdev->commands[16] |= 0x04;	/* Enable Device Under Test Mode */
+}
+
+static void set_bredr_commands(struct btdev *btdev)
+{
+	set_common_commands_all(btdev);
+	set_common_commands_bredrle(btdev);
+	set_common_commands_bredr20(btdev);
+
+	btdev->commands[16] |= 0x08;	/* Setup Synchronous Connection */
+	btdev->commands[17] |= 0x01;	/* Read Extended Inquiry Response */
+	btdev->commands[17] |= 0x02;	/* Write Extended Inquiry Response */
+	btdev->commands[17] |= 0x20;	/* Read Simple Pairing Mode */
+	btdev->commands[17] |= 0x40;	/* Write Simple Pairing Mode */
+	btdev->commands[17] |= 0x80;	/* Read Local OOB Data */
+	btdev->commands[18] |= 0x01;	/* Read Inquiry Response TX Power */
+	btdev->commands[18] |= 0x02;	/* Write Inquiry Response TX Power */
+	btdev->commands[18] |= 0x80;	/* IO Capability Request Reply */
+	btdev->commands[20] |= 0x10;	/* Read Encryption Key Size */
+	btdev->commands[23] |= 0x04;	/* Read Data Block Size */
+	btdev->commands[29] |= 0x20;	/* Read Local Supported Codecs */
+	btdev->commands[30] |= 0x08;	/* Get MWS Transport Layer Config */
+}
+
+static void set_bredr20_commands(struct btdev *btdev)
+{
+	set_common_commands_all(btdev);
+	set_common_commands_bredrle(btdev);
+	set_common_commands_bredr20(btdev);
+}
+
+static void set_le_commands(struct btdev *btdev)
+{
+	set_common_commands_all(btdev);
+	set_common_commands_bredrle(btdev);
+
+	btdev->commands[24] |= 0x20;	/* Read LE Host Supported */
+	btdev->commands[24] |= 0x20;	/* Write LE Host Supported */
+	btdev->commands[25] |= 0x01;	/* LE Set Event Mask */
+	btdev->commands[25] |= 0x02;	/* LE Read Buffer Size */
+	btdev->commands[25] |= 0x04;	/* LE Read Local Features */
+	btdev->commands[25] |= 0x10;	/* LE Set Random Address */
+	btdev->commands[25] |= 0x20;	/* LE Set Adv Parameters */
+	btdev->commands[25] |= 0x40;	/* LE Read Adv TX Power */
+	btdev->commands[25] |= 0x80;	/* LE Set Adv Data */
+	btdev->commands[26] |= 0x01;	/* LE Set Scan Response Data */
+	btdev->commands[26] |= 0x02;	/* LE Set Adv Enable */
+	btdev->commands[26] |= 0x04;	/* LE Set Scan Parameters */
+	btdev->commands[26] |= 0x08;	/* LE Set Scan Enable */
+	btdev->commands[26] |= 0x10;	/* LE Create Connection */
+	btdev->commands[26] |= 0x40;	/* LE Read White List Size */
+	btdev->commands[26] |= 0x80;	/* LE Clear White List */
+	btdev->commands[27] |= 0x04;	/* LE Connection Update */
+	btdev->commands[27] |= 0x20;	/* LE Read Remote Used Features */
+	btdev->commands[27] |= 0x40;	/* LE Encrypt */
+	btdev->commands[27] |= 0x80;	/* LE Rand */
+	btdev->commands[28] |= 0x01;	/* LE Start Encryption */
+	btdev->commands[28] |= 0x02;	/* LE Long Term Key Request Reply */
+	btdev->commands[28] |= 0x04;	/* LE Long Term Key Request Neg Reply */
+	btdev->commands[28] |= 0x08;	/* LE Read Supported States */
+	btdev->commands[28] |= 0x10;	/* LE Receiver Test */
+	btdev->commands[28] |= 0x20;	/* LE Transmitter Test */
+	btdev->commands[28] |= 0x40;	/* LE Test End */
+
+	/* Extra LE commands for >= 4.1 adapters */
+	btdev->commands[33] |= 0x10;	/* LE Remote Conn Param Req Reply */
+	btdev->commands[33] |= 0x20;	/* LE Remote Conn Param Req Neg Reply */
+
+	/* Extra LE commands for >= 4.2 adapters */
+	btdev->commands[34] |= 0x02;	/* LE Read Local P-256 Public Key */
+	btdev->commands[34] |= 0x04;	/* LE Generate DHKey */
+}
+
+static void set_bredrle_commands(struct btdev *btdev)
+{
+	set_bredr_commands(btdev);
+	set_le_commands(btdev);
+
+	/* Extra BR/EDR commands we want to only support for >= 4.0
+	 * adapters.
+	 */
+	btdev->commands[22] |= 0x04;	/* Set Event Mask Page 2 */
+	btdev->commands[31] |= 0x80;	/* Read Sync Train Parameters */
+	btdev->commands[32] |= 0x04;	/* Read Secure Connections Support */
+	btdev->commands[32] |= 0x08;	/* Write Secure Connections Support */
+	btdev->commands[32] |= 0x10;	/* Read Auth Payload Timeout */
+	btdev->commands[32] |= 0x20;	/* Write Auth Payload Timeout */
+	btdev->commands[32] |= 0x40;	/* Read Local OOB Extended Data */
+}
+
+static void set_amp_commands(struct btdev *btdev)
+{
+	set_common_commands_all(btdev);
+
+	btdev->commands[22] |= 0x20;	/* Read Local AMP Info */
+}
+
+static void set_bredrle_features(struct btdev *btdev)
+{
+	btdev->features[0] |= 0x04;	/* Encryption */
+	btdev->features[0] |= 0x20;	/* Role switch */
+	btdev->features[0] |= 0x80;	/* Sniff mode */
+	btdev->features[1] |= 0x08;	/* SCO link */
+	btdev->features[2] |= 0x08;	/* Transparent SCO */
+	btdev->features[3] |= 0x40;	/* RSSI with inquiry results */
+	btdev->features[3] |= 0x80;	/* Extended SCO link */
+	btdev->features[4] |= 0x08;	/* AFH capable slave */
+	btdev->features[4] |= 0x10;	/* AFH classification slave */
+	btdev->features[4] |= 0x40;	/* LE Supported */
+	btdev->features[5] |= 0x02;	/* Sniff subrating */
+	btdev->features[5] |= 0x04;	/* Pause encryption */
+	btdev->features[5] |= 0x08;	/* AFH capable master */
+	btdev->features[5] |= 0x10;	/* AFH classification master */
+	btdev->features[6] |= 0x01;	/* Extended Inquiry Response */
+	btdev->features[6] |= 0x02;	/* Simultaneous LE and BR/EDR */
+	btdev->features[6] |= 0x08;	/* Secure Simple Pairing */
+	btdev->features[6] |= 0x10;	/* Encapsulated PDU */
+	btdev->features[6] |= 0x20;	/* Erroneous Data Reporting */
+	btdev->features[6] |= 0x40;	/* Non-flushable Packet Boundary Flag */
+	btdev->features[7] |= 0x01;	/* Link Supervision Timeout Event */
+	btdev->features[7] |= 0x02;	/* Inquiry TX Power Level */
+	btdev->features[7] |= 0x80;	/* Extended features */
+
+	btdev->feat_page_2[0] |= 0x01;	/* CSB - Master Operation */
+	btdev->feat_page_2[0] |= 0x02;	/* CSB - Slave Operation */
+	btdev->feat_page_2[0] |= 0x04;	/* Synchronization Train */
+	btdev->feat_page_2[0] |= 0x08;	/* Synchronization Scan */
+	btdev->feat_page_2[0] |= 0x10;	/* Inquiry Response Notification */
+	btdev->feat_page_2[1] |= 0x01;	/* Secure Connections */
+	btdev->feat_page_2[1] |= 0x02;	/* Ping */
+
+	btdev->max_page = 2;
+}
+
+static void set_bredr_features(struct btdev *btdev)
+{
+	btdev->features[0] |= 0x04;	/* Encryption */
+	btdev->features[0] |= 0x20;	/* Role switch */
+	btdev->features[0] |= 0x80;	/* Sniff mode */
+	btdev->features[1] |= 0x08;	/* SCO link */
+	btdev->features[3] |= 0x40;	/* RSSI with inquiry results */
+	btdev->features[3] |= 0x80;	/* Extended SCO link */
+	btdev->features[4] |= 0x08;	/* AFH capable slave */
+	btdev->features[4] |= 0x10;	/* AFH classification slave */
+	btdev->features[5] |= 0x02;	/* Sniff subrating */
+	btdev->features[5] |= 0x04;	/* Pause encryption */
+	btdev->features[5] |= 0x08;	/* AFH capable master */
+	btdev->features[5] |= 0x10;	/* AFH classification master */
+	btdev->features[6] |= 0x01;	/* Extended Inquiry Response */
+	btdev->features[6] |= 0x08;	/* Secure Simple Pairing */
+	btdev->features[6] |= 0x10;	/* Encapsulated PDU */
+	btdev->features[6] |= 0x20;	/* Erroneous Data Reporting */
+	btdev->features[6] |= 0x40;	/* Non-flushable Packet Boundary Flag */
+	btdev->features[7] |= 0x01;	/* Link Supervision Timeout Event */
+	btdev->features[7] |= 0x02;	/* Inquiry TX Power Level */
+	btdev->features[7] |= 0x80;	/* Extended features */
+
+	btdev->max_page = 1;
+}
+
+static void set_bredr20_features(struct btdev *btdev)
+{
+	btdev->features[0] |= 0x04;	/* Encryption */
+	btdev->features[0] |= 0x20;	/* Role switch */
+	btdev->features[0] |= 0x80;	/* Sniff mode */
+	btdev->features[1] |= 0x08;	/* SCO link */
+	btdev->features[3] |= 0x40;	/* RSSI with inquiry results */
+	btdev->features[3] |= 0x80;	/* Extended SCO link */
+	btdev->features[4] |= 0x08;	/* AFH capable slave */
+	btdev->features[4] |= 0x10;	/* AFH classification slave */
+	btdev->features[5] |= 0x02;	/* Sniff subrating */
+	btdev->features[5] |= 0x04;	/* Pause encryption */
+	btdev->features[5] |= 0x08;	/* AFH capable master */
+	btdev->features[5] |= 0x10;	/* AFH classification master */
+	btdev->features[7] |= 0x80;	/* Extended features */
+
+	btdev->max_page = 1;
+}
+
+static void set_le_features(struct btdev *btdev)
+{
+	btdev->features[4] |= 0x20;	/* BR/EDR Not Supported */
+	btdev->features[4] |= 0x40;	/* LE Supported */
+
+	btdev->max_page = 1;
+
+	btdev->le_features[0] |= 0x01;	/* LE Encryption */
+	btdev->le_features[0] |= 0x02;	/* Connection Parameters Request */
+	btdev->le_features[0] |= 0x08;	/* Slave-initiated Features Exchange */
+}
+
+static void set_amp_features(struct btdev *btdev)
+{
+}
+
+struct btdev *btdev_create(enum btdev_type type, uint16_t id)
+{
+	struct btdev *btdev;
+	int index;
+
+	btdev = malloc(sizeof(*btdev));
+	if (!btdev)
+		return NULL;
+
+	memset(btdev, 0, sizeof(*btdev));
+
+	if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE) {
+		btdev->crypto = bt_crypto_new();
+		if (!btdev->crypto) {
+			free(btdev);
+			return NULL;
+		}
+	}
+
+	btdev->type = type;
+
+	btdev->manufacturer = 63;
+	btdev->revision = 0x0000;
+
+	switch (btdev->type) {
+	case BTDEV_TYPE_BREDRLE:
+		btdev->version = 0x08;
+		set_bredrle_features(btdev);
+		set_bredrle_commands(btdev);
+		break;
+	case BTDEV_TYPE_BREDR:
+		btdev->version = 0x05;
+		set_bredr_features(btdev);
+		set_bredr_commands(btdev);
+		break;
+	case BTDEV_TYPE_LE:
+		btdev->version = 0x08;
+		set_le_features(btdev);
+		set_le_commands(btdev);
+		break;
+	case BTDEV_TYPE_AMP:
+		btdev->version = 0x01;
+		set_amp_features(btdev);
+		set_amp_commands(btdev);
+		break;
+	case BTDEV_TYPE_BREDR20:
+		btdev->version = 0x03;
+		set_bredr20_features(btdev);
+		set_bredr20_commands(btdev);
+		break;
+	}
+
+	btdev->page_scan_interval = 0x0800;
+	btdev->page_scan_window = 0x0012;
+	btdev->page_scan_type = 0x00;
+
+	btdev->sync_train_interval = 0x0080;
+	btdev->sync_train_timeout = 0x0002ee00;
+	btdev->sync_train_service_data = 0x00;
+
+	btdev->acl_mtu = 192;
+	btdev->acl_max_pkt = 1;
+
+	btdev->country_code = 0x00;
+
+	index = add_btdev(btdev);
+	if (index < 0) {
+		bt_crypto_unref(btdev->crypto);
+		free(btdev);
+		return NULL;
+	}
+
+	get_bdaddr(id, index, btdev->bdaddr);
+
+	return btdev;
+}
+
+void btdev_destroy(struct btdev *btdev)
+{
+	if (!btdev)
+		return;
+
+	if (btdev->inquiry_id > 0)
+		timeout_remove(btdev->inquiry_id);
+
+	bt_crypto_unref(btdev->crypto);
+	del_btdev(btdev);
+
+	free(btdev);
+}
+
+const uint8_t *btdev_get_bdaddr(struct btdev *btdev)
+{
+	return btdev->bdaddr;
+}
+
+uint8_t *btdev_get_features(struct btdev *btdev)
+{
+	return btdev->features;
+}
+
+uint8_t btdev_get_scan_enable(struct btdev *btdev)
+{
+	return btdev->scan_enable;
+}
+
+uint8_t btdev_get_le_scan_enable(struct btdev *btdev)
+{
+	return btdev->le_scan_enable;
+}
+
+static bool use_ssp(struct btdev *btdev1, struct btdev *btdev2)
+{
+	if (btdev1->auth_enable || btdev2->auth_enable)
+		return false;
+
+	return (btdev1->simple_pairing_mode && btdev2->simple_pairing_mode);
+}
+
+void btdev_set_command_handler(struct btdev *btdev, btdev_command_func handler,
+							void *user_data)
+{
+	if (!btdev)
+		return;
+
+	btdev->command_handler = handler;
+	btdev->command_data = user_data;
+}
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+							void *user_data)
+{
+	if (!btdev)
+		return;
+
+	btdev->send_handler = handler;
+	btdev->send_data = user_data;
+}
+
+static void send_packet(struct btdev *btdev, const struct iovec *iov,
+								int iovlen)
+{
+	if (!btdev->send_handler)
+		return;
+
+	btdev->send_handler(iov, iovlen, btdev->send_data);
+}
+
+static void send_event(struct btdev *btdev, uint8_t event,
+						const void *data, uint8_t len)
+{
+	struct bt_hci_evt_hdr hdr;
+	struct iovec iov[3];
+	uint8_t pkt = BT_H4_EVT_PKT;
+
+	iov[0].iov_base = &pkt;
+	iov[0].iov_len = sizeof(pkt);
+
+	hdr.evt = event;
+	hdr.plen = len;
+
+	iov[1].iov_base = &hdr;
+	iov[1].iov_len = sizeof(hdr);
+
+	if (len > 0) {
+		iov[2].iov_base = (void *) data;
+		iov[2].iov_len = len;
+	}
+
+	if (run_hooks(btdev, BTDEV_HOOK_POST_EVT, event, data, len))
+		send_packet(btdev, iov, len > 0 ? 3 : 2);
+}
+
+static void send_cmd(struct btdev *btdev, uint8_t evt, uint16_t opcode,
+					const struct iovec *iov, int iovlen)
+{
+	struct bt_hci_evt_hdr hdr;
+	struct iovec iov2[2 + iovlen];
+	uint8_t pkt = BT_H4_EVT_PKT;
+	int i;
+
+	iov2[0].iov_base = &pkt;
+	iov2[0].iov_len = sizeof(pkt);
+
+	hdr.evt = evt;
+	hdr.plen = 0;
+
+	iov2[1].iov_base = &hdr;
+	iov2[1].iov_len = sizeof(hdr);
+
+	for (i = 0; i < iovlen; i++) {
+		hdr.plen += iov[i].iov_len;
+		iov2[2 + i].iov_base = iov[i].iov_base;
+		iov2[2 + i].iov_len = iov[i].iov_len;
+	}
+
+	if (run_hooks(btdev, BTDEV_HOOK_POST_CMD, opcode, iov[i -1].iov_base,
+							iov[i -1].iov_len))
+		send_packet(btdev, iov2, 2 + iovlen);
+}
+
+static void cmd_complete(struct btdev *btdev, uint16_t opcode,
+						const void *data, uint8_t len)
+{
+	struct bt_hci_evt_cmd_complete cc;
+	struct iovec iov[2];
+
+	cc.ncmd = 0x01;
+	cc.opcode = cpu_to_le16(opcode);
+
+	iov[0].iov_base = &cc;
+	iov[0].iov_len = sizeof(cc);
+
+	iov[1].iov_base = (void *) data;
+	iov[1].iov_len = len;
+
+	send_cmd(btdev, BT_HCI_EVT_CMD_COMPLETE, opcode, iov, 2);
+}
+
+static void cmd_status(struct btdev *btdev, uint8_t status, uint16_t opcode)
+{
+	struct bt_hci_evt_cmd_status cs;
+	struct iovec iov;
+
+	cs.status = status;
+	cs.ncmd = 0x01;
+	cs.opcode = cpu_to_le16(opcode);
+
+	iov.iov_base = &cs;
+	iov.iov_len = sizeof(cs);
+
+	send_cmd(btdev, BT_HCI_EVT_CMD_STATUS, opcode, &iov, 1);
+}
+
+static void le_meta_event(struct btdev *btdev, uint8_t event,
+						void *data, uint8_t len)
+{
+	void *pkt_data;
+
+	pkt_data = alloca(1 + len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = event;
+
+	if (len > 0)
+		memcpy(pkt_data + 1, data, len);
+
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, pkt_data, 1 + len);
+}
+
+static void num_completed_packets(struct btdev *btdev)
+{
+	if (btdev->conn) {
+		struct bt_hci_evt_num_completed_packets ncp;
+
+		ncp.num_handles = 1;
+		ncp.handle = cpu_to_le16(42);
+		ncp.count = cpu_to_le16(1);
+
+		send_event(btdev, BT_HCI_EVT_NUM_COMPLETED_PACKETS,
+							&ncp, sizeof(ncp));
+	}
+}
+
+static bool inquiry_callback(void *user_data)
+{
+	struct inquiry_data *data = user_data;
+	struct btdev *btdev = data->btdev;
+	struct bt_hci_evt_inquiry_complete ic;
+	int sent = data->sent_count;
+	int i;
+
+	/*Report devices only once and wait for inquiry timeout*/
+	if (data->iter == MAX_BTDEV_ENTRIES)
+		return true;
+
+	for (i = data->iter; i < MAX_BTDEV_ENTRIES; i++) {
+		/*Lets sent 10 inquiry results at once */
+		if (sent + 10 == data->sent_count)
+			break;
+
+		if (!btdev_list[i] || btdev_list[i] == btdev)
+			continue;
+
+		if (!(btdev_list[i]->scan_enable & 0x02))
+			continue;
+
+		if (btdev->inquiry_mode == 0x02 &&
+					btdev_list[i]->ext_inquiry_rsp[0]) {
+			struct bt_hci_evt_ext_inquiry_result ir;
+
+			ir.num_resp = 0x01;
+			memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+			ir.pscan_rep_mode = 0x00;
+			ir.pscan_period_mode = 0x00;
+			memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+			ir.clock_offset = 0x0000;
+			ir.rssi = -60;
+			memcpy(ir.data, btdev_list[i]->ext_inquiry_rsp, 240);
+
+			send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
+							&ir, sizeof(ir));
+			data->sent_count++;
+			continue;
+		}
+
+		if (btdev->inquiry_mode > 0x00) {
+			struct bt_hci_evt_inquiry_result_with_rssi ir;
+
+			ir.num_resp = 0x01;
+			memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+			ir.pscan_rep_mode = 0x00;
+			ir.pscan_period_mode = 0x00;
+			memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+			ir.clock_offset = 0x0000;
+			ir.rssi = -60;
+
+			send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
+							&ir, sizeof(ir));
+			data->sent_count++;
+		} else {
+			struct bt_hci_evt_inquiry_result ir;
+
+			ir.num_resp = 0x01;
+			memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+			ir.pscan_rep_mode = 0x00;
+			ir.pscan_period_mode = 0x00;
+			ir.pscan_mode = 0x00;
+			memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+			ir.clock_offset = 0x0000;
+
+			send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT,
+							&ir, sizeof(ir));
+			data->sent_count++;
+		}
+	}
+	data->iter = i;
+
+	/* Check if we sent already required amount of responses*/
+	if (data->num_resp && data->sent_count == data->num_resp)
+		goto finish;
+
+	return true;
+
+finish:
+	/* Note that destroy will be called */
+	ic.status = BT_HCI_ERR_SUCCESS;
+	send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+
+	return false;
+}
+
+static void inquiry_destroy(void *user_data)
+{
+	struct inquiry_data *data = user_data;
+	struct btdev *btdev = data->btdev;
+
+	if (!btdev)
+		goto finish;
+
+	btdev->inquiry_id = 0;
+
+	if (btdev->inquiry_timeout_id > 0) {
+		timeout_remove(btdev->inquiry_timeout_id);
+		btdev->inquiry_timeout_id = 0;
+	}
+
+finish:
+	free(data);
+}
+
+static bool inquiry_timeout(void *user_data)
+{
+	struct inquiry_data *data = user_data;
+	struct btdev *btdev = data->btdev;
+	struct bt_hci_evt_inquiry_complete ic;
+
+	timeout_remove(btdev->inquiry_id);
+	btdev->inquiry_timeout_id = 0;
+
+	/* Inquiry is stopped, send Inquiry complete event. */
+	ic.status = BT_HCI_ERR_SUCCESS;
+	send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+
+	return false;
+}
+
+static void inquiry_cmd(struct btdev *btdev, const void *cmd)
+{
+	const struct bt_hci_cmd_inquiry *inq_cmd = cmd;
+	struct inquiry_data *data;
+	struct bt_hci_evt_inquiry_complete ic;
+	int status = BT_HCI_ERR_HARDWARE_FAILURE;
+	unsigned int inquiry_len_ms;
+
+	if (btdev->inquiry_id > 0) {
+		status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		goto failed;
+	}
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		goto failed;
+
+	memset(data, 0, sizeof(*data));
+	data->btdev = btdev;
+	data->num_resp = inq_cmd->num_resp;
+
+	/* Add timeout to cancel inquiry */
+	inquiry_len_ms = 1280 * inq_cmd->length;
+	if (inquiry_len_ms)
+		btdev->inquiry_timeout_id = timeout_add(inquiry_len_ms,
+							inquiry_timeout,
+							data, NULL);
+
+	btdev->inquiry_id = timeout_add(DEFAULT_INQUIRY_INTERVAL,
+							inquiry_callback, data,
+							inquiry_destroy);
+	/* Return if success */
+	if (btdev->inquiry_id > 0)
+		return;
+
+failed:
+	ic.status = status;
+	send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+}
+
+static void inquiry_cancel(struct btdev *btdev)
+{
+	uint8_t status;
+
+	if (!btdev->inquiry_id) {
+		status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		cmd_complete(btdev, BT_HCI_CMD_INQUIRY_CANCEL, &status,
+							sizeof(status));
+		return;
+	}
+
+	timeout_remove(btdev->inquiry_timeout_id);
+	btdev->inquiry_timeout_id = 0;
+	timeout_remove(btdev->inquiry_id);
+	btdev->inquiry_id = 0;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(btdev, BT_HCI_CMD_INQUIRY_CANCEL, &status,
+							sizeof(status));
+}
+
+static void conn_complete(struct btdev *btdev,
+					const uint8_t *bdaddr, uint8_t status)
+{
+	struct bt_hci_evt_conn_complete cc;
+
+	if (!status) {
+		struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+		btdev->conn = remote;
+		remote->conn = btdev;
+
+		cc.status = status;
+		memcpy(cc.bdaddr, btdev->bdaddr, 6);
+		cc.encr_mode = 0x00;
+
+		cc.handle = cpu_to_le16(42);
+		cc.link_type = 0x01;
+
+		send_event(remote, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+
+		cc.handle = cpu_to_le16(42);
+		cc.link_type = 0x01;
+	} else {
+		cc.handle = cpu_to_le16(0x0000);
+		cc.link_type = 0x01;
+	}
+
+	cc.status = status;
+	memcpy(cc.bdaddr, bdaddr, 6);
+	cc.encr_mode = 0x00;
+
+	send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+}
+
+static void accept_conn_request_complete(struct btdev *btdev,
+							const uint8_t *bdaddr)
+{
+	struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+	if (!remote)
+		return;
+
+	if (btdev->auth_enable || remote->auth_enable)
+		send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST,
+							btdev->bdaddr, 6);
+	else
+		conn_complete(btdev, bdaddr, BT_HCI_ERR_SUCCESS);
+}
+
+static void sync_conn_complete(struct btdev *btdev, uint16_t voice_setting,
+								uint8_t status)
+{
+	struct bt_hci_evt_sync_conn_complete cc;
+
+	if (!btdev->conn)
+		return;
+
+	cc.status = status;
+	memcpy(cc.bdaddr, btdev->conn->bdaddr, 6);
+
+	cc.handle = cpu_to_le16(status == BT_HCI_ERR_SUCCESS ? 257 : 0);
+	cc.link_type = 0x02;
+	cc.tx_interval = 0x000c;
+	cc.retrans_window = 0x06;
+	cc.rx_pkt_len = 60;
+	cc.tx_pkt_len = 60;
+	cc.air_mode = (voice_setting == 0x0060) ? 0x02 : 0x03;
+
+	send_event(btdev, BT_HCI_EVT_SYNC_CONN_COMPLETE, &cc, sizeof(cc));
+}
+
+static void sco_conn_complete(struct btdev *btdev, uint8_t status)
+{
+	struct bt_hci_evt_conn_complete cc;
+
+	if (!btdev->conn)
+		return;
+
+	cc.status = status;
+	memcpy(cc.bdaddr, btdev->conn->bdaddr, 6);
+	cc.handle = cpu_to_le16(status == BT_HCI_ERR_SUCCESS ? 257 : 0);
+	cc.link_type = 0x00;
+	cc.encr_mode = 0x00;
+
+	send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+}
+
+static void le_conn_complete(struct btdev *btdev,
+				const struct bt_hci_cmd_le_create_conn *lecc,
+				uint8_t status)
+{
+	char buf[1 + sizeof(struct bt_hci_evt_le_conn_complete)];
+	struct bt_hci_evt_le_conn_complete *cc = (void *) &buf[1];
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = BT_HCI_EVT_LE_CONN_COMPLETE;
+
+	if (!status) {
+		struct btdev *remote;
+
+		remote = find_btdev_by_bdaddr_type(lecc->peer_addr,
+							lecc->peer_addr_type);
+
+		btdev->conn = remote;
+		btdev->le_adv_enable = 0;
+		remote->conn = btdev;
+		remote->le_adv_enable = 0;
+
+		cc->status = status;
+		cc->peer_addr_type = btdev->le_scan_own_addr_type;
+		if (cc->peer_addr_type == 0x01)
+			memcpy(cc->peer_addr, btdev->random_addr, 6);
+		else
+			memcpy(cc->peer_addr, btdev->bdaddr, 6);
+
+		cc->role = 0x01;
+		cc->handle = cpu_to_le16(42);
+		cc->interval = lecc->max_interval;
+		cc->latency = lecc->latency;
+		cc->supv_timeout = lecc->supv_timeout;
+
+		send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+	}
+
+	cc->status = status;
+	cc->peer_addr_type = lecc->peer_addr_type;
+	memcpy(cc->peer_addr, lecc->peer_addr, 6);
+	cc->role = 0x00;
+
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+}
+
+static const uint8_t *scan_addr(const struct btdev *btdev)
+{
+	if (btdev->le_scan_own_addr_type == 0x01)
+		return btdev->random_addr;
+
+	return btdev->bdaddr;
+}
+
+static const uint8_t *adv_addr(const struct btdev *btdev)
+{
+	if (btdev->le_adv_own_addr == 0x01)
+		return btdev->random_addr;
+
+	return btdev->bdaddr;
+}
+
+static bool adv_match(struct btdev *scan, struct btdev *adv)
+{
+	/* Match everything if this is not directed advertising */
+	if (adv->le_adv_type != 0x01 && adv->le_adv_type != 0x04)
+		return true;
+
+	if (scan->le_scan_own_addr_type != adv->le_adv_direct_addr_type)
+		return false;
+
+	return !memcmp(scan_addr(scan), adv->le_adv_direct_addr, 6);
+}
+
+static bool adv_connectable(struct btdev *btdev)
+{
+	if (!btdev->le_adv_enable)
+		return false;
+
+	return btdev->le_adv_type != 0x03;
+}
+
+static void le_conn_request(struct btdev *btdev,
+				const struct bt_hci_cmd_le_create_conn *lecc)
+{
+	struct btdev *remote = find_btdev_by_bdaddr_type(lecc->peer_addr,
+							lecc->peer_addr_type);
+
+	if (remote && adv_connectable(remote) && adv_match(btdev, remote) &&
+				remote->le_adv_own_addr == lecc->peer_addr_type)
+		le_conn_complete(btdev, lecc, 0);
+	else
+		le_conn_complete(btdev, lecc,
+					BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH);
+}
+
+static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
+{
+	struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+	if (remote && remote->scan_enable & 0x02) {
+		struct bt_hci_evt_conn_request cr;
+
+		memcpy(cr.bdaddr, btdev->bdaddr, 6);
+		memcpy(cr.dev_class, btdev->dev_class, 3);
+		cr.link_type = 0x01;
+
+		send_event(remote, BT_HCI_EVT_CONN_REQUEST, &cr, sizeof(cr));
+	} else {
+		conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
+	}
+}
+
+static void rej_le_conn_update(struct btdev *btdev, uint16_t handle,
+								uint8_t reason)
+{
+	struct btdev *remote = btdev->conn;
+	struct __packed {
+		uint8_t subevent;
+		struct bt_hci_evt_le_conn_update_complete ev;
+	} ev;
+
+	if (!remote)
+		return;
+
+	ev.subevent = BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE;
+	ev.ev.handle = cpu_to_le16(handle);
+	ev.ev.status = cpu_to_le16(reason);
+
+	send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
+static void le_conn_update(struct btdev *btdev, uint16_t handle,
+				uint16_t min_interval, uint16_t max_interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t min_length, uint16_t max_length)
+{
+	struct btdev *remote = btdev->conn;
+	struct __packed {
+		uint8_t subevent;
+		struct bt_hci_evt_le_conn_update_complete ev;
+	} ev;
+
+	ev.subevent = BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE;
+	ev.ev.handle = cpu_to_le16(handle);
+	ev.ev.interval = cpu_to_le16(min_interval);
+	ev.ev.latency = cpu_to_le16(latency);
+	ev.ev.supv_timeout = cpu_to_le16(supv_timeout);
+
+	if (remote)
+		ev.ev.status = BT_HCI_ERR_SUCCESS;
+	else
+		ev.ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+
+	if (remote)
+		send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
+static void le_conn_param_req(struct btdev *btdev, uint16_t handle,
+				uint16_t min_interval, uint16_t max_interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t min_length, uint16_t max_length)
+{
+	struct btdev *remote = btdev->conn;
+	struct __packed {
+		uint8_t subevent;
+		struct bt_hci_evt_le_conn_param_request ev;
+	} ev;
+
+	if (!remote)
+		return;
+
+	ev.subevent = BT_HCI_EVT_LE_CONN_PARAM_REQUEST;
+	ev.ev.handle = cpu_to_le16(handle);
+	ev.ev.min_interval = cpu_to_le16(min_interval);
+	ev.ev.max_interval = cpu_to_le16(max_interval);
+	ev.ev.latency = cpu_to_le16(latency);
+	ev.ev.supv_timeout = cpu_to_le16(supv_timeout);
+
+	send_event(remote, BT_HCI_EVT_LE_META_EVENT, &ev, sizeof(ev));
+}
+
+static void disconnect_complete(struct btdev *btdev, uint16_t handle,
+							uint8_t reason)
+{
+	struct bt_hci_evt_disconnect_complete dc;
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		dc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		dc.handle = cpu_to_le16(handle);
+		dc.reason = 0x00;
+
+		send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE,
+							&dc, sizeof(dc));
+		return;
+	}
+
+	dc.status = BT_HCI_ERR_SUCCESS;
+	dc.handle = cpu_to_le16(handle);
+	dc.reason = reason;
+
+	btdev->conn = NULL;
+	remote->conn = NULL;
+
+	send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+	send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+}
+
+static void link_key_req_reply_complete(struct btdev *btdev,
+					const uint8_t *bdaddr,
+					const uint8_t *link_key)
+{
+	struct btdev *remote = btdev->conn;
+	struct bt_hci_evt_auth_complete ev;
+
+	memcpy(btdev->link_key, link_key, 16);
+
+	if (!remote) {
+		remote = find_btdev_by_bdaddr(bdaddr);
+		if (!remote)
+			return;
+	}
+
+	if (!memcmp(remote->link_key, LINK_KEY_NONE, 16)) {
+		send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST,
+							btdev->bdaddr, 6);
+		return;
+	}
+
+	ev.handle = cpu_to_le16(42);
+
+	if (!memcmp(btdev->link_key, remote->link_key, 16))
+		ev.status = BT_HCI_ERR_SUCCESS;
+	else
+		ev.status = BT_HCI_ERR_AUTH_FAILURE;
+
+	send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+	send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+}
+
+static void link_key_req_neg_reply_complete(struct btdev *btdev,
+							const uint8_t *bdaddr)
+{
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		remote = find_btdev_by_bdaddr(bdaddr);
+		if (!remote)
+			return;
+	}
+
+	if (use_ssp(btdev, remote)) {
+		struct bt_hci_evt_io_capability_request io_req;
+
+		memcpy(io_req.bdaddr, bdaddr, 6);
+		send_event(btdev, BT_HCI_EVT_IO_CAPABILITY_REQUEST, &io_req,
+							sizeof(io_req));
+	} else {
+		struct bt_hci_evt_pin_code_request pin_req;
+
+		memcpy(pin_req.bdaddr, bdaddr, 6);
+		send_event(btdev, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req,
+							sizeof(pin_req));
+	}
+}
+
+static uint8_t get_link_key_type(struct btdev *btdev)
+{
+	struct btdev *remote = btdev->conn;
+	uint8_t auth, unauth;
+
+	if (!remote)
+		return 0x00;
+
+	if (!btdev->simple_pairing_mode)
+		return 0x00;
+
+	if (btdev->ssp_debug_mode || remote->ssp_debug_mode)
+		return 0x03;
+
+	if (btdev->secure_conn_support && remote->secure_conn_support) {
+		unauth = 0x07;
+		auth = 0x08;
+	} else {
+		unauth = 0x04;
+		auth = 0x05;
+	}
+
+	if (btdev->io_cap == 0x03 || remote->io_cap == 0x03)
+		return unauth;
+
+	if (!(btdev->auth_req & 0x01) && !(remote->auth_req & 0x01))
+		return unauth;
+
+	/* DisplayOnly only produces authenticated with KeyboardOnly */
+	if (btdev->io_cap == 0x00 && remote->io_cap != 0x02)
+		return unauth;
+
+	/* DisplayOnly only produces authenticated with KeyboardOnly */
+	if (remote->io_cap == 0x00 && btdev->io_cap != 0x02)
+		return unauth;
+
+	return auth;
+}
+
+static void link_key_notify(struct btdev *btdev, const uint8_t *bdaddr,
+							const uint8_t *key)
+{
+	struct bt_hci_evt_link_key_notify ev;
+
+	memcpy(btdev->link_key, key, 16);
+
+	memcpy(ev.bdaddr, bdaddr, 6);
+	memcpy(ev.link_key, key, 16);
+	ev.key_type = get_link_key_type(btdev);
+
+	send_event(btdev, BT_HCI_EVT_LINK_KEY_NOTIFY, &ev, sizeof(ev));
+}
+
+static void encrypt_change(struct btdev *btdev, uint8_t mode, uint8_t status)
+{
+	struct bt_hci_evt_encrypt_change ev;
+
+	ev.status = status;
+	ev.handle = cpu_to_le16(42);
+	ev.encr_mode = mode;
+
+	send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+}
+
+static void pin_code_req_reply_complete(struct btdev *btdev,
+					const uint8_t *bdaddr, uint8_t pin_len,
+					const uint8_t *pin_code)
+{
+	struct bt_hci_evt_auth_complete ev;
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		remote = find_btdev_by_bdaddr(bdaddr);
+		if (!remote)
+			return;
+	}
+
+	memcpy(btdev->pin, pin_code, pin_len);
+	btdev->pin_len = pin_len;
+
+	if (!remote->pin_len) {
+		struct bt_hci_evt_pin_code_request pin_req;
+
+		memcpy(pin_req.bdaddr, btdev->bdaddr, 6);
+		send_event(remote, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req,
+							sizeof(pin_req));
+		return;
+	}
+
+	if (btdev->pin_len == remote->pin_len &&
+			!memcmp(btdev->pin, remote->pin, btdev->pin_len)) {
+		link_key_notify(btdev, remote->bdaddr, LINK_KEY_DUMMY);
+		link_key_notify(remote, btdev->bdaddr, LINK_KEY_DUMMY);
+		ev.status = BT_HCI_ERR_SUCCESS;
+	} else {
+		ev.status = BT_HCI_ERR_AUTH_FAILURE;
+	}
+
+	if (remote->conn) {
+		ev.handle = cpu_to_le16(42);
+		send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+	} else {
+		conn_complete(remote, btdev->bdaddr, ev.status);
+	}
+
+	btdev->pin_len = 0;
+	remote->pin_len = 0;
+}
+
+static void pin_code_req_neg_reply_complete(struct btdev *btdev,
+							const uint8_t *bdaddr)
+{
+	struct bt_hci_evt_auth_complete ev;
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		remote = find_btdev_by_bdaddr(bdaddr);
+		if (!remote)
+			return;
+	}
+
+	ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
+	ev.handle = cpu_to_le16(42);
+
+	if (btdev->conn)
+		send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+	else
+		conn_complete(btdev, bdaddr, BT_HCI_ERR_PIN_OR_KEY_MISSING);
+
+	if (remote->conn) {
+	        if (remote->pin_len)
+			send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev,
+								sizeof(ev));
+	} else {
+		conn_complete(remote, btdev->bdaddr,
+					BT_HCI_ERR_PIN_OR_KEY_MISSING);
+	}
+}
+
+static void auth_request_complete(struct btdev *btdev, uint16_t handle)
+{
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		struct bt_hci_evt_auth_complete ev;
+
+		ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		ev.handle = cpu_to_le16(handle);
+
+		send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+
+		return;
+	}
+
+	btdev->auth_init = true;
+
+	send_event(btdev, BT_HCI_EVT_LINK_KEY_REQUEST, remote->bdaddr, 6);
+}
+
+static void name_request_complete(struct btdev *btdev,
+					const uint8_t *bdaddr, uint8_t status)
+{
+        struct bt_hci_evt_remote_name_request_complete nc;
+
+	nc.status = status;
+	memcpy(nc.bdaddr, bdaddr, 6);
+	memset(nc.name, 0, 248);
+
+	if (!status) {
+		struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+		if (remote)
+			memcpy(nc.name, remote->name, 248);
+		else
+			nc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+	}
+
+	send_event(btdev, BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE,
+							&nc, sizeof(nc));
+}
+
+static void remote_features_complete(struct btdev *btdev, uint16_t handle)
+{
+	struct bt_hci_evt_remote_features_complete rfc;
+
+	if (btdev->conn) {
+		rfc.status = BT_HCI_ERR_SUCCESS;
+		rfc.handle = cpu_to_le16(handle);
+		memcpy(rfc.features, btdev->conn->features, 8);
+	} else {
+		rfc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		rfc.handle = cpu_to_le16(handle);
+		memset(rfc.features, 0, 8);
+	}
+
+	send_event(btdev, BT_HCI_EVT_REMOTE_FEATURES_COMPLETE,
+							&rfc, sizeof(rfc));
+}
+
+static void btdev_get_host_features(struct btdev *btdev, uint8_t features[8])
+{
+	memset(features, 0, 8);
+	if (btdev->simple_pairing_mode)
+		features[0] |= 0x01;
+	if (btdev->le_supported)
+		features[0] |= 0x02;
+	if (btdev->le_simultaneous)
+		features[0] |= 0x04;
+	if (btdev->secure_conn_support)
+		features[0] |= 0x08;
+}
+
+static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle,
+								uint8_t page)
+{
+	struct bt_hci_evt_remote_ext_features_complete refc;
+
+	if (btdev->conn && page < 0x02) {
+		refc.handle = cpu_to_le16(handle);
+		refc.page = page;
+		refc.max_page = 0x01;
+
+		switch (page) {
+		case 0x00:
+			refc.status = BT_HCI_ERR_SUCCESS;
+			memcpy(refc.features, btdev->conn->features, 8);
+			break;
+		case 0x01:
+			refc.status = BT_HCI_ERR_SUCCESS;
+			btdev_get_host_features(btdev->conn, refc.features);
+			break;
+		default:
+			refc.status = BT_HCI_ERR_INVALID_PARAMETERS;
+			memset(refc.features, 0, 8);
+			break;
+		}
+	} else {
+		refc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		refc.handle = cpu_to_le16(handle);
+		refc.page = page;
+		refc.max_page = 0x01;
+		memset(refc.features, 0, 8);
+	}
+
+	send_event(btdev, BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE,
+							&refc, sizeof(refc));
+}
+
+static void remote_version_complete(struct btdev *btdev, uint16_t handle)
+{
+	struct bt_hci_evt_remote_version_complete rvc;
+
+	if (btdev->conn) {
+		rvc.status = BT_HCI_ERR_SUCCESS;
+		rvc.handle = cpu_to_le16(handle);
+		rvc.lmp_ver = btdev->conn->version;
+		rvc.manufacturer = cpu_to_le16(btdev->conn->manufacturer);
+		rvc.lmp_subver = cpu_to_le16(btdev->conn->revision);
+	} else {
+		rvc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		rvc.handle = cpu_to_le16(handle);
+		rvc.lmp_ver = 0x00;
+		rvc.manufacturer = cpu_to_le16(0);
+		rvc.lmp_subver = cpu_to_le16(0);
+	}
+
+	send_event(btdev, BT_HCI_EVT_REMOTE_VERSION_COMPLETE,
+							&rvc, sizeof(rvc));
+}
+
+static void remote_clock_offset_complete(struct btdev *btdev, uint16_t handle)
+{
+	struct bt_hci_evt_clock_offset_complete coc;
+
+	if (btdev->conn) {
+		coc.status = BT_HCI_ERR_SUCCESS;
+		coc.handle = cpu_to_le16(handle);
+		coc.clock_offset = 0;
+	} else {
+		coc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		coc.handle = cpu_to_le16(handle);
+		coc.clock_offset = 0;
+	}
+
+	send_event(btdev, BT_HCI_EVT_CLOCK_OFFSET_COMPLETE,
+							&coc, sizeof(coc));
+}
+
+static void read_enc_key_size_complete(struct btdev *btdev, uint16_t handle)
+{
+	struct bt_hci_rsp_read_encrypt_key_size rsp;
+
+	rsp.handle = cpu_to_le16(handle);
+
+	if (btdev->conn) {
+		rsp.status = BT_HCI_ERR_SUCCESS;
+		rsp.key_size = 16;
+	} else {
+		rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		rsp.key_size = 0;
+	}
+
+	cmd_complete(btdev, BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE,
+							&rsp, sizeof(rsp));
+}
+
+static void io_cap_req_reply_complete(struct btdev *btdev,
+					const uint8_t *bdaddr,
+					uint8_t capability, uint8_t oob_data,
+					uint8_t authentication)
+{
+	struct btdev *remote = btdev->conn;
+	struct bt_hci_evt_io_capability_response ev;
+	struct bt_hci_rsp_io_capability_request_reply rsp;
+	uint8_t status;
+
+	if (!remote) {
+		status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		goto done;
+	}
+
+	status = BT_HCI_ERR_SUCCESS;
+
+	btdev->io_cap = capability;
+	btdev->auth_req = authentication;
+
+	memcpy(ev.bdaddr, btdev->bdaddr, 6);
+	ev.capability = capability;
+	ev.oob_data = oob_data;
+	ev.authentication = authentication;
+
+	send_event(remote, BT_HCI_EVT_IO_CAPABILITY_RESPONSE, &ev, sizeof(ev));
+
+	if (remote->io_cap) {
+		struct bt_hci_evt_user_confirm_request cfm;
+
+		memcpy(cfm.bdaddr, btdev->bdaddr, 6);
+		cfm.passkey = 0;
+
+		send_event(remote, BT_HCI_EVT_USER_CONFIRM_REQUEST,
+							&cfm, sizeof(cfm));
+
+		memcpy(cfm.bdaddr, bdaddr, 6);
+		send_event(btdev, BT_HCI_EVT_USER_CONFIRM_REQUEST,
+							&cfm, sizeof(cfm));
+	} else {
+		send_event(remote, BT_HCI_EVT_IO_CAPABILITY_REQUEST,
+							btdev->bdaddr, 6);
+	}
+
+done:
+	rsp.status = status;
+	memcpy(rsp.bdaddr, bdaddr, 6);
+	cmd_complete(btdev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+							&rsp, sizeof(rsp));
+}
+
+static void io_cap_req_neg_reply_complete(struct btdev *btdev,
+							const uint8_t *bdaddr)
+{
+	struct bt_hci_rsp_io_capability_request_neg_reply rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.bdaddr, bdaddr, 6);
+	cmd_complete(btdev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY,
+							&rsp, sizeof(rsp));
+}
+
+static void ssp_complete(struct btdev *btdev, const uint8_t *bdaddr,
+						uint8_t status, bool wait)
+{
+	struct bt_hci_evt_simple_pairing_complete iev, aev;
+	struct bt_hci_evt_auth_complete auth;
+	struct btdev *remote = btdev->conn;
+	struct btdev *init, *accp;
+
+	if (!remote)
+		return;
+
+	btdev->ssp_status = status;
+	btdev->ssp_auth_complete = true;
+
+	if (!remote->ssp_auth_complete && wait)
+		return;
+
+	if (status == BT_HCI_ERR_SUCCESS &&
+				remote->ssp_status != BT_HCI_ERR_SUCCESS)
+		status = remote->ssp_status;
+
+	iev.status = status;
+	aev.status = status;
+
+	if (btdev->auth_init) {
+		init = btdev;
+		accp = remote;
+		memcpy(iev.bdaddr, bdaddr, 6);
+		memcpy(aev.bdaddr, btdev->bdaddr, 6);
+	} else {
+		init = remote;
+		accp = btdev;
+		memcpy(iev.bdaddr, btdev->bdaddr, 6);
+		memcpy(aev.bdaddr, bdaddr, 6);
+	}
+
+	send_event(init, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &iev,
+								sizeof(iev));
+	send_event(accp, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &aev,
+								sizeof(aev));
+
+	if (status == BT_HCI_ERR_SUCCESS) {
+		link_key_notify(init, iev.bdaddr, LINK_KEY_DUMMY);
+		link_key_notify(accp, aev.bdaddr, LINK_KEY_DUMMY);
+	}
+
+	auth.status = status;
+	auth.handle = cpu_to_le16(42);
+	send_event(init, BT_HCI_EVT_AUTH_COMPLETE, &auth, sizeof(auth));
+}
+
+static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote,
+								uint8_t type)
+{
+	struct __packed {
+		uint8_t subevent;
+		union {
+			struct bt_hci_evt_le_adv_report lar;
+			uint8_t raw[10 + 31 + 1];
+		};
+	} meta_event;
+
+	meta_event.subevent = BT_HCI_EVT_LE_ADV_REPORT;
+
+	memset(&meta_event.lar, 0, sizeof(meta_event.lar));
+	meta_event.lar.num_reports = 1;
+	meta_event.lar.event_type = type;
+	meta_event.lar.addr_type = remote->le_adv_own_addr;
+	memcpy(meta_event.lar.addr, adv_addr(remote), 6);
+
+	/* Scan or advertising response */
+	if (type == 0x04) {
+		meta_event.lar.data_len = remote->le_scan_data_len;
+		memcpy(meta_event.lar.data, remote->le_scan_data,
+						meta_event.lar.data_len);
+	} else {
+		meta_event.lar.data_len = remote->le_adv_data_len;
+		memcpy(meta_event.lar.data, remote->le_adv_data,
+						meta_event.lar.data_len);
+	}
+	/* Not available */
+	meta_event.raw[10 + meta_event.lar.data_len] = 127;
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, &meta_event,
+					1 + 10 + meta_event.lar.data_len + 1);
+}
+
+static uint8_t get_adv_report_type(uint8_t adv_type)
+{
+	/*
+	 * Connectable low duty cycle directed advertising creates a
+	 * connectable directed advertising report type.
+	 */
+	if (adv_type == 0x04)
+		return 0x01;
+
+	return adv_type;
+}
+
+static void le_set_adv_enable_complete(struct btdev *btdev)
+{
+	uint8_t report_type;
+	int i;
+
+	report_type = get_adv_report_type(btdev->le_adv_type);
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		if (!btdev_list[i] || btdev_list[i] == btdev)
+			continue;
+
+		if (!btdev_list[i]->le_scan_enable)
+			continue;
+
+		if (!adv_match(btdev_list[i], btdev))
+			continue;
+
+		le_send_adv_report(btdev_list[i], btdev, report_type);
+
+		if (btdev_list[i]->le_scan_type != 0x01)
+			continue;
+
+		/* ADV_IND & ADV_SCAN_IND generate a scan response */
+		if (btdev->le_adv_type == 0x00 || btdev->le_adv_type == 0x02)
+			le_send_adv_report(btdev_list[i], btdev, 0x04);
+	}
+}
+
+static void le_set_scan_enable_complete(struct btdev *btdev)
+{
+	int i;
+
+	for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+		uint8_t report_type;
+
+		if (!btdev_list[i] || btdev_list[i] == btdev)
+			continue;
+
+		if (!btdev_list[i]->le_adv_enable)
+			continue;
+
+		if (!adv_match(btdev, btdev_list[i]))
+			continue;
+
+		report_type = get_adv_report_type(btdev_list[i]->le_adv_type);
+		le_send_adv_report(btdev, btdev_list[i], report_type);
+
+		if (btdev->le_scan_type != 0x01)
+			continue;
+
+		/* ADV_IND & ADV_SCAN_IND generate a scan response */
+		if (btdev_list[i]->le_adv_type == 0x00 ||
+					btdev_list[i]->le_adv_type == 0x02)
+			le_send_adv_report(btdev, btdev_list[i], 0x04);
+	}
+}
+
+static void le_read_remote_features_complete(struct btdev *btdev)
+{
+	char buf[1 + sizeof(struct bt_hci_evt_le_remote_features_complete)];
+	struct bt_hci_evt_le_remote_features_complete *ev = (void *) &buf[1];
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		cmd_status(btdev, BT_HCI_ERR_UNKNOWN_CONN_ID,
+					BT_HCI_CMD_LE_READ_REMOTE_FEATURES);
+		return;
+	}
+
+	cmd_status(btdev, BT_HCI_ERR_SUCCESS,
+				BT_HCI_CMD_LE_READ_REMOTE_FEATURES);
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE;
+	ev->status = BT_HCI_ERR_SUCCESS;
+	ev->handle = cpu_to_le16(42);
+	memcpy(ev->features, remote->le_features, 8);
+
+	send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+}
+
+static void le_start_encrypt_complete(struct btdev *btdev, uint16_t ediv,
+								uint64_t rand)
+{
+	char buf[1 + sizeof(struct bt_hci_evt_le_long_term_key_request)];
+	struct bt_hci_evt_le_long_term_key_request *ev = (void *) &buf[1];
+	struct btdev *remote = btdev->conn;
+
+	if (!remote) {
+		cmd_status(btdev, BT_HCI_ERR_UNKNOWN_CONN_ID,
+						BT_HCI_CMD_LE_START_ENCRYPT);
+		return;
+	}
+
+	cmd_status(btdev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_START_ENCRYPT);
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST;
+	ev->handle = cpu_to_le16(42);
+	ev->ediv = ediv;
+	ev->rand = rand;
+
+	send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+}
+
+static void le_encrypt_complete(struct btdev *btdev)
+{
+	struct bt_hci_evt_encrypt_change ev;
+	struct bt_hci_rsp_le_ltk_req_reply rp;
+	struct btdev *remote = btdev->conn;
+
+	memset(&rp, 0, sizeof(rp));
+	rp.handle = cpu_to_le16(42);
+
+	if (!remote) {
+		rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp,
+							sizeof(rp));
+		return;
+	}
+
+	rp.status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp, sizeof(rp));
+
+	memset(&ev, 0, sizeof(ev));
+
+	if (memcmp(btdev->le_ltk, remote->le_ltk, 16)) {
+		ev.status = BT_HCI_ERR_AUTH_FAILURE;
+		ev.encr_mode = 0x00;
+	} else {
+		ev.status = BT_HCI_ERR_SUCCESS;
+		ev.encr_mode = 0x01;
+	}
+
+	ev.handle = cpu_to_le16(42);
+
+	send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+	send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+}
+
+static void ltk_neg_reply_complete(struct btdev *btdev)
+{
+	struct bt_hci_rsp_le_ltk_req_neg_reply rp;
+	struct bt_hci_evt_encrypt_change ev;
+	struct btdev *remote = btdev->conn;
+
+	memset(&rp, 0, sizeof(rp));
+	rp.handle = cpu_to_le16(42);
+
+	if (!remote) {
+		rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+		cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp,
+							sizeof(rp));
+		return;
+	}
+
+	rp.status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp, sizeof(rp));
+
+	memset(&ev, 0, sizeof(ev));
+	ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
+	ev.handle = cpu_to_le16(42);
+
+	send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+}
+
+static void btdev_reset(struct btdev *btdev)
+{
+	/* FIXME: include here clearing of all states that should be
+	 * cleared upon HCI_Reset
+	 */
+
+	btdev->le_scan_enable		= 0x00;
+	btdev->le_adv_enable		= 0x00;
+}
+
+static void default_cmd(struct btdev *btdev, uint16_t opcode,
+						const void *data, uint8_t len)
+{
+	const struct bt_hci_cmd_remote_name_request_cancel *rnrc;
+	const struct bt_hci_cmd_write_default_link_policy *wdlp;
+	const struct bt_hci_cmd_set_event_mask *sem;
+	const struct bt_hci_cmd_set_event_filter *sef;
+	const struct bt_hci_cmd_write_local_name *wln;
+	const struct bt_hci_cmd_write_conn_accept_timeout *wcat;
+	const struct bt_hci_cmd_write_page_timeout *wpt;
+	const struct bt_hci_cmd_write_scan_enable *wse;
+	const struct bt_hci_cmd_write_page_scan_activity *wpsa;
+	const struct bt_hci_cmd_write_inquiry_scan_activity *wisa;
+	const struct bt_hci_cmd_write_page_scan_type *wpst;
+	const struct bt_hci_cmd_write_auth_enable *wae;
+	const struct bt_hci_cmd_write_class_of_dev *wcod;
+	const struct bt_hci_cmd_write_voice_setting *wvs;
+	const struct bt_hci_cmd_set_host_flow_control *shfc;
+	const struct bt_hci_cmd_write_inquiry_mode *wim;
+	const struct bt_hci_cmd_write_afh_assessment_mode *waam;
+	const struct bt_hci_cmd_write_ext_inquiry_response *weir;
+	const struct bt_hci_cmd_write_simple_pairing_mode *wspm;
+	const struct bt_hci_cmd_io_capability_request_reply *icrr;
+	const struct bt_hci_cmd_io_capability_request_reply *icrnr;
+	const struct bt_hci_cmd_read_encrypt_key_size *reks;
+	const struct bt_hci_cmd_write_le_host_supported *wlhs;
+	const struct bt_hci_cmd_write_secure_conn_support *wscs;
+	const struct bt_hci_cmd_set_event_mask_page2 *semp2;
+	const struct bt_hci_cmd_le_set_event_mask *lsem;
+	const struct bt_hci_cmd_le_set_random_address *lsra;
+	const struct bt_hci_cmd_le_set_adv_parameters *lsap;
+	const struct bt_hci_cmd_le_set_adv_data *lsad;
+	const struct bt_hci_cmd_le_set_scan_rsp_data *lssrd;
+	const struct bt_hci_cmd_setup_sync_conn *ssc;
+	const struct bt_hci_cmd_write_ssp_debug_mode *wsdm;
+	const struct bt_hci_cmd_le_set_adv_enable *lsae;
+	const struct bt_hci_cmd_le_set_scan_parameters *lssp;
+	const struct bt_hci_cmd_le_set_scan_enable *lsse;
+	const struct bt_hci_cmd_le_start_encrypt *lse;
+	const struct bt_hci_cmd_le_ltk_req_reply *llrr;
+	const struct bt_hci_cmd_le_encrypt *lenc_cmd;
+	const struct bt_hci_cmd_le_generate_dhkey *dh;
+	const struct bt_hci_cmd_le_conn_param_req_reply *lcprr_cmd;
+	const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr_cmd;
+	const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd;
+	const struct bt_hci_cmd_read_rssi *rrssi;
+	const struct bt_hci_cmd_read_tx_power *rtxp;
+	struct bt_hci_rsp_read_default_link_policy rdlp;
+	struct bt_hci_rsp_read_stored_link_key rslk;
+	struct bt_hci_rsp_write_stored_link_key wslk;
+	struct bt_hci_rsp_delete_stored_link_key dslk;
+	struct bt_hci_rsp_read_local_name rln;
+	struct bt_hci_rsp_read_conn_accept_timeout rcat;
+	struct bt_hci_rsp_read_page_timeout rpt;
+	struct bt_hci_rsp_read_scan_enable rse;
+	struct bt_hci_rsp_read_page_scan_activity rpsa;
+	struct bt_hci_rsp_read_inquiry_scan_activity risa;
+	struct bt_hci_rsp_read_page_scan_type rpst;
+	struct bt_hci_rsp_read_auth_enable rae;
+	struct bt_hci_rsp_read_class_of_dev rcod;
+	struct bt_hci_rsp_read_voice_setting rvs;
+	struct bt_hci_rsp_read_num_supported_iac rnsi;
+	struct bt_hci_rsp_read_current_iac_lap *rcil;
+	struct bt_hci_rsp_read_inquiry_mode rim;
+	struct bt_hci_rsp_read_afh_assessment_mode raam;
+	struct bt_hci_rsp_read_ext_inquiry_response reir;
+	struct bt_hci_rsp_read_simple_pairing_mode rspm;
+	struct bt_hci_rsp_read_local_oob_data rlod;
+	struct bt_hci_rsp_read_inquiry_resp_tx_power rirtp;
+	struct bt_hci_rsp_read_le_host_supported rlhs;
+	struct bt_hci_rsp_read_secure_conn_support rscs;
+	struct bt_hci_rsp_read_local_oob_ext_data rloed;
+	struct bt_hci_rsp_read_sync_train_params rstp;
+	struct bt_hci_rsp_read_local_version rlv;
+	struct bt_hci_rsp_read_local_commands rlc;
+	struct bt_hci_rsp_read_local_features rlf;
+	struct bt_hci_rsp_read_local_ext_features rlef;
+	struct bt_hci_rsp_read_buffer_size rbs;
+	struct bt_hci_rsp_read_country_code rcc;
+	struct bt_hci_rsp_read_bd_addr rba;
+	struct bt_hci_rsp_read_data_block_size rdbs;
+	struct bt_hci_rsp_read_local_codecs *rlsc;
+	struct bt_hci_rsp_read_local_amp_info rlai;
+	struct bt_hci_rsp_read_local_amp_assoc rlaa_rsp;
+	struct bt_hci_rsp_get_mws_transport_config *gmtc;
+	struct bt_hci_rsp_le_conn_param_req_reply lcprr_rsp;
+	struct bt_hci_rsp_le_conn_param_req_neg_reply lcprnr_rsp;
+	struct bt_hci_rsp_le_read_buffer_size lrbs;
+	struct bt_hci_rsp_le_read_local_features lrlf;
+	struct bt_hci_rsp_le_read_adv_tx_power lratp;
+	struct bt_hci_rsp_le_read_supported_states lrss;
+	struct bt_hci_rsp_le_read_white_list_size lrwls;
+	struct bt_hci_rsp_le_encrypt lenc;
+	struct bt_hci_rsp_le_rand lr;
+	struct bt_hci_rsp_le_test_end lte;
+	struct bt_hci_rsp_remote_name_request_cancel rnrc_rsp;
+	struct bt_hci_rsp_link_key_request_reply lkrr_rsp;
+	struct bt_hci_rsp_link_key_request_neg_reply lkrnr_rsp;
+	struct bt_hci_rsp_pin_code_request_neg_reply pcrr_rsp;
+	struct bt_hci_rsp_pin_code_request_neg_reply pcrnr_rsp;
+	struct bt_hci_rsp_user_confirm_request_reply ucrr_rsp;
+	struct bt_hci_rsp_user_confirm_request_neg_reply ucrnr_rsp;
+	struct bt_hci_rsp_read_rssi rrssi_rsp;
+	struct bt_hci_rsp_read_tx_power rtxp_rsp;
+	struct bt_hci_evt_le_read_local_pk256_complete pk_evt;
+	struct bt_hci_evt_le_generate_dhkey_complete dh_evt;
+	uint8_t status, page;
+
+	switch (opcode) {
+	case BT_HCI_CMD_INQUIRY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_INQUIRY_CANCEL:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		inquiry_cancel(btdev);
+		break;
+
+	case BT_HCI_CMD_CREATE_CONN:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_DISCONNECT:
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_CREATE_CONN_CANCEL:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_ACCEPT_CONN_REQUEST:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_REJECT_CONN_REQUEST:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_LINK_KEY_REQUEST_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		lkrr_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(lkrr_rsp.bdaddr, data, 6);
+		cmd_complete(btdev, opcode, &lkrr_rsp, sizeof(lkrr_rsp));
+		break;
+
+	case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		lkrnr_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(lkrnr_rsp.bdaddr, data, 6);
+		cmd_complete(btdev, opcode, &lkrnr_rsp, sizeof(lkrnr_rsp));
+		break;
+
+	case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		pcrr_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(pcrr_rsp.bdaddr, data, 6);
+		cmd_complete(btdev, opcode, &pcrr_rsp, sizeof(pcrr_rsp));
+		break;
+
+	case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		pcrnr_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(pcrnr_rsp.bdaddr, data, 6);
+		cmd_complete(btdev, opcode, &pcrnr_rsp, sizeof(pcrnr_rsp));
+		break;
+
+	case BT_HCI_CMD_AUTH_REQUESTED:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_SET_CONN_ENCRYPT:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_REMOTE_NAME_REQUEST:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rnrc = data;
+		rnrc_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(rnrc_rsp.bdaddr, rnrc->bdaddr, 6);
+		cmd_complete(btdev, opcode, &rnrc_rsp, sizeof(rnrc_rsp));
+		break;
+
+	case BT_HCI_CMD_READ_REMOTE_FEATURES:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_READ_REMOTE_EXT_FEATURES:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_READ_REMOTE_VERSION:
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_READ_CLOCK_OFFSET:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_READ_DEFAULT_LINK_POLICY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rdlp.status = BT_HCI_ERR_SUCCESS;
+		rdlp.policy = cpu_to_le16(btdev->default_link_policy);
+		cmd_complete(btdev, opcode, &rdlp, sizeof(rdlp));
+		break;
+
+	case BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wdlp = data;
+		btdev->default_link_policy = le16_to_cpu(wdlp->policy);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_SET_EVENT_MASK:
+		sem = data;
+		memcpy(btdev->event_mask, sem->mask, 8);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_RESET:
+		btdev_reset(btdev);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_SET_EVENT_FILTER:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		sef = data;
+		btdev->event_filter = sef->type;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_STORED_LINK_KEY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rslk.status = BT_HCI_ERR_SUCCESS;
+		rslk.max_num_keys = cpu_to_le16(0);
+		rslk.num_keys = cpu_to_le16(0);
+		cmd_complete(btdev, opcode, &rslk, sizeof(rslk));
+		break;
+
+	case BT_HCI_CMD_WRITE_STORED_LINK_KEY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wslk.status = BT_HCI_ERR_SUCCESS;
+		wslk.num_keys = 0;
+		cmd_complete(btdev, opcode, &wslk, sizeof(wslk));
+		break;
+
+	case BT_HCI_CMD_DELETE_STORED_LINK_KEY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		dslk.status = BT_HCI_ERR_SUCCESS;
+		dslk.num_keys = cpu_to_le16(0);
+		cmd_complete(btdev, opcode, &dslk, sizeof(dslk));
+		break;
+
+	case BT_HCI_CMD_WRITE_LOCAL_NAME:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wln = data;
+		memcpy(btdev->name, wln->name, 248);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_NAME:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rln.status = BT_HCI_ERR_SUCCESS;
+		memcpy(rln.name, btdev->name, 248);
+		cmd_complete(btdev, opcode, &rln, sizeof(rln));
+		break;
+
+	case BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rcat.status = BT_HCI_ERR_SUCCESS;
+		rcat.timeout = cpu_to_le16(btdev->conn_accept_timeout);
+		cmd_complete(btdev, opcode, &rcat, sizeof(rcat));
+		break;
+
+	case BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wcat = data;
+		btdev->conn_accept_timeout = le16_to_cpu(wcat->timeout);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_PAGE_TIMEOUT:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rpt.status = BT_HCI_ERR_SUCCESS;
+		rpt.timeout = cpu_to_le16(btdev->page_timeout);
+		cmd_complete(btdev, opcode, &rpt, sizeof(rpt));
+		break;
+
+	case BT_HCI_CMD_WRITE_PAGE_TIMEOUT:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wpt = data;
+		btdev->page_timeout = le16_to_cpu(wpt->timeout);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_SCAN_ENABLE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rse.status = BT_HCI_ERR_SUCCESS;
+		rse.enable = btdev->scan_enable;
+		cmd_complete(btdev, opcode, &rse, sizeof(rse));
+		break;
+
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wse = data;
+		btdev->scan_enable = wse->enable;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rpsa.status = BT_HCI_ERR_SUCCESS;
+		rpsa.interval = cpu_to_le16(btdev->page_scan_interval);
+		rpsa.window = cpu_to_le16(btdev->page_scan_window);
+		cmd_complete(btdev, opcode, &rpsa, sizeof(rpsa));
+		break;
+
+	case BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wpsa = data;
+		btdev->page_scan_interval = le16_to_cpu(wpsa->interval);
+		btdev->page_scan_window = le16_to_cpu(wpsa->window);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		risa.status = BT_HCI_ERR_SUCCESS;
+		risa.interval = cpu_to_le16(btdev->inquiry_scan_interval);
+		risa.window = cpu_to_le16(btdev->inquiry_scan_window);
+		cmd_complete(btdev, opcode, &risa, sizeof(risa));
+		break;
+
+	case BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wisa = data;
+		btdev->inquiry_scan_interval = le16_to_cpu(wisa->interval);
+		btdev->inquiry_scan_window = le16_to_cpu(wisa->window);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_PAGE_SCAN_TYPE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rpst.status = BT_HCI_ERR_SUCCESS;
+		rpst.type = btdev->page_scan_type;
+		cmd_complete(btdev, opcode, &rpst, sizeof(rpst));
+		break;
+
+	case BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wpst = data;
+		btdev->page_scan_type = wpst->type;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_AUTH_ENABLE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rae.status = BT_HCI_ERR_SUCCESS;
+		rae.enable = btdev->auth_enable;
+		cmd_complete(btdev, opcode, &rae, sizeof(rae));
+		break;
+
+	case BT_HCI_CMD_WRITE_AUTH_ENABLE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wae = data;
+		btdev->auth_enable = wae->enable;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_CLASS_OF_DEV:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rcod.status = BT_HCI_ERR_SUCCESS;
+		memcpy(rcod.dev_class, btdev->dev_class, 3);
+		cmd_complete(btdev, opcode, &rcod, sizeof(rcod));
+		break;
+
+	case BT_HCI_CMD_WRITE_CLASS_OF_DEV:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wcod = data;
+		memcpy(btdev->dev_class, wcod->dev_class, 3);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_VOICE_SETTING:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rvs.status = BT_HCI_ERR_SUCCESS;
+		rvs.setting = cpu_to_le16(btdev->voice_setting);
+		cmd_complete(btdev, opcode, &rvs, sizeof(rvs));
+		break;
+
+	case BT_HCI_CMD_WRITE_VOICE_SETTING:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wvs = data;
+		btdev->voice_setting = le16_to_cpu(wvs->setting);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_SET_HOST_FLOW_CONTROL:
+		shfc = data;
+		if (shfc->enable > 0x03) {
+			status = BT_HCI_ERR_INVALID_PARAMETERS;
+		} else {
+			btdev->host_flow_control = shfc->enable;
+			status = BT_HCI_ERR_SUCCESS;
+		}
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_HOST_BUFFER_SIZE:
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_HOST_NUM_COMPLETED_PACKETS:
+		/* This command is special in the sense that no event is
+		 * normally generated after the command has completed.
+		 */
+		break;
+
+	case BT_HCI_CMD_READ_NUM_SUPPORTED_IAC:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rnsi.status = BT_HCI_ERR_SUCCESS;
+		rnsi.num_iac = 0x01;
+		cmd_complete(btdev, opcode, &rnsi, sizeof(rnsi));
+		break;
+
+	case BT_HCI_CMD_READ_CURRENT_IAC_LAP:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rcil = alloca(sizeof(*rcil) + 3);
+		rcil->status = BT_HCI_ERR_SUCCESS;
+		rcil->num_iac = 0x01;
+		rcil->iac_lap[0] = 0x33;
+		rcil->iac_lap[1] = 0x8b;
+		rcil->iac_lap[2] = 0x9e;
+		cmd_complete(btdev, opcode, rcil, sizeof(*rcil) + 3);
+		break;
+
+	case BT_HCI_CMD_WRITE_CURRENT_IAC_LAP:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_INQUIRY_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rim.status = BT_HCI_ERR_SUCCESS;
+		rim.mode = btdev->inquiry_mode;
+		cmd_complete(btdev, opcode, &rim, sizeof(rim));
+		break;
+
+	case BT_HCI_CMD_WRITE_INQUIRY_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wim = data;
+		btdev->inquiry_mode = wim->mode;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		raam.status = BT_HCI_ERR_SUCCESS;
+		raam.mode = btdev->afh_assessment_mode;
+		cmd_complete(btdev, opcode, &raam, sizeof(raam));
+		break;
+
+	case BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		waam = data;
+		btdev->afh_assessment_mode = waam->mode;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_EXT_INQUIRY_RESPONSE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		reir.status = BT_HCI_ERR_SUCCESS;
+		reir.fec = btdev->ext_inquiry_fec;
+		memcpy(reir.data, btdev->ext_inquiry_rsp, 240);
+		cmd_complete(btdev, opcode, &reir, sizeof(reir));
+		break;
+
+	case BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		weir = data;
+		btdev->ext_inquiry_fec = weir->fec;
+		memcpy(btdev->ext_inquiry_rsp, weir->data, 240);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rspm.status = BT_HCI_ERR_SUCCESS;
+		rspm.mode = btdev->simple_pairing_mode;
+		cmd_complete(btdev, opcode, &rspm, sizeof(rspm));
+		break;
+
+	case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wspm = data;
+		btdev->simple_pairing_mode = wspm->mode;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		icrr = data;
+		io_cap_req_reply_complete(btdev, icrr->bdaddr,
+							icrr->capability,
+							icrr->oob_data,
+							icrr->authentication);
+		break;
+
+	case BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		icrnr = data;
+		io_cap_req_neg_reply_complete(btdev, icrnr->bdaddr);
+		ssp_complete(btdev, icrnr->bdaddr, BT_HCI_ERR_AUTH_FAILURE,
+									false);
+		break;
+
+	case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		ucrr_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(ucrr_rsp.bdaddr, data, 6);
+		cmd_complete(btdev, opcode, &ucrr_rsp, sizeof(ucrr_rsp));
+		ssp_complete(btdev, data, BT_HCI_ERR_SUCCESS, true);
+		break;
+
+	case BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		ucrnr_rsp.status = BT_HCI_ERR_SUCCESS;
+		memcpy(ucrnr_rsp.bdaddr, data, 6);
+		cmd_complete(btdev, opcode, &ucrnr_rsp, sizeof(ucrnr_rsp));
+		ssp_complete(btdev, data, BT_HCI_ERR_AUTH_FAILURE, true);
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_OOB_DATA:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rlod.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &rlod, sizeof(rlod));
+		break;
+
+	case BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rirtp.status = BT_HCI_ERR_SUCCESS;
+		rirtp.level = 0;
+		cmd_complete(btdev, opcode, &rirtp, sizeof(rirtp));
+		break;
+
+	case BT_HCI_CMD_READ_LE_HOST_SUPPORTED:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		rlhs.status = BT_HCI_ERR_SUCCESS;
+		rlhs.supported = btdev->le_supported;
+		rlhs.simultaneous = btdev->le_simultaneous;
+		cmd_complete(btdev, opcode, &rlhs, sizeof(rlhs));
+		break;
+
+	case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		wlhs = data;
+		btdev->le_supported = wlhs->supported;
+		btdev->le_simultaneous = wlhs->simultaneous;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_SECURE_CONN_SUPPORT:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		rscs.status = BT_HCI_ERR_SUCCESS;
+		rscs.support = btdev->secure_conn_support;
+		cmd_complete(btdev, opcode, &rscs, sizeof(rscs));
+		break;
+
+	case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		wscs = data;
+		btdev->secure_conn_support = wscs->support;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		rloed.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &rloed, sizeof(rloed));
+		break;
+
+	case BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		rstp.status = BT_HCI_ERR_SUCCESS;
+		rstp.interval = cpu_to_le16(btdev->sync_train_interval);
+		rstp.timeout = cpu_to_le32(btdev->sync_train_timeout);
+		rstp.service_data = btdev->sync_train_service_data;
+		cmd_complete(btdev, opcode, &rstp, sizeof(rstp));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_VERSION:
+		rlv.status = BT_HCI_ERR_SUCCESS;
+		rlv.hci_ver = btdev->version;
+		rlv.hci_rev = cpu_to_le16(btdev->revision);
+		rlv.lmp_ver = btdev->version;
+		rlv.manufacturer = cpu_to_le16(btdev->manufacturer);
+		rlv.lmp_subver = cpu_to_le16(btdev->revision);
+		cmd_complete(btdev, opcode, &rlv, sizeof(rlv));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_COMMANDS:
+		rlc.status = BT_HCI_ERR_SUCCESS;
+		memcpy(rlc.commands, btdev->commands, 64);
+		cmd_complete(btdev, opcode, &rlc, sizeof(rlc));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_FEATURES:
+		rlf.status = BT_HCI_ERR_SUCCESS;
+		memcpy(rlf.features, btdev->features, 8);
+		cmd_complete(btdev, opcode, &rlf, sizeof(rlf));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_EXT_FEATURES:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+
+		page = ((const uint8_t *) data)[0];
+
+		rlef.page = page;
+		rlef.max_page = btdev->max_page;
+
+		if (page > btdev->max_page) {
+			rlef.status = BT_HCI_ERR_INVALID_PARAMETERS;
+			memset(rlef.features, 0, 8);
+			cmd_complete(btdev, opcode, &rlef, sizeof(rlef));
+			break;
+		}
+
+		switch (page) {
+		case 0x00:
+			rlef.status = BT_HCI_ERR_SUCCESS;
+			memcpy(rlef.features, btdev->features, 8);
+			break;
+		case 0x01:
+			rlef.status = BT_HCI_ERR_SUCCESS;
+			btdev_get_host_features(btdev, rlef.features);
+			break;
+		case 0x02:
+			rlef.status = BT_HCI_ERR_SUCCESS;
+			memcpy(rlef.features, btdev->feat_page_2, 8);
+			break;
+		default:
+			rlef.status = BT_HCI_ERR_INVALID_PARAMETERS;
+			memset(rlef.features, 0, 8);
+			break;
+		}
+		cmd_complete(btdev, opcode, &rlef, sizeof(rlef));
+		break;
+
+	case BT_HCI_CMD_READ_BUFFER_SIZE:
+		rbs.status = BT_HCI_ERR_SUCCESS;
+		rbs.acl_mtu = cpu_to_le16(btdev->acl_mtu);
+		rbs.sco_mtu = 0;
+		rbs.acl_max_pkt = cpu_to_le16(btdev->acl_max_pkt);
+		rbs.sco_max_pkt = cpu_to_le16(0);
+		cmd_complete(btdev, opcode, &rbs, sizeof(rbs));
+		break;
+
+	case BT_HCI_CMD_READ_COUNTRY_CODE:
+		rcc.status = BT_HCI_ERR_SUCCESS;
+		rcc.code = btdev->country_code;
+		cmd_complete(btdev, opcode, &rcc, sizeof(rcc));
+		break;
+
+	case BT_HCI_CMD_READ_BD_ADDR:
+		rba.status = BT_HCI_ERR_SUCCESS;
+		memcpy(rba.bdaddr, btdev->bdaddr, 6);
+		cmd_complete(btdev, opcode, &rba, sizeof(rba));
+		break;
+
+	case BT_HCI_CMD_READ_DATA_BLOCK_SIZE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rdbs.status = BT_HCI_ERR_SUCCESS;
+		rdbs.max_acl_len = cpu_to_le16(btdev->acl_mtu);
+		rdbs.block_len = cpu_to_le16(btdev->acl_mtu);
+		rdbs.num_blocks = cpu_to_le16(btdev->acl_max_pkt);
+		cmd_complete(btdev, opcode, &rdbs, sizeof(rdbs));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_CODECS:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		rlsc = alloca(sizeof(*rlsc) + 7);
+		rlsc->status = BT_HCI_ERR_SUCCESS;
+		rlsc->num_codecs = 0x06;
+		rlsc->codec[0] = 0x00;
+		rlsc->codec[1] = 0x01;
+		rlsc->codec[2] = 0x02;
+		rlsc->codec[3] = 0x03;
+		rlsc->codec[4] = 0x04;
+		rlsc->codec[5] = 0x05;
+		rlsc->codec[6] = 0x00;
+		cmd_complete(btdev, opcode, rlsc, sizeof(*rlsc) + 7);
+		break;
+
+	case BT_HCI_CMD_READ_RSSI:
+		rrssi = data;
+
+		rrssi_rsp.status = BT_HCI_ERR_SUCCESS;
+		rrssi_rsp.handle = rrssi->handle;
+		rrssi_rsp.rssi = -1; /* non-zero so we can see it in tester */
+		cmd_complete(btdev, opcode, &rrssi_rsp, sizeof(rrssi_rsp));
+		break;
+
+	case BT_HCI_CMD_READ_TX_POWER:
+		rtxp = data;
+
+		switch (rtxp->type) {
+		case 0x00:
+			rtxp_rsp.status = BT_HCI_ERR_SUCCESS;
+			rtxp_rsp.level =  -1; /* non-zero */
+			break;
+
+		case 0x01:
+			rtxp_rsp.status = BT_HCI_ERR_SUCCESS;
+			rtxp_rsp.level = 4; /* max for class 2 radio */
+			break;
+
+		default:
+			rtxp_rsp.level = 0;
+			rtxp_rsp.status = BT_HCI_ERR_INVALID_PARAMETERS;
+			break;
+		}
+
+		rtxp_rsp.handle = rtxp->handle;
+		cmd_complete(btdev, opcode, &rtxp_rsp, sizeof(rtxp_rsp));
+		break;
+
+	case BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE:
+		if (btdev->type != BTDEV_TYPE_BREDRLE &&
+					btdev->type != BTDEV_TYPE_BREDR)
+			goto unsupported;
+		reks = data;
+		read_enc_key_size_complete(btdev, le16_to_cpu(reks->handle));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_AMP_INFO:
+		if (btdev->type != BTDEV_TYPE_AMP)
+			goto unsupported;
+		rlai.status = BT_HCI_ERR_SUCCESS;
+		rlai.amp_status = 0x01;		/* Used for Bluetooth only */
+		rlai.total_bw = cpu_to_le32(0);
+		rlai.max_bw = cpu_to_le32(0);
+		rlai.min_latency = cpu_to_le32(0);
+		rlai.max_pdu = cpu_to_le32(672);
+		rlai.amp_type = 0x01;		/* 802.11 AMP Controller */
+		rlai.pal_cap = cpu_to_le16(0x0000);
+		rlai.max_assoc_len = cpu_to_le16(672);
+		rlai.max_flush_to = cpu_to_le32(0xffffffff);
+		rlai.be_flush_to = cpu_to_le32(0xffffffff);
+		cmd_complete(btdev, opcode, &rlai, sizeof(rlai));
+		break;
+
+	case BT_HCI_CMD_READ_LOCAL_AMP_ASSOC:
+		if (btdev->type != BTDEV_TYPE_AMP)
+			goto unsupported;
+		rlaa_cmd = data;
+		rlaa_rsp.status = BT_HCI_ERR_SUCCESS;
+		rlaa_rsp.phy_handle = rlaa_cmd->phy_handle;
+		rlaa_rsp.remain_assoc_len = cpu_to_le16(1);
+		rlaa_rsp.assoc_fragment[0] = 0x42;
+		memset(rlaa_rsp.assoc_fragment + 1, 0,
+					sizeof(rlaa_rsp.assoc_fragment) - 1);
+		cmd_complete(btdev, opcode, &rlaa_rsp, sizeof(rlaa_rsp));
+		break;
+
+	case BT_HCI_CMD_GET_MWS_TRANSPORT_CONFIG:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		gmtc = alloca(sizeof(*gmtc));
+		gmtc->status = BT_HCI_ERR_SUCCESS;
+		gmtc->num_transports = 0x00;
+		cmd_complete(btdev, opcode, gmtc, sizeof(*gmtc));
+		break;
+
+	case BT_HCI_CMD_SET_EVENT_MASK_PAGE2:
+		if (btdev->type != BTDEV_TYPE_BREDRLE)
+			goto unsupported;
+		semp2 = data;
+		memcpy(btdev->event_mask_page2, semp2->mask, 8);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_SET_EVENT_MASK:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lsem = data;
+		memcpy(btdev->le_event_mask, lsem->mask, 8);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_READ_BUFFER_SIZE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lrbs.status = BT_HCI_ERR_SUCCESS;
+		lrbs.le_mtu = cpu_to_le16(btdev->acl_mtu);
+		lrbs.le_max_pkt = btdev->acl_max_pkt;
+		cmd_complete(btdev, opcode, &lrbs, sizeof(lrbs));
+		break;
+
+	case BT_HCI_CMD_LE_READ_LOCAL_FEATURES:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lrlf.status = BT_HCI_ERR_SUCCESS;
+		memcpy(lrlf.features, btdev->le_features, 8);
+		cmd_complete(btdev, opcode, &lrlf, sizeof(lrlf));
+		break;
+
+	case BT_HCI_CMD_LE_SET_RANDOM_ADDRESS:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lsra = data;
+		memcpy(btdev->random_addr, lsra->addr, 6);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_SET_ADV_PARAMETERS:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+
+		if (btdev->le_adv_enable) {
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+			cmd_complete(btdev, opcode, &status, sizeof(status));
+			break;
+		}
+
+		lsap = data;
+		btdev->le_adv_type = lsap->type;
+		btdev->le_adv_own_addr = lsap->own_addr_type;
+		btdev->le_adv_direct_addr_type = lsap->direct_addr_type;
+		memcpy(btdev->le_adv_direct_addr, lsap->direct_addr, 6);
+
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_READ_ADV_TX_POWER:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lratp.status = BT_HCI_ERR_SUCCESS;
+		lratp.level = 0;
+		cmd_complete(btdev, opcode, &lratp, sizeof(lratp));
+		break;
+
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lsae = data;
+		if (btdev->le_adv_enable == lsae->enable)
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		else {
+			btdev->le_adv_enable = lsae->enable;
+			status = BT_HCI_ERR_SUCCESS;
+		}
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		if (status == BT_HCI_ERR_SUCCESS && btdev->le_adv_enable)
+			le_set_adv_enable_complete(btdev);
+		break;
+
+	case BT_HCI_CMD_LE_SET_SCAN_PARAMETERS:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+
+		lssp = data;
+
+		if (btdev->le_scan_enable)
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		else {
+			status = BT_HCI_ERR_SUCCESS;
+			btdev->le_scan_type = lssp->type;
+			btdev->le_scan_own_addr_type = lssp->own_addr_type;
+		}
+
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lsse = data;
+		if (btdev->le_scan_enable == lsse->enable)
+			status = BT_HCI_ERR_COMMAND_DISALLOWED;
+		else {
+			btdev->le_scan_enable = lsse->enable;
+			btdev->le_filter_dup = lsse->filter_dup;
+			status = BT_HCI_ERR_SUCCESS;
+		}
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_CREATE_CONN:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lrwls.status = BT_HCI_ERR_SUCCESS;
+		lrwls.size = 0;
+		cmd_complete(btdev, opcode, &lrwls, sizeof(lrwls));
+		break;
+
+	case BT_HCI_CMD_LE_CONN_UPDATE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
+	case BT_HCI_CMD_LE_CLEAR_WHITE_LIST:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_ENCRYPT:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lenc_cmd = data;
+		if (!bt_crypto_e(btdev->crypto, lenc_cmd->key,
+				 lenc_cmd->plaintext, lenc.data)) {
+			cmd_status(btdev, BT_HCI_ERR_COMMAND_DISALLOWED,
+				   opcode);
+			break;
+		}
+		lenc.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &lenc, sizeof(lenc));
+		break;
+
+	case BT_HCI_CMD_LE_RAND:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		if (!bt_crypto_random_bytes(btdev->crypto,
+					    (uint8_t *)&lr.number, 8)) {
+			cmd_status(btdev, BT_HCI_ERR_COMMAND_DISALLOWED,
+				   opcode);
+			break;
+		}
+		lr.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &lr, sizeof(lr));
+		break;
+
+	case BT_HCI_CMD_LE_READ_LOCAL_PK256:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		if (!ecc_make_key(pk_evt.local_pk256, btdev->le_local_sk256)) {
+			cmd_status(btdev, BT_HCI_ERR_COMMAND_DISALLOWED,
+									opcode);
+			break;
+		}
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS,
+						BT_HCI_CMD_LE_READ_LOCAL_PK256);
+		pk_evt.status = BT_HCI_ERR_SUCCESS;
+		le_meta_event(btdev, BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE,
+						&pk_evt, sizeof(pk_evt));
+		break;
+
+	case BT_HCI_CMD_LE_GENERATE_DHKEY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		dh = data;
+		if (!ecdh_shared_secret(dh->remote_pk256, btdev->le_local_sk256,
+								dh_evt.dhkey)) {
+			cmd_status(btdev, BT_HCI_ERR_COMMAND_DISALLOWED,
+									opcode);
+			break;
+		}
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS,
+						BT_HCI_CMD_LE_GENERATE_DHKEY);
+		le_meta_event(btdev, BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE,
+						&dh_evt, sizeof(dh_evt));
+		break;
+
+	case BT_HCI_CMD_LE_READ_SUPPORTED_STATES:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lrss.status = BT_HCI_ERR_SUCCESS;
+		memcpy(lrss.states, btdev->le_states, 8);
+		cmd_complete(btdev, opcode, &lrss, sizeof(lrss));
+		break;
+
+	case BT_HCI_CMD_LE_SET_ADV_DATA:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lsad = data;
+		btdev->le_adv_data_len = lsad->len;
+		memcpy(btdev->le_adv_data, lsad->data, 31);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_SET_SCAN_RSP_DATA:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lssrd = data;
+		btdev->le_scan_data_len = lssrd->len;
+		memcpy(btdev->le_scan_data, lssrd->data, 31);
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_READ_REMOTE_FEATURES:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		le_read_remote_features_complete(btdev);
+		break;
+
+	case BT_HCI_CMD_LE_START_ENCRYPT:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lse = data;
+		memcpy(btdev->le_ltk, lse->ltk, 16);
+		le_start_encrypt_complete(btdev, lse->ediv, lse->rand);
+		break;
+
+	case BT_HCI_CMD_LE_LTK_REQ_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		llrr = data;
+		memcpy(btdev->le_ltk, llrr->ltk, 16);
+		le_encrypt_complete(btdev);
+		break;
+
+	case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		ltk_neg_reply_complete(btdev);
+		break;
+
+	case BT_HCI_CMD_SETUP_SYNC_CONN:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		ssc = data;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		sync_conn_complete(btdev, ssc->voice_setting,
+							BT_HCI_ERR_SUCCESS);
+		break;
+
+	case BT_HCI_CMD_ADD_SCO_CONN:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		sco_conn_complete(btdev, BT_HCI_ERR_SUCCESS);
+		break;
+
+	case BT_HCI_CMD_ENABLE_DUT_MODE:
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_WRITE_SSP_DEBUG_MODE:
+		if (btdev->type == BTDEV_TYPE_LE)
+			goto unsupported;
+		wsdm = data;
+		btdev->ssp_debug_mode = wsdm->mode;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_RECEIVER_TEST:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_TRANSMITTER_TEST:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &status, sizeof(status));
+		break;
+
+	case BT_HCI_CMD_LE_TEST_END:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lte.status = BT_HCI_ERR_SUCCESS;
+		lte.num_packets = 0;
+		cmd_complete(btdev, opcode, &lte, sizeof(lte));
+		break;
+
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lcprr_cmd = data;
+		lcprr_rsp.handle = lcprr_cmd->handle;
+		lcprr_rsp.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &lcprr_rsp, sizeof(lcprr_rsp));
+		break;
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			goto unsupported;
+		lcprnr_cmd = data;
+		lcprnr_rsp.handle = lcprnr_cmd->handle;
+		lcprnr_rsp.status = BT_HCI_ERR_SUCCESS;
+		cmd_complete(btdev, opcode, &lcprnr_rsp, sizeof(lcprnr_rsp));
+		break;
+	default:
+		goto unsupported;
+	}
+
+	return;
+
+unsupported:
+	printf("Unsupported command 0x%4.4x\n", opcode);
+	hexdump(data, len);
+	cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+}
+
+static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
+						const void *data, uint8_t len)
+{
+	const struct bt_hci_cmd_create_conn *cc;
+	const struct bt_hci_cmd_disconnect *dc;
+	const struct bt_hci_cmd_create_conn_cancel *ccc;
+	const struct bt_hci_cmd_accept_conn_request *acr;
+	const struct bt_hci_cmd_reject_conn_request *rcr;
+	const struct bt_hci_cmd_auth_requested *ar;
+	const struct bt_hci_cmd_set_conn_encrypt *sce;
+	const struct bt_hci_cmd_link_key_request_reply *lkrr;
+	const struct bt_hci_cmd_link_key_request_neg_reply *lkrnr;
+	const struct bt_hci_cmd_pin_code_request_neg_reply *pcrnr;
+	const struct bt_hci_cmd_pin_code_request_reply *pcrr;
+	const struct bt_hci_cmd_remote_name_request *rnr;
+	const struct bt_hci_cmd_remote_name_request_cancel *rnrc;
+	const struct bt_hci_cmd_read_remote_features *rrf;
+	const struct bt_hci_cmd_read_remote_ext_features *rref;
+	const struct bt_hci_cmd_read_remote_version *rrv;
+	const struct bt_hci_cmd_read_clock_offset *rco;
+	const struct bt_hci_cmd_le_create_conn *lecc;
+	const struct bt_hci_cmd_le_conn_update *lecu;
+	const struct bt_hci_cmd_le_conn_param_req_reply *lcprr;
+	const struct bt_hci_cmd_le_conn_param_req_neg_reply *lcprnr;
+	const struct bt_hci_cmd_le_set_scan_enable *lsse;
+
+	switch (opcode) {
+	case BT_HCI_CMD_INQUIRY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		inquiry_cmd(btdev, data);
+		break;
+
+	case BT_HCI_CMD_CREATE_CONN:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		cc = data;
+		conn_request(btdev, cc->bdaddr);
+		break;
+
+	case BT_HCI_CMD_DISCONNECT:
+		dc = data;
+		disconnect_complete(btdev, le16_to_cpu(dc->handle), dc->reason);
+		break;
+
+	case BT_HCI_CMD_CREATE_CONN_CANCEL:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		ccc = data;
+		conn_complete(btdev, ccc->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+		break;
+
+	case BT_HCI_CMD_ACCEPT_CONN_REQUEST:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		acr = data;
+		accept_conn_request_complete(btdev, acr->bdaddr);
+		break;
+
+	case BT_HCI_CMD_REJECT_CONN_REQUEST:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		rcr = data;
+		conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+		break;
+
+	case BT_HCI_CMD_LINK_KEY_REQUEST_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		lkrr = data;
+		link_key_req_reply_complete(btdev, lkrr->bdaddr, lkrr->link_key);
+		break;
+
+	case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		lkrnr = data;
+		link_key_req_neg_reply_complete(btdev, lkrnr->bdaddr);
+		break;
+
+	case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		pcrr = data;
+		pin_code_req_reply_complete(btdev, pcrr->bdaddr, pcrr->pin_len,
+							pcrr->pin_code);
+		break;
+
+	case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		pcrnr = data;
+		pin_code_req_neg_reply_complete(btdev, pcrnr->bdaddr);
+		break;
+
+	case BT_HCI_CMD_AUTH_REQUESTED:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		ar = data;
+		auth_request_complete(btdev, le16_to_cpu(ar->handle));
+		break;
+
+	case BT_HCI_CMD_SET_CONN_ENCRYPT:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		sce = data;
+		if (btdev->conn) {
+			uint8_t mode;
+
+			if (!sce->encr_mode)
+				mode = 0x00;
+			else if (btdev->secure_conn_support &&
+					btdev->conn->secure_conn_support)
+				mode = 0x02;
+			else
+				mode = 0x01;
+
+			encrypt_change(btdev, mode, BT_HCI_ERR_SUCCESS);
+			encrypt_change(btdev->conn, mode, BT_HCI_ERR_SUCCESS);
+		}
+		break;
+
+	case BT_HCI_CMD_REMOTE_NAME_REQUEST:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		rnr = data;
+		name_request_complete(btdev, rnr->bdaddr, BT_HCI_ERR_SUCCESS);
+		break;
+
+	case BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		rnrc = data;
+		name_request_complete(btdev, rnrc->bdaddr,
+						BT_HCI_ERR_UNKNOWN_CONN_ID);
+		break;
+
+	case BT_HCI_CMD_READ_REMOTE_FEATURES:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		rrf = data;
+		remote_features_complete(btdev, le16_to_cpu(rrf->handle));
+		break;
+
+	case BT_HCI_CMD_READ_REMOTE_EXT_FEATURES:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		rref = data;
+		remote_ext_features_complete(btdev, le16_to_cpu(rref->handle),
+								rref->page);
+		break;
+
+	case BT_HCI_CMD_READ_REMOTE_VERSION:
+		rrv = data;
+		remote_version_complete(btdev, le16_to_cpu(rrv->handle));
+		break;
+
+	case BT_HCI_CMD_READ_CLOCK_OFFSET:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		rco = data;
+		remote_clock_offset_complete(btdev, le16_to_cpu(rco->handle));
+		break;
+
+	case BT_HCI_CMD_LE_CREATE_CONN:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lecc = data;
+		btdev->le_scan_own_addr_type = lecc->own_addr_type;
+		le_conn_request(btdev, lecc);
+		break;
+
+	case BT_HCI_CMD_LE_CONN_UPDATE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lecu = data;
+		if (btdev->le_features[0] & 0x02)
+			le_conn_param_req(btdev, le16_to_cpu(lecu->handle),
+					le16_to_cpu(lecu->min_interval),
+					le16_to_cpu(lecu->max_interval),
+					le16_to_cpu(lecu->latency),
+					le16_to_cpu(lecu->supv_timeout),
+					le16_to_cpu(lecu->min_length),
+					le16_to_cpu(lecu->max_length));
+		else
+			le_conn_update(btdev, le16_to_cpu(lecu->handle),
+					le16_to_cpu(lecu->min_interval),
+					le16_to_cpu(lecu->max_interval),
+					le16_to_cpu(lecu->latency),
+					le16_to_cpu(lecu->supv_timeout),
+					le16_to_cpu(lecu->min_length),
+					le16_to_cpu(lecu->max_length));
+		break;
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lcprr = data;
+		le_conn_update(btdev, le16_to_cpu(lcprr->handle),
+				le16_to_cpu(lcprr->min_interval),
+				le16_to_cpu(lcprr->max_interval),
+				le16_to_cpu(lcprr->latency),
+				le16_to_cpu(lcprr->supv_timeout),
+				le16_to_cpu(lcprr->min_length),
+				le16_to_cpu(lcprr->max_length));
+		break;
+	case BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lcprnr = data;
+		rej_le_conn_update(btdev, le16_to_cpu(lcprnr->handle),
+					le16_to_cpu(lcprnr->reason));
+		break;
+		break;
+	case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+		if (btdev->type == BTDEV_TYPE_BREDR)
+			return;
+		lsse = data;
+		if (btdev->le_scan_enable && lsse->enable)
+			le_set_scan_enable_complete(btdev);
+
+	}
+}
+
+struct btdev_callback {
+	void (*function)(btdev_callback callback, uint8_t response,
+				uint8_t status, const void *data, uint8_t len);
+	void *user_data;
+	uint16_t opcode;
+	const void *data;
+	uint8_t len;
+};
+
+void btdev_command_response(btdev_callback callback, uint8_t response,
+                                uint8_t status, const void *data, uint8_t len)
+{
+	callback->function(callback, response, status, data, len);
+}
+
+static void handler_callback(btdev_callback callback, uint8_t response,
+				uint8_t status, const void *data, uint8_t len)
+{
+	struct btdev *btdev = callback->user_data;
+
+	switch (response) {
+	case BTDEV_RESPONSE_DEFAULT:
+		if (!run_hooks(btdev, BTDEV_HOOK_PRE_CMD, callback->opcode,
+						callback->data, callback->len))
+			return;
+		default_cmd(btdev, callback->opcode,
+					callback->data, callback->len);
+
+		if (!run_hooks(btdev, BTDEV_HOOK_PRE_EVT, callback->opcode,
+						callback->data, callback->len))
+			return;
+		default_cmd_completion(btdev, callback->opcode,
+					callback->data, callback->len);
+		break;
+	case BTDEV_RESPONSE_COMMAND_STATUS:
+		cmd_status(btdev, status, callback->opcode);
+		break;
+	case BTDEV_RESPONSE_COMMAND_COMPLETE:
+		cmd_complete(btdev, callback->opcode, data, len);
+		break;
+	default:
+		cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND,
+						callback->opcode);
+		break;
+	}
+}
+
+static void process_cmd(struct btdev *btdev, const void *data, uint16_t len)
+{
+	struct btdev_callback callback;
+	const struct bt_hci_cmd_hdr *hdr = data;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	callback.function = handler_callback;
+	callback.user_data = btdev;
+	callback.opcode = le16_to_cpu(hdr->opcode);
+	callback.data = data + sizeof(*hdr);
+	callback.len = hdr->plen;
+
+	if (btdev->command_handler)
+		btdev->command_handler(callback.opcode,
+					callback.data, callback.len,
+					&callback, btdev->command_data);
+	else {
+		if (!run_hooks(btdev, BTDEV_HOOK_PRE_CMD, callback.opcode,
+						callback.data, callback.len))
+			return;
+		default_cmd(btdev, callback.opcode,
+					callback.data, callback.len);
+
+		if (!run_hooks(btdev, BTDEV_HOOK_PRE_EVT, callback.opcode,
+						callback.data, callback.len))
+			return;
+		default_cmd_completion(btdev, callback.opcode,
+					callback.data, callback.len);
+	}
+}
+
+static void send_acl(struct btdev *conn, const void *data, uint16_t len)
+{
+	struct bt_hci_acl_hdr hdr;
+	struct iovec iov[3];
+
+	/* Packet type */
+	iov[0].iov_base = (void *) data;
+	iov[0].iov_len = 1;
+
+	/* ACL_START_NO_FLUSH is only allowed from host to controller.
+	 * From controller to host this should be converted to ACL_START.
+	 */
+	memcpy(&hdr, data + 1, sizeof(hdr));
+	if (acl_flags(hdr.handle) == ACL_START_NO_FLUSH)
+		hdr.handle = acl_handle_pack(acl_handle(hdr.handle), ACL_START);
+
+	iov[1].iov_base = &hdr;
+	iov[1].iov_len = sizeof(hdr);
+
+	iov[2].iov_base = (void *) (data + 1 + sizeof(hdr));
+	iov[2].iov_len = len - 1 - sizeof(hdr);
+
+	send_packet(conn, iov, 3);
+}
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len)
+{
+	uint8_t pkt_type;
+
+	if (!btdev)
+		return;
+
+	if (len < 1)
+		return;
+
+	pkt_type = ((const uint8_t *) data)[0];
+
+	switch (pkt_type) {
+	case BT_H4_CMD_PKT:
+		process_cmd(btdev, data + 1, len - 1);
+		break;
+	case BT_H4_ACL_PKT:
+		if (btdev->conn)
+			send_acl(btdev->conn, data, len);
+		num_completed_packets(btdev);
+		break;
+	default:
+		printf("Unsupported packet 0x%2.2x\n", pkt_type);
+		break;
+	}
+}
+
+int btdev_add_hook(struct btdev *btdev, enum btdev_hook_type type,
+				uint16_t opcode, btdev_hook_func handler,
+				void *user_data)
+{
+	int i;
+
+	if (!btdev)
+		return -1;
+
+	if (get_hook_index(btdev, type, opcode) > 0)
+		return -1;
+
+	for (i = 0; i < MAX_HOOK_ENTRIES; i++) {
+		if (btdev->hook_list[i] == NULL) {
+			btdev->hook_list[i] = malloc(sizeof(struct hook));
+			if (btdev->hook_list[i] == NULL)
+				return -1;
+
+			btdev->hook_list[i]->handler = handler;
+			btdev->hook_list[i]->user_data = user_data;
+			btdev->hook_list[i]->opcode = opcode;
+			btdev->hook_list[i]->type = type;
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+bool btdev_del_hook(struct btdev *btdev, enum btdev_hook_type type,
+								uint16_t opcode)
+{
+	int i;
+
+	if (!btdev)
+		return false;
+
+	for (i = 0; i < MAX_HOOK_ENTRIES; i++) {
+		if (btdev->hook_list[i] == NULL)
+			continue;
+
+		if (btdev->hook_list[i]->type != type ||
+					btdev->hook_list[i]->opcode != opcode)
+			continue;
+
+		free(btdev->hook_list[i]);
+		btdev->hook_list[i] = NULL;
+
+		return true;
+	}
+
+	return false;
+}
diff --git a/bluez/repo/emulator/btdev.h b/bluez/repo/emulator/btdev.h
new file mode 100644
index 0000000..40c7219
--- /dev/null
+++ b/bluez/repo/emulator/btdev.h
@@ -0,0 +1,100 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+
+#define BTDEV_RESPONSE_DEFAULT		0
+#define BTDEV_RESPONSE_COMMAND_STATUS	1
+#define BTDEV_RESPONSE_COMMAND_COMPLETE	2
+
+typedef struct btdev_callback * btdev_callback;
+
+void btdev_command_response(btdev_callback callback, uint8_t response,
+				uint8_t status, const void *data, uint8_t len);
+
+#define btdev_command_default(callback) \
+		btdev_command_response(callback, \
+			BTDEV_RESPONSE_DEFAULT, 0x00, NULL, 0);
+
+#define btdev_command_status(callback, status) \
+		btdev_command_response(callback, \
+			BTDEV_RESPONSE_COMMAND_STATUS, status, NULL, 0);
+
+#define btdev_command_complete(callback, data, len) \
+		 btdev_command_response(callback, \
+			BTDEV_RESPONSE_COMMAND_COMPLETE, 0x00, data, len);
+
+
+typedef void (*btdev_command_func) (uint16_t opcode,
+				const void *data, uint8_t len,
+				btdev_callback callback, void *user_data);
+
+typedef void (*btdev_send_func) (const struct iovec *iov, int iovlen,
+							void *user_data);
+
+typedef bool (*btdev_hook_func) (const void *data, uint16_t len,
+							void *user_data);
+
+enum btdev_type {
+	BTDEV_TYPE_BREDRLE,
+	BTDEV_TYPE_BREDR,
+	BTDEV_TYPE_LE,
+	BTDEV_TYPE_AMP,
+	BTDEV_TYPE_BREDR20,
+};
+
+enum btdev_hook_type {
+	BTDEV_HOOK_PRE_CMD,
+	BTDEV_HOOK_POST_CMD,
+	BTDEV_HOOK_PRE_EVT,
+	BTDEV_HOOK_POST_EVT,
+};
+
+struct btdev;
+
+struct btdev *btdev_create(enum btdev_type type, uint16_t id);
+void btdev_destroy(struct btdev *btdev);
+
+const uint8_t *btdev_get_bdaddr(struct btdev *btdev);
+uint8_t *btdev_get_features(struct btdev *btdev);
+
+uint8_t btdev_get_scan_enable(struct btdev *btdev);
+
+uint8_t btdev_get_le_scan_enable(struct btdev *btdev);
+
+void btdev_set_command_handler(struct btdev *btdev, btdev_command_func handler,
+							void *user_data);
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+							void *user_data);
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len);
+
+int btdev_add_hook(struct btdev *btdev, enum btdev_hook_type type,
+				uint16_t opcode, btdev_hook_func handler,
+				void *user_data);
+
+bool btdev_del_hook(struct btdev *btdev, enum btdev_hook_type type,
+							uint16_t opcode);
diff --git a/bluez/repo/emulator/bthost.c b/bluez/repo/emulator/bthost.c
new file mode 100644
index 0000000..3638fe4
--- /dev/null
+++ b/bluez/repo/emulator/bthost.c
@@ -0,0 +1,2582 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <endian.h>
+#include <stdbool.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "monitor/bt.h"
+#include "monitor/rfcomm.h"
+#include "bthost.h"
+
+#define lmp_bredr_capable(bthost)     (!((bthost)->features[4] & 0x20))
+
+/* ACL handle and flags pack/unpack */
+#define acl_handle_pack(h, f)	(uint16_t)((h & 0x0fff)|(f << 12))
+#define acl_handle(h)		(h & 0x0fff)
+#define acl_flags(h)		(h >> 12)
+
+#define L2CAP_FEAT_FIXED_CHAN	0x00000080
+#define L2CAP_FC_SIG_BREDR	0x02
+#define L2CAP_FC_SMP_BREDR	0x80
+#define L2CAP_IT_FEAT_MASK	0x0002
+#define L2CAP_IT_FIXED_CHAN	0x0003
+
+/* RFCOMM setters */
+#define RFCOMM_ADDR(cr, dlci)	(((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
+#define RFCOMM_CTRL(type, pf)	(((type & 0xef) | (pf << 4)))
+#define RFCOMM_LEN8(len)	(((len) << 1) | 1)
+#define RFCOMM_LEN16(len)	((len) << 1)
+#define RFCOMM_MCC_TYPE(cr, type)	(((type << 2) | (cr << 1) | 0x01))
+
+/* RFCOMM FCS calculation */
+#define CRC(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
+
+static unsigned char rfcomm_crc_table[256] = {
+	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
+	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
+	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
+	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+
+	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+
+	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+
+	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+
+	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+
+	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+
+	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+
+	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
+};
+
+static uint8_t rfcomm_fcs2(uint8_t *data)
+{
+	return 0xff - rfcomm_crc_table[CRC(data) ^ data[2]];
+}
+
+static uint8_t rfcomm_fcs(uint8_t *data)
+{
+	return 0xff - CRC(data);
+}
+
+struct cmd {
+	struct cmd *next;
+	struct cmd *prev;
+	uint8_t data[256 + sizeof(struct bt_hci_cmd_hdr)];
+	uint16_t len;
+};
+
+struct cmd_queue {
+	struct cmd *head;
+	struct cmd *tail;
+};
+
+struct cid_hook {
+	uint16_t cid;
+	bthost_cid_hook_func_t func;
+	void *user_data;
+	struct cid_hook *next;
+};
+
+struct rfcomm_chan_hook {
+	uint8_t channel;
+	bthost_rfcomm_chan_hook_func_t func;
+	void *user_data;
+	struct rfcomm_chan_hook *next;
+};
+
+struct btconn {
+	uint16_t handle;
+	uint8_t bdaddr[6];
+	uint8_t addr_type;
+	uint8_t encr_mode;
+	uint16_t next_cid;
+	uint64_t fixed_chan;
+	struct l2conn *l2conns;
+	struct rcconn *rcconns;
+	struct cid_hook *cid_hooks;
+	struct rfcomm_chan_hook *rfcomm_chan_hooks;
+	struct btconn *next;
+	void *smp_data;
+};
+
+struct l2conn {
+	uint16_t scid;
+	uint16_t dcid;
+	uint16_t psm;
+	struct l2conn *next;
+};
+
+struct rcconn {
+	uint8_t channel;
+	uint16_t scid;
+	struct rcconn *next;
+};
+
+struct l2cap_pending_req {
+	uint8_t ident;
+	bthost_l2cap_rsp_cb cb;
+	void *user_data;
+	struct l2cap_pending_req *next;
+};
+
+struct l2cap_conn_cb_data {
+	uint16_t psm;
+	bthost_l2cap_connect_cb func;
+	void *user_data;
+	struct l2cap_conn_cb_data *next;
+};
+
+struct rfcomm_conn_cb_data {
+	uint8_t channel;
+	bthost_rfcomm_connect_cb func;
+	void *user_data;
+	struct rfcomm_conn_cb_data *next;
+};
+
+struct rfcomm_connection_data {
+	uint8_t channel;
+	struct btconn *conn;
+	bthost_rfcomm_connect_cb cb;
+	void *user_data;
+};
+
+struct bthost {
+	bool ready;
+	bthost_ready_cb ready_cb;
+	uint8_t bdaddr[6];
+	uint8_t features[8];
+	bthost_send_func send_handler;
+	void *send_data;
+	struct cmd_queue cmd_q;
+	uint8_t ncmd;
+	struct btconn *conns;
+	bthost_cmd_complete_cb cmd_complete_cb;
+	void *cmd_complete_data;
+	bthost_new_conn_cb new_conn_cb;
+	void *new_conn_data;
+	struct rfcomm_connection_data *rfcomm_conn_data;
+	struct l2cap_conn_cb_data *new_l2cap_conn_data;
+	struct rfcomm_conn_cb_data *new_rfcomm_conn_data;
+	struct l2cap_pending_req *l2reqs;
+	uint8_t pin[16];
+	uint8_t pin_len;
+	uint8_t io_capability;
+	uint8_t auth_req;
+	bool reject_user_confirm;
+	void *smp_data;
+	bool conn_init;
+	bool le;
+	bool sc;
+};
+
+struct bthost *bthost_create(void)
+{
+	struct bthost *bthost;
+
+	bthost = new0(struct bthost, 1);
+	if (!bthost)
+		return NULL;
+
+	bthost->smp_data = smp_start(bthost);
+	if (!bthost->smp_data) {
+		free(bthost);
+		return NULL;
+	}
+
+	/* Set defaults */
+	bthost->io_capability = 0x03;
+
+	return bthost;
+}
+
+static void l2conn_free(struct l2conn *conn)
+{
+	free(conn);
+}
+
+static void btconn_free(struct btconn *conn)
+{
+	if (conn->smp_data)
+		smp_conn_del(conn->smp_data);
+
+	while (conn->l2conns) {
+		struct l2conn *l2conn = conn->l2conns;
+
+		conn->l2conns = l2conn->next;
+		l2conn_free(l2conn);
+	}
+
+	while (conn->cid_hooks) {
+		struct cid_hook *hook = conn->cid_hooks;
+
+		conn->cid_hooks = hook->next;
+		free(hook);
+	}
+
+	while (conn->rcconns) {
+		struct rcconn *rcconn = conn->rcconns;
+
+		conn->rcconns = rcconn->next;
+		free(rcconn);
+	}
+
+	while (conn->rfcomm_chan_hooks) {
+		struct rfcomm_chan_hook *hook = conn->rfcomm_chan_hooks;
+
+		conn->rfcomm_chan_hooks = hook->next;
+		free(hook);
+	}
+
+	free(conn);
+}
+
+static struct btconn *bthost_find_conn(struct bthost *bthost, uint16_t handle)
+{
+	struct btconn *conn;
+
+	for (conn = bthost->conns; conn != NULL; conn = conn->next) {
+		if (conn->handle == handle)
+			return conn;
+	}
+
+	return NULL;
+}
+
+static struct btconn *bthost_find_conn_by_bdaddr(struct bthost *bthost,
+							const uint8_t *bdaddr)
+{
+	struct btconn *conn;
+
+	for (conn = bthost->conns; conn != NULL; conn = conn->next) {
+		if (!memcmp(conn->bdaddr, bdaddr, 6))
+			return conn;
+	}
+
+	return NULL;
+}
+
+static struct l2conn *bthost_add_l2cap_conn(struct bthost *bthost,
+						struct btconn *conn,
+						uint16_t scid, uint16_t dcid,
+						uint16_t psm)
+{
+	struct l2conn *l2conn;
+
+	l2conn = malloc(sizeof(*l2conn));
+	if (!l2conn)
+		return NULL;
+
+	memset(l2conn, 0, sizeof(*l2conn));
+
+	l2conn->psm = psm;
+	l2conn->scid = scid;
+	l2conn->dcid = dcid;
+
+	l2conn->next = conn->l2conns;
+	conn->l2conns = l2conn;
+
+	return l2conn;
+}
+
+static struct rcconn *bthost_add_rfcomm_conn(struct bthost *bthost,
+						struct btconn *conn,
+						struct l2conn *l2conn,
+						uint8_t channel)
+{
+	struct rcconn *rcconn;
+
+	rcconn = malloc(sizeof(*rcconn));
+	if (!rcconn)
+		return NULL;
+
+	memset(rcconn, 0, sizeof(*rcconn));
+
+	rcconn->channel = channel;
+	rcconn->scid = l2conn->scid;
+
+	rcconn->next = conn->rcconns;
+	conn->rcconns = rcconn;
+
+	return rcconn;
+}
+
+static struct rcconn *btconn_find_rfcomm_conn_by_channel(struct btconn *conn,
+								uint8_t chan)
+{
+	struct rcconn *rcconn;
+
+	for (rcconn = conn->rcconns; rcconn != NULL; rcconn = rcconn->next) {
+		if (rcconn->channel == chan)
+			return rcconn;
+	}
+
+	return NULL;
+}
+
+static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn,
+								uint16_t scid)
+{
+	struct l2conn *l2conn;
+
+	for (l2conn = conn->l2conns; l2conn != NULL; l2conn = l2conn->next) {
+		if (l2conn->scid == scid)
+			return l2conn;
+	}
+
+	return NULL;
+}
+
+static struct l2cap_conn_cb_data *bthost_find_l2cap_cb_by_psm(
+					struct bthost *bthost, uint16_t psm)
+{
+	struct l2cap_conn_cb_data *cb;
+
+	for (cb = bthost->new_l2cap_conn_data; cb != NULL; cb = cb->next) {
+		if (cb->psm == psm)
+			return cb;
+	}
+
+	return NULL;
+}
+
+static struct rfcomm_conn_cb_data *bthost_find_rfcomm_cb_by_channel(
+					struct bthost *bthost, uint8_t channel)
+{
+	struct rfcomm_conn_cb_data *cb;
+
+	for (cb = bthost->new_rfcomm_conn_data; cb != NULL; cb = cb->next) {
+		if (cb->channel == channel)
+			return cb;
+	}
+
+	return NULL;
+}
+
+void bthost_destroy(struct bthost *bthost)
+{
+	if (!bthost)
+		return;
+
+	while (bthost->cmd_q.tail) {
+		struct cmd *cmd = bthost->cmd_q.tail;
+
+		bthost->cmd_q.tail = cmd->prev;
+		free(cmd);
+	}
+
+	while (bthost->conns) {
+		struct btconn *conn = bthost->conns;
+
+		bthost->conns = conn->next;
+		btconn_free(conn);
+	}
+
+	while (bthost->l2reqs) {
+		struct l2cap_pending_req *req = bthost->l2reqs;
+
+		bthost->l2reqs = req->next;
+		req->cb(0, NULL, 0, req->user_data);
+		free(req);
+	}
+
+	while (bthost->new_l2cap_conn_data) {
+		struct l2cap_conn_cb_data *cb = bthost->new_l2cap_conn_data;
+
+		bthost->new_l2cap_conn_data = cb->next;
+		free(cb);
+	}
+
+	while (bthost->new_rfcomm_conn_data) {
+		struct rfcomm_conn_cb_data *cb = bthost->new_rfcomm_conn_data;
+
+		bthost->new_rfcomm_conn_data = cb->next;
+		free(cb);
+	}
+
+	if (bthost->rfcomm_conn_data)
+		free(bthost->rfcomm_conn_data);
+
+	smp_stop(bthost->smp_data);
+
+	free(bthost);
+}
+
+void bthost_set_send_handler(struct bthost *bthost, bthost_send_func handler,
+							void *user_data)
+{
+	if (!bthost)
+		return;
+
+	bthost->send_handler = handler;
+	bthost->send_data = user_data;
+}
+
+static void queue_command(struct bthost *bthost, const struct iovec *iov,
+								int iovlen)
+{
+	struct cmd_queue *cmd_q = &bthost->cmd_q;
+	struct cmd *cmd;
+	int i;
+
+	cmd = malloc(sizeof(*cmd));
+	if (!cmd)
+		return;
+
+	memset(cmd, 0, sizeof(*cmd));
+
+	for (i = 0; i < iovlen; i++) {
+		memcpy(cmd->data + cmd->len, iov[i].iov_base, iov[i].iov_len);
+		cmd->len += iov[i].iov_len;
+	}
+
+	if (cmd_q->tail)
+		cmd_q->tail->next = cmd;
+	else
+		cmd_q->head = cmd;
+
+	cmd->prev = cmd_q->tail;
+	cmd_q->tail = cmd;
+}
+
+static void send_packet(struct bthost *bthost, const struct iovec *iov,
+								int iovlen)
+{
+	if (!bthost->send_handler)
+		return;
+
+	bthost->send_handler(iov, iovlen, bthost->send_data);
+}
+
+static void send_iov(struct bthost *bthost, uint16_t handle, uint16_t cid,
+					const struct iovec *iov, int iovcnt)
+{
+	struct bt_hci_acl_hdr acl_hdr;
+	struct bt_l2cap_hdr l2_hdr;
+	uint8_t pkt = BT_H4_ACL_PKT;
+	struct iovec pdu[3 + iovcnt];
+	int i, len = 0;
+
+	for (i = 0; i < iovcnt; i++) {
+		pdu[3 + i].iov_base = iov[i].iov_base;
+		pdu[3 + i].iov_len = iov[i].iov_len;
+		len += iov[i].iov_len;
+	}
+
+	pdu[0].iov_base = &pkt;
+	pdu[0].iov_len = sizeof(pkt);
+
+	acl_hdr.handle = acl_handle_pack(handle, 0);
+	acl_hdr.dlen = cpu_to_le16(len + sizeof(l2_hdr));
+
+	pdu[1].iov_base = &acl_hdr;
+	pdu[1].iov_len = sizeof(acl_hdr);
+
+	l2_hdr.cid = cpu_to_le16(cid);
+	l2_hdr.len = cpu_to_le16(len);
+
+	pdu[2].iov_base = &l2_hdr;
+	pdu[2].iov_len = sizeof(l2_hdr);
+
+	send_packet(bthost, pdu, 3 + iovcnt);
+}
+
+static void send_acl(struct bthost *bthost, uint16_t handle, uint16_t cid,
+						const void *data, uint16_t len)
+{
+	struct iovec iov;
+
+	iov.iov_base = (void *) data;
+	iov.iov_len = len;
+
+	send_iov(bthost, handle, cid, &iov, 1);
+}
+
+static uint8_t l2cap_sig_send(struct bthost *bthost, struct btconn *conn,
+					uint8_t code, uint8_t ident,
+					const void *data, uint16_t len)
+{
+	static uint8_t next_ident = 1;
+	struct bt_l2cap_hdr_sig hdr;
+	uint16_t cid;
+	struct iovec iov[2];
+
+	if (!ident) {
+		ident = next_ident++;
+		if (!ident)
+			ident = next_ident++;
+	}
+
+	hdr.code  = code;
+	hdr.ident = ident;
+	hdr.len   = cpu_to_le16(len);
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(hdr);
+
+	if (conn->addr_type == BDADDR_BREDR)
+		cid = 0x0001;
+	else
+		cid = 0x0005;
+
+	if (len == 0) {
+		send_iov(bthost, conn->handle, cid, iov, 1);
+		return ident;
+	}
+
+	iov[1].iov_base = (void *) data;
+	iov[1].iov_len = len;
+
+	send_iov(bthost, conn->handle, cid, iov, 2);
+
+	return ident;
+}
+
+void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid,
+				bthost_cid_hook_func_t func, void *user_data)
+{
+	struct cid_hook *hook;
+	struct btconn *conn;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	hook = malloc(sizeof(*hook));
+	if (!hook)
+		return;
+
+	memset(hook, 0, sizeof(*hook));
+
+	hook->cid = cid;
+	hook->func = func;
+	hook->user_data = user_data;
+
+	hook->next = conn->cid_hooks;
+	conn->cid_hooks = hook;
+}
+
+void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid,
+					const void *data, uint16_t len)
+{
+	struct btconn *conn;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	send_acl(bthost, handle, cid, data, len);
+}
+
+void bthost_send_cid_v(struct bthost *bthost, uint16_t handle, uint16_t cid,
+					const struct iovec *iov, int iovcnt)
+{
+	struct btconn *conn;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	send_iov(bthost, handle, cid, iov, iovcnt);
+}
+
+bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t code,
+				const void *data, uint16_t len,
+				bthost_l2cap_rsp_cb cb, void *user_data)
+{
+	struct l2cap_pending_req *req;
+	struct btconn *conn;
+	uint8_t ident;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return false;
+
+	if (code == BT_L2CAP_PDU_CONN_REQ &&
+			len == sizeof(struct bt_l2cap_pdu_conn_req)) {
+		const struct bt_l2cap_pdu_conn_req *req = data;
+
+		bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(req->scid),
+							le16_to_cpu(req->scid),
+							le16_to_cpu(req->psm));
+	}
+
+	ident = l2cap_sig_send(bthost, conn, code, 0, data, len);
+	if (!ident)
+		return false;
+
+	if (!cb)
+		return true;
+
+	req = malloc(sizeof(*req));
+	if (!req)
+		return false;
+
+	memset(req, 0, sizeof(*req));
+	req->ident = ident;
+	req->cb = cb;
+	req->user_data = user_data;
+
+	req->next = bthost->l2reqs;
+	bthost->l2reqs = req;
+
+	return true;
+}
+
+static void send_command(struct bthost *bthost, uint16_t opcode,
+						const void *data, uint8_t len)
+{
+	struct bt_hci_cmd_hdr hdr;
+	uint8_t pkt = BT_H4_CMD_PKT;
+	struct iovec iov[3];
+
+	iov[0].iov_base = &pkt;
+	iov[0].iov_len = sizeof(pkt);
+
+	hdr.opcode = cpu_to_le16(opcode);
+	hdr.plen = len;
+
+	iov[1].iov_base = &hdr;
+	iov[1].iov_len = sizeof(hdr);
+
+	if (len > 0) {
+		iov[2].iov_base = (void *) data;
+		iov[2].iov_len = len;
+	}
+
+	if (bthost->ncmd) {
+		send_packet(bthost, iov, len > 0 ? 3 : 2);
+		bthost->ncmd--;
+	} else {
+		queue_command(bthost, iov, len > 0 ? 3 : 2);
+	}
+}
+
+static void next_cmd(struct bthost *bthost)
+{
+	struct cmd_queue *cmd_q = &bthost->cmd_q;
+	struct cmd *cmd = cmd_q->head;
+	struct cmd *next;
+	struct iovec iov;
+
+	if (!cmd)
+		return;
+
+	next = cmd->next;
+
+	if (!bthost->ncmd)
+		return;
+
+	iov.iov_base = cmd->data;
+	iov.iov_len = cmd->len;
+
+	send_packet(bthost, &iov, 1);
+	bthost->ncmd--;
+
+	if (next)
+		next->prev = NULL;
+	else
+		cmd_q->tail = NULL;
+
+	cmd_q->head = next;
+
+	free(cmd);
+}
+
+static void read_bd_addr_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_rsp_read_bd_addr *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	memcpy(bthost->bdaddr, ev->bdaddr, 6);
+
+	bthost->ready = true;
+
+	if (bthost->ready_cb) {
+		bthost->ready_cb();
+		bthost->ready_cb = NULL;
+	}
+}
+
+void bthost_notify_ready(struct bthost *bthost, bthost_ready_cb cb)
+{
+	if (bthost->ready) {
+		cb();
+		return;
+	}
+
+	bthost->ready_cb = cb;
+}
+
+static void read_local_features_complete(struct bthost *bthost,
+						const void *data, uint8_t len)
+{
+	const struct bt_hci_rsp_read_local_features *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	memcpy(bthost->features, ev->features, 8);
+}
+
+static void evt_cmd_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_cmd_complete *ev = data;
+	const void *param;
+	uint16_t opcode;
+
+	if (len < sizeof(*ev))
+		return;
+
+	param = data + sizeof(*ev);
+
+	bthost->ncmd = ev->ncmd;
+
+	opcode = le16toh(ev->opcode);
+
+	switch (opcode) {
+	case BT_HCI_CMD_RESET:
+		break;
+	case BT_HCI_CMD_READ_LOCAL_FEATURES:
+		read_local_features_complete(bthost, param, len - sizeof(*ev));
+		break;
+	case BT_HCI_CMD_READ_BD_ADDR:
+		read_bd_addr_complete(bthost, param, len - sizeof(*ev));
+		break;
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+		break;
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		break;
+	case BT_HCI_CMD_LE_SET_ADV_PARAMETERS:
+		break;
+	case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
+		break;
+	case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
+		break;
+	case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
+		break;
+	case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+		break;
+	case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
+		break;
+	case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT:
+		break;
+	case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY:
+		break;
+	case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY:
+		break;
+	case BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY:
+		break;
+	case BT_HCI_CMD_LE_LTK_REQ_REPLY:
+		break;
+	case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY:
+		break;
+	case BT_HCI_CMD_LE_SET_ADV_DATA:
+		break;
+	default:
+		printf("Unhandled cmd_complete opcode 0x%04x\n", opcode);
+		break;
+	}
+
+	if (bthost->cmd_complete_cb)
+		bthost->cmd_complete_cb(opcode, 0, param, len - sizeof(*ev),
+						bthost->cmd_complete_data);
+
+	next_cmd(bthost);
+}
+
+static void evt_cmd_status(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_cmd_status *ev = data;
+	uint16_t opcode;
+
+	if (len < sizeof(*ev))
+		return;
+
+	bthost->ncmd = ev->ncmd;
+
+	opcode = le16toh(ev->opcode);
+
+	if (ev->status && bthost->cmd_complete_cb)
+		bthost->cmd_complete_cb(opcode, ev->status, NULL, 0,
+						bthost->cmd_complete_data);
+
+	next_cmd(bthost);
+}
+
+static void evt_conn_request(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_conn_request *ev = data;
+	struct bt_hci_cmd_accept_conn_request cmd;
+
+	if (len < sizeof(*ev))
+		return;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memcpy(cmd.bdaddr, ev->bdaddr, sizeof(ev->bdaddr));
+
+	send_command(bthost, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd,
+								sizeof(cmd));
+}
+
+static void init_conn(struct bthost *bthost, uint16_t handle,
+				const uint8_t *bdaddr, uint8_t addr_type)
+{
+	struct btconn *conn;
+	const uint8_t *ia, *ra;
+
+	conn = malloc(sizeof(*conn));
+	if (!conn)
+		return;
+
+	memset(conn, 0, sizeof(*conn));
+	conn->handle = handle;
+	memcpy(conn->bdaddr, bdaddr, 6);
+	conn->addr_type = addr_type;
+	conn->next_cid = 0x0040;
+
+	conn->next = bthost->conns;
+	bthost->conns = conn;
+
+	if (bthost->conn_init) {
+		ia = bthost->bdaddr;
+		ra = conn->bdaddr;
+	} else {
+		ia = conn->bdaddr;
+		ra = bthost->bdaddr;
+	}
+
+	conn->smp_data = smp_conn_add(bthost->smp_data, handle, ia, ra,
+						addr_type, bthost->conn_init);
+
+	if (bthost->new_conn_cb)
+		bthost->new_conn_cb(conn->handle, bthost->new_conn_data);
+
+	if (addr_type == BDADDR_BREDR) {
+		struct bt_l2cap_pdu_info_req req;
+		req.type = L2CAP_IT_FIXED_CHAN;
+		l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_REQ, 1,
+							&req, sizeof(req));
+	}
+}
+
+static void evt_conn_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_conn_complete *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	init_conn(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR);
+}
+
+static void evt_disconn_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_disconnect_complete *ev = data;
+	struct btconn **curr;
+	uint16_t handle;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	handle = le16_to_cpu(ev->handle);
+
+	for (curr = &bthost->conns; *curr;) {
+		struct btconn *conn = *curr;
+
+		if (conn->handle == handle) {
+			*curr = conn->next;
+			btconn_free(conn);
+		} else {
+			curr = &conn->next;
+		}
+	}
+}
+
+static void evt_num_completed_packets(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_num_completed_packets *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+}
+
+static void evt_auth_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_auth_complete *ev = data;
+	struct bt_hci_cmd_set_conn_encrypt cp;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	cp.handle = ev->handle;
+	cp.encr_mode = 0x01;
+
+	send_command(bthost, BT_HCI_CMD_SET_CONN_ENCRYPT, &cp, sizeof(cp));
+}
+
+static void evt_pin_code_request(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_pin_code_request *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (bthost->pin_len > 0) {
+		struct bt_hci_cmd_pin_code_request_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		memcpy(cp.bdaddr, ev->bdaddr, 6);
+		cp.pin_len = bthost->pin_len;
+		memcpy(cp.pin_code, bthost->pin, bthost->pin_len);
+
+		send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY,
+							&cp, sizeof(cp));
+	} else {
+		struct bt_hci_cmd_pin_code_request_neg_reply cp;
+
+		memcpy(cp.bdaddr, ev->bdaddr, 6);
+		send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+							&cp, sizeof(cp));
+	}
+}
+
+static void evt_link_key_request(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_link_key_request *ev = data;
+	struct bt_hci_cmd_link_key_request_neg_reply cp;
+
+	if (len < sizeof(*ev))
+		return;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(cp.bdaddr, ev->bdaddr, 6);
+
+	send_command(bthost, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY,
+							&cp, sizeof(cp));
+}
+
+static void evt_link_key_notify(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_link_key_notify *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+}
+
+static void evt_encrypt_change(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_encrypt_change *ev = data;
+	struct btconn *conn;
+	uint16_t handle;
+
+	if (len < sizeof(*ev))
+		return;
+
+	handle = acl_handle(ev->handle);
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	if (ev->status)
+		return;
+
+	conn->encr_mode = ev->encr_mode;
+
+	if (conn->smp_data)
+		smp_conn_encrypted(conn->smp_data, conn->encr_mode);
+}
+
+static void evt_io_cap_response(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_io_capability_response *ev = data;
+	struct btconn *conn;
+
+	if (len < sizeof(*ev))
+		return;
+
+	conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
+	if (!conn)
+		return;
+}
+
+static void evt_io_cap_request(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_io_capability_request *ev = data;
+	struct bt_hci_cmd_io_capability_request_reply cp;
+	struct btconn *conn;
+
+	if (len < sizeof(*ev))
+		return;
+
+	conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
+	if (!conn)
+		return;
+
+	memcpy(cp.bdaddr, ev->bdaddr, 6);
+	cp.capability = bthost->io_capability;
+	cp.oob_data = 0x00;
+	cp.authentication = bthost->auth_req;
+
+	send_command(bthost, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+							&cp, sizeof(cp));
+}
+
+static void evt_user_confirm_request(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_user_confirm_request *ev = data;
+	struct btconn *conn;
+
+	if (len < sizeof(*ev))
+		return;
+
+	conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
+	if (!conn)
+		return;
+
+	if (bthost->reject_user_confirm) {
+		send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY,
+								ev->bdaddr, 6);
+		return;
+	}
+
+	send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+								ev->bdaddr, 6);
+}
+
+static void evt_simple_pairing_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_simple_pairing_complete *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+}
+
+static void evt_le_conn_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_le_conn_complete *ev = data;
+	uint8_t addr_type;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+
+	if (ev->peer_addr_type == 0x00)
+		addr_type = BDADDR_LE_PUBLIC;
+	else
+		addr_type = BDADDR_LE_RANDOM;
+
+	init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type);
+}
+
+static void evt_le_conn_update_complete(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_le_conn_update_complete *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+}
+
+static void evt_le_remote_features_complete(struct bthost *bthost,
+						const void *data, uint8_t len)
+{
+	const struct bt_hci_evt_le_remote_features_complete *ev = data;
+
+	if (len < sizeof(*ev))
+		return;
+
+	if (ev->status)
+		return;
+}
+
+static void evt_le_ltk_request(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const struct bt_hci_evt_le_long_term_key_request *ev = data;
+	struct bt_hci_cmd_le_ltk_req_reply cp;
+	struct bt_hci_cmd_le_ltk_req_neg_reply *neg_cp = (void *) &cp;
+	uint16_t handle, ediv;
+	uint64_t rand;
+	struct btconn *conn;
+	int err;
+
+	if (len < sizeof(*ev))
+		return;
+
+	handle = acl_handle(ev->handle);
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	rand = le64_to_cpu(ev->rand);
+	ediv = le16_to_cpu(ev->ediv);
+
+	cp.handle = ev->handle;
+
+	err = smp_get_ltk(conn->smp_data, rand, ediv, cp.ltk);
+	if (err < 0)
+		send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY,
+						neg_cp, sizeof(*neg_cp));
+	else
+		send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_REPLY, &cp,
+								sizeof(cp));
+}
+
+static void evt_le_meta_event(struct bthost *bthost, const void *data,
+								uint8_t len)
+{
+	const uint8_t *event = data;
+	const void *evt_data = data + 1;
+
+	if (len < 1)
+		return;
+
+	switch (*event) {
+	case BT_HCI_EVT_LE_CONN_COMPLETE:
+		evt_le_conn_complete(bthost, evt_data, len - 1);
+		break;
+	case BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE:
+		evt_le_conn_update_complete(bthost, evt_data, len - 1);
+		break;
+	case BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE:
+		evt_le_remote_features_complete(bthost, evt_data, len - 1);
+		break;
+	case BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST:
+		evt_le_ltk_request(bthost, evt_data, len - 1);
+		break;
+	default:
+		printf("Unsupported LE Meta event 0x%2.2x\n", *event);
+		break;
+	}
+}
+
+static void process_evt(struct bthost *bthost, const void *data, uint16_t len)
+{
+	const struct bt_hci_evt_hdr *hdr = data;
+	const void *param;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	if (sizeof(*hdr) + hdr->plen != len)
+		return;
+
+	param = data + sizeof(*hdr);
+
+	switch (hdr->evt) {
+	case BT_HCI_EVT_CMD_COMPLETE:
+		evt_cmd_complete(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_CMD_STATUS:
+		evt_cmd_status(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_CONN_REQUEST:
+		evt_conn_request(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_CONN_COMPLETE:
+		evt_conn_complete(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_DISCONNECT_COMPLETE:
+		evt_disconn_complete(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
+		evt_num_completed_packets(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_AUTH_COMPLETE:
+		evt_auth_complete(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_PIN_CODE_REQUEST:
+		evt_pin_code_request(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_LINK_KEY_REQUEST:
+		evt_link_key_request(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_LINK_KEY_NOTIFY:
+		evt_link_key_notify(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_ENCRYPT_CHANGE:
+		evt_encrypt_change(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_IO_CAPABILITY_RESPONSE:
+		evt_io_cap_response(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_IO_CAPABILITY_REQUEST:
+		evt_io_cap_request(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_USER_CONFIRM_REQUEST:
+		evt_user_confirm_request(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE:
+		evt_simple_pairing_complete(bthost, param, hdr->plen);
+		break;
+
+	case BT_HCI_EVT_LE_META_EVENT:
+		evt_le_meta_event(bthost, param, hdr->plen);
+		break;
+
+	default:
+		printf("Unsupported event 0x%2.2x\n", hdr->evt);
+		break;
+	}
+}
+
+static bool l2cap_cmd_rej(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_cmd_reject *rsp = data;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	return true;
+}
+
+static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_conn_req *req = data;
+	struct l2cap_conn_cb_data *cb_data;
+	struct bt_l2cap_pdu_conn_rsp rsp;
+	uint16_t psm;
+
+	if (len < sizeof(*req))
+		return false;
+
+	psm = le16_to_cpu(req->psm);
+
+	memset(&rsp, 0, sizeof(rsp));
+	rsp.scid = req->scid;
+
+	cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm);
+	if (cb_data)
+		rsp.dcid = rsp.scid;
+	else
+		rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
+
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONN_RSP, ident, &rsp,
+								sizeof(rsp));
+
+	if (!rsp.result) {
+		struct bt_l2cap_pdu_config_req conf_req;
+		struct l2conn *l2conn;
+
+		l2conn = bthost_add_l2cap_conn(bthost, conn,
+							le16_to_cpu(rsp.dcid),
+							le16_to_cpu(rsp.scid),
+							le16_to_cpu(psm));
+
+		memset(&conf_req, 0, sizeof(conf_req));
+		conf_req.dcid = rsp.scid;
+
+		l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
+						&conf_req, sizeof(conf_req));
+
+		if (cb_data && l2conn->psm == cb_data->psm && cb_data->func)
+			cb_data->func(conn->handle, l2conn->dcid,
+							cb_data->user_data);
+	}
+
+	return true;
+}
+
+static void rfcomm_sabm_send(struct bthost *bthost, struct btconn *conn,
+			struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	cmd.address = RFCOMM_ADDR(cr, dlci);
+	cmd.control = RFCOMM_CTRL(RFCOMM_SABM, 1);
+	cmd.length = RFCOMM_LEN8(0);
+	cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
+
+	send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd));
+}
+
+static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_conn_rsp *rsp = data;
+	struct bt_l2cap_pdu_config_req req;
+	struct l2conn *l2conn;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	l2conn = btconn_find_l2cap_conn_by_scid(conn, le16_to_cpu(rsp->scid));
+	if (l2conn)
+		l2conn->dcid = le16_to_cpu(rsp->dcid);
+	else
+		return false;
+
+	if (rsp->result)
+		return true;
+
+	memset(&req, 0, sizeof(req));
+	req.dcid = rsp->dcid;
+
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
+							&req, sizeof(req));
+
+	return true;
+}
+
+static bool l2cap_config_req(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_config_req *req = data;
+	struct bt_l2cap_pdu_config_rsp rsp;
+	struct l2conn *l2conn;
+	uint16_t dcid;
+
+	if (len < sizeof(*req))
+		return false;
+
+	dcid = le16_to_cpu(req->dcid);
+
+	l2conn = btconn_find_l2cap_conn_by_scid(conn, dcid);
+	if (!l2conn)
+		return false;
+
+	memset(&rsp, 0, sizeof(rsp));
+	rsp.scid  = cpu_to_le16(l2conn->dcid);
+	rsp.flags = req->flags;
+
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_RSP, ident, &rsp,
+								sizeof(rsp));
+
+	return true;
+}
+
+static bool l2cap_config_rsp(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_config_rsp *rsp = data;
+	struct l2conn *l2conn;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp->scid);
+	if (!l2conn)
+		return false;
+
+	if (l2conn->psm == 0x0003 && !rsp->result && bthost->rfcomm_conn_data)
+		rfcomm_sabm_send(bthost, conn, l2conn, 1, 0);
+
+	return true;
+}
+
+static bool l2cap_disconn_req(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_disconn_req *req = data;
+	struct bt_l2cap_pdu_disconn_rsp rsp;
+
+	if (len < sizeof(*req))
+		return false;
+
+	memset(&rsp, 0, sizeof(rsp));
+	rsp.dcid = req->dcid;
+	rsp.scid = req->scid;
+
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_DISCONN_RSP, ident, &rsp,
+								sizeof(rsp));
+
+	return true;
+}
+
+static bool l2cap_info_req(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_info_req *req = data;
+	uint64_t fixed_chan;
+	uint16_t type;
+	uint8_t buf[12];
+	struct bt_l2cap_pdu_info_rsp *rsp = (void *) buf;
+
+	if (len < sizeof(*req))
+		return false;
+
+	memset(buf, 0, sizeof(buf));
+	rsp->type = req->type;
+
+	type = le16_to_cpu(req->type);
+
+	switch (type) {
+	case L2CAP_IT_FEAT_MASK:
+		rsp->result = 0x0000;
+		put_le32(L2CAP_FEAT_FIXED_CHAN, rsp->data);
+		l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident,
+							rsp, sizeof(*rsp) + 4);
+		break;
+	case L2CAP_IT_FIXED_CHAN:
+		rsp->result = 0x0000;
+		fixed_chan = L2CAP_FC_SIG_BREDR;
+		if (bthost->sc && bthost->le)
+			fixed_chan |= L2CAP_FC_SMP_BREDR;
+		put_le64(fixed_chan, rsp->data);
+		l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident,
+				rsp, sizeof(*rsp) + sizeof(fixed_chan));
+		break;
+	default:
+		rsp->result = cpu_to_le16(0x0001); /* Not Supported */
+		l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_RSP, ident,
+							rsp, sizeof(*rsp));
+		break;
+	}
+
+	return true;
+}
+
+static bool l2cap_info_rsp(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_info_rsp *rsp = data;
+	uint16_t type;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	if (rsp->result)
+		return true;
+
+	type = le16_to_cpu(rsp->type);
+
+	switch (type) {
+	case L2CAP_IT_FIXED_CHAN:
+		if (len < sizeof(*rsp) + 8)
+			return false;
+		conn->fixed_chan = get_le64(rsp->data);
+		if (conn->smp_data && conn->encr_mode)
+			smp_conn_encrypted(conn->smp_data, conn->encr_mode);
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+static void handle_pending_l2reqs(struct bthost *bthost, struct btconn *conn,
+						uint8_t ident, uint8_t code,
+						const void *data, uint16_t len)
+{
+	struct l2cap_pending_req **curr;
+
+	for (curr = &bthost->l2reqs; *curr != NULL;) {
+		struct l2cap_pending_req *req = *curr;
+
+		if (req->ident != ident) {
+			curr = &req->next;
+			continue;
+		}
+
+		*curr = req->next;
+		req->cb(code, data, len, req->user_data);
+		free(req);
+	}
+}
+
+static void l2cap_sig(struct bthost *bthost, struct btconn *conn,
+						const void *data, uint16_t len)
+{
+	const struct bt_l2cap_hdr_sig *hdr = data;
+	struct bt_l2cap_pdu_cmd_reject rej;
+	uint16_t hdr_len;
+	bool ret;
+
+	if (len < sizeof(*hdr))
+		goto reject;
+
+	hdr_len = le16_to_cpu(hdr->len);
+
+	if (sizeof(*hdr) + hdr_len != len)
+		goto reject;
+
+	switch (hdr->code) {
+	case BT_L2CAP_PDU_CMD_REJECT:
+		ret = l2cap_cmd_rej(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_CONN_REQ:
+		ret = l2cap_conn_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_CONN_RSP:
+		ret = l2cap_conn_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_CONFIG_REQ:
+		ret = l2cap_config_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_CONFIG_RSP:
+		ret = l2cap_config_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_DISCONN_REQ:
+		ret = l2cap_disconn_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_INFO_REQ:
+		ret = l2cap_info_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_INFO_RSP:
+		ret = l2cap_info_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	default:
+		printf("Unknown L2CAP code 0x%02x\n", hdr->code);
+		ret = false;
+	}
+
+	handle_pending_l2reqs(bthost, conn, hdr->ident, hdr->code,
+						data + sizeof(*hdr), hdr_len);
+
+	if (ret)
+		return;
+
+reject:
+	memset(&rej, 0, sizeof(rej));
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CMD_REJECT, 0,
+							&rej, sizeof(rej));
+}
+
+static bool l2cap_conn_param_req(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_conn_param_req *req = data;
+	struct bt_l2cap_pdu_conn_param_rsp rsp;
+	struct bt_hci_cmd_le_conn_update hci_cmd;
+
+	if (len < sizeof(*req))
+		return false;
+
+	memset(&hci_cmd, 0, sizeof(hci_cmd));
+	hci_cmd.handle = cpu_to_le16(conn->handle);
+	hci_cmd.min_interval = req->min_interval;
+	hci_cmd.max_interval = req->max_interval;
+	hci_cmd.latency = req->latency;
+	hci_cmd.supv_timeout = req->timeout;
+	hci_cmd.min_length = cpu_to_le16(0x0001);
+	hci_cmd.max_length = cpu_to_le16(0x0001);
+
+	send_command(bthost, BT_HCI_CMD_LE_CONN_UPDATE,
+						&hci_cmd, sizeof(hci_cmd));
+
+	memset(&rsp, 0, sizeof(rsp));
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONN_PARAM_RSP, ident,
+							&rsp, sizeof(rsp));
+
+	return true;
+}
+
+static bool l2cap_conn_param_rsp(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_conn_param_req *rsp = data;
+
+	if (len < sizeof(*rsp))
+		return false;
+
+	return true;
+}
+
+static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_le_conn_req *req = data;
+	struct bt_l2cap_pdu_le_conn_rsp rsp;
+	uint16_t psm;
+
+	if (len < sizeof(*req))
+		return false;
+
+	psm = le16_to_cpu(req->psm);
+
+	memset(&rsp, 0, sizeof(rsp));
+
+	rsp.mtu = 23;
+	rsp.mps = 23;
+	rsp.credits = 1;
+
+	if (bthost_find_l2cap_cb_by_psm(bthost, psm))
+		rsp.dcid = cpu_to_le16(conn->next_cid++);
+	else
+		rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
+
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_CONN_RSP, ident, &rsp,
+								sizeof(rsp));
+
+	return true;
+}
+
+static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn,
+				uint8_t ident, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_pdu_le_conn_rsp *rsp = data;
+
+	if (len < sizeof(*rsp))
+		return false;
+	/* TODO add L2CAP connection before with proper PSM */
+	bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid), 0);
+
+	return true;
+}
+
+static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
+						const void *data, uint16_t len)
+{
+	const struct bt_l2cap_hdr_sig *hdr = data;
+	struct bt_l2cap_pdu_cmd_reject rej;
+	uint16_t hdr_len;
+	bool ret;
+
+	if (len < sizeof(*hdr))
+		goto reject;
+
+	hdr_len = le16_to_cpu(hdr->len);
+
+	if (sizeof(*hdr) + hdr_len != len)
+		goto reject;
+
+	switch (hdr->code) {
+	case BT_L2CAP_PDU_CMD_REJECT:
+		ret = l2cap_cmd_rej(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_DISCONN_REQ:
+		ret = l2cap_disconn_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_CONN_PARAM_REQ:
+		ret = l2cap_conn_param_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_CONN_PARAM_RSP:
+		ret = l2cap_conn_param_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_LE_CONN_REQ:
+		ret = l2cap_le_conn_req(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	case BT_L2CAP_PDU_LE_CONN_RSP:
+		ret = l2cap_le_conn_rsp(bthost, conn, hdr->ident,
+						data + sizeof(*hdr), hdr_len);
+		break;
+
+	default:
+		printf("Unknown L2CAP code 0x%02x\n", hdr->code);
+		ret = false;
+	}
+
+	handle_pending_l2reqs(bthost, conn, hdr->ident, hdr->code,
+						data + sizeof(*hdr), hdr_len);
+
+	if (ret)
+		return;
+
+reject:
+	memset(&rej, 0, sizeof(rej));
+	l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CMD_REJECT, 0,
+							&rej, sizeof(rej));
+}
+
+static struct cid_hook *find_cid_hook(struct btconn *conn, uint16_t cid)
+{
+	struct cid_hook *hook;
+
+	for (hook = conn->cid_hooks; hook != NULL; hook = hook->next) {
+		if (hook->cid == cid)
+			return hook;
+	}
+
+	return NULL;
+}
+
+static struct rfcomm_chan_hook *find_rfcomm_chan_hook(struct btconn *conn,
+							uint8_t channel)
+{
+	struct rfcomm_chan_hook *hook;
+
+	for (hook = conn->rfcomm_chan_hooks; hook != NULL; hook = hook->next)
+		if (hook->channel == channel)
+			return hook;
+
+	return NULL;
+}
+
+static void rfcomm_ua_send(struct bthost *bthost, struct btconn *conn,
+			struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	cmd.address = RFCOMM_ADDR(cr, dlci);
+	cmd.control = RFCOMM_CTRL(RFCOMM_UA, 1);
+	cmd.length = RFCOMM_LEN8(0);
+	cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
+
+	send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd));
+}
+
+static void rfcomm_dm_send(struct bthost *bthost, struct btconn *conn,
+			struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	cmd.address = RFCOMM_ADDR(cr, dlci);
+	cmd.control = RFCOMM_CTRL(RFCOMM_DM, 1);
+	cmd.length = RFCOMM_LEN8(0);
+	cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
+
+	send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd));
+}
+
+static void rfcomm_sabm_recv(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, const void *data,
+				uint16_t len)
+{
+	const struct rfcomm_cmd *hdr = data;
+	uint8_t dlci;
+	struct rfcomm_conn_cb_data *cb;
+	uint8_t chan;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	chan = RFCOMM_GET_CHANNEL(hdr->address);
+	dlci = RFCOMM_GET_DLCI(hdr->address);
+
+	cb = bthost_find_rfcomm_cb_by_channel(bthost, chan);
+	if (!dlci || cb) {
+		bthost_add_rfcomm_conn(bthost, conn, l2conn, chan);
+		rfcomm_ua_send(bthost, conn, l2conn, 1, dlci);
+		if (cb && cb->func)
+			cb->func(conn->handle, l2conn->scid, cb->user_data,
+									true);
+	} else {
+		rfcomm_dm_send(bthost, conn, l2conn, 1, dlci);
+	}
+}
+
+static void rfcomm_disc_recv(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, const void *data,
+				uint16_t len)
+{
+	const struct rfcomm_cmd *hdr = data;
+	uint8_t dlci;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	dlci = RFCOMM_GET_DLCI(hdr->address);
+
+	rfcomm_ua_send(bthost, conn, l2conn, 0, dlci);
+}
+
+static void rfcomm_uih_send(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, uint8_t address,
+				uint8_t type, const void *data, uint16_t len)
+{
+	struct rfcomm_hdr hdr;
+	struct rfcomm_mcc mcc;
+	uint8_t fcs;
+	struct iovec iov[4];
+
+	hdr.address = address;
+	hdr.control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+	hdr.length  = RFCOMM_LEN8(sizeof(mcc) + len);
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(hdr);
+
+	mcc.type = type;
+	mcc.length = RFCOMM_LEN8(len);
+
+	iov[1].iov_base = &mcc;
+	iov[1].iov_len = sizeof(mcc);
+
+	iov[2].iov_base = (void *) data;
+	iov[2].iov_len = len;
+
+	fcs = rfcomm_fcs((uint8_t *) &hdr);
+
+	iov[3].iov_base = &fcs;
+	iov[3].iov_len = sizeof(fcs);
+
+	send_iov(bthost, conn->handle, l2conn->dcid, iov, 4);
+}
+
+static void rfcomm_ua_recv(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, const void *data,
+				uint16_t len)
+{
+	const struct rfcomm_cmd *ua_hdr = data;
+	uint8_t channel;
+	struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data;
+	uint8_t type;
+	struct rfcomm_pn pn_cmd;
+
+	if (len < sizeof(*ua_hdr))
+		return;
+
+	channel = RFCOMM_GET_CHANNEL(ua_hdr->address);
+	type = RFCOMM_GET_TYPE(ua_hdr->control);
+
+	if (channel && conn_data && conn_data->channel == channel) {
+		bthost_add_rfcomm_conn(bthost, conn, l2conn, channel);
+		if (conn_data->cb)
+			conn_data->cb(conn->handle, l2conn->scid,
+						conn_data->user_data, true);
+		free(bthost->rfcomm_conn_data);
+		bthost->rfcomm_conn_data = NULL;
+		return;
+	}
+
+	if (!conn_data || !RFCOMM_TEST_CR(type))
+		return;
+
+	bthost_add_rfcomm_conn(bthost, conn, l2conn, channel);
+
+	pn_cmd.dlci = conn_data->channel * 2;
+	pn_cmd.priority = 7;
+	pn_cmd.ack_timer = 0;
+	pn_cmd.max_retrans = 0;
+	pn_cmd.mtu = 667;
+	pn_cmd.credits = 7;
+
+	rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(1, 0),
+			RFCOMM_MCC_TYPE(1, RFCOMM_PN), &pn_cmd, sizeof(pn_cmd));
+}
+
+static void rfcomm_dm_recv(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, const void *data,
+				uint16_t len)
+{
+	const struct rfcomm_cmd *hdr = data;
+	uint8_t channel;
+	struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	channel = RFCOMM_GET_CHANNEL(hdr->address);
+
+	if (conn_data && conn_data->channel == channel) {
+		if (conn_data->cb)
+			conn_data->cb(conn->handle, l2conn->scid,
+						conn_data->user_data, false);
+		free(bthost->rfcomm_conn_data);
+		bthost->rfcomm_conn_data = NULL;
+	}
+}
+
+static void rfcomm_msc_recv(struct bthost *bthost, struct btconn *conn,
+					struct l2conn *l2conn, uint8_t cr,
+					const struct rfcomm_msc *msc)
+{
+	struct rfcomm_msc msc_cmd;
+
+	msc_cmd.dlci = msc->dlci;
+	msc_cmd.v24_sig = msc->v24_sig;
+
+	rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(0, 0),
+				RFCOMM_MCC_TYPE(cr, RFCOMM_MSC), &msc_cmd,
+				sizeof(msc_cmd));
+}
+
+static void rfcomm_pn_recv(struct bthost *bthost, struct btconn *conn,
+					struct l2conn *l2conn, uint8_t cr,
+					const struct rfcomm_pn *pn)
+{
+	struct rfcomm_pn pn_cmd;
+
+	if (!cr) {
+		rfcomm_sabm_send(bthost, conn, l2conn, 1,
+					bthost->rfcomm_conn_data->channel * 2);
+		return;
+	}
+
+	pn_cmd.dlci = pn->dlci;
+	pn_cmd.flow_ctrl = pn->flow_ctrl;
+	pn_cmd.priority = pn->priority;
+	pn_cmd.ack_timer = pn->ack_timer;
+	pn_cmd.max_retrans = pn->max_retrans;
+	pn_cmd.mtu = pn->mtu;
+	pn_cmd.credits = pn->credits;
+
+	rfcomm_uih_send(bthost, conn, l2conn, RFCOMM_ADDR(1, 0),
+			RFCOMM_MCC_TYPE(0, RFCOMM_PN), &pn_cmd, sizeof(pn_cmd));
+}
+
+static void rfcomm_mcc_recv(struct bthost *bthost, struct btconn *conn,
+			struct l2conn *l2conn, const void *data, uint16_t len)
+{
+	const struct rfcomm_mcc *mcc = data;
+	const struct rfcomm_msc *msc;
+	const struct rfcomm_pn *pn;
+
+	if (len < sizeof(*mcc))
+		return;
+
+	switch (RFCOMM_GET_MCC_TYPE(mcc->type)) {
+	case RFCOMM_MSC:
+		if (len - sizeof(*mcc) < sizeof(*msc))
+			break;
+
+		msc = data + sizeof(*mcc);
+
+		rfcomm_msc_recv(bthost, conn, l2conn,
+				RFCOMM_TEST_CR(mcc->type) / 2, msc);
+		break;
+	case RFCOMM_PN:
+		if (len - sizeof(*mcc) < sizeof(*pn))
+			break;
+
+		pn = data + sizeof(*mcc);
+
+		rfcomm_pn_recv(bthost, conn, l2conn,
+				RFCOMM_TEST_CR(mcc->type) / 2, pn);
+		break;
+	default:
+		break;
+	}
+}
+
+#define GET_LEN8(length)	((length & 0xfe) >> 1)
+#define GET_LEN16(length)	((length & 0xfffe) >> 1)
+
+static void rfcomm_uih_recv(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, const void *data,
+				uint16_t len)
+{
+	const struct rfcomm_hdr *hdr = data;
+	uint16_t hdr_len, data_len;
+	const void *p;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	if (RFCOMM_TEST_EA(hdr->length)) {
+		data_len = (uint16_t) GET_LEN8(hdr->length);
+		hdr_len = sizeof(*hdr);
+	} else {
+		uint8_t ex_len = *((uint8_t *)(data + sizeof(*hdr)));
+		data_len = ((uint16_t) hdr->length << 8) | ex_len;
+		hdr_len = sizeof(*hdr) + sizeof(uint8_t);
+	}
+
+	if (len < hdr_len + data_len)
+		return;
+
+	p = data + hdr_len;
+
+	if (RFCOMM_GET_DLCI(hdr->address)) {
+		struct rfcomm_chan_hook *hook;
+
+		hook = find_rfcomm_chan_hook(conn,
+					RFCOMM_GET_CHANNEL(hdr->address));
+		if (hook && data_len)
+			hook->func(p, data_len, hook->user_data);
+	} else {
+		rfcomm_mcc_recv(bthost, conn, l2conn, p, data_len);
+	}
+}
+
+static void process_rfcomm(struct bthost *bthost, struct btconn *conn,
+				struct l2conn *l2conn, const void *data,
+				uint16_t len)
+{
+	const struct rfcomm_hdr *hdr = data;
+
+	switch (RFCOMM_GET_TYPE(hdr->control)) {
+	case RFCOMM_SABM:
+		rfcomm_sabm_recv(bthost, conn, l2conn, data, len);
+		break;
+	case RFCOMM_DISC:
+		rfcomm_disc_recv(bthost, conn, l2conn, data, len);
+		break;
+	case RFCOMM_UA:
+		rfcomm_ua_recv(bthost, conn, l2conn, data, len);
+		break;
+	case RFCOMM_DM:
+		rfcomm_dm_recv(bthost, conn, l2conn, data, len);
+		break;
+	case RFCOMM_UIH:
+		rfcomm_uih_recv(bthost, conn, l2conn, data, len);
+		break;
+	default:
+		printf("Unknown frame type\n");
+		break;
+	}
+}
+
+static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
+{
+	const struct bt_hci_acl_hdr *acl_hdr = data;
+	const struct bt_l2cap_hdr *l2_hdr = data + sizeof(*acl_hdr);
+	uint16_t handle, cid, acl_len, l2_len;
+	struct cid_hook *hook;
+	struct btconn *conn;
+	struct l2conn *l2conn;
+	const void *l2_data;
+
+	if (len < sizeof(*acl_hdr) + sizeof(*l2_hdr))
+		return;
+
+	acl_len = le16_to_cpu(acl_hdr->dlen);
+	if (len != sizeof(*acl_hdr) + acl_len)
+		return;
+
+	handle = acl_handle(acl_hdr->handle);
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn) {
+		printf("ACL data for unknown handle 0x%04x\n", handle);
+		return;
+	}
+
+	l2_len = le16_to_cpu(l2_hdr->len);
+	if (len - sizeof(*acl_hdr) != sizeof(*l2_hdr) + l2_len)
+		return;
+
+	l2_data = data + sizeof(*acl_hdr) + sizeof(*l2_hdr);
+
+	cid = le16_to_cpu(l2_hdr->cid);
+
+	hook = find_cid_hook(conn, cid);
+	if (hook) {
+		hook->func(l2_data, l2_len, hook->user_data);
+		return;
+	}
+
+	switch (cid) {
+	case 0x0001:
+		l2cap_sig(bthost, conn, l2_data, l2_len);
+		break;
+	case 0x0005:
+		l2cap_le_sig(bthost, conn, l2_data, l2_len);
+		break;
+	case 0x0006:
+		smp_data(conn->smp_data, l2_data, l2_len);
+		break;
+	case 0x0007:
+		smp_bredr_data(conn->smp_data, l2_data, l2_len);
+		break;
+	default:
+		l2conn = btconn_find_l2cap_conn_by_scid(conn, cid);
+		if (l2conn && l2conn->psm == 0x0003)
+			process_rfcomm(bthost, conn, l2conn, l2_data, l2_len);
+		else
+			printf("Packet for unknown CID 0x%04x (%u)\n", cid,
+									cid);
+		break;
+	}
+}
+
+void bthost_receive_h4(struct bthost *bthost, const void *data, uint16_t len)
+{
+	uint8_t pkt_type;
+
+	if (!bthost)
+		return;
+
+	if (len < 1)
+		return;
+
+	pkt_type = ((const uint8_t *) data)[0];
+
+	switch (pkt_type) {
+	case BT_H4_EVT_PKT:
+		process_evt(bthost, data + 1, len - 1);
+		break;
+	case BT_H4_ACL_PKT:
+		process_acl(bthost, data + 1, len - 1);
+		break;
+	default:
+		printf("Unsupported packet 0x%2.2x\n", pkt_type);
+		break;
+	}
+}
+
+void bthost_set_cmd_complete_cb(struct bthost *bthost,
+				bthost_cmd_complete_cb cb, void *user_data)
+{
+	bthost->cmd_complete_cb = cb;
+	bthost->cmd_complete_data = user_data;
+}
+
+void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb,
+							void *user_data)
+{
+	bthost->new_conn_cb = cb;
+	bthost->new_conn_data = user_data;
+}
+
+void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr,
+							uint8_t addr_type)
+{
+	bthost->conn_init = true;
+
+	if (addr_type == BDADDR_BREDR) {
+		struct bt_hci_cmd_create_conn cc;
+
+		memset(&cc, 0, sizeof(cc));
+		memcpy(cc.bdaddr, bdaddr, sizeof(cc.bdaddr));
+
+		send_command(bthost, BT_HCI_CMD_CREATE_CONN, &cc, sizeof(cc));
+	} else {
+		struct bt_hci_cmd_le_create_conn cc;
+
+		memset(&cc, 0, sizeof(cc));
+		memcpy(cc.peer_addr, bdaddr, sizeof(cc.peer_addr));
+
+		if (addr_type == BDADDR_LE_RANDOM)
+			cc.peer_addr_type = 0x01;
+
+		cc.scan_interval = cpu_to_le16(0x0060);
+		cc.scan_window = cpu_to_le16(0x0030);
+		cc.min_interval = cpu_to_le16(0x0028);
+		cc.max_interval = cpu_to_le16(0x0038);
+		cc.supv_timeout = cpu_to_le16(0x002a);
+
+		send_command(bthost, BT_HCI_CMD_LE_CREATE_CONN,
+							&cc, sizeof(cc));
+	}
+}
+
+void bthost_hci_disconnect(struct bthost *bthost, uint16_t handle,
+								uint8_t reason)
+{
+	struct bt_hci_cmd_disconnect disc;
+
+	disc.handle = cpu_to_le16(handle);
+	disc.reason = reason;
+
+	send_command(bthost, BT_HCI_CMD_DISCONNECT, &disc, sizeof(disc));
+}
+
+void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan)
+{
+	send_command(bthost, BT_HCI_CMD_WRITE_SCAN_ENABLE, &scan, 1);
+}
+
+void bthost_set_adv_data(struct bthost *bthost, const uint8_t *data,
+								uint8_t len)
+{
+	struct bt_hci_cmd_le_set_adv_data adv_cp;
+
+	memset(adv_cp.data, 0, 31);
+
+	if (len) {
+		adv_cp.len = len;
+		memcpy(adv_cp.data, data, len);
+	}
+
+	send_command(bthost, BT_HCI_CMD_LE_SET_ADV_DATA, &adv_cp,
+							sizeof(adv_cp));
+}
+
+void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable)
+{
+	struct bt_hci_cmd_le_set_adv_parameters cp;
+
+	memset(&cp, 0, sizeof(cp));
+	send_command(bthost, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+							&cp, sizeof(cp));
+
+	send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1);
+}
+
+void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode)
+{
+	send_command(bthost, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &mode, 1);
+}
+
+void bthost_write_le_host_supported(struct bthost *bthost, uint8_t mode)
+{
+	struct bt_hci_cmd_write_le_host_supported cmd;
+
+	bthost->le = mode;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.supported = mode;
+	send_command(bthost, BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED,
+							&cmd, sizeof(cmd));
+}
+
+void bthost_request_auth(struct bthost *bthost, uint16_t handle)
+{
+	struct btconn *conn;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	if (conn->addr_type == BDADDR_BREDR) {
+		struct bt_hci_cmd_auth_requested cp;
+
+		cp.handle = cpu_to_le16(handle);
+		send_command(bthost, BT_HCI_CMD_AUTH_REQUESTED, &cp, sizeof(cp));
+	} else {
+		uint8_t auth_req = bthost->auth_req;
+
+		if (bthost->sc)
+			auth_req |= 0x08;
+
+		smp_pair(conn->smp_data, bthost->io_capability, auth_req);
+	}
+}
+
+void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
+							const uint8_t ltk[16])
+{
+	struct bt_hci_cmd_le_start_encrypt cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = htobs(handle);
+	memcpy(cmd.ltk, ltk, 16);
+
+	send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd));
+}
+
+uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle)
+{
+	struct btconn *conn;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return 0;
+
+	return conn->fixed_chan;
+}
+
+void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm,
+				bthost_l2cap_connect_cb func, void *user_data)
+{
+	struct l2cap_conn_cb_data *data;
+
+	data = malloc(sizeof(struct l2cap_conn_cb_data));
+	if (!data)
+		return;
+
+	data->psm = psm;
+	data->user_data = user_data;
+	data->func = func;
+	data->next = bthost->new_l2cap_conn_data;
+
+	bthost->new_l2cap_conn_data = data;
+}
+
+void bthost_set_sc_support(struct bthost *bthost, bool enable)
+{
+	struct bt_hci_cmd_write_secure_conn_support cmd;
+
+	bthost->sc = enable;
+
+	if (!lmp_bredr_capable(bthost))
+		return;
+
+	cmd.support = enable;
+	send_command(bthost, BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT,
+							&cmd, sizeof(cmd));
+}
+
+void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin,
+							uint8_t pin_len)
+{
+	memcpy(bthost->pin, pin, pin_len);
+	bthost->pin_len = pin_len;
+}
+
+void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability)
+{
+	bthost->io_capability = io_capability;
+}
+
+uint8_t bthost_get_io_capability(struct bthost *bthost)
+{
+	return bthost->io_capability;
+}
+
+void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req)
+{
+	bthost->auth_req = auth_req;
+}
+
+uint8_t bthost_get_auth_req(struct bthost *bthost)
+{
+	uint8_t auth_req = bthost->auth_req;
+
+	if (bthost->sc)
+		auth_req |= 0x08;
+
+	return auth_req;
+}
+
+void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject)
+{
+	bthost->reject_user_confirm = reject;
+}
+
+bool bthost_get_reject_user_confirm(struct bthost *bthost)
+{
+	return bthost->reject_user_confirm;
+}
+
+void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel,
+				bthost_rfcomm_connect_cb func, void *user_data)
+{
+	struct rfcomm_conn_cb_data *data;
+
+	data = malloc(sizeof(struct rfcomm_conn_cb_data));
+	if (!data)
+		return;
+
+	data->channel = channel;
+	data->user_data = user_data;
+	data->func = func;
+	data->next = bthost->new_rfcomm_conn_data;
+
+	bthost->new_rfcomm_conn_data = data;
+}
+
+void bthost_start(struct bthost *bthost)
+{
+	if (!bthost)
+		return;
+
+	bthost->ncmd = 1;
+
+	send_command(bthost, BT_HCI_CMD_RESET, NULL, 0);
+
+	send_command(bthost, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0);
+	send_command(bthost, BT_HCI_CMD_READ_BD_ADDR, NULL, 0);
+}
+
+bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle,
+				uint8_t channel, bthost_rfcomm_connect_cb func,
+				void *user_data)
+{
+	struct rfcomm_connection_data *data;
+	struct bt_l2cap_pdu_conn_req req;
+	struct btconn *conn;
+
+	if (bthost->rfcomm_conn_data)
+		return false;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return false;
+
+	data = malloc(sizeof(struct rfcomm_connection_data));
+	if (!data)
+		return false;
+
+	data->channel = channel;
+	data->conn = conn;
+	data->cb = func;
+	data->user_data = user_data;
+
+	bthost->rfcomm_conn_data = data;
+
+	req.psm = cpu_to_le16(0x0003);
+	req.scid = cpu_to_le16(conn->next_cid++);
+
+	return bthost_l2cap_req(bthost, handle, BT_L2CAP_PDU_CONN_REQ,
+					&req, sizeof(req), NULL, NULL);
+}
+
+void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle,
+					uint8_t channel,
+					bthost_rfcomm_chan_hook_func_t func,
+					void *user_data)
+{
+	struct rfcomm_chan_hook *hook;
+	struct btconn *conn;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	hook = malloc(sizeof(*hook));
+	if (!hook)
+		return;
+
+	memset(hook, 0, sizeof(*hook));
+
+	hook->channel = channel;
+	hook->func = func;
+	hook->user_data = user_data;
+
+	hook->next = conn->rfcomm_chan_hooks;
+	conn->rfcomm_chan_hooks = hook;
+}
+
+void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle,
+					uint8_t channel, const void *data,
+					uint16_t len)
+{
+	struct btconn *conn;
+	struct rcconn *rcconn;
+	struct rfcomm_hdr *hdr;
+	uint8_t *uih_frame;
+	uint16_t uih_len;
+
+	conn = bthost_find_conn(bthost, handle);
+	if (!conn)
+		return;
+
+	rcconn = btconn_find_rfcomm_conn_by_channel(conn, channel);
+	if (!rcconn)
+		return;
+
+	if (len > 127)
+		uih_len = len + sizeof(struct rfcomm_cmd) + sizeof(uint8_t);
+	else
+		uih_len = len + sizeof(struct rfcomm_cmd);
+
+	uih_frame = malloc(uih_len);
+	if (!uih_frame)
+		return;
+
+	hdr = (struct rfcomm_hdr *) uih_frame;
+	hdr->address = RFCOMM_ADDR(1, channel * 2);
+	hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+	if (len > 127) {
+		hdr->length  = RFCOMM_LEN16(cpu_to_le16(sizeof(*hdr) + len));
+		memcpy(uih_frame + sizeof(*hdr) + 1, data, len);
+	} else {
+		hdr->length  = RFCOMM_LEN8(sizeof(*hdr) + len);
+		memcpy(uih_frame + sizeof(*hdr), data, len);
+	}
+
+	uih_frame[uih_len - 1] = rfcomm_fcs((void *)hdr);
+	send_acl(bthost, handle, rcconn->scid, uih_frame, uih_len);
+
+	free(uih_frame);
+}
diff --git a/bluez/repo/emulator/bthost.h b/bluez/repo/emulator/bthost.h
new file mode 100644
index 0000000..7110db8
--- /dev/null
+++ b/bluez/repo/emulator/bthost.h
@@ -0,0 +1,148 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <sys/uio.h>
+
+typedef void (*bthost_send_func) (const struct iovec *iov, int iovlen,
+							void *user_data);
+
+struct bthost;
+
+struct bthost *bthost_create(void);
+void bthost_destroy(struct bthost *bthost);
+
+typedef void (*bthost_ready_cb) (void);
+void bthost_notify_ready(struct bthost *bthost, bthost_ready_cb cb);
+
+void bthost_set_send_handler(struct bthost *bthost, bthost_send_func handler,
+							void *user_data);
+
+void bthost_receive_h4(struct bthost *bthost, const void *data, uint16_t len);
+
+typedef void (*bthost_cmd_complete_cb) (uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data);
+
+void bthost_set_cmd_complete_cb(struct bthost *bthost,
+				bthost_cmd_complete_cb cb, void *user_data);
+
+typedef void (*bthost_new_conn_cb) (uint16_t handle, void *user_data);
+
+void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb,
+							void *user_data);
+
+void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr,
+							uint8_t addr_type);
+
+void bthost_hci_disconnect(struct bthost *bthost, uint16_t handle,
+								uint8_t reason);
+
+typedef void (*bthost_cid_hook_func_t)(const void *data, uint16_t len,
+							void *user_data);
+
+void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid,
+				bthost_cid_hook_func_t func, void *user_data);
+
+void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid,
+					const void *data, uint16_t len);
+void bthost_send_cid_v(struct bthost *bthost, uint16_t handle, uint16_t cid,
+					const struct iovec *iov, int iovcnt);
+
+typedef void (*bthost_l2cap_rsp_cb) (uint8_t code, const void *data,
+						uint16_t len, void *user_data);
+
+bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t req,
+				const void *data, uint16_t len,
+				bthost_l2cap_rsp_cb cb, void *user_data);
+
+void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan);
+
+void bthost_set_adv_data(struct bthost *bthost, const uint8_t *data,
+								uint8_t len);
+void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable);
+
+void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode);
+
+void bthost_write_le_host_supported(struct bthost *bthost, uint8_t mode);
+
+void bthost_request_auth(struct bthost *bthost, uint16_t handle);
+
+void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
+							const uint8_t ltk[16]);
+typedef void (*bthost_l2cap_connect_cb) (uint16_t handle, uint16_t cid,
+							void *user_data);
+
+void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm,
+				bthost_l2cap_connect_cb func, void *user_data);
+
+void bthost_set_sc_support(struct bthost *bthost, bool enable);
+
+void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin,
+							uint8_t pin_len);
+void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability);
+uint8_t bthost_get_io_capability(struct bthost *bthost);
+void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req);
+uint8_t bthost_get_auth_req(struct bthost *bthost);
+void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject);
+bool bthost_get_reject_user_confirm(struct bthost *bthost);
+
+uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle);
+
+typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid,
+						void *user_data, bool status);
+
+void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel,
+			bthost_rfcomm_connect_cb func, void *user_data);
+
+bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle,
+				uint8_t channel, bthost_rfcomm_connect_cb func,
+				void *user_data);
+
+typedef void (*bthost_rfcomm_chan_hook_func_t) (const void *data, uint16_t len,
+							void *user_data);
+
+void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle,
+					uint8_t channel,
+					bthost_rfcomm_chan_hook_func_t func,
+					void *user_data);
+
+void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle,
+					uint8_t channel, const void *data,
+					uint16_t len);
+
+void bthost_start(struct bthost *bthost);
+
+/* LE SMP support */
+
+void *smp_start(struct bthost *bthost);
+void smp_stop(void *smp_data);
+void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia,
+			const uint8_t *ra, uint8_t addr_type, bool conn_init);
+void smp_conn_del(void *conn_data);
+void smp_conn_encrypted(void *conn_data, uint8_t encrypt);
+void smp_data(void *conn_data, const void *data, uint16_t len);
+void smp_bredr_data(void *conn_data, const void *data, uint16_t len);
+int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk);
+void smp_pair(void *conn_data, uint8_t io_cap, uint8_t auth_req);
diff --git a/bluez/repo/emulator/hciemu.c b/bluez/repo/emulator/hciemu.c
new file mode 100644
index 0000000..6a53499
--- /dev/null
+++ b/bluez/repo/emulator/hciemu.c
@@ -0,0 +1,525 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "monitor/bt.h"
+#include "emulator/btdev.h"
+#include "emulator/bthost.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "emulator/hciemu.h"
+
+struct hciemu {
+	int ref_count;
+	enum btdev_type btdev_type;
+	struct bthost *host_stack;
+	struct btdev *master_dev;
+	struct btdev *client_dev;
+	guint host_source;
+	guint master_source;
+	guint client_source;
+	struct queue *post_command_hooks;
+	char bdaddr_str[18];
+};
+
+struct hciemu_command_hook {
+	hciemu_command_func_t function;
+	void *user_data;
+};
+
+static void destroy_command_hook(void *data)
+{
+	struct hciemu_command_hook *hook = data;
+
+	free(hook);
+}
+
+struct run_data {
+	uint16_t opcode;
+	const void *data;
+	uint8_t len;
+};
+
+static void run_command_hook(void *data, void *user_data)
+{
+	struct hciemu_command_hook *hook = data;
+	struct run_data *run_data = user_data;
+
+	if (hook->function)
+		hook->function(run_data->opcode, run_data->data,
+					run_data->len, hook->user_data);
+}
+
+static void master_command_callback(uint16_t opcode,
+				const void *data, uint8_t len,
+				btdev_callback callback, void *user_data)
+{
+	struct hciemu *hciemu = user_data;
+	struct run_data run_data = { .opcode = opcode,
+						.data = data, .len = len };
+
+	btdev_command_default(callback);
+
+	queue_foreach(hciemu->post_command_hooks, run_command_hook, &run_data);
+}
+
+static void client_command_callback(uint16_t opcode,
+				const void *data, uint8_t len,
+				btdev_callback callback, void *user_data)
+{
+	btdev_command_default(callback);
+}
+
+static void writev_callback(const struct iovec *iov, int iovlen,
+								void *user_data)
+{
+	GIOChannel *channel = user_data;
+	ssize_t written;
+	int fd;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	written = writev(fd, iov, iovlen);
+	if (written < 0)
+		return;
+}
+
+static gboolean receive_bthost(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	struct bthost *bthost = user_data;
+	unsigned char buf[4096];
+	ssize_t len;
+	int fd;
+
+	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		return FALSE;
+
+	bthost_receive_h4(bthost, buf, len);
+
+	return TRUE;
+}
+
+static guint create_source_bthost(int fd, struct bthost *bthost)
+{
+	GIOChannel *channel;
+	guint source;
+
+	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);
+
+	bthost_set_send_handler(bthost, writev_callback, channel);
+
+	source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				receive_bthost, bthost, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static gboolean receive_btdev(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	struct btdev *btdev = user_data;
+	unsigned char buf[4096];
+	ssize_t len;
+	int fd;
+
+	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			return TRUE;
+
+		return FALSE;
+	}
+
+	if (len < 1)
+		return FALSE;
+
+	switch (buf[0]) {
+	case BT_H4_CMD_PKT:
+	case BT_H4_ACL_PKT:
+	case BT_H4_SCO_PKT:
+		btdev_receive_h4(btdev, buf, len);
+		break;
+	}
+
+	return TRUE;
+}
+
+static guint create_source_btdev(int fd, struct btdev *btdev)
+{
+	GIOChannel *channel;
+	guint source;
+
+	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);
+
+	btdev_set_send_handler(btdev, writev_callback, channel);
+
+	source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				receive_btdev, btdev, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static bool create_vhci(struct hciemu *hciemu)
+{
+	struct btdev *btdev;
+	uint8_t create_req[2];
+	ssize_t written;
+	int fd;
+
+	btdev = btdev_create(hciemu->btdev_type, 0x00);
+	if (!btdev)
+		return false;
+
+	btdev_set_command_handler(btdev, master_command_callback, hciemu);
+
+	fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC);
+	if (fd < 0) {
+		perror("Opening /dev/vhci failed");
+		btdev_destroy(btdev);
+		return false;
+	}
+
+	create_req[0] = HCI_VENDOR_PKT;
+	create_req[1] = HCI_BREDR;
+	written = write(fd, create_req, sizeof(create_req));
+	if (written < 0) {
+		close(fd);
+		btdev_destroy(btdev);
+		return false;
+	}
+
+	hciemu->master_dev = btdev;
+
+	hciemu->master_source = create_source_btdev(fd, btdev);
+
+	return true;
+}
+
+struct bthost *hciemu_client_get_host(struct hciemu *hciemu)
+{
+	if (!hciemu)
+		return NULL;
+
+	return hciemu->host_stack;
+}
+
+static bool create_stack(struct hciemu *hciemu)
+{
+	struct btdev *btdev;
+	struct bthost *bthost;
+	int sv[2];
+
+	btdev = btdev_create(hciemu->btdev_type, 0x00);
+	if (!btdev)
+		return false;
+
+	bthost = bthost_create();
+	if (!bthost) {
+		btdev_destroy(btdev);
+		return false;
+	}
+
+	btdev_set_command_handler(btdev, client_command_callback, hciemu);
+
+	if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC,
+								0, sv) < 0) {
+		bthost_destroy(bthost);
+		btdev_destroy(btdev);
+		return false;
+	}
+
+	hciemu->client_dev = btdev;
+	hciemu->host_stack = bthost;
+
+	hciemu->client_source = create_source_btdev(sv[0], btdev);
+	hciemu->host_source = create_source_bthost(sv[1], bthost);
+
+	return true;
+}
+
+static gboolean start_stack(gpointer user_data)
+{
+	struct hciemu *hciemu = user_data;
+
+	bthost_start(hciemu->host_stack);
+
+	return FALSE;
+}
+
+struct hciemu *hciemu_new(enum hciemu_type type)
+{
+	struct hciemu *hciemu;
+
+	hciemu = new0(struct hciemu, 1);
+	if (!hciemu)
+		return NULL;
+
+	switch (type) {
+	case HCIEMU_TYPE_BREDRLE:
+		hciemu->btdev_type = BTDEV_TYPE_BREDRLE;
+		break;
+	case HCIEMU_TYPE_BREDR:
+		hciemu->btdev_type = BTDEV_TYPE_BREDR;
+		break;
+	case HCIEMU_TYPE_LE:
+		hciemu->btdev_type = BTDEV_TYPE_LE;
+		break;
+	case HCIEMU_TYPE_LEGACY:
+		hciemu->btdev_type = BTDEV_TYPE_BREDR20;
+		break;
+	default:
+		return NULL;
+	}
+
+	hciemu->post_command_hooks = queue_new();
+	if (!hciemu->post_command_hooks) {
+		free(hciemu);
+		return NULL;
+	}
+
+	if (!create_vhci(hciemu)) {
+		queue_destroy(hciemu->post_command_hooks, NULL);
+		free(hciemu);
+		return NULL;
+	}
+
+	if (!create_stack(hciemu)) {
+		g_source_remove(hciemu->master_source);
+		btdev_destroy(hciemu->master_dev);
+		queue_destroy(hciemu->post_command_hooks, NULL);
+		free(hciemu);
+		return NULL;
+	}
+
+	g_idle_add(start_stack, hciemu);
+
+	return hciemu_ref(hciemu);
+}
+
+struct hciemu *hciemu_ref(struct hciemu *hciemu)
+{
+	if (!hciemu)
+		return NULL;
+
+	__sync_fetch_and_add(&hciemu->ref_count, 1);
+
+	return hciemu;
+}
+
+void hciemu_unref(struct hciemu *hciemu)
+{
+	if (!hciemu)
+		return;
+
+	if (__sync_sub_and_fetch(&hciemu->ref_count, 1))
+		return;
+
+	queue_destroy(hciemu->post_command_hooks, destroy_command_hook);
+
+	g_source_remove(hciemu->host_source);
+	g_source_remove(hciemu->client_source);
+	g_source_remove(hciemu->master_source);
+
+	bthost_destroy(hciemu->host_stack);
+	btdev_destroy(hciemu->client_dev);
+	btdev_destroy(hciemu->master_dev);
+
+	free(hciemu);
+}
+
+const char *hciemu_get_address(struct hciemu *hciemu)
+{
+	const uint8_t *addr;
+
+	if (!hciemu || !hciemu->master_dev)
+		return NULL;
+
+	addr = btdev_get_bdaddr(hciemu->master_dev);
+	sprintf(hciemu->bdaddr_str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+			addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
+	return hciemu->bdaddr_str;
+}
+
+uint8_t *hciemu_get_features(struct hciemu *hciemu)
+{
+	if (!hciemu || !hciemu->master_dev)
+		return NULL;
+
+	return btdev_get_features(hciemu->master_dev);
+}
+
+const uint8_t *hciemu_get_master_bdaddr(struct hciemu *hciemu)
+{
+	if (!hciemu || !hciemu->master_dev)
+		return NULL;
+
+	return btdev_get_bdaddr(hciemu->master_dev);
+}
+
+const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu)
+{
+	if (!hciemu || !hciemu->client_dev)
+		return NULL;
+
+	return btdev_get_bdaddr(hciemu->client_dev);
+}
+
+uint8_t hciemu_get_master_scan_enable(struct hciemu *hciemu)
+{
+	if (!hciemu || !hciemu->master_dev)
+		return 0;
+
+	return btdev_get_scan_enable(hciemu->master_dev);
+}
+
+uint8_t hciemu_get_master_le_scan_enable(struct hciemu *hciemu)
+{
+	if (!hciemu || !hciemu->master_dev)
+		return 0;
+
+	return btdev_get_le_scan_enable(hciemu->master_dev);
+}
+
+bool hciemu_add_master_post_command_hook(struct hciemu *hciemu,
+			hciemu_command_func_t function, void *user_data)
+{
+	struct hciemu_command_hook *hook;
+
+	if (!hciemu)
+		return false;
+
+	hook = new0(struct hciemu_command_hook, 1);
+	if (!hook)
+		return false;
+
+	hook->function = function;
+	hook->user_data = user_data;
+
+	if (!queue_push_tail(hciemu->post_command_hooks, hook)) {
+		free(hook);
+		return false;
+	}
+
+	return true;
+}
+
+int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
+				uint16_t opcode, hciemu_hook_func_t function,
+				void *user_data)
+{
+	enum btdev_hook_type hook_type;
+
+	if (!hciemu)
+		return -1;
+
+	switch (type) {
+	case HCIEMU_HOOK_PRE_CMD:
+		hook_type = BTDEV_HOOK_PRE_CMD;
+		break;
+	case HCIEMU_HOOK_POST_CMD:
+		hook_type = BTDEV_HOOK_POST_CMD;
+		break;
+	case HCIEMU_HOOK_PRE_EVT:
+		hook_type = BTDEV_HOOK_PRE_EVT;
+		break;
+	case HCIEMU_HOOK_POST_EVT:
+		hook_type = BTDEV_HOOK_POST_EVT;
+		break;
+	default:
+		return -1;
+	}
+
+	return btdev_add_hook(hciemu->master_dev, hook_type, opcode, function,
+								user_data);
+}
+
+bool hciemu_del_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
+								uint16_t opcode)
+{
+	enum btdev_hook_type hook_type;
+
+	if (!hciemu)
+		return false;
+
+	switch (type) {
+	case HCIEMU_HOOK_PRE_CMD:
+		hook_type = BTDEV_HOOK_PRE_CMD;
+		break;
+	case HCIEMU_HOOK_POST_CMD:
+		hook_type = BTDEV_HOOK_POST_CMD;
+		break;
+	case HCIEMU_HOOK_PRE_EVT:
+		hook_type = BTDEV_HOOK_PRE_EVT;
+		break;
+	case HCIEMU_HOOK_POST_EVT:
+		hook_type = BTDEV_HOOK_POST_EVT;
+		break;
+	default:
+		return false;
+	}
+
+	return btdev_del_hook(hciemu->master_dev, hook_type, opcode);
+}
diff --git a/bluez/repo/emulator/hciemu.h b/bluez/repo/emulator/hciemu.h
new file mode 100644
index 0000000..c5578d1
--- /dev/null
+++ b/bluez/repo/emulator/hciemu.h
@@ -0,0 +1,74 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+struct hciemu;
+
+enum hciemu_type {
+	HCIEMU_TYPE_BREDRLE,
+	HCIEMU_TYPE_BREDR,
+	HCIEMU_TYPE_LE,
+	HCIEMU_TYPE_LEGACY,
+};
+
+enum hciemu_hook_type {
+	HCIEMU_HOOK_PRE_CMD,
+	HCIEMU_HOOK_POST_CMD,
+	HCIEMU_HOOK_PRE_EVT,
+	HCIEMU_HOOK_POST_EVT,
+};
+
+struct hciemu *hciemu_new(enum hciemu_type type);
+
+struct hciemu *hciemu_ref(struct hciemu *hciemu);
+void hciemu_unref(struct hciemu *hciemu);
+
+struct bthost *hciemu_client_get_host(struct hciemu *hciemu);
+
+const char *hciemu_get_address(struct hciemu *hciemu);
+uint8_t *hciemu_get_features(struct hciemu *hciemu);
+
+const uint8_t *hciemu_get_master_bdaddr(struct hciemu *hciemu);
+const uint8_t *hciemu_get_client_bdaddr(struct hciemu *hciemu);
+
+uint8_t hciemu_get_master_scan_enable(struct hciemu *hciemu);
+
+uint8_t hciemu_get_master_le_scan_enable(struct hciemu *hciemu);
+
+typedef void (*hciemu_command_func_t)(uint16_t opcode, const void *data,
+						uint8_t len, void *user_data);
+
+typedef bool (*hciemu_hook_func_t)(const void *data, uint16_t len,
+							void *user_data);
+
+bool hciemu_add_master_post_command_hook(struct hciemu *hciemu,
+			hciemu_command_func_t function, void *user_data);
+
+int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
+				uint16_t opcode, hciemu_hook_func_t function,
+				void *user_data);
+
+bool hciemu_del_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
+							uint16_t opcode);
diff --git a/bluez/repo/emulator/hfp.c b/bluez/repo/emulator/hfp.c
new file mode 100644
index 0000000..e70054a
--- /dev/null
+++ b/bluez/repo/emulator/hfp.c
@@ -0,0 +1,120 @@
+/*
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "src/shared/mainloop.h"
+#include "src/shared/hfp.h"
+
+static void hfp_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	printf("%s%s\n", prefix, str);
+}
+
+static void command_handler(const char *command, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	printf("Command: %s\n", command);
+
+	hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
+}
+
+static bool open_connection(void)
+{
+	static const char SOCKET_PATH[] = "\0hfp-headset";
+	struct hfp_gw *hfp;
+	struct sockaddr_un addr;
+	int fd;
+
+	fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to create Unix socket");
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	memcpy(addr.sun_path, SOCKET_PATH, sizeof(SOCKET_PATH));
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to connect Unix socket");
+		close(fd);
+		return false;
+	}
+
+	hfp = hfp_gw_new(fd);
+	if (!hfp) {
+		close(fd);
+		return false;
+	}
+
+	hfp_gw_set_close_on_unref(hfp, true);
+
+	hfp_gw_set_debug(hfp, hfp_debug, "HFP: ", NULL);
+
+	hfp_gw_set_command_handler(hfp, command_handler, hfp, NULL);
+
+	return true;
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	sigset_t mask;
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	if (!open_connection())
+		return EXIT_FAILURE;
+
+	return mainloop_run();
+}
diff --git a/bluez/repo/emulator/le.c b/bluez/repo/emulator/le.c
new file mode 100644
index 0000000..82ae573
--- /dev/null
+++ b/bluez/repo/emulator/le.c
@@ -0,0 +1,1930 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <time.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "src/shared/crypto.h"
+#include "src/shared/ecc.h"
+#include "src/shared/mainloop.h"
+#include "monitor/bt.h"
+
+#include "phy.h"
+#include "le.h"
+
+#define WHITE_LIST_SIZE		16
+#define RESOLV_LIST_SIZE	16
+#define SCAN_CACHE_SIZE		64
+
+#define DEFAULT_TX_LEN		0x001b
+#define DEFAULT_TX_TIME		0x0148
+#define MAX_TX_LEN		0x00fb
+#define MAX_TX_TIME		0x0848
+#define MAX_RX_LEN		0x00fb
+#define MAX_RX_TIME		0x0848
+
+struct bt_peer {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+};
+
+struct bt_le {
+	volatile int ref_count;
+	int vhci_fd;
+	struct bt_phy *phy;
+	struct bt_crypto *crypto;
+	int adv_timeout_id;
+	int scan_timeout_id;
+	bool scan_window_active;
+	uint8_t scan_chan_idx;
+
+	uint8_t  event_mask[16];
+	uint16_t manufacturer;
+	uint8_t  commands[64];
+	uint8_t  features[8];
+	uint8_t  bdaddr[6];
+
+	uint8_t  le_event_mask[8];
+	uint16_t le_mtu;
+	uint8_t  le_max_pkt;
+	uint8_t  le_features[8];
+	uint8_t  le_random_addr[6];
+	uint16_t le_adv_min_interval;
+	uint16_t le_adv_max_interval;
+	uint8_t  le_adv_type;
+	uint8_t  le_adv_own_addr_type;
+	uint8_t  le_adv_direct_addr_type;
+	uint8_t  le_adv_direct_addr[6];
+	uint8_t  le_adv_channel_map;
+	uint8_t  le_adv_filter_policy;
+	int8_t   le_adv_tx_power;
+	uint8_t  le_adv_data_len;
+	uint8_t  le_adv_data[31];
+	uint8_t  le_scan_rsp_data_len;
+	uint8_t  le_scan_rsp_data[31];
+	uint8_t  le_adv_enable;
+	uint8_t  le_scan_type;
+	uint16_t le_scan_interval;
+	uint16_t le_scan_window;
+	uint8_t  le_scan_own_addr_type;
+	uint8_t  le_scan_filter_policy;
+	uint8_t  le_scan_enable;
+	uint8_t  le_scan_filter_dup;
+
+	uint8_t  le_conn_peer_addr_type;
+	uint8_t  le_conn_peer_addr[6];
+	uint8_t  le_conn_own_addr_type;
+	uint8_t  le_conn_enable;
+
+	uint8_t  le_white_list_size;
+	uint8_t  le_white_list[WHITE_LIST_SIZE][7];
+	uint8_t  le_states[8];
+
+	uint16_t le_default_tx_len;
+	uint16_t le_default_tx_time;
+	uint8_t  le_local_sk256[32];
+	uint8_t  le_resolv_list[RESOLV_LIST_SIZE][39];
+	uint8_t  le_resolv_list_size;
+	uint8_t  le_resolv_enable;
+	uint16_t le_resolv_timeout;
+
+	struct bt_peer scan_cache[SCAN_CACHE_SIZE];
+	uint8_t scan_cache_count;
+};
+
+static bool is_in_white_list(struct bt_le *hci, uint8_t addr_type,
+							const uint8_t addr[6])
+{
+	int i;
+
+	for (i = 0; i < hci->le_white_list_size; i++) {
+		if (hci->le_white_list[i][0] == addr_type &&
+				!memcmp(&hci->le_white_list[i][1], addr, 6))
+			return true;
+	}
+
+	return false;
+}
+
+static void clear_white_list(struct bt_le *hci)
+{
+	int i;
+
+	for (i = 0; i < hci->le_white_list_size; i++) {
+		hci->le_white_list[i][0] = 0xff;
+		memset(&hci->le_white_list[i][1], 0, 6);
+	}
+}
+
+static void resolve_peer_addr(struct bt_le *hci, uint8_t peer_addr_type,
+					const uint8_t peer_addr[6],
+					uint8_t *addr_type, uint8_t addr[6])
+{
+	int i;
+
+	if (!hci->le_resolv_enable)
+		goto done;
+
+	if (peer_addr_type != 0x01)
+		goto done;
+
+	if ((peer_addr[5] & 0xc0) != 0x40)
+		goto done;
+
+	for (i = 0; i < hci->le_resolv_list_size; i++) {
+		uint8_t local_hash[3];
+
+		if (hci->le_resolv_list[i][0] == 0xff)
+			continue;
+
+		bt_crypto_ah(hci->crypto, &hci->le_resolv_list[i][7],
+						peer_addr + 3, local_hash);
+
+		if (!memcmp(peer_addr, local_hash, 3)) {
+			switch (hci->le_resolv_list[i][0]) {
+			case 0x00:
+				*addr_type = 0x02;
+				break;
+			case 0x01:
+				*addr_type = 0x03;
+				break;
+			default:
+				continue;
+			}
+			memcpy(addr, &hci->le_resolv_list[i][1], 6);
+			return;
+		}
+	}
+
+done:
+	*addr_type = peer_addr_type;
+	memcpy(addr, peer_addr, 6);
+}
+
+static void clear_resolv_list(struct bt_le *hci)
+{
+	int i;
+
+	for (i = 0; i < hci->le_resolv_list_size; i++) {
+		hci->le_resolv_list[i][0] = 0xff;
+		memset(&hci->le_resolv_list[i][1], 0, 38);
+	}
+}
+
+static void reset_defaults(struct bt_le *hci)
+{
+	memset(hci->event_mask, 0, sizeof(hci->event_mask));
+	hci->event_mask[0] |= 0x10;	/* Disconnection Complete */
+	hci->event_mask[0] |= 0x80;	/* Encryption Change */
+	hci->event_mask[1] |= 0x08;	/* Read Remote Version Information Complete */
+	hci->event_mask[1] |= 0x20;	/* Command Complete */
+	hci->event_mask[1] |= 0x40;	/* Command Status */
+	hci->event_mask[1] |= 0x80;	/* Hardware Error */
+	hci->event_mask[2] |= 0x04;	/* Number of Completed Packets */
+	hci->event_mask[3] |= 0x02;	/* Data Buffer Overflow */
+	hci->event_mask[5] |= 0x80;	/* Encryption Key Refresh Complete */
+	//hci->event_mask[7] |= 0x20;	/* LE Meta Event */
+
+	hci->manufacturer = 0x003f;	/* Bluetooth SIG (63) */
+
+	memset(hci->commands, 0, sizeof(hci->commands));
+	hci->commands[0]  |= 0x20;	/* Disconnect */
+	//hci->commands[2]  |= 0x80;	/* Read Remote Version Information */
+	hci->commands[5]  |= 0x40;	/* Set Event Mask */
+	hci->commands[5]  |= 0x80;	/* Reset */
+	//hci->commands[10] |= 0x04;	/* Read Transmit Power Level */
+	hci->commands[14] |= 0x08;	/* Read Local Version Information */
+	hci->commands[14] |= 0x10;	/* Read Local Supported Commands */
+	hci->commands[14] |= 0x20;	/* Read Local Supported Features */
+	hci->commands[14] |= 0x80;	/* Read Buffer Size */
+	hci->commands[15] |= 0x02;	/* Read BD ADDR */
+	//hci->commands[15] |= 0x20;	/* Read RSSI */
+	hci->commands[22] |= 0x04;	/* Set Event Mask Page 2 */
+	hci->commands[25] |= 0x01;	/* LE Set Event Mask */
+	hci->commands[25] |= 0x02;	/* LE Read Buffer Size */
+	hci->commands[25] |= 0x04;	/* LE Read Local Supported Features */
+	hci->commands[25] |= 0x10;	/* LE Set Random Address */
+	hci->commands[25] |= 0x20;	/* LE Set Advertising Parameters */
+	hci->commands[25] |= 0x40;	/* LE Read Advertising Channel TX Power */
+	hci->commands[25] |= 0x80;	/* LE Set Advertising Data */
+	hci->commands[26] |= 0x01;	/* LE Set Scan Response Data */
+	hci->commands[26] |= 0x02;	/* LE Set Advertise Enable */
+	hci->commands[26] |= 0x04;	/* LE Set Scan Parameters */
+	hci->commands[26] |= 0x08;	/* LE Set Scan Enable */
+	hci->commands[26] |= 0x10;	/* LE Create Connection */
+	hci->commands[26] |= 0x20;	/* LE Create Connection Cancel */
+	hci->commands[26] |= 0x40;	/* LE Read White List Size */
+	hci->commands[26] |= 0x80;	/* LE Clear White List */
+	hci->commands[27] |= 0x01;	/* LE Add Device To White List */
+	hci->commands[27] |= 0x02;	/* LE Remove Device From White List */
+	//hci->commands[27] |= 0x04;	/* LE Connection Update */
+	//hci->commands[27] |= 0x08;	/* LE Set Host Channel Classification */
+	//hci->commands[27] |= 0x10;	/* LE Read Channel Map */
+	//hci->commands[27] |= 0x20;	/* LE Read Remote Used Features */
+	hci->commands[27] |= 0x40;	/* LE Encrypt */
+	hci->commands[27] |= 0x80;	/* LE Rand */
+	//hci->commands[28] |= 0x01;	/* LE Start Encryption */
+	//hci->commands[28] |= 0x02;	/* LE Long Term Key Request Reply */
+	//hci->commands[28] |= 0x04;	/* LE Long Term Key Request Negative Reply */
+	hci->commands[28] |= 0x08;	/* LE Read Supported States */
+	//hci->commands[28] |= 0x10;	/* LE Receiver Test */
+	//hci->commands[28] |= 0x20;	/* LE Transmitter Test */
+	//hci->commands[28] |= 0x40;	/* LE Test End */
+	//hci->commands[33] |= 0x10;	/* LE Remote Connection Parameter Request Reply */
+	//hci->commands[33] |= 0x20;	/* LE Remote Connection Parameter Request Negative Reply */
+	hci->commands[33] |= 0x40;	/* LE Set Data Length */
+	hci->commands[33] |= 0x80;	/* LE Read Suggested Default Data Length */
+	hci->commands[34] |= 0x01;	/* LE Write Suggested Default Data Length */
+	hci->commands[34] |= 0x02;	/* LE Read Local P-256 Public Key */
+	hci->commands[34] |= 0x04;	/* LE Generate DHKey */
+	hci->commands[34] |= 0x08;	/* LE Add Device To Resolving List */
+	hci->commands[34] |= 0x10;	/* LE Remove Device From Resolving List */
+	hci->commands[34] |= 0x20;	/* LE Clear Resolving List */
+	hci->commands[34] |= 0x40;	/* LE Read Resolving List Size */
+	hci->commands[34] |= 0x80;	/* LE Read Peer Resolvable Address */
+	hci->commands[35] |= 0x01;	/* LE Read Local Resolvable Address */
+	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 */
+
+	memset(hci->features, 0, sizeof(hci->features));
+	hci->features[4] |= 0x20;	/* BR/EDR Not Supported */
+	hci->features[4] |= 0x40;	/* LE Supported */
+
+	memset(hci->bdaddr, 0, sizeof(hci->bdaddr));
+
+	memset(hci->le_event_mask, 0, sizeof(hci->le_event_mask));
+	hci->le_event_mask[0] |= 0x01;	/* LE Connection Complete */
+	hci->le_event_mask[0] |= 0x02;	/* LE Advertising Report */
+	hci->le_event_mask[0] |= 0x04;	/* LE Connection Update Complete */
+	hci->le_event_mask[0] |= 0x08;	/* LE Read Remote Used Features Complete */
+	hci->le_event_mask[0] |= 0x10;	/* LE Long Term Key Request */
+	//hci->le_event_mask[0] |= 0x20;	/* LE Remote Connection Parameter Request */
+	//hci->le_event_mask[0] |= 0x40;	/* LE Data Length Change */
+	//hci->le_event_mask[0] |= 0x80;	/* LE Read Local P-256 Public Key Complete */
+	//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_mtu = 64;
+	hci->le_max_pkt = 1;
+
+	memset(hci->le_features, 0, sizeof(hci->le_features));
+	hci->le_features[0] |= 0x01;	/* LE Encryption */
+	//hci->le_features[0] |= 0x02;	/* Connection Parameter Request Procedure */
+	//hci->le_features[0] |= 0x04;	/* Extended Reject Indication */
+	//hci->le_features[0] |= 0x08;	/* Slave-initiated Features Exchange */
+	hci->le_features[0] |= 0x10;	/* LE Ping */
+	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 */
+
+	memset(hci->le_random_addr, 0, sizeof(hci->le_random_addr));
+
+	hci->le_adv_min_interval = 0x0800;
+	hci->le_adv_max_interval = 0x0800;
+	hci->le_adv_type = 0x00;
+	hci->le_adv_own_addr_type = 0x00;
+	hci->le_adv_direct_addr_type = 0x00;
+	memset(hci->le_adv_direct_addr, 0, 6);
+	hci->le_adv_channel_map = 0x07;
+	hci->le_adv_filter_policy = 0x00;
+
+	hci->le_adv_tx_power = 0;
+
+	memset(hci->le_adv_data, 0, sizeof(hci->le_adv_data));
+	hci->le_adv_data_len = 0;
+
+	memset(hci->le_scan_rsp_data, 0, sizeof(hci->le_scan_rsp_data));
+	hci->le_scan_rsp_data_len = 0;
+
+	hci->le_adv_enable = 0x00;
+
+	hci->le_scan_type = 0x00;		/* Passive Scanning */
+	hci->le_scan_interval = 0x0010;		/* 10 ms */
+	hci->le_scan_window = 0x0010;		/* 10 ms */
+	hci->le_scan_own_addr_type = 0x00;	/* Public Device Address */
+	hci->le_scan_filter_policy = 0x00;
+	hci->le_scan_enable = 0x00;
+	hci->le_scan_filter_dup = 0x00;
+
+	hci->le_conn_enable = 0x00;
+
+	hci->le_white_list_size = WHITE_LIST_SIZE;
+	clear_white_list(hci);
+
+	memset(hci->le_states, 0, sizeof(hci->le_states));
+	hci->le_states[0] |= 0x01;	/* Non-connectable Advertising */
+	hci->le_states[0] |= 0x02;	/* Scannable Advertising */
+	hci->le_states[0] |= 0x04;	/* Connectable Advertising */
+	hci->le_states[0] |= 0x08;	/* High Duty Cycle Directed Advertising */
+	hci->le_states[0] |= 0x10;	/* Passive Scanning */
+	hci->le_states[0] |= 0x20;	/* Active Scanning */
+	hci->le_states[0] |= 0x40;	/* Initiating + Connection (Master Role) */
+	hci->le_states[0] |= 0x80;	/* Connection (Slave Role) */
+	hci->le_states[1] |= 0x01;	/* Passive Scanning +
+					 * Non-connectable Advertising */
+
+	hci->le_default_tx_len = DEFAULT_TX_LEN;
+	hci->le_default_tx_time = DEFAULT_TX_TIME;
+
+	memset(hci->le_local_sk256, 0, sizeof(hci->le_local_sk256));
+
+	hci->le_resolv_list_size = RESOLV_LIST_SIZE;
+	clear_resolv_list(hci);
+	hci->le_resolv_enable = 0x00;
+	hci->le_resolv_timeout = 0x0384;	/* 900 secs or 15 minutes */
+}
+
+static void clear_scan_cache(struct bt_le *hci)
+{
+	memset(hci->scan_cache, 0, sizeof(hci->scan_cache));
+	hci->scan_cache_count = 0;
+}
+
+static bool add_to_scan_cache(struct bt_le *hci, uint8_t addr_type,
+							const uint8_t addr[6])
+{
+	int i;
+
+	for (i = 0; i < hci->scan_cache_count; i++) {
+		if (hci->scan_cache[i].addr_type == addr_type &&
+				!memcmp(hci->scan_cache[i].addr, addr, 6))
+			return false;
+	}
+
+	if (hci->scan_cache_count >= SCAN_CACHE_SIZE)
+		return true;
+
+	hci->scan_cache[hci->scan_cache_count].addr_type = addr_type;
+	memcpy(hci->scan_cache[hci->scan_cache_count].addr, addr, 6);
+	hci->scan_cache_count++;
+
+	return true;
+}
+
+static void send_event(struct bt_le *hci, uint8_t event,
+						void *data, uint8_t size)
+{
+	uint8_t type = BT_H4_EVT_PKT;
+	struct bt_hci_evt_hdr hdr;
+	struct iovec iov[3];
+	int iovcnt;
+
+	hdr.evt  = event;
+	hdr.plen = size;
+
+	iov[0].iov_base = &type;
+	iov[0].iov_len  = 1;
+	iov[1].iov_base = &hdr;
+	iov[1].iov_len  = sizeof(hdr);
+
+	if (size > 0) {
+		iov[2].iov_base = data;
+		iov[2].iov_len  = size;
+		iovcnt = 3;
+	} else
+		iovcnt = 2;
+
+	if (writev(hci->vhci_fd, iov, iovcnt) < 0)
+		fprintf(stderr, "Write to /dev/vhci failed (%m)\n");
+}
+
+static void send_adv_pkt(struct bt_le *hci, uint8_t channel)
+{
+	struct bt_phy_pkt_adv pkt;
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.chan_idx = channel;
+	pkt.pdu_type = hci->le_adv_type;
+	pkt.tx_addr_type = hci->le_adv_own_addr_type;
+	switch (hci->le_adv_own_addr_type) {
+	case 0x00:
+	case 0x02:
+		memcpy(pkt.tx_addr, hci->bdaddr, 6);
+		break;
+	case 0x01:
+	case 0x03:
+		memcpy(pkt.tx_addr, hci->le_random_addr, 6);
+		break;
+	}
+	pkt.rx_addr_type = hci->le_adv_direct_addr_type;
+	memcpy(pkt.rx_addr, hci->le_adv_direct_addr, 6);
+	pkt.adv_data_len = hci->le_adv_data_len;
+	pkt.scan_rsp_len = hci->le_scan_rsp_data_len;
+
+	bt_phy_send_vector(hci->phy, BT_PHY_PKT_ADV, &pkt, sizeof(pkt),
+				hci->le_adv_data, pkt.adv_data_len,
+				hci->le_scan_rsp_data, pkt.scan_rsp_len);
+}
+
+static unsigned int get_adv_delay(void)
+{
+	/* The advertising delay is a pseudo-random value with a range
+	 * of 0 ms to 10 ms generated for each advertising event.
+	 */
+	srand(time(NULL));
+	return (rand() % 11);
+}
+
+static void adv_timeout_callback(int id, void *user_data)
+{
+	struct bt_le *hci = user_data;
+	unsigned int msec, min_msec, max_msec;
+
+	if (hci->le_adv_channel_map & 0x01)
+		send_adv_pkt(hci, 37);
+	if (hci->le_adv_channel_map & 0x02)
+		send_adv_pkt(hci, 38);
+	if (hci->le_adv_channel_map & 0x04)
+		send_adv_pkt(hci, 39);
+
+	min_msec = (hci->le_adv_min_interval * 625) / 1000;
+	max_msec = (hci->le_adv_max_interval * 625) / 1000;
+
+	msec = ((min_msec + max_msec) / 2) + get_adv_delay();
+
+	if (mainloop_modify_timeout(id, msec) < 0) {
+		fprintf(stderr, "Setting advertising timeout failed\n");
+		hci->le_adv_enable = 0x00;
+	}
+}
+
+static bool start_adv(struct bt_le *hci)
+{
+	unsigned int msec;
+
+	if (hci->adv_timeout_id >= 0)
+		return false;
+
+	msec = ((hci->le_adv_min_interval * 625) / 1000) + get_adv_delay();
+
+	hci->adv_timeout_id = mainloop_add_timeout(msec, adv_timeout_callback,
+								hci, NULL);
+	if (hci->adv_timeout_id < 0)
+		return false;
+
+	return true;
+}
+
+static bool stop_adv(struct bt_le *hci)
+{
+	if (hci->adv_timeout_id < 0)
+		return false;
+
+	mainloop_remove_timeout(hci->adv_timeout_id);
+	hci->adv_timeout_id = -1;
+
+	return true;
+}
+
+static void scan_timeout_callback(int id, void *user_data)
+{
+	struct bt_le *hci = user_data;
+	unsigned int msec;
+
+	if (hci->le_scan_window == hci->le_scan_interval ||
+						!hci->scan_window_active) {
+		msec = (hci->le_scan_window * 625) / 1000;
+		hci->scan_window_active = true;
+
+		hci->scan_chan_idx++;
+		if (hci->scan_chan_idx > 39)
+			hci->scan_chan_idx = 37;
+	} else {
+		msec = ((hci->le_scan_interval -
+					hci->le_scan_window) * 625) / 1000;
+		hci->scan_window_active = false;
+	}
+
+	if (mainloop_modify_timeout(id, msec) < 0) {
+		fprintf(stderr, "Setting scanning timeout failed\n");
+		hci->le_scan_enable = 0x00;
+		hci->scan_window_active = false;
+	}
+}
+
+static bool start_scan(struct bt_le *hci)
+{
+	unsigned int msec;
+
+	if (hci->scan_timeout_id >= 0)
+		return false;
+
+	msec = (hci->le_scan_window * 625) / 1000;
+
+	hci->scan_timeout_id = mainloop_add_timeout(msec, scan_timeout_callback,
+								hci, NULL);
+	if (hci->scan_timeout_id < 0)
+		return false;
+
+	hci->scan_window_active = true;
+	hci->scan_chan_idx = 37;
+
+	return true;
+}
+
+static bool stop_scan(struct bt_le *hci)
+{
+	if (hci->scan_timeout_id < 0)
+		return false;
+
+	mainloop_remove_timeout(hci->scan_timeout_id);
+	hci->scan_timeout_id = -1;
+
+	hci->scan_window_active = false;
+
+	return true;
+}
+
+static void cmd_complete(struct bt_le *hci, uint16_t opcode,
+						const void *data, uint8_t len)
+{
+	struct bt_hci_evt_cmd_complete *cc;
+	void *pkt_data;
+
+	pkt_data = alloca(sizeof(*cc) + len);
+	if (!pkt_data)
+		return;
+
+	cc = pkt_data;
+	cc->ncmd = 0x01;
+	cc->opcode = cpu_to_le16(opcode);
+
+	if (len > 0)
+		memcpy(pkt_data + sizeof(*cc), data, len);
+
+	send_event(hci, BT_HCI_EVT_CMD_COMPLETE, pkt_data, sizeof(*cc) + len);
+}
+
+static void cmd_status(struct bt_le *hci, uint8_t status, uint16_t opcode)
+{
+	struct bt_hci_evt_cmd_status cs;
+
+	cs.status = status;
+	cs.ncmd = 0x01;
+	cs.opcode = cpu_to_le16(opcode);
+
+	send_event(hci, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs));
+}
+
+static void le_meta_event(struct bt_le *hci, uint8_t event,
+						void *data, uint8_t len)
+{
+	void *pkt_data;
+
+	if (!(hci->event_mask[7] & 0x20))
+		return;
+
+	pkt_data = alloca(1 + len);
+	if (!pkt_data)
+		return;
+
+	((uint8_t *) pkt_data)[0] = event;
+
+	if (len > 0)
+		memcpy(pkt_data + 1, data, len);
+
+	send_event(hci, BT_HCI_EVT_LE_META_EVENT, pkt_data, 1 + len);
+}
+
+static void cmd_disconnect(struct bt_le *hci, const void *data, uint8_t size)
+{
+	cmd_status(hci, BT_HCI_ERR_UNKNOWN_CONN_ID, BT_HCI_CMD_DISCONNECT);
+}
+
+static void cmd_set_event_mask(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_event_mask *cmd = data;
+	uint8_t status;
+
+	memcpy(hci->event_mask, cmd->mask, 8);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status));
+}
+
+static void cmd_reset(struct bt_le *hci, const void *data, uint8_t size)
+{
+	uint8_t status;
+
+	stop_adv(hci);
+	stop_scan(hci);
+	reset_defaults(hci);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_RESET, &status, sizeof(status));
+}
+
+static void cmd_set_event_mask_page2(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_event_mask_page2 *cmd = data;
+	uint8_t status;
+
+	memcpy(hci->event_mask + 8, cmd->mask, 8);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_SET_EVENT_MASK_PAGE2,
+						&status, sizeof(status));
+}
+
+static void cmd_read_local_version(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_version rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.hci_ver = 0x08;
+	rsp.hci_rev = cpu_to_le16(0x0000);
+	rsp.lmp_ver = 0x08;
+	rsp.manufacturer = cpu_to_le16(hci->manufacturer);
+	rsp.lmp_subver = cpu_to_le16(0x0000);
+
+	cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_commands(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_commands rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.commands, hci->commands, 64);
+
+	cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_features(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_local_features rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.features, hci->features, 8);
+
+	cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_buffer_size(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_buffer_size rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.acl_mtu = cpu_to_le16(0x0000);
+	rsp.sco_mtu = 0x00;
+	rsp.acl_max_pkt = cpu_to_le16(0x0000);
+	rsp.sco_max_pkt = cpu_to_le16(0x0000);
+
+	cmd_complete(hci, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_bd_addr(struct bt_le *hci, const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_read_bd_addr rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.bdaddr, hci->bdaddr, 6);
+
+	cmd_complete(hci, BT_HCI_CMD_READ_BD_ADDR, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_event_mask(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_event_mask *cmd = data;
+	uint8_t status;
+
+	memcpy(hci->le_event_mask, cmd->mask, 8);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_EVENT_MASK,
+						&status, sizeof(status));
+}
+
+static void cmd_le_read_buffer_size(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_buffer_size rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.le_mtu = cpu_to_le16(hci->le_mtu);
+	rsp.le_max_pkt = hci->le_max_pkt;
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_BUFFER_SIZE, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_local_features(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_local_features rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.features, hci->le_features, 8);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_LOCAL_FEATURES,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_random_address(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_random_address *cmd = data;
+	uint8_t status;
+
+	memcpy(hci->le_random_addr, cmd->addr, 6);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+						&status, sizeof(status));
+}
+
+static void cmd_le_set_adv_parameters(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_adv_parameters *cmd = data;
+	uint16_t min_interval, max_interval;
+	uint8_t status;
+
+	if (hci->le_adv_enable == 0x01) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+		return;
+	}
+
+	min_interval = le16_to_cpu(cmd->min_interval);
+	max_interval = le16_to_cpu(cmd->max_interval);
+
+	/* Valid range for advertising type is 0x00 to 0x03 */
+	switch (cmd->type) {
+	case 0x00:	/* ADV_IND */
+		/* Range for advertising interval min is 0x0020 to 0x4000 */
+		if (min_interval < 0x0020 || min_interval > 0x4000) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		/* Range for advertising interval max is 0x0020 to 0x4000 */
+		if (max_interval < 0x0020 || max_interval > 0x4000) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		/* Advertising interval max shall be less or equal */
+		if (min_interval > max_interval) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		break;
+
+	case 0x01:	/* ADV_DIRECT_IND */
+		/* Range for direct address type is 0x00 to 0x01 */
+		if (cmd->direct_addr_type > 0x01) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		break;
+
+	case 0x02:	/* ADV_SCAN_IND */
+	case 0x03:	/* ADV_NONCONN_IND */
+		/* Range for advertising interval min is 0x00a0 to 0x4000 */
+		if (min_interval < 0x00a0 || min_interval > 0x4000) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		/* Range for advertising interval max is 0x00a0 to 0x4000 */
+		if (max_interval < 0x00a0 || max_interval > 0x4000) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		/* Advertising interval min shall be less or equal */
+		if (min_interval > max_interval) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+			return;
+		}
+		break;
+
+	default:
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for own address type is 0x00 to 0x03 */
+	if (cmd->own_addr_type > 0x03) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for advertising channel map is 0x01 to 0x07 */
+	if (cmd->channel_map < 0x01 || cmd->channel_map > 0x07) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for advertising filter policy is 0x00 to 0x03 */
+	if (cmd->filter_policy > 0x03) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+		return;
+	}
+
+	hci->le_adv_min_interval = min_interval;
+	hci->le_adv_max_interval = max_interval;
+	hci->le_adv_type = cmd->type;
+	hci->le_adv_own_addr_type = cmd->own_addr_type;
+	hci->le_adv_direct_addr_type = cmd->direct_addr_type;
+	memcpy(hci->le_adv_direct_addr, cmd->direct_addr, 6);
+	hci->le_adv_channel_map = cmd->channel_map;
+	hci->le_adv_filter_policy = cmd->filter_policy;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+						&status, sizeof(status));
+}
+
+static void cmd_le_read_adv_tx_power(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_adv_tx_power rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.level = hci->le_adv_tx_power;
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_ADV_TX_POWER, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_adv_data(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_adv_data *cmd = data;
+	uint8_t status;
+
+	/* Valid range for advertising data length is 0x00 to 0x1f */
+	if (cmd->len > 0x1f) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_DATA);
+		return;
+	}
+
+	hci->le_adv_data_len = cmd->len;
+	memcpy(hci->le_adv_data, cmd->data, 31);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_DATA, &status, sizeof(status));
+}
+
+static void cmd_le_set_scan_rsp_data(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data;
+	uint8_t status;
+
+	/* Valid range for scan response data length is 0x00 to 0x1f */
+	if (cmd->len > 0x1f) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_RSP_DATA);
+		return;
+	}
+
+	hci->le_scan_rsp_data_len = cmd->len;
+	memcpy(hci->le_scan_rsp_data, cmd->data, 31);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+						&status, sizeof(status));
+}
+
+static void cmd_le_set_adv_enable(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_adv_enable *cmd = data;
+	uint8_t status;
+	bool result;
+
+	/* Valid range for advertising enable is 0x00 to 0x01 */
+	if (cmd->enable > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_ADV_ENABLE);
+		return;
+	}
+
+	if (cmd->enable == hci->le_adv_enable) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_SET_ADV_ENABLE);
+		return;
+	}
+
+	if (cmd->enable == 0x01)
+		result = start_adv(hci);
+	else
+		result = stop_adv(hci);
+
+	if (!result) {
+		cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_LE_SET_ADV_ENABLE);
+		return;
+	}
+
+	hci->le_adv_enable = cmd->enable;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+						&status, sizeof(status));
+}
+
+static void cmd_le_set_scan_parameters(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_scan_parameters *cmd = data;
+	uint16_t interval, window;
+	uint8_t status;
+
+	if (hci->le_scan_enable == 0x01) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	interval = le16_to_cpu(cmd->interval);
+	window = le16_to_cpu(cmd->window);
+
+	/* Valid range for scan type is 0x00 to 0x01 */
+	if (cmd->type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for scan interval is 0x0004 to 0x4000 */
+	if (interval < 0x0004 || interval > 0x4000) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for scan window is 0x0004 to 0x4000 */
+	if (window < 0x0004 || window > 0x4000) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	/* Scan window shall be less or equal than scan interval */
+	if (window > interval) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for own address type is 0x00 to 0x03 */
+	if (cmd->own_addr_type > 0x03) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	/* Valid range for scanning filter policy is 0x00 to 0x03 */
+	if (cmd->filter_policy > 0x03) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_PARAMETERS);
+		return;
+	}
+
+	hci->le_scan_type = cmd->type;
+	hci->le_scan_interval = interval;
+	hci->le_scan_window = window;
+	hci->le_scan_own_addr_type = cmd->own_addr_type;
+	hci->le_scan_filter_policy = cmd->filter_policy;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS,
+						&status, sizeof(status));
+}
+
+static void cmd_le_set_scan_enable(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_scan_enable *cmd = data;
+	uint8_t status;
+	bool result;
+
+	/* Valid range for scan enable is 0x00 to 0x01 */
+	if (cmd->enable > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_ENABLE);
+		return;
+	}
+
+	/* Valid range for filter duplicates is 0x00 to 0x01 */
+	if (cmd->filter_dup > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_SCAN_ENABLE);
+		return;
+	}
+
+	if (cmd->enable == hci->le_scan_enable) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_SET_SCAN_ENABLE);
+		return;
+	}
+
+	clear_scan_cache(hci);
+
+	if (cmd->enable == 0x01)
+		result = start_scan(hci);
+	else
+		result = stop_scan(hci);
+
+	if (!result) {
+		cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_LE_SET_SCAN_ENABLE);
+		return;
+	}
+
+	hci->le_scan_enable = cmd->enable;
+	hci->le_scan_filter_dup = cmd->filter_dup;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+						&status, sizeof(status));
+}
+
+static void cmd_le_create_conn(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_create_conn *cmd = data;
+
+	if (hci->le_conn_enable == 0x01) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_CREATE_CONN);
+		return;
+	}
+
+	/* Valid range for peer address type is 0x00 to 0x03 */
+	if (cmd->peer_addr_type > 0x03) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_CREATE_CONN);
+		return;
+	}
+
+	/* Valid range for own address type is 0x00 to 0x03 */
+	if (cmd->own_addr_type > 0x03) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_CREATE_CONN);
+		return;
+	}
+
+	hci->le_conn_peer_addr_type = cmd->peer_addr_type;
+	memcpy(hci->le_conn_peer_addr, cmd->peer_addr, 6);
+	hci->le_conn_own_addr_type = cmd->own_addr_type;
+	hci->le_conn_enable = 0x01;
+
+	cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_CREATE_CONN);
+}
+
+static void cmd_le_create_conn_cancel(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_evt_le_conn_complete evt;
+	uint8_t status;
+
+	if (hci->le_conn_enable == 0x00) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_CREATE_CONN_CANCEL);
+		return;
+	}
+
+	hci->le_conn_enable = 0x00;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_CREATE_CONN_CANCEL,
+						&status, sizeof(status));
+
+	evt.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+	evt.handle = cpu_to_le16(0x0000);
+	evt.role = 0x00;
+	evt.peer_addr_type = 0x00;
+	memset(evt.peer_addr, 0, 6);
+	evt.interval = cpu_to_le16(0x0000);
+	evt.latency = cpu_to_le16(0x0000);
+	evt.supv_timeout = cpu_to_le16(0x0000);
+	evt.clock_accuracy = 0x00;
+
+	if (hci->le_event_mask[0] & 0x01)
+		le_meta_event(hci, BT_HCI_EVT_LE_CONN_COMPLETE,
+							&evt, sizeof(evt));
+}
+
+static void cmd_le_read_white_list_size(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_white_list_size rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.size = hci->le_white_list_size;
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_clear_white_list(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	uint8_t status;
+
+	clear_white_list(hci);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_WHITE_LIST,
+						&status, sizeof(status));
+}
+
+static void cmd_le_add_to_white_list(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_add_to_white_list *cmd = data;
+	uint8_t status;
+	bool exists = false;
+	int i, pos = -1;
+
+	/* Valid range for address type is 0x00 to 0x01 */
+	if (cmd->addr_type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_ADD_TO_WHITE_LIST);
+		return;
+	}
+
+	for (i = 0; i < hci->le_white_list_size; i++) {
+		if (hci->le_white_list[i][0] == cmd->addr_type &&
+				!memcmp(&hci->le_white_list[i][1],
+							cmd->addr, 6)) {
+			exists = true;
+			break;
+		} else if (pos < 0 && hci->le_white_list[i][0] == 0xff)
+			pos = i;
+	}
+
+	if (exists) {
+		cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_LE_ADD_TO_WHITE_LIST);
+		return;
+	}
+
+	if (pos < 0) {
+		cmd_status(hci, BT_HCI_ERR_MEM_CAPACITY_EXCEEDED,
+					BT_HCI_CMD_LE_ADD_TO_WHITE_LIST);
+		return;
+	}
+
+	hci->le_white_list[pos][0] = cmd->addr_type;
+	memcpy(&hci->le_white_list[pos][1], cmd->addr, 6);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_ADD_TO_WHITE_LIST,
+						&status, sizeof(status));
+}
+
+static void cmd_le_remove_from_white_list(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_remove_from_white_list *cmd = data;
+	uint8_t status;
+	int i, pos = -1;
+
+	/* Valid range for address type is 0x00 to 0x01 */
+	if (cmd->addr_type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_REMOVE_FROM_WHITE_LIST);
+		return;
+	}
+
+	for (i = 0; i < hci->le_white_list_size; i++) {
+		if (hci->le_white_list[i][0] == cmd->addr_type &&
+				!memcmp(&hci->le_white_list[i][1],
+							cmd->addr, 6)) {
+			pos = i;
+			break;
+		}
+	}
+
+	if (pos < 0) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_REMOVE_FROM_WHITE_LIST);
+		return;
+	}
+
+	hci->le_white_list[pos][0] = 0xff;
+	memset(&hci->le_white_list[pos][1], 0, 6);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_REMOVE_FROM_WHITE_LIST,
+						&status, sizeof(status));
+}
+
+static void cmd_le_encrypt(struct bt_le *hci, const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_encrypt *cmd = data;
+	struct bt_hci_rsp_le_encrypt rsp;
+
+	if (!bt_crypto_e(hci->crypto, cmd->key, cmd->plaintext, rsp.data)) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_ENCRYPT);
+		return;
+	}
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+
+	cmd_complete(hci, BT_HCI_CMD_LE_ENCRYPT, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_rand(struct bt_le *hci, const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_rand rsp;
+	uint8_t value[8];
+
+	if (!bt_crypto_random_bytes(hci->crypto, value, 8)) {
+		cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_RAND);
+		return;
+	}
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(&rsp.number, value, 8);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_RAND, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_supported_states(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_supported_states rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	memcpy(rsp.states, hci->le_states, 8);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_SUPPORTED_STATES,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_data_length(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_data_length *cmd = data;
+	struct bt_hci_rsp_le_set_data_length rsp;
+	uint16_t handle, tx_len, tx_time;
+
+	handle = le16_to_cpu(cmd->handle);
+	tx_len = le16_to_cpu(cmd->tx_len);
+	tx_time = le16_to_cpu(cmd->tx_time);
+
+	/* Valid range for connection handle is 0x0000 to 0x0eff */
+	if (handle > 0x0eff) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_DATA_LENGTH);
+		return;
+	}
+
+	/* Valid range for suggested max TX octets is 0x001b to 0x00fb */
+	if (tx_len < 0x001b || tx_len > 0x00fb) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_DATA_LENGTH);
+		return;
+	}
+
+	/* Valid range for suggested max TX time is 0x0148 to 0x0848 */
+	if (tx_time < 0x0148 || tx_time > 0x0848) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_DATA_LENGTH);
+		return;
+	}
+
+	/* Max TX len and time shall be less or equal supported */
+	if (tx_len > MAX_TX_LEN || tx_time > MAX_TX_TIME) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_DATA_LENGTH);
+		return;
+	}
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.handle = cpu_to_le16(handle);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_DATA_LENGTH, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_default_data_length(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_default_data_length rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.tx_len = cpu_to_le16(hci->le_default_tx_len);
+	rsp.tx_time = cpu_to_le16(hci->le_default_tx_time);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_DEFAULT_DATA_LENGTH,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_write_default_data_length(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_write_default_data_length *cmd = data;
+	uint16_t tx_len, tx_time;
+	uint8_t status;
+
+	tx_len = le16_to_cpu(cmd->tx_len);
+	tx_time = le16_to_cpu(cmd->tx_time);
+
+	/* Valid range for suggested max TX octets is 0x001b to 0x00fb */
+	if (tx_len < 0x001b || tx_len > 0x00fb) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+				BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH);
+		return;
+	}
+
+	/* Valid range for suggested max TX time is 0x0148 to 0x0848 */
+	if (tx_time < 0x0148 || tx_time > 0x0848) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+				BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH);
+		return;
+	}
+
+	/* Suggested max TX len and time shall be less or equal supported */
+	if (tx_len > MAX_TX_LEN || tx_time > MAX_TX_TIME) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+				BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH);
+		return;
+	}
+
+	hci->le_default_tx_len = tx_len;
+	hci->le_default_tx_time = tx_time;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH,
+						&status, sizeof(status));
+}
+
+static void cmd_le_read_local_pk256(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_evt_le_read_local_pk256_complete evt;
+
+	cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_READ_LOCAL_PK256);
+
+	evt.status = BT_HCI_ERR_SUCCESS;
+	ecc_make_key(evt.local_pk256, hci->le_local_sk256);
+
+	if (hci->le_event_mask[0] & 0x80)
+		le_meta_event(hci, BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE,
+							&evt, sizeof(evt));
+}
+
+static void cmd_le_generate_dhkey(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_generate_dhkey *cmd = data;
+	struct bt_hci_evt_le_generate_dhkey_complete evt;
+
+	cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_GENERATE_DHKEY);
+
+	evt.status = BT_HCI_ERR_SUCCESS;
+	ecdh_shared_secret(cmd->remote_pk256, hci->le_local_sk256, evt.dhkey);
+
+	if (hci->le_event_mask[1] & 0x01)
+		le_meta_event(hci, BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE,
+							&evt, sizeof(evt));
+}
+
+static void cmd_le_add_to_resolv_list(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_add_to_resolv_list *cmd = data;
+	uint8_t status;
+	bool exists = false;
+	int i, pos = -1;
+
+	/* Valid range for address type is 0x00 to 0x01 */
+	if (cmd->addr_type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST);
+		return;
+	}
+
+	for (i = 0; i < hci->le_resolv_list_size; i++) {
+		if (hci->le_resolv_list[i][0] == cmd->addr_type &&
+				!memcmp(&hci->le_resolv_list[i][1],
+							cmd->addr, 6)) {
+			exists = true;
+			break;
+		} else if (pos < 0 && hci->le_resolv_list[i][0] == 0xff)
+			pos = i;
+	}
+
+	if (exists) {
+		cmd_status(hci, BT_HCI_ERR_UNSPECIFIED_ERROR,
+					BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST);
+		return;
+	}
+
+	if (pos < 0) {
+		cmd_status(hci, BT_HCI_ERR_MEM_CAPACITY_EXCEEDED,
+					BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST);
+		return;
+	}
+
+	hci->le_resolv_list[pos][0] = cmd->addr_type;
+	memcpy(&hci->le_resolv_list[pos][1], cmd->addr, 6);
+	memcpy(&hci->le_resolv_list[pos][7], cmd->peer_irk, 16);
+	memcpy(&hci->le_resolv_list[pos][23], cmd->local_irk, 16);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST,
+						&status, sizeof(status));
+}
+
+static void cmd_le_remove_from_resolv_list(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_remove_from_resolv_list *cmd = data;
+	uint8_t status;
+	int i, pos = -1;
+
+	/* Valid range for address type is 0x00 to 0x01 */
+	if (cmd->addr_type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST);
+		return;
+	}
+
+	for (i = 0; i < hci->le_resolv_list_size; i++) {
+		if (hci->le_resolv_list[i][0] == cmd->addr_type &&
+				!memcmp(&hci->le_resolv_list[i][1],
+							cmd->addr, 6)) {
+			pos = i;
+			break;
+		}
+	}
+
+	if (pos < 0) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST);
+		return;
+	}
+
+	hci->le_resolv_list[pos][0] = 0xff;
+	memset(&hci->le_resolv_list[pos][1], 0, 38);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST,
+						&status, sizeof(status));
+}
+
+static void cmd_le_clear_resolv_list(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	uint8_t status;
+
+	clear_resolv_list(hci);
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_RESOLV_LIST,
+						&status, sizeof(status));
+}
+
+static void cmd_le_read_resolv_list_size(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_resolv_list_size rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.size = hci->le_resolv_list_size;
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_peer_resolv_addr(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_peer_resolv_addr *cmd = data;
+	struct bt_hci_rsp_le_read_peer_resolv_addr rsp;
+
+	/* Valid range for address type is 0x00 to 0x01 */
+	if (cmd->addr_type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR);
+		return;
+	}
+
+	rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+	memset(rsp.addr, 0, 6);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_local_resolv_addr(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_local_resolv_addr *cmd = data;
+	struct bt_hci_rsp_le_read_local_resolv_addr rsp;
+
+	/* Valid range for address type is 0x00 to 0x01 */
+	if (cmd->addr_type > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR);
+		return;
+	}
+
+	rsp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+	memset(rsp.addr, 0, 6);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR,
+							&rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_resolv_enable(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_resolv_enable *cmd = data;
+	uint8_t status;
+
+	/* Valid range for address resolution enable is 0x00 to 0x01 */
+	if (cmd->enable > 0x01) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_RESOLV_ENABLE);
+		return;
+	}
+
+	hci->le_resolv_enable = cmd->enable;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_RESOLV_ENABLE,
+						&status, sizeof(status));
+}
+
+static void cmd_le_set_resolv_timeout(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_resolv_timeout *cmd = data;
+	uint16_t timeout;
+	uint8_t status;
+
+	timeout = le16_to_cpu(cmd->timeout);
+
+	/* Valid range for RPA timeout is 0x0001 to 0xa1b8 */
+	if (timeout < 0x0001 || timeout > 0xa1b8) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+					BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT);
+		return;
+	}
+
+	hci->le_resolv_timeout = timeout;
+
+	status = BT_HCI_ERR_SUCCESS;
+	cmd_complete(hci, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT,
+						&status, sizeof(status));
+}
+
+static void cmd_le_read_max_data_length(struct bt_le *hci,
+						const void *data, uint8_t size)
+{
+	struct bt_hci_rsp_le_read_max_data_length rsp;
+
+	rsp.status = BT_HCI_ERR_SUCCESS;
+	rsp.max_tx_len = cpu_to_le16(MAX_TX_LEN);
+	rsp.max_tx_time = cpu_to_le16(MAX_TX_TIME);
+	rsp.max_rx_len = cpu_to_le16(MAX_RX_LEN);
+	rsp.max_rx_time = cpu_to_le16(MAX_RX_TIME);
+
+	cmd_complete(hci, BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH,
+							&rsp, sizeof(rsp));
+}
+
+static const struct {
+	uint16_t opcode;
+	void (*func) (struct bt_le *hci, const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+} cmd_table[] = {
+	{ BT_HCI_CMD_DISCONNECT,           cmd_disconnect,           3, true },
+
+	{ BT_HCI_CMD_SET_EVENT_MASK,       cmd_set_event_mask,       8, true },
+	{ BT_HCI_CMD_RESET,                cmd_reset,                0, true },
+	{ BT_HCI_CMD_SET_EVENT_MASK_PAGE2, cmd_set_event_mask_page2, 8, true },
+
+	{ BT_HCI_CMD_READ_LOCAL_VERSION,   cmd_read_local_version,   0, true },
+	{ BT_HCI_CMD_READ_LOCAL_COMMANDS,  cmd_read_local_commands,  0, true },
+	{ BT_HCI_CMD_READ_LOCAL_FEATURES,  cmd_read_local_features,  0, true },
+	{ BT_HCI_CMD_READ_BUFFER_SIZE,     cmd_read_buffer_size,     0, true },
+	{ BT_HCI_CMD_READ_BD_ADDR,         cmd_read_bd_addr,         0, true },
+
+	{ BT_HCI_CMD_LE_SET_EVENT_MASK,
+				cmd_le_set_event_mask, 8, true },
+	{ BT_HCI_CMD_LE_READ_BUFFER_SIZE,
+				cmd_le_read_buffer_size, 0, true },
+	{ BT_HCI_CMD_LE_READ_LOCAL_FEATURES,
+				cmd_le_read_local_features, 0, true },
+	{ BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+				cmd_le_set_random_address, 6, true },
+	{ BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+				cmd_le_set_adv_parameters, 15, true },
+	{ BT_HCI_CMD_LE_READ_ADV_TX_POWER,
+				cmd_le_read_adv_tx_power, 0, true },
+	{ BT_HCI_CMD_LE_SET_ADV_DATA,
+				cmd_le_set_adv_data, 32, true },
+	{ BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+				cmd_le_set_scan_rsp_data, 32, true },
+	{ BT_HCI_CMD_LE_SET_ADV_ENABLE,
+				cmd_le_set_adv_enable, 1, true },
+	{ BT_HCI_CMD_LE_SET_SCAN_PARAMETERS,
+				cmd_le_set_scan_parameters, 7, true },
+	{ BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+				cmd_le_set_scan_enable, 2, true },
+	{ BT_HCI_CMD_LE_CREATE_CONN,
+				cmd_le_create_conn, 25, true },
+	{ BT_HCI_CMD_LE_CREATE_CONN_CANCEL,
+				cmd_le_create_conn_cancel, 0, true },
+	{ BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE,
+				cmd_le_read_white_list_size, 0, true },
+	{ BT_HCI_CMD_LE_CLEAR_WHITE_LIST,
+				cmd_le_clear_white_list, 0, true },
+	{ BT_HCI_CMD_LE_ADD_TO_WHITE_LIST,
+				cmd_le_add_to_white_list,  7, true },
+	{ BT_HCI_CMD_LE_REMOVE_FROM_WHITE_LIST,
+				cmd_le_remove_from_white_list, 7, true },
+
+	{ BT_HCI_CMD_LE_ENCRYPT, cmd_le_encrypt, 32, true },
+	{ BT_HCI_CMD_LE_RAND, cmd_le_rand, 0, true },
+
+	{ BT_HCI_CMD_LE_READ_SUPPORTED_STATES,
+				cmd_le_read_supported_states, 0, true },
+
+	{ BT_HCI_CMD_LE_SET_DATA_LENGTH,
+				cmd_le_set_data_length, 6, true },
+	{ BT_HCI_CMD_LE_READ_DEFAULT_DATA_LENGTH,
+				cmd_le_read_default_data_length, 0, true },
+	{ BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH,
+				cmd_le_write_default_data_length, 4, true },
+	{ BT_HCI_CMD_LE_READ_LOCAL_PK256,
+				cmd_le_read_local_pk256, 0, true },
+	{ BT_HCI_CMD_LE_GENERATE_DHKEY,
+				cmd_le_generate_dhkey, 64, true },
+	{ BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST,
+				cmd_le_add_to_resolv_list,  39, true },
+	{ BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST,
+				cmd_le_remove_from_resolv_list, 7, true },
+	{ BT_HCI_CMD_LE_CLEAR_RESOLV_LIST,
+				cmd_le_clear_resolv_list, 0, true },
+	{ BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE,
+				cmd_le_read_resolv_list_size, 0, true },
+	{ BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR,
+				cmd_le_read_peer_resolv_addr, 7, true },
+	{ BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR,
+				cmd_le_read_local_resolv_addr, 7, true },
+	{ BT_HCI_CMD_LE_SET_RESOLV_ENABLE,
+				cmd_le_set_resolv_enable, 1, true },
+	{ BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT,
+				cmd_le_set_resolv_timeout, 2, true },
+	{ BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH,
+				cmd_le_read_max_data_length, 0, true },
+
+	{ }
+};
+
+static void process_command(struct bt_le *hci, const void *data, size_t size)
+{
+	const struct bt_hci_cmd_hdr *hdr = data;
+	uint16_t opcode;
+	unsigned int i;
+
+	if (size < sizeof(*hdr))
+		return;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	opcode = le16_to_cpu(hdr->opcode);
+
+	if (hdr->plen != size) {
+		cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode);
+		return;
+	}
+
+	for (i = 0; cmd_table[i].func; i++) {
+		if (cmd_table[i].opcode != opcode)
+			continue;
+
+		if ((cmd_table[i].fixed && size != cmd_table[i].size) ||
+						size < cmd_table[i].size) {
+			cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode);
+			return;
+		}
+
+		cmd_table[i].func(hci, data, size);
+		return;
+	}
+
+	cmd_status(hci, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct bt_le *hci = user_data;
+	unsigned char buf[4096];
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+	len = read(hci->vhci_fd, buf, sizeof(buf));
+	if (len < 1)
+		return;
+
+	switch (buf[0]) {
+	case BT_H4_CMD_PKT:
+		process_command(hci, buf + 1, len - 1);
+		break;
+	}
+}
+
+static void phy_recv_callback(uint16_t type, const void *data,
+						size_t size, void *user_data)
+{
+	struct bt_le *hci = user_data;
+
+	switch (type) {
+	case BT_PHY_PKT_ADV:
+		if (!(hci->le_event_mask[0] & 0x02))
+			return;
+
+		if (hci->scan_window_active) {
+			const struct bt_phy_pkt_adv *pkt = data;
+			uint8_t buf[100];
+			struct bt_hci_evt_le_adv_report *evt = (void *) buf;
+			uint8_t tx_addr_type, tx_addr[6];
+
+			if (hci->scan_chan_idx != pkt->chan_idx)
+				break;
+
+			resolve_peer_addr(hci, pkt->tx_addr_type, pkt->tx_addr,
+							&tx_addr_type, tx_addr);
+
+			if (hci->le_scan_filter_policy == 0x01 ||
+					hci->le_scan_filter_policy == 0x03) {
+				if (!is_in_white_list(hci, tx_addr_type,
+								tx_addr))
+					break;
+			}
+
+			if (hci->le_scan_filter_dup) {
+				if (!add_to_scan_cache(hci, tx_addr_type,
+								tx_addr))
+					break;
+			}
+
+			memset(buf, 0, sizeof(buf));
+			evt->num_reports = 0x01;
+			evt->event_type = pkt->pdu_type;
+			evt->addr_type = tx_addr_type;
+			memcpy(evt->addr, tx_addr, 6);
+			evt->data_len = pkt->adv_data_len;
+			memcpy(buf + sizeof(*evt), data + sizeof(*pkt),
+							pkt->adv_data_len);
+
+			le_meta_event(hci, BT_HCI_EVT_LE_ADV_REPORT, buf,
+					sizeof(*evt) + pkt->adv_data_len + 1);
+
+			if (hci->le_scan_type == 0x00)
+				break;
+
+			memset(buf, 0, sizeof(buf));
+			evt->num_reports = 0x01;
+			evt->event_type = 0x04;
+			evt->addr_type = tx_addr_type;
+			memcpy(evt->addr, tx_addr, 6);
+			evt->data_len = pkt->scan_rsp_len;
+			memcpy(buf + sizeof(*evt), data + sizeof(*pkt) +
+							pkt->adv_data_len,
+							pkt->scan_rsp_len);
+
+			le_meta_event(hci, BT_HCI_EVT_LE_ADV_REPORT, buf,
+					sizeof(*evt) + pkt->scan_rsp_len + 1);
+		}
+		break;
+	}
+}
+
+struct bt_le *bt_le_new(void)
+{
+	unsigned char setup_cmd[2];
+	struct bt_le *hci;
+
+	hci = calloc(1, sizeof(*hci));
+	if (!hci)
+		return NULL;
+
+	hci->adv_timeout_id = -1;
+	hci->scan_timeout_id = -1;
+	hci->scan_window_active = false;
+
+	reset_defaults(hci);
+
+	hci->vhci_fd = open("/dev/vhci", O_RDWR);
+	if (hci->vhci_fd < 0) {
+		free(hci);
+		return NULL;
+	}
+
+	setup_cmd[0] = HCI_VENDOR_PKT;
+	setup_cmd[1] = HCI_BREDR;
+
+	if (write(hci->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) {
+		close(hci->vhci_fd);
+		free(hci);
+		return NULL;
+	}
+
+	mainloop_add_fd(hci->vhci_fd, EPOLLIN, vhci_read_callback, hci, NULL);
+
+	hci->phy = bt_phy_new();
+	hci->crypto = bt_crypto_new();
+
+	bt_phy_register(hci->phy, phy_recv_callback, hci);
+
+	return bt_le_ref(hci);
+}
+
+struct bt_le *bt_le_ref(struct bt_le *hci)
+{
+	if (!hci)
+		return NULL;
+
+	__sync_fetch_and_add(&hci->ref_count, 1);
+
+	return hci;
+}
+
+void bt_le_unref(struct bt_le *hci)
+{
+	if (!hci)
+		return;
+
+	if (__sync_sub_and_fetch(&hci->ref_count, 1))
+		return;
+
+	stop_adv(hci);
+
+	bt_crypto_unref(hci->crypto);
+	bt_phy_unref(hci->phy);
+
+	mainloop_remove_fd(hci->vhci_fd);
+
+	close(hci->vhci_fd);
+
+	free(hci);
+}
diff --git a/bluez/repo/emulator/le.h b/bluez/repo/emulator/le.h
new file mode 100644
index 0000000..5e832e8
--- /dev/null
+++ b/bluez/repo/emulator/le.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 bt_le;
+
+struct bt_le *bt_le_new(void);
+
+struct bt_le *bt_le_ref(struct bt_le *le);
+void bt_le_unref(struct bt_le *le);
diff --git a/bluez/repo/emulator/main.c b/bluez/repo/emulator/main.c
new file mode 100644
index 0000000..56246a3
--- /dev/null
+++ b/bluez/repo/emulator/main.c
@@ -0,0 +1,231 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <getopt.h>
+
+#include "src/shared/mainloop.h"
+#include "serial.h"
+#include "server.h"
+#include "vhci.h"
+#include "amp.h"
+#include "le.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("btvirt - Bluetooth emulator\n"
+		"Usage:\n");
+	printf("\tbtvirt [options]\n");
+	printf("options:\n"
+		"\t-S                    Create local serial port\n"
+		"\t-s                    Create local server sockets\n"
+		"\t-l [num]              Number of local controllers\n"
+		"\t-L                    Create LE only controller\n"
+		"\t-B                    Create BR/EDR only controller\n"
+		"\t-A                    Create AMP controller\n"
+		"\t-h, --help            Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "serial",  no_argument,       NULL, 'S' },
+	{ "server",  no_argument,       NULL, 's' },
+	{ "local",   optional_argument, NULL, 'l' },
+	{ "le",      no_argument,       NULL, 'L' },
+	{ "bredr",   no_argument,       NULL, 'B' },
+	{ "amp",     no_argument,       NULL, 'A' },
+	{ "letest",  optional_argument, NULL, 'U' },
+	{ "amptest", optional_argument, NULL, 'T' },
+	{ "version", no_argument,	NULL, 'v' },
+	{ "help",    no_argument,	NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	struct server *server1;
+	struct server *server2;
+	struct server *server3;
+	struct server *server4;
+	struct server *server5;
+	bool server_enabled = false;
+	bool serial_enabled = false;
+	int letest_count = 0;
+	int amptest_count = 0;
+	int vhci_count = 0;
+	enum vhci_type vhci_type = VHCI_TYPE_BREDRLE;
+	sigset_t mask;
+	int i;
+
+	mainloop_init();
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "Ssl::LBAUTvh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'S':
+			serial_enabled = true;
+			break;
+		case 's':
+			server_enabled = true;
+			break;
+		case 'l':
+			if (optarg)
+				vhci_count = atoi(optarg);
+			else
+				vhci_count = 1;
+			break;
+		case 'L':
+			vhci_type = VHCI_TYPE_LE;
+			break;
+		case 'B':
+			vhci_type = VHCI_TYPE_BREDR;
+			break;
+		case 'A':
+			vhci_type = VHCI_TYPE_AMP;
+			break;
+		case 'U':
+			if (optarg)
+				letest_count = atoi(optarg);
+			else
+				letest_count = 1;
+			break;
+		case 'T':
+			if (optarg)
+				amptest_count = atoi(optarg);
+			else
+				amptest_count = 1;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (letest_count < 1 && amptest_count < 1 &&
+			vhci_count < 1 && !server_enabled && !serial_enabled) {
+		fprintf(stderr, "No emulator specified\n");
+		return EXIT_FAILURE;
+	}
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Bluetooth emulator ver %s\n", VERSION);
+
+	for (i = 0; i < letest_count; i++) {
+		struct bt_le *le;
+
+		le = bt_le_new();
+		if (!le) {
+			fprintf(stderr, "Failed to create LE controller\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	for (i = 0; i < amptest_count; i++) {
+		struct bt_amp *amp;
+
+		amp = bt_amp_new();
+		if (!amp) {
+			fprintf(stderr, "Failed to create AMP controller\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	for (i = 0; i < vhci_count; i++) {
+		struct vhci *vhci;
+
+		vhci = vhci_open(vhci_type);
+		if (!vhci) {
+			fprintf(stderr, "Failed to open Virtual HCI device\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (serial_enabled) {
+		struct serial *serial;
+
+		serial = serial_open(SERIAL_TYPE_BREDRLE);
+		if (!serial)
+			fprintf(stderr, "Failed to open serial emulation\n");
+	}
+
+	if (server_enabled) {
+		server1 = server_open_unix(SERVER_TYPE_BREDRLE,
+						"/tmp/bt-server-bredrle");
+		if (!server1)
+			fprintf(stderr, "Failed to open BR/EDR/LE server\n");
+
+		server2 = server_open_unix(SERVER_TYPE_BREDR,
+						"/tmp/bt-server-bredr");
+		if (!server2)
+			fprintf(stderr, "Failed to open BR/EDR server\n");
+
+		server3 = server_open_unix(SERVER_TYPE_AMP,
+						"/tmp/bt-server-amp");
+		if (!server3)
+			fprintf(stderr, "Failed to open AMP server\n");
+
+		server4 = server_open_unix(SERVER_TYPE_LE,
+						"/tmp/bt-server-le");
+		if (!server4)
+			fprintf(stderr, "Failed to open LE server\n");
+
+		server5 = server_open_unix(SERVER_TYPE_MONITOR,
+						"/tmp/bt-server-mon");
+		if (!server5)
+			fprintf(stderr, "Failed to open monitor server\n");
+	}
+
+	return mainloop_run();
+}
diff --git a/bluez/repo/emulator/phy.c b/bluez/repo/emulator/phy.c
new file mode 100644
index 0000000..c63dc38
--- /dev/null
+++ b/bluez/repo/emulator/phy.c
@@ -0,0 +1,298 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <time.h>
+
+#include "src/shared/util.h"
+#include "src/shared/mainloop.h"
+
+#include "phy.h"
+
+#define BT_PHY_PORT 45023
+
+struct bt_phy {
+	volatile int ref_count;
+	int rx_fd;
+	int tx_fd;
+	uint64_t id;
+	bt_phy_callback_func_t callback;
+	void *user_data;
+};
+
+struct bt_phy_hdr {
+	uint64_t id;
+	uint32_t flags;
+	uint16_t type;
+	uint16_t len;
+} __attribute__ ((packed));
+
+static bool 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;
+}
+
+static void phy_rx_callback(int fd, uint32_t events, void *user_data)
+{
+	struct bt_phy *phy = user_data;
+	struct msghdr msg;
+	struct iovec iov[2];
+	struct bt_phy_hdr hdr;
+	unsigned char buf[4096];
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(hdr);
+	iov[1].iov_base = buf;
+	iov[1].iov_len = sizeof(buf);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 2;
+
+	len = recvmsg(phy->rx_fd, &msg, MSG_DONTWAIT);
+	if (len < 0)
+		return;
+
+	if ((size_t) len < sizeof(hdr))
+		return;
+
+	if (le64_to_cpu(hdr.id) == phy->id)
+		return;
+
+	if (len - sizeof(hdr) != le16_to_cpu(hdr.len))
+		return;
+
+	if (phy->callback)
+		phy->callback(le16_to_cpu(hdr.type),
+				buf, len - sizeof(hdr), phy->user_data);
+}
+
+static int create_rx_socket(void)
+{
+	struct sockaddr_in addr;
+	int fd, opt = 1;
+
+	fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		return -1;
+
+	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(BT_PHY_PORT);
+	addr.sin_addr.s_addr = INADDR_BROADCAST;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int create_tx_socket(void)
+{
+	int fd, opt = 1;
+
+	fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		return -1;
+
+	setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
+
+	return fd;
+}
+
+struct bt_phy *bt_phy_new(void)
+{
+	struct bt_phy *phy;
+
+	phy = calloc(1, sizeof(*phy));
+	if (!phy)
+		return NULL;
+
+	phy->rx_fd = create_rx_socket();
+	if (phy->rx_fd < 0) {
+		free(phy);
+		return NULL;
+	}
+
+	phy->tx_fd = create_tx_socket();
+	if (phy->tx_fd < 0) {
+		close(phy->rx_fd);
+		free(phy);
+		return NULL;
+	}
+
+	mainloop_add_fd(phy->rx_fd, EPOLLIN, phy_rx_callback, phy, NULL);
+
+	if (!get_random_bytes(&phy->id, sizeof(phy->id))) {
+		srandom(time(NULL));
+		phy->id = random();
+	}
+
+	bt_phy_send(phy, BT_PHY_PKT_NULL, NULL, 0);
+
+	return bt_phy_ref(phy);
+}
+
+struct bt_phy *bt_phy_ref(struct bt_phy *phy)
+{
+	if (!phy)
+		return NULL;
+
+	__sync_fetch_and_add(&phy->ref_count, 1);
+
+	return phy;
+}
+
+void bt_phy_unref(struct bt_phy *phy)
+{
+	if (!phy)
+		return;
+
+	if (__sync_sub_and_fetch(&phy->ref_count, 1))
+		return;
+
+	mainloop_remove_fd(phy->rx_fd);
+
+	close(phy->tx_fd);
+	close(phy->rx_fd);
+
+	free(phy);
+}
+
+bool bt_phy_send(struct bt_phy *phy, uint16_t type,
+					const void *data, size_t size)
+{
+	return bt_phy_send_vector(phy, type, data, size, NULL, 0, NULL, 0);
+}
+
+bool bt_phy_send_vector(struct bt_phy *phy, uint16_t type,
+					const void *data1, size_t size1,
+					const void *data2, size_t size2,
+					const void *data3, size_t size3)
+{
+	struct bt_phy_hdr hdr;
+	struct sockaddr_in addr;
+	struct msghdr msg;
+	struct iovec iov[4];
+	ssize_t len;
+
+	if (!phy)
+		return false;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(BT_PHY_PORT);
+	addr.sin_addr.s_addr = INADDR_BROADCAST;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_name = &addr;
+	msg.msg_namelen = sizeof(addr);
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 0;
+
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.id = cpu_to_le64(phy->id);
+	hdr.flags = cpu_to_le32(0);
+	hdr.type = cpu_to_le16(type);
+	hdr.len = cpu_to_le16(size1 + size2 + size3);
+
+	iov[msg.msg_iovlen].iov_base = &hdr;
+	iov[msg.msg_iovlen].iov_len = sizeof(hdr);
+	msg.msg_iovlen++;
+
+	if (data1 && size1 > 0) {
+		iov[msg.msg_iovlen].iov_base = (void *) data1;
+		iov[msg.msg_iovlen].iov_len = size1;
+		msg.msg_iovlen++;
+	}
+
+	if (data2 && size2 > 0) {
+		iov[msg.msg_iovlen].iov_base = (void *) data2;
+		iov[msg.msg_iovlen].iov_len = size2;
+		msg.msg_iovlen++;
+	}
+
+	if (data3 && size3 > 0) {
+		iov[msg.msg_iovlen].iov_base = (void *) data3;
+		iov[msg.msg_iovlen].iov_len = size3;
+		msg.msg_iovlen++;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(BT_PHY_PORT);
+	addr.sin_addr.s_addr = INADDR_BROADCAST;
+
+	len = sendmsg(phy->tx_fd, &msg, MSG_DONTWAIT);
+	if (len < 0)
+		return false;
+
+	return true;
+}
+
+bool bt_phy_register(struct bt_phy *phy, bt_phy_callback_func_t callback,
+							void *user_data)
+{
+	if (!phy)
+		return false;
+
+	phy->callback = callback;
+	phy->user_data = user_data;
+
+	return true;
+}
diff --git a/bluez/repo/emulator/phy.h b/bluez/repo/emulator/phy.h
new file mode 100644
index 0000000..d5efa51
--- /dev/null
+++ b/bluez/repo/emulator/phy.h
@@ -0,0 +1,72 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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>
+
+struct bt_phy;
+
+struct bt_phy *bt_phy_new(void);
+
+struct bt_phy *bt_phy_ref(struct bt_phy *phy);
+void bt_phy_unref(struct bt_phy *phy);
+
+bool bt_phy_send(struct bt_phy *phy, uint16_t type,
+					const void *data, size_t size);
+bool bt_phy_send_vector(struct bt_phy *phy, uint16_t type,
+					const void *data1, size_t size1,
+					const void *data2, size_t size2,
+					const void *data3, size_t size3);
+
+typedef void (*bt_phy_callback_func_t)(uint16_t type, const void *data,
+						size_t size, void *user_data);
+
+bool bt_phy_register(struct bt_phy *phy, bt_phy_callback_func_t callback,
+							void *user_data);
+
+#define BT_PHY_PKT_NULL		0x0000
+
+#define BT_PHY_PKT_ADV		0x0001
+struct bt_phy_pkt_adv {
+	uint8_t  chan_idx;
+	uint8_t  pdu_type;
+	uint8_t  tx_addr_type;
+	uint8_t  tx_addr[6];
+	uint8_t  rx_addr_type;
+	uint8_t  rx_addr[6];
+	uint8_t  adv_data_len;
+	uint8_t  scan_rsp_len;
+} __attribute__ ((packed));
+
+#define BT_PHY_PKT_CONN		0x0002
+struct bt_phy_pkt_conn {
+	uint8_t  chan_idx;
+	uint8_t  link_type;
+	uint8_t  tx_addr_type;
+	uint8_t  tx_addr[6];
+	uint8_t  rx_addr_type;
+	uint8_t  rx_addr[6];
+	uint8_t  features[8];
+	uint8_t  id;
+} __attribute__ ((packed));
diff --git a/bluez/repo/emulator/serial.c b/bluez/repo/emulator/serial.c
new file mode 100644
index 0000000..f404b15
--- /dev/null
+++ b/bluez/repo/emulator/serial.c
@@ -0,0 +1,248 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/epoll.h>
+#include <sys/uio.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/mainloop.h"
+#include "btdev.h"
+#include "serial.h"
+
+#define uninitialized_var(x) x = x
+
+struct serial {
+	enum serial_type type;
+	uint16_t id;
+	int fd;
+	char path[PATH_MAX];
+	struct btdev *btdev;
+	uint8_t *pkt_data;
+	uint8_t pkt_type;
+	uint16_t pkt_expect;
+	uint16_t pkt_len;
+	uint16_t pkt_offset;
+};
+
+static void open_pty(struct serial *serial);
+
+static void serial_destroy(void *user_data)
+{
+	struct serial *serial = user_data;
+
+	btdev_destroy(serial->btdev);
+	serial->btdev = NULL;
+
+	close(serial->fd);
+	serial->fd = -1;
+}
+
+static void serial_write_callback(const struct iovec *iov, int iovlen,
+							void *user_data)
+{
+	struct serial *serial = user_data;
+	ssize_t written;
+
+	written = writev(serial->fd, iov, iovlen);
+	if (written < 0)
+		return;
+}
+
+static void serial_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct serial *serial = user_data;
+	static uint8_t buf[4096];
+	uint8_t *ptr = buf;
+	ssize_t len;
+	uint16_t count;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(serial->fd);
+		open_pty(serial);
+		return;
+	}
+
+again:
+	len = read(serial->fd, buf + serial->pkt_offset,
+			sizeof(buf) - serial->pkt_offset);
+	if (len < 0) {
+		if (errno == EAGAIN)
+			goto again;
+		return;
+	}
+
+	if (!serial->btdev)
+		return;
+
+	count = serial->pkt_offset + len;
+
+	while (count > 0) {
+		hci_command_hdr *cmd_hdr;
+
+		if (!serial->pkt_data) {
+			serial->pkt_type = ptr[0];
+
+			switch (serial->pkt_type) {
+			case HCI_COMMAND_PKT:
+				if (count < HCI_COMMAND_HDR_SIZE + 1) {
+					serial->pkt_offset += len;
+					return;
+				}
+				cmd_hdr = (hci_command_hdr *) (ptr + 1);
+				serial->pkt_expect = HCI_COMMAND_HDR_SIZE +
+							cmd_hdr->plen + 1;
+				serial->pkt_data = malloc(serial->pkt_expect);
+				serial->pkt_len = 0;
+				break;
+			default:
+				printf("packet error\n");
+				return;
+			}
+
+			serial->pkt_offset = 0;
+		}
+
+		if (count >= serial->pkt_expect) {
+			memcpy(serial->pkt_data + serial->pkt_len,
+						ptr, serial->pkt_expect);
+			ptr += serial->pkt_expect;
+			count -= serial->pkt_expect;
+
+			btdev_receive_h4(serial->btdev, serial->pkt_data,
+					serial->pkt_len + serial->pkt_expect);
+
+			free(serial->pkt_data);
+			serial->pkt_data = NULL;
+		} else {
+			memcpy(serial->pkt_data + serial->pkt_len, ptr, count);
+			serial->pkt_len += count;
+			serial->pkt_expect -= count;
+			count = 0;
+		}
+	}
+}
+
+static void open_pty(struct serial *serial)
+{
+	enum btdev_type uninitialized_var(type);
+
+	serial->fd = posix_openpt(O_RDWR | O_NOCTTY);
+	if (serial->fd < 0) {
+		perror("Failed to get master pseudo terminal");
+		return;
+	}
+
+	if (grantpt(serial->fd) < 0) {
+		perror("Failed to grant slave pseudo terminal");
+		close(serial->fd);
+		serial->fd = -1;
+		return;
+	}
+
+	if (unlockpt(serial->fd) < 0) {
+		perror("Failed to unlock slave pseudo terminal");
+		close(serial->fd);
+		serial->fd = -1;
+		return;
+	}
+
+	ptsname_r(serial->fd, serial->path, sizeof(serial->path));
+
+	printf("Pseudo terminal at %s\n", serial->path);
+
+	switch (serial->type) {
+	case SERIAL_TYPE_BREDRLE:
+		type = BTDEV_TYPE_BREDRLE;
+		break;
+	case SERIAL_TYPE_BREDR:
+		type = BTDEV_TYPE_BREDR;
+		break;
+	case SERIAL_TYPE_LE:
+		type = BTDEV_TYPE_LE;
+		break;
+	case SERIAL_TYPE_AMP:
+		type = BTDEV_TYPE_AMP;
+		break;
+	}
+
+	serial->btdev = btdev_create(type, serial->id);
+	if (!serial->btdev) {
+		close(serial->fd);
+		serial->fd = -1;
+		return;
+	}
+
+	btdev_set_send_handler(serial->btdev, serial_write_callback, serial);
+
+	if (mainloop_add_fd(serial->fd, EPOLLIN, serial_read_callback,
+						serial, serial_destroy) < 0) {
+		btdev_destroy(serial->btdev);
+		serial->btdev = NULL;
+		close(serial->fd);
+		serial->fd = -1;
+		return;
+	}
+}
+
+struct serial *serial_open(enum serial_type type)
+{
+	struct serial *serial;
+	enum btdev_type uninitialized_var(dev_type);
+
+	serial = malloc(sizeof(*serial));
+	if (!serial)
+		return NULL;
+
+	memset(serial, 0, sizeof(*serial));
+	serial->type = type;
+	serial->id = 0x42;
+
+	open_pty(serial);
+
+	return serial;
+}
+
+void serial_close(struct serial *serial)
+{
+	if (!serial)
+		return;
+
+	mainloop_remove_fd(serial->fd);
+
+	free(serial);
+}
diff --git a/bluez/repo/emulator/serial.h b/bluez/repo/emulator/serial.h
new file mode 100644
index 0000000..4e5a56f
--- /dev/null
+++ b/bluez/repo/emulator/serial.h
@@ -0,0 +1,37 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+enum serial_type {
+	SERIAL_TYPE_BREDRLE,
+	SERIAL_TYPE_BREDR,
+	SERIAL_TYPE_LE,
+	SERIAL_TYPE_AMP,
+};
+
+struct serial;
+
+struct serial *serial_open(enum serial_type type);
+void serial_close(struct serial *serial);
diff --git a/bluez/repo/emulator/server.c b/bluez/repo/emulator/server.c
new file mode 100644
index 0000000..c28b15e
--- /dev/null
+++ b/bluez/repo/emulator/server.c
@@ -0,0 +1,395 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/mainloop.h"
+#include "btdev.h"
+#include "server.h"
+
+#define uninitialized_var(x) x = x
+
+struct server {
+	enum server_type type;
+	uint16_t id;
+	int fd;
+};
+
+struct client {
+	int fd;
+	struct btdev *btdev;
+	uint8_t *pkt_data;
+	uint8_t pkt_type;
+	uint16_t pkt_expect;
+	uint16_t pkt_len;
+	uint16_t pkt_offset;
+};
+
+static void server_destroy(void *user_data)
+{
+	struct server *server = user_data;
+
+	close(server->fd);
+
+	free(server);
+}
+
+static void client_destroy(void *user_data)
+{
+	struct client *client = user_data;
+
+	btdev_destroy(client->btdev);
+
+	close(client->fd);
+
+	free(client);
+}
+
+static void client_write_callback(const struct iovec *iov, int iovlen,
+							void *user_data)
+{
+	struct client *client = user_data;
+	struct msghdr msg;
+	ssize_t written;
+
+	memset(&msg, 0, sizeof(msg));
+
+	msg.msg_iov = (struct iovec *) iov;
+	msg.msg_iovlen = iovlen;
+
+	written = sendmsg(client->fd, &msg, MSG_DONTWAIT);
+	if (written < 0)
+		return;
+}
+
+static void client_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct client *client = user_data;
+	static uint8_t buf[4096];
+	uint8_t *ptr = buf;
+	ssize_t len;
+	uint16_t count;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(client->fd);
+		return;
+	}
+
+again:
+	len = recv(fd, buf + client->pkt_offset,
+			sizeof(buf) - client->pkt_offset, MSG_DONTWAIT);
+	if (len < 0) {
+		if (errno == EAGAIN)
+			goto again;
+		return;
+	}
+
+	if (!client->btdev)
+		return;
+
+	count = client->pkt_offset + len;
+
+	while (count > 0) {
+		hci_command_hdr *cmd_hdr;
+		hci_acl_hdr *acl_hdr;
+
+		if (!client->pkt_data) {
+			client->pkt_type = ptr[0];
+
+			switch (client->pkt_type) {
+			case HCI_COMMAND_PKT:
+				if (count < HCI_COMMAND_HDR_SIZE + 1) {
+					client->pkt_offset += len;
+					return;
+				}
+				cmd_hdr = (hci_command_hdr *) (ptr + 1);
+				client->pkt_expect = HCI_COMMAND_HDR_SIZE +
+							cmd_hdr->plen + 1;
+				client->pkt_data = malloc(client->pkt_expect);
+				client->pkt_len = 0;
+				break;
+			case HCI_ACLDATA_PKT:
+				acl_hdr = (hci_acl_hdr*)(ptr + 1);
+				client->pkt_expect = HCI_ACL_HDR_SIZE + acl_hdr->dlen + 1;
+				client->pkt_data = malloc(client->pkt_expect);
+				client->pkt_len = 0;
+				break;
+			default:
+				printf("packet error\n");
+				return;
+			}
+
+			client->pkt_offset = 0;
+		}
+
+		if (count >= client->pkt_expect) {
+			memcpy(client->pkt_data + client->pkt_len,
+						ptr, client->pkt_expect);
+			ptr += client->pkt_expect;
+			count -= client->pkt_expect;
+
+			btdev_receive_h4(client->btdev, client->pkt_data,
+					client->pkt_len + client->pkt_expect);
+
+			free(client->pkt_data);
+			client->pkt_data = NULL;
+		} else {
+			memcpy(client->pkt_data + client->pkt_len, ptr, count);
+			client->pkt_len += count;
+			client->pkt_expect -= count;
+			count = 0;
+		}
+	}
+}
+
+static int accept_client(int fd)
+{
+	struct sockaddr_un addr;
+	socklen_t len;
+	int nfd;
+
+	memset(&addr, 0, sizeof(addr));
+	len = sizeof(addr);
+
+	if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) {
+		perror("Failed to get socket name");
+		return -1;
+	}
+
+	printf("Request for %s\n", addr.sun_path);
+
+	nfd = accept(fd, (struct sockaddr *) &addr, &len);
+	if (nfd < 0) {
+		perror("Failed to accept client socket");
+		return -1;
+	}
+
+	return nfd;
+}
+
+static void server_accept_callback(int fd, uint32_t events, void *user_data)
+{
+	struct server *server = user_data;
+	struct client *client;
+	enum btdev_type uninitialized_var(type);
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(server->fd);
+		return;
+	}
+
+	client = malloc(sizeof(*client));
+	if (!client)
+		return;
+
+	memset(client, 0, sizeof(*client));
+
+	client->fd = accept_client(server->fd);
+	if (client->fd < 0) {
+		free(client);
+		return;
+	}
+
+	switch (server->type) {
+	case SERVER_TYPE_BREDRLE:
+		type = BTDEV_TYPE_BREDRLE;
+		break;
+	case SERVER_TYPE_BREDR:
+		type = BTDEV_TYPE_BREDR;
+		break;
+	case SERVER_TYPE_LE:
+		type = BTDEV_TYPE_LE;
+		break;
+	case SERVER_TYPE_AMP:
+		type = BTDEV_TYPE_AMP;
+		break;
+	case SERVER_TYPE_MONITOR:
+		goto done;
+	}
+
+	client->btdev = btdev_create(type, server->id);
+	if (!client->btdev) {
+		close(client->fd);
+		free(client);
+		return;
+	}
+
+	btdev_set_send_handler(client->btdev, client_write_callback, client);
+
+done:
+	if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback,
+						client, client_destroy) < 0) {
+		btdev_destroy(client->btdev);
+		close(client->fd);
+		free(client);
+	}
+}
+
+static int open_unix(const char *path)
+{
+	struct sockaddr_un addr;
+	int fd;
+
+	unlink(path);
+
+	fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0) {
+		perror("Failed to open server socket");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strcpy(addr.sun_path, path);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind server socket");
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, 5) < 0) {
+		perror("Failed to listen server socket");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+struct server *server_open_unix(enum server_type type, const char *path)
+{
+	struct server *server;
+
+	server = malloc(sizeof(*server));
+	if (!server)
+		return NULL;
+
+	memset(server, 0, sizeof(*server));
+	server->type = type;
+	server->id = 0x42;
+
+	server->fd = open_unix(path);
+	if (server->fd < 0) {
+		free(server);
+		return NULL;
+	}
+
+	if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback,
+						server, server_destroy) < 0) {
+		close(server->fd);
+		free(server);
+		return NULL;
+	}
+
+	return server;
+}
+
+static int open_tcp(void)
+{
+	struct sockaddr_in addr;
+	int fd, opt = 1;
+
+	fd = socket(PF_INET, SOCK_STREAM, 0);
+	if (fd < 0) {
+		perror("Failed to open server socket");
+		return -1;
+	}
+
+	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = INADDR_ANY;
+	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+	addr.sin_port = htons(45550);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind server socket");
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, 5) < 0) {
+		perror("Failed to listen server socket");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+struct server *server_open_tcp(enum server_type type)
+{
+	struct server *server;
+
+	server = malloc(sizeof(*server));
+	if (!server)
+		return server;
+
+	memset(server, 0, sizeof(*server));
+	server->type = type;
+	server->id = 0x43;
+
+	server->fd = open_tcp();
+	if (server->fd < 0) {
+		free(server);
+		return NULL;
+	}
+
+	if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback,
+						server, server_destroy) < 0) {
+		close(server->fd);
+		free(server);
+		return NULL;
+	}
+
+	return server;
+}
+
+void server_close(struct server *server)
+{
+	if (!server)
+		return;
+
+	mainloop_remove_fd(server->fd);
+}
diff --git a/bluez/repo/emulator/server.h b/bluez/repo/emulator/server.h
new file mode 100644
index 0000000..bf725e7
--- /dev/null
+++ b/bluez/repo/emulator/server.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+enum server_type {
+	SERVER_TYPE_BREDRLE,
+	SERVER_TYPE_BREDR,
+	SERVER_TYPE_LE,
+	SERVER_TYPE_AMP,
+	SERVER_TYPE_MONITOR,
+};
+
+struct server;
+
+struct server *server_open_unix(enum server_type type, const char *path);
+struct server *server_open_tcp(enum server_type type);
+void server_close(struct server *server);
diff --git a/bluez/repo/emulator/smp.c b/bluez/repo/emulator/smp.c
new file mode 100644
index 0000000..e941141
--- /dev/null
+++ b/bluez/repo/emulator/smp.c
@@ -0,0 +1,905 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "src/shared/crypto.h"
+#include "src/shared/ecc.h"
+#include "monitor/bt.h"
+#include "bthost.h"
+
+#define SMP_CID		0x0006
+#define SMP_BREDR_CID	0x0007
+
+#define L2CAP_FC_SMP_BREDR	0x80
+
+#define SMP_PASSKEY_ENTRY_FAILED	0x01
+#define SMP_OOB_NOT_AVAIL		0x02
+#define SMP_AUTH_REQUIREMENTS		0x03
+#define SMP_CONFIRM_FAILED		0x04
+#define SMP_PAIRING_NOTSUPP		0x05
+#define SMP_ENC_KEY_SIZE		0x06
+#define SMP_CMD_NOTSUPP			0x07
+#define SMP_UNSPECIFIED			0x08
+#define SMP_REPEATED_ATTEMPTS		0x09
+#define SMP_INVALID_PARAMS		0x0a
+#define SMP_DHKEY_CHECK_FAILED		0x0b
+#define SMP_NUMERIC_COMP_FAILED		0x0c
+#define SMP_BREDR_PAIRING_IN_PROGRESS	0x0d
+
+#define DIST_ENC_KEY	0x01
+#define DIST_ID_KEY	0x02
+#define DIST_SIGN	0x04
+#define DIST_LINK_KEY	0x08
+
+#define KEY_DIST	(DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN)
+
+#define SC_NO_DIST	(DIST_ENC_KEY | DIST_LINK_KEY)
+
+#define MAX_IO_CAP	0x04
+
+#define SMP_AUTH_NONE		0x00
+#define SMP_AUTH_BONDING	0x01
+#define SMP_AUTH_MITM		0x04
+#define SMP_AUTH_SC		0x08
+#define SMP_AUTH_KEYPRESS	0x10
+
+struct smp {
+	struct bthost *bthost;
+	struct smp_conn *conn;
+	struct bt_crypto *crypto;
+};
+
+struct smp_conn {
+	struct smp *smp;
+	uint16_t handle;
+	uint8_t addr_type;
+	bool out;
+	bool sc;
+	bool initiator;
+	uint8_t method;
+	uint8_t local_key_dist;
+	uint8_t remote_key_dist;
+	uint8_t ia[6];
+	uint8_t ia_type;
+	uint8_t ra[6];
+	uint8_t ra_type;
+	uint8_t tk[16];
+	uint8_t prnd[16];
+	uint8_t rrnd[16];
+	uint8_t pcnf[16];
+	uint8_t preq[7];
+	uint8_t prsp[7];
+	uint8_t ltk[16];
+
+	uint8_t local_sk[32];
+	uint8_t local_pk[64];
+	uint8_t remote_pk[64];
+	uint8_t dhkey[32];
+	uint8_t mackey[16];
+
+	uint8_t passkey_notify;
+	uint8_t passkey_round;
+};
+
+enum {
+	JUST_WORKS,
+	JUST_CFM,
+	REQ_PASSKEY,
+	CFM_PASSKEY,
+	REQ_OOB,
+	DSP_PASSKEY,
+	OVERLAP,
+};
+
+static const uint8_t gen_method[5][5] = {
+	{ JUST_WORKS,  JUST_CFM,    REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+	{ JUST_WORKS,  JUST_CFM,    REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+	{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+	{ JUST_WORKS,  JUST_CFM,    JUST_WORKS,  JUST_WORKS, JUST_CFM    },
+	{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP     },
+};
+
+static const uint8_t sc_method[5][5] = {
+	{ JUST_WORKS,  JUST_CFM,    REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+	{ JUST_WORKS,  CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+	{ DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY },
+	{ JUST_WORKS,  JUST_CFM,    JUST_WORKS,  JUST_WORKS, JUST_CFM    },
+	{ DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+};
+
+static uint8_t get_auth_method(struct smp_conn *conn, uint8_t local_io,
+							uint8_t remote_io)
+{
+	/* If either side has unknown io_caps, use JUST_CFM (which gets
+	 * converted later to JUST_WORKS if we're initiators.
+	 */
+	if (local_io > MAX_IO_CAP || remote_io > MAX_IO_CAP)
+		return JUST_CFM;
+
+	if (conn->sc)
+		return sc_method[remote_io][local_io];
+
+	return gen_method[remote_io][local_io];
+}
+
+static uint8_t sc_select_method(struct smp_conn *conn)
+{
+	struct bt_l2cap_smp_pairing_request *local, *remote;
+	uint8_t local_mitm, remote_mitm, local_io, remote_io, method;
+
+	if (conn->out) {
+		local = (void *) &conn->preq[1];
+		remote = (void *) &conn->prsp[1];
+	} else {
+		local = (void *) &conn->prsp[1];
+		remote = (void *) &conn->preq[1];
+	}
+
+	local_io = local->io_capa;
+	remote_io = remote->io_capa;
+
+	local_mitm = (local->auth_req & SMP_AUTH_MITM);
+	remote_mitm = (remote->auth_req & SMP_AUTH_MITM);
+
+	/* If either side wants MITM, look up the method from the table,
+	 * otherwise use JUST WORKS.
+	 */
+	if (local_mitm || remote_mitm)
+		method = get_auth_method(conn, local_io, remote_io);
+	else
+		method = JUST_WORKS;
+
+	/* Don't confirm locally initiated pairing attempts */
+	if (method == JUST_CFM && conn->initiator)
+		method = JUST_WORKS;
+
+	return method;
+}
+
+static void smp_send(struct smp_conn *conn, uint8_t smp_cmd, const void *data,
+								uint8_t len)
+{
+	struct iovec iov[2];
+	uint16_t cid;
+
+	iov[0].iov_base = &smp_cmd;
+	iov[0].iov_len = 1;
+
+	iov[1].iov_base = (void *) data;
+	iov[1].iov_len = len;
+
+	if (conn->addr_type == BDADDR_BREDR)
+		cid = SMP_BREDR_CID;
+	else
+		cid = SMP_CID;
+
+	bthost_send_cid_v(conn->smp->bthost, conn->handle, cid, iov, 2);
+}
+
+static bool send_public_key(struct smp_conn *conn)
+{
+	if (!ecc_make_key(conn->local_pk, conn->local_sk))
+		return false;
+
+	smp_send(conn, BT_L2CAP_SMP_PUBLIC_KEY, conn->local_pk, 64);
+
+	return true;
+}
+
+static void sc_dhkey_check(struct smp_conn *conn)
+{
+	uint8_t io_cap[3], r[16], a[7], b[7], *local_addr, *remote_addr;
+	struct bt_l2cap_smp_dhkey_check check;
+
+	memcpy(a, conn->ia, 6);
+	memcpy(b, conn->ra, 6);
+	a[6] = conn->ia_type;
+	b[6] = conn->ra_type;
+
+	if (conn->out) {
+		local_addr = a;
+		remote_addr = b;
+		memcpy(io_cap, &conn->preq[1], 3);
+	} else {
+		local_addr = b;
+		remote_addr = a;
+		memcpy(io_cap, &conn->prsp[1], 3);
+	}
+
+	memset(r, 0, sizeof(r));
+
+	bt_crypto_f6(conn->smp->crypto, conn->mackey, conn->prnd, conn->rrnd,
+				r, io_cap, local_addr, remote_addr, check.e);
+
+	smp_send(conn, BT_L2CAP_SMP_DHKEY_CHECK, &check, sizeof(check));
+}
+
+static void sc_mackey_and_ltk(struct smp_conn *conn)
+{
+	uint8_t *na, *nb, a[7], b[7];
+
+	if (conn->out) {
+		na = conn->prnd;
+		nb = conn->rrnd;
+	} else {
+		na = conn->rrnd;
+		nb = conn->prnd;
+	}
+
+	memcpy(a, conn->ia, 6);
+	memcpy(b, conn->ra, 6);
+	a[6] = conn->ia_type;
+	b[6] = conn->ra_type;
+
+	bt_crypto_f5(conn->smp->crypto, conn->dhkey, na, nb, a, b,
+						conn->mackey, conn->ltk);
+}
+
+static uint8_t sc_passkey_send_confirm(struct smp_conn *conn)
+{
+	struct bt_l2cap_smp_pairing_confirm cfm;
+	uint8_t r;
+
+	r = ((conn->passkey_notify >> conn->passkey_round) & 0x01);
+	r |= 0x80;
+
+	if (!bt_crypto_f4(conn->smp->crypto, conn->local_pk, conn->remote_pk,
+					conn->prnd, r, cfm.value))
+		return SMP_UNSPECIFIED;
+
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, &cfm, sizeof(cfm));
+
+	return 0;
+}
+
+static uint8_t sc_passkey_round(struct smp_conn *conn, uint8_t smp_op)
+{
+	uint8_t cfm[16], r;
+
+	/* Ignore the PDU if we've already done 20 rounds (0 - 19) */
+	if (conn->passkey_round >= 20)
+		return 0;
+
+	switch (smp_op) {
+	case BT_L2CAP_SMP_PAIRING_RANDOM:
+		r = ((conn->passkey_notify >> conn->passkey_round) & 0x01);
+		r |= 0x80;
+
+		if (!bt_crypto_f4(conn->smp->crypto, conn->remote_pk,
+					conn->local_pk, conn->rrnd, r, cfm))
+			return SMP_UNSPECIFIED;
+
+		if (memcmp(conn->pcnf, cfm, 16))
+			return SMP_CONFIRM_FAILED;
+
+		conn->passkey_round++;
+
+		if (conn->passkey_round == 20) {
+			/* Generate MacKey and LTK */
+			sc_mackey_and_ltk(conn);
+		}
+
+		/* The round is only complete when the initiator
+		 * receives pairing random.
+		 */
+		if (!conn->out) {
+			smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM,
+					conn->prnd, sizeof(conn->prnd));
+			return 0;
+		}
+
+		/* Start the next round */
+		if (conn->passkey_round != 20)
+			return sc_passkey_round(conn, 0);
+
+		/* Passkey rounds are complete - start DHKey Check */
+		sc_dhkey_check(conn);
+
+		break;
+
+	case BT_L2CAP_SMP_PAIRING_CONFIRM:
+		if (conn->out) {
+			smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM,
+					conn->prnd, sizeof(conn->prnd));
+			return 0;
+		}
+
+		return sc_passkey_send_confirm(conn);
+
+	case BT_L2CAP_SMP_PUBLIC_KEY:
+	default:
+		/* Initiating device starts the round */
+		if (!conn->out)
+			return 0;
+
+		return sc_passkey_send_confirm(conn);
+	}
+
+	return 0;
+}
+
+static bool verify_random(struct smp_conn *conn, const uint8_t rnd[16])
+{
+	uint8_t confirm[16];
+
+	if (!bt_crypto_c1(conn->smp->crypto, conn->tk, conn->rrnd, conn->prsp,
+				conn->preq, conn->ia_type, conn->ia,
+				conn->ra_type, conn->ra, confirm))
+		return false;
+
+	if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf) != 0)) {
+		printf("Confirmation values don't match\n");
+		return false;
+	}
+
+	if (conn->out) {
+		bt_crypto_s1(conn->smp->crypto, conn->tk, conn->rrnd,
+							conn->prnd, conn->ltk);
+		bthost_le_start_encrypt(conn->smp->bthost, conn->handle,
+								conn->ltk);
+	} else {
+		bt_crypto_s1(conn->smp->crypto, conn->tk, conn->prnd,
+							conn->rrnd, conn->ltk);
+	}
+
+	return true;
+}
+
+static void distribute_keys(struct smp_conn *conn)
+{
+	uint8_t buf[16];
+
+	if (conn->local_key_dist & DIST_ENC_KEY) {
+		memset(buf, 0, sizeof(buf));
+		smp_send(conn, BT_L2CAP_SMP_ENCRYPT_INFO, buf, sizeof(buf));
+		smp_send(conn, BT_L2CAP_SMP_MASTER_IDENT, buf, 10);
+	}
+
+	if (conn->local_key_dist & DIST_ID_KEY) {
+		memset(buf, 0, sizeof(buf));
+		smp_send(conn, BT_L2CAP_SMP_IDENT_INFO, buf, sizeof(buf));
+
+		memset(buf, 0, sizeof(buf));
+
+		if (conn->out) {
+			buf[0] = conn->ia_type;
+			memcpy(&buf[1], conn->ia, 6);
+		} else {
+			buf[0] = conn->ra_type;
+			memcpy(&buf[1], conn->ra, 6);
+		}
+
+		smp_send(conn, BT_L2CAP_SMP_IDENT_ADDR_INFO, buf, 7);
+	}
+
+	if (conn->local_key_dist & DIST_SIGN) {
+		memset(buf, 0, sizeof(buf));
+		smp_send(conn, BT_L2CAP_SMP_SIGNING_INFO, buf, sizeof(buf));
+	}
+}
+
+static void pairing_req(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	struct bthost *bthost = conn->smp->bthost;
+	struct bt_l2cap_smp_pairing_response rsp;
+
+	memcpy(conn->preq, data, sizeof(conn->preq));
+
+	if (conn->addr_type == BDADDR_BREDR) {
+		rsp.io_capa	= 0x00;
+		rsp.oob_data	= 0x00;
+		rsp.auth_req	= 0x00;
+	} else {
+		rsp.io_capa	= bthost_get_io_capability(bthost);
+		rsp.oob_data	= 0x00;
+		rsp.auth_req	= bthost_get_auth_req(bthost);
+	}
+
+	rsp.max_key_size	= 0x10;
+	rsp.init_key_dist	= conn->preq[5] & KEY_DIST;
+	rsp.resp_key_dist	= conn->preq[6] & KEY_DIST;
+
+	conn->prsp[0] = BT_L2CAP_SMP_PAIRING_RESPONSE;
+	memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
+
+	conn->local_key_dist	= rsp.resp_key_dist;
+	conn->remote_key_dist	= rsp.init_key_dist;
+
+	if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) ||
+					conn->addr_type == BDADDR_BREDR) {
+		conn->sc = true;
+		conn->local_key_dist &= ~SC_NO_DIST;
+		conn->remote_key_dist &= ~SC_NO_DIST;
+	}
+
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_RESPONSE, &rsp, sizeof(rsp));
+
+	if (conn->addr_type == BDADDR_BREDR)
+		distribute_keys(conn);
+}
+
+static void pairing_rsp(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	struct smp *smp = conn->smp;
+	uint8_t cfm[16];
+
+	memcpy(conn->prsp, data, sizeof(conn->prsp));
+
+	conn->local_key_dist = conn->prsp[5];
+	conn->remote_key_dist = conn->prsp[6];
+
+	if (conn->addr_type == BDADDR_BREDR) {
+		conn->local_key_dist &= ~SC_NO_DIST;
+		conn->remote_key_dist &= ~SC_NO_DIST;
+		distribute_keys(conn);
+		return;
+	}
+
+	if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) ||
+					conn->addr_type == BDADDR_BREDR) {
+		conn->sc = true;
+		conn->local_key_dist &= ~SC_NO_DIST;
+		conn->remote_key_dist &= ~SC_NO_DIST;
+		if (conn->addr_type == BDADDR_BREDR)
+			distribute_keys(conn);
+		else
+			send_public_key(conn);
+		return;
+	}
+
+	bt_crypto_c1(smp->crypto, conn->tk, conn->prnd, conn->prsp,
+			conn->preq, conn->ia_type, conn->ia,
+			conn->ra_type, conn->ra, cfm);
+
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, cfm, sizeof(cfm));
+}
+static void sc_check_confirm(struct smp_conn *conn)
+{
+	if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY) {
+		sc_passkey_round(conn, BT_L2CAP_SMP_PAIRING_CONFIRM);
+		return;
+	}
+
+	if (conn->out)
+		smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd,
+							sizeof(conn->prnd));
+}
+
+static void pairing_cfm(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	uint8_t rsp[16];
+
+	memcpy(conn->pcnf, data + 1, 16);
+
+	if (conn->sc) {
+		sc_check_confirm(conn);
+		return;
+	}
+
+	if (conn->out) {
+		memset(rsp, 0, sizeof(rsp));
+		smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, rsp, sizeof(rsp));
+	} else {
+		bt_crypto_c1(conn->smp->crypto, conn->tk, conn->prnd,
+				conn->prsp, conn->preq, conn->ia_type,
+				conn->ia, conn->ra_type, conn->ra, rsp);
+		smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, rsp, sizeof(rsp));
+	}
+}
+
+static uint8_t sc_random(struct smp_conn *conn)
+{
+	/* Passkey entry has special treatment */
+	if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY)
+		return sc_passkey_round(conn, BT_L2CAP_SMP_PAIRING_RANDOM);
+
+	if (conn->out) {
+		uint8_t cfm[16];
+
+		bt_crypto_f4(conn->smp->crypto, conn->remote_pk,
+					conn->local_pk, conn->rrnd, 0, cfm);
+
+		if (memcmp(conn->pcnf, cfm, 16))
+			return 0x04; /* Confirm Value Failed */
+	} else {
+		smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, conn->prnd, 16);
+	}
+
+	sc_mackey_and_ltk(conn);
+
+	if (conn->out)
+		sc_dhkey_check(conn);
+
+	return 0;
+}
+
+static void pairing_rnd(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	uint8_t rsp[16];
+
+	memcpy(conn->rrnd, data + 1, 16);
+
+	if (conn->sc) {
+		uint8_t reason = sc_random(conn);
+		if (reason)
+			smp_send(conn, BT_L2CAP_SMP_PAIRING_FAILED, &reason,
+							sizeof(reason));
+		return;
+	}
+
+	if (!verify_random(conn, data + 1))
+		return;
+
+	if (conn->out)
+		return;
+
+	memset(rsp, 0, sizeof(rsp));
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_RANDOM, rsp, sizeof(rsp));
+}
+
+static void encrypt_info(struct smp_conn *conn, const void *data, uint16_t len)
+{
+}
+
+static void master_ident(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	conn->remote_key_dist &= ~DIST_ENC_KEY;
+
+	if (conn->out && !conn->remote_key_dist)
+		distribute_keys(conn);
+}
+
+static void ident_addr_info(struct smp_conn *conn, const void *data,
+								uint16_t len)
+{
+}
+
+static void ident_info(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	conn->remote_key_dist &= ~DIST_ID_KEY;
+
+	if (conn->out && !conn->remote_key_dist)
+		distribute_keys(conn);
+}
+
+static void signing_info(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	conn->remote_key_dist &= ~DIST_SIGN;
+
+	if (conn->out && !conn->remote_key_dist)
+		distribute_keys(conn);
+}
+
+static void public_key(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	struct smp *smp = conn->smp;
+	uint8_t buf[16];
+
+	memcpy(conn->remote_pk, data + 1, 64);
+
+	if (!conn->out) {
+		if (!send_public_key(conn))
+			return;
+	}
+
+	if (!ecdh_shared_secret(conn->remote_pk, conn->local_sk, conn->dhkey))
+		return;
+
+	conn->method = sc_select_method(conn);
+
+	if (conn->method == DSP_PASSKEY || conn->method == REQ_PASSKEY) {
+		sc_passkey_round(conn, BT_L2CAP_SMP_PUBLIC_KEY);
+		return;
+	}
+
+	if (conn->out)
+		return;
+
+	if (!bt_crypto_f4(smp->crypto, conn->local_pk, conn->remote_pk,
+							conn->prnd, 0, buf))
+		return;
+
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_CONFIRM, buf, sizeof(buf));
+}
+
+static void dhkey_check(struct smp_conn *conn, const void *data, uint16_t len)
+{
+	const struct bt_l2cap_smp_dhkey_check *cmd = data + 1;
+	uint8_t a[7], b[7], *local_addr, *remote_addr;
+	uint8_t io_cap[3], r[16], e[16];
+
+	memcpy(a, &conn->ia, 6);
+	memcpy(b, &conn->ra, 6);
+	a[6] = conn->ia_type;
+	b[6] = conn->ra_type;
+
+	if (conn->out) {
+		local_addr = a;
+		remote_addr = b;
+		memcpy(io_cap, &conn->prsp[1], 3);
+	} else {
+		local_addr = b;
+		remote_addr = a;
+		memcpy(io_cap, &conn->preq[1], 3);
+	}
+
+	memset(r, 0, sizeof(r));
+
+	if (conn->method == REQ_PASSKEY || conn->method == DSP_PASSKEY)
+		put_le32(conn->passkey_notify, r);
+
+	if (!bt_crypto_f6(conn->smp->crypto, conn->mackey, conn->rrnd,
+			conn->prnd, r, io_cap, remote_addr, local_addr, e))
+		return;
+
+	if (memcmp(cmd->e, e, 16)) {
+		uint8_t reason = 0x0b; /* DHKey Check Failed */
+		smp_send(conn, BT_L2CAP_SMP_PAIRING_FAILED, &reason,
+							sizeof(reason));
+	}
+
+	if (conn->out)
+		bthost_le_start_encrypt(conn->smp->bthost, conn->handle,
+								conn->ltk);
+	else
+		sc_dhkey_check(conn);
+}
+
+void smp_pair(void *conn_data, uint8_t io_cap, uint8_t auth_req)
+{
+	struct smp_conn *conn = conn_data;
+	struct bt_l2cap_smp_pairing_request req;
+
+	req.io_capa		= io_cap;
+	req.oob_data		= 0x00;
+	req.auth_req		= auth_req;
+	req.max_key_size	= 0x10;
+	req.init_key_dist	= KEY_DIST;
+	req.resp_key_dist	= KEY_DIST;
+
+	conn->preq[0] = BT_L2CAP_SMP_PAIRING_REQUEST;
+	memcpy(&conn->preq[1], &req, sizeof(req));
+
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req));
+}
+
+void smp_data(void *conn_data, const void *data, uint16_t len)
+{
+	struct smp_conn *conn = conn_data;
+	uint8_t opcode;
+
+	if (len < 1) {
+		printf("Received too small SMP PDU\n");
+		return;
+	}
+
+	if (conn->addr_type == BDADDR_BREDR) {
+		printf("Received BR/EDR SMP data on LE link\n");
+		return;
+	}
+
+	opcode = *((const uint8_t *) data);
+
+	switch (opcode) {
+	case BT_L2CAP_SMP_PAIRING_REQUEST:
+		pairing_req(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_PAIRING_RESPONSE:
+		pairing_rsp(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_PAIRING_CONFIRM:
+		pairing_cfm(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_PAIRING_RANDOM:
+		pairing_rnd(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_ENCRYPT_INFO:
+		encrypt_info(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_MASTER_IDENT:
+		master_ident(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_IDENT_ADDR_INFO:
+		ident_addr_info(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_IDENT_INFO:
+		ident_info(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_SIGNING_INFO:
+		signing_info(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_PUBLIC_KEY:
+		public_key(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_DHKEY_CHECK:
+		dhkey_check(conn, data, len);
+		break;
+	default:
+		break;
+	}
+}
+
+void smp_bredr_data(void *conn_data, const void *data, uint16_t len)
+{
+	struct smp_conn *conn = conn_data;
+	uint8_t opcode;
+
+	if (len < 1) {
+		printf("Received too small SMP PDU\n");
+		return;
+	}
+
+	if (conn->addr_type != BDADDR_BREDR) {
+		printf("Received LE SMP data on BR/EDR link\n");
+		return;
+	}
+
+	opcode = *((const uint8_t *) data);
+
+	switch (opcode) {
+	case BT_L2CAP_SMP_PAIRING_REQUEST:
+		pairing_req(conn, data, len);
+		break;
+	case BT_L2CAP_SMP_PAIRING_RESPONSE:
+		pairing_rsp(conn, data, len);
+		break;
+	default:
+		break;
+	}
+}
+
+int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk)
+{
+	struct smp_conn *conn = smp_data;
+	static const uint8_t no_ltk[16] = { 0 };
+
+	if (!memcmp(conn->ltk, no_ltk, 16))
+		return -ENOENT;
+
+	memcpy(ltk, conn->ltk, 16);
+
+	return 0;
+}
+
+static void smp_conn_bredr(struct smp_conn *conn, uint8_t encrypt)
+{
+	struct smp *smp = conn->smp;
+	struct bt_l2cap_smp_pairing_request req;
+	uint64_t fixed_chan;
+
+	if (encrypt != 0x02)
+		return;
+
+	conn->sc = true;
+
+	if (!conn->out)
+		return;
+
+	fixed_chan = bthost_conn_get_fixed_chan(smp->bthost, conn->handle);
+	if (!(fixed_chan & L2CAP_FC_SMP_BREDR))
+		return;
+
+	memset(&req, 0, sizeof(req));
+	req.max_key_size = 0x10;
+	req.init_key_dist = KEY_DIST;
+	req.resp_key_dist = KEY_DIST;
+
+	smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req));
+}
+
+void smp_conn_encrypted(void *conn_data, uint8_t encrypt)
+{
+	struct smp_conn *conn = conn_data;
+
+	if (!encrypt)
+		return;
+
+	if (conn->addr_type == BDADDR_BREDR) {
+		smp_conn_bredr(conn, encrypt);
+		return;
+	}
+
+	if (conn->out && conn->remote_key_dist)
+		return;
+
+	distribute_keys(conn);
+}
+
+void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia,
+			const uint8_t *ra, uint8_t addr_type, bool conn_init)
+{
+	struct smp *smp = smp_data;
+	struct smp_conn *conn;
+
+	conn = malloc(sizeof(struct smp_conn));
+	if (!conn)
+		return NULL;
+
+	memset(conn, 0, sizeof(*conn));
+
+	conn->smp = smp;
+	conn->handle = handle;
+	conn->addr_type = addr_type;
+	conn->out = conn_init;
+
+	conn->ia_type = LE_PUBLIC_ADDRESS;
+	conn->ra_type = LE_PUBLIC_ADDRESS;
+	memcpy(conn->ia, ia, 6);
+	memcpy(conn->ra, ra, 6);
+
+	return conn;
+}
+
+void smp_conn_del(void *conn_data)
+{
+	struct smp_conn *conn = conn_data;
+
+	free(conn);
+}
+
+void *smp_start(struct bthost *bthost)
+{
+	struct smp *smp;
+
+	smp = malloc(sizeof(struct smp));
+	if (!smp)
+		return NULL;
+
+	memset(smp, 0, sizeof(*smp));
+
+	smp->crypto = bt_crypto_new();
+	if (!smp->crypto) {
+		free(smp);
+		return NULL;
+	}
+
+	smp->bthost = bthost;
+
+	return smp;
+}
+
+void smp_stop(void *smp_data)
+{
+	struct smp *smp = smp_data;
+
+	bt_crypto_unref(smp->crypto);
+
+	free(smp);
+}
diff --git a/bluez/repo/emulator/vhci.c b/bluez/repo/emulator/vhci.c
new file mode 100644
index 0000000..6bba4e2
--- /dev/null
+++ b/bluez/repo/emulator/vhci.c
@@ -0,0 +1,172 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/mainloop.h"
+#include "monitor/bt.h"
+#include "btdev.h"
+#include "vhci.h"
+
+#define uninitialized_var(x) x = x
+
+struct vhci {
+	enum vhci_type type;
+	int fd;
+	struct btdev *btdev;
+};
+
+static void vhci_destroy(void *user_data)
+{
+	struct vhci *vhci = user_data;
+
+	btdev_destroy(vhci->btdev);
+
+	close(vhci->fd);
+
+	free(vhci);
+}
+
+static void vhci_write_callback(const struct iovec *iov, int iovlen,
+							void *user_data)
+{
+	struct vhci *vhci = user_data;
+	ssize_t written;
+
+	written = writev(vhci->fd, iov, iovlen);
+	if (written < 0)
+		return;
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct vhci *vhci = user_data;
+	unsigned char buf[4096];
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+	len = read(vhci->fd, buf, sizeof(buf));
+	if (len < 1)
+		return;
+
+	switch (buf[0]) {
+	case BT_H4_CMD_PKT:
+	case BT_H4_ACL_PKT:
+	case BT_H4_SCO_PKT:
+		btdev_receive_h4(vhci->btdev, buf, len);
+		break;
+	}
+}
+
+struct vhci *vhci_open(enum vhci_type type)
+{
+	struct vhci *vhci;
+	enum btdev_type uninitialized_var(btdev_type);
+	unsigned char uninitialized_var(ctrl_type);
+	unsigned char setup_cmd[2];
+	static uint8_t id = 0x23;
+
+	switch (type) {
+	case VHCI_TYPE_BREDRLE:
+		btdev_type = BTDEV_TYPE_BREDRLE;
+		ctrl_type = HCI_BREDR;
+		break;
+	case VHCI_TYPE_BREDR:
+		btdev_type = BTDEV_TYPE_BREDR;
+		ctrl_type = HCI_BREDR;
+		break;
+	case VHCI_TYPE_LE:
+		btdev_type = BTDEV_TYPE_LE;
+		ctrl_type = HCI_BREDR;
+		break;
+	case VHCI_TYPE_AMP:
+		btdev_type = BTDEV_TYPE_AMP;
+		ctrl_type = HCI_AMP;
+		break;
+	}
+
+	vhci = malloc(sizeof(*vhci));
+	if (!vhci)
+		return NULL;
+
+	memset(vhci, 0, sizeof(*vhci));
+	vhci->type = type;
+
+	vhci->fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+	if (vhci->fd < 0) {
+		free(vhci);
+		return NULL;
+	}
+
+	setup_cmd[0] = HCI_VENDOR_PKT;
+	setup_cmd[1] = ctrl_type;
+
+	if (write(vhci->fd, setup_cmd, sizeof(setup_cmd)) < 0) {
+		close(vhci->fd);
+		free(vhci);
+		return NULL;
+	}
+
+	vhci->btdev = btdev_create(btdev_type, id++);
+	if (!vhci->btdev) {
+		close(vhci->fd);
+		free(vhci);
+		return NULL;
+	}
+
+	btdev_set_send_handler(vhci->btdev, vhci_write_callback, vhci);
+
+	if (mainloop_add_fd(vhci->fd, EPOLLIN, vhci_read_callback,
+						vhci, vhci_destroy) < 0) {
+		btdev_destroy(vhci->btdev);
+		close(vhci->fd);
+		free(vhci);
+		return NULL;
+	}
+
+	return vhci;
+}
+
+void vhci_close(struct vhci *vhci)
+{
+	if (!vhci)
+		return;
+
+	mainloop_remove_fd(vhci->fd);
+}
diff --git a/bluez/repo/emulator/vhci.h b/bluez/repo/emulator/vhci.h
new file mode 100644
index 0000000..1ec7191
--- /dev/null
+++ b/bluez/repo/emulator/vhci.h
@@ -0,0 +1,37 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+enum vhci_type {
+	VHCI_TYPE_BREDRLE,
+	VHCI_TYPE_BREDR,
+	VHCI_TYPE_LE,
+	VHCI_TYPE_AMP,
+};
+
+struct vhci;
+
+struct vhci *vhci_open(enum vhci_type type);
+void vhci_close(struct vhci *vhci);
diff --git a/bluez/repo/gdbus/._client.c b/bluez/repo/gdbus/._client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gdbus/._client.c
Binary files differ
diff --git a/bluez/repo/gdbus/._gdbus.h b/bluez/repo/gdbus/._gdbus.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gdbus/._gdbus.h
Binary files differ
diff --git a/bluez/repo/gdbus/._mainloop.c b/bluez/repo/gdbus/._mainloop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gdbus/._mainloop.c
Binary files differ
diff --git a/bluez/repo/gdbus/._object.c b/bluez/repo/gdbus/._object.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gdbus/._object.c
Binary files differ
diff --git a/bluez/repo/gdbus/._polkit.c b/bluez/repo/gdbus/._polkit.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gdbus/._polkit.c
Binary files differ
diff --git a/bluez/repo/gdbus/._watch.c b/bluez/repo/gdbus/._watch.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gdbus/._watch.c
Binary files differ
diff --git a/bluez/repo/gdbus/client.c b/bluez/repo/gdbus/client.c
new file mode 100644
index 0000000..48711ae
--- /dev/null
+++ b/bluez/repo/gdbus/client.c
@@ -0,0 +1,1398 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define METHOD_CALL_TIMEOUT (300 * 1000)
+
+#ifndef DBUS_INTERFACE_OBJECT_MANAGER
+#define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager"
+#endif
+
+struct GDBusClient {
+	int ref_count;
+	DBusConnection *dbus_conn;
+	char *service_name;
+	char *base_path;
+	char *root_path;
+	guint watch;
+	guint added_watch;
+	guint removed_watch;
+	GPtrArray *match_rules;
+	DBusPendingCall *pending_call;
+	DBusPendingCall *get_objects_call;
+	GDBusWatchFunction connect_func;
+	void *connect_data;
+	GDBusWatchFunction disconn_func;
+	gboolean connected;
+	void *disconn_data;
+	GDBusMessageFunction signal_func;
+	void *signal_data;
+	GDBusProxyFunction proxy_added;
+	GDBusProxyFunction proxy_removed;
+	GDBusClientFunction ready;
+	void *ready_data;
+	GDBusPropertyFunction property_changed;
+	void *user_data;
+	GList *proxy_list;
+};
+
+struct GDBusProxy {
+	int ref_count;
+	GDBusClient *client;
+	char *obj_path;
+	char *interface;
+	GHashTable *prop_list;
+	guint watch;
+	GDBusPropertyFunction prop_func;
+	void *prop_data;
+	GDBusProxyFunction removed_func;
+	void *removed_data;
+};
+
+struct prop_entry {
+	char *name;
+	int type;
+	DBusMessage *msg;
+};
+
+static void modify_match_reply(DBusPendingCall *call, void *user_data)
+{
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, reply) == TRUE)
+		dbus_error_free(&error);
+
+	dbus_message_unref(reply);
+}
+
+static gboolean modify_match(DBusConnection *conn, const char *member,
+							const char *rule)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+					DBUS_INTERFACE_DBUS, member);
+	if (msg == NULL)
+		return FALSE;
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule,
+						DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(conn, msg, &call, -1) == FALSE) {
+		dbus_message_unref(msg);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL);
+	dbus_pending_call_unref(call);
+
+	dbus_message_unref(msg);
+
+	return TRUE;
+}
+
+static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter)
+{
+	int type;
+
+	type = dbus_message_iter_get_arg_type(iter);
+
+	if (dbus_type_is_basic(type)) {
+		const void *value;
+
+		dbus_message_iter_get_basic(iter, &value);
+		dbus_message_iter_append_basic(base, type, &value);
+	} else if (dbus_type_is_container(type)) {
+		DBusMessageIter iter_sub, base_sub;
+		char *sig;
+
+		dbus_message_iter_recurse(iter, &iter_sub);
+
+		switch (type) {
+		case DBUS_TYPE_ARRAY:
+		case DBUS_TYPE_VARIANT:
+			sig = dbus_message_iter_get_signature(&iter_sub);
+			break;
+		default:
+			sig = NULL;
+			break;
+		}
+
+		dbus_message_iter_open_container(base, type, sig, &base_sub);
+
+		if (sig != NULL)
+			dbus_free(sig);
+
+		while (dbus_message_iter_get_arg_type(&iter_sub) !=
+							DBUS_TYPE_INVALID) {
+			iter_append_iter(&base_sub, &iter_sub);
+			dbus_message_iter_next(&iter_sub);
+		}
+
+		dbus_message_iter_close_container(base, &base_sub);
+	}
+}
+
+static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter)
+{
+	DBusMessage *msg;
+	DBusMessageIter base;
+
+	msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &base);
+	iter_append_iter(&base, iter);
+
+	if (prop->msg != NULL)
+		dbus_message_unref(prop->msg);
+
+	prop->msg = dbus_message_copy(msg);
+	dbus_message_unref(msg);
+}
+
+static struct prop_entry *prop_entry_new(const char *name,
+						DBusMessageIter *iter)
+{
+	struct prop_entry *prop;
+
+	prop = g_try_new0(struct prop_entry, 1);
+	if (prop == NULL)
+		return NULL;
+
+	prop->name = g_strdup(name);
+	prop->type = dbus_message_iter_get_arg_type(iter);
+
+	prop_entry_update(prop, iter);
+
+	return prop;
+}
+
+static void prop_entry_free(gpointer data)
+{
+	struct prop_entry *prop = data;
+
+	if (prop->msg != NULL)
+		dbus_message_unref(prop->msg);
+
+	g_free(prop->name);
+
+	g_free(prop);
+}
+
+static void add_property(GDBusProxy *proxy, const char *name,
+				DBusMessageIter *iter, gboolean send_changed)
+{
+	GDBusClient *client = proxy->client;
+	DBusMessageIter value;
+	struct prop_entry *prop;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+		return;
+
+	dbus_message_iter_recurse(iter, &value);
+
+	prop = g_hash_table_lookup(proxy->prop_list, name);
+	if (prop != NULL) {
+		prop_entry_update(prop, &value);
+		goto done;
+	}
+
+	prop = prop_entry_new(name, &value);
+	if (prop == NULL)
+		return;
+
+	g_hash_table_replace(proxy->prop_list, prop->name, prop);
+
+done:
+	if (proxy->prop_func)
+		proxy->prop_func(proxy, name, &value, proxy->prop_data);
+
+	if (client == NULL || send_changed == FALSE)
+		return;
+
+	if (client->property_changed)
+		client->property_changed(proxy, name, &value,
+							client->user_data);
+}
+
+static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter,
+							gboolean send_changed)
+{
+	DBusMessageIter dict;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+		const char *name;
+
+		dbus_message_iter_recurse(&dict, &entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			break;
+
+		dbus_message_iter_get_basic(&entry, &name);
+		dbus_message_iter_next(&entry);
+
+		add_property(proxy, name, &entry, send_changed);
+
+		dbus_message_iter_next(&dict);
+	}
+}
+
+static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+	GDBusClient *client = proxy->client;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusMessageIter iter;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, reply) == TRUE) {
+		dbus_error_free(&error);
+		goto done;
+	}
+
+	dbus_message_iter_init(reply, &iter);
+
+	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);
+	}
+
+	dbus_message_unref(reply);
+
+	g_dbus_client_unref(client);
+}
+
+static void get_all_properties(GDBusProxy *proxy)
+{
+	GDBusClient *client = proxy->client;
+	const char *service_name = client->service_name;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(service_name, proxy->obj_path,
+					DBUS_INTERFACE_PROPERTIES, "GetAll");
+	if (msg == NULL)
+		return;
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface,
+							DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+							&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_message_unref(msg);
+}
+
+static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path,
+						const char *interface)
+{
+	GList *list;
+
+	for (list = g_list_first(client->proxy_list); list;
+						list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+
+		if (g_str_equal(proxy->interface, interface) == TRUE &&
+				g_str_equal(proxy->obj_path, path) == TRUE)
+			return proxy;
+        }
+
+	return NULL;
+}
+
+static gboolean properties_changed(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+	GDBusClient *client = proxy->client;
+	DBusMessageIter iter, entry;
+	const char *interface;
+
+	if (dbus_message_iter_init(msg, &iter) == FALSE)
+		return TRUE;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return TRUE;
+
+	dbus_message_iter_get_basic(&iter, &interface);
+	dbus_message_iter_next(&iter);
+
+	update_properties(proxy, &iter, TRUE);
+
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return TRUE;
+
+	dbus_message_iter_recurse(&iter, &entry);
+
+	while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+		const char *name;
+
+		dbus_message_iter_get_basic(&entry, &name);
+
+		g_hash_table_remove(proxy->prop_list, name);
+
+		if (proxy->prop_func)
+			proxy->prop_func(proxy, name, NULL, proxy->prop_data);
+
+		if (client->property_changed)
+			client->property_changed(proxy, name, NULL,
+							client->user_data);
+
+		dbus_message_iter_next(&entry);
+	}
+
+	return TRUE;
+}
+
+static GDBusProxy *proxy_new(GDBusClient *client, const char *path,
+						const char *interface)
+{
+	GDBusProxy *proxy;
+
+	proxy = g_try_new0(GDBusProxy, 1);
+	if (proxy == NULL)
+		return NULL;
+
+	proxy->client = client;
+	proxy->obj_path = g_strdup(path);
+	proxy->interface = g_strdup(interface);
+
+	proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+							NULL, prop_entry_free);
+	proxy->watch = g_dbus_add_properties_watch(client->dbus_conn,
+							client->service_name,
+							proxy->obj_path,
+							proxy->interface,
+							properties_changed,
+							proxy, NULL);
+
+	return g_dbus_proxy_ref(proxy);
+}
+
+static void proxy_free(gpointer data)
+{
+	GDBusProxy *proxy = data;
+
+	if (proxy->client) {
+		GDBusClient *client = proxy->client;
+
+		if (client->proxy_removed)
+			client->proxy_removed(proxy, client->user_data);
+
+		g_dbus_remove_watch(client->dbus_conn, proxy->watch);
+
+		g_hash_table_remove_all(proxy->prop_list);
+
+		proxy->client = NULL;
+	}
+
+	if (proxy->removed_func)
+		proxy->removed_func(proxy, proxy->removed_data);
+
+	g_dbus_proxy_unref(proxy);
+}
+
+static void proxy_remove(GDBusClient *client, const char *path,
+						const char *interface)
+{
+	GList *list;
+
+	for (list = g_list_first(client->proxy_list); list;
+						list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+
+		if (g_str_equal(proxy->interface, interface) == TRUE &&
+				g_str_equal(proxy->obj_path, path) == TRUE) {
+			client->proxy_list =
+				g_list_delete_link(client->proxy_list, list);
+			proxy_free(proxy);
+			break;
+		}
+	}
+}
+
+GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
+							const char *interface)
+{
+	GDBusProxy *proxy;
+
+	if (client == NULL)
+		return NULL;
+
+	proxy = proxy_lookup(client, path, interface);
+	if (proxy)
+		return g_dbus_proxy_ref(proxy);
+
+	proxy = proxy_new(client, path, interface);
+	if (proxy == NULL)
+		return NULL;
+
+	get_all_properties(proxy);
+
+	return g_dbus_proxy_ref(proxy);
+}
+
+GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy)
+{
+	if (proxy == NULL)
+		return NULL;
+
+	__sync_fetch_and_add(&proxy->ref_count, 1);
+
+	return proxy;
+}
+
+void g_dbus_proxy_unref(GDBusProxy *proxy)
+{
+	if (proxy == NULL)
+		return;
+
+	if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0)
+		return;
+
+	g_hash_table_destroy(proxy->prop_list);
+
+	g_free(proxy->obj_path);
+	g_free(proxy->interface);
+
+	g_free(proxy);
+}
+
+const char *g_dbus_proxy_get_path(GDBusProxy *proxy)
+{
+	if (proxy == NULL)
+		return NULL;
+
+	return proxy->obj_path;
+}
+
+const char *g_dbus_proxy_get_interface(GDBusProxy *proxy)
+{
+	if (proxy == NULL)
+		return NULL;
+
+	return proxy->interface;
+}
+
+gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
+                                                        DBusMessageIter *iter)
+{
+	struct prop_entry *prop;
+
+	if (proxy == NULL || name == NULL)
+		return FALSE;
+
+	prop = g_hash_table_lookup(proxy->prop_list, name);
+	if (prop == NULL)
+		return FALSE;
+
+	if (prop->msg == NULL)
+		return FALSE;
+
+	if (dbus_message_iter_init(prop->msg, iter) == FALSE)
+		return FALSE;
+
+	return TRUE;
+}
+
+struct refresh_property_data {
+	GDBusProxy *proxy;
+	char *name;
+};
+
+static void refresh_property_free(gpointer user_data)
+{
+	struct refresh_property_data *data = user_data;
+
+	g_free(data->name);
+	g_free(data);
+}
+
+static void refresh_property_reply(DBusPendingCall *call, void *user_data)
+{
+	struct refresh_property_data *data = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, reply) == FALSE) {
+		DBusMessageIter iter;
+
+		dbus_message_iter_init(reply, &iter);
+
+		add_property(data->proxy, data->name, &iter, TRUE);
+	} else
+		dbus_error_free(&error);
+
+	dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name)
+{
+	struct refresh_property_data *data;
+	GDBusClient *client;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	DBusPendingCall *call;
+
+	if (proxy == NULL || name == NULL)
+		return FALSE;
+
+	client = proxy->client;
+	if (client == NULL)
+		return FALSE;
+
+	data = g_try_new0(struct refresh_property_data, 1);
+	if (data == NULL)
+		return FALSE;
+
+	data->proxy = proxy;
+	data->name = g_strdup(name);
+
+	msg = dbus_message_new_method_call(client->service_name,
+			proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get");
+	if (msg == NULL) {
+		refresh_property_free(data);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+							&proxy->interface);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+							&call, -1) == FALSE) {
+		dbus_message_unref(msg);
+		refresh_property_free(data);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(call, refresh_property_reply,
+						data, refresh_property_free);
+	dbus_pending_call_unref(call);
+
+	dbus_message_unref(msg);
+
+	return TRUE;
+}
+
+struct set_property_data {
+	GDBusResultFunction function;
+	void *user_data;
+	GDBusDestroyFunction destroy;
+};
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+	struct set_property_data *data = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	dbus_set_error_from_message(&error, reply);
+
+	if (data->function)
+		data->function(&error, data->user_data);
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+
+	dbus_error_free(&error);
+
+	dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
+				const char *name, int type, const void *value,
+				GDBusResultFunction function, void *user_data,
+				GDBusDestroyFunction destroy)
+{
+	struct set_property_data *data;
+	GDBusClient *client;
+	DBusMessage *msg;
+	DBusMessageIter iter, variant;
+	DBusPendingCall *call;
+	char type_as_str[2];
+
+	if (proxy == NULL || name == NULL || value == NULL)
+		return FALSE;
+
+	if (dbus_type_is_basic(type) == FALSE)
+		return FALSE;
+
+	client = proxy->client;
+	if (client == NULL)
+		return FALSE;
+
+	data = g_try_new0(struct set_property_data, 1);
+	if (data == NULL)
+		return FALSE;
+
+	data->function = function;
+	data->user_data = user_data;
+	data->destroy = destroy;
+
+	msg = dbus_message_new_method_call(client->service_name,
+			proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+	if (msg == NULL) {
+		g_free(data);
+		return FALSE;
+	}
+
+	type_as_str[0] = (char) type;
+	type_as_str[1] = '\0';
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+							&proxy->interface);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+						type_as_str, &variant);
+	dbus_message_iter_append_basic(&variant, type, value);
+	dbus_message_iter_close_container(&iter, &variant);
+
+	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+							&call, -1) == FALSE) {
+		dbus_message_unref(msg);
+		g_free(data);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+	dbus_pending_call_unref(call);
+
+	dbus_message_unref(msg);
+
+	return TRUE;
+}
+
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+				const char *name, int type, const void *value,
+				size_t size, GDBusResultFunction function,
+				void *user_data, GDBusDestroyFunction destroy)
+{
+	struct set_property_data *data;
+	GDBusClient *client;
+	DBusMessage *msg;
+	DBusMessageIter iter, variant, array;
+	DBusPendingCall *call;
+	char array_sig[3];
+	char type_sig[2];
+
+	if (!proxy || !name || !value)
+		return FALSE;
+
+	if (!dbus_type_is_basic(type))
+		return FALSE;
+
+	client = proxy->client;
+	if (!client)
+		return FALSE;
+
+	data = g_try_new0(struct set_property_data, 1);
+	if (!data)
+		return FALSE;
+
+	data->function = function;
+	data->user_data = user_data;
+	data->destroy = destroy;
+
+	msg = dbus_message_new_method_call(client->service_name,
+						proxy->obj_path,
+						DBUS_INTERFACE_PROPERTIES,
+						"Set");
+	if (!msg) {
+		g_free(data);
+		return FALSE;
+	}
+
+	array_sig[0] = DBUS_TYPE_ARRAY;
+	array_sig[1] = (char) type;
+	array_sig[2] = '\0';
+
+	type_sig[0] = (char) type;
+	type_sig[1] = '\0';
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+							&proxy->interface);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+	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))
+		dbus_message_iter_append_fixed_array(&array, type, &value,
+									size);
+	else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+		const char **str = (const char **) value;
+		size_t i;
+
+		for (i = 0; i < size; i++)
+			dbus_message_iter_append_basic(&array, type, &str[i]);
+	}
+
+	dbus_message_iter_close_container(&variant, &array);
+	dbus_message_iter_close_container(&iter, &variant);
+
+	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+							&call, -1) == FALSE) {
+		dbus_message_unref(msg);
+		g_free(data);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+	dbus_pending_call_unref(call);
+
+	dbus_message_unref(msg);
+
+	return TRUE;
+}
+
+struct method_call_data {
+	GDBusReturnFunction function;
+	void *user_data;
+	GDBusDestroyFunction destroy;
+};
+
+static void method_call_reply(DBusPendingCall *call, void *user_data)
+{
+	struct method_call_data *data = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+	if (data->function)
+		data->function(reply, data->user_data);
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+
+	dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
+				GDBusSetupFunction setup,
+				GDBusReturnFunction function, void *user_data,
+				GDBusDestroyFunction destroy)
+{
+	struct method_call_data *data;
+	GDBusClient *client;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	if (proxy == NULL || method == NULL)
+		return FALSE;
+
+	client = proxy->client;
+	if (client == NULL)
+		return FALSE;
+
+	data = g_try_new0(struct method_call_data, 1);
+	if (data == NULL)
+		return FALSE;
+
+	data->function = function;
+	data->user_data = user_data;
+	data->destroy = destroy;
+
+	msg = dbus_message_new_method_call(client->service_name,
+				proxy->obj_path, proxy->interface, method);
+	if (msg == NULL) {
+		g_free(data);
+		return FALSE;
+	}
+
+	if (setup) {
+		DBusMessageIter iter;
+
+		dbus_message_iter_init_append(msg, &iter);
+		setup(&iter, data->user_data);
+	}
+
+	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+					&call, METHOD_CALL_TIMEOUT) == FALSE) {
+		dbus_message_unref(msg);
+		g_free(data);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(call, method_call_reply, data, g_free);
+	dbus_pending_call_unref(call);
+
+	dbus_message_unref(msg);
+
+	return TRUE;
+}
+
+gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
+			GDBusPropertyFunction function, void *user_data)
+{
+	if (proxy == NULL)
+		return FALSE;
+
+	proxy->prop_func = function;
+	proxy->prop_data = user_data;
+
+	return TRUE;
+}
+
+gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy,
+				GDBusProxyFunction function, void *user_data)
+{
+	if (proxy == NULL)
+		return FALSE;
+
+	proxy->removed_func = function;
+	proxy->removed_data = user_data;
+
+	return TRUE;
+}
+
+static void refresh_properties(GDBusClient *client)
+{
+	GList *list;
+
+	for (list = g_list_first(client->proxy_list); list;
+						list = g_list_next(list)) {
+		GDBusProxy *proxy = list->data;
+
+		get_all_properties(proxy);
+        }
+}
+
+static void parse_properties(GDBusClient *client, const char *path,
+				const char *interface, DBusMessageIter *iter)
+{
+	GDBusProxy *proxy;
+
+	if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE)
+		return;
+
+	if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
+		return;
+
+	proxy = proxy_lookup(client, path, interface);
+	if (proxy) {
+		update_properties(proxy, iter, FALSE);
+		return;
+	}
+
+	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);
+}
+
+static void parse_interfaces(GDBusClient *client, const char *path,
+						DBusMessageIter *iter)
+{
+	DBusMessageIter dict;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+		const char *interface;
+
+		dbus_message_iter_recurse(&dict, &entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			break;
+
+		dbus_message_iter_get_basic(&entry, &interface);
+		dbus_message_iter_next(&entry);
+
+		parse_properties(client, path, interface, &entry);
+
+		dbus_message_iter_next(&dict);
+	}
+}
+
+static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	GDBusClient *client = user_data;
+	DBusMessageIter iter;
+	const char *path;
+
+	if (dbus_message_iter_init(msg, &iter) == FALSE)
+		return TRUE;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+		return TRUE;
+
+	dbus_message_iter_get_basic(&iter, &path);
+	dbus_message_iter_next(&iter);
+
+	g_dbus_client_ref(client);
+
+	parse_interfaces(client, path, &iter);
+
+	g_dbus_client_unref(client);
+
+	return TRUE;
+}
+
+static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	GDBusClient *client = user_data;
+	DBusMessageIter iter, entry;
+	const char *path;
+
+	if (dbus_message_iter_init(msg, &iter) == FALSE)
+		return TRUE;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+		return TRUE;
+
+	dbus_message_iter_get_basic(&iter, &path);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return TRUE;
+
+	dbus_message_iter_recurse(&iter, &entry);
+
+	g_dbus_client_ref(client);
+
+	while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+		const char *interface;
+
+		dbus_message_iter_get_basic(&entry, &interface);
+		proxy_remove(client, path, interface);
+		dbus_message_iter_next(&entry);
+	}
+
+	g_dbus_client_unref(client);
+
+	return TRUE;
+}
+
+static void parse_managed_objects(GDBusClient *client, DBusMessage *msg)
+{
+	DBusMessageIter iter, dict;
+
+	if (dbus_message_iter_init(msg, &iter) == FALSE)
+		return;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+		const char *path;
+
+		dbus_message_iter_recurse(&dict, &entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) !=
+							DBUS_TYPE_OBJECT_PATH)
+			break;
+
+		dbus_message_iter_get_basic(&entry, &path);
+		dbus_message_iter_next(&entry);
+
+		parse_interfaces(client, path, &entry);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (client->ready)
+		client->ready(client, client->ready_data);
+}
+
+static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
+{
+	GDBusClient *client = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError error;
+
+	g_dbus_client_ref(client);
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, reply) == TRUE) {
+		dbus_error_free(&error);
+		goto done;
+	}
+
+	parse_managed_objects(client, reply);
+
+done:
+	dbus_message_unref(reply);
+
+	dbus_pending_call_unref(client->get_objects_call);
+	client->get_objects_call = NULL;
+
+	g_dbus_client_unref(client);
+}
+
+static void get_managed_objects(GDBusClient *client)
+{
+	DBusMessage *msg;
+
+	if (!client->connected)
+		return;
+
+	if ((!client->proxy_added && !client->proxy_removed) ||
+							!client->root_path) {
+		refresh_properties(client);
+		return;
+	}
+
+	if (client->get_objects_call != NULL)
+		return;
+
+	msg = dbus_message_new_method_call(client->service_name,
+						client->root_path,
+						DBUS_INTERFACE_OBJECT_MANAGER,
+						"GetManagedObjects");
+	if (msg == NULL)
+		return;
+
+	dbus_message_append_args(msg, DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+				&client->get_objects_call, -1) == FALSE) {
+		dbus_message_unref(msg);
+		return;
+	}
+
+	dbus_pending_call_set_notify(client->get_objects_call,
+						get_managed_objects_reply,
+						client, NULL);
+
+	dbus_message_unref(msg);
+}
+
+static void service_connect(DBusConnection *conn, void *user_data)
+{
+	GDBusClient *client = user_data;
+
+	g_dbus_client_ref(client);
+
+	client->connected = TRUE;
+
+	if (client->connect_func)
+		client->connect_func(conn, client->connect_data);
+
+	get_managed_objects(client);
+
+	g_dbus_client_unref(client);
+}
+
+static void service_disconnect(DBusConnection *conn, void *user_data)
+{
+	GDBusClient *client = user_data;
+
+	client->connected = FALSE;
+
+	g_list_free_full(client->proxy_list, proxy_free);
+	client->proxy_list = NULL;
+
+	if (client->disconn_func)
+		client->disconn_func(conn, client->disconn_data);
+}
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	GDBusClient *client = user_data;
+	const char *sender, *path, *interface;
+
+	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	sender = dbus_message_get_sender(message);
+	if (sender == NULL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	path = dbus_message_get_path(message);
+	interface = dbus_message_get_interface(message);
+
+	if (g_str_has_prefix(path, client->base_path) == FALSE)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (client->signal_func)
+		client->signal_func(connection, message, client->signal_data);
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+GDBusClient *g_dbus_client_new(DBusConnection *connection,
+					const char *service, const char *path)
+{
+	return g_dbus_client_new_full(connection, service, path, "/");
+}
+
+GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
+							const char *service,
+							const char *path,
+							const char *root_path)
+{
+	GDBusClient *client;
+	unsigned int i;
+
+	if (!connection || !service)
+		return NULL;
+
+	client = g_try_new0(GDBusClient, 1);
+	if (client == NULL)
+		return NULL;
+
+	if (dbus_connection_add_filter(connection, message_filter,
+						client, NULL) == FALSE) {
+		g_free(client);
+		return NULL;
+	}
+
+	client->dbus_conn = dbus_connection_ref(connection);
+	client->service_name = g_strdup(service);
+	client->base_path = g_strdup(path);
+	client->root_path = g_strdup(root_path);
+	client->connected = FALSE;
+
+	client->match_rules = g_ptr_array_sized_new(1);
+	g_ptr_array_set_free_func(client->match_rules, g_free);
+
+	client->watch = g_dbus_add_service_watch(connection, service,
+						service_connect,
+						service_disconnect,
+						client, NULL);
+
+	if (!root_path)
+		return g_dbus_client_ref(client);
+
+	client->added_watch = g_dbus_add_signal_watch(connection, service,
+						client->root_path,
+						DBUS_INTERFACE_OBJECT_MANAGER,
+						"InterfacesAdded",
+						interfaces_added,
+						client, NULL);
+	client->removed_watch = g_dbus_add_signal_watch(connection, service,
+						client->root_path,
+						DBUS_INTERFACE_OBJECT_MANAGER,
+						"InterfacesRemoved",
+						interfaces_removed,
+						client, NULL);
+	g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
+				"sender='%s',path_namespace='%s'",
+				client->service_name, client->base_path));
+
+	for (i = 0; i < client->match_rules->len; i++) {
+		modify_match(client->dbus_conn, "AddMatch",
+				g_ptr_array_index(client->match_rules, i));
+	}
+
+	return g_dbus_client_ref(client);
+}
+
+GDBusClient *g_dbus_client_ref(GDBusClient *client)
+{
+	if (client == NULL)
+		return NULL;
+
+	__sync_fetch_and_add(&client->ref_count, 1);
+
+	return client;
+}
+
+void g_dbus_client_unref(GDBusClient *client)
+{
+	unsigned int i;
+
+	if (client == NULL)
+		return;
+
+	if (__sync_sub_and_fetch(&client->ref_count, 1) > 0)
+		return;
+
+	if (client->pending_call != NULL) {
+		dbus_pending_call_cancel(client->pending_call);
+		dbus_pending_call_unref(client->pending_call);
+	}
+
+	if (client->get_objects_call != NULL) {
+		dbus_pending_call_cancel(client->get_objects_call);
+		dbus_pending_call_unref(client->get_objects_call);
+	}
+
+	for (i = 0; i < client->match_rules->len; i++) {
+		modify_match(client->dbus_conn, "RemoveMatch",
+				g_ptr_array_index(client->match_rules, i));
+	}
+
+	g_ptr_array_free(client->match_rules, TRUE);
+
+	dbus_connection_remove_filter(client->dbus_conn,
+						message_filter, client);
+
+	g_list_free_full(client->proxy_list, proxy_free);
+
+	/*
+	 * Don't call disconn_func twice if disconnection
+	 * was previously reported.
+	 */
+	if (client->disconn_func && client->connected)
+		client->disconn_func(client->dbus_conn, client->disconn_data);
+
+	g_dbus_remove_watch(client->dbus_conn, client->watch);
+	g_dbus_remove_watch(client->dbus_conn, client->added_watch);
+	g_dbus_remove_watch(client->dbus_conn, client->removed_watch);
+
+	dbus_connection_unref(client->dbus_conn);
+
+	g_free(client->service_name);
+	g_free(client->base_path);
+	g_free(client->root_path);
+
+	g_free(client);
+}
+
+gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
+				GDBusWatchFunction function, void *user_data)
+{
+	if (client == NULL)
+		return FALSE;
+
+	client->connect_func = function;
+	client->connect_data = user_data;
+
+	return TRUE;
+}
+
+gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
+				GDBusWatchFunction function, void *user_data)
+{
+	if (client == NULL)
+		return FALSE;
+
+	client->disconn_func = function;
+	client->disconn_data = user_data;
+
+	return TRUE;
+}
+
+gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
+				GDBusMessageFunction function, void *user_data)
+{
+	if (client == NULL)
+		return FALSE;
+
+	client->signal_func = function;
+	client->signal_data = user_data;
+
+	return TRUE;
+}
+
+gboolean g_dbus_client_set_ready_watch(GDBusClient *client,
+				GDBusClientFunction ready, void *user_data)
+{
+	if (client == NULL)
+		return FALSE;
+
+	client->ready = ready;
+	client->ready_data = user_data;
+
+	return TRUE;
+}
+
+gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
+					GDBusProxyFunction proxy_added,
+					GDBusProxyFunction proxy_removed,
+					GDBusPropertyFunction property_changed,
+					void *user_data)
+{
+	if (client == NULL)
+		return FALSE;
+
+	client->proxy_added = proxy_added;
+	client->proxy_removed = proxy_removed;
+	client->property_changed = property_changed;
+	client->user_data = user_data;
+
+	if (proxy_added || proxy_removed || property_changed)
+		get_managed_objects(client);
+
+	return TRUE;
+}
diff --git a/bluez/repo/gdbus/gdbus.h b/bluez/repo/gdbus/gdbus.h
new file mode 100644
index 0000000..69fbc10
--- /dev/null
+++ b/bluez/repo/gdbus/gdbus.h
@@ -0,0 +1,394 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef struct GDBusArgInfo GDBusArgInfo;
+typedef struct GDBusMethodTable GDBusMethodTable;
+typedef struct GDBusSignalTable GDBusSignalTable;
+typedef struct GDBusPropertyTable GDBusPropertyTable;
+typedef struct GDBusSecurityTable GDBusSecurityTable;
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+							void *user_data);
+
+typedef void (* GDBusMessageFunction) (DBusConnection *connection,
+					 DBusMessage *message, void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+					DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+							DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+							DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+							DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+				GDBusWatchFunction function,
+				void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+					DBusMessage *message, void *user_data);
+
+typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data);
+
+typedef guint32 GDBusPendingPropertySet;
+
+typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property,
+			DBusMessageIter *value, GDBusPendingPropertySet id,
+			void *data);
+
+typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property,
+								void *data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+						const char *action,
+						gboolean interaction,
+						GDBusPendingReply pending);
+
+enum GDBusFlags {
+	G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0),
+};
+
+enum GDBusMethodFlags {
+	G_DBUS_METHOD_FLAG_DEPRECATED   = (1 << 0),
+	G_DBUS_METHOD_FLAG_NOREPLY      = (1 << 1),
+	G_DBUS_METHOD_FLAG_ASYNC        = (1 << 2),
+	G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3),
+};
+
+enum GDBusSignalFlags {
+	G_DBUS_SIGNAL_FLAG_DEPRECATED   = (1 << 0),
+	G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1),
+};
+
+enum GDBusPropertyFlags {
+	G_DBUS_PROPERTY_FLAG_DEPRECATED   = (1 << 0),
+	G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1),
+};
+
+enum GDBusSecurityFlags {
+	G_DBUS_SECURITY_FLAG_DEPRECATED        = (1 << 0),
+	G_DBUS_SECURITY_FLAG_BUILTIN           = (1 << 1),
+	G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+};
+
+enum GDbusPropertyChangedFlags {
+	G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH = (1 << 0),
+};
+
+typedef enum GDBusMethodFlags GDBusMethodFlags;
+typedef enum GDBusSignalFlags GDBusSignalFlags;
+typedef enum GDBusPropertyFlags GDBusPropertyFlags;
+typedef enum GDBusSecurityFlags GDBusSecurityFlags;
+typedef enum GDbusPropertyChangedFlags GDbusPropertyChangedFlags;
+
+struct GDBusArgInfo {
+	const char *name;
+	const char *signature;
+};
+
+struct GDBusMethodTable {
+	const char *name;
+	GDBusMethodFunction function;
+	GDBusMethodFlags flags;
+	unsigned int privilege;
+	const GDBusArgInfo *in_args;
+	const GDBusArgInfo *out_args;
+};
+
+struct GDBusSignalTable {
+	const char *name;
+	GDBusSignalFlags flags;
+	const GDBusArgInfo *args;
+};
+
+struct GDBusPropertyTable {
+	const char *name;
+	const char *type;
+	GDBusPropertyGetter get;
+	GDBusPropertySetter set;
+	GDBusPropertyExists exists;
+	GDBusPropertyFlags flags;
+};
+
+struct GDBusSecurityTable {
+	unsigned int privilege;
+	const char *action;
+	GDBusSecurityFlags flags;
+	GDBusSecurityFunction function;
+};
+
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function, \
+	.flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function, \
+	.flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function, \
+	.flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function, \
+	.flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL
+
+#define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function, \
+	.flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+	.name = _name, \
+	.in_args = _in_args, \
+	.out_args = _out_args, \
+	.function = _function, \
+	.flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+	.name = _name, \
+	.args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+	.name = _name, \
+	.args = _args, \
+	.flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
+#define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \
+	.name = _name, \
+	.args = _args, \
+	.flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL
+
+void g_dbus_set_flags(int flags);
+int g_dbus_get_flags(void);
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+					const char *path, const char *name,
+					const GDBusMethodTable *methods,
+					const GDBusSignalTable *signals,
+					const GDBusPropertyTable *properties,
+					void *user_data,
+					GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+					const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+					GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+				GDBusPendingReply pending,
+				const char *name, const char *format, ...)
+					__attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+				GDBusPendingReply pending, const char *name,
+					const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+						const char *format, ...)
+					__attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+					const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+						int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_message_with_reply(DBusConnection *connection,
+					DBusMessage *message,
+					DBusPendingCall **call, int timeout);
+gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message,
+				const char *name, const char *format, ...)
+					 __attribute__((format(printf, 4, 5)));
+gboolean g_dbus_send_error_valist(DBusConnection *connection,
+					DBusMessage *message, const char *name,
+					const char *format, va_list args);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+				DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+				DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+				GDBusWatchFunction connect,
+				GDBusWatchFunction disconnect,
+				void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+				GDBusWatchFunction function,
+				void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+				const char *sender, const char *path,
+				const char *interface, const char *member,
+				GDBusSignalFunction function, void *user_data,
+				GDBusDestroyFunction destroy);
+guint g_dbus_add_properties_watch(DBusConnection *connection,
+				const char *sender, const char *path,
+				const char *interface,
+				GDBusSignalFunction function, void *user_data,
+				GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+void g_dbus_pending_property_success(GDBusPendingPropertySet id);
+void g_dbus_pending_property_error_valist(GDBusPendingReply id,
+			const char *name, const char *format, va_list args);
+void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
+						const char *format, ...);
+void g_dbus_emit_property_changed(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name);
+void g_dbus_emit_property_changed_full(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name,
+				GDbusPropertyChangedFlags flags);
+gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
+				const char *interface, DBusMessageIter *iter);
+
+gboolean g_dbus_attach_object_manager(DBusConnection *connection);
+gboolean g_dbus_detach_object_manager(DBusConnection *connection);
+
+typedef struct GDBusClient GDBusClient;
+typedef struct GDBusProxy GDBusProxy;
+
+GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
+							const char *interface);
+
+GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy);
+void g_dbus_proxy_unref(GDBusProxy *proxy);
+
+const char *g_dbus_proxy_get_path(GDBusProxy *proxy);
+const char *g_dbus_proxy_get_interface(GDBusProxy *proxy);
+
+gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
+							DBusMessageIter *iter);
+
+gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name);
+
+typedef void (* GDBusResultFunction) (const DBusError *error, void *user_data);
+
+gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
+				const char *name, int type, const void *value,
+				GDBusResultFunction function, void *user_data,
+				GDBusDestroyFunction destroy);
+
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+				const char *name, int type, const void *value,
+				size_t size, GDBusResultFunction function,
+				void *user_data, GDBusDestroyFunction destroy);
+
+typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
+typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
+
+gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
+				GDBusSetupFunction setup,
+				GDBusReturnFunction function, void *user_data,
+				GDBusDestroyFunction destroy);
+
+typedef void (* GDBusClientFunction) (GDBusClient *client, void *user_data);
+typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data);
+typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data);
+
+gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
+			GDBusPropertyFunction function, void *user_data);
+
+gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy,
+			GDBusProxyFunction destroy, void *user_data);
+
+GDBusClient *g_dbus_client_new(DBusConnection *connection,
+					const char *service, const char *path);
+GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
+							const char *service,
+							const char *path,
+							const char *root_path);
+
+GDBusClient *g_dbus_client_ref(GDBusClient *client);
+void g_dbus_client_unref(GDBusClient *client);
+
+gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
+				GDBusWatchFunction function, void *user_data);
+gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
+				GDBusWatchFunction function, void *user_data);
+gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
+				GDBusMessageFunction function, void *user_data);
+gboolean g_dbus_client_set_ready_watch(GDBusClient *client,
+				GDBusClientFunction ready, void *user_data);
+gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
+					GDBusProxyFunction proxy_added,
+					GDBusProxyFunction proxy_removed,
+					GDBusPropertyFunction property_changed,
+					void *user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/bluez/repo/gdbus/mainloop.c b/bluez/repo/gdbus/mainloop.c
new file mode 100644
index 0000000..b90a844
--- /dev/null
+++ b/bluez/repo/gdbus/mainloop.c
@@ -0,0 +1,378 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+	guint id;
+	DBusTimeout *timeout;
+};
+
+struct watch_info {
+	guint id;
+	DBusWatch *watch;
+	DBusConnection *conn;
+};
+
+struct disconnect_data {
+	GDBusWatchFunction function;
+	void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct disconnect_data *dc_data = data;
+
+	error("Got disconnected from the system message bus");
+
+	dc_data->function(conn, dc_data->user_data);
+
+	dbus_connection_unref(conn);
+
+	return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+	DBusConnection *conn = data;
+
+	/* Dispatch messages */
+	while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+	dbus_connection_unref(conn);
+
+	return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+						DBusDispatchStatus status)
+{
+	if (status == DBUS_DISPATCH_DATA_REMAINS)
+		g_idle_add(message_dispatch, dbus_connection_ref(conn));
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct watch_info *info = data;
+	unsigned int flags = 0;
+	DBusDispatchStatus status;
+	DBusConnection *conn;
+
+	if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
+	if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+	if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+	if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+	/* Protect connection from being destroyed by dbus_watch_handle */
+	conn = dbus_connection_ref(info->conn);
+
+	dbus_watch_handle(info->watch, flags);
+
+	status = dbus_connection_get_dispatch_status(conn);
+	queue_dispatch(conn, status);
+
+	dbus_connection_unref(conn);
+
+	return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+	struct watch_info *info = data;
+
+	if (info->id > 0) {
+		g_source_remove(info->id);
+		info->id = 0;
+	}
+
+	dbus_connection_unref(info->conn);
+
+	g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+	DBusConnection *conn = data;
+	GIOCondition cond = G_IO_HUP | G_IO_ERR;
+	GIOChannel *chan;
+	struct watch_info *info;
+	unsigned int flags;
+	int fd;
+
+	if (!dbus_watch_get_enabled(watch))
+		return TRUE;
+
+	info = g_new0(struct watch_info, 1);
+
+	fd = dbus_watch_get_unix_fd(watch);
+	chan = g_io_channel_unix_new(fd);
+
+	info->watch = watch;
+	info->conn = dbus_connection_ref(conn);
+
+	dbus_watch_set_data(watch, info, watch_info_free);
+
+	flags = dbus_watch_get_flags(watch);
+
+	if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+	if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+	info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+	g_io_channel_unref(chan);
+
+	return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+	if (dbus_watch_get_enabled(watch))
+		return;
+
+	/* will trigger watch_info_free() */
+	dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+	/* Because we just exit on OOM, enable/disable is
+	 * no different from add/remove */
+	if (dbus_watch_get_enabled(watch))
+		add_watch(watch, data);
+	else
+		remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+	struct timeout_handler *handler = data;
+
+	handler->id = 0;
+
+	/* if not enabled should not be polled by the main loop */
+	if (!dbus_timeout_get_enabled(handler->timeout))
+		return FALSE;
+
+	dbus_timeout_handle(handler->timeout);
+
+	return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+	struct timeout_handler *handler = data;
+
+	if (handler->id > 0) {
+		g_source_remove(handler->id);
+		handler->id = 0;
+	}
+
+	g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+	int interval = dbus_timeout_get_interval(timeout);
+	struct timeout_handler *handler;
+
+	if (!dbus_timeout_get_enabled(timeout))
+		return TRUE;
+
+	handler = g_new0(struct timeout_handler, 1);
+
+	handler->timeout = timeout;
+
+	dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+	handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+								handler);
+
+	return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+	/* will trigger timeout_handler_free() */
+	dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+	if (dbus_timeout_get_enabled(timeout))
+		add_timeout(timeout, data);
+	else
+		remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+					DBusDispatchStatus status, void *data)
+{
+	if (!dbus_connection_get_is_connected(conn))
+		return;
+
+	queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+	dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+						watch_toggled, conn, NULL);
+
+	dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+						timeout_toggled, NULL, NULL);
+
+	dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+								NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+						DBusError *error)
+{
+	gboolean result;
+	DBusDispatchStatus status;
+
+	if (name != NULL) {
+		result = g_dbus_request_name(conn, name, error);
+
+		if (error != NULL) {
+			if (dbus_error_is_set(error) == TRUE)
+				return FALSE;
+		}
+
+		if (result == FALSE)
+			return FALSE;
+	}
+
+	setup_dbus_with_main_loop(conn);
+
+	status = dbus_connection_get_dispatch_status(conn);
+	queue_dispatch(conn, status);
+
+	return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+							DBusError *error)
+{
+	DBusConnection *conn;
+
+	conn = dbus_bus_get(type, error);
+
+	if (error != NULL) {
+		if (dbus_error_is_set(error) == TRUE)
+			return NULL;
+	}
+
+	if (conn == NULL)
+		return NULL;
+
+	if (setup_bus(conn, name, error) == FALSE) {
+		dbus_connection_unref(conn);
+		return NULL;
+	}
+
+	return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+							DBusError *error)
+{
+	DBusConnection *conn;
+
+	conn = dbus_bus_get_private(type, error);
+
+	if (error != NULL) {
+		if (dbus_error_is_set(error) == TRUE)
+			return NULL;
+	}
+
+	if (conn == NULL)
+		return NULL;
+
+	if (setup_bus(conn, name, error) == FALSE) {
+		dbus_connection_close(conn);
+		dbus_connection_unref(conn);
+		return NULL;
+	}
+
+	return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+							DBusError *error)
+{
+	int result;
+
+	result = dbus_bus_request_name(connection, name,
+					DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+	if (error != NULL) {
+		if (dbus_error_is_set(error) == TRUE)
+			return FALSE;
+	}
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		if (error != NULL)
+			dbus_set_error(error, name, "Name already in use");
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+				GDBusWatchFunction function,
+				void *user_data, DBusFreeFunction destroy)
+{
+	struct disconnect_data *dc_data;
+
+	dc_data = g_new0(struct disconnect_data, 1);
+
+	dc_data->function = function;
+	dc_data->user_data = user_data;
+
+	dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+	if (g_dbus_add_signal_watch(connection, NULL, NULL,
+				DBUS_INTERFACE_LOCAL, "Disconnected",
+				disconnected_signal, dc_data, g_free) == 0) {
+		error("Failed to add watch for D-Bus Disconnected signal");
+		g_free(dc_data);
+		return FALSE;
+	}
+
+	return TRUE;
+}
diff --git a/bluez/repo/gdbus/object.c b/bluez/repo/gdbus/object.c
new file mode 100644
index 0000000..a220101
--- /dev/null
+++ b/bluez/repo/gdbus/object.c
@@ -0,0 +1,1834 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
+
+#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
+#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
+#endif
+
+#ifndef DBUS_ERROR_PROPERTY_READ_ONLY
+#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly"
+#endif
+
+struct generic_data {
+	unsigned int refcount;
+	DBusConnection *conn;
+	char *path;
+	GSList *interfaces;
+	GSList *objects;
+	GSList *added;
+	GSList *removed;
+	guint process_id;
+	gboolean pending_prop;
+	char *introspect;
+	struct generic_data *parent;
+};
+
+struct interface_data {
+	char *name;
+	const GDBusMethodTable *methods;
+	const GDBusSignalTable *signals;
+	const GDBusPropertyTable *properties;
+	GSList *pending_prop;
+	void *user_data;
+	GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+	GDBusPendingReply pending;
+	DBusMessage *message;
+	const GDBusMethodTable *method;
+	void *iface_user_data;
+};
+
+struct property_data {
+	DBusConnection *conn;
+	GDBusPendingPropertySet id;
+	DBusMessage *message;
+};
+
+static int global_flags = 0;
+static struct generic_data *root;
+static GSList *pending = NULL;
+
+static gboolean process_changes(gpointer user_data);
+static void process_properties_from_interface(struct generic_data *data,
+						struct interface_data *iface);
+static void process_property_changes(struct generic_data *data);
+
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
+						const char *direction)
+{
+	for (; args && args->name; args++) {
+		g_string_append_printf(gstr,
+					"<arg name=\"%s\" type=\"%s\"",
+					args->name, args->signature);
+
+		if (direction)
+			g_string_append_printf(gstr,
+					" direction=\"%s\"/>\n", direction);
+		else
+			g_string_append_printf(gstr, "/>\n");
+
+	}
+}
+
+#define G_DBUS_ANNOTATE(name_, value_)				\
+	"<annotation name=\"org.freedesktop.DBus." name_ "\" "	\
+	"value=\"" value_ "\"/>"
+
+#define G_DBUS_ANNOTATE_DEPRECATED \
+	G_DBUS_ANNOTATE("Deprecated", "true")
+
+#define G_DBUS_ANNOTATE_NOREPLY \
+	G_DBUS_ANNOTATE("Method.NoReply", "true")
+
+static gboolean check_experimental(int flags, int flag)
+{
+	if (!(flags & flag))
+		return FALSE;
+
+	return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL);
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+	const GDBusMethodTable *method;
+	const GDBusSignalTable *signal;
+	const GDBusPropertyTable *property;
+
+	for (method = iface->methods; method && method->name; method++) {
+		if (check_experimental(method->flags,
+					G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+			continue;
+
+		g_string_append_printf(gstr, "<method name=\"%s\">",
+								method->name);
+		print_arguments(gstr, method->in_args, "in");
+		print_arguments(gstr, method->out_args, "out");
+
+		if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED)
+			g_string_append_printf(gstr,
+						G_DBUS_ANNOTATE_DEPRECATED);
+
+		if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY)
+			g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY);
+
+		g_string_append_printf(gstr, "</method>");
+	}
+
+	for (signal = iface->signals; signal && signal->name; signal++) {
+		if (check_experimental(signal->flags,
+					G_DBUS_SIGNAL_FLAG_EXPERIMENTAL))
+			continue;
+
+		g_string_append_printf(gstr, "<signal name=\"%s\">",
+								signal->name);
+		print_arguments(gstr, signal->args, NULL);
+
+		if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED)
+			g_string_append_printf(gstr,
+						G_DBUS_ANNOTATE_DEPRECATED);
+
+		g_string_append_printf(gstr, "</signal>\n");
+	}
+
+	for (property = iface->properties; property && property->name;
+								property++) {
+		if (check_experimental(property->flags,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+			continue;
+
+		g_string_append_printf(gstr, "<property name=\"%s\""
+					" type=\"%s\" access=\"%s%s\">",
+					property->name,	property->type,
+					property->get ? "read" : "",
+					property->set ? "write" : "");
+
+		if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED)
+			g_string_append_printf(gstr,
+						G_DBUS_ANNOTATE_DEPRECATED);
+
+		g_string_append_printf(gstr, "</property>");
+	}
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+				struct generic_data *data, const char *path)
+{
+	GSList *list;
+	GString *gstr;
+	char **children;
+	int i;
+
+	g_free(data->introspect);
+
+	gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+	g_string_append_printf(gstr, "<node>");
+
+	for (list = data->interfaces; list; list = list->next) {
+		struct interface_data *iface = list->data;
+
+		g_string_append_printf(gstr, "<interface name=\"%s\">",
+								iface->name);
+
+		generate_interface_xml(gstr, iface);
+
+		g_string_append_printf(gstr, "</interface>");
+	}
+
+	if (!dbus_connection_list_registered(conn, path, &children))
+		goto done;
+
+	for (i = 0; children[i]; i++)
+		g_string_append_printf(gstr, "<node name=\"%s\"/>",
+								children[i]);
+
+	dbus_free_string_array(children);
+
+done:
+	g_string_append_printf(gstr, "</node>");
+
+	data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct generic_data *data = user_data;
+	DBusMessage *reply;
+
+	if (data->introspect == NULL)
+		generate_introspection_xml(connection, data,
+						dbus_message_get_path(message));
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+					DBUS_TYPE_INVALID);
+
+	return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+			DBusMessage *message, const GDBusMethodTable *method,
+							void *iface_user_data)
+{
+	DBusMessage *reply;
+
+	reply = method->function(connection, message, iface_user_data);
+
+	if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY ||
+					dbus_message_get_no_reply(message)) {
+		if (reply != NULL)
+			dbus_message_unref(reply);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+		if (reply == NULL)
+			return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	if (reply == NULL)
+		return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+	g_dbus_send_message(connection, reply);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+					GDBusPendingReply pending)
+{
+	GSList *list;
+
+	for (list = pending_security; list; list = list->next) {
+		struct security_data *secdata = list->data;
+
+		if (secdata->pending != pending)
+			continue;
+
+		pending_security = g_slist_remove(pending_security, secdata);
+
+		process_message(connection, secdata->message,
+				secdata->method, secdata->iface_user_data);
+
+		dbus_message_unref(secdata->message);
+		g_free(secdata);
+		return;
+	}
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+				GDBusPendingReply pending, const char *name,
+					const char *format, va_list args)
+{
+	GSList *list;
+
+	for (list = pending_security; list; list = list->next) {
+		struct security_data *secdata = list->data;
+
+		if (secdata->pending != pending)
+			continue;
+
+		pending_security = g_slist_remove(pending_security, secdata);
+
+		g_dbus_send_error_valist(connection, secdata->message,
+							name, format, args);
+
+		dbus_message_unref(secdata->message);
+		g_free(secdata);
+		return;
+	}
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+				GDBusPendingReply pending,
+				const char *name, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+
+	g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+	va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+				const char *action, gboolean interaction,
+				void (*function) (dbus_bool_t authorized,
+							void *user_data),
+						void *user_data, int timeout);
+
+struct builtin_security_data {
+	DBusConnection *conn;
+	GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+	struct builtin_security_data *data = user_data;
+
+	if (authorized == TRUE)
+		g_dbus_pending_success(data->conn, data->pending);
+	else
+		g_dbus_pending_error(data->conn, data->pending,
+						DBUS_ERROR_AUTH_FAILED, NULL);
+
+	g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+						const char *action,
+						gboolean interaction,
+						GDBusPendingReply pending)
+{
+	struct builtin_security_data *data;
+
+	data = g_new0(struct builtin_security_data, 1);
+	data->conn = conn;
+	data->pending = pending;
+
+	if (polkit_check_authorization(conn, action, interaction,
+				builtin_security_result, data, 30000) < 0)
+		g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+			const GDBusMethodTable *method, void *iface_user_data)
+{
+	const GDBusSecurityTable *security;
+
+	for (security = security_table; security && security->privilege;
+								security++) {
+		struct security_data *secdata;
+		gboolean interaction;
+
+		if (security->privilege != method->privilege)
+			continue;
+
+		secdata = g_new(struct security_data, 1);
+		secdata->pending = next_pending++;
+		secdata->message = dbus_message_ref(msg);
+		secdata->method = method;
+		secdata->iface_user_data = iface_user_data;
+
+		pending_security = g_slist_prepend(pending_security, secdata);
+
+		if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+			interaction = TRUE;
+		else
+			interaction = FALSE;
+
+		if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+							security->function)
+			security->function(conn, security->action,
+						interaction, secdata->pending);
+		else
+			builtin_security_function(conn, security->action,
+						interaction, secdata->pending);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static GDBusPendingPropertySet next_pending_property = 1;
+static GSList *pending_property_set;
+
+static struct property_data *remove_pending_property_data(
+						GDBusPendingPropertySet id)
+{
+	struct property_data *propdata;
+	GSList *l;
+
+	for (l = pending_property_set; l != NULL; l = l->next) {
+		propdata = l->data;
+		if (propdata->id != id)
+			continue;
+
+		break;
+	}
+
+	if (l == NULL)
+		return NULL;
+
+	pending_property_set = g_slist_delete_link(pending_property_set, l);
+
+	return propdata;
+}
+
+void g_dbus_pending_property_success(GDBusPendingPropertySet id)
+{
+	struct property_data *propdata;
+
+	propdata = remove_pending_property_data(id);
+	if (propdata == NULL)
+		return;
+
+	g_dbus_send_reply(propdata->conn, propdata->message,
+							DBUS_TYPE_INVALID);
+	dbus_message_unref(propdata->message);
+	g_free(propdata);
+}
+
+void g_dbus_pending_property_error_valist(GDBusPendingReply id,
+					const char *name, const char *format,
+					va_list args)
+{
+	struct property_data *propdata;
+
+	propdata = remove_pending_property_data(id);
+	if (propdata == NULL)
+		return;
+
+	g_dbus_send_error_valist(propdata->conn, propdata->message, name,
+								format, args);
+
+	dbus_message_unref(propdata->message);
+	g_free(propdata);
+}
+
+void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
+						const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+
+	g_dbus_pending_property_error_valist(id, name, format, args);
+
+	va_end(args);
+}
+
+static void reset_parent(gpointer data, gpointer user_data)
+{
+	struct generic_data *child = data;
+	struct generic_data *parent = user_data;
+
+	child->parent = parent;
+}
+
+static void append_property(struct interface_data *iface,
+			const GDBusPropertyTable *p, DBusMessageIter *dict)
+{
+	DBusMessageIter entry, value;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
+								&entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type,
+								&value);
+
+	p->get(p, &value, iface->user_data);
+
+	dbus_message_iter_close_container(&entry, &value);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_properties(struct interface_data *data,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter dict;
+	const GDBusPropertyTable *p;
+
+	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);
+
+	for (p = data->properties; p && p->name; p++) {
+		if (check_experimental(p->flags,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+			continue;
+
+		if (p->get == NULL)
+			continue;
+
+		if (p->exists != NULL && !p->exists(p, data->user_data))
+			continue;
+
+		append_property(data, p, &dict);
+	}
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void append_interface(gpointer data, gpointer user_data)
+{
+	struct interface_data *iface = data;
+	DBusMessageIter *array = user_data;
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+								&entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name);
+	append_properties(data, &entry);
+	dbus_message_iter_close_container(array, &entry);
+}
+
+static void emit_interfaces_added(struct generic_data *data)
+{
+	DBusMessage *signal;
+	DBusMessageIter iter, array;
+
+	if (root == NULL || data == root)
+		return;
+
+	signal = dbus_message_new_signal(root->path,
+					DBUS_INTERFACE_OBJECT_MANAGER,
+					"InterfacesAdded");
+	if (signal == NULL)
+		return;
+
+	dbus_message_iter_init_append(signal, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+								&data->path);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+				DBUS_TYPE_STRING_AS_STRING
+				DBUS_TYPE_ARRAY_AS_STRING
+				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+				DBUS_TYPE_STRING_AS_STRING
+				DBUS_TYPE_VARIANT_AS_STRING
+				DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+				DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+	g_slist_foreach(data->added, append_interface, &array);
+	g_slist_free(data->added);
+	data->added = NULL;
+
+	dbus_message_iter_close_container(&iter, &array);
+
+	/* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+	dbus_connection_send(data->conn, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+						const char *name)
+{
+	GSList *list;
+
+	if (name == NULL)
+		return NULL;
+
+	for (list = interfaces; list; list = list->next) {
+		struct interface_data *iface = list->data;
+		if (!strcmp(name, iface->name))
+			return iface;
+	}
+
+	return NULL;
+}
+
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+							DBusMessage *message)
+{
+	const char *sig = dbus_message_get_signature(message);
+	const char *p = NULL;
+
+	for (; args && args->signature && *sig; args++) {
+		p = args->signature;
+
+		for (; *sig && *p; sig++, p++) {
+			if (*p != *sig)
+				return FALSE;
+		}
+	}
+
+	if (*sig || (p && *p) || (args && args->signature))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void add_pending(struct generic_data *data)
+{
+	if (data->process_id > 0)
+		return;
+
+	data->process_id = g_idle_add(process_changes, data);
+
+	pending = g_slist_append(pending, data);
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+	struct interface_data *iface;
+
+	iface = find_interface(data->interfaces, name);
+	if (iface == NULL)
+		return FALSE;
+
+	process_properties_from_interface(data, iface);
+
+	data->interfaces = g_slist_remove(data->interfaces, iface);
+
+	if (iface->destroy) {
+		iface->destroy(iface->user_data);
+		iface->user_data = NULL;
+	}
+
+	/*
+	 * Interface being removed was just added, on the same mainloop
+	 * iteration? Don't send any signal
+	 */
+	if (g_slist_find(data->added, iface)) {
+		data->added = g_slist_remove(data->added, iface);
+		g_free(iface->name);
+		g_free(iface);
+		return TRUE;
+	}
+
+	if (data->parent == NULL) {
+		g_free(iface->name);
+		g_free(iface);
+		return TRUE;
+	}
+
+	data->removed = g_slist_prepend(data->removed, iface->name);
+	g_free(iface);
+
+	add_pending(data);
+
+	return TRUE;
+}
+
+static struct generic_data *invalidate_parent_data(DBusConnection *conn,
+						const char *child_path)
+{
+	struct generic_data *data = NULL, *child = NULL, *parent = NULL;
+	char *parent_path, *slash;
+
+	parent_path = g_strdup(child_path);
+	slash = strrchr(parent_path, '/');
+	if (slash == NULL)
+		goto done;
+
+	if (slash == parent_path && parent_path[1] != '\0')
+		parent_path[1] = '\0';
+	else
+		*slash = '\0';
+
+	if (!strlen(parent_path))
+		goto done;
+
+	if (dbus_connection_get_object_path_data(conn, parent_path,
+							(void *) &data) == FALSE) {
+		goto done;
+	}
+
+	parent = invalidate_parent_data(conn, parent_path);
+
+	if (data == NULL) {
+		data = parent;
+		if (data == NULL)
+			goto done;
+	}
+
+	g_free(data->introspect);
+	data->introspect = NULL;
+
+	if (!dbus_connection_get_object_path_data(conn, child_path,
+							(void *) &child))
+		goto done;
+
+	if (child == NULL || g_slist_find(data->objects, child) != NULL)
+		goto done;
+
+	data->objects = g_slist_prepend(data->objects, child);
+	child->parent = data;
+
+done:
+	g_free(parent_path);
+	return data;
+}
+
+static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties,
+							const char *name)
+{
+	const GDBusPropertyTable *p;
+
+	for (p = properties; p && p->name; p++) {
+		if (strcmp(name, p->name) != 0)
+			continue;
+
+		if (check_experimental(p->flags,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+			break;
+
+		return p;
+	}
+
+	return NULL;
+}
+
+static DBusMessage *properties_get(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct generic_data *data = user_data;
+	struct interface_data *iface;
+	const GDBusPropertyTable *property;
+	const char *interface, *name;
+	DBusMessageIter iter, value;
+	DBusMessage *reply;
+
+	if (!dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &interface,
+					DBUS_TYPE_STRING, &name,
+					DBUS_TYPE_INVALID))
+		return NULL;
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+				"No such interface '%s'", interface);
+
+	property = find_property(iface->properties, name);
+	if (property == NULL)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+				"No such property '%s'", name);
+
+	if (property->exists != NULL &&
+			!property->exists(property, iface->user_data))
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+					"No such property '%s'", name);
+
+	if (property->get == NULL)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+				"Property '%s' is not readable", name);
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+						property->type, &value);
+
+	if (!property->get(property, &value, iface->user_data)) {
+		dbus_message_unref(reply);
+		return NULL;
+	}
+
+	dbus_message_iter_close_container(&iter, &value);
+
+	return reply;
+}
+
+static DBusMessage *properties_get_all(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct generic_data *data = user_data;
+	struct interface_data *iface;
+	const char *interface;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+
+	if (!dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &interface,
+					DBUS_TYPE_INVALID))
+		return NULL;
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+					"No such interface '%s'", interface);
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	append_properties(iface, &iter);
+
+	return reply;
+}
+
+static DBusMessage *properties_set(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct generic_data *data = user_data;
+	DBusMessageIter iter, sub;
+	struct interface_data *iface;
+	const GDBusPropertyTable *property;
+	const char *name, *interface;
+	struct property_data *propdata;
+	gboolean valid_signature;
+	char *signature;
+
+	if (!dbus_message_iter_init(message, &iter))
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+							"No arguments given");
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_get_basic(&iter, &interface);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_get_basic(&iter, &name);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_recurse(&iter, &sub);
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL)
+		return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+					"No such interface '%s'", interface);
+
+	property = find_property(iface->properties, name);
+	if (property == NULL)
+		return g_dbus_create_error(message,
+						DBUS_ERROR_UNKNOWN_PROPERTY,
+						"No such property '%s'", name);
+
+	if (property->set == NULL)
+		return g_dbus_create_error(message,
+					DBUS_ERROR_PROPERTY_READ_ONLY,
+					"Property '%s' is not writable", name);
+
+	if (property->exists != NULL &&
+			!property->exists(property, iface->user_data))
+		return g_dbus_create_error(message,
+						DBUS_ERROR_UNKNOWN_PROPERTY,
+						"No such property '%s'", name);
+
+	signature = dbus_message_iter_get_signature(&sub);
+	valid_signature = strcmp(signature, property->type) ? FALSE : TRUE;
+	dbus_free(signature);
+	if (!valid_signature)
+		return g_dbus_create_error(message,
+					DBUS_ERROR_INVALID_SIGNATURE,
+					"Invalid signature for '%s'", name);
+
+	propdata = g_new(struct property_data, 1);
+	propdata->id = next_pending_property++;
+	propdata->message = dbus_message_ref(message);
+	propdata->conn = connection;
+	pending_property_set = g_slist_prepend(pending_property_set, propdata);
+
+	property->set(property, &sub, propdata->id, iface->user_data);
+
+	return NULL;
+}
+
+static const GDBusMethodTable properties_methods[] = {
+	{ GDBUS_METHOD("Get",
+			GDBUS_ARGS({ "interface", "s" }, { "name", "s" }),
+			GDBUS_ARGS({ "value", "v" }),
+			properties_get) },
+	{ GDBUS_ASYNC_METHOD("Set",
+			GDBUS_ARGS({ "interface", "s" }, { "name", "s" },
+							{ "value", "v" }),
+			NULL,
+			properties_set) },
+	{ GDBUS_METHOD("GetAll",
+			GDBUS_ARGS({ "interface", "s" }),
+			GDBUS_ARGS({ "properties", "a{sv}" }),
+			properties_get_all) },
+	{ }
+};
+
+static const GDBusSignalTable properties_signals[] = {
+	{ GDBUS_SIGNAL("PropertiesChanged",
+			GDBUS_ARGS({ "interface", "s" },
+					{ "changed_properties", "a{sv}" },
+					{ "invalidated_properties", "as"})) },
+	{ }
+};
+
+static void append_name(gpointer data, gpointer user_data)
+{
+	char *name = data;
+	DBusMessageIter *iter = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+}
+
+static void emit_interfaces_removed(struct generic_data *data)
+{
+	DBusMessage *signal;
+	DBusMessageIter iter, array;
+
+	if (root == NULL || data == root)
+		return;
+
+	signal = dbus_message_new_signal(root->path,
+					DBUS_INTERFACE_OBJECT_MANAGER,
+					"InterfacesRemoved");
+	if (signal == NULL)
+		return;
+
+	dbus_message_iter_init_append(signal, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+								&data->path);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &array);
+
+	g_slist_foreach(data->removed, append_name, &array);
+	g_slist_free_full(data->removed, g_free);
+	data->removed = NULL;
+
+	dbus_message_iter_close_container(&iter, &array);
+
+	/* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+	dbus_connection_send(data->conn, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+static void remove_pending(struct generic_data *data)
+{
+	if (data->process_id > 0) {
+		g_source_remove(data->process_id);
+		data->process_id = 0;
+	}
+
+	pending = g_slist_remove(pending, data);
+}
+
+static gboolean process_changes(gpointer user_data)
+{
+	struct generic_data *data = user_data;
+
+	remove_pending(data);
+
+	if (data->added != NULL)
+		emit_interfaces_added(data);
+
+	/* Flush pending properties */
+	if (data->pending_prop == TRUE)
+		process_property_changes(data);
+
+	if (data->removed != NULL)
+		emit_interfaces_removed(data);
+
+	data->process_id = 0;
+
+	return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+	struct generic_data *data = user_data;
+	struct generic_data *parent = data->parent;
+
+	if (parent != NULL)
+		parent->objects = g_slist_remove(parent->objects, data);
+
+	if (data->process_id > 0) {
+		g_source_remove(data->process_id);
+		data->process_id = 0;
+		process_changes(data);
+	}
+
+	g_slist_foreach(data->objects, reset_parent, data->parent);
+	g_slist_free(data->objects);
+
+	dbus_connection_unref(data->conn);
+	g_free(data->introspect);
+	g_free(data->path);
+	g_free(data);
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct generic_data *data = user_data;
+	struct interface_data *iface;
+	const GDBusMethodTable *method;
+	const char *interface;
+
+	interface = dbus_message_get_interface(message);
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	for (method = iface->methods; method &&
+			method->name && method->function; method++) {
+
+		if (dbus_message_is_method_call(message, iface->name,
+							method->name) == FALSE)
+			continue;
+
+		if (check_experimental(method->flags,
+					G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+		if (g_dbus_args_have_signature(method->in_args,
+							message) == FALSE)
+			continue;
+
+		if (check_privilege(connection, message, method,
+						iface->user_data) == TRUE)
+			return DBUS_HANDLER_RESULT_HANDLED;
+
+		return process_message(connection, message, method,
+							iface->user_data);
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+	.unregister_function	= generic_unregister,
+	.message_function	= generic_message,
+};
+
+static const GDBusMethodTable introspect_methods[] = {
+	{ GDBUS_METHOD("Introspect", NULL,
+			GDBUS_ARGS({ "xml", "s" }), introspect) },
+	{ }
+};
+
+static void append_interfaces(struct generic_data *data, DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+				DBUS_TYPE_STRING_AS_STRING
+				DBUS_TYPE_ARRAY_AS_STRING
+				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+				DBUS_TYPE_STRING_AS_STRING
+				DBUS_TYPE_VARIANT_AS_STRING
+				DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+				DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+	g_slist_foreach(data->interfaces, append_interface, &array);
+
+	dbus_message_iter_close_container(iter, &array);
+}
+
+static void append_object(gpointer data, gpointer user_data)
+{
+	struct generic_data *child = data;
+	DBusMessageIter *array = user_data;
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+								&entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+								&child->path);
+	append_interfaces(child, &entry);
+	dbus_message_iter_close_container(array, &entry);
+
+	g_slist_foreach(child->objects, append_object, user_data);
+}
+
+static DBusMessage *get_objects(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct generic_data *data = user_data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter array;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_OBJECT_PATH_AS_STRING
+					DBUS_TYPE_ARRAY_AS_STRING
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_ARRAY_AS_STRING
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&array);
+
+	g_slist_foreach(data->objects, append_object, &array);
+
+	dbus_message_iter_close_container(&iter, &array);
+
+	return reply;
+}
+
+static const GDBusMethodTable manager_methods[] = {
+	{ GDBUS_METHOD("GetManagedObjects", NULL,
+		GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) },
+	{ }
+};
+
+static const GDBusSignalTable manager_signals[] = {
+	{ GDBUS_SIGNAL("InterfacesAdded",
+		GDBUS_ARGS({ "object", "o" },
+				{ "interfaces", "a{sa{sv}}" })) },
+	{ GDBUS_SIGNAL("InterfacesRemoved",
+		GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) },
+	{ }
+};
+
+static gboolean add_interface(struct generic_data *data,
+				const char *name,
+				const GDBusMethodTable *methods,
+				const GDBusSignalTable *signals,
+				const GDBusPropertyTable *properties,
+				void *user_data,
+				GDBusDestroyFunction destroy)
+{
+	struct interface_data *iface;
+	const GDBusMethodTable *method;
+	const GDBusSignalTable *signal;
+	const GDBusPropertyTable *property;
+
+	for (method = methods; method && method->name; method++) {
+		if (!check_experimental(method->flags,
+					G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+			goto done;
+	}
+
+	for (signal = signals; signal && signal->name; signal++) {
+		if (!check_experimental(signal->flags,
+					G_DBUS_SIGNAL_FLAG_EXPERIMENTAL))
+			goto done;
+	}
+
+	for (property = properties; property && property->name; property++) {
+		if (!check_experimental(property->flags,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+			goto done;
+	}
+
+	/* Nothing to register */
+	return FALSE;
+
+done:
+	iface = g_new0(struct interface_data, 1);
+	iface->name = g_strdup(name);
+	iface->methods = methods;
+	iface->signals = signals;
+	iface->properties = properties;
+	iface->user_data = user_data;
+	iface->destroy = destroy;
+
+	data->interfaces = g_slist_append(data->interfaces, iface);
+	if (data->parent == NULL)
+		return TRUE;
+
+	data->added = g_slist_append(data->added, iface);
+
+	add_pending(data);
+
+	return TRUE;
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+							const char *path)
+{
+	struct generic_data *data;
+
+	if (dbus_connection_get_object_path_data(connection, path,
+						(void *) &data) == TRUE) {
+		if (data != NULL) {
+			data->refcount++;
+			return data;
+		}
+	}
+
+	data = g_new0(struct generic_data, 1);
+	data->conn = dbus_connection_ref(connection);
+	data->path = g_strdup(path);
+	data->refcount = 1;
+
+	data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+	if (!dbus_connection_register_object_path(connection, path,
+						&generic_table, data)) {
+		dbus_connection_unref(data->conn);
+		g_free(data->path);
+		g_free(data->introspect);
+		g_free(data);
+		return NULL;
+	}
+
+	invalidate_parent_data(connection, path);
+
+	add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods,
+						NULL, NULL, data, NULL);
+
+	return data;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+	struct generic_data *data = NULL;
+
+	if (dbus_connection_get_object_path_data(connection, path,
+						(void *) &data) == FALSE)
+		return;
+
+	if (data == NULL)
+		return;
+
+	data->refcount--;
+
+	if (data->refcount > 0)
+		return;
+
+	remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+	remove_interface(data, DBUS_INTERFACE_PROPERTIES);
+
+	invalidate_parent_data(data->conn, data->path);
+
+	dbus_connection_unregister_object_path(data->conn, data->path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+				const char *interface, const char *name,
+				const GDBusArgInfo **args)
+{
+	struct generic_data *data = NULL;
+	struct interface_data *iface;
+	const GDBusSignalTable *signal;
+
+	*args = NULL;
+	if (!dbus_connection_get_object_path_data(conn, path,
+					(void *) &data) || data == NULL) {
+		error("dbus_connection_emit_signal: path %s isn't registered",
+				path);
+		return FALSE;
+	}
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL) {
+		error("dbus_connection_emit_signal: %s does not implement %s",
+				path, interface);
+		return FALSE;
+	}
+
+	for (signal = iface->signals; signal && signal->name; signal++) {
+		if (strcmp(signal->name, name) != 0)
+			continue;
+
+		if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) {
+			const char *env = g_getenv("GDBUS_EXPERIMENTAL");
+			if (g_strcmp0(env, "1") != 0)
+				break;
+		}
+
+		*args = signal->args;
+		return TRUE;
+	}
+
+	error("No signal named %s on interface %s", name, interface);
+	return FALSE;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+					const char *path, const char *name,
+					const GDBusMethodTable *methods,
+					const GDBusSignalTable *signals,
+					const GDBusPropertyTable *properties,
+					void *user_data,
+					GDBusDestroyFunction destroy)
+{
+	struct generic_data *data;
+
+	data = object_path_ref(connection, path);
+	if (data == NULL)
+		return FALSE;
+
+	if (find_interface(data->interfaces, name)) {
+		object_path_unref(connection, path);
+		return FALSE;
+	}
+
+	if (!add_interface(data, name, methods, signals, properties, user_data,
+								destroy)) {
+		object_path_unref(connection, path);
+		return FALSE;
+	}
+
+	if (properties != NULL && !find_interface(data->interfaces,
+						DBUS_INTERFACE_PROPERTIES))
+		add_interface(data, DBUS_INTERFACE_PROPERTIES,
+				properties_methods, properties_signals, NULL,
+				data, NULL);
+
+	g_free(data->introspect);
+	data->introspect = NULL;
+
+	return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+					const char *path, const char *name)
+{
+	struct generic_data *data = NULL;
+
+	if (path == NULL)
+		return FALSE;
+
+	if (dbus_connection_get_object_path_data(connection, path,
+						(void *) &data) == FALSE)
+		return FALSE;
+
+	if (data == NULL)
+		return FALSE;
+
+	if (remove_interface(data, name) == FALSE)
+		return FALSE;
+
+	g_free(data->introspect);
+	data->introspect = NULL;
+
+	object_path_unref(connection, data->path);
+
+	return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+	if (security_table != NULL)
+		return FALSE;
+
+	security_table = security;
+
+	return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+	security_table = NULL;
+
+	return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+					const char *format, va_list args)
+{
+	char str[1024];
+
+	if (format)
+		vsnprintf(str, sizeof(str), format, args);
+	else
+		str[0] = '\0';
+
+	return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+						const char *format, ...)
+{
+	va_list args;
+	DBusMessage *reply;
+
+	va_start(args, format);
+
+	reply = g_dbus_create_error_valist(message, name, format, args);
+
+	va_end(args);
+
+	return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+						int type, va_list args)
+{
+	DBusMessage *reply;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+		dbus_message_unref(reply);
+		return NULL;
+	}
+
+	return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+	va_list args;
+	DBusMessage *reply;
+
+	va_start(args, type);
+
+	reply = g_dbus_create_reply_valist(message, type, args);
+
+	va_end(args);
+
+	return reply;
+}
+
+static void g_dbus_flush(DBusConnection *connection)
+{
+	GSList *l;
+
+	for (l = pending; l;) {
+		struct generic_data *data = l->data;
+
+		l = l->next;
+		if (data->conn != connection)
+			continue;
+
+		process_changes(data);
+	}
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+	dbus_bool_t result = FALSE;
+
+	if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+		dbus_message_set_no_reply(message, TRUE);
+	else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) {
+		const char *path = dbus_message_get_path(message);
+		const char *interface = dbus_message_get_interface(message);
+		const char *name = dbus_message_get_member(message);
+		const GDBusArgInfo *args;
+
+		if (!check_signal(connection, path, interface, name, &args))
+			goto out;
+	}
+
+	/* Flush pending signal to guarantee message order */
+	g_dbus_flush(connection);
+
+	result = dbus_connection_send(connection, message, NULL);
+
+out:
+	dbus_message_unref(message);
+
+	return result;
+}
+
+gboolean g_dbus_send_message_with_reply(DBusConnection *connection,
+					DBusMessage *message,
+					DBusPendingCall **call, int timeout)
+{
+	dbus_bool_t ret;
+
+	/* Flush pending signal to guarantee message order */
+	g_dbus_flush(connection);
+
+	ret = dbus_connection_send_with_reply(connection, message, call,
+								timeout);
+
+	if (ret == TRUE && call != NULL && *call == NULL) {
+		error("Unable to send message (passing fd blocked?)");
+		return FALSE;
+	}
+
+	return ret;
+}
+
+gboolean g_dbus_send_error_valist(DBusConnection *connection,
+					DBusMessage *message, const char *name,
+					const char *format, va_list args)
+{
+	DBusMessage *error;
+
+	error = g_dbus_create_error_valist(message, name, format, args);
+	if (error == NULL)
+		return FALSE;
+
+	return g_dbus_send_message(connection, error);
+}
+
+gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message,
+				const char *name, const char *format, ...)
+{
+	va_list args;
+	gboolean result;
+
+	va_start(args, format);
+
+	result = g_dbus_send_error_valist(connection, message, name,
+							format, args);
+
+	va_end(args);
+
+	return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+				DBusMessage *message, int type, va_list args)
+{
+	DBusMessage *reply;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return FALSE;
+
+	if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+		dbus_message_unref(reply);
+		return FALSE;
+	}
+
+	return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+				DBusMessage *message, int type, ...)
+{
+	va_list args;
+	gboolean result;
+
+	va_start(args, type);
+
+	result = g_dbus_send_reply_valist(connection, message, type, args);
+
+	va_end(args);
+
+	return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name, int type, ...)
+{
+	va_list args;
+	gboolean result;
+
+	va_start(args, type);
+
+	result = g_dbus_emit_signal_valist(connection, path, interface,
+							name, type, args);
+
+	va_end(args);
+
+	return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name, int type, va_list args)
+{
+	DBusMessage *signal;
+	dbus_bool_t ret;
+	const GDBusArgInfo *args_info;
+
+	if (!check_signal(connection, path, interface, name, &args_info))
+		return FALSE;
+
+	signal = dbus_message_new_signal(path, interface, name);
+	if (signal == NULL) {
+		error("Unable to allocate new %s.%s signal", interface,  name);
+		return FALSE;
+	}
+
+	ret = dbus_message_append_args_valist(signal, type, args);
+	if (!ret)
+		goto fail;
+
+	if (g_dbus_args_have_signature(args_info, signal) == FALSE) {
+		error("%s.%s: got unexpected signature '%s'", interface, name,
+					dbus_message_get_signature(signal));
+		ret = FALSE;
+		goto fail;
+	}
+
+	return g_dbus_send_message(connection, signal);
+
+fail:
+	dbus_message_unref(signal);
+
+	return ret;
+}
+
+static void process_properties_from_interface(struct generic_data *data,
+						struct interface_data *iface)
+{
+	GSList *l;
+	DBusMessage *signal;
+	DBusMessageIter iter, dict, array;
+	GSList *invalidated;
+
+	data->pending_prop = FALSE;
+
+	if (iface->pending_prop == NULL)
+		return;
+
+	signal = dbus_message_new_signal(data->path,
+			DBUS_INTERFACE_PROPERTIES, "PropertiesChanged");
+	if (signal == NULL) {
+		error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES
+						".PropertiesChanged signal");
+		return;
+	}
+
+	iface->pending_prop = g_slist_reverse(iface->pending_prop);
+
+	dbus_message_iter_init_append(signal, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,	&iface->name);
+	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);
+
+	invalidated = NULL;
+
+	for (l = iface->pending_prop; l != NULL; l = l->next) {
+		GDBusPropertyTable *p = l->data;
+
+		if (p->get == NULL)
+			continue;
+
+		if (p->exists != NULL && !p->exists(p, iface->user_data)) {
+			invalidated = g_slist_prepend(invalidated, p);
+			continue;
+		}
+
+		append_property(iface, p, &dict);
+	}
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_STRING_AS_STRING, &array);
+	for (l = invalidated; l != NULL; l = g_slist_next(l)) {
+		GDBusPropertyTable *p = l->data;
+
+		dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+								&p->name);
+	}
+	g_slist_free(invalidated);
+	dbus_message_iter_close_container(&iter, &array);
+
+	g_slist_free(iface->pending_prop);
+	iface->pending_prop = NULL;
+
+	/* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+	dbus_connection_send(data->conn, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+static void process_property_changes(struct generic_data *data)
+{
+	GSList *l;
+
+	for (l = data->interfaces; l != NULL; l = l->next) {
+		struct interface_data *iface = l->data;
+
+		process_properties_from_interface(data, iface);
+	}
+}
+
+void g_dbus_emit_property_changed_full(DBusConnection *connection,
+				const char *path, const char *interface,
+				const char *name,
+				GDbusPropertyChangedFlags flags)
+{
+	const GDBusPropertyTable *property;
+	struct generic_data *data;
+	struct interface_data *iface;
+
+	if (path == NULL)
+		return;
+
+	if (!dbus_connection_get_object_path_data(connection, path,
+					(void **) &data) || data == NULL)
+		return;
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL)
+		return;
+
+	/*
+	 * If ObjectManager is attached, don't emit property changed if
+	 * interface is not yet published
+	 */
+	if (root && g_slist_find(data->added, iface))
+		return;
+
+	property = find_property(iface->properties, name);
+	if (property == NULL) {
+		error("Could not find property %s in %p", name,
+							iface->properties);
+		return;
+	}
+
+	if (g_slist_find(iface->pending_prop, (void *) property) != NULL)
+		return;
+
+	data->pending_prop = TRUE;
+	iface->pending_prop = g_slist_prepend(iface->pending_prop,
+						(void *) property);
+
+	if (flags & G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH)
+		process_property_changes(data);
+	else
+		add_pending(data);
+}
+
+void g_dbus_emit_property_changed(DBusConnection *connection, const char *path,
+				const char *interface, const char *name)
+{
+	g_dbus_emit_property_changed_full(connection, path, interface, name, 0);
+}
+
+gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
+				const char *interface, DBusMessageIter *iter)
+{
+	struct generic_data *data;
+	struct interface_data *iface;
+
+	if (path == NULL)
+		return FALSE;
+
+	if (!dbus_connection_get_object_path_data(connection, path,
+					(void **) &data) || data == NULL)
+		return FALSE;
+
+	iface = find_interface(data->interfaces, interface);
+	if (iface == NULL)
+		return FALSE;
+
+	append_properties(iface, iter);
+
+	return TRUE;
+}
+
+gboolean g_dbus_attach_object_manager(DBusConnection *connection)
+{
+	struct generic_data *data;
+
+	data = object_path_ref(connection, "/");
+	if (data == NULL)
+		return FALSE;
+
+	add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER,
+					manager_methods, manager_signals,
+					NULL, data, NULL);
+	root = data;
+
+	return TRUE;
+}
+
+gboolean g_dbus_detach_object_manager(DBusConnection *connection)
+{
+	if (!g_dbus_unregister_interface(connection, "/",
+					DBUS_INTERFACE_OBJECT_MANAGER))
+		return FALSE;
+
+	root = NULL;
+
+	return TRUE;
+}
+
+void g_dbus_set_flags(int flags)
+{
+	global_flags = flags;
+}
+
+int g_dbus_get_flags(void)
+{
+	return global_flags;
+}
diff --git a/bluez/repo/gdbus/polkit.c b/bluez/repo/gdbus/polkit.c
new file mode 100644
index 0000000..9e95fa3
--- /dev/null
+++ b/bluez/repo/gdbus/polkit.c
@@ -0,0 +1,202 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+				const char *action, gboolean interaction,
+				void (*function) (dbus_bool_t authorized,
+							void *user_data),
+						void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+					const char *key, const char *str)
+{
+	DBusMessageIter dict, entry, value;
+
+	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_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+								NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					DBUS_TYPE_STRING_AS_STRING, &value);
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+	dbus_message_iter_close_container(&entry, &value);
+
+	dbus_message_iter_close_container(&dict, &entry);
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+	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_STRING_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+				const char *action, dbus_uint32_t flags)
+{
+	const char *busname = dbus_bus_get_unique_name(conn);
+	const char *kind = "system-bus-name";
+	const char *cancel = "";
+	DBusMessageIter subject;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+							NULL, &subject);
+	dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+	add_dict_with_string_value(&subject, "name", busname);
+	dbus_message_iter_close_container(iter, &subject);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+	add_empty_string_dict(iter);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+	DBusMessageIter result;
+	dbus_bool_t authorized, challenge;
+
+	dbus_message_iter_recurse(iter, &result);
+
+	dbus_message_iter_get_basic(&result, &authorized);
+	dbus_message_iter_get_basic(&result, &challenge);
+
+	return authorized;
+}
+
+struct authorization_data {
+	void (*function) (dbus_bool_t authorized, void *user_data);
+	void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+	struct authorization_data *data = user_data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	dbus_bool_t authorized = FALSE;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+		goto done;
+
+	if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+		goto done;
+
+	dbus_message_iter_init(reply, &iter);
+
+	authorized = parse_result(&iter);
+
+done:
+	if (data->function != NULL)
+		data->function(authorized, data->user_data);
+
+	dbus_message_unref(reply);
+
+	dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS	"org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF	"org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH	"/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+				const char *action, gboolean interaction,
+				void (*function) (dbus_bool_t authorized,
+							void *user_data),
+						void *user_data, int timeout)
+{
+	struct authorization_data *data;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	DBusPendingCall *call;
+	dbus_uint32_t flags = 0x00000000;
+
+	if (conn == NULL)
+		return -EINVAL;
+
+	data = dbus_malloc0(sizeof(*data));
+	if (data == NULL)
+		return -ENOMEM;
+
+	msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+				AUTHORITY_INTF, "CheckAuthorization");
+	if (msg == NULL) {
+		dbus_free(data);
+		return -ENOMEM;
+	}
+
+	if (interaction == TRUE)
+		flags |= 0x00000001;
+
+	if (action == NULL)
+		action = "org.freedesktop.policykit.exec";
+
+	dbus_message_iter_init_append(msg, &iter);
+	add_arguments(conn, &iter, action, flags);
+
+	if (dbus_connection_send_with_reply(conn, msg,
+						&call, timeout) == FALSE) {
+		dbus_message_unref(msg);
+		dbus_free(data);
+		return -EIO;
+	}
+
+	if (call == NULL) {
+		dbus_message_unref(msg);
+		dbus_free(data);
+		return -EIO;
+	}
+
+	data->function = function;
+	data->user_data = user_data;
+
+	dbus_pending_call_set_notify(call, authorization_reply,
+							data, dbus_free);
+
+	dbus_message_unref(msg);
+
+	return 0;
+}
diff --git a/bluez/repo/gdbus/watch.c b/bluez/repo/gdbus/watch.c
new file mode 100644
index 0000000..447e486
--- /dev/null
+++ b/bluez/repo/gdbus/watch.c
@@ -0,0 +1,818 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+					DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+	DBusConnection *conn;
+	DBusPendingCall *call;
+	char *name;
+	const char *owner;
+	guint id;
+	struct filter_callback *callback;
+};
+
+struct filter_callback {
+	GDBusWatchFunction conn_func;
+	GDBusWatchFunction disc_func;
+	GDBusSignalFunction signal_func;
+	GDBusDestroyFunction destroy_func;
+	struct service_data *data;
+	void *user_data;
+	guint id;
+};
+
+struct filter_data {
+	DBusConnection *connection;
+	DBusHandleMessageFunction handle_func;
+	char *name;
+	char *owner;
+	char *path;
+	char *interface;
+	char *member;
+	char *argument;
+	GSList *callbacks;
+	GSList *processed;
+	guint name_watch;
+	gboolean lock;
+	gboolean registered;
+};
+
+static struct filter_data *filter_data_find_match(DBusConnection *connection,
+							const char *name,
+							const char *owner,
+							const char *path,
+							const char *interface,
+							const char *member,
+							const char *argument)
+{
+	GSList *current;
+
+	for (current = listeners;
+			current != NULL; current = current->next) {
+		struct filter_data *data = current->data;
+
+		if (connection != data->connection)
+			continue;
+
+		if (g_strcmp0(name, data->name) != 0)
+			continue;
+
+		if (g_strcmp0(owner, data->owner) != 0)
+			continue;
+
+		if (g_strcmp0(path, data->path) != 0)
+			continue;
+
+		if (g_strcmp0(interface, data->interface) != 0)
+			continue;
+
+		if (g_strcmp0(member, data->member) != 0)
+			continue;
+
+		if (g_strcmp0(argument, data->argument) != 0)
+			continue;
+
+		return data;
+	}
+
+	return NULL;
+}
+
+static struct filter_data *filter_data_find(DBusConnection *connection)
+{
+	GSList *current;
+
+	for (current = listeners;
+			current != NULL; current = current->next) {
+		struct filter_data *data = current->data;
+
+		if (connection != data->connection)
+			continue;
+
+		return data;
+	}
+
+	return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+	const char *sender;
+	int offset;
+
+	offset = snprintf(rule, size, "type='signal'");
+	sender = data->name ? : data->owner;
+
+	if (sender)
+		offset += snprintf(rule + offset, size - offset,
+				",sender='%s'", sender);
+	if (data->path)
+		offset += snprintf(rule + offset, size - offset,
+				",path='%s'", data->path);
+	if (data->interface)
+		offset += snprintf(rule + offset, size - offset,
+				",interface='%s'", data->interface);
+	if (data->member)
+		offset += snprintf(rule + offset, size - offset,
+				",member='%s'", data->member);
+	if (data->argument)
+		snprintf(rule + offset, size - offset,
+				",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+				DBusHandleMessageFunction filter)
+{
+	DBusError err;
+	char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+	format_rule(data, rule, sizeof(rule));
+	dbus_error_init(&err);
+
+	dbus_bus_add_match(data->connection, rule, &err);
+	if (dbus_error_is_set(&err)) {
+		error("Adding match rule \"%s\" failed: %s", rule,
+				err.message);
+		dbus_error_free(&err);
+		return FALSE;
+	}
+
+	data->handle_func = filter;
+	data->registered = TRUE;
+
+	return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+	DBusError err;
+	char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+	format_rule(data, rule, sizeof(rule));
+
+	dbus_error_init(&err);
+
+	dbus_bus_remove_match(data->connection, rule, &err);
+	if (dbus_error_is_set(&err)) {
+		error("Removing owner match rule for %s failed: %s",
+				rule, err.message);
+		dbus_error_free(&err);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+	GSList *l;
+
+	/* Remove filter if there are no listeners left for the connection */
+	if (filter_data_find(data->connection) == NULL)
+		dbus_connection_remove_filter(data->connection, message_filter,
+									NULL);
+
+	for (l = data->callbacks; l != NULL; l = l->next)
+		g_free(l->data);
+
+	g_slist_free(data->callbacks);
+	g_dbus_remove_watch(data->connection, data->name_watch);
+	g_free(data->name);
+	g_free(data->owner);
+	g_free(data->path);
+	g_free(data->interface);
+	g_free(data->member);
+	g_free(data->argument);
+	dbus_connection_unref(data->connection);
+	g_free(data);
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+					DBusHandleMessageFunction filter,
+					const char *sender,
+					const char *path,
+					const char *interface,
+					const char *member,
+					const char *argument)
+{
+	struct filter_data *data;
+	const char *name = NULL, *owner = NULL;
+
+	if (filter_data_find(connection) == NULL) {
+		if (!dbus_connection_add_filter(connection,
+					message_filter, NULL, NULL)) {
+			error("dbus_connection_add_filter() failed");
+			return NULL;
+		}
+	}
+
+	if (sender == NULL)
+		goto proceed;
+
+	if (sender[0] == ':')
+		owner = sender;
+	else
+		name = sender;
+
+proceed:
+	data = filter_data_find_match(connection, name, owner, path,
+						interface, member, argument);
+	if (data)
+		return data;
+
+	data = g_new0(struct filter_data, 1);
+
+	data->connection = dbus_connection_ref(connection);
+	data->name = g_strdup(name);
+	data->owner = g_strdup(owner);
+	data->path = g_strdup(path);
+	data->interface = g_strdup(interface);
+	data->member = g_strdup(member);
+	data->argument = g_strdup(argument);
+
+	if (!add_match(data, filter)) {
+		filter_data_free(data);
+		return NULL;
+	}
+
+	listeners = g_slist_append(listeners, data);
+
+	return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+						struct filter_data *data,
+						guint id)
+{
+	GSList *l;
+
+	for (l = data->callbacks; l; l = l->next) {
+		struct filter_callback *cb = l->data;
+		if (cb->id == id)
+			return cb;
+	}
+	for (l = data->processed; l; l = l->next) {
+		struct filter_callback *cb = l->data;
+		if (cb->id == id)
+			return cb;
+	}
+
+	return NULL;
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+	GSList *l;
+
+	for (l = data->callbacks; l != NULL; l = l->next) {
+		struct filter_callback *cb = l->data;
+		if (cb->disc_func)
+			cb->disc_func(data->connection, cb->user_data);
+		if (cb->destroy_func)
+			cb->destroy_func(cb->user_data);
+		g_free(cb);
+	}
+
+	filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+						struct filter_data *data,
+						GDBusWatchFunction connect,
+						GDBusWatchFunction disconnect,
+						GDBusSignalFunction signal,
+						GDBusDestroyFunction destroy,
+						void *user_data)
+{
+	struct filter_callback *cb = NULL;
+
+	cb = g_new0(struct filter_callback, 1);
+
+	cb->conn_func = connect;
+	cb->disc_func = disconnect;
+	cb->signal_func = signal;
+	cb->destroy_func = destroy;
+	cb->user_data = user_data;
+	cb->id = ++listener_id;
+
+	if (data->lock)
+		data->processed = g_slist_append(data->processed, cb);
+	else
+		data->callbacks = g_slist_append(data->callbacks, cb);
+
+	return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+	struct filter_callback *callback = data->callback;
+
+	dbus_connection_unref(data->conn);
+
+	if (data->call)
+		dbus_pending_call_unref(data->call);
+
+	if (data->id)
+		g_source_remove(data->id);
+
+	g_free(data->name);
+	g_free(data);
+
+	callback->data = NULL;
+}
+
+/* Returns TRUE if data is freed */
+static gboolean filter_data_remove_callback(struct filter_data *data,
+						struct filter_callback *cb)
+{
+	data->callbacks = g_slist_remove(data->callbacks, cb);
+	data->processed = g_slist_remove(data->processed, cb);
+
+	/* Cancel pending operations */
+	if (cb->data) {
+		if (cb->data->call)
+			dbus_pending_call_cancel(cb->data->call);
+		service_data_free(cb->data);
+	}
+
+	if (cb->destroy_func)
+		cb->destroy_func(cb->user_data);
+
+	g_free(cb);
+
+	/* Don't remove the filter if other callbacks exist or data is lock
+	 * processing callbacks */
+	if (data->callbacks || data->lock)
+		return FALSE;
+
+	if (data->registered && !remove_match(data))
+		return FALSE;
+
+	listeners = g_slist_remove(listeners, data);
+	filter_data_free(data);
+
+	return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct filter_data *data = user_data;
+	struct filter_callback *cb;
+
+	while (data->callbacks) {
+		cb = data->callbacks->data;
+
+		if (cb->signal_func && !cb->signal_func(connection, message,
+							cb->user_data)) {
+			if (filter_data_remove_callback(data, cb))
+				break;
+
+			continue;
+		}
+
+		/* Check if the watch was removed/freed by the callback
+		 * function */
+		if (!g_slist_find(data->callbacks, cb))
+			continue;
+
+		data->callbacks = g_slist_remove(data->callbacks, cb);
+		data->processed = g_slist_append(data->processed, cb);
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+	GSList *l;
+
+	for (l = listeners; l != NULL; l = l->next) {
+		struct filter_data *data = l->data;
+
+		if (g_strcmp0(data->name, name) != 0)
+			continue;
+
+		g_free(data->owner);
+		data->owner = g_strdup(owner);
+	}
+}
+
+static const char *check_name_cache(const char *name)
+{
+	GSList *l;
+
+	for (l = listeners; l != NULL; l = l->next) {
+		struct filter_data *data = l->data;
+
+		if (g_strcmp0(data->name, name) != 0)
+			continue;
+
+		return data->owner;
+	}
+
+	return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct filter_data *data = user_data;
+	struct filter_callback *cb;
+	char *name, *old, *new;
+
+	if (!dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &name,
+				DBUS_TYPE_STRING, &old,
+				DBUS_TYPE_STRING, &new,
+				DBUS_TYPE_INVALID)) {
+		error("Invalid arguments for NameOwnerChanged signal");
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+
+	update_name_cache(name, new);
+
+	while (data->callbacks) {
+		cb = data->callbacks->data;
+
+		if (*new == '\0') {
+			if (cb->disc_func)
+				cb->disc_func(connection, cb->user_data);
+		} else {
+			if (cb->conn_func)
+				cb->conn_func(connection, cb->user_data);
+		}
+
+		/* Check if the watch was removed/freed by the callback
+		 * function */
+		if (!g_slist_find(data->callbacks, cb))
+			continue;
+
+		/* Only auto remove if it is a bus name watch */
+		if (data->argument[0] == ':' &&
+				(cb->conn_func == NULL || cb->disc_func == NULL)) {
+			if (filter_data_remove_callback(data, cb))
+				break;
+
+			continue;
+		}
+
+		data->callbacks = g_slist_remove(data->callbacks, cb);
+		data->processed = g_slist_append(data->processed, cb);
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct filter_data *data;
+	const char *sender, *path, *iface, *member, *arg = NULL;
+	GSList *current, *delete_listener = NULL;
+
+	/* Only filter signals */
+	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	sender = dbus_message_get_sender(message);
+	path = dbus_message_get_path(message);
+	iface = dbus_message_get_interface(message);
+	member = dbus_message_get_member(message);
+	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+	/* If sender != NULL it is always the owner */
+
+	for (current = listeners; current != NULL; current = current->next) {
+		data = current->data;
+
+		if (connection != data->connection)
+			continue;
+
+		if (!sender && data->owner)
+			continue;
+
+		if (data->owner && g_str_equal(sender, data->owner) == FALSE)
+			continue;
+
+		if (data->path && g_str_equal(path, data->path) == FALSE)
+			continue;
+
+		if (data->interface && g_str_equal(iface,
+						data->interface) == FALSE)
+			continue;
+
+		if (data->member && g_str_equal(member, data->member) == FALSE)
+			continue;
+
+		if (data->argument && g_str_equal(arg,
+						data->argument) == FALSE)
+			continue;
+
+		if (data->handle_func) {
+			data->lock = TRUE;
+
+			data->handle_func(connection, message, data);
+
+			data->callbacks = data->processed;
+			data->processed = NULL;
+			data->lock = FALSE;
+		}
+
+		if (!data->callbacks)
+			delete_listener = g_slist_prepend(delete_listener,
+								current);
+	}
+
+	if (delete_listener == NULL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	for (current = delete_listener; current != NULL;
+					current = delete_listener->next) {
+		GSList *l = current->data;
+
+		data = l->data;
+
+		/* Has any other callback added callbacks back to this data? */
+		if (data->callbacks != NULL)
+			continue;
+
+		remove_match(data);
+		listeners = g_slist_delete_link(listeners, l);
+
+		filter_data_free(data);
+	}
+
+	g_slist_free(delete_listener);
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+	struct service_data *data = user_data;
+	struct filter_callback *cb = data->callback;
+	DBusConnection *conn;
+
+	conn = dbus_connection_ref(data->conn);
+	service_data_free(data);
+
+	if (cb->conn_func)
+		cb->conn_func(conn, cb->user_data);
+
+	dbus_connection_unref(conn);
+
+	return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+	struct service_data *data = user_data;
+	DBusMessage *reply;
+	DBusError err;
+
+	reply = dbus_pending_call_steal_reply(call);
+	if (reply == NULL)
+		return;
+
+	dbus_error_init(&err);
+
+	if (dbus_set_error_from_message(&err, reply))
+		goto fail;
+
+	if (dbus_message_get_args(reply, &err,
+					DBUS_TYPE_STRING, &data->owner,
+						DBUS_TYPE_INVALID) == FALSE)
+		goto fail;
+
+	update_service(data);
+
+	goto done;
+
+fail:
+	error("%s", err.message);
+	dbus_error_free(&err);
+	service_data_free(data);
+done:
+	dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+					const char *name,
+					struct filter_callback *callback)
+{
+	DBusMessage *message;
+	struct service_data *data;
+
+	data = g_try_malloc0(sizeof(*data));
+	if (data == NULL) {
+		error("Can't allocate data structure");
+		return;
+	}
+
+	data->conn = dbus_connection_ref(connection);
+	data->name = g_strdup(name);
+	data->callback = callback;
+	callback->data = data;
+
+	data->owner = check_name_cache(name);
+	if (data->owner != NULL) {
+		data->id = g_idle_add(update_service, data);
+		return;
+	}
+
+	message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+			DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+	if (message == NULL) {
+		error("Can't allocate new message");
+		g_free(data);
+		return;
+	}
+
+	dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+							DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, message,
+							&data->call, -1) == FALSE) {
+		error("Failed to execute method call");
+		g_free(data);
+		goto done;
+	}
+
+	if (data->call == NULL) {
+		error("D-Bus connection not available");
+		g_free(data);
+		goto done;
+	}
+
+	dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+	dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+				GDBusWatchFunction connect,
+				GDBusWatchFunction disconnect,
+				void *user_data, GDBusDestroyFunction destroy)
+{
+	struct filter_data *data;
+	struct filter_callback *cb;
+
+	if (name == NULL)
+		return 0;
+
+	data = filter_data_get(connection, service_filter,
+				DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+				DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+				name);
+	if (data == NULL)
+		return 0;
+
+	cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
+					user_data);
+	if (cb == NULL)
+		return 0;
+
+	if (connect)
+		check_service(connection, name, cb);
+
+	return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+				GDBusWatchFunction func,
+				void *user_data, GDBusDestroyFunction destroy)
+{
+	return g_dbus_add_service_watch(connection, name, NULL, func,
+							user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+				const char *sender, const char *path,
+				const char *interface, const char *member,
+				GDBusSignalFunction function, void *user_data,
+				GDBusDestroyFunction destroy)
+{
+	struct filter_data *data;
+	struct filter_callback *cb;
+
+	data = filter_data_get(connection, signal_filter, sender, path,
+				interface, member, NULL);
+	if (data == NULL)
+		return 0;
+
+	cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+					user_data);
+	if (cb == NULL)
+		return 0;
+
+	if (data->name != NULL && data->name_watch == 0)
+		data->name_watch = g_dbus_add_service_watch(connection,
+							data->name, NULL,
+							NULL, NULL, NULL);
+
+	return cb->id;
+}
+
+guint g_dbus_add_properties_watch(DBusConnection *connection,
+				const char *sender, const char *path,
+				const char *interface,
+				GDBusSignalFunction function, void *user_data,
+				GDBusDestroyFunction destroy)
+{
+	struct filter_data *data;
+	struct filter_callback *cb;
+
+	data = filter_data_get(connection, signal_filter, sender, path,
+				DBUS_INTERFACE_PROPERTIES, "PropertiesChanged",
+				interface);
+	if (data == NULL)
+		return 0;
+
+	cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+					user_data);
+	if (cb == NULL)
+		return 0;
+
+	if (data->name != NULL && data->name_watch == 0)
+		data->name_watch = g_dbus_add_service_watch(connection,
+							data->name, NULL,
+							NULL, NULL, NULL);
+
+	return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+	struct filter_data *data;
+	struct filter_callback *cb;
+	GSList *ldata;
+
+	if (id == 0)
+		return FALSE;
+
+	for (ldata = listeners; ldata; ldata = ldata->next) {
+		data = ldata->data;
+
+		cb = filter_data_find_callback(data, id);
+		if (cb) {
+			filter_data_remove_callback(data, cb);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+	struct filter_data *data;
+
+	while ((data = filter_data_find(connection))) {
+		listeners = g_slist_remove(listeners, data);
+		filter_data_call_and_free(data);
+	}
+}
diff --git a/bluez/repo/gobex/._gobex-apparam.c b/bluez/repo/gobex/._gobex-apparam.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-apparam.c
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-apparam.h b/bluez/repo/gobex/._gobex-apparam.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-apparam.h
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-debug.h b/bluez/repo/gobex/._gobex-debug.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-debug.h
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-defs.c b/bluez/repo/gobex/._gobex-defs.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-defs.c
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-defs.h b/bluez/repo/gobex/._gobex-defs.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-defs.h
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-header.c b/bluez/repo/gobex/._gobex-header.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-header.c
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-header.h b/bluez/repo/gobex/._gobex-header.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-header.h
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-packet.c b/bluez/repo/gobex/._gobex-packet.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-packet.c
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-packet.h b/bluez/repo/gobex/._gobex-packet.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-packet.h
Binary files differ
diff --git a/bluez/repo/gobex/._gobex-transfer.c b/bluez/repo/gobex/._gobex-transfer.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex-transfer.c
Binary files differ
diff --git a/bluez/repo/gobex/._gobex.c b/bluez/repo/gobex/._gobex.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex.c
Binary files differ
diff --git a/bluez/repo/gobex/._gobex.h b/bluez/repo/gobex/._gobex.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/gobex/._gobex.h
Binary files differ
diff --git a/bluez/repo/gobex/gobex-apparam.c b/bluez/repo/gobex/gobex-apparam.c
new file mode 100644
index 0000000..4328172
--- /dev/null
+++ b/bluez/repo/gobex/gobex-apparam.c
@@ -0,0 +1,365 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2012  Intel Corporation.
+ *
+ *  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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "gobex-apparam.h"
+#include "gobex-debug.h"
+
+struct _GObexApparam {
+	GHashTable *tags;
+};
+
+struct apparam_tag {
+	guint8 id;
+	guint8 len;
+	union {
+		/* Data is stored in network order */
+		char string[0];
+		guint8 data[0];
+		guint8 u8;
+		guint16 u16;
+		guint32 u32;
+		guint64 u64;
+	} value;
+} __attribute__ ((packed));
+
+static struct apparam_tag *tag_new(guint8 id, guint8 len, const void *data)
+{
+	struct apparam_tag *tag;
+
+	tag = g_malloc0(2 + len);
+	tag->id = id;
+	tag->len = len;
+	memcpy(tag->value.data, data, len);
+
+	return tag;
+}
+
+static GObexApparam *g_obex_apparam_new(void)
+{
+	GObexApparam *apparam;
+
+	apparam = g_new0(GObexApparam, 1);
+	apparam->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+							NULL, g_free);
+
+	return apparam;
+}
+
+static struct apparam_tag *apparam_tag_decode(const void *data, gsize size,
+							gsize *parsed)
+{
+	struct apparam_tag *tag;
+	const guint8 *ptr = data;
+	guint8 id;
+	guint8 len;
+
+	if (size < 2)
+		return NULL;
+
+	id = ptr[0];
+	len = ptr[1];
+
+	if (len > size - 2)
+		return NULL;
+
+	tag = tag_new(id, len, ptr + 2);
+	if (tag == NULL)
+		return NULL;
+
+	*parsed = 2 + tag->len;
+
+	return tag;
+}
+
+GObexApparam *g_obex_apparam_decode(const void *data, gsize size)
+{
+	GObexApparam *apparam;
+	GHashTable *tags;
+	gsize count = 0;
+
+	if (size < 2)
+		return NULL;
+
+	apparam = g_obex_apparam_new();
+
+	tags = apparam->tags;
+	while (count < size) {
+		struct apparam_tag *tag;
+		gsize parsed;
+		guint id;
+
+		tag = apparam_tag_decode(data + count, size - count, &parsed);
+		if (tag == NULL)
+			break;
+
+		id = tag->id;
+		g_hash_table_insert(tags, GUINT_TO_POINTER(id), tag);
+
+		count += parsed;
+	}
+
+	if (count != size) {
+		g_obex_apparam_free(apparam);
+		return NULL;
+	}
+
+	return apparam;
+}
+
+static gssize tag_encode(struct apparam_tag *tag, void *buf, gsize len)
+{
+	gsize count = 2 + tag->len;
+
+	if (len < count)
+		return -ENOBUFS;
+
+	memcpy(buf, tag, count);
+
+	return count;
+}
+
+gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len)
+{
+	gsize count = 0;
+	gssize ret;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	g_hash_table_iter_init(&iter, apparam->tags);
+	while (g_hash_table_iter_next(&iter, &key, &value)) {
+		struct apparam_tag *tag = value;
+
+		ret = tag_encode(tag, buf + count, len - count);
+		if (ret < 0)
+			return ret;
+
+		count += ret;
+	}
+
+	return count;
+}
+
+GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id,
+						const void *value, gsize len)
+{
+	struct apparam_tag *tag;
+	guint uid = id;
+
+	if (apparam == NULL)
+		apparam = g_obex_apparam_new();
+
+	tag = tag_new(id, len, value);
+	g_hash_table_replace(apparam->tags, GUINT_TO_POINTER(uid), tag);
+
+	return apparam;
+}
+
+GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id,
+							guint8 value)
+{
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value);
+
+	return g_obex_apparam_set_bytes(apparam, id, &value, 1);
+}
+
+GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id,
+							guint16 value)
+{
+	guint16 num = g_htons(value);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value);
+
+	return g_obex_apparam_set_bytes(apparam, id, &num, 2);
+}
+
+GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id,
+							guint32 value)
+{
+	guint32 num = g_htonl(value);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value);
+
+	return g_obex_apparam_set_bytes(apparam, id, &num, 4);
+}
+
+GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id,
+							guint64 value)
+{
+	guint64 num = GUINT64_TO_BE(value);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %"
+						G_GUINT64_FORMAT, id, value);
+
+	return g_obex_apparam_set_bytes(apparam, id, &num, 8);
+}
+
+GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id,
+							const char *value)
+{
+	gsize len;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %s", id, value);
+
+	len = strlen(value) + 1;
+	if (len > G_MAXUINT8) {
+		((char *) value)[G_MAXUINT8 - 1] = '\0';
+		len = G_MAXUINT8;
+	}
+
+	return g_obex_apparam_set_bytes(apparam, id, value, len);
+}
+
+static struct apparam_tag *g_obex_apparam_find_tag(GObexApparam *apparam,
+								guint id)
+{
+	return g_hash_table_lookup(apparam->tags, GUINT_TO_POINTER(id));
+}
+
+gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id,
+							guint8 *dest)
+{
+	struct apparam_tag *tag;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id);
+
+	tag = g_obex_apparam_find_tag(apparam, id);
+	if (tag == NULL)
+		return FALSE;
+
+	*dest = tag->value.u8;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest);
+
+	return TRUE;
+}
+
+gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id,
+							guint16 *dest)
+{
+	struct apparam_tag *tag;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id);
+
+	tag = g_obex_apparam_find_tag(apparam, id);
+	if (tag == NULL)
+		return FALSE;
+
+	if (tag->len < sizeof(*dest))
+		return FALSE;
+
+	*dest = g_ntohs(tag->value.u16);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest);
+
+	return TRUE;
+}
+
+gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id,
+							guint32 *dest)
+{
+	struct apparam_tag *tag;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id);
+
+	tag = g_obex_apparam_find_tag(apparam, id);
+	if (tag == NULL)
+		return FALSE;
+
+	if (tag->len < sizeof(*dest))
+		return FALSE;
+
+	*dest = g_ntohl(tag->value.u32);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest);
+
+	return TRUE;
+}
+
+gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id,
+							guint64 *dest)
+{
+	struct apparam_tag *tag;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id);
+
+	tag = g_obex_apparam_find_tag(apparam, id);
+	if (tag == NULL)
+		return FALSE;
+
+	if (tag->len < sizeof(*dest))
+		return FALSE;
+
+	*dest = GUINT64_FROM_BE(tag->value.u64);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "%" G_GUINT64_FORMAT, *dest);
+
+	return TRUE;
+}
+
+char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id)
+{
+	struct apparam_tag *tag;
+	char *string;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id);
+
+	tag = g_obex_apparam_find_tag(apparam, id);
+	if (tag == NULL)
+		return NULL;
+
+	string = g_strndup(tag->value.string, tag->len);
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "%s", string);
+
+	return string;
+}
+
+gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id,
+					const guint8 **val, gsize *len)
+{
+	struct apparam_tag *tag;
+
+	g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id);
+
+	tag = g_obex_apparam_find_tag(apparam, id);
+	if (tag == NULL)
+		return FALSE;
+
+	*len = tag->len;
+	*val = tag->value.data;
+
+	return TRUE;
+}
+
+void g_obex_apparam_free(GObexApparam *apparam)
+{
+	g_hash_table_unref(apparam->tags);
+	g_free(apparam);
+}
diff --git a/bluez/repo/gobex/gobex-apparam.h b/bluez/repo/gobex/gobex-apparam.h
new file mode 100644
index 0000000..6c08609
--- /dev/null
+++ b/bluez/repo/gobex/gobex-apparam.h
@@ -0,0 +1,60 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2012  Intel Corporation.
+ *
+ *  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 __GOBEX_APPARAM_H
+#define __GOBEX_APPARAM_H
+
+#include <glib.h>
+
+typedef struct _GObexApparam GObexApparam;
+
+GObexApparam *g_obex_apparam_decode(const void *data, gsize size);
+gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize size);
+
+GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id,
+						const void *value, gsize size);
+GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id,
+							guint8 value);
+GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id,
+							guint16 value);
+GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id,
+							guint32 value);
+GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id,
+							guint64 value);
+GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id,
+							const char *value);
+
+gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id,
+					const guint8 **val, gsize *len);
+gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id,
+							guint8 *value);
+gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id,
+							guint16 *value);
+gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id,
+							guint32 *value);
+gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id,
+							guint64 *value);
+char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id);
+
+void g_obex_apparam_free(GObexApparam *apparam);
+
+#endif /* __GOBEX_APPARAM_H */
diff --git a/bluez/repo/gobex/gobex-debug.h b/bluez/repo/gobex/gobex-debug.h
new file mode 100644
index 0000000..a98653d
--- /dev/null
+++ b/bluez/repo/gobex/gobex-debug.h
@@ -0,0 +1,78 @@
+/*
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GOBEX_DEBUG_H
+#define __GOBEX_DEBUG_H
+
+#include <glib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define G_OBEX_DEBUG_NONE	1
+#define G_OBEX_DEBUG_ERROR	(1 << 1)
+#define G_OBEX_DEBUG_COMMAND	(1 << 2)
+#define G_OBEX_DEBUG_TRANSFER	(1 << 3)
+#define G_OBEX_DEBUG_HEADER	(1 << 4)
+#define G_OBEX_DEBUG_PACKET	(1 << 5)
+#define G_OBEX_DEBUG_DATA	(1 << 6)
+#define G_OBEX_DEBUG_APPARAM	(1 << 7)
+
+extern guint gobex_debug;
+
+#define g_obex_debug(level, format, ...) \
+	if (gobex_debug & level) \
+		g_log("gobex", G_LOG_LEVEL_DEBUG, "%s:%s() " format, __FILE__, \
+						__func__, ## __VA_ARGS__)
+
+static inline void g_obex_dump(guint level, const char *prefix,
+					const void *buf, gsize len)
+{
+	const guint8 *data = buf;
+	int n = 0;
+
+	if (!(gobex_debug & level))
+		return;
+
+	while (len > 0) {
+		int i, size;
+
+		printf("%s %04x:", prefix, n);
+
+		size = len > 16 ? 16 : len;
+
+		for (i = 0; i < size; i++)
+			printf("%02x%s", data[i], (i + 1) % 8 ? " " : "  ");
+
+		for (; i < 16; i++)
+			printf("  %s", (i + 1) % 8 ? " " : "  ");
+
+		for (i = 0; i < size; i++)
+			printf("%1c", isprint(data[i]) ? data[i] : '.');
+
+		printf("\n");
+
+		data += size;
+		len -= size;
+		n += size;
+	}
+}
+
+#endif /* __GOBEX_DEBUG_H */
diff --git a/bluez/repo/gobex/gobex-defs.c b/bluez/repo/gobex/gobex-defs.c
new file mode 100644
index 0000000..1c7c39a
--- /dev/null
+++ b/bluez/repo/gobex/gobex-defs.c
@@ -0,0 +1,34 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "gobex-defs.h"
+
+GQuark g_obex_error_quark(void)
+{
+	return g_quark_from_static_string("g-obex-error-quark");
+}
diff --git a/bluez/repo/gobex/gobex-defs.h b/bluez/repo/gobex/gobex-defs.h
new file mode 100644
index 0000000..326e3cb
--- /dev/null
+++ b/bluez/repo/gobex/gobex-defs.h
@@ -0,0 +1,53 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GOBEX_DEFS_H
+#define __GOBEX_DEFS_H
+
+#include <glib.h>
+
+typedef enum {
+	G_OBEX_DATA_INHERIT,
+	G_OBEX_DATA_COPY,
+	G_OBEX_DATA_REF,
+} GObexDataPolicy;
+
+#define G_OBEX_ERROR_FIRST (0xff + 1)
+#define G_OBEX_PROTO_ERROR(code) ((code) < G_OBEX_ERROR_FIRST)
+
+typedef enum {
+	G_OBEX_ERROR_PARSE_ERROR = G_OBEX_ERROR_FIRST,
+	G_OBEX_ERROR_INVALID_ARGS,
+	G_OBEX_ERROR_DISCONNECTED,
+	G_OBEX_ERROR_TIMEOUT,
+	G_OBEX_ERROR_CANCELLED,
+	G_OBEX_ERROR_FAILED,
+} GObexError;
+
+typedef gssize (*GObexDataProducer) (void *buf, gsize len, gpointer user_data);
+typedef gboolean (*GObexDataConsumer) (const void *buf, gsize len,
+							gpointer user_data);
+
+#define G_OBEX_ERROR g_obex_error_quark()
+GQuark g_obex_error_quark(void);
+
+#endif /* __GOBEX_DEFS_H */
diff --git a/bluez/repo/gobex/gobex-header.c b/bluez/repo/gobex/gobex-header.c
new file mode 100644
index 0000000..c594999
--- /dev/null
+++ b/bluez/repo/gobex/gobex-header.c
@@ -0,0 +1,551 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "gobex-header.h"
+#include "gobex-debug.h"
+
+/* Header types */
+#define G_OBEX_HDR_ENC_UNICODE	(0 << 6)
+#define G_OBEX_HDR_ENC_BYTES	(1 << 6)
+#define G_OBEX_HDR_ENC_UINT8	(2 << 6)
+#define G_OBEX_HDR_ENC_UINT32	(3 << 6)
+
+#define G_OBEX_HDR_ENC(id)	((id) & 0xc0)
+
+struct _GObexHeader {
+	guint8 id;
+	gboolean extdata;
+	gsize vlen;			/* Length of value */
+	gsize hlen;			/* Length of full encoded header */
+	union {
+		char *string;		/* UTF-8 converted from UTF-16 */
+		guint8 *data;		/* Own buffer */
+		const guint8 *extdata;	/* Reference to external buffer */
+		guint8 u8;
+		guint32 u32;
+	} v;
+};
+
+static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
+	glong utf16_len;
+	int i;
+
+	if (*utf8 == '\0') {
+		*utf16 = NULL;
+		return 0;
+	}
+
+	*utf16 = g_utf8_to_utf16(utf8, -1, NULL, &utf16_len, NULL);
+	if (*utf16 == NULL)
+		return -1;
+
+	/* g_utf8_to_utf16 produces host-byteorder UTF-16,
+	 * but OBEX requires network byteorder (big endian) */
+	for (i = 0; i < utf16_len; i++)
+		(*utf16)[i] = g_htons((*utf16)[i]);
+
+	utf16_len = (utf16_len + 1) << 1;
+
+	return utf16_len;
+}
+
+static guint8 *put_bytes(guint8 *to, const void *from, gsize count)
+{
+	memcpy(to, from, count);
+	return (to + count);
+}
+
+static const guint8 *get_bytes(void *to, const guint8 *from, gsize count)
+{
+	memcpy(to, from, count);
+	return (from + count);
+}
+
+gssize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
+{
+	guint8 *ptr = buf;
+	guint16 u16;
+	guint32 u32;
+	gunichar2 *utf16;
+	glong utf16_len;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	if (buf_len < header->hlen)
+		return -1;
+
+	ptr = put_bytes(ptr, &header->id, sizeof(header->id));
+
+	switch (G_OBEX_HDR_ENC(header->id)) {
+	case G_OBEX_HDR_ENC_UNICODE:
+		utf16_len = utf8_to_utf16(&utf16, header->v.string);
+		if (utf16_len < 0 || (guint16) utf16_len > buf_len)
+			return -1;
+		g_assert_cmpuint(utf16_len + 3, ==, header->hlen);
+		u16 = g_htons(utf16_len + 3);
+		ptr = put_bytes(ptr, &u16, sizeof(u16));
+		put_bytes(ptr, utf16, utf16_len);
+		g_free(utf16);
+		break;
+	case G_OBEX_HDR_ENC_BYTES:
+		u16 = g_htons(header->hlen);
+		ptr = put_bytes(ptr, &u16, sizeof(u16));
+		if (header->extdata)
+			put_bytes(ptr, header->v.extdata, header->vlen);
+		else
+			put_bytes(ptr, header->v.data, header->vlen);
+		break;
+	case G_OBEX_HDR_ENC_UINT8:
+		*ptr = header->v.u8;
+		break;
+	case G_OBEX_HDR_ENC_UINT32:
+		u32 = g_htonl(header->v.u32);
+		put_bytes(ptr, &u32, sizeof(u32));
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	return header->hlen;
+}
+
+GObexHeader *g_obex_header_decode(const void *data, gsize len,
+				GObexDataPolicy data_policy, gsize *parsed,
+				GError **err)
+{
+	GObexHeader *header;
+	const guint8 *ptr = data;
+	guint16 hdr_len;
+	gsize str_len;
+	GError *conv_err = NULL;
+
+	if (len < 2) {
+		if (!err)
+			return NULL;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+						"Too short header in packet");
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return NULL;
+	}
+
+	header = g_new0(GObexHeader, 1);
+
+	ptr = get_bytes(&header->id, ptr, sizeof(header->id));
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	switch (G_OBEX_HDR_ENC(header->id)) {
+	case G_OBEX_HDR_ENC_UNICODE:
+		if (len < 3) {
+			g_set_error(err, G_OBEX_ERROR,
+				G_OBEX_ERROR_PARSE_ERROR,
+				"Not enough data for unicode header (0x%02x)",
+				header->id);
+			goto failed;
+		}
+		ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len));
+		hdr_len = g_ntohs(hdr_len);
+
+		if (hdr_len == 3) {
+			header->v.string = g_strdup("");
+			header->vlen = 0;
+			header->hlen = hdr_len;
+			*parsed = hdr_len;
+			break;
+		}
+
+		if (hdr_len > len || hdr_len < 5) {
+			g_set_error(err, G_OBEX_ERROR,
+				G_OBEX_ERROR_PARSE_ERROR,
+				"Invalid unicode header (0x%02x) length (%u)",
+				header->id, hdr_len);
+			goto failed;
+		}
+
+		header->v.string = g_convert((const char *) ptr, hdr_len - 5,
+						"UTF-8", "UTF-16BE",
+						NULL, &str_len, &conv_err);
+		if (header->v.string == NULL) {
+			g_set_error(err, G_OBEX_ERROR,
+					G_OBEX_ERROR_PARSE_ERROR,
+					"Unicode conversion failed: %s",
+					conv_err->message);
+			g_error_free(conv_err);
+			goto failed;
+		}
+
+		header->vlen = (gsize) str_len;
+		header->hlen = hdr_len;
+
+		*parsed = hdr_len;
+
+		break;
+	case G_OBEX_HDR_ENC_BYTES:
+		if (len < 3) {
+			g_set_error(err, G_OBEX_ERROR,
+					G_OBEX_ERROR_PARSE_ERROR,
+					"Too short byte array header");
+			goto failed;
+		}
+		ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len));
+		hdr_len = g_ntohs(hdr_len);
+		if (hdr_len > len) {
+			g_set_error(err, G_OBEX_ERROR,
+					G_OBEX_ERROR_PARSE_ERROR,
+					"Too long byte array header");
+			goto failed;
+		}
+
+		if (hdr_len < 3) {
+			g_set_error(err, G_OBEX_ERROR,
+					G_OBEX_ERROR_PARSE_ERROR,
+					"Too small byte array length");
+			goto failed;
+		}
+
+		header->vlen = hdr_len - 3;
+		header->hlen = hdr_len;
+
+		switch (data_policy) {
+		case G_OBEX_DATA_COPY:
+			header->v.data = g_memdup(ptr, header->vlen);
+			break;
+		case G_OBEX_DATA_REF:
+			header->extdata = TRUE;
+			header->v.extdata = ptr;
+			break;
+		case G_OBEX_DATA_INHERIT:
+		default:
+			g_set_error(err, G_OBEX_ERROR,
+					G_OBEX_ERROR_INVALID_ARGS,
+					"Invalid data policy");
+			goto failed;
+		}
+
+		*parsed = hdr_len;
+
+		break;
+	case G_OBEX_HDR_ENC_UINT8:
+		header->vlen = 1;
+		header->hlen = 2;
+		header->v.u8 = *ptr;
+		*parsed = 2;
+		break;
+	case G_OBEX_HDR_ENC_UINT32:
+		if (len < 5) {
+			g_set_error(err, G_OBEX_ERROR,
+					G_OBEX_ERROR_PARSE_ERROR,
+					"Too short uint32 header");
+			goto failed;
+		}
+		header->vlen = 4;
+		header->hlen = 5;
+		get_bytes(&header->v.u32, ptr, sizeof(header->v.u32));
+		header->v.u32 = g_ntohl(header->v.u32);
+		*parsed = 5;
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	return header;
+
+failed:
+	if (*err)
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+	g_obex_header_free(header);
+	return NULL;
+}
+
+void g_obex_header_free(GObexHeader *header)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	switch (G_OBEX_HDR_ENC(header->id)) {
+	case G_OBEX_HDR_ENC_UNICODE:
+		g_free(header->v.string);
+		break;
+	case G_OBEX_HDR_ENC_BYTES:
+		if (!header->extdata)
+			g_free(header->v.data);
+		break;
+	case G_OBEX_HDR_ENC_UINT8:
+	case G_OBEX_HDR_ENC_UINT32:
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	g_free(header);
+}
+
+gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UNICODE)
+		return FALSE;
+
+	*str = header->v.string;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", *str);
+
+	return TRUE;
+}
+
+gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val,
+								gsize *len)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_BYTES)
+		return FALSE;
+
+	*len = header->vlen;
+
+	if (header->extdata)
+		*val = header->v.extdata;
+	else
+		*val = header->v.data;
+
+	return TRUE;
+}
+
+GObexApparam *g_obex_header_get_apparam(GObexHeader *header)
+{
+	gboolean ret;
+	const guint8 *val;
+	gsize len;
+
+	ret = g_obex_header_get_bytes(header, &val, &len);
+	if (!ret)
+		return NULL;
+
+	return g_obex_apparam_decode(val, len);
+}
+
+gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT8)
+		return FALSE;
+
+	*val = header->v.u8;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val);
+
+	return TRUE;
+}
+
+gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
+						G_OBEX_HDR_ENC(header->id));
+
+	if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT32)
+		return FALSE;
+
+	*val = header->v.u32;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val);
+
+	return TRUE;
+}
+
+GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
+{
+	GObexHeader *header;
+	gsize len;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
+
+	if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE)
+		return NULL;
+
+	header = g_new0(GObexHeader, 1);
+
+	header->id = id;
+
+	len = g_utf8_strlen(str, -1);
+
+	header->vlen = len;
+	header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2);
+	header->v.string = g_strdup(str);
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string);
+
+	return header;
+}
+
+GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len)
+{
+	GObexHeader *header;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
+
+	if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_BYTES)
+		return NULL;
+
+	header = g_new0(GObexHeader, 1);
+
+	header->id = id;
+	header->vlen = len;
+	header->hlen = len + 3;
+	header->v.data = g_memdup(data, len);
+
+	return header;
+}
+
+GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam)
+{
+	guint8 buf[1024];
+	gssize len;
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	if (len < 0)
+		return NULL;
+
+	return g_obex_header_new_bytes(id, buf, len);
+}
+
+GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam)
+{
+	return g_obex_header_new_tag(G_OBEX_HDR_APPARAM, apparam);
+}
+
+GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val)
+{
+	GObexHeader *header;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
+
+	if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT8)
+		return NULL;
+
+	header = g_new0(GObexHeader, 1);
+
+	header->id = id;
+	header->vlen = 1;
+	header->hlen = 2;
+	header->v.u8 = val;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u8);
+
+	return header;
+}
+
+GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val)
+{
+	GObexHeader *header;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
+
+	if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT32)
+		return NULL;
+
+	header = g_new0(GObexHeader, 1);
+
+	header->id = id;
+	header->vlen = 4;
+	header->hlen = 5;
+	header->v.u32 = val;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u32);
+
+	return header;
+}
+
+guint8 g_obex_header_get_id(GObexHeader *header)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x id 0x%02x",
+				G_OBEX_HDR_ENC(header->id), header->id);
+
+	return header->id;
+}
+
+guint16 g_obex_header_get_length(GObexHeader *header)
+{
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x length %zu",
+				G_OBEX_HDR_ENC(header->id), header->hlen);
+
+	return header->hlen;
+}
+
+GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args,
+							gsize *total_len)
+{
+	unsigned int id = first_hdr_id;
+	GSList *l = NULL;
+
+	g_obex_debug(G_OBEX_DEBUG_HEADER, "");
+
+	*total_len = 0;
+
+	while (id != G_OBEX_HDR_INVALID) {
+		GObexHeader *hdr;
+		const char *str;
+		const void *bytes;
+		unsigned int val;
+		gsize len;
+
+		switch (G_OBEX_HDR_ENC(id)) {
+		case G_OBEX_HDR_ENC_UNICODE:
+			str = va_arg(args, const char *);
+			hdr = g_obex_header_new_unicode(id, str);
+			break;
+		case G_OBEX_HDR_ENC_BYTES:
+			bytes = va_arg(args, void *);
+			len = va_arg(args, gsize);
+			hdr = g_obex_header_new_bytes(id, bytes, len);
+			break;
+		case G_OBEX_HDR_ENC_UINT8:
+			val = va_arg(args, unsigned int);
+			hdr = g_obex_header_new_uint8(id, val);
+			break;
+		case G_OBEX_HDR_ENC_UINT32:
+			val = va_arg(args, unsigned int);
+			hdr = g_obex_header_new_uint32(id, val);
+			break;
+		default:
+			g_assert_not_reached();
+		}
+
+		l = g_slist_append(l, hdr);
+		*total_len += hdr->hlen;
+		id = va_arg(args, int);
+	}
+
+	return l;
+}
diff --git a/bluez/repo/gobex/gobex-header.h b/bluez/repo/gobex/gobex-header.h
new file mode 100644
index 0000000..6600b1b
--- /dev/null
+++ b/bluez/repo/gobex/gobex-header.h
@@ -0,0 +1,103 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GOBEX_HEADER_H
+#define __GOBEX_HEADER_H
+
+#include <glib.h>
+
+#include "gobex/gobex-defs.h"
+#include "gobex/gobex-apparam.h"
+
+/* Header ID's */
+#define G_OBEX_HDR_INVALID	0x00
+
+#define G_OBEX_HDR_COUNT		0xc0
+#define G_OBEX_HDR_NAME			0x01
+#define G_OBEX_HDR_TYPE			0x42
+#define G_OBEX_HDR_LENGTH		0xc3
+#define G_OBEX_HDR_TIME			0x44
+#define G_OBEX_HDR_DESCRIPTION		0x05
+#define G_OBEX_HDR_TARGET		0x46
+#define G_OBEX_HDR_HTTP			0x47
+#define G_OBEX_HDR_BODY			0x48
+#define G_OBEX_HDR_BODY_END		0x49
+#define G_OBEX_HDR_WHO			0x4a
+#define G_OBEX_HDR_CONNECTION		0xcb
+#define G_OBEX_HDR_APPARAM		0x4c
+#define G_OBEX_HDR_AUTHCHAL		0x4d
+#define G_OBEX_HDR_AUTHRESP		0x4e
+#define G_OBEX_HDR_CREATOR		0xcf
+#define G_OBEX_HDR_WANUUID		0x50
+#define G_OBEX_HDR_OBJECTCLASS		0x51
+#define G_OBEX_HDR_SESSIONPARAM		0x52
+#define G_OBEX_HDR_SESSIONSEQ		0x93
+#define G_OBEX_HDR_ACTION		0x94
+#define G_OBEX_HDR_DESTNAME		0x15
+#define G_OBEX_HDR_PERMISSIONS		0xd6
+#define G_OBEX_HDR_SRM			0x97
+#define G_OBEX_HDR_SRMP			0x98
+
+/* Action header values */
+#define G_OBEX_ACTION_COPY		0x00
+#define G_OBEX_ACTION_MOVE		0x01
+#define G_OBEX_ACTION_SETPERM		0x02
+
+/* SRM header values */
+#define G_OBEX_SRM_DISABLE		0x00
+#define G_OBEX_SRM_ENABLE		0x01
+#define G_OBEX_SRM_INDICATE		0x02
+
+/* SRMP header values */
+#define G_OBEX_SRMP_NEXT		0x00
+#define G_OBEX_SRMP_WAIT		0x01
+#define G_OBEX_SRMP_NEXT_WAIT		0x02
+
+typedef struct _GObexHeader GObexHeader;
+
+gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str);
+gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val,
+								gsize *len);
+gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val);
+gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val);
+GObexApparam *g_obex_header_get_apparam(GObexHeader *header);
+
+GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str);
+GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len);
+GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val);
+GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val);
+GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam);
+GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam);
+
+GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args,
+							gsize *total_len);
+
+guint8 g_obex_header_get_id(GObexHeader *header);
+guint16 g_obex_header_get_length(GObexHeader *header);
+
+gssize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len);
+GObexHeader *g_obex_header_decode(const void *data, gsize len,
+				GObexDataPolicy data_policy, gsize *parsed,
+				GError **err);
+void g_obex_header_free(GObexHeader *header);
+
+#endif /* __GOBEX_HEADER_H */
diff --git a/bluez/repo/gobex/gobex-packet.c b/bluez/repo/gobex/gobex-packet.c
new file mode 100644
index 0000000..db56ed0
--- /dev/null
+++ b/bluez/repo/gobex/gobex-packet.c
@@ -0,0 +1,462 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include "gobex-defs.h"
+#include "gobex-packet.h"
+#include "gobex-debug.h"
+
+#define FINAL_BIT 0x80
+
+struct _GObexPacket {
+	guint8 opcode;
+	gboolean final;
+
+	GObexDataPolicy data_policy;
+
+	union {
+		void *buf;		/* Non-header data */
+		const void *buf_ref;	/* Reference to non-header data */
+	} data;
+	gsize data_len;
+
+	gsize hlen;		/* Length of all encoded headers */
+	GSList *headers;
+
+	GObexDataProducer get_body;
+	gpointer get_body_data;
+};
+
+GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id)
+{
+	GSList *l;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	for (l = pkt->headers; l != NULL; l = g_slist_next(l)) {
+		GObexHeader *hdr = l->data;
+
+		if (g_obex_header_get_id(hdr) == id)
+			return hdr;
+	}
+
+	return NULL;
+}
+
+GObexHeader *g_obex_packet_get_body(GObexPacket *pkt)
+{
+	GObexHeader *body;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	body = g_obex_packet_get_header(pkt, G_OBEX_HDR_BODY);
+	if (body != NULL)
+		return body;
+
+	return g_obex_packet_get_header(pkt, G_OBEX_HDR_BODY_END);
+}
+
+guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	if (final)
+		*final = pkt->final;
+
+	return pkt->opcode;
+}
+
+gboolean g_obex_packet_prepend_header(GObexPacket *pkt, GObexHeader *header)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	pkt->headers = g_slist_prepend(pkt->headers, header);
+	pkt->hlen += g_obex_header_get_length(header);
+
+	return TRUE;
+}
+
+gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	pkt->headers = g_slist_append(pkt->headers, header);
+	pkt->hlen += g_obex_header_get_length(header);
+
+	return TRUE;
+}
+
+gboolean g_obex_packet_add_body(GObexPacket *pkt, GObexDataProducer func,
+							gpointer user_data)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	if (pkt->get_body != NULL)
+		return FALSE;
+
+	pkt->get_body = func;
+	pkt->get_body_data = user_data;
+
+	return TRUE;
+}
+
+gboolean g_obex_packet_add_unicode(GObexPacket *pkt, guint8 id,
+							const char *str)
+{
+	GObexHeader *hdr;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	hdr = g_obex_header_new_unicode(id, str);
+	if (hdr == NULL)
+		return FALSE;
+
+	return g_obex_packet_add_header(pkt, hdr);
+}
+
+gboolean g_obex_packet_add_bytes(GObexPacket *pkt, guint8 id,
+						const void *data, gsize len)
+{
+	GObexHeader *hdr;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	hdr = g_obex_header_new_bytes(id, data, len);
+	if (hdr == NULL)
+		return FALSE;
+
+	return g_obex_packet_add_header(pkt, hdr);
+}
+
+gboolean g_obex_packet_add_uint8(GObexPacket *pkt, guint8 id, guint8 val)
+{
+	GObexHeader *hdr;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	hdr = g_obex_header_new_uint8(id, val);
+	if (hdr == NULL)
+		return FALSE;
+
+	return g_obex_packet_add_header(pkt, hdr);
+}
+
+gboolean g_obex_packet_add_uint32(GObexPacket *pkt, guint8 id, guint32 val)
+{
+	GObexHeader *hdr;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	hdr = g_obex_header_new_uint32(id, val);
+	if (hdr == NULL)
+		return FALSE;
+
+	return g_obex_packet_add_header(pkt, hdr);
+}
+
+const void *g_obex_packet_get_data(GObexPacket *pkt, gsize *len)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	if (pkt->data_len == 0) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = pkt->data_len;
+
+	switch (pkt->data_policy) {
+	case G_OBEX_DATA_INHERIT:
+	case G_OBEX_DATA_COPY:
+		return pkt->data.buf;
+	case G_OBEX_DATA_REF:
+		return pkt->data.buf_ref;
+	}
+
+	g_assert_not_reached();
+}
+
+gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, gsize len,
+						GObexDataPolicy data_policy)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	if (pkt->data.buf || pkt->data.buf_ref)
+		return FALSE;
+
+	pkt->data_policy = data_policy;
+	pkt->data_len = len;
+
+	switch (data_policy) {
+	case G_OBEX_DATA_COPY:
+		pkt->data.buf = g_memdup(data, len);
+		break;
+	case G_OBEX_DATA_REF:
+		pkt->data.buf_ref = data;
+		break;
+	case G_OBEX_DATA_INHERIT:
+		pkt->data.buf = (void *) data;
+		break;
+	}
+
+	return TRUE;
+}
+
+GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
+					guint8 first_hdr_id, va_list args)
+{
+	GObexPacket *pkt;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", opcode);
+
+	pkt = g_new0(GObexPacket, 1);
+
+	pkt->opcode = opcode;
+	pkt->final = final;
+	pkt->headers = g_obex_header_create_list(first_hdr_id, args,
+								&pkt->hlen);
+	pkt->data_policy = G_OBEX_DATA_COPY;
+
+	return pkt;
+}
+
+GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final,
+						guint8 first_hdr_id, ...)
+{
+	GObexPacket *pkt;
+	va_list args;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", opcode);
+
+	va_start(args, first_hdr_id);
+	pkt = g_obex_packet_new_valist(opcode, final, first_hdr_id, args);
+	va_end(args);
+
+	return pkt;
+}
+
+void g_obex_packet_free(GObexPacket *pkt)
+{
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	switch (pkt->data_policy) {
+	case G_OBEX_DATA_INHERIT:
+	case G_OBEX_DATA_COPY:
+		g_free(pkt->data.buf);
+		break;
+	case G_OBEX_DATA_REF:
+		break;
+	}
+
+	g_slist_foreach(pkt->headers, (GFunc) g_obex_header_free, NULL);
+	g_slist_free(pkt->headers);
+	g_free(pkt);
+}
+
+static gboolean parse_headers(GObexPacket *pkt, const void *data, gsize len,
+						GObexDataPolicy data_policy,
+						GError **err)
+{
+	const guint8 *buf = data;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	while (len > 0) {
+		GObexHeader *header;
+		gsize parsed;
+
+		header = g_obex_header_decode(buf, len, data_policy, &parsed,
+									err);
+		if (header == NULL)
+			return FALSE;
+
+		pkt->headers = g_slist_append(pkt->headers, header);
+		pkt->hlen += parsed;
+
+		len -= parsed;
+		buf += parsed;
+	}
+
+	return TRUE;
+}
+
+static const guint8 *get_bytes(void *to, const guint8 *from, gsize count)
+{
+	memcpy(to, from, count);
+	return (from + count);
+}
+
+GObexPacket *g_obex_packet_decode(const void *data, gsize len,
+						gsize header_offset,
+						GObexDataPolicy data_policy,
+						GError **err)
+{
+	const guint8 *buf = data;
+	guint16 packet_len;
+	guint8 opcode;
+	GObexPacket *pkt;
+	gboolean final;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "");
+
+	if (data_policy == G_OBEX_DATA_INHERIT) {
+		if (!err)
+			return NULL;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS,
+							"Invalid data policy");
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return NULL;
+	}
+
+	if (len < 3 + header_offset) {
+		if (!err)
+			return NULL;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+					"Not enough data to decode packet");
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return NULL;
+	}
+
+	buf = get_bytes(&opcode, buf, sizeof(opcode));
+	buf = get_bytes(&packet_len, buf, sizeof(packet_len));
+
+	packet_len = g_ntohs(packet_len);
+	if (packet_len != len) {
+		if (!err)
+			return NULL;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+				"Incorrect packet length (%u != %zu)",
+				packet_len, len);
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return NULL;
+	}
+
+	final = (opcode & FINAL_BIT) ? TRUE : FALSE;
+	opcode &= ~FINAL_BIT;
+
+	pkt = g_obex_packet_new(opcode, final, G_OBEX_HDR_INVALID);
+
+	if (header_offset == 0)
+		goto headers;
+
+	g_obex_packet_set_data(pkt, buf, header_offset, data_policy);
+	buf += header_offset;
+
+headers:
+	if (!parse_headers(pkt, buf, len - (3 + header_offset),
+							data_policy, err))
+		goto failed;
+
+	return pkt;
+
+failed:
+	g_obex_packet_free(pkt);
+	return NULL;
+}
+
+static gssize get_body(GObexPacket *pkt, guint8 *buf, gsize len)
+{
+	guint16 u16;
+	gssize ret;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	if (len < 3)
+		return -ENOBUFS;
+
+	ret = pkt->get_body(buf + 3, len - 3, pkt->get_body_data);
+	if (ret < 0)
+		return ret;
+
+	if (ret > 0)
+		buf[0] = G_OBEX_HDR_BODY;
+	else
+		buf[0] = G_OBEX_HDR_BODY_END;
+
+	u16 = g_htons(ret + 3);
+	memcpy(&buf[1], &u16, sizeof(u16));
+
+	return ret;
+}
+
+gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len)
+{
+	gssize ret;
+	gsize count;
+	guint16 u16;
+	GSList *l;
+
+	g_obex_debug(G_OBEX_DEBUG_PACKET, "opcode 0x%02x", pkt->opcode);
+
+	if (3 + pkt->data_len + pkt->hlen > len)
+		return -ENOBUFS;
+
+	buf[0] = pkt->opcode;
+	if (pkt->final)
+		buf[0] |= FINAL_BIT;
+
+	if (pkt->data_len > 0) {
+		if (pkt->data_policy == G_OBEX_DATA_REF)
+			memcpy(&buf[3], pkt->data.buf_ref, pkt->data_len);
+		else
+			memcpy(&buf[3], pkt->data.buf, pkt->data_len);
+	}
+
+	count = 3 + pkt->data_len;
+
+	for (l = pkt->headers; l != NULL; l = g_slist_next(l)) {
+		GObexHeader *hdr = l->data;
+
+		if (count >= len)
+			return -ENOBUFS;
+
+		ret = g_obex_header_encode(hdr, buf + count, len - count);
+		if (ret < 0)
+			return ret;
+
+		count += ret;
+	}
+
+	if (pkt->get_body) {
+		ret = get_body(pkt, buf + count, len - count);
+		if (ret < 0)
+			return ret;
+		if (ret == 0) {
+			if (pkt->opcode == G_OBEX_RSP_CONTINUE)
+				buf[0] = G_OBEX_RSP_SUCCESS;
+			buf[0] |= FINAL_BIT;
+		}
+
+		count += ret + 3;
+	}
+
+	u16 = g_htons(count);
+	memcpy(&buf[1], &u16, sizeof(u16));
+
+	return count;
+}
diff --git a/bluez/repo/gobex/gobex-packet.h b/bluez/repo/gobex/gobex-packet.h
new file mode 100644
index 0000000..d1007ea
--- /dev/null
+++ b/bluez/repo/gobex/gobex-packet.h
@@ -0,0 +1,112 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GOBEX_PACKET_H
+#define __GOBEX_PACKET_H
+
+#include <stdarg.h>
+#include <glib.h>
+
+#include "gobex/gobex-defs.h"
+#include "gobex/gobex-header.h"
+
+/* Request opcodes */
+#define G_OBEX_OP_CONNECT			0x00
+#define G_OBEX_OP_DISCONNECT			0x01
+#define G_OBEX_OP_PUT				0x02
+#define G_OBEX_OP_GET				0x03
+#define G_OBEX_OP_SETPATH			0x05
+#define G_OBEX_OP_ACTION			0x06
+#define G_OBEX_OP_SESSION			0x07
+#define G_OBEX_OP_ABORT				0x7f
+
+/* Response codes */
+#define G_OBEX_RSP_CONTINUE			0x10
+#define G_OBEX_RSP_SUCCESS			0x20
+#define G_OBEX_RSP_CREATED			0x21
+#define G_OBEX_RSP_ACCEPTED			0x22
+#define G_OBEX_RSP_NON_AUTHORITATIVE		0x23
+#define G_OBEX_RSP_NO_CONTENT			0x24
+#define G_OBEX_RSP_RESET_CONTENT		0x25
+#define G_OBEX_RSP_PARTIAL_CONTENT		0x26
+#define G_OBEX_RSP_MULTIPLE_CHOICES		0x30
+#define G_OBEX_RSP_MOVED_PERMANENTLY		0x31
+#define G_OBEX_RSP_MOVED_TEMPORARILY		0x32
+#define G_OBEX_RSP_SEE_OTHER			0x33
+#define G_OBEX_RSP_NOT_MODIFIED			0x34
+#define G_OBEX_RSP_USE_PROXY			0x35
+#define G_OBEX_RSP_BAD_REQUEST			0x40
+#define G_OBEX_RSP_UNAUTHORIZED			0x41
+#define G_OBEX_RSP_PAYMENT_REQUIRED		0x42
+#define G_OBEX_RSP_FORBIDDEN			0x43
+#define G_OBEX_RSP_NOT_FOUND			0x44
+#define G_OBEX_RSP_METHOD_NOT_ALLOWED		0x45
+#define G_OBEX_RSP_NOT_ACCEPTABLE		0x46
+#define G_OBEX_RSP_PROXY_AUTH_REQUIRED		0x47
+#define G_OBEX_RSP_REQUEST_TIME_OUT		0x48
+#define G_OBEX_RSP_CONFLICT			0x49
+#define G_OBEX_RSP_GONE				0x4a
+#define G_OBEX_RSP_LENGTH_REQUIRED		0x4b
+#define G_OBEX_RSP_PRECONDITION_FAILED		0x4c
+#define G_OBEX_RSP_REQ_ENTITY_TOO_LARGE		0x4d
+#define G_OBEX_RSP_REQ_URL_TOO_LARGE		0x4e
+#define G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE	0x4f
+#define G_OBEX_RSP_INTERNAL_SERVER_ERROR	0x50
+#define G_OBEX_RSP_NOT_IMPLEMENTED		0x51
+#define G_OBEX_RSP_BAD_GATEWAY			0x52
+#define G_OBEX_RSP_SERVICE_UNAVAILABLE		0x53
+#define G_OBEX_RSP_GATEWAY_TIMEOUT		0x54
+#define G_OBEX_RSP_VERSION_NOT_SUPPORTED	0x55
+#define G_OBEX_RSP_DATABASE_FULL		0x60
+#define G_OBEX_RSP_DATABASE_LOCKED		0x61
+
+typedef struct _GObexPacket GObexPacket;
+
+GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id);
+GObexHeader *g_obex_packet_get_body(GObexPacket *pkt);
+guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final);
+gboolean g_obex_packet_prepend_header(GObexPacket *pkt, GObexHeader *header);
+gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header);
+gboolean g_obex_packet_add_body(GObexPacket *pkt, GObexDataProducer func,
+							gpointer user_data);
+gboolean g_obex_packet_add_unicode(GObexPacket *pkt, guint8 id,
+							const char *str);
+gboolean g_obex_packet_add_bytes(GObexPacket *pkt, guint8 id,
+						const void *data, gsize len);
+gboolean g_obex_packet_add_uint8(GObexPacket *pkt, guint8 id, guint8 val);
+gboolean g_obex_packet_add_uint32(GObexPacket *pkt, guint8 id, guint32 val);
+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, ...);
+GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
+					guint8 first_hdr_id, va_list args);
+void g_obex_packet_free(GObexPacket *pkt);
+
+GObexPacket *g_obex_packet_decode(const void *data, gsize len,
+						gsize header_offset,
+						GObexDataPolicy data_policy,
+						GError **err);
+gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len);
+
+#endif /* __GOBEX_PACKET_H */
diff --git a/bluez/repo/gobex/gobex-transfer.c b/bluez/repo/gobex/gobex-transfer.c
new file mode 100644
index 0000000..a5115bf
--- /dev/null
+++ b/bluez/repo/gobex/gobex-transfer.c
@@ -0,0 +1,670 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-debug.h"
+
+#define FIRST_PACKET_TIMEOUT 60
+
+static GSList *transfers = NULL;
+
+static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data);
+
+struct transfer {
+	guint id;
+	guint8 opcode;
+
+	GObex *obex;
+
+	guint req_id;
+
+	guint put_id;
+	guint get_id;
+	guint abort_id;
+
+	GObexDataProducer data_producer;
+	GObexDataConsumer data_consumer;
+	GObexFunc complete_func;
+
+	gpointer user_data;
+};
+
+static void transfer_free(struct transfer *transfer)
+{
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	transfers = g_slist_remove(transfers, transfer);
+
+	if (transfer->req_id > 0)
+		g_obex_cancel_req(transfer->obex, transfer->req_id, TRUE);
+
+	if (transfer->put_id > 0)
+		g_obex_remove_request_function(transfer->obex,
+							transfer->put_id);
+
+	if (transfer->get_id > 0)
+		g_obex_remove_request_function(transfer->obex,
+							transfer->get_id);
+
+	if (transfer->abort_id > 0)
+		g_obex_remove_request_function(transfer->obex,
+							transfer->abort_id);
+
+	g_obex_unref(transfer->obex);
+	g_free(transfer);
+}
+
+static struct transfer *find_transfer(guint id)
+{
+	GSList *l;
+
+	for (l = transfers; l != NULL; l = g_slist_next(l)) {
+		struct transfer *t = l->data;
+		if (t->id == id)
+			return t;
+	}
+
+	return NULL;
+}
+
+static void transfer_complete(struct transfer *transfer, GError *err)
+{
+	guint id = transfer->id;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", id);
+
+	transfer->complete_func(transfer->obex, err, transfer->user_data);
+	/* Check if the complete_func removed the transfer */
+	if (find_transfer(id) == NULL)
+		return;
+
+	transfer_free(transfer);
+}
+
+static void transfer_abort_response(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	transfer->req_id = 0;
+
+	/* Intentionally override error */
+	err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
+						"Operation was aborted");
+	g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
+	transfer_complete(transfer, err);
+	g_error_free(err);
+}
+
+
+static gssize put_get_data(void *buf, gsize len, gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+	GObexPacket *req;
+	GError *err = NULL;
+	gssize ret;
+
+	ret = transfer->data_producer(buf, len, transfer->user_data);
+	if (ret == 0 || ret == -EAGAIN)
+		return ret;
+
+	if (ret > 0) {
+		/* Check if SRM is active */
+		if (!g_obex_srm_active(transfer->obex))
+			return ret;
+
+		/* Generate next packet */
+		req = g_obex_packet_new(transfer->opcode, FALSE,
+							G_OBEX_HDR_INVALID);
+		g_obex_packet_add_body(req, put_get_data, transfer);
+		transfer->req_id = g_obex_send_req(transfer->obex, req, -1,
+						transfer_response, transfer,
+						&err);
+		goto done;
+	}
+
+	transfer->req_id = g_obex_abort(transfer->obex, transfer_abort_response,
+								transfer, &err);
+done:
+	if (err != NULL) {
+		transfer_complete(transfer, err);
+		g_error_free(err);
+	}
+
+	return ret;
+}
+
+static gboolean handle_get_body(struct transfer *transfer, GObexPacket *rsp,
+								GError **err)
+{
+	GObexHeader *body = g_obex_packet_get_body(rsp);
+	gboolean ret;
+	const guint8 *buf;
+	gsize len;
+
+	if (body == NULL)
+		return TRUE;
+
+	g_obex_header_get_bytes(body, &buf, &len);
+	if (len == 0)
+		return TRUE;
+
+	ret = transfer->data_consumer(buf, len, transfer->user_data);
+	if (ret == FALSE)
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
+				"Data consumer callback failed");
+
+	return ret;
+}
+
+static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+	GObexPacket *req;
+	gboolean rspcode, final;
+	guint id;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	id = transfer->req_id;
+	transfer->req_id = 0;
+
+	if (err != NULL) {
+		transfer_complete(transfer, err);
+		return;
+	}
+
+	rspcode = g_obex_packet_get_operation(rsp, &final);
+	if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) {
+		err = g_error_new(G_OBEX_ERROR, rspcode, "%s",
+						g_obex_strerror(rspcode));
+		goto failed;
+	}
+
+	if (transfer->opcode == G_OBEX_OP_GET) {
+		handle_get_body(transfer, rsp, &err);
+		if (err != NULL)
+			goto failed;
+	}
+
+	if (rspcode == G_OBEX_RSP_SUCCESS) {
+		transfer_complete(transfer, NULL);
+		return;
+	}
+
+	if (transfer->opcode == G_OBEX_OP_PUT) {
+		req = g_obex_packet_new(transfer->opcode, FALSE,
+							G_OBEX_HDR_INVALID);
+		g_obex_packet_add_body(req, put_get_data, transfer);
+	} else if (!g_obex_srm_active(transfer->obex)) {
+		req = g_obex_packet_new(transfer->opcode, TRUE,
+							G_OBEX_HDR_INVALID);
+	} else {
+		/* Keep id since request still outstanting */
+		transfer->req_id = id;
+		return;
+	}
+
+	transfer->req_id = g_obex_send_req(obex, req, -1, transfer_response,
+							transfer, &err);
+failed:
+	if (err != NULL) {
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
+		transfer_complete(transfer, err);
+		g_error_free(err);
+	}
+}
+
+static struct transfer *transfer_new(GObex *obex, guint8 opcode,
+				GObexFunc complete_func, gpointer user_data)
+{
+	static guint next_id = 1;
+	struct transfer *transfer;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p opcode %u", obex, opcode);
+
+	transfer = g_new0(struct transfer, 1);
+
+	transfer->id = next_id++;
+	transfer->opcode = opcode;
+	transfer->obex = g_obex_ref(obex);
+	transfer->complete_func = complete_func;
+	transfer->user_data = user_data;
+
+	transfers = g_slist_append(transfers, transfer);
+
+	return transfer;
+}
+
+guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
+			GObexDataProducer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err)
+{
+	struct transfer *transfer;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_PUT)
+		return 0;
+
+	transfer = transfer_new(obex, G_OBEX_OP_PUT, complete_func, user_data);
+	transfer->data_producer = data_func;
+
+	g_obex_packet_add_body(req, put_get_data, transfer);
+
+	transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT,
+					transfer_response, transfer, err);
+	if (transfer->req_id == 0) {
+		transfer_free(transfer);
+		return 0;
+	}
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	return transfer->id;
+}
+
+guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
+			GObexFunc complete_func, gpointer user_data,
+			GError **err, guint8 first_hdr_id, ...)
+{
+	GObexPacket *req;
+	va_list args;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	va_start(args, first_hdr_id);
+	req = g_obex_packet_new_valist(G_OBEX_OP_PUT, FALSE,
+							first_hdr_id, args);
+	va_end(args);
+
+	return g_obex_put_req_pkt(obex, req, data_func, complete_func,
+							user_data, err);
+}
+
+static void transfer_abort_req(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+	GObexPacket *rsp;
+	GError *err;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
+						"Request was aborted");
+	rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
+	g_obex_send(obex, rsp, NULL);
+
+	transfer_complete(transfer, err);
+	g_error_free(err);
+}
+
+static guint8 put_get_bytes(struct transfer *transfer, GObexPacket *req)
+{
+	GObexHeader *body;
+	gboolean final;
+	guint8 rsp;
+	const guint8 *buf;
+	gsize len;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	g_obex_packet_get_operation(req, &final);
+	if (final)
+		rsp = G_OBEX_RSP_SUCCESS;
+	else
+		rsp = G_OBEX_RSP_CONTINUE;
+
+	body = g_obex_packet_get_body(req);
+	if (body == NULL)
+		return rsp;
+
+	g_obex_header_get_bytes(body, &buf, &len);
+	if (len == 0)
+		return rsp;
+
+	if (transfer->data_consumer(buf, len, transfer->user_data) == FALSE)
+		rsp = G_OBEX_RSP_FORBIDDEN;
+
+	return rsp;
+}
+
+static void transfer_put_req_first(struct transfer *transfer, GObexPacket *req,
+					guint8 first_hdr_id, va_list args)
+{
+	GError *err = NULL;
+	GObexPacket *rsp;
+	guint8 rspcode;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	rspcode = put_get_bytes(transfer, req);
+
+	rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_id, args);
+
+	if (!g_obex_send(transfer->obex, rsp, &err)) {
+		transfer_complete(transfer, err);
+		g_error_free(err);
+		return;
+	}
+
+	if (rspcode != G_OBEX_RSP_CONTINUE)
+		transfer_complete(transfer, NULL);
+}
+
+static void transfer_put_req(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+	GError *err = NULL;
+	GObexPacket *rsp;
+	guint8 rspcode;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	rspcode = put_get_bytes(transfer, req);
+
+	/* Don't send continue while SRM is active */
+	if (g_obex_srm_active(transfer->obex) &&
+				rspcode == G_OBEX_RSP_CONTINUE)
+		goto done;
+
+	rsp = g_obex_packet_new(rspcode, TRUE, G_OBEX_HDR_INVALID);
+
+	if (!g_obex_send(obex, rsp, &err)) {
+		transfer_complete(transfer, err);
+		g_error_free(err);
+		return;
+	}
+
+done:
+	if (rspcode != G_OBEX_RSP_CONTINUE)
+		transfer_complete(transfer, NULL);
+}
+
+guint g_obex_put_rsp(GObex *obex, GObexPacket *req,
+			GObexDataConsumer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err,
+			guint8 first_hdr_id, ...)
+{
+	struct transfer *transfer;
+	va_list args;
+	guint id;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	transfer = transfer_new(obex, G_OBEX_OP_PUT, complete_func, user_data);
+	transfer->data_consumer = data_func;
+
+	va_start(args, first_hdr_id);
+	transfer_put_req_first(transfer, req, first_hdr_id, args);
+	va_end(args);
+	if (!g_slist_find(transfers, transfer))
+		return 0;
+
+	id = g_obex_add_request_function(obex, G_OBEX_OP_PUT, transfer_put_req,
+								transfer);
+	transfer->put_id = id;
+
+	id = g_obex_add_request_function(obex, G_OBEX_OP_ABORT,
+						transfer_abort_req, transfer);
+	transfer->abort_id = id;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	return transfer->id;
+}
+
+guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
+			GObexDataConsumer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err)
+{
+	struct transfer *transfer;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_GET)
+		return 0;
+
+	transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data);
+	transfer->data_consumer = data_func;
+	transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT,
+					transfer_response, transfer, err);
+	if (transfer->req_id == 0) {
+		transfer_free(transfer);
+		return 0;
+	}
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	return transfer->id;
+}
+
+guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
+			GObexFunc complete_func, gpointer user_data,
+			GError **err, guint8 first_hdr_id, ...)
+{
+	struct transfer *transfer;
+	GObexPacket *req;
+	va_list args;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data);
+	transfer->data_consumer = data_func;
+
+	va_start(args, first_hdr_id);
+	req = g_obex_packet_new_valist(G_OBEX_OP_GET, TRUE,
+							first_hdr_id, args);
+	va_end(args);
+
+	transfer->req_id = g_obex_send_req(obex, req, FIRST_PACKET_TIMEOUT,
+					transfer_response, transfer, err);
+	if (transfer->req_id == 0) {
+		transfer_free(transfer);
+		return 0;
+	}
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	return transfer->id;
+}
+
+static gssize get_get_data(void *buf, gsize len, gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+	GObexPacket *req, *rsp;
+	GError *err = NULL;
+	gssize ret;
+	guint8 op;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	ret = transfer->data_producer(buf, len, transfer->user_data);
+	if (ret > 0) {
+		if (!g_obex_srm_active(transfer->obex))
+			return ret;
+
+		/* Generate next response */
+		rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE,
+							G_OBEX_HDR_INVALID);
+		g_obex_packet_add_body(rsp, get_get_data, transfer);
+
+		if (!g_obex_send(transfer->obex, rsp, &err)) {
+			transfer_complete(transfer, err);
+			g_error_free(err);
+		}
+
+		return ret;
+	}
+
+	if (ret == -EAGAIN)
+		return ret;
+
+	if (ret == 0) {
+		transfer_complete(transfer, NULL);
+		return ret;
+	}
+
+	op = g_obex_errno_to_rsp(ret);
+
+	req = g_obex_packet_new(op, TRUE, G_OBEX_HDR_INVALID);
+	g_obex_send(transfer->obex, req, NULL);
+
+	err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
+				"Data producer function failed");
+	g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
+	transfer_complete(transfer, err);
+	g_error_free(err);
+
+	return ret;
+}
+
+static gboolean transfer_get_req_first(struct transfer *transfer,
+							GObexPacket *rsp)
+{
+	GError *err = NULL;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	g_obex_packet_add_body(rsp, get_get_data, transfer);
+
+	if (!g_obex_send(transfer->obex, rsp, &err)) {
+		transfer_complete(transfer, err);
+		g_error_free(err);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void transfer_get_req(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct transfer *transfer = user_data;
+	GError *err = NULL;
+	GObexPacket *rsp;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
+	g_obex_packet_add_body(rsp, get_get_data, transfer);
+
+	if (!g_obex_send(obex, rsp, &err)) {
+		transfer_complete(transfer, err);
+		g_error_free(err);
+	}
+}
+
+guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp,
+			GObexDataProducer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err)
+{
+	struct transfer *transfer;
+	guint id;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	transfer = transfer_new(obex, G_OBEX_OP_GET, complete_func, user_data);
+	transfer->data_producer = data_func;
+
+	if (!transfer_get_req_first(transfer, rsp))
+		return 0;
+
+	if (!g_slist_find(transfers, transfer))
+		return 0;
+
+	id = g_obex_add_request_function(obex, G_OBEX_OP_GET, transfer_get_req,
+								transfer);
+	transfer->get_id = id;
+
+	id = g_obex_add_request_function(obex, G_OBEX_OP_ABORT,
+						transfer_abort_req, transfer);
+	transfer->abort_id = id;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
+
+	return transfer->id;
+}
+
+guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
+			GObexFunc complete_func, gpointer user_data,
+			GError **err, guint8 first_hdr_id, ...)
+{
+	GObexPacket *rsp;
+	va_list args;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "obex %p", obex);
+
+	va_start(args, first_hdr_id);
+	rsp = g_obex_packet_new_valist(G_OBEX_RSP_CONTINUE, TRUE,
+							first_hdr_id, args);
+	va_end(args);
+
+	return g_obex_get_rsp_pkt(obex, rsp, data_func, complete_func,
+							user_data, err);
+}
+
+gboolean g_obex_cancel_transfer(guint id, GObexFunc complete_func,
+			gpointer user_data)
+{
+	struct transfer *transfer = NULL;
+	gboolean ret = TRUE;
+
+	g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", id);
+
+	transfer = find_transfer(id);
+
+	if (transfer == NULL)
+		return FALSE;
+
+	if (complete_func == NULL)
+		goto done;
+
+	transfer->complete_func = complete_func;
+	transfer->user_data = user_data;
+
+	if (!transfer->req_id) {
+		transfer->req_id = g_obex_abort(transfer->obex,
+						transfer_abort_response,
+						transfer, NULL);
+		if (transfer->req_id)
+			return TRUE;
+	}
+
+	ret = g_obex_cancel_req(transfer->obex, transfer->req_id, FALSE);
+	if (ret)
+		return TRUE;
+
+done:
+	transfer_free(transfer);
+	return ret;
+}
diff --git a/bluez/repo/gobex/gobex.c b/bluez/repo/gobex/gobex.c
new file mode 100644
index 0000000..42175fc
--- /dev/null
+++ b/bluez/repo/gobex/gobex.c
@@ -0,0 +1,1712 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gobex.h"
+#include "gobex-debug.h"
+
+#define G_OBEX_DEFAULT_MTU	4096
+#define G_OBEX_MINIMUM_MTU	255
+#define G_OBEX_MAXIMUM_MTU	65535
+
+#define G_OBEX_DEFAULT_TIMEOUT	10
+#define G_OBEX_ABORT_TIMEOUT	5
+
+#define G_OBEX_OP_NONE		0xff
+
+#define FINAL_BIT		0x80
+
+#define CONNID_INVALID		0xffffffff
+
+/* Challenge request */
+#define NONCE_TAG		0x00
+#define NONCE_LEN		16
+
+/* Challenge response */
+#define DIGEST_TAG		0x00
+
+guint gobex_debug = 0;
+
+struct srm_config {
+	guint8 op;
+	gboolean enabled;
+	guint8 srm;
+	guint8 srmp;
+	gboolean outgoing;
+};
+
+struct _GObex {
+	int ref_count;
+	GIOChannel *io;
+	guint io_source;
+
+	gboolean (*read) (GObex *obex, GError **err);
+	gboolean (*write) (GObex *obex, GError **err);
+
+	guint8 *rx_buf;
+	size_t rx_data;
+	guint16 rx_pkt_len;
+	guint8 rx_last_op;
+
+	guint8 *tx_buf;
+	size_t tx_data;
+	size_t tx_sent;
+
+	gboolean suspended;
+	gboolean use_srm;
+
+	struct srm_config *srm;
+
+	guint write_source;
+
+	gssize io_rx_mtu;
+	gssize io_tx_mtu;
+
+	guint16 rx_mtu;
+	guint16 tx_mtu;
+
+	guint32 conn_id;
+	GObexApparam *authchal;
+
+	GQueue *tx_queue;
+
+	GSList *req_handlers;
+
+	GObexFunc disconn_func;
+	gpointer disconn_func_data;
+
+	struct pending_pkt *pending_req;
+};
+
+struct pending_pkt {
+	guint id;
+	GObex *obex;
+	GObexPacket *pkt;
+	guint timeout;
+	guint timeout_id;
+	GObexResponseFunc rsp_func;
+	gpointer rsp_data;
+	gboolean cancelled;
+	gboolean suspended;
+	gboolean authenticating;
+};
+
+struct req_handler {
+	guint id;
+	guint8 opcode;
+	GObexRequestFunc func;
+	gpointer user_data;
+};
+
+struct connect_data {
+	guint8 version;
+	guint8 flags;
+	guint16 mtu;
+} __attribute__ ((packed));
+
+struct setpath_data {
+	guint8 flags;
+	guint8 constants;
+} __attribute__ ((packed));
+
+static struct error_code {
+	guint8 code;
+	const char *name;
+} obex_errors[] = {
+	{ G_OBEX_RSP_CONTINUE,			"Continue" },
+	{ G_OBEX_RSP_SUCCESS,			"Success" },
+	{ G_OBEX_RSP_CREATED,			"Created" },
+	{ G_OBEX_RSP_ACCEPTED,			"Accepted" },
+	{ G_OBEX_RSP_NON_AUTHORITATIVE,		"Non Authoritative" },
+	{ G_OBEX_RSP_NO_CONTENT,		"No Content" },
+	{ G_OBEX_RSP_RESET_CONTENT,		"Reset Content" },
+	{ G_OBEX_RSP_PARTIAL_CONTENT,		"Partial Content" },
+	{ G_OBEX_RSP_MULTIPLE_CHOICES,		"Multiple Choices" },
+	{ G_OBEX_RSP_MOVED_PERMANENTLY,		"Moved Permanently" },
+	{ G_OBEX_RSP_MOVED_TEMPORARILY,		"Moved Temporarily" },
+	{ G_OBEX_RSP_SEE_OTHER,			"See Other" },
+	{ G_OBEX_RSP_NOT_MODIFIED,		"Not Modified" },
+	{ G_OBEX_RSP_USE_PROXY,			"Use Proxy" },
+	{ G_OBEX_RSP_BAD_REQUEST,		"Bad Request" },
+	{ G_OBEX_RSP_UNAUTHORIZED,		"Unauthorized" },
+	{ G_OBEX_RSP_PAYMENT_REQUIRED,		"Payment Required" },
+	{ G_OBEX_RSP_FORBIDDEN,			"Forbidden" },
+	{ G_OBEX_RSP_NOT_FOUND,			"Not Found" },
+	{ G_OBEX_RSP_METHOD_NOT_ALLOWED,	"Method Not Allowed" },
+	{ G_OBEX_RSP_NOT_ACCEPTABLE,		"Not Acceptable" },
+	{ G_OBEX_RSP_PROXY_AUTH_REQUIRED,	"Proxy Authentication Required" },
+	{ G_OBEX_RSP_REQUEST_TIME_OUT,		"Request Time Out" },
+	{ G_OBEX_RSP_CONFLICT,			"Conflict" },
+	{ G_OBEX_RSP_GONE,			"Gone" },
+	{ G_OBEX_RSP_LENGTH_REQUIRED,		"Length Required" },
+	{ G_OBEX_RSP_PRECONDITION_FAILED,	"Precondition Failed" },
+	{ G_OBEX_RSP_REQ_ENTITY_TOO_LARGE,	"Request Entity Too Large" },
+	{ G_OBEX_RSP_REQ_URL_TOO_LARGE,		"Request URL Too Large" },
+	{ G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE,	"Unsupported Media Type" },
+	{ G_OBEX_RSP_INTERNAL_SERVER_ERROR,	"Internal Server Error" },
+	{ G_OBEX_RSP_NOT_IMPLEMENTED,		"Not Implemented" },
+	{ G_OBEX_RSP_BAD_GATEWAY,		"Bad Gateway" },
+	{ G_OBEX_RSP_SERVICE_UNAVAILABLE,	"Service Unavailable" },
+	{ G_OBEX_RSP_GATEWAY_TIMEOUT,		"Gateway Timeout" },
+	{ G_OBEX_RSP_VERSION_NOT_SUPPORTED,	"Version Not Supported" },
+	{ G_OBEX_RSP_DATABASE_FULL,		"Database Full" },
+	{ G_OBEX_RSP_DATABASE_LOCKED,		"Database Locked" },
+	{ 0x00,					NULL }
+};
+
+const char *g_obex_strerror(guint8 err_code)
+{
+	struct error_code *error;
+
+	for (error = obex_errors; error->name != NULL; error++) {
+		if (error->code == err_code)
+			return error->name;
+	}
+
+	return "<unknown>";
+}
+
+static ssize_t req_header_offset(guint8 opcode)
+{
+	switch (opcode) {
+	case G_OBEX_OP_CONNECT:
+		return sizeof(struct connect_data);
+	case G_OBEX_OP_SETPATH:
+		return sizeof(struct setpath_data);
+	case G_OBEX_OP_DISCONNECT:
+	case G_OBEX_OP_PUT:
+	case G_OBEX_OP_GET:
+	case G_OBEX_OP_SESSION:
+	case G_OBEX_OP_ABORT:
+	case G_OBEX_OP_ACTION:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+static ssize_t rsp_header_offset(guint8 opcode)
+{
+	switch (opcode) {
+	case G_OBEX_OP_CONNECT:
+		return sizeof(struct connect_data);
+	case G_OBEX_OP_SETPATH:
+	case G_OBEX_OP_DISCONNECT:
+	case G_OBEX_OP_PUT:
+	case G_OBEX_OP_GET:
+	case G_OBEX_OP_SESSION:
+	case G_OBEX_OP_ABORT:
+	case G_OBEX_OP_ACTION:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+static void pending_pkt_free(struct pending_pkt *p)
+{
+	if (p->obex != NULL)
+		g_obex_unref(p->obex);
+
+	if (p->timeout_id > 0)
+		g_source_remove(p->timeout_id);
+
+	g_obex_packet_free(p->pkt);
+
+	g_free(p);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+	GObex *obex = user_data;
+	struct pending_pkt *p = obex->pending_req;
+	GError *err;
+
+	g_assert(p != NULL);
+
+	p->timeout_id = 0;
+	obex->pending_req = NULL;
+
+	err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT,
+					"Timed out waiting for response");
+
+	g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
+
+	if (p->rsp_func)
+		p->rsp_func(obex, err, NULL, p->rsp_data);
+
+	g_error_free(err);
+	pending_pkt_free(p);
+
+	return FALSE;
+}
+
+static gboolean write_stream(GObex *obex, GError **err)
+{
+	GIOStatus status;
+	gsize bytes_written;
+	char *buf;
+
+	buf = (char *) &obex->tx_buf[obex->tx_sent];
+	status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
+							&bytes_written, err);
+	if (status != G_IO_STATUS_NORMAL)
+		return FALSE;
+
+	g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written);
+
+	obex->tx_sent += bytes_written;
+	obex->tx_data -= bytes_written;
+
+	return TRUE;
+}
+
+static gboolean write_packet(GObex *obex, GError **err)
+{
+	GIOStatus status;
+	gsize bytes_written;
+	char *buf;
+
+	buf = (char *) &obex->tx_buf[obex->tx_sent];
+	status = g_io_channel_write_chars(obex->io, buf, obex->tx_data,
+							&bytes_written, err);
+	if (status != G_IO_STATUS_NORMAL)
+		return FALSE;
+
+	if (bytes_written != obex->tx_data)
+		return FALSE;
+
+	g_obex_dump(G_OBEX_DEBUG_DATA, "<", buf, bytes_written);
+
+	obex->tx_sent += bytes_written;
+	obex->tx_data -= bytes_written;
+
+	return TRUE;
+}
+
+static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing)
+{
+	struct srm_config *config = obex->srm;
+
+	if (config == NULL)
+		return;
+
+	/* Dont't reset if direction doesn't match */
+	if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing)
+		return;
+
+	config->srmp = srmp;
+	config->outgoing = outgoing;
+}
+
+static void set_srm(GObex *obex, guint8 op, guint8 srm)
+{
+	struct srm_config *config = obex->srm;
+	gboolean enable;
+
+	if (config == NULL) {
+		if (srm == G_OBEX_SRM_DISABLE)
+			return;
+
+		config = g_new0(struct srm_config, 1);
+		config->op = op;
+		config->srm = srm;
+		obex->srm = config;
+		return;
+	}
+
+	/* Indicate response, treat it as request */
+	if (config->srm == G_OBEX_SRM_INDICATE) {
+		if (srm != G_OBEX_SRM_ENABLE)
+			goto done;
+		config->srm = srm;
+		return;
+	}
+
+	enable = (srm == G_OBEX_SRM_ENABLE);
+	if (config->enabled == enable)
+		goto done;
+
+	config->enabled = enable;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ?
+						"Enabled" : "Disabled");
+
+done:
+	if (config->enabled)
+		return;
+
+	g_free(obex->srm);
+	obex->srm = NULL;
+}
+
+static gboolean g_obex_srm_enabled(GObex *obex)
+{
+	if (!obex->use_srm)
+		return FALSE;
+
+	if (obex->srm == NULL)
+		return FALSE;
+
+	return obex->srm->enabled;
+}
+
+static void check_srm_final(GObex *obex, guint8 op)
+{
+	if (!g_obex_srm_enabled(obex))
+		return;
+
+	switch (obex->srm->op) {
+	case G_OBEX_OP_CONNECT:
+		return;
+	default:
+		if (op <= G_OBEX_RSP_CONTINUE)
+			return;
+	}
+
+	set_srm(obex, op, G_OBEX_SRM_DISABLE);
+}
+
+static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
+{
+	GObexHeader *hdr;
+	guint8 op;
+	gboolean final;
+
+	if (!obex->use_srm)
+		return;
+
+	op = g_obex_packet_get_operation(pkt, &final);
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
+	if (hdr != NULL) {
+		guint8 srm;
+		g_obex_header_get_uint8(hdr, &srm);
+		g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm);
+		set_srm(obex, op, srm);
+	} else if (!g_obex_srm_enabled(obex))
+		set_srm(obex, op, G_OBEX_SRM_DISABLE);
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
+	if (hdr != NULL) {
+		guint8 srmp;
+		g_obex_header_get_uint8(hdr, &srmp);
+		g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp);
+		set_srmp(obex, srmp, outgoing);
+	} else if (obex->pending_req && obex->pending_req->suspended)
+		g_obex_packet_add_uint8(pkt, G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT);
+	else
+		set_srmp(obex, -1, outgoing);
+
+	if (final)
+		check_srm_final(obex, op);
+}
+
+static gboolean write_data(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	GObex *obex = user_data;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		goto stop_tx;
+
+	if (obex->tx_data == 0) {
+		struct pending_pkt *p = g_queue_pop_head(obex->tx_queue);
+		ssize_t len;
+
+		if (p == NULL)
+			goto stop_tx;
+
+		setup_srm(obex, p->pkt, TRUE);
+
+		if (g_obex_srm_enabled(obex))
+			goto encode;
+
+		/* Can't send a request while there's a pending one */
+		if (obex->pending_req && p->id > 0) {
+			g_queue_push_head(obex->tx_queue, p);
+			goto stop_tx;
+		}
+
+encode:
+		len = g_obex_packet_encode(p->pkt, obex->tx_buf, obex->tx_mtu);
+		if (len == -EAGAIN) {
+			g_queue_push_head(obex->tx_queue, p);
+			g_obex_suspend(obex);
+			goto stop_tx;
+		}
+
+		if (len < 0) {
+			pending_pkt_free(p);
+			goto done;
+		}
+
+		if (p->id > 0) {
+			if (obex->pending_req != NULL)
+				pending_pkt_free(obex->pending_req);
+			obex->pending_req = p;
+			p->timeout_id = g_timeout_add_seconds(p->timeout,
+							req_timeout, obex);
+		} else {
+			/* During packet encode final bit can be set */
+			if (obex->tx_buf[0] & FINAL_BIT)
+				check_srm_final(obex,
+						obex->tx_buf[0] & ~FINAL_BIT);
+			pending_pkt_free(p);
+		}
+
+		obex->tx_data = len;
+		obex->tx_sent = 0;
+	}
+
+	if (obex->suspended) {
+		obex->write_source = 0;
+		return FALSE;
+	}
+
+	if (!obex->write(obex, NULL))
+		goto stop_tx;
+
+done:
+	if (obex->tx_data > 0 || g_queue_get_length(obex->tx_queue) > 0)
+		return TRUE;
+
+stop_tx:
+	obex->rx_last_op = G_OBEX_OP_NONE;
+	obex->tx_data = 0;
+	obex->write_source = 0;
+	return FALSE;
+}
+
+static void enable_tx(GObex *obex)
+{
+	GIOCondition cond;
+
+	if (obex->suspended)
+		return;
+
+	if (!obex->io || obex->write_source > 0)
+		return;
+
+	cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex);
+}
+
+static gboolean g_obex_send_internal(GObex *obex, struct pending_pkt *p,
+								GError **err)
+{
+
+	if (obex->io == NULL) {
+		if (!err)
+			return FALSE;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
+					"The transport is not connected");
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return FALSE;
+	}
+
+	if (g_obex_packet_get_operation(p->pkt, NULL) == G_OBEX_OP_ABORT)
+		g_queue_push_head(obex->tx_queue, p);
+	else
+		g_queue_push_tail(obex->tx_queue, p);
+
+	if (obex->pending_req == NULL || p->id == 0)
+		enable_tx(obex);
+
+	return TRUE;
+}
+
+static void init_connect_data(GObex *obex, struct connect_data *data)
+{
+	guint16 u16;
+
+	memset(data, 0, sizeof(*data));
+
+	data->version = 0x10;
+	data->flags = 0;
+
+	u16 = g_htons(obex->rx_mtu);
+	memcpy(&data->mtu, &u16, sizeof(u16));
+}
+
+static guint8 *digest_response(const guint8 *nonce)
+{
+	GChecksum *md5;
+	guint8 *result;
+	gsize size;
+
+	result = g_new0(guint8, NONCE_LEN);
+
+	md5 = g_checksum_new(G_CHECKSUM_MD5);
+	if (md5 == NULL)
+		return result;
+
+	g_checksum_update(md5, nonce, NONCE_LEN);
+	g_checksum_update(md5, (guint8 *) ":BlueZ", 6);
+
+	size = NONCE_LEN;
+	g_checksum_get_digest(md5, result, &size);
+
+	g_checksum_free(md5);
+
+	return result;
+}
+
+static void prepare_auth_rsp(GObex *obex, GObexPacket *rsp)
+{
+	GObexHeader *hdr;
+	GObexApparam *authrsp;
+	const guint8 *nonce;
+	guint8 *result;
+	gsize len;
+
+	/* Check if client is already responding to authentication challenge */
+	hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_AUTHRESP);
+	if (hdr)
+		goto done;
+
+	if (!g_obex_apparam_get_bytes(obex->authchal, NONCE_TAG, &nonce, &len))
+		goto done;
+
+	if (len != NONCE_LEN)
+		goto done;
+
+	result = digest_response(nonce);
+	authrsp = g_obex_apparam_set_bytes(NULL, DIGEST_TAG, result, NONCE_LEN);
+
+	hdr = g_obex_header_new_tag(G_OBEX_HDR_AUTHRESP, authrsp);
+	g_obex_packet_add_header(rsp, hdr);
+
+	g_obex_apparam_free(authrsp);
+	g_free(result);
+
+done:
+	g_obex_apparam_free(obex->authchal);
+	obex->authchal = NULL;
+}
+
+static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
+{
+	GObexHeader *hdr;
+	struct connect_data data;
+	static guint32 next_connid = 1;
+
+	init_connect_data(obex, &data);
+	g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
+
+	hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
+	if (hdr) {
+		g_obex_header_get_uint32(hdr, &obex->conn_id);
+		goto done;
+	}
+
+	obex->conn_id = next_connid++;
+
+	hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
+	g_obex_packet_prepend_header(rsp, hdr);
+
+done:
+	if (obex->authchal)
+		prepare_auth_rsp(obex, rsp);
+}
+
+static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
+{
+	GObexHeader *hdr;
+
+	if (!obex->use_srm || obex->srm == NULL)
+		return;
+
+	if (obex->srm->enabled)
+		return;
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
+	if (hdr != NULL)
+		return;
+
+	hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
+	g_obex_packet_prepend_header(pkt, hdr);
+}
+
+gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
+{
+	struct pending_pkt *p;
+	gboolean ret;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	if (obex == NULL || pkt == NULL) {
+		if (!err)
+			return FALSE;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_INVALID_ARGS,
+				"Invalid arguments");
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return FALSE;
+	}
+
+	switch (obex->rx_last_op) {
+	case G_OBEX_OP_CONNECT:
+		prepare_connect_rsp(obex, pkt);
+		break;
+	case G_OBEX_OP_GET:
+	case G_OBEX_OP_PUT:
+		prepare_srm_rsp(obex, pkt);
+		break;
+	}
+
+	p = g_new0(struct pending_pkt, 1);
+	p->pkt = pkt;
+
+	ret = g_obex_send_internal(obex, p, err);
+	if (ret == FALSE)
+		pending_pkt_free(p);
+
+	return ret;
+}
+
+static void prepare_srm_req(GObex *obex, GObexPacket *pkt)
+{
+	GObexHeader *hdr;
+
+	if (!obex->use_srm)
+		return;
+
+	if (obex->srm != NULL && obex->srm->enabled)
+		return;
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
+	if (hdr != NULL)
+		return;
+
+	hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
+	g_obex_packet_prepend_header(pkt, hdr);
+}
+
+guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout,
+			GObexResponseFunc func, gpointer user_data,
+			GError **err)
+{
+	GObexHeader *hdr;
+	struct pending_pkt *p;
+	static guint id = 1;
+	guint8 op;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	op = g_obex_packet_get_operation(req, NULL);
+	if (op == G_OBEX_OP_PUT || op == G_OBEX_OP_GET) {
+		/* Only enable SRM automatically for GET and PUT */
+		prepare_srm_req(obex, req);
+	}
+
+	if (obex->conn_id == CONNID_INVALID)
+		goto create_pending;
+
+	if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
+		goto create_pending;
+
+	if (g_obex_srm_enabled(obex) && obex->pending_req != NULL)
+		goto create_pending;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
+	if (hdr != NULL)
+		goto create_pending;
+
+	hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
+	g_obex_packet_prepend_header(req, hdr);
+
+create_pending:
+	p = g_new0(struct pending_pkt, 1);
+
+	p->pkt = req;
+	p->id = id++;
+	p->rsp_func = func;
+	p->rsp_data = user_data;
+
+	if (timeout < 0)
+		p->timeout = G_OBEX_DEFAULT_TIMEOUT;
+	else
+		p->timeout = timeout;
+
+	if (!g_obex_send_internal(obex, p, err)) {
+		pending_pkt_free(p);
+		return 0;
+	}
+
+	return p->id;
+}
+
+static int pending_pkt_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct pending_pkt *p = a;
+	guint id = GPOINTER_TO_UINT(b);
+
+	return (p->id - id);
+}
+
+static gboolean pending_req_abort(GObex *obex, GError **err)
+{
+	struct pending_pkt *p = obex->pending_req;
+	GObexPacket *req;
+
+	if (p->cancelled)
+		return TRUE;
+
+	p->cancelled = TRUE;
+
+	if (p->timeout_id > 0)
+		g_source_remove(p->timeout_id);
+
+	p->timeout = G_OBEX_ABORT_TIMEOUT;
+	p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
+
+	req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
+
+	return g_obex_send(obex, req, err);
+}
+
+static gboolean cancel_complete(gpointer user_data)
+{
+	struct pending_pkt *p = user_data;
+	GObex *obex = p->obex;
+	GError *err;
+
+	g_assert(p->rsp_func != NULL);
+
+	err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
+					"The request was cancelled");
+	p->rsp_func(obex, err, NULL, p->rsp_data);
+
+	g_error_free(err);
+
+	pending_pkt_free(p);
+
+	return FALSE;
+}
+
+gboolean g_obex_cancel_req(GObex *obex, guint req_id, gboolean remove_callback)
+{
+	GList *match;
+	struct pending_pkt *p;
+
+	if (obex->pending_req && obex->pending_req->id == req_id) {
+		if (!pending_req_abort(obex, NULL)) {
+			p = obex->pending_req;
+			obex->pending_req = NULL;
+			goto immediate_completion;
+		}
+
+		if (remove_callback)
+			obex->pending_req->rsp_func = NULL;
+
+		return TRUE;
+	}
+
+	match = g_queue_find_custom(obex->tx_queue, GUINT_TO_POINTER(req_id),
+							pending_pkt_cmp);
+	if (match == NULL)
+		return FALSE;
+
+	p = match->data;
+
+	g_queue_delete_link(obex->tx_queue, match);
+
+immediate_completion:
+	p->cancelled = TRUE;
+	p->obex = g_obex_ref(obex);
+
+	if (remove_callback || p->rsp_func == NULL)
+		pending_pkt_free(p);
+	else
+		g_idle_add(cancel_complete, p);
+
+	return TRUE;
+}
+
+gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
+						guint8 first_hdr_type, ...)
+{
+	GObexPacket *rsp;
+	va_list args;
+
+	va_start(args, first_hdr_type);
+	rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_type, args);
+	va_end(args);
+
+	return g_obex_send(obex, rsp, err);
+}
+
+void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
+							gpointer user_data)
+{
+	obex->disconn_func = func;
+	obex->disconn_func_data = user_data;
+}
+
+static int req_handler_cmpop(gconstpointer a, gconstpointer b)
+{
+	const struct req_handler *handler = a;
+	guint opcode = GPOINTER_TO_UINT(b);
+
+	return (int) handler->opcode - (int) opcode;
+}
+
+static int req_handler_cmpid(gconstpointer a, gconstpointer b)
+{
+	const struct req_handler *handler = a;
+	guint id = GPOINTER_TO_UINT(b);
+
+	return (int) handler->id - (int) id;
+}
+
+guint g_obex_add_request_function(GObex *obex, guint8 opcode,
+						GObexRequestFunc func,
+						gpointer user_data)
+{
+	struct req_handler *handler;
+	static guint next_id = 1;
+
+	handler = g_new0(struct req_handler, 1);
+	handler->id = next_id++;
+	handler->opcode = opcode;
+	handler->func = func;
+	handler->user_data = user_data;
+
+	obex->req_handlers = g_slist_prepend(obex->req_handlers, handler);
+
+	return handler->id;
+}
+
+gboolean g_obex_remove_request_function(GObex *obex, guint id)
+{
+	struct req_handler *handler;
+	GSList *match;
+
+	match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(id),
+							req_handler_cmpid);
+	if (match == NULL)
+		return FALSE;
+
+	handler = match->data;
+
+	obex->req_handlers = g_slist_delete_link(obex->req_handlers, match);
+	g_free(handler);
+
+	return TRUE;
+}
+
+static void g_obex_srm_suspend(GObex *obex)
+{
+	struct pending_pkt *p = obex->pending_req;
+	GObexPacket *req;
+
+	if (p->timeout_id > 0) {
+		g_source_remove(p->timeout_id);
+		p->timeout_id = 0;
+	}
+
+	p->suspended = TRUE;
+
+	req = g_obex_packet_new(G_OBEX_OP_GET, TRUE,
+					G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
+					G_OBEX_HDR_INVALID);
+
+	g_obex_send(obex, req, NULL);
+}
+
+void g_obex_suspend(GObex *obex)
+{
+	struct pending_pkt *req = obex->pending_req;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	if (!g_obex_srm_active(obex) || !req)
+		goto done;
+
+	/* Send SRMP wait in case of GET */
+	if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) {
+		g_obex_srm_suspend(obex);
+		return;
+	}
+
+done:
+	obex->suspended = TRUE;
+
+	if (obex->write_source > 0) {
+		g_source_remove(obex->write_source);
+		obex->write_source = 0;
+	}
+}
+
+static void g_obex_srm_resume(GObex *obex)
+{
+	struct pending_pkt *p = obex->pending_req;
+	GObexPacket *req;
+
+	p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
+	p->suspended = FALSE;
+
+	req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+
+	g_obex_send(obex, req, NULL);
+}
+
+void g_obex_resume(GObex *obex)
+{
+	struct pending_pkt *req = obex->pending_req;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	obex->suspended = FALSE;
+
+	if (g_obex_srm_active(obex) || !req)
+		goto done;
+
+	if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET)
+		g_obex_srm_resume(obex);
+
+done:
+	if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0)
+		enable_tx(obex);
+}
+
+gboolean g_obex_srm_active(GObex *obex)
+{
+	gboolean ret = FALSE;
+
+	if (!g_obex_srm_enabled(obex))
+		goto done;
+
+	if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
+		goto done;
+
+	ret = TRUE;
+done:
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
+	return ret;
+}
+
+static void auth_challenge(GObex *obex)
+{
+	struct pending_pkt *p = obex->pending_req;
+
+	if (p->authenticating)
+		return;
+
+	p->authenticating = TRUE;
+
+	prepare_auth_rsp(obex, p->pkt);
+
+	/* Remove it as pending and add it back to the queue so it gets sent
+	 * again */
+	if (p->timeout_id > 0) {
+		g_source_remove(p->timeout_id);
+		p->timeout_id = 0;
+	}
+	obex->pending_req = NULL;
+	g_obex_send_internal(obex, p, NULL);
+}
+
+static void parse_connect_data(GObex *obex, GObexPacket *pkt)
+{
+	const struct connect_data *data;
+	GObexHeader *hdr;
+	guint16 u16;
+	size_t data_len;
+
+	data = g_obex_packet_get_data(pkt, &data_len);
+	if (data == NULL || data_len != sizeof(*data))
+		return;
+
+	memcpy(&u16, &data->mtu, sizeof(u16));
+
+	obex->tx_mtu = g_ntohs(u16);
+	if (obex->io_tx_mtu > 0 && obex->tx_mtu > obex->io_tx_mtu)
+		obex->tx_mtu = obex->io_tx_mtu;
+	obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu);
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
+	if (hdr)
+		g_obex_header_get_uint32(hdr, &obex->conn_id);
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_AUTHCHAL);
+	if (hdr)
+		obex->authchal = g_obex_header_get_apparam(hdr);
+}
+
+static gboolean parse_response(GObex *obex, GObexPacket *rsp)
+{
+	struct pending_pkt *p = obex->pending_req;
+	guint8 opcode, rspcode;
+	gboolean final;
+
+	rspcode = g_obex_packet_get_operation(rsp, &final);
+
+	opcode = g_obex_packet_get_operation(p->pkt, NULL);
+	if (opcode == G_OBEX_OP_CONNECT) {
+		parse_connect_data(obex, rsp);
+		if (rspcode == G_OBEX_RSP_UNAUTHORIZED && obex->authchal)
+			auth_challenge(obex);
+	}
+
+	setup_srm(obex, rsp, FALSE);
+
+	if (!g_obex_srm_enabled(obex))
+		return final;
+
+	/*
+	 * Resposes have final bit set but in case of GET with SRM
+	 * we should not remove the request since the remote side will
+	 * continue sending responses until the transfer is finished
+	 */
+	if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) {
+		if (p->timeout_id > 0)
+			g_source_remove(p->timeout_id);
+
+		p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout,
+									obex);
+		return FALSE;
+	}
+
+	return final;
+}
+
+static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
+{
+	struct pending_pkt *p;
+	gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
+
+	if (rsp != NULL)
+		final_rsp = parse_response(obex, rsp);
+
+	if (!obex->pending_req)
+		return;
+
+	p = obex->pending_req;
+
+	/* Reset if final so it can no longer be cancelled */
+	if (final_rsp)
+		obex->pending_req = NULL;
+
+	if (p->cancelled)
+		err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
+					"The operation was cancelled");
+
+	if (err)
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
+
+	if (p->rsp_func) {
+		p->rsp_func(obex, err, rsp, p->rsp_data);
+
+		/* Check if user callback removed the request */
+		if (!final_rsp && p != obex->pending_req)
+			return;
+	}
+
+	if (p->cancelled)
+		g_error_free(err);
+
+	if (final_rsp)
+		pending_pkt_free(p);
+
+	if (!disconn && g_queue_get_length(obex->tx_queue) > 0)
+		enable_tx(obex);
+}
+
+static gboolean check_connid(GObex *obex, GObexPacket *pkt)
+{
+	GObexHeader *hdr;
+	guint32 id;
+
+	if (obex->conn_id == CONNID_INVALID)
+		return TRUE;
+
+	hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
+	if (hdr == NULL)
+		return TRUE;
+
+	g_obex_header_get_uint32(hdr, &id);
+
+	return obex->conn_id == id;
+}
+
+static int parse_request(GObex *obex, GObexPacket *req)
+{
+	guint8 op;
+	gboolean final;
+
+	op = g_obex_packet_get_operation(req, &final);
+	switch (op) {
+	case G_OBEX_OP_CONNECT:
+		parse_connect_data(obex, req);
+		break;
+	case G_OBEX_OP_ABORT:
+		break;
+	default:
+		if (check_connid(obex, req))
+			break;
+
+		return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
+	}
+
+	setup_srm(obex, req, FALSE);
+
+	return op;
+}
+
+static void handle_request(GObex *obex, GObexPacket *req)
+{
+	GSList *match;
+	int op;
+
+	op = parse_request(obex, req);
+	if (op < 0)
+		goto fail;
+
+	match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op),
+							req_handler_cmpop);
+	if (match) {
+		struct req_handler *handler = match->data;
+		handler->func(obex, req, handler->user_data);
+		return;
+	}
+
+	op = -G_OBEX_RSP_NOT_IMPLEMENTED;
+
+fail:
+	g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", g_obex_strerror(-op));
+	g_obex_send_rsp(obex, -op, NULL, G_OBEX_HDR_INVALID);
+}
+
+static gboolean read_stream(GObex *obex, GError **err)
+{
+	GIOChannel *io = obex->io;
+	GIOStatus status;
+	gsize rbytes, toread;
+	guint16 u16;
+	char *buf;
+
+	if (obex->rx_data >= 3)
+		goto read_body;
+
+	rbytes = 0;
+	toread = 3 - obex->rx_data;
+	buf = (char *) &obex->rx_buf[obex->rx_data];
+
+	status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
+	if (status != G_IO_STATUS_NORMAL)
+		return TRUE;
+
+	obex->rx_data += rbytes;
+	if (obex->rx_data < 3)
+		goto done;
+
+	memcpy(&u16, &buf[1], sizeof(u16));
+	obex->rx_pkt_len = g_ntohs(u16);
+
+	if (obex->rx_pkt_len > obex->rx_mtu) {
+		if (!err)
+			return FALSE;
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+				"Too big incoming packet");
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+		return FALSE;
+	}
+
+read_body:
+	if (obex->rx_data >= obex->rx_pkt_len)
+		goto done;
+
+	do {
+		toread = obex->rx_pkt_len - obex->rx_data;
+		buf = (char *) &obex->rx_buf[obex->rx_data];
+
+		status = g_io_channel_read_chars(io, buf, toread, &rbytes, NULL);
+		if (status != G_IO_STATUS_NORMAL)
+			goto done;
+
+		obex->rx_data += rbytes;
+	} while (rbytes > 0 && obex->rx_data < obex->rx_pkt_len);
+
+done:
+	g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data);
+
+	return TRUE;
+}
+
+static gboolean read_packet(GObex *obex, GError **err)
+{
+	GIOChannel *io = obex->io;
+	GError *read_err = NULL;
+	GIOStatus status;
+	gsize rbytes;
+	guint16 u16;
+
+	if (obex->rx_data > 0) {
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+				"RX buffer not empty before reading packet");
+		goto fail;
+	}
+
+	status = g_io_channel_read_chars(io, (char *) obex->rx_buf,
+					obex->rx_mtu, &rbytes, &read_err);
+	if (status != G_IO_STATUS_NORMAL) {
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+				"Unable to read data: %s", read_err->message);
+		g_error_free(read_err);
+		goto fail;
+	}
+
+	obex->rx_data += rbytes;
+
+	if (rbytes < 3) {
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+				"Incomplete packet received");
+		goto fail;
+	}
+
+	memcpy(&u16, &obex->rx_buf[1], sizeof(u16));
+	obex->rx_pkt_len = g_ntohs(u16);
+
+	if (obex->rx_pkt_len != rbytes) {
+		g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+			"Data size doesn't match packet size (%zu != %u)",
+			rbytes, obex->rx_pkt_len);
+		return FALSE;
+	}
+
+	g_obex_dump(G_OBEX_DEBUG_DATA, ">", obex->rx_buf, obex->rx_data);
+
+	return TRUE;
+fail:
+	if (err)
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
+
+	return FALSE;
+}
+
+static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	GObex *obex = user_data;
+	GObexPacket *pkt;
+	ssize_t header_offset;
+	GError *err = NULL;
+	guint8 opcode;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR)) {
+		err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED,
+					"Transport got disconnected");
+		goto failed;
+	}
+
+	if (!obex->read(obex, &err))
+		goto failed;
+
+	if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
+		return TRUE;
+
+	obex->rx_last_op = obex->rx_buf[0] & ~FINAL_BIT;
+
+	if (obex->pending_req) {
+		struct pending_pkt *p = obex->pending_req;
+		opcode = g_obex_packet_get_operation(p->pkt, NULL);
+		header_offset = rsp_header_offset(opcode);
+	} else {
+		opcode = obex->rx_last_op;
+		/* Unexpected response -- fail silently */
+		if (opcode > 0x1f && opcode != G_OBEX_OP_ABORT) {
+			obex->rx_data = 0;
+			return TRUE;
+		}
+		header_offset = req_header_offset(opcode);
+	}
+
+	if (header_offset < 0) {
+		err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
+				"Unknown header offset for opcode 0x%02x",
+				opcode);
+		goto failed;
+	}
+
+	pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
+							G_OBEX_DATA_REF, &err);
+	if (pkt == NULL)
+		goto failed;
+
+	/* Protect against user callback freeing the object */
+	g_obex_ref(obex);
+
+	if (obex->pending_req)
+		handle_response(obex, NULL, pkt);
+	else
+		handle_request(obex, pkt);
+
+	obex->rx_data = 0;
+
+	g_obex_unref(obex);
+
+	if (err != NULL)
+		g_error_free(err);
+
+	if (pkt != NULL)
+		g_obex_packet_free(pkt);
+
+	return TRUE;
+
+failed:
+	if (err)
+		g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
+
+	g_io_channel_unref(obex->io);
+	obex->io = NULL;
+	obex->io_source = 0;
+	obex->rx_data = 0;
+
+	/* Protect against user callback freeing the object */
+	g_obex_ref(obex);
+
+	if (obex->pending_req)
+		handle_response(obex, err, NULL);
+
+	if (obex->disconn_func)
+		obex->disconn_func(obex, err, obex->disconn_func_data);
+
+	g_obex_unref(obex);
+
+	g_error_free(err);
+
+	return FALSE;
+}
+
+static GDebugKey keys[] = {
+	{ "error",	G_OBEX_DEBUG_ERROR },
+	{ "command",	G_OBEX_DEBUG_COMMAND },
+	{ "transfer",	G_OBEX_DEBUG_TRANSFER },
+	{ "header",	G_OBEX_DEBUG_HEADER },
+	{ "packet",	G_OBEX_DEBUG_PACKET },
+	{ "data",	G_OBEX_DEBUG_DATA },
+	{ "apparam",	G_OBEX_DEBUG_APPARAM },
+};
+
+GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
+					gssize io_rx_mtu, gssize io_tx_mtu)
+{
+	GObex *obex;
+	GIOCondition cond;
+
+	if (gobex_debug == 0) {
+		const char *env = g_getenv("GOBEX_DEBUG");
+
+		if (env) {
+			gobex_debug = g_parse_debug_string(env, keys, 7);
+			g_setenv("G_MESSAGES_DEBUG", "gobex", FALSE);
+		} else
+			gobex_debug = G_OBEX_DEBUG_NONE;
+	}
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
+
+	if (io == NULL)
+		return NULL;
+
+	if (io_rx_mtu >= 0 && io_rx_mtu < G_OBEX_MINIMUM_MTU)
+		return NULL;
+
+	if (io_tx_mtu >= 0 && io_tx_mtu < G_OBEX_MINIMUM_MTU)
+		return NULL;
+
+	obex = g_new0(GObex, 1);
+
+	obex->io = g_io_channel_ref(io);
+	obex->ref_count = 1;
+	obex->conn_id = CONNID_INVALID;
+	obex->rx_last_op = G_OBEX_OP_NONE;
+
+	obex->io_rx_mtu = io_rx_mtu;
+	obex->io_tx_mtu = io_tx_mtu;
+
+	if (io_rx_mtu > G_OBEX_MAXIMUM_MTU)
+		obex->rx_mtu = G_OBEX_MAXIMUM_MTU;
+	else if (io_rx_mtu < G_OBEX_MINIMUM_MTU)
+		obex->rx_mtu = G_OBEX_DEFAULT_MTU;
+	else
+		obex->rx_mtu = io_rx_mtu;
+
+	obex->tx_mtu = G_OBEX_MINIMUM_MTU;
+
+	obex->tx_queue = g_queue_new();
+	obex->rx_buf = g_malloc(obex->rx_mtu);
+	obex->tx_buf = g_malloc(obex->tx_mtu);
+
+	switch (transport_type) {
+	case G_OBEX_TRANSPORT_STREAM:
+		obex->read = read_stream;
+		obex->write = write_stream;
+		break;
+	case G_OBEX_TRANSPORT_PACKET:
+		obex->use_srm = TRUE;
+		obex->read = read_packet;
+		obex->write = write_packet;
+		break;
+	default:
+		g_obex_unref(obex);
+		return NULL;
+	}
+
+	g_io_channel_set_encoding(io, NULL, NULL);
+	g_io_channel_set_buffered(io, FALSE);
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	obex->io_source = g_io_add_watch(io, cond, incoming_data, obex);
+
+	return obex;
+}
+
+GObex *g_obex_ref(GObex *obex)
+{
+	int refs;
+
+	if (obex == NULL)
+		return NULL;
+
+	refs = __sync_add_and_fetch(&obex->ref_count, 1);
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", refs);
+
+	return obex;
+}
+
+void g_obex_unref(GObex *obex)
+{
+	int refs;
+
+	refs = __sync_sub_and_fetch(&obex->ref_count, 1);
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "ref %u", refs);
+
+	if (refs > 0)
+		return;
+
+	g_slist_free_full(obex->req_handlers, g_free);
+
+	g_queue_foreach(obex->tx_queue, (GFunc) pending_pkt_free, NULL);
+	g_queue_free(obex->tx_queue);
+
+	if (obex->io != NULL)
+		g_io_channel_unref(obex->io);
+
+	if (obex->io_source > 0)
+		g_source_remove(obex->io_source);
+
+	if (obex->write_source > 0)
+		g_source_remove(obex->write_source);
+
+	g_free(obex->rx_buf);
+	g_free(obex->tx_buf);
+	g_free(obex->srm);
+
+	if (obex->pending_req)
+		pending_pkt_free(obex->pending_req);
+
+	if (obex->authchal)
+		g_obex_apparam_free(obex->authchal);
+
+	g_free(obex);
+}
+
+/* Higher level functions */
+
+guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
+					GError **err, guint8 first_hdr_id, ...)
+{
+	GObexPacket *req;
+	struct connect_data data;
+	va_list args;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
+
+	va_start(args, first_hdr_id);
+	req = g_obex_packet_new_valist(G_OBEX_OP_CONNECT, TRUE,
+							first_hdr_id, args);
+	va_end(args);
+
+	init_connect_data(obex, &data);
+	g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
+								GError **err)
+{
+	GObexPacket *req;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
+
+	req = g_obex_packet_new(G_OBEX_OP_DISCONNECT, TRUE, G_OBEX_HDR_INVALID);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
+					gpointer user_data, GError **err)
+{
+	GObexPacket *req;
+	struct setpath_data data;
+	const char *folder;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_INVALID);
+
+	memset(&data, 0, sizeof(data));
+
+	if (path != NULL && strncmp("..", path, 2) == 0) {
+		data.flags = 0x03;
+		folder = (path[2] == '/') ? &path[3] : NULL;
+	} else {
+		data.flags = 0x02;
+		folder = path;
+	}
+
+	if (folder != NULL) {
+		GObexHeader *hdr;
+		hdr = g_obex_header_new_unicode(G_OBEX_HDR_NAME, folder);
+		g_obex_packet_add_header(req, hdr);
+	}
+
+	g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func,
+					gpointer user_data, GError **err)
+{
+	GObexPacket *req;
+	struct setpath_data data;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	req = g_obex_packet_new(G_OBEX_OP_SETPATH, TRUE, G_OBEX_HDR_NAME, path,
+							G_OBEX_HDR_INVALID);
+
+	memset(&data, 0, sizeof(data));
+	g_obex_packet_set_data(req, &data, sizeof(data), G_OBEX_DATA_COPY);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func,
+					gpointer user_data, GError **err)
+{
+	GObexPacket *req;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_NAME, name,
+							G_OBEX_HDR_INVALID);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_copy(GObex *obex, const char *name, const char *dest,
+			GObexResponseFunc func, gpointer user_data,
+			GError **err)
+{
+	GObexPacket *req;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
+					G_OBEX_HDR_ACTION, G_OBEX_ACTION_COPY,
+					G_OBEX_HDR_NAME, name,
+					G_OBEX_HDR_DESTNAME, dest,
+					G_OBEX_HDR_INVALID);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_move(GObex *obex, const char *name, const char *dest,
+			GObexResponseFunc func, gpointer user_data,
+			GError **err)
+{
+	GObexPacket *req;
+
+	g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+
+	req = g_obex_packet_new(G_OBEX_OP_ACTION, TRUE,
+					G_OBEX_HDR_ACTION, G_OBEX_ACTION_MOVE,
+					G_OBEX_HDR_NAME, name,
+					G_OBEX_HDR_DESTNAME, dest,
+					G_OBEX_HDR_INVALID);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint g_obex_abort(GObex *obex, GObexResponseFunc func, gpointer user_data,
+								GError **err)
+{
+	GObexPacket *req;
+
+	req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
+
+	return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
+guint8 g_obex_errno_to_rsp(int err)
+{
+	switch (err) {
+	case 0:
+		return G_OBEX_RSP_SUCCESS;
+	case -EPERM:
+	case -EACCES:
+		return G_OBEX_RSP_FORBIDDEN;
+	case -ENOENT:
+		return G_OBEX_RSP_NOT_FOUND;
+	case -EINVAL:
+	case -EBADR:
+		return G_OBEX_RSP_BAD_REQUEST;
+	case -EFAULT:
+		return G_OBEX_RSP_SERVICE_UNAVAILABLE;
+	case -ENOSYS:
+		return G_OBEX_RSP_NOT_IMPLEMENTED;
+	case -ENOTEMPTY:
+	case -EEXIST:
+		return G_OBEX_RSP_PRECONDITION_FAILED;
+	default:
+		return G_OBEX_RSP_INTERNAL_SERVER_ERROR;
+	}
+}
diff --git a/bluez/repo/gobex/gobex.h b/bluez/repo/gobex/gobex.h
new file mode 100644
index 0000000..5bc9103
--- /dev/null
+++ b/bluez/repo/gobex/gobex.h
@@ -0,0 +1,138 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GOBEX_H
+#define __GOBEX_H
+
+#include <stdarg.h>
+#include <glib.h>
+
+#include "gobex/gobex-defs.h"
+#include "gobex/gobex-packet.h"
+
+typedef enum {
+	G_OBEX_TRANSPORT_STREAM,
+	G_OBEX_TRANSPORT_PACKET,
+} GObexTransportType;
+
+typedef struct _GObex GObex;
+
+typedef void (*GObexFunc) (GObex *obex, GError *err, gpointer user_data);
+typedef void (*GObexRequestFunc) (GObex *obex, GObexPacket *req,
+							gpointer user_data);
+typedef void (*GObexResponseFunc) (GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data);
+
+gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err);
+
+guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout,
+			GObexResponseFunc func, gpointer user_data,
+			GError **err);
+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, ...);
+
+void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
+							gpointer user_data);
+guint g_obex_add_request_function(GObex *obex, guint8 opcode,
+						GObexRequestFunc func,
+						gpointer user_data);
+gboolean g_obex_remove_request_function(GObex *obex, guint id);
+
+void g_obex_suspend(GObex *obex);
+void g_obex_resume(GObex *obex);
+gboolean g_obex_srm_active(GObex *obex);
+
+GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
+						gssize rx_mtu, gssize tx_mtu);
+
+GObex *g_obex_ref(GObex *obex);
+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, ...);
+
+guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
+								GError **err);
+
+guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
+					gpointer user_data, GError **err);
+
+guint g_obex_mkdir(GObex *obex, const char *path, GObexResponseFunc func,
+					gpointer user_data, GError **err);
+
+guint g_obex_delete(GObex *obex, const char *name, GObexResponseFunc func,
+					gpointer user_data, GError **err);
+
+guint g_obex_copy(GObex *obex, const char *name, const char *dest,
+			GObexResponseFunc func, gpointer user_data,
+			GError **err);
+
+guint g_obex_move(GObex *obex, const char *name, const char *dest,
+			GObexResponseFunc func, gpointer user_data,
+			GError **err);
+
+guint g_obex_abort(GObex *obex, GObexResponseFunc func, gpointer user_data,
+								GError **err);
+
+/* Transfer related high-level functions */
+
+guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
+			GObexFunc complete_func, gpointer user_data,
+			GError **err, guint8 first_hdr_id, ...);
+
+guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
+			GObexDataProducer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err);
+
+guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
+			GObexFunc complete_func, gpointer user_data,
+			GError **err, guint8 first_hdr_id, ...);
+
+guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
+			GObexDataConsumer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err);
+
+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 g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
+			GObexFunc complete_func, gpointer user_data,
+			GError **err, guint8 first_hdr_id, ...);
+
+guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp,
+			GObexDataProducer data_func, GObexFunc complete_func,
+			gpointer user_data, GError **err);
+
+gboolean g_obex_cancel_transfer(guint id, GObexFunc complete_func,
+							gpointer user_data);
+
+const char *g_obex_strerror(guint8 err_code);
+guint8 g_obex_errno_to_rsp(int err);
+
+#endif /* __GOBEX_H */
diff --git a/bluez/repo/lib/._a2mp.h b/bluez/repo/lib/._a2mp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._a2mp.h
Binary files differ
diff --git a/bluez/repo/lib/._amp.h b/bluez/repo/lib/._amp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._amp.h
Binary files differ
diff --git a/bluez/repo/lib/._bluetooth.c b/bluez/repo/lib/._bluetooth.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._bluetooth.c
Binary files differ
diff --git a/bluez/repo/lib/._bluetooth.h b/bluez/repo/lib/._bluetooth.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._bluetooth.h
Binary files differ
diff --git a/bluez/repo/lib/._bluez.pc.in b/bluez/repo/lib/._bluez.pc.in
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._bluez.pc.in
Binary files differ
diff --git a/bluez/repo/lib/._bnep.h b/bluez/repo/lib/._bnep.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._bnep.h
Binary files differ
diff --git a/bluez/repo/lib/._cmtp.h b/bluez/repo/lib/._cmtp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._cmtp.h
Binary files differ
diff --git a/bluez/repo/lib/._hci.c b/bluez/repo/lib/._hci.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._hci.c
Binary files differ
diff --git a/bluez/repo/lib/._hci.h b/bluez/repo/lib/._hci.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._hci.h
Binary files differ
diff --git a/bluez/repo/lib/._hci_lib.h b/bluez/repo/lib/._hci_lib.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._hci_lib.h
Binary files differ
diff --git a/bluez/repo/lib/._hidp.h b/bluez/repo/lib/._hidp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._hidp.h
Binary files differ
diff --git a/bluez/repo/lib/._l2cap.h b/bluez/repo/lib/._l2cap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._l2cap.h
Binary files differ
diff --git a/bluez/repo/lib/._mgmt.h b/bluez/repo/lib/._mgmt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._mgmt.h
Binary files differ
diff --git a/bluez/repo/lib/._rfcomm.h b/bluez/repo/lib/._rfcomm.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._rfcomm.h
Binary files differ
diff --git a/bluez/repo/lib/._sco.h b/bluez/repo/lib/._sco.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._sco.h
Binary files differ
diff --git a/bluez/repo/lib/._sdp.c b/bluez/repo/lib/._sdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._sdp.c
Binary files differ
diff --git a/bluez/repo/lib/._sdp.h b/bluez/repo/lib/._sdp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._sdp.h
Binary files differ
diff --git a/bluez/repo/lib/._sdp_lib.h b/bluez/repo/lib/._sdp_lib.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._sdp_lib.h
Binary files differ
diff --git a/bluez/repo/lib/._uuid.c b/bluez/repo/lib/._uuid.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._uuid.c
Binary files differ
diff --git a/bluez/repo/lib/._uuid.h b/bluez/repo/lib/._uuid.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/lib/._uuid.h
Binary files differ
diff --git a/bluez/repo/lib/a2mp.h b/bluez/repo/lib/a2mp.h
new file mode 100644
index 0000000..da937d1
--- /dev/null
+++ b/bluez/repo/lib/a2mp.h
@@ -0,0 +1,149 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (c) 2012  Code Aurora Forum. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A2MP Protocol */
+
+/* A2MP command codes */
+
+#define A2MP_COMMAND_REJ	0x01
+#define A2MP_DISCOVER_REQ	0x02
+#define A2MP_DISCOVER_RSP	0x03
+#define A2MP_CHANGE_NOTIFY	0x04
+#define A2MP_CHANGE_RSP		0x05
+#define A2MP_INFO_REQ		0x06
+#define A2MP_INFO_RSP		0x07
+#define A2MP_ASSOC_REQ		0x08
+#define A2MP_ASSOC_RSP		0x09
+#define A2MP_CREATE_REQ		0x0a
+#define A2MP_CREATE_RSP		0x0b
+#define A2MP_DISCONN_REQ	0x0c
+#define A2MP_DISCONN_RSP	0x0d
+
+struct a2mp_hdr {
+	uint8_t		code;
+	uint8_t		ident;
+	uint16_t	len;
+} __attribute__ ((packed));
+#define A2MP_HDR_SIZE 4
+
+struct a2mp_command_rej {
+	uint16_t	reason;
+} __attribute__ ((packed));
+
+struct a2mp_discover_req {
+	uint16_t	mtu;
+	uint16_t	mask;
+} __attribute__ ((packed));
+
+struct a2mp_ctrl {
+	uint8_t		id;
+	uint8_t		type;
+	uint8_t		status;
+} __attribute__ ((packed));
+
+struct a2mp_discover_rsp {
+	uint16_t	mtu;
+	uint16_t	mask;
+	struct a2mp_ctrl ctrl_list[0];
+} __attribute__ ((packed));
+
+struct a2mp_info_req {
+	uint8_t		id;
+} __attribute__ ((packed));
+
+struct a2mp_info_rsp {
+	uint8_t		id;
+	uint8_t		status;
+	uint32_t	total_bw;
+	uint32_t	max_bw;
+	uint32_t	min_latency;
+	uint16_t	pal_caps;
+	uint16_t	assoc_size;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_req {
+	uint8_t		id;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_rsp {
+	uint8_t		id;
+	uint8_t		status;
+	uint8_t		assoc_data[0];
+} __attribute__ ((packed));
+
+struct a2mp_create_req {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+	uint8_t		assoc_data[0];
+} __attribute__ ((packed));
+
+struct a2mp_create_rsp {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+	uint8_t		status;
+} __attribute__ ((packed));
+
+struct a2mp_disconn_req {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+} __attribute__ ((packed));
+
+struct a2mp_disconn_rsp {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+	uint8_t		status;
+} __attribute__ ((packed));
+
+#define A2MP_COMMAND_NOT_RECOGNIZED 0x0000
+
+/* AMP controller status */
+#define AMP_CTRL_POWERED_DOWN		0x00
+#define AMP_CTRL_BLUETOOTH_ONLY		0x01
+#define AMP_CTRL_NO_CAPACITY		0x02
+#define AMP_CTRL_LOW_CAPACITY		0x03
+#define AMP_CTRL_MEDIUM_CAPACITY	0x04
+#define AMP_CTRL_HIGH_CAPACITY		0x05
+#define AMP_CTRL_FULL_CAPACITY		0x06
+
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS				0x00
+#define A2MP_STATUS_INVALID_CTRL_ID			0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION		0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS		0x02
+#define A2MP_STATUS_COLLISION_OCCURED			0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD			0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS			0x05
+#define A2MP_STATUS_SECURITY_VIOLATION			0x06
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __A2MP_H */
diff --git a/bluez/repo/lib/amp.h b/bluez/repo/lib/amp.h
new file mode 100644
index 0000000..27aab1d
--- /dev/null
+++ b/bluez/repo/lib/amp.h
@@ -0,0 +1,172 @@
+/*
+ *
+ *	BlueZ - Bluetooth protocol stack for Linux
+ *
+ *	Copyright (C) 2010-2011 Code Aurora Forum.  All rights reserved.
+ *	Copyright (C) 2012 Intel Corporation.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License version 2 and
+ *	only version 2 as published by the Free Software Foundation.
+ *
+ *	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.
+ *
+ */
+
+#ifndef __AMP_H
+#define __AMP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AMP_MGR_CID 0x03
+
+/* AMP manager codes */
+#define AMP_COMMAND_REJ		0x01
+#define AMP_DISCOVER_REQ	0x02
+#define AMP_DISCOVER_RSP	0x03
+#define AMP_CHANGE_NOTIFY	0x04
+#define AMP_CHANGE_RSP		0x05
+#define AMP_INFO_REQ		0x06
+#define AMP_INFO_RSP		0x07
+#define AMP_ASSOC_REQ		0x08
+#define AMP_ASSOC_RSP		0x09
+#define AMP_LINK_REQ		0x0a
+#define AMP_LINK_RSP		0x0b
+#define AMP_DISCONN_REQ		0x0c
+#define AMP_DISCONN_RSP		0x0d
+
+typedef struct {
+	uint8_t		code;
+	uint8_t		ident;
+	uint16_t	len;
+} __attribute__ ((packed)) amp_mgr_hdr;
+#define AMP_MGR_HDR_SIZE 4
+
+/* AMP ASSOC structure */
+typedef struct {
+	uint8_t		type_id;
+	uint16_t	len;
+	uint8_t		data[0];
+} __attribute__ ((packed)) amp_assoc_tlv;
+
+typedef struct {
+	uint16_t	reason;
+} __attribute__ ((packed)) amp_cmd_rej_parms;
+
+typedef struct {
+	uint16_t	mtu;
+	uint16_t	mask;
+} __attribute__ ((packed)) amp_discover_req_parms;
+
+typedef struct {
+	uint16_t	mtu;
+	uint16_t	mask;
+	uint8_t		controller_list[0];
+} __attribute__ ((packed)) amp_discover_rsp_parms;
+
+typedef struct {
+	uint8_t		id;
+} __attribute__ ((packed)) amp_info_req_parms;
+
+typedef struct {
+	uint8_t		id;
+	uint8_t		status;
+	uint32_t	total_bandwidth;
+	uint32_t	max_bandwidth;
+	uint32_t	min_latency;
+	uint16_t	pal_caps;
+	uint16_t	assoc_size;
+} __attribute__ ((packed)) amp_info_rsp_parms;
+
+typedef struct {
+	uint8_t		id;
+	uint8_t		status;
+	amp_assoc_tlv	assoc;
+} __attribute__ ((packed)) amp_assoc_rsp_parms;
+
+typedef struct {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+	amp_assoc_tlv	assoc;
+} __attribute__ ((packed)) amp_link_req_parms;
+
+typedef struct {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+	uint8_t		status;
+} __attribute__ ((packed)) amp_link_rsp_parms;
+
+typedef struct {
+	uint8_t		local_id;
+	uint8_t		remote_id;
+} __attribute__ ((packed)) amp_disconn_req_parms;
+
+#define A2MP_MAC_ADDR_TYPE		1
+#define A2MP_PREF_CHANLIST_TYPE		2
+#define A2MP_CONNECTED_CHAN		3
+#define A2MP_PAL_CAP_TYPE		4
+#define A2MP_PAL_VER_INFO		5
+
+struct amp_tlv {
+	uint8_t type;
+	uint16_t len;
+	uint8_t val[0];
+} __attribute__ ((packed));
+
+struct amp_pal_ver {
+	uint8_t ver;
+	uint16_t company_id;
+	uint16_t sub_ver;
+} __attribute__ ((packed));
+
+struct amp_country_triplet {
+	union {
+		struct {
+			uint8_t first_channel;
+			uint8_t num_channels;
+			int8_t max_power;
+		} __attribute__ ((packed)) chans;
+		struct {
+			uint8_t reg_extension_id;
+			uint8_t reg_class;
+			uint8_t coverage_class;
+		} __attribute__ ((packed)) ext;
+	};
+} __attribute__ ((packed));
+
+struct amp_chan_list {
+	uint8_t country_code[3];
+	struct amp_country_triplet triplets[0];
+} __attribute__ ((packed));
+
+#define AMP_COMMAND_NOT_RECOGNIZED 0x0000
+
+/* AMP controller status */
+#define AMP_CT_POWERED_DOWN		0x00
+#define AMP_CT_BLUETOOTH_ONLY		0x01
+#define AMP_CT_NO_CAPACITY		0x02
+#define AMP_CT_LOW_CAPACITY		0x03
+#define AMP_CT_MEDIUM_CAPACITY		0x04
+#define AMP_CT_HIGH_CAPACITY		0x05
+#define AMP_CT_FULL_CAPACITY		0x06
+
+/* AMP response status */
+#define AMP_STATUS_SUCCESS				0x00
+#define AMP_STATUS_INVALID_CTRL_ID			0x01
+#define AMP_STATUS_UNABLE_START_LINK_CREATION		0x02
+#define AMP_STATUS_NO_PHYSICAL_LINK_EXISTS		0x02
+#define AMP_STATUS_COLLISION_OCCURED			0x03
+#define AMP_STATUS_DISCONN_REQ_RECVD			0x04
+#define AMP_STATUS_PHYS_LINK_EXISTS			0x05
+#define AMP_STATUS_SECURITY_VIOLATION			0x06
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AMP_H */
diff --git a/bluez/repo/lib/bluetooth.c b/bluez/repo/lib/bluetooth.c
new file mode 100644
index 0000000..4a569c0
--- /dev/null
+++ b/bluez/repo/lib/bluetooth.c
@@ -0,0 +1,1753 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src)
+{
+	register unsigned char *d = (unsigned char *) dst;
+	register const unsigned char *s = (const unsigned char *) src;
+	register int i;
+
+	for (i = 0; i < 6; i++)
+		d[i] = s[5-i];
+}
+
+char *batostr(const bdaddr_t *ba)
+{
+	char *str = bt_malloc(18);
+	if (!str)
+		return NULL;
+
+	sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+		ba->b[0], ba->b[1], ba->b[2],
+		ba->b[3], ba->b[4], ba->b[5]);
+
+	return str;
+}
+
+bdaddr_t *strtoba(const char *str)
+{
+	bdaddr_t b;
+	bdaddr_t *ba = bt_malloc(sizeof(*ba));
+
+	if (ba) {
+		str2ba(str, &b);
+		baswap(ba, &b);
+	}
+
+	return ba;
+}
+
+int ba2str(const bdaddr_t *ba, char *str)
+{
+	return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+		ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
+}
+
+int str2ba(const char *str, bdaddr_t *ba)
+{
+	int i;
+
+	if (bachk(str) < 0) {
+		memset(ba, 0, sizeof(*ba));
+		return -1;
+	}
+
+	for (i = 5; i >= 0; i--, str += 3)
+		ba->b[i] = strtol(str, NULL, 16);
+
+	return 0;
+}
+
+int ba2oui(const bdaddr_t *ba, char *str)
+{
+	return sprintf(str, "%2.2X-%2.2X-%2.2X", ba->b[5], ba->b[4], ba->b[3]);
+}
+
+int bachk(const char *str)
+{
+	if (!str)
+		return -1;
+
+	if (strlen(str) != 17)
+		return -1;
+
+	while (*str) {
+		if (!isxdigit(*str++))
+			return -1;
+
+		if (!isxdigit(*str++))
+			return -1;
+
+		if (*str == 0)
+			break;
+
+		if (*str++ != ':')
+			return -1;
+	}
+
+	return 0;
+}
+
+int baprintf(const char *format, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, format);
+	len = vprintf(format, ap);
+	va_end(ap);
+
+	return len;
+}
+
+int bafprintf(FILE *stream, const char *format, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, format);
+	len = vfprintf(stream, format, ap);
+	va_end(ap);
+
+	return len;
+}
+
+int basprintf(char *str, const char *format, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, format);
+	len = vsnprintf(str, (~0U) >> 1, format, ap);
+	va_end(ap);
+
+	return len;
+}
+
+int basnprintf(char *str, size_t size, const char *format, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, format);
+	len = vsnprintf(str, size, format, ap);
+	va_end(ap);
+
+	return len;
+}
+
+void *bt_malloc(size_t size)
+{
+	return malloc(size);
+}
+
+void bt_free(void *ptr)
+{
+	free(ptr);
+}
+
+/* Bluetooth error codes to Unix errno mapping */
+int bt_error(uint16_t code)
+{
+	switch (code) {
+	case 0:
+		return 0;
+	case HCI_UNKNOWN_COMMAND:
+		return EBADRQC;
+	case HCI_NO_CONNECTION:
+		return ENOTCONN;
+	case HCI_HARDWARE_FAILURE:
+		return EIO;
+	case HCI_PAGE_TIMEOUT:
+		return EHOSTDOWN;
+	case HCI_AUTHENTICATION_FAILURE:
+		return EACCES;
+	case HCI_PIN_OR_KEY_MISSING:
+		return EINVAL;
+	case HCI_MEMORY_FULL:
+		return ENOMEM;
+	case HCI_CONNECTION_TIMEOUT:
+		return ETIMEDOUT;
+	case HCI_MAX_NUMBER_OF_CONNECTIONS:
+	case HCI_MAX_NUMBER_OF_SCO_CONNECTIONS:
+		return EMLINK;
+	case HCI_ACL_CONNECTION_EXISTS:
+		return EALREADY;
+	case HCI_COMMAND_DISALLOWED:
+	case HCI_TRANSACTION_COLLISION:
+	case HCI_ROLE_SWITCH_PENDING:
+		return EBUSY;
+	case HCI_REJECTED_LIMITED_RESOURCES:
+	case HCI_REJECTED_PERSONAL:
+	case HCI_QOS_REJECTED:
+		return ECONNREFUSED;
+	case HCI_HOST_TIMEOUT:
+		return ETIMEDOUT;
+	case HCI_UNSUPPORTED_FEATURE:
+	case HCI_QOS_NOT_SUPPORTED:
+	case HCI_PAIRING_NOT_SUPPORTED:
+	case HCI_CLASSIFICATION_NOT_SUPPORTED:
+	case HCI_UNSUPPORTED_LMP_PARAMETER_VALUE:
+	case HCI_PARAMETER_OUT_OF_RANGE:
+	case HCI_QOS_UNACCEPTABLE_PARAMETER:
+		return EOPNOTSUPP;
+	case HCI_INVALID_PARAMETERS:
+	case HCI_SLOT_VIOLATION:
+		return EINVAL;
+	case HCI_OE_USER_ENDED_CONNECTION:
+	case HCI_OE_LOW_RESOURCES:
+	case HCI_OE_POWER_OFF:
+		return ECONNRESET;
+	case HCI_CONNECTION_TERMINATED:
+		return ECONNABORTED;
+	case HCI_REPEATED_ATTEMPTS:
+		return ELOOP;
+	case HCI_REJECTED_SECURITY:
+	case HCI_PAIRING_NOT_ALLOWED:
+	case HCI_INSUFFICIENT_SECURITY:
+		return EACCES;
+	case HCI_UNSUPPORTED_REMOTE_FEATURE:
+		return EPROTONOSUPPORT;
+	case HCI_SCO_OFFSET_REJECTED:
+		return ECONNREFUSED;
+	case HCI_UNKNOWN_LMP_PDU:
+	case HCI_INVALID_LMP_PARAMETERS:
+	case HCI_LMP_ERROR_TRANSACTION_COLLISION:
+	case HCI_LMP_PDU_NOT_ALLOWED:
+	case HCI_ENCRYPTION_MODE_NOT_ACCEPTED:
+		return EPROTO;
+	default:
+		return ENOSYS;
+	}
+}
+
+const char *bt_compidtostr(int compid)
+{
+	switch (compid) {
+	case 0:
+		return "Ericsson Technology Licensing";
+	case 1:
+		return "Nokia Mobile Phones";
+	case 2:
+		return "Intel Corp.";
+	case 3:
+		return "IBM Corp.";
+	case 4:
+		return "Toshiba Corp.";
+	case 5:
+		return "3Com";
+	case 6:
+		return "Microsoft";
+	case 7:
+		return "Lucent";
+	case 8:
+		return "Motorola";
+	case 9:
+		return "Infineon Technologies AG";
+	case 10:
+		return "Cambridge Silicon Radio";
+	case 11:
+		return "Silicon Wave";
+	case 12:
+		return "Digianswer A/S";
+	case 13:
+		return "Texas Instruments Inc.";
+	case 14:
+		return "Ceva, Inc. (formerly Parthus Technologies, Inc.)";
+	case 15:
+		return "Broadcom Corporation";
+	case 16:
+		return "Mitel Semiconductor";
+	case 17:
+		return "Widcomm, Inc";
+	case 18:
+		return "Zeevo, Inc.";
+	case 19:
+		return "Atmel Corporation";
+	case 20:
+		return "Mitsubishi Electric Corporation";
+	case 21:
+		return "RTX Telecom A/S";
+	case 22:
+		return "KC Technology Inc.";
+	case 23:
+		return "NewLogic";
+	case 24:
+		return "Transilica, Inc.";
+	case 25:
+		return "Rohde & Schwarz GmbH & Co. KG";
+	case 26:
+		return "TTPCom Limited";
+	case 27:
+		return "Signia Technologies, Inc.";
+	case 28:
+		return "Conexant Systems Inc.";
+	case 29:
+		return "Qualcomm";
+	case 30:
+		return "Inventel";
+	case 31:
+		return "AVM Berlin";
+	case 32:
+		return "BandSpeed, Inc.";
+	case 33:
+		return "Mansella Ltd";
+	case 34:
+		return "NEC Corporation";
+	case 35:
+		return "WavePlus Technology Co., Ltd.";
+	case 36:
+		return "Alcatel";
+	case 37:
+		return "NXP Semiconductors (formerly Philips Semiconductors)";
+	case 38:
+		return "C Technologies";
+	case 39:
+		return "Open Interface";
+	case 40:
+		return "R F Micro Devices";
+	case 41:
+		return "Hitachi Ltd";
+	case 42:
+		return "Symbol Technologies, Inc.";
+	case 43:
+		return "Tenovis";
+	case 44:
+		return "Macronix International Co. Ltd.";
+	case 45:
+		return "GCT Semiconductor";
+	case 46:
+		return "Norwood Systems";
+	case 47:
+		return "MewTel Technology Inc.";
+	case 48:
+		return "ST Microelectronics";
+	case 49:
+		return "Synopsis";
+	case 50:
+		return "Red-M (Communications) Ltd";
+	case 51:
+		return "Commil Ltd";
+	case 52:
+		return "Computer Access Technology Corporation (CATC)";
+	case 53:
+		return "Eclipse (HQ Espana) S.L.";
+	case 54:
+		return "Renesas Electronics Corporation";
+	case 55:
+		return "Mobilian Corporation";
+	case 56:
+		return "Terax";
+	case 57:
+		return "Integrated System Solution Corp.";
+	case 58:
+		return "Matsushita Electric Industrial Co., Ltd.";
+	case 59:
+		return "Gennum Corporation";
+	case 60:
+		return "BlackBerry Limited (formerly Research In Motion)";
+	case 61:
+		return "IPextreme, Inc.";
+	case 62:
+		return "Systems and Chips, Inc.";
+	case 63:
+		return "Bluetooth SIG, Inc.";
+	case 64:
+		return "Seiko Epson Corporation";
+	case 65:
+		return "Integrated Silicon Solution Taiwan, Inc.";
+	case 66:
+		return "CONWISE Technology Corporation Ltd";
+	case 67:
+		return "PARROT SA";
+	case 68:
+		return "Socket Mobile";
+	case 69:
+		return "Atheros Communications, Inc.";
+	case 70:
+		return "MediaTek, Inc.";
+	case 71:
+		return "Bluegiga";
+	case 72:
+		return "Marvell Technology Group Ltd.";
+	case 73:
+		return "3DSP Corporation";
+	case 74:
+		return "Accel Semiconductor Ltd.";
+	case 75:
+		return "Continental Automotive Systems";
+	case 76:
+		return "Apple, Inc.";
+	case 77:
+		return "Staccato Communications, Inc.";
+	case 78:
+		return "Avago Technologies";
+	case 79:
+		return "APT Licensing Ltd.";
+	case 80:
+		return "SiRF Technology";
+	case 81:
+		return "Tzero Technologies, Inc.";
+	case 82:
+		return "J&M Corporation";
+	case 83:
+		return "Free2move AB";
+	case 84:
+		return "3DiJoy Corporation";
+	case 85:
+		return "Plantronics, Inc.";
+	case 86:
+		return "Sony Ericsson Mobile Communications";
+	case 87:
+		return "Harman International Industries, Inc.";
+	case 88:
+		return "Vizio, Inc.";
+	case 89:
+		return "Nordic Semiconductor ASA";
+	case 90:
+		return "EM Microelectronic-Marin SA";
+	case 91:
+		return "Ralink Technology Corporation";
+	case 92:
+		return "Belkin International, Inc.";
+	case 93:
+		return "Realtek Semiconductor Corporation";
+	case 94:
+		return "Stonestreet One, LLC";
+	case 95:
+		return "Wicentric, Inc.";
+	case 96:
+		return "RivieraWaves S.A.S";
+	case 97:
+		return "RDA Microelectronics";
+	case 98:
+		return "Gibson Guitars";
+	case 99:
+		return "MiCommand Inc.";
+	case 100:
+		return "Band XI International, LLC";
+	case 101:
+		return "Hewlett-Packard Company";
+	case 102:
+		return "9Solutions Oy";
+	case 103:
+		return "GN Netcom A/S";
+	case 104:
+		return "General Motors";
+	case 105:
+		return "A&D Engineering, Inc.";
+	case 106:
+		return "MindTree Ltd.";
+	case 107:
+		return "Polar Electro OY";
+	case 108:
+		return "Beautiful Enterprise Co., Ltd.";
+	case 109:
+		return "BriarTek, Inc.";
+	case 110:
+		return "Summit Data Communications, Inc.";
+	case 111:
+		return "Sound ID";
+	case 112:
+		return "Monster, LLC";
+	case 113:
+		return "connectBlue AB";
+	case 114:
+		return "ShangHai Super Smart Electronics Co. Ltd.";
+	case 115:
+		return "Group Sense Ltd.";
+	case 116:
+		return "Zomm, LLC";
+	case 117:
+		return "Samsung Electronics Co. Ltd.";
+	case 118:
+		return "Creative Technology Ltd.";
+	case 119:
+		return "Laird Technologies";
+	case 120:
+		return "Nike, Inc.";
+	case 121:
+		return "lesswire AG";
+	case 122:
+		return "MStar Semiconductor, Inc.";
+	case 123:
+		return "Hanlynn Technologies";
+	case 124:
+		return "A & R Cambridge";
+	case 125:
+		return "Seers Technology Co. Ltd";
+	case 126:
+		return "Sports Tracking Technologies Ltd.";
+	case 127:
+		return "Autonet Mobile";
+	case 128:
+		return "DeLorme Publishing Company, Inc.";
+	case 129:
+		return "WuXi Vimicro";
+	case 130:
+		return "Sennheiser Communications A/S";
+	case 131:
+		return "TimeKeeping Systems, Inc.";
+	case 132:
+		return "Ludus Helsinki Ltd.";
+	case 133:
+		return "BlueRadios, Inc.";
+	case 134:
+		return "equinox AG";
+	case 135:
+		return "Garmin International, Inc.";
+	case 136:
+		return "Ecotest";
+	case 137:
+		return "GN ReSound A/S";
+	case 138:
+		return "Jawbone";
+	case 139:
+		return "Topcorn Positioning Systems, LLC";
+	case 140:
+		return "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)";
+	case 141:
+		return "Zscan Software";
+	case 142:
+		return "Quintic Corp.";
+	case 143:
+		return "Stollman E+V GmbH";
+	case 144:
+		return "Funai Electric Co., Ltd.";
+	case 145:
+		return "Advanced PANMOBIL Systems GmbH & Co. KG";
+	case 146:
+		return "ThinkOptics, Inc.";
+	case 147:
+		return "Universal Electronics, Inc.";
+	case 148:
+		return "Airoha Technology Corp.";
+	case 149:
+		return "NEC Lighting, Ltd.";
+	case 150:
+		return "ODM Technology, Inc.";
+	case 151:
+		return "ConnecteDevice Ltd.";
+	case 152:
+		return "zer01.tv GmbH";
+	case 153:
+		return "i.Tech Dynamic Global Distribution Ltd.";
+	case 154:
+		return "Alpwise";
+	case 155:
+		return "Jiangsu Toppower Automotive Electronics Co., Ltd.";
+	case 156:
+		return "Colorfy, Inc.";
+	case 157:
+		return "Geoforce Inc.";
+	case 158:
+		return "Bose Corporation";
+	case 159:
+		return "Suunto Oy";
+	case 160:
+		return "Kensington Computer Products Group";
+	case 161:
+		return "SR-Medizinelektronik";
+	case 162:
+		return "Vertu Corporation Limited";
+	case 163:
+		return "Meta Watch Ltd.";
+	case 164:
+		return "LINAK A/S";
+	case 165:
+		return "OTL Dynamics LLC";
+	case 166:
+		return "Panda Ocean Inc.";
+	case 167:
+		return "Visteon Corporation";
+	case 168:
+		return "ARP Devices Limited";
+	case 169:
+		return "Magneti Marelli S.p.A";
+	case 170:
+		return "CAEN RFID srl";
+	case 171:
+		return "Ingenieur-Systemgruppe Zahn GmbH";
+	case 172:
+		return "Green Throttle Games";
+	case 173:
+		return "Peter Systemtechnik GmbH";
+	case 174:
+		return "Omegawave Oy";
+	case 175:
+		return "Cinetix";
+	case 176:
+		return "Passif Semiconductor Corp";
+	case 177:
+		return "Saris Cycling Group, Inc";
+	case 178:
+		return "Bekey A/S";
+	case 179:
+		return "Clarinox Technologies Pty. Ltd.";
+	case 180:
+		return "BDE Technology Co., Ltd.";
+	case 181:
+		return "Swirl Networks";
+	case 182:
+		return "Meso international";
+	case 183:
+		return "TreLab Ltd";
+	case 184:
+		return "Qualcomm Innovation Center, Inc. (QuIC)";
+	case 185:
+		return "Johnson Controls, Inc.";
+	case 186:
+		return "Starkey Laboratories Inc.";
+	case 187:
+		return "S-Power Electronics Limited";
+	case 188:
+		return "Ace Sensor Inc";
+	case 189:
+		return "Aplix Corporation";
+	case 190:
+		return "AAMP of America";
+	case 191:
+		return "Stalmart Technology Limited";
+	case 192:
+		return "AMICCOM Electronics Corporation";
+	case 193:
+		return "Shenzhen Excelsecu Data Technology Co.,Ltd";
+	case 194:
+		return "Geneq Inc.";
+	case 195:
+		return "adidas AG";
+	case 196:
+		return "LG Electronics";
+	case 197:
+		return "Onset Computer Corporation";
+	case 198:
+		return "Selfly BV";
+	case 199:
+		return "Quuppa Oy.";
+	case 200:
+		return "GeLo Inc";
+	case 201:
+		return "Evluma";
+	case 202:
+		return "MC10";
+	case 203:
+		return "Binauric SE";
+	case 204:
+		return "Beats Electronics";
+	case 205:
+		return "Microchip Technology Inc.";
+	case 206:
+		return "Elgato Systems GmbH";
+	case 207:
+		return "ARCHOS SA";
+	case 208:
+		return "Dexcom, Inc.";
+	case 209:
+		return "Polar Electro Europe B.V.";
+	case 210:
+		return "Dialog Semiconductor B.V.";
+	case 211:
+		return "Taixingbang Technology (HK) Co,. LTD.";
+	case 212:
+		return "Kawantech";
+	case 213:
+		return "Austco Communication Systems";
+	case 214:
+		return "Timex Group USA, Inc.";
+	case 215:
+		return "Qualcomm Technologies, Inc.";
+	case 216:
+		return "Qualcomm Connected Experiences, Inc.";
+	case 217:
+		return "Voyetra Turtle Beach";
+	case 218:
+		return "txtr GmbH";
+	case 219:
+		return "Biosentronics";
+	case 220:
+		return "Procter & Gamble";
+	case 221:
+		return "Hosiden Corporation";
+	case 222:
+		return "Muzik LLC";
+	case 223:
+		return "Misfit Wearables Corp";
+	case 224:
+		return "Google";
+	case 225:
+		return "Danlers Ltd";
+	case 226:
+		return "Semilink Inc";
+	case 227:
+		return "inMusic Brands, Inc";
+	case 228:
+		return "L.S. Research Inc.";
+	case 229:
+		return "Eden Software Consultants Ltd.";
+	case 230:
+		return "Freshtemp";
+	case 231:
+		return "KS Technologies";
+	case 232:
+		return "ACTS Technologies";
+	case 233:
+		return "Vtrack Systems";
+	case 234:
+		return "Nielsen-Kellerman Company";
+	case 235:
+		return "Server Technology, Inc.";
+	case 236:
+		return "BioResearch Associates";
+	case 237:
+		return "Jolly Logic, LLC";
+	case 238:
+		return "Above Average Outcomes, Inc.";
+	case 239:
+		return "Bitsplitters GmbH";
+	case 240:
+		return "PayPal, Inc.";
+	case 241:
+		return "Witron Technology Limited";
+	case 242:
+		return "Aether Things Inc. (formerly Morse Project Inc.)";
+	case 243:
+		return "Kent Displays Inc.";
+	case 244:
+		return "Nautilus Inc.";
+	case 245:
+		return "Smartifier Oy";
+	case 246:
+		return "Elcometer Limited";
+	case 247:
+		return "VSN Technologies Inc.";
+	case 248:
+		return "AceUni Corp., Ltd.";
+	case 249:
+		return "StickNFind";
+	case 250:
+		return "Crystal Code AB";
+	case 251:
+		return "KOUKAAM a.s.";
+	case 252:
+		return "Delphi Corporation";
+	case 253:
+		return "ValenceTech Limited";
+	case 254:
+		return "Reserved";
+	case 255:
+		return "Typo Products, LLC";
+	case 256:
+		return "TomTom International BV";
+	case 257:
+		return "Fugoo, Inc";
+	case 258:
+		return "Keiser Corporation";
+	case 259:
+		return "Bang & Olufsen A/S";
+	case 260:
+		return "PLUS Locations Systems Pty Ltd";
+	case 261:
+		return "Ubiquitous Computing Technology Corporation";
+	case 262:
+		return "Innovative Yachtter Solutions";
+	case 263:
+		return "William Demant Holding A/S";
+	case 264:
+		return "Chicony Electronics Co., Ltd.";
+	case 265:
+		return "Atus BV";
+	case 266:
+		return "Codegate Ltd.";
+	case 267:
+		return "ERi, Inc.";
+	case 268:
+		return "Transducers Direct, LLC";
+	case 269:
+		return "Fujitsu Ten Limited";
+	case 270:
+		return "Audi AG";
+	case 271:
+		return "HiSilicon Technologies Co., Ltd.";
+	case 272:
+		return "Nippon Seiki Co., Ltd.";
+	case 273:
+		return "Steelseries ApS";
+	case 274:
+		return "Visybl Inc.";
+	case 275:
+		return "Openbrain Technologies, Co., Ltd.";
+	case 276:
+		return "Xensr";
+	case 277:
+		return "e.solutions";
+	case 278:
+		return "1OAK Technologies";
+	case 279:
+		return "Wimoto Technologies Inc";
+	case 280:
+		return "Radius Networks, Inc.";
+	case 281:
+		return "Wize Technology Co., Ltd.";
+	case 282:
+		return "Qualcomm Labs, Inc.";
+	case 283:
+		return "Aruba Networks";
+	case 284:
+		return "Baidu";
+	case 285:
+		return "Arendi AG";
+	case 286:
+		return "Skoda Auto a.s.";
+	case 287:
+		return "Volkswagon AG";
+	case 288:
+		return "Porsche AG";
+	case 289:
+		return "Sino Wealth Electronic Ltd.";
+	case 290:
+		return "AirTurn, Inc.";
+	case 291:
+		return "Kinsa, Inc.";
+	case 292:
+		return "HID Global";
+	case 293:
+		return "SEAT es";
+	case 294:
+		return "Promethean Ltd.";
+	case 295:
+		return "Salutica Allied Solutions";
+	case 296:
+		return "GPSI Group Pty Ltd";
+	case 297:
+		return "Nimble Devices Oy";
+	case 298:
+		return "Changzhou Yongse Infotech Co., Ltd";
+	case 299:
+		return "SportIQ";
+	case 300:
+		return "TEMEC Instruments B.V.";
+	case 301:
+		return "Sony Corporation";
+	case 302:
+		return "ASSA ABLOY";
+	case 303:
+		return "Clarion Co., Ltd.";
+	case 304:
+		return "Warehouse Innovations";
+	case 305:
+		return "Cypress Semiconductor Corporation";
+	case 306:
+		return "MADS Inc";
+	case 307:
+		return "Blue Maestro Limited";
+	case 308:
+		return "Resolution Products, Inc.";
+	case 309:
+		return "Airewear LLC";
+	case 310:
+		return "Seed Labs, Inc. (formerly ETC sp. z.o.o.)";
+	case 311:
+		return "Prestigio Plaza Ltd.";
+	case 312:
+		return "NTEO Inc.";
+	case 313:
+		return "Focus Systems Corporation";
+	case 314:
+		return "Tencent Holdings Limited";
+	case 315:
+		return "Allegion";
+	case 316:
+		return "Murata Manufacuring Co., Ltd.";
+	case 317:
+		return "WirelessWERX";
+	case 318:
+		return "Nod, Inc.";
+	case 319:
+		return "B&B Manufacturing Company";
+	case 320:
+		return "Alpine Electronics (China) Co., Ltd";
+	case 321:
+		return "FedEx Services";
+	case 322:
+		return "Grape Systems Inc.";
+	case 323:
+		return "Bkon Connect";
+	case 324:
+		return "Lintech GmbH";
+	case 325:
+		return "Novatel Wireless";
+	case 326:
+		return "Ciright";
+	case 327:
+		return "Mighty Cast, Inc.";
+	case 328:
+		return "Ambimat Electronics";
+	case 329:
+		return "Perytons Ltd.";
+	case 330:
+		return "Tivoli Audio, LLC";
+	case 331:
+		return "Master Lock";
+	case 332:
+		return "Mesh-Net Ltd";
+	case 333:
+		return "Huizhou Desay SV Automotive CO., LTD.";
+	case 334:
+		return "Tangerine, Inc.";
+	case 335:
+		return "B&W Group Ltd.";
+	case 336:
+		return "Pioneer Corporation";
+	case 337:
+		return "OnBeep";
+	case 338:
+		return "Vernier Software & Technology";
+	case 339:
+		return "ROL Ergo";
+	case 340:
+		return "Pebble Technology";
+	case 341:
+		return "NETATMO";
+	case 342:
+		return "Accumulate AB";
+	case 343:
+		return "Anhui Huami Information Technology Co., Ltd.";
+	case 344:
+		return "Inmite s.r.o.";
+	case 345:
+		return "ChefSteps, Inc.";
+	case 346:
+		return "micas AG";
+	case 347:
+		return "Biomedical Research Ltd.";
+	case 348:
+		return "Pitius Tec S.L.";
+	case 349:
+		return "Estimote, Inc.";
+	case 350:
+		return "Unikey Technologies, Inc.";
+	case 351:
+		return "Timer Cap Co.";
+	case 352:
+		return "AwoX";
+	case 353:
+		return "yikes";
+	case 354:
+		return "MADSGlobal NZ Ltd.";
+	case 355:
+		return "PCH International";
+	case 356:
+		return "Qingdao Yeelink Information Technology Co., Ltd.";
+	case 357:
+		return "Milwaukee Tool (formerly Milwaukee Electric Tools)";
+	case 358:
+		return "MISHIK Pte Ltd";
+	case 359:
+		return "Bayer HealthCare";
+	case 360:
+		return "Spicebox LLC";
+	case 361:
+		return "emberlight";
+	case 362:
+		return "Cooper-Atkins Corporation";
+	case 363:
+		return "Qblinks";
+	case 364:
+		return "MYSPHERA";
+	case 365:
+		return "LifeScan Inc";
+	case 366:
+		return "Volantic AB";
+	case 367:
+		return "Podo Labs, Inc";
+	case 368:
+		return "Roche Diabetes Care AG";
+	case 369:
+		return "Amazon Fulfillment Service";
+	case 370:
+		return "Connovate Technology Private Limited";
+	case 371:
+		return "Kocomojo, LLC";
+	case 372:
+		return "Everykey LLC";
+	case 373:
+		return "Dynamic Controls";
+	case 374:
+		return "SentriLock";
+	case 375:
+		return "I-SYST inc.";
+	case 376:
+		return "CASIO COMPUTER CO., LTD.";
+	case 377:
+		return "LAPIS Semiconductor Co., Ltd.";
+	case 378:
+		return "Telemonitor, Inc.";
+	case 379:
+		return "taskit GmbH";
+	case 380:
+		return "Daimler AG";
+	case 381:
+		return "BatAndCat";
+	case 382:
+		return "BluDotz Ltd";
+	case 383:
+		return "XTel ApS";
+	case 384:
+		return "Gigaset Communications GmbH";
+	case 385:
+		return "Gecko Health Innovations, Inc.";
+	case 386:
+		return "HOP Ubiquitous";
+	case 387:
+		return "To Be Assigned";
+	case 388:
+		return "Nectar";
+	case 389:
+		return "bel'apps LLC";
+	case 390:
+		return "CORE Lighting Ltd";
+	case 391:
+		return "Seraphim Sense Ltd";
+	case 392:
+		return "Unico RBC";
+	case 393:
+		return "Physical Enterprises Inc.";
+	case 394:
+		return "Able Trend Technology Limited";
+	case 395:
+		return "Konica Minolta, Inc.";
+	case 396:
+		return "Wilo SE";
+	case 397:
+		return "Extron Design Services";
+	case 398:
+		return "Fitbit, Inc.";
+	case 399:
+		return "Fireflies Systems";
+	case 400:
+		return "Intelletto Technologies Inc.";
+	case 401:
+		return "FDK CORPORATION";
+	case 402:
+		return "Cloudleaf, Inc";
+	case 403:
+		return "Maveric Automation LLC";
+	case 404:
+		return "Acoustic Stream Corporation";
+	case 405:
+		return "Zuli";
+	case 406:
+		return "Paxton Access Ltd";
+	case 407:
+		return "WiSilica Inc";
+	case 408:
+		return "Vengit Limited";
+	case 409:
+		return "SALTO SYSTEMS S.L.";
+	case 410:
+		return "TRON Forum (formerly T-Engine Forum)";
+	case 411:
+		return "CUBETECH s.r.o.";
+	case 412:
+		return "Cokiya Incorporated";
+	case 413:
+		return "CVS Health";
+	case 414:
+		return "Ceruus";
+	case 415:
+		return "Strainstall Ltd";
+	case 416:
+		return "Channel Enterprises (HK) Ltd.";
+	case 417:
+		return "FIAMM";
+	case 418:
+		return "GIGALANE.CO.,LTD";
+	case 419:
+		return "EROAD";
+	case 420:
+		return "Mine Safety Appliances";
+	case 421:
+		return "Icon Health and Fitness";
+	case 422:
+		return "Asandoo GmbH";
+	case 423:
+		return "ENERGOUS CORPORATION";
+	case 424:
+		return "Taobao";
+	case 425:
+		return "Canon Inc.";
+	case 426:
+		return "Geophysical Technology Inc.";
+	case 427:
+		return "Facebook, Inc.";
+	case 428:
+		return "Nipro Diagnostics, Inc.";
+	case 429:
+		return "FlightSafety International";
+	case 430:
+		return "Earlens Corporation";
+	case 431:
+		return "Sunrise Micro Devices, Inc.";
+	case 432:
+		return "Star Micronics Co., Ltd.";
+	case 433:
+		return "Netizens Sp. z o.o.";
+	case 434:
+		return "Nymi Inc.";
+	case 435:
+		return "Nytec, Inc.";
+	case 436:
+		return "Trineo Sp. z o.o.";
+	case 437:
+		return "Nest Labs Inc.";
+	case 438:
+		return "LM Technologies Ltd";
+	case 439:
+		return "General Electric Company";
+	case 440:
+		return "i+D3 S.L.";
+	case 441:
+		return "HANA Micron";
+	case 442:
+		return "Stages Cycling LLC";
+	case 443:
+		return "Cochlear Bone Anchored Solutions AB";
+	case 444:
+		return "SenionLab AB";
+	case 445:
+		return "Syszone Co., Ltd";
+	case 446:
+		return "Pulsate Mobile Ltd.";
+	case 447:
+		return "Hong Kong HunterSun Electronic Limited";
+	case 448:
+		return "pironex GmbH";
+	case 449:
+		return "BRADATECH Corp.";
+	case 450:
+		return "Transenergooil AG";
+	case 451:
+		return "Bunch";
+	case 452:
+		return "DME Microelectronics";
+	case 453:
+		return "Bitcraze AB";
+	case 454:
+		return "HASWARE Inc.";
+	case 455:
+		return "Abiogenix Inc.";
+	case 456:
+		return "Poly-Control ApS";
+	case 457:
+		return "Avi-on";
+	case 458:
+		return "Laerdal Medical AS";
+	case 459:
+		return "Fetch My Pet";
+	case 460:
+		return "Sam Labs Ltd.";
+	case 461:
+		return "Chengdu Synwing Technology Ltd";
+	case 462:
+		return "HOUWA SYSTEM DESIGN, k.k.";
+	case 463:
+		return "BSH";
+	case 464:
+		return "Primus Inter Pares Ltd";
+	case 465:
+		return "August";
+	case 466:
+		return "Gill Electronics";
+	case 467:
+		return "Sky Wave Design";
+	case 468:
+		return "Newlab S.r.l.";
+	case 469:
+		return "ELAD srl";
+	case 470:
+		return "G-wearables inc.";
+	case 471:
+		return "Squadrone Systems Inc.";
+	case 472:
+		return "Code Corporation";
+	case 473:
+		return "Savant Systems LLC";
+	case 474:
+		return "Logitech International SA";
+	case 475:
+		return "Innblue Consulting";
+	case 476:
+		return "iParking Ltd.";
+	case 477:
+		return "Koninklijke Philips Electronics N.V.";
+	case 478:
+		return "Minelab Electronics Pty Limited";
+	case 479:
+		return "Bison Group Ltd.";
+	case 480:
+		return "Widex A/S";
+	case 481:
+		return "Jolla Ltd";
+	case 482:
+		return "Lectronix, Inc.";
+	case 483:
+		return "Caterpillar Inc";
+	case 484:
+		return "Freedom Innovations";
+	case 485:
+		return "Dynamic Devices Ltd";
+	case 486:
+		return "Technology Solutions (UK) Ltd";
+	case 487:
+		return "IPS Group Inc.";
+	case 488:
+		return "STIR";
+	case 489:
+		return "Sano, Inc";
+	case 490:
+		return "Advanced Application Design, Inc.";
+	case 491:
+		return "AutoMap LLC";
+	case 492:
+		return "Spreadtrum Communications Shanghai Ltd";
+	case 493:
+		return "CuteCircuit LTD";
+	case 494:
+		return "Valeo Service";
+	case 495:
+		return "Fullpower Technologies, Inc.";
+	case 496:
+		return "KloudNation";
+	case 497:
+		return "Zebra Technologies Corporation";
+	case 498:
+		return "Itron, Inc.";
+	case 499:
+		return "The University of Tokyo";
+	case 500:
+		return "UTC Fire and Security";
+	case 501:
+		return "Cool Webthings Limited";
+	case 502:
+		return "DJO Global";
+	case 503:
+		return "Gelliner Limited";
+	case 504:
+		return "Anyka (Guangzhou) Microelectronics Technology Co, LTD";
+	case 505:
+		return "Medtronic, Inc.";
+	case 506:
+		return "Gozio, Inc.";
+	case 507:
+		return "Form Lifting, LLC";
+	case 508:
+		return "Wahoo Fitness, LLC";
+	case 509:
+		return "Kontakt Micro-Location Sp. z o.o.";
+	case 510:
+		return "Radio System Corporation";
+	case 511:
+		return "Freescale Semiconductor, Inc.";
+	case 512:
+		return "Verifone Systems PTe Ltd. Taiwan Branch";
+	case 513:
+		return "AR Timing";
+	case 514:
+		return "Rigado LLC";
+	case 515:
+		return "Kemppi Oy";
+	case 516:
+		return "Tapcentive Inc.";
+	case 517:
+		return "Smartbotics Inc.";
+	case 518:
+		return "Otter Products, LLC";
+	case 519:
+		return "STEMP Inc.";
+	case 520:
+		return "LumiGeek LLC";
+	case 521:
+		return "InvisionHeart Inc.";
+	case 522:
+		return "Macnica Inc.";
+	case 523:
+		return "Jaguar Land Rover Limited";
+	case 524:
+		return "CoroWare Technologies, Inc";
+	case 525:
+		return "Simplo Technology Co., LTD";
+	case 526:
+		return "Omron Healthcare Co., LTD";
+	case 527:
+		return "Comodule GMBH";
+	case 528:
+		return "ikeGPS";
+	case 529:
+		return "Telink Semiconductor Co. Ltd";
+	case 530:
+		return "Interplan Co., Ltd";
+	case 531:
+		return "Wyler AG";
+	case 532:
+		return "IK Multimedia Production srl";
+	case 533:
+		return "Lukoton Experience Oy";
+	case 534:
+		return "MTI Ltd";
+	case 535:
+		return "Tech4home, Lda";
+	case 536:
+		return "Hiotech AB";
+	case 537:
+		return "DOTT Limited";
+	case 538:
+		return "Blue Speck Labs, LLC";
+	case 539:
+		return "Cisco Systems Inc";
+	case 540:
+		return "Mobicomm Inc";
+	case 541:
+		return "Edamic";
+	case 542:
+		return "Goodnet Ltd";
+	case 543:
+		return "Luster Leaf Products Inc";
+	case 544:
+		return "Manus Machina BV";
+	case 545:
+		return "Mobiquity Networks Inc";
+	case 546:
+		return "Praxis Dynamics";
+	case 547:
+		return "Philip Morris Products S.A.";
+	case 548:
+		return "Comarch SA";
+	case 549:
+		return "Nestlé Nespresso S.A.";
+	case 550:
+		return "Merlinia A/S";
+	case 551:
+		return "LifeBEAM Technologies";
+	case 552:
+		return "Twocanoes Labs, LLC";
+	case 553:
+		return "Muoverti Limited";
+	case 554:
+		return "Stamer Musikanlagen GMBH";
+	case 555:
+		return "Tesla Motors";
+	case 556:
+		return "Pharynks Corporation";
+	case 557:
+		return "Lupine";
+	case 558:
+		return "Siemens AG";
+	case 559:
+		return "Huami (Shanghai) Culture Communication CO., LTD";
+	case 560:
+		return "Foster Electric Company, Ltd";
+	case 561:
+		return "ETA SA";
+	case 562:
+		return "x-Senso Solutions Kft";
+	case 563:
+		return "Shenzhen SuLong Communication Ltd";
+	case 564:
+		return "FengFan (BeiJing) Technology Co, Ltd";
+	case 565:
+		return "Qrio Inc";
+	case 566:
+		return "Pitpatpet Ltd";
+	case 567:
+		return "MSHeli s.r.l.";
+	case 568:
+		return "Trakm8 Ltd";
+	case 569:
+		return "JIN CO, Ltd";
+	case 570:
+		return "Alatech Technology";
+	case 571:
+		return "Beijing CarePulse Electronic Technology Co, Ltd";
+	case 572:
+		return "Awarepoint";
+	case 573:
+		return "ViCentra B.V.";
+	case 574:
+		return "Raven Industries";
+	case 575:
+		return "WaveWare Technologies";
+	case 576:
+		return "Argenox Technologies";
+	case 577:
+		return "Bragi GmbH";
+	case 578:
+		return "16Lab Inc";
+	case 579:
+		return "Masimo Corp";
+	case 580:
+		return "Iotera Inc.";
+	case 581:
+		return "Endress+Hauser";
+	case 582:
+		return "ACKme Networks, Inc.";
+	case 583:
+		return "FiftyThree Inc.";
+	case 584:
+		return "Parker Hannifin Corp";
+	case 585:
+		return "Transcranial Ltd";
+	case 586:
+		return "Uwatec AG";
+	case 587:
+		return "Orlan LLC";
+	case 588:
+		return "Blue Clover Devices";
+	case 589:
+		return "M-Way Solutions GmbH";
+	case 590:
+		return "Microtronics Engineering GmbH";
+	case 591:
+		return "Schneider Schreibgeräte GmbH";
+	case 592:
+		return "Sapphire Circuits LLC";
+	case 593:
+		return "Lumo Bodytech Inc.";
+	case 594:
+		return "UKC Technosolution";
+	case 595:
+		return "Xicato Inc.";
+	case 596:
+		return "Playbrush";
+	case 597:
+		return "Dai Nippon Printing Co., Ltd.";
+	case 598:
+		return "G24 Power Limited";
+	case 599:
+		return "AdBabble Local Commerce Inc.";
+	case 600:
+		return "Devialet SA";
+	case 601:
+		return "ALTYOR";
+	case 602:
+		return "University of Applied Sciences Valais/Haute Ecole Valaisanne";
+	case 603:
+		return "Five Interactive, LLC dba Zendo";
+	case 604:
+		return "NetEase (Hangzhou) Network co.Ltd.";
+	case 605:
+		return "Lexmark International Inc.";
+	case 606:
+		return "Fluke Corporation";
+	case 607:
+		return "Yardarm Technologies";
+	case 608:
+		return "SensaRx";
+	case 609:
+		return "SECVRE GmbH";
+	case 610:
+		return "Glacial Ridge Technologies";
+	case 611:
+		return "Identiv, Inc.";
+	case 612:
+		return "DDS, Inc.";
+	case 613:
+		return "SMK Corporation";
+	case 614:
+		return "Schawbel Technologies LLC";
+	case 615:
+		return "XMI Systems SA";
+	case 616:
+		return "Cerevo";
+	case 617:
+		return "Torrox GmbH & Co KG";
+	case 618:
+		return "Gemalto";
+	case 619:
+		return "DEKA Research & Development Corp.";
+	case 620:
+		return "Domster Tadeusz Szydlowski";
+	case 621:
+		return "Technogym SPA";
+	case 622:
+		return "FLEURBAEY BVBA";
+	case 623:
+		return "Aptcode Solutions";
+	case 624:
+		return "LSI ADL Technology";
+	case 625:
+		return "Animas Corp";
+	case 626:
+		return "Alps Electric Co., Ltd.";
+	case 627:
+		return "OCEASOFT";
+	case 628:
+		return "Motsai Research";
+	case 629:
+		return "Geotab";
+	case 630:
+		return "E.G.O. Elektro-Gerätebau GmbH";
+	case 631:
+		return "bewhere inc";
+	case 632:
+		return "Johnson Outdoors Inc";
+	case 633:
+		return "steute Schaltgerate GmbH & Co. KG";
+	case 634:
+		return "Ekomini inc.";
+	case 635:
+		return "DEFA AS";
+	case 636:
+		return "Aseptika Ltd";
+	case 637:
+		return "HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 )";
+	case 638:
+		return "HabitAware, LLC";
+	case 639:
+		return "ruwido austria gmbh";
+	case 640:
+		return "ITEC corporation";
+	case 641:
+		return "StoneL";
+	case 642:
+		return "Sonova AG";
+	case 643:
+		return "Maven Machines, Inc.";
+	case 644:
+		return "Synapse Electronics";
+	case 645:
+		return "Standard Innovation Inc.";
+	case 646:
+		return "RF Code, Inc.";
+	case 647:
+		return "Wally Ventures S.L.";
+	case 648:
+		return "Willowbank Electronics Ltd";
+	case 649:
+		return "SK Telecom";
+	case 650:
+		return "Jetro AS";
+	case 651:
+		return "Code Gears LTD";
+	case 652:
+		return "NANOLINK APS";
+	case 653:
+		return "IF, LLC";
+	case 654:
+		return "RF Digital Corp";
+	case 655:
+		return "Church & Dwight Co., Inc";
+	case 656:
+		return "Multibit Oy";
+	case 657:
+		return "CliniCloud Inc";
+	case 658:
+		return "SwiftSensors";
+	case 659:
+		return "Blue Bite";
+	case 660:
+		return "ELIAS GmbH";
+	case 661:
+		return "Sivantos GmbH";
+	case 662:
+		return "Petzl";
+	case 663:
+		return "storm power ltd";
+	case 664:
+		return "EISST Ltd";
+	case 665:
+		return "Inexess Technology Simma KG";
+	case 666:
+		return "Currant, Inc.";
+	case 667:
+		return "C2 Development, Inc.";
+	case 668:
+		return "Blue Sky Scientific, LLC";
+	case 669:
+		return "ALOTTAZS LABS, LLC";
+	case 670:
+		return "Kupson spol. s r.o.";
+	case 671:
+		return "Areus Engineering GmbH";
+	case 672:
+		return "Impossible Camera GmbH";
+	case 673:
+		return "InventureTrack Systems";
+	case 674:
+		return "LockedUp";
+	case 675:
+		return "Itude";
+	case 676:
+		return "Pacific Lock Company";
+	case 677:
+		return "Tendyron Corporation ( 天地融科技股份有限公司 )";
+	case 678:
+		return "Robert Bosch GmbH";
+	case 679:
+		return "Illuxtron international B.V.";
+	case 680:
+		return "miSport Ltd.";
+	case 681:
+		return "Chargelib";
+	case 682:
+		return "Doppler Lab";
+	case 683:
+		return "BBPOS Limited";
+	case 684:
+		return "RTB Elektronik GmbH & Co. KG";
+	case 685:
+		return "Rx Networks, Inc.";
+	case 686:
+		return "WeatherFlow, Inc.";
+	case 687:
+		return "Technicolor USA Inc.";
+	case 688:
+		return "Bestechnic(Shanghai),Ltd";
+	case 689:
+		return "Raden Inc";
+	case 690:
+		return "JouZen Oy";
+	case 691:
+		return "CLABER S.P.A.";
+	case 692:
+		return "Hyginex, Inc.";
+	case 693:
+		return "HANSHIN ELECTRIC RAILWAY CO.,LTD.";
+	case 694:
+		return "Schneider Electric";
+	case 695:
+		return "Oort Technologies LLC";
+	case 696:
+		return "Chrono Therapeutics";
+	case 697:
+		return "Rinnai Corporation";
+	case 698:
+		return "Swissprime Technologies AG";
+	case 699:
+		return "Koha.,Co.Ltd";
+	case 700:
+		return "Genevac Ltd";
+	case 701:
+		return "Chemtronics";
+	case 702:
+		return "Seguro Technology Sp. z o.o.";
+	case 703:
+		return "Redbird Flight Simulations";
+	case 704:
+		return "Dash Robotics";
+	case 705:
+		return "LINE Corporation";
+	case 706:
+		return "Guillemot Corporation";
+	case 707:
+		return "Techtronic Power Tools Technology Limited";
+	case 708:
+		return "Wilson Sporting Goods";
+	case 709:
+		return "Lenovo (Singapore) Pte Ltd. ( 联想(新加坡) )";
+	case 710:
+		return "Ayatan Sensors";
+	case 711:
+		return "Electronics Tomorrow Limited";
+	case 712:
+		return "VASCO Data Security International, Inc.";
+	case 713:
+		return "PayRange Inc.";
+	case 714:
+		return "ABOV Semiconductor";
+	case 715:
+		return "AINA-Wireless Inc.";
+	case 716:
+		return "Eijkelkamp Soil & Water";
+	case 717:
+		return "BMA ergonomics b.v.";
+	case 718:
+		return "Teva Branded Pharmaceutical Products R&D, Inc.";
+	case 719:
+		return "Anima";
+	case 720:
+		return "3M";
+	case 721:
+		return "Empatica Srl";
+	case 722:
+		return "Afero, Inc.";
+	case 723:
+		return "Powercast Corporation";
+	case 724:
+		return "Secuyou ApS";
+	case 725:
+		return "OMRON Corporation";
+	case 726:
+		return "Send Solutions";
+	case 727:
+		return "NIPPON SYSTEMWARE CO.,LTD.";
+	case 728:
+		return "Neosfar";
+	case 729:
+		return "Fliegl Agrartechnik GmbH";
+	case 730:
+		return "Gilvader";
+	case 731:
+		return "Digi International Inc (R)";
+	case 732:
+		return "DeWalch Technologies, Inc.";
+	case 733:
+		return "Flint Rehabilitation Devices, LLC";
+	case 734:
+		return "Samsung SDS Co., Ltd.";
+	case 735:
+		return "Blur Product Development";
+	case 736:
+		return "University of Michigan";
+	case 737:
+		return "Victron Energy BV";
+	case 738:
+		return "NTT docomo";
+	case 739:
+		return "Carmanah Technologies Corp.";
+	case 740:
+		return "Bytestorm Ltd.";
+	case 741:
+		return "Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )";
+	case 65535:
+		return "internal use";
+	default:
+		return "not assigned";
+	}
+}
diff --git a/bluez/repo/lib/bluetooth.h b/bluez/repo/lib/bluetooth.h
new file mode 100644
index 0000000..852a6b2
--- /dev/null
+++ b/bluez/repo/lib/bluetooth.h
@@ -0,0 +1,403 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __BLUETOOTH_H
+#define __BLUETOOTH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <netinet/in.h>
+
+#ifndef AF_BLUETOOTH
+#define AF_BLUETOOTH	31
+#define PF_BLUETOOTH	AF_BLUETOOTH
+#endif
+
+#define BTPROTO_L2CAP	0
+#define BTPROTO_HCI	1
+#define BTPROTO_SCO	2
+#define BTPROTO_RFCOMM	3
+#define BTPROTO_BNEP	4
+#define BTPROTO_CMTP	5
+#define BTPROTO_HIDP	6
+#define BTPROTO_AVDTP	7
+
+#define SOL_HCI		0
+#define SOL_L2CAP	6
+#define SOL_SCO		17
+#define SOL_RFCOMM	18
+
+#ifndef SOL_BLUETOOTH
+#define SOL_BLUETOOTH	274
+#endif
+
+#define BT_SECURITY	4
+struct bt_security {
+	uint8_t level;
+	uint8_t key_size;
+};
+#define BT_SECURITY_SDP		0
+#define BT_SECURITY_LOW		1
+#define BT_SECURITY_MEDIUM	2
+#define BT_SECURITY_HIGH	3
+
+#define BT_DEFER_SETUP	7
+
+#define BT_FLUSHABLE	8
+
+#define BT_FLUSHABLE_OFF	0
+#define BT_FLUSHABLE_ON		1
+
+#define BT_POWER		9
+struct bt_power {
+	uint8_t force_active;
+};
+#define BT_POWER_FORCE_ACTIVE_OFF 0
+#define BT_POWER_FORCE_ACTIVE_ON  1
+
+#define BT_CHANNEL_POLICY	10
+
+/* BR/EDR only (default policy)
+ *   AMP controllers cannot be used.
+ *   Channel move requests from the remote device are denied.
+ *   If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_BREDR_ONLY		0
+
+/* BR/EDR Preferred
+ *   Allow use of AMP controllers.
+ *   If the L2CAP channel is currently on AMP, move it to BR/EDR.
+ *   Channel move requests from the remote device are allowed.
+ */
+#define BT_CHANNEL_POLICY_BREDR_PREFERRED	1
+
+/* AMP Preferred
+ *   Allow use of AMP controllers
+ *   If the L2CAP channel is currently on BR/EDR and AMP controller
+ *     resources are available, initiate a channel move to AMP.
+ *   Channel move requests from the remote device are allowed.
+ *   If the L2CAP socket has not been connected yet, try to create
+ *     and configure the channel directly on an AMP controller rather
+ *     than BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_AMP_PREFERRED		2
+
+#define BT_VOICE		11
+struct bt_voice {
+	uint16_t setting;
+};
+
+#define BT_SNDMTU		12
+#define BT_RCVMTU		13
+
+#define BT_VOICE_TRANSPARENT			0x0003
+#define BT_VOICE_CVSD_16BIT			0x0060
+
+/* Connection and socket states */
+enum {
+	BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
+	BT_OPEN,
+	BT_BOUND,
+	BT_LISTEN,
+	BT_CONNECT,
+	BT_CONNECT2,
+	BT_CONFIG,
+	BT_DISCONN,
+	BT_CLOSED
+};
+
+/* Byte order conversions */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htobs(d)  (d)
+#define htobl(d)  (d)
+#define htobll(d) (d)
+#define btohs(d)  (d)
+#define btohl(d)  (d)
+#define btohll(d) (d)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define htobs(d)  bswap_16(d)
+#define htobl(d)  bswap_32(d)
+#define htobll(d) bswap_64(d)
+#define btohs(d)  bswap_16(d)
+#define btohl(d)  bswap_32(d)
+#define btohll(d) bswap_64(d)
+#else
+#error "Unknown byte order"
+#endif
+
+/* Bluetooth unaligned access */
+#define bt_get_unaligned(ptr)			\
+__extension__ ({				\
+	struct __attribute__((packed)) {	\
+		__typeof__(*(ptr)) __v;		\
+	} *__p = (__typeof__(__p)) (ptr);	\
+	__p->__v;				\
+})
+
+#define bt_put_unaligned(val, ptr)		\
+do {						\
+	struct __attribute__((packed)) {	\
+		__typeof__(*(ptr)) __v;		\
+	} *__p = (__typeof__(__p)) (ptr);	\
+	__p->__v = (val);			\
+} while(0)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint64_t bt_get_le64(const void *ptr)
+{
+	return bt_get_unaligned((const uint64_t *) ptr);
+}
+
+static inline uint64_t bt_get_be64(const void *ptr)
+{
+	return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint32_t bt_get_le32(const void *ptr)
+{
+	return bt_get_unaligned((const uint32_t *) ptr);
+}
+
+static inline uint32_t bt_get_be32(const void *ptr)
+{
+	return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint16_t bt_get_le16(const void *ptr)
+{
+	return bt_get_unaligned((const uint16_t *) ptr);
+}
+
+static inline uint16_t bt_get_be16(const void *ptr)
+{
+	return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
+}
+
+static inline void bt_put_le64(uint64_t val, const void *ptr)
+{
+	bt_put_unaligned(val, (uint64_t *) ptr);
+}
+
+static inline void bt_put_be64(uint64_t val, const void *ptr)
+{
+	bt_put_unaligned(bswap_64(val), (uint64_t *) ptr);
+}
+
+static inline void bt_put_le32(uint32_t val, const void *ptr)
+{
+	bt_put_unaligned(val, (uint32_t *) ptr);
+}
+
+static inline void bt_put_be32(uint32_t val, const void *ptr)
+{
+	bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
+}
+
+static inline void bt_put_le16(uint16_t val, const void *ptr)
+{
+	bt_put_unaligned(val, (uint16_t *) ptr);
+}
+
+static inline void bt_put_be16(uint16_t val, const void *ptr)
+{
+	bt_put_unaligned(bswap_16(val), (uint16_t *) ptr);
+}
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint64_t bt_get_le64(const void *ptr)
+{
+	return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint64_t bt_get_be64(const void *ptr)
+{
+	return bt_get_unaligned((const uint64_t *) ptr);
+}
+
+static inline uint32_t bt_get_le32(const void *ptr)
+{
+	return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint32_t bt_get_be32(const void *ptr)
+{
+	return bt_get_unaligned((const uint32_t *) ptr);
+}
+
+static inline uint16_t bt_get_le16(const void *ptr)
+{
+	return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint16_t bt_get_be16(const void *ptr)
+{
+	return bt_get_unaligned((const uint16_t *) ptr);
+}
+
+static inline void bt_put_le64(uint64_t val, const void *ptr)
+{
+	bt_put_unaligned(bswap_64(val), (uint64_t *) ptr);
+}
+
+static inline void bt_put_be64(uint64_t val, const void *ptr)
+{
+	bt_put_unaligned(val, (uint64_t *) ptr);
+}
+
+static inline void bt_put_le32(uint32_t val, const void *ptr)
+{
+	bt_put_unaligned(bswap_32(val), (uint32_t *) ptr);
+}
+
+static inline void bt_put_be32(uint32_t val, const void *ptr)
+{
+	bt_put_unaligned(val, (uint32_t *) ptr);
+}
+
+static inline void bt_put_le16(uint16_t val, const void *ptr)
+{
+	bt_put_unaligned(bswap_16(val), (uint16_t *) ptr);
+}
+
+static inline void bt_put_be16(uint16_t val, const void *ptr)
+{
+	bt_put_unaligned(val, (uint16_t *) ptr);
+}
+#else
+#error "Unknown byte order"
+#endif
+
+/* BD Address */
+typedef struct {
+	uint8_t b[6];
+} __attribute__((packed)) bdaddr_t;
+
+/* BD Address type */
+#define BDADDR_BREDR           0x00
+#define BDADDR_LE_PUBLIC       0x01
+#define BDADDR_LE_RANDOM       0x02
+
+#define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_ALL   (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
+
+/* Copy, swap, convert BD Address */
+static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
+{
+	return memcmp(ba1, ba2, sizeof(bdaddr_t));
+}
+static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src)
+{
+	memcpy(dst, src, sizeof(bdaddr_t));
+}
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src);
+bdaddr_t *strtoba(const char *str);
+char *batostr(const bdaddr_t *ba);
+int ba2str(const bdaddr_t *ba, char *str);
+int str2ba(const char *str, bdaddr_t *ba);
+int ba2oui(const bdaddr_t *ba, char *oui);
+int bachk(const char *str);
+
+int baprintf(const char *format, ...);
+int bafprintf(FILE *stream, const char *format, ...);
+int basprintf(char *str, const char *format, ...);
+int basnprintf(char *str, size_t size, const char *format, ...);
+
+void *bt_malloc(size_t size);
+void bt_free(void *ptr);
+
+int bt_error(uint16_t code);
+const char *bt_compidtostr(int id);
+
+typedef struct {
+	uint8_t data[16];
+} uint128_t;
+
+static inline void bswap_128(const void *src, void *dst)
+{
+	const uint8_t *s = (const uint8_t *) src;
+	uint8_t *d = (uint8_t *) dst;
+	int i;
+
+	for (i = 0; i < 16; i++)
+		d[15 - i] = s[i];
+}
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+#define ntoh64(x) (x)
+
+static inline void ntoh128(const uint128_t *src, uint128_t *dst)
+{
+	memcpy(dst, src, sizeof(uint128_t));
+}
+
+static inline void btoh128(const uint128_t *src, uint128_t *dst)
+{
+	bswap_128(src, dst);
+}
+
+#else
+
+static inline uint64_t ntoh64(uint64_t n)
+{
+	uint64_t h;
+	uint64_t tmp = ntohl(n & 0x00000000ffffffff);
+
+	h = ntohl(n >> 32);
+	h |= tmp << 32;
+
+	return h;
+}
+
+static inline void ntoh128(const uint128_t *src, uint128_t *dst)
+{
+	bswap_128(src, dst);
+}
+
+static inline void btoh128(const uint128_t *src, uint128_t *dst)
+{
+	memcpy(dst, src, sizeof(uint128_t));
+}
+
+#endif
+
+#define hton64(x)     ntoh64(x)
+#define hton128(x, y) ntoh128(x, y)
+#define htob128(x, y) btoh128(x, y)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLUETOOTH_H */
diff --git a/bluez/repo/lib/bluez.pc.in b/bluez/repo/lib/bluez.pc.in
new file mode 100644
index 0000000..3d6e596
--- /dev/null
+++ b/bluez/repo/lib/bluez.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+ 
+Name: BlueZ
+Description: Bluetooth protocol stack for Linux
+Version: @VERSION@
+Libs: -L${libdir} -lbluetooth
+Cflags: -I${includedir}
diff --git a/bluez/repo/lib/bnep.h b/bluez/repo/lib/bnep.h
new file mode 100644
index 0000000..e7c2c87
--- /dev/null
+++ b/bluez/repo/lib/bnep.h
@@ -0,0 +1,162 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __BNEP_H
+#define __BNEP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bluetooth/bluetooth.h>
+
+#ifndef ETH_ALEN
+#define ETH_ALEN	6		/* from <net/ethernet.h> */
+#endif
+
+/* BNEP UUIDs */
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16    0x02
+#define BNEP_UUID32    0x04
+#define BNEP_UUID128   0x16
+
+#define BNEP_SVC_PANU  0x1115
+#define BNEP_SVC_NAP   0x1116
+#define BNEP_SVC_GN    0x1117
+
+/* BNEP packet types */
+#define BNEP_GENERAL               0x00
+#define BNEP_CONTROL               0x01
+#define BNEP_COMPRESSED            0x02
+#define BNEP_COMPRESSED_SRC_ONLY   0x03
+#define BNEP_COMPRESSED_DST_ONLY   0x04
+
+/* BNEP control types */
+#define BNEP_CMD_NOT_UNDERSTOOD    0x00
+#define BNEP_SETUP_CONN_REQ        0x01
+#define BNEP_SETUP_CONN_RSP        0x02
+#define BNEP_FILTER_NET_TYPE_SET   0x03
+#define BNEP_FILTER_NET_TYPE_RSP   0x04
+#define BNEP_FILTER_MULT_ADDR_SET  0x05
+#define BNEP_FILTER_MULT_ADDR_RSP  0x06
+
+/* BNEP response messages */
+#define BNEP_SUCCESS               0x00
+
+#define BNEP_CONN_INVALID_DST      0x01
+#define BNEP_CONN_INVALID_SRC      0x02
+#define BNEP_CONN_INVALID_SVC      0x03
+#define BNEP_CONN_NOT_ALLOWED      0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ    0x01
+#define BNEP_FILTER_INVALID_RANGE      0x02
+#define BNEP_FILTER_INVALID_MCADDR     0x02
+#define BNEP_FILTER_LIMIT_REACHED      0x03
+#define BNEP_FILTER_DENIED_SECURITY    0x04
+
+/* L2CAP settings */
+#define BNEP_MTU         1691
+#define BNEP_FLUSH_TO    0xffff
+#define BNEP_CONNECT_TO  15
+#define BNEP_FILTER_TO   15
+
+#ifndef BNEP_PSM
+#define BNEP_PSM	 0x0f
+#endif
+
+/* BNEP headers */
+#define BNEP_TYPE_MASK	 0x7f
+#define BNEP_EXT_HEADER	 0x80
+
+struct bnep_setup_conn_req {
+	uint8_t  type;
+	uint8_t  ctrl;
+	uint8_t  uuid_size;
+	uint8_t  service[0];
+} __attribute__((packed));
+
+struct bnep_set_filter_req {
+	uint8_t  type;
+	uint8_t  ctrl;
+	uint16_t len;
+	uint8_t  list[0];
+} __attribute__((packed));
+
+struct bnep_ctrl_cmd_not_understood_cmd {
+	uint8_t type;
+	uint8_t ctrl;
+	uint8_t unkn_ctrl;
+} __attribute__((packed));
+
+struct bnep_control_rsp {
+	uint8_t  type;
+	uint8_t  ctrl;
+	uint16_t resp;
+} __attribute__((packed));
+
+struct bnep_ext_hdr {
+	uint8_t  type;
+	uint8_t  len;
+	uint8_t  data[0];
+} __attribute__((packed));
+
+/* BNEP ioctl defines */
+#define BNEPCONNADD	_IOW('B', 200, int)
+#define BNEPCONNDEL	_IOW('B', 201, int)
+#define BNEPGETCONNLIST	_IOR('B', 210, int)
+#define BNEPGETCONNINFO	_IOR('B', 211, int)
+#define BNEPGETSUPPFEAT	_IOR('B', 212, int)
+
+#define BNEP_SETUP_RESPONSE	0
+
+struct bnep_connadd_req {
+	int      sock;		/* Connected socket */
+	uint32_t flags;
+	uint16_t role;
+	char     device[16];	/* Name of the Ethernet device */
+};
+
+struct bnep_conndel_req {
+	uint32_t flags;
+	uint8_t  dst[ETH_ALEN];
+};
+
+struct bnep_conninfo {
+	uint32_t flags;
+	uint16_t role;
+	uint16_t state;
+	uint8_t  dst[ETH_ALEN];
+	char     device[16];
+};
+
+struct bnep_connlist_req {
+	uint32_t cnum;
+	struct bnep_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BNEP_H */
diff --git a/bluez/repo/lib/cmtp.h b/bluez/repo/lib/cmtp.h
new file mode 100644
index 0000000..ce937bd
--- /dev/null
+++ b/bluez/repo/lib/cmtp.h
@@ -0,0 +1,69 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __CMTP_H
+#define __CMTP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* CMTP defaults */
+#define CMTP_MINIMUM_MTU 152
+#define CMTP_DEFAULT_MTU 672
+
+/* CMTP ioctl defines */
+#define CMTPCONNADD	_IOW('C', 200, int)
+#define CMTPCONNDEL	_IOW('C', 201, int)
+#define CMTPGETCONNLIST	_IOR('C', 210, int)
+#define CMTPGETCONNINFO	_IOR('C', 211, int)
+
+#define CMTP_LOOPBACK	0
+
+struct cmtp_connadd_req {
+	int sock;	/* Connected socket */
+	uint32_t flags;
+};
+
+struct cmtp_conndel_req {
+	bdaddr_t bdaddr;
+	uint32_t flags;
+};
+
+struct cmtp_conninfo {
+	bdaddr_t bdaddr;
+	uint32_t flags;
+	uint16_t state;
+	int      num;
+};
+
+struct cmtp_connlist_req {
+	uint32_t cnum;
+	struct cmtp_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CMTP_H */
diff --git a/bluez/repo/lib/hci.c b/bluez/repo/lib/hci.c
new file mode 100644
index 0000000..c25be9e
--- /dev/null
+++ b/bluez/repo/lib/hci.c
@@ -0,0 +1,3115 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+#include "hci_lib.h"
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+typedef struct {
+	char *str;
+	unsigned int val;
+} hci_map;
+
+static char *hci_bit2str(hci_map *m, unsigned int val)
+{
+	char *str = malloc(120);
+	char *ptr = str;
+
+	if (!str)
+		return NULL;
+
+	*ptr = 0;
+	while (m->str) {
+		if ((unsigned int) m->val & val)
+			ptr += sprintf(ptr, "%s ", m->str);
+		m++;
+	}
+	return str;
+}
+
+static int hci_str2bit(hci_map *map, char *str, unsigned int *val)
+{
+	char *t, *ptr;
+	hci_map *m;
+	int set;
+
+	if (!str || !(str = ptr = strdup(str)))
+		return 0;
+
+	*val = set = 0;
+
+	while ((t = strsep(&ptr, ","))) {
+		for (m = map; m->str; m++) {
+			if (!strcasecmp(m->str, t)) {
+				*val |= (unsigned int) m->val;
+				set = 1;
+			}
+		}
+	}
+	free(str);
+
+	return set;
+}
+
+static char *hci_uint2str(hci_map *m, unsigned int val)
+{
+	char *str = malloc(50);
+	char *ptr = str;
+
+	if (!str)
+		return NULL;
+
+	*ptr = 0;
+	while (m->str) {
+		if ((unsigned int) m->val == val) {
+			ptr += sprintf(ptr, "%s", m->str);
+			break;
+		}
+		m++;
+	}
+	return str;
+}
+
+static int hci_str2uint(hci_map *map, char *str, unsigned int *val)
+{
+	char *t, *ptr;
+	hci_map *m;
+	int set = 0;
+
+	if (!str)
+		return 0;
+
+	str = ptr = strdup(str);
+
+	while ((t = strsep(&ptr, ","))) {
+		for (m = map; m->str; m++) {
+			if (!strcasecmp(m->str,t)) {
+				*val = (unsigned int) m->val;
+				set = 1;
+				break;
+			}
+		}
+	}
+	free(str);
+
+	return set;
+}
+
+char *hci_bustostr(int bus)
+{
+	switch (bus) {
+	case HCI_VIRTUAL:
+		return "VIRTUAL";
+	case HCI_USB:
+		return "USB";
+	case HCI_PCCARD:
+		return "PCCARD";
+	case HCI_UART:
+		return "UART";
+	case HCI_RS232:
+		return "RS232";
+	case HCI_PCI:
+		return "PCI";
+	case HCI_SDIO:
+		return "SDIO";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+char *hci_dtypetostr(int type)
+{
+	return hci_bustostr(type & 0x0f);
+}
+
+char *hci_typetostr(int type)
+{
+	switch (type) {
+	case HCI_BREDR:
+		return "BR/EDR";
+	case HCI_AMP:
+		return "AMP";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+/* HCI dev flags mapping */
+static hci_map dev_flags_map[] = {
+	{ "UP",      HCI_UP      },
+	{ "INIT",    HCI_INIT    },
+	{ "RUNNING", HCI_RUNNING },
+	{ "RAW",     HCI_RAW     },
+	{ "PSCAN",   HCI_PSCAN   },
+	{ "ISCAN",   HCI_ISCAN   },
+	{ "INQUIRY", HCI_INQUIRY },
+	{ "AUTH",    HCI_AUTH    },
+	{ "ENCRYPT", HCI_ENCRYPT },
+	{ NULL }
+};
+
+char *hci_dflagstostr(uint32_t flags)
+{
+	char *str = bt_malloc(50);
+	char *ptr = str;
+	hci_map *m = dev_flags_map;
+
+	if (!str)
+		return NULL;
+
+	*ptr = 0;
+
+	if (!hci_test_bit(HCI_UP, &flags))
+		ptr += sprintf(ptr, "DOWN ");
+
+	while (m->str) {
+		if (hci_test_bit(m->val, &flags))
+			ptr += sprintf(ptr, "%s ", m->str);
+		m++;
+	}
+	return str;
+}
+
+/* HCI packet type mapping */
+static hci_map pkt_type_map[] = {
+	{ "DM1",   HCI_DM1  },
+	{ "DM3",   HCI_DM3  },
+	{ "DM5",   HCI_DM5  },
+	{ "DH1",   HCI_DH1  },
+	{ "DH3",   HCI_DH3  },
+	{ "DH5",   HCI_DH5  },
+	{ "HV1",   HCI_HV1  },
+	{ "HV2",   HCI_HV2  },
+	{ "HV3",   HCI_HV3  },
+	{ "2-DH1", HCI_2DH1 },
+	{ "2-DH3", HCI_2DH3 },
+	{ "2-DH5", HCI_2DH5 },
+	{ "3-DH1", HCI_3DH1 },
+	{ "3-DH3", HCI_3DH3 },
+	{ "3-DH5", HCI_3DH5 },
+	{ NULL }
+};
+
+static hci_map sco_ptype_map[] = {
+	{ "HV1",   0x0001   },
+	{ "HV2",   0x0002   },
+	{ "HV3",   0x0004   },
+	{ "EV3",   HCI_EV3  },
+	{ "EV4",   HCI_EV4  },
+	{ "EV5",   HCI_EV5  },
+	{ "2-EV3", HCI_2EV3 },
+	{ "2-EV5", HCI_2EV5 },
+	{ "3-EV3", HCI_3EV3 },
+	{ "3-EV5", HCI_3EV5 },
+	{ NULL }
+};
+
+char *hci_ptypetostr(unsigned int ptype)
+{
+	return hci_bit2str(pkt_type_map, ptype);
+}
+
+int hci_strtoptype(char *str, unsigned int *val)
+{
+	return hci_str2bit(pkt_type_map, str, val);
+}
+
+char *hci_scoptypetostr(unsigned int ptype)
+{
+	return hci_bit2str(sco_ptype_map, ptype);
+}
+
+int hci_strtoscoptype(char *str, unsigned int *val)
+{
+	return hci_str2bit(sco_ptype_map, str, val);
+}
+
+/* Link policy mapping */
+static hci_map link_policy_map[] = {
+	{ "NONE",	0		},
+	{ "RSWITCH",	HCI_LP_RSWITCH	},
+	{ "HOLD",	HCI_LP_HOLD	},
+	{ "SNIFF",	HCI_LP_SNIFF	},
+	{ "PARK",	HCI_LP_PARK	},
+	{ NULL }
+};
+
+char *hci_lptostr(unsigned int lp)
+{
+	return hci_bit2str(link_policy_map, lp);
+}
+
+int hci_strtolp(char *str, unsigned int *val)
+{
+	return hci_str2bit(link_policy_map, str, val);
+}
+
+/* Link mode mapping */
+static hci_map link_mode_map[] = {
+	{ "NONE",	0		},
+	{ "ACCEPT",	HCI_LM_ACCEPT	},
+	{ "MASTER",	HCI_LM_MASTER	},
+	{ "AUTH",	HCI_LM_AUTH	},
+	{ "ENCRYPT",	HCI_LM_ENCRYPT	},
+	{ "TRUSTED",	HCI_LM_TRUSTED	},
+	{ "RELIABLE",	HCI_LM_RELIABLE	},
+	{ "SECURE",	HCI_LM_SECURE	},
+	{ NULL }
+};
+
+char *hci_lmtostr(unsigned int lm)
+{
+	char *s, *str = bt_malloc(50);
+	if (!str)
+		return NULL;
+
+	*str = 0;
+	if (!(lm & HCI_LM_MASTER))
+		strcpy(str, "SLAVE ");
+
+	s = hci_bit2str(link_mode_map, lm);
+	if (!s) {
+		bt_free(str);
+		return NULL;
+	}
+
+	strcat(str, s);
+	free(s);
+	return str;
+}
+
+int hci_strtolm(char *str, unsigned int *val)
+{
+	return hci_str2bit(link_mode_map, str, val);
+}
+
+/* Command mapping */
+static hci_map commands_map[] = {
+	{ "Inquiry",					0   },
+	{ "Inquiry Cancel",				1   },
+	{ "Periodic Inquiry Mode",			2   },
+	{ "Exit Periodic Inquiry Mode",			3   },
+	{ "Create Connection",				4   },
+	{ "Disconnect",					5   },
+	{ "Add SCO Connection",				6   },
+	{ "Cancel Create Connection",			7   },
+
+	{ "Accept Connection Request",			8   },
+	{ "Reject Connection Request",			9   },
+	{ "Link Key Request Reply",			10  },
+	{ "Link Key Request Negative Reply",		11  },
+	{ "PIN Code Request Reply",			12  },
+	{ "PIN Code Request Negative Reply",		13  },
+	{ "Change Connection Packet Type",		14  },
+	{ "Authentication Requested",			15  },
+
+	{ "Set Connection Encryption",			16  },
+	{ "Change Connection Link Key",			17  },
+	{ "Master Link Key",				18  },
+	{ "Remote Name Request",			19  },
+	{ "Cancel Remote Name Request",			20  },
+	{ "Read Remote Supported Features",		21  },
+	{ "Read Remote Extended Features",		22  },
+	{ "Read Remote Version Information",		23  },
+
+	{ "Read Clock Offset",				24  },
+	{ "Read LMP Handle",				25  },
+	{ "Reserved",					26  },
+	{ "Reserved",					27  },
+	{ "Reserved",					28  },
+	{ "Reserved",					29  },
+	{ "Reserved",					30  },
+	{ "Reserved",					31  },
+
+	{ "Reserved",					32  },
+	{ "Hold Mode",					33  },
+	{ "Sniff Mode",					34  },
+	{ "Exit Sniff Mode",				35  },
+	{ "Park State",					36  },
+	{ "Exit Park State",				37  },
+	{ "QoS Setup",					38  },
+	{ "Role Discovery",				39  },
+
+	{ "Switch Role",				40  },
+	{ "Read Link Policy Settings",			41  },
+	{ "Write Link Policy Settings",			42  },
+	{ "Read Default Link Policy Settings",		43  },
+	{ "Write Default Link Policy Settings",		44  },
+	{ "Flow Specification",				45  },
+	{ "Set Event Mask",				46  },
+	{ "Reset",					47  },
+
+	{ "Set Event Filter",				48  },
+	{ "Flush",					49  },
+	{ "Read PIN Type",				50  },
+	{ "Write PIN Type",				51  },
+	{ "Create New Unit Key",			52  },
+	{ "Read Stored Link Key",			53  },
+	{ "Write Stored Link Key",			54  },
+	{ "Delete Stored Link Key",			55  },
+
+	{ "Write Local Name",				56  },
+	{ "Read Local Name",				57  },
+	{ "Read Connection Accept Timeout",		58  },
+	{ "Write Connection Accept Timeout",		59  },
+	{ "Read Page Timeout",				60  },
+	{ "Write Page Timeout",				61  },
+	{ "Read Scan Enable",				62  },
+	{ "Write Scan Enable",				63  },
+
+	{ "Read Page Scan Activity",			64  },
+	{ "Write Page Scan Activity",			65  },
+	{ "Read Inquiry Scan Activity",			66  },
+	{ "Write Inquiry Scan Activity",		67  },
+	{ "Read Authentication Enable",			68  },
+	{ "Write Authentication Enable",		69  },
+	{ "Read Encryption Mode",			70  },
+	{ "Write Encryption Mode",			71  },
+
+	{ "Read Class Of Device",			72  },
+	{ "Write Class Of Device",			73  },
+	{ "Read Voice Setting",				74  },
+	{ "Write Voice Setting",			75  },
+	{ "Read Automatic Flush Timeout",		76  },
+	{ "Write Automatic Flush Timeout",		77  },
+	{ "Read Num Broadcast Retransmissions",		78  },
+	{ "Write Num Broadcast Retransmissions",	79  },
+
+	{ "Read Hold Mode Activity",			80  },
+	{ "Write Hold Mode Activity",			81  },
+	{ "Read Transmit Power Level",			82  },
+	{ "Read Synchronous Flow Control Enable",	83  },
+	{ "Write Synchronous Flow Control Enable",	84  },
+	{ "Set Host Controller To Host Flow Control",	85  },
+	{ "Host Buffer Size",				86  },
+	{ "Host Number Of Completed Packets",		87  },
+
+	{ "Read Link Supervision Timeout",		88  },
+	{ "Write Link Supervision Timeout",		89  },
+	{ "Read Number of Supported IAC",		90  },
+	{ "Read Current IAC LAP",			91  },
+	{ "Write Current IAC LAP",			92  },
+	{ "Read Page Scan Period Mode",			93  },
+	{ "Write Page Scan Period Mode",		94  },
+	{ "Read Page Scan Mode",			95  },
+
+	{ "Write Page Scan Mode",			96  },
+	{ "Set AFH Channel Classification",		97  },
+	{ "Reserved",					98  },
+	{ "Reserved",					99  },
+	{ "Read Inquiry Scan Type",			100 },
+	{ "Write Inquiry Scan Type",			101 },
+	{ "Read Inquiry Mode",				102 },
+	{ "Write Inquiry Mode",				103 },
+
+	{ "Read Page Scan Type",			104 },
+	{ "Write Page Scan Type",			105 },
+	{ "Read AFH Channel Assessment Mode",		106 },
+	{ "Write AFH Channel Assessment Mode",		107 },
+	{ "Reserved",					108 },
+	{ "Reserved",					109 },
+	{ "Reserved",					110 },
+	{ "Reserved",					111 },
+
+	{ "Reserved",					112 },
+	{ "Reserved",					113 },
+	{ "Reserved",					114 },
+	{ "Read Local Version Information",		115 },
+	{ "Read Local Supported Commands",		116 },
+	{ "Read Local Supported Features",		117 },
+	{ "Read Local Extended Features",		118 },
+	{ "Read Buffer Size",				119 },
+
+	{ "Read Country Code",				120 },
+	{ "Read BD ADDR",				121 },
+	{ "Read Failed Contact Counter",		122 },
+	{ "Reset Failed Contact Counter",		123 },
+	{ "Get Link Quality",				124 },
+	{ "Read RSSI",					125 },
+	{ "Read AFH Channel Map",			126 },
+	{ "Read BD Clock",				127 },
+
+	{ "Read Loopback Mode",				128 },
+	{ "Write Loopback Mode",			129 },
+	{ "Enable Device Under Test Mode",		130 },
+	{ "Setup Synchronous Connection",		131 },
+	{ "Accept Synchronous Connection",		132 },
+	{ "Reject Synchronous Connection",		133 },
+	{ "Reserved",					134 },
+	{ "Reserved",					135 },
+
+	{ "Read Extended Inquiry Response",		136 },
+	{ "Write Extended Inquiry Response",		137 },
+	{ "Refresh Encryption Key",			138 },
+	{ "Reserved",					139 },
+	{ "Sniff Subrating",				140 },
+	{ "Read Simple Pairing Mode",			141 },
+	{ "Write Simple Pairing Mode",			142 },
+	{ "Read Local OOB Data",			143 },
+
+	{ "Read Inquiry Response Transmit Power Level",	144 },
+	{ "Write Inquiry Transmit Power Level",		145 },
+	{ "Read Default Erroneous Data Reporting",	146 },
+	{ "Write Default Erroneous Data Reporting",	147 },
+	{ "Reserved",					148 },
+	{ "Reserved",					149 },
+	{ "Reserved",					150 },
+	{ "IO Capability Request Reply",		151 },
+
+	{ "User Confirmation Request Reply",		152 },
+	{ "User Confirmation Request Negative Reply",	153 },
+	{ "User Passkey Request Reply",			154 },
+	{ "User Passkey Request Negative Reply",	155 },
+	{ "Remote OOB Data Request Reply",		156 },
+	{ "Write Simple Pairing Debug Mode",		157 },
+	{ "Enhanced Flush",				158 },
+	{ "Remote OOB Data Request Negative Reply",	159 },
+
+	{ "Reserved",					160 },
+	{ "Reserved",					161 },
+	{ "Send Keypress Notification",			162 },
+	{ "IO Capability Request Negative Reply",	163 },
+	{ "Read Encryption Key Size",			164 },
+	{ "Reserved",					165 },
+	{ "Reserved",					166 },
+	{ "Reserved",					167 },
+
+	{ "Create Physical Link",			168 },
+	{ "Accept Physical Link",			169 },
+	{ "Disconnect Physical Link",			170 },
+	{ "Create Logical Link",			171 },
+	{ "Accept Logical Link",			172 },
+	{ "Disconnect Logical Link",			173 },
+	{ "Logical Link Cancel",			174 },
+	{ "Flow Specification Modify",			175 },
+
+	{ "Read Logical Link Accept Timeout",		176 },
+	{ "Write Logical Link Accept Timeout",		177 },
+	{ "Set Event Mask Page 2",			178 },
+	{ "Read Location Data",				179 },
+	{ "Write Location Data",			180 },
+	{ "Read Local AMP Info",			181 },
+	{ "Read Local AMP_ASSOC",			182 },
+	{ "Write Remote AMP_ASSOC",			183 },
+
+	{ "Read Flow Control Mode",			184 },
+	{ "Write Flow Control Mode",			185 },
+	{ "Read Data Block Size",			186 },
+	{ "Reserved",					187 },
+	{ "Reserved",					188 },
+	{ "Enable AMP Receiver Reports",		189 },
+	{ "AMP Test End",				190 },
+	{ "AMP Test Command",				191 },
+
+	{ "Read Enhanced Transmit Power Level",		192 },
+	{ "Reserved",					193 },
+	{ "Read Best Effort Flush Timeout",		194 },
+	{ "Write Best Effort Flush Timeout",		195 },
+	{ "Short Range Mode",				196 },
+	{ "Read LE Host Support",			197 },
+	{ "Write LE Host Support",			198 },
+	{ "Reserved",					199 },
+
+	{ "LE Set Event Mask",				200 },
+	{ "LE Read Buffer Size",			201 },
+	{ "LE Read Local Supported Features",		202 },
+	{ "Reserved",					203 },
+	{ "LE Set Random Address",			204 },
+	{ "LE Set Advertising Parameters",		205 },
+	{ "LE Read Advertising Channel TX Power",	206 },
+	{ "LE Set Advertising Data",			207 },
+
+	{ "LE Set Scan Response Data",			208 },
+	{ "LE Set Advertise Enable",			209 },
+	{ "LE Set Scan Parameters",			210 },
+	{ "LE Set Scan Enable",				211 },
+	{ "LE Create Connection",			212 },
+	{ "LE Create Connection Cancel",		213 },
+	{ "LE Read White List Size",			214 },
+	{ "LE Clear White List",			215 },
+
+	{ "LE Add Device To White List",		216 },
+	{ "LE Remove Device From White List",		217 },
+	{ "LE Connection Update",			218 },
+	{ "LE Set Host Channel Classification",		219 },
+	{ "LE Read Channel Map",			220 },
+	{ "LE Read Remote Used Features",		221 },
+	{ "LE Encrypt",					222 },
+	{ "LE Rand",					223 },
+
+	{ "LE Start Encryption",			224 },
+	{ "LE Long Term Key Request Reply",		225 },
+	{ "LE Long Term Key Request Negative Reply",	226 },
+	{ "LE Read Supported States",			227 },
+	{ "LE Receiver Test",				228 },
+	{ "LE Transmitter Test",			229 },
+	{ "LE Test End",				230 },
+	{ "Reserved",					231 },
+
+	{ NULL }
+};
+
+char *hci_cmdtostr(unsigned int cmd)
+{
+	return hci_uint2str(commands_map, cmd);
+}
+
+char *hci_commandstostr(uint8_t *commands, char *pref, int width)
+{
+	unsigned int maxwidth = width - 3;
+	hci_map *m;
+	char *off, *ptr, *str;
+	int size = 10;
+
+	m = commands_map;
+
+	while (m->str) {
+		if (commands[m->val / 8] & (1 << (m->val % 8)))
+			size += strlen(m->str) + (pref ? strlen(pref) : 0) + 3;
+		m++;
+	}
+
+	str = bt_malloc(size);
+	if (!str)
+		return NULL;
+
+	ptr = str; *ptr = '\0';
+
+	if (pref)
+		ptr += sprintf(ptr, "%s", pref);
+
+	off = ptr;
+
+	m = commands_map;
+
+	while (m->str) {
+		if (commands[m->val / 8] & (1 << (m->val % 8))) {
+			if (strlen(off) + strlen(m->str) > maxwidth) {
+				ptr += sprintf(ptr, "\n%s", pref ? pref : "");
+				off = ptr;
+			}
+			ptr += sprintf(ptr, "'%s' ", m->str);
+		}
+		m++;
+	}
+
+	return str;
+}
+
+/* Version mapping */
+static hci_map ver_map[] = {
+	{ "1.0b",	0x00 },
+	{ "1.1",	0x01 },
+	{ "1.2",	0x02 },
+	{ "2.0",	0x03 },
+	{ "2.1",	0x04 },
+	{ "3.0",	0x05 },
+	{ "4.0",	0x06 },
+	{ "4.1",	0x07 },
+	{ "4.2",	0x08 },
+	{ NULL }
+};
+
+char *hci_vertostr(unsigned int ver)
+{
+	return hci_uint2str(ver_map, ver);
+}
+
+int hci_strtover(char *str, unsigned int *ver)
+{
+	return hci_str2uint(ver_map, str, ver);
+}
+
+char *lmp_vertostr(unsigned int ver)
+{
+	return hci_uint2str(ver_map, ver);
+}
+
+int lmp_strtover(char *str, unsigned int *ver)
+{
+	return hci_str2uint(ver_map, str, ver);
+}
+
+static hci_map pal_map[] = {
+	{ "3.0",	0x01 },
+	{ NULL }
+};
+
+char *pal_vertostr(unsigned int ver)
+{
+	return hci_uint2str(pal_map, ver);
+}
+
+int pal_strtover(char *str, unsigned int *ver)
+{
+	return hci_str2uint(pal_map, str, ver);
+}
+
+/* LMP features mapping */
+static hci_map lmp_features_map[8][9] = {
+	{	/* Byte 0 */
+		{ "<3-slot packets>",	LMP_3SLOT	},	/* Bit 0 */
+		{ "<5-slot packets>",	LMP_5SLOT	},	/* Bit 1 */
+		{ "<encryption>",	LMP_ENCRYPT	},	/* Bit 2 */
+		{ "<slot offset>",	LMP_SOFFSET	},	/* Bit 3 */
+		{ "<timing accuracy>",	LMP_TACCURACY	},	/* Bit 4 */
+		{ "<role switch>",	LMP_RSWITCH	},	/* Bit 5 */
+		{ "<hold mode>",	LMP_HOLD	},	/* Bit 6 */
+		{ "<sniff mode>",	LMP_SNIFF	},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 1 */
+		{ "<park state>",	LMP_PARK	},	/* Bit 0 */
+		{ "<RSSI>",		LMP_RSSI	},	/* Bit 1 */
+		{ "<channel quality>",	LMP_QUALITY	},	/* Bit 2 */
+		{ "<SCO link>",		LMP_SCO		},	/* Bit 3 */
+		{ "<HV2 packets>",	LMP_HV2		},	/* Bit 4 */
+		{ "<HV3 packets>",	LMP_HV3		},	/* Bit 5 */
+		{ "<u-law log>",	LMP_ULAW	},	/* Bit 6 */
+		{ "<A-law log>",	LMP_ALAW	},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 2 */
+		{ "<CVSD>",		LMP_CVSD	},	/* Bit 0 */
+		{ "<paging scheme>",	LMP_PSCHEME	},	/* Bit 1 */
+		{ "<power control>",	LMP_PCONTROL	},	/* Bit 2 */
+		{ "<transparent SCO>",	LMP_TRSP_SCO	},	/* Bit 3 */
+		{ "<broadcast encrypt>",LMP_BCAST_ENC	},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 3 */
+		{ "<no. 24>",		0x01		},	/* Bit 0 */
+		{ "<EDR ACL 2 Mbps>",	LMP_EDR_ACL_2M	},	/* Bit 1 */
+		{ "<EDR ACL 3 Mbps>",	LMP_EDR_ACL_3M	},	/* Bit 2 */
+		{ "<enhanced iscan>",	LMP_ENH_ISCAN	},	/* Bit 3 */
+		{ "<interlaced iscan>",	LMP_ILACE_ISCAN	},	/* Bit 4 */
+		{ "<interlaced pscan>",	LMP_ILACE_PSCAN	},	/* Bit 5 */
+		{ "<inquiry with RSSI>",LMP_RSSI_INQ	},	/* Bit 6 */
+		{ "<extended SCO>",	LMP_ESCO	},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 4 */
+		{ "<EV4 packets>",	LMP_EV4		},	/* Bit 0 */
+		{ "<EV5 packets>",	LMP_EV5		},	/* Bit 1 */
+		{ "<no. 34>",		0x04		},	/* Bit 2 */
+		{ "<AFH cap. slave>",	LMP_AFH_CAP_SLV	},	/* Bit 3 */
+		{ "<AFH class. slave>",	LMP_AFH_CLS_SLV	},	/* Bit 4 */
+		{ "<BR/EDR not supp.>",	LMP_NO_BREDR	},	/* Bit 5 */
+		{ "<LE support>",	LMP_LE		},	/* Bit 6 */
+		{ "<3-slot EDR ACL>",	LMP_EDR_3SLOT	},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 5 */
+		{ "<5-slot EDR ACL>",	LMP_EDR_5SLOT	},	/* Bit 0 */
+		{ "<sniff subrating>",	LMP_SNIFF_SUBR	},	/* Bit 1 */
+		{ "<pause encryption>",	LMP_PAUSE_ENC	},	/* Bit 2 */
+		{ "<AFH cap. master>",	LMP_AFH_CAP_MST	},	/* Bit 3 */
+		{ "<AFH class. master>",LMP_AFH_CLS_MST	},	/* Bit 4 */
+		{ "<EDR eSCO 2 Mbps>",	LMP_EDR_ESCO_2M	},	/* Bit 5 */
+		{ "<EDR eSCO 3 Mbps>",	LMP_EDR_ESCO_3M	},	/* Bit 6 */
+		{ "<3-slot EDR eSCO>",	LMP_EDR_3S_ESCO	},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 6 */
+		{ "<extended inquiry>",	LMP_EXT_INQ	},	/* Bit 0 */
+		{ "<LE and BR/EDR>",	LMP_LE_BREDR	},	/* Bit 1 */
+		{ "<no. 50>",		0x04		},	/* Bit 2 */
+		{ "<simple pairing>",	LMP_SIMPLE_PAIR	},	/* Bit 3 */
+		{ "<encapsulated PDU>",	LMP_ENCAPS_PDU	},	/* Bit 4 */
+		{ "<err. data report>",	LMP_ERR_DAT_REP	},	/* Bit 5 */
+		{ "<non-flush flag>",	LMP_NFLUSH_PKTS	},	/* Bit 6 */
+		{ "<no. 55>",		0x80		},	/* Bit 7 */
+		{ NULL }
+	},
+	{	/* Byte 7 */
+		{ "<LSTO>",		LMP_LSTO	},	/* Bit 1 */
+		{ "<inquiry TX power>",	LMP_INQ_TX_PWR	},	/* Bit 1 */
+		{ "<EPC>",		LMP_EPC		},	/* Bit 2 */
+		{ "<no. 59>",		0x08		},	/* Bit 3 */
+		{ "<no. 60>",		0x10		},	/* Bit 4 */
+		{ "<no. 61>",		0x20		},	/* Bit 5 */
+		{ "<no. 62>",		0x40		},	/* Bit 6 */
+		{ "<extended features>",LMP_EXT_FEAT	},	/* Bit 7 */
+		{ NULL }
+	},
+};
+
+char *lmp_featurestostr(uint8_t *features, char *pref, int width)
+{
+	unsigned int maxwidth = width - 1;
+	char *off, *ptr, *str;
+	int i, size = 10;
+
+	for (i = 0; i < 8; i++) {
+		hci_map *m = lmp_features_map[i];
+
+		while (m->str) {
+			if (m->val & features[i])
+				size += strlen(m->str) +
+						(pref ? strlen(pref) : 0) + 1;
+			m++;
+		}
+	}
+
+	str = bt_malloc(size);
+	if (!str)
+		return NULL;
+
+	ptr = str; *ptr = '\0';
+
+	if (pref)
+		ptr += sprintf(ptr, "%s", pref);
+
+	off = ptr;
+
+	for (i = 0; i < 8; i++) {
+		hci_map *m = lmp_features_map[i];
+
+		while (m->str) {
+			if (m->val & features[i]) {
+				if (strlen(off) + strlen(m->str) > maxwidth) {
+					ptr += sprintf(ptr, "\n%s",
+							pref ? pref : "");
+					off = ptr;
+				}
+				ptr += sprintf(ptr, "%s ", m->str);
+			}
+			m++;
+		}
+	}
+
+	return str;
+}
+
+/* HCI functions that do not require open device */
+int hci_for_each_dev(int flag, int (*func)(int dd, int dev_id, long arg),
+			long arg)
+{
+	struct hci_dev_list_req *dl;
+	struct hci_dev_req *dr;
+	int dev_id = -1;
+	int i, sk, err = 0;
+
+	sk = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (sk < 0)
+		return -1;
+
+	dl = malloc(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
+	if (!dl) {
+		err = errno;
+		goto done;
+	}
+
+	memset(dl, 0, HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
+
+	dl->dev_num = HCI_MAX_DEV;
+	dr = dl->dev_req;
+
+	if (ioctl(sk, HCIGETDEVLIST, (void *) dl) < 0) {
+		err = errno;
+		goto free;
+	}
+
+	for (i = 0; i < dl->dev_num; i++, dr++) {
+		if (hci_test_bit(flag, &dr->dev_opt))
+			if (!func || func(sk, dr->dev_id, arg)) {
+				dev_id = dr->dev_id;
+				break;
+			}
+	}
+
+	if (dev_id < 0)
+		err = ENODEV;
+
+free:
+	free(dl);
+
+done:
+	close(sk);
+	errno = err;
+
+	return dev_id;
+}
+
+static int __other_bdaddr(int dd, int dev_id, long arg)
+{
+	struct hci_dev_info di = { .dev_id = dev_id };
+
+	if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
+		return 0;
+
+	if (hci_test_bit(HCI_RAW, &di.flags))
+		return 0;
+
+	return bacmp((bdaddr_t *) arg, &di.bdaddr);
+}
+
+static int __same_bdaddr(int dd, int dev_id, long arg)
+{
+	struct hci_dev_info di = { .dev_id = dev_id };
+
+	if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
+		return 0;
+
+	return !bacmp((bdaddr_t *) arg, &di.bdaddr);
+}
+
+int hci_get_route(bdaddr_t *bdaddr)
+{
+	int dev_id;
+
+	dev_id = hci_for_each_dev(HCI_UP, __other_bdaddr,
+				(long) (bdaddr ? bdaddr : BDADDR_ANY));
+	if (dev_id < 0)
+		dev_id = hci_for_each_dev(HCI_UP, __same_bdaddr,
+				(long) (bdaddr ? bdaddr : BDADDR_ANY));
+
+	return dev_id;
+}
+
+int hci_devid(const char *str)
+{
+	bdaddr_t ba;
+	int id = -1;
+
+	if (!strncmp(str, "hci", 3) && strlen(str) >= 4) {
+		id = atoi(str + 3);
+		if (hci_devba(id, &ba) < 0)
+			return -1;
+	} else {
+		errno = ENODEV;
+		str2ba(str, &ba);
+		id = hci_for_each_dev(HCI_UP, __same_bdaddr, (long) &ba);
+	}
+
+	return id;
+}
+
+int hci_devinfo(int dev_id, struct hci_dev_info *di)
+{
+	int dd, err, ret;
+
+	dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (dd < 0)
+		return dd;
+
+	memset(di, 0, sizeof(struct hci_dev_info));
+
+	di->dev_id = dev_id;
+	ret = ioctl(dd, HCIGETDEVINFO, (void *) di);
+
+	err = errno;
+	close(dd);
+	errno = err;
+
+	return ret;
+}
+
+int hci_devba(int dev_id, bdaddr_t *bdaddr)
+{
+	struct hci_dev_info di;
+
+	memset(&di, 0, sizeof(di));
+
+	if (hci_devinfo(dev_id, &di))
+		return -1;
+
+	if (!hci_test_bit(HCI_UP, &di.flags)) {
+		errno = ENETDOWN;
+		return -1;
+	}
+
+	bacpy(bdaddr, &di.bdaddr);
+
+	return 0;
+}
+
+int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap,
+		inquiry_info **ii, long flags)
+{
+	struct hci_inquiry_req *ir;
+	uint8_t num_rsp = nrsp;
+	void *buf;
+	int dd, size, err, ret = -1;
+
+	if (nrsp <= 0) {
+		num_rsp = 0;
+		nrsp = 255;
+	}
+
+	if (dev_id < 0) {
+		dev_id = hci_get_route(NULL);
+		if (dev_id < 0) {
+			errno = ENODEV;
+			return -1;
+		}
+	}
+
+	dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (dd < 0)
+		return dd;
+
+	buf = malloc(sizeof(*ir) + (sizeof(inquiry_info) * (nrsp)));
+	if (!buf)
+		goto done;
+
+	ir = buf;
+	ir->dev_id  = dev_id;
+	ir->num_rsp = num_rsp;
+	ir->length  = len;
+	ir->flags   = flags;
+
+	if (lap) {
+		memcpy(ir->lap, lap, 3);
+	} else {
+		ir->lap[0] = 0x33;
+		ir->lap[1] = 0x8b;
+		ir->lap[2] = 0x9e;
+	}
+
+	ret = ioctl(dd, HCIINQUIRY, (unsigned long) buf);
+	if (ret < 0)
+		goto free;
+
+	size = sizeof(inquiry_info) * ir->num_rsp;
+
+	if (!*ii)
+		*ii = malloc(size);
+
+	if (*ii) {
+		memcpy((void *) *ii, buf + sizeof(*ir), size);
+		ret = ir->num_rsp;
+	} else
+		ret = -1;
+
+free:
+	free(buf);
+
+done:
+	err = errno;
+	close(dd);
+	errno = err;
+
+	return ret;
+}
+
+/* Open HCI device.
+ * Returns device descriptor (dd). */
+int hci_open_dev(int dev_id)
+{
+	struct sockaddr_hci a;
+	int dd, err;
+
+	/* Check for valid device id */
+	if (dev_id < 0) {
+		errno = ENODEV;
+		return -1;
+	}
+
+	/* Create HCI socket */
+	dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (dd < 0)
+		return dd;
+
+	/* Bind socket to the HCI device */
+	memset(&a, 0, sizeof(a));
+	a.hci_family = AF_BLUETOOTH;
+	a.hci_dev = dev_id;
+	if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0)
+		goto failed;
+
+	return dd;
+
+failed:
+	err = errno;
+	close(dd);
+	errno = err;
+
+	return -1;
+}
+
+int hci_close_dev(int dd)
+{
+	return close(dd);
+}
+
+/* HCI functions that require open device
+ * dd - Device descriptor returned by hci_open_dev. */
+
+int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
+{
+	uint8_t type = HCI_COMMAND_PKT;
+	hci_command_hdr hc;
+	struct iovec iv[3];
+	int ivn;
+
+	hc.opcode = htobs(cmd_opcode_pack(ogf, ocf));
+	hc.plen= plen;
+
+	iv[0].iov_base = &type;
+	iv[0].iov_len  = 1;
+	iv[1].iov_base = &hc;
+	iv[1].iov_len  = HCI_COMMAND_HDR_SIZE;
+	ivn = 2;
+
+	if (plen) {
+		iv[2].iov_base = param;
+		iv[2].iov_len  = plen;
+		ivn = 3;
+	}
+
+	while (writev(dd, iv, ivn) < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			continue;
+		return -1;
+	}
+	return 0;
+}
+
+int hci_send_req(int dd, struct hci_request *r, int to)
+{
+	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+	uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf));
+	struct hci_filter nf, of;
+	socklen_t olen;
+	hci_event_hdr *hdr;
+	int err, try;
+
+	olen = sizeof(of);
+	if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0)
+		return -1;
+
+	hci_filter_clear(&nf);
+	hci_filter_set_ptype(HCI_EVENT_PKT,  &nf);
+	hci_filter_set_event(EVT_CMD_STATUS, &nf);
+	hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
+	hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+	hci_filter_set_event(r->event, &nf);
+	hci_filter_set_opcode(opcode, &nf);
+	if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
+		return -1;
+
+	if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0)
+		goto failed;
+
+	try = 10;
+	while (try--) {
+		evt_cmd_complete *cc;
+		evt_cmd_status *cs;
+		evt_remote_name_req_complete *rn;
+		evt_le_meta_event *me;
+		remote_name_req_cp *cp;
+		int len;
+
+		if (to) {
+			struct pollfd p;
+			int n;
+
+			p.fd = dd; p.events = POLLIN;
+			while ((n = poll(&p, 1, to)) < 0) {
+				if (errno == EAGAIN || errno == EINTR)
+					continue;
+				goto failed;
+			}
+
+			if (!n) {
+				errno = ETIMEDOUT;
+				goto failed;
+			}
+
+			to -= 10;
+			if (to < 0)
+				to = 0;
+
+		}
+
+		while ((len = read(dd, buf, sizeof(buf))) < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+			goto failed;
+		}
+
+		hdr = (void *) (buf + 1);
+		ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+		len -= (1 + HCI_EVENT_HDR_SIZE);
+
+		switch (hdr->evt) {
+		case EVT_CMD_STATUS:
+			cs = (void *) ptr;
+
+			if (cs->opcode != opcode)
+				continue;
+
+			if (r->event != EVT_CMD_STATUS) {
+				if (cs->status) {
+					errno = EIO;
+					goto failed;
+				}
+				break;
+			}
+
+			r->rlen = MIN(len, r->rlen);
+			memcpy(r->rparam, ptr, r->rlen);
+			goto done;
+
+		case EVT_CMD_COMPLETE:
+			cc = (void *) ptr;
+
+			if (cc->opcode != opcode)
+				continue;
+
+			ptr += EVT_CMD_COMPLETE_SIZE;
+			len -= EVT_CMD_COMPLETE_SIZE;
+
+			r->rlen = MIN(len, r->rlen);
+			memcpy(r->rparam, ptr, r->rlen);
+			goto done;
+
+		case EVT_REMOTE_NAME_REQ_COMPLETE:
+			if (hdr->evt != r->event)
+				break;
+
+			rn = (void *) ptr;
+			cp = r->cparam;
+
+			if (bacmp(&rn->bdaddr, &cp->bdaddr))
+				continue;
+
+			r->rlen = MIN(len, r->rlen);
+			memcpy(r->rparam, ptr, r->rlen);
+			goto done;
+
+		case EVT_LE_META_EVENT:
+			me = (void *) ptr;
+
+			if (me->subevent != r->event)
+				continue;
+
+			len -= 1;
+			r->rlen = MIN(len, r->rlen);
+			memcpy(r->rparam, me->data, r->rlen);
+			goto done;
+
+		default:
+			if (hdr->evt != r->event)
+				break;
+
+			r->rlen = MIN(len, r->rlen);
+			memcpy(r->rparam, ptr, r->rlen);
+			goto done;
+		}
+	}
+	errno = ETIMEDOUT;
+
+failed:
+	err = errno;
+	setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+	errno = err;
+	return -1;
+
+done:
+	setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+	return 0;
+}
+
+int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype,
+				uint16_t clkoffset, uint8_t rswitch,
+				uint16_t *handle, int to)
+{
+	evt_conn_complete rp;
+	create_conn_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.pkt_type       = ptype;
+	cp.pscan_rep_mode = 0x02;
+	cp.clock_offset   = clkoffset;
+	cp.role_switch    = rswitch;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_CREATE_CONN;
+	rq.event  = EVT_CONN_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = CREATE_CONN_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_CONN_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*handle = rp.handle;
+	return 0;
+}
+
+int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to)
+{
+	evt_disconn_complete rp;
+	disconnect_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+	cp.reason = reason;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_DISCONNECT;
+	rq.event  = EVT_DISCONN_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = DISCONNECT_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_DISCONN_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+	return 0;
+}
+
+int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+	struct hci_request rq;
+	le_add_device_to_white_list_cp cp;
+	uint8_t status;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.bdaddr_type = type;
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_ADD_DEVICE_TO_WHITE_LIST;
+	rq.cparam = &cp;
+	rq.clen = LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+	struct hci_request rq;
+	le_remove_device_from_white_list_cp cp;
+	uint8_t status;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.bdaddr_type = type;
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST;
+	rq.cparam = &cp;
+	rq.clen = LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_read_white_list_size(int dd, uint8_t *size, int to)
+{
+	struct hci_request rq;
+	le_read_white_list_size_rp rp;
+
+	memset(&rp, 0, sizeof(rp));
+	memset(&rq, 0, sizeof(rq));
+
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_READ_WHITE_LIST_SIZE;
+	rq.rparam = &rp;
+	rq.rlen = LE_READ_WHITE_LIST_SIZE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (size)
+		*size = rp.size;
+
+	return 0;
+}
+
+int hci_le_clear_white_list(int dd, int to)
+{
+	struct hci_request rq;
+	uint8_t status;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_CLEAR_WHITE_LIST;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type,
+				uint8_t *peer_irk, uint8_t *local_irk, int to)
+{
+	struct hci_request rq;
+	le_add_device_to_resolv_list_cp cp;
+	uint8_t status;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.bdaddr_type = type;
+	bacpy(&cp.bdaddr, bdaddr);
+	if (peer_irk)
+		memcpy(cp.peer_irk, peer_irk, 16);
+	if (local_irk)
+		memcpy(cp.local_irk, local_irk, 16);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_ADD_DEVICE_TO_RESOLV_LIST;
+	rq.cparam = &cp;
+	rq.clen = LE_ADD_DEVICE_TO_RESOLV_LIST_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+	struct hci_request rq;
+	le_remove_device_from_resolv_list_cp cp;
+	uint8_t status;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.bdaddr_type = type;
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_REMOVE_DEVICE_FROM_RESOLV_LIST;
+	rq.cparam = &cp;
+	rq.clen = LE_REMOVE_DEVICE_FROM_RESOLV_LIST_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_clear_resolving_list(int dd, int to)
+{
+	struct hci_request rq;
+	uint8_t status;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_CLEAR_RESOLV_LIST;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to)
+{
+	struct hci_request rq;
+	le_read_resolv_list_size_rp rp;
+
+	memset(&rp, 0, sizeof(rp));
+	memset(&rq, 0, sizeof(rq));
+
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_READ_RESOLV_LIST_SIZE;
+	rq.rparam = &rp;
+	rq.rlen = LE_READ_RESOLV_LIST_SIZE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (size)
+		*size = rp.size;
+
+	return 0;
+}
+
+int hci_le_set_address_resolution_enable(int dd, uint8_t enable, int to)
+{
+	struct hci_request rq;
+	le_set_address_resolution_enable_cp cp;
+	uint8_t status;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.enable = enable;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE;
+	rq.cparam = &cp;
+	rq.clen = LE_SET_ADDRESS_RESOLUTION_ENABLE_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_local_name(int dd, int len, char *name, int to)
+{
+	read_local_name_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_LOCAL_NAME;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_NAME_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	rp.name[247] = '\0';
+	strncpy(name, (char *) rp.name, len);
+	return 0;
+}
+
+int hci_write_local_name(int dd, const char *name, int to)
+{
+	change_local_name_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	strncpy((char *) cp.name, name, sizeof(cp.name));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_CHANGE_LOCAL_NAME;
+	rq.cparam = &cp;
+	rq.clen   = CHANGE_LOCAL_NAME_CP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	return 0;
+}
+
+int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr,
+						uint8_t pscan_rep_mode,
+						uint16_t clkoffset,
+						int len, char *name, int to)
+{
+	evt_remote_name_req_complete rn;
+	remote_name_req_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.pscan_rep_mode = pscan_rep_mode;
+	cp.clock_offset   = clkoffset;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_REMOTE_NAME_REQ;
+	rq.cparam = &cp;
+	rq.clen   = REMOTE_NAME_REQ_CP_SIZE;
+	rq.event  = EVT_REMOTE_NAME_REQ_COMPLETE;
+	rq.rparam = &rn;
+	rq.rlen   = EVT_REMOTE_NAME_REQ_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rn.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	rn.name[247] = '\0';
+	strncpy(name, (char *) rn.name, len);
+	return 0;
+}
+
+int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name,
+				int to)
+{
+	return hci_read_remote_name_with_clock_offset(dd, bdaddr, 0x02, 0x0000,
+							len, name, to);
+}
+
+int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to)
+{
+	remote_name_req_cancel_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_REMOTE_NAME_REQ_CANCEL;
+	rq.cparam = &cp;
+	rq.clen   = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	return 0;
+}
+
+int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver,
+				int to)
+{
+	evt_read_remote_version_complete rp;
+	read_remote_version_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_READ_REMOTE_VERSION;
+	rq.event  = EVT_READ_REMOTE_VERSION_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = READ_REMOTE_VERSION_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_READ_REMOTE_VERSION_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	ver->manufacturer = btohs(rp.manufacturer);
+	ver->lmp_ver      = rp.lmp_ver;
+	ver->lmp_subver   = btohs(rp.lmp_subver);
+	return 0;
+}
+
+int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)
+{
+	evt_read_remote_features_complete rp;
+	read_remote_features_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_READ_REMOTE_FEATURES;
+	rq.event  = EVT_READ_REMOTE_FEATURES_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = READ_REMOTE_FEATURES_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (features)
+		memcpy(features, rp.features, 8);
+
+	return 0;
+}
+
+int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page,
+					uint8_t *max_page, uint8_t *features,
+					int to)
+{
+	evt_read_remote_ext_features_complete rp;
+	read_remote_ext_features_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle   = handle;
+	cp.page_num = page;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_READ_REMOTE_EXT_FEATURES;
+	rq.event  = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = READ_REMOTE_EXT_FEATURES_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (max_page)
+		*max_page = rp.max_page_num;
+
+	if (features)
+		memcpy(features, rp.features, 8);
+
+	return 0;
+}
+
+int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to)
+{
+	evt_read_clock_offset_complete rp;
+	read_clock_offset_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_READ_CLOCK_OFFSET;
+	rq.event  = EVT_READ_CLOCK_OFFSET_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = READ_CLOCK_OFFSET_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*clkoffset = rp.clock_offset;
+	return 0;
+}
+
+int hci_read_local_version(int dd, struct hci_version *ver, int to)
+{
+	read_local_version_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_INFO_PARAM;
+	rq.ocf    = OCF_READ_LOCAL_VERSION;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_VERSION_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	ver->manufacturer = btohs(rp.manufacturer);
+	ver->hci_ver      = rp.hci_ver;
+	ver->hci_rev      = btohs(rp.hci_rev);
+	ver->lmp_ver      = rp.lmp_ver;
+	ver->lmp_subver   = btohs(rp.lmp_subver);
+	return 0;
+}
+
+int hci_read_local_commands(int dd, uint8_t *commands, int to)
+{
+	read_local_commands_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_INFO_PARAM;
+	rq.ocf    = OCF_READ_LOCAL_COMMANDS;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_COMMANDS_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (commands)
+		memcpy(commands, rp.commands, 64);
+
+	return 0;
+}
+
+int hci_read_local_features(int dd, uint8_t *features, int to)
+{
+	read_local_features_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_INFO_PARAM;
+	rq.ocf    = OCF_READ_LOCAL_FEATURES;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_FEATURES_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (features)
+		memcpy(features, rp.features, 8);
+
+	return 0;
+}
+
+int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page,
+				uint8_t *features, int to)
+{
+	read_local_ext_features_cp cp;
+	read_local_ext_features_rp rp;
+	struct hci_request rq;
+
+	cp.page_num = page;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_INFO_PARAM;
+	rq.ocf    = OCF_READ_LOCAL_EXT_FEATURES;
+	rq.cparam = &cp;
+	rq.clen   = READ_LOCAL_EXT_FEATURES_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_EXT_FEATURES_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (max_page)
+		*max_page = rp.max_page_num;
+
+	if (features)
+		memcpy(features, rp.features, 8);
+
+	return 0;
+}
+
+int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to)
+{
+	read_bd_addr_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_INFO_PARAM;
+	rq.ocf    = OCF_READ_BD_ADDR;
+	rq.rparam = &rp;
+	rq.rlen   = READ_BD_ADDR_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (bdaddr)
+		bacpy(bdaddr, &rp.bdaddr);
+
+	return 0;
+}
+
+int hci_read_class_of_dev(int dd, uint8_t *cls, int to)
+{
+	read_class_of_dev_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_CLASS_OF_DEV;
+	rq.rparam = &rp;
+	rq.rlen   = READ_CLASS_OF_DEV_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	memcpy(cls, rp.dev_class, 3);
+	return 0;
+}
+
+int hci_write_class_of_dev(int dd, uint32_t cls, int to)
+{
+	write_class_of_dev_cp cp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	cp.dev_class[0] = cls & 0xff;
+	cp.dev_class[1] = (cls >> 8) & 0xff;
+	cp.dev_class[2] = (cls >> 16) & 0xff;
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_CLASS_OF_DEV;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_CLASS_OF_DEV_CP_SIZE;
+	return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_voice_setting(int dd, uint16_t *vs, int to)
+{
+	read_voice_setting_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_VOICE_SETTING;
+	rq.rparam = &rp;
+	rq.rlen   = READ_VOICE_SETTING_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*vs = rp.voice_setting;
+	return 0;
+}
+
+int hci_write_voice_setting(int dd, uint16_t vs, int to)
+{
+	write_voice_setting_cp cp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	cp.voice_setting = vs;
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_VOICE_SETTING;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_VOICE_SETTING_CP_SIZE;
+
+	return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to)
+{
+	read_current_iac_lap_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_CURRENT_IAC_LAP;
+	rq.rparam = &rp;
+	rq.rlen   = READ_CURRENT_IAC_LAP_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*num_iac = rp.num_current_iac;
+	memcpy(lap, rp.lap, rp.num_current_iac * 3);
+	return 0;
+}
+
+int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to)
+{
+	write_current_iac_lap_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.num_current_iac = num_iac;
+	memcpy(&cp.lap, lap, num_iac * 3);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_CURRENT_IAC_LAP;
+	rq.cparam = &cp;
+	rq.clen   = num_iac * 3 + 1;
+
+	return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
+{
+	read_stored_link_key_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.read_all = all;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_STORED_LINK_KEY;
+	rq.cparam = &cp;
+	rq.clen   = READ_STORED_LINK_KEY_CP_SIZE;
+
+	return hci_send_req(dd, &rq, to);
+}
+
+int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to)
+{
+	unsigned char cp[WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 1;
+	bacpy((bdaddr_t *) (cp + 1), bdaddr);
+	memcpy(cp + 7, key, 16);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_STORED_LINK_KEY;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16;
+
+	return hci_send_req(dd, &rq, to);
+}
+
+int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
+{
+	delete_stored_link_key_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.delete_all = all;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_DELETE_STORED_LINK_KEY;
+	rq.cparam = &cp;
+	rq.clen   = DELETE_STORED_LINK_KEY_CP_SIZE;
+
+	return hci_send_req(dd, &rq, to);
+}
+
+int hci_authenticate_link(int dd, uint16_t handle, int to)
+{
+	auth_requested_cp cp;
+	evt_auth_complete rp;
+	struct hci_request rq;
+
+	cp.handle = handle;
+
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_AUTH_REQUESTED;
+	rq.event  = EVT_AUTH_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = AUTH_REQUESTED_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_AUTH_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to)
+{
+	set_conn_encrypt_cp cp;
+	evt_encrypt_change rp;
+	struct hci_request rq;
+
+	cp.handle  = handle;
+	cp.encrypt = encrypt;
+
+	rq.ogf     = OGF_LINK_CTL;
+	rq.ocf     = OCF_SET_CONN_ENCRYPT;
+	rq.event   = EVT_ENCRYPT_CHANGE;
+	rq.cparam  = &cp;
+	rq.clen    = SET_CONN_ENCRYPT_CP_SIZE;
+	rq.rparam  = &rp;
+	rq.rlen    = EVT_ENCRYPT_CHANGE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_change_link_key(int dd, uint16_t handle, int to)
+{
+	change_conn_link_key_cp cp;
+	evt_change_conn_link_key_complete rp;
+	struct hci_request rq;
+
+	cp.handle = handle;
+
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_CHANGE_CONN_LINK_KEY;
+	rq.event  = EVT_CHANGE_CONN_LINK_KEY_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = CHANGE_CONN_LINK_KEY_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to)
+{
+	switch_role_cp cp;
+	evt_role_change rp;
+	struct hci_request rq;
+
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.role   = role;
+	rq.ogf    = OGF_LINK_POLICY;
+	rq.ocf    = OCF_SWITCH_ROLE;
+	rq.cparam = &cp;
+	rq.clen   = SWITCH_ROLE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_ROLE_CHANGE_SIZE;
+	rq.event  = EVT_ROLE_CHANGE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval,
+			uint16_t min_interval, int to)
+{
+	park_mode_cp cp;
+	evt_mode_change rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof (cp));
+	cp.handle       = handle;
+	cp.max_interval = max_interval;
+	cp.min_interval = min_interval;
+
+	memset(&rq, 0, sizeof (rq));
+	rq.ogf    = OGF_LINK_POLICY;
+	rq.ocf    = OCF_PARK_MODE;
+	rq.event  = EVT_MODE_CHANGE;
+	rq.cparam = &cp;
+	rq.clen   = PARK_MODE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_MODE_CHANGE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_exit_park_mode(int dd, uint16_t handle, int to)
+{
+	exit_park_mode_cp cp;
+	evt_mode_change rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof (cp));
+	cp.handle = handle;
+
+	memset (&rq, 0, sizeof (rq));
+	rq.ogf    = OGF_LINK_POLICY;
+	rq.ocf    = OCF_EXIT_PARK_MODE;
+	rq.event  = EVT_MODE_CHANGE;
+	rq.cparam = &cp;
+	rq.clen   = EXIT_PARK_MODE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_MODE_CHANGE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to)
+{
+	read_inquiry_scan_type_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_INQUIRY_SCAN_TYPE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_INQUIRY_SCAN_TYPE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*type = rp.type;
+	return 0;
+}
+
+int hci_write_inquiry_scan_type(int dd, uint8_t type, int to)
+{
+	write_inquiry_scan_type_cp cp;
+	write_inquiry_scan_type_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.type = type;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_INQUIRY_SCAN_TYPE;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_INQUIRY_SCAN_TYPE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_INQUIRY_SCAN_TYPE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_inquiry_mode(int dd, uint8_t *mode, int to)
+{
+	read_inquiry_mode_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_INQUIRY_MODE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_INQUIRY_MODE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*mode = rp.mode;
+	return 0;
+}
+
+int hci_write_inquiry_mode(int dd, uint8_t mode, int to)
+{
+	write_inquiry_mode_cp cp;
+	write_inquiry_mode_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.mode = mode;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_INQUIRY_MODE;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_INQUIRY_MODE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_INQUIRY_MODE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_afh_mode(int dd, uint8_t *mode, int to)
+{
+	read_afh_mode_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_AFH_MODE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_AFH_MODE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*mode = rp.mode;
+	return 0;
+}
+
+int hci_write_afh_mode(int dd, uint8_t mode, int to)
+{
+	write_afh_mode_cp cp;
+	write_afh_mode_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.mode = mode;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_AFH_MODE;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_AFH_MODE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_AFH_MODE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to)
+{
+	read_ext_inquiry_response_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_EXT_INQUIRY_RESPONSE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_EXT_INQUIRY_RESPONSE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*fec = rp.fec;
+	memcpy(data, rp.data, HCI_MAX_EIR_LENGTH);
+
+	return 0;
+}
+
+int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to)
+{
+	write_ext_inquiry_response_cp cp;
+	write_ext_inquiry_response_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.fec = fec;
+	memcpy(cp.data, data, HCI_MAX_EIR_LENGTH);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_EXT_INQUIRY_RESPONSE;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to)
+{
+	read_simple_pairing_mode_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_SIMPLE_PAIRING_MODE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_SIMPLE_PAIRING_MODE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*mode = rp.mode;
+	return 0;
+}
+
+int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to)
+{
+	write_simple_pairing_mode_cp cp;
+	write_simple_pairing_mode_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.mode = mode;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_SIMPLE_PAIRING_MODE;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_SIMPLE_PAIRING_MODE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_SIMPLE_PAIRING_MODE_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to)
+{
+	read_local_oob_data_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_LOCAL_OOB_DATA;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_OOB_DATA_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	memcpy(hash, rp.hash, 16);
+	memcpy(randomizer, rp.randomizer, 16);
+	return 0;
+}
+
+int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to)
+{
+	read_inq_response_tx_power_level_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL;
+	rq.rparam = &rp;
+	rq.rlen   = READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*level = rp.level;
+	return 0;
+}
+
+int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to)
+{
+	return hci_read_inq_response_tx_power_level(dd, level, to);
+}
+
+int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to)
+{
+	write_inquiry_transmit_power_level_cp cp;
+	write_inquiry_transmit_power_level_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.level = level;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type,
+					int8_t *level, int to)
+{
+	read_transmit_power_level_cp cp;
+	read_transmit_power_level_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+	cp.type   = type;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_TRANSMIT_POWER_LEVEL;
+	rq.cparam = &cp;
+	rq.clen   = READ_TRANSMIT_POWER_LEVEL_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_TRANSMIT_POWER_LEVEL_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*level = rp.level;
+	return 0;
+}
+
+int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to)
+{
+	read_link_policy_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_POLICY;
+	rq.ocf    = OCF_READ_LINK_POLICY;
+	rq.cparam = &handle;
+	rq.clen   = 2;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LINK_POLICY_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*policy = rp.policy;
+	return 0;
+}
+
+int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to)
+{
+	write_link_policy_cp cp;
+	write_link_policy_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+	cp.policy = policy;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_POLICY;
+	rq.ocf    = OCF_WRITE_LINK_POLICY;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_LINK_POLICY_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_LINK_POLICY_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_link_supervision_timeout(int dd, uint16_t handle,
+					uint16_t *timeout, int to)
+{
+	read_link_supervision_timeout_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_READ_LINK_SUPERVISION_TIMEOUT;
+	rq.cparam = &handle;
+	rq.clen   = 2;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*timeout = rp.timeout;
+	return 0;
+}
+
+int hci_write_link_supervision_timeout(int dd, uint16_t handle,
+					uint16_t timeout, int to)
+{
+	write_link_supervision_timeout_cp cp;
+	write_link_supervision_timeout_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle  = handle;
+	cp.timeout = timeout;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_WRITE_LINK_SUPERVISION_TIMEOUT;
+	rq.cparam = &cp;
+	rq.clen   = WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_set_afh_classification(int dd, uint8_t *map, int to)
+{
+	set_afh_classification_cp cp;
+	set_afh_classification_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(cp.map, map, 10);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_HOST_CTL;
+	rq.ocf    = OCF_SET_AFH_CLASSIFICATION;
+	rq.cparam = &cp;
+	rq.clen   = SET_AFH_CLASSIFICATION_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = SET_AFH_CLASSIFICATION_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality,
+				int to)
+{
+	read_link_quality_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_READ_LINK_QUALITY;
+	rq.cparam = &handle;
+	rq.clen   = 2;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LINK_QUALITY_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*link_quality = rp.link_quality;
+	return 0;
+}
+
+int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
+{
+	read_rssi_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_READ_RSSI;
+	rq.cparam = &handle;
+	rq.clen   = 2;
+	rq.rparam = &rp;
+	rq.rlen   = READ_RSSI_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*rssi = rp.rssi;
+	return 0;
+}
+
+int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map,
+			int to)
+{
+	read_afh_map_rp rp;
+	struct hci_request rq;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_READ_AFH_MAP;
+	rq.cparam = &handle;
+	rq.clen   = 2;
+	rq.rparam = &rp;
+	rq.rlen   = READ_AFH_MAP_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*mode = rp.mode;
+	memcpy(map, rp.map, 10);
+	return 0;
+}
+
+int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock,
+			uint16_t *accuracy, int to)
+{
+	read_clock_cp cp;
+	read_clock_rp rp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle      = handle;
+	cp.which_clock = which;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_READ_CLOCK;
+	rq.cparam = &cp;
+	rq.clen   = READ_CLOCK_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_CLOCK_RP_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	*clock    = rp.clock;
+	*accuracy = rp.accuracy;
+	return 0;
+}
+
+int hci_le_set_scan_enable(int dd, uint8_t enable, uint8_t filter_dup, int to)
+{
+	struct hci_request rq;
+	le_set_scan_enable_cp scan_cp;
+	uint8_t status;
+
+	memset(&scan_cp, 0, sizeof(scan_cp));
+	scan_cp.enable = enable;
+	scan_cp.filter_dup = filter_dup;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_SCAN_ENABLE;
+	rq.cparam = &scan_cp;
+	rq.clen = LE_SET_SCAN_ENABLE_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_set_scan_parameters(int dd, uint8_t type,
+					uint16_t interval, uint16_t window,
+					uint8_t own_type, uint8_t filter, int to)
+{
+	struct hci_request rq;
+	le_set_scan_parameters_cp param_cp;
+	uint8_t status;
+
+	memset(&param_cp, 0, sizeof(param_cp));
+	param_cp.type = type;
+	param_cp.interval = interval;
+	param_cp.window = window;
+	param_cp.own_bdaddr_type = own_type;
+	param_cp.filter = filter;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_SCAN_PARAMETERS;
+	rq.cparam = &param_cp;
+	rq.clen = LE_SET_SCAN_PARAMETERS_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_set_advertise_enable(int dd, uint8_t enable, int to)
+{
+	struct hci_request rq;
+	le_set_advertise_enable_cp adv_cp;
+	uint8_t status;
+
+	memset(&adv_cp, 0, sizeof(adv_cp));
+	adv_cp.enable = enable;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+	rq.cparam = &adv_cp;
+	rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
+		uint8_t initiator_filter, uint8_t peer_bdaddr_type,
+		bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
+		uint16_t min_interval, uint16_t max_interval,
+		uint16_t latency, uint16_t supervision_timeout,
+		uint16_t min_ce_length, uint16_t max_ce_length,
+		uint16_t *handle, int to)
+{
+	struct hci_request rq;
+	le_create_connection_cp create_conn_cp;
+	evt_le_connection_complete conn_complete_rp;
+
+	memset(&create_conn_cp, 0, sizeof(create_conn_cp));
+	create_conn_cp.interval = interval;
+	create_conn_cp.window = window;
+	create_conn_cp.initiator_filter = initiator_filter;
+	create_conn_cp.peer_bdaddr_type = peer_bdaddr_type;
+	create_conn_cp.peer_bdaddr = peer_bdaddr;
+	create_conn_cp.own_bdaddr_type = own_bdaddr_type;
+	create_conn_cp.min_interval = min_interval;
+	create_conn_cp.max_interval = max_interval;
+	create_conn_cp.latency = latency;
+	create_conn_cp.supervision_timeout = supervision_timeout;
+	create_conn_cp.min_ce_length = min_ce_length;
+	create_conn_cp.max_ce_length = max_ce_length;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_CREATE_CONN;
+	rq.event = EVT_LE_CONN_COMPLETE;
+	rq.cparam = &create_conn_cp;
+	rq.clen = LE_CREATE_CONN_CP_SIZE;
+	rq.rparam = &conn_complete_rp;
+	rq.rlen = EVT_CONN_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (conn_complete_rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (handle)
+		*handle = conn_complete_rp.handle;
+
+	return 0;
+}
+
+int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
+			uint16_t max_interval, uint16_t latency,
+			uint16_t supervision_timeout, int to)
+{
+	evt_le_connection_update_complete evt;
+	le_connection_update_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+	cp.min_interval = min_interval;
+	cp.max_interval = max_interval;
+	cp.latency = latency;
+	cp.supervision_timeout = supervision_timeout;
+	cp.min_ce_length = htobs(0x0001);
+	cp.max_ce_length = htobs(0x0001);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_CONN_UPDATE;
+	rq.cparam = &cp;
+	rq.clen = LE_CONN_UPDATE_CP_SIZE;
+	rq.event = EVT_LE_CONN_UPDATE_COMPLETE;
+	rq.rparam = &evt;
+	rq.rlen = sizeof(evt);
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (evt.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)
+{
+	evt_le_read_remote_used_features_complete rp;
+	le_read_remote_used_features_cp cp;
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = handle;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LE_CTL;
+	rq.ocf    = OCF_LE_READ_REMOTE_USED_FEATURES;
+	rq.event  = EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE;
+	rq.cparam = &cp;
+	rq.clen   = LE_READ_REMOTE_USED_FEATURES_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE;
+
+	if (hci_send_req(dd, &rq, to) < 0)
+		return -1;
+
+	if (rp.status) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (features)
+		memcpy(features, rp.features, 8);
+
+	return 0;
+}
diff --git a/bluez/repo/lib/hci.h b/bluez/repo/lib/hci.h
new file mode 100644
index 0000000..397caf2
--- /dev/null
+++ b/bluez/repo/lib/hci.h
@@ -0,0 +1,2450 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __HCI_H
+#define __HCI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+#define HCI_MAX_DEV	16
+
+#define HCI_MAX_ACL_SIZE	(1492 + 4)
+#define HCI_MAX_SCO_SIZE	255
+#define HCI_MAX_EVENT_SIZE	260
+#define HCI_MAX_FRAME_SIZE	(HCI_MAX_ACL_SIZE + 4)
+
+/* HCI dev events */
+#define HCI_DEV_REG	1
+#define HCI_DEV_UNREG	2
+#define HCI_DEV_UP	3
+#define HCI_DEV_DOWN	4
+#define HCI_DEV_SUSPEND	5
+#define HCI_DEV_RESUME	6
+
+/* HCI bus types */
+#define HCI_VIRTUAL	0
+#define HCI_USB		1
+#define HCI_PCCARD	2
+#define HCI_UART	3
+#define HCI_RS232	4
+#define HCI_PCI		5
+#define HCI_SDIO	6
+
+/* HCI controller types */
+#define HCI_BREDR	0x00
+#define HCI_AMP		0x01
+
+/* HCI device flags */
+enum {
+	HCI_UP,
+	HCI_INIT,
+	HCI_RUNNING,
+
+	HCI_PSCAN,
+	HCI_ISCAN,
+	HCI_AUTH,
+	HCI_ENCRYPT,
+	HCI_INQUIRY,
+
+	HCI_RAW,
+};
+
+/* LE address type */
+enum {
+	LE_PUBLIC_ADDRESS = 0x00,
+	LE_RANDOM_ADDRESS = 0x01
+};
+
+/* HCI ioctl defines */
+#define HCIDEVUP	_IOW('H', 201, int)
+#define HCIDEVDOWN	_IOW('H', 202, int)
+#define HCIDEVRESET	_IOW('H', 203, int)
+#define HCIDEVRESTAT	_IOW('H', 204, int)
+
+#define HCIGETDEVLIST	_IOR('H', 210, int)
+#define HCIGETDEVINFO	_IOR('H', 211, int)
+#define HCIGETCONNLIST	_IOR('H', 212, int)
+#define HCIGETCONNINFO	_IOR('H', 213, int)
+#define HCIGETAUTHINFO	_IOR('H', 215, int)
+
+#define HCISETRAW	_IOW('H', 220, int)
+#define HCISETSCAN	_IOW('H', 221, int)
+#define HCISETAUTH	_IOW('H', 222, int)
+#define HCISETENCRYPT	_IOW('H', 223, int)
+#define HCISETPTYPE	_IOW('H', 224, int)
+#define HCISETLINKPOL	_IOW('H', 225, int)
+#define HCISETLINKMODE	_IOW('H', 226, int)
+#define HCISETACLMTU	_IOW('H', 227, int)
+#define HCISETSCOMTU	_IOW('H', 228, int)
+
+#define HCIBLOCKADDR	_IOW('H', 230, int)
+#define HCIUNBLOCKADDR	_IOW('H', 231, int)
+
+#define HCIINQUIRY	_IOR('H', 240, int)
+
+#ifndef __NO_HCI_DEFS
+
+/* HCI Packet types */
+#define HCI_COMMAND_PKT		0x01
+#define HCI_ACLDATA_PKT		0x02
+#define HCI_SCODATA_PKT		0x03
+#define HCI_EVENT_PKT		0x04
+#define HCI_VENDOR_PKT		0xff
+
+/* HCI Packet types */
+#define HCI_2DH1	0x0002
+#define HCI_3DH1	0x0004
+#define HCI_DM1		0x0008
+#define HCI_DH1		0x0010
+#define HCI_2DH3	0x0100
+#define HCI_3DH3	0x0200
+#define HCI_DM3		0x0400
+#define HCI_DH3		0x0800
+#define HCI_2DH5	0x1000
+#define HCI_3DH5	0x2000
+#define HCI_DM5		0x4000
+#define HCI_DH5		0x8000
+
+#define HCI_HV1		0x0020
+#define HCI_HV2		0x0040
+#define HCI_HV3		0x0080
+
+#define HCI_EV3		0x0008
+#define HCI_EV4		0x0010
+#define HCI_EV5		0x0020
+#define HCI_2EV3	0x0040
+#define HCI_3EV3	0x0080
+#define HCI_2EV5	0x0100
+#define HCI_3EV5	0x0200
+
+#define SCO_PTYPE_MASK	(HCI_HV1 | HCI_HV2 | HCI_HV3)
+#define ACL_PTYPE_MASK	(HCI_DM1 | HCI_DH1 | HCI_DM3 | HCI_DH3 | HCI_DM5 | HCI_DH5)
+
+/* HCI Error codes */
+#define HCI_UNKNOWN_COMMAND			0x01
+#define HCI_NO_CONNECTION			0x02
+#define HCI_HARDWARE_FAILURE			0x03
+#define HCI_PAGE_TIMEOUT			0x04
+#define HCI_AUTHENTICATION_FAILURE		0x05
+#define HCI_PIN_OR_KEY_MISSING			0x06
+#define HCI_MEMORY_FULL				0x07
+#define HCI_CONNECTION_TIMEOUT			0x08
+#define HCI_MAX_NUMBER_OF_CONNECTIONS		0x09
+#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS	0x0a
+#define HCI_ACL_CONNECTION_EXISTS		0x0b
+#define HCI_COMMAND_DISALLOWED			0x0c
+#define HCI_REJECTED_LIMITED_RESOURCES		0x0d
+#define HCI_REJECTED_SECURITY			0x0e
+#define HCI_REJECTED_PERSONAL			0x0f
+#define HCI_HOST_TIMEOUT			0x10
+#define HCI_UNSUPPORTED_FEATURE			0x11
+#define HCI_INVALID_PARAMETERS			0x12
+#define HCI_OE_USER_ENDED_CONNECTION		0x13
+#define HCI_OE_LOW_RESOURCES			0x14
+#define HCI_OE_POWER_OFF			0x15
+#define HCI_CONNECTION_TERMINATED		0x16
+#define HCI_REPEATED_ATTEMPTS			0x17
+#define HCI_PAIRING_NOT_ALLOWED			0x18
+#define HCI_UNKNOWN_LMP_PDU			0x19
+#define HCI_UNSUPPORTED_REMOTE_FEATURE		0x1a
+#define HCI_SCO_OFFSET_REJECTED			0x1b
+#define HCI_SCO_INTERVAL_REJECTED		0x1c
+#define HCI_AIR_MODE_REJECTED			0x1d
+#define HCI_INVALID_LMP_PARAMETERS		0x1e
+#define HCI_UNSPECIFIED_ERROR			0x1f
+#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE	0x20
+#define HCI_ROLE_CHANGE_NOT_ALLOWED		0x21
+#define HCI_LMP_RESPONSE_TIMEOUT		0x22
+#define HCI_LMP_ERROR_TRANSACTION_COLLISION	0x23
+#define HCI_LMP_PDU_NOT_ALLOWED			0x24
+#define HCI_ENCRYPTION_MODE_NOT_ACCEPTED	0x25
+#define HCI_UNIT_LINK_KEY_USED			0x26
+#define HCI_QOS_NOT_SUPPORTED			0x27
+#define HCI_INSTANT_PASSED			0x28
+#define HCI_PAIRING_NOT_SUPPORTED		0x29
+#define HCI_TRANSACTION_COLLISION		0x2a
+#define HCI_QOS_UNACCEPTABLE_PARAMETER		0x2c
+#define HCI_QOS_REJECTED			0x2d
+#define HCI_CLASSIFICATION_NOT_SUPPORTED	0x2e
+#define HCI_INSUFFICIENT_SECURITY		0x2f
+#define HCI_PARAMETER_OUT_OF_RANGE		0x30
+#define HCI_ROLE_SWITCH_PENDING			0x32
+#define HCI_SLOT_VIOLATION			0x34
+#define HCI_ROLE_SWITCH_FAILED			0x35
+#define HCI_EIR_TOO_LARGE			0x36
+#define HCI_SIMPLE_PAIRING_NOT_SUPPORTED	0x37
+#define HCI_HOST_BUSY_PAIRING			0x38
+
+/* ACL flags */
+#define ACL_START_NO_FLUSH	0x00
+#define ACL_CONT		0x01
+#define ACL_START		0x02
+#define ACL_ACTIVE_BCAST	0x04
+#define ACL_PICO_BCAST		0x08
+
+/* Baseband links */
+#define SCO_LINK	0x00
+#define ACL_LINK	0x01
+#define ESCO_LINK	0x02
+
+/* LMP features */
+#define LMP_3SLOT	0x01
+#define LMP_5SLOT	0x02
+#define LMP_ENCRYPT	0x04
+#define LMP_SOFFSET	0x08
+#define LMP_TACCURACY	0x10
+#define LMP_RSWITCH	0x20
+#define LMP_HOLD	0x40
+#define LMP_SNIFF	0x80
+
+#define LMP_PARK	0x01
+#define LMP_RSSI	0x02
+#define LMP_QUALITY	0x04
+#define LMP_SCO		0x08
+#define LMP_HV2		0x10
+#define LMP_HV3		0x20
+#define LMP_ULAW	0x40
+#define LMP_ALAW	0x80
+
+#define LMP_CVSD	0x01
+#define LMP_PSCHEME	0x02
+#define LMP_PCONTROL	0x04
+#define LMP_TRSP_SCO	0x08
+#define LMP_BCAST_ENC	0x80
+
+#define LMP_EDR_ACL_2M	0x02
+#define LMP_EDR_ACL_3M	0x04
+#define LMP_ENH_ISCAN	0x08
+#define LMP_ILACE_ISCAN	0x10
+#define LMP_ILACE_PSCAN	0x20
+#define LMP_RSSI_INQ	0x40
+#define LMP_ESCO	0x80
+
+#define LMP_EV4		0x01
+#define LMP_EV5		0x02
+#define LMP_AFH_CAP_SLV	0x08
+#define LMP_AFH_CLS_SLV	0x10
+#define LMP_NO_BREDR	0x20
+#define LMP_LE		0x40
+#define LMP_EDR_3SLOT	0x80
+
+#define LMP_EDR_5SLOT	0x01
+#define LMP_SNIFF_SUBR	0x02
+#define LMP_PAUSE_ENC	0x04
+#define LMP_AFH_CAP_MST	0x08
+#define LMP_AFH_CLS_MST	0x10
+#define LMP_EDR_ESCO_2M	0x20
+#define LMP_EDR_ESCO_3M	0x40
+#define LMP_EDR_3S_ESCO	0x80
+
+#define LMP_EXT_INQ	0x01
+#define LMP_LE_BREDR	0x02
+#define LMP_SIMPLE_PAIR	0x08
+#define LMP_ENCAPS_PDU	0x10
+#define LMP_ERR_DAT_REP	0x20
+#define LMP_NFLUSH_PKTS	0x40
+
+#define LMP_LSTO	0x01
+#define LMP_INQ_TX_PWR	0x02
+#define LMP_EPC		0x04
+#define LMP_EXT_FEAT	0x80
+
+/* Extended LMP features */
+#define LMP_HOST_SSP		0x01
+#define LMP_HOST_LE		0x02
+#define LMP_HOST_LE_BREDR	0x04
+
+/* Link policies */
+#define HCI_LP_RSWITCH	0x0001
+#define HCI_LP_HOLD	0x0002
+#define HCI_LP_SNIFF	0x0004
+#define HCI_LP_PARK	0x0008
+
+/* Link mode */
+#define HCI_LM_ACCEPT	0x8000
+#define HCI_LM_MASTER	0x0001
+#define HCI_LM_AUTH	0x0002
+#define HCI_LM_ENCRYPT	0x0004
+#define HCI_LM_TRUSTED	0x0008
+#define HCI_LM_RELIABLE	0x0010
+#define HCI_LM_SECURE	0x0020
+
+/* Link Key types */
+#define HCI_LK_COMBINATION		0x00
+#define HCI_LK_LOCAL_UNIT		0x01
+#define HCI_LK_REMOTE_UNIT		0x02
+#define HCI_LK_DEBUG_COMBINATION	0x03
+#define HCI_LK_UNAUTH_COMBINATION	0x04
+#define HCI_LK_AUTH_COMBINATION		0x05
+#define HCI_LK_CHANGED_COMBINATION	0x06
+#define HCI_LK_INVALID			0xFF
+
+/* -----  HCI Commands ----- */
+
+/* Link Control */
+#define OGF_LINK_CTL		0x01
+
+#define OCF_INQUIRY			0x0001
+typedef struct {
+	uint8_t		lap[3];
+	uint8_t		length;		/* 1.28s units */
+	uint8_t		num_rsp;
+} __attribute__ ((packed)) inquiry_cp;
+#define INQUIRY_CP_SIZE 5
+
+typedef struct {
+	uint8_t		status;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) status_bdaddr_rp;
+#define STATUS_BDADDR_RP_SIZE 7
+
+#define OCF_INQUIRY_CANCEL		0x0002
+
+#define OCF_PERIODIC_INQUIRY		0x0003
+typedef struct {
+	uint16_t	max_period;	/* 1.28s units */
+	uint16_t	min_period;	/* 1.28s units */
+	uint8_t		lap[3];
+	uint8_t		length;		/* 1.28s units */
+	uint8_t		num_rsp;
+} __attribute__ ((packed)) periodic_inquiry_cp;
+#define PERIODIC_INQUIRY_CP_SIZE 9
+
+#define OCF_EXIT_PERIODIC_INQUIRY	0x0004
+
+#define OCF_CREATE_CONN			0x0005
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint16_t	pkt_type;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_mode;
+	uint16_t	clock_offset;
+	uint8_t		role_switch;
+} __attribute__ ((packed)) create_conn_cp;
+#define CREATE_CONN_CP_SIZE 13
+
+#define OCF_DISCONNECT			0x0006
+typedef struct {
+	uint16_t	handle;
+	uint8_t		reason;
+} __attribute__ ((packed)) disconnect_cp;
+#define DISCONNECT_CP_SIZE 3
+
+#define OCF_ADD_SCO			0x0007
+typedef struct {
+	uint16_t	handle;
+	uint16_t	pkt_type;
+} __attribute__ ((packed)) add_sco_cp;
+#define ADD_SCO_CP_SIZE 4
+
+#define OCF_CREATE_CONN_CANCEL		0x0008
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) create_conn_cancel_cp;
+#define CREATE_CONN_CANCEL_CP_SIZE 6
+
+#define OCF_ACCEPT_CONN_REQ		0x0009
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		role;
+} __attribute__ ((packed)) accept_conn_req_cp;
+#define ACCEPT_CONN_REQ_CP_SIZE	7
+
+#define OCF_REJECT_CONN_REQ		0x000A
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		reason;
+} __attribute__ ((packed)) reject_conn_req_cp;
+#define REJECT_CONN_REQ_CP_SIZE	7
+
+#define OCF_LINK_KEY_REPLY		0x000B
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		link_key[16];
+} __attribute__ ((packed)) link_key_reply_cp;
+#define LINK_KEY_REPLY_CP_SIZE 22
+
+#define OCF_LINK_KEY_NEG_REPLY		0x000C
+
+#define OCF_PIN_CODE_REPLY		0x000D
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pin_len;
+	uint8_t		pin_code[16];
+} __attribute__ ((packed)) pin_code_reply_cp;
+#define PIN_CODE_REPLY_CP_SIZE 23
+
+#define OCF_PIN_CODE_NEG_REPLY		0x000E
+
+#define OCF_SET_CONN_PTYPE		0x000F
+typedef struct {
+	uint16_t	 handle;
+	uint16_t	 pkt_type;
+} __attribute__ ((packed)) set_conn_ptype_cp;
+#define SET_CONN_PTYPE_CP_SIZE 4
+
+#define OCF_AUTH_REQUESTED		0x0011
+typedef struct {
+	uint16_t	 handle;
+} __attribute__ ((packed)) auth_requested_cp;
+#define AUTH_REQUESTED_CP_SIZE 2
+
+#define OCF_SET_CONN_ENCRYPT		0x0013
+typedef struct {
+	uint16_t	handle;
+	uint8_t		encrypt;
+} __attribute__ ((packed)) set_conn_encrypt_cp;
+#define SET_CONN_ENCRYPT_CP_SIZE 3
+
+#define OCF_CHANGE_CONN_LINK_KEY	0x0015
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) change_conn_link_key_cp;
+#define CHANGE_CONN_LINK_KEY_CP_SIZE 2
+
+#define OCF_MASTER_LINK_KEY		0x0017
+typedef struct {
+	uint8_t		key_flag;
+} __attribute__ ((packed)) master_link_key_cp;
+#define MASTER_LINK_KEY_CP_SIZE 1
+
+#define OCF_REMOTE_NAME_REQ		0x0019
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_mode;
+	uint16_t	clock_offset;
+} __attribute__ ((packed)) remote_name_req_cp;
+#define REMOTE_NAME_REQ_CP_SIZE 10
+
+#define OCF_REMOTE_NAME_REQ_CANCEL	0x001A
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) remote_name_req_cancel_cp;
+#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6
+
+#define OCF_READ_REMOTE_FEATURES	0x001B
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) read_remote_features_cp;
+#define READ_REMOTE_FEATURES_CP_SIZE 2
+
+#define OCF_READ_REMOTE_EXT_FEATURES	0x001C
+typedef struct {
+	uint16_t	handle;
+	uint8_t		page_num;
+} __attribute__ ((packed)) read_remote_ext_features_cp;
+#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3
+
+#define OCF_READ_REMOTE_VERSION		0x001D
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) read_remote_version_cp;
+#define READ_REMOTE_VERSION_CP_SIZE 2
+
+#define OCF_READ_CLOCK_OFFSET		0x001F
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) read_clock_offset_cp;
+#define READ_CLOCK_OFFSET_CP_SIZE 2
+
+#define OCF_READ_LMP_HANDLE		0x0020
+
+#define OCF_SETUP_SYNC_CONN		0x0028
+typedef struct {
+	uint16_t	handle;
+	uint32_t	tx_bandwith;
+	uint32_t	rx_bandwith;
+	uint16_t	max_latency;
+	uint16_t	voice_setting;
+	uint8_t		retrans_effort;
+	uint16_t	pkt_type;
+} __attribute__ ((packed)) setup_sync_conn_cp;
+#define SETUP_SYNC_CONN_CP_SIZE 17
+
+#define OCF_ACCEPT_SYNC_CONN_REQ	0x0029
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint32_t	tx_bandwith;
+	uint32_t	rx_bandwith;
+	uint16_t	max_latency;
+	uint16_t	voice_setting;
+	uint8_t		retrans_effort;
+	uint16_t	pkt_type;
+} __attribute__ ((packed)) accept_sync_conn_req_cp;
+#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21
+
+#define OCF_REJECT_SYNC_CONN_REQ	0x002A
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		reason;
+} __attribute__ ((packed)) reject_sync_conn_req_cp;
+#define REJECT_SYNC_CONN_REQ_CP_SIZE 7
+
+#define OCF_IO_CAPABILITY_REPLY		0x002B
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		capability;
+	uint8_t		oob_data;
+	uint8_t		authentication;
+} __attribute__ ((packed)) io_capability_reply_cp;
+#define IO_CAPABILITY_REPLY_CP_SIZE 9
+
+#define OCF_USER_CONFIRM_REPLY		0x002C
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) user_confirm_reply_cp;
+#define USER_CONFIRM_REPLY_CP_SIZE 6
+
+#define OCF_USER_CONFIRM_NEG_REPLY	0x002D
+
+#define OCF_USER_PASSKEY_REPLY		0x002E
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint32_t	passkey;
+} __attribute__ ((packed)) user_passkey_reply_cp;
+#define USER_PASSKEY_REPLY_CP_SIZE 10
+
+#define OCF_USER_PASSKEY_NEG_REPLY	0x002F
+
+#define OCF_REMOTE_OOB_DATA_REPLY	0x0030
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		hash[16];
+	uint8_t		randomizer[16];
+} __attribute__ ((packed)) remote_oob_data_reply_cp;
+#define REMOTE_OOB_DATA_REPLY_CP_SIZE 38
+
+#define OCF_REMOTE_OOB_DATA_NEG_REPLY	0x0033
+
+#define OCF_IO_CAPABILITY_NEG_REPLY	0x0034
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		reason;
+} __attribute__ ((packed)) io_capability_neg_reply_cp;
+#define IO_CAPABILITY_NEG_REPLY_CP_SIZE 7
+
+#define OCF_CREATE_PHYSICAL_LINK		0x0035
+typedef struct {
+	uint8_t		handle;
+	uint8_t		key_length;
+	uint8_t		key_type;
+	uint8_t		key[32];
+} __attribute__ ((packed)) create_physical_link_cp;
+#define CREATE_PHYSICAL_LINK_CP_SIZE 35
+
+#define OCF_ACCEPT_PHYSICAL_LINK		0x0036
+typedef struct {
+	uint8_t		handle;
+	uint8_t		key_length;
+	uint8_t		key_type;
+	uint8_t		key[32];
+} __attribute__ ((packed)) accept_physical_link_cp;
+#define ACCEPT_PHYSICAL_LINK_CP_SIZE 35
+
+#define OCF_DISCONNECT_PHYSICAL_LINK		0x0037
+typedef struct {
+	uint8_t		handle;
+	uint8_t		reason;
+} __attribute__ ((packed)) disconnect_physical_link_cp;
+#define DISCONNECT_PHYSICAL_LINK_CP_SIZE 2
+
+#define OCF_CREATE_LOGICAL_LINK		0x0038
+typedef struct {
+	uint8_t		handle;
+	uint8_t		tx_flow[16];
+	uint8_t		rx_flow[16];
+} __attribute__ ((packed)) create_logical_link_cp;
+#define CREATE_LOGICAL_LINK_CP_SIZE 33
+
+#define OCF_ACCEPT_LOGICAL_LINK		0x0039
+
+#define OCF_DISCONNECT_LOGICAL_LINK		0x003A
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) disconnect_logical_link_cp;
+#define DISCONNECT_LOGICAL_LINK_CP_SIZE 2
+
+#define OCF_LOGICAL_LINK_CANCEL		0x003B
+typedef struct {
+	uint8_t		handle;
+	uint8_t		tx_flow_id;
+} __attribute__ ((packed)) cancel_logical_link_cp;
+#define LOGICAL_LINK_CANCEL_CP_SIZE 2
+typedef struct {
+	uint8_t		status;
+	uint8_t		handle;
+	uint8_t		tx_flow_id;
+} __attribute__ ((packed)) cancel_logical_link_rp;
+#define LOGICAL_LINK_CANCEL_RP_SIZE 3
+
+#define OCF_FLOW_SPEC_MODIFY		0x003C
+
+/* Link Policy */
+#define OGF_LINK_POLICY		0x02
+
+#define OCF_HOLD_MODE			0x0001
+typedef struct {
+	uint16_t	handle;
+	uint16_t	max_interval;
+	uint16_t	min_interval;
+} __attribute__ ((packed)) hold_mode_cp;
+#define HOLD_MODE_CP_SIZE 6
+
+#define OCF_SNIFF_MODE			0x0003
+typedef struct {
+	uint16_t	handle;
+	uint16_t	max_interval;
+	uint16_t	min_interval;
+	uint16_t	attempt;
+	uint16_t	timeout;
+} __attribute__ ((packed)) sniff_mode_cp;
+#define SNIFF_MODE_CP_SIZE 10
+
+#define OCF_EXIT_SNIFF_MODE		0x0004
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) exit_sniff_mode_cp;
+#define EXIT_SNIFF_MODE_CP_SIZE 2
+
+#define OCF_PARK_MODE			0x0005
+typedef struct {
+	uint16_t	handle;
+	uint16_t	max_interval;
+	uint16_t	min_interval;
+} __attribute__ ((packed)) park_mode_cp;
+#define PARK_MODE_CP_SIZE 6
+
+#define OCF_EXIT_PARK_MODE		0x0006
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) exit_park_mode_cp;
+#define EXIT_PARK_MODE_CP_SIZE 2
+
+#define OCF_QOS_SETUP			0x0007
+typedef struct {
+	uint8_t		service_type;		/* 1 = best effort */
+	uint32_t	token_rate;		/* Byte per seconds */
+	uint32_t	peak_bandwidth;		/* Byte per seconds */
+	uint32_t	latency;		/* Microseconds */
+	uint32_t	delay_variation;	/* Microseconds */
+} __attribute__ ((packed)) hci_qos;
+#define HCI_QOS_CP_SIZE 17
+typedef struct {
+	uint16_t	handle;
+	uint8_t		flags;			/* Reserved */
+	hci_qos		qos;
+} __attribute__ ((packed)) qos_setup_cp;
+#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE)
+
+#define OCF_ROLE_DISCOVERY		0x0009
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) role_discovery_cp;
+#define ROLE_DISCOVERY_CP_SIZE 2
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		role;
+} __attribute__ ((packed)) role_discovery_rp;
+#define ROLE_DISCOVERY_RP_SIZE 4
+
+#define OCF_SWITCH_ROLE			0x000B
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		role;
+} __attribute__ ((packed)) switch_role_cp;
+#define SWITCH_ROLE_CP_SIZE 7
+
+#define OCF_READ_LINK_POLICY		0x000C
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) read_link_policy_cp;
+#define READ_LINK_POLICY_CP_SIZE 2
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint16_t	policy;
+} __attribute__ ((packed)) read_link_policy_rp;
+#define READ_LINK_POLICY_RP_SIZE 5
+
+#define OCF_WRITE_LINK_POLICY		0x000D
+typedef struct {
+	uint16_t	handle;
+	uint16_t	policy;
+} __attribute__ ((packed)) write_link_policy_cp;
+#define WRITE_LINK_POLICY_CP_SIZE 4
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) write_link_policy_rp;
+#define WRITE_LINK_POLICY_RP_SIZE 3
+
+#define OCF_READ_DEFAULT_LINK_POLICY	0x000E
+
+#define OCF_WRITE_DEFAULT_LINK_POLICY	0x000F
+
+#define OCF_FLOW_SPECIFICATION		0x0010
+
+#define OCF_SNIFF_SUBRATING		0x0011
+typedef struct {
+	uint16_t	handle;
+	uint16_t	max_latency;
+	uint16_t	min_remote_timeout;
+	uint16_t	min_local_timeout;
+} __attribute__ ((packed)) sniff_subrating_cp;
+#define SNIFF_SUBRATING_CP_SIZE 8
+
+/* Host Controller and Baseband */
+#define OGF_HOST_CTL		0x03
+
+#define OCF_SET_EVENT_MASK		0x0001
+typedef struct {
+	uint8_t		mask[8];
+} __attribute__ ((packed)) set_event_mask_cp;
+#define SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_RESET			0x0003
+
+#define OCF_SET_EVENT_FLT		0x0005
+typedef struct {
+	uint8_t		flt_type;
+	uint8_t		cond_type;
+	uint8_t		condition[0];
+} __attribute__ ((packed)) set_event_flt_cp;
+#define SET_EVENT_FLT_CP_SIZE 2
+
+/* Filter types */
+#define FLT_CLEAR_ALL			0x00
+#define FLT_INQ_RESULT			0x01
+#define FLT_CONN_SETUP			0x02
+/* INQ_RESULT Condition types */
+#define INQ_RESULT_RETURN_ALL		0x00
+#define INQ_RESULT_RETURN_CLASS		0x01
+#define INQ_RESULT_RETURN_BDADDR	0x02
+/* CONN_SETUP Condition types */
+#define CONN_SETUP_ALLOW_ALL		0x00
+#define CONN_SETUP_ALLOW_CLASS		0x01
+#define CONN_SETUP_ALLOW_BDADDR		0x02
+/* CONN_SETUP Conditions */
+#define CONN_SETUP_AUTO_OFF		0x01
+#define CONN_SETUP_AUTO_ON		0x02
+
+#define OCF_FLUSH			0x0008
+
+#define OCF_READ_PIN_TYPE		0x0009
+typedef struct {
+	uint8_t		status;
+	uint8_t		pin_type;
+} __attribute__ ((packed)) read_pin_type_rp;
+#define READ_PIN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_PIN_TYPE		0x000A
+typedef struct {
+	uint8_t		pin_type;
+} __attribute__ ((packed)) write_pin_type_cp;
+#define WRITE_PIN_TYPE_CP_SIZE 1
+
+#define OCF_CREATE_NEW_UNIT_KEY		0x000B
+
+#define OCF_READ_STORED_LINK_KEY	0x000D
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		read_all;
+} __attribute__ ((packed)) read_stored_link_key_cp;
+#define READ_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+	uint8_t		status;
+	uint16_t	max_keys;
+	uint16_t	num_keys;
+} __attribute__ ((packed)) read_stored_link_key_rp;
+#define READ_STORED_LINK_KEY_RP_SIZE 5
+
+#define OCF_WRITE_STORED_LINK_KEY	0x0011
+typedef struct {
+	uint8_t		num_keys;
+	/* variable length part */
+} __attribute__ ((packed)) write_stored_link_key_cp;
+#define WRITE_STORED_LINK_KEY_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+	uint8_t		num_keys;
+} __attribute__ ((packed)) write_stored_link_key_rp;
+#define READ_WRITE_LINK_KEY_RP_SIZE 2
+
+#define OCF_DELETE_STORED_LINK_KEY	0x0012
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		delete_all;
+} __attribute__ ((packed)) delete_stored_link_key_cp;
+#define DELETE_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+	uint8_t		status;
+	uint16_t	num_keys;
+} __attribute__ ((packed)) delete_stored_link_key_rp;
+#define DELETE_STORED_LINK_KEY_RP_SIZE 3
+
+#define HCI_MAX_NAME_LENGTH		248
+
+#define OCF_CHANGE_LOCAL_NAME		0x0013
+typedef struct {
+	uint8_t		name[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) change_local_name_cp;
+#define CHANGE_LOCAL_NAME_CP_SIZE 248
+
+#define OCF_READ_LOCAL_NAME		0x0014
+typedef struct {
+	uint8_t		status;
+	uint8_t		name[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) read_local_name_rp;
+#define READ_LOCAL_NAME_RP_SIZE 249
+
+#define OCF_READ_CONN_ACCEPT_TIMEOUT	0x0015
+typedef struct {
+	uint8_t		status;
+	uint16_t	timeout;
+} __attribute__ ((packed)) read_conn_accept_timeout_rp;
+#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_CONN_ACCEPT_TIMEOUT	0x0016
+typedef struct {
+	uint16_t	timeout;
+} __attribute__ ((packed)) write_conn_accept_timeout_cp;
+#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_PAGE_TIMEOUT		0x0017
+typedef struct {
+	uint8_t		status;
+	uint16_t	timeout;
+} __attribute__ ((packed)) read_page_timeout_rp;
+#define READ_PAGE_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_PAGE_TIMEOUT		0x0018
+typedef struct {
+	uint16_t	timeout;
+} __attribute__ ((packed)) write_page_timeout_cp;
+#define WRITE_PAGE_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_SCAN_ENABLE		0x0019
+typedef struct {
+	uint8_t		status;
+	uint8_t		enable;
+} __attribute__ ((packed)) read_scan_enable_rp;
+#define READ_SCAN_ENABLE_RP_SIZE 2
+
+#define OCF_WRITE_SCAN_ENABLE		0x001A
+	#define SCAN_DISABLED		0x00
+	#define SCAN_INQUIRY		0x01
+	#define SCAN_PAGE		0x02
+
+#define OCF_READ_PAGE_ACTIVITY		0x001B
+typedef struct {
+	uint8_t		status;
+	uint16_t	interval;
+	uint16_t	window;
+} __attribute__ ((packed)) read_page_activity_rp;
+#define READ_PAGE_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_PAGE_ACTIVITY		0x001C
+typedef struct {
+	uint16_t	interval;
+	uint16_t	window;
+} __attribute__ ((packed)) write_page_activity_cp;
+#define WRITE_PAGE_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_INQ_ACTIVITY		0x001D
+typedef struct {
+	uint8_t		status;
+	uint16_t	interval;
+	uint16_t	window;
+} __attribute__ ((packed)) read_inq_activity_rp;
+#define READ_INQ_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_INQ_ACTIVITY		0x001E
+typedef struct {
+	uint16_t	interval;
+	uint16_t	window;
+} __attribute__ ((packed)) write_inq_activity_cp;
+#define WRITE_INQ_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_AUTH_ENABLE		0x001F
+
+#define OCF_WRITE_AUTH_ENABLE		0x0020
+	#define AUTH_DISABLED		0x00
+	#define AUTH_ENABLED		0x01
+
+#define OCF_READ_ENCRYPT_MODE		0x0021
+
+#define OCF_WRITE_ENCRYPT_MODE		0x0022
+	#define ENCRYPT_DISABLED	0x00
+	#define ENCRYPT_P2P		0x01
+	#define ENCRYPT_BOTH		0x02
+
+#define OCF_READ_CLASS_OF_DEV		0x0023
+typedef struct {
+	uint8_t		status;
+	uint8_t		dev_class[3];
+} __attribute__ ((packed)) read_class_of_dev_rp;
+#define READ_CLASS_OF_DEV_RP_SIZE 4
+
+#define OCF_WRITE_CLASS_OF_DEV		0x0024
+typedef struct {
+	uint8_t		dev_class[3];
+} __attribute__ ((packed)) write_class_of_dev_cp;
+#define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
+#define OCF_READ_VOICE_SETTING		0x0025
+typedef struct {
+	uint8_t		status;
+	uint16_t	voice_setting;
+} __attribute__ ((packed)) read_voice_setting_rp;
+#define READ_VOICE_SETTING_RP_SIZE 3
+
+#define OCF_WRITE_VOICE_SETTING		0x0026
+typedef struct {
+	uint16_t	voice_setting;
+} __attribute__ ((packed)) write_voice_setting_cp;
+#define WRITE_VOICE_SETTING_CP_SIZE 2
+
+#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT	0x0027
+
+#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT	0x0028
+
+#define OCF_READ_NUM_BROADCAST_RETRANS	0x0029
+
+#define OCF_WRITE_NUM_BROADCAST_RETRANS	0x002A
+
+#define OCF_READ_HOLD_MODE_ACTIVITY	0x002B
+
+#define OCF_WRITE_HOLD_MODE_ACTIVITY	0x002C
+
+#define OCF_READ_TRANSMIT_POWER_LEVEL	0x002D
+typedef struct {
+	uint16_t	handle;
+	uint8_t		type;
+} __attribute__ ((packed)) read_transmit_power_level_cp;
+#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	int8_t		level;
+} __attribute__ ((packed)) read_transmit_power_level_rp;
+#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4
+
+#define OCF_READ_SYNC_FLOW_ENABLE	0x002E
+
+#define OCF_WRITE_SYNC_FLOW_ENABLE	0x002F
+
+#define OCF_SET_CONTROLLER_TO_HOST_FC	0x0031
+
+#define OCF_HOST_BUFFER_SIZE		0x0033
+typedef struct {
+	uint16_t	acl_mtu;
+	uint8_t		sco_mtu;
+	uint16_t	acl_max_pkt;
+	uint16_t	sco_max_pkt;
+} __attribute__ ((packed)) host_buffer_size_cp;
+#define HOST_BUFFER_SIZE_CP_SIZE 7
+
+#define OCF_HOST_NUM_COMP_PKTS		0x0035
+typedef struct {
+	uint8_t		num_hndl;
+	/* variable length part */
+} __attribute__ ((packed)) host_num_comp_pkts_cp;
+#define HOST_NUM_COMP_PKTS_CP_SIZE 1
+
+#define OCF_READ_LINK_SUPERVISION_TIMEOUT	0x0036
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint16_t	timeout;
+} __attribute__ ((packed)) read_link_supervision_timeout_rp;
+#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT	0x0037
+typedef struct {
+	uint16_t	handle;
+	uint16_t	timeout;
+} __attribute__ ((packed)) write_link_supervision_timeout_cp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) write_link_supervision_timeout_rp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3
+
+#define OCF_READ_NUM_SUPPORTED_IAC	0x0038
+
+#define MAX_IAC_LAP 0x40
+#define OCF_READ_CURRENT_IAC_LAP	0x0039
+typedef struct {
+	uint8_t		status;
+	uint8_t		num_current_iac;
+	uint8_t		lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) read_current_iac_lap_rp;
+#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP
+
+#define OCF_WRITE_CURRENT_IAC_LAP	0x003A
+typedef struct {
+	uint8_t		num_current_iac;
+	uint8_t		lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) write_current_iac_lap_cp;
+#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP
+
+#define OCF_READ_PAGE_SCAN_PERIOD_MODE	0x003B
+
+#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE	0x003C
+
+#define OCF_READ_PAGE_SCAN_MODE		0x003D
+
+#define OCF_WRITE_PAGE_SCAN_MODE	0x003E
+
+#define OCF_SET_AFH_CLASSIFICATION	0x003F
+typedef struct {
+	uint8_t		map[10];
+} __attribute__ ((packed)) set_afh_classification_cp;
+#define SET_AFH_CLASSIFICATION_CP_SIZE 10
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) set_afh_classification_rp;
+#define SET_AFH_CLASSIFICATION_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_SCAN_TYPE	0x0042
+typedef struct {
+	uint8_t		status;
+	uint8_t		type;
+} __attribute__ ((packed)) read_inquiry_scan_type_rp;
+#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_SCAN_TYPE	0x0043
+typedef struct {
+	uint8_t		type;
+} __attribute__ ((packed)) write_inquiry_scan_type_cp;
+#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_inquiry_scan_type_rp;
+#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_MODE		0x0044
+typedef struct {
+	uint8_t		status;
+	uint8_t		mode;
+} __attribute__ ((packed)) read_inquiry_mode_rp;
+#define READ_INQUIRY_MODE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_MODE		0x0045
+typedef struct {
+	uint8_t		mode;
+} __attribute__ ((packed)) write_inquiry_mode_cp;
+#define WRITE_INQUIRY_MODE_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_inquiry_mode_rp;
+#define WRITE_INQUIRY_MODE_RP_SIZE 1
+
+#define OCF_READ_PAGE_SCAN_TYPE		0x0046
+
+#define OCF_WRITE_PAGE_SCAN_TYPE	0x0047
+	#define PAGE_SCAN_TYPE_STANDARD		0x00
+	#define PAGE_SCAN_TYPE_INTERLACED	0x01
+
+#define OCF_READ_AFH_MODE		0x0048
+typedef struct {
+	uint8_t		status;
+	uint8_t		mode;
+} __attribute__ ((packed)) read_afh_mode_rp;
+#define READ_AFH_MODE_RP_SIZE 2
+
+#define OCF_WRITE_AFH_MODE		0x0049
+typedef struct {
+	uint8_t		mode;
+} __attribute__ ((packed)) write_afh_mode_cp;
+#define WRITE_AFH_MODE_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_afh_mode_rp;
+#define WRITE_AFH_MODE_RP_SIZE 1
+
+#define HCI_MAX_EIR_LENGTH		240
+
+#define OCF_READ_EXT_INQUIRY_RESPONSE	0x0051
+typedef struct {
+	uint8_t		status;
+	uint8_t		fec;
+	uint8_t		data[HCI_MAX_EIR_LENGTH];
+} __attribute__ ((packed)) read_ext_inquiry_response_rp;
+#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242
+
+#define OCF_WRITE_EXT_INQUIRY_RESPONSE	0x0052
+typedef struct {
+	uint8_t		fec;
+	uint8_t		data[HCI_MAX_EIR_LENGTH];
+} __attribute__ ((packed)) write_ext_inquiry_response_cp;
+#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_ext_inquiry_response_rp;
+#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1
+
+#define OCF_REFRESH_ENCRYPTION_KEY	0x0053
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) refresh_encryption_key_cp;
+#define REFRESH_ENCRYPTION_KEY_CP_SIZE 2
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) refresh_encryption_key_rp;
+#define REFRESH_ENCRYPTION_KEY_RP_SIZE 1
+
+#define OCF_READ_SIMPLE_PAIRING_MODE	0x0055
+typedef struct {
+	uint8_t		status;
+	uint8_t		mode;
+} __attribute__ ((packed)) read_simple_pairing_mode_rp;
+#define READ_SIMPLE_PAIRING_MODE_RP_SIZE 2
+
+#define OCF_WRITE_SIMPLE_PAIRING_MODE	0x0056
+typedef struct {
+	uint8_t		mode;
+} __attribute__ ((packed)) write_simple_pairing_mode_cp;
+#define WRITE_SIMPLE_PAIRING_MODE_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_simple_pairing_mode_rp;
+#define WRITE_SIMPLE_PAIRING_MODE_RP_SIZE 1
+
+#define OCF_READ_LOCAL_OOB_DATA		0x0057
+typedef struct {
+	uint8_t		status;
+	uint8_t		hash[16];
+	uint8_t		randomizer[16];
+} __attribute__ ((packed)) read_local_oob_data_rp;
+#define READ_LOCAL_OOB_DATA_RP_SIZE 33
+
+#define OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL	0x0058
+typedef struct {
+	uint8_t		status;
+	int8_t		level;
+} __attribute__ ((packed)) read_inq_response_tx_power_level_rp;
+#define READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE 2
+
+#define OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL	0x0058
+typedef struct {
+	uint8_t		status;
+	int8_t		level;
+} __attribute__ ((packed)) read_inquiry_transmit_power_level_rp;
+#define READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL	0x0059
+typedef struct {
+	int8_t		level;
+} __attribute__ ((packed)) write_inquiry_transmit_power_level_cp;
+#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_inquiry_transmit_power_level_rp;
+#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 1
+
+#define OCF_READ_DEFAULT_ERROR_DATA_REPORTING	0x005A
+typedef struct {
+	uint8_t		status;
+	uint8_t		reporting;
+} __attribute__ ((packed)) read_default_error_data_reporting_rp;
+#define READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 2
+
+#define OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING	0x005B
+typedef struct {
+	uint8_t		reporting;
+} __attribute__ ((packed)) write_default_error_data_reporting_cp;
+#define WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_default_error_data_reporting_rp;
+#define WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 1
+
+#define OCF_ENHANCED_FLUSH		0x005F
+typedef struct {
+	uint16_t	handle;
+	uint8_t		type;
+} __attribute__ ((packed)) enhanced_flush_cp;
+#define ENHANCED_FLUSH_CP_SIZE 3
+
+#define OCF_SEND_KEYPRESS_NOTIFY	0x0060
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		type;
+} __attribute__ ((packed)) send_keypress_notify_cp;
+#define SEND_KEYPRESS_NOTIFY_CP_SIZE 7
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) send_keypress_notify_rp;
+#define SEND_KEYPRESS_NOTIFY_RP_SIZE 1
+
+#define OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT	 0x0061
+typedef struct {
+	uint8_t		status;
+	uint16_t	timeout;
+} __attribute__ ((packed)) read_log_link_accept_timeout_rp;
+#define READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT	0x0062
+typedef struct {
+	uint16_t	timeout;
+} __attribute__ ((packed)) write_log_link_accept_timeout_cp;
+#define WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_SET_EVENT_MASK_PAGE_2	0x0063
+
+#define OCF_READ_LOCATION_DATA		0x0064
+
+#define OCF_WRITE_LOCATION_DATA	0x0065
+
+#define OCF_READ_FLOW_CONTROL_MODE	0x0066
+
+#define OCF_WRITE_FLOW_CONTROL_MODE	0x0067
+
+#define OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL	0x0068
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	int8_t		level_gfsk;
+	int8_t		level_dqpsk;
+	int8_t		level_8dpsk;
+} __attribute__ ((packed)) read_enhanced_transmit_power_level_rp;
+#define READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE 6
+
+#define OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT	0x0069
+typedef struct {
+	uint8_t		status;
+	uint32_t	timeout;
+} __attribute__ ((packed)) read_best_effort_flush_timeout_rp;
+#define READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT	0x006A
+typedef struct {
+	uint16_t	handle;
+	uint32_t	timeout;
+} __attribute__ ((packed)) write_best_effort_flush_timeout_cp;
+#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE 6
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_best_effort_flush_timeout_rp;
+#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 1
+
+#define OCF_READ_LE_HOST_SUPPORTED	0x006C
+typedef struct {
+	uint8_t		status;
+	uint8_t		le;
+	uint8_t		simul;
+} __attribute__ ((packed)) read_le_host_supported_rp;
+#define READ_LE_HOST_SUPPORTED_RP_SIZE 3
+
+#define OCF_WRITE_LE_HOST_SUPPORTED	0x006D
+typedef struct {
+	uint8_t		le;
+	uint8_t		simul;
+} __attribute__ ((packed)) write_le_host_supported_cp;
+#define WRITE_LE_HOST_SUPPORTED_CP_SIZE 2
+
+/* Informational Parameters */
+#define OGF_INFO_PARAM		0x04
+
+#define OCF_READ_LOCAL_VERSION		0x0001
+typedef struct {
+	uint8_t		status;
+	uint8_t		hci_ver;
+	uint16_t	hci_rev;
+	uint8_t		lmp_ver;
+	uint16_t	manufacturer;
+	uint16_t	lmp_subver;
+} __attribute__ ((packed)) read_local_version_rp;
+#define READ_LOCAL_VERSION_RP_SIZE 9
+
+#define OCF_READ_LOCAL_COMMANDS		0x0002
+typedef struct {
+	uint8_t		status;
+	uint8_t		commands[64];
+} __attribute__ ((packed)) read_local_commands_rp;
+#define READ_LOCAL_COMMANDS_RP_SIZE 65
+
+#define OCF_READ_LOCAL_FEATURES		0x0003
+typedef struct {
+	uint8_t		status;
+	uint8_t		features[8];
+} __attribute__ ((packed)) read_local_features_rp;
+#define READ_LOCAL_FEATURES_RP_SIZE 9
+
+#define OCF_READ_LOCAL_EXT_FEATURES	0x0004
+typedef struct {
+	uint8_t		page_num;
+} __attribute__ ((packed)) read_local_ext_features_cp;
+#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+	uint8_t		page_num;
+	uint8_t		max_page_num;
+	uint8_t		features[8];
+} __attribute__ ((packed)) read_local_ext_features_rp;
+#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11
+
+#define OCF_READ_BUFFER_SIZE		0x0005
+typedef struct {
+	uint8_t		status;
+	uint16_t	acl_mtu;
+	uint8_t		sco_mtu;
+	uint16_t	acl_max_pkt;
+	uint16_t	sco_max_pkt;
+} __attribute__ ((packed)) read_buffer_size_rp;
+#define READ_BUFFER_SIZE_RP_SIZE 8
+
+#define OCF_READ_COUNTRY_CODE		0x0007
+
+#define OCF_READ_BD_ADDR		0x0009
+typedef struct {
+	uint8_t		status;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) read_bd_addr_rp;
+#define READ_BD_ADDR_RP_SIZE 7
+
+#define OCF_READ_DATA_BLOCK_SIZE	0x000A
+typedef struct {
+	uint8_t		status;
+	uint16_t	max_acl_len;
+	uint16_t	data_block_len;
+	uint16_t	num_blocks;
+} __attribute__ ((packed)) read_data_block_size_rp;
+
+/* Status params */
+#define OGF_STATUS_PARAM	0x05
+
+#define OCF_READ_FAILED_CONTACT_COUNTER		0x0001
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		counter;
+} __attribute__ ((packed)) read_failed_contact_counter_rp;
+#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_RESET_FAILED_CONTACT_COUNTER	0x0002
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) reset_failed_contact_counter_rp;
+#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 3
+
+#define OCF_READ_LINK_QUALITY		0x0003
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		link_quality;
+} __attribute__ ((packed)) read_link_quality_rp;
+#define READ_LINK_QUALITY_RP_SIZE 4
+
+#define OCF_READ_RSSI			0x0005
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	int8_t		rssi;
+} __attribute__ ((packed)) read_rssi_rp;
+#define READ_RSSI_RP_SIZE 4
+
+#define OCF_READ_AFH_MAP		0x0006
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		mode;
+	uint8_t		map[10];
+} __attribute__ ((packed)) read_afh_map_rp;
+#define READ_AFH_MAP_RP_SIZE 14
+
+#define OCF_READ_CLOCK			0x0007
+typedef struct {
+	uint16_t	handle;
+	uint8_t		which_clock;
+} __attribute__ ((packed)) read_clock_cp;
+#define READ_CLOCK_CP_SIZE 3
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint32_t	clock;
+	uint16_t	accuracy;
+} __attribute__ ((packed)) read_clock_rp;
+#define READ_CLOCK_RP_SIZE 9
+
+#define OCF_READ_LOCAL_AMP_INFO	0x0009
+typedef struct {
+	uint8_t		status;
+	uint8_t		amp_status;
+	uint32_t	total_bandwidth;
+	uint32_t	max_guaranteed_bandwidth;
+	uint32_t	min_latency;
+	uint32_t	max_pdu_size;
+	uint8_t		controller_type;
+	uint16_t	pal_caps;
+	uint16_t	max_amp_assoc_length;
+	uint32_t	max_flush_timeout;
+	uint32_t	best_effort_flush_timeout;
+} __attribute__ ((packed)) read_local_amp_info_rp;
+#define READ_LOCAL_AMP_INFO_RP_SIZE 31
+
+#define OCF_READ_LOCAL_AMP_ASSOC	0x000A
+typedef struct {
+	uint8_t		handle;
+	uint16_t	length_so_far;
+	uint16_t	assoc_length;
+} __attribute__ ((packed)) read_local_amp_assoc_cp;
+#define READ_LOCAL_AMP_ASSOC_CP_SIZE 5
+typedef struct {
+	uint8_t		status;
+	uint8_t		handle;
+	uint16_t	length;
+	uint8_t		fragment[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) read_local_amp_assoc_rp;
+#define READ_LOCAL_AMP_ASSOC_RP_SIZE 252
+
+#define OCF_WRITE_REMOTE_AMP_ASSOC	0x000B
+typedef struct {
+	uint8_t		handle;
+	uint16_t	length_so_far;
+	uint16_t	remaining_length;
+	uint8_t		fragment[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) write_remote_amp_assoc_cp;
+#define WRITE_REMOTE_AMP_ASSOC_CP_SIZE 253
+typedef struct {
+	uint8_t		status;
+	uint8_t		handle;
+} __attribute__ ((packed)) write_remote_amp_assoc_rp;
+#define WRITE_REMOTE_AMP_ASSOC_RP_SIZE 2
+
+/* Testing commands */
+#define OGF_TESTING_CMD		0x3e
+
+#define OCF_READ_LOOPBACK_MODE			0x0001
+
+#define OCF_WRITE_LOOPBACK_MODE			0x0002
+
+#define OCF_ENABLE_DEVICE_UNDER_TEST_MODE	0x0003
+
+#define OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE	0x0004
+typedef struct {
+	uint8_t		mode;
+} __attribute__ ((packed)) write_simple_pairing_debug_mode_cp;
+#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE 1
+typedef struct {
+	uint8_t		status;
+} __attribute__ ((packed)) write_simple_pairing_debug_mode_rp;
+#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE 1
+
+/* LE commands */
+#define OGF_LE_CTL		0x08
+
+#define OCF_LE_SET_EVENT_MASK			0x0001
+typedef struct {
+	uint8_t		mask[8];
+} __attribute__ ((packed)) le_set_event_mask_cp;
+#define LE_SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_LE_READ_BUFFER_SIZE			0x0002
+typedef struct {
+	uint8_t		status;
+	uint16_t	pkt_len;
+	uint8_t		max_pkt;
+} __attribute__ ((packed)) le_read_buffer_size_rp;
+#define LE_READ_BUFFER_SIZE_RP_SIZE 4
+
+#define OCF_LE_READ_LOCAL_SUPPORTED_FEATURES	0x0003
+typedef struct {
+	uint8_t		status;
+	uint8_t		features[8];
+} __attribute__ ((packed)) le_read_local_supported_features_rp;
+#define LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE 9
+
+#define OCF_LE_SET_RANDOM_ADDRESS		0x0005
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) le_set_random_address_cp;
+#define LE_SET_RANDOM_ADDRESS_CP_SIZE 6
+
+#define OCF_LE_SET_ADVERTISING_PARAMETERS	0x0006
+typedef struct {
+	uint16_t	min_interval;
+	uint16_t	max_interval;
+	uint8_t		advtype;
+	uint8_t		own_bdaddr_type;
+	uint8_t		direct_bdaddr_type;
+	bdaddr_t	direct_bdaddr;
+	uint8_t		chan_map;
+	uint8_t		filter;
+} __attribute__ ((packed)) le_set_advertising_parameters_cp;
+#define LE_SET_ADVERTISING_PARAMETERS_CP_SIZE 15
+
+#define OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER	0x0007
+typedef struct {
+	uint8_t		status;
+	int8_t		level;
+} __attribute__ ((packed)) le_read_advertising_channel_tx_power_rp;
+#define LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE 2
+
+#define OCF_LE_SET_ADVERTISING_DATA		0x0008
+typedef struct {
+	uint8_t		length;
+	uint8_t		data[31];
+} __attribute__ ((packed)) le_set_advertising_data_cp;
+#define LE_SET_ADVERTISING_DATA_CP_SIZE 32
+
+#define OCF_LE_SET_SCAN_RESPONSE_DATA		0x0009
+typedef struct {
+	uint8_t		length;
+	uint8_t		data[31];
+} __attribute__ ((packed)) le_set_scan_response_data_cp;
+#define LE_SET_SCAN_RESPONSE_DATA_CP_SIZE 32
+
+#define OCF_LE_SET_ADVERTISE_ENABLE		0x000A
+typedef struct {
+	uint8_t		enable;
+} __attribute__ ((packed)) le_set_advertise_enable_cp;
+#define LE_SET_ADVERTISE_ENABLE_CP_SIZE 1
+
+#define OCF_LE_SET_SCAN_PARAMETERS		0x000B
+typedef struct {
+	uint8_t		type;
+	uint16_t	interval;
+	uint16_t	window;
+	uint8_t		own_bdaddr_type;
+	uint8_t		filter;
+} __attribute__ ((packed)) le_set_scan_parameters_cp;
+#define LE_SET_SCAN_PARAMETERS_CP_SIZE 7
+
+#define OCF_LE_SET_SCAN_ENABLE			0x000C
+typedef struct {
+	uint8_t		enable;
+	uint8_t		filter_dup;
+} __attribute__ ((packed)) le_set_scan_enable_cp;
+#define LE_SET_SCAN_ENABLE_CP_SIZE 2
+
+#define OCF_LE_CREATE_CONN			0x000D
+typedef struct {
+	uint16_t	interval;
+	uint16_t	window;
+	uint8_t		initiator_filter;
+	uint8_t		peer_bdaddr_type;
+	bdaddr_t	peer_bdaddr;
+	uint8_t		own_bdaddr_type;
+	uint16_t	min_interval;
+	uint16_t	max_interval;
+	uint16_t	latency;
+	uint16_t	supervision_timeout;
+	uint16_t	min_ce_length;
+	uint16_t	max_ce_length;
+} __attribute__ ((packed)) le_create_connection_cp;
+#define LE_CREATE_CONN_CP_SIZE 25
+
+#define OCF_LE_CREATE_CONN_CANCEL		0x000E
+
+#define OCF_LE_READ_WHITE_LIST_SIZE		0x000F
+typedef struct {
+	uint8_t		status;
+	uint8_t		size;
+} __attribute__ ((packed)) le_read_white_list_size_rp;
+#define LE_READ_WHITE_LIST_SIZE_RP_SIZE 2
+
+#define OCF_LE_CLEAR_WHITE_LIST			0x0010
+
+#define OCF_LE_ADD_DEVICE_TO_WHITE_LIST		0x0011
+typedef struct {
+	uint8_t		bdaddr_type;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) le_add_device_to_white_list_cp;
+#define LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE 7
+
+#define OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST	0x0012
+typedef struct {
+	uint8_t		bdaddr_type;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) le_remove_device_from_white_list_cp;
+#define LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE 7
+
+#define OCF_LE_CONN_UPDATE			0x0013
+typedef struct {
+	uint16_t	handle;
+	uint16_t	min_interval;
+	uint16_t	max_interval;
+	uint16_t	latency;
+	uint16_t	supervision_timeout;
+	uint16_t	min_ce_length;
+	uint16_t	max_ce_length;
+} __attribute__ ((packed)) le_connection_update_cp;
+#define LE_CONN_UPDATE_CP_SIZE 14
+
+#define OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION	0x0014
+typedef struct {
+	uint8_t		map[5];
+} __attribute__ ((packed)) le_set_host_channel_classification_cp;
+#define LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE 5
+
+#define OCF_LE_READ_CHANNEL_MAP			0x0015
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) le_read_channel_map_cp;
+#define LE_READ_CHANNEL_MAP_CP_SIZE 2
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		map[5];
+} __attribute__ ((packed)) le_read_channel_map_rp;
+#define LE_READ_CHANNEL_MAP_RP_SIZE 8
+
+#define OCF_LE_READ_REMOTE_USED_FEATURES	0x0016
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) le_read_remote_used_features_cp;
+#define LE_READ_REMOTE_USED_FEATURES_CP_SIZE 2
+
+#define OCF_LE_ENCRYPT				0x0017
+typedef struct {
+	uint8_t		key[16];
+	uint8_t		plaintext[16];
+} __attribute__ ((packed)) le_encrypt_cp;
+#define LE_ENCRYPT_CP_SIZE 32
+typedef struct {
+	uint8_t		status;
+	uint8_t		data[16];
+} __attribute__ ((packed)) le_encrypt_rp;
+#define LE_ENCRYPT_RP_SIZE 17
+
+#define OCF_LE_RAND				0x0018
+typedef struct {
+	uint8_t		status;
+	uint64_t	random;
+} __attribute__ ((packed)) le_rand_rp;
+#define LE_RAND_RP_SIZE 9
+
+#define OCF_LE_START_ENCRYPTION			0x0019
+typedef struct {
+	uint16_t	handle;
+	uint64_t	random;
+	uint16_t	diversifier;
+	uint8_t		key[16];
+} __attribute__ ((packed)) le_start_encryption_cp;
+#define LE_START_ENCRYPTION_CP_SIZE 28
+
+#define OCF_LE_LTK_REPLY			0x001A
+typedef struct {
+	uint16_t	handle;
+	uint8_t		key[16];
+} __attribute__ ((packed)) le_ltk_reply_cp;
+#define LE_LTK_REPLY_CP_SIZE 18
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) le_ltk_reply_rp;
+#define LE_LTK_REPLY_RP_SIZE 3
+
+#define OCF_LE_LTK_NEG_REPLY			0x001B
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) le_ltk_neg_reply_cp;
+#define LE_LTK_NEG_REPLY_CP_SIZE 2
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) le_ltk_neg_reply_rp;
+#define LE_LTK_NEG_REPLY_RP_SIZE 3
+
+#define OCF_LE_READ_SUPPORTED_STATES		0x001C
+typedef struct {
+	uint8_t		status;
+	uint64_t	states;
+} __attribute__ ((packed)) le_read_supported_states_rp;
+#define LE_READ_SUPPORTED_STATES_RP_SIZE 9
+
+#define OCF_LE_RECEIVER_TEST			0x001D
+typedef struct {
+	uint8_t		frequency;
+} __attribute__ ((packed)) le_receiver_test_cp;
+#define LE_RECEIVER_TEST_CP_SIZE 1
+
+#define OCF_LE_TRANSMITTER_TEST			0x001E
+typedef struct {
+	uint8_t		frequency;
+	uint8_t		length;
+	uint8_t		payload;
+} __attribute__ ((packed)) le_transmitter_test_cp;
+#define LE_TRANSMITTER_TEST_CP_SIZE 3
+
+#define OCF_LE_TEST_END				0x001F
+typedef struct {
+	uint8_t		status;
+	uint16_t	num_pkts;
+} __attribute__ ((packed)) le_test_end_rp;
+#define LE_TEST_END_RP_SIZE 3
+
+#define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST	0x0027
+typedef struct {
+	uint8_t		bdaddr_type;
+	bdaddr_t	bdaddr;
+	uint8_t		peer_irk[16];
+	uint8_t		local_irk[16];
+} __attribute__ ((packed)) le_add_device_to_resolv_list_cp;
+#define LE_ADD_DEVICE_TO_RESOLV_LIST_CP_SIZE 39
+
+#define OCF_LE_REMOVE_DEVICE_FROM_RESOLV_LIST	0x0028
+typedef struct {
+	uint8_t		bdaddr_type;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) le_remove_device_from_resolv_list_cp;
+#define LE_REMOVE_DEVICE_FROM_RESOLV_LIST_CP_SIZE 7
+
+#define OCF_LE_CLEAR_RESOLV_LIST		0x0029
+
+#define OCF_LE_READ_RESOLV_LIST_SIZE		0x002A
+typedef struct {
+	uint8_t		status;
+	uint8_t		size;
+} __attribute__ ((packed)) le_read_resolv_list_size_rp;
+#define LE_READ_RESOLV_LIST_SIZE_RP_SIZE 2
+
+#define OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE	0x002D
+typedef struct {
+	uint8_t		enable;
+} __attribute__ ((packed)) le_set_address_resolution_enable_cp;
+#define LE_SET_ADDRESS_RESOLUTION_ENABLE_CP_SIZE 1
+
+/* Vendor specific commands */
+#define OGF_VENDOR_CMD		0x3f
+
+/* ---- HCI Events ---- */
+
+#define EVT_INQUIRY_COMPLETE		0x01
+
+#define EVT_INQUIRY_RESULT		0x02
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_period_mode;
+	uint8_t		pscan_mode;
+	uint8_t		dev_class[3];
+	uint16_t	clock_offset;
+} __attribute__ ((packed)) inquiry_info;
+#define INQUIRY_INFO_SIZE 14
+
+#define EVT_CONN_COMPLETE		0x03
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	bdaddr_t	bdaddr;
+	uint8_t		link_type;
+	uint8_t		encr_mode;
+} __attribute__ ((packed)) evt_conn_complete;
+#define EVT_CONN_COMPLETE_SIZE 11
+
+#define EVT_CONN_REQUEST		0x04
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		dev_class[3];
+	uint8_t		link_type;
+} __attribute__ ((packed)) evt_conn_request;
+#define EVT_CONN_REQUEST_SIZE 10
+
+#define EVT_DISCONN_COMPLETE		0x05
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		reason;
+} __attribute__ ((packed)) evt_disconn_complete;
+#define EVT_DISCONN_COMPLETE_SIZE 4
+
+#define EVT_AUTH_COMPLETE		0x06
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) evt_auth_complete;
+#define EVT_AUTH_COMPLETE_SIZE 3
+
+#define EVT_REMOTE_NAME_REQ_COMPLETE	0x07
+typedef struct {
+	uint8_t		status;
+	bdaddr_t	bdaddr;
+	uint8_t		name[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) evt_remote_name_req_complete;
+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
+
+#define EVT_ENCRYPT_CHANGE		0x08
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		encrypt;
+} __attribute__ ((packed)) evt_encrypt_change;
+#define EVT_ENCRYPT_CHANGE_SIZE 4
+
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE	0x09
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+}  __attribute__ ((packed)) evt_change_conn_link_key_complete;
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3
+
+#define EVT_MASTER_LINK_KEY_COMPLETE		0x0A
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		key_flag;
+} __attribute__ ((packed)) evt_master_link_key_complete;
+#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4
+
+#define EVT_READ_REMOTE_FEATURES_COMPLETE	0x0B
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		features[8];
+} __attribute__ ((packed)) evt_read_remote_features_complete;
+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_READ_REMOTE_VERSION_COMPLETE	0x0C
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		lmp_ver;
+	uint16_t	manufacturer;
+	uint16_t	lmp_subver;
+} __attribute__ ((packed)) evt_read_remote_version_complete;
+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
+
+#define EVT_QOS_SETUP_COMPLETE		0x0D
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		flags;			/* Reserved */
+	hci_qos		qos;
+} __attribute__ ((packed)) evt_qos_setup_complete;
+#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE)
+
+#define EVT_CMD_COMPLETE		0x0E
+typedef struct {
+	uint8_t		ncmd;
+	uint16_t	opcode;
+} __attribute__ ((packed)) evt_cmd_complete;
+#define EVT_CMD_COMPLETE_SIZE 3
+
+#define EVT_CMD_STATUS			0x0F
+typedef struct {
+	uint8_t		status;
+	uint8_t		ncmd;
+	uint16_t	opcode;
+} __attribute__ ((packed)) evt_cmd_status;
+#define EVT_CMD_STATUS_SIZE 4
+
+#define EVT_HARDWARE_ERROR		0x10
+typedef struct {
+	uint8_t		code;
+} __attribute__ ((packed)) evt_hardware_error;
+#define EVT_HARDWARE_ERROR_SIZE 1
+
+#define EVT_FLUSH_OCCURRED		0x11
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) evt_flush_occured;
+#define EVT_FLUSH_OCCURRED_SIZE 2
+
+#define EVT_ROLE_CHANGE			0x12
+typedef struct {
+	uint8_t		status;
+	bdaddr_t	bdaddr;
+	uint8_t		role;
+} __attribute__ ((packed)) evt_role_change;
+#define EVT_ROLE_CHANGE_SIZE 8
+
+#define EVT_NUM_COMP_PKTS		0x13
+typedef struct {
+	uint8_t		num_hndl;
+	/* variable length part */
+} __attribute__ ((packed)) evt_num_comp_pkts;
+#define EVT_NUM_COMP_PKTS_SIZE 1
+
+#define EVT_MODE_CHANGE			0x14
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		mode;
+	uint16_t	interval;
+} __attribute__ ((packed)) evt_mode_change;
+#define EVT_MODE_CHANGE_SIZE 6
+
+#define EVT_RETURN_LINK_KEYS		0x15
+typedef struct {
+	uint8_t		num_keys;
+	/* variable length part */
+} __attribute__ ((packed)) evt_return_link_keys;
+#define EVT_RETURN_LINK_KEYS_SIZE 1
+
+#define EVT_PIN_CODE_REQ		0x16
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) evt_pin_code_req;
+#define EVT_PIN_CODE_REQ_SIZE 6
+
+#define EVT_LINK_KEY_REQ		0x17
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) evt_link_key_req;
+#define EVT_LINK_KEY_REQ_SIZE 6
+
+#define EVT_LINK_KEY_NOTIFY		0x18
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		link_key[16];
+	uint8_t		key_type;
+} __attribute__ ((packed)) evt_link_key_notify;
+#define EVT_LINK_KEY_NOTIFY_SIZE 23
+
+#define EVT_LOOPBACK_COMMAND		0x19
+
+#define EVT_DATA_BUFFER_OVERFLOW	0x1A
+typedef struct {
+	uint8_t		link_type;
+} __attribute__ ((packed)) evt_data_buffer_overflow;
+#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1
+
+#define EVT_MAX_SLOTS_CHANGE		0x1B
+typedef struct {
+	uint16_t	handle;
+	uint8_t		max_slots;
+} __attribute__ ((packed)) evt_max_slots_change;
+#define EVT_MAX_SLOTS_CHANGE_SIZE 3
+
+#define EVT_READ_CLOCK_OFFSET_COMPLETE	0x1C
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint16_t	clock_offset;
+} __attribute__ ((packed)) evt_read_clock_offset_complete;
+#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5
+
+#define EVT_CONN_PTYPE_CHANGED		0x1D
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint16_t	ptype;
+} __attribute__ ((packed)) evt_conn_ptype_changed;
+#define EVT_CONN_PTYPE_CHANGED_SIZE 5
+
+#define EVT_QOS_VIOLATION		0x1E
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) evt_qos_violation;
+#define EVT_QOS_VIOLATION_SIZE 2
+
+#define EVT_PSCAN_REP_MODE_CHANGE	0x20
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+} __attribute__ ((packed)) evt_pscan_rep_mode_change;
+#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7
+
+#define EVT_FLOW_SPEC_COMPLETE		0x21
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		flags;
+	uint8_t		direction;
+	hci_qos		qos;
+} __attribute__ ((packed)) evt_flow_spec_complete;
+#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE)
+
+#define EVT_INQUIRY_RESULT_WITH_RSSI	0x22
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_period_mode;
+	uint8_t		dev_class[3];
+	uint16_t	clock_offset;
+	int8_t		rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi;
+#define INQUIRY_INFO_WITH_RSSI_SIZE 14
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_period_mode;
+	uint8_t		pscan_mode;
+	uint8_t		dev_class[3];
+	uint16_t	clock_offset;
+	int8_t		rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode;
+#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 15
+
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE	0x23
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		page_num;
+	uint8_t		max_page_num;
+	uint8_t		features[8];
+} __attribute__ ((packed)) evt_read_remote_ext_features_complete;
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13
+
+#define EVT_SYNC_CONN_COMPLETE		0x2C
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	bdaddr_t	bdaddr;
+	uint8_t		link_type;
+	uint8_t		trans_interval;
+	uint8_t		retrans_window;
+	uint16_t	rx_pkt_len;
+	uint16_t	tx_pkt_len;
+	uint8_t		air_mode;
+} __attribute__ ((packed)) evt_sync_conn_complete;
+#define EVT_SYNC_CONN_COMPLETE_SIZE 17
+
+#define EVT_SYNC_CONN_CHANGED		0x2D
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		trans_interval;
+	uint8_t		retrans_window;
+	uint16_t	rx_pkt_len;
+	uint16_t	tx_pkt_len;
+} __attribute__ ((packed)) evt_sync_conn_changed;
+#define EVT_SYNC_CONN_CHANGED_SIZE 9
+
+#define EVT_SNIFF_SUBRATING		0x2E
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint16_t	max_tx_latency;
+	uint16_t	max_rx_latency;
+	uint16_t	min_remote_timeout;
+	uint16_t	min_local_timeout;
+} __attribute__ ((packed)) evt_sniff_subrating;
+#define EVT_SNIFF_SUBRATING_SIZE 11
+
+#define EVT_EXTENDED_INQUIRY_RESULT	0x2F
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_period_mode;
+	uint8_t		dev_class[3];
+	uint16_t	clock_offset;
+	int8_t		rssi;
+	uint8_t		data[HCI_MAX_EIR_LENGTH];
+} __attribute__ ((packed)) extended_inquiry_info;
+#define EXTENDED_INQUIRY_INFO_SIZE 254
+
+#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE	0x30
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) evt_encryption_key_refresh_complete;
+#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE 3
+
+#define EVT_IO_CAPABILITY_REQUEST	0x31
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) evt_io_capability_request;
+#define EVT_IO_CAPABILITY_REQUEST_SIZE 6
+
+#define EVT_IO_CAPABILITY_RESPONSE	0x32
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		capability;
+	uint8_t		oob_data;
+	uint8_t		authentication;
+} __attribute__ ((packed)) evt_io_capability_response;
+#define EVT_IO_CAPABILITY_RESPONSE_SIZE 9
+
+#define EVT_USER_CONFIRM_REQUEST	0x33
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint32_t	passkey;
+} __attribute__ ((packed)) evt_user_confirm_request;
+#define EVT_USER_CONFIRM_REQUEST_SIZE 10
+
+#define EVT_USER_PASSKEY_REQUEST	0x34
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) evt_user_passkey_request;
+#define EVT_USER_PASSKEY_REQUEST_SIZE 6
+
+#define EVT_REMOTE_OOB_DATA_REQUEST	0x35
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) evt_remote_oob_data_request;
+#define EVT_REMOTE_OOB_DATA_REQUEST_SIZE 6
+
+#define EVT_SIMPLE_PAIRING_COMPLETE	0x36
+typedef struct {
+	uint8_t		status;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) evt_simple_pairing_complete;
+#define EVT_SIMPLE_PAIRING_COMPLETE_SIZE 7
+
+#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED	0x38
+typedef struct {
+	uint16_t	handle;
+	uint16_t	timeout;
+} __attribute__ ((packed)) evt_link_supervision_timeout_changed;
+#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE 4
+
+#define EVT_ENHANCED_FLUSH_COMPLETE	0x39
+typedef struct {
+	uint16_t	handle;
+} __attribute__ ((packed)) evt_enhanced_flush_complete;
+#define EVT_ENHANCED_FLUSH_COMPLETE_SIZE 2
+
+#define EVT_USER_PASSKEY_NOTIFY		0x3B
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint32_t	passkey;
+} __attribute__ ((packed)) evt_user_passkey_notify;
+#define EVT_USER_PASSKEY_NOTIFY_SIZE 10
+
+#define EVT_KEYPRESS_NOTIFY		0x3C
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		type;
+} __attribute__ ((packed)) evt_keypress_notify;
+#define EVT_KEYPRESS_NOTIFY_SIZE 7
+
+#define EVT_REMOTE_HOST_FEATURES_NOTIFY	0x3D
+typedef struct {
+	bdaddr_t	bdaddr;
+	uint8_t		features[8];
+} __attribute__ ((packed)) evt_remote_host_features_notify;
+#define EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE 14
+
+#define EVT_LE_META_EVENT	0x3E
+typedef struct {
+	uint8_t		subevent;
+	uint8_t		data[0];
+} __attribute__ ((packed)) evt_le_meta_event;
+#define EVT_LE_META_EVENT_SIZE 1
+
+#define EVT_LE_CONN_COMPLETE	0x01
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		role;
+	uint8_t		peer_bdaddr_type;
+	bdaddr_t	peer_bdaddr;
+	uint16_t	interval;
+	uint16_t	latency;
+	uint16_t	supervision_timeout;
+	uint8_t		master_clock_accuracy;
+} __attribute__ ((packed)) evt_le_connection_complete;
+#define EVT_LE_CONN_COMPLETE_SIZE 18
+
+#define EVT_LE_ADVERTISING_REPORT	0x02
+typedef struct {
+	uint8_t		evt_type;
+	uint8_t		bdaddr_type;
+	bdaddr_t	bdaddr;
+	uint8_t		length;
+	uint8_t		data[0];
+} __attribute__ ((packed)) le_advertising_info;
+#define LE_ADVERTISING_INFO_SIZE 9
+
+#define EVT_LE_CONN_UPDATE_COMPLETE	0x03
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint16_t	interval;
+	uint16_t	latency;
+	uint16_t	supervision_timeout;
+} __attribute__ ((packed)) evt_le_connection_update_complete;
+#define EVT_LE_CONN_UPDATE_COMPLETE_SIZE 9
+
+#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE	0x04
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+	uint8_t		features[8];
+} __attribute__ ((packed)) evt_le_read_remote_used_features_complete;
+#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_LE_LTK_REQUEST	0x05
+typedef struct {
+	uint16_t	handle;
+	uint64_t	random;
+	uint16_t	diversifier;
+} __attribute__ ((packed)) evt_le_long_term_key_request;
+#define EVT_LE_LTK_REQUEST_SIZE 12
+
+#define EVT_PHYSICAL_LINK_COMPLETE		0x40
+typedef struct {
+	uint8_t		status;
+	uint8_t		handle;
+} __attribute__ ((packed)) evt_physical_link_complete;
+#define EVT_PHYSICAL_LINK_COMPLETE_SIZE 2
+
+#define EVT_CHANNEL_SELECTED		0x41
+
+#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE	0x42
+typedef struct {
+	uint8_t		status;
+	uint8_t		handle;
+	uint8_t		reason;
+} __attribute__ ((packed)) evt_disconn_physical_link_complete;
+#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE 3
+
+#define EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING	0x43
+typedef struct {
+	uint8_t		handle;
+	uint8_t		reason;
+} __attribute__ ((packed)) evt_physical_link_loss_warning;
+#define EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE 2
+
+#define EVT_PHYSICAL_LINK_RECOVERY		0x44
+typedef struct {
+	uint8_t		handle;
+} __attribute__ ((packed)) evt_physical_link_recovery;
+#define EVT_PHYSICAL_LINK_RECOVERY_SIZE 1
+
+#define EVT_LOGICAL_LINK_COMPLETE		0x45
+typedef struct {
+	uint8_t		status;
+	uint16_t	log_handle;
+	uint8_t		handle;
+	uint8_t		tx_flow_id;
+} __attribute__ ((packed)) evt_logical_link_complete;
+#define EVT_LOGICAL_LINK_COMPLETE_SIZE 5
+
+#define EVT_DISCONNECT_LOGICAL_LINK_COMPLETE	0x46
+
+#define EVT_FLOW_SPEC_MODIFY_COMPLETE		0x47
+typedef struct {
+	uint8_t		status;
+	uint16_t	handle;
+} __attribute__ ((packed)) evt_flow_spec_modify_complete;
+#define EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE 3
+
+#define EVT_NUMBER_COMPLETED_BLOCKS		0x48
+typedef struct {
+	uint16_t		handle;
+	uint16_t		num_cmplt_pkts;
+	uint16_t		num_cmplt_blks;
+} __attribute__ ((packed)) cmplt_handle;
+typedef struct {
+	uint16_t		total_num_blocks;
+	uint8_t			num_handles;
+	cmplt_handle		handles[0];
+}  __attribute__ ((packed)) evt_num_completed_blocks;
+
+#define EVT_AMP_STATUS_CHANGE			0x4D
+typedef struct {
+	uint8_t		status;
+	uint8_t		amp_status;
+} __attribute__ ((packed)) evt_amp_status_change;
+#define EVT_AMP_STATUS_CHANGE_SIZE 2
+
+#define EVT_TESTING			0xFE
+
+#define EVT_VENDOR			0xFF
+
+/* Internal events generated by BlueZ stack */
+#define EVT_STACK_INTERNAL		0xFD
+typedef struct {
+	uint16_t	type;
+	uint8_t		data[0];
+} __attribute__ ((packed)) evt_stack_internal;
+#define EVT_STACK_INTERNAL_SIZE 2
+
+#define EVT_SI_DEVICE	0x01
+typedef struct {
+	uint16_t	event;
+	uint16_t	dev_id;
+} __attribute__ ((packed)) evt_si_device;
+#define EVT_SI_DEVICE_SIZE 4
+
+/* --------  HCI Packet structures  -------- */
+#define HCI_TYPE_LEN	1
+
+typedef struct {
+	uint16_t	opcode;		/* OCF & OGF */
+	uint8_t		plen;
+} __attribute__ ((packed))	hci_command_hdr;
+#define HCI_COMMAND_HDR_SIZE	3
+
+typedef struct {
+	uint8_t		evt;
+	uint8_t		plen;
+} __attribute__ ((packed))	hci_event_hdr;
+#define HCI_EVENT_HDR_SIZE	2
+
+typedef struct {
+	uint16_t	handle;		/* Handle & Flags(PB, BC) */
+	uint16_t	dlen;
+} __attribute__ ((packed))	hci_acl_hdr;
+#define HCI_ACL_HDR_SIZE	4
+
+typedef struct {
+	uint16_t	handle;
+	uint8_t		dlen;
+} __attribute__ ((packed))	hci_sco_hdr;
+#define HCI_SCO_HDR_SIZE	3
+
+typedef struct {
+	uint16_t	device;
+	uint16_t	type;
+	uint16_t	plen;
+} __attribute__ ((packed))	hci_msg_hdr;
+#define HCI_MSG_HDR_SIZE	6
+
+/* Command opcode pack/unpack */
+#define cmd_opcode_pack(ogf, ocf)	(uint16_t)((ocf & 0x03ff)|(ogf << 10))
+#define cmd_opcode_ogf(op)		(op >> 10)
+#define cmd_opcode_ocf(op)		(op & 0x03ff)
+
+/* ACL handle and flags pack/unpack */
+#define acl_handle_pack(h, f)	(uint16_t)((h & 0x0fff)|(f << 12))
+#define acl_handle(h)		(h & 0x0fff)
+#define acl_flags(h)		(h >> 12)
+
+#endif /* _NO_HCI_DEFS */
+
+/* HCI Socket options */
+#define HCI_DATA_DIR	1
+#define HCI_FILTER	2
+#define HCI_TIME_STAMP	3
+
+/* HCI CMSG flags */
+#define HCI_CMSG_DIR	0x0001
+#define HCI_CMSG_TSTAMP	0x0002
+
+struct sockaddr_hci {
+	sa_family_t	hci_family;
+	unsigned short	hci_dev;
+	unsigned short  hci_channel;
+};
+#define HCI_DEV_NONE	0xffff
+
+#define HCI_CHANNEL_RAW		0
+#define HCI_CHANNEL_USER	1
+#define HCI_CHANNEL_MONITOR	2
+#define HCI_CHANNEL_CONTROL	3
+#define HCI_CHANNEL_LOGGING	4
+
+struct hci_filter {
+	uint32_t type_mask;
+	uint32_t event_mask[2];
+	uint16_t opcode;
+};
+
+#define HCI_FLT_TYPE_BITS	31
+#define HCI_FLT_EVENT_BITS	63
+#define HCI_FLT_OGF_BITS	63
+#define HCI_FLT_OCF_BITS	127
+
+/* Ioctl requests structures */
+struct hci_dev_stats {
+	uint32_t err_rx;
+	uint32_t err_tx;
+	uint32_t cmd_tx;
+	uint32_t evt_rx;
+	uint32_t acl_tx;
+	uint32_t acl_rx;
+	uint32_t sco_tx;
+	uint32_t sco_rx;
+	uint32_t byte_rx;
+	uint32_t byte_tx;
+};
+
+struct hci_dev_info {
+	uint16_t dev_id;
+	char     name[8];
+
+	bdaddr_t bdaddr;
+
+	uint32_t flags;
+	uint8_t  type;
+
+	uint8_t  features[8];
+
+	uint32_t pkt_type;
+	uint32_t link_policy;
+	uint32_t link_mode;
+
+	uint16_t acl_mtu;
+	uint16_t acl_pkts;
+	uint16_t sco_mtu;
+	uint16_t sco_pkts;
+
+	struct   hci_dev_stats stat;
+};
+
+struct hci_conn_info {
+	uint16_t handle;
+	bdaddr_t bdaddr;
+	uint8_t  type;
+	uint8_t	 out;
+	uint16_t state;
+	uint32_t link_mode;
+};
+
+struct hci_dev_req {
+	uint16_t dev_id;
+	uint32_t dev_opt;
+};
+
+struct hci_dev_list_req {
+	uint16_t dev_num;
+	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
+};
+
+struct hci_conn_list_req {
+	uint16_t dev_id;
+	uint16_t conn_num;
+	struct hci_conn_info conn_info[0];
+};
+
+struct hci_conn_info_req {
+	bdaddr_t bdaddr;
+	uint8_t  type;
+	struct hci_conn_info conn_info[0];
+};
+
+struct hci_auth_info_req {
+	bdaddr_t bdaddr;
+	uint8_t  type;
+};
+
+struct hci_inquiry_req {
+	uint16_t dev_id;
+	uint16_t flags;
+	uint8_t  lap[3];
+	uint8_t  length;
+	uint8_t  num_rsp;
+};
+#define IREQ_CACHE_FLUSH 0x0001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HCI_H */
diff --git a/bluez/repo/lib/hci_lib.h b/bluez/repo/lib/hci_lib.h
new file mode 100644
index 0000000..55aeb17
--- /dev/null
+++ b/bluez/repo/lib/hci_lib.h
@@ -0,0 +1,242 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __HCI_LIB_H
+#define __HCI_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct hci_request {
+	uint16_t ogf;
+	uint16_t ocf;
+	int      event;
+	void     *cparam;
+	int      clen;
+	void     *rparam;
+	int      rlen;
+};
+
+struct hci_version {
+	uint16_t manufacturer;
+	uint8_t  hci_ver;
+	uint16_t hci_rev;
+	uint8_t  lmp_ver;
+	uint16_t lmp_subver;
+};
+
+int hci_open_dev(int dev_id);
+int hci_close_dev(int dd);
+int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
+int hci_send_req(int dd, struct hci_request *req, int timeout);
+
+int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
+int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to);
+
+int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
+int hci_devinfo(int dev_id, struct hci_dev_info *di);
+int hci_devba(int dev_id, bdaddr_t *bdaddr);
+int hci_devid(const char *str);
+
+int hci_read_local_name(int dd, int len, char *name, int to);
+int hci_write_local_name(int dd, const char *name, int to);
+int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to);
+int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to);
+int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to);
+int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to);
+int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
+int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
+int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to);
+int hci_read_local_version(int dd, struct hci_version *ver, int to);
+int hci_read_local_commands(int dd, uint8_t *commands, int to);
+int hci_read_local_features(int dd, uint8_t *features, int to);
+int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
+int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
+int hci_read_class_of_dev(int dd, uint8_t *cls, int to);
+int hci_write_class_of_dev(int dd, uint32_t cls, int to);
+int hci_read_voice_setting(int dd, uint16_t *vs, int to);
+int hci_write_voice_setting(int dd, uint16_t vs, int to);
+int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to);
+int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to);
+int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
+int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to);
+int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
+int hci_authenticate_link(int dd, uint16_t handle, int to);
+int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to);
+int hci_change_link_key(int dd, uint16_t handle, int to);
+int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to);
+int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to);
+int hci_exit_park_mode(int dd, uint16_t handle, int to);
+int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to);
+int hci_write_inquiry_scan_type(int dd, uint8_t type, int to);
+int hci_read_inquiry_mode(int dd, uint8_t *mode, int to);
+int hci_write_inquiry_mode(int dd, uint8_t mode, int to);
+int hci_read_afh_mode(int dd, uint8_t *mode, int to);
+int hci_write_afh_mode(int dd, uint8_t mode, int to);
+int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to);
+int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to);
+int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to);
+int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to);
+int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to);
+int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to);
+int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to);
+int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to);
+int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to);
+int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to);
+int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to);
+int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to);
+int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to);
+int hci_set_afh_classification(int dd, uint8_t *map, int to);
+int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to);
+int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to);
+int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to);
+int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to);
+
+int hci_le_set_scan_enable(int dev_id, uint8_t enable, uint8_t filter_dup, int to);
+int hci_le_set_scan_parameters(int dev_id, uint8_t type, uint16_t interval,
+					uint16_t window, uint8_t own_type,
+					uint8_t filter, int to);
+int hci_le_set_advertise_enable(int dev_id, uint8_t enable, int to);
+int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
+		uint8_t initiator_filter, uint8_t peer_bdaddr_type,
+		bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
+		uint16_t min_interval, uint16_t max_interval,
+		uint16_t latency, uint16_t supervision_timeout,
+		uint16_t min_ce_length, uint16_t max_ce_length,
+		uint16_t *handle, int to);
+int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
+			uint16_t max_interval, uint16_t latency,
+			uint16_t supervision_timeout, int to);
+int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_read_white_list_size(int dd, uint8_t *size, int to);
+int hci_le_clear_white_list(int dd, int to);
+int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type,
+				uint8_t *peer_irk, uint8_t *local_irk, int to);
+int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_clear_resolving_list(int dd, int to);
+int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to);
+int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to);
+int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
+
+int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
+int hci_get_route(bdaddr_t *bdaddr);
+
+char *hci_bustostr(int bus);
+char *hci_typetostr(int type);
+char *hci_dtypetostr(int type);
+char *hci_dflagstostr(uint32_t flags);
+char *hci_ptypetostr(unsigned int ptype);
+int hci_strtoptype(char *str, unsigned int *val);
+char *hci_scoptypetostr(unsigned int ptype);
+int hci_strtoscoptype(char *str, unsigned int *val);
+char *hci_lptostr(unsigned int ptype);
+int hci_strtolp(char *str, unsigned int *val);
+char *hci_lmtostr(unsigned int ptype);
+int hci_strtolm(char *str, unsigned int *val);
+
+char *hci_cmdtostr(unsigned int cmd);
+char *hci_commandstostr(uint8_t *commands, char *pref, int width);
+
+char *hci_vertostr(unsigned int ver);
+int hci_strtover(char *str, unsigned int *ver);
+char *lmp_vertostr(unsigned int ver);
+int lmp_strtover(char *str, unsigned int *ver);
+char *pal_vertostr(unsigned int ver);
+int pal_strtover(char *str, unsigned int *ver);
+
+char *lmp_featurestostr(uint8_t *features, char *pref, int width);
+
+static inline void hci_set_bit(int nr, void *addr)
+{
+	*((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31));
+}
+
+static inline void hci_clear_bit(int nr, void *addr)
+{
+	*((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31));
+}
+
+static inline int hci_test_bit(int nr, void *addr)
+{
+	return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31));
+}
+
+/* HCI filter tools */
+static inline void hci_filter_clear(struct hci_filter *f)
+{
+	memset(f, 0, sizeof(*f));
+}
+static inline void hci_filter_set_ptype(int t, struct hci_filter *f)
+{
+	hci_set_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline void hci_filter_clear_ptype(int t, struct hci_filter *f)
+{
+	hci_clear_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline int hci_filter_test_ptype(int t, struct hci_filter *f)
+{
+	return hci_test_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline void hci_filter_all_ptypes(struct hci_filter *f)
+{
+	memset((void *) &f->type_mask, 0xff, sizeof(f->type_mask));
+}
+static inline void hci_filter_set_event(int e, struct hci_filter *f)
+{
+	hci_set_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline void hci_filter_clear_event(int e, struct hci_filter *f)
+{
+	hci_clear_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline int hci_filter_test_event(int e, struct hci_filter *f)
+{
+	return hci_test_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline void hci_filter_all_events(struct hci_filter *f)
+{
+	memset((void *) f->event_mask, 0xff, sizeof(f->event_mask));
+}
+static inline void hci_filter_set_opcode(int opcode, struct hci_filter *f)
+{
+	f->opcode = opcode;
+}
+static inline void hci_filter_clear_opcode(struct hci_filter *f)
+{
+	f->opcode = 0;
+}
+static inline int hci_filter_test_opcode(int opcode, struct hci_filter *f)
+{
+	return (f->opcode == opcode);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HCI_LIB_H */
diff --git a/bluez/repo/lib/hidp.h b/bluez/repo/lib/hidp.h
new file mode 100644
index 0000000..c5e6a78
--- /dev/null
+++ b/bluez/repo/lib/hidp.h
@@ -0,0 +1,85 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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
+ *
+ */
+
+#ifndef __HIDP_H
+#define __HIDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HIDP defaults */
+#define HIDP_MINIMUM_MTU 48
+#define HIDP_DEFAULT_MTU 48
+
+/* HIDP ioctl defines */
+#define HIDPCONNADD	_IOW('H', 200, int)
+#define HIDPCONNDEL	_IOW('H', 201, int)
+#define HIDPGETCONNLIST	_IOR('H', 210, int)
+#define HIDPGETCONNINFO	_IOR('H', 211, int)
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG	0
+#define HIDP_BOOT_PROTOCOL_MODE		1
+#define HIDP_BLUETOOTH_VENDOR_ID	9
+
+struct hidp_connadd_req {
+	int ctrl_sock;		/* Connected control socket */
+	int intr_sock;		/* Connected interrupt socket */
+	uint16_t parser;	/* Parser version */
+	uint16_t rd_size;	/* Report descriptor size */
+	uint8_t *rd_data;	/* Report descriptor data */
+	uint8_t  country;
+	uint8_t  subclass;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t version;
+	uint32_t flags;
+	uint32_t idle_to;
+	char name[128];		/* Device name */
+};
+
+struct hidp_conndel_req {
+	bdaddr_t bdaddr;
+	uint32_t flags;
+};
+
+struct hidp_conninfo {
+	bdaddr_t bdaddr;
+	uint32_t flags;
+	uint16_t state;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t version;
+	char name[128];
+};
+
+struct hidp_connlist_req {
+	uint32_t cnum;
+	struct hidp_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HIDP_H */
diff --git a/bluez/repo/lib/l2cap.h b/bluez/repo/lib/l2cap.h
new file mode 100644
index 0000000..5ce94c4
--- /dev/null
+++ b/bluez/repo/lib/l2cap.h
@@ -0,0 +1,279 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (c) 2012       Code Aurora Forum. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __L2CAP_H
+#define __L2CAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+/* L2CAP defaults */
+#define L2CAP_DEFAULT_MTU	672
+#define L2CAP_DEFAULT_FLUSH_TO	0xFFFF
+
+/* L2CAP socket address */
+struct sockaddr_l2 {
+	sa_family_t	l2_family;
+	unsigned short	l2_psm;
+	bdaddr_t	l2_bdaddr;
+	unsigned short	l2_cid;
+	uint8_t		l2_bdaddr_type;
+};
+
+/* L2CAP socket options */
+#define L2CAP_OPTIONS	0x01
+struct l2cap_options {
+	uint16_t	omtu;
+	uint16_t	imtu;
+	uint16_t	flush_to;
+	uint8_t		mode;
+	uint8_t		fcs;
+	uint8_t		max_tx;
+	uint16_t	txwin_size;
+};
+
+#define L2CAP_CONNINFO	0x02
+struct l2cap_conninfo {
+	uint16_t	hci_handle;
+	uint8_t		dev_class[3];
+};
+
+#define L2CAP_LM	0x03
+#define L2CAP_LM_MASTER		0x0001
+#define L2CAP_LM_AUTH		0x0002
+#define L2CAP_LM_ENCRYPT	0x0004
+#define L2CAP_LM_TRUSTED	0x0008
+#define L2CAP_LM_RELIABLE	0x0010
+#define L2CAP_LM_SECURE		0x0020
+
+/* L2CAP command codes */
+#define L2CAP_COMMAND_REJ	0x01
+#define L2CAP_CONN_REQ		0x02
+#define L2CAP_CONN_RSP		0x03
+#define L2CAP_CONF_REQ		0x04
+#define L2CAP_CONF_RSP		0x05
+#define L2CAP_DISCONN_REQ	0x06
+#define L2CAP_DISCONN_RSP	0x07
+#define L2CAP_ECHO_REQ		0x08
+#define L2CAP_ECHO_RSP		0x09
+#define L2CAP_INFO_REQ		0x0a
+#define L2CAP_INFO_RSP		0x0b
+#define L2CAP_CREATE_REQ	0x0c
+#define L2CAP_CREATE_RSP	0x0d
+#define L2CAP_MOVE_REQ		0x0e
+#define L2CAP_MOVE_RSP		0x0f
+#define L2CAP_MOVE_CFM		0x10
+#define L2CAP_MOVE_CFM_RSP	0x11
+
+/* L2CAP extended feature mask */
+#define L2CAP_FEAT_FLOWCTL	0x00000001
+#define L2CAP_FEAT_RETRANS	0x00000002
+#define L2CAP_FEAT_BIDIR_QOS	0x00000004
+#define L2CAP_FEAT_ERTM		0x00000008
+#define L2CAP_FEAT_STREAMING	0x00000010
+#define L2CAP_FEAT_FCS		0x00000020
+#define L2CAP_FEAT_EXT_FLOW	0x00000040
+#define L2CAP_FEAT_FIXED_CHAN	0x00000080
+#define L2CAP_FEAT_EXT_WINDOW	0x00000100
+#define L2CAP_FEAT_UCD		0x00000200
+
+/* L2CAP fixed channels */
+#define L2CAP_FC_L2CAP		0x02
+#define L2CAP_FC_CONNLESS	0x04
+#define L2CAP_FC_A2MP		0x08
+
+/* L2CAP structures */
+typedef struct {
+	uint16_t	len;
+	uint16_t	cid;
+} __attribute__ ((packed)) l2cap_hdr;
+#define L2CAP_HDR_SIZE 4
+
+typedef struct {
+	uint8_t		code;
+	uint8_t		ident;
+	uint16_t	len;
+} __attribute__ ((packed)) l2cap_cmd_hdr;
+#define L2CAP_CMD_HDR_SIZE 4
+
+typedef struct {
+	uint16_t	reason;
+} __attribute__ ((packed)) l2cap_cmd_rej;
+#define L2CAP_CMD_REJ_SIZE 2
+
+typedef struct {
+	uint16_t	psm;
+	uint16_t	scid;
+} __attribute__ ((packed)) l2cap_conn_req;
+#define L2CAP_CONN_REQ_SIZE 4
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+	uint16_t	result;
+	uint16_t	status;
+} __attribute__ ((packed)) l2cap_conn_rsp;
+#define L2CAP_CONN_RSP_SIZE 8
+
+/* connect result */
+#define L2CAP_CR_SUCCESS	0x0000
+#define L2CAP_CR_PEND		0x0001
+#define L2CAP_CR_BAD_PSM	0x0002
+#define L2CAP_CR_SEC_BLOCK	0x0003
+#define L2CAP_CR_NO_MEM		0x0004
+
+/* connect status */
+#define L2CAP_CS_NO_INFO	0x0000
+#define L2CAP_CS_AUTHEN_PEND	0x0001
+#define L2CAP_CS_AUTHOR_PEND	0x0002
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	flags;
+	uint8_t		data[0];
+} __attribute__ ((packed)) l2cap_conf_req;
+#define L2CAP_CONF_REQ_SIZE 4
+
+typedef struct {
+	uint16_t	scid;
+	uint16_t	flags;
+	uint16_t	result;
+	uint8_t		data[0];
+} __attribute__ ((packed)) l2cap_conf_rsp;
+#define L2CAP_CONF_RSP_SIZE 6
+
+#define L2CAP_CONF_SUCCESS	0x0000
+#define L2CAP_CONF_UNACCEPT	0x0001
+#define L2CAP_CONF_REJECT	0x0002
+#define L2CAP_CONF_UNKNOWN	0x0003
+#define L2CAP_CONF_PENDING	0x0004
+#define L2CAP_CONF_EFS_REJECT	0x0005
+
+typedef struct {
+	uint8_t		type;
+	uint8_t		len;
+	uint8_t		val[0];
+} __attribute__ ((packed)) l2cap_conf_opt;
+#define L2CAP_CONF_OPT_SIZE 2
+
+#define L2CAP_CONF_MTU		0x01
+#define L2CAP_CONF_FLUSH_TO	0x02
+#define L2CAP_CONF_QOS		0x03
+#define L2CAP_CONF_RFC		0x04
+#define L2CAP_CONF_FCS		0x05
+#define L2CAP_CONF_EFS		0x06
+#define L2CAP_CONF_EWS		0x07
+
+#define L2CAP_CONF_MAX_SIZE	22
+
+#define L2CAP_MODE_BASIC	0x00
+#define L2CAP_MODE_RETRANS	0x01
+#define L2CAP_MODE_FLOWCTL	0x02
+#define L2CAP_MODE_ERTM		0x03
+#define L2CAP_MODE_STREAMING	0x04
+
+#define L2CAP_SERVTYPE_NOTRAFFIC	0x00
+#define L2CAP_SERVTYPE_BESTEFFORT	0x01
+#define L2CAP_SERVTYPE_GUARANTEED	0x02
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+} __attribute__ ((packed)) l2cap_disconn_req;
+#define L2CAP_DISCONN_REQ_SIZE 4
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+} __attribute__ ((packed)) l2cap_disconn_rsp;
+#define L2CAP_DISCONN_RSP_SIZE 4
+
+typedef struct {
+	uint16_t	type;
+} __attribute__ ((packed)) l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE 2
+
+typedef struct {
+	uint16_t	type;
+	uint16_t	result;
+	uint8_t		data[0];
+} __attribute__ ((packed)) l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE 4
+
+/* info type */
+#define L2CAP_IT_CL_MTU		0x0001
+#define L2CAP_IT_FEAT_MASK	0x0002
+
+/* info result */
+#define L2CAP_IR_SUCCESS	0x0000
+#define L2CAP_IR_NOTSUPP	0x0001
+
+typedef struct {
+	uint16_t	psm;
+	uint16_t	scid;
+	uint8_t		id;
+} __attribute__ ((packed)) l2cap_create_req;
+#define L2CAP_CREATE_REQ_SIZE 5
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+	uint16_t	result;
+	uint16_t	status;
+} __attribute__ ((packed)) l2cap_create_rsp;
+#define L2CAP_CREATE_RSP_SIZE 8
+
+typedef struct {
+	uint16_t	icid;
+	uint8_t		id;
+} __attribute__ ((packed)) l2cap_move_req;
+#define L2CAP_MOVE_REQ_SIZE 3
+
+typedef struct {
+	uint16_t	icid;
+	uint16_t	result;
+} __attribute__ ((packed)) l2cap_move_rsp;
+#define L2CAP_MOVE_RSP_SIZE 4
+
+typedef struct {
+	uint16_t	icid;
+	uint16_t	result;
+} __attribute__ ((packed)) l2cap_move_cfm;
+#define L2CAP_MOVE_CFM_SIZE 4
+
+typedef struct {
+	uint16_t	icid;
+} __attribute__ ((packed)) l2cap_move_cfm_rsp;
+#define L2CAP_MOVE_CFM_RSP_SIZE 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __L2CAP_H */
diff --git a/bluez/repo/lib/mgmt.h b/bluez/repo/lib/mgmt.h
new file mode 100644
index 0000000..d76bdbb
--- /dev/null
+++ b/bluez/repo/lib/mgmt.h
@@ -0,0 +1,899 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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
+ *
+ */
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define MGMT_INDEX_NONE			0xFFFF
+
+#define MGMT_STATUS_SUCCESS		0x00
+#define MGMT_STATUS_UNKNOWN_COMMAND	0x01
+#define MGMT_STATUS_NOT_CONNECTED	0x02
+#define MGMT_STATUS_FAILED		0x03
+#define MGMT_STATUS_CONNECT_FAILED	0x04
+#define MGMT_STATUS_AUTH_FAILED		0x05
+#define MGMT_STATUS_NOT_PAIRED		0x06
+#define MGMT_STATUS_NO_RESOURCES	0x07
+#define MGMT_STATUS_TIMEOUT		0x08
+#define MGMT_STATUS_ALREADY_CONNECTED	0x09
+#define MGMT_STATUS_BUSY		0x0a
+#define MGMT_STATUS_REJECTED		0x0b
+#define MGMT_STATUS_NOT_SUPPORTED	0x0c
+#define MGMT_STATUS_INVALID_PARAMS	0x0d
+#define MGMT_STATUS_DISCONNECTED	0x0e
+#define MGMT_STATUS_NOT_POWERED		0x0f
+#define MGMT_STATUS_CANCELLED		0x10
+#define MGMT_STATUS_INVALID_INDEX	0x11
+#define MGMT_STATUS_RFKILLED		0x12
+#define MGMT_STATUS_ALREADY_PAIRED	0x13
+#define MGMT_STATUS_PERMISSION_DENIED	0x14
+
+struct mgmt_hdr {
+	uint16_t opcode;
+	uint16_t index;
+	uint16_t len;
+} __packed;
+#define MGMT_HDR_SIZE	6
+
+struct mgmt_addr_info {
+	bdaddr_t bdaddr;
+	uint8_t type;
+} __packed;
+
+#define MGMT_OP_READ_VERSION		0x0001
+struct mgmt_rp_read_version {
+	uint8_t version;
+	uint16_t revision;
+} __packed;
+
+#define MGMT_OP_READ_COMMANDS		0x0002
+struct mgmt_rp_read_commands {
+	uint16_t num_commands;
+	uint16_t num_events;
+	uint16_t opcodes[0];
+} __packed;
+
+#define MGMT_OP_READ_INDEX_LIST		0x0003
+struct mgmt_rp_read_index_list {
+	uint16_t num_controllers;
+	uint16_t index[0];
+} __packed;
+
+/* Reserve one extra byte for names in management messages so that they
+ * are always guaranteed to be nul-terminated */
+#define MGMT_MAX_NAME_LENGTH		(248 + 1)
+#define MGMT_MAX_SHORT_NAME_LENGTH	(10 + 1)
+
+#define MGMT_SETTING_POWERED		0x00000001
+#define MGMT_SETTING_CONNECTABLE	0x00000002
+#define MGMT_SETTING_FAST_CONNECTABLE	0x00000004
+#define MGMT_SETTING_DISCOVERABLE	0x00000008
+#define MGMT_SETTING_BONDABLE		0x00000010
+#define MGMT_SETTING_LINK_SECURITY	0x00000020
+#define MGMT_SETTING_SSP		0x00000040
+#define MGMT_SETTING_BREDR		0x00000080
+#define MGMT_SETTING_HS			0x00000100
+#define MGMT_SETTING_LE			0x00000200
+#define MGMT_SETTING_ADVERTISING	0x00000400
+#define MGMT_SETTING_SECURE_CONN	0x00000800
+#define MGMT_SETTING_DEBUG_KEYS		0x00001000
+#define MGMT_SETTING_PRIVACY		0x00002000
+#define MGMT_SETTING_CONFIGURATION	0x00004000
+#define MGMT_SETTING_STATIC_ADDRESS	0x00008000
+
+#define MGMT_OP_READ_INFO		0x0004
+struct mgmt_rp_read_info {
+	bdaddr_t bdaddr;
+	uint8_t version;
+	uint16_t manufacturer;
+	uint32_t supported_settings;
+	uint32_t current_settings;
+	uint8_t dev_class[3];
+	uint8_t name[MGMT_MAX_NAME_LENGTH];
+	uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+struct mgmt_mode {
+	uint8_t val;
+} __packed;
+
+struct mgmt_cod {
+	uint8_t val[3];
+} __packed;
+
+#define MGMT_OP_SET_POWERED		0x0005
+
+#define MGMT_OP_SET_DISCOVERABLE	0x0006
+struct mgmt_cp_set_discoverable {
+	uint8_t val;
+	uint16_t timeout;
+} __packed;
+
+#define MGMT_OP_SET_CONNECTABLE		0x0007
+
+#define MGMT_OP_SET_FAST_CONNECTABLE	0x0008
+
+#define MGMT_OP_SET_BONDABLE		0x0009
+
+#define MGMT_OP_SET_LINK_SECURITY	0x000A
+
+#define MGMT_OP_SET_SSP			0x000B
+
+#define MGMT_OP_SET_HS			0x000C
+
+#define MGMT_OP_SET_LE			0x000D
+
+#define MGMT_OP_SET_DEV_CLASS		0x000E
+struct mgmt_cp_set_dev_class {
+	uint8_t major;
+	uint8_t minor;
+} __packed;
+
+#define MGMT_OP_SET_LOCAL_NAME		0x000F
+struct mgmt_cp_set_local_name {
+	uint8_t name[MGMT_MAX_NAME_LENGTH];
+	uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+#define MGMT_OP_ADD_UUID		0x0010
+struct mgmt_cp_add_uuid {
+	uint8_t uuid[16];
+	uint8_t svc_hint;
+} __packed;
+
+#define MGMT_OP_REMOVE_UUID		0x0011
+struct mgmt_cp_remove_uuid {
+	uint8_t uuid[16];
+} __packed;
+
+struct mgmt_link_key_info {
+	struct mgmt_addr_info addr;
+	uint8_t type;
+	uint8_t val[16];
+	uint8_t pin_len;
+} __packed;
+
+#define MGMT_OP_LOAD_LINK_KEYS		0x0012
+struct mgmt_cp_load_link_keys {
+	uint8_t debug_keys;
+	uint16_t key_count;
+	struct mgmt_link_key_info keys[0];
+} __packed;
+
+struct mgmt_ltk_info {
+	struct mgmt_addr_info addr;
+	uint8_t type;
+	uint8_t master;
+	uint8_t enc_size;
+	uint16_t ediv;
+	uint64_t rand;
+	uint8_t val[16];
+} __packed;
+
+#define MGMT_OP_LOAD_LONG_TERM_KEYS	0x0013
+struct mgmt_cp_load_long_term_keys {
+	uint16_t key_count;
+	struct mgmt_ltk_info keys[0];
+} __packed;
+
+#define MGMT_OP_DISCONNECT		0x0014
+struct mgmt_cp_disconnect {
+	struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_disconnect {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_GET_CONNECTIONS		0x0015
+struct mgmt_rp_get_connections {
+	uint16_t conn_count;
+	struct mgmt_addr_info addr[0];
+} __packed;
+
+#define MGMT_OP_PIN_CODE_REPLY		0x0016
+struct mgmt_cp_pin_code_reply {
+	struct mgmt_addr_info addr;
+	uint8_t pin_len;
+	uint8_t pin_code[16];
+} __packed;
+
+#define MGMT_OP_PIN_CODE_NEG_REPLY	0x0017
+struct mgmt_cp_pin_code_neg_reply {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_SET_IO_CAPABILITY	0x0018
+struct mgmt_cp_set_io_capability {
+	uint8_t io_capability;
+} __packed;
+
+#define MGMT_OP_PAIR_DEVICE		0x0019
+struct mgmt_cp_pair_device {
+	struct mgmt_addr_info addr;
+	uint8_t io_cap;
+} __packed;
+struct mgmt_rp_pair_device {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_CANCEL_PAIR_DEVICE	0x001A
+
+#define MGMT_OP_UNPAIR_DEVICE		0x001B
+struct mgmt_cp_unpair_device {
+	struct mgmt_addr_info addr;
+	uint8_t disconnect;
+} __packed;
+struct mgmt_rp_unpair_device {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_REPLY	0x001C
+struct mgmt_cp_user_confirm_reply {
+	struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_user_confirm_reply {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_NEG_REPLY	0x001D
+
+#define MGMT_OP_USER_PASSKEY_REPLY	0x001E
+struct mgmt_cp_user_passkey_reply {
+	struct mgmt_addr_info addr;
+	uint32_t passkey;
+} __packed;
+struct mgmt_rp_user_passkey_reply {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_PASSKEY_NEG_REPLY	0x001F
+struct mgmt_cp_user_passkey_neg_reply {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_READ_LOCAL_OOB_DATA	0x0020
+struct mgmt_rp_read_local_oob_data {
+	uint8_t hash192[16];
+	uint8_t rand192[16];
+	uint8_t hash256[16];
+	uint8_t rand256[16];
+} __packed;
+
+#define MGMT_OP_ADD_REMOTE_OOB_DATA	0x0021
+struct mgmt_cp_add_remote_oob_data {
+	struct mgmt_addr_info addr;
+	uint8_t hash192[16];
+	uint8_t rand192[16];
+	uint8_t hash256[16];
+	uint8_t rand256[16];
+} __packed;
+
+#define MGMT_OP_REMOVE_REMOTE_OOB_DATA	0x0022
+struct mgmt_cp_remove_remote_oob_data {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_START_DISCOVERY		0x0023
+struct mgmt_cp_start_discovery {
+	uint8_t type;
+} __packed;
+
+#define MGMT_OP_STOP_DISCOVERY		0x0024
+struct mgmt_cp_stop_discovery {
+	uint8_t type;
+} __packed;
+
+#define MGMT_OP_CONFIRM_NAME		0x0025
+struct mgmt_cp_confirm_name {
+	struct mgmt_addr_info addr;
+	uint8_t name_known;
+} __packed;
+struct mgmt_rp_confirm_name {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_BLOCK_DEVICE		0x0026
+struct mgmt_cp_block_device {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_UNBLOCK_DEVICE		0x0027
+struct mgmt_cp_unblock_device {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_SET_DEVICE_ID		0x0028
+struct mgmt_cp_set_device_id {
+	uint16_t source;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t version;
+} __packed;
+
+#define MGMT_OP_SET_ADVERTISING		0x0029
+
+#define MGMT_OP_SET_BREDR		0x002A
+
+#define MGMT_OP_SET_STATIC_ADDRESS	0x002B
+struct mgmt_cp_set_static_address {
+	bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_SET_SCAN_PARAMS		0x002C
+struct mgmt_cp_set_scan_params {
+	uint16_t interval;
+	uint16_t window;
+} __packed;
+
+#define MGMT_OP_SET_SECURE_CONN		0x002D
+
+#define MGMT_OP_SET_DEBUG_KEYS		0x002E
+
+struct mgmt_irk_info {
+	struct mgmt_addr_info addr;
+	uint8_t val[16];
+} __packed;
+
+#define MGMT_OP_SET_PRIVACY		0x002F
+struct mgmt_cp_set_privacy {
+	uint8_t privacy;
+	uint8_t irk[16];
+} __packed;
+
+#define MGMT_OP_LOAD_IRKS		0x0030
+struct mgmt_cp_load_irks {
+	uint16_t irk_count;
+	struct mgmt_irk_info irks[0];
+} __packed;
+
+#define MGMT_OP_GET_CONN_INFO		0x0031
+struct mgmt_cp_get_conn_info {
+	struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_get_conn_info {
+	struct mgmt_addr_info addr;
+	int8_t rssi;
+	int8_t tx_power;
+	int8_t max_tx_power;
+} __packed;
+
+#define MGMT_OP_GET_CLOCK_INFO		0x0032
+struct mgmt_cp_get_clock_info {
+	struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_get_clock_info {
+	struct mgmt_addr_info addr;
+	uint32_t  local_clock;
+	uint32_t  piconet_clock;
+	uint16_t  accuracy;
+} __packed;
+
+#define MGMT_OP_ADD_DEVICE		0x0033
+struct mgmt_cp_add_device {
+	struct mgmt_addr_info addr;
+	uint8_t action;
+} __packed;
+struct mgmt_rp_add_device {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_REMOVE_DEVICE		0x0034
+struct mgmt_cp_remove_device {
+	struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_remove_device {
+	struct mgmt_addr_info addr;
+} __packed;
+
+struct mgmt_conn_param {
+	struct mgmt_addr_info addr;
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t latency;
+	uint16_t timeout;
+} __packed;
+
+#define MGMT_OP_LOAD_CONN_PARAM		0x0035
+struct mgmt_cp_load_conn_param {
+	uint16_t param_count;
+	struct mgmt_conn_param params[0];
+} __packed;
+
+#define MGMT_OP_READ_UNCONF_INDEX_LIST	0x0036
+struct mgmt_rp_read_unconf_index_list {
+	uint16_t num_controllers;
+	uint16_t index[0];
+} __packed;
+
+#define MGMT_OPTION_EXTERNAL_CONFIG	0x00000001
+#define MGMT_OPTION_PUBLIC_ADDRESS	0x00000002
+
+#define MGMT_OP_READ_CONFIG_INFO	0x0037
+struct mgmt_rp_read_config_info {
+	uint16_t manufacturer;
+	uint32_t supported_options;
+	uint32_t missing_options;
+} __packed;
+
+#define MGMT_OP_SET_EXTERNAL_CONFIG	0x0038
+struct mgmt_cp_set_external_config {
+	uint8_t config;
+} __packed;
+
+#define MGMT_OP_SET_PUBLIC_ADDRESS	0x0039
+struct mgmt_cp_set_public_address {
+	bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_START_SERVICE_DISCOVERY		0x003A
+struct mgmt_cp_start_service_discovery {
+	uint8_t type;
+	int8_t rssi;
+	uint16_t uuid_count;
+	uint8_t uuids[0][16];
+} __packed;
+
+#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA	0x003B
+struct mgmt_cp_read_local_oob_ext_data {
+	uint8_t  type;
+} __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+	uint8_t  type;
+	uint16_t eir_len;
+	uint8_t  eir[0];
+} __packed;
+
+#define MGMT_OP_READ_EXT_INDEX_LIST	0x003C
+struct mgmt_rp_read_ext_index_list {
+	uint16_t num_controllers;
+	struct {
+		uint16_t index;
+		uint8_t type;
+		uint8_t bus;
+	} entry[0];
+} __packed;
+
+#define MGMT_OP_READ_ADV_FEATURES	0x003D
+struct mgmt_rp_read_adv_features {
+	uint32_t supported_flags;
+	uint8_t  max_adv_data_len;
+	uint8_t  max_scan_rsp_len;
+	uint8_t  max_instances;
+	uint8_t  num_instances;
+	uint8_t  instance[0];
+} __packed;
+
+#define MGMT_OP_ADD_ADVERTISING		0x003E
+struct mgmt_cp_add_advertising {
+	uint8_t  instance;
+	uint32_t flags;
+	uint16_t duration;
+	uint16_t timeout;
+	uint8_t  adv_data_len;
+	uint8_t  scan_rsp_len;
+	uint8_t  data[0];
+} __packed;
+struct mgmt_rp_add_advertising {
+	uint8_t instance;
+} __packed;
+
+#define MGMT_ADV_FLAG_CONNECTABLE	(1 << 0)
+#define MGMT_ADV_FLAG_DISCOV		(1 << 1)
+#define MGMT_ADV_FLAG_LIMITED_DISCOV	(1 << 2)
+#define MGMT_ADV_FLAG_MANAGED_FLAGS	(1 << 3)
+#define MGMT_ADV_FLAG_TX_POWER		(1 << 4)
+#define MGMT_ADV_FLAG_APPEARANCE	(1 << 5)
+#define MGMT_ADV_FLAG_LOCAL_NAME	(1 << 6)
+
+#define MGMT_OP_REMOVE_ADVERTISING	0x003F
+struct mgmt_cp_remove_advertising {
+	uint8_t instance;
+} __packed;
+#define MGMT_REMOVE_ADVERTISING_SIZE	1
+struct mgmt_rp_remove_advertising {
+	uint8_t instance;
+} __packed;
+
+#define MGMT_OP_GET_ADV_SIZE_INFO	0x0040
+struct mgmt_cp_get_adv_size_info {
+	uint8_t  instance;
+	uint32_t flags;
+} __packed;
+#define MGMT_GET_ADV_SIZE_INFO_SIZE	5
+struct mgmt_rp_get_adv_size_info {
+	uint8_t  instance;
+	uint32_t flags;
+	uint8_t  max_adv_data_len;
+	uint8_t  max_scan_rsp_len;
+} __packed;
+
+#define MGMT_EV_CMD_COMPLETE		0x0001
+struct mgmt_ev_cmd_complete {
+	uint16_t opcode;
+	uint8_t status;
+	uint8_t data[0];
+} __packed;
+
+#define MGMT_EV_CMD_STATUS		0x0002
+struct mgmt_ev_cmd_status {
+	uint16_t opcode;
+	uint8_t status;
+} __packed;
+
+#define MGMT_EV_CONTROLLER_ERROR	0x0003
+struct mgmt_ev_controller_error {
+	uint8_t error_code;
+} __packed;
+
+#define MGMT_EV_INDEX_ADDED		0x0004
+
+#define MGMT_EV_INDEX_REMOVED		0x0005
+
+#define MGMT_EV_NEW_SETTINGS		0x0006
+
+#define MGMT_EV_CLASS_OF_DEV_CHANGED	0x0007
+struct mgmt_ev_class_of_dev_changed {
+	uint8_t dev_class[3];
+} __packed;
+
+#define MGMT_EV_LOCAL_NAME_CHANGED	0x0008
+struct mgmt_ev_local_name_changed {
+	uint8_t name[MGMT_MAX_NAME_LENGTH];
+	uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+#define MGMT_EV_NEW_LINK_KEY		0x0009
+struct mgmt_ev_new_link_key {
+	uint8_t store_hint;
+	struct mgmt_link_key_info key;
+} __packed;
+
+#define MGMT_EV_NEW_LONG_TERM_KEY	0x000A
+struct mgmt_ev_new_long_term_key {
+	uint8_t store_hint;
+	struct mgmt_ltk_info key;
+} __packed;
+
+#define MGMT_EV_DEVICE_CONNECTED	0x000B
+struct mgmt_ev_device_connected {
+	struct mgmt_addr_info addr;
+	uint32_t flags;
+	uint16_t eir_len;
+	uint8_t eir[0];
+} __packed;
+
+#define MGMT_DEV_DISCONN_UNKNOWN	0x00
+#define MGMT_DEV_DISCONN_TIMEOUT	0x01
+#define MGMT_DEV_DISCONN_LOCAL_HOST	0x02
+#define MGMT_DEV_DISCONN_REMOTE		0x03
+
+#define MGMT_EV_DEVICE_DISCONNECTED	0x000C
+struct mgmt_ev_device_disconnected {
+	struct mgmt_addr_info addr;
+	uint8_t reason;
+} __packed;
+
+#define MGMT_EV_CONNECT_FAILED		0x000D
+struct mgmt_ev_connect_failed {
+	struct mgmt_addr_info addr;
+	uint8_t status;
+} __packed;
+
+#define MGMT_EV_PIN_CODE_REQUEST	0x000E
+struct mgmt_ev_pin_code_request {
+	struct mgmt_addr_info addr;
+	uint8_t secure;
+} __packed;
+
+#define MGMT_EV_USER_CONFIRM_REQUEST	0x000F
+struct mgmt_ev_user_confirm_request {
+	struct mgmt_addr_info addr;
+	uint8_t confirm_hint;
+	uint32_t value;
+} __packed;
+
+#define MGMT_EV_USER_PASSKEY_REQUEST	0x0010
+struct mgmt_ev_user_passkey_request {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_AUTH_FAILED		0x0011
+struct mgmt_ev_auth_failed {
+	struct mgmt_addr_info addr;
+	uint8_t status;
+} __packed;
+
+#define MGMT_DEV_FOUND_CONFIRM_NAME	0x01
+#define MGMT_DEV_FOUND_LEGACY_PAIRING	0x02
+#define MGMT_DEV_FOUND_NOT_CONNECTABLE	0x04
+
+#define MGMT_EV_DEVICE_FOUND		0x0012
+struct mgmt_ev_device_found {
+	struct mgmt_addr_info addr;
+	int8_t rssi;
+	uint32_t flags;
+	uint16_t eir_len;
+	uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_DISCOVERING		0x0013
+struct mgmt_ev_discovering {
+	uint8_t type;
+	uint8_t discovering;
+} __packed;
+
+#define MGMT_EV_DEVICE_BLOCKED		0x0014
+struct mgmt_ev_device_blocked {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_DEVICE_UNBLOCKED	0x0015
+struct mgmt_ev_device_unblocked {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_DEVICE_UNPAIRED		0x0016
+struct mgmt_ev_device_unpaired {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_PASSKEY_NOTIFY		0x0017
+struct mgmt_ev_passkey_notify {
+	struct mgmt_addr_info addr;
+	uint32_t passkey;
+	uint8_t entered;
+} __packed;
+
+#define MGMT_EV_NEW_IRK			0x0018
+struct mgmt_ev_new_irk {
+	uint8_t  store_hint;
+	bdaddr_t rpa;
+	struct mgmt_irk_info key;
+} __packed;
+
+struct mgmt_csrk_info {
+	struct mgmt_addr_info addr;
+	uint8_t type;
+	uint8_t val[16];
+} __packed;
+
+#define MGMT_EV_NEW_CSRK		0x0019
+struct mgmt_ev_new_csrk {
+	uint8_t store_hint;
+	struct mgmt_csrk_info key;
+} __packed;
+
+#define MGMT_EV_DEVICE_ADDED		0x001a
+struct mgmt_ev_device_added {
+	struct mgmt_addr_info addr;
+	uint8_t action;
+} __packed;
+
+#define MGMT_EV_DEVICE_REMOVED		0x001b
+struct mgmt_ev_device_removed {
+	struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_NEW_CONN_PARAM		0x001c
+struct mgmt_ev_new_conn_param {
+	struct mgmt_addr_info addr;
+	uint8_t store_hint;
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t latency;
+	uint16_t timeout;
+} __packed;
+
+#define MGMT_EV_UNCONF_INDEX_ADDED	0x001d
+
+#define MGMT_EV_UNCONF_INDEX_REMOVED	0x001e
+
+#define MGMT_EV_NEW_CONFIG_OPTIONS	0x001f
+
+#define MGMT_EV_EXT_INDEX_ADDED		0x0020
+struct mgmt_ev_ext_index_added {
+	uint8_t type;
+	uint8_t bus;
+} __packed;
+
+#define MGMT_EV_EXT_INDEX_REMOVED	0x0021
+struct mgmt_ev_ext_index_removed {
+	uint8_t type;
+	uint8_t bus;
+} __packed;
+
+#define MGMT_EV_LOCAL_OOB_DATA_UPDATED	0x0022
+struct mgmt_ev_local_oob_data_updated {
+	uint8_t  type;
+	uint16_t eir_len;
+	uint8_t  eir[0];
+} __packed;
+
+#define MGMT_EV_ADVERTISING_ADDED	0x0023
+struct mgmt_ev_advertising_added {
+	uint8_t instance;
+} __packed;
+
+#define MGMT_EV_ADVERTISING_REMOVED	0x0024
+struct mgmt_ev_advertising_removed {
+	uint8_t instance;
+} __packed;
+
+static const char *mgmt_op[] = {
+	"<0x0000>",
+	"Read Version",
+	"Read Commands",
+	"Read Index List",
+	"Read Controller Info",
+	"Set Powered",
+	"Set Discoverable",
+	"Set Connectable",
+	"Set Fast Connectable",			/* 0x0008 */
+	"Set Bondable",
+	"Set Link Security",
+	"Set Secure Simple Pairing",
+	"Set High Speed",
+	"Set Low Energy",
+	"Set Dev Class",
+	"Set Local Name",
+	"Add UUID",					/* 0x0010 */
+	"Remove UUID",
+	"Load Link Keys",
+	"Load Long Term Keys",
+	"Disconnect",
+	"Get Connections",
+	"PIN Code Reply",
+	"PIN Code Neg Reply",
+	"Set IO Capability",				/* 0x0018 */
+	"Pair Device",
+	"Cancel Pair Device",
+	"Unpair Device",
+	"User Confirm Reply",
+	"User Confirm Neg Reply",
+	"User Passkey Reply",
+	"User Passkey Neg Reply",
+	"Read Local OOB Data",				/* 0x0020 */
+	"Add Remote OOB Data",
+	"Remove Remove OOB Data",
+	"Start Discovery",
+	"Stop Discovery",
+	"Confirm Name",
+	"Block Device",
+	"Unblock Device",
+	"Set Device ID",				/* 0x0028 */
+	"Set Advertising",
+	"Set BR/EDR",
+	"Set Static Address",
+	"Set Scan Parameters",
+	"Set Secure Connections",
+	"Set Debug Keys",
+	"Set Privacy",
+	"Load Identity Resolving Keys",			/* 0x0030 */
+	"Get Connection Information",
+	"Get Clock Information",
+	"Add Device",
+	"Remove Device",
+	"Load Connection Parameters",
+	"Read Unconfigured Index List",
+	"Read Controller Configuration Information",
+	"Set External Configuration",			/* 0x0038 */
+	"Set Public Address",
+	"Start Service Discovery",
+	"Read Local Out Of Band Extended Data",
+	"Read Extended Controller Index List",
+	"Read Advertising Features",
+	"Add Advertising",
+	"Remove Advertising",
+	"Get Advertising Size Information",		/* 0x0040 */
+	"Start Limited Discovery",
+};
+
+static const char *mgmt_ev[] = {
+	"<0x0000>",
+	"Command Complete",
+	"Command Status",
+	"Controller Error",
+	"Index Added",
+	"Index Removed",
+	"New Settings",
+	"Class of Device Changed",
+	"Local Name Changed",				/* 0x0008 */
+	"New Link Key",
+	"New Long Term Key",
+	"Device Connected",
+	"Device Disconnected",
+	"Connect Failed",
+	"PIN Code Request",
+	"User Confirm Request",
+	"User Passkey Request",				/* 0x0010 */
+	"Authentication Failed",
+	"Device Found",
+	"Discovering",
+	"Device Blocked",
+	"Device Unblocked",
+	"Device Unpaired",
+	"Passkey Notify",
+	"New Identity Resolving Key",			/* 0x0018 */
+	"New Signature Resolving Key",
+	"Device Added",
+	"Device Removed",
+	"New Connection Parameter",
+	"Unconfigured Index Added",
+	"Unconfigured Index Removed",
+	"New Configuration Options",
+	"Extended Index Added",				/* 0x0020 */
+	"Extended Index Removed",
+	"Local Out Of Band Extended Data Updated",
+	"Advertising Added",
+	"Advertising Removed",
+};
+
+static const char *mgmt_status[] = {
+	"Success",
+	"Unknown Command",
+	"Not Connected",
+	"Failed",
+	"Connect Failed",
+	"Authentication Failed",
+	"Not Paired",
+	"No Resources",
+	"Timeout",
+	"Already Connected",
+	"Busy",
+	"Rejected",
+	"Not Supported",
+	"Invalid Parameters",
+	"Disconnected",
+	"Not Powered",
+	"Cancelled",
+	"Invalid Index",
+	"Blocked through rfkill",
+	"Already Paired",
+	"Permission Denied",
+};
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static inline const char *mgmt_opstr(uint16_t op)
+{
+	if (op >= NELEM(mgmt_op))
+		return "<unknown opcode>";
+	return mgmt_op[op];
+}
+
+static inline const char *mgmt_evstr(uint16_t ev)
+{
+	if (ev >= NELEM(mgmt_ev))
+		return "<unknown event>";
+	return mgmt_ev[ev];
+}
+
+static inline const char *mgmt_errstr(uint8_t status)
+{
+	if (status >= NELEM(mgmt_status))
+		return "<unknown status>";
+	return mgmt_status[status];
+}
diff --git a/bluez/repo/lib/rfcomm.h b/bluez/repo/lib/rfcomm.h
new file mode 100644
index 0000000..ad6c0e1
--- /dev/null
+++ b/bluez/repo/lib/rfcomm.h
@@ -0,0 +1,99 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __RFCOMM_H
+#define __RFCOMM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+/* RFCOMM defaults */
+#define RFCOMM_DEFAULT_MTU	127
+
+#define RFCOMM_PSM 3
+
+/* RFCOMM socket address */
+struct sockaddr_rc {
+	sa_family_t	rc_family;
+	bdaddr_t	rc_bdaddr;
+	uint8_t		rc_channel;
+};
+
+/* RFCOMM socket options */
+#define RFCOMM_CONNINFO	0x02
+struct rfcomm_conninfo {
+	uint16_t	hci_handle;
+	uint8_t		dev_class[3];
+};
+
+#define RFCOMM_LM	0x03
+#define RFCOMM_LM_MASTER	0x0001
+#define RFCOMM_LM_AUTH		0x0002
+#define RFCOMM_LM_ENCRYPT	0x0004
+#define RFCOMM_LM_TRUSTED	0x0008
+#define RFCOMM_LM_RELIABLE	0x0010
+#define RFCOMM_LM_SECURE	0x0020
+
+/* RFCOMM TTY support */
+#define RFCOMM_MAX_DEV	256
+
+#define RFCOMMCREATEDEV		_IOW('R', 200, int)
+#define RFCOMMRELEASEDEV	_IOW('R', 201, int)
+#define RFCOMMGETDEVLIST	_IOR('R', 210, int)
+#define RFCOMMGETDEVINFO	_IOR('R', 211, int)
+
+struct rfcomm_dev_req {
+	int16_t		dev_id;
+	uint32_t	flags;
+	bdaddr_t	src;
+	bdaddr_t	dst;
+	uint8_t	channel;
+};
+#define RFCOMM_REUSE_DLC	0
+#define RFCOMM_RELEASE_ONHUP	1
+#define RFCOMM_HANGUP_NOW	2
+#define RFCOMM_TTY_ATTACHED	3
+
+struct rfcomm_dev_info {
+	int16_t		id;
+	uint32_t	flags;
+	uint16_t	state;
+	bdaddr_t	src;
+	bdaddr_t	dst;
+	uint8_t		channel;
+};
+
+struct rfcomm_dev_list_req {
+	uint16_t	dev_num;
+	struct rfcomm_dev_info dev_info[0];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RFCOMM_H */
diff --git a/bluez/repo/lib/sco.h b/bluez/repo/lib/sco.h
new file mode 100644
index 0000000..75336a5
--- /dev/null
+++ b/bluez/repo/lib/sco.h
@@ -0,0 +1,62 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+#ifndef __SCO_H
+#define __SCO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SCO defaults */
+#define SCO_DEFAULT_MTU		500
+#define SCO_DEFAULT_FLUSH_TO	0xFFFF
+
+#define SCO_CONN_TIMEOUT	(HZ * 40)
+#define SCO_DISCONN_TIMEOUT	(HZ * 2)
+#define SCO_CONN_IDLE_TIMEOUT	(HZ * 60)
+
+/* SCO socket address */
+struct sockaddr_sco {
+	sa_family_t	sco_family;
+	bdaddr_t	sco_bdaddr;
+};
+
+/* set/get sockopt defines */
+#define SCO_OPTIONS	0x01
+struct sco_options {
+	uint16_t	mtu;
+};
+
+#define SCO_CONNINFO	0x02
+struct sco_conninfo {
+	uint16_t	hci_handle;
+	uint8_t		dev_class[3];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCO_H */
diff --git a/bluez/repo/lib/sdp.c b/bluez/repo/lib/sdp.c
new file mode 100644
index 0000000..eb408a9
--- /dev/null
+++ b/bluez/repo/lib/sdp.c
@@ -0,0 +1,4950 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+#include "hci_lib.h"
+#include "l2cap.h"
+#include "sdp.h"
+#include "sdp_lib.h"
+
+#define SDPINF(fmt, arg...) syslog(LOG_INFO, fmt "\n", ## arg)
+#define SDPERR(fmt, arg...) syslog(LOG_ERR, "%s: " fmt "\n", __func__ , ## arg)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#ifdef SDP_DEBUG
+#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
+#else
+#define SDPDBG(fmt...)
+#endif
+
+static uint128_t bluetooth_base_uuid = {
+	.data = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
+};
+
+#define SDP_MAX_ATTR_LEN 65535
+
+/* match MTU used by RFCOMM */
+#define SDP_LARGE_L2CAP_MTU 1013
+
+static sdp_data_t *sdp_copy_seq(sdp_data_t *data);
+static int sdp_attr_add_new_with_length(sdp_record_t *rec,
+	uint16_t attr, uint8_t dtd, const void *value, uint32_t len);
+static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d);
+
+/* Message structure. */
+struct tupla {
+	int index;
+	char *str;
+};
+
+static struct tupla Protocol[] = {
+	{ SDP_UUID,		"SDP"		},
+	{ UDP_UUID,		"UDP"		},
+	{ RFCOMM_UUID,		"RFCOMM"	},
+	{ TCP_UUID,		"TCP"		},
+	{ TCS_BIN_UUID,		"TCS-BIN"	},
+	{ TCS_AT_UUID,		"TCS-AT"	},
+	{ OBEX_UUID,		"OBEX"		},
+	{ IP_UUID,		"IP"		},
+	{ FTP_UUID,		"FTP"		},
+	{ HTTP_UUID,		"HTTP"		},
+	{ WSP_UUID,		"WSP"		},
+	{ BNEP_UUID,		"BNEP"		},
+	{ UPNP_UUID,		"UPNP"		},
+	{ HIDP_UUID,		"HIDP"		},
+	{ HCRP_CTRL_UUID,	"HCRP-Ctrl"	},
+	{ HCRP_DATA_UUID,	"HCRP-Data"	},
+	{ HCRP_NOTE_UUID,	"HCRP-Notify"	},
+	{ AVCTP_UUID,		"AVCTP"		},
+	{ AVDTP_UUID,		"AVDTP"		},
+	{ CMTP_UUID,		"CMTP"		},
+	{ UDI_UUID,		"UDI"		},
+	{ MCAP_CTRL_UUID,	"MCAP-Ctrl"	},
+	{ MCAP_DATA_UUID,	"MCAP-Data"	},
+	{ L2CAP_UUID,		"L2CAP"		},
+	{ ATT_UUID,		"ATT"		},
+	{ 0 }
+};
+
+static struct tupla ServiceClass[] = {
+	{ SDP_SERVER_SVCLASS_ID,		"SDP Server"			},
+	{ BROWSE_GRP_DESC_SVCLASS_ID,		"Browse Group Descriptor"	},
+	{ PUBLIC_BROWSE_GROUP,			"Public Browse Group"		},
+	{ SERIAL_PORT_SVCLASS_ID,		"Serial Port"			},
+	{ LAN_ACCESS_SVCLASS_ID,		"LAN Access Using PPP"		},
+	{ DIALUP_NET_SVCLASS_ID,		"Dialup Networking"		},
+	{ IRMC_SYNC_SVCLASS_ID,			"IrMC Sync"			},
+	{ OBEX_OBJPUSH_SVCLASS_ID,		"OBEX Object Push"		},
+	{ OBEX_FILETRANS_SVCLASS_ID,		"OBEX File Transfer"		},
+	{ IRMC_SYNC_CMD_SVCLASS_ID,		"IrMC Sync Command"		},
+	{ HEADSET_SVCLASS_ID,			"Headset"			},
+	{ CORDLESS_TELEPHONY_SVCLASS_ID,	"Cordless Telephony"		},
+	{ AUDIO_SOURCE_SVCLASS_ID,		"Audio Source"			},
+	{ AUDIO_SINK_SVCLASS_ID,		"Audio Sink"			},
+	{ AV_REMOTE_TARGET_SVCLASS_ID,		"AV Remote Target"		},
+	{ ADVANCED_AUDIO_SVCLASS_ID,		"Advanced Audio"		},
+	{ AV_REMOTE_SVCLASS_ID,			"AV Remote"			},
+	{ AV_REMOTE_CONTROLLER_SVCLASS_ID,	"AV Remote Controller"		},
+	{ INTERCOM_SVCLASS_ID,			"Intercom"			},
+	{ FAX_SVCLASS_ID,			"Fax"				},
+	{ HEADSET_AGW_SVCLASS_ID,		"Headset Audio Gateway"		},
+	{ WAP_SVCLASS_ID,			"WAP"				},
+	{ WAP_CLIENT_SVCLASS_ID,		"WAP Client"			},
+	{ PANU_SVCLASS_ID,			"PAN User"			},
+	{ NAP_SVCLASS_ID,			"Network Access Point"		},
+	{ GN_SVCLASS_ID,			"PAN Group Network"		},
+	{ DIRECT_PRINTING_SVCLASS_ID,		"Direct Printing"		},
+	{ REFERENCE_PRINTING_SVCLASS_ID,	"Reference Printing"		},
+	{ IMAGING_SVCLASS_ID,			"Imaging"			},
+	{ IMAGING_RESPONDER_SVCLASS_ID,		"Imaging Responder"		},
+	{ IMAGING_ARCHIVE_SVCLASS_ID,		"Imaging Automatic Archive"	},
+	{ IMAGING_REFOBJS_SVCLASS_ID,		"Imaging Referenced Objects"	},
+	{ HANDSFREE_SVCLASS_ID,			"Handsfree"			},
+	{ HANDSFREE_AGW_SVCLASS_ID,		"Handsfree Audio Gateway"	},
+	{ DIRECT_PRT_REFOBJS_SVCLASS_ID,	"Direct Printing Ref. Objects"	},
+	{ REFLECTED_UI_SVCLASS_ID,		"Reflected UI"			},
+	{ BASIC_PRINTING_SVCLASS_ID,		"Basic Printing"		},
+	{ PRINTING_STATUS_SVCLASS_ID,		"Printing Status"		},
+	{ HID_SVCLASS_ID,			"Human Interface Device"	},
+	{ HCR_SVCLASS_ID,			"Hardcopy Cable Replacement"	},
+	{ HCR_PRINT_SVCLASS_ID,			"HCR Print"			},
+	{ HCR_SCAN_SVCLASS_ID,			"HCR Scan"			},
+	{ CIP_SVCLASS_ID,			"Common ISDN Access"		},
+	{ VIDEO_CONF_GW_SVCLASS_ID,		"Video Conferencing Gateway"	},
+	{ UDI_MT_SVCLASS_ID,			"UDI MT"			},
+	{ UDI_TA_SVCLASS_ID,			"UDI TA"			},
+	{ AV_SVCLASS_ID,			"Audio/Video"			},
+	{ SAP_SVCLASS_ID,			"SIM Access"			},
+	{ PBAP_PCE_SVCLASS_ID,			"Phonebook Access - PCE"	},
+	{ PBAP_PSE_SVCLASS_ID,			"Phonebook Access - PSE"	},
+	{ PBAP_SVCLASS_ID,			"Phonebook Access"		},
+	{ MAP_MSE_SVCLASS_ID,			"Message Access - MAS"		},
+	{ MAP_MCE_SVCLASS_ID,			"Message Access - MNS"		},
+	{ MAP_SVCLASS_ID,			"Message Access"		},
+	{ PNP_INFO_SVCLASS_ID,			"PnP Information"		},
+	{ GENERIC_NETWORKING_SVCLASS_ID,	"Generic Networking"		},
+	{ GENERIC_FILETRANS_SVCLASS_ID,		"Generic File Transfer"		},
+	{ GENERIC_AUDIO_SVCLASS_ID,		"Generic Audio"			},
+	{ GENERIC_TELEPHONY_SVCLASS_ID,		"Generic Telephony"		},
+	{ UPNP_SVCLASS_ID,			"UPnP"				},
+	{ UPNP_IP_SVCLASS_ID,			"UPnP IP"			},
+	{ UPNP_PAN_SVCLASS_ID,			"UPnP PAN"			},
+	{ UPNP_LAP_SVCLASS_ID,			"UPnP LAP"			},
+	{ UPNP_L2CAP_SVCLASS_ID,		"UPnP L2CAP"			},
+	{ VIDEO_SOURCE_SVCLASS_ID,		"Video Source"			},
+	{ VIDEO_SINK_SVCLASS_ID,		"Video Sink"			},
+	{ VIDEO_DISTRIBUTION_SVCLASS_ID,	"Video Distribution"		},
+	{ HDP_SVCLASS_ID,			"HDP"				},
+	{ HDP_SOURCE_SVCLASS_ID,		"HDP Source"			},
+	{ HDP_SINK_SVCLASS_ID,			"HDP Sink"			},
+	{ GENERIC_ACCESS_SVCLASS_ID,		"Generic Access"		},
+	{ GENERIC_ATTRIB_SVCLASS_ID,		"Generic Attribute"		},
+	{ APPLE_AGENT_SVCLASS_ID,		"Apple Agent"			},
+	{ 0 }
+};
+
+#define Profile ServiceClass
+
+static char *string_lookup(struct tupla *pt0, int index)
+{
+	struct tupla *pt;
+
+	for (pt = pt0; pt->index; pt++)
+		if (pt->index == index)
+			return pt->str;
+
+	return "";
+}
+
+static char *string_lookup_uuid(struct tupla *pt0, const uuid_t *uuid)
+{
+	uuid_t tmp_uuid;
+
+	memcpy(&tmp_uuid, uuid, sizeof(tmp_uuid));
+
+	if (sdp_uuid128_to_uuid(&tmp_uuid)) {
+		switch (tmp_uuid.type) {
+		case SDP_UUID16:
+			return string_lookup(pt0, tmp_uuid.value.uuid16);
+		case SDP_UUID32:
+			return string_lookup(pt0, tmp_uuid.value.uuid32);
+		}
+	}
+
+	return "";
+}
+
+/*
+ * Prints into a string the Protocol UUID
+ * coping a maximum of n characters.
+ */
+static int uuid2str(struct tupla *message, const uuid_t *uuid, char *str, size_t n)
+{
+	char *str2;
+
+	if (!uuid) {
+		snprintf(str, n, "NULL");
+		return -2;
+	}
+
+	switch (uuid->type) {
+	case SDP_UUID16:
+		str2 = string_lookup(message, uuid->value.uuid16);
+		snprintf(str, n, "%s", str2);
+		break;
+	case SDP_UUID32:
+		str2 = string_lookup(message, uuid->value.uuid32);
+		snprintf(str, n, "%s", str2);
+		break;
+	case SDP_UUID128:
+		str2 = string_lookup_uuid(message, uuid);
+		snprintf(str, n, "%s", str2);
+		break;
+	default:
+		snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+		return -1;
+	}
+
+	return 0;
+}
+
+int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+	return uuid2str(Protocol, uuid, str, n);
+}
+
+int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+	return uuid2str(ServiceClass, uuid, str, n);
+}
+
+int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+	return uuid2str(Profile, uuid, str, n);
+}
+
+/*
+ * convert the UUID to string, copying a maximum of n characters.
+ */
+int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+	if (!uuid) {
+		snprintf(str, n, "NULL");
+		return -2;
+	}
+	switch (uuid->type) {
+	case SDP_UUID16:
+		snprintf(str, n, "%.4x", uuid->value.uuid16);
+		break;
+	case SDP_UUID32:
+		snprintf(str, n, "%.8x", uuid->value.uuid32);
+		break;
+	case SDP_UUID128:{
+		unsigned int   data0;
+		unsigned short data1;
+		unsigned short data2;
+		unsigned short data3;
+		unsigned int   data4;
+		unsigned short data5;
+
+		memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+		memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+		memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+		memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+		memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+		memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+		snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+				ntohl(data0), ntohs(data1),
+				ntohs(data2), ntohs(data3),
+				ntohl(data4), ntohs(data5));
+		}
+		break;
+	default:
+		snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+		return -1;	/* Enum type of UUID not set */
+	}
+	return 0;
+}
+
+#ifdef SDP_DEBUG
+/*
+ * Function prints the UUID in hex as per defined syntax -
+ *
+ * 4bytes-2bytes-2bytes-2bytes-6bytes
+ *
+ * There is some ugly code, including hardcoding, but
+ * that is just the way it is converting 16 and 32 bit
+ * UUIDs to 128 bit as defined in the SDP doc
+ */
+void sdp_uuid_print(const uuid_t *uuid)
+{
+	if (uuid == NULL) {
+		SDPERR("Null passed to print UUID");
+		return;
+	}
+	if (uuid->type == SDP_UUID16) {
+		SDPDBG("  uint16_t : 0x%.4x", uuid->value.uuid16);
+	} else if (uuid->type == SDP_UUID32) {
+		SDPDBG("  uint32_t : 0x%.8x", uuid->value.uuid32);
+	} else if (uuid->type == SDP_UUID128) {
+		unsigned int data0;
+		unsigned short data1;
+		unsigned short data2;
+		unsigned short data3;
+		unsigned int data4;
+		unsigned short data5;
+
+		memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+		memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+		memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+		memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+		memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+		memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+		SDPDBG("  uint128_t : 0x%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+				ntohl(data0), ntohs(data1), ntohs(data2),
+				ntohs(data3), ntohl(data4), ntohs(data5));
+	} else
+		SDPERR("Enum type of UUID not set");
+}
+#endif
+
+sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value,
+							uint32_t length)
+{
+	sdp_data_t *seq;
+	sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+	if (!d)
+		return NULL;
+
+	memset(d, 0, sizeof(sdp_data_t));
+	d->dtd = dtd;
+	d->unitSize = sizeof(uint8_t);
+
+	switch (dtd) {
+	case SDP_DATA_NIL:
+		break;
+	case SDP_UINT8:
+		d->val.uint8 = *(uint8_t *) value;
+		d->unitSize += sizeof(uint8_t);
+		break;
+	case SDP_INT8:
+	case SDP_BOOL:
+		d->val.int8 = *(int8_t *) value;
+		d->unitSize += sizeof(int8_t);
+		break;
+	case SDP_UINT16:
+		d->val.uint16 = bt_get_unaligned((uint16_t *) value);
+		d->unitSize += sizeof(uint16_t);
+		break;
+	case SDP_INT16:
+		d->val.int16 = bt_get_unaligned((int16_t *) value);
+		d->unitSize += sizeof(int16_t);
+		break;
+	case SDP_UINT32:
+		d->val.uint32 = bt_get_unaligned((uint32_t *) value);
+		d->unitSize += sizeof(uint32_t);
+		break;
+	case SDP_INT32:
+		d->val.int32 = bt_get_unaligned((int32_t *) value);
+		d->unitSize += sizeof(int32_t);
+		break;
+	case SDP_INT64:
+		d->val.int64 = bt_get_unaligned((int64_t *) value);
+		d->unitSize += sizeof(int64_t);
+		break;
+	case SDP_UINT64:
+		d->val.uint64 = bt_get_unaligned((uint64_t *) value);
+		d->unitSize += sizeof(uint64_t);
+		break;
+	case SDP_UINT128:
+		memcpy(&d->val.uint128.data, value, sizeof(uint128_t));
+		d->unitSize += sizeof(uint128_t);
+		break;
+	case SDP_INT128:
+		memcpy(&d->val.int128.data, value, sizeof(uint128_t));
+		d->unitSize += sizeof(uint128_t);
+		break;
+	case SDP_UUID16:
+		sdp_uuid16_create(&d->val.uuid, bt_get_unaligned((uint16_t *) value));
+		d->unitSize += sizeof(uint16_t);
+		break;
+	case SDP_UUID32:
+		sdp_uuid32_create(&d->val.uuid, bt_get_unaligned((uint32_t *) value));
+		d->unitSize += sizeof(uint32_t);
+		break;
+	case SDP_UUID128:
+		sdp_uuid128_create(&d->val.uuid, value);
+		d->unitSize += sizeof(uint128_t);
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+		if (!value) {
+			free(d);
+			return NULL;
+		}
+
+		d->unitSize += length;
+		if (length <= USHRT_MAX) {
+			d->val.str = malloc(length);
+			if (!d->val.str) {
+				free(d);
+				return NULL;
+			}
+
+			memcpy(d->val.str, value, length);
+		} else {
+			SDPERR("Strings of size > USHRT_MAX not supported");
+			free(d);
+			d = NULL;
+		}
+		break;
+	case SDP_URL_STR32:
+	case SDP_TEXT_STR32:
+		SDPERR("Strings of size > USHRT_MAX not supported");
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		if (dtd == SDP_ALT8 || dtd == SDP_SEQ8)
+			d->unitSize += sizeof(uint8_t);
+		else if (dtd == SDP_ALT16 || dtd == SDP_SEQ16)
+			d->unitSize += sizeof(uint16_t);
+		else if (dtd == SDP_ALT32 || dtd == SDP_SEQ32)
+			d->unitSize += sizeof(uint32_t);
+		seq = (sdp_data_t *)value;
+		d->val.dataseq = seq;
+		for (; seq; seq = seq->next)
+			d->unitSize += seq->unitSize;
+		break;
+	default:
+		free(d);
+		d = NULL;
+	}
+
+	return d;
+}
+
+sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value)
+{
+	uint32_t length;
+
+	switch (dtd) {
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+		if (!value)
+			return NULL;
+
+		length = strlen((char *) value);
+		break;
+	default:
+		length = 0;
+		break;
+	}
+
+	return sdp_data_alloc_with_length(dtd, value, length);
+}
+
+sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *d)
+{
+	if (seq) {
+		sdp_data_t *p;
+		for (p = seq; p->next; p = p->next);
+		p->next = d;
+	} else
+		seq = d;
+	d->next = NULL;
+	return seq;
+}
+
+sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length,
+								int len)
+{
+	sdp_data_t *curr = NULL, *seq = NULL;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		sdp_data_t *data;
+		int8_t dtd = *(uint8_t *) dtds[i];
+
+		if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
+			data = (sdp_data_t *) values[i];
+		else
+			data = sdp_data_alloc_with_length(dtd, values[i], length[i]);
+
+		if (!data)
+			return NULL;
+
+		if (curr)
+			curr->next = data;
+		else
+			seq = data;
+
+		curr = data;
+	}
+
+	return sdp_data_alloc(SDP_SEQ8, seq);
+}
+
+sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len)
+{
+	sdp_data_t *curr = NULL, *seq = NULL;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		sdp_data_t *data;
+		uint8_t dtd = *(uint8_t *) dtds[i];
+
+		if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
+			data = (sdp_data_t *) values[i];
+		else
+			data = sdp_data_alloc(dtd, values[i]);
+
+		if (!data)
+			return NULL;
+
+		if (curr)
+			curr->next = data;
+		else
+			seq = data;
+
+		curr = data;
+	}
+
+	return sdp_data_alloc(SDP_SEQ8, seq);
+}
+
+static void extract_svclass_uuid(sdp_data_t *data, uuid_t *uuid)
+{
+	sdp_data_t *d;
+
+	if (!data || !SDP_IS_SEQ(data->dtd))
+		return;
+
+	d = data->val.dataseq;
+	if (!d)
+		return;
+
+	if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128)
+		return;
+
+	*uuid = d->val.uuid;
+}
+
+int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
+{
+	sdp_data_t *p = sdp_data_get(rec, attr);
+
+	if (p)
+		return -1;
+
+	d->attrId = attr;
+	rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
+
+	if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+		extract_svclass_uuid(d, &rec->svclass);
+
+	return 0;
+}
+
+void sdp_attr_remove(sdp_record_t *rec, uint16_t attr)
+{
+	sdp_data_t *d = sdp_data_get(rec, attr);
+
+	if (d)
+		rec->attrlist = sdp_list_remove(rec->attrlist, d);
+
+	if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+		memset(&rec->svclass, 0, sizeof(rec->svclass));
+}
+
+void sdp_set_seq_len(uint8_t *ptr, uint32_t length)
+{
+	uint8_t dtd = *ptr++;
+
+	switch (dtd) {
+	case SDP_SEQ8:
+	case SDP_ALT8:
+	case SDP_TEXT_STR8:
+	case SDP_URL_STR8:
+		*ptr = (uint8_t) length;
+		break;
+	case SDP_SEQ16:
+	case SDP_ALT16:
+	case SDP_TEXT_STR16:
+	case SDP_URL_STR16:
+		bt_put_be16(length, ptr);
+		break;
+	case SDP_SEQ32:
+	case SDP_ALT32:
+	case SDP_TEXT_STR32:
+	case SDP_URL_STR32:
+		bt_put_be32(length, ptr);
+		break;
+	}
+}
+
+static int sdp_get_data_type_size(uint8_t dtd)
+{
+	int size = sizeof(uint8_t);
+
+	switch (dtd) {
+	case SDP_SEQ8:
+	case SDP_TEXT_STR8:
+	case SDP_URL_STR8:
+	case SDP_ALT8:
+		size += sizeof(uint8_t);
+		break;
+	case SDP_SEQ16:
+	case SDP_TEXT_STR16:
+	case SDP_URL_STR16:
+	case SDP_ALT16:
+		size += sizeof(uint16_t);
+		break;
+	case SDP_SEQ32:
+	case SDP_TEXT_STR32:
+	case SDP_URL_STR32:
+	case SDP_ALT32:
+		size += sizeof(uint32_t);
+		break;
+	}
+
+	return size;
+}
+
+void sdp_set_attrid(sdp_buf_t *buf, uint16_t attr)
+{
+	uint8_t *p = buf->data;
+
+	/* data type for attr */
+	*p++ = SDP_UINT16;
+	buf->data_size = sizeof(uint8_t);
+	bt_put_be16(attr, p);
+	buf->data_size += sizeof(uint16_t);
+}
+
+static int get_data_size(sdp_buf_t *buf, sdp_data_t *sdpdata)
+{
+	sdp_data_t *d;
+	int n = 0;
+
+	for (d = sdpdata->val.dataseq; d; d = d->next) {
+		if (buf->data)
+			n += sdp_gen_pdu(buf, d);
+		else
+			n += sdp_gen_buffer(buf, d);
+	}
+
+	return n;
+}
+
+static int sdp_get_data_size(sdp_buf_t *buf, sdp_data_t *d)
+{
+	uint32_t data_size = 0;
+	uint8_t dtd = d->dtd;
+
+	switch (dtd) {
+	case SDP_DATA_NIL:
+		break;
+	case SDP_UINT8:
+		data_size = sizeof(uint8_t);
+		break;
+	case SDP_UINT16:
+		data_size = sizeof(uint16_t);
+		break;
+	case SDP_UINT32:
+		data_size = sizeof(uint32_t);
+		break;
+	case SDP_UINT64:
+		data_size = sizeof(uint64_t);
+		break;
+	case SDP_UINT128:
+		data_size = sizeof(uint128_t);
+		break;
+	case SDP_INT8:
+	case SDP_BOOL:
+		data_size = sizeof(int8_t);
+		break;
+	case SDP_INT16:
+		data_size = sizeof(int16_t);
+		break;
+	case SDP_INT32:
+		data_size = sizeof(int32_t);
+		break;
+	case SDP_INT64:
+		data_size = sizeof(int64_t);
+		break;
+	case SDP_INT128:
+		data_size = sizeof(uint128_t);
+		break;
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		data_size = d->unitSize - sizeof(uint8_t);
+		break;
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		data_size = get_data_size(buf, d);
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		data_size = get_data_size(buf, d);
+		break;
+	case SDP_UUID16:
+		data_size = sizeof(uint16_t);
+		break;
+	case SDP_UUID32:
+		data_size = sizeof(uint32_t);
+		break;
+	case SDP_UUID128:
+		data_size = sizeof(uint128_t);
+		break;
+	default:
+		break;
+	}
+
+	return data_size;
+}
+
+static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d)
+{
+	int orig = buf->buf_size;
+
+	if (buf->buf_size == 0 && d->dtd == 0) {
+		/* create initial sequence */
+		buf->buf_size += sizeof(uint8_t);
+
+		/* reserve space for sequence size */
+		buf->buf_size += sizeof(uint8_t);
+	}
+
+	/* attribute length */
+	buf->buf_size += sizeof(uint8_t) + sizeof(uint16_t);
+
+	buf->buf_size += sdp_get_data_type_size(d->dtd);
+	buf->buf_size += sdp_get_data_size(buf, d);
+
+	if (buf->buf_size > UCHAR_MAX && d->dtd == SDP_SEQ8)
+		buf->buf_size += sizeof(uint8_t);
+
+	return buf->buf_size - orig;
+}
+
+int sdp_gen_pdu(sdp_buf_t *buf, sdp_data_t *d)
+{
+	uint32_t pdu_size, data_size;
+	unsigned char *src = NULL, is_seq = 0, is_alt = 0;
+	uint16_t u16;
+	uint32_t u32;
+	uint64_t u64;
+	uint128_t u128;
+	uint8_t *seqp = buf->data + buf->data_size;
+	uint32_t orig_data_size = buf->data_size;
+
+recalculate:
+	pdu_size = sdp_get_data_type_size(d->dtd);
+	buf->data_size += pdu_size;
+
+	data_size = sdp_get_data_size(buf, d);
+	if (data_size > UCHAR_MAX && d->dtd == SDP_SEQ8) {
+		buf->data_size = orig_data_size;
+		d->dtd = SDP_SEQ16;
+		goto recalculate;
+	}
+
+	*seqp = d->dtd;
+
+	switch (d->dtd) {
+	case SDP_DATA_NIL:
+		break;
+	case SDP_UINT8:
+		src = &d->val.uint8;
+		break;
+	case SDP_UINT16:
+		u16 = htons(d->val.uint16);
+		src = (unsigned char *) &u16;
+		break;
+	case SDP_UINT32:
+		u32 = htonl(d->val.uint32);
+		src = (unsigned char *) &u32;
+		break;
+	case SDP_UINT64:
+		u64 = hton64(d->val.uint64);
+		src = (unsigned char *) &u64;
+		break;
+	case SDP_UINT128:
+		hton128(&d->val.uint128, &u128);
+		src = (unsigned char *) &u128;
+		break;
+	case SDP_INT8:
+	case SDP_BOOL:
+		src = (unsigned char *) &d->val.int8;
+		break;
+	case SDP_INT16:
+		u16 = htons(d->val.int16);
+		src = (unsigned char *) &u16;
+		break;
+	case SDP_INT32:
+		u32 = htonl(d->val.int32);
+		src = (unsigned char *) &u32;
+		break;
+	case SDP_INT64:
+		u64 = hton64(d->val.int64);
+		src = (unsigned char *) &u64;
+		break;
+	case SDP_INT128:
+		hton128(&d->val.int128, &u128);
+		src = (unsigned char *) &u128;
+		break;
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		src = (unsigned char *) d->val.str;
+		sdp_set_seq_len(seqp, data_size);
+		break;
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		is_seq = 1;
+		sdp_set_seq_len(seqp, data_size);
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		is_alt = 1;
+		sdp_set_seq_len(seqp, data_size);
+		break;
+	case SDP_UUID16:
+		u16 = htons(d->val.uuid.value.uuid16);
+		src = (unsigned char *) &u16;
+		break;
+	case SDP_UUID32:
+		u32 = htonl(d->val.uuid.value.uuid32);
+		src = (unsigned char *) &u32;
+		break;
+	case SDP_UUID128:
+		src = (unsigned char *) &d->val.uuid.value.uuid128;
+		break;
+	default:
+		break;
+	}
+
+	if (!is_seq && !is_alt) {
+		if (src && buf->buf_size >= buf->data_size + data_size) {
+			memcpy(buf->data + buf->data_size, src, data_size);
+			buf->data_size += data_size;
+		} else if (d->dtd != SDP_DATA_NIL) {
+			SDPDBG("Gen PDU : Can't copy from invalid source or dest");
+		}
+	}
+
+	pdu_size += data_size;
+
+	return pdu_size;
+}
+
+static void sdp_attr_pdu(void *value, void *udata)
+{
+	sdp_append_to_pdu((sdp_buf_t *)udata, (sdp_data_t *)value);
+}
+
+static void sdp_attr_size(void *value, void *udata)
+{
+	sdp_gen_buffer((sdp_buf_t *)udata, (sdp_data_t *)value);
+}
+
+int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *buf)
+{
+	memset(buf, 0, sizeof(sdp_buf_t));
+	sdp_list_foreach(rec->attrlist, sdp_attr_size, buf);
+
+	buf->data = malloc(buf->buf_size);
+	if (!buf->data)
+		return -ENOMEM;
+	buf->data_size = 0;
+	memset(buf->data, 0, buf->buf_size);
+
+	sdp_list_foreach(rec->attrlist, sdp_attr_pdu, buf);
+
+	return 0;
+}
+
+void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
+{
+	sdp_data_t *p;
+
+	if (!rec)
+		return;
+
+	p = sdp_data_get(rec, attr);
+	if (p) {
+		rec->attrlist = sdp_list_remove(rec->attrlist, p);
+		sdp_data_free(p);
+	}
+
+	d->attrId = attr;
+	rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
+
+	if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+		extract_svclass_uuid(d, &rec->svclass);
+}
+
+int sdp_attrid_comp_func(const void *key1, const void *key2)
+{
+	const sdp_data_t *d1 = (const sdp_data_t *)key1;
+	const sdp_data_t *d2 = (const sdp_data_t *)key2;
+
+	if (d1 && d2)
+		return d1->attrId - d2->attrId;
+	return 0;
+}
+
+static void data_seq_free(sdp_data_t *seq)
+{
+	sdp_data_t *d = seq->val.dataseq;
+
+	while (d) {
+		sdp_data_t *next = d->next;
+		sdp_data_free(d);
+		d = next;
+	}
+}
+
+void sdp_data_free(sdp_data_t *d)
+{
+	switch (d->dtd) {
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		data_seq_free(d);
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+		free(d->val.str);
+		break;
+	}
+	free(d);
+}
+
+int sdp_uuid_extract(const uint8_t *p, int bufsize, uuid_t *uuid, int *scanned)
+{
+	uint8_t type;
+
+	if (bufsize < (int) sizeof(uint8_t)) {
+		SDPERR("Unexpected end of packet");
+		return -1;
+	}
+
+	type = *(const uint8_t *) p;
+
+	if (!SDP_IS_UUID(type)) {
+		SDPERR("Unknown data type : %d expecting a svc UUID", type);
+		return -1;
+	}
+	p += sizeof(uint8_t);
+	*scanned += sizeof(uint8_t);
+	bufsize -= sizeof(uint8_t);
+	if (type == SDP_UUID16) {
+		if (bufsize < (int) sizeof(uint16_t)) {
+			SDPERR("Not enough room for 16-bit UUID");
+			return -1;
+		}
+		sdp_uuid16_create(uuid, bt_get_be16(p));
+		*scanned += sizeof(uint16_t);
+	} else if (type == SDP_UUID32) {
+		if (bufsize < (int) sizeof(uint32_t)) {
+			SDPERR("Not enough room for 32-bit UUID");
+			return -1;
+		}
+		sdp_uuid32_create(uuid, bt_get_be32(p));
+		*scanned += sizeof(uint32_t);
+	} else {
+		if (bufsize < (int) sizeof(uint128_t)) {
+			SDPERR("Not enough room for 128-bit UUID");
+			return -1;
+		}
+		sdp_uuid128_create(uuid, p);
+		*scanned += sizeof(uint128_t);
+	}
+	return 0;
+}
+
+static sdp_data_t *extract_int(const void *p, int bufsize, int *len)
+{
+	sdp_data_t *d;
+
+	if (bufsize < (int) sizeof(uint8_t)) {
+		SDPERR("Unexpected end of packet");
+		return NULL;
+	}
+
+	d = malloc(sizeof(sdp_data_t));
+	if (!d)
+		return NULL;
+
+	SDPDBG("Extracting integer");
+	memset(d, 0, sizeof(sdp_data_t));
+	d->dtd = *(uint8_t *) p;
+	p += sizeof(uint8_t);
+	*len += sizeof(uint8_t);
+	bufsize -= sizeof(uint8_t);
+
+	switch (d->dtd) {
+	case SDP_DATA_NIL:
+		break;
+	case SDP_BOOL:
+	case SDP_INT8:
+	case SDP_UINT8:
+		if (bufsize < (int) sizeof(uint8_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		*len += sizeof(uint8_t);
+		d->val.uint8 = *(uint8_t *) p;
+		break;
+	case SDP_INT16:
+	case SDP_UINT16:
+		if (bufsize < (int) sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		*len += sizeof(uint16_t);
+		d->val.uint16 = bt_get_be16(p);
+		break;
+	case SDP_INT32:
+	case SDP_UINT32:
+		if (bufsize < (int) sizeof(uint32_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		*len += sizeof(uint32_t);
+		d->val.uint32 = bt_get_be32(p);
+		break;
+	case SDP_INT64:
+	case SDP_UINT64:
+		if (bufsize < (int) sizeof(uint64_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		*len += sizeof(uint64_t);
+		d->val.uint64 = bt_get_be64(p);
+		break;
+	case SDP_INT128:
+	case SDP_UINT128:
+		if (bufsize < (int) sizeof(uint128_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		*len += sizeof(uint128_t);
+		ntoh128((uint128_t *) p, &d->val.uint128);
+		break;
+	default:
+		free(d);
+		d = NULL;
+	}
+	return d;
+}
+
+static sdp_data_t *extract_uuid(const uint8_t *p, int bufsize, int *len,
+							sdp_record_t *rec)
+{
+	sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+	if (!d)
+		return NULL;
+
+	SDPDBG("Extracting UUID");
+	memset(d, 0, sizeof(sdp_data_t));
+	if (sdp_uuid_extract(p, bufsize, &d->val.uuid, len) < 0) {
+		free(d);
+		return NULL;
+	}
+	d->dtd = *p;
+	if (rec)
+		sdp_pattern_add_uuid(rec, &d->val.uuid);
+	return d;
+}
+
+/*
+ * Extract strings from the PDU (could be service description and similar info)
+ */
+static sdp_data_t *extract_str(const void *p, int bufsize, int *len)
+{
+	char *s;
+	int n;
+	sdp_data_t *d;
+
+	if (bufsize < (int) sizeof(uint8_t)) {
+		SDPERR("Unexpected end of packet");
+		return NULL;
+	}
+
+	d = malloc(sizeof(sdp_data_t));
+	if (!d)
+		return NULL;
+
+	memset(d, 0, sizeof(sdp_data_t));
+	d->dtd = *(uint8_t *) p;
+	p += sizeof(uint8_t);
+	*len += sizeof(uint8_t);
+	bufsize -= sizeof(uint8_t);
+
+	switch (d->dtd) {
+	case SDP_TEXT_STR8:
+	case SDP_URL_STR8:
+		if (bufsize < (int) sizeof(uint8_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		n = *(uint8_t *) p;
+		p += sizeof(uint8_t);
+		*len += sizeof(uint8_t);
+		bufsize -= sizeof(uint8_t);
+		break;
+	case SDP_TEXT_STR16:
+	case SDP_URL_STR16:
+		if (bufsize < (int) sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			free(d);
+			return NULL;
+		}
+		n = bt_get_be16(p);
+		p += sizeof(uint16_t);
+		*len += sizeof(uint16_t);
+		bufsize -= sizeof(uint16_t);
+		break;
+	default:
+		SDPERR("Sizeof text string > UINT16_MAX");
+		free(d);
+		return NULL;
+	}
+
+	if (bufsize < n) {
+		SDPERR("String too long to fit in packet");
+		free(d);
+		return NULL;
+	}
+
+	s = malloc(n + 1);
+	if (!s) {
+		SDPERR("Not enough memory for incoming string");
+		free(d);
+		return NULL;
+	}
+	memset(s, 0, n + 1);
+	memcpy(s, p, n);
+
+	*len += n;
+
+	SDPDBG("Len : %d", n);
+	SDPDBG("Str : %s", s);
+
+	d->val.str = s;
+	d->unitSize = n + sizeof(uint8_t);
+	return d;
+}
+
+/*
+ * Extract the sequence type and its length, and return offset into buf
+ * or 0 on failure.
+ */
+int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size)
+{
+	uint8_t dtd;
+	int scanned = sizeof(uint8_t);
+
+	if (bufsize < (int) sizeof(uint8_t)) {
+		SDPERR("Unexpected end of packet");
+		return 0;
+	}
+
+	dtd = *(uint8_t *) buf;
+	buf += sizeof(uint8_t);
+	bufsize -= sizeof(uint8_t);
+	*dtdp = dtd;
+	switch (dtd) {
+	case SDP_SEQ8:
+	case SDP_ALT8:
+		if (bufsize < (int) sizeof(uint8_t)) {
+			SDPERR("Unexpected end of packet");
+			return 0;
+		}
+		*size = *(uint8_t *) buf;
+		scanned += sizeof(uint8_t);
+		break;
+	case SDP_SEQ16:
+	case SDP_ALT16:
+		if (bufsize < (int) sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			return 0;
+		}
+		*size = bt_get_be16(buf);
+		scanned += sizeof(uint16_t);
+		break;
+	case SDP_SEQ32:
+	case SDP_ALT32:
+		if (bufsize < (int) sizeof(uint32_t)) {
+			SDPERR("Unexpected end of packet");
+			return 0;
+		}
+		*size = bt_get_be32(buf);
+		scanned += sizeof(uint32_t);
+		break;
+	default:
+		SDPERR("Unknown sequence type, aborting");
+		return 0;
+	}
+	return scanned;
+}
+
+static sdp_data_t *extract_seq(const void *p, int bufsize, int *len,
+							sdp_record_t *rec)
+{
+	int seqlen, n = 0;
+	sdp_data_t *curr, *prev;
+	sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+	if (!d)
+		return NULL;
+
+	SDPDBG("Extracting SEQ");
+	memset(d, 0, sizeof(sdp_data_t));
+	*len = sdp_extract_seqtype(p, bufsize, &d->dtd, &seqlen);
+	SDPDBG("Sequence Type : 0x%x length : 0x%x", d->dtd, seqlen);
+
+	if (*len == 0)
+		return d;
+
+	if (*len > bufsize) {
+		SDPERR("Packet not big enough to hold sequence.");
+		free(d);
+		return NULL;
+	}
+
+	p += *len;
+	bufsize -= *len;
+	prev = NULL;
+	while (n < seqlen) {
+		int attrlen = 0;
+		curr = sdp_extract_attr(p, bufsize, &attrlen, rec);
+		if (curr == NULL)
+			break;
+
+		if (prev)
+			prev->next = curr;
+		else
+			d->val.dataseq = curr;
+		prev = curr;
+		p += attrlen;
+		n += attrlen;
+		bufsize -= attrlen;
+
+		SDPDBG("Extracted: %d SequenceLength: %d", n, seqlen);
+	}
+
+	*len += n;
+	return d;
+}
+
+sdp_data_t *sdp_extract_attr(const uint8_t *p, int bufsize, int *size,
+							sdp_record_t *rec)
+{
+	sdp_data_t *elem;
+	int n = 0;
+	uint8_t dtd;
+
+	if (bufsize < (int) sizeof(uint8_t)) {
+		SDPERR("Unexpected end of packet");
+		return NULL;
+	}
+
+	dtd = *(const uint8_t *)p;
+
+	SDPDBG("extract_attr: dtd=0x%x", dtd);
+	switch (dtd) {
+	case SDP_DATA_NIL:
+	case SDP_BOOL:
+	case SDP_UINT8:
+	case SDP_UINT16:
+	case SDP_UINT32:
+	case SDP_UINT64:
+	case SDP_UINT128:
+	case SDP_INT8:
+	case SDP_INT16:
+	case SDP_INT32:
+	case SDP_INT64:
+	case SDP_INT128:
+		elem = extract_int(p, bufsize, &n);
+		break;
+	case SDP_UUID16:
+	case SDP_UUID32:
+	case SDP_UUID128:
+		elem = extract_uuid(p, bufsize, &n, rec);
+		break;
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		elem = extract_str(p, bufsize, &n);
+		break;
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		elem = extract_seq(p, bufsize, &n, rec);
+		break;
+	default:
+		SDPERR("Unknown data descriptor : 0x%x terminating", dtd);
+		return NULL;
+	}
+	*size += n;
+	return elem;
+}
+
+#ifdef SDP_DEBUG
+static void attr_print_func(void *value, void *userData)
+{
+	sdp_data_t *d = (sdp_data_t *)value;
+
+	SDPDBG("=====================================");
+	SDPDBG("ATTRIBUTE IDENTIFIER : 0x%x",  d->attrId);
+	SDPDBG("ATTRIBUTE VALUE PTR : %p", value);
+	if (d)
+		sdp_data_print(d);
+	else
+		SDPDBG("NULL value");
+	SDPDBG("=====================================");
+}
+
+void sdp_print_service_attr(sdp_list_t *svcAttrList)
+{
+	SDPDBG("Printing service attr list %p", svcAttrList);
+	sdp_list_foreach(svcAttrList, attr_print_func, NULL);
+	SDPDBG("Printed service attr list %p", svcAttrList);
+}
+#endif
+
+sdp_record_t *sdp_extract_pdu(const uint8_t *buf, int bufsize, int *scanned)
+{
+	int extracted = 0, seqlen = 0;
+	uint8_t dtd;
+	uint16_t attr;
+	sdp_record_t *rec = sdp_record_alloc();
+	const uint8_t *p = buf;
+
+	*scanned = sdp_extract_seqtype(buf, bufsize, &dtd, &seqlen);
+	p += *scanned;
+	bufsize -= *scanned;
+	rec->attrlist = NULL;
+
+	while (extracted < seqlen && bufsize > 0) {
+		int n = sizeof(uint8_t), attrlen = 0;
+		sdp_data_t *data = NULL;
+
+		SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
+							seqlen, extracted);
+
+		if (bufsize < n + (int) sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			break;
+		}
+
+		dtd = *(uint8_t *) p;
+		attr = bt_get_be16(p + n);
+		n += sizeof(uint16_t);
+
+		SDPDBG("DTD of attrId : %d Attr id : 0x%x ", dtd, attr);
+
+		data = sdp_extract_attr(p + n, bufsize - n, &attrlen, rec);
+
+		SDPDBG("Attr id : 0x%x attrValueLength : %d", attr, attrlen);
+
+		n += attrlen;
+		if (data == NULL) {
+			SDPDBG("Terminating extraction of attributes");
+			break;
+		}
+
+		if (attr == SDP_ATTR_RECORD_HANDLE)
+			rec->handle = data->val.uint32;
+
+		if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+			extract_svclass_uuid(data, &rec->svclass);
+
+		extracted += n;
+		p += n;
+		bufsize -= n;
+		sdp_attr_replace(rec, attr, data);
+
+		SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
+							seqlen, extracted);
+	}
+#ifdef SDP_DEBUG
+	SDPDBG("Successful extracting of Svc Rec attributes");
+	sdp_print_service_attr(rec->attrlist);
+#endif
+	*scanned += seqlen;
+	return rec;
+}
+
+static void sdp_copy_pattern(void *value, void *udata)
+{
+	uuid_t *uuid = value;
+	sdp_record_t *rec = udata;
+
+	sdp_pattern_add_uuid(rec, uuid);
+}
+
+static void *sdp_data_value(sdp_data_t *data, uint32_t *len)
+{
+	void *val = NULL;
+
+	switch (data->dtd) {
+	case SDP_DATA_NIL:
+		break;
+	case SDP_UINT8:
+		val = &data->val.uint8;
+		break;
+	case SDP_INT8:
+	case SDP_BOOL:
+		val = &data->val.int8;
+		break;
+	case SDP_UINT16:
+		val = &data->val.uint16;
+		break;
+	case SDP_INT16:
+		val = &data->val.int16;
+		break;
+	case SDP_UINT32:
+		val = &data->val.uint32;
+		break;
+	case SDP_INT32:
+		val = &data->val.int32;
+		break;
+	case SDP_INT64:
+		val = &data->val.int64;
+		break;
+	case SDP_UINT64:
+		val = &data->val.uint64;
+		break;
+	case SDP_UINT128:
+		val = &data->val.uint128;
+		break;
+	case SDP_INT128:
+		val = &data->val.int128;
+		break;
+	case SDP_UUID16:
+		val = &data->val.uuid.value.uuid16;
+		break;
+	case SDP_UUID32:
+		val = &data->val.uuid.value.uuid32;
+		break;
+	case SDP_UUID128:
+		val = &data->val.uuid.value.uuid128;
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_URL_STR32:
+	case SDP_TEXT_STR32:
+		val = data->val.str;
+		if (len)
+			*len = data->unitSize - 1;
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		val = sdp_copy_seq(data->val.dataseq);
+		break;
+	}
+
+	return val;
+}
+
+static sdp_data_t *sdp_copy_seq(sdp_data_t *data)
+{
+	sdp_data_t *tmp, *seq = NULL, *cur = NULL;
+
+	for (tmp = data; tmp; tmp = tmp->next) {
+		sdp_data_t *datatmp;
+		void *value;
+
+		value = sdp_data_value(tmp, NULL);
+		datatmp = sdp_data_alloc_with_length(tmp->dtd, value,
+								tmp->unitSize);
+
+		if (cur)
+			cur->next = datatmp;
+		else
+			seq = datatmp;
+
+		cur = datatmp;
+	}
+
+	return seq;
+}
+
+static void sdp_copy_attrlist(void *value, void *udata)
+{
+	sdp_data_t *data = value;
+	sdp_record_t *rec = udata;
+	void *val;
+	uint32_t len = 0;
+
+	val = sdp_data_value(data, &len);
+
+	if (!len)
+		sdp_attr_add_new(rec, data->attrId, data->dtd, val);
+	else
+		sdp_attr_add_new_with_length(rec, data->attrId,
+							data->dtd, val, len);
+}
+
+sdp_record_t *sdp_copy_record(sdp_record_t *rec)
+{
+	sdp_record_t *cpy;
+
+	cpy = sdp_record_alloc();
+
+	cpy->handle = rec->handle;
+
+	sdp_list_foreach(rec->pattern, sdp_copy_pattern, cpy);
+	sdp_list_foreach(rec->attrlist, sdp_copy_attrlist, cpy);
+
+	cpy->svclass = rec->svclass;
+
+	return cpy;
+}
+
+#ifdef SDP_DEBUG
+static void print_dataseq(sdp_data_t *p)
+{
+	sdp_data_t *d;
+
+	for (d = p; d; d = d->next)
+		sdp_data_print(d);
+}
+#endif
+
+void sdp_record_print(const sdp_record_t *rec)
+{
+	sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+	if (d && SDP_IS_TEXT_STR(d->dtd))
+		printf("Service Name: %.*s\n", d->unitSize, d->val.str);
+	d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY);
+	if (d && SDP_IS_TEXT_STR(d->dtd))
+		printf("Service Description: %.*s\n", d->unitSize, d->val.str);
+	d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY);
+	if (d && SDP_IS_TEXT_STR(d->dtd))
+		printf("Service Provider: %.*s\n", d->unitSize, d->val.str);
+}
+
+#ifdef SDP_DEBUG
+void sdp_data_print(sdp_data_t *d)
+{
+	switch (d->dtd) {
+	case SDP_DATA_NIL:
+		SDPDBG("NIL");
+		break;
+	case SDP_BOOL:
+	case SDP_UINT8:
+	case SDP_UINT16:
+	case SDP_UINT32:
+	case SDP_UINT64:
+	case SDP_UINT128:
+	case SDP_INT8:
+	case SDP_INT16:
+	case SDP_INT32:
+	case SDP_INT64:
+	case SDP_INT128:
+		SDPDBG("Integer : 0x%x", d->val.uint32);
+		break;
+	case SDP_UUID16:
+	case SDP_UUID32:
+	case SDP_UUID128:
+		SDPDBG("UUID");
+		sdp_uuid_print(&d->val.uuid);
+		break;
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+		SDPDBG("Text : %s", d->val.str);
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		SDPDBG("URL : %s", d->val.str);
+		break;
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		print_dataseq(d->val.dataseq);
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		SDPDBG("Data Sequence Alternates");
+		print_dataseq(d->val.dataseq);
+		break;
+	}
+}
+#endif
+
+sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attrId)
+{
+	if (rec && rec->attrlist) {
+		sdp_data_t sdpTemplate;
+		sdp_list_t *p;
+
+		sdpTemplate.attrId = attrId;
+		p = sdp_list_find(rec->attrlist, &sdpTemplate, sdp_attrid_comp_func);
+		if (p)
+			return p->data;
+	}
+	return NULL;
+}
+
+static int sdp_send_req(sdp_session_t *session, uint8_t *buf, uint32_t size)
+{
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = send(session->sock, buf + sent, size - sent, 0);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+	return 0;
+}
+
+static int sdp_read_rsp(sdp_session_t *session, uint8_t *buf, uint32_t size)
+{
+	fd_set readFds;
+	struct timeval timeout = { SDP_RESPONSE_TIMEOUT, 0 };
+
+	FD_ZERO(&readFds);
+	FD_SET(session->sock, &readFds);
+	SDPDBG("Waiting for response");
+	if (select(session->sock + 1, &readFds, NULL, NULL, &timeout) == 0) {
+		SDPERR("Client timed out");
+		errno = ETIMEDOUT;
+		return -1;
+	}
+	return recv(session->sock, buf, size, 0);
+}
+
+/*
+ * generic send request, wait for response method.
+ */
+int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *reqbuf,
+			uint8_t *rspbuf, uint32_t reqsize, uint32_t *rspsize)
+{
+	int n;
+	sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+	sdp_pdu_hdr_t *rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+
+	SDPDBG("");
+	if (0 > sdp_send_req(session, reqbuf, reqsize)) {
+		SDPERR("Error sending data:%m");
+		return -1;
+	}
+	n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
+	if (0 > n)
+		return -1;
+	SDPDBG("Read : %d", n);
+	if (n == 0 || reqhdr->tid != rsphdr->tid) {
+		errno = EPROTO;
+		return -1;
+	}
+	*rspsize = n;
+	return 0;
+}
+
+/*
+ * singly-linked lists (after openobex implementation)
+ */
+sdp_list_t *sdp_list_append(sdp_list_t *p, void *d)
+{
+	sdp_list_t *q, *n = malloc(sizeof(sdp_list_t));
+
+	if (!n)
+		return NULL;
+
+	n->data = d;
+	n->next = 0;
+
+	if (!p)
+		return n;
+
+	for (q = p; q->next; q = q->next);
+	q->next = n;
+
+	return p;
+}
+
+sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d)
+{
+	sdp_list_t *p, *q;
+
+	for (q = 0, p = list; p; q = p, p = p->next)
+		if (p->data == d) {
+			if (q)
+				q->next = p->next;
+			else
+				list = p->next;
+			free(p);
+			break;
+		}
+
+	return list;
+}
+
+sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *d,
+							sdp_comp_func_t f)
+{
+	sdp_list_t *q, *p, *n;
+
+	n = malloc(sizeof(sdp_list_t));
+	if (!n)
+		return NULL;
+	n->data = d;
+	for (q = 0, p = list; p; q = p, p = p->next)
+		if (f(p->data, d) >= 0)
+			break;
+	/* insert between q and p; if !q insert at head */
+	if (q)
+		q->next = n;
+	else
+		list = n;
+	n->next = p;
+	return list;
+}
+
+/*
+ * Every element of the list points to things which need
+ * to be free()'d. This method frees the list's contents
+ */
+void sdp_list_free(sdp_list_t *list, sdp_free_func_t f)
+{
+	sdp_list_t *next;
+	while (list) {
+		next = list->next;
+		if (f)
+			f(list->data);
+		free(list);
+		list = next;
+	}
+}
+
+static inline int __find_port(sdp_data_t *seq, int proto)
+{
+	if (!seq || !seq->next)
+		return 0;
+
+	if (SDP_IS_UUID(seq->dtd) && sdp_uuid_to_proto(&seq->val.uuid) == proto) {
+		seq = seq->next;
+		switch (seq->dtd) {
+		case SDP_UINT8:
+			return seq->val.uint8;
+		case SDP_UINT16:
+			return seq->val.uint16;
+		}
+	}
+	return 0;
+}
+
+int sdp_get_proto_port(const sdp_list_t *list, int proto)
+{
+	if (proto != L2CAP_UUID && proto != RFCOMM_UUID) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	for (; list; list = list->next) {
+		sdp_list_t *p;
+		for (p = list->data; p; p = p->next) {
+			sdp_data_t *seq = p->data;
+			int port = __find_port(seq, proto);
+			if (port)
+				return port;
+		}
+	}
+	return 0;
+}
+
+sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto)
+{
+	for (; list; list = list->next) {
+		sdp_list_t *p;
+		for (p = list->data; p; p = p->next) {
+			sdp_data_t *seq = p->data;
+			if (SDP_IS_UUID(seq->dtd) &&
+					sdp_uuid_to_proto(&seq->val.uuid) == proto)
+				return seq->next;
+		}
+	}
+	return NULL;
+}
+
+static int sdp_get_proto_descs(uint16_t attr_id, const sdp_record_t *rec,
+							sdp_list_t **pap)
+{
+	sdp_data_t *pdlist, *curr;
+	sdp_list_t *ap = NULL;
+
+	pdlist = sdp_data_get(rec, attr_id);
+	if (pdlist == NULL) {
+		errno = ENODATA;
+		return -1;
+	}
+
+	SDPDBG("Attribute value type: 0x%02x", pdlist->dtd);
+
+	if (attr_id == SDP_ATTR_ADD_PROTO_DESC_LIST) {
+		if (!SDP_IS_SEQ(pdlist->dtd)) {
+			errno = EINVAL;
+			return -1;
+		}
+		pdlist = pdlist->val.dataseq;
+	}
+
+	for (; pdlist; pdlist = pdlist->next) {
+		sdp_list_t *pds = NULL;
+
+		if (!SDP_IS_SEQ(pdlist->dtd) && !SDP_IS_ALT(pdlist->dtd))
+			goto failed;
+
+		for (curr = pdlist->val.dataseq; curr; curr = curr->next) {
+			if (!SDP_IS_SEQ(curr->dtd)) {
+				sdp_list_free(pds, NULL);
+				goto failed;
+			}
+			pds = sdp_list_append(pds, curr->val.dataseq);
+		}
+
+		ap = sdp_list_append(ap, pds);
+	}
+
+	*pap = ap;
+
+	return 0;
+
+failed:
+	sdp_list_foreach(ap, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(ap, NULL);
+	errno = EINVAL;
+
+	return -1;
+}
+
+int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
+{
+	return sdp_get_proto_descs(SDP_ATTR_PROTO_DESC_LIST, rec, pap);
+}
+
+int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
+{
+	return sdp_get_proto_descs(SDP_ATTR_ADD_PROTO_DESC_LIST, rec, pap);
+}
+
+int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr,
+							sdp_list_t **seqp)
+{
+	sdp_data_t *sdpdata = sdp_data_get(rec, attr);
+
+	*seqp = NULL;
+	if (sdpdata && SDP_IS_SEQ(sdpdata->dtd)) {
+		sdp_data_t *d;
+		for (d = sdpdata->val.dataseq; d; d = d->next) {
+			uuid_t *u;
+			if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128) {
+				errno = EINVAL;
+				goto fail;
+			}
+
+			u = malloc(sizeof(uuid_t));
+			if (!u)
+				goto fail;
+
+			*u = d->val.uuid;
+			*seqp = sdp_list_append(*seqp, u);
+		}
+		return 0;
+	}
+fail:
+	sdp_list_free(*seqp, free);
+	*seqp = NULL;
+	return -1;
+}
+
+int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t aid, sdp_list_t *seq)
+{
+	int status = 0, i, len;
+	void **dtds, **values;
+	uint8_t uuid16 = SDP_UUID16;
+	uint8_t uuid32 = SDP_UUID32;
+	uint8_t uuid128 = SDP_UUID128;
+	sdp_list_t *p;
+
+	len = sdp_list_len(seq);
+	if (!seq || len == 0)
+		return -1;
+	dtds = malloc(len * sizeof(void *));
+	if (!dtds)
+		return -1;
+
+	values = malloc(len * sizeof(void *));
+	if (!values) {
+		free(dtds);
+		return -1;
+	}
+
+	for (p = seq, i = 0; i < len; i++, p = p->next) {
+		uuid_t *uuid = p->data;
+		if (uuid)
+			switch (uuid->type) {
+			case SDP_UUID16:
+				dtds[i] = &uuid16;
+				values[i] = &uuid->value.uuid16;
+				break;
+			case SDP_UUID32:
+				dtds[i] = &uuid32;
+				values[i] = &uuid->value.uuid32;
+				break;
+			case SDP_UUID128:
+				dtds[i] = &uuid128;
+				values[i] = &uuid->value.uuid128;
+				break;
+			default:
+				status = -1;
+				break;
+			}
+		else {
+			status = -1;
+			break;
+		}
+	}
+	if (status == 0) {
+		sdp_data_t *data = sdp_seq_alloc(dtds, values, len);
+		sdp_attr_replace(rec, aid, data);
+		sdp_pattern_add_uuidseq(rec, seq);
+	}
+	free(dtds);
+	free(values);
+	return status;
+}
+
+int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq)
+{
+	sdp_lang_attr_t *lang;
+	sdp_data_t *sdpdata, *curr_data;
+
+	*langSeq = NULL;
+	sdpdata = sdp_data_get(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST);
+	if (sdpdata == NULL) {
+		errno = ENODATA;
+		return -1;
+	}
+
+	if (!SDP_IS_SEQ(sdpdata->dtd))
+		goto invalid;
+	curr_data = sdpdata->val.dataseq;
+
+	while (curr_data) {
+		sdp_data_t *pCode, *pEncoding, *pOffset;
+
+		pCode = curr_data;
+		if (pCode->dtd != SDP_UINT16)
+			goto invalid;
+
+		/* LanguageBaseAttributeIDList entries are always grouped as
+		 * triplets */
+		if (!pCode->next || !pCode->next->next)
+			goto invalid;
+
+		pEncoding = pCode->next;
+		if (pEncoding->dtd != SDP_UINT16)
+			goto invalid;
+
+		pOffset = pEncoding->next;
+		if (pOffset->dtd != SDP_UINT16)
+			goto invalid;
+
+		lang = malloc(sizeof(sdp_lang_attr_t));
+		if (!lang) {
+			sdp_list_free(*langSeq, free);
+			*langSeq = NULL;
+			return -1;
+		}
+		lang->code_ISO639 = pCode->val.uint16;
+		lang->encoding = pEncoding->val.uint16;
+		lang->base_offset = pOffset->val.uint16;
+		SDPDBG("code_ISO639 :  0x%02x", lang->code_ISO639);
+		SDPDBG("encoding :     0x%02x", lang->encoding);
+		SDPDBG("base_offfset : 0x%02x", lang->base_offset);
+		*langSeq = sdp_list_append(*langSeq, lang);
+
+		curr_data = pOffset->next;
+	}
+
+	return 0;
+
+invalid:
+	sdp_list_free(*langSeq, free);
+	*langSeq = NULL;
+	errno = EINVAL;
+
+	return -1;
+}
+
+int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDescSeq)
+{
+	sdp_profile_desc_t *profDesc;
+	sdp_data_t *sdpdata, *seq;
+
+	*profDescSeq = NULL;
+	sdpdata = sdp_data_get(rec, SDP_ATTR_PFILE_DESC_LIST);
+	if (sdpdata == NULL) {
+		errno = ENODATA;
+		return -1;
+	}
+
+	if (!SDP_IS_SEQ(sdpdata->dtd) || sdpdata->val.dataseq == NULL)
+		goto invalid;
+
+	for (seq = sdpdata->val.dataseq; seq; seq = seq->next) {
+		uuid_t *uuid = NULL;
+		uint16_t version = 0x100;
+
+		if (SDP_IS_UUID(seq->dtd)) {
+			/* Mac OS X 10.7.3 and old Samsung phones do not comply
+			 * to the SDP specification for
+			 * BluetoothProfileDescriptorList. This workaround
+			 * allows to properly parse UUID/version from SDP
+			 * record published by these systems. */
+			sdp_data_t *next = seq->next;
+			uuid = &seq->val.uuid;
+			if (next && next->dtd == SDP_UINT16) {
+				version = next->val.uint16;
+				seq = next;
+			}
+		} else if (SDP_IS_SEQ(seq->dtd)) {
+			sdp_data_t *puuid, *pVnum;
+
+			puuid = seq->val.dataseq;
+			if (puuid == NULL || !SDP_IS_UUID(puuid->dtd))
+				goto invalid;
+
+			uuid = &puuid->val.uuid;
+
+			pVnum = puuid->next;
+			if (pVnum == NULL || pVnum->dtd != SDP_UINT16)
+				goto invalid;
+
+			version = pVnum->val.uint16;
+		} else
+			goto invalid;
+
+		if (uuid != NULL) {
+			profDesc = malloc(sizeof(sdp_profile_desc_t));
+			if (!profDesc) {
+				sdp_list_free(*profDescSeq, free);
+				*profDescSeq = NULL;
+				return -1;
+			}
+			profDesc->uuid = *uuid;
+			profDesc->version = version;
+#ifdef SDP_DEBUG
+			sdp_uuid_print(&profDesc->uuid);
+			SDPDBG("Vnum : 0x%04x", profDesc->version);
+#endif
+			*profDescSeq = sdp_list_append(*profDescSeq, profDesc);
+		}
+	}
+	return 0;
+
+invalid:
+	sdp_list_free(*profDescSeq, free);
+	*profDescSeq = NULL;
+	errno = EINVAL;
+
+	return -1;
+}
+
+int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **u16)
+{
+	sdp_data_t *d, *curr;
+
+	*u16 = NULL;
+	d = sdp_data_get(rec, SDP_ATTR_VERSION_NUM_LIST);
+	if (d == NULL) {
+		errno = ENODATA;
+		return -1;
+	}
+
+	if (!SDP_IS_SEQ(d->dtd) || d->val.dataseq == NULL)
+		goto invalid;
+
+	for (curr = d->val.dataseq; curr; curr = curr->next) {
+		if (curr->dtd != SDP_UINT16)
+			goto invalid;
+		*u16 = sdp_list_append(*u16, &curr->val.uint16);
+	}
+
+	return 0;
+
+invalid:
+	sdp_list_free(*u16, NULL);
+	*u16 = NULL;
+	errno = EINVAL;
+
+	return -1;
+}
+
+/* flexible extraction of basic attributes - Jean II */
+/* How do we expect caller to extract predefined data sequences? */
+int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attrid, int *value)
+{
+	sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
+
+	if (sdpdata)
+		/* Verify that it is what the caller expects */
+		if (sdpdata->dtd == SDP_BOOL || sdpdata->dtd == SDP_UINT8 ||
+		sdpdata->dtd == SDP_UINT16 || sdpdata->dtd == SDP_UINT32 ||
+		sdpdata->dtd == SDP_INT8 || sdpdata->dtd == SDP_INT16 ||
+		sdpdata->dtd == SDP_INT32) {
+			*value = sdpdata->val.uint32;
+			return 0;
+		}
+	errno = EINVAL;
+	return -1;
+}
+
+int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attrid, char *value,
+								int valuelen)
+{
+	sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
+	if (sdpdata)
+		/* Verify that it is what the caller expects */
+		if (SDP_IS_TEXT_STR(sdpdata->dtd))
+			if ((int) strlen(sdpdata->val.str) < valuelen) {
+				strcpy(value, sdpdata->val.str);
+				return 0;
+			}
+	errno = EINVAL;
+	return -1;
+}
+
+#define get_basic_attr(attrID, pAttrValue, fieldName)		\
+	sdp_data_t *data = sdp_data_get(rec, attrID);		\
+	if (data) {						\
+		*pAttrValue = data->val.fieldName;		\
+		return 0;					\
+	}							\
+	errno = EINVAL;						\
+	return -1;
+
+int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid)
+{
+	get_basic_attr(SDP_ATTR_SERVICE_ID, uuid, uuid);
+}
+
+int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid)
+{
+	get_basic_attr(SDP_ATTR_GROUP_ID, uuid, uuid);
+}
+
+int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState)
+{
+	get_basic_attr(SDP_ATTR_RECORD_STATE, svcRecState, uint32);
+}
+
+int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail)
+{
+	get_basic_attr(SDP_ATTR_SERVICE_AVAILABILITY, svcAvail, uint8);
+}
+
+int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo)
+{
+	get_basic_attr(SDP_ATTR_SVCINFO_TTL, svcTTLInfo, uint32);
+}
+
+int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState)
+{
+	get_basic_attr(SDP_ATTR_SVCDB_STATE, svcDBState, uint32);
+}
+
+/*
+ * NOTE that none of the setXXX() functions below will
+ * actually update the SDP server, unless the
+ * {register, update}sdp_record_t() function is invoked.
+ */
+
+int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd,
+							const void *value)
+{
+	sdp_data_t *d = sdp_data_alloc(dtd, value);
+	if (d) {
+		sdp_attr_replace(rec, attr, d);
+		return 0;
+	}
+	return -1;
+}
+
+static int sdp_attr_add_new_with_length(sdp_record_t *rec,
+	uint16_t attr, uint8_t dtd, const void *value, uint32_t len)
+{
+	sdp_data_t *d;
+
+	d = sdp_data_alloc_with_length(dtd, value, len);
+	if (!d)
+		return -1;
+
+	sdp_attr_replace(rec, attr, d);
+
+	return 0;
+}
+
+/*
+ * Set the information attributes of the service
+ * pointed to by rec. The attributes are
+ * service name, description and provider name
+ */
+void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov,
+							const char *desc)
+{
+	if (name)
+		sdp_attr_add_new(rec, SDP_ATTR_SVCNAME_PRIMARY,
+							SDP_TEXT_STR8, name);
+	if (prov)
+		sdp_attr_add_new(rec, SDP_ATTR_PROVNAME_PRIMARY,
+							SDP_TEXT_STR8, prov);
+	if (desc)
+		sdp_attr_add_new(rec, SDP_ATTR_SVCDESC_PRIMARY,
+							SDP_TEXT_STR8, desc);
+}
+
+static sdp_data_t *access_proto_to_dataseq(sdp_record_t *rec, sdp_list_t *proto)
+{
+	sdp_data_t *seq = NULL;
+	void *dtds[10], *values[10];
+	void **seqDTDs, **seqs;
+	int i, seqlen;
+	sdp_list_t *p;
+
+	seqlen = sdp_list_len(proto);
+	seqDTDs = malloc(seqlen * sizeof(void *));
+	if (!seqDTDs)
+		return NULL;
+
+	seqs = malloc(seqlen * sizeof(void *));
+	if (!seqs) {
+		free(seqDTDs);
+		return NULL;
+	}
+
+	for (i = 0, p = proto; p; p = p->next, i++) {
+		sdp_list_t *elt = p->data;
+		sdp_data_t *s;
+		uuid_t *uuid = NULL;
+		unsigned int pslen = 0;
+		for (; elt && pslen < ARRAY_SIZE(dtds); elt = elt->next, pslen++) {
+			sdp_data_t *d = elt->data;
+			dtds[pslen] = &d->dtd;
+			switch (d->dtd) {
+			case SDP_UUID16:
+				uuid = (uuid_t *) d;
+				values[pslen] = &uuid->value.uuid16;
+				break;
+			case SDP_UUID32:
+				uuid = (uuid_t *) d;
+				values[pslen] = &uuid->value.uuid32;
+				break;
+			case SDP_UUID128:
+				uuid = (uuid_t *) d;
+				values[pslen] = &uuid->value.uuid128;
+				break;
+			case SDP_UINT8:
+				values[pslen] = &d->val.uint8;
+				break;
+			case SDP_UINT16:
+				values[pslen] = &d->val.uint16;
+				break;
+			case SDP_SEQ8:
+			case SDP_SEQ16:
+			case SDP_SEQ32:
+				values[pslen] = d;
+				break;
+			/* FIXME: more */
+			}
+		}
+		s = sdp_seq_alloc(dtds, values, pslen);
+		if (s) {
+			seqDTDs[i] = &s->dtd;
+			seqs[i] = s;
+			if (uuid)
+				sdp_pattern_add_uuid(rec, uuid);
+		}
+	}
+	seq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
+	free(seqDTDs);
+	free(seqs);
+	return seq;
+}
+
+/*
+ * sets the access protocols of the service specified
+ * to the value specified in "access_proto"
+ *
+ * Note that if there are alternate mechanisms by
+ * which the service is accessed, then they should
+ * be specified as sequences
+ *
+ * Using a value of NULL for accessProtocols has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the existing sdp_access_proto_t
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
+{
+	const sdp_list_t *p;
+	sdp_data_t *protos = NULL;
+
+	for (p = ap; p; p = p->next) {
+		sdp_data_t *seq = access_proto_to_dataseq(rec, p->data);
+		protos = sdp_seq_append(protos, seq);
+	}
+
+	sdp_attr_add(rec, SDP_ATTR_PROTO_DESC_LIST, protos);
+
+	return 0;
+}
+
+int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
+{
+	const sdp_list_t *p;
+	sdp_data_t *protos = NULL;
+
+	for (p = ap; p; p = p->next) {
+		sdp_data_t *seq = access_proto_to_dataseq(rec, p->data);
+		protos = sdp_seq_append(protos, seq);
+	}
+
+	sdp_attr_add(rec, SDP_ATTR_ADD_PROTO_DESC_LIST,
+			protos ? sdp_data_alloc(SDP_SEQ8, protos) : NULL);
+
+	return 0;
+}
+
+/*
+ * set the "LanguageBase" attributes of the service record
+ * record to the value specified in "langAttrList".
+ *
+ * "langAttrList" is a linked list of "sdp_lang_attr_t"
+ * objects, one for each language in which user visible
+ * attributes are present in the service record.
+ *
+ * Using a value of NULL for langAttrList has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the exisiting sdp_lang_attr_t
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *seq)
+{
+	uint8_t uint16 = SDP_UINT16;
+	int status = 0, i = 0, seqlen = sdp_list_len(seq);
+	void **dtds, **values;
+	const sdp_list_t *p;
+
+	dtds = malloc(3 * seqlen * sizeof(void *));
+	if (!dtds)
+		return -1;
+
+	values = malloc(3 * seqlen * sizeof(void *));
+	if (!values) {
+		free(dtds);
+		return -1;
+	}
+
+	for (p = seq; p; p = p->next) {
+		sdp_lang_attr_t *lang = p->data;
+		if (!lang) {
+			status = -1;
+			break;
+		}
+		dtds[i] = &uint16;
+		values[i] = &lang->code_ISO639;
+		i++;
+		dtds[i] = &uint16;
+		values[i] = &lang->encoding;
+		i++;
+		dtds[i] = &uint16;
+		values[i] = &lang->base_offset;
+		i++;
+	}
+	if (status == 0) {
+		sdp_data_t *seq = sdp_seq_alloc(dtds, values, 3 * seqlen);
+		sdp_attr_add(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, seq);
+	}
+	free(dtds);
+	free(values);
+	return status;
+}
+
+/*
+ * set the "ServiceID" attribute of the service.
+ *
+ * This is the UUID of the service.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid)
+{
+	switch (uuid.type) {
+	case SDP_UUID16:
+		sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID16,
+							&uuid.value.uuid16);
+		break;
+	case SDP_UUID32:
+		sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID32,
+							&uuid.value.uuid32);
+		break;
+	case SDP_UUID128:
+		sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID128,
+							&uuid.value.uuid128);
+		break;
+	}
+	sdp_pattern_add_uuid(rec, &uuid);
+}
+
+/*
+ * set the GroupID attribute of the service record defining a group.
+ *
+ * This is the UUID of the group.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+void sdp_set_group_id(sdp_record_t *rec, uuid_t uuid)
+{
+	switch (uuid.type) {
+	case SDP_UUID16:
+		sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID16,
+							&uuid.value.uuid16);
+		break;
+	case SDP_UUID32:
+		sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID32,
+							&uuid.value.uuid32);
+		break;
+	case SDP_UUID128:
+		sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID128,
+							&uuid.value.uuid128);
+		break;
+	}
+	sdp_pattern_add_uuid(rec, &uuid);
+}
+
+/*
+ * set the ProfileDescriptorList attribute of the service record
+ * pointed to by record to the value specified in "profileDesc".
+ *
+ * Each element in the list is an object of type
+ * sdp_profile_desc_t which is a definition of the
+ * Bluetooth profile that this service conforms to.
+ *
+ * Using a value of NULL for profileDesc has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the exisiting ProfileDescriptorList
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *profiles)
+{
+	int status = 0;
+	uint8_t uuid16 = SDP_UUID16;
+	uint8_t uuid32 = SDP_UUID32;
+	uint8_t uuid128 = SDP_UUID128;
+	uint8_t uint16 = SDP_UINT16;
+	int i = 0, seqlen = sdp_list_len(profiles);
+	void **seqDTDs, **seqs;
+	const sdp_list_t *p;
+	sdp_data_t *pAPSeq;
+
+	seqDTDs = malloc(seqlen * sizeof(void *));
+	if (!seqDTDs)
+		return -1;
+
+	seqs = malloc(seqlen * sizeof(void *));
+	if (!seqs) {
+		free(seqDTDs);
+		return -1;
+	}
+
+	for (p = profiles; p; p = p->next) {
+		sdp_data_t *seq;
+		void *dtds[2], *values[2];
+		sdp_profile_desc_t *profile = p->data;
+		if (!profile) {
+			status = -1;
+			goto end;
+		}
+		switch (profile->uuid.type) {
+		case SDP_UUID16:
+			dtds[0] = &uuid16;
+			values[0] = &profile->uuid.value.uuid16;
+			break;
+		case SDP_UUID32:
+			dtds[0] = &uuid32;
+			values[0] = &profile->uuid.value.uuid32;
+			break;
+		case SDP_UUID128:
+			dtds[0] = &uuid128;
+			values[0] = &profile->uuid.value.uuid128;
+			break;
+		default:
+			status = -1;
+			goto end;
+		}
+		dtds[1] = &uint16;
+		values[1] = &profile->version;
+		seq = sdp_seq_alloc(dtds, values, 2);
+
+		if (seq == NULL) {
+			status = -1;
+			goto end;
+		}
+
+		seqDTDs[i] = &seq->dtd;
+		seqs[i] = seq;
+		sdp_pattern_add_uuid(rec, &profile->uuid);
+		i++;
+	}
+
+	pAPSeq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
+	sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, pAPSeq);
+end:
+	free(seqDTDs);
+	free(seqs);
+	return status;
+}
+
+/*
+ * sets various URL attributes of the service
+ * pointed to by record. The URL include
+ *
+ * client: a URL to the client's
+ *   platform specific (WinCE, PalmOS) executable
+ *   code that can be used to access this service.
+ *
+ * doc: a URL pointing to service documentation
+ *
+ * icon: a URL to an icon that can be used to represent
+ *   this service.
+ *
+ * Note that you need to pass NULL for any URLs
+ * that you don't want to set or remove
+ */
+void sdp_set_url_attr(sdp_record_t *rec, const char *client, const char *doc,
+							const char *icon)
+{
+	sdp_attr_add_new(rec, SDP_ATTR_CLNT_EXEC_URL, SDP_URL_STR8, client);
+	sdp_attr_add_new(rec, SDP_ATTR_DOC_URL, SDP_URL_STR8, doc);
+	sdp_attr_add_new(rec, SDP_ATTR_ICON_URL, SDP_URL_STR8, icon);
+}
+
+uuid_t *sdp_uuid16_create(uuid_t *u, uint16_t val)
+{
+	memset(u, 0, sizeof(uuid_t));
+	u->type = SDP_UUID16;
+	u->value.uuid16 = val;
+	return u;
+}
+
+uuid_t *sdp_uuid32_create(uuid_t *u, uint32_t val)
+{
+	memset(u, 0, sizeof(uuid_t));
+	u->type = SDP_UUID32;
+	u->value.uuid32 = val;
+	return u;
+}
+
+uuid_t *sdp_uuid128_create(uuid_t *u, const void *val)
+{
+	memset(u, 0, sizeof(uuid_t));
+	u->type = SDP_UUID128;
+	memcpy(&u->value.uuid128, val, sizeof(uint128_t));
+	return u;
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid_cmp(const void *p1, const void *p2)
+{
+	uuid_t *u1 = sdp_uuid_to_uuid128(p1);
+	uuid_t *u2 = sdp_uuid_to_uuid128(p2);
+	int ret;
+
+	ret = sdp_uuid128_cmp(u1, u2);
+
+	bt_free(u1);
+	bt_free(u2);
+
+	return ret;
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid16_cmp(const void *p1, const void *p2)
+{
+	const uuid_t *u1 = p1;
+	const uuid_t *u2 = p2;
+	return memcmp(&u1->value.uuid16, &u2->value.uuid16, sizeof(uint16_t));
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid128_cmp(const void *p1, const void *p2)
+{
+	const uuid_t *u1 = p1;
+	const uuid_t *u2 = p2;
+	return memcmp(&u1->value.uuid128, &u2->value.uuid128, sizeof(uint128_t));
+}
+
+/*
+ * 128 to 16 bit and 32 to 16 bit UUID conversion functions
+ * yet to be implemented. Note that the input is in NBO in
+ * both 32 and 128 bit UUIDs and conversion is needed
+ */
+void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16)
+{
+	/*
+	 * We have a 16 bit value, which needs to be added to
+	 * bytes 3 and 4 (at indices 2 and 3) of the Bluetooth base
+	 */
+	unsigned short data1;
+
+	/* allocate a 128bit UUID and init to the Bluetooth base UUID */
+	uuid128->value.uuid128 = bluetooth_base_uuid;
+	uuid128->type = SDP_UUID128;
+
+	/* extract bytes 2 and 3 of 128bit BT base UUID */
+	memcpy(&data1, &bluetooth_base_uuid.data[2], 2);
+
+	/* add the given UUID (16 bits) */
+	data1 += htons(uuid16->value.uuid16);
+
+	/* set bytes 2 and 3 of the 128 bit value */
+	memcpy(&uuid128->value.uuid128.data[2], &data1, 2);
+}
+
+void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32)
+{
+	/*
+	 * We have a 32 bit value, which needs to be added to
+	 * bytes 1->4 (at indices 0 thru 3) of the Bluetooth base
+	 */
+	unsigned int data0;
+
+	/* allocate a 128bit UUID and init to the Bluetooth base UUID */
+	uuid128->value.uuid128 = bluetooth_base_uuid;
+	uuid128->type = SDP_UUID128;
+
+	/* extract first 4 bytes */
+	memcpy(&data0, &bluetooth_base_uuid.data[0], 4);
+
+	/* add the given UUID (32bits) */
+	data0 += htonl(uuid32->value.uuid32);
+
+	/* set the 4 bytes of the 128 bit value */
+	memcpy(&uuid128->value.uuid128.data[0], &data0, 4);
+}
+
+uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid)
+{
+	uuid_t *uuid128 = bt_malloc(sizeof(uuid_t));
+
+	if (!uuid128)
+		return NULL;
+
+	memset(uuid128, 0, sizeof(uuid_t));
+	switch (uuid->type) {
+	case SDP_UUID128:
+		*uuid128 = *uuid;
+		break;
+	case SDP_UUID32:
+		sdp_uuid32_to_uuid128(uuid128, uuid);
+		break;
+	case SDP_UUID16:
+		sdp_uuid16_to_uuid128(uuid128, uuid);
+		break;
+	}
+	return uuid128;
+}
+
+/*
+ * converts a 128-bit uuid to a 16/32-bit one if possible
+ * returns true if uuid contains a 16/32-bit UUID at exit
+ */
+int sdp_uuid128_to_uuid(uuid_t *uuid)
+{
+	uint128_t *b = &bluetooth_base_uuid;
+	uint128_t *u = &uuid->value.uuid128;
+	uint32_t data;
+	unsigned int i;
+
+	if (uuid->type != SDP_UUID128)
+		return 1;
+
+	for (i = 4; i < sizeof(b->data); i++)
+		if (b->data[i] != u->data[i])
+			return 0;
+
+	memcpy(&data, u->data, 4);
+	data = htonl(data);
+	if (data <= 0xffff) {
+		uuid->type = SDP_UUID16;
+		uuid->value.uuid16 = (uint16_t) data;
+	} else {
+		uuid->type = SDP_UUID32;
+		uuid->value.uuid32 = data;
+	}
+	return 1;
+}
+
+/*
+ * convert a UUID to the 16-bit short-form
+ */
+int sdp_uuid_to_proto(uuid_t *uuid)
+{
+	uuid_t u = *uuid;
+	if (sdp_uuid128_to_uuid(&u)) {
+		switch (u.type) {
+		case SDP_UUID16:
+			return u.value.uuid16;
+		case SDP_UUID32:
+			return u.value.uuid32;
+		}
+	}
+	return 0;
+}
+
+/*
+ * This function appends data to the PDU buffer "dst" from source "src".
+ * The data length is also computed and set.
+ * Should the PDU length exceed 2^8, then sequence type is
+ * set accordingly and the data is memmove()'d.
+ */
+void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len)
+{
+	uint8_t *p = dst->data;
+	uint8_t dtd = *p;
+
+	SDPDBG("Append src size: %d", len);
+	SDPDBG("Append dst size: %d", dst->data_size);
+	SDPDBG("Dst buffer size: %d", dst->buf_size);
+	if (dst->data_size == 0 && dtd == 0) {
+		/* create initial sequence */
+		*p = SDP_SEQ8;
+		dst->data_size += sizeof(uint8_t);
+		/* reserve space for sequence size */
+		dst->data_size += sizeof(uint8_t);
+	}
+
+	memcpy(dst->data + dst->data_size, data, len);
+	dst->data_size += len;
+
+	dtd = *(uint8_t *) dst->data;
+	if (dst->data_size > UCHAR_MAX && dtd == SDP_SEQ8) {
+		short offset = sizeof(uint8_t) + sizeof(uint8_t);
+		memmove(dst->data + offset + 1, dst->data + offset,
+						dst->data_size - offset);
+		*p = SDP_SEQ16;
+		dst->data_size += 1;
+	}
+	dtd = *(uint8_t *) p;
+	p += sizeof(uint8_t);
+	switch (dtd) {
+	case SDP_SEQ8:
+		*(uint8_t *) p = dst->data_size - sizeof(uint8_t) - sizeof(uint8_t);
+		break;
+	case SDP_SEQ16:
+		bt_put_be16(dst->data_size - sizeof(uint8_t) - sizeof(uint16_t), p);
+		break;
+	case SDP_SEQ32:
+		bt_put_be32(dst->data_size - sizeof(uint8_t) - sizeof(uint32_t), p);
+		break;
+	}
+}
+
+void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d)
+{
+	sdp_buf_t append;
+
+	memset(&append, 0, sizeof(sdp_buf_t));
+	sdp_gen_buffer(&append, d);
+	append.data = malloc(append.buf_size);
+	if (!append.data)
+		return;
+
+	sdp_set_attrid(&append, d->attrId);
+	sdp_gen_pdu(&append, d);
+	sdp_append_to_buf(pdu, append.data, append.data_size);
+	free(append.data);
+}
+
+/*
+ * Registers an sdp record.
+ *
+ * It is incorrect to call this method on a record that
+ * has been already registered with the server.
+ *
+ * Returns zero on success, otherwise -1 (and sets errno).
+ */
+int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle)
+{
+	uint8_t *req, *rsp, *p;
+	uint32_t reqsize, rspsize;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	int status;
+
+	SDPDBG("");
+
+	if (!session->local) {
+		errno = EREMOTE;
+		return -1;
+	}
+	req = malloc(SDP_REQ_BUFFER_SIZE);
+	rsp = malloc(SDP_RSP_BUFFER_SIZE);
+	if (req == NULL || rsp == NULL) {
+		status = -1;
+		errno = ENOMEM;
+		goto end;
+	}
+
+	reqhdr = (sdp_pdu_hdr_t *)req;
+	reqhdr->pdu_id = SDP_SVC_REGISTER_REQ;
+	reqhdr->tid    = htons(sdp_gen_tid(session));
+	reqsize = sizeof(sdp_pdu_hdr_t) + 1;
+	p = req + sizeof(sdp_pdu_hdr_t);
+
+	if (bacmp(device, BDADDR_ANY)) {
+		*p++ = flags | SDP_DEVICE_RECORD;
+		bacpy((bdaddr_t *) p, device);
+		p += sizeof(bdaddr_t);
+		reqsize += sizeof(bdaddr_t);
+	} else
+		*p++ = flags;
+
+	memcpy(p, data, size);
+	reqsize += size;
+	reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+	status = sdp_send_req_w4_rsp(session, req, rsp, reqsize, &rspsize);
+	if (status < 0)
+		goto end;
+
+	if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+		SDPERR("Unexpected end of packet");
+		errno = EPROTO;
+		status = -1;
+		goto end;
+	}
+
+	rsphdr = (sdp_pdu_hdr_t *) rsp;
+	p = rsp + sizeof(sdp_pdu_hdr_t);
+
+	if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+		/* Invalid service record */
+		errno = EINVAL;
+		status = -1;
+	} else if (rsphdr->pdu_id != SDP_SVC_REGISTER_RSP) {
+		errno = EPROTO;
+		status = -1;
+	} else {
+		if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint32_t)) {
+			SDPERR("Unexpected end of packet");
+			errno = EPROTO;
+			status = -1;
+			goto end;
+		}
+		if (handle)
+			*handle  = bt_get_be32(p);
+	}
+
+end:
+	free(req);
+	free(rsp);
+
+	return status;
+}
+
+int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags)
+{
+	sdp_buf_t pdu;
+	uint32_t handle;
+	int err;
+
+	SDPDBG("");
+
+	if (rec->handle && rec->handle != 0xffffffff) {
+		uint32_t handle = rec->handle;
+		sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
+		sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+	}
+
+	if (sdp_gen_record_pdu(rec, &pdu) < 0) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	err = sdp_device_record_register_binary(session, device,
+				pdu.data, pdu.data_size, flags, &handle);
+
+	free(pdu.data);
+
+	if (err == 0) {
+		sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
+		rec->handle = handle;
+		sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+	}
+
+	return err;
+}
+
+int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags)
+{
+	return sdp_device_record_register(session, BDADDR_ANY, rec, flags);
+}
+
+/*
+ * unregister a service record
+ */
+int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle)
+{
+	uint8_t *reqbuf, *rspbuf, *p;
+	uint32_t reqsize = 0, rspsize = 0;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	int status;
+
+	SDPDBG("");
+
+	if (handle == SDP_SERVER_RECORD_HANDLE) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!session->local) {
+		errno = EREMOTE;
+		return -1;
+	}
+
+	reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+	rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+	if (!reqbuf || !rspbuf) {
+		errno = ENOMEM;
+		status = -1;
+		goto end;
+	}
+	reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+	reqhdr->pdu_id = SDP_SVC_REMOVE_REQ;
+	reqhdr->tid    = htons(sdp_gen_tid(session));
+
+	p = reqbuf + sizeof(sdp_pdu_hdr_t);
+	reqsize = sizeof(sdp_pdu_hdr_t);
+	bt_put_be32(handle, p);
+	reqsize += sizeof(uint32_t);
+
+	reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+	status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+	if (status < 0)
+		goto end;
+
+	if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
+		SDPERR("Unexpected end of packet");
+		errno = EPROTO;
+		status = -1;
+		goto end;
+	}
+
+	rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+	p = rspbuf + sizeof(sdp_pdu_hdr_t);
+
+	if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+		/* For this case the status always is invalid record handle */
+		errno = EINVAL;
+		status = -1;
+	} else if (rsphdr->pdu_id != SDP_SVC_REMOVE_RSP) {
+		errno = EPROTO;
+		status = -1;
+	} else {
+		uint16_t tmp;
+
+		memcpy(&tmp, p, sizeof(tmp));
+
+		status = tmp;
+	}
+end:
+	free(reqbuf);
+	free(rspbuf);
+
+	return status;
+}
+
+int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec)
+{
+	int err;
+
+	err = sdp_device_record_unregister_binary(session, device, rec->handle);
+	if (err == 0)
+		sdp_record_free(rec);
+
+	return err;
+}
+
+int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec)
+{
+	return sdp_device_record_unregister(session, BDADDR_ANY, rec);
+}
+
+/*
+ * modify an existing service record
+ */
+int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size)
+{
+	return -1;
+}
+
+int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec)
+{
+	uint8_t *reqbuf, *rspbuf, *p;
+	uint32_t reqsize, rspsize;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	uint32_t handle;
+	sdp_buf_t pdu;
+	int status;
+
+	SDPDBG("");
+
+	handle = rec->handle;
+
+	if (handle == SDP_SERVER_RECORD_HANDLE) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (!session->local) {
+		errno = EREMOTE;
+		return -1;
+	}
+	reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+	rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+	if (!reqbuf || !rspbuf) {
+		errno = ENOMEM;
+		status = -1;
+		goto end;
+	}
+	reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+	reqhdr->pdu_id = SDP_SVC_UPDATE_REQ;
+	reqhdr->tid    = htons(sdp_gen_tid(session));
+
+	p = reqbuf + sizeof(sdp_pdu_hdr_t);
+	reqsize = sizeof(sdp_pdu_hdr_t);
+
+	bt_put_be32(handle, p);
+	reqsize += sizeof(uint32_t);
+	p += sizeof(uint32_t);
+
+	if (sdp_gen_record_pdu(rec, &pdu) < 0) {
+		errno = ENOMEM;
+		status = -1;
+		goto end;
+	}
+	memcpy(p, pdu.data, pdu.data_size);
+	reqsize += pdu.data_size;
+	free(pdu.data);
+
+	reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+	status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+	if (status < 0)
+		goto end;
+
+	if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
+		SDPERR("Unexpected end of packet");
+		errno = EPROTO;
+		status = -1;
+		goto end;
+	}
+
+	SDPDBG("Send req status : %d", status);
+
+	rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+	p = rspbuf + sizeof(sdp_pdu_hdr_t);
+
+	if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+		/* The status can be invalid sintax or invalid record handle */
+		errno = EINVAL;
+		status = -1;
+	} else if (rsphdr->pdu_id != SDP_SVC_UPDATE_RSP) {
+		errno = EPROTO;
+		status = -1;
+	} else {
+		uint16_t tmp;
+
+		memcpy(&tmp, p, sizeof(tmp));
+
+		status = tmp;
+	}
+end:
+	free(reqbuf);
+	free(rspbuf);
+	return status;
+}
+
+int sdp_record_update(sdp_session_t *session, const sdp_record_t *rec)
+{
+	return sdp_device_record_update(session, BDADDR_ANY, rec);
+}
+
+sdp_record_t *sdp_record_alloc(void)
+{
+	sdp_record_t *rec = malloc(sizeof(sdp_record_t));
+
+	if (!rec)
+		return NULL;
+
+	memset(rec, 0, sizeof(sdp_record_t));
+	rec->handle = 0xffffffff;
+	return rec;
+}
+
+/*
+ * Free the contents of a service record
+ */
+void sdp_record_free(sdp_record_t *rec)
+{
+	sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
+	sdp_list_free(rec->pattern, free);
+	free(rec);
+}
+
+void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid)
+{
+	uuid_t *uuid128 = sdp_uuid_to_uuid128(uuid);
+
+	SDPDBG("Elements in target pattern : %d", sdp_list_len(rec->pattern));
+	SDPDBG("Trying to add : 0x%lx", (unsigned long) uuid128);
+
+	if (sdp_list_find(rec->pattern, uuid128, sdp_uuid128_cmp) == NULL)
+		rec->pattern = sdp_list_insert_sorted(rec->pattern, uuid128, sdp_uuid128_cmp);
+	else
+		bt_free(uuid128);
+
+	SDPDBG("Elements in target pattern : %d", sdp_list_len(rec->pattern));
+}
+
+void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq)
+{
+	for (; seq; seq = seq->next) {
+		uuid_t *uuid = (uuid_t *)seq->data;
+		sdp_pattern_add_uuid(rec, uuid);
+	}
+}
+
+/*
+ * Extract a sequence of service record handles from a PDU buffer
+ * and add the entries to a sdp_list_t. Note that the service record
+ * handles are not in "data element sequence" form, but just like
+ * an array of service handles
+ */
+static void extract_record_handle_seq(uint8_t *pdu, int bufsize, sdp_list_t **seq, int count, unsigned int *scanned)
+{
+	sdp_list_t *pSeq = *seq;
+	uint8_t *pdata = pdu;
+	int n;
+
+	for (n = 0; n < count; n++) {
+		uint32_t *pSvcRec;
+		if (bufsize < (int) sizeof(uint32_t)) {
+			SDPERR("Unexpected end of packet");
+			break;
+		}
+		pSvcRec = malloc(sizeof(uint32_t));
+		if (!pSvcRec)
+			break;
+		*pSvcRec = bt_get_be32(pdata);
+		pSeq = sdp_list_append(pSeq, pSvcRec);
+		pdata += sizeof(uint32_t);
+		*scanned += sizeof(uint32_t);
+		bufsize -= sizeof(uint32_t);
+	}
+	*seq = pSeq;
+}
+/*
+ * Generate the attribute sequence pdu form
+ * from sdp_list_t elements. Return length of attr seq
+ */
+static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd)
+{
+	sdp_data_t *dataseq;
+	void **types, **values;
+	sdp_buf_t buf;
+	int i, seqlen = sdp_list_len(seq);
+
+	/* Fill up the value and the dtd arrays */
+	SDPDBG("");
+
+	SDPDBG("Seq length : %d", seqlen);
+
+	types = malloc(seqlen * sizeof(void *));
+	if (!types)
+		return -ENOMEM;
+
+	values = malloc(seqlen * sizeof(void *));
+	if (!values) {
+		free(types);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < seqlen; i++) {
+		void *data = seq->data;
+		types[i] = &dtd;
+		if (SDP_IS_UUID(dtd))
+			data = &((uuid_t *)data)->value;
+		values[i] = data;
+		seq = seq->next;
+	}
+
+	dataseq = sdp_seq_alloc(types, values, seqlen);
+	if (!dataseq) {
+		free(types);
+		free(values);
+		return -ENOMEM;
+	}
+
+	memset(&buf, 0, sizeof(sdp_buf_t));
+	sdp_gen_buffer(&buf, dataseq);
+	buf.data = malloc(buf.buf_size);
+
+	if (!buf.data) {
+		sdp_data_free(dataseq);
+		free(types);
+		free(values);
+		return -ENOMEM;
+	}
+
+	SDPDBG("Data Seq : 0x%p", seq);
+	seqlen = sdp_gen_pdu(&buf, dataseq);
+	SDPDBG("Copying : %d", buf.data_size);
+	memcpy(dst, buf.data, buf.data_size);
+
+	sdp_data_free(dataseq);
+
+	free(types);
+	free(values);
+	free(buf.data);
+	return seqlen;
+}
+
+static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq)
+{
+	uuid_t *uuid = seq->data;
+	return gen_dataseq_pdu(dst, seq, uuid->type);
+}
+
+static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType)
+{
+	return gen_dataseq_pdu(dst, seq, dataType);
+}
+
+typedef struct {
+	uint8_t length;
+	unsigned char data[16];
+} __attribute__ ((packed)) sdp_cstate_t;
+
+static int copy_cstate(uint8_t *pdata, int pdata_len, const sdp_cstate_t *cstate)
+{
+	if (cstate) {
+		uint8_t len = cstate->length;
+		if (len >= pdata_len) {
+			SDPERR("Continuation state size exceeds internal buffer");
+			len = pdata_len - 1;
+		}
+		*pdata++ = len;
+		memcpy(pdata, cstate->data, len);
+		return len + 1;
+	}
+	*pdata = 0;
+	return 1;
+}
+
+/*
+ * This is a service search request.
+ *
+ * INPUT :
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   uint16_t max_rec_num
+ *      A 16 bit integer which tells the service, the maximum
+ *      entries that the client can handle in the response. The
+ *      server is obliged not to return > max_rec_num entries
+ *
+ * OUTPUT :
+ *
+ *   int return value
+ *     0:
+ *       The request completed successfully. This does not
+ *       mean the requested services were found
+ *     -1:
+ *       On any failure and sets errno
+ *
+ *   sdp_list_t **rsp_list
+ *     This variable is set on a successful return if there are
+ *     non-zero service handles. It is a singly linked list of
+ *     service record handles (uint16_t)
+ */
+int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search,
+			uint16_t max_rec_num, sdp_list_t **rsp)
+{
+	int status = 0;
+	uint32_t reqsize = 0, _reqsize;
+	uint32_t rspsize = 0, rsplen;
+	int seqlen = 0;
+	int rec_count;
+	unsigned scanned, pdata_len;
+	uint8_t *pdata, *_pdata;
+	uint8_t *reqbuf, *rspbuf;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	sdp_cstate_t *cstate = NULL;
+
+	reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+	rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+	if (!reqbuf || !rspbuf) {
+		errno = ENOMEM;
+		status = -1;
+		goto end;
+	}
+	reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+	reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
+	pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+	reqsize = sizeof(sdp_pdu_hdr_t);
+
+	/* add service class IDs for search */
+	seqlen = gen_searchseq_pdu(pdata, search);
+
+	SDPDBG("Data seq added : %d", seqlen);
+
+	/* set the length and increment the pointer */
+	reqsize += seqlen;
+	pdata += seqlen;
+
+	/* specify the maximum svc rec count that client expects */
+	bt_put_be16(max_rec_num, pdata);
+	reqsize += sizeof(uint16_t);
+	pdata += sizeof(uint16_t);
+
+	_reqsize = reqsize;
+	_pdata   = pdata;
+	*rsp = NULL;
+
+	do {
+		/* Add continuation state or NULL (first time) */
+		reqsize = _reqsize + copy_cstate(_pdata,
+					SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+		/* Set the request header's param length */
+		reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+		reqhdr->tid  = htons(sdp_gen_tid(session));
+		/*
+		 * Send the request, wait for response and if
+		 * no error, set the appropriate values and return
+		 */
+		status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+		if (status < 0)
+			goto end;
+
+		if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+			SDPERR("Unexpected end of packet");
+			status = -1;
+			goto end;
+		}
+
+		rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+		rsplen = ntohs(rsphdr->plen);
+
+		if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+			SDPDBG("Status : 0x%x", rsphdr->pdu_id);
+			status = -1;
+			goto end;
+		}
+		scanned = 0;
+		pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+		pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+		if (pdata_len < sizeof(uint16_t) + sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			status = -1;
+			goto end;
+		}
+
+		/* net service record match count */
+		pdata += sizeof(uint16_t);
+		scanned += sizeof(uint16_t);
+		pdata_len -= sizeof(uint16_t);
+		rec_count = bt_get_be16(pdata);
+		pdata += sizeof(uint16_t);
+		scanned += sizeof(uint16_t);
+		pdata_len -= sizeof(uint16_t);
+
+		SDPDBG("Current svc count: %d", rec_count);
+		SDPDBG("ResponseLength: %d", rsplen);
+
+		if (!rec_count) {
+			status = -1;
+			goto end;
+		}
+		extract_record_handle_seq(pdata, pdata_len, rsp, rec_count, &scanned);
+		SDPDBG("BytesScanned : %d", scanned);
+
+		if (rsplen > scanned) {
+			uint8_t cstate_len;
+
+			if (rspsize < sizeof(sdp_pdu_hdr_t) + scanned + sizeof(uint8_t)) {
+				SDPERR("Unexpected end of packet: continuation state data missing");
+				status = -1;
+				goto end;
+			}
+
+			pdata = rspbuf + sizeof(sdp_pdu_hdr_t) + scanned;
+			cstate_len = *(uint8_t *) pdata;
+			if (cstate_len > 0) {
+				cstate = (sdp_cstate_t *)pdata;
+				SDPDBG("Cont state length: %d", cstate_len);
+			} else
+				cstate = NULL;
+		}
+	} while (cstate);
+
+end:
+	free(reqbuf);
+	free(rspbuf);
+
+	return status;
+}
+
+/*
+ * This is a service attribute request.
+ *
+ * INPUT :
+ *
+ *   uint32_t handle
+ *     The handle of the service for which the attribute(s) are
+ *     requested
+ *
+ *   sdp_attrreq_type_t reqtype
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrid
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ *   return sdp_record_t *
+ *     0:
+ *       On any error and sets errno
+ *     !0:
+ *	 The service record
+ */
+sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle,
+			sdp_attrreq_type_t reqtype, const sdp_list_t *attrids)
+{
+	uint32_t reqsize = 0, _reqsize;
+	uint32_t rspsize = 0, rsp_count;
+	int attr_list_len = 0;
+	int seqlen = 0;
+	unsigned int pdata_len;
+	uint8_t *pdata, *_pdata;
+	uint8_t *reqbuf, *rspbuf;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	sdp_cstate_t *cstate = NULL;
+	uint8_t cstate_len = 0;
+	sdp_buf_t rsp_concat_buf;
+	sdp_record_t *rec = 0;
+
+	if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+	reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+	rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+	if (!reqbuf || !rspbuf) {
+		errno = ENOMEM;
+		goto end;
+	}
+	reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+	reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
+
+	pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+	reqsize = sizeof(sdp_pdu_hdr_t);
+
+	/* add the service record handle */
+	bt_put_be32(handle, pdata);
+	reqsize += sizeof(uint32_t);
+	pdata += sizeof(uint32_t);
+
+	/* specify the response limit */
+	bt_put_be16(65535, pdata);
+	reqsize += sizeof(uint16_t);
+	pdata += sizeof(uint16_t);
+
+	/* get attr seq PDU form */
+	seqlen = gen_attridseq_pdu(pdata, attrids,
+		reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
+	if (seqlen == -1) {
+		errno = EINVAL;
+		goto end;
+	}
+	pdata += seqlen;
+	reqsize += seqlen;
+	SDPDBG("Attr list length : %d", seqlen);
+
+	/* save before Continuation State */
+	_pdata = pdata;
+	_reqsize = reqsize;
+
+	do {
+		int status;
+
+		/* add NULL continuation state */
+		reqsize = _reqsize + copy_cstate(_pdata,
+					SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+		/* set the request header's param length */
+		reqhdr->tid  = htons(sdp_gen_tid(session));
+		reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+		status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+		if (status < 0)
+			goto end;
+
+		if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+			SDPERR("Unexpected end of packet");
+			goto end;
+		}
+
+		rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+		if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+			SDPDBG("PDU ID : 0x%x", rsphdr->pdu_id);
+			goto end;
+		}
+		pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+		pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+		if (pdata_len < sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			goto end;
+		}
+
+		rsp_count = bt_get_be16(pdata);
+		attr_list_len += rsp_count;
+		pdata += sizeof(uint16_t);
+		pdata_len -= sizeof(uint16_t);
+
+		/*
+		 * if continuation state set need to re-issue request before
+		 * parsing
+		 */
+		if (pdata_len < rsp_count + sizeof(uint8_t)) {
+			SDPERR("Unexpected end of packet: continuation state data missing");
+			goto end;
+		}
+		cstate_len = *(uint8_t *) (pdata + rsp_count);
+
+		SDPDBG("Response id : %d", rsphdr->pdu_id);
+		SDPDBG("Attrlist byte count : %d", rsp_count);
+		SDPDBG("sdp_cstate_t length : %d", cstate_len);
+
+		/*
+		 * a split response: concatenate intermediate responses
+		 * and the last one (which has cstate_len == 0)
+		 */
+		if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
+			uint8_t *targetPtr = NULL;
+
+			cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
+
+			/* build concatenated response buffer */
+			rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
+			rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
+			targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
+			memcpy(targetPtr, pdata, rsp_count);
+			rsp_concat_buf.data_size += rsp_count;
+		}
+	} while (cstate);
+
+	if (attr_list_len > 0) {
+		int scanned = 0;
+		if (rsp_concat_buf.data_size != 0) {
+			pdata = rsp_concat_buf.data;
+			pdata_len = rsp_concat_buf.data_size;
+		}
+		rec = sdp_extract_pdu(pdata, pdata_len, &scanned);
+	}
+
+end:
+	free(reqbuf);
+	free(rsp_concat_buf.data);
+	free(rspbuf);
+	return rec;
+}
+
+/*
+ * SDP transaction structure for asynchronous search
+ */
+struct sdp_transaction {
+	sdp_callback_t *cb;	/* called when the transaction finishes */
+	void *udata;		/* client user data */
+	uint8_t *reqbuf;	/* pointer to request PDU */
+	sdp_buf_t rsp_concat_buf;
+	uint32_t reqsize;	/* without cstate */
+	int err;		/* ZERO if success or the errno if failed */
+};
+
+/*
+ * Creates a new sdp session for asynchronous search
+ * INPUT:
+ *  int sk
+ *     non-blocking L2CAP socket
+ *
+ * RETURN:
+ *  sdp_session_t *
+ *  NULL - On memory allocation failure
+ */
+sdp_session_t *sdp_create(int sk, uint32_t flags)
+{
+	sdp_session_t *session;
+	struct sdp_transaction *t;
+
+	session = malloc(sizeof(sdp_session_t));
+	if (!session) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	memset(session, 0, sizeof(*session));
+
+	session->flags = flags;
+	session->sock = sk;
+
+	t = malloc(sizeof(struct sdp_transaction));
+	if (!t) {
+		errno = ENOMEM;
+		free(session);
+		return NULL;
+	}
+	memset(t, 0, sizeof(*t));
+
+	session->priv = t;
+
+	return session;
+}
+
+/*
+ * Sets the callback function/user data used to notify the application
+ * that the asynchronous transaction finished. This function must be
+ * called before request an asynchronous search.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *	Current sdp session to be handled
+ *  sdp_callback_t *cb
+ *      callback to be called when the transaction finishes
+ *  void *udata
+ *      user data passed to callback
+ * RETURN:
+ * 	 0 - Success
+ * 	-1 - Failure
+ */
+int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata)
+{
+	struct sdp_transaction *t;
+
+	if (!session || !session->priv)
+		return -1;
+
+	t = session->priv;
+	t->cb = func;
+	t->udata = udata;
+
+	return 0;
+}
+
+/*
+ * This function starts an asynchronous service search request.
+ * The incoming and outgoing data are stored in the transaction structure
+ * buffers. When there is incoming data the sdp_process function must be
+ * called to get the data and handle the continuation state.
+ *
+ * INPUT :
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   uint16_t max_rec_num
+ *      A 16 bit integer which tells the service, the maximum
+ *      entries that the client can handle in the response. The
+ *      server is obliged not to return > max_rec_num entries
+ *
+ * OUTPUT :
+ *
+ *   int return value
+ * 	0  - if the request has been sent properly
+ * 	-1 - On any failure and sets errno
+ */
+
+int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num)
+{
+	struct sdp_transaction *t;
+	sdp_pdu_hdr_t *reqhdr;
+	uint8_t *pdata;
+	int cstate_len, seqlen = 0;
+
+	if (!session || !session->priv)
+		return -1;
+
+	t = session->priv;
+
+	/* clean possible allocated buffer */
+	free(t->rsp_concat_buf.data);
+	memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+	if (!t->reqbuf) {
+		t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+		if (!t->reqbuf) {
+			t->err = ENOMEM;
+			goto end;
+		}
+	}
+	memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+	reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+	reqhdr->tid = htons(sdp_gen_tid(session));
+	reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
+
+	/* generate PDU */
+	pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+	t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+	/* add service class IDs for search */
+	seqlen = gen_searchseq_pdu(pdata, search);
+
+	SDPDBG("Data seq added : %d", seqlen);
+
+	/* now set the length and increment the pointer */
+	t->reqsize += seqlen;
+	pdata += seqlen;
+
+	bt_put_be16(max_rec_num, pdata);
+	t->reqsize += sizeof(uint16_t);
+	pdata += sizeof(uint16_t);
+
+	/* set the request header's param length */
+	cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+	reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+	if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+		SDPERR("Error sending data:%m");
+		t->err = errno;
+		goto end;
+	}
+
+	return 0;
+end:
+
+	free(t->reqbuf);
+	t->reqbuf = NULL;
+
+	return -1;
+}
+
+/*
+ * This function starts an asynchronous service attribute request.
+ * The incoming and outgoing data are stored in the transaction structure
+ * buffers. When there is incoming data the sdp_process function must be
+ * called to get the data and handle the continuation state.
+ *
+ * INPUT :
+ *  sdp_session_t *session
+ *	Current sdp session to be handled
+ *
+ *   uint32_t handle
+ *     The handle of the service for which the attribute(s) are
+ *     requested
+ *
+ *   sdp_attrreq_type_t reqtype
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrid_list
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ *   int return value
+ * 	 0 - if the request has been sent properly
+ * 	-1 - On any failure and sets errno
+ */
+
+int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
+{
+	struct sdp_transaction *t;
+	sdp_pdu_hdr_t *reqhdr;
+	uint8_t *pdata;
+	int cstate_len, seqlen = 0;
+
+	if (!session || !session->priv)
+		return -1;
+
+	t = session->priv;
+
+	/* clean possible allocated buffer */
+	free(t->rsp_concat_buf.data);
+	memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+	if (!t->reqbuf) {
+		t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+		if (!t->reqbuf) {
+			t->err = ENOMEM;
+			goto end;
+		}
+	}
+	memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+	reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+	reqhdr->tid = htons(sdp_gen_tid(session));
+	reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
+
+	/* generate PDU */
+	pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+	t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+	/* add the service record handle */
+	bt_put_be32(handle, pdata);
+	t->reqsize += sizeof(uint32_t);
+	pdata += sizeof(uint32_t);
+
+	/* specify the response limit */
+	bt_put_be16(65535, pdata);
+	t->reqsize += sizeof(uint16_t);
+	pdata += sizeof(uint16_t);
+
+	/* get attr seq PDU form */
+	seqlen = gen_attridseq_pdu(pdata, attrid_list,
+			reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
+	if (seqlen == -1) {
+		t->err = EINVAL;
+		goto end;
+	}
+
+	/* now set the length and increment the pointer */
+	t->reqsize += seqlen;
+	pdata += seqlen;
+	SDPDBG("Attr list length : %d", seqlen);
+
+	/* set the request header's param length */
+	cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+	reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+	if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+		SDPERR("Error sending data:%m");
+		t->err = errno;
+		goto end;
+	}
+
+	return 0;
+end:
+
+	free(t->reqbuf);
+	t->reqbuf = NULL;
+
+	return -1;
+}
+
+/*
+ * This function starts an asynchronous service search attributes.
+ * It is a service search request combined with attribute request. The incoming
+ * and outgoing data are stored in the transaction structure buffers. When there
+ * is incoming data the sdp_process function must be called to get the data
+ * and handle the continuation state.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *	Current sdp session to be handled
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   AttributeSpecification attrSpec
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrid_list
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+
+ * RETURN:
+ * 	 0 - if the request has been sent properly
+ * 	-1 - On any failure
+ */
+int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
+{
+	struct sdp_transaction *t;
+	sdp_pdu_hdr_t *reqhdr;
+	uint8_t *pdata;
+	int cstate_len, seqlen = 0;
+
+	if (!session || !session->priv)
+		return -1;
+
+	t = session->priv;
+
+	/* clean possible allocated buffer */
+	free(t->rsp_concat_buf.data);
+	memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+	if (!t->reqbuf) {
+		t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+		if (!t->reqbuf) {
+			t->err = ENOMEM;
+			goto end;
+		}
+	}
+	memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+	reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+	reqhdr->tid = htons(sdp_gen_tid(session));
+	reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
+
+	/* generate PDU */
+	pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+	t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+	/* add service class IDs for search */
+	seqlen = gen_searchseq_pdu(pdata, search);
+
+	SDPDBG("Data seq added : %d", seqlen);
+
+	/* now set the length and increment the pointer */
+	t->reqsize += seqlen;
+	pdata += seqlen;
+
+	bt_put_be16(SDP_MAX_ATTR_LEN, pdata);
+	t->reqsize += sizeof(uint16_t);
+	pdata += sizeof(uint16_t);
+
+	SDPDBG("Max attr byte count : %d", SDP_MAX_ATTR_LEN);
+
+	/* get attr seq PDU form */
+	seqlen = gen_attridseq_pdu(pdata, attrid_list,
+			reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
+	if (seqlen == -1) {
+		t->err = EINVAL;
+		goto end;
+	}
+
+	pdata += seqlen;
+	SDPDBG("Attr list length : %d", seqlen);
+	t->reqsize += seqlen;
+
+	/* set the request header's param length */
+	cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+	reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+	if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+		SDPERR("Error sending data:%m");
+		t->err = errno;
+		goto end;
+	}
+
+	return 0;
+end:
+
+	free(t->reqbuf);
+	t->reqbuf = NULL;
+
+	return -1;
+}
+
+/*
+ * Function used to get the error reason after sdp_callback_t function has been called
+ * and the status is 0xffff or if sdp_service_{search, attr, search_attr}_async returns -1.
+ * It indicates that an error NOT related to SDP_ErrorResponse happened. Get errno directly
+ * is not safe because multiple transactions can be triggered.
+ * This function must be used with asynchronous sdp functions only.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *	Current sdp session to be handled
+ * RETURN:
+ * 	 0 = No error in the current transaction
+ * 	-1 - if the session is invalid
+ * 	positive value - the errno value
+ *
+ */
+int sdp_get_error(sdp_session_t *session)
+{
+	struct sdp_transaction *t;
+
+	if (!session || !session->priv) {
+		SDPERR("Invalid session");
+		return -1;
+	}
+
+	t = session->priv;
+
+	return t->err;
+}
+
+/*
+ * Receive the incoming SDP PDU. This function must be called when there is data
+ * available to be read. On continuation state, the original request (with a new
+ * transaction ID) and the continuation state data will be appended in the initial PDU.
+ * If an error happens or the transaction finishes the callback function will be called.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *	Current sdp session to be handled
+ * RETURN:
+ * 	0  - if the transaction is on continuation state
+ * 	-1 - On any failure or the transaction finished
+ */
+int sdp_process(sdp_session_t *session)
+{
+	struct sdp_transaction *t;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	sdp_cstate_t *pcstate;
+	uint8_t *pdata, *rspbuf, *targetPtr;
+	int rsp_count, err = -1;
+	size_t size = 0;
+	int n, plen;
+	uint16_t status = 0xffff;
+	uint8_t pdu_id = 0x00;
+
+	if (!session || !session->priv) {
+		SDPERR("Invalid session");
+		return -1;
+	}
+
+	rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+	if (!rspbuf) {
+		SDPERR("Response buffer alloc failure:%m (%d)", errno);
+		return -1;
+	}
+
+	memset(rspbuf, 0, SDP_RSP_BUFFER_SIZE);
+
+	t = session->priv;
+	reqhdr = (sdp_pdu_hdr_t *)t->reqbuf;
+	rsphdr = (sdp_pdu_hdr_t *)rspbuf;
+
+	pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+
+	n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
+	if (n < 0) {
+		SDPERR("Read response:%m (%d)", errno);
+		t->err = errno;
+		goto end;
+	}
+
+	if (reqhdr->tid != rsphdr->tid) {
+		t->err = EPROTO;
+		SDPERR("Protocol error: transaction id does not match");
+		goto end;
+	}
+
+	if (n != (int) (ntohs(rsphdr->plen) + sizeof(sdp_pdu_hdr_t))) {
+		t->err = EPROTO;
+		SDPERR("Protocol error: invalid length");
+		goto end;
+	}
+
+	pdu_id = rsphdr->pdu_id;
+	switch (rsphdr->pdu_id) {
+	uint8_t *ssr_pdata;
+	uint16_t tsrc, csrc;
+	case SDP_SVC_SEARCH_RSP:
+		/*
+		 * TSRC: Total Service Record Count (2 bytes)
+		 * CSRC: Current Service Record Count (2 bytes)
+		 */
+		ssr_pdata = pdata;
+		tsrc = bt_get_be16(ssr_pdata);
+		ssr_pdata += sizeof(uint16_t);
+		csrc = bt_get_be16(ssr_pdata);
+
+		/* csrc should never be larger than tsrc */
+		if (csrc > tsrc) {
+			t->err = EPROTO;
+			SDPERR("Protocol error: wrong current service record count value.");
+			goto end;
+		}
+
+		SDPDBG("Total svc count: %d", tsrc);
+		SDPDBG("Current svc count: %d", csrc);
+
+		/* parameter length without continuation state */
+		plen = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
+
+		if (t->rsp_concat_buf.data_size == 0) {
+			/* first fragment */
+			rsp_count = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
+		} else if (t->rsp_concat_buf.data_size >= sizeof(uint16_t) * 2) {
+			/* point to the first csrc */
+			uint8_t *pcsrc = t->rsp_concat_buf.data + 2;
+			uint16_t tcsrc, tcsrc2;
+
+			/* FIXME: update the interface later. csrc doesn't need be passed to clients */
+
+			pdata += sizeof(uint16_t); /* point to csrc */
+
+			/* the first csrc contains the sum of partial csrc responses */
+			memcpy(&tcsrc, pcsrc, sizeof(tcsrc));
+			memcpy(&tcsrc2, pdata, sizeof(tcsrc2));
+			tcsrc += tcsrc2;
+			memcpy(pcsrc, &tcsrc, sizeof(tcsrc));
+
+			pdata += sizeof(uint16_t); /* point to the first handle */
+			rsp_count = csrc * 4;
+		} else {
+			t->err = EPROTO;
+			SDPERR("Protocol error: invalid PDU size");
+			status = SDP_INVALID_PDU_SIZE;
+			goto end;
+		}
+		status = 0x0000;
+		break;
+	case SDP_SVC_ATTR_RSP:
+	case SDP_SVC_SEARCH_ATTR_RSP:
+		rsp_count = bt_get_be16(pdata);
+		SDPDBG("Attrlist byte count : %d", rsp_count);
+
+		/* Valid range for rsp_count is 0x0002-0xFFFF */
+		if (t->rsp_concat_buf.data_size == 0 && rsp_count < 0x0002) {
+			t->err = EPROTO;
+			SDPERR("Protocol error: invalid AttrList size");
+			status = SDP_INVALID_PDU_SIZE;
+			goto end;
+		}
+
+		/*
+		 * Number of bytes in the AttributeLists parameter(without
+		 * continuation state) + AttributeListsByteCount field size.
+		 */
+		plen = sizeof(uint16_t) + rsp_count;
+
+		pdata += sizeof(uint16_t); /* points to attribute list */
+		status = 0x0000;
+		break;
+	case SDP_ERROR_RSP:
+		status = bt_get_be16(pdata);
+		size = ntohs(rsphdr->plen);
+
+		goto end;
+	default:
+		t->err = EPROTO;
+		SDPERR("Illegal PDU ID: 0x%x", rsphdr->pdu_id);
+		goto end;
+	}
+
+	/* Out of bound check before using rsp_count as offset for
+	 * continuation state, which has at least a one byte size
+	 * field.
+	 */
+	if ((n - (int) sizeof(sdp_pdu_hdr_t)) < plen + 1) {
+		t->err = EPROTO;
+		SDPERR("Protocol error: invalid PDU size");
+		status = SDP_INVALID_PDU_SIZE;
+		goto end;
+	}
+
+	pcstate = (sdp_cstate_t *) (pdata + rsp_count);
+
+	SDPDBG("Cstate length : %d", pcstate->length);
+
+	/*
+	 * Check out of bound. Continuation state must have at least
+	 * 1 byte: ZERO to indicate that it is not a partial response.
+	 */
+	if ((n - (int) sizeof(sdp_pdu_hdr_t))  != (plen + pcstate->length + 1)) {
+		t->err = EPROTO;
+		SDPERR("Protocol error: wrong PDU size.");
+		status = 0xffff;
+		goto end;
+	}
+
+	/*
+	 * This is a split response, need to concatenate intermediate
+	 * responses and the last one which will have cstate length == 0
+	 */
+	t->rsp_concat_buf.data = realloc(t->rsp_concat_buf.data, t->rsp_concat_buf.data_size + rsp_count);
+	targetPtr = t->rsp_concat_buf.data + t->rsp_concat_buf.data_size;
+	t->rsp_concat_buf.buf_size = t->rsp_concat_buf.data_size + rsp_count;
+	memcpy(targetPtr, pdata, rsp_count);
+	t->rsp_concat_buf.data_size += rsp_count;
+
+	if (pcstate->length > 0) {
+		int reqsize, cstate_len;
+
+		reqhdr->tid = htons(sdp_gen_tid(session));
+
+		/* add continuation state */
+		cstate_len = copy_cstate(t->reqbuf + t->reqsize,
+				SDP_REQ_BUFFER_SIZE - t->reqsize, pcstate);
+
+		reqsize = t->reqsize + cstate_len;
+
+		/* set the request header's param length */
+		reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+		if (sdp_send_req(session, t->reqbuf, reqsize) < 0) {
+			SDPERR("Error sending data:%m(%d)", errno);
+			status = 0xffff;
+			t->err = errno;
+			goto end;
+		}
+		err = 0;
+	}
+
+end:
+	if (err) {
+		if (t->rsp_concat_buf.data_size != 0) {
+			pdata = t->rsp_concat_buf.data;
+			size = t->rsp_concat_buf.data_size;
+		}
+		if (t->cb)
+			t->cb(pdu_id, status, pdata, size, t->udata);
+	}
+
+	free(rspbuf);
+
+	return err;
+}
+
+/*
+ * This is a service search request combined with the service
+ * attribute request. First a service class match is done and
+ * for matching service, requested attributes are extracted
+ *
+ * INPUT :
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   AttributeSpecification attrSpec
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrids
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ *   int return value
+ *     0:
+ *       The request completed successfully. This does not
+ *       mean the requested services were found
+ *     -1:
+ *       On any error and sets errno
+ *
+ *   sdp_list_t **rsp
+ *     This variable is set on a successful return to point to
+ *     service(s) found. Each element of this list is of type
+ *     sdp_record_t* (of the services which matched the search list)
+ */
+int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrids, sdp_list_t **rsp)
+{
+	int status = 0;
+	uint32_t reqsize = 0, _reqsize;
+	uint32_t rspsize = 0;
+	int seqlen = 0, attr_list_len = 0;
+	int rsp_count = 0, cstate_len = 0;
+	unsigned int pdata_len;
+	uint8_t *pdata, *_pdata;
+	uint8_t *reqbuf, *rspbuf;
+	sdp_pdu_hdr_t *reqhdr, *rsphdr;
+	uint8_t dataType;
+	sdp_list_t *rec_list = NULL;
+	sdp_buf_t rsp_concat_buf;
+	sdp_cstate_t *cstate = NULL;
+
+	if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+	reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+	rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+	if (!reqbuf || !rspbuf) {
+		errno = ENOMEM;
+		status = -1;
+		goto end;
+	}
+
+	reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+	reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
+
+	/* generate PDU */
+	pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+	reqsize = sizeof(sdp_pdu_hdr_t);
+
+	/* add service class IDs for search */
+	seqlen = gen_searchseq_pdu(pdata, search);
+	if (seqlen < 0) {
+		errno = EINVAL;
+		status = -1;
+		goto end;
+	}
+
+	SDPDBG("Data seq added : %d", seqlen);
+
+	/* now set the length and increment the pointer */
+	reqsize += seqlen;
+	pdata += seqlen;
+
+	bt_put_be16(SDP_MAX_ATTR_LEN, pdata);
+	reqsize += sizeof(uint16_t);
+	pdata += sizeof(uint16_t);
+
+	SDPDBG("Max attr byte count : %d", SDP_MAX_ATTR_LEN);
+
+	/* get attr seq PDU form */
+	seqlen = gen_attridseq_pdu(pdata, attrids,
+		reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
+	if (seqlen == -1) {
+		errno = EINVAL;
+		status = -1;
+		goto end;
+	}
+	pdata += seqlen;
+	SDPDBG("Attr list length : %d", seqlen);
+	reqsize += seqlen;
+	*rsp = 0;
+
+	/* save before Continuation State */
+	_pdata = pdata;
+	_reqsize = reqsize;
+
+	do {
+		reqhdr->tid = htons(sdp_gen_tid(session));
+
+		/* add continuation state (can be null) */
+		reqsize = _reqsize + copy_cstate(_pdata,
+					SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+		/* set the request header's param length */
+		reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+		rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+		status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+		if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+			SDPERR("Unexpected end of packet");
+			status = -1;
+			goto end;
+		}
+
+		if (status < 0) {
+			SDPDBG("Status : 0x%x", rsphdr->pdu_id);
+			goto end;
+		}
+
+		if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+			status = -1;
+			goto end;
+		}
+
+		pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+		pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+		if (pdata_len < sizeof(uint16_t)) {
+			SDPERR("Unexpected end of packet");
+			status = -1;
+			goto end;
+		}
+
+		rsp_count = bt_get_be16(pdata);
+		attr_list_len += rsp_count;
+		pdata += sizeof(uint16_t); /* pdata points to attribute list */
+		pdata_len -= sizeof(uint16_t);
+
+		if (pdata_len < rsp_count + sizeof(uint8_t)) {
+			SDPERR("Unexpected end of packet: continuation state data missing");
+			status = -1;
+			goto end;
+		}
+
+		cstate_len = *(uint8_t *) (pdata + rsp_count);
+
+		SDPDBG("Attrlist byte count : %d", attr_list_len);
+		SDPDBG("Response byte count : %d", rsp_count);
+		SDPDBG("Cstate length : %d", cstate_len);
+		/*
+		 * This is a split response, need to concatenate intermediate
+		 * responses and the last one which will have cstate_len == 0
+		 */
+		if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
+			uint8_t *targetPtr = NULL;
+
+			cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
+
+			/* build concatenated response buffer */
+			rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
+			targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
+			rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
+			memcpy(targetPtr, pdata, rsp_count);
+			rsp_concat_buf.data_size += rsp_count;
+		}
+	} while (cstate);
+
+	if (attr_list_len > 0) {
+		int scanned = 0;
+
+		if (rsp_concat_buf.data_size != 0) {
+			pdata = rsp_concat_buf.data;
+			pdata_len = rsp_concat_buf.data_size;
+		}
+
+		/*
+		 * Response is a sequence of sequence(s) for one or
+		 * more data element sequence(s) representing services
+		 * for which attributes are returned
+		 */
+		scanned = sdp_extract_seqtype(pdata, pdata_len, &dataType, &seqlen);
+
+		SDPDBG("Bytes scanned : %d", scanned);
+		SDPDBG("Seq length : %d", seqlen);
+
+		if (scanned && seqlen) {
+			pdata += scanned;
+			pdata_len -= scanned;
+			do {
+				int recsize = 0;
+				sdp_record_t *rec = sdp_extract_pdu(pdata, pdata_len, &recsize);
+				if (rec == NULL) {
+					SDPERR("SVC REC is null");
+					status = -1;
+					goto end;
+				}
+				if (!recsize) {
+					sdp_record_free(rec);
+					break;
+				}
+				scanned += recsize;
+				pdata += recsize;
+				pdata_len -= recsize;
+
+				SDPDBG("Loc seq length : %d", recsize);
+				SDPDBG("Svc Rec Handle : 0x%x", rec->handle);
+				SDPDBG("Bytes scanned : %d", scanned);
+				SDPDBG("Attrlist byte count : %d", attr_list_len);
+				rec_list = sdp_list_append(rec_list, rec);
+			} while (scanned < attr_list_len && pdata_len > 0);
+
+			SDPDBG("Successful scan of service attr lists");
+			*rsp = rec_list;
+		}
+	}
+end:
+	free(rsp_concat_buf.data);
+	free(reqbuf);
+	free(rspbuf);
+	return status;
+}
+
+/*
+ * Find devices in the piconet.
+ */
+int sdp_general_inquiry(inquiry_info *ii, int num_dev, int duration, uint8_t *found)
+{
+	int n = hci_inquiry(-1, 10, num_dev, NULL, &ii, 0);
+	if (n < 0) {
+		SDPERR("Inquiry failed:%m");
+		return -1;
+	}
+	*found = n;
+	return 0;
+}
+
+int sdp_close(sdp_session_t *session)
+{
+	struct sdp_transaction *t;
+	int ret;
+
+	if (!session)
+		return -1;
+
+	ret = close(session->sock);
+
+	t = session->priv;
+
+	if (t) {
+		free(t->reqbuf);
+
+		free(t->rsp_concat_buf.data);
+
+		free(t);
+	}
+	free(session);
+	return ret;
+}
+
+static inline int sdp_is_local(const bdaddr_t *device)
+{
+	return memcmp(device, BDADDR_LOCAL, sizeof(bdaddr_t)) == 0;
+}
+
+static int sdp_connect_local(sdp_session_t *session)
+{
+	struct sockaddr_un sa;
+
+	session->sock = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (session->sock < 0)
+		return -1;
+	session->local = 1;
+
+	sa.sun_family = AF_UNIX;
+	strcpy(sa.sun_path, SDP_UNIX_PATH);
+
+	return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa));
+}
+
+static int set_l2cap_mtu(int sk, uint16_t mtu)
+{
+	struct l2cap_options l2o;
+	socklen_t len;
+
+	memset(&l2o, 0, sizeof(l2o));
+	len = sizeof(l2o);
+
+	if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0)
+		return -1;
+
+	l2o.imtu = mtu;
+	l2o.omtu = mtu;
+
+	if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int sdp_connect_l2cap(const bdaddr_t *src,
+		const bdaddr_t *dst, sdp_session_t *session)
+{
+	uint32_t flags = session->flags;
+	struct sockaddr_l2 sa;
+	int sk;
+	int sockflags = SOCK_SEQPACKET | SOCK_CLOEXEC;
+
+	if (flags & SDP_NON_BLOCKING)
+		sockflags |= SOCK_NONBLOCK;
+
+	session->sock = socket(PF_BLUETOOTH, sockflags, BTPROTO_L2CAP);
+	if (session->sock < 0)
+		return -1;
+	session->local = 0;
+
+	sk = session->sock;
+
+	memset(&sa, 0, sizeof(sa));
+
+	sa.l2_family = AF_BLUETOOTH;
+	sa.l2_psm = 0;
+
+	if (bacmp(src, BDADDR_ANY)) {
+		sa.l2_bdaddr = *src;
+		if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+			return -1;
+	}
+
+	if (flags & SDP_WAIT_ON_CLOSE) {
+		struct linger l = { .l_onoff = 1, .l_linger = 1 };
+		setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+	}
+
+	if ((flags & SDP_LARGE_MTU) &&
+				set_l2cap_mtu(sk, SDP_LARGE_L2CAP_MTU) < 0)
+		return -1;
+
+	sa.l2_psm = htobs(SDP_PSM);
+	sa.l2_bdaddr = *dst;
+
+	do {
+		int ret = connect(sk, (struct sockaddr *) &sa, sizeof(sa));
+		if (!ret)
+			return 0;
+		if (ret < 0 && (flags & SDP_NON_BLOCKING) &&
+				(errno == EAGAIN || errno == EINPROGRESS))
+			return 0;
+	} while (errno == EBUSY && (flags & SDP_RETRY_IF_BUSY));
+
+	return -1;
+}
+
+sdp_session_t *sdp_connect(const bdaddr_t *src,
+		const bdaddr_t *dst, uint32_t flags)
+{
+	sdp_session_t *session;
+	int err;
+
+	if ((flags & SDP_RETRY_IF_BUSY) && (flags & SDP_NON_BLOCKING)) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	session = sdp_create(-1, flags);
+	if (!session)
+		return NULL;
+
+	if (sdp_is_local(dst)) {
+		if (sdp_connect_local(session) < 0)
+			goto fail;
+	} else {
+		if (sdp_connect_l2cap(src, dst, session) < 0)
+			goto fail;
+	}
+
+	return session;
+
+fail:
+	err = errno;
+	if (session->sock >= 0)
+		close(session->sock);
+	free(session->priv);
+	free(session);
+	errno = err;
+
+	return NULL;
+}
+
+int sdp_get_socket(const sdp_session_t *session)
+{
+	return session->sock;
+}
+
+uint16_t sdp_gen_tid(sdp_session_t *session)
+{
+	return session->tid++;
+}
+
+/*
+ * Set the supported features
+ */
+int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf)
+{
+	const sdp_list_t *p, *r;
+	sdp_data_t *feat, *seq_feat;
+	int seqlen, i;
+	void **seqDTDs, **seqVals;
+
+	seqlen = sdp_list_len(sf);
+	seqDTDs = malloc(seqlen * sizeof(void *));
+	if (!seqDTDs)
+		return -1;
+	seqVals = malloc(seqlen * sizeof(void *));
+	if (!seqVals) {
+		free(seqDTDs);
+		return -1;
+	}
+
+	for (p = sf, i = 0; p; p = p->next, i++) {
+		int plen, j;
+		void **dtds, **vals;
+		int *lengths;
+
+		plen = sdp_list_len(p->data);
+		dtds = malloc(plen * sizeof(void *));
+		if (!dtds)
+			goto fail;
+		vals = malloc(plen * sizeof(void *));
+		if (!vals) {
+			free(dtds);
+			goto fail;
+		}
+		lengths = malloc(plen * sizeof(int));
+		if (!lengths) {
+			free(dtds);
+			free(vals);
+			goto fail;
+		}
+		for (r = p->data, j = 0; r; r = r->next, j++) {
+			sdp_data_t *data = (sdp_data_t *) r->data;
+			dtds[j] = &data->dtd;
+			switch (data->dtd) {
+			case SDP_URL_STR8:
+			case SDP_URL_STR16:
+			case SDP_TEXT_STR8:
+			case SDP_TEXT_STR16:
+				vals[j] = data->val.str;
+				lengths[j] = data->unitSize - sizeof(uint8_t);
+				break;
+			case SDP_ALT8:
+			case SDP_ALT16:
+			case SDP_ALT32:
+			case SDP_SEQ8:
+			case SDP_SEQ16:
+			case SDP_SEQ32:
+				vals[j] = data->val.dataseq;
+				lengths[j] = 0;
+				break;
+			default:
+				vals[j] = &data->val;
+				lengths[j] = 0;
+				break;
+			}
+		}
+		feat = sdp_seq_alloc_with_length(dtds, vals, lengths, plen);
+		free(dtds);
+		free(vals);
+		free(lengths);
+		if (!feat)
+			goto fail;
+		seqDTDs[i] = &feat->dtd;
+		seqVals[i] = feat;
+	}
+	seq_feat = sdp_seq_alloc(seqDTDs, seqVals, seqlen);
+	if (!seq_feat)
+		goto fail;
+	sdp_attr_replace(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST, seq_feat);
+
+	free(seqVals);
+	free(seqDTDs);
+	return 0;
+
+fail:
+	free(seqVals);
+	free(seqDTDs);
+	return -1;
+}
+
+/*
+ * Get the supported features
+ * If an error occurred -1 is returned and errno is set
+ */
+int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+	sdp_data_t *sdpdata, *d;
+	sdp_list_t *tseq;
+	tseq = NULL;
+
+	sdpdata = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+
+	if (!sdpdata || !SDP_IS_SEQ(sdpdata->dtd))
+		return sdp_get_uuidseq_attr(rec,
+					SDP_ATTR_SUPPORTED_FEATURES_LIST, seqp);
+
+	for (d = sdpdata->val.dataseq; d; d = d->next) {
+		sdp_data_t *dd;
+		sdp_list_t *subseq;
+
+		if (!SDP_IS_SEQ(d->dtd))
+			goto fail;
+
+		subseq = NULL;
+
+		for (dd = d->val.dataseq; dd; dd = dd->next) {
+			sdp_data_t *data;
+			void *val;
+			int length;
+
+			switch (dd->dtd) {
+			case SDP_URL_STR8:
+			case SDP_URL_STR16:
+			case SDP_TEXT_STR8:
+			case SDP_TEXT_STR16:
+				val = dd->val.str;
+				length = dd->unitSize - sizeof(uint8_t);
+				break;
+			case SDP_UINT8:
+			case SDP_UINT16:
+				val = &dd->val;
+				length = 0;
+				break;
+			default:
+				sdp_list_free(subseq, free);
+				goto fail;
+			}
+
+			data = sdp_data_alloc_with_length(dd->dtd, val, length);
+			if (data)
+				subseq = sdp_list_append(subseq, data);
+		}
+		tseq = sdp_list_append(tseq, subseq);
+	}
+	*seqp = tseq;
+	return 0;
+
+fail:
+	while (tseq) {
+		sdp_list_t * next;
+
+		next = tseq->next;
+		sdp_list_free(tseq, free);
+		tseq = next;
+	}
+	errno = EINVAL;
+	return -1;
+}
+
+void sdp_add_lang_attr(sdp_record_t *rec)
+{
+	sdp_lang_attr_t base_lang;
+	sdp_list_t *langs;
+
+	base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+	base_lang.encoding = 106;
+	base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+
+	langs = sdp_list_append(0, &base_lang);
+	sdp_set_lang_attr(rec, langs);
+	sdp_list_free(langs, NULL);
+}
diff --git a/bluez/repo/lib/sdp.h b/bluez/repo/lib/sdp.h
new file mode 100644
index 0000000..cc10e9f
--- /dev/null
+++ b/bluez/repo/lib/sdp.h
@@ -0,0 +1,543 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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 __SDP_H
+#define __SDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <bluetooth/bluetooth.h>
+
+#define SDP_UNIX_PATH "/var/run/sdp"
+#define SDP_RESPONSE_TIMEOUT	20
+#define SDP_REQ_BUFFER_SIZE	2048
+#define SDP_RSP_BUFFER_SIZE	65535
+#define SDP_PDU_CHUNK_SIZE	1024
+
+/*
+ * All definitions are based on Bluetooth Assigned Numbers
+ * of the Bluetooth Specification
+ */
+#define SDP_PSM		0x0001
+
+/*
+ * Protocol UUIDs
+ */
+#define SDP_UUID	0x0001
+#define UDP_UUID	0x0002
+#define RFCOMM_UUID	0x0003
+#define TCP_UUID	0x0004
+#define TCS_BIN_UUID	0x0005
+#define TCS_AT_UUID	0x0006
+#define ATT_UUID	0x0007
+#define OBEX_UUID	0x0008
+#define IP_UUID		0x0009
+#define FTP_UUID	0x000a
+#define HTTP_UUID	0x000c
+#define WSP_UUID	0x000e
+#define BNEP_UUID	0x000f
+#define UPNP_UUID	0x0010
+#define HIDP_UUID	0x0011
+#define HCRP_CTRL_UUID	0x0012
+#define HCRP_DATA_UUID	0x0014
+#define HCRP_NOTE_UUID	0x0016
+#define AVCTP_UUID	0x0017
+#define AVDTP_UUID	0x0019
+#define CMTP_UUID	0x001b
+#define UDI_UUID	0x001d
+#define MCAP_CTRL_UUID	0x001e
+#define MCAP_DATA_UUID	0x001f
+#define L2CAP_UUID	0x0100
+
+/*
+ * Service class identifiers of standard services and service groups
+ */
+#define SDP_SERVER_SVCLASS_ID		0x1000
+#define BROWSE_GRP_DESC_SVCLASS_ID	0x1001
+#define PUBLIC_BROWSE_GROUP		0x1002
+#define SERIAL_PORT_SVCLASS_ID		0x1101
+#define LAN_ACCESS_SVCLASS_ID		0x1102
+#define DIALUP_NET_SVCLASS_ID		0x1103
+#define IRMC_SYNC_SVCLASS_ID		0x1104
+#define OBEX_OBJPUSH_SVCLASS_ID		0x1105
+#define OBEX_FILETRANS_SVCLASS_ID	0x1106
+#define IRMC_SYNC_CMD_SVCLASS_ID	0x1107
+#define HEADSET_SVCLASS_ID		0x1108
+#define CORDLESS_TELEPHONY_SVCLASS_ID	0x1109
+#define AUDIO_SOURCE_SVCLASS_ID		0x110a
+#define AUDIO_SINK_SVCLASS_ID		0x110b
+#define AV_REMOTE_TARGET_SVCLASS_ID	0x110c
+#define ADVANCED_AUDIO_SVCLASS_ID	0x110d
+#define AV_REMOTE_SVCLASS_ID		0x110e
+#define AV_REMOTE_CONTROLLER_SVCLASS_ID	0x110f
+#define INTERCOM_SVCLASS_ID		0x1110
+#define FAX_SVCLASS_ID			0x1111
+#define HEADSET_AGW_SVCLASS_ID		0x1112
+#define WAP_SVCLASS_ID			0x1113
+#define WAP_CLIENT_SVCLASS_ID		0x1114
+#define PANU_SVCLASS_ID			0x1115
+#define NAP_SVCLASS_ID			0x1116
+#define GN_SVCLASS_ID			0x1117
+#define DIRECT_PRINTING_SVCLASS_ID	0x1118
+#define REFERENCE_PRINTING_SVCLASS_ID	0x1119
+#define IMAGING_SVCLASS_ID		0x111a
+#define IMAGING_RESPONDER_SVCLASS_ID	0x111b
+#define IMAGING_ARCHIVE_SVCLASS_ID	0x111c
+#define IMAGING_REFOBJS_SVCLASS_ID	0x111d
+#define HANDSFREE_SVCLASS_ID		0x111e
+#define HANDSFREE_AGW_SVCLASS_ID	0x111f
+#define DIRECT_PRT_REFOBJS_SVCLASS_ID	0x1120
+#define REFLECTED_UI_SVCLASS_ID		0x1121
+#define BASIC_PRINTING_SVCLASS_ID	0x1122
+#define PRINTING_STATUS_SVCLASS_ID	0x1123
+#define HID_SVCLASS_ID			0x1124
+#define HCR_SVCLASS_ID			0x1125
+#define HCR_PRINT_SVCLASS_ID		0x1126
+#define HCR_SCAN_SVCLASS_ID		0x1127
+#define CIP_SVCLASS_ID			0x1128
+#define VIDEO_CONF_GW_SVCLASS_ID	0x1129
+#define UDI_MT_SVCLASS_ID		0x112a
+#define UDI_TA_SVCLASS_ID		0x112b
+#define AV_SVCLASS_ID			0x112c
+#define SAP_SVCLASS_ID			0x112d
+#define PBAP_PCE_SVCLASS_ID		0x112e
+#define PBAP_PSE_SVCLASS_ID		0x112f
+#define PBAP_SVCLASS_ID			0x1130
+#define MAP_MSE_SVCLASS_ID		0x1132
+#define MAP_MCE_SVCLASS_ID		0x1133
+#define MAP_SVCLASS_ID			0x1134
+#define GNSS_SVCLASS_ID			0x1135
+#define GNSS_SERVER_SVCLASS_ID		0x1136
+#define MPS_SC_SVCLASS_ID		0x113A
+#define MPS_SVCLASS_ID			0x113B
+#define PNP_INFO_SVCLASS_ID		0x1200
+#define GENERIC_NETWORKING_SVCLASS_ID	0x1201
+#define GENERIC_FILETRANS_SVCLASS_ID	0x1202
+#define GENERIC_AUDIO_SVCLASS_ID	0x1203
+#define GENERIC_TELEPHONY_SVCLASS_ID	0x1204
+#define UPNP_SVCLASS_ID			0x1205
+#define UPNP_IP_SVCLASS_ID		0x1206
+#define UPNP_PAN_SVCLASS_ID		0x1300
+#define UPNP_LAP_SVCLASS_ID		0x1301
+#define UPNP_L2CAP_SVCLASS_ID		0x1302
+#define VIDEO_SOURCE_SVCLASS_ID		0x1303
+#define VIDEO_SINK_SVCLASS_ID		0x1304
+#define VIDEO_DISTRIBUTION_SVCLASS_ID	0x1305
+#define HDP_SVCLASS_ID			0x1400
+#define HDP_SOURCE_SVCLASS_ID		0x1401
+#define HDP_SINK_SVCLASS_ID		0x1402
+#define GENERIC_ACCESS_SVCLASS_ID	0x1800
+#define GENERIC_ATTRIB_SVCLASS_ID	0x1801
+#define APPLE_AGENT_SVCLASS_ID		0x2112
+
+/*
+ * Standard profile descriptor identifiers; note these
+ * may be identical to some of the service classes defined above
+ */
+#define SDP_SERVER_PROFILE_ID		SDP_SERVER_SVCLASS_ID
+#define BROWSE_GRP_DESC_PROFILE_ID	BROWSE_GRP_DESC_SVCLASS_ID
+#define SERIAL_PORT_PROFILE_ID		SERIAL_PORT_SVCLASS_ID
+#define LAN_ACCESS_PROFILE_ID		LAN_ACCESS_SVCLASS_ID
+#define DIALUP_NET_PROFILE_ID		DIALUP_NET_SVCLASS_ID
+#define IRMC_SYNC_PROFILE_ID		IRMC_SYNC_SVCLASS_ID
+#define OBEX_OBJPUSH_PROFILE_ID		OBEX_OBJPUSH_SVCLASS_ID
+#define OBEX_FILETRANS_PROFILE_ID	OBEX_FILETRANS_SVCLASS_ID
+#define IRMC_SYNC_CMD_PROFILE_ID	IRMC_SYNC_CMD_SVCLASS_ID
+#define HEADSET_PROFILE_ID		HEADSET_SVCLASS_ID
+#define CORDLESS_TELEPHONY_PROFILE_ID	CORDLESS_TELEPHONY_SVCLASS_ID
+#define AUDIO_SOURCE_PROFILE_ID		AUDIO_SOURCE_SVCLASS_ID
+#define AUDIO_SINK_PROFILE_ID		AUDIO_SINK_SVCLASS_ID
+#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
+#define WAP_PROFILE_ID			WAP_SVCLASS_ID
+#define WAP_CLIENT_PROFILE_ID		WAP_CLIENT_SVCLASS_ID
+#define PANU_PROFILE_ID			PANU_SVCLASS_ID
+#define NAP_PROFILE_ID			NAP_SVCLASS_ID
+#define GN_PROFILE_ID			GN_SVCLASS_ID
+#define DIRECT_PRINTING_PROFILE_ID	DIRECT_PRINTING_SVCLASS_ID
+#define REFERENCE_PRINTING_PROFILE_ID	REFERENCE_PRINTING_SVCLASS_ID
+#define IMAGING_PROFILE_ID		IMAGING_SVCLASS_ID
+#define IMAGING_RESPONDER_PROFILE_ID	IMAGING_RESPONDER_SVCLASS_ID
+#define IMAGING_ARCHIVE_PROFILE_ID	IMAGING_ARCHIVE_SVCLASS_ID
+#define IMAGING_REFOBJS_PROFILE_ID	IMAGING_REFOBJS_SVCLASS_ID
+#define HANDSFREE_PROFILE_ID		HANDSFREE_SVCLASS_ID
+#define HANDSFREE_AGW_PROFILE_ID	HANDSFREE_AGW_SVCLASS_ID
+#define DIRECT_PRT_REFOBJS_PROFILE_ID	DIRECT_PRT_REFOBJS_SVCLASS_ID
+#define REFLECTED_UI_PROFILE_ID		REFLECTED_UI_SVCLASS_ID
+#define BASIC_PRINTING_PROFILE_ID	BASIC_PRINTING_SVCLASS_ID
+#define PRINTING_STATUS_PROFILE_ID	PRINTING_STATUS_SVCLASS_ID
+#define HID_PROFILE_ID			HID_SVCLASS_ID
+#define HCR_PROFILE_ID			HCR_SCAN_SVCLASS_ID
+#define HCR_PRINT_PROFILE_ID		HCR_PRINT_SVCLASS_ID
+#define HCR_SCAN_PROFILE_ID		HCR_SCAN_SVCLASS_ID
+#define CIP_PROFILE_ID			CIP_SVCLASS_ID
+#define VIDEO_CONF_GW_PROFILE_ID	VIDEO_CONF_GW_SVCLASS_ID
+#define UDI_MT_PROFILE_ID		UDI_MT_SVCLASS_ID
+#define UDI_TA_PROFILE_ID		UDI_TA_SVCLASS_ID
+#define AV_PROFILE_ID			AV_SVCLASS_ID
+#define SAP_PROFILE_ID			SAP_SVCLASS_ID
+#define PBAP_PCE_PROFILE_ID		PBAP_PCE_SVCLASS_ID
+#define PBAP_PSE_PROFILE_ID		PBAP_PSE_SVCLASS_ID
+#define PBAP_PROFILE_ID			PBAP_SVCLASS_ID
+#define MAP_PROFILE_ID			MAP_SVCLASS_ID
+#define PNP_INFO_PROFILE_ID		PNP_INFO_SVCLASS_ID
+#define GENERIC_NETWORKING_PROFILE_ID	GENERIC_NETWORKING_SVCLASS_ID
+#define GENERIC_FILETRANS_PROFILE_ID	GENERIC_FILETRANS_SVCLASS_ID
+#define GENERIC_AUDIO_PROFILE_ID	GENERIC_AUDIO_SVCLASS_ID
+#define GENERIC_TELEPHONY_PROFILE_ID	GENERIC_TELEPHONY_SVCLASS_ID
+#define UPNP_PROFILE_ID			UPNP_SVCLASS_ID
+#define UPNP_IP_PROFILE_ID		UPNP_IP_SVCLASS_ID
+#define UPNP_PAN_PROFILE_ID		UPNP_PAN_SVCLASS_ID
+#define UPNP_LAP_PROFILE_ID		UPNP_LAP_SVCLASS_ID
+#define UPNP_L2CAP_PROFILE_ID		UPNP_L2CAP_SVCLASS_ID
+#define VIDEO_SOURCE_PROFILE_ID		VIDEO_SOURCE_SVCLASS_ID
+#define VIDEO_SINK_PROFILE_ID		VIDEO_SINK_SVCLASS_ID
+#define VIDEO_DISTRIBUTION_PROFILE_ID	VIDEO_DISTRIBUTION_SVCLASS_ID
+#define HDP_PROFILE_ID			HDP_SVCLASS_ID
+#define HDP_SOURCE_PROFILE_ID		HDP_SOURCE_SVCLASS_ID
+#define HDP_SINK_PROFILE_ID		HDP_SINK_SVCLASS_ID
+#define GENERIC_ACCESS_PROFILE_ID	GENERIC_ACCESS_SVCLASS_ID
+#define GENERIC_ATTRIB_PROFILE_ID	GENERIC_ATTRIB_SVCLASS_ID
+#define APPLE_AGENT_PROFILE_ID		APPLE_AGENT_SVCLASS_ID
+#define MPS_PROFILE_ID			MPS_SC_SVCLASS_ID
+
+/*
+ * Compatibility macros for the old MDP acronym
+ */
+#define MDP_SVCLASS_ID			HDP_SVCLASS_ID
+#define MDP_SOURCE_SVCLASS_ID		HDP_SOURCE_SVCLASS_ID
+#define MDP_SINK_SVCLASS_ID		HDP_SINK_SVCLASS_ID
+#define MDP_PROFILE_ID			HDP_PROFILE_ID
+#define MDP_SOURCE_PROFILE_ID		HDP_SOURCE_PROFILE_ID
+#define MDP_SINK_PROFILE_ID		HDP_SINK_PROFILE_ID
+
+/*
+ * Attribute identifier codes
+ */
+#define SDP_SERVER_RECORD_HANDLE		0x0000
+
+/*
+ * Possible values for attribute-id are listed below.
+ * See SDP Spec, section "Service Attribute Definitions" for more details.
+ */
+#define SDP_ATTR_RECORD_HANDLE			0x0000
+#define SDP_ATTR_SVCLASS_ID_LIST		0x0001
+#define SDP_ATTR_RECORD_STATE			0x0002
+#define SDP_ATTR_SERVICE_ID			0x0003
+#define SDP_ATTR_PROTO_DESC_LIST		0x0004
+#define SDP_ATTR_BROWSE_GRP_LIST		0x0005
+#define SDP_ATTR_LANG_BASE_ATTR_ID_LIST		0x0006
+#define SDP_ATTR_SVCINFO_TTL			0x0007
+#define SDP_ATTR_SERVICE_AVAILABILITY		0x0008
+#define SDP_ATTR_PFILE_DESC_LIST		0x0009
+#define SDP_ATTR_DOC_URL			0x000a
+#define SDP_ATTR_CLNT_EXEC_URL			0x000b
+#define SDP_ATTR_ICON_URL			0x000c
+#define SDP_ATTR_ADD_PROTO_DESC_LIST		0x000d
+
+#define SDP_ATTR_GROUP_ID			0x0200
+#define SDP_ATTR_IP_SUBNET			0x0200
+#define SDP_ATTR_VERSION_NUM_LIST		0x0200
+#define SDP_ATTR_SUPPORTED_FEATURES_LIST	0x0200
+#define SDP_ATTR_GOEP_L2CAP_PSM			0x0200
+#define SDP_ATTR_SVCDB_STATE			0x0201
+
+#define SDP_ATTR_MPSD_SCENARIOS			0x0200
+#define SDP_ATTR_MPMD_SCENARIOS			0x0201
+#define SDP_ATTR_MPS_DEPENDENCIES		0x0202
+
+#define SDP_ATTR_SERVICE_VERSION		0x0300
+#define SDP_ATTR_EXTERNAL_NETWORK		0x0301
+#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST	0x0301
+#define SDP_ATTR_DATA_EXCHANGE_SPEC		0x0301
+#define SDP_ATTR_NETWORK			0x0301
+#define SDP_ATTR_FAX_CLASS1_SUPPORT		0x0302
+#define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL	0x0302
+#define SDP_ATTR_MCAP_SUPPORTED_PROCEDURES	0x0302
+#define SDP_ATTR_FAX_CLASS20_SUPPORT		0x0303
+#define SDP_ATTR_SUPPORTED_FORMATS_LIST		0x0303
+#define SDP_ATTR_FAX_CLASS2_SUPPORT		0x0304
+#define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT		0x0305
+#define SDP_ATTR_NETWORK_ADDRESS		0x0306
+#define SDP_ATTR_WAP_GATEWAY			0x0307
+#define SDP_ATTR_HOMEPAGE_URL			0x0308
+#define SDP_ATTR_WAP_STACK_TYPE			0x0309
+#define SDP_ATTR_SECURITY_DESC			0x030a
+#define SDP_ATTR_NET_ACCESS_TYPE		0x030b
+#define SDP_ATTR_MAX_NET_ACCESSRATE		0x030c
+#define SDP_ATTR_IP4_SUBNET			0x030d
+#define SDP_ATTR_IP6_SUBNET			0x030e
+#define SDP_ATTR_SUPPORTED_CAPABILITIES		0x0310
+#define SDP_ATTR_SUPPORTED_FEATURES		0x0311
+#define SDP_ATTR_SUPPORTED_FUNCTIONS		0x0312
+#define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY	0x0313
+#define SDP_ATTR_SUPPORTED_REPOSITORIES		0x0314
+#define SDP_ATTR_MAS_INSTANCE_ID		0x0315
+#define SDP_ATTR_SUPPORTED_MESSAGE_TYPES	0x0316
+#define SDP_ATTR_PBAP_SUPPORTED_FEATURES	0x0317
+#define SDP_ATTR_MAP_SUPPORTED_FEATURES		0x0317
+
+#define SDP_ATTR_SPECIFICATION_ID		0x0200
+#define SDP_ATTR_VENDOR_ID			0x0201
+#define SDP_ATTR_PRODUCT_ID			0x0202
+#define SDP_ATTR_VERSION			0x0203
+#define SDP_ATTR_PRIMARY_RECORD			0x0204
+#define SDP_ATTR_VENDOR_ID_SOURCE		0x0205
+
+#define SDP_ATTR_HID_DEVICE_RELEASE_NUMBER	0x0200
+#define SDP_ATTR_HID_PARSER_VERSION		0x0201
+#define SDP_ATTR_HID_DEVICE_SUBCLASS		0x0202
+#define SDP_ATTR_HID_COUNTRY_CODE		0x0203
+#define SDP_ATTR_HID_VIRTUAL_CABLE		0x0204
+#define SDP_ATTR_HID_RECONNECT_INITIATE		0x0205
+#define SDP_ATTR_HID_DESCRIPTOR_LIST		0x0206
+#define SDP_ATTR_HID_LANG_ID_BASE_LIST		0x0207
+#define SDP_ATTR_HID_SDP_DISABLE		0x0208
+#define SDP_ATTR_HID_BATTERY_POWER		0x0209
+#define SDP_ATTR_HID_REMOTE_WAKEUP		0x020a
+#define SDP_ATTR_HID_PROFILE_VERSION		0x020b
+#define SDP_ATTR_HID_SUPERVISION_TIMEOUT	0x020c
+#define SDP_ATTR_HID_NORMALLY_CONNECTABLE	0x020d
+#define SDP_ATTR_HID_BOOT_DEVICE		0x020e
+
+/*
+ * These identifiers are based on the SDP spec stating that
+ * "base attribute id of the primary (universal) language must be 0x0100"
+ *
+ * Other languages should have their own offset; e.g.:
+ * #define XXXLangBase yyyy
+ * #define AttrServiceName_XXX	0x0000+XXXLangBase
+ */
+#define SDP_PRIMARY_LANG_BASE		0x0100
+
+#define SDP_ATTR_SVCNAME_PRIMARY	0x0000 + SDP_PRIMARY_LANG_BASE
+#define SDP_ATTR_SVCDESC_PRIMARY	0x0001 + SDP_PRIMARY_LANG_BASE
+#define SDP_ATTR_PROVNAME_PRIMARY	0x0002 + SDP_PRIMARY_LANG_BASE
+
+/*
+ * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec)
+ * These are the exact data type+size descriptor values
+ * that go into the PDU buffer.
+ *
+ * The datatype (leading 5bits) + size descriptor (last 3 bits)
+ * is 8 bits. The size descriptor is critical to extract the
+ * right number of bytes for the data value from the PDU.
+ *
+ * For most basic types, the datatype+size descriptor is
+ * straightforward. However for constructed types and strings,
+ * the size of the data is in the next "n" bytes following the
+ * 8 bits (datatype+size) descriptor. Exactly what the "n" is
+ * specified in the 3 bits of the data size descriptor.
+ *
+ * TextString and URLString can be of size 2^{8, 16, 32} bytes
+ * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32}
+ * The size are computed post-facto in the API and are not known apriori
+ */
+#define SDP_DATA_NIL		0x00
+#define SDP_UINT8		0x08
+#define SDP_UINT16		0x09
+#define SDP_UINT32		0x0A
+#define SDP_UINT64		0x0B
+#define SDP_UINT128		0x0C
+#define SDP_INT8		0x10
+#define SDP_INT16		0x11
+#define SDP_INT32		0x12
+#define SDP_INT64		0x13
+#define SDP_INT128		0x14
+#define SDP_UUID_UNSPEC		0x18
+#define SDP_UUID16		0x19
+#define SDP_UUID32		0x1A
+#define SDP_UUID128		0x1C
+#define SDP_TEXT_STR_UNSPEC	0x20
+#define SDP_TEXT_STR8		0x25
+#define SDP_TEXT_STR16		0x26
+#define SDP_TEXT_STR32		0x27
+#define SDP_BOOL		0x28
+#define SDP_SEQ_UNSPEC		0x30
+#define SDP_SEQ8		0x35
+#define SDP_SEQ16		0x36
+#define SDP_SEQ32		0x37
+#define SDP_ALT_UNSPEC		0x38
+#define SDP_ALT8		0x3D
+#define SDP_ALT16		0x3E
+#define SDP_ALT32		0x3F
+#define SDP_URL_STR_UNSPEC	0x40
+#define SDP_URL_STR8		0x45
+#define SDP_URL_STR16		0x46
+#define SDP_URL_STR32		0x47
+
+/*
+ * The PDU identifiers of SDP packets between client and server
+ */
+#define SDP_ERROR_RSP		0x01
+#define SDP_SVC_SEARCH_REQ	0x02
+#define SDP_SVC_SEARCH_RSP	0x03
+#define SDP_SVC_ATTR_REQ	0x04
+#define SDP_SVC_ATTR_RSP	0x05
+#define SDP_SVC_SEARCH_ATTR_REQ	0x06
+#define SDP_SVC_SEARCH_ATTR_RSP	0x07
+
+/*
+ * Some additions to support service registration.
+ * These are outside the scope of the Bluetooth specification
+ */
+#define SDP_SVC_REGISTER_REQ	0x75
+#define SDP_SVC_REGISTER_RSP	0x76
+#define SDP_SVC_UPDATE_REQ	0x77
+#define SDP_SVC_UPDATE_RSP	0x78
+#define SDP_SVC_REMOVE_REQ	0x79
+#define SDP_SVC_REMOVE_RSP	0x80
+
+/*
+ * SDP Error codes
+ */
+#define SDP_INVALID_VERSION		0x0001
+#define SDP_INVALID_RECORD_HANDLE	0x0002
+#define SDP_INVALID_SYNTAX		0x0003
+#define SDP_INVALID_PDU_SIZE		0x0004
+#define SDP_INVALID_CSTATE		0x0005
+
+/*
+ * SDP PDU
+ */
+typedef struct {
+	uint8_t  pdu_id;
+	uint16_t tid;
+	uint16_t plen;
+} __attribute__ ((packed)) sdp_pdu_hdr_t;
+
+/*
+ * Common definitions for attributes in the SDP.
+ * Should the type of any of these change, you need only make a change here.
+ */
+
+typedef struct {
+	uint8_t type;
+	union {
+		uint16_t  uuid16;
+		uint32_t  uuid32;
+		uint128_t uuid128;
+	} value;
+} uuid_t;
+
+#define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || \
+							(x) == SDP_UUID128)
+#define SDP_IS_ALT(x)  ((x) == SDP_ALT8 || (x) == SDP_ALT16 || (x) == SDP_ALT32)
+#define SDP_IS_SEQ(x)  ((x) == SDP_SEQ8 || (x) == SDP_SEQ16 || (x) == SDP_SEQ32)
+#define SDP_IS_TEXT_STR(x) ((x) == SDP_TEXT_STR8 || (x) == SDP_TEXT_STR16 || \
+							(x) == SDP_TEXT_STR32)
+
+typedef struct _sdp_list sdp_list_t;
+struct _sdp_list {
+	sdp_list_t *next;
+	void *data;
+};
+
+/*
+ * User-visible strings can be in many languages
+ * in addition to the universal language.
+ *
+ * Language meta-data includes language code in ISO639
+ * followed by the encoding format. The third field in this
+ * structure is the attribute offset for the language.
+ * User-visible strings in the specified language can be
+ * obtained at this offset.
+ */
+typedef struct {
+	uint16_t code_ISO639;
+	uint16_t encoding;
+	uint16_t base_offset;
+} sdp_lang_attr_t;
+
+/*
+ * Profile descriptor is the Bluetooth profile metadata. If a
+ * service conforms to a well-known profile, then its profile
+ * identifier (UUID) is an attribute of the service. In addition,
+ * if the profile has a version number it is specified here.
+ */
+typedef struct {
+	uuid_t uuid;
+	uint16_t version;
+} sdp_profile_desc_t;
+
+typedef struct {
+	uint8_t major;
+	uint8_t minor;
+} sdp_version_t;
+
+typedef struct {
+	uint8_t *data;
+	uint32_t data_size;
+	uint32_t buf_size;
+} sdp_buf_t;
+
+typedef struct {
+	uint32_t handle;
+
+	/* Search pattern: a sequence of all UUIDs seen in this record */
+	sdp_list_t *pattern;
+	sdp_list_t *attrlist;
+
+	/* Main service class for Extended Inquiry Response */
+	uuid_t svclass;
+} sdp_record_t;
+
+typedef struct sdp_data_struct sdp_data_t;
+struct sdp_data_struct {
+	uint8_t dtd;
+	uint16_t attrId;
+	union {
+		int8_t    int8;
+		int16_t   int16;
+		int32_t   int32;
+		int64_t   int64;
+		uint128_t int128;
+		uint8_t   uint8;
+		uint16_t  uint16;
+		uint32_t  uint32;
+		uint64_t  uint64;
+		uint128_t uint128;
+		uuid_t    uuid;
+		char     *str;
+		sdp_data_t *dataseq;
+	} val;
+	sdp_data_t *next;
+	int unitSize;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SDP_H */
diff --git a/bluez/repo/lib/sdp_lib.h b/bluez/repo/lib/sdp_lib.h
new file mode 100644
index 0000000..3ded393
--- /dev/null
+++ b/bluez/repo/lib/sdp_lib.h
@@ -0,0 +1,634 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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 __SDP_LIB_H
+#define __SDP_LIB_H
+
+#include <sys/socket.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SDP lists
+ */
+typedef void(*sdp_list_func_t)(void *, void *);
+typedef void(*sdp_free_func_t)(void *);
+typedef int (*sdp_comp_func_t)(const void *, const void *);
+
+sdp_list_t *sdp_list_append(sdp_list_t *list, void *d);
+sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d);
+sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *data, sdp_comp_func_t f);
+void        sdp_list_free(sdp_list_t *list, sdp_free_func_t f);
+
+static inline int sdp_list_len(const sdp_list_t *list)
+{
+	int n = 0;
+	for (; list; list = list->next)
+		n++;
+	return n;
+}
+
+static inline sdp_list_t *sdp_list_find(sdp_list_t *list, void *u, sdp_comp_func_t f)
+{
+	for (; list; list = list->next)
+		if (f(list->data, u) == 0)
+			return list;
+	return NULL;
+}
+
+static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u)
+{
+	for (; list; list = list->next)
+		f(list->data, u);
+}
+
+/*
+ * Values of the flags parameter to sdp_record_register
+ */
+#define SDP_RECORD_PERSIST	0x01
+#define SDP_DEVICE_RECORD	0x02
+
+/*
+ * Values of the flags parameter to sdp_connect
+ */
+#define SDP_RETRY_IF_BUSY	0x01
+#define SDP_WAIT_ON_CLOSE	0x02
+#define SDP_NON_BLOCKING	0x04
+#define SDP_LARGE_MTU		0x08
+
+/*
+ * a session with an SDP server
+ */
+typedef struct {
+	int sock;
+	int state;
+	int local;
+	int flags;
+	uint16_t tid;	/* Current transaction ID */
+	void *priv;
+} sdp_session_t;
+
+typedef enum {
+	/*
+	 *  Attributes are specified as individual elements
+	 */
+	SDP_ATTR_REQ_INDIVIDUAL = 1,
+	/*
+	 *  Attributes are specified as a range
+	 */
+	SDP_ATTR_REQ_RANGE
+} sdp_attrreq_type_t;
+
+/*
+ * 	When the pdu_id(type) is a sdp error response, check the status value
+ * 	to figure out the error reason. For status values 0x0001-0x0006 check
+ * 	Bluetooth SPEC. If the status is 0xffff, call sdp_get_error function
+ * 	to get the real reason:
+ * 	    - wrong transaction ID(EPROTO)
+ * 	    - wrong PDU id or(EPROTO)
+ * 	    - I/O error
+ */
+typedef void sdp_callback_t(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *udata);
+
+/*
+ * create an L2CAP connection to a Bluetooth device
+ *
+ * INPUT:
+ *
+ *  bdaddr_t *src:
+ *	Address of the local device to use to make the connection
+ *	(or BDADDR_ANY)
+ *
+ *  bdaddr_t *dst:
+ *    Address of the SDP server device
+ */
+sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags);
+int sdp_close(sdp_session_t *session);
+int sdp_get_socket(const sdp_session_t *session);
+
+/*
+ * SDP transaction: functions for asynchronous search.
+ */
+sdp_session_t *sdp_create(int sk, uint32_t flags);
+int sdp_get_error(sdp_session_t *session);
+int sdp_process(sdp_session_t *session);
+int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata);
+
+int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num);
+int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+
+uint16_t sdp_gen_tid(sdp_session_t *session);
+
+/*
+ * find all devices in the piconet
+ */
+int sdp_general_inquiry(inquiry_info *ii, int dev_num, int duration, uint8_t *found);
+
+/* flexible extraction of basic attributes - Jean II */
+int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attr, int *value);
+int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attr, char *value, int valuelen);
+
+/*
+ * Basic sdp data functions
+ */
+sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value);
+sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length);
+void sdp_data_free(sdp_data_t *data);
+sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attr_id);
+
+sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len);
+sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len);
+sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *data);
+
+int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
+void sdp_attr_remove(sdp_record_t *rec, uint16_t attr);
+void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
+int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t attr, sdp_list_t *seq);
+int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp);
+
+/*
+ * NOTE that none of the functions below will update the SDP server,
+ * unless the {register, update}sdp_record_t() function is invoked.
+ * All functions which return an integer value, return 0 on success
+ * or -1 on failure.
+ */
+
+/*
+ * Create an attribute and add it to the service record's attribute list.
+ * This consists of the data type descriptor of the attribute,
+ * the value of the attribute and the attribute identifier.
+ */
+int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *p);
+
+/*
+ * Set the information attributes of the service record.
+ * The set of attributes comprises service name, description
+ * and provider name
+ */
+void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc);
+
+/*
+ * Set the ServiceClassID attribute to the sequence specified by seq.
+ * Note that the identifiers need to be in sorted order from the most
+ * specific to the most generic service class that this service
+ * conforms to.
+ */
+static inline int sdp_set_service_classes(sdp_record_t *rec, sdp_list_t *seq)
+{
+	return sdp_set_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seq);
+}
+
+/*
+ * Get the service classes to which the service conforms.
+ *
+ * When set, the list contains elements of ServiceClassIdentifer(uint16_t)
+ * ordered from most specific to most generic
+ */
+static inline int sdp_get_service_classes(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+	return sdp_get_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seqp);
+}
+
+/*
+ * Set the BrowseGroupList attribute to the list specified by seq.
+ *
+ * A service can belong to one or more service groups
+ * and the list comprises such group identifiers (UUIDs)
+ */
+static inline int sdp_set_browse_groups(sdp_record_t *rec, sdp_list_t *seq)
+{
+	return sdp_set_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seq);
+}
+
+/*
+ * Set the access protocols of the record to those specified in proto
+ */
+int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *proto);
+
+/*
+ * Set the additional access protocols of the record to those specified in proto
+ */
+int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *proto);
+
+/*
+ * Get protocol port (i.e. PSM for L2CAP, Channel for RFCOMM)
+ */
+int sdp_get_proto_port(const sdp_list_t *list, int proto);
+
+/*
+ * Get protocol descriptor.
+ */
+sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto);
+
+/*
+ * Set the LanguageBase attributes to the values specified in list
+ * (a linked list of sdp_lang_attr_t objects, one for each language in
+ * which user-visible attributes are present).
+ */
+int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *list);
+
+/*
+ * Set the ServiceInfoTimeToLive attribute of the service.
+ * This is the number of seconds that this record is guaranteed
+ * not to change after being obtained by a client.
+ */
+static inline int sdp_set_service_ttl(sdp_record_t *rec, uint32_t ttl)
+{
+	return sdp_attr_add_new(rec, SDP_ATTR_SVCINFO_TTL, SDP_UINT32, &ttl);
+}
+
+/*
+ * Set the ServiceRecordState attribute of a service. This is
+ * guaranteed to change if there is any kind of modification to
+ * the record.
+ */
+static inline int sdp_set_record_state(sdp_record_t *rec, uint32_t state)
+{
+	return sdp_attr_add_new(rec, SDP_ATTR_RECORD_STATE, SDP_UINT32, &state);
+}
+
+/*
+ * Set the ServiceID attribute of a service.
+ */
+void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid);
+
+/*
+ * Set the GroupID attribute of a service
+ */
+void sdp_set_group_id(sdp_record_t *rec, uuid_t grouuuid);
+
+/*
+ * Set the ServiceAvailability attribute of a service.
+ *
+ * Note that this represents the relative availability
+ * of the service: 0x00 means completely unavailable;
+ * 0xFF means maximum availability.
+ */
+static inline int sdp_set_service_avail(sdp_record_t *rec, uint8_t avail)
+{
+	return sdp_attr_add_new(rec, SDP_ATTR_SERVICE_AVAILABILITY, SDP_UINT8, &avail);
+}
+
+/*
+ * Set the profile descriptor list attribute of a record.
+ *
+ * Each element in the list is an object of type
+ * sdp_profile_desc_t which is a definition of the
+ * Bluetooth profile that this service conforms to.
+ */
+int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *desc);
+
+/*
+ * Set URL attributes of a record.
+ *
+ * ClientExecutableURL: a URL to a client's platform specific (WinCE,
+ * PalmOS) executable code that can be used to access this service.
+ *
+ * DocumentationURL: a URL pointing to service documentation
+ *
+ * IconURL: a URL to an icon that can be used to represent this service.
+ *
+ * Note: pass NULL for any URLs that you don't want to set or remove
+ */
+void sdp_set_url_attr(sdp_record_t *rec, const char *clientExecURL, const char *docURL, const char *iconURL);
+
+/*
+ * a service search request.
+ *
+ *  INPUT :
+ *
+ *    sdp_list_t *search
+ *      list containing elements of the search
+ *      pattern. Each entry in the list is a UUID
+ *      of the service to be searched
+ *
+ *    uint16_t max_rec_num
+ *       An integer specifying the maximum number of
+ *       entries that the client can handle in the response.
+ *
+ *  OUTPUT :
+ *
+ *    int return value
+ *      0
+ *        The request completed successfully. This does not
+ *        mean the requested services were found
+ *      -1
+ *        The request completed unsuccessfully
+ *
+ *    sdp_list_t *rsp_list
+ *      This variable is set on a successful return if there are
+ *      non-zero service handles. It is a singly linked list of
+ *      service record handles (uint16_t)
+ */
+int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp_list);
+
+/*
+ *  a service attribute request.
+ *
+ *  INPUT :
+ *
+ *    uint32_t handle
+ *      The handle of the service for which the attribute(s) are
+ *      requested
+ *
+ *    sdp_attrreq_type_t reqtype
+ *      Attribute identifiers are 16 bit unsigned integers specified
+ *      in one of 2 ways described below :
+ *      SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *         They are the actual attribute identifiers in ascending order
+ *
+ *      SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *         The high-order 16bits is the start of range
+ *         the low-order 16bits are the end of range
+ *         0x0000 to 0xFFFF gets all attributes
+ *
+ *    sdp_list_t *attrid_list
+ *      Singly linked list containing attribute identifiers desired.
+ *      Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *      or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ *  OUTPUT :
+ *    int return value
+ *      0
+ *        The request completed successfully. This does not
+ *        mean the requested services were found
+ *      -1
+ *        The request completed unsuccessfully due to a timeout
+ */
+sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+
+/*
+ *  This is a service search request combined with the service
+ *  attribute request. First a service class match is done and
+ *  for matching service, requested attributes are extracted
+ *
+ *  INPUT :
+ *
+ *    sdp_list_t *search
+ *      Singly linked list containing elements of the search
+ *      pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ *      of the service to be searched
+ *
+ *    AttributeSpecification attrSpec
+ *      Attribute identifiers are 16 bit unsigned integers specified
+ *      in one of 2 ways described below :
+ *      SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *         They are the actual attribute identifiers in ascending order
+ *
+ *      SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *         The high-order 16bits is the start of range
+ *         the low-order 16bits are the end of range
+ *         0x0000 to 0xFFFF gets all attributes
+ *
+ *    sdp_list_t *attrid_list
+ *      Singly linked list containing attribute identifiers desired.
+ *      Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *      or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ *  OUTPUT :
+ *    int return value
+ *      0
+ *        The request completed successfully. This does not
+ *        mean the requested services were found
+ *      -1
+ *        The request completed unsuccessfully due to a timeout
+ *
+ *    sdp_list_t *rsp_list
+ *      This variable is set on a successful return to point to
+ *      service(s) found. Each element of this list is of type
+ *      sdp_record_t *.
+ */
+int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list);
+
+/*
+ * Allocate/free a service record and its attributes
+ */
+sdp_record_t *sdp_record_alloc(void);
+void sdp_record_free(sdp_record_t *rec);
+
+/*
+ * Register a service record.
+ *
+ * Note: It is the responsbility of the Service Provider to create the
+ * record first and set its attributes using setXXX() methods.
+ *
+ * The service provider must then call sdp_record_register() to make
+ * the service record visible to SDP clients.  This function returns 0
+ * on success or -1 on failure (and sets errno).
+ */
+int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle);
+int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags);
+int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags);
+
+/*
+ * Unregister a service record.
+ */
+int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle);
+int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec);
+int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec);
+
+/*
+ * Update an existing service record.  (Calling this function
+ * before a previous call to sdp_record_register() will result
+ * in an error.)
+ */
+int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size);
+int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec);
+int sdp_record_update(sdp_session_t *sess, const sdp_record_t *rec);
+
+void sdp_record_print(const sdp_record_t *rec);
+
+/*
+ * UUID functions
+ */
+uuid_t *sdp_uuid16_create(uuid_t *uuid, uint16_t data);
+uuid_t *sdp_uuid32_create(uuid_t *uuid, uint32_t data);
+uuid_t *sdp_uuid128_create(uuid_t *uuid, const void *data);
+int sdp_uuid16_cmp(const void *p1, const void *p2);
+int sdp_uuid128_cmp(const void *p1, const void *p2);
+int sdp_uuid_cmp(const void *p1, const void *p2);
+uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid);
+void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16);
+void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32);
+int sdp_uuid128_to_uuid(uuid_t *uuid);
+int sdp_uuid_to_proto(uuid_t *uuid);
+int sdp_uuid_extract(const uint8_t *buffer, int bufsize, uuid_t *uuid, int *scanned);
+void sdp_uuid_print(const uuid_t *uuid);
+
+#define MAX_LEN_UUID_STR 37
+#define MAX_LEN_PROTOCOL_UUID_STR 8
+#define MAX_LEN_SERVICECLASS_UUID_STR 28
+#define MAX_LEN_PROFILEDESCRIPTOR_UUID_STR 28
+
+int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+
+/*
+ * In all the sdp_get_XXX(handle, XXX *xxx) functions below,
+ * the XXX * is set to point to the value, should it exist
+ * and 0 is returned. If the value does not exist, -1 is
+ * returned and errno set to ENODATA.
+ *
+ * In all the methods below, the memory management rules are
+ * simple. Don't free anything! The pointer returned, in the
+ * case of constructed types, is a pointer to the contents
+ * of the sdp_record_t.
+ */
+
+/*
+ * Get the access protocols from the service record
+ */
+int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
+
+/*
+ * Get the additional access protocols from the service record
+ */
+int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
+
+/*
+ * Extract the list of browse groups to which the service belongs.
+ * When set, seqp contains elements of GroupID (uint16_t)
+ */
+static inline int sdp_get_browse_groups(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+	return sdp_get_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seqp);
+}
+
+/*
+ * Extract language attribute meta-data of the service record.
+ * For each language in the service record, LangSeq has a struct of type
+ * sdp_lang_attr_t.
+ */
+int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq);
+
+/*
+ * Extract the Bluetooth profile descriptor sequence from a record.
+ * Each element in the list is of type sdp_profile_desc_t
+ * which contains the UUID of the profile and its version number
+ * (encoded as major and minor in the high-order 8bits
+ * and low-order 8bits respectively of the uint16_t)
+ */
+int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDesc);
+
+/*
+ * Extract SDP server version numbers
+ *
+ * Note: that this is an attribute of the SDP server only and
+ * contains a list of uint16_t each of which represent the
+ * major and minor SDP version numbers supported by this server
+ */
+int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **pVnumList);
+
+int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid);
+int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid);
+int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState);
+int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail);
+int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo);
+int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState);
+
+static inline int sdp_get_service_name(const sdp_record_t *rec, char *str, int len)
+{
+	return sdp_get_string_attr(rec, SDP_ATTR_SVCNAME_PRIMARY, str, len);
+}
+
+static inline int sdp_get_service_desc(const sdp_record_t *rec, char *str, int len)
+{
+	return sdp_get_string_attr(rec, SDP_ATTR_SVCDESC_PRIMARY, str, len);
+}
+
+static inline int sdp_get_provider_name(const sdp_record_t *rec, char *str, int len)
+{
+	return sdp_get_string_attr(rec, SDP_ATTR_PROVNAME_PRIMARY, str, len);
+}
+
+static inline int sdp_get_doc_url(const sdp_record_t *rec, char *str, int len)
+{
+	return sdp_get_string_attr(rec, SDP_ATTR_DOC_URL, str, len);
+}
+
+static inline int sdp_get_clnt_exec_url(const sdp_record_t *rec, char *str, int len)
+{
+	return sdp_get_string_attr(rec, SDP_ATTR_CLNT_EXEC_URL, str, len);
+}
+
+static inline int sdp_get_icon_url(const sdp_record_t *rec, char *str, int len)
+{
+	return sdp_get_string_attr(rec, SDP_ATTR_ICON_URL, str, len);
+}
+
+/*
+ * Set the supported features
+ * sf should be a list of list with each feature data
+ * Returns 0 on success -1 on fail
+ */
+int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf);
+
+/*
+ * Get the supported features
+ * seqp is set to a list of list with each feature data
+ * Returns 0 on success, if an error occurred -1 is returned and errno is set
+ */
+int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp);
+
+sdp_record_t *sdp_extract_pdu(const uint8_t *pdata, int bufsize, int *scanned);
+sdp_record_t *sdp_copy_record(sdp_record_t *rec);
+
+void sdp_data_print(sdp_data_t *data);
+void sdp_print_service_attr(sdp_list_t *alist);
+
+int sdp_attrid_comp_func(const void *key1, const void *key2);
+
+void sdp_set_seq_len(uint8_t *ptr, uint32_t length);
+void sdp_set_attrid(sdp_buf_t *pdu, uint16_t id);
+void sdp_append_to_pdu(sdp_buf_t *dst, sdp_data_t *d);
+void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len);
+
+int sdp_gen_pdu(sdp_buf_t *pdu, sdp_data_t *data);
+int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *pdu);
+
+int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size);
+
+sdp_data_t *sdp_extract_attr(const uint8_t *pdata, int bufsize, int *extractedLength, sdp_record_t *rec);
+
+void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid);
+void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq);
+
+int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize);
+
+void sdp_add_lang_attr(sdp_record_t *rec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SDP_LIB_H */
diff --git a/bluez/repo/lib/uuid.c b/bluez/repo/lib/uuid.c
new file mode 100644
index 0000000..20b67d0
--- /dev/null
+++ b/bluez/repo/lib/uuid.c
@@ -0,0 +1,309 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "uuid.h"
+
+static uint128_t bluetooth_base_uuid = {
+	.data = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
+};
+
+#define BASE_UUID16_OFFSET	2
+#define BASE_UUID32_OFFSET	0
+
+static void bt_uuid16_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+	uint16_t be16;
+
+	dst->value.u128 = bluetooth_base_uuid;
+	dst->type = BT_UUID128;
+
+	/*
+	 * No matter the system: 128-bit UUIDs should be stored
+	 * as big-endian. 16-bit UUIDs are stored on host order.
+	 */
+
+	be16 = htons(src->value.u16);
+	memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET], &be16, sizeof(be16));
+}
+
+static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+	uint32_t be32;
+
+	dst->value.u128 = bluetooth_base_uuid;
+	dst->type = BT_UUID128;
+
+	/*
+	 * No matter the system: 128-bit UUIDs should be stored
+	 * as big-endian. 32-bit UUIDs are stored on host order.
+	 */
+
+	be32 = htonl(src->value.u32);
+	memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET], &be32, sizeof(be32));
+}
+
+void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+	switch (src->type) {
+	case BT_UUID128:
+		*dst = *src;
+		break;
+	case BT_UUID32:
+		bt_uuid32_to_uuid128(src, dst);
+		break;
+	case BT_UUID16:
+		bt_uuid16_to_uuid128(src, dst);
+		break;
+	case BT_UUID_UNSPEC:
+	default:
+		break;
+	}
+}
+
+static int bt_uuid128_cmp(const bt_uuid_t *u1, const bt_uuid_t *u2)
+{
+	return memcmp(&u1->value.u128, &u2->value.u128, sizeof(uint128_t));
+}
+
+int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value)
+{
+	memset(btuuid, 0, sizeof(bt_uuid_t));
+	btuuid->type = BT_UUID16;
+	btuuid->value.u16 = value;
+
+	return 0;
+}
+
+int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value)
+{
+	memset(btuuid, 0, sizeof(bt_uuid_t));
+	btuuid->type = BT_UUID32;
+	btuuid->value.u32 = value;
+
+	return 0;
+}
+
+int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value)
+{
+	memset(btuuid, 0, sizeof(bt_uuid_t));
+	btuuid->type = BT_UUID128;
+	btuuid->value.u128 = value;
+
+	return 0;
+}
+
+int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2)
+{
+	bt_uuid_t u1, u2;
+
+	bt_uuid_to_uuid128(uuid1, &u1);
+	bt_uuid_to_uuid128(uuid2, &u2);
+
+	return bt_uuid128_cmp(&u1, &u2);
+}
+
+/*
+ * convert the UUID to string, copying a maximum of n characters.
+ */
+int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n)
+{
+	bt_uuid_t tmp;
+	unsigned int   data0;
+	unsigned short data1;
+	unsigned short data2;
+	unsigned short data3;
+	unsigned int   data4;
+	unsigned short data5;
+	const uint8_t *data;
+
+	if (!uuid || uuid->type == BT_UUID_UNSPEC) {
+		snprintf(str, n, "NULL");
+		return -EINVAL;
+	}
+
+	/* Convert to 128 Bit format */
+	bt_uuid_to_uuid128(uuid, &tmp);
+	data = (uint8_t *) &tmp.value.u128;
+
+	memcpy(&data0, &data[0], 4);
+	memcpy(&data1, &data[4], 2);
+	memcpy(&data2, &data[6], 2);
+	memcpy(&data3, &data[8], 2);
+	memcpy(&data4, &data[10], 4);
+	memcpy(&data5, &data[14], 2);
+
+	snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+				ntohl(data0), ntohs(data1),
+				ntohs(data2), ntohs(data3),
+				ntohl(data4), ntohs(data5));
+
+	return 0;
+}
+
+static inline int is_uuid128(const char *string)
+{
+	return (strlen(string) == 36 &&
+			string[8] == '-' &&
+			string[13] == '-' &&
+			string[18] == '-' &&
+			string[23] == '-');
+}
+
+static inline int is_base_uuid128(const char *string)
+{
+	uint16_t uuid;
+	char dummy;
+
+	if (!is_uuid128(string))
+		return 0;
+
+	return sscanf(string,
+		"0000%04hx-0000-1000-8000-00805%1[fF]9%1[bB]34%1[fF]%1[bB]",
+		&uuid, &dummy, &dummy, &dummy, &dummy) == 5;
+}
+
+static inline int is_uuid32(const char *string)
+{
+	return (strlen(string) == 8 || strlen(string) == 10);
+}
+
+static inline int is_uuid16(const char *string)
+{
+	return (strlen(string) == 4 || strlen(string) == 6);
+}
+
+static int bt_string_to_uuid16(bt_uuid_t *uuid, const char *string)
+{
+	uint16_t u16;
+	char *endptr = NULL;
+
+	u16 = strtol(string, &endptr, 16);
+	if (endptr && (*endptr == '\0' || *endptr == '-')) {
+		bt_uuid16_create(uuid, u16);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int bt_string_to_uuid32(bt_uuid_t *uuid, const char *string)
+{
+	uint32_t u32;
+	char *endptr = NULL;
+
+	u32 = strtol(string, &endptr, 16);
+	if (endptr && *endptr == '\0') {
+		bt_uuid32_create(uuid, u32);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string)
+{
+	uint32_t data0, data4;
+	uint16_t data1, data2, data3, data5;
+	uint128_t u128;
+	uint8_t *val = (uint8_t *) &u128;
+
+	if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+				&data0, &data1, &data2,
+				&data3, &data4, &data5) != 6)
+		return -EINVAL;
+
+	data0 = htonl(data0);
+	data1 = htons(data1);
+	data2 = htons(data2);
+	data3 = htons(data3);
+	data4 = htonl(data4);
+	data5 = htons(data5);
+
+	memcpy(&val[0], &data0, 4);
+	memcpy(&val[4], &data1, 2);
+	memcpy(&val[6], &data2, 2);
+	memcpy(&val[8], &data3, 2);
+	memcpy(&val[10], &data4, 4);
+	memcpy(&val[14], &data5, 2);
+
+	bt_uuid128_create(uuid, u128);
+
+	return 0;
+}
+
+int bt_string_to_uuid(bt_uuid_t *uuid, const char *string)
+{
+	if (is_base_uuid128(string))
+		return bt_string_to_uuid16(uuid, string + 4);
+	else if (is_uuid128(string))
+		return bt_string_to_uuid128(uuid, string);
+	else if (is_uuid32(string))
+		return bt_string_to_uuid32(uuid, string);
+	else if (is_uuid16(string))
+		return bt_string_to_uuid16(uuid, string);
+
+	return -EINVAL;
+}
+
+int bt_uuid_strcmp(const void *a, const void *b)
+{
+	bt_uuid_t u1, u2;
+
+	bt_string_to_uuid(&u1, a);
+	bt_string_to_uuid(&u2, b);
+
+	return bt_uuid_cmp(&u1, &u2);
+}
+
+int bt_uuid_to_le(const bt_uuid_t *src, void *dst)
+{
+	bt_uuid_t uuid;
+
+	switch (src->type) {
+	case BT_UUID16:
+		bt_put_le16(src->value.u16, dst);
+		return 0;
+	case BT_UUID32:
+		bt_uuid32_to_uuid128(src, &uuid);
+		src = &uuid;
+		/* Fallthrough */
+	case BT_UUID128:
+		/* Convert from 128-bit BE to LE */
+		bswap_128(&src->value.u128, dst);
+		return 0;
+	case BT_UUID_UNSPEC:
+	default:
+		return -EINVAL;
+	}
+}
diff --git a/bluez/repo/lib/uuid.h b/bluez/repo/lib/uuid.h
new file mode 100644
index 0000000..2dcfe9e
--- /dev/null
+++ b/bluez/repo/lib/uuid.h
@@ -0,0 +1,181 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_UUID_H
+#define __BLUETOOTH_UUID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define GENERIC_AUDIO_UUID	"00001203-0000-1000-8000-00805f9b34fb"
+
+#define HSP_HS_UUID		"00001108-0000-1000-8000-00805f9b34fb"
+#define HSP_AG_UUID		"00001112-0000-1000-8000-00805f9b34fb"
+
+#define HFP_HS_UUID		"0000111e-0000-1000-8000-00805f9b34fb"
+#define HFP_AG_UUID		"0000111f-0000-1000-8000-00805f9b34fb"
+
+#define ADVANCED_AUDIO_UUID	"0000110d-0000-1000-8000-00805f9b34fb"
+
+#define A2DP_SOURCE_UUID	"0000110a-0000-1000-8000-00805f9b34fb"
+#define A2DP_SINK_UUID		"0000110b-0000-1000-8000-00805f9b34fb"
+
+#define AVRCP_REMOTE_UUID	"0000110e-0000-1000-8000-00805f9b34fb"
+#define AVRCP_TARGET_UUID	"0000110c-0000-1000-8000-00805f9b34fb"
+
+#define PANU_UUID		"00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID		"00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID			"00001117-0000-1000-8000-00805f9b34fb"
+#define BNEP_SVC_UUID		"0000000f-0000-1000-8000-00805f9b34fb"
+
+#define PNPID_UUID		"00002a50-0000-1000-8000-00805f9b34fb"
+#define DEVICE_INFORMATION_UUID	"0000180a-0000-1000-8000-00805f9b34fb"
+
+#define GATT_UUID		"00001801-0000-1000-8000-00805f9b34fb"
+#define IMMEDIATE_ALERT_UUID	"00001802-0000-1000-8000-00805f9b34fb"
+#define LINK_LOSS_UUID		"00001803-0000-1000-8000-00805f9b34fb"
+#define TX_POWER_UUID		"00001804-0000-1000-8000-00805f9b34fb"
+#define BATTERY_UUID		"0000180f-0000-1000-8000-00805f9b34fb"
+#define SCAN_PARAMETERS_UUID	"00001813-0000-1000-8000-00805f9b34fb"
+
+#define SAP_UUID		"0000112D-0000-1000-8000-00805f9b34fb"
+
+#define HEART_RATE_UUID			"0000180d-0000-1000-8000-00805f9b34fb"
+#define HEART_RATE_MEASUREMENT_UUID	"00002a37-0000-1000-8000-00805f9b34fb"
+#define BODY_SENSOR_LOCATION_UUID	"00002a38-0000-1000-8000-00805f9b34fb"
+#define HEART_RATE_CONTROL_POINT_UUID	"00002a39-0000-1000-8000-00805f9b34fb"
+
+#define HEALTH_THERMOMETER_UUID		"00001809-0000-1000-8000-00805f9b34fb"
+#define TEMPERATURE_MEASUREMENT_UUID	"00002a1c-0000-1000-8000-00805f9b34fb"
+#define TEMPERATURE_TYPE_UUID		"00002a1d-0000-1000-8000-00805f9b34fb"
+#define INTERMEDIATE_TEMPERATURE_UUID	"00002a1e-0000-1000-8000-00805f9b34fb"
+#define MEASUREMENT_INTERVAL_UUID	"00002a21-0000-1000-8000-00805f9b34fb"
+
+#define CYCLING_SC_UUID		"00001816-0000-1000-8000-00805f9b34fb"
+#define CSC_MEASUREMENT_UUID	"00002a5b-0000-1000-8000-00805f9b34fb"
+#define CSC_FEATURE_UUID	"00002a5c-0000-1000-8000-00805f9b34fb"
+#define SENSOR_LOCATION_UUID	"00002a5d-0000-1000-8000-00805f9b34fb"
+#define SC_CONTROL_POINT_UUID	"00002a55-0000-1000-8000-00805f9b34fb"
+
+#define RFCOMM_UUID_STR		"00000003-0000-1000-8000-00805f9b34fb"
+
+#define HDP_UUID		"00001400-0000-1000-8000-00805f9b34fb"
+#define HDP_SOURCE_UUID		"00001401-0000-1000-8000-00805f9b34fb"
+#define HDP_SINK_UUID		"00001402-0000-1000-8000-00805f9b34fb"
+
+#define HID_UUID		"00001124-0000-1000-8000-00805f9b34fb"
+
+#define DUN_GW_UUID		"00001103-0000-1000-8000-00805f9b34fb"
+
+#define GAP_UUID		"00001800-0000-1000-8000-00805f9b34fb"
+#define PNP_UUID		"00001200-0000-1000-8000-00805f9b34fb"
+
+#define SPP_UUID		"00001101-0000-1000-8000-00805f9b34fb"
+
+#define OBEX_SYNC_UUID		"00001104-0000-1000-8000-00805f9b34fb"
+#define OBEX_OPP_UUID		"00001105-0000-1000-8000-00805f9b34fb"
+#define OBEX_FTP_UUID		"00001106-0000-1000-8000-00805f9b34fb"
+#define OBEX_PCE_UUID		"0000112e-0000-1000-8000-00805f9b34fb"
+#define OBEX_PSE_UUID		"0000112f-0000-1000-8000-00805f9b34fb"
+#define OBEX_PBAP_UUID		"00001130-0000-1000-8000-00805f9b34fb"
+#define OBEX_MAS_UUID		"00001132-0000-1000-8000-00805f9b34fb"
+#define OBEX_MNS_UUID		"00001133-0000-1000-8000-00805f9b34fb"
+#define OBEX_MAP_UUID		"00001134-0000-1000-8000-00805f9b34fb"
+
+/* GATT UUIDs section */
+#define GATT_PRIM_SVC_UUID				0x2800
+#define GATT_SND_SVC_UUID				0x2801
+#define GATT_INCLUDE_UUID				0x2802
+#define GATT_CHARAC_UUID				0x2803
+
+/* GATT Characteristic Types */
+#define GATT_CHARAC_DEVICE_NAME				0x2A00
+#define GATT_CHARAC_APPEARANCE				0x2A01
+#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG		0x2A02
+#define GATT_CHARAC_RECONNECTION_ADDRESS		0x2A03
+#define GATT_CHARAC_PERIPHERAL_PREF_CONN		0x2A04
+#define GATT_CHARAC_SERVICE_CHANGED			0x2A05
+#define GATT_CHARAC_SYSTEM_ID				0x2A23
+#define GATT_CHARAC_MODEL_NUMBER_STRING			0x2A24
+#define GATT_CHARAC_SERIAL_NUMBER_STRING		0x2A25
+#define GATT_CHARAC_FIRMWARE_REVISION_STRING		0x2A26
+#define GATT_CHARAC_HARDWARE_REVISION_STRING		0x2A27
+#define GATT_CHARAC_SOFTWARE_REVISION_STRING		0x2A28
+#define GATT_CHARAC_MANUFACTURER_NAME_STRING		0x2A29
+#define GATT_CHARAC_PNP_ID				0x2A50
+
+/* GATT Characteristic Descriptors */
+#define GATT_CHARAC_EXT_PROPER_UUID			0x2900
+#define GATT_CHARAC_USER_DESC_UUID			0x2901
+#define GATT_CLIENT_CHARAC_CFG_UUID			0x2902
+#define GATT_SERVER_CHARAC_CFG_UUID			0x2903
+#define GATT_CHARAC_FMT_UUID				0x2904
+#define GATT_CHARAC_AGREG_FMT_UUID			0x2905
+#define GATT_CHARAC_VALID_RANGE_UUID			0x2906
+#define GATT_EXTERNAL_REPORT_REFERENCE			0x2907
+#define GATT_REPORT_REFERENCE				0x2908
+
+typedef struct {
+	enum {
+		BT_UUID_UNSPEC = 0,
+		BT_UUID16 = 16,
+		BT_UUID32 = 32,
+		BT_UUID128 = 128,
+	} type;
+	union {
+		uint16_t  u16;
+		uint32_t  u32;
+		uint128_t u128;
+	} value;
+} bt_uuid_t;
+
+int bt_uuid_strcmp(const void *a, const void *b);
+
+int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value);
+int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value);
+int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value);
+
+int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2);
+void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst);
+
+#define MAX_LEN_UUID_STR 37
+
+int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n);
+int bt_string_to_uuid(bt_uuid_t *uuid, const char *string);
+
+int bt_uuid_to_le(const bt_uuid_t *uuid, void *dst);
+
+static inline int bt_uuid_len(const bt_uuid_t *uuid)
+{
+	return uuid->type / 8;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLUETOOTH_UUID_H */
diff --git a/bluez/repo/monitor/._a2dp.c b/bluez/repo/monitor/._a2dp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._a2dp.c
Binary files differ
diff --git a/bluez/repo/monitor/._a2dp.h b/bluez/repo/monitor/._a2dp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._a2dp.h
Binary files differ
diff --git a/bluez/repo/monitor/._analyze.c b/bluez/repo/monitor/._analyze.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._analyze.c
Binary files differ
diff --git a/bluez/repo/monitor/._analyze.h b/bluez/repo/monitor/._analyze.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._analyze.h
Binary files differ
diff --git a/bluez/repo/monitor/._avctp.c b/bluez/repo/monitor/._avctp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._avctp.c
Binary files differ
diff --git a/bluez/repo/monitor/._avctp.h b/bluez/repo/monitor/._avctp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._avctp.h
Binary files differ
diff --git a/bluez/repo/monitor/._avdtp.c b/bluez/repo/monitor/._avdtp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._avdtp.c
Binary files differ
diff --git a/bluez/repo/monitor/._avdtp.h b/bluez/repo/monitor/._avdtp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._avdtp.h
Binary files differ
diff --git a/bluez/repo/monitor/._bnep.c b/bluez/repo/monitor/._bnep.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._bnep.c
Binary files differ
diff --git a/bluez/repo/monitor/._bnep.h b/bluez/repo/monitor/._bnep.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._bnep.h
Binary files differ
diff --git a/bluez/repo/monitor/._broadcom.c b/bluez/repo/monitor/._broadcom.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._broadcom.c
Binary files differ
diff --git a/bluez/repo/monitor/._broadcom.h b/bluez/repo/monitor/._broadcom.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._broadcom.h
Binary files differ
diff --git a/bluez/repo/monitor/._bt.h b/bluez/repo/monitor/._bt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._bt.h
Binary files differ
diff --git a/bluez/repo/monitor/._control.c b/bluez/repo/monitor/._control.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._control.c
Binary files differ
diff --git a/bluez/repo/monitor/._control.h b/bluez/repo/monitor/._control.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._control.h
Binary files differ
diff --git a/bluez/repo/monitor/._crc.c b/bluez/repo/monitor/._crc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._crc.c
Binary files differ
diff --git a/bluez/repo/monitor/._crc.h b/bluez/repo/monitor/._crc.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._crc.h
Binary files differ
diff --git a/bluez/repo/monitor/._display.c b/bluez/repo/monitor/._display.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._display.c
Binary files differ
diff --git a/bluez/repo/monitor/._display.h b/bluez/repo/monitor/._display.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._display.h
Binary files differ
diff --git a/bluez/repo/monitor/._ellisys.c b/bluez/repo/monitor/._ellisys.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._ellisys.c
Binary files differ
diff --git a/bluez/repo/monitor/._ellisys.h b/bluez/repo/monitor/._ellisys.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._ellisys.h
Binary files differ
diff --git a/bluez/repo/monitor/._hcidump.c b/bluez/repo/monitor/._hcidump.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._hcidump.c
Binary files differ
diff --git a/bluez/repo/monitor/._hcidump.h b/bluez/repo/monitor/._hcidump.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._hcidump.h
Binary files differ
diff --git a/bluez/repo/monitor/._hwdb.c b/bluez/repo/monitor/._hwdb.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._hwdb.c
Binary files differ
diff --git a/bluez/repo/monitor/._hwdb.h b/bluez/repo/monitor/._hwdb.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._hwdb.h
Binary files differ
diff --git a/bluez/repo/monitor/._intel.c b/bluez/repo/monitor/._intel.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._intel.c
Binary files differ
diff --git a/bluez/repo/monitor/._intel.h b/bluez/repo/monitor/._intel.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._intel.h
Binary files differ
diff --git a/bluez/repo/monitor/._keys.c b/bluez/repo/monitor/._keys.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._keys.c
Binary files differ
diff --git a/bluez/repo/monitor/._keys.h b/bluez/repo/monitor/._keys.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._keys.h
Binary files differ
diff --git a/bluez/repo/monitor/._l2cap.c b/bluez/repo/monitor/._l2cap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._l2cap.c
Binary files differ
diff --git a/bluez/repo/monitor/._l2cap.h b/bluez/repo/monitor/._l2cap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._l2cap.h
Binary files differ
diff --git a/bluez/repo/monitor/._ll.c b/bluez/repo/monitor/._ll.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._ll.c
Binary files differ
diff --git a/bluez/repo/monitor/._ll.h b/bluez/repo/monitor/._ll.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._ll.h
Binary files differ
diff --git a/bluez/repo/monitor/._lmp.c b/bluez/repo/monitor/._lmp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._lmp.c
Binary files differ
diff --git a/bluez/repo/monitor/._lmp.h b/bluez/repo/monitor/._lmp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._lmp.h
Binary files differ
diff --git a/bluez/repo/monitor/._main.c b/bluez/repo/monitor/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._main.c
Binary files differ
diff --git a/bluez/repo/monitor/._packet.c b/bluez/repo/monitor/._packet.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._packet.c
Binary files differ
diff --git a/bluez/repo/monitor/._packet.h b/bluez/repo/monitor/._packet.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._packet.h
Binary files differ
diff --git a/bluez/repo/monitor/._rfcomm.c b/bluez/repo/monitor/._rfcomm.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._rfcomm.c
Binary files differ
diff --git a/bluez/repo/monitor/._rfcomm.h b/bluez/repo/monitor/._rfcomm.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._rfcomm.h
Binary files differ
diff --git a/bluez/repo/monitor/._sdp.c b/bluez/repo/monitor/._sdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._sdp.c
Binary files differ
diff --git a/bluez/repo/monitor/._sdp.h b/bluez/repo/monitor/._sdp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._sdp.h
Binary files differ
diff --git a/bluez/repo/monitor/._uuid.c b/bluez/repo/monitor/._uuid.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._uuid.c
Binary files differ
diff --git a/bluez/repo/monitor/._uuid.h b/bluez/repo/monitor/._uuid.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._uuid.h
Binary files differ
diff --git a/bluez/repo/monitor/._vendor.c b/bluez/repo/monitor/._vendor.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._vendor.c
Binary files differ
diff --git a/bluez/repo/monitor/._vendor.h b/bluez/repo/monitor/._vendor.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/monitor/._vendor.h
Binary files differ
diff --git a/bluez/repo/monitor/a2dp.c b/bluez/repo/monitor/a2dp.c
new file mode 100644
index 0000000..94f9758
--- /dev/null
+++ b/bluez/repo/monitor/a2dp.c
@@ -0,0 +1,638 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+ *
+ *
+ *  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 <string.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "bt.h"
+#include "packet.h"
+#include "display.h"
+#include "l2cap.h"
+#include "a2dp.h"
+
+#define BASE_INDENT	4
+
+/* Codec Types */
+#define A2DP_CODEC_SBC		0x00
+#define A2DP_CODEC_MPEG12	0x01
+#define A2DP_CODEC_MPEG24	0x02
+#define A2DP_CODEC_ATRAC	0x04
+#define A2DP_CODEC_VENDOR	0xff
+
+/* Vendor Specific A2DP Codecs */
+#define APTX_VENDOR_ID		0x0000004f
+#define APTX_CODEC_ID		0x0001
+#define LDAC_VENDOR_ID		0x0000012d
+#define LDAC_CODEC_ID		0x00aa
+
+struct bit_desc {
+	uint8_t bit_num;
+	const char *str;
+};
+
+static const struct bit_desc sbc_frequency_table[] = {
+	{  7, "16000" },
+	{  6, "32000" },
+	{  5, "44100" },
+	{  4, "48000" },
+	{ }
+};
+
+static const struct bit_desc sbc_channel_mode_table[] = {
+	{  3, "Mono" },
+	{  2, "Dual Channel" },
+	{  1, "Stereo" },
+	{  0, "Joint Stereo" },
+	{ }
+};
+
+static const struct bit_desc sbc_blocklen_table[] = {
+	{  7, "4" },
+	{  6, "8" },
+	{  5, "12" },
+	{  4, "16" },
+	{ }
+};
+
+static const struct bit_desc sbc_subbands_table[] = {
+	{  3, "4" },
+	{  2, "8" },
+	{ }
+};
+
+static const struct bit_desc sbc_allocation_table[] = {
+	{  1, "SNR" },
+	{  0, "Loudness" },
+	{ }
+};
+
+static const struct bit_desc mpeg12_layer_table[] = {
+	{  7, "Layer I (mp1)" },
+	{  6, "Layer II (mp2)" },
+	{  5, "Layer III (mp3)" },
+	{ }
+};
+
+static const struct bit_desc mpeg12_channel_mode_table[] = {
+	{  3, "Mono" },
+	{  2, "Dual Channel" },
+	{  1, "Stereo" },
+	{  0, "Joint Stereo" },
+	{ }
+};
+
+static const struct bit_desc mpeg12_frequency_table[] = {
+	{  5, "16000" },
+	{  4, "22050" },
+	{  3, "24000" },
+	{  2, "32000" },
+	{  1, "44100" },
+	{  0, "48000" },
+	{ }
+};
+
+static const struct bit_desc mpeg12_bitrate_table[] = {
+	{ 14, "1110" },
+	{ 13, "1101" },
+	{ 12, "1100" },
+	{ 11, "1011" },
+	{ 10, "1010" },
+	{  9, "1001" },
+	{  8, "1000" },
+	{  7, "0111" },
+	{  6, "0110" },
+	{  5, "0101" },
+	{  4, "0100" },
+	{  3, "0011" },
+	{  2, "0010" },
+	{  1, "0001" },
+	{  0, "0000" },
+	{ }
+};
+
+static const struct bit_desc aac_object_type_table[] = {
+	{  7, "MPEG-2 AAC LC" },
+	{  6, "MPEG-4 AAC LC" },
+	{  5, "MPEG-4 AAC LTP" },
+	{  4, "MPEG-4 AAC scalable" },
+	{  3, "RFA (b3)" },
+	{  2, "RFA (b2)" },
+	{  1, "RFA (b1)" },
+	{  0, "RFA (b0)" },
+	{ }
+};
+
+static const struct bit_desc aac_frequency_table[] = {
+	{ 15, "8000" },
+	{ 14, "11025" },
+	{ 13, "12000" },
+	{ 12, "16000" },
+	{ 11, "22050" },
+	{ 10, "24000" },
+	{  9, "32000" },
+	{  8, "44100" },
+	{  7, "48000" },
+	{  6, "64000" },
+	{  5, "88200" },
+	{  4, "96000" },
+	{ }
+};
+
+static const struct bit_desc aac_channels_table[] = {
+	{  3, "1" },
+	{  2, "2" },
+	{ }
+};
+
+static const struct bit_desc aptx_frequency_table[] = {
+	{  7, "16000" },
+	{  6, "32000" },
+	{  5, "44100" },
+	{  4, "48000" },
+	{ }
+};
+
+static const struct bit_desc aptx_channel_mode_table[] = {
+	{  0, "Mono" },
+	{  1, "Stereo" },
+	{ }
+};
+
+static void print_value_bits(uint8_t indent, uint32_t value,
+						const struct bit_desc *table)
+{
+	int i;
+
+	for (i = 0; table[i].str; i++) {
+		if (value & (1 << table[i].bit_num))
+			print_field("%*c%s", indent + 2, ' ', table[i].str);
+	}
+}
+
+static const char *find_value_bit(uint32_t value,
+						const struct bit_desc *table)
+{
+	int i;
+
+	for (i = 0; table[i].str; i++) {
+		if (value & (1 << table[i].bit_num))
+			return table[i].str;
+	}
+
+	return "Unknown";
+}
+
+static const char *vndcodec2str(uint32_t vendor_id, uint16_t codec_id)
+{
+	if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
+		return "aptX";
+	else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
+		return "LDAC";
+
+	return "Unknown";
+}
+
+static bool codec_sbc_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint8_t cap = 0;
+
+	if (losc != 4)
+		return false;
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cFrequency: 0x%02x", BASE_INDENT, ' ', cap & 0xf0);
+	print_value_bits(BASE_INDENT, cap & 0xf0, sbc_frequency_table);
+
+	print_field("%*cChannel Mode: 0x%02x", BASE_INDENT, ' ', cap & 0x0f);
+	print_value_bits(BASE_INDENT, cap & 0x0f, sbc_channel_mode_table);
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cBlock Length: 0x%02x", BASE_INDENT, ' ', cap & 0xf0);
+	print_value_bits(BASE_INDENT, cap & 0xf0, sbc_blocklen_table);
+
+	print_field("%*cSubbands: 0x%02x", BASE_INDENT, ' ', cap & 0x0c);
+	print_value_bits(BASE_INDENT, cap & 0x0c, sbc_subbands_table);
+
+	print_field("%*cAllocation Method: 0x%02x", BASE_INDENT, ' ',
+								cap & 0x03);
+	print_value_bits(BASE_INDENT, cap & 0x03, sbc_allocation_table);
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cMinimum Bitpool: %d", BASE_INDENT, ' ', cap);
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cMaximum Bitpool: %d", BASE_INDENT, ' ', cap);
+
+	return true;
+}
+
+static bool codec_sbc_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint8_t cap = 0;
+
+	if (losc != 4)
+		return false;
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(cap & 0xf0, sbc_frequency_table),
+			cap & 0xf0);
+
+	print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(cap & 0x0f, sbc_channel_mode_table),
+			cap & 0x0f);
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cBlock Length: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(cap & 0xf0, sbc_blocklen_table),
+			cap & 0xf0);
+
+	print_field("%*cSubbands: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(cap & 0x0c, sbc_subbands_table),
+			cap & 0x0c);
+
+	print_field("%*cAllocation Method: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(cap & 0x03, sbc_allocation_table),
+			cap & 0x03);
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cMinimum Bitpool: %d", BASE_INDENT, ' ', cap);
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cMaximum Bitpool: %d", BASE_INDENT, ' ', cap);
+
+	return true;
+}
+
+static bool codec_mpeg12_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint16_t cap = 0;
+	uint8_t layer;
+	uint8_t chan;
+	uint8_t freq;
+	uint16_t bitrate;
+	bool crc, mpf, vbr;
+
+	if (losc != 4)
+		return false;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	layer = (cap >> 8) & 0xe0;
+	crc = cap & 0x1000;
+	chan = (cap >> 8) & 0x0f;
+	mpf = cap & 0x0040;
+	freq = cap & 0x003f;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	vbr = cap & 0x8000;
+	bitrate = cap & 0x7fff;
+
+	print_field("%*cLayer: 0x%02x", BASE_INDENT, ' ', layer);
+	print_value_bits(BASE_INDENT, layer, mpeg12_layer_table);
+
+	print_field("%*cCRC: %s", BASE_INDENT, ' ', crc ? "Yes" : "No");
+
+	print_field("%*cChannel Mode: 0x%02x", BASE_INDENT, ' ', chan);
+	print_value_bits(BASE_INDENT, chan, mpeg12_channel_mode_table);
+
+	print_field("%*cMedia Payload Format: %s", BASE_INDENT, ' ',
+					mpf ? "RFC-2250 RFC-3119" : "RFC-2250");
+
+	print_field("%*cFrequency: 0x%02x", BASE_INDENT, ' ', freq);
+	print_value_bits(BASE_INDENT, freq, mpeg12_frequency_table);
+
+	if (!vbr) {
+		print_field("%*cBitrate Index: 0x%04x", BASE_INDENT, ' ',
+								bitrate);
+		print_value_bits(BASE_INDENT, freq, mpeg12_bitrate_table);
+	}
+
+	print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No");
+
+	return true;
+}
+
+static bool codec_mpeg12_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint16_t cap = 0;
+	uint8_t layer;
+	uint8_t chan;
+	uint8_t freq;
+	uint16_t bitrate;
+	bool crc, mpf, vbr;
+
+	if (losc != 4)
+		return false;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	layer = (cap >> 8) & 0xe0;
+	crc = cap & 0x1000;
+	chan = (cap >> 8) & 0x0f;
+	mpf = cap & 0x0040;
+	freq = cap & 0x003f;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	vbr = cap & 0x8000;
+	bitrate = cap & 0x7fff;
+
+	print_field("%*cLayer: %s (0x%02x)", BASE_INDENT, ' ',
+				find_value_bit(layer, mpeg12_layer_table),
+				layer);
+
+	print_field("%*cCRC: %s", BASE_INDENT, ' ', crc ? "Yes" : "No");
+
+	print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT, ' ',
+				find_value_bit(chan, mpeg12_channel_mode_table),
+				chan);
+
+	print_field("%*cMedia Payload Format: %s", BASE_INDENT, ' ',
+					mpf ? "RFC-2250 RFC-3119" : "RFC-2250");
+
+	print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT, ' ',
+				find_value_bit(freq, mpeg12_frequency_table),
+				freq);
+
+	if (!vbr)
+		print_field("%*cBitrate Index: %s (0x%04x)", BASE_INDENT, ' ',
+				find_value_bit(freq, mpeg12_bitrate_table),
+				bitrate);
+
+	print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No");
+
+	return true;
+}
+
+static bool codec_aac_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint16_t cap = 0;
+	uint8_t type;
+	uint16_t freq;
+	uint8_t chan;
+	uint32_t bitrate;
+	bool vbr;
+
+	if (losc != 6)
+		return false;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	type = cap >> 8;
+	freq = cap << 8;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	freq |= (cap >> 8) & 0xf0;
+	chan = (cap >> 8) & 0x0c;
+	bitrate = (cap << 16) & 0x7f0000;
+	vbr = cap & 0x0080;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	bitrate |= cap;
+
+	print_field("%*cObject Type: 0x%02x", BASE_INDENT, ' ', type);
+	print_value_bits(BASE_INDENT, type, aac_object_type_table);
+
+	print_field("%*cFrequency: 0x%02x", BASE_INDENT, ' ', freq);
+	print_value_bits(BASE_INDENT, freq, aac_frequency_table);
+
+	print_field("%*cChannels: 0x%02x", BASE_INDENT, ' ', chan);
+	print_value_bits(BASE_INDENT, chan, aac_channels_table);
+
+	print_field("%*cBitrate: %ubps", BASE_INDENT, ' ', bitrate);
+	print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No");
+
+	return true;
+}
+
+static bool codec_aac_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint16_t cap = 0;
+	uint8_t type;
+	uint16_t freq;
+	uint8_t chan;
+	uint32_t bitrate;
+	bool vbr;
+
+	if (losc != 6)
+		return false;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	type = cap >> 8;
+	freq = cap << 8;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	freq |= (cap >> 8) & 0xf0;
+	chan = (cap >> 8) & 0x0c;
+	bitrate = (cap << 16) & 0x7f0000;
+	vbr = cap & 0x0080;
+
+	l2cap_frame_get_be16(frame, &cap);
+
+	bitrate |= cap;
+
+	print_field("%*cObject Type: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(type, aac_object_type_table), type);
+
+	print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(freq, aac_frequency_table), freq);
+
+	print_field("%*cChannels: %s (0x%02x)", BASE_INDENT, ' ',
+			find_value_bit(chan, aac_channels_table), chan);
+
+	print_field("%*cBitrate: %ubps", BASE_INDENT, ' ', bitrate);
+	print_field("%*cVBR: %s", BASE_INDENT, ' ', vbr ? "Yes" : "No");
+
+	return true;
+}
+
+static bool codec_vendor_aptx_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint8_t cap = 0;
+
+	if (losc != 1)
+		return false;
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cFrequency: 0x%02x", BASE_INDENT + 2, ' ', cap & 0xf0);
+	print_value_bits(BASE_INDENT + 2, cap & 0xf0, aptx_frequency_table);
+
+	print_field("%*cChannel Mode: 0x%02x", BASE_INDENT + 2, ' ',
+								cap & 0x0f);
+	print_value_bits(BASE_INDENT + 2, cap & 0x0f, aptx_channel_mode_table);
+
+	return true;
+}
+
+static bool codec_vendor_ldac(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint16_t cap = 0;
+
+	if (losc != 2)
+		return false;
+
+	l2cap_frame_get_le16(frame, &cap);
+
+	print_field("%*cUnknown: 0x%04x", BASE_INDENT + 2, ' ', cap);
+
+	return true;
+}
+
+static bool codec_vendor_cap(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint32_t vendor_id = 0;
+	uint16_t codec_id = 0;
+
+	if (losc < 6)
+		return false;
+
+	l2cap_frame_get_le32(frame, &vendor_id);
+	l2cap_frame_get_le16(frame, &codec_id);
+
+	losc -= 6;
+
+	print_field("%*cVendor ID: %s (0x%08x)", BASE_INDENT, ' ',
+					bt_compidtostr(vendor_id),  vendor_id);
+
+	print_field("%*cVendor Specific Codec ID: %s (0x%04x)", BASE_INDENT,
+			' ', vndcodec2str(vendor_id, codec_id), codec_id);
+
+	if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
+		return codec_vendor_aptx_cap(losc, frame);
+	else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
+		return codec_vendor_ldac(losc, frame);
+
+	packet_hexdump(frame->data, losc);
+	l2cap_frame_pull(frame, frame, losc);
+
+	return true;
+}
+
+static bool codec_vendor_aptx_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint8_t cap = 0;
+
+	if (losc != 1)
+		return false;
+
+	l2cap_frame_get_u8(frame, &cap);
+
+	print_field("%*cFrequency: %s (0x%02x)", BASE_INDENT + 2, ' ',
+			find_value_bit(cap & 0xf0, aptx_frequency_table),
+			cap & 0xf0);
+
+	print_field("%*cChannel Mode: %s (0x%02x)", BASE_INDENT + 2, ' ',
+			find_value_bit(cap & 0x0f, aptx_channel_mode_table),
+			cap & 0x0f);
+
+	return true;
+}
+
+static bool codec_vendor_cfg(uint8_t losc, struct l2cap_frame *frame)
+{
+	uint32_t vendor_id = 0;
+	uint16_t codec_id = 0;
+
+	if (losc < 6)
+		return false;
+
+	l2cap_frame_get_le32(frame, &vendor_id);
+	l2cap_frame_get_le16(frame, &codec_id);
+
+	losc -= 6;
+
+	print_field("%*cVendor ID: %s (0x%08x)", BASE_INDENT, ' ',
+					bt_compidtostr(vendor_id),  vendor_id);
+
+	print_field("%*cVendor Specific Codec ID: %s (0x%04x)", BASE_INDENT,
+			' ', vndcodec2str(vendor_id, codec_id), codec_id);
+
+	if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
+		return codec_vendor_aptx_cfg(losc, frame);
+	else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
+		return codec_vendor_ldac(losc, frame);
+
+	packet_hexdump(frame->data, losc);
+	l2cap_frame_pull(frame, frame, losc);
+
+	return true;
+}
+
+bool a2dp_codec_cap(uint8_t codec, uint8_t losc, struct l2cap_frame *frame)
+{
+	switch (codec) {
+	case A2DP_CODEC_SBC:
+		return codec_sbc_cap(losc, frame);
+	case A2DP_CODEC_MPEG12:
+		return codec_mpeg12_cap(losc, frame);
+	case A2DP_CODEC_MPEG24:
+		return codec_aac_cap(losc, frame);
+	case A2DP_CODEC_VENDOR:
+		return codec_vendor_cap(losc, frame);
+	default:
+		packet_hexdump(frame->data, losc);
+		l2cap_frame_pull(frame, frame, losc);
+		return true;
+	}
+}
+
+bool a2dp_codec_cfg(uint8_t codec, uint8_t losc, struct l2cap_frame *frame)
+{
+	switch (codec) {
+	case A2DP_CODEC_SBC:
+		return codec_sbc_cfg(losc, frame);
+	case A2DP_CODEC_MPEG12:
+		return codec_mpeg12_cfg(losc, frame);
+	case A2DP_CODEC_MPEG24:
+		return codec_aac_cfg(losc, frame);
+	case A2DP_CODEC_VENDOR:
+		return codec_vendor_cfg(losc, frame);
+	default:
+		packet_hexdump(frame->data, losc);
+		l2cap_frame_pull(frame, frame, losc);
+		return true;
+	}
+}
diff --git a/bluez/repo/monitor/a2dp.h b/bluez/repo/monitor/a2dp.h
new file mode 100644
index 0000000..72a8f1f
--- /dev/null
+++ b/bluez/repo/monitor/a2dp.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+ *
+ *
+ *  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 a2dp_codec_cap(uint8_t codec, uint8_t losc, struct l2cap_frame *frame);
+
+bool a2dp_codec_cfg(uint8_t codec, uint8_t losc, struct l2cap_frame *frame);
diff --git a/bluez/repo/monitor/analyze.c b/bluez/repo/monitor/analyze.c
new file mode 100644
index 0000000..0f2d19a
--- /dev/null
+++ b/bluez/repo/monitor/analyze.c
@@ -0,0 +1,329 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/btsnoop.h"
+#include "monitor/bt.h"
+#include "analyze.h"
+
+struct hci_dev {
+	uint16_t index;
+	uint8_t type;
+	uint8_t bdaddr[6];
+	struct timeval time_added;
+	struct timeval time_removed;
+	unsigned long num_cmd;
+	unsigned long num_evt;
+	unsigned long num_acl;
+	unsigned long num_sco;
+};
+
+static struct queue *dev_list;
+
+static void dev_destroy(void *data)
+{
+	struct hci_dev *dev = data;
+	const char *str;
+
+	switch (dev->type) {
+	case 0x00:
+		str = "BR/EDR";
+		break;
+	case 0x01:
+		str = "AMP";
+		break;
+	default:
+		str = "unknown";
+		break;
+	}
+
+	printf("Found %s controller with index %u\n", str, dev->index);
+	printf("  BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+			dev->bdaddr[5], dev->bdaddr[4], dev->bdaddr[3],
+			dev->bdaddr[2], dev->bdaddr[1], dev->bdaddr[0]);
+	printf("  %lu commands\n", dev->num_cmd);
+	printf("  %lu events\n", dev->num_evt);
+	printf("  %lu ACL packets\n", dev->num_acl);
+	printf("  %lu SCO packets\n", dev->num_sco);
+	printf("\n");
+
+	free(dev);
+}
+
+static struct hci_dev *dev_alloc(uint16_t index)
+{
+	struct hci_dev *dev;
+
+	dev = new0(struct hci_dev, 1);
+	if (!dev) {
+		fprintf(stderr, "Failed to allocate new device entry\n");
+		return NULL;
+	}
+
+	dev->index = index;
+
+	return dev;
+}
+
+static bool dev_match_index(const void *a, const void *b)
+{
+	const struct hci_dev *dev = a;
+	uint16_t index = PTR_TO_UINT(b);
+
+	return dev->index == index;
+}
+
+static struct hci_dev *dev_lookup(uint16_t index)
+{
+	struct hci_dev *dev;
+
+	dev = queue_find(dev_list, dev_match_index, UINT_TO_PTR(index));
+	if (!dev) {
+		fprintf(stderr, "Creating new device for unknown index\n");
+
+		dev = dev_alloc(index);
+		if (!dev)
+			return NULL;
+
+		queue_push_tail(dev_list, dev);
+	}
+
+	return dev;
+}
+
+static void new_index(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const struct btsnoop_opcode_new_index *ni = data;
+	struct hci_dev *dev;
+
+	dev = dev_alloc(index);
+	if (!dev)
+		return;
+
+	dev->type = ni->type;
+	memcpy(dev->bdaddr, ni->bdaddr, 6);
+
+	queue_push_tail(dev_list, dev);
+}
+
+static void del_index(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	struct hci_dev *dev;
+
+	dev = queue_remove_if(dev_list, dev_match_index, UINT_TO_PTR(index));
+	if (!dev) {
+		fprintf(stderr, "Remove for an unexisting device\n");
+		return;
+	}
+
+	dev_destroy(dev);
+}
+
+static void command_pkt(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const struct bt_hci_cmd_hdr *hdr = data;
+	struct hci_dev *dev;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->num_cmd++;
+}
+
+static void rsp_read_bd_addr(struct hci_dev *dev, struct timeval *tv,
+					const void *data, uint16_t size)
+{
+	const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+	printf("Read BD Addr event with status 0x%2.2x\n", rsp->status);
+
+	if (rsp->status)
+		return;
+
+	memcpy(dev->bdaddr, rsp->bdaddr, 6);
+}
+
+static void evt_cmd_complete(struct hci_dev *dev, struct timeval *tv,
+					const void *data, uint16_t size)
+{
+	const struct bt_hci_evt_cmd_complete *evt = data;
+	uint16_t opcode;
+
+	data += sizeof(*evt);
+	size -= sizeof(*evt);
+
+	opcode = le16_to_cpu(evt->opcode);
+
+	switch (opcode) {
+	case BT_HCI_CMD_READ_BD_ADDR:
+		rsp_read_bd_addr(dev, tv, data, size);
+		break;
+	}
+}
+
+static void event_pkt(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const struct bt_hci_evt_hdr *hdr = data;
+	struct hci_dev *dev;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->num_evt++;
+
+	switch (hdr->evt) {
+	case BT_HCI_EVT_CMD_COMPLETE:
+		evt_cmd_complete(dev, tv, data, size);
+		break;
+	}
+}
+
+static void acl_pkt(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const struct bt_hci_acl_hdr *hdr = data;
+	struct hci_dev *dev;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->num_acl++;
+}
+
+static void sco_pkt(struct timeval *tv, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const struct bt_hci_sco_hdr *hdr = data;
+	struct hci_dev *dev;
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	dev = dev_lookup(index);
+	if (!dev)
+		return;
+
+	dev->num_sco++;
+}
+
+void analyze_trace(const char *path)
+{
+	struct btsnoop *btsnoop_file;
+	unsigned long num_packets = 0;
+	uint32_t format;
+
+	btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+	if (!btsnoop_file)
+		return;
+
+	format = btsnoop_get_format(btsnoop_file);
+
+	switch (format) {
+	case BTSNOOP_FORMAT_HCI:
+	case BTSNOOP_FORMAT_UART:
+	case BTSNOOP_FORMAT_MONITOR:
+		break;
+	default:
+		fprintf(stderr, "Unsupported packet format\n");
+		goto done;
+	}
+
+	dev_list = queue_new();
+	if (!dev_list) {
+		fprintf(stderr, "Failed to allocate device list\n");
+		goto done;
+	}
+
+	while (1) {
+		unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+		struct timeval tv;
+		uint16_t index, opcode, pktlen;
+
+		if (!btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode,
+								buf, &pktlen))
+			break;
+
+		switch (opcode) {
+		case BTSNOOP_OPCODE_NEW_INDEX:
+			new_index(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_DEL_INDEX:
+			del_index(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_COMMAND_PKT:
+			command_pkt(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_EVENT_PKT:
+			event_pkt(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_ACL_TX_PKT:
+		case BTSNOOP_OPCODE_ACL_RX_PKT:
+			acl_pkt(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_SCO_TX_PKT:
+		case BTSNOOP_OPCODE_SCO_RX_PKT:
+			sco_pkt(&tv, index, buf, pktlen);
+			break;
+		case BTSNOOP_OPCODE_OPEN_INDEX:
+		case BTSNOOP_OPCODE_CLOSE_INDEX:
+			break;
+		default:
+			fprintf(stderr, "Wrong opcode %u\n", opcode);
+			goto done;
+		}
+
+		num_packets++;
+	}
+
+	printf("Trace contains %lu packets\n\n", num_packets);
+
+	queue_destroy(dev_list, dev_destroy);
+
+done:
+	btsnoop_unref(btsnoop_file);
+}
diff --git a/bluez/repo/monitor/analyze.h b/bluez/repo/monitor/analyze.h
new file mode 100644
index 0000000..c643d35
--- /dev/null
+++ b/bluez/repo/monitor/analyze.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+void analyze_trace(const char *path);
diff --git a/bluez/repo/monitor/avctp.c b/bluez/repo/monitor/avctp.c
new file mode 100644
index 0000000..a024a0f
--- /dev/null
+++ b/bluez/repo/monitor/avctp.c
@@ -0,0 +1,2547 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "lib/bluetooth.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"
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL		0x0
+#define AVC_CTYPE_STATUS		0x1
+#define AVC_CTYPE_SPECIFIC_INQUIRY	0x2
+#define AVC_CTYPE_NOTIFY		0x3
+#define AVC_CTYPE_GENERAL_INQUIRY	0x4
+#define AVC_CTYPE_NOT_IMPLEMENTED	0x8
+#define AVC_CTYPE_ACCEPTED		0x9
+#define AVC_CTYPE_REJECTED		0xA
+#define AVC_CTYPE_IN_TRANSITION		0xB
+#define AVC_CTYPE_STABLE		0xC
+#define AVC_CTYPE_CHANGED		0xD
+#define AVC_CTYPE_INTERIM		0xF
+
+/* subunit type */
+#define AVC_SUBUNIT_MONITOR		0x00
+#define AVC_SUBUNIT_AUDIO		0x01
+#define AVC_SUBUNIT_PRINTER		0x02
+#define AVC_SUBUNIT_DISC		0x03
+#define AVC_SUBUNIT_TAPE		0x04
+#define AVC_SUBUNIT_TUNER		0x05
+#define AVC_SUBUNIT_CA			0x06
+#define AVC_SUBUNIT_CAMERA		0x07
+#define AVC_SUBUNIT_PANEL		0x09
+#define AVC_SUBUNIT_BULLETIN_BOARD	0x0a
+#define AVC_SUBUNIT_CAMERA_STORAGE	0x0b
+#define AVC_SUBUNIT_VENDOR_UNIQUE	0x0c
+#define AVC_SUBUNIT_EXTENDED		0x1e
+#define AVC_SUBUNIT_UNIT		0x1f
+
+/* opcodes */
+#define AVC_OP_VENDORDEP		0x00
+#define AVC_OP_UNITINFO			0x30
+#define AVC_OP_SUBUNITINFO		0x31
+#define AVC_OP_PASSTHROUGH		0x7c
+
+/* notification events */
+#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED			0x02
+#define AVRCP_EVENT_TRACK_REACHED_END			0x03
+#define AVRCP_EVENT_TRACK_REACHED_START			0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED		0x05
+#define AVRCP_EVENT_BATT_STATUS_CHANGED			0x06
+#define AVRCP_EVENT_SYSTEM_STATUS_CHANGED		0x07
+#define AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED	0x08
+#define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED		0x09
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED		0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED		0x0b
+#define AVRCP_EVENT_UIDS_CHANGED			0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED			0x0d
+
+/* error statuses */
+#define AVRCP_STATUS_INVALID_COMMAND			0x00
+#define AVRCP_STATUS_INVALID_PARAMETER			0x01
+#define AVRCP_STATUS_NOT_FOUND				0x02
+#define AVRCP_STATUS_INTERNAL_ERROR			0x03
+#define AVRCP_STATUS_SUCCESS				0x04
+#define AVRCP_STATUS_UID_CHANGED			0x05
+#define AVRCP_STATUS_INVALID_DIRECTION			0x07
+#define AVRCP_STATUS_NOT_DIRECTORY			0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST			0x09
+#define AVRCP_STATUS_INVALID_SCOPE			0x0a
+#define AVRCP_STATUS_OUT_OF_BOUNDS			0x0b
+#define AVRCP_STATUS_IS_DIRECTORY			0x0c
+#define AVRCP_STATUS_MEDIA_IN_USE			0x0d
+#define AVRCP_STATUS_NOW_PLAYING_LIST_FULL		0x0e
+#define AVRCP_STATUS_SEARCH_NOT_SUPPORTED		0x0f
+#define AVRCP_STATUS_SEARCH_IN_PROGRESS			0x10
+#define AVRCP_STATUS_INVALID_PLAYER_ID			0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE		0x12
+#define AVRCP_STATUS_PLAYER_NOT_ADDRESSED		0x13
+#define AVRCP_STATUS_NO_VALID_SEARCH_RESULTS		0x14
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS		0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED		0x16
+
+/* pdu ids */
+#define AVRCP_GET_CAPABILITIES		0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0x11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
+#define AVRCP_SET_PLAYER_VALUE		0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
+#define AVRCP_DISPLAYABLE_CHARSET	0x17
+#define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+#define AVRCP_REQUEST_CONTINUING	0x40
+#define AVRCP_ABORT_CONTINUING		0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_ADDRESSED_PLAYER	0x60
+#define AVRCP_SET_BROWSED_PLAYER	0x70
+#define AVRCP_GET_FOLDER_ITEMS		0x71
+#define AVRCP_CHANGE_PATH		0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES	0x73
+#define AVRCP_PLAY_ITEM			0x74
+#define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS	0x75
+#define AVRCP_SEARCH			0x80
+#define AVRCP_ADD_TO_NOW_PLAYING	0x90
+#define AVRCP_GENERAL_REJECT		0xA0
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE	0x00
+#define AVRCP_PACKET_TYPE_START		0x01
+#define AVRCP_PACKET_TYPE_CONTINUING	0x02
+#define AVRCP_PACKET_TYPE_END		0x03
+
+/* player attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL		0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER	0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE	0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE		0x03
+#define AVRCP_ATTRIBUTE_SCAN		0x04
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL	0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE	0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST	0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM	0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK	0x04
+#define AVRCP_MEDIA_ATTRIBUTE_TOTAL	0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE	0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION	0x07
+
+/* play status */
+#define AVRCP_PLAY_STATUS_STOPPED	0x00
+#define AVRCP_PLAY_STATUS_PLAYING	0x01
+#define AVRCP_PLAY_STATUS_PAUSED	0x02
+#define AVRCP_PLAY_STATUS_FWD_SEEK	0x03
+#define AVRCP_PLAY_STATUS_REV_SEEK	0x04
+#define AVRCP_PLAY_STATUS_ERROR		0xFF
+
+/* media scope */
+#define AVRCP_MEDIA_PLAYER_LIST		0x00
+#define AVRCP_MEDIA_PLAYER_VFS		0x01
+#define AVRCP_MEDIA_SEARCH		0x02
+#define AVRCP_MEDIA_NOW_PLAYING		0x03
+
+/* Media Item Type */
+#define AVRCP_MEDIA_PLAYER_ITEM_TYPE	0x01
+#define AVRCP_FOLDER_ITEM_TYPE			0x02
+#define AVRCP_MEDIA_ELEMENT_ITEM_TYPE	0x03
+
+/* operands in passthrough commands */
+#define AVC_PANEL_VOLUME_UP		0x41
+#define AVC_PANEL_VOLUME_DOWN		0x42
+#define AVC_PANEL_MUTE			0x43
+#define AVC_PANEL_PLAY			0x44
+#define AVC_PANEL_STOP			0x45
+#define AVC_PANEL_PAUSE			0x46
+#define AVC_PANEL_RECORD		0x47
+#define AVC_PANEL_REWIND		0x48
+#define AVC_PANEL_FAST_FORWARD		0x49
+#define AVC_PANEL_EJECT			0x4a
+#define AVC_PANEL_FORWARD		0x4b
+#define AVC_PANEL_BACKWARD		0x4c
+
+struct avctp_frame {
+	uint8_t hdr;
+	uint8_t pt;
+	uint16_t pid;
+	struct l2cap_frame l2cap_frame;
+};
+
+static struct avrcp_continuing {
+	uint16_t num;
+	uint16_t size;
+} avrcp_continuing;
+
+static const char *ctype2str(uint8_t ctype)
+{
+	switch (ctype & 0x0f) {
+	case AVC_CTYPE_CONTROL:
+		return "Control";
+	case AVC_CTYPE_STATUS:
+		return "Status";
+	case AVC_CTYPE_SPECIFIC_INQUIRY:
+		return "Specific Inquiry";
+	case AVC_CTYPE_NOTIFY:
+		return "Notify";
+	case AVC_CTYPE_GENERAL_INQUIRY:
+		return "General Inquiry";
+	case AVC_CTYPE_NOT_IMPLEMENTED:
+		return "Not Implemented";
+	case AVC_CTYPE_ACCEPTED:
+		return "Accepted";
+	case AVC_CTYPE_REJECTED:
+		return "Rejected";
+	case AVC_CTYPE_IN_TRANSITION:
+		return "In Transition";
+	case AVC_CTYPE_STABLE:
+		return "Stable";
+	case AVC_CTYPE_CHANGED:
+		return "Changed";
+	case AVC_CTYPE_INTERIM:
+		return "Interim";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *subunit2str(uint8_t subunit)
+{
+	switch (subunit) {
+	case AVC_SUBUNIT_MONITOR:
+		return "Monitor";
+	case AVC_SUBUNIT_AUDIO:
+		return "Audio";
+	case AVC_SUBUNIT_PRINTER:
+		return "Printer";
+	case AVC_SUBUNIT_DISC:
+		return "Disc";
+	case AVC_SUBUNIT_TAPE:
+		return "Tape";
+	case AVC_SUBUNIT_TUNER:
+		return "Tuner";
+	case AVC_SUBUNIT_CA:
+		return "CA";
+	case AVC_SUBUNIT_CAMERA:
+		return "Camera";
+	case AVC_SUBUNIT_PANEL:
+		return "Panel";
+	case AVC_SUBUNIT_BULLETIN_BOARD:
+		return "Bulletin Board";
+	case AVC_SUBUNIT_CAMERA_STORAGE:
+		return "Camera Storage";
+	case AVC_SUBUNIT_VENDOR_UNIQUE:
+		return "Vendor Unique";
+	case AVC_SUBUNIT_EXTENDED:
+		return "Extended to next byte";
+	case AVC_SUBUNIT_UNIT:
+		return "Unit";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *opcode2str(uint8_t opcode)
+{
+	switch (opcode) {
+	case AVC_OP_VENDORDEP:
+		return "Vendor Dependent";
+	case AVC_OP_UNITINFO:
+		return "Unit Info";
+	case AVC_OP_SUBUNITINFO:
+		return "Subunit Info";
+	case AVC_OP_PASSTHROUGH:
+		return "Passthrough";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *cap2str(uint8_t cap)
+{
+	switch (cap) {
+	case 0x2:
+		return "CompanyID";
+	case 0x3:
+		return "EventsID";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *event2str(uint8_t event)
+{
+	switch (event) {
+	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+		return "EVENT_PLAYBACK_STATUS_CHANGED";
+	case AVRCP_EVENT_TRACK_CHANGED:
+		return "EVENT_TRACK_CHANGED";
+	case AVRCP_EVENT_TRACK_REACHED_END:
+		return "EVENT_TRACK_REACHED_END";
+	case AVRCP_EVENT_TRACK_REACHED_START:
+		return "EVENT_TRACK_REACHED_START";
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		return "EVENT_PLAYBACK_POS_CHANGED";
+	case AVRCP_EVENT_BATT_STATUS_CHANGED:
+		return "EVENT_BATT_STATUS_CHANGED";
+	case AVRCP_EVENT_SYSTEM_STATUS_CHANGED:
+		return "EVENT_SYSTEM_STATUS_CHANGED";
+	case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
+		return "EVENT_PLAYER_APPLICATION_SETTING_CHANGED";
+	case AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED:
+		return "EVENT_NOW_PLAYING_CONTENT_CHANGED";
+	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		return "EVENT_AVAILABLE_PLAYERS_CHANGED";
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		return "EVENT_ADDRESSED_PLAYER_CHANGED";
+	case AVRCP_EVENT_UIDS_CHANGED:
+		return "EVENT_UIDS_CHANGED";
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		return "EVENT_VOLUME_CHANGED";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *error2str(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_STATUS_INVALID_COMMAND:
+		return "Invalid Command";
+	case AVRCP_STATUS_INVALID_PARAMETER:
+		return "Invalid Parameter";
+	case AVRCP_STATUS_NOT_FOUND:
+		return "Not Found";
+	case AVRCP_STATUS_INTERNAL_ERROR:
+		return "Internal Error";
+	case AVRCP_STATUS_SUCCESS:
+		return "Success";
+	case AVRCP_STATUS_UID_CHANGED:
+		return "UID Changed";
+	case AVRCP_STATUS_INVALID_DIRECTION:
+		return "Invalid Direction";
+	case AVRCP_STATUS_NOT_DIRECTORY:
+		return "Not a Directory";
+	case AVRCP_STATUS_DOES_NOT_EXIST:
+		return "Does Not Exist";
+	case AVRCP_STATUS_INVALID_SCOPE:
+		return "Invalid Scope";
+	case AVRCP_STATUS_OUT_OF_BOUNDS:
+		return "Range Out of Bounds";
+	case AVRCP_STATUS_MEDIA_IN_USE:
+		return "Media in Use";
+	case AVRCP_STATUS_IS_DIRECTORY:
+		return "UID is a Directory";
+	case AVRCP_STATUS_NOW_PLAYING_LIST_FULL:
+		return "Now Playing List Full";
+	case AVRCP_STATUS_SEARCH_NOT_SUPPORTED:
+		return "Search Not Supported";
+	case AVRCP_STATUS_SEARCH_IN_PROGRESS:
+		return "Search in Progress";
+	case AVRCP_STATUS_INVALID_PLAYER_ID:
+		return "Invalid Player ID";
+	case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
+		return "Player Not Browsable";
+	case AVRCP_STATUS_PLAYER_NOT_ADDRESSED:
+		return "Player Not Addressed";
+	case AVRCP_STATUS_NO_VALID_SEARCH_RESULTS:
+		return "No Valid Search Result";
+	case AVRCP_STATUS_NO_AVAILABLE_PLAYERS:
+		return "No Available Players";
+	case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED:
+		return "Addressed Player Changed";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *pdu2str(uint8_t pduid)
+{
+	switch (pduid) {
+	case AVRCP_GET_CAPABILITIES:
+		return "GetCapabilities";
+	case AVRCP_LIST_PLAYER_ATTRIBUTES:
+		return "ListPlayerApplicationSettingAttributes";
+	case AVRCP_LIST_PLAYER_VALUES:
+		return "ListPlayerApplicationSettingValues";
+	case AVRCP_GET_CURRENT_PLAYER_VALUE:
+		return "GetCurrentPlayerApplicationSettingValue";
+	case AVRCP_SET_PLAYER_VALUE:
+		return "SetPlayerApplicationSettingValue";
+	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
+		return "GetPlayerApplicationSettingAttributeText";
+	case AVRCP_GET_PLAYER_VALUE_TEXT:
+		return "GetPlayerApplicationSettingValueText";
+	case AVRCP_DISPLAYABLE_CHARSET:
+		return "InformDisplayableCharacterSet";
+	case AVRCP_CT_BATTERY_STATUS:
+		return "InformBatteryStatusOfCT";
+	case AVRCP_GET_ELEMENT_ATTRIBUTES:
+		return "GetElementAttributes";
+	case AVRCP_GET_PLAY_STATUS:
+		return "GetPlayStatus";
+	case AVRCP_REGISTER_NOTIFICATION:
+		return "RegisterNotification";
+	case AVRCP_REQUEST_CONTINUING:
+		return "RequestContinuingResponse";
+	case AVRCP_ABORT_CONTINUING:
+		return "AbortContinuingResponse";
+	case AVRCP_SET_ABSOLUTE_VOLUME:
+		return "SetAbsoluteVolume";
+	case AVRCP_SET_ADDRESSED_PLAYER:
+		return "SetAddressedPlayer";
+	case AVRCP_SET_BROWSED_PLAYER:
+		return "SetBrowsedPlayer";
+	case AVRCP_GET_FOLDER_ITEMS:
+		return "GetFolderItems";
+	case AVRCP_CHANGE_PATH:
+		return "ChangePath";
+	case AVRCP_GET_ITEM_ATTRIBUTES:
+		return "GetItemAttributes";
+	case AVRCP_PLAY_ITEM:
+		return "PlayItem";
+	case AVRCP_GET_TOTAL_NUMBER_OF_ITEMS:
+		return "GetTotalNumOfItems";
+	case AVRCP_SEARCH:
+		return "Search";
+	case AVRCP_ADD_TO_NOW_PLAYING:
+		return "AddToNowPlaying";
+	case AVRCP_GENERAL_REJECT:
+		return "GeneralReject";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *pt2str(uint8_t pt)
+{
+	switch (pt) {
+	case AVRCP_PACKET_TYPE_SINGLE:
+		return "Single";
+	case AVRCP_PACKET_TYPE_START:
+		return "Start";
+	case AVRCP_PACKET_TYPE_CONTINUING:
+		return "Continuing";
+	case AVRCP_PACKET_TYPE_END:
+		return "End";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *attr2str(uint8_t attr)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_ILEGAL:
+		return "Illegal";
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		return "Equalizer ON/OFF Status";
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		return "Repeat Mode Status";
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		return "Shuffle ON/OFF Status";
+	case AVRCP_ATTRIBUTE_SCAN:
+		return "Scan ON/OFF Status";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *value2str(uint8_t attr, uint8_t value)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_ILEGAL:
+		return "Illegal";
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "ON";
+		default:
+			return "Reserved";
+		}
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "Single Track Repeat";
+		case 0x03:
+			return "All Track Repeat";
+		case 0x04:
+			return "Group Repeat";
+		default:
+			return "Reserved";
+		}
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "All Track Shuffle";
+		case 0x03:
+			return "Group Shuffle";
+		default:
+			return "Reserved";
+		}
+	case AVRCP_ATTRIBUTE_SCAN:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "All Track Scan";
+		case 0x03:
+			return "Group Scan";
+		default:
+			return "Reserved";
+		}
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *charset2str(uint16_t charset)
+{
+	switch (charset) {
+	case 1:
+	case 2:
+		return "Reserved";
+	case 3:
+		return "ASCII";
+	case 4:
+		return "ISO_8859-1";
+	case 5:
+		return "ISO_8859-2";
+	case 6:
+		return "ISO_8859-3";
+	case 7:
+		return "ISO_8859-4";
+	case 8:
+		return "ISO_8859-5";
+	case 9:
+		return "ISO_8859-6";
+	case 10:
+		return "ISO_8859-7";
+	case 11:
+		return "ISO_8859-8";
+	case 12:
+		return "ISO_8859-9";
+	case 106:
+		return "UTF-8";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *mediattr2str(uint32_t attr)
+{
+	switch (attr) {
+	case AVRCP_MEDIA_ATTRIBUTE_ILLEGAL:
+		return "Illegal";
+	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+		return "Title";
+	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+		return "Artist";
+	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+		return "Album";
+	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+		return "Track";
+	case AVRCP_MEDIA_ATTRIBUTE_TOTAL:
+		return "Track Total";
+	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+		return "Genre";
+	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+		return "Track duration";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *playstatus2str(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_PLAY_STATUS_STOPPED:
+		return "STOPPED";
+	case AVRCP_PLAY_STATUS_PLAYING:
+		return "PLAYING";
+	case AVRCP_PLAY_STATUS_PAUSED:
+		return "PAUSED";
+	case AVRCP_PLAY_STATUS_FWD_SEEK:
+		return "FWD_SEEK";
+	case AVRCP_PLAY_STATUS_REV_SEEK:
+		return "REV_SEEK";
+	case AVRCP_PLAY_STATUS_ERROR:
+		return "ERROR";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *status2str(uint8_t status)
+{
+	switch (status) {
+	case 0x0:
+		return "NORMAL";
+	case 0x1:
+		return "WARNING";
+	case 0x2:
+		return "CRITICAL";
+	case 0x3:
+		return "EXTERNAL";
+	case 0x4:
+		return "FULL_CHARGE";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *scope2str(uint8_t scope)
+{
+	switch (scope) {
+	case AVRCP_MEDIA_PLAYER_LIST:
+		return "Media Player List";
+	case AVRCP_MEDIA_PLAYER_VFS:
+		return "Media Player Virtual Filesystem";
+	case AVRCP_MEDIA_SEARCH:
+		return "Search";
+	case AVRCP_MEDIA_NOW_PLAYING:
+		return "Now Playing";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *op2str(uint8_t op)
+{
+	switch (op & 0x7f) {
+	case AVC_PANEL_VOLUME_UP:
+		return "VOLUME UP";
+	case AVC_PANEL_VOLUME_DOWN:
+		return "VOLUME DOWN";
+	case AVC_PANEL_MUTE:
+		return "MUTE";
+	case AVC_PANEL_PLAY:
+		return "PLAY";
+	case AVC_PANEL_STOP:
+		return "STOP";
+	case AVC_PANEL_PAUSE:
+		return "PAUSE";
+	case AVC_PANEL_RECORD:
+		return "RECORD";
+	case AVC_PANEL_REWIND:
+		return "REWIND";
+	case AVC_PANEL_FAST_FORWARD:
+		return "FAST FORWARD";
+	case AVC_PANEL_EJECT:
+		return "EJECT";
+	case AVC_PANEL_FORWARD:
+		return "FORWARD";
+	case AVC_PANEL_BACKWARD:
+		return "BACKWARD";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *type2str(uint8_t type)
+{
+	switch (type) {
+	case AVRCP_MEDIA_PLAYER_ITEM_TYPE:
+		return "Media Player";
+	case AVRCP_FOLDER_ITEM_TYPE:
+		return "Folder";
+	case AVRCP_MEDIA_ELEMENT_ITEM_TYPE:
+		return "Media Element";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *playertype2str(uint8_t type)
+{
+	switch (type & 0x0F) {
+	case 0x01:
+		return "Audio";
+	case 0x02:
+		return "Video";
+	case 0x03:
+		return "Audio, Video";
+	case 0x04:
+		return "Audio Broadcasting";
+	case 0x05:
+		return "Audio, Audio Broadcasting";
+	case 0x06:
+		return "Video, Audio Broadcasting";
+	case 0x07:
+		return "Audio, Video, Audio Broadcasting";
+	case 0x08:
+		return "Video Broadcasting";
+	case 0x09:
+		return "Audio, Video Broadcasting";
+	case 0x0A:
+		return "Video, Video Broadcasting";
+	case 0x0B:
+		return "Audio, Video, Video Broadcasting";
+	case 0x0C:
+		return "Audio Broadcasting, Video Broadcasting";
+	case 0x0D:
+		return "Audio, Audio Broadcasting, Video Broadcasting";
+	case 0x0E:
+		return "Video, Audio Broadcasting, Video Broadcasting";
+	case 0x0F:
+		return "Audio, Video, Audio Broadcasting, Video Broadcasting";
+	}
+
+	return "None";
+}
+
+static const char *playersubtype2str(uint32_t subtype)
+{
+	switch (subtype & 0x03) {
+	case 0x01:
+		return "Audio Book";
+	case 0x02:
+		return "Podcast";
+	case 0x03:
+		return "Audio Book, Podcast";
+	}
+
+	return "None";
+}
+
+static const char *foldertype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "Mixed";
+	case 0x01:
+		return "Titles";
+	case 0x02:
+		return "Albums";
+	case 0x03:
+		return "Artists";
+	case 0x04:
+		return "Genres";
+	case 0x05:
+		return "Playlists";
+	case 0x06:
+		return "Years";
+	}
+
+	return "Reserved";
+}
+
+static const char *elementtype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "Audio";
+	case 0x01:
+		return "Video";
+	}
+
+	return "Reserved";
+}
+
+static bool avrcp_passthrough_packet(struct avctp_frame *avctp_frame,
+								uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t op, len;
+
+	if (!l2cap_frame_get_u8(frame, &op))
+		return false;
+
+	print_field("%*cOperation: 0x%02x (%s %s)", (indent - 8), ' ', op,
+				op2str(op), op & 0x80 ? "Released" : "Pressed");
+
+	if (!l2cap_frame_get_u8(frame, &len))
+		return false;
+
+	print_field("%*cLength: 0x%02x", (indent - 8), ' ', len);
+
+	packet_hexdump(frame->data, frame->size);
+	return true;
+}
+
+static bool avrcp_get_capabilities(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t cap, count;
+	int i;
+
+	if (!l2cap_frame_get_u8(frame, &cap))
+		return false;
+
+	print_field("%*cCapabilityID: 0x%02x (%s)", (indent - 8), ' ', cap,
+								cap2str(cap));
+
+	if (len == 1)
+		return true;
+
+	if (!l2cap_frame_get_u8(frame, &count))
+		return false;
+
+	print_field("%*cCapabilityCount: 0x%02x", (indent - 8), ' ', count);
+
+	switch (cap) {
+	case 0x2:
+		for (; count > 0; count--) {
+			uint8_t company[3];
+
+			if (!l2cap_frame_get_u8(frame, &company[0]) ||
+				!l2cap_frame_get_u8(frame, &company[1]) ||
+				!l2cap_frame_get_u8(frame, &company[2]))
+				return false;
+
+			print_field("%*c%s: 0x%02x%02x%02x", (indent - 8), ' ',
+					cap2str(cap), company[0], company[1],
+					company[2]);
+		}
+		break;
+	case 0x3:
+		for (i = 0; count > 0; count--, i++) {
+			uint8_t event;
+
+			if (!l2cap_frame_get_u8(frame, &event))
+				return false;
+
+			print_field("%*c%s: 0x%02x (%s)", (indent - 8), ' ',
+					cap2str(cap), event, event2str(event));
+		}
+		break;
+	default:
+		packet_hexdump(frame->data, frame->size);
+	}
+
+	return true;
+}
+
+static bool avrcp_list_player_attributes(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t num;
+	int i;
+
+	if (len == 0)
+		return true;
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (i = 0; num > 0; num--, i++) {
+		uint8_t attr;
+
+		if (!l2cap_frame_get_u8(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ',
+							attr, attr2str(attr));
+	}
+
+	return true;
+}
+
+static bool avrcp_list_player_values(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	static uint8_t attr = 0;
+	uint8_t num;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_u8(frame, &attr))
+		return false;
+
+	print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ',
+						attr, attr2str(attr));
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint8_t value;
+
+		if (!l2cap_frame_get_u8(frame, &value))
+			return false;
+
+		print_field("%*cValueID: 0x%02x (%s)", (indent - 8),
+					' ', value, value2str(attr, value));
+	}
+
+	return true;
+}
+
+static bool avrcp_get_current_player_value(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t num;
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		if (!l2cap_frame_get_u8(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8),
+						' ', attr, attr2str(attr));
+	}
+
+	return true;
+
+response:
+	print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint8_t attr, value;
+
+		if (!l2cap_frame_get_u8(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8),
+						' ', attr, attr2str(attr));
+
+		if (!l2cap_frame_get_u8(frame, &value))
+			return false;
+
+		print_field("%*cValueID: 0x%02x (%s)", (indent - 8),
+					' ', value, value2str(attr, value));
+	}
+
+	return true;
+}
+
+static bool avrcp_set_player_value(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t num;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		return true;
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint8_t attr, value;
+
+		if (!l2cap_frame_get_u8(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ',
+							attr, attr2str(attr));
+
+		if (!l2cap_frame_get_u8(frame, &value))
+			return false;
+
+		print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ',
+						value, value2str(attr, value));
+	}
+
+	return true;
+}
+
+static bool avrcp_get_player_attribute_text(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t num;
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		if (!l2cap_frame_get_u8(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8),
+						' ', attr, attr2str(attr));
+	}
+
+	return true;
+
+response:
+	for (; num > 0; num--) {
+		uint8_t attr, len;
+		uint16_t charset;
+
+		if (!l2cap_frame_get_u8(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8),
+						' ', attr, attr2str(attr));
+
+		if (!l2cap_frame_get_be16(frame, &charset))
+			return false;
+
+		print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8),
+					' ', charset, charset2str(charset));
+
+		if (!l2cap_frame_get_u8(frame, &len))
+			return false;
+
+		print_field("%*cStringLength: 0x%02x", (indent - 8), ' ', len);
+
+		printf("String: ");
+		for (; len > 0; len--) {
+			uint8_t c;
+
+			if (!l2cap_frame_get_u8(frame, &c))
+				return false;
+
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+
+	return true;
+}
+
+static bool avrcp_get_player_value_text(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	static uint8_t attr = 0;
+	uint8_t num;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_u8(frame, &attr))
+		return false;
+
+	print_field("%*cAttributeID: 0x%02x (%s)", (indent - 8), ' ',
+						attr, attr2str(attr));
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint8_t value;
+
+		if (!l2cap_frame_get_u8(frame, &value))
+			return false;
+
+		print_field("%*cValueID: 0x%02x (%s)", (indent - 8),
+				' ', value, value2str(attr, value));
+	}
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cValueCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint8_t value, len;
+		uint16_t charset;
+
+		if (!l2cap_frame_get_u8(frame, &value))
+			return false;
+
+		print_field("%*cValueID: 0x%02x (%s)", (indent - 8), ' ',
+						value, value2str(attr, value));
+
+		if (!l2cap_frame_get_be16(frame, &charset))
+			return false;
+
+		print_field("%*cCharsetIDID: 0x%02x (%s)", (indent - 8), ' ',
+						charset, charset2str(charset));
+
+		if (!l2cap_frame_get_u8(frame, &len))
+			return false;
+
+		print_field("%*cStringLength: 0x%02x", (indent - 8), ' ', len);
+
+		printf("String: ");
+		for (; len > 0; len--) {
+			uint8_t c;
+
+			if (!l2cap_frame_get_u8(frame, &c))
+				return false;
+
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+
+	return true;
+}
+
+static bool avrcp_displayable_charset(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t num;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		return true;
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cCharsetCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint16_t charset;
+
+		if (!l2cap_frame_get_be16(frame, &charset))
+			return false;
+
+		print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8),
+					' ', charset, charset2str(charset));
+	}
+
+	return true;
+}
+
+static bool avrcp_get_element_attributes(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint64_t id;
+	uint8_t num;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_be64(frame, &id))
+		return false;
+
+	print_field("%*cIdentifier: 0x%jx (%s)", (indent - 8), ' ',
+					id, id ? "Reserved" : "PLAYING");
+
+	if (!l2cap_frame_get_u8(frame, &num))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x", (indent - 8), ' ', num);
+
+	for (; num > 0; num--) {
+		uint32_t attr;
+
+		if (!l2cap_frame_get_le32(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%08x (%s)", (indent - 8),
+					' ', attr, mediattr2str(attr));
+	}
+
+	return true;
+
+response:
+	switch (avctp_frame->pt) {
+	case AVRCP_PACKET_TYPE_SINGLE:
+	case AVRCP_PACKET_TYPE_START:
+		if (!l2cap_frame_get_u8(frame, &num))
+			return false;
+
+		avrcp_continuing.num = num;
+		print_field("%*cAttributeCount: 0x%02x", (indent - 8),
+								' ', num);
+		len--;
+		break;
+	case AVRCP_PACKET_TYPE_CONTINUING:
+	case AVRCP_PACKET_TYPE_END:
+		num = avrcp_continuing.num;
+
+		if (avrcp_continuing.size > 0) {
+			char attrval[UINT8_MAX] = {0};
+			uint16_t size;
+			uint8_t idx;
+
+			if (avrcp_continuing.size > len) {
+				size = len;
+				avrcp_continuing.size -= len;
+			} else {
+				size = avrcp_continuing.size;
+				avrcp_continuing.size = 0;
+			}
+
+			for (idx = 0; size > 0; idx++, size--) {
+				uint8_t c;
+
+				if (!l2cap_frame_get_u8(frame, &c))
+					goto failed;
+
+				sprintf(&attrval[idx], "%1c",
+							isprint(c) ? c : '.');
+			}
+			print_field("%*cContinuingAttributeValue: %s",
+						(indent - 8), ' ', attrval);
+
+			len -= size;
+		}
+		break;
+	default:
+		goto failed;
+	}
+
+	while (num > 0 && len > 0) {
+		uint32_t attr;
+		uint16_t charset, attrlen;
+		uint8_t idx;
+		char attrval[UINT8_MAX] = {0};
+
+		if (!l2cap_frame_get_be32(frame, &attr))
+			goto failed;
+
+		print_field("%*cAttribute: 0x%08x (%s)", (indent - 8),
+						' ', attr, mediattr2str(attr));
+
+		if (!l2cap_frame_get_be16(frame, &charset))
+			goto failed;
+
+		print_field("%*cCharsetID: 0x%04x (%s)", (indent - 8),
+				' ', charset, charset2str(charset));
+
+		if (!l2cap_frame_get_be16(frame, &attrlen))
+			goto failed;
+
+		print_field("%*cAttributeValueLength: 0x%04x",
+						(indent - 8), ' ', attrlen);
+
+		len -= sizeof(attr) + sizeof(charset) + sizeof(attrlen);
+		num--;
+
+		for (idx = 0; attrlen > 0 && len > 0; idx++, attrlen--, len--) {
+			uint8_t c;
+
+			if (!l2cap_frame_get_u8(frame, &c))
+				goto failed;
+
+			sprintf(&attrval[idx], "%1c", isprint(c) ? c : '.');
+		}
+		print_field("%*cAttributeValue: %s", (indent - 8),
+								' ', attrval);
+
+		if (attrlen > 0)
+			avrcp_continuing.size = attrlen;
+	}
+
+	avrcp_continuing.num = num;
+	return true;
+
+failed:
+	avrcp_continuing.num = 0;
+	avrcp_continuing.size = 0;
+	return false;
+}
+
+static bool avrcp_get_play_status(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint32_t interval;
+	uint8_t status;
+
+	if (ctype <= AVC_CTYPE_GENERAL_INQUIRY)
+		return true;
+
+	if (!l2cap_frame_get_be32(frame, &interval))
+		return false;
+
+	print_field("%*cSongLength: 0x%08x (%u miliseconds)",
+					(indent - 8), ' ', interval, interval);
+
+	if (!l2cap_frame_get_be32(frame, &interval))
+		return false;
+
+	print_field("%*cSongPosition: 0x%08x (%u miliseconds)",
+					(indent - 8), ' ', interval, interval);
+
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cPlayStatus: 0x%02x (%s)", (indent - 8),
+					' ', status, playstatus2str(status));
+
+	return true;
+}
+
+static bool avrcp_register_notification(struct avctp_frame *avctp_frame,
+					uint8_t ctype, uint8_t len,
+					uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t event, status;
+	uint16_t uid;
+	uint32_t interval;
+	uint64_t id;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_u8(frame, &event))
+		return false;
+
+	print_field("%*cEventID: 0x%02x (%s)", (indent - 8),
+						' ', event, event2str(event));
+
+	if (!l2cap_frame_get_be32(frame, &interval))
+		return false;
+
+	print_field("%*cInterval: 0x%08x (%u seconds)",
+					(indent - 8), ' ', interval, interval);
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &event))
+		return false;
+
+	print_field("%*cEventID: 0x%02x (%s)", (indent - 8),
+						' ', event, event2str(event));
+
+	switch (event) {
+	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+		if (!l2cap_frame_get_u8(frame, &status))
+			return false;
+
+		print_field("%*cPlayStatus: 0x%02x (%s)", (indent - 8),
+					' ', status, playstatus2str(status));
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		if (!l2cap_frame_get_be64(frame, &id))
+			return false;
+
+		print_field("%*cIdentifier: 0x%16" PRIx64 " (%" PRIu64 ")",
+						(indent - 8), ' ', id, id);
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		if (!l2cap_frame_get_be32(frame, &interval))
+			return false;
+
+		print_field("%*cPosition: 0x%08x (%u miliseconds)",
+					(indent - 8), ' ', interval, interval);
+		break;
+	case AVRCP_EVENT_BATT_STATUS_CHANGED:
+		if (!l2cap_frame_get_u8(frame, &status))
+			return false;
+
+		print_field("%*cBatteryStatus: 0x%02x (%s)", (indent - 8),
+					' ', status, status2str(status));
+
+		break;
+	case AVRCP_EVENT_SYSTEM_STATUS_CHANGED:
+		if (!l2cap_frame_get_u8(frame, &status))
+			return false;
+
+		print_field("%*cSystemStatus: 0x%02x ", (indent - 8),
+								' ', status);
+		switch (status) {
+		case 0x00:
+			printf("(POWER_ON)\n");
+			break;
+		case 0x01:
+			printf("(POWER_OFF)\n");
+			break;
+		case 0x02:
+			printf("(UNPLUGGED)\n");
+			break;
+		default:
+			printf("(UNKNOWN)\n");
+			break;
+		}
+		break;
+	case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
+		if (!l2cap_frame_get_u8(frame, &status))
+			return false;
+
+		print_field("%*cAttributeCount: 0x%02x", (indent - 8),
+								' ', status);
+
+		for (; status > 0; status--) {
+			uint8_t attr, value;
+
+			if (!l2cap_frame_get_u8(frame, &attr))
+				return false;
+
+			print_field("%*cAttributeID: 0x%02x (%s)",
+				(indent - 8), ' ', attr, attr2str(attr));
+
+			if (!l2cap_frame_get_u8(frame, &value))
+				return false;
+
+			print_field("%*cValueID: 0x%02x (%s)", (indent - 8),
+					' ', value, value2str(attr, value));
+		}
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		if (!l2cap_frame_get_u8(frame, &status))
+			return false;
+
+		status &= 0x7F;
+
+		print_field("%*cVolume: %.2f%% (%d/127)", (indent - 8),
+						' ', status/1.27, status);
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		if (!l2cap_frame_get_be16(frame, &uid))
+			return false;
+
+		print_field("%*cPlayerID: 0x%04x (%u)", (indent - 8),
+								' ', uid, uid);
+
+		if (!l2cap_frame_get_be16(frame, &uid))
+			return false;
+
+		print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8),
+								' ', uid, uid);
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		if (!l2cap_frame_get_be16(frame, &uid))
+			return false;
+
+		print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8),
+								' ', uid, uid);
+		break;
+	}
+
+	return true;
+}
+
+static bool avrcp_set_absolute_volume(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t value;
+
+	if (!l2cap_frame_get_u8(frame, &value))
+		return false;
+
+	value &= 0x7F;
+	print_field("%*cVolume: %.2f%% (%d/127)", (indent - 8),
+						' ', value/1.27, value);
+
+	return true;
+}
+
+static bool avrcp_set_addressed_player(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint16_t id;
+	uint8_t status;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_be16(frame, &id))
+		return false;
+
+	print_field("%*cPlayerID: 0x%04x (%u)", (indent - 8), ' ', id, id);
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ',
+						status, error2str(status));
+
+	return true;
+}
+
+static bool avrcp_play_item(struct avctp_frame *avctp_frame, uint8_t ctype,
+						uint8_t len, uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint64_t uid;
+	uint16_t uidcounter;
+	uint8_t scope, status;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_u8(frame, &scope))
+		return false;
+
+	print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ',
+						scope, scope2str(scope));
+
+	if (!l2cap_frame_get_be64(frame, &uid))
+		return false;
+
+	print_field("%*cUID: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8),
+								' ', uid, uid);
+
+	if (!l2cap_frame_get_be16(frame, &uidcounter))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ',
+							uidcounter, uidcounter);
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status,
+							error2str(status));
+
+	return true;
+}
+
+static bool avrcp_add_to_now_playing(struct avctp_frame *avctp_frame,
+						uint8_t ctype, uint8_t len,
+						uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint64_t uid;
+	uint16_t uidcounter;
+	uint8_t scope, status;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (!l2cap_frame_get_u8(frame, &scope))
+		return false;
+
+	print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ',
+						scope, scope2str(scope));
+
+	if (!l2cap_frame_get_be64(frame, &uid))
+		return false;
+
+	print_field("%*cUID: 0x%16" PRIx64 " (%" PRIu64 ")", (indent - 8),
+								' ', uid, uid);
+
+	if (!l2cap_frame_get_be16(frame, &uidcounter))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", (indent - 8), ' ',
+							uidcounter, uidcounter);
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", (indent - 8), ' ', status,
+							error2str(status));
+
+	return true;
+}
+
+struct avrcp_ctrl_pdu_data {
+	uint8_t pduid;
+	bool (*func) (struct avctp_frame *avctp_frame, uint8_t ctype,
+						uint8_t len, uint8_t indent);
+};
+
+static const struct avrcp_ctrl_pdu_data avrcp_ctrl_pdu_table[] = {
+	{ 0x10, avrcp_get_capabilities			},
+	{ 0x11, avrcp_list_player_attributes		},
+	{ 0x12, avrcp_list_player_values		},
+	{ 0x13, avrcp_get_current_player_value		},
+	{ 0x14, avrcp_set_player_value			},
+	{ 0x15, avrcp_get_player_attribute_text		},
+	{ 0x16, avrcp_get_player_value_text		},
+	{ 0x17, avrcp_displayable_charset		},
+	{ 0x20, avrcp_get_element_attributes		},
+	{ 0x30, avrcp_get_play_status			},
+	{ 0x31, avrcp_register_notification		},
+	{ 0x50, avrcp_set_absolute_volume		},
+	{ 0x60, avrcp_set_addressed_player		},
+	{ 0x74, avrcp_play_item				},
+	{ 0x90, avrcp_add_to_now_playing		},
+	{ }
+};
+
+static bool avrcp_rejected_packet(struct l2cap_frame *frame, uint8_t indent)
+{
+	uint8_t status;
+
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cError: 0x%02x (%s)", (indent - 8), ' ', status,
+							error2str(status));
+
+	return true;
+}
+
+static bool avrcp_pdu_packet(struct avctp_frame *avctp_frame, uint8_t ctype,
+								uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t pduid;
+	uint16_t len;
+	int i;
+	const struct avrcp_ctrl_pdu_data *ctrl_pdu_data = NULL;
+
+	if (!l2cap_frame_get_u8(frame, &pduid))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &avctp_frame->pt))
+		return false;
+
+	if (!l2cap_frame_get_be16(frame, &len))
+		return false;
+
+	print_indent(indent, COLOR_OFF, "AVRCP: ", pdu2str(pduid), COLOR_OFF,
+			" pt %s len 0x%04x", pt2str(avctp_frame->pt), len);
+
+	if (frame->size != len)
+		return false;
+
+	if (ctype == 0xA)
+		return avrcp_rejected_packet(frame, indent + 2);
+
+	for (i = 0; avrcp_ctrl_pdu_table[i].func; i++) {
+		if (avrcp_ctrl_pdu_table[i].pduid == pduid) {
+			ctrl_pdu_data = &avrcp_ctrl_pdu_table[i];
+			break;
+		}
+	}
+
+	if (!ctrl_pdu_data || !ctrl_pdu_data->func) {
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	}
+
+	return ctrl_pdu_data->func(avctp_frame, ctype, len, indent + 2);
+}
+
+static bool avrcp_control_packet(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+
+	uint8_t ctype, address, subunit, opcode, company[3], indent = 2;
+
+	if (!l2cap_frame_get_u8(frame, &ctype) ||
+				!l2cap_frame_get_u8(frame, &address) ||
+				!l2cap_frame_get_u8(frame, &opcode))
+		return false;
+
+	print_field("AV/C: %s: address 0x%02x opcode 0x%02x",
+				ctype2str(ctype), address, opcode);
+
+	subunit = address >> 3;
+
+	print_field("%*cSubunit: %s", indent, ' ', subunit2str(subunit));
+
+	print_field("%*cOpcode: %s", indent, ' ', opcode2str(opcode));
+
+	/* Skip non-panel subunit packets */
+	if (subunit != 0x09) {
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	}
+
+	/* Not implemented should not contain any operand */
+	if (ctype == 0x8) {
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	}
+
+	switch (opcode) {
+	case 0x7c:
+		return avrcp_passthrough_packet(avctp_frame, 10);
+	case 0x00:
+		if (!l2cap_frame_get_u8(frame, &company[0]) ||
+				!l2cap_frame_get_u8(frame, &company[1]) ||
+				!l2cap_frame_get_u8(frame, &company[2]))
+			return false;
+
+		print_field("%*cCompany ID: 0x%02x%02x%02x", indent, ' ',
+					company[0], company[1], company[2]);
+
+		return avrcp_pdu_packet(avctp_frame, ctype, 10);
+	default:
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	}
+}
+
+static const char *dir2str(uint8_t dir)
+{
+	switch (dir) {
+	case 0x00:
+		return "Folder Up";
+	case 0x01:
+		return "Folder Down";
+	}
+
+	return "Reserved";
+}
+
+static bool avrcp_change_path(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint64_t uid;
+	uint32_t items;
+	uint16_t uidcounter;
+	uint8_t dir, status, indent = 2;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	if (frame->size < 11) {
+		print_field("%*cPDU Malformed", indent, ' ');
+		packet_hexdump(frame->data, frame->size);
+		return false;
+	}
+
+	if (!l2cap_frame_get_be16(frame, &uidcounter))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ',
+					uidcounter, uidcounter);
+
+	if (!l2cap_frame_get_u8(frame, &dir))
+		return false;
+
+	print_field("%*cDirection: 0x%02x (%s)", indent, ' ',
+					dir, dir2str(dir));
+
+	if (!l2cap_frame_get_be64(frame, &uid))
+		return false;
+
+	print_field("%*cFolderUID: 0x%16" PRIx64 " (%" PRIu64 ")", indent, ' ',
+					uid, uid);
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ',
+					status, error2str(status));
+
+	if (frame->size == 1)
+		return false;
+
+	if (!l2cap_frame_get_be32(frame, &items))
+		return false;
+
+	print_field("%*cNumber of Items: 0x%04x (%u)", indent, ' ',
+					items, items);
+
+	return true;
+}
+
+
+static struct {
+	const char *str;
+	bool reserved;
+} features_table[] = {
+	/* Ignore passthrough bits */
+	[58] = { "Advanced Control Player" },
+	[59] = { "Browsing" },
+	[60] = { "Searching" },
+	[61] = { "AddToNowPlaying" },
+	[62] = { "Unique UIDs" },
+	[63] = { "OnlyBrowsableWhenAddressed" },
+	[64] = { "OnlySearchableWhenAddressed" },
+	[65] = { "NowPlaying" },
+	[66] = { "UIDPersistency" },
+	/* 67-127 reserved */
+	[67 ... 127] = { .reserved = true },
+};
+
+static void print_features(uint8_t features[16], uint8_t indent)
+{
+	int i;
+
+	for (i = 0; i < 127; i++) {
+		if (!(features[i / 8] & (1 << (i % 8))))
+			continue;
+
+		if (features_table[i].reserved) {
+			print_text(COLOR_WHITE_BG, "Unknown bit %u", i);
+			continue;
+		}
+
+		if (!features_table[i].str)
+			continue;
+
+		print_field("%*c%s", indent, ' ', features_table[i].str);
+	}
+}
+
+static bool avrcp_media_player_item(struct avctp_frame *avctp_frame,
+							uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint16_t id, charset, namelen;
+	uint8_t type, status, i;
+	uint32_t subtype;
+	uint8_t features[16];
+
+	if (!l2cap_frame_get_be16(frame, &id))
+		return false;
+
+	print_field("%*cPlayerID: 0x%04x (%u)", indent, ' ', id, id);
+
+	if (!l2cap_frame_get_u8(frame, &type))
+		return false;
+
+	print_field("%*cPlayerType: 0x%04x (%s)", indent, ' ',
+						type, playertype2str(type));
+
+	if (!l2cap_frame_get_be32(frame, &subtype))
+		return false;
+
+	print_field("%*cPlayerSubType: 0x%08x (%s)", indent, ' ',
+					subtype, playersubtype2str(subtype));
+
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cPlayStatus: 0x%02x (%s)", indent, ' ',
+						status, playstatus2str(status));
+
+	printf("%*cFeatures: 0x", indent+8, ' ');
+
+	for (i = 0; i < 16; i++) {
+		if (!l2cap_frame_get_u8(frame, &features[i]))
+			return false;
+
+		printf("%02x", features[i]);
+	}
+
+	printf("\n");
+
+	print_features(features, indent + 2);
+
+	if (!l2cap_frame_get_be16(frame, &charset))
+		return false;
+
+	print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
+						charset, charset2str(charset));
+
+	if (!l2cap_frame_get_be16(frame, &namelen))
+		return false;
+
+	print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
+						namelen, namelen);
+
+	printf("%*cName: ", indent+8, ' ');
+	for (; namelen > 0; namelen--) {
+		uint8_t c;
+
+		if (!l2cap_frame_get_u8(frame, &c))
+			return false;
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+
+	return true;
+}
+
+static bool avrcp_folder_item(struct avctp_frame *avctp_frame,
+							uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t type, playable;
+	uint16_t charset, namelen;
+	uint64_t uid;
+
+	if (frame->size < 14) {
+		printf("PDU Malformed\n");
+		return false;
+	}
+
+	if (!l2cap_frame_get_be64(frame, &uid))
+		return false;
+
+	print_field("%*cFolderUID: 0x%16" PRIx64 " (%" PRIu64 ")", indent, ' ',
+						uid, uid);
+
+	if (!l2cap_frame_get_u8(frame, &type))
+		return false;
+
+	print_field("%*cFolderType: 0x%02x (%s)", indent, ' ',
+						type, foldertype2str(type));
+
+	if (!l2cap_frame_get_u8(frame, &playable))
+		return false;
+
+	print_field("%*cIsPlayable: 0x%02x (%s)", indent, ' ', playable,
+					playable & 0x01 ? "True" : "False");
+
+	if (!l2cap_frame_get_be16(frame, &charset))
+		return false;
+
+	print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
+					charset, charset2str(charset));
+
+	if (!l2cap_frame_get_be16(frame, &namelen))
+		return false;
+
+	print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
+					namelen, namelen);
+
+	print_field("%*cName: ", indent, ' ');
+	for (; namelen > 0; namelen--) {
+		uint8_t c;
+		if (!l2cap_frame_get_u8(frame, &c))
+			return false;
+
+		printf("%1c", isprint(c) ? c : '.');
+	}
+
+	return true;
+}
+
+static bool avrcp_attribute_entry_list(struct avctp_frame *avctp_frame,
+						uint8_t indent, uint8_t count)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+
+	for (; count > 0; count--) {
+		uint32_t attr;
+		uint16_t charset;
+		uint8_t len;
+
+		if (!l2cap_frame_get_be32(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%08x (%s)", indent, ' ',
+						attr, mediattr2str(attr));
+
+		if (!l2cap_frame_get_be16(frame, &charset))
+			return false;
+
+		print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
+						charset, charset2str(charset));
+
+		if (!l2cap_frame_get_u8(frame, &len))
+			return false;
+
+		print_field("%*cAttributeLength: 0x%02x (%u)", indent, ' ',
+						len, len);
+
+		print_field("%*cAttributeValue: ", indent, ' ');
+		for (; len > 0; len--) {
+			uint8_t c;
+
+			if (!l2cap_frame_get_u8(frame, &c))
+				return false;
+
+			printf("%1c", isprint(c) ? c : '.');
+		}
+	}
+
+	return true;
+}
+
+static bool avrcp_media_element_item(struct avctp_frame *avctp_frame,
+							uint8_t indent)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint64_t uid;
+	uint16_t charset, namelen;
+	uint8_t type, count;
+
+	if (!l2cap_frame_get_be64(frame, &uid))
+		return false;
+
+	print_field("%*cElementUID: 0x%16" PRIx64 " (%" PRIu64 ")",
+					indent, ' ', uid, uid);
+
+	if (!l2cap_frame_get_u8(frame, &type))
+		return false;
+
+	print_field("%*cElementType: 0x%02x (%s)", indent, ' ',
+					type, elementtype2str(type));
+
+	if (!l2cap_frame_get_be16(frame, &charset))
+		return false;
+
+	print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
+					charset, charset2str(charset));
+
+	if (!l2cap_frame_get_be16(frame, &namelen))
+		return false;
+
+	print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
+					namelen, namelen);
+
+	print_field("%*cName: ", indent, ' ');
+	for (; namelen > 0; namelen--) {
+		uint8_t c;
+		if (!l2cap_frame_get_u8(frame, &c))
+			return false;
+
+		printf("%1c", isprint(c) ? c : '.');
+	}
+
+	if (!l2cap_frame_get_u8(frame, &count))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ',
+						count, count);
+
+	if (!avrcp_attribute_entry_list(avctp_frame, indent, count))
+		return false;
+
+	return true;
+}
+
+static bool avrcp_general_reject(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t status, indent = 2;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	print_field("%*cPDU Malformed", indent, ' ');
+	packet_hexdump(frame->data, frame->size);
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ',
+				status, error2str(status));
+
+	return true;
+}
+
+static bool avrcp_get_total_number_of_items(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint32_t num_of_items;
+	uint16_t uidcounter;
+	uint8_t scope, status, indent = 2;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	if (frame->size < 4) {
+		printf("PDU Malformed\n");
+		packet_hexdump(frame->data, frame->size);
+		return false;
+	}
+
+	if (!l2cap_frame_get_u8(frame, &scope))
+		return false;
+
+	print_field("%*cScope: 0x%02x (%s)", (indent - 8), ' ',
+						scope, scope2str(scope));
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ',
+				status, error2str(status));
+
+	if (frame->size == 1)
+		return false;
+
+	if (!l2cap_frame_get_be16(frame, &uidcounter))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ',
+				uidcounter, uidcounter);
+
+	if (!l2cap_frame_get_be32(frame, &num_of_items))
+		return false;
+
+	print_field("%*cNumber of Items: 0x%04x (%u)", indent, ' ',
+				num_of_items, num_of_items);
+
+	return true;
+}
+
+static bool avrcp_search_item(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint32_t items;
+	uint16_t charset, namelen, uidcounter;
+	uint8_t status, indent = 2;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	if (frame->size < 4) {
+		printf("PDU Malformed\n");
+		packet_hexdump(frame->data, frame->size);
+		return false;
+	}
+
+	if (!l2cap_frame_get_be16(frame, &charset))
+		return false;
+
+	print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
+				charset, charset2str(charset));
+
+	if (!l2cap_frame_get_be16(frame, &namelen))
+		return false;
+
+	print_field("%*cLength: 0x%04x (%u)", indent, ' ', namelen, namelen);
+
+	printf("%*cString: ", indent+8, ' ');
+	for (; namelen > 0; namelen--) {
+		uint8_t c;
+
+		if (!l2cap_frame_get_u8(frame, &c))
+			return false;
+
+		printf("%1c", isprint(c) ? c : '.');
+	}
+
+	printf("\n");
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ',
+				status, error2str(status));
+
+	if (frame->size == 1)
+		return false;
+
+	if (!l2cap_frame_get_be16(frame, &uidcounter))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ',
+				uidcounter, uidcounter);
+
+	if (!l2cap_frame_get_be32(frame, &items))
+		return false;
+
+	print_field("%*cNumber of Items: 0x%04x (%u)", indent, ' ',
+				items, items);
+
+	return true;
+}
+
+static bool avrcp_get_item_attributes(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint64_t uid;
+	uint16_t uidcounter;
+	uint8_t scope, count, status, indent = 2;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	if (frame->size < 12) {
+		print_field("%*cPDU Malformed", indent, ' ');
+		packet_hexdump(frame->data, frame->size);
+		return false;
+	}
+
+	if (!l2cap_frame_get_u8(frame, &scope))
+		return false;
+
+	print_field("%*cScope: 0x%02x (%s)", indent, ' ',
+					scope, scope2str(scope));
+
+	if (!l2cap_frame_get_be64(frame, &uid))
+		return false;
+
+	print_field("%*cUID: 0x%016" PRIx64 " (%" PRIu64 ")", indent,
+					' ', uid, uid);
+
+	if (!l2cap_frame_get_be16(frame, &uidcounter))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ',
+					uidcounter, uidcounter);
+
+	if (!l2cap_frame_get_u8(frame, &count))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ',
+					count, count);
+
+	for (; count > 0; count--) {
+		uint32_t attr;
+
+		if (!l2cap_frame_get_be32(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%08x (%s)", indent, ' ',
+					attr, mediattr2str(attr));
+	}
+
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ',
+					status, error2str(status));
+
+	if (frame->size == 1)
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &count))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ',
+					count, count);
+
+	if (!avrcp_attribute_entry_list(avctp_frame, indent, count))
+		return false;
+
+	return true;
+}
+
+static bool avrcp_get_folder_items(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint8_t scope, count, status, indent = 2;
+	uint32_t start, end;
+	uint16_t uid, num;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	if (!l2cap_frame_get_u8(frame, &scope))
+		return false;
+
+	print_field("%*cScope: 0x%02x (%s)", indent, ' ',
+					scope, scope2str(scope));
+
+	if (!l2cap_frame_get_be32(frame, &start))
+		return false;
+
+	print_field("%*cStartItem: 0x%08x (%u)", indent, ' ', start, start);
+
+	if (!l2cap_frame_get_be32(frame, &end))
+		return false;
+
+	print_field("%*cEndItem: 0x%08x (%u)", indent, ' ', end, end);
+
+	if (!l2cap_frame_get_u8(frame, &count))
+		return false;
+
+	print_field("%*cAttributeCount: 0x%02x (%u)", indent, ' ',
+						count, count);
+
+	for (; count > 0; count--) {
+		uint16_t attr;
+
+		if (!l2cap_frame_get_be16(frame, &attr))
+			return false;
+
+		print_field("%*cAttributeID: 0x%08x (%s)", indent, ' ',
+					attr, mediattr2str(attr));
+	}
+
+	return false;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ',
+				status, error2str(status));
+
+	if (!l2cap_frame_get_be16(frame, &uid))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uid, uid);
+
+	if (!l2cap_frame_get_be16(frame, &num))
+		return false;
+
+	print_field("%*cNumOfItems: 0x%04x (%u)", indent, ' ', num, num);
+
+	for (; num > 0; num--) {
+		uint8_t type;
+		uint16_t len;
+
+		if (!l2cap_frame_get_u8(frame, &type))
+			return false;
+
+		if (!l2cap_frame_get_be16(frame, &len))
+			return false;
+
+		print_field("%*cItem: 0x%02x (%s) ", indent, ' ',
+					type, type2str(type));
+		print_field("%*cLength: 0x%04x (%u)", indent, ' ', len, len);
+
+		switch (type) {
+		case AVRCP_MEDIA_PLAYER_ITEM_TYPE:
+			avrcp_media_player_item(avctp_frame, indent);
+			break;
+		case AVRCP_FOLDER_ITEM_TYPE:
+			avrcp_folder_item(avctp_frame, indent);
+			break;
+		case AVRCP_MEDIA_ELEMENT_ITEM_TYPE:
+			avrcp_media_element_item(avctp_frame, indent);
+			break;
+		default:
+			print_field("%*cUnknown Media Item type", indent, ' ');
+			packet_hexdump(frame->data, frame->size);
+			break;
+		}
+	}
+	return true;
+}
+
+static bool avrcp_set_browsed_player(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint32_t items;
+	uint16_t id, uids, charset;
+	uint8_t status, folders, indent = 2;
+
+	if (avctp_frame->hdr & 0x02)
+		goto response;
+
+	if (!l2cap_frame_get_be16(frame, &id))
+		return false;
+
+	print_field("%*cPlayerID: 0x%04x (%u)", indent, ' ', id, id);
+	return true;
+
+response:
+	if (!l2cap_frame_get_u8(frame, &status))
+		return false;
+
+	print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status,
+							error2str(status));
+
+	if (!l2cap_frame_get_be16(frame, &uids))
+		return false;
+
+	print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uids, uids);
+
+	if (!l2cap_frame_get_be32(frame, &items))
+		return false;
+
+	print_field("%*cNumber of Items: 0x%08x (%u)", indent, ' ',
+								items, items);
+
+	if (!l2cap_frame_get_be16(frame, &charset))
+		return false;
+
+	print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset,
+							charset2str(charset));
+
+	if (!l2cap_frame_get_u8(frame, &folders))
+		return false;
+
+	print_field("%*cFolder Depth: 0x%02x (%u)", indent, ' ', folders,
+								folders);
+
+	for (; folders > 0; folders--) {
+		uint8_t len;
+
+		if (!l2cap_frame_get_u8(frame, &len))
+			return false;
+
+		printf("Folder: ");
+		for (; len > 0; len--) {
+			uint8_t c;
+
+			if (!l2cap_frame_get_u8(frame, &c))
+				return false;
+
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+
+	return true;
+}
+
+static bool avrcp_browsing_packet(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	uint16_t len;
+	uint8_t pduid;
+
+	if (!l2cap_frame_get_u8(frame, &pduid))
+		return false;
+
+	if (!l2cap_frame_get_be16(frame, &len))
+		return false;
+
+	print_field("AVRCP: %s: len 0x%04x", pdu2str(pduid), len);
+
+	switch (pduid) {
+	case AVRCP_SET_BROWSED_PLAYER:
+		avrcp_set_browsed_player(avctp_frame);
+		break;
+	case AVRCP_GET_FOLDER_ITEMS:
+		avrcp_get_folder_items(avctp_frame);
+		break;
+	case AVRCP_CHANGE_PATH:
+		avrcp_change_path(avctp_frame);
+		break;
+	case AVRCP_GET_ITEM_ATTRIBUTES:
+		avrcp_get_item_attributes(avctp_frame);
+		break;
+	case AVRCP_GET_TOTAL_NUMBER_OF_ITEMS:
+		avrcp_get_total_number_of_items(avctp_frame);
+		break;
+	case AVRCP_SEARCH:
+		avrcp_search_item(avctp_frame);
+		break;
+	case AVRCP_GENERAL_REJECT:
+		avrcp_general_reject(avctp_frame);
+		break;
+	default:
+		packet_hexdump(frame->data, frame->size);
+	}
+
+	return true;
+}
+
+static void avrcp_packet(struct avctp_frame *avctp_frame)
+{
+	struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+	bool ret;
+
+	switch (frame->psm) {
+	case 0x17:
+		ret = avrcp_control_packet(avctp_frame);
+		break;
+	case 0x1B:
+		ret = avrcp_browsing_packet(avctp_frame);
+		break;
+	default:
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	if (!ret) {
+		print_text(COLOR_ERROR, "PDU malformed");
+		packet_hexdump(frame->data, frame->size);
+	}
+}
+
+void avctp_packet(const struct l2cap_frame *frame)
+{
+	struct l2cap_frame *l2cap_frame;
+	struct avctp_frame avctp_frame;
+	const char *pdu_color;
+
+	l2cap_frame_pull(&avctp_frame.l2cap_frame, frame, 0);
+
+	l2cap_frame = &avctp_frame.l2cap_frame;
+
+	if (!l2cap_frame_get_u8(l2cap_frame, &avctp_frame.hdr) ||
+			!l2cap_frame_get_be16(l2cap_frame, &avctp_frame.pid)) {
+		print_text(COLOR_ERROR, "frame too short");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	if (frame->in)
+		pdu_color = COLOR_MAGENTA;
+	else
+		pdu_color = COLOR_BLUE;
+
+	print_indent(6, pdu_color, "AVCTP", "", COLOR_OFF,
+				" %s: %s: type 0x%02x label %d PID 0x%04x",
+				frame->psm == 23 ? "Control" : "Browsing",
+				avctp_frame.hdr & 0x02 ? "Response" : "Command",
+				avctp_frame.hdr & 0x0c, avctp_frame.hdr >> 4,
+				avctp_frame.pid);
+
+	if (avctp_frame.pid == 0x110e || avctp_frame.pid == 0x110c)
+		avrcp_packet(&avctp_frame);
+	else
+		packet_hexdump(frame->data, frame->size);
+}
diff --git a/bluez/repo/monitor/avctp.h b/bluez/repo/monitor/avctp.h
new file mode 100644
index 0000000..2613f14
--- /dev/null
+++ b/bluez/repo/monitor/avctp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+void avctp_packet(const struct l2cap_frame *frame);
diff --git a/bluez/repo/monitor/avdtp.c b/bluez/repo/monitor/avdtp.c
new file mode 100644
index 0000000..3524faa
--- /dev/null
+++ b/bluez/repo/monitor/avdtp.c
@@ -0,0 +1,787 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+ *
+ *
+ *  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 <string.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "bt.h"
+#include "packet.h"
+#include "display.h"
+#include "l2cap.h"
+#include "avdtp.h"
+#include "a2dp.h"
+
+/* Message Types */
+#define AVDTP_MSG_TYPE_COMMAND		0x00
+#define AVDTP_MSG_TYPE_GENERAL_REJECT	0x01
+#define AVDTP_MSG_TYPE_RESPONSE_ACCEPT	0x02
+#define AVDTP_MSG_TYPE_RESPONSE_REJECT	0x03
+
+/* Signal Identifiers */
+#define AVDTP_DISCOVER			0x01
+#define AVDTP_GET_CAPABILITIES		0x02
+#define AVDTP_SET_CONFIGURATION		0x03
+#define AVDTP_GET_CONFIGURATION		0x04
+#define AVDTP_RECONFIGURE		0x05
+#define AVDTP_OPEN			0x06
+#define AVDTP_START			0x07
+#define AVDTP_CLOSE			0x08
+#define AVDTP_SUSPEND			0x09
+#define AVDTP_ABORT			0x0a
+#define AVDTP_SECURITY_CONTROL		0x0b
+#define AVDTP_GET_ALL_CAPABILITIES	0x0c
+#define AVDTP_DELAYREPORT		0x0d
+
+/* Service Categories */
+#define AVDTP_MEDIA_TRANSPORT		0x01
+#define AVDTP_REPORTING			0x02
+#define AVDTP_RECOVERY			0x03
+#define AVDTP_CONTENT_PROTECTION	0x04
+#define AVDTP_HEADER_COMPRESSION	0x05
+#define AVDTP_MULTIPLEXING		0x06
+#define AVDTP_MEDIA_CODEC		0x07
+#define AVDTP_DELAY_REPORTING		0x08
+
+struct avdtp_frame {
+	uint8_t hdr;
+	uint8_t sig_id;
+	struct l2cap_frame l2cap_frame;
+};
+
+static inline bool is_configuration_sig_id(uint8_t sig_id)
+{
+	return (sig_id == AVDTP_SET_CONFIGURATION) ||
+			(sig_id == AVDTP_GET_CONFIGURATION) ||
+			(sig_id == AVDTP_RECONFIGURE);
+}
+
+static const char *msgtype2str(uint8_t msgtype)
+{
+	switch (msgtype) {
+	case 0:
+		return "Command";
+	case 1:
+		return "General Reject";
+	case 2:
+		return "Response Accept";
+	case 3:
+		return "Response Reject";
+	}
+
+	return "";
+}
+
+static const char *sigid2str(uint8_t sigid)
+{
+	switch (sigid) {
+	case AVDTP_DISCOVER:
+		return "Discover";
+	case AVDTP_GET_CAPABILITIES:
+		return "Get Capabilities";
+	case AVDTP_SET_CONFIGURATION:
+		return "Set Configuration";
+	case AVDTP_GET_CONFIGURATION:
+		return "Get Configuration";
+	case AVDTP_RECONFIGURE:
+		return "Reconfigure";
+	case AVDTP_OPEN:
+		return "Open";
+	case AVDTP_START:
+		return "Start";
+	case AVDTP_CLOSE:
+		return "Close";
+	case AVDTP_SUSPEND:
+		return "Suspend";
+	case AVDTP_ABORT:
+		return "Abort";
+	case AVDTP_SECURITY_CONTROL:
+		return "Security Control";
+	case AVDTP_GET_ALL_CAPABILITIES:
+		return "Get All Capabilities";
+	case AVDTP_DELAYREPORT:
+		return "Delay Report";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *error2str(uint8_t error)
+{
+	switch (error) {
+	case 0x01:
+		return "BAD_HEADER_FORMAT";
+	case 0x11:
+		return "BAD_LENGTH";
+	case 0x12:
+		return "BAD_ACP_SEID";
+	case 0x13:
+		return "SEP_IN_USE";
+	case 0x14:
+		return "SEP_NOT_IN_USER";
+	case 0x17:
+		return "BAD_SERV_CATEGORY";
+	case 0x18:
+		return "BAD_PAYLOAD_FORMAT";
+	case 0x19:
+		return "NOT_SUPPORTED_COMMAND";
+	case 0x1a:
+		return "INVALID_CAPABILITIES";
+	case 0x22:
+		return "BAD_RECOVERY_TYPE";
+	case 0x23:
+		return "BAD_MEDIA_TRANSPORT_FORMAT";
+	case 0x25:
+		return "BAD_RECOVERY_FORMAT";
+	case 0x26:
+		return "BAD_ROHC_FORMAT";
+	case 0x27:
+		return "BAD_CP_FORMAT";
+	case 0x28:
+		return "BAD_MULTIPLEXING_FORMAT";
+	case 0x29:
+		return "UNSUPPORTED_CONFIGURATION";
+	case 0x31:
+		return "BAD_STATE";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *mediatype2str(uint8_t media_type)
+{
+	switch (media_type) {
+	case 0x00:
+		return "Audio";
+	case 0x01:
+		return "Video";
+	case 0x02:
+		return "Multimedia";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *mediacodec2str(uint8_t codec)
+{
+	switch (codec) {
+	case 0x00:
+		return "SBC";
+	case 0x01:
+		return "MPEG-1,2 Audio";
+	case 0x02:
+		return "MPEG-2,4 AAC";
+	case 0x04:
+		return "ATRAC Family";
+	case 0xff:
+		return "Non-A2DP";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *cptype2str(uint8_t cp)
+{
+	switch (cp) {
+	case 0x0001:
+		return "DTCP";
+	case 0x0002:
+		return "SCMS-T";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *servicecat2str(uint8_t service_cat)
+{
+	switch (service_cat) {
+	case AVDTP_MEDIA_TRANSPORT:
+		return "Media Transport";
+	case AVDTP_REPORTING:
+		return "Reporting";
+	case AVDTP_RECOVERY:
+		return "Recovery";
+	case AVDTP_CONTENT_PROTECTION:
+		return "Content Protection";
+	case AVDTP_HEADER_COMPRESSION:
+		return "Header Compression";
+	case AVDTP_MULTIPLEXING:
+		return "Multiplexing";
+	case AVDTP_MEDIA_CODEC:
+		return "Media Codec";
+	case AVDTP_DELAY_REPORTING:
+		return "Delay Reporting";
+	default:
+		return "Reserved";
+	}
+}
+
+static bool avdtp_reject_common(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t error;
+
+	if (!l2cap_frame_get_u8(frame, &error))
+		return false;
+
+	print_field("Error code: %s (0x%02x)", error2str(error), error);
+
+	return true;
+}
+
+static bool service_content_protection(struct avdtp_frame *avdtp_frame,
+								uint8_t losc)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint16_t type = 0;
+
+	if (losc < 2)
+		return false;
+
+	if (!l2cap_frame_get_le16(frame, &type))
+		return false;
+
+	losc -= 2;
+
+	print_field("%*cContent Protection Type: %s (0x%04x)", 2, ' ',
+							cptype2str(type), type);
+
+	/* TODO: decode protection specific information */
+	packet_hexdump(frame->data, losc);
+
+	l2cap_frame_pull(frame, frame, losc);
+
+	return true;
+}
+
+static bool service_media_codec(struct avdtp_frame *avdtp_frame, uint8_t losc)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = 0;
+	uint8_t codec = 0;
+
+	if (losc < 2)
+		return false;
+
+	l2cap_frame_get_u8(frame, &type);
+	l2cap_frame_get_u8(frame, &codec);
+
+	losc -= 2;
+
+	print_field("%*cMedia Type: %s (0x%02x)", 2, ' ',
+					mediatype2str(type >> 4), type >> 4);
+
+	print_field("%*cMedia Codec: %s (0x%02x)", 2, ' ',
+					mediacodec2str(codec), codec);
+
+	if (is_configuration_sig_id(avdtp_frame->sig_id))
+		return a2dp_codec_cfg(codec, losc, frame);
+	else
+		return a2dp_codec_cap(codec, losc, frame);
+}
+
+static bool decode_capabilities(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t service_cat;
+	uint8_t losc;
+
+	while (l2cap_frame_get_u8(frame, &service_cat)) {
+		print_field("Service Category: %s (0x%02x)",
+				servicecat2str(service_cat), service_cat);
+
+		if (!l2cap_frame_get_u8(frame, &losc))
+			return false;
+
+		if (frame->size < losc)
+			return false;
+
+		switch (service_cat) {
+		case AVDTP_CONTENT_PROTECTION:
+			if (!service_content_protection(avdtp_frame, losc))
+				return false;
+			break;
+		case AVDTP_MEDIA_CODEC:
+			if (!service_media_codec(avdtp_frame, losc))
+				return false;
+			break;
+		case AVDTP_MEDIA_TRANSPORT:
+		case AVDTP_REPORTING:
+		case AVDTP_RECOVERY:
+		case AVDTP_HEADER_COMPRESSION:
+		case AVDTP_MULTIPLEXING:
+		case AVDTP_DELAY_REPORTING:
+		default:
+			packet_hexdump(frame->data, losc);
+			l2cap_frame_pull(frame, frame, losc);
+		}
+
+	}
+
+	return true;
+}
+
+static bool avdtp_discover(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+	uint8_t info;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		while (l2cap_frame_get_u8(frame, &seid)) {
+			print_field("ACP SEID: %d", seid >> 2);
+
+			if (!l2cap_frame_get_u8(frame, &info))
+				return false;
+
+			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 >> 3) & 0x01);
+			print_field("%*cIn use: %s", 2, ' ',
+						seid & 0x02 ? "Yes" : "No");
+		}
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_get_capabilities(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return decode_capabilities(avdtp_frame);
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_set_configuration(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t acp_seid, int_seid;
+	uint8_t service_cat;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &acp_seid))
+			return false;
+
+		print_field("ACP SEID: %d", acp_seid >> 2);
+
+		if (!l2cap_frame_get_u8(frame, &int_seid))
+			return false;
+
+		print_field("INT SEID: %d", int_seid >> 2);
+
+		return decode_capabilities(avdtp_frame);
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		if (!l2cap_frame_get_u8(frame, &service_cat))
+			return false;
+
+		print_field("Service Category: %s (0x%02x)",
+				servicecat2str(service_cat), service_cat);
+
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_get_configuration(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return decode_capabilities(avdtp_frame);
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_reconfigure(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+	uint8_t service_cat;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return decode_capabilities(avdtp_frame);
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		if (!l2cap_frame_get_u8(frame, &service_cat))
+			return false;
+
+		print_field("Service Category: %s (0x%02x)",
+				servicecat2str(service_cat), service_cat);
+
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_open(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_start(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		while (l2cap_frame_get_u8(frame, &seid))
+			print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_close(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_suspend(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		while (l2cap_frame_get_u8(frame, &seid))
+			print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_abort(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	}
+
+	return false;
+}
+
+static bool avdtp_security_control(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		/* TODO: decode more information */
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		/* TODO: decode more information */
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_delayreport(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	uint8_t type = avdtp_frame->hdr & 0x03;
+	uint8_t seid;
+	uint16_t delay;
+
+	switch (type) {
+	case AVDTP_MSG_TYPE_COMMAND:
+		if (!l2cap_frame_get_u8(frame, &seid))
+			return false;
+
+		print_field("ACP SEID: %d", seid >> 2);
+
+		if (!l2cap_frame_get_be16(frame, &delay))
+			return false;
+
+		print_field("Delay: %d.%dms", delay / 10, delay % 10);
+
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_ACCEPT:
+		return true;
+	case AVDTP_MSG_TYPE_RESPONSE_REJECT:
+		return avdtp_reject_common(avdtp_frame);
+	}
+
+	return false;
+}
+
+static bool avdtp_signalling_packet(struct avdtp_frame *avdtp_frame)
+{
+	struct l2cap_frame *frame = &avdtp_frame->l2cap_frame;
+	const char *pdu_color;
+	uint8_t hdr;
+	uint8_t sig_id;
+	uint8_t nosp = 0;
+
+	if (frame->in)
+		pdu_color = COLOR_MAGENTA;
+	else
+		pdu_color = COLOR_BLUE;
+
+	if (!l2cap_frame_get_u8(frame, &hdr))
+		return false;
+
+	avdtp_frame->hdr = hdr;
+
+	/* Continue Packet || End Packet */
+	if (((hdr & 0x0c) == 0x08) || ((hdr & 0x0c) == 0x0c)) {
+		/* TODO: handle fragmentation */
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	}
+
+	/* Start Packet */
+	if ((hdr & 0x0c) == 0x04) {
+		if (!l2cap_frame_get_u8(frame, &nosp))
+			return false;
+	}
+
+	if (!l2cap_frame_get_u8(frame, &sig_id))
+		return false;
+
+	sig_id &= 0x3f;
+
+	avdtp_frame->sig_id = sig_id;
+
+	print_indent(6, pdu_color, "AVDTP: ", sigid2str(sig_id), COLOR_OFF,
+			" (0x%02x) %s (0x%02x) type 0x%02x label %d nosp %d",
+			sig_id, msgtype2str(hdr & 0x03), hdr & 0x03,
+			hdr & 0x0c, hdr >> 4, nosp);
+
+	/* Start Packet */
+	if ((hdr & 0x0c) == 0x04) {
+		/* TODO: handle fragmentation */
+		packet_hexdump(frame->data, frame->size);
+		return true;
+	}
+
+	/* General Reject */
+	if ((hdr & 0x03) == 0x03)
+		return true;
+
+	switch (sig_id) {
+	case AVDTP_DISCOVER:
+		return avdtp_discover(avdtp_frame);
+	case AVDTP_GET_CAPABILITIES:
+	case AVDTP_GET_ALL_CAPABILITIES:
+		return avdtp_get_capabilities(avdtp_frame);
+	case AVDTP_SET_CONFIGURATION:
+		return avdtp_set_configuration(avdtp_frame);
+	case AVDTP_GET_CONFIGURATION:
+		return avdtp_get_configuration(avdtp_frame);
+	case AVDTP_RECONFIGURE:
+		return avdtp_reconfigure(avdtp_frame);
+	case AVDTP_OPEN:
+		return avdtp_open(avdtp_frame);
+	case AVDTP_START:
+		return avdtp_start(avdtp_frame);
+	case AVDTP_CLOSE:
+		return avdtp_close(avdtp_frame);
+	case AVDTP_SUSPEND:
+		return avdtp_suspend(avdtp_frame);
+	case AVDTP_ABORT:
+		return avdtp_abort(avdtp_frame);
+	case AVDTP_SECURITY_CONTROL:
+		return avdtp_security_control(avdtp_frame);
+	case AVDTP_DELAYREPORT:
+		return avdtp_delayreport(avdtp_frame);
+	}
+
+	packet_hexdump(frame->data, frame->size);
+
+	return true;
+}
+
+void avdtp_packet(const struct l2cap_frame *frame)
+{
+	struct avdtp_frame avdtp_frame;
+	bool ret;
+
+	l2cap_frame_pull(&avdtp_frame.l2cap_frame, frame, 0);
+
+	switch (frame->seq_num) {
+	case 1:
+		ret = avdtp_signalling_packet(&avdtp_frame);
+		break;
+	default:
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	if (!ret) {
+		print_text(COLOR_ERROR, "PDU malformed");
+		packet_hexdump(frame->data, frame->size);
+	}
+}
diff --git a/bluez/repo/monitor/avdtp.h b/bluez/repo/monitor/avdtp.h
new file mode 100644
index 0000000..f77d82e
--- /dev/null
+++ b/bluez/repo/monitor/avdtp.h
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
+ *
+ *
+ *  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
+ *
+ */
+
+void avdtp_packet(const struct l2cap_frame *frame);
diff --git a/bluez/repo/monitor/bnep.c b/bluez/repo/monitor/bnep.c
new file mode 100644
index 0000000..01392e8
--- /dev/null
+++ b/bluez/repo/monitor/bnep.c
@@ -0,0 +1,482 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "lib/bluetooth.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 "bnep.h"
+
+#define GET_PKT_TYPE(type) (type & 0x7f)
+#define GET_EXTENSION(type) (type & 0x80)
+
+/* BNEP Extension Type */
+#define BNEP_EXTENSION_CONTROL		0x00
+
+#define BNEP_CONTROL			0x01
+
+uint16_t proto = 0x0000;
+
+struct bnep_frame {
+	uint8_t type;
+	int extension;
+	struct l2cap_frame l2cap_frame;
+};
+
+static bool get_macaddr(struct bnep_frame *bnep_frame, char *str)
+{
+	uint8_t addr[6];
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	int i;
+
+	for (i = 0; i < 6; i++)
+		if (!l2cap_frame_get_u8(frame, &addr[i]))
+			return false;
+
+	sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+		addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+	return true;
+}
+
+static bool bnep_general(struct bnep_frame *bnep_frame,
+					uint8_t indent,	int hdr_len)
+{
+	struct l2cap_frame *frame;
+	char src_addr[20], dest_addr[20];
+
+	if (!get_macaddr(bnep_frame, dest_addr))
+		return false;
+
+	if (!get_macaddr(bnep_frame, src_addr))
+		return false;
+
+	frame = &bnep_frame->l2cap_frame;
+
+	if (!l2cap_frame_get_be16(frame, &proto))
+		return false;
+
+	print_field("%*cdst %s src %s [proto 0x%04x] ", indent,
+					' ', dest_addr, src_addr, proto);
+
+	return true;
+
+}
+
+static bool cmd_nt_understood(struct bnep_frame *bnep_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	uint8_t ptype;
+
+	if (!l2cap_frame_get_u8(frame, &ptype))
+		return false;
+
+	print_field("%*cType: 0x%02x ", indent, ' ', ptype);
+
+	return true;
+}
+
+static bool setup_conn_req(struct bnep_frame *bnep_frame, uint8_t indent)
+{
+
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	uint8_t uuid_size;
+	uint32_t src_uuid = 0, dst_uuid = 0;
+
+	if (!l2cap_frame_get_u8(frame, &uuid_size))
+		return false;
+
+	print_field("%*cSize: 0x%02x ", indent, ' ', uuid_size);
+
+	switch (uuid_size) {
+	case 2:
+		if (!l2cap_frame_get_be16(frame, (uint16_t *) &dst_uuid))
+			return false;
+
+		if (!l2cap_frame_get_be16(frame, (uint16_t *) &src_uuid))
+			return false;
+		break;
+	case 4:
+		if (!l2cap_frame_get_be32(frame, &dst_uuid))
+			return false;
+
+		if (!l2cap_frame_get_be32(frame, &src_uuid))
+			return false;
+		break;
+	case 16:
+		if (!l2cap_frame_get_be32(frame, &dst_uuid))
+			return false;
+
+		l2cap_frame_pull(frame, frame, 12);
+
+		if (!l2cap_frame_get_be32(frame, &src_uuid))
+			return false;
+
+		l2cap_frame_pull(frame, frame, 12);
+		break;
+	default:
+		l2cap_frame_pull(frame, frame, (uuid_size * 2));
+		return true;
+	}
+
+	print_field("%*cDst: 0x%x(%s)", indent, ' ', dst_uuid,
+						uuid32_to_str(dst_uuid));
+	print_field("%*cSrc: 0x%x(%s)", indent, ' ', src_uuid,
+						uuid32_to_str(src_uuid));
+	return true;
+}
+
+static const char *value2str(uint16_t value)
+{
+	switch (value) {
+	case 0x00:
+		return "Operation Successful";
+	case 0x01:
+		return "Operation Failed - Invalid Dst Srv UUID";
+	case 0x02:
+		return "Operation Failed - Invalid Src Srv UUID";
+	case 0x03:
+		return "Operation Failed - Invalid Srv UUID size";
+	case 0x04:
+		return "Operation Failed - Conn not allowed";
+	default:
+		return "Unknown";
+	}
+}
+
+static bool print_rsp_msg(struct bnep_frame *bnep_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	uint16_t rsp_msg;
+
+	if (!l2cap_frame_get_be16(frame, &rsp_msg))
+		return false;
+
+	print_field("%*cRsp msg: %s(0x%04x) ", indent, ' ',
+					value2str(rsp_msg), rsp_msg);
+
+	return true;
+}
+
+static bool filter_nettype_req(struct bnep_frame *bnep_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	uint16_t length, start_range, end_range;
+	int i;
+
+	if (!l2cap_frame_get_be16(frame, &length))
+		return false;
+
+	print_field("%*cLength: 0x%04x", indent, ' ', length);
+
+	for (i = 0; i < length / 4; i++) {
+
+		if (!l2cap_frame_get_be16(frame, &start_range))
+			return false;
+
+		if (!l2cap_frame_get_be16(frame, &end_range))
+			return false;
+
+		print_field("%*c0x%04x - 0x%04x", indent, ' ',
+						start_range, end_range);
+	}
+
+	return true;
+}
+
+static bool filter_multaddr_req(struct bnep_frame *bnep_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	uint16_t length;
+	char start_addr[20], end_addr[20];
+	int i;
+
+	if (!l2cap_frame_get_be16(frame, &length))
+		return false;
+
+	print_field("%*cLength: 0x%04x", indent, ' ', length);
+
+	for (i = 0; i < length / 12; i++) {
+
+		if (!get_macaddr(bnep_frame, start_addr))
+			return false;
+
+		if (!get_macaddr(bnep_frame, end_addr))
+			return false;
+
+		print_field("%*c%s - %s", indent, ' ', start_addr, end_addr);
+	}
+
+	return true;
+}
+
+struct bnep_control_data {
+	uint8_t type;
+	const char *str;
+	bool (*func) (struct bnep_frame *frame, uint8_t indent);
+};
+
+static const struct bnep_control_data bnep_control_table[] = {
+	{ 0x00, "Command Not Understood",	cmd_nt_understood	},
+	{ 0x01, "Setup Conn Req",		setup_conn_req		},
+	{ 0x02, "Setup Conn Rsp",		print_rsp_msg		},
+	{ 0x03, "Filter NetType Set",		filter_nettype_req	},
+	{ 0x04, "Filter NetType Rsp",		print_rsp_msg		},
+	{ 0x05, "Filter MultAddr Set",		filter_multaddr_req	},
+	{ 0x06, "Filter MultAddr Rsp",		print_rsp_msg		},
+	{ }
+};
+
+static bool bnep_control(struct bnep_frame *bnep_frame,
+					uint8_t indent,	int hdr_len)
+{
+	uint8_t ctype;
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	const struct bnep_control_data *bnep_control_data = NULL;
+	const char *type_str;
+	int i;
+
+	if (!l2cap_frame_get_u8(frame, &ctype))
+		return false;
+
+	for (i = 0; bnep_control_table[i].str; i++) {
+		if (bnep_control_table[i].type == ctype) {
+			bnep_control_data = &bnep_control_table[i];
+			break;
+		}
+	}
+
+	if (bnep_control_data)
+		type_str = bnep_control_data->str;
+	else
+		type_str = "Unknown control type";
+
+	print_field("%*c%s (0x%02x) ", indent, ' ', type_str, ctype);
+
+	if (!bnep_control_data || !bnep_control_data->func) {
+		packet_hexdump(frame->data, hdr_len - 1);
+		l2cap_frame_pull(frame, frame, hdr_len - 1);
+		goto done;
+	}
+
+	if (!bnep_control_data->func(bnep_frame, indent+2))
+		return false;
+
+done:
+	return true;
+}
+
+static bool bnep_compressed(struct bnep_frame *bnep_frame,
+					uint8_t indent,	int hdr_len)
+{
+
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+
+	if (!l2cap_frame_get_be16(frame, &proto))
+		return false;
+
+	print_field("%*c[proto 0x%04x] ", indent, ' ', proto);
+
+	return true;
+}
+
+static bool bnep_src_only(struct bnep_frame *bnep_frame,
+					uint8_t indent,	int hdr_len)
+{
+
+	struct l2cap_frame *frame;
+	char src_addr[20];
+
+	if (!get_macaddr(bnep_frame, src_addr))
+		return false;
+
+	frame = &bnep_frame->l2cap_frame;
+
+	if (!l2cap_frame_get_be16(frame, &proto))
+		return false;
+
+	print_field("%*csrc %s [proto 0x%04x] ", indent,
+					' ', src_addr, proto);
+
+	return true;
+}
+
+static bool bnep_dst_only(struct bnep_frame *bnep_frame,
+					uint8_t indent,	int hdr_len)
+{
+
+	struct l2cap_frame *frame;
+	char dest_addr[20];
+
+	if (!get_macaddr(bnep_frame, dest_addr))
+		return false;
+
+	frame = &bnep_frame->l2cap_frame;
+
+	if (!l2cap_frame_get_be16(frame, &proto))
+		return false;
+
+	print_field("%*cdst %s [proto 0x%04x] ", indent,
+					' ', dest_addr, proto);
+
+	return true;
+}
+
+static bool bnep_eval_extension(struct bnep_frame *bnep_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &bnep_frame->l2cap_frame;
+	uint8_t type, length;
+	int extension;
+
+	if (!l2cap_frame_get_u8(frame, &type))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &length))
+		return false;
+
+	extension = GET_EXTENSION(type);
+	type = GET_PKT_TYPE(type);
+
+	switch (type) {
+	case BNEP_EXTENSION_CONTROL:
+		print_field("%*cExt Control(0x%02x|%s) len 0x%02x", indent,
+				' ', type, extension ? "1" : "0", length);
+		if (!bnep_control(bnep_frame, indent+2, length))
+			return false;
+		break;
+
+	default:
+		print_field("%*cExt Unknown(0x%02x|%s) len 0x%02x", indent,
+				' ', type, extension ? "1" : "0", length);
+		packet_hexdump(frame->data, length);
+		l2cap_frame_pull(frame, frame, length);
+	}
+
+	if (extension)
+		if (!bnep_eval_extension(bnep_frame, indent))
+			return false;
+
+	return true;
+}
+
+struct bnep_data {
+	uint8_t type;
+	const char *str;
+	bool (*func) (struct bnep_frame *frame, uint8_t indent, int hdr_len);
+};
+
+static const struct bnep_data bnep_table[] = {
+	{ 0x00, "General Ethernet",		bnep_general	},
+	{ 0x01, "Control",			bnep_control	},
+	{ 0x02, "Compressed Ethernet",		bnep_compressed	},
+	{ 0x03, "Compressed Ethernet SrcOnly",	bnep_src_only	},
+	{ 0x04, "Compressed Ethernet DestOnly",	bnep_dst_only	},
+	{ }
+};
+
+void bnep_packet(const struct l2cap_frame *frame)
+{
+	uint8_t type, indent = 1;
+	struct bnep_frame bnep_frame;
+	struct l2cap_frame *l2cap_frame;
+	const struct bnep_data *bnep_data = NULL;
+	const char *pdu_color, *pdu_str;
+	int i;
+
+	l2cap_frame_pull(&bnep_frame.l2cap_frame, frame, 0);
+	l2cap_frame = &bnep_frame.l2cap_frame;
+
+	if (!l2cap_frame_get_u8(l2cap_frame, &type))
+		goto fail;
+
+	bnep_frame.extension = GET_EXTENSION(type);
+	bnep_frame.type = GET_PKT_TYPE(type);
+
+	for (i = 0; bnep_table[i].str; i++) {
+		if (bnep_table[i].type == bnep_frame.type) {
+			bnep_data = &bnep_table[i];
+			break;
+		}
+	}
+
+	if (bnep_data) {
+		if (bnep_data->func) {
+			if (frame->in)
+				pdu_color = COLOR_MAGENTA;
+			else
+				pdu_color = COLOR_BLUE;
+		} else
+			pdu_color = COLOR_WHITE_BG;
+		pdu_str = bnep_data->str;
+	} else {
+		pdu_color = COLOR_WHITE_BG;
+		pdu_str = "Unknown packet type";
+	}
+
+	print_indent(6, pdu_color, "BNEP: ", pdu_str, COLOR_OFF,
+				" (0x%02x|%s)", bnep_frame.type,
+				bnep_frame.extension ? "1" : "0");
+
+	if (!bnep_data || !bnep_data->func) {
+		packet_hexdump(l2cap_frame->data, l2cap_frame->size);
+		return;
+	}
+
+	if (!bnep_data->func(&bnep_frame, indent, -1))
+		goto fail;
+
+	/* Extension info */
+	if (bnep_frame.extension)
+		if (!bnep_eval_extension(&bnep_frame, indent+2))
+			goto fail;
+
+	/* Control packet => No payload info */
+	if (bnep_frame.type == BNEP_CONTROL)
+		return;
+
+	/* TODO: Handle BNEP IP packet */
+	packet_hexdump(l2cap_frame->data, l2cap_frame->size);
+
+	return;
+
+fail:
+	print_text(COLOR_ERROR, "frame too short");
+	packet_hexdump(frame->data, frame->size);
+}
diff --git a/bluez/repo/monitor/bnep.h b/bluez/repo/monitor/bnep.h
new file mode 100644
index 0000000..38340d6
--- /dev/null
+++ b/bluez/repo/monitor/bnep.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+void bnep_packet(const struct l2cap_frame *frame);
diff --git a/bluez/repo/monitor/broadcom.c b/bluez/repo/monitor/broadcom.c
new file mode 100644
index 0000000..a3c3443
--- /dev/null
+++ b/bluez/repo/monitor/broadcom.c
@@ -0,0 +1,251 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 "src/shared/util.h"
+#include "display.h"
+#include "packet.h"
+#include "lmp.h"
+#include "ll.h"
+#include "vendor.h"
+#include "broadcom.h"
+
+static void print_status(uint8_t status)
+{
+	packet_print_error("Status", status);
+}
+
+static void null_cmd(const void *data, uint8_t size)
+{
+}
+
+static void status_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+
+	print_status(status);
+}
+
+static void write_bd_addr_cmd(const void *data, uint8_t size)
+{
+	packet_print_addr("Address", data, false);
+}
+
+static void enable_usb_hid_emulation_cmd(const void *data, uint8_t size)
+{
+	uint8_t enable = get_u8(data);
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Bluetooth mode";
+		break;
+	case 0x01:
+		str = "HID Mode";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Enable: %s (0x%2.2x)", str, enable);
+}
+
+static void write_ram_cmd(const void *data, uint8_t size)
+{
+	uint32_t addr = get_le32(data);
+
+	print_field("Address: 0x%8.8x", addr);
+
+	packet_hexdump(data + 4, size - 4);
+}
+
+static void launch_ram_cmd(const void *data, uint8_t size)
+{
+	uint32_t addr = get_le32(data);
+
+	print_field("Address: 0x%8.8x", addr);
+}
+
+static void read_vid_pid_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+	uint16_t vid = get_le16(data + 1);
+	uint16_t pid = get_le16(data + 3);
+
+	print_status(status);
+	print_field("Product: %4.4x:%4.4x", vid, pid);
+}
+
+static void read_verbose_version_info_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+	uint8_t chip_id = get_u8(data + 1);
+	uint8_t target_id = get_u8(data + 2);
+	uint16_t build_base = get_le16(data + 3);
+	uint16_t build_num = get_le16(data + 5);
+	const char *str;
+
+	print_status(status);
+	print_field("Chip ID: %u (0x%2.2x)", chip_id, chip_id);
+
+	switch (target_id) {
+	case 254:
+		str = "Invalid";
+		break;
+	case 255:
+		str = "Undefined";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Build target: %s (%u)", str, target_id);
+	print_field("Build baseline: %u (0x%4.4x)", build_base, build_base);
+	print_field("Build number: %u (0x%4.4x)", build_num, build_num);
+}
+
+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" },
+	{ 0x02e, "Download Minidriver",
+			null_cmd, 0, 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" },
+	{ 0x04c, "Write RAM",
+			write_ram_cmd, 4, false,
+			status_rsp, 1, true },
+	{ 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 },
+	{ 0x079, "Read Verbose Config Version Info",
+			null_cmd, 0, true,
+			read_verbose_version_info_rsp, 7, true },
+	{ }
+};
+
+const struct vendor_ocf *broadcom_vendor_ocf(uint16_t ocf)
+{
+	int i;
+
+	for (i = 0; vendor_ocf_table[i].str; i++) {
+		if (vendor_ocf_table[i].ocf == ocf)
+			return &vendor_ocf_table[i];
+	}
+
+	return NULL;
+}
+
+void broadcom_lm_diag(const void *data, uint8_t size)
+{
+	uint8_t type;
+	uint32_t clock;
+	const uint8_t *addr;
+	const char *str;
+
+	if (size != 63) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	type = *((uint8_t *) data);
+	clock = get_be32(data + 1);
+
+	switch (type) {
+	case 0x00:
+		str = "LMP sent";
+		break;
+	case 0x01:
+		str = "LMP receive";
+		break;
+	case 0x80:
+		str = "LL sent";
+		break;
+	case 0x81:
+		str = "LL receive";
+		break;
+	default:
+		str = "Unknown";
+		break;
+	}
+
+	print_field("Type: %s (%u)", str, type);
+	print_field("Clock: 0x%8.8x", clock);
+
+	switch (type) {
+	case 0x00:
+		addr = data + 5;
+		print_field("Address: --:--:%2.2X:%2.2X:%2.2X:%2.2X",
+					addr[0], addr[1], addr[2], addr[3]);
+		packet_hexdump(data + 9, 1);
+		lmp_packet(data + 10, size - 10, true);
+		break;
+	case 0x01:
+		addr = data + 5;
+		print_field("Address: --:--:%2.2X:%2.2X:%2.2X:%2.2X",
+					addr[0], addr[1], addr[2], addr[3]);
+		packet_hexdump(data + 9, 4);
+		lmp_packet(data + 13, size - 13, true);
+		break;
+	case 0x80:
+	case 0x81:
+		packet_hexdump(data + 5, 7);
+		llcp_packet(data + 12, size - 12, true);
+		break;
+	default:
+		packet_hexdump(data + 9, size - 9);
+		break;
+	}
+}
+
+static const struct vendor_evt vendor_evt_table[] = {
+	{ }
+};
+
+const struct vendor_evt *broadcom_vendor_evt(uint8_t evt)
+{
+	int i;
+
+	for (i = 0; vendor_evt_table[i].str; i++) {
+		if (vendor_evt_table[i].evt == evt)
+			return &vendor_evt_table[i];
+	}
+
+	return NULL;
+}
diff --git a/bluez/repo/monitor/broadcom.h b/bluez/repo/monitor/broadcom.h
new file mode 100644
index 0000000..ceda0e1
--- /dev/null
+++ b/bluez/repo/monitor/broadcom.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+struct vendor_ocf;
+struct vendor_evt;
+
+const struct vendor_ocf *broadcom_vendor_ocf(uint16_t ocf);
+const struct vendor_evt *broadcom_vendor_evt(uint8_t evt);
+void broadcom_lm_diag(const void *data, uint8_t size);
diff --git a/bluez/repo/monitor/bt.h b/bluez/repo/monitor/bt.h
new file mode 100644
index 0000000..1a21592
--- /dev/null
+++ b/bluez/repo/monitor/bt.h
@@ -0,0 +1,3136 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+struct bt_ll_hdr {
+	uint8_t  preamble;
+	uint32_t access_addr;
+} __attribute__ ((packed));
+
+#define BT_LL_CONN_UPDATE_REQ	0x00
+struct bt_ll_conn_update_req {
+	uint8_t  win_size;
+	uint16_t win_offset;
+	uint16_t interval;
+	uint16_t latency;
+	uint16_t timeout;
+	uint16_t instant;
+} __attribute__ ((packed));
+
+#define BT_LL_CHANNEL_MAP_REQ	0x01
+struct bt_ll_channel_map_req {
+	uint8_t  map[5];
+	uint16_t instant;
+} __attribute__ ((packed));
+
+#define BT_LL_TERMINATE_IND	0x02
+struct bt_ll_terminate_ind {
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LL_ENC_REQ		0x03
+struct bt_ll_enc_req {
+	uint64_t rand;
+	uint16_t ediv;
+	uint64_t skd;
+	uint32_t iv;
+} __attribute__ ((packed));
+
+#define BT_LL_ENC_RSP		0x04
+struct bt_ll_enc_rsp {
+	uint64_t skd;
+	uint32_t iv;
+} __attribute__ ((packed));
+
+#define BT_LL_START_ENC_REQ	0x05
+
+#define BT_LL_START_ENC_RSP	0x06
+
+#define BT_LL_UNKNOWN_RSP	0x07
+struct bt_ll_unknown_rsp {
+	uint8_t  type;
+} __attribute__ ((packed));
+
+#define BT_LL_FEATURE_REQ	0x08
+struct bt_ll_feature_req {
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LL_FEATURE_RSP	0x09
+struct bt_ll_feature_rsp {
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LL_PAUSE_ENC_REQ	0x0a
+
+#define BT_LL_PAUSE_ENC_RSP	0x0b
+
+#define BT_LL_VERSION_IND	0x0c
+struct bt_ll_version_ind {
+	uint8_t  version;
+	uint16_t company;
+	uint16_t subversion;
+} __attribute__ ((packed));
+
+#define BT_LL_REJECT_IND	0x0d
+struct bt_ll_reject_ind {
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LL_SLAVE_FEATURE_REQ	0x0e
+struct bt_ll_slave_feature_req {
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LL_CONN_PARAM_REQ	0x0f
+
+#define BT_LL_CONN_PARAM_RSP	0x10
+
+#define BT_LL_REJECT_IND_EXT	0x11
+struct bt_ll_reject_ind_ext {
+	uint8_t  opcode;
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LL_PING_REQ		0x12
+
+#define BT_LL_PING_RSP		0x13
+
+#define BT_LL_LENGTH_REQ	0x14
+
+#define BT_LL_LENGTH_RSP	0x15
+
+#define LMP_ESC4(x) ((127 << 8) | (x))
+
+#define BT_LMP_NAME_REQ			1
+struct bt_lmp_name_req {
+	uint8_t  offset;
+} __attribute__ ((packed));
+
+#define BT_LMP_NAME_RSP			2
+struct bt_lmp_name_rsp {
+	uint8_t  offset;
+	uint8_t  length;
+	uint8_t  fragment[14];
+} __attribute__ ((packed));
+
+#define BT_LMP_ACCEPTED			3
+struct bt_lmp_accepted {
+	uint8_t  opcode;
+} __attribute__ ((packed));
+
+#define BT_LMP_NOT_ACCEPTED		4
+struct bt_lmp_not_accepted {
+	uint8_t  opcode;
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LMP_CLKOFFSET_REQ		5
+
+#define BT_LMP_CLKOFFSET_RSP		6
+struct bt_lmp_clkoffset_rsp {
+	uint16_t offset;
+} __attribute__ ((packed));
+
+#define BT_LMP_DETACH			7
+struct bt_lmp_detach {
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LMP_AU_RAND			11
+struct bt_lmp_au_rand {
+	uint8_t  number[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_SRES			12
+struct bt_lmp_sres {
+	uint8_t  response[4];
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCRYPTION_MODE_REQ	15
+struct bt_lmp_encryption_mode_req {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCRYPTION_KEY_SIZE_REQ	16
+struct bt_lmp_encryption_key_size_req {
+	uint8_t  key_size;
+} __attribute__ ((packed));
+
+#define BT_LMP_START_ENCRYPTION_REQ	17
+struct bt_lmp_start_encryption_req {
+	uint8_t  number[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_STOP_ENCRYPTION_REQ	18
+
+#define BT_LMP_SWITCH_REQ		19
+struct bt_lmp_switch_req {
+	uint32_t instant;
+} __attribute__ ((packed));
+
+#define BT_LMP_UNSNIFF_REQ		24
+
+#define BT_LMP_MAX_POWER		33
+
+#define BT_LMP_MIN_POWER		34
+
+#define BT_LMP_AUTO_RATE		35
+
+#define BT_LMP_PREFERRED_RATE		36
+struct bt_lmp_preferred_rate {
+	uint8_t  rate;
+} __attribute__ ((packed));
+
+#define BT_LMP_VERSION_REQ		37
+struct bt_lmp_version_req {
+	uint8_t  version;
+	uint16_t company;
+	uint16_t subversion;
+} __attribute__ ((packed));
+
+#define BT_LMP_VERSION_RES		38
+struct bt_lmp_version_res {
+	uint8_t  version;
+	uint16_t company;
+	uint16_t subversion;
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_REQ		39
+struct bt_lmp_features_req {
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_RES		40
+struct bt_lmp_features_res {
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_MAX_SLOT			45
+struct bt_lmp_max_slot {
+	uint8_t  slots;
+} __attribute__ ((packed));
+
+#define BT_LMP_MAX_SLOT_REQ		46
+struct bt_lmp_max_slot_req {
+	uint8_t  slots;
+} __attribute__ ((packed));
+
+#define BT_LMP_TIMING_ACCURACY_REQ	47
+
+#define BT_LMP_TIMING_ACCURACY_RES	48
+struct bt_lmp_timing_accuracy_res {
+	uint8_t  drift;
+	uint8_t  jitter;
+} __attribute__ ((packed));
+
+#define BT_LMP_SETUP_COMPLETE		49
+
+#define BT_LMP_USE_SEMI_PERMANENT_KEY	50
+
+#define BT_LMP_HOST_CONNECTION_REQ	51
+
+#define BT_LMP_SLOT_OFFSET		52
+struct bt_lmp_slot_offset {
+	uint16_t offset;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_LMP_PAGE_SCAN_MODE_REQ	54
+struct bt_lmp_page_scan_mode_req {
+	uint8_t  scheme;
+	uint8_t  settings;
+} __attribute__ ((packed));
+
+#define BT_LMP_TEST_ACTIVATE		56
+
+#define BT_LMP_ENCRYPTION_KEY_SIZE_MASK_REQ	58
+
+#define BT_LMP_SET_AFH			60
+struct bt_lmp_set_afh {
+	uint32_t instant;
+	uint8_t  mode;
+	uint8_t  map[10];
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCAPSULATED_HEADER	61
+struct bt_lmp_encapsulated_header {
+	uint8_t  major;
+	uint8_t  minor;
+	uint8_t  length;
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCAPSULATED_PAYLOAD	62
+struct bt_lmp_encapsulated_payload {
+	uint8_t  data[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_SIMPLE_PAIRING_CONFIRM	63
+struct bt_lmp_simple_pairing_confirm {
+	uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_SIMPLE_PAIRING_NUMBER	64
+struct bt_lmp_simple_pairing_number {
+	uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_DHKEY_CHECK		65
+struct bt_lmp_dhkey_check {
+	uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_PAUSE_ENCRYPTION_AES_REQ	66
+
+#define BT_LMP_ACCEPTED_EXT		LMP_ESC4(1)
+struct bt_lmp_accepted_ext {
+	uint8_t  escape;
+	uint8_t  opcode;
+} __attribute__ ((packed));
+
+#define BT_LMP_NOT_ACCEPTED_EXT		LMP_ESC4(2)
+struct bt_lmp_not_accepted_ext {
+	uint8_t  escape;
+	uint8_t  opcode;
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_REQ_EXT		LMP_ESC4(3)
+struct bt_lmp_features_req_ext {
+	uint8_t  page;
+	uint8_t  max_page;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_RES_EXT		LMP_ESC4(4)
+struct bt_lmp_features_res_ext {
+	uint8_t  page;
+	uint8_t  max_page;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_PACKET_TYPE_TABLE_REQ	LMP_ESC4(11)
+struct bt_lmp_packet_type_table_req {
+	uint8_t  table;
+} __attribute__ ((packed));
+
+#define BT_LMP_CHANNEL_CLASSIFICATION_REQ	LMP_ESC4(16)
+struct bt_lmp_channel_classification_req {
+	uint8_t  mode;
+	uint16_t min_interval;
+	uint16_t max_interval;
+} __attribute__ ((packed));
+
+#define BT_LMP_CHANNEL_CLASSIFICATION	LMP_ESC4(17)
+struct bt_lmp_channel_classification {
+	uint8_t  classification[10];
+} __attribute__ ((packed));
+
+#define BT_LMP_PAUSE_ENCRYPTION_REQ	LMP_ESC4(23)
+
+#define BT_LMP_RESUME_ENCRYPTION_REQ	LMP_ESC4(24)
+
+#define BT_LMP_IO_CAPABILITY_REQ	LMP_ESC4(25)
+struct bt_lmp_io_capability_req {
+	uint8_t  capability;
+	uint8_t  oob_data;
+	uint8_t  authentication;
+} __attribute__ ((packed));
+
+#define BT_LMP_IO_CAPABILITY_RES	LMP_ESC4(26)
+struct bt_lmp_io_capability_res {
+	uint8_t  capability;
+	uint8_t  oob_data;
+	uint8_t  authentication;
+} __attribute__ ((packed));
+
+#define BT_LMP_NUMERIC_COMPARISON_FAILED	LMP_ESC(27)
+
+#define BT_LMP_PASSKEY_FAILED		LMP_ESC4(28)
+
+#define BT_LMP_OOB_FAILED		LMP_ESC(29)
+
+#define BT_LMP_POWER_CONTROL_REQ	LMP_ESC4(31)
+struct bt_lmp_power_control_req {
+	uint8_t  request;
+} __attribute__ ((packed));
+
+#define BT_LMP_POWER_CONTROL_RES	LMP_ESC4(32)
+struct bt_lmp_power_control_res {
+	uint8_t  response;
+} __attribute__ ((packed));
+
+#define BT_LMP_PING_REQ			LMP_ESC4(33)
+
+#define BT_LMP_PING_RES			LMP_ESC4(34)
+
+#define BT_H4_CMD_PKT	0x01
+#define BT_H4_ACL_PKT	0x02
+#define BT_H4_SCO_PKT	0x03
+#define BT_H4_EVT_PKT	0x04
+
+struct bt_hci_cmd_hdr {
+	uint16_t opcode;
+	uint8_t  plen;
+} __attribute__ ((packed));
+
+struct bt_hci_acl_hdr {
+	uint16_t handle;
+	uint16_t dlen;
+} __attribute__ ((packed));
+
+struct bt_hci_sco_hdr {
+	uint16_t handle;
+	uint8_t  dlen;
+} __attribute__ ((packed));
+
+struct bt_hci_evt_hdr {
+	uint8_t  evt;
+	uint8_t  plen;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_NOP				0x0000
+
+#define BT_HCI_CMD_INQUIRY			0x0401
+struct bt_hci_cmd_inquiry {
+	uint8_t  lap[3];
+	uint8_t  length;
+	uint8_t  num_resp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_INQUIRY_CANCEL		0x0402
+
+#define BT_HCI_CMD_PERIODIC_INQUIRY		0x0403
+struct bt_hci_cmd_periodic_inquiry {
+	uint16_t max_period;
+	uint16_t min_period;
+	uint8_t  lap[3];
+	uint8_t  length;
+	uint8_t  num_resp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_EXIT_PERIODIC_INQUIRY	0x0404
+
+#define BT_HCI_CMD_CREATE_CONN			0x0405
+struct bt_hci_cmd_create_conn {
+	uint8_t  bdaddr[6];
+	uint16_t pkt_type;
+	uint8_t  pscan_rep_mode;
+	uint8_t  pscan_mode;
+	uint16_t clock_offset;
+	uint8_t  role_switch;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DISCONNECT			0x0406
+struct bt_hci_cmd_disconnect {
+	uint16_t handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ADD_SCO_CONN			0x0407
+struct bt_hci_cmd_add_sco_conn {
+	uint16_t handle;
+	uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CREATE_CONN_CANCEL		0x0408
+struct bt_hci_cmd_create_conn_cancel {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ACCEPT_CONN_REQUEST		0x0409
+struct bt_hci_cmd_accept_conn_request {
+	uint8_t  bdaddr[6];
+	uint8_t  role;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REJECT_CONN_REQUEST		0x040a
+struct bt_hci_cmd_reject_conn_request {
+	uint8_t  bdaddr[6];
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LINK_KEY_REQUEST_REPLY	0x040b
+struct bt_hci_cmd_link_key_request_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  link_key[16];
+} __attribute__ ((packed));
+struct bt_hci_rsp_link_key_request_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY	0x040c
+struct bt_hci_cmd_link_key_request_neg_reply {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_link_key_request_neg_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_PIN_CODE_REQUEST_REPLY	0x040d
+struct bt_hci_cmd_pin_code_request_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  pin_len;
+	uint8_t  pin_code[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY	0x040e
+struct bt_hci_cmd_pin_code_request_neg_reply {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_pin_code_request_neg_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CHANGE_CONN_PKT_TYPE		0x040f
+struct bt_hci_cmd_change_conn_pkt_type {
+	uint16_t handle;
+	uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_AUTH_REQUESTED		0x0411
+struct bt_hci_cmd_auth_requested {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_CONN_ENCRYPT		0x0413
+struct bt_hci_cmd_set_conn_encrypt {
+	uint16_t handle;
+	uint8_t  encr_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CHANGE_CONN_LINK_KEY		0x0415
+struct bt_hci_cmd_change_conn_link_key {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_MASTER_LINK_KEY		0x0417
+struct bt_hci_cmd_master_link_key {
+	uint8_t  key_flag;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_NAME_REQUEST		0x0419
+struct bt_hci_cmd_remote_name_request {
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_rep_mode;
+	uint8_t  pscan_mode;
+	uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL	0x041a
+struct bt_hci_cmd_remote_name_request_cancel {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_remote_name_request_cancel {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_FEATURES		0x041b
+struct bt_hci_cmd_read_remote_features {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_EXT_FEATURES	0x041c
+struct bt_hci_cmd_read_remote_ext_features {
+	uint16_t handle;
+	uint8_t  page;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_VERSION		0x041d
+struct bt_hci_cmd_read_remote_version {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CLOCK_OFFSET		0x041f
+struct bt_hci_cmd_read_clock_offset {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LMP_HANDLE		0x0420
+struct bt_hci_cmd_read_lmp_handle {
+	uint16_t  handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_lmp_handle {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  lmp_handle;
+	uint32_t reserved;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SETUP_SYNC_CONN		0x0428
+struct bt_hci_cmd_setup_sync_conn {
+	uint16_t handle;
+	uint32_t tx_bandwidth;
+	uint32_t rx_bandwidth;
+	uint16_t max_latency;
+	uint16_t voice_setting;
+	uint8_t  retrans_effort;
+	uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ACCEPT_SYNC_CONN_REQUEST	0x0429
+struct bt_hci_cmd_accept_sync_conn_request {
+	uint8_t  bdaddr[6];
+	uint32_t tx_bandwidth;
+	uint32_t rx_bandwidth;
+	uint16_t max_latency;
+	uint16_t voice_setting;
+	uint8_t  retrans_effort;
+	uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REJECT_SYNC_CONN_REQUEST	0x042a
+struct bt_hci_cmd_reject_sync_conn_request {
+	uint8_t  bdaddr[6];
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY		0x042b
+struct bt_hci_cmd_io_capability_request_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  capability;
+	uint8_t  oob_data;
+	uint8_t  authentication;
+} __attribute__ ((packed));
+struct bt_hci_rsp_io_capability_request_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY		0x042c
+struct bt_hci_cmd_user_confirm_request_reply {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_user_confirm_request_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY	0x042d
+struct bt_hci_cmd_user_confirm_request_neg_reply {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_user_confirm_request_neg_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_USER_PASSKEY_REQUEST_REPLY		0x042e
+struct bt_hci_cmd_user_passkey_request_reply {
+	uint8_t  bdaddr[6];
+	uint32_t passkey;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_USER_PASSKEY_REQUEST_NEG_REPLY	0x042f
+struct bt_hci_cmd_user_passkey_request_neg_reply {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_OOB_DATA_REQUEST_REPLY	0x0430
+struct bt_hci_cmd_remote_oob_data_request_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  hash[16];
+	uint8_t  randomizer[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_OOB_DATA_REQUEST_NEG_REPLY	0x0433
+struct bt_hci_cmd_remote_oob_data_request_neg_reply {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY	0x0434
+struct bt_hci_cmd_io_capability_request_neg_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  reason;
+} __attribute__ ((packed));
+struct bt_hci_rsp_io_capability_request_neg_reply {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CREATE_PHY_LINK		0x0435
+struct bt_hci_cmd_create_phy_link {
+	uint8_t  phy_handle;
+	uint8_t  key_len;
+	uint8_t  key_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ACCEPT_PHY_LINK		0x0436
+struct bt_hci_cmd_accept_phy_link {
+	uint8_t  phy_handle;
+	uint8_t  key_len;
+	uint8_t  key_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DISCONN_PHY_LINK		0x0437
+struct bt_hci_cmd_disconn_phy_link {
+	uint8_t  phy_handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CREATE_LOGIC_LINK		0x0438
+struct bt_hci_cmd_create_logic_link {
+	uint8_t  phy_handle;
+	uint8_t  tx_flow_spec[16];
+	uint8_t  rx_flow_spec[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ACCEPT_LOGIC_LINK		0x0439
+struct bt_hci_cmd_accept_logic_link {
+	uint8_t  phy_handle;
+	uint8_t  tx_flow_spec[16];
+	uint8_t  rx_flow_spec[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DISCONN_LOGIC_LINK		0x043a
+struct bt_hci_cmd_disconn_logic_link {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LOGIC_LINK_CANCEL		0x043b
+struct bt_hci_cmd_logic_link_cancel {
+	uint8_t  phy_handle;
+	uint8_t  flow_spec;
+} __attribute__ ((packed));
+struct bt_hci_rsp_logic_link_cancel {
+	uint8_t  status;
+	uint8_t  phy_handle;
+	uint8_t  flow_spec;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_FLOW_SPEC_MODIFY		0x043c
+struct bt_hci_cmd_flow_spec_modify {
+	uint16_t handle;
+	uint8_t  tx_flow_spec[16];
+	uint8_t  rx_flow_spec[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ENHANCED_SETUP_SYNC_CONN	0x043d
+struct bt_hci_cmd_enhanced_setup_sync_conn {
+	uint16_t handle;
+	uint32_t tx_bandwidth;
+	uint32_t rx_bandwidth;
+	uint8_t  tx_coding_format[5];
+	uint8_t  rx_coding_format[5];
+	uint16_t tx_codec_frame_size;
+	uint16_t rx_codec_frame_size;
+	uint32_t input_bandwidth;
+	uint32_t output_bandwidth;
+	uint8_t  input_coding_format[5];
+	uint8_t  output_coding_format[5];
+	uint16_t input_coded_data_size;
+	uint16_t output_coded_data_size;
+	uint8_t  input_pcm_data_format;
+	uint8_t  output_pcm_data_format;
+	uint8_t  input_pcm_msb_position;
+	uint8_t  output_pcm_msb_position;
+	uint8_t  input_data_path;
+	uint8_t  output_data_path;
+	uint8_t  input_unit_size;
+	uint8_t  output_unit_size;
+	uint16_t max_latency;
+	uint16_t pkt_type;
+	uint8_t  retrans_effort;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ENHANCED_ACCEPT_SYNC_CONN_REQUEST	0x043e
+struct bt_hci_cmd_enhanced_accept_sync_conn_request {
+	uint8_t  bdaddr[6];
+	uint32_t tx_bandwidth;
+	uint32_t rx_bandwidth;
+	uint8_t  tx_coding_format[5];
+	uint8_t  rx_coding_format[5];
+	uint16_t tx_codec_frame_size;
+	uint16_t rx_codec_frame_size;
+	uint32_t input_bandwidth;
+	uint32_t output_bandwidth;
+	uint8_t  input_coding_format[5];
+	uint8_t  output_coding_format[5];
+	uint16_t input_coded_data_size;
+	uint16_t output_coded_data_size;
+	uint8_t  input_pcm_data_format;
+	uint8_t  output_pcm_data_format;
+	uint8_t  input_pcm_msb_position;
+	uint8_t  output_pcm_msb_position;
+	uint8_t  input_data_path;
+	uint8_t  output_data_path;
+	uint8_t  input_unit_size;
+	uint8_t  output_unit_size;
+	uint16_t max_latency;
+	uint16_t pkt_type;
+	uint8_t  retrans_effort;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_TRUNCATED_PAGE		0x043f
+struct bt_hci_cmd_truncated_page {
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_rep_mode;
+	uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_TRUNCATED_PAGE_CANCEL	0x0440
+struct bt_hci_cmd_truncated_page_cancel {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_SLAVE_BROADCAST		0x0441
+struct bt_hci_cmd_set_slave_broadcast {
+	uint8_t  enable;
+	uint8_t  lt_addr;
+	uint8_t  lpo_allowed;
+	uint16_t pkt_type;
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_slave_broadcast {
+	uint8_t  status;
+	uint8_t  lt_addr;
+	uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE	0x0442
+struct bt_hci_cmd_set_slave_broadcast_receive {
+	uint8_t  enable;
+	uint8_t  bdaddr[6];
+	uint8_t  lt_addr;
+	uint16_t interval;
+	uint32_t offset;
+	uint32_t instant;
+	uint16_t timeout;
+	uint8_t  accuracy;
+	uint8_t  skip;
+	uint16_t pkt_type;
+	uint8_t  map[10];
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_slave_broadcast_receive {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_START_SYNC_TRAIN		0x0443
+
+#define BT_HCI_CMD_RECEIVE_SYNC_TRAIN		0x0444
+struct bt_hci_cmd_receive_sync_train {
+	uint8_t  bdaddr[6];
+	uint16_t timeout;
+	uint16_t window;
+	uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_OOB_EXT_DATA_REQUEST_REPLY	0x0445
+struct bt_hci_cmd_remote_oob_ext_data_request_reply {
+	uint8_t  bdaddr[6];
+	uint8_t  hash192[16];
+	uint8_t  randomizer192[16];
+	uint8_t  hash256[16];
+	uint8_t  randomizer256[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_HOLD_MODE			0x0801
+struct bt_hci_cmd_hold_mode {
+	uint16_t handle;
+	uint16_t max_interval;
+	uint16_t min_interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SNIFF_MODE			0x0803
+struct bt_hci_cmd_sniff_mode {
+	uint16_t handle;
+	uint16_t max_interval;
+	uint16_t min_interval;
+	uint16_t attempt;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_EXIT_SNIFF_MODE		0x0804
+struct bt_hci_cmd_exit_sniff_mode {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_PARK_STATE			0x0805
+struct bt_hci_cmd_park_state {
+	uint16_t handle;
+	uint16_t max_interval;
+	uint16_t min_interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_EXIT_PARK_STATE		0x0806
+struct bt_hci_cmd_exit_park_state {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_QOS_SETUP			0x0807
+struct bt_hci_cmd_qos_setup {
+	uint16_t handle;
+	uint8_t  flags;
+	uint8_t  service_type;
+	uint32_t token_rate;
+	uint32_t peak_bandwidth;
+	uint32_t latency;
+	uint32_t delay_variation;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ROLE_DISCOVERY		0x0809
+struct bt_hci_cmd_role_discovery {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_role_discovery {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  role;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SWITCH_ROLE			0x080b
+struct bt_hci_cmd_switch_role {
+	uint8_t  bdaddr[6];
+	uint8_t  role;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LINK_POLICY		0x080c
+struct bt_hci_cmd_read_link_policy {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_link_policy {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LINK_POLICY		0x080d
+struct bt_hci_cmd_write_link_policy {
+	uint16_t handle;
+	uint16_t policy;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_link_policy {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_DEFAULT_LINK_POLICY	0x080e
+struct bt_hci_rsp_read_default_link_policy {
+	uint8_t  status;
+	uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY	0x080f
+struct bt_hci_cmd_write_default_link_policy {
+	uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_FLOW_SPEC			0x0810
+struct bt_hci_cmd_flow_spec {
+	uint16_t handle;
+	uint8_t  flags;
+	uint8_t  direction;
+	uint8_t  service_type;
+	uint32_t token_rate;
+	uint32_t token_bucket_size;
+	uint32_t peak_bandwidth;
+	uint32_t access_latency;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SNIFF_SUBRATING		0x0811
+struct bt_hci_cmd_sniff_subrating {
+	uint16_t handle;
+	uint16_t max_latency;
+	uint16_t min_remote_timeout;
+	uint16_t min_local_timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_sniff_subrating {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_EVENT_MASK		0x0c01
+struct bt_hci_cmd_set_event_mask {
+	uint8_t  mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_RESET			0x0c03
+
+#define BT_HCI_CMD_SET_EVENT_FILTER		0x0c05
+struct bt_hci_cmd_set_event_filter {
+	uint8_t  type;
+	uint8_t  cond_type;
+	uint8_t  cond[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_FLUSH			0x0c08
+struct bt_hci_cmd_flush {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_flush {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PIN_TYPE		0x0c09
+struct bt_hci_rsp_read_pin_type {
+	uint8_t  status;
+	uint8_t  pin_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PIN_TYPE		0x0c0a
+struct bt_hci_cmd_write_pin_type {
+	uint8_t  pin_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CREATE_NEW_UNIT_KEY		0x0c0b
+
+#define BT_HCI_CMD_READ_STORED_LINK_KEY		0x0c0d
+struct bt_hci_cmd_read_stored_link_key {
+	uint8_t  bdaddr[6];
+	uint8_t  read_all;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_stored_link_key {
+	uint8_t  status;
+	uint16_t max_num_keys;
+	uint16_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_STORED_LINK_KEY	0x0c11
+struct bt_hci_cmd_write_stored_link_key {
+	uint8_t  num_keys;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_stored_link_key {
+	uint8_t  status;
+	uint8_t  num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DELETE_STORED_LINK_KEY	0x0c12
+struct bt_hci_cmd_delete_stored_link_key {
+	uint8_t  bdaddr[6];
+	uint8_t  delete_all;
+} __attribute__ ((packed));
+struct bt_hci_rsp_delete_stored_link_key {
+	uint8_t  status;
+	uint16_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LOCAL_NAME		0x0c13
+struct bt_hci_cmd_write_local_name {
+	uint8_t  name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_NAME		0x0c14
+struct bt_hci_rsp_read_local_name {
+	uint8_t  status;
+	uint8_t  name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT	0x0c15
+struct bt_hci_rsp_read_conn_accept_timeout {
+	uint8_t  status;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT	0x0c16
+struct bt_hci_cmd_write_conn_accept_timeout {
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_TIMEOUT		0x0c17
+struct bt_hci_rsp_read_page_timeout {
+	uint8_t  status;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_TIMEOUT		0x0c18
+struct bt_hci_cmd_write_page_timeout {
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SCAN_ENABLE		0x0c19
+struct bt_hci_rsp_read_scan_enable {
+	uint8_t  status;
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SCAN_ENABLE		0x0c1a
+struct bt_hci_cmd_write_scan_enable {
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_SCAN_ACTIVITY	0x0c1b
+struct bt_hci_rsp_read_page_scan_activity {
+	uint8_t  status;
+	uint16_t interval;
+	uint16_t window;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_SCAN_ACTIVITY	0x0c1c
+struct bt_hci_cmd_write_page_scan_activity {
+	uint16_t interval;
+	uint16_t window;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_SCAN_ACTIVITY	0x0c1d
+struct bt_hci_rsp_read_inquiry_scan_activity {
+	uint8_t  status;
+	uint16_t interval;
+	uint16_t window;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_INQUIRY_SCAN_ACTIVITY	0x0c1e
+struct bt_hci_cmd_write_inquiry_scan_activity {
+	uint16_t interval;
+	uint16_t window;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AUTH_ENABLE		0x0c1f
+struct bt_hci_rsp_read_auth_enable {
+	uint8_t  status;
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AUTH_ENABLE		0x0c20
+struct bt_hci_cmd_write_auth_enable {
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_ENCRYPT_MODE		0x0c21
+struct bt_hci_rsp_read_encrypt_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_ENCRYPT_MODE		0x0c22
+struct bt_hci_cmd_write_encrypt_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CLASS_OF_DEV		0x0c23
+struct bt_hci_rsp_read_class_of_dev {
+	uint8_t  status;
+	uint8_t  dev_class[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CLASS_OF_DEV		0x0c24
+struct bt_hci_cmd_write_class_of_dev {
+	uint8_t  dev_class[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_VOICE_SETTING		0x0c25
+struct bt_hci_rsp_read_voice_setting {
+	uint8_t  status;
+	uint16_t setting;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_VOICE_SETTING		0x0c26
+struct bt_hci_cmd_write_voice_setting {
+	uint16_t setting;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AUTO_FLUSH_TIMEOUT	0x0c27
+struct bt_hci_cmd_read_auto_flush_timeout {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_auto_flush_timeout {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AUTO_FLUSH_TIMEOUT	0x0c28
+struct bt_hci_cmd_write_auto_flush_timeout {
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_auto_flush_timeout {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_NUM_BROADCAST_RETRANS	0x0c29
+struct bt_hci_rsp_read_num_broadcast_retrans {
+	uint8_t  status;
+	uint8_t  num_retrans;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_NUM_BROADCAST_RETRANS	0x0c2a
+struct bt_hci_cmd_write_num_broadcast_retrans {
+	uint8_t  num_retrans;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_HOLD_MODE_ACTIVITY	0x0c2b
+struct bt_hci_rsp_read_hold_mode_activity {
+	uint8_t  status;
+	uint8_t  activity;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_HOLD_MODE_ACTIVITY	0x0c2c
+struct bt_hci_cmd_write_hold_mode_activity {
+	uint8_t  activity;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_TX_POWER		0x0c2d
+struct bt_hci_cmd_read_tx_power {
+	uint16_t handle;
+	uint8_t  type;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_tx_power {
+	uint8_t  status;
+	uint16_t handle;
+	int8_t   level;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SYNC_FLOW_CONTROL	0x0c2e
+struct bt_hci_rsp_read_sync_flow_control {
+	uint8_t  status;
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SYNC_FLOW_CONTROL	0x0c2f
+struct bt_hci_cmd_write_sync_flow_control {
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_HOST_FLOW_CONTROL	0x0c31
+struct bt_hci_cmd_set_host_flow_control {
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_HOST_BUFFER_SIZE		0x0c33
+struct bt_hci_cmd_host_buffer_size {
+	uint16_t acl_mtu;
+	uint8_t  sco_mtu;
+	uint16_t acl_max_pkt;
+	uint16_t sco_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_HOST_NUM_COMPLETED_PACKETS	0x0c35
+struct bt_hci_cmd_host_num_completed_packets {
+	uint8_t  num_handles;
+	uint16_t handle;
+	uint16_t count;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LINK_SUPV_TIMEOUT	0x0c36
+struct bt_hci_cmd_read_link_supv_timeout {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_link_supv_timeout {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LINK_SUPV_TIMEOUT	0x0c37
+struct bt_hci_cmd_write_link_supv_timeout {
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_link_supv_timeout {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_NUM_SUPPORTED_IAC	0x0c38
+struct bt_hci_rsp_read_num_supported_iac {
+	uint8_t  status;
+	uint8_t  num_iac;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CURRENT_IAC_LAP		0x0c39
+struct bt_hci_rsp_read_current_iac_lap {
+	uint8_t  status;
+	uint8_t  num_iac;
+	uint8_t  iac_lap[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CURRENT_IAC_LAP	0x0c3a
+struct bt_hci_cmd_write_current_iac_lap {
+	uint8_t  num_iac;
+	uint8_t  iac_lap[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_SCAN_PERIOD_MODE	0x0c3b
+struct bt_hci_rsp_read_page_scan_period_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_SCAN_PERIOD_MODE	0x0c3c
+struct bt_hci_cmd_write_page_scan_period_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_SCAN_MODE		0x0c3d
+struct bt_hci_rsp_read_page_scan_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_SCAN_MODE		0x0c3e
+struct bt_hci_cmd_write_page_scan_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_AFH_HOST_CLASSIFICATION	0x0c3f
+struct bt_hci_cmd_set_afh_host_classification {
+	uint8_t  map[10];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_SCAN_TYPE	0x0c42
+struct bt_hci_rsp_read_inquiry_scan_type {
+	uint8_t  status;
+	uint8_t  type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_INQUIRY_SCAN_TYPE	0x0c43
+struct bt_hci_cmd_write_inquiry_scan_type {
+	uint8_t type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_MODE		0x0c44
+struct bt_hci_rsp_read_inquiry_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_INQUIRY_MODE		0x0c45
+struct bt_hci_cmd_write_inquiry_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_SCAN_TYPE		0x0c46
+struct bt_hci_rsp_read_page_scan_type {
+	uint8_t status;
+	uint8_t type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_SCAN_TYPE		0x0c47
+struct bt_hci_cmd_write_page_scan_type {
+	uint8_t type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AFH_ASSESSMENT_MODE	0x0c48
+struct bt_hci_rsp_read_afh_assessment_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AFH_ASSESSMENT_MODE	0x0c49
+struct bt_hci_cmd_write_afh_assessment_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_EXT_INQUIRY_RESPONSE	0x0c51
+struct bt_hci_rsp_read_ext_inquiry_response {
+	uint8_t  status;
+	uint8_t  fec;
+	uint8_t  data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE	0x0c52
+struct bt_hci_cmd_write_ext_inquiry_response {
+	uint8_t  fec;
+	uint8_t  data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REFRESH_ENCRYPT_KEY		0x0c53
+struct bt_hci_cmd_refresh_encrypt_key {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE	0x0c55
+struct bt_hci_rsp_read_simple_pairing_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE	0x0c56
+struct bt_hci_cmd_write_simple_pairing_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_OOB_DATA		0x0c57
+struct bt_hci_rsp_read_local_oob_data {
+	uint8_t  status;
+	uint8_t  hash[16];
+	uint8_t  randomizer[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER	0x0c58
+struct bt_hci_rsp_read_inquiry_resp_tx_power {
+	uint8_t  status;
+	int8_t   level;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_INQUIRY_TX_POWER	0x0c59
+struct bt_hci_cmd_write_inquiry_tx_power {
+	int8_t   level;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_ERRONEOUS_REPORTING	0x0c5a
+struct bt_hci_rsp_read_erroneous_reporting {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_ERRONEOUS_REPORTING	0x0c5b
+struct bt_hci_cmd_write_erroneous_reporting {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ENHANCED_FLUSH		0x0c5f
+struct bt_hci_cmd_enhanced_flush {
+	uint16_t handle;
+	uint8_t  type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SEND_KEYPRESS_NOTIFY		0x0c60
+struct bt_hci_cmd_send_keypress_notify {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+} __attribute__ ((packed));
+struct bt_hci_rsp_send_keypress_notify {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_EVENT_MASK_PAGE2		0x0c63
+struct bt_hci_cmd_set_event_mask_page2 {
+	uint8_t  mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCATION_DATA		0x0c64
+struct bt_hci_rsp_read_location_data {
+	uint8_t  status;
+	uint8_t  domain_aware;
+	uint8_t  domain[2];
+	uint8_t  domain_options;
+	uint8_t  options;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LOCATION_DATA		0x0c65
+struct bt_hci_cmd_write_location_data {
+	uint8_t  domain_aware;
+	uint8_t  domain[2];
+	uint8_t  domain_options;
+	uint8_t  options;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_FLOW_CONTROL_MODE	0x0c66
+struct bt_hci_rsp_read_flow_control_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_FLOW_CONTROL_MODE	0x0c67
+struct bt_hci_cmd_write_flow_control_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_ENHANCED_TX_POWER	0x0c68
+struct bt_hci_cmd_read_enhanced_tx_power {
+	uint16_t handle;
+	uint8_t  type;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_enhanced_tx_power {
+	uint8_t  status;
+	uint16_t handle;
+	int8_t   level_gfsk;
+	int8_t   level_dqpsk;
+	int8_t   level_8dpsk;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SHORT_RANGE_MODE		0x0c6b
+struct bt_hci_cmd_short_range_mode {
+	uint8_t  phy_handle;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LE_HOST_SUPPORTED	0x0c6c
+struct bt_hci_rsp_read_le_host_supported {
+	uint8_t  status;
+	uint8_t  supported;
+	uint8_t  simultaneous;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED	0x0c6d
+struct bt_hci_cmd_write_le_host_supported {
+	uint8_t  supported;
+	uint8_t  simultaneous;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_RESERVED_LT_ADDR		0x0c74
+struct bt_hci_cmd_set_reserved_lt_addr {
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_reserved_lt_addr {
+	uint8_t  status;
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DELETE_RESERVED_LT_ADDR	0x0c75
+struct bt_hci_cmd_delete_reserved_lt_addr {
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+struct bt_hci_rsp_delete_reserved_lt_addr {
+	uint8_t  status;
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA	0x0c76
+struct bt_hci_cmd_set_slave_broadcast_data {
+	uint8_t  lt_addr;
+	uint8_t  fragment;
+	uint8_t  length;
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_slave_broadcast_data {
+	uint8_t  status;
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS	0x0c77
+struct bt_hci_rsp_read_sync_train_params {
+	uint8_t  status;
+	uint16_t interval;
+	uint32_t timeout;
+	uint8_t  service_data;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS	0x0c78
+struct bt_hci_cmd_write_sync_train_params {
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint32_t timeout;
+	uint8_t  service_data;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_sync_train_params {
+	uint8_t  status;
+	uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SECURE_CONN_SUPPORT	0x0c79
+struct bt_hci_rsp_read_secure_conn_support {
+	uint8_t  status;
+	uint8_t  support;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT	0x0c7a
+struct bt_hci_cmd_write_secure_conn_support {
+	uint8_t support;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AUTH_PAYLOAD_TIMEOUT	0x0c7b
+struct bt_hci_cmd_read_auth_payload_timeout {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_auth_payload_timeout {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AUTH_PAYLOAD_TIMEOUT	0x0c7c
+struct bt_hci_cmd_write_auth_payload_timeout {
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_auth_payload_timeout {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA	0x0c7d
+struct bt_hci_rsp_read_local_oob_ext_data {
+	uint8_t  status;
+	uint8_t  hash192[16];
+	uint8_t  randomizer192[16];
+	uint8_t  hash256[16];
+	uint8_t  randomizer256[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_EXT_PAGE_TIMEOUT	0x0c7e
+struct bt_hci_rsp_read_ext_page_timeout {
+	uint8_t  status;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_PAGE_TIMEOUT	0x0c7f
+struct bt_hci_cmd_write_ext_page_timeout {
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_EXT_INQUIRY_LENGTH	0x0c80
+struct bt_hci_rsp_read_ext_inquiry_length {
+	uint8_t  status;
+	uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_INQUIRY_LENGTH	0x0c81
+struct bt_hci_cmd_write_ext_inquiry_length {
+	uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_VERSION		0x1001
+struct bt_hci_rsp_read_local_version {
+	uint8_t  status;
+	uint8_t  hci_ver;
+	uint16_t hci_rev;
+	uint8_t  lmp_ver;
+	uint16_t manufacturer;
+	uint16_t lmp_subver;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_COMMANDS		0x1002
+struct bt_hci_rsp_read_local_commands {
+	uint8_t  status;
+	uint8_t  commands[64];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_FEATURES		0x1003
+struct bt_hci_rsp_read_local_features {
+	uint8_t  status;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_EXT_FEATURES	0x1004
+struct bt_hci_cmd_read_local_ext_features {
+	uint8_t  page;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_local_ext_features {
+	uint8_t  status;
+	uint8_t  page;
+	uint8_t  max_page;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_BUFFER_SIZE		0x1005
+struct bt_hci_rsp_read_buffer_size {
+	uint8_t  status;
+	uint16_t acl_mtu;
+	uint8_t  sco_mtu;
+	uint16_t acl_max_pkt;
+	uint16_t sco_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_COUNTRY_CODE		0x1007
+struct bt_hci_rsp_read_country_code {
+	uint8_t  status;
+	uint8_t  code;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_BD_ADDR			0x1009
+struct bt_hci_rsp_read_bd_addr {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_DATA_BLOCK_SIZE		0x100a
+struct bt_hci_rsp_read_data_block_size {
+	uint8_t  status;
+	uint16_t max_acl_len;
+	uint16_t block_len;
+	uint16_t num_blocks;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_CODECS		0x100b
+struct bt_hci_rsp_read_local_codecs {
+	uint8_t  status;
+	uint8_t  num_codecs;
+	uint8_t  codec[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_FAILED_CONTACT_COUNTER	0x1401
+struct bt_hci_cmd_read_failed_contact_counter {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_failed_contact_counter {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t counter;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_RESET_FAILED_CONTACT_COUNTER	0x1402
+struct bt_hci_cmd_reset_failed_contact_counter {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_reset_failed_contact_counter {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LINK_QUALITY		0x1403
+struct bt_hci_cmd_read_link_quality {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_link_quality {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  link_quality;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_RSSI			0x1405
+struct bt_hci_cmd_read_rssi {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_rssi {
+	uint8_t  status;
+	uint16_t handle;
+	int8_t   rssi;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AFH_CHANNEL_MAP		0x1406
+struct bt_hci_cmd_read_afh_channel_map {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_afh_channel_map {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  mode;
+	uint8_t  map[10];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CLOCK			0x1407
+struct bt_hci_cmd_read_clock {
+	uint16_t handle;
+	uint8_t  type;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_clock {
+	uint8_t  status;
+	uint16_t handle;
+	uint32_t clock;
+	uint16_t accuracy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_ENCRYPT_KEY_SIZE	0x1408
+struct bt_hci_cmd_read_encrypt_key_size {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_encrypt_key_size {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  key_size;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_AMP_INFO		0x1409
+struct bt_hci_rsp_read_local_amp_info {
+	uint8_t  status;
+	uint8_t  amp_status;
+	uint32_t total_bw;
+	uint32_t max_bw;
+	uint32_t min_latency;
+	uint32_t max_pdu;
+	uint8_t  amp_type;
+	uint16_t pal_cap;
+	uint16_t max_assoc_len;
+	uint32_t max_flush_to;
+	uint32_t be_flush_to;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_AMP_ASSOC		0x140a
+struct bt_hci_cmd_read_local_amp_assoc {
+	uint8_t  phy_handle;
+	uint16_t len_so_far;
+	uint16_t max_assoc_len;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_local_amp_assoc {
+	uint8_t  status;
+	uint8_t  phy_handle;
+	uint16_t remain_assoc_len;
+	uint8_t  assoc_fragment[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_REMOTE_AMP_ASSOC	0x140b
+struct bt_hci_cmd_write_remote_amp_assoc {
+	uint8_t  phy_handle;
+	uint16_t len_so_far;
+	uint16_t remain_assoc_len;
+	uint8_t  assoc_fragment[248];
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_remote_amp_assoc {
+	uint8_t  status;
+	uint8_t  phy_handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_GET_MWS_TRANSPORT_CONFIG	0x140c
+struct bt_hci_rsp_get_mws_transport_config {
+	uint8_t  status;
+	uint8_t  num_transports;
+	uint8_t  transport[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_TRIGGERED_CLOCK_CAPTURE	0x140d
+struct bt_hci_cmd_set_triggered_clock_capture {
+	uint16_t handle;
+	uint8_t  enable;
+	uint8_t  type;
+	uint8_t  lpo_allowed;
+	uint8_t  num_filter;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOOPBACK_MODE		0x1801
+struct bt_hci_rsp_read_loopback_mode {
+	uint8_t  status;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LOOPBACK_MODE		0x1802
+struct bt_hci_cmd_write_loopback_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ENABLE_DUT_MODE		0x1803
+
+#define BT_HCI_CMD_WRITE_SSP_DEBUG_MODE		0x1804
+struct bt_hci_cmd_write_ssp_debug_mode {
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EVENT_MASK		0x2001
+struct bt_hci_cmd_le_set_event_mask {
+	uint8_t  mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_BUFFER_SIZE		0x2002
+struct bt_hci_rsp_le_read_buffer_size {
+	uint8_t  status;
+        uint16_t le_mtu;
+        uint8_t  le_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_LOCAL_FEATURES	0x2003
+struct bt_hci_rsp_le_read_local_features {
+	uint8_t  status;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_RANDOM_ADDRESS	0x2005
+struct bt_hci_cmd_le_set_random_address {
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_ADV_PARAMETERS	0x2006
+struct bt_hci_cmd_le_set_adv_parameters {
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint8_t  type;
+	uint8_t  own_addr_type;
+	uint8_t  direct_addr_type;
+	uint8_t  direct_addr[6];
+	uint8_t  channel_map;
+	uint8_t  filter_policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_ADV_TX_POWER		0x2007
+struct bt_hci_rsp_le_read_adv_tx_power {
+	uint8_t  status;
+	int8_t   level;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_ADV_DATA		0x2008
+struct bt_hci_cmd_le_set_adv_data {
+	uint8_t  len;
+	uint8_t  data[31];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_RSP_DATA		0x2009
+struct bt_hci_cmd_le_set_scan_rsp_data {
+	uint8_t  len;
+	uint8_t  data[31];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_ADV_ENABLE		0x200a
+struct bt_hci_cmd_le_set_adv_enable {
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_PARAMETERS	0x200b
+struct bt_hci_cmd_le_set_scan_parameters {
+	uint8_t  type;
+	uint16_t interval;
+	uint16_t window;
+	uint8_t  own_addr_type;
+	uint8_t  filter_policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_ENABLE		0x200c
+struct bt_hci_cmd_le_set_scan_enable {
+	uint8_t  enable;
+	uint8_t  filter_dup;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CREATE_CONN		0x200d
+struct bt_hci_cmd_le_create_conn {
+	uint16_t scan_interval;
+	uint16_t scan_window;
+	uint8_t  filter_policy;
+	uint8_t  peer_addr_type;
+	uint8_t  peer_addr[6];
+	uint8_t  own_addr_type;
+	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_CREATE_CONN_CANCEL	0x200e
+
+#define BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE	0x200f
+struct bt_hci_rsp_le_read_white_list_size {
+	uint8_t  status;
+	uint8_t  size;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CLEAR_WHITE_LIST		0x2010
+
+#define BT_HCI_CMD_LE_ADD_TO_WHITE_LIST		0x2011
+struct bt_hci_cmd_le_add_to_white_list {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_FROM_WHITE_LIST	0x2012
+struct bt_hci_cmd_le_remove_from_white_list {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CONN_UPDATE		0x2013
+struct bt_hci_cmd_le_conn_update {
+	uint16_t handle;
+	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_SET_HOST_CLASSIFICATION	0x2014
+struct bt_hci_cmd_le_set_host_classification {
+	uint8_t  map[5];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_CHANNEL_MAP		0x2015
+struct bt_hci_cmd_le_read_channel_map {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_read_channel_map {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  map[5];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_REMOTE_FEATURES	0x2016
+struct bt_hci_cmd_le_read_remote_features {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_ENCRYPT			0x2017
+struct bt_hci_cmd_le_encrypt {
+	uint8_t  key[16];
+	uint8_t  plaintext[16];
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_encrypt {
+	uint8_t  status;
+	uint8_t  data[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_RAND			0x2018
+struct bt_hci_rsp_le_rand {
+	uint8_t  status;
+	uint64_t number;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_START_ENCRYPT		0x2019
+struct bt_hci_cmd_le_start_encrypt {
+	uint16_t handle;
+	uint64_t rand;
+	uint16_t ediv;
+	uint8_t  ltk[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_LTK_REQ_REPLY		0x201a
+struct bt_hci_cmd_le_ltk_req_reply {
+	uint16_t handle;
+	uint8_t  ltk[16];
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_ltk_req_reply {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY		0x201b
+struct bt_hci_cmd_le_ltk_req_neg_reply {
+	uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_ltk_req_neg_reply {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_SUPPORTED_STATES	0x201c
+struct bt_hci_rsp_le_read_supported_states {
+	uint8_t  status;
+	uint8_t  states[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_RECEIVER_TEST		0x201d
+struct bt_hci_cmd_le_receiver_test {
+	uint8_t  frequency;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_TRANSMITTER_TEST		0x201e
+struct bt_hci_cmd_le_transmitter_test {
+	uint8_t  frequency;
+	uint8_t  data_len;
+	uint8_t  payload;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_TEST_END			0x201f
+struct bt_hci_rsp_le_test_end {
+	uint8_t  status;
+	uint16_t num_packets;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CONN_PARAM_REQ_REPLY	0x2020
+struct bt_hci_cmd_le_conn_param_req_reply {
+	uint16_t handle;
+	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));
+struct bt_hci_rsp_le_conn_param_req_reply {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CONN_PARAM_REQ_NEG_REPLY	0x2021
+struct bt_hci_cmd_le_conn_param_req_neg_reply {
+	uint16_t handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_conn_param_req_neg_reply {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_DATA_LENGTH		0x2022
+struct bt_hci_cmd_le_set_data_length {
+	uint16_t handle;
+	uint16_t tx_len;
+	uint16_t tx_time;
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_set_data_length {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_DEFAULT_DATA_LENGTH	0x2023
+struct bt_hci_rsp_le_read_default_data_length {
+	uint8_t  status;
+	uint16_t tx_len;
+	uint16_t tx_time;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_WRITE_DEFAULT_DATA_LENGTH	0x2024
+struct bt_hci_cmd_le_write_default_data_length {
+	uint16_t tx_len;
+	uint16_t tx_time;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_LOCAL_PK256		0x2025
+
+#define BT_HCI_CMD_LE_GENERATE_DHKEY		0x2026
+struct bt_hci_cmd_le_generate_dhkey {
+	uint8_t  remote_pk256[64];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST	0x2027
+struct bt_hci_cmd_le_add_to_resolv_list {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+	uint8_t  peer_irk[16];
+	uint8_t  local_irk[16];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_FROM_RESOLV_LIST	0x2028
+struct bt_hci_cmd_le_remove_from_resolv_list {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CLEAR_RESOLV_LIST		0x2029
+
+#define BT_HCI_CMD_LE_READ_RESOLV_LIST_SIZE	0x202a
+struct bt_hci_rsp_le_read_resolv_list_size {
+	uint8_t  status;
+	uint8_t  size;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_PEER_RESOLV_ADDR	0x202b
+struct bt_hci_cmd_le_read_peer_resolv_addr {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_read_peer_resolv_addr {
+	uint8_t  status;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_LOCAL_RESOLV_ADDR	0x202c
+struct bt_hci_cmd_le_read_local_resolv_addr {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_read_local_resolv_addr {
+	uint8_t  status;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_RESOLV_ENABLE		0x202d
+struct bt_hci_cmd_le_set_resolv_enable {
+	uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT	0x202e
+struct bt_hci_cmd_le_set_resolv_timeout {
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH	0x202f
+struct bt_hci_rsp_le_read_max_data_length {
+	uint8_t  status;
+	uint16_t max_tx_len;
+	uint16_t max_tx_time;
+	uint16_t max_rx_len;
+	uint16_t max_rx_time;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_COMPLETE		0x01
+struct bt_hci_evt_inquiry_complete {
+	uint8_t  status;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESULT		0x02
+struct bt_hci_evt_inquiry_result {
+	uint8_t  num_resp;
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_rep_mode;
+	uint8_t  pscan_period_mode;
+	uint8_t  pscan_mode;
+	uint8_t  dev_class[3];
+	uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_COMPLETE		0x03
+struct bt_hci_evt_conn_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  bdaddr[6];
+	uint8_t  link_type;
+	uint8_t  encr_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_REQUEST			0x04
+struct bt_hci_evt_conn_request {
+	uint8_t  bdaddr[6];
+	uint8_t  dev_class[3];
+	uint8_t  link_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_DISCONNECT_COMPLETE		0x05
+struct bt_hci_evt_disconnect_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_AUTH_COMPLETE		0x06
+struct bt_hci_evt_auth_complete {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE	0x07
+struct bt_hci_evt_remote_name_request_complete {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint8_t  name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_ENCRYPT_CHANGE		0x08
+struct bt_hci_evt_encrypt_change {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  encr_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09
+struct bt_hci_evt_change_conn_link_key_complete {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_MASTER_LINK_KEY_COMPLETE	0x0a
+struct bt_hci_evt_master_link_key_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  key_flag;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_FEATURES_COMPLETE	0x0b
+struct bt_hci_evt_remote_features_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_VERSION_COMPLETE	0x0c
+struct bt_hci_evt_remote_version_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  lmp_ver;
+	uint16_t manufacturer;
+	uint16_t lmp_subver;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_QOS_SETUP_COMPLETE		0x0d
+struct bt_hci_evt_qos_setup_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  flags;
+	uint8_t  service_type;
+	uint32_t token_rate;
+	uint32_t peak_bandwidth;
+	uint32_t latency;
+	uint32_t delay_variation;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CMD_COMPLETE			0x0e
+struct bt_hci_evt_cmd_complete {
+	uint8_t  ncmd;
+	uint16_t opcode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CMD_STATUS			0x0f
+struct bt_hci_evt_cmd_status {
+	uint8_t  status;
+	uint8_t  ncmd;
+	uint16_t opcode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_HARDWARE_ERROR		0x10
+struct bt_hci_evt_hardware_error {
+	uint8_t  code;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_FLUSH_OCCURRED		0x11
+struct bt_hci_evt_flush_occurred {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_ROLE_CHANGE			0x12
+struct bt_hci_evt_role_change {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint8_t  role;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_NUM_COMPLETED_PACKETS	0x13
+struct bt_hci_evt_num_completed_packets {
+	uint8_t  num_handles;
+	uint16_t handle;
+	uint16_t count;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_MODE_CHANGE			0x14
+struct bt_hci_evt_mode_change {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  mode;
+	uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_RETURN_LINK_KEYS		0x15
+struct bt_hci_evt_return_link_keys {
+	uint8_t  num_keys;
+	uint8_t  keys[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_PIN_CODE_REQUEST		0x16
+struct bt_hci_evt_pin_code_request {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LINK_KEY_REQUEST		0x17
+struct bt_hci_evt_link_key_request {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LINK_KEY_NOTIFY		0x18
+struct bt_hci_evt_link_key_notify {
+	uint8_t  bdaddr[6];
+	uint8_t  link_key[16];
+	uint8_t  key_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LOOPBACK_COMMAND		0x19
+
+#define BT_HCI_EVT_DATA_BUFFER_OVERFLOW		0x1a
+struct bt_hci_evt_data_buffer_overflow {
+	uint8_t  link_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_MAX_SLOTS_CHANGE		0x1b
+struct bt_hci_evt_max_slots_change {
+	uint16_t handle;
+	uint8_t  max_slots;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CLOCK_OFFSET_COMPLETE	0x1c
+struct bt_hci_evt_clock_offset_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_PKT_TYPE_CHANGED	0x1d
+struct bt_hci_evt_conn_pkt_type_changed {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_QOS_VIOLATION		0x1e
+struct bt_hci_evt_qos_violation {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_PSCAN_MODE_CHANGE		0x1f
+struct bt_hci_evt_pscan_mode_change {
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_PSCAN_REP_MODE_CHANGE	0x20
+struct bt_hci_evt_pscan_rep_mode_change {
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_rep_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_FLOW_SPEC_COMPLETE		0x21
+struct bt_hci_evt_flow_spec_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  flags;
+	uint8_t  direction;
+	uint8_t  service_type;
+	uint32_t token_rate;
+	uint32_t token_bucket_size;
+	uint32_t peak_bandwidth;
+	uint32_t access_latency;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI	0x22
+struct bt_hci_evt_inquiry_result_with_rssi {
+	uint8_t  num_resp;
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_rep_mode;
+	uint8_t  pscan_period_mode;
+	uint8_t  dev_class[3];
+	uint16_t clock_offset;
+	int8_t   rssi;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE	0x23
+struct bt_hci_evt_remote_ext_features_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  page;
+	uint8_t  max_page;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SYNC_CONN_COMPLETE		0x2c
+struct bt_hci_evt_sync_conn_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  bdaddr[6];
+	uint8_t  link_type;
+	uint8_t  tx_interval;
+	uint8_t  retrans_window;
+	uint16_t rx_pkt_len;
+	uint16_t tx_pkt_len;
+	uint8_t  air_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SYNC_CONN_CHANGED		0x2d
+struct bt_hci_evt_sync_conn_changed {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  tx_interval;
+	uint8_t  retrans_window;
+	uint16_t rx_pkt_len;
+	uint16_t tx_pkt_len;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SNIFF_SUBRATING		0x2e
+struct bt_hci_evt_sniff_subrating {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t max_tx_latency;
+	uint16_t max_rx_latency;
+	uint16_t min_remote_timeout;
+	uint16_t min_local_timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_EXT_INQUIRY_RESULT		0x2f
+struct bt_hci_evt_ext_inquiry_result {
+	uint8_t  num_resp;
+	uint8_t  bdaddr[6];
+	uint8_t  pscan_rep_mode;
+	uint8_t  pscan_period_mode;
+	uint8_t  dev_class[3];
+	uint16_t clock_offset;
+	int8_t   rssi;
+	uint8_t  data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE	0x30
+struct bt_hci_evt_encrypt_key_refresh_complete {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_IO_CAPABILITY_REQUEST	0x31
+struct bt_hci_evt_io_capability_request {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_IO_CAPABILITY_RESPONSE	0x32
+struct bt_hci_evt_io_capability_response {
+	uint8_t  bdaddr[6];
+	uint8_t  capability;
+	uint8_t  oob_data;
+	uint8_t  authentication;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_USER_CONFIRM_REQUEST		0x33
+struct bt_hci_evt_user_confirm_request {
+	uint8_t  bdaddr[6];
+	uint32_t passkey;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_USER_PASSKEY_REQUEST		0x34
+struct bt_hci_evt_user_passkey_request {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_OOB_DATA_REQUEST	0x35
+struct bt_hci_evt_remote_oob_data_request {
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE	0x36
+struct bt_hci_evt_simple_pairing_complete {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LINK_SUPV_TIMEOUT_CHANGED	0x38
+struct bt_hci_evt_link_supv_timeout_changed {
+	uint16_t handle;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_ENHANCED_FLUSH_COMPLETE	0x39
+struct bt_hci_evt_enhanced_flush_complete {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_USER_PASSKEY_NOTIFY		0x3b
+struct bt_hci_evt_user_passkey_notify {
+	uint8_t  bdaddr[6];
+	uint32_t passkey;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_KEYPRESS_NOTIFY		0x3c
+struct bt_hci_evt_keypress_notify {
+	uint8_t  bdaddr[6];
+	uint8_t  type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_HOST_FEATURES_NOTIFY	0x3d
+struct bt_hci_evt_remote_host_features_notify {
+	uint8_t  bdaddr[6];
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_META_EVENT		0x3e
+
+#define BT_HCI_EVT_PHY_LINK_COMPLETE		0x40
+struct bt_hci_evt_phy_link_complete {
+	uint8_t  status;
+	uint8_t  phy_handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CHANNEL_SELECTED		0x41
+struct bt_hci_evt_channel_selected {
+	uint8_t  phy_handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_DISCONN_PHY_LINK_COMPLETE	0x42
+struct bt_hci_evt_disconn_phy_link_complete {
+	uint8_t  status;
+	uint8_t  phy_handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_PHY_LINK_LOSS_EARLY_WARNING	0x43
+struct bt_hci_evt_phy_link_loss_early_warning {
+	uint8_t  phy_handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_PHY_LINK_RECOVERY		0x44
+struct bt_hci_evt_phy_link_recovery {
+	uint8_t  phy_handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LOGIC_LINK_COMPLETE		0x45
+struct bt_hci_evt_logic_link_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  phy_handle;
+	uint8_t  flow_spec;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_DISCONN_LOGIC_LINK_COMPLETE	0x46
+struct bt_hci_evt_disconn_logic_link_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_FLOW_SPEC_MODIFY_COMPLETE	0x47
+struct bt_hci_evt_flow_spec_modify_complete {
+	uint8_t  status;
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_NUM_COMPLETED_DATA_BLOCKS	0x48
+struct bt_hci_evt_num_completed_data_blocks {
+	uint16_t total_num_blocks;
+	uint8_t  num_handles;
+	uint16_t handle;
+	uint16_t num_packets;
+	uint16_t num_blocks;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SHORT_RANGE_MODE_CHANGE	0x4c
+struct bt_hci_evt_short_range_mode_change {
+	uint8_t  status;
+	uint8_t  phy_handle;
+	uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_AMP_STATUS_CHANGE		0x4d
+struct bt_hci_evt_amp_status_change {
+	uint8_t  status;
+	uint8_t  amp_status;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_TRIGGERED_CLOCK_CAPTURE	0x4e
+struct bt_hci_evt_triggered_clock_capture {
+	uint16_t handle;
+	uint8_t  type;
+	uint32_t clock;
+	uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SYNC_TRAIN_COMPLETE		0x4f
+struct bt_hci_evt_sync_train_complete {
+	uint8_t  status;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SYNC_TRAIN_RECEIVED		0x50
+struct bt_hci_evt_sync_train_received {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint32_t offset;
+	uint8_t  map[10];
+	uint8_t  lt_addr;
+	uint32_t instant;
+	uint16_t interval;
+	uint8_t  service_data;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SLAVE_BROADCAST_RECEIVE	0x51
+struct bt_hci_evt_slave_broadcast_receive {
+	uint8_t  bdaddr[6];
+	uint8_t  lt_addr;
+	uint32_t clock;
+	uint32_t offset;
+	uint8_t  status;
+	uint8_t  fragment;
+	uint8_t  length;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SLAVE_BROADCAST_TIMEOUT	0x52
+struct bt_hci_evt_slave_broadcast_timeout {
+	uint8_t  bdaddr[6];
+	uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE	0x53
+struct bt_hci_evt_truncated_page_complete {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT	0x54
+
+#define BT_HCI_EVT_SLAVE_BROADCAST_CHANNEL_MAP_CHANGE	0x55
+struct bt_hci_evt_slave_broadcast_channel_map_change {
+	uint8_t  map[10];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESPONSE_NOTIFY	0x56
+struct bt_hci_evt_inquiry_response_notify {
+	uint8_t  lap[3];
+	int8_t   rssi;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXPIRED	0x57
+struct bt_hci_evt_auth_payload_timeout_expired {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_CONN_COMPLETE		0x01
+struct bt_hci_evt_le_conn_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  role;
+	uint8_t  peer_addr_type;
+	uint8_t  peer_addr[6];
+	uint16_t interval;
+	uint16_t latency;
+	uint16_t supv_timeout;
+	uint8_t  clock_accuracy;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_ADV_REPORT		0x02
+struct bt_hci_evt_le_adv_report {
+	uint8_t  num_reports;
+	uint8_t  event_type;
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+	uint8_t  data_len;
+	uint8_t  data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE	0x03
+struct bt_hci_evt_le_conn_update_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint16_t interval;
+	uint16_t latency;
+	uint16_t supv_timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_REMOTE_FEATURES_COMPLETE	0x04
+struct bt_hci_evt_le_remote_features_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST	0x05
+struct bt_hci_evt_le_long_term_key_request {
+	uint16_t handle;
+	uint64_t rand;
+	uint16_t ediv;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_CONN_PARAM_REQUEST	0x06
+struct bt_hci_evt_le_conn_param_request {
+	uint16_t handle;
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t latency;
+	uint16_t supv_timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_DATA_LENGTH_CHANGE	0x07
+struct bt_hci_evt_le_data_length_change {
+	uint16_t handle;
+	uint16_t max_tx_len;
+	uint16_t max_tx_time;
+	uint16_t max_rx_len;
+	uint16_t max_rx_time;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE	0x08
+struct bt_hci_evt_le_read_local_pk256_complete {
+	uint8_t  status;
+	uint8_t  local_pk256[64];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE	0x09
+struct bt_hci_evt_le_generate_dhkey_complete {
+	uint8_t  status;
+	uint8_t  dhkey[32];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_ENHANCED_CONN_COMPLETE	0x0a
+struct bt_hci_evt_le_enhanced_conn_complete {
+	uint8_t  status;
+	uint16_t handle;
+	uint8_t  role;
+	uint8_t  peer_addr_type;
+	uint8_t  peer_addr[6];
+	uint8_t  local_rpa[6];
+	uint8_t  peer_rpa[6];
+	uint16_t interval;
+	uint16_t latency;
+	uint16_t supv_timeout;
+	uint8_t  clock_accuracy;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_DIRECT_ADV_REPORT		0x0b
+struct bt_hci_evt_le_direct_adv_report {
+	uint8_t  num_reports;
+	uint8_t  event_type;
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+	uint8_t  direct_addr_type;
+	uint8_t  direct_addr[6];
+	int8_t   rssi;
+} __attribute__ ((packed));
+
+#define BT_HCI_ERR_SUCCESS			0x00
+#define BT_HCI_ERR_UNKNOWN_COMMAND		0x01
+#define BT_HCI_ERR_UNKNOWN_CONN_ID		0x02
+#define BT_HCI_ERR_HARDWARE_FAILURE		0x03
+#define BT_HCI_ERR_PAGE_TIMEOUT			0x04
+#define BT_HCI_ERR_AUTH_FAILURE			0x05
+#define BT_HCI_ERR_PIN_OR_KEY_MISSING		0x06
+#define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED	0x07
+#define BT_HCI_ERR_COMMAND_DISALLOWED		0x0c
+#define BT_HCI_ERR_UNSUPPORTED_FEATURE		0x11
+#define BT_HCI_ERR_INVALID_PARAMETERS		0x12
+#define BT_HCI_ERR_UNSPECIFIED_ERROR		0x1f
+#define BT_HCI_ERR_CONN_FAILED_TO_ESTABLISH	0x3e
+
+struct bt_l2cap_hdr {
+	uint16_t len;
+	uint16_t cid;
+} __attribute__ ((packed));
+
+struct bt_l2cap_hdr_sig {
+	uint8_t  code;
+	uint8_t  ident;
+	uint16_t len;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CMD_REJECT		0x01
+struct bt_l2cap_pdu_cmd_reject {
+	uint16_t reason;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CONN_REQ		0x02
+struct bt_l2cap_pdu_conn_req {
+	uint16_t psm;
+	uint16_t scid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CONN_RSP		0x03
+struct bt_l2cap_pdu_conn_rsp {
+	uint16_t dcid;
+	uint16_t scid;
+	uint16_t result;
+	uint16_t status;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CONFIG_REQ		0x04
+struct bt_l2cap_pdu_config_req {
+	uint16_t dcid;
+	uint16_t flags;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CONFIG_RSP		0x05
+struct bt_l2cap_pdu_config_rsp {
+	uint16_t scid;
+	uint16_t flags;
+	uint16_t result;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_DISCONN_REQ	0x06
+struct bt_l2cap_pdu_disconn_req {
+	uint16_t dcid;
+	uint16_t scid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_DISCONN_RSP	0x07
+struct bt_l2cap_pdu_disconn_rsp {
+	uint16_t dcid;
+	uint16_t scid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_ECHO_REQ		0x08
+
+#define BT_L2CAP_PDU_ECHO_RSP		0x09
+
+#define BT_L2CAP_PDU_INFO_REQ		0x0a
+struct bt_l2cap_pdu_info_req {
+	uint16_t type;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_INFO_RSP		0x0b
+struct bt_l2cap_pdu_info_rsp {
+	uint16_t type;
+	uint16_t result;
+	uint8_t  data[0];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CREATE_CHAN_REQ	0x0c
+struct bt_l2cap_pdu_create_chan_req {
+	uint16_t psm;
+	uint16_t scid;
+	uint8_t  ctrlid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CREATE_CHAN_RSP	0x0d
+struct bt_l2cap_pdu_create_chan_rsp {
+	uint16_t dcid;
+	uint16_t scid;
+	uint16_t result;
+	uint16_t status;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_MOVE_CHAN_REQ	0x0e
+struct bt_l2cap_pdu_move_chan_req {
+	uint16_t icid;
+	uint8_t  ctrlid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_MOVE_CHAN_RSP	0x0f
+struct bt_l2cap_pdu_move_chan_rsp {
+	uint16_t icid;
+	uint16_t result;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_MOVE_CHAN_CFM	0x10
+struct bt_l2cap_pdu_move_chan_cfm {
+	uint16_t icid;
+	uint16_t result;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_MOVE_CHAN_CFM_RSP	0x11
+struct bt_l2cap_pdu_move_chan_cfm_rsp {
+	uint16_t icid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CONN_PARAM_REQ	0x12
+struct bt_l2cap_pdu_conn_param_req {
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t latency;
+	uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_CONN_PARAM_RSP	0x13
+struct bt_l2cap_pdu_conn_param_rsp {
+	uint16_t result;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_LE_CONN_REQ	0x14
+struct bt_l2cap_pdu_le_conn_req {
+	uint16_t psm;
+	uint16_t scid;
+	uint16_t mtu;
+	uint16_t mps;
+	uint16_t credits;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_LE_CONN_RSP	0x15
+struct bt_l2cap_pdu_le_conn_rsp {
+	uint16_t dcid;
+	uint16_t mtu;
+	uint16_t mps;
+	uint16_t credits;
+	uint16_t result;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_LE_FLOWCTL_CREDS	0x16
+struct bt_l2cap_pdu_le_flowctl_creds {
+	uint16_t cid;
+	uint16_t credits;
+} __attribute__ ((packed));
+
+struct bt_l2cap_hdr_connless {
+	uint16_t psm;
+} __attribute__ ((packed));
+
+struct bt_l2cap_hdr_amp {
+	uint8_t  code;
+	uint8_t  ident;
+	uint16_t len;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_CMD_REJECT		0x01
+struct bt_l2cap_amp_cmd_reject {
+	uint16_t reason;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_DISCOVER_REQ	0x02
+struct bt_l2cap_amp_discover_req {
+	uint16_t size;
+	uint16_t features;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_DISCOVER_RSP	0x03
+struct bt_l2cap_amp_discover_rsp {
+	uint16_t size;
+	uint16_t features;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_CHANGE_NOTIFY	0x04
+
+#define BT_L2CAP_AMP_CHANGE_RESPONSE	0x05
+
+#define BT_L2CAP_AMP_GET_INFO_REQ	0x06
+struct bt_l2cap_amp_get_info_req {
+	uint8_t  ctrlid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_GET_INFO_RSP	0x07
+struct bt_l2cap_amp_get_info_rsp {
+	uint8_t  ctrlid;
+	uint8_t  status;
+	uint32_t total_bw;
+	uint32_t max_bw;
+	uint32_t min_latency;
+	uint16_t pal_cap;
+	uint16_t max_assoc_len;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_GET_ASSOC_REQ	0x08
+struct bt_l2cap_amp_get_assoc_req {
+	uint8_t  ctrlid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_GET_ASSOC_RSP	0x09
+struct bt_l2cap_amp_get_assoc_rsp {
+	uint8_t  ctrlid;
+	uint8_t  status;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_CREATE_PHY_LINK_REQ	0x0a
+struct bt_l2cap_amp_create_phy_link_req {
+	uint8_t  local_ctrlid;
+	uint8_t  remote_ctrlid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_CREATE_PHY_LINK_RSP	0x0b
+struct bt_l2cap_amp_create_phy_link_rsp {
+	uint8_t  local_ctrlid;
+	uint8_t  remote_ctrlid;
+	uint8_t  status;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_DISCONN_PHY_LINK_REQ	0x0c
+struct bt_l2cap_amp_disconn_phy_link_req {
+	uint8_t  local_ctrlid;
+	uint8_t  remote_ctrlid;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_AMP_DISCONN_PHY_LINK_RSP	0x0d
+struct bt_l2cap_amp_disconn_phy_link_rsp {
+	uint8_t  local_ctrlid;
+	uint8_t  remote_ctrlid;
+	uint8_t  status;
+} __attribute__ ((packed));
+
+struct bt_l2cap_hdr_att {
+	uint8_t  code;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_ERROR_RESPONSE		0x01
+struct bt_l2cap_att_error_response {
+	uint8_t  request;
+	uint16_t handle;
+	uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_EXCHANGE_MTU_REQ		0x02
+struct bt_l2cap_att_exchange_mtu_req {
+	uint16_t mtu;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_EXCHANGE_MTU_RSP		0x03
+struct bt_l2cap_att_exchange_mtu_rsp {
+	uint16_t mtu;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_READ_TYPE_REQ		0x08
+struct bt_l2cap_att_read_type_req {
+	uint16_t start_handle;
+	uint16_t end_handle;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_READ_TYPE_RSP		0x09
+struct bt_l2cap_att_read_type_rsp {
+	uint8_t  length;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_READ_REQ			0x0a
+struct bt_l2cap_att_read_req {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_READ_RSP			0x0b
+
+#define BT_L2CAP_ATT_READ_GROUP_TYPE_REQ	0x10
+struct bt_l2cap_att_read_group_type_req {
+	uint16_t start_handle;
+	uint16_t end_handle;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_READ_GROUP_TYPE_RSP	0x11
+struct bt_l2cap_att_read_group_type_rsp {
+	uint8_t  length;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_HANDLE_VALUE_NOTIFY	0x1b
+struct bt_l2cap_att_handle_value_notify {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_HANDLE_VALUE_IND		0x1d
+struct bt_l2cap_att_handle_value_ind {
+	uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_ATT_HANDLE_VALUE_CONF		0x1e
+
+struct bt_l2cap_hdr_smp {
+	uint8_t  code;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_PAIRING_REQUEST	0x01
+struct bt_l2cap_smp_pairing_request {
+	uint8_t  io_capa;
+	uint8_t  oob_data;
+	uint8_t  auth_req;
+	uint8_t  max_key_size;
+	uint8_t  init_key_dist;
+	uint8_t  resp_key_dist;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_PAIRING_RESPONSE	0x02
+struct bt_l2cap_smp_pairing_response {
+	uint8_t  io_capa;
+	uint8_t  oob_data;
+	uint8_t  auth_req;
+	uint8_t  max_key_size;
+	uint8_t  init_key_dist;
+	uint8_t  resp_key_dist;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_PAIRING_CONFIRM	0x03
+struct bt_l2cap_smp_pairing_confirm {
+	uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_PAIRING_RANDOM	0x04
+struct bt_l2cap_smp_pairing_random {
+	uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_PAIRING_FAILED	0x05
+struct bt_l2cap_smp_pairing_failed {
+	uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_ENCRYPT_INFO	0x06
+struct bt_l2cap_smp_encrypt_info {
+	uint8_t  ltk[16];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_MASTER_IDENT	0x07
+struct bt_l2cap_smp_master_ident {
+	uint16_t ediv;
+	uint64_t rand;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_IDENT_INFO		0x08
+struct bt_l2cap_smp_ident_info {
+	uint8_t  irk[16];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_IDENT_ADDR_INFO	0x09
+struct bt_l2cap_smp_ident_addr_info {
+	uint8_t  addr_type;
+	uint8_t  addr[6];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_SIGNING_INFO	0x0a
+struct bt_l2cap_smp_signing_info {
+	uint8_t  csrk[16];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_SECURITY_REQUEST	0x0b
+struct bt_l2cap_smp_security_request {
+	uint8_t  auth_req;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_PUBLIC_KEY		0x0c
+struct bt_l2cap_smp_public_key {
+	uint8_t  x[32];
+	uint8_t  y[32];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_DHKEY_CHECK	0x0d
+struct bt_l2cap_smp_dhkey_check {
+	uint8_t  e[16];
+} __attribute__ ((packed));
+
+#define BT_L2CAP_SMP_KEYPRESS_NOTIFY	0x0e
+struct bt_l2cap_smp_keypress_notify {
+	uint8_t  type;
+} __attribute__ ((packed));
+
+struct bt_sdp_hdr {
+	uint8_t  pdu;
+	uint16_t tid;
+	uint16_t plen;
+} __attribute__ ((packed));
diff --git a/bluez/repo/monitor/control.c b/bluez/repo/monitor/control.c
new file mode 100644
index 0000000..b3fc93d
--- /dev/null
+++ b/bluez/repo/monitor/control.c
@@ -0,0 +1,1249 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/util.h"
+#include "src/shared/btsnoop.h"
+#include "src/shared/mainloop.h"
+
+#include "display.h"
+#include "packet.h"
+#include "hcidump.h"
+#include "ellisys.h"
+#include "control.h"
+
+static struct btsnoop *btsnoop_file = NULL;
+static bool hcidump_fallback = false;
+
+struct control_data {
+	uint16_t channel;
+	int fd;
+	unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+	uint16_t offset;
+};
+
+static void free_data(void *user_data)
+{
+	struct control_data *data = user_data;
+
+	close(data->fd);
+
+	free(data);
+}
+
+static void mgmt_index_added(uint16_t len, const void *buf)
+{
+	printf("@ Index Added\n");
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_index_removed(uint16_t len, const void *buf)
+{
+	printf("@ Index Removed\n");
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_unconf_index_added(uint16_t len, const void *buf)
+{
+	printf("@ Unconfigured Index Added\n");
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_unconf_index_removed(uint16_t len, const void *buf)
+{
+	printf("@ Unconfigured Index Removed\n");
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_ext_index_added(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_ext_index_added *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Extended Index Added control\n");
+		return;
+	}
+
+	printf("@ Extended Index Added: %u (%u)\n", ev->type, ev->bus);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_ext_index_removed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_ext_index_removed *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Extended Index Removed control\n");
+		return;
+	}
+
+	printf("@ Extended Index Removed: %u (%u)\n", ev->type, ev->bus);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_controller_error(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_controller_error *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Controller Error control\n");
+		return;
+	}
+
+	printf("@ Controller Error: 0x%2.2x\n", ev->error_code);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static const char *config_options_str[] = {
+	"external", "public-address",
+};
+
+static void mgmt_new_config_options(uint16_t len, const void *buf)
+{
+	uint32_t options;
+	unsigned int i;
+
+	if (len < 4) {
+		printf("* Malformed New Configuration Options control\n");
+		return;
+	}
+
+	options = get_le32(buf);
+
+	printf("@ New Configuration Options: 0x%4.4x\n", options);
+
+	if (options) {
+		printf("%-12c", ' ');
+		for (i = 0; i < NELEM(config_options_str); i++) {
+			if (options & (1 << i))
+				printf("%s ", config_options_str[i]);
+		}
+		printf("\n");
+	}
+
+	buf += 4;
+	len -= 4;
+
+	packet_hexdump(buf, len);
+}
+
+static const char *settings_str[] = {
+	"powered", "connectable", "fast-connectable", "discoverable",
+	"bondable", "link-security", "ssp", "br/edr", "hs", "le",
+	"advertising", "secure-conn", "debug-keys", "privacy",
+	"configuration", "static-addr",
+};
+
+static void mgmt_new_settings(uint16_t len, const void *buf)
+{
+	uint32_t settings;
+	unsigned int i;
+
+	if (len < 4) {
+		printf("* Malformed New Settings control\n");
+		return;
+	}
+
+	settings = get_le32(buf);
+
+	printf("@ New Settings: 0x%4.4x\n", settings);
+
+	if (settings) {
+		printf("%-12c", ' ');
+		for (i = 0; i < NELEM(settings_str); i++) {
+			if (settings & (1 << i))
+				printf("%s ", settings_str[i]);
+		}
+		printf("\n");
+	}
+
+	buf += 4;
+	len -= 4;
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_class_of_dev_changed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_class_of_dev_changed *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Class of Device Changed control\n");
+		return;
+	}
+
+	printf("@ Class of Device Changed: 0x%2.2x%2.2x%2.2x\n",
+						ev->dev_class[2],
+						ev->dev_class[1],
+						ev->dev_class[0]);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_local_name_changed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_local_name_changed *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Local Name Changed control\n");
+		return;
+	}
+
+	printf("@ Local Name Changed: %s (%s)\n", ev->name, ev->short_name);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_new_link_key(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_new_link_key *ev = buf;
+	const char *type;
+	char str[18];
+	static const char *types[] = {
+		"Combination key",
+		"Local Unit key",
+		"Remote Unit key",
+		"Debug Combination key",
+		"Unauthenticated Combination key from P-192",
+		"Authenticated Combination key from P-192",
+		"Changed Combination key",
+		"Unauthenticated Combination key from P-256",
+		"Authenticated Combination key from P-256",
+	};
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New Link Key control\n");
+		return;
+	}
+
+	if (ev->key.type < NELEM(types))
+		type = types[ev->key.type];
+	else
+		type = "Reserved";
+
+	ba2str(&ev->key.addr.bdaddr, str);
+
+	printf("@ New Link Key: %s (%d) %s (%u)\n", str,
+				ev->key.addr.type, type, ev->key.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_new_long_term_key(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_new_long_term_key *ev = buf;
+	const char *type;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New Long Term Key control\n");
+		return;
+	}
+
+	/* LE SC keys are both for master and slave */
+	switch (ev->key.type) {
+	case 0x00:
+		if (ev->key.master)
+			type = "Master (Unauthenticated)";
+		else
+			type = "Slave (Unauthenticated)";
+		break;
+	case 0x01:
+		if (ev->key.master)
+			type = "Master (Authenticated)";
+		else
+			type = "Slave (Authenticated)";
+		break;
+	case 0x02:
+		type = "SC (Unauthenticated)";
+		break;
+	case 0x03:
+		type = "SC (Authenticated)";
+		break;
+	case 0x04:
+		type = "SC (Debug)";
+		break;
+	default:
+		type = "<unknown>";
+		break;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, str);
+
+	printf("@ New Long Term Key: %s (%d) %s 0x%02x\n", str,
+			ev->key.addr.type, type, ev->key.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_connected(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_connected *ev = buf;
+	uint32_t flags;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Connected control\n");
+		return;
+	}
+
+	flags = le32_to_cpu(ev->flags);
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Connected: %s (%d) flags 0x%4.4x\n",
+						str, ev->addr.type, flags);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_disconnected(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_disconnected *ev = buf;
+	char str[18];
+	uint8_t reason;
+	uint16_t consumed_len;
+
+	if (len < sizeof(struct mgmt_addr_info)) {
+		printf("* Malformed Device Disconnected control\n");
+		return;
+	}
+
+	if (len < sizeof(*ev)) {
+		reason = MGMT_DEV_DISCONN_UNKNOWN;
+		consumed_len = len;
+	} else {
+		reason = ev->reason;
+		consumed_len = sizeof(*ev);
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Disconnected: %s (%d) reason %u\n", str, ev->addr.type,
+									reason);
+
+	buf += consumed_len;
+	len -= consumed_len;
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_connect_failed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_connect_failed *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Connect Failed control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Connect Failed: %s (%d) status 0x%2.2x\n",
+					str, ev->addr.type, ev->status);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_pin_code_request(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_pin_code_request *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed PIN Code Request control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ PIN Code Request: %s (%d) secure 0x%2.2x\n",
+					str, ev->addr.type, ev->secure);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_user_confirm_request(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_user_confirm_request *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed User Confirmation Request control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ User Confirmation Request: %s (%d) hint %d value %d\n",
+			str, ev->addr.type, ev->confirm_hint, ev->value);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_user_passkey_request(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_user_passkey_request *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed User Passkey Request control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ User Passkey Request: %s (%d)\n", str, ev->addr.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_auth_failed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_auth_failed *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Authentication Failed control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Authentication Failed: %s (%d) status 0x%2.2x\n",
+					str, ev->addr.type, ev->status);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_found(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_found *ev = buf;
+	uint32_t flags;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Found control\n");
+		return;
+	}
+
+	flags = le32_to_cpu(ev->flags);
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n",
+					str, ev->addr.type, ev->rssi, flags);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_discovering(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_discovering *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Discovering control\n");
+		return;
+	}
+
+	printf("@ Discovering: 0x%2.2x (%d)\n", ev->discovering, ev->type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_blocked(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_blocked *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Blocked control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Blocked: %s (%d)\n", str, ev->addr.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_unblocked(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_unblocked *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Unblocked control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Unblocked: %s (%d)\n", str, ev->addr.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_unpaired(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_unpaired *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Unpaired control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Unpaired: %s (%d)\n", str, ev->addr.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_passkey_notify(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_passkey_notify *ev = buf;
+	uint32_t passkey;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Passkey Notify control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	passkey = le32_to_cpu(ev->passkey);
+
+	printf("@ Passkey Notify: %s (%d) passkey %06u entered %u\n",
+				str, ev->addr.type, passkey, ev->entered);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_new_irk(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_new_irk *ev = buf;
+	char addr[18], rpa[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New IRK control\n");
+		return;
+	}
+
+	ba2str(&ev->rpa, rpa);
+	ba2str(&ev->key.addr.bdaddr, addr);
+
+	printf("@ New IRK: %s (%d) %s\n", addr, ev->key.addr.type, rpa);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_new_csrk(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_new_csrk *ev = buf;
+	const char *type;
+	char addr[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New CSRK control\n");
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, addr);
+
+	switch (ev->key.type) {
+	case 0x00:
+		type = "Local Unauthenticated";
+		break;
+	case 0x01:
+		type = "Remote Unauthenticated";
+		break;
+	case 0x02:
+		type = "Local Authenticated";
+		break;
+	case 0x03:
+		type = "Remote Authenticated";
+		break;
+	default:
+		type = "<unknown>";
+		break;
+	}
+
+	printf("@ New CSRK: %s (%d) %s (%u)\n", addr, ev->key.addr.type,
+							type, ev->key.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_added(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_added *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Added control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Added: %s (%d) %d\n", str, ev->addr.type, ev->action);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_device_removed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_device_removed *ev = buf;
+	char str[18];
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Device Removed control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("@ Device Removed: %s (%d)\n", str, ev->addr.type);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_new_conn_param(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_new_conn_param *ev = buf;
+	char addr[18];
+	uint16_t min, max, latency, timeout;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed New Connection Parameter control\n");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	min = le16_to_cpu(ev->min_interval);
+	max = le16_to_cpu(ev->max_interval);
+	latency = le16_to_cpu(ev->latency);
+	timeout = le16_to_cpu(ev->timeout);
+
+	printf("@ New Conn Param: %s (%d) hint %d min 0x%4.4x max 0x%4.4x "
+		"latency 0x%4.4x timeout 0x%4.4x\n", addr, ev->addr.type,
+		ev->store_hint, min, max, latency, timeout);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_advertising_added(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_advertising_added *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Advertising Added control\n");
+		return;
+	}
+
+	printf("@ Advertising Added: %u\n", ev->instance);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+static void mgmt_advertising_removed(uint16_t len, const void *buf)
+{
+	const struct mgmt_ev_advertising_removed *ev = buf;
+
+	if (len < sizeof(*ev)) {
+		printf("* Malformed Advertising Removed control\n");
+		return;
+	}
+
+	printf("@ Advertising Removed: %u\n", ev->instance);
+
+	buf += sizeof(*ev);
+	len -= sizeof(*ev);
+
+	packet_hexdump(buf, len);
+}
+
+void control_message(uint16_t opcode, const void *data, uint16_t size)
+{
+	switch (opcode) {
+	case MGMT_EV_INDEX_ADDED:
+		mgmt_index_added(size, data);
+		break;
+	case MGMT_EV_INDEX_REMOVED:
+		mgmt_index_removed(size, data);
+		break;
+	case MGMT_EV_CONTROLLER_ERROR:
+		mgmt_controller_error(size, data);
+		break;
+	case MGMT_EV_NEW_SETTINGS:
+		mgmt_new_settings(size, data);
+		break;
+	case MGMT_EV_CLASS_OF_DEV_CHANGED:
+		mgmt_class_of_dev_changed(size, data);
+		break;
+	case MGMT_EV_LOCAL_NAME_CHANGED:
+		mgmt_local_name_changed(size, data);
+		break;
+	case MGMT_EV_NEW_LINK_KEY:
+		mgmt_new_link_key(size, data);
+		break;
+	case MGMT_EV_NEW_LONG_TERM_KEY:
+		mgmt_new_long_term_key(size, data);
+		break;
+	case MGMT_EV_DEVICE_CONNECTED:
+		mgmt_device_connected(size, data);
+		break;
+	case MGMT_EV_DEVICE_DISCONNECTED:
+		mgmt_device_disconnected(size, data);
+		break;
+	case MGMT_EV_CONNECT_FAILED:
+		mgmt_connect_failed(size, data);
+		break;
+	case MGMT_EV_PIN_CODE_REQUEST:
+		mgmt_pin_code_request(size, data);
+		break;
+	case MGMT_EV_USER_CONFIRM_REQUEST:
+		mgmt_user_confirm_request(size, data);
+		break;
+	case MGMT_EV_USER_PASSKEY_REQUEST:
+		mgmt_user_passkey_request(size, data);
+		break;
+	case MGMT_EV_AUTH_FAILED:
+		mgmt_auth_failed(size, data);
+		break;
+	case MGMT_EV_DEVICE_FOUND:
+		mgmt_device_found(size, data);
+		break;
+	case MGMT_EV_DISCOVERING:
+		mgmt_discovering(size, data);
+		break;
+	case MGMT_EV_DEVICE_BLOCKED:
+		mgmt_device_blocked(size, data);
+		break;
+	case MGMT_EV_DEVICE_UNBLOCKED:
+		mgmt_device_unblocked(size, data);
+		break;
+	case MGMT_EV_DEVICE_UNPAIRED:
+		mgmt_device_unpaired(size, data);
+		break;
+	case MGMT_EV_PASSKEY_NOTIFY:
+		mgmt_passkey_notify(size, data);
+		break;
+	case MGMT_EV_NEW_IRK:
+		mgmt_new_irk(size, data);
+		break;
+	case MGMT_EV_NEW_CSRK:
+		mgmt_new_csrk(size, data);
+		break;
+	case MGMT_EV_DEVICE_ADDED:
+		mgmt_device_added(size, data);
+		break;
+	case MGMT_EV_DEVICE_REMOVED:
+		mgmt_device_removed(size, data);
+		break;
+	case MGMT_EV_NEW_CONN_PARAM:
+		mgmt_new_conn_param(size, data);
+		break;
+	case MGMT_EV_UNCONF_INDEX_ADDED:
+		mgmt_unconf_index_added(size, data);
+		break;
+	case MGMT_EV_UNCONF_INDEX_REMOVED:
+		mgmt_unconf_index_removed(size, data);
+		break;
+	case MGMT_EV_NEW_CONFIG_OPTIONS:
+		mgmt_new_config_options(size, data);
+		break;
+	case MGMT_EV_EXT_INDEX_ADDED:
+		mgmt_ext_index_added(size, data);
+		break;
+	case MGMT_EV_EXT_INDEX_REMOVED:
+		mgmt_ext_index_removed(size, data);
+		break;
+	case MGMT_EV_ADVERTISING_ADDED:
+		mgmt_advertising_added(size, data);
+		break;
+	case MGMT_EV_ADVERTISING_REMOVED:
+		mgmt_advertising_removed(size, data);
+		break;
+	default:
+		printf("* Unknown control (code %d len %d)\n", opcode, size);
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+	struct control_data *data = user_data;
+	unsigned char control[64];
+	struct mgmt_hdr hdr;
+	struct msghdr msg;
+	struct iovec iov[2];
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(data->fd);
+		return;
+	}
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = MGMT_HDR_SIZE;
+	iov[1].iov_base = data->buf;
+	iov[1].iov_len = sizeof(data->buf);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 2;
+	msg.msg_control = control;
+	msg.msg_controllen = sizeof(control);
+
+	while (1) {
+		struct cmsghdr *cmsg;
+		struct timeval *tv = NULL;
+		struct timeval ctv;
+		struct ucred *cred = NULL;
+		struct ucred ccred;
+		uint16_t opcode, index, pktlen;
+		ssize_t len;
+
+		len = recvmsg(data->fd, &msg, MSG_DONTWAIT);
+		if (len < 0)
+			break;
+
+		if (len < MGMT_HDR_SIZE)
+			break;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level != SOL_SOCKET)
+				continue;
+
+			if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+				memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+				tv = &ctv;
+			}
+
+			if (cmsg->cmsg_type == SCM_CREDENTIALS) {
+				memcpy(&ccred, CMSG_DATA(cmsg), sizeof(ccred));
+				cred = &ccred;
+			}
+		}
+
+		opcode = le16_to_cpu(hdr.opcode);
+		index  = le16_to_cpu(hdr.index);
+		pktlen = le16_to_cpu(hdr.len);
+
+		switch (data->channel) {
+		case HCI_CHANNEL_CONTROL:
+			packet_control(tv, cred, index, opcode,
+							data->buf, pktlen);
+			break;
+		case HCI_CHANNEL_MONITOR:
+			btsnoop_write_hci(btsnoop_file, tv, index, opcode,
+							data->buf, pktlen);
+			ellisys_inject_hci(tv, index, opcode,
+							data->buf, pktlen);
+			packet_monitor(tv, cred, index, opcode,
+							data->buf, pktlen);
+			break;
+		}
+	}
+}
+
+static int open_socket(uint16_t channel)
+{
+	struct sockaddr_hci addr;
+	int fd, opt = 1;
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open channel");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	addr.hci_channel = channel;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		if (errno == EINVAL) {
+			/* Fallback to hcidump support */
+			hcidump_fallback = true;
+			close(fd);
+			return -1;
+		}
+		perror("Failed to bind channel");
+		close(fd);
+		return -1;
+	}
+
+	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
+		perror("Failed to enable timestamps");
+		close(fd);
+		return -1;
+	}
+
+	if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) < 0) {
+		perror("Failed to enable credentials");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int open_channel(uint16_t channel)
+{
+	struct control_data *data;
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		return -1;
+
+	memset(data, 0, sizeof(*data));
+	data->channel = channel;
+
+	data->fd = open_socket(channel);
+	if (data->fd < 0) {
+		free(data);
+		return -1;
+	}
+
+	mainloop_add_fd(data->fd, EPOLLIN, data_callback, data, free_data);
+
+	return 0;
+}
+
+static void client_callback(int fd, uint32_t events, void *user_data)
+{
+	struct control_data *data = user_data;
+	ssize_t len;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(data->fd);
+		return;
+	}
+
+	len = recv(data->fd, data->buf + data->offset,
+			sizeof(data->buf) - data->offset, MSG_DONTWAIT);
+	if (len < 0)
+		return;
+
+	data->offset += len;
+
+	if (data->offset > MGMT_HDR_SIZE) {
+		struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf;
+		uint16_t pktlen = le16_to_cpu(hdr->len);
+
+		if (data->offset > pktlen + MGMT_HDR_SIZE) {
+			uint16_t opcode = le16_to_cpu(hdr->opcode);
+			uint16_t index = le16_to_cpu(hdr->index);
+
+			packet_monitor(NULL, NULL, index, opcode,
+					data->buf + MGMT_HDR_SIZE, pktlen);
+
+			data->offset -= pktlen + MGMT_HDR_SIZE;
+
+			if (data->offset > 0)
+				memmove(data->buf, data->buf +
+					 MGMT_HDR_SIZE + pktlen, data->offset);
+		}
+	}
+}
+
+static void server_accept_callback(int fd, uint32_t events, void *user_data)
+{
+	struct control_data *data;
+	struct sockaddr_un addr;
+	socklen_t len;
+	int nfd;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	len = sizeof(addr);
+
+	nfd = accept(fd, (struct sockaddr *) &addr, &len);
+	if (nfd < 0) {
+		perror("Failed to accept client socket");
+		return;
+	}
+
+	printf("--- New monitor connection ---\n");
+
+	data = malloc(sizeof(*data));
+	if (!data) {
+		close(nfd);
+		return;
+	}
+
+	memset(data, 0, sizeof(*data));
+	data->channel = HCI_CHANNEL_MONITOR;
+	data->fd = nfd;
+
+        mainloop_add_fd(data->fd, EPOLLIN, client_callback, data, free_data);
+}
+
+static int server_fd = -1;
+
+void control_server(const char *path)
+{
+	struct sockaddr_un addr;
+	int fd;
+
+	if (server_fd >= 0)
+		return;
+
+	unlink(path);
+
+	fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to open server socket");
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strcpy(addr.sun_path, path);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind server socket");
+		close(fd);
+		return;
+	}
+
+	if (listen(fd, 5) < 0) {
+		perror("Failed to listen server socket");
+		close(fd);
+		return;
+	}
+
+	if (mainloop_add_fd(fd, EPOLLIN, server_accept_callback,
+						NULL, NULL) < 0) {
+		close(fd);
+		return;
+	}
+
+	server_fd = fd;
+}
+
+bool control_writer(const char *path)
+{
+	btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
+
+	return !!btsnoop_file;
+}
+
+void control_reader(const char *path)
+{
+	unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+	uint16_t pktlen;
+	uint32_t format;
+	struct timeval tv;
+
+	btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+	if (!btsnoop_file)
+		return;
+
+	format = btsnoop_get_format(btsnoop_file);
+
+	switch (format) {
+	case BTSNOOP_FORMAT_HCI:
+	case BTSNOOP_FORMAT_UART:
+	case BTSNOOP_FORMAT_SIMULATOR:
+		packet_del_filter(PACKET_FILTER_SHOW_INDEX);
+		break;
+
+	case BTSNOOP_FORMAT_MONITOR:
+		packet_add_filter(PACKET_FILTER_SHOW_INDEX);
+		break;
+	}
+
+	open_pager();
+
+	switch (format) {
+	case BTSNOOP_FORMAT_HCI:
+	case BTSNOOP_FORMAT_UART:
+	case BTSNOOP_FORMAT_MONITOR:
+		while (1) {
+			uint16_t index, opcode;
+
+			if (!btsnoop_read_hci(btsnoop_file, &tv, &index,
+							&opcode, buf, &pktlen))
+				break;
+
+			if (opcode == 0xffff)
+				continue;
+
+			packet_monitor(&tv, NULL, index, opcode, buf, pktlen);
+			ellisys_inject_hci(&tv, index, opcode, buf, pktlen);
+		}
+		break;
+
+	case BTSNOOP_FORMAT_SIMULATOR:
+		while (1) {
+			uint16_t frequency;
+
+			if (!btsnoop_read_phy(btsnoop_file, &tv, &frequency,
+								buf, &pktlen))
+				break;
+
+			packet_simulator(&tv, frequency, buf, pktlen);
+		}
+		break;
+	}
+
+	close_pager();
+
+	btsnoop_unref(btsnoop_file);
+}
+
+int control_tracing(void)
+{
+	packet_add_filter(PACKET_FILTER_SHOW_INDEX);
+
+	if (server_fd >= 0)
+		return 0;
+
+	if (open_channel(HCI_CHANNEL_MONITOR) < 0) {
+		if (!hcidump_fallback)
+			return -1;
+		if (hcidump_tracing() < 0)
+			return -1;
+		return 0;
+	}
+
+	open_channel(HCI_CHANNEL_CONTROL);
+
+	return 0;
+}
diff --git a/bluez/repo/monitor/control.h b/bluez/repo/monitor/control.h
new file mode 100644
index 0000000..28f16db
--- /dev/null
+++ b/bluez/repo/monitor/control.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+bool control_writer(const char *path);
+void control_reader(const char *path);
+void control_server(const char *path);
+int control_tracing(void);
+
+void control_message(uint16_t opcode, const void *data, uint16_t size);
diff --git a/bluez/repo/monitor/crc.c b/bluez/repo/monitor/crc.c
new file mode 100644
index 0000000..912b37e
--- /dev/null
+++ b/bluez/repo/monitor/crc.c
@@ -0,0 +1,84 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 "crc.h"
+
+uint32_t crc24_bit_reverse(uint32_t value)
+{
+	uint32_t result = 0;
+	uint8_t i;
+
+	for (i = 0; i < 24; i++)
+		result |= ((value >> i) & 1) << (23 - i);
+
+	return result;
+}
+
+uint32_t crc24_calculate(uint32_t preset, const uint8_t *data, uint8_t len)
+{
+	uint32_t state = preset;
+	uint8_t i;
+
+	for (i = 0; i < len; i++) {
+		uint8_t n, cur = data[i];
+
+		for (n = 0; n < 8; n++) {
+			int next_bit = (state ^ cur) & 1;
+
+			cur >>= 1;
+			state >>= 1;
+			if (next_bit) {
+				state |= 1 << 23;
+				state ^= 0x5a6000;
+			}
+		}
+	}
+
+	return state;
+}
+
+uint32_t crc24_reverse(uint32_t crc, const uint8_t *data, uint8_t len)
+{
+	uint32_t state = crc;
+	uint8_t i;
+
+	for (i = 0; i < len; i++) {
+		uint8_t n, cur = data[len - i - 1];
+
+		for (n = 0; n < 8; n++) {
+			int top_bit = state >> 23;
+
+			state = (state << 1) & 0xffffff;
+			state |= top_bit ^ ((cur >> (7 - n)) & 1);
+			if (top_bit)
+				state ^= 0xb4c000;
+		}
+	}
+
+	return state;
+}
diff --git a/bluez/repo/monitor/crc.h b/bluez/repo/monitor/crc.h
new file mode 100644
index 0000000..772388b
--- /dev/null
+++ b/bluez/repo/monitor/crc.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+uint32_t crc24_bit_reverse(uint32_t value);
+
+uint32_t crc24_calculate(uint32_t preset, const uint8_t *data, uint8_t len);
+uint32_t crc24_reverse(uint32_t crc, const uint8_t *data, uint8_t len);
diff --git a/bluez/repo/monitor/display.c b/bluez/repo/monitor/display.c
new file mode 100644
index 0000000..411af94
--- /dev/null
+++ b/bluez/repo/monitor/display.c
@@ -0,0 +1,171 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+
+#include "display.h"
+
+static pid_t pager_pid = 0;
+
+bool use_color(void)
+{
+	static int cached_use_color = -1;
+
+	if (__builtin_expect(!!(cached_use_color < 0), 0))
+		cached_use_color = isatty(STDOUT_FILENO) > 0 || pager_pid > 0;
+
+	return cached_use_color;
+}
+
+int num_columns(void)
+{
+	static int cached_num_columns = -1;
+
+	if (__builtin_expect(!!(cached_num_columns < 0), 0)) {
+		struct winsize ws;
+
+		if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0 ||
+								ws.ws_col == 0)
+			cached_num_columns = FALLBACK_TERMINAL_WIDTH;
+		else
+			cached_num_columns = ws.ws_col;
+	}
+
+	return cached_num_columns;
+}
+
+static void close_pipe(int p[])
+{
+	if (p[0] >= 0)
+		close(p[0]);
+	if (p[1] >= 0)
+		close(p[1]);
+}
+
+static void wait_for_terminate(pid_t pid)
+{
+	siginfo_t dummy;
+
+	for (;;) {
+		memset(&dummy, 0, sizeof(dummy));
+
+		if (waitid(P_PID, pid, &dummy, WEXITED) < 0) {
+			if (errno == EINTR)
+				continue;
+			return;
+		}
+
+		return;
+	}
+}
+
+void open_pager(void)
+{
+	const char *pager;
+	pid_t parent_pid;
+	int fd[2];
+
+	if (pager_pid > 0)
+		return;
+
+	pager = getenv("PAGER");
+	if (pager) {
+		if (!*pager || strcmp(pager, "cat") == 0)
+			return;
+	}
+
+	if (!(isatty(STDOUT_FILENO) > 0))
+		return;
+
+	num_columns();
+
+	if (pipe(fd) < 0) {
+		perror("Failed to create pager pipe");
+		return;
+	}
+
+	parent_pid = getpid();
+
+	pager_pid = fork();
+	if (pager_pid < 0) {
+		perror("Failed to fork pager");
+		close_pipe(fd);
+		return;
+	}
+
+	if (pager_pid == 0) {
+		dup2(fd[0], STDIN_FILENO);
+		close_pipe(fd);
+
+		setenv("LESS", "FRSX", 0);
+
+		if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+			_exit(EXIT_FAILURE);
+
+		if (getppid() != parent_pid)
+			_exit(EXIT_SUCCESS);
+
+		if (pager) {
+			execlp(pager, pager, NULL);
+			execl("/bin/sh", "sh", "-c", pager, NULL);
+		}
+
+		execlp("pager", "pager", NULL);
+		execlp("less", "less", NULL);
+		execlp("more", "more", NULL);
+
+		_exit(EXIT_FAILURE);
+	}
+
+	if (dup2(fd[1], STDOUT_FILENO) < 0) {
+		perror("Failed to duplicate pager pipe");
+		return;
+	}
+
+	close_pipe(fd);
+}
+
+void close_pager(void)
+{
+	if (pager_pid <= 0)
+		return;
+
+	fclose(stdout);
+	kill(pager_pid, SIGCONT);
+	wait_for_terminate(pager_pid);
+	pager_pid = 0;
+}
diff --git a/bluez/repo/monitor/display.h b/bluez/repo/monitor/display.h
new file mode 100644
index 0000000..36e189a
--- /dev/null
+++ b/bluez/repo/monitor/display.h
@@ -0,0 +1,65 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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>
+
+bool use_color(void);
+
+#define COLOR_OFF	"\x1B[0m"
+#define COLOR_BLACK	"\x1B[0;30m"
+#define COLOR_RED	"\x1B[0;31m"
+#define COLOR_GREEN	"\x1B[0;32m"
+#define COLOR_YELLOW	"\x1B[0;33m"
+#define COLOR_BLUE	"\x1B[0;34m"
+#define COLOR_MAGENTA	"\x1B[0;35m"
+#define COLOR_CYAN	"\x1B[0;36m"
+#define COLOR_WHITE	"\x1B[0;37m"
+#define COLOR_WHITE_BG	"\x1B[0;47;30m"
+#define COLOR_HIGHLIGHT	"\x1B[1;39m"
+
+#define COLOR_ERROR	"\x1B[1;31m"
+#define COLOR_WARN	"\x1B[1m"
+#define COLOR_INFO	COLOR_OFF
+#define COLOR_DEBUG	COLOR_WHITE
+
+#define FALLBACK_TERMINAL_WIDTH 80
+
+#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
+do { \
+	printf("%*c%s%s%s%s" fmt "%s\n", (indent), ' ', \
+		use_color() ? (color1) : "", prefix, title, \
+		use_color() ? (color2) : "", ## args, \
+		use_color() ? COLOR_OFF : ""); \
+} while (0)
+
+#define print_text(color, fmt, args...) \
+		print_indent(8, COLOR_OFF, "", "", color, fmt, ## args)
+
+#define print_field(fmt, args...) \
+		print_indent(8, COLOR_OFF, "", "", COLOR_OFF, fmt, ## args)
+
+int num_columns(void);
+
+void open_pager(void);
+void close_pager(void);
diff --git a/bluez/repo/monitor/ellisys.c b/bluez/repo/monitor/ellisys.c
new file mode 100644
index 0000000..bafbb5d
--- /dev/null
+++ b/bluez/repo/monitor/ellisys.c
@@ -0,0 +1,162 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "src/shared/btsnoop.h"
+#include "ellisys.h"
+
+static int ellisys_fd = -1;
+static uint16_t ellisys_index = 0xffff;
+
+void ellisys_enable(const char *server, uint16_t port)
+{
+	struct sockaddr_in addr;
+	int fd;
+
+	if (ellisys_fd >= 0) {
+		fprintf(stderr, "Ellisys injection already enabled\n");
+		return;
+	}
+
+	fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to open UDP injection socket");
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = inet_addr(server);
+	addr.sin_port = htons(port);
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to connect UDP injection socket");
+		close(fd);
+		return;
+	}
+
+	ellisys_fd = fd;
+}
+
+void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size)
+{
+	uint8_t msg[] = {
+		/* HCI Injection Service, Version 1 */
+		0x02, 0x00, 0x01,
+		/* DateTimeNs Object */
+		0x02, 0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		/* Bitrate Object, 12000000 bps */
+		0x80, 0x00, 0x1b, 0x37, 0x4b,
+		/* HCI Packet Type Object */
+		0x81, 0x00,
+		/* HCI Packet Data Object */
+		0x82
+	};
+	long nsec;
+	time_t t;
+	struct tm tm;
+	struct iovec iov[2];
+	int iovcnt;
+
+	if (!tv)
+		return;
+
+	if (ellisys_fd < 0)
+		return;
+
+	if (ellisys_index == 0xffff)
+		ellisys_index = index;
+
+	if (index != ellisys_index)
+		return;
+
+	t = tv->tv_sec;
+	localtime_r(&t, &tm);
+
+	nsec = ((tm.tm_sec + (tm.tm_min * 60) +
+			(tm.tm_hour * 3600)) * 1000000l + tv->tv_usec) * 1000l;
+
+	msg[4]  = (1900 + tm.tm_year) & 0xff;
+	msg[5]  = (1900 + tm.tm_year) >> 8;
+	msg[6]  = (tm.tm_mon + 1) & 0xff;
+	msg[7]  = tm.tm_mday & 0xff;
+	msg[8]  = (nsec & 0x0000000000ffl);
+	msg[9]  = (nsec & 0x00000000ff00l) >> 8;
+	msg[10] = (nsec & 0x000000ff0000l) >> 16;
+	msg[11] = (nsec & 0x0000ff000000l) >> 24;
+	msg[12] = (nsec & 0x00ff00000000l) >> 32;
+	msg[13] = (nsec & 0xff0000000000l) >> 40;
+
+	switch (opcode) {
+	case BTSNOOP_OPCODE_COMMAND_PKT:
+		msg[20] = 0x01;
+		break;
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		msg[20] = 0x84;
+		break;
+	case BTSNOOP_OPCODE_ACL_TX_PKT:
+		msg[20] = 0x02;
+		break;
+	case BTSNOOP_OPCODE_ACL_RX_PKT:
+		msg[20] = 0x82;
+		break;
+	case BTSNOOP_OPCODE_SCO_TX_PKT:
+		msg[20] = 0x03;
+		break;
+	case BTSNOOP_OPCODE_SCO_RX_PKT:
+		msg[20] = 0x83;
+		break;
+	default:
+		return;
+	}
+
+	iov[0].iov_base = msg;
+	iov[0].iov_len  = sizeof(msg);
+
+	if (size > 0) {
+		iov[1].iov_base = (void *) data;
+		iov[1].iov_len  = size;
+		iovcnt = 2;
+	} else
+		iovcnt = 1;
+
+	if (writev(ellisys_fd, iov, iovcnt) < 0)
+		perror("Failed to send Ellisys injection packet");
+}
diff --git a/bluez/repo/monitor/ellisys.h b/bluez/repo/monitor/ellisys.h
new file mode 100644
index 0000000..8be888d
--- /dev/null
+++ b/bluez/repo/monitor/ellisys.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+void ellisys_enable(const char *server, uint16_t port);
+
+void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size);
diff --git a/bluez/repo/monitor/hcidump.c b/bluez/repo/monitor/hcidump.c
new file mode 100644
index 0000000..bef1338
--- /dev/null
+++ b/bluez/repo/monitor/hcidump.c
@@ -0,0 +1,412 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/shared/mainloop.h"
+
+#include "packet.h"
+#include "hcidump.h"
+
+struct hcidump_data {
+	uint16_t index;
+	int fd;
+};
+
+static void free_data(void *user_data)
+{
+	struct hcidump_data *data = user_data;
+
+	close(data->fd);
+
+	free(data);
+}
+
+static int open_hci_dev(uint16_t index)
+{
+	struct sockaddr_hci addr;
+	struct hci_filter flt;
+	int fd, opt = 1;
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open channel");
+		return -1;
+	}
+
+	/* Setup filter */
+	hci_filter_clear(&flt);
+	hci_filter_all_ptypes(&flt);
+	hci_filter_all_events(&flt);
+
+	if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+		perror("Failed to set HCI filter");
+		close(fd);
+		return -1;
+	}
+
+	if (setsockopt(fd, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
+		perror("Failed to enable HCI data direction info");
+		close(fd);
+		return -1;
+	}
+
+	if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+		perror("Failed to enable HCI time stamps");
+		close(fd);
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = index;
+	addr.hci_channel = HCI_CHANNEL_RAW;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind channel");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static void device_callback(int fd, uint32_t events, void *user_data)
+{
+	struct hcidump_data *data = user_data;
+	unsigned char buf[HCI_MAX_FRAME_SIZE * 2];
+	unsigned char control[64];
+	struct msghdr msg;
+	struct iovec iov;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	iov.iov_base = buf;
+	iov.iov_len = sizeof(buf);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = control;
+	msg.msg_controllen = sizeof(control);
+
+	while (1) {
+		struct cmsghdr *cmsg;
+		struct timeval *tv = NULL;
+		struct timeval ctv;
+		int dir = -1;
+		ssize_t len;
+
+		len = recvmsg(fd, &msg, MSG_DONTWAIT);
+		if (len < 0)
+			break;
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level != SOL_HCI)
+				continue;
+
+			switch (cmsg->cmsg_type) {
+			case HCI_DATA_DIR:
+				memcpy(&dir, CMSG_DATA(cmsg), sizeof(dir));
+				break;
+			case HCI_CMSG_TSTAMP:
+				memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+				tv = &ctv;
+				break;
+			}
+		}
+
+		if (dir < 0 || len < 1)
+			continue;
+
+		switch (buf[0]) {
+		case HCI_COMMAND_PKT:
+			packet_hci_command(tv, NULL, data->index,
+							buf + 1, len - 1);
+			break;
+		case HCI_EVENT_PKT:
+			packet_hci_event(tv, NULL, data->index,
+							buf + 1, len - 1);
+			break;
+		case HCI_ACLDATA_PKT:
+			packet_hci_acldata(tv, NULL, data->index, !!dir,
+							buf + 1, len - 1);
+			break;
+		case HCI_SCODATA_PKT:
+			packet_hci_scodata(tv, NULL, data->index, !!dir,
+							buf + 1, len - 1);
+			break;
+		}
+	}
+}
+
+static void open_device(uint16_t index)
+{
+	struct hcidump_data *data;
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		return;
+
+	memset(data, 0, sizeof(*data));
+	data->index = index;
+
+	data->fd = open_hci_dev(index);
+	if (data->fd < 0) {
+		free(data);
+		return;
+	}
+
+	mainloop_add_fd(data->fd, EPOLLIN, device_callback, data, free_data);
+}
+
+static void device_info(int fd, uint16_t index, uint8_t *type, uint8_t *bus,
+						bdaddr_t *bdaddr, char *name)
+{
+	struct hci_dev_info di;
+
+	memset(&di, 0, sizeof(di));
+	di.dev_id = index;
+
+	if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0) {
+		perror("Failed to get device information");
+		return;
+	}
+
+	*type = di.type >> 4;
+	*bus = di.type & 0x0f;
+
+	bacpy(bdaddr, &di.bdaddr);
+	memcpy(name, di.name, 8);
+}
+
+static void device_list(int fd, int max_dev)
+{
+	struct hci_dev_list_req *dl;
+	struct hci_dev_req *dr;
+	int i;
+
+	dl = malloc(max_dev * sizeof(*dr) + sizeof(*dl));
+	if (!dl) {
+		perror("Failed to allocate device list memory");
+		return;
+	}
+
+	memset(dl, 0, max_dev * sizeof(*dr) + sizeof(*dl));
+	dl->dev_num = max_dev;
+
+	dr = dl->dev_req;
+
+	if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) {
+		perror("Failed to get device list");
+		goto done;
+	}
+
+	for (i = 0; i < dl->dev_num; i++, dr++) {
+		struct timeval tmp_tv, *tv = NULL;
+		uint8_t type = 0xff, bus = 0xff;
+		char str[18], name[8] = "";
+		bdaddr_t bdaddr;
+
+		bacpy(&bdaddr, BDADDR_ANY);
+
+		if (!gettimeofday(&tmp_tv, NULL))
+			tv = &tmp_tv;
+
+		device_info(fd, dr->dev_id, &type, &bus, &bdaddr, name);
+		ba2str(&bdaddr, str);
+		packet_new_index(tv, dr->dev_id, str, type, bus, name);
+		open_device(dr->dev_id);
+	}
+
+done:
+	free(dl);
+}
+
+static int open_stack_internal(void)
+{
+	struct sockaddr_hci addr;
+	struct hci_filter flt;
+	int fd, opt = 1;
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open channel");
+		return -1;
+	}
+
+	/* Setup filter */
+	hci_filter_clear(&flt);
+	hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+	hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+
+	if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+		perror("Failed to set HCI filter");
+		close(fd);
+		return -1;
+	}
+
+	if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+		perror("Failed to enable HCI time stamps");
+		close(fd);
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	addr.hci_channel = HCI_CHANNEL_RAW;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind channel");
+		close(fd);
+		return -1;
+	}
+
+	device_list(fd, HCI_MAX_DEV);
+
+	return fd;
+}
+
+static void stack_internal_callback(int fd, uint32_t events, void *user_data)
+{
+	unsigned char buf[HCI_MAX_FRAME_SIZE];
+	unsigned char control[32];
+	struct msghdr msg;
+	struct iovec iov;
+	struct cmsghdr *cmsg;
+	ssize_t len;
+	hci_event_hdr *eh;
+	evt_stack_internal *si;
+	evt_si_device *sd;
+	struct timeval *tv = NULL;
+	struct timeval ctv;
+	uint8_t type = 0xff, bus = 0xff;
+	char str[18], name[8] = "";
+	bdaddr_t bdaddr;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	iov.iov_base = buf;
+	iov.iov_len = sizeof(buf);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = control;
+	msg.msg_controllen = sizeof(control);
+
+	len = recvmsg(fd, &msg, MSG_DONTWAIT);
+	if (len < 0)
+		return;
+
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+					cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level != SOL_HCI)
+			continue;
+
+		switch (cmsg->cmsg_type) {
+		case HCI_CMSG_TSTAMP:
+			memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+			tv = &ctv;
+			break;
+		}
+	}
+
+	if (len < 1 + HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE +
+							EVT_SI_DEVICE_SIZE)
+		return;
+
+	if (buf[0] != HCI_EVENT_PKT)
+		return;
+
+	eh = (hci_event_hdr *) (buf + 1);
+	if (eh->evt != EVT_STACK_INTERNAL)
+		return;
+
+	si = (evt_stack_internal *) (buf + 1 + HCI_EVENT_HDR_SIZE);
+	if (si->type != EVT_SI_DEVICE)
+		return;
+
+	sd = (evt_si_device *) &si->data;
+
+	switch (sd->event) {
+	case HCI_DEV_REG:
+		device_info(fd, sd->dev_id, &type, &bus, &bdaddr, name);
+		ba2str(&bdaddr, str);
+		packet_new_index(tv, sd->dev_id, str, type, bus, name);
+		open_device(sd->dev_id);
+		break;
+	case HCI_DEV_UNREG:
+		ba2str(&bdaddr, str);
+		packet_del_index(tv, sd->dev_id, str);
+		break;
+	}
+}
+
+int hcidump_tracing(void)
+{
+	struct hcidump_data *data;
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		return -1;
+
+	memset(data, 0, sizeof(*data));
+	data->index = HCI_DEV_NONE;
+
+	data->fd = open_stack_internal();
+	if (data->fd < 0) {
+		free(data);
+		return -1;
+	}
+
+	mainloop_add_fd(data->fd, EPOLLIN, stack_internal_callback,
+							data, free_data);
+
+	return 0;
+}
diff --git a/bluez/repo/monitor/hcidump.h b/bluez/repo/monitor/hcidump.h
new file mode 100644
index 0000000..c908650
--- /dev/null
+++ b/bluez/repo/monitor/hcidump.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+int hcidump_tracing(void);
diff --git a/bluez/repo/monitor/hwdb.c b/bluez/repo/monitor/hwdb.c
new file mode 100644
index 0000000..6931660
--- /dev/null
+++ b/bluez/repo/monitor/hwdb.c
@@ -0,0 +1,137 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+
+#include "hwdb.h"
+
+#ifdef HAVE_UDEV_HWDB_NEW
+#include <libudev.h>
+
+bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model)
+{
+	struct udev *udev;
+	struct udev_hwdb *hwdb;
+	struct udev_list_entry *head, *entry;
+	bool result;
+
+	udev = udev_new();
+	if (!udev)
+		return false;
+
+	hwdb = udev_hwdb_new(udev);
+	if (!hwdb) {
+		result = false;
+		goto done;
+	}
+
+	*vendor = NULL;
+	*model = NULL;
+
+	head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0);
+
+	udev_list_entry_foreach(entry, head) {
+		const char *name = udev_list_entry_get_name(entry);
+
+		if (!name)
+			continue;
+
+		if (!*vendor && !strcmp(name, "ID_VENDOR_FROM_DATABASE"))
+			*vendor = strdup(udev_list_entry_get_value(entry));
+		else if (!*model && !strcmp(name, "ID_MODEL_FROM_DATABASE"))
+			*model = strdup(udev_list_entry_get_value(entry));
+	}
+
+	hwdb = udev_hwdb_unref(hwdb);
+
+	result = true;
+
+done:
+	udev = udev_unref(udev);
+
+	return result;
+}
+
+bool hwdb_get_company(const uint8_t *bdaddr, char **company)
+{
+	struct udev *udev;
+	struct udev_hwdb *hwdb;
+	struct udev_list_entry *head, *entry;
+	char modalias[11];
+	bool result;
+
+	if (!bdaddr[2] && !bdaddr[1] && !bdaddr[0])
+		return false;
+
+	sprintf(modalias, "OUI:%2.2X%2.2X%2.2X",
+				bdaddr[5], bdaddr[4], bdaddr[3]);
+
+	udev = udev_new();
+	if (!udev)
+		return false;
+
+	hwdb = udev_hwdb_new(udev);
+	if (!hwdb) {
+		result = false;
+		goto done;
+	}
+
+	*company = NULL;
+
+	head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0);
+
+	udev_list_entry_foreach(entry, head) {
+		const char *name = udev_list_entry_get_name(entry);
+
+		if (name && !strcmp(name, "ID_OUI_FROM_DATABASE")) {
+			*company = strdup(udev_list_entry_get_value(entry));
+			break;
+		}
+	}
+
+	hwdb = udev_hwdb_unref(hwdb);
+
+	result = true;
+
+done:
+	udev = udev_unref(udev);
+
+	return result;
+}
+#else
+bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model)
+{
+	return false;
+}
+
+bool hwdb_get_company(const uint8_t *bdaddr, char **company)
+{
+	return false;
+}
+#endif
diff --git a/bluez/repo/monitor/hwdb.h b/bluez/repo/monitor/hwdb.h
new file mode 100644
index 0000000..79f505a
--- /dev/null
+++ b/bluez/repo/monitor/hwdb.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+
+bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model);
+bool hwdb_get_company(const uint8_t *bdaddr, char **company);
diff --git a/bluez/repo/monitor/intel.c b/bluez/repo/monitor/intel.c
new file mode 100644
index 0000000..1ca9a95
--- /dev/null
+++ b/bluez/repo/monitor/intel.c
@@ -0,0 +1,932 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <inttypes.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "display.h"
+#include "packet.h"
+#include "lmp.h"
+#include "ll.h"
+#include "vendor.h"
+#include "intel.h"
+
+#define COLOR_UNKNOWN_EVENT_MASK	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_SCAN_STATUS	COLOR_WHITE_BG
+
+static void print_status(uint8_t status)
+{
+	packet_print_error("Status", status);
+}
+
+static void print_module(uint8_t module)
+{
+	const char *str;
+
+	switch (module) {
+	case 0x01:
+		str = "BC";
+		break;
+	case 0x02:
+		str = "HCI";
+		break;
+	case 0x03:
+		str = "LLC";
+		break;
+	case 0x04:
+		str = "OS";
+		break;
+	case 0x05:
+		str = "LM";
+		break;
+	case 0x06:
+		str = "SC";
+		break;
+	case 0x07:
+		str = "SP";
+		break;
+	case 0x08:
+		str = "OSAL";
+		break;
+	case 0x09:
+		str = "LC";
+		break;
+	case 0x0a:
+		str = "APP";
+		break;
+	case 0x0b:
+		str = "TLD";
+		break;
+	case 0xf0:
+		str = "Debug";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Module: %s (0x%2.2x)", str, module);
+}
+
+static void null_cmd(const void *data, uint8_t size)
+{
+}
+
+static void status_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+
+	print_status(status);
+}
+
+static void reset_cmd(const void *data, uint8_t size)
+{
+	uint8_t reset_type = get_u8(data);
+	uint8_t patch_enable = get_u8(data + 1);
+	uint8_t ddc_reload = get_u8(data + 2);
+	uint8_t boot_option = get_u8(data + 3);
+	uint32_t boot_addr = get_le32(data + 4);
+	const char *str;
+
+	switch (reset_type) {
+	case 0x00:
+		str = "Soft software reset";
+		break;
+	case 0x01:
+		str = "Hard software reset";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reset type: %s (0x%2.2x)", str, reset_type);
+
+	switch (patch_enable) {
+	case 0x00:
+		str = "Do not enable";
+		break;
+	case 0x01:
+		str = "Enable";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Patch vectors: %s (0x%2.2x)", str, patch_enable);
+
+	switch (ddc_reload) {
+	case 0x00:
+		str = "Do not reload";
+		break;
+	case 0x01:
+		str = "Reload from OTP";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("DDC parameters: %s (0x%2.2x)", str, ddc_reload);
+
+	switch (boot_option) {
+	case 0x00:
+		str = "Current image";
+		break;
+	case 0x01:
+		str = "Specified address";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Boot option: %s (0x%2.2x)", str, boot_option);
+	print_field("Boot address: 0x%8.8x", boot_addr);
+}
+
+static void read_version_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+	uint8_t hw_platform = get_u8(data + 1);
+	uint8_t hw_variant = get_u8(data + 2);
+	uint8_t hw_revision = get_u8(data + 3);
+	uint8_t fw_variant = get_u8(data + 4);
+	uint8_t fw_revision = get_u8(data + 5);
+	uint8_t fw_build_nn = get_u8(data + 6);
+	uint8_t fw_build_cw = get_u8(data + 7);
+	uint8_t fw_build_yy = get_u8(data + 8);
+	uint8_t fw_patch = get_u8(data + 9);
+
+	print_status(status);
+	print_field("Hardware platform: 0x%2.2x", hw_platform);
+	print_field("Hardware variant: 0x%2.2x", hw_variant);
+	print_field("Hardware revision: %u.%u", hw_revision >> 4,
+						hw_revision & 0x0f);
+	print_field("Firmware variant: 0x%2.2x", fw_variant);
+	print_field("Firmware revision: %u.%u", fw_revision >> 4,
+						fw_revision & 0x0f);
+
+	print_field("Firmware build: %u-%u.%u", fw_build_nn,
+					fw_build_cw, 2000 + fw_build_yy);
+	print_field("Firmware patch: %u", fw_patch);
+}
+
+static void secure_send_cmd(const void *data, uint8_t size)
+{
+	uint8_t type = get_u8(data);
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Init";
+		break;
+	case 0x01:
+		str = "Data";
+		break;
+	case 0x02:
+		str = "Sign";
+		break;
+	case 0x03:
+		str = "PKey";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s fragment (0x%2.2x)", str, type);
+
+	packet_hexdump(data + 1, size - 1);
+}
+
+static void manufacturer_mode_cmd(const void *data, uint8_t size)
+{
+	uint8_t mode = get_u8(data);
+	uint8_t reset = get_u8(data + 1);
+	const char *str;
+
+	switch (mode) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode switch: %s (0x%2.2x)", str, mode);
+
+	switch (reset) {
+	case 0x00:
+		str = "No reset";
+		break;
+	case 0x01:
+		str = "Reset and deactivate patches";
+		break;
+	case 0x02:
+		str = "Reset and activate patches";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reset behavior: %s (0x%2.2x)", str, reset);
+}
+
+static void write_bd_data_cmd(const void *data, uint8_t size)
+{
+	uint8_t features[8];
+
+	packet_print_addr("Address", data, false);
+	packet_hexdump(data + 6, 6);
+
+	memcpy(features, data + 12, 8);
+	packet_print_features_lmp(features, 0);
+
+	memcpy(features, data + 20, 1);
+	memset(features + 1, 0, 7);
+	packet_print_features_ll(features);
+
+	packet_hexdump(data + 21, size - 21);
+}
+
+static void read_bd_data_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+
+	print_status(status);
+	packet_print_addr("Address", data + 1, false);
+	packet_hexdump(data + 7, size - 7);
+}
+
+static void write_bd_address_cmd(const void *data, uint8_t size)
+{
+	packet_print_addr("Address", data, false);
+}
+
+static void act_deact_traces_cmd(const void *data, uint8_t size)
+{
+	uint8_t tx = get_u8(data);
+	uint8_t tx_arq = get_u8(data + 1);
+	uint8_t rx = get_u8(data + 2);
+
+	print_field("Transmit traces: 0x%2.2x", tx);
+	print_field("Transmit ARQ: 0x%2.2x", tx_arq);
+	print_field("Receive traces: 0x%2.2x", rx);
+}
+
+static void stimulate_exception_cmd(const void *data, uint8_t size)
+{
+	uint8_t type = get_u8(data);
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Fatal Exception";
+		break;
+	case 0x01:
+		str = "Debug Exception";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, type);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} events_table[] = {
+	{  0, "Bootup"			},
+	{  1, "SCO Rejected via LMP"	},
+	{  2, "PTT Switch Notification"	},
+	{  7, "Scan Status"		},
+	{  9, "Debug Exception"		},
+	{ 10, "Fatal Exception"		},
+	{ 11, "System Exception"	},
+	{ 13, "LE Link Established"	},
+	{ 14, "FW Trace String"		},
+	{ }
+};
+
+static void set_event_mask_cmd(const void *data, uint8_t size)
+{
+	const uint8_t *events_array = data;
+	uint64_t mask, events = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		events |= ((uint64_t) events_array[i]) << (i * 8);
+
+	print_field("Mask: 0x%16.16" PRIx64, events);
+
+	mask = events;
+
+	for (i = 0; events_table[i].str; i++) {
+		if (events & (((uint64_t) 1) << events_table[i].bit)) {
+			print_field("  %s", events_table[i].str);
+			mask &= ~(((uint64_t) 1) << events_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_EVENT_MASK, "  Unknown mask "
+						"(0x%16.16" PRIx64 ")", mask);
+}
+
+static void ddc_config_write_cmd(const void *data, uint8_t size)
+{
+	while (size > 0) {
+		uint8_t param_len = get_u8(data);
+		uint16_t param_id = get_le16(data + 1);
+
+		print_field("Identifier: 0x%4.4x", param_id);
+		packet_hexdump(data + 2, param_len - 2);
+
+		data += param_len + 1;
+		size -= param_len + 1;
+	}
+}
+
+static void ddc_config_write_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+	uint16_t param_id = get_le16(data + 1);
+
+	print_status(status);
+	print_field("Identifier: 0x%4.4x", param_id);
+}
+
+static void memory_write_cmd(const void *data, uint8_t size)
+{
+	uint32_t addr = get_le32(data);
+	uint8_t mode = get_u8(data + 4);
+	uint8_t length = get_u8(data + 5);
+	const char *str;
+
+	print_field("Address: 0x%8.8x", addr);
+
+	switch (mode) {
+	case 0x00:
+		str = "Byte access";
+		break;
+	case 0x01:
+		str = "Half word access";
+		break;
+	case 0x02:
+		str = "Word access";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode: %s (0x%2.2x)", str, mode);
+	print_field("Length: %u", length);
+
+	packet_hexdump(data + 6, size - 6);
+}
+
+static const struct vendor_ocf vendor_ocf_table[] = {
+	{ 0x001, "Reset",
+			reset_cmd, 8, true,
+			status_rsp, 1, true },
+	{ 0x002, "No Operation" },
+	{ 0x005, "Read Version",
+			null_cmd, 0, true,
+			read_version_rsp, 10, true },
+	{ 0x006, "Set UART Baudrate" },
+	{ 0x007, "Enable LPM" },
+	{ 0x008, "PCM Write Configuration" },
+	{ 0x009, "Secure Send",
+			secure_send_cmd, 1, false,
+			status_rsp, 1, true },
+	{ 0x00d, "Read Secure Boot Params",
+			null_cmd, 0, true },
+	{ 0x00e, "Write Secure Boot Params" },
+	{ 0x00f, "Unlock" },
+	{ 0x010, "Change UART Baudrate" },
+	{ 0x011, "Manufacturer Mode",
+			manufacturer_mode_cmd, 2, true,
+			status_rsp, 1, true },
+	{ 0x012, "Read Link RSSI" },
+	{ 0x022, "Get Exception Info" },
+	{ 0x024, "Clear Exception Info" },
+	{ 0x02f, "Write BD Data",
+			write_bd_data_cmd, 6, false },
+	{ 0x030, "Read BD Data",
+			null_cmd, 0, true,
+			read_bd_data_rsp, 7, false },
+	{ 0x031, "Write BD Address",
+			write_bd_address_cmd, 6, true,
+			status_rsp, 1, true },
+	{ 0x032, "Flow Specification" },
+	{ 0x034, "Read Secure ID" },
+	{ 0x038, "Set Synchronous USB Interface Type" },
+	{ 0x039, "Config Synchronous Interface" },
+	{ 0x03f, "SW RF Kill",
+			null_cmd, 0, true,
+			status_rsp, 1, true },
+	{ 0x043, "Activate Deactivate Traces",
+			act_deact_traces_cmd, 3, true },
+	{ 0x04d, "Stimulate Exception",
+			stimulate_exception_cmd, 1, true,
+			status_rsp, 1, true },
+	{ 0x050, "Read HW Version" },
+	{ 0x052, "Set Event Mask",
+			set_event_mask_cmd, 8, true,
+			status_rsp, 1, true },
+	{ 0x053, "Config_Link_Controller" },
+	{ 0x089, "DDC Write" },
+	{ 0x08a, "DDC Read" },
+	{ 0x08b, "DDC Config Write",
+			ddc_config_write_cmd, 3, false,
+			ddc_config_write_rsp, 3, true },
+	{ 0x08c, "DDC Config Read" },
+	{ 0x08d, "Memory Read" },
+	{ 0x08e, "Memory Write",
+			memory_write_cmd, 6, false,
+			status_rsp, 1, true },
+	{ }
+};
+
+const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf)
+{
+	int i;
+
+	for (i = 0; vendor_ocf_table[i].str; i++) {
+		if (vendor_ocf_table[i].ocf == ocf)
+			return &vendor_ocf_table[i];
+	}
+
+	return NULL;
+}
+
+static void startup_evt(const void *data, uint8_t size)
+{
+}
+
+static void fatal_exception_evt(const void *data, uint8_t size)
+{
+	uint16_t line = get_le16(data);
+	uint8_t module = get_u8(data + 2);
+	uint8_t reason = get_u8(data + 3);
+
+	print_field("Line: %u", line);
+	print_module(module);
+	print_field("Reason: 0x%2.2x", reason);
+}
+
+static void bootup_evt(const void *data, uint8_t size)
+{
+	uint8_t zero = get_u8(data);
+	uint8_t num_packets = get_u8(data + 1);
+	uint8_t source = get_u8(data + 2);
+	uint8_t reset_type = get_u8(data + 3);
+	uint8_t reset_reason = get_u8(data + 4);
+	uint8_t ddc_status = get_u8(data + 5);
+	const char *str;
+
+	print_field("Zero: 0x%2.2x", zero);
+	print_field("Number of packets: %d", num_packets);
+
+	switch (source) {
+	case 0x00:
+		str = "Bootloader";
+		break;
+	case 0x01:
+		str = "Operational firmware";
+		break;
+	case 0x02:
+		str = "Self test firmware";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Source: %s (0x%2.2x)", str, source);
+
+	switch (reset_type) {
+	case 0x00:
+		str = "Hardware reset";
+		break;
+	case 0x01:
+		str = "Soft watchdog reset";
+		break;
+	case 0x02:
+		str = "Soft software reset";
+		break;
+	case 0x03:
+		str = "Hard watchdog reset";
+		break;
+	case 0x04:
+		str = "Hard software reset";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reset type: %s (0x%2.2x)", str, reset_type);
+
+	switch (reset_reason) {
+	case 0x00:
+		str = "Power on";
+		break;
+	case 0x01:
+		str = "Reset command";
+		break;
+	case 0x02:
+		str = "Intel reset command";
+		break;
+	case 0x03:
+		str = "Watchdog";
+		break;
+	case 0x04:
+		str = "Fatal exception";
+		break;
+	case 0x05:
+		str = "System exception";
+		break;
+	case 0xff:
+		str = "Unknown";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reset reason: %s (0x%2.2x)", str, reset_reason);
+
+	switch (ddc_status) {
+	case 0x00:
+		str = "Firmware default";
+		break;
+	case 0x01:
+		str = "Firmware default plus OTP";
+		break;
+	case 0x02:
+		str = "Persistent RAM";
+		break;
+	case 0x03:
+		str = "Not used";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("DDC status: %s (0x%2.2x)", str, ddc_status);
+}
+
+static void default_bd_data_evt(const void *data, uint8_t size)
+{
+	uint8_t mem_status = get_u8(data);
+	const char *str;
+
+	switch (mem_status) {
+	case 0x02:
+		str = "Invalid manufacturing data";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Memory status: %s (0x%2.2x)", str, mem_status);
+}
+
+static void secure_send_commands_result_evt(const void *data, uint8_t size)
+{
+	uint8_t result = get_u8(data);
+	uint16_t opcode = get_le16(data + 1);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+	uint8_t status = get_u8(data + 3);
+	const char *str;
+
+	switch (result) {
+	case 0x00:
+		str = "Success";
+		break;
+	case 0x01:
+		str = "General failure";
+		break;
+	case 0x02:
+		str = "Hardware failure";
+		break;
+	case 0x03:
+		str = "Signature verification failed";
+		break;
+	case 0x04:
+		str = "Parsing error of command buffer";
+		break;
+	case 0x05:
+		str = "Command execution failure";
+		break;
+	case 0x06:
+		str = "Command parameters error";
+		break;
+	case 0x07:
+		str = "Command missing";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%2.2x)", str, result);
+	print_field("Opcode: 0x%4.4x (0x%2.2x|0x%4.4x)", opcode, ogf, ocf);
+	print_status(status);
+}
+
+static void debug_exception_evt(const void *data, uint8_t size)
+{
+	uint16_t line = get_le16(data);
+	uint8_t module = get_u8(data + 2);
+	uint8_t reason = get_u8(data + 3);
+
+	print_field("Line: %u", line);
+	print_module(module);
+	print_field("Reason: 0x%2.2x", reason);
+}
+
+static void le_link_established_evt(const void *data, uint8_t size)
+{
+	uint16_t handle = get_le16(data);
+	uint32_t access_addr = get_le32(data + 10);
+
+	print_field("Handle: %u", handle);
+
+	packet_hexdump(data + 2, 8);
+
+	print_field("Access address: 0x%8.8x", access_addr);
+
+	packet_hexdump(data + 14, size - 14);
+}
+
+static void scan_status_evt(const void *data, uint8_t size)
+{
+	uint8_t enable = get_u8(data);
+
+	print_field("Inquiry scan: %s",
+				(enable & 0x01) ? "Enabled" : "Disabled");
+	print_field("Page scan: %s",
+				(enable & 0x02) ? "Enabled" : "Disabled");
+
+	if (enable & 0xfc)
+		print_text(COLOR_UNKNOWN_SCAN_STATUS,
+				"  Unknown status (0x%2.2x)", enable & 0xfc);
+
+}
+
+static void act_deact_traces_complete_evt(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+
+	print_status(status);
+}
+
+static void lmp_pdu_trace_evt(const void *data, uint8_t size)
+{
+	uint8_t type, len, id;
+	uint16_t handle, count;
+	uint32_t clock;
+	const char *str;
+
+	type = get_u8(data);
+	handle = get_le16(data + 1);
+
+	switch (type) {
+	case 0x00:
+		str = "RX LMP";
+		break;
+	case 0x01:
+		str = "TX LMP";
+		break;
+	case 0x02:
+		str = "ACK LMP";
+		break;
+	case 0x03:
+		str = "RX LL";
+		break;
+	case 0x04:
+		str = "TX LL";
+		break;
+	case 0x05:
+		str = "ACK LL";
+		break;
+	default:
+		str = "Unknown";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, type);
+	print_field("Handle: %u", handle);
+
+	switch (type) {
+	case 0x00:
+		len = size - 8;
+		clock = get_le32(data + 4 + len);
+
+		packet_hexdump(data + 3, 1);
+		lmp_packet(data + 4, len, false);
+		print_field("Clock: 0x%8.8x", clock);
+		break;
+	case 0x01:
+		len = size - 9;
+		clock = get_le32(data + 4 + len);
+		id = get_u8(data + 4 + len + 4);
+
+		packet_hexdump(data + 3, 1);
+		lmp_packet(data + 4, len, false);
+		print_field("Clock: 0x%8.8x", clock);
+		print_field("ID: 0x%2.2x", id);
+		break;
+	case 0x02:
+		clock = get_le32(data + 3);
+		id = get_u8(data + 3 + 4);
+
+		print_field("Clock: 0x%8.8x", clock);
+		print_field("ID: 0x%2.2x", id);
+		break;
+	case 0x03:
+		len = size - 8;
+		count = get_le16(data + 3);
+
+		print_field("Count: 0x%4.4x", count);
+		packet_hexdump(data + 3 + 2 + 1, 2);
+		llcp_packet(data + 8, len, false);
+		break;
+	case 0x04:
+		len = size - 8;
+		count = get_le16(data + 3);
+		id = get_u8(data + 3 + 2);
+
+		print_field("Count: 0x%4.4x", count);
+		print_field("ID: 0x%2.2x", id);
+		packet_hexdump(data + 3 + 2 + 1, 2);
+		llcp_packet(data + 8, len, false);
+		break;
+	case 0x05:
+		count = get_le16(data + 3);
+		id = get_u8(data + 3 + 2);
+
+		print_field("Count: 0x%4.4x", count);
+		print_field("ID: 0x%2.2x", id);
+		break;
+	default:
+		packet_hexdump(data + 3, size - 3);
+		break;
+	}
+}
+
+static void write_bd_data_complete_evt(const void *data, uint8_t size)
+{
+	uint8_t status = get_u8(data);
+
+	print_status(status);
+}
+
+static void sco_rejected_via_lmp_evt(const void *data, uint8_t size)
+{
+	uint8_t reason = get_u8(data + 6);
+
+	packet_print_addr("Address", data, false);
+	packet_print_error("Reason", reason);
+}
+
+static void ptt_switch_notification_evt(const void *data, uint8_t size)
+{
+	uint16_t handle = get_le16(data);
+	uint8_t table = get_u8(data + 2);
+	const char *str;
+
+	print_field("Handle: %u", handle);
+
+	switch (table) {
+	case 0x00:
+		str = "Basic rate";
+		break;
+	case 0x01:
+		str = "Enhanced data rate";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Packet type table: %s (0x%2.2x)", str, table);
+}
+
+static void system_exception_evt(const void *data, uint8_t size)
+{
+	uint8_t type = get_u8(data);
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "No Exception";
+		break;
+	case 0x01:
+		str = "Undefined Instruction";
+		break;
+	case 0x02:
+		str = "Prefetch abort";
+		break;
+	case 0x03:
+		str = "Data abort";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, type);
+
+	packet_hexdump(data + 1, size - 1);
+}
+
+static const struct vendor_evt vendor_evt_table[] = {
+	{ 0x00, "Startup",
+			startup_evt, 0, true },
+	{ 0x01, "Fatal Exception",
+			fatal_exception_evt, 4, true },
+	{ 0x02, "Bootup",
+			bootup_evt, 6, true },
+	{ 0x05, "Default BD Data",
+			default_bd_data_evt, 1, true },
+	{ 0x06, "Secure Send Commands Result",
+			secure_send_commands_result_evt, 4, true },
+	{ 0x08, "Debug Exception",
+			debug_exception_evt, 4, true },
+	{ 0x0f, "LE Link Established",
+			le_link_established_evt, 26, true },
+	{ 0x11, "Scan Status",
+			scan_status_evt, 1, true },
+	{ 0x16, "Activate Deactivate Traces Complete",
+			act_deact_traces_complete_evt, 1, true },
+	{ 0x17, "LMP PDU Trace",
+			lmp_pdu_trace_evt, 3, false },
+	{ 0x19, "Write BD Data Complete",
+			write_bd_data_complete_evt, 1, true },
+	{ 0x25, "SCO Rejected via LMP",
+			sco_rejected_via_lmp_evt, 7, true },
+	{ 0x26, "PTT Switch Notification",
+			ptt_switch_notification_evt, 3, true },
+	{ 0x29, "System Exception",
+			system_exception_evt, 133, true },
+	{ 0x2c, "FW Trace String" },
+	{ 0x2e, "FW Trace Binary" },
+	{ }
+};
+
+const struct vendor_evt *intel_vendor_evt(uint8_t evt)
+{
+	int i;
+
+	for (i = 0; vendor_evt_table[i].str; i++) {
+		if (vendor_evt_table[i].evt == evt)
+			return &vendor_evt_table[i];
+	}
+
+	return NULL;
+}
diff --git a/bluez/repo/monitor/intel.h b/bluez/repo/monitor/intel.h
new file mode 100644
index 0000000..573b23f
--- /dev/null
+++ b/bluez/repo/monitor/intel.h
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+struct vendor_ocf;
+struct vendor_evt;
+
+const struct vendor_ocf *intel_vendor_ocf(uint16_t ocf);
+const struct vendor_evt *intel_vendor_evt(uint8_t evt);
diff --git a/bluez/repo/monitor/keys.c b/bluez/repo/monitor/keys.c
new file mode 100644
index 0000000..e60aa93
--- /dev/null
+++ b/bluez/repo/monitor/keys.c
@@ -0,0 +1,127 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/crypto.h"
+
+#include "keys.h"
+
+static const uint8_t empty_key[16] = { 0x00, };
+static const uint8_t empty_addr[6] = { 0x00, };
+
+static struct bt_crypto *crypto;
+
+struct irk_data {
+	uint8_t key[16];
+	uint8_t addr[6];
+	uint8_t addr_type;
+};
+
+static struct queue *irk_list;
+
+void keys_setup(void)
+{
+	crypto = bt_crypto_new();
+
+	irk_list = queue_new();
+}
+
+void keys_cleanup(void)
+{
+	bt_crypto_unref(crypto);
+
+	queue_destroy(irk_list, free);
+}
+
+void keys_update_identity_key(const uint8_t key[16])
+{
+	struct irk_data *irk;
+
+	irk = queue_peek_tail(irk_list);
+	if (irk && !memcmp(irk->key, empty_key, 16)) {
+		memcpy(irk->key, key, 16);
+		return;
+	}
+
+	irk = new0(struct irk_data, 1);
+	if (irk) {
+		memcpy(irk->key, key, 16);
+		if (!queue_push_tail(irk_list, irk))
+			free(irk);
+	}
+}
+
+void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type)
+{
+	struct irk_data *irk;
+
+	irk = queue_peek_tail(irk_list);
+	if (irk && !memcmp(irk->addr, empty_addr, 6)) {
+		memcpy(irk->addr, addr, 6);
+		irk->addr_type = addr_type;
+		return;
+	}
+
+	irk = new0(struct irk_data, 1);
+	if (irk) {
+		memcpy(irk->addr, addr, 6);
+		irk->addr_type = addr_type;
+		if (!queue_push_tail(irk_list, irk))
+			free(irk);
+	}
+}
+
+static bool match_resolve_irk(const void *data, const void *match_data)
+{
+	const struct irk_data *irk = data;
+	const uint8_t *addr = match_data;
+	uint8_t local_hash[3];
+
+	bt_crypto_ah(crypto, irk->key, addr + 3, local_hash);
+
+	return !memcmp(addr, local_hash, 3);
+}
+
+bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6],
+							uint8_t *ident_type)
+{
+	struct irk_data *irk;
+
+	irk = queue_find(irk_list, match_resolve_irk, addr);
+
+	if (irk) {
+		memcpy(ident, irk->addr, 6);
+		*ident_type = irk->addr_type;
+		return true;
+	}
+
+	return false;
+}
diff --git a/bluez/repo/monitor/keys.h b/bluez/repo/monitor/keys.h
new file mode 100644
index 0000000..61ec50a
--- /dev/null
+++ b/bluez/repo/monitor/keys.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+
+void keys_setup(void);
+void keys_cleanup(void);
+
+void keys_update_identity_key(const uint8_t key[16]);
+void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type);
+
+bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6],
+							uint8_t *ident_type);
diff --git a/bluez/repo/monitor/l2cap.c b/bluez/repo/monitor/l2cap.c
new file mode 100644
index 0000000..93a1b20
--- /dev/null
+++ b/bluez/repo/monitor/l2cap.c
@@ -0,0 +1,3239 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <inttypes.h>
+
+#include "lib/bluetooth.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"
+#include "avdtp.h"
+#include "rfcomm.h"
+#include "bnep.h"
+
+/* L2CAP Control Field bit masks */
+#define L2CAP_CTRL_SAR_MASK		0xC000
+#define L2CAP_CTRL_REQSEQ_MASK		0x3F00
+#define L2CAP_CTRL_TXSEQ_MASK		0x007E
+#define L2CAP_CTRL_SUPERVISE_MASK	0x000C
+
+#define L2CAP_CTRL_RETRANS		0x0080
+#define L2CAP_CTRL_FINAL		0x0080
+#define L2CAP_CTRL_POLL			0x0010
+#define L2CAP_CTRL_FRAME_TYPE		0x0001 /* I- or S-Frame */
+
+#define L2CAP_CTRL_TXSEQ_SHIFT		1
+#define L2CAP_CTRL_SUPER_SHIFT		2
+#define L2CAP_CTRL_REQSEQ_SHIFT		8
+#define L2CAP_CTRL_SAR_SHIFT		14
+
+#define L2CAP_EXT_CTRL_TXSEQ_MASK	0xFFFC0000
+#define L2CAP_EXT_CTRL_SAR_MASK		0x00030000
+#define L2CAP_EXT_CTRL_SUPERVISE_MASK	0x00030000
+#define L2CAP_EXT_CTRL_REQSEQ_MASK	0x0000FFFC
+
+#define L2CAP_EXT_CTRL_POLL		0x00040000
+#define L2CAP_EXT_CTRL_FINAL		0x00000002
+#define L2CAP_EXT_CTRL_FRAME_TYPE	0x00000001 /* I- or S-Frame */
+
+#define L2CAP_EXT_CTRL_REQSEQ_SHIFT	2
+#define L2CAP_EXT_CTRL_SAR_SHIFT	16
+#define L2CAP_EXT_CTRL_SUPER_SHIFT	16
+#define L2CAP_EXT_CTRL_TXSEQ_SHIFT	18
+
+/* L2CAP Supervisory Function */
+#define L2CAP_SUPER_RR		0x00
+#define L2CAP_SUPER_REJ		0x01
+#define L2CAP_SUPER_RNR		0x02
+#define L2CAP_SUPER_SREJ	0x03
+
+/* L2CAP Segmentation and Reassembly */
+#define L2CAP_SAR_UNSEGMENTED	0x00
+#define L2CAP_SAR_START		0x01
+#define L2CAP_SAR_END		0x02
+#define L2CAP_SAR_CONTINUE	0x03
+
+#define MAX_CHAN 64
+
+struct chan_data {
+	uint16_t index;
+	uint16_t handle;
+	uint8_t ident;
+	uint16_t scid;
+	uint16_t dcid;
+	uint16_t psm;
+	uint8_t  ctrlid;
+	uint8_t  mode;
+	uint8_t  ext_ctrl;
+	uint8_t  seq_num;
+};
+
+static struct chan_data chan_list[MAX_CHAN];
+
+static void assign_scid(const struct l2cap_frame *frame,
+				uint16_t scid, uint16_t psm, uint8_t ctrlid)
+{
+	int i, n = -1;
+	uint8_t seq_num = 1;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (n < 0 && chan_list[i].handle == 0x0000) {
+			n = i;
+			continue;
+		}
+
+		if (chan_list[i].index != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (chan_list[i].psm == psm)
+			seq_num++;
+
+		/* Don't break on match - we still need to go through all
+		 * channels to find proper seq_num.
+		 */
+		if (frame->in) {
+			if (chan_list[i].dcid == scid)
+				n = i;
+		} else {
+			if (chan_list[i].scid == scid)
+				n = i;
+		}
+	}
+
+	if (n < 0)
+		return;
+
+	memset(&chan_list[n], 0, sizeof(chan_list[n]));
+	chan_list[n].index = frame->index;
+	chan_list[n].handle = frame->handle;
+	chan_list[n].ident = frame->ident;
+
+	if (frame->in)
+		chan_list[n].dcid = scid;
+	else
+		chan_list[n].scid = scid;
+
+	chan_list[n].psm = psm;
+	chan_list[n].ctrlid = ctrlid;
+	chan_list[n].mode = 0;
+
+	chan_list[n].seq_num = seq_num;
+}
+
+static void release_scid(const struct l2cap_frame *frame, uint16_t scid)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (chan_list[i].index != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == scid) {
+				chan_list[i].handle = 0;
+				break;
+			}
+		} else {
+			if (chan_list[i].dcid == scid) {
+				chan_list[i].handle = 0;
+				break;
+			}
+		}
+	}
+}
+
+static void assign_dcid(const struct l2cap_frame *frame, uint16_t dcid,
+								uint16_t scid)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (chan_list[i].index != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (frame->ident != 0 && chan_list[i].ident != frame->ident)
+			continue;
+
+		if (frame->in) {
+			if (scid) {
+				if (chan_list[i].scid == scid) {
+					chan_list[i].dcid = dcid;
+					break;
+				}
+			} else {
+				if (chan_list[i].scid && !chan_list[i].dcid) {
+					chan_list[i].dcid = dcid;
+					break;
+				}
+			}
+		} else {
+			if (scid) {
+				if (chan_list[i].dcid == scid) {
+					chan_list[i].scid = dcid;
+					break;
+				}
+			} else {
+				if (chan_list[i].dcid && !chan_list[i].scid) {
+					chan_list[i].scid = dcid;
+					break;
+				}
+			}
+		}
+	}
+}
+
+static void assign_mode(const struct l2cap_frame *frame,
+					uint8_t mode, uint16_t dcid)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (chan_list[i].index != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == dcid) {
+				chan_list[i].mode = mode;
+				break;
+			}
+		} else {
+			if (chan_list[i].dcid == dcid) {
+				chan_list[i].mode = mode;
+				break;
+			}
+		}
+	}
+}
+
+static int get_chan_data_index(const struct l2cap_frame *frame)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (chan_list[i].index != frame->index &&
+					chan_list[i].ctrlid == 0)
+			continue;
+
+		if (chan_list[i].ctrlid != 0 &&
+					chan_list[i].ctrlid != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == frame->cid)
+				return i;
+		} else {
+			if (chan_list[i].dcid == frame->cid)
+				return i;
+		}
+	}
+
+	return -1;
+}
+
+static uint16_t get_psm(const struct l2cap_frame *frame)
+{
+	int i = get_chan_data_index(frame);
+
+	if (i < 0)
+		return 0;
+
+	return chan_list[i].psm;
+}
+
+static uint8_t get_mode(const struct l2cap_frame *frame)
+{
+	int i = get_chan_data_index(frame);
+
+	if (i < 0)
+		return 0;
+
+	return chan_list[i].mode;
+}
+
+static uint16_t get_chan(const struct l2cap_frame *frame)
+{
+	int i = get_chan_data_index(frame);
+
+	if (i < 0)
+		return 0;
+
+	return i;
+}
+
+static uint8_t get_seq_num(const struct l2cap_frame *frame)
+{
+	int i = get_chan_data_index(frame);
+
+	if (i < 0)
+		return 0;
+
+	return chan_list[i].seq_num;
+}
+
+static void assign_ext_ctrl(const struct l2cap_frame *frame,
+					uint8_t ext_ctrl, uint16_t dcid)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHAN; i++) {
+		if (chan_list[i].index != frame->index)
+			continue;
+
+		if (chan_list[i].handle != frame->handle)
+			continue;
+
+		if (frame->in) {
+			if (chan_list[i].scid == dcid) {
+				chan_list[i].ext_ctrl = ext_ctrl;
+				break;
+			}
+		} else {
+			if (chan_list[i].dcid == dcid) {
+				chan_list[i].ext_ctrl = ext_ctrl;
+				break;
+			}
+		}
+	}
+}
+
+static uint8_t get_ext_ctrl(const struct l2cap_frame *frame)
+{
+	int i = get_chan_data_index(frame);
+
+	if (i < 0)
+		return 0;
+
+	return chan_list[i].ext_ctrl;
+}
+
+static char *sar2str(uint8_t sar)
+{
+	switch (sar) {
+	case L2CAP_SAR_UNSEGMENTED:
+		return "Unsegmented";
+	case L2CAP_SAR_START:
+		return "Start";
+	case L2CAP_SAR_END:
+		return "End";
+	case L2CAP_SAR_CONTINUE:
+		return "Continuation";
+	default:
+		return "Bad SAR";
+	}
+}
+
+static char *supervisory2str(uint8_t supervisory)
+{
+	switch (supervisory) {
+	case L2CAP_SUPER_RR:
+		return "Receiver Ready (RR)";
+	case L2CAP_SUPER_REJ:
+		return "Reject (REJ)";
+	case L2CAP_SUPER_RNR:
+		return "Receiver Not Ready (RNR)";
+	case L2CAP_SUPER_SREJ:
+		return "Select Reject (SREJ)";
+	default:
+		return "Bad Supervisory";
+	}
+}
+
+static void l2cap_ctrl_ext_parse(struct l2cap_frame *frame, uint32_t ctrl)
+{
+	printf("      %s:",
+		ctrl & L2CAP_EXT_CTRL_FRAME_TYPE ? "S-frame" : "I-frame");
+
+	if (ctrl & L2CAP_EXT_CTRL_FRAME_TYPE) {
+		printf(" %s",
+		supervisory2str((ctrl & L2CAP_EXT_CTRL_SUPERVISE_MASK) >>
+						L2CAP_EXT_CTRL_SUPER_SHIFT));
+
+		if (ctrl & L2CAP_EXT_CTRL_POLL)
+			printf(" P-bit");
+	} else {
+		uint8_t sar = (ctrl & L2CAP_EXT_CTRL_SAR_MASK) >>
+						L2CAP_EXT_CTRL_SAR_SHIFT;
+		printf(" %s", sar2str(sar));
+		if (sar == L2CAP_SAR_START) {
+			uint16_t len;
+
+			if (!l2cap_frame_get_le16(frame, &len))
+				return;
+
+			printf(" (len %d)", len);
+		}
+		printf(" TxSeq %d", (ctrl & L2CAP_EXT_CTRL_TXSEQ_MASK) >>
+						L2CAP_EXT_CTRL_TXSEQ_SHIFT);
+	}
+
+	printf(" ReqSeq %d", (ctrl & L2CAP_EXT_CTRL_REQSEQ_MASK) >>
+						L2CAP_EXT_CTRL_REQSEQ_SHIFT);
+
+	if (ctrl & L2CAP_EXT_CTRL_FINAL)
+		printf(" F-bit");
+}
+
+static void l2cap_ctrl_parse(struct l2cap_frame *frame, uint32_t ctrl)
+{
+	printf("      %s:",
+			ctrl & L2CAP_CTRL_FRAME_TYPE ? "S-frame" : "I-frame");
+
+	if (ctrl & 0x01) {
+		printf(" %s",
+			supervisory2str((ctrl & L2CAP_CTRL_SUPERVISE_MASK) >>
+						L2CAP_CTRL_SUPER_SHIFT));
+
+		if (ctrl & L2CAP_CTRL_POLL)
+			printf(" P-bit");
+	} else {
+		uint8_t sar;
+
+		sar = (ctrl & L2CAP_CTRL_SAR_MASK) >> L2CAP_CTRL_SAR_SHIFT;
+		printf(" %s", sar2str(sar));
+		if (sar == L2CAP_SAR_START) {
+			uint16_t len;
+
+			if (!l2cap_frame_get_le16(frame, &len))
+				return;
+
+			printf(" (len %d)", len);
+		}
+		printf(" TxSeq %d", (ctrl & L2CAP_CTRL_TXSEQ_MASK) >>
+						L2CAP_CTRL_TXSEQ_SHIFT);
+	}
+
+	printf(" ReqSeq %d", (ctrl & L2CAP_CTRL_REQSEQ_MASK) >>
+						L2CAP_CTRL_REQSEQ_SHIFT);
+
+	if (ctrl & L2CAP_CTRL_FINAL)
+		printf(" F-bit");
+}
+
+#define MAX_INDEX 16
+
+struct index_data {
+	void *frag_buf;
+	uint16_t frag_pos;
+	uint16_t frag_len;
+	uint16_t frag_cid;
+};
+
+static struct index_data index_list[MAX_INDEX][2];
+
+static void clear_fragment_buffer(uint16_t index, bool in)
+{
+	free(index_list[index][in].frag_buf);
+	index_list[index][in].frag_buf = NULL;
+	index_list[index][in].frag_pos = 0;
+	index_list[index][in].frag_len = 0;
+}
+
+static void print_psm(uint16_t psm)
+{
+	print_field("PSM: %d (0x%4.4x)", le16_to_cpu(psm), le16_to_cpu(psm));
+}
+
+static void print_cid(const char *type, uint16_t cid)
+{
+	print_field("%s CID: %d", type, le16_to_cpu(cid));
+}
+
+static void print_reject_reason(uint16_t reason)
+{
+	const char *str;
+
+	switch (le16_to_cpu(reason)) {
+	case 0x0000:
+		str = "Command not understood";
+		break;
+	case 0x0001:
+		str = "Signaling MTU exceeded";
+		break;
+	case 0x0002:
+		str = "Invalid CID in request";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reason: %s (0x%4.4x)", str, le16_to_cpu(reason));
+}
+
+static void print_conn_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Connection successful";
+		break;
+	case 0x0001:
+		str = "Connection pending";
+		break;
+	case 0x0002:
+		str = "Connection refused - PSM not supported";
+		break;
+	case 0x0003:
+		str = "Connection refused - security block";
+		break;
+	case 0x0004:
+		str = "Connection refused - no resources available";
+		break;
+	case 0x0006:
+		str = "Connection refused - Invalid Source CID";
+		break;
+	case 0x0007:
+		str = "Connection refused - Source CID already allocated";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void print_le_conn_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Connection successful";
+		break;
+	case 0x0002:
+		str = "Connection refused - PSM not supported";
+		break;
+	case 0x0004:
+		str = "Connection refused - no resources available";
+		break;
+	case 0x0005:
+		str = "Connection refused - insufficient authentication";
+		break;
+	case 0x0006:
+		str = "Connection refused - insufficient authorization";
+		break;
+	case 0x0007:
+		str = "Connection refused - insufficient encryption key size";
+		break;
+	case 0x0008:
+		str = "Connection refused - insufficient encryption";
+		break;
+	case 0x0009:
+		str = "Connection refused - Invalid Source CID";
+		break;
+	case 0x0010:
+		str = "Connection refused - Source CID already allocated";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void print_create_chan_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Connection successful";
+		break;
+	case 0x0001:
+		str = "Connection pending";
+		break;
+	case 0x0002:
+		str = "Connection refused - PSM not supported";
+		break;
+	case 0x0003:
+		str = "Connection refused - security block";
+		break;
+	case 0x0004:
+		str = "Connection refused - no resources available";
+		break;
+	case 0x0005:
+		str = "Connection refused - Controller ID not supported";
+		break;
+	case 0x0006:
+		str = "Connection refused - Invalid Source CID";
+		break;
+	case 0x0007:
+		str = "Connection refused - Source CID already allocated";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void print_conn_status(uint16_t status)
+{
+        const char *str;
+
+	switch (le16_to_cpu(status)) {
+	case 0x0000:
+		str = "No further information available";
+		break;
+	case 0x0001:
+		str = "Authentication pending";
+		break;
+	case 0x0002:
+		str = "Authorization pending";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Status: %s (0x%4.4x)", str, le16_to_cpu(status));
+}
+
+static void print_config_flags(uint16_t flags)
+{
+	const char *str;
+
+	if (le16_to_cpu(flags) & 0x0001)
+		str = " (continuation)";
+	else
+		str = "";
+
+	print_field("Flags: 0x%4.4x%s", le16_to_cpu(flags), str);
+}
+
+static void print_config_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Success";
+		break;
+	case 0x0001:
+		str = "Failure - unacceptable parameters";
+		break;
+	case 0x0002:
+		str = "Failure - rejected";
+		break;
+	case 0x0003:
+		str = "Failure - unknown options";
+		break;
+	case 0x0004:
+		str = "Pending";
+		break;
+	case 0x0005:
+		str = "Failure - flow spec rejected";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static struct {
+	uint8_t type;
+	uint8_t len;
+	const char *str;
+} options_table[] = {
+	{ 0x01,  2, "Maximum Transmission Unit"		},
+	{ 0x02,  2, "Flush Timeout"			},
+	{ 0x03, 22, "Quality of Service"		},
+	{ 0x04,  9, "Retransmission and Flow Control"	},
+	{ 0x05,  1, "Frame Check Sequence"		},
+	{ 0x06, 16, "Extended Flow Specification"	},
+	{ 0x07,  2, "Extended Window Size"		},
+        { }
+};
+
+static void print_config_options(const struct l2cap_frame *frame,
+				uint8_t offset, uint16_t cid, bool response)
+{
+	const uint8_t *data = frame->data + offset;
+	uint16_t size = frame->size - offset;
+	uint16_t consumed = 0;
+
+	while (consumed < size - 2) {
+		const char *str = "Unknown";
+		uint8_t type = data[consumed] & 0x7f;
+		uint8_t hint = data[consumed] & 0x80;
+		uint8_t len = data[consumed + 1];
+		uint8_t expect_len = 0;
+		int i;
+
+		for (i = 0; options_table[i].str; i++) {
+			if (options_table[i].type == type) {
+				str = options_table[i].str;
+				expect_len = options_table[i].len;
+				break;
+			}
+		}
+
+		print_field("Option: %s (0x%2.2x) [%s]", str, type,
+						hint ? "hint" : "mandatory");
+
+		if (expect_len == 0) {
+			consumed += 2;
+			break;
+		}
+
+		if (len != expect_len) {
+			print_text(COLOR_ERROR, "wrong option size (%d != %d)",
+							len, expect_len);
+			consumed += 2;
+			break;
+		}
+
+		switch (type) {
+		case 0x01:
+			print_field("  MTU: %d",
+					get_le16(data + consumed + 2));
+			break;
+		case 0x02:
+			print_field("  Flush timeout: %d",
+					get_le16(data + consumed + 2));
+			break;
+		case 0x03:
+			switch (data[consumed + 3]) {
+			case 0x00:
+				str = "No Traffic";
+				break;
+			case 0x01:
+				str = "Best Effort";
+				break;
+			case 0x02:
+				str = "Guaranteed";
+				break;
+			default:
+				str = "Reserved";
+				break;
+			}
+			print_field("  Flags: 0x%2.2x", data[consumed + 2]);
+			print_field("  Service type: %s (0x%2.2x)",
+						str, data[consumed + 3]);
+			print_field("  Token rate: 0x%8.8x",
+					get_le32(data + consumed + 4));
+			print_field("  Token bucket size: 0x%8.8x",
+					get_le32(data + consumed + 8));
+			print_field("  Peak bandwidth: 0x%8.8x",
+					get_le32(data + consumed + 12));
+			print_field("  Latency: 0x%8.8x",
+					get_le32(data + consumed + 16));
+			print_field("  Delay variation: 0x%8.8x",
+					get_le32(data + consumed + 20));
+                        break;
+		case 0x04:
+			if (response)
+				assign_mode(frame, data[consumed + 2], cid);
+
+			switch (data[consumed + 2]) {
+			case 0x00:
+				str = "Basic";
+				break;
+			case 0x01:
+				str = "Retransmission";
+				break;
+			case 0x02:
+				str = "Flow control";
+				break;
+			case 0x03:
+				str = "Enhanced retransmission";
+				break;
+			case 0x04:
+				str = "Streaming";
+				break;
+			default:
+				str = "Reserved";
+				break;
+			}
+			print_field("  Mode: %s (0x%2.2x)",
+						str, data[consumed + 2]);
+			print_field("  TX window size: %d", data[consumed + 3]);
+			print_field("  Max transmit: %d", data[consumed + 4]);
+			print_field("  Retransmission timeout: %d",
+					get_le16(data + consumed + 5));
+			print_field("  Monitor timeout: %d",
+					get_le16(data + consumed + 7));
+			print_field("  Maximum PDU size: %d",
+					get_le16(data + consumed + 9));
+			break;
+		case 0x05:
+			switch (data[consumed + 2]) {
+			case 0x00:
+				str = "No FCS";
+				break;
+			case 0x01:
+				str = "16-bit FCS";
+				break;
+			default:
+				str = "Reserved";
+				break;
+			}
+			print_field("  FCS: %s (0x%2.2d)",
+						str, data[consumed + 2]);
+			break;
+		case 0x06:
+			switch (data[consumed + 3]) {
+			case 0x00:
+				str = "No traffic";
+				break;
+			case 0x01:
+				str = "Best effort";
+				break;
+			case 0x02:
+				str = "Guaranteed";
+				break;
+			default:
+				str = "Reserved";
+				break;
+			}
+			print_field("  Identifier: 0x%2.2x",
+						data[consumed + 2]);
+			print_field("  Service type: %s (0x%2.2x)",
+						str, data[consumed + 3]);
+			print_field("  Maximum SDU size: 0x%4.4x",
+					get_le16(data + consumed + 4));
+			print_field("  SDU inter-arrival time: 0x%8.8x",
+					get_le32(data + consumed + 6));
+			print_field("  Access latency: 0x%8.8x",
+					get_le32(data + consumed + 10));
+			print_field("  Flush timeout: 0x%8.8x",
+					get_le32(data + consumed + 14));
+			break;
+		case 0x07:
+			print_field("  Extended window size: %d",
+					get_le16(data + consumed + 2));
+			assign_ext_ctrl(frame, 1, cid);
+			break;
+		default:
+			packet_hexdump(data + consumed + 2, len);
+			break;
+		}
+
+		consumed += len + 2;
+	}
+
+	if (consumed < size)
+		packet_hexdump(data + consumed, size - consumed);
+}
+
+static void print_info_type(uint16_t type)
+{
+	const char *str;
+
+	switch (le16_to_cpu(type)) {
+	case 0x0001:
+		str = "Connectionless MTU";
+		break;
+	case 0x0002:
+		str = "Extended features supported";
+		break;
+	case 0x0003:
+		str = "Fixed channels supported";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%4.4x)", str, le16_to_cpu(type));
+}
+
+static void print_info_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Success";
+		break;
+	case 0x0001:
+		str = "Not supported";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static struct {
+	uint8_t bit;
+	const char *str;
+} features_table[] = {
+	{  0, "Flow control mode"			},
+	{  1, "Retransmission mode"			},
+	{  2, "Bi-directional QoS"			},
+	{  3, "Enhanced Retransmission Mode"		},
+	{  4, "Streaming Mode"				},
+	{  5, "FCS Option"				},
+	{  6, "Extended Flow Specification for BR/EDR"	},
+	{  7, "Fixed Channels"				},
+	{  8, "Extended Window Size"			},
+	{  9, "Unicast Connectionless Data Reception"	},
+	{ 31, "Reserved for feature mask extension"	},
+	{ }
+};
+
+static void print_features(uint32_t features)
+{
+	uint32_t mask = features;
+	int i;
+
+	print_field("Features: 0x%8.8x", features);
+
+	for (i = 0; features_table[i].str; i++) {
+		if (features & (1 << features_table[i].bit)) {
+			print_field("  %s", features_table[i].str);
+			mask &= ~(1 << features_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_field("  Unknown features (0x%8.8x)", mask);
+}
+
+static struct {
+	uint16_t cid;
+	const char *str;
+} channels_table[] = {
+	{ 0x0000, "Null identifier"		},
+	{ 0x0001, "L2CAP Signaling (BR/EDR)"	},
+	{ 0x0002, "Connectionless reception"	},
+	{ 0x0003, "AMP Manager Protocol"	},
+	{ 0x0004, "Attribute Protocol"		},
+	{ 0x0005, "L2CAP Signaling (LE)"	},
+	{ 0x0006, "Security Manager (LE)"	},
+	{ 0x0007, "Security Manager (BR/EDR)"	},
+	{ 0x003f, "AMP Test Manager"		},
+	{ }
+};
+
+static void print_channels(uint64_t channels)
+{
+	uint64_t mask = channels;
+	int i;
+
+	print_field("Channels: 0x%16.16" PRIx64, channels);
+
+	for (i = 0; channels_table[i].str; i++) {
+		if (channels & (1 << channels_table[i].cid)) {
+			print_field("  %s", channels_table[i].str);
+			mask &= ~(1 << channels_table[i].cid);
+		}
+	}
+
+	if (mask)
+		print_field("  Unknown channels (0x%8.8" PRIx64 ")", mask);
+}
+
+static void print_move_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Move success";
+		break;
+	case 0x0001:
+		str = "Move pending";
+		break;
+	case 0x0002:
+		str = "Move refused - Controller ID not supported";
+		break;
+	case 0x0003:
+		str = "Move refused - new Controller ID is same";
+		break;
+	case 0x0004:
+		str = "Move refused - Configuration not supported";
+		break;
+	case 0x0005:
+		str = "Move refused - Move Channel collision";
+		break;
+	case 0x0006:
+		str = "Move refused - Channel not allowed to be moved";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void print_move_cfm_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Move success - both sides succeed";
+		break;
+	case 0x0001:
+		str = "Move failure - one or both sides refuse";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void print_conn_param_result(uint16_t result)
+{
+	const char *str;
+
+	switch (le16_to_cpu(result)) {
+	case 0x0000:
+		str = "Connection Parameters accepted";
+		break;
+	case 0x0001:
+		str = "Connection Parameters rejected";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
+}
+
+static void sig_cmd_reject(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_cmd_reject *pdu = frame->data;
+	const void *data = frame->data;
+	uint16_t size = frame->size;
+	uint16_t scid, dcid;
+
+	print_reject_reason(pdu->reason);
+
+	data += sizeof(*pdu);
+	size -= sizeof(*pdu);
+
+	switch (le16_to_cpu(pdu->reason)) {
+	case 0x0000:
+		if (size != 0) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+			break;
+		}
+		break;
+	case 0x0001:
+		if (size != 2) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+			break;
+		}
+		print_field("MTU: %d", get_le16(data));
+		break;
+	case 0x0002:
+		if (size != 4) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+			break;
+		}
+		dcid = get_le16(data);
+		scid = get_le16(data + 2);
+		print_cid("Destination", cpu_to_le16(dcid));
+		print_cid("Source", cpu_to_le16(scid));
+		break;
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+static void sig_conn_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_conn_req *pdu = frame->data;
+
+	print_psm(pdu->psm);
+	print_cid("Source", pdu->scid);
+
+	assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), 0);
+}
+
+static void sig_conn_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_conn_rsp *pdu = frame->data;
+
+	print_cid("Destination", pdu->dcid);
+	print_cid("Source", pdu->scid);
+	print_conn_result(pdu->result);
+	print_conn_status(pdu->status);
+
+	assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));
+}
+
+static void sig_config_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_config_req *pdu = frame->data;
+
+	print_cid("Destination", pdu->dcid);
+	print_config_flags(pdu->flags);
+	print_config_options(frame, 4, le16_to_cpu(pdu->dcid), false);
+}
+
+static void sig_config_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_config_rsp *pdu = frame->data;
+
+	print_cid("Source", pdu->scid);
+	print_config_flags(pdu->flags);
+	print_config_result(pdu->result);
+	print_config_options(frame, 6, le16_to_cpu(pdu->scid), true);
+}
+
+static void sig_disconn_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_disconn_req *pdu = frame->data;
+
+	print_cid("Destination", pdu->dcid);
+	print_cid("Source", pdu->scid);
+}
+
+static void sig_disconn_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_disconn_rsp *pdu = frame->data;
+
+	print_cid("Destination", pdu->dcid);
+	print_cid("Source", pdu->scid);
+
+	release_scid(frame, le16_to_cpu(pdu->scid));
+}
+
+static void sig_echo_req(const struct l2cap_frame *frame)
+{
+	packet_hexdump(frame->data, frame->size);
+}
+
+static void sig_echo_rsp(const struct l2cap_frame *frame)
+{
+	packet_hexdump(frame->data, frame->size);
+}
+
+static void sig_info_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_info_req *pdu = frame->data;
+
+	print_info_type(pdu->type);
+}
+
+static void sig_info_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_info_rsp *pdu = frame->data;
+	const void *data = frame->data;
+	uint16_t size = frame->size;
+
+	print_info_type(pdu->type);
+	print_info_result(pdu->result);
+
+	data += sizeof(*pdu);
+	size -= sizeof(*pdu);
+
+	if (le16_to_cpu(pdu->result) != 0x0000) {
+		if (size > 0) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+		}
+		return;
+	}
+
+	switch (le16_to_cpu(pdu->type)) {
+	case 0x0001:
+		if (size != 2) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+			break;
+		}
+		print_field("MTU: %d", get_le16(data));
+		break;
+	case 0x0002:
+		if (size != 4) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+			break;
+		}
+		print_features(get_le32(data));
+		break;
+	case 0x0003:
+		if (size != 8) {
+			print_text(COLOR_ERROR, "invalid data size");
+			packet_hexdump(data, size);
+			break;
+		}
+		print_channels(get_le64(data));
+		break;
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+static void sig_create_chan_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_create_chan_req *pdu = frame->data;
+
+	print_psm(pdu->psm);
+	print_cid("Source", pdu->scid);
+	print_field("Controller ID: %d", pdu->ctrlid);
+
+	assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm),
+								pdu->ctrlid);
+}
+
+static void sig_create_chan_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_create_chan_rsp *pdu = frame->data;
+
+	print_cid("Destination", pdu->dcid);
+	print_cid("Source", pdu->scid);
+	print_create_chan_result(pdu->result);
+	print_conn_status(pdu->status);
+
+	assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));
+}
+
+static void sig_move_chan_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_move_chan_req *pdu = frame->data;
+
+	print_cid("Initiator", pdu->icid);
+	print_field("Controller ID: %d", pdu->ctrlid);
+}
+
+static void sig_move_chan_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_move_chan_rsp *pdu = frame->data;
+
+	print_cid("Initiator", pdu->icid);
+	print_move_result(pdu->result);
+}
+
+static void sig_move_chan_cfm(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_move_chan_cfm *pdu = frame->data;
+
+	print_cid("Initiator", pdu->icid);
+	print_move_cfm_result(pdu->result);
+}
+
+static void sig_move_chan_cfm_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_move_chan_cfm_rsp *pdu = frame->data;
+
+	print_cid("Initiator", pdu->icid);
+}
+
+static void sig_conn_param_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_conn_param_req *pdu = frame->data;
+
+	print_field("Min interval: %d", le16_to_cpu(pdu->min_interval));
+	print_field("Max interval: %d", le16_to_cpu(pdu->max_interval));
+	print_field("Slave latency: %d", le16_to_cpu(pdu->latency));
+	print_field("Timeout multiplier: %d", le16_to_cpu(pdu->timeout));
+}
+
+static void sig_conn_param_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_conn_param_rsp *pdu = frame->data;
+
+	print_conn_param_result(pdu->result);
+}
+
+static void sig_le_conn_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_le_conn_req *pdu = frame->data;
+
+	print_psm(pdu->psm);
+	print_cid("Source", pdu->scid);
+	print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+	print_field("MPS: %u", le16_to_cpu(pdu->mps));
+	print_field("Credits: %u", le16_to_cpu(pdu->credits));
+
+	assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), 0);
+}
+
+static void sig_le_conn_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_le_conn_rsp *pdu = frame->data;
+
+	print_cid("Destination", pdu->dcid);
+	print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+	print_field("MPS: %u", le16_to_cpu(pdu->mps));
+	print_field("Credits: %u", le16_to_cpu(pdu->credits));
+	print_le_conn_result(pdu->result);
+
+	assign_dcid(frame, le16_to_cpu(pdu->dcid), 0);
+}
+
+static void sig_le_flowctl_creds(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_pdu_le_flowctl_creds *pdu = frame->data;
+
+	print_cid("Source", pdu->cid);
+	print_field("Credits: %u", le16_to_cpu(pdu->credits));
+}
+
+struct sig_opcode_data {
+	uint8_t opcode;
+	const char *str;
+	void (*func) (const struct l2cap_frame *frame);
+	uint16_t size;
+	bool fixed;
+};
+
+static const struct sig_opcode_data bredr_sig_opcode_table[] = {
+	{ 0x01, "Command Reject",
+			sig_cmd_reject, 2, false },
+	{ 0x02, "Connection Request",
+			sig_conn_req, 4, true },
+	{ 0x03, "Connection Response",
+			sig_conn_rsp, 8, true },
+	{ 0x04, "Configure Request",
+			sig_config_req, 4, false },
+	{ 0x05, "Configure Response",
+			sig_config_rsp, 6, false },
+	{ 0x06, "Disconnection Request",
+			sig_disconn_req, 4, true },
+	{ 0x07, "Disconnection Response",
+			sig_disconn_rsp, 4, true },
+	{ 0x08, "Echo Request",
+			sig_echo_req, 0, false },
+	{ 0x09, "Echo Response",
+			sig_echo_rsp, 0, false },
+	{ 0x0a, "Information Request",
+			sig_info_req, 2, true },
+	{ 0x0b, "Information Response",
+			sig_info_rsp, 4, false },
+	{ 0x0c, "Create Channel Request",
+			sig_create_chan_req, 5, true },
+	{ 0x0d, "Create Channel Response",
+			sig_create_chan_rsp, 8, true },
+	{ 0x0e, "Move Channel Request",
+			sig_move_chan_req, 3, true },
+	{ 0x0f, "Move Channel Response",
+			sig_move_chan_rsp, 4, true },
+	{ 0x10, "Move Channel Confirmation",
+			sig_move_chan_cfm, 4, true },
+	{ 0x11, "Move Channel Confirmation Response",
+			sig_move_chan_cfm_rsp, 2, true },
+	{ },
+};
+
+static const struct sig_opcode_data le_sig_opcode_table[] = {
+	{ 0x01, "Command Reject",
+			sig_cmd_reject, 2, false },
+	{ 0x06, "Disconnection Request",
+			sig_disconn_req, 4, true },
+	{ 0x07, "Disconnection Response",
+			sig_disconn_rsp, 4, true },
+	{ 0x12, "Connection Parameter Update Request",
+			sig_conn_param_req, 8, true },
+	{ 0x13, "Connection Parameter Update Response",
+			sig_conn_param_rsp, 2, true },
+	{ 0x14, "LE Connection Request",
+			sig_le_conn_req, 10, true },
+	{ 0x15, "LE Connection Response",
+			sig_le_conn_rsp, 10, true },
+	{ 0x16, "LE Flow Control Credit",
+			sig_le_flowctl_creds, 4, true },
+	{ },
+};
+
+static void l2cap_frame_init(struct l2cap_frame *frame, uint16_t index, bool in,
+				uint16_t handle, uint8_t ident,
+				uint16_t cid, const void *data, uint16_t size)
+{
+	frame->index   = index;
+	frame->in      = in;
+	frame->handle  = handle;
+	frame->ident   = ident;
+	frame->cid     = cid;
+	frame->data    = data;
+	frame->size    = size;
+	frame->psm     = get_psm(frame);
+	frame->mode    = get_mode(frame);
+	frame->chan    = get_chan(frame);
+	frame->seq_num = get_seq_num(frame);
+}
+
+static void bredr_sig_packet(uint16_t index, bool in, uint16_t handle,
+				uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+
+	while (size > 0) {
+		const struct bt_l2cap_hdr_sig *hdr = data;
+		const struct sig_opcode_data *opcode_data = NULL;
+		const char *opcode_color, *opcode_str;
+		uint16_t len;
+		int i;
+
+		if (size < 4) {
+			print_text(COLOR_ERROR, "malformed signal packet");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		len = le16_to_cpu(hdr->len);
+
+		data += 4;
+		size -= 4;
+
+		if (size < len) {
+			print_text(COLOR_ERROR, "invalid signal packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		for (i = 0; bredr_sig_opcode_table[i].str; i++) {
+			if (bredr_sig_opcode_table[i].opcode == hdr->code) {
+				opcode_data = &bredr_sig_opcode_table[i];
+				break;
+			}
+		}
+
+		if (opcode_data) {
+			if (opcode_data->func) {
+				if (in)
+					opcode_color = COLOR_MAGENTA;
+				else
+					opcode_color = COLOR_BLUE;
+			} else
+				opcode_color = COLOR_WHITE_BG;
+			opcode_str = opcode_data->str;
+		} else {
+			opcode_color = COLOR_WHITE_BG;
+			opcode_str = "Unknown";
+		}
+
+		print_indent(6, opcode_color, "L2CAP: ", opcode_str,
+					COLOR_OFF,
+					" (0x%2.2x) ident %d len %d",
+					hdr->code, hdr->ident, len);
+
+		if (!opcode_data || !opcode_data->func) {
+			packet_hexdump(data, len);
+			data += len;
+			size -= len;
+			return;
+		}
+
+		if (opcode_data->fixed) {
+			if (len != opcode_data->size) {
+				print_text(COLOR_ERROR, "invalid size");
+				packet_hexdump(data, len);
+				data += len;
+				size -= len;
+				continue;
+			}
+		} else {
+			if (len < opcode_data->size) {
+				print_text(COLOR_ERROR, "too short packet");
+				packet_hexdump(data, size);
+				data += len;
+				size -= len;
+				continue;
+			}
+		}
+
+		l2cap_frame_init(&frame, index, in, handle, hdr->ident, cid,
+								data, len);
+		opcode_data->func(&frame);
+
+		data += len;
+		size -= len;
+	}
+
+	packet_hexdump(data, size);
+}
+
+static void le_sig_packet(uint16_t index, bool in, uint16_t handle,
+				uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+	const struct bt_l2cap_hdr_sig *hdr = data;
+	const struct sig_opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	uint16_t len;
+	int i;
+
+	if (size < 4) {
+		print_text(COLOR_ERROR, "malformed signal packet");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	len = le16_to_cpu(hdr->len);
+
+	data += 4;
+	size -= 4;
+
+	if (size != len) {
+		print_text(COLOR_ERROR, "invalid signal packet size");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	for (i = 0; le_sig_opcode_table[i].str; i++) {
+		if (le_sig_opcode_table[i].opcode == hdr->code) {
+			opcode_data = &le_sig_opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		if (opcode_data->func) {
+			if (in)
+				opcode_color = COLOR_MAGENTA;
+			else
+				opcode_color = COLOR_BLUE;
+		} else
+			opcode_color = COLOR_WHITE_BG;
+		opcode_str = opcode_data->str;
+	} else {
+		opcode_color = COLOR_WHITE_BG;
+		opcode_str = "Unknown";
+	}
+
+	print_indent(6, opcode_color, "LE L2CAP: ", opcode_str, COLOR_OFF,
+					" (0x%2.2x) ident %d len %d",
+					hdr->code, hdr->ident, len);
+
+	if (!opcode_data || !opcode_data->func) {
+		packet_hexdump(data, len);
+		return;
+	}
+
+	if (opcode_data->fixed) {
+		if (len != opcode_data->size) {
+			print_text(COLOR_ERROR, "invalid size");
+			packet_hexdump(data, len);
+			return;
+		}
+	} else {
+		if (len < opcode_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	l2cap_frame_init(&frame, index, in, handle, hdr->ident, cid, data, len);
+	opcode_data->func(&frame);
+}
+
+static void connless_packet(uint16_t index, bool in, uint16_t handle,
+				uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+	const struct bt_l2cap_hdr_connless *hdr = data;
+	uint16_t psm;
+
+	if (size < 2) {
+		print_text(COLOR_ERROR, "malformed connectionless packet");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	psm = le16_to_cpu(hdr->psm);
+
+	data += 2;
+	size -= 2;
+
+	print_indent(6, COLOR_CYAN, "L2CAP: Connectionless", "", COLOR_OFF,
+						" len %d [PSM %d]", size, psm);
+
+	switch (psm) {
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+
+	l2cap_frame_init(&frame, index, in, handle, 0, cid, data, size);
+}
+
+static void print_controller_list(const uint8_t *data, uint16_t size)
+{
+	while (size > 2) {
+		const char *str;
+
+		print_field("Controller ID: %d", data[0]);
+
+		switch (data[1]) {
+		case 0x00:
+			str = "Primary BR/EDR Controller";
+			break;
+		case 0x01:
+			str = "802.11 AMP Controller";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("  Type: %s (0x%2.2x)", str, data[1]);
+
+		switch (data[2]) {
+		case 0x00:
+			str = "Present";
+			break;
+		case 0x01:
+			str = "Bluetooth only";
+			break;
+		case 0x02:
+			str = "No capacity";
+			break;
+		case 0x03:
+			str = "Low capacity";
+			break;
+		case 0x04:
+			str = "Medium capacity";
+			break;
+		case 0x05:
+			str = "High capacity";
+			break;
+		case 0x06:
+			str = "Full capacity";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("  Status: %s (0x%2.2x)", str, data[2]);
+
+		data += 3;
+		size -= 3;
+	}
+
+	packet_hexdump(data, size);
+}
+
+static void amp_cmd_reject(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_cmd_reject *pdu = frame->data;
+
+	print_field("Reason: 0x%4.4x", le16_to_cpu(pdu->reason));
+}
+
+static void amp_discover_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_discover_req *pdu = frame->data;
+
+	print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size));
+	print_field("Extended feature mask: 0x%4.4x",
+					le16_to_cpu(pdu->features));
+}
+
+static void amp_discover_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_discover_rsp *pdu = frame->data;
+
+	print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size));
+	print_field("Extended feature mask: 0x%4.4x",
+					le16_to_cpu(pdu->features));
+
+	print_controller_list(frame->data + 4, frame->size - 4);
+}
+
+static void amp_change_notify(const struct l2cap_frame *frame)
+{
+	print_controller_list(frame->data, frame->size);
+}
+
+static void amp_change_response(const struct l2cap_frame *frame)
+{
+}
+
+static void amp_get_info_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_get_info_req *pdu = frame->data;
+
+	print_field("Controller ID: %d", pdu->ctrlid);
+}
+
+static void amp_get_info_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_get_info_rsp *pdu = frame->data;
+	const char *str;
+
+	print_field("Controller ID: %d", pdu->ctrlid);
+
+	switch (pdu->status) {
+	case 0x00:
+		str = "Success";
+		break;
+	case 0x01:
+		str = "Invalid Controller ID";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Status: %s (0x%2.2x)", str, pdu->status);
+
+	print_field("Total bandwidth: %d kbps", le32_to_cpu(pdu->total_bw));
+	print_field("Max guaranteed bandwidth: %d kbps",
+						le32_to_cpu(pdu->max_bw));
+	print_field("Min latency: %d", le32_to_cpu(pdu->min_latency));
+
+	print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(pdu->pal_cap));
+	print_field("Max ASSOC length: %d", le16_to_cpu(pdu->max_assoc_len));
+}
+
+static void amp_get_assoc_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_get_assoc_req *pdu = frame->data;
+
+	print_field("Controller ID: %d", pdu->ctrlid);
+}
+
+static void amp_get_assoc_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_get_assoc_rsp *pdu = frame->data;
+	const char *str;
+
+	print_field("Controller ID: %d", pdu->ctrlid);
+
+	switch (pdu->status) {
+	case 0x00:
+		str = "Success";
+		break;
+	case 0x01:
+		str = "Invalid Controller ID";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Status: %s (0x%2.2x)", str, pdu->status);
+
+	packet_hexdump(frame->data + 2, frame->size - 2);
+}
+
+static void amp_create_phy_link_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_create_phy_link_req *pdu = frame->data;
+
+	print_field("Local controller ID: %d", pdu->local_ctrlid);
+	print_field("Remote controller ID: %d", pdu->remote_ctrlid);
+
+	packet_hexdump(frame->data + 2, frame->size - 2);
+}
+
+static void amp_create_phy_link_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_create_phy_link_rsp *pdu = frame->data;
+	const char *str;
+
+	print_field("Local controller ID: %d", pdu->local_ctrlid);
+	print_field("Remote controller ID: %d", pdu->remote_ctrlid);
+
+	switch (pdu->status) {
+	case 0x00:
+		str = "Success";
+		break;
+	case 0x01:
+		str = "Invalid Controller ID";
+		break;
+	case 0x02:
+		str = "Failed - Unable to start link creation";
+		break;
+	case 0x03:
+		str = "Failed - Collision occurred";
+		break;
+	case 0x04:
+		str = "Failed - Disconnected link packet received";
+		break;
+	case 0x05:
+		str = "Failed - Link already exists";
+		break;
+	case 0x06:
+		str = "Failed - Security violation";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Status: %s (0x%2.2x)", str, pdu->status);
+}
+
+static void amp_disconn_phy_link_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_disconn_phy_link_req *pdu = frame->data;
+
+	print_field("Local controller ID: %d", pdu->local_ctrlid);
+	print_field("Remote controller ID: %d", pdu->remote_ctrlid);
+}
+
+static void amp_disconn_phy_link_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_amp_disconn_phy_link_rsp *pdu = frame->data;
+	const char *str;
+
+	print_field("Local controller ID: %d", pdu->local_ctrlid);
+	print_field("Remote controller ID: %d", pdu->remote_ctrlid);
+
+	switch (pdu->status) {
+	case 0x00:
+		str = "Success";
+		break;
+	case 0x01:
+		str = "Invalid Controller ID";
+		break;
+	case 0x02:
+		str = "Failed - No link exists";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Status: %s (0x%2.2x)", str, pdu->status);
+}
+
+struct amp_opcode_data {
+	uint8_t opcode;
+	const char *str;
+	void (*func) (const struct l2cap_frame *frame);
+	uint16_t size;
+	bool fixed;
+};
+
+static const struct amp_opcode_data amp_opcode_table[] = {
+	{ 0x01, "Command Reject",
+			amp_cmd_reject, 2, false },
+	{ 0x02, "Discover Request",
+			amp_discover_req, 4, true },
+	{ 0x03, "Discover Response",
+			amp_discover_rsp, 7, false },
+	{ 0x04, "Change Notify",
+			amp_change_notify, 3, false },
+	{ 0x05, "Change Response",
+			amp_change_response, 0, true },
+	{ 0x06, "Get Info Request",
+			amp_get_info_req, 1, true },
+	{ 0x07, "Get Info Response",
+			amp_get_info_rsp, 18, true },
+	{ 0x08, "Get Assoc Request",
+			amp_get_assoc_req, 1, true },
+	{ 0x09, "Get Assoc Response",
+			amp_get_assoc_rsp, 2, false },
+	{ 0x0a, "Create Physical Link Request",
+			amp_create_phy_link_req, 2, false },
+	{ 0x0b, "Create Physical Link Response",
+			amp_create_phy_link_rsp, 3, true },
+	{ 0x0c, "Disconnect Physical Link Request",
+			amp_disconn_phy_link_req, 2, true },
+	{ 0x0d, "Disconnect Physical Link Response",
+			amp_disconn_phy_link_rsp, 3, true },
+	{ },
+};
+
+static void amp_packet(uint16_t index, bool in, uint16_t handle,
+			uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+	uint16_t control, fcs, len;
+	uint8_t opcode, ident;
+	const struct amp_opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	int i;
+
+	if (size < 4) {
+		print_text(COLOR_ERROR, "malformed info frame packet");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	control = get_le16(data);
+	fcs = get_le16(data + size - 2);
+
+	print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF,
+				" %d dlen %d control 0x%4.4x fcs 0x%4.4x",
+						3, size, control, fcs);
+
+	if (control & 0x01)
+		return;
+
+	if (size < 8) {
+		print_text(COLOR_ERROR, "malformed manager packet");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	opcode = *((const uint8_t *) (data + 2));
+	ident = *((const uint8_t *) (data + 3));
+	len = get_le16(data + 4);
+
+	if (len != size - 8) {
+		print_text(COLOR_ERROR, "invalid manager packet size");
+		packet_hexdump(data +  2, size - 4);
+		return;
+	}
+
+	for (i = 0; amp_opcode_table[i].str; i++) {
+		if (amp_opcode_table[i].opcode == opcode) {
+			opcode_data = &amp_opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		if (opcode_data->func) {
+			if (in)
+				opcode_color = COLOR_MAGENTA;
+			else
+				opcode_color = COLOR_BLUE;
+		} else
+			opcode_color = COLOR_WHITE_BG;
+		opcode_str = opcode_data->str;
+	} else {
+		opcode_color = COLOR_WHITE_BG;
+		opcode_str = "Unknown";
+	}
+
+	print_indent(6, opcode_color, "AMP: ", opcode_str, COLOR_OFF,
+			" (0x%2.2x) ident %d len %d", opcode, ident, len);
+
+	if (!opcode_data || !opcode_data->func) {
+		packet_hexdump(data + 6, size - 8);
+		return;
+	}
+
+	if (opcode_data->fixed) {
+		if (len != opcode_data->size) {
+			print_text(COLOR_ERROR, "invalid size");
+			packet_hexdump(data + 6, size - 8);
+			return;
+		}
+	} else {
+		if (len < opcode_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + 6, size - 8);
+			return;
+		}
+	}
+
+	l2cap_frame_init(&frame, index, in, handle, 0, cid, data + 6, len);
+	opcode_data->func(&frame);
+}
+
+static void print_hex_field(const char *label, const uint8_t *data,
+								uint8_t len)
+{
+	char str[len * 2 + 1];
+	uint8_t i;
+
+	str[0] = '\0';
+
+	for (i = 0; i < len; i++)
+		sprintf(str + (i * 2), "%2.2x", data[i]);
+
+	print_field("%s: %s", label, str);
+}
+
+static void print_uuid(const char *label, const void *data, uint16_t size)
+{
+	const char *str;
+
+	switch (size) {
+	case 2:
+		str = 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));
+		print_field("%s: %s (0x%8.8x)", label, str, get_le32(data));
+		break;
+	case 16:
+		str = uuid128_to_str(data);
+		print_field("%s: %s (%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x)",
+				label, str,
+				get_le32(data + 12), get_le16(data + 10),
+				get_le16(data + 8), get_le16(data + 6),
+				get_le32(data + 2), get_le16(data + 0));
+		break;
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+static void print_handle_range(const char *label, const void *data)
+{
+	print_field("%s: 0x%4.4x-0x%4.4x", label,
+				get_le16(data), get_le16(data + 2));
+}
+
+static void print_data_list(const char *label, uint8_t length,
+					const void *data, uint16_t size)
+{
+	uint8_t count;
+
+	if (length == 0)
+		return;
+
+	count = size / length;
+
+	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
+
+	while (size >= length) {
+		print_field("Handle: 0x%4.4x", get_le16(data));
+		print_hex_field("Value", data + 2, length - 2);
+
+		data += length;
+		size -= length;
+	}
+
+	packet_hexdump(data, size);
+}
+
+static void print_attribute_info(uint16_t type, const void *data, uint16_t len)
+{
+	const char *str = uuid16_to_str(type);
+
+	print_field("%s: %s (0x%4.4x)", "Attribute type", str, type);
+
+	switch (type) {
+	case 0x2800:	/* Primary Service */
+	case 0x2801:	/* Secondary Service */
+		print_uuid("  UUID", data, len);
+		break;
+	case 0x2802:	/* Include */
+		if (len < 4) {
+			print_hex_field("  Value", data, len);
+			break;
+		}
+		print_handle_range("  Handle range", data);
+		print_uuid("  UUID", data + 4, len - 4);
+		break;
+	case 0x2803:	/* Characteristic */
+		if (len < 3) {
+			print_hex_field("  Value", data, len);
+			break;
+		}
+		print_field("  Properties: 0x%2.2x", *((uint8_t *) data));
+		print_field("  Handle: 0x%2.2x", get_le16(data + 1));
+		print_uuid("  UUID", data + 3, len - 3);
+		break;
+	default:
+		print_hex_field("Value", data, len);
+		break;
+	}
+}
+
+static const char *att_opcode_to_str(uint8_t opcode);
+
+static void att_error_response(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_error_response *pdu = frame->data;
+	const char *str;
+
+	switch (pdu->error) {
+	case 0x01:
+		str = "Invalid Handle";
+		break;
+	case 0x02:
+		str = "Read Not Permitted";
+		break;
+	case 0x03:
+		str = "Write Not Permitted";
+		break;
+	case 0x04:
+		str = "Invalid PDU";
+		break;
+	case 0x05:
+		str = "Insufficient Authentication";
+		break;
+	case 0x06:
+		str = "Request Not Supported";
+		break;
+	case 0x07:
+		str = "Invalid Offset";
+		break;
+	case 0x08:
+		str = "Insufficient Authorization";
+		break;
+	case 0x09:
+		str = "Prepare Queue Full";
+		break;
+	case 0x0a:
+		str = "Attribute Not Found";
+		break;
+	case 0x0b:
+		str = "Attribute Not Long";
+		break;
+	case 0x0c:
+		str = "Insufficient Encryption Key Size";
+		break;
+	case 0x0d:
+		str = "Invalid Attribute Value Length";
+		break;
+	case 0x0e:
+		str = "Unlikely Error";
+		break;
+	case 0x0f:
+		str = "Insufficient Encryption";
+		break;
+	case 0x10:
+		str = "Unsupported Group Type";
+		break;
+	case 0x11:
+		str = "Insufficient Resources";
+		break;
+	case 0xfd:
+		str = "CCC Improperly Configured";
+		break;
+	case 0xfe:
+		str = "Procedure Already in Progress";
+		break;
+	case 0xff:
+		str = "Out of Range";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s (0x%2.2x)", att_opcode_to_str(pdu->request),
+							pdu->request);
+	print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
+	print_field("Error: %s (0x%2.2x)", str, pdu->error);
+}
+
+static void att_exchange_mtu_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_exchange_mtu_req *pdu = frame->data;
+
+	print_field("Client RX MTU: %d", le16_to_cpu(pdu->mtu));
+}
+
+static void att_exchange_mtu_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_exchange_mtu_rsp *pdu = frame->data;
+
+	print_field("Server RX MTU: %d", le16_to_cpu(pdu->mtu));
+}
+
+static void att_find_info_req(const struct l2cap_frame *frame)
+{
+	print_handle_range("Handle range", frame->data);
+}
+
+static const char *att_format_str(uint8_t format)
+{
+	switch (format) {
+	case 0x01:
+		return "UUID-16";
+	case 0x02:
+		return "UUID-128";
+	default:
+		return "unknown";
+	}
+}
+
+static uint16_t print_info_data_16(const void *data, uint16_t len)
+{
+	while (len >= 4) {
+		print_field("Handle: 0x%4.4x", get_le16(data));
+		print_uuid("UUID", data + 2, 2);
+		data += 4;
+		len -= 4;
+	}
+
+	return len;
+}
+
+static uint16_t print_info_data_128(const void *data, uint16_t len)
+{
+	while (len >= 18) {
+		print_field("Handle: 0x%4.4x", get_le16(data));
+		print_uuid("UUID", data + 2, 16);
+		data += 18;
+		len -= 18;
+	}
+
+	return len;
+}
+
+static void att_find_info_rsp(const struct l2cap_frame *frame)
+{
+	const uint8_t *format = frame->data;
+	uint16_t len;
+
+	print_field("Format: %s (0x%2.2x)", att_format_str(*format), *format);
+
+	if (*format == 0x01)
+		len = print_info_data_16(frame->data + 1, frame->size - 1);
+	else if (*format == 0x02)
+		len = print_info_data_128(frame->data + 1, frame->size - 1);
+	else
+		len = frame->size - 1;
+
+	packet_hexdump(frame->data + (frame->size - len), len);
+}
+
+static void att_find_by_type_val_req(const struct l2cap_frame *frame)
+{
+	uint16_t type;
+
+	print_handle_range("Handle range", frame->data);
+
+	type = get_le16(frame->data + 4);
+	print_attribute_info(type, frame->data + 6, frame->size - 6);
+}
+
+static void att_find_by_type_val_rsp(const struct l2cap_frame *frame)
+{
+	const uint8_t *ptr = frame->data;
+	uint16_t len = frame->size;
+
+	while (len >= 4) {
+		print_handle_range("Handle range", ptr);
+		ptr += 4;
+		len -= 4;
+	}
+
+	packet_hexdump(ptr, len);
+}
+
+static void att_read_type_req(const struct l2cap_frame *frame)
+{
+	print_handle_range("Handle range", frame->data);
+	print_uuid("Attribute type", frame->data + 4, frame->size - 4);
+}
+
+static void att_read_type_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_read_group_type_rsp *pdu = frame->data;
+
+	print_field("Attribute data length: %d", pdu->length);
+	print_data_list("Attribute data list", pdu->length,
+					frame->data + 1, frame->size - 1);
+}
+
+static void att_read_req(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_read_req *pdu = frame->data;
+
+	print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
+}
+
+static void att_read_rsp(const struct l2cap_frame *frame)
+{
+	print_hex_field("Value", frame->data, frame->size);
+}
+
+static void att_read_blob_req(const struct l2cap_frame *frame)
+{
+	print_field("Handle: 0x%4.4x", get_le16(frame->data));
+	print_field("Offset: 0x%4.4x", get_le16(frame->data + 2));
+}
+
+static void att_read_blob_rsp(const struct l2cap_frame *frame)
+{
+	packet_hexdump(frame->data, frame->size);
+}
+
+static void att_read_multiple_req(const struct l2cap_frame *frame)
+{
+	int i, count;
+
+	count = frame->size / 2;
+
+	for (i = 0; i < count; i++)
+		print_field("Handle: 0x%4.4x",
+					get_le16(frame->data + (i * 2)));
+}
+
+static void att_read_group_type_req(const struct l2cap_frame *frame)
+{
+	print_handle_range("Handle range", frame->data);
+	print_uuid("Attribute group type", frame->data + 4, frame->size - 4);
+}
+
+static void print_group_list(const char *label, uint8_t length,
+					const void *data, uint16_t size)
+{
+	uint8_t count;
+
+	if (length == 0)
+		return;
+
+	count = size / length;
+
+	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
+
+	while (size >= length) {
+		print_handle_range("Handle range", data);
+		print_uuid("UUID", data + 4, length - 4);
+
+		data += length;
+		size -= length;
+	}
+
+	packet_hexdump(data, size);
+}
+
+static void att_read_group_type_rsp(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_read_group_type_rsp *pdu = frame->data;
+
+	print_field("Attribute data length: %d", pdu->length);
+	print_group_list("Attribute group list", pdu->length,
+					frame->data + 1, frame->size - 1);
+}
+
+static void att_write_req(const struct l2cap_frame *frame)
+{
+	print_field("Handle: 0x%4.4x", get_le16(frame->data));
+	print_hex_field("  Data", frame->data + 2, frame->size - 2);
+}
+
+static void att_write_rsp(const struct l2cap_frame *frame)
+{
+}
+
+static void att_prepare_write_req(const struct l2cap_frame *frame)
+{
+	print_field("Handle: 0x%4.4x", get_le16(frame->data));
+	print_field("Offset: 0x%4.4x", get_le16(frame->data + 2));
+	print_hex_field("  Data", frame->data + 4, frame->size - 4);
+}
+
+static void att_prepare_write_rsp(const struct l2cap_frame *frame)
+{
+	print_field("Handle: 0x%4.4x", get_le16(frame->data));
+	print_field("Offset: 0x%4.4x", get_le16(frame->data + 2));
+	print_hex_field("  Data", frame->data + 4, frame->size - 4);
+}
+
+static void att_execute_write_req(const struct l2cap_frame *frame)
+{
+	uint8_t flags = *(uint8_t *) frame->data;
+	const char *flags_str;
+
+	switch (flags) {
+	case 0x00:
+		flags_str = "Cancel all prepared writes";
+		break;
+	case 0x01:
+		flags_str = "Immediately write all pending values";
+		break;
+	default:
+		flags_str = "Unknown";
+		break;
+	}
+
+	print_field("Flags: %s (0x%02x)", flags_str, flags);
+}
+
+static void att_handle_value_notify(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_handle_value_notify *pdu = frame->data;
+
+	print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
+	print_hex_field("  Data", frame->data + 2, frame->size - 2);
+}
+
+static void att_handle_value_ind(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_att_handle_value_ind *pdu = frame->data;
+
+	print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
+	print_hex_field("  Data", frame->data + 2, frame->size - 2);
+}
+
+static void att_handle_value_conf(const struct l2cap_frame *frame)
+{
+}
+
+static void att_write_command(const struct l2cap_frame *frame)
+{
+	print_field("Handle: 0x%4.4x", get_le16(frame->data));
+	print_hex_field("  Data", frame->data + 2, frame->size - 2);
+}
+
+static void att_signed_write_command(const struct l2cap_frame *frame)
+{
+	print_field("Handle: 0x%4.4x", get_le16(frame->data));
+	print_hex_field("  Data", frame->data + 2, frame->size - 2 - 12);
+	print_hex_field("  Signature", frame->data + frame->size - 12, 12);
+}
+
+struct att_opcode_data {
+	uint8_t opcode;
+	const char *str;
+	void (*func) (const struct l2cap_frame *frame);
+	uint8_t size;
+	bool fixed;
+};
+
+static const struct att_opcode_data att_opcode_table[] = {
+	{ 0x01, "Error Response",
+			att_error_response, 4, true },
+	{ 0x02, "Exchange MTU Request",
+			att_exchange_mtu_req, 2, true },
+	{ 0x03, "Exchange MTU Response",
+			att_exchange_mtu_rsp, 2, true },
+	{ 0x04, "Find Information Request",
+			att_find_info_req, 4, true },
+	{ 0x05, "Find Information Response",
+			att_find_info_rsp, 5, false },
+	{ 0x06, "Find By Type Value Request",
+			att_find_by_type_val_req, 6, false },
+	{ 0x07, "Find By Type Value Response",
+			att_find_by_type_val_rsp, 4, false },
+	{ 0x08, "Read By Type Request",
+			att_read_type_req, 6, false },
+	{ 0x09, "Read By Type Response",
+			att_read_type_rsp, 3, false },
+	{ 0x0a, "Read Request",
+			att_read_req, 2, true },
+	{ 0x0b, "Read Response",
+			att_read_rsp, 0, false },
+	{ 0x0c, "Read Blob Request",
+			att_read_blob_req, 4, true },
+	{ 0x0d, "Read Blob Response",
+			att_read_blob_rsp, 0, false },
+	{ 0x0e, "Read Multiple Request",
+			att_read_multiple_req, 4, false },
+	{ 0x0f, "Read Multiple Response"	},
+	{ 0x10, "Read By Group Type Request",
+			att_read_group_type_req, 6, false },
+	{ 0x11, "Read By Group Type Response",
+			att_read_group_type_rsp, 4, false },
+	{ 0x12, "Write Request"	,
+			att_write_req, 2, false	},
+	{ 0x13, "Write Response",
+			att_write_rsp, 0, true	},
+	{ 0x16, "Prepare Write Request",
+			att_prepare_write_req, 4, false },
+	{ 0x17, "Prepare Write Response",
+			att_prepare_write_rsp, 4, false },
+	{ 0x18, "Execute Write Request",
+			att_execute_write_req, 1, true },
+	{ 0x19, "Execute Write Response"	},
+	{ 0x1b, "Handle Value Notification",
+			att_handle_value_notify, 2, false },
+	{ 0x1d, "Handle Value Indication",
+			att_handle_value_ind, 2, false },
+	{ 0x1e, "Handle Value Confirmation",
+			att_handle_value_conf, 0, true },
+	{ 0x52, "Write Command",
+			att_write_command, 2, false },
+	{ 0xd2, "Signed Write Command", att_signed_write_command, 14, false },
+	{ }
+};
+
+static const char *att_opcode_to_str(uint8_t opcode)
+{
+	int i;
+
+	for (i = 0; att_opcode_table[i].str; i++) {
+		if (att_opcode_table[i].opcode == opcode)
+			return att_opcode_table[i].str;
+	}
+
+	return "Unknown";
+}
+
+static void att_packet(uint16_t index, bool in, uint16_t handle,
+			uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+	uint8_t opcode = *((const uint8_t *) data);
+	const struct att_opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	int i;
+
+	if (size < 1) {
+		print_text(COLOR_ERROR, "malformed attribute packet");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	for (i = 0; att_opcode_table[i].str; i++) {
+		if (att_opcode_table[i].opcode == opcode) {
+			opcode_data = &att_opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		if (opcode_data->func) {
+			if (in)
+				opcode_color = COLOR_MAGENTA;
+			else
+				opcode_color = COLOR_BLUE;
+		} else
+			opcode_color = COLOR_WHITE_BG;
+		opcode_str = opcode_data->str;
+	} else {
+		opcode_color = COLOR_WHITE_BG;
+		opcode_str = "Unknown";
+	}
+
+	print_indent(6, opcode_color, "ATT: ", opcode_str, COLOR_OFF,
+				" (0x%2.2x) len %d", opcode, size - 1);
+
+	if (!opcode_data || !opcode_data->func) {
+		packet_hexdump(data + 1, size - 1);
+		return;
+	}
+
+	if (opcode_data->fixed) {
+		if (size - 1 != opcode_data->size) {
+			print_text(COLOR_ERROR, "invalid size");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	} else {
+		if (size - 1 < opcode_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	}
+
+	l2cap_frame_init(&frame, index, in, handle, 0, cid, data + 1, size - 1);
+	opcode_data->func(&frame);
+}
+
+static void print_addr(const uint8_t *addr, uint8_t addr_type)
+{
+	const char *str;
+
+	switch (addr_type) {
+	case 0x00:
+		print_field("Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+						addr[5], addr[4], addr[3],
+						addr[2], addr[1], addr[0]);
+		break;
+	case 0x01:
+		switch ((addr[5] & 0xc0) >> 6) {
+		case 0x00:
+			str = "Non-Resolvable";
+			break;
+		case 0x01:
+			str = "Resolvable";
+			break;
+		case 0x03:
+			str = "Static";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
+					" (%s)", addr[5], addr[4], addr[3],
+					addr[2], addr[1], addr[0], str);
+		break;
+	default:
+		print_field("Address: %2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X",
+						addr[5], addr[4], addr[3],
+						addr[2], addr[1], addr[0]);
+		break;
+	}
+}
+
+static void print_addr_type(uint8_t addr_type)
+{
+	const char *str;
+
+	switch (addr_type) {
+	case 0x00:
+		str = "Public";
+		break;
+	case 0x01:
+		str = "Random";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Address type: %s (0x%2.2x)", str, addr_type);
+}
+
+static void print_smp_io_capa(uint8_t io_capa)
+{
+	const char *str;
+
+	switch (io_capa) {
+	case 0x00:
+		str = "DisplayOnly";
+		break;
+	case 0x01:
+		str = "DisplayYesNo";
+		break;
+	case 0x02:
+		str = "KeyboardOnly";
+		break;
+	case 0x03:
+		str = "NoInputNoOutput";
+		break;
+	case 0x04:
+		str = "KeyboardDisplay";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("IO capability: %s (0x%2.2x)", str, io_capa);
+}
+
+static void print_smp_oob_data(uint8_t oob_data)
+{
+	const char *str;
+
+	switch (oob_data) {
+	case 0x00:
+		str = "Authentication data not present";
+		break;
+	case 0x01:
+		str = "Authentication data from remote device present";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("OOB data: %s (0x%2.2x)", str, oob_data);
+}
+
+static void print_smp_auth_req(uint8_t auth_req)
+{
+	const char *bond, *mitm, *sc, *kp;
+
+	switch (auth_req & 0x03) {
+	case 0x00:
+		bond = "No bonding";
+		break;
+	case 0x01:
+		bond = "Bonding";
+		break;
+	default:
+		bond = "Reserved";
+		break;
+	}
+
+	if ((auth_req & 0x04))
+		mitm = "MITM";
+	else
+		mitm = "No MITM";
+
+	if ((auth_req & 0x08))
+		sc = "SC";
+	else
+		sc = "Legacy";
+
+	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);
+}
+
+static void print_smp_key_dist(const char *label, uint8_t dist)
+{
+	char str[27];
+
+	if (!(dist & 0x07)) {
+		strcpy(str, "<none> ");
+	} else {
+		str[0] = '\0';
+		if (dist & 0x01)
+			strcat(str, "EncKey ");
+		if (dist & 0x02)
+			strcat(str, "IdKey ");
+		if (dist & 0x04)
+			strcat(str, "Sign ");
+		if (dist & 0x08)
+			strcat(str, "LinkKey ");
+	}
+
+	print_field("%s: %s(0x%2.2x)", label, str, dist);
+}
+
+static void smp_pairing_request(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_pairing_request *pdu = frame->data;
+
+	print_smp_io_capa(pdu->io_capa);
+	print_smp_oob_data(pdu->oob_data);
+	print_smp_auth_req(pdu->auth_req);
+
+	print_field("Max encryption key size: %d", pdu->max_key_size);
+	print_smp_key_dist("Initiator key distribution", pdu->init_key_dist);
+	print_smp_key_dist("Responder key distribution", pdu->resp_key_dist);
+}
+
+static void smp_pairing_response(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_pairing_response *pdu = frame->data;
+
+	print_smp_io_capa(pdu->io_capa);
+	print_smp_oob_data(pdu->oob_data);
+	print_smp_auth_req(pdu->auth_req);
+
+	print_field("Max encryption key size: %d", pdu->max_key_size);
+	print_smp_key_dist("Initiator key distribution", pdu->init_key_dist);
+	print_smp_key_dist("Responder key distribution", pdu->resp_key_dist);
+}
+
+static void smp_pairing_confirm(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_pairing_confirm *pdu = frame->data;
+
+	print_hex_field("Confim value", pdu->value, 16);
+}
+
+static void smp_pairing_random(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_pairing_random *pdu = frame->data;
+
+	print_hex_field("Random value", pdu->value, 16);
+}
+
+static void smp_pairing_failed(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_pairing_failed *pdu = frame->data;
+	const char *str;
+
+	switch (pdu->reason) {
+	case 0x01:
+		str = "Passkey entry failed";
+		break;
+	case 0x02:
+		str = "OOB not available";
+		break;
+	case 0x03:
+		str = "Authentication requirements";
+		break;
+	case 0x04:
+		str = "Confirm value failed";
+		break;
+	case 0x05:
+		str = "Pairing not supported";
+		break;
+	case 0x06:
+		str = "Encryption key size";
+		break;
+	case 0x07:
+		str = "Command not supported";
+		break;
+	case 0x08:
+		str = "Unspecified reason";
+		break;
+	case 0x09:
+		str = "Repeated attempts";
+		break;
+	case 0x0a:
+		str = "Invalid parameters";
+		break;
+	case 0x0b:
+		str = "DHKey check failed";
+		break;
+	case 0x0c:
+		str = "Numeric comparison failed";
+		break;
+	case 0x0d:
+		str = "BR/EDR pairing in progress";
+		break;
+	case 0x0e:
+		str = "Cross-transport Key Derivation/Generation not allowed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reason: %s (0x%2.2x)", str, pdu->reason);
+}
+
+static void smp_encrypt_info(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_encrypt_info *pdu = frame->data;
+
+	print_hex_field("Long term key", pdu->ltk, 16);
+}
+
+static void smp_master_ident(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_master_ident *pdu = frame->data;
+
+	print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv));
+	print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand));
+}
+
+static void smp_ident_info(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_ident_info *pdu = frame->data;
+
+	print_hex_field("Identity resolving key", pdu->irk, 16);
+
+	keys_update_identity_key(pdu->irk);
+}
+
+static void smp_ident_addr_info(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_ident_addr_info *pdu = frame->data;
+
+	print_addr_type(pdu->addr_type);
+	print_addr(pdu->addr, pdu->addr_type);
+
+	keys_update_identity_addr(pdu->addr, pdu->addr_type);
+}
+
+static void smp_signing_info(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_signing_info *pdu = frame->data;
+
+	print_hex_field("Signature key", pdu->csrk, 16);
+}
+
+static void smp_security_request(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_security_request *pdu = frame->data;
+
+	print_smp_auth_req(pdu->auth_req);
+}
+
+static void smp_pairing_public_key(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_public_key *pdu = frame->data;
+
+	print_hex_field("X", pdu->x, 32);
+	print_hex_field("Y", pdu->y, 32);
+}
+
+static void smp_pairing_dhkey_check(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_dhkey_check *pdu = frame->data;
+
+	print_hex_field("E", pdu->e, 16);
+}
+
+static void smp_pairing_keypress_notification(const struct l2cap_frame *frame)
+{
+	const struct bt_l2cap_smp_keypress_notify *pdu = frame->data;
+	const char *str;
+
+	switch (pdu->type) {
+	case 0x00:
+		str = "Passkey entry started";
+		break;
+	case 0x01:
+		str = "Passkey digit entered";
+		break;
+	case 0x02:
+		str = "Passkey digit erased";
+		break;
+	case 0x03:
+		str = "Passkey cleared";
+		break;
+	case 0x04:
+		str = "Passkey entry completed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, pdu->type);
+}
+
+struct smp_opcode_data {
+	uint8_t opcode;
+	const char *str;
+	void (*func) (const struct l2cap_frame *frame);
+	uint8_t size;
+	bool fixed;
+};
+
+static const struct smp_opcode_data smp_opcode_table[] = {
+	{ 0x01, "Pairing Request",
+			smp_pairing_request, 6, true },
+	{ 0x02, "Pairing Response",
+			smp_pairing_response, 6, true },
+	{ 0x03, "Pairing Confirm",
+			smp_pairing_confirm, 16, true },
+	{ 0x04, "Pairing Random",
+			smp_pairing_random, 16, true },
+	{ 0x05, "Pairing Failed",
+			smp_pairing_failed, 1, true },
+	{ 0x06, "Encryption Information",
+			smp_encrypt_info, 16, true },
+	{ 0x07, "Master Identification",
+			smp_master_ident, 10, true },
+	{ 0x08, "Identity Information",
+			smp_ident_info, 16, true },
+	{ 0x09, "Identity Address Information",
+			smp_ident_addr_info, 7, true },
+	{ 0x0a, "Signing Information",
+			smp_signing_info, 16, true },
+	{ 0x0b, "Security Request",
+			smp_security_request, 1, true },
+	{ 0x0c, "Pairing Public Key",
+			smp_pairing_public_key, 64, true },
+	{ 0x0d, "Pairing DHKey Check",
+			smp_pairing_dhkey_check, 16, true },
+	{ 0x0e, "Pairing Keypress Notification",
+			smp_pairing_keypress_notification, 1, true },
+	{ }
+};
+
+static void smp_packet(uint16_t index, bool in, uint16_t handle,
+			uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+	uint8_t opcode = *((const uint8_t *) data);
+	const struct smp_opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	int i;
+
+	if (size < 1) {
+		print_text(COLOR_ERROR, "malformed attribute packet");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	for (i = 0; smp_opcode_table[i].str; i++) {
+		if (smp_opcode_table[i].opcode == opcode) {
+			opcode_data = &smp_opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		if (opcode_data->func) {
+			if (in)
+				opcode_color = COLOR_MAGENTA;
+			else
+				opcode_color = COLOR_BLUE;
+		} else
+			opcode_color = COLOR_WHITE_BG;
+		opcode_str = opcode_data->str;
+	} else {
+		opcode_color = COLOR_WHITE_BG;
+		opcode_str = "Unknown";
+	}
+
+	print_indent(6, opcode_color, cid == 0x0006 ? "SMP: " : "BR/EDR SMP: ",
+				opcode_str, COLOR_OFF, " (0x%2.2x) len %d",
+				opcode, size - 1);
+
+	if (!opcode_data || !opcode_data->func) {
+		packet_hexdump(data + 1, size - 1);
+		return;
+	}
+
+	if (opcode_data->fixed) {
+		if (size - 1 != opcode_data->size) {
+			print_text(COLOR_ERROR, "invalid size");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	} else {
+		if (size - 1 < opcode_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	}
+
+	l2cap_frame_init(&frame, index, in, handle, 0, cid, data + 1, size - 1);
+	opcode_data->func(&frame);
+}
+
+static void l2cap_frame(uint16_t index, bool in, uint16_t handle,
+			uint16_t cid, const void *data, uint16_t size)
+{
+	struct l2cap_frame frame;
+	uint32_t ctrl32 = 0;
+	uint16_t ctrl16 = 0;
+	uint8_t ext_ctrl;
+
+	switch (cid) {
+	case 0x0001:
+		bredr_sig_packet(index, in, handle, cid, data, size);
+		break;
+	case 0x0002:
+		connless_packet(index, in, handle, cid, data, size);
+		break;
+	case 0x0003:
+		amp_packet(index, in, handle, cid, data, size);
+		break;
+	case 0x0004:
+		att_packet(index, in, handle, cid, data, size);
+		break;
+	case 0x0005:
+		le_sig_packet(index, in, handle, cid, data, size);
+		break;
+	case 0x0006:
+	case 0x0007:
+		smp_packet(index, in, handle, cid, data, size);
+		break;
+	default:
+		l2cap_frame_init(&frame, index, in, handle, 0, cid, data, size);
+
+		if (frame.mode > 0) {
+			ext_ctrl = get_ext_ctrl(&frame);
+
+			if (ext_ctrl) {
+				if (!l2cap_frame_get_le32(&frame, &ctrl32))
+					return;
+
+				print_indent(6, COLOR_CYAN, "Channel:", "",
+						COLOR_OFF, " %d len %d"
+						" ext_ctrl 0x%8.8x"
+						" [PSM %d mode %d] {chan %d}",
+						cid, size, ctrl32, frame.psm,
+						frame.mode, frame.chan);
+
+				l2cap_ctrl_ext_parse(&frame, ctrl32);
+			} else {
+				if (!l2cap_frame_get_le16(&frame, &ctrl16))
+					return;
+
+				print_indent(6, COLOR_CYAN, "Channel:", "",
+						COLOR_OFF, " %d len %d"
+						" ctrl 0x%4.4x"
+						" [PSM %d mode %d] {chan %d}",
+						cid, size, ctrl16, frame.psm,
+						frame.mode, frame.chan);
+
+				l2cap_ctrl_parse(&frame, ctrl16);
+			}
+
+			printf("\n");
+		} else {
+			print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF,
+					" %d len %d [PSM %d mode %d] {chan %d}",
+						cid, size, frame.psm,
+						frame.mode, frame.chan);
+		}
+
+		switch (frame.psm) {
+		case 0x0001:
+			sdp_packet(&frame);
+			break;
+		case 0x0003:
+			rfcomm_packet(&frame);
+			break;
+		case 0x000f:
+			bnep_packet(&frame);
+			break;
+		case 0x001f:
+			att_packet(index, in, handle, cid, data, size);
+			break;
+		case 0x0017:
+		case 0x001B:
+			avctp_packet(&frame);
+			break;
+		case 0x0019:
+			avdtp_packet(&frame);
+			break;
+		default:
+			packet_hexdump(data, size);
+			break;
+		}
+		break;
+	}
+}
+
+void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags,
+					const void *data, uint16_t size)
+{
+	const struct bt_l2cap_hdr *hdr = data;
+	uint16_t len, cid;
+
+	if (index > MAX_INDEX - 1) {
+		print_text(COLOR_ERROR, "controller index too large");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	switch (flags) {
+	case 0x00:	/* start of a non-automatically-flushable PDU */
+	case 0x02:	/* start of an automatically-flushable PDU */
+		if (index_list[index][in].frag_len) {
+			print_text(COLOR_ERROR, "unexpected start frame");
+			packet_hexdump(data, size);
+			clear_fragment_buffer(index, in);
+			return;
+		}
+
+		if (size < sizeof(*hdr)) {
+			print_text(COLOR_ERROR, "frame too short");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		len = le16_to_cpu(hdr->len);
+		cid = le16_to_cpu(hdr->cid);
+
+		data += sizeof(*hdr);
+		size -= sizeof(*hdr);
+
+		if (len == size) {
+			/* complete frame */
+			l2cap_frame(index, in, handle, cid, data, len);
+			return;
+		}
+
+		if (size > len) {
+			print_text(COLOR_ERROR, "frame too long");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		index_list[index][in].frag_buf = malloc(len);
+		if (!index_list[index][in].frag_buf) {
+			print_text(COLOR_ERROR, "failed buffer allocation");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		memcpy(index_list[index][in].frag_buf, data, size);
+		index_list[index][in].frag_pos = size;
+		index_list[index][in].frag_len = len - size;
+		index_list[index][in].frag_cid = cid;
+		break;
+
+	case 0x01:	/* continuing fragment */
+		if (!index_list[index][in].frag_len) {
+			print_text(COLOR_ERROR, "unexpected continuation");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		if (size > index_list[index][in].frag_len) {
+			print_text(COLOR_ERROR, "fragment too long");
+			packet_hexdump(data, size);
+			clear_fragment_buffer(index, in);
+			return;
+		}
+
+		memcpy(index_list[index][in].frag_buf +
+				index_list[index][in].frag_pos, data, size);
+		index_list[index][in].frag_pos += size;
+		index_list[index][in].frag_len -= size;
+
+		if (!index_list[index][in].frag_len) {
+			/* complete frame */
+			l2cap_frame(index, in, handle,
+					index_list[index][in].frag_cid,
+					index_list[index][in].frag_buf,
+					index_list[index][in].frag_pos);
+			clear_fragment_buffer(index, in);
+			return;
+		}
+		break;
+
+	case 0x03:	/* complete automatically-flushable PDU */
+		if (index_list[index][in].frag_len) {
+			print_text(COLOR_ERROR, "unexpected complete frame");
+			packet_hexdump(data, size);
+			clear_fragment_buffer(index, in);
+			return;
+		}
+
+		if (size < sizeof(*hdr)) {
+			print_text(COLOR_ERROR, "frame too short");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		len = le16_to_cpu(hdr->len);
+		cid = le16_to_cpu(hdr->cid);
+
+		data += sizeof(*hdr);
+		size -= sizeof(*hdr);
+
+		if (len != size) {
+			print_text(COLOR_ERROR, "wrong frame size");
+			packet_hexdump(data, size);
+			return;
+		}
+
+		/* complete frame */
+		l2cap_frame(index, in, handle, cid, data, len);
+		break;
+
+	default:
+		print_text(COLOR_ERROR, "invalid packet flags (0x%2.2x)",
+								flags);
+		packet_hexdump(data, size);
+		return;
+	}
+}
diff --git a/bluez/repo/monitor/l2cap.h b/bluez/repo/monitor/l2cap.h
new file mode 100644
index 0000000..813c793
--- /dev/null
+++ b/bluez/repo/monitor/l2cap.h
@@ -0,0 +1,176 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+
+struct l2cap_frame {
+	uint16_t index;
+	bool in;
+	uint16_t handle;
+	uint8_t ident;
+	uint16_t cid;
+	uint16_t psm;
+	uint16_t chan;
+	uint8_t mode;
+	uint8_t seq_num;
+	const void *data;
+	uint16_t size;
+};
+
+static inline void l2cap_frame_pull(struct l2cap_frame *frame,
+				const struct l2cap_frame *source, uint16_t len)
+{
+	if (frame != source) {
+		frame->index   = source->index;
+		frame->in      = source->in;
+		frame->handle  = source->handle;
+		frame->ident   = source->ident;
+		frame->cid     = source->cid;
+		frame->psm     = source->psm;
+		frame->chan    = source->chan;
+		frame->mode    = source->mode;
+	}
+
+	frame->data = source->data + len;
+	frame->size = source->size - len;
+}
+
+static inline bool l2cap_frame_get_u8(struct l2cap_frame *frame, uint8_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = *((uint8_t *) frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_be16(struct l2cap_frame *frame,
+								uint16_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = get_be16(frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_le16(struct l2cap_frame *frame,
+								uint16_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = get_le16(frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_be32(struct l2cap_frame *frame,
+								uint32_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = get_be32(frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_le32(struct l2cap_frame *frame,
+								uint32_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = get_le32(frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_be64(struct l2cap_frame *frame,
+								uint64_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = get_be64(frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_le64(struct l2cap_frame *frame,
+								uint64_t *value)
+{
+	if (frame->size < sizeof(*value))
+		return false;
+
+	if (value)
+		*value = get_le64(frame->data);
+
+	l2cap_frame_pull(frame, frame, sizeof(*value));
+
+	return true;
+}
+
+static inline bool l2cap_frame_get_be128(struct l2cap_frame *frame,
+					uint64_t *lvalue, uint64_t *rvalue)
+{
+	if (frame->size < (sizeof(*lvalue) + sizeof(*rvalue)))
+		return false;
+
+	if (lvalue && rvalue) {
+		*lvalue = get_be64(frame->data);
+		*rvalue = get_be64(frame->data);
+	}
+
+	l2cap_frame_pull(frame, frame, (sizeof(*lvalue) + sizeof(*rvalue)));
+
+	return true;
+}
+
+void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags,
+					const void *data, uint16_t size);
+
+void rfcomm_packet(const struct l2cap_frame *frame);
diff --git a/bluez/repo/monitor/ll.c b/bluez/repo/monitor/ll.c
new file mode 100644
index 0000000..6212f79
--- /dev/null
+++ b/bluez/repo/monitor/ll.c
@@ -0,0 +1,572 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <inttypes.h>
+
+#include "src/shared/util.h"
+#include "display.h"
+#include "packet.h"
+#include "crc.h"
+#include "bt.h"
+#include "ll.h"
+
+#define COLOR_OPCODE		COLOR_MAGENTA
+#define COLOR_OPCODE_UNKNOWN	COLOR_WHITE_BG
+
+#define MAX_CHANNEL 16
+
+struct channel_data {
+	uint32_t access_addr;
+	uint32_t crc_init;
+};
+
+static struct channel_data channel_list[MAX_CHANNEL];
+
+static void set_crc_init(uint32_t access_addr, uint32_t crc_init)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHANNEL; i++) {
+		if (channel_list[i].access_addr == 0x00000000 ||
+				channel_list[i].access_addr == access_addr) {
+			channel_list[i].access_addr = access_addr;
+			channel_list[i].crc_init = crc_init;
+			break;
+		}
+	}
+}
+
+static uint32_t get_crc_init(uint32_t access_addr)
+{
+	int i;
+
+	for (i = 0; i < MAX_CHANNEL; i++) {
+		if (channel_list[i].access_addr == access_addr)
+			return channel_list[i].crc_init;
+	}
+
+	return 0x00000000;
+}
+
+static void advertising_packet(const void *data, uint8_t size)
+{
+	const uint8_t *ptr = data;
+	uint8_t pdu_type, length, win_size, hop, sca;
+	bool tx_add, rx_add;
+	uint32_t access_addr, crc_init;
+	uint16_t win_offset, interval, latency, timeout;
+	const char *str;
+
+	if (size < 2) {
+		print_text(COLOR_ERROR, "packet too short");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	pdu_type = ptr[0] & 0x0f;
+	tx_add = !!(ptr[0] & 0x40);
+	rx_add = !!(ptr[0] & 0x80);
+	length = ptr[1] & 0x3f;
+
+	switch (pdu_type) {
+	case 0x00:
+		str = "ADV_IND";
+		break;
+	case 0x01:
+		str = "ADV_DIRECT_IND";
+		break;
+	case 0x02:
+		str = "ADV_NONCONN_IND";
+		break;
+	case 0x03:
+		str = "SCAN_REQ";
+		break;
+	case 0x04:
+		str = "SCAN_RSP";
+		break;
+	case 0x05:
+		str = "CONNECT_REQ";
+		break;
+	case 0x06:
+		str = "ADV_SCAN_IND";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, pdu_type);
+	print_field("TxAdd: %u", tx_add);
+	print_field("RxAdd: %u", rx_add);
+	print_field("Length: %u", length);
+
+	if (length != size - 2) {
+		print_text(COLOR_ERROR, "packet size mismatch");
+		packet_hexdump(data + 2, size - 2);
+		return;
+	}
+
+	switch (pdu_type) {
+	case 0x00:	/* ADV_IND */
+	case 0x02:	/* AVD_NONCONN_IND */
+	case 0x06:	/* ADV_SCAN_IND */
+	case 0x04:	/* SCAN_RSP */
+		if (length < 6) {
+			print_text(COLOR_ERROR, "payload too short");
+			packet_hexdump(data + 2, length);
+			return;
+		}
+
+		packet_print_addr("Advertiser address", data + 2, tx_add);
+		packet_print_ad(data + 8, length - 6);
+		break;
+
+	case 0x01:	/* ADV_DIRECT_IND */
+		if (length < 12) {
+			print_text(COLOR_ERROR, "payload too short");
+			packet_hexdump(data + 2, length);
+			return;
+		}
+
+		packet_print_addr("Advertiser address", data + 2, tx_add);
+		packet_print_addr("Inititator address", data + 8, rx_add);
+		break;
+
+	case 0x03:	/* SCAN_REQ */
+		if (length < 12) {
+			print_text(COLOR_ERROR, "payload too short");
+			packet_hexdump(data + 2, length);
+			return;
+		}
+
+		packet_print_addr("Scanner address", data + 2, tx_add);
+		packet_print_addr("Advertiser address", data + 8, rx_add);
+		break;
+
+	case 0x05:	/* CONNECT_REQ */
+		if (length < 34) {
+			print_text(COLOR_ERROR, "payload too short");
+			packet_hexdump(data + 2, length);
+			return;
+		}
+
+		packet_print_addr("Inititator address", data + 2, tx_add);
+		packet_print_addr("Advertiser address", data + 8, rx_add);
+
+		access_addr = ptr[14] | ptr[15] << 8 |
+					ptr[16] << 16 | ptr[17] << 24;
+		crc_init = ptr[18] | ptr[19] << 8 | ptr[20] << 16;
+
+		print_field("Access address: 0x%8.8x", access_addr);
+		print_field("CRC init: 0x%6.6x", crc_init);
+
+		set_crc_init(access_addr, crc24_bit_reverse(crc_init));
+
+		win_size = ptr[21];
+		win_offset = ptr[22] | ptr[23] << 8;
+		interval = ptr[24] | ptr[25] << 8;
+		latency = ptr[26] | ptr[27] << 8;
+		timeout = ptr[28] | ptr[29] << 8;
+
+		print_field("Transmit window size: %u", win_size);
+		print_field("Transmit window offset: %u", win_offset);
+		print_field("Connection interval: %u", interval);
+		print_field("Connection slave latency: %u", latency);
+		print_field("Connection supervision timeout: %u", timeout);
+
+		packet_print_channel_map_ll(ptr + 30);
+
+		hop = ptr[35] & 0x1f;
+		sca = (ptr[35] & 0xe0) >> 5;
+
+		switch (sca) {
+		case 0:
+			str = "251 ppm to 500 ppm";
+			break;
+		case 1:
+			str = "151 ppm to 250 ppm";
+			break;
+		case 2:
+			str = "101 ppm to 150ppm";
+			break;
+		case 3:
+			str = "76 ppm to 100 ppm";
+			break;
+		case 4:
+			str = "51 ppm to 75 ppm";
+			break;
+		case 5:
+			str = "31 ppm to 50 ppm";
+			break;
+		case 6:
+			str = "21 ppm to 30 ppm";
+			break;
+		case 7:
+			str = "0 ppm to 20 ppm";
+			break;
+		default:
+			str = "Invalid";
+			break;
+		}
+
+		print_field("Hop increment: %u", hop);
+		print_field("Sleep clock accuracy: %s (%u)", str, sca);
+		break;
+
+	default:
+		packet_hexdump(data + 2, length);
+		break;
+	}
+}
+
+static void data_packet(const void *data, uint8_t size, bool padded)
+{
+	const uint8_t *ptr = data;
+	uint8_t llid, length;
+	bool nesn, sn, md;
+	const char *str;
+
+	if (size < 2) {
+		print_text(COLOR_ERROR, "packet too short");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	llid = ptr[0] & 0x03;
+	nesn = !!(ptr[0] & 0x04);
+	sn = !!(ptr[0] & 0x08);
+	md = !!(ptr[0] & 0x10);
+	length = ptr[1] & 0x1f;
+
+	switch (llid) {
+	case 0x01:
+		if (length > 0)
+			str = "Continuation fragement of L2CAP message";
+		else
+			str = "Empty message";
+		break;
+	case 0x02:
+		str = "Start of L2CAP message";
+		break;
+	case 0x03:
+		str = "Control";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("LLID: %s (0x%2.2x)", str, llid);
+	print_field("Next expected sequence number: %u", nesn);
+	print_field("Sequence number: %u", sn);
+	print_field("More data: %u", md);
+	print_field("Length: %u", length);
+
+	switch (llid) {
+	case 0x03:
+		llcp_packet(data + 2, size - 2, padded);
+		break;
+
+	default:
+		packet_hexdump(data + 2, size - 2);
+		break;
+	}
+}
+
+void ll_packet(uint16_t frequency, const void *data, uint8_t size, bool padded)
+{
+	const struct bt_ll_hdr *hdr = data;
+	uint8_t channel = (frequency - 2402) / 2;
+	uint32_t access_addr;
+	char access_str[12];
+	const char *channel_label, *channel_color;
+	const uint8_t *pdu_data;
+	uint8_t pdu_len;
+	uint32_t pdu_crc, crc, crc_init;
+
+	if (size < sizeof(*hdr)) {
+		print_text(COLOR_ERROR, "packet missing header");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (size < sizeof(*hdr) + 3) {
+		print_text(COLOR_ERROR, "packet missing checksum");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (hdr->preamble != 0xaa && hdr->preamble != 0x55) {
+		print_text(COLOR_ERROR, "invalid preamble");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	access_addr = le32_to_cpu(hdr->access_addr);
+
+	pdu_data = data + sizeof(*hdr);
+	pdu_len = size - sizeof(*hdr) - 3;
+
+	pdu_crc = pdu_data[pdu_len + 0] | (pdu_data[pdu_len + 1] << 8) |
+						(pdu_data[pdu_len + 2] << 16);
+
+	if (access_addr == 0x8e89bed6) {
+		channel_label = "Advertising channel: ";
+		channel_color = COLOR_MAGENTA;
+	} else {
+		channel_label = "Data channel: ";
+		channel_color = COLOR_CYAN;
+	}
+
+	sprintf(access_str, "0x%8.8x", access_addr);
+
+	print_indent(6, channel_color, channel_label, access_str, COLOR_OFF,
+		" (channel %d) len %d crc 0x%6.6x", channel, pdu_len, pdu_crc);
+
+	if (access_addr == 0x8e89bed6)
+		crc_init = 0xaaaaaa;
+	else
+		crc_init = get_crc_init(access_addr);
+
+	if (crc_init) {
+		crc = crc24_calculate(crc_init, pdu_data, pdu_len);
+
+		if (crc != pdu_crc) {
+			print_text(COLOR_ERROR, "invalid checksum");
+			packet_hexdump(pdu_data, pdu_len);
+			return;
+		}
+	} else
+		print_text(COLOR_ERROR, "unknown access address");
+
+	if (access_addr == 0x8e89bed6)
+		advertising_packet(pdu_data, pdu_len);
+	else
+		data_packet(pdu_data, pdu_len, padded);
+}
+
+static void null_pdu(const void *data, uint8_t size)
+{
+}
+
+static void conn_update_req(const void *data, uint8_t size)
+{
+	const struct bt_ll_conn_update_req *pdu = data;
+
+	print_field("Transmit window size: %u", pdu->win_size);
+	print_field("Transmit window offset: %u", le16_to_cpu(pdu->win_offset));
+	print_field("Connection interval: %u", le16_to_cpu(pdu->interval));
+	print_field("Connection slave latency: %u", le16_to_cpu(pdu->latency));
+	print_field("Connection supervision timeout: %u", le16_to_cpu(pdu->timeout));
+	print_field("Connection instant: %u", le16_to_cpu(pdu->instant));
+}
+
+static void channel_map_req(const void *data, uint8_t size)
+{
+	const struct bt_ll_channel_map_req *pdu = data;
+
+	packet_print_channel_map_ll(pdu->map);
+	print_field("Connection instant: %u", le16_to_cpu(pdu->instant));
+}
+
+static void terminate_ind(const void *data, uint8_t size)
+{
+	const struct bt_ll_terminate_ind *pdu = data;
+
+	packet_print_error("Error code", pdu->error);
+}
+
+static void enc_req(const void *data, uint8_t size)
+{
+	const struct bt_ll_enc_req *pdu = data;
+
+	print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand));
+	print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv));
+	print_field("SKD (master): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd));
+	print_field("IV (master): 0x%8.8x", le32_to_cpu(pdu->iv));
+}
+
+static void enc_rsp(const void *data, uint8_t size)
+{
+	const struct bt_ll_enc_rsp *pdu = data;
+
+	print_field("SKD (slave): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd));
+	print_field("IV (slave): 0x%8.8x", le32_to_cpu(pdu->iv));
+}
+
+static const char *opcode_to_string(uint8_t opcode);
+
+static void unknown_rsp(const void *data, uint8_t size)
+{
+	const struct bt_ll_unknown_rsp *pdu = data;
+
+	print_field("Unknown type: %s (0x%2.2x)",
+				opcode_to_string(pdu->type), pdu->type);
+}
+
+static void feature_req(const void *data, uint8_t size)
+{
+	const struct bt_ll_feature_req *pdu = data;
+
+	packet_print_features_ll(pdu->features);
+}
+
+static void feature_rsp(const void *data, uint8_t size)
+{
+	const struct bt_ll_feature_rsp *pdu = data;
+
+	packet_print_features_ll(pdu->features);
+}
+
+static void version_ind(const void *data, uint8_t size)
+{
+	const struct bt_ll_version_ind *pdu = data;
+
+	packet_print_version("Version", pdu->version,
+				"Subversion", le16_to_cpu(pdu->subversion));
+	packet_print_company("Company", le16_to_cpu(pdu->company));
+}
+
+static void reject_ind(const void *data, uint8_t size)
+{
+	const struct bt_ll_reject_ind *pdu = data;
+
+	packet_print_error("Error code", pdu->error);
+}
+
+static void slave_feature_req(const void *data, uint8_t size)
+{
+	const struct bt_ll_slave_feature_req *pdu = data;
+
+	packet_print_features_ll(pdu->features);
+}
+
+static void reject_ind_ext(const void *data, uint8_t size)
+{
+	const struct bt_ll_reject_ind_ext *pdu = data;
+
+	print_field("Reject opcode: %u (0x%2.2x)", pdu->opcode, pdu->opcode);
+	packet_print_error("Error code", pdu->error);
+}
+
+struct llcp_data {
+	uint8_t opcode;
+	const char *str;
+	void (*func) (const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+};
+
+static const struct llcp_data llcp_table[] = {
+	{ 0x00, "LL_CONNECTION_UPDATE_REQ", conn_update_req,   11, true },
+	{ 0x01, "LL_CHANNEL_MAP_REQ",       channel_map_req,    7, true },
+	{ 0x02, "LL_TERMINATE_IND",         terminate_ind,      1, true },
+	{ 0x03, "LL_ENC_REQ",               enc_req,           22, true },
+	{ 0x04, "LL_ENC_RSP",               enc_rsp,           12, true },
+	{ 0x05, "LL_START_ENC_REQ",         null_pdu,           0, true },
+	{ 0x06, "LL_START_ENC_RSP",         null_pdu,           0, true },
+	{ 0x07, "LL_UNKNOWN_RSP",           unknown_rsp,        1, true },
+	{ 0x08, "LL_FEATURE_REQ",           feature_req,        8, true },
+	{ 0x09, "LL_FEATURE_RSP",           feature_rsp,        8, true },
+	{ 0x0a, "LL_PAUSE_ENC_REQ",         null_pdu,           0, true },
+	{ 0x0b, "LL_PAUSE_ENC_RSP",         null_pdu,           0, true },
+	{ 0x0c, "LL_VERSION_IND",           version_ind,        5, true },
+	{ 0x0d, "LL_REJECT_IND",            reject_ind,         1, true },
+	{ 0x0e, "LL_SLAVE_FEATURE_REQ",     slave_feature_req,  8, true },
+	{ 0x0f, "LL_CONNECTION_PARAM_REQ",  NULL,              23, true },
+	{ 0x10, "LL_CONNECTION_PARAM_RSP",  NULL,              23, true },
+	{ 0x11, "LL_REJECT_IND_EXT",        reject_ind_ext,     2, true },
+	{ 0x12, "LL_PING_REQ",              null_pdu,           0, true },
+	{ 0x13, "LL_PING_RSP",              null_pdu,           0, true },
+	{ 0x14, "LL_LENGTH_REQ",            NULL,               8, true },
+	{ 0x15, "LL_LENGTH_RSP",            NULL,               8, true },
+	{ }
+};
+
+static const char *opcode_to_string(uint8_t opcode)
+{
+	int i;
+
+	for (i = 0; llcp_table[i].str; i++) {
+		if (llcp_table[i].opcode == opcode)
+			return llcp_table[i].str;
+	}
+
+	return "Unknown";
+}
+
+void llcp_packet(const void *data, uint8_t size, bool padded)
+{
+	uint8_t opcode = ((const uint8_t *) data)[0];
+	const struct llcp_data *llcp_data = NULL;
+	const char *opcode_color, *opcode_str;
+	int i;
+
+	for (i = 0; llcp_table[i].str; i++) {
+		if (llcp_table[i].opcode == opcode) {
+			llcp_data = &llcp_table[i];
+			break;
+		}
+	}
+
+	if (llcp_data) {
+		if (llcp_data->func)
+			opcode_color = COLOR_OPCODE;
+		else
+			opcode_color = COLOR_OPCODE_UNKNOWN;
+		opcode_str = llcp_data->str;
+	} else {
+		opcode_color = COLOR_OPCODE_UNKNOWN;
+		opcode_str = "Unknown";
+	}
+
+	print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+						" (0x%2.2x)", opcode);
+
+	if (!llcp_data || !llcp_data->func) {
+		packet_hexdump(data + 1, size - 1);
+		return;
+	}
+
+	if (llcp_data->fixed && !padded) {
+		if (size - 1 != llcp_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	} else {
+		if (size - 1 < llcp_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + 1, size - 1);
+			return;
+		}
+	}
+
+	llcp_data->func(data + 1, size - 1);
+}
diff --git a/bluez/repo/monitor/ll.h b/bluez/repo/monitor/ll.h
new file mode 100644
index 0000000..98e0bf4
--- /dev/null
+++ b/bluez/repo/monitor/ll.h
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+void ll_packet(uint16_t frequency, const void *data, uint8_t size, bool padded);
+void llcp_packet(const void *data, uint8_t size, bool padded);
diff --git a/bluez/repo/monitor/lmp.c b/bluez/repo/monitor/lmp.c
new file mode 100644
index 0000000..e7e6b25
--- /dev/null
+++ b/bluez/repo/monitor/lmp.c
@@ -0,0 +1,933 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+
+#include "src/shared/util.h"
+#include "display.h"
+#include "packet.h"
+#include "bt.h"
+#include "lmp.h"
+
+#define COLOR_OPCODE		COLOR_MAGENTA
+#define COLOR_OPCODE_UNKNOWN	COLOR_WHITE_BG
+
+static const char *get_opcode_str(uint16_t opcode);
+
+static void print_opcode(uint16_t opcode)
+{
+	const char *str;
+
+	str = get_opcode_str(opcode);
+	if (!str)
+		str = "Unknown";
+
+	if (opcode & 0xff00)
+		print_field("Operation: %s (%u/%u)", str,
+						opcode >> 8, opcode & 0xff);
+	else
+		print_field("Operation: %s (%u)", str, opcode);
+}
+
+static void name_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_name_req *pdu = data;
+
+	print_field("Offset: %u", pdu->offset);
+}
+
+static void name_rsp(const void *data, uint8_t size)
+{
+	const struct bt_lmp_name_rsp *pdu = data;
+	char str[15];
+
+	memcpy(str, pdu->fragment, 14);
+	str[14] = '\0';
+
+	print_field("Offset: %u", pdu->offset);
+	print_field("Length: %u", pdu->length);
+	print_field("Fragment: %s", str);
+}
+
+static void accepted(const void *data, uint8_t size)
+{
+	const struct bt_lmp_accepted *pdu = data;
+
+	print_opcode(pdu->opcode);
+}
+
+static void not_accepted(const void *data, uint8_t size)
+{
+	const struct bt_lmp_not_accepted *pdu = data;
+
+	print_opcode(pdu->opcode);
+	packet_print_error("Error code", pdu->error);
+}
+
+static void clkoffset_req(const void *data, uint8_t size)
+{
+}
+
+static void clkoffset_rsp(const void *data, uint8_t size)
+{
+	const struct bt_lmp_clkoffset_rsp *pdu = data;
+
+	print_field("Clock offset: 0x%4.4x", le16_to_cpu(pdu->offset));
+}
+
+static void detach(const void *data, uint8_t size)
+{
+	const struct bt_lmp_detach *pdu = data;
+
+	packet_print_error("Error code", pdu->error);
+}
+
+static void au_rand(const void *data, uint8_t size)
+{
+	const struct bt_lmp_au_rand *pdu = data;
+
+	packet_hexdump(pdu->number, 16);
+}
+
+static void sres(const void *data, uint8_t size)
+{
+	const struct bt_lmp_sres *pdu = data;
+
+	packet_hexdump(pdu->response, 4);
+}
+
+static void encryption_mode_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_encryption_mode_req *pdu = data;
+	const char *str;
+
+	switch (pdu->mode) {
+	case 0x00:
+		str = "No encryption";
+		break;
+	case 0x01:
+		str = "Encryption";
+		break;
+	case 0x02:
+		str = "Encryption";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode: %s (%u)", str, pdu->mode);
+}
+
+static void encryption_key_size_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_encryption_key_size_req *pdu = data;
+
+	print_field("Key size: %u", pdu->key_size);
+}
+
+static void start_encryption_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_start_encryption_req *pdu = data;
+
+	packet_hexdump(pdu->number, 16);
+}
+
+static void stop_encryption_req(const void *data, uint8_t size)
+{
+}
+
+static void switch_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_switch_req *pdu = data;
+
+	print_field("Instant: 0x%8.8x", le32_to_cpu(pdu->instant));
+}
+
+static void unsniff_req(const void *data, uint8_t size)
+{
+}
+
+static void max_power(const void *data, uint8_t size)
+{
+}
+
+static void min_power(const void *data, uint8_t size)
+{
+}
+
+static void auto_rate(const void *data, uint8_t size)
+{
+}
+
+static void preferred_rate(const void *data, uint8_t size)
+{
+	const struct bt_lmp_preferred_rate *pdu = data;
+	const char *str;
+
+	str = (pdu->rate & 0x01) ? "do not use FEC" : "use FEC";
+
+	print_field("Basic data rate: %s (0x%02x)", str, pdu->rate & 0x01);
+
+	switch ((pdu->rate & 0x06) >> 1) {
+	case 0:
+		str = "No packet-size preference available";
+		break;
+	case 1:
+		str = "use 1-slot packets";
+		break;
+	case 2:
+		str = "use 3-slot packets";
+		break;
+	case 3:
+		str = "use 5-slot packets";
+		break;
+	}
+
+	print_field("Basic data rate: %s (0x%02x)", str, pdu->rate & 0x06);
+
+	switch ((pdu->rate & 0x11) >> 3) {
+	case 0:
+		str = "use DM1 packets";
+		break;
+	case 1:
+		str = "use 2 Mb/s packets";
+		break;
+	case 2:
+		str = "use 3 MB/s packets";
+		break;
+	case 3:
+		str = "reserved";
+		break;
+	}
+
+	print_field("Enhanced data rate: %s (0x%2.2x)", str, pdu->rate & 0x11);
+
+	switch ((pdu->rate & 0x60) >> 5) {
+	case 0:
+		str = "No packet-size preference available";
+		break;
+	case 1:
+		str = "use 1-slot packets";
+		break;
+	case 2:
+		str = "use 3-slot packets";
+		break;
+	case 3:
+		str = "use 5-slot packets";
+		break;
+	}
+
+	print_field("Enhanced data rate: %s (0x%2.2x)", str, pdu->rate & 0x60);
+}
+
+static void version_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_version_req *pdu = data;
+
+	packet_print_version("Version", pdu->version,
+				"Subversion", le16_to_cpu(pdu->subversion));
+	packet_print_company("Company", le16_to_cpu(pdu->company));
+}
+
+static void version_res(const void *data, uint8_t size)
+{
+	const struct bt_lmp_version_res *pdu = data;
+
+	packet_print_version("Version", pdu->version,
+				"Subversion", le16_to_cpu(pdu->subversion));
+	packet_print_company("Company", le16_to_cpu(pdu->company));
+}
+
+static void features_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_features_req *pdu = data;
+
+	packet_print_features_lmp(pdu->features, 0x00);
+}
+
+static void features_res(const void *data, uint8_t size)
+{
+	const struct bt_lmp_features_res *pdu = data;
+
+	packet_print_features_lmp(pdu->features, 0x00);
+}
+
+static void max_slot(const void *data, uint8_t size)
+{
+	const struct bt_lmp_max_slot *pdu = data;
+
+	print_field("Slots: 0x%4.4x", pdu->slots);
+}
+
+static void max_slot_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_max_slot_req *pdu = data;
+
+	print_field("Slots: 0x%4.4x", pdu->slots);
+}
+
+static void timing_accuracy_req(const void *data, uint8_t size)
+{
+}
+
+static void timing_accuracy_res(const void *data, uint8_t size)
+{
+	const struct bt_lmp_timing_accuracy_res *pdu = data;
+
+	print_field("Drift: %u ppm", pdu->drift);
+	print_field("Jitter: %u usec", pdu->jitter);
+}
+
+static void setup_complete(const void *data, uint8_t size)
+{
+}
+
+static void use_semi_permanent_key(const void *data, uint8_t size)
+{
+}
+
+static void host_connection_req(const void *data, uint8_t size)
+{
+}
+
+static void slot_offset(const void *data, uint8_t size)
+{
+	const struct bt_lmp_slot_offset *pdu = data;
+
+	print_field("Offset: %u usec", le16_to_cpu(pdu->offset));
+	packet_print_addr("Address", pdu->bdaddr, false);
+}
+
+static void page_scan_mode_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_page_scan_mode_req *pdu = data;
+	const char *str;
+
+	switch (pdu->scheme) {
+	case 0x00:
+		str = "Mandatory";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Paging scheme: %s (%u)", str, pdu->scheme);
+
+	if (pdu->scheme == 0x00) {
+		switch (pdu->settings) {
+		case 0x00:
+			str = "R0";
+			break;
+		case 0x01:
+			str = "R1";
+			break;
+		case 0x02:
+			str = "R2";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+	} else
+		str = "Reserved";
+
+	print_field("Paging scheme settings: %s (%u)", str, pdu->settings);
+}
+
+static void test_activate(const void *data, uint8_t size)
+{
+}
+
+static void encryption_key_size_mask_req(const void *data, uint8_t size)
+{
+}
+
+static void set_afh(const void *data, uint8_t size)
+{
+	const struct bt_lmp_set_afh *pdu = data;
+	const char *str;
+
+	print_field("Instant: %u", le32_to_cpu(pdu->instant));
+
+	switch (pdu->mode) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode: %s (0x%2.2x)", str, pdu->mode);
+	packet_print_channel_map_lmp(pdu->map);
+}
+
+static void encapsulated_header(const void *data, uint8_t size)
+{
+	const struct bt_lmp_encapsulated_header *pdu = data;
+	const char *str;
+
+	print_field("Major type: %u", pdu->major);
+	print_field("Minor type: %u", pdu->minor);
+
+	if (pdu->major == 0x01) {
+		switch (pdu->minor) {
+		case 0x01:
+			str = "P-192 Public Key";
+			break;
+		case 0x02:
+			str = "P-256 Public Key";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("  %s", str);
+	}
+
+	print_field("Length: %u", pdu->length);
+}
+
+static void encapsulated_payload(const void *data, uint8_t size)
+{
+	const struct bt_lmp_encapsulated_payload *pdu = data;
+
+	packet_hexdump(pdu->data, 16);
+}
+
+static void simple_pairing_confirm(const void *data, uint8_t size)
+{
+	const struct bt_lmp_simple_pairing_confirm *pdu = data;
+
+	packet_hexdump(pdu->value, 16);
+}
+
+static void simple_pairing_number(const void *data, uint8_t size)
+{
+	const struct bt_lmp_simple_pairing_number *pdu = data;
+
+	packet_hexdump(pdu->value, 16);
+}
+
+static void dhkey_check(const void *data, uint8_t size)
+{
+	const struct bt_lmp_dhkey_check *pdu = data;
+
+	packet_hexdump(pdu->value, 16);
+}
+
+static void accepted_ext(const void *data, uint8_t size)
+{
+	const struct bt_lmp_accepted_ext *pdu = data;
+	uint16_t opcode;
+
+	switch (pdu->escape) {
+	case 127:
+		opcode = LMP_ESC4(pdu->opcode);
+		break;
+	default:
+		return;
+	}
+
+	print_opcode(opcode);
+}
+
+static void not_accepted_ext(const void *data, uint8_t size)
+{
+	const struct bt_lmp_not_accepted_ext *pdu = data;
+	uint16_t opcode;
+
+	switch (pdu->escape) {
+	case 127:
+		opcode = LMP_ESC4(pdu->opcode);
+		break;
+	default:
+		return;
+	}
+
+	print_opcode(opcode);
+	print_field("Error code: %u", pdu->error);
+}
+
+static void features_req_ext(const void *data, uint8_t size)
+{
+	const struct bt_lmp_features_req_ext *pdu = data;
+
+	print_field("Features page: %u", pdu->page);
+	print_field("Max supported page: %u", pdu->max_page);
+	packet_print_features_lmp(pdu->features, pdu->page);
+}
+
+static void features_res_ext(const void *data, uint8_t size)
+{
+	const struct bt_lmp_features_res_ext *pdu = data;
+
+	print_field("Features page: %u", pdu->page);
+	print_field("Max supported page: %u", pdu->max_page);
+	packet_print_features_lmp(pdu->features, pdu->page);
+}
+
+static void packet_type_table_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_packet_type_table_req *pdu = data;
+	const char *str;
+
+	switch (pdu->table) {
+	case 0x00:
+		str = "1 Mbps only";
+		break;
+	case 0x01:
+		str = "2/3 Mbps";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Table: %s (0x%2.2x)", str, pdu->table);
+}
+
+static void channel_classification_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_channel_classification_req *pdu = data;
+	const char *str;
+
+	switch (pdu->mode) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reporting mode: %s (0x%2.2x)", str, pdu->mode);
+	print_field("Min interval: 0x%2.2x", pdu->min_interval);
+	print_field("Max interval: 0x%2.2x", pdu->max_interval);
+}
+
+static void channel_classification(const void *data, uint8_t size)
+{
+	const struct bt_lmp_channel_classification *pdu = data;
+	char str[21];
+	int i;
+
+	for (i = 0; i < 10; i++)
+		sprintf(str + (i * 2), "%2.2x", pdu->classification[i]);
+
+	print_field("Classification: 0x%s", str);
+}
+
+static void pause_encryption_req(const void *data, uint8_t size)
+{
+}
+
+static void resume_encryption_req(const void *data, uint8_t size)
+{
+}
+
+static void io_capability_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_io_capability_req *pdu = data;
+	const char *str;
+
+	packet_print_io_capability(pdu->capability);
+
+	switch (pdu->oob_data) {
+	case 0x00:
+		str = "No authentication data received";
+		break;
+	case 0x01:
+		str = "Authentication data received";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data);
+
+	packet_print_io_authentication(pdu->authentication);
+}
+
+static void io_capability_res(const void *data, uint8_t size)
+{
+	const struct bt_lmp_io_capability_res *pdu = data;
+	const char *str;
+
+	packet_print_io_capability(pdu->capability);
+
+	switch (pdu->oob_data) {
+	case 0x00:
+		str = "No authentication data received";
+		break;
+	case 0x01:
+		str = "Authentication data received";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data);
+
+	packet_print_io_authentication(pdu->authentication);
+}
+
+static void numeric_comparison_failed(const void *data, uint8_t size)
+{
+}
+
+static void passkey_failed(const void *data, uint8_t size)
+{
+}
+
+static void oob_failed(const void *data, uint8_t size)
+{
+}
+
+static void power_control_req(const void *data, uint8_t size)
+{
+	const struct bt_lmp_power_control_req *pdu = data;
+	const char *str;
+
+	switch (pdu->request) {
+	case 0x00:
+		str = "Decrement power one step";
+		break;
+	case 0x01:
+		str = "Increment power one step";
+		break;
+	case 0x02:
+		str = "Increase to maximum power";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Request: %s (0x%2.2x)", str, pdu->request);
+}
+
+static void power_control_res(const void *data, uint8_t size)
+{
+	const struct bt_lmp_power_control_res *pdu = data;
+	const char *str;
+
+	print_field("Response: 0x%2.2x", pdu->response);
+
+	switch (pdu->response & 0x03) {
+	case 0x00:
+		str = "Not supported";
+		break;
+	case 0x01:
+		str = "Changed one step";
+		break;
+	case 0x02:
+		str = "Max power";
+		break;
+	case 0x03:
+		str = "Min power";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("  GFSK: %s", str);
+
+	switch ((pdu->response & 0x0c) >> 2) {
+	case 0x00:
+		str = "Not supported";
+		break;
+	case 0x01:
+		str = "Changed one step";
+		break;
+	case 0x02:
+		str = "Max power";
+		break;
+	case 0x03:
+		str = "Min power";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("  DQPSK: %s", str);
+
+	switch ((pdu->response & 0x30) >> 4) {
+	case 0x00:
+		str = "Not supported";
+		break;
+	case 0x01:
+		str = "Changed one step";
+		break;
+	case 0x02:
+		str = "Max power";
+		break;
+	case 0x03:
+		str = "Min power";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("  8DPSK: %s", str);
+}
+
+static void ping_req(const void *data, uint8_t size)
+{
+}
+
+static void ping_res(const void *data, uint8_t size)
+{
+}
+
+struct lmp_data {
+	uint16_t opcode;
+	const char *str;
+	void (*func) (const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+};
+
+static const struct lmp_data lmp_table[] = {
+	{  1, "LMP_name_req", name_req, 1, true },
+	{  2, "LMP_name_res", name_rsp, 16, true },
+	{  3, "LMP_accepted", accepted, 1, true },
+	{  4, "LMP_not_accepted", not_accepted, 2, true },
+	{  5, "LMP_clkoffset_req", clkoffset_req, 0, true },
+	{  6, "LMP_clkoffset_res", clkoffset_rsp, 2, true },
+	{  7, "LMP_detach", detach, 1, true },
+	{  8, "LMP_in_rand" },
+	{  9, "LMP_comb_key" },
+	{ 10, "LMP_unit_key" },
+	{ 11, "LMP_au_rand", au_rand, 16, true },
+	{ 12, "LMP_sres", sres, 4, true },
+	{ 13, "LMP_temp_rand" },
+	{ 14, "LMP_temp_key" },
+	{ 15, "LMP_encryption_mode_req", encryption_mode_req, 1, true },
+	{ 16, "LMP_encryption_key_size_req", encryption_key_size_req, 1, true },
+	{ 17, "LMP_start_encryption_req", start_encryption_req, 16, true },
+	{ 18, "LMP_stop_encryption_req", stop_encryption_req, 0, true },
+	{ 19, "LMP_switch_req", switch_req, 4, true },
+	{ 20, "LMP_hold" },
+	{ 21, "LMP_hold_req" },
+	{ 22, "LMP_sniff" },
+	{ 23, "LMP_sniff_req" },
+	{ 24, "LMP_unsniff_req", unsniff_req, 0, true },
+	{ 25, "LMP_park_req" },
+	{ 26, "LMP_park" },
+	{ 27, "LMP_set_broadcast_scan_window" },
+	{ 28, "LMP_modify_beacon" },
+	{ 29, "LMP_unpark_BD_ADDR_req" },
+	{ 30, "LMP_unpark_PM_ADDR_req" },
+	{ 31, "LMP_incr_power_req" },
+	{ 32, "LMP_decr_power_req" },
+	{ 33, "LMP_max_power", max_power, 0, true },
+	{ 34, "LMP_min_power", min_power, 0, true },
+	{ 35, "LMP_auto_rate", auto_rate, 0, true },
+	{ 36, "LMP_preferred_rate", preferred_rate, 1, true },
+	{ 37, "LMP_version_req", version_req, 5, true },
+	{ 38, "LMP_version_res", version_res, 5, true },
+	{ 39, "LMP_features_req", features_req, 8, true },
+	{ 40, "LMP_features_res", features_res, 8, true },
+	{ 41, "LMP_quality_of_service" },
+	{ 42, "LMP_quality_of_service_req" },
+	{ 43, "LMP_SCO_link_req" },
+	{ 44, "LMP_remove_SCO_link_req" },
+	{ 45, "LMP_max_slot", max_slot, 1, true },
+	{ 46, "LMP_max_slot_req", max_slot_req, 1, true },
+	{ 47, "LMP_timing_accuracy_req", timing_accuracy_req, 0, true },
+	{ 48, "LMP_timing_accuracy_res", timing_accuracy_res, 2, true },
+	{ 49, "LMP_setup_complete", setup_complete, 0, true },
+	{ 50, "LMP_use_semi_permanent_key", use_semi_permanent_key, 0, true },
+	{ 51, "LMP_host_connection_req", host_connection_req, 0, true },
+	{ 52, "LMP_slot_offset", slot_offset, 8, true },
+	{ 53, "LMP_page_mode_req" },
+	{ 54, "LMP_page_scan_mode_req", page_scan_mode_req, 2, true },
+	{ 55, "LMP_supervision_timeout" },
+	{ 56, "LMP_test_activate", test_activate, 0, true },
+	{ 57, "LMP_test_control" },
+	{ 58, "LMP_encryption_key_size_mask_req", encryption_key_size_mask_req, 0, true },
+	{ 59, "LMP_encryption_key_size_mask_res" },
+	{ 60, "LMP_set_AFH", set_afh, 15, true },
+	{ 61, "LMP_encapsulated_header", encapsulated_header, 3, true },
+	{ 62, "LMP_encapsulated_payload", encapsulated_payload, 16, true },
+	{ 63, "LMP_simple_pairing_confirm", simple_pairing_confirm, 16, true },
+	{ 64, "LMP_simple_pairing_number", simple_pairing_number, 16, true },
+	{ 65, "LMP_DHkey_check", dhkey_check, 16, true },
+	{ 66, "LMP_pause_encryption_aes_req" },
+	{ LMP_ESC4(1),  "LMP_accepted_ext", accepted_ext, 2, true },
+	{ LMP_ESC4(2),  "LMP_not_accepted_ext", not_accepted_ext, 3, true },
+	{ LMP_ESC4(3),  "LMP_features_req_ext", features_req_ext, 10, true },
+	{ LMP_ESC4(4),  "LMP_features_res_ext", features_res_ext, 10, true },
+	{ LMP_ESC4(5),  "LMP_clk_adj" },
+	{ LMP_ESC4(6),  "LMP_clk_adj_ack" },
+	{ LMP_ESC4(7),  "LMP_clk_adj_req" },
+	{ LMP_ESC4(11), "LMP_packet_type_table_req", packet_type_table_req, 1, true },
+	{ LMP_ESC4(12), "LMP_eSCO_link_req" },
+	{ LMP_ESC4(13), "LMP_remove_eSCO_link_req" },
+	{ LMP_ESC4(16), "LMP_channel_classification_req", channel_classification_req, 5, true },
+	{ LMP_ESC4(17), "LMP_channel_classification", channel_classification, 10, true },
+	{ LMP_ESC4(21), "LMP_sniff_subrating_req" },
+	{ LMP_ESC4(22), "LMP_sniff_subrating_res" },
+	{ LMP_ESC4(23), "LMP_pause_encryption_req", pause_encryption_req, 0, true },
+	{ LMP_ESC4(24), "LMP_resume_encryption_req", resume_encryption_req, 0, true },
+	{ LMP_ESC4(25), "LMP_IO_capability_req", io_capability_req, 3, true },
+	{ LMP_ESC4(26), "LMP_IO_capability_res", io_capability_res, 3, true },
+	{ LMP_ESC4(27), "LMP_numeric_comparison_failed", numeric_comparison_failed, 0, true },
+	{ LMP_ESC4(28), "LMP_passkey_failed", passkey_failed, 0, true },
+	{ LMP_ESC4(29), "LMP_oob_failed", oob_failed, 0, true },
+	{ LMP_ESC4(30), "LMP_keypress_notification" },
+	{ LMP_ESC4(31), "LMP_power_control_req", power_control_req, 1, true },
+	{ 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 },
+	{ }
+};
+
+static const char *get_opcode_str(uint16_t opcode)
+{
+	int i;
+
+	for (i = 0; lmp_table[i].str; i++) {
+		if (lmp_table[i].opcode == opcode)
+			return lmp_table[i].str;
+	}
+
+	return NULL;
+}
+
+void lmp_packet(const void *data, uint8_t size, bool padded)
+{
+	const struct lmp_data *lmp_data = NULL;
+	const char *opcode_color, *opcode_str;
+	uint16_t opcode;
+	uint8_t tid, off;
+	const char *tid_str;
+	int i;
+
+	tid = ((const uint8_t *) data)[0] & 0x01;
+	opcode = (((const uint8_t *) data)[0] & 0xfe) >> 1;
+
+	tid_str = tid == 0x00 ? "Master" : "Slave";
+
+	switch (opcode) {
+	case 127:
+		if (size < 2) {
+			print_text(COLOR_ERROR, "extended opcode too short");
+			packet_hexdump(data, size);
+			return;
+		}
+		opcode = LMP_ESC4(((const uint8_t *) data)[1]);
+		off = 2;
+		break;
+	case 126:
+	case 125:
+	case 124:
+		return;
+	default:
+		off = 1;
+		break;
+	}
+
+	for (i = 0; lmp_table[i].str; i++) {
+		if (lmp_table[i].opcode == opcode) {
+			lmp_data = &lmp_table[i];
+			break;
+		}
+	}
+
+	if (lmp_data) {
+		if (lmp_data->func)
+			opcode_color = COLOR_OPCODE;
+		else
+			opcode_color = COLOR_OPCODE_UNKNOWN;
+		opcode_str = lmp_data->str;
+	} else {
+		opcode_color = COLOR_OPCODE_UNKNOWN;
+		opcode_str = "Unknown";
+	}
+
+	if (opcode & 0xff00)
+		print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+				" (%u/%u) %s transaction (%u)",
+				opcode >> 8, opcode & 0xff, tid_str, tid);
+	else
+		print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+				" (%u) %s transaction (%d)",
+				opcode, tid_str, tid);
+
+	if (!lmp_data || !lmp_data->func) {
+		packet_hexdump(data + off, size - off);
+		return;
+	}
+
+	if (lmp_data->fixed && !padded) {
+		if (size - off != lmp_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data + off, size - off);
+			return;
+		}
+	} else {
+		if (size - off < lmp_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + off, size - off);
+			return;
+		}
+	}
+
+	lmp_data->func(data + off, size - off);
+}
+
+void lmp_todo(void)
+{
+	int i;
+
+	printf("LMP operations with missing decodings:\n");
+
+	for (i = 0; lmp_table[i].str; i++) {
+		if (lmp_table[i].func)
+			continue;
+
+		printf("\t%s\n", lmp_table[i].str);
+	}
+}
diff --git a/bluez/repo/monitor/lmp.h b/bluez/repo/monitor/lmp.h
new file mode 100644
index 0000000..9564c77
--- /dev/null
+++ b/bluez/repo/monitor/lmp.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+void lmp_packet(const void *data, uint8_t size, bool padded);
+
+void lmp_todo(void);
diff --git a/bluez/repo/monitor/main.c b/bluez/repo/monitor/main.c
new file mode 100644
index 0000000..5f6be79
--- /dev/null
+++ b/bluez/repo/monitor/main.c
@@ -0,0 +1,223 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "src/shared/mainloop.h"
+
+#include "packet.h"
+#include "lmp.h"
+#include "keys.h"
+#include "analyze.h"
+#include "ellisys.h"
+#include "control.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("btmon - Bluetooth monitor\n"
+		"Usage:\n");
+	printf("\tbtmon [options]\n");
+	printf("options:\n"
+		"\t-r, --read <file>      Read traces in btsnoop format\n"
+		"\t-w, --write <file>     Save traces in btsnoop format\n"
+		"\t-a, --analyze <file>   Analyze traces in btsnoop format\n"
+		"\t-s, --server <socket>  Start monitor server socket\n"
+		"\t-p, --priority <level> Show only priority or lower\n"
+		"\t-i, --index <num>      Show only specified controller\n"
+		"\t-t, --time             Show time instead of time offset\n"
+		"\t-T, --date             Show time and date information\n"
+		"\t-S, --sco              Dump SCO traffic\n"
+		"\t-E, --ellisys [ip]     Send Ellisys HCI Injection\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "read",    required_argument, NULL, 'r' },
+	{ "write",   required_argument, NULL, 'w' },
+	{ "analyze", required_argument, NULL, 'a' },
+	{ "server",  required_argument, NULL, 's' },
+	{ "priority",required_argument, NULL, 'p' },
+	{ "index",   required_argument, NULL, 'i' },
+	{ "time",    no_argument,       NULL, 't' },
+	{ "date",    no_argument,       NULL, 'T' },
+	{ "sco",     no_argument,	NULL, 'S' },
+	{ "ellisys", required_argument, NULL, 'E' },
+	{ "todo",    no_argument,       NULL, '#' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	unsigned long filter_mask = 0;
+	const char *reader_path = NULL;
+	const char *writer_path = NULL;
+	const char *analyze_path = NULL;
+	const char *ellisys_server = NULL;
+	unsigned short ellisys_port = 0;
+	const char *str;
+	int exit_status;
+	sigset_t mask;
+
+	mainloop_init();
+
+	filter_mask |= PACKET_FILTER_SHOW_TIME_OFFSET;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "r:w:a:s:p:i:tTSE:vh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'r':
+			reader_path = optarg;
+			break;
+		case 'w':
+			writer_path = optarg;
+			break;
+		case 'a':
+			analyze_path = optarg;
+			break;
+		case 's':
+			control_server(optarg);
+			break;
+		case 'p':
+			packet_set_priority(optarg);
+			break;
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				usage();
+				return EXIT_FAILURE;
+			}
+			packet_select_index(atoi(str));
+			break;
+		case 't':
+			filter_mask &= ~PACKET_FILTER_SHOW_TIME_OFFSET;
+			filter_mask |= PACKET_FILTER_SHOW_TIME;
+			break;
+		case 'T':
+			filter_mask &= ~PACKET_FILTER_SHOW_TIME_OFFSET;
+			filter_mask |= PACKET_FILTER_SHOW_TIME;
+			filter_mask |= PACKET_FILTER_SHOW_DATE;
+			break;
+		case 'S':
+			filter_mask |= PACKET_FILTER_SHOW_SCO_DATA;
+			break;
+		case 'E':
+			ellisys_server = optarg;
+			ellisys_port = 24352;
+			break;
+		case '#':
+			packet_todo();
+			lmp_todo();
+			return EXIT_SUCCESS;
+		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;
+	}
+
+	if (reader_path && analyze_path) {
+		fprintf(stderr, "Display and analyze can't be combined\n");
+		return EXIT_FAILURE;
+	}
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Bluetooth monitor ver %s\n", VERSION);
+
+	keys_setup();
+
+	packet_set_filter(filter_mask);
+
+	if (analyze_path) {
+		analyze_trace(analyze_path);
+		return EXIT_SUCCESS;
+	}
+
+	if (reader_path) {
+		if (ellisys_server)
+			ellisys_enable(ellisys_server, ellisys_port);
+
+		control_reader(reader_path);
+		return EXIT_SUCCESS;
+	}
+
+	if (writer_path && !control_writer(writer_path)) {
+		printf("Failed to open '%s'\n", writer_path);
+		return EXIT_FAILURE;
+	}
+
+	if (ellisys_server)
+		ellisys_enable(ellisys_server, ellisys_port);
+
+	if (control_tracing() < 0)
+		return EXIT_FAILURE;
+
+	exit_status = mainloop_run();
+
+	keys_cleanup();
+
+	return exit_status;
+}
diff --git a/bluez/repo/monitor/packet.c b/bluez/repo/monitor/packet.c
new file mode 100644
index 0000000..322bba6
--- /dev/null
+++ b/bluez/repo/monitor/packet.c
@@ -0,0 +1,9075 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/shared/util.h"
+#include "src/shared/btsnoop.h"
+#include "display.h"
+#include "bt.h"
+#include "ll.h"
+#include "hwdb.h"
+#include "keys.h"
+#include "uuid.h"
+#include "l2cap.h"
+#include "control.h"
+#include "vendor.h"
+#include "intel.h"
+#include "broadcom.h"
+#include "packet.h"
+
+#define COLOR_INDEX_LABEL		COLOR_WHITE
+#define COLOR_TIMESTAMP			COLOR_YELLOW
+
+#define COLOR_NEW_INDEX			COLOR_GREEN
+#define COLOR_DEL_INDEX			COLOR_RED
+#define COLOR_OPEN_INDEX		COLOR_GREEN
+#define COLOR_CLOSE_INDEX		COLOR_RED
+#define COLOR_INDEX_INFO		COLOR_GREEN
+#define COLOR_VENDOR_DIAG		COLOR_YELLOW
+
+#define COLOR_HCI_COMMAND		COLOR_BLUE
+#define COLOR_HCI_COMMAND_UNKNOWN	COLOR_WHITE_BG
+
+#define COLOR_HCI_EVENT			COLOR_MAGENTA
+#define COLOR_HCI_EVENT_UNKNOWN		COLOR_WHITE_BG
+
+#define COLOR_HCI_ACLDATA		COLOR_CYAN
+#define COLOR_HCI_SCODATA		COLOR_YELLOW
+
+#define COLOR_UNKNOWN_ERROR		COLOR_WHITE_BG
+#define COLOR_UNKNOWN_FEATURE_BIT	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_COMMAND_BIT	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_EVENT_MASK	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_LE_STATES		COLOR_WHITE_BG
+#define COLOR_UNKNOWN_SERVICE_CLASS	COLOR_WHITE_BG
+#define COLOR_UNKNOWN_PKT_TYPE_BIT	COLOR_WHITE_BG
+
+#define COLOR_PHY_PACKET		COLOR_BLUE
+
+static time_t time_offset = ((time_t) -1);
+static int priority_level = BTSNOOP_PRIORITY_INFO;
+static unsigned long filter_mask = 0;
+static bool index_filter = false;
+static uint16_t index_number = 0;
+static uint16_t index_current = 0;
+
+#define UNKNOWN_MANUFACTURER 0xffff
+
+#define MAX_CONN 16
+
+struct conn_data {
+	uint16_t handle;
+	uint8_t  type;
+};
+
+static struct conn_data conn_list[MAX_CONN];
+
+static void assign_handle(uint16_t handle, uint8_t type)
+{
+	int i;
+
+	for (i = 0; i < MAX_CONN; i++) {
+		if (conn_list[i].handle == 0x0000) {
+			conn_list[i].handle = handle;
+			conn_list[i].type = type;
+			break;
+		}
+	}
+}
+
+static void release_handle(uint16_t handle)
+{
+	int i;
+
+	for (i = 0; i < MAX_CONN; i++) {
+		if (conn_list[i].handle == handle) {
+			conn_list[i].handle = 0x0000;
+			conn_list[i].type = 0x00;
+			break;
+		}
+	}
+}
+
+static uint8_t get_type(uint16_t handle)
+{
+	int i;
+
+	for (i = 0; i < MAX_CONN; i++) {
+		if (conn_list[i].handle == handle)
+			return conn_list[i].type;
+	}
+
+	return 0xff;
+}
+
+void packet_set_filter(unsigned long filter)
+{
+	filter_mask = filter;
+}
+
+void packet_add_filter(unsigned long filter)
+{
+	if (index_filter)
+		filter &= ~PACKET_FILTER_SHOW_INDEX;
+
+	filter_mask |= filter;
+}
+
+void packet_del_filter(unsigned long filter)
+{
+	filter_mask &= ~filter;
+}
+
+void packet_set_priority(const char *priority)
+{
+	if (!priority)
+		return;
+
+	if (!strcasecmp(priority, "debug"))
+		priority_level = BTSNOOP_PRIORITY_DEBUG;
+	else
+		priority_level = atoi(priority);
+}
+
+void packet_select_index(uint16_t index)
+{
+	filter_mask &= ~PACKET_FILTER_SHOW_INDEX;
+
+	index_filter = true;
+	index_number = index;
+}
+
+#define print_space(x) printf("%*c", (x), ' ');
+
+static void print_packet(struct timeval *tv, struct ucred *cred,
+					uint16_t index, char ident,
+					const char *color, const char *label,
+					const char *text, const char *extra)
+{
+	int col = num_columns();
+	char line[256], ts_str[64];
+	int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
+
+	if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
+					index != HCI_DEV_NONE) {
+		if (use_color()) {
+			n = sprintf(ts_str + ts_pos, "%s", COLOR_INDEX_LABEL);
+			if (n > 0)
+				ts_pos += n;
+		}
+
+		n = sprintf(ts_str + ts_pos, " [hci%d]", index);
+		if (n > 0) {
+			ts_pos += n;
+			ts_len += n;
+		}
+	}
+
+	if (tv) {
+		time_t t = tv->tv_sec;
+		struct tm tm;
+
+		localtime_r(&t, &tm);
+
+		if (use_color()) {
+			n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
+			if (n > 0)
+				ts_pos += n;
+		}
+
+		if (filter_mask & PACKET_FILTER_SHOW_DATE) {
+			n = sprintf(ts_str + ts_pos, " %04d-%02d-%02d",
+				tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+			if (n > 0) {
+				ts_pos += n;
+				ts_len += n;
+			}
+		}
+
+		if (filter_mask & PACKET_FILTER_SHOW_TIME) {
+			n = sprintf(ts_str + ts_pos, " %02d:%02d:%02d.%06lu",
+				tm.tm_hour, tm.tm_min, tm.tm_sec, tv->tv_usec);
+			if (n > 0) {
+				ts_pos += n;
+				ts_len += n;
+			}
+		}
+
+		if (filter_mask & PACKET_FILTER_SHOW_TIME_OFFSET) {
+			n = sprintf(ts_str + ts_pos, " %lu.%06lu",
+					tv->tv_sec - time_offset, tv->tv_usec);
+			if (n > 0) {
+				ts_pos += n;
+				ts_len += n;
+			}
+		}
+	}
+
+	if (use_color()) {
+		n = sprintf(ts_str + ts_pos, "%s", COLOR_OFF);
+		if (n > 0)
+			ts_pos += n;
+	}
+
+	if (use_color()) {
+		n = sprintf(line + pos, "%s", color);
+		if (n > 0)
+			pos += n;
+	}
+
+	n = sprintf(line + pos, "%c %s", ident, label ? label : "");
+	if (n > 0) {
+		pos += n;
+		len += n;
+	}
+
+	if (text) {
+		int extra_len = extra ? strlen(extra) : 0;
+		int max_len = col - len - extra_len - ts_len - 3;
+
+		n = snprintf(line + pos, max_len + 1, "%s%s",
+						label ? ": " : "", text);
+		if (n > max_len) {
+			line[pos + max_len - 1] = '.';
+			line[pos + max_len - 2] = '.';
+			if (line[pos + max_len - 3] == ' ')
+				line[pos + max_len - 3] = '.';
+
+			n = max_len;
+		}
+
+		if (n > 0) {
+			pos += n;
+			len += n;
+		}
+	}
+
+	if (use_color()) {
+		n = sprintf(line + pos, "%s", COLOR_OFF);
+		if (n > 0)
+			pos += n;
+	}
+
+	if (extra) {
+		n = sprintf(line + pos, " %s", extra);
+		if (n > 0) {
+			pos += n;
+			len += n;
+		}
+	}
+
+	if (ts_len > 0) {
+		printf("%s", line);
+		if (len < col)
+			print_space(col - len - ts_len - 1);
+		printf("%s%s\n", use_color() ? COLOR_TIMESTAMP : "", ts_str);
+	} else
+		printf("%s\n", line);
+}
+
+static const struct {
+	uint8_t error;
+	const char *str;
+} error2str_table[] = {
+	{ 0x00, "Success"						},
+	{ 0x01, "Unknown HCI Command"					},
+	{ 0x02, "Unknown Connection Identifier"				},
+	{ 0x03, "Hardware Failure"					},
+	{ 0x04, "Page Timeout"						},
+	{ 0x05, "Authentication Failure"				},
+	{ 0x06, "PIN or Key Missing"					},
+	{ 0x07, "Memory Capacity Exceeded"				},
+	{ 0x08, "Connection Timeout"					},
+	{ 0x09, "Connection Limit Exceeded"				},
+	{ 0x0a, "Synchronous Connection Limit to a Device Exceeded"	},
+	{ 0x0b, "ACL Connection Already Exists"				},
+	{ 0x0c, "Command Disallowed"					},
+	{ 0x0d, "Connection Rejected due to Limited Resources"		},
+	{ 0x0e, "Connection Rejected due to Security Reasons"		},
+	{ 0x0f, "Connection Rejected due to Unacceptable BD_ADDR"	},
+	{ 0x10, "Connection Accept Timeout Exceeded"			},
+	{ 0x11, "Unsupported Feature or Parameter Value"		},
+	{ 0x12, "Invalid HCI Command Parameters"			},
+	{ 0x13, "Remote User Terminated Connection"			},
+	{ 0x14, "Remote Device Terminated due to Low Resources"		},
+	{ 0x15, "Remote Device Terminated due to Power Off"		},
+	{ 0x16, "Connection Terminated By Local Host"			},
+	{ 0x17, "Repeated Attempts"					},
+	{ 0x18, "Pairing Not Allowed"					},
+	{ 0x19, "Unknown LMP PDU"					},
+	{ 0x1a, "Unsupported Remote Feature / Unsupported LMP Feature"	},
+	{ 0x1b, "SCO Offset Rejected"					},
+	{ 0x1c, "SCO Interval Rejected"					},
+	{ 0x1d, "SCO Air Mode Rejected"					},
+	{ 0x1e, "Invalid LMP Parameters / Invalid LL Parameters"	},
+	{ 0x1f, "Unspecified Error"					},
+	{ 0x20, "Unsupported LMP Parameter Value / "
+		"Unsupported LL Parameter Value"			},
+	{ 0x21, "Role Change Not Allowed"				},
+	{ 0x22, "LMP Response Timeout / LL Response Timeout"		},
+	{ 0x23, "LMP Error Transaction Collision"			},
+	{ 0x24, "LMP PDU Not Allowed"					},
+	{ 0x25, "Encryption Mode Not Acceptable"			},
+	{ 0x26, "Link Key cannot be Changed"				},
+	{ 0x27, "Requested QoS Not Supported"				},
+	{ 0x28, "Instant Passed"					},
+	{ 0x29, "Pairing With Unit Key Not Supported"			},
+	{ 0x2a, "Different Transaction Collision"			},
+	{ 0x2b, "Reserved"						},
+	{ 0x2c, "QoS Unacceptable Parameter"				},
+	{ 0x2d, "QoS Rejected"						},
+	{ 0x2e, "Channel Classification Not Supported"			},
+	{ 0x2f, "Insufficient Security"					},
+	{ 0x30, "Parameter Out Of Manadatory Range"			},
+	{ 0x31, "Reserved"						},
+	{ 0x32, "Role Switch Pending"					},
+	{ 0x33, "Reserved"						},
+	{ 0x34, "Reserved Slot Violation"				},
+	{ 0x35, "Role Switch Failed"					},
+	{ 0x36, "Extended Inquiry Response Too Large"			},
+	{ 0x37, "Secure Simple Pairing Not Supported By Host"		},
+	{ 0x38, "Host Busy - Pairing"					},
+	{ 0x39, "Connection Rejected due to No Suitable Channel Found"	},
+	{ 0x3a, "Controller Busy"					},
+	{ 0x3b, "Unacceptable Connection Parameters"			},
+	{ 0x3c, "Directed 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"		},
+	{ }
+};
+
+static void print_error(const char *label, uint8_t error)
+{
+	const char *str = "Unknown";
+	const char *color_on, *color_off;
+	bool unknown = true;
+	int i;
+
+	for (i = 0; error2str_table[i].str; i++) {
+		if (error2str_table[i].error == error) {
+			str = error2str_table[i].str;
+			unknown = false;
+			break;
+		}
+	}
+
+	if (use_color()) {
+		if (error) {
+			if (unknown)
+				color_on = COLOR_UNKNOWN_ERROR;
+			else
+				color_on = COLOR_RED;
+		} else
+			color_on = COLOR_GREEN;
+		color_off = COLOR_OFF;
+	} else {
+		color_on = "";
+		color_off = "";
+	}
+
+	print_field("%s: %s%s%s (0x%2.2x)", label,
+				color_on, str, color_off, error);
+}
+
+static void print_status(uint8_t status)
+{
+	print_error("Status", status);
+}
+
+static void print_reason(uint8_t reason)
+{
+	print_error("Reason", reason);
+}
+
+void packet_print_error(const char *label, uint8_t error)
+{
+	print_error(label, error);
+}
+
+static void print_addr_type(const char *label, uint8_t addr_type)
+{
+	const char *str;
+
+	switch (addr_type) {
+	case 0x00:
+		str = "Public";
+		break;
+	case 0x01:
+		str = "Random";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s: %s (0x%2.2x)", label, str, addr_type);
+}
+
+static void print_own_addr_type(uint8_t addr_type)
+{
+	const char *str;
+
+	switch (addr_type) {
+	case 0x00:
+	case 0x02:
+		str = "Public";
+		break;
+	case 0x01:
+	case 0x03:
+		str = "Random";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Own address type: %s (0x%2.2x)", str, addr_type);
+}
+
+static void print_peer_addr_type(const char *label, uint8_t addr_type)
+{
+	const char *str;
+
+	switch (addr_type) {
+	case 0x00:
+		str = "Public";
+		break;
+	case 0x01:
+		str = "Random";
+		break;
+	case 0x02:
+		str = "Resolved Public";
+		break;
+	case 0x03:
+		str = "Resolved Random";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s: %s (0x%2.2x)", label, str, addr_type);
+}
+
+static void print_addr_resolve(const char *label, const uint8_t *addr,
+					uint8_t addr_type, bool resolve)
+{
+	const char *str;
+	char *company;
+
+	switch (addr_type) {
+	case 0x00:
+	case 0x02:
+		if (!hwdb_get_company(addr, &company))
+			company = NULL;
+
+		if (company) {
+			print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
+					" (%s)", label, addr[5], addr[4],
+							addr[3], addr[2],
+							addr[1], addr[0],
+							company);
+			free(company);
+		} else {
+			print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
+					" (OUI %2.2X-%2.2X-%2.2X)", label,
+						addr[5], addr[4], addr[3],
+						addr[2], addr[1], addr[0],
+						addr[5], addr[4], addr[3]);
+		}
+		break;
+	case 0x01:
+	case 0x03:
+		switch ((addr[5] & 0xc0) >> 6) {
+		case 0x00:
+			str = "Non-Resolvable";
+			break;
+		case 0x01:
+			str = "Resolvable";
+			break;
+		case 0x03:
+			str = "Static";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)",
+					label, addr[5], addr[4], addr[3],
+					addr[2], addr[1], addr[0], str);
+
+		if (resolve && (addr[5] & 0xc0) == 0x40) {
+			uint8_t ident[6], ident_type;
+
+			if (keys_resolve_identity(addr, ident, &ident_type)) {
+				print_addr_type("  Identity type", ident_type);
+				print_addr_resolve("  Identity", ident,
+							ident_type, false);
+			}
+		}
+		break;
+	default:
+		print_field("%s: %2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X",
+					label, addr[5], addr[4], addr[3],
+					addr[2], addr[1], addr[0]);
+		break;
+	}
+}
+
+static void print_addr(const char *label, const uint8_t *addr,
+						uint8_t addr_type)
+{
+	print_addr_resolve(label, addr, addr_type, true);
+}
+
+static void print_bdaddr(const uint8_t *bdaddr)
+{
+	print_addr("Address", bdaddr, 0x00);
+}
+
+static void print_lt_addr(uint8_t lt_addr)
+{
+	print_field("LT address: %d", lt_addr);
+}
+
+static void print_handle(uint16_t handle)
+{
+	print_field("Handle: %d", le16_to_cpu(handle));
+}
+
+static void print_phy_handle(uint8_t phy_handle)
+{
+	print_field("Physical handle: %d", phy_handle);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} pkt_type_table[] = {
+	{  1, "2-DH1 may not be used"	},
+	{  2, "3-DH1 may not be used"	},
+	{  3, "DM1 may be used"		},
+	{  4, "DH1 may be used"		},
+	{  8, "2-DH3 may not be used"	},
+	{  9, "3-DH3 may not be used"	},
+	{ 10, "DM3 may be used"		},
+	{ 11, "DH3 may be used"		},
+	{ 12, "3-DH5 may not be used"	},
+	{ 13, "3-DH5 may not be used"	},
+	{ 14, "DM5 may be used"		},
+	{ 15, "DH5 may be used"		},
+	{ }
+};
+
+static void print_pkt_type(uint16_t pkt_type)
+{
+	uint16_t mask;
+	int i;
+
+	print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type));
+
+	mask = le16_to_cpu(pkt_type);
+
+	for (i = 0; pkt_type_table[i].str; i++) {
+		if (le16_to_cpu(pkt_type) & (1 << pkt_type_table[i].bit)) {
+			print_field("  %s", pkt_type_table[i].str);
+			mask &= ~(1 << pkt_type_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_PKT_TYPE_BIT,
+				"  Unknown packet types (0x%4.4x)", mask);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} pkt_type_sco_table[] = {
+	{  0, "HV1 may be used"		},
+	{  1, "HV2 may be used"		},
+	{  2, "HV3 may be used"		},
+	{  3, "EV3 may be used"		},
+	{  4, "EV4 may be used"		},
+	{  5, "EV5 may be used"		},
+	{  6, "2-EV3 may not be used"	},
+	{  7, "3-EV3 may not be used"	},
+	{  8, "2-EV5 may not be used"	},
+	{  9, "3-EV5 may not be used"	},
+	{ }
+};
+
+static void print_pkt_type_sco(uint16_t pkt_type)
+{
+	uint16_t mask;
+	int i;
+
+	print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type));
+
+	mask = le16_to_cpu(pkt_type);
+
+	for (i = 0; pkt_type_sco_table[i].str; i++) {
+		if (le16_to_cpu(pkt_type) & (1 << pkt_type_sco_table[i].bit)) {
+			print_field("  %s", pkt_type_sco_table[i].str);
+			mask &= ~(1 << pkt_type_sco_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_PKT_TYPE_BIT,
+				"  Unknown packet types (0x%4.4x)", mask);
+}
+
+static void print_iac(const uint8_t *lap)
+{
+	const char *str = "";
+
+	if (lap[2] == 0x9e && lap[1] == 0x8b) {
+		switch (lap[0]) {
+		case 0x33:
+			str = " (General Inquiry)";
+			break;
+		case 0x00:
+			str = " (Limited Inquiry)";
+			break;
+		}
+	}
+
+	print_field("Access code: 0x%2.2x%2.2x%2.2x%s",
+						lap[2], lap[1], lap[0], str);
+}
+
+static void print_auth_enable(uint8_t enable)
+{
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Authentication not required";
+		break;
+	case 0x01:
+		str = "Authentication required for all connections";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Enable: %s (0x%2.2x)", str, enable);
+}
+
+static void print_encrypt_mode(uint8_t mode)
+{
+	const char *str;
+
+	switch (mode) {
+	case 0x00:
+		str = "Encryption not required";
+		break;
+	case 0x01:
+		str = "Encryption required for all connections";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} svc_class_table[] = {
+	{ 0, "Positioning (Location identification)"		},
+	{ 1, "Networking (LAN, Ad hoc)"				},
+	{ 2, "Rendering (Printing, Speaker)"			},
+	{ 3, "Capturing (Scanner, Microphone)"			},
+	{ 4, "Object Transfer (v-Inbox, v-Folder)"		},
+	{ 5, "Audio (Speaker, Microphone, Headset)"		},
+	{ 6, "Telephony (Cordless telephony, Modem, Headset)"	},
+	{ 7, "Information (WEB-server, WAP-server)"		},
+	{ }
+};
+
+static const struct {
+	uint8_t val;
+	const char *str;
+} major_class_computer_table[] = {
+	{ 0x00, "Uncategorized, code for device not assigned"	},
+	{ 0x01, "Desktop workstation"				},
+	{ 0x02, "Server-class computer"				},
+	{ 0x03, "Laptop"					},
+	{ 0x04, "Handheld PC/PDA (clam shell)"			},
+	{ 0x05, "Palm sized PC/PDA"				},
+	{ 0x06, "Wearable computer (Watch sized)"		},
+	{ 0x07, "Tablet"					},
+	{ }
+};
+
+static const char *major_class_computer(uint8_t minor)
+{
+	int i;
+
+	for (i = 0; major_class_computer_table[i].str; i++) {
+		if (major_class_computer_table[i].val == minor)
+			return major_class_computer_table[i].str;
+	}
+
+	return NULL;
+}
+
+static const struct {
+	uint8_t val;
+	const char *str;
+} major_class_phone_table[] = {
+	{ 0x00, "Uncategorized, code for device not assigned"	},
+	{ 0x01, "Cellular"					},
+	{ 0x02, "Cordless"					},
+	{ 0x03, "Smart phone"					},
+	{ 0x04, "Wired modem or voice gateway"			},
+	{ 0x05, "Common ISDN Access"				},
+	{ }
+};
+
+static const char *major_class_phone(uint8_t minor)
+{
+	int i;
+
+	for (i = 0; major_class_phone_table[i].str; i++) {
+		if (major_class_phone_table[i].val == minor)
+			return major_class_phone_table[i].str;
+	}
+
+	return NULL;
+}
+
+static const struct {
+	uint8_t val;
+	const char *str;
+} major_class_av_table[] = {
+	{ 0x00, "Uncategorized, code for device not assigned"	},
+	{ 0x01, "Wearable Headset Device"			},
+	{ 0x02, "Hands-free Device"				},
+	{ 0x04, "Microphone"					},
+	{ 0x05, "Loudspeaker"					},
+	{ 0x06, "Headphones"					},
+	{ 0x07, "Portable Audio"				},
+	{ 0x08, "Car audio"					},
+	{ 0x09, "Set-top box"					},
+	{ 0x0a, "HiFi Audio Device"				},
+	{ 0x0b, "VCR"						},
+	{ 0x0c, "Video Camera"					},
+	{ 0x0d, "Camcorder"					},
+	{ 0x0e, "Video Monitor"					},
+	{ 0x0f, "Video Display and Loudspeaker"			},
+	{ 0x10, "Video Conferencing"				},
+	{ 0x12, "Gaming/Toy"					},
+	{ }
+};
+
+static const char *major_class_av(uint8_t minor)
+{
+	int i;
+
+	for (i = 0; major_class_av_table[i].str; i++) {
+		if (major_class_av_table[i].val == minor)
+			return major_class_av_table[i].str;
+	}
+
+	return NULL;
+}
+
+static const struct {
+	uint8_t val;
+	const char *str;
+} major_class_wearable_table[] = {
+	{ 0x01, "Wrist Watch"	},
+	{ 0x02, "Pager"		},
+	{ 0x03, "Jacket"	},
+	{ 0x04, "Helmet"	},
+	{ 0x05, "Glasses"	},
+	{ }
+};
+
+static const char *major_class_wearable(uint8_t minor)
+{
+	int i;
+
+	for (i = 0; major_class_wearable_table[i].str; i++) {
+		if (major_class_wearable_table[i].val == minor)
+			return major_class_wearable_table[i].str;
+	}
+
+	return NULL;
+}
+
+static const struct {
+	uint8_t val;
+	const char *str;
+	const char *(*func)(uint8_t minor);
+} major_class_table[] = {
+	{ 0x00, "Miscellaneous"						},
+	{ 0x01, "Computer (desktop, notebook, PDA, organizers)",
+						major_class_computer	},
+	{ 0x02, "Phone (cellular, cordless, payphone, modem)",
+						major_class_phone	},
+	{ 0x03, "LAN /Network Access point"				},
+	{ 0x04, "Audio/Video (headset, speaker, stereo, video, vcr)",
+						major_class_av		},
+	{ 0x05, "Peripheral (mouse, joystick, keyboards)"		},
+	{ 0x06, "Imaging (printing, scanner, camera, display)"		},
+	{ 0x07, "Wearable",			major_class_wearable	},
+	{ 0x08, "Toy"							},
+	{ 0x09, "Health"						},
+	{ 0x1f, "Uncategorized, specific device code not specified"	},
+	{ }
+};
+
+static void print_dev_class(const uint8_t *dev_class)
+{
+	uint8_t mask, major_cls, minor_cls;
+	const char *major_str = NULL;
+	const char *minor_str = NULL;
+	int i;
+
+	print_field("Class: 0x%2.2x%2.2x%2.2x",
+			dev_class[2], dev_class[1], dev_class[0]);
+
+	if ((dev_class[0] & 0x03) != 0x00) {
+		print_field("  Format type: 0x%2.2x", dev_class[0] & 0x03);
+		print_text(COLOR_ERROR, "  invalid format type");
+		return;
+	}
+
+	major_cls = dev_class[1] & 0x1f;
+	minor_cls = (dev_class[0] & 0xfc) >> 2;
+
+	for (i = 0; major_class_table[i].str; i++) {
+		if (major_class_table[i].val == major_cls) {
+			major_str = major_class_table[i].str;
+
+			if (!major_class_table[i].func)
+				break;
+
+			minor_str = major_class_table[i].func(minor_cls);
+			break;
+		}
+	}
+
+	if (major_str) {
+		print_field("  Major class: %s", major_str);
+		if (minor_str)
+			print_field("  Minor class: %s", minor_str);
+		else
+			print_field("  Minor class: 0x%2.2x", minor_cls);
+	} else {
+		print_field("  Major class: 0x%2.2x", major_cls);
+		print_field("  Minor class: 0x%2.2x", minor_cls);
+	}
+
+	if (dev_class[1] & 0x20)
+		print_field("  Limited Discoverable Mode");
+
+	if ((dev_class[1] & 0xc0) != 0x00) {
+		print_text(COLOR_ERROR, "  invalid service class");
+		return;
+	}
+
+	mask = dev_class[2];
+
+	for (i = 0; svc_class_table[i].str; i++) {
+		if (dev_class[2] & (1 << svc_class_table[i].bit)) {
+			print_field("  %s", svc_class_table[i].str);
+			mask &= ~(1 << svc_class_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_SERVICE_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);
+}
+
+static void print_num_broadcast_retrans(uint8_t num_retrans)
+{
+	print_field("Number of broadcast retransmissions: %u", num_retrans);
+}
+
+static void print_hold_mode_activity(uint8_t activity)
+{
+	print_field("Activity: 0x%2.2x", activity);
+
+	if (activity == 0x00) {
+		print_field("  Maintain current Power State");
+		return;
+	}
+
+	if (activity & 0x01)
+		print_field("  Suspend Page Scan");
+	if (activity & 0x02)
+		print_field("  Suspend Inquiry Scan");
+	if (activity & 0x04)
+		print_field("  Suspend Periodic Inquiries");
+}
+
+static void print_power_type(uint8_t type)
+{
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Current Transmit Power Level";
+		break;
+	case 0x01:
+		str = "Maximum Transmit Power Level";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, 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);
+}
+
+static void print_host_flow_control(uint8_t enable)
+{
+	const char *str;
+
+	switch (enable) {
+	case 0x00:
+		str = "Off";
+		break;
+	case 0x01:
+		str = "ACL Data Packets";
+		break;
+	case 0x02:
+		str = "Synchronous Data Packets";
+		break;
+	case 0x03:
+		str = "ACL and Synchronous Data Packets";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Flow control: %s (0x%2.2x)", str, enable);
+}
+
+static void print_voice_setting(uint16_t setting)
+{
+	uint8_t input_coding = (le16_to_cpu(setting) & 0x0300) >> 8;
+	uint8_t input_data_format = (le16_to_cpu(setting) & 0xc0) >> 6;
+	uint8_t air_coding_format = le16_to_cpu(setting) & 0x0003;
+	const char *str;
+
+	print_field("Setting: 0x%4.4x", le16_to_cpu(setting));
+
+	switch (input_coding) {
+	case 0x00:
+		str = "Linear";
+		break;
+	case 0x01:
+		str = "u-law";
+		break;
+	case 0x02:
+		str = "A-law";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("  Input Coding: %s", str);
+
+	switch (input_data_format) {
+	case 0x00:
+		str = "1's complement";
+		break;
+	case 0x01:
+		str = "2's complement";
+		break;
+	case 0x02:
+		str = "Sign-Magnitude";
+		break;
+	case 0x03:
+		str = "Unsigned";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("  Input Data Format: %s", str);
+
+	if (input_coding == 0x00) {
+		print_field("  Input Sample Size: %s",
+			le16_to_cpu(setting) & 0x20 ? "16-bit" : "8-bit");
+		print_field("  # of bits padding at MSB: %d",
+					(le16_to_cpu(setting) & 0x1c) >> 2);
+	}
+
+	switch (air_coding_format) {
+	case 0x00:
+		str = "CVSD";
+		break;
+	case 0x01:
+		str = "u-law";
+		break;
+	case 0x02:
+		str = "A-law";
+		break;
+	case 0x03:
+		str = "Transparent Data";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("  Air Coding Format: %s", str);
+}
+
+static void print_retransmission_effort(uint8_t effort)
+{
+	const char *str;
+
+	switch (effort) {
+	case 0x00:
+		str = "No retransmissions";
+		break;
+	case 0x01:
+		str = "Optimize for power consumption";
+		break;
+	case 0x02:
+		str = "Optimize for link quality";
+		break;
+	case 0xff:
+		str = "Don't care";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Retransmission effort: %s (0x%2.2x)", str, effort);
+}
+
+static void print_scan_enable(uint8_t scan_enable)
+{
+	const char *str;
+
+	switch (scan_enable) {
+	case 0x00:
+		str = "No Scans";
+		break;
+	case 0x01:
+		str = "Inquiry Scan";
+		break;
+	case 0x02:
+		str = "Page Scan";
+		break;
+	case 0x03:
+		str = "Inquiry Scan + Page Scan";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Scan enable: %s (0x%2.2x)", str, scan_enable);
+}
+
+static void print_link_policy(uint16_t link_policy)
+{
+	uint16_t policy = le16_to_cpu(link_policy);
+
+	print_field("Link policy: 0x%4.4x", policy);
+
+	if (policy == 0x0000) {
+		print_field("  Disable All Modes");
+		return;
+	}
+
+	if (policy & 0x0001)
+		print_field("  Enable Role Switch");
+	if (policy & 0x0002)
+		print_field("  Enable Hold Mode");
+	if (policy & 0x0004)
+		print_field("  Enable Sniff Mode");
+	if (policy & 0x0008)
+		print_field("  Enable Park State");
+}
+
+static void print_air_mode(uint8_t mode)
+{
+	const char *str;
+
+	switch (mode) {
+	case 0x00:
+		str = "u-law log";
+		break;
+	case 0x01:
+		str = "A-law log";
+		break;
+	case 0x02:
+		str = "CVSD";
+		break;
+	case 0x03:
+		str = "Transparent";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Air mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_codec(const char *label, uint8_t codec)
+{
+	const char *str;
+
+	switch (codec) {
+	case 0x00:
+		str = "u-law log";
+		break;
+	case 0x01:
+		str = "A-law log";
+		break;
+	case 0x02:
+		str = "CVSD";
+		break;
+	case 0x03:
+		str = "Transparent";
+		break;
+	case 0x04:
+		str = "Linear PCM";
+		break;
+	case 0x05:
+		str = "mSBC";
+		break;
+	case 0xff:
+		str = "Vendor specific";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s: %s (0x%2.2x)", label, str, codec);
+}
+
+static void print_inquiry_mode(uint8_t mode)
+{
+	const char *str;
+
+	switch (mode) {
+	case 0x00:
+		str = "Standard Inquiry Result";
+		break;
+	case 0x01:
+		str = "Inquiry Result with RSSI";
+		break;
+	case 0x02:
+		str = "Inquiry Result with RSSI or Extended Inquiry Result";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_inquiry_scan_type(uint8_t type)
+{
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Standard Scan";
+		break;
+	case 0x01:
+		str = "Interlaced Scan";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, type);
+}
+
+static void print_pscan_type(uint8_t type)
+{
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Standard Scan";
+		break;
+	case 0x01:
+		str = "Interlaced Scan";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	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;
+
+	switch (mode) {
+	case 0x00:
+		str = "No Loopback";
+		break;
+	case 0x01:
+		str = "Local Loopback";
+		break;
+	case 0x02:
+		str = "Remote Loopback";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	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)",
+			le16_to_cpu(timeout) * 10, le16_to_cpu(timeout));
+}
+
+static void print_pscan_rep_mode(uint8_t pscan_rep_mode)
+{
+	const char *str;
+
+	switch (pscan_rep_mode) {
+	case 0x00:
+		str = "R0";
+		break;
+	case 0x01:
+		str = "R1";
+		break;
+	case 0x02:
+		str = "R2";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Page scan repetition mode: %s (0x%2.2x)",
+						str, pscan_rep_mode);
+}
+
+static void print_pscan_period_mode(uint8_t pscan_period_mode)
+{
+	const char *str;
+
+	switch (pscan_period_mode) {
+	case 0x00:
+		str = "P0";
+		break;
+	case 0x01:
+		str = "P1";
+		break;
+	case 0x02:
+		str = "P2";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Page period mode: %s (0x%2.2x)", str, pscan_period_mode);
+}
+
+static void print_pscan_mode(uint8_t pscan_mode)
+{
+	const char *str;
+
+	switch (pscan_mode) {
+	case 0x00:
+		str = "Mandatory";
+		break;
+	case 0x01:
+		str = "Optional I";
+		break;
+	case 0x02:
+		str = "Optional II";
+		break;
+	case 0x03:
+		str = "Optional III";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Page scan mode: %s (0x%2.2x)", str, pscan_mode);
+}
+
+static void print_clock_offset(uint16_t clock_offset)
+{
+	print_field("Clock offset: 0x%4.4x", le16_to_cpu(clock_offset));
+}
+
+static void print_clock(uint32_t clock)
+{
+	print_field("Clock: 0x%8.8x", le32_to_cpu(clock));
+}
+
+static void print_clock_type(uint8_t type)
+{
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Local clock";
+		break;
+	case 0x01:
+		str = "Piconet clock";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, type);
+}
+
+static void print_clock_accuracy(uint16_t accuracy)
+{
+	if (le16_to_cpu(accuracy) == 0xffff)
+		print_field("Accuracy: Unknown (0x%4.4x)",
+						le16_to_cpu(accuracy));
+	else
+		print_field("Accuracy: %.4f msec (0x%4.4x)",
+						le16_to_cpu(accuracy) * 0.3125,
+						le16_to_cpu(accuracy));
+}
+
+static void print_lpo_allowed(uint8_t lpo_allowed)
+{
+	print_field("LPO allowed: 0x%2.2x", lpo_allowed);
+}
+
+static void print_broadcast_fragment(uint8_t fragment)
+{
+	const char *str;
+
+	switch (fragment) {
+	case 0x00:
+		str = "Continuation fragment";
+		break;
+	case 0x01:
+		str = "Starting fragment";
+		break;
+	case 0x02:
+		str = "Ending fragment";
+		break;
+	case 0x03:
+		str = "No fragmentation";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Fragment: %s (0x%2.2x)", str, fragment);
+}
+
+static void print_link_type(uint8_t link_type)
+{
+	const char *str;
+
+	switch (link_type) {
+	case 0x00:
+		str = "SCO";
+		break;
+	case 0x01:
+		str = "ACL";
+		break;
+	case 0x02:
+		str = "eSCO";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	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;
+	uint8_t conn_type;
+
+	conn_type = get_type(le16_to_cpu(handle));
+
+	switch (encr_mode) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		switch (conn_type) {
+		case 0x00:
+			str = "Enabled with E0";
+			break;
+		case 0x01:
+			str = "Enabled with AES-CCM";
+			break;
+		default:
+			str = "Enabled";
+			break;
+		}
+		break;
+	case 0x02:
+		str = "Enabled with AES-CCM";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Encryption: %s (0x%2.2x)", str, encr_mode);
+}
+
+static void print_pin_type(uint8_t pin_type)
+{
+	const char *str;
+
+	switch (pin_type) {
+	case 0x00:
+		str = "Variable";
+		break;
+	case 0x01:
+		str = "Fixed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("PIN type: %s (0x%2.2x)", str, pin_type);
+}
+
+static void print_key_flag(uint8_t key_flag)
+{
+	const char *str;
+
+	switch (key_flag) {
+	case 0x00:
+		str = "Semi-permanent";
+		break;
+	case 0x01:
+		str = "Temporary";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Key flag: %s (0x%2.2x)", str, key_flag);
+}
+
+static void print_key_len(uint8_t key_len)
+{
+	const char *str;
+
+	switch (key_len) {
+	case 32:
+		str = "802.11 PAL";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Key length: %s (%d)", str, key_len);
+}
+
+static void print_key_type(uint8_t key_type)
+{
+	const char *str;
+
+	switch (key_type) {
+	case 0x00:
+		str = "Combination key";
+		break;
+	case 0x01:
+		str = "Local Unit key";
+		break;
+	case 0x02:
+		str = "Remote Unit key";
+		break;
+	case 0x03:
+		str = "Debug Combination key";
+		break;
+	case 0x04:
+		str = "Unauthenticated Combination key from P-192";
+		break;
+	case 0x05:
+		str = "Authenticated Combination key from P-192";
+		break;
+	case 0x06:
+		str = "Changed Combination key";
+		break;
+	case 0x07:
+		str = "Unauthenticated Combination key from P-256";
+		break;
+	case 0x08:
+		str = "Authenticated Combination key from P-256";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Key type: %s (0x%2.2x)", str, key_type);
+}
+
+static void print_key_size(uint8_t key_size)
+{
+	print_field("Key size: %d", key_size);
+}
+
+static void print_hex_field(const char *label, const uint8_t *data,
+								uint8_t len)
+{
+	char str[len * 2 + 1];
+	uint8_t i;
+
+	str[0] = '\0';
+
+	for (i = 0; i < len; i++)
+		sprintf(str + (i * 2), "%2.2x", data[i]);
+
+	print_field("%s: %s", label, str);
+}
+
+static void print_key(const char *label, const uint8_t *link_key)
+{
+	print_hex_field(label, link_key, 16);
+}
+
+static void print_link_key(const uint8_t *link_key)
+{
+	print_key("Link key", link_key);
+}
+
+static void print_pin_code(const uint8_t *pin_code, uint8_t pin_len)
+{
+	char str[pin_len + 1];
+	uint8_t i;
+
+	for (i = 0; i < pin_len; i++)
+		sprintf(str + i, "%c", (const char) pin_code[i]);
+
+	print_field("PIN code: %s", str);
+}
+
+static void print_hash_p192(const uint8_t *hash)
+{
+	print_key("Hash C from P-192", hash);
+}
+
+static void print_hash_p256(const uint8_t *hash)
+{
+	print_key("Hash C from P-256", hash);
+}
+
+static void print_randomizer_p192(const uint8_t *randomizer)
+{
+	print_key("Randomizer R with P-192", randomizer);
+}
+
+static void print_randomizer_p256(const uint8_t *randomizer)
+{
+	print_key("Randomizer R with P-256", randomizer);
+}
+
+static void print_pk256(const char *label, const uint8_t *key)
+{
+	print_hex_field(label, key, 64);
+}
+
+static void print_dhkey(const uint8_t *dhkey)
+{
+	print_hex_field("Diffie-Hellman key", dhkey, 32);
+}
+
+static void print_passkey(uint32_t passkey)
+{
+	print_field("Passkey: %06d", le32_to_cpu(passkey));
+}
+
+static void print_io_capability(uint8_t capability)
+{
+	const char *str;
+
+	switch (capability) {
+	case 0x00:
+		str = "DisplayOnly";
+		break;
+	case 0x01:
+		str = "DisplayYesNo";
+		break;
+	case 0x02:
+		str = "KeyboardOnly";
+		break;
+	case 0x03:
+		str = "NoInputNoOutput";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("IO capability: %s (0x%2.2x)", str, capability);
+}
+
+static void print_oob_data(uint8_t oob_data)
+{
+	const char *str;
+
+	switch (oob_data) {
+	case 0x00:
+		str = "Authentication data not present";
+		break;
+	case 0x01:
+		str = "P-192 authentication data present";
+		break;
+	case 0x02:
+		str = "P-256 authentication data present";
+		break;
+	case 0x03:
+		str = "P-192 and P-256 authentication data present";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("OOB data: %s (0x%2.2x)", str, oob_data);
+}
+
+static void print_oob_data_response(uint8_t oob_data)
+{
+	const char *str;
+
+	switch (oob_data) {
+	case 0x00:
+		str = "Authentication data not present";
+		break;
+	case 0x01:
+		str = "Authentication data present";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("OOB data: %s (0x%2.2x)", str, oob_data);
+}
+
+static void print_authentication(uint8_t authentication)
+{
+	const char *str;
+
+	switch (authentication) {
+	case 0x00:
+		str = "No Bonding - MITM not required";
+		break;
+	case 0x01:
+		str = "No Bonding - MITM required";
+		break;
+	case 0x02:
+		str = "Dedicated Bonding - MITM not required";
+		break;
+	case 0x03:
+		str = "Dedicated Bonding - MITM required";
+		break;
+	case 0x04:
+		str = "General Bonding - MITM not required";
+		break;
+	case 0x05:
+		str = "General Bonding - MITM required";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Authentication: %s (0x%2.2x)", str, authentication);
+}
+
+void packet_print_io_capability(uint8_t capability)
+{
+	print_io_capability(capability);
+}
+
+void packet_print_io_authentication(uint8_t authentication)
+{
+	print_authentication(authentication);
+}
+
+static void print_location_domain_aware(uint8_t aware)
+{
+	const char *str;
+
+	switch (aware) {
+	case 0x00:
+		str = "Regulatory domain unknown";
+		break;
+	case 0x01:
+		str = "Regulatory domain known";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Domain aware: %s (0x%2.2x)", str, aware);
+}
+
+static void print_location_domain(const uint8_t *domain)
+{
+	print_field("Domain: %c%c (0x%2.2x%2.2x)",
+		(char) domain[0], (char) domain[1], domain[0], domain[1]);
+}
+
+static void print_location_domain_options(uint8_t options)
+{
+	print_field("Domain options: %c (0x%2.2x)", (char) options, options);
+}
+
+static void print_location_options(uint8_t options)
+{
+	print_field("Options: 0x%2.2x", options);
+}
+
+static void print_flow_control_mode(uint8_t mode)
+{
+	const char *str;
+
+	switch (mode) {
+	case 0x00:
+		str = "Packet based";
+		break;
+	case 0x01:
+		str = "Data block based";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Flow control mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_flow_direction(uint8_t direction)
+{
+	const char *str;
+
+	switch (direction) {
+	case 0x00:
+		str = "Outgoing";
+		break;
+	case 0x01:
+		str = "Incoming";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Flow direction: %s (0x%2.2x)", str, direction);
+}
+
+static void print_service_type(uint8_t service_type)
+{
+	const char *str;
+
+	switch (service_type) {
+	case 0x00:
+		str = "No Traffic";
+		break;
+	case 0x01:
+		str = "Best Effort";
+		break;
+	case 0x02:
+		str = "Guaranteed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Service type: %s (0x%2.2x)", str, service_type);
+}
+
+static void print_flow_spec(const char *label, const uint8_t *data)
+{
+	const char *str;
+
+	switch (data[1]) {
+	case 0x00:
+		str = "No traffic";
+		break;
+	case 0x01:
+		str = "Best effort";
+		break;
+	case 0x02:
+		str = "Guaranteed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s flow spec: 0x%2.2x", label, data[0]);
+	print_field("  Service type: %s (0x%2.2x)", str, data[1]);
+	print_field("  Maximum SDU size: 0x%4.4x", get_le16(data + 2));
+	print_field("  SDU inter-arrival time: 0x%8.8x", get_le32(data + 4));
+	print_field("  Access latency: 0x%8.8x", get_le32(data + 8));
+	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;
+
+	switch (amp_status) {
+	case 0x00:
+		str = "Present";
+		break;
+	case 0x01:
+		str = "Bluetooth only";
+		break;
+	case 0x02:
+		str = "No capacity";
+		break;
+	case 0x03:
+		str = "Low capacity";
+		break;
+	case 0x04:
+		str = "Medium capacity";
+		break;
+	case 0x05:
+		str = "High capacity";
+		break;
+	case 0x06:
+		str = "Full capacity";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("AMP status: %s (0x%2.2x)", str, amp_status);
+}
+
+static void print_num_resp(uint8_t num_resp)
+{
+	print_field("Num responses: %d", num_resp);
+}
+
+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)
+{
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Connectable undirected - ADV_IND";
+		break;
+	case 0x01:
+		str = "Connectable directed - ADV_DIRECT_IND";
+		break;
+	case 0x02:
+		str = "Scannable undirected - ADV_SCAN_IND";
+		break;
+	case 0x03:
+		str = "Non connectable undirected - ADV_NONCONN_IND";
+		break;
+	case 0x04:
+		str = "Scan response - SCAN_RSP";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Event type: %s (0x%2.2x)", str, type);
+}
+
+static void print_rssi(int8_t rssi)
+{
+	if ((uint8_t) rssi == 0x99 || rssi == 127)
+		print_field("RSSI: invalid (0x%2.2x)", (uint8_t) rssi);
+	else
+		print_field("RSSI: %d dBm (0x%2.2x)", rssi, (uint8_t) rssi);
+}
+
+static void print_slot_625(const char *label, uint16_t value)
+{
+	 print_field("%s: %.3f msec (0x%4.4x)", label,
+				le16_to_cpu(value) * 0.625, le16_to_cpu(value));
+}
+
+static void print_slot_125(const char *label, uint16_t value)
+{
+	print_field("%s: %.2f msec (0x%4.4x)", label,
+				le16_to_cpu(value) * 1.25, le16_to_cpu(value));
+}
+
+static void print_timeout(uint16_t timeout)
+{
+	print_slot_625("Timeout", timeout);
+}
+
+static void print_interval(uint16_t interval)
+{
+	print_slot_625("Interval", interval);
+}
+
+static void print_window(uint16_t window)
+{
+	print_slot_625("Window", window);
+}
+
+static void print_role(uint8_t role)
+{
+	const char *str;
+
+	switch (role) {
+	case 0x00:
+		str = "Master";
+		break;
+	case 0x01:
+		str = "Slave";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Role: %s (0x%2.2x)", str, role);
+}
+
+static void print_mode(uint8_t mode)
+{
+	const char *str;
+
+	switch (mode) {
+	case 0x00:
+		str = "Active";
+		break;
+	case 0x01:
+		str = "Hold";
+		break;
+	case 0x02:
+		str = "Sniff";
+		break;
+	case 0x03:
+		str = "Park";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_name(const uint8_t *name)
+{
+	char str[249];
+
+	memcpy(str, name, 248);
+	str[248] = '\0';
+
+	print_field("Name: %s", str);
+}
+
+static void print_channel_map(const uint8_t *map)
+{
+	unsigned int count = 0, start = 0;
+	char str[21];
+	int i, n;
+
+	for (i = 0; i < 10; i++)
+		sprintf(str + (i * 2), "%2.2x", map[i]);
+
+	print_field("Channel map: 0x%s", str);
+
+	for (i = 0; i < 10; i++) {
+		for (n = 0; n < 8; n++) {
+			if (map[i] & (1 << n)) {
+				if (count == 0)
+					start = (i * 8) + n;
+				count++;
+				continue;
+			}
+
+			if (count > 1) {
+				print_field("  Channel %u-%u",
+						start, start + count - 1);
+				count = 0;
+			} else if (count > 0) {
+				print_field("  Channel %u", start);
+				count = 0;
+			}
+		}
+	}
+}
+
+void packet_print_channel_map_lmp(const uint8_t *map)
+{
+	print_channel_map(map);
+}
+
+static void print_flush_timeout(uint16_t timeout)
+{
+	if (timeout)
+		print_timeout(timeout);
+	else
+		print_field("Timeout: No Automatic Flush");
+}
+
+void packet_print_version(const char *label, uint8_t version,
+				const char *sublabel, uint16_t subversion)
+{
+	const char *str;
+
+	switch (version) {
+	case 0x00:
+		str = "Bluetooth 1.0b";
+		break;
+	case 0x01:
+		str = "Bluetooth 1.1";
+		break;
+	case 0x02:
+		str = "Bluetooth 1.2";
+		break;
+	case 0x03:
+		str = "Bluetooth 2.0";
+		break;
+	case 0x04:
+		str = "Bluetooth 2.1";
+		break;
+	case 0x05:
+		str = "Bluetooth 3.0";
+		break;
+	case 0x06:
+		str = "Bluetooth 4.0";
+		break;
+	case 0x07:
+		str = "Bluetooth 4.1";
+		break;
+	case 0x08:
+		str = "Bluetooth 4.2";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)", label, str, version,
+					sublabel, subversion, subversion);
+}
+
+static void print_hci_version(uint8_t version, uint16_t revision)
+{
+	packet_print_version("HCI version", version,
+				"Revision", le16_to_cpu(revision));
+}
+
+static void print_lmp_version(uint8_t version, uint16_t subversion)
+{
+	packet_print_version("LMP version", version,
+				"Subversion", le16_to_cpu(subversion));
+}
+
+static void print_pal_version(uint8_t version, uint16_t subversion)
+{
+	const char *str;
+
+	switch (version) {
+	case 0x01:
+		str = "Bluetooth 3.0";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("PAL version: %s (0x%2.2x) - Subversion %d (0x%4.4x)",
+						str, version,
+						le16_to_cpu(subversion),
+						le16_to_cpu(subversion));
+}
+
+void packet_print_company(const char *label, uint16_t company)
+{
+	print_field("%s: %s (%d)", label, bt_compidtostr(company), company);
+}
+
+static void print_manufacturer(uint16_t manufacturer)
+{
+	packet_print_company("Manufacturer", le16_to_cpu(manufacturer));
+}
+
+static const struct {
+	uint16_t ver;
+	const char *str;
+} broadcom_uart_subversion_table[] = {
+	{ 0x210b, "BCM43142A0"	},	/* 001.001.011 */
+	{ 0x410e, "BCM43341B0"	},	/* 002.001.014 */
+	{ 0x4406, "BCM4324B3"	},	/* 002.004.006 */
+	{ }
+};
+
+static const struct {
+	uint16_t ver;
+	const char *str;
+} broadcom_usb_subversion_table[] = {
+	{ 0x210b, "BCM43142A0"	},	/* 001.001.011 */
+	{ 0x2112, "BCM4314A0"	},	/* 001.001.018 */
+	{ 0x2118, "BCM20702A0"	},	/* 001.001.024 */
+	{ 0x2126, "BCM4335A0"	},	/* 001.001.038 */
+	{ 0x220e, "BCM20702A1"	},	/* 001.002.014 */
+	{ 0x230f, "BCM4354A2"	},	/* 001.003.015 */
+	{ 0x4106, "BCM4335B0"	},	/* 002.001.006 */
+	{ 0x410e, "BCM20702B0"	},	/* 002.001.014 */
+	{ 0x6109, "BCM4335C0"	},	/* 003.001.009 */
+	{ 0x610c, "BCM4354"	},	/* 003.001.012 */
+	{ }
+};
+
+static void print_manufacturer_broadcom(uint16_t subversion, uint16_t revision)
+{
+	uint16_t ver = le16_to_cpu(subversion);
+	uint16_t rev = le16_to_cpu(revision);
+	const char *str = NULL;
+	int i;
+
+	switch ((rev & 0xf000) >> 12) {
+	case 0:
+	case 3:
+		for (i = 0; broadcom_uart_subversion_table[i].str; i++) {
+			if (broadcom_uart_subversion_table[i].ver == ver) {
+				str = broadcom_uart_subversion_table[i].str;
+				break;
+			}
+		}
+		break;
+	case 1:
+	case 2:
+		for (i = 0; broadcom_usb_subversion_table[i].str; i++) {
+			if (broadcom_usb_subversion_table[i].ver == ver) {
+				str = broadcom_usb_subversion_table[i].str;
+				break;
+			}
+		}
+		break;
+	}
+
+	if (str)
+		print_field("  Firmware: %3.3u.%3.3u.%3.3u (%s)",
+				(ver & 0xe000) >> 13,
+				(ver & 0x1f00) >> 8, ver & 0x00ff, str);
+	else
+		print_field("  Firmware: %3.3u.%3.3u.%3.3u",
+				(ver & 0xe000) >> 13,
+				(ver & 0x1f00) >> 8, ver & 0x00ff);
+
+	if (rev != 0xffff)
+		print_field("  Build: %4.4u", rev & 0x0fff);
+}
+
+static const char *get_supported_command(int bit);
+
+static void print_commands(const uint8_t *commands)
+{
+	unsigned int count = 0;
+	int i, n;
+
+	for (i = 0; i < 64; i++) {
+		for (n = 0; n < 8; n++) {
+			if (commands[i] & (1 << n))
+				count++;
+		}
+	}
+
+	print_field("Commands: %u entr%s", count, count == 1 ? "y" : "ies");
+
+	for (i = 0; i < 64; i++) {
+		for (n = 0; n < 8; n++) {
+			const char *cmd;
+
+			if (!(commands[i] & (1 << n)))
+				continue;
+
+			cmd = get_supported_command((i * 8) + n);
+			if (cmd)
+				print_field("  %s (Octet %d - Bit %d)",
+								cmd, i, n);
+			else
+				print_text(COLOR_UNKNOWN_COMMAND_BIT,
+						"  Octet %d - Bit %d ", i, n);
+		}
+	}
+}
+
+struct features_data {
+	uint8_t bit;
+	const char *str;
+};
+
+static const struct features_data features_page0[] = {
+	{  0, "3 slot packets"				},
+	{  1, "5 slot packets"				},
+	{  2, "Encryption"				},
+	{  3, "Slot offset"				},
+	{  4, "Timing accuracy"				},
+	{  5, "Role switch"				},
+	{  6, "Hold mode"				},
+	{  7, "Sniff mode"				},
+	{  8, "Park state"				},
+	{  9, "Power control requests"			},
+	{ 10, "Channel quality driven data rate (CQDDR)"},
+	{ 11, "SCO link"				},
+	{ 12, "HV2 packets"				},
+	{ 13, "HV3 packets"				},
+	{ 14, "u-law log synchronous data"		},
+	{ 15, "A-law log synchronous data"		},
+	{ 16, "CVSD synchronous data"			},
+	{ 17, "Paging parameter negotiation"		},
+	{ 18, "Power control"				},
+	{ 19, "Transparent synchronous data"		},
+	{ 20, "Flow control lag (least significant bit)"},
+	{ 21, "Flow control lag (middle bit)"		},
+	{ 22, "Flow control lag (most significant bit)"	},
+	{ 23, "Broadcast Encryption"			},
+	{ 25, "Enhanced Data Rate ACL 2 Mbps mode"	},
+	{ 26, "Enhanced Data Rate ACL 3 Mbps mode"	},
+	{ 27, "Enhanced inquiry scan"			},
+	{ 28, "Interlaced inquiry scan"			},
+	{ 29, "Interlaced page scan"			},
+	{ 30, "RSSI with inquiry results"		},
+	{ 31, "Extended SCO link (EV3 packets)"		},
+	{ 32, "EV4 packets"				},
+	{ 33, "EV5 packets"				},
+	{ 35, "AFH capable slave"			},
+	{ 36, "AFH classification slave"		},
+	{ 37, "BR/EDR Not Supported"			},
+	{ 38, "LE Supported (Controller)"		},
+	{ 39, "3-slot Enhanced Data Rate ACL packets"	},
+	{ 40, "5-slot Enhanced Data Rate ACL packets"	},
+	{ 41, "Sniff subrating"				},
+	{ 42, "Pause encryption"			},
+	{ 43, "AFH capable master"			},
+	{ 44, "AFH classification master"		},
+	{ 45, "Enhanced Data Rate eSCO 2 Mbps mode"	},
+	{ 46, "Enhanced Data Rate eSCO 3 Mbps mode"	},
+	{ 47, "3-slot Enhanced Data Rate eSCO packets"	},
+	{ 48, "Extended Inquiry Response"		},
+	{ 49, "Simultaneous LE and BR/EDR (Controller)"	},
+	{ 51, "Secure Simple Pairing"			},
+	{ 52, "Encapsulated PDU"			},
+	{ 53, "Erroneous Data Reporting"		},
+	{ 54, "Non-flushable Packet Boundary Flag"	},
+	{ 56, "Link Supervision Timeout Changed Event"	},
+	{ 57, "Inquiry TX Power Level"			},
+	{ 58, "Enhanced Power Control"			},
+	{ 63, "Extended features"			},
+	{ }
+};
+
+static const struct features_data features_page1[] = {
+	{  0, "Secure Simple Pairing (Host Support)"	},
+	{  1, "LE Supported (Host)"			},
+	{  2, "Simultaneous LE and BR/EDR (Host)"	},
+	{  3, "Secure Connections (Host Support)"	},
+	{ }
+};
+
+static const struct features_data features_page2[] = {
+	{  0, "Connectionless Slave Broadcast - Master"	},
+	{  1, "Connectionless Slave Broadcast - Slave"	},
+	{  2, "Synchronization Train"			},
+	{  3, "Synchronization Scan"			},
+	{  4, "Inquiry Response Notification Event"	},
+	{  5, "Generalized interlaced scan"		},
+	{  6, "Coarse Clock Adjustment"			},
+	{  8, "Secure Connections (Controller Support)"	},
+	{  9, "Ping"					},
+	{ 11, "Train nudging"				},
+	{ }
+};
+
+static const struct features_data features_le[] = {
+	{  0, "LE Encryption"				},
+	{  1, "Connection Parameter Request Procedure"	},
+	{  2, "Extended Reject Indication"		},
+	{  3, "Slave-initiated Features Exchange"	},
+	{  4, "LE Ping"					},
+	{  5, "LE Data Packet Length Extension"		},
+	{  6, "LL Privacy"				},
+	{  7, "Extended Scanner Filter Policies"	},
+	{ }
+};
+
+static void print_features(uint8_t page, const uint8_t *features_array,
+								uint8_t type)
+{
+	const struct features_data *features_table = NULL;
+	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);
+
+	switch (type) {
+	case 0x00:
+		switch (page) {
+		case 0:
+			features_table = features_page0;
+			break;
+		case 1:
+			features_table = features_page1;
+			break;
+		case 2:
+			features_table = features_page2;
+			break;
+		}
+		break;
+	case 0x01:
+		switch (page) {
+		case 0:
+			features_table = features_le;
+			break;
+		}
+		break;
+	}
+
+	if (!features_table)
+		return;
+
+	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);
+}
+
+void packet_print_features_lmp(const uint8_t *features, uint8_t page)
+{
+	print_features(page, features, 0x00);
+}
+
+void packet_print_features_ll(const uint8_t *features)
+{
+	print_features(0, features, 0x01);
+}
+
+#define LE_STATE_SCAN_ADV		0x0001
+#define LE_STATE_CONN_ADV		0x0002
+#define LE_STATE_NONCONN_ADV		0x0004
+#define LE_STATE_HIGH_DIRECT_ADV	0x0008
+#define LE_STATE_LOW_DIRECT_ADV		0x0010
+#define LE_STATE_ACTIVE_SCAN		0x0020
+#define LE_STATE_PASSIVE_SCAN		0x0040
+#define LE_STATE_INITIATING		0x0080
+#define LE_STATE_CONN_MASTER		0x0100
+#define LE_STATE_CONN_SLAVE		0x0200
+#define LE_STATE_MASTER_MASTER		0x0400
+#define LE_STATE_SLAVE_SLAVE		0x0800
+#define LE_STATE_MASTER_SLAVE		0x1000
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} le_states_desc_table[] = {
+	{  0, "Scannable Advertising State"			},
+	{  1, "Connectable Advertising State"			},
+	{  2, "Non-connectable Advertising State"		},
+	{  3, "High Duty Cycle Directed Advertising State"	},
+	{  4, "Low Duty Cycle Directed Advertising State"	},
+	{  5, "Active Scanning State"				},
+	{  6, "Passive Scanning State"				},
+	{  7, "Initiating State"				},
+	{  8, "Connection State (Master Role)"			},
+	{  9, "Connection State (Slave Role)"			},
+	{ 10, "Master Role & Master Role"			},
+	{ 11, "Slave Role & Slave Role"				},
+	{ 12, "Master Role & Slave Role"			},
+	{ }
+};
+
+static const struct {
+	uint8_t bit;
+	uint16_t states;
+} le_states_comb_table[] = {
+	{  0, LE_STATE_NONCONN_ADV				},
+	{  1, LE_STATE_SCAN_ADV					},
+	{  2, LE_STATE_CONN_ADV					},
+	{  3, LE_STATE_HIGH_DIRECT_ADV				},
+	{  4, LE_STATE_PASSIVE_SCAN				},
+	{  5, LE_STATE_ACTIVE_SCAN				},
+	{  6, LE_STATE_INITIATING | LE_STATE_CONN_MASTER	},
+	{  7, LE_STATE_CONN_SLAVE				},
+	{  8, LE_STATE_PASSIVE_SCAN | LE_STATE_NONCONN_ADV	},
+	{  9, LE_STATE_PASSIVE_SCAN | LE_STATE_SCAN_ADV		},
+	{ 10, LE_STATE_PASSIVE_SCAN | LE_STATE_CONN_ADV		},
+	{ 11, LE_STATE_PASSIVE_SCAN | LE_STATE_HIGH_DIRECT_ADV	},
+	{ 12, LE_STATE_ACTIVE_SCAN | LE_STATE_NONCONN_ADV	},
+	{ 13, LE_STATE_ACTIVE_SCAN | LE_STATE_SCAN_ADV		},
+	{ 14, LE_STATE_ACTIVE_SCAN | LE_STATE_CONN_ADV		},
+	{ 15, LE_STATE_ACTIVE_SCAN | LE_STATE_HIGH_DIRECT_ADV	},
+	{ 16, LE_STATE_INITIATING | LE_STATE_NONCONN_ADV	},
+	{ 17, LE_STATE_INITIATING | LE_STATE_SCAN_ADV		},
+	{ 18, LE_STATE_CONN_MASTER | LE_STATE_NONCONN_ADV	},
+	{ 19, LE_STATE_CONN_MASTER | LE_STATE_SCAN_ADV		},
+	{ 20, LE_STATE_CONN_SLAVE | LE_STATE_NONCONN_ADV	},
+	{ 21, LE_STATE_CONN_SLAVE | LE_STATE_SCAN_ADV		},
+	{ 22, LE_STATE_INITIATING | LE_STATE_PASSIVE_SCAN	},
+	{ 23, LE_STATE_INITIATING | LE_STATE_ACTIVE_SCAN	},
+	{ 24, LE_STATE_CONN_MASTER | LE_STATE_PASSIVE_SCAN	},
+	{ 25, LE_STATE_CONN_MASTER | LE_STATE_ACTIVE_SCAN	},
+	{ 26, LE_STATE_CONN_SLAVE | LE_STATE_PASSIVE_SCAN	},
+	{ 27, LE_STATE_CONN_SLAVE | LE_STATE_ACTIVE_SCAN	},
+	{ 28, LE_STATE_INITIATING | LE_STATE_CONN_MASTER |
+					LE_STATE_MASTER_MASTER	},
+	{ 29, LE_STATE_LOW_DIRECT_ADV				},
+	{ 30, LE_STATE_LOW_DIRECT_ADV | LE_STATE_PASSIVE_SCAN	},
+	{ 31, LE_STATE_LOW_DIRECT_ADV | LE_STATE_ACTIVE_SCAN	},
+	{ 32, LE_STATE_INITIATING | LE_STATE_CONN_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 33, LE_STATE_INITIATING | LE_STATE_HIGH_DIRECT_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 34, LE_STATE_INITIATING | LE_STATE_LOW_DIRECT_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 35, LE_STATE_CONN_MASTER | LE_STATE_CONN_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 36, LE_STATE_CONN_MASTER | LE_STATE_HIGH_DIRECT_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 37, LE_STATE_CONN_MASTER | LE_STATE_LOW_DIRECT_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 38, LE_STATE_CONN_SLAVE | LE_STATE_CONN_ADV |
+					LE_STATE_MASTER_SLAVE	},
+	{ 39, LE_STATE_CONN_SLAVE | LE_STATE_HIGH_DIRECT_ADV |
+					LE_STATE_SLAVE_SLAVE	},
+	{ 40, LE_STATE_CONN_SLAVE | LE_STATE_LOW_DIRECT_ADV |
+					LE_STATE_SLAVE_SLAVE	},
+	{ 41, LE_STATE_INITIATING | LE_STATE_CONN_SLAVE |
+					LE_STATE_MASTER_SLAVE	},
+	{ }
+};
+
+static void print_le_states(const uint8_t *states_array)
+{
+	uint64_t mask, states = 0;
+	int i, n;
+
+	for (i = 0; i < 8; i++)
+		states |= ((uint64_t) states_array[i]) << (i * 8);
+
+	print_field("States: 0x%16.16" PRIx64, states);
+
+	mask = states;
+
+	for (i = 0; le_states_comb_table[i].states; i++) {
+		uint64_t val = (((uint64_t) 1) << le_states_comb_table[i].bit);
+		const char *str[3] = { NULL, };
+		int num = 0;
+
+		if (!(states & val))
+			continue;
+
+		for (n = 0; n < 16; n++) {
+			if (le_states_comb_table[i].states & (1 << n))
+				str[num++] = le_states_desc_table[n].str;
+		}
+
+		if (num > 0) {
+			print_field("  %s", str[0]);
+			for (n = 1; n < num; n++)
+				print_field("    and %s", str[n]);
+		}
+
+		mask &= ~val;
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_LE_STATES, "  Unknown states "
+						"(0x%16.16" PRIx64 ")", mask);
+}
+
+static void print_le_channel_map(const uint8_t *map)
+{
+	unsigned int count = 0, start = 0;
+	char str[11];
+	int i, n;
+
+	for (i = 0; i < 5; i++)
+		sprintf(str + (i * 2), "%2.2x", map[i]);
+
+	print_field("Channel map: 0x%s", str);
+
+	for (i = 0; i < 5; i++) {
+		for (n = 0; n < 8; n++) {
+			if (map[i] & (1 << n)) {
+				if (count == 0)
+					start = (i * 8) + n;
+				count++;
+				continue;
+			}
+
+			if (count > 1) {
+				print_field("  Channel %u-%u",
+						start, start + count - 1);
+				count = 0;
+			} else if (count > 0) {
+				print_field("  Channel %u", start);
+				count = 0;
+			}
+		}
+	}
+}
+
+void packet_print_channel_map_ll(const uint8_t *map)
+{
+	print_le_channel_map(map);
+}
+
+static void print_random_number(uint64_t rand)
+{
+	print_field("Random number: 0x%16.16" PRIx64, le64_to_cpu(rand));
+}
+
+static void print_encrypted_diversifier(uint16_t ediv)
+{
+	print_field("Encrypted diversifier: 0x%4.4x", le16_to_cpu(ediv));
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} events_table[] = {
+	{  0, "Inquiry Complete"					},
+	{  1, "Inquiry Result"						},
+	{  2, "Connection Complete"					},
+	{  3, "Connection Request"					},
+	{  4, "Disconnection Complete"					},
+	{  5, "Authentication Complete"					},
+	{  6, "Remote Name Request Complete"				},
+	{  7, "Encryption Change"					},
+	{  8, "Change Connection Link Key Complete"			},
+	{  9, "Master Link Key Complete"				},
+	{ 10, "Read Remote Supported Features Complete"			},
+	{ 11, "Read Remote Version Information Complete"		},
+	{ 12, "QoS Setup Complete"					},
+	{ 13, "Command Complete"					},
+	{ 14, "Command Status"						},
+	{ 15, "Hardware Error"						},
+	{ 16, "Flush Occurred"						},
+	{ 17, "Role Change"						},
+	{ 18, "Number of Completed Packets"				},
+	{ 19, "Mode Change"						},
+	{ 20, "Return Link Keys"					},
+	{ 21, "PIN Code Request"					},
+	{ 22, "Link Key Request"					},
+	{ 23, "Link Key Notification"					},
+	{ 24, "Loopback Command"					},
+	{ 25, "Data Buffer Overflow"					},
+	{ 26, "Max Slots Change"					},
+	{ 27, "Read Clock Offset Complete"				},
+	{ 28, "Connection Packet Type Changed"				},
+	{ 29, "QoS Violation"						},
+	{ 30, "Page Scan Mode Change"					},
+	{ 31, "Page Scan Repetition Mode Change"			},
+	{ 32, "Flow Specification Complete"				},
+	{ 33, "Inquiry Result with RSSI"				},
+	{ 34, "Read Remote Extended Features Complete"			},
+	{ 43, "Synchronous Connection Complete"				},
+	{ 44, "Synchronous Connection Changed"				},
+	{ 45, "Sniff Subrating"						},
+	{ 46, "Extended Inquiry Result"					},
+	{ 47, "Encryption Key Refresh Complete"				},
+	{ 48, "IO Capability Request"					},
+	{ 49, "IO Capability Request Reply"				},
+	{ 50, "User Confirmation Request"				},
+	{ 51, "User Passkey Request"					},
+	{ 52, "Remote OOB Data Request"					},
+	{ 53, "Simple Pairing Complete"					},
+	{ 55, "Link Supervision Timeout Changed"			},
+	{ 56, "Enhanced Flush Complete"					},
+	{ 58, "User Passkey Notification"				},
+	{ 59, "Keypress Notification"					},
+	{ 60, "Remote Host Supported Features Notification"		},
+	{ 61, "LE Meta"							},
+	{ }
+};
+
+static void print_event_mask(const uint8_t *events_array)
+{
+	uint64_t mask, events = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		events |= ((uint64_t) events_array[i]) << (i * 8);
+
+	print_field("Mask: 0x%16.16" PRIx64, events);
+
+	mask = events;
+
+	for (i = 0; events_table[i].str; i++) {
+		if (events & (((uint64_t) 1) << events_table[i].bit)) {
+			print_field("  %s", events_table[i].str);
+			mask &= ~(((uint64_t) 1) << events_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_EVENT_MASK, "  Unknown mask "
+						"(0x%16.16" PRIx64 ")", mask);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} events_page2_table[] = {
+	{  0, "Physical Link Complete"					},
+	{  1, "Channel Selected"					},
+	{  2, "Disconnection Physical Link Complete"			},
+	{  3, "Physical Link Loss Early Warning"			},
+	{  4, "Physical Link Recovery"					},
+	{  5, "Logical Link Complete"					},
+	{  6, "Disconnection Logical Link Complete"			},
+	{  7, "Flow Specification Modify Complete"			},
+	{  8, "Number of Completed Data Blocks"				},
+	{  9, "AMP Start Test"						},
+	{ 10, "AMP Test End"						},
+	{ 11, "AMP Receiver Report"					},
+	{ 12, "Short Range Mode Change Complete"			},
+	{ 13, "AMP Status Change"					},
+	{ 14, "Triggered Clock Capture"					},
+	{ 15, "Synchronization Train Complete"				},
+	{ 16, "Synchronization Train Received"				},
+	{ 17, "Connectionless Slave Broadcast Receive"			},
+	{ 18, "Connectionless Slave Broadcast Timeout"			},
+	{ 19, "Truncated Page Complete"					},
+	{ 20, "Slave Page Response Timeout"				},
+	{ 21, "Connectionless Slave Broadcast Channel Map Change"	},
+	{ 22, "Inquiry Response Notification"				},
+	{ 23, "Authenticated Payload Timeout Expired"			},
+	{ }
+};
+
+static void print_event_mask_page2(const uint8_t *events_array)
+{
+	uint64_t mask, events = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		events |= ((uint64_t) events_array[i]) << (i * 8);
+
+	print_field("Mask: 0x%16.16" PRIx64, events);
+
+	mask = events;
+
+	for (i = 0; events_page2_table[i].str; i++) {
+		if (events & (((uint64_t) 1) << events_page2_table[i].bit)) {
+			print_field("  %s", events_page2_table[i].str);
+			mask &= ~(((uint64_t) 1) << events_page2_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_EVENT_MASK, "  Unknown mask "
+						"(0x%16.16" PRIx64 ")", mask);
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} events_le_table[] = {
+	{  0, "LE Connection Complete"			},
+	{  1, "LE Advertising Report"			},
+	{  2, "LE Connection Update Complete"		},
+	{  3, "LE Read Remote Used Features Complete"	},
+	{  4, "LE Long Term Key Request"		},
+	{  5, "LE Remote Connection Parameter Request"	},
+	{  6, "LE Data Length Change"			},
+	{  7, "LE Read Local P-256 Public Key Complete"	},
+	{  8, "LE Generate DHKey Complete"		},
+	{  9, "LE Enhanced Connection Complete"		},
+	{ 10, "LE Direct Advertising Report"		},
+	{ }
+};
+
+static void print_event_mask_le(const uint8_t *events_array)
+{
+	uint64_t mask, events = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		events |= ((uint64_t) events_array[i]) << (i * 8);
+
+	print_field("Mask: 0x%16.16" PRIx64, events);
+
+	mask = events;
+
+	for (i = 0; events_le_table[i].str; i++) {
+		if (events & (((uint64_t) 1) << events_le_table[i].bit)) {
+			print_field("  %s", events_le_table[i].str);
+			mask &= ~(((uint64_t) 1) << events_le_table[i].bit);
+		}
+	}
+
+	if (mask)
+		print_text(COLOR_UNKNOWN_EVENT_MASK, "  Unknown mask "
+						"(0x%16.16" PRIx64 ")", mask);
+}
+
+static void print_fec(uint8_t fec)
+{
+	const char *str;
+
+	switch (fec) {
+	case 0x00:
+		str = "Not required";
+		break;
+	case 0x01:
+		str = "Required";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("FEC: %s (0x%02x)", str, fec);
+}
+
+#define BT_EIR_FLAGS			0x01
+#define BT_EIR_UUID16_SOME		0x02
+#define BT_EIR_UUID16_ALL		0x03
+#define BT_EIR_UUID32_SOME		0x04
+#define BT_EIR_UUID32_ALL		0x05
+#define BT_EIR_UUID128_SOME		0x06
+#define BT_EIR_UUID128_ALL		0x07
+#define BT_EIR_NAME_SHORT		0x08
+#define BT_EIR_NAME_COMPLETE		0x09
+#define BT_EIR_TX_POWER			0x0a
+#define BT_EIR_CLASS_OF_DEV		0x0d
+#define BT_EIR_SSP_HASH_P192		0x0e
+#define BT_EIR_SSP_RANDOMIZER_P192	0x0f
+#define BT_EIR_DEVICE_ID		0x10
+#define BT_EIR_SMP_TK			0x10
+#define BT_EIR_SMP_OOB_FLAGS		0x11
+#define BT_EIR_SLAVE_CONN_INTERVAL	0x12
+#define BT_EIR_SERVICE_UUID16		0x14
+#define BT_EIR_SERVICE_UUID128		0x15
+#define BT_EIR_SERVICE_DATA		0x16
+#define BT_EIR_PUBLIC_ADDRESS		0x17
+#define BT_EIR_RANDOM_ADDRESS		0x18
+#define BT_EIR_GAP_APPEARANCE		0x19
+#define BT_EIR_ADVERTISING_INTERVAL	0x1a
+#define BT_EIR_LE_DEVICE_ADDRESS	0x1b
+#define BT_EIR_LE_ROLE			0x1c
+#define BT_EIR_SSP_HASH_P256		0x1d
+#define BT_EIR_SSP_RANDOMIZER_P256	0x1e
+#define BT_EIR_3D_INFO_DATA		0x3d
+#define BT_EIR_MANUFACTURER_DATA	0xff
+
+static void print_manufacturer_apple(const void *data, uint8_t data_len)
+{
+	uint8_t type = *((uint8_t *) data);
+
+	if (data_len < 1)
+		return;
+
+	if (type == 0x01) {
+		char identifier[100];
+
+		snprintf(identifier, sizeof(identifier) - 1, "%s",
+						(const char *) (data + 1));
+
+		print_field("  Identifier: %s", identifier);
+		return;
+	}
+
+	while (data_len > 0) {
+		uint8_t len;
+		const char *str;
+
+		type = *((uint8_t *) data);
+		data++;
+		data_len--;
+
+		if (type == 0x00)
+			continue;
+
+		if (data_len < 1)
+			break;
+
+		switch (type) {
+		case 0x02:
+			str = "iBeacon";
+			break;
+		case 0x05:
+			str = "AirDrop";
+			break;
+		case 0x09:
+			str = "Apple TV";
+			break;
+		default:
+			str = "Unknown";
+			break;
+		}
+
+		print_field("  Type: %s (%u)", str, type);
+
+		len = *((uint8_t *) data);
+		data++;
+		data_len--;
+
+		if (len < 1)
+			continue;
+
+		if (len > data_len)
+			break;
+
+		if (type == 0x02 && len == 0x15) {
+			const uint8_t *uuid;
+			uint16_t minor, major;
+			int8_t tx_power;
+
+			uuid = data;
+			print_field("  UUID: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+				get_le32(&uuid[12]), get_le16(&uuid[10]),
+				get_le16(&uuid[8]), get_le16(&uuid[6]),
+				get_le32(&uuid[2]), get_le16(&uuid[0]));
+
+			major = get_le16(data + 16);
+			minor = get_le16(data + 18);
+			print_field("  Version: %u.%u", major, minor);
+
+			tx_power = *(int8_t *) (data + 20);
+			print_field("  TX power: %d dB", tx_power);
+		} else
+			print_hex_field("  Data", data, len);
+
+		data += len;
+		data_len -= len;
+	}
+
+	packet_hexdump(data, data_len);
+}
+
+static void print_manufacturer_data(const void *data, uint8_t data_len)
+{
+	uint16_t company = get_le16(data);
+
+	packet_print_company("Company", company);
+
+	switch (company) {
+	case 76:
+	case 19456:
+		print_manufacturer_apple(data + 2, data_len - 2);
+		break;
+	default:
+		print_hex_field("  Data", data + 2, data_len - 2);
+		break;
+	}
+}
+
+static void print_device_id(const void *data, uint8_t data_len)
+{
+	uint16_t source, vendor, product, version;
+	char modalias[26], *vendor_str, *product_str;
+	const char *str;
+
+	if (data_len < 8)
+		return;
+
+	source = get_le16(data);
+	vendor = get_le16(data + 2);
+	product = get_le16(data + 4);
+	version = get_le16(data + 6);
+
+	switch (source) {
+	case 0x0001:
+		str = "Bluetooth SIG assigned";
+		sprintf(modalias, "bluetooth:v%04Xp%04Xd%04X",
+						vendor, product, version);
+		break;
+	case 0x0002:
+		str = "USB Implementer's Forum assigned";
+		sprintf(modalias, "usb:v%04Xp%04Xd%04X",
+						vendor, product, version);
+		break;
+	default:
+		str = "Reserved";
+		modalias[0] = '\0';
+		break;
+	}
+
+	print_field("Device ID: %s (0x%4.4x)", str, source);
+
+	if (!hwdb_get_vendor_model(modalias, &vendor_str, &product_str)) {
+		vendor_str = NULL;
+		product_str = NULL;
+	}
+
+	if (source != 0x0001) {
+		if (vendor_str)
+			print_field("  Vendor: %s (0x%4.4x)",
+						vendor_str, vendor);
+		else
+			print_field("  Vendor: 0x%4.4x", vendor);
+	} else
+		packet_print_company("  Vendor", vendor);
+
+	if (product_str)
+		print_field("  Product: %s (0x%4.4x)", product_str, product);
+	else
+		print_field("  Product: 0x%4.4x", product);
+
+	print_field("  Version: %u.%u.%u (0x%4.4x)",
+					(version & 0xff00) >> 8,
+					(version & 0x00f0) >> 4,
+					(version & 0x000f), version);
+
+	free(vendor_str);
+	free(product_str);
+}
+
+static void print_uuid16_list(const char *label, const void *data,
+							uint8_t data_len)
+{
+	uint8_t count = data_len / sizeof(uint16_t);
+	unsigned int i;
+
+	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
+
+	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);
+	}
+}
+
+static void print_uuid32_list(const char *label, const void *data,
+							uint8_t data_len)
+{
+	uint8_t count = data_len / sizeof(uint32_t);
+	unsigned int i;
+
+	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
+
+	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);
+	}
+}
+
+static void print_uuid128_list(const char *label, const void *data,
+							uint8_t data_len)
+{
+	uint8_t count = data_len / 16;
+	unsigned int i;
+
+	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
+
+	for (i = 0; i < count; i++) {
+		const uint8_t *uuid = data + (i * 16);
+
+		print_field("  %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+				get_le32(&uuid[12]), get_le16(&uuid[10]),
+				get_le16(&uuid[8]), get_le16(&uuid[6]),
+				get_le32(&uuid[2]), get_le16(&uuid[0]));
+	}
+}
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} eir_flags_table[] = {
+	{ 0, "LE Limited Discoverable Mode"		},
+	{ 1, "LE General Discoverable Mode"		},
+	{ 2, "BR/EDR Not Supported"			},
+	{ 3, "Simultaneous LE and BR/EDR (Controller)"	},
+	{ 4, "Simultaneous LE and BR/EDR (Host)"	},
+	{ }
+};
+
+static const struct {
+	uint8_t bit;
+	const char *str;
+} eir_3d_table[] = {
+	{ 0, "Association Notification"					},
+	{ 1, "Battery Level Reporting"					},
+	{ 2, "Send Battery Level Report on Start-up Synchronization"	},
+	{ 7, "Factory Test Mode"					},
+	{ }
+};
+
+static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
+{
+	uint16_t len = 0;
+
+	if (eir_len == 0)
+		return;
+
+	while (len < eir_len - 1) {
+		uint8_t field_len = eir[0];
+		const uint8_t *data = &eir[2];
+		uint8_t data_len;
+		char name[239], label[100];
+		uint8_t flags, mask;
+		int i;
+
+		/* Check for the end of EIR */
+		if (field_len == 0)
+			break;
+
+		len += field_len + 1;
+
+		/* Do not continue EIR Data parsing if got incorrect length */
+		if (len > eir_len) {
+			len -= field_len + 1;
+			break;
+		}
+
+		data_len = field_len - 1;
+
+		switch (eir[1]) {
+		case BT_EIR_FLAGS:
+			flags = *data;
+			mask = flags;
+
+			print_field("Flags: 0x%2.2x", flags);
+
+			for (i = 0; eir_flags_table[i].str; i++) {
+				if (flags & (1 << eir_flags_table[i].bit)) {
+					print_field("  %s",
+							eir_flags_table[i].str);
+					mask &= ~(1 << eir_flags_table[i].bit);
+				}
+			}
+
+			if (mask)
+				print_text(COLOR_UNKNOWN_SERVICE_CLASS,
+					"  Unknown flags (0x%2.2x)", mask);
+			break;
+
+		case BT_EIR_UUID16_SOME:
+			if (data_len < sizeof(uint16_t))
+				break;
+			print_uuid16_list("16-bit Service UUIDs (partial)",
+							data, data_len);
+			break;
+
+		case BT_EIR_UUID16_ALL:
+			if (data_len < sizeof(uint16_t))
+				break;
+			print_uuid16_list("16-bit Service UUIDs (complete)",
+							data, data_len);
+			break;
+
+		case BT_EIR_UUID32_SOME:
+			if (data_len < sizeof(uint32_t))
+				break;
+			print_uuid32_list("32-bit Service UUIDs (partial)",
+							data, data_len);
+			break;
+
+		case BT_EIR_UUID32_ALL:
+			if (data_len < sizeof(uint32_t))
+				break;
+			print_uuid32_list("32-bit Service UUIDs (complete)",
+							data, data_len);
+			break;
+
+		case BT_EIR_UUID128_SOME:
+			if (data_len < 16)
+				break;
+			print_uuid128_list("128-bit Service UUIDs (partial)",
+								data, data_len);
+			break;
+
+		case BT_EIR_UUID128_ALL:
+			if (data_len < 16)
+				break;
+			print_uuid128_list("128-bit Service UUIDs (complete)",
+								data, data_len);
+			break;
+
+		case BT_EIR_NAME_SHORT:
+			memset(name, 0, sizeof(name));
+			memcpy(name, data, data_len);
+			print_field("Name (short): %s", name);
+			break;
+
+		case BT_EIR_NAME_COMPLETE:
+			memset(name, 0, sizeof(name));
+			memcpy(name, data, data_len);
+			print_field("Name (complete): %s", name);
+			break;
+
+		case BT_EIR_TX_POWER:
+			if (data_len < 1)
+				break;
+			print_field("TX power: %d dBm", (int8_t) *data);
+			break;
+
+		case BT_EIR_CLASS_OF_DEV:
+			if (data_len < 3)
+				break;
+			print_dev_class(data);
+			break;
+
+		case BT_EIR_SSP_HASH_P192:
+			if (data_len < 16)
+				break;
+			print_hash_p192(data);
+			break;
+
+		case BT_EIR_SSP_RANDOMIZER_P192:
+			if (data_len < 16)
+				break;
+			print_randomizer_p192(data);
+			break;
+
+		case BT_EIR_DEVICE_ID:
+			/* SMP TK has the same value as Device ID */
+			if (le)
+				print_hex_field("SMP TK", data, data_len);
+			else if (data_len >= 8)
+				print_device_id(data, data_len);
+			break;
+
+		case BT_EIR_SMP_OOB_FLAGS:
+			print_field("SMP OOB Flags: 0x%2.2x", *data);
+			break;
+
+		case BT_EIR_SLAVE_CONN_INTERVAL:
+			if (data_len < 4)
+				break;
+			print_field("Slave Conn. Interval: 0x%4.4x - 0x%4.4x",
+							get_le16(&data[0]),
+							get_le16(&data[2]));
+			break;
+
+		case BT_EIR_SERVICE_UUID16:
+			if (data_len < sizeof(uint16_t))
+				break;
+			print_uuid16_list("16-bit Service UUIDs",
+							data, data_len);
+			break;
+
+		case BT_EIR_SERVICE_UUID128:
+			if (data_len < 16)
+				break;
+			print_uuid128_list("128-bit Service UUIDs",
+							data, data_len);
+			break;
+
+		case BT_EIR_SERVICE_DATA:
+			if (data_len < 2)
+				break;
+			sprintf(label, "Service Data (UUID 0x%4.4x)",
+							get_le16(&data[0]));
+			print_hex_field(label, &data[2], data_len - 2);
+			break;
+
+		case BT_EIR_RANDOM_ADDRESS:
+			if (data_len < 6)
+				break;
+			print_addr("Random Address", data, 0x01);
+			break;
+
+		case BT_EIR_PUBLIC_ADDRESS:
+			if (data_len < 6)
+				break;
+			print_addr("Public Address", data, 0x00);
+			break;
+
+		case BT_EIR_GAP_APPEARANCE:
+			if (data_len < 2)
+				break;
+			print_appearance(get_le16(data));
+			break;
+
+		case BT_EIR_SSP_HASH_P256:
+			if (data_len < 16)
+				break;
+			print_hash_p256(data);
+			break;
+
+		case BT_EIR_SSP_RANDOMIZER_P256:
+			if (data_len < 16)
+				break;
+			print_randomizer_p256(data);
+			break;
+
+		case BT_EIR_3D_INFO_DATA:
+			print_hex_field("3D Information Data", data, data_len);
+			if (data_len < 2)
+				break;
+
+			flags = *data;
+			mask = flags;
+
+			print_field("  Features: 0x%2.2x", flags);
+
+			for (i = 0; eir_3d_table[i].str; i++) {
+				if (flags & (1 << eir_3d_table[i].bit)) {
+					print_field("    %s",
+							eir_3d_table[i].str);
+					mask &= ~(1 << eir_3d_table[i].bit);
+				}
+			}
+
+			if (mask)
+				print_text(COLOR_UNKNOWN_FEATURE_BIT,
+					"      Unknown features (0x%2.2x)", mask);
+
+			print_field("  Path Loss Threshold: %d", data[1]);
+			break;
+
+		case BT_EIR_MANUFACTURER_DATA:
+			if (data_len < 2)
+				break;
+			print_manufacturer_data(data, data_len);
+			break;
+
+		default:
+			sprintf(label, "Unknown EIR field 0x%2.2x", eir[1]);
+			print_hex_field(label, data, data_len);
+			break;
+		}
+
+		eir += field_len + 1;
+	}
+
+	if (len < eir_len && eir[0] != 0)
+		packet_hexdump(eir, eir_len - len);
+}
+
+void packet_print_addr(const char *label, const void *data, bool random)
+{
+	print_addr(label ? : "Address", data, random ? 0x01 : 0x00);
+}
+
+void packet_print_ad(const void *data, uint8_t size)
+{
+	print_eir(data, size, true);
+}
+
+struct broadcast_message {
+	uint32_t frame_sync_instant;
+	uint16_t bluetooth_clock_phase;
+	uint16_t left_open_offset;
+	uint16_t left_close_offset;
+	uint16_t right_open_offset;
+	uint16_t right_close_offset;
+	uint16_t frame_sync_period;
+	uint8_t  frame_sync_period_fraction;
+} __attribute__ ((packed));
+
+static void print_3d_broadcast(const void *data, uint8_t size)
+{
+	const struct broadcast_message *msg = data;
+	uint32_t instant;
+	uint16_t left_open, left_close, right_open, right_close;
+	uint16_t phase, period;
+	uint8_t period_frac;
+	bool mode;
+
+	instant = le32_to_cpu(msg->frame_sync_instant);
+	mode = !!(instant & 0x40000000);
+	phase = le16_to_cpu(msg->bluetooth_clock_phase);
+	left_open = le16_to_cpu(msg->left_open_offset);
+	left_close = le16_to_cpu(msg->left_close_offset);
+	right_open = le16_to_cpu(msg->right_open_offset);
+	right_close = le16_to_cpu(msg->right_close_offset);
+	period = le16_to_cpu(msg->frame_sync_period);
+	period_frac = msg->frame_sync_period_fraction;
+
+	print_field("  Frame sync instant: 0x%8.8x", instant & 0x7fffffff);
+	print_field("  Video mode: %s (%d)", mode ? "Dual View" : "3D", mode);
+	print_field("  Bluetooth clock phase: %d usec (0x%4.4x)",
+						phase, phase);
+	print_field("  Left lense shutter open offset: %d usec (0x%4.4x)",
+						left_open, left_open);
+	print_field("  Left lense shutter close offset: %d usec (0x%4.4x)",
+						left_close, left_close);
+	print_field("  Right lense shutter open offset: %d usec (0x%4.4x)",
+						right_open, right_open);
+	print_field("  Right lense shutter close offset: %d usec (0x%4.4x)",
+						right_close, right_close);
+	print_field("  Frame sync period: %d.%d usec (0x%4.4x 0x%2.2x)",
+						period, period_frac * 256,
+						period, period_frac);
+}
+
+void packet_hexdump(const unsigned char *buf, uint16_t len)
+{
+	static const char hexdigits[] = "0123456789abcdef";
+	char str[68];
+	uint16_t i;
+
+	if (!len)
+		return;
+
+	for (i = 0; i < len; i++) {
+		str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+		str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+		str[((i % 16) * 3) + 2] = ' ';
+		str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+		if ((i + 1) % 16 == 0) {
+			str[47] = ' ';
+			str[48] = ' ';
+			str[65] = '\0';
+			print_text(COLOR_WHITE, "%s", str);
+			str[0] = ' ';
+		}
+	}
+
+	if (i % 16 > 0) {
+		uint16_t j;
+		for (j = (i % 16); j < 16; j++) {
+			str[(j * 3) + 0] = ' ';
+			str[(j * 3) + 1] = ' ';
+			str[(j * 3) + 2] = ' ';
+			str[j + 49] = ' ';
+		}
+		str[47] = ' ';
+		str[48] = ' ';
+		str[65] = '\0';
+		print_text(COLOR_WHITE, "%s", str);
+	}
+}
+
+void packet_control(struct timeval *tv, struct ucred *cred,
+					uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size)
+{
+	if (index_filter && index_number != index)
+		return;
+
+	control_message(opcode, data, size);
+}
+
+static int addr2str(const uint8_t *addr, char *str)
+{
+	return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+			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)
+{
+	const struct btsnoop_opcode_new_index *ni;
+	const struct btsnoop_opcode_index_info *ii;
+	const struct btsnoop_opcode_user_logging *ul;
+	char str[18], extra_str[24];
+	uint16_t manufacturer;
+	const char *ident;
+
+	if (index_filter && index_number != index)
+		return;
+
+	index_current = index;
+
+	if (tv && time_offset == ((time_t) -1))
+		time_offset = tv->tv_sec;
+
+	switch (opcode) {
+	case BTSNOOP_OPCODE_NEW_INDEX:
+		ni = data;
+
+		if (index < MAX_INDEX) {
+			index_list[index].type = ni->type;
+			memcpy(index_list[index].bdaddr, ni->bdaddr, 6);
+			index_list[index].manufacturer = UNKNOWN_MANUFACTURER;
+		}
+
+		addr2str(ni->bdaddr, str);
+		packet_new_index(tv, index, str, ni->type, ni->bus, ni->name);
+		break;
+	case BTSNOOP_OPCODE_DEL_INDEX:
+		if (index < MAX_INDEX)
+			addr2str(index_list[index].bdaddr, str);
+		else
+			sprintf(str, "00:00:00:00:00:00");
+
+		packet_del_index(tv, index, str);
+		break;
+	case BTSNOOP_OPCODE_COMMAND_PKT:
+		packet_hci_command(tv, cred, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		packet_hci_event(tv, cred, index, data, size);
+		break;
+	case BTSNOOP_OPCODE_ACL_TX_PKT:
+		packet_hci_acldata(tv, cred, index, false, data, size);
+		break;
+	case BTSNOOP_OPCODE_ACL_RX_PKT:
+		packet_hci_acldata(tv, cred, index, true, data, size);
+		break;
+	case BTSNOOP_OPCODE_SCO_TX_PKT:
+		packet_hci_scodata(tv, cred, index, false, data, size);
+		break;
+	case BTSNOOP_OPCODE_SCO_RX_PKT:
+		packet_hci_scodata(tv, cred, index, true, data, size);
+		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+		if (index < MAX_INDEX)
+			addr2str(index_list[index].bdaddr, str);
+		else
+			sprintf(str, "00:00:00:00:00:00");
+
+		packet_open_index(tv, index, str);
+		break;
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		if (index < MAX_INDEX)
+			addr2str(index_list[index].bdaddr, str);
+		else
+			sprintf(str, "00:00:00:00:00:00");
+
+		packet_close_index(tv, index, str);
+		break;
+	case BTSNOOP_OPCODE_INDEX_INFO:
+		ii = data;
+		manufacturer = le16_to_cpu(ii->manufacturer);
+
+		if (index < MAX_INDEX) {
+			memcpy(index_list[index].bdaddr, ii->bdaddr, 6);
+			index_list[index].manufacturer = manufacturer;
+		}
+
+		addr2str(ii->bdaddr, str);
+		packet_index_info(tv, index, str, manufacturer);
+		break;
+	case BTSNOOP_OPCODE_VENDOR_DIAG:
+		if (index < MAX_INDEX)
+			manufacturer = index_list[index].manufacturer;
+		else
+			manufacturer = UNKNOWN_MANUFACTURER;
+
+		packet_vendor_diag(tv, index, manufacturer, data, size);
+		break;
+	case BTSNOOP_OPCODE_SYSTEM_NOTE:
+		packet_system_note(tv, cred, index, data);
+		break;
+	case BTSNOOP_OPCODE_USER_LOGGING:
+		ul = data;
+		ident = ul->ident_len ? data + sizeof(*ul) : NULL;
+
+		packet_user_logging(tv, cred, index, ul->priority, ident,
+					data + sizeof(*ul) + ul->ident_len);
+		break;
+	default:
+		sprintf(extra_str, "(code %d len %d)", opcode, size);
+		print_packet(tv, cred, index, '*', COLOR_ERROR,
+					"Unknown packet", NULL, extra_str);
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+void packet_simulator(struct timeval *tv, uint16_t frequency,
+					const void *data, uint16_t size)
+{
+	char str[10];
+
+	if (tv && time_offset == ((time_t) -1))
+		time_offset = tv->tv_sec;
+
+	sprintf(str, "%u MHz", frequency);
+
+	print_packet(tv, NULL, 0, '*', COLOR_PHY_PACKET,
+					"Physical packet:", NULL, str);
+
+	ll_packet(frequency, data, size, false);
+}
+
+static void null_cmd(const void *data, uint8_t size)
+{
+}
+
+static void status_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = *((const uint8_t *) data);
+
+	print_status(status);
+}
+
+static void status_bdaddr_rsp(const void *data, uint8_t size)
+{
+	uint8_t status = *((const uint8_t *) data);
+
+	print_status(status);
+	print_bdaddr(data + 1);
+}
+
+static void inquiry_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_inquiry *cmd = data;
+
+	print_iac(cmd->lap);
+	print_field("Length: %.2fs (0x%2.2x)",
+				cmd->length * 1.28, cmd->length);
+	print_num_resp(cmd->num_resp);
+}
+
+static void periodic_inquiry_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_periodic_inquiry *cmd = data;
+
+	print_field("Max period: %.2fs (0x%2.2x)",
+				cmd->max_period * 1.28, cmd->max_period);
+	print_field("Min period: %.2fs (0x%2.2x)",
+				cmd->min_period * 1.28, cmd->min_period);
+	print_iac(cmd->lap);
+	print_field("Length: %.2fs (0x%2.2x)",
+				cmd->length * 1.28, cmd->length);
+	print_num_resp(cmd->num_resp);
+}
+
+static void create_conn_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_create_conn *cmd = data;
+	const char *str;
+
+	print_bdaddr(cmd->bdaddr);
+	print_pkt_type(cmd->pkt_type);
+	print_pscan_rep_mode(cmd->pscan_rep_mode);
+	print_pscan_mode(cmd->pscan_mode);
+	print_clock_offset(cmd->clock_offset);
+
+	switch (cmd->role_switch) {
+	case 0x00:
+		str = "Stay master";
+		break;
+	case 0x01:
+		str = "Allow slave";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Role switch: %s (0x%2.2x)", str, cmd->role_switch);
+}
+
+static void disconnect_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_disconnect *cmd = data;
+
+	print_handle(cmd->handle);
+	print_reason(cmd->reason);
+}
+
+static void add_sco_conn_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_add_sco_conn *cmd = data;
+
+	print_handle(cmd->handle);
+	print_pkt_type_sco(cmd->pkt_type);
+}
+
+static void create_conn_cancel_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_create_conn_cancel *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void accept_conn_request_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_accept_conn_request *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_role(cmd->role);
+}
+
+static void reject_conn_request_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_reject_conn_request *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_reason(cmd->reason);
+}
+
+static void link_key_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_link_key_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_link_key(cmd->link_key);
+}
+
+static void link_key_request_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_link_key_request_neg_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void pin_code_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_pin_code_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_field("PIN length: %d", cmd->pin_len);
+	print_pin_code(cmd->pin_code, cmd->pin_len);
+}
+
+static void pin_code_request_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_pin_code_request_neg_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void change_conn_pkt_type_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_change_conn_pkt_type *cmd = data;
+
+	print_handle(cmd->handle);
+	print_pkt_type(cmd->pkt_type);
+}
+
+static void auth_requested_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_auth_requested *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+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);
+}
+
+static void change_conn_link_key_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_change_conn_link_key *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void master_link_key_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_master_link_key *cmd = data;
+
+	print_key_flag(cmd->key_flag);
+}
+
+static void remote_name_request_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_remote_name_request *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_pscan_rep_mode(cmd->pscan_rep_mode);
+	print_pscan_mode(cmd->pscan_mode);
+	print_clock_offset(cmd->clock_offset);
+}
+
+static void remote_name_request_cancel_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_remote_name_request_cancel *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void read_remote_features_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_remote_features *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_remote_ext_features_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_remote_ext_features *cmd = data;
+
+	print_handle(cmd->handle);
+	print_field("Page: %d", cmd->page);
+}
+
+static void read_remote_version_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_remote_version *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_clock_offset_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_clock_offset *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_lmp_handle_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_lmp_handle *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_lmp_handle_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_lmp_handle *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_field("LMP handle: %d", rsp->lmp_handle);
+	print_field("Reserved: %d", le32_to_cpu(rsp->reserved));
+}
+
+static void setup_sync_conn_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_setup_sync_conn *cmd = data;
+
+	print_handle(cmd->handle);
+	print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+	print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+	print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
+	print_voice_setting(cmd->voice_setting);
+	print_retransmission_effort(cmd->retrans_effort);
+	print_pkt_type_sco(cmd->pkt_type);
+}
+
+static void accept_sync_conn_request_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_accept_sync_conn_request *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+	print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+	print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
+	print_voice_setting(cmd->voice_setting);
+	print_retransmission_effort(cmd->retrans_effort);
+	print_pkt_type_sco(cmd->pkt_type);
+}
+
+static void reject_sync_conn_request_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_reject_sync_conn_request *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_reason(cmd->reason);
+}
+
+static void io_capability_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_io_capability_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_io_capability(cmd->capability);
+	print_oob_data(cmd->oob_data);
+	print_authentication(cmd->authentication);
+}
+
+static void user_confirm_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_user_confirm_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void user_confirm_request_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_user_confirm_request_neg_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void user_passkey_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_user_passkey_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_passkey(cmd->passkey);
+}
+
+static void user_passkey_request_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_user_passkey_request_neg_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void remote_oob_data_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_remote_oob_data_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_hash_p192(cmd->hash);
+	print_randomizer_p192(cmd->randomizer);
+}
+
+static void remote_oob_data_request_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_remote_oob_data_request_neg_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void io_capability_request_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_io_capability_request_neg_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_reason(cmd->reason);
+}
+
+static void create_phy_link_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_create_phy_link *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_key_len(cmd->key_len);
+	print_key_type(cmd->key_type);
+
+	packet_hexdump(data + 3, size - 3);
+}
+
+static void accept_phy_link_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_accept_phy_link *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_key_len(cmd->key_len);
+	print_key_type(cmd->key_type);
+
+	packet_hexdump(data + 3, size - 3);
+}
+
+static void disconn_phy_link_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_disconn_phy_link *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_reason(cmd->reason);
+}
+
+static void create_logic_link_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_create_logic_link *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_flow_spec("TX", cmd->tx_flow_spec);
+	print_flow_spec("RX", cmd->rx_flow_spec);
+}
+
+static void accept_logic_link_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_accept_logic_link *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_flow_spec("TX", cmd->tx_flow_spec);
+	print_flow_spec("RX", cmd->rx_flow_spec);
+}
+
+static void disconn_logic_link_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_disconn_logic_link *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void logic_link_cancel_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_logic_link_cancel *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_field("TX flow spec: 0x%2.2x", cmd->flow_spec);
+}
+
+static void logic_link_cancel_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_logic_link_cancel *rsp = data;
+
+	print_status(rsp->status);
+	print_phy_handle(rsp->phy_handle);
+	print_field("TX flow spec: 0x%2.2x", rsp->flow_spec);
+}
+
+static void flow_spec_modify_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_flow_spec_modify *cmd = data;
+
+	print_handle(cmd->handle);
+	print_flow_spec("TX", cmd->tx_flow_spec);
+	print_flow_spec("RX", cmd->rx_flow_spec);
+}
+
+static void enhanced_setup_sync_conn_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data;
+
+	print_handle(cmd->handle);
+	print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+	print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+
+	/* TODO */
+
+	print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
+	print_pkt_type_sco(cmd->pkt_type);
+	print_retransmission_effort(cmd->retrans_effort);
+}
+
+static void enhanced_accept_sync_conn_request_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_enhanced_accept_sync_conn_request *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+	print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+
+	/* TODO */
+
+	print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
+	print_pkt_type_sco(cmd->pkt_type);
+	print_retransmission_effort(cmd->retrans_effort);
+}
+
+static void truncated_page_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_truncated_page *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_pscan_rep_mode(cmd->pscan_rep_mode);
+	print_clock_offset(cmd->clock_offset);
+}
+
+static void truncated_page_cancel_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_truncated_page_cancel *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+}
+
+static void set_slave_broadcast_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_slave_broadcast *cmd = data;
+
+	print_field("Enable: 0x%2.2x", cmd->enable);
+	print_lt_addr(cmd->lt_addr);
+	print_lpo_allowed(cmd->lpo_allowed);
+	print_pkt_type(cmd->pkt_type);
+	print_slot_625("Min interval", cmd->min_interval);
+	print_slot_625("Max interval", cmd->max_interval);
+	print_slot_625("Supervision timeout", cmd->timeout);
+}
+
+static void set_slave_broadcast_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_set_slave_broadcast *rsp = data;
+
+	print_status(rsp->status);
+	print_lt_addr(rsp->lt_addr);
+	print_interval(rsp->interval);
+}
+
+static void set_slave_broadcast_receive_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_slave_broadcast_receive *cmd = data;
+
+	print_field("Enable: 0x%2.2x", cmd->enable);
+	print_bdaddr(cmd->bdaddr);
+	print_lt_addr(cmd->lt_addr);
+	print_interval(cmd->interval);
+	print_field("Offset: 0x%8.8x", le32_to_cpu(cmd->offset));
+	print_field("Next broadcast instant: 0x%4.4x",
+					le16_to_cpu(cmd->instant));
+	print_slot_625("Supervision timeout", cmd->timeout);
+	print_field("Remote timing accuracy: %d ppm", cmd->accuracy);
+	print_field("Skip: 0x%2.2x", cmd->skip);
+	print_pkt_type(cmd->pkt_type);
+	print_channel_map(cmd->map);
+}
+
+static void set_slave_broadcast_receive_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_set_slave_broadcast_receive *rsp = data;
+
+	print_status(rsp->status);
+	print_bdaddr(rsp->bdaddr);
+	print_lt_addr(rsp->lt_addr);
+}
+
+static void receive_sync_train_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_receive_sync_train *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_timeout(cmd->timeout);
+	print_window(cmd->window);
+	print_interval(cmd->interval);
+}
+
+static void remote_oob_ext_data_request_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_remote_oob_ext_data_request_reply *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_hash_p192(cmd->hash192);
+	print_randomizer_p192(cmd->randomizer192);
+	print_hash_p256(cmd->hash256);
+	print_randomizer_p256(cmd->randomizer256);
+}
+
+static void hold_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_hold_mode *cmd = data;
+
+	print_handle(cmd->handle);
+	print_slot_625("Hold max interval", cmd->max_interval);
+	print_slot_625("Hold min interval", cmd->min_interval);
+}
+
+static void sniff_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_sniff_mode *cmd = data;
+
+	print_handle(cmd->handle);
+	print_slot_625("Sniff max interval", cmd->max_interval);
+	print_slot_625("Sniff min interval", cmd->min_interval);
+	print_slot_125("Sniff attempt", cmd->attempt);
+	print_slot_125("Sniff timeout", cmd->timeout);
+}
+
+static void exit_sniff_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_exit_sniff_mode *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void park_state_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_park_state *cmd = data;
+
+	print_handle(cmd->handle);
+	print_slot_625("Beacon max interval", cmd->max_interval);
+	print_slot_625("Beacon min interval", cmd->min_interval);
+}
+
+static void exit_park_state_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_exit_park_state *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void qos_setup_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_qos_setup *cmd = data;
+
+	print_handle(cmd->handle);
+	print_field("Flags: 0x%2.2x", cmd->flags);
+
+	print_service_type(cmd->service_type);
+
+	print_field("Token rate: %d", le32_to_cpu(cmd->token_rate));
+	print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth));
+	print_field("Latency: %d", le32_to_cpu(cmd->latency));
+	print_field("Delay variation: %d", le32_to_cpu(cmd->delay_variation));
+}
+
+static void role_discovery_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_role_discovery *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void role_discovery_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_role_discovery *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_role(rsp->role);
+}
+
+static void switch_role_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_switch_role *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_role(cmd->role);
+}
+
+static void read_link_policy_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_link_policy *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_link_policy_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_link_policy *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_link_policy(rsp->policy);
+}
+
+static void write_link_policy_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_link_policy *cmd = data;
+
+	print_handle(cmd->handle);
+	print_link_policy(cmd->policy);
+}
+
+static void write_link_policy_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_link_policy *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void read_default_link_policy_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_default_link_policy *rsp = data;
+
+	print_status(rsp->status);
+	print_link_policy(rsp->policy);
+}
+
+static void write_default_link_policy_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_default_link_policy *cmd = data;
+
+	print_link_policy(cmd->policy);
+}
+
+static void flow_spec_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_flow_spec *cmd = data;
+
+	print_handle(cmd->handle);
+	print_field("Flags: 0x%2.2x", cmd->flags);
+
+	print_flow_direction(cmd->direction);
+	print_service_type(cmd->service_type);
+
+	print_field("Token rate: %d", le32_to_cpu(cmd->token_rate));
+	print_field("Token bucket size: %d",
+					le32_to_cpu(cmd->token_bucket_size));
+	print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth));
+	print_field("Access latency: %d", le32_to_cpu(cmd->access_latency));
+}
+
+static void sniff_subrating_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_sniff_subrating *cmd = data;
+
+	print_handle(cmd->handle);
+	print_slot_625("Max latency", cmd->max_latency);
+	print_slot_625("Min remote timeout", cmd->min_remote_timeout);
+	print_slot_625("Min local timeout", cmd->min_local_timeout);
+}
+
+static void sniff_subrating_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_sniff_subrating *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void set_event_mask_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_event_mask *cmd = data;
+
+	print_event_mask(cmd->mask);
+}
+
+static void set_event_filter_cmd(const void *data, uint8_t size)
+{
+	uint8_t type = *((const uint8_t *) data);
+	uint8_t filter;
+	const char *str;
+
+	switch (type) {
+	case 0x00:
+		str = "Clear All Filters";
+		break;
+	case 0x01:
+		str = "Inquiry Result";
+		break;
+	case 0x02:
+		str = "Connection Setup";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, type);
+
+	switch (type) {
+	case 0x00:
+		if (size > 1) {
+			print_text(COLOR_ERROR, "  invalid parameter size");
+			packet_hexdump(data + 1, size - 1);
+		}
+		break;
+
+	case 0x01:
+		filter = *((const uint8_t *) (data + 1));
+
+		switch (filter) {
+		case 0x00:
+			str = "Return responses from all devices";
+			break;
+		case 0x01:
+			str = "Device with specific Class of Device";
+			break;
+		case 0x02:
+			str = "Device with specific BD_ADDR";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("Filter: %s (0x%2.2x)", str, filter);
+		packet_hexdump(data + 2, size - 2);
+		break;
+
+	case 0x02:
+		filter = *((const uint8_t *) (data + 1));
+
+		switch (filter) {
+		case 0x00:
+			str = "Allow connections all devices";
+			break;
+		case 0x01:
+			str = "Allow connections with specific Class of Device";
+			break;
+		case 0x02:
+			str = "Allow connections with specific BD_ADDR";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("Filter: %s (0x%2.2x)", str, filter);
+		packet_hexdump(data + 2, size - 2);
+		break;
+
+	default:
+		filter = *((const uint8_t *) (data + 1));
+
+		print_field("Filter: Reserved (0x%2.2x)", filter);
+		packet_hexdump(data + 2, size - 2);
+		break;
+	}
+}
+
+static void flush_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_flush *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void flush_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_flush *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void read_pin_type_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_pin_type *rsp = data;
+
+	print_status(rsp->status);
+	print_pin_type(rsp->pin_type);
+}
+
+static void write_pin_type_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_pin_type *cmd = data;
+
+	print_pin_type(cmd->pin_type);
+}
+
+static void read_stored_link_key_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_stored_link_key *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_field("Read all: 0x%2.2x", cmd->read_all);
+}
+
+static void read_stored_link_key_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_stored_link_key *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Max num keys: %d", le16_to_cpu(rsp->max_num_keys));
+	print_field("Num keys: %d", le16_to_cpu(rsp->num_keys));
+}
+
+static void write_stored_link_key_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_stored_link_key *cmd = data;
+
+	print_field("Num keys: %d", cmd->num_keys);
+
+	packet_hexdump(data + 1, size - 1);
+}
+
+static void write_stored_link_key_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_stored_link_key *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Num keys: %d", rsp->num_keys);
+}
+
+static void delete_stored_link_key_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_delete_stored_link_key *cmd = data;
+
+	print_bdaddr(cmd->bdaddr);
+	print_field("Delete all: 0x%2.2x", cmd->delete_all);
+}
+
+static void delete_stored_link_key_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_delete_stored_link_key *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Num keys: %d", le16_to_cpu(rsp->num_keys));
+}
+
+static void write_local_name_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_local_name *cmd = data;
+
+	print_name(cmd->name);
+}
+
+static void read_local_name_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_name *rsp = data;
+
+	print_status(rsp->status);
+	print_name(rsp->name);
+}
+
+static void read_conn_accept_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_conn_accept_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_timeout(rsp->timeout);
+}
+
+static void write_conn_accept_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_conn_accept_timeout *cmd = data;
+
+	print_timeout(cmd->timeout);
+}
+
+static void read_page_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_page_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_timeout(rsp->timeout);
+}
+
+static void write_page_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_page_timeout *cmd = data;
+
+	print_timeout(cmd->timeout);
+}
+
+static void read_scan_enable_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_scan_enable *rsp = data;
+
+	print_status(rsp->status);
+	print_scan_enable(rsp->enable);
+}
+
+static void write_scan_enable_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_scan_enable *cmd = data;
+
+	print_scan_enable(cmd->enable);
+}
+
+static void read_page_scan_activity_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_page_scan_activity *rsp = data;
+
+	print_status(rsp->status);
+	print_interval(rsp->interval);
+	print_window(rsp->window);
+}
+
+static void write_page_scan_activity_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_page_scan_activity *cmd = data;
+
+	print_interval(cmd->interval);
+	print_window(cmd->window);
+}
+
+static void read_inquiry_scan_activity_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_inquiry_scan_activity *rsp = data;
+
+	print_status(rsp->status);
+	print_interval(rsp->interval);
+	print_window(rsp->window);
+}
+
+static void write_inquiry_scan_activity_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_inquiry_scan_activity *cmd = data;
+
+	print_interval(cmd->interval);
+	print_window(cmd->window);
+}
+
+static void read_auth_enable_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_auth_enable *rsp = data;
+
+	print_status(rsp->status);
+	print_auth_enable(rsp->enable);
+}
+
+static void write_auth_enable_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_auth_enable *cmd = data;
+
+	print_auth_enable(cmd->enable);
+}
+
+static void read_encrypt_mode_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_encrypt_mode *rsp = data;
+
+	print_status(rsp->status);
+	print_encrypt_mode(rsp->mode);
+}
+
+static void write_encrypt_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_encrypt_mode *cmd = data;
+
+	print_encrypt_mode(cmd->mode);
+}
+
+static void read_class_of_dev_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_class_of_dev *rsp = data;
+
+	print_status(rsp->status);
+	print_dev_class(rsp->dev_class);
+}
+
+static void write_class_of_dev_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_class_of_dev *cmd = data;
+
+	print_dev_class(cmd->dev_class);
+}
+
+static void read_voice_setting_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_voice_setting *rsp = data;
+
+	print_status(rsp->status);
+	print_voice_setting(rsp->setting);
+}
+
+static void write_voice_setting_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_voice_setting *cmd = data;
+
+	print_voice_setting(cmd->setting);
+}
+
+static void read_auto_flush_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_auto_flush_timeout *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_auto_flush_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_auto_flush_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_flush_timeout(rsp->timeout);
+}
+
+static void write_auto_flush_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_auto_flush_timeout *cmd = data;
+
+	print_handle(cmd->handle);
+	print_flush_timeout(cmd->timeout);
+}
+
+static void write_auto_flush_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_auto_flush_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void read_num_broadcast_retrans_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_num_broadcast_retrans *rsp = data;
+
+	print_status(rsp->status);
+	print_num_broadcast_retrans(rsp->num_retrans);
+}
+
+static void write_num_broadcast_retrans_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_num_broadcast_retrans *cmd = data;
+
+	print_num_broadcast_retrans(cmd->num_retrans);
+}
+
+static void read_hold_mode_activity_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_hold_mode_activity *rsp = data;
+
+	print_status(rsp->status);
+	print_hold_mode_activity(rsp->activity);
+}
+
+static void write_hold_mode_activity_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_hold_mode_activity *cmd = data;
+
+	print_hold_mode_activity(cmd->activity);
+}
+
+static void read_tx_power_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_tx_power *cmd = data;
+
+	print_handle(cmd->handle);
+	print_power_type(cmd->type);
+}
+
+static void read_tx_power_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_tx_power *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_power_level(rsp->level, NULL);
+}
+
+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);
+}
+
+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);
+}
+
+static void set_host_flow_control_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_host_flow_control *cmd = data;
+
+	print_host_flow_control(cmd->enable);
+}
+
+static void host_buffer_size_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_host_buffer_size *cmd = data;
+
+	print_field("ACL MTU: %-4d ACL max packet: %d",
+					le16_to_cpu(cmd->acl_mtu),
+					le16_to_cpu(cmd->acl_max_pkt));
+	print_field("SCO MTU: %-4d SCO max packet: %d",
+					cmd->sco_mtu,
+					le16_to_cpu(cmd->sco_max_pkt));
+}
+
+static void host_num_completed_packets_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_host_num_completed_packets *cmd = data;
+
+	print_field("Num handles: %d", cmd->num_handles);
+	print_handle(cmd->handle);
+	print_field("Count: %d", le16_to_cpu(cmd->count));
+
+	if (size > sizeof(*cmd))
+		packet_hexdump(data + sizeof(*cmd), size - sizeof(*cmd));
+}
+
+static void read_link_supv_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_link_supv_timeout *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_link_supv_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_link_supv_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_timeout(rsp->timeout);
+}
+
+static void write_link_supv_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_link_supv_timeout *cmd = data;
+
+	print_handle(cmd->handle);
+	print_timeout(cmd->timeout);
+}
+
+static void write_link_supv_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_link_supv_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void read_num_supported_iac_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_num_supported_iac *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Number of IAC: %d", rsp->num_iac);
+}
+
+static void read_current_iac_lap_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_current_iac_lap *rsp = data;
+	uint8_t i;
+
+	print_status(rsp->status);
+	print_field("Number of IAC: %d", rsp->num_iac);
+
+	for (i = 0; i < rsp->num_iac; i++)
+		print_iac(rsp->iac_lap + (i * 3));
+}
+
+static void write_current_iac_lap_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_current_iac_lap *cmd = data;
+	uint8_t i;
+
+	print_field("Number of IAC: %d", cmd->num_iac);
+
+	for (i = 0; i < cmd->num_iac; i++)
+		print_iac(cmd->iac_lap + (i * 3));
+}
+
+static void read_page_scan_period_mode_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_page_scan_period_mode *rsp = data;
+
+	print_status(rsp->status);
+	print_pscan_period_mode(rsp->mode);
+}
+
+static void write_page_scan_period_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_page_scan_period_mode *cmd = data;
+
+	print_pscan_period_mode(cmd->mode);
+}
+
+static void read_page_scan_mode_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_page_scan_mode *rsp = data;
+
+	print_status(rsp->status);
+	print_pscan_mode(rsp->mode);
+}
+
+static void write_page_scan_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_page_scan_mode *cmd = data;
+
+	print_pscan_mode(cmd->mode);
+}
+
+static void set_afh_host_classification_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_afh_host_classification *cmd = data;
+
+	print_channel_map(cmd->map);
+}
+
+static void read_inquiry_scan_type_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_inquiry_scan_type *rsp = data;
+
+	print_status(rsp->status);
+	print_inquiry_scan_type(rsp->type);
+}
+
+static void write_inquiry_scan_type_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_inquiry_scan_type *cmd = data;
+
+	print_inquiry_scan_type(cmd->type);
+}
+
+static void read_inquiry_mode_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_inquiry_mode *rsp = data;
+
+	print_status(rsp->status);
+	print_inquiry_mode(rsp->mode);
+}
+
+static void write_inquiry_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_inquiry_mode *cmd = data;
+
+	print_inquiry_mode(cmd->mode);
+}
+
+static void read_page_scan_type_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_page_scan_type *rsp = data;
+
+	print_status(rsp->status);
+	print_pscan_type(rsp->type);
+}
+
+static void write_page_scan_type_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_page_scan_type *cmd = data;
+
+	print_pscan_type(cmd->type);
+}
+
+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);
+}
+
+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);
+}
+
+static void read_ext_inquiry_response_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_ext_inquiry_response *rsp = data;
+
+	print_status(rsp->status);
+	print_fec(rsp->fec);
+	print_eir(rsp->data, sizeof(rsp->data), false);
+}
+
+static void write_ext_inquiry_response_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_ext_inquiry_response *cmd = data;
+
+	print_fec(cmd->fec);
+	print_eir(cmd->data, sizeof(cmd->data), false);
+}
+
+static void refresh_encrypt_key_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_refresh_encrypt_key *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+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);
+}
+
+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);
+}
+
+static void read_local_oob_data_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_oob_data *rsp = data;
+
+	print_status(rsp->status);
+	print_hash_p192(rsp->hash);
+	print_randomizer_p192(rsp->randomizer);
+}
+
+static void read_inquiry_resp_tx_power_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data;
+
+	print_status(rsp->status);
+	print_power_level(rsp->level, NULL);
+}
+
+static void write_inquiry_tx_power_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_inquiry_tx_power *cmd = data;
+
+	print_power_level(cmd->level, NULL);
+}
+
+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);
+}
+
+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);
+}
+
+static void enhanced_flush_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_enhanced_flush *cmd = data;
+	const char *str;
+
+	print_handle(cmd->handle);
+
+	switch (cmd->type) {
+	case 0x00:
+		str = "Automatic flushable only";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, cmd->type);
+}
+
+static void send_keypress_notify_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_send_keypress_notify *cmd = data;
+	const char *str;
+
+	print_bdaddr(cmd->bdaddr);
+
+	switch (cmd->type) {
+	case 0x00:
+		str = "Passkey entry started";
+		break;
+	case 0x01:
+		str = "Passkey digit entered";
+		break;
+	case 0x02:
+		str = "Passkey digit erased";
+		break;
+	case 0x03:
+		str = "Passkey cleared";
+		break;
+	case 0x04:
+		str = "Passkey entry completed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, cmd->type);
+}
+
+static void send_keypress_notify_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_send_keypress_notify *rsp = data;
+
+	print_status(rsp->status);
+	print_bdaddr(rsp->bdaddr);
+}
+
+static void set_event_mask_page2_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_event_mask_page2 *cmd = data;
+
+	print_event_mask_page2(cmd->mask);
+}
+
+static void read_location_data_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_location_data *rsp = data;
+
+	print_status(rsp->status);
+	print_location_domain_aware(rsp->domain_aware);
+	print_location_domain(rsp->domain);
+	print_location_domain_options(rsp->domain_options);
+	print_location_options(rsp->options);
+}
+
+static void write_location_data_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_location_data *cmd = data;
+
+	print_location_domain_aware(cmd->domain_aware);
+	print_location_domain(cmd->domain);
+	print_location_domain_options(cmd->domain_options);
+	print_location_options(cmd->options);
+}
+
+static void read_flow_control_mode_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_flow_control_mode *rsp = data;
+
+	print_status(rsp->status);
+	print_flow_control_mode(rsp->mode);
+}
+
+static void write_flow_control_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_flow_control_mode *cmd = data;
+
+	print_flow_control_mode(cmd->mode);
+}
+
+static void read_enhanced_tx_power_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_enhanced_tx_power *cmd = data;
+
+	print_handle(cmd->handle);
+	print_power_type(cmd->type);
+}
+
+static void read_enhanced_tx_power_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_enhanced_tx_power *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_power_level(rsp->level_gfsk, "GFSK");
+	print_power_level(rsp->level_dqpsk, "DQPSK");
+	print_power_level(rsp->level_8dpsk, "8DPSK");
+}
+
+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);
+}
+
+static void read_le_host_supported_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_le_host_supported *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Supported: 0x%2.2x", rsp->supported);
+	print_field("Simultaneous: 0x%2.2x", rsp->simultaneous);
+}
+
+static void write_le_host_supported_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_le_host_supported *cmd = data;
+
+	print_field("Supported: 0x%2.2x", cmd->supported);
+	print_field("Simultaneous: 0x%2.2x", cmd->simultaneous);
+}
+
+static void set_reserved_lt_addr_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_reserved_lt_addr *cmd = data;
+
+	print_lt_addr(cmd->lt_addr);
+}
+
+static void set_reserved_lt_addr_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_set_reserved_lt_addr *rsp = data;
+
+	print_status(rsp->status);
+	print_lt_addr(rsp->lt_addr);
+}
+
+static void delete_reserved_lt_addr_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_delete_reserved_lt_addr *cmd = data;
+
+	print_lt_addr(cmd->lt_addr);
+}
+
+static void delete_reserved_lt_addr_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_delete_reserved_lt_addr *rsp = data;
+
+	print_status(rsp->status);
+	print_lt_addr(rsp->lt_addr);
+}
+
+static void set_slave_broadcast_data_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_set_slave_broadcast_data *cmd = data;
+
+	print_lt_addr(cmd->lt_addr);
+	print_broadcast_fragment(cmd->fragment);
+	print_field("Length: %d", cmd->length);
+
+	if (size - 3 != cmd->length)
+		print_text(COLOR_ERROR, "invalid data size (%d != %d)",
+						size - 3, cmd->length);
+
+	packet_hexdump(data + 3, size - 3);
+}
+
+static void set_slave_broadcast_data_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_set_slave_broadcast_data *rsp = data;
+
+	print_status(rsp->status);
+	print_lt_addr(rsp->lt_addr);
+}
+
+static void read_sync_train_params_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_sync_train_params *rsp = data;
+
+	print_status(rsp->status);
+	print_interval(rsp->interval);
+	print_field("Timeout: %.3f msec (0x%8.8x)",
+					le32_to_cpu(rsp->timeout) * 0.625,
+					le32_to_cpu(rsp->timeout));
+	print_field("Service data: 0x%2.2x", rsp->service_data);
+}
+
+static void write_sync_train_params_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_sync_train_params *cmd = data;
+
+	print_slot_625("Min interval", cmd->min_interval);
+	print_slot_625("Max interval", cmd->max_interval);
+	print_field("Timeout: %.3f msec (0x%8.8x)",
+					le32_to_cpu(cmd->timeout) * 0.625,
+					le32_to_cpu(cmd->timeout));
+	print_field("Service data: 0x%2.2x", cmd->service_data);
+}
+
+static void write_sync_train_params_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_sync_train_params *rsp = data;
+
+	print_status(rsp->status);
+	print_interval(rsp->interval);
+}
+
+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);
+}
+
+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);
+}
+
+static void read_auth_payload_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_auth_payload_timeout *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_auth_payload_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_auth_payload_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_auth_payload_timeout(rsp->timeout);
+}
+
+static void write_auth_payload_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_auth_payload_timeout *cmd = data;
+
+	print_handle(cmd->handle);
+	print_auth_payload_timeout(cmd->timeout);
+}
+
+static void write_auth_payload_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_auth_payload_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void read_local_oob_ext_data_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_oob_ext_data *rsp = data;
+
+	print_status(rsp->status);
+	print_hash_p192(rsp->hash192);
+	print_randomizer_p192(rsp->randomizer192);
+	print_hash_p256(rsp->hash256);
+	print_randomizer_p256(rsp->randomizer256);
+}
+
+static void read_ext_page_timeout_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_ext_page_timeout *rsp = data;
+
+	print_status(rsp->status);
+	print_timeout(rsp->timeout);
+}
+
+static void write_ext_page_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_ext_page_timeout *cmd = data;
+
+	print_timeout(cmd->timeout);
+}
+
+static void read_ext_inquiry_length_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_ext_inquiry_length *rsp = data;
+
+	print_status(rsp->status);
+	print_interval(rsp->interval);
+}
+
+static void write_ext_inquiry_length_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_ext_inquiry_length *cmd = data;
+
+	print_interval(cmd->interval);
+}
+
+static void read_local_version_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_version *rsp = data;
+	uint16_t manufacturer;
+
+	print_status(rsp->status);
+	print_hci_version(rsp->hci_ver, rsp->hci_rev);
+
+	manufacturer = le16_to_cpu(rsp->manufacturer);
+
+	if (index_current < MAX_INDEX) {
+		switch (index_list[index_current].type) {
+		case HCI_BREDR:
+			print_lmp_version(rsp->lmp_ver, rsp->lmp_subver);
+			break;
+		case HCI_AMP:
+			print_pal_version(rsp->lmp_ver, rsp->lmp_subver);
+			break;
+		}
+
+		index_list[index_current].manufacturer = manufacturer;
+	}
+
+	print_manufacturer(rsp->manufacturer);
+
+	switch (manufacturer) {
+	case 15:
+		print_manufacturer_broadcom(rsp->lmp_subver, rsp->hci_rev);
+		break;
+	}
+}
+
+static void read_local_commands_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_commands *rsp = data;
+
+	print_status(rsp->status);
+	print_commands(rsp->commands);
+}
+
+static void read_local_features_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_features *rsp = data;
+
+	print_status(rsp->status);
+	print_features(0, rsp->features, 0x00);
+}
+
+static void read_local_ext_features_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_local_ext_features *cmd = data;
+
+	print_field("Page: %d", cmd->page);
+}
+
+static void read_local_ext_features_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_ext_features *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Page: %d/%d", rsp->page, rsp->max_page);
+	print_features(rsp->page, rsp->features, 0x00);
+}
+
+static void read_buffer_size_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_buffer_size *rsp = data;
+
+	print_status(rsp->status);
+	print_field("ACL MTU: %-4d ACL max packet: %d",
+					le16_to_cpu(rsp->acl_mtu),
+					le16_to_cpu(rsp->acl_max_pkt));
+	print_field("SCO MTU: %-4d SCO max packet: %d",
+					rsp->sco_mtu,
+					le16_to_cpu(rsp->sco_max_pkt));
+}
+
+static void read_country_code_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_country_code *rsp = data;
+	const char *str;
+
+	print_status(rsp->status);
+
+	switch (rsp->code) {
+	case 0x00:
+		str = "North America, Europe*, Japan";
+		break;
+	case 0x01:
+		str = "France";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Country code: %s (0x%2.2x)", str, rsp->code);
+}
+
+static void read_bd_addr_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+	print_status(rsp->status);
+	print_bdaddr(rsp->bdaddr);
+
+	if (index_current < MAX_INDEX)
+		memcpy(index_list[index_current].bdaddr, rsp->bdaddr, 6);
+}
+
+static void read_data_block_size_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_data_block_size *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Max ACL length: %d", le16_to_cpu(rsp->max_acl_len));
+	print_field("Block length: %d", le16_to_cpu(rsp->block_len));
+	print_field("Num blocks: %d", le16_to_cpu(rsp->num_blocks));
+}
+
+static void read_local_codecs_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_codecs *rsp = data;
+	uint8_t i, num_vnd_codecs;
+
+	print_status(rsp->status);
+	print_field("Number of supported codecs: %d", rsp->num_codecs);
+
+	for (i = 0; i < rsp->num_codecs; i++)
+		print_codec("  Codec", rsp->codec[i]);
+
+	num_vnd_codecs = rsp->codec[rsp->num_codecs];
+
+	print_field("Number of vendor codecs: %d", num_vnd_codecs);
+
+	packet_hexdump(data + rsp->num_codecs + 3,
+					size - rsp->num_codecs - 3);
+}
+
+static void read_failed_contact_counter_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_failed_contact_counter *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_failed_contact_counter_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_failed_contact_counter *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_field("Counter: %u", le16_to_cpu(rsp->counter));
+}
+
+static void reset_failed_contact_counter_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_reset_failed_contact_counter *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void reset_failed_contact_counter_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_reset_failed_contact_counter *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void read_link_quality_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_link_quality *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_link_quality_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_link_quality *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_field("Link quality: 0x%2.2x", rsp->link_quality);
+}
+
+static void read_rssi_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_rssi *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_rssi_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_rssi *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_rssi(rsp->rssi);
+}
+
+static void read_afh_channel_map_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_afh_channel_map *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_afh_channel_map_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_afh_channel_map *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_afh_mode(rsp->mode);
+	print_channel_map(rsp->map);
+}
+
+static void read_clock_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_clock *cmd = data;
+
+	print_handle(cmd->handle);
+	print_clock_type(cmd->type);
+}
+
+static void read_clock_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_clock *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_clock(rsp->clock);
+	print_clock_accuracy(rsp->accuracy);
+}
+
+static void read_encrypt_key_size_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_encrypt_key_size *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void read_encrypt_key_size_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_encrypt_key_size *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_key_size(rsp->key_size);
+}
+
+static void read_local_amp_info_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_amp_info *rsp = data;
+	const char *str;
+
+	print_status(rsp->status);
+	print_amp_status(rsp->amp_status);
+
+	print_field("Total bandwidth: %d kbps", le32_to_cpu(rsp->total_bw));
+	print_field("Max guaranteed bandwidth: %d kbps",
+						le32_to_cpu(rsp->max_bw));
+	print_field("Min latency: %d", le32_to_cpu(rsp->min_latency));
+	print_field("Max PDU size: %d", le32_to_cpu(rsp->max_pdu));
+
+	switch (rsp->amp_type) {
+	case 0x00:
+		str = "Primary BR/EDR Controller";
+		break;
+	case 0x01:
+		str = "802.11 AMP Controller";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Controller type: %s (0x%2.2x)", str, rsp->amp_type);
+
+	print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(rsp->pal_cap));
+	print_field("Max ASSOC length: %d", le16_to_cpu(rsp->max_assoc_len));
+	print_field("Max flush timeout: %d", le32_to_cpu(rsp->max_flush_to));
+	print_field("Best effort flush timeout: %d",
+					le32_to_cpu(rsp->be_flush_to));
+}
+
+static void read_local_amp_assoc_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_read_local_amp_assoc *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far));
+	print_field("Max ASSOC length: %d", le16_to_cpu(cmd->max_assoc_len));
+}
+
+static void read_local_amp_assoc_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_local_amp_assoc *rsp = data;
+
+	print_status(rsp->status);
+	print_phy_handle(rsp->phy_handle);
+	print_field("Remaining ASSOC length: %d",
+					le16_to_cpu(rsp->remain_assoc_len));
+
+	packet_hexdump(data + 4, size - 4);
+}
+
+static void write_remote_amp_assoc_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_remote_amp_assoc *cmd = data;
+
+	print_phy_handle(cmd->phy_handle);
+	print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far));
+	print_field("Remaining ASSOC length: %d",
+					le16_to_cpu(cmd->remain_assoc_len));
+
+	packet_hexdump(data + 5, size - 5);
+}
+
+static void write_remote_amp_assoc_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_write_remote_amp_assoc *rsp = data;
+
+	print_status(rsp->status);
+	print_phy_handle(rsp->phy_handle);
+}
+
+static void get_mws_transport_config_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_get_mws_transport_config *rsp = data;
+	uint8_t sum_baud_rates = 0;
+	int i;
+
+	print_status(rsp->status);
+	print_field("Number of transports: %d", rsp->num_transports);
+
+	for (i = 0; i < rsp->num_transports; i++) {
+		uint8_t transport = rsp->transport[0];
+		uint8_t num_baud_rates = rsp->transport[1];
+		const char *str;
+
+		switch (transport) {
+		case 0x00:
+			str = "Disbabled";
+			break;
+		case 0x01:
+			str = "WCI-1";
+			break;
+		case 0x02:
+			str = "WCI-2";
+			break;
+		default:
+			str = "Reserved";
+			break;
+		}
+
+		print_field("  Transport layer: %s (0x%2.2x)", str, transport);
+		print_field("  Number of baud rates: %d", num_baud_rates);
+
+		sum_baud_rates += num_baud_rates;
+	}
+
+	print_field("Baud rate list: %u entr%s", sum_baud_rates,
+					sum_baud_rates == 1 ? "y" : "ies");
+
+	for (i = 0; i < sum_baud_rates; i++) {
+		uint32_t to_baud_rate, from_baud_rate;
+
+		to_baud_rate = get_le32(data + 2 +
+					rsp->num_transports * 2 + i * 4);
+		from_baud_rate = get_le32(data + 2 +
+						rsp->num_transports * 2 +
+						sum_baud_rates * 4 + i * 4);
+
+		print_field("  Bluetooth to MWS: %d", to_baud_rate);
+		print_field("  MWS to Bluetooth: %d", from_baud_rate);
+	}
+
+	packet_hexdump(data + 2 + rsp->num_transports * 2 + sum_baud_rates * 8,
+		size - 2 - rsp->num_transports * 2 - sum_baud_rates * 8);
+}
+
+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_clock_type(cmd->type);
+	print_lpo_allowed(cmd->lpo_allowed);
+	print_field("Clock captures to filter: %u", cmd->num_filter);
+}
+
+static void read_loopback_mode_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_read_loopback_mode *rsp = data;
+
+	print_status(rsp->status);
+	print_loopback_mode(rsp->mode);
+}
+
+static void write_loopback_mode_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_write_loopback_mode *cmd = data;
+
+	print_loopback_mode(cmd->mode);
+}
+
+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);
+}
+
+static void le_set_event_mask_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_event_mask *cmd = data;
+
+	print_event_mask_le(cmd->mask);
+}
+
+static void le_read_buffer_size_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_buffer_size *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Data packet length: %d", le16_to_cpu(rsp->le_mtu));
+	print_field("Num data packets: %d", rsp->le_max_pkt);
+}
+
+static void le_read_local_features_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_local_features *rsp = data;
+
+	print_status(rsp->status);
+	print_features(0, rsp->features, 0x01);
+}
+
+static void le_set_random_address_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_random_address *cmd = data;
+
+	print_addr("Address", cmd->addr, 0x01);
+}
+
+static void le_set_adv_parameters_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_adv_parameters *cmd = data;
+	const char *str;
+
+	print_slot_625("Min advertising interval", cmd->min_interval);
+	print_slot_625("Max advertising interval", cmd->max_interval);
+
+	switch (cmd->type) {
+	case 0x00:
+		str = "Connectable undirected - ADV_IND";
+		break;
+	case 0x01:
+		str = "Connectable directed - ADV_DIRECT_IND (high duty cycle)";
+		break;
+	case 0x02:
+		str = "Scannable undirected - ADV_SCAN_IND";
+		break;
+	case 0x03:
+		str = "Non connectable undirected - ADV_NONCONN_IND";
+		break;
+	case 0x04:
+		str = "Connectable directed - ADV_DIRECT_IND (low duty cycle)";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, cmd->type);
+
+	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);
+}
+
+static void le_read_adv_tx_power_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
+
+	print_status(rsp->status);
+	print_power_level(rsp->level, NULL);
+}
+
+static void le_set_adv_data_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_adv_data *cmd = data;
+
+	print_field("Length: %d", cmd->len);
+	print_eir(cmd->data, cmd->len, true);
+}
+
+static void le_set_scan_rsp_data_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data;
+
+	print_field("Length: %d", cmd->len);
+	print_eir(cmd->data, cmd->len, true);
+}
+
+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);
+}
+
+static void le_set_scan_parameters_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_scan_parameters *cmd = data;
+	const char *str;
+
+	switch (cmd->type) {
+	case 0x00:
+		str = "Passive";
+		break;
+	case 0x01:
+		str = "Active";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Type: %s (0x%2.2x)", str, cmd->type);
+
+	print_interval(cmd->interval);
+	print_window(cmd->window);
+	print_own_addr_type(cmd->own_addr_type);
+
+	switch (cmd->filter_policy) {
+	case 0x00:
+		str = "Accept all advertisement";
+		break;
+	case 0x01:
+		str = "Ignore not in white list";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+}
+
+static void le_set_scan_enable_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;
+	}
+
+	print_field("Scanning: %s (0x%2.2x)", str, cmd->enable);
+
+	switch (cmd->filter_dup) {
+	case 0x00:
+		str = "Disabled";
+		break;
+	case 0x01:
+		str = "Enabled";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Filter duplicates: %s (0x%2.2x)", str, cmd->filter_dup);
+}
+
+static void le_create_conn_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_create_conn *cmd = data;
+	const char *str;
+
+	print_slot_625("Scan interval", cmd->scan_interval);
+	print_slot_625("Scan window", cmd->scan_window);
+
+	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_peer_addr_type("Peer address type", cmd->peer_addr_type);
+	print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
+	print_own_addr_type(cmd->own_addr_type);
+
+	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_field("Supervision timeout: %d msec (0x%4.4x)",
+					le16_to_cpu(cmd->supv_timeout) * 10,
+					le16_to_cpu(cmd->supv_timeout));
+	print_slot_625("Min connection length", cmd->min_length);
+	print_slot_625("Max connection length", cmd->max_length);
+}
+
+static void le_read_white_list_size_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_white_list_size *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Size: %u", rsp->size);
+}
+
+static void le_add_to_white_list_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_add_to_white_list *cmd = data;
+
+	print_addr_type("Address type", cmd->addr_type);
+	print_addr("Address", cmd->addr, cmd->addr_type);
+}
+
+static void le_remove_from_white_list_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_remove_from_white_list *cmd = data;
+
+	print_addr_type("Address type", cmd->addr_type);
+	print_addr("Address", cmd->addr, cmd->addr_type);
+}
+
+static void le_conn_update_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_conn_update *cmd = data;
+
+	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_field("Supervision timeout: %d msec (0x%4.4x)",
+					le16_to_cpu(cmd->supv_timeout) * 10,
+					le16_to_cpu(cmd->supv_timeout));
+	print_slot_625("Min connection length", cmd->min_length);
+	print_slot_625("Max connection length", cmd->max_length);
+}
+
+static void le_set_host_classification_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_host_classification *cmd = data;
+
+	print_le_channel_map(cmd->map);
+}
+
+static void le_read_channel_map_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_channel_map *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void le_read_channel_map_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_channel_map *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+	print_le_channel_map(rsp->map);
+}
+
+static void le_read_remote_features_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_remote_features *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void le_encrypt_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_encrypt *cmd = data;
+
+	print_key("Key", cmd->key);
+	print_key("Plaintext data", cmd->plaintext);
+}
+
+static void le_encrypt_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_encrypt *rsp = data;
+
+	print_status(rsp->status);
+	print_key("Encrypted data", rsp->data);
+}
+
+static void le_rand_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_rand *rsp = data;
+
+	print_status(rsp->status);
+	print_random_number(rsp->number);
+}
+
+static void le_start_encrypt_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_start_encrypt *cmd = data;
+
+	print_handle(cmd->handle);
+	print_random_number(cmd->rand);
+	print_encrypted_diversifier(cmd->ediv);
+	print_key("Long term key", cmd->ltk);
+}
+
+static void le_ltk_req_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_ltk_req_reply *cmd = data;
+
+	print_handle(cmd->handle);
+	print_key("Long term key", cmd->ltk);
+}
+
+static void le_ltk_req_reply_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_ltk_req_reply *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void le_ltk_req_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_ltk_req_neg_reply *cmd = data;
+
+	print_handle(cmd->handle);
+}
+
+static void le_ltk_req_neg_reply_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_ltk_req_neg_reply *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void le_read_supported_states_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_supported_states *rsp = data;
+
+	print_status(rsp->status);
+	print_le_states(rsp->states);
+}
+
+static void le_receiver_test_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_receiver_test *cmd = data;
+
+	print_field("RX frequency: %d MHz (0x%2.2x)",
+				(cmd->frequency * 2) + 2402, cmd->frequency);
+}
+
+static void le_transmitter_test_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_transmitter_test *cmd = data;
+
+	print_field("TX frequency: %d MHz (0x%2.2x)",
+				(cmd->frequency * 2) + 2402, cmd->frequency);
+	print_field("Test data length: %d bytes", cmd->data_len);
+	print_field("Packet payload: 0x%2.2x", cmd->payload);
+}
+
+static void le_test_end_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_test_end *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Number of packets: %d", le16_to_cpu(rsp->num_packets));
+}
+
+static void le_conn_param_req_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_conn_param_req_reply *cmd = data;
+
+	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_field("Supervision timeout: %d msec (0x%4.4x)",
+					le16_to_cpu(cmd->supv_timeout) * 10,
+					le16_to_cpu(cmd->supv_timeout));
+	print_slot_625("Min connection length", cmd->min_length);
+	print_slot_625("Max connection length", cmd->max_length);
+}
+
+static void le_conn_param_req_reply_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_conn_param_req_reply *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void le_conn_param_req_neg_reply_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_conn_param_req_neg_reply *cmd = data;
+
+	print_handle(cmd->handle);
+	print_reason(cmd->reason);
+}
+
+static void le_conn_param_req_neg_reply_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_conn_param_req_neg_reply *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void le_set_data_length_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_data_length *cmd = data;
+
+	print_handle(cmd->handle);
+	print_field("TX octets: %d", le16_to_cpu(cmd->tx_len));
+	print_field("TX time: %d", le16_to_cpu(cmd->tx_time));
+}
+
+static void le_set_data_length_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_set_data_length *rsp = data;
+
+	print_status(rsp->status);
+	print_handle(rsp->handle);
+}
+
+static void le_read_default_data_length_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_default_data_length *rsp = data;
+
+	print_status(rsp->status);
+	print_field("TX octets: %d", le16_to_cpu(rsp->tx_len));
+	print_field("TX time: %d", le16_to_cpu(rsp->tx_time));
+}
+
+static void le_write_default_data_length_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_write_default_data_length *cmd = data;
+
+	print_field("TX octets: %d", le16_to_cpu(cmd->tx_len));
+	print_field("TX time: %d", le16_to_cpu(cmd->tx_time));
+}
+
+static void le_generate_dhkey_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_generate_dhkey *cmd = data;
+
+	print_pk256("Remote P-256 public key", cmd->remote_pk256);
+}
+
+static void le_add_to_resolv_list_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_add_to_resolv_list *cmd = data;
+
+	print_addr_type("Address type", cmd->addr_type);
+	print_addr("Address", cmd->addr, cmd->addr_type);
+	print_key("Peer identity resolving key", cmd->peer_irk);
+	print_key("Local identity resolving key", cmd->local_irk);
+}
+
+static void le_remove_from_resolv_list_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_remove_from_resolv_list *cmd = data;
+
+	print_addr_type("Address type", cmd->addr_type);
+	print_addr("Address", cmd->addr, cmd->addr_type);
+}
+
+static void le_read_resolv_list_size_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_resolv_list_size *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Size: %u", rsp->size);
+}
+
+static void le_read_peer_resolv_addr_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_peer_resolv_addr *cmd = data;
+
+	print_addr_type("Address type", cmd->addr_type);
+	print_addr("Address", cmd->addr, cmd->addr_type);
+}
+
+static void le_read_peer_resolv_addr_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_peer_resolv_addr *rsp = data;
+
+	print_status(rsp->status);
+	print_addr("Address", rsp->addr, 0x01);
+}
+
+static void le_read_local_resolv_addr_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_read_local_resolv_addr *cmd = data;
+
+	print_addr_type("Address type", cmd->addr_type);
+	print_addr("Address", cmd->addr, cmd->addr_type);
+}
+
+static void le_read_local_resolv_addr_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_local_resolv_addr *rsp = data;
+
+	print_status(rsp->status);
+	print_addr("Address", rsp->addr, 0x01);
+}
+
+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);
+}
+
+static void le_set_resolv_timeout_cmd(const void *data, uint8_t size)
+{
+	const struct bt_hci_cmd_le_set_resolv_timeout *cmd = data;
+
+	print_field("Timeout: %u seconds", le16_to_cpu(cmd->timeout));
+}
+
+static void le_read_max_data_length_rsp(const void *data, uint8_t size)
+{
+	const struct bt_hci_rsp_le_read_max_data_length *rsp = data;
+
+	print_status(rsp->status);
+	print_field("Max TX octets: %d", le16_to_cpu(rsp->max_tx_len));
+	print_field("Max TX time: %d", le16_to_cpu(rsp->max_tx_time));
+	print_field("Max RX octets: %d", le16_to_cpu(rsp->max_rx_len));
+	print_field("Max RX time: %d", le16_to_cpu(rsp->max_rx_time));
+}
+
+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;
+	bool rsp_fixed;
+};
+
+static const struct opcode_data opcode_table[] = {
+	{ 0x0000,  -1, "NOP" },
+
+	/* OGF 1 - Link Control */
+	{ 0x0401,   0, "Inquiry",
+				inquiry_cmd, 5, true },
+	{ 0x0402,   1, "Inquiry Cancel",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x0403,   2, "Periodic Inquiry Mode",
+				periodic_inquiry_cmd, 9, true,
+				status_rsp, 1, true },
+	{ 0x0404,   3, "Exit Periodic Inquiry Mode",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x0405,   4, "Create Connection",
+				create_conn_cmd, 13, true },
+	{ 0x0406,   5, "Disconnect",
+				disconnect_cmd, 3, true },
+	{ 0x0407,   6, "Add SCO Connection",
+				add_sco_conn_cmd, 4, true },
+	{ 0x0408,   7, "Create Connection Cancel",
+				create_conn_cancel_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x0409,   8, "Accept Connection Request",
+				accept_conn_request_cmd, 7, true },
+	{ 0x040a,   9, "Reject Connection Request",
+				reject_conn_request_cmd, 7, true },
+	{ 0x040b,  10, "Link Key Request Reply",
+				link_key_request_reply_cmd, 22, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x040c,  11, "Link Key Request Negative Reply",
+				link_key_request_neg_reply_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x040d,  12, "PIN Code Request Reply",
+				pin_code_request_reply_cmd, 23, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x040e,  13, "PIN Code Request Negative Reply",
+				pin_code_request_neg_reply_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x040f,  14, "Change Connection Packet Type",
+				change_conn_pkt_type_cmd, 4, true },
+	{ 0x0411,  15, "Authentication Requested",
+				auth_requested_cmd, 2, true },
+	{ 0x0413,  16, "Set Connection Encryption",
+				set_conn_encrypt_cmd, 3, true },
+	{ 0x0415,  17, "Change Connection Link Key",
+				change_conn_link_key_cmd, 2, true },
+	{ 0x0417,  18, "Master Link Key",
+				master_link_key_cmd, 1, true },
+	{ 0x0419,  19, "Remote Name Request",
+				remote_name_request_cmd, 10, true },
+	{ 0x041a,  20, "Remote Name Request Cancel",
+				remote_name_request_cancel_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x041b,  21, "Read Remote Supported Features",
+				read_remote_features_cmd, 2, true },
+	{ 0x041c,  22, "Read Remote Extended Features",
+				read_remote_ext_features_cmd, 3, true },
+	{ 0x041d,  23, "Read Remote Version Information",
+				read_remote_version_cmd, 2, true },
+	{ 0x041f,  24, "Read Clock Offset",
+				read_clock_offset_cmd, 2, true },
+	{ 0x0420,  25, "Read LMP Handle",
+				read_lmp_handle_cmd, 2, true,
+				read_lmp_handle_rsp, 8, true },
+	{ 0x0428, 131, "Setup Synchronous Connection",
+				setup_sync_conn_cmd, 17, true },
+	{ 0x0429, 132, "Accept Synchronous Connection Request",
+				accept_sync_conn_request_cmd, 21, true },
+	{ 0x042a, 133, "Reject Synchronous Connection Request",
+				reject_sync_conn_request_cmd, 7, true },
+	{ 0x042b, 151, "IO Capability Request Reply",
+				io_capability_request_reply_cmd, 9, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x042c, 152, "User Confirmation Request Reply",
+				user_confirm_request_reply_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x042d, 153, "User Confirmation Request Neg Reply",
+				user_confirm_request_neg_reply_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x042e, 154, "User Passkey Request Reply",
+				user_passkey_request_reply_cmd, 10, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x042f, 155, "User Passkey Request Negative Reply",
+				user_passkey_request_neg_reply_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x0430, 156, "Remote OOB Data Request Reply",
+				remote_oob_data_request_reply_cmd, 38, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x0433, 159, "Remote OOB Data Request Neg Reply",
+				remote_oob_data_request_neg_reply_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x0434, 163, "IO Capability Request Negative Reply",
+				io_capability_request_neg_reply_cmd, 7, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x0435, 168, "Create Physical Link",
+				create_phy_link_cmd, 3, false },
+	{ 0x0436, 169, "Accept Physical Link",
+				accept_phy_link_cmd, 3, false },
+	{ 0x0437, 170, "Disconnect Physical Link",
+				disconn_phy_link_cmd, 2, true },
+	{ 0x0438, 171, "Create Logical Link",
+				create_logic_link_cmd, 33, true },
+	{ 0x0439, 172, "Accept Logical Link",
+				accept_logic_link_cmd, 33, true },
+	{ 0x043a, 173, "Disconnect Logical Link",
+				disconn_logic_link_cmd, 2, true },
+	{ 0x043b, 174, "Logical Link Cancel",
+				logic_link_cancel_cmd, 2, true,
+				logic_link_cancel_rsp, 3, true },
+	{ 0x043c, 175, "Flow Specifcation Modify",
+				flow_spec_modify_cmd, 34, true },
+	{ 0x043d, 235, "Enhanced Setup Synchronous Connection",
+				enhanced_setup_sync_conn_cmd, 59, true },
+	{ 0x043e, 236, "Enhanced Accept Synchronous Connection Request",
+				enhanced_accept_sync_conn_request_cmd, 63, true },
+	{ 0x043f, 246, "Truncated Page",
+				truncated_page_cmd, 9, true },
+	{ 0x0440, 247, "Truncated Page Cancel",
+				truncated_page_cancel_cmd, 6, true,
+				status_bdaddr_rsp, 7, true },
+	{ 0x0441, 248, "Set Connectionless Slave Broadcast",
+				set_slave_broadcast_cmd, 11, true,
+				set_slave_broadcast_rsp, 4, true },
+	{ 0x0442, 249, "Set Connectionless Slave Broadcast Receive",
+				set_slave_broadcast_receive_cmd, 34, true,
+				set_slave_broadcast_receive_rsp, 8, true },
+	{ 0x0443, 250, "Start Synchronization Train",
+				null_cmd, 0, true },
+	{ 0x0444, 251, "Receive Synchronization Train",
+				receive_sync_train_cmd, 12, true },
+	{ 0x0445, 257, "Remote OOB Extended Data Request Reply",
+				remote_oob_ext_data_request_reply_cmd, 70, true,
+				status_bdaddr_rsp, 7, true },
+
+	/* OGF 2 - Link Policy */
+	{ 0x0801,  33, "Hold Mode",
+				hold_mode_cmd, 6, true },
+	{ 0x0803,  34, "Sniff Mode",
+				sniff_mode_cmd, 10, true },
+	{ 0x0804,  35, "Exit Sniff Mode",
+				exit_sniff_mode_cmd, 2, true },
+	{ 0x0805,  36, "Park State",
+				park_state_cmd, 6, true },
+	{ 0x0806,  37, "Exit Park State",
+				exit_park_state_cmd, 2, true },
+	{ 0x0807,  38, "QoS Setup",
+				qos_setup_cmd, 20, true },
+	{ 0x0809,  39, "Role Discovery",
+				role_discovery_cmd, 2, true,
+				role_discovery_rsp, 4, true },
+	{ 0x080b,  40, "Switch Role",
+				switch_role_cmd, 7, true },
+	{ 0x080c,  41, "Read Link Policy Settings",
+				read_link_policy_cmd, 2, true,
+				read_link_policy_rsp, 5, true },
+	{ 0x080d,  42, "Write Link Policy Settings",
+				write_link_policy_cmd, 4, true,
+				write_link_policy_rsp, 3, true },
+	{ 0x080e,  43, "Read Default Link Policy Settings",
+				null_cmd, 0, true,
+				read_default_link_policy_rsp, 3, true },
+	{ 0x080f,  44, "Write Default Link Policy Settings",
+				write_default_link_policy_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x0810,  45, "Flow Specification",
+				flow_spec_cmd, 21, true },
+	{ 0x0811, 140, "Sniff Subrating",
+				sniff_subrating_cmd, 8, true,
+				sniff_subrating_rsp, 3, true },
+
+	/* OGF 3 - Host Control */
+	{ 0x0c01,  46, "Set Event Mask",
+				set_event_mask_cmd, 8, true,
+				status_rsp, 1, true },
+	{ 0x0c03,  47, "Reset",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x0c05,  48, "Set Event Filter",
+				set_event_filter_cmd, 1, false,
+				status_rsp, 1, true },
+	{ 0x0c08,  49, "Flush",
+				flush_cmd, 2, true,
+				flush_rsp, 3, true },
+	{ 0x0c09,  50, "Read PIN Type",
+				null_cmd, 0, true,
+				read_pin_type_rsp, 2, true },
+	{ 0x0c0a,  51, "Write PIN Type",
+				write_pin_type_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c0b,  52, "Create New Unit Key",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x0c0d,  53, "Read Stored Link Key",
+				read_stored_link_key_cmd, 7, true,
+				read_stored_link_key_rsp, 5, true },
+	{ 0x0c11,  54, "Write Stored Link Key",
+				write_stored_link_key_cmd, 1, false,
+				write_stored_link_key_rsp, 2, true },
+	{ 0x0c12,  55, "Delete Stored Link Key",
+				delete_stored_link_key_cmd, 7, true,
+				delete_stored_link_key_rsp, 3, true },
+	{ 0x0c13,  56, "Write Local Name",
+				write_local_name_cmd, 248, true,
+				status_rsp, 1, true },
+	{ 0x0c14,  57, "Read Local Name",
+				null_cmd, 0, true,
+				read_local_name_rsp, 249, true },
+	{ 0x0c15,  58, "Read Connection Accept Timeout",
+				null_cmd, 0, true,
+				read_conn_accept_timeout_rsp, 3, true },
+	{ 0x0c16,  59, "Write Connection Accept Timeout",
+				write_conn_accept_timeout_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x0c17,  60, "Read Page Timeout",
+				null_cmd, 0, true,
+				read_page_timeout_rsp, 3, true },
+	{ 0x0c18,  61, "Write Page Timeout",
+				write_page_timeout_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x0c19,  62, "Read Scan Enable",
+				null_cmd, 0, true,
+				read_scan_enable_rsp, 2, true },
+	{ 0x0c1a,  63, "Write Scan Enable",
+				write_scan_enable_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c1b,  64, "Read Page Scan Activity",
+				null_cmd, 0, true,
+				read_page_scan_activity_rsp, 5, true },
+	{ 0x0c1c,  65, "Write Page Scan Activity",
+				write_page_scan_activity_cmd, 4, true,
+				status_rsp, 1, true },
+	{ 0x0c1d,  66, "Read Inquiry Scan Activity",
+				null_cmd, 0, true,
+				read_inquiry_scan_activity_rsp, 5, true },
+	{ 0x0c1e,  67, "Write Inquiry Scan Activity",
+				write_inquiry_scan_activity_cmd, 4, true,
+				status_rsp, 1, true },
+	{ 0x0c1f,  68, "Read Authentication Enable",
+				null_cmd, 0, true,
+				read_auth_enable_rsp, 2, true },
+	{ 0x0c20,  69, "Write Authentication Enable",
+				write_auth_enable_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c21,  70, "Read Encryption Mode",
+				null_cmd, 0, true,
+				read_encrypt_mode_rsp, 2, true },
+	{ 0x0c22,  71, "Write Encryption Mode",
+				write_encrypt_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c23,  72, "Read Class of Device",
+				null_cmd, 0, true,
+				read_class_of_dev_rsp, 4, true },
+	{ 0x0c24,  73, "Write Class of Device",
+				write_class_of_dev_cmd, 3, true,
+				status_rsp, 1, true },
+	{ 0x0c25,  74, "Read Voice Setting",
+				null_cmd, 0, true,
+				read_voice_setting_rsp, 3, true },
+	{ 0x0c26,  75, "Write Voice Setting",
+				write_voice_setting_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x0c27,  76, "Read Automatic Flush Timeout",
+				read_auto_flush_timeout_cmd, 2, true,
+				read_auto_flush_timeout_rsp, 5, true },
+	{ 0x0c28,  77, "Write Automatic Flush Timeout",
+				write_auto_flush_timeout_cmd, 4, true,
+				write_auto_flush_timeout_rsp, 3, true },
+	{ 0x0c29,  78, "Read Num Broadcast Retransmissions",
+				null_cmd, 0, true,
+				read_num_broadcast_retrans_rsp, 2, true },
+	{ 0x0c2a,  79, "Write Num Broadcast Retransmissions",
+				write_num_broadcast_retrans_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c2b,  80, "Read Hold Mode Activity",
+				null_cmd, 0, true,
+				read_hold_mode_activity_rsp, 2, true },
+	{ 0x0c2c,  81, "Write Hold Mode Activity",
+				write_hold_mode_activity_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c2d,  82, "Read Transmit Power Level",
+				read_tx_power_cmd, 3, true,
+				read_tx_power_rsp, 4, true },
+	{ 0x0c2e,  83, "Read Sync Flow Control Enable",
+				null_cmd, 0, true,
+				read_sync_flow_control_rsp, 2, true },
+	{ 0x0c2f,  84, "Write Sync Flow Control Enable",
+				write_sync_flow_control_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c31,  85, "Set Controller To Host Flow Control",
+				set_host_flow_control_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c33,  86, "Host Buffer Size",
+				host_buffer_size_cmd, 7, true,
+				status_rsp, 1, true },
+	{ 0x0c35,  87, "Host Number of Completed Packets",
+				host_num_completed_packets_cmd, 5, false },
+	{ 0x0c36,  88, "Read Link Supervision Timeout",
+				read_link_supv_timeout_cmd, 2, true,
+				read_link_supv_timeout_rsp, 5, true },
+	{ 0x0c37,  89, "Write Link Supervision Timeout",
+				write_link_supv_timeout_cmd, 4, true,
+				write_link_supv_timeout_rsp, 3, true },
+	{ 0x0c38,  90, "Read Number of Supported IAC",
+				null_cmd, 0, true,
+				read_num_supported_iac_rsp, 2, true },
+	{ 0x0c39,  91, "Read Current IAC LAP",
+				null_cmd, 0, true,
+				read_current_iac_lap_rsp, 2, false },
+	{ 0x0c3a,  92, "Write Current IAC LAP",
+				write_current_iac_lap_cmd, 1, false,
+				status_rsp, 1, true },
+	{ 0x0c3b,  93, "Read Page Scan Period Mode",
+				null_cmd, 0, true,
+				read_page_scan_period_mode_rsp, 2, true },
+	{ 0x0c3c,  94, "Write Page Scan Period Mode",
+				write_page_scan_period_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c3d,  95, "Read Page Scan Mode",
+				null_cmd, 0, true,
+				read_page_scan_mode_rsp, 2, true },
+	{ 0x0c3e,  96, "Write Page Scan Mode",
+				write_page_scan_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c3f,  97, "Set AFH Host Channel Classification",
+				set_afh_host_classification_cmd, 10, true,
+				status_rsp, 1, true },
+	{ 0x0c42, 100, "Read Inquiry Scan Type",
+				null_cmd, 0, true,
+				read_inquiry_scan_type_rsp, 2, true },
+	{ 0x0c43, 101, "Write Inquiry Scan Type",
+				write_inquiry_scan_type_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c44, 102, "Read Inquiry Mode",
+				null_cmd, 0, true,
+				read_inquiry_mode_rsp, 2, true },
+	{ 0x0c45, 103, "Write Inquiry Mode",
+				write_inquiry_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c46, 104, "Read Page Scan Type",
+				null_cmd, 0, true,
+				read_page_scan_type_rsp, 2, true },
+	{ 0x0c47, 105, "Write Page Scan Type",
+				write_page_scan_type_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c48, 106, "Read AFH Channel Assessment Mode",
+				null_cmd, 0, true,
+				read_afh_assessment_mode_rsp, 2, true },
+	{ 0x0c49, 107, "Write AFH Channel Assessment Mode",
+				write_afh_assessment_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c51, 136, "Read Extended Inquiry Response",
+				null_cmd, 0, true,
+				read_ext_inquiry_response_rsp, 242, true },
+	{ 0x0c52, 137, "Write Extended Inquiry Response",
+				write_ext_inquiry_response_cmd, 241, true,
+				status_rsp, 1, true },
+	{ 0x0c53, 138, "Refresh Encryption Key",
+				refresh_encrypt_key_cmd, 2, true },
+	{ 0x0c55, 141, "Read Simple Pairing Mode",
+				null_cmd, 0, true,
+				read_simple_pairing_mode_rsp, 2, true },
+	{ 0x0c56, 142, "Write Simple Pairing Mode",
+				write_simple_pairing_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c57, 143, "Read Local OOB Data",
+				null_cmd, 0, true,
+				read_local_oob_data_rsp, 33, true },
+	{ 0x0c58, 144, "Read Inquiry Response TX Power Level",
+				null_cmd, 0, true,
+				read_inquiry_resp_tx_power_rsp, 2, true },
+	{ 0x0c59, 145, "Write Inquiry Transmit Power Level",
+				write_inquiry_tx_power_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c5a, 146, "Read Default Erroneous Data Reporting",
+				null_cmd, 0, true,
+				read_erroneous_reporting_rsp, 2, true },
+	{ 0x0c5b, 147, "Write Default Erroneous Data Reporting",
+				write_erroneous_reporting_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c5f, 158, "Enhanced Flush",
+				enhanced_flush_cmd, 3, true },
+	{ 0x0c60, 162, "Send Keypress Notification",
+				send_keypress_notify_cmd, 7, true,
+				send_keypress_notify_rsp, 7, true },
+	{ 0x0c61, 176, "Read Logical Link Accept Timeout" },
+	{ 0x0c62, 177, "Write Logical Link Accept Timeout" },
+	{ 0x0c63, 178, "Set Event Mask Page 2",
+				set_event_mask_page2_cmd, 8, true,
+				status_rsp, 1, true },
+	{ 0x0c64, 179, "Read Location Data",
+				null_cmd, 0, true,
+				read_location_data_rsp, 6, true },
+	{ 0x0c65, 180, "Write Location Data",
+				write_location_data_cmd, 5, true,
+				status_rsp, 1, true },
+	{ 0x0c66, 184, "Read Flow Control Mode",
+				null_cmd, 0, true,
+				read_flow_control_mode_rsp, 2, true },
+	{ 0x0c67, 185, "Write Flow Control Mode",
+				write_flow_control_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c68, 192, "Read Enhanced Transmit Power Level",
+				read_enhanced_tx_power_cmd, 3, true,
+				read_enhanced_tx_power_rsp, 6, true },
+	{ 0x0c69, 194, "Read Best Effort Flush Timeout" },
+	{ 0x0c6a, 195, "Write Best Effort Flush Timeout" },
+	{ 0x0c6b, 196, "Short Range Mode",
+				short_range_mode_cmd, 2, true },
+	{ 0x0c6c, 197, "Read LE Host Supported",
+				null_cmd, 0, true,
+				read_le_host_supported_rsp, 3, true },
+	{ 0x0c6d, 198, "Write LE Host Supported",
+				write_le_host_supported_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x0c6e, 238, "Set MWS Channel Parameters" },
+	{ 0x0c6f, 239, "Set External Frame Configuration" },
+	{ 0x0c70, 240, "Set MWS Signaling" },
+	{ 0x0c71, 241, "Set MWS Transport Layer" },
+	{ 0x0c72, 242, "Set MWS Scan Frequency Table" },
+	{ 0x0c73, 244, "Set MWS Pattern Configuration" },
+	{ 0x0c74, 252, "Set Reserved LT_ADDR",
+				set_reserved_lt_addr_cmd, 1, true,
+				set_reserved_lt_addr_rsp, 2, true },
+	{ 0x0c75, 253, "Delete Reserved LT_ADDR",
+				delete_reserved_lt_addr_cmd, 1, true,
+				delete_reserved_lt_addr_rsp, 2, true },
+	{ 0x0c76, 254, "Set Connectionless Slave Broadcast Data",
+				set_slave_broadcast_data_cmd, 3, false,
+				set_slave_broadcast_data_rsp, 2, true },
+	{ 0x0c77, 255, "Read Synchronization Train Parameters",
+				null_cmd, 0, true,
+				read_sync_train_params_rsp, 8, true },
+	{ 0x0c78, 256, "Write Synchronization Train Parameters",
+				write_sync_train_params_cmd, 9, true,
+				write_sync_train_params_rsp, 3, true },
+	{ 0x0c79, 258, "Read Secure Connections Host Support",
+				null_cmd, 0, true,
+				read_secure_conn_support_rsp, 2, true },
+	{ 0x0c7a, 259, "Write Secure Connections Host Support",
+				write_secure_conn_support_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x0c7b, 260, "Read Authenticated Payload Timeout",
+				read_auth_payload_timeout_cmd, 2, true,
+				read_auth_payload_timeout_rsp, 5, true },
+	{ 0x0c7c, 261, "Write Authenticated Payload Timeout",
+				write_auth_payload_timeout_cmd, 4, true,
+				write_auth_payload_timeout_rsp, 3, true },
+	{ 0x0c7d, 262, "Read Local OOB Extended Data",
+				null_cmd, 0, true,
+				read_local_oob_ext_data_rsp, 65, true },
+	{ 0x0c7e, 264, "Read Extended Page Timeout",
+				null_cmd, 0, true,
+				read_ext_page_timeout_rsp, 3, true },
+	{ 0x0c7f, 265, "Write Extended Page Timeout",
+				write_ext_page_timeout_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x0c80, 266, "Read Extended Inquiry Length",
+				null_cmd, 0, true,
+				read_ext_inquiry_length_rsp, 3, true },
+	{ 0x0c81, 267, "Write Extended Inquiry Length",
+				write_ext_inquiry_length_cmd, 2, true,
+				status_rsp, 1, true },
+
+	/* OGF 4 - Information Parameter */
+	{ 0x1001, 115, "Read Local Version Information",
+				null_cmd, 0, true,
+				read_local_version_rsp, 9, true },
+	{ 0x1002, 116, "Read Local Supported Commands",
+				null_cmd, 0, true,
+				read_local_commands_rsp, 65, true },
+	{ 0x1003, 117, "Read Local Supported Features",
+				null_cmd, 0, true,
+				read_local_features_rsp, 9, true },
+	{ 0x1004, 118, "Read Local Extended Features",
+				read_local_ext_features_cmd, 1, true,
+				read_local_ext_features_rsp, 11, true },
+	{ 0x1005, 119, "Read Buffer Size",
+				null_cmd, 0, true,
+				read_buffer_size_rsp, 8, true },
+	{ 0x1007, 120, "Read Country Code",
+				null_cmd, 0, true,
+				read_country_code_rsp, 2, true },
+	{ 0x1009, 121, "Read BD ADDR",
+				null_cmd, 0, true,
+				read_bd_addr_rsp, 7, true },
+	{ 0x100a, 186, "Read Data Block Size",
+				null_cmd, 0, true,
+				read_data_block_size_rsp, 7, true },
+	{ 0x100b, 237, "Read Local Supported Codecs",
+				null_cmd, 0, true,
+				read_local_codecs_rsp, 3, false },
+
+	/* OGF 5 - Status Parameter */
+	{ 0x1401, 122, "Read Failed Contact Counter",
+				read_failed_contact_counter_cmd, 2, true,
+				read_failed_contact_counter_rsp, 5, true },
+	{ 0x1402, 123, "Reset Failed Contact Counter",
+				reset_failed_contact_counter_cmd, 2, true,
+				reset_failed_contact_counter_rsp, 3, true },
+	{ 0x1403, 124, "Read Link Quality",
+				read_link_quality_cmd, 2, true,
+				read_link_quality_rsp, 4, true },
+	{ 0x1405, 125, "Read RSSI",
+				read_rssi_cmd, 2, true,
+				read_rssi_rsp, 4, true },
+	{ 0x1406, 126, "Read AFH Channel Map",
+				read_afh_channel_map_cmd, 2, true,
+				read_afh_channel_map_rsp, 14, true },
+	{ 0x1407, 127, "Read Clock",
+				read_clock_cmd, 3, true,
+				read_clock_rsp, 9, true },
+	{ 0x1408, 164, "Read Encryption Key Size",
+				read_encrypt_key_size_cmd, 2, true,
+				read_encrypt_key_size_rsp, 4, true },
+	{ 0x1409, 181, "Read Local AMP Info",
+				null_cmd, 0, true,
+				read_local_amp_info_rsp, 31, true },
+	{ 0x140a, 182, "Read Local AMP ASSOC",
+				read_local_amp_assoc_cmd, 5, true,
+				read_local_amp_assoc_rsp, 5, false },
+	{ 0x140b, 183, "Write Remote AMP ASSOC",
+				write_remote_amp_assoc_cmd, 6, false,
+				write_remote_amp_assoc_rsp, 2, true },
+	{ 0x140c, 243, "Get MWS Transport Layer Configuration",
+				null_cmd, 0, true,
+				get_mws_transport_config_rsp, 2, false },
+	{ 0x140d, 245, "Set Triggered Clock Capture",
+				set_triggered_clock_capture_cmd, 6, true,
+				status_rsp, 1, true },
+
+	/* OGF 6 - Testing */
+	{ 0x1801, 128, "Read Loopback Mode",
+				null_cmd, 0, true,
+				read_loopback_mode_rsp, 2, true },
+	{ 0x1802, 129, "Write Loopback Mode",
+				write_loopback_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x1803, 130, "Enable Device Under Test Mode",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x1804, 157, "Write Simple Pairing Debug Mode",
+				write_ssp_debug_mode_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x1807, 189, "Enable AMP Receiver Reports" },
+	{ 0x1808, 190, "AMP Test End" },
+	{ 0x1809, 191, "AMP Test" },
+	{ 0x180a, 263, "Write Secure Connections Test Mode" },
+
+	/* OGF 8 - LE Control */
+	{ 0x2001, 200, "LE Set Event Mask",
+				le_set_event_mask_cmd, 8, true,
+				status_rsp, 1, true },
+	{ 0x2002, 201, "LE Read Buffer Size",
+				null_cmd, 0, true,
+				le_read_buffer_size_rsp, 4, true },
+	{ 0x2003, 202, "LE Read Local Supported Features",
+				null_cmd, 0, true,
+				le_read_local_features_rsp, 9, true },
+	{ 0x2005, 204, "LE Set Random Address",
+				le_set_random_address_cmd, 6, true,
+				status_rsp, 1, true },
+	{ 0x2006, 205, "LE Set Advertising Parameters",
+				le_set_adv_parameters_cmd, 15, true,
+				status_rsp, 1, true },
+	{ 0x2007, 206, "LE Read Advertising Channel TX Power",
+				null_cmd, 0, true,
+				le_read_adv_tx_power_rsp, 2, true },
+	{ 0x2008, 207, "LE Set Advertising Data",
+				le_set_adv_data_cmd, 32, true,
+				status_rsp, 1, true },
+	{ 0x2009, 208, "LE Set Scan Response Data",
+				le_set_scan_rsp_data_cmd, 32, true,
+				status_rsp, 1, true },
+	{ 0x200a, 209, "LE Set Advertise Enable",
+				le_set_adv_enable_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x200b, 210, "LE Set Scan Parameters",
+				le_set_scan_parameters_cmd, 7, true,
+				status_rsp, 1, true },
+	{ 0x200c, 211, "LE Set Scan Enable",
+				le_set_scan_enable_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x200d, 212, "LE Create Connection",
+				le_create_conn_cmd, 25, true },
+	{ 0x200e, 213, "LE Create Connection Cancel",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x200f, 214, "LE Read White List Size",
+				null_cmd, 0, true,
+				le_read_white_list_size_rsp, 2, true },
+	{ 0x2010, 215, "LE Clear White List",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x2011, 216, "LE Add Device To White List",
+				le_add_to_white_list_cmd, 7, true,
+				status_rsp, 1, true },
+	{ 0x2012, 217, "LE Remove Device From White List",
+				le_remove_from_white_list_cmd, 7, true,
+				status_rsp, 1, true },
+	{ 0x2013, 218, "LE Connection Update",
+				le_conn_update_cmd, 14, true },
+	{ 0x2014, 219, "LE Set Host Channel Classification",
+				le_set_host_classification_cmd, 5, true,
+				status_rsp, 1, true },
+	{ 0x2015, 220, "LE Read Channel Map",
+				le_read_channel_map_cmd, 2, true,
+				le_read_channel_map_rsp, 8, true },
+	{ 0x2016, 221, "LE Read Remote Used Features",
+				le_read_remote_features_cmd, 2, true },
+	{ 0x2017, 222, "LE Encrypt",
+				le_encrypt_cmd, 32, true,
+				le_encrypt_rsp, 17, true },
+	{ 0x2018, 223, "LE Rand",
+				null_cmd, 0, true,
+				le_rand_rsp, 9, true },
+	{ 0x2019, 224, "LE Start Encryption",
+				le_start_encrypt_cmd, 28, true },
+	{ 0x201a, 225, "LE Long Term Key Request Reply",
+				le_ltk_req_reply_cmd, 18, true,
+				le_ltk_req_reply_rsp, 3, true },
+	{ 0x201b, 226, "LE Long Term Key Request Neg Reply",
+				le_ltk_req_neg_reply_cmd, 2, true,
+				le_ltk_req_neg_reply_rsp, 3, true },
+	{ 0x201c, 227, "LE Read Supported States",
+				null_cmd, 0, true,
+				le_read_supported_states_rsp, 9, true },
+	{ 0x201d, 228, "LE Receiver Test",
+				le_receiver_test_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x201e, 229, "LE Transmitter Test",
+				le_transmitter_test_cmd, 3, true,
+				status_rsp, 1, true },
+	{ 0x201f, 230, "LE Test End",
+				null_cmd, 0, true,
+				le_test_end_rsp, 3, true },
+	{ 0x2020, 268, "LE Remote Connection Parameter Request Reply",
+				le_conn_param_req_reply_cmd, 14, true,
+				le_conn_param_req_reply_rsp, 3, true },
+	{ 0x2021, 269, "LE Remote Connection Parameter Request Negative Reply",
+				le_conn_param_req_neg_reply_cmd, 3, true,
+				le_conn_param_req_neg_reply_rsp, 3, true },
+	{ 0x2022, 270, "LE Set Data Length",
+				le_set_data_length_cmd, 6, true,
+				le_set_data_length_rsp, 3, true },
+	{ 0x2023, 271, "LE Read Suggested Default Data Length",
+				null_cmd, 0, true,
+				le_read_default_data_length_rsp, 5, true },
+	{ 0x2024, 272, "LE Write Suggested Default Data Length",
+				le_write_default_data_length_cmd, 4, true,
+				status_rsp, 1, true },
+	{ 0x2025, 273, "LE Read Local P-256 Public Key",
+				null_cmd, 0, true },
+	{ 0x2026, 274, "LE Generate DHKey",
+				le_generate_dhkey_cmd, 64, true },
+	{ 0x2027, 275, "LE Add Device To Resolving List",
+				le_add_to_resolv_list_cmd, 39, true,
+				status_rsp, 1, true },
+	{ 0x2028, 276, "LE Remove Device From Resolving List",
+				le_remove_from_resolv_list_cmd, 7, true,
+				status_rsp, 1, true },
+	{ 0x2029, 277, "LE Clear Resolving List",
+				null_cmd, 0, true,
+				status_rsp, 1, true },
+	{ 0x202a, 278, "LE Read Resolving List Size",
+				null_cmd, 0, true,
+				le_read_resolv_list_size_rsp, 2, true },
+	{ 0x202b, 279, "LE Read Peer Resolvable Address",
+				le_read_peer_resolv_addr_cmd, 7, true,
+				le_read_peer_resolv_addr_rsp, 7, true },
+	{ 0x202c, 280, "LE Read Local Resolvable Address",
+				le_read_local_resolv_addr_cmd, 7, true,
+				le_read_local_resolv_addr_rsp, 7, true },
+	{ 0x202d, 281, "LE Set Address Resolution Enable",
+				le_set_resolv_enable_cmd, 1, true,
+				status_rsp, 1, true },
+	{ 0x202e, 282, "LE Set Resolvable Private Address Timeout",
+				le_set_resolv_timeout_cmd, 2, true,
+				status_rsp, 1, true },
+	{ 0x202f, 283, "LE Read Maximum Data Length",
+				null_cmd, 0, true,
+				le_read_max_data_length_rsp, 9, true },
+	{ }
+};
+
+static const char *get_supported_command(int bit)
+{
+	int i;
+
+	for (i = 0; opcode_table[i].str; i++) {
+		if (opcode_table[i].bit == bit)
+			return opcode_table[i].str;
+	}
+
+	return NULL;
+}
+
+static const char *current_vendor_str(void)
+{
+	uint16_t manufacturer;
+
+	if (index_current < MAX_INDEX)
+		manufacturer = index_list[index_current].manufacturer;
+	else
+		manufacturer = UNKNOWN_MANUFACTURER;
+
+	switch (manufacturer) {
+	case 2:
+		return "Intel";
+	case 15:
+		return "Broadcom";
+	}
+
+	return NULL;
+}
+
+static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)
+{
+	uint16_t manufacturer;
+
+	if (index_current < MAX_INDEX)
+		manufacturer = index_list[index_current].manufacturer;
+	else
+		manufacturer = UNKNOWN_MANUFACTURER;
+
+	switch (manufacturer) {
+	case 2:
+		return intel_vendor_ocf(ocf);
+	case 15:
+		return broadcom_vendor_ocf(ocf);
+	}
+
+	return NULL;
+}
+
+static const struct vendor_evt *current_vendor_evt(uint8_t evt)
+{
+	uint16_t manufacturer;
+
+	if (index_current < MAX_INDEX)
+		manufacturer = index_list[index_current].manufacturer;
+	else
+		manufacturer = UNKNOWN_MANUFACTURER;
+
+	switch (manufacturer) {
+	case 2:
+		return intel_vendor_evt(evt);
+	case 15:
+		return broadcom_vendor_evt(evt);
+	}
+
+	return NULL;
+}
+
+static void inquiry_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_inquiry_complete *evt = data;
+
+	print_status(evt->status);
+}
+
+static void inquiry_result_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_inquiry_result *evt = data;
+
+	print_num_resp(evt->num_resp);
+	print_bdaddr(evt->bdaddr);
+	print_pscan_rep_mode(evt->pscan_rep_mode);
+	print_pscan_period_mode(evt->pscan_period_mode);
+	print_pscan_mode(evt->pscan_mode);
+	print_dev_class(evt->dev_class);
+	print_clock_offset(evt->clock_offset);
+
+	if (size > sizeof(*evt))
+		packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
+}
+
+static void conn_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_conn_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_bdaddr(evt->bdaddr);
+	print_link_type(evt->link_type);
+	print_encr_mode(evt->encr_mode);
+
+	if (evt->status == 0x00)
+		assign_handle(le16_to_cpu(evt->handle), 0x00);
+}
+
+static void conn_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_conn_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_dev_class(evt->dev_class);
+	print_link_type(evt->link_type);
+}
+
+static void disconnect_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_disconnect_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_reason(evt->reason);
+
+	if (evt->status == 0x00)
+		release_handle(le16_to_cpu(evt->handle));
+}
+
+static void auth_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_auth_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+}
+
+static void remote_name_request_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_remote_name_request_complete *evt = data;
+
+	print_status(evt->status);
+	print_bdaddr(evt->bdaddr);
+	print_name(evt->name);
+}
+
+static void encrypt_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_encrypt_change *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_encr_mode_change(evt->encr_mode, evt->handle);
+}
+
+static void change_conn_link_key_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_change_conn_link_key_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+}
+
+static void master_link_key_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_master_link_key_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_key_flag(evt->key_flag);
+}
+
+static void remote_features_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_remote_features_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_features(0, evt->features, 0x00);
+}
+
+static void remote_version_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_remote_version_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_lmp_version(evt->lmp_ver, evt->lmp_subver);
+	print_manufacturer(evt->manufacturer);
+
+	switch (le16_to_cpu(evt->manufacturer)) {
+	case 15:
+		print_manufacturer_broadcom(evt->lmp_subver, 0xffff);
+		break;
+	}
+}
+
+static void qos_setup_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_qos_setup_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_field("Flags: 0x%2.2x", evt->flags);
+
+	print_service_type(evt->service_type);
+
+	print_field("Token rate: %d", le32_to_cpu(evt->token_rate));
+	print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth));
+	print_field("Latency: %d", le32_to_cpu(evt->latency));
+	print_field("Delay variation: %d", le32_to_cpu(evt->delay_variation));
+}
+
+static void cmd_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_cmd_complete *evt = data;
+	uint16_t opcode = le16_to_cpu(evt->opcode);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+	struct opcode_data vendor_data;
+	const struct opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	char vendor_str[150];
+	int i;
+
+	for (i = 0; opcode_table[i].str; i++) {
+		if (opcode_table[i].opcode == opcode) {
+			opcode_data = &opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		if (opcode_data->rsp_func)
+			opcode_color = COLOR_HCI_COMMAND;
+		else
+			opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+		opcode_str = opcode_data->str;
+	} else {
+		if (ogf == 0x3f) {
+			const struct vendor_ocf *vnd = current_vendor_ocf(ocf);
+
+			if (vnd) {
+				const char *str = current_vendor_str();
+
+				if (str) {
+					snprintf(vendor_str, sizeof(vendor_str),
+							"%s %s", str, vnd->str);
+					vendor_data.str = vendor_str;
+				} else
+					vendor_data.str = vnd->str;
+				vendor_data.rsp_func = vnd->rsp_func;
+				vendor_data.rsp_size = vnd->rsp_size;
+				vendor_data.rsp_fixed = vnd->rsp_fixed;
+
+				opcode_data = &vendor_data;
+
+				if (opcode_data->rsp_func)
+					opcode_color = COLOR_HCI_COMMAND;
+				else
+					opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+				opcode_str = opcode_data->str;
+			} else {
+				opcode_color = COLOR_HCI_COMMAND;
+				opcode_str = "Vendor";
+			}
+		} else {
+			opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+			opcode_str = "Unknown";
+		}
+	}
+
+	print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+			" (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd);
+
+	if (!opcode_data || !opcode_data->rsp_func) {
+		if (size > 3) {
+			uint8_t status = *((uint8_t *) (data + 3));
+
+			print_status(status);
+			packet_hexdump(data + 4, size - 4);
+		}
+		return;
+	}
+
+	if (opcode_data->rsp_size > 1 && size - 3 == 1) {
+		uint8_t status = *((uint8_t *) (data + 3));
+
+		print_status(status);
+		return;
+	}
+
+	if (opcode_data->rsp_fixed) {
+		if (size - 3 != opcode_data->rsp_size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data + 3, size - 3);
+			return;
+		}
+	} else {
+		if (size - 3 < opcode_data->rsp_size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data + 3, size - 3);
+			return;
+		}
+	}
+
+	opcode_data->rsp_func(data + 3, size - 3);
+}
+
+static void cmd_status_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_cmd_status *evt = data;
+	uint16_t opcode = le16_to_cpu(evt->opcode);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+	const struct opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	char vendor_str[150];
+	int i;
+
+	for (i = 0; opcode_table[i].str; i++) {
+		if (opcode_table[i].opcode == opcode) {
+			opcode_data = &opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		opcode_color = COLOR_HCI_COMMAND;
+		opcode_str = opcode_data->str;
+	} else {
+		if (ogf == 0x3f) {
+			const struct vendor_ocf *vnd = current_vendor_ocf(ocf);
+
+			if (vnd) {
+				const char *str = current_vendor_str();
+
+				if (str) {
+					snprintf(vendor_str, sizeof(vendor_str),
+							"%s %s", str, vnd->str);
+					opcode_str = vendor_str;
+				} else
+					opcode_str = vnd->str;
+
+				opcode_color = COLOR_HCI_COMMAND;
+			} else {
+				opcode_color = COLOR_HCI_COMMAND;
+				opcode_str = "Vendor";
+			}
+		} else {
+			opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+			opcode_str = "Unknown";
+		}
+	}
+
+	print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
+			" (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd);
+
+	print_status(evt->status);
+}
+
+static void hardware_error_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_hardware_error *evt = data;
+
+	print_field("Code: 0x%2.2x", evt->code);
+}
+
+static void flush_occurred_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_flush_occurred *evt = data;
+
+	print_handle(evt->handle);
+}
+
+static void role_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_role_change *evt = data;
+
+	print_status(evt->status);
+	print_bdaddr(evt->bdaddr);
+	print_role(evt->role);
+}
+
+static void num_completed_packets_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_num_completed_packets *evt = data;
+
+	print_field("Num handles: %d", evt->num_handles);
+	print_handle(evt->handle);
+	print_field("Count: %d", le16_to_cpu(evt->count));
+
+	if (size > sizeof(*evt))
+		packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
+}
+
+static void mode_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_mode_change *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_mode(evt->mode);
+	print_interval(evt->interval);
+}
+
+static void return_link_keys_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_return_link_keys *evt = data;
+	uint8_t i;
+
+	print_field("Num keys: %d", evt->num_keys);
+
+	for (i = 0; i < evt->num_keys; i++) {
+		print_bdaddr(evt->keys + (i * 22));
+		print_link_key(evt->keys + (i * 22) + 6);
+	}
+}
+
+static void pin_code_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_pin_code_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+}
+
+static void link_key_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_link_key_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+}
+
+static void link_key_notify_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_link_key_notify *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_link_key(evt->link_key);
+	print_key_type(evt->key_type);
+}
+
+static void loopback_command_evt(const void *data, uint8_t size)
+{
+	packet_hexdump(data, size);
+}
+
+static void data_buffer_overflow_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_data_buffer_overflow *evt = data;
+
+	print_link_type(evt->link_type);
+}
+
+static void max_slots_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_max_slots_change *evt = data;
+
+	print_handle(evt->handle);
+	print_field("Max slots: %d", evt->max_slots);
+}
+
+static void clock_offset_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_clock_offset_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_clock_offset(evt->clock_offset);
+}
+
+static void conn_pkt_type_changed_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_conn_pkt_type_changed *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_pkt_type(evt->pkt_type);
+}
+
+static void qos_violation_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_qos_violation *evt = data;
+
+	print_handle(evt->handle);
+}
+
+static void pscan_mode_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_pscan_mode_change *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_pscan_mode(evt->pscan_mode);
+}
+
+static void pscan_rep_mode_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_pscan_rep_mode_change *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_pscan_rep_mode(evt->pscan_rep_mode);
+}
+
+static void flow_spec_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_flow_spec_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_field("Flags: 0x%2.2x", evt->flags);
+
+	print_flow_direction(evt->direction);
+	print_service_type(evt->service_type);
+
+	print_field("Token rate: %d", le32_to_cpu(evt->token_rate));
+	print_field("Token bucket size: %d",
+					le32_to_cpu(evt->token_bucket_size));
+	print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth));
+	print_field("Access latency: %d", le32_to_cpu(evt->access_latency));
+}
+
+static void inquiry_result_with_rssi_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_inquiry_result_with_rssi *evt = data;
+
+	print_num_resp(evt->num_resp);
+	print_bdaddr(evt->bdaddr);
+	print_pscan_rep_mode(evt->pscan_rep_mode);
+	print_pscan_period_mode(evt->pscan_period_mode);
+	print_dev_class(evt->dev_class);
+	print_clock_offset(evt->clock_offset);
+	print_rssi(evt->rssi);
+
+	if (size > sizeof(*evt))
+		packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
+}
+
+static void remote_ext_features_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_remote_ext_features_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_field("Page: %d/%d", evt->page, evt->max_page);
+	print_features(evt->page, evt->features, 0x00);
+}
+
+static void sync_conn_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_sync_conn_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_bdaddr(evt->bdaddr);
+	print_link_type(evt->link_type);
+	print_field("Transmission interval: 0x%2.2x", evt->tx_interval);
+	print_field("Retransmission window: 0x%2.2x", evt->retrans_window);
+	print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len));
+	print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len));
+	print_air_mode(evt->air_mode);
+}
+
+static void sync_conn_changed_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_sync_conn_changed *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_field("Transmission interval: 0x%2.2x", evt->tx_interval);
+	print_field("Retransmission window: 0x%2.2x", evt->retrans_window);
+	print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len));
+	print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len));
+}
+
+static void sniff_subrating_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_sniff_subrating *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_slot_625("Max transmit latency", evt->max_tx_latency);
+	print_slot_625("Max receive latency", evt->max_rx_latency);
+	print_slot_625("Min remote timeout", evt->min_remote_timeout);
+	print_slot_625("Min local timeout", evt->min_local_timeout);
+}
+
+static void ext_inquiry_result_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_ext_inquiry_result *evt = data;
+
+	print_num_resp(evt->num_resp);
+	print_bdaddr(evt->bdaddr);
+	print_pscan_rep_mode(evt->pscan_rep_mode);
+	print_pscan_period_mode(evt->pscan_period_mode);
+	print_dev_class(evt->dev_class);
+	print_clock_offset(evt->clock_offset);
+	print_rssi(evt->rssi);
+	print_eir(evt->data, sizeof(evt->data), false);
+}
+
+static void encrypt_key_refresh_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_encrypt_key_refresh_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+}
+
+static void io_capability_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_io_capability_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+}
+
+static void io_capability_response_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_io_capability_response *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_io_capability(evt->capability);
+	print_oob_data_response(evt->oob_data);
+	print_authentication(evt->authentication);
+}
+
+static void user_confirm_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_user_confirm_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_passkey(evt->passkey);
+}
+
+static void user_passkey_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_user_passkey_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+}
+
+static void remote_oob_data_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_remote_oob_data_request *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+}
+
+static void simple_pairing_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_simple_pairing_complete *evt = data;
+
+	print_status(evt->status);
+	print_bdaddr(evt->bdaddr);
+}
+
+static void link_supv_timeout_changed_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_link_supv_timeout_changed *evt = data;
+
+	print_handle(evt->handle);
+	print_timeout(evt->timeout);
+}
+
+static void enhanced_flush_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_enhanced_flush_complete *evt = data;
+
+	print_handle(evt->handle);
+}
+
+static void user_passkey_notify_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_user_passkey_notify *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_passkey(evt->passkey);
+}
+
+static void keypress_notify_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_keypress_notify *evt = data;
+	const char *str;
+
+	print_bdaddr(evt->bdaddr);
+
+	switch (evt->type) {
+	case 0x00:
+		str = "Passkey entry started";
+		break;
+	case 0x01:
+		str = "Passkey digit entered";
+		break;
+	case 0x02:
+		str = "Passkey digit erased";
+		break;
+	case 0x03:
+		str = "Passkey clared";
+		break;
+	case 0x04:
+		str = "Passkey entry completed";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Notification type: %s (0x%2.2x)", str, evt->type);
+}
+
+static void remote_host_features_notify_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_remote_host_features_notify *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_features(1, evt->features, 0x00);
+}
+
+static void phy_link_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_phy_link_complete *evt = data;
+
+	print_status(evt->status);
+	print_phy_handle(evt->phy_handle);
+}
+
+static void channel_selected_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_channel_selected *evt = data;
+
+	print_phy_handle(evt->phy_handle);
+}
+
+static void disconn_phy_link_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_disconn_phy_link_complete *evt = data;
+
+	print_status(evt->status);
+	print_phy_handle(evt->phy_handle);
+	print_reason(evt->reason);
+}
+
+static void phy_link_loss_early_warning_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_phy_link_loss_early_warning *evt = data;
+	const char *str;
+
+	print_phy_handle(evt->phy_handle);
+
+	switch (evt->reason) {
+	case 0x00:
+		str = "Unknown";
+		break;
+	case 0x01:
+		str = "Range related";
+		break;
+	case 0x02:
+		str = "Bandwidth related";
+		break;
+	case 0x03:
+		str = "Resolving conflict";
+		break;
+	case 0x04:
+		str = "Interference";
+		break;
+	default:
+		str = "Reserved";
+		break;
+	}
+
+	print_field("Reason: %s (0x%2.2x)", str, evt->reason);
+}
+
+static void phy_link_recovery_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_phy_link_recovery *evt = data;
+
+	print_phy_handle(evt->phy_handle);
+}
+
+static void logic_link_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_logic_link_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_phy_handle(evt->phy_handle);
+	print_field("TX flow spec: 0x%2.2x", evt->flow_spec);
+}
+
+static void disconn_logic_link_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_disconn_logic_link_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_reason(evt->reason);
+}
+
+static void flow_spec_modify_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_flow_spec_modify_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+}
+
+static void num_completed_data_blocks_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_num_completed_data_blocks *evt = data;
+
+	print_field("Total num data blocks: %d",
+				le16_to_cpu(evt->total_num_blocks));
+	print_field("Num handles: %d", evt->num_handles);
+	print_handle(evt->handle);
+	print_field("Num packets: %d", evt->num_packets);
+	print_field("Num blocks: %d", evt->num_blocks);
+
+	if (size > sizeof(*evt))
+		packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
+}
+
+static void short_range_mode_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_short_range_mode_change *evt = data;
+
+	print_status(evt->status);
+	print_phy_handle(evt->phy_handle);
+	print_short_range_mode(evt->mode);
+}
+
+static void amp_status_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_amp_status_change *evt = data;
+
+	print_status(evt->status);
+	print_amp_status(evt->amp_status);
+}
+
+static void triggered_clock_capture_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_triggered_clock_capture *evt = data;
+
+	print_handle(evt->handle);
+	print_clock_type(evt->type);
+	print_clock(evt->clock);
+	print_clock_offset(evt->clock_offset);
+}
+
+static void sync_train_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_sync_train_complete *evt = data;
+
+	print_status(evt->status);
+}
+
+static void sync_train_received_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_sync_train_received *evt = data;
+
+	print_status(evt->status);
+	print_bdaddr(evt->bdaddr);
+	print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset));
+	print_channel_map(evt->map);
+	print_lt_addr(evt->lt_addr);
+	print_field("Next broadcast instant: 0x%4.4x",
+					le16_to_cpu(evt->instant));
+	print_interval(evt->interval);
+	print_field("Service Data: 0x%2.2x", evt->service_data);
+}
+
+static void slave_broadcast_receive_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_slave_broadcast_receive *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_lt_addr(evt->lt_addr);
+	print_field("Clock: 0x%8.8x", le32_to_cpu(evt->clock));
+	print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset));
+	print_field("Receive status: 0x%2.2x", evt->status);
+	print_broadcast_fragment(evt->fragment);
+	print_field("Length: %d", evt->length);
+
+	if (size - 18 != evt->length)
+		print_text(COLOR_ERROR, "invalid data size (%d != %d)",
+						size - 18, evt->length);
+
+	if (evt->lt_addr == 0x01 && evt->length == 17)
+		print_3d_broadcast(data + 18, size - 18);
+	else
+		packet_hexdump(data + 18, size - 18);
+}
+
+static void slave_broadcast_timeout_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_slave_broadcast_timeout *evt = data;
+
+	print_bdaddr(evt->bdaddr);
+	print_lt_addr(evt->lt_addr);
+}
+
+static void truncated_page_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_truncated_page_complete *evt = data;
+
+	print_status(evt->status);
+	print_bdaddr(evt->bdaddr);
+}
+
+static void slave_page_response_timeout_evt(const void *data, uint8_t size)
+{
+}
+
+static void slave_broadcast_channel_map_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_slave_broadcast_channel_map_change *evt = data;
+
+	print_channel_map(evt->map);
+}
+
+static void inquiry_response_notify_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_inquiry_response_notify *evt = data;
+
+	print_iac(evt->lap);
+	print_rssi(evt->rssi);
+}
+
+static void auth_payload_timeout_expired_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_auth_payload_timeout_expired *evt = data;
+
+	print_handle(evt->handle);
+}
+
+static void le_conn_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_conn_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_role(evt->role);
+	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_field("Supervision timeout: %d msec (0x%4.4x)",
+					le16_to_cpu(evt->supv_timeout) * 10,
+					le16_to_cpu(evt->supv_timeout));
+	print_field("Master clock accuracy: 0x%2.2x", evt->clock_accuracy);
+
+	if (evt->status == 0x00)
+		assign_handle(le16_to_cpu(evt->handle), 0x01);
+}
+
+static void le_adv_report_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_adv_report *evt = data;
+	uint8_t evt_len;
+	int8_t *rssi;
+
+	print_num_reports(evt->num_reports);
+
+report:
+	print_adv_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);
+	print_eir(evt->data, evt->data_len, true);
+
+	rssi = (int8_t *) (evt->data + evt->data_len);
+	print_rssi(*rssi);
+
+	evt_len = sizeof(*evt) + evt->data_len + 1;
+
+	if (size > evt_len) {
+		data += evt_len - 1;
+		size -= evt_len - 1;
+		evt = data;
+		goto report;
+	}
+}
+
+static void le_conn_update_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_conn_update_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_slot_125("Connection interval", evt->interval);
+	print_slot_125("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));
+}
+
+static void le_remote_features_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_remote_features_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_features(0, evt->features, 0x01);
+}
+
+static void le_long_term_key_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_long_term_key_request *evt = data;
+
+	print_handle(evt->handle);
+	print_random_number(evt->rand);
+	print_encrypted_diversifier(evt->ediv);
+}
+
+static void le_conn_param_request_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_conn_param_request *evt = data;
+
+	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_field("Supervision timeout: %d msec (0x%4.4x)",
+					le16_to_cpu(evt->supv_timeout) * 10,
+					le16_to_cpu(evt->supv_timeout));
+}
+
+static void le_data_length_change_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_data_length_change *evt = data;
+
+	print_handle(evt->handle);
+	print_field("Max TX octets: %d", le16_to_cpu(evt->max_tx_len));
+	print_field("Max TX time: %d", le16_to_cpu(evt->max_tx_time));
+	print_field("Max RX octets: %d", le16_to_cpu(evt->max_rx_len));
+	print_field("Max RX time: %d", le16_to_cpu(evt->max_rx_time));
+}
+
+static void le_read_local_pk256_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_read_local_pk256_complete *evt = data;
+
+	print_status(evt->status);
+	print_pk256("Local P-256 public key", evt->local_pk256);
+}
+
+static void le_generate_dhkey_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_generate_dhkey_complete *evt = data;
+
+	print_status(evt->status);
+	print_dhkey(evt->dhkey);
+}
+
+static void le_enhanced_conn_complete_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_enhanced_conn_complete *evt = data;
+
+	print_status(evt->status);
+	print_handle(evt->handle);
+	print_role(evt->role);
+	print_peer_addr_type("Peer address type", evt->peer_addr_type);
+	print_addr("Peer address", evt->peer_addr, evt->peer_addr_type);
+	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_field("Supervision timeout: %d msec (0x%4.4x)",
+					le16_to_cpu(evt->supv_timeout) * 10,
+					le16_to_cpu(evt->supv_timeout));
+	print_field("Master clock accuracy: 0x%2.2x", evt->clock_accuracy);
+
+	if (evt->status == 0x00)
+		assign_handle(le16_to_cpu(evt->handle), 0x01);
+}
+
+static void le_direct_adv_report_evt(const void *data, uint8_t size)
+{
+	const struct bt_hci_evt_le_direct_adv_report *evt = data;
+
+	print_num_reports(evt->num_reports);
+
+	print_adv_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);
+	print_addr("Direct address", evt->direct_addr, evt->direct_addr_type);
+	print_rssi(evt->rssi);
+
+	if (size > sizeof(*evt))
+		packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
+}
+
+struct subevent_data {
+	uint8_t subevent;
+	const char *str;
+	void (*func) (const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+};
+
+static void print_subevent(const struct subevent_data *subevent_data,
+					const void *data, uint8_t size)
+{
+	const char *subevent_color;
+
+	if (subevent_data->func)
+		subevent_color = COLOR_HCI_EVENT;
+	else
+		subevent_color = COLOR_HCI_EVENT_UNKNOWN;
+
+	print_indent(6, subevent_color, "", subevent_data->str, COLOR_OFF,
+					" (0x%2.2x)", subevent_data->subevent);
+
+	if (!subevent_data->func) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (subevent_data->fixed) {
+		if (size != subevent_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+	} else {
+		if (size < subevent_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	subevent_data->func(data, size);
+}
+
+static const struct subevent_data le_meta_event_table[] = {
+	{ 0x01, "LE Connection Complete",
+				le_conn_complete_evt, 18, true },
+	{ 0x02, "LE Advertising Report",
+				le_adv_report_evt, 1, false },
+	{ 0x03, "LE Connection Update Complete",
+				le_conn_update_complete_evt, 9, true },
+	{ 0x04, "LE Read Remote Used Features",
+				le_remote_features_complete_evt, 11, true },
+	{ 0x05, "LE Long Term Key Request",
+				le_long_term_key_request_evt, 12, true },
+	{ 0x06, "LE Remote Connection Parameter Request",
+				le_conn_param_request_evt, 10, true },
+	{ 0x07, "LE Data Length Change",
+				le_data_length_change_evt, 10, true },
+	{ 0x08, "LE Read Local P-256 Public Key Complete",
+				le_read_local_pk256_complete_evt, 65, true },
+	{ 0x09, "LE Generate DHKey Complete",
+				le_generate_dhkey_complete_evt, 33, true },
+	{ 0x0a, "LE Enhanced Connection Complete",
+				le_enhanced_conn_complete_evt, 30, true },
+	{ 0x0b, "LE Direct Advertising Report",
+				le_direct_adv_report_evt, 1, false },
+	{ }
+};
+
+static void le_meta_event_evt(const void *data, uint8_t size)
+{
+	uint8_t subevent = *((const uint8_t *) data);
+	struct subevent_data unknown;
+	const struct subevent_data *subevent_data = &unknown;
+	int i;
+
+	unknown.subevent = subevent;
+	unknown.str = "Unknown";
+	unknown.func = NULL;
+	unknown.size = 0;
+	unknown.fixed = true;
+
+	for (i = 0; le_meta_event_table[i].str; i++) {
+		if (le_meta_event_table[i].subevent == subevent) {
+			subevent_data = &le_meta_event_table[i];
+			break;
+		}
+	}
+
+	print_subevent(subevent_data, data + 1, size - 1);
+}
+
+static void vendor_evt(const void *data, uint8_t size)
+{
+	uint8_t subevent = *((const uint8_t *) data);
+	struct subevent_data vendor_data;
+	char vendor_str[150];
+	const struct vendor_evt *vnd = current_vendor_evt(subevent);
+
+	if (vnd) {
+		const char *str = current_vendor_str();
+
+		if (str) {
+			snprintf(vendor_str, sizeof(vendor_str),
+						"%s %s", str, vnd->str);
+			vendor_data.str = vendor_str;
+		} else
+			vendor_data.str = vnd->str;
+		vendor_data.subevent = subevent;
+		vendor_data.func = vnd->evt_func;
+		vendor_data.size = vnd->evt_size;
+		vendor_data.fixed = vnd->evt_fixed;
+
+		print_subevent(&vendor_data, data + 1, size - 1);
+	} else {
+		uint16_t manufacturer;
+
+		if (index_current < MAX_INDEX)
+			manufacturer = index_list[index_current].manufacturer;
+		else
+			manufacturer = UNKNOWN_MANUFACTURER;
+
+		vendor_event(manufacturer, data, size);
+	}
+}
+
+struct event_data {
+	uint8_t event;
+	const char *str;
+	void (*func) (const void *data, uint8_t size);
+	uint8_t size;
+	bool fixed;
+};
+
+static const struct event_data event_table[] = {
+	{ 0x01, "Inquiry Complete",
+				inquiry_complete_evt, 1, true },
+	{ 0x02, "Inquiry Result",
+				inquiry_result_evt, 1, false },
+	{ 0x03, "Connect Complete",
+				conn_complete_evt, 11, true },
+	{ 0x04, "Connect Request",
+				conn_request_evt, 10, true },
+	{ 0x05, "Disconnect Complete",
+				disconnect_complete_evt, 4, true },
+	{ 0x06, "Auth Complete",
+				auth_complete_evt, 3, true },
+	{ 0x07, "Remote Name Req Complete",
+				remote_name_request_complete_evt, 255, true },
+	{ 0x08, "Encryption Change",
+				encrypt_change_evt, 4, true },
+	{ 0x09, "Change Connection Link Key Complete",
+				change_conn_link_key_complete_evt, 3, true },
+	{ 0x0a, "Master Link Key Complete",
+				master_link_key_complete_evt, 4, true },
+	{ 0x0b, "Read Remote Supported Features",
+				remote_features_complete_evt, 11, true },
+	{ 0x0c, "Read Remote Version Complete",
+				remote_version_complete_evt, 8, true },
+	{ 0x0d, "QoS Setup Complete",
+				qos_setup_complete_evt, 21, true },
+	{ 0x0e, "Command Complete",
+				cmd_complete_evt, 3, false },
+	{ 0x0f, "Command Status",
+				cmd_status_evt, 4, true },
+	{ 0x10, "Hardware Error",
+				hardware_error_evt, 1, true },
+	{ 0x11, "Flush Occurred",
+				flush_occurred_evt, 2, true },
+	{ 0x12, "Role Change",
+				role_change_evt, 8, true },
+	{ 0x13, "Number of Completed Packets",
+				num_completed_packets_evt, 1, false },
+	{ 0x14, "Mode Change",
+				mode_change_evt, 6, true },
+	{ 0x15, "Return Link Keys",
+				return_link_keys_evt, 1, false },
+	{ 0x16, "PIN Code Request",
+				pin_code_request_evt, 6, true },
+	{ 0x17, "Link Key Request",
+				link_key_request_evt, 6, true },
+	{ 0x18, "Link Key Notification",
+				link_key_notify_evt, 23, true },
+	{ 0x19, "Loopback Command",
+				loopback_command_evt, 3, false },
+	{ 0x1a, "Data Buffer Overflow",
+				data_buffer_overflow_evt, 1, true },
+	{ 0x1b, "Max Slots Change",
+				max_slots_change_evt, 3, true },
+	{ 0x1c, "Read Clock Offset Complete",
+				clock_offset_complete_evt, 5, true },
+	{ 0x1d, "Connection Packet Type Changed",
+				conn_pkt_type_changed_evt, 5, true },
+	{ 0x1e, "QoS Violation",
+				qos_violation_evt, 2, true },
+	{ 0x1f, "Page Scan Mode Change",
+				pscan_mode_change_evt, 7, true },
+	{ 0x20, "Page Scan Repetition Mode Change",
+				pscan_rep_mode_change_evt, 7, true },
+	{ 0x21, "Flow Specification Complete",
+				flow_spec_complete_evt, 22, true },
+	{ 0x22, "Inquiry Result with RSSI",
+				inquiry_result_with_rssi_evt, 1, false },
+	{ 0x23, "Read Remote Extended Features",
+				remote_ext_features_complete_evt, 13, true },
+	{ 0x2c, "Synchronous Connect Complete",
+				sync_conn_complete_evt, 17, true },
+	{ 0x2d, "Synchronous Connect Changed",
+				sync_conn_changed_evt, 9, true },
+	{ 0x2e, "Sniff Subrating",
+				sniff_subrating_evt, 11, true },
+	{ 0x2f, "Extended Inquiry Result",
+				ext_inquiry_result_evt, 1, false },
+	{ 0x30, "Encryption Key Refresh Complete",
+				encrypt_key_refresh_complete_evt, 3, true },
+	{ 0x31, "IO Capability Request",
+				io_capability_request_evt, 6, true },
+	{ 0x32, "IO Capability Response",
+				io_capability_response_evt, 9, true },
+	{ 0x33, "User Confirmation Request",
+				user_confirm_request_evt, 10, true },
+	{ 0x34, "User Passkey Request",
+				user_passkey_request_evt, 6, true },
+	{ 0x35, "Remote OOB Data Request",
+				remote_oob_data_request_evt, 6, true },
+	{ 0x36, "Simple Pairing Complete",
+				simple_pairing_complete_evt, 7, true },
+	{ 0x38, "Link Supervision Timeout Changed",
+				link_supv_timeout_changed_evt, 4, true },
+	{ 0x39, "Enhanced Flush Complete",
+				enhanced_flush_complete_evt, 2, true },
+	{ 0x3b, "User Passkey Notification",
+				user_passkey_notify_evt, 10, true },
+	{ 0x3c, "Keypress Notification",
+				keypress_notify_evt, 7, true },
+	{ 0x3d, "Remote Host Supported Features",
+				remote_host_features_notify_evt, 14, true },
+	{ 0x3e, "LE Meta Event",
+				le_meta_event_evt, 1, false },
+	{ 0x40, "Physical Link Complete",
+				phy_link_complete_evt, 2, true },
+	{ 0x41, "Channel Selected",
+				channel_selected_evt, 1, true },
+	{ 0x42, "Disconnect Physical Link Complete",
+				disconn_phy_link_complete_evt, 3, true },
+	{ 0x43, "Physical Link Loss Early Warning",
+				phy_link_loss_early_warning_evt, 2, true },
+	{ 0x44, "Physical Link Recovery",
+				phy_link_recovery_evt, 1, true },
+	{ 0x45, "Logical Link Complete",
+				logic_link_complete_evt, 5, true },
+	{ 0x46, "Disconnect Logical Link Complete",
+				disconn_logic_link_complete_evt, 4, true },
+	{ 0x47, "Flow Specification Modify Complete",
+				flow_spec_modify_complete_evt, 3, true },
+	{ 0x48, "Number of Completed Data Blocks",
+				num_completed_data_blocks_evt, 3, false },
+	{ 0x49, "AMP Start Test" },
+	{ 0x4a, "AMP Test End" },
+	{ 0x4b, "AMP Receiver Report" },
+	{ 0x4c, "Short Range Mode Change Complete",
+				short_range_mode_change_evt, 3, true },
+	{ 0x4d, "AMP Status Change",
+				amp_status_change_evt, 2, true },
+	{ 0x4e, "Triggered Clock Capture",
+				triggered_clock_capture_evt, 9, true },
+	{ 0x4f, "Synchronization Train Complete",
+				sync_train_complete_evt, 1, true },
+	{ 0x50, "Synchronization Train Received",
+				sync_train_received_evt, 29, true },
+	{ 0x51, "Connectionless Slave Broadcast Receive",
+				slave_broadcast_receive_evt, 18, false },
+	{ 0x52, "Connectionless Slave Broadcast Timeout",
+				slave_broadcast_timeout_evt, 7, true },
+	{ 0x53, "Truncated Page Complete",
+				truncated_page_complete_evt, 7, true },
+	{ 0x54, "Slave Page Response Timeout",
+				slave_page_response_timeout_evt, 0, true },
+	{ 0x55, "Connectionless Slave Broadcast Channel Map Change",
+				slave_broadcast_channel_map_change_evt, 10, true },
+	{ 0x56, "Inquiry Response Notification",
+				inquiry_response_notify_evt, 4, true },
+	{ 0x57, "Authenticated Payload Timeout Expired",
+				auth_payload_timeout_expired_evt, 2, true },
+	{ 0xfe, "Testing" },
+	{ 0xff, "Vendor", vendor_evt, 0, false },
+	{ }
+};
+
+void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
+				uint8_t type, uint8_t bus, const char *name)
+{
+	char details[48];
+
+	sprintf(details, "(%s,%s,%s)", hci_typetostr(type),
+					hci_bustostr(bus), name);
+
+	print_packet(tv, NULL, index, '=', COLOR_NEW_INDEX, "New Index",
+							label, details);
+}
+
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
+{
+	print_packet(tv, NULL, index, '=', COLOR_DEL_INDEX, "Delete Index",
+							label, NULL);
+}
+
+void packet_open_index(struct timeval *tv, uint16_t index, const char *label)
+{
+	print_packet(tv, NULL, index, '=', COLOR_OPEN_INDEX, "Open Index",
+							label, NULL);
+}
+
+void packet_close_index(struct timeval *tv, uint16_t index, const char *label)
+{
+	print_packet(tv, NULL, index, '=', COLOR_CLOSE_INDEX, "Close Index",
+							label, NULL);
+}
+
+void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
+							uint16_t manufacturer)
+{
+	char details[128];
+
+	sprintf(details, "(%s)", bt_compidtostr(manufacturer));
+
+	print_packet(tv, NULL, index, '=', COLOR_INDEX_INFO, "Index Info",
+							label, details);
+}
+
+void packet_vendor_diag(struct timeval *tv, uint16_t index,
+					uint16_t manufacturer,
+					const void *data, uint16_t size)
+{
+	char extra_str[16];
+
+	sprintf(extra_str, "(len %d)", size);
+
+	print_packet(tv, NULL, index, '=', COLOR_VENDOR_DIAG,
+					"Vendor Diagnostic", NULL, extra_str);
+
+	switch (manufacturer) {
+	case 15:
+		broadcom_lm_diag(data, size);
+		break;
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+void packet_system_note(struct timeval *tv, struct ucred *cred,
+					uint16_t index, const void *message)
+{
+	print_packet(tv, cred, index, '=', COLOR_INFO, "Note", message, NULL);
+}
+
+void packet_user_logging(struct timeval *tv, struct ucred *cred,
+					uint16_t index, uint8_t priority,
+					const char *ident, const char *message)
+{
+	char pid_str[128];
+	const char *label;
+	const char *color;
+
+	if (priority > priority_level)
+		return;
+
+	switch (priority) {
+	case BTSNOOP_PRIORITY_ERR:
+		color = COLOR_ERROR;
+		break;
+	case BTSNOOP_PRIORITY_WARNING:
+		color = COLOR_WARN;
+		break;
+	case BTSNOOP_PRIORITY_INFO:
+		color = COLOR_INFO;
+		break;
+	case BTSNOOP_PRIORITY_DEBUG:
+		color = COLOR_DEBUG;
+		break;
+	default:
+		color = COLOR_WHITE_BG;
+		break;
+	}
+
+	if (cred) {
+		char *path = alloca(24);
+		char line[128];
+		FILE *fp;
+
+		snprintf(path, 23, "/proc/%u/comm", cred->pid);
+
+		fp = fopen(path, "re");
+		if (fp) {
+			if (fgets(line, sizeof(line), fp)) {
+				line[strcspn(line, "\r\n")] = '\0';
+				snprintf(pid_str, sizeof(pid_str), "%s[%u]",
+							line, cred->pid);
+			} else
+				snprintf(pid_str, sizeof(pid_str), "%u",
+								cred->pid);
+			fclose(fp);
+		} else
+			snprintf(pid_str, sizeof(pid_str), "%u", cred->pid);
+
+		label = pid_str;
+        } else {
+		if (ident)
+			label = ident;
+		else
+			label = "Message";
+	}
+
+	print_packet(tv, cred, index, '=', color, label, message, NULL);
+}
+
+void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const hci_command_hdr *hdr = data;
+	uint16_t opcode = le16_to_cpu(hdr->opcode);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+	struct opcode_data vendor_data;
+	const struct opcode_data *opcode_data = NULL;
+	const char *opcode_color, *opcode_str;
+	char extra_str[25], vendor_str[150];
+	int i;
+
+	if (size < HCI_COMMAND_HDR_SIZE) {
+		sprintf(extra_str, "(len %d)", size);
+		print_packet(tv, cred, index, '*', COLOR_ERROR,
+			"Malformed HCI Command packet", NULL, extra_str);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	data += HCI_COMMAND_HDR_SIZE;
+	size -= HCI_COMMAND_HDR_SIZE;
+
+	for (i = 0; opcode_table[i].str; i++) {
+		if (opcode_table[i].opcode == opcode) {
+			opcode_data = &opcode_table[i];
+			break;
+		}
+	}
+
+	if (opcode_data) {
+		if (opcode_data->cmd_func)
+			opcode_color = COLOR_HCI_COMMAND;
+		else
+			opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+		opcode_str = opcode_data->str;
+	} else {
+		if (ogf == 0x3f) {
+			const struct vendor_ocf *vnd = current_vendor_ocf(ocf);
+
+			if (vnd) {
+				const char *str = current_vendor_str();
+
+				if (str) {
+					snprintf(vendor_str, sizeof(vendor_str),
+							"%s %s", str, vnd->str);
+					vendor_data.str = vendor_str;
+				} else
+					vendor_data.str = vnd->str;
+				vendor_data.cmd_func = vnd->cmd_func;
+				vendor_data.cmd_size = vnd->cmd_size;
+				vendor_data.cmd_fixed = vnd->cmd_fixed;
+
+				opcode_data = &vendor_data;
+
+				if (opcode_data->cmd_func)
+					opcode_color = COLOR_HCI_COMMAND;
+				else
+					opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+				opcode_str = opcode_data->str;
+			} else {
+				opcode_color = COLOR_HCI_COMMAND;
+				opcode_str = "Vendor";
+			}
+		} else {
+			opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+			opcode_str = "Unknown";
+		}
+	}
+
+	sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen);
+
+	print_packet(tv, cred, index, '<', opcode_color, "HCI Command",
+							opcode_str, extra_str);
+
+	if (!opcode_data || !opcode_data->cmd_func) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (size != hdr->plen) {
+		print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size,
+								hdr->plen);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (opcode_data->cmd_fixed) {
+		if (hdr->plen != opcode_data->cmd_size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+	} else {
+		if (hdr->plen < opcode_data->cmd_size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	opcode_data->cmd_func(data, hdr->plen);
+}
+
+void packet_hci_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size)
+{
+	const hci_event_hdr *hdr = data;
+	const struct event_data *event_data = NULL;
+	const char *event_color, *event_str;
+	char extra_str[25];
+	int i;
+
+	if (size < HCI_EVENT_HDR_SIZE) {
+		sprintf(extra_str, "(len %d)", size);
+		print_packet(tv, cred, index, '*', COLOR_ERROR,
+			"Malformed HCI Event packet", NULL, extra_str);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	data += HCI_EVENT_HDR_SIZE;
+	size -= HCI_EVENT_HDR_SIZE;
+
+	for (i = 0; event_table[i].str; i++) {
+		if (event_table[i].event == hdr->evt) {
+			event_data = &event_table[i];
+			break;
+		}
+	}
+
+	if (event_data) {
+		if (event_data->func)
+			event_color = COLOR_HCI_EVENT;
+		else
+			event_color = COLOR_HCI_EVENT_UNKNOWN;
+		event_str = event_data->str;
+	} else {
+		event_color = COLOR_HCI_EVENT_UNKNOWN;
+		event_str = "Unknown";
+	}
+
+	sprintf(extra_str, "(0x%2.2x) plen %d", hdr->evt, hdr->plen);
+
+	print_packet(tv, cred, index, '>', event_color, "HCI Event",
+						event_str, extra_str);
+
+	if (!event_data || !event_data->func) {
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (size != hdr->plen) {
+		print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size,
+								hdr->plen);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (event_data->fixed) {
+		if (hdr->plen != event_data->size) {
+			print_text(COLOR_ERROR, "invalid packet size");
+			packet_hexdump(data, size);
+			return;
+		}
+	} else {
+		if (hdr->plen < event_data->size) {
+			print_text(COLOR_ERROR, "too short packet");
+			packet_hexdump(data, size);
+			return;
+		}
+	}
+
+	event_data->func(data, hdr->plen);
+}
+
+void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index,
+				bool in, const void *data, uint16_t size)
+{
+	const struct bt_hci_acl_hdr *hdr = data;
+	uint16_t handle = le16_to_cpu(hdr->handle);
+	uint16_t dlen = le16_to_cpu(hdr->dlen);
+	uint8_t flags = acl_flags(handle);
+	char handle_str[16], extra_str[32];
+
+	if (size < sizeof(*hdr)) {
+		if (in)
+			print_packet(tv, cred, index, '*', COLOR_ERROR,
+				"Malformed ACL Data RX packet", NULL, NULL);
+		else
+			print_packet(tv, cred, index, '*', COLOR_ERROR,
+				"Malformed ACL Data TX packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	data += sizeof(*hdr);
+	size -= sizeof(*hdr);
+
+	sprintf(handle_str, "Handle %d", acl_handle(handle));
+	sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, dlen);
+
+	print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_ACLDATA,
+				in ? "ACL Data RX" : "ACL Data TX",
+						handle_str, extra_str);
+
+	if (size != dlen) {
+		print_text(COLOR_ERROR, "invalid packet size (%d != %d)",
+								size, dlen);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (filter_mask & PACKET_FILTER_SHOW_ACL_DATA)
+		packet_hexdump(data, size);
+
+	l2cap_packet(index, in, acl_handle(handle), flags, data, size);
+}
+
+void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
+				bool in, const void *data, uint16_t size)
+{
+	const hci_sco_hdr *hdr = data;
+	uint16_t handle = le16_to_cpu(hdr->handle);
+	uint8_t flags = acl_flags(handle);
+	char handle_str[16], extra_str[32];
+
+	if (size < HCI_SCO_HDR_SIZE) {
+		if (in)
+			print_packet(tv, cred, index, '*', COLOR_ERROR,
+				"Malformed SCO Data RX packet", NULL, NULL);
+		else
+			print_packet(tv, cred, index, '*', COLOR_ERROR,
+				"Malformed SCO Data TX packet", NULL, NULL);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	data += HCI_SCO_HDR_SIZE;
+	size -= HCI_SCO_HDR_SIZE;
+
+	sprintf(handle_str, "Handle %d", acl_handle(handle));
+	sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen);
+
+	print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_SCODATA,
+				in ? "SCO Data RX" : "SCO Data TX",
+						handle_str, extra_str);
+
+	if (size != hdr->dlen) {
+		print_text(COLOR_ERROR, "invalid packet size (%d != %d)",
+							size, hdr->dlen);
+		packet_hexdump(data, size);
+		return;
+	}
+
+	if (filter_mask & PACKET_FILTER_SHOW_SCO_DATA)
+		packet_hexdump(data, size);
+}
+
+void packet_todo(void)
+{
+	int i;
+
+	printf("HCI commands with missing decodings:\n");
+
+	for (i = 0; opcode_table[i].str; i++) {
+		if (opcode_table[i].bit < 0)
+			continue;
+
+		if (opcode_table[i].cmd_func)
+			continue;
+
+		printf("\t%s\n", opcode_table[i].str);
+	}
+
+	printf("HCI events with missing decodings:\n");
+
+	for (i = 0; event_table[i].str; i++) {
+		if (event_table[i].func)
+			continue;
+
+		printf("\t%s\n", event_table[i].str);
+	}
+
+	for (i = 0; le_meta_event_table[i].str; i++) {
+		if (le_meta_event_table[i].func)
+			continue;
+
+		printf("\t%s\n", le_meta_event_table[i].str);
+	}
+}
diff --git a/bluez/repo/monitor/packet.h b/bluez/repo/monitor/packet.h
new file mode 100644
index 0000000..322f101
--- /dev/null
+++ b/bluez/repo/monitor/packet.h
@@ -0,0 +1,92 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#define PACKET_FILTER_SHOW_INDEX	(1 << 0)
+#define PACKET_FILTER_SHOW_DATE		(1 << 1)
+#define PACKET_FILTER_SHOW_TIME		(1 << 2)
+#define PACKET_FILTER_SHOW_TIME_OFFSET	(1 << 3)
+#define PACKET_FILTER_SHOW_ACL_DATA	(1 << 4)
+#define PACKET_FILTER_SHOW_SCO_DATA	(1 << 5)
+
+void packet_set_filter(unsigned long filter);
+void packet_add_filter(unsigned long filter);
+void packet_del_filter(unsigned long filter);
+
+void packet_set_priority(const char *priority);
+void packet_select_index(uint16_t index);
+
+void packet_hexdump(const unsigned char *buf, uint16_t len);
+void packet_print_error(const char *label, uint8_t error);
+void packet_print_version(const char *label, uint8_t version,
+				const char *sublabel, uint16_t subversion);
+void packet_print_company(const char *label, uint16_t company);
+void packet_print_addr(const char *label, const void *data, bool random);
+void packet_print_ad(const void *data, uint8_t size);
+void packet_print_features_lmp(const uint8_t *features, uint8_t page);
+void packet_print_features_ll(const uint8_t *features);
+void packet_print_channel_map_lmp(const uint8_t *map);
+void packet_print_channel_map_ll(const uint8_t *map);
+void packet_print_io_capability(uint8_t capability);
+void packet_print_io_authentication(uint8_t authentication);
+
+void packet_control(struct timeval *tv, struct ucred *cred,
+					uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size);
+void packet_monitor(struct timeval *tv, struct ucred *cred,
+					uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size);
+void packet_simulator(struct timeval *tv, uint16_t frequency,
+					const void *data, uint16_t size);
+
+void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
+				uint8_t type, uint8_t bus, const char *name);
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_open_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_close_index(struct timeval *tv, uint16_t index, const char *label);
+void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
+							uint16_t manufacturer);
+void packet_vendor_diag(struct timeval *tv, uint16_t index,
+					uint16_t manufacturer,
+					const void *data, uint16_t size);
+void packet_system_note(struct timeval *tv, struct ucred *cred,
+					uint16_t index, const void *message);
+void packet_user_logging(struct timeval *tv, struct ucred *cred,
+					uint16_t index, uint8_t priority,
+					const char *ident, const char *message);
+
+void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size);
+void packet_hci_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+					const void *data, uint16_t size);
+void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index,
+				bool in, const void *data, uint16_t size);
+void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
+				bool in, const void *data, uint16_t size);
+
+void packet_todo(void);
diff --git a/bluez/repo/monitor/rfcomm.c b/bluez/repo/monitor/rfcomm.c
new file mode 100644
index 0000000..b32ad40
--- /dev/null
+++ b/bluez/repo/monitor/rfcomm.c
@@ -0,0 +1,512 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "lib/bluetooth.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"
+
+static char *cr_str[] = {
+	"RSP",
+	"CMD"
+};
+
+/* RFCOMM frame parsing macros */
+#define CR_STR(type)		cr_str[GET_CR(type)]
+#define GET_LEN8(length)	((length & 0xfe) >> 1)
+#define GET_LEN16(length)	((length & 0xfffe) >> 1)
+#define GET_CR(type)		((type & 0x02) >> 1)
+#define GET_PF(ctr)		(((ctr) >> 4) & 0x1)
+
+/* MSC macros */
+#define GET_V24_FC(sigs)	((sigs & 0x02) >> 1)
+#define GET_V24_RTC(sigs)	((sigs & 0x04) >> 2)
+#define GET_V24_RTR(sigs)	((sigs & 0x08) >> 3)
+#define GET_V24_IC(sigs)	((sigs & 0x40) >> 6)
+#define GET_V24_DV(sigs)	((sigs & 0x80) >> 7)
+
+/* RPN macros */
+#define GET_RPN_DB(parity)	(parity & 0x03)
+#define GET_RPN_SB(parity)	((parity & 0x04) >> 2)
+#define GET_RPN_PARITY(parity)	((parity & 0x08) >> 3)
+#define GET_RPN_PTYPE(parity)	((parity & 0x30) >> 4)
+#define GET_RPN_XIN(io)		(io & 0x01)
+#define GET_RPN_XOUT(io)	((io & 0x02) >> 1)
+#define GET_RPN_RTRI(io)	((io & 0x04) >> 2)
+#define GET_RPN_RTRO(io)	((io & 0x08) >> 3)
+#define GET_RPN_RTCI(io)	((io & 0x10) >> 4)
+#define GET_RPN_RTCO(io)	((io & 0x20) >> 5)
+
+/* RLS macro */
+#define GET_ERROR(err)		(err & 0x0f)
+
+/* PN macros */
+#define GET_FRM_TYPE(ctrl)	((ctrl & 0x0f))
+#define GET_CRT_FLOW(ctrl)	((ctrl & 0xf0) >> 4)
+#define GET_PRIORITY(prio)	((prio & 0x3f))
+#define GET_PN_DLCI(dlci)	((dlci & 0x3f))
+
+struct rfcomm_lhdr {
+	uint8_t address;
+	uint8_t control;
+	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;
+	uint8_t bit_rate;
+	uint8_t parity;
+	uint8_t io;
+	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;
+	struct rfcomm_lmcc mcc;
+	struct l2cap_frame l2cap_frame;
+};
+
+static void print_rfcomm_hdr(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	struct rfcomm_lhdr hdr = rfcomm_frame->hdr;
+
+	/* Address field */
+	print_field("%*cAddress: 0x%2.2x cr %d dlci 0x%2.2x", indent, ' ',
+					hdr.address, GET_CR(hdr.address),
+					RFCOMM_GET_DLCI(hdr.address));
+
+	/* Control field */
+	print_field("%*cControl: 0x%2.2x poll/final %d", indent, ' ',
+					hdr.control, GET_PF(hdr.control));
+
+	/* Length and FCS */
+	print_field("%*cLength: %d", indent, ' ', hdr.length);
+	print_field("%*cFCS: 0x%2.2x", indent, ' ', hdr.fcs);
+}
+
+static inline bool mcc_test(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	uint8_t data;
+
+	printf("%*cTest Data: 0x ", indent, ' ');
+
+	while (frame->size > 1) {
+		if (!l2cap_frame_get_u8(frame, &data))
+			return false;
+		printf("%2.2x ", data);
+	}
+
+	printf("\n");
+	return true;
+}
+
+static inline bool mcc_msc(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	struct rfcomm_lmsc msc;
+
+	if (!l2cap_frame_get_u8(frame, &msc.dlci))
+		return false;
+
+	print_field("%*cdlci %d ", indent, ' ', RFCOMM_GET_DLCI(msc.dlci));
+
+	if (!l2cap_frame_get_u8(frame, &msc.v24_sig))
+		return false;
+
+	/* v24 control signals */
+	print_field("%*cfc %d rtc %d rtr %d ic %d dv %d", indent, ' ',
+		GET_V24_FC(msc.v24_sig), GET_V24_RTC(msc.v24_sig),
+		GET_V24_RTR(msc.v24_sig), GET_V24_IC(msc.v24_sig),
+					GET_V24_DV(msc.v24_sig));
+
+	if (frame->size < 2)
+		goto done;
+
+	/*
+	 * TODO: Implement the break signals decoding.
+	 */
+
+	packet_hexdump(frame->data, frame->size);
+
+done:
+	return true;
+}
+
+static inline bool mcc_rpn(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	struct rfcomm_rpn rpn;
+
+	if (!l2cap_frame_get_u8(frame, &rpn.dlci))
+		return false;
+
+	print_field("%*cdlci %d", indent, ' ', RFCOMM_GET_DLCI(rpn.dlci));
+
+	if (frame->size < 7)
+		goto done;
+
+	/* port value octets (optional) */
+
+	if (!l2cap_frame_get_u8(frame, &rpn.bit_rate))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &rpn.parity))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &rpn.io))
+		return false;
+
+	print_field("%*cbr %d db %d sb %d p %d pt %d xi %d xo %d", indent, ' ',
+		rpn.bit_rate, GET_RPN_DB(rpn.parity), GET_RPN_SB(rpn.parity),
+		GET_RPN_PARITY(rpn.parity), GET_RPN_PTYPE(rpn.parity),
+		GET_RPN_XIN(rpn.io), GET_RPN_XOUT(rpn.io));
+
+	if (!l2cap_frame_get_u8(frame, &rpn.xon))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &rpn.xoff))
+		return false;
+
+	print_field("%*crtri %d rtro %d rtci %d rtco %d xon %d xoff %d",
+		indent, ' ', GET_RPN_RTRI(rpn.io), GET_RPN_RTRO(rpn.io),
+		GET_RPN_RTCI(rpn.io), GET_RPN_RTCO(rpn.io), rpn.xon,
+		rpn.xoff);
+
+	if (!l2cap_frame_get_le16(frame, &rpn.pm))
+		return false;
+
+	print_field("%*cpm 0x%04x", indent, ' ', rpn.pm);
+
+done:
+	return true;
+}
+
+static inline bool mcc_rls(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	struct rfcomm_rls rls;
+
+	if (!l2cap_frame_get_u8(frame, &rls.dlci))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &rls.error))
+		return false;
+
+	print_field("%*cdlci %d error: %d", indent, ' ',
+			RFCOMM_GET_DLCI(rls.dlci), GET_ERROR(rls.error));
+
+	return true;
+}
+
+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;
+
+	/* rfcomm_pn struct is defined in rfcomm.h */
+
+	if (!l2cap_frame_get_u8(frame, &pn.dlci))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &pn.flow_ctrl))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &pn.priority))
+		return false;
+
+	print_field("%*cdlci %d frame_type %d credit_flow %d pri %d", indent,
+			' ', GET_PN_DLCI(pn.dlci), GET_FRM_TYPE(pn.flow_ctrl),
+			GET_CRT_FLOW(pn.flow_ctrl), GET_PRIORITY(pn.priority));
+
+	if (!l2cap_frame_get_u8(frame, &pn.ack_timer))
+		return false;
+
+	if (!l2cap_frame_get_le16(frame, &pn.mtu))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &pn.max_retrans))
+		return false;
+
+	if (!l2cap_frame_get_u8(frame, &pn.credits))
+		return false;
+
+	print_field("%*cack_timer %d frame_size %d max_retrans %d credits %d",
+			indent, ' ', pn.ack_timer, pn.mtu, pn.max_retrans,
+			pn.credits);
+
+	return true;
+}
+
+static inline bool mcc_nsc(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	struct rfcomm_nsc nsc;
+
+	if (!l2cap_frame_get_u8(frame, &nsc.cmd_type))
+		return false;
+
+	print_field("%*ccr %d, mcc_cmd_type %x", indent, ' ',
+		GET_CR(nsc.cmd_type), RFCOMM_GET_MCC_TYPE(nsc.cmd_type));
+
+	return true;
+}
+
+struct mcc_data {
+	uint8_t type;
+	const char *str;
+};
+
+static const struct mcc_data mcc_table[] = {
+	{ 0x08, "Test Command" },
+	{ 0x28, "Flow Control On Command" },
+	{ 0x18, "Flow Control Off Command" },
+	{ 0x38, "Modem Status Command" },
+	{ 0x24, "Remote Port Negotiation Command" },
+	{ 0x14, "Remote Line Status" },
+	{ 0x20, "DLC Parameter Negotiation" },
+	{ 0x04, "Non Supported Command" },
+	{ }
+};
+
+static inline bool mcc_frame(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	uint8_t length, ex_length, type;
+	const char *type_str;
+	int i;
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	struct rfcomm_lmcc mcc;
+	const struct mcc_data *mcc_data = NULL;
+
+	if (!l2cap_frame_get_u8(frame, &mcc.type) ||
+			!l2cap_frame_get_u8(frame, &length))
+		return false;
+
+	if (RFCOMM_TEST_EA(length))
+		mcc.length = (uint16_t) GET_LEN8(length);
+	else {
+		if (!l2cap_frame_get_u8(frame, &ex_length))
+			return false;
+		mcc.length = ((uint16_t) length << 8) | ex_length;
+		mcc.length = GET_LEN16(mcc.length);
+	}
+
+	type = RFCOMM_GET_MCC_TYPE(mcc.type);
+
+	for (i = 0; mcc_table[i].str; i++) {
+		if (mcc_table[i].type == type) {
+			mcc_data = &mcc_table[i];
+			break;
+		}
+	}
+
+	if (mcc_data)
+		type_str = mcc_data->str;
+	else
+		type_str = "Unknown";
+
+	print_field("%*cMCC Message type: %s %s (0x%2.2x)", indent, ' ',
+				type_str, CR_STR(mcc.type), type);
+
+	print_field("%*cLength: %d", indent+2, ' ', mcc.length);
+
+	rfcomm_frame->mcc = mcc;
+
+	switch (type) {
+	case RFCOMM_TEST:
+		return mcc_test(rfcomm_frame, indent+10);
+	case RFCOMM_MSC:
+		return mcc_msc(rfcomm_frame, indent+2);
+	case RFCOMM_RPN:
+		return mcc_rpn(rfcomm_frame, indent+2);
+	case RFCOMM_RLS:
+		return mcc_rls(rfcomm_frame, indent+2);
+	case RFCOMM_PN:
+		return mcc_pn(rfcomm_frame, indent+2);
+	case RFCOMM_NSC:
+		return mcc_nsc(rfcomm_frame, indent+2);
+	default:
+		packet_hexdump(frame->data, frame->size);
+	}
+
+	return true;
+}
+
+static bool uih_frame(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
+{
+	uint8_t credits;
+	struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
+	struct rfcomm_lhdr *hdr = &rfcomm_frame->hdr;
+
+	if (!RFCOMM_GET_CHANNEL(hdr->address))
+		return mcc_frame(rfcomm_frame, indent);
+
+	/* fetching credits from UIH frame */
+	if (GET_PF(hdr->control)) {
+		if (!l2cap_frame_get_u8(frame, &credits))
+			return false;
+		hdr->credits = credits;
+		print_field("%*cCredits: %d", indent, ' ', hdr->credits);
+	}
+
+	packet_hexdump(frame->data, frame->size);
+	return true;
+}
+
+struct rfcomm_data {
+	uint8_t frame;
+	const char *str;
+};
+
+static const struct rfcomm_data rfcomm_table[] = {
+	{ 0x2f, "Set Async Balance Mode (SABM)" },
+	{ 0x63, "Unnumbered Ack (UA)" },
+	{ 0x0f, "Disconnect Mode (DM)" },
+	{ 0x43, "Disconnect (DISC)" },
+	{ 0xef, "Unnumbered Info with Header Check (UIH)" },
+	{ }
+};
+
+void rfcomm_packet(const struct l2cap_frame *frame)
+{
+	uint8_t ctype, length, ex_length, indent = 1;
+	const char *frame_str, *frame_color;
+	struct l2cap_frame *l2cap_frame, tmp_frame;
+	struct rfcomm_frame rfcomm_frame;
+	struct rfcomm_lhdr hdr;
+	const struct rfcomm_data *rfcomm_data = NULL;
+	int i;
+
+	l2cap_frame_pull(&rfcomm_frame.l2cap_frame, frame, 0);
+
+	l2cap_frame = &rfcomm_frame.l2cap_frame;
+
+	if (frame->size < 4)
+		goto fail;
+
+	if (!l2cap_frame_get_u8(l2cap_frame, &hdr.address) ||
+			!l2cap_frame_get_u8(l2cap_frame, &hdr.control) ||
+			!l2cap_frame_get_u8(l2cap_frame, &length))
+		goto fail;
+
+	/* length maybe 1 or 2 octets */
+	if (RFCOMM_TEST_EA(length))
+		hdr.length = (uint16_t) GET_LEN8(length);
+	else {
+		if (!l2cap_frame_get_u8(l2cap_frame, &ex_length))
+			goto fail;
+		hdr.length = ((uint16_t)length << 8) | ex_length;
+		hdr.length = GET_LEN16(hdr.length);
+	}
+
+	l2cap_frame_pull(&tmp_frame, l2cap_frame, l2cap_frame->size-1);
+
+	if (!l2cap_frame_get_u8(&tmp_frame, &hdr.fcs))
+		goto fail;
+
+	/* Decoding frame type */
+	ctype = RFCOMM_GET_TYPE(hdr.control);
+
+	for (i = 0; rfcomm_table[i].str; i++) {
+		if (rfcomm_table[i].frame == ctype) {
+			rfcomm_data = &rfcomm_table[i];
+			break;
+		}
+	}
+
+	if (rfcomm_data) {
+		if (frame->in)
+			frame_color = COLOR_MAGENTA;
+		else
+			frame_color = COLOR_BLUE;
+		frame_str = rfcomm_data->str;
+	} else {
+		frame_color = COLOR_WHITE_BG;
+		frame_str = "Unknown";
+	}
+
+	if (!rfcomm_data) {
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	print_indent(6, frame_color, "RFCOMM: ", frame_str, COLOR_OFF,
+						" (0x%2.2x)", ctype);
+
+	rfcomm_frame.hdr = hdr;
+	print_rfcomm_hdr(&rfcomm_frame, indent);
+
+	/* UIH frame */
+	if (ctype == 0xef)
+		if (!uih_frame(&rfcomm_frame, indent))
+			goto fail;
+
+	return;
+
+fail:
+	print_text(COLOR_ERROR, "Frame too short");
+	packet_hexdump(frame->data, frame->size);
+	return;
+}
diff --git a/bluez/repo/monitor/rfcomm.h b/bluez/repo/monitor/rfcomm.h
new file mode 100644
index 0000000..c157352
--- /dev/null
+++ b/bluez/repo/monitor/rfcomm.h
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 RFCOMM_SABM	0x2f
+#define RFCOMM_DISC	0x43
+#define RFCOMM_UA	0x63
+#define RFCOMM_DM	0x0f
+#define RFCOMM_UIH	0xef
+
+#define RFCOMM_GET_TYPE(control)	((control) & 0xef)
+#define RFCOMM_GET_DLCI(address)	((address & 0xfc) >> 2)
+#define RFCOMM_GET_CHANNEL(address)	((address & 0xf8) >> 3)
+#define RFCOMM_GET_DIR(address)		((address & 0x04) >> 2)
+#define RFCOMM_TEST_EA(length)		((length & 0x01))
+
+struct rfcomm_hdr {
+	uint8_t address;
+	uint8_t control;
+	uint8_t length;
+} __attribute__((packed));
+
+struct rfcomm_cmd {
+	uint8_t address;
+	uint8_t control;
+	uint8_t length;
+	uint8_t fcs;
+} __attribute__((packed));
+
+#define RFCOMM_TEST    0x08
+#define RFCOMM_FCON    0x28
+#define RFCOMM_FCOFF   0x18
+#define RFCOMM_MSC     0x38
+#define RFCOMM_RPN     0x24
+#define RFCOMM_RLS     0x14
+#define RFCOMM_PN      0x20
+#define RFCOMM_NSC     0x04
+
+#define RFCOMM_TEST_CR(type)		((type & 0x02))
+#define RFCOMM_GET_MCC_TYPE(type)	((type & 0xfc) >> 2)
+
+struct rfcomm_mcc {
+	uint8_t type;
+	uint8_t length;
+} __attribute__((packed));
+
+struct rfcomm_msc {
+	uint8_t dlci;
+	uint8_t v24_sig;
+} __attribute__((packed));
+
+struct rfcomm_pn {
+	uint8_t  dlci;
+	uint8_t  flow_ctrl;
+	uint8_t  priority;
+	uint8_t  ack_timer;
+	uint16_t mtu;
+	uint8_t  max_retrans;
+	uint8_t  credits;
+} __attribute__((packed));
diff --git a/bluez/repo/monitor/sdp.c b/bluez/repo/monitor/sdp.c
new file mode 100644
index 0000000..417a21c
--- /dev/null
+++ b/bluez/repo/monitor/sdp.c
@@ -0,0 +1,747 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <inttypes.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+
+#include "bt.h"
+#include "packet.h"
+#include "display.h"
+#include "l2cap.h"
+#include "uuid.h"
+#include "sdp.h"
+
+#define MAX_TID 16
+
+struct tid_data {
+	bool inuse;
+	uint16_t tid;
+	uint16_t channel;
+	uint8_t cont[17];
+};
+
+static struct tid_data tid_list[MAX_TID];
+
+static struct tid_data *get_tid(uint16_t tid, uint16_t channel)
+{
+	int i, n = -1;
+
+	for (i = 0; i < MAX_TID; i++) {
+		if (!tid_list[i].inuse) {
+			if (n < 0)
+				n = i;
+			continue;
+		}
+
+		if (tid_list[i].tid == tid && tid_list[i].channel == channel)
+			return &tid_list[i];
+	}
+
+	if (n < 0)
+		return NULL;
+
+	tid_list[n].inuse = true;
+	tid_list[n].tid = tid;
+	tid_list[n].channel = channel;
+
+	return &tid_list[n];
+}
+
+static void clear_tid(struct tid_data *tid)
+{
+	if (tid)
+		tid->inuse = false;
+}
+
+static void print_uint(uint8_t indent, const uint8_t *data, uint32_t size)
+{
+	switch (size) {
+	case 1:
+		print_field("%*c0x%2.2x", indent, ' ', data[0]);
+		break;
+	case 2:
+		print_field("%*c0x%4.4x", indent, ' ', get_be16(data));
+		break;
+	case 4:
+		print_field("%*c0x%8.8x", indent, ' ', get_be32(data));
+		break;
+	case 8:
+		print_field("%*c0x%16.16" PRIx64, indent, ' ', get_be64(data));
+		break;
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+static void print_sint(uint8_t indent, const uint8_t *data, uint32_t size)
+{
+	packet_hexdump(data, size);
+}
+
+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));
+		break;
+	case 4:
+		print_field("%*c%s (0x%8.8x)", indent, ' ',
+			uuid32_to_str(get_be32(data)), get_be32(data));
+		break;
+	case 16:
+		/* BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB */
+		print_field("%*c%8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.4x",
+				indent, ' ',
+				get_be32(data), get_be16(data + 4),
+				get_be16(data + 6), get_be16(data + 8),
+				get_be16(data + 10), get_be32(data + 12));
+		if (get_be16(data + 4) == 0x0000 &&
+				get_be16(data + 6) == 0x1000 &&
+				get_be16(data + 8) == 0x8000 &&
+				get_be16(data + 10) == 0x0080 &&
+				get_be32(data + 12) == 0x5F9B34FB)
+			print_field("%*c%s", indent, ' ',
+				uuid32_to_str(get_be32(data)));
+		break;
+	default:
+		packet_hexdump(data, size);
+		break;
+	}
+}
+
+static void print_string(uint8_t indent, const uint8_t *data, uint32_t size)
+{
+	char *str = alloca(size + 1);
+
+	str[size] = '\0';
+	strncpy(str, (const char *) data, size);
+
+	print_field("%*c%s [len %d]", indent, ' ', str, size);
+}
+
+static void print_boolean(uint8_t indent, const uint8_t *data, uint32_t size)
+{
+	print_field("%*c%s", indent, ' ', data[0] ? "true" : "false");
+}
+
+#define SIZES(args...) ((uint8_t[]) { args, 0xff } )
+
+static struct {
+	uint8_t value;
+	uint8_t *sizes;
+	bool recurse;
+	const char *str;
+	void (*print) (uint8_t indent, const uint8_t *data, uint32_t size);
+} type_table[] = {
+	{ 0, SIZES(0),             false, "Nil"			},
+	{ 1, SIZES(0, 1, 2, 3, 4), false, "Unsigned Integer",	print_uint },
+	{ 2, SIZES(0, 1, 2, 3, 4), false, "Signed Integer",	print_sint },
+	{ 3, SIZES(1, 2, 4),       false, "UUID",		print_uuid },
+	{ 4, SIZES(5, 6, 7),       false, "String",		print_string },
+	{ 5, SIZES(0),             false, "Boolean",		print_boolean },
+	{ 6, SIZES(5, 6, 7),       true,  "Sequence"		},
+	{ 7, SIZES(5, 6, 7),       true,  "Alternative"		},
+	{ 8, SIZES(5, 6, 7),       false, "URL",		print_string },
+	{ }
+};
+
+static struct {
+	uint8_t index;
+	uint8_t bits;
+	uint8_t size;
+	const char *str;
+} size_table[] = {
+	{ 0,  0,  1, "1 byte"	},
+	{ 1,  0,  2, "2 bytes"	},
+	{ 2,  0,  4, "4 bytes"	},
+	{ 3,  0,  8, "8 bytes"	},
+	{ 4,  0, 16, "16 bytes"	},
+	{ 5,  8,  0, "8 bits"	},
+	{ 6, 16,  0, "16 bits"	},
+	{ 7, 32,  0, "32 bits"	},
+	{ }
+};
+
+static bool valid_size(uint8_t size, uint8_t *sizes)
+{
+	int i;
+
+	for (i = 0; sizes[i] != 0xff; i++) {
+		if (sizes[i] == size)
+			return true;
+	}
+
+	return false;
+}
+
+static uint8_t get_bits(const uint8_t *data, uint32_t size)
+{
+	int i;
+
+	for (i = 0; size_table[i].str; i++) {
+		if (size_table[i].index == (data[0] & 0x07))
+			return size_table[i].bits;
+	}
+
+	return 0;
+}
+
+static uint32_t get_size(const uint8_t *data, uint32_t size)
+{
+	int i;
+
+	for (i = 0; size_table[i].str; i++) {
+		if (size_table[i].index == (data[0] & 0x07)) {
+			switch (size_table[i].bits) {
+			case 0:
+				if ((data[0] & 0xf8) == 0)
+					return 0;
+				else
+					return size_table[i].size;
+			case 8:
+				return data[1];
+			case 16:
+				return get_be16(data + 1);
+			case 32:
+				return get_be32(data + 1);
+			default:
+				return 0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void decode_data_elements(uint32_t position, uint8_t indent,
+				const uint8_t *data, uint32_t size,
+				void (*print_func) (uint32_t, uint8_t, uint8_t,
+						const uint8_t *, uint32_t))
+
+{
+	uint32_t datalen, elemlen, extrabits;
+	int i;
+
+	if (!size)
+		return;
+
+	extrabits = get_bits(data, size);
+
+	if (size < 1 + (extrabits / 8)) {
+		print_text(COLOR_ERROR, "data element descriptor too short");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	datalen = get_size(data, size);
+
+	if (size < 1 + (extrabits / 8) + datalen) {
+		print_text(COLOR_ERROR, "data element size too short");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	elemlen = 1 + (extrabits / 8) + datalen;
+
+	for (i = 0; type_table[i].str; i++) {
+		uint8_t type = (data[0] & 0xf8) >> 3;
+
+		if (type_table[i].value != type)
+			continue;
+
+		if (print_func) {
+			print_func(position, indent, type,
+					data + 1 + (extrabits / 8), datalen);
+			break;
+		}
+
+		print_field("%*c%s (%d) with %u byte%s [%u extra bits] len %u",
+					indent, ' ', type_table[i].str, type,
+					datalen, datalen == 1 ? "" : "s",
+					extrabits, elemlen);
+		if (!valid_size(data[0] & 0x07, type_table[i].sizes)) {
+			print_text(COLOR_ERROR, "invalid data element size");
+			packet_hexdump(data + 1 + (extrabits / 8), datalen);
+			break;
+		}
+
+		if (type_table[i].recurse)
+			decode_data_elements(0, indent + 2,
+					data + 1 + (extrabits / 8), datalen,
+								print_func);
+		else if (type_table[i].print)
+			type_table[i].print(indent + 2,
+					data + 1 + (extrabits / 8), datalen);
+		break;
+	}
+
+	data += elemlen;
+	size -= elemlen;
+
+	decode_data_elements(position + 1, indent, data, size, print_func);
+}
+
+static uint32_t get_bytes(const uint8_t *data, uint32_t size)
+{
+	switch (data[0] & 0x07) {
+	case 5:
+		return 2 + data[1];
+	case 6:
+		return 3 + get_be16(data + 1);
+	case 7:
+		return 5 + get_be32(data + 1);
+	}
+
+	return 0;
+}
+
+static struct {
+	uint16_t id;
+	const char *str;
+} attribute_table[] = {
+	{ 0x0000, "Service Record Handle"		},
+	{ 0x0001, "Service Class ID List"		},
+	{ 0x0002, "Service Record State"		},
+	{ 0x0003, "Service ID"				},
+	{ 0x0004, "Protocol Descriptor List"		},
+	{ 0x0005, "Browse Group List"			},
+	{ 0x0006, "Language Base Attribute ID List"	},
+	{ 0x0007, "Service Info Time To Live"		},
+	{ 0x0008, "Service Availability"		},
+	{ 0x0009, "Bluetooth Profile Descriptor List"	},
+	{ 0x000a, "Documentation URL"			},
+	{ 0x000b, "Client Executable URL"		},
+	{ 0x000c, "Icon URL"				},
+	{ 0x000d, "Additional Protocol Descriptor List" },
+	{ }
+};
+
+static void print_attr(uint32_t position, uint8_t indent, uint8_t type,
+					const uint8_t *data, uint32_t size)
+{
+	int i;
+
+	if ((position % 2) == 0) {
+		uint16_t id = get_be16(data);
+		const char *str = "Unknown";
+
+		for (i = 0; attribute_table[i].str; i++) {
+			if (attribute_table[i].id == id)
+				str = attribute_table[i].str;
+		}
+
+		print_field("%*cAttribute: %s (0x%4.4x) [len %d]",
+						indent, ' ', str, id, size);
+		return;
+	}
+
+	for (i = 0; type_table[i].str; i++) {
+		if (type_table[i].value != type)
+			continue;
+
+		if (type_table[i].recurse)
+			decode_data_elements(0, indent + 2, data, size, NULL);
+		else if (type_table[i].print)
+			type_table[i].print(indent + 2, data, size);
+		break;
+	}
+}
+
+static void print_attr_list(uint32_t position, uint8_t indent, uint8_t type,
+					const uint8_t *data, uint32_t size)
+{
+	print_field("%*cAttribute list: [len %d] {position %d}",
+						indent, ' ', size, position);
+
+	decode_data_elements(0, indent + 2, data, size, print_attr);
+}
+
+static void print_attr_lists(uint32_t position, uint8_t indent, uint8_t type,
+					const uint8_t *data, uint32_t size)
+{
+	decode_data_elements(0, indent, data, size, print_attr_list);
+}
+
+static void print_continuation(const uint8_t *data, uint16_t size)
+{
+	if (data[0] != size - 1) {
+		print_text(COLOR_ERROR, "invalid continuation state");
+		packet_hexdump(data, size);
+		return;
+	}
+
+	print_field("Continuation state: %d", data[0]);
+	packet_hexdump(data + 1, size - 1);
+}
+
+static void store_continuation(struct tid_data *tid,
+					const uint8_t *data, uint16_t size)
+{
+	memcpy(tid->cont, data, size);
+	print_continuation(data, size);
+}
+
+#define MAX_CONT 8
+
+struct cont_data {
+	uint16_t channel;
+	uint8_t cont[17];
+	void *data;
+	uint32_t size;
+};
+
+static struct cont_data cont_list[MAX_CONT];
+
+static void handle_continuation(struct tid_data *tid, bool nested,
+			uint16_t bytes, const uint8_t *data, uint16_t size)
+{
+	uint8_t *newdata;
+	int i, n = -1;
+
+	if (bytes + 1 > size) {
+		print_text(COLOR_ERROR, "missing continuation state");
+		return;
+	}
+
+	if (tid->cont[0] == 0x00 && data[bytes] == 0x00) {
+		decode_data_elements(0, 2, data, bytes,
+				nested ? print_attr_lists : print_attr_list);
+
+		print_continuation(data + bytes, size - bytes);
+		return;
+	}
+
+	for (i = 0; i < MAX_CONT; i++) {
+		if (cont_list[i].cont[0] == 0x00) {
+			if (n < 0)
+				n = i;
+			continue;
+		}
+
+		if (cont_list[i].channel != tid->channel)
+			continue;
+
+		if (cont_list[i].cont[0] != tid->cont[0])
+			continue;
+
+		if (!memcmp(cont_list[i].cont + 1,
+					tid->cont + 1, tid->cont[0])) {
+			n = i;
+			break;
+		}
+	}
+
+	print_continuation(data + bytes, size - bytes);
+
+	if (n < 0)
+		return;
+
+	newdata = realloc(cont_list[n].data, cont_list[n].size + bytes);
+	if (!newdata) {
+		print_text(COLOR_ERROR, "failed buffer allocation");
+		free(cont_list[n].data);
+		cont_list[n].data = NULL;
+		cont_list[n].size = 0;
+		return;
+	}
+
+	cont_list[n].channel = tid->channel;
+	cont_list[n].data = newdata;
+
+	if (bytes > 0) {
+		memcpy(cont_list[n].data + cont_list[n].size, data, bytes);
+		cont_list[n].size += bytes;
+	}
+
+	if (data[bytes] == 0x00) {
+		print_field("Combined attribute bytes: %d", cont_list[n].size);
+
+		decode_data_elements(0, 2, cont_list[n].data, cont_list[n].size,
+				nested ? print_attr_lists : print_attr_list);
+
+		free(cont_list[n].data);
+		cont_list[n].data = NULL;
+		cont_list[n].size = 0;
+	} else
+		memcpy(cont_list[i].cont, data + bytes, data[bytes] + 1);
+}
+
+static uint16_t common_rsp(const struct l2cap_frame *frame,
+						struct tid_data *tid)
+{
+	uint16_t bytes;
+
+	if (frame->size < 2) {
+		print_text(COLOR_ERROR, "invalid size");
+		packet_hexdump(frame->data, frame->size);
+		return 0;
+	}
+
+	bytes = get_be16(frame->data);
+	print_field("Attribute bytes: %d", bytes);
+
+	if (bytes > frame->size - 2) {
+		print_text(COLOR_ERROR, "invalid attribute size");
+		packet_hexdump(frame->data + 2, frame->size - 2);
+		return 0;
+	}
+
+	return bytes;
+}
+
+static void error_rsp(const struct l2cap_frame *frame, struct tid_data *tid)
+{
+	uint16_t error;
+
+	clear_tid(tid);
+
+	if (frame->size < 2) {
+		print_text(COLOR_ERROR, "invalid size");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	error = get_be16(frame->data);
+
+	print_field("Error code: 0x%2.2x", error);
+}
+
+static void service_req(const struct l2cap_frame *frame, struct tid_data *tid)
+{
+	uint32_t search_bytes;
+
+	search_bytes = get_bytes(frame->data, frame->size);
+	print_field("Search pattern: [len %d]", search_bytes);
+
+	if (search_bytes + 2 > frame->size) {
+		print_text(COLOR_ERROR, "invalid search list length");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	decode_data_elements(0, 2, frame->data, search_bytes, NULL);
+
+	print_field("Max record count: %d",
+				get_be16(frame->data + search_bytes));
+
+	print_continuation(frame->data + search_bytes + 2,
+					frame->size - search_bytes - 2);
+}
+
+static void service_rsp(const struct l2cap_frame *frame, struct tid_data *tid)
+{
+	uint16_t count;
+	int i;
+
+	clear_tid(tid);
+
+	if (frame->size < 4) {
+		print_text(COLOR_ERROR, "invalid size");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	count = get_be16(frame->data + 2);
+
+	print_field("Total record count: %d", get_be16(frame->data));
+	print_field("Current record count: %d", count);
+
+	for (i = 0; i < count; i++)
+		print_field("Record handle: 0x%4.4x",
+				get_be32(frame->data + 4 + (i * 4)));
+
+	print_continuation(frame->data + 4 + (count * 4),
+					frame->size - 4 - (count * 4));
+}
+
+static void attr_req(const struct l2cap_frame *frame, struct tid_data *tid)
+{
+	uint32_t attr_bytes;
+
+	if (frame->size < 6) {
+		print_text(COLOR_ERROR, "invalid size");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	print_field("Record handle: 0x%4.4x", get_be32(frame->data));
+	print_field("Max attribute bytes: %d", get_be16(frame->data + 4));
+
+	attr_bytes = get_bytes(frame->data + 6, frame->size - 6);
+	print_field("Attribute list: [len %d]", attr_bytes);
+
+	if (attr_bytes + 6 > frame->size) {
+		print_text(COLOR_ERROR, "invalid attribute list length");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	decode_data_elements(0, 2, frame->data + 6, attr_bytes, NULL);
+
+	store_continuation(tid, frame->data + 6 + attr_bytes,
+					frame->size - 6 - attr_bytes);
+}
+
+static void attr_rsp(const struct l2cap_frame *frame, struct tid_data *tid)
+{
+	uint16_t bytes;
+
+	bytes = common_rsp(frame, tid);
+
+	handle_continuation(tid, false, bytes,
+					frame->data + 2, frame->size - 2);
+
+	clear_tid(tid);
+}
+
+static void search_attr_req(const struct l2cap_frame *frame,
+						struct tid_data *tid)
+{
+	uint32_t search_bytes, attr_bytes;
+
+	search_bytes = get_bytes(frame->data, frame->size);
+	print_field("Search pattern: [len %d]", search_bytes);
+
+	if (search_bytes + 2 > frame->size) {
+		print_text(COLOR_ERROR, "invalid search list length");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	decode_data_elements(0, 2, frame->data, search_bytes, NULL);
+
+	print_field("Max record count: %d",
+				get_be16(frame->data + search_bytes));
+
+	attr_bytes = get_bytes(frame->data + search_bytes + 2,
+				frame->size - search_bytes - 2);
+	print_field("Attribute list: [len %d]", attr_bytes);
+
+	decode_data_elements(0, 2, frame->data + search_bytes + 2,
+						attr_bytes, NULL);
+
+	store_continuation(tid, frame->data + search_bytes + 2 + attr_bytes,
+				frame->size - search_bytes - 2 - attr_bytes);
+}
+
+static void search_attr_rsp(const struct l2cap_frame *frame,
+						struct tid_data *tid)
+{
+	uint16_t bytes;
+
+	bytes = common_rsp(frame, tid);
+
+	handle_continuation(tid, true, bytes, frame->data + 2, frame->size - 2);
+
+	clear_tid(tid);
+}
+
+struct sdp_data {
+	uint8_t pdu;
+	const char *str;
+	void (*func) (const struct l2cap_frame *frame, struct tid_data *tid);
+};
+
+static const struct sdp_data sdp_table[] = {
+	{ 0x01, "Error Response",			error_rsp	},
+	{ 0x02, "Service Search Request",		service_req	},
+	{ 0x03, "Service Search Response",		service_rsp	},
+	{ 0x04, "Service Attribute Request",		attr_req	},
+	{ 0x05, "Service Attribute Response",		attr_rsp	},
+	{ 0x06, "Service Search Attribute Request",	search_attr_req	},
+	{ 0x07, "Service Search Attribute Response",	search_attr_rsp	},
+	{ }
+};
+
+void sdp_packet(const struct l2cap_frame *frame)
+{
+	uint8_t pdu;
+	uint16_t tid, plen;
+	struct l2cap_frame sdp_frame;
+	struct tid_data *tid_info;
+	const struct sdp_data *sdp_data = NULL;
+	const char *pdu_color, *pdu_str;
+	int i;
+
+	l2cap_frame_pull(&sdp_frame, frame, 0);
+
+	if (!l2cap_frame_get_u8(&sdp_frame, &pdu) ||
+				!l2cap_frame_get_be16(&sdp_frame, &tid) ||
+				!l2cap_frame_get_be16(&sdp_frame, &plen)) {
+		print_text(COLOR_ERROR, "frame too short");
+		packet_hexdump(frame->data, frame->size);
+		return;
+	}
+
+	if (sdp_frame.size != plen) {
+		print_text(COLOR_ERROR, "invalid frame size");
+		packet_hexdump(sdp_frame.data, sdp_frame.size);
+		return;
+	}
+
+	for (i = 0; sdp_table[i].str; i++) {
+		if (sdp_table[i].pdu == pdu) {
+			sdp_data = &sdp_table[i];
+			break;
+		}
+	}
+
+	if (sdp_data) {
+		if (sdp_data->func) {
+			if (frame->in)
+				pdu_color = COLOR_MAGENTA;
+			else
+				pdu_color = COLOR_BLUE;
+		} else
+			pdu_color = COLOR_WHITE_BG;
+		pdu_str = sdp_data->str;
+	} else {
+		pdu_color = COLOR_WHITE_BG;
+		pdu_str = "Unknown";
+	}
+
+	print_indent(6, pdu_color, "SDP: ", pdu_str, COLOR_OFF,
+				" (0x%2.2x) tid %d len %d", pdu, tid, plen);
+
+	tid_info = get_tid(tid, frame->chan);
+
+	if (!sdp_data || !sdp_data->func || !tid_info) {
+		packet_hexdump(sdp_frame.data, sdp_frame.size);
+		return;
+	}
+
+	sdp_data->func(&sdp_frame, tid_info);
+}
diff --git a/bluez/repo/monitor/sdp.h b/bluez/repo/monitor/sdp.h
new file mode 100644
index 0000000..c8a9bb0
--- /dev/null
+++ b/bluez/repo/monitor/sdp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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
+ *
+ */
+
+void sdp_packet(const struct l2cap_frame *frame);
diff --git a/bluez/repo/monitor/uuid.c b/bluez/repo/monitor/uuid.c
new file mode 100644
index 0000000..b123ebe
--- /dev/null
+++ b/bluez/repo/monitor/uuid.c
@@ -0,0 +1,564 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+
+#include "uuid.h"
+
+static 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"				},
+	/* 0x1824 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"				},
+	/* 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."				},
+	/* SDO defined */
+	{ 0xfffe, "Alliance for Wireless Power (A4WP)"		},
+	{ 0xfffd, "Fast IDentity Online Alliance (FIDO)"	},
+	{ }
+};
+
+const char *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 *uuid32_to_str(uint32_t uuid)
+{
+	if ((uuid & 0xffff0000) == 0x0000)
+		return uuid16_to_str(uuid & 0x0000ffff);
+
+	return "Unknown";
+}
+
+const char *uuid128_to_str(const unsigned char *uuid)
+{
+	return "Unknown";
+}
+
+const char *uuidstr_to_str(const char *uuid)
+{
+	uint32_t val;
+
+	if (!uuid)
+		return NULL;
+
+	if (strlen(uuid) != 36)
+		return NULL;
+
+	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 uuid32_to_str(val);
+}
diff --git a/bluez/repo/monitor/uuid.h b/bluez/repo/monitor/uuid.h
new file mode 100644
index 0000000..f467f51
--- /dev/null
+++ b/bluez/repo/monitor/uuid.h
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+const char *uuid16_to_str(uint16_t uuid);
+const char *uuid32_to_str(uint32_t uuid);
+const char *uuid128_to_str(const unsigned char *uuid);
+
+const char *uuidstr_to_str(const char *uuid);
diff --git a/bluez/repo/monitor/vendor.c b/bluez/repo/monitor/vendor.c
new file mode 100644
index 0000000..8d3b614
--- /dev/null
+++ b/bluez/repo/monitor/vendor.c
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 "packet.h"
+#include "vendor.h"
+
+void vendor_event(uint16_t manufacturer, const void *data, uint8_t size)
+{
+	packet_hexdump(data, size);
+}
diff --git a/bluez/repo/monitor/vendor.h b/bluez/repo/monitor/vendor.h
new file mode 100644
index 0000000..f5792b3
--- /dev/null
+++ b/bluez/repo/monitor/vendor.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+
+struct vendor_ocf {
+	uint16_t ocf;
+	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;
+	bool rsp_fixed;
+};
+
+struct vendor_evt {
+	uint8_t evt;
+	const char *str;
+	void (*evt_func) (const void *data, uint8_t size);
+	uint8_t evt_size;
+	bool evt_fixed;
+};
+
+void vendor_event(uint16_t manufacturer, const void *data, uint8_t size);
diff --git a/bluez/repo/obexd/._client b/bluez/repo/obexd/._client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/._client
Binary files differ
diff --git a/bluez/repo/obexd/._plugins b/bluez/repo/obexd/._plugins
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/._plugins
Binary files differ
diff --git a/bluez/repo/obexd/._src b/bluez/repo/obexd/._src
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/._src
Binary files differ
diff --git a/bluez/repo/obexd/client/._bluetooth.c b/bluez/repo/obexd/client/._bluetooth.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._bluetooth.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._bluetooth.h b/bluez/repo/obexd/client/._bluetooth.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._bluetooth.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._dbus.c b/bluez/repo/obexd/client/._dbus.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._dbus.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._dbus.h b/bluez/repo/obexd/client/._dbus.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._dbus.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._driver.c b/bluez/repo/obexd/client/._driver.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._driver.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._driver.h b/bluez/repo/obexd/client/._driver.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._driver.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._ftp.c b/bluez/repo/obexd/client/._ftp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._ftp.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._ftp.h b/bluez/repo/obexd/client/._ftp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._ftp.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._manager.c b/bluez/repo/obexd/client/._manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._manager.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._manager.h b/bluez/repo/obexd/client/._manager.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._manager.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._map-event.c b/bluez/repo/obexd/client/._map-event.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._map-event.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._map-event.h b/bluez/repo/obexd/client/._map-event.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._map-event.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._map.c b/bluez/repo/obexd/client/._map.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._map.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._map.h b/bluez/repo/obexd/client/._map.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._map.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._mns.c b/bluez/repo/obexd/client/._mns.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._mns.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._opp.c b/bluez/repo/obexd/client/._opp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._opp.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._opp.h b/bluez/repo/obexd/client/._opp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._opp.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._pbap.c b/bluez/repo/obexd/client/._pbap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._pbap.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._pbap.h b/bluez/repo/obexd/client/._pbap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._pbap.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._session.c b/bluez/repo/obexd/client/._session.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._session.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._session.h b/bluez/repo/obexd/client/._session.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._session.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._sync.c b/bluez/repo/obexd/client/._sync.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._sync.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._sync.h b/bluez/repo/obexd/client/._sync.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._sync.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._transfer.c b/bluez/repo/obexd/client/._transfer.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._transfer.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._transfer.h b/bluez/repo/obexd/client/._transfer.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._transfer.h
Binary files differ
diff --git a/bluez/repo/obexd/client/._transport.c b/bluez/repo/obexd/client/._transport.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._transport.c
Binary files differ
diff --git a/bluez/repo/obexd/client/._transport.h b/bluez/repo/obexd/client/._transport.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/client/._transport.h
Binary files differ
diff --git a/bluez/repo/obexd/client/bluetooth.c b/bluez/repo/obexd/client/bluetooth.c
new file mode 100644
index 0000000..be007de
--- /dev/null
+++ b/bluez/repo/obexd/client/bluetooth.c
@@ -0,0 +1,534 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *
+ *  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 <errno.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/rfcomm.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "gdbus/gdbus.h"
+#include "btio/btio.h"
+
+#include "obexd/src/log.h"
+#include "transport.h"
+#include "bluetooth.h"
+
+#define BT_RX_MTU 32767
+#define BT_TX_MTU 32767
+
+#define OBC_BT_ERROR obc_bt_error_quark()
+
+struct bluetooth_session {
+	guint id;
+	bdaddr_t src;
+	bdaddr_t dst;
+	uint16_t port;
+	sdp_session_t *sdp;
+	sdp_record_t *sdp_record;
+	GIOChannel *io;
+	char *service;
+	obc_transport_func func;
+	void *user_data;
+};
+
+static GSList *sessions = NULL;
+
+static GQuark obc_bt_error_quark(void)
+{
+	return g_quark_from_static_string("obc-bluetooth-error-quark");
+}
+
+static void session_destroy(struct bluetooth_session *session)
+{
+	DBG("%p", session);
+
+	if (g_slist_find(sessions, session) == NULL)
+		return;
+
+	sessions = g_slist_remove(sessions, session);
+
+	if (session->io != NULL) {
+		g_io_channel_shutdown(session->io, TRUE, NULL);
+		g_io_channel_unref(session->io);
+	}
+
+	if (session->sdp)
+		sdp_close(session->sdp);
+
+	if (session->sdp_record)
+		sdp_record_free(session->sdp_record);
+
+	g_free(session->service);
+	g_free(session);
+}
+
+static void transport_callback(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct bluetooth_session *session = user_data;
+
+	DBG("");
+
+	if (session->func)
+		session->func(io, err, session->user_data);
+
+	if (err != NULL)
+		session_destroy(session);
+}
+
+static GIOChannel *transport_connect(const bdaddr_t *src, const bdaddr_t *dst,
+					uint16_t port, BtIOConnect function,
+					gpointer user_data)
+{
+	GIOChannel *io;
+	GError *err = NULL;
+
+	DBG("port %u", port);
+
+	if (port > 31) {
+		io = bt_io_connect(function, user_data,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_DEST_BDADDR, dst,
+				BT_IO_OPT_PSM, port,
+				BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
+				BT_IO_OPT_OMTU, BT_TX_MTU,
+				BT_IO_OPT_IMTU, BT_RX_MTU,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	} else {
+		io = bt_io_connect(function, user_data,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_DEST_BDADDR, dst,
+				BT_IO_OPT_CHANNEL, port,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	}
+
+	if (io != NULL)
+		return io;
+
+	error("%s", err->message);
+	g_error_free(err);
+	return NULL;
+}
+
+static void search_callback(uint8_t type, uint16_t status,
+			uint8_t *rsp, size_t size, void *user_data)
+{
+	struct bluetooth_session *session = user_data;
+	unsigned int scanned, bytesleft = size;
+	int seqlen = 0;
+	uint8_t dataType;
+	uint16_t port = 0;
+	GError *gerr = NULL;
+
+	if (status || type != SDP_SVC_SEARCH_ATTR_RSP)
+		goto failed;
+
+	scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
+	if (!scanned || !seqlen)
+		goto failed;
+
+	rsp += scanned;
+	bytesleft -= scanned;
+	do {
+		sdp_record_t *rec;
+		sdp_list_t *protos;
+		sdp_data_t *data;
+		int recsize, ch = -1;
+
+		recsize = 0;
+		rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
+		if (!rec)
+			break;
+
+		if (!recsize) {
+			sdp_record_free(rec);
+			break;
+		}
+
+		if (!sdp_get_access_protos(rec, &protos)) {
+			ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+			sdp_list_foreach(protos,
+					(sdp_list_func_t) sdp_list_free, NULL);
+			sdp_list_free(protos, NULL);
+			protos = NULL;
+		}
+
+		data = sdp_data_get(rec, 0x0200);
+		/* PSM must be odd and lsb of upper byte must be 0 */
+		if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001)
+			ch = data->val.uint16;
+
+		/* Cache the sdp record associated with the service that we
+		 * attempt to connect. This allows reading its application
+		 * specific service attributes. */
+		if (ch > 0) {
+			port = ch;
+			session->sdp_record = rec;
+			break;
+		}
+
+		sdp_record_free(rec);
+
+		scanned += recsize;
+		rsp += recsize;
+		bytesleft -= recsize;
+	} while (scanned < size && bytesleft > 0);
+
+	if (port == 0)
+		goto failed;
+
+	session->port = port;
+
+	g_io_channel_set_close_on_unref(session->io, FALSE);
+	g_io_channel_unref(session->io);
+
+	session->io = transport_connect(&session->src, &session->dst, port,
+						transport_callback, session);
+	if (session->io != NULL) {
+		sdp_close(session->sdp);
+		session->sdp = NULL;
+		return;
+	}
+
+failed:
+	if (session->io != NULL) {
+		g_io_channel_shutdown(session->io, TRUE, NULL);
+		g_io_channel_unref(session->io);
+		session->io = NULL;
+	}
+
+	g_set_error(&gerr, OBC_BT_ERROR, -EIO,
+					"Unable to find service record");
+	if (session->func)
+		session->func(session->io, gerr, session->user_data);
+
+	g_clear_error(&gerr);
+
+	session_destroy(session);
+}
+
+static gboolean process_callback(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct bluetooth_session *session = user_data;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		return FALSE;
+
+	if (sdp_process(session->sdp) < 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static int bt_string2uuid(uuid_t *uuid, const char *string)
+{
+	uint32_t data0, data4;
+	uint16_t data1, data2, data3, data5;
+
+	if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+				&data0, &data1, &data2, &data3, &data4, &data5) == 6) {
+		uint8_t val[16];
+
+		data0 = g_htonl(data0);
+		data1 = g_htons(data1);
+		data2 = g_htons(data2);
+		data3 = g_htons(data3);
+		data4 = g_htonl(data4);
+		data5 = g_htons(data5);
+
+		memcpy(&val[0], &data0, 4);
+		memcpy(&val[4], &data1, 2);
+		memcpy(&val[6], &data2, 2);
+		memcpy(&val[8], &data3, 2);
+		memcpy(&val[10], &data4, 4);
+		memcpy(&val[14], &data5, 2);
+
+		sdp_uuid128_create(uuid, val);
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static gboolean service_callback(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct bluetooth_session *session = user_data;
+	sdp_list_t *search, *attrid;
+	uint32_t range = 0x0000ffff;
+	GError *gerr = NULL;
+	uuid_t uuid;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & G_IO_ERR)
+		goto failed;
+
+	if (sdp_set_notify(session->sdp, search_callback, session) < 0)
+		goto failed;
+
+	if (bt_string2uuid(&uuid, session->service) < 0)
+		goto failed;
+
+	search = sdp_list_append(NULL, &uuid);
+	attrid = sdp_list_append(NULL, &range);
+
+	if (sdp_service_search_attr_async(session->sdp,
+				search, SDP_ATTR_REQ_RANGE, attrid) < 0) {
+		sdp_list_free(attrid, NULL);
+		sdp_list_free(search, NULL);
+		goto failed;
+	}
+
+	sdp_list_free(attrid, NULL);
+	sdp_list_free(search, NULL);
+
+	g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+						process_callback, session);
+
+	return FALSE;
+
+failed:
+	g_io_channel_shutdown(session->io, TRUE, NULL);
+	g_io_channel_unref(session->io);
+	session->io = NULL;
+
+	g_set_error(&gerr, OBC_BT_ERROR, -EIO,
+					"Unable to find service record");
+	if (session->func)
+		session->func(session->io, gerr, session->user_data);
+	g_clear_error(&gerr);
+
+	session_destroy(session);
+	return FALSE;
+}
+
+static sdp_session_t *service_connect(const bdaddr_t *src, const bdaddr_t *dst,
+					GIOFunc function, gpointer user_data)
+{
+	struct bluetooth_session *session = user_data;
+	sdp_session_t *sdp;
+	GIOChannel *io;
+
+	DBG("");
+
+	sdp = sdp_connect(src, dst, SDP_NON_BLOCKING);
+	if (sdp == NULL)
+		return NULL;
+
+	io = g_io_channel_unix_new(sdp_get_socket(sdp));
+	if (io == NULL) {
+		sdp_close(sdp);
+		return NULL;
+	}
+
+	g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							function, user_data);
+
+	session->io = io;
+
+	return sdp;
+}
+
+static int session_connect(struct bluetooth_session *session)
+{
+	int err;
+
+	DBG("session %p", session);
+
+	if (session->port > 0) {
+		session->io = transport_connect(&session->src, &session->dst,
+							session->port,
+							transport_callback,
+							session);
+		err = (session->io == NULL) ? -EINVAL : 0;
+	} else {
+		session->sdp = service_connect(&session->src, &session->dst,
+						service_callback, session);
+		err = (session->sdp == NULL) ? -ENOMEM : 0;
+	}
+
+	return err;
+}
+
+static guint bluetooth_connect(const char *source, const char *destination,
+				const char *service, uint16_t port,
+				obc_transport_func func, void *user_data)
+{
+	struct bluetooth_session *session;
+	static guint id = 0;
+
+	DBG("src %s dest %s service %s port %u",
+				source, destination, service, port);
+
+	if (destination == NULL)
+		return 0;
+
+	session = g_try_malloc0(sizeof(*session));
+	if (session == NULL)
+		return 0;
+
+	session->id = ++id;
+	session->func = func;
+	session->port = port;
+	session->user_data = user_data;
+
+	str2ba(destination, &session->dst);
+	str2ba(source, &session->src);
+
+	if (session_connect(session) < 0) {
+		g_free(session);
+		return 0;
+	}
+
+	session->service = g_strdup(service);
+	sessions = g_slist_prepend(sessions, session);
+
+	return session->id;
+}
+
+static void bluetooth_disconnect(guint id)
+{
+	GSList *l;
+
+	DBG("");
+
+	for (l = sessions; l; l = l->next) {
+		struct bluetooth_session *session = l->data;
+
+		if (session->id == id) {
+			session_destroy(session);
+			return;
+		}
+	}
+}
+
+static int bluetooth_getpacketopt(GIOChannel *io, int *tx_mtu, int *rx_mtu)
+{
+	int sk = g_io_channel_unix_get_fd(io);
+	int type;
+	int omtu = -1;
+	int imtu = -1;
+	socklen_t len = sizeof(int);
+
+	DBG("");
+
+	if (getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0)
+		return -errno;
+
+	if (type != SOCK_SEQPACKET)
+		return -EINVAL;
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu,
+						BT_IO_OPT_IMTU, &imtu,
+						BT_IO_OPT_INVALID))
+		return -EINVAL;
+
+	if (tx_mtu)
+		*tx_mtu = omtu;
+
+	if (rx_mtu)
+		*rx_mtu = imtu;
+
+	return 0;
+}
+
+static const void *bluetooth_getattribute(guint id, int attribute_id)
+{
+	GSList *l;
+	sdp_data_t *data;
+
+	for (l = sessions; l; l = l->next) {
+		struct bluetooth_session *session = l->data;
+
+		if (session->id != id)
+			continue;
+
+		if (session->sdp_record == NULL)
+			break;
+
+		/* Read version since UUID is already known */
+		if (attribute_id == SDP_ATTR_PFILE_DESC_LIST) {
+			sdp_list_t *descs;
+			void *ret = NULL;
+
+			if (sdp_get_profile_descs(session->sdp_record,
+								&descs) < 0)
+				return NULL;
+
+			if (descs && descs->data) {
+				sdp_profile_desc_t *desc = descs->data;
+				ret = GINT_TO_POINTER(desc->version);
+			}
+
+			sdp_list_free(descs, free);
+
+			return ret;
+		}
+
+		data = sdp_data_get(session->sdp_record, attribute_id);
+		if (!data)
+			break;
+
+		return &data->val;
+	}
+	return NULL;
+}
+
+static struct obc_transport bluetooth = {
+	.name = "Bluetooth",
+	.connect = bluetooth_connect,
+	.getpacketopt = bluetooth_getpacketopt,
+	.disconnect = bluetooth_disconnect,
+	.getattribute = bluetooth_getattribute,
+};
+
+int bluetooth_init(void)
+{
+	DBG("");
+
+	return obc_transport_register(&bluetooth);
+}
+
+void bluetooth_exit(void)
+{
+	DBG("");
+
+	obc_transport_unregister(&bluetooth);
+}
diff --git a/bluez/repo/obexd/client/bluetooth.h b/bluez/repo/obexd/client/bluetooth.h
new file mode 100644
index 0000000..968e131
--- /dev/null
+++ b/bluez/repo/obexd/client/bluetooth.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2011 Intel Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+int bluetooth_init(void);
+void bluetooth_exit(void);
diff --git a/bluez/repo/obexd/client/dbus.c b/bluez/repo/obexd/client/dbus.c
new file mode 100644
index 0000000..bfe5c49
--- /dev/null
+++ b/bluez/repo/obexd/client/dbus.c
@@ -0,0 +1,94 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "dbus.h"
+
+static void append_variant(DBusMessageIter *iter,
+				int type, void *value)
+{
+	char sig[2];
+	DBusMessageIter valueiter;
+
+	sig[0] = type;
+	sig[1] = 0;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+						sig, &valueiter);
+
+	dbus_message_iter_append_basic(&valueiter, type, value);
+
+	dbus_message_iter_close_container(iter, &valueiter);
+}
+
+void obex_dbus_dict_append(DBusMessageIter *dict,
+			const char *key, int type, void *value)
+{
+	DBusMessageIter keyiter;
+
+	if (type == DBUS_TYPE_STRING) {
+		const char *str = *((const char **) value);
+		if (str == NULL)
+			return;
+	}
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &keyiter);
+
+	dbus_message_iter_append_basic(&keyiter, DBUS_TYPE_STRING, &key);
+
+	append_variant(&keyiter, type, value);
+
+	dbus_message_iter_close_container(dict, &keyiter);
+}
+
+int obex_dbus_signal_property_changed(DBusConnection *conn,
+					const char *path,
+					const char *interface,
+					const char *name,
+					int type, void *value)
+{
+	DBusMessage *signal;
+	DBusMessageIter iter;
+
+	signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+	if (signal == NULL) {
+		error("Unable to allocate new %s.PropertyChanged signal",
+				interface);
+		return -1;
+	}
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+	append_variant(&iter, type, value);
+
+	return g_dbus_send_message(conn, signal);
+}
diff --git a/bluez/repo/obexd/client/dbus.h b/bluez/repo/obexd/client/dbus.h
new file mode 100644
index 0000000..6136bf5
--- /dev/null
+++ b/bluez/repo/obexd/client/dbus.h
@@ -0,0 +1,48 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 __OBEX_DBUS_H
+#define __OBEX_DBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+
+/* Essentially a{sv} */
+#define OBC_PROPERTIES_ARRAY_SIGNATURE DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
+					DBUS_TYPE_STRING_AS_STRING \
+					DBUS_TYPE_VARIANT_AS_STRING \
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+
+void obex_dbus_dict_append(DBusMessageIter *dict, const char *key, int type,
+				void *value);
+
+int obex_dbus_signal_property_changed(DBusConnection *conn, const char *path,
+					const char *interface, const char *name,
+					int type, void *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OBEX_DBUS_H */
diff --git a/bluez/repo/obexd/client/driver.c b/bluez/repo/obexd/client/driver.c
new file mode 100644
index 0000000..27857b5
--- /dev/null
+++ b/bluez/repo/obexd/client/driver.c
@@ -0,0 +1,88 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <string.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+
+static GSList *drivers = NULL;
+
+struct obc_driver *obc_driver_find(const char *pattern)
+{
+	GSList *l;
+
+	for (l = drivers; l; l = l->next) {
+		struct obc_driver *driver = l->data;
+
+		if (strcasecmp(pattern, driver->service) == 0)
+			return driver;
+
+		if (strcasecmp(pattern, driver->uuid) == 0)
+			return driver;
+	}
+
+	return NULL;
+}
+
+int obc_driver_register(struct obc_driver *driver)
+{
+	if (!driver) {
+		error("Invalid driver");
+		return -EINVAL;
+	}
+
+	if (obc_driver_find(driver->service)) {
+		error("Permission denied: service %s already registered",
+			driver->service);
+		return -EPERM;
+	}
+
+	DBG("driver %p service %s registered", driver, driver->service);
+
+	drivers = g_slist_append(drivers, driver);
+
+	return 0;
+}
+
+void obc_driver_unregister(struct obc_driver *driver)
+{
+	if (!g_slist_find(drivers, driver)) {
+		error("Unable to unregister: No such driver %p", driver);
+		return;
+	}
+
+	DBG("driver %p service %s unregistered", driver, driver->service);
+
+	drivers = g_slist_remove(drivers, driver);
+}
diff --git a/bluez/repo/obexd/client/driver.h b/bluez/repo/obexd/client/driver.h
new file mode 100644
index 0000000..0112219
--- /dev/null
+++ b/bluez/repo/obexd/client/driver.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+struct obc_driver {
+	const char *service;
+	const char *uuid;
+	void *target;
+	gsize target_len;
+	void *(*supported_features) (struct obc_session *session);
+	int (*probe) (struct obc_session *session);
+	void (*remove) (struct obc_session *session);
+};
+
+int obc_driver_register(struct obc_driver *driver);
+void obc_driver_unregister(struct obc_driver *driver);
+struct obc_driver *obc_driver_find(const char *pattern);
diff --git a/bluez/repo/obexd/client/ftp.c b/bluez/repo/obexd/client/ftp.c
new file mode 100644
index 0000000..3628657
--- /dev/null
+++ b/bluez/repo/obexd/client/ftp.c
@@ -0,0 +1,510 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-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 <errno.h>
+#include <string.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "dbus.h"
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "ftp.h"
+
+#define OBEX_FTP_UUID \
+	"\xF9\xEC\x7B\xC4\x95\x3C\x11\xD2\x98\x4E\x52\x54\x00\xDC\x9E\x09"
+#define OBEX_FTP_UUID_LEN 16
+
+#define FTP_INTERFACE "org.bluez.obex.FileTransfer1"
+#define ERROR_INTERFACE "org.bluez.obex.Error"
+#define FTP_UUID "00001106-0000-1000-8000-00805f9b34fb"
+#define PCSUITE_UUID "00005005-0000-1000-8000-0002ee000001"
+
+static DBusConnection *conn = NULL;
+
+struct ftp_data {
+	struct obc_session *session;
+};
+
+static void async_cb(struct obc_session *session, struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	DBusMessage *reply, *msg = user_data;
+
+	if (err != NULL)
+		reply = g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+	else
+		reply = dbus_message_new_method_return(msg);
+
+	g_dbus_send_message(conn, reply);
+	dbus_message_unref(msg);
+}
+
+static DBusMessage *change_folder(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	const char *folder;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &folder,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	obc_session_setpath(session, folder, async_cb, message, &err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply =  g_dbus_create_error(message,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		g_error_free(err);
+		return reply;
+	}
+
+	dbus_message_ref(message);
+
+	return NULL;
+}
+
+static void xml_element(GMarkupParseContext *ctxt,
+			const char *element,
+			const char **names,
+			const char **values,
+			gpointer user_data,
+			GError **gerr)
+{
+	DBusMessageIter dict, *iter = user_data;
+	char *key;
+	int i;
+
+	if (strcasecmp("folder", element) != 0 && strcasecmp("file", element) != 0)
+		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);
+
+	obex_dbus_dict_append(&dict, "Type", DBUS_TYPE_STRING, &element);
+
+	/* FIXME: User, Group, Other permission must be reviewed */
+
+	i = 0;
+	for (key = (char *) names[i]; key; key = (char *) names[++i]) {
+		key[0] = g_ascii_toupper(key[0]);
+		if (g_str_equal("Size", key) == TRUE) {
+			guint64 size;
+			size = g_ascii_strtoll(values[i], NULL, 10);
+			obex_dbus_dict_append(&dict, key, DBUS_TYPE_UINT64,
+								&size);
+		} else
+			obex_dbus_dict_append(&dict, key, DBUS_TYPE_STRING,
+								&values[i]);
+	}
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static const GMarkupParser parser = {
+	xml_element,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void list_folder_callback(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	DBusMessage *msg = user_data;
+	GMarkupParseContext *ctxt;
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+	char *contents;
+	size_t size;
+
+	reply = dbus_message_new_method_return(msg);
+
+	if (obc_transfer_get_contents(transfer, &contents, &size) < 0)
+		goto done;
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_TYPE_ARRAY_AS_STRING
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+	ctxt = g_markup_parse_context_new(&parser, 0, &array, NULL);
+	g_markup_parse_context_parse(ctxt, contents, size, NULL);
+	g_markup_parse_context_free(ctxt);
+	dbus_message_iter_close_container(&iter, &array);
+	g_free(contents);
+
+done:
+	g_dbus_send_message(conn, reply);
+	dbus_message_unref(msg);
+}
+
+static DBusMessage *create_folder(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	const char *folder;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &folder,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	obc_session_mkdir(session, folder, async_cb, message, &err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply = g_dbus_create_error(message,
+				ERROR_INTERFACE ".Failed",
+				"%s", err->message);
+		g_error_free(err);
+		return reply;
+	}
+
+	dbus_message_ref(message);
+
+	return NULL;
+}
+
+static DBusMessage *list_folder(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err);
+	if (transfer == NULL)
+		goto fail;
+
+	if (obc_session_queue(session, transfer, list_folder_callback,
+							message, &err)) {
+		dbus_message_ref(message);
+		return NULL;
+	}
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *get_file(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	struct obc_transfer *transfer;
+	const char *target_file, *source_file;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &target_file,
+				DBUS_TYPE_STRING, &source_file,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	transfer = obc_transfer_get(NULL, source_file, target_file, &err);
+	if (transfer == NULL)
+		goto fail;
+
+	if (!obc_session_queue(session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *put_file(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	struct obc_transfer *transfer;
+	char *sourcefile, *targetfile;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	if (dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &sourcefile,
+					DBUS_TYPE_STRING, &targetfile,
+					DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments",
+				"Invalid arguments in method call");
+
+	transfer = obc_transfer_put(NULL, targetfile, sourcefile, NULL, 0,
+									&err);
+	if (transfer == NULL)
+		goto fail;
+
+	if (!obc_session_queue(session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *copy_file(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	const char *filename, *destname;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &filename,
+				DBUS_TYPE_STRING, &destname,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	obc_session_copy(session, filename, destname, async_cb, message, &err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
+							"%s", err->message);
+		g_error_free(err);
+		return reply;
+	}
+
+	dbus_message_ref(message);
+
+	return NULL;
+}
+
+static DBusMessage *move_file(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	const char *filename, *destname;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &filename,
+				DBUS_TYPE_STRING, &destname,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	obc_session_move(session, filename, destname, async_cb, message, &err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
+							"%s", err->message);
+		g_error_free(err);
+		return reply;
+	}
+
+	dbus_message_ref(message);
+
+	return NULL;
+}
+
+static DBusMessage *delete(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct ftp_data *ftp = user_data;
+	struct obc_session *session = ftp->session;
+	const char *file;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &file,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	obc_session_delete(session, file, async_cb, message, &err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
+							"%s", err->message);
+		g_error_free(err);
+		return reply;
+	}
+
+	dbus_message_ref(message);
+
+	return NULL;
+}
+
+static const GDBusMethodTable ftp_methods[] = {
+	{ GDBUS_ASYNC_METHOD("ChangeFolder",
+		GDBUS_ARGS({ "folder", "s" }), NULL, change_folder) },
+	{ GDBUS_ASYNC_METHOD("CreateFolder",
+		GDBUS_ARGS({ "folder", "s" }), NULL, create_folder) },
+	{ GDBUS_ASYNC_METHOD("ListFolder",
+		NULL, GDBUS_ARGS({ "folderinfo", "aa{sv}" }), list_folder) },
+	{ GDBUS_METHOD("GetFile",
+		GDBUS_ARGS({ "targetfile", "s" }, { "sourcefile", "s" }),
+		GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
+		get_file) },
+	{ GDBUS_METHOD("PutFile",
+		GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }),
+		GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
+		put_file) },
+	{ GDBUS_ASYNC_METHOD("CopyFile",
+		GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), NULL,
+		copy_file) },
+	{ GDBUS_ASYNC_METHOD("MoveFile",
+		GDBUS_ARGS({ "sourcefile", "s" }, { "targetfile", "s" }), NULL,
+		move_file) },
+	{ GDBUS_ASYNC_METHOD("Delete",
+		GDBUS_ARGS({ "file", "s" }), NULL, delete) },
+	{ }
+};
+
+static void ftp_free(void *data)
+{
+	struct ftp_data *ftp = data;
+
+	obc_session_unref(ftp->session);
+	g_free(ftp);
+}
+
+static int ftp_probe(struct obc_session *session)
+{
+	struct ftp_data *ftp;
+	const char *path;
+
+	path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	ftp = g_try_new0(struct ftp_data, 1);
+	if (!ftp)
+		return -ENOMEM;
+
+	ftp->session = obc_session_ref(session);
+
+	if (!g_dbus_register_interface(conn, path, FTP_INTERFACE, ftp_methods,
+						NULL, NULL, ftp, ftp_free)) {
+		ftp_free(ftp);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void ftp_remove(struct obc_session *session)
+{
+	const char *path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	g_dbus_unregister_interface(conn, path, FTP_INTERFACE);
+}
+
+static struct obc_driver ftp = {
+	.service = "FTP",
+	.uuid = FTP_UUID,
+	.target = OBEX_FTP_UUID,
+	.target_len = OBEX_FTP_UUID_LEN,
+	.probe = ftp_probe,
+	.remove = ftp_remove
+};
+
+static struct obc_driver pcsuite = {
+	.service = "PCSUITE",
+	.uuid = PCSUITE_UUID,
+	.target = OBEX_FTP_UUID,
+	.target_len = OBEX_FTP_UUID_LEN,
+	.probe = ftp_probe,
+	.remove = ftp_remove
+};
+
+int ftp_init(void)
+{
+	int err;
+
+	DBG("");
+
+	conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+	if (!conn)
+		return -EIO;
+
+	err = obc_driver_register(&ftp);
+	if (err < 0)
+		goto failed;
+
+	err = obc_driver_register(&pcsuite);
+	if (err < 0) {
+		obc_driver_unregister(&ftp);
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	dbus_connection_unref(conn);
+	conn = NULL;
+	return err;
+}
+
+void ftp_exit(void)
+{
+	DBG("");
+
+	dbus_connection_unref(conn);
+	conn = NULL;
+
+	obc_driver_unregister(&ftp);
+	obc_driver_unregister(&pcsuite);
+}
diff --git a/bluez/repo/obexd/client/ftp.h b/bluez/repo/obexd/client/ftp.h
new file mode 100644
index 0000000..3d90968
--- /dev/null
+++ b/bluez/repo/obexd/client/ftp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+int ftp_init(void);
+void ftp_exit(void);
diff --git a/bluez/repo/obexd/client/manager.c b/bluez/repo/obexd/client/manager.c
new file mode 100644
index 0000000..fbcad6d
--- /dev/null
+++ b/bluez/repo/obexd/client/manager.c
@@ -0,0 +1,316 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-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 <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
+#include "transfer.h"
+#include "session.h"
+#include "bluetooth.h"
+#include "opp.h"
+#include "ftp.h"
+#include "pbap.h"
+#include "sync.h"
+#include "map.h"
+#include "manager.h"
+
+#define CLIENT_INTERFACE	"org.bluez.obex.Client1"
+#define ERROR_INTERFACE		"org.bluez.obex.Error"
+#define CLIENT_PATH		"/org/bluez/obex"
+
+struct send_data {
+	DBusConnection *connection;
+	DBusMessage *message;
+};
+
+static GSList *sessions = NULL;
+
+static void shutdown_session(struct obc_session *session)
+{
+	obc_session_shutdown(session);
+	obc_session_unref(session);
+}
+
+static void release_session(struct obc_session *session)
+{
+	sessions = g_slist_remove(sessions, session);
+	shutdown_session(session);
+}
+
+static void unregister_session(void *data)
+{
+	struct obc_session *session = data;
+
+	if (g_slist_find(sessions, session) == NULL)
+		return;
+
+	sessions = g_slist_remove(sessions, session);
+	obc_session_unref(session);
+}
+
+static void create_callback(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct send_data *data = user_data;
+	const char *path;
+
+	if (err != NULL) {
+		DBusMessage *error = g_dbus_create_error(data->message,
+					ERROR_INTERFACE ".Failed",
+					"%s", err->message);
+		g_dbus_send_message(data->connection, error);
+		shutdown_session(session);
+		goto done;
+	}
+
+
+	path = obc_session_register(session, unregister_session);
+	if (path == NULL) {
+		DBusMessage *error = g_dbus_create_error(data->message,
+					ERROR_INTERFACE ".Failed",
+					NULL);
+		g_dbus_send_message(data->connection, error);
+		shutdown_session(session);
+		goto done;
+	}
+
+	sessions = g_slist_append(sessions, session);
+	g_dbus_send_reply(data->connection, data->message,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID);
+
+done:
+	dbus_message_unref(data->message);
+	dbus_connection_unref(data->connection);
+	g_free(data);
+}
+
+static int parse_device_dict(DBusMessageIter *iter,
+		const char **source, const char **target, uint8_t *channel)
+{
+	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, value;
+		const char *key;
+
+		dbus_message_iter_recurse(iter, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		switch (dbus_message_iter_get_arg_type(&value)) {
+		case DBUS_TYPE_STRING:
+			if (g_str_equal(key, "Source") == TRUE)
+				dbus_message_iter_get_basic(&value, source);
+			else if (g_str_equal(key, "Target") == TRUE)
+				dbus_message_iter_get_basic(&value, target);
+			break;
+		case DBUS_TYPE_BYTE:
+			if (g_str_equal(key, "Channel") == TRUE)
+				dbus_message_iter_get_basic(&value, channel);
+			break;
+		}
+
+		dbus_message_iter_next(iter);
+	}
+
+	return 0;
+}
+
+static struct obc_session *find_session(const char *path)
+{
+	GSList *l;
+
+	for (l = sessions; l; l = l->next) {
+		struct obc_session *session = l->data;
+
+		if (g_strcmp0(obc_session_get_path(session), path) == 0)
+			return session;
+	}
+
+	return NULL;
+}
+
+static DBusMessage *create_session(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	DBusMessageIter iter, dict;
+	struct obc_session *session;
+	struct send_data *data;
+	const char *source = NULL, *dest = NULL, *target = NULL;
+	uint8_t channel = 0;
+
+	dbus_message_iter_init(message, &iter);
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&iter, &dest);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	parse_device_dict(&dict, &source, &target, &channel);
+	if (dest == NULL || target == NULL)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	data = g_try_malloc0(sizeof(*data));
+	if (data == NULL)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".Error.NoMemory", NULL);
+
+	data->connection = dbus_connection_ref(connection);
+	data->message = dbus_message_ref(message);
+
+	session = obc_session_create(source, dest, target, channel,
+					dbus_message_get_sender(message),
+					create_callback, data);
+	if (session != NULL) {
+		return NULL;
+	}
+
+	dbus_message_unref(data->message);
+	dbus_connection_unref(data->connection);
+	g_free(data);
+
+	return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", NULL);
+}
+
+static DBusMessage *remove_session(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct obc_session *session;
+	const char *sender, *path;
+
+	if (dbus_message_get_args(message, NULL,
+			DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	session = find_session(path);
+	if (session == NULL)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	sender = dbus_message_get_sender(message);
+	if (g_str_equal(sender, obc_session_get_owner(session)) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotAuthorized",
+				"Not Authorized");
+
+	release_session(session);
+
+	return dbus_message_new_method_return(message);
+}
+
+static const GDBusMethodTable client_methods[] = {
+	{ GDBUS_ASYNC_METHOD("CreateSession",
+			GDBUS_ARGS({ "destination", "s" }, { "args", "a{sv}" }),
+			GDBUS_ARGS({ "session", "o" }), create_session) },
+	{ GDBUS_ASYNC_METHOD("RemoveSession",
+			GDBUS_ARGS({ "session", "o" }), NULL, remove_session) },
+	{ }
+};
+
+static DBusConnection *conn = NULL;
+
+static struct obc_module {
+	const char *name;
+	int (*init) (void);
+	void (*exit) (void);
+} modules[] = {
+	{ "bluetooth", bluetooth_init, bluetooth_exit },
+	{ "opp", opp_init, opp_exit },
+	{ "ftp", ftp_init, ftp_exit },
+	{ "pbap", pbap_init, pbap_exit },
+	{ "sync", sync_init, sync_exit },
+	{ "map", map_init, map_exit },
+	{ }
+};
+
+int client_manager_init(void)
+{
+	DBusError derr;
+	struct obc_module *module;
+
+	dbus_error_init(&derr);
+
+	conn = manager_dbus_get_connection();
+	if (conn == NULL) {
+		error("Can't get client D-Bus connection");
+		return -1;
+	}
+
+	if (g_dbus_register_interface(conn, CLIENT_PATH, CLIENT_INTERFACE,
+						client_methods, NULL, NULL,
+							NULL, NULL) == FALSE) {
+		error("Can't register client interface");
+		dbus_connection_unref(conn);
+		conn = NULL;
+		return -1;
+	}
+
+	for (module = modules; module && module->init; module++) {
+		if (module->init() < 0)
+			continue;
+
+		DBG("Module %s loaded", module->name);
+	}
+
+	return 0;
+}
+
+void client_manager_exit(void)
+{
+	struct obc_module *module;
+
+	if (conn == NULL)
+		return;
+
+	for (module = modules; module && module->exit; module++)
+		module->exit();
+
+	g_dbus_unregister_interface(conn, CLIENT_PATH, CLIENT_INTERFACE);
+
+	dbus_connection_unref(conn);
+}
diff --git a/bluez/repo/obexd/client/manager.h b/bluez/repo/obexd/client/manager.h
new file mode 100644
index 0000000..e4068de
--- /dev/null
+++ b/bluez/repo/obexd/client/manager.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+int client_manager_init(void);
+void client_manager_exit(void);
diff --git a/bluez/repo/obexd/client/map-event.c b/bluez/repo/obexd/client/map-event.c
new file mode 100644
index 0000000..e164e86
--- /dev/null
+++ b/bluez/repo/obexd/client/map-event.c
@@ -0,0 +1,113 @@
+/*
+ *
+ *  OBEX
+ *
+ *  Copyright (C) 2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "map-event.h"
+
+#include "transfer.h"
+#include "session.h"
+
+static GSList *handlers;
+
+struct mns_handler {
+	int mas_id;
+	struct obc_session *session;
+	map_event_cb cb;
+	void *user_data;
+};
+
+static struct mns_handler *find_handler(int mas_id, const char *device)
+{
+	GSList *list;
+
+	for (list = handlers; list; list = list->next) {
+		struct mns_handler *handler = list->data;
+
+		if (mas_id != handler->mas_id)
+			continue;
+
+		if (g_str_equal(device,
+				obc_session_get_destination(handler->session)))
+			return handler;
+	}
+
+	return NULL;
+}
+
+bool map_register_event_handler(struct obc_session *session,
+					int mas_id, map_event_cb cb,
+					void *user_data)
+{
+	struct mns_handler *handler;
+
+	handler = find_handler(mas_id, obc_session_get_destination(session));
+	if (handler != NULL)
+		return FALSE;
+
+	handler = g_new0(struct mns_handler, 1);
+	handler->mas_id = mas_id;
+	handler->session = session;
+	handler->cb = cb;
+	handler->user_data = user_data;
+
+	handlers = g_slist_prepend(handlers, handler);
+	DBG("Handler %p for %s:%d registered", handler,
+			obc_session_get_destination(session), mas_id);
+
+	return TRUE;
+}
+
+void map_unregister_event_handler(struct obc_session *session, int mas_id)
+{
+	struct mns_handler *handler;
+
+	handler = find_handler(mas_id, obc_session_get_destination(session));
+	if (handler == NULL)
+		return;
+
+	handlers = g_slist_remove(handlers, handler);
+	DBG("Handler %p for %s:%d unregistered", handler,
+			obc_session_get_destination(session), mas_id);
+	g_free(handler);
+}
+
+void map_dispatch_event(int mas_id, const char *device,
+						struct map_event *event)
+{
+	struct mns_handler *handler;
+
+	handler = find_handler(mas_id, device);
+	if (handler)
+		handler->cb(event, handler->user_data);
+}
diff --git a/bluez/repo/obexd/client/map-event.h b/bluez/repo/obexd/client/map-event.h
new file mode 100644
index 0000000..5414b26
--- /dev/null
+++ b/bluez/repo/obexd/client/map-event.h
@@ -0,0 +1,71 @@
+/*
+ *
+ *  OBEX
+ *
+ *  Copyright (C) 2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct obc_session;
+
+enum map_event_type {
+	MAP_ET_NEW_MESSAGE,
+	MAP_ET_DELIVERY_SUCCESS,
+	MAP_ET_SENDING_SUCCESS,
+	MAP_ET_DELIVERY_FAILURE,
+	MAP_ET_SENDING_FAILURE,
+	MAP_ET_MEMORY_FULL,
+	MAP_ET_MEMORY_AVAILABLE,
+	MAP_ET_MESSAGE_DELETED,
+	MAP_ET_MESSAGE_SHIFT
+};
+
+struct map_event {
+	enum map_event_type type;
+	uint64_t handle;
+	char *folder;
+	char *old_folder;
+	char *msg_type;
+	char *datetime;
+	char *subject;
+	char *sender_name;
+	char *priority;
+};
+
+/* Handle notification in map client.
+ *
+ * event: Event report.
+ *
+ * Callback shall be called for every received event.
+ */
+typedef void (*map_event_cb) (struct map_event *event, void *user_data);
+
+/* Registers client notification handler callback for events that are
+ * addressed to the given mas instance id for the given device.
+ */
+bool map_register_event_handler(struct obc_session *session, int mas_id,
+					map_event_cb cb, void *user_data);
+
+/* Unregisters client notification handler callback.
+ */
+void map_unregister_event_handler(struct obc_session *session, int mas_id);
+
+/* Dispatch notification to a registered notification handler callback.
+ */
+void map_dispatch_event(int mas_id, const char *device,
+						struct map_event *event);
diff --git a/bluez/repo/obexd/client/map.c b/bluez/repo/obexd/client/map.c
new file mode 100644
index 0000000..4c6d676
--- /dev/null
+++ b/bluez/repo/obexd/client/map.c
@@ -0,0 +1,2099 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2011  Bartosz Szatkowski <bulislaw@linux.com> for Comarch
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+
+#include "gobex/gobex-apparam.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/map_ap.h"
+#include "dbus.h"
+#include "map-event.h"
+
+#include "map.h"
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+
+#define OBEX_MAS_UUID \
+	"\xBB\x58\x2B\x40\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66"
+#define OBEX_MAS_UUID_LEN 16
+
+#define MAP_INTERFACE "org.bluez.obex.MessageAccess1"
+#define MAP_MSG_INTERFACE "org.bluez.obex.Message1"
+#define ERROR_INTERFACE "org.bluez.obex.Error"
+#define MAS_UUID "00001132-0000-1000-8000-00805f9b34fb"
+
+#define DEFAULT_COUNT 1024
+#define DEFAULT_OFFSET 0
+
+#define CHARSET_NATIVE 0
+#define CHARSET_UTF8 1
+
+static const char * const filter_list[] = {
+	"subject",
+	"timestamp",
+	"sender",
+	"sender-address",
+	"recipient",
+	"recipient-address",
+	"type",
+	"size",
+	"status",
+	"text",
+	"attachment",
+	"priority",
+	"read",
+	"sent",
+	"protected",
+	"replyto",
+	NULL
+};
+
+#define FILTER_BIT_MAX	15
+#define FILTER_ALL	0x0000FFFF
+
+#define FILTER_READ_STATUS_NONE		0x00
+#define FILTER_READ_STATUS_ONLY_UNREAD	0x01
+#define FILTER_READ_STATUS_ONLY_READ	0x02
+
+#define FILTER_PRIORITY_NONE		0x00
+#define FILTER_PRIORITY_ONLY_HIGH	0x01
+#define FILTER_PRIORITY_ONLY_NONHIGH	0x02
+
+#define STATUS_READ 0
+#define STATUS_DELETE 1
+#define FILLER_BYTE 0x30
+
+struct map_data {
+	struct obc_session *session;
+	GHashTable *messages;
+	int16_t mas_instance_id;
+	uint8_t supported_message_types;
+	uint32_t supported_features;
+};
+
+struct pending_request {
+	struct map_data *map;
+	DBusMessage *msg;
+	char *folder;
+};
+
+#define MAP_MSG_FLAG_PRIORITY	0x01
+#define MAP_MSG_FLAG_READ	0x02
+#define MAP_MSG_FLAG_SENT	0x04
+#define MAP_MSG_FLAG_PROTECTED	0x08
+#define MAP_MSG_FLAG_TEXT	0x10
+
+struct map_msg {
+	struct map_data *data;
+	char *path;
+	uint64_t handle;
+	char *subject;
+	char *timestamp;
+	char *sender;
+	char *sender_address;
+	char *replyto;
+	char *recipient;
+	char *recipient_address;
+	char *type;
+	uint64_t size;
+	char *status;
+	uint64_t attachment_size;
+	uint8_t flags;
+	char *folder;
+	GDBusPendingPropertySet pending;
+};
+
+struct map_parser {
+	struct pending_request *request;
+	DBusMessageIter *iter;
+};
+
+static DBusConnection *conn = NULL;
+
+static struct pending_request *pending_request_new(struct map_data *map,
+							DBusMessage *message)
+{
+	struct pending_request *p;
+
+	p = g_new0(struct pending_request, 1);
+	p->map = map;
+	p->msg = dbus_message_ref(message);
+
+	return p;
+}
+
+static void pending_request_free(struct pending_request *p)
+{
+	dbus_message_unref(p->msg);
+
+	g_free(p->folder);
+	g_free(p);
+}
+
+static void simple_cb(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	DBusMessage *reply;
+
+	if (err != NULL)
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+	else
+		reply = dbus_message_new_method_return(request->msg);
+
+	g_dbus_send_message(conn, reply);
+	pending_request_free(request);
+}
+
+static DBusMessage *map_setpath(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct map_data *map = user_data;
+	const char *folder;
+	struct pending_request *request;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &folder,
+						DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".InvalidArguments",
+					NULL);
+
+	request = pending_request_new(map, message);
+
+	obc_session_setpath(map->session, folder, simple_cb, request, &err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply =  g_dbus_create_error(message,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		g_error_free(err);
+		pending_request_free(request);
+		return reply;
+	}
+
+	return NULL;
+}
+
+static void folder_element(GMarkupParseContext *ctxt, const char *element,
+				const char **names, const char **values,
+				gpointer user_data, GError **gerr)
+{
+	DBusMessageIter dict, *iter = user_data;
+	const char *key;
+	int i;
+
+	if (strcasecmp("folder", element) != 0)
+		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);
+
+	for (i = 0, key = names[i]; key; key = names[++i]) {
+		if (strcasecmp("name", key) == 0)
+			obex_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING,
+								&values[i]);
+	}
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static const GMarkupParser folder_parser = {
+	folder_element,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void folder_listing_cb(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	GMarkupParseContext *ctxt;
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+	char *contents;
+	size_t size;
+	int perr;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		goto done;
+	}
+
+	perr = obc_transfer_get_contents(transfer, &contents, &size);
+	if (perr < 0) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"Error reading contents: %s",
+						strerror(-perr));
+		goto done;
+	}
+
+	reply = dbus_message_new_method_return(request->msg);
+	if (reply == NULL) {
+		g_free(contents);
+		goto clean;
+	}
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_TYPE_ARRAY_AS_STRING
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+	ctxt = g_markup_parse_context_new(&folder_parser, 0, &array, NULL);
+	g_markup_parse_context_parse(ctxt, contents, size, NULL);
+	g_markup_parse_context_free(ctxt);
+	dbus_message_iter_close_container(&iter, &array);
+	g_free(contents);
+
+done:
+	g_dbus_send_message(conn, reply);
+clean:
+	pending_request_free(request);
+}
+
+static DBusMessage *get_folder_listing(struct map_data *map,
+							DBusMessage *message,
+							GObexApparam *apparam)
+{
+	struct pending_request *request;
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err);
+	if (transfer == NULL) {
+		g_obex_apparam_free(apparam);
+		goto fail;
+	}
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	request = pending_request_new(map, message);
+
+	if (!obc_session_queue(map->session, transfer, folder_listing_cb,
+							request, &err)) {
+		pending_request_free(request);
+		goto fail;
+	}
+
+	return NULL;
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	guint16 num;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &num);
+
+	return g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, num);
+}
+
+static GObexApparam *parse_max_count(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	guint16 num;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &num);
+
+	return g_obex_apparam_set_uint16(apparam, MAP_AP_MAXLISTCOUNT, num);
+}
+
+static GObexApparam *parse_folder_filters(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (strcasecmp(key, "Offset") == 0) {
+			if (parse_offset(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "MaxCount") == 0) {
+			if (parse_max_count(apparam, &value) == NULL)
+				return NULL;
+		}
+
+		dbus_message_iter_next(&array);
+	}
+
+	return apparam;
+}
+
+static DBusMessage *map_list_folders(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct map_data *map = user_data;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	dbus_message_iter_init(message, &args);
+
+	apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT,
+							DEFAULT_COUNT);
+	apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET,
+							DEFAULT_OFFSET);
+
+	if (parse_folder_filters(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return get_folder_listing(map, message, apparam);
+}
+
+static void map_msg_free(void *data)
+{
+	struct map_msg *msg = data;
+
+	g_free(msg->path);
+	g_free(msg->subject);
+	g_free(msg->folder);
+	g_free(msg->timestamp);
+	g_free(msg->sender);
+	g_free(msg->sender_address);
+	g_free(msg->replyto);
+	g_free(msg->recipient);
+	g_free(msg->recipient_address);
+	g_free(msg->type);
+	g_free(msg->status);
+	g_free(msg);
+}
+
+static DBusMessage *map_msg_get(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct map_msg *msg = user_data;
+	struct obc_transfer *transfer;
+	const char *target_file;
+	gboolean attachment;
+	GError *err = NULL;
+	DBusMessage *reply;
+	GObexApparam *apparam;
+	char handle[17];
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &target_file,
+				DBUS_TYPE_BOOLEAN, &attachment,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle);
+
+	transfer = obc_transfer_get("x-bt/message", handle, target_file, &err);
+	if (transfer == NULL)
+		goto fail;
+
+	apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_ATTACHMENT,
+								attachment);
+	apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET,
+								CHARSET_UTF8);
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	if (!obc_session_queue(msg->data->session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static void set_message_status_cb(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct map_msg *msg = user_data;
+
+	if (err != NULL) {
+		g_dbus_pending_property_error(msg->pending,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		goto done;
+	}
+
+	g_dbus_pending_property_success(msg->pending);
+
+done:
+	msg->pending = 0;
+}
+
+static gboolean get_folder(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->folder);
+
+	return TRUE;
+}
+
+static gboolean subject_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->subject != NULL;
+}
+
+static gboolean get_subject(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->subject);
+
+	return TRUE;
+}
+
+static gboolean timestamp_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->timestamp != NULL;
+}
+
+static gboolean get_timestamp(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->timestamp);
+
+	return TRUE;
+}
+
+static gboolean sender_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->sender != NULL;
+}
+
+static gboolean get_sender(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->sender);
+
+	return TRUE;
+}
+
+static gboolean sender_address_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->sender_address != NULL;
+}
+
+static gboolean get_sender_address(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&msg->sender_address);
+
+	return TRUE;
+}
+
+static gboolean replyto_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->replyto != NULL;
+}
+
+static gboolean get_replyto(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->replyto);
+
+	return TRUE;
+}
+
+static gboolean recipient_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->recipient != NULL;
+}
+
+static gboolean get_recipient(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->recipient);
+
+	return TRUE;
+}
+
+static gboolean recipient_address_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->recipient_address != NULL;
+}
+
+static gboolean get_recipient_address(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+						&msg->recipient_address);
+
+	return TRUE;
+}
+
+static gboolean type_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->type != NULL;
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->type);
+
+	return TRUE;
+}
+
+static gboolean get_size(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &msg->size);
+
+	return TRUE;
+}
+
+static gboolean reception_status_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct map_msg *msg = data;
+
+	return msg->status != NULL;
+}
+
+static gboolean get_reception_status(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->status);
+
+	return TRUE;
+}
+
+static gboolean get_attachment_size(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct map_msg *msg = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
+							&msg->attachment_size);
+
+	return TRUE;
+}
+
+static gboolean get_flag(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, uint8_t flag,
+					void *data)
+{
+	struct map_msg *msg = data;
+	dbus_bool_t value = (msg->flags & flag) != 0;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean get_priority(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	return get_flag(property, iter, MAP_MSG_FLAG_PRIORITY, data);
+}
+
+static gboolean get_read(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	return get_flag(property, iter, MAP_MSG_FLAG_READ, data);
+}
+
+static gboolean get_sent(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	return get_flag(property, iter, MAP_MSG_FLAG_SENT, data);
+}
+
+static gboolean get_protected(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	return get_flag(property, iter, MAP_MSG_FLAG_PROTECTED, data);
+}
+
+static gboolean get_text(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	return get_flag(property, iter, MAP_MSG_FLAG_TEXT, data);
+}
+
+static void set_status(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			uint8_t status, void *data)
+{
+	struct map_msg *msg = data;
+	struct obc_transfer *transfer;
+	gboolean value;
+	GError *err = NULL;
+	GObexApparam *apparam;
+	char contents[1];
+	char handle[17];
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	contents[0] = FILLER_BYTE;
+
+	snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle);
+
+	transfer = obc_transfer_put("x-bt/messageStatus", handle, NULL,
+					contents, sizeof(contents), &err);
+	if (transfer == NULL)
+		goto fail;
+
+	apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_STATUSINDICATOR,
+								status);
+	apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_STATUSVALUE,
+								value);
+	obc_transfer_set_apparam(transfer, apparam);
+
+	if (!obc_session_queue(msg->data->session, transfer,
+				set_message_status_cb, msg, &err))
+		goto fail;
+
+	msg->pending = id;
+	return;
+
+fail:
+	g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+}
+
+static void set_read(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	set_status(property, iter, id, STATUS_READ, data);
+}
+
+static void set_deleted(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	set_status(property, iter, id, STATUS_DELETE, data);
+}
+
+static const GDBusMethodTable map_msg_methods[] = {
+	{ GDBUS_METHOD("Get",
+			GDBUS_ARGS({ "targetfile", "s" },
+						{ "attachment", "b" }),
+			GDBUS_ARGS({ "transfer", "o" },
+						{ "properties", "a{sv}" }),
+			map_msg_get) },
+	{ }
+};
+
+static const GDBusPropertyTable map_msg_properties[] = {
+	{ "Folder", "s", get_folder },
+	{ "Subject", "s", get_subject, NULL, subject_exists },
+	{ "Timestamp", "s", get_timestamp, NULL, timestamp_exists },
+	{ "Sender", "s", get_sender, NULL, sender_exists },
+	{ "SenderAddress", "s", get_sender_address, NULL,
+						sender_address_exists },
+	{ "ReplyTo", "s", get_replyto, NULL, replyto_exists },
+	{ "Recipient", "s", get_recipient, NULL, recipient_exists },
+	{ "RecipientAddress", "s", get_recipient_address, NULL,
+						recipient_address_exists },
+	{ "Type", "s", get_type, NULL, type_exists },
+	{ "Size", "t", get_size },
+	{ "Text", "b", get_text },
+	{ "Status", "s", get_reception_status, NULL, reception_status_exists },
+	{ "AttachmentSize", "t", get_attachment_size },
+	{ "Priority", "b", get_priority },
+	{ "Read", "b", get_read, set_read },
+	{ "Sent", "b", get_sent },
+	{ "Protected", "b", get_protected },
+	{ "Deleted", "b", NULL, set_deleted },
+	{ }
+};
+
+static void parse_type(struct map_msg *msg, const char *value)
+{
+	const char *type = NULL;
+
+	if (strcasecmp(value, "SMS_GSM") == 0)
+		type = "sms-gsm";
+	else if (strcasecmp(value, "SMS_CDMA") == 0)
+		type = "sms-cdma";
+	else if (strcasecmp(value, "EMAIL") == 0)
+		type = "email";
+	else if (strcasecmp(value, "MMS") == 0)
+		type = "mms";
+
+	if (g_strcmp0(msg->type, type) == 0)
+		return;
+
+	g_free(msg->type);
+	msg->type = g_strdup(type);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Type");
+}
+
+static struct map_msg *map_msg_create(struct map_data *data, uint64_t handle,
+					const char *folder, const char *type)
+{
+	struct map_msg *msg;
+
+	msg = g_new0(struct map_msg, 1);
+	msg->data = data;
+	msg->handle = handle;
+	msg->path = g_strdup_printf("%s/message%" PRIu64,
+					obc_session_get_path(data->session),
+					msg->handle);
+	msg->folder = g_strdup(folder);
+
+	if (!g_dbus_register_interface(conn, msg->path, MAP_MSG_INTERFACE,
+						map_msg_methods, NULL,
+						map_msg_properties,
+						msg, map_msg_free)) {
+		map_msg_free(msg);
+		return NULL;
+	}
+
+	g_hash_table_insert(data->messages, &msg->handle, msg);
+
+	if (type)
+		parse_type(msg, type);
+
+	return msg;
+}
+
+static void parse_subject(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->subject, value) == 0)
+		return;
+
+	g_free(msg->subject);
+	msg->subject = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Subject");
+}
+
+static void parse_datetime(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->timestamp, value) == 0)
+		return;
+
+	g_free(msg->timestamp);
+	msg->timestamp = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Timestamp");
+}
+
+static void parse_sender(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->sender, value) == 0)
+		return;
+
+	g_free(msg->sender);
+	msg->sender = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Sender");
+}
+
+static void parse_sender_address(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->sender_address, value) == 0)
+		return;
+
+	g_free(msg->sender_address);
+	msg->sender_address = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+					MAP_MSG_INTERFACE, "SenderAddress");
+}
+
+static void parse_replyto(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->replyto, value) == 0)
+		return;
+
+	g_free(msg->replyto);
+	msg->replyto = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "ReplyTo");
+}
+
+static void parse_recipient(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->recipient, value) == 0)
+		return;
+
+	g_free(msg->recipient);
+	msg->recipient = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Recipient");
+}
+
+static void parse_recipient_address(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->recipient_address, value) == 0)
+		return;
+
+	g_free(msg->recipient_address);
+	msg->recipient_address = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+					MAP_MSG_INTERFACE, "RecipientAddress");
+}
+
+static void parse_size(struct map_msg *msg, const char *value)
+{
+	uint64_t size = g_ascii_strtoll(value, NULL, 10);
+
+	if (msg->size == size)
+		return;
+
+	msg->size = size;
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Size");
+}
+
+static void parse_text(struct map_msg *msg, const char *value)
+{
+	gboolean flag = strcasecmp(value, "no") != 0;
+	uint8_t oldflags = msg->flags;
+
+	if (flag)
+		msg->flags |= MAP_MSG_FLAG_TEXT;
+	else
+		msg->flags &= ~MAP_MSG_FLAG_TEXT;
+
+	if (msg->flags != oldflags)
+		g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Text");
+}
+
+static void parse_status(struct map_msg *msg, const char *value)
+{
+	if (g_strcmp0(msg->status, value) == 0)
+		return;
+
+	g_free(msg->status);
+	msg->status = g_strdup(value);
+
+	g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Status");
+}
+
+static void parse_attachment_size(struct map_msg *msg, const char *value)
+{
+	uint64_t attachment_size = g_ascii_strtoll(value, NULL, 10);
+
+	if (msg->attachment_size == attachment_size)
+		return;
+
+	msg->attachment_size = attachment_size;
+
+	g_dbus_emit_property_changed(conn, msg->path,
+					MAP_MSG_INTERFACE, "AttachmentSize");
+}
+
+static void parse_priority(struct map_msg *msg, const char *value)
+{
+	gboolean flag = strcasecmp(value, "no") != 0;
+	uint8_t oldflags = msg->flags;
+
+	if (flag)
+		msg->flags |= MAP_MSG_FLAG_PRIORITY;
+	else
+		msg->flags &= ~MAP_MSG_FLAG_PRIORITY;
+
+	if (msg->flags != oldflags)
+		g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Priority");
+}
+
+static void parse_read(struct map_msg *msg, const char *value)
+{
+	gboolean flag = strcasecmp(value, "no") != 0;
+	uint8_t oldflags = msg->flags;
+
+	if (flag)
+		msg->flags |= MAP_MSG_FLAG_READ;
+	else
+		msg->flags &= ~MAP_MSG_FLAG_READ;
+
+	if (msg->flags != oldflags)
+		g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Read");
+}
+
+static void parse_sent(struct map_msg *msg, const char *value)
+{
+	gboolean flag = strcasecmp(value, "no") != 0;
+	uint8_t oldflags = msg->flags;
+
+	if (flag)
+		msg->flags |= MAP_MSG_FLAG_SENT;
+	else
+		msg->flags &= ~MAP_MSG_FLAG_SENT;
+
+	if (msg->flags != oldflags)
+		g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Sent");
+}
+
+static void parse_protected(struct map_msg *msg, const char *value)
+{
+	gboolean flag = strcasecmp(value, "no") != 0;
+	uint8_t oldflags = msg->flags;
+
+	if (flag)
+		msg->flags |= MAP_MSG_FLAG_PROTECTED;
+	else
+		msg->flags &= ~MAP_MSG_FLAG_PROTECTED;
+
+	if (msg->flags != oldflags)
+		g_dbus_emit_property_changed(conn, msg->path,
+						MAP_MSG_INTERFACE, "Protected");
+}
+
+static struct map_msg_parser {
+	const char *name;
+	void (*func) (struct map_msg *msg, const char *value);
+} msg_parsers[] = {
+		{ "subject", parse_subject },
+		{ "datetime", parse_datetime },
+		{ "sender_name", parse_sender },
+		{ "sender_addressing", parse_sender_address },
+		{ "replyto_addressing", parse_replyto },
+		{ "recipient_name", parse_recipient },
+		{ "recipient_addressing", parse_recipient_address },
+		{ "type", parse_type },
+		{ "size", parse_size },
+		{ "text", parse_text },
+		{ "reception_status", parse_status },
+		{ "attachment_size", parse_attachment_size },
+		{ "priority", parse_priority },
+		{ "read", parse_read },
+		{ "sent", parse_sent },
+		{ "protected", parse_protected },
+		{ }
+};
+
+static void msg_element(GMarkupParseContext *ctxt, const char *element,
+				const char **names, const char **values,
+				gpointer user_data, GError **gerr)
+{
+	struct map_parser *parser = user_data;
+	struct map_data *data = parser->request->map;
+	DBusMessageIter entry, *iter = parser->iter;
+	struct map_msg *msg;
+	const char *key;
+	int i;
+	uint64_t handle;
+
+	if (strcasecmp("msg", element) != 0)
+		return;
+
+	for (i = 0, key = names[i]; key; key = names[++i]) {
+		if (strcasecmp(key, "handle") == 0)
+			break;
+	}
+
+	handle = strtoull(values[i], NULL, 16);
+
+	msg = g_hash_table_lookup(data->messages, &handle);
+	if (msg == NULL) {
+		msg = map_msg_create(data, handle, parser->request->folder,
+									NULL);
+		if (msg == NULL)
+			return;
+	}
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL,
+								&entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+								&msg->path);
+
+	for (i = 0, key = names[i]; key; key = names[++i]) {
+		struct map_msg_parser *parser;
+
+		for (parser = msg_parsers; parser && parser->name; parser++) {
+			if (strcasecmp(key, parser->name) == 0) {
+				if (values[i])
+					parser->func(msg, values[i]);
+				break;
+			}
+		}
+	}
+
+	g_dbus_get_properties(conn, msg->path, MAP_MSG_INTERFACE, &entry);
+
+	dbus_message_iter_close_container(iter, &entry);
+}
+
+static const GMarkupParser msg_parser = {
+	msg_element,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void message_listing_cb(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	struct map_parser *parser;
+	GMarkupParseContext *ctxt;
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+	char *contents;
+	size_t size;
+	int perr;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		goto done;
+	}
+
+	perr = obc_transfer_get_contents(transfer, &contents, &size);
+	if (perr < 0) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"Error reading contents: %s",
+						strerror(-perr));
+		goto done;
+	}
+
+	reply = dbus_message_new_method_return(request->msg);
+	if (reply == NULL) {
+		g_free(contents);
+		goto clean;
+	}
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_OBJECT_PATH_AS_STRING
+					DBUS_TYPE_ARRAY_AS_STRING
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&array);
+
+	parser = g_new(struct map_parser, 1);
+	parser->request = request;
+	parser->iter = &array;
+
+	ctxt = g_markup_parse_context_new(&msg_parser, 0, parser, NULL);
+	g_markup_parse_context_parse(ctxt, contents, size, NULL);
+	g_markup_parse_context_free(ctxt);
+	dbus_message_iter_close_container(&iter, &array);
+	g_free(contents);
+	g_free(parser);
+
+done:
+	g_dbus_send_message(conn, reply);
+clean:
+	pending_request_free(request);
+}
+
+static char *get_absolute_folder(struct map_data *map, const char *subfolder)
+{
+	const char *root = obc_session_get_folder(map->session);
+
+	if (!subfolder || strlen(subfolder) == 0)
+		return g_strdup(root);
+	else if (g_str_has_suffix(root, "/"))
+		return g_strconcat(root, subfolder, NULL);
+	else
+		return g_strconcat(root, "/", subfolder, NULL);
+}
+
+static DBusMessage *get_message_listing(struct map_data *map,
+							DBusMessage *message,
+							const char *folder,
+							GObexApparam *apparam)
+{
+	struct pending_request *request;
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	transfer = obc_transfer_get("x-bt/MAP-msg-listing", folder, NULL, &err);
+	if (transfer == NULL) {
+		g_obex_apparam_free(apparam);
+		goto fail;
+	}
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	request = pending_request_new(map, message);
+	request->folder = get_absolute_folder(map, folder);
+
+	if (!obc_session_queue(map->session, transfer, message_listing_cb,
+							request, &err)) {
+		pending_request_free(request);
+		goto fail;
+	}
+
+	return NULL;
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static GObexApparam *parse_subject_length(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	guint8 num;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &num);
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_SUBJECTLENGTH, num);
+}
+
+static uint64_t get_filter_mask(const char *filterstr)
+{
+	int i;
+
+	if (!filterstr)
+		return 0;
+
+	if (!g_ascii_strcasecmp(filterstr, "ALL"))
+		return FILTER_ALL;
+
+	for (i = 0; filter_list[i] != NULL; i++)
+		if (!g_ascii_strcasecmp(filterstr, filter_list[i]))
+			return 1ULL << i;
+
+	return 0;
+}
+
+static int set_field(guint32 *filter, const char *filterstr)
+{
+	guint64 mask;
+
+	mask = get_filter_mask(filterstr);
+
+	if (mask == 0)
+		return -EINVAL;
+
+	*filter |= mask;
+	return 0;
+}
+
+static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+	guint32 filter = 0;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+		const char *string;
+
+		dbus_message_iter_get_basic(&array, &string);
+
+		if (set_field(&filter, string) < 0)
+			return NULL;
+
+		dbus_message_iter_next(&array);
+	}
+
+	return g_obex_apparam_set_uint32(apparam, MAP_AP_PARAMETERMASK,
+								filter);
+}
+
+static GObexApparam *parse_filter_type(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+	guint8 types = 0;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+		const char *string;
+
+		dbus_message_iter_get_basic(&array, &string);
+
+		if (!g_ascii_strcasecmp(string, "sms"))
+			types |= 0x03; /* sms-gsm and sms-cdma */
+		else if (!g_ascii_strcasecmp(string, "email"))
+			types |= 0x04; /* email */
+		else if (!g_ascii_strcasecmp(string, "mms"))
+			types |= 0x08; /* mms */
+		else
+			return NULL;
+
+		dbus_message_iter_next(&array);
+	}
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERMESSAGETYPE,
+									types);
+}
+
+static GObexApparam *parse_period_begin(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	const char *string;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODBEGIN,
+								string);
+}
+
+static GObexApparam *parse_period_end(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	const char *string;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODEND,
+								string);
+}
+
+static GObexApparam *parse_filter_read(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	guint8 status = FILTER_READ_STATUS_NONE;
+	dbus_bool_t dbus_status = FALSE;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &dbus_status);
+
+	if (dbus_status)
+		status = FILTER_READ_STATUS_ONLY_READ;
+	else
+		status = FILTER_READ_STATUS_ONLY_UNREAD;
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERREADSTATUS,
+								status);
+}
+
+static GObexApparam *parse_filter_recipient(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	const char *string;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	return g_obex_apparam_set_string(apparam, MAP_AP_FILTERRECIPIENT,
+								string);
+}
+
+static GObexApparam *parse_filter_sender(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	const char *string;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	return g_obex_apparam_set_string(apparam, MAP_AP_FILTERORIGINATOR,
+								string);
+}
+
+static GObexApparam *parse_filter_priority(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	guint8 priority = FILTER_PRIORITY_NONE;
+	dbus_bool_t dbus_priority = FALSE;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &dbus_priority);
+
+	if (dbus_priority)
+		priority = FILTER_PRIORITY_ONLY_HIGH;
+	else
+		priority = FILTER_PRIORITY_ONLY_NONHIGH;
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERPRIORITY,
+								priority);
+}
+
+static GObexApparam *parse_message_filters(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+
+	DBG("");
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (strcasecmp(key, "Offset") == 0) {
+			if (parse_offset(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "MaxCount") == 0) {
+			if (parse_max_count(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "SubjectLength") == 0) {
+			if (parse_subject_length(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Fields") == 0) {
+			if (parse_fields(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Types") == 0) {
+			if (parse_filter_type(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "PeriodBegin") == 0) {
+			if (parse_period_begin(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "PeriodEnd") == 0) {
+			if (parse_period_end(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Read") == 0) {
+			if (parse_filter_read(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Recipient") == 0) {
+			if (parse_filter_recipient(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Sender") == 0) {
+			if (parse_filter_sender(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Priority") == 0) {
+			if (parse_filter_priority(apparam, &value) == NULL)
+				return NULL;
+		}
+
+		dbus_message_iter_next(&array);
+	}
+
+	return apparam;
+}
+
+static DBusMessage *map_list_messages(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct map_data *map = user_data;
+	const char *folder;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	dbus_message_iter_init(message, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &folder);
+
+	apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT,
+							DEFAULT_COUNT);
+	apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET,
+							DEFAULT_OFFSET);
+
+	dbus_message_iter_next(&args);
+
+	if (parse_message_filters(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return get_message_listing(map, message, folder, apparam);
+}
+
+static char **get_filter_strs(uint64_t filter, int *size)
+{
+	char **list, **item;
+	int i;
+
+	list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2));
+
+	item = list;
+
+	for (i = 0; filter_list[i] != NULL; i++)
+		if (filter & (1ULL << i))
+			*(item++) = g_strdup(filter_list[i]);
+
+	*item = NULL;
+	*size = item - list;
+	return list;
+}
+
+static DBusMessage *map_list_filter_fields(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	char **filters = NULL;
+	int size;
+	DBusMessage *reply;
+
+	filters = get_filter_strs(FILTER_ALL, &size);
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_STRING, &filters, size,
+				DBUS_TYPE_INVALID);
+
+	g_strfreev(filters);
+	return reply;
+}
+
+static void update_inbox_cb(struct obc_session *session,
+				struct obc_transfer *transfer,
+				GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	DBusMessage *reply;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		goto done;
+	}
+
+	reply = dbus_message_new_method_return(request->msg);
+
+done:
+	g_dbus_send_message(conn, reply);
+	pending_request_free(request);
+}
+
+static DBusMessage *map_update_inbox(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct map_data *map = user_data;
+	DBusMessage *reply;
+	char contents[1];
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	struct pending_request *request;
+
+	contents[0] = FILLER_BYTE;
+
+	transfer = obc_transfer_put("x-bt/MAP-messageUpdate", NULL, NULL,
+						contents, sizeof(contents),
+						&err);
+	if (transfer == NULL)
+		goto fail;
+
+	request = pending_request_new(map, message);
+
+	if (!obc_session_queue(map->session, transfer, update_inbox_cb,
+							request, &err)) {
+		pending_request_free(request);
+		goto fail;
+	}
+
+	return NULL;
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *push_message(struct map_data *map,
+							DBusMessage *message,
+							const char *filename,
+							const char *folder,
+							GObexApparam *apparam)
+{
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	transfer = obc_transfer_put("x-bt/message", folder, filename,
+								NULL, 0, &err);
+	if (transfer == NULL) {
+		g_obex_apparam_free(apparam);
+		goto fail;
+	}
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	if (!obc_session_queue(map->session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static GObexApparam *parse_transparent(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	dbus_bool_t transparent;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &transparent);
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_TRANSPARENT,
+						transparent ? TRUE : FALSE);
+}
+
+static GObexApparam *parse_retry(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	dbus_bool_t retry;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &retry);
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_RETRY,
+							retry ? TRUE : FALSE);
+}
+
+static GObexApparam *parse_charset(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	guint8 charset = 0;
+	const char *string;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	if (strcasecmp(string, "native") == 0)
+		charset = CHARSET_NATIVE;
+	else if (strcasecmp(string, "utf8") == 0)
+		charset = CHARSET_UTF8;
+	else
+		return NULL;
+
+	return g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET, charset);
+}
+
+static GObexApparam *parse_push_options(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (strcasecmp(key, "Transparent") == 0) {
+			if (parse_transparent(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Retry") == 0) {
+			if (parse_retry(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Charset") == 0) {
+			if (parse_charset(apparam, &value) == NULL)
+				return NULL;
+		}
+
+		dbus_message_iter_next(&array);
+	}
+
+	return apparam;
+}
+
+static DBusMessage *map_push_message(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct map_data *map = user_data;
+	char *filename;
+	char *folder;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	dbus_message_iter_init(message, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &filename);
+
+	dbus_message_iter_next(&args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	dbus_message_iter_get_basic(&args, &folder);
+
+	dbus_message_iter_next(&args);
+
+	apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_CHARSET, CHARSET_UTF8);
+
+	if (parse_push_options(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return push_message(map, message, filename, folder, apparam);
+}
+
+static const GDBusMethodTable map_methods[] = {
+	{ GDBUS_ASYNC_METHOD("SetFolder",
+				GDBUS_ARGS({ "name", "s" }), NULL,
+				map_setpath) },
+	{ GDBUS_ASYNC_METHOD("ListFolders",
+			GDBUS_ARGS({ "filters", "a{sv}" }),
+			GDBUS_ARGS({ "content", "aa{sv}" }),
+			map_list_folders) },
+	{ GDBUS_ASYNC_METHOD("ListMessages",
+			GDBUS_ARGS({ "folder", "s" }, { "filter", "a{sv}" }),
+			GDBUS_ARGS({ "messages", "a{oa{sv}}" }),
+			map_list_messages) },
+	{ GDBUS_METHOD("ListFilterFields",
+			NULL,
+			GDBUS_ARGS({ "fields", "as" }),
+			map_list_filter_fields) },
+	{ GDBUS_ASYNC_METHOD("UpdateInbox",
+			NULL,
+			NULL,
+			map_update_inbox) },
+	{ GDBUS_ASYNC_METHOD("PushMessage",
+			GDBUS_ARGS({ "file", "s" }, { "folder", "s" },
+						{ "args", "a{sv}" }),
+			GDBUS_ARGS({ "transfer", "o" },
+						{ "properties", "a{sv}" }),
+			map_push_message) },
+	{ }
+};
+
+static void map_msg_remove(void *data)
+{
+	struct map_msg *msg = data;
+	char *path;
+
+	path = msg->path;
+	msg->path = NULL;
+	g_dbus_unregister_interface(conn, path, MAP_MSG_INTERFACE);
+	g_free(path);
+}
+
+static void map_handle_new_message(struct map_data *map,
+							struct map_event *event)
+{
+	struct map_msg *msg;
+
+	msg = g_hash_table_lookup(map->messages, &event->handle);
+	/* New message event can be used if a new message replaces an old one */
+	if (msg)
+		g_hash_table_remove(map->messages, &event->handle);
+
+	map_msg_create(map, event->handle, event->folder, event->msg_type);
+}
+
+static void map_handle_status_changed(struct map_data *map,
+							struct map_event *event,
+							const char *status)
+{
+	struct map_msg *msg;
+
+	msg = g_hash_table_lookup(map->messages, &event->handle);
+	if (msg == NULL)
+		return;
+
+	if (g_strcmp0(msg->status, status) == 0)
+		return;
+
+	g_free(msg->status);
+	msg->status = g_strdup(status);
+
+	g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE,
+								"Status");
+}
+
+static void map_handle_folder_changed(struct map_data *map,
+							struct map_event *event,
+							const char *folder)
+{
+	struct map_msg *msg;
+
+	if (!folder)
+		return;
+
+	msg = g_hash_table_lookup(map->messages, &event->handle);
+	if (!msg)
+		return;
+
+	if (g_strcmp0(msg->folder, folder) == 0)
+		return;
+
+	g_free(msg->folder);
+	msg->folder = g_strdup(folder);
+
+	g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE,
+								"Folder");
+}
+
+static void map_handle_notification(struct map_event *event, void *user_data)
+{
+	struct map_data *map = user_data;
+
+	DBG("Event report for %s:%d", obc_session_get_destination(map->session),
+							map->mas_instance_id);
+	DBG("type=%x handle=%" PRIx64 " folder=%s old_folder=%s msg_type=%s",
+		event->type, event->handle, event->folder, event->old_folder,
+		event->msg_type);
+
+	switch (event->type) {
+	case MAP_ET_NEW_MESSAGE:
+		map_handle_new_message(map, event);
+		break;
+	case MAP_ET_DELIVERY_SUCCESS:
+		map_handle_status_changed(map, event, "delivery-success");
+		break;
+	case MAP_ET_SENDING_SUCCESS:
+		map_handle_status_changed(map, event, "sending-success");
+		break;
+	case MAP_ET_DELIVERY_FAILURE:
+		map_handle_status_changed(map, event, "delivery-failure");
+		break;
+	case MAP_ET_SENDING_FAILURE:
+		map_handle_status_changed(map, event, "sending-failure");
+		break;
+	case MAP_ET_MESSAGE_DELETED:
+		map_handle_folder_changed(map, event, "/telecom/msg/deleted");
+		break;
+	case MAP_ET_MESSAGE_SHIFT:
+		map_handle_folder_changed(map, event, event->folder);
+		break;
+	case MAP_ET_MEMORY_FULL:
+	case MAP_ET_MEMORY_AVAILABLE:
+	default:
+		break;
+	}
+}
+
+static bool set_notification_registration(struct map_data *map, bool status)
+{
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	GObexApparam *apparam;
+	char contents[1];
+	const char *address;
+
+	address = obc_session_get_destination(map->session);
+	if (!address || map->mas_instance_id < 0)
+		return FALSE;
+
+	if (status) {
+		map_register_event_handler(map->session, map->mas_instance_id,
+						&map_handle_notification, map);
+	} else {
+		map_unregister_event_handler(map->session,
+							map->mas_instance_id);
+	}
+
+	contents[0] = FILLER_BYTE;
+
+	transfer = obc_transfer_put("x-bt/MAP-NotificationRegistration", NULL,
+					NULL, contents, sizeof(contents), &err);
+
+	if (transfer == NULL)
+		return false;
+
+	apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_NOTIFICATIONSTATUS,
+									status);
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	if (obc_session_queue(map->session, transfer, NULL, map, &err))
+		return true;
+
+	return false;
+}
+
+static void map_free(void *data)
+{
+	struct map_data *map = data;
+
+	set_notification_registration(map, false);
+
+	obc_session_unref(map->session);
+	g_hash_table_unref(map->messages);
+	g_free(map);
+}
+
+static void parse_service_record(struct map_data *map)
+{
+	const void *data;
+
+	/* MAS instance id */
+	map->mas_instance_id = -1;
+	data = obc_session_get_attribute(map->session,
+						SDP_ATTR_MAS_INSTANCE_ID);
+	if (data != NULL)
+		map->mas_instance_id = *(uint8_t *)data;
+	else
+		DBG("Failed to read MAS instance id");
+
+	/* Supported Message Types */
+	data = obc_session_get_attribute(map->session,
+					SDP_ATTR_SUPPORTED_MESSAGE_TYPES);
+	if (data != NULL)
+		map->supported_message_types = *(uint8_t *)data;
+	else
+		DBG("Failed to read supported message types");
+
+	/* Supported Feature Bits */
+	data = obc_session_get_attribute(map->session,
+					SDP_ATTR_MAP_SUPPORTED_FEATURES);
+	if(data != NULL)
+		map->supported_features = *(uint32_t *) data;
+	else
+		map->supported_features = 0x0000001f;
+}
+
+static int map_probe(struct obc_session *session)
+{
+	struct map_data *map;
+	const char *path;
+
+	path = obc_session_get_path(session);
+
+	map = g_try_new0(struct map_data, 1);
+	if (!map)
+		return -ENOMEM;
+
+	map->session = obc_session_ref(session);
+	map->messages = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL,
+								map_msg_remove);
+
+	parse_service_record(map);
+
+	DBG("%s, instance id %d", path, map->mas_instance_id);
+
+	set_notification_registration(map, true);
+
+	if (!g_dbus_register_interface(conn, path, MAP_INTERFACE, map_methods,
+					NULL, NULL, map, map_free)) {
+		map_free(map);
+
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void map_remove(struct obc_session *session)
+{
+	const char *path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	g_dbus_unregister_interface(conn, path, MAP_INTERFACE);
+}
+
+static struct obc_driver map = {
+	.service = "MAP",
+	.uuid = MAS_UUID,
+	.target = OBEX_MAS_UUID,
+	.target_len = OBEX_MAS_UUID_LEN,
+	.probe = map_probe,
+	.remove = map_remove
+};
+
+int map_init(void)
+{
+	int err;
+
+	DBG("");
+
+	conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+	if (!conn)
+		return -EIO;
+
+	err = obc_driver_register(&map);
+	if (err < 0) {
+		dbus_connection_unref(conn);
+		conn = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+void map_exit(void)
+{
+	DBG("");
+
+	dbus_connection_unref(conn);
+	conn = NULL;
+
+	obc_driver_unregister(&map);
+}
diff --git a/bluez/repo/obexd/client/map.h b/bluez/repo/obexd/client/map.h
new file mode 100644
index 0000000..86f6b95
--- /dev/null
+++ b/bluez/repo/obexd/client/map.h
@@ -0,0 +1,24 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2011  Bartosz Szatkowski <bulislaw@linux.com> for Comarch
+ *
+ *  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
+ *
+ */
+
+int map_init(void);
+void map_exit(void);
diff --git a/bluez/repo/obexd/client/mns.c b/bluez/repo/obexd/client/mns.c
new file mode 100644
index 0000000..14b1848
--- /dev/null
+++ b/bluez/repo/obexd/client/mns.c
@@ -0,0 +1,406 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/map_ap.h"
+#include "map-event.h"
+
+#include "obexd/src/manager.h"
+
+struct mns_session {
+	GString *buffer;
+	GObexApparam *inparams;
+	char *remote_address;
+	uint8_t mas_instance_id;
+};
+
+static const uint8_t MNS_TARGET[TARGET_SIZE] = {
+			0xbb, 0x58, 0x2b, 0x41, 0x42, 0x0c, 0x11, 0xdb,
+			0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66  };
+
+static int get_params(struct obex_session *os, struct mns_session *mns)
+{
+	const uint8_t *buffer;
+	ssize_t size;
+
+	size = obex_get_apparam(os, &buffer);
+	if (size < 0)
+		size = 0;
+
+	mns->inparams = g_obex_apparam_decode(buffer, size);
+	if (mns->inparams == NULL) {
+		DBG("Error when parsing parameters!");
+		return -EBADR;
+	}
+
+	return 0;
+}
+
+static void reset_request(struct mns_session *mns)
+{
+	if (mns->buffer) {
+		g_string_free(mns->buffer, TRUE);
+		mns->buffer = NULL;
+	}
+
+	if (mns->inparams) {
+		g_obex_apparam_free(mns->inparams);
+		mns->inparams = NULL;
+	}
+}
+
+static void mns_session_free(struct mns_session *mns)
+{
+	reset_request(mns);
+
+	if (mns->remote_address)
+		g_free(mns->remote_address);
+
+	g_free(mns);
+}
+
+static void *mns_connect(struct obex_session *os, int *err)
+{
+	struct mns_session *mns;
+	char *address;
+
+	manager_register_session(os);
+
+	mns = g_new0(struct mns_session, 1);
+
+	if (obex_getpeername(os, &address) == 0) {
+		mns->remote_address = g_strdup(address);
+		g_free(address);
+	}
+
+	DBG("MNS connected to %s", mns->remote_address);
+
+	if (err)
+		*err = 0;
+
+	return mns;
+}
+
+static void mns_disconnect(struct obex_session *os, void *user_data)
+{
+	struct mns_session *mns = user_data;
+
+	DBG("MNS disconnected from %s", mns->remote_address);
+
+	manager_unregister_session(os);
+
+	mns_session_free(mns);
+}
+
+static int mns_put(struct obex_session *os, void *user_data)
+{
+	struct mns_session *mns = user_data;
+	const char *type = obex_get_type(os);
+	const char *name = obex_get_name(os);
+	int ret;
+
+	DBG("PUT: name %s type %s mns %p", name, type, mns);
+
+	if (type == NULL)
+		return -EBADR;
+
+	ret = get_params(os, mns);
+	if (ret < 0)
+		goto failed;
+
+	ret = obex_put_stream_start(os, name);
+	if (ret < 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	reset_request(mns);
+
+	return ret;
+}
+
+static void parse_event_report_type(struct map_event *event, const char *value)
+{
+	if (!g_ascii_strcasecmp(value, "NewMessage"))
+		event->type = MAP_ET_NEW_MESSAGE;
+	else if (!g_ascii_strcasecmp(value, "DeliverySuccess"))
+		event->type = MAP_ET_DELIVERY_SUCCESS;
+	else if (!g_ascii_strcasecmp(value, "SendingSuccess"))
+		event->type = MAP_ET_SENDING_SUCCESS;
+	else if (!g_ascii_strcasecmp(value, "DeliveryFailure"))
+		event->type = MAP_ET_DELIVERY_FAILURE;
+	else if (!g_ascii_strcasecmp(value, "SendingFailure"))
+		event->type = MAP_ET_SENDING_FAILURE;
+	else if (!g_ascii_strcasecmp(value, "MemoryFull"))
+		event->type = MAP_ET_MEMORY_FULL;
+	else if (!g_ascii_strcasecmp(value, "MemoryAvailable"))
+		event->type = MAP_ET_MEMORY_AVAILABLE;
+	else if (!g_ascii_strcasecmp(value, "MessageDeleted"))
+		event->type = MAP_ET_MESSAGE_DELETED;
+	else if (!g_ascii_strcasecmp(value, "MessageShift"))
+		event->type = MAP_ET_MESSAGE_SHIFT;
+}
+
+static void parse_event_report_handle(struct map_event *event,
+							const char *value)
+{
+	event->handle = strtoull(value, NULL, 16);
+}
+
+static void parse_event_report_folder(struct map_event *event,
+							const char *value)
+{
+	g_free(event->folder);
+
+	if (g_str_has_prefix(value, "/"))
+		event->folder = g_strdup(value);
+	else
+		event->folder = g_strconcat("/", value, NULL);
+}
+
+static void parse_event_report_old_folder(struct map_event *event,
+							const char *value)
+{
+	g_free(event->old_folder);
+
+	if (g_str_has_prefix(value, "/"))
+		event->old_folder = g_strdup(value);
+	else
+		event->old_folder = g_strconcat("/", value, NULL);
+}
+
+static void parse_event_report_msg_type(struct map_event *event,
+							const char *value)
+{
+	g_free(event->msg_type);
+	event->msg_type = g_strdup(value);
+}
+
+static void parse_event_report_date_time(struct map_event *event,
+							const char *value)
+{
+	g_free(event->datetime);
+	event->datetime = g_strdup(value);
+}
+
+static void parse_event_report_subject(struct map_event *event,
+							const char *value)
+{
+	g_free(event->subject);
+	event->subject = g_strdup(value);
+}
+
+static void parse_event_report_sender_name(struct map_event *event,
+							const char *value)
+{
+	g_free(event->sender_name);
+	event->sender_name = g_strdup(value);
+}
+
+static void parse_event_report_priority(struct map_event *event,
+							const char *value)
+{
+	g_free(event->priority);
+	event->priority = g_strdup(value);
+}
+
+static struct map_event_report_parser {
+	const char *name;
+	void (*func) (struct map_event *event, const char *value);
+} event_report_parsers[] = {
+		{ "type", parse_event_report_type },
+		{ "handle", parse_event_report_handle },
+		{ "folder", parse_event_report_folder },
+		{ "old_folder", parse_event_report_old_folder },
+		{ "msg_type", parse_event_report_msg_type },
+		{ "datetime", parse_event_report_date_time },
+		{ "subject", parse_event_report_subject },
+		{ "sender_name", parse_event_report_sender_name },
+		{ "priority", parse_event_report_priority },
+		{ }
+};
+
+static void event_report_element(GMarkupParseContext *ctxt,
+				const char *element, const char **names,
+				const char **values, gpointer user_data,
+								GError **gerr)
+{
+	struct map_event *event = user_data;
+	const char *key;
+	int i;
+
+	if (strcasecmp("event", element) != 0)
+		return;
+
+	for (i = 0, key = names[i]; key; key = names[++i]) {
+		struct map_event_report_parser *parser;
+
+		for (parser = event_report_parsers; parser && parser->name;
+								parser++) {
+			if (strcasecmp(key, parser->name) == 0) {
+				if (values[i])
+					parser->func(event, values[i]);
+				break;
+			}
+		}
+	}
+}
+
+static const GMarkupParser event_report_parser = {
+	event_report_element,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void map_event_free(struct map_event *event)
+{
+	g_free(event->folder);
+	g_free(event->old_folder);
+	g_free(event->msg_type);
+	g_free(event->datetime);
+	g_free(event->subject);
+	g_free(event->sender_name);
+	g_free(event->priority);
+	g_free(event);
+}
+
+static void *event_report_open(const char *name, int oflag, mode_t mode,
+				void *driver_data, size_t *size, int *err)
+{
+	struct mns_session *mns = driver_data;
+
+	DBG("");
+
+	g_obex_apparam_get_uint8(mns->inparams, MAP_AP_MASINSTANCEID,
+							&mns->mas_instance_id);
+
+	mns->buffer = g_string_new("");
+
+	if (err != NULL)
+		*err = 0;
+
+	return mns;
+}
+
+static int event_report_close(void *obj)
+{
+	struct mns_session *mns = obj;
+	GMarkupParseContext *ctxt;
+	struct map_event *event;
+
+	DBG("");
+
+	event = g_new0(struct map_event, 1);
+	ctxt = g_markup_parse_context_new(&event_report_parser, 0, event,
+									NULL);
+	g_markup_parse_context_parse(ctxt, mns->buffer->str, mns->buffer->len,
+									NULL);
+	g_markup_parse_context_free(ctxt);
+
+	map_dispatch_event(mns->mas_instance_id, mns->remote_address, event);
+	map_event_free(event);
+
+	reset_request(mns);
+
+	return 0;
+}
+
+static ssize_t event_report_write(void *obj, const void *buf, size_t count)
+{
+	struct mns_session *mns = obj;
+
+	DBG("");
+
+	g_string_append_len(mns->buffer, buf, count);
+	return count;
+}
+
+static struct obex_service_driver mns = {
+	.name = "Message Notification server",
+	.service = OBEX_MNS,
+	.target = MNS_TARGET,
+	.target_size = TARGET_SIZE,
+	.connect = mns_connect,
+	.put = mns_put,
+	.disconnect = mns_disconnect,
+};
+
+static struct obex_mime_type_driver mime_event_report = {
+	.target = MNS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/MAP-event-report",
+	.open = event_report_open,
+	.close = event_report_close,
+	.write = event_report_write,
+};
+
+static int mns_init(void)
+{
+	int err;
+
+	err = obex_mime_type_driver_register(&mime_event_report);
+	if (err < 0)
+		goto fail_mime_event;
+
+	err = obex_service_driver_register(&mns);
+	if (err < 0)
+		goto fail_mns_reg;
+
+	return 0;
+
+fail_mns_reg:
+	obex_mime_type_driver_unregister(&mime_event_report);
+fail_mime_event:
+	return err;
+}
+
+static void mns_exit(void)
+{
+	obex_service_driver_unregister(&mns);
+	obex_mime_type_driver_unregister(&mime_event_report);
+}
+
+OBEX_PLUGIN_DEFINE(mns, mns_init, mns_exit)
diff --git a/bluez/repo/obexd/client/opp.c b/bluez/repo/obexd/client/opp.c
new file mode 100644
index 0000000..3c2801a
--- /dev/null
+++ b/bluez/repo/obexd/client/opp.c
@@ -0,0 +1,215 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2011 Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "opp.h"
+
+#define OPP_UUID "00001105-0000-1000-8000-00805f9b34fb"
+#define OPP_INTERFACE "org.bluez.obex.ObjectPush1"
+#define ERROR_INTERFACE "org.bluez.obex.Error"
+
+struct opp_data {
+	struct obc_session *session;
+};
+
+static DBusConnection *conn = NULL;
+
+static DBusMessage *opp_send_file(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct opp_data *opp = user_data;
+	struct obc_transfer *transfer;
+	DBusMessage *reply;
+	char *filename;
+	char *basename;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &filename,
+					DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	basename = g_path_get_basename(filename);
+
+	transfer = obc_transfer_put(NULL, basename, filename, NULL, 0, &err);
+
+	g_free(basename);
+
+	if (transfer == NULL)
+		goto fail;
+
+	if (!obc_session_queue(opp->session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message,
+				ERROR_INTERFACE ".Failed", "%s", err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *opp_pull_business_card(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct opp_data *opp = user_data;
+	struct obc_transfer *pull;
+	DBusMessage *reply;
+	const char *filename = NULL;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+				DBUS_TYPE_STRING, &filename,
+				DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	pull = obc_transfer_get("text/x-vcard", NULL, filename, &err);
+	if (pull == NULL)
+		goto fail;
+
+	if (!obc_session_queue(opp->session, pull, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(pull, message);
+
+fail:
+	reply = g_dbus_create_error(message,
+				ERROR_INTERFACE ".Failed", "%s", err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *opp_exchange_business_cards(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", NULL);
+}
+
+static const GDBusMethodTable opp_methods[] = {
+	{ GDBUS_METHOD("SendFile",
+		GDBUS_ARGS({ "sourcefile", "s" }),
+		GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
+		opp_send_file) },
+	{ GDBUS_METHOD("PullBusinessCard",
+		GDBUS_ARGS({ "targetfile", "s" }),
+		GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
+		opp_pull_business_card) },
+	{ GDBUS_METHOD("ExchangeBusinessCards",
+		GDBUS_ARGS({ "clientfile", "s" }, { "targetfile", "s" }),
+		GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
+		opp_exchange_business_cards) },
+	{ }
+};
+
+static void opp_free(void *data)
+{
+	struct opp_data *opp = data;
+
+	obc_session_unref(opp->session);
+	g_free(opp);
+}
+
+static int opp_probe(struct obc_session *session)
+{
+	struct opp_data *opp;
+	const char *path;
+
+	path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	opp = g_try_new0(struct opp_data, 1);
+	if (!opp)
+		return -ENOMEM;
+
+	opp->session = obc_session_ref(session);
+
+	if (!g_dbus_register_interface(conn, path, OPP_INTERFACE, opp_methods,
+						NULL, NULL, opp, opp_free)) {
+		opp_free(opp);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void opp_remove(struct obc_session *session)
+{
+	const char *path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	g_dbus_unregister_interface(conn, path, OPP_INTERFACE);
+}
+
+static struct obc_driver opp = {
+	.service = "OPP",
+	.uuid = OPP_UUID,
+	.probe = opp_probe,
+	.remove = opp_remove
+};
+
+int opp_init(void)
+{
+	int err;
+
+	DBG("");
+
+	conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+	if (!conn)
+		return -EIO;
+
+	err = obc_driver_register(&opp);
+	if (err < 0) {
+		dbus_connection_unref(conn);
+		conn = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+void opp_exit(void)
+{
+	DBG("");
+
+	dbus_connection_unref(conn);
+	conn = NULL;
+
+	obc_driver_unregister(&opp);
+}
diff --git a/bluez/repo/obexd/client/opp.h b/bluez/repo/obexd/client/opp.h
new file mode 100644
index 0000000..a23e94e
--- /dev/null
+++ b/bluez/repo/obexd/client/opp.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2011 Intel Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+int opp_init(void);
+void opp_exit(void);
diff --git a/bluez/repo/obexd/client/pbap.c b/bluez/repo/obexd/client/pbap.c
new file mode 100644
index 0000000..1ab34a7
--- /dev/null
+++ b/bluez/repo/obexd/client/pbap.c
@@ -0,0 +1,1338 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gobex/gobex-apparam.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "pbap.h"
+
+#define OBEX_PBAP_UUID \
+	"\x79\x61\x35\xF0\xF0\xC5\x11\xD8\x09\x66\x08\x00\x20\x0C\x9A\x66"
+#define OBEX_PBAP_UUID_LEN 16
+
+#define FORMAT_VCARD21	0x0
+#define FORMAT_VCARD30	0x1
+
+#define ORDER_INDEXED		0x0
+#define ORDER_ALPHANUMERIC	0x1
+#define ORDER_PHONETIC		0x2
+
+#define ATTRIB_NAME		0x0
+#define ATTRIB_NUMBER		0x1
+#define ATTRIB_SOUND		0x2
+
+#define DEFAULT_COUNT	65535
+#define DEFAULT_OFFSET	0
+
+#define PULLPHONEBOOK		0x1
+#define GETPHONEBOOKSIZE	0x2
+
+#define ORDER_TAG		0x01
+#define SEARCHVALUE_TAG		0x02
+#define SEARCHATTRIB_TAG	0x03
+#define MAXLISTCOUNT_TAG	0x04
+#define LISTSTARTOFFSET_TAG	0x05
+#define FILTER_TAG		0x06
+#define FORMAT_TAG		0X07
+#define PHONEBOOKSIZE_TAG	0X08
+#define NEWMISSEDCALLS_TAG	0X09
+#define PRIMARY_COUNTER_TAG	0X0A
+#define SECONDARY_COUNTER_TAG	0X0B
+#define DATABASEID_TAG		0X0D
+#define SUPPORTED_FEATURES_TAG  0x10
+
+#define DOWNLOAD_FEATURE	0x00000001
+#define BROWSE_FEATURE		0x00000002
+#define DATABASEID_FEATURE	0x00000004
+#define FOLDER_VERSION_FEATURE	0x00000008
+#define VCARD_SELECTING_FEATURE	0x00000010
+#define ENHANCED_CALLS_FEATURE	0x00000020
+#define UCI_FEATURE		0x00000040
+#define UID_FEATURE		0x00000080
+#define REFERENCING_FEATURE	0x00000100
+#define DEFAULT_IMAGE_FEATURE	0x00000200
+
+static const char *filter_list[] = {
+	"VERSION",
+	"FN",
+	"N",
+	"PHOTO",
+	"BDAY",
+	"ADR",
+	"LABEL",
+	"TEL",
+	"EMAIL",
+	"MAILER",
+	"TZ",
+	"GEO",
+	"TITLE",
+	"ROLE",
+	"LOGO",
+	"AGENT",
+	"ORG",
+	"NOTE",
+	"REV",
+	"SOUND",
+	"URL",
+	"UID",
+	"KEY",
+	"NICKNAME",
+	"CATEGORIES",
+	"PROID",
+	"CLASS",
+	"SORT-STRING",
+	"X-IRMC-CALL-DATETIME",
+	"X-BT-SPEEDDIALKEY",
+	"X-BT-UCI",
+	"X-BT-UID",
+	NULL
+};
+
+#define FILTER_BIT_MAX	63
+#define FILTER_ALL	0xFFFFFFFFFFFFFFFFULL
+
+#define PBAP_INTERFACE "org.bluez.obex.PhonebookAccess1"
+#define ERROR_INTERFACE "org.bluez.obex.Error"
+#define PBAP_UUID "0000112f-0000-1000-8000-00805f9b34fb"
+
+struct pbap_data {
+	struct obc_session *session;
+	char *path;
+	uint16_t version;
+	uint32_t supported_features;
+	uint8_t databaseid[16];
+	uint8_t primary[16];
+	uint8_t secondary[16];
+};
+
+struct pending_request {
+	struct pbap_data *pbap;
+	DBusMessage *msg;
+};
+
+static DBusConnection *conn = NULL;
+
+static struct pending_request *pending_request_new(struct pbap_data *pbap,
+							DBusMessage *message)
+{
+	struct pending_request *p;
+
+	p = g_new0(struct pending_request, 1);
+	p->pbap = pbap;
+	p->msg = dbus_message_ref(message);
+
+	return p;
+}
+
+static void pending_request_free(struct pending_request *p)
+{
+	dbus_message_unref(p->msg);
+	g_free(p);
+}
+
+static void listing_element(GMarkupParseContext *ctxt,
+				const char *element,
+				const char **names,
+				const char **values,
+				gpointer user_data,
+				GError **gerr)
+{
+	DBusMessageIter *item = user_data, entry;
+	char **key;
+	const char *handle = NULL, *vcardname = NULL;
+
+	if (g_str_equal(element, "card") != TRUE)
+		return;
+
+	for (key = (char **) names; *key; key++, values++) {
+		if (g_str_equal(*key, "handle") == TRUE)
+			handle = *values;
+		else if (g_str_equal(*key, "name") == TRUE)
+			vcardname = *values;
+	}
+
+	if (!handle || !vcardname)
+		return;
+
+	dbus_message_iter_open_container(item, DBUS_TYPE_STRUCT, NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &handle);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &vcardname);
+	dbus_message_iter_close_container(item, &entry);
+}
+
+static const GMarkupParser listing_parser = {
+	listing_element,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static char *build_phonebook_path(const char *location, const char *item)
+{
+	char *path = NULL, *tmp, *tmp1;
+	gboolean internal = FALSE;
+
+	if (!g_ascii_strcasecmp(location, "int") ||
+			!g_ascii_strcasecmp(location, "internal")) {
+		path = g_strdup("/telecom");
+		internal = TRUE;
+	} else if (!g_ascii_strncasecmp(location, "sim", 3)) {
+		if (strlen(location) == 3)
+			tmp = g_strdup("sim1");
+		else
+			tmp = g_ascii_strup(location, 4);
+
+		path = g_build_filename("/", tmp, "telecom", NULL);
+		g_free(tmp);
+	} else
+		return NULL;
+
+	if (!g_ascii_strcasecmp(item, "pb") ||
+		!g_ascii_strcasecmp(item, "ich") ||
+		!g_ascii_strcasecmp(item, "och") ||
+		!g_ascii_strcasecmp(item, "mch") ||
+		!g_ascii_strcasecmp(item, "cch") ||
+		(internal && !g_ascii_strcasecmp(item, "spd")) ||
+		(internal && !g_ascii_strcasecmp(item, "fav"))) {
+		tmp = path;
+		tmp1 = g_ascii_strdown(item, -1);
+		path = g_build_filename(tmp, tmp1, NULL);
+		g_free(tmp);
+		g_free(tmp1);
+	} else {
+		g_free(path);
+		return NULL;
+	}
+
+	return path;
+}
+
+/* should only be called inside pbap_set_path */
+static void pbap_reset_path(struct pbap_data *pbap)
+{
+	if (!pbap->path)
+		return;
+
+	obc_session_setpath(pbap->session, pbap->path, NULL, NULL, NULL);
+}
+
+static void pbap_setpath_cb(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	struct pbap_data *pbap = request->pbap;
+
+	if (err != NULL)
+		pbap_reset_path(pbap);
+	else
+		g_dbus_emit_property_changed(conn,
+					obc_session_get_path(pbap->session),
+					PBAP_INTERFACE, "Folder");
+
+	if (err) {
+		DBusMessage *reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		g_dbus_send_message(conn, reply);
+	} else
+		g_dbus_send_reply(conn, request->msg, DBUS_TYPE_INVALID);
+
+	pending_request_free(request);
+}
+
+static void read_version(struct pbap_data *pbap, GObexApparam *apparam)
+{
+	const guint8 *data;
+	uint8_t value[16];
+	gsize len;
+
+	if (!(pbap->supported_features & FOLDER_VERSION_FEATURE))
+		return;
+
+	if (!g_obex_apparam_get_bytes(apparam, PRIMARY_COUNTER_TAG, &data,
+								&len)) {
+		len = sizeof(value);
+		memset(value, 0, len);
+		data = value;
+	}
+
+	if (memcmp(pbap->primary, data, len)) {
+		memcpy(pbap->primary, data, len);
+		g_dbus_emit_property_changed(conn,
+					obc_session_get_path(pbap->session),
+					PBAP_INTERFACE, "PrimaryCounter");
+	}
+
+	if (!g_obex_apparam_get_bytes(apparam, SECONDARY_COUNTER_TAG, &data,
+								&len)) {
+		len = sizeof(value);
+		memset(value, 0, len);
+		data = value;
+	}
+
+	if (memcmp(pbap->secondary, data, len)) {
+		memcpy(pbap->secondary, data, len);
+		g_dbus_emit_property_changed(conn,
+					obc_session_get_path(pbap->session),
+					PBAP_INTERFACE, "SecondaryCounter");
+	}
+}
+
+static void read_databaseid(struct pbap_data *pbap, GObexApparam *apparam)
+{
+	const guint8 *data;
+	guint8 value[16];
+	gsize len;
+
+	if (!(pbap->supported_features & DATABASEID_FEATURE))
+		return;
+
+	if (!g_obex_apparam_get_bytes(apparam, DATABASEID_TAG, &data, &len)) {
+		len = sizeof(value);
+		memset(value, 0, len);
+		data = value;
+	}
+
+	if (memcmp(data, pbap->databaseid, len)) {
+		memcpy(pbap->databaseid, data, len);
+		g_dbus_emit_property_changed(conn,
+					obc_session_get_path(pbap->session),
+					PBAP_INTERFACE, "DatabaseIdentifier");
+	}
+}
+
+static void read_return_apparam(struct obc_transfer *transfer,
+					struct pbap_data *pbap,
+					guint16 *phone_book_size,
+					guint8 *new_missed_calls)
+{
+	GObexApparam *apparam;
+
+	*phone_book_size = 0;
+	*new_missed_calls = 0;
+
+	apparam = obc_transfer_get_apparam(transfer);
+	if (apparam == NULL)
+		return;
+
+	g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG,
+							phone_book_size);
+	g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG,
+							new_missed_calls);
+
+	read_version(pbap, apparam);
+	read_databaseid(pbap, apparam);
+}
+
+static void phonebook_size_callback(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	DBusMessage *reply;
+	guint16 phone_book_size;
+	guint8 new_missed_calls;
+
+	if (err) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		goto send;
+	}
+
+	reply = dbus_message_new_method_return(request->msg);
+
+	read_return_apparam(transfer, request->pbap, &phone_book_size,
+							&new_missed_calls);
+
+	if (dbus_message_is_method_call(request->msg, PBAP_INTERFACE,
+								"GetSize"))
+		dbus_message_append_args(reply,
+					DBUS_TYPE_UINT16, &phone_book_size,
+					DBUS_TYPE_INVALID);
+
+send:
+	g_dbus_send_message(conn, reply);
+	pending_request_free(request);
+}
+
+static void pull_vcard_listing_callback(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *request = user_data;
+	GMarkupParseContext *ctxt;
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+	char *contents;
+	size_t size;
+	int perr;
+
+	if (err) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", err->message);
+		goto send;
+	}
+
+	perr = obc_transfer_get_contents(transfer, &contents, &size);
+	if (perr < 0) {
+		reply = g_dbus_create_error(request->msg,
+						ERROR_INTERFACE ".Failed",
+						"Error reading contents: %s",
+						strerror(-perr));
+		goto send;
+	}
+
+	reply = dbus_message_new_method_return(request->msg);
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+			DBUS_STRUCT_END_CHAR_AS_STRING, &array);
+	ctxt = g_markup_parse_context_new(&listing_parser, 0, &array, NULL);
+	g_markup_parse_context_parse(ctxt, contents, size, NULL);
+	g_markup_parse_context_free(ctxt);
+	dbus_message_iter_close_container(&iter, &array);
+	g_free(contents);
+
+send:
+	g_dbus_send_message(conn, reply);
+	pending_request_free(request);
+}
+
+static GObexApparam *parse_format(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	const char *string;
+	guint8 format;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	if (!string || g_str_equal(string, ""))
+		format = FORMAT_VCARD21;
+	else if (!g_ascii_strcasecmp(string, "vcard21"))
+		format = FORMAT_VCARD21;
+	else if (!g_ascii_strcasecmp(string, "vcard30"))
+		format = FORMAT_VCARD30;
+	else
+		return NULL;
+
+	return g_obex_apparam_set_uint8(apparam, FORMAT_TAG, format);
+}
+
+static GObexApparam *parse_order(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	const char *string;
+	guint8 order;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &string);
+
+	if (!string || g_str_equal(string, ""))
+		order = ORDER_INDEXED;
+	else if (!g_ascii_strcasecmp(string, "indexed"))
+		order = ORDER_INDEXED;
+	else if (!g_ascii_strcasecmp(string, "alphanumeric"))
+		order = ORDER_ALPHANUMERIC;
+	else if (!g_ascii_strcasecmp(string, "phonetic"))
+		order = ORDER_PHONETIC;
+	else
+		return NULL;
+
+	return g_obex_apparam_set_uint8(apparam, ORDER_TAG, order);
+}
+
+static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	guint16 num;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &num);
+
+	return g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, num);
+}
+
+static GObexApparam *parse_max_count(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	guint16 num;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+		return NULL;
+
+	dbus_message_iter_get_basic(iter, &num);
+
+	return g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, num);
+}
+
+static uint64_t get_filter_mask(const char *filterstr)
+{
+	int i, bit = -1;
+
+	if (!filterstr)
+		return 0;
+
+	if (!g_ascii_strcasecmp(filterstr, "ALL"))
+		return FILTER_ALL;
+
+	for (i = 0; filter_list[i] != NULL; i++)
+		if (!g_ascii_strcasecmp(filterstr, filter_list[i]))
+			return 1ULL << i;
+
+	if (strlen(filterstr) < 4 || strlen(filterstr) > 5
+			|| g_ascii_strncasecmp(filterstr, "bit", 3) != 0)
+		return 0;
+
+	sscanf(&filterstr[3], "%d", &bit);
+	if (bit >= 0 && bit <= FILTER_BIT_MAX)
+		return 1ULL << bit;
+	else
+		return 0;
+}
+
+static int set_field(guint64 *filter, const char *filterstr)
+{
+	guint64 mask;
+
+	mask = get_filter_mask(filterstr);
+
+	if (mask == 0)
+		return -EINVAL;
+
+	*filter |= mask;
+	return 0;
+}
+
+static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+	guint64 filter = 0;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+		const char *string;
+
+		dbus_message_iter_get_basic(&array, &string);
+
+		if (set_field(&filter, string) < 0)
+			return NULL;
+
+		dbus_message_iter_next(&array);
+	}
+
+	return g_obex_apparam_set_uint64(apparam, FILTER_TAG, filter);
+}
+
+static GObexApparam *parse_filters(GObexApparam *apparam,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return NULL;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (strcasecmp(key, "Format") == 0) {
+			if (parse_format(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Order") == 0) {
+			if (parse_order(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Offset") == 0) {
+			if (parse_offset(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "MaxCount") == 0) {
+			if (parse_max_count(apparam, &value) == NULL)
+				return NULL;
+		} else if (strcasecmp(key, "Fields") == 0) {
+			if (parse_fields(apparam, &value) == NULL)
+				return NULL;
+		}
+
+		dbus_message_iter_next(&array);
+	}
+
+	return apparam;
+}
+
+static DBusMessage *pull_phonebook(struct pbap_data *pbap,
+						DBusMessage *message,
+						guint8 type,
+						const char *targetfile,
+						GObexApparam *apparam)
+{
+	struct pending_request *request;
+	struct obc_transfer *transfer;
+	char *name;
+	session_callback_t func;
+	DBusMessage *reply;
+	GError *err = NULL;
+
+	name = g_strconcat(g_path_skip_root(pbap->path), ".vcf", NULL);
+
+	transfer = obc_transfer_get("x-bt/phonebook", name, targetfile, &err);
+	if (transfer == NULL) {
+		g_obex_apparam_free(apparam);
+		goto fail;
+	}
+
+	switch (type) {
+	case PULLPHONEBOOK:
+		func = NULL;
+		request = NULL;
+		break;
+	case GETPHONEBOOKSIZE:
+		func = phonebook_size_callback;
+		request = pending_request_new(pbap, message);
+		break;
+	default:
+		error("Unexpected type : 0x%2x", type);
+		return NULL;
+	}
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	if (!obc_session_queue(pbap->session, transfer, func, request, &err)) {
+		if (request != NULL)
+			pending_request_free(request);
+
+		goto fail;
+	}
+
+	g_free(name);
+
+	if (targetfile == NULL)
+		return NULL;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	g_free(name);
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *pull_vcard_listing(struct pbap_data *pbap,
+					DBusMessage *message, const char *name,
+					GObexApparam *apparam)
+{
+	struct pending_request *request;
+	struct obc_transfer *transfer;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err);
+	if (transfer == NULL) {
+		g_obex_apparam_free(apparam);
+		goto fail;
+	}
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	request = pending_request_new(pbap, message);
+	if (obc_session_queue(pbap->session, transfer,
+				pull_vcard_listing_callback, request, &err))
+		return NULL;
+
+	pending_request_free(request);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *pbap_select(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+	const char *item, *location;
+	char *path;
+	struct pending_request *request;
+	GError *err = NULL;
+
+	if (dbus_message_get_args(message, NULL,
+			DBUS_TYPE_STRING, &location,
+			DBUS_TYPE_STRING, &item,
+			DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	path = build_phonebook_path(location, item);
+	if (path == NULL)
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid path");
+
+	if (pbap->path != NULL && g_str_equal(pbap->path, path)) {
+		g_free(path);
+		return dbus_message_new_method_return(message);
+	}
+
+	request = pending_request_new(pbap, message);
+
+	obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
+									&err);
+	if (err != NULL) {
+		DBusMessage *reply;
+		reply =  g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
+							"%s", err->message);
+		g_error_free(err);
+		g_free(path);
+		pending_request_free(request);
+		return reply;
+	}
+
+	g_free(pbap->path);
+	pbap->path = path;
+
+	return NULL;
+}
+
+static DBusMessage *pbap_pull_all(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+	const char *targetfile;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	if (!pbap->path)
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".Forbidden",
+					"Call Select first of all");
+
+	dbus_message_iter_init(message, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &targetfile);
+	dbus_message_iter_next(&args);
+
+	apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
+							DEFAULT_COUNT);
+	apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
+							DEFAULT_OFFSET);
+
+	if (parse_filters(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return pull_phonebook(pbap, message, PULLPHONEBOOK, targetfile,
+								apparam);
+}
+
+static DBusMessage *pull_vcard(struct pbap_data *pbap, DBusMessage *message,
+				const char *name, const char *targetfile,
+				GObexApparam *apparam)
+{
+	struct obc_transfer *transfer;
+	DBusMessage *reply;
+	GError *err = NULL;
+
+	transfer = obc_transfer_get("x-bt/vcard", name, targetfile, &err);
+	if (transfer == NULL) {
+		g_obex_apparam_free(apparam);
+		goto fail;
+	}
+
+	obc_transfer_set_apparam(transfer, apparam);
+
+	if (!obc_session_queue(pbap->session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *pbap_pull_vcard(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+	GObexApparam *apparam;
+	const char *name, *targetfile;
+	DBusMessageIter args;
+
+	if (!pbap->path)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".Forbidden",
+				"Call Select first of all");
+
+	dbus_message_iter_init(message, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &name);
+	dbus_message_iter_next(&args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &targetfile);
+	dbus_message_iter_next(&args);
+
+	apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
+							DEFAULT_COUNT);
+	apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
+							DEFAULT_OFFSET);
+
+	if (parse_filters(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return pull_vcard(pbap, message, name, targetfile, apparam);
+}
+
+static DBusMessage *pbap_list(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	if (!pbap->path)
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".Forbidden",
+					"Call Select first of all");
+
+	dbus_message_iter_init(message, &args);
+
+	apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
+							DEFAULT_COUNT);
+	apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
+							DEFAULT_OFFSET);
+
+	if (parse_filters(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return pull_vcard_listing(pbap, message, "", apparam);
+}
+
+static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field)
+{
+	guint8 attrib;
+
+	if (!field || g_str_equal(field, ""))
+		attrib = ATTRIB_NAME;
+	else if (!g_ascii_strcasecmp(field, "name"))
+		attrib = ATTRIB_NAME;
+	else if (!g_ascii_strcasecmp(field, "number"))
+		attrib = ATTRIB_NUMBER;
+	else if (!g_ascii_strcasecmp(field, "sound"))
+		attrib = ATTRIB_SOUND;
+	else
+		return NULL;
+
+	return g_obex_apparam_set_uint8(apparam, SEARCHATTRIB_TAG, attrib);
+}
+
+static DBusMessage *pbap_search(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+	char *field, *value;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	if (!pbap->path)
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".Forbidden",
+					"Call Select first of all");
+
+	dbus_message_iter_init(message, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &field);
+	dbus_message_iter_next(&args);
+
+	apparam = parse_attribute(NULL, field);
+	if (apparam == NULL)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+
+	dbus_message_iter_get_basic(&args, &value);
+	dbus_message_iter_next(&args);
+
+	apparam = g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG,
+							DEFAULT_COUNT);
+	apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
+							DEFAULT_OFFSET);
+	apparam = g_obex_apparam_set_string(apparam, SEARCHVALUE_TAG, value);
+
+	if (parse_filters(apparam, &args) == NULL) {
+		g_obex_apparam_free(apparam);
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InvalidArguments", NULL);
+	}
+
+	return pull_vcard_listing(pbap, message, "", apparam);
+}
+
+static DBusMessage *pbap_get_size(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+	GObexApparam *apparam;
+	DBusMessageIter args;
+
+	if (!pbap->path)
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".Forbidden",
+					"Call Select first of all");
+
+	dbus_message_iter_init(message, &args);
+
+	apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, 0);
+	apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
+							DEFAULT_OFFSET);
+
+	return pull_phonebook(pbap, message, GETPHONEBOOKSIZE, NULL, apparam);
+}
+
+static char **get_filter_strs(uint64_t filter, int *size)
+{
+	char **list, **item;
+	int i;
+
+	list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2));
+
+	item = list;
+
+	for (i = 0; filter_list[i] != NULL; i++)
+		if (filter & (1ULL << i))
+			*(item++) = g_strdup(filter_list[i]);
+
+	for (; i <= FILTER_BIT_MAX; i++)
+		if (filter & (1ULL << i))
+			*(item++) = g_strdup_printf("%s%d", "BIT", i);
+
+	*item = NULL;
+	*size = item - list;
+	return list;
+}
+
+static DBusMessage *pbap_list_filter_fields(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	char **filters = NULL;
+	int size;
+	DBusMessage *reply;
+
+	filters = get_filter_strs(FILTER_ALL, &size);
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_STRING, &filters, size,
+				DBUS_TYPE_INVALID);
+
+	g_strfreev(filters);
+	return reply;
+}
+
+static DBusMessage *pbap_update_version(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct pbap_data *pbap = user_data;
+
+	if (!(pbap->supported_features & FOLDER_VERSION_FEATURE))
+		return g_dbus_create_error(message,
+					ERROR_INTERFACE ".NotSupported",
+					"Operation is not supported");
+
+	return pbap_get_size(connection, message, user_data);
+}
+
+static const GDBusMethodTable pbap_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Select",
+			GDBUS_ARGS({ "location", "s" }, { "phonebook", "s" }),
+			NULL, pbap_select) },
+	{ GDBUS_METHOD("PullAll",
+			GDBUS_ARGS({ "targetfile", "s" },
+					{ "filters", "a{sv}" }),
+			GDBUS_ARGS({ "transfer", "o" },
+					{ "properties", "a{sv}" }),
+			pbap_pull_all) },
+	{ GDBUS_METHOD("Pull",
+			GDBUS_ARGS({ "vcard", "s" }, { "targetfile", "s" },
+					{ "filters", "a{sv}" }),
+			GDBUS_ARGS({ "transfer", "o" },
+					{ "properties", "a{sv}" }),
+			pbap_pull_vcard) },
+	{ GDBUS_ASYNC_METHOD("List",
+			GDBUS_ARGS({ "filters", "a{sv}" }),
+			GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
+			pbap_list) },
+	{ GDBUS_ASYNC_METHOD("Search",
+			GDBUS_ARGS({ "field", "s" }, { "value", "s" },
+					{ "filters", "a{sv}" }),
+			GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
+			pbap_search) },
+	{ GDBUS_ASYNC_METHOD("GetSize",
+				NULL, GDBUS_ARGS({ "size", "q" }),
+				pbap_get_size) },
+	{ GDBUS_METHOD("ListFilterFields",
+				NULL, GDBUS_ARGS({ "fields", "as" }),
+				pbap_list_filter_fields) },
+	{ GDBUS_ASYNC_METHOD("UpdateVersion", NULL, NULL,
+				pbap_update_version) },
+	{ }
+};
+
+static gboolean folder_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct pbap_data *pbap = data;
+
+	return pbap->path != NULL;
+}
+
+static gboolean get_folder(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct pbap_data *pbap = data;
+
+	if (!pbap->path)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pbap->path);
+
+	return TRUE;
+}
+
+static gboolean databaseid_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct pbap_data *pbap = data;
+
+	return pbap->supported_features & DATABASEID_FEATURE;
+}
+
+static int u128_to_string(uint8_t *data, char *str, size_t len)
+{
+	return snprintf(str, len, "%02X%02X%02X%02X%02X%02X%02X%02X"
+					"%02X%02X%02X%02X%02X%02X%02X%02X",
+					data[0], data[1], data[2], data[3],
+					data[3], data[5], data[6], data[7],
+					data[8], data[9], data[10], data[11],
+					data[12], data[13], data[14], data[15]);
+}
+
+static gboolean get_databaseid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct pbap_data *pbap = data;
+	char value[33];
+	const char *pvalue = value;
+
+	if (u128_to_string(pbap->databaseid, value, sizeof(value)) < 0)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
+
+	return TRUE;
+}
+
+static gboolean version_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct pbap_data *pbap = data;
+
+	return pbap->supported_features & FOLDER_VERSION_FEATURE;
+}
+
+static gboolean get_primary(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct pbap_data *pbap = data;
+	char value[33];
+	const char *pvalue = value;
+
+	if (u128_to_string(pbap->primary, value, sizeof(value)) < 0)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
+
+	return TRUE;
+}
+
+static gboolean get_secondary(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct pbap_data *pbap = data;
+	char value[33];
+	const char *pvalue = value;
+
+	if (u128_to_string(pbap->secondary, value, sizeof(value)) < 0)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
+
+	return TRUE;
+}
+
+static gboolean image_size_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct pbap_data *pbap = data;
+
+	return pbap->supported_features & DEFAULT_IMAGE_FEATURE;
+}
+
+static gboolean get_image_size(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = TRUE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable pbap_properties[] = {
+	{ "Folder", "s", get_folder, NULL, folder_exists },
+	{ "DatabaseIdentifier", "s", get_databaseid, NULL, databaseid_exists },
+	{ "PrimaryCounter", "s", get_primary, NULL, version_exists },
+	{ "SecondaryCounter", "s", get_secondary, NULL, version_exists },
+	{ "FixedImageSize", "b", get_image_size, NULL, image_size_exists },
+	{ }
+};
+
+static void pbap_free(void *data)
+{
+	struct pbap_data *pbap = data;
+
+	obc_session_unref(pbap->session);
+	g_free(pbap->path);
+	g_free(pbap);
+}
+
+static void parse_service_record(struct pbap_data *pbap)
+{
+	const void *data;
+
+	/* Version */
+	data = obc_session_get_attribute(pbap->session,
+						SDP_ATTR_PFILE_DESC_LIST);
+	if (!data)
+		return;
+
+	pbap->version = GPOINTER_TO_UINT(data);
+
+	/*
+	 * If the PbapSupportedFeatures attribute is not present
+	 * 0x00000003 shall be assumed for a remote PSE.
+	 */
+	pbap->supported_features = 0x00000003;
+
+	if (pbap->version < 0x0102)
+		return;
+
+	/* Supported Feature Bits */
+	data = obc_session_get_attribute(pbap->session,
+					SDP_ATTR_PBAP_SUPPORTED_FEATURES);
+	if (data)
+		pbap->supported_features = *(uint32_t *) data;
+
+}
+
+static void *pbap_supported_features(struct obc_session *session)
+{
+	const void *data;
+	uint16_t version;
+
+	/* Version */
+	data = obc_session_get_attribute(session, SDP_ATTR_PFILE_DESC_LIST);
+	if (!data)
+		return NULL;
+
+	version = GPOINTER_TO_UINT(data);
+
+	if (version < 0x0102)
+		return NULL;
+
+	/* Supported Feature Bits */
+	data = obc_session_get_attribute(session,
+					SDP_ATTR_PBAP_SUPPORTED_FEATURES);
+	if (!data)
+		return NULL;
+
+	return g_obex_apparam_set_uint32(NULL, SUPPORTED_FEATURES_TAG,
+						DOWNLOAD_FEATURE |
+						BROWSE_FEATURE |
+						DATABASEID_FEATURE |
+						FOLDER_VERSION_FEATURE |
+						VCARD_SELECTING_FEATURE |
+						ENHANCED_CALLS_FEATURE |
+						UCI_FEATURE |
+						UID_FEATURE |
+						REFERENCING_FEATURE |
+						DEFAULT_IMAGE_FEATURE);
+}
+
+static int pbap_probe(struct obc_session *session)
+{
+	struct pbap_data *pbap;
+	const char *path;
+
+	path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	pbap = g_try_new0(struct pbap_data, 1);
+	if (!pbap)
+		return -ENOMEM;
+
+	pbap->session = obc_session_ref(session);
+
+	parse_service_record(pbap);
+
+	DBG("%s, version 0x%04x supported features 0x%08x", path, pbap->version,
+						pbap->supported_features);
+
+	if (!g_dbus_register_interface(conn, path, PBAP_INTERFACE, pbap_methods,
+						NULL, pbap_properties, pbap,
+						pbap_free)) {
+		pbap_free(pbap);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void pbap_remove(struct obc_session *session)
+{
+	const char *path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	g_dbus_unregister_interface(conn, path, PBAP_INTERFACE);
+}
+
+static struct obc_driver pbap = {
+	.service = "PBAP",
+	.uuid = PBAP_UUID,
+	.target = OBEX_PBAP_UUID,
+	.target_len = OBEX_PBAP_UUID_LEN,
+	.supported_features = pbap_supported_features,
+	.probe = pbap_probe,
+	.remove = pbap_remove
+};
+
+int pbap_init(void)
+{
+	int err;
+
+	DBG("");
+
+	conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+	if (!conn)
+		return -EIO;
+
+	err = obc_driver_register(&pbap);
+	if (err < 0) {
+		dbus_connection_unref(conn);
+		conn = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+void pbap_exit(void)
+{
+	DBG("");
+
+	dbus_connection_unref(conn);
+	conn = NULL;
+
+	obc_driver_unregister(&pbap);
+}
diff --git a/bluez/repo/obexd/client/pbap.h b/bluez/repo/obexd/client/pbap.h
new file mode 100644
index 0000000..ce56258
--- /dev/null
+++ b/bluez/repo/obexd/client/pbap.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Intel Corporation
+ *  Copyright (C) 2007-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
+ *
+ */
+
+int pbap_init(void);
+void pbap_exit(void);
diff --git a/bluez/repo/obexd/client/session.c b/bluez/repo/obexd/client/session.c
new file mode 100644
index 0000000..ef998f9
--- /dev/null
+++ b/bluez/repo/obexd/client/session.c
@@ -0,0 +1,1410 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+#include "gobex/gobex.h"
+
+#include "obexd/src/log.h"
+#include "dbus.h"
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "transport.h"
+
+#define SESSION_INTERFACE "org.bluez.obex.Session1"
+#define ERROR_INTERFACE "org.bluez.obex.Error"
+#define SESSION_BASEPATH "/org/bluez/obex/client"
+
+#define OBEX_IO_ERROR obex_io_error_quark()
+#define OBEX_IO_ERROR_FIRST (0xff + 1)
+
+enum {
+	OBEX_IO_DISCONNECTED = OBEX_IO_ERROR_FIRST,
+	OBEX_IO_BUSY,
+};
+
+static guint64 counter = 0;
+
+struct callback_data {
+	struct obc_session *session;
+	guint id;
+	session_callback_t func;
+	void *data;
+};
+
+struct pending_request;
+typedef int (*session_process_t) (struct pending_request *p, GError **err);
+typedef void (*destroy_t) (void *data);
+
+struct pending_request {
+	guint id;
+	guint req_id;
+	struct obc_session *session;
+	session_process_t process;
+	struct obc_transfer *transfer;
+	session_callback_t func;
+	void *data;
+	destroy_t destroy;
+};
+
+struct setpath_data {
+	char **remaining;
+	int index;
+	session_callback_t func;
+	void *user_data;
+};
+
+struct file_data {
+	char *srcname;
+	char *destname;
+	session_callback_t func;
+	void *user_data;
+};
+
+struct obc_session {
+	guint id;
+	int refcount;
+	char *source;
+	char *destination;
+	uint8_t channel;
+	struct obc_transport *transport;
+	struct obc_driver *driver;
+	char *path;		/* Session path */
+	DBusConnection *conn;
+	GObex *obex;
+	struct pending_request *p;
+	char *owner;		/* Session owner */
+	guint watch;
+	GQueue *queue;
+	guint process_id;
+	char *folder;
+	struct callback_data *callback;
+};
+
+static GSList *sessions = NULL;
+
+static void session_process_queue(struct obc_session *session);
+static void session_terminate_transfer(struct obc_session *session,
+					struct obc_transfer *transfer,
+					GError *gerr);
+static void transfer_complete(struct obc_transfer *transfer,
+					GError *err, void *user_data);
+
+static GQuark obex_io_error_quark(void)
+{
+	return g_quark_from_static_string("obex-io-error-quark");
+}
+
+struct obc_session *obc_session_ref(struct obc_session *session)
+{
+	int refs = __sync_add_and_fetch(&session->refcount, 1);
+
+	DBG("%p: ref=%d", session, refs);
+
+	return session;
+}
+
+static void session_unregistered(struct obc_session *session)
+{
+	char *path;
+
+	if (session->driver && session->driver->remove)
+		session->driver->remove(session);
+
+	path = session->path;
+	session->path = NULL;
+
+	g_dbus_unregister_interface(session->conn, path, SESSION_INTERFACE);
+
+	DBG("Session(%p) unregistered %s", session, path);
+
+	g_free(path);
+}
+
+static struct pending_request *pending_request_new(struct obc_session *session,
+						session_process_t process,
+						struct obc_transfer *transfer,
+						session_callback_t func,
+						void *data,
+						destroy_t destroy)
+{
+	struct pending_request *p;
+	static guint id = 0;
+
+	p = g_new0(struct pending_request, 1);
+	p->id = ++id;
+	p->session = obc_session_ref(session);
+	p->process = process;
+	p->destroy = destroy;
+	p->transfer = transfer;
+	p->func = func;
+	p->data = data;
+
+	return p;
+}
+
+static void pending_request_free(struct pending_request *p)
+{
+	if (p->req_id > 0)
+		g_obex_cancel_req(p->session->obex, p->req_id, TRUE);
+
+	if (p->destroy)
+		p->destroy(p->data);
+
+	if (p->transfer)
+		obc_transfer_unregister(p->transfer);
+
+	if (p->session)
+		obc_session_unref(p->session);
+
+	g_free(p);
+}
+
+static void setpath_data_free(void *process_data)
+{
+	struct setpath_data *data = process_data;
+
+	g_strfreev(data->remaining);
+	g_free(data);
+}
+
+static void file_data_free(void *process_data)
+{
+	struct file_data *data = process_data;
+
+	g_free(data->srcname);
+	g_free(data->destname);
+	g_free(data);
+}
+
+static void session_free(struct obc_session *session)
+{
+	DBG("%p", session);
+
+	if (session->process_id != 0)
+		g_source_remove(session->process_id);
+
+	if (session->queue) {
+		g_queue_foreach(session->queue, (GFunc) pending_request_free,
+									NULL);
+		g_queue_free(session->queue);
+	}
+
+	if (session->watch)
+		g_dbus_remove_watch(session->conn, session->watch);
+
+	if (session->obex) {
+		g_obex_set_disconnect_function(session->obex, NULL, NULL);
+		g_obex_unref(session->obex);
+	}
+
+	if (session->id > 0 && session->transport != NULL)
+		session->transport->disconnect(session->id);
+
+	if (session->path)
+		session_unregistered(session);
+
+	if (session->conn)
+		dbus_connection_unref(session->conn);
+
+	if (session->p)
+		pending_request_free(session->p);
+
+	g_free(session->path);
+	g_free(session->owner);
+	g_free(session->source);
+	g_free(session->destination);
+	g_free(session->folder);
+	g_free(session);
+}
+
+static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp,
+							void *user_data)
+{
+	struct obc_session *session = user_data;
+
+	DBG("");
+
+	if (err)
+		error("%s", err->message);
+
+	/* Disconnect transport */
+	if (session->id > 0 && session->transport != NULL) {
+		session->transport->disconnect(session->id);
+		session->id = 0;
+	}
+
+	session_free(session);
+}
+
+void obc_session_unref(struct obc_session *session)
+{
+	int refs;
+
+	refs = __sync_sub_and_fetch(&session->refcount, 1);
+
+	DBG("%p: ref=%d", session, refs);
+
+	if (refs > 0)
+		return;
+
+	sessions = g_slist_remove(sessions, session);
+
+	if (!session->obex)
+		goto disconnect;
+
+	/* Wait OBEX Disconnect to complete if command succeed otherwise
+	 * proceed with transport disconnection since there is nothing else to
+	 * be done */
+	if (g_obex_disconnect(session->obex, disconnect_complete, session,
+									NULL))
+		return;
+
+disconnect:
+	/* Disconnect transport */
+	if (session->id > 0 && session->transport != NULL) {
+		session->transport->disconnect(session->id);
+		session->id = 0;
+	}
+
+	session_free(session);
+}
+
+static void callback_destroy(struct callback_data *callback, GError *err)
+{
+	struct obc_session *session = callback->session;
+
+	if (callback->id > 0)
+		g_obex_cancel_req(session->obex, callback->id, TRUE);
+
+	callback->func(session, NULL, err, callback->data);
+	g_free(callback);
+	session->callback = NULL;
+	obc_session_unref(session);
+}
+
+static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct callback_data *callback = user_data;
+	GError *gerr = NULL;
+	uint8_t rsp_code;
+
+	callback->id = 0;
+
+	if (err != NULL) {
+		error("connect_cb: %s", err->message);
+		gerr = g_error_copy(err);
+		goto done;
+	}
+
+	rsp_code = g_obex_packet_get_operation(rsp, NULL);
+	if (rsp_code != G_OBEX_RSP_SUCCESS)
+		gerr = g_error_new(OBEX_IO_ERROR, -EIO,
+				"OBEX Connect failed with 0x%02x", rsp_code);
+
+done:
+	callback_destroy(callback, gerr);
+	if (gerr != NULL)
+		g_error_free(gerr);
+}
+
+static void session_disconnected(GObex *obex, GError *err, gpointer user_data)
+{
+	struct obc_session *session = user_data;
+
+	if (err)
+		error("%s", err->message);
+
+	obc_session_shutdown(session);
+}
+
+static void transport_func(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct callback_data *callback = user_data;
+	struct obc_session *session = callback->session;
+	struct obc_driver *driver = session->driver;
+	struct obc_transport *transport = session->transport;
+	GObex *obex;
+	GObexApparam *apparam;
+	GObexTransportType type;
+	int tx_mtu = -1;
+	int rx_mtu = -1;
+
+	DBG("");
+
+	if (err != NULL) {
+		error("%s", err->message);
+		goto done;
+	}
+
+	g_io_channel_set_close_on_unref(io, FALSE);
+
+	if (transport->getpacketopt &&
+			transport->getpacketopt(io, &tx_mtu, &rx_mtu) == 0)
+		type = G_OBEX_TRANSPORT_PACKET;
+	else
+		type = G_OBEX_TRANSPORT_STREAM;
+
+	obex = g_obex_new(io, type, tx_mtu, rx_mtu);
+	if (obex == NULL)
+		goto done;
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	apparam = NULL;
+
+	if (driver->supported_features)
+		apparam = driver->supported_features(session);
+
+	if (apparam) {
+		uint8_t buf[1024];
+		ssize_t len;
+
+		len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+		if (driver->target)
+			callback->id = g_obex_connect(obex, connect_cb,
+					callback, &err,
+					G_OBEX_HDR_TARGET,
+					driver->target, driver->target_len,
+					G_OBEX_HDR_APPARAM,
+					buf, len,
+					G_OBEX_HDR_INVALID);
+		else
+			callback->id = g_obex_connect(obex, connect_cb,
+					callback, &err,
+					G_OBEX_HDR_APPARAM, buf, len,
+					G_OBEX_HDR_INVALID);
+		g_obex_apparam_free(apparam);
+	} else if (driver->target)
+		callback->id = g_obex_connect(obex, connect_cb, callback, &err,
+			G_OBEX_HDR_TARGET, driver->target, driver->target_len,
+			G_OBEX_HDR_INVALID);
+	else
+		callback->id = g_obex_connect(obex, connect_cb, callback,
+						&err, G_OBEX_HDR_INVALID);
+
+	if (err != NULL) {
+		error("%s", err->message);
+		g_obex_unref(obex);
+		goto done;
+	}
+
+	session->obex = obex;
+	sessions = g_slist_prepend(sessions, session);
+
+	g_obex_set_disconnect_function(obex, session_disconnected, session);
+
+	return;
+done:
+	callback_destroy(callback, err);
+}
+
+static void owner_disconnected(DBusConnection *connection, void *user_data)
+{
+	struct obc_session *session = user_data;
+	GError *err;
+
+	DBG("");
+
+	/*
+	 * If connection still connecting notify the callback to destroy the
+	 * session.
+	 */
+	if (session->callback) {
+		err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session closed by user");
+		callback_destroy(session->callback, err);
+		g_error_free(err);
+		return;
+	}
+
+	obc_session_shutdown(session);
+}
+
+int obc_session_set_owner(struct obc_session *session, const char *name,
+			GDBusWatchFunction func)
+{
+	if (session == NULL)
+		return -EINVAL;
+
+	if (session->watch)
+		g_dbus_remove_watch(session->conn, session->watch);
+
+	session->watch = g_dbus_add_disconnect_watch(session->conn, name, func,
+							session, NULL);
+	if (session->watch == 0)
+		return -EINVAL;
+
+	session->owner = g_strdup(name);
+
+	return 0;
+}
+
+
+static struct obc_session *session_find(const char *source,
+						const char *destination,
+						const char *service,
+						uint8_t channel,
+						const char *owner)
+{
+	GSList *l;
+
+	for (l = sessions; l; l = l->next) {
+		struct obc_session *session = l->data;
+
+		if (g_strcmp0(session->destination, destination))
+			continue;
+
+		if (g_strcmp0(service, session->driver->service))
+			continue;
+
+		if (source && g_strcmp0(session->source, source))
+			continue;
+
+		if (channel && session->channel != channel)
+			continue;
+
+		if (g_strcmp0(owner, session->owner))
+			continue;
+
+		return session;
+	}
+
+	return NULL;
+}
+
+static gboolean connection_complete(gpointer data)
+{
+	struct callback_data *cb = data;
+
+	cb->func(cb->session, NULL, NULL, cb->data);
+
+	obc_session_unref(cb->session);
+
+	g_free(cb);
+
+	return FALSE;
+}
+
+static int session_connect(struct obc_session *session,
+				session_callback_t function, void *user_data)
+{
+	struct callback_data *callback;
+	struct obc_transport *transport = session->transport;
+	struct obc_driver *driver = session->driver;
+
+	callback = g_try_malloc0(sizeof(*callback));
+	if (callback == NULL)
+		return -ENOMEM;
+
+	callback->func = function;
+	callback->data = user_data;
+	callback->session = obc_session_ref(session);
+
+	/* Connection completed */
+	if (session->obex) {
+		g_idle_add(connection_complete, callback);
+		return 0;
+	}
+
+	/* Ongoing connection */
+	if (session->id > 0) {
+		obc_session_unref(callback->session);
+		g_free(callback);
+		return 0;
+	}
+
+	session->id = transport->connect(session->source, session->destination,
+					driver->uuid, session->channel,
+					transport_func, callback);
+	if (session->id == 0) {
+		obc_session_unref(callback->session);
+		g_free(callback);
+		return -EINVAL;
+	}
+
+	session->callback = callback;
+
+	return 0;
+}
+
+struct obc_session *obc_session_create(const char *source,
+						const char *destination,
+						const char *service,
+						uint8_t channel,
+						const char *owner,
+						session_callback_t function,
+						void *user_data)
+{
+	DBusConnection *conn;
+	struct obc_session *session;
+	struct obc_transport *transport;
+	struct obc_driver *driver;
+
+	if (destination == NULL)
+		return NULL;
+
+	session = session_find(source, destination, service, channel, owner);
+	if (session != NULL)
+		goto proceed;
+
+	/* FIXME: Do proper transport lookup when the API supports it */
+	transport = obc_transport_find("Bluetooth");
+	if (transport == NULL)
+		return NULL;
+
+	driver = obc_driver_find(service);
+	if (driver == NULL)
+		return NULL;
+
+	conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+	if (conn == NULL)
+		return NULL;
+
+	session = g_try_malloc0(sizeof(*session));
+	if (session == NULL)
+		return NULL;
+
+	session->refcount = 1;
+	session->transport = transport;
+	session->driver = driver;
+	session->conn = conn;
+	session->source = g_strdup(source);
+	session->destination = g_strdup(destination);
+	session->channel = channel;
+	session->queue = g_queue_new();
+	session->folder = g_strdup("/");
+
+	if (owner)
+		obc_session_set_owner(session, owner, owner_disconnected);
+
+proceed:
+	if (session_connect(session, function, user_data) < 0) {
+		obc_session_unref(session);
+		return NULL;
+	}
+
+	DBG("session %p transport %s driver %s", session,
+			session->transport->name, session->driver->service);
+
+	return session;
+}
+
+void obc_session_shutdown(struct obc_session *session)
+{
+	struct pending_request *p;
+	GError *err;
+
+	DBG("%p", session);
+
+	obc_session_ref(session);
+
+	/* Unregister any pending transfer */
+	err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session closed by user");
+
+	if (session->p != NULL && session->p->id != 0) {
+		p = session->p;
+		session->p = NULL;
+
+		if (p->func)
+			p->func(session, p->transfer, err, p->data);
+
+		pending_request_free(p);
+	}
+
+	while ((p = g_queue_pop_head(session->queue))) {
+		if (p->func)
+			p->func(session, p->transfer, err, p->data);
+
+		pending_request_free(p);
+	}
+
+	g_error_free(err);
+
+	/* Unregister interfaces */
+	if (session->path)
+		session_unregistered(session);
+
+	obc_session_unref(session);
+}
+
+static void capabilities_complete_callback(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	DBusMessage *message = user_data;
+	char *contents;
+	size_t size;
+	int perr;
+
+	if (err != NULL) {
+		DBusMessage *error = g_dbus_create_error(message,
+					ERROR_INTERFACE ".Failed",
+					"%s", err->message);
+		g_dbus_send_message(session->conn, error);
+		goto done;
+	}
+
+	perr = obc_transfer_get_contents(transfer, &contents, &size);
+	if (perr < 0) {
+		DBusMessage *error = g_dbus_create_error(message,
+						ERROR_INTERFACE ".Failed",
+						"Error reading contents: %s",
+						strerror(-perr));
+		g_dbus_send_message(session->conn, error);
+		goto done;
+	}
+
+	g_dbus_send_reply(session->conn, message,
+						DBUS_TYPE_STRING, &contents,
+						DBUS_TYPE_INVALID);
+	g_free(contents);
+
+done:
+	dbus_message_unref(message);
+}
+
+static DBusMessage *get_capabilities(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct obc_session *session = user_data;
+	struct obc_transfer *pull;
+	DBusMessage *reply;
+	GError *gerr = NULL;
+
+	pull = obc_transfer_get("x-obex/capability", NULL, NULL, &gerr);
+	if (pull == NULL)
+		goto fail;
+
+	if (!obc_session_queue(session, pull, capabilities_complete_callback,
+								message, &gerr))
+		goto fail;
+
+	dbus_message_ref(message);
+
+	return NULL;
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
+								gerr->message);
+	g_error_free(gerr);
+	return reply;
+
+}
+
+static gboolean get_source(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_session *session = data;
+
+	if (session->source == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&session->source);
+
+	return TRUE;
+}
+
+static gboolean source_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct obc_session *session = data;
+
+	return session->source != NULL;
+}
+
+static gboolean get_destination(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_session *session = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&session->destination);
+
+	return TRUE;
+}
+
+static gboolean get_channel(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_session *session = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+							&session->channel);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable session_methods[] = {
+	{ GDBUS_ASYNC_METHOD("GetCapabilities",
+				NULL, GDBUS_ARGS({ "capabilities", "s" }),
+				get_capabilities) },
+	{ }
+};
+
+static gboolean get_target(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_session *session = data;
+
+	if (session->driver->uuid == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+						&session->driver->uuid);
+
+	return TRUE;
+}
+
+static gboolean target_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct obc_session *session = data;
+
+	return session->driver->uuid != NULL;
+}
+
+static const GDBusPropertyTable session_properties[] = {
+	{ "Source", "s", get_source, NULL, source_exists },
+	{ "Destination", "s", get_destination },
+	{ "Channel", "y", get_channel },
+	{ "Target", "s", get_target, NULL, target_exists },
+	{ }
+};
+
+static gboolean session_process(gpointer data)
+{
+	struct obc_session *session = data;
+
+	session->process_id = 0;
+
+	session_process_queue(session);
+
+	return FALSE;
+}
+
+static void session_queue(struct pending_request *p)
+{
+	g_queue_push_tail(p->session->queue, p);
+
+	if (p->session->process_id == 0)
+		p->session->process_id = g_idle_add(session_process,
+								p->session);
+}
+
+static int session_process_transfer(struct pending_request *p, GError **err)
+{
+	if (!obc_transfer_start(p->transfer, p->session->obex, err))
+		return -1;
+
+	DBG("Tranfer(%p) started", p->transfer);
+	p->session->p = p;
+	return 0;
+}
+
+guint obc_session_queue(struct obc_session *session,
+				struct obc_transfer *transfer,
+				session_callback_t func, void *user_data,
+				GError **err)
+{
+	struct pending_request *p;
+
+	if (session->obex == NULL) {
+		obc_transfer_unregister(transfer);
+		g_set_error(err, OBEX_IO_ERROR, -ENOTCONN,
+						"Session not connected");
+		return 0;
+	}
+
+	if (!obc_transfer_register(transfer, session->conn, session->path,
+							session->owner, err)) {
+		obc_transfer_unregister(transfer);
+		return 0;
+	}
+
+	obc_transfer_set_callback(transfer, transfer_complete, session);
+
+	p = pending_request_new(session, session_process_transfer, transfer,
+							func, user_data, NULL);
+	session_queue(p);
+	return p->id;
+}
+
+static void session_process_queue(struct obc_session *session)
+{
+	struct pending_request *p;
+
+	if (session->p != NULL)
+		return;
+
+	if (session->queue == NULL || g_queue_is_empty(session->queue))
+		return;
+
+	obc_session_ref(session);
+
+	while ((p = g_queue_pop_head(session->queue))) {
+		GError *gerr = NULL;
+
+		if (p->process(p, &gerr) == 0)
+			break;
+
+		if (p->func)
+			p->func(session, p->transfer, gerr, p->data);
+
+		g_clear_error(&gerr);
+
+		pending_request_free(p);
+	}
+
+	obc_session_unref(session);
+}
+
+static int pending_transfer_cmptransfer(gconstpointer a, gconstpointer b)
+{
+	const struct pending_request *p = a;
+	const struct obc_transfer *transfer = b;
+
+	if (p->transfer == transfer)
+		return 0;
+
+	return -1;
+}
+
+static void session_terminate_transfer(struct obc_session *session,
+					struct obc_transfer *transfer,
+					GError *gerr)
+{
+	struct pending_request *p = session->p;
+
+	if (p == NULL || p->transfer != transfer) {
+		GList *match;
+
+		match = g_list_find_custom(session->queue->head, transfer,
+						pending_transfer_cmptransfer);
+		if (match == NULL)
+			return;
+
+		p = match->data;
+		g_queue_delete_link(session->queue, match);
+	} else
+		session->p = NULL;
+
+	obc_session_ref(session);
+
+	if (p->func)
+		p->func(session, p->transfer, gerr, p->data);
+
+	pending_request_free(p);
+
+	if (session->p == NULL)
+		session_process_queue(session);
+
+	obc_session_unref(session);
+}
+
+static void session_notify_complete(struct obc_session *session,
+				struct obc_transfer *transfer)
+{
+	DBG("Transfer(%p) complete", transfer);
+
+	session_terminate_transfer(session, transfer, NULL);
+}
+
+static void session_notify_error(struct obc_session *session,
+				struct obc_transfer *transfer,
+				GError *err)
+{
+	error("Transfer(%p) Error: %s", transfer, err->message);
+
+	session_terminate_transfer(session, transfer, err);
+}
+
+static void transfer_complete(struct obc_transfer *transfer,
+					GError *err, void *user_data)
+{
+	struct obc_session *session = user_data;
+
+	if (err != 0)
+		goto fail;
+
+	session_notify_complete(session, transfer);
+
+	return;
+
+fail:
+	session_notify_error(session, transfer, err);
+}
+
+const char *obc_session_register(struct obc_session *session,
+						GDBusDestroyFunction destroy)
+{
+	if (session->path)
+		return session->path;
+
+	session->path = g_strdup_printf("%s/session%ju",
+						SESSION_BASEPATH, counter++);
+
+	if (g_dbus_register_interface(session->conn, session->path,
+					SESSION_INTERFACE, session_methods,
+					NULL, session_properties, session,
+					destroy) == FALSE)
+		goto fail;
+
+	if (session->driver->probe && session->driver->probe(session) < 0) {
+		g_dbus_unregister_interface(session->conn, session->path,
+							SESSION_INTERFACE);
+		goto fail;
+	}
+
+	DBG("Session(%p) registered %s", session, session->path);
+
+	return session->path;
+
+fail:
+	g_free(session->path);
+	session->path = NULL;
+	return NULL;
+}
+
+const void *obc_session_get_attribute(struct obc_session *session,
+							int attribute_id)
+{
+	if (session == NULL || session->id == 0)
+		return NULL;
+
+	return session->transport->getattribute(session->id, attribute_id);
+}
+
+const char *obc_session_get_owner(struct obc_session *session)
+{
+	if (session == NULL)
+		return NULL;
+
+	return session->owner;
+}
+
+const char *obc_session_get_destination(struct obc_session *session)
+{
+	return session->destination;
+}
+
+const char *obc_session_get_path(struct obc_session *session)
+{
+	return session->path;
+}
+
+const char *obc_session_get_target(struct obc_session *session)
+{
+	return session->driver->target;
+}
+
+const char *obc_session_get_folder(struct obc_session *session)
+{
+	return session->folder;
+}
+
+static void setpath_complete(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct pending_request *p = user_data;
+
+	if (p->func)
+		p->func(session, NULL, err, p->data);
+
+	if (session->p == p)
+		session->p = NULL;
+
+	pending_request_free(p);
+
+	session_process_queue(session);
+}
+
+static void setpath_op_complete(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct setpath_data *data = user_data;
+
+	if (data->func)
+		data->func(session, NULL, err, data->user_data);
+}
+
+static void setpath_set_folder(struct obc_session *session, const char *cur)
+{
+	char *folder = NULL;
+	const char *delim;
+
+	delim = strrchr(session->folder, '/');
+	if (strlen(cur) == 0 || delim == NULL ||
+			(strcmp(cur, "..") == 0 && delim == session->folder)) {
+		folder = g_strdup("/");
+	} else {
+		if (strcmp(cur, "..") == 0) {
+			folder = g_strndup(session->folder,
+						delim - session->folder);
+		} else {
+			if (g_str_has_suffix(session->folder, "/"))
+				folder = g_strconcat(session->folder,
+								cur, NULL);
+			else
+				folder = g_strconcat(session->folder, "/",
+								cur, NULL);
+		}
+	}
+	g_free(session->folder);
+	session->folder = folder;
+}
+
+static void setpath_cb(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct pending_request *p = user_data;
+	struct setpath_data *data = p->data;
+	char *next;
+	char *current;
+	guint8 code;
+
+	p->req_id = 0;
+
+	if (err != NULL) {
+		setpath_complete(p->session, NULL, err, user_data);
+		return;
+	}
+
+	code = g_obex_packet_get_operation(rsp, NULL);
+	if (code != G_OBEX_RSP_SUCCESS) {
+		GError *gerr = NULL;
+		g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
+							g_obex_strerror(code));
+		setpath_complete(p->session, NULL, gerr, user_data);
+		g_clear_error(&gerr);
+		return;
+	}
+
+	current = data->remaining[data->index - 1];
+	setpath_set_folder(p->session, current);
+
+	/* Ignore empty folder names to avoid resetting the current path */
+	while ((next = data->remaining[data->index]) && strlen(next) == 0)
+		data->index++;
+
+	if (next == NULL) {
+		setpath_complete(p->session, NULL, NULL, user_data);
+		return;
+	}
+
+	data->index++;
+
+	p->req_id = g_obex_setpath(obex, next, setpath_cb, p, &err);
+	if (err != NULL) {
+		setpath_complete(p->session, NULL, err, user_data);
+		g_error_free(err);
+	}
+}
+
+static int session_process_setpath(struct pending_request *p, GError **err)
+{
+	struct setpath_data *req = p->data;
+	const char *first = "";
+
+	/* Relative path */
+	if (req->remaining[0][0] != '/')
+		first = req->remaining[req->index];
+	req->index++;
+
+	p->req_id = g_obex_setpath(p->session->obex, first, setpath_cb, p, err);
+	if (*err != NULL)
+		return (*err)->code;
+
+	p->session->p = p;
+
+	return 0;
+}
+
+guint obc_session_setpath(struct obc_session *session, const char *path,
+				session_callback_t func, void *user_data,
+				GError **err)
+{
+	struct setpath_data *data;
+	struct pending_request *p;
+
+	if (session->obex == NULL) {
+		g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session disconnected");
+		return 0;
+	}
+
+	data = g_new0(struct setpath_data, 1);
+	data->func = func;
+	data->user_data = user_data;
+	data->remaining = g_strsplit(strlen(path) ? path : "/", "/", 0);
+
+	if (!data->remaining || !data->remaining[0]) {
+		error("obc_session_setpath: invalid path %s", path);
+		g_set_error(err, OBEX_IO_ERROR, -EINVAL, "Invalid argument");
+		return 0;
+	}
+
+	p = pending_request_new(session, session_process_setpath, NULL,
+				setpath_op_complete, data, setpath_data_free);
+	session_queue(p);
+	return p->id;
+}
+
+static void async_cb(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct pending_request *p = user_data;
+	struct obc_session *session = p->session;
+	GError *gerr = NULL;
+	uint8_t code;
+
+	p->req_id = 0;
+
+	if (err != NULL) {
+		if (p->func)
+			p->func(p->session, NULL, err, p->data);
+		goto done;
+	}
+
+	code = g_obex_packet_get_operation(rsp, NULL);
+	if (code != G_OBEX_RSP_SUCCESS)
+		g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
+							g_obex_strerror(code));
+
+	if (p->func)
+		p->func(p->session, NULL, gerr, p->data);
+
+	if (gerr != NULL)
+		g_clear_error(&gerr);
+
+done:
+	pending_request_free(p);
+	session->p = NULL;
+
+	session_process_queue(session);
+}
+
+static void file_op_complete(struct obc_session *session,
+						struct obc_transfer *transfer,
+						GError *err, void *user_data)
+{
+	struct file_data *data = user_data;
+
+	if (data->func)
+		data->func(session, NULL, err, data->user_data);
+}
+
+static int session_process_mkdir(struct pending_request *p, GError **err)
+{
+	struct file_data *req = p->data;
+
+	p->req_id = g_obex_mkdir(p->session->obex, req->srcname, async_cb, p,
+									err);
+	if (*err != NULL)
+		return (*err)->code;
+
+	p->session->p = p;
+
+	return 0;
+}
+
+guint obc_session_mkdir(struct obc_session *session, const char *folder,
+				session_callback_t func, void *user_data,
+				GError **err)
+{
+	struct file_data *data;
+	struct pending_request *p;
+
+	if (session->obex == NULL) {
+		g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session disconnected");
+		return 0;
+	}
+
+	data = g_new0(struct file_data, 1);
+	data->srcname = g_strdup(folder);
+	data->func = func;
+	data->user_data = user_data;
+
+	p = pending_request_new(session, session_process_mkdir, NULL,
+					file_op_complete, data, file_data_free);
+	session_queue(p);
+	return p->id;
+}
+
+static int session_process_copy(struct pending_request *p, GError **err)
+{
+	struct file_data *req = p->data;
+
+	p->req_id = g_obex_copy(p->session->obex, req->srcname, req->destname,
+							async_cb, p, err);
+	if (*err != NULL)
+		return (*err)->code;
+
+	p->session->p = p;
+
+	return 0;
+}
+
+guint obc_session_copy(struct obc_session *session, const char *srcname,
+				const char *destname, session_callback_t func,
+				void *user_data, GError **err)
+{
+	struct file_data *data;
+	struct pending_request *p;
+
+	if (session->obex == NULL) {
+		g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session disconnected");
+		return 0;
+	}
+
+	data = g_new0(struct file_data, 1);
+	data->srcname = g_strdup(srcname);
+	data->destname = g_strdup(destname);
+	data->func = func;
+	data->user_data = user_data;
+
+	p = pending_request_new(session, session_process_copy, NULL,
+				file_op_complete, data, file_data_free);
+	session_queue(p);
+	return p->id;
+}
+
+static int session_process_move(struct pending_request *p, GError **err)
+{
+	struct file_data *req = p->data;
+
+	p->req_id = g_obex_move(p->session->obex, req->srcname, req->destname,
+							async_cb, p, err);
+	if (*err != NULL)
+		return (*err)->code;
+
+	p->session->p = p;
+
+	return 0;
+}
+
+guint obc_session_move(struct obc_session *session, const char *srcname,
+				const char *destname, session_callback_t func,
+				void *user_data, GError **err)
+{
+	struct file_data *data;
+	struct pending_request *p;
+
+	if (session->obex == NULL) {
+		g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session disconnected");
+		return 0;
+	}
+
+	data = g_new0(struct file_data, 1);
+	data->srcname = g_strdup(srcname);
+	data->destname = g_strdup(destname);
+	data->func = func;
+	data->user_data = user_data;
+
+	p = pending_request_new(session, session_process_move, NULL,
+				file_op_complete, data, file_data_free);
+	session_queue(p);
+	return p->id;
+}
+
+static int session_process_delete(struct pending_request *p, GError **err)
+{
+	struct file_data *req = p->data;
+
+	p->req_id = g_obex_delete(p->session->obex, req->srcname, async_cb, p,
+									err);
+	if (*err != NULL)
+		return (*err)->code;
+
+	p->session->p = p;
+
+	return 0;
+}
+
+guint obc_session_delete(struct obc_session *session, const char *file,
+				session_callback_t func, void *user_data,
+				GError **err)
+{
+	struct file_data *data;
+	struct pending_request *p;
+
+	if (session->obex == NULL) {
+		g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
+						"Session disconnected");
+		return 0;
+	}
+
+	data = g_new0(struct file_data, 1);
+	data->srcname = g_strdup(file);
+	data->func = func;
+	data->user_data = user_data;
+
+	p = pending_request_new(session, session_process_delete, NULL,
+				file_op_complete, data, file_data_free);
+	session_queue(p);
+	return p->id;
+}
+
+void obc_session_cancel(struct obc_session *session, guint id,
+							gboolean remove)
+{
+	struct pending_request *p = session->p;
+
+	if (p == NULL || p->id != id)
+		return;
+
+	if (p->req_id == 0)
+		return;
+
+	g_obex_cancel_req(session->obex, p->req_id, remove);
+	p->req_id = 0;
+
+	if (!remove)
+		return;
+
+	pending_request_free(p);
+	session->p = NULL;
+
+	session_process_queue(session);
+}
diff --git a/bluez/repo/obexd/client/session.h b/bluez/repo/obexd/client/session.h
new file mode 100644
index 0000000..b561b7e
--- /dev/null
+++ b/bluez/repo/obexd/client/session.h
@@ -0,0 +1,82 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <glib.h>
+
+struct obc_session;
+
+typedef void (*session_callback_t) (struct obc_session *session,
+					struct obc_transfer *transfer,
+					GError *err, void *user_data);
+
+struct obc_session *obc_session_create(const char *source,
+						const char *destination,
+						const char *service,
+						uint8_t channel,
+						const char *owner,
+						session_callback_t function,
+						void *user_data);
+
+struct obc_session *obc_session_ref(struct obc_session *session);
+void obc_session_unref(struct obc_session *session);
+void obc_session_shutdown(struct obc_session *session);
+
+int obc_session_set_owner(struct obc_session *session, const char *name,
+			GDBusWatchFunction func);
+const char *obc_session_get_owner(struct obc_session *session);
+
+const char *obc_session_get_destination(struct obc_session *session);
+const char *obc_session_get_path(struct obc_session *session);
+const char *obc_session_get_target(struct obc_session *session);
+
+const char *obc_session_register(struct obc_session *session,
+						GDBusDestroyFunction destroy);
+
+const void *obc_session_get_attribute(struct obc_session *session,
+							int attribute_id);
+
+const char *obc_session_get_folder(struct obc_session *session);
+
+guint obc_session_queue(struct obc_session *session,
+				struct obc_transfer *transfer,
+				session_callback_t func, void *user_data,
+				GError **err);
+guint obc_session_setpath(struct obc_session *session, const char *path,
+				session_callback_t func, void *user_data,
+				GError **err);
+guint obc_session_mkdir(struct obc_session *session, const char *folder,
+				session_callback_t func, void *user_data,
+				GError **err);
+guint obc_session_copy(struct obc_session *session, const char *srcname,
+				const char *destname, session_callback_t func,
+				void *user_data, GError **err);
+guint obc_session_move(struct obc_session *session, const char *srcname,
+				const char *destname, session_callback_t func,
+				void *user_data, GError **err);
+guint obc_session_delete(struct obc_session *session, const char *file,
+				session_callback_t func, void *user_data,
+				GError **err);
+void obc_session_cancel(struct obc_session *session, guint id,
+							gboolean remove);
diff --git a/bluez/repo/obexd/client/sync.c b/bluez/repo/obexd/client/sync.c
new file mode 100644
index 0000000..548c318
--- /dev/null
+++ b/bluez/repo/obexd/client/sync.c
@@ -0,0 +1,262 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "sync.h"
+
+#define OBEX_SYNC_UUID "IRMC-SYNC"
+#define OBEX_SYNC_UUID_LEN 9
+
+#define SYNC_INTERFACE "org.bluez.obex.Synchronization1"
+#define ERROR_INF SYNC_INTERFACE ".Error"
+#define SYNC_UUID "00001104-0000-1000-8000-00805f9b34fb"
+
+struct sync_data {
+	struct obc_session *session;
+	char *phonebook_path;
+	DBusMessage *msg;
+};
+
+static DBusConnection *conn = NULL;
+
+static DBusMessage *sync_setlocation(DBusConnection *connection,
+			DBusMessage *message, void *user_data)
+{
+	struct sync_data *sync = user_data;
+	const char *location;
+	char *path = NULL, *tmp;
+
+	if (dbus_message_get_args(message, NULL,
+			DBUS_TYPE_STRING, &location,
+			DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+			ERROR_INF ".InvalidArguments", NULL);
+
+	if (!g_ascii_strcasecmp(location, "int") ||
+			!g_ascii_strcasecmp(location, "internal"))
+		path = g_strdup("telecom/pb.vcf");
+	else if (!g_ascii_strncasecmp(location, "sim", 3)) {
+		tmp = g_ascii_strup(location, 4);
+		path = g_build_filename(tmp, "telecom/pb.vcf", NULL);
+		g_free(tmp);
+	} else
+		return g_dbus_create_error(message,
+			ERROR_INF ".InvalidArguments", "InvalidPhonebook");
+
+	g_free(sync->phonebook_path);
+	sync->phonebook_path = path;
+
+	return dbus_message_new_method_return(message);
+}
+
+static DBusMessage *sync_getphonebook(DBusConnection *connection,
+			DBusMessage *message, void *user_data)
+{
+	struct sync_data *sync = user_data;
+	struct obc_transfer *transfer;
+	const char *target_file;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	if (dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &target_file,
+					DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INF ".InvalidArguments",
+				"Invalid arguments in method call");
+
+	if (sync->msg)
+		return g_dbus_create_error(message,
+			ERROR_INF ".InProgress", "Transfer in progress");
+
+	/* set default phonebook_path to memory internal phonebook */
+	if (!sync->phonebook_path)
+		sync->phonebook_path = g_strdup("telecom/pb.vcf");
+
+	transfer = obc_transfer_get("phonebook", sync->phonebook_path,
+							target_file, &err);
+	if (transfer == NULL)
+		goto fail;
+
+	if (!obc_session_queue(sync->session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static DBusMessage *sync_putphonebook(DBusConnection *connection,
+			DBusMessage *message, void *user_data)
+{
+	struct sync_data *sync = user_data;
+	struct obc_transfer *transfer;
+	const char *source_file;
+	GError *err = NULL;
+	DBusMessage *reply;
+
+	if (dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &source_file,
+					DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				ERROR_INF ".InvalidArguments",
+				"Invalid arguments in method call");
+
+	/* set default phonebook_path to memory internal phonebook */
+	if (!sync->phonebook_path)
+		sync->phonebook_path = g_strdup("telecom/pb.vcf");
+
+	transfer = obc_transfer_put(NULL, sync->phonebook_path, source_file,
+							NULL, 0, &err);
+	if (transfer == NULL)
+		goto fail;
+
+	if (!obc_session_queue(sync->session, transfer, NULL, NULL, &err))
+		goto fail;
+
+	return obc_transfer_create_dbus_reply(transfer, message);
+
+fail:
+	reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s",
+								err->message);
+	g_error_free(err);
+	return reply;
+}
+
+static const GDBusMethodTable sync_methods[] = {
+	{ GDBUS_METHOD("SetLocation",
+			GDBUS_ARGS({ "location", "s" }), NULL,
+			sync_setlocation) },
+	{ GDBUS_METHOD("GetPhonebook",
+			GDBUS_ARGS({ "targetfile", "s" }),
+			GDBUS_ARGS({ "transfer", "o" },
+					{ "properties", "a{sv}" }),
+			sync_getphonebook) },
+	{ GDBUS_METHOD("PutPhonebook",
+			GDBUS_ARGS({ "sourcefile", "s" }),
+			GDBUS_ARGS({ "transfer", "o" },
+					{ "properties", "a{sv}" }),
+			sync_putphonebook) },
+	{ }
+};
+
+static void sync_free(void *data)
+{
+	struct sync_data *sync = data;
+
+	obc_session_unref(sync->session);
+	g_free(sync->phonebook_path);
+	g_free(sync);
+}
+
+static int sync_probe(struct obc_session *session)
+{
+	struct sync_data *sync;
+	const char *path;
+
+	path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	sync = g_try_new0(struct sync_data, 1);
+	if (!sync)
+		return -ENOMEM;
+
+	sync->session = obc_session_ref(session);
+
+	if (!g_dbus_register_interface(conn, path, SYNC_INTERFACE, sync_methods,
+						NULL, NULL, sync, sync_free)) {
+		sync_free(sync);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void sync_remove(struct obc_session *session)
+{
+	const char *path = obc_session_get_path(session);
+
+	DBG("%s", path);
+
+	g_dbus_unregister_interface(conn, path, SYNC_INTERFACE);
+}
+
+static struct obc_driver sync = {
+	.service = "SYNC",
+	.uuid = SYNC_UUID,
+	.target = OBEX_SYNC_UUID,
+	.target_len = OBEX_SYNC_UUID_LEN,
+	.probe = sync_probe,
+	.remove = sync_remove
+};
+
+int sync_init(void)
+{
+	int err;
+
+	DBG("");
+
+	conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+	if (!conn)
+		return -EIO;
+
+	err = obc_driver_register(&sync);
+	if (err < 0) {
+		dbus_connection_unref(conn);
+		conn = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+void sync_exit(void)
+{
+	DBG("");
+
+	dbus_connection_unref(conn);
+	conn = NULL;
+
+	obc_driver_unregister(&sync);
+}
diff --git a/bluez/repo/obexd/client/sync.h b/bluez/repo/obexd/client/sync.h
new file mode 100644
index 0000000..8adc5f8
--- /dev/null
+++ b/bluez/repo/obexd/client/sync.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Intel Corporation
+ *  Copyright (C) 2007-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
+ *
+ */
+
+int sync_init(void);
+void sync_exit(void);
diff --git a/bluez/repo/obexd/client/transfer.c b/bluez/repo/obexd/client/transfer.c
new file mode 100644
index 0000000..092e72f
--- /dev/null
+++ b/bluez/repo/obexd/client/transfer.c
@@ -0,0 +1,981 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+#include "gobex/gobex.h"
+
+#include "obexd/src/log.h"
+#include "dbus.h"
+#include "transfer.h"
+
+#define TRANSFER_INTERFACE "org.bluez.obex.Transfer1"
+#define ERROR_INTERFACE "org.bluez.obex.Error"
+
+#define OBC_TRANSFER_ERROR obc_transfer_error_quark()
+
+#define FIRST_PACKET_TIMEOUT 60
+
+static guint64 counter = 0;
+
+struct transfer_callback {
+	transfer_callback_t func;
+	void *data;
+};
+
+enum {
+	TRANSFER_STATUS_QUEUED = 0,
+	TRANSFER_STATUS_ACTIVE,
+	TRANSFER_STATUS_SUSPENDED,
+	TRANSFER_STATUS_COMPLETE,
+	TRANSFER_STATUS_SUSPENDED_QUEUED,
+	TRANSFER_STATUS_ERROR
+};
+
+struct obc_transfer {
+	GObex *obex;
+	uint8_t status;
+	GObexApparam *apparam;
+	guint8 op;
+	struct transfer_callback *callback;
+	DBusConnection *conn;
+	DBusMessage *msg;
+	char *session;		/* Session path */
+	char *owner;		/* Transfer initiator */
+	char *path;		/* Transfer path */
+	char *filename;		/* Transfer file location */
+	char *name;		/* Transfer object name */
+	char *type;		/* Transfer object type */
+	int fd;
+	guint req;
+	guint xfer;
+	gint64 size;
+	gint64 transferred;
+	gint64 progress;
+	guint progress_id;
+};
+
+static GQuark obc_transfer_error_quark(void)
+{
+	return g_quark_from_static_string("obc-transfer-error-quark");
+}
+
+DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer,
+							DBusMessage *message)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+
+	reply = dbus_message_new_method_return(message);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+							&transfer->path);
+	g_dbus_get_properties(transfer->conn, transfer->path,
+						TRANSFER_INTERFACE, &iter);
+
+	return reply;
+}
+
+static void abort_complete(GObex *obex, GError *err, gpointer user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	struct transfer_callback *callback = transfer->callback;
+	DBusMessage *reply;
+
+	transfer->xfer = 0;
+
+	reply = dbus_message_new_method_return(transfer->msg);
+	if (reply)
+		g_dbus_send_message(transfer->conn, reply);
+
+	dbus_message_unref(transfer->msg);
+	transfer->msg = NULL;
+
+	if (callback == NULL)
+		return;
+
+	if (err) {
+		callback->func(transfer, err, callback->data);
+	} else {
+		GError *abort_err;
+
+		abort_err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
+						"Transfer cancelled by user");
+		callback->func(transfer, abort_err, callback->data);
+		g_error_free(abort_err);
+	}
+}
+
+static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	const char *sender;
+
+	sender = dbus_message_get_sender(message);
+	if (g_strcmp0(transfer->owner, sender) != 0)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotAuthorized",
+				"Not Authorized");
+
+	if (transfer->msg != NULL)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".InProgress",
+				"Cancellation already in progress");
+
+	if (transfer->status == TRANSFER_STATUS_SUSPENDED)
+		g_obex_resume(transfer->obex);
+
+	if (transfer->req > 0) {
+		if (!g_obex_cancel_req(transfer->obex, transfer->req, TRUE))
+			return g_dbus_create_error(message,
+						ERROR_INTERFACE ".Failed",
+						"Failed");
+		transfer->req = 0;
+	}
+
+	if (transfer->xfer == 0) {
+		struct transfer_callback *callback = transfer->callback;
+
+		if (callback != NULL) {
+			GError *err;
+
+			err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
+						"Transfer cancelled by user");
+			callback->func(transfer, err, callback->data);
+			g_error_free(err);
+		}
+
+		return dbus_message_new_method_return(message);
+	}
+
+	if (transfer->progress_id != 0) {
+		g_source_remove(transfer->progress_id);
+		transfer->progress_id = 0;
+	}
+
+	if (!g_obex_cancel_transfer(transfer->xfer, abort_complete, transfer))
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".Failed",
+				"Failed");
+
+	transfer->msg = dbus_message_ref(message);
+
+	return NULL;
+}
+
+static void transfer_set_status(struct obc_transfer *transfer, uint8_t status)
+{
+	if (transfer->status == status)
+		return;
+
+	transfer->status = status;
+
+	g_dbus_emit_property_changed(transfer->conn, transfer->path,
+					TRANSFER_INTERFACE, "Status");
+}
+
+static DBusMessage *obc_transfer_suspend(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	const char *sender;
+	uint8_t status;
+
+	sender = dbus_message_get_sender(message);
+	if (g_strcmp0(transfer->owner, sender) != 0)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotAuthorized",
+				"Not Authorized");
+
+	switch (transfer->status) {
+	case TRANSFER_STATUS_QUEUED:
+		status = TRANSFER_STATUS_SUSPENDED_QUEUED;
+		break;
+	case TRANSFER_STATUS_ACTIVE:
+		if (transfer->xfer)
+			g_obex_suspend(transfer->obex);
+		status = TRANSFER_STATUS_SUSPENDED;
+		break;
+	default:
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotInProgress",
+				"Not in progress");
+	}
+
+	transfer_set_status(transfer, status);
+
+	return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *obc_transfer_resume(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	const char *sender;
+	uint8_t status;
+
+	sender = dbus_message_get_sender(message);
+	if (g_strcmp0(transfer->owner, sender) != 0)
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotAuthorized",
+				"Not Authorized");
+
+	switch (transfer->status) {
+	case TRANSFER_STATUS_SUSPENDED_QUEUED:
+		status = TRANSFER_STATUS_QUEUED;
+		break;
+	case TRANSFER_STATUS_SUSPENDED:
+		if (transfer->xfer)
+			g_obex_resume(transfer->obex);
+		else
+			obc_transfer_start(transfer, NULL, NULL);
+		status = TRANSFER_STATUS_ACTIVE;
+		break;
+	default:
+		return g_dbus_create_error(message,
+				ERROR_INTERFACE ".NotInProgress",
+				"Not in progress");
+	}
+
+	transfer_set_status(transfer, status);
+
+	return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
+}
+
+static gboolean name_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	return transfer->name != NULL;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	if (transfer->name == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&transfer->name);
+
+	return TRUE;
+}
+
+static gboolean get_size(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
+							&transfer->size);
+
+	return TRUE;
+}
+
+static gboolean filename_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	return transfer->filename != NULL;
+}
+
+static gboolean get_filename(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	if (transfer->filename == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&transfer->filename);
+
+	return TRUE;
+}
+
+static gboolean transferred_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	return transfer->obex != NULL;
+}
+
+static gboolean get_transferred(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	if (transfer->obex == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
+							&transfer->progress);
+
+	return TRUE;
+}
+
+static const char *status2str(uint8_t status)
+{
+	switch (status) {
+	case TRANSFER_STATUS_QUEUED:
+		return "queued";
+	case TRANSFER_STATUS_ACTIVE:
+		return "active";
+	case TRANSFER_STATUS_SUSPENDED_QUEUED:
+	case TRANSFER_STATUS_SUSPENDED:
+		return "suspended";
+	case TRANSFER_STATUS_COMPLETE:
+		return "complete";
+	case TRANSFER_STATUS_ERROR:
+	default:
+		return "error";
+	}
+}
+
+static gboolean get_status(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_transfer *transfer = data;
+	const char *status = status2str(transfer->status);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+	return TRUE;
+}
+
+static gboolean get_session(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obc_transfer *transfer = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&transfer->session);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable obc_transfer_methods[] = {
+	{ GDBUS_METHOD("Suspend", NULL, NULL, obc_transfer_suspend) },
+	{ GDBUS_METHOD("Resume", NULL, NULL, obc_transfer_resume) },
+	{ GDBUS_ASYNC_METHOD("Cancel", NULL, NULL,
+				obc_transfer_cancel) },
+	{ }
+};
+
+static const GDBusPropertyTable obc_transfer_properties[] = {
+	{ "Status", "s", get_status },
+	{ "Name", "s", get_name, NULL, name_exists },
+	{ "Size", "t", get_size },
+	{ "Filename", "s", get_filename, NULL, filename_exists },
+	{ "Transferred", "t", get_transferred, NULL, transferred_exists },
+	{ "Session", "o", get_session },
+	{ }
+};
+
+static void obc_transfer_free(struct obc_transfer *transfer)
+{
+	DBG("%p", transfer);
+
+	if (transfer->status == TRANSFER_STATUS_SUSPENDED)
+		g_obex_resume(transfer->obex);
+
+	if (transfer->req > 0)
+		g_obex_cancel_req(transfer->obex, transfer->req, TRUE);
+
+	if (transfer->xfer)
+		g_obex_cancel_transfer(transfer->xfer, NULL, NULL);
+
+	if (transfer->progress_id != 0) {
+		g_source_remove(transfer->progress_id);
+		transfer->progress_id = 0;
+	}
+
+	if (transfer->op == G_OBEX_OP_GET &&
+				transfer->status != TRANSFER_STATUS_COMPLETE &&
+				transfer->filename)
+		remove(transfer->filename);
+
+	if (transfer->fd > 0)
+		close(transfer->fd);
+
+	if (transfer->apparam != NULL)
+		g_obex_apparam_free(transfer->apparam);
+
+	if (transfer->conn)
+		dbus_connection_unref(transfer->conn);
+
+	if (transfer->msg)
+		dbus_message_unref(transfer->msg);
+
+	if (transfer->obex)
+		g_obex_unref(transfer->obex);
+
+	g_free(transfer->callback);
+	g_free(transfer->owner);
+	g_free(transfer->filename);
+	g_free(transfer->name);
+	g_free(transfer->type);
+	g_free(transfer->session);
+	g_free(transfer->path);
+	g_free(transfer);
+}
+
+static struct obc_transfer *obc_transfer_create(guint8 op,
+						const char *filename,
+						const char *name,
+						const char *type)
+{
+	struct obc_transfer *transfer;
+
+	transfer = g_new0(struct obc_transfer, 1);
+	transfer->op = op;
+	transfer->filename = g_strdup(filename);
+	transfer->name = g_strdup(name);
+	transfer->type = g_strdup(type);
+
+	return transfer;
+}
+
+gboolean obc_transfer_register(struct obc_transfer *transfer,
+						DBusConnection *conn,
+						const char *session,
+						const char *owner,
+						GError **err)
+{
+	transfer->owner = g_strdup(owner);
+
+	transfer->session = g_strdup(session);
+	transfer->path = g_strdup_printf("%s/transfer%ju", session, counter++);
+
+	transfer->conn = dbus_connection_ref(conn);
+	if (transfer->conn == NULL) {
+		g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
+						"Unable to connect to D-Bus");
+		return FALSE;
+	}
+
+	if (g_dbus_register_interface(transfer->conn, transfer->path,
+				TRANSFER_INTERFACE,
+				obc_transfer_methods, NULL,
+				obc_transfer_properties, transfer,
+				NULL) == FALSE) {
+		g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
+						"Unable to register to D-Bus");
+		return FALSE;
+	}
+
+	DBG("%p registered %s", transfer, transfer->path);
+
+	return TRUE;
+}
+
+static gboolean transfer_open(struct obc_transfer *transfer, int flags,
+						mode_t mode, GError **err)
+{
+	int fd;
+	char *filename;
+
+	if (transfer->filename != NULL && strcmp(transfer->filename, "") != 0) {
+		fd = open(transfer->filename, flags, mode);
+		if (fd < 0) {
+			error("open(): %s(%d)", strerror(errno), errno);
+			g_set_error(err, OBC_TRANSFER_ERROR, -errno,
+							"Unable to open file");
+			return FALSE;
+		}
+		goto done;
+	}
+
+	fd = g_file_open_tmp("obex-clientXXXXXX", &filename, err);
+	if (fd < 0) {
+		error("g_file_open_tmp(): %s", (*err)->message);
+		return FALSE;
+	}
+
+	if (transfer->filename == NULL) {
+		remove(filename); /* remove always only if NULL was given */
+		g_free(filename);
+	} else {
+		g_free(transfer->filename);
+		transfer->filename = filename;
+	}
+
+done:
+	transfer->fd = fd;
+	return TRUE;
+}
+
+struct obc_transfer *obc_transfer_get(const char *type, const char *name,
+					const char *filename, GError **err)
+{
+	struct obc_transfer *transfer;
+	int perr;
+
+	transfer = obc_transfer_create(G_OBEX_OP_GET, filename, name, type);
+
+	perr = transfer_open(transfer, O_WRONLY | O_CREAT | O_TRUNC, 0600, err);
+	if (perr < 0) {
+		obc_transfer_free(transfer);
+		return NULL;
+	}
+
+	return transfer;
+}
+
+struct obc_transfer *obc_transfer_put(const char *type, const char *name,
+					const char *filename,
+					const void *contents, size_t size,
+					GError **err)
+{
+	struct obc_transfer *transfer;
+	struct stat st;
+	int perr;
+
+	if ((filename == NULL || strcmp(filename, "") == 0) &&
+							contents == NULL) {
+		g_set_error(err, OBC_TRANSFER_ERROR, -EINVAL,
+						"Invalid filename given");
+		return NULL;
+	}
+
+	transfer = obc_transfer_create(G_OBEX_OP_PUT, filename, name, type);
+
+	if (contents != NULL) {
+		ssize_t w;
+
+		if (!transfer_open(transfer, O_RDWR, 0, err))
+			goto fail;
+
+		w = write(transfer->fd, contents, size);
+		if (w < 0) {
+			perr = errno;
+			error("write(): %s(%d)", strerror(perr), perr);
+			g_set_error(err, OBC_TRANSFER_ERROR, -perr,
+						"Writing to file failed");
+			goto fail;
+		} else if ((size_t) w != size) {
+			error("Unable to write all contents to file");
+			g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
+					"Writing all contents to file failed");
+			goto fail;
+		}
+		lseek(transfer->fd, 0, SEEK_SET);
+	} else {
+		if (!transfer_open(transfer, O_RDONLY, 0, err))
+			goto fail;
+	}
+
+	if (fstat(transfer->fd, &st) < 0) {
+		perr = errno;
+		error("fstat(): %s(%d)", strerror(perr), perr);
+		g_set_error(err, OBC_TRANSFER_ERROR, -perr,
+						"Unable to get file status");
+		goto fail;
+	}
+
+	transfer->size = st.st_size;
+
+	return transfer;
+
+fail:
+	obc_transfer_free(transfer);
+	return NULL;
+}
+
+void obc_transfer_unregister(struct obc_transfer *transfer)
+{
+	if (transfer->path) {
+		g_dbus_unregister_interface(transfer->conn,
+			transfer->path, TRANSFER_INTERFACE);
+	}
+
+	DBG("%p unregistered %s", transfer, transfer->path);
+
+	obc_transfer_free(transfer);
+}
+
+static gboolean get_xfer_progress(const void *buf, gsize len,
+							gpointer user_data)
+{
+	struct obc_transfer *transfer = user_data;
+
+	if (transfer->fd > 0) {
+		int w;
+
+		w = write(transfer->fd, buf, len);
+		if (w < 0)
+			return FALSE;
+
+		transfer->transferred += w;
+	}
+
+	return TRUE;
+}
+
+static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	struct transfer_callback *callback = transfer->callback;
+
+	transfer->xfer = 0;
+
+	if (transfer->progress_id != 0) {
+		g_source_remove(transfer->progress_id);
+		transfer->progress_id = 0;
+	}
+
+	if (transfer->status == TRANSFER_STATUS_SUSPENDED)
+		g_obex_resume(transfer->obex);
+
+	if (err)
+		transfer_set_status(transfer, TRANSFER_STATUS_ERROR);
+	else
+		transfer_set_status(transfer, TRANSFER_STATUS_COMPLETE);
+
+	if (callback)
+		callback->func(transfer, err, callback->data);
+}
+
+static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	GObexPacket *req;
+	GObexHeader *hdr;
+	GObexApparam *apparam;
+	const guint8 *buf;
+	gsize len;
+	guint8 rspcode;
+	gboolean final;
+
+	if (err != NULL) {
+		xfer_complete(obex, err, transfer);
+		return;
+	}
+
+	rspcode = g_obex_packet_get_operation(rsp, &final);
+	if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) {
+		err = g_error_new(OBC_TRANSFER_ERROR, rspcode, "%s",
+						g_obex_strerror(rspcode));
+		xfer_complete(obex, err, transfer);
+		g_error_free(err);
+		return;
+	}
+
+	hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_LENGTH);
+	if (hdr) {
+		uint32_t len;
+		if (g_obex_header_get_uint32(hdr, &len)) {
+			transfer->size = len;
+			g_dbus_emit_property_changed(transfer->conn,
+						transfer->path,
+						TRANSFER_INTERFACE, "Size");
+		}
+	}
+
+	hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM);
+	if (hdr) {
+		apparam = g_obex_header_get_apparam(hdr);
+		if (apparam != NULL)
+			obc_transfer_set_apparam(transfer, apparam);
+	}
+
+	hdr = g_obex_packet_get_body(rsp);
+	if (hdr) {
+		g_obex_header_get_bytes(hdr, &buf, &len);
+		if (len != 0)
+			get_xfer_progress(buf, len, transfer);
+	}
+
+	if (rspcode == G_OBEX_RSP_SUCCESS) {
+		transfer->req = 0;
+		xfer_complete(obex, err, transfer);
+		return;
+	}
+
+	if (g_obex_srm_active(obex) ||
+				transfer->status == TRANSFER_STATUS_SUSPENDED)
+		return;
+
+	transfer->req = 0;
+
+	req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+
+	transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress,
+						xfer_complete, transfer,
+						&err);
+}
+
+static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
+{
+	struct obc_transfer *transfer = user_data;
+	gssize size;
+
+	size = read(transfer->fd, buf, len);
+	if (size <= 0)
+		return size;
+
+	transfer->transferred += size;
+
+	return size;
+}
+
+gboolean obc_transfer_set_callback(struct obc_transfer *transfer,
+					transfer_callback_t func,
+					void *user_data)
+{
+	struct transfer_callback *callback;
+
+	if (transfer->callback != NULL)
+		return FALSE;
+
+	callback = g_new0(struct transfer_callback, 1);
+	callback->func = func;
+	callback->data = user_data;
+
+	transfer->callback = callback;
+
+	return TRUE;
+}
+
+static gboolean report_progress(gpointer data)
+{
+	struct obc_transfer *transfer = data;
+
+	if (transfer->transferred == transfer->progress)
+		return TRUE;
+
+	transfer->progress = transfer->transferred;
+
+	if (transfer->transferred == transfer->size) {
+		transfer->progress_id = 0;
+		return FALSE;
+	}
+
+	if (transfer->status != TRANSFER_STATUS_ACTIVE &&
+				transfer->status != TRANSFER_STATUS_SUSPENDED)
+		transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
+
+	g_dbus_emit_property_changed(transfer->conn, transfer->path,
+					TRANSFER_INTERFACE, "Transferred");
+
+	return TRUE;
+}
+
+static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err)
+{
+	GObexPacket *req;
+	GObexHeader *hdr;
+
+	if (transfer->xfer > 0) {
+		g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
+						"Transfer already started");
+		return FALSE;
+	}
+
+	req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+
+	if (transfer->name != NULL)
+		g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
+							transfer->name);
+
+	if (transfer->type != NULL)
+		g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
+						strlen(transfer->type) + 1);
+
+	if (transfer->apparam != NULL) {
+		hdr = g_obex_header_new_apparam(transfer->apparam);
+		g_obex_packet_add_header(req, hdr);
+	}
+
+	transfer->req = g_obex_send_req(transfer->obex, req,
+						FIRST_PACKET_TIMEOUT,
+						get_xfer_progress_first,
+						transfer, err);
+	if (transfer->req == 0)
+		return FALSE;
+
+	if (transfer->path == NULL)
+		return TRUE;
+
+	transfer->progress_id = g_timeout_add_seconds(1, report_progress,
+								transfer);
+
+	return TRUE;
+}
+
+static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err)
+{
+	GObexPacket *req;
+	GObexHeader *hdr;
+
+	if (transfer->xfer > 0) {
+		g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
+						"Transfer already started");
+		return FALSE;
+	}
+
+	req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
+
+	if (transfer->name != NULL)
+		g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
+							transfer->name);
+
+	if (transfer->type != NULL)
+		g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
+						strlen(transfer->type) + 1);
+
+	if (transfer->size < UINT32_MAX)
+		g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size);
+
+	if (transfer->apparam != NULL) {
+		hdr = g_obex_header_new_apparam(transfer->apparam);
+		g_obex_packet_add_header(req, hdr);
+	}
+
+	transfer->xfer = g_obex_put_req_pkt(transfer->obex, req,
+					put_xfer_progress, xfer_complete,
+					transfer, err);
+	if (transfer->xfer == 0)
+		return FALSE;
+
+	if (transfer->path == NULL)
+		return TRUE;
+
+	transfer->progress_id = g_timeout_add_seconds(1, report_progress,
+								transfer);
+
+	return TRUE;
+}
+
+gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex,
+								GError **err)
+{
+	if (!transfer->obex)
+		transfer->obex = g_obex_ref(obex);
+
+	if (transfer->status == TRANSFER_STATUS_SUSPENDED_QUEUED) {
+		/* Reset status so the transfer can be resumed */
+		transfer->status = TRANSFER_STATUS_SUSPENDED;
+		return TRUE;
+	}
+
+	switch (transfer->op) {
+	case G_OBEX_OP_GET:
+		return transfer_start_get(transfer, err);
+	case G_OBEX_OP_PUT:
+		return transfer_start_put(transfer, err);
+	}
+
+	g_set_error(err, OBC_TRANSFER_ERROR, -ENOTSUP, "Not supported");
+	return FALSE;
+}
+
+guint8 obc_transfer_get_operation(struct obc_transfer *transfer)
+{
+	return transfer->op;
+}
+
+void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data)
+{
+	if (transfer->apparam != NULL)
+		g_obex_apparam_free(transfer->apparam);
+
+	if (data == NULL)
+		return;
+
+	transfer->apparam = data;
+}
+
+void *obc_transfer_get_apparam(struct obc_transfer *transfer)
+{
+	return transfer->apparam;
+}
+
+int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents,
+								size_t *size)
+{
+	struct stat st;
+	ssize_t ret;
+
+	if (contents == NULL)
+		return -EINVAL;
+
+	if (fstat(transfer->fd, &st) < 0) {
+		error("fstat(): %s(%d)", strerror(errno), errno);
+		return -errno;
+	}
+
+	if (lseek(transfer->fd, 0, SEEK_SET) < 0) {
+		error("lseek(): %s(%d)", strerror(errno), errno);
+		return -errno;
+	}
+
+	*contents = g_malloc(st.st_size + 1);
+
+	ret = read(transfer->fd, *contents, st.st_size);
+	if (ret < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		g_free(*contents);
+		return -errno;
+	}
+
+	(*contents)[ret] = '\0';
+
+	if (size)
+		*size = ret;
+
+	return 0;
+}
+
+const char *obc_transfer_get_path(struct obc_transfer *transfer)
+{
+	return transfer->path;
+}
+
+gint64 obc_transfer_get_size(struct obc_transfer *transfer)
+{
+	return transfer->size;
+}
diff --git a/bluez/repo/obexd/client/transfer.h b/bluez/repo/obexd/client/transfer.h
new file mode 100644
index 0000000..b6b835d
--- /dev/null
+++ b/bluez/repo/obexd/client/transfer.h
@@ -0,0 +1,62 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct obc_transfer;
+
+typedef void (*transfer_callback_t) (struct obc_transfer *transfer,
+					GError *err, void *user_data);
+
+struct obc_transfer *obc_transfer_get(const char *type, const char *name,
+					const char *filename, GError **err);
+struct obc_transfer *obc_transfer_put(const char *type, const char *name,
+					const char *filename,
+					const void *contents, size_t size,
+					GError **err);
+
+gboolean obc_transfer_register(struct obc_transfer *transfer,
+					DBusConnection *conn,
+					const char *session,
+					const char *owner,
+					GError **err);
+
+void obc_transfer_unregister(struct obc_transfer *transfer);
+
+gboolean obc_transfer_set_callback(struct obc_transfer *transfer,
+					transfer_callback_t func,
+					void *user_data);
+
+gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex,
+								GError **err);
+guint8 obc_transfer_get_operation(struct obc_transfer *transfer);
+
+void obc_transfer_set_apparam(struct obc_transfer *transfer, void *data);
+void *obc_transfer_get_apparam(struct obc_transfer *transfer);
+int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents,
+								size_t *size);
+
+const char *obc_transfer_get_path(struct obc_transfer *transfer);
+gint64 obc_transfer_get_size(struct obc_transfer *transfer);
+
+DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer,
+							DBusMessage *message);
diff --git a/bluez/repo/obexd/client/transport.c b/bluez/repo/obexd/client/transport.c
new file mode 100644
index 0000000..aae6780
--- /dev/null
+++ b/bluez/repo/obexd/client/transport.c
@@ -0,0 +1,82 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *
+ *  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 <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <inttypes.h>
+
+#include "obexd/src/log.h"
+#include "transport.h"
+
+static GSList *transports = NULL;
+
+struct obc_transport *obc_transport_find(const char *name)
+{
+	GSList *l;
+
+	for (l = transports; l; l = l->next) {
+		struct obc_transport *transport = l->data;
+
+		if (strcasecmp(name, transport->name) == 0)
+			return transport;
+	}
+
+	return NULL;
+}
+
+int obc_transport_register(struct obc_transport *transport)
+{
+	if (!transport) {
+		error("Invalid transport");
+		return -EINVAL;
+	}
+
+	if (obc_transport_find(transport->name)) {
+		error("Permission denied: transport %s already registered",
+							transport->name);
+		return -EPERM;
+	}
+
+	DBG("transport %p name %s registered", transport, transport->name);
+
+	transports = g_slist_append(transports, transport);
+
+	return 0;
+}
+
+void obc_transport_unregister(struct obc_transport *transport)
+{
+	if (!g_slist_find(transports, transport)) {
+		error("Unable to unregister: No such transport %p", transport);
+		return;
+	}
+
+	DBG("transport %p name %s unregistered", transport, transport->name);
+
+	transports = g_slist_remove(transports, transport);
+}
diff --git a/bluez/repo/obexd/client/transport.h b/bluez/repo/obexd/client/transport.h
new file mode 100644
index 0000000..b035cfc
--- /dev/null
+++ b/bluez/repo/obexd/client/transport.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *
+ *  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 (*obc_transport_func)(GIOChannel *io, GError *err,
+							gpointer user_data);
+
+struct obc_transport {
+	const char *name;
+	guint (*connect) (const char *source, const char *destination,
+				const char *service, uint16_t port,
+				obc_transport_func func, void *user_data);
+	int (*getpacketopt) (GIOChannel *io, int *tx_mtu, int *rx_mtu);
+	void (*disconnect) (guint id);
+	const void *(*getattribute) (guint id, int attribute_id);
+};
+
+int obc_transport_register(struct obc_transport *transport);
+void obc_transport_unregister(struct obc_transport *transport);
+struct obc_transport *obc_transport_find(const char *name);
diff --git a/bluez/repo/obexd/plugins/._bluetooth.c b/bluez/repo/obexd/plugins/._bluetooth.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._bluetooth.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._filesystem.c b/bluez/repo/obexd/plugins/._filesystem.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._filesystem.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._filesystem.h b/bluez/repo/obexd/plugins/._filesystem.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._filesystem.h
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._ftp.c b/bluez/repo/obexd/plugins/._ftp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._ftp.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._ftp.h b/bluez/repo/obexd/plugins/._ftp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._ftp.h
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._irmc.c b/bluez/repo/obexd/plugins/._irmc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._irmc.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._mas.c b/bluez/repo/obexd/plugins/._mas.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._mas.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._messages-dummy.c b/bluez/repo/obexd/plugins/._messages-dummy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._messages-dummy.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._messages-tracker.c b/bluez/repo/obexd/plugins/._messages-tracker.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._messages-tracker.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._messages.h b/bluez/repo/obexd/plugins/._messages.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._messages.h
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._opp.c b/bluez/repo/obexd/plugins/._opp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._opp.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._pbap.c b/bluez/repo/obexd/plugins/._pbap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._pbap.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._pcsuite.c b/bluez/repo/obexd/plugins/._pcsuite.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._pcsuite.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._phonebook-dummy.c b/bluez/repo/obexd/plugins/._phonebook-dummy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._phonebook-dummy.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._phonebook-ebook.c b/bluez/repo/obexd/plugins/._phonebook-ebook.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._phonebook-ebook.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._phonebook-tracker.c b/bluez/repo/obexd/plugins/._phonebook-tracker.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._phonebook-tracker.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._phonebook.h b/bluez/repo/obexd/plugins/._phonebook.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._phonebook.h
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._syncevolution.c b/bluez/repo/obexd/plugins/._syncevolution.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._syncevolution.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._vcard.c b/bluez/repo/obexd/plugins/._vcard.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._vcard.c
Binary files differ
diff --git a/bluez/repo/obexd/plugins/._vcard.h b/bluez/repo/obexd/plugins/._vcard.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/plugins/._vcard.h
Binary files differ
diff --git a/bluez/repo/obexd/plugins/bluetooth.c b/bluez/repo/obexd/plugins/bluetooth.c
new file mode 100644
index 0000000..d8b872a
--- /dev/null
+++ b/bluez/repo/obexd/plugins/bluetooth.c
@@ -0,0 +1,497 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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 <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/server.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/transport.h"
+#include "obexd/src/service.h"
+#include "obexd/src/log.h"
+
+#define BT_RX_MTU 32767
+#define BT_TX_MTU 32767
+
+struct bluetooth_profile {
+	struct obex_server *server;
+	struct obex_service_driver *driver;
+	char *uuid;
+	char *path;
+};
+
+static GSList *profiles = NULL;
+
+static DBusConnection *connection = NULL;
+
+static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void connect_event(GIOChannel *io, GError *err, void *user_data)
+{
+	int sk = g_io_channel_unix_get_fd(io);
+	struct bluetooth_profile *profile = user_data;
+	struct obex_server *server = profile->server;
+	int type;
+	int omtu = BT_TX_MTU;
+	int imtu = BT_RX_MTU;
+	gboolean stream = TRUE;
+	socklen_t len = sizeof(int);
+
+	if (err)
+		goto drop;
+
+	if (getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0)
+		goto done;
+
+	if (type != SOCK_SEQPACKET)
+		goto done;
+
+	stream = FALSE;
+
+	/* Read MTU if io is an L2CAP socket */
+	bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu,
+							BT_IO_OPT_INVALID);
+
+done:
+	if (obex_server_new_connection(server, io, omtu, imtu, stream) < 0)
+		g_io_channel_shutdown(io, TRUE, NULL);
+
+	return;
+
+drop:
+	error("%s", err->message);
+	g_io_channel_shutdown(io, TRUE, NULL);
+	return;
+}
+
+static DBusMessage *invalid_args(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
+					"Invalid arguments in method call");
+}
+
+static DBusMessage *profile_new_connection(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	DBusMessageIter args;
+	const char *device;
+	int fd;
+	GIOChannel *io;
+
+	dbus_message_iter_init(msg, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &device);
+
+	dbus_message_iter_next(&args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_UNIX_FD)
+		return invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &fd);
+
+	if (fd < 0) {
+		error("bluetooth: NewConnection invalid fd");
+		return invalid_args(msg);
+	}
+
+	/* Read fd flags to make sure it can be used */
+	if (fcntl(fd, F_GETFD) < 0) {
+		error("bluetooth: fcntl(%d, F_GETFD): %s (%d)", fd,
+						strerror(errno), errno);
+		return invalid_args(msg);
+	}
+
+	io = g_io_channel_unix_new(fd);
+	if (io == NULL)
+		return invalid_args(msg);
+
+	DBG("device %s", device);
+
+	connect_event(io, NULL, data);
+	g_io_channel_unref(io);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *profile_request_disconnection(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *profile_cancel(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable profile_methods[] = {
+	{ GDBUS_METHOD("Release",
+			NULL, NULL,
+			profile_release) },
+	{ GDBUS_METHOD("NewConnection",
+			GDBUS_ARGS({ "device", "o" }, { "fd", "h" },
+			{ "options", "a{sv}" }), NULL,
+			profile_new_connection) },
+	{ GDBUS_METHOD("RequestDisconnection",
+			GDBUS_ARGS({ "device", "o" }), NULL,
+			profile_request_disconnection) },
+	{ GDBUS_METHOD("Cancel",
+			NULL, NULL,
+			profile_cancel) },
+	{ }
+};
+
+static void unregister_profile(struct bluetooth_profile *profile)
+{
+	g_dbus_unregister_interface(connection, profile->path,
+						"org.bluez.Profile1");
+	g_free(profile->path);
+	profile->path = NULL;
+}
+
+static void register_profile_reply(DBusPendingCall *call, void *user_data)
+{
+	struct bluetooth_profile *profile = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		DBG("Profile %s registered", profile->path);
+		goto done;
+	}
+
+	unregister_profile(profile);
+
+	error("bluetooth: RequestProfile error: %s, %s", derr.name,
+								derr.message);
+	dbus_error_free(&derr);
+done:
+	dbus_message_unref(reply);
+}
+
+static void profile_free(void *data)
+{
+	struct bluetooth_profile *profile = data;
+
+	if (profile->path != NULL)
+		unregister_profile(profile);
+
+	g_free(profile->uuid);
+	g_free(profile);
+}
+
+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 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 int register_profile(struct bluetooth_profile *profile)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, opt;
+	DBusPendingCall *call;
+	dbus_bool_t auto_connect = FALSE;
+	char *xml;
+	int ret = 0;
+
+	profile->path = g_strconcat("/org/bluez/obex/", profile->uuid, NULL);
+	g_strdelimit(profile->path, "-", '_');
+
+	if (!g_dbus_register_interface(connection, profile->path,
+					"org.bluez.Profile1", profile_methods,
+					NULL, NULL,
+					profile, NULL)) {
+		error("D-Bus failed to register %s", profile->path);
+		g_free(profile->path);
+		profile->path = NULL;
+		return -1;
+	}
+
+	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
+						"org.bluez.ProfileManager1",
+						"RegisterProfile");
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+							&profile->path);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+							&profile->uuid);
+	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,
+					&opt);
+	dict_append_entry(&opt, "AutoConnect", DBUS_TYPE_BOOLEAN,
+								&auto_connect);
+	if (profile->driver->record) {
+		if (profile->driver->port != 0)
+			xml = g_markup_printf_escaped(profile->driver->record,
+						profile->driver->channel,
+						profile->driver->name,
+						profile->driver->port);
+		else
+			xml = g_markup_printf_escaped(profile->driver->record,
+						profile->driver->channel,
+						profile->driver->name);
+		dict_append_entry(&opt, "ServiceRecord", DBUS_TYPE_STRING,
+								&xml);
+		g_free(xml);
+	}
+	dbus_message_iter_close_container(&iter, &opt);
+
+	if (!g_dbus_send_message_with_reply(connection, msg, &call, -1)) {
+		ret = -1;
+		unregister_profile(profile);
+		goto failed;
+	}
+
+	dbus_pending_call_set_notify(call, register_profile_reply, profile,
+									NULL);
+	dbus_pending_call_unref(call);
+
+failed:
+	dbus_message_unref(msg);
+	return ret;
+}
+
+static const char *service2uuid(uint16_t service)
+{
+	switch (service) {
+	case OBEX_OPP:
+		return OBEX_OPP_UUID;
+	case OBEX_FTP:
+		return OBEX_FTP_UUID;
+	case OBEX_PBAP:
+		return OBEX_PSE_UUID;
+	case OBEX_IRMC:
+		return OBEX_SYNC_UUID;
+	case OBEX_PCSUITE:
+		return "00005005-0000-1000-8000-0002ee000001";
+	case OBEX_SYNCEVOLUTION:
+		return "00000002-0000-1000-8000-0002ee000002";
+	case OBEX_MAS:
+		return OBEX_MAS_UUID;
+	case OBEX_MNS:
+		return OBEX_MNS_UUID;
+	}
+
+	return NULL;
+}
+
+static void name_acquired(DBusConnection *conn, void *user_data)
+{
+	GSList *l;
+
+	DBG("org.bluez appeared");
+
+	for (l = profiles; l; l = l->next) {
+		struct bluetooth_profile *profile = l->data;
+
+		if (profile->path != NULL)
+			continue;
+
+		if (register_profile(profile) < 0) {
+			error("bluetooth: Failed to register profile %s",
+							profile->path);
+			g_free(profile->path);
+			profile->path = NULL;
+		}
+	}
+}
+
+static void name_released(DBusConnection *conn, void *user_data)
+{
+	GSList *l;
+
+	DBG("org.bluez disappered");
+
+	for (l = profiles; l; l = l->next) {
+		struct bluetooth_profile *profile = l->data;
+
+		if (profile->path == NULL)
+			continue;
+
+		unregister_profile(profile);
+	}
+}
+
+static void *bluetooth_start(struct obex_server *server, int *err)
+{
+	const GSList *l;
+
+	for (l = server->drivers; l; l = l->next) {
+		struct obex_service_driver *driver = l->data;
+		struct bluetooth_profile *profile;
+		const char *uuid;
+
+		uuid = service2uuid(driver->service);
+		if (uuid == NULL)
+			continue;
+
+		profile = g_new0(struct bluetooth_profile, 1);
+		profile->driver = driver;
+		profile->server = server;
+		profile->uuid = g_strdup(uuid);
+
+		profiles = g_slist_prepend(profiles, profile);
+	}
+
+	return profiles;
+}
+
+static void bluetooth_stop(void *data)
+{
+	g_slist_free_full(profiles, profile_free);
+	profiles = NULL;
+}
+
+static int bluetooth_getpeername(GIOChannel *io, char **name)
+{
+	GError *gerr = NULL;
+	char address[18];
+
+	bt_io_get(io, &gerr, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return -EINVAL;
+	}
+
+	*name = g_strdup(address);
+
+	return 0;
+}
+
+static int bluetooth_getsockname(GIOChannel *io, char **name)
+{
+	GError *gerr = NULL;
+	char address[18];
+
+	bt_io_get(io, &gerr, BT_IO_OPT_SOURCE, address, BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return -EINVAL;
+	}
+
+	*name = g_strdup(address);
+
+	return 0;
+}
+
+static struct obex_transport_driver driver = {
+	.name = "bluetooth",
+	.start = bluetooth_start,
+	.getpeername = bluetooth_getpeername,
+	.getsockname = bluetooth_getsockname,
+	.stop = bluetooth_stop
+};
+
+static unsigned int listener_id = 0;
+
+static int bluetooth_init(void)
+{
+	connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
+	if (connection == NULL)
+		return -EPERM;
+
+	listener_id = g_dbus_add_service_watch(connection, "org.bluez",
+				name_acquired, name_released, NULL, NULL);
+
+	return obex_transport_driver_register(&driver);
+}
+
+static void bluetooth_exit(void)
+{
+	g_dbus_remove_watch(connection, listener_id);
+
+	g_slist_free_full(profiles, profile_free);
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	obex_transport_driver_unregister(&driver);
+}
+
+OBEX_PLUGIN_DEFINE(bluetooth, bluetooth_init, bluetooth_exit)
diff --git a/bluez/repo/obexd/plugins/filesystem.c b/bluez/repo/obexd/plugins/filesystem.c
new file mode 100644
index 0000000..0e6cd49
--- /dev/null
+++ b/bluez/repo/obexd/plugins/filesystem.c
@@ -0,0 +1,722 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2009-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sendfile.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/mimetype.h"
+#include "filesystem.h"
+
+#define EOL_CHARS "\n"
+
+#define FL_VERSION "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" EOL_CHARS
+
+#define FL_TYPE "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">" EOL_CHARS
+
+#define FL_TYPE_PCSUITE "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\"" EOL_CHARS \
+			"  [ <!ATTLIST folder mem-type CDATA #IMPLIED> ]>" EOL_CHARS
+
+#define FL_BODY_BEGIN "<folder-listing version=\"1.0\">" EOL_CHARS
+
+#define FL_BODY_END "</folder-listing>" EOL_CHARS
+
+#define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>" EOL_CHARS
+
+#define FL_FILE_ELEMENT "<file name=\"%s\" size=\"%" PRIu64 "\"" \
+			" %s accessed=\"%s\" " \
+			"modified=\"%s\" created=\"%s\"/>" EOL_CHARS
+
+#define FL_FOLDER_ELEMENT "<folder name=\"%s\" %s accessed=\"%s\" " \
+			"modified=\"%s\" created=\"%s\"/>" EOL_CHARS
+
+#define FL_FOLDER_ELEMENT_PCSUITE "<folder name=\"%s\" %s accessed=\"%s\"" \
+			" modified=\"%s\" mem-type=\"DEV\"" \
+			" created=\"%s\"/>" EOL_CHARS
+
+#define FTP_TARGET_SIZE 16
+
+static const uint8_t FTP_TARGET[FTP_TARGET_SIZE] = {
+			0xF9, 0xEC, 0x7B, 0xC4,  0x95, 0x3C, 0x11, 0xD2,
+			0x98, 0x4E, 0x52, 0x54,  0x00, 0xDC, 0x9E, 0x09  };
+
+#define PCSUITE_WHO_SIZE 8
+
+static const uint8_t PCSUITE_WHO[PCSUITE_WHO_SIZE] = {
+			'P', 'C', ' ', 'S', 'u', 'i', 't', 'e' };
+
+gboolean is_filename(const char *name)
+{
+	if (strchr(name, '/'))
+		return FALSE;
+
+	if (strcmp(name, ".") == 0)
+		return FALSE;
+
+	if (strcmp(name, "..") == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+int verify_path(const char *path)
+{
+	char *t;
+	int ret = 0;
+
+	if (obex_option_symlinks())
+		return 0;
+
+	t = realpath(path, NULL);
+	if (t == NULL)
+		return -errno;
+
+	if (!g_str_has_prefix(t, obex_option_root_folder()))
+		ret = -EPERM;
+
+	free(t);
+
+	return ret;
+}
+
+static char *file_stat_line(char *filename, struct stat *fstat,
+					struct stat *dstat, gboolean root,
+					gboolean pcsuite)
+{
+	char perm[51], atime[18], ctime[18], mtime[18];
+	char *escaped, *ret = NULL;
+
+	snprintf(perm, 50, "user-perm=\"%s%s%s\" group-perm=\"%s%s%s\" "
+			"other-perm=\"%s%s%s\"",
+			(fstat->st_mode & S_IRUSR ? "R" : ""),
+			(fstat->st_mode & S_IWUSR ? "W" : ""),
+			(dstat->st_mode & S_IWUSR ? "D" : ""),
+			(fstat->st_mode & S_IRGRP ? "R" : ""),
+			(fstat->st_mode & S_IWGRP ? "W" : ""),
+			(dstat->st_mode & S_IWGRP ? "D" : ""),
+			(fstat->st_mode & S_IROTH ? "R" : ""),
+			(fstat->st_mode & S_IWOTH ? "W" : ""),
+			(dstat->st_mode & S_IWOTH ? "D" : ""));
+
+	strftime(atime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_atime));
+	strftime(ctime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_ctime));
+	strftime(mtime, 17, "%Y%m%dT%H%M%SZ", gmtime(&fstat->st_mtime));
+
+	escaped = g_markup_escape_text(filename, -1);
+
+	if (S_ISDIR(fstat->st_mode)) {
+		if (pcsuite && root && g_str_equal(filename, "Data"))
+			ret = g_strdup_printf(FL_FOLDER_ELEMENT_PCSUITE,
+						escaped, perm, atime,
+						mtime, ctime);
+		else
+			ret = g_strdup_printf(FL_FOLDER_ELEMENT, escaped, perm,
+							atime, mtime, ctime);
+	} else if (S_ISREG(fstat->st_mode))
+		ret = g_strdup_printf(FL_FILE_ELEMENT, escaped,
+					(uint64_t) fstat->st_size,
+					perm, atime, mtime, ctime);
+
+	g_free(escaped);
+
+	return ret;
+}
+
+static void *filesystem_open(const char *name, int oflag, mode_t mode,
+					void *context, size_t *size, int *err)
+{
+	struct stat stats;
+	struct statvfs buf;
+	int fd, ret;
+	uint64_t avail;
+
+	fd = open(name, oflag, mode);
+	if (fd < 0) {
+		if (err)
+			*err = -errno;
+		return NULL;
+	}
+
+	if (fstat(fd, &stats) < 0) {
+		if (err)
+			*err = -errno;
+		goto failed;
+	}
+
+	ret = verify_path(name);
+	if (ret < 0) {
+		if (err)
+			*err = ret;
+		goto failed;
+	}
+
+	if (oflag == O_RDONLY) {
+		if (size)
+			*size = stats.st_size;
+		goto done;
+	}
+
+	if (fstatvfs(fd, &buf) < 0) {
+		if (err)
+			*err = -errno;
+		goto failed;
+	}
+
+	if (size == NULL)
+		goto done;
+
+	avail = (uint64_t) buf.f_bsize * buf.f_bavail;
+	if (avail < *size) {
+		if (err)
+			*err = -ENOSPC;
+		goto failed;
+	}
+
+done:
+	if (err)
+		*err = 0;
+
+	return GINT_TO_POINTER(fd);
+
+failed:
+	close(fd);
+	return NULL;
+}
+
+static int filesystem_close(void *object)
+{
+	if (close(GPOINTER_TO_INT(object)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static ssize_t filesystem_read(void *object, void *buf, size_t count)
+{
+	ssize_t ret;
+
+	ret = read(GPOINTER_TO_INT(object), buf, count);
+	if (ret < 0)
+		return -errno;
+
+	return ret;
+}
+
+static ssize_t filesystem_write(void *object, const void *buf, size_t count)
+{
+	ssize_t ret;
+
+	ret = write(GPOINTER_TO_INT(object), buf, count);
+	if (ret < 0)
+		return -errno;
+
+	return ret;
+}
+
+static int filesystem_rename(const char *name, const char *destname)
+{
+	int ret;
+
+	ret = rename(name, destname);
+	if (ret < 0) {
+		error("rename(%s, %s): %s (%d)", name, destname,
+						strerror(errno), errno);
+		return -errno;
+	}
+
+	return ret;
+}
+
+static int sendfile_async(int out_fd, int in_fd, off_t *offset, size_t count)
+{
+	int pid;
+
+	/* Run sendfile on child process */
+	pid = fork();
+	switch (pid) {
+		case 0:
+			break;
+		case -1:
+			error("fork() %s (%d)", strerror(errno), errno);
+			return -errno;
+		default:
+			DBG("child %d forked", pid);
+			return pid;
+	}
+
+	/* At child */
+	if (sendfile(out_fd, in_fd, offset, count) < 0)
+		error("sendfile(): %s (%d)", strerror(errno), errno);
+
+	close(in_fd);
+	close(out_fd);
+
+	exit(errno);
+}
+
+static int filesystem_copy(const char *name, const char *destname)
+{
+	void *in, *out;
+	ssize_t ret;
+	size_t size;
+	struct stat st;
+	int in_fd, out_fd, err;
+
+	in = filesystem_open(name, O_RDONLY, 0, NULL, &size, &err);
+	if (in == NULL) {
+		error("open(%s): %s (%d)", name, strerror(-err), -err);
+		return -err;
+	}
+
+	in_fd = GPOINTER_TO_INT(in);
+	ret = fstat(in_fd, &st);
+	if (ret < 0) {
+		error("stat(%s): %s (%d)", name, strerror(errno), errno);
+		return -errno;
+	}
+
+	out = filesystem_open(destname, O_WRONLY | O_CREAT | O_TRUNC,
+					st.st_mode, NULL, &size, &err);
+	if (out == NULL) {
+		error("open(%s): %s (%d)", destname, strerror(-err), -err);
+		filesystem_close(in);
+		return -errno;
+	}
+
+	out_fd = GPOINTER_TO_INT(out);
+
+	/* Check if sendfile is supported */
+	ret = sendfile(out_fd, in_fd, NULL, 0);
+	if (ret < 0) {
+		ret = -errno;
+		error("sendfile: %s (%zd)", strerror(-ret), -ret);
+		goto done;
+	}
+
+	ret = sendfile_async(out_fd, in_fd, NULL, st.st_size);
+	if (ret < 0)
+		goto done;
+
+	return 0;
+
+done:
+	filesystem_close(in);
+	filesystem_close(out);
+
+	return ret;
+}
+
+struct capability_object {
+	int pid;
+	int output;
+	int err;
+	gboolean aborted;
+	GString *buffer;
+};
+
+static void script_exited(GPid pid, int status, void *data)
+{
+	struct capability_object *object = data;
+	char buf[128];
+
+	object->pid = -1;
+
+	DBG("pid: %d status: %d", pid, status);
+
+	g_spawn_close_pid(pid);
+
+	/* free the object if aborted */
+	if (object->aborted) {
+		if (object->buffer != NULL)
+			g_string_free(object->buffer, TRUE);
+
+		g_free(object);
+		return;
+	}
+
+	if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+		memset(buf, 0, sizeof(buf));
+		if (read(object->err, buf, sizeof(buf)) > 0)
+			error("%s", buf);
+		obex_object_set_io_flags(data, G_IO_ERR, -EPERM);
+	} else
+		obex_object_set_io_flags(data, G_IO_IN, 0);
+}
+
+static int capability_exec(const char **argv, int *output, int *err)
+{
+	GError *gerr = NULL;
+	int pid;
+	GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+
+	if (!g_spawn_async_with_pipes(NULL, (char **) argv, NULL, flags, NULL,
+				NULL, &pid, NULL, output, err, &gerr)) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return -EPERM;
+	}
+
+	DBG("executing %s pid %d", argv[0], pid);
+
+	return pid;
+}
+
+static void *capability_open(const char *name, int oflag, mode_t mode,
+					void *context, size_t *size, int *err)
+{
+	struct capability_object *object = NULL;
+	char *buf;
+	const char *argv[2];
+
+	if (oflag != O_RDONLY)
+		goto fail;
+
+	object = g_new0(struct capability_object, 1);
+	object->pid = -1;
+	object->output = -1;
+	object->err = -1;
+
+	if (name[0] != '!') {
+		GError *gerr = NULL;
+		gboolean ret;
+
+		ret = g_file_get_contents(name, &buf, NULL, &gerr);
+		if (ret == FALSE) {
+			error("%s", gerr->message);
+			g_error_free(gerr);
+			goto fail;
+		}
+
+		object->buffer = g_string_new(buf);
+
+		if (size)
+			*size = object->buffer->len;
+
+		goto done;
+	}
+
+	argv[0] = &name[1];
+	argv[1] = NULL;
+
+	object->pid = capability_exec(argv, &object->output, &object->err);
+	if (object->pid < 0)
+		goto fail;
+
+	/* Watch cannot be removed while the process is still running */
+	g_child_watch_add(object->pid, script_exited, object);
+
+done:
+	if (err)
+		*err = 0;
+
+	return object;
+
+fail:
+	if (err)
+		*err = -EPERM;
+
+	g_free(object);
+	return NULL;
+}
+
+static GString *append_pcsuite_preamble(GString *object)
+{
+	return g_string_append(object, FL_TYPE_PCSUITE);
+}
+
+static GString *append_folder_preamble(GString *object)
+{
+	return g_string_append(object, FL_TYPE);
+}
+
+static GString *append_listing(GString *object, const char *name,
+				gboolean pcsuite, size_t *size, int *err)
+{
+	struct stat fstat, dstat;
+	struct dirent *ep;
+	DIR *dp;
+	gboolean root;
+	int ret;
+
+	root = g_str_equal(name, obex_option_root_folder());
+
+	dp = opendir(name);
+	if (dp == NULL) {
+		if (err)
+			*err = -ENOENT;
+		goto failed;
+	}
+
+	if (!root)
+		object = g_string_append(object, FL_PARENT_FOLDER_ELEMENT);
+
+	ret = verify_path(name);
+	if (ret < 0) {
+		*err = ret;
+		goto failed;
+	}
+
+	ret = stat(name, &dstat);
+	if (ret < 0) {
+		if (err)
+			*err = -errno;
+		goto failed;
+	}
+
+	while ((ep = readdir(dp))) {
+		char *filename;
+		char *fullname;
+		char *line;
+
+		if (ep->d_name[0] == '.')
+			continue;
+
+		filename = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
+		if (filename == NULL) {
+			error("g_filename_to_utf8: invalid filename");
+			continue;
+		}
+
+		fullname = g_build_filename(name, ep->d_name, NULL);
+
+		ret = stat(fullname, &fstat);
+		if (ret < 0) {
+			DBG("stat: %s(%d)", strerror(errno), errno);
+			g_free(filename);
+			g_free(fullname);
+			continue;
+		}
+
+		g_free(fullname);
+
+		line = file_stat_line(filename, &fstat, &dstat, root, FALSE);
+		if (line == NULL) {
+			g_free(filename);
+			continue;
+		}
+
+		g_free(filename);
+
+		object = g_string_append(object, line);
+		g_free(line);
+	}
+
+	closedir(dp);
+
+	object = g_string_append(object, FL_BODY_END);
+	if (size)
+		*size = object->len;
+
+	if (err)
+		*err = 0;
+
+	return object;
+
+failed:
+	if (dp)
+		closedir(dp);
+
+	g_string_free(object, TRUE);
+	return NULL;
+}
+
+static void *folder_open(const char *name, int oflag, mode_t mode,
+					void *context, size_t *size, int *err)
+{
+	GString *object;
+
+	object = g_string_new(FL_VERSION);
+	object = append_folder_preamble(object);
+	object = g_string_append(object, FL_BODY_BEGIN);
+
+	return append_listing(object, name, FALSE, size, err);
+}
+
+static void *pcsuite_open(const char *name, int oflag, mode_t mode,
+					void *context, size_t *size, int *err)
+{
+	GString *object;
+
+	object = g_string_new(FL_VERSION);
+	object = append_pcsuite_preamble(object);
+	object = g_string_append(object, FL_BODY_BEGIN);
+
+	return append_listing(object, name, TRUE, size, err);
+}
+
+static int string_free(void *object)
+{
+	GString *string = object;
+
+	g_string_free(string, TRUE);
+
+	return 0;
+}
+
+ssize_t string_read(void *object, void *buf, size_t count)
+{
+	GString *string = object;
+	ssize_t len;
+
+	if (string->len == 0)
+		return 0;
+
+	len = MIN(string->len, count);
+	memcpy(buf, string->str, len);
+	g_string_erase(string, 0, len);
+
+	return len;
+}
+
+static ssize_t folder_read(void *object, void *buf, size_t count)
+{
+	return string_read(object, buf, count);
+}
+
+static ssize_t capability_read(void *object, void *buf, size_t count)
+{
+	struct capability_object *obj = object;
+
+	if (obj->buffer)
+		return string_read(obj->buffer, buf, count);
+
+	if (obj->pid >= 0)
+		return -EAGAIN;
+
+	return read(obj->output, buf, count);
+}
+
+static int capability_close(void *object)
+{
+	struct capability_object *obj = object;
+	int err = 0;
+
+	if (obj->pid < 0)
+		goto done;
+
+	DBG("kill: pid %d", obj->pid);
+	err = kill(obj->pid, SIGTERM);
+	if (err < 0) {
+		err = -errno;
+		error("kill: %s (%d)", strerror(-err), -err);
+		goto done;
+	}
+
+	obj->aborted = TRUE;
+	return 0;
+
+done:
+	if (obj->buffer != NULL)
+		g_string_free(obj->buffer, TRUE);
+
+	g_free(obj);
+
+	return err;
+}
+
+static struct obex_mime_type_driver file = {
+	.open = filesystem_open,
+	.close = filesystem_close,
+	.read = filesystem_read,
+	.write = filesystem_write,
+	.remove = remove,
+	.move = filesystem_rename,
+	.copy = filesystem_copy,
+};
+
+static struct obex_mime_type_driver capability = {
+	.target = FTP_TARGET,
+	.target_size = FTP_TARGET_SIZE,
+	.mimetype = "x-obex/capability",
+	.open = capability_open,
+	.close = capability_close,
+	.read = capability_read,
+};
+
+static struct obex_mime_type_driver folder = {
+	.target = FTP_TARGET,
+	.target_size = FTP_TARGET_SIZE,
+	.mimetype = "x-obex/folder-listing",
+	.open = folder_open,
+	.close = string_free,
+	.read = folder_read,
+};
+
+static struct obex_mime_type_driver pcsuite = {
+	.target = FTP_TARGET,
+	.target_size = FTP_TARGET_SIZE,
+	.who = PCSUITE_WHO,
+	.who_size = PCSUITE_WHO_SIZE,
+	.mimetype = "x-obex/folder-listing",
+	.open = pcsuite_open,
+	.close = string_free,
+	.read = folder_read,
+};
+
+static int filesystem_init(void)
+{
+	int err;
+
+	err = obex_mime_type_driver_register(&folder);
+	if (err < 0)
+		return err;
+
+	err = obex_mime_type_driver_register(&capability);
+	if (err < 0)
+		return err;
+
+	err = obex_mime_type_driver_register(&pcsuite);
+	if (err < 0)
+		return err;
+
+	return obex_mime_type_driver_register(&file);
+}
+
+static void filesystem_exit(void)
+{
+	obex_mime_type_driver_unregister(&folder);
+	obex_mime_type_driver_unregister(&capability);
+	obex_mime_type_driver_unregister(&file);
+}
+
+OBEX_PLUGIN_DEFINE(filesystem, filesystem_init, filesystem_exit)
diff --git a/bluez/repo/obexd/plugins/filesystem.h b/bluez/repo/obexd/plugins/filesystem.h
new file mode 100644
index 0000000..f95773b
--- /dev/null
+++ b/bluez/repo/obexd/plugins/filesystem.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+ssize_t string_read(void *object, void *buf, size_t count);
+gboolean is_filename(const char *name);
+int verify_path(const char *path);
diff --git a/bluez/repo/obexd/plugins/ftp.c b/bluez/repo/obexd/plugins/ftp.c
new file mode 100644
index 0000000..a906527
--- /dev/null
+++ b/bluez/repo/obexd/plugins/ftp.c
@@ -0,0 +1,530 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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 <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/service.h"
+#include "ftp.h"
+#include "filesystem.h"
+
+#define LST_TYPE "x-obex/folder-listing"
+#define CAP_TYPE "x-obex/capability"
+
+static const uint8_t FTP_TARGET[TARGET_SIZE] = {
+			0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
+			0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
+
+struct ftp_session {
+	struct obex_session *os;
+	struct obex_transfer *transfer;
+	char *folder;
+};
+
+static void set_folder(struct ftp_session *ftp, const char *new_folder)
+{
+	DBG("%p folder %s", ftp, new_folder);
+
+	g_free(ftp->folder);
+
+	ftp->folder = new_folder ? g_strdup(new_folder) : NULL;
+}
+
+static int get_by_type(struct ftp_session *ftp, const char *type)
+{
+	struct obex_session *os = ftp->os;
+	const char *capability = obex_option_capability();
+	const char *name = obex_get_name(os);
+	char *path;
+	int err;
+
+	DBG("%p name %s type %s", ftp, name, type);
+
+	if (type == NULL && name == NULL)
+		return -EBADR;
+
+	if (type != NULL && g_ascii_strcasecmp(type, CAP_TYPE) == 0)
+		return obex_get_stream_start(os, capability);
+
+	if (name != NULL && !is_filename(name))
+		return -EBADR;
+
+	path = g_build_filename(ftp->folder, name, NULL);
+	err = obex_get_stream_start(os, path);
+
+	g_free(path);
+
+	return err;
+}
+
+void *ftp_connect(struct obex_session *os, int *err)
+{
+	struct ftp_session *ftp;
+	const char *root_folder;
+
+	DBG("");
+
+	root_folder = obex_option_root_folder();
+
+	manager_register_session(os);
+
+	ftp = g_new0(struct ftp_session, 1);
+	set_folder(ftp, root_folder);
+	ftp->os = os;
+
+	if (err)
+		*err = 0;
+
+	ftp->transfer = manager_register_transfer(os);
+
+	DBG("session %p created", ftp);
+
+	return ftp;
+}
+
+int ftp_get(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+	const char *type = obex_get_type(os);
+	int ret;
+
+	DBG("%p", ftp);
+
+	if (ftp->folder == NULL)
+		return -ENOENT;
+
+	ret = get_by_type(ftp, type);
+	if (ret < 0)
+		return ret;
+
+	/* Only track progress of file transfer */
+	if (type == NULL)
+		manager_emit_transfer_started(ftp->transfer);
+
+	return 0;
+}
+
+static int ftp_delete(struct ftp_session *ftp, const char *name)
+{
+	char *path;
+	int ret = 0;
+
+	DBG("%p name %s", ftp, name);
+
+	if (!(ftp->folder && name))
+		return -EINVAL;
+
+	path = g_build_filename(ftp->folder, name, NULL);
+
+	if (obex_remove(ftp->os, path) < 0)
+		ret = -errno;
+
+	g_free(path);
+
+	return ret;
+}
+
+int ftp_chkput(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+	const char *name = obex_get_name(os);
+	char *path;
+	int ret;
+
+	DBG("%p name %s", ftp, name);
+
+	if (name == NULL)
+		return -EBADR;
+
+	if (!is_filename(name))
+		return -EBADR;
+
+	if (obex_get_size(os) == OBJECT_SIZE_DELETE)
+		return 0;
+
+	path = g_build_filename(ftp->folder, name, NULL);
+
+	ret = obex_put_stream_start(os, path);
+
+	if (ret == 0)
+		manager_emit_transfer_started(ftp->transfer);
+
+	g_free(path);
+
+	return ret;
+}
+
+int ftp_put(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+	const char *name = obex_get_name(os);
+	ssize_t size = obex_get_size(os);
+
+	DBG("%p name %s size %zd", ftp, name, size);
+
+	if (ftp->folder == NULL)
+		return -EPERM;
+
+	if (name == NULL)
+		return -EBADR;
+
+	if (!is_filename(name))
+		return -EBADR;
+
+	if (size == OBJECT_SIZE_DELETE)
+		return ftp_delete(ftp, name);
+
+	return 0;
+}
+
+int ftp_setpath(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+	const char *root_folder, *name;
+	const uint8_t *nonhdr;
+	char *fullname;
+	struct stat dstat;
+	gboolean root;
+	int err;
+
+	if (obex_get_non_header_data(os, &nonhdr) != 2) {
+		error("Set path failed: flag and constants not found!");
+		return -EBADMSG;
+	}
+
+	name = obex_get_name(os);
+	root_folder = obex_option_root_folder();
+	root = g_str_equal(root_folder, ftp->folder);
+
+	DBG("%p name %s", ftp, name);
+
+	/* Check flag "Backup" */
+	if ((nonhdr[0] & 0x01) == 0x01) {
+		DBG("Set to parent path");
+
+		if (root)
+			return -EPERM;
+
+		fullname = g_path_get_dirname(ftp->folder);
+		set_folder(ftp, fullname);
+		g_free(fullname);
+
+		DBG("Set to parent path: %s", ftp->folder);
+
+		return 0;
+	}
+
+	if (!name) {
+		DBG("Set path failed: name missing!");
+		return -EINVAL;
+	}
+
+	if (strlen(name) == 0) {
+		DBG("Set to root");
+		set_folder(ftp, root_folder);
+		return 0;
+	}
+
+	/* Check and set to name path */
+	if (!is_filename(name)) {
+		error("Set path failed: name incorrect!");
+		return -EPERM;
+	}
+
+	fullname = g_build_filename(ftp->folder, name, NULL);
+
+	DBG("Fullname: %s", fullname);
+
+	err = verify_path(fullname);
+
+	if (err < 0)
+		goto done;
+
+	err = stat(fullname, &dstat);
+
+	if (err < 0) {
+		err = -errno;
+
+		if (err == -ENOENT)
+			goto not_found;
+
+		DBG("stat: %s(%d)", strerror(-err), -err);
+
+		goto done;
+	}
+
+	if (S_ISDIR(dstat.st_mode) && (dstat.st_mode & S_IRUSR) &&
+						(dstat.st_mode & S_IXUSR)) {
+		set_folder(ftp, fullname);
+		goto done;
+	}
+
+	err = -EPERM;
+	goto done;
+
+not_found:
+	if (nonhdr[0] != 0) {
+		err = -ENOENT;
+		goto done;
+	}
+
+	if (mkdir(fullname, 0755) <  0) {
+		err = -errno;
+		DBG("mkdir: %s(%d)", strerror(-err), -err);
+		goto done;
+	}
+
+	err = 0;
+	set_folder(ftp, fullname);
+
+done:
+	g_free(fullname);
+	return err;
+}
+
+static gboolean is_valid_path(const char *path)
+{
+	char **elements, **cur;
+	int depth = 0;
+
+	elements = g_strsplit(path, "/", 0);
+
+	for (cur = elements; *cur != NULL; cur++) {
+		if (**cur == '\0' || strcmp(*cur, ".") == 0)
+			continue;
+
+		if (strcmp(*cur, "..") == 0) {
+			depth--;
+			if (depth < 0)
+				break;
+			continue;
+		}
+
+		depth++;
+	}
+
+	g_strfreev(elements);
+
+	if (depth < 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static char *ftp_build_filename(struct ftp_session *ftp, const char *destname)
+{
+	char *filename;
+
+	/* DestName can either be relative or absolute (FTP style) */
+	if (destname[0] == '/')
+		filename = g_build_filename(obex_option_root_folder(),
+                                                                destname, NULL);
+	else
+		filename = g_build_filename(ftp->folder, destname, NULL);
+
+	if (is_valid_path(filename + strlen(obex_option_root_folder())))
+		return filename;
+
+	g_free(filename);
+
+	return NULL;
+}
+
+static int ftp_copy(struct ftp_session *ftp, const char *name,
+							const char *destname)
+{
+	char *source, *destination, *destdir;
+	int ret;
+
+	DBG("%p name %s destination %s", ftp, name, destname);
+
+	if (ftp->folder == NULL) {
+		error("No folder set");
+		return -ENOENT;
+	}
+
+	if (name == NULL || destname == NULL)
+		return -EINVAL;
+
+	destination = ftp_build_filename(ftp, destname);
+
+	if (destination == NULL)
+		return -EBADR;
+
+	destdir = g_path_get_dirname(destination);
+	ret = verify_path(destdir);
+	g_free(destdir);
+
+	if (ret < 0)
+		return ret;
+
+	source = g_build_filename(ftp->folder, name, NULL);
+
+	ret = obex_copy(ftp->os, source, destination);
+
+	g_free(source);
+	g_free(destination);
+
+	return ret;
+}
+
+static int ftp_move(struct ftp_session *ftp, const char *name,
+							const char *destname)
+{
+	char *source, *destination, *destdir;
+	int ret;
+
+	DBG("%p name %s destname %s", ftp, name, destname);
+
+	if (ftp->folder == NULL) {
+		error("No folder set");
+		return -ENOENT;
+	}
+
+	if (name == NULL || destname == NULL)
+		return -EINVAL;
+
+	destination = ftp_build_filename(ftp, destname);
+
+	if (destination == NULL)
+		return -EBADR;
+
+	destdir = g_path_get_dirname(destination);
+	ret = verify_path(destdir);
+	g_free(destdir);
+
+	if (ret < 0)
+		return ret;
+
+	source = g_build_filename(ftp->folder, name, NULL);
+
+	ret = obex_move(ftp->os, source, destination);
+
+	g_free(source);
+	g_free(destination);
+
+	return ret;
+}
+
+int ftp_action(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+	const char *name, *destname;
+	uint8_t action_id;
+
+	name = obex_get_name(os);
+	if (name == NULL || !is_filename(name))
+		return -EBADR;
+
+	destname = obex_get_destname(os);
+	action_id = obex_get_action_id(os);
+
+	DBG("%p action 0x%x", ftp, action_id);
+
+	switch (action_id) {
+	case 0x00: /* Copy Object */
+		return ftp_copy(ftp, name, destname);
+	case 0x01: /* Move/Rename Object */
+		return ftp_move(ftp, name, destname);
+	default:
+		return -ENOSYS;
+	}
+}
+
+void ftp_disconnect(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+
+	DBG("%p", ftp);
+
+	manager_unregister_session(os);
+
+	manager_unregister_transfer(ftp->transfer);
+
+	g_free(ftp->folder);
+	g_free(ftp);
+}
+
+static void ftp_progress(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+
+	manager_emit_transfer_progress(ftp->transfer);
+}
+
+static void ftp_reset(struct obex_session *os, void *user_data)
+{
+	struct ftp_session *ftp = user_data;
+
+	manager_emit_transfer_completed(ftp->transfer);
+}
+
+static struct obex_service_driver ftp = {
+	.name = "File Transfer server",
+	.service = OBEX_FTP,
+	.target = FTP_TARGET,
+	.target_size = TARGET_SIZE,
+	.connect = ftp_connect,
+	.progress = ftp_progress,
+	.get = ftp_get,
+	.put = ftp_put,
+	.chkput = ftp_chkput,
+	.setpath = ftp_setpath,
+	.action = ftp_action,
+	.disconnect = ftp_disconnect,
+	.reset = ftp_reset
+};
+
+static int ftp_init(void)
+{
+	return obex_service_driver_register(&ftp);
+}
+
+static void ftp_exit(void)
+{
+	obex_service_driver_unregister(&ftp);
+}
+
+OBEX_PLUGIN_DEFINE(ftp, ftp_init, ftp_exit)
diff --git a/bluez/repo/obexd/plugins/ftp.h b/bluez/repo/obexd/plugins/ftp.h
new file mode 100644
index 0000000..f06de84
--- /dev/null
+++ b/bluez/repo/obexd/plugins/ftp.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+void *ftp_connect(struct obex_session *os, int *err);
+int ftp_get(struct obex_session *os, void *user_data);
+int ftp_chkput(struct obex_session *os, void *user_data);
+int ftp_put(struct obex_session *os, void *user_data);
+int ftp_setpath(struct obex_session *os, void *user_data);
+void ftp_disconnect(struct obex_session *os, void *user_data);
+int ftp_action(struct obex_session *os, void *user_data);
diff --git a/bluez/repo/obexd/plugins/irmc.c b/bluez/repo/obexd/plugins/irmc.c
new file mode 100644
index 0000000..a3bbd60
--- /dev/null
+++ b/bluez/repo/obexd/plugins/irmc.c
@@ -0,0 +1,488 @@
+/*
+ *
+ *  OBEX IrMC Sync Server
+ *
+ *  Copyright (C) 2010  Marcel Mol <marcel@mesa.nl>
+ *
+ *  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 <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/mimetype.h"
+#include "phonebook.h"
+#include "filesystem.h"
+
+struct aparam_header {
+	uint8_t tag;
+	uint8_t len;
+	uint8_t val[0];
+} __attribute__ ((packed));
+
+#define DID_LEN 18
+
+struct irmc_session {
+	struct obex_session *os;
+	struct apparam_field *params;
+	uint16_t entries;
+	GString *buffer;
+	char sn[DID_LEN];
+	char did[DID_LEN];
+	char manu[DID_LEN];
+	char model[DID_LEN];
+	void *request;
+};
+
+#define IRMC_TARGET_SIZE 9
+
+static const guint8 IRMC_TARGET[IRMC_TARGET_SIZE] = {
+			0x49, 0x52, 0x4d, 0x43,  0x2d, 0x53, 0x59, 0x4e, 0x43 };
+
+/* FIXME:
+ * the IrMC specs state the first vcard should be the owner
+ * vcard. As there is no simple way to collect ownerdetails
+ * just create an empty vcard (which is allowed according to the
+ * specs).
+ */
+static const char *owner_vcard =
+		"BEGIN:VCARD\r\n"
+		"VERSION:2.1\r\n"
+		"N:\r\n"
+		"TEL:\r\n"
+		"X-IRMX-LUID:0\r\n"
+		"END:VCARD\r\n";
+
+static void phonebook_size_result(const char *buffer, size_t bufsize,
+					int vcards, int missed,
+					gboolean lastpart, void *user_data)
+{
+	struct irmc_session *irmc = user_data;
+
+	DBG("vcards %d", vcards);
+
+	irmc->params->maxlistcount = vcards;
+
+	if (irmc->request) {
+		phonebook_req_finalize(irmc->request);
+		irmc->request = NULL;
+	}
+}
+
+static void query_result(const char *buffer, size_t bufsize, int vcards,
+				int missed, gboolean lastpart, void *user_data)
+{
+	struct irmc_session *irmc = user_data;
+	const char *s, *t;
+
+	DBG("bufsize %zu vcards %d missed %d", bufsize, vcards, missed);
+
+	if (irmc->request) {
+		phonebook_req_finalize(irmc->request);
+		irmc->request = NULL;
+	}
+
+	/* first add a 'owner' vcard */
+	if (!irmc->buffer)
+		irmc->buffer = g_string_new(owner_vcard);
+	else
+		irmc->buffer = g_string_append(irmc->buffer, owner_vcard);
+
+	if (buffer == NULL)
+		goto done;
+
+	/* loop around buffer and add X-IRMC-LUID attribs */
+	s = buffer;
+	while ((t = strstr(s, "UID:")) != NULL) {
+		/* add up to UID: into buffer */
+		irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
+		/*
+		 * add UID: line into buffer
+		 * Not sure if UID is still needed if X-IRMC-LUID is there
+		 */
+		s = t;
+		t = strstr(s, "\r\n");
+		t += 2;
+		irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
+		/* add X-IRMC-LUID with same number as UID */
+		irmc->buffer = g_string_append_len(irmc->buffer,
+							"X-IRMC-LUID:", 12);
+		s += 4; /* point to uid number */
+		irmc->buffer = g_string_append_len(irmc->buffer, s, t-s);
+		s = t;
+	}
+	/* add remaining bit of buffer */
+	irmc->buffer = g_string_append(irmc->buffer, s);
+
+done:
+	obex_object_set_io_flags(irmc, G_IO_IN, 0);
+}
+
+static void *irmc_connect(struct obex_session *os, int *err)
+{
+	struct irmc_session *irmc;
+	struct apparam_field *param;
+	int ret;
+
+	DBG("");
+
+	manager_register_session(os);
+
+	irmc = g_new0(struct irmc_session, 1);
+	irmc->os = os;
+
+	/* FIXME:
+	 * Ideally get capabilities info here and use that to define
+	 * IrMC DID and SN etc parameters.
+	 * For now lets used hostname and some 'random' value
+	 */
+	gethostname(irmc->did, DID_LEN);
+	strncpy(irmc->sn, "12345", sizeof(irmc->sn) - 1);
+	strncpy(irmc->manu, "obex", sizeof(irmc->manu) - 1);
+	strncpy(irmc->model, "mymodel", sizeof(irmc->model) - 1);
+
+	/* We need to know the number of contact/cal/nt entries
+	 * somewhere so why not do it now.
+	 */
+	param = g_new0(struct apparam_field, 1);
+	param->maxlistcount = 0; /* to count the number of vcards... */
+	param->filter = 0x200085; /* UID TEL N VERSION */
+	irmc->params = param;
+	irmc->request = phonebook_pull(PB_CONTACTS, irmc->params,
+					phonebook_size_result, irmc, err);
+	ret = phonebook_pull_read(irmc->request);
+	if (err)
+		*err = ret;
+
+	return irmc;
+}
+
+static int irmc_get(struct obex_session *os, void *user_data)
+{
+	struct irmc_session *irmc = user_data;
+	const char *type = obex_get_type(os);
+	const char *name = obex_get_name(os);
+	char *path;
+	int ret;
+
+	DBG("name %s type %s irmc %p", name, type ? type : "NA", irmc);
+
+	path = g_strdup(name);
+
+	ret = obex_get_stream_start(os, path);
+
+	g_free(path);
+
+	return ret;
+}
+
+static void irmc_disconnect(struct obex_session *os, void *user_data)
+{
+	struct irmc_session *irmc = user_data;
+
+	DBG("");
+
+	manager_unregister_session(os);
+
+	if (irmc->params) {
+		if (irmc->params->searchval)
+			g_free(irmc->params->searchval);
+		g_free(irmc->params);
+	}
+
+	if (irmc->buffer)
+		g_string_free(irmc->buffer, TRUE);
+
+	g_free(irmc);
+}
+
+static int irmc_chkput(struct obex_session *os, void *user_data)
+{
+	DBG("");
+	/* Reject all PUTs */
+	return -EBADR;
+}
+
+static int irmc_open_devinfo(struct irmc_session *irmc)
+{
+	if (!irmc->buffer)
+		irmc->buffer = g_string_new("");
+
+	g_string_append_printf(irmc->buffer,
+				"MANU:%s\r\n"
+				"MOD:%s\r\n"
+				"SN:%s\r\n"
+				"IRMC-VERSION:1.1\r\n"
+				"PB-TYPE-TX:VCARD2.1\r\n"
+				"PB-TYPE-RX:NONE\r\n"
+				"CAL-TYPE-TX:NONE\r\n"
+				"CAL-TYPE-RX:NONE\r\n"
+				"MSG-TYPE-TX:NONE\r\n"
+				"MSG-TYPE-RX:NONE\r\n"
+				"NOTE-TYPE-TX:NONE\r\n"
+				"NOTE-TYPE-RX:NONE\r\n",
+				irmc->manu, irmc->model, irmc->sn);
+
+	return 0;
+}
+
+static int irmc_open_pb(struct irmc_session *irmc)
+{
+	int ret;
+
+	/* how can we tell if the vcard count call already finished? */
+	irmc->request = phonebook_pull(PB_CONTACTS, irmc->params,
+						query_result, irmc, &ret);
+	if (ret < 0) {
+		DBG("phonebook_pull failed...");
+		return ret;
+	}
+
+	ret = phonebook_pull_read(irmc->request);
+	if (ret < 0) {
+		DBG("phonebook_pull_read failed...");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int irmc_open_info(struct irmc_session *irmc)
+{
+	if (irmc->buffer == NULL)
+		irmc->buffer = g_string_new("");
+
+	g_string_printf(irmc->buffer, "Total-Records:%d\r\n"
+				"Maximum-Records:%d\r\n"
+				"IEL:2\r\n"
+				"DID:%s\r\n",
+				irmc->params->maxlistcount,
+				irmc->params->maxlistcount, irmc->did);
+
+	return 0;
+}
+
+static int irmc_open_cc(struct irmc_session *irmc)
+{
+	if (irmc->buffer == NULL)
+		irmc->buffer = g_string_new("");
+
+	g_string_printf(irmc->buffer, "%d\r\n", irmc->params->maxlistcount);
+
+	return 0;
+}
+
+static int irmc_open_cal(struct irmc_session *irmc)
+{
+	/* no suport yet. Just return an empty buffer. cal.vcs */
+	DBG("unsupported, returning empty buffer");
+
+	if (!irmc->buffer)
+		irmc->buffer = g_string_new("");
+
+	return 0;
+}
+
+static int irmc_open_nt(struct irmc_session *irmc)
+{
+	/* no suport yet. Just return an empty buffer. nt.vnt */
+	DBG("unsupported, returning empty buffer");
+
+	if (!irmc->buffer)
+		irmc->buffer = g_string_new("");
+
+	return 0;
+}
+
+static int irmc_open_luid(struct irmc_session *irmc)
+{
+	if (irmc->buffer == NULL)
+		irmc->buffer = g_string_new("");
+
+	DBG("changelog request, force whole book");
+	g_string_printf(irmc->buffer, "SN:%s\r\n"
+					"DID:%s\r\n"
+					"Total-Records:%d\r\n"
+					"Maximum-Records:%d\r\n"
+					"*\r\n",
+					irmc->sn, irmc->did,
+					irmc->params->maxlistcount,
+					irmc->params->maxlistcount);
+
+	return 0;
+}
+
+static void *irmc_open(const char *name, int oflag, mode_t mode, void *context,
+							size_t *size, int *err)
+{
+	struct irmc_session *irmc = context;
+	int ret = 0;
+	char *path;
+
+	DBG("name %s context %p", name, context);
+
+	if (oflag != O_RDONLY) {
+		ret = -EPERM;
+		goto fail;
+	}
+
+	if (name == NULL) {
+		ret = -EBADR;
+		goto fail;
+	}
+
+	/* Always contains the absolute path */
+	if (g_path_is_absolute(name))
+		path = g_strdup(name);
+	else
+		path = g_build_filename("/", name, NULL);
+
+	if (g_str_equal(path, PB_DEVINFO))
+		ret = irmc_open_devinfo(irmc);
+	else if (g_str_equal(path, PB_CONTACTS))
+		ret = irmc_open_pb(irmc);
+	else if (g_str_equal(path, PB_INFO_LOG))
+		ret = irmc_open_info(irmc);
+	else if (g_str_equal(path, PB_CC_LOG))
+		ret = irmc_open_cc(irmc);
+	else if (g_str_has_prefix(path, PB_CALENDAR_FOLDER))
+		ret = irmc_open_cal(irmc);
+	else if (g_str_has_prefix(path, PB_NOTES_FOLDER))
+		ret = irmc_open_nt(irmc);
+	else if (g_str_has_prefix(path, PB_LUID_FOLDER))
+		ret = irmc_open_luid(irmc);
+	else
+		ret = -EBADR;
+
+	g_free(path);
+
+	if (ret == 0)
+		return irmc;
+
+fail:
+	if (err)
+		*err = ret;
+
+	return NULL;
+}
+
+static int irmc_close(void *object)
+{
+	struct irmc_session *irmc = object;
+
+	DBG("");
+
+	if (irmc->buffer) {
+		g_string_free(irmc->buffer, TRUE);
+		irmc->buffer = NULL;
+	}
+
+	if (irmc->request) {
+		phonebook_req_finalize(irmc->request);
+		irmc->request = NULL;
+	}
+
+	return 0;
+}
+
+static ssize_t irmc_read(void *object, void *buf, size_t count)
+{
+	struct irmc_session *irmc = object;
+	int len;
+
+	DBG("buffer %p count %zu", irmc->buffer, count);
+	if (!irmc->buffer)
+                return -EAGAIN;
+
+	len = string_read(irmc->buffer, buf, count);
+	DBG("returning %d bytes", len);
+	return len;
+}
+
+static struct obex_mime_type_driver irmc_driver = {
+	.target = IRMC_TARGET,
+	.target_size = IRMC_TARGET_SIZE,
+	.open = irmc_open,
+	.close = irmc_close,
+	.read = irmc_read,
+};
+
+static struct obex_service_driver irmc = {
+	.name = "IRMC Sync server",
+	.service = OBEX_IRMC,
+	.target = IRMC_TARGET,
+	.target_size = IRMC_TARGET_SIZE,
+	.connect = irmc_connect,
+	.get = irmc_get,
+	.disconnect = irmc_disconnect,
+	.chkput = irmc_chkput
+};
+
+static int irmc_init(void)
+{
+	int err;
+
+	DBG("");
+	err = phonebook_init();
+	if (err < 0)
+		return err;
+
+	err = obex_mime_type_driver_register(&irmc_driver);
+	if (err < 0)
+		goto fail_mime_irmc;
+
+	err = obex_service_driver_register(&irmc);
+	if (err < 0)
+		goto fail_irmc_reg;
+
+	return 0;
+
+fail_irmc_reg:
+	obex_mime_type_driver_unregister(&irmc_driver);
+fail_mime_irmc:
+	phonebook_exit();
+
+	return err;
+}
+
+static void irmc_exit(void)
+{
+	DBG("");
+	obex_service_driver_unregister(&irmc);
+	obex_mime_type_driver_unregister(&irmc_driver);
+	phonebook_exit();
+}
+
+OBEX_PLUGIN_DEFINE(irmc, irmc_init, irmc_exit)
diff --git a/bluez/repo/obexd/plugins/mas.c b/bluez/repo/obexd/plugins/mas.c
new file mode 100644
index 0000000..ef67ec5
--- /dev/null
+++ b/bluez/repo/obexd/plugins/mas.c
@@ -0,0 +1,933 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation
+ *
+ *
+ *  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 <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/time.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/map_ap.h"
+#include "filesystem.h"
+#include "messages.h"
+
+#define READ_STATUS_REQ 0
+#define DELETE_STATUS_REQ 1
+
+#define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+
+/* Building blocks for x-obex/folder-listing */
+#define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
+#define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
+#define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
+#define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
+#define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
+#define FL_BODY_END "</folder-listing>"
+
+#define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
+#define ML_BODY_END "</MAP-msg-listing>"
+
+struct mas_session {
+	struct mas_request *request;
+	void *backend_data;
+	gboolean finished;
+	gboolean nth_call;
+	GString *buffer;
+	GObexApparam *inparams;
+	GObexApparam *outparams;
+	gboolean ap_sent;
+	uint8_t notification_status;
+};
+
+static const uint8_t MAS_TARGET[TARGET_SIZE] = {
+			0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb,
+			0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66  };
+
+static int get_params(struct obex_session *os, struct mas_session *mas)
+{
+	const uint8_t *buffer;
+	ssize_t size;
+
+	size = obex_get_apparam(os, &buffer);
+	if (size <= 0)
+		return 0;
+
+	mas->inparams = g_obex_apparam_decode(buffer, size);
+	if (mas->inparams == NULL) {
+		DBG("Error when parsing parameters!");
+		return -EBADR;
+	}
+
+	return 0;
+}
+
+static void reset_request(struct mas_session *mas)
+{
+	if (mas->buffer) {
+		g_string_free(mas->buffer, TRUE);
+		mas->buffer = NULL;
+	}
+
+	if (mas->inparams) {
+		g_obex_apparam_free(mas->inparams);
+		mas->inparams = NULL;
+	}
+
+	if (mas->outparams) {
+		g_obex_apparam_free(mas->outparams);
+		mas->outparams = NULL;
+	}
+
+	mas->nth_call = FALSE;
+	mas->finished = FALSE;
+	mas->ap_sent = FALSE;
+}
+
+static void mas_clean(struct mas_session *mas)
+{
+	reset_request(mas);
+	g_free(mas);
+}
+
+static void *mas_connect(struct obex_session *os, int *err)
+{
+	struct mas_session *mas;
+
+	DBG("");
+
+	mas = g_new0(struct mas_session, 1);
+
+	*err = messages_connect(&mas->backend_data);
+	if (*err < 0)
+		goto failed;
+
+	manager_register_session(os);
+
+	return mas;
+
+failed:
+	g_free(mas);
+
+	return NULL;
+}
+
+static void mas_disconnect(struct obex_session *os, void *user_data)
+{
+	struct mas_session *mas = user_data;
+
+	DBG("");
+
+	manager_unregister_session(os);
+	messages_disconnect(mas->backend_data);
+
+	mas_clean(mas);
+}
+
+static int mas_get(struct obex_session *os, void *user_data)
+{
+	struct mas_session *mas = user_data;
+	const char *type = obex_get_type(os);
+	const char *name = obex_get_name(os);
+	int ret;
+
+	DBG("GET: name %s type %s mas %p",
+			name, type, mas);
+
+	if (type == NULL)
+		return -EBADR;
+
+	ret = get_params(os, mas);
+	if (ret < 0)
+		goto failed;
+
+	ret = obex_get_stream_start(os, name);
+	if (ret < 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	reset_request(mas);
+
+	return ret;
+}
+
+static int mas_put(struct obex_session *os, void *user_data)
+{
+	struct mas_session *mas = user_data;
+	const char *type = obex_get_type(os);
+	const char *name = obex_get_name(os);
+	int ret;
+
+	DBG("PUT: name %s type %s mas %p", name, type, mas);
+
+	if (type == NULL)
+		return -EBADR;
+
+	ret = get_params(os, mas);
+	if (ret < 0)
+		goto failed;
+
+	ret = obex_put_stream_start(os, name);
+	if (ret < 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	reset_request(mas);
+
+	return ret;
+}
+
+/* FIXME: Preserve whitespaces */
+static void g_string_append_escaped_printf(GString *string,
+						const char *format, ...)
+{
+	va_list ap;
+	char *escaped;
+
+	va_start(ap, format);
+	escaped = g_markup_vprintf_escaped(format, ap);
+	g_string_append(string, escaped);
+	g_free(escaped);
+	va_end(ap);
+}
+
+static gchar *get_mse_timestamp(void)
+{
+	struct timeval time_val;
+	struct tm ltime;
+	gchar *local_ts;
+	char sign;
+
+	if (gettimeofday(&time_val, NULL) < 0)
+		return NULL;
+
+	if (!localtime_r(&time_val.tv_sec, &ltime))
+		return NULL;
+
+	if (difftime(mktime(localtime(&time_val.tv_sec)),
+				mktime(gmtime(&time_val.tv_sec))) < 0)
+		sign = '+';
+	else
+		sign = '-';
+
+	local_ts = g_strdup_printf("%04d%02d%02dT%02d%02d%02d%c%2ld%2ld",
+					ltime.tm_year + 1900, ltime.tm_mon + 1,
+					ltime.tm_mday, ltime.tm_hour,
+					ltime.tm_min, ltime.tm_sec, sign,
+					ltime.tm_gmtoff/3600,
+					(ltime.tm_gmtoff%3600)/60);
+
+	return local_ts;
+}
+
+static const char *yesorno(gboolean a)
+{
+	if (a)
+		return "yes";
+
+	return "no";
+}
+
+static void get_messages_listing_cb(void *session, int err, uint16_t size,
+					gboolean newmsg,
+					const struct messages_message *entry,
+					void *user_data)
+{
+	struct mas_session *mas = user_data;
+	uint16_t max = 1024;
+	gchar *mse_time;
+
+	if (err < 0 && err != -EAGAIN) {
+		obex_object_set_io_flags(mas, G_IO_ERR, err);
+		return;
+	}
+
+	if (mas->inparams)
+		g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT,
+									&max);
+
+	if (max == 0) {
+		if (!entry)
+			mas->finished = TRUE;
+
+		goto proceed;
+	}
+
+	if (!mas->nth_call) {
+		g_string_append(mas->buffer, ML_BODY_BEGIN);
+		mas->nth_call = TRUE;
+	}
+
+	if (!entry) {
+		g_string_append(mas->buffer, ML_BODY_END);
+		mas->finished = TRUE;
+
+		goto proceed;
+	}
+
+	g_string_append(mas->buffer, "<msg");
+
+	g_string_append_escaped_printf(mas->buffer, " handle=\"%s\"",
+								entry->handle);
+
+	if (entry->mask & PMASK_SUBJECT)
+		g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"",
+				entry->subject);
+
+	if (entry->mask & PMASK_DATETIME)
+		g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"",
+				entry->datetime);
+
+	if (entry->mask & PMASK_SENDER_NAME)
+		g_string_append_escaped_printf(mas->buffer,
+						" sender_name=\"%s\"",
+						entry->sender_name);
+
+	if (entry->mask & PMASK_SENDER_ADDRESSING)
+		g_string_append_escaped_printf(mas->buffer,
+						" sender_addressing=\"%s\"",
+						entry->sender_addressing);
+
+	if (entry->mask & PMASK_REPLYTO_ADDRESSING)
+		g_string_append_escaped_printf(mas->buffer,
+						" replyto_addressing=\"%s\"",
+						entry->replyto_addressing);
+
+	if (entry->mask & PMASK_RECIPIENT_NAME)
+		g_string_append_escaped_printf(mas->buffer,
+						" recipient_name=\"%s\"",
+						entry->recipient_name);
+
+	if (entry->mask & PMASK_RECIPIENT_ADDRESSING)
+		g_string_append_escaped_printf(mas->buffer,
+						" recipient_addressing=\"%s\"",
+						entry->recipient_addressing);
+
+	if (entry->mask & PMASK_TYPE)
+		g_string_append_escaped_printf(mas->buffer, " type=\"%s\"",
+				entry->type);
+
+	if (entry->mask & PMASK_RECEPTION_STATUS)
+		g_string_append_escaped_printf(mas->buffer,
+						" reception_status=\"%s\"",
+						entry->reception_status);
+
+	if (entry->mask & PMASK_SIZE)
+		g_string_append_escaped_printf(mas->buffer, " size=\"%s\"",
+				entry->size);
+
+	if (entry->mask & PMASK_ATTACHMENT_SIZE)
+		g_string_append_escaped_printf(mas->buffer,
+						" attachment_size=\"%s\"",
+						entry->attachment_size);
+
+	if (entry->mask & PMASK_TEXT)
+		g_string_append_escaped_printf(mas->buffer, " text=\"%s\"",
+				yesorno(entry->text));
+
+	if (entry->mask & PMASK_READ)
+		g_string_append_escaped_printf(mas->buffer, " read=\"%s\"",
+				yesorno(entry->read));
+
+	if (entry->mask & PMASK_SENT)
+		g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"",
+				yesorno(entry->sent));
+
+	if (entry->mask & PMASK_PROTECTED)
+		g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"",
+				yesorno(entry->protect));
+
+	if (entry->mask & PMASK_PRIORITY)
+		g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"",
+				yesorno(entry->priority));
+
+	g_string_append(mas->buffer, "/>\n");
+
+proceed:
+	if (!entry) {
+		mas->outparams = g_obex_apparam_set_uint16(mas->outparams,
+						MAP_AP_MESSAGESLISTINGSIZE,
+						size);
+		mas->outparams = g_obex_apparam_set_uint8(mas->outparams,
+						MAP_AP_NEWMESSAGE,
+						newmsg ? 1 : 0);
+		/* Response to report the local time of MSE */
+		mse_time = get_mse_timestamp();
+		if (mse_time) {
+			g_obex_apparam_set_string(mas->outparams,
+						MAP_AP_MSETIME, mse_time);
+			g_free(mse_time);
+		}
+	}
+
+	if (err != -EAGAIN)
+		obex_object_set_io_flags(mas, G_IO_IN, 0);
+}
+
+static void get_message_cb(void *session, int err, gboolean fmore,
+					const char *chunk, void *user_data)
+{
+	struct mas_session *mas = user_data;
+
+	DBG("");
+
+	if (err < 0 && err != -EAGAIN) {
+		obex_object_set_io_flags(mas, G_IO_ERR, err);
+		return;
+	}
+
+	if (!chunk) {
+		mas->finished = TRUE;
+		goto proceed;
+	}
+
+	g_string_append(mas->buffer, chunk);
+
+proceed:
+	if (err != -EAGAIN)
+		obex_object_set_io_flags(mas, G_IO_IN, 0);
+}
+
+static void get_folder_listing_cb(void *session, int err, uint16_t size,
+					const char *name, void *user_data)
+{
+	struct mas_session *mas = user_data;
+	uint16_t max = 1024;
+
+	if (err < 0 && err != -EAGAIN) {
+		obex_object_set_io_flags(mas, G_IO_ERR, err);
+		return;
+	}
+
+	if (mas->inparams)
+		g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT,
+									&max);
+
+	if (max == 0) {
+		if (err != -EAGAIN)
+			mas->outparams = g_obex_apparam_set_uint16(
+						mas->outparams,
+						MAP_AP_FOLDERLISTINGSIZE,
+						size);
+
+		if (!name)
+			mas->finished = TRUE;
+
+		goto proceed;
+	}
+
+	if (!mas->nth_call) {
+		g_string_append(mas->buffer, XML_DECL);
+		g_string_append(mas->buffer, FL_DTD);
+		if (!name) {
+			g_string_append(mas->buffer, FL_BODY_EMPTY);
+			mas->finished = TRUE;
+			goto proceed;
+		}
+		g_string_append(mas->buffer, FL_BODY_BEGIN);
+		mas->nth_call = TRUE;
+	}
+
+	if (!name) {
+		g_string_append(mas->buffer, FL_BODY_END);
+		mas->finished = TRUE;
+		goto proceed;
+	}
+
+	if (g_strcmp0(name, "..") == 0)
+		g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
+	else
+		g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
+									name);
+
+proceed:
+	if (err != -EAGAIN)
+		obex_object_set_io_flags(mas, G_IO_IN, err);
+}
+
+static void set_status_cb(void *session, int err, void *user_data)
+{
+	struct mas_session *mas = user_data;
+
+	DBG("");
+
+	mas->finished = TRUE;
+
+	if (err < 0)
+		obex_object_set_io_flags(mas, G_IO_ERR, err);
+	else
+		obex_object_set_io_flags(mas, G_IO_OUT, 0);
+}
+
+static int mas_setpath(struct obex_session *os, void *user_data)
+{
+	const char *name;
+	const uint8_t *nonhdr;
+	struct mas_session *mas = user_data;
+
+	if (obex_get_non_header_data(os, &nonhdr) != 2) {
+		error("Set path failed: flag and constants not found!");
+		return -EBADR;
+	}
+
+	name = obex_get_name(os);
+
+	DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
+
+	if ((nonhdr[0] & 0x02) != 0x02) {
+		DBG("Error: requested directory creation");
+		return -EBADR;
+	}
+
+	return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
+}
+
+static void *folder_listing_open(const char *name, int oflag, mode_t mode,
+				void *driver_data, size_t *size, int *err)
+{
+	struct mas_session *mas = driver_data;
+	/* 1024 is the default when there was no MaxListCount sent */
+	uint16_t max = 1024;
+	uint16_t offset = 0;
+
+	if (oflag != O_RDONLY) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	DBG("name = %s", name);
+
+	if (mas->inparams) {
+		g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT,
+									&max);
+		g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET,
+								&offset);
+	}
+
+	*err = messages_get_folder_listing(mas->backend_data, name, max,
+					offset, get_folder_listing_cb, mas);
+
+	mas->buffer = g_string_new("");
+
+	if (*err < 0)
+		return NULL;
+	else
+		return mas;
+}
+
+static void *msg_listing_open(const char *name, int oflag, mode_t mode,
+				void *driver_data, size_t *size, int *err)
+{
+	struct mas_session *mas = driver_data;
+	struct messages_filter filter = { 0, };
+	/* 1024 is the default when there was no MaxListCount sent */
+	uint16_t max = 1024;
+	uint16_t offset = 0;
+	/* If MAP client does not specify the subject length,
+	   then subject_len = 0 and subject should be sent unaltered. */
+	uint8_t subject_len = 0;
+
+	DBG("");
+
+	if (oflag != O_RDONLY) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	if (!mas->inparams)
+		goto done;
+
+	g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
+	g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
+	g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH,
+						&subject_len);
+
+	g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK,
+						&filter.parameter_mask);
+	g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE,
+						&filter.type);
+	filter.period_begin = g_obex_apparam_get_string(mas->inparams,
+						MAP_AP_FILTERPERIODBEGIN);
+	filter.period_end = g_obex_apparam_get_string(mas->inparams,
+						MAP_AP_FILTERPERIODEND);
+	g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS,
+						&filter.read_status);
+	filter.recipient = g_obex_apparam_get_string(mas->inparams,
+						MAP_AP_FILTERRECIPIENT);
+	filter.originator = g_obex_apparam_get_string(mas->inparams,
+						MAP_AP_FILTERORIGINATOR);
+	g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY,
+						&filter.priority);
+
+done:
+	*err = messages_get_messages_listing(mas->backend_data, name, max,
+			offset, subject_len, &filter,
+			get_messages_listing_cb, mas);
+
+	mas->buffer = g_string_new("");
+
+	if (*err < 0)
+		return NULL;
+	else
+		return mas;
+}
+
+static void *message_open(const char *name, int oflag, mode_t mode,
+				void *driver_data, size_t *size, int *err)
+{
+	struct mas_session *mas = driver_data;
+
+	DBG("");
+
+	if (oflag != O_RDONLY) {
+		DBG("Message pushing unsupported");
+		*err = -ENOSYS;
+
+		return NULL;
+	}
+
+	*err = messages_get_message(mas->backend_data, name, 0,
+			get_message_cb, mas);
+
+	mas->buffer = g_string_new("");
+
+	if (*err < 0)
+		return NULL;
+	else
+		return mas;
+}
+
+static void *message_update_open(const char *name, int oflag, mode_t mode,
+					void *driver_data, size_t *size,
+					int *err)
+{
+	struct mas_session *mas = driver_data;
+
+	DBG("");
+
+	if (oflag == O_RDONLY) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	*err = messages_update_inbox(mas->backend_data, set_status_cb, mas);
+	if (*err < 0)
+		return NULL;
+	else
+		return mas;
+}
+
+static void *message_set_status_open(const char *name, int oflag, mode_t mode,
+					void *driver_data, size_t *size,
+					int *err)
+
+{
+	struct mas_session *mas = driver_data;
+	uint8_t indicator;
+	uint8_t value;
+
+	DBG("");
+
+	if (oflag == O_RDONLY) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR,
+								&indicator)) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE,
+								&value)) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	if (indicator == READ_STATUS_REQ)
+		*err = messages_set_read(mas->backend_data, name, value,
+							set_status_cb, mas);
+	else if (indicator == DELETE_STATUS_REQ)
+		*err = messages_set_delete(mas->backend_data, name, value,
+							set_status_cb, mas);
+	else
+		*err = -EBADR;
+
+	if (*err < 0)
+		return NULL;
+
+	return mas;
+}
+
+static ssize_t any_get_next_header(void *object, void *buf, size_t mtu,
+								uint8_t *hi)
+{
+	struct mas_session *mas = object;
+
+	DBG("");
+
+	if (mas->buffer->len == 0 && !mas->finished)
+		return -EAGAIN;
+
+	*hi = G_OBEX_HDR_APPARAM;
+
+	if (mas->ap_sent)
+		return 0;
+
+	mas->ap_sent = TRUE;
+	if (!mas->outparams)
+		return 0;
+
+	return g_obex_apparam_encode(mas->outparams, buf, mtu);
+}
+
+static void *any_open(const char *name, int oflag, mode_t mode,
+				void *driver_data, size_t *size, int *err)
+{
+	DBG("");
+
+	*err = -ENOSYS;
+
+	return NULL;
+}
+
+static ssize_t any_write(void *object, const void *buf, size_t count)
+{
+	DBG("");
+
+	return count;
+}
+
+static ssize_t any_read(void *obj, void *buf, size_t count)
+{
+	struct mas_session *mas = obj;
+	ssize_t len;
+
+	DBG("");
+
+	len = string_read(mas->buffer, buf, count);
+
+	if (len == 0 && !mas->finished)
+		return -EAGAIN;
+
+	return len;
+}
+
+static int any_close(void *obj)
+{
+	struct mas_session *mas = obj;
+
+	DBG("");
+
+	if (!mas->finished)
+		messages_abort(mas->backend_data);
+
+	reset_request(mas);
+
+	return 0;
+}
+
+static void *notification_registration_open(const char *name, int oflag,
+						mode_t mode, void *driver_data,
+						size_t *size, int *err)
+{
+	struct mas_session *mas = driver_data;
+	uint8_t status;
+
+	DBG("");
+
+	if (O_RDONLY) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_NOTIFICATIONSTATUS,
+								&status)) {
+		*err = -EBADR;
+		return NULL;
+	}
+
+	mas->notification_status = status;
+	mas->finished = TRUE;
+	*err = 0;
+
+	return mas;
+}
+
+static struct obex_service_driver mas = {
+	.name = "Message Access server",
+	.service = OBEX_MAS,
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.connect = mas_connect,
+	.get = mas_get,
+	.put = mas_put,
+	.setpath = mas_setpath,
+	.disconnect = mas_disconnect,
+};
+
+static struct obex_mime_type_driver mime_map = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = NULL,
+	.open = any_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver mime_message = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/message",
+	.open = message_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver mime_folder_listing = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-obex/folder-listing",
+	.get_next_header = any_get_next_header,
+	.open = folder_listing_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver mime_msg_listing = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/MAP-msg-listing",
+	.get_next_header = any_get_next_header,
+	.open = msg_listing_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver mime_notification_registration = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/MAP-NotificationRegistration",
+	.open = notification_registration_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver mime_message_status = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/messageStatus",
+	.open = message_set_status_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver mime_message_update = {
+	.target = MAS_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/MAP-messageUpdate",
+	.open = message_update_open,
+	.close = any_close,
+	.read = any_read,
+	.write = any_write,
+};
+
+static struct obex_mime_type_driver *map_drivers[] = {
+	&mime_map,
+	&mime_message,
+	&mime_folder_listing,
+	&mime_msg_listing,
+	&mime_notification_registration,
+	&mime_message_status,
+	&mime_message_update,
+	NULL
+};
+
+static int mas_init(void)
+{
+	int err;
+	int i;
+
+	err = messages_init();
+	if (err < 0)
+		return err;
+
+	for (i = 0; map_drivers[i] != NULL; ++i) {
+		err = obex_mime_type_driver_register(map_drivers[i]);
+		if (err < 0)
+			goto failed;
+	}
+
+	err = obex_service_driver_register(&mas);
+	if (err < 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	for (--i; i >= 0; --i)
+		obex_mime_type_driver_unregister(map_drivers[i]);
+
+	messages_exit();
+
+	return err;
+}
+
+static void mas_exit(void)
+{
+	int i;
+
+	obex_service_driver_unregister(&mas);
+
+	for (i = 0; map_drivers[i] != NULL; ++i)
+		obex_mime_type_driver_unregister(map_drivers[i]);
+
+	messages_exit();
+}
+
+OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)
diff --git a/bluez/repo/obexd/plugins/messages-dummy.c b/bluez/repo/obexd/plugins/messages-dummy.c
new file mode 100644
index 0000000..765f08d
--- /dev/null
+++ b/bluez/repo/obexd/plugins/messages-dummy.c
@@ -0,0 +1,376 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation
+ *
+ *
+ *  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 <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "obexd/src/log.h"
+
+#include "messages.h"
+
+static char *root_folder = NULL;
+
+struct session {
+	char *cwd;
+	char *cwd_absolute;
+	void *request;
+};
+
+struct folder_listing_data {
+	struct session *session;
+	const char *name;
+	uint16_t max;
+	uint16_t offset;
+	messages_folder_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
+ * test suite requiring folder listing to be in that order.
+ */
+static int folder_names_cmp(gconstpointer a, gconstpointer b,
+						gpointer user_data)
+{
+	static const char *order[] = {
+		"inbox", "outbox", "sent", "deleted", "draft", NULL
+	};
+	struct session *session = user_data;
+	int ia, ib;
+
+	if (g_strcmp0(session->cwd, "telecom/msg") == 0) {
+		for (ia = 0; order[ia]; ia++) {
+			if (g_strcmp0(a, order[ia]) == 0)
+				break;
+		}
+		for (ib = 0; order[ib]; ib++) {
+			if (g_strcmp0(b, order[ib]) == 0)
+				break;
+		}
+		if (ia != ib)
+			return ia - ib;
+	}
+
+	return g_strcmp0(a, b);
+}
+
+static char *get_next_subdir(DIR *dp, char *path)
+{
+	struct dirent *ep;
+	char *abs, *name;
+
+	for (;;) {
+		if ((ep = readdir(dp)) == NULL)
+			return NULL;
+
+		if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
+			continue;
+
+		abs = g_build_filename(path, ep->d_name, NULL);
+
+		if (g_file_test(abs, G_FILE_TEST_IS_DIR)) {
+			g_free(abs);
+			break;
+		}
+
+		g_free(abs);
+	}
+
+	name = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
+
+	if (name == NULL) {
+		DBG("g_filename_to_utf8(): invalid filename");
+		return NULL;
+	}
+
+	return name;
+}
+
+static ssize_t get_subdirs(struct folder_listing_data *fld, GSList **list)
+{
+	DIR *dp;
+	char *path, *name;
+	size_t n;
+
+	path = g_build_filename(fld->session->cwd_absolute, fld->name, NULL);
+	dp = opendir(path);
+
+	if (dp == NULL) {
+		int err = -errno;
+
+		DBG("opendir(): %d, %s", -err, strerror(-err));
+		g_free(path);
+
+		return err;
+	}
+
+	n = 0;
+
+	while ((name = get_next_subdir(dp, path)) != NULL) {
+		n++;
+		if (fld->max > 0)
+			*list = g_slist_prepend(*list, name);
+	}
+
+	closedir(dp);
+	g_free(path);
+
+	*list = g_slist_sort_with_data(*list, folder_names_cmp, fld->session);
+
+	return n;
+}
+
+static void return_folder_listing(struct folder_listing_data *fld, GSList *list)
+{
+	struct session *session = fld->session;
+	GSList *cur;
+	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)
+			break;
+	}
+
+	for (; cur != NULL && num < fld->max; cur = cur->next, num++)
+		fld->callback(session, -EAGAIN, 0, cur->data, fld->user_data);
+
+	fld->callback(session, 0, 0, NULL, fld->user_data);
+}
+
+static gboolean get_folder_listing(void *d)
+{
+	struct folder_listing_data *fld = d;
+	ssize_t n;
+	GSList *list = NULL;
+
+	n = get_subdirs(fld, &list);
+
+	if (n < 0) {
+		fld->callback(fld->session, n, 0, NULL, fld->user_data);
+		return FALSE;
+	}
+
+	if (fld->max == 0) {
+		fld->callback(fld->session, 0, n, NULL, fld->user_data);
+		return FALSE;
+	}
+
+	return_folder_listing(fld, list);
+	g_slist_free_full(list, g_free);
+
+	return FALSE;
+}
+
+int messages_init(void)
+{
+	char *tmp;
+
+	if (root_folder)
+		return 0;
+
+	tmp = getenv("MAP_ROOT");
+	if (tmp) {
+		root_folder = g_strdup(tmp);
+		return 0;
+	}
+
+	tmp = getenv("HOME");
+	if (!tmp)
+		return -ENOENT;
+
+	root_folder = g_build_filename(tmp, "map-messages", NULL);
+
+	return 0;
+}
+
+void messages_exit(void)
+{
+	g_free(root_folder);
+	root_folder = NULL;
+}
+
+int messages_connect(void **s)
+{
+	struct session *session;
+
+	session = g_new0(struct session, 1);
+	session->cwd = g_strdup("");
+	session->cwd_absolute = g_strdup(root_folder);
+
+	*s = session;
+
+	return 0;
+}
+
+void messages_disconnect(void *s)
+{
+	struct session *session = s;
+
+	g_free(session->cwd);
+	g_free(session->cwd_absolute);
+	g_free(session);
+}
+
+int messages_set_notification_registration(void *session,
+		void (*send_event)(void *session,
+			const struct messages_event *event, void *user_data),
+		void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_set_folder(void *s, const char *name, gboolean cdup)
+{
+	struct session *session = s;
+	char *newrel = NULL;
+	char *newabs;
+	char *tmp;
+
+	if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
+		return -EBADR;
+
+	if (cdup) {
+		if (session->cwd[0] == 0)
+			return -ENOENT;
+
+		newrel = g_path_get_dirname(session->cwd);
+
+		/* We use empty string for indication of the root directory */
+		if (newrel[0] == '.' && newrel[1] == 0)
+			newrel[0] = 0;
+	}
+
+	tmp = newrel;
+	if (!cdup && (!name || name[0] == 0))
+		newrel = g_strdup("");
+	else
+		newrel = g_build_filename(newrel ? newrel : session->cwd, name,
+				NULL);
+	g_free(tmp);
+
+	newabs = g_build_filename(root_folder, newrel, NULL);
+
+	if (!g_file_test(newabs, G_FILE_TEST_IS_DIR)) {
+		g_free(newrel);
+		g_free(newabs);
+		return -ENOENT;
+	}
+
+	g_free(session->cwd);
+	session->cwd = newrel;
+
+	g_free(session->cwd_absolute);
+	session->cwd_absolute = newabs;
+
+	return 0;
+}
+
+int messages_get_folder_listing(void *s, const char *name, uint16_t max,
+					uint16_t offset,
+					messages_folder_listing_cb callback,
+					void *user_data)
+{
+	struct session *session =  s;
+	struct folder_listing_data *fld;
+
+	fld = g_new0(struct folder_listing_data, 1);
+	fld->session = session;
+	fld->name = name;
+	fld->max = max;
+	fld->offset = offset;
+	fld->callback = callback;
+	fld->user_data = user_data;
+
+	session->request = fld;
+
+	g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_folder_listing,
+								fld, g_free);
+
+	return 0;
+}
+
+int messages_get_messages_listing(void *session, const char *name,
+				uint16_t max, uint16_t offset,
+				uint8_t subject_len,
+				const struct messages_filter *filter,
+				messages_get_messages_listing_cb callback,
+				void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_get_message(void *session, const char *handle,
+					unsigned long flags,
+					messages_get_message_cb callback,
+					void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_update_inbox(void *session, messages_status_cb callback,
+							void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_set_read(void *session, const char *handle, uint8_t value,
+				messages_status_cb callback, void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_set_delete(void *session, const char *handle, uint8_t value,
+				messages_status_cb callback, void *user_data)
+{
+	return -ENOSYS;
+}
+
+void messages_abort(void *s)
+{
+	struct session *session = s;
+
+	if (session->request) {
+		g_idle_remove_by_data(session->request);
+		session->request = NULL;
+	}
+}
diff --git a/bluez/repo/obexd/plugins/messages-tracker.c b/bluez/repo/obexd/plugins/messages-tracker.c
new file mode 100644
index 0000000..60f3a80
--- /dev/null
+++ b/bluez/repo/obexd/plugins/messages-tracker.c
@@ -0,0 +1,345 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+
+#include "messages.h"
+
+struct message_folder {
+	char *name;
+	GSList *subfolders;
+	char *query;
+};
+
+struct session {
+	char *cwd;
+	struct message_folder *folder;
+	char *name;
+	uint16_t max;
+	uint16_t offset;
+	void *user_data;
+	void (*folder_list_cb)(void *session, int err, uint16_t size,
+					const char *name, void *user_data);
+};
+
+static struct message_folder *folder_tree = NULL;
+
+static struct message_folder *get_folder(const char *folder)
+{
+	GSList *folders = folder_tree->subfolders;
+	struct message_folder *last = NULL;
+	char **path;
+	int i;
+
+	if (g_strcmp0(folder, "/") == 0)
+		return folder_tree;
+
+	path = g_strsplit(folder, "/", 0);
+
+	for (i = 1; path[i] != NULL; i++) {
+		gboolean match_found = FALSE;
+		GSList *l;
+
+		for (l = folders; l != NULL; l = g_slist_next(l)) {
+			struct message_folder *folder = l->data;
+
+			if (g_strcmp0(folder->name, path[i]) == 0) {
+				match_found = TRUE;
+				last = l->data;
+				folders = folder->subfolders;
+				break;
+			}
+		}
+
+		if (!match_found) {
+			g_strfreev(path);
+			return NULL;
+		}
+	}
+
+	g_strfreev(path);
+
+	return last;
+}
+
+static struct message_folder *create_folder(const char *name, const char *query)
+{
+	struct message_folder *folder = g_new0(struct message_folder, 1);
+
+	folder->name = g_strdup(name);
+	folder->query = g_strdup(query);
+
+	return folder;
+}
+
+static void destroy_folder_tree(void *root)
+{
+	struct message_folder *folder = root;
+	GSList *tmp, *next;
+
+	if (folder == NULL)
+		return;
+
+	g_free(folder->name);
+	g_free(folder->query);
+
+	tmp = folder->subfolders;
+	while (tmp != NULL) {
+		next = g_slist_next(tmp);
+		destroy_folder_tree(tmp->data);
+		tmp = next;
+	}
+
+	g_slist_free(folder->subfolders);
+	g_free(folder);
+}
+
+static void create_folder_tree(void)
+{
+	struct message_folder *parent, *child;
+
+	folder_tree = create_folder("/", "FILTER (!BOUND(?msg))");
+
+	parent = create_folder("telecom", "FILTER (!BOUND(?msg))");
+	folder_tree->subfolders = g_slist_append(folder_tree->subfolders,
+								parent);
+
+	child = create_folder("msg", "FILTER (!BOUND(?msg))");
+	parent->subfolders = g_slist_append(parent->subfolders, child);
+
+	parent = child;
+
+	child = create_folder("inbox", "?msg nmo:isSent \"false\" ; "
+				"nmo:isDeleted \"false\" ; "
+				"nmo:isDraft \"false\". ");
+	parent->subfolders = g_slist_append(parent->subfolders, child);
+
+	child = create_folder("sent", "?msg nmo:isDeleted \"false\" ; "
+				"nmo:isSent \"true\" . ");
+	parent->subfolders = g_slist_append(parent->subfolders, child);
+
+	child = create_folder("deleted", "?msg nmo:isDeleted \"true\" . ");
+	parent->subfolders = g_slist_append(parent->subfolders, child);
+}
+
+int messages_init(void)
+{
+	create_folder_tree();
+
+	return 0;
+}
+
+void messages_exit(void)
+{
+	destroy_folder_tree(folder_tree);
+}
+
+int messages_connect(void **s)
+{
+	struct session *session = g_new0(struct session, 1);
+
+	session->cwd = g_strdup("/");
+	session->folder = folder_tree;
+
+	*s = session;
+
+	return 0;
+}
+
+void messages_disconnect(void *s)
+{
+	struct session *session = s;
+
+	g_free(session->cwd);
+	g_free(session);
+}
+
+int messages_set_notification_registration(void *session,
+		void (*send_event)(void *session,
+			const struct messages_event *event, void *user_data),
+		void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_set_folder(void *s, const char *name, gboolean cdup)
+{
+	struct session *session = s;
+	char *newrel = NULL;
+	char *newabs;
+	char *tmp;
+
+	if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
+		return -EBADR;
+
+	if (cdup) {
+		if (session->cwd[0] == 0)
+			return -ENOENT;
+
+		newrel = g_path_get_dirname(session->cwd);
+
+		/* We use empty string for indication of the root directory */
+		if (newrel[0] == '.' && newrel[1] == 0)
+			newrel[0] = 0;
+	}
+
+	tmp = newrel;
+	if (!cdup && (!name || name[0] == 0))
+		newrel = g_strdup("");
+	else
+		newrel = g_build_filename(newrel ? newrel : session->cwd, name,
+									NULL);
+	g_free(tmp);
+
+	if (newrel[0] != '/')
+		newabs = g_build_filename("/", newrel, NULL);
+	else
+		newabs = g_strdup(newrel);
+
+	session->folder = get_folder(newabs);
+	if (session->folder == NULL) {
+		g_free(newrel);
+		g_free(newabs);
+
+		return -ENOENT;
+	}
+
+	g_free(newrel);
+	g_free(session->cwd);
+	session->cwd = newabs;
+
+	return 0;
+}
+
+static gboolean async_get_folder_listing(void *s)
+{
+	struct session *session = s;
+	gboolean count = FALSE;
+	int folder_count = 0;
+	char *path = NULL;
+	struct message_folder *folder;
+	GSList *dir;
+
+	if (session->name && strchr(session->name, '/') != NULL)
+		goto done;
+
+	path = g_build_filename(session->cwd, session->name, NULL);
+
+	if (path == NULL || strlen(path) == 0)
+		goto done;
+
+	folder = get_folder(path);
+
+	if (folder == NULL)
+		goto done;
+
+	if (session->max == 0) {
+		session->max = 0xffff;
+		session->offset = 0;
+		count = TRUE;
+	}
+
+	for (dir = folder->subfolders; dir &&
+				(folder_count - session->offset) < session->max;
+				folder_count++, dir = g_slist_next(dir)) {
+		struct message_folder *dir_data = dir->data;
+
+		if (count == FALSE && session->offset <= folder_count)
+			session->folder_list_cb(session, -EAGAIN, 0,
+					dir_data->name, session->user_data);
+	}
+
+ done:
+	session->folder_list_cb(session, 0, folder_count, NULL,
+							session->user_data);
+
+	g_free(path);
+	g_free(session->name);
+
+	return FALSE;
+}
+
+int messages_get_folder_listing(void *s, const char *name,
+					uint16_t max, uint16_t offset,
+					messages_folder_listing_cb callback,
+					void *user_data)
+{
+	struct session *session = s;
+	session->name = g_strdup(name);
+	session->max = max;
+	session->offset = offset;
+	session->folder_list_cb = callback;
+	session->user_data = user_data;
+
+	g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing,
+						session, NULL);
+
+	return 0;
+}
+
+int messages_get_messages_listing(void *session, const char *name,
+				uint16_t max, uint16_t offset,
+				uint8_t subject_len,
+				const struct messages_filter *filter,
+				messages_get_messages_listing_cb callback,
+				void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_get_message(void *session, const char *handle,
+				unsigned long flags,
+				messages_get_message_cb callback,
+				void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_update_inbox(void *session, messages_status_cb callback,
+							void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_set_read(void *session, const char *handle, uint8_t value,
+				messages_status_cb callback, void *user_data)
+{
+	return -ENOSYS;
+}
+
+int messages_set_delete(void *session, const char *handle, uint8_t value,
+					messages_status_cb callback,
+					void *user_data)
+{
+	return -ENOSYS;
+}
+
+void messages_abort(void *session)
+{
+}
diff --git a/bluez/repo/obexd/plugins/messages.h b/bluez/repo/obexd/plugins/messages.h
new file mode 100644
index 0000000..00a16b1
--- /dev/null
+++ b/bluez/repo/obexd/plugins/messages.h
@@ -0,0 +1,309 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation
+ *
+ *
+ *  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>
+#include <stdint.h>
+
+/* Those are used by backend to notify transport plugin which properties did it
+ * send.
+ */
+#define PMASK_SUBJECT			0x0001
+#define PMASK_DATETIME			0x0002
+#define PMASK_SENDER_NAME		0x0004
+#define PMASK_SENDER_ADDRESSING		0x0008
+#define PMASK_RECIPIENT_NAME		0x0010
+#define PMASK_RECIPIENT_ADDRESSING	0x0020
+#define PMASK_TYPE			0x0040
+#define PMASK_SIZE			0x0080
+#define PMASK_RECEPTION_STATUS		0x0100
+#define PMASK_TEXT			0x0200
+#define PMASK_ATTACHMENT_SIZE		0x0400
+#define PMASK_PRIORITY			0x0800
+#define PMASK_READ			0x1000
+#define PMASK_SENT			0x2000
+#define PMASK_PROTECTED			0x4000
+#define PMASK_REPLYTO_ADDRESSING	0x8000
+
+/* This one is used in a response to GetMessagesListing. Use PMASK_* values to
+ * notify the plugin which members are actually set. Backend shall not omit
+ * properties required by MAP specification (subject, datetime,
+ * recipient_addressing, type, size, reception_status, attachment_size) unless
+ * ordered by PARAMETERMASK. Boolean values should be probably
+ * always sent (need checking). Handle is mandatory. Plugin will filter out any
+ * properties that were not wanted by MCE.
+ *
+ * Handle shall be set to hexadecimal representation with upper-case letters. No
+ * prefix shall be appended and without no zeros. This corresponds to PTS
+ * behaviour described in comments to the MAP specification.
+ *
+ * The rest of char * fields shall be set according to the MAP specification
+ * rules.
+ */
+struct messages_message {
+	uint32_t mask;
+	char *handle;
+	char *subject;
+	char *datetime;
+	char *sender_name;
+	char *sender_addressing;
+	char *replyto_addressing;
+	char *recipient_name;
+	char *recipient_addressing;
+	char *type;
+	char *reception_status;
+	char *size;
+	char *attachment_size;
+	gboolean text;
+	gboolean read;
+	gboolean sent;
+	gboolean protect;
+	gboolean priority;
+};
+
+/* Type of message event to be delivered to MNS server */
+enum messages_event_type {
+	MET_NEW_MESSAGE,
+	MET_DELIVERY_SUCCESS,
+	MET_SENDING_SUCCESS,
+	MET_DELIVERY_FAILURE,
+	MET_SENDING_FAILURE,
+	MET_MEMORY_FULL,
+	MET_MEMORY_AVAILABLE,
+	MET_MESSAGE_DELETED,
+	MET_MESSAGE_SHIFT
+};
+
+/* Data for sending MNS notification. Handle shall be formatted as described in
+ * messages_message.
+ */
+struct messages_event {
+	enum messages_event_type type;
+	uint8_t instance_id;
+	char *handle;
+	char *folder;
+	char *old_folder;
+	char *msg_type;
+};
+
+/* parameter_mask: |-ed PMASK_* values
+ * See MAP specification for the rest.
+ */
+struct messages_filter {
+	uint32_t parameter_mask;
+	uint8_t type;
+	const char *period_begin;
+	const char *period_end;
+	uint8_t read_status;
+	const char *recipient;
+	const char *originator;
+	uint8_t priority;
+};
+
+/* This is called once after server starts.
+ *
+ * Returns value less than zero if error. This will prevent MAP plugin from
+ * starting.
+ */
+int messages_init(void);
+
+/* This gets called right before server finishes
+ */
+void messages_exit(void);
+
+/* Starts a new MAP session.
+ *
+ * session: variable to store pointer to backend session data. This one shall be
+ * passed to all in-session calls.
+ *
+ * If session start succeeded, backend shall return 0. Otherwise the error value
+ * will be sent as a response to OBEX connect.
+ */
+int messages_connect(void **session);
+
+/* Closes a MAP session.
+ *
+ * This call should free buffer reserved by messages_connect.
+ */
+void messages_disconnect(void *session);
+
+/******************************************************************************
+ * NOTE on callbacks.
+ *
+ * All functions requiring callbacks have to call them asynchronously.
+ * 'user_data' is for passing arbitrary user data.
+ *
+ * Functions for GetMessagesListing, GetFolder listing and GetMessage call their
+ * callbacks multiple times - one for each listing entry or message body chunk.
+ * To indicate the end of operation backend must call callback with the data
+ * pointer parameter set to NULL.
+ *
+ * If err == -EAGAIN the transport * plugin does not wake IO.
+ *
+ * Keep in mind that application parameters has to be send first. Therefore the
+ * first time err == 0 and thus sending is started, callback will use provided
+ * parameters (e.g. size in case of folder listing) to build applications
+ * parameters header used in response. In any other case those parameters will
+ * be ignored.
+ *
+ * If err != 0 && err != -EAGAIN, the operation is finished immediately and err
+ * value is used to set the error code in OBEX response.
+ ******************************************************************************/
+
+/* Registers for messaging events notifications.
+ *
+ * session: Backend session.
+ * send_event: Function that will be called to indicate a new event.
+ *
+ * To unregister currently registered notifications, call this with send_event
+ * set to NULL.
+ */
+int messages_set_notification_registration(void *session,
+		void (*send_event)(void *session,
+			const struct messages_event *event, void *user_data),
+		void *user_data);
+
+/* Changes current directory.
+ *
+ * session: Backend session.
+ * name: Subdirectory to go to. If empty or null and cdup is false, go to the
+ *	root directory.
+ * cdup: If true, go up one level first.
+ */
+int messages_set_folder(void *session, const char *name, gboolean cdup);
+
+/* Retrieves subdirectories listing from a current directory.
+ *
+ * session: Backend session.
+ * name: Optional subdirectory name (not strictly required by MAP).
+ * max: Maximum number of entries to retrieve.
+ * offset: Offset of the first entry.
+ * size: Total size of listing to be returned.
+ *
+ * Callback shall be called for every entry of the listing. 'name' is the
+ * subdirectory name.
+ */
+typedef void (*messages_folder_listing_cb)(void *session, int err,
+		uint16_t size, const char *name, void *user_data);
+
+int messages_get_folder_listing(void *session, const char *name, uint16_t max,
+				uint16_t offset,
+				messages_folder_listing_cb callback,
+				void *user_data);
+
+/* Retrieves messages listing from a current directory.
+ *
+ * session: Backend session.
+ * name: Optional subdirectory name.
+ * max: Maximum number of entries to retrieve.
+ * offset: Offset of the first entry.
+ * subject_len: Maximum string length of the "subject" parameter in the entries.
+ * filter: Filter to apply on returned message listing.
+ * size: Total size of listing to be returned.
+ * newmsg: Indicates presence of unread messages.
+ *
+ * Callback shall be called for every entry of the listing, giving message data
+ * in 'message'.
+ */
+typedef void (*messages_get_messages_listing_cb)(void *session, int err,
+					uint16_t size, gboolean newmsg,
+					const struct messages_message *message,
+					void *user_data);
+
+int messages_get_messages_listing(void *session, const char *name,
+				uint16_t max, uint16_t offset,
+				uint8_t subject_len,
+				const struct messages_filter *filter,
+				messages_get_messages_listing_cb callback,
+				void *user_data);
+
+#define MESSAGES_ATTACHMENT	(1 << 0)
+#define MESSAGES_UTF8		(1 << 1)
+#define MESSAGES_FRACTION	(1 << 2)
+#define MESSAGES_NEXT		(1 << 3)
+
+/* Retrieves bMessage object (see MAP specification, ch. 3.1.3) of a given
+ * message.
+ *
+ * session: Backend session.
+ * handle: Handle of the message to retrieve.
+ * flags: or-ed mask of following:
+ *	MESSAGES_ATTACHMENT: Selects whether or not attachments (if any) are to
+ *		be included.
+ *	MESSAGES_UTF8: If true, convert message to utf-8. Otherwise use native
+ *		encoding.
+ *	MESSAGES_FRACTION: If true, deliver fractioned message.
+ *	MESSAGES_NEXT: If fraction is true this indicates whether to retrieve
+ *		first fraction
+ *	or the next one.
+ * fmore: Indicates whether next fraction is available.
+ * chunk: chunk of bMessage body
+ *
+ * Callback allows for returning bMessage in chunks.
+ */
+typedef void (*messages_get_message_cb)(void *session, int err, gboolean fmore,
+	const char *chunk, void *user_data);
+
+int messages_get_message(void *session, const char *handle,
+					unsigned long flags,
+					messages_get_message_cb callback,
+					void *user_data);
+
+typedef void (*messages_status_cb)(void *session, int err, void *user_data);
+
+/* Informs Message Server to Update Inbox via network.
+ *
+ * session: Backend session.
+ * user_data: User data if any to be sent.
+ * Callback shall be called for every update inbox request received from MCE.
+ */
+int messages_update_inbox(void *session, messages_status_cb callback,
+							void *user_data);
+/* Informs Message Server to modify read status of a given message.
+ *
+ * session: Backend session.
+ * handle: Unique identifier to the message.
+ * value: Indicates the new value of the read status for a given message.
+ * Callback shall be called for every read status update request
+ *	recieved from MCE.
+ * user_data: User data if any to be sent.
+ */
+int messages_set_read(void *session, const char *handle, uint8_t value,
+				messages_status_cb callback, void *user_data);
+
+/* Informs Message Server to modify delete status of a given message.
+ *
+ * session: Backend session.
+ * handle: Unique identifier to the message.
+ * value: Indicates the new value of the delete status for a given message.
+ * Callback shall be called for every delete status update request
+ *	recieved from MCE.
+ * user_data: User data if any to be sent.
+ */
+int messages_set_delete(void *session, const char *handle, uint8_t value,
+				messages_status_cb callback, void *user_data);
+
+/* Aborts currently pending request.
+ *
+ * session: Backend session.
+ */
+void messages_abort(void *session);
diff --git a/bluez/repo/obexd/plugins/opp.c b/bluez/repo/obexd/plugins/opp.c
new file mode 100644
index 0000000..5bb7667
--- /dev/null
+++ b/bluez/repo/obexd/plugins/opp.c
@@ -0,0 +1,193 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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 <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
+#include "filesystem.h"
+
+#define VCARD_TYPE "text/x-vcard"
+
+static void *opp_connect(struct obex_session *os, int *err)
+{
+	manager_register_session(os);
+
+	if (err)
+		*err = 0;
+
+	return manager_register_transfer(os);
+}
+
+static void opp_progress(struct obex_session *os, void *user_data)
+{
+	manager_emit_transfer_progress(user_data);
+}
+
+static int opp_chkput(struct obex_session *os, void *user_data)
+{
+	char *folder, *name, *path;
+	const char *t;
+	int err;
+
+	if (obex_get_size(os) == OBJECT_SIZE_DELETE)
+		return -ENOSYS;
+
+	t = obex_get_name(os);
+	if (t != NULL && !is_filename(t))
+		return -EBADR;
+
+	if (obex_option_auto_accept()) {
+		folder = g_strdup(obex_option_root_folder());
+		name = g_strdup(obex_get_name(os));
+		goto skip_auth;
+	}
+
+	err = manager_request_authorization(user_data, &folder, &name);
+	if (err < 0)
+		return -EPERM;
+
+	if (folder == NULL)
+		folder = g_strdup(obex_option_root_folder());
+
+	if (name == NULL)
+		name = g_strdup(obex_get_name(os));
+
+skip_auth:
+	if (name == NULL || strlen(name) == 0) {
+		err = -EBADR;
+		goto failed;
+	}
+
+	if (g_strcmp0(name, obex_get_name(os)) != 0)
+		obex_set_name(os, name);
+
+	path = g_build_filename(folder, name, NULL);
+
+	err = obex_put_stream_start(os, path);
+
+	g_free(path);
+
+	if (err < 0)
+		goto failed;
+
+	manager_emit_transfer_started(user_data);
+
+failed:
+	g_free(folder);
+	g_free(name);
+
+	return err;
+}
+
+static int opp_put(struct obex_session *os, void *user_data)
+{
+	const char *name = obex_get_name(os);
+	const char *folder = obex_option_root_folder();
+
+	if (folder == NULL)
+		return -EPERM;
+
+	if (name == NULL)
+		return -EBADR;
+
+	return 0;
+}
+
+static int opp_get(struct obex_session *os, void *user_data)
+{
+	const char *type;
+	char *folder, *path;
+	int err = 0;
+
+	if (obex_get_name(os))
+		return -EPERM;
+
+	type = obex_get_type(os);
+
+	if (type == NULL)
+		return -EPERM;
+
+	folder = g_strdup(obex_option_root_folder());
+	path = g_build_filename(folder, "/vcard.vcf", NULL);
+
+	if (g_ascii_strcasecmp(type, VCARD_TYPE) == 0) {
+		if (obex_get_stream_start(os, path) < 0)
+			err = -ENOENT;
+
+	} else
+		err = -EPERM;
+
+	g_free(folder);
+	g_free(path);
+	return err;
+}
+
+static void opp_disconnect(struct obex_session *os, void *user_data)
+{
+	manager_unregister_transfer(user_data);
+	manager_unregister_session(os);
+}
+
+static void opp_reset(struct obex_session *os, void *user_data)
+{
+	manager_emit_transfer_completed(user_data);
+}
+
+static struct obex_service_driver driver = {
+	.name = "Object Push server",
+	.service = OBEX_OPP,
+	.connect = opp_connect,
+	.progress = opp_progress,
+	.disconnect = opp_disconnect,
+	.get = opp_get,
+	.put = opp_put,
+	.chkput = opp_chkput,
+	.reset = opp_reset
+};
+
+static int opp_init(void)
+{
+	return obex_service_driver_register(&driver);
+}
+
+static void opp_exit(void)
+{
+	obex_service_driver_unregister(&driver);
+}
+
+OBEX_PLUGIN_DEFINE(opp, opp_init, opp_exit)
diff --git a/bluez/repo/obexd/plugins/pbap.c b/bluez/repo/obexd/plugins/pbap.c
new file mode 100644
index 0000000..bab691c
--- /dev/null
+++ b/bluez/repo/obexd/plugins/pbap.c
@@ -0,0 +1,998 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2009-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/mimetype.h"
+#include "phonebook.h"
+#include "filesystem.h"
+
+#define PHONEBOOK_TYPE		"x-bt/phonebook"
+#define VCARDLISTING_TYPE	"x-bt/vcard-listing"
+#define VCARDENTRY_TYPE		"x-bt/vcard"
+
+#define ORDER_TAG		0x01
+#define SEARCHVALUE_TAG		0x02
+#define SEARCHATTRIB_TAG	0x03
+#define MAXLISTCOUNT_TAG	0x04
+#define LISTSTARTOFFSET_TAG	0x05
+#define FILTER_TAG		0x06
+#define FORMAT_TAG		0X07
+#define PHONEBOOKSIZE_TAG	0X08
+#define NEWMISSEDCALLS_TAG	0X09
+
+struct cache {
+	gboolean valid;
+	uint32_t index;
+	GSList *entries;
+};
+
+struct cache_entry {
+	uint32_t handle;
+	char *id;
+	char *name;
+	char *sound;
+	char *tel;
+};
+
+struct pbap_session {
+	struct apparam_field *params;
+	char *folder;
+	uint32_t find_handle;
+	struct cache cache;
+	struct pbap_object *obj;
+};
+
+struct pbap_object {
+	GString *buffer;
+	GObexApparam *apparam;
+	gboolean firstpacket;
+	gboolean lastpart;
+	struct pbap_session *session;
+	void *request;
+};
+
+static const uint8_t PBAP_TARGET[TARGET_SIZE] = {
+			0x79, 0x61, 0x35, 0xF0,  0xF0, 0xC5, 0x11, 0xD8,
+			0x09, 0x66, 0x08, 0x00,  0x20, 0x0C, 0x9A, 0x66  };
+
+typedef int (*cache_entry_find_f) (const struct cache_entry *entry,
+			const char *value);
+
+static void cache_entry_free(void *data)
+{
+	struct cache_entry *entry = data;
+
+	g_free(entry->id);
+	g_free(entry->name);
+	g_free(entry->sound);
+	g_free(entry->tel);
+	g_free(entry);
+}
+
+static gboolean entry_name_find(const struct cache_entry *entry,
+		const char *value)
+{
+	char *name;
+	gboolean ret;
+
+	if (!entry->name)
+		return FALSE;
+
+	if (strlen(value) == 0)
+		return TRUE;
+
+	name = g_utf8_strdown(entry->name, -1);
+	ret = (g_strstr_len(name, -1, value) ? TRUE : FALSE);
+	g_free(name);
+
+	return ret;
+}
+
+static gboolean entry_sound_find(const struct cache_entry *entry,
+		const char *value)
+{
+	if (!entry->sound)
+		return FALSE;
+
+	return (g_strstr_len(entry->sound, -1, value) ? TRUE : FALSE);
+}
+
+static gboolean entry_tel_find(const struct cache_entry *entry,
+		const char *value)
+{
+	if (!entry->tel)
+		return FALSE;
+
+	return (g_strstr_len(entry->tel, -1, value) ? TRUE : FALSE);
+}
+
+static const char *cache_find(struct cache *cache, uint32_t handle)
+{
+	GSList *l;
+
+	for (l = cache->entries; l; l = l->next) {
+		struct cache_entry *entry = l->data;
+
+		if (entry->handle == handle)
+			return entry->id;
+	}
+
+	return NULL;
+}
+
+static void cache_clear(struct cache *cache)
+{
+	g_slist_free_full(cache->entries, cache_entry_free);
+	cache->entries = NULL;
+}
+
+static void phonebook_size_result(const char *buffer, size_t bufsize,
+					int vcards, int missed,
+					gboolean lastpart, void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+	uint16_t phonebooksize;
+
+	if (pbap->obj->request) {
+		phonebook_req_finalize(pbap->obj->request);
+		pbap->obj->request = NULL;
+	}
+
+	if (vcards < 0)
+		vcards = 0;
+
+	DBG("vcards %d", vcards);
+
+	phonebooksize = vcards;
+
+	pbap->obj->apparam = g_obex_apparam_set_uint16(NULL, PHONEBOOKSIZE_TAG,
+								phonebooksize);
+
+	pbap->obj->firstpacket = TRUE;
+
+	if (missed > 0)	{
+		DBG("missed %d", missed);
+
+		pbap->obj->apparam = g_obex_apparam_set_uint16(
+							pbap->obj->apparam,
+							NEWMISSEDCALLS_TAG,
+							missed);
+	}
+
+	obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
+}
+
+static void query_result(const char *buffer, size_t bufsize, int vcards,
+				int missed, gboolean lastpart, void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+
+	DBG("");
+
+	if (pbap->obj->request && lastpart) {
+		phonebook_req_finalize(pbap->obj->request);
+		pbap->obj->request = NULL;
+	}
+
+	pbap->obj->lastpart = lastpart;
+
+	if (vcards < 0) {
+		obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
+		return;
+	}
+
+	if (!pbap->obj->buffer)
+		pbap->obj->buffer = g_string_new_len(buffer, bufsize);
+	else
+		pbap->obj->buffer = g_string_append_len(pbap->obj->buffer,
+							buffer,	bufsize);
+
+	if (missed > 0)	{
+		DBG("missed %d", missed);
+
+		pbap->obj->firstpacket = TRUE;
+
+		pbap->obj->apparam = g_obex_apparam_set_uint16(
+							pbap->obj->apparam,
+							NEWMISSEDCALLS_TAG,
+							missed);
+	}
+
+	obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
+}
+
+static void cache_entry_notify(const char *id, uint32_t handle,
+					const char *name, const char *sound,
+					const char *tel, void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+	struct cache_entry *entry = g_new0(struct cache_entry, 1);
+	struct cache *cache = &pbap->cache;
+
+	if (handle != PHONEBOOK_INVALID_HANDLE)
+		entry->handle = handle;
+	else
+		entry->handle = ++pbap->cache.index;
+
+	entry->id = g_strdup(id);
+	entry->name = g_strdup(name);
+	entry->sound = g_strdup(sound);
+	entry->tel = g_strdup(tel);
+
+	cache->entries = g_slist_append(cache->entries, entry);
+}
+
+static int alpha_sort(gconstpointer a, gconstpointer b)
+{
+	const struct cache_entry *e1 = a;
+	const struct cache_entry *e2 = b;
+
+	return g_strcmp0(e1->name, e2->name);
+}
+
+static int indexed_sort(gconstpointer a, gconstpointer b)
+{
+	const struct cache_entry *e1 = a;
+	const struct cache_entry *e2 = b;
+
+	return (e1->handle - e2->handle);
+}
+
+static int phonetical_sort(gconstpointer a, gconstpointer b)
+{
+	const struct cache_entry *e1 = a;
+	const struct cache_entry *e2 = b;
+
+	/* SOUND attribute is optional. Use Indexed sort if not present. */
+	if (!e1->sound || !e2->sound)
+		return indexed_sort(a, b);
+
+	return g_strcmp0(e1->sound, e2->sound);
+}
+
+static GSList *sort_entries(GSList *l, uint8_t order, uint8_t search_attrib,
+							const char *value)
+{
+	GSList *sorted = NULL;
+	cache_entry_find_f find;
+	GCompareFunc sort;
+	char *searchval;
+
+	/*
+	 * Default sorter is "Indexed". Some backends doesn't inform the index,
+	 * for this case a sequential internal index is assigned.
+	 * 0x00 = indexed
+	 * 0x01 = alphanumeric
+	 * 0x02 = phonetic
+	 */
+	switch (order) {
+	case 0x01:
+		sort = alpha_sort;
+		break;
+	case 0x02:
+		sort = phonetical_sort;
+		break;
+	default:
+		sort = indexed_sort;
+		break;
+	}
+
+	/*
+	 * This implementation checks if the given field CONTAINS the
+	 * search value(case insensitive). Name is the default field
+	 * when the attribute is not provided.
+	 */
+	switch (search_attrib) {
+		/* Number */
+		case 1:
+			find = entry_tel_find;
+			break;
+		/* Sound */
+		case 2:
+			find = entry_sound_find;
+			break;
+		default:
+			find = entry_name_find;
+			break;
+	}
+
+	searchval = value ? g_utf8_strdown(value, -1) : NULL;
+	for (; l; l = l->next) {
+		struct cache_entry *entry = l->data;
+
+		if (searchval && !find(entry, (const char *) searchval))
+			continue;
+
+		sorted = g_slist_insert_sorted(sorted, entry, sort);
+	}
+
+	g_free(searchval);
+
+	return sorted;
+}
+
+static int generate_response(void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+	GSList *sorted;
+	GSList *l;
+	uint16_t max = pbap->params->maxlistcount;
+
+	DBG("");
+
+	if (max == 0) {
+		/* Ignore all other parameter and return PhoneBookSize */
+		uint16_t size = g_slist_length(pbap->cache.entries);
+
+		pbap->obj->apparam = g_obex_apparam_set_uint16(
+							pbap->obj->apparam,
+							PHONEBOOKSIZE_TAG,
+							size);
+
+		return 0;
+	}
+
+	/*
+	 * Don't free the sorted list content: this list contains
+	 * only the reference for the "real" cache entry.
+	 */
+	sorted = sort_entries(pbap->cache.entries, pbap->params->order,
+				pbap->params->searchattrib,
+				(const char *) pbap->params->searchval);
+
+	/* Computing offset considering first entry of the phonebook */
+	l = g_slist_nth(sorted, pbap->params->liststartoffset);
+
+	pbap->obj->buffer = g_string_new(VCARD_LISTING_BEGIN);
+	for (; l && max; l = l->next, max--) {
+		const struct cache_entry *entry = l->data;
+		char *escaped_name = g_markup_escape_text(entry->name, -1);
+
+		g_string_append_printf(pbap->obj->buffer,
+			VCARD_LISTING_ELEMENT, entry->handle, escaped_name);
+
+		g_free(escaped_name);
+	}
+
+	pbap->obj->buffer = g_string_append(pbap->obj->buffer,
+							VCARD_LISTING_END);
+	g_slist_free(sorted);
+
+	return 0;
+}
+
+static void cache_ready_notify(void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+
+	DBG("");
+
+	phonebook_req_finalize(pbap->obj->request);
+	pbap->obj->request = NULL;
+
+	pbap->cache.valid = TRUE;
+
+	generate_response(pbap);
+	obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
+}
+
+static void cache_entry_done(void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+	const char *id;
+	int ret;
+
+	DBG("");
+
+	pbap->cache.valid = TRUE;
+
+	id = cache_find(&pbap->cache, pbap->find_handle);
+	if (id == NULL) {
+		DBG("Entry %d not found on cache", pbap->find_handle);
+		obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
+		return;
+	}
+
+	phonebook_req_finalize(pbap->obj->request);
+	pbap->obj->request = phonebook_get_entry(pbap->folder, id,
+				pbap->params, query_result, pbap, &ret);
+	if (ret < 0)
+		obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret);
+}
+
+static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
+{
+	GObexApparam *apparam;
+	struct apparam_field *param;
+
+	apparam = g_obex_apparam_decode(buffer, hlen);
+	if (apparam == NULL)
+		return NULL;
+
+	param = g_new0(struct apparam_field, 1);
+
+	/*
+	 * As per spec when client doesn't include MAXLISTCOUNT_TAG then it
+	 * should be assume as Maximum value in vcardlisting 65535
+	 */
+	param->maxlistcount = UINT16_MAX;
+
+	g_obex_apparam_get_uint8(apparam, ORDER_TAG, &param->order);
+	g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG,
+						&param->searchattrib);
+	g_obex_apparam_get_uint8(apparam, FORMAT_TAG, &param->format);
+	g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
+						&param->maxlistcount);
+	g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG,
+						&param->liststartoffset);
+	g_obex_apparam_get_uint64(apparam, FILTER_TAG, &param->filter);
+	param->searchval = g_obex_apparam_get_string(apparam, SEARCHVALUE_TAG);
+
+	DBG("o %x sa %x sv %s fil %" G_GINT64_MODIFIER "x for %x max %x off %x",
+			param->order, param->searchattrib, param->searchval,
+			param->filter, param->format, param->maxlistcount,
+			param->liststartoffset);
+
+	g_obex_apparam_free(apparam);
+
+	return param;
+}
+
+static void *pbap_connect(struct obex_session *os, int *err)
+{
+	struct pbap_session *pbap;
+
+	manager_register_session(os);
+
+	pbap = g_new0(struct pbap_session, 1);
+	pbap->folder = g_strdup("/");
+	pbap->find_handle = PHONEBOOK_INVALID_HANDLE;
+
+	if (err)
+		*err = 0;
+
+	return pbap;
+}
+
+static int pbap_get(struct obex_session *os, void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+	const char *type = obex_get_type(os);
+	const char *name = obex_get_name(os);
+	struct apparam_field *params;
+	const uint8_t *buffer;
+	char *path;
+	ssize_t rsize;
+	int ret;
+
+	DBG("name %s type %s pbap %p", name, type, pbap);
+
+	if (type == NULL)
+		return -EBADR;
+
+	rsize = obex_get_apparam(os, &buffer);
+	if (rsize < 0) {
+		if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) != 0)
+			return -EBADR;
+
+		rsize = 0;
+	}
+
+	params = parse_aparam(buffer, rsize);
+	if (params == NULL)
+		return -EBADR;
+
+	if (pbap->params) {
+		g_free(pbap->params->searchval);
+		g_free(pbap->params);
+	}
+
+	pbap->params = params;
+
+	if (g_ascii_strcasecmp(type, PHONEBOOK_TYPE) == 0) {
+		/* Always contains the absolute path */
+		if (g_path_is_absolute(name))
+			path = g_strdup(name);
+		else
+			path = g_build_filename("/", name, NULL);
+
+	} else if (g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) {
+		/* Always relative */
+		if (!name || strlen(name) == 0)
+			/* Current folder */
+			path = g_strdup(pbap->folder);
+		else
+			/* Current folder + relative path */
+			path = g_build_filename(pbap->folder, name, NULL);
+
+	} else if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) {
+		/* File name only */
+		path = g_strdup(name);
+	} else
+		return -EBADR;
+
+	if (path == NULL)
+		return -EBADR;
+
+	ret = obex_get_stream_start(os, path);
+
+	g_free(path);
+
+	return ret;
+}
+
+static int pbap_setpath(struct obex_session *os, void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+	const char *name;
+	const uint8_t *nonhdr;
+	char *fullname;
+	int err;
+
+	if (obex_get_non_header_data(os, &nonhdr) != 2) {
+		error("Set path failed: flag and constants not found!");
+		return -EBADMSG;
+	}
+
+	name = obex_get_name(os);
+
+	DBG("name %s folder %s nonhdr 0x%x%x", name, pbap->folder,
+							nonhdr[0], nonhdr[1]);
+
+	fullname = phonebook_set_folder(pbap->folder, name, nonhdr[0], &err);
+	if (err < 0)
+		return err;
+
+	g_free(pbap->folder);
+	pbap->folder = fullname;
+
+	/*
+	 * FIXME: Define a criteria to mark the cache as invalid
+	 */
+	pbap->cache.valid = FALSE;
+	pbap->cache.index = 0;
+	cache_clear(&pbap->cache);
+
+	return 0;
+}
+
+static void pbap_disconnect(struct obex_session *os, void *user_data)
+{
+	struct pbap_session *pbap = user_data;
+
+	manager_unregister_session(os);
+
+	if (pbap->obj)
+		pbap->obj->session = NULL;
+
+	if (pbap->params) {
+		g_free(pbap->params->searchval);
+		g_free(pbap->params);
+	}
+
+	cache_clear(&pbap->cache);
+	g_free(pbap->folder);
+	g_free(pbap);
+}
+
+static int pbap_chkput(struct obex_session *os, void *user_data)
+{
+	/* Rejects all PUTs */
+	return -EBADR;
+}
+
+static struct obex_service_driver pbap = {
+	.name = "Phonebook Access server",
+	.service = OBEX_PBAP,
+	.target = PBAP_TARGET,
+	.target_size = TARGET_SIZE,
+	.connect = pbap_connect,
+	.get = pbap_get,
+	.setpath = pbap_setpath,
+	.disconnect = pbap_disconnect,
+	.chkput = pbap_chkput
+};
+
+static struct pbap_object *vobject_create(struct pbap_session *pbap,
+								void *request)
+{
+	struct pbap_object *obj;
+
+	obj = g_new0(struct pbap_object, 1);
+	obj->session = pbap;
+	pbap->obj = obj;
+	obj->request = request;
+
+	return obj;
+}
+
+static void *vobject_pull_open(const char *name, int oflag, mode_t mode,
+				void *context, size_t *size, int *err)
+{
+	struct pbap_session *pbap = context;
+	phonebook_cb cb;
+	int ret;
+	void *request;
+
+	DBG("name %s context %p maxlistcount %d", name, context,
+						pbap->params->maxlistcount);
+
+	if (oflag != O_RDONLY) {
+		ret = -EPERM;
+		goto fail;
+	}
+
+	if (name == NULL) {
+		ret = -EBADR;
+		goto fail;
+	}
+
+	if (pbap->params->maxlistcount == 0)
+		cb = phonebook_size_result;
+	else
+		cb = query_result;
+
+	request = phonebook_pull(name, pbap->params, cb, pbap, &ret);
+
+	if (ret < 0)
+		goto fail;
+
+	/* reading first part of results from backend */
+	ret = phonebook_pull_read(request);
+	if (ret < 0)
+		goto fail;
+
+	if (err)
+		*err = 0;
+
+	return vobject_create(pbap, request);
+
+fail:
+	if (err)
+		*err = ret;
+
+	return NULL;
+}
+
+static int vobject_close(void *object)
+{
+	struct pbap_object *obj = object;
+
+	DBG("");
+
+	if (obj->session)
+		obj->session->obj = NULL;
+
+	if (obj->buffer)
+		g_string_free(obj->buffer, TRUE);
+
+	if (obj->apparam)
+		g_obex_apparam_free(obj->apparam);
+
+	if (obj->request)
+		phonebook_req_finalize(obj->request);
+
+	g_free(obj);
+
+	return 0;
+}
+
+static void *vobject_list_open(const char *name, int oflag, mode_t mode,
+				void *context, size_t *size, int *err)
+{
+	struct pbap_session *pbap = context;
+	struct pbap_object *obj = NULL;
+	int ret;
+	void *request;
+
+	if (name == NULL) {
+		ret = -EBADR;
+		goto fail;
+	}
+
+	DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
+
+	if (oflag != O_RDONLY) {
+		ret = -EPERM;
+		goto fail;
+	}
+
+	/* PullvCardListing always get the contacts from the cache */
+
+	if (pbap->cache.valid) {
+		obj = vobject_create(pbap, NULL);
+		ret = generate_response(pbap);
+	} else {
+		request = phonebook_create_cache(name, cache_entry_notify,
+					cache_ready_notify, pbap, &ret);
+		if (ret == 0)
+			obj = vobject_create(pbap, request);
+	}
+	if (ret < 0)
+		goto fail;
+
+	if (err)
+		*err = 0;
+
+	return obj;
+
+fail:
+	if (obj)
+		vobject_close(obj);
+
+	if (err)
+		*err = ret;
+
+	return NULL;
+}
+
+static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
+					void *context, size_t *size, int *err)
+{
+	struct pbap_session *pbap = context;
+	const char *id;
+	uint32_t handle;
+	int ret;
+	void *request;
+
+	DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
+
+	if (oflag != O_RDONLY) {
+		ret = -EPERM;
+		goto fail;
+	}
+
+	if (name == NULL || sscanf(name, "%u.vcf", &handle) != 1) {
+		ret = -EBADR;
+		goto fail;
+	}
+
+	if (pbap->cache.valid == FALSE) {
+		pbap->find_handle = handle;
+		request = phonebook_create_cache(pbap->folder,
+			cache_entry_notify, cache_entry_done, pbap, &ret);
+		goto done;
+	}
+
+	id = cache_find(&pbap->cache, handle);
+	if (!id) {
+		ret = -ENOENT;
+		goto fail;
+	}
+
+	request = phonebook_get_entry(pbap->folder, id, pbap->params,
+						query_result, pbap, &ret);
+
+done:
+	if (ret < 0)
+		goto fail;
+
+	if (err)
+		*err = 0;
+
+	return vobject_create(pbap, request);
+
+fail:
+	if (err)
+		*err = ret;
+
+	return NULL;
+}
+
+static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu,
+								uint8_t *hi)
+{
+	struct pbap_object *obj = object;
+
+	if (!obj->buffer && !obj->apparam)
+		return -EAGAIN;
+
+	*hi = G_OBEX_HDR_APPARAM;
+
+	if (obj->firstpacket) {
+		obj->firstpacket = FALSE;
+
+		return g_obex_apparam_encode(obj->apparam, buf, mtu);
+	}
+
+	return 0;
+}
+
+static ssize_t vobject_pull_read(void *object, void *buf, size_t count)
+{
+	struct pbap_object *obj = object;
+	struct pbap_session *pbap = obj->session;
+	int len, ret;
+
+	DBG("buffer %p maxlistcount %d", obj->buffer,
+						pbap->params->maxlistcount);
+
+	if (!obj->buffer) {
+		if (pbap->params->maxlistcount == 0)
+			return -ENOSTR;
+
+		return -EAGAIN;
+	}
+
+	len = string_read(obj->buffer, buf, count);
+	if (len == 0 && !obj->lastpart) {
+		/* in case when buffer is empty and we know that more
+		 * data is still available in backend, requesting new
+		 * data part via phonebook_pull_read and returning
+		 * -EAGAIN to suspend request for now */
+		ret = phonebook_pull_read(obj->request);
+		if (ret)
+			return -EPERM;
+
+		return -EAGAIN;
+	}
+
+	return len;
+}
+
+static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
+								uint8_t *hi)
+{
+	struct pbap_object *obj = object;
+	struct pbap_session *pbap = obj->session;
+
+	/* Backend still busy reading contacts */
+	if (!pbap->cache.valid)
+		return -EAGAIN;
+
+	*hi = G_OBEX_HDR_APPARAM;
+
+	if (pbap->params->maxlistcount == 0)
+		return g_obex_apparam_encode(obj->apparam, buf, mtu);
+
+	return 0;
+}
+
+static ssize_t vobject_list_read(void *object, void *buf, size_t count)
+{
+	struct pbap_object *obj = object;
+	struct pbap_session *pbap = obj->session;
+
+	DBG("valid %d maxlistcount %d", pbap->cache.valid,
+						pbap->params->maxlistcount);
+
+	if (pbap->params->maxlistcount == 0)
+		return -ENOSTR;
+
+	return string_read(obj->buffer, buf, count);
+}
+
+static ssize_t vobject_vcard_read(void *object, void *buf, size_t count)
+{
+	struct pbap_object *obj = object;
+
+	DBG("buffer %p", obj->buffer);
+
+	if (!obj->buffer)
+		return -EAGAIN;
+
+	return string_read(obj->buffer, buf, count);
+}
+
+static struct obex_mime_type_driver mime_pull = {
+	.target = PBAP_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/phonebook",
+	.open = vobject_pull_open,
+	.close = vobject_close,
+	.read = vobject_pull_read,
+	.get_next_header = vobject_pull_get_next_header,
+};
+
+static struct obex_mime_type_driver mime_list = {
+	.target = PBAP_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/vcard-listing",
+	.open = vobject_list_open,
+	.close = vobject_close,
+	.read = vobject_list_read,
+	.get_next_header = vobject_list_get_next_header,
+};
+
+static struct obex_mime_type_driver mime_vcard = {
+	.target = PBAP_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "x-bt/vcard",
+	.open = vobject_vcard_open,
+	.close = vobject_close,
+	.read = vobject_vcard_read,
+};
+
+static int pbap_init(void)
+{
+	int err;
+
+	err = phonebook_init();
+	if (err < 0)
+		return err;
+
+	err = obex_mime_type_driver_register(&mime_pull);
+	if (err < 0)
+		goto fail_mime_pull;
+
+	err = obex_mime_type_driver_register(&mime_list);
+	if (err < 0)
+		goto fail_mime_list;
+
+	err = obex_mime_type_driver_register(&mime_vcard);
+	if (err < 0)
+		goto fail_mime_vcard;
+
+	err = obex_service_driver_register(&pbap);
+	if (err < 0)
+		goto fail_pbap_reg;
+
+	return 0;
+
+fail_pbap_reg:
+	obex_mime_type_driver_unregister(&mime_vcard);
+fail_mime_vcard:
+	obex_mime_type_driver_unregister(&mime_list);
+fail_mime_list:
+	obex_mime_type_driver_unregister(&mime_pull);
+fail_mime_pull:
+	phonebook_exit();
+
+	return err;
+}
+
+static void pbap_exit(void)
+{
+	obex_service_driver_unregister(&pbap);
+	obex_mime_type_driver_unregister(&mime_pull);
+	obex_mime_type_driver_unregister(&mime_list);
+	obex_mime_type_driver_unregister(&mime_vcard);
+	phonebook_exit();
+}
+
+OBEX_PLUGIN_DEFINE(pbap, pbap_init, pbap_exit)
diff --git a/bluez/repo/obexd/plugins/pcsuite.c b/bluez/repo/obexd/plugins/pcsuite.c
new file mode 100644
index 0000000..43ab409
--- /dev/null
+++ b/bluez/repo/obexd/plugins/pcsuite.c
@@ -0,0 +1,513 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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 <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/service.h"
+#include "ftp.h"
+
+#define PCSUITE_CHANNEL 24
+#define PCSUITE_WHO_SIZE 8
+
+#define PCSUITE_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>	\
+<record>								\
+  <attribute id=\"0x0001\">						\
+    <sequence>								\
+      <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/>		\
+    </sequence>								\
+  </attribute>								\
+									\
+  <attribute id=\"0x0004\">						\
+    <sequence>								\
+      <sequence>							\
+        <uuid value=\"0x0100\"/>					\
+      </sequence>							\
+      <sequence>							\
+        <uuid value=\"0x0003\"/>					\
+        <uint8 value=\"%u\" name=\"channel\"/>				\
+      </sequence>							\
+      <sequence>							\
+        <uuid value=\"0x0008\"/>					\
+      </sequence>							\
+    </sequence>								\
+  </attribute>								\
+									\
+  <attribute id=\"0x0005\">						\
+    <sequence>								\
+      <uuid value=\"0x1002\"/>						\
+    </sequence>								\
+  </attribute>								\
+									\
+  <attribute id=\"0x0009\">						\
+    <sequence>								\
+      <sequence>							\
+        <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/>		\
+        <uint16 value=\"0x0100\" name=\"version\"/>			\
+      </sequence>							\
+    </sequence>								\
+  </attribute>								\
+									\
+  <attribute id=\"0x0100\">						\
+    <text value=\"%s\" name=\"name\"/>					\
+  </attribute>								\
+</record>"
+
+#define BACKUP_BUS_NAME		"com.nokia.backup.plugin"
+#define BACKUP_PATH		"/com/nokia/backup"
+#define BACKUP_PLUGIN_INTERFACE	"com.nokia.backup.plugin"
+#define BACKUP_DBUS_TIMEOUT	(1000 * 60 * 15)
+
+static const uint8_t FTP_TARGET[TARGET_SIZE] = {
+			0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
+			0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
+
+static const uint8_t PCSUITE_WHO[PCSUITE_WHO_SIZE] = {
+			'P', 'C', ' ', 'S', 'u', 'i', 't', 'e' };
+
+struct pcsuite_session {
+	struct ftp_session *ftp;
+	char *lock_file;
+	int fd;
+};
+
+static void *pcsuite_connect(struct obex_session *os, int *err)
+{
+	struct pcsuite_session *pcsuite;
+	struct ftp_session *ftp;
+	int fd;
+	char *filename;
+
+	DBG("");
+
+	ftp = ftp_connect(os, err);
+	if (ftp == NULL)
+		return NULL;
+
+	filename = g_build_filename(g_get_home_dir(), ".pcsuite", NULL);
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
+	if (fd < 0 && errno != EEXIST) {
+		error("open(%s): %s(%d)", filename, strerror(errno), errno);
+		goto fail;
+	}
+
+	/* Try to remove the file before retrying since it could be
+	   that some process left/crash without removing it */
+	if (fd < 0) {
+		if (remove(filename) < 0) {
+			error("remove(%s): %s(%d)", filename, strerror(errno),
+									errno);
+			goto fail;
+		}
+
+		fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
+		if (fd < 0) {
+			error("open(%s): %s(%d)", filename, strerror(errno),
+									errno);
+			goto fail;
+		}
+	}
+
+	DBG("%s created", filename);
+
+	pcsuite = g_new0(struct pcsuite_session, 1);
+	pcsuite->ftp = ftp;
+	pcsuite->lock_file = filename;
+	pcsuite->fd = fd;
+
+	DBG("session %p created", pcsuite);
+
+	if (err)
+		*err = 0;
+
+	return pcsuite;
+
+fail:
+	if (ftp)
+		ftp_disconnect(os, ftp);
+	if (err)
+		*err = -errno;
+
+	g_free(filename);
+
+	return NULL;
+}
+
+static int pcsuite_get(struct obex_session *os, void *user_data)
+{
+	struct pcsuite_session *pcsuite = user_data;
+
+	DBG("%p", pcsuite);
+
+	return ftp_get(os, pcsuite->ftp);
+}
+
+static int pcsuite_chkput(struct obex_session *os, void *user_data)
+{
+	struct pcsuite_session *pcsuite = user_data;
+
+	DBG("%p", pcsuite);
+
+	return ftp_chkput(os, pcsuite->ftp);
+}
+
+static int pcsuite_put(struct obex_session *os, void *user_data)
+{
+	struct pcsuite_session *pcsuite = user_data;
+
+	DBG("%p", pcsuite);
+
+	return ftp_put(os, pcsuite->ftp);
+}
+
+static int pcsuite_setpath(struct obex_session *os, void *user_data)
+{
+	struct pcsuite_session *pcsuite = user_data;
+
+	DBG("%p", pcsuite);
+
+	return ftp_setpath(os, pcsuite->ftp);
+}
+
+static int pcsuite_action(struct obex_session *os, void *user_data)
+{
+	struct pcsuite_session *pcsuite = user_data;
+
+	DBG("%p", pcsuite);
+
+	return ftp_action(os, pcsuite->ftp);
+}
+
+static void pcsuite_disconnect(struct obex_session *os, void *user_data)
+{
+	struct pcsuite_session *pcsuite = user_data;
+
+	DBG("%p", pcsuite);
+
+	if (pcsuite->fd >= 0)
+		close(pcsuite->fd);
+
+	if (pcsuite->lock_file) {
+		remove(pcsuite->lock_file);
+		g_free(pcsuite->lock_file);
+	}
+
+	if (pcsuite->ftp)
+		ftp_disconnect(os, pcsuite->ftp);
+
+	g_free(pcsuite);
+}
+
+static struct obex_service_driver pcsuite = {
+	.name = "Nokia OBEX PC Suite Services",
+	.service = OBEX_PCSUITE,
+	.channel = PCSUITE_CHANNEL,
+	.secure = TRUE,
+	.record = PCSUITE_RECORD,
+	.target = FTP_TARGET,
+	.target_size = TARGET_SIZE,
+	.who = PCSUITE_WHO,
+	.who_size = PCSUITE_WHO_SIZE,
+	.connect = pcsuite_connect,
+	.get = pcsuite_get,
+	.put = pcsuite_put,
+	.chkput = pcsuite_chkput,
+	.setpath = pcsuite_setpath,
+	.action = pcsuite_action,
+	.disconnect = pcsuite_disconnect
+};
+
+struct backup_object {
+	char *cmd;
+	int fd;
+	int oflag;
+	int error_code;
+	mode_t mode;
+	DBusPendingCall *pending_call;
+	DBusConnection *conn;
+};
+
+static void on_backup_dbus_notify(DBusPendingCall *pending_call,
+					void *user_data)
+{
+	struct backup_object *obj = user_data;
+	DBusMessage *reply;
+	const char *filename;
+	int error_code;
+
+	DBG("Notification received for pending call - %s", obj->cmd);
+
+	reply = dbus_pending_call_steal_reply(pending_call);
+
+	if (reply && dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32,
+					&error_code, DBUS_TYPE_STRING,
+					&filename, DBUS_TYPE_INVALID)) {
+
+		obj->error_code = error_code;
+
+		if (filename) {
+			DBG("Notification - file path = %s, error_code = %d",
+					filename, error_code);
+			if (error_code == 0)
+				obj->fd = open(filename,obj->oflag,obj->mode);
+		}
+
+	} else
+		DBG("Notification timed out or connection got closed");
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_pending_call_unref(pending_call);
+	obj->pending_call = NULL;
+	dbus_connection_unref(obj->conn);
+	obj->conn = NULL;
+
+	if (obj->fd >= 0) {
+		DBG("File opened, setting io flags, cmd = %s",
+				obj->cmd);
+		if (obj->oflag == O_RDONLY)
+			obex_object_set_io_flags(user_data, G_IO_IN, 0);
+		else
+			obex_object_set_io_flags(user_data, G_IO_OUT, 0);
+	} else {
+		DBG("File open error, setting io error, cmd = %s",
+				obj->cmd);
+		obex_object_set_io_flags(user_data, G_IO_ERR, -EPERM);
+	}
+}
+
+static gboolean send_backup_dbus_message(const char *oper,
+					struct backup_object *obj,
+					size_t *size)
+{
+	DBusConnection *conn;
+	DBusMessage *msg;
+	DBusPendingCall *pending_call;
+	gboolean ret = FALSE;
+	dbus_uint32_t file_size;
+
+	file_size = size ? *size : 0;
+
+	conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
+
+	if (conn == NULL)
+		return FALSE;
+
+	msg = dbus_message_new_method_call(BACKUP_BUS_NAME, BACKUP_PATH,
+						BACKUP_PLUGIN_INTERFACE,
+						"request");
+	if (msg == NULL) {
+		dbus_connection_unref(conn);
+		return FALSE;
+	}
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &oper,
+					DBUS_TYPE_STRING, &obj->cmd,
+					DBUS_TYPE_INT32, &file_size,
+					DBUS_TYPE_INVALID);
+
+	if (strcmp(oper, "open") == 0) {
+		ret = g_dbus_send_message_with_reply(conn, msg, &pending_call,
+							BACKUP_DBUS_TIMEOUT);
+		dbus_message_unref(msg);
+		if (ret) {
+			obj->conn = conn;
+			obj->pending_call = pending_call;
+			ret = dbus_pending_call_set_notify(pending_call,
+							on_backup_dbus_notify,
+							obj, NULL);
+		} else
+			dbus_connection_unref(conn);
+	} else {
+		g_dbus_send_message(conn, msg);
+		dbus_connection_unref(conn);
+	}
+
+	return ret;
+}
+
+static void *backup_open(const char *name, int oflag, mode_t mode,
+				void *context, size_t *size, int *err)
+{
+	struct backup_object *obj = g_new0(struct backup_object, 1);
+
+	DBG("cmd = %s", name);
+
+	obj->cmd = g_path_get_basename(name);
+	obj->oflag = oflag;
+	obj->mode = mode;
+	obj->fd = -1;
+	obj->pending_call = NULL;
+	obj->conn = NULL;
+	obj->error_code = 0;
+
+	if (send_backup_dbus_message("open", obj, size) == FALSE) {
+		g_free(obj);
+		obj = NULL;
+	}
+
+	if (err)
+		*err = 0;
+
+	return obj;
+}
+
+static int backup_close(void *object)
+{
+	struct backup_object *obj = object;
+	size_t size = 0;
+
+	DBG("cmd = %s", obj->cmd);
+
+	if (obj->fd != -1)
+		close(obj->fd);
+
+	if (obj->pending_call) {
+		dbus_pending_call_cancel(obj->pending_call);
+		dbus_pending_call_unref(obj->pending_call);
+		dbus_connection_unref(obj->conn);
+	}
+
+	send_backup_dbus_message("close", obj, &size);
+
+	g_free(obj->cmd);
+	g_free(obj);
+
+	return 0;
+}
+
+static ssize_t backup_read(void *object, void *buf, size_t count)
+{
+	struct backup_object *obj = object;
+	ssize_t ret = 0;
+
+	if (obj->pending_call) {
+		DBG("cmd = %s, IN WAITING STAGE", obj->cmd);
+		return -EAGAIN;
+	}
+
+	if (obj->fd != -1) {
+		DBG("cmd = %s, READING DATA", obj->cmd);
+		ret = read(obj->fd, buf, count);
+		if (ret < 0)
+			ret = -errno;
+	} else {
+		DBG("cmd = %s, PERMANENT FAILURE", obj->cmd);
+		ret = obj->error_code ? -obj->error_code : -ENOENT;
+	}
+
+	return ret;
+}
+
+static ssize_t backup_write(void *object, const void *buf, size_t count)
+{
+	struct backup_object *obj = object;
+	ssize_t ret = 0;
+
+	if (obj->pending_call) {
+		DBG("cmd = %s, IN WAITING STAGE", obj->cmd);
+		return -EAGAIN;
+	}
+
+	if (obj->fd != -1) {
+		ret = write(obj->fd, buf, count);
+
+		DBG("cmd = %s, WRITTING", obj->cmd);
+
+		if (ret < 0) {
+			error("backup: cmd = %s", obj->cmd);
+			ret = -errno;
+		}
+	} else {
+		error("backup: cmd = %s", obj->cmd);
+		ret = obj->error_code ? -obj->error_code : -ENOENT;
+	}
+
+	return ret;
+}
+
+static int backup_flush(void *object)
+{
+	DBG("%p", object);
+
+	return 0;
+}
+
+static struct obex_mime_type_driver backup = {
+	.target = FTP_TARGET,
+	.target_size = TARGET_SIZE,
+	.mimetype = "application/vnd.nokia-backup",
+	.open = backup_open,
+	.close = backup_close,
+	.read = backup_read,
+	.write = backup_write,
+	.flush = backup_flush,
+};
+
+static int pcsuite_init(void)
+{
+	int err;
+
+	err = obex_service_driver_register(&pcsuite);
+	if (err < 0)
+		return err;
+
+	err = obex_mime_type_driver_register(&backup);
+	if (err < 0)
+		obex_service_driver_unregister(&pcsuite);
+
+	return err;
+}
+
+static void pcsuite_exit(void)
+{
+	obex_mime_type_driver_unregister(&backup);
+	obex_service_driver_unregister(&pcsuite);
+}
+
+OBEX_PLUGIN_DEFINE(pcsuite, pcsuite_init, pcsuite_exit)
diff --git a/bluez/repo/obexd/plugins/phonebook-dummy.c b/bluez/repo/obexd/plugins/phonebook-dummy.c
new file mode 100644
index 0000000..eeb078f
--- /dev/null
+++ b/bluez/repo/obexd/plugins/phonebook-dummy.c
@@ -0,0 +1,582 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2009-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libical/ical.h>
+#include <libical/vobject.h>
+#include <libical/vcc.h>
+
+#include "obexd/src/log.h"
+#include "phonebook.h"
+
+typedef void (*vcard_func_t) (const char *file, VObject *vo, void *user_data);
+
+struct dummy_data {
+	phonebook_cb cb;
+	void *user_data;
+	const struct apparam_field *apparams;
+	char *folder;
+	int fd;
+	guint id;
+};
+
+struct cache_query {
+	phonebook_entry_cb entry_cb;
+	phonebook_cache_ready_cb ready_cb;
+	void *user_data;
+	DIR *dp;
+};
+
+static char *root_folder = NULL;
+
+static void dummy_free(void *user_data)
+{
+	struct dummy_data *dummy = user_data;
+
+	if (dummy->fd >= 0)
+		close(dummy->fd);
+
+	g_free(dummy->folder);
+	g_free(dummy);
+}
+
+static void query_free(void *user_data)
+{
+	struct cache_query *query = user_data;
+
+	if (query->dp)
+		closedir(query->dp);
+
+	g_free(query);
+}
+
+int phonebook_init(void)
+{
+	if (root_folder)
+		return 0;
+
+	/* FIXME: It should NOT be hard-coded */
+	root_folder = g_build_filename(getenv("HOME"), "phonebook", NULL);
+
+	return 0;
+}
+
+void phonebook_exit(void)
+{
+	g_free(root_folder);
+	root_folder = NULL;
+}
+
+static int handle_cmp(gconstpointer a, gconstpointer b)
+{
+	const char *f1 = a;
+	const char *f2 = b;
+	unsigned int i1, i2;
+
+	if (sscanf(f1, "%u.vcf", &i1) != 1)
+		return -1;
+
+	if (sscanf(f2, "%u.vcf", &i2) != 1)
+		return -1;
+
+	return (i1 - i2);
+}
+
+static int foreach_vcard(DIR *dp, vcard_func_t func, uint16_t offset,
+			uint16_t maxlistcount, void *user_data, uint16_t *count)
+{
+	struct dirent *ep;
+	GSList *sorted = NULL, *l;
+	VObject *v;
+	FILE *fp;
+	int err, fd, folderfd;
+	uint16_t n = 0;
+
+	folderfd = dirfd(dp);
+	if (folderfd < 0) {
+		err = errno;
+		error("dirfd(): %s(%d)", strerror(err), err);
+		return -err;
+	}
+
+	/*
+	 * Sorting vcards by file name. versionsort is a GNU extension.
+	 * The simple sorting function implemented on handle_cmp address
+	 * vcards handle only(handle is always a number). This sort function
+	 * doesn't address filename started by "0".
+	 */
+	while ((ep = readdir(dp))) {
+		char *filename;
+
+		if (ep->d_name[0] == '.')
+			continue;
+
+		filename = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
+		if (filename == NULL) {
+			error("g_filename_to_utf8: invalid filename");
+			continue;
+		}
+
+		if (!g_str_has_suffix(filename, ".vcf")) {
+			g_free(filename);
+			continue;
+		}
+
+		sorted = g_slist_insert_sorted(sorted, filename, handle_cmp);
+	}
+
+	/*
+	 * Filtering only the requested vCards attributes. Offset
+	 * shall be based on the first entry of the phonebook.
+	 */
+	for (l = g_slist_nth(sorted, offset);
+			l && n < maxlistcount; l = l->next) {
+		const char *filename = l->data;
+
+		fd = openat(folderfd, filename, O_RDONLY);
+		if (fd < 0) {
+			err = errno;
+			error("openat(%s): %s(%d)", filename, strerror(err), err);
+			continue;
+		}
+
+		fp = fdopen(fd, "r");
+		v = Parse_MIME_FromFile(fp);
+		if (v != NULL) {
+			func(filename, v, user_data);
+			deleteVObject(v);
+			n++;
+		}
+
+		close(fd);
+	}
+
+	g_slist_free_full(sorted, g_free);
+
+	if (count)
+		*count = n;
+
+	return 0;
+}
+
+static void entry_concat(const char *filename, VObject *v, void *user_data)
+{
+	GString *buffer = user_data;
+	char tmp[1024];
+	int len;
+
+	/*
+	 * VObject API uses len for IN and OUT
+	 * Written bytes is also returned in the len variable
+	 */
+	len = sizeof(tmp);
+	memset(tmp, 0, len);
+
+	writeMemVObject(tmp, &len, v);
+
+	/* FIXME: only the requested fields must be added */
+	g_string_append_len(buffer, tmp, len);
+}
+
+static gboolean read_dir(void *user_data)
+{
+	struct dummy_data *dummy = user_data;
+	GString *buffer;
+	DIR *dp;
+	uint16_t count = 0, max, offset;
+
+	buffer = g_string_new("");
+
+	dp = opendir(dummy->folder);
+	if (dp == NULL) {
+		int err = errno;
+		DBG("opendir(): %s(%d)", strerror(err), err);
+		goto done;
+	}
+
+	/*
+	 * For PullPhoneBook function, the decision of returning the size
+	 * or contacts is made in the PBAP core. When MaxListCount is ZERO,
+	 * PCE wants to know the size of a given folder, PSE shall ignore all
+	 * other applicattion parameters that may be present in the request.
+	 */
+	if (dummy->apparams->maxlistcount == 0) {
+		max = 0xffff;
+		offset = 0;
+	} else {
+		max = dummy->apparams->maxlistcount;
+		offset = dummy->apparams->liststartoffset;
+	}
+
+	foreach_vcard(dp, entry_concat, offset, max, buffer, &count);
+
+	closedir(dp);
+done:
+	/* FIXME: Missing vCards fields filtering */
+	dummy->cb(buffer->str, buffer->len, count, 0, TRUE, dummy->user_data);
+
+	g_string_free(buffer, TRUE);
+
+	return FALSE;
+}
+
+static void entry_notify(const char *filename, VObject *v, void *user_data)
+{
+	struct cache_query *query = user_data;
+	VObject *property, *subproperty;
+	GString *name;
+	const char *tel;
+	long unsigned int handle;
+
+	property = isAPropertyOf(v, VCNameProp);
+	if (!property)
+		return;
+
+	if (sscanf(filename, "%lu.vcf", &handle) != 1)
+		return;
+
+	if (handle > UINT32_MAX)
+		return;
+
+	/* LastName; FirstName; MiddleName; Prefix; Suffix */
+
+	name = g_string_new("");
+	subproperty = isAPropertyOf(property, VCFamilyNameProp);
+	if (subproperty) {
+		g_string_append(name,
+				fakeCString(vObjectUStringZValue(subproperty)));
+	}
+
+	subproperty = isAPropertyOf(property, VCGivenNameProp);
+	if (subproperty)
+		g_string_append_printf(name, ";%s",
+				fakeCString(vObjectUStringZValue(subproperty)));
+
+	subproperty = isAPropertyOf(property, VCAdditionalNamesProp);
+	if (subproperty)
+		g_string_append_printf(name, ";%s",
+				fakeCString(vObjectUStringZValue(subproperty)));
+
+	subproperty = isAPropertyOf(property, VCNamePrefixesProp);
+	if (subproperty)
+		g_string_append_printf(name, ";%s",
+				fakeCString(vObjectUStringZValue(subproperty)));
+
+
+	subproperty = isAPropertyOf(property, VCNameSuffixesProp);
+	if (subproperty)
+		g_string_append_printf(name, ";%s",
+				fakeCString(vObjectUStringZValue(subproperty)));
+
+	property = isAPropertyOf(v, VCTelephoneProp);
+
+	tel = property ? fakeCString(vObjectUStringZValue(property)) : NULL;
+
+	query->entry_cb(filename, handle, name->str, NULL, tel,
+							query->user_data);
+	g_string_free(name, TRUE);
+}
+
+static gboolean create_cache(void *user_data)
+{
+	struct cache_query *query = user_data;
+
+	/*
+	 * MaxListCount and ListStartOffset shall not be used
+	 * when creating the cache. All entries shall be fetched.
+	 * PBAP core is responsible for consider these application
+	 * parameters before reply the entries.
+	 */
+	foreach_vcard(query->dp, entry_notify, 0, 0xffff, query, NULL);
+
+	query->ready_cb(query->user_data);
+
+	return FALSE;
+}
+
+static gboolean read_entry(void *user_data)
+{
+	struct dummy_data *dummy = user_data;
+	char buffer[1024];
+	ssize_t count;
+
+	memset(buffer, 0, sizeof(buffer));
+	count = read(dummy->fd, buffer, sizeof(buffer));
+
+	if (count < 0) {
+		int err = errno;
+		error("read(): %s(%d)", strerror(err), err);
+		count = 0;
+	}
+
+	/* FIXME: Missing vCards fields filtering */
+
+	dummy->cb(buffer, count, 1, 0, TRUE, dummy->user_data);
+
+	return FALSE;
+}
+
+static gboolean is_dir(const char *dir)
+{
+	struct stat st;
+
+	if (stat(dir, &st) < 0) {
+		int err = errno;
+		error("stat(%s): %s (%d)", dir, strerror(err), err);
+		return FALSE;
+	}
+
+	return S_ISDIR(st.st_mode);
+}
+
+char *phonebook_set_folder(const char *current_folder,
+		const char *new_folder, uint8_t flags, int *err)
+{
+	gboolean root, child;
+	char *tmp1, *tmp2, *base, *absolute, *relative = NULL;
+	int len, ret = 0;
+
+	root = (g_strcmp0("/", current_folder) == 0);
+	child = (new_folder && strlen(new_folder) != 0);
+
+	switch (flags) {
+	case 0x02:
+		/* Go back to root */
+		if (!child) {
+			relative = g_strdup("/");
+			goto done;
+		}
+
+		relative = g_build_filename(current_folder, new_folder, NULL);
+		break;
+	case 0x03:
+		/* Go up 1 level */
+		if (root) {
+			/* Already root */
+			ret = -EBADR;
+			goto done;
+		}
+
+		/*
+		 * Removing one level of the current folder. Current folder
+		 * contains AT LEAST one level since it is not at root folder.
+		 * Use glib utility functions to handle invalid chars in the
+		 * folder path properly.
+		 */
+		tmp1 = g_path_get_basename(current_folder);
+		tmp2 = g_strrstr(current_folder, tmp1);
+		len = tmp2 - (current_folder + 1);
+
+		g_free(tmp1);
+
+		if (len == 0)
+			base = g_strdup("/");
+		else
+			base = g_strndup(current_folder, len);
+
+		/* Return: one level only */
+		if (!child) {
+			relative = base;
+			goto done;
+		}
+
+		relative = g_build_filename(base, new_folder, NULL);
+		g_free(base);
+
+		break;
+	default:
+		ret = -EBADR;
+		break;
+	}
+
+done:
+	if (!relative) {
+		if (err)
+			*err = ret;
+
+		return NULL;
+	}
+
+	absolute = g_build_filename(root_folder, relative, NULL);
+	if (!is_dir(absolute)) {
+		g_free(relative);
+		relative = NULL;
+		ret = -ENOENT;
+	}
+
+	g_free(absolute);
+
+	if (err)
+		*err = ret;
+
+	return relative;
+}
+
+void phonebook_req_finalize(void *request)
+{
+	struct dummy_data *dummy = request;
+
+	/* dummy_data will be cleaned when request will be finished via
+	 * g_source_remove */
+	if (dummy && dummy->id)
+		g_source_remove(dummy->id);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err)
+{
+	struct dummy_data *dummy;
+	char *filename, *folder;
+
+	/*
+	 * Main phonebook objects will be created dinamically based on the
+	 * folder content. All vcards inside the given folder will be appended
+	 * in the "virtual" main phonebook object.
+	 */
+
+	filename = g_build_filename(root_folder, name, NULL);
+
+	if (!g_str_has_suffix(filename, ".vcf")) {
+		g_free(filename);
+		if (err)
+			*err = -EBADR;
+		return NULL;
+	}
+
+	folder = g_strndup(filename, strlen(filename) - 4);
+	g_free(filename);
+	if (!is_dir(folder)) {
+		g_free(folder);
+		if (err)
+			*err = -ENOENT;
+		return NULL;
+	}
+
+	dummy = g_new0(struct dummy_data, 1);
+	dummy->cb = cb;
+	dummy->user_data = user_data;
+	dummy->apparams = params;
+	dummy->folder = folder;
+	dummy->fd = -1;
+
+	if (err)
+		*err = 0;
+
+	return dummy;
+}
+
+int phonebook_pull_read(void *request)
+{
+	struct dummy_data *dummy = request;
+
+	if (!dummy)
+		return -ENOENT;
+
+	dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_dir, dummy,
+								dummy_free);
+
+	return 0;
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+			const struct apparam_field *params, phonebook_cb cb,
+			void *user_data, int *err)
+{
+	struct dummy_data *dummy;
+	char *filename;
+	int fd;
+	guint ret;
+
+	filename = g_build_filename(root_folder, folder, id, NULL);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		DBG("open(): %s(%d)", strerror(errno), errno);
+		if (err)
+			*err = -ENOENT;
+		return NULL;
+	}
+
+	dummy = g_new0(struct dummy_data, 1);
+	dummy->cb = cb;
+	dummy->user_data = user_data;
+	dummy->apparams = params;
+	dummy->fd = fd;
+
+	ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy,
+								dummy_free);
+
+	if (err)
+		*err = 0;
+
+	return GINT_TO_POINTER(ret);
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+	struct cache_query *query;
+	char *foldername;
+	DIR *dp;
+	guint ret;
+
+	foldername = g_build_filename(root_folder, name, NULL);
+	dp = opendir(foldername);
+	g_free(foldername);
+
+	if (dp == NULL) {
+		DBG("opendir(): %s(%d)", strerror(errno), errno);
+		if (err)
+			*err = -ENOENT;
+		return NULL;
+	}
+
+	query = g_new0(struct cache_query, 1);
+	query->entry_cb = entry_cb;
+	query->ready_cb = ready_cb;
+	query->user_data = user_data;
+	query->dp = dp;
+
+	ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, query,
+								query_free);
+
+	if (err)
+		*err = 0;
+
+	return GINT_TO_POINTER(ret);
+}
diff --git a/bluez/repo/obexd/plugins/phonebook-ebook.c b/bluez/repo/obexd/plugins/phonebook-ebook.c
new file mode 100644
index 0000000..c422585
--- /dev/null
+++ b/bluez/repo/obexd/plugins/phonebook-ebook.c
@@ -0,0 +1,708 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2009-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <libebook/e-book.h>
+
+#include "lib/bluetooth.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "phonebook.h"
+
+#define QUERY_FN "(contains \"family_name\" \"%s\")"
+#define QUERY_NAME "(contains \"given_name\" \"%s\")"
+#define QUERY_PHONE "(contains \"phone\" \"%s\")"
+
+struct query_context {
+	const struct apparam_field *params;
+	phonebook_cb contacts_cb;
+	phonebook_entry_cb entry_cb;
+	phonebook_cache_ready_cb ready_cb;
+	EBookQuery *query;
+	unsigned int count;
+	GString *buf;
+	char *id;
+	unsigned queued_calls;
+	void *user_data;
+	GSList *ebooks;
+	gboolean canceled;
+};
+
+static char *attribute_mask[] = {
+/* 0 */		"VERSION",
+		"FN",
+		"N",
+		"PHOTO",
+		"BDAY",
+		"ADR",
+		"LABEL",
+		"TEL",
+/* 8 */		"EMAIL",
+		"MAILER",
+		"TZ",
+		"GEO",
+		"TITLE",
+		"ROLE",
+		"LOGO",
+		"AGENT",
+/* 16 */	"ORG",
+		"NOTE",
+		"REV",
+		"SOUND",
+		"URL",
+		"UID",
+		"KEY",
+		"NICKNAME",
+/* 24 */	"CATEGORIES",
+		"PROID",
+		"CLASS",
+		"SORT-STRING",
+/* 28 */	"X-IRMC-CALL-DATETIME",
+		NULL
+
+};
+
+static void close_ebooks(GSList *ebooks)
+{
+	g_slist_free_full(ebooks, g_object_unref);
+}
+
+static void free_query_context(struct query_context *data)
+{
+	g_free(data->id);
+
+	if (data->buf != NULL)
+		g_string_free(data->buf, TRUE);
+
+	if (data->query != NULL)
+		e_book_query_unref(data->query);
+
+	close_ebooks(data->ebooks);
+
+	g_free(data);
+}
+
+static char *evcard_to_string(EVCard *evcard, unsigned int format,
+							uint64_t filter)
+{
+	EVCard *evcard2;
+	GList *l;
+	char *vcard;
+
+	if (!filter)
+		return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30);
+		/* XXX There is no support for VCARD 2.1 at this time */
+
+	/*
+	 * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL.
+	 * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL
+	 */
+	filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85;
+
+	l = e_vcard_get_attributes(evcard);
+	evcard2 = e_vcard_new();
+	for (; l; l = g_list_next(l)) {
+		EVCardAttribute *attrib = l->data;
+		const char *name;
+		int i;
+
+		if (!attrib)
+			continue;
+
+		name = e_vcard_attribute_get_name(attrib);
+
+		for (i = 0; attribute_mask[i] != NULL; i++) {
+			if (!(filter & (1 << i)))
+				continue;
+			if (g_strcmp0(name, attribute_mask[i]) != 0)
+				continue;
+
+			e_vcard_add_attribute(evcard2,
+					e_vcard_attribute_copy(attrib));
+		}
+	}
+
+	vcard = e_vcard_to_string(evcard2, format);
+	g_object_unref(evcard2);
+
+	return vcard;
+}
+
+static void ebookpull_cb(EBook *book, const GError *gerr, GList *contacts,
+							void *user_data)
+{
+	struct query_context *data = user_data;
+	GList *l;
+	unsigned int count, maxcount;
+
+	data->queued_calls--;
+
+	if (data->canceled)
+		goto canceled;
+
+	if (gerr != NULL) {
+		error("E-Book query failed: %s", gerr->message);
+		goto done;
+	}
+
+	DBG("");
+
+	/*
+	 * When MaxListCount is zero, PCE wants to know the number of used
+	 * indexes in the phonebook of interest. All other parameters that
+	 * may be present in the request shall be ignored.
+	 */
+	maxcount = data->params->maxlistcount;
+	if (maxcount == 0) {
+		data->count += g_list_length(contacts);
+		goto done;
+	}
+
+	l = g_list_nth(contacts, data->params->liststartoffset);
+
+	for (count = 0; l && count + data->count < maxcount; l = g_list_next(l),
+								count++) {
+		EContact *contact = E_CONTACT(l->data);
+		EVCard *evcard = E_VCARD(contact);
+		char *vcard;
+
+		vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
+						data->params->filter);
+
+		data->buf = g_string_append(data->buf, vcard);
+		data->buf = g_string_append(data->buf, "\r\n");
+		g_free(vcard);
+	}
+
+	DBG("collected %d vcards", count);
+
+	data->count += count;
+
+	g_list_free_full(contacts, g_object_unref);
+
+done:
+	if (data->queued_calls == 0) {
+		GString *buf = data->buf;
+		data->buf = NULL;
+
+		data->contacts_cb(buf->str, buf->len, data->count,
+						0, TRUE, data->user_data);
+
+		g_string_free(buf, TRUE);
+
+	}
+
+	return;
+
+canceled:
+	if (data->queued_calls == 0)
+		free_query_context(data);
+}
+
+static void ebook_entry_cb(EBook *book, const GError *gerr,
+				EContact *contact, void *user_data)
+{
+	struct query_context *data = user_data;
+	EVCard *evcard;
+	char *vcard;
+	size_t len;
+
+	data->queued_calls--;
+
+	if (data->canceled)
+		goto done;
+
+	if (gerr != NULL) {
+		error("E-Book query failed: %s", gerr->message);
+		goto done;
+	}
+
+	DBG("");
+
+	evcard = E_VCARD(contact);
+
+	vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
+					data->params->filter);
+
+	len = vcard ? strlen(vcard) : 0;
+
+	data->count++;
+	data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data);
+
+	g_free(vcard);
+	g_object_unref(contact);
+
+	return;
+
+done:
+	if (data->queued_calls == 0) {
+		if (data->count == 0)
+			data->contacts_cb(NULL, 0, 1, 0, TRUE,
+						data->user_data);
+		else if (data->canceled)
+			free_query_context(data);
+	}
+}
+
+static char *evcard_name_attribute_to_string(EVCard *evcard)
+{
+	EVCardAttribute *attrib;
+	GList *l;
+	GString *name = NULL;
+
+	attrib = e_vcard_get_attribute(evcard, EVC_N);
+	if (!attrib)
+		return NULL;
+
+	for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) {
+		const char *value = l->data;
+
+		if (!strlen(value))
+			continue;
+
+		if (!name)
+			name = g_string_new(value);
+		else {
+			name = g_string_append(name, ";");
+			name = g_string_append(name, l->data);
+		}
+	}
+
+	if (!name)
+		return NULL;
+
+	return g_string_free(name, FALSE);
+}
+
+static void cache_cb(EBook *book, const GError *gerr, GList *contacts,
+							void *user_data)
+{
+	struct query_context *data = user_data;
+	GList *l;
+
+	data->queued_calls--;
+
+	if (data->canceled)
+		goto canceled;
+
+	if (gerr != NULL) {
+		error("E-Book operation failed: %s", gerr->message);
+		goto done;
+	}
+
+	DBG("");
+
+	for (l = contacts; l; l = g_list_next(l)) {
+		EContact *contact = E_CONTACT(l->data);
+		EVCard *evcard = E_VCARD(contact);
+		EVCardAttribute *attrib;
+		char *uid, *tel, *name;
+
+		name = evcard_name_attribute_to_string(evcard);
+		if (!name)
+			continue;
+
+		attrib = e_vcard_get_attribute(evcard, EVC_UID);
+		if (!attrib)
+			continue;
+
+		uid = e_vcard_attribute_get_value(attrib);
+		if (!uid)
+			continue;
+
+		attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+		if (attrib)
+			tel = e_vcard_attribute_get_value(attrib);
+		else
+			tel = g_strdup("");
+
+		data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL,
+							tel, data->user_data);
+
+		g_free(name);
+		g_free(uid);
+		g_free(tel);
+	}
+
+	g_list_free_full(contacts, g_object_unref);
+
+done:
+	if (data->queued_calls == 0)
+		data->ready_cb(data->user_data);
+
+	return;
+
+canceled:
+	if (data->queued_calls == 0)
+		free_query_context(data);
+}
+
+static GSList *traverse_sources(GSList *ebooks, GSList *sources,
+							char **default_src) {
+	GError *gerr = NULL;
+
+	for (; sources != NULL; sources = g_slist_next(sources)) {
+		char *uri;
+		ESource *source = E_SOURCE(sources->data);
+		EBook *ebook = e_book_new(source, &gerr);
+
+		if (ebook == NULL) {
+			error("Can't create user's address book: %s",
+								gerr->message);
+			g_clear_error(&gerr);
+			continue;
+		}
+
+		uri = e_source_get_uri(source);
+		if (g_strcmp0(*default_src, uri) == 0) {
+			g_free(uri);
+			continue;
+		}
+		g_free(uri);
+
+		if (e_book_open(ebook, FALSE, &gerr) == FALSE) {
+			error("Can't open e-book address book: %s",
+							gerr->message);
+			g_object_unref(ebook);
+			g_clear_error(&gerr);
+			continue;
+		}
+
+		if (*default_src == NULL)
+			*default_src = e_source_get_uri(source);
+
+		DBG("%s address book opened", e_source_peek_name(source));
+
+		ebooks = g_slist_append(ebooks, ebook);
+	}
+
+	return ebooks;
+}
+
+int phonebook_init(void)
+{
+	g_type_init();
+
+	return 0;
+}
+
+static GSList *open_ebooks(void)
+{
+	GError *gerr = NULL;
+	ESourceList *src_list;
+	GSList *list;
+	char *default_src = NULL;
+	GSList *ebooks = NULL;
+
+	if (e_book_get_addressbooks(&src_list, &gerr) == FALSE) {
+		error("Can't list user's address books: %s", gerr->message);
+		g_error_free(gerr);
+		return NULL;
+	}
+
+	list = e_source_list_peek_groups(src_list);
+	while (list != NULL) {
+		ESourceGroup *group = E_SOURCE_GROUP(list->data);
+		GSList *sources = e_source_group_peek_sources(group);
+
+		ebooks = traverse_sources(ebooks, sources, &default_src);
+
+		list = list->next;
+	}
+
+	g_free(default_src);
+	g_object_unref(src_list);
+
+	return ebooks;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder,
+		const char *new_folder, uint8_t flags, int *err)
+{
+	gboolean root, child;
+	char *fullname = NULL, *tmp1, *tmp2, *base;
+	int ret = 0, len;
+
+	root = (g_strcmp0("/", current_folder) == 0);
+	child = (new_folder && strlen(new_folder) != 0);
+
+	/* Evolution back-end will support /telecom/pb folder only */
+
+	switch (flags) {
+	case 0x02:
+		/* Go back to root */
+		if (!child) {
+			fullname = g_strdup("/");
+			goto done;
+		}
+
+		/* Go down 1 level */
+		fullname = g_build_filename(current_folder, new_folder, NULL);
+		if (strcmp(PB_TELECOM_FOLDER, fullname) != 0 &&
+				strcmp(PB_CONTACTS_FOLDER, fullname) != 0) {
+			g_free(fullname);
+			fullname = NULL;
+			ret = -ENOENT;
+		}
+
+		break;
+	case 0x03:
+		/* Go up 1 level */
+		if (root) {
+			/* Already root */
+			ret = -EBADR;
+			goto done;
+		}
+
+		/*
+		 * Removing one level of the current folder. Current folder
+		 * contains AT LEAST one level since it is not at root folder.
+		 * Use glib utility functions to handle invalid chars in the
+		 * folder path properly.
+		 */
+		tmp1 = g_path_get_basename(current_folder);
+		tmp2 = g_strrstr(current_folder, tmp1);
+		len = tmp2 - (current_folder + 1);
+
+		g_free(tmp1);
+
+		if (len == 0)
+			base = g_strdup("/");
+		else
+			base = g_strndup(current_folder, len);
+
+		/* Return one level only */
+		if (!child) {
+			fullname = base;
+			goto done;
+		}
+
+		fullname = g_build_filename(base, new_folder, NULL);
+		if (strcmp(fullname, PB_TELECOM_FOLDER) != 0 &&
+				strcmp(fullname, PB_CONTACTS_FOLDER) != 0) {
+			g_free(fullname);
+			fullname = NULL;
+			ret = -ENOENT;
+		}
+
+		g_free(base);
+
+		break;
+	default:
+		ret = -EBADR;
+		break;
+	}
+
+done:
+	if (err)
+		*err = ret;
+
+	return fullname;
+}
+
+void phonebook_req_finalize(void *request)
+{
+	struct query_context *data = request;
+
+	if (data->queued_calls == 0)
+		free_query_context(data);
+	else
+		data->canceled = TRUE;
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err)
+{
+	struct query_context *data;
+
+	if (g_strcmp0(PB_CONTACTS, name) != 0) {
+		if (err)
+			*err = -ENOENT;
+
+		return NULL;
+	}
+
+	data = g_new0(struct query_context, 1);
+	data->contacts_cb = cb;
+	data->params = params;
+	data->user_data = user_data;
+	data->buf = g_string_new("");
+	data->query = e_book_query_any_field_contains("");
+	data->ebooks = open_ebooks();
+
+	if (err)
+		*err = data->ebooks == NULL ? -EIO : 0;
+
+	return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+	struct query_context *data = request;
+	GSList *l;
+
+	if (!data)
+		return -ENOENT;
+
+	for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+		EBook *ebook = l->data;
+
+		if (e_book_is_opened(ebook) == FALSE)
+			continue;
+
+		if (e_book_get_contacts_async(ebook, data->query,
+						ebookpull_cb, data) == TRUE)
+			data->queued_calls++;
+	}
+
+	if (data->queued_calls == 0)
+		return -ENOENT;
+
+	return 0;
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+				const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err)
+{
+	struct query_context *data;
+	GSList *l;
+
+	data = g_new0(struct query_context, 1);
+	data->contacts_cb = cb;
+	data->params = params;
+	data->user_data = user_data;
+	data->id = g_strdup(id);
+	data->ebooks = open_ebooks();
+
+	for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+		EBook *ebook = l->data;
+
+		if (e_book_is_opened(ebook) == FALSE)
+			continue;
+
+		if (e_book_get_contact_async(ebook, data->id,
+						ebook_entry_cb, data) == TRUE)
+			data->queued_calls++;
+	}
+
+	if (err)
+		*err = (data->queued_calls == 0 ? -ENOENT : 0);
+
+	return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+	struct query_context *data;
+	EBookQuery *query;
+	GSList *l;
+	EContact *me;
+	EVCard *evcard;
+	GError *gerr = NULL;
+	EBook *eb;
+	EVCardAttribute *attrib;
+	char *uid, *tel, *cname;
+
+	if (g_strcmp0(PB_CONTACTS_FOLDER, name) != 0) {
+		if (err)
+			*err = -ENOENT;
+
+		return NULL;
+	}
+
+	DBG("");
+
+	query = e_book_query_any_field_contains("");
+
+	data = g_new0(struct query_context, 1);
+	data->entry_cb = entry_cb;
+	data->ready_cb = ready_cb;
+	data->user_data = user_data;
+	data->query = query;
+	data->ebooks = open_ebooks();
+
+	/* Add 0.vcf */
+	if (e_book_get_self(&me, &eb, &gerr) == FALSE) {
+		g_error_free(gerr);
+		goto next;
+	}
+
+	evcard = E_VCARD(me);
+
+	cname = evcard_name_attribute_to_string(evcard);
+	if (!cname)
+		cname = g_strdup("");
+
+	attrib = e_vcard_get_attribute(evcard, EVC_UID);
+	uid = e_vcard_attribute_get_value(attrib);
+	if (!uid)
+		uid = g_strdup("");
+
+	attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+	if (attrib)
+		tel =  e_vcard_attribute_get_value(attrib);
+	else
+		tel = g_strdup("");
+
+	data->entry_cb(uid, 0, cname, NULL, tel, data->user_data);
+
+	data->count++;
+
+	g_free(cname);
+	g_free(uid);
+	g_free(tel);
+	g_object_unref(eb);
+
+next:
+	for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+		EBook *ebook = l->data;
+
+		if (e_book_is_opened(ebook) == FALSE)
+			continue;
+
+		if (e_book_get_contacts_async(ebook, query,
+						cache_cb, data) == TRUE)
+			data->queued_calls++;
+	}
+
+	if (err)
+		*err = (data->queued_calls == 0 ? -ENOENT : 0);
+
+	return data;
+}
diff --git a/bluez/repo/obexd/plugins/phonebook-tracker.c b/bluez/repo/obexd/plugins/phonebook-tracker.c
new file mode 100644
index 0000000..0743629
--- /dev/null
+++ b/bluez/repo/obexd/plugins/phonebook-tracker.c
@@ -0,0 +1,1718 @@
+/*
+ *  Phonebook access through D-Bus vCard and call history service
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *
+ *
+ *  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 <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "phonebook.h"
+#include "vcard.h"
+
+#define TRACKER_SERVICE "org.freedesktop.Tracker1"
+#define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources"
+#define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources"
+
+#define TRACKER_DEFAULT_CONTACT_ME "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#default-contact-me"
+#define AFFILATION_HOME "Home"
+#define AFFILATION_WORK "Work"
+#define ADDR_FIELD_AMOUNT 7
+#define PULL_QUERY_COL_AMOUNT 23
+#define COUNT_QUERY_COL_AMOUNT 1
+
+#define COL_PHONE_AFF 0 /* work/home phone numbers */
+#define COL_FULL_NAME 1
+#define COL_FAMILY_NAME 2
+#define COL_GIVEN_NAME 3
+#define COL_ADDITIONAL_NAME 4
+#define COL_NAME_PREFIX 5
+#define COL_NAME_SUFFIX 6
+#define COL_ADDR_AFF 7 /* addresses from affilation */
+#define COL_BIRTH_DATE 8
+#define COL_NICKNAME 9
+#define COL_URL 10
+#define COL_PHOTO 11
+#define COL_ORG_ROLE 12
+#define COL_UID 13
+#define COL_TITLE 14
+#define COL_AFF_TYPE 15
+#define COL_ORG_NAME 16
+#define COL_ORG_DEPARTMENT 17
+#define COL_EMAIL_AFF 18 /* email's from affilation (work/home) */
+#define COL_DATE 19
+#define COL_SENT 20
+#define COL_ANSWERED 21
+#define CONTACTS_ID_COL 22
+#define CONTACT_ID_PREFIX "urn:uuid:"
+#define CALL_ID_PREFIX "message:"
+
+#define FAX_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#FaxNumber"
+#define MOBILE_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#CellPhoneNumber"
+
+#define MAIN_DELIM "\30" /* Main delimiter between phones, addresses, emails*/
+#define SUB_DELIM "\31" /* Delimiter used in telephone number strings*/
+#define ADDR_DELIM "\37" /* Delimiter used for address data fields */
+#define MAX_FIELDS 100 /* Max amount of fields to be concatenated at once*/
+#define VCARDS_PART_COUNT 50 /* amount of vcards sent at once to PBAP core */
+#define QUERY_OFFSET_FORMAT "%s OFFSET %d"
+
+#define CONTACTS_QUERY_ALL						\
+"SELECT "								\
+"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number),"			\
+"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")"			\
+"WHERE {"								\
+"	?_role nco:hasPhoneNumber ?aff_number"				\
+"}) "									\
+"nco:fullname(?_contact) "						\
+"nco:nameFamily(?_contact) "						\
+"nco:nameGiven(?_contact) "						\
+"nco:nameAdditional(?_contact) "					\
+"nco:nameHonorificPrefix(?_contact) "					\
+"nco:nameHonorificSuffix(?_contact) "					\
+"(SELECT GROUP_CONCAT(fn:concat("					\
+"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","	\
+"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","	\
+"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:country(?aff_addr), \"\"), "			\
+"\"\31\", rdfs:label(?_role) ), "					\
+"\"\30\") "								\
+"WHERE {"								\
+"?_role nco:hasPostalAddress ?aff_addr"					\
+"}) "									\
+"nco:birthDate(?_contact) "						\
+"(SELECT "								\
+"	?nick "								\
+"	WHERE { "							\
+"		{ "							\
+"			?_contact nco:nickname ?nick "			\
+"		} UNION { "						\
+"			?_contact nco:hasAffiliation ?role . "		\
+"			?role nco:hasIMAddress ?im . "			\
+"			?im nco:imNickname ?nick "			\
+"		} "							\
+"	} "								\
+") "									\
+"(SELECT GROUP_CONCAT(fn:concat( "					\
+	"?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\
+	"), \"\30\") "							\
+	"WHERE {"							\
+		"?_role nco:url ?url_val . "				\
+"})"									\
+"nie:url(nco:photo(?_contact)) "					\
+"nco:role(?_role) "							\
+"nco:contactUID(?_contact) "						\
+"nco:title(?_role) "							\
+"rdfs:label(?_role) "							\
+"nco:fullname(nco:org(?_role))"						\
+"nco:department(?_role) "						\
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\","			\
+	"tracker:coalesce(rdfs:label(?_role), \"\")),"			\
+	"\"\30\") "							\
+	"WHERE { "							\
+	"?_role nco:hasEmailAddress "					\
+	"		[ nco:emailAddress ?emailaddress ] "		\
+	"}) "								\
+"\"NOTACALL\" \"false\" \"false\" "					\
+"?_contact "								\
+"WHERE {"								\
+"	?_contact a nco:PersonContact ."				\
+"	OPTIONAL {?_contact nco:hasAffiliation ?_role .}"		\
+"}"									\
+"ORDER BY tracker:id(?_contact)"
+
+#define CONTACTS_QUERY_ALL_LIST						\
+	"SELECT ?c nco:nameFamily(?c) "					\
+	"nco:nameGiven(?c) nco:nameAdditional(?c) "			\
+	"nco:nameHonorificPrefix(?c) nco:nameHonorificSuffix(?c) "	\
+	"(SELECT "							\
+		"?nick "						\
+		"WHERE { "						\
+			"{ "						\
+				"?c nco:nickname ?nick "		\
+			"} UNION { "					\
+				"?c nco:hasAffiliation ?role . "	\
+				"?role nco:hasIMAddress ?im . "		\
+				"?im nco:imNickname ?nick "		\
+			"} "						\
+		"} "							\
+	") "								\
+	"nco:phoneNumber(?h) "						\
+	"WHERE { "							\
+		"?c a nco:PersonContact . "				\
+	"OPTIONAL { ?c nco:hasPhoneNumber ?h . } "			\
+	"OPTIONAL { "							\
+		"?c nco:hasAffiliation ?a . "				\
+		"?a nco:hasPhoneNumber ?h . "				\
+	"} "								\
+	"} GROUP BY ?c"
+
+#define CALLS_CONSTRAINTS(CONSTRAINT)					\
+" WHERE { "								\
+	"?_call a nmo:Call . "						\
+	"?_unb_contact a nco:Contact . "				\
+	"?_unb_contact nco:hasPhoneNumber ?_cpn . "			\
+CONSTRAINT								\
+	"OPTIONAL { "							\
+		"{ SELECT ?_contact ?_no ?_role ?_number "		\
+			"count(?_contact) as ?cnt "			\
+		"WHERE { "						\
+			"?_contact a nco:PersonContact . "		\
+			"{ "						\
+				"?_contact nco:hasAffiliation ?_role . "\
+				"?_role nco:hasPhoneNumber ?_number . " \
+			"} UNION { "					\
+				"?_contact nco:hasPhoneNumber ?_number" \
+			"} "						\
+			"?_number maemo:localPhoneNumber ?_no . "	\
+		"} GROUP BY ?_no } "					\
+		"FILTER(?cnt = 1) "					\
+		"?_cpn maemo:localPhoneNumber ?_no . "			\
+	"} "								\
+"} "
+
+#define CALLS_LIST(CONSTRAINT)						\
+"SELECT ?_call nco:nameFamily(?_contact) "				\
+	"nco:nameGiven(?_contact) nco:nameAdditional(?_contact) "	\
+	"nco:nameHonorificPrefix(?_contact) "				\
+	"nco:nameHonorificSuffix(?_contact) "				\
+	"(SELECT "							\
+		"?nick "						\
+		"WHERE { "						\
+			"{ "						\
+				"?_contact nco:nickname ?nick "		\
+			"} UNION { "					\
+				"?_contact nco:hasAffiliation ?role . "	\
+				"?role nco:hasIMAddress ?im . "		\
+				"?im nco:imNickname ?nick "		\
+			"} "						\
+		"} "							\
+	") "								\
+	"nco:phoneNumber(?_cpn) "					\
+CALLS_CONSTRAINTS(CONSTRAINT)						\
+"ORDER BY DESC(nmo:sentDate(?_call)) "
+
+#define CALLS_QUERY(CONSTRAINT)						\
+"SELECT "								\
+"(SELECT fn:concat(rdf:type(?role_number),"				\
+	"\"\31\", nco:phoneNumber(?role_number))"			\
+	"WHERE {"							\
+	"{"								\
+	"	?_role nco:hasPhoneNumber ?role_number "		\
+	"	FILTER (?role_number = ?_number)"			\
+	"} UNION { "							\
+		"?_unb_contact nco:hasPhoneNumber ?role_number . "	\
+	"	FILTER (!bound(?_role)) "				\
+	"}"								\
+"} GROUP BY nco:phoneNumber(?role_number) ) "				\
+	"nco:fullname(?_contact) "					\
+	"nco:nameFamily(?_contact) "					\
+	"nco:nameGiven(?_contact) "					\
+	"nco:nameAdditional(?_contact) "				\
+	"nco:nameHonorificPrefix(?_contact) "				\
+	"nco:nameHonorificSuffix(?_contact) "				\
+"(SELECT GROUP_CONCAT(fn:concat("					\
+	"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\","	\
+	"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","\
+	"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","\
+	"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\","	\
+	"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\","	\
+	"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\","	\
+	"tracker:coalesce(nco:country(?aff_addr), \"\"), "		\
+	"\"\31\", rdfs:label(?c_role) ), "				\
+	"\"\30\") "							\
+	"WHERE {"							\
+	"?_contact nco:hasAffiliation ?c_role . "			\
+	"?c_role nco:hasPostalAddress ?aff_addr"			\
+	"}) "								\
+	"nco:birthDate(?_contact) "					\
+"(SELECT "								\
+	"?nick "							\
+	"WHERE { "							\
+	"	{ "							\
+	"	?_contact nco:nickname ?nick "				\
+	"		} UNION { "					\
+	"			?_contact nco:hasAffiliation ?role . "	\
+	"			?role nco:hasIMAddress ?im . "		\
+	"			?im nco:imNickname ?nick "		\
+	"		} "						\
+	"	} "							\
+	") "								\
+"(SELECT GROUP_CONCAT(fn:concat(?url_value, \"\31\", "			\
+	"tracker:coalesce(rdfs:label(?c_role), \"\")), \"\30\") "	\
+	"WHERE {"							\
+		"?_contact nco:hasAffiliation ?c_role . "		\
+		"?c_role nco:url ?url_value . "				\
+"})"									\
+	"nie:url(nco:photo(?_contact)) "				\
+	"nco:role(?_role) "						\
+	"nco:contactUID(?_contact) "					\
+	"nco:title(?_role) "						\
+	"rdfs:label(?_role) "						\
+	"nco:fullname(nco:org(?_role)) "				\
+	"nco:department(?_role) "					\
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\","			\
+	"tracker:coalesce(rdfs:label(?c_role), \"\")),"			\
+	"\"\30\") "							\
+	"WHERE { "							\
+	"?_contact nco:hasAffiliation ?c_role . "			\
+	"?c_role nco:hasEmailAddress "					\
+	"		[ nco:emailAddress ?emailaddress ] "		\
+	"}) "								\
+	"nmo:receivedDate(?_call) "					\
+	"nmo:isSent(?_call) "						\
+	"nmo:isAnswered(?_call) "					\
+	"?_call "							\
+CALLS_CONSTRAINTS(CONSTRAINT)						\
+"ORDER BY DESC(nmo:sentDate(?_call)) "
+
+#define MISSED_CONSTRAINT		\
+"?_call nmo:from ?_unb_contact . "	\
+"?_call nmo:isSent false . "		\
+"?_call nmo:isAnswered false . "
+
+#define INCOMING_CONSTRAINT		\
+"?_call nmo:from ?_unb_contact . "	\
+"?_call nmo:isSent false . "		\
+"?_call nmo:isAnswered true . "
+
+#define OUTGOING_CONSTRAINT		\
+"?_call nmo:to ?_unb_contact . "	\
+"?_call nmo:isSent true . "
+
+#define COMBINED_CONSTRAINT			\
+"{ "						\
+"	?_call nmo:from ?_unb_contact .  "	\
+"	?_call nmo:isSent false "		\
+"} UNION { "					\
+"	?_call nmo:to ?_unb_contact . "		\
+"	?_call nmo:isSent true "		\
+"} "
+
+#define CALL_URI_CONSTRAINT	\
+COMBINED_CONSTRAINT		\
+"FILTER (?_call = <%s>) "
+
+#define MISSED_CALLS_QUERY CALLS_QUERY(MISSED_CONSTRAINT)
+#define MISSED_CALLS_LIST CALLS_LIST(MISSED_CONSTRAINT)
+#define INCOMING_CALLS_QUERY CALLS_QUERY(INCOMING_CONSTRAINT)
+#define INCOMING_CALLS_LIST CALLS_LIST(INCOMING_CONSTRAINT)
+#define OUTGOING_CALLS_QUERY CALLS_QUERY(OUTGOING_CONSTRAINT)
+#define OUTGOING_CALLS_LIST CALLS_LIST(OUTGOING_CONSTRAINT)
+#define COMBINED_CALLS_QUERY CALLS_QUERY(COMBINED_CONSTRAINT)
+#define COMBINED_CALLS_LIST CALLS_LIST(COMBINED_CONSTRAINT)
+#define CONTACT_FROM_CALL_QUERY CALLS_QUERY(CALL_URI_CONSTRAINT)
+
+#define CONTACTS_QUERY_FROM_URI						\
+"SELECT "								\
+"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number),"			\
+"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")"			\
+"WHERE {"								\
+"	?_role nco:hasPhoneNumber ?aff_number"				\
+"}) "									\
+"nco:fullname(<%s>) "							\
+"nco:nameFamily(<%s>) "							\
+"nco:nameGiven(<%s>) "							\
+"nco:nameAdditional(<%s>) "						\
+"nco:nameHonorificPrefix(<%s>) "					\
+"nco:nameHonorificSuffix(<%s>) "					\
+"(SELECT GROUP_CONCAT(fn:concat("					\
+"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","	\
+"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","	\
+"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\","		\
+"tracker:coalesce(nco:country(?aff_addr), \"\"), "			\
+"\"\31\", rdfs:label(?_role) ), "					\
+"\"\30\") "								\
+"WHERE {"								\
+"?_role nco:hasPostalAddress ?aff_addr"					\
+"}) "									\
+"nco:birthDate(<%s>) "							\
+"(SELECT "								\
+"	?nick "								\
+"	WHERE { "							\
+"		{ "							\
+"			?_contact nco:nickname ?nick "			\
+"		} UNION { "						\
+"			?_contact nco:hasAffiliation ?role . "		\
+"			?role nco:hasIMAddress ?im . "			\
+"			?im nco:imNickname ?nick "			\
+"		} "							\
+"		FILTER (?_contact = <%s>)"				\
+"	} "								\
+") "									\
+"(SELECT GROUP_CONCAT(fn:concat( "					\
+	"?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\
+	"), \"\30\") "							\
+	"WHERE {"							\
+		"?_role nco:url ?url_val . "				\
+"})"									\
+"nie:url(nco:photo(<%s>)) "						\
+"nco:role(?_role) "							\
+"nco:contactUID(<%s>) "							\
+"nco:title(?_role) "							\
+"rdfs:label(?_role) "							\
+"nco:fullname(nco:org(?_role))"						\
+"nco:department(?_role) "						\
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\","			\
+	"tracker:coalesce(rdfs:label(?_role), \"\")),"			\
+	"\"\30\") "							\
+	"WHERE { "							\
+	"?_role nco:hasEmailAddress "					\
+	"		[ nco:emailAddress ?emailaddress ] "		\
+	"}) "								\
+"\"NOTACALL\" \"false\" \"false\" "					\
+"<%s> "									\
+"WHERE {"								\
+"	<%s> a nco:PersonContact ."					\
+"	OPTIONAL {<%s> nco:hasAffiliation ?_role .}"			\
+"}"
+
+#define CONTACTS_OTHER_QUERY_FROM_URI					\
+	"SELECT fn:concat(\"TYPE_OTHER\", \"\31\", nco:phoneNumber(?t))"\
+	"\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" "			\
+	"\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" "		\
+	" \"NOTACALL\" \"false\" \"false\" <%s> "			\
+	"WHERE { "							\
+		"<%s> a nco:Contact . "					\
+		"OPTIONAL { <%s> nco:hasPhoneNumber ?t . } "		\
+	"} "
+
+#define CONTACTS_COUNT_QUERY						\
+	"SELECT COUNT(?c) "						\
+	"WHERE {"							\
+		"?c a nco:PersonContact ."				\
+	"}"
+
+#define MISSED_CALLS_COUNT_QUERY					\
+	"SELECT COUNT(?call) WHERE {"					\
+		"?c a nco:Contact ;"					\
+		"nco:hasPhoneNumber ?h ."				\
+		"?call a nmo:Call ;"					\
+		"nmo:isSent false ;"					\
+		"nmo:from ?c ;"						\
+		"nmo:isAnswered false ."				\
+	"}"
+
+#define INCOMING_CALLS_COUNT_QUERY					\
+	"SELECT COUNT(?call) WHERE {"					\
+		"?c a nco:Contact ;"					\
+		"nco:hasPhoneNumber ?h ."				\
+		"?call a nmo:Call ;"					\
+		"nmo:isSent false ;"					\
+		"nmo:from ?c ;"						\
+		"nmo:isAnswered true ."					\
+	"}"
+
+#define OUTGOING_CALLS_COUNT_QUERY					\
+	"SELECT COUNT(?call) WHERE {"					\
+		"?c a nco:Contact ;"					\
+		"nco:hasPhoneNumber ?h ."				\
+		"?call a nmo:Call ;"					\
+		"nmo:isSent true ;"					\
+		"nmo:to ?c ."						\
+	"}"
+
+#define COMBINED_CALLS_COUNT_QUERY					\
+	"SELECT COUNT(?call) WHERE {"					\
+	"{"								\
+		"?c a nco:Contact ;"					\
+		"nco:hasPhoneNumber ?h ."				\
+		"?call a nmo:Call ;"					\
+		"nmo:isSent true ;"					\
+		"nmo:to ?c ."						\
+	"}UNION {"							\
+		"?c a nco:Contact ;"					\
+		"nco:hasPhoneNumber ?h ."				\
+		"?call a nmo:Call ;"					\
+		"nmo:from ?c ."						\
+	"}"								\
+	"}"
+
+#define NEW_MISSED_CALLS_COUNT_QUERY					\
+	"SELECT COUNT(?call) WHERE {"					\
+		"?c a nco:Contact ;"					\
+		"nco:hasPhoneNumber ?h ."				\
+		"?call a nmo:Call ;"					\
+		"nmo:isSent false ;"					\
+		"nmo:from ?c ;"						\
+		"nmo:isAnswered false ;"				\
+		"nmo:isRead false ."					\
+	"}"
+
+typedef int (*reply_list_foreach_t) (const char **reply, int num_fields,
+							void *user_data);
+
+typedef void (*add_field_t) (struct phonebook_contact *contact,
+						const char *value, int type);
+
+struct pending_reply {
+	reply_list_foreach_t callback;
+	void *user_data;
+	int num_fields;
+};
+
+struct contact_data {
+	char *id;
+	struct phonebook_contact *contact;
+};
+
+struct phonebook_data {
+	phonebook_cb cb;
+	void *user_data;
+	int index;
+	gboolean vcardentry;
+	const struct apparam_field *params;
+	GSList *contacts;
+	phonebook_cache_ready_cb ready_cb;
+	phonebook_entry_cb entry_cb;
+	int newmissedcalls;
+	GCancellable *query_canc;
+	char *req_name;
+	int vcard_part_count;
+	int tracker_index;
+};
+
+struct phonebook_index {
+	GArray *phonebook;
+	int index;
+};
+
+static TrackerSparqlConnection *connection = NULL;
+
+static const char *name2query(const char *name)
+{
+	if (g_str_equal(name, PB_CONTACTS))
+		return CONTACTS_QUERY_ALL;
+	else if (g_str_equal(name, PB_CALLS_INCOMING))
+		return INCOMING_CALLS_QUERY;
+	else if (g_str_equal(name, PB_CALLS_OUTGOING))
+		return OUTGOING_CALLS_QUERY;
+	else if (g_str_equal(name, PB_CALLS_MISSED))
+		return MISSED_CALLS_QUERY;
+	else if (g_str_equal(name, PB_CALLS_COMBINED))
+		return COMBINED_CALLS_QUERY;
+
+	return NULL;
+}
+
+static const char *name2count_query(const char *name)
+{
+	if (g_str_equal(name, PB_CONTACTS))
+		return CONTACTS_COUNT_QUERY;
+	else if (g_str_equal(name, PB_CALLS_INCOMING))
+		return INCOMING_CALLS_COUNT_QUERY;
+	else if (g_str_equal(name, PB_CALLS_OUTGOING))
+		return OUTGOING_CALLS_COUNT_QUERY;
+	else if (g_str_equal(name, PB_CALLS_MISSED))
+		return MISSED_CALLS_COUNT_QUERY;
+	else if (g_str_equal(name, PB_CALLS_COMBINED))
+		return COMBINED_CALLS_COUNT_QUERY;
+
+	return NULL;
+}
+
+static gboolean folder_is_valid(const char *folder)
+{
+	if (folder == NULL)
+		return FALSE;
+
+	if (g_str_equal(folder, "/"))
+		return TRUE;
+	else if (g_str_equal(folder, PB_TELECOM_FOLDER))
+		return TRUE;
+	else if (g_str_equal(folder, PB_CONTACTS_FOLDER))
+		return TRUE;
+	else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER))
+		return TRUE;
+	else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER))
+		return TRUE;
+	else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER))
+		return TRUE;
+	else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER))
+		return TRUE;
+
+	return FALSE;
+}
+
+static const char *folder2query(const char *folder)
+{
+	if (g_str_equal(folder, PB_CONTACTS_FOLDER))
+		return CONTACTS_QUERY_ALL_LIST;
+	else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER))
+		return INCOMING_CALLS_LIST;
+	else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER))
+		return OUTGOING_CALLS_LIST;
+	else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER))
+		return MISSED_CALLS_LIST;
+	else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER))
+		return COMBINED_CALLS_LIST;
+
+	return NULL;
+}
+
+static const char **string_array_from_cursor(TrackerSparqlCursor *cursor,
+								int array_len)
+{
+	const char **result;
+	int i;
+
+	result = g_new0(const char *, array_len);
+
+	for (i = 0; i < array_len; ++i) {
+		TrackerSparqlValueType type;
+
+		type = tracker_sparql_cursor_get_value_type(cursor, i);
+
+		if (type == TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE ||
+				type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND)
+			/* For null/unbound type filling result part with ""*/
+			result[i] = "";
+		else
+			/* Filling with string representation of content*/
+			result[i] = tracker_sparql_cursor_get_string(cursor, i,
+									NULL);
+	}
+
+	return result;
+}
+
+static void update_cancellable(struct phonebook_data *pdata,
+							GCancellable *canc)
+{
+	if (pdata->query_canc)
+		g_object_unref(pdata->query_canc);
+
+	pdata->query_canc = canc;
+}
+
+static void async_query_cursor_next_cb(GObject *source, GAsyncResult *result,
+							gpointer user_data)
+{
+	struct pending_reply *pending = user_data;
+	TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR(source);
+	GCancellable *cancellable;
+	GError *error = NULL;
+	gboolean success;
+	const char **node;
+	int err;
+
+	success = tracker_sparql_cursor_next_finish(
+						TRACKER_SPARQL_CURSOR(source),
+						result, &error);
+
+	if (!success) {
+		if (error) {
+			DBG("cursor_next error: %s", error->message);
+			g_error_free(error);
+		} else
+			/* When tracker_sparql_cursor_next_finish ends with
+			 * failure and no error is set, that means end of
+			 * results returned by query */
+			pending->callback(NULL, 0, pending->user_data);
+
+		goto failed;
+	}
+
+	node = string_array_from_cursor(cursor, pending->num_fields);
+	err = pending->callback(node, pending->num_fields, pending->user_data);
+	g_free(node);
+
+	/* Fetch next result only if processing current chunk ended with
+	 * success. Sometimes during processing data, we are able to determine
+	 * if there is no need to get more data from tracker - by example
+	 * stored amount of data parts is big enough for sending and we might
+	 * want to suspend processing or just some error occurred. */
+	if (!err) {
+		cancellable = g_cancellable_new();
+		update_cancellable(pending->user_data, cancellable);
+		tracker_sparql_cursor_next_async(cursor, cancellable,
+						async_query_cursor_next_cb,
+						pending);
+		return;
+	}
+
+failed:
+	g_object_unref(cursor);
+	g_free(pending);
+}
+
+static int query_tracker(const char *query, int num_fields,
+				reply_list_foreach_t callback, void *user_data)
+{
+	struct pending_reply *pending;
+	GCancellable *cancellable;
+	TrackerSparqlCursor *cursor;
+	GError *error = NULL;
+
+	DBG("");
+
+	if (connection == NULL)
+		connection = tracker_sparql_connection_get_direct(
+								NULL, &error);
+
+	if (!connection) {
+		if (error) {
+			DBG("direct-connection error: %s", error->message);
+			g_error_free(error);
+		}
+
+		return -EINTR;
+	}
+
+	cancellable = g_cancellable_new();
+	update_cancellable(user_data, cancellable);
+	cursor = tracker_sparql_connection_query(connection, query,
+							cancellable, &error);
+
+	if (cursor == NULL) {
+		if (error) {
+			DBG("connection_query error: %s", error->message);
+			g_error_free(error);
+		}
+
+		g_object_unref(cancellable);
+
+		return -EINTR;
+	}
+
+	pending = g_new0(struct pending_reply, 1);
+	pending->callback = callback;
+	pending->user_data = user_data;
+	pending->num_fields = num_fields;
+
+	/* Now asynchronously going through each row of results - callback
+	 * async_query_cursor_next_cb will be called ALWAYS, even if async
+	 * request was canceled */
+	tracker_sparql_cursor_next_async(cursor, cancellable,
+						async_query_cursor_next_cb,
+						pending);
+
+	return 0;
+}
+
+static char *iso8601_utc_to_localtime(const char *datetime)
+{
+	time_t time;
+	struct tm tm, *local;
+	char localdate[32];
+	int nr;
+
+	memset(&tm, 0, sizeof(tm));
+
+	nr = sscanf(datetime, "%04u-%02u-%02uT%02u:%02u:%02u",
+			&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+			&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+	if (nr < 6) {
+		/* Invalid time format */
+		error("sscanf(): %s (%d)", strerror(errno), errno);
+		return g_strdup("");
+	}
+
+	/* Time already in localtime */
+	if (!g_str_has_suffix(datetime, "Z")) {
+		strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", &tm);
+		return g_strdup(localdate);
+	}
+
+	tm.tm_year -= 1900;	/* Year since 1900 */
+	tm.tm_mon--;		/* Months since January, values 0-11 */
+
+	time = mktime(&tm);
+	time -= timezone;
+
+	local = localtime(&time);
+
+	strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", local);
+
+	return g_strdup(localdate);
+}
+
+static void set_call_type(struct phonebook_contact *contact,
+				const char *datetime, const char *is_sent,
+				const char *is_answered)
+{
+	gboolean sent, answered;
+
+	if (g_strcmp0(datetime, "NOTACALL") == 0) {
+		contact->calltype = CALL_TYPE_NOT_A_CALL;
+		return;
+	}
+
+	sent = g_str_equal(is_sent, "true");
+	answered = g_str_equal(is_answered, "true");
+
+	if (sent == FALSE) {
+		if (answered == FALSE)
+			contact->calltype = CALL_TYPE_MISSED;
+		else
+			contact->calltype = CALL_TYPE_INCOMING;
+	} else
+		contact->calltype = CALL_TYPE_OUTGOING;
+
+	/* Tracker gives time in the ISO 8601 format, UTC time */
+	contact->datetime = iso8601_utc_to_localtime(datetime);
+}
+
+static gboolean contact_matches(struct contact_data *c_data, const char *id,
+							const char *datetime)
+{
+	char *localtime;
+	int cmp_ret;
+
+	if (g_strcmp0(c_data->id, id) != 0)
+		return FALSE;
+
+	/* id is equal and not call history entry => contact matches */
+	if (c_data->contact->calltype == CALL_TYPE_NOT_A_CALL)
+		return TRUE;
+
+	/* for call history entries have to compare also timestamps of calls */
+	localtime = iso8601_utc_to_localtime(datetime);
+	cmp_ret = g_strcmp0(c_data->contact->datetime, localtime);
+	g_free(localtime);
+
+	return (cmp_ret == 0) ? TRUE : FALSE;
+}
+
+static struct phonebook_contact *find_contact(GSList *contacts, const char *id,
+							const char *datetime)
+{
+	GSList *l;
+
+	for (l = contacts; l; l = l->next) {
+		struct contact_data *c_data = l->data;
+
+		if (contact_matches(c_data, id, datetime))
+			return c_data->contact;
+	}
+
+	return NULL;
+}
+
+static struct phonebook_field *find_field(GSList *fields, const char *value,
+								int type)
+{
+	GSList *l;
+
+	for (l = fields; l; l = l->next) {
+		struct phonebook_field *field = l->data;
+		/* Returning phonebook number if phone values and type values
+		 * are equal */
+		if (g_strcmp0(field->text, value) == 0 && field->type == type)
+			return field;
+	}
+
+	return NULL;
+}
+
+static void add_phone_number(struct phonebook_contact *contact,
+						const char *phone, int type)
+{
+	struct phonebook_field *number;
+
+	if (phone == NULL || strlen(phone) == 0)
+		return;
+
+	/* Not adding number if there is already added with the same value */
+	if (find_field(contact->numbers, phone, type))
+		return;
+
+	number = g_new0(struct phonebook_field, 1);
+	number->text = g_strdup(phone);
+	number->type = type;
+
+	contact->numbers = g_slist_append(contact->numbers, number);
+}
+
+static void add_email(struct phonebook_contact *contact, const char *address,
+								int type)
+{
+	struct phonebook_field *email;
+
+	if (address == NULL || strlen(address) == 0)
+		return;
+
+	/* Not adding email if there is already added with the same value */
+	if (find_field(contact->emails, address, type))
+		return;
+
+	email = g_new0(struct phonebook_field, 1);
+	email->text = g_strdup(address);
+	email->type = type;
+
+	contact->emails = g_slist_append(contact->emails, email);
+}
+
+static gboolean addr_matches(struct phonebook_addr *a, struct phonebook_addr *b)
+{
+	GSList *la, *lb;
+
+	if (a->type != b->type)
+		return FALSE;
+
+	for (la = a->fields, lb = b->fields; la && lb;
+						la = la->next, lb = lb->next) {
+		char *field_a = la->data;
+		char *field_b = lb->data;
+
+		if (g_strcmp0(field_a, field_b) != 0)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+/* generates phonebook_addr struct from tracker address data string. */
+static struct phonebook_addr *gen_addr(const char *address, int type)
+{
+	struct phonebook_addr *addr;
+	GSList *fields = NULL;
+	char **addr_parts;
+	int i;
+
+	/* This test handles cases when address points to empty string
+	 * (or address is NULL pointer) or string containing only six
+	 * separators. It indicates that none of address fields is present
+	 * and there is no sense to create dummy phonebook_addr struct */
+	if (address == NULL || strlen(address) < ADDR_FIELD_AMOUNT)
+		return NULL;
+
+	addr_parts = g_strsplit(address, ADDR_DELIM, ADDR_FIELD_AMOUNT);
+
+	for (i = 0; i < ADDR_FIELD_AMOUNT; ++i)
+		fields = g_slist_append(fields, g_strdup(addr_parts[i]));
+
+	g_strfreev(addr_parts);
+
+	addr = g_new0(struct phonebook_addr, 1);
+	addr->fields = fields;
+	addr->type = type;
+
+	return addr;
+}
+
+static void add_address(struct phonebook_contact *contact,
+					const char *address, int type)
+{
+	struct phonebook_addr *addr;
+	GSList *l;
+
+	addr = gen_addr(address, type);
+	if (addr == NULL)
+		return;
+
+	/* Not adding address if there is already added with the same value.
+	 * These type of checks have to be done because sometimes tracker
+	 * returns results for contact data in more than 1 row - then the same
+	 * address may be returned more than once in query results */
+	for (l = contact->addresses; l; l = l->next) {
+		struct phonebook_addr *tmp = l->data;
+
+		if (addr_matches(tmp, addr)) {
+			phonebook_addr_free(addr);
+			return;
+		}
+	}
+
+	contact->addresses = g_slist_append(contact->addresses, addr);
+}
+
+static void add_url(struct phonebook_contact *contact, const char *url_val,
+								int type)
+{
+	struct phonebook_field *url;
+
+	if (url_val == NULL || strlen(url_val) == 0)
+		return;
+
+	/* Not adding url if there is already added with the same value */
+	if (find_field(contact->urls, url_val, type))
+		return;
+
+	url = g_new0(struct phonebook_field, 1);
+
+	url->text = g_strdup(url_val);
+	url->type = type;
+
+	contact->urls = g_slist_append(contact->urls, url);
+}
+
+static GString *gen_vcards(GSList *contacts,
+					const struct apparam_field *params)
+{
+	GSList *l;
+	GString *vcards;
+
+	vcards = g_string_new(NULL);
+
+	/* Generating VCARD string from contacts and freeing used contacts */
+	for (l = contacts; l; l = l->next) {
+		struct contact_data *c_data = l->data;
+		phonebook_add_contact(vcards, c_data->contact,
+					params->filter, params->format);
+	}
+
+	return vcards;
+}
+
+static int pull_contacts_size(const char **reply, int num_fields,
+							void *user_data)
+{
+	struct phonebook_data *data = user_data;
+
+	if (num_fields < 0) {
+		data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+		return -EINTR;
+	}
+
+	if (reply != NULL) {
+		data->index = atoi(reply[0]);
+		return 0;
+	}
+
+	data->cb(NULL, 0, data->index, data->newmissedcalls, TRUE,
+							data->user_data);
+
+	return 0;
+	/*
+	 * phonebook_data is freed in phonebook_req_finalize. Useful in
+	 * cases when call is terminated.
+	 */
+}
+
+static void add_affiliation(char **field, const char *value)
+{
+	if (strlen(*field) > 0 || value == NULL || strlen(value) == 0)
+		return;
+
+	g_free(*field);
+
+	*field = g_strdup(value);
+}
+
+static void contact_init(struct phonebook_contact *contact,
+							const char **reply)
+{
+	if (reply[COL_FAMILY_NAME][0] == '\0' &&
+			reply[COL_GIVEN_NAME][0] == '\0' &&
+			reply[COL_ADDITIONAL_NAME][0] == '\0' &&
+			reply[COL_NAME_PREFIX][0] == '\0' &&
+			reply[COL_NAME_SUFFIX][0] == '\0') {
+		if (reply[COL_FULL_NAME][0] != '\0')
+			contact->family = g_strdup(reply[COL_FULL_NAME]);
+		else
+			contact->family = g_strdup(reply[COL_NICKNAME]);
+	} else {
+		contact->family = g_strdup(reply[COL_FAMILY_NAME]);
+		contact->given = g_strdup(reply[COL_GIVEN_NAME]);
+		contact->additional = g_strdup(reply[COL_ADDITIONAL_NAME]);
+		contact->prefix = g_strdup(reply[COL_NAME_PREFIX]);
+		contact->suffix = g_strdup(reply[COL_NAME_SUFFIX]);
+	}
+	contact->fullname = g_strdup(reply[COL_FULL_NAME]);
+	contact->birthday = g_strdup(reply[COL_BIRTH_DATE]);
+	contact->nickname = g_strdup(reply[COL_NICKNAME]);
+	contact->photo = g_strdup(reply[COL_PHOTO]);
+	contact->company = g_strdup(reply[COL_ORG_NAME]);
+	contact->department = g_strdup(reply[COL_ORG_DEPARTMENT]);
+	contact->role = g_strdup(reply[COL_ORG_ROLE]);
+	contact->uid = g_strdup(reply[COL_UID]);
+	contact->title = g_strdup(reply[COL_TITLE]);
+
+	set_call_type(contact, reply[COL_DATE], reply[COL_SENT],
+							reply[COL_ANSWERED]);
+}
+
+static enum phonebook_number_type get_phone_type(const char *affilation)
+{
+	if (g_strcmp0(AFFILATION_HOME, affilation) == 0)
+		return TEL_TYPE_HOME;
+	else if (g_strcmp0(AFFILATION_WORK, affilation) == 0)
+		return TEL_TYPE_WORK;
+
+	return TEL_TYPE_OTHER;
+}
+
+static void add_aff_number(struct phonebook_contact *contact,
+				const char *pnumber, const char *aff_type)
+{
+	char **num_parts;
+	char *type, *number;
+
+	/* For phone taken directly from contacts data, phone number string
+	 * is represented as number type and number string - those strings are
+	 * separated by SUB_DELIM string */
+	num_parts = g_strsplit(pnumber, SUB_DELIM, 2);
+
+	if (!num_parts)
+		return;
+
+	if (num_parts[0])
+		type = num_parts[0];
+	else
+		goto failed;
+
+	if (num_parts[1])
+		number = num_parts[1];
+	else
+		goto failed;
+
+	if (g_strrstr(type, FAX_NUM_TYPE))
+		add_phone_number(contact, number, TEL_TYPE_FAX);
+	else if (g_strrstr(type, MOBILE_NUM_TYPE))
+		add_phone_number(contact, number, TEL_TYPE_MOBILE);
+	else
+		/* if this is no fax/mobile phone, then adding phone number
+		 * type based on type of the affilation field */
+		add_phone_number(contact, number, get_phone_type(aff_type));
+
+failed:
+	g_strfreev(num_parts);
+}
+
+static void contact_add_numbers(struct phonebook_contact *contact,
+							const char **reply)
+{
+	char **aff_numbers;
+	int i;
+
+	/* Filling phone numbers from contact's affilation */
+	aff_numbers = g_strsplit(reply[COL_PHONE_AFF], MAIN_DELIM, MAX_FIELDS);
+
+	if (aff_numbers)
+		for (i = 0; aff_numbers[i]; ++i)
+			add_aff_number(contact, aff_numbers[i],
+							reply[COL_AFF_TYPE]);
+
+	g_strfreev(aff_numbers);
+}
+
+static enum phonebook_field_type get_field_type(const char *affilation)
+{
+	if (g_strcmp0(AFFILATION_HOME, affilation) == 0)
+		return FIELD_TYPE_HOME;
+	else if (g_strcmp0(AFFILATION_WORK, affilation) == 0)
+		return FIELD_TYPE_WORK;
+
+	return FIELD_TYPE_OTHER;
+}
+
+static void add_aff_field(struct phonebook_contact *contact,
+			const char *aff_email, add_field_t add_field_cb)
+{
+	char **email_parts;
+	char *type, *email;
+
+	/* Emails from affilation data, are represented as real email
+	 * string and affilation type - those strings are separated by
+	 * SUB_DELIM string */
+	email_parts = g_strsplit(aff_email, SUB_DELIM, 2);
+
+	if (!email_parts)
+		return;
+
+	if (email_parts[0])
+		email = email_parts[0];
+	else
+		goto failed;
+
+	if (email_parts[1])
+		type = email_parts[1];
+	else
+		goto failed;
+
+	add_field_cb(contact, email, get_field_type(type));
+
+failed:
+	g_strfreev(email_parts);
+}
+
+static void contact_add_emails(struct phonebook_contact *contact,
+							const char **reply)
+{
+	char **aff_emails;
+	int i;
+
+	/* Emails from affilation */
+	aff_emails = g_strsplit(reply[COL_EMAIL_AFF], MAIN_DELIM, MAX_FIELDS);
+
+	if (aff_emails)
+		for (i = 0; aff_emails[i] != NULL; ++i)
+			add_aff_field(contact, aff_emails[i], add_email);
+
+	g_strfreev(aff_emails);
+}
+
+static void contact_add_addresses(struct phonebook_contact *contact,
+							const char **reply)
+{
+	char **aff_addr;
+	int i;
+
+	/* Addresses from affilation */
+	aff_addr = g_strsplit(reply[COL_ADDR_AFF], MAIN_DELIM, MAX_FIELDS);
+
+	if (aff_addr)
+		for (i = 0; aff_addr[i] != NULL; ++i)
+			add_aff_field(contact, aff_addr[i], add_address);
+
+	g_strfreev(aff_addr);
+}
+
+static void contact_add_urls(struct phonebook_contact *contact,
+							const char **reply)
+{
+	char **aff_url;
+	int i;
+
+	/* Addresses from affilation */
+	aff_url = g_strsplit(reply[COL_URL], MAIN_DELIM, MAX_FIELDS);
+
+	if (aff_url)
+		for (i = 0; aff_url[i] != NULL; ++i)
+			add_aff_field(contact, aff_url[i], add_url);
+
+	g_strfreev(aff_url);
+}
+
+static void contact_add_organization(struct phonebook_contact *contact,
+							const char **reply)
+{
+	/* Adding fields connected by nco:hasAffiliation - they may be in
+	 * separate replies */
+	add_affiliation(&contact->title, reply[COL_TITLE]);
+	add_affiliation(&contact->company, reply[COL_ORG_NAME]);
+	add_affiliation(&contact->department, reply[COL_ORG_DEPARTMENT]);
+	add_affiliation(&contact->role, reply[COL_ORG_ROLE]);
+}
+
+static void free_data_contacts(struct phonebook_data *data)
+{
+	GSList *l;
+
+	/* freeing contacts */
+	for (l = data->contacts; l; l = l->next) {
+		struct contact_data *c_data = l->data;
+
+		g_free(c_data->id);
+		phonebook_contact_free(c_data->contact);
+		g_free(c_data);
+	}
+
+	g_slist_free(data->contacts);
+	data->contacts = NULL;
+}
+
+static void send_pull_part(struct phonebook_data *data,
+			const struct apparam_field *params, gboolean lastpart)
+{
+	GString *vcards;
+
+	DBG("");
+	vcards = gen_vcards(data->contacts, params);
+	data->cb(vcards->str, vcards->len, g_slist_length(data->contacts),
+			data->newmissedcalls, lastpart, data->user_data);
+
+	if (!lastpart)
+		free_data_contacts(data);
+	g_string_free(vcards, TRUE);
+}
+
+static int pull_contacts(const char **reply, int num_fields, void *user_data)
+{
+	struct phonebook_data *data = user_data;
+	const struct apparam_field *params = data->params;
+	struct phonebook_contact *contact;
+	struct contact_data *contact_data;
+	int last_index, i;
+	gboolean cdata_present = FALSE, part_sent = FALSE;
+	static char *temp_id = NULL;
+
+	if (num_fields < 0) {
+		data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+		goto fail;
+	}
+
+	DBG("reply %p", reply);
+	data->tracker_index++;
+
+	if (reply == NULL)
+		goto done;
+
+	/* Trying to find contact in recently added contacts. It is needed for
+	 * contacts that have more than one telephone number filled */
+	contact = find_contact(data->contacts, reply[CONTACTS_ID_COL],
+							reply[COL_DATE]);
+
+	/* If contact is already created then adding only new phone numbers */
+	if (contact) {
+		cdata_present = TRUE;
+		goto add_numbers;
+	}
+
+	/* We are doing a PullvCardEntry, no need for those checks */
+	if (data->vcardentry)
+		goto add_entry;
+
+	/* Last four fields are always present, ignoring them */
+	for (i = 0; i < num_fields - 4; i++) {
+		if (reply[i][0] != '\0')
+			break;
+	}
+
+	if (i == num_fields - 4 && !g_str_equal(reply[CONTACTS_ID_COL],
+						TRACKER_DEFAULT_CONTACT_ME))
+		return 0;
+
+	if (g_strcmp0(temp_id, reply[CONTACTS_ID_COL])) {
+		data->index++;
+		g_free(temp_id);
+		temp_id = g_strdup(reply[CONTACTS_ID_COL]);
+
+		/* Incrementing counter for vcards in current part of data,
+		 * but only if liststartoffset has been already reached */
+		if (data->index > params->liststartoffset)
+			data->vcard_part_count++;
+	}
+
+	if (data->vcard_part_count > VCARDS_PART_COUNT) {
+		DBG("Part of vcard data ready for sending...");
+		data->vcard_part_count = 0;
+		/* Sending part of data to PBAP core - more data can be still
+		 * fetched, so marking lastpart as FALSE */
+		send_pull_part(data, params, FALSE);
+
+		/* Later, after adding contact data, need to return -EINTR to
+		 * stop fetching more data for this request. Data will be
+		 * downloaded again from this point, when phonebook_pull_read
+		 * will be called again with current request as a parameter*/
+		part_sent = TRUE;
+	}
+
+	last_index = params->liststartoffset + params->maxlistcount;
+
+	if (data->index <= params->liststartoffset)
+		return 0;
+
+	/* max number of results achieved - need send vcards data that was
+	 * already collected and stop further data processing (these operations
+	 * will be invoked in "done" section) */
+	if (data->index > last_index && params->maxlistcount > 0) {
+		DBG("Maxlistcount achieved");
+		goto done;
+	}
+
+add_entry:
+	contact = g_new0(struct phonebook_contact, 1);
+	contact_init(contact, reply);
+
+add_numbers:
+	contact_add_numbers(contact, reply);
+	contact_add_emails(contact, reply);
+	contact_add_addresses(contact, reply);
+	contact_add_urls(contact, reply);
+	contact_add_organization(contact, reply);
+
+	DBG("contact %p", contact);
+
+	/* Adding contacts data to wrapper struct - this data will be used to
+	 * generate vcard list */
+	if (!cdata_present) {
+		contact_data = g_new0(struct contact_data, 1);
+		contact_data->contact = contact;
+		contact_data->id = g_strdup(reply[CONTACTS_ID_COL]);
+		data->contacts = g_slist_append(data->contacts, contact_data);
+	}
+
+	if (part_sent)
+		return -EINTR;
+
+	return 0;
+
+done:
+	/* Processing is end, this is definitely last part of transmission
+	 * (marking lastpart as TRUE) */
+	send_pull_part(data, params, TRUE);
+
+fail:
+	g_free(temp_id);
+	temp_id = NULL;
+
+	return -EINTR;
+	/*
+	 * phonebook_data is freed in phonebook_req_finalize. Useful in
+	 * cases when call is terminated.
+	 */
+}
+
+static int add_to_cache(const char **reply, int num_fields, void *user_data)
+{
+	struct phonebook_data *data = user_data;
+	char *formatted;
+	int i;
+
+	if (reply == NULL || num_fields < 0)
+		goto done;
+
+	/* the first element is the URI, always not empty */
+	for (i = 1; i < num_fields; i++) {
+		if (reply[i][0] != '\0')
+			break;
+	}
+
+	if (i == num_fields &&
+			!g_str_equal(reply[0], TRACKER_DEFAULT_CONTACT_ME))
+		return 0;
+
+	if (i == 7)
+		formatted = g_strdup(reply[7]);
+	else if (i == 6)
+		formatted = g_strdup(reply[6]);
+	else
+		formatted = g_strdup_printf("%s;%s;%s;%s;%s",
+					reply[1], reply[2], reply[3], reply[4],
+					reply[5]);
+
+	/* The owner vCard must have the 0 handle */
+	if (strcmp(reply[0], TRACKER_DEFAULT_CONTACT_ME) == 0)
+		data->entry_cb(reply[0], 0, formatted, "",
+						reply[6], data->user_data);
+	else
+		data->entry_cb(reply[0], PHONEBOOK_INVALID_HANDLE, formatted,
+					"", reply[6], data->user_data);
+
+	g_free(formatted);
+
+	return 0;
+
+done:
+	if (num_fields <= 0)
+		data->ready_cb(data->user_data);
+
+	return -EINTR;
+	/*
+	 * phonebook_data is freed in phonebook_req_finalize. Useful in
+	 * cases when call is terminated.
+	 */
+}
+
+int phonebook_init(void)
+{
+	g_thread_init(NULL);
+	g_type_init();
+
+	return 0;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder, const char *new_folder,
+						uint8_t flags, int *err)
+{
+	char *tmp1, *tmp2, *base, *path = NULL;
+	gboolean root, child;
+	int ret = 0;
+	int len;
+
+	root = (g_strcmp0("/", current_folder) == 0);
+	child = (new_folder && strlen(new_folder) != 0);
+
+	switch (flags) {
+	case 0x02:
+		/* Go back to root */
+		if (!child) {
+			path = g_strdup("/");
+			goto done;
+		}
+
+		path = g_build_filename(current_folder, new_folder, NULL);
+		break;
+	case 0x03:
+		/* Go up 1 level */
+		if (root) {
+			/* Already root */
+			path = g_strdup("/");
+			goto done;
+		}
+
+		/*
+		 * Removing one level of the current folder. Current folder
+		 * contains AT LEAST one level since it is not at root folder.
+		 * Use glib utility functions to handle invalid chars in the
+		 * folder path properly.
+		 */
+		tmp1 = g_path_get_basename(current_folder);
+		tmp2 = g_strrstr(current_folder, tmp1);
+		len = tmp2 - (current_folder + 1);
+
+		g_free(tmp1);
+
+		if (len == 0)
+			base = g_strdup("/");
+		else
+			base = g_strndup(current_folder, len);
+
+		/* Return: one level only */
+		if (!child) {
+			path = base;
+			goto done;
+		}
+
+		path = g_build_filename(base, new_folder, NULL);
+		g_free(base);
+
+		break;
+	default:
+		ret = -EBADR;
+		break;
+	}
+
+done:
+	if (path && !folder_is_valid(path))
+		ret = -ENOENT;
+
+	if (ret < 0) {
+		g_free(path);
+		path = NULL;
+	}
+
+	if (err)
+		*err = ret;
+
+	return path;
+}
+
+static int pull_newmissedcalls(const char **reply, int num_fields,
+							void *user_data)
+{
+	struct phonebook_data *data = user_data;
+	reply_list_foreach_t pull_cb;
+	int col_amount, err;
+	const char *query;
+	int nmissed;
+
+	if (num_fields < 0) {
+		data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+
+		return -EINTR;
+	}
+
+	if (reply != NULL) {
+		nmissed = atoi(reply[0]);
+		data->newmissedcalls =
+			nmissed <= UINT8_MAX ? nmissed : UINT8_MAX;
+		DBG("newmissedcalls %d", data->newmissedcalls);
+
+		return 0;
+	}
+
+	if (data->params->maxlistcount == 0) {
+		query = name2count_query(PB_CALLS_MISSED);
+		col_amount = COUNT_QUERY_COL_AMOUNT;
+		pull_cb = pull_contacts_size;
+	} else {
+		query = name2query(PB_CALLS_MISSED);
+		col_amount = PULL_QUERY_COL_AMOUNT;
+		pull_cb = pull_contacts;
+	}
+
+	err = query_tracker(query, col_amount, pull_cb, data);
+	if (err < 0) {
+		data->cb(NULL, 0, err, 0, TRUE, data->user_data);
+
+		return -EINTR;
+	}
+
+	return 0;
+}
+
+void phonebook_req_finalize(void *request)
+{
+	struct phonebook_data *data = request;
+
+	DBG("");
+
+	if (!data)
+		return;
+
+	/* canceling asynchronous operation on tracker if any is active */
+	if (data->query_canc) {
+		g_cancellable_cancel(data->query_canc);
+		g_object_unref(data->query_canc);
+	}
+
+	free_data_contacts(data);
+	g_free(data->req_name);
+	g_free(data);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err)
+{
+	struct phonebook_data *data;
+
+	DBG("name %s", name);
+
+	data = g_new0(struct phonebook_data, 1);
+	data->params = params;
+	data->user_data = user_data;
+	data->cb = cb;
+	data->req_name = g_strdup(name);
+
+	if (err)
+		*err = 0;
+
+	return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+	struct phonebook_data *data = request;
+	reply_list_foreach_t pull_cb;
+	const char *query;
+	char *offset_query;
+	int col_amount;
+	int ret;
+
+	if (!data)
+		return -ENOENT;
+
+	data->newmissedcalls = 0;
+
+	if (g_strcmp0(data->req_name, PB_CALLS_MISSED) == 0 &&
+						data->tracker_index == 0) {
+		/* new missed calls amount should be counted only once - it
+		 * will be done during generating first part of results of
+		 * missed calls history */
+		query = NEW_MISSED_CALLS_COUNT_QUERY;
+		col_amount = COUNT_QUERY_COL_AMOUNT;
+		pull_cb = pull_newmissedcalls;
+	} else if (data->params->maxlistcount == 0) {
+		query = name2count_query(data->req_name);
+		col_amount = COUNT_QUERY_COL_AMOUNT;
+		pull_cb = pull_contacts_size;
+	} else {
+		query = name2query(data->req_name);
+		col_amount = PULL_QUERY_COL_AMOUNT;
+		pull_cb = pull_contacts;
+	}
+
+	if (query == NULL)
+		return -ENOENT;
+
+	if (pull_cb == pull_contacts && data->tracker_index > 0) {
+		/* Adding offset to pull query to download next parts of data
+		 * from tracker (phonebook_pull_read may be called many times
+		 * from PBAP core to fetch data partially) */
+		offset_query = g_strdup_printf(QUERY_OFFSET_FORMAT, query,
+							data->tracker_index);
+		ret = query_tracker(offset_query, col_amount, pull_cb, data);
+
+		g_free(offset_query);
+
+		return ret;
+	}
+
+	return query_tracker(query, col_amount, pull_cb, data);
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+				const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err)
+{
+	struct phonebook_data *data;
+	char *query;
+	int ret;
+
+	DBG("folder %s id %s", folder, id);
+
+	data = g_new0(struct phonebook_data, 1);
+	data->user_data = user_data;
+	data->params = params;
+	data->cb = cb;
+	data->vcardentry = TRUE;
+
+	if (g_str_has_prefix(id, CONTACT_ID_PREFIX) == TRUE ||
+				g_strcmp0(id, TRACKER_DEFAULT_CONTACT_ME) == 0)
+		query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id,
+					id, id, id, id, id, id, id, id, id);
+	else if (g_str_has_prefix(id, CALL_ID_PREFIX) == TRUE)
+		query = g_strdup_printf(CONTACT_FROM_CALL_QUERY, id);
+	else
+		query = g_strdup_printf(CONTACTS_OTHER_QUERY_FROM_URI,
+								id, id, id);
+
+	ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data);
+	if (err)
+		*err = ret;
+
+	g_free(query);
+
+	return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+	struct phonebook_data *data;
+	const char *query;
+	int ret;
+
+	DBG("name %s", name);
+
+	query = folder2query(name);
+	if (query == NULL) {
+		if (err)
+			*err = -ENOENT;
+		return NULL;
+	}
+
+	data = g_new0(struct phonebook_data, 1);
+	data->entry_cb = entry_cb;
+	data->ready_cb = ready_cb;
+	data->user_data = user_data;
+
+	ret = query_tracker(query, 8, add_to_cache, data);
+	if (err)
+		*err = ret;
+
+	return data;
+}
diff --git a/bluez/repo/obexd/plugins/phonebook.h b/bluez/repo/obexd/plugins/phonebook.h
new file mode 100644
index 0000000..70a9cb7
--- /dev/null
+++ b/bluez/repo/obexd/plugins/phonebook.h
@@ -0,0 +1,166 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+#define EOL	"\r\n"
+#define VCARD_LISTING_BEGIN \
+	"<?xml version=\"1.0\"?>" EOL\
+	"<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">" EOL\
+	"<vCard-listing version=\"1.0\">" EOL
+#define VCARD_LISTING_ELEMENT "<card handle = \"%d.vcf\" name = \"%s\"/>" EOL
+#define VCARD_LISTING_END "</vCard-listing>"
+
+#define PB_TELECOM_FOLDER "/telecom"
+#define PB_CONTACTS_FOLDER "/telecom/pb"
+#define PB_CALENDAR_FOLDER "/telecom/cal"
+#define PB_NOTES_FOLDER "/telecom/nt"
+#define PB_CALLS_COMBINED_FOLDER "/telecom/cch"
+#define PB_CALLS_INCOMING_FOLDER "/telecom/ich"
+#define PB_CALLS_MISSED_FOLDER "/telecom/mch"
+#define PB_CALLS_OUTGOING_FOLDER "/telecom/och"
+#define PB_CALLS_SPEEDDIAL_FOLDER "/telecom/spd"
+#define PB_CALLS_FAVORITE_FOLDER "/telecom/fav"
+#define PB_LUID_FOLDER "/telecom/pb/luid"
+
+#define PB_CONTACTS "/telecom/pb.vcf"
+#define PB_CALLS_COMBINED "/telecom/cch.vcf"
+#define PB_CALLS_INCOMING "/telecom/ich.vcf"
+#define PB_CALLS_MISSED "/telecom/mch.vcf"
+#define PB_CALLS_OUTGOING "/telecom/och.vcf"
+#define PB_CALLS_SPEEDDIAL "/telecom/spd.vcf"
+#define PB_CALLS_FAVORITE "/telecom/fav.vcf"
+#define PB_DEVINFO "/telecom/devinfo.txt"
+#define PB_INFO_LOG "/telecom/pb/info.log"
+#define PB_CC_LOG "/telecom/pb/luid/cc.log"
+
+
+struct apparam_field {
+	/* list and pull attributes */
+	uint16_t maxlistcount;
+	uint16_t liststartoffset;
+
+	/* pull and vcard attributes */
+	uint64_t filter;
+	uint8_t format;
+
+	/* list attributes only */
+	uint8_t order;
+	uint8_t searchattrib;
+	char *searchval;
+};
+
+/*
+ * Interface between the PBAP core and backends to retrieve
+ * all contacts that match the application parameters rules.
+ * Contacts will be returned in the vcard format.
+ */
+typedef void (*phonebook_cb) (const char *buffer, size_t bufsize,
+		int vcards, int missed, gboolean lastpart, void *user_data);
+
+/*
+ * Interface between the PBAP core and backends to
+ * append a new entry in the PBAP folder cache.
+ */
+#define PHONEBOOK_INVALID_HANDLE 0xffffffff
+typedef void (*phonebook_entry_cb) (const char *id, uint32_t handle,
+					const char *name, const char *sound,
+					const char *tel, void *user_data);
+
+/*
+ * After notify all entries to PBAP core, the backend
+ * needs to notify that the operation has finished.
+ */
+typedef void (*phonebook_cache_ready_cb) (void *user_data);
+
+
+int phonebook_init(void);
+void phonebook_exit(void);
+
+/*
+ * Changes the current folder in the phonebook back-end. The PBAP core
+ * doesn't validate or restrict the possible values for the folders,
+ * allowing non-standard backends implementation which doesn't follow
+ * the PBAP virtual folder architecture. Validate the folder's name
+ * is responsibility of the back-ends.
+*/
+char *phonebook_set_folder(const char *current_folder,
+		const char *new_folder, uint8_t flags, int *err);
+
+/*
+ * phonebook_pull should be used only to prepare pull request - prepared
+ * request data is returned by this function. Start of fetching data from
+ * back-end will be done only after calling phonebook_pull_read with this
+ * returned request given as a parameter.
+ *
+ * phonebook_req_finalize MUST always be used to free associated resources.
+ */
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err);
+
+/*
+ * phonebook_pull_read should be used to start getting results from back-end.
+ * The back-end can return data as one response or can return it many parts.
+ * After obtaining one part, PBAP core need to call phonebook_pull_read with
+ * the same request again to get more results from back-end.
+ * The back-end MUST return only the content based on the application
+ * parameters requested by the client.
+ *
+ * Returns error code or 0 in case of success
+ */
+int phonebook_pull_read(void *request);
+
+/*
+ * Function used to retrieve a contact from the backend. Only contacts
+ * found in the cache are requested to the back-ends. The back-end MUST
+ * return only the content based on the application parameters requested
+ * by the client.
+ *
+ * Return value is a pointer to asynchronous request to phonebook back-end.
+ * phonebook_req_finalize MUST always be used to free associated resources.
+ */
+void *phonebook_get_entry(const char *folder, const char *id,
+				const struct apparam_field *params,
+				phonebook_cb cb, void *user_data, int *err);
+
+/*
+ * PBAP core will keep the contacts cache per folder. SetPhoneBook or
+ * PullvCardListing can invalidate the cache if the current folder changes.
+ * Cache will store only the necessary information required to reply to
+ * PullvCardListing request and verify if a given contact belongs to the
+ * source.
+ *
+ * Return value is a pointer to asynchronous request to phonebook back-end.
+ * phonebook_req_finalize MUST always be used to free associated resources.
+ */
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+		phonebook_cache_ready_cb ready_cb, void *user_data, int *err);
+
+/*
+ * Finalizes request to phonebook back-end and deallocates associated
+ * resources. Operation is canceled if not completed. This function MUST
+ * always be used after any of phonebook_pull, phonebook_get_entry, and
+ * phonebook_create_cache invoked.
+ *
+ * request is a pointer to asynchronous operation returned by phonebook_pull,
+ * phonebook_get_entry, and phonebook_create_cache.
+ */
+void phonebook_req_finalize(void *request);
diff --git a/bluez/repo/obexd/plugins/syncevolution.c b/bluez/repo/obexd/plugins/syncevolution.c
new file mode 100644
index 0000000..854505a
--- /dev/null
+++ b/bluez/repo/obexd/plugins/syncevolution.c
@@ -0,0 +1,483 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Intel Corporation
+ *  Copyright (C) 2007-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 <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/obexd.h"
+#include "filesystem.h"
+
+#define SYNCML_TARGET_SIZE 11
+
+static const uint8_t SYNCML_TARGET[SYNCML_TARGET_SIZE] = {
+			0x53, 0x59, 0x4E, 0x43, 0x4D, 0x4C, 0x2D, 0x53,
+			0x59, 0x4E, 0x43 };
+
+#define SYNCEVOLUTION_CHANNEL  19
+
+#define SYNCEVOLUTION_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\
+<record>								\
+ <attribute id=\"0x0001\">						\
+    <sequence>								\
+      <uuid value=\"00000002-0000-1000-8000-0002ee000002\"/>		\
+    </sequence>							\
+ </attribute>								\
+									\
+ <attribute id=\"0x0004\">						\
+    <sequence>								\
+      <sequence>							\
+        <uuid value=\"0x0100\"/>					\
+      </sequence>							\
+      <sequence>							\
+        <uuid value=\"0x0003\"/>					\
+        <uint8 value=\"%u\" name=\"channel\"/>				\
+      </sequence>							\
+      <sequence>							\
+        <uuid value=\"0x0008\"/>					\
+      </sequence>							\
+    </sequence>							\
+ </attribute>								\
+									\
+ <attribute id=\"0x0100\">						\
+    <text value=\"%s\" name=\"name\"/>					\
+ </attribute>								\
+</record>"
+
+#define SYNCE_BUS_NAME	"org.syncevolution"
+#define SYNCE_PATH	"/org/syncevolution/Server"
+#define SYNCE_SERVER_INTERFACE	"org.syncevolution.Server"
+#define SYNCE_CONN_INTERFACE	"org.syncevolution.Connection"
+
+struct synce_context {
+	struct obex_session *os;
+	DBusConnection *dbus_conn;
+	char *conn_obj;
+	unsigned int reply_watch;
+	unsigned int abort_watch;
+	GString *buffer;
+	int lasterr;
+	char *id;
+};
+
+static void append_dict_entry(DBusMessageIter *dict, const char *key,
+							int type, void *val)
+{
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static gboolean reply_signal(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct synce_context *context = data;
+	const char *path = dbus_message_get_path(msg);
+	DBusMessageIter iter, array_iter;
+	char *value;
+	int length;
+
+	if (strcmp(context->conn_obj, path) != 0) {
+		obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+		context->lasterr = -EPERM;
+		return FALSE;
+	}
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_recurse(&iter, &array_iter);
+	dbus_message_iter_get_fixed_array(&array_iter, &value, &length);
+
+	context->buffer = g_string_new_len(value, length);
+	obex_object_set_io_flags(context, G_IO_IN, 0);
+	context->lasterr = 0;
+
+	return TRUE;
+}
+
+static gboolean abort_signal(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct synce_context *context = data;
+
+	obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+	context->lasterr = -EPERM;
+
+	return TRUE;
+}
+
+static void connect_cb(DBusPendingCall *call, void *user_data)
+{
+	struct synce_context *context = user_data;
+	DBusConnection *conn;
+	DBusMessage *reply;
+	DBusError err;
+	char *path;
+
+	conn = context->dbus_conn;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID) == FALSE) {
+		error("%s", err.message);
+		dbus_error_free(&err);
+		goto failed;
+	}
+
+	DBG("Got conn object %s from syncevolution", path);
+	context->conn_obj = g_strdup(path);
+
+	context->reply_watch = g_dbus_add_signal_watch(conn, NULL, path,
+						SYNCE_CONN_INTERFACE, "Reply",
+						reply_signal, context, NULL);
+
+	context->abort_watch = g_dbus_add_signal_watch(conn, NULL, path,
+						SYNCE_CONN_INTERFACE, "Abort",
+						abort_signal, context, NULL);
+
+	dbus_message_unref(reply);
+
+	return;
+
+failed:
+	obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+	context->lasterr = -EPERM;
+}
+
+static void process_cb(DBusPendingCall *call, void *user_data)
+{
+	struct synce_context *context = user_data;
+	DBusMessage *reply;
+	DBusError derr;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_error_init(&derr);
+	if (dbus_set_error_from_message(&derr, reply)) {
+		error("process_cb(): syncevolution replied with an error:"
+					" %s, %s", derr.name, derr.message);
+		dbus_error_free(&derr);
+
+		obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+		context->lasterr = -EPERM;
+		goto done;
+	}
+
+	obex_object_set_io_flags(context, G_IO_OUT, 0);
+	context->lasterr = 0;
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void *synce_connect(struct obex_session *os, int *err)
+{
+	DBusConnection *conn;
+	struct synce_context *context;
+	char *address;
+
+	manager_register_session(os);
+
+	conn = manager_dbus_get_connection();
+	if (!conn)
+		goto failed;
+
+	context = g_new0(struct synce_context, 1);
+	context->dbus_conn = conn;
+	context->lasterr = -EAGAIN;
+	context->os = os;
+
+	if (obex_getpeername(os, &address) == 0) {
+		context->id = g_strdup_printf("%s+%d", address,
+							SYNCEVOLUTION_CHANNEL);
+		g_free(address);
+	}
+
+	if (err)
+		*err = 0;
+
+	return context;
+
+failed:
+	if (err)
+		*err = -EPERM;
+
+	return NULL;
+}
+
+static int synce_put(struct obex_session *os, void *user_data)
+{
+	return 0;
+}
+
+static int synce_get(struct obex_session *os, void *user_data)
+{
+	return obex_get_stream_start(os, NULL);
+}
+
+static void close_cb(DBusPendingCall *call, void *user_data)
+{
+	DBusMessage *reply;
+	DBusError derr;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_error_init(&derr);
+	if (dbus_set_error_from_message(&derr, reply)) {
+		error("close_cb(): syncevolution replied with an error:"
+					" %s, %s", derr.name, derr.message);
+		dbus_error_free(&derr);
+	}
+
+	dbus_message_unref(reply);
+}
+
+static void synce_disconnect(struct obex_session *os, void *user_data)
+{
+	struct synce_context *context = user_data;
+
+	g_free(context);
+}
+
+static void *synce_open(const char *name, int oflag, mode_t mode,
+				void *user_data, size_t *size, int *err)
+{
+	struct synce_context *context = user_data;
+
+	if (err)
+		*err = context ? 0 : -EFAULT;
+
+	return user_data;
+}
+
+static int synce_close(void *object)
+{
+	struct synce_context *context = object;
+	DBusMessage *msg;
+	const char *error;
+	gboolean normal;
+	DBusPendingCall *call;
+
+	if (!context->conn_obj)
+		goto done;
+
+	msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
+						SYNCE_CONN_INTERFACE, "Close");
+	if (!msg)
+		goto failed;
+
+	normal = TRUE;
+	error = "none";
+	dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &normal,
+				DBUS_TYPE_STRING, &error, DBUS_TYPE_INVALID);
+
+	g_dbus_send_message_with_reply(context->dbus_conn, msg, &call, -1);
+	dbus_pending_call_set_notify(call, close_cb, NULL, NULL);
+	dbus_message_unref(msg);
+	dbus_pending_call_unref(call);
+
+failed:
+	g_dbus_remove_watch(context->dbus_conn, context->reply_watch);
+	context->reply_watch = 0;
+	g_dbus_remove_watch(context->dbus_conn, context->abort_watch);
+	context->abort_watch = 0;
+
+	g_free(context->conn_obj);
+	context->conn_obj = NULL;
+
+done:
+	dbus_connection_unref(context->dbus_conn);
+	g_free(context);
+	return 0;
+}
+
+static ssize_t synce_read(void *object, void *buf, size_t count)
+{
+	struct synce_context *context = object;
+	DBusConnection *conn;
+	char transport[36], transport_description[24];
+	const char *session;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict;
+	gboolean authenticate;
+	DBusPendingCall *call;
+
+	if (context->buffer)
+		return string_read(context->buffer, buf, count);
+
+	conn = manager_dbus_get_connection();
+	if (conn == NULL)
+		return -EPERM;
+
+	msg = dbus_message_new_method_call(SYNCE_BUS_NAME, SYNCE_PATH,
+				SYNCE_SERVER_INTERFACE, "Connect");
+	if (!msg)
+		return -EPERM;
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+		DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+		DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+		DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	append_dict_entry(&dict, "id", DBUS_TYPE_STRING, context->id);
+
+	snprintf(transport, sizeof(transport), "%s.obexd", OBEXD_SERVICE);
+	append_dict_entry(&dict, "transport", DBUS_TYPE_STRING, transport);
+
+	snprintf(transport_description, sizeof(transport_description),
+						"version %s", VERSION);
+	append_dict_entry(&dict, "transport_description", DBUS_TYPE_STRING,
+							transport_description);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	authenticate = FALSE;
+	session = "";
+	dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &authenticate,
+			DBUS_TYPE_STRING, &session, DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
+		error("D-Bus call to %s failed.", SYNCE_SERVER_INTERFACE);
+		dbus_message_unref(msg);
+		return -EPERM;
+	}
+
+	dbus_pending_call_set_notify(call, connect_cb, context, NULL);
+
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return -EAGAIN;
+}
+
+static ssize_t synce_write(void *object, const void *buf, size_t count)
+{
+	struct synce_context *context = object;
+	DBusMessage *msg;
+	DBusMessageIter iter, array_iter;
+	DBusPendingCall *call;
+	const char *type = obex_get_type(context->os);
+
+	if (context->lasterr == 0)
+		return count;
+
+	if (!context->conn_obj)
+		return -EFAULT;
+
+	msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
+					SYNCE_CONN_INTERFACE, "Process");
+	if (!msg)
+		return -EFAULT;
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_BYTE_AS_STRING, &array_iter);
+
+	dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+						&buf, count);
+	dbus_message_iter_close_container(&iter, &array_iter);
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &type,
+						DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message_with_reply(context->dbus_conn, msg,
+								&call, -1)) {
+		error("D-Bus call to %s failed.", SYNCE_CONN_INTERFACE);
+		dbus_message_unref(msg);
+		return -EPERM;
+	}
+
+	dbus_pending_call_set_notify(call, process_cb, context, NULL);
+
+	dbus_message_unref(msg);
+	dbus_pending_call_unref(call);
+
+	return -EAGAIN;
+}
+
+static struct obex_mime_type_driver synce_driver = {
+	.target = SYNCML_TARGET,
+	.target_size = SYNCML_TARGET_SIZE,
+	.open = synce_open,
+	.close = synce_close,
+	.read = synce_read,
+	.write = synce_write,
+};
+
+static struct obex_service_driver synce = {
+	.name = "OBEX server for SyncML, using SyncEvolution",
+	.service = OBEX_SYNCEVOLUTION,
+	.channel = SYNCEVOLUTION_CHANNEL,
+	.secure = TRUE,
+	.record = SYNCEVOLUTION_RECORD,
+	.target = SYNCML_TARGET,
+	.target_size = SYNCML_TARGET_SIZE,
+	.get = synce_get,
+	.put = synce_put,
+	.connect = synce_connect,
+	.disconnect = synce_disconnect,
+};
+
+static int synce_init(void)
+{
+	int err;
+
+	err = obex_mime_type_driver_register(&synce_driver);
+	if (err < 0)
+		return err;
+
+	return obex_service_driver_register(&synce);
+}
+
+static void synce_exit(void)
+{
+	obex_service_driver_unregister(&synce);
+	obex_mime_type_driver_unregister(&synce_driver);
+}
+
+OBEX_PLUGIN_DEFINE(syncevolution, synce_init, synce_exit)
diff --git a/bluez/repo/obexd/plugins/vcard.c b/bluez/repo/obexd/plugins/vcard.c
new file mode 100644
index 0000000..37f7c85
--- /dev/null
+++ b/bluez/repo/obexd/plugins/vcard.c
@@ -0,0 +1,927 @@
+/*
+ * OBEX Server
+ *
+ * Copyright (C) 2008-2010 Intel Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "vcard.h"
+
+#define ADDR_FIELD_AMOUNT 7
+#define LEN_MAX 128
+#define TYPE_INTERNATIONAL 145
+
+#define PHONEBOOK_FLAG_CACHED 0x1
+
+#define FILTER_VERSION (1 << 0)
+#define FILTER_FN (1 << 1)
+#define FILTER_N (1 << 2)
+#define FILTER_PHOTO (1 << 3)
+#define FILTER_BDAY (1 << 4)
+#define FILTER_ADR (1 << 5)
+#define FILTER_LABEL (1 << 6)
+#define FILTER_TEL (1 << 7)
+#define FILTER_EMAIL (1 << 8)
+#define FILTER_MAILER (1 << 9)
+#define FILTER_TZ (1 << 10)
+#define FILTER_GEO (1 << 11)
+#define FILTER_TITLE (1 << 12)
+#define FILTER_ROLE (1 << 13)
+#define FILTER_LOGO (1 << 14)
+#define FILTER_AGENT (1 << 15)
+#define FILTER_ORG (1 << 16)
+#define FILTER_NOTE (1 << 17)
+#define FILTER_REV (1 << 18)
+#define FILTER_SOUND (1 << 19)
+#define FILTER_URL (1 << 20)
+#define FILTER_UID (1 << 21)
+#define FILTER_KEY (1 << 22)
+#define FILTER_NICKNAME (1 << 23)
+#define FILTER_CATEGORIES (1 << 24)
+#define FILTER_PROID (1 << 25)
+#define FILTER_CLASS (1 << 26)
+#define FILTER_SORT_STRING (1 << 27)
+#define FILTER_X_IRMC_CALL_DATETIME (1 << 28)
+
+#define FORMAT_VCARD21 0x00
+#define FORMAT_VCARD30 0x01
+
+#define QP_LINE_LEN 75
+#define QP_CHAR_LEN 3
+#define QP_CR 0x0D
+#define QP_LF 0x0A
+#define QP_ESC 0x5C
+#define QP_SOFT_LINE_BREAK "="
+#define QP_SELECT "\n!\"#$=@[\\]^`{|}~"
+#define ASCII_LIMIT 0x7F
+
+/* according to RFC 2425, the output string may need folding */
+static void vcard_printf(GString *str, const char *fmt, ...)
+{
+	char buf[1024];
+	va_list ap;
+	int len_temp, line_number, i;
+	unsigned int line_delimit = 75;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	line_number = strlen(buf) / line_delimit + 1;
+
+	for (i = 0; i < line_number; i++) {
+		len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
+		g_string_append_len(str,  buf + line_delimit * i, len_temp);
+		if (i != line_number - 1)
+			g_string_append(str, "\r\n ");
+	}
+
+	g_string_append(str, "\r\n");
+}
+
+/* According to RFC 2426, we need escape following characters:
+ *  '\n', '\r', ';', ',', '\'.
+ */
+static void add_slash(char *dest, const char *src, int len_max, int len)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
+		/* filling dest buffer - last field need to be reserved
+		 * for '\0'*/
+		switch (src[i]) {
+		case '\n':
+			if (j + 2 >= len_max)
+				/* not enough space in the buffer to put char
+				 * preceded with escaping sequence (and '\0' in
+				 * the end) */
+				goto done;
+
+			dest[j++] = '\\';
+			dest[j] = 'n';
+			break;
+		case '\r':
+			if (j + 2 >= len_max)
+				goto done;
+
+			dest[j++] = '\\';
+			dest[j] = 'r';
+			break;
+		case '\\':
+		case ';':
+		case ',':
+			if (j + 2 >= len_max)
+				goto done;
+
+			dest[j++] = '\\';
+		default:
+			dest[j] = src[i];
+			break;
+		}
+	}
+
+done:
+	dest[j] = 0;
+}
+
+static void escape_semicolon(char *dest, const char *src, int len_max, int len)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
+		if (src[i] == ';') {
+			if (j + 2 >= len_max)
+				break;
+
+			dest[j++] = '\\';
+		}
+
+		dest[j] = src[i];
+	}
+
+	dest[j] = 0;
+}
+
+static void set_escape(uint8_t format, char *dest, const char *src,
+							int len_max, int len)
+{
+	if (format == FORMAT_VCARD30)
+		add_slash(dest, src, len_max, len);
+	else if (format == FORMAT_VCARD21)
+		escape_semicolon(dest, src, len_max, len);
+}
+
+static void get_escaped_fields(uint8_t format, char **fields, ...)
+{
+	va_list ap;
+	GString *line;
+	char *field;
+	char escaped[LEN_MAX];
+
+	va_start(ap, fields);
+	line = g_string_new("");
+
+	for (field = va_arg(ap, char *); field; ) {
+		set_escape(format, escaped, field, LEN_MAX, strlen(field));
+		g_string_append(line, escaped);
+
+		field = va_arg(ap, char *);
+
+		if (field)
+			g_string_append(line, ";");
+	}
+
+	va_end(ap);
+
+	*fields = g_string_free(line, FALSE);
+}
+
+static gboolean set_qp_encoding(char c)
+{
+	unsigned char q = c;
+
+	if (strchr(QP_SELECT, q) != NULL)
+		return TRUE;
+
+	if (q < '!' || q > '~')
+		return TRUE;
+
+	return FALSE;
+}
+
+static void append_qp_break_line(GString *vcards, size_t *limit)
+{
+	/* Quoted Printable lines of text must be limited to less than 76
+	 * characters and terminated by Quoted Printable softline break
+	 * sequence of "=" (if some more characters left) */
+	g_string_append(vcards, QP_SOFT_LINE_BREAK);
+	g_string_append(vcards, "\r\n ");
+	*limit = QP_LINE_LEN - 1;
+}
+
+static void append_qp_ascii(GString *vcards, size_t *limit, char c)
+{
+	if (*limit == 0)
+		append_qp_break_line(vcards, limit);
+
+	g_string_append_c(vcards, c);
+	--*limit;
+}
+
+static void append_qp_hex(GString *vcards, size_t *limit, char c)
+{
+	if (*limit < QP_CHAR_LEN)
+		append_qp_break_line(vcards, limit);
+
+	g_string_append_printf(vcards, "=%2.2X", (unsigned char) c);
+	*limit -= QP_CHAR_LEN;
+}
+
+static void append_qp_new_line(GString *vcards, size_t *limit)
+{
+	/* Multiple lines of text are separated with a Quoted Printable CRLF
+	 * sequence of "=0D" followed by "=0A" followed by a Quoted Printable
+	 * softline break sequence of "=" */
+	append_qp_hex(vcards, limit, QP_CR);
+	append_qp_hex(vcards, limit, QP_LF);
+	append_qp_break_line(vcards, limit);
+}
+
+static gboolean utf8_select(const char *field)
+{
+	const char *pos;
+
+	if (g_utf8_validate(field, -1, NULL) == FALSE)
+		return FALSE;
+
+	for (pos = field; *pos != '\0'; pos = g_utf8_next_char(pos)) {
+		/* Test for non-standard UTF-8 character (out of range
+		 * standard ASCII set), composed of more than single byte
+		 * and represented by 32-bit value greater than 0x7F */
+		if (g_utf8_get_char(pos) > ASCII_LIMIT)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void vcard_qp_print_encoded(GString *vcards, const char *desc, ...)
+{
+	const char *field, *charset = "";
+	const char *encoding = ";ENCODING=QUOTED-PRINTABLE";
+	size_t limit, param_len;
+	va_list ap;
+
+	va_start(ap, desc);
+
+	for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
+		if (utf8_select(field) == TRUE) {
+			charset = ";CHARSET=UTF-8";
+			break;
+		}
+	}
+
+	va_end(ap);
+
+	vcard_printf(vcards, "%s%s%s:", desc, encoding, charset);
+	g_string_truncate(vcards, vcards->len - 2);
+
+	param_len = strlen(desc) + strlen(encoding) + strlen(charset) + 1;
+	limit = QP_LINE_LEN - param_len;
+
+	va_start(ap, desc);
+
+	for (field = va_arg(ap, char *); field != NULL; ) {
+		size_t i, size = strlen(field);
+
+		for (i = 0; i < size; ++i) {
+			if (set_qp_encoding(field[i])) {
+				if (field[i] == '\n') {
+					append_qp_new_line(vcards, &limit);
+					continue;
+				}
+
+				append_qp_hex(vcards, &limit, field[i]);
+			} else {
+				/* According to vCard 2.1 spec. semicolons in
+				 * property parameter value must be escaped */
+				if (field[i] == ';')
+					append_qp_hex(vcards, &limit, QP_ESC);
+
+				append_qp_ascii(vcards, &limit, field[i]);
+			}
+		}
+
+		field = va_arg(ap, char *);
+		if (field)
+			append_qp_ascii(vcards, &limit, ';');
+	}
+
+	va_end(ap);
+
+	g_string_append(vcards, "\r\n");
+}
+
+static gboolean select_qp_encoding(uint8_t format, ...)
+{
+	char *field;
+	va_list ap;
+
+	if (format != FORMAT_VCARD21)
+		return FALSE;
+
+	va_start(ap, format);
+
+	for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
+		int i;
+		unsigned char c;
+
+		if (strpbrk(field, QP_SELECT)) {
+			va_end(ap);
+			return TRUE;
+		}
+
+		/* Quoted Printable encoding is selected if there is
+		 * a character, which value is out of range standard
+		 * ASCII set, since it may be a part of some
+		 * non-standard character such as specified by UTF-8 */
+		for (i = 0; (c = field[i]) != '\0'; ++i) {
+			if (c > ASCII_LIMIT) {
+				va_end(ap);
+				return TRUE;
+			}
+		}
+	}
+
+	va_end(ap);
+
+	return FALSE;
+}
+
+static void vcard_printf_begin(GString *vcards, uint8_t format)
+{
+	vcard_printf(vcards, "BEGIN:VCARD");
+
+	if (format == FORMAT_VCARD30)
+		vcard_printf(vcards, "VERSION:3.0");
+	else if (format == FORMAT_VCARD21)
+		vcard_printf(vcards, "VERSION:2.1");
+}
+
+/* check if there is at least one contact field with personal data present */
+static gboolean contact_fields_present(struct phonebook_contact * contact)
+{
+	if (contact->family && strlen(contact->family) > 0)
+		return TRUE;
+
+	if (contact->given && strlen(contact->given) > 0)
+		return TRUE;
+
+	if (contact->additional && strlen(contact->additional) > 0)
+		return TRUE;
+
+	if (contact->prefix && strlen(contact->prefix) > 0)
+		return TRUE;
+
+	if (contact->suffix && strlen(contact->suffix) > 0)
+		return TRUE;
+
+	/* none of the personal data fields are present*/
+	return FALSE;
+}
+
+static void vcard_printf_name(GString *vcards, uint8_t format,
+					struct phonebook_contact *contact)
+{
+	char *fields;
+
+	if (contact_fields_present(contact) == FALSE) {
+		/* If fields are empty, add only 'N:' as parameter.
+		 * This is crucial for some devices (Nokia BH-903) which
+		 * have problems with history listings and can't determine
+		 * that a parameter is really empty if there are unnecessary
+		 * characters after 'N:' (e.g. 'N:;;;;').
+		 * We need to add only'N:' param - without semicolons.
+		 */
+		vcard_printf(vcards, "N:");
+		return;
+	}
+
+	if (select_qp_encoding(format, contact->family, contact->given,
+					contact->additional, contact->prefix,
+					contact->suffix, NULL)) {
+		vcard_qp_print_encoded(vcards, "N", contact->family,
+					contact->given, contact->additional,
+					contact->prefix, contact->suffix,
+					NULL);
+		return;
+	}
+
+	get_escaped_fields(format, &fields, contact->family,
+				contact->given, contact->additional,
+				contact->prefix, contact->suffix,
+				NULL);
+
+	vcard_printf(vcards, "N:%s", fields);
+
+	g_free(fields);
+}
+
+static void vcard_printf_fullname(GString *vcards, uint8_t format,
+							const char *text)
+{
+	char field[LEN_MAX];
+
+	if (!text || strlen(text) == 0) {
+		vcard_printf(vcards, "FN:");
+		return;
+	}
+
+	if (select_qp_encoding(format, text, NULL)) {
+		vcard_qp_print_encoded(vcards, "FN", text, NULL);
+		return;
+	}
+
+	set_escape(format, field, text, LEN_MAX, strlen(text));
+	vcard_printf(vcards, "FN:%s", field);
+}
+
+static void vcard_printf_number(GString *vcards, uint8_t format,
+					const char *number, int type,
+					enum phonebook_number_type category)
+{
+	const char *intl = "", *category_string = "";
+	char buf[LEN_MAX], field[LEN_MAX];
+
+	/* TEL is a mandatory field, include even if empty */
+	if (!number || !strlen(number) || !type) {
+		vcard_printf(vcards, "TEL:");
+		return;
+	}
+
+	switch (category) {
+	case TEL_TYPE_HOME:
+		if (format == FORMAT_VCARD21)
+			category_string = "HOME;VOICE";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=HOME;TYPE=VOICE";
+		break;
+	case TEL_TYPE_MOBILE:
+		if (format == FORMAT_VCARD21)
+			category_string = "CELL;VOICE";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=CELL;TYPE=VOICE";
+		break;
+	case TEL_TYPE_FAX:
+		if (format == FORMAT_VCARD21)
+			category_string = "FAX";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=FAX";
+		break;
+	case TEL_TYPE_WORK:
+		if (format == FORMAT_VCARD21)
+			category_string = "WORK;VOICE";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=WORK;TYPE=VOICE";
+		break;
+	case TEL_TYPE_OTHER:
+		if (format == FORMAT_VCARD21)
+			category_string = "OTHER;VOICE";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=OTHER;TYPE=VOICE";
+		break;
+	}
+
+	if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
+		intl = "+";
+
+	snprintf(field, sizeof(field), "%s%s", intl, number);
+
+	if (select_qp_encoding(format, number, NULL)) {
+		snprintf(buf, sizeof(buf), "TEL;%s", category_string);
+		vcard_qp_print_encoded(vcards, buf, field, NULL);
+		return;
+	}
+
+	vcard_printf(vcards, "TEL;%s:%s", category_string, field);
+}
+
+static void vcard_printf_tag(GString *vcards, uint8_t format,
+					const char *tag, const char *category,
+					const char *fld)
+{
+	int len;
+	char *separator = "", *type = "";
+	char buf[LEN_MAX], field[LEN_MAX];
+
+	if (tag == NULL || strlen(tag) == 0)
+		return;
+
+	if (fld == NULL || (len = strlen(fld)) == 0) {
+		vcard_printf(vcards, "%s:", tag);
+		return;
+	}
+
+	if (category && strlen(category)) {
+		separator = ";";
+		if (format == FORMAT_VCARD30)
+			type = "TYPE=";
+	} else {
+		category = "";
+	}
+
+	snprintf(buf, LEN_MAX, "%s%s%s%s", tag, separator, type, category);
+
+	if (select_qp_encoding(format, fld, NULL)) {
+		vcard_qp_print_encoded(vcards, buf, fld, NULL);
+		return;
+	}
+
+	set_escape(format, field, fld, LEN_MAX, len);
+	vcard_printf(vcards, "%s:%s", buf, field);
+}
+
+static void vcard_printf_email(GString *vcards, uint8_t format,
+					const char *address,
+					enum phonebook_field_type category)
+{
+	const char *category_string = "";
+	char buf[LEN_MAX], field[LEN_MAX];
+	int len = 0;
+
+	if (!address || !(len = strlen(address))) {
+		vcard_printf(vcards, "EMAIL:");
+		return;
+	}
+	switch (category) {
+	case FIELD_TYPE_HOME:
+		if (format == FORMAT_VCARD21)
+			category_string = "INTERNET;HOME";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=INTERNET;TYPE=HOME";
+		break;
+	case FIELD_TYPE_WORK:
+		if (format == FORMAT_VCARD21)
+			category_string = "INTERNET;WORK";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=INTERNET;TYPE=WORK";
+		break;
+	case FIELD_TYPE_OTHER:
+	default:
+		if (format == FORMAT_VCARD21)
+			category_string = "INTERNET";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=INTERNET;TYPE=OTHER";
+	}
+
+	if (select_qp_encoding(format, address, NULL)) {
+		snprintf(buf, sizeof(buf), "EMAIL;%s", category_string);
+		vcard_qp_print_encoded(vcards, buf, address, NULL);
+		return;
+	}
+
+	set_escape(format, field, address, LEN_MAX, len);
+	vcard_printf(vcards, "EMAIL;%s:%s", category_string, field);
+}
+
+static void vcard_printf_url(GString *vcards, uint8_t format,
+					const char *url,
+					enum phonebook_field_type category)
+{
+	const char *category_string = "";
+	char buf[LEN_MAX], field[LEN_MAX];
+
+	if (!url || strlen(url) == 0) {
+		vcard_printf(vcards, "URL:");
+		return;
+	}
+
+	switch (category) {
+	case FIELD_TYPE_HOME:
+		if (format == FORMAT_VCARD21)
+			category_string = "INTERNET;HOME";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=INTERNET;TYPE=HOME";
+		break;
+	case FIELD_TYPE_WORK:
+		if (format == FORMAT_VCARD21)
+			category_string = "INTERNET;WORK";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=INTERNET;TYPE=WORK";
+		break;
+	case FIELD_TYPE_OTHER:
+	default:
+		if (format == FORMAT_VCARD21)
+			category_string = "INTERNET";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=INTERNET";
+		break;
+	}
+
+	if (select_qp_encoding(format, url, NULL)) {
+		snprintf(buf, sizeof(buf), "URL;%s", category_string);
+		vcard_qp_print_encoded(vcards, buf, url, NULL);
+		return;
+	}
+
+	set_escape(format, field, url, LEN_MAX, strlen(url));
+	vcard_printf(vcards, "URL;%s:%s", category_string, field);
+}
+
+static gboolean org_fields_present(struct phonebook_contact *contact)
+{
+	if (contact->company && strlen(contact->company))
+		return TRUE;
+
+	if (contact->department && strlen(contact->department))
+		return TRUE;
+
+	return FALSE;
+}
+
+static void vcard_printf_org(GString *vcards, uint8_t format,
+					struct phonebook_contact *contact)
+{
+	char *fields;
+
+	if (org_fields_present(contact) == FALSE)
+		return;
+
+	if (select_qp_encoding(format, contact->company,
+						contact->department, NULL)) {
+		vcard_qp_print_encoded(vcards, "ORG", contact->company,
+						contact->department, NULL);
+		return;
+	}
+
+	get_escaped_fields(format, &fields, contact->company,
+					contact->department, NULL);
+
+	vcard_printf(vcards, "ORG:%s", fields);
+
+	g_free(fields);
+}
+
+static void vcard_printf_address(GString *vcards, uint8_t format,
+					struct phonebook_addr *address)
+{
+	char *fields, field_esc[LEN_MAX];
+	const char *category_string = "";
+	char buf[LEN_MAX], *address_fields[ADDR_FIELD_AMOUNT];
+	int i;
+	size_t len;
+	GSList *l;
+
+	if (!address) {
+		vcard_printf(vcards, "ADR:");
+		return;
+	}
+
+	switch (address->type) {
+	case FIELD_TYPE_HOME:
+		if (format == FORMAT_VCARD21)
+			category_string = "HOME";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=HOME";
+		break;
+	case FIELD_TYPE_WORK:
+		if (format == FORMAT_VCARD21)
+			category_string = "WORK";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=WORK";
+		break;
+	default:
+		if (format == FORMAT_VCARD21)
+			category_string = "OTHER";
+		else if (format == FORMAT_VCARD30)
+			category_string = "TYPE=OTHER";
+		break;
+	}
+
+	for (i = 0, l = address->fields; l; l = l->next)
+		address_fields[i++] = l->data;
+
+	if (select_qp_encoding(format, address_fields[0], address_fields[1],
+					address_fields[2], address_fields[3],
+					address_fields[4], address_fields[5],
+					address_fields[6], NULL)) {
+		snprintf(buf, sizeof(buf), "ADR;%s", category_string);
+		vcard_qp_print_encoded(vcards, buf,
+					address_fields[0], address_fields[1],
+					address_fields[2], address_fields[3],
+					address_fields[4], address_fields[5],
+					address_fields[6], NULL);
+		return;
+	}
+
+	/* allocate enough memory to insert address fields separated by ';'
+	 * and terminated by '\0' */
+	len = ADDR_FIELD_AMOUNT * LEN_MAX;
+	fields = g_malloc0(len);
+
+	for (l = address->fields; l; l = l->next) {
+		char *field = l->data;
+
+		if (field) {
+			set_escape(format, field_esc, field, LEN_MAX,
+								strlen(field));
+			g_strlcat(fields, field_esc, len);
+		}
+
+		if (l->next)
+			/* not adding ';' after last addr field */
+			g_strlcat(fields, ";", len);
+	}
+
+	vcard_printf(vcards,"ADR;%s:%s", category_string, fields);
+
+	g_free(fields);
+}
+
+static void vcard_printf_datetime(GString *vcards, uint8_t format,
+					struct phonebook_contact *contact)
+{
+	const char *type;
+	char buf[LEN_MAX];
+
+	switch (contact->calltype) {
+	case CALL_TYPE_MISSED:
+		type = "MISSED";
+		break;
+
+	case CALL_TYPE_INCOMING:
+		type = "RECEIVED";
+		break;
+
+	case CALL_TYPE_OUTGOING:
+		type = "DIALED";
+		break;
+
+	case CALL_TYPE_NOT_A_CALL:
+	default:
+		return;
+	}
+
+	if (select_qp_encoding(format, contact->datetime, NULL)) {
+		snprintf(buf, sizeof(buf), "X-IRMC-CALL-DATETIME;%s", type);
+		vcard_qp_print_encoded(vcards, buf, contact->datetime, NULL);
+		return;
+	}
+
+	vcard_printf(vcards, "X-IRMC-CALL-DATETIME;%s:%s", type,
+							contact->datetime);
+}
+
+static void vcard_printf_end(GString *vcards)
+{
+	vcard_printf(vcards, "END:VCARD");
+}
+
+void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
+					uint64_t filter, uint8_t format)
+{
+	if (format == FORMAT_VCARD30 && filter)
+		filter |= (FILTER_VERSION | FILTER_FN | FILTER_N | FILTER_TEL);
+	else if (format == FORMAT_VCARD21 && filter)
+		filter |= (FILTER_VERSION | FILTER_N | FILTER_TEL);
+	else
+		filter = (FILTER_VERSION | FILTER_UID | FILTER_N | FILTER_FN |
+				FILTER_TEL | FILTER_EMAIL | FILTER_ADR |
+				FILTER_BDAY | FILTER_NICKNAME | FILTER_URL |
+				FILTER_PHOTO | FILTER_ORG | FILTER_ROLE |
+				FILTER_TITLE | FILTER_X_IRMC_CALL_DATETIME);
+
+	vcard_printf_begin(vcards, format);
+
+	if (filter & FILTER_UID && *contact->uid)
+		vcard_printf_tag(vcards, format, "UID", NULL, contact->uid);
+
+	if (filter & FILTER_N)
+		vcard_printf_name(vcards, format, contact);
+
+	if (filter & FILTER_FN && (*contact->fullname ||
+					format == FORMAT_VCARD30))
+		vcard_printf_fullname(vcards, format, contact->fullname);
+
+	if (filter & FILTER_TEL) {
+		GSList *l = contact->numbers;
+
+		if (g_slist_length(l) == 0)
+			vcard_printf_number(vcards, format, NULL, 1,
+							TEL_TYPE_OTHER);
+
+		for (; l; l = l->next) {
+			struct phonebook_field *number = l->data;
+
+			vcard_printf_number(vcards, format, number->text, 1,
+								number->type);
+		}
+	}
+
+	if (filter & FILTER_EMAIL) {
+		GSList *l = contact->emails;
+
+		for (; l; l = l->next) {
+			struct phonebook_field *email = l->data;
+			vcard_printf_email(vcards, format, email->text,
+								email->type);
+		}
+	}
+
+	if (filter & FILTER_ADR) {
+		GSList *l = contact->addresses;
+
+		for (; l; l = l->next) {
+			struct phonebook_addr *addr = l->data;
+			vcard_printf_address(vcards, format, addr);
+		}
+	}
+
+	if (filter & FILTER_BDAY && *contact->birthday)
+		vcard_printf_tag(vcards, format, "BDAY", NULL,
+						contact->birthday);
+
+	if (filter & FILTER_NICKNAME && *contact->nickname)
+		vcard_printf_tag(vcards, format, "NICKNAME", NULL,
+							contact->nickname);
+
+	if (filter & FILTER_URL) {
+		GSList *l = contact->urls;
+
+		for (; l; l = l->next) {
+			struct phonebook_field *url = l->data;
+			vcard_printf_url(vcards, format, url->text, url->type);
+		}
+	}
+
+	if (filter & FILTER_PHOTO && *contact->photo)
+		vcard_printf_tag(vcards, format, "PHOTO", NULL,
+							contact->photo);
+
+	if (filter & FILTER_ORG)
+		vcard_printf_org(vcards, format, contact);
+
+	if (filter & FILTER_ROLE && *contact->role)
+		vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role);
+
+	if (filter & FILTER_TITLE && *contact->title)
+		vcard_printf_tag(vcards, format, "TITLE", NULL, contact->title);
+
+	if (filter & FILTER_X_IRMC_CALL_DATETIME)
+		vcard_printf_datetime(vcards, format, contact);
+
+	vcard_printf_end(vcards);
+}
+
+static void field_free(gpointer data)
+{
+	struct phonebook_field *field = data;
+
+	g_free(field->text);
+	g_free(field);
+}
+
+void phonebook_addr_free(gpointer addr)
+{
+	struct phonebook_addr *address = addr;
+
+	g_slist_free_full(address->fields, g_free);
+	g_free(address);
+}
+
+void phonebook_contact_free(struct phonebook_contact *contact)
+{
+	if (contact == NULL)
+		return;
+
+	g_slist_free_full(contact->numbers, field_free);
+	g_slist_free_full(contact->emails, field_free);
+	g_slist_free_full(contact->addresses, phonebook_addr_free);
+	g_slist_free_full(contact->urls, field_free);
+
+	g_free(contact->uid);
+	g_free(contact->fullname);
+	g_free(contact->given);
+	g_free(contact->family);
+	g_free(contact->additional);
+	g_free(contact->prefix);
+	g_free(contact->suffix);
+	g_free(contact->birthday);
+	g_free(contact->nickname);
+	g_free(contact->photo);
+	g_free(contact->company);
+	g_free(contact->department);
+	g_free(contact->role);
+	g_free(contact->title);
+	g_free(contact->datetime);
+	g_free(contact);
+}
diff --git a/bluez/repo/obexd/plugins/vcard.h b/bluez/repo/obexd/plugins/vcard.h
new file mode 100644
index 0000000..22c3f68
--- /dev/null
+++ b/bluez/repo/obexd/plugins/vcard.h
@@ -0,0 +1,81 @@
+/*
+ * OBEX Server
+ *
+ * Copyright (C) 2008-2010 Intel Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum phonebook_number_type {
+	TEL_TYPE_HOME,
+	TEL_TYPE_MOBILE,
+	TEL_TYPE_FAX,
+	TEL_TYPE_WORK,
+	TEL_TYPE_OTHER,
+};
+
+enum phonebook_field_type {
+	FIELD_TYPE_HOME,
+	FIELD_TYPE_WORK,
+	FIELD_TYPE_OTHER,
+};
+
+enum phonebook_call_type {
+	CALL_TYPE_NOT_A_CALL,
+	CALL_TYPE_MISSED,
+	CALL_TYPE_INCOMING,
+	CALL_TYPE_OUTGOING,
+};
+
+struct phonebook_field {
+	char *text;
+	int type;
+};
+
+struct phonebook_addr {
+	GSList *fields;
+	int type;
+};
+
+struct phonebook_contact {
+	char *uid;
+	char *fullname;
+	char *given;
+	char *family;
+	char *additional;
+	GSList *numbers;
+	GSList *emails;
+	char *prefix;
+	char *suffix;
+	GSList *addresses;
+	char *birthday;
+	char *nickname;
+	GSList *urls;
+	char *photo;
+	char *company;
+	char *department;
+	char *role;
+	char *title;
+	char *datetime;
+	int calltype;
+};
+
+void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
+					uint64_t filter, uint8_t format);
+
+void phonebook_contact_free(struct phonebook_contact *contact);
+
+void phonebook_addr_free(gpointer addr);
diff --git a/bluez/repo/obexd/src/._genbuiltin b/bluez/repo/obexd/src/._genbuiltin
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._genbuiltin
Binary files differ
diff --git a/bluez/repo/obexd/src/._log.c b/bluez/repo/obexd/src/._log.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._log.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._log.h b/bluez/repo/obexd/src/._log.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._log.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._main.c b/bluez/repo/obexd/src/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._main.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._manager.c b/bluez/repo/obexd/src/._manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._manager.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._manager.h b/bluez/repo/obexd/src/._manager.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._manager.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._map_ap.h b/bluez/repo/obexd/src/._map_ap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._map_ap.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._mimetype.c b/bluez/repo/obexd/src/._mimetype.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._mimetype.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._mimetype.h b/bluez/repo/obexd/src/._mimetype.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._mimetype.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._obex-priv.h b/bluez/repo/obexd/src/._obex-priv.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._obex-priv.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._obex.c b/bluez/repo/obexd/src/._obex.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._obex.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._obex.h b/bluez/repo/obexd/src/._obex.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._obex.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._obex.service.in b/bluez/repo/obexd/src/._obex.service.in
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._obex.service.in
Binary files differ
diff --git a/bluez/repo/obexd/src/._obexd.h b/bluez/repo/obexd/src/._obexd.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._obexd.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._org.bluez.obex.service b/bluez/repo/obexd/src/._org.bluez.obex.service
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._org.bluez.obex.service
Binary files differ
diff --git a/bluez/repo/obexd/src/._plugin.c b/bluez/repo/obexd/src/._plugin.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._plugin.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._plugin.h b/bluez/repo/obexd/src/._plugin.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._plugin.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._server.c b/bluez/repo/obexd/src/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._server.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._server.h b/bluez/repo/obexd/src/._server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._server.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._service.c b/bluez/repo/obexd/src/._service.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._service.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._service.h b/bluez/repo/obexd/src/._service.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._service.h
Binary files differ
diff --git a/bluez/repo/obexd/src/._transport.c b/bluez/repo/obexd/src/._transport.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._transport.c
Binary files differ
diff --git a/bluez/repo/obexd/src/._transport.h b/bluez/repo/obexd/src/._transport.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/obexd/src/._transport.h
Binary files differ
diff --git a/bluez/repo/obexd/src/genbuiltin b/bluez/repo/obexd/src/genbuiltin
new file mode 100755
index 0000000..39f7735
--- /dev/null
+++ b/bluez/repo/obexd/src/genbuiltin
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+	echo "extern struct obex_plugin_desc __obex_builtin_$i;"
+done
+
+echo
+echo "static struct obex_plugin_desc *__obex_builtin[] = {"
+
+for i in $*
+do
+	echo "  &__obex_builtin_$i,"
+done
+
+echo "  NULL"
+echo "};"
diff --git a/bluez/repo/obexd/src/log.c b/bluez/repo/obexd/src/log.c
new file mode 100644
index 0000000..f259728
--- /dev/null
+++ b/bluez/repo/obexd/src/log.c
@@ -0,0 +1,136 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <stdarg.h>
+#include <syslog.h>
+
+#include <glib.h>
+
+#include "log.h"
+
+void info(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vsyslog(LOG_INFO, format, ap);
+
+	va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vsyslog(LOG_ERR, format, ap);
+
+	va_end(ap);
+}
+
+void obex_debug(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vsyslog(LOG_DEBUG, format, ap);
+
+	va_end(ap);
+}
+
+extern struct obex_debug_desc __start___debug[];
+extern struct obex_debug_desc __stop___debug[];
+
+static char **enabled = NULL;
+
+static gboolean is_enabled(struct obex_debug_desc *desc)
+{
+	int i;
+
+	if (enabled == NULL)
+		return 0;
+
+	for (i = 0; enabled[i] != NULL; i++) {
+		if (desc->name != NULL && g_pattern_match_simple(enabled[i],
+							desc->name) == TRUE)
+			return 1;
+		if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+							desc->file) == TRUE)
+			return 1;
+	}
+
+	return 0;
+}
+
+void __obex_log_enable_debug(void)
+{
+	struct obex_debug_desc *desc;
+
+	for (desc = __start___debug; desc < __stop___debug; desc++)
+		desc->flags |= OBEX_DEBUG_FLAG_PRINT;
+}
+
+void __obex_log_init(const char *debug, int detach)
+{
+	int option = LOG_NDELAY | LOG_PID;
+	struct obex_debug_desc *desc;
+	const char *name = NULL, *file = NULL;
+
+	if (debug != NULL)
+		enabled = g_strsplit_set(debug, ":, ", 0);
+
+	for (desc = __start___debug; desc < __stop___debug; desc++) {
+		if (file != NULL || name != NULL) {
+			if (g_strcmp0(desc->file, file) == 0) {
+				if (desc->name == NULL)
+					desc->name = name;
+			} else
+				file = NULL;
+		}
+
+		if (is_enabled(desc))
+			desc->flags |= OBEX_DEBUG_FLAG_PRINT;
+	}
+
+	if (!detach)
+		option |= LOG_PERROR;
+
+	openlog("obexd", option, LOG_DAEMON);
+
+	info("OBEX daemon %s", VERSION);
+}
+
+void __obex_log_cleanup(void)
+{
+	closelog();
+
+	g_strfreev(enabled);
+}
diff --git a/bluez/repo/obexd/src/log.h b/bluez/repo/obexd/src/log.h
new file mode 100644
index 0000000..d9fb867
--- /dev/null
+++ b/bluez/repo/obexd/src/log.h
@@ -0,0 +1,56 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void obex_debug(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void __obex_log_init(const char *debug, int detach);
+void __obex_log_cleanup(void);
+void __obex_log_enable_debug(void);
+
+struct obex_debug_desc {
+	const char *name;
+	const char *file;
+#define OBEX_DEBUG_FLAG_DEFAULT (0)
+#define OBEX_DEBUG_FLAG_PRINT   (1 << 0)
+	unsigned int flags;
+} __attribute__((aligned(8)));
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around debug() which also include the function
+ * name it is called in.
+ */
+#define DBG(fmt, arg...) do { \
+	static struct obex_debug_desc __obex_debug_desc \
+	__attribute__((used, section("__debug"), aligned(8))) = { \
+		.file = __FILE__, .flags = OBEX_DEBUG_FLAG_DEFAULT, \
+	}; \
+	if (__obex_debug_desc.flags & OBEX_DEBUG_FLAG_PRINT) \
+		obex_debug("%s:%s() " fmt,  __FILE__, __func__ , ## arg); \
+} while (0)
diff --git a/bluez/repo/obexd/src/main.c b/bluez/repo/obexd/src/main.c
new file mode 100644
index 0000000..c774cda
--- /dev/null
+++ b/bluez/repo/obexd/src/main.c
@@ -0,0 +1,342 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/signalfd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "../client/manager.h"
+
+#include "log.h"
+#include "obexd.h"
+#include "server.h"
+
+#define DEFAULT_CAP_FILE CONFIGDIR "/capability.xml"
+
+static GMainLoop *main_loop = NULL;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	static unsigned int __terminated = 0;
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		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:
+	case SIGTERM:
+		if (__terminated == 0) {
+			info("Terminating");
+			g_main_loop_quit(main_loop);
+		}
+
+		__terminated = 1;
+		break;
+	case SIGUSR2:
+		__obex_log_enable_debug();
+		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);
+	sigaddset(&mask, SIGUSR2);
+
+	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 option_detach = TRUE;
+static char *option_debug = NULL;
+
+static char *option_root = NULL;
+static char *option_root_setup = NULL;
+static char *option_capability = NULL;
+static char *option_plugin = NULL;
+static char *option_noplugin = NULL;
+
+static gboolean option_autoaccept = FALSE;
+static gboolean option_symlinks = FALSE;
+
+static gboolean parse_debug(const char *key, const char *value,
+				gpointer user_data, GError **error)
+{
+	if (value)
+		option_debug = g_strdup(value);
+	else
+		option_debug = g_strdup("*");
+
+	return TRUE;
+}
+
+static GOptionEntry options[] = {
+	{ "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+				G_OPTION_ARG_CALLBACK, parse_debug,
+				"Enable debug information output", "DEBUG" },
+	{ "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+				"Specify plugins to load", "NAME,..." },
+	{ "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+				"Specify plugins not to load", "NAME,..." },
+	{ "nodetach", 'n', G_OPTION_FLAG_REVERSE,
+				G_OPTION_ARG_NONE, &option_detach,
+				"Run with logging in foreground" },
+	{ "root", 'r', 0, G_OPTION_ARG_STRING, &option_root,
+				"Specify root folder location. Both absolute "
+				"and relative can be used, but relative paths "
+				"are assumed to be relative to user $HOME "
+				"folder. Default $XDG_CACHE_HOME", "PATH" },
+	{ "root-setup", 'S', 0, G_OPTION_ARG_STRING, &option_root_setup,
+				"Root folder setup script", "SCRIPT" },
+	{ "symlinks", 'l', 0, G_OPTION_ARG_NONE, &option_symlinks,
+				"Allow symlinks leading outside of the root "
+				"folder" },
+	{ "capability", 'c', 0, G_OPTION_ARG_STRING, &option_capability,
+				"Specify capability file, use '!' mark for "
+				"scripts", "FILE" },
+	{ "auto-accept", 'a', 0, G_OPTION_ARG_NONE, &option_autoaccept,
+				"Automatically accept push requests" },
+	{ NULL },
+};
+
+gboolean obex_option_auto_accept(void)
+{
+	return option_autoaccept;
+}
+
+const char *obex_option_root_folder(void)
+{
+	return option_root;
+}
+
+gboolean obex_option_symlinks(void)
+{
+	return option_symlinks;
+}
+
+const char *obex_option_capability(void)
+{
+	return option_capability;
+}
+
+static gboolean is_dir(const char *dir)
+{
+	struct stat st;
+
+	if (stat(dir, &st) < 0) {
+		error("stat(%s): %s (%d)", dir, strerror(errno), errno);
+		return FALSE;
+	}
+
+	return S_ISDIR(st.st_mode);
+}
+
+static gboolean root_folder_setup(char *root, char *root_setup)
+{
+	int status;
+	char *argv[3] = { root_setup, root, NULL };
+
+	if (is_dir(root))
+		return TRUE;
+
+	if (root_setup == NULL)
+		return FALSE;
+
+	DBG("Setting up %s using %s", root, root_setup);
+
+	if (!g_spawn_sync(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL,
+							&status, NULL)) {
+		error("Unable to execute %s", root_setup);
+		return FALSE;
+	}
+
+	if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+		error("%s exited with status %d", root_setup,
+							WEXITSTATUS(status));
+		return FALSE;
+	}
+
+	return is_dir(root);
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	guint signal;
+
+#ifdef NEED_THREADS
+	if (g_thread_supported() == FALSE)
+		g_thread_init(NULL);
+#endif
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+		if (err != NULL) {
+			g_printerr("%s\n", err->message);
+			g_error_free(err);
+		} else
+			g_printerr("An unknown error occurred\n");
+		exit(EXIT_FAILURE);
+	}
+
+	g_option_context_free(context);
+
+	__obex_log_init(option_debug, option_detach);
+
+	DBG("Entering main loop");
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	signal = setup_signalfd();
+
+#ifdef NEED_THREADS
+	if (dbus_threads_init_default() == FALSE) {
+		fprintf(stderr, "Can't init usage of threads\n");
+		exit(EXIT_FAILURE);
+	}
+#endif
+
+	if (manager_init() == FALSE) {
+		error("manager_init failed");
+		exit(EXIT_FAILURE);
+	}
+
+	if (option_root == NULL) {
+		option_root = g_build_filename(g_get_user_cache_dir(), "obexd",
+									NULL);
+		g_mkdir_with_parents(option_root, 0700);
+	}
+
+	if (option_root[0] != '/') {
+		const char *home = getenv("HOME");
+		if (home) {
+			char *old_root = option_root;
+			option_root = g_strdup_printf("%s/%s", home, old_root);
+			g_free(old_root);
+		}
+	}
+
+	if (option_capability == NULL)
+		option_capability = g_strdup(DEFAULT_CAP_FILE);
+
+	plugin_init(option_plugin, option_noplugin);
+
+	if (obex_server_init() < 0) {
+		error("obex_server_init failed");
+		exit(EXIT_FAILURE);
+	}
+
+	if (!root_folder_setup(option_root, option_root_setup)) {
+		error("Unable to setup root folder %s", option_root);
+		exit(EXIT_FAILURE);
+	}
+
+	if (client_manager_init() < 0) {
+		error("client_manager_init failed");
+		exit(EXIT_FAILURE);
+	}
+
+	g_main_loop_run(main_loop);
+
+	g_source_remove(signal);
+
+	client_manager_exit();
+
+	obex_server_exit();
+
+	plugin_cleanup();
+
+	manager_cleanup();
+
+	g_main_loop_unref(main_loop);
+
+	g_free(option_capability);
+	g_free(option_root);
+
+	__obex_log_cleanup();
+
+	return 0;
+}
diff --git a/bluez/repo/obexd/src/manager.c b/bluez/repo/obexd/src/manager.c
new file mode 100644
index 0000000..f84384a
--- /dev/null
+++ b/bluez/repo/obexd/src/manager.c
@@ -0,0 +1,806 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <inttypes.h>
+
+#include "gdbus/gdbus.h"
+#include "gobex/gobex.h"
+
+#include "btio/btio.h"
+#include "obexd.h"
+#include "obex.h"
+#include "obex-priv.h"
+#include "server.h"
+#include "manager.h"
+#include "log.h"
+#include "service.h"
+
+#define OBEX_BASE_PATH "/org/bluez/obex"
+#define SESSION_BASE_PATH OBEX_BASE_PATH "/server"
+#define OBEX_MANAGER_INTERFACE OBEXD_SERVICE ".AgentManager1"
+#define ERROR_INTERFACE OBEXD_SERVICE ".Error"
+#define TRANSFER_INTERFACE OBEXD_SERVICE ".Transfer1"
+#define SESSION_INTERFACE OBEXD_SERVICE ".Session1"
+#define AGENT_INTERFACE OBEXD_SERVICE ".Agent1"
+
+#define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
+
+struct agent {
+	char *bus_name;
+	char *path;
+	gboolean auth_pending;
+	char *new_name;
+	char *new_folder;
+	unsigned int watch_id;
+};
+
+enum {
+	TRANSFER_STATUS_QUEUED = 0,
+	TRANSFER_STATUS_ACTIVE,
+	TRANSFER_STATUS_COMPLETE,
+	TRANSFER_STATUS_ERROR
+};
+
+struct obex_transfer {
+	uint8_t status;
+	char *path;
+	struct obex_session *session;
+};
+
+static struct agent *agent = NULL;
+
+static DBusConnection *connection = NULL;
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->new_folder);
+	g_free(agent->new_name);
+	g_free(agent->bus_name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".InvalidArguments",
+			"Invalid arguments in method call");
+}
+
+static inline DBusMessage *not_supported(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".NotSupported",
+			"Operation is not supported");
+}
+
+static inline DBusMessage *agent_already_exists(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".AlreadyExists",
+			"Agent already exists");
+}
+
+static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".DoesNotExist",
+			"Agent does not exist");
+}
+
+static inline DBusMessage *not_authorized(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".NotAuthorized",
+			"Not authorized");
+}
+
+static void agent_disconnected(DBusConnection *conn, void *user_data)
+{
+	DBG("Agent exited");
+	agent_free(agent);
+	agent = NULL;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	const char *path, *sender;
+
+	if (agent)
+		return agent_already_exists(msg);
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	sender = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	agent->bus_name = g_strdup(sender);
+	agent->path = g_strdup(path);
+
+	agent->watch_id = g_dbus_add_disconnect_watch(conn, sender,
+					agent_disconnected, NULL, NULL);
+
+	DBG("Agent registered");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	const char *path, *sender;
+
+	if (!agent)
+		return agent_does_not_exist(msg);
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	if (strcmp(agent->path, path) != 0)
+		return agent_does_not_exist(msg);
+
+	sender = dbus_message_get_sender(msg);
+	if (strcmp(agent->bus_name, sender) != 0)
+		return not_authorized(msg);
+
+	g_dbus_remove_watch(conn, agent->watch_id);
+
+	agent_free(agent);
+	agent = NULL;
+
+	DBG("Agent unregistered");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static gboolean get_source(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_session *os = data;
+	char *s;
+
+	s = os->src;
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s);
+
+	return TRUE;
+}
+
+static gboolean get_destination(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_session *os = data;
+	char *s;
+
+	s = os->dst;
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s);
+
+	return TRUE;
+}
+
+static gboolean session_target_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obex_session *os = data;
+
+	return os->service->target ? TRUE : FALSE;
+}
+
+static char *target2str(const uint8_t *t)
+{
+	if (!t)
+		return NULL;
+
+	return g_strdup_printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-"
+				"%02X%02X-%02X%02X%02X%02X%02X%02X",
+				t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7],
+				t[8], t[9], t[10], t[11], t[12], t[13], t[14],
+				t[15]);
+}
+
+static gboolean get_target(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_session *os = data;
+	char *uuid;
+
+	uuid = target2str(os->service->target);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+	g_free(uuid);
+
+	return TRUE;
+}
+
+static gboolean get_root(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	const char *root;
+
+	root = obex_option_root_folder();
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &root);
+
+	return TRUE;
+}
+
+static DBusMessage *transfer_cancel(DBusConnection *connection,
+				DBusMessage *msg, void *user_data)
+{
+	struct obex_transfer *transfer = user_data;
+	struct obex_session *os = transfer->session;
+	const char *sender;
+
+	if (!os)
+		return invalid_args(msg);
+
+	sender = dbus_message_get_sender(msg);
+	if (strcmp(agent->bus_name, sender) != 0)
+		return not_authorized(msg);
+
+	os->aborted = TRUE;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const char *status2str(uint8_t status)
+{
+	switch (status) {
+	case TRANSFER_STATUS_QUEUED:
+		return "queued";
+	case TRANSFER_STATUS_ACTIVE:
+		return "active";
+	case TRANSFER_STATUS_COMPLETE:
+		return "complete";
+	case TRANSFER_STATUS_ERROR:
+	default:
+		return "error";
+	}
+}
+
+static gboolean transfer_get_status(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	const char *status = status2str(transfer->status);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+	return TRUE;
+}
+
+static gboolean transfer_get_session(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+	char *path;
+
+	if (session == NULL)
+		return FALSE;
+
+	path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, session->id);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	g_free(path);
+
+	return TRUE;
+}
+
+static gboolean transfer_name_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	return session->name != NULL;
+}
+
+static gboolean transfer_get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	if (session->name == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->name);
+
+	return TRUE;
+}
+
+static gboolean transfer_type_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	return session->type != NULL;
+}
+
+static gboolean transfer_get_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	if (session->type == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->type);
+
+	return TRUE;
+}
+
+static gboolean transfer_size_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	return session->size != OBJECT_SIZE_UNKNOWN;
+}
+
+static gboolean transfer_get_size(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	if (session->size == OBJECT_SIZE_UNKNOWN)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &session->size);
+
+	return TRUE;
+}
+
+static gboolean transfer_time_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	return session->time != 0;
+}
+
+static gboolean transfer_get_time(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+	dbus_uint64_t time_u64;
+
+	if (session->size == 0)
+		return FALSE;
+
+	time_u64 = session->time;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &time_u64);
+
+	return TRUE;
+}
+
+static gboolean transfer_filename_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	return session->path != NULL;
+}
+
+static gboolean transfer_get_filename(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	if (session->path == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->path);
+
+	return TRUE;
+}
+
+static gboolean transfer_get_transferred(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct obex_transfer *transfer = data;
+	struct obex_session *session = transfer->session;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
+							&session->offset);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable manager_methods[] = {
+	{ GDBUS_METHOD("RegisterAgent",
+			GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
+	{ GDBUS_METHOD("UnregisterAgent",
+			GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
+	{ }
+};
+
+static const GDBusMethodTable transfer_methods[] = {
+	{ GDBUS_METHOD("Cancel", NULL, NULL, transfer_cancel) },
+	{ }
+};
+
+static const GDBusPropertyTable transfer_properties[] = {
+	{ "Status", "s", transfer_get_status },
+	{ "Session", "o", transfer_get_session },
+	{ "Name", "s", transfer_get_name, NULL, transfer_name_exists },
+	{ "Type", "s", transfer_get_type, NULL, transfer_type_exists },
+	{ "Size", "t", transfer_get_size, NULL, transfer_size_exists },
+	{ "Time", "t", transfer_get_time, NULL, transfer_time_exists },
+	{ "Filename", "s", transfer_get_filename, NULL,
+						transfer_filename_exists },
+	{ "Transferred", "t", transfer_get_transferred },
+	{ }
+};
+
+static const GDBusPropertyTable session_properties[] = {
+	{ "Source", "s", get_source },
+	{ "Destination", "s", get_destination },
+	{ "Target", "s", get_target, NULL, session_target_exists },
+	{ "Root", "s", get_root },
+	{ }
+};
+
+gboolean manager_init(void)
+{
+	DBusError err;
+
+	DBG("");
+
+	dbus_error_init(&err);
+
+	connection = g_dbus_setup_bus(DBUS_BUS_SESSION, OBEXD_SERVICE, &err);
+	if (connection == NULL) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		} else
+			fprintf(stderr, "Can't register with session bus\n");
+		return FALSE;
+	}
+
+	g_dbus_attach_object_manager(connection);
+
+	return g_dbus_register_interface(connection, OBEX_BASE_PATH,
+					OBEX_MANAGER_INTERFACE,
+					manager_methods, NULL, NULL,
+					NULL, NULL);
+}
+
+void manager_cleanup(void)
+{
+	DBG("");
+
+	g_dbus_unregister_interface(connection, OBEX_BASE_PATH,
+						OBEX_MANAGER_INTERFACE);
+
+	/* FIXME: Release agent? */
+
+	agent_free(agent);
+
+	g_dbus_detach_object_manager(connection);
+
+	dbus_connection_unref(connection);
+}
+
+void manager_emit_transfer_started(struct obex_transfer *transfer)
+{
+	transfer->status = TRANSFER_STATUS_ACTIVE;
+
+	g_dbus_emit_property_changed(connection, transfer->path,
+					TRANSFER_INTERFACE, "Status");
+}
+
+static void emit_transfer_completed(struct obex_transfer *transfer,
+							gboolean success)
+{
+	if (transfer->path == NULL)
+		return;
+
+	transfer->status = success ? TRANSFER_STATUS_COMPLETE :
+						TRANSFER_STATUS_ERROR;
+
+	g_dbus_emit_property_changed(connection, transfer->path,
+					TRANSFER_INTERFACE, "Status");
+}
+
+static void emit_transfer_progress(struct obex_transfer *transfer,
+					uint32_t total, uint32_t transferred)
+{
+	if (transfer->path == NULL)
+		return;
+
+	g_dbus_emit_property_changed(connection, transfer->path,
+					TRANSFER_INTERFACE, "Transferred");
+}
+
+static void transfer_free(struct obex_transfer *transfer)
+{
+	g_free(transfer->path);
+	g_free(transfer);
+}
+
+struct obex_transfer *manager_register_transfer(struct obex_session *os)
+{
+	struct obex_transfer *transfer;
+	static unsigned int id = 0;
+
+	transfer = g_new0(struct obex_transfer, 1);
+	transfer->path = g_strdup_printf("%s/session%u/transfer%u",
+					SESSION_BASE_PATH, os->id, id++);
+	transfer->session = os;
+
+	if (!g_dbus_register_interface(connection, transfer->path,
+				TRANSFER_INTERFACE,
+				transfer_methods, NULL,
+				transfer_properties, transfer, NULL)) {
+		error("Cannot register Transfer interface.");
+		transfer_free(transfer);
+		return NULL;
+	}
+
+	return transfer;
+}
+
+void manager_unregister_transfer(struct obex_transfer *transfer)
+{
+	struct obex_session *os;
+
+	if (transfer == NULL)
+		return;
+
+	os = transfer->session;
+
+	if (transfer->status == TRANSFER_STATUS_ACTIVE)
+		emit_transfer_completed(transfer, os->offset == os->size);
+
+	g_dbus_unregister_interface(connection, transfer->path,
+							TRANSFER_INTERFACE);
+
+	transfer_free(transfer);
+}
+
+static void agent_cancel(void)
+{
+	DBusMessage *msg;
+
+	if (agent == NULL)
+		return;
+
+	msg = dbus_message_new_method_call(agent->bus_name, agent->path,
+						AGENT_INTERFACE, "Cancel");
+
+	g_dbus_send_message(connection, msg);
+}
+
+static void agent_reply(DBusPendingCall *call, void *user_data)
+{
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	const char *name;
+	DBusError derr;
+	gboolean *got_reply = user_data;
+
+	*got_reply = TRUE;
+
+	/* Received a reply after the agent exited */
+	if (!agent)
+		return;
+
+	agent->auth_pending = FALSE;
+
+	dbus_error_init(&derr);
+	if (dbus_set_error_from_message(&derr, reply)) {
+		error("Agent replied with an error: %s, %s",
+				derr.name, derr.message);
+
+		if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
+			agent_cancel();
+
+		dbus_error_free(&derr);
+		dbus_message_unref(reply);
+		return;
+	}
+
+	if (dbus_message_get_args(reply, NULL,
+				DBUS_TYPE_STRING, &name,
+				DBUS_TYPE_INVALID)) {
+		/* Splits folder and name */
+		const char *slash = strrchr(name, '/');
+		DBG("Agent replied with %s", name);
+		if (!slash) {
+			agent->new_name = g_strdup(name);
+			agent->new_folder = NULL;
+		} else {
+			agent->new_name = g_strdup(slash + 1);
+			agent->new_folder = g_strndup(name, slash - name);
+		}
+	}
+
+	dbus_message_unref(reply);
+}
+
+static gboolean auth_error(GIOChannel *io, GIOCondition cond, void *user_data)
+{
+	agent->auth_pending = FALSE;
+
+	return FALSE;
+}
+
+int manager_request_authorization(struct obex_transfer *transfer,
+					char **new_folder, char **new_name)
+{
+	struct obex_session *os = transfer->session;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	unsigned int watch;
+	gboolean got_reply;
+
+	if (!agent)
+		return -1;
+
+	if (agent->auth_pending)
+		return -EPERM;
+
+	if (!new_folder || !new_name)
+		return -EINVAL;
+
+	msg = dbus_message_new_method_call(agent->bus_name, agent->path,
+							AGENT_INTERFACE,
+							"AuthorizePush");
+
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &transfer->path,
+							DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message_with_reply(connection, msg, &call, TIMEOUT)) {
+		dbus_message_unref(msg);
+		return -EPERM;
+	}
+
+	dbus_message_unref(msg);
+
+	agent->auth_pending = TRUE;
+	got_reply = FALSE;
+
+	/* Catches errors before authorization response comes */
+	watch = g_io_add_watch_full(os->io, G_PRIORITY_DEFAULT,
+			G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			auth_error, NULL, NULL);
+
+	dbus_pending_call_set_notify(call, agent_reply, &got_reply, NULL);
+
+	/* Workaround: process events while agent doesn't reply */
+	while (agent && agent->auth_pending)
+		g_main_context_iteration(NULL, TRUE);
+
+	g_source_remove(watch);
+
+	if (!got_reply) {
+		dbus_pending_call_cancel(call);
+		agent_cancel();
+	}
+
+	dbus_pending_call_unref(call);
+
+	if (!agent || !agent->new_name)
+		return -EPERM;
+
+	*new_folder = agent->new_folder;
+	*new_name = agent->new_name;
+	agent->new_folder = NULL;
+	agent->new_name = NULL;
+
+	return 0;
+}
+
+static DBusMessage *session_get_capabilities(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	return not_supported(message);
+}
+
+static const GDBusMethodTable session_methods[] = {
+	{ GDBUS_ASYNC_METHOD("GetCapabilities",
+				NULL, GDBUS_ARGS({ "capabilities", "s" }),
+				session_get_capabilities) },
+	{ }
+};
+
+void manager_register_session(struct obex_session *os)
+{
+	char *path;
+
+	path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, os->id);
+
+	if (!g_dbus_register_interface(connection, path,
+				SESSION_INTERFACE,
+				session_methods, NULL,
+				session_properties, os, NULL))
+		error("Cannot register Session interface.");
+
+	g_free(path);
+}
+
+void manager_unregister_session(struct obex_session *os)
+{
+	char *path;
+
+	path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, os->id);
+
+	g_dbus_unregister_interface(connection, path, SESSION_INTERFACE);
+
+	g_free(path);
+}
+
+void manager_emit_transfer_progress(struct obex_transfer *transfer)
+{
+	emit_transfer_progress(transfer, transfer->session->size,
+						transfer->session->offset);
+}
+
+void manager_emit_transfer_completed(struct obex_transfer *transfer)
+{
+	struct obex_session *session;
+
+	if (transfer == NULL)
+		return;
+
+	session = transfer->session;
+
+	if (session == NULL || session->object == NULL)
+		return;
+
+	emit_transfer_completed(transfer, !session->aborted);
+}
+
+DBusConnection *manager_dbus_get_connection(void)
+{
+	if (connection == NULL)
+		return NULL;
+
+	return dbus_connection_ref(connection);
+}
diff --git a/bluez/repo/obexd/src/manager.h b/bluez/repo/obexd/src/manager.h
new file mode 100644
index 0000000..d9781b2
--- /dev/null
+++ b/bluez/repo/obexd/src/manager.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *
+ *
+ *  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 <dbus/dbus.h>
+
+#define OBEXD_SERVICE  "org.bluez.obex"
+
+struct obex_session;
+struct obex_transfer;
+
+void manager_register_session(struct obex_session *os);
+void manager_unregister_session(struct obex_session *os);
+
+struct obex_transfer *manager_register_transfer(struct obex_session *os);
+void manager_unregister_transfer(struct obex_transfer *transfer);
+void manager_emit_transfer_started(struct obex_transfer *transfer);
+void manager_emit_transfer_progress(struct obex_transfer *transfer);
+void manager_emit_transfer_completed(struct obex_transfer *transfer);
+int manager_request_authorization(struct obex_transfer *transfer,
+					char **new_folder, char **new_name);
+
+DBusConnection *manager_dbus_get_connection(void);
diff --git a/bluez/repo/obexd/src/map_ap.h b/bluez/repo/obexd/src/map_ap.h
new file mode 100644
index 0000000..da108fe
--- /dev/null
+++ b/bluez/repo/obexd/src/map_ap.h
@@ -0,0 +1,51 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+/* List of OBEX application parameters tags as per MAP specification. */
+enum map_ap_tag {
+	MAP_AP_MAXLISTCOUNT		= 0x01,		/* uint16_t	*/
+	MAP_AP_STARTOFFSET		= 0x02,		/* uint16_t	*/
+	MAP_AP_FILTERMESSAGETYPE	= 0x03,		/* uint8_t	*/
+	MAP_AP_FILTERPERIODBEGIN	= 0x04,		/* char *	*/
+	MAP_AP_FILTERPERIODEND		= 0x05,		/* char *	*/
+	MAP_AP_FILTERREADSTATUS		= 0x06,		/* uint8_t	*/
+	MAP_AP_FILTERRECIPIENT		= 0x07,		/* char *	*/
+	MAP_AP_FILTERORIGINATOR		= 0x08,		/* char *	*/
+	MAP_AP_FILTERPRIORITY		= 0x09,		/* uint8_t	*/
+	MAP_AP_ATTACHMENT		= 0x0A,		/* uint8_t	*/
+	MAP_AP_TRANSPARENT		= 0x0B,		/* uint8_t	*/
+	MAP_AP_RETRY			= 0x0C,		/* uint8_t	*/
+	MAP_AP_NEWMESSAGE		= 0x0D,		/* uint8_t	*/
+	MAP_AP_NOTIFICATIONSTATUS	= 0x0E,		/* uint8_t	*/
+	MAP_AP_MASINSTANCEID		= 0x0F,		/* uint8_t	*/
+	MAP_AP_PARAMETERMASK		= 0x10,		/* uint32_t	*/
+	MAP_AP_FOLDERLISTINGSIZE	= 0x11,		/* uint16_t	*/
+	MAP_AP_MESSAGESLISTINGSIZE	= 0x12,		/* uint16_t	*/
+	MAP_AP_SUBJECTLENGTH		= 0x13,		/* uint8_t	*/
+	MAP_AP_CHARSET			= 0x14,		/* uint8_t	*/
+	MAP_AP_FRACTIONREQUEST		= 0x15,		/* uint8_t	*/
+	MAP_AP_FRACTIONDELIVER		= 0x16,		/* uint8_t	*/
+	MAP_AP_STATUSINDICATOR		= 0x17,		/* uint8_t	*/
+	MAP_AP_STATUSVALUE		= 0x18,		/* uint8_t	*/
+	MAP_AP_MSETIME			= 0x19,		/* char *	*/
+};
diff --git a/bluez/repo/obexd/src/mimetype.c b/bluez/repo/obexd/src/mimetype.c
new file mode 100644
index 0000000..833ddc7
--- /dev/null
+++ b/bluez/repo/obexd/src/mimetype.c
@@ -0,0 +1,216 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "obex.h"
+#include "mimetype.h"
+
+static GSList *drivers = NULL;
+
+static GSList *watches = NULL;
+
+struct io_watch {
+	void *object;
+	obex_object_io_func func;
+	void *user_data;
+};
+
+void obex_object_set_io_flags(void *object, int flags, int err)
+{
+	GSList *l;
+
+	for (l = watches; l;) {
+		struct io_watch *watch = l->data;
+
+		l = l->next;
+
+		if (watch->object != object)
+			continue;
+
+		if (watch->func(object, flags, err, watch->user_data) == TRUE)
+			continue;
+
+		if (g_slist_find(watches, watch) == NULL)
+			continue;
+
+		watches = g_slist_remove(watches, watch);
+		g_free(watch);
+	}
+}
+
+static struct io_watch *find_io_watch(void *object)
+{
+	GSList *l;
+
+	for (l = watches; l; l = l->next) {
+		struct io_watch *watch = l->data;
+
+		if (watch->object == object)
+			return watch;
+	}
+
+	return NULL;
+}
+
+static void reset_io_watch(void *object)
+{
+	struct io_watch *watch;
+
+	watch = find_io_watch(object);
+	if (watch == NULL)
+		return;
+
+	watches = g_slist_remove(watches, watch);
+	g_free(watch);
+}
+
+static int set_io_watch(void *object, obex_object_io_func func,
+				void *user_data)
+{
+	struct io_watch *watch;
+
+	if (func == NULL) {
+		reset_io_watch(object);
+		return 0;
+	}
+
+	watch = find_io_watch(object);
+	if (watch)
+		return -EPERM;
+
+	watch = g_new0(struct io_watch, 1);
+	watch->object = object;
+	watch->func = func;
+	watch->user_data = user_data;
+
+	watches = g_slist_append(watches, watch);
+
+	return 0;
+}
+
+static struct obex_mime_type_driver *find_driver(const uint8_t *target,
+				unsigned int target_size,
+				const char *mimetype, const uint8_t *who,
+				unsigned int who_size)
+{
+	GSList *l;
+
+	for (l = drivers; l; l = l->next) {
+		struct obex_mime_type_driver *driver = l->data;
+
+		if (memncmp0(target, target_size, driver->target, driver->target_size))
+			continue;
+
+		if (memncmp0(who, who_size, driver->who, driver->who_size))
+			continue;
+
+		if (mimetype == NULL || driver->mimetype == NULL) {
+			if (mimetype == driver->mimetype)
+				return driver;
+			else
+				continue;
+		}
+
+		if (g_ascii_strcasecmp(mimetype, driver->mimetype) == 0)
+			return driver;
+	}
+
+	return NULL;
+}
+
+struct obex_mime_type_driver *obex_mime_type_driver_find(const uint8_t *target,
+				unsigned int target_size,
+				const char *mimetype, const uint8_t *who,
+				unsigned int who_size)
+{
+	struct obex_mime_type_driver *driver;
+
+	driver = find_driver(target, target_size, mimetype, who, who_size);
+	if (driver == NULL) {
+		if (who != NULL) {
+			/* Fallback to non-who specific */
+			driver = find_driver(target, target_size, mimetype, NULL, 0);
+			if (driver != NULL)
+				return driver;
+		}
+
+		if (mimetype != NULL)
+			/* Fallback to target default */
+			driver = find_driver(target, target_size, NULL, NULL, 0);
+
+		if (driver == NULL)
+			/* Fallback to general default */
+			driver = find_driver(NULL, 0, NULL, NULL, 0);
+	}
+
+	return driver;
+}
+
+int obex_mime_type_driver_register(struct obex_mime_type_driver *driver)
+{
+	if (!driver) {
+		error("Invalid driver");
+		return -EINVAL;
+	}
+
+	if (find_driver(driver->target, driver->target_size, driver->mimetype,
+					driver->who, driver->who_size)) {
+		error("Permission denied: %s could not be registered",
+				driver->mimetype);
+		return -EPERM;
+	}
+
+	if (driver->set_io_watch == NULL)
+		driver->set_io_watch = set_io_watch;
+
+	DBG("driver %p mimetype %s registered", driver, driver->mimetype);
+
+	drivers = g_slist_append(drivers, driver);
+
+	return 0;
+}
+
+void obex_mime_type_driver_unregister(struct obex_mime_type_driver *driver)
+{
+	if (!g_slist_find(drivers, driver)) {
+		error("Unable to unregister: No such driver %p", driver);
+		return;
+	}
+
+	DBG("driver %p mimetype %s unregistered", driver, driver->mimetype);
+
+	drivers = g_slist_remove(drivers, driver);
+}
diff --git a/bluez/repo/obexd/src/mimetype.h b/bluez/repo/obexd/src/mimetype.h
new file mode 100644
index 0000000..79529b8
--- /dev/null
+++ b/bluez/repo/obexd/src/mimetype.h
@@ -0,0 +1,55 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+typedef gboolean (*obex_object_io_func) (void *object, int flags, int err,
+							void *user_data);
+
+struct obex_mime_type_driver {
+	const uint8_t *target;
+	unsigned int target_size;
+	const char *mimetype;
+	const uint8_t *who;
+	unsigned int who_size;
+	void *(*open) (const char *name, int oflag, mode_t mode,
+			void *driver_data, size_t *size, int *err);
+	int (*close) (void *object);
+	ssize_t (*get_next_header)(void *object, void *buf, size_t mtu,
+								uint8_t *hi);
+	ssize_t (*read) (void *object, void *buf, size_t count);
+	ssize_t (*write) (void *object, const void *buf, size_t count);
+	int (*flush) (void *object);
+	int (*copy) (const char *name, const char *destname);
+	int (*move) (const char *name, const char *destname);
+	int (*remove) (const char *name);
+	int (*set_io_watch) (void *object, obex_object_io_func func,
+				void *user_data);
+};
+
+int obex_mime_type_driver_register(struct obex_mime_type_driver *driver);
+void obex_mime_type_driver_unregister(struct obex_mime_type_driver *driver);
+struct obex_mime_type_driver *obex_mime_type_driver_find(const uint8_t *target,
+				unsigned int target_size,
+				const char *mimetype, const uint8_t *who,
+				unsigned int who_size);
+
+void obex_object_set_io_flags(void *object, int flags, int err);
diff --git a/bluez/repo/obexd/src/obex-priv.h b/bluez/repo/obexd/src/obex-priv.h
new file mode 100644
index 0000000..355a7f8
--- /dev/null
+++ b/bluez/repo/obexd/src/obex-priv.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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
+ *
+ */
+
+struct obex_session {
+	GIOChannel *io;
+	uint32_t id;
+	uint8_t cmd;
+	uint8_t action_id;
+	char *src;
+	char *dst;
+	char *name;
+	char *destname;
+	char *type;
+	char *path;
+	time_t time;
+	uint8_t *apparam;
+	size_t apparam_len;
+	const void *nonhdr;
+	size_t nonhdr_len;
+	guint get_rsp;
+	uint8_t *buf;
+	int64_t pending;
+	int64_t offset;
+	int64_t size;
+	void *object;
+	gboolean aborted;
+	int err;
+	struct obex_service_driver *service;
+	void *service_data;
+	struct obex_server *server;
+	gboolean checked;
+	GObex *obex;
+	struct obex_mime_type_driver *driver;
+	gboolean headers_sent;
+};
+
+int obex_session_start(GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu,
+				gboolean stream, struct obex_server *server);
diff --git a/bluez/repo/obexd/src/obex.c b/bluez/repo/obexd/src/obex.c
new file mode 100644
index 0000000..788bffc
--- /dev/null
+++ b/bluez/repo/obexd/src/obex.c
@@ -0,0 +1,1171 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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 <stdlib.h>
+#include <signal.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "gobex/gobex.h"
+
+#include "btio/btio.h"
+#include "obexd.h"
+#include "log.h"
+#include "obex.h"
+#include "obex-priv.h"
+#include "server.h"
+#include "manager.h"
+#include "mimetype.h"
+#include "service.h"
+#include "transport.h"
+
+static GSList *sessions = NULL;
+
+typedef struct {
+	uint8_t  version;
+	uint8_t  flags;
+	uint16_t mtu;
+} __attribute__ ((packed)) obex_connect_hdr_t;
+
+struct auth_header {
+	uint8_t tag;
+	uint8_t len;
+	uint8_t val[0];
+} __attribute__ ((packed));
+
+/* Possible commands */
+static struct {
+	int cmd;
+	const char *name;
+} obex_command[] = {
+	{ G_OBEX_OP_CONNECT,	"CONNECT"	},
+	{ G_OBEX_OP_DISCONNECT,	"DISCONNECT"	},
+	{ G_OBEX_OP_PUT,	"PUT"		},
+	{ G_OBEX_OP_GET,	"GET"		},
+	{ G_OBEX_OP_SETPATH,	"SETPATH"	},
+	{ G_OBEX_OP_SESSION,	"SESSION"	},
+	{ G_OBEX_OP_ABORT,	"ABORT"		},
+	{ G_OBEX_OP_ACTION,	"ACTION"	},
+	{ 0xFF,			NULL		},
+};
+
+/* Possible Response */
+static struct {
+	int rsp;
+	const char *name;
+} obex_response[] = {
+	{ G_OBEX_RSP_CONTINUE,			"CONTINUE"		},
+	{ G_OBEX_RSP_SUCCESS,			"SUCCESS"		},
+	{ G_OBEX_RSP_CREATED,			"CREATED"		},
+	{ G_OBEX_RSP_ACCEPTED,			"ACCEPTED"		},
+	{ G_OBEX_RSP_NON_AUTHORITATIVE,		"NON_AUTHORITATIVE"	},
+	{ G_OBEX_RSP_NO_CONTENT,		"NO_CONTENT"		},
+	{ G_OBEX_RSP_RESET_CONTENT,		"RESET_CONTENT"		},
+	{ G_OBEX_RSP_PARTIAL_CONTENT,		"PARTIAL_CONTENT"	},
+	{ G_OBEX_RSP_MULTIPLE_CHOICES,		"MULTIPLE_CHOICES"	},
+	{ G_OBEX_RSP_MOVED_PERMANENTLY,		"MOVED_PERMANENTLY"	},
+	{ G_OBEX_RSP_MOVED_TEMPORARILY,		"MOVED_TEMPORARILY"	},
+	{ G_OBEX_RSP_SEE_OTHER,			"SEE_OTHER"		},
+	{ G_OBEX_RSP_NOT_MODIFIED,		"NOT_MODIFIED"		},
+	{ G_OBEX_RSP_USE_PROXY,			"USE_PROXY"		},
+	{ G_OBEX_RSP_BAD_REQUEST,		"BAD_REQUEST"		},
+	{ G_OBEX_RSP_UNAUTHORIZED,		"UNAUTHORIZED"		},
+	{ G_OBEX_RSP_PAYMENT_REQUIRED,		"PAYMENT_REQUIRED"	},
+	{ G_OBEX_RSP_FORBIDDEN,			"FORBIDDEN"		},
+	{ G_OBEX_RSP_NOT_FOUND,			"NOT_FOUND"		},
+	{ G_OBEX_RSP_METHOD_NOT_ALLOWED,	"METHOD_NOT_ALLOWED"	},
+	{ G_OBEX_RSP_NOT_ACCEPTABLE,		"NOT_ACCEPTABLE"	},
+	{ G_OBEX_RSP_PROXY_AUTH_REQUIRED,	"PROXY_AUTH_REQUIRED"	},
+	{ G_OBEX_RSP_REQUEST_TIME_OUT,		"REQUEST_TIME_OUT"	},
+	{ G_OBEX_RSP_CONFLICT,			"CONFLICT"		},
+	{ G_OBEX_RSP_GONE,			"GONE"			},
+	{ G_OBEX_RSP_LENGTH_REQUIRED,		"LENGTH_REQUIRED"	},
+	{ G_OBEX_RSP_PRECONDITION_FAILED,	"PRECONDITION_FAILED"	},
+	{ G_OBEX_RSP_REQ_ENTITY_TOO_LARGE,	"REQ_ENTITY_TOO_LARGE"	},
+	{ G_OBEX_RSP_REQ_URL_TOO_LARGE,		"REQ_URL_TOO_LARGE"	},
+	{ G_OBEX_RSP_UNSUPPORTED_MEDIA_TYPE,	"UNSUPPORTED_MEDIA_TYPE"},
+	{ G_OBEX_RSP_INTERNAL_SERVER_ERROR,	"INTERNAL_SERVER_ERROR"	},
+	{ G_OBEX_RSP_NOT_IMPLEMENTED,		"NOT_IMPLEMENTED"	},
+	{ G_OBEX_RSP_BAD_GATEWAY,		"BAD_GATEWAY"		},
+	{ G_OBEX_RSP_SERVICE_UNAVAILABLE,	"SERVICE_UNAVAILABLE"	},
+	{ G_OBEX_RSP_GATEWAY_TIMEOUT,		"GATEWAY_TIMEOUT"	},
+	{ G_OBEX_RSP_VERSION_NOT_SUPPORTED,	"VERSION_NOT_SUPPORTED"	},
+	{ G_OBEX_RSP_DATABASE_FULL,		"DATABASE_FULL"		},
+	{ G_OBEX_RSP_DATABASE_LOCKED,		"DATABASE_LOCKED"	},
+	{ 0xFF,					NULL			},
+};
+
+static gboolean handle_async_io(void *object, int flags, int err,
+						void *user_data);
+
+static void print_event(int cmd, int rsp)
+{
+	const char *cmdstr = NULL, *rspstr = NULL;
+	int i;
+	static int lastcmd;
+
+	if (cmd < 0)
+		cmd = lastcmd;
+	else
+		lastcmd = cmd;
+
+	for (i = 0; obex_command[i].cmd != 0xFF; i++) {
+		if (obex_command[i].cmd != cmd)
+			continue;
+		cmdstr = obex_command[i].name;
+	}
+
+	for (i = 0; obex_response[i].rsp != 0xFF; i++) {
+		if (obex_response[i].rsp != rsp)
+			continue;
+		rspstr = obex_response[i].name;
+	}
+
+	obex_debug("%s(0x%x), %s(0x%x)", cmdstr, cmd, rspstr, rsp);
+}
+
+static void os_set_response(struct obex_session *os, int err)
+{
+	uint8_t rsp;
+
+	rsp = g_obex_errno_to_rsp(err);
+
+	print_event(-1, rsp);
+
+	g_obex_send_rsp(os->obex, rsp, NULL, G_OBEX_HDR_INVALID);
+}
+
+static void os_session_mark_aborted(struct obex_session *os)
+{
+	/* the session was already cancelled/aborted or size in unknown */
+	if (os->aborted || os->size == OBJECT_SIZE_UNKNOWN)
+		return;
+
+	os->aborted = (os->size != os->offset);
+}
+
+static void os_reset_session(struct obex_session *os)
+{
+	os_session_mark_aborted(os);
+
+	if (os->object) {
+		os->driver->set_io_watch(os->object, NULL, NULL);
+		os->driver->close(os->object);
+		if (os->aborted && os->cmd == G_OBEX_OP_PUT && os->path &&
+				os->driver->remove)
+			os->driver->remove(os->path);
+	}
+
+	if (os->service && os->service->reset)
+		os->service->reset(os, os->service_data);
+
+	if (os->name) {
+		g_free(os->name);
+		os->name = NULL;
+	}
+	if (os->type) {
+		g_free(os->type);
+		os->type = NULL;
+	}
+	if (os->buf) {
+		g_free(os->buf);
+		os->buf = NULL;
+	}
+	if (os->path) {
+		g_free(os->path);
+		os->path = NULL;
+	}
+	if (os->apparam) {
+		g_free(os->apparam);
+		os->apparam = NULL;
+		os->apparam_len = 0;
+	}
+
+	if (os->get_rsp > 0) {
+		g_obex_remove_request_function(os->obex, os->get_rsp);
+		os->get_rsp = 0;
+	}
+
+	os->object = NULL;
+	os->driver = NULL;
+	os->aborted = FALSE;
+	os->pending = 0;
+	os->offset = 0;
+	os->size = OBJECT_SIZE_DELETE;
+	os->headers_sent = FALSE;
+	os->checked = FALSE;
+}
+
+static void obex_session_free(struct obex_session *os)
+{
+	sessions = g_slist_remove(sessions, os);
+
+	if (os->io)
+		g_io_channel_unref(os->io);
+
+	if (os->obex)
+		g_obex_unref(os->obex);
+
+	g_free(os->src);
+	g_free(os->dst);
+
+	g_free(os);
+}
+
+/* From Imendio's GnomeVFS OBEX module (om-utils.c) */
+static time_t parse_iso8610(const char *val, int size)
+{
+	time_t time, tz_offset = 0;
+	struct tm tm;
+	char *date;
+	char tz;
+	int nr;
+
+	memset(&tm, 0, sizeof(tm));
+	/* According to spec the time doesn't have to be null terminated */
+	date = g_strndup(val, size);
+	nr = sscanf(date, "%04u%02u%02uT%02u%02u%02u%c",
+			&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+			&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+			&tz);
+	g_free(date);
+	if (nr < 6) {
+		/* Invalid time format */
+		return -1;
+	}
+
+	tm.tm_year -= 1900;	/* Year since 1900 */
+	tm.tm_mon--;		/* Months since January, values 0-11 */
+	tm.tm_isdst = -1;	/* Daylight savings information not avail */
+
+#if defined(HAVE_TM_GMTOFF)
+	tz_offset = tm.tm_gmtoff;
+#elif defined(HAVE_TIMEZONE)
+	tz_offset = -timezone;
+	if (tm.tm_isdst > 0)
+		tz_offset += 3600;
+#endif
+
+	time = mktime(&tm);
+	if (nr == 7) {
+		/*
+		 * Date/Time was in localtime (to remote device)
+		 * already. Since we don't know anything about the
+		 * timezone on that one we won't try to apply UTC offset
+		 */
+		time += tz_offset;
+	}
+
+	return time;
+}
+
+static void parse_service(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	const guint8 *target = NULL, *who = NULL;
+	gsize target_size = 0, who_size = 0;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_WHO);
+	if (hdr == NULL)
+		goto target;
+
+	g_obex_header_get_bytes(hdr, &who, &who_size);
+
+target:
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TARGET);
+	if (hdr == NULL)
+		goto probe;
+
+	g_obex_header_get_bytes(hdr, &target, &target_size);
+
+probe:
+	os->service = obex_service_driver_find(os->server->drivers,
+						target, target_size,
+						who, who_size);
+}
+
+static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data)
+{
+	struct obex_session *os = user_data;
+	GObexPacket *rsp;
+	GObexHeader *hdr;
+	int err;
+
+	DBG("");
+
+	print_event(G_OBEX_OP_CONNECT, -1);
+
+	parse_service(os, req);
+
+	if (os->service == NULL || os->service->connect == NULL) {
+		error("Connect attempt to a non-supported target");
+		os_set_response(os, -EPERM);
+		return;
+	}
+
+	DBG("Selected driver: %s", os->service->name);
+
+	os->service_data = os->service->connect(os, &err);
+	if (err < 0) {
+		os_set_response(os, err);
+		return;
+	}
+
+	os->cmd = G_OBEX_OP_CONNECT;
+
+	rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
+
+	if (os->service->target) {
+		hdr = g_obex_header_new_bytes(G_OBEX_HDR_WHO,
+						os->service->target,
+						os->service->target_size);
+		g_obex_packet_add_header(rsp, hdr);
+	}
+
+	g_obex_send(obex, rsp, NULL);
+
+	print_event(-1, 0);
+}
+
+static void cmd_disconnect(GObex *obex, GObexPacket *req, void *user_data)
+{
+	struct obex_session *os = user_data;
+
+	DBG("session %p", os);
+
+	print_event(G_OBEX_OP_DISCONNECT, -1);
+
+	os->cmd = G_OBEX_OP_DISCONNECT;
+
+	os_set_response(os, 0);
+}
+
+static ssize_t driver_write(struct obex_session *os)
+{
+	ssize_t len = 0;
+
+	while (os->pending > 0) {
+		ssize_t w;
+
+		w = os->driver->write(os->object, os->buf + len, os->pending);
+		if (w < 0) {
+			error("write(): %s (%zd)", strerror(-w), -w);
+			if (w == -EINTR)
+				continue;
+			else if (w == -EINVAL)
+				memmove(os->buf, os->buf + len, os->pending);
+
+			return w;
+		}
+
+		len += w;
+		os->offset += w;
+		os->pending -= w;
+	}
+
+	DBG("%zd written", len);
+
+	if (os->service->progress != NULL)
+		os->service->progress(os, os->service_data);
+
+	return len;
+}
+
+static gssize driver_read(struct obex_session *os, void *buf, gsize size)
+{
+	gssize len;
+
+	if (os->object == NULL)
+		return -EIO;
+
+	if (os->service->progress != NULL)
+		os->service->progress(os, os->service_data);
+
+	len = os->driver->read(os->object, buf, size);
+	if (len < 0) {
+		error("read(): %s (%zd)", strerror(-len), -len);
+		if (len == -ENOSTR)
+			return 0;
+		if (len == -EAGAIN)
+			os->driver->set_io_watch(os->object, handle_async_io,
+									os);
+	}
+
+	os->offset += len;
+
+	DBG("%zd read", len);
+
+	return len;
+}
+
+static gssize send_data(void *buf, gsize size, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+
+	DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object,
+									size);
+
+	if (os->aborted)
+		return os->err < 0 ? os->err : -EPERM;
+
+	return driver_read(os, buf, size);
+}
+
+static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+
+	DBG("");
+
+	if (err != NULL) {
+		error("transfer failed: %s\n", err->message);
+		goto reset;
+	}
+
+	if (os->object && os->driver && os->driver->flush) {
+		if (os->driver->flush(os->object) == -EAGAIN) {
+			g_obex_suspend(os->obex);
+			os->driver->set_io_watch(os->object, handle_async_io,
+									os);
+			return;
+		}
+	}
+
+reset:
+	os_reset_session(os);
+}
+
+static int driver_get_headers(struct obex_session *os)
+{
+	GObexPacket *rsp;
+	gssize len;
+	guint8 data[255];
+	guint8 id;
+	GObexHeader *hdr;
+
+	DBG("name=%s type=%s object=%p", os->name, os->type, os->object);
+
+	if (os->aborted)
+		return os->err < 0 ? os->err : -EPERM;
+
+	if (os->object == NULL)
+		return -EIO;
+
+	if (os->headers_sent)
+		return 0;
+
+	rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
+
+	if (os->driver->get_next_header == NULL)
+		goto done;
+
+	while ((len = os->driver->get_next_header(os->object, &data,
+							sizeof(data), &id))) {
+		if (len < 0) {
+			error("get_next_header(): %s (%zd)", strerror(-len),
+								-len);
+
+			g_obex_packet_free(rsp);
+
+			if (len == -EAGAIN)
+				return len;
+
+			g_free(os->buf);
+			os->buf = NULL;
+
+			return len;
+		}
+
+		hdr = g_obex_header_new_bytes(id, data, len);
+		g_obex_packet_add_header(rsp, hdr);
+	}
+
+done:
+	if (os->size != OBJECT_SIZE_UNKNOWN && os->size < UINT32_MAX) {
+		hdr = g_obex_header_new_uint32(G_OBEX_HDR_LENGTH, os->size);
+		g_obex_packet_add_header(rsp, hdr);
+	}
+
+	g_obex_get_rsp_pkt(os->obex, rsp, send_data, transfer_complete, os,
+									NULL);
+
+	os->headers_sent = TRUE;
+
+	print_event(-1, G_OBEX_RSP_CONTINUE);
+
+	return 0;
+}
+
+static gboolean handle_async_io(void *object, int flags, int err,
+						void *user_data)
+{
+	struct obex_session *os = user_data;
+
+	if (err < 0)
+		goto done;
+
+	if (flags & G_IO_OUT)
+		err = driver_write(os);
+	if ((flags & G_IO_IN) && !os->headers_sent)
+		err = driver_get_headers(os);
+
+	if (err == -EAGAIN)
+		return TRUE;
+
+done:
+	if (err < 0) {
+		os->err = err;
+		os->aborted = TRUE;
+	}
+
+	g_obex_resume(os->obex);
+
+	return FALSE;
+}
+
+static gboolean recv_data(const void *buf, gsize size, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+	ssize_t ret;
+
+	DBG("name=%s type=%s file=%p size=%zu", os->name, os->type, os->object,
+									size);
+
+	if (os->aborted)
+		return FALSE;
+
+	/* workaround: client didn't send the object length */
+	if (os->size == OBJECT_SIZE_DELETE)
+		os->size = OBJECT_SIZE_UNKNOWN;
+
+	os->buf = g_realloc(os->buf, os->pending + size);
+	memcpy(os->buf + os->pending, buf, size);
+	os->pending += size;
+
+	/* only write if both object and driver are valid */
+	if (os->object == NULL || os->driver == NULL) {
+		DBG("Stored %" PRIu64 " bytes into temporary buffer",
+								os->pending);
+		return TRUE;
+	}
+
+	ret = driver_write(os);
+	if (ret >= 0)
+		return TRUE;
+
+	if (ret == -EAGAIN) {
+		g_obex_suspend(os->obex);
+		os->driver->set_io_watch(os->object, handle_async_io, os);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void parse_type(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	const guint8 *type;
+	gsize len;
+
+	g_free(os->type);
+	os->type = NULL;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TYPE);
+	if (hdr == NULL)
+		goto probe;
+
+	if (!g_obex_header_get_bytes(hdr, &type, &len))
+		goto probe;
+
+	/* Ensure null termination */
+	if (type[len - 1] != '\0')
+		goto probe;
+
+	os->type = g_strndup((const char *) type, len);
+	DBG("TYPE: %s", os->type);
+
+probe:
+	os->driver = obex_mime_type_driver_find(os->service->target,
+						os->service->target_size,
+						os->type,
+						os->service->who,
+						os->service->who_size);
+}
+
+static void parse_name(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	const char *name;
+
+	g_free(os->name);
+	os->name = NULL;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_NAME);
+	if (hdr == NULL)
+		return;
+
+	if (!g_obex_header_get_unicode(hdr, &name))
+		return;
+
+	os->name = g_strdup(name);
+	DBG("NAME: %s", os->name);
+}
+
+static void parse_apparam(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	const guint8 *apparam;
+	gsize len;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_APPARAM);
+	if (hdr == NULL)
+		return;
+
+	if (!g_obex_header_get_bytes(hdr, &apparam, &len))
+		return;
+
+	os->apparam = g_memdup(apparam, len);
+	os->apparam_len = len;
+	DBG("APPARAM");
+}
+
+static void cmd_get(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+	int err;
+
+	DBG("session %p", os);
+
+	print_event(G_OBEX_OP_GET, -1);
+
+	if (os->service == NULL) {
+		os_set_response(os, -EPERM);
+		return;
+	}
+
+	if (os->service->get == NULL) {
+		os_set_response(os, -ENOSYS);
+		return;
+	}
+
+	os->headers_sent = FALSE;
+
+	if (os->type) {
+		g_free(os->type);
+		os->type = NULL;
+	}
+
+	parse_type(os, req);
+
+	if (!os->driver) {
+		error("No driver found");
+		os_set_response(os, -ENOSYS);
+		return;
+	}
+
+	os->cmd = G_OBEX_OP_GET;
+
+	parse_name(os, req);
+
+	parse_apparam(os, req);
+
+	err = os->service->get(os, os->service_data);
+	if (err == 0)
+		return;
+
+	os_set_response(os, err);
+}
+
+static void cmd_setpath(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+	int err;
+
+	DBG("");
+
+	print_event(G_OBEX_OP_SETPATH, -1);
+
+	if (os->service == NULL) {
+		err = -EPERM;
+		goto done;
+	}
+
+	if (os->service->setpath == NULL) {
+		err = -ENOSYS;
+		goto done;
+	}
+
+	os->cmd = G_OBEX_OP_SETPATH;
+
+	parse_name(os, req);
+
+	os->nonhdr = g_obex_packet_get_data(req, &os->nonhdr_len);
+
+	err = os->service->setpath(os, os->service_data);
+done:
+	os_set_response(os, err);
+}
+
+int obex_get_stream_start(struct obex_session *os, const char *filename)
+{
+	int err;
+	void *object;
+	size_t size = OBJECT_SIZE_UNKNOWN;
+
+	object = os->driver->open(filename, O_RDONLY, 0, os->service_data,
+								&size, &err);
+	if (object == NULL) {
+		error("open(%s): %s (%d)", filename, strerror(-err), -err);
+		return err;
+	}
+
+	os->object = object;
+	os->offset = 0;
+	os->size = size;
+
+	err = driver_get_headers(os);
+	if (err != -EAGAIN)
+		return err;
+
+	g_obex_suspend(os->obex);
+	os->driver->set_io_watch(os->object, handle_async_io, os);
+	return 0;
+}
+
+int obex_put_stream_start(struct obex_session *os, const char *filename)
+{
+	int err;
+
+	os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC,
+					0600, os->service_data,
+					os->size != OBJECT_SIZE_UNKNOWN ?
+					(size_t *) &os->size : NULL, &err);
+	if (os->object == NULL) {
+		error("open(%s): %s (%d)", filename, strerror(-err), -err);
+		return err;
+	}
+
+	os->path = g_strdup(filename);
+
+	return 0;
+}
+
+static void parse_length(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	guint32 size;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_LENGTH);
+	if (hdr == NULL)
+		return;
+
+	if (!g_obex_header_get_uint32(hdr, &size))
+		return;
+
+	os->size = size;
+	DBG("LENGTH: %" PRIu64, os->size);
+}
+
+static void parse_time(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	const guint8 *time;
+	gsize len;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TIME);
+	if (hdr == NULL)
+		return;
+
+
+	if (!g_obex_header_get_bytes(hdr, &time, &len))
+		return;
+
+	os->time = parse_iso8610((const char *) time, len);
+	DBG("TIME: %s", ctime(&os->time));
+}
+
+static gboolean check_put(GObex *obex, GObexPacket *req, void *user_data)
+{
+	struct obex_session *os = user_data;
+	int ret;
+
+	if (os->service->chkput == NULL)
+		goto done;
+
+	ret = os->service->chkput(os, os->service_data);
+	switch (ret) {
+	case 0:
+		break;
+	case -EAGAIN:
+		g_obex_suspend(os->obex);
+		os->driver->set_io_watch(os->object, handle_async_io, os);
+		return TRUE;
+	default:
+		os_set_response(os, ret);
+		return FALSE;
+	}
+
+	if (os->size == OBJECT_SIZE_DELETE || os->size == OBJECT_SIZE_UNKNOWN)
+		DBG("Got a PUT without a Length");
+
+done:
+	os->checked = TRUE;
+
+	return TRUE;
+}
+
+static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+	int err;
+
+	DBG("");
+
+	print_event(G_OBEX_OP_PUT, -1);
+
+	if (os->service == NULL) {
+		os_set_response(os, -EPERM);
+		return;
+	}
+
+	parse_type(os, req);
+
+	if (os->driver == NULL) {
+		error("No driver found");
+		os_set_response(os, -ENOSYS);
+		return;
+	}
+
+	os->cmd = G_OBEX_OP_PUT;
+
+	/* Set size to unknown if a body header exists */
+	if (g_obex_packet_get_body(req))
+		os->size = OBJECT_SIZE_UNKNOWN;
+
+	parse_name(os, req);
+	parse_length(os, req);
+	parse_time(os, req);
+	parse_apparam(os, req);
+
+	if (!os->checked) {
+		if (!check_put(obex, req, user_data))
+			return;
+	}
+
+	if (os->service->put == NULL) {
+		os_set_response(os, -ENOSYS);
+		return;
+	}
+
+	err = os->service->put(os, os->service_data);
+	if (err == 0) {
+		g_obex_put_rsp(obex, req, recv_data, transfer_complete, os,
+						NULL, G_OBEX_HDR_INVALID);
+		print_event(G_OBEX_OP_PUT, G_OBEX_RSP_CONTINUE);
+		return;
+	}
+
+	os_set_response(os, err);
+}
+
+static void parse_destname(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	const char *destname;
+
+	g_free(os->destname);
+	os->destname = NULL;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_DESTNAME);
+	if (hdr == NULL)
+		return;
+
+	if (!g_obex_header_get_unicode(hdr, &destname))
+		return;
+
+	os->destname = g_strdup(destname);
+	DBG("DESTNAME: %s", os->destname);
+}
+
+static void parse_action(struct obex_session *os, GObexPacket *req)
+{
+	GObexHeader *hdr;
+	guint8 id;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_ACTION);
+	if (hdr == NULL)
+		return;
+
+	if (!g_obex_header_get_uint8(hdr, &id))
+		return;
+
+	os->action_id = id;
+	DBG("ACTION: 0x%02x", os->action_id);
+}
+
+static void cmd_action(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+	int err;
+
+	DBG("");
+
+	print_event(G_OBEX_OP_ACTION, -1);
+
+	if (os->service == NULL) {
+		err = -EPERM;
+		goto done;
+	}
+
+	if (os->service->action == NULL) {
+		err = -ENOSYS;
+		goto done;
+	}
+
+	os->cmd = G_OBEX_OP_ACTION;
+
+	parse_name(os, req);
+	parse_destname(os, req);
+	parse_action(os, req);
+
+	os->driver = obex_mime_type_driver_find(os->service->target,
+						os->service->target_size,
+						NULL,
+						os->service->who,
+						os->service->who_size);
+	if (os->driver == NULL) {
+		err = -ENOSYS;
+		goto done;
+	}
+
+	err = os->service->action(os, os->service_data);
+done:
+	os_set_response(os, err);
+}
+
+static void cmd_abort(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+
+	DBG("");
+
+	print_event(G_OBEX_OP_ABORT, -1);
+
+	os_reset_session(os);
+
+	os_set_response(os, 0);
+}
+
+static void obex_session_destroy(struct obex_session *os)
+{
+	DBG("");
+
+	os_reset_session(os);
+
+	if (os->service && os->service->disconnect)
+		os->service->disconnect(os, os->service_data);
+
+	obex_session_free(os);
+}
+
+static void disconn_func(GObex *obex, GError *err, gpointer user_data)
+{
+	struct obex_session *os = user_data;
+
+	error("disconnected: %s\n", err ? err->message : "<no err>");
+	obex_session_destroy(os);
+}
+
+int obex_session_start(GIOChannel *io, uint16_t tx_mtu, uint16_t rx_mtu,
+				gboolean stream, struct obex_server *server)
+{
+	struct obex_session *os;
+	GObex *obex;
+	GObexTransportType type;
+	static uint32_t id = 0;
+
+	DBG("");
+
+	os = g_new0(struct obex_session, 1);
+	os->id = ++id;
+
+	os->service = obex_service_driver_find(server->drivers, NULL,
+							0, NULL, 0);
+	os->server = server;
+	os->size = OBJECT_SIZE_DELETE;
+
+	type = stream ? G_OBEX_TRANSPORT_STREAM : G_OBEX_TRANSPORT_PACKET;
+
+	obex = g_obex_new(io, type, rx_mtu, tx_mtu);
+	if (!obex) {
+		obex_session_free(os);
+		return -EIO;
+	}
+
+	g_obex_set_disconnect_function(obex, disconn_func, os);
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT, cmd_connect, os);
+	g_obex_add_request_function(obex, G_OBEX_OP_DISCONNECT, cmd_disconnect,
+									os);
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, cmd_put, os);
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, cmd_get, os);
+	g_obex_add_request_function(obex, G_OBEX_OP_SETPATH, cmd_setpath, os);
+	g_obex_add_request_function(obex, G_OBEX_OP_ACTION, cmd_action, os);
+	g_obex_add_request_function(obex, G_OBEX_OP_ABORT, cmd_abort, os);
+
+	os->obex = obex;
+	os->io = g_io_channel_ref(io);
+
+	obex_getsockname(os, &os->src);
+	obex_getpeername(os, &os->dst);
+
+	sessions = g_slist_prepend(sessions, os);
+
+	return 0;
+}
+
+const char *obex_get_name(struct obex_session *os)
+{
+	return os->name;
+}
+
+const char *obex_get_destname(struct obex_session *os)
+{
+	return os->destname;
+}
+
+void obex_set_name(struct obex_session *os, const char *name)
+{
+	g_free(os->name);
+	os->name = g_strdup(name);
+	DBG("Name changed: %s", os->name);
+}
+
+ssize_t obex_get_size(struct obex_session *os)
+{
+	return os->size;
+}
+
+const char *obex_get_type(struct obex_session *os)
+{
+	return os->type;
+}
+
+int obex_remove(struct obex_session *os, const char *path)
+{
+	if (os->driver == NULL)
+		return -ENOSYS;
+
+	return os->driver->remove(path);
+}
+
+int obex_copy(struct obex_session *os, const char *source,
+						const char *destination)
+{
+	if (os->driver == NULL || os->driver->copy == NULL)
+		return -ENOSYS;
+
+	DBG("%s %s", source, destination);
+
+	return os->driver->copy(source, destination);
+}
+
+int obex_move(struct obex_session *os, const char *source,
+						const char *destination)
+{
+	if (os->driver == NULL || os->driver->move == NULL)
+		return -ENOSYS;
+
+	DBG("%s %s", source, destination);
+
+	return os->driver->move(source, destination);
+}
+
+uint8_t obex_get_action_id(struct obex_session *os)
+{
+	return os->action_id;
+}
+
+ssize_t obex_get_apparam(struct obex_session *os, const uint8_t **buffer)
+{
+	*buffer = os->apparam;
+
+	return os->apparam_len;
+}
+
+ssize_t obex_get_non_header_data(struct obex_session *os,
+							const uint8_t **data)
+{
+	*data = os->nonhdr;
+
+	return os->nonhdr_len;
+}
+
+int obex_getpeername(struct obex_session *os, char **name)
+{
+	struct obex_transport_driver *transport = os->server->transport;
+
+	if (transport == NULL || transport->getpeername == NULL)
+		return -ENOTSUP;
+
+	return transport->getpeername(os->io, name);
+}
+
+int obex_getsockname(struct obex_session *os, char **name)
+{
+	struct obex_transport_driver *transport = os->server->transport;
+
+	if (transport == NULL || transport->getsockname == NULL)
+		return -ENOTSUP;
+
+	return transport->getsockname(os->io, name);
+}
+
+int memncmp0(const void *a, size_t na, const void *b, size_t nb)
+{
+	if (na != nb)
+		return na - nb;
+
+	if (a == NULL)
+		return -(a != b);
+
+	if (b == NULL)
+		return a != b;
+
+	return memcmp(a, b, na);
+}
diff --git a/bluez/repo/obexd/src/obex.h b/bluez/repo/obexd/src/obex.h
new file mode 100644
index 0000000..fc16747
--- /dev/null
+++ b/bluez/repo/obexd/src/obex.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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
+ *
+ */
+
+#define OBJECT_SIZE_UNKNOWN -1
+#define OBJECT_SIZE_DELETE -2
+
+#define TARGET_SIZE 16
+
+struct obex_session;
+
+int obex_get_stream_start(struct obex_session *os, const char *filename);
+int obex_put_stream_start(struct obex_session *os, const char *filename);
+const char *obex_get_name(struct obex_session *os);
+const char *obex_get_destname(struct obex_session *os);
+void obex_set_name(struct obex_session *os, const char *name);
+ssize_t obex_get_size(struct obex_session *os);
+const char *obex_get_type(struct obex_session *os);
+int obex_remove(struct obex_session *os, const char *path);
+int obex_copy(struct obex_session *os, const char *source,
+						const char *destination);
+int obex_move(struct obex_session *os, const char *source,
+						const char *destination);
+uint8_t obex_get_action_id(struct obex_session *os);
+ssize_t obex_get_apparam(struct obex_session *os, const uint8_t **buffer);
+ssize_t obex_get_non_header_data(struct obex_session *os,
+							const uint8_t **data);
+int obex_getpeername(struct obex_session *os, char **name);
+int obex_getsockname(struct obex_session *os, char **name);
+
+/* Just a thin wrapper around memcmp to deal with NULL values */
+int memncmp0(const void *a, size_t na, const void *b, size_t nb);
diff --git a/bluez/repo/obexd/src/obex.service.in b/bluez/repo/obexd/src/obex.service.in
new file mode 100644
index 0000000..bca3aef
--- /dev/null
+++ b/bluez/repo/obexd/src/obex.service.in
@@ -0,0 +1,10 @@
+[Unit]
+Description=Bluetooth OBEX service
+
+[Service]
+Type=dbus
+BusName=org.bluez.obex
+ExecStart=@libexecdir@/obexd
+
+[Install]
+Alias=dbus-org.bluez.obex.service
diff --git a/bluez/repo/obexd/src/obexd.h b/bluez/repo/obexd/src/obexd.h
new file mode 100644
index 0000000..42c3c4d
--- /dev/null
+++ b/bluez/repo/obexd/src/obexd.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+#define OBEX_OPP	(1 << 1)
+#define OBEX_FTP	(1 << 2)
+#define OBEX_BIP	(1 << 3)
+#define OBEX_PBAP	(1 << 4)
+#define OBEX_IRMC	(1 << 5)
+#define OBEX_PCSUITE	(1 << 6)
+#define OBEX_SYNCEVOLUTION	(1 << 7)
+#define OBEX_MAS	(1 << 8)
+#define OBEX_MNS	(1 << 9)
+
+gboolean plugin_init(const char *pattern, const char *exclude);
+void plugin_cleanup(void);
+
+gboolean manager_init(void);
+void manager_cleanup(void);
+
+gboolean obex_option_auto_accept(void);
+const char *obex_option_root_folder(void);
+gboolean obex_option_symlinks(void);
+const char *obex_option_capability(void);
diff --git a/bluez/repo/obexd/src/org.bluez.obex.service b/bluez/repo/obexd/src/org.bluez.obex.service
new file mode 100644
index 0000000..a538088
--- /dev/null
+++ b/bluez/repo/obexd/src/org.bluez.obex.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.bluez.obex
+Exec=/bin/false
+SystemdService=dbus-org.bluez.obex.service
diff --git a/bluez/repo/obexd/src/plugin.c b/bluez/repo/obexd/src/plugin.c
new file mode 100644
index 0000000..7d971b6
--- /dev/null
+++ b/bluez/repo/obexd/src/plugin.c
@@ -0,0 +1,207 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <errno.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "obexd.h"
+#include "plugin.h"
+#include "log.h"
+
+/*
+ * Plugins that are using libraries with threads and their own mainloop
+ * will crash on exit. This is a bug inside these libraries, but there is
+ * nothing much that can be done about it. One bad example is libebook.
+ */
+#ifdef NEED_THREADS
+#define PLUGINFLAG (RTLD_NOW | RTLD_NODELETE)
+#else
+#define PLUGINFLAG (RTLD_NOW)
+#endif
+
+static GSList *plugins = NULL;
+
+struct obex_plugin {
+	void *handle;
+	struct obex_plugin_desc *desc;
+};
+
+static gboolean add_plugin(void *handle, struct obex_plugin_desc *desc)
+{
+	struct obex_plugin *plugin;
+
+	if (desc->init == NULL)
+		return FALSE;
+
+	plugin = g_try_new0(struct obex_plugin, 1);
+	if (plugin == NULL)
+		return FALSE;
+
+	plugin->handle = handle;
+	plugin->desc = desc;
+
+	if (desc->init() < 0) {
+		g_free(plugin);
+		return FALSE;
+	}
+
+	plugins = g_slist_append(plugins, plugin);
+	DBG("Plugin %s loaded", desc->name);
+
+	return TRUE;
+}
+
+static gboolean check_plugin(struct obex_plugin_desc *desc,
+				char **patterns, char **excludes)
+{
+	if (excludes) {
+		for (; *excludes; excludes++)
+			if (g_pattern_match_simple(*excludes, desc->name))
+				break;
+		if (*excludes) {
+			info("Excluding %s", desc->name);
+			return FALSE;
+		}
+	}
+
+	if (patterns) {
+		for (; *patterns; patterns++)
+			if (g_pattern_match_simple(*patterns, desc->name))
+				break;
+		if (*patterns == NULL) {
+			info("Ignoring %s", desc->name);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+
+#include "builtin.h"
+
+gboolean plugin_init(const char *pattern, const char *exclude)
+{
+	char **patterns = NULL;
+	char **excludes = NULL;
+	GDir *dir;
+	const char *file;
+	unsigned int i;
+
+	if (strlen(PLUGINDIR) == 0)
+		return FALSE;
+
+	if (pattern)
+		patterns = g_strsplit_set(pattern, ":, ", -1);
+
+	if (exclude)
+		excludes = g_strsplit_set(exclude, ":, ", -1);
+
+	DBG("Loading builtin plugins");
+
+	for (i = 0; __obex_builtin[i]; i++) {
+		if (check_plugin(__obex_builtin[i],
+					patterns, excludes) == FALSE)
+			continue;
+
+		add_plugin(NULL,  __obex_builtin[i]);
+	}
+
+	DBG("Loading plugins %s", PLUGINDIR);
+
+	dir = g_dir_open(PLUGINDIR, 0, NULL);
+	if (!dir)
+		return FALSE;
+
+	while ((file = g_dir_read_name(dir)) != NULL) {
+		struct obex_plugin_desc *desc;
+		void *handle;
+		char *filename;
+
+		if (g_str_has_prefix(file, "lib") == TRUE ||
+				g_str_has_suffix(file, ".so") == FALSE)
+			continue;
+
+		filename = g_build_filename(PLUGINDIR, file, NULL);
+
+		handle = dlopen(filename, PLUGINFLAG);
+		if (handle == NULL) {
+			error("Can't load plugin %s: %s", filename,
+								dlerror());
+			g_free(filename);
+			continue;
+		}
+
+		g_free(filename);
+
+		desc = dlsym(handle, "obex_plugin_desc");
+		if (desc == NULL) {
+			error("Can't load plugin description: %s", dlerror());
+			dlclose(handle);
+			continue;
+		}
+
+		if (check_plugin(desc, patterns, excludes) == FALSE) {
+			dlclose(handle);
+			continue;
+		}
+
+		if (add_plugin(handle, desc) == FALSE)
+			dlclose(handle);
+	}
+
+	g_dir_close(dir);
+	g_strfreev(patterns);
+	g_strfreev(excludes);
+
+	return TRUE;
+}
+
+void plugin_cleanup(void)
+{
+	GSList *list;
+
+	DBG("Cleanup plugins");
+
+	for (list = plugins; list; list = list->next) {
+		struct obex_plugin *plugin = list->data;
+
+		if (plugin->desc->exit)
+			plugin->desc->exit();
+
+		if (plugin->handle != NULL)
+			dlclose(plugin->handle);
+
+		g_free(plugin);
+	}
+
+	g_slist_free(plugins);
+}
diff --git a/bluez/repo/obexd/src/plugin.h b/bluez/repo/obexd/src/plugin.h
new file mode 100644
index 0000000..13d7769
--- /dev/null
+++ b/bluez/repo/obexd/src/plugin.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+struct obex_plugin_desc {
+	const char *name;
+	int (*init) (void);
+	void (*exit) (void);
+};
+
+#ifdef OBEX_PLUGIN_BUILTIN
+#define OBEX_PLUGIN_DEFINE(name, init, exit) \
+		struct obex_plugin_desc __obex_builtin_ ## name = { \
+			#name, init, exit \
+		};
+#else
+#define OBEX_PLUGIN_DEFINE(name,init,exit) \
+		extern struct obex_plugin_desc obex_plugin_desc \
+				__attribute__ ((visibility("default"))); \
+		struct obex_plugin_desc obex_plugin_desc = { \
+			#name, init, exit \
+		};
+#endif
diff --git a/bluez/repo/obexd/src/server.c b/bluez/repo/obexd/src/server.c
new file mode 100644
index 0000000..db85423
--- /dev/null
+++ b/bluez/repo/obexd/src/server.c
@@ -0,0 +1,128 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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 <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "gobex/gobex.h"
+
+#include "log.h"
+#include "obex.h"
+#include "obex-priv.h"
+#include "server.h"
+#include "service.h"
+#include "transport.h"
+
+static GSList *servers = NULL;
+
+static void init_server(uint16_t service, GSList *transports)
+{
+	GSList *l;
+
+	for (l = transports; l; l = l->next) {
+		struct obex_transport_driver *transport = l->data;
+		struct obex_server *server;
+		int err;
+
+		if (transport->service != 0 &&
+				(transport->service & service) == FALSE)
+			continue;
+
+		server = g_new0(struct obex_server, 1);
+		server->transport = transport;
+		server->drivers = obex_service_driver_list(service);
+
+		server->transport_data = transport->start(server, &err);
+		if (server->transport_data == NULL) {
+			DBG("Unable to start %s transport: %s (%d)",
+					transport->name, strerror(err), err);
+			g_free(server);
+			continue;
+		}
+
+		servers = g_slist_prepend(servers, server);
+	}
+}
+
+int obex_server_init(void)
+{
+	GSList *drivers;
+	GSList *transports;
+	GSList *l;
+
+	drivers = obex_service_driver_list(0);
+	if (drivers == NULL) {
+		DBG("No service driver registered");
+		return -EINVAL;
+	}
+
+	transports = obex_transport_driver_list();
+	if (transports == NULL) {
+		DBG("No transport driver registered");
+		return -EINVAL;
+	}
+
+	for (l = drivers; l; l = l->next) {
+		struct obex_service_driver *driver = l->data;
+
+		init_server(driver->service, transports);
+	}
+
+	return 0;
+}
+
+void obex_server_exit(void)
+{
+	GSList *l;
+
+	for (l = servers; l; l = l->next) {
+		struct obex_server *server = l->data;
+
+		server->transport->stop(server->transport_data);
+		g_slist_free(server->drivers);
+		g_free(server);
+	}
+
+	g_slist_free(servers);
+
+	return;
+}
+
+int obex_server_new_connection(struct obex_server *server, GIOChannel *io,
+					uint16_t tx_mtu, uint16_t rx_mtu,
+					gboolean stream)
+{
+	return obex_session_start(io, tx_mtu, rx_mtu, stream, server);
+}
diff --git a/bluez/repo/obexd/src/server.h b/bluez/repo/obexd/src/server.h
new file mode 100644
index 0000000..278c35f
--- /dev/null
+++ b/bluez/repo/obexd/src/server.h
@@ -0,0 +1,37 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2010  Nokia Corporation
+ *  Copyright (C) 2007-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
+ *
+ */
+
+struct obex_server {
+	struct obex_transport_driver *transport;
+	void *transport_data;
+	GSList *drivers;
+};
+
+int obex_server_init(void);
+
+void obex_server_exit(void);
+
+int obex_server_new_connection(struct obex_server *server, GIOChannel *io,
+					uint16_t tx_mtu, uint16_t rx_mtu,
+					gboolean stream);
diff --git a/bluez/repo/obexd/src/service.c b/bluez/repo/obexd/src/service.c
new file mode 100644
index 0000000..c088535
--- /dev/null
+++ b/bluez/repo/obexd/src/service.c
@@ -0,0 +1,132 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "obex.h"
+#include "service.h"
+#include "log.h"
+
+static GSList *drivers = NULL;
+
+struct obex_service_driver *obex_service_driver_find(GSList *drivers,
+			const uint8_t *target, unsigned int target_size,
+			const uint8_t *who, unsigned int who_size)
+{
+	GSList *l;
+
+	for (l = drivers; l; l = l->next) {
+		struct obex_service_driver *driver = l->data;
+
+		/* who is optional, so only check for it if not NULL */
+		if (who != NULL && memncmp0(who, who_size, driver->who,
+							driver->who_size))
+			continue;
+
+		if (memncmp0(target, target_size, driver->target,
+						driver->target_size) == 0)
+			return driver;
+	}
+
+	return NULL;
+}
+
+GSList *obex_service_driver_list(uint16_t services)
+{
+	GSList *l;
+	GSList *list = NULL;
+
+	if (services == 0)
+		return drivers;
+
+	for (l = drivers; l && services; l = l->next) {
+		struct obex_service_driver *driver = l->data;
+
+		if (driver->service & services) {
+			list = g_slist_append(list, driver);
+			services &= ~driver->service;
+		}
+	}
+
+	return list;
+}
+
+static struct obex_service_driver *find_driver(uint16_t service)
+{
+	GSList *l;
+
+	for (l = drivers; l; l = l->next) {
+		struct obex_service_driver *driver = l->data;
+
+		if (driver->service == service)
+			return driver;
+	}
+
+	return NULL;
+}
+
+int obex_service_driver_register(struct obex_service_driver *driver)
+{
+	if (!driver) {
+		error("Invalid driver");
+		return -EINVAL;
+	}
+
+	if (find_driver(driver->service)) {
+		error("Permission denied: service %s already registered",
+			driver->name);
+		return -EPERM;
+	}
+
+	DBG("driver %p service %s registered", driver, driver->name);
+
+	/* Drivers that support who has priority */
+	if (driver->who)
+		drivers = g_slist_prepend(drivers, driver);
+	else
+		drivers = g_slist_append(drivers, driver);
+
+	return 0;
+}
+
+void obex_service_driver_unregister(struct obex_service_driver *driver)
+{
+	if (!g_slist_find(drivers, driver)) {
+		error("Unable to unregister: No such driver %p", driver);
+		return;
+	}
+
+	DBG("driver %p service %s unregistered", driver, driver->name);
+
+	drivers = g_slist_remove(drivers, driver);
+}
diff --git a/bluez/repo/obexd/src/service.h b/bluez/repo/obexd/src/service.h
new file mode 100644
index 0000000..5d9d325
--- /dev/null
+++ b/bluez/repo/obexd/src/service.h
@@ -0,0 +1,53 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+#define OBEX_PORT_RANDOM UINT16_MAX
+
+struct obex_service_driver {
+	const char *name;
+	uint16_t service;
+	uint8_t channel;
+	uint16_t port;
+	gboolean secure;
+	const uint8_t *target;
+	unsigned int target_size;
+	const uint8_t *who;
+	unsigned int who_size;
+	const char *record;
+	void *(*connect) (struct obex_session *os, int *err);
+	void (*progress) (struct obex_session *os, void *user_data);
+	int (*get) (struct obex_session *os, void *user_data);
+	int (*put) (struct obex_session *os, void *user_data);
+	int (*chkput) (struct obex_session *os, void *user_data);
+	int (*setpath) (struct obex_session *os, void *user_data);
+	int (*action) (struct obex_session *os, void *user_data);
+	void (*disconnect) (struct obex_session *os, void *user_data);
+	void (*reset) (struct obex_session *os, void *user_data);
+};
+
+int obex_service_driver_register(struct obex_service_driver *driver);
+void obex_service_driver_unregister(struct obex_service_driver *driver);
+GSList *obex_service_driver_list(uint16_t services);
+struct obex_service_driver *obex_service_driver_find(GSList *drivers,
+			const uint8_t *target, unsigned int target_size,
+			const uint8_t *who, unsigned int who_size);
diff --git a/bluez/repo/obexd/src/transport.c b/bluez/repo/obexd/src/transport.c
new file mode 100644
index 0000000..4984643
--- /dev/null
+++ b/bluez/repo/obexd/src/transport.c
@@ -0,0 +1,93 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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 <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "obex.h"
+#include "server.h"
+#include "transport.h"
+#include "log.h"
+
+static GSList *drivers = NULL;
+
+static struct obex_transport_driver *obex_transport_driver_find(
+							const char *name)
+{
+	GSList *l;
+
+	for (l = drivers; l; l = l->next) {
+		struct obex_transport_driver *driver = l->data;
+
+		if (g_strcmp0(name, driver->name) == 0)
+			return driver;
+	}
+
+	return NULL;
+}
+
+GSList *obex_transport_driver_list(void)
+{
+	return drivers;
+}
+
+int obex_transport_driver_register(struct obex_transport_driver *driver)
+{
+	if (!driver) {
+		error("Invalid driver");
+		return -EINVAL;
+	}
+
+	if (obex_transport_driver_find(driver->name) != NULL) {
+		error("Permission denied: transport %s already registered",
+			driver->name);
+		return -EPERM;
+	}
+
+	DBG("driver %p transport %s registered", driver, driver->name);
+
+	drivers = g_slist_prepend(drivers, driver);
+
+	return 0;
+}
+
+void obex_transport_driver_unregister(struct obex_transport_driver *driver)
+{
+	if (!g_slist_find(drivers, driver)) {
+		error("Unable to unregister: No such driver %p", driver);
+		return;
+	}
+
+	DBG("driver %p transport %s unregistered", driver, driver->name);
+
+	drivers = g_slist_remove(drivers, driver);
+}
diff --git a/bluez/repo/obexd/src/transport.h b/bluez/repo/obexd/src/transport.h
new file mode 100644
index 0000000..97e10d0
--- /dev/null
+++ b/bluez/repo/obexd/src/transport.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-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
+ *
+ */
+
+struct obex_transport_driver {
+	const char *name;
+	uint16_t service;
+	void *(*start) (struct obex_server *server, int *err);
+	int (*getpeername) (GIOChannel *io, char **name);
+	int (*getsockname) (GIOChannel *io, char **name);
+	void (*stop) (void *data);
+};
+
+int obex_transport_driver_register(struct obex_transport_driver *driver);
+void obex_transport_driver_unregister(struct obex_transport_driver *driver);
+GSList *obex_transport_driver_list(void);
diff --git a/bluez/repo/peripheral/._attach.c b/bluez/repo/peripheral/._attach.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._attach.c
Binary files differ
diff --git a/bluez/repo/peripheral/._attach.h b/bluez/repo/peripheral/._attach.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._attach.h
Binary files differ
diff --git a/bluez/repo/peripheral/._efivars.c b/bluez/repo/peripheral/._efivars.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._efivars.c
Binary files differ
diff --git a/bluez/repo/peripheral/._efivars.h b/bluez/repo/peripheral/._efivars.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._efivars.h
Binary files differ
diff --git a/bluez/repo/peripheral/._gap.c b/bluez/repo/peripheral/._gap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._gap.c
Binary files differ
diff --git a/bluez/repo/peripheral/._gap.h b/bluez/repo/peripheral/._gap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._gap.h
Binary files differ
diff --git a/bluez/repo/peripheral/._gatt.c b/bluez/repo/peripheral/._gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._gatt.c
Binary files differ
diff --git a/bluez/repo/peripheral/._gatt.h b/bluez/repo/peripheral/._gatt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._gatt.h
Binary files differ
diff --git a/bluez/repo/peripheral/._log.c b/bluez/repo/peripheral/._log.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._log.c
Binary files differ
diff --git a/bluez/repo/peripheral/._log.h b/bluez/repo/peripheral/._log.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._log.h
Binary files differ
diff --git a/bluez/repo/peripheral/._main.c b/bluez/repo/peripheral/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/peripheral/._main.c
Binary files differ
diff --git a/bluez/repo/peripheral/attach.c b/bluez/repo/peripheral/attach.c
new file mode 100644
index 0000000..a70ca55
--- /dev/null
+++ b/bluez/repo/peripheral/attach.c
@@ -0,0 +1,144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <sys/ioctl.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "tools/hciattach.h"
+#include "peripheral/attach.h"
+
+static const char *serial_dev = "/dev/ttyS1";
+static int serial_fd = -1;
+
+static int open_serial(const char *path)
+{
+	struct termios ti;
+	int fd, saved_ldisc, ldisc = N_HCI;
+
+	fd = open(path, O_RDWR | O_NOCTTY);
+	if (fd < 0) {
+		perror("Failed to open serial port");
+		return -1;
+	}
+
+	if (tcflush(fd, TCIOFLUSH) < 0) {
+		perror("Failed to flush serial port");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) {
+		perror("Failed get serial line discipline");
+		close(fd);
+		return -1;
+	}
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= (B115200 | CLOCAL | CREAD);
+
+	/* Set flow control */
+	ti.c_cflag |= CRTSCTS;
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		perror("Failed to set serial port settings");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
+		perror("Failed set serial line discipline");
+		close(fd);
+		return -1;
+	}
+
+	printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc);
+
+	return fd;
+}
+
+static int attach_proto(const char *path, unsigned int proto,
+						unsigned int flags)
+{
+	int fd, dev_id;
+
+	fd = open_serial(path);
+	if (fd < 0)
+		return -1;
+
+	if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+		perror("Failed to set flags");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) {
+		perror("Failed to set protocol");
+		close(fd);
+		return -1;
+	}
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE);
+	if (dev_id < 0) {
+		perror("Failed to get device id");
+		close(fd);
+		return -1;
+	}
+
+	printf("Device index %d attached\n", dev_id);
+
+	return fd;
+}
+
+void attach_start(void)
+{
+	unsigned long flags;
+
+	if (serial_fd >= 0)
+		return;
+
+	printf("Attaching BR/EDR controller to %s\n", serial_dev);
+
+	flags = (1 << HCI_UART_RESET_ON_INIT);
+
+	serial_fd = attach_proto(serial_dev, HCI_UART_H4, flags);
+}
+
+void attach_stop(void)
+{
+	if (serial_fd < 0)
+		return;
+
+	close(serial_fd);
+	serial_fd = -1;
+}
diff --git a/bluez/repo/peripheral/attach.h b/bluez/repo/peripheral/attach.h
new file mode 100644
index 0000000..f76e2fb
--- /dev/null
+++ b/bluez/repo/peripheral/attach.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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
+ *
+ */
+
+void attach_start(void);
+void attach_stop(void);
diff --git a/bluez/repo/peripheral/efivars.c b/bluez/repo/peripheral/efivars.c
new file mode 100644
index 0000000..a86031f
--- /dev/null
+++ b/bluez/repo/peripheral/efivars.c
@@ -0,0 +1,132 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+
+#include "peripheral/efivars.h"
+
+#define SYSFS_EFIVARS "/sys/firmware/efi/efivars"
+
+typedef struct {
+    uint32_t data1;
+    uint16_t data2;
+    uint16_t data3;
+    uint8_t  data4[8];
+} efi_guid_t;
+
+#define VENDOR_GUID \
+	(efi_guid_t) { 0xd5f9d775, 0x1a09, 0x4e89, \
+			{ 0x96, 0xcf, 0x1d, 0x19, 0x55, 0x4d, 0xa6, 0x67 } }
+
+static void efivars_pathname(const char *name, char *pathname, size_t size)
+{
+	static efi_guid_t guid = VENDOR_GUID;
+
+	snprintf(pathname, size - 1,
+		"%s/%s-%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		SYSFS_EFIVARS, name, guid.data1, guid.data2, guid.data3,
+		guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3],
+		guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7]);
+}
+
+int efivars_read(const char *name, uint32_t *attributes,
+					void *data, size_t size)
+{
+	char pathname[PATH_MAX];
+	struct iovec iov[2];
+	uint32_t attr;
+	ssize_t len;
+	int fd;
+
+	efivars_pathname(name, pathname, PATH_MAX);
+
+	fd = open(pathname, O_RDONLY | O_CLOEXEC);
+	if (fd < 0)
+		return -EIO;
+
+	iov[0].iov_base = &attr;
+	iov[0].iov_len = sizeof(attr);
+	iov[1].iov_base = data;
+	iov[1].iov_len = size;
+
+	len = readv(fd, iov, 2);
+
+	close(fd);
+
+	if (len < 0)
+		return -EIO;
+
+	if (attributes)
+		*attributes = attr;
+
+	return 0;
+}
+
+int efivars_write(const char *name, uint32_t attributes,
+					const void *data, size_t size)
+{
+	char pathname[PATH_MAX];
+	void *buf;
+	ssize_t written;
+	int fd;
+
+	efivars_pathname(name, pathname, PATH_MAX);
+
+	buf = malloc(size + sizeof(attributes));
+	if (!buf)
+		return -ENOMEM;
+
+	fd = open(pathname, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+				S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if (fd < 0) {
+		free(buf);
+		return -EIO;
+	}
+
+	memcpy(buf, &attributes, sizeof(attributes));
+	memcpy(buf + sizeof(attributes), data, size);
+
+	written = write(fd, buf, size + sizeof(attributes));
+
+	close(fd);
+	free(buf);
+
+	if (written < 0)
+		return -EIO;
+
+	return 0;
+}
diff --git a/bluez/repo/peripheral/efivars.h b/bluez/repo/peripheral/efivars.h
new file mode 100644
index 0000000..430d143
--- /dev/null
+++ b/bluez/repo/peripheral/efivars.h
@@ -0,0 +1,33 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 EFIVARS_NON_VOLATILE			0x00000001
+#define EFIVARS_BOOTSERVICE_ACCESS		0x00000002
+#define EFIVARS_RUNTIME_ACCESS			0x00000004
+#define EFIVARS_HARDWARE_ERROR_RECORD		0x00000008
+#define EFIVARS_AUTHENTICATED_WRITE_ACCESS	0x00000010
+
+int efivars_read(const char *name, uint32_t *attributes,
+					void *data, size_t size);
+int efivars_write(const char *name, uint32_t attributes,
+					const void *data, size_t size);
diff --git a/bluez/repo/peripheral/gap.c b/bluez/repo/peripheral/gap.c
new file mode 100644
index 0000000..f659f7f
--- /dev/null
+++ b/bluez/repo/peripheral/gap.c
@@ -0,0 +1,551 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <string.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "peripheral/gatt.h"
+#include "peripheral/gap.h"
+
+static struct mgmt *mgmt = NULL;
+static uint16_t mgmt_index = MGMT_INDEX_NONE;
+
+static bool adv_features = false;
+static bool adv_instances = false;
+static bool require_connectable = true;
+
+static uint8_t static_addr[6] = { 0x90, 0x78, 0x56, 0x34, 0x12, 0xc0 };
+static uint8_t dev_name[260] = { 0x00, };
+static uint8_t dev_name_len = 0;
+
+void gap_set_static_address(uint8_t addr[6])
+{
+	memcpy(static_addr, addr, sizeof(static_addr));
+
+	printf("Using static address %02x:%02x:%02x:%02x:%02x:%02x\n",
+			static_addr[5], static_addr[4], static_addr[3],
+			static_addr[2], static_addr[1], static_addr[0]);
+}
+
+static void clear_long_term_keys(uint16_t index)
+{
+	struct mgmt_cp_load_long_term_keys cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.key_count = cpu_to_le16(0);
+
+	mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void clear_identity_resolving_keys(uint16_t index)
+{
+	struct mgmt_cp_load_irks cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.irk_count = cpu_to_le16(0);
+
+	mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void add_advertising(uint16_t index)
+{
+	const char ad[] = { 0x11, 0x15,
+			0xd0, 0x00, 0x2d, 0x12, 0x1e, 0x4b, 0x0f, 0xa4,
+			0x99, 0x4e, 0xce, 0xb5, 0x31, 0xf4, 0x05, 0x79 };
+	struct mgmt_cp_add_advertising *cp;
+	void *buf;
+
+	buf = malloc(sizeof(*cp) + sizeof(ad));
+	if (!buf)
+		return;
+
+	memset(buf, 0, sizeof(*cp) + sizeof(ad));
+	cp = buf;
+	cp->instance = 0x01;
+	cp->flags = cpu_to_le32((1 << 0) | (1 << 1) | (1 << 4));
+	cp->duration = cpu_to_le16(0);
+	cp->timeout = cpu_to_le16(0);
+	cp->adv_data_len = sizeof(ad);
+	cp->scan_rsp_len = 0;
+	memcpy(cp->data, ad, sizeof(ad));
+
+	mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index,
+			sizeof(*cp) + sizeof(ad), buf, NULL, NULL, NULL);
+
+	free(buf);
+}
+
+static void enable_advertising(uint16_t index)
+{
+	uint8_t val;
+
+	val = require_connectable ? 0x01 : 0x00;
+	mgmt_send(mgmt, MGMT_OP_SET_CONNECTABLE, index, 1, &val,
+						NULL, NULL, NULL);
+
+	val = 0x01;
+	mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
+						NULL, NULL, NULL);
+
+	if (adv_instances) {
+		add_advertising(index);
+		return;
+	}
+
+	val = require_connectable ? 0x01 : 0x02;
+	mgmt_send(mgmt, MGMT_OP_SET_ADVERTISING, index, 1, &val,
+						NULL, NULL, NULL);
+}
+
+static void new_settings_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("New settings\n");
+}
+
+static void local_name_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Local name changed\n");
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("New long term key\n");
+}
+
+static void device_connected_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Device connected\n");
+}
+
+static void device_disconnected_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Device disconnected\n");
+}
+
+static void user_confirm_request_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("User confirm request\n");
+}
+
+static void user_passkey_request_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("User passkey request\n");
+}
+
+static void auth_failed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Authentication failed\n");
+}
+
+static void device_unpaired_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Device unpaired\n");
+}
+
+static void passkey_notify_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Passkey notification\n");
+}
+
+static void new_irk_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("New identify resolving key\n");
+}
+
+static void new_csrk_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("New connection signature resolving key\n");
+}
+
+static void new_conn_param_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("New connection parameter\n");
+}
+
+static void advertising_added_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Advertising added\n");
+}
+
+static void advertising_removed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Advertising removed\n");
+}
+
+static void read_adv_features_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_adv_features *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t flags;
+
+	flags = le32_to_cpu(rp->supported_flags);
+
+	if (rp->max_instances > 0) {
+		adv_instances = true;
+
+		if (flags & (1 << 0))
+			require_connectable = false;
+	} else
+		require_connectable = false;
+
+	enable_advertising(index);
+}
+
+static void read_info_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t required_settings = MGMT_SETTING_LE |
+					MGMT_SETTING_STATIC_ADDRESS;
+	uint32_t supported_settings, current_settings;
+	uint8_t val;
+
+	required_settings = MGMT_SETTING_LE;
+
+	if (status) {
+		fprintf(stderr, "Reading info for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		return;
+	}
+
+	if (mgmt_index != MGMT_INDEX_NONE)
+		return;
+
+	supported_settings = le32_to_cpu(rp->supported_settings);
+	current_settings = le32_to_cpu(rp->current_settings);
+
+	if ((supported_settings & required_settings) != required_settings)
+		return;
+
+	printf("Selecting index %u\n", index);
+	mgmt_index = index;
+
+	mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index,
+					new_settings_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index,
+					local_name_changed_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_LONG_TERM_KEY, index,
+					new_long_term_key_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index,
+					device_connected_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index,
+					device_disconnected_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index,
+					user_confirm_request_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index,
+					user_passkey_request_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index,
+					auth_failed_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DEVICE_UNPAIRED, index,
+					device_unpaired_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index,
+					passkey_notify_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_IRK, index,
+					new_irk_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_CSRK, index,
+					new_csrk_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_CONN_PARAM, index,
+					new_conn_param_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index,
+					advertising_added_event, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index,
+					advertising_removed_event, NULL, NULL);
+
+	dev_name_len = snprintf((char *) dev_name, 26, "BlueZ Peripheral");
+
+	if (current_settings & MGMT_SETTING_POWERED) {
+		val = 0x00;
+		mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
+							NULL, NULL, NULL);
+	}
+
+	if (!(current_settings & MGMT_SETTING_LE)) {
+		val = 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
+							NULL, NULL, NULL);
+	}
+
+	if (current_settings & MGMT_SETTING_BREDR) {
+		val = 0x00;
+		mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
+							NULL, NULL, NULL);
+	}
+
+	if ((supported_settings & MGMT_SETTING_SECURE_CONN) &&
+			!(current_settings & MGMT_SETTING_SECURE_CONN)) {
+		val = 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+							NULL, NULL, NULL);
+	}
+
+	if (current_settings & MGMT_SETTING_DEBUG_KEYS) {
+		val = 0x00;
+		mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+							NULL, NULL, NULL);
+	}
+
+	if (!(current_settings & MGMT_SETTING_BONDABLE)) {
+		val = 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val,
+							NULL, NULL, NULL);
+	}
+
+	clear_long_term_keys(mgmt_index);
+	clear_identity_resolving_keys(mgmt_index);
+
+	mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
+					6, static_addr, NULL, NULL, NULL);
+
+	mgmt_send(mgmt, MGMT_OP_SET_LOCAL_NAME, index,
+					260, dev_name, NULL, NULL, NULL);
+
+	gatt_set_static_address(static_addr);
+	gatt_set_device_name(dev_name, dev_name_len);
+	gatt_server_start();
+
+	if (adv_features)
+		mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL,
+						read_adv_features_complete,
+						UINT_TO_PTR(index), NULL);
+	else
+		enable_advertising(index);
+}
+
+static void read_index_list_complete(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));
+		return;
+	}
+
+	count = le16_to_cpu(rp->num_controllers);
+
+	printf("Index list: %u\n", count);
+
+	for (i = 0; i < count; i++) {
+		uint16_t index = cpu_to_le16(rp->index[i]);
+
+		mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+				read_info_complete, UINT_TO_PTR(index), NULL);
+	}
+}
+
+static void index_added_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Index added\n");
+
+	if (mgmt_index != MGMT_INDEX_NONE)
+		return;
+
+	mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+				read_info_complete, UINT_TO_PTR(index), NULL);
+}
+
+static void index_removed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	printf("Index removed\n");
+
+	if (mgmt_index != index)
+		return;
+
+	mgmt_index = MGMT_INDEX_NONE;
+}
+
+static void read_ext_index_list_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_ext_index_list *rp = param;
+	uint16_t count;
+	int i;
+
+	if (status) {
+		fprintf(stderr, "Reading extended index list failed: %s\n",
+						mgmt_errstr(status));
+		return;
+	}
+
+	count = le16_to_cpu(rp->num_controllers);
+
+	printf("Extended index list: %u\n", count);
+
+	for (i = 0; i < count; i++) {
+		uint16_t index = cpu_to_le16(rp->entry[i].index);
+
+		if (rp->entry[i].type != 0x00)
+			continue;
+
+		mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+				read_info_complete, UINT_TO_PTR(index), NULL);
+	}
+}
+
+static void ext_index_added_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_ext_index_added *ev = param;
+
+	printf("Extended index added: %u\n", ev->type);
+
+	if (mgmt_index != MGMT_INDEX_NONE)
+		return;
+
+	if (ev->type != 0x00)
+		return;
+
+	mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+				read_info_complete, UINT_TO_PTR(index), NULL);
+}
+
+static void ext_index_removed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_ext_index_added *ev = param;
+
+	printf("Extended index removed: %u\n", ev->type);
+
+	if (mgmt_index != index)
+		return;
+
+	if (ev->type != 0x00)
+		return;
+
+	mgmt_index = MGMT_INDEX_NONE;
+}
+
+static void read_commands_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_commands *rp = param;
+	const uint16_t *opcode;
+	uint16_t num_commands;
+	bool ext_index_list = false;
+	int i;
+
+	if (status) {
+		fprintf(stderr, "Reading index list failed: %s\n",
+						mgmt_errstr(status));
+		return;
+	}
+
+	num_commands = le16_to_cpu(rp->num_commands);
+	opcode = rp->opcodes;
+
+	for (i = 0; i < num_commands; i++) {
+		uint16_t op = get_le16(opcode++);
+
+		if (op == MGMT_OP_READ_EXT_INDEX_LIST)
+			ext_index_list = true;
+		else if (op == MGMT_OP_READ_ADV_FEATURES)
+			adv_features = true;
+	}
+
+	if (ext_index_list) {
+		mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, MGMT_INDEX_NONE,
+					ext_index_added_event, NULL, NULL);
+		mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, MGMT_INDEX_NONE,
+					ext_index_removed_event, NULL, NULL);
+
+		if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_ext_index_list_complete, NULL, NULL)) {
+			fprintf(stderr, "Failed to read extended index list\n");
+			return;
+		}
+	} else {
+		mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_event, NULL, NULL);
+		mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_event, NULL, NULL);
+
+		if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_index_list_complete, NULL, NULL)) {
+			fprintf(stderr, "Failed to read index list\n");
+			return;
+		}
+	}
+}
+
+void gap_start(void)
+{
+	mgmt = mgmt_new_default();
+	if (!mgmt) {
+		fprintf(stderr, "Failed to open management socket\n");
+		return;
+	}
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_COMMANDS,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_commands_complete, NULL, NULL)) {
+		fprintf(stderr, "Failed to read supported commands\n");
+		return;
+	}
+}
+
+void gap_stop(void)
+{
+	if (!mgmt)
+		return;
+
+	gatt_server_stop();
+
+        mgmt_unref(mgmt);
+	mgmt = NULL;
+
+	mgmt_index = MGMT_INDEX_NONE;
+}
diff --git a/bluez/repo/peripheral/gap.h b/bluez/repo/peripheral/gap.h
new file mode 100644
index 0000000..6d67378
--- /dev/null
+++ b/bluez/repo/peripheral/gap.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <stdint.h>
+
+void gap_set_static_address(uint8_t addr[6]);
+
+void gap_start(void);
+void gap_stop(void);
diff --git a/bluez/repo/peripheral/gatt.c b/bluez/repo/peripheral/gatt.c
new file mode 100644
index 0000000..4c5531d
--- /dev/null
+++ b/bluez/repo/peripheral/gatt.c
@@ -0,0 +1,318 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <string.h>
+#include <sys/epoll.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/uuid.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-client.h"
+#include "peripheral/gatt.h"
+
+#define ATT_CID 4
+
+#define UUID_GAP 0x1800
+
+struct gatt_conn {
+	struct bt_att *att;
+	struct bt_gatt_server *gatt;
+	struct bt_gatt_client *client;
+};
+
+static int att_fd = -1;
+static struct queue *conn_list = NULL;
+static struct gatt_db *gatt_db = NULL;
+static struct gatt_db *gatt_cache = NULL;
+
+static uint8_t static_addr[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static uint8_t dev_name[20];
+static uint8_t dev_name_len = 0;
+
+void gatt_set_static_address(uint8_t addr[6])
+{
+	memcpy(static_addr, addr, sizeof(static_addr));
+}
+
+void gatt_set_device_name(uint8_t name[20], uint8_t len)
+{
+	memcpy(dev_name, name, sizeof(dev_name));
+	dev_name_len = len;
+}
+
+static void gatt_conn_destroy(void *data)
+{
+	struct gatt_conn *conn = data;
+
+	bt_gatt_client_unref(conn->client);
+	bt_gatt_server_unref(conn->gatt);
+	bt_att_unref(conn->att);
+
+	free(conn);
+}
+
+static void gatt_conn_disconnect(int err, void *user_data)
+{
+	struct gatt_conn *conn = user_data;
+
+	printf("Device disconnected: %s\n", strerror(err));
+
+	queue_remove(conn_list, conn);
+	gatt_conn_destroy(conn);
+}
+
+static void client_ready_callback(bool success, uint8_t att_ecode,
+							void *user_data)
+{
+	printf("GATT client discovery complete\n");
+}
+
+static void client_service_changed_callback(uint16_t start_handle,
+						uint16_t end_handle,
+						void *user_data)
+{
+	printf("GATT client service changed notification\n");
+}
+
+static struct gatt_conn *gatt_conn_new(int fd)
+{
+	struct gatt_conn *conn;
+	uint16_t mtu = 0;
+
+	conn = new0(struct gatt_conn, 1);
+	if (!conn)
+		return NULL;
+
+	conn->att = bt_att_new(fd, false);
+	if (!conn->att) {
+		fprintf(stderr, "Failed to initialze ATT transport layer\n");
+		free(conn);
+		return NULL;
+	}
+
+	bt_att_set_close_on_unref(conn->att, true);
+	bt_att_register_disconnect(conn->att, gatt_conn_disconnect, conn, NULL);
+
+	bt_att_set_security(conn->att, BT_SECURITY_MEDIUM);
+
+	conn->gatt = bt_gatt_server_new(gatt_db, conn->att, mtu);
+	if (!conn->gatt) {
+		fprintf(stderr, "Failed to create GATT server\n");
+		bt_att_unref(conn->att);
+		free(conn);
+		return NULL;
+	}
+
+	conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu);
+	if (!conn->gatt) {
+		fprintf(stderr, "Failed to create GATT client\n");
+		bt_gatt_server_unref(conn->gatt);
+		bt_att_unref(conn->att);
+		free(conn);
+		return NULL;
+	}
+
+	bt_gatt_client_set_ready_handler(conn->client,
+				client_ready_callback, conn, NULL);
+	bt_gatt_client_set_service_changed(conn->client,
+				client_service_changed_callback, conn, NULL);
+
+	return conn;
+}
+
+static void att_conn_callback(int fd, uint32_t events, void *user_data)
+{
+	struct gatt_conn *conn;
+	struct sockaddr_l2 addr;
+	socklen_t addrlen;
+	int new_fd;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addrlen = sizeof(addr);
+
+	new_fd = accept(att_fd, (struct sockaddr *) &addr, &addrlen);
+	if (new_fd < 0) {
+		fprintf(stderr, "Failed to accept new ATT connection: %m\n");
+		return;
+	}
+
+	conn = gatt_conn_new(new_fd);
+	if (!conn) {
+		fprintf(stderr, "Failed to create GATT connection\n");
+		close(new_fd);
+		return;
+	}
+
+	if (!queue_push_tail(conn_list, conn)) {
+		fprintf(stderr, "Failed to add GATT connection\n");
+		gatt_conn_destroy(conn);
+		close(new_fd);
+	}
+
+	printf("New device connected\n");
+}
+
+static void gap_device_name_read(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	uint8_t error;
+	const uint8_t *value;
+	size_t len;
+
+	if (offset > dev_name_len) {
+		error = BT_ATT_ERROR_INVALID_OFFSET;
+		value = NULL;
+		len = dev_name_len;
+	} else {
+		error = 0;
+		len = dev_name_len - offset;
+		value = len ? &dev_name[offset] : NULL;
+	}
+
+	gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void populate_gap_service(struct gatt_db *db)
+{
+	struct gatt_db_attribute *service;
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, UUID_GAP);
+	service = gatt_db_add_service(db, &uuid, true, 6);
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+	gatt_db_service_add_characteristic(service, &uuid,
+					BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ,
+					gap_device_name_read, NULL, NULL);
+
+	gatt_db_service_set_active(service, true);
+}
+
+static void populate_devinfo_service(struct gatt_db *db)
+{
+	struct gatt_db_attribute *service;
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, 0x180a);
+	service = gatt_db_add_service(db, &uuid, true, 17);
+
+	gatt_db_service_set_active(service, true);
+}
+
+void gatt_server_start(void)
+{
+	struct sockaddr_l2 addr;
+
+	if (att_fd >= 0)
+		return;
+
+	att_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_CLOEXEC,
+							BTPROTO_L2CAP);
+	if (att_fd < 0) {
+		fprintf(stderr, "Failed to create ATT server socket: %m\n");
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_cid = htobs(ATT_CID);
+	memcpy(&addr.l2_bdaddr, static_addr, 6);
+	addr.l2_bdaddr_type = BDADDR_LE_RANDOM;
+
+	if (bind(att_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		fprintf(stderr, "Failed to bind ATT server socket: %m\n");
+		close(att_fd);
+		att_fd = -1;
+		return;
+	}
+
+	if (listen(att_fd, 1) < 0) {
+		fprintf(stderr, "Failed to listen on ATT server socket: %m\n");
+		close(att_fd);
+		att_fd = -1;
+		return;
+	}
+
+	gatt_db = gatt_db_new();
+	if (!gatt_db) {
+		close(att_fd);
+		att_fd = -1;
+		return;
+	}
+
+	populate_gap_service(gatt_db);
+	populate_devinfo_service(gatt_db);
+
+	gatt_cache = gatt_db_new();
+
+	conn_list = queue_new();
+	if (!conn_list) {
+		gatt_db_unref(gatt_db);
+		gatt_db = NULL;
+		close(att_fd);
+		att_fd = -1;
+		return;
+	}
+
+	mainloop_add_fd(att_fd, EPOLLIN, att_conn_callback, NULL, NULL);
+}
+
+void gatt_server_stop(void)
+{
+	if (att_fd < 0)
+		return;
+
+	mainloop_remove_fd(att_fd);
+
+	queue_destroy(conn_list, gatt_conn_destroy);
+
+	gatt_db_unref(gatt_cache);
+	gatt_cache = NULL;
+
+	gatt_db_unref(gatt_db);
+	gatt_db = NULL;
+
+	close(att_fd);
+	att_fd = -1;
+}
diff --git a/bluez/repo/peripheral/gatt.h b/bluez/repo/peripheral/gatt.h
new file mode 100644
index 0000000..5b68f35
--- /dev/null
+++ b/bluez/repo/peripheral/gatt.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <stdint.h>
+
+void gatt_set_static_address(uint8_t addr[6]);
+void gatt_set_device_name(uint8_t name[20], uint8_t len);
+
+void gatt_server_start(void);
+void gatt_server_stop(void);
diff --git a/bluez/repo/peripheral/log.c b/bluez/repo/peripheral/log.c
new file mode 100644
index 0000000..7aaeb4d
--- /dev/null
+++ b/bluez/repo/peripheral/log.c
@@ -0,0 +1,56 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <fcntl.h>
+#include <unistd.h>
+
+#include "peripheral/log.h"
+
+static int kmsg_fd = -1;
+
+void log_open(void)
+{
+	if (kmsg_fd >= 0)
+		return;
+
+	kmsg_fd = open("/dev/kmsg", O_WRONLY | O_NOCTTY | O_CLOEXEC);
+	if (kmsg_fd < 0) {
+		fprintf(stderr, "Failed to open kernel logging: %m\n");
+		return;
+	}
+}
+
+void log_close(void)
+{
+	if (kmsg_fd < 0)
+		return;
+
+	close(kmsg_fd);
+	kmsg_fd = -1;
+}
diff --git a/bluez/repo/peripheral/log.h b/bluez/repo/peripheral/log.h
new file mode 100644
index 0000000..36619b3
--- /dev/null
+++ b/bluez/repo/peripheral/log.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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
+ *
+ */
+
+void log_open(void);
+void log_close(void);
diff --git a/bluez/repo/peripheral/main.c b/bluez/repo/peripheral/main.c
new file mode 100644
index 0000000..d7e10f3
--- /dev/null
+++ b/bluez/repo/peripheral/main.c
@@ -0,0 +1,247 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  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 <string.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+#include "src/shared/mainloop.h"
+#include "peripheral/efivars.h"
+#include "peripheral/attach.h"
+#include "peripheral/gap.h"
+#include "peripheral/log.h"
+
+static bool is_init = false;
+static pid_t shell_pid = -1;
+
+static const struct {
+	const char *target;
+	const char *linkpath;
+} dev_table[] = {
+	{ "/proc/self/fd",	"/dev/fd"	},
+	{ "/proc/self/fd/0",	"/dev/stdin"	},
+	{ "/proc/self/fd/1",	"/dev/stdout"	},
+	{ "/proc/self/fd/2",	"/dev/stderr"	},
+	{ }
+};
+
+static const struct {
+	const char *fstype;
+	const char *target;
+	const char *options;
+	unsigned long flags;
+} mount_table[] = {
+	{ "sysfs",    "/sys",		NULL,	MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "proc",     "/proc",		NULL,	MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "devtmpfs", "/dev",		NULL,	MS_NOSUID|MS_STRICTATIME },
+	{ "efivarfs", "/sys/firmware/efi/efivars",
+					NULL,	MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "pstore",   "/sys/fs/pstore",	NULL,	MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ }
+};
+
+static void prepare_filesystem(void)
+{
+	int i;
+
+	if (!is_init)
+		return;
+
+	for (i = 0; mount_table[i].fstype; i++) {
+		struct stat st;
+
+		if (lstat(mount_table[i].target, &st) < 0) {
+			printf("Creating %s\n", mount_table[i].target);
+			mkdir(mount_table[i].target, 0755);
+		}
+
+		printf("Mounting %s to %s\n", mount_table[i].fstype,
+						mount_table[i].target);
+
+		if (mount(mount_table[i].fstype,
+				mount_table[i].target,
+				mount_table[i].fstype,
+				mount_table[i].flags, NULL) < 0)
+			perror("Failed to mount filesystem");
+	}
+
+	for (i = 0; dev_table[i].target; i++) {
+		printf("Linking %s to %s\n", dev_table[i].linkpath,
+						dev_table[i].target);
+
+		if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0)
+			perror("Failed to create device symlink");
+	}
+}
+
+static void run_shell(void)
+{
+	pid_t pid;
+
+	printf("Starting shell\n");
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return;
+	}
+
+	if (pid == 0) {
+		char *prg_argv[] = { "/bin/sh", NULL };
+		char *prg_envp[] = { NULL };
+
+		execve(prg_argv[0], prg_argv, prg_envp);
+		exit(0);
+	}
+
+	printf("PID %d created\n", pid);
+
+	shell_pid = pid;
+}
+
+static void exit_shell(void)
+{
+	shell_pid = -1;
+
+	if (!is_init) {
+		mainloop_quit();
+		return;
+	}
+
+	run_shell();
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	case SIGCHLD:
+		while (1) {
+			pid_t pid;
+			int status;
+
+			pid = waitpid(WAIT_ANY, &status, WNOHANG);
+			if (pid < 0 || pid == 0)
+				break;
+
+			if (WIFEXITED(status)) {
+				printf("PID %d exited (status=%d)\n",
+						pid, WEXITSTATUS(status));
+
+				if (pid == shell_pid)
+					exit_shell();
+			} else if (WIFSIGNALED(status)) {
+				printf("PID %d terminated (signal=%d)\n",
+							pid, WTERMSIG(status));
+
+				if (pid == shell_pid)
+					exit_shell();
+			}
+		}
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	sigset_t mask;
+	int exit_status;
+
+	if (getpid() == 1 && getppid() == 0)
+		is_init = true;
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+	sigaddset(&mask, SIGCHLD);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Bluetooth periperhal ver %s\n", VERSION);
+
+	prepare_filesystem();
+
+	if (is_init) {
+		uint8_t addr[6];
+
+		if (efivars_read("BluetoothStaticAddress", NULL,
+							addr, 6) < 0) {
+			printf("Generating new persistent static address\n");
+
+			addr[0] = rand();
+			addr[1] = rand();
+			addr[2] = rand();
+			addr[3] = 0x34;
+			addr[4] = 0x12;
+			addr[5] = 0xc0;
+
+			efivars_write("BluetoothStaticAddress",
+					EFIVARS_NON_VOLATILE |
+					EFIVARS_BOOTSERVICE_ACCESS |
+					EFIVARS_RUNTIME_ACCESS,
+					addr, 6);
+		}
+
+		gap_set_static_address(addr);
+
+		run_shell();
+	}
+
+	log_open();
+	gap_start();
+
+	if (is_init)
+		attach_start();
+
+	exit_status = mainloop_run();
+
+	if (is_init)
+		attach_stop();
+
+	gap_stop();
+	log_close();
+
+	return exit_status;
+}
diff --git a/bluez/repo/plugins/._autopair.c b/bluez/repo/plugins/._autopair.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._autopair.c
Binary files differ
diff --git a/bluez/repo/plugins/._external-dummy.c b/bluez/repo/plugins/._external-dummy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._external-dummy.c
Binary files differ
diff --git a/bluez/repo/plugins/._gatt-example.c b/bluez/repo/plugins/._gatt-example.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._gatt-example.c
Binary files differ
diff --git a/bluez/repo/plugins/._hostname.c b/bluez/repo/plugins/._hostname.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._hostname.c
Binary files differ
diff --git a/bluez/repo/plugins/._neard.c b/bluez/repo/plugins/._neard.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._neard.c
Binary files differ
diff --git a/bluez/repo/plugins/._policy.c b/bluez/repo/plugins/._policy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._policy.c
Binary files differ
diff --git a/bluez/repo/plugins/._sixaxis.c b/bluez/repo/plugins/._sixaxis.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._sixaxis.c
Binary files differ
diff --git a/bluez/repo/plugins/._wiimote.c b/bluez/repo/plugins/._wiimote.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/plugins/._wiimote.c
Binary files differ
diff --git a/bluez/repo/plugins/autopair.c b/bluez/repo/plugins/autopair.c
new file mode 100644
index 0000000..c201722
--- /dev/null
+++ b/bluez/repo/plugins/autopair.c
@@ -0,0 +1,196 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Google 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 <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/log.h"
+#include "src/storage.h"
+
+/*
+ * Plugin to handle automatic pairing of devices with reduced user
+ * interaction, including implementing the recommendation of the HID spec
+ * for keyboard devices.
+ *
+ * The plugin works by intercepting the PIN request for devices; if the
+ * device is a keyboard a random six-digit numeric PIN is generated and
+ * returned, flagged for displaying using DisplayPinCode.
+ *
+ */
+
+static ssize_t autopair_pincb(struct btd_adapter *adapter,
+						struct btd_device *device,
+						char *pinbuf, bool *display,
+						unsigned int attempt)
+{
+	char addr[18];
+	char pinstr[7];
+	uint32_t class;
+
+	ba2str(device_get_address(device), addr);
+
+	class = btd_device_get_class(device);
+
+	DBG("device %s 0x%x", addr, class);
+
+	/* This is a class-based pincode guesser. Ignore devices with an
+	 * unknown class.
+	 */
+	if (class == 0)
+		return 0;
+
+	switch ((class & 0x1f00) >> 8) {
+	case 0x04:		/* Audio/Video */
+		switch ((class & 0xfc) >> 2) {
+		case 0x01:		/* Wearable Headset Device */
+		case 0x02:		/* Hands-free Device */
+		case 0x06:		/* Headphones */
+		case 0x07:		/* Portable Audio */
+		case 0x0a:		/* HiFi Audio Device */
+			if (attempt > 1)
+				return 0;
+			memcpy(pinbuf, "0000", 4);
+			return 4;
+		}
+		break;
+
+	case 0x05:		/* Peripheral */
+		switch ((class & 0xc0) >> 6) {
+		case 0x01:		/* Keyboard */
+		case 0x03:		/* Combo keyboard/pointing device */
+			/* For keyboards rejecting the first random code
+			 * in less than 500ms, try a fixed code. */
+			if (attempt > 1 &&
+				device_bonding_last_duration(device) < 500) {
+				/* Don't try more than one dumb code */
+				if (attempt > 2)
+					return 0;
+				/* Try "0000" as the code for the second
+				 * attempt. */
+				memcpy(pinbuf, "0000", 4);
+				return 4;
+			}
+
+			/* Never try more than 3 random pincodes. */
+			if (attempt >= 4)
+				return 0;
+
+			snprintf(pinstr, sizeof(pinstr), "%06d",
+						rand() % 1000000);
+			*display = true;
+			memcpy(pinbuf, pinstr, 6);
+			return 6;
+
+		case 0x02: /* Pointing device */
+			if (attempt > 1)
+				return 0;
+			memcpy(pinbuf, "0000", 4);
+			return 4;
+		}
+
+		break;
+	case 0x06:		/* Imaging */
+		if (class & 0x80) {	/* Printer */
+			if (attempt > 1)
+				return 0;
+			memcpy(pinbuf, "0000", 4);
+			return 4;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+
+static int autopair_probe(struct btd_adapter *adapter)
+{
+	btd_adapter_register_pin_cb(adapter, autopair_pincb);
+
+	return 0;
+}
+
+static void autopair_remove(struct btd_adapter *adapter)
+{
+	btd_adapter_unregister_pin_cb(adapter, autopair_pincb);
+}
+
+static struct btd_adapter_driver autopair_driver = {
+	.name = "autopair",
+	.probe = autopair_probe,
+	.remove = autopair_remove,
+};
+
+static int autopair_init(void)
+{
+	/* Initialize the random seed from /dev/urandom */
+	unsigned int seed;
+	int fd, err;
+	ssize_t n;
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd < 0) {
+		err = -errno;
+		error("Failed to open /dev/urandom: %s (%d)", strerror(-err),
+									-err);
+		return err;
+	}
+
+	n = read(fd, &seed, sizeof(seed));
+	if (n < (ssize_t) sizeof(seed)) {
+		err = (n == -1) ? -errno : -EIO;
+		error("Failed to read %zu bytes from /dev/urandom: %s (%d)",
+					sizeof(seed), strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	close(fd);
+
+	srand(seed);
+
+	return btd_register_adapter_driver(&autopair_driver);
+}
+
+static void autopair_exit(void)
+{
+	btd_unregister_adapter_driver(&autopair_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(autopair, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+						autopair_init, autopair_exit)
diff --git a/bluez/repo/plugins/external-dummy.c b/bluez/repo/plugins/external-dummy.c
new file mode 100644
index 0000000..536ad06
--- /dev/null
+++ b/bluez/repo/plugins/external-dummy.c
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 "src/plugin.h"
+#include "src/log.h"
+
+static int dummy_init(void)
+{
+	DBG("");
+
+	return 0;
+}
+
+static void dummy_exit(void)
+{
+	DBG("");
+}
+
+BLUETOOTH_PLUGIN_DEFINE(external_dummy, VERSION,
+		BLUETOOTH_PLUGIN_PRIORITY_LOW, dummy_init, dummy_exit)
diff --git a/bluez/repo/plugins/gatt-example.c b/bluez/repo/plugins/gatt-example.c
new file mode 100644
index 0000000..5249751
--- /dev/null
+++ b/bluez/repo/plugins/gatt-example.c
@@ -0,0 +1,578 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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 <glib.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt-service.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/attrib-server.h"
+
+/* FIXME: Not defined by SIG? UUID128? */
+#define OPCODES_SUPPORTED_UUID          0xA001
+#define BATTERY_STATE_SVC_UUID		0xA002
+#define BATTERY_STATE_UUID		0xA003
+#define THERM_HUMIDITY_SVC_UUID		0xA004
+#define MANUFACTURER_SVC_UUID		0xA005
+#define TEMPERATURE_UUID		0xA006
+#define FMT_CELSIUS_UUID		0xA007
+#define FMT_OUTSIDE_UUID		0xA008
+#define RELATIVE_HUMIDITY_UUID		0xA009
+#define FMT_PERCENT_UUID		0xA00A
+#define BLUETOOTH_SIG_UUID		0xA00B
+#define MANUFACTURER_NAME_UUID		0xA00C
+#define MANUFACTURER_SERIAL_UUID	0xA00D
+#define VENDOR_SPECIFIC_SVC_UUID	0xA00E
+#define VENDOR_SPECIFIC_TYPE_UUID	0xA00F
+#define FMT_KILOGRAM_UUID		0xA010
+#define FMT_HANGING_UUID		0xA011
+
+struct gatt_example_adapter {
+	struct btd_adapter	*adapter;
+	GSList			*sdp_handles;
+};
+
+static GSList *adapters = NULL;
+
+static void gatt_example_adapter_free(struct gatt_example_adapter *gadapter)
+{
+	while (gadapter->sdp_handles != NULL) {
+		uint32_t handle = GPOINTER_TO_UINT(gadapter->sdp_handles->data);
+
+		attrib_free_sdp(gadapter->adapter, handle);
+		gadapter->sdp_handles = g_slist_remove(gadapter->sdp_handles,
+						gadapter->sdp_handles->data);
+	}
+
+	if (gadapter->adapter != NULL)
+		btd_adapter_unref(gadapter->adapter);
+
+	g_free(gadapter);
+}
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_example_adapter *gatt_adapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (gatt_adapter->adapter == adapter)
+		return 0;
+
+	return -1;
+}
+
+static uint8_t battery_state_read(struct attribute *a,
+				  struct btd_device *device, gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint8_t value;
+
+	value = 0x04;
+	attrib_db_update(adapter, a->handle, NULL, &value, sizeof(value), NULL);
+
+	return 0;
+}
+
+static gboolean register_battery_service(struct btd_adapter *adapter)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, BATTERY_STATE_SVC_UUID);
+
+	return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+			/* battery state characteristic */
+			GATT_OPT_CHR_UUID16, BATTERY_STATE_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+							GATT_CHR_PROP_NOTIFY,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+						battery_state_read, adapter,
+
+			GATT_OPT_INVALID);
+}
+
+static void register_termometer_service(struct gatt_example_adapter *adapter,
+			const uint16_t manuf1[2], const uint16_t manuf2[2])
+{
+	const char *desc_out_temp = "Outside Temperature";
+	const char *desc_out_hum = "Outside Relative Humidity";
+	uint16_t start_handle, h;
+	const int svc_size = 11;
+	uint32_t sdp_handle;
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+	int len;
+
+	bt_uuid16_create(&uuid, THERM_HUMIDITY_SVC_UUID);
+	start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		return;
+	}
+
+	DBG("start_handle=0x%04x manuf1=0x%04x-0x%04x, manuf2=0x%04x-0x%04x",
+		start_handle, manuf1[0], manuf1[1], manuf2[0], manuf2[1]);
+
+	h = start_handle;
+
+	/* Thermometer: primary service definition */
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	put_le16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+
+	/* Thermometer: Include */
+	if (manuf1[0] && manuf1[1]) {
+		put_le16(manuf1[0], &atval[0]);
+		put_le16(manuf1[1], &atval[2]);
+		put_le16(MANUFACTURER_SVC_UUID, &atval[4]);
+		attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+						ATT_NOT_PERMITTED, atval, 6);
+	}
+
+	/* Thermometer: Include */
+	if (manuf2[0] && manuf2[1]) {
+		put_le16(manuf2[0], &atval[0]);
+		put_le16(manuf2[1], &atval[2]);
+		put_le16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]);
+		attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+						ATT_NOT_PERMITTED, atval, 6);
+	}
+
+	/* Thermometer: temperature characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(TEMPERATURE_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Thermometer: temperature characteristic value */
+	bt_uuid16_create(&uuid, TEMPERATURE_UUID);
+	atval[0] = 0x8A;
+	atval[1] = 0x02;
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	/* Thermometer: temperature characteristic format */
+	bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+	atval[0] = 0x0E;
+	atval[1] = 0xFE;
+	put_le16(FMT_CELSIUS_UUID, &atval[2]);
+	atval[4] = 0x01;
+	put_le16(FMT_OUTSIDE_UUID, &atval[5]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 7);
+
+	/* Thermometer: characteristic user description */
+	bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+	len = strlen(desc_out_temp);
+	strncpy((char *) atval, desc_out_temp, len);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+
+	/* Thermometer: relative humidity characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(RELATIVE_HUMIDITY_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Thermometer: relative humidity value */
+	bt_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
+	atval[0] = 0x27;
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 1);
+
+	/* Thermometer: relative humidity characteristic format */
+	bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+	atval[0] = 0x04;
+	atval[1] = 0x00;
+	put_le16(FMT_PERCENT_UUID, &atval[2]);
+	put_le16(BLUETOOTH_SIG_UUID, &atval[4]);
+	put_le16(FMT_OUTSIDE_UUID, &atval[6]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 8);
+
+	/* Thermometer: characteristic user description */
+	bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+	len = strlen(desc_out_hum);
+	strncpy((char *) atval, desc_out_hum, len);
+	attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+
+	g_assert(h - start_handle + 1 == svc_size);
+
+	/* Add an SDP record for the above service */
+	sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
+								"Thermometer");
+	if (sdp_handle)
+		adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles,
+						GUINT_TO_POINTER(sdp_handle));
+}
+
+static void register_manuf1_service(struct gatt_example_adapter *adapter,
+							uint16_t range[2])
+{
+	const char *manufacturer_name1 = "ACME Temperature Sensor";
+	const char *serial1 = "237495-3282-A";
+	uint16_t start_handle, h;
+	const int svc_size = 5;
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+	int len;
+
+	bt_uuid16_create(&uuid, MANUFACTURER_SVC_UUID);
+	start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		return;
+	}
+
+	DBG("start_handle=0x%04x", start_handle);
+
+	h = start_handle;
+
+	/* Secondary Service: Manufacturer Service */
+	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+	put_le16(MANUFACTURER_SVC_UUID, &atval[0]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	/* Manufacturer name characteristic definition */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(MANUFACTURER_NAME_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Manufacturer name characteristic value */
+	bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+	len = strlen(manufacturer_name1);
+	strncpy((char *) atval, manufacturer_name1, len);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+
+	/* Manufacturer serial number characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Manufacturer serial number characteristic value */
+	bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+	len = strlen(serial1);
+	strncpy((char *) atval, serial1, len);
+	attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+
+	g_assert(h - start_handle + 1 == svc_size);
+
+	range[0] = start_handle;
+	range[1] = start_handle + svc_size - 1;
+}
+
+static void register_manuf2_service(struct gatt_example_adapter *adapter,
+							uint16_t range[2])
+{
+	const char *manufacturer_name2 = "ACME Weighing Scales";
+	const char *serial2 = "11267-2327A00239";
+	uint16_t start_handle, h;
+	const int svc_size = 5;
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+	int len;
+
+	bt_uuid16_create(&uuid, MANUFACTURER_SVC_UUID);
+	start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		return;
+	}
+
+	DBG("start_handle=0x%04x", start_handle);
+
+	h = start_handle;
+
+	/* Secondary Service: Manufacturer Service */
+	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+	put_le16(MANUFACTURER_SVC_UUID, &atval[0]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	/* Manufacturer name characteristic definition */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(MANUFACTURER_NAME_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Manufacturer name attribute */
+	bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+	len = strlen(manufacturer_name2);
+	strncpy((char *) atval, manufacturer_name2, len);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+
+	/* Characteristic: serial number */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Serial number characteristic value */
+	bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+	len = strlen(serial2);
+	strncpy((char *) atval, serial2, len);
+	attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+
+	g_assert(h - start_handle + 1 == svc_size);
+
+	range[0] = start_handle;
+	range[1] = start_handle + svc_size - 1;
+}
+
+static void register_vendor_service(struct gatt_example_adapter *adapter,
+							uint16_t range[2])
+{
+	uint16_t start_handle, h;
+	const int svc_size = 3;
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, VENDOR_SPECIFIC_SVC_UUID);
+	start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		return;
+	}
+
+	DBG("start_handle=0x%04x", start_handle);
+
+	h = start_handle;
+
+	/* Secondary Service: Vendor Specific Service */
+	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+	put_le16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	/* Vendor Specific Type characteristic definition */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	put_le16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* Vendor Specific Type characteristic value */
+	bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
+	atval[0] = 0x56;
+	atval[1] = 0x65;
+	atval[2] = 0x6E;
+	atval[3] = 0x64;
+	atval[4] = 0x6F;
+	atval[5] = 0x72;
+	attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 6);
+
+	g_assert(h - start_handle + 1 == svc_size);
+
+	range[0] = start_handle;
+	range[1] = start_handle + svc_size - 1;
+}
+
+static void register_weight_service(struct gatt_example_adapter *adapter,
+						const uint16_t vendor[2])
+{
+	const char *desc_weight = "Rucksack Weight";
+	const uint128_t char_weight_uuid_btorder = {
+		.data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B,
+			  0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } };
+	const uint128_t prim_weight_uuid_btorder = {
+		.data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11,
+			  0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } };
+	uint128_t prim_weight_uuid, char_weight_uuid;
+	uint16_t start_handle, h;
+	const int svc_size = 6;
+	uint32_t sdp_handle;
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+	int len;
+
+	btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
+	btoh128(&prim_weight_uuid_btorder, &prim_weight_uuid);
+	bt_uuid128_create(&uuid, prim_weight_uuid);
+	start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		return;
+	}
+
+	DBG("start_handle=0x%04x, vendor=0x%04x-0x%04x", start_handle,
+							vendor[0], vendor[1]);
+
+	h = start_handle;
+
+	/* Weight service: primary service definition */
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	memcpy(atval, &prim_weight_uuid_btorder, 16);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 16);
+
+	if (vendor[0] && vendor[1]) {
+		/* Weight: include */
+		bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+		put_le16(vendor[0], &atval[0]);
+		put_le16(vendor[1], &atval[2]);
+		put_le16(MANUFACTURER_SVC_UUID, &atval[4]);
+		attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+						ATT_NOT_PERMITTED, atval, 6);
+	}
+
+	/* Weight: characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(h + 1, &atval[1]);
+	memcpy(&atval[3], &char_weight_uuid_btorder, 16);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 19);
+
+	/* Weight: characteristic value */
+	bt_uuid128_create(&uuid, char_weight_uuid);
+	atval[0] = 0x82;
+	atval[1] = 0x55;
+	atval[2] = 0x00;
+	atval[3] = 0x00;
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 4);
+
+	/* Weight: characteristic format */
+	bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+	atval[0] = 0x08;
+	atval[1] = 0xFD;
+	put_le16(FMT_KILOGRAM_UUID, &atval[2]);
+	put_le16(BLUETOOTH_SIG_UUID, &atval[4]);
+	put_le16(FMT_HANGING_UUID, &atval[6]);
+	attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 8);
+
+	/* Weight: characteristic user description */
+	bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+	len = strlen(desc_weight);
+	strncpy((char *) atval, desc_weight, len);
+	attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, len);
+	g_assert(h - start_handle + 1 == svc_size);
+
+	/* Add an SDP record for the above service */
+	sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
+							"Weight Service");
+	if (sdp_handle)
+		adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles,
+						GUINT_TO_POINTER(sdp_handle));
+}
+
+static int gatt_example_adapter_probe(struct btd_adapter *adapter)
+{
+	uint16_t manuf1_range[2] = {0, 0}, manuf2_range[2] = {0, 0};
+	uint16_t vendor_range[2] = {0, 0};
+	struct gatt_example_adapter *gadapter;
+
+	gadapter = g_new0(struct gatt_example_adapter, 1);
+	gadapter->adapter = btd_adapter_ref(adapter);
+
+	if (!register_battery_service(adapter)) {
+		DBG("Battery service could not be registered");
+		gatt_example_adapter_free(gadapter);
+		return -EIO;
+	}
+
+	register_manuf1_service(gadapter, manuf1_range);
+	register_manuf2_service(gadapter, manuf2_range);
+	register_termometer_service(gadapter, manuf1_range, manuf2_range);
+	register_vendor_service(gadapter, vendor_range);
+	register_weight_service(gadapter, vendor_range);
+
+	adapters = g_slist_append(adapters, gadapter);
+
+	return 0;
+}
+
+static void gatt_example_adapter_remove(struct btd_adapter *adapter)
+{
+	struct gatt_example_adapter *gadapter;
+	GSList *l;
+
+	l = g_slist_find_custom(adapters, adapter, adapter_cmp);
+	if (l == NULL)
+		return;
+
+	gadapter = l->data;
+	adapters = g_slist_remove(adapters, gadapter);
+	gatt_example_adapter_free(gadapter);
+}
+
+static struct btd_adapter_driver gatt_example_adapter_driver = {
+	.name	= "gatt-example-adapter-driver",
+	.probe	= gatt_example_adapter_probe,
+	.remove	= gatt_example_adapter_remove,
+};
+
+static int gatt_example_init(void)
+{
+	return btd_register_adapter_driver(&gatt_example_adapter_driver);
+}
+
+static void gatt_example_exit(void)
+{
+	btd_unregister_adapter_driver(&gatt_example_adapter_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gatt_example, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW,
+					gatt_example_init, gatt_example_exit)
diff --git a/bluez/repo/plugins/hostname.c b/bluez/repo/plugins/hostname.c
new file mode 100644
index 0000000..4f9dfe6
--- /dev/null
+++ b/bluez/repo/plugins/hostname.c
@@ -0,0 +1,328 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/dbus-common.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/log.h"
+
+/* http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm */
+
+#define MAJOR_CLASS_MISCELLANEOUS	0x00
+#define MAJOR_CLASS_COMPUTER		0x01
+
+#define MINOR_CLASS_UNCATEGORIZED	0x00
+#define MINOR_CLASS_DESKTOP		0x01
+#define MINOR_CLASS_SERVER		0x02
+#define MINOR_CLASS_LAPTOP		0x03
+#define MINOR_CLASS_HANDHELD		0x04
+#define MINOR_CLASS_PALM_SIZED		0x05
+#define MINOR_CLASS_WEARABLE		0x06
+#define MINOR_CLASS_TABLET		0x07
+
+static uint8_t major_class = MAJOR_CLASS_MISCELLANEOUS;
+static uint8_t minor_class = MINOR_CLASS_UNCATEGORIZED;
+
+static char *pretty_hostname = NULL;
+static char *static_hostname = NULL;
+
+/*
+ * Fallback to static hostname only if empty pretty hostname was already
+ * received.
+ */
+static const char *get_hostname(void)
+{
+	if (pretty_hostname) {
+		if (g_str_equal(pretty_hostname, "") == FALSE)
+			return pretty_hostname;
+
+		if (static_hostname &&
+				g_str_equal(static_hostname, "") == FALSE)
+			return static_hostname;
+	}
+
+	return NULL;
+}
+
+static void update_name(struct btd_adapter *adapter, gpointer user_data)
+{
+	const char *hostname = get_hostname();
+
+	if (hostname == NULL)
+		return;
+
+	if (btd_adapter_is_default(adapter)) {
+		DBG("name: %s", hostname);
+
+		adapter_set_name(adapter, hostname);
+	} else {
+		uint16_t index = btd_adapter_get_index(adapter);
+		char *str;
+
+		/* Avoid "some device #0" names, start at #1 */
+		str = g_strdup_printf("%s #%u", hostname, index + 1);
+
+		DBG("name: %s", str);
+
+		adapter_set_name(adapter, str);
+
+		g_free(str);
+	}
+}
+
+static void update_class(struct btd_adapter *adapter, gpointer user_data)
+{
+	if (major_class == MAJOR_CLASS_MISCELLANEOUS)
+		return;
+
+	DBG("major: 0x%02x minor: 0x%02x", major_class, minor_class);
+
+	btd_adapter_set_class(adapter, major_class, minor_class);
+}
+
+static const struct {
+	const char *chassis;
+	uint8_t major_class;
+	uint8_t minor_class;
+} chassis_table[] = {
+	{ "desktop",  MAJOR_CLASS_COMPUTER, MINOR_CLASS_DESKTOP  },
+	{ "server",   MAJOR_CLASS_COMPUTER, MINOR_CLASS_SERVER   },
+	{ "laptop",   MAJOR_CLASS_COMPUTER, MINOR_CLASS_LAPTOP   },
+	{ "handset",  MAJOR_CLASS_COMPUTER, MINOR_CLASS_HANDHELD },
+	{ "tablet",   MAJOR_CLASS_COMPUTER, MINOR_CLASS_TABLET   },
+	{ }
+};
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	if (g_str_equal(name, "PrettyHostname") == TRUE) {
+		if (iter == NULL) {
+			g_dbus_proxy_refresh_property(proxy, name);
+			return;
+		}
+
+		if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+			const char *str;
+
+			dbus_message_iter_get_basic(iter, &str);
+
+			DBG("pretty hostname: %s", str);
+
+			g_free(pretty_hostname);
+			pretty_hostname = g_strdup(str);
+
+			adapter_foreach(update_name, NULL);
+		}
+	} else if (g_str_equal(name, "StaticHostname") == TRUE) {
+		if (iter == NULL) {
+			g_dbus_proxy_refresh_property(proxy, name);
+			return;
+		}
+
+		if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+			const char *str;
+
+			dbus_message_iter_get_basic(iter, &str);
+
+			DBG("static hostname: %s", str);
+
+			g_free(static_hostname);
+			static_hostname = g_strdup(str);
+
+			adapter_foreach(update_name, NULL);
+		}
+	} else if (g_str_equal(name, "Chassis") == TRUE) {
+		if (iter == NULL) {
+			g_dbus_proxy_refresh_property(proxy, name);
+			return;
+		}
+
+		if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+			const char *str;
+			int i;
+
+			dbus_message_iter_get_basic(iter, &str);
+
+			DBG("chassis: %s", str);
+
+			for (i = 0; chassis_table[i].chassis; i++) {
+				if (strcmp(chassis_table[i].chassis, str))
+					continue;
+
+				major_class = chassis_table[i].major_class;
+				minor_class = chassis_table[i].minor_class;
+
+				adapter_foreach(update_class, NULL);
+				break;
+			}
+		}
+	}
+}
+
+static int hostname_probe(struct btd_adapter *adapter)
+{
+	DBG("");
+
+	update_name(adapter, NULL);
+	update_class(adapter, NULL);
+
+	return 0;
+}
+
+static void hostname_remove(struct btd_adapter *adapter)
+{
+	DBG("");
+}
+
+static struct btd_adapter_driver hostname_driver = {
+	.name	= "hostname",
+	.probe	= hostname_probe,
+	.remove	= hostname_remove,
+};
+
+static void read_dmi_fallback(void)
+{
+	char *contents;
+	int i, type;
+	const char *str;
+
+	if (g_file_get_contents("/sys/class/dmi/id/chassis_type",
+					&contents, NULL, NULL) == FALSE)
+		return;
+
+	type = atoi(contents);
+	if (type < 0 || type > 0x1D)
+		return;
+
+	g_free(contents);
+
+	/* from systemd hostname chassis list */
+	switch (type) {
+	case 0x3:
+	case 0x4:
+	case 0x6:
+	case 0x7:
+		str = "desktop";
+		break;
+	case 0x8:
+	case 0x9:
+	case 0xA:
+	case 0xE:
+		str = "laptop";
+		break;
+	case 0xB:
+		str = "handset";
+		break;
+	case 0x11:
+	case 0x1C:
+		str = "server";
+		break;
+	default:
+		return;
+	}
+
+	DBG("chassis: %s", str);
+
+	for (i = 0; chassis_table[i].chassis; i++) {
+		if (!strcmp(chassis_table[i].chassis, str)) {
+			major_class = chassis_table[i].major_class;
+			minor_class = chassis_table[i].minor_class;
+			break;
+		}
+	}
+
+	DBG("major: 0x%02x minor: 0x%02x", major_class, minor_class);
+}
+
+static GDBusClient *hostname_client = NULL;
+static GDBusProxy *hostname_proxy = NULL;
+
+static int hostname_init(void)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	int err;
+
+	read_dmi_fallback();
+
+	hostname_client = g_dbus_client_new(conn, "org.freedesktop.hostname1",
+						"/org/freedesktop/hostname1");
+	if (!hostname_client)
+		return -EIO;
+
+	hostname_proxy = g_dbus_proxy_new(hostname_client,
+						"/org/freedesktop/hostname1",
+						"org.freedesktop.hostname1");
+	if (!hostname_proxy) {
+		g_dbus_client_unref(hostname_client);
+		hostname_client = NULL;
+		return -EIO;
+	}
+
+	g_dbus_proxy_set_property_watch(hostname_proxy, property_changed, NULL);
+
+	err = btd_register_adapter_driver(&hostname_driver);
+	if (err < 0) {
+		g_dbus_proxy_unref(hostname_proxy);
+		hostname_proxy = NULL;
+		g_dbus_client_unref(hostname_client);
+		hostname_client = NULL;
+	}
+
+	return err;
+}
+
+static void hostname_exit(void)
+{
+	btd_unregister_adapter_driver(&hostname_driver);
+
+	if (hostname_proxy) {
+		g_dbus_proxy_unref(hostname_proxy);
+		hostname_proxy = NULL;
+	}
+
+	if (hostname_client) {
+		g_dbus_client_unref(hostname_client);
+		hostname_client = NULL;
+	}
+
+	g_free(pretty_hostname);
+	g_free(static_hostname);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hostname, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+						hostname_init, hostname_exit)
diff --git a/bluez/repo/plugins/neard.c b/bluez/repo/plugins/neard.c
new file mode 100644
index 0000000..cabcf34
--- /dev/null
+++ b/bluez/repo/plugins/neard.c
@@ -0,0 +1,910 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  Tieto Poland
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.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"
+
+#define NEARD_NAME "org.neard"
+#define NEARD_PATH "/"
+#define NEARD_MANAGER_INTERFACE "org.neard.Manager"
+#define AGENT_INTERFACE "org.neard.HandoverAgent"
+#define AGENT_PATH "/org/bluez/neard_handover_agent"
+#define AGENT_CARRIER_TYPE "bluetooth"
+#define ERROR_INTERFACE "org.neard.HandoverAgent.Error"
+
+static guint watcher_id = 0;
+static char *neard_service = NULL;
+static bool agent_register_postpone = false;
+
+/* For NFC mimetype limits max OOB EIR size */
+#define NFC_OOB_EIR_MAX UINT8_MAX
+
+enum cps {
+	CPS_ACTIVE,
+	CPS_INACTIVE,
+	CPS_ACTIVATING,
+	CPS_UNKNOWN,
+};
+
+struct oob_params {
+	bdaddr_t address;
+	uint32_t class;
+	char *name;
+	GSList *services;
+	uint8_t *hash;
+	uint8_t *randomizer;
+	uint8_t *pin;
+	int pin_len;
+	enum cps power_state;
+};
+
+static void free_oob_params(struct oob_params *params)
+{
+	g_slist_free_full(params->services, free);
+	g_free(params->name);
+	g_free(params->hash);
+	g_free(params->randomizer);
+	g_free(params->pin);
+}
+
+static DBusMessage *error_reply(DBusMessage *msg, int error)
+{
+	const char *name;
+
+	if (error == EINPROGRESS)
+		name = ERROR_INTERFACE ".InProgress";
+	else
+		name = ERROR_INTERFACE ".Failed";
+
+	return g_dbus_create_error(msg, name , "%s", strerror(error));
+}
+
+static void register_agent(bool append_carrier);
+
+static void register_agent_cb(DBusPendingCall *call, void *user_data)
+{
+	DBusMessage *reply;
+	DBusError err;
+	static bool try_fallback = true;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, reply)) {
+		if (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) &&
+				try_fallback) {
+			DBG("Register to neard failed, trying legacy way");
+
+			register_agent(false);
+			try_fallback = false;
+		} else {
+			error("neard manager replied with an error: %s, %s",
+							err.name, err.message);
+
+			g_dbus_unregister_interface(btd_get_dbus_connection(),
+						AGENT_PATH, AGENT_INTERFACE);
+			try_fallback = true;
+		}
+
+		dbus_error_free(&err);
+		dbus_message_unref(reply);
+
+		return;
+	}
+
+	dbus_message_unref(reply);
+	neard_service = g_strdup(dbus_message_get_sender(reply));
+
+	try_fallback = true;
+
+	info("Registered as neard handover agent");
+}
+
+static void register_agent(bool append_carrier)
+{
+	DBusMessage *message;
+	DBusPendingCall *call;
+	const char *path = AGENT_PATH;
+	const char *carrier = AGENT_CARRIER_TYPE;
+
+	message = dbus_message_new_method_call(NEARD_NAME, NEARD_PATH,
+			NEARD_MANAGER_INTERFACE, "RegisterHandoverAgent");
+	if (!message) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID);
+
+	if (append_carrier)
+		dbus_message_append_args(message, DBUS_TYPE_STRING, &carrier,
+							DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message_with_reply(btd_get_dbus_connection(),
+							message, &call, -1)) {
+		dbus_message_unref(message);
+		error("D-Bus send failed");
+		return;
+	}
+
+	dbus_pending_call_set_notify(call, register_agent_cb, NULL, NULL);
+	dbus_pending_call_unref(call);
+
+	dbus_message_unref(message);
+}
+
+static void unregister_agent(void)
+{
+	DBusMessage *message;
+	const char *path = AGENT_PATH;
+	const char *carrier = AGENT_CARRIER_TYPE;
+
+	g_free(neard_service);
+	neard_service = NULL;
+
+	message = dbus_message_new_method_call(NEARD_NAME, NEARD_PATH,
+			NEARD_MANAGER_INTERFACE, "UnregisterHandoverAgent");
+
+	if (!message) {
+		error("Couldn't allocate D-Bus message");
+		goto unregister;
+	}
+
+	dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID);
+
+	dbus_message_append_args(message, DBUS_TYPE_STRING, &carrier,
+							DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message(btd_get_dbus_connection(), message))
+		error("D-Bus send failed");
+
+unregister:
+	g_dbus_unregister_interface(btd_get_dbus_connection(), AGENT_PATH,
+							AGENT_INTERFACE);
+}
+
+static void add_power_state(DBusMessageIter *dict, struct btd_adapter *adapter)
+{
+	const char *state;
+
+	if (btd_adapter_get_powered(adapter) &&
+					btd_adapter_get_connectable(adapter))
+		state = "active";
+	else
+		state = "inactive";
+
+	dict_append_entry(dict, "State", DBUS_TYPE_STRING, &state);
+}
+
+static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter,
+						const uint8_t *hash,
+						const uint8_t *randomizer,
+						DBusMessage *msg)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	uint8_t eir[NFC_OOB_EIR_MAX];
+	uint8_t *peir = eir;
+	int len;
+
+	len = eir_create_oob(btd_adapter_get_address(adapter),
+				btd_adapter_get_name(adapter),
+				btd_adapter_get_class(adapter), hash,
+				randomizer, main_opts.did_vendor,
+				main_opts.did_product, main_opts.did_version,
+				main_opts.did_source,
+				btd_adapter_get_services(adapter), eir);
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	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, "EIR", DBUS_TYPE_BYTE, &peir, len);
+
+	add_power_state(&dict, adapter);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static void read_local_complete(struct btd_adapter *adapter,
+				const uint8_t *hash, const uint8_t *randomizer,
+				void *user_data)
+{
+	DBusMessage *msg = user_data;
+	DBusMessage *reply;
+
+	DBG("");
+
+	if (neard_service == NULL) {
+		dbus_message_unref(msg);
+
+		if (agent_register_postpone) {
+			agent_register_postpone = false;
+			register_agent(true);
+		}
+
+		return;
+	}
+
+	if (hash && randomizer)
+		reply = create_request_oob_reply(adapter, hash, randomizer,
+									msg);
+	else
+		reply = error_reply(msg, EIO);
+
+	dbus_message_unref(msg);
+
+	if (!g_dbus_send_message(btd_get_dbus_connection(), reply))
+		error("D-Bus send failed");
+}
+
+static void bonding_complete(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr, uint8_t status,
+					void *user_data)
+{
+	DBusMessage *msg = user_data;
+	DBusMessage *reply;
+
+	DBG("");
+
+	if (neard_service == NULL) {
+		dbus_message_unref(msg);
+
+		if (agent_register_postpone) {
+			agent_register_postpone = false;
+			register_agent(true);
+		}
+
+		return;
+	}
+
+	if (status)
+		reply = error_reply(msg, EIO);
+	else
+		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	dbus_message_unref(msg);
+
+	if (!g_dbus_send_message(btd_get_dbus_connection(), reply))
+		error("D-Bus send failed");
+}
+
+static int check_device(struct btd_device *device)
+{
+	if (!device)
+		return -ENOENT;
+
+	/* If already paired */
+	if (device_is_paired(device, BDADDR_BREDR)) {
+		DBG("already paired");
+		return -EALREADY;
+	}
+
+	/* Pairing in progress... */
+	if (device_is_bonding(device, NULL)) {
+		DBG("pairing in progress");
+		return -EINPROGRESS;
+	}
+
+	return 0;
+}
+
+static int process_eir(uint8_t *eir, size_t size, struct oob_params *remote)
+{
+	struct eir_data eir_data;
+
+	DBG("size %zu", size);
+
+	memset(&eir_data, 0, sizeof(eir_data));
+
+	if (eir_parse_oob(&eir_data, eir, size) < 0)
+		return -EINVAL;
+
+	bacpy(&remote->address, &eir_data.addr);
+
+	remote->class = eir_data.class;
+
+	remote->name = eir_data.name;
+	eir_data.name = NULL;
+
+	remote->services = eir_data.services;
+	eir_data.services = NULL;
+
+	remote->hash = eir_data.hash;
+	eir_data.hash = NULL;
+
+	remote->randomizer = eir_data.randomizer;
+	eir_data.randomizer = NULL;
+
+	eir_data_free(&eir_data);
+
+	return 0;
+}
+
+/*
+ * This is (barely documented) Nokia extension format, most work was done by
+ * reverse engineering.
+ *
+ * Binary format varies among different devices, type depends on first byte
+ * 0x00 - BT address not reversed, 16 bytes authentication data (all zeros)
+ * 0x01 - BT address not reversed, 16 bytes authentication data (4 digit PIN,
+ *        padded with zeros)
+ * 0x02 - BT address not reversed, 16 bytes authentication data (not sure if
+ *        16 digit PIN or link key?, Nokia refers to it as ' Public Key')
+ * 0x10 - BT address reversed, no authentication data
+ * 0x24 - BT address not reversed, 4 bytes authentication data (4 digit PIN)
+ *
+ * General structure:
+ * 1 byte  - marker
+ * 6 bytes - BT address (reversed or not, depends on marker)
+ * 3 bytes - Class of Device
+ * 0, 4 or 16 bytes - authentication data, interpretation depends on marker
+ * 1 bytes - name length
+ * N bytes - name
+ */
+
+static int process_nokia_long (void *data, size_t size, uint8_t marker,
+						struct oob_params *remote)
+{
+	struct {
+		bdaddr_t address;
+		uint8_t class[3];
+		uint8_t authentication[16];
+		uint8_t name_len;
+		uint8_t name[0];
+	} __attribute__((packed)) *n = data;
+
+	if (size != sizeof(*n) + n->name_len)
+		return -EINVAL;
+
+	/* address is not reverted */
+	baswap(&remote->address, &n->address);
+
+	remote->class = n->class[0] | (n->class[1] << 8) | (n->class[2] << 16);
+
+	if (n->name_len > 0)
+		remote->name = g_strndup((char *)n->name, n->name_len);
+
+	if (marker == 0x01) {
+		remote->pin = g_memdup(n->authentication, 4);
+		remote->pin_len = 4;
+	} else if (marker == 0x02) {
+		remote->pin = g_memdup(n->authentication, 16);
+		remote->pin_len = 16;
+	}
+
+	return 0;
+}
+
+static int process_nokia_short (void *data, size_t size,
+						struct oob_params *remote)
+{
+	struct {
+		bdaddr_t address;
+		uint8_t class[3];
+		uint8_t authentication[4];
+		uint8_t name_len;
+		uint8_t name[0];
+	} __attribute__((packed)) *n = data;
+
+	if (size != sizeof(*n) + n->name_len)
+		return -EINVAL;
+
+	/* address is not reverted */
+	baswap(&remote->address, &n->address);
+
+	remote->class = n->class[0] | (n->class[1] << 8) | (n->class[2] << 16);
+
+	if (n->name_len > 0)
+		remote->name = g_strndup((char *)n->name, n->name_len);
+
+	remote->pin = g_memdup(n->authentication, 4);
+	remote->pin_len = 4;
+
+	return 0;
+}
+
+static int process_nokia_extra_short (void *data, size_t size,
+						struct oob_params *remote)
+{
+	struct {
+		bdaddr_t address;
+		uint8_t class[3];
+		uint8_t name_len;
+		uint8_t name[0];
+	} __attribute__((packed)) *n = data;
+
+	if (size != sizeof(*n) + n->name_len)
+		return -EINVAL;
+
+	bacpy(&remote->address, &n->address);
+
+	remote->class = n->class[0] | (n->class[1] << 8) | (n->class[2] << 16);
+
+	if (n->name_len > 0)
+		remote->name = g_strndup((char *)n->name, n->name_len);
+
+	return 0;
+}
+
+static int process_nokia_com_bt(uint8_t *data, size_t size,
+						struct oob_params *remote)
+{
+	uint8_t marker;
+
+	marker = *data++;
+	size--;
+
+	DBG("marker: 0x%.2x  size: %zu", marker, size);
+
+	switch (marker) {
+	case 0x00:
+	case 0x01:
+	case 0x02:
+		return process_nokia_long(data, size, marker, remote);
+	case 0x10:
+		return process_nokia_extra_short(data, size, remote);
+	case 0x24:
+		return process_nokia_short(data, size, remote);
+	default:
+		warn("Not supported Nokia NFC extension (0x%.2x)", marker);
+		return -EPROTONOSUPPORT;
+	}
+}
+
+static enum cps process_state(const char *state)
+{
+	if (strcasecmp(state, "active") == 0)
+		return CPS_ACTIVE;
+
+	if (strcasecmp(state, "activating") == 0)
+		return CPS_ACTIVATING;
+
+	if (strcasecmp(state, "inactive") == 0)
+		return CPS_INACTIVE;
+
+	return CPS_UNKNOWN;
+}
+
+static int process_message(DBusMessage *msg, struct oob_params *remote)
+{
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+
+	dbus_message_iter_init(msg, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	/* set CPS to unknown in case State was not provided */
+	remote->power_state = CPS_UNKNOWN;
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter value;
+		DBusMessageIter entry;
+		const char *key;
+
+		dbus_message_iter_recurse(&dict, &entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			goto error;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (strcasecmp(key, "EIR") == 0) {
+			DBusMessageIter array;
+			uint8_t *eir;
+			int size;
+
+			/* nokia.com:bt and EIR should not be passed together */
+			if (bacmp(&remote->address, BDADDR_ANY) != 0)
+				goto error;
+
+			if (dbus_message_iter_get_arg_type(&value) !=
+					DBUS_TYPE_ARRAY)
+				goto error;
+
+			dbus_message_iter_recurse(&value, &array);
+			dbus_message_iter_get_fixed_array(&array, &eir, &size);
+
+			if (process_eir(eir, size, remote) < 0)
+				goto error;
+		} else if (strcasecmp(key, "nokia.com:bt") == 0) {
+			DBusMessageIter array;
+			uint8_t *data;
+			int size;
+
+			/* nokia.com:bt and EIR should not be passed together */
+			if (bacmp(&remote->address, BDADDR_ANY) != 0)
+				goto error;
+
+			if (dbus_message_iter_get_arg_type(&value) !=
+					DBUS_TYPE_ARRAY)
+				goto error;
+
+			dbus_message_iter_recurse(&value, &array);
+			dbus_message_iter_get_fixed_array(&array, &data, &size);
+
+			if (process_nokia_com_bt(data, size, remote))
+				goto error;
+		} else if (strcasecmp(key, "State") == 0) {
+			DBusMessageIter array;
+			const char *state;
+
+			if (dbus_message_iter_get_arg_type(&value) !=
+					DBUS_TYPE_STRING)
+				goto error;
+
+			dbus_message_iter_recurse(&value, &array);
+			dbus_message_iter_get_basic(&value, &state);
+
+			remote->power_state = process_state(state);
+			if (remote->power_state == CPS_UNKNOWN)
+				goto error;
+		}
+
+		dbus_message_iter_next(&dict);
+	}
+
+	/* Check if 'State' was passed along with one of other fields */
+	if (remote->power_state != CPS_UNKNOWN
+			&& bacmp(&remote->address, BDADDR_ANY) == 0)
+		return -EINVAL;
+
+	return 0;
+
+error:
+	if (bacmp(&remote->address, BDADDR_ANY) != 0) {
+		free_oob_params(remote);
+		memset(remote, 0, sizeof(*remote));
+	}
+
+	return -EINVAL;
+}
+
+static int check_adapter(struct btd_adapter *adapter)
+{
+	if (!adapter)
+		return -ENOENT;
+
+	if (btd_adapter_check_oob_handler(adapter))
+		return -EINPROGRESS;
+
+	if (!btd_adapter_ssp_enabled(adapter))
+		return -ENOTSUP;
+
+	return 0;
+}
+
+static void store_params(struct btd_adapter *adapter, struct btd_device *device,
+						struct oob_params *params)
+{
+	if (params->class != 0)
+		device_set_class(device, params->class);
+
+	if (params->name) {
+		device_store_cached_name(device, params->name);
+		btd_device_device_set_name(device, params->name);
+	}
+
+	if (params->services)
+		device_add_eir_uuids(device, params->services);
+
+	if (params->hash) {
+		btd_adapter_add_remote_oob_data(adapter, &params->address,
+							params->hash,
+							params->randomizer);
+	} else if (params->pin_len) {
+		/* TODO
+		 * Handle PIN, for now only discovery mode and 'common' PINs
+		 * that might be provided by agent will work correctly.
+		 */
+	}
+}
+
+static DBusMessage *push_oob(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	struct btd_adapter *adapter;
+	struct agent *agent;
+	struct oob_handler *handler;
+	struct oob_params remote;
+	struct btd_device *device;
+	uint8_t io_cap;
+	int err;
+
+	if (neard_service == NULL ||
+			!g_str_equal(neard_service, dbus_message_get_sender(msg)))
+		return error_reply(msg, EPERM);
+
+	DBG("");
+
+	adapter = btd_adapter_get_default();
+
+	err = check_adapter(adapter);
+	if (err < 0)
+		return error_reply(msg, -err);
+
+	if (!btd_adapter_get_powered(adapter))
+		return error_reply(msg, ENONET);
+
+	agent = adapter_get_agent(adapter);
+	if (!agent)
+		return error_reply(msg, ENONET);
+
+	io_cap = agent_get_io_capability(agent);
+	agent_unref(agent);
+
+	memset(&remote, 0, sizeof(remote));
+
+	err = process_message(msg, &remote);
+	if (err < 0)
+		return error_reply(msg, -err);
+
+	if (bacmp(&remote.address, BDADDR_ANY) == 0) {
+		free_oob_params(&remote);
+
+		return error_reply(msg, EINVAL);
+	}
+
+	device = btd_adapter_get_device(adapter, &remote.address,
+								BDADDR_BREDR);
+
+	err = check_device(device);
+	if (err < 0) {
+		free_oob_params(&remote);
+
+		/* already paired, reply immediately */
+		if (err == -EALREADY)
+			return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+		return error_reply(msg, -err);
+	}
+
+	if (!btd_adapter_get_pairable(adapter)) {
+		free_oob_params(&remote);
+
+		return error_reply(msg, ENONET);
+	}
+
+	store_params(adapter, device, &remote);
+
+	free_oob_params(&remote);
+
+	err = adapter_create_bonding(adapter, device_get_address(device),
+							BDADDR_BREDR, io_cap);
+	if (err < 0)
+		return error_reply(msg, -err);
+
+	handler = g_new0(struct oob_handler, 1);
+	handler->bonding_cb = bonding_complete;
+	bacpy(&handler->remote_addr, device_get_address(device));
+	handler->user_data = dbus_message_ref(msg);
+
+	btd_adapter_set_oob_handler(adapter, handler);
+
+	return NULL;
+}
+
+static DBusMessage *request_oob(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct btd_adapter *adapter;
+	struct oob_handler *handler;
+	struct oob_params remote;
+	struct btd_device *device;
+	int err;
+
+	if (neard_service == NULL ||
+			!g_str_equal(neard_service, dbus_message_get_sender(msg)))
+		return error_reply(msg, EPERM);
+
+	DBG("");
+
+	adapter = btd_adapter_get_default();
+
+	err = check_adapter(adapter);
+	if (err < 0)
+		return error_reply(msg, -err);
+
+	memset(&remote, 0, sizeof(remote));
+
+	err = process_message(msg, &remote);
+	if (err < 0)
+		return error_reply(msg, -err);
+
+	if (bacmp(&remote.address, BDADDR_ANY) == 0) {
+		if (btd_adapter_get_powered(adapter))
+			goto read_local;
+
+		goto done;
+	}
+
+	device = btd_adapter_get_device(adapter, &remote.address, BDADDR_BREDR);
+
+	err = check_device(device);
+	if (err < 0)
+		goto done;
+
+	if (!btd_adapter_get_pairable(adapter)) {
+		err = -ENONET;
+		goto done;
+	}
+
+	store_params(adapter, device, &remote);
+
+	if (remote.hash && btd_adapter_get_powered(adapter))
+		goto read_local;
+done:
+	free_oob_params(&remote);
+
+	if (err < 0 && err != -EALREADY)
+		return error_reply(msg, -err);
+
+	return create_request_oob_reply(adapter, NULL, NULL, msg);
+
+read_local:
+	free_oob_params(&remote);
+
+	err = btd_adapter_read_local_oob_data(adapter);
+	if (err < 0)
+		return error_reply(msg, -err);
+
+	handler = g_new0(struct oob_handler, 1);
+	handler->read_local_cb = read_local_complete;
+	handler->user_data = dbus_message_ref(msg);
+
+	btd_adapter_set_oob_handler(adapter, handler);
+
+	return NULL;
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	if (neard_service == NULL ||
+			!g_str_equal(neard_service, dbus_message_get_sender(msg)))
+		return error_reply(msg, EPERM);
+
+	DBG("");
+
+	g_free(neard_service);
+	neard_service = NULL;
+
+	g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable neard_methods[] = {
+	{ GDBUS_ASYNC_METHOD("RequestOOB",
+			GDBUS_ARGS({ "data", "a{sv}" }),
+			GDBUS_ARGS({ "data", "a{sv}" }), request_oob) },
+	{ GDBUS_ASYNC_METHOD("PushOOB",
+			GDBUS_ARGS({ "data", "a{sv}"}), NULL, push_oob) },
+	{ GDBUS_METHOD("Release", NULL, NULL, release) },
+	{ }
+};
+
+static void neard_appeared(DBusConnection *conn, void *user_data)
+{
+	struct btd_adapter *adapter;
+
+	DBG("");
+
+	if (!g_dbus_register_interface(conn, AGENT_PATH, AGENT_INTERFACE,
+						neard_methods,
+						NULL, NULL, NULL, NULL)) {
+		error("neard interface init failed on path " AGENT_PATH);
+		return;
+	}
+
+	/*
+	 * If there is pending action ongoing when neard appeared, possibly
+	 * due to neard crash or release before action was completed, postpone
+	 * register until action is finished.
+	 */
+	adapter = btd_adapter_get_default();
+
+	if (adapter && btd_adapter_check_oob_handler(adapter))
+		agent_register_postpone = true;
+	else
+		register_agent(true);
+}
+
+static void neard_vanished(DBusConnection *conn, void *user_data)
+{
+	DBG("");
+
+	/* neard existed without unregistering agent */
+	if (neard_service != NULL) {
+		g_free(neard_service);
+		neard_service = NULL;
+
+		g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE);
+	}
+}
+
+static int neard_init(void)
+{
+	DBG("Setup neard plugin");
+
+	watcher_id = g_dbus_add_service_watch(btd_get_dbus_connection(),
+						NEARD_NAME, neard_appeared,
+						neard_vanished, NULL, NULL);
+	if (watcher_id == 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void neard_exit(void)
+{
+	DBG("Cleanup neard plugin");
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher_id);
+	watcher_id = 0;
+
+	if (neard_service != NULL)
+		unregister_agent();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(neard, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+						neard_init, neard_exit)
diff --git a/bluez/repo/plugins/policy.c b/bluez/repo/plugins/policy.c
new file mode 100644
index 0000000..218a3ed
--- /dev/null
+++ b/bluez/repo/plugins/policy.c
@@ -0,0 +1,843 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation.
+ *
+ *
+ *  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 <unistd.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+#include "lib/mgmt.h"
+
+#include "src/log.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/service.h"
+#include "src/profile.h"
+#include "src/hcid.h"
+
+#define CONTROL_CONNECT_TIMEOUT 2
+#define SOURCE_RETRY_TIMEOUT 2
+#define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT
+#define CT_RETRY_TIMEOUT 1
+#define TG_RETRY_TIMEOUT CT_RETRY_TIMEOUT
+#define SOURCE_RETRIES 1
+#define SINK_RETRIES SOURCE_RETRIES
+#define CT_RETRIES 1
+#define TG_RETRIES CT_RETRIES
+
+struct reconnect_data {
+	struct btd_device *dev;
+	bool reconnect;
+	GSList *services;
+	guint timer;
+	bool active;
+	unsigned int attempt;
+};
+
+static const char *default_reconnect[] = {
+			HSP_AG_UUID, HFP_AG_UUID, A2DP_SOURCE_UUID, NULL };
+static char **reconnect_uuids = NULL;
+
+static const size_t default_attempts = 7;
+static size_t reconnect_attempts = 0;
+
+static const int default_intervals[] = { 1, 2, 4, 8, 16, 32, 64 };
+static int *reconnect_intervals = NULL;
+static size_t reconnect_intervals_len = 0;
+
+static GSList *reconnects = NULL;
+
+static unsigned int service_id = 0;
+static GSList *devices = NULL;
+
+static bool auto_enable = false;
+
+struct policy_data {
+	struct btd_device *dev;
+
+	guint source_timer;
+	uint8_t source_retries;
+	guint sink_timer;
+	uint8_t sink_retries;
+	guint ct_timer;
+	uint8_t ct_retries;
+	guint tg_timer;
+	uint8_t tg_retries;
+};
+
+static void policy_connect(struct policy_data *data,
+						struct btd_service *service)
+{
+	struct btd_profile *profile = btd_service_get_profile(service);
+
+	DBG("%s profile %s", device_get_path(data->dev), profile->name);
+
+	btd_service_connect(service);
+}
+
+static void policy_disconnect(struct policy_data *data,
+						struct btd_service *service)
+{
+	struct btd_profile *profile = btd_service_get_profile(service);
+
+	DBG("%s profile %s", device_get_path(data->dev), profile->name);
+
+	btd_service_disconnect(service);
+}
+
+static gboolean policy_connect_ct(gpointer user_data)
+{
+	struct policy_data *data = user_data;
+	struct btd_service *service;
+
+	data->ct_timer = 0;
+	data->ct_retries++;
+
+	service = btd_device_get_service(data->dev, AVRCP_REMOTE_UUID);
+	if (service != NULL)
+		policy_connect(data, service);
+
+	return FALSE;
+}
+
+static void policy_set_ct_timer(struct policy_data *data, int timeout)
+{
+	if (data->ct_timer > 0)
+		g_source_remove(data->ct_timer);
+
+	data->ct_timer = g_timeout_add_seconds(timeout, policy_connect_ct,
+									data);
+}
+
+static struct policy_data *find_data(struct btd_device *dev)
+{
+	GSList *l;
+
+	for (l = devices; l; l = l->next) {
+		struct policy_data *data = l->data;
+
+		if (data->dev == dev)
+			return data;
+	}
+
+	return NULL;
+}
+
+static void policy_remove(void *user_data)
+{
+	struct policy_data *data = user_data;
+
+	if (data->source_timer > 0)
+		g_source_remove(data->source_timer);
+
+	if (data->sink_timer > 0)
+		g_source_remove(data->sink_timer);
+
+	if (data->ct_timer > 0)
+		g_source_remove(data->ct_timer);
+
+	if (data->tg_timer > 0)
+		g_source_remove(data->tg_timer);
+
+	g_free(data);
+}
+
+static struct policy_data *policy_get_data(struct btd_device *dev)
+{
+	struct policy_data *data;
+
+	data = find_data(dev);
+	if (data != NULL)
+		return data;
+
+	data = g_new0(struct policy_data, 1);
+	data->dev = dev;
+
+	devices = g_slist_prepend(devices, data);
+
+	return data;
+}
+
+static gboolean policy_connect_sink(gpointer user_data)
+{
+	struct policy_data *data = user_data;
+	struct btd_service *service;
+
+	data->source_timer = 0;
+	data->sink_retries++;
+
+	service = btd_device_get_service(data->dev, A2DP_SINK_UUID);
+	if (service != NULL)
+		policy_connect(data, service);
+
+	return FALSE;
+}
+
+static void policy_set_sink_timer(struct policy_data *data)
+{
+	if (data->sink_timer > 0)
+		g_source_remove(data->sink_timer);
+
+	data->sink_timer = g_timeout_add_seconds(SINK_RETRY_TIMEOUT,
+							policy_connect_sink,
+							data);
+}
+
+static void sink_cb(struct btd_service *service, btd_service_state_t old_state,
+						btd_service_state_t new_state)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct policy_data *data;
+	struct btd_service *controller;
+
+	controller = btd_device_get_service(dev, AVRCP_REMOTE_UUID);
+	if (controller == NULL)
+		return;
+
+	data = policy_get_data(dev);
+
+	switch (new_state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		if (data->sink_timer > 0) {
+			g_source_remove(data->sink_timer);
+			data->sink_timer = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+			int err = btd_service_get_error(service);
+
+			if (err == -EAGAIN) {
+				if (data->sink_retries < SINK_RETRIES)
+					policy_set_sink_timer(data);
+				else
+					data->sink_retries = 0;
+				break;
+			} else if (data->sink_timer > 0) {
+				g_source_remove(data->sink_timer);
+				data->sink_timer = 0;
+			}
+		}
+
+		if (data->ct_timer > 0) {
+			g_source_remove(data->ct_timer);
+			data->ct_timer = 0;
+		} else if (btd_service_get_state(controller) !=
+						BTD_SERVICE_STATE_DISCONNECTED)
+			policy_disconnect(data, controller);
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+		break;
+	case BTD_SERVICE_STATE_CONNECTED:
+		if (data->sink_timer > 0) {
+			g_source_remove(data->sink_timer);
+			data->sink_timer = 0;
+		}
+
+		/* Check if service initiate the connection then proceed
+		 * immediatelly otherwise set timer
+		 */
+		if (old_state == BTD_SERVICE_STATE_CONNECTING)
+			policy_connect(data, controller);
+		else if (btd_service_get_state(controller) !=
+						BTD_SERVICE_STATE_CONNECTED)
+			policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		break;
+	}
+}
+
+static gboolean policy_connect_tg(gpointer user_data)
+{
+	struct policy_data *data = user_data;
+	struct btd_service *service;
+
+	data->tg_timer = 0;
+	data->tg_retries++;
+
+	service = btd_device_get_service(data->dev, AVRCP_TARGET_UUID);
+	if (service != NULL)
+		policy_connect(data, service);
+
+	return FALSE;
+}
+
+static void policy_set_tg_timer(struct policy_data *data, int timeout)
+{
+	if (data->tg_timer > 0)
+		g_source_remove(data->tg_timer);
+
+	data->tg_timer = g_timeout_add_seconds(timeout, policy_connect_tg,
+							data);
+}
+
+static gboolean policy_connect_source(gpointer user_data)
+{
+	struct policy_data *data = user_data;
+	struct btd_service *service;
+
+	data->source_timer = 0;
+	data->source_retries++;
+
+	service = btd_device_get_service(data->dev, A2DP_SOURCE_UUID);
+	if (service != NULL)
+		policy_connect(data, service);
+
+	return FALSE;
+}
+
+static void policy_set_source_timer(struct policy_data *data)
+{
+	if (data->source_timer > 0)
+		g_source_remove(data->source_timer);
+
+	data->source_timer = g_timeout_add_seconds(SOURCE_RETRY_TIMEOUT,
+							policy_connect_source,
+							data);
+}
+
+static void source_cb(struct btd_service *service,
+						btd_service_state_t old_state,
+						btd_service_state_t new_state)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct policy_data *data;
+	struct btd_service *target;
+
+	target = btd_device_get_service(dev, AVRCP_TARGET_UUID);
+	if (target == NULL)
+		return;
+
+	data = policy_get_data(dev);
+
+	switch (new_state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		if (data->source_timer > 0) {
+			g_source_remove(data->source_timer);
+			data->source_timer = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+			int err = btd_service_get_error(service);
+
+			if (err == -EAGAIN) {
+				if (data->source_retries < SOURCE_RETRIES)
+					policy_set_source_timer(data);
+				else
+					data->source_retries = 0;
+				break;
+			} else if (data->source_timer > 0) {
+				g_source_remove(data->source_timer);
+				data->source_timer = 0;
+			}
+		}
+
+		if (data->tg_timer > 0) {
+			g_source_remove(data->tg_timer);
+			data->tg_timer = 0;
+		} else if (btd_service_get_state(target) !=
+						BTD_SERVICE_STATE_DISCONNECTED)
+			policy_disconnect(data, target);
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+		break;
+	case BTD_SERVICE_STATE_CONNECTED:
+		if (data->source_timer > 0) {
+			g_source_remove(data->source_timer);
+			data->source_timer = 0;
+		}
+
+		/* Check if service initiate the connection then proceed
+		 * immediatelly otherwise set timer
+		 */
+		if (old_state == BTD_SERVICE_STATE_CONNECTING)
+			policy_connect(data, target);
+		else if (btd_service_get_state(target) !=
+						BTD_SERVICE_STATE_CONNECTED)
+			policy_set_tg_timer(data, CONTROL_CONNECT_TIMEOUT);
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		break;
+	}
+}
+
+static void controller_cb(struct btd_service *service,
+						btd_service_state_t old_state,
+						btd_service_state_t new_state)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct policy_data *data;
+
+	data = find_data(dev);
+	if (data == NULL)
+		return;
+
+	switch (new_state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		if (data->ct_timer > 0) {
+			g_source_remove(data->ct_timer);
+			data->ct_timer = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+			int err = btd_service_get_error(service);
+
+			if (err == -EAGAIN) {
+				if (data->ct_retries < CT_RETRIES)
+					policy_set_ct_timer(data,
+							CT_RETRY_TIMEOUT);
+				else
+					data->ct_retries = 0;
+				break;
+			} else if (data->ct_timer > 0) {
+				g_source_remove(data->ct_timer);
+				data->ct_timer = 0;
+			}
+		} else if (old_state == BTD_SERVICE_STATE_CONNECTED) {
+			data->ct_retries = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+		break;
+	case BTD_SERVICE_STATE_CONNECTED:
+		if (data->ct_timer > 0) {
+			g_source_remove(data->ct_timer);
+			data->ct_timer = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		break;
+	}
+}
+
+static void target_cb(struct btd_service *service,
+						btd_service_state_t old_state,
+						btd_service_state_t new_state)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct policy_data *data;
+
+	data = find_data(dev);
+	if (data == NULL)
+		return;
+
+	switch (new_state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		if (data->tg_timer > 0) {
+			g_source_remove(data->tg_timer);
+			data->tg_timer = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+			int err = btd_service_get_error(service);
+
+			if (err == -EAGAIN) {
+				if (data->tg_retries < TG_RETRIES)
+					policy_set_tg_timer(data,
+							TG_RETRY_TIMEOUT);
+				else
+					data->tg_retries = 0;
+				break;
+			} else if (data->tg_timer > 0) {
+				g_source_remove(data->tg_timer);
+				data->tg_timer = 0;
+			}
+		} else if (old_state == BTD_SERVICE_STATE_CONNECTED) {
+			data->tg_retries = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+		break;
+	case BTD_SERVICE_STATE_CONNECTED:
+		if (data->tg_timer > 0) {
+			g_source_remove(data->tg_timer);
+			data->tg_timer = 0;
+		}
+		break;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		break;
+	}
+}
+
+static void reconnect_reset(struct reconnect_data *reconnect)
+{
+	reconnect->attempt = 0;
+
+	if (reconnect->timer > 0) {
+		g_source_remove(reconnect->timer);
+		reconnect->timer = 0;
+	}
+}
+
+static bool reconnect_match(const char *uuid)
+{
+	char **str;
+
+	if (!reconnect_uuids)
+		return false;
+
+	for (str = reconnect_uuids; *str; str++) {
+		if (!bt_uuid_strcmp(uuid, *str))
+			return true;
+	}
+
+	return false;
+}
+
+static struct reconnect_data *reconnect_find(struct btd_device *dev)
+{
+	GSList *l;
+
+	for (l = reconnects; l; l = g_slist_next(l)) {
+		struct reconnect_data *reconnect = l->data;
+
+		if (reconnect->dev == dev)
+			return reconnect;
+	}
+
+	return NULL;
+}
+
+static struct reconnect_data *reconnect_add(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct reconnect_data *reconnect;
+
+	reconnect = reconnect_find(dev);
+	if (!reconnect) {
+		reconnect = g_new0(struct reconnect_data, 1);
+		reconnect->dev = dev;
+		reconnects = g_slist_append(reconnects, reconnect);
+	}
+
+	if (g_slist_find(reconnect->services, service))
+		return reconnect;
+
+	reconnect->services = g_slist_append(reconnect->services,
+						btd_service_ref(service));
+
+	return reconnect;
+}
+
+static void reconnect_destroy(gpointer data)
+{
+	struct reconnect_data *reconnect = data;
+
+	if (reconnect->timer > 0)
+		g_source_remove(reconnect->timer);
+
+	g_slist_free_full(reconnect->services,
+					(GDestroyNotify) btd_service_unref);
+	g_free(reconnect);
+}
+
+static void reconnect_remove(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct reconnect_data *reconnect;
+	GSList *l;
+
+	reconnect = reconnect_find(dev);
+	if (!reconnect)
+		return;
+
+	l = g_slist_find(reconnect->services, service);
+	if (!l)
+		return;
+
+	reconnect->services = g_slist_delete_link(reconnect->services, l);
+	btd_service_unref(service);
+
+	if (reconnect->services)
+		return;
+
+	reconnects = g_slist_remove(reconnects, reconnect);
+
+	if (reconnect->timer > 0)
+		g_source_remove(reconnect->timer);
+
+	g_free(reconnect);
+}
+
+static void service_cb(struct btd_service *service,
+						btd_service_state_t old_state,
+						btd_service_state_t new_state,
+						void *user_data)
+{
+	struct btd_profile *profile = btd_service_get_profile(service);
+	struct reconnect_data *reconnect;
+
+	if (g_str_equal(profile->remote_uuid, A2DP_SINK_UUID))
+		sink_cb(service, old_state, new_state);
+	else if (g_str_equal(profile->remote_uuid, A2DP_SOURCE_UUID))
+		source_cb(service, old_state, new_state);
+	else if (g_str_equal(profile->remote_uuid, AVRCP_REMOTE_UUID))
+		controller_cb(service, old_state, new_state);
+	else if (g_str_equal(profile->remote_uuid, AVRCP_TARGET_UUID))
+		target_cb(service, old_state, new_state);
+
+	/*
+	 * Return if the reconnection feature is not enabled (all
+	 * subsequent code in this function is about that).
+	 */
+	if (!reconnect_uuids || !reconnect_uuids[0])
+		return;
+
+	/*
+	 * We're only interested in reconnecting profiles which have set
+	 * auto_connect to true.
+	 */
+	if (!profile->auto_connect)
+		return;
+
+	/*
+	 * If the service went away remove it from the reconnection
+	 * tracking. The function will remove the entire tracking data
+	 * if this was the last service for the device.
+	 */
+	if (new_state == BTD_SERVICE_STATE_UNAVAILABLE) {
+		reconnect_remove(service);
+		return;
+	}
+
+	if (new_state != BTD_SERVICE_STATE_CONNECTED)
+		return;
+
+	/*
+	 * Add an entry to track reconnections. The function will return
+	 * an existing entry if there is one.
+	 */
+	reconnect = reconnect_add(service);
+
+	reconnect->active = false;
+	reconnect_reset(reconnect);
+
+	/*
+	 * Should this device be reconnected? A matching UUID might not
+	 * be the first profile that's connected so we might have an
+	 * entry but with the reconnect flag set to false.
+	 */
+	if (!reconnect->reconnect)
+		reconnect->reconnect = reconnect_match(profile->remote_uuid);
+
+	DBG("Added %s reconnect %u", profile->name, reconnect->reconnect);
+}
+
+static gboolean reconnect_timeout(gpointer data)
+{
+	struct reconnect_data *reconnect = data;
+	int err;
+
+	DBG("Reconnecting profiles");
+
+	/* Mark the GSource as invalid */
+	reconnect->timer = 0;
+
+	err = btd_device_connect_services(reconnect->dev, reconnect->services);
+	if (err < 0) {
+		error("Reconnecting services failed: %s (%d)",
+							strerror(-err), -err);
+		reconnect_reset(reconnect);
+		return FALSE;
+	}
+
+	reconnect->active = true;
+	reconnect->attempt++;
+
+	return FALSE;
+}
+
+static void reconnect_set_timer(struct reconnect_data *reconnect)
+{
+	static int timeout = 0;
+
+	reconnect->attempt++;
+
+	if (reconnect->attempt < reconnect_intervals_len)
+		timeout = reconnect_intervals[reconnect->attempt];
+
+	DBG("%d seconds", timeout);
+
+	reconnect->timer = g_timeout_add_seconds(timeout, reconnect_timeout,
+								reconnect);
+}
+
+static void disconnect_cb(struct btd_device *dev, uint8_t reason)
+{
+	struct reconnect_data *reconnect;
+
+	DBG("reason %u", reason);
+
+	if (reason != MGMT_DEV_DISCONN_TIMEOUT)
+		return;
+
+	reconnect = reconnect_find(dev);
+	if (!reconnect || !reconnect->reconnect)
+		return;
+
+	DBG("Device %s identified for auto-reconnection",
+							device_get_path(dev));
+
+	reconnect_set_timer(reconnect);
+}
+
+static void conn_fail_cb(struct btd_device *dev, uint8_t status)
+{
+	struct reconnect_data *reconnect;
+
+	DBG("status %u", status);
+
+	reconnect = reconnect_find(dev);
+	if (!reconnect || !reconnect->reconnect)
+		return;
+
+	if (!reconnect->active)
+		return;
+
+	reconnect->active = false;
+
+	/* Give up if we were powered off */
+	if (status == MGMT_STATUS_NOT_POWERED) {
+		reconnect_reset(reconnect);
+		return;
+	}
+
+	/* Reset if ReconnectAttempts was reached */
+	if (reconnect->attempt == reconnect_attempts) {
+		reconnect_reset(reconnect);
+		return;
+	}
+
+	reconnect_set_timer(reconnect);
+}
+
+static int policy_adapter_probe(struct btd_adapter *adapter)
+{
+	DBG("");
+
+	btd_adapter_restore_powered(adapter);
+
+	return 0;
+}
+
+static struct btd_adapter_driver policy_driver = {
+	.name	= "policy",
+	.probe	= policy_adapter_probe,
+};
+
+static int policy_init(void)
+{
+	GError *gerr = NULL;
+	GKeyFile *conf;
+
+	service_id = btd_service_add_state_cb(service_cb, NULL);
+
+	conf = btd_get_main_conf();
+	if (!conf) {
+		reconnect_uuids = g_strdupv((char **) default_reconnect);
+		reconnect_attempts = default_attempts;
+		reconnect_intervals_len = sizeof(default_intervals) /
+						sizeof(*reconnect_intervals);
+		reconnect_intervals = g_memdup(default_intervals,
+						reconnect_intervals_len);
+		goto done;
+	}
+
+	reconnect_uuids = g_key_file_get_string_list(conf, "Policy",
+							"ReconnectUUIDs",
+							NULL, &gerr);
+	if (gerr) {
+		g_clear_error(&gerr);
+		reconnect_uuids = g_strdupv((char **) default_reconnect);
+	}
+
+	reconnect_attempts = g_key_file_get_integer(conf, "Policy",
+							"ReconnectAttempts",
+							&gerr);
+	if (gerr) {
+		g_clear_error(&gerr);
+		reconnect_attempts = default_attempts;
+	}
+
+	reconnect_intervals = g_key_file_get_integer_list(conf, "Policy",
+					"ReconnectIntervals",
+					(size_t *) &reconnect_intervals_len,
+					&gerr);
+	if (gerr) {
+		g_clear_error(&gerr);
+		reconnect_intervals_len = sizeof(default_intervals);
+		reconnect_intervals = g_memdup(default_intervals,
+						reconnect_intervals_len);
+	}
+
+	auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable",
+									NULL);
+
+done:
+	if (reconnect_uuids && reconnect_uuids[0] && reconnect_attempts) {
+		btd_add_disconnect_cb(disconnect_cb);
+		btd_add_conn_fail_cb(conn_fail_cb);
+	}
+
+	if (auto_enable)
+		btd_register_adapter_driver(&policy_driver);
+
+	return 0;
+}
+
+static void policy_exit(void)
+{
+	btd_remove_disconnect_cb(disconnect_cb);
+	btd_remove_conn_fail_cb(conn_fail_cb);
+
+	if (reconnect_uuids)
+		g_strfreev(reconnect_uuids);
+
+	g_free(reconnect_intervals);
+
+	g_slist_free_full(reconnects, reconnect_destroy);
+
+	g_slist_free_full(devices, policy_remove);
+
+	btd_service_remove_state_cb(service_id);
+
+	if (auto_enable)
+		btd_unregister_adapter_driver(&policy_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(policy, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+						policy_init, policy_exit)
diff --git a/bluez/repo/plugins/sixaxis.c b/bluez/repo/plugins/sixaxis.c
new file mode 100644
index 0000000..fcc93bc
--- /dev/null
+++ b/bluez/repo/plugins/sixaxis.c
@@ -0,0 +1,590 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <linux/hidraw.h>
+#include <linux/input.h>
+#include <glib.h>
+#include <libudev.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/shared/util.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 leds_data {
+	char *syspath_prefix;
+	uint8_t bitmap;
+};
+
+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;
+
+static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
+{
+	uint8_t buf[18];
+	int ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = 0xf2;
+
+	ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+	if (ret < 0) {
+		error("sixaxis: failed to read device address (%s)",
+							strerror(errno));
+		return ret;
+	}
+
+	baswap(bdaddr, (bdaddr_t *) (buf + 4));
+
+	return 0;
+}
+
+static int get_master_bdaddr(int fd, bdaddr_t *bdaddr)
+{
+	uint8_t buf[8];
+	int ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	buf[0] = 0xf5;
+
+	ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+	if (ret < 0) {
+		error("sixaxis: failed to read master address (%s)",
+							strerror(errno));
+		return ret;
+	}
+
+	baswap(bdaddr, (bdaddr_t *) (buf + 2));
+
+	return 0;
+}
+
+static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
+{
+	uint8_t buf[8];
+	int ret;
+
+	buf[0] = 0xf5;
+	buf[1] = 0x01;
+
+	baswap((bdaddr_t *) (buf + 2), bdaddr);
+
+	ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
+	if (ret < 0)
+		error("sixaxis: failed to write master address (%s)",
+							strerror(errno));
+
+	return ret;
+}
+
+static uint8_t calc_leds_bitmap(int number)
+{
+	uint8_t bitmap = 0;
+
+	/* TODO we could support up to 10 (1 + 2 + 3 + 4) */
+	if (number > 7)
+		return bitmap;
+
+	if (number > 4) {
+		bitmap |= 0x10;
+		number -= 4;
+	}
+
+	bitmap |= 0x01 << number;
+
+	return bitmap;
+}
+
+static void set_leds_hidraw(int fd, uint8_t leds_bitmap)
+{
+	/*
+	 * 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,
+	};
+	int ret;
+
+	leds_report[10] = leds_bitmap;
+
+	ret = write(fd, leds_report, sizeof(leds_report));
+	if (ret == sizeof(leds_report))
+		return;
+
+	if (ret < 0)
+		error("sixaxis: failed to set LEDS (%s)", strerror(errno));
+	else
+		error("sixaxis: failed to set LEDS (%d bytes written)", ret);
+}
+
+static bool set_leds_sysfs(struct leds_data *data)
+{
+	int i;
+
+	if (!data->syspath_prefix)
+		return false;
+
+	/* 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;
+
+		snprintf(path, PATH_MAX, "%s%d/brightness",
+						data->syspath_prefix, i);
+
+		fd = open(path, O_WRONLY);
+		if (fd < 0) {
+			error("sixaxis: cannot open %s (%s)", path,
+							strerror(errno));
+			return false;
+		}
+
+		buf[0] = '0' + !!(data->bitmap & (1 << i));
+		ret = write(fd, buf, sizeof(buf));
+		close(fd);
+		if (ret != sizeof(buf))
+			return false;
+	}
+
+	return true;
+}
+
+static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct leds_data *data = user_data;
+
+	if (!data)
+		return FALSE;
+
+	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;
+}
+
+static bool setup_device(int fd, int index, struct btd_adapter *adapter)
+{
+	char device_addr[18], master_addr[18], adapter_addr[18];
+	bdaddr_t device_bdaddr, master_bdaddr;
+	const bdaddr_t *adapter_bdaddr;
+	struct btd_device *device;
+
+	if (get_device_bdaddr(fd, &device_bdaddr) < 0)
+		return false;
+
+	if (get_master_bdaddr(fd, &master_bdaddr) < 0)
+		return false;
+
+	/* 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;
+
+	adapter_bdaddr = btd_adapter_get_address(adapter);
+
+	if (bacmp(adapter_bdaddr, &master_bdaddr)) {
+		if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
+			return false;
+	}
+
+	ba2str(&device_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;
+}
+
+static char *get_leds_syspath_prefix(struct udev_device *udevice)
+{
+	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);
+
+	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);
+
+	dev_list_entry = udev_enumerate_get_list_entry(enumerate);
+	if (!dev_list_entry) {
+		syspath_prefix = NULL;
+		goto out;
+	}
+
+	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;
+
+	number = get_js_number(udevice);
+	DBG("number %d", number);
+
+	data = malloc0(sizeof(*data));
+	if (!data)
+		return NULL;
+
+	data->bitmap = calc_leds_bitmap(number);
+	if (data->bitmap == 0) {
+		leds_data_destroy(data);
+		return NULL;
+	}
+
+	/*
+	 * 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);
+
+	return data;
+}
+
+static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
+{
+	struct udev_device *hid_parent;
+	uint16_t vid, pid;
+	const char *hid_id;
+	guint i;
+
+	hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
+								"hid", NULL);
+	if (!hid_parent)
+		return -1;
+
+	hid_id = udev_device_get_property_value(hid_parent, "HID_ID");
+
+	if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3)
+		return -1;
+
+	for (i = 0; i < G_N_ELEMENTS(devices); i++) {
+		if (devices[i].vid == vid && devices[i].pid == pid)
+			return i;
+	}
+
+	return -1;
+}
+
+static void device_added(struct udev_device *udevice)
+{
+	struct btd_adapter *adapter;
+	GIOChannel *io;
+	uint16_t bus;
+	int index;
+	int fd;
+
+	adapter = btd_adapter_get_default();
+	if (!adapter)
+		return;
+
+	index = get_supported_device(udevice, &bus);
+	if (index < 0)
+		return;
+
+	info("sixaxis: compatible device connected: %s (%04X:%04X)",
+				devices[index].name, devices[index].vid,
+				devices[index].pid);
+
+	fd = open(udev_device_get_devnode(udevice), O_RDWR);
+	if (fd < 0)
+		return;
+
+	io = g_io_channel_unix_new(fd);
+
+	switch (bus) {
+	case BUS_USB:
+		if (!setup_device(fd, index, adapter))
+			break;
+
+		/* 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));
+
+		break;
+	default:
+		DBG("uknown bus type (%u)", bus);
+		break;
+	}
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_unref(io);
+}
+
+static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
+							gpointer data)
+{
+	struct udev_device *udevice;
+
+	udevice = udev_monitor_receive_device(monitor);
+	if (!udevice)
+		return TRUE;
+
+	if (!g_strcmp0(udev_device_get_action(udevice), "add"))
+		device_added(udevice);
+
+	udev_device_unref(udevice);
+
+	return TRUE;
+}
+
+static int sixaxis_init(void)
+{
+	GIOChannel *channel;
+
+	DBG("");
+
+	ctx = udev_new();
+	if (!ctx)
+		return -EIO;
+
+	monitor = udev_monitor_new_from_netlink(ctx, "udev");
+	if (!monitor) {
+		udev_unref(ctx);
+		ctx = NULL;
+
+		return -EIO;
+	}
+
+	/* Listen for newly connected hidraw interfaces */
+	udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw",
+									NULL);
+	udev_monitor_enable_receiving(monitor);
+
+	channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor));
+	watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL);
+	g_io_channel_unref(channel);
+
+	return 0;
+}
+
+static void sixaxis_exit(void)
+{
+	DBG("");
+
+	g_source_remove(watch_id);
+	watch_id = 0;
+
+	udev_monitor_unref(monitor);
+	monitor = NULL;
+
+	udev_unref(ctx);
+	ctx = NULL;
+}
+
+BLUETOOTH_PLUGIN_DEFINE(sixaxis, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW,
+						sixaxis_init, sixaxis_exit)
diff --git a/bluez/repo/plugins/wiimote.c b/bluez/repo/plugins/wiimote.c
new file mode 100644
index 0000000..0ced275
--- /dev/null
+++ b/bluez/repo/plugins/wiimote.c
@@ -0,0 +1,143 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012 David Herrmann <dh.herrmann@googlemail.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/sdp.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/log.h"
+#include "src/storage.h"
+
+/*
+ * Nintendo Wii Remote devices require the bdaddr of the host as pin input for
+ * authentication. This plugin registers a pin-callback and forces this pin
+ * to be used for authentication.
+ *
+ * There are two ways to place the wiimote into discoverable mode.
+ *  - Pressing the red-sync button on the back of the wiimote. This module
+ *    supports pairing via this method. Auto-reconnect should be possible after
+ *    the device was paired once.
+ *  - Pressing the 1+2 buttons on the front of the wiimote. This module does
+ *    not support this method since this method never enables auto-reconnect.
+ *    Hence, pairing is not needed. Use it without pairing if you want.
+ * After connecting the wiimote you should immediately connect to the input
+ * service of the wiimote. If you don't, the wiimote will close the connection.
+ * The wiimote waits about 5 seconds until it turns off again.
+ * Auto-reconnect is only enabled when pairing with the wiimote via the red
+ * sync-button and then connecting to the input service. If you do not connect
+ * to the input service, then auto-reconnect is not enabled.
+ * If enabled, the wiimote connects to the host automatically when any button
+ * is pressed.
+ */
+
+static uint16_t wii_ids[][2] = {
+	{ 0x057e, 0x0306 },		/* 1st gen */
+	{ 0x054c, 0x0306 },		/* LEGO wiimote */
+	{ 0x057e, 0x0330 },		/* 2nd gen */
+};
+
+static const char *wii_names[] = {
+	"Nintendo RVL-CNT-01",		/* 1st gen */
+	"Nintendo RVL-CNT-01-TR",	/* 2nd gen */
+	"Nintendo RVL-CNT-01-UC",	/* Wii U Pro Controller */
+	"Nintendo RVL-WBC-01",		/* Balance Board */
+};
+
+static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
+						char *pinbuf, bool *display,
+						unsigned int attempt)
+{
+	uint16_t vendor, product;
+	char addr[18], name[25];
+	unsigned int i;
+
+	/* Only try the pin code once per device. If it's not correct then it's
+	 * an unknown device. */
+	if (attempt > 1)
+		return 0;
+
+	ba2str(device_get_address(device), addr);
+
+	vendor = btd_device_get_vendor(device);
+	product = btd_device_get_product(device);
+
+	device_get_name(device, name, sizeof(name));
+
+	for (i = 0; i < G_N_ELEMENTS(wii_ids); ++i) {
+		if (vendor == wii_ids[i][0] && product == wii_ids[i][1])
+			goto found;
+	}
+
+	for (i = 0; i < G_N_ELEMENTS(wii_names); ++i) {
+		if (g_str_equal(name, wii_names[i]))
+			goto found;
+	}
+
+	return 0;
+
+found:
+	DBG("Forcing fixed pin on detected wiimote %s", addr);
+	memcpy(pinbuf, btd_adapter_get_address(adapter), 6);
+	return 6;
+}
+
+static int wii_probe(struct btd_adapter *adapter)
+{
+	btd_adapter_register_pin_cb(adapter, wii_pincb);
+
+	return 0;
+}
+
+static void wii_remove(struct btd_adapter *adapter)
+{
+	btd_adapter_unregister_pin_cb(adapter, wii_pincb);
+}
+
+static struct btd_adapter_driver wii_driver = {
+	.name	= "wiimote",
+	.probe	= wii_probe,
+	.remove	= wii_remove,
+};
+
+static int wii_init(void)
+{
+	return btd_register_adapter_driver(&wii_driver);
+}
+
+static void wii_exit(void)
+{
+	btd_unregister_adapter_driver(&wii_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(wiimote, VERSION,
+		BLUETOOTH_PLUGIN_PRIORITY_LOW, wii_init, wii_exit)
diff --git a/bluez/repo/profiles/._alert b/bluez/repo/profiles/._alert
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._alert
Binary files differ
diff --git a/bluez/repo/profiles/._audio b/bluez/repo/profiles/._audio
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._audio
Binary files differ
diff --git a/bluez/repo/profiles/._battery b/bluez/repo/profiles/._battery
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._battery
Binary files differ
diff --git a/bluez/repo/profiles/._cups b/bluez/repo/profiles/._cups
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._cups
Binary files differ
diff --git a/bluez/repo/profiles/._cyclingspeed b/bluez/repo/profiles/._cyclingspeed
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._cyclingspeed
Binary files differ
diff --git a/bluez/repo/profiles/._deviceinfo b/bluez/repo/profiles/._deviceinfo
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._deviceinfo
Binary files differ
diff --git a/bluez/repo/profiles/._gap b/bluez/repo/profiles/._gap
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._gap
Binary files differ
diff --git a/bluez/repo/profiles/._health b/bluez/repo/profiles/._health
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._health
Binary files differ
diff --git a/bluez/repo/profiles/._heartrate b/bluez/repo/profiles/._heartrate
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._heartrate
Binary files differ
diff --git a/bluez/repo/profiles/._iap b/bluez/repo/profiles/._iap
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._iap
Binary files differ
diff --git a/bluez/repo/profiles/._input b/bluez/repo/profiles/._input
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._input
Binary files differ
diff --git a/bluez/repo/profiles/._network b/bluez/repo/profiles/._network
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._network
Binary files differ
diff --git a/bluez/repo/profiles/._proximity b/bluez/repo/profiles/._proximity
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._proximity
Binary files differ
diff --git a/bluez/repo/profiles/._sap b/bluez/repo/profiles/._sap
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._sap
Binary files differ
diff --git a/bluez/repo/profiles/._scanparam b/bluez/repo/profiles/._scanparam
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._scanparam
Binary files differ
diff --git a/bluez/repo/profiles/._thermometer b/bluez/repo/profiles/._thermometer
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._thermometer
Binary files differ
diff --git a/bluez/repo/profiles/._time b/bluez/repo/profiles/._time
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/._time
Binary files differ
diff --git a/bluez/repo/profiles/alert/._server.c b/bluez/repo/profiles/alert/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/alert/._server.c
Binary files differ
diff --git a/bluez/repo/profiles/alert/server.c b/bluez/repo/profiles/alert/server.c
new file mode 100644
index 0000000..2f6e3cd
--- /dev/null
+++ b/bluez/repo/profiles/alert/server.c
@@ -0,0 +1,1044 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "attrib/att.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "attrib/att-database.h"
+#include "src/log.h"
+#include "attrib/gatt-service.h"
+#include "attrib/gattrib.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt.h"
+#include "src/profile.h"
+#include "src/error.h"
+#include "src/textfile.h"
+#include "src/attio.h"
+
+#define PHONE_ALERT_STATUS_SVC_UUID	0x180E
+#define ALERT_NOTIF_SVC_UUID		0x1811
+
+#define ALERT_STATUS_CHR_UUID		0x2A3F
+#define RINGER_CP_CHR_UUID		0x2A40
+#define RINGER_SETTING_CHR_UUID		0x2A41
+
+#define ALERT_NOTIF_CP_CHR_UUID		0x2A44
+#define UNREAD_ALERT_CHR_UUID		0x2A45
+#define NEW_ALERT_CHR_UUID		0x2A46
+#define SUPP_NEW_ALERT_CAT_CHR_UUID	0x2A47
+#define SUPP_UNREAD_ALERT_CAT_CHR_UUID	0x2A48
+
+#define ALERT_OBJECT_PATH		"/org/bluez"
+#define ALERT_INTERFACE			"org.bluez.Alert1"
+#define ALERT_AGENT_INTERFACE		"org.bluez.AlertAgent1"
+
+/* Maximum length for "Text String Information" */
+#define NEW_ALERT_MAX_INFO_SIZE		18
+/* Maximum length for New Alert Characteristic Value */
+#define NEW_ALERT_CHR_MAX_VALUE_SIZE	(NEW_ALERT_MAX_INFO_SIZE + 2)
+
+enum {
+	ENABLE_NEW_INCOMING,
+	ENABLE_UNREAD_CAT,
+	DISABLE_NEW_INCOMING,
+	DISABLE_UNREAD_CAT,
+	NOTIFY_NEW_INCOMING,
+	NOTIFY_UNREAD_CAT,
+};
+
+enum {
+	RINGER_SILENT_MODE = 1,
+	RINGER_MUTE_ONCE,
+	RINGER_CANCEL_SILENT_MODE,
+};
+
+/* Ringer Setting characteristic values */
+enum {
+	RINGER_SILENT,
+	RINGER_NORMAL,
+};
+
+enum notify_type {
+	NOTIFY_RINGER_SETTING = 0,
+	NOTIFY_ALERT_STATUS,
+	NOTIFY_NEW_ALERT,
+	NOTIFY_UNREAD_ALERT,
+	NOTIFY_SIZE,
+};
+
+struct alert_data {
+	const char *category;
+	char *srv;
+	char *path;
+	guint watcher;
+};
+
+struct alert_adapter {
+	struct btd_adapter *adapter;
+	uint16_t supp_new_alert_cat_handle;
+	uint16_t supp_unread_alert_cat_handle;
+	uint16_t hnd_ccc[NOTIFY_SIZE];
+	uint16_t hnd_value[NOTIFY_SIZE];
+};
+
+struct notify_data {
+	struct alert_adapter *al_adapter;
+	enum notify_type type;
+	uint8_t *value;
+	size_t len;
+};
+
+struct notify_callback {
+	struct notify_data *notify_data;
+	struct btd_device *device;
+	guint id;
+};
+
+static GSList *registered_alerts = NULL;
+static GSList *alert_adapters = NULL;
+static uint8_t ringer_setting = RINGER_NORMAL;
+static uint8_t alert_status = 0;
+
+static const char * const anp_categories[] = {
+	"simple",
+	"email",
+	"news",
+	"call",
+	"missed-call",
+	"sms-mms",
+	"voice-mail",
+	"schedule",
+	"high-priority",
+	"instant-message",
+};
+
+static const char * const pasp_categories[] = {
+	"ringer",
+	"vibrate",
+	"display",
+};
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct alert_adapter *al_adapter = a;
+	const struct btd_adapter *adapter = b;
+
+	return al_adapter->adapter == adapter ? 0 : -1;
+}
+
+static struct alert_adapter *find_alert_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(alert_adapters, adapter, adapter_cmp);
+
+	return l ? l->data : NULL;
+}
+
+static void alert_data_destroy(gpointer user_data)
+{
+	struct alert_data *alert = user_data;
+
+	if (alert->watcher)
+		g_dbus_remove_watch(btd_get_dbus_connection(), alert->watcher);
+
+	g_free(alert->srv);
+	g_free(alert->path);
+	g_free(alert);
+}
+
+static void alert_release(gpointer user_data)
+{
+	struct alert_data *alert = user_data;
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(alert->srv, alert->path,
+							ALERT_AGENT_INTERFACE,
+							"Release");
+	if (msg)
+		g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+	alert_data_destroy(alert);
+}
+
+static void alert_destroy(gpointer user_data)
+{
+	DBG("");
+
+	g_slist_free_full(registered_alerts, alert_release);
+	registered_alerts = NULL;
+}
+
+static const char *valid_category(const char *category)
+{
+	unsigned i;
+
+	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+		if (g_str_equal(anp_categories[i], category))
+			return anp_categories[i];
+	}
+
+	for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
+		if (g_str_equal(pasp_categories[i], category))
+			return pasp_categories[i];
+	}
+
+	return NULL;
+}
+
+static struct alert_data *get_alert_data_by_category(const char *category)
+{
+	GSList *l;
+	struct alert_data *alert;
+
+	for (l = registered_alerts; l; l = g_slist_next(l)) {
+		alert = l->data;
+		if (g_str_equal(alert->category, category))
+			return alert;
+	}
+
+	return NULL;
+}
+
+static gboolean registered_category(const char *category)
+{
+	struct alert_data *alert;
+
+	alert = get_alert_data_by_category(category);
+	if (alert)
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean pasp_category(const char *category)
+{
+	unsigned i;
+
+	for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++)
+		if (g_str_equal(category, pasp_categories[i]))
+			return TRUE;
+
+	return FALSE;
+}
+
+static gboolean valid_description(const char *category,
+						const char *description)
+{
+	if (!pasp_category(category)) {
+		if (strlen(description) >= NEW_ALERT_MAX_INFO_SIZE)
+			return FALSE;
+
+		return TRUE;
+	}
+
+	if (g_str_equal(description, "active") ||
+					g_str_equal(description, "not active"))
+		return TRUE;
+
+	if (g_str_equal(category, "ringer"))
+		if (g_str_equal(description, "enabled") ||
+					g_str_equal(description, "disabled"))
+			return TRUE;
+
+	return FALSE;
+}
+
+static gboolean valid_count(const char *category, uint16_t count)
+{
+	if (!pasp_category(category) && count > 0 && count <= 255)
+		return TRUE;
+
+	if (pasp_category(category) && count == 1)
+		return TRUE;
+
+	return FALSE;
+}
+
+static void update_supported_categories(gpointer data, gpointer user_data)
+{
+	struct alert_adapter *al_adapter = data;
+	struct btd_adapter *adapter = al_adapter->adapter;
+	uint8_t value[2];
+	unsigned int i;
+
+	memset(value, 0, sizeof(value));
+
+	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+		if (registered_category(anp_categories[i]))
+			hci_set_bit(i, value);
+	}
+
+	attrib_db_update(adapter, al_adapter->supp_new_alert_cat_handle, NULL,
+						value, sizeof(value), NULL);
+
+	/* FIXME: For now report all registered categories as supporting unread
+	 * status, until it is known which ones should be supported */
+	attrib_db_update(adapter, al_adapter->supp_unread_alert_cat_handle,
+					NULL, value, sizeof(value), NULL);
+}
+
+static void watcher_disconnect(DBusConnection *conn, void *user_data)
+{
+	struct alert_data *alert = user_data;
+
+	DBG("Category %s was disconnected", alert->category);
+
+	registered_alerts = g_slist_remove(registered_alerts, alert);
+	alert_data_destroy(alert);
+
+	g_slist_foreach(alert_adapters, update_supported_categories, NULL);
+}
+
+static gboolean is_notifiable_device(struct btd_device *device, uint16_t ccc)
+{
+	char *filename;
+	GKeyFile *key_file;
+	char handle[6];
+	char *str;
+	uint16_t val;
+	gboolean result;
+
+	sprintf(handle, "%hu", ccc);
+
+	filename = btd_device_get_storage_path(device, "ccc");
+	if (!filename) {
+		warn("Unable to get ccc storage path for device");
+		return FALSE;
+	}
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	str = g_key_file_get_string(key_file, handle, "Value", NULL);
+	if (!str) {
+		result = FALSE;
+		goto end;
+	}
+
+	val = strtol(str, NULL, 16);
+	if (!(val & 0x0001)) {
+		result = FALSE;
+		goto end;
+	}
+
+	result = TRUE;
+end:
+	g_free(str);
+	g_free(filename);
+	g_key_file_free(key_file);
+
+	return result;
+}
+
+static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct notify_callback *cb = user_data;
+
+	DBG("status=%#x", status);
+
+	btd_device_remove_attio_callback(cb->device, cb->id);
+	btd_device_unref(cb->device);
+	g_free(cb->notify_data->value);
+	g_free(cb->notify_data);
+	g_free(cb);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct notify_callback *cb = user_data;
+	struct notify_data *nd = cb->notify_data;
+	enum notify_type type = nd->type;
+	struct alert_adapter *al_adapter = nd->al_adapter;
+	size_t len;
+	uint8_t *pdu = g_attrib_get_buffer(attrib, &len);
+
+
+	switch (type) {
+	case NOTIFY_RINGER_SETTING:
+		len = enc_notification(al_adapter->hnd_value[type],
+				&ringer_setting, sizeof(ringer_setting),
+				pdu, len);
+		break;
+	case NOTIFY_ALERT_STATUS:
+		len = enc_notification(al_adapter->hnd_value[type],
+				&alert_status, sizeof(alert_status),
+				pdu, len);
+		break;
+	case NOTIFY_NEW_ALERT:
+	case NOTIFY_UNREAD_ALERT:
+		len = enc_notification(al_adapter->hnd_value[type],
+					nd->value, nd->len, pdu, len);
+		break;
+	case NOTIFY_SIZE:
+	default:
+		DBG("Unknown type, could not send notification");
+		goto end;
+	}
+
+	DBG("Send notification for handle: 0x%04x, ccc: 0x%04x",
+					al_adapter->hnd_value[type],
+					al_adapter->hnd_ccc[type]);
+
+	g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL);
+
+	return;
+
+end:
+	btd_device_remove_attio_callback(cb->device, cb->id);
+	btd_device_unref(cb->device);
+	g_free(cb->notify_data->value);
+	g_free(cb->notify_data);
+	g_free(cb);
+}
+
+static void filter_devices_notify(struct btd_device *device, void *user_data)
+{
+	struct notify_data *notify_data = user_data;
+	struct alert_adapter *al_adapter = notify_data->al_adapter;
+	enum notify_type type = notify_data->type;
+	struct notify_callback *cb;
+
+	if (!is_notifiable_device(device, al_adapter->hnd_ccc[type]))
+		return;
+
+	cb = g_new0(struct notify_callback, 1);
+	cb->notify_data = notify_data;
+	cb->device = btd_device_ref(device);
+	cb->id = btd_device_add_attio_callback(device,
+						attio_connected_cb, NULL, cb);
+}
+
+static void notify_devices(struct alert_adapter *al_adapter,
+			enum notify_type type, uint8_t *value, size_t len)
+{
+	struct notify_data *notify_data;
+
+	notify_data = g_new0(struct notify_data, 1);
+	notify_data->al_adapter = al_adapter;
+	notify_data->type = type;
+	notify_data->value = g_memdup(value, len);
+	notify_data->len = len;
+
+	btd_adapter_for_each_device(al_adapter->adapter, filter_devices_notify,
+					notify_data);
+}
+
+static void pasp_notification(enum notify_type type)
+{
+	GSList *it;
+	struct alert_adapter *al_adapter;
+
+	for (it = alert_adapters; it; it = g_slist_next(it)) {
+		al_adapter = it->data;
+
+		notify_devices(al_adapter, type, NULL, 0);
+	}
+}
+
+static DBusMessage *register_alert(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+	const char *category;
+	const char *c;
+	struct alert_data *alert;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &c,
+			DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	category = valid_category(c);
+	if (!category) {
+		DBG("Invalid category: %s", c);
+		return btd_error_invalid_args(msg);
+	}
+
+	if (registered_category(category)) {
+		DBG("Category %s already registered", category);
+		return dbus_message_new_method_return(msg);
+	}
+
+	alert = g_new0(struct alert_data, 1);
+	alert->srv = g_strdup(sender);
+	alert->path = g_strdup(path);
+	alert->category = category;
+	alert->watcher = g_dbus_add_disconnect_watch(conn, alert->srv,
+					watcher_disconnect, alert, NULL);
+
+	if (alert->watcher == 0) {
+		alert_data_destroy(alert);
+		DBG("Could not register disconnect watcher");
+		return btd_error_failed(msg,
+				"Could not register disconnect watcher");
+	}
+
+	registered_alerts = g_slist_append(registered_alerts, alert);
+
+	g_slist_foreach(alert_adapters, update_supported_categories, NULL);
+
+	DBG("RegisterAlert(\"%s\", \"%s\")", alert->category, alert->path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static void update_new_alert(gpointer data, gpointer user_data)
+{
+	struct alert_adapter *al_adapter = data;
+	struct btd_adapter *adapter = al_adapter->adapter;
+	uint8_t *value = user_data;
+
+	attrib_db_update(adapter, al_adapter->hnd_value[NOTIFY_NEW_ALERT], NULL,
+						&value[1], value[0], NULL);
+
+	notify_devices(al_adapter, NOTIFY_NEW_ALERT, &value[1], value[0]);
+}
+
+static void update_phone_alerts(const char *category, const char *description)
+{
+	unsigned int i;
+
+	if (g_str_equal(category, "ringer")) {
+		if (g_str_equal(description, "enabled")) {
+			ringer_setting = RINGER_NORMAL;
+			pasp_notification(NOTIFY_RINGER_SETTING);
+			return;
+		} else if (g_str_equal(description, "disabled")) {
+			ringer_setting = RINGER_SILENT;
+			pasp_notification(NOTIFY_RINGER_SETTING);
+			return;
+		}
+	}
+
+	for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
+		if (g_str_equal(pasp_categories[i], category)) {
+			if (g_str_equal(description, "active")) {
+				alert_status |= (1 << i);
+				pasp_notification(NOTIFY_ALERT_STATUS);
+			} else if (g_str_equal(description, "not active")) {
+				alert_status &= ~(1 << i);
+				pasp_notification(NOTIFY_ALERT_STATUS);
+			}
+			break;
+		}
+	}
+}
+
+static DBusMessage *new_alert(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	const char *category, *description;
+	struct alert_data *alert;
+	uint16_t count;
+	unsigned int i;
+	size_t dlen;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
+			DBUS_TYPE_UINT16, &count, DBUS_TYPE_STRING,
+			&description, DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	alert = get_alert_data_by_category(category);
+	if (!alert) {
+		DBG("Category %s not registered", category);
+		return btd_error_invalid_args(msg);
+	}
+
+	if (!g_str_equal(alert->srv, sender)) {
+		DBG("Sender %s is not registered in category %s", sender,
+								category);
+		return btd_error_invalid_args(msg);
+	}
+
+	if (!valid_description(category, description)) {
+		DBG("Description %s is invalid for %s category",
+							description, category);
+		return btd_error_invalid_args(msg);
+	}
+
+	if (!valid_count(category, count)) {
+		DBG("Count %d is invalid for %s category", count, category);
+		return btd_error_invalid_args(msg);
+	}
+
+	dlen = strlen(description);
+
+	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+		uint8_t value[NEW_ALERT_CHR_MAX_VALUE_SIZE + 1];
+		uint8_t *ptr = value;
+
+		if (!g_str_equal(anp_categories[i], category))
+			continue;
+
+		memset(value, 0, sizeof(value));
+
+		*ptr++ = 2; /* Attribute value size */
+		*ptr++ = i; /* Category ID (mandatory) */
+		*ptr++ = count; /* Number of New Alert (mandatory) */
+		/* Text String Information (optional) */
+		strncpy((char *) ptr, description,
+						NEW_ALERT_MAX_INFO_SIZE - 1);
+
+		if (dlen > 0)
+			*value += dlen + 1;
+
+		g_slist_foreach(alert_adapters, update_new_alert, value);
+	}
+
+	if (pasp_category(category))
+		update_phone_alerts(category, description);
+
+	DBG("NewAlert(\"%s\", %d, \"%s\")", category, count, description);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static int agent_ringer_mute_once(void)
+{
+	struct alert_data *alert;
+	DBusMessage *msg;
+
+	alert = get_alert_data_by_category("ringer");
+	if (!alert) {
+		DBG("Category ringer is not registered");
+		return -EINVAL;
+	}
+
+	msg = dbus_message_new_method_call(alert->srv, alert->path,
+					ALERT_AGENT_INTERFACE, "MuteOnce");
+	if (!msg)
+		return -ENOMEM;
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+	return 0;
+}
+
+static int agent_ringer_set_ringer(const char *mode)
+{
+	struct alert_data *alert;
+	DBusMessage *msg;
+
+	alert = get_alert_data_by_category("ringer");
+	if (!alert) {
+		DBG("Category ringer is not registered");
+		return -EINVAL;
+	}
+
+	msg = dbus_message_new_method_call(alert->srv, alert->path,
+					ALERT_AGENT_INTERFACE, "SetRinger");
+	if (!msg)
+		return -ENOMEM;
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &mode,
+							DBUS_TYPE_INVALID);
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+	return 0;
+}
+
+static void update_unread_alert(gpointer data, gpointer user_data)
+{
+	struct alert_adapter *al_adapter = data;
+	struct btd_adapter *adapter = al_adapter->adapter;
+	uint8_t *value = user_data;
+
+	attrib_db_update(adapter,
+			al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], NULL, value,
+			2, NULL);
+
+	notify_devices(al_adapter, NOTIFY_UNREAD_ALERT, value, 2);
+}
+
+static DBusMessage *unread_alert(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	struct alert_data *alert;
+	const char *category;
+	unsigned int i;
+	uint16_t count;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
+						DBUS_TYPE_UINT16, &count,
+						DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	alert = get_alert_data_by_category(category);
+	if (!alert) {
+		DBG("Category %s not registered", category);
+		return btd_error_invalid_args(msg);
+	}
+
+	if (!valid_count(category, count)) {
+		DBG("Count %d is invalid for %s category", count, category);
+		return btd_error_invalid_args(msg);
+	}
+
+	if (!g_str_equal(alert->srv, sender)) {
+		DBG("Sender %s is not registered in category %s", sender,
+								category);
+		return btd_error_invalid_args(msg);
+	}
+
+	for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+		if (g_str_equal(anp_categories[i], category)) {
+			uint8_t value[2];
+
+			value[0] = i; /* Category ID */
+			value[1] = count; /* Unread count */
+
+			g_slist_foreach(alert_adapters, update_unread_alert,
+									value);
+		}
+	}
+
+	DBG("category %s, count %d", category, count);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static uint8_t ringer_cp_write(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	DBG("a = %p", a);
+
+	if (a->len > 1) {
+		DBG("Invalid command size (%zu)", a->len);
+		return 0;
+	}
+
+	switch (a->data[0]) {
+	case RINGER_SILENT_MODE:
+		DBG("Silent Mode");
+		agent_ringer_set_ringer("disabled");
+		break;
+	case RINGER_MUTE_ONCE:
+		DBG("Mute Once");
+		agent_ringer_mute_once();
+		break;
+	case RINGER_CANCEL_SILENT_MODE:
+		DBG("Cancel Silent Mode");
+		agent_ringer_set_ringer("enabled");
+		break;
+	default:
+		DBG("Invalid command (0x%02x)", a->data[0]);
+	}
+
+	return 0;
+}
+
+static uint8_t alert_status_read(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	DBG("a = %p", a);
+
+	if (a->data == NULL || a->data[0] != alert_status)
+		attrib_db_update(adapter, a->handle, NULL, &alert_status,
+						sizeof(alert_status), NULL);
+
+	return 0;
+}
+
+static uint8_t ringer_setting_read(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	DBG("a = %p", a);
+
+	if (a->data == NULL || a->data[0] != ringer_setting)
+		attrib_db_update(adapter, a->handle, NULL, &ringer_setting,
+						sizeof(ringer_setting), NULL);
+
+	return 0;
+}
+
+static void register_phone_alert_service(struct alert_adapter *al_adapter)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID);
+
+	/* Phone Alert Status Service */
+	gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
+			/* Alert Status characteristic */
+			GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+							GATT_CHR_PROP_NOTIFY,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+			alert_status_read, al_adapter->adapter,
+			GATT_OPT_CCC_GET_HANDLE,
+			&al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS],
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+			&al_adapter->hnd_value[NOTIFY_ALERT_STATUS],
+			/* Ringer Control Point characteristic */
+			GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+			ringer_cp_write, NULL,
+			/* Ringer Setting characteristic */
+			GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+							GATT_CHR_PROP_NOTIFY,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+			ringer_setting_read, al_adapter->adapter,
+			GATT_OPT_CCC_GET_HANDLE,
+			&al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING],
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+			&al_adapter->hnd_value[NOTIFY_RINGER_SETTING],
+			GATT_OPT_INVALID);
+}
+
+static uint8_t supp_new_alert_cat_read(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint8_t value[] = { 0x00, 0x00 };
+
+	DBG("a = %p", a);
+
+	if (a->data == NULL)
+		attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
+									NULL);
+
+	return 0;
+}
+
+static uint8_t supp_unread_alert_cat_read(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint8_t value[] = { 0x00, 0x00 };
+
+	DBG("a = %p", a);
+
+	if (a->data == NULL)
+		attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
+									NULL);
+
+	return 0;
+}
+
+static uint8_t alert_notif_cp_write(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	DBG("a = %p", a);
+
+	if (a->len < 2)
+		return 0;
+
+	switch (a->data[0]) {
+	case ENABLE_NEW_INCOMING:
+		DBG("ENABLE_NEW_INCOMING: 0x%02x", a->data[1]);
+		break;
+	case ENABLE_UNREAD_CAT:
+		DBG("ENABLE_UNREAD_CAT: 0x%02x", a->data[1]);
+		break;
+	case DISABLE_NEW_INCOMING:
+		DBG("DISABLE_NEW_INCOMING: 0x%02x", a->data[1]);
+		break;
+	case DISABLE_UNREAD_CAT:
+		DBG("DISABLE_UNREAD_CAT: 0x%02x", a->data[1]);
+		break;
+	case NOTIFY_NEW_INCOMING:
+		DBG("NOTIFY_NEW_INCOMING: 0x%02x", a->data[1]);
+		break;
+	case NOTIFY_UNREAD_CAT:
+		DBG("NOTIFY_UNREAD_CAT: 0x%02x", a->data[1]);
+		break;
+	default:
+		DBG("0x%02x 0x%02x", a->data[0], a->data[1]);
+	}
+
+	return 0;
+}
+
+static void register_alert_notif_service(struct alert_adapter *al_adapter)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, ALERT_NOTIF_SVC_UUID);
+
+	/* Alert Notification Service */
+	gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
+			/* Supported New Alert Category */
+			GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+			supp_new_alert_cat_read, al_adapter->adapter,
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+			&al_adapter->supp_new_alert_cat_handle,
+			/* New Alert */
+			GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
+			GATT_OPT_CCC_GET_HANDLE,
+			&al_adapter->hnd_ccc[NOTIFY_NEW_ALERT],
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+			&al_adapter->hnd_value[NOTIFY_NEW_ALERT],
+			/* Supported Unread Alert Category */
+			GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+			supp_unread_alert_cat_read, al_adapter->adapter,
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+			&al_adapter->supp_unread_alert_cat_handle,
+			/* Unread Alert Status */
+			GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
+			GATT_OPT_CCC_GET_HANDLE,
+			&al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT],
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+			&al_adapter->hnd_value[NOTIFY_UNREAD_ALERT],
+			/* Alert Notification Control Point */
+			GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID,
+			GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+			alert_notif_cp_write, NULL,
+			GATT_OPT_INVALID);
+}
+
+static int alert_server_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct alert_adapter *al_adapter;
+
+	al_adapter = g_new0(struct alert_adapter, 1);
+	al_adapter->adapter = btd_adapter_ref(adapter);
+
+	alert_adapters = g_slist_append(alert_adapters, al_adapter);
+
+	register_phone_alert_service(al_adapter);
+	register_alert_notif_service(al_adapter);
+
+	return 0;
+}
+
+static void alert_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct alert_adapter *al_adapter;
+
+	al_adapter = find_alert_adapter(adapter);
+	if (!al_adapter)
+		return;
+
+	alert_adapters = g_slist_remove(alert_adapters, al_adapter);
+	btd_adapter_unref(al_adapter->adapter);
+
+	g_free(al_adapter);
+}
+
+static struct btd_profile alert_profile = {
+	.name = "gatt-alert-server",
+	.adapter_probe = alert_server_probe,
+	.adapter_remove = alert_server_remove,
+};
+
+static const GDBusMethodTable alert_methods[] = {
+	{ GDBUS_METHOD("RegisterAlert",
+			GDBUS_ARGS({ "category", "s" },
+				   { "agent", "o" }), NULL,
+			register_alert) },
+	{ GDBUS_METHOD("NewAlert",
+			GDBUS_ARGS({ "category", "s" },
+				   { "count", "q" },
+				   { "description", "s" }), NULL,
+			new_alert) },
+	{ GDBUS_METHOD("UnreadAlert",
+			GDBUS_ARGS({ "category", "s" }, { "count", "q" }), NULL,
+			unread_alert) },
+	{ }
+};
+
+static int alert_server_init(void)
+{
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					ALERT_OBJECT_PATH, ALERT_INTERFACE,
+					alert_methods, NULL, NULL, NULL,
+					alert_destroy)) {
+		error("D-Bus failed to register %s interface",
+							ALERT_INTERFACE);
+		return -EIO;
+	}
+
+	return btd_profile_register(&alert_profile);
+}
+
+static void alert_server_exit(void)
+{
+	btd_profile_unregister(&alert_profile);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					ALERT_OBJECT_PATH, ALERT_INTERFACE);
+}
+
+static int alert_init(void)
+{
+	return alert_server_init();
+}
+
+static void alert_exit(void)
+{
+	alert_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+			alert_init, alert_exit)
diff --git a/bluez/repo/profiles/audio/._a2dp-codecs.h b/bluez/repo/profiles/audio/._a2dp-codecs.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._a2dp-codecs.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._a2dp.c b/bluez/repo/profiles/audio/._a2dp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._a2dp.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._a2dp.h b/bluez/repo/profiles/audio/._a2dp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._a2dp.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._avctp.c b/bluez/repo/profiles/audio/._avctp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._avctp.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._avctp.h b/bluez/repo/profiles/audio/._avctp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._avctp.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._avdtp.c b/bluez/repo/profiles/audio/._avdtp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._avdtp.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._avdtp.h b/bluez/repo/profiles/audio/._avdtp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._avdtp.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._avrcp.c b/bluez/repo/profiles/audio/._avrcp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._avrcp.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._avrcp.h b/bluez/repo/profiles/audio/._avrcp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._avrcp.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._control.c b/bluez/repo/profiles/audio/._control.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._control.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._control.h b/bluez/repo/profiles/audio/._control.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._control.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._media.c b/bluez/repo/profiles/audio/._media.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._media.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._media.h b/bluez/repo/profiles/audio/._media.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._media.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._player.c b/bluez/repo/profiles/audio/._player.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._player.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._player.h b/bluez/repo/profiles/audio/._player.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._player.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._sink.c b/bluez/repo/profiles/audio/._sink.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._sink.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._sink.h b/bluez/repo/profiles/audio/._sink.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._sink.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._source.c b/bluez/repo/profiles/audio/._source.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._source.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._source.h b/bluez/repo/profiles/audio/._source.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._source.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/._transport.c b/bluez/repo/profiles/audio/._transport.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._transport.c
Binary files differ
diff --git a/bluez/repo/profiles/audio/._transport.h b/bluez/repo/profiles/audio/._transport.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/audio/._transport.h
Binary files differ
diff --git a/bluez/repo/profiles/audio/a2dp-codecs.h b/bluez/repo/profiles/audio/a2dp-codecs.h
new file mode 100644
index 0000000..e9da0bf
--- /dev/null
+++ b/bluez/repo/profiles/audio/a2dp-codecs.h
@@ -0,0 +1,239 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 A2DP_CODEC_SBC			0x00
+#define A2DP_CODEC_MPEG12		0x01
+#define A2DP_CODEC_MPEG24		0x02
+#define A2DP_CODEC_ATRAC		0x03
+#define A2DP_CODEC_VENDOR		0xFF
+
+#define SBC_SAMPLING_FREQ_16000		(1 << 3)
+#define SBC_SAMPLING_FREQ_32000		(1 << 2)
+#define SBC_SAMPLING_FREQ_44100		(1 << 1)
+#define SBC_SAMPLING_FREQ_48000		1
+
+#define SBC_CHANNEL_MODE_MONO		(1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2)
+#define SBC_CHANNEL_MODE_STEREO		(1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO	1
+
+#define SBC_BLOCK_LENGTH_4		(1 << 3)
+#define SBC_BLOCK_LENGTH_8		(1 << 2)
+#define SBC_BLOCK_LENGTH_12		(1 << 1)
+#define SBC_BLOCK_LENGTH_16		1
+
+#define SBC_SUBBANDS_4			(1 << 1)
+#define SBC_SUBBANDS_8			1
+
+#define SBC_ALLOCATION_SNR		(1 << 1)
+#define SBC_ALLOCATION_LOUDNESS		1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#define MPEG_CHANNEL_MODE_MONO		(1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL	(1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO	(1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO	1
+
+#define MPEG_LAYER_MP1			(1 << 2)
+#define MPEG_LAYER_MP2			(1 << 1)
+#define MPEG_LAYER_MP3			1
+
+#define MPEG_SAMPLING_FREQ_16000	(1 << 5)
+#define MPEG_SAMPLING_FREQ_22050	(1 << 4)
+#define MPEG_SAMPLING_FREQ_24000	(1 << 3)
+#define MPEG_SAMPLING_FREQ_32000	(1 << 2)
+#define MPEG_SAMPLING_FREQ_44100	(1 << 1)
+#define MPEG_SAMPLING_FREQ_48000	1
+
+#define MPEG_BIT_RATE_VBR		0x8000
+#define MPEG_BIT_RATE_320000		0x4000
+#define MPEG_BIT_RATE_256000		0x2000
+#define MPEG_BIT_RATE_224000		0x1000
+#define MPEG_BIT_RATE_192000		0x0800
+#define MPEG_BIT_RATE_160000		0x0400
+#define MPEG_BIT_RATE_128000		0x0200
+#define MPEG_BIT_RATE_112000		0x0100
+#define MPEG_BIT_RATE_96000		0x0080
+#define MPEG_BIT_RATE_80000		0x0040
+#define MPEG_BIT_RATE_64000		0x0020
+#define MPEG_BIT_RATE_56000		0x0010
+#define MPEG_BIT_RATE_48000		0x0008
+#define MPEG_BIT_RATE_40000		0x0004
+#define MPEG_BIT_RATE_32000		0x0002
+#define MPEG_BIT_RATE_FREE		0x0001
+
+#define AAC_OBJECT_TYPE_MPEG2_AAC_LC	0x80
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LC	0x40
+#define AAC_OBJECT_TYPE_MPEG4_AAC_LTP	0x20
+#define AAC_OBJECT_TYPE_MPEG4_AAC_SCA	0x10
+
+#define AAC_SAMPLING_FREQ_8000		0x0800
+#define AAC_SAMPLING_FREQ_11025		0x0400
+#define AAC_SAMPLING_FREQ_12000		0x0200
+#define AAC_SAMPLING_FREQ_16000		0x0100
+#define AAC_SAMPLING_FREQ_22050		0x0080
+#define AAC_SAMPLING_FREQ_24000		0x0040
+#define AAC_SAMPLING_FREQ_32000		0x0020
+#define AAC_SAMPLING_FREQ_44100		0x0010
+#define AAC_SAMPLING_FREQ_48000		0x0008
+#define AAC_SAMPLING_FREQ_64000		0x0004
+#define AAC_SAMPLING_FREQ_88200		0x0002
+#define AAC_SAMPLING_FREQ_96000		0x0001
+
+#define AAC_CHANNELS_1			0x02
+#define AAC_CHANNELS_2			0x01
+
+#define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \
+					(a).bitrate2 << 8 | (a).bitrate3)
+#define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2)
+
+#define AAC_SET_BITRATE(a, b) \
+	do { \
+		(a).bitrate1 = (b >> 16) & 0x7f; \
+		(a).bitrate2 = (b >> 8) & 0xff; \
+		(a).bitrate3 = b & 0xff; \
+	} while (0)
+#define AAC_SET_FREQUENCY(a, f) \
+	do { \
+		(a).frequency1 = (f >> 4) & 0xff; \
+		(a).frequency2 = f & 0x0f; \
+	} while (0)
+
+#define AAC_INIT_BITRATE(b) \
+	.bitrate1 = (b >> 16) & 0x7f, \
+	.bitrate2 = (b >> 8) & 0xff, \
+	.bitrate3 = b & 0xff,
+#define AAC_INIT_FREQUENCY(f) \
+	.frequency1 = (f >> 4) & 0xff, \
+	.frequency2 = f & 0x0f,
+
+#define APTX_VENDOR_ID			0x0000004f
+#define APTX_CODEC_ID			0x0001
+
+#define APTX_CHANNEL_MODE_MONO		0x01
+#define APTX_CHANNEL_MODE_STEREO	0x02
+
+#define APTX_SAMPLING_FREQ_16000	0x08
+#define APTX_SAMPLING_FREQ_32000	0x04
+#define APTX_SAMPLING_FREQ_44100	0x02
+#define APTX_SAMPLING_FREQ_48000	0x01
+
+#define LDAC_VENDOR_ID			0x0000012d
+#define LDAC_CODEC_ID			0x00aa
+
+typedef struct {
+	uint32_t vendor_id;
+	uint16_t codec_id;
+} __attribute__ ((packed)) a2dp_vendor_codec_t;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct {
+	uint8_t channel_mode:4;
+	uint8_t frequency:4;
+	uint8_t allocation_method:2;
+	uint8_t subbands:2;
+	uint8_t block_length:4;
+	uint8_t min_bitpool;
+	uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+	uint8_t channel_mode:4;
+	uint8_t crc:1;
+	uint8_t layer:3;
+	uint8_t frequency:6;
+	uint8_t mpf:1;
+	uint8_t rfa:1;
+	uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+typedef struct {
+	uint8_t object_type;
+	uint8_t frequency1;
+	uint8_t rfa:2;
+	uint8_t channels:2;
+	uint8_t frequency2:4;
+	uint8_t bitrate1:7;
+	uint8_t vbr:1;
+	uint8_t bitrate2;
+	uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t channel_mode:4;
+	uint8_t frequency:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t unknown[2];
+} __attribute__ ((packed)) a2dp_ldac_t;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct {
+	uint8_t frequency:4;
+	uint8_t channel_mode:4;
+	uint8_t block_length:4;
+	uint8_t subbands:2;
+	uint8_t allocation_method:2;
+	uint8_t min_bitpool;
+	uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+	uint8_t layer:3;
+	uint8_t crc:1;
+	uint8_t channel_mode:4;
+	uint8_t rfa:1;
+	uint8_t mpf:1;
+	uint8_t frequency:6;
+	uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+typedef struct {
+	uint8_t object_type;
+	uint8_t frequency1;
+	uint8_t frequency2:4;
+	uint8_t channels:2;
+	uint8_t rfa:2;
+	uint8_t vbr:1;
+	uint8_t bitrate1:7;
+	uint8_t bitrate2;
+	uint8_t bitrate3;
+} __attribute__ ((packed)) a2dp_aac_t;
+
+typedef struct {
+	a2dp_vendor_codec_t info;
+	uint8_t frequency:4;
+	uint8_t channel_mode:4;
+} __attribute__ ((packed)) a2dp_aptx_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/bluez/repo/profiles/audio/a2dp.c b/bluez/repo/profiles/audio/a2dp.c
new file mode 100644
index 0000000..b0fea7c
--- /dev/null
+++ b/bluez/repo/profiles/audio/a2dp.c
@@ -0,0 +1,2482 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.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/log.h"
+#include "src/sdpd.h"
+#include "src/shared/queue.h"
+
+#include "btio/btio.h"
+
+#include "avdtp.h"
+#include "sink.h"
+#include "source.h"
+#include "a2dp.h"
+#include "a2dp-codecs.h"
+#include "media.h"
+
+/* The duration that streams without users are allowed to stay in
+ * STREAMING state. */
+#define SUSPEND_TIMEOUT 5
+#define RECONFIGURE_TIMEOUT 500
+
+#define AVDTP_PSM 25
+
+struct a2dp_sep {
+	struct a2dp_server *server;
+	struct a2dp_endpoint *endpoint;
+	uint8_t type;
+	uint8_t codec;
+	struct avdtp_local_sep *lsep;
+	struct avdtp *session;
+	struct avdtp_stream *stream;
+	guint suspend_timer;
+	gboolean delay_reporting;
+	gboolean locked;
+	gboolean suspending;
+	gboolean starting;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct a2dp_setup_cb {
+	struct a2dp_setup *setup;
+	a2dp_discover_cb_t discover_cb;
+	a2dp_select_cb_t select_cb;
+	a2dp_config_cb_t config_cb;
+	a2dp_stream_cb_t resume_cb;
+	a2dp_stream_cb_t suspend_cb;
+	guint source_id;
+	void *user_data;
+	unsigned int id;
+};
+
+struct a2dp_setup {
+	struct avdtp *session;
+	struct a2dp_sep *sep;
+	struct avdtp_remote_sep *rsep;
+	struct avdtp_stream *stream;
+	struct avdtp_error *err;
+	avdtp_set_configuration_cb setconf_cb;
+	GSList *seps;
+	GSList *caps;
+	gboolean reconfigure;
+	gboolean start;
+	GSList *cb;
+	int ref;
+};
+
+struct a2dp_server {
+	struct btd_adapter *adapter;
+	GSList *sinks;
+	GSList *sources;
+	uint32_t source_record_id;
+	uint32_t sink_record_id;
+	gboolean sink_enabled;
+	gboolean source_enabled;
+	GIOChannel *io;
+	struct queue *seps;
+	struct queue *channels;
+};
+
+struct a2dp_channel {
+	struct a2dp_server *server;
+	struct btd_device *device;
+	GIOChannel *io;
+	guint io_id;
+	unsigned int state_id;
+	unsigned int auth_id;
+	struct avdtp *session;
+};
+
+static GSList *servers = NULL;
+static GSList *setups = NULL;
+static unsigned int cb_id = 0;
+
+static struct a2dp_setup *setup_ref(struct a2dp_setup *setup)
+{
+	setup->ref++;
+
+	DBG("%p: ref=%d", setup, setup->ref);
+
+	return setup;
+}
+
+static struct a2dp_setup *setup_new(struct avdtp *session)
+{
+	struct a2dp_setup *setup;
+
+	setup = g_new0(struct a2dp_setup, 1);
+	setup->session = avdtp_ref(session);
+	setups = g_slist_append(setups, setup);
+
+	return setup;
+}
+
+static void setup_free(struct a2dp_setup *s)
+{
+	DBG("%p", s);
+
+	setups = g_slist_remove(setups, s);
+	if (s->session)
+		avdtp_unref(s->session);
+	g_slist_free_full(s->cb, g_free);
+	g_slist_free_full(s->caps, g_free);
+	g_free(s);
+}
+
+static void setup_unref(struct a2dp_setup *setup)
+{
+	setup->ref--;
+
+	DBG("%p: ref=%d", setup, setup->ref);
+
+	if (setup->ref > 0)
+		return;
+
+	setup_free(setup);
+}
+
+static struct a2dp_setup_cb *setup_cb_new(struct a2dp_setup *setup)
+{
+	struct a2dp_setup_cb *cb;
+
+	cb = g_new0(struct a2dp_setup_cb, 1);
+	cb->setup = setup;
+	cb->id = ++cb_id;
+
+	setup->cb = g_slist_append(setup->cb, cb);
+	return cb;
+}
+
+static void setup_cb_free(struct a2dp_setup_cb *cb)
+{
+	struct a2dp_setup *setup = cb->setup;
+
+	if (cb->source_id)
+		g_source_remove(cb->source_id);
+
+	setup->cb = g_slist_remove(setup->cb, cb);
+	setup_unref(cb->setup);
+	g_free(cb);
+}
+
+static void finalize_setup_errno(struct a2dp_setup *s, int err,
+					GSourceFunc cb1, ...)
+{
+	GSourceFunc finalize;
+	va_list args;
+	struct avdtp_error avdtp_err;
+
+	if (err < 0) {
+		avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
+		s->err = &avdtp_err;
+	}
+
+	va_start(args, cb1);
+	finalize = cb1;
+	setup_ref(s);
+	while (finalize != NULL) {
+		finalize(s);
+		finalize = va_arg(args, GSourceFunc);
+	}
+	setup_unref(s);
+	va_end(args);
+}
+
+static gboolean finalize_config(gpointer data)
+{
+	struct a2dp_setup *s = data;
+	GSList *l;
+	struct avdtp_stream *stream = s->err ? NULL : s->stream;
+
+	for (l = s->cb; l != NULL; ) {
+		struct a2dp_setup_cb *cb = l->data;
+
+		l = l->next;
+
+		if (!cb->config_cb)
+			continue;
+
+		cb->config_cb(s->session, s->sep, stream, s->err,
+							cb->user_data);
+		setup_cb_free(cb);
+	}
+
+	return FALSE;
+}
+
+static gboolean finalize_resume(gpointer data)
+{
+	struct a2dp_setup *s = data;
+	GSList *l;
+
+	for (l = s->cb; l != NULL; ) {
+		struct a2dp_setup_cb *cb = l->data;
+
+		l = l->next;
+
+		if (!cb->resume_cb)
+			continue;
+
+		cb->resume_cb(s->session, s->err, cb->user_data);
+		setup_cb_free(cb);
+	}
+
+	return FALSE;
+}
+
+static gboolean finalize_suspend(gpointer data)
+{
+	struct a2dp_setup *s = data;
+	GSList *l;
+
+	for (l = s->cb; l != NULL; ) {
+		struct a2dp_setup_cb *cb = l->data;
+
+		l = l->next;
+
+		if (!cb->suspend_cb)
+			continue;
+
+		cb->suspend_cb(s->session, s->err, cb->user_data);
+		setup_cb_free(cb);
+	}
+
+	return FALSE;
+}
+
+static void finalize_select(struct a2dp_setup *s)
+{
+	GSList *l;
+
+	for (l = s->cb; l != NULL; ) {
+		struct a2dp_setup_cb *cb = l->data;
+
+		l = l->next;
+
+		if (!cb->select_cb)
+			continue;
+
+		cb->select_cb(s->session, s->sep, s->caps, cb->user_data);
+		setup_cb_free(cb);
+	}
+}
+
+static void finalize_discover(struct a2dp_setup *s)
+{
+	GSList *l;
+
+	for (l = s->cb; l != NULL; ) {
+		struct a2dp_setup_cb *cb = l->data;
+
+		l = l->next;
+
+		if (!cb->discover_cb)
+			continue;
+
+		cb->discover_cb(s->session, s->seps, s->err, cb->user_data);
+		setup_cb_free(cb);
+	}
+}
+
+static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
+{
+	GSList *l;
+
+	for (l = setups; l != NULL; l = l->next) {
+		struct a2dp_setup *setup = l->data;
+
+		if (setup->session == session)
+			return setup;
+	}
+
+	return NULL;
+}
+
+static struct a2dp_setup *a2dp_setup_get(struct avdtp *session)
+{
+	struct a2dp_setup *setup;
+
+	setup = find_setup_by_session(session);
+	if (!setup) {
+		setup = setup_new(session);
+		if (!setup)
+			return NULL;
+	}
+
+	return setup_ref(setup);
+}
+
+static struct a2dp_setup *find_setup_by_stream(struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = setups; l != NULL; l = l->next) {
+		struct a2dp_setup *setup = l->data;
+
+		if (setup->stream == stream)
+			return setup;
+	}
+
+	return NULL;
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data)
+{
+	struct a2dp_sep *sep = user_data;
+
+	if (new_state == AVDTP_STATE_OPEN) {
+		struct a2dp_setup *setup;
+		int err;
+
+		setup = find_setup_by_stream(stream);
+		if (!setup || !setup->start)
+			return;
+
+		setup->start = FALSE;
+
+		err = avdtp_start(setup->session, stream);
+		if (err < 0 && err != -EINPROGRESS) {
+			error("avdtp_start: %s (%d)", strerror(-err), -err);
+			finalize_setup_errno(setup, err, finalize_resume,
+									NULL);
+			return;
+		}
+
+		sep->starting = TRUE;
+
+		return;
+	}
+
+	if (new_state != AVDTP_STATE_IDLE)
+		return;
+
+	if (sep->suspend_timer) {
+		g_source_remove(sep->suspend_timer);
+		sep->suspend_timer = 0;
+	}
+
+	if (sep->session) {
+		avdtp_unref(sep->session);
+		sep->session = NULL;
+	}
+
+	sep->stream = NULL;
+
+	if (sep->endpoint && sep->endpoint->clear_configuration)
+		sep->endpoint->clear_configuration(sep, sep->user_data);
+}
+
+static gboolean auto_config(gpointer data)
+{
+	struct a2dp_setup *setup = data;
+	struct btd_device *dev = NULL;
+	struct btd_service *service;
+
+	/* Check if configuration was aborted */
+	if (setup->sep->stream == NULL)
+		return FALSE;
+
+	if (setup->err != NULL)
+		goto done;
+
+	dev = avdtp_get_device(setup->session);
+
+	avdtp_stream_add_cb(setup->session, setup->stream,
+				stream_state_changed, setup->sep);
+
+	if (setup->sep->type == AVDTP_SEP_TYPE_SOURCE) {
+		service = btd_device_get_service(dev, A2DP_SINK_UUID);
+		sink_new_stream(service, setup->session, setup->stream);
+	} else {
+		service = btd_device_get_service(dev, A2DP_SOURCE_UUID);
+		source_new_stream(service, setup->session, setup->stream);
+	}
+
+done:
+	if (setup->setconf_cb)
+		setup->setconf_cb(setup->session, setup->stream, setup->err);
+
+	finalize_config(setup);
+
+	if (setup->err) {
+		g_free(setup->err);
+		setup->err = NULL;
+	}
+
+	setup_unref(setup);
+
+	return FALSE;
+}
+
+static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
+{
+	if (ret == FALSE) {
+		setup->err = g_new(struct avdtp_error, 1);
+		avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+					AVDTP_UNSUPPORTED_CONFIGURATION);
+	}
+
+	auto_config(setup);
+}
+
+static gboolean endpoint_match_codec_ind(struct avdtp *session,
+				struct avdtp_media_codec_capability *codec,
+				void *user_data)
+{
+	struct a2dp_sep *sep = user_data;
+	a2dp_vendor_codec_t *remote_codec;
+	a2dp_vendor_codec_t *local_codec;
+	uint8_t *capabilities;
+	size_t length;
+
+	if (codec->media_codec_type != A2DP_CODEC_VENDOR)
+		return TRUE;
+
+	if (sep->endpoint == NULL)
+		return FALSE;
+
+	length = sep->endpoint->get_capabilities(sep, &capabilities,
+							sep->user_data);
+	if (length < sizeof(a2dp_vendor_codec_t))
+		return FALSE;
+
+	local_codec = (a2dp_vendor_codec_t *) capabilities;
+	remote_codec = (a2dp_vendor_codec_t *) codec->data;
+
+	if (remote_codec->vendor_id != local_codec->vendor_id)
+		return FALSE;
+
+	if (remote_codec->codec_id != local_codec->codec_id)
+		return FALSE;
+
+	DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
+						btohs(remote_codec->codec_id));
+	return TRUE;
+}
+
+static gboolean endpoint_setconf_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						GSList *caps,
+						avdtp_set_configuration_cb cb,
+						void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Set_Configuration_Ind", sep);
+	else
+		DBG("Source %p: Set_Configuration_Ind", sep);
+
+	setup = a2dp_setup_get(session);
+	if (!session)
+		return FALSE;
+
+	a2dp_sep->stream = stream;
+	setup->sep = a2dp_sep;
+	setup->stream = stream;
+	setup->setconf_cb = cb;
+
+	for (; caps != NULL; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+		struct avdtp_media_codec_capability *codec;
+		gboolean ret;
+
+		if (cap->category == AVDTP_DELAY_REPORTING &&
+					!a2dp_sep->delay_reporting) {
+			setup->err = g_new(struct avdtp_error, 1);
+			avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+					AVDTP_UNSUPPORTED_CONFIGURATION);
+			goto done;
+		}
+
+		if (cap->category != AVDTP_MEDIA_CODEC)
+			continue;
+
+		codec = (struct avdtp_media_codec_capability *) cap->data;
+
+		if (codec->media_codec_type != a2dp_sep->codec) {
+			setup->err = g_new(struct avdtp_error, 1);
+			avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+					AVDTP_UNSUPPORTED_CONFIGURATION);
+			goto done;
+		}
+
+		ret = a2dp_sep->endpoint->set_configuration(a2dp_sep,
+						codec->data,
+						cap->length - sizeof(*codec),
+						setup,
+						endpoint_setconf_cb,
+						a2dp_sep->user_data);
+		if (ret == 0)
+			return TRUE;
+
+		setup->err = g_new(struct avdtp_error, 1);
+		avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+					AVDTP_UNSUPPORTED_CONFIGURATION);
+		break;
+	}
+
+done:
+	g_idle_add(auto_config, setup);
+	return TRUE;
+}
+
+static gboolean endpoint_getcap_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					gboolean get_all, GSList **caps,
+					uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *codec_caps;
+	uint8_t *capabilities;
+	size_t length;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Get_Capability_Ind", sep);
+	else
+		DBG("Source %p: Get_Capability_Ind", sep);
+
+	*caps = NULL;
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	length = a2dp_sep->endpoint->get_capabilities(a2dp_sep, &capabilities,
+							a2dp_sep->user_data);
+
+	codec_caps = g_malloc0(sizeof(*codec_caps) + length);
+	codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec_caps->media_codec_type = a2dp_sep->codec;
+	memcpy(codec_caps->data, capabilities, length);
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+						sizeof(*codec_caps) + length);
+
+	*caps = g_slist_append(*caps, media_codec);
+	g_free(codec_caps);
+
+	if (get_all) {
+		struct avdtp_service_capability *delay_reporting;
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		*caps = g_slist_append(*caps, delay_reporting);
+	}
+
+	return TRUE;
+}
+
+static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret)
+{
+	int err;
+
+	if (ret == FALSE) {
+		setup->stream = NULL;
+		finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+		return;
+	}
+
+	err = avdtp_open(setup->session, setup->stream);
+	if (err == 0)
+		return;
+
+	error("Error on avdtp_open %s (%d)", strerror(-err), -err);
+	setup->stream = NULL;
+	finalize_setup_errno(setup, err, finalize_config, NULL);
+}
+
+static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+	struct btd_device *dev;
+	struct btd_service *service;
+	int ret;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Set_Configuration_Cfm", sep);
+	else
+		DBG("Source %p: Set_Configuration_Cfm", sep);
+
+	setup = find_setup_by_session(session);
+
+	if (err) {
+		if (setup) {
+			setup_ref(setup);
+			setup->err = err;
+			finalize_config(setup);
+			setup->err = NULL;
+			setup_unref(setup);
+		}
+		return;
+	}
+
+	avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+	a2dp_sep->stream = stream;
+
+	if (!setup)
+		return;
+
+	dev = avdtp_get_device(session);
+
+	/* Notify D-Bus interface of the new stream */
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) {
+		service = btd_device_get_service(dev, A2DP_SINK_UUID);
+		sink_new_stream(service, session, setup->stream);
+	} else {
+		service = btd_device_get_service(dev, A2DP_SOURCE_UUID);
+		source_new_stream(service, session, setup->stream);
+	}
+
+	/* Notify Endpoint */
+	if (a2dp_sep->endpoint) {
+		struct avdtp_service_capability *service;
+		struct avdtp_media_codec_capability *codec;
+		int err;
+
+		service = avdtp_stream_get_codec(stream);
+		codec = (struct avdtp_media_codec_capability *) service->data;
+
+		err = a2dp_sep->endpoint->set_configuration(a2dp_sep,
+						codec->data, service->length -
+						sizeof(*codec),
+						setup,
+						endpoint_open_cb,
+						a2dp_sep->user_data);
+		if (err == 0)
+			return;
+
+		setup->stream = NULL;
+		finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+		return;
+	}
+
+	ret = avdtp_open(session, stream);
+	if (ret < 0) {
+		error("Error on avdtp_open %s (%d)", strerror(-ret), -ret);
+		setup->stream = NULL;
+		finalize_setup_errno(setup, ret, finalize_config, NULL);
+	}
+}
+
+static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Get_Configuration_Ind", sep);
+	else
+		DBG("Source %p: Get_Configuration_Ind", sep);
+	return TRUE;
+}
+
+static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Set_Configuration_Cfm", sep);
+	else
+		DBG("Source %p: Set_Configuration_Cfm", sep);
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Open_Ind", sep);
+	else
+		DBG("Source %p: Open_Ind", sep);
+
+	setup = a2dp_setup_get(session);
+	if (!setup)
+		return FALSE;
+
+	setup->stream = stream;
+
+	if (setup->reconfigure)
+		setup->reconfigure = FALSE;
+
+	finalize_config(setup);
+
+	return TRUE;
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Open_Cfm", sep);
+	else
+		DBG("Source %p: Open_Cfm", sep);
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	if (setup->reconfigure)
+		setup->reconfigure = FALSE;
+
+	if (err) {
+		setup->stream = NULL;
+		setup->err = err;
+		if (setup->start)
+			finalize_resume(setup);
+	}
+
+	finalize_config(setup);
+
+	return;
+}
+
+static gboolean suspend_timeout(struct a2dp_sep *sep)
+{
+	if (avdtp_suspend(sep->session, sep->stream) == 0)
+		sep->suspending = TRUE;
+
+	sep->suspend_timer = 0;
+
+	avdtp_unref(sep->session);
+	sep->session = NULL;
+
+	return FALSE;
+}
+
+static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Start_Ind", sep);
+	else
+		DBG("Source %p: Start_Ind", sep);
+
+	if (!a2dp_sep->locked) {
+		a2dp_sep->session = avdtp_ref(session);
+		a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+						(GSourceFunc) suspend_timeout,
+						a2dp_sep);
+	}
+
+	if (!a2dp_sep->starting)
+		return TRUE;
+
+	a2dp_sep->starting = FALSE;
+
+	setup = find_setup_by_session(session);
+	if (setup)
+		finalize_resume(setup);
+
+	return TRUE;
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Start_Cfm", sep);
+	else
+		DBG("Source %p: Start_Cfm", sep);
+
+	a2dp_sep->starting = FALSE;
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	if (err) {
+		setup->stream = NULL;
+		setup->err = err;
+	}
+
+	finalize_resume(setup);
+}
+
+static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+	gboolean start;
+	int start_err;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Suspend_Ind", sep);
+	else
+		DBG("Source %p: Suspend_Ind", sep);
+
+	if (a2dp_sep->suspend_timer) {
+		g_source_remove(a2dp_sep->suspend_timer);
+		a2dp_sep->suspend_timer = 0;
+		avdtp_unref(a2dp_sep->session);
+		a2dp_sep->session = NULL;
+	}
+
+	if (!a2dp_sep->suspending)
+		return TRUE;
+
+	a2dp_sep->suspending = FALSE;
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return TRUE;
+
+	start = setup->start;
+	setup->start = FALSE;
+
+	finalize_suspend(setup);
+
+	if (!start)
+		return TRUE;
+
+	start_err = avdtp_start(session, a2dp_sep->stream);
+	if (start_err < 0 && start_err != -EINPROGRESS) {
+		error("avdtp_start: %s (%d)", strerror(-start_err),
+								-start_err);
+		finalize_setup_errno(setup, start_err, finalize_resume, NULL);
+	}
+
+	return TRUE;
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+	gboolean start;
+	int start_err;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Suspend_Cfm", sep);
+	else
+		DBG("Source %p: Suspend_Cfm", sep);
+
+	a2dp_sep->suspending = FALSE;
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	start = setup->start;
+	setup->start = FALSE;
+
+	if (err) {
+		setup->stream = NULL;
+		setup->err = err;
+	}
+
+	finalize_suspend(setup);
+
+	if (!start)
+		return;
+
+	if (err) {
+		finalize_resume(setup);
+		return;
+	}
+
+	start_err = avdtp_start(session, a2dp_sep->stream);
+	if (start_err < 0 && start_err != -EINPROGRESS) {
+		error("avdtp_start: %s (%d)", strerror(-start_err),
+								-start_err);
+		finalize_setup_errno(setup, start_err, finalize_suspend, NULL);
+	}
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Close_Ind", sep);
+	else
+		DBG("Source %p: Close_Ind", sep);
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return TRUE;
+
+	finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
+							finalize_resume, NULL);
+
+	return TRUE;
+}
+
+static gboolean a2dp_reconfigure(gpointer data)
+{
+	struct a2dp_setup *setup = data;
+	struct a2dp_sep *sep = setup->sep;
+	int posix_err;
+	struct avdtp_media_codec_capability *rsep_codec;
+	struct avdtp_service_capability *cap;
+
+	if (setup->rsep) {
+		cap = avdtp_get_codec(setup->rsep);
+		rsep_codec = (struct avdtp_media_codec_capability *) cap->data;
+	}
+
+	if (!setup->rsep || sep->codec != rsep_codec->media_codec_type)
+		setup->rsep = avdtp_find_remote_sep(setup->session, sep->lsep);
+
+	posix_err = avdtp_set_configuration(setup->session, setup->rsep,
+						sep->lsep,
+						setup->caps,
+						&setup->stream);
+	if (posix_err < 0) {
+		error("avdtp_set_configuration: %s", strerror(-posix_err));
+		goto failed;
+	}
+
+	return FALSE;
+
+failed:
+	finalize_setup_errno(setup, posix_err, finalize_config, NULL);
+	return FALSE;
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Close_Cfm", sep);
+	else
+		DBG("Source %p: Close_Cfm", sep);
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	if (err) {
+		setup->stream = NULL;
+		setup->err = err;
+		finalize_config(setup);
+		return;
+	}
+
+	if (!setup->rsep)
+		setup->rsep = avdtp_stream_get_remote_sep(stream);
+
+	if (setup->reconfigure)
+		g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
+}
+
+static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Abort_Ind", sep);
+	else
+		DBG("Source %p: Abort_Ind", sep);
+
+	a2dp_sep->stream = NULL;
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
+							finalize_resume,
+							finalize_config, NULL);
+
+	return;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: Abort_Cfm", sep);
+	else
+		DBG("Source %p: Abort_Cfm", sep);
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	setup_unref(setup);
+}
+
+static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: ReConfigure_Ind", sep);
+	else
+		DBG("Source %p: ReConfigure_Ind", sep);
+
+	return TRUE;
+}
+
+static gboolean endpoint_delayreport_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						uint8_t rseid, uint16_t delay,
+						uint8_t *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: DelayReport_Ind", sep);
+	else
+		DBG("Source %p: DelayReport_Ind", sep);
+
+	if (a2dp_sep->endpoint == NULL ||
+				a2dp_sep->endpoint->set_delay == NULL)
+		return FALSE;
+
+	a2dp_sep->endpoint->set_delay(a2dp_sep, delay, a2dp_sep->user_data);
+
+	return TRUE;
+}
+
+static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+	struct a2dp_setup *setup;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: ReConfigure_Cfm", sep);
+	else
+		DBG("Source %p: ReConfigure_Cfm", sep);
+
+	setup = find_setup_by_session(session);
+	if (!setup)
+		return;
+
+	if (err) {
+		setup->stream = NULL;
+		setup->err = err;
+	}
+
+	finalize_config(setup);
+}
+
+static void delay_report_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_sep *a2dp_sep = user_data;
+
+	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+		DBG("Sink %p: DelayReport_Cfm", sep);
+	else
+		DBG("Source %p: DelayReport_Cfm", sep);
+}
+
+static struct avdtp_sep_cfm cfm = {
+	.set_configuration	= setconf_cfm,
+	.get_configuration	= getconf_cfm,
+	.open			= open_cfm,
+	.start			= start_cfm,
+	.suspend		= suspend_cfm,
+	.close			= close_cfm,
+	.abort			= abort_cfm,
+	.reconfigure		= reconf_cfm,
+	.delay_report		= delay_report_cfm,
+};
+
+static struct avdtp_sep_ind endpoint_ind = {
+	.match_codec		= endpoint_match_codec_ind,
+	.get_capability		= endpoint_getcap_ind,
+	.set_configuration	= endpoint_setconf_ind,
+	.get_configuration	= getconf_ind,
+	.open			= open_ind,
+	.start			= start_ind,
+	.suspend		= suspend_ind,
+	.close			= close_ind,
+	.abort			= abort_ind,
+	.reconfigure		= reconf_ind,
+	.delayreport		= endpoint_delayreport_ind,
+};
+
+static sdp_record_t *a2dp_record(uint8_t type)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = AVDTP_UUID;
+	uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	if (type == AVDTP_SEP_TYPE_SOURCE)
+		sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+	else
+		sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &a2dp_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+	profile[0].version = a2dp_ver;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+	proto[1] = sdp_list_append(0, &avdtp_uuid);
+	version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	if (type == AVDTP_SEP_TYPE_SOURCE)
+		sdp_set_info_attr(record, "Audio Source", 0, 0);
+	else
+		sdp_set_info_attr(record, "Audio Sink", 0, 0);
+
+	free(psm);
+	free(version);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+
+	return record;
+}
+
+static struct a2dp_server *find_server(GSList *list, struct btd_adapter *a)
+{
+
+	for (; list; list = list->next) {
+		struct a2dp_server *server = list->data;
+
+		if (server->adapter == a)
+			return server;
+	}
+
+	return NULL;
+}
+
+static void channel_free(void *data)
+{
+	struct a2dp_channel *chan = data;
+
+	if (chan->auth_id > 0)
+		btd_cancel_authorization(chan->auth_id);
+
+	if (chan->io_id > 0)
+		g_source_remove(chan->io_id);
+
+	if (chan->io) {
+		g_io_channel_shutdown(chan->io, TRUE, NULL);
+		g_io_channel_unref(chan->io);
+	}
+
+	avdtp_remove_state_cb(chan->state_id);
+
+	g_free(chan);
+}
+
+static void channel_remove(struct a2dp_channel *chan)
+{
+	struct a2dp_server *server = chan->server;
+
+	DBG("chan %p", chan);
+
+	queue_remove(server->channels, chan);
+
+	channel_free(chan);
+}
+
+static gboolean disconnect_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+	struct a2dp_channel *chan = data;
+
+	DBG("chan %p", chan);
+
+	chan->io_id = 0;
+
+	channel_remove(chan);
+
+	return FALSE;
+}
+
+static void avdtp_state_cb(struct btd_device *dev, struct avdtp *session,
+					avdtp_session_state_t old_state,
+					avdtp_session_state_t new_state,
+					void *user_data)
+{
+	struct a2dp_channel *chan = user_data;
+
+	switch (new_state) {
+	case AVDTP_SESSION_STATE_DISCONNECTED:
+		if (chan->session == session)
+			channel_remove(chan);
+		break;
+	case AVDTP_SESSION_STATE_CONNECTING:
+		break;
+	case AVDTP_SESSION_STATE_CONNECTED:
+		break;
+	}
+}
+
+static struct a2dp_channel *channel_new(struct a2dp_server *server,
+					struct btd_device *device,
+					GIOChannel *io)
+{
+	struct a2dp_channel *chan;
+
+	chan = g_new0(struct a2dp_channel, 1);
+	chan->server = server;
+	chan->device = device;
+	chan->state_id = avdtp_add_state_cb(device, avdtp_state_cb, chan);
+
+	if (!queue_push_tail(server->channels, chan)) {
+		g_free(chan);
+		return NULL;
+	}
+
+	if (!io)
+		return chan;
+
+	chan->io = g_io_channel_ref(io);
+	chan->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) disconnect_cb, chan);
+
+	return chan;
+}
+
+static bool match_by_device(const void *data, const void *user_data)
+{
+	const struct a2dp_channel *chan = data;
+	const struct btd_device *device = user_data;
+
+	return chan->device == device;
+}
+
+struct avdtp *a2dp_avdtp_get(struct btd_device *device)
+{
+	struct a2dp_server *server;
+	struct a2dp_channel *chan;
+
+	server = find_server(servers, device_get_adapter(device));
+	if (server == NULL)
+		return NULL;
+
+	chan = queue_find(server->channels, match_by_device, device);
+	if (!chan) {
+		chan = channel_new(server, device, NULL);
+		if (!chan)
+			return NULL;
+	}
+
+	if (chan->session)
+		return avdtp_ref(chan->session);
+
+	chan->session = avdtp_new(NULL, device, server->seps);
+	if (!chan->session) {
+		channel_remove(chan);
+		return NULL;
+	}
+
+	return avdtp_ref(chan->session);
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct a2dp_channel *chan = user_data;
+
+	if (err) {
+		error("%s", err->message);
+		goto fail;
+	}
+
+	chan->session = avdtp_new(chan->io, chan->device, chan->server->seps);
+	if (!chan->session) {
+		error("Unable to create AVDTP session");
+		goto fail;
+	}
+
+	g_io_channel_unref(chan->io);
+	chan->io = NULL;
+
+	g_source_remove(chan->io_id);
+	chan->io_id = 0;
+
+	return;
+
+fail:
+	channel_remove(chan);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+	struct a2dp_channel *chan = user_data;
+	GError *err = NULL;
+
+	chan->auth_id = 0;
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Access denied: %s", derr->message);
+		goto fail;
+	}
+
+	if (!bt_io_accept(chan->io, connect_cb, chan, NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	channel_remove(chan);
+}
+
+static void transport_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct a2dp_setup *setup = user_data;
+	uint16_t omtu, imtu;
+
+	if (err) {
+		error("%s", err->message);
+		goto drop;
+	}
+
+	bt_io_get(io, &err, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu,
+							BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	if (!avdtp_stream_set_transport(setup->stream,
+					g_io_channel_unix_get_fd(io),
+					imtu, omtu))
+		goto drop;
+
+	g_io_channel_set_close_on_unref(io, FALSE);
+
+	setup_unref(setup);
+
+	return;
+
+drop:
+	setup_unref(setup);
+	g_io_channel_shutdown(io, TRUE, NULL);
+
+}
+static void confirm_cb(GIOChannel *io, gpointer data)
+{
+	struct a2dp_server *server = data;
+	struct a2dp_channel *chan;
+	char address[18];
+	bdaddr_t src, dst;
+	GError *err = NULL;
+	struct btd_device *device;
+
+	bt_io_get(io, &err,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("AVDTP: incoming connect from %s", address);
+
+	device = btd_adapter_find_device(adapter_find(&src), &dst,
+								BDADDR_BREDR);
+	if (!device)
+		goto drop;
+
+	chan = queue_find(server->channels, match_by_device, device);
+	if (chan) {
+		struct a2dp_setup *setup;
+
+		setup = find_setup_by_session(chan->session);
+		if (!setup || !setup->stream)
+			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;
+		}
+
+		return;
+	}
+
+	chan = channel_new(server, device, io);
+	if (!chan)
+		goto drop;
+
+	chan->auth_id = btd_request_authorization(&src, &dst,
+							ADVANCED_AUDIO_UUID,
+							auth_cb, chan);
+	if (chan->auth_id == 0 && !chan->session)
+		channel_remove(chan);
+
+	return;
+
+drop:
+	g_io_channel_shutdown(io, TRUE, NULL);
+}
+
+static bool a2dp_server_listen(struct a2dp_server *server)
+{
+	GError *err = NULL;
+
+	if (server->io)
+		return true;
+
+	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);
+	if (server->io)
+		return true;
+
+	error("%s", err->message);
+	g_error_free(err);
+
+	return false;
+}
+
+static struct a2dp_server *a2dp_server_register(struct btd_adapter *adapter)
+{
+	struct a2dp_server *server;
+
+	server = g_new0(struct a2dp_server, 1);
+
+	server->adapter = btd_adapter_ref(adapter);
+	server->seps = queue_new();
+	server->channels = queue_new();
+
+	servers = g_slist_append(servers, server);
+
+	return server;
+}
+
+static void a2dp_unregister_sep(struct a2dp_sep *sep)
+{
+	struct a2dp_server *server = sep->server;
+
+	if (sep->destroy) {
+		sep->destroy(sep->user_data);
+		sep->endpoint = NULL;
+	}
+
+	avdtp_unregister_sep(server->seps, sep->lsep);
+
+	g_free(sep);
+
+	if (!queue_isempty(server->seps))
+		return;
+
+	if (server->io) {
+		g_io_channel_shutdown(server->io, TRUE, NULL);
+		g_io_channel_unref(server->io);
+		server->io = NULL;
+	}
+}
+
+static void a2dp_server_unregister(struct a2dp_server *server)
+{
+	servers = g_slist_remove(servers, server);
+	queue_destroy(server->channels, channel_free);
+	queue_destroy(server->seps, NULL);
+
+	if (server->io) {
+		g_io_channel_shutdown(server->io, TRUE, NULL);
+		g_io_channel_unref(server->io);
+	}
+
+	btd_adapter_unref(server->adapter);
+	g_free(server);
+}
+
+struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
+				uint8_t codec, gboolean delay_reporting,
+				struct a2dp_endpoint *endpoint,
+				void *user_data, GDestroyNotify destroy,
+				int *err)
+{
+	struct a2dp_server *server;
+	struct a2dp_sep *sep;
+	GSList **l;
+	uint32_t *record_id;
+	sdp_record_t *record;
+
+	server = find_server(servers, adapter);
+	if (server == NULL) {
+		if (err)
+			*err = -EPROTONOSUPPORT;
+		return NULL;
+	}
+
+	if (type == AVDTP_SEP_TYPE_SINK && !server->sink_enabled) {
+		if (err)
+			*err = -EPROTONOSUPPORT;
+		return NULL;
+	}
+
+	if (type == AVDTP_SEP_TYPE_SOURCE && !server->source_enabled) {
+		if (err)
+			*err = -EPROTONOSUPPORT;
+		return NULL;
+	}
+
+	sep = g_new0(struct a2dp_sep, 1);
+
+	sep->lsep = avdtp_register_sep(server->seps, type,
+					AVDTP_MEDIA_TYPE_AUDIO, codec,
+					delay_reporting, &endpoint_ind,
+					&cfm, sep);
+
+	if (sep->lsep == NULL) {
+		g_free(sep);
+		if (err)
+			*err = -EINVAL;
+		return NULL;
+	}
+
+	sep->server = server;
+	sep->endpoint = endpoint;
+	sep->codec = codec;
+	sep->type = type;
+	sep->delay_reporting = delay_reporting;
+	sep->user_data = user_data;
+	sep->destroy = destroy;
+
+	if (type == AVDTP_SEP_TYPE_SOURCE) {
+		l = &server->sources;
+		record_id = &server->source_record_id;
+	} else {
+		l = &server->sinks;
+		record_id = &server->sink_record_id;
+	}
+
+	if (*record_id != 0)
+		goto add;
+
+	record = a2dp_record(type);
+	if (!record) {
+		error("Unable to allocate new service record");
+		a2dp_unregister_sep(sep);
+		if (err)
+			*err = -EINVAL;
+		return NULL;
+	}
+
+	if (adapter_service_add(server->adapter, record) < 0) {
+		error("Unable to register A2DP service record");
+		sdp_record_free(record);
+		a2dp_unregister_sep(sep);
+		if (err)
+			*err = -EINVAL;
+		return NULL;
+	}
+
+	if (!a2dp_server_listen(server)) {
+		sdp_record_free(record);
+		a2dp_unregister_sep(sep);
+		if (err)
+			*err = -EINVAL;
+		return NULL;
+	}
+
+	*record_id = record->handle;
+
+add:
+	*l = g_slist_append(*l, sep);
+
+	if (err)
+		*err = 0;
+	return sep;
+}
+
+void a2dp_remove_sep(struct a2dp_sep *sep)
+{
+	struct a2dp_server *server = sep->server;
+
+	if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+		if (g_slist_find(server->sources, sep) == NULL)
+			return;
+		server->sources = g_slist_remove(server->sources, sep);
+		if (server->sources == NULL && server->source_record_id) {
+			adapter_service_remove(server->adapter,
+						server->source_record_id);
+			server->source_record_id = 0;
+		}
+	} else {
+		if (g_slist_find(server->sinks, sep) == NULL)
+			return;
+		server->sinks = g_slist_remove(server->sinks, sep);
+		if (server->sinks == NULL && server->sink_record_id) {
+			adapter_service_remove(server->adapter,
+						server->sink_record_id);
+			server->sink_record_id = 0;
+		}
+	}
+
+	if (sep->locked)
+		return;
+
+	a2dp_unregister_sep(sep);
+}
+
+static void select_cb(struct a2dp_setup *setup, void *ret, int size)
+{
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *cap;
+
+	if (size < 0) {
+		DBG("Endpoint replied an invalid configuration");
+		goto done;
+	}
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	setup->caps = g_slist_append(setup->caps, media_transport);
+
+	cap = g_malloc0(sizeof(*cap) + size);
+	cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	cap->media_codec_type = setup->sep->codec;
+	memcpy(cap->data, ret, size);
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+						sizeof(*cap) + size);
+
+	setup->caps = g_slist_append(setup->caps, media_codec);
+	g_free(cap);
+
+done:
+	finalize_select(setup);
+}
+
+static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+					const char *sender)
+{
+	for (; list; list = list->next) {
+		struct a2dp_sep *sep = list->data;
+
+		/* Use sender's endpoint if available */
+		if (sender) {
+			const char *name;
+
+			if (sep->endpoint == NULL)
+				continue;
+
+			name = sep->endpoint->get_name(sep, sep->user_data);
+			if (g_strcmp0(sender, name) != 0)
+				continue;
+		}
+
+		if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+			continue;
+
+		return sep;
+
+	}
+
+	return NULL;
+}
+
+static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+					const char *sender)
+{
+	struct a2dp_server *server;
+	struct a2dp_sep *sep;
+	GSList *l;
+
+	server = find_server(servers, avdtp_get_adapter(session));
+	if (!server)
+		return NULL;
+
+	l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
+
+	/* Check sender's seps first */
+	sep = a2dp_find_sep(session, l, sender);
+	if (sep != NULL)
+		return sep;
+
+	return a2dp_find_sep(session, l, NULL);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+	struct a2dp_setup *setup = user_data;
+
+	DBG("err %p", err);
+
+	setup->seps = seps;
+	setup->err = err;
+
+	finalize_discover(setup);
+}
+
+unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
+							void *user_data)
+{
+	struct a2dp_setup *setup;
+	struct a2dp_setup_cb *cb_data;
+
+	setup = a2dp_setup_get(session);
+	if (!setup)
+		return 0;
+
+	cb_data = setup_cb_new(setup);
+	cb_data->discover_cb = cb;
+	cb_data->user_data = user_data;
+
+	if (avdtp_discover(session, discover_cb, setup) == 0)
+		return cb_data->id;
+
+	setup_cb_free(cb_data);
+	return 0;
+}
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+					uint8_t type, const char *sender,
+					a2dp_select_cb_t cb,
+					void *user_data)
+{
+	struct a2dp_setup *setup;
+	struct a2dp_setup_cb *cb_data;
+	struct a2dp_sep *sep;
+	struct avdtp_service_capability *service;
+	struct avdtp_media_codec_capability *codec;
+	int err;
+
+	sep = a2dp_select_sep(session, type, sender);
+	if (!sep) {
+		error("Unable to select SEP");
+		return 0;
+	}
+
+	setup = a2dp_setup_get(session);
+	if (!setup)
+		return 0;
+
+	cb_data = setup_cb_new(setup);
+	cb_data->select_cb = cb;
+	cb_data->user_data = user_data;
+
+	setup->sep = sep;
+	setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+
+	if (setup->rsep == NULL) {
+		error("Could not find remote sep");
+		goto fail;
+	}
+
+	service = avdtp_get_codec(setup->rsep);
+	codec = (struct avdtp_media_codec_capability *) service->data;
+
+	err = sep->endpoint->select_configuration(sep, codec->data,
+					service->length - sizeof(*codec),
+					setup,
+					select_cb, sep->user_data);
+	if (err == 0)
+		return cb_data->id;
+
+fail:
+	setup_cb_free(cb_data);
+	return 0;
+
+}
+
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+				a2dp_config_cb_t cb, GSList *caps,
+				void *user_data)
+{
+	struct a2dp_setup_cb *cb_data;
+	GSList *l;
+	struct a2dp_server *server;
+	struct a2dp_setup *setup;
+	struct a2dp_sep *tmp;
+	struct avdtp_service_capability *cap;
+	struct avdtp_media_codec_capability *codec_cap = NULL;
+	int posix_err;
+
+	server = find_server(servers, avdtp_get_adapter(session));
+	if (!server)
+		return 0;
+
+	for (l = caps; l != NULL; l = l->next) {
+		cap = l->data;
+
+		if (cap->category != AVDTP_MEDIA_CODEC)
+			continue;
+
+		codec_cap = (void *) cap->data;
+		break;
+	}
+
+	if (!codec_cap)
+		return 0;
+
+	if (sep->codec != codec_cap->media_codec_type)
+		return 0;
+
+	DBG("a2dp_config: selected SEP %p", sep->lsep);
+
+	setup = a2dp_setup_get(session);
+	if (!setup)
+		return 0;
+
+	cb_data = setup_cb_new(setup);
+	cb_data->config_cb = cb;
+	cb_data->user_data = user_data;
+
+	setup->sep = sep;
+	setup->stream = sep->stream;
+
+	/* Copy given caps if they are different than current caps */
+	if (setup->caps != caps) {
+		g_slist_free_full(setup->caps, g_free);
+		setup->caps = g_slist_copy(caps);
+	}
+
+	switch (avdtp_sep_get_state(sep->lsep)) {
+	case AVDTP_STATE_IDLE:
+		if (sep->type == AVDTP_SEP_TYPE_SOURCE)
+			l = server->sources;
+		else
+			l = server->sinks;
+
+		for (; l != NULL; l = l->next) {
+			tmp = l->data;
+			if (avdtp_has_stream(session, tmp->stream))
+				break;
+		}
+
+		if (l != NULL) {
+			if (tmp->locked)
+				goto failed;
+			setup->reconfigure = TRUE;
+			if (avdtp_close(session, tmp->stream, FALSE) < 0) {
+				error("avdtp_close failed");
+				goto failed;
+			}
+			break;
+		}
+
+		setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+		if (setup->rsep == NULL) {
+			error("No matching ACP and INT SEPs found");
+			goto failed;
+		}
+
+		posix_err = avdtp_set_configuration(session, setup->rsep,
+							sep->lsep, caps,
+							&setup->stream);
+		if (posix_err < 0) {
+			error("avdtp_set_configuration: %s",
+				strerror(-posix_err));
+			goto failed;
+		}
+		break;
+	case AVDTP_STATE_OPEN:
+	case AVDTP_STATE_STREAMING:
+		if (avdtp_stream_has_capabilities(setup->stream, caps)) {
+			DBG("Configuration match: resuming");
+			cb_data->source_id = g_idle_add(finalize_config,
+								setup);
+		} else if (!setup->reconfigure) {
+			setup->reconfigure = TRUE;
+			if (avdtp_close(session, sep->stream, FALSE) < 0) {
+				error("avdtp_close failed");
+				goto failed;
+			}
+		}
+		break;
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+	default:
+		error("SEP in bad state for requesting a new stream");
+		goto failed;
+	}
+
+	return cb_data->id;
+
+failed:
+	setup_cb_free(cb_data);
+	return 0;
+}
+
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+				a2dp_stream_cb_t cb, void *user_data)
+{
+	struct a2dp_setup_cb *cb_data;
+	struct a2dp_setup *setup;
+
+	setup = a2dp_setup_get(session);
+	if (!setup)
+		return 0;
+
+	cb_data = setup_cb_new(setup);
+	cb_data->resume_cb = cb;
+	cb_data->user_data = user_data;
+
+	setup->sep = sep;
+	setup->stream = sep->stream;
+
+	switch (avdtp_sep_get_state(sep->lsep)) {
+	case AVDTP_STATE_IDLE:
+		goto failed;
+		break;
+	case AVDTP_STATE_CONFIGURED:
+		setup->start = TRUE;
+		break;
+	case AVDTP_STATE_OPEN:
+		if (avdtp_start(session, sep->stream) < 0) {
+			error("avdtp_start failed");
+			goto failed;
+		}
+		sep->starting = TRUE;
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (!sep->suspending && sep->suspend_timer) {
+			g_source_remove(sep->suspend_timer);
+			sep->suspend_timer = 0;
+			avdtp_unref(sep->session);
+			sep->session = NULL;
+		}
+		if (sep->suspending)
+			setup->start = TRUE;
+		else
+			cb_data->source_id = g_idle_add(finalize_resume,
+								setup);
+		break;
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+	default:
+		error("SEP in bad state for resume");
+		goto failed;
+	}
+
+	return cb_data->id;
+
+failed:
+	setup_cb_free(cb_data);
+	return 0;
+}
+
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+				a2dp_stream_cb_t cb, void *user_data)
+{
+	struct a2dp_setup_cb *cb_data;
+	struct a2dp_setup *setup;
+
+	setup = a2dp_setup_get(session);
+	if (!setup)
+		return 0;
+
+	cb_data = setup_cb_new(setup);
+	cb_data->suspend_cb = cb;
+	cb_data->user_data = user_data;
+
+	setup->sep = sep;
+	setup->stream = sep->stream;
+
+	switch (avdtp_sep_get_state(sep->lsep)) {
+	case AVDTP_STATE_IDLE:
+		error("a2dp_suspend: no stream to suspend");
+		goto failed;
+		break;
+	case AVDTP_STATE_OPEN:
+		cb_data->source_id = g_idle_add(finalize_suspend, setup);
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (avdtp_suspend(session, sep->stream) < 0) {
+			error("avdtp_suspend failed");
+			goto failed;
+		}
+		sep->suspending = TRUE;
+		break;
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+	default:
+		error("SEP in bad state for suspend");
+		goto failed;
+	}
+
+	return cb_data->id;
+
+failed:
+	setup_cb_free(cb_data);
+	return 0;
+}
+
+gboolean a2dp_cancel(unsigned int id)
+{
+	GSList *ls;
+
+	for (ls = setups; ls != NULL; ls = g_slist_next(ls)) {
+		struct a2dp_setup *setup = ls->data;
+		GSList *l;
+		for (l = setup->cb; l != NULL; l = g_slist_next(l)) {
+			struct a2dp_setup_cb *cb = l->data;
+
+			if (cb->id != id)
+				continue;
+
+			setup_ref(setup);
+			setup_cb_free(cb);
+
+			if (!setup->cb) {
+				DBG("aborting setup %p", setup);
+				if (!avdtp_abort(setup->session, setup->stream))
+					return TRUE;
+			}
+
+			setup_unref(setup);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
+{
+	if (sep->locked)
+		return FALSE;
+
+	DBG("SEP %p locked", sep->lsep);
+	sep->locked = TRUE;
+
+	return TRUE;
+}
+
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
+{
+	struct a2dp_server *server = sep->server;
+	avdtp_state_t state;
+	GSList *l;
+
+	state = avdtp_sep_get_state(sep->lsep);
+
+	sep->locked = FALSE;
+
+	DBG("SEP %p unlocked", sep->lsep);
+
+	if (sep->type == AVDTP_SEP_TYPE_SOURCE)
+		l = server->sources;
+	else
+		l = server->sinks;
+
+	/* Unregister sep if it was removed */
+	if (g_slist_find(l, sep) == NULL) {
+		a2dp_unregister_sep(sep);
+		return TRUE;
+	}
+
+	if (!sep->stream || state == AVDTP_STATE_IDLE)
+		return TRUE;
+
+	switch (state) {
+	case AVDTP_STATE_OPEN:
+		/* Set timer here */
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (avdtp_suspend(session, sep->stream) == 0)
+			sep->suspending = TRUE;
+		break;
+	case AVDTP_STATE_IDLE:
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+	default:
+		break;
+	}
+
+	return TRUE;
+}
+
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep)
+{
+	return sep->stream;
+}
+
+struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup)
+{
+	if (setup->session == NULL)
+		return NULL;
+
+	return avdtp_get_device(setup->session);
+}
+
+static int a2dp_source_probe(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	DBG("path %s", device_get_path(dev));
+
+	source_init(service);
+
+	return 0;
+}
+
+static void a2dp_source_remove(struct btd_service *service)
+{
+	source_unregister(service);
+}
+
+static int a2dp_sink_probe(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	DBG("path %s", device_get_path(dev));
+
+	return sink_init(service);
+}
+
+static void a2dp_sink_remove(struct btd_service *service)
+{
+	sink_unregister(service);
+}
+
+static int a2dp_source_connect(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct btd_adapter *adapter = device_get_adapter(dev);
+	struct a2dp_server *server;
+	const char *path = device_get_path(dev);
+
+	DBG("path %s", path);
+
+	server = find_server(servers, adapter);
+	if (!server || !server->sink_enabled) {
+		DBG("Unexpected error: cannot find server");
+		return -EPROTONOSUPPORT;
+	}
+
+	/* Return protocol not available if no record/endpoint exists */
+	if (server->sink_record_id == 0)
+		return -ENOPROTOOPT;
+
+	return source_connect(service);
+}
+
+static int a2dp_source_disconnect(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	const char *path = device_get_path(dev);
+
+	DBG("path %s", path);
+
+	return source_disconnect(service);
+}
+
+static int a2dp_sink_connect(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct btd_adapter *adapter = device_get_adapter(dev);
+	struct a2dp_server *server;
+	const char *path = device_get_path(dev);
+
+	DBG("path %s", path);
+
+	server = find_server(servers, adapter);
+	if (!server || !server->source_enabled) {
+		DBG("Unexpected error: cannot find server");
+		return -EPROTONOSUPPORT;
+	}
+
+	/* Return protocol not available if no record/endpoint exists */
+	if (server->source_record_id == 0)
+		return -ENOPROTOOPT;
+
+	return sink_connect(service);
+}
+
+static int a2dp_sink_disconnect(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	const char *path = device_get_path(dev);
+
+	DBG("path %s", path);
+
+	return sink_disconnect(service);
+}
+
+static int a2dp_source_server_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct a2dp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (server != NULL)
+		goto done;
+
+	server = a2dp_server_register(adapter);
+	if (server == NULL)
+		return -EPROTONOSUPPORT;
+
+done:
+	server->source_enabled = TRUE;
+
+	return 0;
+}
+
+static void a2dp_source_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct a2dp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	g_slist_free_full(server->sources,
+					(GDestroyNotify) a2dp_unregister_sep);
+
+	if (server->source_record_id) {
+		adapter_service_remove(server->adapter,
+					server->source_record_id);
+		server->source_record_id = 0;
+	}
+
+	if (server->sink_record_id)
+		return;
+
+	a2dp_server_unregister(server);
+}
+
+static int a2dp_sink_server_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct a2dp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (server != NULL)
+		goto done;
+
+	server = a2dp_server_register(adapter);
+	if (server == NULL)
+		return -EPROTONOSUPPORT;
+
+done:
+	server->sink_enabled = TRUE;
+
+	return 0;
+}
+
+static void a2dp_sink_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct a2dp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	g_slist_free_full(server->sinks, (GDestroyNotify) a2dp_unregister_sep);
+
+	if (server->sink_record_id) {
+		adapter_service_remove(server->adapter, server->sink_record_id);
+		server->sink_record_id = 0;
+	}
+
+	if (server->source_record_id)
+		return;
+
+	a2dp_server_unregister(server);
+}
+
+static int media_server_probe(struct btd_adapter *adapter)
+{
+	DBG("path %s", adapter_get_path(adapter));
+
+	return media_register(adapter);
+}
+
+static void media_server_remove(struct btd_adapter *adapter)
+{
+	DBG("path %s", adapter_get_path(adapter));
+
+	media_unregister(adapter);
+}
+
+static struct btd_profile a2dp_source_profile = {
+	.name		= "a2dp-source",
+	.priority	= BTD_PROFILE_PRIORITY_MEDIUM,
+
+	.remote_uuid	= A2DP_SOURCE_UUID,
+	.device_probe	= a2dp_source_probe,
+	.device_remove	= a2dp_source_remove,
+
+	.auto_connect	= true,
+	.connect	= a2dp_source_connect,
+	.disconnect	= a2dp_source_disconnect,
+
+	.adapter_probe	= a2dp_sink_server_probe,
+	.adapter_remove	= a2dp_sink_server_remove,
+};
+
+static struct btd_profile a2dp_sink_profile = {
+	.name		= "a2dp-sink",
+	.priority	= BTD_PROFILE_PRIORITY_MEDIUM,
+
+	.remote_uuid	= A2DP_SINK_UUID,
+	.device_probe	= a2dp_sink_probe,
+	.device_remove	= a2dp_sink_remove,
+
+	.auto_connect	= true,
+	.connect	= a2dp_sink_connect,
+	.disconnect	= a2dp_sink_disconnect,
+
+	.adapter_probe	= a2dp_source_server_probe,
+	.adapter_remove	= a2dp_source_server_remove,
+};
+
+static struct btd_adapter_driver media_driver = {
+	.name		= "media",
+	.probe		= media_server_probe,
+	.remove		= media_server_remove,
+};
+
+static int a2dp_init(void)
+{
+	btd_register_adapter_driver(&media_driver);
+	btd_profile_register(&a2dp_source_profile);
+	btd_profile_register(&a2dp_sink_profile);
+
+	return 0;
+}
+
+static void a2dp_exit(void)
+{
+	btd_unregister_adapter_driver(&media_driver);
+	btd_profile_unregister(&a2dp_source_profile);
+	btd_profile_unregister(&a2dp_sink_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(a2dp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+							a2dp_init, a2dp_exit)
diff --git a/bluez/repo/profiles/audio/a2dp.h b/bluez/repo/profiles/audio/a2dp.h
new file mode 100644
index 0000000..19d1877
--- /dev/null
+++ b/bluez/repo/profiles/audio/a2dp.h
@@ -0,0 +1,96 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct a2dp_sep;
+struct a2dp_setup;
+
+typedef void (*a2dp_endpoint_select_t) (struct a2dp_setup *setup, void *ret,
+					int size);
+typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret);
+
+struct a2dp_endpoint {
+	const char *(*get_name) (struct a2dp_sep *sep, void *user_data);
+	size_t (*get_capabilities) (struct a2dp_sep *sep,
+						uint8_t **capabilities,
+						void *user_data);
+	int (*select_configuration) (struct a2dp_sep *sep,
+						uint8_t *capabilities,
+						size_t length,
+						struct a2dp_setup *setup,
+						a2dp_endpoint_select_t cb,
+						void *user_data);
+	int (*set_configuration) (struct a2dp_sep *sep,
+						uint8_t *configuration,
+						size_t length,
+						struct a2dp_setup *setup,
+						a2dp_endpoint_config_t cb,
+						void *user_data);
+	void (*clear_configuration) (struct a2dp_sep *sep, void *user_data);
+	void (*set_delay) (struct a2dp_sep *sep, uint16_t delay,
+							void *user_data);
+};
+
+typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps,
+					struct avdtp_error *err,
+					void *user_data);
+typedef void (*a2dp_select_cb_t) (struct avdtp *session,
+					struct a2dp_sep *sep, GSList *caps,
+					void *user_data);
+typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
+					struct avdtp_error *err,
+					void *user_data);
+
+struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
+				uint8_t codec, gboolean delay_reporting,
+				struct a2dp_endpoint *endpoint,
+				void *user_data, GDestroyNotify destroy,
+				int *err);
+void a2dp_remove_sep(struct a2dp_sep *sep);
+
+
+unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
+							void *user_data);
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+					uint8_t type, const char *sender,
+					a2dp_select_cb_t cb,
+					void *user_data);
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+				a2dp_config_cb_t cb, GSList *caps,
+				void *user_data);
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+				a2dp_stream_cb_t cb, void *user_data);
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+				a2dp_stream_cb_t cb, void *user_data);
+gboolean a2dp_cancel(unsigned int id);
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
+struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup);
+struct avdtp *a2dp_avdtp_get(struct btd_device *device);
diff --git a/bluez/repo/profiles/audio/avctp.c b/bluez/repo/profiles/audio/avctp.c
new file mode 100644
index 0000000..22bf35b
--- /dev/null
+++ b/bluez/repo/profiles/audio/avctp.c
@@ -0,0 +1,2065 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/l2cap.h"
+#include "lib/uuid.h"
+
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/uinput.h"
+
+#include "avctp.h"
+#include "avrcp.h"
+
+/* AV/C Panel 1.23, page 76:
+ * command with the pressed value is valid for two seconds
+ */
+#define AVC_PRESS_TIMEOUT	2
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND		0
+#define AVCTP_RESPONSE		1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE	0
+#define AVCTP_PACKET_START	1
+#define AVCTP_PACKET_CONTINUE	2
+#define AVCTP_PACKET_END	3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+	uint8_t ipid:1;
+	uint8_t cr:1;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avc_header {
+	uint8_t code:4;
+	uint8_t _hdr0:4;
+	uint8_t subunit_id:3;
+	uint8_t subunit_type:5;
+	uint8_t opcode;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t cr:1;
+	uint8_t ipid:1;
+	uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avc_header {
+	uint8_t _hdr0:4;
+	uint8_t code:4;
+	uint8_t subunit_type:5;
+	uint8_t subunit_id:3;
+	uint8_t opcode;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_state_callback {
+	avctp_state_cb cb;
+	struct btd_device *dev;
+	unsigned int id;
+	void *user_data;
+};
+
+struct avctp_server {
+	struct btd_adapter *adapter;
+	GIOChannel *control_io;
+	GIOChannel *browsing_io;
+	GSList *sessions;
+};
+
+struct avctp_control_req {
+	struct avctp_pending_req *p;
+	uint8_t code;
+	uint8_t subunit;
+	uint8_t op;
+	uint8_t *operands;
+	uint16_t operand_count;
+	avctp_rsp_cb func;
+	void *user_data;
+};
+
+struct avctp_browsing_req {
+	struct avctp_pending_req *p;
+	uint8_t *operands;
+	uint16_t operand_count;
+	avctp_browsing_rsp_cb func;
+	void *user_data;
+};
+
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+	struct avctp_channel *chan;
+	uint8_t transaction;
+	guint timeout;
+	int err;
+	avctp_process_cb process;
+	void *data;
+	GDestroyNotify destroy;
+};
+
+struct avctp_channel {
+	struct avctp *session;
+	GIOChannel *io;
+	uint8_t transaction;
+	guint watch;
+	uint16_t imtu;
+	uint16_t omtu;
+	uint8_t *buffer;
+	GSList *handlers;
+	struct avctp_pending_req *p;
+	GQueue *queue;
+	GSList *processed;
+	guint process_id;
+	GDestroyNotify destroy;
+};
+
+struct key_pressed {
+	uint16_t op;
+	guint timer;
+};
+
+struct avctp {
+	struct avctp_server *server;
+	struct btd_device *device;
+
+	avctp_state_t state;
+
+	int uinput;
+
+	guint auth_id;
+	unsigned int passthrough_id;
+	unsigned int unit_id;
+	unsigned int subunit_id;
+
+	struct avctp_channel *control;
+	struct avctp_channel *browsing;
+
+	struct avctp_passthrough_handler *handler;
+
+	uint8_t key_quirks[256];
+	struct key_pressed key;
+	bool initiator;
+};
+
+struct avctp_passthrough_handler {
+	avctp_passthrough_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_pdu_handler {
+	uint8_t opcode;
+	avctp_control_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+	unsigned int id;
+	GDestroyNotify destroy;
+};
+
+static struct {
+	const char *name;
+	uint8_t avc;
+	uint16_t uinput;
+} key_map[] = {
+	{ "SELECT",		AVC_SELECT,		KEY_SELECT },
+	{ "UP",			AVC_UP,			KEY_UP },
+	{ "DOWN",		AVC_DOWN,		KEY_DOWN },
+	{ "LEFT",		AVC_LEFT,		KEY_LEFT },
+	{ "RIGHT",		AVC_RIGHT,		KEY_RIGHT },
+	{ "ROOT MENU",		AVC_ROOT_MENU,		KEY_MENU },
+	{ "CONTENTS MENU",	AVC_CONTENTS_MENU,	KEY_PROGRAM },
+	{ "FAVORITE MENU",	AVC_FAVORITE_MENU,	KEY_FAVORITES },
+	{ "EXIT",		AVC_EXIT,		KEY_EXIT },
+	{ "ON DEMAND MENU",	AVC_ON_DEMAND_MENU,	KEY_MENU },
+	{ "APPS MENU",		AVC_APPS_MENU,		KEY_MENU },
+	{ "0",			AVC_0,			KEY_0 },
+	{ "1",			AVC_1,			KEY_1 },
+	{ "2",			AVC_2,			KEY_2 },
+	{ "3",			AVC_3,			KEY_3 },
+	{ "4",			AVC_4,			KEY_4 },
+	{ "5",			AVC_5,			KEY_5 },
+	{ "6",			AVC_6,			KEY_6 },
+	{ "7",			AVC_7,			KEY_7 },
+	{ "8",			AVC_8,			KEY_8 },
+	{ "9",			AVC_9,			KEY_9 },
+	{ "DOT",		AVC_DOT,		KEY_DOT },
+	{ "ENTER",		AVC_ENTER,		KEY_ENTER },
+	{ "CHANNEL UP",		AVC_CHANNEL_UP,		KEY_CHANNELUP },
+	{ "CHANNEL DOWN",	AVC_CHANNEL_DOWN,	KEY_CHANNELDOWN },
+	{ "CHANNEL PREVIOUS",	AVC_CHANNEL_PREVIOUS,	KEY_LAST },
+	{ "INPUT SELECT",	AVC_INPUT_SELECT,	KEY_CONFIG },
+	{ "INFO",		AVC_INFO,		KEY_INFO },
+	{ "HELP",		AVC_HELP,		KEY_HELP },
+	{ "POWER",		AVC_POWER,		KEY_POWER2 },
+	{ "VOLUME UP",		AVC_VOLUME_UP,		KEY_VOLUMEUP },
+	{ "VOLUME DOWN",	AVC_VOLUME_DOWN,	KEY_VOLUMEDOWN },
+	{ "MUTE",		AVC_MUTE,		KEY_MUTE },
+	{ "PLAY",		AVC_PLAY,		KEY_PLAYCD },
+	{ "STOP",		AVC_STOP,		KEY_STOPCD },
+	{ "PAUSE",		AVC_PAUSE,		KEY_PAUSECD },
+	{ "FORWARD",		AVC_FORWARD,		KEY_NEXTSONG },
+	{ "BACKWARD",		AVC_BACKWARD,		KEY_PREVIOUSSONG },
+	{ "RECORD",		AVC_RECORD,		KEY_RECORD },
+	{ "REWIND",		AVC_REWIND,		KEY_REWIND },
+	{ "FAST FORWARD",	AVC_FAST_FORWARD,	KEY_FASTFORWARD },
+	{ "LIST",		AVC_LIST,		KEY_LIST },
+	{ "F1",			AVC_F1,			KEY_F1 },
+	{ "F2",			AVC_F2,			KEY_F2 },
+	{ "F3",			AVC_F3,			KEY_F3 },
+	{ "F4",			AVC_F4,			KEY_F4 },
+	{ "F5",			AVC_F5,			KEY_F5 },
+	{ "F6",			AVC_F6,			KEY_F6 },
+	{ "F7",			AVC_F7,			KEY_F7 },
+	{ "F8",			AVC_F8,			KEY_F8 },
+	{ "F9",			AVC_F9,			KEY_F9 },
+	{ "RED",		AVC_RED,		KEY_RED },
+	{ "GREEN",		AVC_GREEN,		KEY_GREEN },
+	{ "BLUE",		AVC_BLUE,		KEY_BLUE },
+	{ "YELLOW",		AVC_YELLOW,		KEY_YELLOW },
+	{ NULL }
+};
+
+static GSList *callbacks = NULL;
+static GSList *servers = NULL;
+
+static void auth_cb(DBusError *derr, void *user_data);
+static gboolean process_queue(gpointer user_data);
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+	struct uinput_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type	= type;
+	event.code	= code;
+	event.value	= value;
+
+	return write(fd, &event, sizeof(event));
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+	if (fd < 0)
+		return;
+
+	send_event(fd, EV_KEY, key, pressed);
+	send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean auto_release(gpointer user_data)
+{
+	struct avctp *session = user_data;
+
+	session->key.timer = 0;
+
+	DBG("AV/C: key press timeout");
+
+	send_key(session->uinput, session->key.op, 0);
+
+	return FALSE;
+}
+
+static void handle_press(struct avctp *session, uint16_t op)
+{
+	if (session->key.timer > 0) {
+		g_source_remove(session->key.timer);
+
+		/* Only auto release if keys are different */
+		if (session->key.op == op)
+			goto done;
+
+		send_key(session->uinput, session->key.op, 0);
+	}
+
+	session->key.op = op;
+
+	send_key(session->uinput, op, 1);
+
+done:
+	session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+							auto_release, session);
+}
+
+static void handle_release(struct avctp *session, uint16_t op)
+{
+	if (session->key.timer > 0) {
+		g_source_remove(session->key.timer);
+		session->key.timer = 0;
+	}
+
+	send_key(session->uinput, op, 0);
+}
+
+static size_t handle_panel_passthrough(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avctp_passthrough_handler *handler = session->handler;
+	const char *status;
+	int pressed, i;
+
+	if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+		*code = AVC_CTYPE_REJECTED;
+		return operand_count;
+	}
+
+	if (operand_count == 0)
+		goto done;
+
+	if (operands[0] & 0x80) {
+		status = "released";
+		pressed = 0;
+	} else {
+		status = "pressed";
+		pressed = 1;
+	}
+
+	if (session->key.timer == 0 && handler != NULL) {
+		if (handler->cb(session, operands[0] & 0x7F,
+						pressed, handler->user_data))
+			goto done;
+	}
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		uint8_t key_quirks;
+
+		if ((operands[0] & 0x7F) != key_map[i].avc)
+			continue;
+
+		DBG("AV/C: %s %s", key_map[i].name, status);
+
+		key_quirks = session->key_quirks[key_map[i].avc];
+
+		if (key_quirks & QUIRK_NO_RELEASE) {
+			if (!pressed) {
+				DBG("AV/C: Ignoring release");
+				break;
+			}
+
+			DBG("AV/C: treating key press as press + release");
+			send_key(session->uinput, key_map[i].uinput, 1);
+			send_key(session->uinput, key_map[i].uinput, 0);
+			break;
+		}
+
+		if (pressed)
+			handle_press(session, key_map[i].uinput);
+		else
+			handle_release(session, key_map[i].uinput);
+
+		break;
+	}
+
+	if (key_map[i].name == NULL) {
+		DBG("AV/C: unknown button 0x%02X %s",
+						operands[0] & 0x7F, status);
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return operand_count;
+	}
+
+done:
+	*code = AVC_CTYPE_ACCEPTED;
+	return operand_count;
+}
+
+static size_t handle_unit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/* The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it */
+	if (operand_count >= 1)
+		operands[0] = 0x07;
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_UNITINFO");
+
+	return operand_count;
+}
+
+static size_t handle_subunit_info(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (*code != AVC_CTYPE_STATUS) {
+		*code = AVC_CTYPE_REJECTED;
+		return 0;
+	}
+
+	*code = AVC_CTYPE_STABLE;
+
+	/* The first operand should be 0x07 for the UNITINFO response.
+	 * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+	 * Interface Command Set (section 9.2.1, page 45) specs
+	 * explain this value but both use it */
+	if (operand_count >= 2)
+		operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+	DBG("reply to AVC_OP_SUBUNITINFO");
+
+	return operand_count;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+	for (; list; list = list->next) {
+		struct avctp_pdu_handler *handler = list->data;
+
+		if (handler->opcode == opcode)
+			return handler;
+	}
+
+	return NULL;
+}
+
+static void pending_destroy(gpointer data, gpointer user_data)
+{
+	struct avctp_pending_req *req = data;
+
+	if (req->destroy)
+		req->destroy(req->data);
+
+	if (req->timeout > 0)
+		g_source_remove(req->timeout);
+
+	g_free(req);
+}
+
+static void avctp_channel_destroy(struct avctp_channel *chan)
+{
+	g_io_channel_shutdown(chan->io, TRUE, NULL);
+	g_io_channel_unref(chan->io);
+
+	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_foreach(chan->processed, pending_destroy, NULL);
+	g_slist_free(chan->processed);
+	g_slist_free_full(chan->handlers, g_free);
+	g_free(chan);
+}
+
+static void avctp_disconnected(struct avctp *session)
+{
+	struct avctp_server *server;
+
+	if (!session)
+		return;
+
+	if (session->browsing)
+		avctp_channel_destroy(session->browsing);
+
+	if (session->control)
+		avctp_channel_destroy(session->control);
+
+	if (session->auth_id != 0) {
+		btd_cancel_authorization(session->auth_id);
+		session->auth_id = 0;
+	}
+
+	if (session->key.timer > 0)
+		g_source_remove(session->key.timer);
+
+	if (session->uinput >= 0) {
+		char address[18];
+
+		ba2str(device_get_address(session->device), address);
+		DBG("AVCTP: closing uinput for %s", address);
+
+		ioctl(session->uinput, UI_DEV_DESTROY);
+		close(session->uinput);
+		session->uinput = -1;
+	}
+
+	server = session->server;
+	server->sessions = g_slist_remove(server->sessions, session);
+	btd_device_unref(session->device);
+	g_free(session);
+}
+
+static void avctp_set_state(struct avctp *session, avctp_state_t new_state,
+									int err)
+{
+	GSList *l;
+	avctp_state_t old_state = session->state;
+
+	session->state = new_state;
+
+	for (l = callbacks; l != NULL; l = l->next) {
+		struct avctp_state_callback *cb = l->data;
+
+		if (cb->dev && cb->dev != session->device)
+			continue;
+
+		cb->cb(session->device, old_state, new_state, err,
+								cb->user_data);
+	}
+
+	switch (new_state) {
+	case AVCTP_STATE_DISCONNECTED:
+		DBG("AVCTP Disconnected");
+		avctp_disconnected(session);
+		break;
+	case AVCTP_STATE_CONNECTING:
+		DBG("AVCTP Connecting");
+		break;
+	case AVCTP_STATE_CONNECTED:
+		DBG("AVCTP Connected");
+		break;
+	case AVCTP_STATE_BROWSING_CONNECTING:
+		DBG("AVCTP Browsing Connecting");
+		break;
+	case AVCTP_STATE_BROWSING_CONNECTED:
+		DBG("AVCTP Browsing Connected");
+		break;
+	default:
+		error("Invalid AVCTP state %d", new_state);
+		return;
+	}
+}
+
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+				uint8_t cr, uint8_t code,
+				uint8_t subunit, uint8_t opcode,
+				uint8_t *operands, size_t operand_count)
+{
+	struct avctp_header *avctp;
+	struct avc_header *avc;
+	struct msghdr msg;
+	struct iovec iov[2];
+	int sk, err = 0;
+
+	iov[0].iov_base = control->buffer;
+	iov[0].iov_len  = sizeof(*avctp) + sizeof(*avc);
+	iov[1].iov_base = operands;
+	iov[1].iov_len  = operand_count;
+
+	if (control->omtu < (iov[0].iov_len + iov[1].iov_len))
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(control->io);
+
+	memset(control->buffer, 0, iov[0].iov_len);
+
+	avctp = (void *) control->buffer;
+	avc = (void *) avctp + sizeof(*avctp);
+
+	avctp->transaction = transaction;
+	avctp->packet_type = AVCTP_PACKET_SINGLE;
+	avctp->cr = cr;
+	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	avc->code = code;
+	avc->subunit_type = subunit;
+	avc->opcode = opcode;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 2;
+
+	if (sendmsg(sk, &msg, 0) < 0)
+		err = -errno;
+
+	return err;
+}
+
+static int avctp_browsing_send(struct avctp_channel *browsing,
+				uint8_t transaction, uint8_t cr,
+				uint8_t *operands, size_t operand_count)
+{
+	struct avctp_header *avctp;
+	struct msghdr msg;
+	struct iovec iov[2];
+	int sk, err = 0;
+
+	iov[0].iov_base = browsing->buffer;
+	iov[0].iov_len  = sizeof(*avctp);
+	iov[1].iov_base = operands;
+	iov[1].iov_len  = operand_count;
+
+	if (browsing->omtu < (iov[0].iov_len + iov[1].iov_len))
+		return -EOVERFLOW;
+
+	sk = g_io_channel_unix_get_fd(browsing->io);
+
+	memset(browsing->buffer, 0, iov[0].iov_len);
+
+	avctp = (void *) browsing->buffer;
+
+	avctp->transaction = transaction;
+	avctp->packet_type = AVCTP_PACKET_SINGLE;
+	avctp->cr = cr;
+	avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 2;
+
+	if (sendmsg(sk, &msg, 0) < 0)
+		err = -errno;
+
+	return err;
+}
+
+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;
+
+	if (p->err == 0 || req->func == NULL)
+		goto done;
+
+	req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
+							req->user_data);
+
+done:
+	g_free(req->operands);
+	g_free(req);
+}
+
+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;
+
+	if (p->err == 0 || req->func == NULL)
+		goto done;
+
+	req->func(session, NULL, 0, req->user_data);
+
+done:
+	g_free(req->operands);
+	g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+	struct avctp_channel *chan = user_data;
+	struct avctp_pending_req *p = chan->p;
+
+	DBG("transaction %u", p->transaction);
+
+	p->timeout = 0;
+	p->err = -ETIMEDOUT;
+
+	pending_destroy(p, NULL);
+	chan->p = NULL;
+
+	if (chan->process_id == 0)
+		chan->process_id = g_idle_add(process_queue, chan);
+
+	return FALSE;
+}
+
+static int process_control(void *data)
+{
+	struct avctp_control_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+					req->subunit, req->op,
+					req->operands, req->operand_count);
+}
+
+static int process_browsing(void *data)
+{
+	struct avctp_browsing_req *req = data;
+	struct avctp_pending_req *p = req->p;
+
+	return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+					req->operands, req->operand_count);
+}
+
+static gboolean process_queue(void *user_data)
+{
+	struct avctp_channel *chan = user_data;
+	struct avctp_pending_req *p = chan->p;
+
+	chan->process_id = 0;
+
+	if (p != NULL)
+		return FALSE;
+
+	while ((p = g_queue_pop_head(chan->queue))) {
+
+		if (p->process(p->data) == 0)
+			break;
+
+		pending_destroy(p, NULL);
+	}
+
+	if (p == NULL)
+		return FALSE;
+
+	chan->p = p;
+	p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+	return FALSE;
+
+}
+
+static void control_response(struct avctp_channel *control,
+					struct avctp_header *avctp,
+					struct avc_header *avc,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = control->p;
+	struct avctp_control_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		control->processed = g_slist_prepend(control->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		control->p = NULL;
+
+		if (control->process_id == 0)
+			control->process_id = g_idle_add(process_queue,
+								control);
+	}
+
+	for (l = control->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(control->session, avc->code,
+						avc->subunit_type,
+						operands, operand_count,
+						req->user_data))
+			return;
+
+		control->processed = g_slist_remove(control->processed, p);
+		pending_destroy(p, NULL);
+
+		return;
+	}
+}
+
+static void browsing_response(struct avctp_channel *browsing,
+					struct avctp_header *avctp,
+					uint8_t *operands,
+					size_t operand_count)
+{
+	struct avctp_pending_req *p = browsing->p;
+	struct avctp_browsing_req *req;
+	GSList *l;
+
+	if (p && p->transaction == avctp->transaction) {
+		browsing->processed = g_slist_prepend(browsing->processed, p);
+
+		if (p->timeout > 0) {
+			g_source_remove(p->timeout);
+			p->timeout = 0;
+		}
+
+		browsing->p = NULL;
+
+		if (browsing->process_id == 0)
+			browsing->process_id = g_idle_add(process_queue,
+								browsing);
+	}
+
+	for (l = browsing->processed; l; l = l->next) {
+		p = l->data;
+		req = p->data;
+
+		if (p->transaction != avctp->transaction)
+			continue;
+
+		if (req->func && req->func(browsing->session, operands,
+						operand_count, req->user_data))
+			return;
+
+		browsing->processed = g_slist_remove(browsing->processed, p);
+		pending_destroy(p, NULL);
+
+		return;
+	}
+}
+
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *browsing = session->browsing;
+	uint8_t *buf = browsing->buffer;
+	uint8_t *operands;
+	struct avctp_header *avctp;
+	int sock, ret, packet_size, operand_count;
+	struct avctp_browsing_pdu_handler *handler;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(chan);
+
+	ret = read(sock, buf, browsing->imtu);
+	if (ret <= 0)
+		goto failed;
+
+	avctp = (struct avctp_header *) buf;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE)
+		goto failed;
+
+	operands = buf + AVCTP_HEADER_LENGTH;
+	ret -= AVCTP_HEADER_LENGTH;
+	operand_count = ret;
+
+	if (avctp->cr == AVCTP_RESPONSE) {
+		browsing_response(browsing, avctp, operands, operand_count);
+		return TRUE;
+	}
+
+	packet_size = AVCTP_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	handler = g_slist_nth_data(browsing->handlers, 0);
+	if (handler == NULL) {
+		DBG("handler not found");
+		packet_size += avrcp_browsing_general_reject(operands);
+		goto send;
+	}
+
+	packet_size += handler->cb(session, avctp->transaction,
+						operands, operand_count,
+						handler->user_data);
+
+send:
+	if (packet_size != 0) {
+		ret = write(sock, buf, packet_size);
+		if (ret != packet_size)
+			goto failed;
+	}
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP Browsing: disconnected");
+	avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
+
+	if (session->browsing) {
+		avctp_channel_destroy(session->browsing);
+		session->browsing = NULL;
+	}
+
+	return FALSE;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *control = session->control;
+	uint8_t *buf = control->buffer;
+	uint8_t *operands, code, subunit;
+	struct avctp_header *avctp;
+	struct avc_header *avc;
+	int ret, packet_size, operand_count, sock;
+	struct avctp_pdu_handler *handler;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(chan);
+
+	ret = read(sock, buf, control->imtu);
+	if (ret <= 0)
+		goto failed;
+
+	if (ret < AVCTP_HEADER_LENGTH) {
+		error("Too small AVCTP packet");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	ret -= AVCTP_HEADER_LENGTH;
+	if (ret < AVC_HEADER_LENGTH) {
+		error("Too small AVC packet");
+		goto failed;
+	}
+
+	avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH);
+
+	ret -= AVC_HEADER_LENGTH;
+
+	operands = (uint8_t *) avc + AVC_HEADER_LENGTH;
+	operand_count = ret;
+
+	if (avctp->cr == AVCTP_RESPONSE) {
+		control_response(control, avctp, avc, operands, operand_count);
+		return TRUE;
+	}
+
+	packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+		avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+		goto done;
+	}
+
+	if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+		avctp->ipid = 1;
+		packet_size = AVCTP_HEADER_LENGTH;
+		goto done;
+	}
+
+	handler = find_handler(control->handlers, avc->opcode);
+	if (!handler) {
+		DBG("handler not found for 0x%02x", avc->opcode);
+		packet_size += avrcp_handle_vendor_reject(&code, operands);
+		avc->code = code;
+		goto done;
+	}
+
+	code = avc->code;
+	subunit = avc->subunit_type;
+
+	packet_size += handler->cb(session, avctp->transaction, &code,
+					&subunit, operands, operand_count,
+					handler->user_data);
+
+	avc->code = code;
+	avc->subunit_type = subunit;
+
+done:
+	ret = write(sock, buf, packet_size);
+	if (ret != packet_size)
+		goto failed;
+
+	return TRUE;
+
+failed:
+	DBG("AVCTP session %p got disconnected", session);
+	avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+	return FALSE;
+}
+
+static int uinput_create(char *name)
+{
+	struct uinput_dev dev;
+	int fd, err, i;
+
+	fd = open("/dev/uinput", O_RDWR);
+	if (fd < 0) {
+		fd = open("/dev/input/uinput", O_RDWR);
+		if (fd < 0) {
+			fd = open("/dev/misc/uinput", O_RDWR);
+			if (fd < 0) {
+				err = -errno;
+				error("Can't open input device: %s (%d)",
+							strerror(-err), -err);
+				return err;
+			}
+		}
+	}
+
+	memset(&dev, 0, sizeof(dev));
+	if (name)
+		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+	dev.id.bustype = BUS_BLUETOOTH;
+	dev.id.vendor  = 0x0000;
+	dev.id.product = 0x0000;
+	dev.id.version = 0x0000;
+
+	if (write(fd, &dev, sizeof(dev)) < 0) {
+		err = -errno;
+		error("Can't write device information: %s (%d)",
+						strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	ioctl(fd, UI_SET_EVBIT, EV_KEY);
+	ioctl(fd, UI_SET_EVBIT, EV_REL);
+	ioctl(fd, UI_SET_EVBIT, EV_REP);
+	ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+	for (i = 0; key_map[i].name != NULL; i++)
+		ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+	if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+		err = -errno;
+		error("Can't create uinput device: %s (%d)",
+						strerror(-err), -err);
+		close(fd);
+		return err;
+	}
+
+	send_event(fd, EV_REP, REP_DELAY, 300);
+
+	return fd;
+}
+
+static void init_uinput(struct avctp *session)
+{
+	char address[18], name[248 + 1];
+
+	device_get_name(session->device, name, sizeof(name));
+	if (g_str_equal(name, "Nokia CK-20W")) {
+		session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+		session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
+	}
+
+	ba2str(device_get_address(session->device), address);
+	session->uinput = uinput_create(address);
+	if (session->uinput < 0)
+		error("AVRCP: failed to init uinput for %s", address);
+	else
+		DBG("AVRCP: uinput initialized for %s", address);
+}
+
+static struct avctp_channel *avctp_channel_create(struct avctp *session,
+							GIOChannel *io,
+							GDestroyNotify destroy)
+{
+	struct avctp_channel *chan;
+
+	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;
+
+	return chan;
+}
+
+static void handler_free(void *data)
+{
+	struct avctp_browsing_pdu_handler *handler = data;
+
+	if (handler->destroy)
+		handler->destroy(handler->user_data);
+
+	g_free(data);
+}
+
+static void avctp_destroy_browsing(void *data)
+{
+	struct avctp_channel *chan = data;
+
+	g_slist_free_full(chan->handlers, handler_free);
+
+	chan->handlers = NULL;
+}
+
+static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
+							gpointer data)
+{
+	struct avctp *session = data;
+	struct avctp_channel *browsing = session->browsing;
+	char address[18];
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+
+	if (err) {
+		error("Browsing: %s", err->message);
+		goto fail;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST, &address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	DBG("AVCTP Browsing: connected to %s", address);
+
+	if (browsing == NULL) {
+		browsing = avctp_channel_create(session, chan,
+						avctp_destroy_browsing);
+		session->browsing = browsing;
+	}
+
+	browsing->imtu = imtu;
+	browsing->omtu = omtu;
+	browsing->buffer = g_malloc0(MAX(imtu, omtu));
+	browsing->watch = g_io_add_watch(session->browsing->io,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) session_browsing_cb, session);
+
+	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);
+
+	return;
+
+fail:
+	avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
+
+	if (session->browsing) {
+		avctp_channel_destroy(session->browsing);
+		session->browsing = NULL;
+	}
+}
+
+static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	struct avctp *session = data;
+	char address[18];
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+
+	if (err) {
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_DEST, &address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_IMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	DBG("AVCTP: connected to %s", address);
+
+	if (session->control == NULL)
+		session->control = avctp_channel_create(session, chan, NULL);
+
+	session->control->imtu = imtu;
+	session->control->omtu = omtu;
+	session->control->buffer = g_malloc0(MAX(imtu, omtu));
+	session->control->watch = g_io_add_watch(session->control->io,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) session_cb, session);
+
+	session->passthrough_id = avctp_register_pdu_handler(session,
+						AVC_OP_PASSTHROUGH,
+						handle_panel_passthrough,
+						NULL);
+	session->unit_id = avctp_register_pdu_handler(session,
+						AVC_OP_UNITINFO,
+						handle_unit_info,
+						NULL);
+	session->subunit_id = avctp_register_pdu_handler(session,
+						AVC_OP_SUBUNITINFO,
+						handle_subunit_info,
+						NULL);
+
+	init_uinput(session);
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+	struct avctp *session = user_data;
+	GError *err = NULL;
+
+	session->auth_id = 0;
+
+	if (session->control->watch > 0) {
+		g_source_remove(session->control->watch);
+		session->control->watch = 0;
+	}
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Access denied: %s", derr->message);
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+		return;
+	}
+
+	if (!bt_io_accept(session->control->io, avctp_connect_cb, session,
+								NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+	}
+}
+
+static struct avctp_server *find_server(GSList *list, struct btd_adapter *a)
+{
+	for (; list; list = list->next) {
+		struct avctp_server *server = list->data;
+
+		if (server->adapter == a)
+			return server;
+	}
+
+	return NULL;
+}
+
+static struct avctp *find_session(GSList *list, struct btd_device *device)
+{
+	for (; list != NULL; list = g_slist_next(list)) {
+		struct avctp *s = list->data;
+
+		if (s->device == device)
+			return s;
+	}
+
+	return NULL;
+}
+
+static struct avctp *avctp_get_internal(struct btd_device *device)
+{
+	struct avctp_server *server;
+	struct avctp *session;
+
+	server = find_server(servers, device_get_adapter(device));
+	if (server == NULL)
+		return NULL;
+
+	session = find_session(server->sessions, device);
+	if (session)
+		return session;
+
+	session = g_new0(struct avctp, 1);
+
+	session->server = server;
+	session->device = btd_device_ref(device);
+	session->state = AVCTP_STATE_DISCONNECTED;
+	session->uinput = -1;
+
+	server->sessions = g_slist_append(server->sessions, session);
+
+	return session;
+}
+
+static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
+						struct btd_device *dev)
+{
+	const bdaddr_t *src;
+	const bdaddr_t *dst;
+
+	if (session->control != NULL) {
+		error("Control: Refusing unexpected connect");
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+		/*
+		 * Close AVCTP channel if remote tried connect
+		 * at the same time
+		 * AVRCP SPEC V1.5 4.1.1 Connection Establishment
+		 */
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EAGAIN);
+		return;
+	}
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
+	session->control = avctp_channel_create(session, chan, NULL);
+
+	src = btd_adapter_get_address(device_get_adapter(dev));
+	dst = device_get_address(dev);
+
+	session->auth_id = btd_request_authorization(src, dst,
+							AVRCP_REMOTE_UUID,
+							auth_cb, session);
+	if (session->auth_id == 0)
+		goto drop;
+
+	session->control->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP |
+						G_IO_NVAL, session_cb, session);
+	return;
+
+drop:
+	avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+}
+
+static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
+						struct btd_device *dev)
+{
+	GError *err = NULL;
+
+	if (session->control == NULL || session->browsing != NULL) {
+		error("Browsing: Refusing unexpected connect");
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	if (bt_io_accept(chan, avctp_connect_browsing_cb, session, NULL,
+								&err)) {
+		avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0);
+		return;
+	}
+
+	error("Browsing: %s", err->message);
+	g_error_free(err);
+
+	return;
+}
+
+static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+	struct avctp *session;
+	char address[18];
+	bdaddr_t src, dst;
+	GError *err = NULL;
+	uint16_t psm;
+	struct btd_device *device;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_PSM, &psm,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	DBG("AVCTP: incoming connect from %s", address);
+
+	device = btd_adapter_find_device(adapter_find(&src), &dst,
+								BDADDR_BREDR);
+	if (!device)
+		return;
+
+	session = avctp_get_internal(device);
+	if (session == NULL)
+		return;
+
+	if (btd_device_get_service(device, AVRCP_REMOTE_UUID) == NULL)
+		btd_device_add_uuid(device, AVRCP_REMOTE_UUID);
+
+	if (btd_device_get_service(device, AVRCP_TARGET_UUID) == NULL)
+		btd_device_add_uuid(device, AVRCP_TARGET_UUID);
+
+	switch (psm) {
+	case AVCTP_CONTROL_PSM:
+		avctp_control_confirm(session, chan, device);
+		break;
+	case AVCTP_BROWSING_PSM:
+		avctp_browsing_confirm(session, chan, device);
+		break;
+	}
+
+	return;
+}
+
+static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master,
+						uint8_t mode, uint16_t psm)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+
+	io = bt_io_listen(NULL, avctp_confirm_cb, NULL,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_PSM, psm,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_MASTER, master,
+				BT_IO_OPT_MODE, mode,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("%s", err->message);
+		g_error_free(err);
+	}
+
+	return io;
+}
+
+int avctp_register(struct btd_adapter *adapter, gboolean master)
+{
+	struct avctp_server *server;
+	const bdaddr_t *src = btd_adapter_get_address(adapter);
+
+	server = g_new0(struct avctp_server, 1);
+
+	server->control_io = avctp_server_socket(src, master, L2CAP_MODE_BASIC,
+							AVCTP_CONTROL_PSM);
+	if (!server->control_io) {
+		g_free(server);
+		return -1;
+	}
+	server->browsing_io = avctp_server_socket(src, master, L2CAP_MODE_ERTM,
+							AVCTP_BROWSING_PSM);
+	if (!server->browsing_io) {
+		if (server->control_io) {
+			g_io_channel_shutdown(server->control_io, TRUE, NULL);
+			g_io_channel_unref(server->control_io);
+			server->control_io = NULL;
+		}
+		g_free(server);
+		return -1;
+	}
+
+	server->adapter = btd_adapter_ref(adapter);
+
+	servers = g_slist_append(servers, server);
+
+	return 0;
+}
+
+void avctp_unregister(struct btd_adapter *adapter)
+{
+	struct avctp_server *server;
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	while (server->sessions)
+		avctp_disconnected(server->sessions->data);
+
+	servers = g_slist_remove(servers, server);
+
+	g_io_channel_shutdown(server->browsing_io, TRUE, NULL);
+	g_io_channel_unref(server->browsing_io);
+	server->browsing_io = NULL;
+
+	g_io_channel_shutdown(server->control_io, TRUE, NULL);
+	g_io_channel_unref(server->control_io);
+	btd_adapter_unref(server->adapter);
+	g_free(server);
+}
+
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+						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->process = process;
+	p->data = data;
+	p->destroy = destroy;
+
+	chan->transaction++;
+	chan->transaction %= 16;
+
+	return p;
+}
+
+static int avctp_send_req(struct avctp *session, uint8_t code,
+				uint8_t subunit, uint8_t opcode,
+				uint8_t *operands, size_t operand_count,
+				avctp_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_pending_req *p;
+	struct avctp_control_req *req;
+
+	if (control == NULL)
+		return -ENOTCONN;
+
+	req = g_new0(struct avctp_control_req, 1);
+	req->code = code;
+	req->subunit = subunit;
+	req->op = opcode;
+	req->func = func;
+	req->operands = g_memdup(operands, operand_count);
+	req->operand_count = operand_count;
+	req->user_data = user_data;
+
+	p = pending_create(control, process_control, req, control_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(control->queue, p);
+
+	if (control->process_id == 0)
+		control->process_id = g_idle_add(process_queue, control);
+
+	return 0;
+}
+
+int avctp_send_browsing_req(struct avctp *session,
+				uint8_t *operands, size_t operand_count,
+				avctp_browsing_rsp_cb func, void *user_data)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_pending_req *p;
+	struct avctp_browsing_req *req;
+
+	if (browsing == NULL)
+		return -ENOTCONN;
+
+	req = g_new0(struct avctp_browsing_req, 1);
+	req->func = func;
+	req->operands = g_memdup(operands, operand_count);
+	req->operand_count = operand_count;
+	req->user_data = user_data;
+
+	p = pending_create(browsing, process_browsing, req,
+			browsing_req_destroy);
+
+	req->p = p;
+
+	g_queue_push_tail(browsing->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);
+
+	return 0;
+}
+
+static const char *op2str(uint8_t op)
+{
+	int i;
+
+	for (i = 0; key_map[i].name != NULL; i++) {
+		if ((op & 0x7F) == key_map[i].avc)
+			return key_map[i].name;
+	}
+
+	return "UNKNOWN";
+}
+
+static int avctp_passthrough_press(struct avctp *session, uint8_t op)
+{
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	/* Button pressed */
+	operands[0] = op & 0x7f;
+	operands[1] = 0;
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				operands, sizeof(operands),
+				avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op)
+{
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	/* Button released */
+	operands[0] = op | 0x80;
+	operands[1] = 0;
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				operands, sizeof(operands),
+				NULL, NULL);
+}
+
+static gboolean repeat_timeout(gpointer user_data)
+{
+	struct avctp *session = user_data;
+
+	avctp_passthrough_release(session, session->key.op);
+	avctp_passthrough_press(session, session->key.op);
+
+	return TRUE;
+}
+
+static void release_pressed(struct avctp *session)
+{
+	avctp_passthrough_release(session, session->key.op);
+
+	if (session->key.timer > 0)
+		g_source_remove(session->key.timer);
+
+	session->key.timer = 0;
+}
+
+static bool set_pressed(struct avctp *session, uint8_t op)
+{
+	if (session->key.timer > 0) {
+		if (session->key.op == op)
+			return TRUE;
+		release_pressed(session);
+	}
+
+	if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+		return FALSE;
+
+	session->key.op = op;
+	session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+							repeat_timeout,
+							session);
+
+	return TRUE;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	if (code != AVC_CTYPE_ACCEPTED)
+		return FALSE;
+
+	if (set_pressed(session, operands[0]))
+		return FALSE;
+
+	avctp_passthrough_release(session, operands[0]);
+
+	return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+	/* Auto release if key pressed */
+	if (session->key.timer > 0)
+		release_pressed(session);
+
+	return avctp_passthrough_press(session, op);
+}
+
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				uint8_t *operands, size_t operand_count)
+{
+	struct avctp_channel *control = session->control;
+
+	if (control == NULL)
+		return -ENOTCONN;
+
+	return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
+					AVC_OP_VENDORDEP, operands, operand_count);
+}
+
+int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count,
+					avctp_rsp_cb func, void *user_data)
+{
+	return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP,
+						operands, operand_count,
+						func, user_data);
+}
+
+unsigned int avctp_add_state_cb(struct btd_device *dev, avctp_state_cb cb,
+							void *user_data)
+{
+	struct avctp_state_callback *state_cb;
+	static unsigned int id = 0;
+
+	state_cb = g_new(struct avctp_state_callback, 1);
+	state_cb->cb = cb;
+	state_cb->dev = dev;
+	state_cb->id = ++id;
+	state_cb->user_data = user_data;
+
+	callbacks = g_slist_append(callbacks, state_cb);
+
+	return state_cb->id;
+}
+
+gboolean avctp_remove_state_cb(unsigned int id)
+{
+	GSList *l;
+
+	for (l = callbacks; l != NULL; l = l->next) {
+		struct avctp_state_callback *cb = l->data;
+		if (cb && cb->id == id) {
+			callbacks = g_slist_remove(callbacks, cb);
+			g_free(cb);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+						avctp_passthrough_cb cb,
+						void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_passthrough_handler *handler;
+	static unsigned int id = 0;
+
+	if (control == NULL || session->handler != NULL)
+		return 0;
+
+	handler = g_new(struct avctp_passthrough_handler, 1);
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	session->handler = handler;
+
+	return handler->id;
+}
+
+bool avctp_unregister_passthrough_handler(unsigned int id)
+{
+	GSList *l;
+
+	for (l = servers; l; l = l->next) {
+		struct avctp_server *server = l->data;
+		GSList *s;
+
+		for (s = server->sessions; s; s = s->next) {
+			struct avctp *session = s->data;
+
+			if (session->handler == NULL)
+				continue;
+
+			if (session->handler->id == id) {
+				g_free(session->handler);
+				session->handler = NULL;
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+						avctp_control_pdu_cb cb,
+						void *user_data)
+{
+	struct avctp_channel *control = session->control;
+	struct avctp_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	if (control == NULL)
+		return 0;
+
+	handler = find_handler(control->handlers, opcode);
+	if (handler)
+		return 0;
+
+	handler = g_new(struct avctp_pdu_handler, 1);
+	handler->opcode = opcode;
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+
+	control->handlers = g_slist_append(control->handlers, handler);
+
+	return handler->id;
+}
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+						avctp_browsing_pdu_cb cb,
+						void *user_data,
+						GDestroyNotify destroy)
+{
+	struct avctp_channel *browsing = session->browsing;
+	struct avctp_browsing_pdu_handler *handler;
+	static unsigned int id = 0;
+
+	if (browsing == NULL)
+		return 0;
+
+	if (browsing->handlers != NULL)
+		return 0;
+
+	handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	handler->cb = cb;
+	handler->user_data = user_data;
+	handler->id = ++id;
+	handler->destroy = destroy;
+
+	browsing->handlers = g_slist_append(browsing->handlers, handler);
+
+	return handler->id;
+}
+
+gboolean avctp_unregister_pdu_handler(unsigned int id)
+{
+	GSList *l;
+
+	for (l = servers; l; l = l->next) {
+		struct avctp_server *server = l->data;
+		GSList *s;
+
+		for (s = server->sessions; s; s = s->next) {
+			struct avctp *session = s->data;
+			struct avctp_channel *control = session->control;
+			GSList *h;
+
+			if (control == NULL)
+				continue;
+
+			for (h = control->handlers; h; h = h->next) {
+				struct avctp_pdu_handler *handler = h->data;
+
+				if (handler->id != id)
+					continue;
+
+				control->handlers = g_slist_remove(
+							control->handlers,
+							handler);
+				g_free(handler);
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+gboolean avctp_unregister_browsing_pdu_handler(unsigned int id)
+{
+	GSList *l;
+
+	for (l = servers; l; l = l->next) {
+		struct avctp_server *server = l->data;
+		GSList *s;
+
+		for (s = server->sessions; s; s = s->next) {
+			struct avctp *session = s->data;
+			struct avctp_channel *browsing = session->browsing;
+			GSList *h;
+
+			if (browsing == NULL)
+				continue;
+
+			for (h = browsing->handlers; h; h = h->next) {
+				struct avctp_browsing_pdu_handler *handler =
+								h->data;
+
+				if (handler->id != id)
+					continue;
+
+				browsing->handlers = g_slist_remove(
+							browsing->handlers,
+							handler);
+				g_free(handler);
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+struct avctp *avctp_connect(struct btd_device *device)
+{
+	struct avctp *session;
+	GError *err = NULL;
+	GIOChannel *io;
+	const bdaddr_t *src;
+
+	session = avctp_get_internal(device);
+	if (!session)
+		return NULL;
+
+	if (session->state > AVCTP_STATE_DISCONNECTED)
+		return session;
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
+
+	src = btd_adapter_get_address(session->server->adapter);
+
+	io = bt_io_connect(avctp_connect_cb, session, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_DEST_BDADDR,
+				device_get_address(session->device),
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
+				BT_IO_OPT_INVALID);
+	if (err) {
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+		error("%s", err->message);
+		g_error_free(err);
+		return NULL;
+	}
+
+	session->control = avctp_channel_create(session, io, NULL);
+	session->initiator = true;
+	g_io_channel_unref(io);
+
+	return session;
+}
+
+int avctp_connect_browsing(struct avctp *session)
+{
+	const bdaddr_t *src;
+	GError *err = NULL;
+	GIOChannel *io;
+
+	if (session->state != AVCTP_STATE_CONNECTED)
+		return -ENOTCONN;
+
+	if (session->browsing != NULL)
+		return 0;
+
+	avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0);
+
+	src = btd_adapter_get_address(session->server->adapter);
+
+	io = bt_io_connect(avctp_connect_browsing_cb, session, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_DEST_BDADDR,
+				device_get_address(session->device),
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_PSM, AVCTP_BROWSING_PSM,
+				BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+				BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return -EIO;
+	}
+
+	session->browsing = avctp_channel_create(session, io,
+						avctp_destroy_browsing);
+	g_io_channel_unref(io);
+
+	return 0;
+}
+
+void avctp_disconnect(struct avctp *session)
+{
+	if (session->state == AVCTP_STATE_DISCONNECTED)
+		return;
+
+	avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
+}
+
+struct avctp *avctp_get(struct btd_device *device)
+{
+	return avctp_get_internal(device);
+}
+
+bool avctp_is_initiator(struct avctp *session)
+{
+	return session->initiator;
+}
diff --git a/bluez/repo/profiles/audio/avctp.h b/bluez/repo/profiles/audio/avctp.h
new file mode 100644
index 0000000..6c19ce4
--- /dev/null
+++ b/bluez/repo/profiles/audio/avctp.h
@@ -0,0 +1,184 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+#define AVCTP_CONTROL_PSM		23
+#define AVCTP_BROWSING_PSM		27
+
+#define AVC_MTU 512
+#define AVC_HEADER_LENGTH 3
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL		0x0
+#define AVC_CTYPE_STATUS		0x1
+#define AVC_CTYPE_NOTIFY		0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED	0x8
+#define AVC_CTYPE_ACCEPTED		0x9
+#define AVC_CTYPE_REJECTED		0xA
+#define AVC_CTYPE_STABLE		0xC
+#define AVC_CTYPE_CHANGED		0xD
+#define AVC_CTYPE_INTERIM		0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP		0x00
+#define AVC_OP_UNITINFO			0x30
+#define AVC_OP_SUBUNITINFO		0x31
+#define AVC_OP_PASSTHROUGH		0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL		0x09
+
+/* operands in passthrough commands */
+#define AVC_SELECT			0x00
+#define AVC_UP				0x01
+#define AVC_DOWN			0x02
+#define AVC_LEFT			0x03
+#define AVC_RIGHT			0x04
+#define AVC_ROOT_MENU			0x09
+#define AVC_CONTENTS_MENU		0x0b
+#define AVC_FAVORITE_MENU		0x0c
+#define AVC_EXIT			0x0d
+#define AVC_ON_DEMAND_MENU		0x0e
+#define AVC_APPS_MENU			0x0f
+#define AVC_0				0x20
+#define AVC_1				0x21
+#define AVC_2				0x22
+#define AVC_3				0x23
+#define AVC_4				0x24
+#define AVC_5				0x25
+#define AVC_6				0x26
+#define AVC_7				0x27
+#define AVC_8				0x28
+#define AVC_9				0x29
+#define AVC_DOT				0x2a
+#define AVC_ENTER			0x2b
+#define AVC_CHANNEL_UP			0x30
+#define AVC_CHANNEL_DOWN		0x31
+#define AVC_CHANNEL_PREVIOUS		0x32
+#define AVC_INPUT_SELECT		0x34
+#define AVC_INFO			0x35
+#define AVC_HELP			0x36
+#define AVC_PAGE_UP			0x37
+#define AVC_PAGE_DOWN			0x38
+#define AVC_LOCK			0x3a
+#define AVC_POWER			0x40
+#define AVC_VOLUME_UP			0x41
+#define AVC_VOLUME_DOWN			0x42
+#define AVC_MUTE			0x43
+#define AVC_PLAY			0x44
+#define AVC_STOP			0x45
+#define AVC_PAUSE			0x46
+#define AVC_RECORD			0x47
+#define AVC_REWIND			0x48
+#define AVC_FAST_FORWARD		0x49
+#define AVC_EJECT			0x4a
+#define AVC_FORWARD			0x4b
+#define AVC_BACKWARD			0x4c
+#define AVC_LIST			0x4d
+#define AVC_F1				0x71
+#define AVC_F2				0x72
+#define AVC_F3				0x73
+#define AVC_F4				0x74
+#define AVC_F5				0x75
+#define AVC_F6				0x76
+#define AVC_F7				0x77
+#define AVC_F8				0x78
+#define AVC_F9				0x79
+#define AVC_RED				0x7a
+#define AVC_GREEN			0x7b
+#define AVC_BLUE			0x7c
+#define AVC_YELLOW			0x7c
+
+struct avctp;
+
+typedef enum {
+	AVCTP_STATE_DISCONNECTED = 0,
+	AVCTP_STATE_CONNECTING,
+	AVCTP_STATE_CONNECTED,
+	AVCTP_STATE_BROWSING_CONNECTING,
+	AVCTP_STATE_BROWSING_CONNECTED
+} avctp_state_t;
+
+typedef void (*avctp_state_cb) (struct btd_device *dev,
+				avctp_state_t old_state,
+				avctp_state_t new_state,
+				int err, void *user_data);
+
+typedef bool (*avctp_passthrough_cb) (struct avctp *session,
+					uint8_t op, bool pressed,
+					void *user_data);
+typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
+
+unsigned int avctp_add_state_cb(struct btd_device *dev, avctp_state_cb cb,
+							void *user_data);
+gboolean avctp_remove_state_cb(unsigned int id);
+
+int avctp_register(struct btd_adapter *adapter, gboolean master);
+void avctp_unregister(struct btd_adapter *adapter);
+
+struct avctp *avctp_connect(struct btd_device *device);
+struct avctp *avctp_get(struct btd_device *device);
+bool avctp_is_initiator(struct avctp *session);
+int avctp_connect_browsing(struct avctp *session);
+void avctp_disconnect(struct avctp *session);
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+						avctp_passthrough_cb cb,
+						void *user_data);
+bool avctp_unregister_passthrough_handler(unsigned int id);
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+						avctp_control_pdu_cb cb,
+						void *user_data);
+gboolean avctp_unregister_pdu_handler(unsigned int id);
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+						avctp_browsing_pdu_cb cb,
+						void *user_data,
+						GDestroyNotify destroy);
+gboolean avctp_unregister_browsing_pdu_handler(unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op);
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+				uint8_t code, uint8_t subunit,
+				uint8_t *operands, size_t operand_count);
+int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count,
+					avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing_req(struct avctp *session,
+				uint8_t *operands, size_t operand_count,
+				avctp_browsing_rsp_cb func, void *user_data);
diff --git a/bluez/repo/profiles/audio/avdtp.c b/bluez/repo/profiles/audio/avdtp.c
new file mode 100644
index 0000000..52ee767
--- /dev/null
+++ b/bluez/repo/profiles/audio/avdtp.c
@@ -0,0 +1,3679 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/adapter.h"
+#include "src/device.h"
+
+#include "avdtp.h"
+#include "sink.h"
+#include "source.h"
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+static unsigned int seids;
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER				0x01
+#define AVDTP_GET_CAPABILITIES			0x02
+#define AVDTP_SET_CONFIGURATION			0x03
+#define AVDTP_GET_CONFIGURATION			0x04
+#define AVDTP_RECONFIGURE			0x05
+#define AVDTP_OPEN				0x06
+#define AVDTP_START				0x07
+#define AVDTP_CLOSE				0x08
+#define AVDTP_SUSPEND				0x09
+#define AVDTP_ABORT				0x0A
+#define AVDTP_SECURITY_CONTROL			0x0B
+#define AVDTP_GET_ALL_CAPABILITIES		0x0C
+#define AVDTP_DELAY_REPORT			0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE			0x00
+#define AVDTP_PKT_TYPE_START			0x01
+#define AVDTP_PKT_TYPE_CONTINUE			0x02
+#define AVDTP_PKT_TYPE_END			0x03
+
+#define AVDTP_MSG_TYPE_COMMAND			0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT		0x01
+#define AVDTP_MSG_TYPE_ACCEPT			0x02
+#define AVDTP_MSG_TYPE_REJECT			0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define START_TIMEOUT 1
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t no_of_packets;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t rfa0:1;
+	uint8_t inuse:1;
+	uint8_t seid:6;
+	uint8_t rfa2:3;
+	uint8_t type:1;
+	uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t rfa0:2;
+	uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t no_of_packets;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t seid:6;
+	uint8_t inuse:1;
+	uint8_t rfa0:1;
+	uint8_t media_type:4;
+	uint8_t type:1;
+	uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+	struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+	uint8_t category;
+	uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t rfa1:2;
+	uint8_t int_seid:6;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t int_seid:6;
+	uint8_t rfa1:2;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+	gboolean active;
+	int no_of_packets;
+	uint8_t transaction;
+	uint8_t message_type;
+	uint8_t signal_id;
+	uint8_t buf[1024];
+	uint8_t data_size;
+};
+
+struct pending_req {
+	uint8_t transaction;
+	uint8_t signal_id;
+	void *data;
+	size_t data_size;
+	struct avdtp_stream *stream; /* Set if the request targeted a stream */
+	guint timeout;
+	gboolean collided;
+};
+
+struct avdtp_remote_sep {
+	uint8_t seid;
+	uint8_t type;
+	uint8_t media_type;
+	struct avdtp_service_capability *codec;
+	gboolean delay_reporting;
+	GSList *caps; /* of type struct avdtp_service_capability */
+	struct avdtp_stream *stream;
+};
+
+struct avdtp_local_sep {
+	avdtp_state_t state;
+	struct avdtp_stream *stream;
+	struct seid_info info;
+	uint8_t codec;
+	gboolean delay_reporting;
+	GSList *caps;
+	struct avdtp_sep_ind *ind;
+	struct avdtp_sep_cfm *cfm;
+	void *user_data;
+};
+
+struct stream_callback {
+	avdtp_stream_state_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct avdtp_state_callback {
+	avdtp_session_state_cb cb;
+	struct btd_device *dev;
+	unsigned int id;
+	void *user_data;
+};
+
+struct discover_callback {
+	unsigned int id;
+	avdtp_discover_cb_t cb;
+	void *user_data;
+};
+
+struct avdtp_stream {
+	GIOChannel *io;
+	uint16_t imtu;
+	uint16_t omtu;
+	struct avdtp *session;
+	struct avdtp_local_sep *lsep;
+	uint8_t rseid;
+	GSList *caps;
+	GSList *callbacks;
+	struct avdtp_service_capability *codec;
+	guint io_id;		/* Transport GSource ID */
+	guint timer;		/* Waiting for other side to close or open
+				 * the transport channel */
+	gboolean open_acp;	/* If we are in ACT role for Open */
+	gboolean close_int;	/* If we are in INT role for Close */
+	gboolean abort_int;	/* If we are in INT role for Abort */
+	guint start_timer;	/* Wait START command timer */
+	gboolean delay_reporting;
+	uint16_t delay;		/* AVDTP 1.3 Delay Reporting feature */
+	gboolean starting;	/* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+	unsigned int ref;
+
+	uint16_t version;
+
+	struct queue *lseps;
+	struct btd_device *device;
+
+	avdtp_session_state_t state;
+
+	GIOChannel *io;
+	guint io_id;
+
+	GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+
+	GSList *streams; /* Elements of type struct avdtp_stream * */
+
+	GSList *req_queue; /* Elements of type struct pending_req * */
+	GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+	struct avdtp_stream *pending_open;
+
+	uint16_t imtu;
+	uint16_t omtu;
+
+	struct in_buf in;
+
+	char *buf;
+
+	struct discover_callback *discover;
+	struct pending_req *req;
+
+	guint dc_timer;
+
+	/* Attempt stream setup instead of disconnecting */
+	gboolean stream_setup;
+};
+
+static GSList *state_callbacks = NULL;
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state);
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+	switch (state) {
+	case AVDTP_STATE_IDLE:
+		return "IDLE";
+	case AVDTP_STATE_CONFIGURED:
+		return "CONFIGURED";
+	case AVDTP_STATE_OPEN:
+		return "OPEN";
+	case AVDTP_STATE_STREAMING:
+		return "STREAMING";
+	case AVDTP_STATE_CLOSING:
+		return "CLOSING";
+	case AVDTP_STATE_ABORTING:
+		return "ABORTING";
+	default:
+		return "<unknown state>";
+	}
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+	int err;
+
+	do {
+		err = send(sk, data, len, 0);
+	} while (err < 0 && errno == EINTR);
+
+	if (err < 0) {
+		error("send: %s (%d)", strerror(errno), errno);
+		return FALSE;
+	} else if ((size_t) err != len) {
+		error("try_send: complete buffer not sent (%d/%zu bytes)",
+								err, len);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+				uint8_t message_type, uint8_t signal_id,
+				void *data, size_t len)
+{
+	unsigned int cont_fragments, sent;
+	struct avdtp_start_header start;
+	struct avdtp_continue_header cont;
+	int sock;
+
+	if (session->io == NULL) {
+		error("avdtp_send: session is closed");
+		return FALSE;
+	}
+
+	sock = g_io_channel_unix_get_fd(session->io);
+
+	/* Single packet - no fragmentation */
+	if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+		struct avdtp_single_header single;
+
+		memset(&single, 0, sizeof(single));
+
+		single.transaction = transaction;
+		single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+		single.message_type = message_type;
+		single.signal_id = signal_id;
+
+		memcpy(session->buf, &single, sizeof(single));
+		memcpy(session->buf + sizeof(single), data, len);
+
+		return try_send(sock, session->buf, sizeof(single) + len);
+	}
+
+	/* Check if there is enough space to start packet */
+	if (session->omtu < sizeof(start)) {
+		error("No enough space to fragment packet");
+		return FALSE;
+	}
+
+	/* Count the number of needed fragments */
+	cont_fragments = (len - (session->omtu - sizeof(start))) /
+					(session->omtu - sizeof(cont)) + 1;
+
+	DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+	/* Send the start packet */
+	memset(&start, 0, sizeof(start));
+	start.transaction = transaction;
+	start.packet_type = AVDTP_PKT_TYPE_START;
+	start.message_type = message_type;
+	start.no_of_packets = cont_fragments + 1;
+	start.signal_id = signal_id;
+
+	memcpy(session->buf, &start, sizeof(start));
+	memcpy(session->buf + sizeof(start), data,
+					session->omtu - sizeof(start));
+
+	if (!try_send(sock, session->buf, session->omtu))
+		return FALSE;
+
+	DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+	sent = session->omtu - sizeof(start);
+
+	/* Send the continue fragments and the end packet */
+	while (sent < len) {
+		int left, to_copy;
+
+		left = len - sent;
+		if (left + sizeof(cont) > session->omtu) {
+			cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+			to_copy = session->omtu - sizeof(cont);
+			DBG("sending continue with %d bytes", to_copy);
+		} else {
+			cont.packet_type = AVDTP_PKT_TYPE_END;
+			to_copy = left;
+			DBG("sending end with %d bytes", to_copy);
+		}
+
+		cont.transaction = transaction;
+		cont.message_type = message_type;
+
+		memcpy(session->buf, &cont, sizeof(cont));
+		memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+		if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+			return FALSE;
+
+		sent += to_copy;
+	}
+
+	return TRUE;
+}
+
+static void pending_req_free(void *data)
+{
+	struct pending_req *req = data;
+
+	if (req->timeout)
+		g_source_remove(req->timeout);
+	g_free(req->data);
+	g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+	int sock;
+
+	if (stream->io == NULL)
+		return;
+
+	sock = g_io_channel_unix_get_fd(stream->io);
+
+	shutdown(sock, SHUT_RDWR);
+
+	g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+	g_io_channel_unref(stream->io);
+	stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to close the transport channel");
+
+	stream->timer = 0;
+
+	close_stream(stream);
+
+	return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to open the transport channel");
+
+	stream->timer = 0;
+
+	stream->session->pending_open = NULL;
+
+	avdtp_abort(stream->session, stream);
+
+	return FALSE;
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+	err->category = category;
+
+	if (category == AVDTP_ERRNO)
+		err->err.posix_errno = id;
+	else
+		err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+	return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+	assert(err->category != AVDTP_ERRNO);
+	return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+	assert(err->category == AVDTP_ERRNO);
+	return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+							uint8_t rseid)
+{
+	GSList *l;
+
+	for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_stream *stream = l->data;
+
+		if (stream->rseid == rseid)
+			return stream;
+	}
+
+	return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+	GSList *l;
+
+	for (l = seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static void avdtp_set_state(struct avdtp *session,
+					avdtp_session_state_t new_state)
+{
+	GSList *l;
+	avdtp_session_state_t old_state = session->state;
+
+	session->state = new_state;
+
+	for (l = state_callbacks; l != NULL;) {
+		struct avdtp_state_callback *cb = l->data;
+
+		l = g_slist_next(l);
+
+		if (session->device != cb->dev)
+			continue;
+
+		cb->cb(cb->dev, session, old_state, new_state, cb->user_data);
+	}
+}
+
+static void stream_free(void *data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_remote_sep *rsep;
+
+	stream->lsep->info.inuse = 0;
+	stream->lsep->stream = NULL;
+
+	rsep = find_remote_sep(stream->session->seps, stream->rseid);
+	if (rsep)
+		rsep->stream = NULL;
+
+	if (stream->timer)
+		g_source_remove(stream->timer);
+
+	if (stream->io)
+		close_stream(stream);
+
+	if (stream->io_id)
+		g_source_remove(stream->io_id);
+
+	g_slist_free_full(stream->callbacks, g_free);
+	g_slist_free_full(stream->caps, g_free);
+
+	g_free(stream);
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (stream->close_int && sep->cfm && sep->cfm->close)
+		sep->cfm->close(stream->session, sep, stream, NULL,
+				sep->user_data);
+
+	if (!(cond & G_IO_NVAL))
+		close_stream(stream);
+
+	stream->io_id = 0;
+
+	if (!stream->abort_int)
+		avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+	return FALSE;
+}
+
+static int get_send_buffer_size(int sk)
+{
+	int size;
+	socklen_t optlen = sizeof(size);
+
+	if (getsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, &optlen) < 0) {
+		int err = -errno;
+		error("getsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err),
+									-err);
+		return err;
+	}
+
+	/*
+	 * Doubled value is returned by getsockopt since kernel uses that
+	 * space for its own purposes (see man 7 socket, bookkeeping overhead
+	 * for SO_SNDBUF).
+	 */
+	return size / 2;
+}
+
+static int set_send_buffer_size(int sk, int size)
+{
+	socklen_t optlen = sizeof(size);
+
+	if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, optlen) < 0) {
+		int err = -errno;
+		error("setsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err),
+									-err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+					uint16_t imtu, uint16_t omtu)
+{
+	struct avdtp_stream *stream = session->pending_open;
+	struct avdtp_local_sep *sep = stream->lsep;
+	int sk, buf_size, min_buf_size;
+	GError *err = NULL;
+
+	session->pending_open = NULL;
+
+	if (stream->timer) {
+		g_source_remove(stream->timer);
+		stream->timer = 0;
+	}
+
+	if (io == NULL) {
+		if (!stream->open_acp && sep->cfm && sep->cfm->open) {
+			struct avdtp_error err;
+			avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+			sep->cfm->open(session, sep, NULL, &err,
+					sep->user_data);
+		}
+		return;
+	}
+
+	if (stream->io == NULL)
+		stream->io = g_io_channel_ref(io);
+
+	stream->omtu = omtu;
+	stream->imtu = imtu;
+
+	/* Apply special settings only if local SEP is of type SRC */
+	if (sep->info.type != AVDTP_SEP_TYPE_SOURCE)
+		goto proceed;
+
+	bt_io_set(stream->io, &err, BT_IO_OPT_FLUSHABLE, TRUE,
+							BT_IO_OPT_INVALID);
+	if (err != NULL) {
+		error("Enabling flushable packets failed: %s", err->message);
+		g_error_free(err);
+	} else
+		DBG("Flushable packets enabled");
+
+	sk = g_io_channel_unix_get_fd(stream->io);
+	buf_size = get_send_buffer_size(sk);
+	if (buf_size < 0)
+		goto proceed;
+
+	DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size);
+	min_buf_size = omtu * 2;
+	if (buf_size < min_buf_size) {
+		DBG("send buffer size to be increassed to %d",
+				min_buf_size);
+		set_send_buffer_size(sk, min_buf_size);
+	}
+
+proceed:
+	if (!stream->open_acp && sep->cfm && sep->cfm->open)
+		sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct pending_req *req = a;
+	const struct avdtp_stream *stream = b;
+
+	if (req->stream == stream)
+		return 0;
+
+	return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+	GSList *l;
+	struct pending_req *req;
+
+	while ((l = g_slist_find_custom(session->prio_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->prio_queue = g_slist_remove(session->prio_queue, req);
+	}
+
+	while ((l = g_slist_find_custom(session->req_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->req_queue = g_slist_remove(session->req_queue, req);
+	}
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+						struct avdtp_stream *stream)
+{
+	struct pending_req *req;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_error err;
+
+	if (session->req->signal_id == AVDTP_ABORT) {
+		/* Avoid freeing the Abort request here */
+		DBG("handle_unanswered_req: Abort req, returning");
+		session->req->stream = NULL;
+		return;
+	}
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+	lsep = stream->lsep;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("No reply to Reconfigure request");
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("No reply to Open request");
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &err,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("No reply to Start request");
+		if (lsep && lsep->cfm && lsep->cfm->start)
+			lsep->cfm->start(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SUSPEND:
+		error("No reply to Suspend request");
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("No reply to Close request");
+		if (lsep && lsep->cfm && lsep->cfm->close)
+			lsep->cfm->close(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("No reply to SetConfiguration request");
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+							&err, lsep->user_data);
+	}
+
+	pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state)
+{
+	struct avdtp_stream *stream = sep->stream;
+	avdtp_state_t old_state;
+	struct avdtp_error err, *err_ptr = NULL;
+	GSList *l;
+
+	if (!stream) {
+		error("Error changing sep state: stream not available");
+		return;
+	}
+
+	if (sep->state == state) {
+		avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+		DBG("stream state change failed: %s", avdtp_strerror(&err));
+		err_ptr = &err;
+	} else {
+		err_ptr = NULL;
+		DBG("stream state changed: %s -> %s",
+				avdtp_statestr(sep->state),
+				avdtp_statestr(state));
+	}
+
+	old_state = sep->state;
+	sep->state = state;
+
+	switch (state) {
+	case AVDTP_STATE_CONFIGURED:
+		if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+			avdtp_delay_report(session, stream, stream->delay);
+		break;
+	case AVDTP_STATE_OPEN:
+		stream->starting = FALSE;
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		stream->open_acp = FALSE;
+		break;
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		break;
+	case AVDTP_STATE_IDLE:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		if (session->pending_open == stream)
+			handle_transport_connect(session, NULL, 0, 0);
+		if (session->req && session->req->stream == stream)
+			handle_unanswered_req(session, stream);
+		/* Remove pending commands for this stream from the queue */
+		cleanup_queue(session, stream);
+		break;
+	default:
+		break;
+	}
+
+	l = stream->callbacks;
+	while (l != NULL) {
+		struct stream_callback *cb = l->data;
+		l = g_slist_next(l);
+		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);
+		stream_free(stream);
+	}
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+	struct discover_callback *discover = session->discover;
+	struct avdtp_error avdtp_err;
+
+	if (!discover)
+		return;
+
+	avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
+
+	if (discover->id > 0)
+		g_source_remove(discover->id);
+
+	if (discover->cb)
+		discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+							discover->user_data);
+	g_free(discover);
+	session->discover = NULL;
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->abort &&
+				(sep->state != AVDTP_STATE_ABORTING ||
+							stream->abort_int))
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void sep_free(gpointer data)
+{
+	struct avdtp_remote_sep *sep = data;
+
+	g_slist_free_full(sep->caps, g_free);
+	g_free(sep);
+}
+
+static void remove_disconnect_timer(struct avdtp *session)
+{
+	g_source_remove(session->dc_timer);
+	session->dc_timer = 0;
+	session->stream_setup = FALSE;
+}
+
+static void avdtp_free(void *data)
+{
+	struct avdtp *session = data;
+
+	DBG("%p", session);
+
+	g_slist_free_full(session->streams, stream_free);
+
+	if (session->io) {
+		g_io_channel_shutdown(session->io, FALSE, NULL);
+		g_io_channel_unref(session->io);
+	}
+
+	if (session->io_id) {
+		g_source_remove(session->io_id);
+		session->io_id = 0;
+	}
+
+	if (session->dc_timer)
+		remove_disconnect_timer(session);
+
+	if (session->req)
+		pending_req_free(session->req);
+
+	g_slist_free_full(session->req_queue, pending_req_free);
+	g_slist_free_full(session->prio_queue, pending_req_free);
+	g_slist_free_full(session->seps, sep_free);
+
+	g_free(session->buf);
+
+	btd_device_unref(session->device);
+	g_free(session);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+	char address[18];
+
+	ba2str(device_get_address(session->device), address);
+	DBG("Disconnected from %s", address);
+
+	g_slist_foreach(session->streams, (GFunc) release_stream, session);
+	session->streams = NULL;
+
+	finalize_discovery(session, err);
+
+	avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+
+	if (session->ref > 0)
+		return;
+
+	avdtp_free(session);
+}
+
+static gboolean disconnect_timeout(gpointer user_data)
+{
+	struct avdtp *session = user_data;
+	struct btd_service *service;
+	gboolean stream_setup;
+
+	session->dc_timer = 0;
+
+	stream_setup = session->stream_setup;
+	session->stream_setup = FALSE;
+
+	service = btd_device_get_service(session->device, A2DP_SINK_UUID);
+	if (service && stream_setup) {
+		sink_setup_stream(service, session);
+		return FALSE;
+	}
+
+	service = btd_device_get_service(session->device, A2DP_SOURCE_UUID);
+	if (service && stream_setup) {
+		source_setup_stream(service, session);
+		return FALSE;
+	}
+
+	connection_lost(session, ETIMEDOUT);
+
+	return FALSE;
+}
+
+static void set_disconnect_timer(struct avdtp *session)
+{
+	if (session->dc_timer)
+		remove_disconnect_timer(session);
+
+	session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+						disconnect_timeout,
+						session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+	if (!session)
+		return;
+
+	session->ref--;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	if (session->ref > 0)
+		return;
+
+	set_disconnect_timer(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+	session->ref++;
+	DBG("%p: ref=%d", session, session->ref);
+	if (session->dc_timer)
+		remove_disconnect_timer(session);
+	return session;
+}
+
+static bool match_by_seid(const void *data, const void *user_data)
+{
+	const struct avdtp_local_sep *sep = data;
+	uint8_t seid = PTR_TO_UINT(user_data);
+
+	return sep->info.seid == seid;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
+								uint8_t seid)
+{
+	return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep)
+{
+	GSList *l;
+
+	if (lsep->info.inuse)
+		return NULL;
+
+	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+		struct avdtp_service_capability *cap;
+		struct avdtp_media_codec_capability *codec_data;
+
+		/* Type must be different: source <-> sink */
+		if (sep->type == lsep->info.type)
+			continue;
+
+		if (sep->media_type != lsep->info.media_type)
+			continue;
+
+		if (!sep->codec)
+			continue;
+
+		cap = sep->codec;
+		codec_data = (void *) cap->data;
+
+		if (codec_data->media_codec_type != lsep->codec)
+			continue;
+
+		if (lsep->ind && lsep->ind->match_codec)
+			if (!lsep->ind->match_codec(session, codec_data,
+							lsep->user_data))
+				continue;
+
+		if (sep->stream == NULL)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+				struct avdtp_service_capability **codec,
+				gboolean *delay_reporting)
+{
+	GSList *caps;
+	int processed;
+
+	if (delay_reporting)
+		*delay_reporting = FALSE;
+
+	for (processed = 0, caps = NULL; processed + 2 <= size;) {
+		struct avdtp_service_capability *cap;
+		uint8_t length, category;
+
+		category = data[0];
+		length = data[1];
+
+		if (processed + 2 + length > size) {
+			error("Invalid capability data in getcap resp");
+			break;
+		}
+
+		cap = g_malloc(sizeof(struct avdtp_service_capability) +
+					length);
+		memcpy(cap, data, 2 + length);
+
+		processed += 2 + length;
+		data += 2 + length;
+
+		caps = g_slist_append(caps, cap);
+
+		if (category == AVDTP_MEDIA_CODEC &&
+				length >=
+				sizeof(struct avdtp_media_codec_capability))
+			*codec = cap;
+		else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+			*delay_reporting = TRUE;
+	}
+
+	return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+							uint8_t signal_id)
+{
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+							signal_id, NULL, 0);
+}
+
+static void copy_seps(void *data, void *user_data)
+{
+	struct avdtp_local_sep *sep = data;
+	struct seid_info **p = user_data;
+
+	memcpy(*p, &sep->info, sizeof(struct seid_info));
+	*p = *p + 1;
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+							void *buf, int size)
+{
+	unsigned int rsp_size, sep_count;
+	struct seid_info *seps, *p;
+	gboolean ret;
+
+	sep_count = queue_length(session->lseps);
+
+	if (sep_count == 0) {
+		uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+		return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DISCOVER, &err, sizeof(err));
+	}
+
+	rsp_size = sep_count * sizeof(struct seid_info);
+
+	seps = g_new0(struct seid_info, sep_count);
+	p = seps;
+
+	queue_foreach(session->lseps, copy_seps, &p);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_DISCOVER, seps, rsp_size);
+	g_free(seps);
+
+	return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, unsigned int size,
+					gboolean get_all)
+{
+	GSList *l, *caps;
+	struct avdtp_local_sep *sep = NULL;
+	unsigned int rsp_size;
+	uint8_t err, buf[1024], *ptr = buf;
+	uint8_t cmd;
+
+	cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+	if (size < sizeof(struct seid_req)) {
+		err = AVDTP_BAD_LENGTH;
+		goto failed;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (!sep->ind->get_capability(session, sep, get_all, &caps,
+							&err, sep->user_data))
+		goto failed;
+
+	for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+
+		g_free(cap);
+	}
+
+	g_slist_free(caps);
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+								buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+							&err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+						struct avdtp_error *err)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+
+	if (err != NULL) {
+		rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+		rej.category = err->err.error_code;
+		avdtp_send(session, session->in.transaction,
+				AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+				&rej, sizeof(rej));
+		return;
+	}
+
+	if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+		stream_free(stream);
+		return;
+	}
+
+	sep = stream->lsep;
+	sep->stream = stream;
+	sep->info.inuse = 1;
+	session->streams = g_slist_append(session->streams, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+				struct setconf_req *req, unsigned int size)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err, category = 0x00;
+	struct btd_service *service;
+	GSList *l;
+
+	if (size < sizeof(struct setconf_req)) {
+		error("Too short getcap request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->stream) {
+		err = AVDTP_SEP_IN_USE;
+		goto failed;
+	}
+
+	switch (sep->info.type) {
+	case AVDTP_SEP_TYPE_SOURCE:
+		service = btd_device_get_service(session->device,
+							A2DP_SINK_UUID);
+		if (service == NULL) {
+			btd_device_add_uuid(session->device, A2DP_SINK_UUID);
+			service = btd_device_get_service(session->device,
+							A2DP_SINK_UUID);
+			if (service == NULL) {
+				error("Unable to get a audio sink object");
+				err = AVDTP_BAD_STATE;
+				goto failed;
+			}
+		}
+		break;
+	case AVDTP_SEP_TYPE_SINK:
+		service = btd_device_get_service(session->device,
+							A2DP_SOURCE_UUID);
+		if (service == NULL) {
+			btd_device_add_uuid(session->device, A2DP_SOURCE_UUID);
+			service = btd_device_get_service(session->device,
+							A2DP_SOURCE_UUID);
+			if (service == NULL) {
+				error("Unable to get a audio source object");
+				err = AVDTP_BAD_STATE;
+				goto failed;
+			}
+		}
+		break;
+	}
+
+	stream = g_new0(struct avdtp_stream, 1);
+	stream->session = session;
+	stream->lsep = sep;
+	stream->rseid = req->int_seid;
+	stream->caps = caps_to_list(req->caps,
+					size - sizeof(struct setconf_req),
+					&stream->codec,
+					&stream->delay_reporting);
+
+	/* Verify that the Media Transport capability's length = 0. Reject otherwise */
+	for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) {
+			err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+			goto failed_stream;
+		}
+	}
+
+	if (stream->delay_reporting && session->version < 0x0103)
+		session->version = 0x0103;
+
+	if (sep->ind && sep->ind->set_configuration) {
+		if (!sep->ind->set_configuration(session, sep, stream,
+							stream->caps,
+							setconf_cb,
+							sep->user_data)) {
+			err = AVDTP_UNSUPPORTED_CONFIGURATION;
+			category = 0x00;
+			goto failed_stream;
+		}
+	} else {
+		if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+			stream_free(stream);
+			return FALSE;
+		}
+
+		sep->stream = stream;
+		sep->info.inuse = 1;
+		session->streams = g_slist_append(session->streams, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+	}
+
+	return TRUE;
+
+failed_stream:
+	stream_free(stream);
+failed:
+	rej.error = err;
+	rej.category = category;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	GSList *l;
+	struct avdtp_local_sep *sep = NULL;
+	int rsp_size;
+	uint8_t err;
+	uint8_t buf[1024];
+	uint8_t *ptr = buf;
+
+	if (size < (int) sizeof(struct seid_req)) {
+		error("Too short getconf request");
+		return FALSE;
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+	if (!sep->stream || !sep->stream->caps) {
+		err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		goto failed;
+	}
+
+	for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	struct conf_rej rej;
+
+	rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+	rej.category = 0x00;
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+	struct seid_req *seid = req->data;
+
+	if (seid->acp_seid == id)
+		req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+	struct start_req *start = req->data;
+	struct seid *seid = &start->first_seid;
+	int count = 1 + req->data_size - sizeof(struct start_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+	struct suspend_req *suspend = req->data;
+	struct seid *seid = &suspend->first_seid;
+	int count = 1 + req->data_size - sizeof(struct suspend_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+					struct avdtp_stream *stream)
+{
+	struct pending_req *req = session->req;
+
+	if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+		return;
+
+	if (cmd == AVDTP_ABORT)
+		cmd = req->signal_id;
+
+	switch (cmd) {
+	case AVDTP_OPEN:
+	case AVDTP_CLOSE:
+		check_seid_collision(req, stream->rseid);
+		break;
+	case AVDTP_START:
+		check_start_collision(req, stream->rseid);
+		break;
+	case AVDTP_SUSPEND:
+		check_suspend_collision(req, stream->rseid);
+		break;
+	}
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_CONFIGURED) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->open) {
+		if (!sep->ind->open(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_OPEN, NULL, 0))
+		return FALSE;
+
+	stream->open_acp = TRUE;
+	session->pending_open = stream;
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+						stream_open_timeout,
+						stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+				struct start_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct start_req)) {
+		error("Too short start request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct start_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(session, seid->seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		/* Also reject start cmd if state is not open */
+		if (sep->state != AVDTP_STATE_OPEN) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+		stream->starting = TRUE;
+
+		if (sep->ind && sep->ind->start) {
+			if (!sep->ind->start(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_START, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_START, NULL, 0);
+
+failed:
+	DBG("Rejecting (%d)", err);
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short close request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_OPEN &&
+			sep->state != AVDTP_STATE_STREAMING) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->close) {
+		if (!sep->ind->close(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_CLOSE, NULL, 0))
+		return FALSE;
+
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+					stream_close_timeout,
+					stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+				struct suspend_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct suspend_req)) {
+		error("Too short suspend request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct suspend_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(session, seid->seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		if (sep->state != AVDTP_STATE_STREAMING) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+
+		if (sep->ind && sep->ind->suspend) {
+			if (!sep->ind->suspend(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_SUSPEND, NULL, 0);
+
+failed:
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t err;
+	gboolean ret;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream)
+		return TRUE;
+
+	if (sep->ind && sep->ind->abort)
+		sep->ind->abort(session, sep, sep->stream, &err,
+							sep->user_data);
+
+	avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_ABORT, NULL, 0);
+	if (ret)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+					uint8_t transaction,
+					struct delay_req *req,
+					unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct delay_req)) {
+		error("Too short delay report request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(session, req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->state != AVDTP_STATE_CONFIGURED &&
+					sep->state != AVDTP_STATE_STREAMING) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream->delay = ntohs(req->delay);
+
+	if (sep->ind && sep->ind->delayreport) {
+		if (!sep->ind->delayreport(session, sep, stream->rseid,
+						stream->delay, &err,
+						sep->user_data))
+			goto failed;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+				uint8_t signal_id, void *buf, int size)
+{
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("Received DISCOVER_CMD");
+		return avdtp_discover_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CAPABILITIES:
+		DBG("Received  GET_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size,
+									FALSE);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		DBG("Received  GET_ALL_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+	case AVDTP_SET_CONFIGURATION:
+		DBG("Received SET_CONFIGURATION_CMD");
+		return avdtp_setconf_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CONFIGURATION:
+		DBG("Received GET_CONFIGURATION_CMD");
+		return avdtp_getconf_cmd(session, transaction, buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("Received RECONFIGURE_CMD");
+		return avdtp_reconf_cmd(session, transaction, buf, size);
+	case AVDTP_OPEN:
+		DBG("Received OPEN_CMD");
+		return avdtp_open_cmd(session, transaction, buf, size);
+	case AVDTP_START:
+		DBG("Received START_CMD");
+		return avdtp_start_cmd(session, transaction, buf, size);
+	case AVDTP_CLOSE:
+		DBG("Received CLOSE_CMD");
+		return avdtp_close_cmd(session, transaction, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("Received SUSPEND_CMD");
+		return avdtp_suspend_cmd(session, transaction, buf, size);
+	case AVDTP_ABORT:
+		DBG("Received ABORT_CMD");
+		return avdtp_abort_cmd(session, transaction, buf, size);
+	case AVDTP_SECURITY_CONTROL:
+		DBG("Received SECURITY_CONTROL_CMD");
+		return avdtp_secctl_cmd(session, transaction, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("Received DELAY_REPORT_CMD");
+		return avdtp_delayreport_cmd(session, transaction, buf, size);
+	default:
+		DBG("Received unknown request id %u", signal_id);
+		return avdtp_unknown_cmd(session, transaction, signal_id);
+	}
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+							void *buf, size_t size)
+{
+	struct avdtp_common_header *header = buf;
+	struct avdtp_single_header *single = (void *) session->buf;
+	struct avdtp_start_header *start = (void *) session->buf;
+	void *payload;
+	gsize payload_size;
+
+	switch (header->packet_type) {
+	case AVDTP_PKT_TYPE_SINGLE:
+		if (size < sizeof(*single)) {
+			error("Received too small single packet (%zu bytes)", size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("SINGLE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(*single);
+		payload_size = size - sizeof(*single);
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.no_of_packets = 1;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.signal_id = single->signal_id;
+
+		break;
+	case AVDTP_PKT_TYPE_START:
+		if (size < sizeof(*start)) {
+			error("Received too small start packet (%zu bytes)", size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("START: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.no_of_packets = start->no_of_packets;
+		session->in.signal_id = start->signal_id;
+
+		payload = session->buf + sizeof(*start);
+		payload_size = size - sizeof(*start);
+
+		break;
+	case AVDTP_PKT_TYPE_CONTINUE:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small continue packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("CONTINUE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("Continue transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets <= 1) {
+			error("Too few continue packets");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	case AVDTP_PKT_TYPE_END:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small end packet (%zu bytes)", size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("END: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("End transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets > 1) {
+			error("Got an end packet too early");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	default:
+		error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+		return PARSE_ERROR;
+	}
+
+	if (session->in.data_size + payload_size >
+					sizeof(session->in.buf)) {
+		error("Not enough incoming buffer space!");
+		return PARSE_ERROR;
+	}
+
+	memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+	session->in.data_size += payload_size;
+
+	if (session->in.no_of_packets > 1) {
+		session->in.no_of_packets--;
+		DBG("Received AVDTP fragment. %d to go",
+						session->in.no_of_packets);
+		return PARSE_FRAGMENT;
+	}
+
+	session->in.active = FALSE;
+
+	return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp *session = data;
+	struct avdtp_common_header *header;
+	ssize_t size;
+	int fd;
+
+	DBG("");
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	header = (void *) session->buf;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		goto failed;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	size = read(fd, session->buf, session->imtu);
+	if (size < 0) {
+		error("IO Channel read error");
+		goto failed;
+	}
+
+	if ((size_t) size < sizeof(struct avdtp_common_header)) {
+		error("Received too small packet (%zu bytes)", size);
+		goto failed;
+	}
+
+	switch (avdtp_parse_data(session, session->buf, size)) {
+	case PARSE_ERROR:
+		goto failed;
+	case PARSE_FRAGMENT:
+		return TRUE;
+	case PARSE_SUCCESS:
+		break;
+	}
+
+	if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+		if (!avdtp_parse_cmd(session, session->in.transaction,
+					session->in.signal_id,
+					session->in.buf,
+					session->in.data_size)) {
+			error("Unable to handle command. Disconnecting");
+			goto failed;
+		}
+
+		if (session->req && session->req->collided) {
+			DBG("Collision detected");
+			goto next;
+		}
+
+		return TRUE;
+	}
+
+	if (session->req == NULL) {
+		error("No pending request, ignoring message");
+		return TRUE;
+	}
+
+	if (header->transaction != session->req->transaction) {
+		error("Transaction label doesn't match");
+		return TRUE;
+	}
+
+	if (session->in.signal_id != session->req->signal_id) {
+		error("Response signal doesn't match");
+		return TRUE;
+	}
+
+	g_source_remove(session->req->timeout);
+	session->req->timeout = 0;
+
+	switch (header->message_type) {
+	case AVDTP_MSG_TYPE_ACCEPT:
+		if (!avdtp_parse_resp(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse accept response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_REJECT:
+		if (!avdtp_parse_rej(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse reject response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_GEN_REJECT:
+		error("Received a General Reject message");
+		break;
+	default:
+		error("Unknown message type 0x%02X", header->message_type);
+		break;
+	}
+
+next:
+	pending_req_free(session->req);
+	session->req = NULL;
+
+	process_queue(session);
+
+	return TRUE;
+
+failed:
+	connection_lost(session, EIO);
+
+	return FALSE;
+}
+
+static uint16_t get_version(struct avdtp *session)
+{
+	const sdp_record_t *rec;
+	sdp_list_t *protos;
+	sdp_data_t *proto_desc;
+	uint16_t ver = 0x0100;
+
+	rec = btd_device_get_record(session->device, A2DP_SINK_UUID);
+	if (!rec)
+		rec = btd_device_get_record(session->device, A2DP_SOURCE_UUID);
+
+	if (!rec)
+		return ver;
+
+	if (sdp_get_access_protos(rec, &protos) < 0)
+		return ver;
+
+	proto_desc = sdp_get_proto_desc(protos, AVDTP_UUID);
+	if (proto_desc && proto_desc->dtd == SDP_UINT16)
+		ver = proto_desc->val.uint16;
+
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+
+	return ver;
+}
+
+static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct avdtp *session = user_data;
+	char address[18];
+	int err_no = EIO;
+
+	if (err) {
+		err_no = err->code;
+		error("%s", err->message);
+		goto failed;
+	}
+
+	if (!session->io)
+		session->io = g_io_channel_ref(chan);
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_OMTU, &session->omtu,
+			BT_IO_OPT_IMTU, &session->imtu,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		err_no = err->code;
+		error("%s", err->message);
+		goto failed;
+	}
+
+	ba2str(device_get_address(session->device), address);
+	DBG("AVDTP: connected %s channel to %s",
+			session->pending_open ? "transport" : "signaling",
+			address);
+
+	if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+		DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);
+
+		session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+		avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);
+
+		if (session->io_id)
+			g_source_remove(session->io_id);
+
+		/* This watch should be low priority since otherwise the
+		 * connect callback might be dispatched before the session
+		 * callback if the kernel wakes us up at the same time for
+		 * them. This could happen if a headset is very quick in
+		 * sending the Start command after connecting the stream
+		 * transport channel.
+		 */
+		session->io_id = g_io_add_watch_full(chan,
+						G_PRIORITY_LOW,
+						G_IO_IN | G_IO_ERR | G_IO_HUP
+						| G_IO_NVAL,
+						(GIOFunc) session_cb, session,
+						NULL);
+
+		if (session->stream_setup)
+			set_disconnect_timer(session);
+	} else if (session->pending_open)
+		handle_transport_connect(session, chan, session->imtu,
+								session->omtu);
+	else
+		goto failed;
+
+	process_queue(session);
+
+	return;
+
+failed:
+	if (session->pending_open) {
+		struct avdtp_stream *stream = session->pending_open;
+
+		handle_transport_connect(session, NULL, 0, 0);
+
+		if (avdtp_abort(session, stream) < 0)
+			avdtp_sep_set_state(session, stream->lsep,
+						AVDTP_STATE_IDLE);
+	} else
+		connection_lost(session, err_no);
+}
+
+struct avdtp *avdtp_new(GIOChannel *chan, struct btd_device *device,
+							struct queue *lseps)
+{
+	struct avdtp *session;
+
+	session = g_new0(struct avdtp, 1);
+
+	session->device = btd_device_ref(device);
+	/* We don't use avdtp_set_state() here since this isn't a state change
+	 * but just setting of the initial state */
+	session->state = AVDTP_SESSION_STATE_DISCONNECTED;
+	session->lseps = lseps;
+
+	session->version = get_version(session);
+
+	if (!chan)
+		return session;
+
+	avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+
+	btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
+
+	session->io = g_io_channel_ref(chan);
+	session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) session_cb, session);
+
+	/* This is so that avdtp_connect_cb will know to do the right thing
+	 * with respect to the disconnect timer */
+	session->stream_setup = TRUE;
+
+	avdtp_connect_cb(chan, NULL, session);
+
+	return session;
+}
+
+static GIOChannel *l2cap_connect(struct avdtp *session)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+	const bdaddr_t *src;
+
+	src = btd_adapter_get_address(device_get_adapter(session->device));
+
+	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);
+	if (!io) {
+		error("%s", err->message);
+		g_error_free(err);
+		return NULL;
+	}
+
+	return io;
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+			gboolean priority)
+{
+	if (priority)
+		session->prio_queue = g_slist_append(session->prio_queue, req);
+	else
+		session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+	if (req->signal_id == AVDTP_DISCOVER)
+		return 0;
+
+	return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+	struct pending_req *req;
+	struct seid_req sreq;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_stream *stream;
+	uint8_t seid;
+	struct avdtp_error averr;
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+	seid = req_get_seid(req);
+	if (seid)
+		stream = find_stream_by_rseid(session, seid);
+	else
+		stream = NULL;
+
+	if (stream) {
+		stream->abort_int = TRUE;
+		lsep = stream->lsep;
+	} else
+		lsep = NULL;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("Reconfigure: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("Open: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &averr,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("Start: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->start) {
+			lsep->cfm->start(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->starting = FALSE;
+		}
+		break;
+	case AVDTP_SUSPEND:
+		error("Suspend: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("Close: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->close) {
+			lsep->cfm->close(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->close_int = FALSE;
+		}
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("SetConfiguration: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+							&averr, lsep->user_data);
+		goto failed;
+	case AVDTP_DISCOVER:
+		error("Discover: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_GET_CAPABILITIES:
+		error("GetCapabilities: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_ABORT:
+		error("Abort: %s (%d)", strerror(err), err);
+		goto failed;
+	}
+
+	if (!stream)
+		goto failed;
+
+	memset(&sreq, 0, sizeof(sreq));
+	sreq.acp_seid = seid;
+
+	err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+				sizeof(sreq));
+	if (err < 0) {
+		error("Unable to send abort request");
+		goto failed;
+	}
+
+	goto done;
+
+failed:
+	connection_lost(session, err);
+done:
+	pending_req_free(req);
+	return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+	struct avdtp *session = user_data;
+
+	cancel_request(session, ETIMEDOUT);
+
+	return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+			struct pending_req *req)
+{
+	static int transaction = 0;
+	int err;
+
+	if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
+		session->io = l2cap_connect(session);
+		if (!session->io) {
+			err = -EIO;
+			goto failed;
+		}
+		avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+	}
+
+	if (session->state < AVDTP_SESSION_STATE_CONNECTED ||
+			session->req != NULL) {
+		queue_request(session, req, priority);
+		return 0;
+	}
+
+	req->transaction = transaction++;
+	transaction %= 16;
+
+	/* FIXME: Should we retry to send if the buffer
+	was not totally sent or in case of EINTR? */
+	if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+				req->signal_id, req->data, req->data_size)) {
+		err = -EIO;
+		goto failed;
+	}
+
+	session->req = req;
+
+	req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+					ABORT_TIMEOUT : REQ_TIMEOUT,
+					request_timeout,
+					session);
+	return 0;
+
+failed:
+	g_free(req->data);
+	g_free(req);
+	return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size)
+{
+	struct pending_req *req;
+
+	if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+		DBG("Unable to send requests while aborting");
+		return -EINVAL;
+	}
+
+	req = g_new0(struct pending_req, 1);
+	req->signal_id = signal_id;
+	req->data = g_malloc(size);
+	memcpy(req->data, buffer, size);
+	req->data_size = size;
+	req->stream = stream;
+
+	return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+					struct discover_resp *resp, int size)
+{
+	int sep_count, i;
+	uint8_t getcap_cmd;
+	int ret = 0;
+	gboolean getcap_pending = FALSE;
+
+	if (session->version >= 0x0103)
+		getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+	else
+		getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+	sep_count = size / sizeof(struct seid_info);
+
+	for (i = 0; i < sep_count; i++) {
+		struct avdtp_remote_sep *sep;
+		struct avdtp_stream *stream;
+		struct seid_req req;
+
+		DBG("seid %d type %d media %d in use %d",
+				resp->seps[i].seid, resp->seps[i].type,
+				resp->seps[i].media_type, resp->seps[i].inuse);
+
+		stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+		sep = find_remote_sep(session->seps, resp->seps[i].seid);
+		if (!sep) {
+			if (resp->seps[i].inuse && !stream)
+				continue;
+			sep = g_new0(struct avdtp_remote_sep, 1);
+			session->seps = g_slist_append(session->seps, sep);
+		}
+
+		sep->stream = stream;
+		sep->seid = resp->seps[i].seid;
+		sep->type = resp->seps[i].type;
+		sep->media_type = resp->seps[i].media_type;
+
+		memset(&req, 0, sizeof(req));
+		req.acp_seid = sep->seid;
+
+		ret = send_request(session, TRUE, NULL, getcap_cmd,
+							&req, sizeof(req));
+		if (ret < 0)
+			break;
+		getcap_pending = TRUE;
+	}
+
+	if (!getcap_pending)
+		finalize_discovery(session, -ret);
+
+	return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+						struct getcap_resp *resp,
+						unsigned int size)
+{
+	struct avdtp_remote_sep *sep;
+	uint8_t seid;
+
+	/* Check for minimum required packet size includes:
+	 *   1. getcap resp header
+	 *   2. media transport capability (2 bytes)
+	 *   3. media codec capability type + length (2 bytes)
+	 *   4. the actual media codec elements
+	 * */
+	if (size < (sizeof(struct getcap_resp) + 4 +
+				sizeof(struct avdtp_media_codec_capability))) {
+		error("Too short getcap resp packet");
+		return FALSE;
+	}
+
+	seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+	sep = find_remote_sep(session->seps, seid);
+
+	DBG("seid %d type %d media %d", sep->seid,
+					sep->type, sep->media_type);
+
+	if (sep->caps) {
+		g_slist_free_full(sep->caps, g_free);
+		sep->caps = NULL;
+		sep->codec = NULL;
+		sep->delay_reporting = FALSE;
+	}
+
+	sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+					&sep->codec, &sep->delay_reporting);
+
+	return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+						struct avdtp_stream *stream,
+						struct avdtp_single_header *resp,
+						int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->set_configuration)
+		sep->cfm->set_configuration(session, sep, stream, NULL,
+						sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+	return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct avdtp_single_header *resp, int size)
+{
+	return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream,
+				struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	stream->io = l2cap_connect(session);
+	if (!stream->io) {
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+		return FALSE;
+	}
+
+	session->pending_open = stream;
+
+	return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->start)
+		sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+	/* We might be in STREAMING already if both sides send START_CMD at the
+	 * same time and the one in SNK role doesn't reject it as it should */
+	if (sep->state != AVDTP_STATE_STREAMING)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+	return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	close_stream(stream);
+
+	return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	if (sep->cfm && sep->cfm->suspend)
+		sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	if (sep->cfm && sep->cfm->abort)
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+	return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->delay_report)
+		sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct pending_req *next;
+	const char *get_all = "";
+
+	if (session->prio_queue)
+		next = session->prio_queue->data;
+	else if (session->req_queue)
+		next = session->req_queue->data;
+	else
+		next = NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("DISCOVER request succeeded");
+		return avdtp_discover_resp(session, buf, size);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		get_all = "ALL_";
+	case AVDTP_GET_CAPABILITIES:
+		DBG("GET_%sCAPABILITIES request succeeded", get_all);
+		if (!avdtp_get_capabilities_resp(session, buf, size))
+			return FALSE;
+		if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+				next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+			finalize_discovery(session, 0);
+		return TRUE;
+	}
+
+	/* The remaining commands require an existing stream so bail out
+	 * here if the stream got unexpectedly disconnected */
+	if (!stream) {
+		DBG("AVDTP: stream was closed while waiting for reply");
+		return TRUE;
+	}
+
+	switch (signal_id) {
+	case AVDTP_SET_CONFIGURATION:
+		DBG("SET_CONFIGURATION request succeeded");
+		return avdtp_set_configuration_resp(session, stream,
+								buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("RECONFIGURE request succeeded");
+		return avdtp_reconfigure_resp(session, stream, buf, size);
+	case AVDTP_OPEN:
+		DBG("OPEN request succeeded");
+		return avdtp_open_resp(session, stream, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("SUSPEND request succeeded");
+		return avdtp_suspend_resp(session, stream, buf, size);
+	case AVDTP_START:
+		DBG("START request succeeded");
+		return avdtp_start_resp(session, stream, buf, size);
+	case AVDTP_CLOSE:
+		DBG("CLOSE request succeeded");
+		return avdtp_close_resp(session, stream, buf, size);
+	case AVDTP_ABORT:
+		DBG("ABORT request succeeded");
+		return avdtp_abort_resp(session, stream, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("DELAY_REPORT request succeeded");
+		return avdtp_delay_report_resp(session, stream, buf, size);
+	}
+
+	error("Unknown signal id in accept response: %u", signal_id);
+	return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+					struct avdtp_error *err)
+{
+	if (size < sizeof(struct seid_rej)) {
+		error("Too small packet for seid_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+				struct avdtp_error *err)
+{
+	if (size < sizeof(struct conf_rej)) {
+		error("Too small packet for conf_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, rej->category, rej->error);
+
+	return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+					struct avdtp_error *err,
+					uint8_t *acp_seid)
+{
+	if (size < sizeof(struct stream_rej)) {
+		error("Too small packet for stream_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	if (acp_seid)
+		*acp_seid = rej->acp_seid;
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct avdtp_error err;
+	uint8_t acp_seid;
+	struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("DISCOVER request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		return TRUE;
+	case AVDTP_GET_CAPABILITIES:
+	case AVDTP_GET_ALL_CAPABILITIES:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("GET_CAPABILITIES request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		return TRUE;
+	case AVDTP_OPEN:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("OPEN request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->open)
+			sep->cfm->open(session, sep, stream, &err,
+					sep->user_data);
+		return TRUE;
+	case AVDTP_SET_CONFIGURATION:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("SET_CONFIGURATION request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->set_configuration)
+			sep->cfm->set_configuration(session, sep, stream,
+							&err, sep->user_data);
+		return TRUE;
+	case AVDTP_RECONFIGURE:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("RECONFIGURE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->reconfigure)
+			sep->cfm->reconfigure(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_START:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("START request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->start) {
+			sep->cfm->start(session, sep, stream, &err,
+					sep->user_data);
+			stream->starting = FALSE;
+		}
+		return TRUE;
+	case AVDTP_SUSPEND:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("SUSPEND request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->suspend)
+			sep->cfm->suspend(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_CLOSE:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("CLOSE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->close) {
+			sep->cfm->close(session, sep, stream, &err,
+					sep->user_data);
+			stream->close_int = FALSE;
+		}
+		return TRUE;
+	case AVDTP_ABORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("ABORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->abort)
+			sep->cfm->abort(session, sep, stream, &err,
+					sep->user_data);
+		return FALSE;
+	case AVDTP_DELAY_REPORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("DELAY_REPORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->delay_report)
+			sep->cfm->delay_report(session, sep, stream, &err,
+							sep->user_data);
+		return TRUE;
+	default:
+		error("Unknown reject response signal id: %u", signal_id);
+		return TRUE;
+	}
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->caps; l; l = l->next) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_CODEC)
+			return cap;
+	}
+
+	return NULL;
+}
+
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+					struct avdtp_service_capability *cap)
+{
+	GSList *l;
+	struct avdtp_service_capability *stream_cap;
+
+	for (l = stream->caps; l; l = g_slist_next(l)) {
+		stream_cap = l->data;
+
+		if (stream_cap->category != cap->category ||
+			stream_cap->length != cap->length)
+			continue;
+
+		if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps)
+{
+	for (; caps; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+
+		if (!avdtp_stream_has_capability(stream, cap))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->session->seps; l; l = l->next) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == stream->rseid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu)
+{
+	GIOChannel *io;
+
+	if (stream != stream->session->pending_open)
+		return FALSE;
+
+	io = g_io_channel_unix_new(fd);
+
+	handle_transport_connect(stream->session, io, imtu, omtu);
+
+	g_io_channel_unref(io);
+
+	return TRUE;
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps)
+{
+	if (stream->io == NULL)
+		return FALSE;
+
+	if (sock)
+		*sock = g_io_channel_unix_get_fd(stream->io);
+
+	if (omtu)
+		*omtu = stream->omtu;
+
+	if (imtu)
+		*imtu = stream->imtu;
+
+	if (caps)
+		*caps = stream->caps;
+
+	return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+	GSList **queue, *l;
+	struct pending_req *req;
+
+	if (session->req)
+		return 0;
+
+	if (session->prio_queue)
+		queue = &session->prio_queue;
+	else
+		queue = &session->req_queue;
+
+	if (!*queue)
+		return 0;
+
+	l = *queue;
+	req = l->data;
+
+	*queue = g_slist_remove(*queue, req);
+
+	return send_req(session, FALSE, req);
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+	return sep->codec;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							void *data, int length)
+{
+	struct avdtp_service_capability *cap;
+
+	if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+		return NULL;
+
+	cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+	cap->category = category;
+	cap->length = length;
+	memcpy(cap->data, data, length);
+
+	return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+	struct avdtp *session = data;
+
+	session->discover->id = 0;
+
+	finalize_discovery(session, 0);
+
+	return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data)
+{
+	int err;
+
+	if (session->discover)
+		return -EBUSY;
+
+	session->discover = g_new0(struct discover_callback, 1);
+
+	if (session->seps) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+		session->discover->id = g_idle_add(process_discover, session);
+		return 0;
+	}
+
+	err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+	if (err == 0) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+	}
+
+	return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id)
+{
+	GSList *l;
+	struct stream_callback *cb;
+
+	if (!stream)
+		return FALSE;
+
+	for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+		struct stream_callback *tmp = l->data;
+		if (tmp && tmp->id == id) {
+			cb = tmp;
+			break;
+		}
+	}
+
+	if (!cb)
+		return FALSE;
+
+	stream->callbacks = g_slist_remove(stream->callbacks, cb);
+	g_free(cb);
+
+	return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data)
+{
+	struct stream_callback *stream_cb;
+	static unsigned int id = 0;
+
+	stream_cb = g_new(struct stream_callback, 1);
+	stream_cb->cb = cb;
+	stream_cb->user_data = data;
+	stream_cb->id = ++id;
+
+	stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+	return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (session->state < AVDTP_SESSION_STATE_CONNECTED)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+							&req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+	struct avdtp_service_capability *src_cap = data;
+	struct avdtp_service_capability *dst_cap;
+	GSList **l = user_data;
+
+	dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+					src_cap->length);
+
+	*l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream)
+{
+	struct setconf_req *req;
+	struct avdtp_stream *new_stream;
+	unsigned char *ptr;
+	int err, caps_len;
+	struct avdtp_service_capability *cap;
+	GSList *l;
+
+	if (session->state != AVDTP_SESSION_STATE_CONNECTED)
+		return -ENOTCONN;
+
+	if (!(lsep && rsep))
+		return -EINVAL;
+
+	DBG("%p: int_seid=%u, acp_seid=%u", session,
+			lsep->info.seid, rsep->seid);
+
+	new_stream = g_new0(struct avdtp_stream, 1);
+	new_stream->session = session;
+	new_stream->lsep = lsep;
+	new_stream->rseid = rsep->seid;
+
+	if (rsep->delay_reporting && lsep->delay_reporting) {
+		struct avdtp_service_capability *delay_reporting;
+
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		caps = g_slist_append(caps, delay_reporting);
+		new_stream->delay_reporting = TRUE;
+	}
+
+	g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+	/* Calculate total size of request */
+	for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		caps_len += cap->length + 2;
+	}
+
+	req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+	req->int_seid = lsep->info.seid;
+	req->acp_seid = rsep->seid;
+
+	/* Copy the capabilities into the request */
+	for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		memcpy(ptr, cap, cap->length + 2);
+		ptr += cap->length + 2;
+	}
+
+	err = send_request(session, FALSE, new_stream,
+				AVDTP_SET_CONFIGURATION, req,
+				sizeof(struct setconf_req) + caps_len);
+	if (err < 0)
+		stream_free(new_stream);
+	else {
+		lsep->info.inuse = 1;
+		lsep->stream = new_stream;
+		rsep->stream = new_stream;
+		session->streams = g_slist_append(session->streams, new_stream);
+		if (stream)
+			*stream = new_stream;
+	}
+
+	g_free(req);
+
+	return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_OPEN,
+							&req, sizeof(req));
+}
+
+static gboolean start_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+	struct avdtp *session = stream->session;
+
+	stream->open_acp = FALSE;
+
+	if (avdtp_start(session, stream) < 0)
+		error("wait_timeout: avdtp_start failed");
+
+	stream->start_timer = 0;
+
+	return FALSE;
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct start_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_OPEN)
+		return -EINVAL;
+
+	/* Recommendation 12:
+	 *  If the RD has configured and opened a stream it is also responsible
+	 *  to start the streaming via GAVDP_START.
+	 */
+	if (stream->open_acp) {
+		/* If timer already active wait it */
+		if (stream->start_timer)
+			return 0;
+
+		stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
+								start_timeout,
+								stream);
+		return 0;
+	}
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_start: rejecting start since close is initiated");
+		return -EINVAL;
+	}
+
+	if (stream->starting == TRUE) {
+		DBG("stream already started");
+		return -EINPROGRESS;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.first_seid.seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_START,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->starting = TRUE;
+
+	return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state < AVDTP_STATE_OPEN)
+		return -EINVAL;
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_close: rejecting since close is already initiated");
+		return -EINVAL;
+	}
+
+	if (immediate && session->req && stream == session->req->stream)
+		return avdtp_abort(session, stream);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->close_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+							&req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!stream && session->discover) {
+		/* Don't call cb since it being aborted */
+		session->discover->cb = NULL;
+		finalize_discovery(session, -ECANCELED);
+		return -EALREADY;
+	}
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state == AVDTP_STATE_ABORTING)
+		return -EINVAL;
+
+	if (session->req && stream == session->req->stream)
+		return cancel_request(session, ECANCELED);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->abort_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay)
+{
+	struct delay_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+				stream->lsep->state != AVDTP_STATE_STREAMING)
+		return -EINVAL;
+
+	if (!stream->delay_reporting || session->version < 0x0103)
+		return -EINVAL;
+
+	stream->delay = delay;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+	req.delay = htons(delay);
+
+	return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+							&req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+						uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t seid = util_get_uid(&seids, MAX_SEID);
+
+	if (!seid)
+		return NULL;
+
+	sep = g_new0(struct avdtp_local_sep, 1);
+
+	sep->state = AVDTP_STATE_IDLE;
+	sep->info.seid = seid;
+	sep->info.type = type;
+	sep->info.media_type = media_type;
+	sep->codec = codec_type;
+	sep->ind = ind;
+	sep->cfm = cfm;
+	sep->user_data = user_data;
+	sep->delay_reporting = delay_reporting;
+
+	DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	if (!queue_push_tail(lseps, sep)) {
+		g_free(sep);
+		return NULL;
+	}
+
+	return sep;
+}
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
+{
+	if (!sep)
+		return -EINVAL;
+
+	if (sep->stream)
+		release_stream(sep->stream, sep->stream->session);
+
+	DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	util_clear_uid(&seids, sep->info.seid);
+	queue_remove(lseps, sep);
+	g_free(sep);
+
+	return 0;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+	if (err->category == AVDTP_ERRNO)
+		return strerror(err->err.posix_errno);
+
+	switch(err->err.error_code) {
+	case AVDTP_BAD_HEADER_FORMAT:
+		return "Bad Header Format";
+	case AVDTP_BAD_LENGTH:
+		return "Bad Packet Length";
+	case AVDTP_BAD_ACP_SEID:
+		return "Bad Acceptor SEID";
+	case AVDTP_SEP_IN_USE:
+		return "Stream End Point in Use";
+	case AVDTP_SEP_NOT_IN_USE:
+		return "Stream End Point Not in Use";
+	case AVDTP_BAD_SERV_CATEGORY:
+		return "Bad Service Category";
+	case AVDTP_BAD_PAYLOAD_FORMAT:
+		return "Bad Payload format";
+	case AVDTP_NOT_SUPPORTED_COMMAND:
+		return "Command Not Supported";
+	case AVDTP_INVALID_CAPABILITIES:
+		return "Invalid Capabilities";
+	case AVDTP_BAD_RECOVERY_TYPE:
+		return "Bad Recovery Type";
+	case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+		return "Bad Media Transport Format";
+	case AVDTP_BAD_RECOVERY_FORMAT:
+		return "Bad Recovery Format";
+	case AVDTP_BAD_ROHC_FORMAT:
+		return "Bad Header Compression Format";
+	case AVDTP_BAD_CP_FORMAT:
+		return "Bad Content Protection Format";
+	case AVDTP_BAD_MULTIPLEXING_FORMAT:
+		return "Bad Multiplexing Format";
+	case AVDTP_UNSUPPORTED_CONFIGURATION:
+		return "Configuration not supported";
+	case AVDTP_BAD_STATE:
+		return "Bad State";
+	default:
+		return "Unknown error";
+	}
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+	return sep->state;
+}
+
+struct btd_adapter *avdtp_get_adapter(struct avdtp *session)
+{
+	return device_get_adapter(session->device);
+}
+
+struct btd_device *avdtp_get_device(struct avdtp *session)
+{
+	return session->device;
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+	return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
+
+unsigned int avdtp_add_state_cb(struct btd_device *dev,
+				avdtp_session_state_cb cb, void *user_data)
+{
+	struct avdtp_state_callback *state_cb;
+	static unsigned int id = 0;
+
+	state_cb = g_new(struct avdtp_state_callback, 1);
+	state_cb->cb = cb;
+	state_cb->dev = dev;
+	state_cb->id = ++id;
+	state_cb->user_data = user_data;
+
+	state_callbacks = g_slist_append(state_callbacks, state_cb);
+
+	return state_cb->id;
+}
+
+gboolean avdtp_remove_state_cb(unsigned int id)
+{
+	GSList *l;
+
+	for (l = state_callbacks; l != NULL; l = l->next) {
+		struct avdtp_state_callback *cb = l->data;
+		if (cb && cb->id == id) {
+			state_callbacks = g_slist_remove(state_callbacks, cb);
+			g_free(cb);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
diff --git a/bluez/repo/profiles/audio/avdtp.h b/bluez/repo/profiles/audio/avdtp.h
new file mode 100644
index 0000000..621a6e3
--- /dev/null
+++ b/bluez/repo/profiles/audio/avdtp.h
@@ -0,0 +1,302 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+typedef enum {
+	AVDTP_SESSION_STATE_DISCONNECTED,
+	AVDTP_SESSION_STATE_CONNECTING,
+	AVDTP_SESSION_STATE_CONNECTED
+} avdtp_session_state_t;
+
+struct avdtp;
+struct avdtp_server;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+	uint8_t category;
+	union {
+		uint8_t error_code;
+		int posix_errno;
+	} err;
+};
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT			0x01
+#define AVDTP_REPORTING				0x02
+#define AVDTP_RECOVERY				0x03
+#define AVDTP_CONTENT_PROTECTION		0x04
+#define AVDTP_HEADER_COMPRESSION		0x05
+#define AVDTP_MULTIPLEXING			0x06
+#define AVDTP_MEDIA_CODEC			0x07
+#define AVDTP_DELAY_REPORTING			0x08
+#define AVDTP_ERRNO				0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT			0x01
+#define AVDTP_BAD_LENGTH			0x11
+#define AVDTP_BAD_ACP_SEID			0x12
+#define AVDTP_SEP_IN_USE			0x13
+#define AVDTP_SEP_NOT_IN_USE			0x14
+#define AVDTP_BAD_SERV_CATEGORY			0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT		0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND		0x19
+#define AVDTP_INVALID_CAPABILITIES		0x1A
+#define AVDTP_BAD_RECOVERY_TYPE			0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT	0x23
+#define AVDTP_BAD_RECOVERY_FORMAT		0x25
+#define AVDTP_BAD_ROHC_FORMAT			0x26
+#define AVDTP_BAD_CP_FORMAT			0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT		0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION		0x29
+#define AVDTP_BAD_STATE				0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE			0x00
+#define AVDTP_SEP_TYPE_SINK			0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO			0x00
+#define AVDTP_MEDIA_TYPE_VIDEO			0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA		0x02
+
+typedef enum {
+	AVDTP_STATE_IDLE,
+	AVDTP_STATE_CONFIGURED,
+	AVDTP_STATE_OPEN,
+	AVDTP_STATE_STREAMING,
+	AVDTP_STATE_CLOSING,
+	AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+	uint8_t category;
+	uint8_t length;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t rfa0:4;
+	uint8_t media_type:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t media_type:4;
+	uint8_t rfa0:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_session_state_cb) (struct btd_device *dev,
+					struct avdtp *session,
+					avdtp_session_state_t old_state,
+					avdtp_session_state_t new_state,
+					void *user_data);
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+						struct avdtp_stream *stream,
+						struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+	void (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*reconfigure) (struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+};
+
+/* Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted */
+struct avdtp_sep_ind {
+	gboolean (*match_codec) (struct avdtp *session,
+				struct avdtp_media_codec_capability *codec,
+				void *user_data);
+	gboolean (*get_capability) (struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					gboolean get_all,
+					GSList **caps, uint8_t *err,
+					void *user_data);
+	gboolean (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					GSList *caps,
+					avdtp_set_configuration_cb cb,
+					void *user_data);
+	gboolean (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*suspend) (struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*reconfigure) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*delayreport) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t rseid, uint16_t delay,
+					uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+					struct avdtp_error *err, void *user_data);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							void *data, int size);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id);
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu);
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream);
+
+unsigned int avdtp_add_state_cb(struct btd_device *dev,
+				avdtp_session_state_cb cb, void *user_data);
+
+gboolean avdtp_remove_state_cb(unsigned int id);
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+				struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+						uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
+
+struct btd_adapter *avdtp_get_adapter(struct avdtp *session);
+struct btd_device *avdtp_get_device(struct avdtp *session);
+struct avdtp_server *avdtp_get_server(struct avdtp_local_sep *lsep);
+
+struct avdtp *avdtp_new(GIOChannel *chan, struct btd_device *device,
+							struct queue *lseps);
diff --git a/bluez/repo/profiles/audio/avrcp.c b/bluez/repo/profiles/audio/avrcp.c
new file mode 100644
index 0000000..7b60012
--- /dev/null
+++ b/bluez/repo/profiles/audio/avrcp.c
@@ -0,0 +1,4435 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  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 <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+
+#include "avctp.h"
+#include "avrcp.h"
+#include "control.h"
+#include "player.h"
+#include "transport.h"
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG		0x001958
+
+/* Status codes */
+#define AVRCP_STATUS_INVALID_COMMAND		0x00
+#define AVRCP_STATUS_INVALID_PARAM		0x01
+#define AVRCP_STATUS_PARAM_NOT_FOUND		0x02
+#define AVRCP_STATUS_INTERNAL_ERROR		0x03
+#define AVRCP_STATUS_SUCCESS			0x04
+#define AVRCP_STATUS_OUT_OF_BOUNDS		0x0b
+#define AVRCP_STATUS_INVALID_PLAYER_ID		0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE	0x12
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS	0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED	0x16
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE	0x00
+#define AVRCP_PACKET_TYPE_START		0x01
+#define AVRCP_PACKET_TYPE_CONTINUING	0x02
+#define AVRCP_PACKET_TYPE_END		0x03
+
+/* PDU types for metadata transfer */
+#define AVRCP_GET_CAPABILITIES		0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0X11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
+#define AVRCP_SET_PLAYER_VALUE		0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
+#define AVRCP_DISPLAYABLE_CHARSET	0x17
+#define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+#define AVRCP_REQUEST_CONTINUING	0x40
+#define AVRCP_ABORT_CONTINUING		0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_ADDRESSED_PLAYER	0x60
+#define AVRCP_SET_BROWSED_PLAYER	0x70
+#define AVRCP_GET_FOLDER_ITEMS		0x71
+#define AVRCP_CHANGE_PATH		0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES	0x73
+#define AVRCP_PLAY_ITEM			0x74
+#define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS	0x75
+#define AVRCP_SEARCH			0x80
+#define AVRCP_ADD_TO_NOW_PLAYING	0x90
+#define AVRCP_GENERAL_REJECT		0xA0
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID		0x02
+#define CAP_EVENTS_SUPPORTED	0x03
+
+#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5
+#define AVRCP_GET_CAPABILITIES_PARAM_LENGTH 1
+
+#define AVRCP_FEATURE_CATEGORY_1	0x0001
+#define AVRCP_FEATURE_CATEGORY_2	0x0002
+#define AVRCP_FEATURE_CATEGORY_3	0x0004
+#define AVRCP_FEATURE_CATEGORY_4	0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS	0x0010
+#define AVRCP_FEATURE_BROWSING			0x0040
+
+#define AVRCP_BATTERY_STATUS_NORMAL		0
+#define AVRCP_BATTERY_STATUS_WARNING		1
+#define AVRCP_BATTERY_STATUS_CRITICAL		2
+#define AVRCP_BATTERY_STATUS_EXTERNAL		3
+#define AVRCP_BATTERY_STATUS_FULL_CHARGE	4
+
+#define AVRCP_CHARSET_UTF8		106
+
+#define AVRCP_BROWSING_TIMEOUT		1
+#define AVRCP_CT_VERSION		0x0106
+#define AVRCP_TG_VERSION		0x0105
+
+#define AVRCP_SCOPE_MEDIA_PLAYER_LIST			0x00
+#define AVRCP_SCOPE_MEDIA_PLAYER_VFS			0x01
+#define AVRCP_SCOPE_SEARCH				0x02
+#define AVRCP_SCOPE_NOW_PLAYING			0x03
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_header {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t packet_type:2;
+	uint8_t rsvd:6;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_header {
+	uint8_t company_id[3];
+	uint8_t pdu_id;
+	uint8_t rsvd:6;
+	uint8_t packet_type:2;
+	uint16_t params_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+#define AVRCP_MTU	(AVC_MTU - AVC_HEADER_LENGTH)
+#define AVRCP_PDU_MTU	(AVRCP_MTU - AVRCP_HEADER_LENGTH)
+
+struct avrcp_browsing_header {
+	uint8_t pdu_id;
+	uint16_t param_len;
+	uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
+struct get_folder_items_rsp {
+	uint8_t status;
+	uint16_t uid_counter;
+	uint16_t num_items;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct folder_item {
+	uint8_t type;
+	uint16_t len;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct player_item {
+	uint16_t player_id;
+	uint8_t type;
+	uint32_t subtype;
+	uint8_t status;
+	uint8_t features[16];
+	uint16_t charset;
+	uint16_t namelen;
+	char name[0];
+} __attribute__ ((packed));
+
+struct avrcp_server {
+	struct btd_adapter *adapter;
+	uint32_t tg_record_id;
+	uint32_t ct_record_id;
+	GSList *players;
+	GSList *sessions;
+};
+
+struct pending_pdu {
+	uint8_t pdu_id;
+	GList *attr_ids;
+	uint16_t offset;
+};
+
+struct pending_list_items {
+	GSList *items;
+	uint32_t start;
+	uint32_t end;
+	uint64_t total;
+};
+
+struct avrcp_player {
+	struct avrcp_server *server;
+	GSList *sessions;
+	uint16_t id;
+	uint8_t scope;
+	uint64_t uid;
+	uint16_t uid_counter;
+	bool browsed;
+	bool addressed;
+	uint8_t *features;
+	char *path;
+	guint changed_id;
+
+	struct pending_list_items *p;
+	char *change_path;
+
+	struct avrcp_player_cb *cb;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct avrcp_data {
+	struct avrcp_player *player;
+	uint16_t version;
+	int features;
+	GSList *players;
+};
+
+struct avrcp {
+	struct avrcp_server *server;
+	struct avctp *conn;
+	struct btd_device *dev;
+	struct avrcp_data *target;
+	struct avrcp_data *controller;
+
+	const struct passthrough_handler *passthrough_handlers;
+	const struct control_pdu_handler *control_handlers;
+
+	unsigned int passthrough_id;
+	unsigned int control_id;
+	unsigned int browsing_id;
+	unsigned int browsing_timer;
+	uint16_t supported_events;
+	uint16_t registered_events;
+	uint8_t transaction;
+	uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
+	struct pending_pdu *pending_pdu;
+};
+
+struct passthrough_handler {
+	uint8_t op;
+	bool (*func) (struct avrcp *session);
+};
+
+struct control_pdu_handler {
+	uint8_t pdu_id;
+	uint8_t code;
+	uint8_t (*func) (struct avrcp *session, struct avrcp_header *pdu,
+							uint8_t transaction);
+};
+
+static GSList *servers = NULL;
+static unsigned int avctp_id = 0;
+
+/* Default feature bit mask for media player as per avctp.c:key_map */
+static const uint8_t features[16] = {
+				0xF8, 0xBF, 0xFF, 0xBF, 0x1F,
+				0xFB, 0x3F, 0x60, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00 };
+
+/* Company IDs supported by this device */
+static uint32_t company_ids[] = {
+	IEEEID_BTSIG,
+};
+
+static void avrcp_register_notification(struct avrcp *session, uint8_t event);
+
+static sdp_record_t *avrcp_ct_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *apseq1, *root;
+	uuid_t root_uuid, l2cap, avctp, avrct, avrctr;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *aproto1, *proto[2], *proto1[2];
+	sdp_record_t *record;
+	sdp_data_t *psm[2], *version, *features;
+	uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
+	uint16_t avctp_ver = 0x0103;
+	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+						AVRCP_FEATURE_CATEGORY_2 |
+						AVRCP_FEATURE_CATEGORY_3 |
+						AVRCP_FEATURE_CATEGORY_4 |
+						AVRCP_FEATURE_BROWSING);
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &avrct);
+	sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &avrctr);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm[0] = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm[0]);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(NULL, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	/* Additional Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto1[0] = sdp_list_append(NULL, &l2cap);
+	psm[1] = sdp_data_alloc(SDP_UINT16, &ap);
+	proto1[0] = sdp_list_append(proto1[0], psm[1]);
+	apseq1 = sdp_list_append(NULL, proto1[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto1[1] = sdp_list_append(NULL, &avctp);
+	proto1[1] = sdp_list_append(proto1[1], version);
+	apseq1 = sdp_list_append(apseq1, proto1[1]);
+
+	aproto1 = sdp_list_append(NULL, apseq1);
+	sdp_set_add_access_protos(record, aproto1);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = AVRCP_CT_VERSION;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP CT", NULL, NULL);
+
+	free(psm[0]);
+	free(psm[1]);
+	free(version);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(proto1[0], NULL);
+	sdp_list_free(proto1[1], NULL);
+	sdp_list_free(aproto1, NULL);
+	sdp_list_free(apseq1, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static sdp_record_t *avrcp_tg_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
+	uuid_t root_uuid, l2cap, avctp, avrtg;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto_control, *proto_control[2];
+	sdp_record_t *record;
+	sdp_data_t *psm_control, *version, *features, *psm_browsing;
+	sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
+	uint16_t lp = AVCTP_CONTROL_PSM;
+	uint16_t lp_browsing = AVCTP_BROWSING_PSM;
+	uint16_t avctp_ver = 0x0103;
+	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+					AVRCP_FEATURE_CATEGORY_2 |
+					AVRCP_FEATURE_CATEGORY_3 |
+					AVRCP_FEATURE_CATEGORY_4 |
+					AVRCP_FEATURE_BROWSING |
+					AVRCP_FEATURE_PLAYER_SETTINGS );
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	/* Service Class ID List */
+	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &avrtg);
+	sdp_set_service_classes(record, svclass_id);
+
+	/* Protocol Descriptor List */
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto_control[0] = sdp_list_append(NULL, &l2cap);
+	psm_control = sdp_data_alloc(SDP_UINT16, &lp);
+	proto_control[0] = sdp_list_append(proto_control[0], psm_control);
+	apseq = sdp_list_append(NULL, proto_control[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto_control[1] = sdp_list_append(NULL, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+	proto_control[1] = sdp_list_append(proto_control[1], version);
+	apseq = sdp_list_append(apseq, proto_control[1]);
+
+	aproto_control = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto_control);
+	proto_browsing[0] = sdp_list_append(NULL, &l2cap);
+	psm_browsing = sdp_data_alloc(SDP_UINT16, &lp_browsing);
+	proto_browsing[0] = sdp_list_append(proto_browsing[0], psm_browsing);
+	apseq_browsing = sdp_list_append(NULL, proto_browsing[0]);
+
+	proto_browsing[1] = sdp_list_append(NULL, &avctp);
+	proto_browsing[1] = sdp_list_append(proto_browsing[1], version);
+	apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]);
+
+	aproto_browsing = sdp_list_append(NULL, apseq_browsing);
+	sdp_set_add_access_protos(record, aproto_browsing);
+
+	/* Bluetooth Profile Descriptor List */
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = AVRCP_TG_VERSION;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
+
+	free(psm_browsing);
+	sdp_list_free(proto_browsing[0], NULL);
+	sdp_list_free(proto_browsing[1], NULL);
+	sdp_list_free(apseq_browsing, NULL);
+	sdp_list_free(aproto_browsing, NULL);
+
+	free(psm_control);
+	free(version);
+	sdp_list_free(proto_control[0], NULL);
+	sdp_list_free(proto_control[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto_control, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static unsigned int attr_get_max_val(uint8_t attr)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		return AVRCP_EQUALIZER_ON;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		return AVRCP_REPEAT_MODE_GROUP;
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		return AVRCP_SHUFFLE_GROUP;
+	case AVRCP_ATTRIBUTE_SCAN:
+		return AVRCP_SCAN_GROUP;
+	}
+
+	return 0;
+}
+
+static const char *battery_status_to_str(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_BATTERY_STATUS_NORMAL:
+		return "normal";
+	case AVRCP_BATTERY_STATUS_WARNING:
+		return "warning";
+	case AVRCP_BATTERY_STATUS_CRITICAL:
+		return "critical";
+	case AVRCP_BATTERY_STATUS_EXTERNAL:
+		return "external";
+	case AVRCP_BATTERY_STATUS_FULL_CHARGE:
+		return "fullcharge";
+	}
+
+	return NULL;
+}
+
+/*
+ * get_company_id:
+ *
+ * Get three-byte Company_ID from incoming AVRCP message
+ */
+static uint32_t get_company_id(const uint8_t cid[3])
+{
+	return cid[0] << 16 | cid[1] << 8 | cid[2];
+}
+
+/*
+ * set_company_id:
+ *
+ * Set three-byte Company_ID into outgoing AVRCP message
+ */
+static void set_company_id(uint8_t cid[3], uint32_t cid_in)
+{
+	cid[0] = (cid_in & 0xff0000) >> 16;
+	cid[1] = (cid_in & 0x00ff00) >> 8;
+	cid[2] = (cid_in & 0x0000ff);
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		return "Equalizer";
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		return "Repeat";
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		return "Shuffle";
+	case AVRCP_ATTRIBUTE_SCAN:
+		return "Scan";
+	}
+
+	return NULL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+	int ret;
+
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_EQUALIZER_OFF;
+		else if (!strcmp(value, "on"))
+			ret = AVRCP_EQUALIZER_ON;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_REPEAT_MODE_OFF;
+		else if (!strcmp(value, "singletrack"))
+			ret = AVRCP_REPEAT_MODE_SINGLE;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_REPEAT_MODE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_REPEAT_MODE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_SHUFFLE_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_SHUFFLE_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_SHUFFLE_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	case AVRCP_ATTRIBUTE_SCAN:
+		if (!strcmp(value, "off"))
+			ret = AVRCP_SCAN_OFF;
+		else if (!strcmp(value, "alltracks"))
+			ret = AVRCP_SCAN_ALL;
+		else if (!strcmp(value, "group"))
+			ret = AVRCP_SCAN_GROUP;
+		else
+			ret = -EINVAL;
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static int attr_to_val(const char *str)
+{
+	if (!strcasecmp(str, "Equalizer"))
+		return AVRCP_ATTRIBUTE_EQUALIZER;
+	else if (!strcasecmp(str, "Repeat"))
+		return AVRCP_ATTRIBUTE_REPEAT_MODE;
+	else if (!strcasecmp(str, "Shuffle"))
+		return AVRCP_ATTRIBUTE_SHUFFLE;
+	else if (!strcasecmp(str, "Scan"))
+		return AVRCP_ATTRIBUTE_SCAN;
+
+	return -EINVAL;
+}
+
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
+{
+	const char *key;
+	const char *value;
+
+	if (player == NULL)
+		return -ENOENT;
+
+	key = attr_to_str(id);
+	if (key == NULL)
+		return -EINVAL;
+
+	value = player->cb->get_setting(key, player->user_data);
+	if (value == NULL)
+		return -EINVAL;
+
+	return attrval_to_val(id, value);
+}
+
+static int play_status_to_val(const char *status)
+{
+	if (!strcasecmp(status, "stopped"))
+		return AVRCP_PLAY_STATUS_STOPPED;
+	else if (!strcasecmp(status, "playing"))
+		return AVRCP_PLAY_STATUS_PLAYING;
+	else if (!strcasecmp(status, "paused"))
+		return AVRCP_PLAY_STATUS_PAUSED;
+	else if (!strcasecmp(status, "forward-seek"))
+		return AVRCP_PLAY_STATUS_FWD_SEEK;
+	else if (!strcasecmp(status, "reverse-seek"))
+		return AVRCP_PLAY_STATUS_REV_SEEK;
+	else if (!strcasecmp(status, "error"))
+		return AVRCP_PLAY_STATUS_ERROR;
+
+	return -EINVAL;
+}
+
+void avrcp_player_event(struct avrcp_player *player, uint8_t id,
+							const void *data)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t code;
+	uint16_t size;
+	GSList *l;
+	int attr;
+	int val;
+
+	if (player->sessions == NULL)
+		return;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+
+	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+
+	DBG("id=%u", id);
+
+	if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
+		code = AVC_CTYPE_REJECTED;
+		size = 1;
+		pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
+		goto done;
+	}
+
+	code = AVC_CTYPE_CHANGED;
+	pdu->params[0] = id;
+
+	switch (id) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+		size = 2;
+		pdu->params[1] = play_status_to_val(data);
+
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		size = 9;
+		memcpy(&pdu->params[1], data, sizeof(uint64_t));
+
+		break;
+	case AVRCP_EVENT_TRACK_REACHED_END:
+	case AVRCP_EVENT_TRACK_REACHED_START:
+		size = 1;
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		size = 2;
+		pdu->params[1] = 1;
+
+		attr = attr_to_val(data);
+		if (attr < 0)
+			return;
+
+		val = player_get_setting(player, attr);
+		if (val < 0)
+			return;
+
+		pdu->params[size++] = attr;
+		pdu->params[size++] = val;
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		size = 5;
+		memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
+		memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
+		break;
+	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		size = 1;
+		break;
+	default:
+		error("Unknown event %u", id);
+		return;
+	}
+
+done:
+	pdu->params_len = htons(size);
+
+	for (l = player->sessions; l; l = l->next) {
+		struct avrcp *session = l->data;
+		int err;
+
+		if (!(session->registered_events & (1 << id)))
+			continue;
+
+		err = avctp_send_vendordep(session->conn,
+					session->transaction_events[id],
+					code, AVC_SUBUNIT_PANEL,
+					buf, size + AVRCP_HEADER_LENGTH);
+
+		if (err < 0)
+			continue;
+
+		/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+		session->registered_events ^= 1 << id;
+	}
+
+	return;
+}
+
+static const char *metadata_to_str(uint32_t id)
+{
+	switch (id) {
+	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+		return "Title";
+	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+		return "Artist";
+	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+		return "Album";
+	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+		return "Genre";
+	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+		return "TrackNumber";
+	case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+		return "NumberOfTracks";
+	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+		return "Duration";
+	}
+
+	return NULL;
+}
+
+static const char *player_get_metadata(struct avrcp_player *player,
+								uint32_t id)
+{
+	const char *key;
+
+	key = metadata_to_str(id);
+	if (key == NULL)
+		return NULL;
+
+	if (player != NULL)
+		return player->cb->get_metadata(key, player->user_data);
+
+	if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+		return "";
+
+	return NULL;
+}
+
+static uint16_t player_write_media_attribute(struct avrcp_player *player,
+						uint32_t id, uint8_t *buf,
+						uint16_t *pos,
+						uint16_t *offset)
+{
+	uint16_t len;
+	uint16_t attr_len;
+	const char *value = NULL;
+
+	DBG("%u", id);
+
+	value = player_get_metadata(player, id);
+	if (value == NULL) {
+		*offset = 0;
+		return 0;
+	}
+
+	attr_len = strlen(value);
+	value = ((char *) value) + *offset;
+	len = attr_len - *offset;
+
+	if (len > AVRCP_PDU_MTU - *pos) {
+		len = AVRCP_PDU_MTU - *pos;
+		*offset += len;
+	} else {
+		*offset = 0;
+	}
+
+	memcpy(&buf[*pos], value, len);
+	*pos += len;
+
+	return attr_len;
+}
+
+static GList *player_fill_media_attribute(struct avrcp_player *player,
+					GList *attr_ids, uint8_t *buf,
+					uint16_t *pos, uint16_t *offset)
+{
+	struct media_attribute_header {
+		uint32_t id;
+		uint16_t charset;
+		uint16_t len;
+	} *hdr = NULL;
+	GList *l;
+
+	for (l = attr_ids; l != NULL; l = g_list_delete_link(l, l)) {
+		uint32_t attr = GPOINTER_TO_UINT(l->data);
+		uint16_t attr_len;
+
+		if (*offset == 0) {
+			if (*pos + sizeof(*hdr) >= AVRCP_PDU_MTU)
+				break;
+
+			hdr = (void *) &buf[*pos];
+			hdr->id = htonl(attr);
+			/* Always use UTF-8 */
+			hdr->charset = htons(AVRCP_CHARSET_UTF8);
+			*pos += sizeof(*hdr);
+		}
+
+		attr_len = player_write_media_attribute(player, attr, buf,
+								pos, offset);
+
+		if (hdr != NULL)
+			hdr->len = htons(attr_len);
+
+		if (*offset > 0)
+			break;
+	}
+
+	return l;
+}
+
+static struct pending_pdu *pending_pdu_new(uint8_t pdu_id, GList *attr_ids,
+							unsigned int offset)
+{
+	struct pending_pdu *pending = g_new(struct pending_pdu, 1);
+
+	pending->pdu_id = pdu_id;
+	pending->attr_ids = attr_ids;
+	pending->offset = offset;
+
+	return pending;
+}
+
+static gboolean session_abort_pending_pdu(struct avrcp *session)
+{
+	if (session->pending_pdu == NULL)
+		return FALSE;
+
+	g_list_free(session->pending_pdu->attr_ids);
+	g_free(session->pending_pdu);
+	session->pending_pdu = NULL;
+
+	return TRUE;
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		switch (value) {
+		case AVRCP_EQUALIZER_ON:
+			return "on";
+		case AVRCP_EQUALIZER_OFF:
+			return "off";
+		}
+
+		break;
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		switch (value) {
+		case AVRCP_REPEAT_MODE_OFF:
+			return "off";
+		case AVRCP_REPEAT_MODE_SINGLE:
+			return "singletrack";
+		case AVRCP_REPEAT_MODE_ALL:
+			return "alltracks";
+		case AVRCP_REPEAT_MODE_GROUP:
+			return "group";
+		}
+
+		break;
+	/* Shuffle and scan have the same values */
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+	case AVRCP_ATTRIBUTE_SCAN:
+		switch (value) {
+		case AVRCP_SCAN_OFF:
+			return "off";
+		case AVRCP_SCAN_ALL:
+			return "alltracks";
+		case AVRCP_SCAN_GROUP:
+			return "group";
+		}
+
+		break;
+	}
+
+	return NULL;
+}
+
+static int player_set_setting(struct avrcp_player *player, uint8_t id,
+								uint8_t val)
+{
+	const char *key, *value;
+
+	key = attr_to_str(id);
+	if (key == NULL)
+		return -EINVAL;
+
+	value = attrval_to_str(id, val);
+	if (value == NULL)
+		return -EINVAL;
+
+	if (player == NULL)
+		return -ENOENT;
+
+	return player->cb->set_setting(key, value, player->user_data);
+}
+
+static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len != 1)
+		goto err;
+
+	DBG("id=%u", pdu->params[0]);
+
+	switch (pdu->params[0]) {
+	case CAP_COMPANY_ID:
+		for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
+			set_company_id(&pdu->params[2 + i * 3],
+							company_ids[i]);
+		}
+
+		pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
+		pdu->params[1] = G_N_ELEMENTS(company_ids);
+
+		return AVC_CTYPE_STABLE;
+	case CAP_EVENTS_SUPPORTED:
+		pdu->params[1] = 0;
+		for (i = 1; i <= AVRCP_EVENT_LAST; i++) {
+			if (session->supported_events & (1 << i)) {
+				pdu->params[1]++;
+				pdu->params[pdu->params[1] + 1] = i;
+			}
+		}
+
+		pdu->params_len = htons(2 + pdu->params[1]);
+		return AVC_CTYPE_STABLE;
+	}
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+
+	return AVC_CTYPE_REJECTED;
+}
+
+static struct avrcp_player *target_get_player(struct avrcp *session)
+{
+	if (!session->target)
+		return NULL;
+
+	return session->target->player;
+}
+
+static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len != 0) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		return AVC_CTYPE_REJECTED;
+	}
+
+	if (!player)
+		goto done;
+
+	for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
+		if (player_get_setting(player, i) < 0)
+			continue;
+
+		len++;
+		pdu->params[len] = i;
+	}
+
+done:
+	pdu->params[0] = len;
+	pdu->params_len = htons(len + 1);
+
+	return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_list_player_values(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+
+	if (len != 1)
+		goto err;
+
+	if (player_get_setting(player, pdu->params[0]) < 0)
+		goto err;
+
+	len = attr_get_max_val(pdu->params[0]);
+
+	for (i = 1; i <= len; i++)
+		pdu->params[i] = i;
+
+	pdu->params[0] = len;
+	pdu->params_len = htons(len + 1);
+
+	return AVC_CTYPE_STABLE;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint32_t str_to_metadata(const char *str)
+{
+	if (strcasecmp(str, "Title") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_TITLE;
+	else if (strcasecmp(str, "Artist") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
+	else if (strcasecmp(str, "Album") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
+	else if (strcasecmp(str, "Genre") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_GENRE;
+	else if (strcasecmp(str, "TrackNumber") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_TRACK;
+	else if (strcasecmp(str, "NumberOfTracks") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
+	else if (strcasecmp(str, "Duration") == 0)
+		return AVRCP_MEDIA_ATTRIBUTE_DURATION;
+
+	return 0;
+}
+
+static GList *player_list_metadata(struct avrcp_player *player)
+{
+	GList *l, *attrs = NULL;
+
+	if (player == NULL)
+		return g_list_prepend(NULL,
+				GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE));
+
+	l = player->cb->list_metadata(player->user_data);
+	for (; l; l = l->next) {
+		const char *key = l->data;
+
+		attrs = g_list_append(attrs,
+					GUINT_TO_POINTER(str_to_metadata(key)));
+	}
+
+	return attrs;
+}
+
+static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	uint64_t identifier = get_le64(&pdu->params[0]);
+	uint16_t pos;
+	uint8_t nattr;
+	GList *attr_ids;
+	uint16_t offset;
+
+	if (len < 9 || identifier != 0)
+		goto err;
+
+	nattr = pdu->params[8];
+
+	if (len < nattr * sizeof(uint32_t) + 1)
+		goto err;
+
+	if (!nattr) {
+		/*
+		 * Return all available information, at least
+		 * title must be returned if there's a track selected.
+		 */
+		attr_ids = player_list_metadata(player);
+		len = g_list_length(attr_ids);
+	} else {
+		unsigned int i;
+		for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++) {
+			uint32_t id;
+
+			id = get_be32(&pdu->params[9] + (i * sizeof(id)));
+
+			/* Don't add invalid attributes */
+			if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+					id > AVRCP_MEDIA_ATTRIBUTE_LAST)
+				continue;
+
+			len++;
+			attr_ids = g_list_prepend(attr_ids,
+							GUINT_TO_POINTER(id));
+		}
+
+		attr_ids = g_list_reverse(attr_ids);
+	}
+
+	if (!len)
+		goto err;
+
+	session_abort_pending_pdu(session);
+	pos = 1;
+	offset = 0;
+	attr_ids = player_fill_media_attribute(player, attr_ids, pdu->params,
+								&pos, &offset);
+
+	if (attr_ids != NULL) {
+		session->pending_pdu = pending_pdu_new(pdu->pdu_id, attr_ids,
+								offset);
+		pdu->packet_type = AVRCP_PACKET_TYPE_START;
+	}
+
+	pdu->params[0] = len;
+	pdu->params_len = htons(pos);
+
+	return AVC_CTYPE_STABLE;
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	uint8_t *settings;
+	unsigned int i;
+
+	if (len <= 1 || pdu->params[0] != len - 1)
+		goto err;
+
+	/*
+	 * Save a copy of requested settings because we can override them
+	 * while responding
+	 */
+	settings = g_memdup(&pdu->params[1], pdu->params[0]);
+	len = 0;
+
+	/*
+	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+	 * and send a response with the existent ones. Only if all IDs are
+	 * non-existent we should send an error.
+	 */
+	for (i = 0; i < pdu->params[0]; i++) {
+		int val;
+
+		if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER ||
+					settings[i] > AVRCP_ATTRIBUTE_SCAN) {
+			DBG("Ignoring %u", settings[i]);
+			continue;
+		}
+
+		val = player_get_setting(player, settings[i]);
+		if (val < 0)
+			continue;
+
+		pdu->params[++len] = settings[i];
+		pdu->params[++len] = val;
+	}
+
+	g_free(settings);
+
+	if (len) {
+		pdu->params[0] = len / 2;
+		pdu->params_len = htons(len + 1);
+
+		return AVC_CTYPE_STABLE;
+	}
+
+	error("No valid attributes in request");
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_set_player_value(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	unsigned int i;
+	uint8_t *param;
+
+	if (len < 3 || len > 2 * pdu->params[0] + 1U || player == NULL)
+		goto err;
+
+	/*
+	 * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+	 * and set the existent ones. Sec. 5.2.4 is not clear however how to
+	 * indicate that a certain ID was not accepted. If at least one
+	 * attribute is valid, we respond with no parameters. Otherwise an
+	 * AVRCP_STATUS_INVALID_PARAM is sent.
+	 */
+	for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0];
+							i++, param += 2) {
+		if (player_set_setting(player, param[0], param[1]) < 0)
+			continue;
+
+		len++;
+	}
+
+	if (len) {
+		pdu->params_len = 0;
+
+		return AVC_CTYPE_ACCEPTED;
+	}
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_displayable_charset(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+
+	if (len < 3) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		return AVC_CTYPE_REJECTED;
+	}
+
+	/*
+	 * We acknowledge the commands, but we always use UTF-8 for
+	 * encoding since CT is obliged to support it.
+	 */
+	pdu->params_len = 0;
+	return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_ct_battery_status(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	const char *valstr;
+
+	if (len != 1)
+		goto err;
+
+	valstr = battery_status_to_str(pdu->params[0]);
+	if (valstr == NULL)
+		goto err;
+
+	pdu->params_len = 0;
+
+	return AVC_CTYPE_STABLE;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint32_t player_get_position(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return 0;
+
+	return player->cb->get_position(player->user_data);
+}
+
+static uint32_t player_get_duration(struct avrcp_player *player)
+{
+	uint32_t num;
+
+	if (player == NULL)
+		return UINT32_MAX;
+
+	num = player->cb->get_duration(player->user_data);
+	if (num == 0)
+		return UINT32_MAX;
+
+	return num;
+}
+
+static uint8_t player_get_status(struct avrcp_player *player)
+{
+	const char *value;
+
+	if (player == NULL)
+		return AVRCP_PLAY_STATUS_STOPPED;
+
+	value = player->cb->get_status(player->user_data);
+	if (value == NULL)
+		return AVRCP_PLAY_STATUS_STOPPED;
+
+	return play_status_to_val(value);
+}
+
+static uint16_t player_get_id(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return 0x0000;
+
+	return player->id;
+}
+
+static uint16_t player_get_uid_counter(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return 0x0000;
+
+	return player->uid_counter;
+}
+
+static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	uint32_t position;
+	uint32_t duration;
+
+	if (len != 0) {
+		pdu->params_len = htons(1);
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		return AVC_CTYPE_REJECTED;
+	}
+
+	position = player_get_position(player);
+	duration = player_get_duration(player);
+
+	position = htonl(position);
+	duration = htonl(duration);
+
+	memcpy(&pdu->params[0], &duration, 4);
+	memcpy(&pdu->params[4], &position, 4);
+	pdu->params[8] = player_get_status(player);
+
+	pdu->params_len = htons(9);
+
+	return AVC_CTYPE_STABLE;
+}
+
+static uint64_t player_get_uid(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return UINT64_MAX;
+
+	return player->cb->get_uid(player->user_data);
+}
+
+static GList *player_list_settings(struct avrcp_player *player)
+{
+	if (player == NULL)
+		return NULL;
+
+	return player->cb->list_settings(player->user_data);
+}
+
+static bool avrcp_handle_play(struct avrcp *session)
+{
+	struct avrcp_player *player = target_get_player(session);
+
+	if (player == NULL)
+		return false;
+
+	return player->cb->play(player->user_data);
+}
+
+static bool avrcp_handle_stop(struct avrcp *session)
+{
+	struct avrcp_player *player = target_get_player(session);
+
+	if (player == NULL)
+		return false;
+
+	return player->cb->stop(player->user_data);
+}
+
+static bool avrcp_handle_pause(struct avrcp *session)
+{
+	struct avrcp_player *player = target_get_player(session);
+
+	if (player == NULL)
+		return false;
+
+	return player->cb->pause(player->user_data);
+}
+
+static bool avrcp_handle_next(struct avrcp *session)
+{
+	struct avrcp_player *player = target_get_player(session);
+
+	if (player == NULL)
+		return false;
+
+	return player->cb->next(player->user_data);
+}
+
+static bool avrcp_handle_previous(struct avrcp *session)
+{
+	struct avrcp_player *player = target_get_player(session);
+
+	if (player == NULL)
+		return false;
+
+	return player->cb->previous(player->user_data);
+}
+
+static const struct passthrough_handler passthrough_handlers[] = {
+		{ AVC_PLAY, avrcp_handle_play },
+		{ AVC_STOP, avrcp_handle_stop },
+		{ AVC_PAUSE, avrcp_handle_pause },
+		{ AVC_FORWARD, avrcp_handle_next },
+		{ AVC_BACKWARD, avrcp_handle_previous },
+		{ },
+};
+
+static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed,
+							void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct passthrough_handler *handler;
+
+	for (handler = session->passthrough_handlers; handler->func;
+								handler++) {
+		if (handler->op == op)
+			break;
+	}
+
+	if (handler->func == NULL)
+		return false;
+
+	/* Do not trigger handler on release */
+	if (!pressed)
+		return true;
+
+	return handler->func(session);
+}
+
+static uint8_t avrcp_handle_register_notification(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	struct btd_device *dev = session->dev;
+	uint16_t len = ntohs(pdu->params_len);
+	uint64_t uid;
+	GList *settings;
+
+	/*
+	 * 1 byte for EventID, 4 bytes for Playback interval but the latest
+	 * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
+	 * 1.3 spec, section 5.4.2.
+	 */
+	if (len != 5)
+		goto err;
+
+	/* Check if event is supported otherwise reject */
+	if (!(session->supported_events & (1 << pdu->params[0])))
+		goto err;
+
+	switch (pdu->params[0]) {
+	case AVRCP_EVENT_STATUS_CHANGED:
+		len = 2;
+		pdu->params[1] = player_get_status(player);
+
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		len = 9;
+		uid = player_get_uid(player);
+		memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
+
+		break;
+	case AVRCP_EVENT_TRACK_REACHED_END:
+	case AVRCP_EVENT_TRACK_REACHED_START:
+		len = 1;
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		len = 1;
+		settings = player_list_settings(player);
+
+		pdu->params[len++] = g_list_length(settings);
+		for (; settings; settings = settings->next) {
+			const char *key = settings->data;
+			int attr;
+			int val;
+
+			attr = attr_to_val(key);
+			if (attr < 0)
+				continue;
+
+			val = player_get_setting(player, attr);
+			if (val < 0)
+				continue;
+
+			pdu->params[len++] = attr;
+			pdu->params[len++] = val;
+		}
+
+		g_list_free(settings);
+
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		len = 5;
+		bt_put_be16(player_get_id(player), &pdu->params[1]);
+		bt_put_be16(player_get_uid_counter(player), &pdu->params[3]);
+		break;
+	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		len = 1;
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		pdu->params[1] = media_transport_get_device_volume(dev);
+		if (pdu->params[1] > 127)
+			goto err;
+
+		len = 2;
+
+		break;
+	default:
+		/* All other events are not supported yet */
+		goto err;
+	}
+
+	/* Register event and save the transaction used */
+	session->registered_events |= (1 << pdu->params[0]);
+	session->transaction_events[pdu->params[0]] = transaction;
+
+	pdu->params_len = htons(len);
+
+	return AVC_CTYPE_INTERIM;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_request_continuing(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint16_t len = ntohs(pdu->params_len);
+	struct pending_pdu *pending;
+
+	if (len != 1 || session->pending_pdu == NULL)
+		goto err;
+
+	pending = session->pending_pdu;
+
+	if (pending->pdu_id != pdu->params[0])
+		goto err;
+
+
+	len = 0;
+	pending->attr_ids = player_fill_media_attribute(player,
+							pending->attr_ids,
+							pdu->params, &len,
+							&pending->offset);
+	pdu->pdu_id = pending->pdu_id;
+
+	if (pending->attr_ids == NULL) {
+		g_free(session->pending_pdu);
+		session->pending_pdu = NULL;
+		pdu->packet_type = AVRCP_PACKET_TYPE_END;
+	} else {
+		pdu->packet_type = AVRCP_PACKET_TYPE_CONTINUING;
+	}
+
+	pdu->params_len = htons(len);
+
+	return AVC_CTYPE_STABLE;
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_abort_continuing(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	struct pending_pdu *pending;
+
+	if (len != 1 || session->pending_pdu == NULL)
+		goto err;
+
+	pending = session->pending_pdu;
+
+	if (pending->pdu_id != pdu->params[0])
+		goto err;
+
+	session_abort_pending_pdu(session);
+	pdu->params_len = 0;
+
+	return AVC_CTYPE_ACCEPTED;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	uint16_t len = ntohs(pdu->params_len);
+	uint8_t volume;
+
+	if (len != 1)
+		goto err;
+
+	volume = pdu->params[0] & 0x7F;
+
+	media_transport_update_device_volume(session->dev, volume);
+
+	return AVC_CTYPE_ACCEPTED;
+
+err:
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+	return AVC_CTYPE_REJECTED;
+}
+
+static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
+{
+	struct avrcp_server *server = session->server;
+	GSList *l;
+
+	for (l = server->players; l; l = l->next) {
+		struct avrcp_player *player = l->data;
+
+		if (player->id == id)
+			return player;
+	}
+
+	return NULL;
+}
+
+static gboolean notify_addressed_player_changed(gpointer user_data)
+{
+	struct avrcp_player *player = user_data;
+	uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
+					AVRCP_EVENT_TRACK_CHANGED,
+					AVRCP_EVENT_TRACK_REACHED_START,
+					AVRCP_EVENT_TRACK_REACHED_END,
+					AVRCP_EVENT_SETTINGS_CHANGED,
+					AVRCP_EVENT_PLAYBACK_POS_CHANGED
+				};
+	uint8_t i;
+
+	avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
+
+	/*
+	 * TG shall complete all player specific
+	 * notifications with AV/C C-Type REJECTED
+	 * with error code as Addressed Player Changed.
+	 */
+	for (i = 0; i < sizeof(events); i++)
+		avrcp_player_event(player, events[i], NULL);
+
+	player->changed_id = 0;
+
+	return FALSE;
+}
+
+static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
+						struct avrcp_header *pdu,
+						uint8_t transaction)
+{
+	struct avrcp_player *player;
+	uint16_t len = ntohs(pdu->params_len);
+	uint16_t player_id = 0;
+	uint8_t status;
+
+	if (len < 1) {
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto err;
+	}
+
+	player_id = bt_get_be16(&pdu->params[0]);
+	player = find_tg_player(session, player_id);
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	if (player) {
+		player->addressed = true;
+		status = AVRCP_STATUS_SUCCESS;
+		pdu->params_len = htons(len);
+		pdu->params[0] = status;
+	} else {
+		status = AVRCP_STATUS_INVALID_PLAYER_ID;
+		goto err;
+	}
+
+	/* Don't emit player changed immediately since PTS expect the
+	 * response of SetAddressedPlayer before the event.
+	 */
+	player->changed_id = g_idle_add(notify_addressed_player_changed,
+								player);
+
+	return AVC_CTYPE_ACCEPTED;
+
+err:
+	pdu->params_len = htons(sizeof(status));
+	pdu->params[0] = status;
+	return AVC_CTYPE_REJECTED;
+}
+
+static const struct control_pdu_handler control_handlers[] = {
+		{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
+					avrcp_handle_get_capabilities },
+		{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
+					avrcp_handle_list_player_attributes },
+		{ AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS,
+					avrcp_handle_list_player_values },
+		{ AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS,
+					avrcp_handle_get_element_attributes },
+		{ AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS,
+					avrcp_handle_get_current_player_value },
+		{ AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL,
+					avrcp_handle_set_player_value },
+		{ AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
+					NULL },
+		{ AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS,
+					NULL },
+		{ AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS,
+					avrcp_handle_displayable_charset },
+		{ AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS,
+					avrcp_handle_ct_battery_status },
+		{ AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS,
+					avrcp_handle_get_play_status },
+		{ AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY,
+					avrcp_handle_register_notification },
+		{ AVRCP_SET_ABSOLUTE_VOLUME, AVC_CTYPE_CONTROL,
+					avrcp_handle_set_absolute_volume },
+		{ AVRCP_REQUEST_CONTINUING, AVC_CTYPE_CONTROL,
+					avrcp_handle_request_continuing },
+		{ AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
+					avrcp_handle_abort_continuing },
+		{ AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
+					avrcp_handle_set_addressed_player },
+		{ },
+};
+
+/* handle vendordep pdu inside an avctp packet */
+static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
+					uint8_t *code, uint8_t *subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	const struct control_pdu_handler *handler;
+	struct avrcp_header *pdu = (void *) operands;
+	uint32_t company_id = get_company_id(pdu->company_id);
+
+	if (company_id != IEEEID_BTSIG) {
+		*code = AVC_CTYPE_NOT_IMPLEMENTED;
+		return 0;
+	}
+
+	DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+			pdu->pdu_id, company_id, ntohs(pdu->params_len));
+
+	pdu->packet_type = 0;
+	pdu->rsvd = 0;
+
+	if (operand_count < AVRCP_HEADER_LENGTH) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto err_metadata;
+	}
+
+	for (handler = session->control_handlers; handler->pdu_id; handler++) {
+		if (handler->pdu_id == pdu->pdu_id)
+			break;
+	}
+
+	if (handler->pdu_id != pdu->pdu_id || handler->code != *code) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+		goto err_metadata;
+	}
+
+	if (!handler->func) {
+		pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+		goto err_metadata;
+	}
+
+	*code = handler->func(session, pdu, transaction);
+
+	if (*code != AVC_CTYPE_REJECTED &&
+				pdu->pdu_id != AVRCP_GET_ELEMENT_ATTRIBUTES &&
+				pdu->pdu_id != AVRCP_REQUEST_CONTINUING &&
+				pdu->pdu_id != AVRCP_ABORT_CONTINUING)
+		session_abort_pending_pdu(session);
+
+	return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+err_metadata:
+	pdu->params_len = htons(1);
+	*code = AVC_CTYPE_REJECTED;
+
+	return AVRCP_HEADER_LENGTH + 1;
+}
+
+static void avrcp_handle_media_player_list(struct avrcp *session,
+				struct avrcp_browsing_header *pdu,
+				uint32_t start_item, uint32_t end_item)
+{
+	struct avrcp_player *player = session->target->player;
+	struct get_folder_items_rsp *rsp;
+	const char *name = NULL;
+	GSList *l;
+
+	rsp = (void *)pdu->params;
+	rsp->status = AVRCP_STATUS_SUCCESS;
+	rsp->uid_counter = htons(player_get_uid_counter(player));
+	rsp->num_items = 0;
+	pdu->param_len = sizeof(*rsp);
+
+	for (l = g_slist_nth(session->server->players, start_item);
+					l; l = g_slist_next(l)) {
+		struct avrcp_player *player = l->data;
+		struct folder_item *folder;
+		struct player_item *item;
+		uint16_t namelen;
+
+		if (rsp->num_items == (end_item - start_item) + 1)
+			break;
+
+		folder = (void *)&pdu->params[pdu->param_len];
+		folder->type = 0x01; /* Media Player */
+
+		pdu->param_len += sizeof(*folder);
+
+		item = (void *)folder->data;
+		item->player_id = htons(player->id);
+		item->type = 0x01; /* Audio */
+		item->subtype = htonl(0x01); /* Audio Book */
+		item->status = player_get_status(player);
+		/* Assign Default Feature Bit Mask */
+		memcpy(&item->features, &features, sizeof(features));
+
+		item->charset = htons(AVRCP_CHARSET_UTF8);
+
+		name = player->cb->get_name(player->user_data);
+		namelen = strlen(name);
+		item->namelen = htons(namelen);
+		memcpy(item->name, name, namelen);
+
+		folder->len = htons(sizeof(*item) + namelen);
+		pdu->param_len += sizeof(*item) + namelen;
+		rsp->num_items++;
+	}
+
+	/* If no player could be found respond with an error */
+	if (!rsp->num_items)
+		goto failed;
+
+	rsp->num_items = htons(rsp->num_items);
+	pdu->param_len = htons(pdu->param_len);
+
+	return;
+
+failed:
+	pdu->params[0] = AVRCP_STATUS_OUT_OF_BOUNDS;
+	pdu->param_len = htons(1);
+}
+
+static void avrcp_handle_get_folder_items(struct avrcp *session,
+				struct avrcp_browsing_header *pdu,
+				uint8_t transaction)
+{
+	uint32_t start_item = 0;
+	uint32_t end_item = 0;
+	uint8_t scope;
+	uint8_t status = AVRCP_STATUS_SUCCESS;
+
+	if (ntohs(pdu->param_len) < 10) {
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto failed;
+	}
+
+	scope = pdu->params[0];
+	start_item = bt_get_be32(&pdu->params[1]);
+	end_item = bt_get_be32(&pdu->params[5]);
+
+	DBG("scope 0x%02x start_item 0x%08x end_item 0x%08x", scope,
+				start_item, end_item);
+
+	if (end_item < start_item) {
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto failed;
+	}
+
+	switch (scope) {
+	case AVRCP_SCOPE_MEDIA_PLAYER_LIST:
+		avrcp_handle_media_player_list(session, pdu,
+						start_item, end_item);
+		break;
+	case AVRCP_SCOPE_MEDIA_PLAYER_VFS:
+	case AVRCP_SCOPE_SEARCH:
+	case AVRCP_SCOPE_NOW_PLAYING:
+	default:
+		status = AVRCP_STATUS_INVALID_PARAM;
+		goto failed;
+	}
+
+	return;
+
+failed:
+	pdu->params[0] = status;
+	pdu->param_len = htons(1);
+}
+
+static struct browsing_pdu_handler {
+	uint8_t pdu_id;
+	void (*func) (struct avrcp *session, struct avrcp_browsing_header *pdu,
+							uint8_t transaction);
+} browsing_handlers[] = {
+		{ AVRCP_GET_FOLDER_ITEMS, avrcp_handle_get_folder_items },
+		{ },
+};
+
+size_t avrcp_browsing_general_reject(uint8_t *operands)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	uint8_t status;
+
+	pdu->pdu_id = AVRCP_GENERAL_REJECT;
+	status = AVRCP_STATUS_INVALID_COMMAND;
+
+	pdu->param_len = htons(sizeof(status));
+	memcpy(pdu->params, &status, (sizeof(status)));
+	return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
+}
+
+static size_t handle_browsing_pdu(struct avctp *conn,
+					uint8_t transaction, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct browsing_pdu_handler *handler;
+	struct avrcp_browsing_header *pdu = (void *) operands;
+
+	DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
+							ntohs(pdu->param_len));
+
+	for (handler = browsing_handlers; handler->pdu_id; handler++) {
+		if (handler->pdu_id == pdu->pdu_id)
+			goto done;
+	}
+
+	return avrcp_browsing_general_reject(operands);
+
+done:
+	session->transaction = transaction;
+	handler->func(session, pdu, transaction);
+	return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len);
+}
+
+size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
+{
+	struct avrcp_header *pdu = (void *) operands;
+	uint32_t company_id = get_company_id(pdu->company_id);
+
+	*code = AVC_CTYPE_REJECTED;
+	pdu->params_len = htons(1);
+	pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+
+	DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+			pdu->pdu_id, company_id, ntohs(pdu->params_len));
+
+	return AVRCP_HEADER_LENGTH + 1;
+}
+
+static struct avrcp_server *find_server(GSList *list, struct btd_adapter *a)
+{
+	for (; list; list = list->next) {
+		struct avrcp_server *server = list->data;
+
+		if (server->adapter == a)
+			return server;
+	}
+
+	return NULL;
+}
+
+static const char *status_to_string(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_PLAY_STATUS_STOPPED:
+		return "stopped";
+	case AVRCP_PLAY_STATUS_PLAYING:
+		return "playing";
+	case AVRCP_PLAY_STATUS_PAUSED:
+		return "paused";
+	case AVRCP_PLAY_STATUS_FWD_SEEK:
+		return "forward-seek";
+	case AVRCP_PLAY_STATUS_REV_SEEK:
+		return "reverse-seek";
+	case AVRCP_PLAY_STATUS_ERROR:
+		return "error";
+	default:
+		return NULL;
+	}
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint32_t duration;
+	uint32_t position;
+	uint8_t status;
+
+	if (pdu == NULL || code == AVC_CTYPE_REJECTED ||
+						ntohs(pdu->params_len) != 9)
+		return FALSE;
+
+	memcpy(&duration, pdu->params, sizeof(uint32_t));
+	duration = ntohl(duration);
+	media_player_set_duration(mp, duration);
+
+	memcpy(&position, pdu->params + 4, sizeof(uint32_t));
+	position = ntohl(position);
+	media_player_set_position(mp, position);
+
+	memcpy(&status, pdu->params + 8, sizeof(uint8_t));
+	media_player_set_status(mp, status_to_string(status));
+
+	return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH];
+	struct avrcp_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+					avrcp_get_play_status_rsp,
+					session);
+}
+
+static const char *status_to_str(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_STATUS_INVALID_COMMAND:
+		return "Invalid Command";
+	case AVRCP_STATUS_INVALID_PARAM:
+		return "Invalid Parameter";
+	case AVRCP_STATUS_INTERNAL_ERROR:
+		return "Internal Error";
+	case AVRCP_STATUS_SUCCESS:
+		return "Success";
+	default:
+		return "Unknown";
+	}
+}
+
+static gboolean avrcp_player_value_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t count;
+	int i;
+
+	if (pdu == NULL) {
+		media_player_set_setting(mp, "Error", "Timeout");
+		return FALSE;
+	}
+
+	if (code == AVC_CTYPE_REJECTED) {
+		media_player_set_setting(mp, "Error",
+					status_to_str(pdu->params[0]));
+		return FALSE;
+	}
+
+	count = pdu->params[0];
+
+	if (pdu->params_len < count * 2)
+		return FALSE;
+
+	for (i = 1; count > 0; count--, i += 2) {
+		const char *key;
+		const char *value;
+
+		key = attr_to_str(pdu->params[i]);
+		if (key == NULL)
+			continue;
+
+		value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+		if (value == NULL)
+			continue;
+
+		media_player_set_setting(mp, key, value);
+	}
+
+	return FALSE;
+}
+
+static void avrcp_get_current_player_value(struct avrcp *session,
+						uint8_t *attrs, uint8_t count)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_ATTRIBUTE_LAST + 1];
+	struct avrcp_header *pdu = (void *) buf;
+	uint16_t length = AVRCP_HEADER_LENGTH + count + 1;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	pdu->params_len = htons(count + 1);
+	pdu->params[0] = count;
+
+	memcpy(pdu->params + 1, attrs, count);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_player_value_rsp, session);
+}
+
+static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+	struct avrcp *session = user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t len, count = 0;
+	int i;
+
+	if (code == AVC_CTYPE_REJECTED)
+		return FALSE;
+
+	len = pdu->params[0];
+
+	if (ntohs(pdu->params_len) < count) {
+		error("Invalid parameters");
+		return FALSE;
+	}
+
+	for (i = 0; len > 0; len--, i++) {
+		/* Don't query invalid attributes */
+		if (pdu->params[i + 1] == AVRCP_ATTRIBUTE_ILEGAL ||
+				pdu->params[i + 1] > AVRCP_ATTRIBUTE_LAST)
+			continue;
+
+		attrs[count++] = pdu->params[i + 1];
+	}
+
+	avrcp_get_current_player_value(session, attrs, count);
+
+	return FALSE;
+}
+
+static void avrcp_list_player_attributes(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH];
+	struct avrcp_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+					avrcp_list_player_attributes_rsp,
+					session);
+}
+
+static void avrcp_parse_attribute_list(struct avrcp_player *player,
+					uint8_t *operands, uint8_t count)
+{
+	struct media_player *mp = player->user_data;
+	struct media_item *item;
+	int i;
+
+	item = media_player_set_playlist_item(mp, player->uid);
+
+	for (i = 0; count > 0; count--) {
+		uint32_t id;
+		uint16_t charset, len;
+
+		id = get_be32(&operands[i]);
+		i += sizeof(uint32_t);
+
+		charset = get_be16(&operands[i]);
+		i += sizeof(uint16_t);
+
+		len = get_be16(&operands[i]);
+		i += sizeof(uint16_t);
+
+		if (charset == 106) {
+			const char *key = metadata_to_str(id);
+
+			if (key != NULL)
+				media_player_set_metadata(mp, item,
+							metadata_to_str(id),
+							&operands[i], len);
+		}
+
+		i += len;
+	}
+}
+
+static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn,
+						uint8_t code, uint8_t subunit,
+						uint8_t *operands,
+						size_t operand_count,
+						void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t count;
+
+	if (code == AVC_CTYPE_REJECTED)
+		return FALSE;
+
+	count = pdu->params[0];
+
+	if (ntohs(pdu->params_len) - 1 < count * 8) {
+		error("Invalid parameters");
+		return FALSE;
+	}
+
+	avrcp_parse_attribute_list(player, &pdu->params[1], count);
+
+	avrcp_get_play_status(session);
+
+	return FALSE;
+}
+
+static void avrcp_get_element_attributes(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+	struct avrcp_header *pdu = (void *) buf;
+	uint16_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
+	pdu->params_len = htons(9);
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_get_element_attributes_rsp,
+					session);
+}
+
+static const char *type_to_string(uint8_t type)
+{
+	switch (type & 0x0F) {
+	case 0x01:
+		return "Audio";
+	case 0x02:
+		return "Video";
+	case 0x03:
+		return "Audio, Video";
+	case 0x04:
+		return "Audio Broadcasting";
+	case 0x05:
+		return "Audio, Audio Broadcasting";
+	case 0x06:
+		return "Video, Audio Broadcasting";
+	case 0x07:
+		return "Audio, Video, Audio Broadcasting";
+	case 0x08:
+		return "Video Broadcasting";
+	case 0x09:
+		return "Audio, Video Broadcasting";
+	case 0x0A:
+		return "Video, Video Broadcasting";
+	case 0x0B:
+		return "Audio, Video, Video Broadcasting";
+	case 0x0C:
+		return "Audio Broadcasting, Video Broadcasting";
+	case 0x0D:
+		return "Audio, Audio Broadcasting, Video Broadcasting";
+	case 0x0E:
+		return "Video, Audio Broadcasting, Video Broadcasting";
+	case 0x0F:
+		return "Audio, Video, Audio Broadcasting, Video Broadcasting";
+	}
+
+	return "None";
+}
+
+static const char *subtype_to_string(uint32_t subtype)
+{
+	switch (subtype & 0x03) {
+	case 0x01:
+		return "Audio Book";
+	case 0x02:
+		return "Podcast";
+	case 0x03:
+		return "Audio Book, Podcast";
+	}
+
+	return "None";
+}
+
+static struct media_item *parse_media_element(struct avrcp *session,
+					uint8_t *operands, uint16_t len)
+{
+	struct avrcp_player *player;
+	struct media_player *mp;
+	struct media_item *item;
+	uint16_t namelen;
+	char name[255];
+	uint64_t uid;
+
+	if (len < 13)
+		return NULL;
+
+	uid = get_be64(&operands[0]);
+
+	namelen = MIN(get_be16(&operands[11]), sizeof(name) - 1);
+	if (namelen > 0) {
+		memcpy(name, &operands[13], namelen);
+		name[namelen] = '\0';
+	}
+
+	player = session->controller->player;
+	mp = player->user_data;
+
+	item = media_player_create_item(mp, name, PLAYER_ITEM_TYPE_AUDIO, uid);
+	if (item == NULL)
+		return NULL;
+
+	media_item_set_playable(item, true);
+
+	return item;
+}
+
+static struct media_item *parse_media_folder(struct avrcp *session,
+					uint8_t *operands, uint16_t len)
+{
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	struct media_item *item;
+	uint16_t namelen;
+	char name[255];
+	uint64_t uid;
+	uint8_t type;
+	uint8_t playable;
+
+	if (len < 12)
+		return NULL;
+
+	uid = get_be64(&operands[0]);
+	type = operands[8];
+	playable = operands[9];
+
+	namelen = MIN(get_be16(&operands[12]), sizeof(name) - 1);
+	if (namelen > 0) {
+		memcpy(name, &operands[14], namelen);
+		name[namelen] = '\0';
+	}
+
+	item = media_player_create_folder(mp, name, type, uid);
+	if (!item)
+		return NULL;
+
+	media_item_set_playable(item, playable & 0x01);
+
+	return item;
+}
+
+static void avrcp_list_items(struct avrcp *session, uint32_t start,
+								uint32_t end);
+static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct pending_list_items *p = player->p;
+	uint16_t count;
+	uint64_t items;
+	size_t i;
+	int err = 0;
+
+	if (pdu == NULL) {
+		err = -ETIMEDOUT;
+		goto done;
+	}
+
+	/* AVRCP 1.5 - Page 76:
+	 * If the TG receives a GetFolderItems command for an empty folder then
+	 * the TG shall return the error (= Range Out of Bounds) in the status
+	 * field of the GetFolderItems response.
+	 */
+	if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
+		goto done;
+
+	if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 5) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	count = get_be16(&operands[6]);
+	if (count == 0)
+		goto done;
+
+	for (i = 8; count && i + 3 < operand_count; count--) {
+		struct media_item *item;
+		uint8_t type;
+		uint16_t len;
+
+		type = operands[i++];
+		len = get_be16(&operands[i]);
+		i += 2;
+
+		if (type != 0x03 && type != 0x02) {
+			i += len;
+			continue;
+		}
+
+		if (i + len > operand_count) {
+			error("Invalid item length");
+			break;
+		}
+
+		if (type == 0x03)
+			item = parse_media_element(session, &operands[i], len);
+		else
+			item = parse_media_folder(session, &operands[i], len);
+
+		if (item) {
+			if (g_slist_find(p->items, item))
+				goto done;
+			p->items = g_slist_append(p->items, item);
+		}
+
+		i += len;
+	}
+
+	items = g_slist_length(p->items);
+
+	DBG("start %u end %u items %" PRIu64 " total %" PRIu64 "", p->start,
+						p->end, items, p->total);
+
+	if (items < p->total) {
+		avrcp_list_items(session, p->start + items, p->end);
+		return FALSE;
+	}
+
+done:
+	media_player_list_complete(player->user_data, p->items, err);
+
+	g_slist_free(p->items);
+	g_free(p);
+	player->p = NULL;
+
+	return FALSE;
+}
+
+static void avrcp_list_items(struct avrcp *session, uint32_t start,
+								uint32_t end)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10 +
+			AVRCP_MEDIA_ATTRIBUTE_LAST * sizeof(uint32_t)];
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_browsing_header *pdu = (void *) buf;
+	uint16_t length = AVRCP_BROWSING_HEADER_LENGTH + 10;
+	uint32_t attribute;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+	pdu->param_len = htons(10 + sizeof(uint32_t));
+
+	pdu->params[0] = player->scope;
+
+	put_be32(start, &pdu->params[1]);
+	put_be32(end, &pdu->params[5]);
+
+	pdu->params[9] = 1;
+
+	/* Only the title (0x01) is mandatory. This can be extended to
+	 * support AVRCP_MEDIA_ATTRIBUTE_* attributes */
+	attribute = htonl(AVRCP_MEDIA_ATTRIBUTE_TITLE);
+	memcpy(&pdu->params[10], &attribute, sizeof(uint32_t));
+
+	length += sizeof(uint32_t);
+
+	avctp_send_browsing_req(session->conn, buf, length,
+					avrcp_list_items_rsp, session);
+}
+
+static gboolean avrcp_change_path_rsp(struct avctp *conn,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	int ret;
+
+	if (pdu == NULL) {
+		ret = -ETIMEDOUT;
+		goto done;
+	}
+
+	if (pdu->params[0] != AVRCP_STATUS_SUCCESS) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = get_be32(&pdu->params[1]);
+
+done:
+	if (ret < 0) {
+		g_free(player->change_path);
+		player->change_path = NULL;
+	} else {
+		g_free(player->path);
+		player->path = player->change_path;
+		player->change_path = NULL;
+	}
+
+	media_player_change_folder_complete(mp, player->path, ret);
+
+	return FALSE;
+}
+
+static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
+						uint8_t *operands,
+						size_t operand_count,
+						void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	uint32_t items;
+	char **folders;
+	uint8_t depth, count;
+	size_t i;
+
+	if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS ||
+							operand_count < 13)
+		return FALSE;
+
+	player->uid_counter = get_be16(&pdu->params[1]);
+	player->browsed = true;
+
+	items = get_be32(&pdu->params[3]);
+
+	depth = pdu->params[9];
+
+	folders = g_new0(char *, depth + 2);
+	folders[0] = g_strdup("/Filesystem");
+
+	for (i = 10, count = 1; count - 1 < depth && i < operand_count;
+								count++) {
+		uint8_t len;
+
+		len = pdu->params[i++];
+
+		if (i + len > operand_count || len == 0) {
+			error("Invalid folder length");
+			break;
+		}
+
+		folders[count] = g_memdup(&pdu->params[i], len);
+		i += len;
+	}
+
+	player->path = g_build_pathv("/", folders);
+	g_strfreev(folders);
+
+	media_player_set_folder(mp, player->path, items);
+
+	return FALSE;
+}
+
+static void avrcp_set_browsed_player(struct avrcp *session,
+						struct avrcp_player *player)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 2];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+	uint16_t id;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_SET_BROWSED_PLAYER;
+	id = htons(player->id);
+	memcpy(pdu->params, &id, 2);
+	pdu->param_len = htons(2);
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+				avrcp_set_browsed_player_rsp, session);
+}
+
+static gboolean avrcp_get_item_attributes_rsp(struct avctp *conn,
+						uint8_t *operands,
+						size_t operand_count,
+						void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	uint8_t count;
+
+	if (pdu == NULL) {
+		avrcp_get_element_attributes(session);
+		return FALSE;
+	}
+
+	if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 4) {
+		avrcp_get_element_attributes(session);
+		return FALSE;
+	}
+
+	count = pdu->params[1];
+
+	if (ntohs(pdu->param_len) - 1 < count * 8) {
+		error("Invalid parameters");
+		return FALSE;
+	}
+
+	avrcp_parse_attribute_list(player, &pdu->params[2], count);
+
+	avrcp_get_play_status(session);
+
+	return FALSE;
+}
+
+static void avrcp_get_item_attributes(struct avrcp *session, uint64_t uid)
+{
+	struct avrcp_player *player = session->controller->player;
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 12];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_GET_ITEM_ATTRIBUTES;
+	pdu->params[0] = 0x03;
+	put_be64(uid, &pdu->params[1]);
+	put_be16(player->uid_counter, &pdu->params[9]);
+	pdu->param_len = htons(12);
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+				avrcp_get_item_attributes_rsp, session);
+}
+
+static void avrcp_player_parse_features(struct avrcp_player *player,
+							uint8_t *features)
+{
+	struct media_player *mp = player->user_data;
+
+	player->features = g_memdup(features, 16);
+
+	if (features[7] & 0x08) {
+		media_player_set_browsable(mp, true);
+		media_player_create_folder(mp, "/Filesystem",
+						PLAYER_FOLDER_TYPE_MIXED, 0);
+	}
+
+	if (features[7] & 0x10)
+		media_player_set_searchable(mp, true);
+
+	if (features[8] & 0x02) {
+		media_player_create_folder(mp, "/NowPlaying",
+						PLAYER_FOLDER_TYPE_MIXED, 0);
+		media_player_set_playlist(mp, "/NowPlaying");
+	}
+}
+
+static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
+								uint8_t val)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 3];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	pdu->params[0] = 1;
+	pdu->params[1] = attr;
+	pdu->params[2] = val;
+	pdu->params_len = htons(3);
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_player_value_rsp, session);
+}
+
+static bool ct_set_setting(struct media_player *mp, const char *key,
+					const char *value, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	int attr;
+	int val;
+	struct avrcp *session;
+
+	session = player->sessions->data;
+	if (session == NULL)
+		return false;
+
+	if (session->controller->version < 0x0103)
+		return false;
+
+	attr = attr_to_val(key);
+	if (attr < 0)
+		return false;
+
+	val = attrval_to_val(attr, value);
+	if (val < 0)
+		return false;
+
+	avrcp_set_player_value(session, attr, val);
+
+	return true;
+}
+
+static int ct_press(struct avrcp_player *player, uint8_t op)
+{
+	int err;
+	struct avrcp *session;
+
+	session = player->sessions->data;
+	if (session == NULL)
+		return -ENOTCONN;
+
+	err = avctp_send_passthrough(session->conn, op);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int ct_play(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_PLAY);
+}
+
+static int ct_pause(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_PAUSE);
+}
+
+static int ct_stop(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_STOP);
+}
+
+static int ct_next(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_FORWARD);
+}
+
+static int ct_previous(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_BACKWARD);
+}
+
+static int ct_fast_forward(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_FAST_FORWARD);
+}
+
+static int ct_rewind(struct media_player *mp, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+
+	return ct_press(player, AVC_REWIND);
+}
+
+static int ct_list_items(struct media_player *mp, const char *name,
+				uint32_t start, uint32_t end, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+	struct pending_list_items *p;
+
+	if (player->p != NULL)
+		return -EBUSY;
+
+	session = player->sessions->data;
+
+	if (g_str_has_prefix(name, "/NowPlaying"))
+		player->scope = 0x03;
+	else if (g_str_has_suffix(name, "/search"))
+		player->scope = 0x02;
+	else
+		player->scope = 0x01;
+
+	avrcp_list_items(session, start, end);
+
+	p = g_new0(struct pending_list_items, 1);
+	p->start = start;
+	p->end = end;
+	p->total = (uint64_t) (p->end - p->start) + 1;
+	player->p = p;
+
+	return 0;
+}
+
+static void avrcp_change_path(struct avrcp *session, uint8_t direction,
+								uint64_t uid)
+{
+	struct avrcp_player *player = session->controller->player;
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 11];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+	put_be16(player->uid_counter, &pdu->params[0]);
+	pdu->params[2] = direction;
+	put_be64(uid, &pdu->params[3]);
+	pdu->pdu_id = AVRCP_CHANGE_PATH;
+	pdu->param_len = htons(11);
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+					avrcp_change_path_rsp, session);
+}
+
+static int ct_change_folder(struct media_player *mp, const char *path,
+					uint64_t uid, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+	uint8_t direction;
+
+	session = player->sessions->data;
+	player->change_path = g_strdup(path);
+
+	direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
+
+	avrcp_change_path(session, direction, uid);
+
+	return 0;
+}
+
+static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	struct avrcp *session = (void *) user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	int ret;
+
+	if (pdu == NULL) {
+		ret = -ETIMEDOUT;
+		goto done;
+	}
+
+	if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	player->uid_counter = get_be16(&pdu->params[1]);
+	ret = get_be32(&pdu->params[3]);
+
+done:
+	media_player_search_complete(mp, ret);
+
+	return FALSE;
+}
+
+static void avrcp_search(struct avrcp *session, const char *string)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 255];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+	uint16_t len, stringlen;
+
+	memset(buf, 0, sizeof(buf));
+	len = AVRCP_BROWSING_HEADER_LENGTH + 4;
+	stringlen = strnlen(string, sizeof(buf) - len);
+	len += stringlen;
+
+	put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]);
+	put_be16(stringlen, &pdu->params[2]);
+	memcpy(&pdu->params[4], string, stringlen);
+	pdu->pdu_id = AVRCP_SEARCH;
+	pdu->param_len = htons(len - AVRCP_BROWSING_HEADER_LENGTH);
+
+	avctp_send_browsing_req(session->conn, buf, len, avrcp_search_rsp,
+								session);
+}
+
+static int ct_search(struct media_player *mp, const char *string,
+							void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+
+	session = player->sessions->data;
+
+	avrcp_search(session, string);
+
+	return 0;
+}
+
+static void avrcp_play_item(struct avrcp *session, uint64_t uid)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 11];
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_header *pdu = (void *) buf;
+	uint16_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_PLAY_ITEM;
+	pdu->params_len = htons(11);
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	pdu->params[0] = player->scope;
+	put_be64(uid, &pdu->params[1]);
+	put_be16(player->uid_counter, &pdu->params[9]);
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+					AVC_SUBUNIT_PANEL, buf, length,
+					NULL, session);
+}
+
+static int ct_play_item(struct media_player *mp, const char *name,
+						uint64_t uid, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+
+	if (player->p != NULL)
+		return -EBUSY;
+
+	session = player->sessions->data;
+
+	if (g_strrstr(name, "/NowPlaying"))
+		player->scope = 0x03;
+	else
+		player->scope = 0x01;
+
+	avrcp_play_item(session, uid);
+
+	return 0;
+}
+
+static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 11];
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_header *pdu = (void *) buf;
+	uint16_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_ADD_TO_NOW_PLAYING;
+	pdu->params_len = htons(11);
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+	pdu->params[0] = player->scope;
+	put_be64(uid, &pdu->params[1]);
+	put_be16(player->uid_counter, &pdu->params[9]);
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+					AVC_SUBUNIT_PANEL, buf, length,
+					NULL, session);
+}
+
+static int ct_add_to_nowplaying(struct media_player *mp, const char *name,
+						uint64_t uid, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+
+	if (player->p != NULL)
+		return -EBUSY;
+
+	session = player->sessions->data;
+
+	if (g_strrstr(name, "/NowPlaying"))
+		player->scope = 0x03;
+	else
+		player->scope = 0x01;
+
+	avrcp_add_to_nowplaying(session, uid);
+
+	return 0;
+}
+
+static gboolean avrcp_get_total_numberofitems_rsp(struct avctp *conn,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	uint32_t num_of_items;
+
+	if (pdu == NULL)
+		return -ETIMEDOUT;
+
+	if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7)
+		return -EINVAL;
+
+	if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
+		goto done;
+
+	player->uid_counter = get_be16(&pdu->params[1]);
+	num_of_items = get_be32(&pdu->params[3]);
+
+	if (!num_of_items)
+		return -EINVAL;
+
+done:
+	media_player_total_items_complete(mp, num_of_items);
+	return FALSE;
+}
+
+static void avrcp_get_total_numberofitems(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 7];
+	struct avrcp_player *player = session->controller->player;
+	struct avrcp_browsing_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_GET_TOTAL_NUMBER_OF_ITEMS;
+	pdu->param_len = htons(7 + sizeof(uint32_t));
+
+	pdu->params[0] = player->scope;
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+				avrcp_get_total_numberofitems_rsp, session);
+}
+
+static int ct_get_total_numberofitems(struct media_player *mp, const char *name,
+						void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct avrcp *session;
+
+	session = player->sessions->data;
+
+	if (session->controller->version != 0x0106) {
+		error("version not supported");
+		return -1;
+	}
+
+	if (g_str_has_prefix(name, "/NowPlaying"))
+		player->scope = 0x03;
+	else if (g_str_has_suffix(name, "/search"))
+		player->scope = 0x02;
+	else
+		player->scope = 0x01;
+
+	avrcp_get_total_numberofitems(session);
+
+	return 0;
+}
+
+static const struct media_player_callback ct_cbs = {
+	.set_setting	= ct_set_setting,
+	.play		= ct_play,
+	.pause		= ct_pause,
+	.stop		= ct_stop,
+	.next		= ct_next,
+	.previous	= ct_previous,
+	.fast_forward	= ct_fast_forward,
+	.rewind		= ct_rewind,
+	.list_items	= ct_list_items,
+	.change_folder	= ct_change_folder,
+	.search		= ct_search,
+	.play_item	= ct_play_item,
+	.add_to_nowplaying = ct_add_to_nowplaying,
+	.total_items = ct_get_total_numberofitems,
+};
+
+static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
+{
+	struct btd_service *service;
+
+	session->controller->player = player;
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+	control_set_player(service, media_player_get_path(player->user_data));
+}
+
+static struct avrcp_player *create_ct_player(struct avrcp *session,
+								uint16_t id)
+{
+	struct avrcp_player *player;
+	struct media_player *mp;
+	const char *path;
+
+	player = g_new0(struct avrcp_player, 1);
+	player->sessions = g_slist_prepend(player->sessions, session);
+
+	path = device_get_path(session->dev);
+
+	mp = media_player_controller_create(path, id);
+	if (mp == NULL)
+		return NULL;
+
+	media_player_set_callbacks(mp, &ct_cbs, player);
+	player->user_data = mp;
+	player->destroy = (GDestroyNotify) media_player_destroy;
+
+	if (session->controller->player == NULL)
+		set_ct_player(session, player);
+
+	session->controller->players = g_slist_prepend(
+						session->controller->players,
+						player);
+
+	return player;
+}
+
+static struct avrcp_player *find_ct_player(struct avrcp *session, uint16_t id)
+{
+	GSList *l;
+
+	for (l = session->controller->players; l; l = l->next) {
+		struct avrcp_player *player = l->data;
+
+		if (player->id == 0) {
+			player->id = id;
+			return player;
+		}
+
+		if (player->id == id)
+			return player;
+	}
+
+	return NULL;
+}
+
+static struct avrcp_player *
+avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands,
+							uint16_t len)
+{
+	struct avrcp_player *player;
+	struct media_player *mp;
+	uint16_t id, namelen;
+	uint32_t subtype;
+	const char *curval, *strval;
+	char name[255];
+
+	if (len < 28)
+		return NULL;
+
+	id = get_be16(&operands[0]);
+
+	player = find_ct_player(session, id);
+	if (player == NULL) {
+		player = create_ct_player(session, id);
+		if (player == NULL)
+			return NULL;
+	} else if (player->features != NULL)
+		return player;
+
+	mp = player->user_data;
+
+	media_player_set_type(mp, type_to_string(operands[2]));
+
+	subtype = get_be32(&operands[3]);
+
+	media_player_set_subtype(mp, subtype_to_string(subtype));
+
+	curval = media_player_get_status(mp);
+	strval = status_to_string(operands[7]);
+
+	if (g_strcmp0(curval, strval) != 0) {
+		media_player_set_status(mp, strval);
+		avrcp_get_play_status(session);
+	}
+
+	avrcp_player_parse_features(player, &operands[8]);
+
+	namelen = get_be16(&operands[26]);
+	if (namelen > 0 && namelen + 28 == len) {
+		namelen = MIN(namelen, sizeof(name) - 1);
+		memcpy(name, &operands[28], namelen);
+		name[namelen] = '\0';
+		media_player_set_name(mp, name);
+	}
+
+	if (session->controller->player == player && !player->browsed)
+		avrcp_set_browsed_player(session, player);
+
+	return player;
+}
+
+static void player_destroy(gpointer data)
+{
+	struct avrcp_player *player = data;
+
+	if (player->destroy)
+		player->destroy(player->user_data);
+
+	if (player->changed_id > 0)
+		g_source_remove(player->changed_id);
+
+	g_slist_free(player->sessions);
+	g_free(player->path);
+	g_free(player->change_path);
+	g_free(player->features);
+	g_free(player);
+}
+
+static void player_remove(gpointer data)
+{
+	struct avrcp_player *player = data;
+	GSList *l;
+
+	for (l = player->sessions; l; l = l->next) {
+		struct avrcp *session = l->data;
+		struct avrcp_data *controller = session->controller;
+
+		controller->players = g_slist_remove(controller->players,
+								player);
+
+		/* Check if current player is being removed */
+		if (controller->player == player)
+			set_ct_player(session, g_slist_nth_data(
+						controller->players, 0));
+	}
+
+	player_destroy(player);
+}
+
+static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
+						uint8_t *operands,
+						size_t operand_count,
+						void *user_data)
+{
+	struct avrcp_browsing_header *pdu = (void *) operands;
+	struct avrcp *session = user_data;
+	uint16_t count;
+	size_t i;
+	GSList *removed;
+
+	if (pdu == NULL || pdu->params[0] != AVRCP_STATUS_SUCCESS ||
+							operand_count < 5)
+		return FALSE;
+
+	removed = g_slist_copy(session->controller->players);
+	count = get_be16(&operands[6]);
+
+	for (i = 8; count && i < operand_count; count--) {
+		struct avrcp_player *player;
+		uint8_t type;
+		uint16_t len;
+
+		type = operands[i++];
+		len = get_be16(&operands[i]);
+		i += 2;
+
+		if (type != 0x01) {
+			i += len;
+			continue;
+		}
+
+		if (i + len > operand_count) {
+			error("Invalid player item length");
+			return FALSE;
+		}
+
+		player = avrcp_parse_media_player_item(session, &operands[i],
+									len);
+		if (player)
+			removed = g_slist_remove(removed, player);
+
+		i += len;
+	}
+
+	g_slist_free_full(removed, player_remove);
+
+	return FALSE;
+}
+
+static void avrcp_get_media_player_list(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 10];
+	struct avrcp_browsing_header *pdu = (void *) buf;
+
+	memset(buf, 0, sizeof(buf));
+
+	pdu->pdu_id = AVRCP_GET_FOLDER_ITEMS;
+	put_be32(0, &pdu->params[1]);
+	put_be32(UINT32_MAX, &pdu->params[5]);
+	pdu->param_len = htons(10);
+
+	avctp_send_browsing_req(session->conn, buf, sizeof(buf),
+				avrcp_get_media_player_list_rsp, session);
+}
+
+static void avrcp_volume_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = target_get_player(session);
+	uint8_t volume;
+
+	if (!player)
+		return;
+
+	volume = pdu->params[1] & 0x7F;
+
+	player->cb->set_volume(volume, session->dev, player->user_data);
+}
+
+static void avrcp_status_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	uint8_t value;
+	const char *curval, *strval;
+
+	value = pdu->params[1];
+
+	curval = media_player_get_status(mp);
+	strval = status_to_string(value);
+
+	if (g_strcmp0(curval, strval) == 0)
+		return;
+
+	media_player_set_status(mp, strval);
+	avrcp_get_play_status(session);
+}
+
+static void avrcp_track_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	if (session->browsing_id) {
+		struct avrcp_player *player = session->controller->player;
+		player->uid = get_be64(&pdu->params[1]);
+		avrcp_get_item_attributes(session, player->uid);
+	} else
+		avrcp_get_element_attributes(session);
+}
+
+static void avrcp_playback_pos_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	uint32_t position;
+
+	position = get_be32(&pdu->params[1]);
+	media_player_set_position(mp, position);
+}
+
+static void avrcp_setting_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->controller->player;
+	struct media_player *mp = player->user_data;
+	uint8_t count = pdu->params[1];
+	int i;
+
+	for (i = 2; count > 0; count--, i += 2) {
+		const char *key;
+		const char *value;
+
+		key = attr_to_str(pdu->params[i]);
+		if (key == NULL)
+			continue;
+
+		value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+		if (value == NULL)
+			continue;
+
+		media_player_set_setting(mp, key, value);
+	}
+}
+
+static void avrcp_available_players_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	avrcp_get_media_player_list(session);
+}
+
+static void avrcp_addressed_player_changed(struct avrcp *session,
+						struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->controller->player;
+	uint16_t id = get_be16(&pdu->params[1]);
+
+	if (player != NULL && player->id == id)
+		return;
+
+	player = find_ct_player(session, id);
+	if (player == NULL) {
+		player = create_ct_player(session, id);
+		if (player == NULL)
+			return;
+	}
+
+	player->uid_counter = get_be16(&pdu->params[3]);
+	set_ct_player(session, player);
+
+	if (player->features != NULL)
+		return;
+
+	avrcp_get_media_player_list(session);
+}
+
+static void avrcp_uids_changed(struct avrcp *session, struct avrcp_header *pdu)
+{
+	struct avrcp_player *player = session->controller->player;
+
+	player->uid_counter = get_be16(&pdu->params[1]);
+}
+
+static gboolean avrcp_handle_event(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t event;
+
+	if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) ||
+								pdu == NULL)
+		return FALSE;
+
+	event = pdu->params[0];
+
+	if (code == AVC_CTYPE_CHANGED) {
+		session->registered_events ^= (1 << event);
+		avrcp_register_notification(session, event);
+		return FALSE;
+	}
+
+	switch (event) {
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		avrcp_volume_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_STATUS_CHANGED:
+		avrcp_status_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		avrcp_track_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		avrcp_playback_pos_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		avrcp_setting_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		avrcp_available_players_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		avrcp_addressed_player_changed(session, pdu);
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		avrcp_uids_changed(session, pdu);
+		break;
+	}
+
+	session->registered_events |= (1 << event);
+
+	return TRUE;
+}
+
+static void avrcp_register_notification(struct avrcp *session, uint8_t event)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	pdu->params[0] = event;
+
+	/*
+	 * Set maximum interval possible for position changed as we only
+	 * use it to resync.
+	 */
+	if (event == AVRCP_EVENT_PLAYBACK_POS_CHANGED)
+		bt_put_be32(UINT32_MAX / 1000, &pdu->params[1]);
+
+	pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_handle_event, session);
+}
+
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_header *pdu = (void *) operands;
+	uint16_t events = 0;
+	uint8_t count;
+
+	if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED ||
+			pdu == NULL || pdu->params[0] != CAP_EVENTS_SUPPORTED)
+		return FALSE;
+
+	/* Connect browsing if pending */
+	if (session->browsing_timer > 0) {
+		g_source_remove(session->browsing_timer);
+		session->browsing_timer = 0;
+		avctp_connect_browsing(session->conn);
+	}
+
+	count = pdu->params[1];
+
+	for (; count > 0; count--) {
+		uint8_t event = pdu->params[1 + count];
+
+		events |= (1 << event);
+
+		switch (event) {
+		case AVRCP_EVENT_STATUS_CHANGED:
+		case AVRCP_EVENT_TRACK_CHANGED:
+		case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		case AVRCP_EVENT_SETTINGS_CHANGED:
+		case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		case AVRCP_EVENT_UIDS_CHANGED:
+		case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+			/* These events above are controller specific */
+			if (!session->controller)
+				break;
+		case AVRCP_EVENT_VOLUME_CHANGED:
+			avrcp_register_notification(session, event);
+			break;
+		}
+	}
+
+	if (!session->controller)
+		return FALSE;
+
+	if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
+		avrcp_list_player_attributes(session);
+
+	if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+		avrcp_get_play_status(session);
+
+	if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+		avrcp_get_element_attributes(session);
+
+	return FALSE;
+}
+
+static void avrcp_get_capabilities(struct avrcp *session)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_GET_CAPABILITIES_PARAM_LENGTH];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t length;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_GET_CAPABILITIES;
+	pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+	pdu->params[0] = CAP_EVENTS_SUPPORTED;
+	pdu->params_len = htons(AVRCP_GET_CAPABILITIES_PARAM_LENGTH);
+
+	length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+	avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+					AVC_SUBUNIT_PANEL, buf, length,
+					avrcp_get_capabilities_resp,
+					session);
+}
+
+static struct avrcp *find_session(GSList *list, struct btd_device *dev)
+{
+	for (; list; list = list->next) {
+		struct avrcp *session = list->data;
+
+		if (session->dev == dev)
+			return session;
+	}
+
+	return NULL;
+}
+
+static void destroy_browsing(void *data)
+{
+	struct avrcp *session = data;
+
+	session->browsing_id = 0;
+}
+
+static void session_init_browsing(struct avrcp *session)
+{
+	if (session->browsing_timer > 0) {
+		g_source_remove(session->browsing_timer);
+		session->browsing_timer = 0;
+	}
+
+	session->browsing_id = avctp_register_browsing_pdu_handler(
+							session->conn,
+							handle_browsing_pdu,
+							session,
+							destroy_browsing);
+}
+
+static struct avrcp_data *data_init(struct avrcp *session, const char *uuid)
+{
+	struct avrcp_data *data;
+	const sdp_record_t *rec;
+	sdp_list_t *list;
+	sdp_profile_desc_t *desc;
+
+	data = g_new0(struct avrcp_data, 1);
+
+	rec = btd_device_get_record(session->dev, uuid);
+	if (rec == NULL)
+		return data;
+
+	if (sdp_get_profile_descs(rec, &list) == 0) {
+		desc = list->data;
+		data->version = desc->version;
+	}
+
+	sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &data->features);
+	sdp_list_free(list, free);
+
+	return data;
+}
+
+static gboolean connect_browsing(gpointer user_data)
+{
+	struct avrcp *session = user_data;
+
+	session->browsing_timer = 0;
+
+	avctp_connect_browsing(session->conn);
+
+	return FALSE;
+}
+
+static void avrcp_connect_browsing(struct avrcp *session)
+{
+	/* Immediately connect browsing channel if initiator otherwise delay
+	 * it to avoid possible collisions
+	 */
+	if (avctp_is_initiator(session->conn)) {
+		avctp_connect_browsing(session->conn);
+		return;
+	}
+
+	if (session->browsing_timer > 0)
+		return;
+
+	session->browsing_timer = g_timeout_add_seconds(AVRCP_BROWSING_TIMEOUT,
+							connect_browsing,
+							session);
+}
+
+static void target_init(struct avrcp *session)
+{
+	struct avrcp_server *server = session->server;
+	struct avrcp_data *target;
+	struct avrcp_player *player;
+	struct btd_service *service;
+
+	if (session->target != NULL)
+		return;
+
+	target = data_init(session, AVRCP_REMOTE_UUID);
+	session->target = target;
+
+	DBG("%p version 0x%04x", target, target->version);
+
+	service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
+	btd_service_connecting_complete(service, 0);
+
+	player = g_slist_nth_data(server->players, 0);
+	if (player != NULL) {
+		target->player = player;
+		player->sessions = g_slist_prepend(player->sessions, session);
+	}
+
+	session->supported_events |= (1 << AVRCP_EVENT_STATUS_CHANGED) |
+				(1 << AVRCP_EVENT_TRACK_CHANGED) |
+				(1 << AVRCP_EVENT_TRACK_REACHED_START) |
+				(1 << AVRCP_EVENT_TRACK_REACHED_END) |
+				(1 << AVRCP_EVENT_SETTINGS_CHANGED);
+
+	if (target->version < 0x0104)
+		return;
+
+	session->supported_events |=
+				(1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
+				(1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
+				(1 << AVRCP_EVENT_VOLUME_CHANGED);
+
+	/* Only check capabilities if controller is not supported */
+	if (session->controller == NULL)
+		avrcp_get_capabilities(session);
+
+	if (!(target->features & AVRCP_FEATURE_BROWSING))
+		return;
+
+	avrcp_connect_browsing(session);
+}
+
+static void controller_init(struct avrcp *session)
+{
+	struct avrcp_player *player;
+	struct btd_service *service;
+	struct avrcp_data *controller;
+
+	if (session->controller != NULL)
+		return;
+
+	controller = data_init(session, AVRCP_TARGET_UUID);
+	session->controller = controller;
+
+	DBG("%p version 0x%04x", controller, controller->version);
+
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+	btd_service_connecting_complete(service, 0);
+
+	/* Only create player if category 1 is supported */
+	if (!(controller->features & AVRCP_FEATURE_CATEGORY_1))
+		return;
+
+	player = create_ct_player(session, 0);
+	if (player == NULL)
+		return;
+
+	if (controller->version < 0x0103)
+		return;
+
+	avrcp_get_capabilities(session);
+
+	if (controller->version < 0x0104)
+		return;
+
+	if (!(controller->features & AVRCP_FEATURE_BROWSING))
+		return;
+
+	avrcp_connect_browsing(session);
+}
+
+static void session_init_control(struct avrcp *session)
+{
+	session->passthrough_id = avctp_register_passthrough_handler(
+							session->conn,
+							handle_passthrough,
+							session);
+	session->passthrough_handlers = passthrough_handlers;
+	session->control_id = avctp_register_pdu_handler(session->conn,
+							AVC_OP_VENDORDEP,
+							handle_vendordep_pdu,
+							session);
+	session->control_handlers = control_handlers;
+
+	if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL)
+		controller_init(session);
+
+	if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL)
+		target_init(session);
+}
+
+static void controller_destroy(struct avrcp *session)
+{
+	struct avrcp_data *controller = session->controller;
+
+	DBG("%p", controller);
+
+	g_slist_free_full(controller->players, player_destroy);
+
+	g_free(controller);
+}
+
+static void target_destroy(struct avrcp *session)
+{
+	struct avrcp_data *target = session->target;
+	struct avrcp_player *player = target->player;
+
+	DBG("%p", target);
+
+	if (player != NULL)
+		player->sessions = g_slist_remove(player->sessions, session);
+
+	g_free(target);
+}
+
+static void session_destroy(struct avrcp *session, int err)
+{
+	struct avrcp_server *server = session->server;
+	struct btd_service *service;
+
+	server->sessions = g_slist_remove(server->sessions, session);
+
+	session_abort_pending_pdu(session);
+
+	service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+	if (service != NULL) {
+		if (session->control_id == 0)
+			btd_service_connecting_complete(service, err);
+		else
+			btd_service_disconnecting_complete(service, 0);
+	}
+
+	service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
+	if (service != NULL) {
+		if (session->control_id == 0)
+			btd_service_connecting_complete(service, err);
+		else
+			btd_service_disconnecting_complete(service, 0);
+	}
+
+	if (session->browsing_timer > 0)
+		g_source_remove(session->browsing_timer);
+
+	if (session->controller != NULL)
+		controller_destroy(session);
+
+	if (session->target != NULL)
+		target_destroy(session);
+
+	if (session->passthrough_id > 0)
+		avctp_unregister_passthrough_handler(session->passthrough_id);
+
+	if (session->control_id > 0)
+		avctp_unregister_pdu_handler(session->control_id);
+
+	if (session->browsing_id > 0)
+		avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+	g_free(session);
+}
+
+static struct avrcp *session_create(struct avrcp_server *server,
+						struct btd_device *device)
+{
+	struct avrcp *session;
+
+	session = g_new0(struct avrcp, 1);
+	session->server = server;
+	session->conn = avctp_connect(device);
+	session->dev = device;
+
+	server->sessions = g_slist_append(server->sessions, session);
+
+	return session;
+}
+
+static void state_changed(struct btd_device *device, avctp_state_t old_state,
+					avctp_state_t new_state, int err,
+					void *user_data)
+{
+	struct avrcp_server *server;
+	struct avrcp *session;
+
+	server = find_server(servers, device_get_adapter(device));
+	if (!server)
+		return;
+
+	session = find_session(server->sessions, device);
+
+	switch (new_state) {
+	case AVCTP_STATE_DISCONNECTED:
+		if (session == NULL)
+			break;
+
+		session_destroy(session, err);
+
+		break;
+	case AVCTP_STATE_CONNECTING:
+		if (session != NULL)
+			break;
+
+		session_create(server, device);
+
+		break;
+	case AVCTP_STATE_CONNECTED:
+		if (session == NULL || session->control_id > 0)
+			break;
+
+		session_init_control(session);
+
+		break;
+	case AVCTP_STATE_BROWSING_CONNECTED:
+		if (session == NULL || session->browsing_id > 0)
+			break;
+
+		session_init_browsing(session);
+
+		break;
+	case AVCTP_STATE_BROWSING_CONNECTING:
+	default:
+		return;
+	}
+}
+
+static struct avrcp_server *avrcp_server_register(struct btd_adapter *adapter)
+{
+	struct avrcp_server *server;
+
+	if (avctp_register(adapter, TRUE) < 0)
+		return NULL;
+
+	server = g_new0(struct avrcp_server, 1);
+	server->adapter = btd_adapter_ref(adapter);
+
+	servers = g_slist_append(servers, server);
+
+	if (!avctp_id)
+		avctp_id = avctp_add_state_cb(NULL, state_changed, NULL);
+
+	return server;
+}
+
+static void avrcp_server_unregister(struct avrcp_server *server)
+{
+	g_slist_free_full(server->sessions, g_free);
+	g_slist_free_full(server->players, player_destroy);
+
+	servers = g_slist_remove(servers, server);
+
+	avctp_unregister(server->adapter);
+	btd_adapter_unref(server->adapter);
+	g_free(server);
+
+	if (servers)
+		return;
+
+	if (avctp_id) {
+		avctp_remove_state_cb(avctp_id);
+		avctp_id = 0;
+	}
+}
+
+struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
+						struct avrcp_player_cb *cb,
+						void *user_data,
+						GDestroyNotify destroy)
+{
+	struct avrcp_server *server;
+	struct avrcp_player *player;
+	GSList *l;
+	static uint16_t id = 0;
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return NULL;
+
+	player = g_new0(struct avrcp_player, 1);
+	player->id = ++id;
+	player->server = server;
+	player->cb = cb;
+	player->user_data = user_data;
+	player->destroy = destroy;
+
+	server->players = g_slist_append(server->players, player);
+
+	/* Assign player to session without current player */
+	for (l = server->sessions; l; l = l->next) {
+		struct avrcp *session = l->data;
+		struct avrcp_data *target = session->target;
+
+		if (target == NULL)
+			continue;
+
+		if (target->player == NULL) {
+			target->player = player;
+			player->sessions = g_slist_append(player->sessions,
+								session);
+		}
+	}
+
+	avrcp_player_event(player,
+				AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
+
+	return player;
+}
+
+void avrcp_unregister_player(struct avrcp_player *player)
+{
+	struct avrcp_server *server = player->server;
+	GSList *l;
+
+	server->players = g_slist_remove(server->players, player);
+
+	/* Remove player from sessions using it */
+	for (l = player->sessions; l; l = l->next) {
+		struct avrcp *session = l->data;
+		struct avrcp_data *target = session->target;
+
+		if (target == NULL)
+			continue;
+
+		if (target->player == player)
+			target->player = g_slist_nth_data(server->players, 0);
+	}
+
+	avrcp_player_event(player,
+				AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
+
+	player_destroy(player);
+}
+
+static gboolean avrcp_handle_set_volume(struct avctp *conn,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct avrcp *session = user_data;
+	struct avrcp_player *player = target_get_player(session);
+	struct avrcp_header *pdu = (void *) operands;
+	uint8_t volume;
+
+	if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED ||
+								pdu == NULL)
+		return FALSE;
+
+	volume = pdu->params[0] & 0x7F;
+
+	if (player != NULL)
+		player->cb->set_volume(volume, session->dev, player->user_data);
+
+	return FALSE;
+}
+
+static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
+{
+	uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+	struct avrcp_header *pdu = (void *) buf;
+	uint8_t code;
+	uint16_t size;
+	int err;
+
+	/* Verify that the event is registered */
+	if (!(session->registered_events & (1 << id)))
+		return -ENOENT;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+	pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+	code = AVC_CTYPE_CHANGED;
+	pdu->params[0] = id;
+
+	DBG("id=%u", id);
+
+	switch (id) {
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		size = 2;
+		memcpy(&pdu->params[1], data, sizeof(uint8_t));
+		break;
+	default:
+		error("Unknown event %u", id);
+		return -EINVAL;
+	}
+
+	pdu->params_len = htons(size);
+
+	err = avctp_send_vendordep(session->conn,
+					session->transaction_events[id],
+					code, AVC_SUBUNIT_PANEL,
+					buf, size + AVRCP_HEADER_LENGTH);
+	if (err < 0)
+		return err;
+
+	/* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+	session->registered_events ^= 1 << id;
+
+	return err;
+}
+
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
+{
+	struct avrcp_server *server;
+	struct avrcp *session;
+	uint8_t buf[AVRCP_HEADER_LENGTH + 1];
+	struct avrcp_header *pdu = (void *) buf;
+
+	server = find_server(servers, device_get_adapter(dev));
+	if (server == NULL)
+		return -EINVAL;
+
+	session = find_session(server->sessions, dev);
+	if (session == NULL)
+		return -ENOTCONN;
+
+	if (notify) {
+		if (!session->target)
+			return -ENOTSUP;
+		return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
+								&volume);
+	}
+
+	if (!session->controller || session->controller->version < 0x0104)
+		return -ENOTSUP;
+
+	memset(buf, 0, sizeof(buf));
+
+	set_company_id(pdu->company_id, IEEEID_BTSIG);
+
+	pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
+	pdu->params[0] = volume;
+	pdu->params_len = htons(1);
+
+	return avctp_send_vendordep_req(session->conn,
+					AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+					buf, sizeof(buf),
+					avrcp_handle_set_volume, session);
+}
+
+static int avrcp_connect(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	const char *path = device_get_path(dev);
+
+	DBG("path %s", path);
+
+	return control_connect(service);
+}
+
+static int avrcp_disconnect(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	const char *path = device_get_path(dev);
+
+	DBG("path %s", path);
+
+	return control_disconnect(service);
+}
+
+static int avrcp_target_probe(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	DBG("path %s", device_get_path(dev));
+
+	return control_init_target(service);
+}
+
+static void avrcp_target_remove(struct btd_service *service)
+{
+	control_unregister(service);
+}
+
+static void avrcp_target_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct avrcp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	if (server->tg_record_id != 0) {
+		adapter_service_remove(adapter, server->tg_record_id);
+		server->tg_record_id = 0;
+	}
+
+	if (server->ct_record_id == 0)
+		avrcp_server_unregister(server);
+}
+
+static int avrcp_target_server_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	sdp_record_t *record;
+	struct avrcp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (server != NULL)
+		goto done;
+
+	server = avrcp_server_register(adapter);
+	if (server == NULL)
+		return -EPROTONOSUPPORT;
+
+done:
+	record = avrcp_tg_record();
+	if (!record) {
+		error("Unable to allocate new service record");
+		avrcp_target_server_remove(p, adapter);
+		return -1;
+	}
+
+	if (adapter_service_add(adapter, record) < 0) {
+		error("Unable to register AVRCP target service record");
+		avrcp_target_server_remove(p, adapter);
+		sdp_record_free(record);
+		return -1;
+	}
+	server->tg_record_id = record->handle;
+
+	return 0;
+}
+
+static struct btd_profile avrcp_target_profile = {
+	.name		= "audio-avrcp-target",
+
+	.remote_uuid	= AVRCP_TARGET_UUID,
+	.device_probe	= avrcp_target_probe,
+	.device_remove	= avrcp_target_remove,
+
+	.connect	= avrcp_connect,
+	.disconnect	= avrcp_disconnect,
+
+	.adapter_probe	= avrcp_target_server_probe,
+	.adapter_remove = avrcp_target_server_remove,
+};
+
+static int avrcp_controller_probe(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	DBG("path %s", device_get_path(dev));
+
+	return control_init_remote(service);
+}
+
+static void avrcp_controller_remove(struct btd_service *service)
+{
+	control_unregister(service);
+}
+
+static void avrcp_controller_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct avrcp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (!server)
+		return;
+
+	if (server->ct_record_id != 0) {
+		adapter_service_remove(adapter, server->ct_record_id);
+		server->ct_record_id = 0;
+	}
+
+	if (server->tg_record_id == 0)
+		avrcp_server_unregister(server);
+}
+
+static int avrcp_controller_server_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	sdp_record_t *record;
+	struct avrcp_server *server;
+
+	DBG("path %s", adapter_get_path(adapter));
+
+	server = find_server(servers, adapter);
+	if (server != NULL)
+		goto done;
+
+	server = avrcp_server_register(adapter);
+	if (server == NULL)
+		return -EPROTONOSUPPORT;
+
+done:
+	record = avrcp_ct_record();
+	if (!record) {
+		error("Unable to allocate new service record");
+		avrcp_controller_server_remove(p, adapter);
+		return -1;
+	}
+
+	if (adapter_service_add(adapter, record) < 0) {
+		error("Unable to register AVRCP service record");
+		avrcp_controller_server_remove(p, adapter);
+		sdp_record_free(record);
+		return -1;
+	}
+	server->ct_record_id = record->handle;
+
+	return 0;
+}
+
+static struct btd_profile avrcp_controller_profile = {
+	.name		= "avrcp-controller",
+
+	.remote_uuid	= AVRCP_REMOTE_UUID,
+	.device_probe	= avrcp_controller_probe,
+	.device_remove	= avrcp_controller_remove,
+
+	.connect	= avrcp_connect,
+	.disconnect	= avrcp_disconnect,
+
+	.adapter_probe	= avrcp_controller_server_probe,
+	.adapter_remove = avrcp_controller_server_remove,
+};
+
+static int avrcp_init(void)
+{
+	btd_profile_register(&avrcp_controller_profile);
+	btd_profile_register(&avrcp_target_profile);
+
+	return 0;
+}
+
+static void avrcp_exit(void)
+{
+	btd_profile_unregister(&avrcp_controller_profile);
+	btd_profile_unregister(&avrcp_target_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(avrcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+							avrcp_init, avrcp_exit)
diff --git a/bluez/repo/profiles/audio/avrcp.h b/bluez/repo/profiles/audio/avrcp.h
new file mode 100644
index 0000000..86d310c
--- /dev/null
+++ b/bluez/repo/profiles/audio/avrcp.h
@@ -0,0 +1,119 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+/* player attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL		0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER	0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE	0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE		0x03
+#define AVRCP_ATTRIBUTE_SCAN		0x04
+#define AVRCP_ATTRIBUTE_LAST		AVRCP_ATTRIBUTE_SCAN
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_OFF		0x01
+#define AVRCP_EQUALIZER_ON		0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF		0x01
+#define AVRCP_REPEAT_MODE_SINGLE	0x02
+#define AVRCP_REPEAT_MODE_ALL		0x03
+#define AVRCP_REPEAT_MODE_GROUP		0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF		0x01
+#define AVRCP_SHUFFLE_ALL		0x02
+#define AVRCP_SHUFFLE_GROUP		0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF			0x01
+#define AVRCP_SCAN_ALL			0x02
+#define AVRCP_SCAN_GROUP		0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL	0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE	0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST	0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM	0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK	0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS	0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE	0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION	0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST	AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* play status */
+#define AVRCP_PLAY_STATUS_STOPPED	0x00
+#define AVRCP_PLAY_STATUS_PLAYING	0x01
+#define AVRCP_PLAY_STATUS_PAUSED	0x02
+#define AVRCP_PLAY_STATUS_FWD_SEEK	0x03
+#define AVRCP_PLAY_STATUS_REV_SEEK	0x04
+#define AVRCP_PLAY_STATUS_ERROR		0xFF
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED		0x02
+#define AVRCP_EVENT_TRACK_REACHED_END		0x03
+#define AVRCP_EVENT_TRACK_REACHED_START		0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED	0x05
+#define AVRCP_EVENT_SETTINGS_CHANGED		0x08
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED	0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED	0x0b
+#define AVRCP_EVENT_UIDS_CHANGED		0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED		0x0d
+#define AVRCP_EVENT_LAST			AVRCP_EVENT_VOLUME_CHANGED
+
+struct avrcp_player_cb {
+	GList *(*list_settings) (void *user_data);
+	const char *(*get_setting) (const char *key, void *user_data);
+	int (*set_setting) (const char *key, const char *value,
+							void *user_data);
+	uint64_t (*get_uid) (void *user_data);
+	const char *(*get_metadata) (const char *key, void *user_data);
+	GList *(*list_metadata) (void *user_data);
+	const char *(*get_status) (void *user_data);
+	uint32_t (*get_position) (void *user_data);
+	uint32_t (*get_duration) (void *user_data);
+	const char *(*get_name) (void *user_data);
+	void (*set_volume) (uint8_t volume, struct btd_device *dev,
+							void *user_data);
+	bool (*play) (void *user_data);
+	bool (*stop) (void *user_data);
+	bool (*pause) (void *user_data);
+	bool (*next) (void *user_data);
+	bool (*previous) (void *user_data);
+};
+
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify);
+
+struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
+						struct avrcp_player_cb *cb,
+						void *user_data,
+						GDestroyNotify destroy);
+void avrcp_unregister_player(struct avrcp_player *player);
+
+void avrcp_player_event(struct avrcp_player *player, uint8_t id,
+							const void *data);
+
+
+size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands);
+size_t avrcp_browsing_general_reject(uint8_t *operands);
diff --git a/bluez/repo/profiles/audio/control.c b/bluez/repo/profiles/audio/control.c
new file mode 100644
index 0000000..edc4a98
--- /dev/null
+++ b/bluez/repo/profiles/audio/control.c
@@ -0,0 +1,389 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  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 <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.h"
+#include "src/uuid-helper.h"
+#include "src/dbus-common.h"
+
+#include "avctp.h"
+#include "control.h"
+#include "player.h"
+
+static GSList *devices = NULL;
+
+struct control {
+	struct btd_device *dev;
+	struct avctp *session;
+	struct btd_service *target;
+	struct btd_service *remote;
+	unsigned int avctp_id;
+	const char *player;
+};
+
+static void state_changed(struct btd_device *dev, avctp_state_t old_state,
+					avctp_state_t new_state, int err,
+					void *user_data)
+{
+	struct control *control = user_data;
+	DBusConnection *conn = btd_get_dbus_connection();
+	const char *path = device_get_path(dev);
+
+	switch (new_state) {
+	case AVCTP_STATE_DISCONNECTED:
+		control->session = NULL;
+		control->player = NULL;
+
+		g_dbus_emit_property_changed(conn, path,
+					AUDIO_CONTROL_INTERFACE, "Connected");
+		g_dbus_emit_property_changed(conn, path,
+					AUDIO_CONTROL_INTERFACE, "Player");
+
+		break;
+	case AVCTP_STATE_CONNECTING:
+		if (control->session)
+			break;
+
+		control->session = avctp_get(dev);
+
+		break;
+	case AVCTP_STATE_CONNECTED:
+		g_dbus_emit_property_changed(conn, path,
+					AUDIO_CONTROL_INTERFACE, "Connected");
+		break;
+	case AVCTP_STATE_BROWSING_CONNECTING:
+	case AVCTP_STATE_BROWSING_CONNECTED:
+	default:
+		return;
+	}
+}
+
+int control_connect(struct btd_service *service)
+{
+	struct control *control = btd_service_get_user_data(service);
+
+	if (control->session)
+		return -EALREADY;
+
+	control->session = avctp_connect(control->dev);
+	if (!control->session)
+		return -EIO;
+
+	return 0;
+}
+
+int control_disconnect(struct btd_service *service)
+{
+	struct control *control = btd_service_get_user_data(service);
+
+	if (!control->session)
+		return -ENOTCONN;
+
+	avctp_disconnect(control->session);
+
+	return 0;
+}
+
+static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
+						uint8_t op, void *data)
+{
+	struct control *control = data;
+	int err;
+
+	if (!control->session)
+		return btd_error_not_connected(msg);
+
+	if (!control->target)
+		return btd_error_not_supported(msg);
+
+	err = avctp_send_passthrough(control->session, op);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *control_volume_up(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_VOLUME_UP, data);
+}
+
+static DBusMessage *control_volume_down(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_VOLUME_DOWN, data);
+}
+
+static DBusMessage *control_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_PLAY, data);
+}
+
+static DBusMessage *control_pause(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_PAUSE, data);
+}
+
+static DBusMessage *control_stop(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_STOP, data);
+}
+
+static DBusMessage *control_next(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_FORWARD, data);
+}
+
+static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_BACKWARD, data);
+}
+
+static DBusMessage *control_fast_forward(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_FAST_FORWARD, data);
+}
+
+static DBusMessage *control_rewind(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_REWIND, data);
+}
+
+static gboolean control_property_get_connected(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct control *control = data;
+	dbus_bool_t value = (control->session != NULL);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean control_player_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct control *control = data;
+
+	return control->player != NULL;
+}
+
+static gboolean control_get_player(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct control *control = data;
+
+	if (!control->player)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&control->player);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable control_methods[] = {
+	{ GDBUS_DEPRECATED_METHOD("Play", NULL, NULL, control_play) },
+	{ GDBUS_DEPRECATED_METHOD("Pause", NULL, NULL, control_pause) },
+	{ GDBUS_DEPRECATED_METHOD("Stop", NULL, NULL, control_stop) },
+	{ GDBUS_DEPRECATED_METHOD("Next", NULL, NULL, control_next) },
+	{ GDBUS_DEPRECATED_METHOD("Previous", NULL, NULL, control_previous) },
+	{ GDBUS_DEPRECATED_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
+	{ GDBUS_DEPRECATED_METHOD("VolumeDown", NULL, NULL,
+							control_volume_down) },
+	{ GDBUS_DEPRECATED_METHOD("FastForward", NULL, NULL,
+							control_fast_forward) },
+	{ GDBUS_DEPRECATED_METHOD("Rewind", NULL, NULL, control_rewind) },
+	{ }
+};
+
+static const GDBusPropertyTable control_properties[] = {
+	{ "Connected", "b", control_property_get_connected },
+	{ "Player", "o", control_get_player, NULL, control_player_exists },
+	{ }
+};
+
+static void path_unregister(void *data)
+{
+	struct control *control = data;
+
+	DBG("Unregistered interface %s on path %s",  AUDIO_CONTROL_INTERFACE,
+						device_get_path(control->dev));
+
+	if (control->session)
+		avctp_disconnect(control->session);
+
+	avctp_remove_state_cb(control->avctp_id);
+
+	if (control->target)
+		btd_service_unref(control->target);
+
+	if (control->remote)
+		btd_service_unref(control->remote);
+
+	devices = g_slist_remove(devices, control);
+	g_free(control);
+}
+
+void control_unregister(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						device_get_path(dev),
+						AUDIO_CONTROL_INTERFACE);
+}
+
+static struct control *find_control(struct btd_device *dev)
+{
+	GSList *l;
+
+	for (l = devices; l; l = l->next) {
+		struct control *control = l->data;
+
+		if (control->dev == dev)
+			return control;
+	}
+
+	return NULL;
+}
+
+static struct control *control_init(struct btd_service *service)
+{
+	struct control *control;
+	struct btd_device *dev = btd_service_get_device(service);
+
+	control = find_control(dev);
+	if (control != NULL)
+		return control;
+
+	control = g_new0(struct control, 1);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					device_get_path(dev),
+					AUDIO_CONTROL_INTERFACE,
+					control_methods, NULL,
+					control_properties, control,
+					path_unregister)) {
+		g_free(control);
+		return NULL;
+	}
+
+	DBG("Registered interface %s on path %s", AUDIO_CONTROL_INTERFACE,
+							device_get_path(dev));
+
+	control->dev = dev;
+	control->avctp_id = avctp_add_state_cb(dev, state_changed, control);
+	devices = g_slist_prepend(devices, control);
+
+	return control;
+}
+
+int control_init_target(struct btd_service *service)
+{
+	struct control *control;
+
+	control = control_init(service);
+	if (control == NULL)
+		return -EINVAL;
+
+	control->target = btd_service_ref(service);
+
+	btd_service_set_user_data(service, control);
+
+	return 0;
+}
+
+int control_init_remote(struct btd_service *service)
+{
+	struct control *control;
+
+	control = control_init(service);
+	if (control == NULL)
+		return -EINVAL;
+
+	control->remote = btd_service_ref(service);
+
+	btd_service_set_user_data(service, control);
+
+	return 0;
+}
+
+int control_set_player(struct btd_service *service, const char *path)
+{
+	struct control *control = btd_service_get_user_data(service);
+
+	if (!control->session)
+		return -ENOTCONN;
+
+	if (g_strcmp0(control->player, path) == 0)
+		return -EALREADY;
+
+	control->player = path;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					device_get_path(control->dev),
+					AUDIO_CONTROL_INTERFACE, "Player");
+
+	return 0;
+}
diff --git a/bluez/repo/profiles/audio/control.h b/bluez/repo/profiles/audio/control.h
new file mode 100644
index 0000000..aab2631
--- /dev/null
+++ b/bluez/repo/profiles/audio/control.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+#define AUDIO_CONTROL_INTERFACE "org.bluez.MediaControl1"
+
+struct btd_service;
+
+int control_init_target(struct btd_service *service);
+int control_init_remote(struct btd_service *service);
+void control_unregister(struct btd_service *service);
+
+int control_connect(struct btd_service *service);
+int control_disconnect(struct btd_service *service);
+
+int control_set_player(struct btd_service *service, const char *path);
diff --git a/bluez/repo/profiles/audio/media.c b/bluez/repo/profiles/audio/media.c
new file mode 100644
index 0000000..69070bf
--- /dev/null
+++ b/bluez/repo/profiles/audio/media.c
@@ -0,0 +1,1942 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/dbus-common.h"
+#include "src/profile.h"
+
+#include "src/uuid-helper.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/shared/queue.h"
+
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "avrcp.h"
+
+#define MEDIA_INTERFACE "org.bluez.Media1"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+#define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
+
+#define REQUEST_TIMEOUT (3 * 1000)		/* 3 seconds */
+
+struct media_adapter {
+	struct btd_adapter	*btd_adapter;
+	GSList			*endpoints;	/* Endpoints list */
+	GSList			*players;	/* Players list */
+};
+
+struct endpoint_request {
+	struct media_endpoint	*endpoint;
+	DBusMessage		*msg;
+	DBusPendingCall		*call;
+	media_endpoint_cb_t	cb;
+	GDestroyNotify		destroy;
+	void			*user_data;
+};
+
+struct media_endpoint {
+	struct a2dp_sep		*sep;
+	char			*sender;	/* Endpoint DBus bus id */
+	char			*path;		/* Endpoint object path */
+	char			*uuid;		/* Endpoint property UUID */
+	uint8_t			codec;		/* Endpoint codec */
+	uint8_t			*capabilities;	/* Endpoint property capabilities */
+	size_t			size;		/* Endpoint capabilities size */
+	guint			hs_watch;
+	guint			ag_watch;
+	guint			watch;
+	GSList			*requests;
+	struct media_adapter	*adapter;
+	GSList			*transports;
+};
+
+struct media_player {
+	struct media_adapter	*adapter;
+	struct avrcp_player	*player;
+	char			*sender;	/* Player DBus bus id */
+	char			*path;		/* Player object path */
+	GHashTable		*settings;	/* Player settings */
+	GHashTable		*track;		/* Player current track */
+	guint			watch;
+	guint			properties_watch;
+	guint			seek_watch;
+	char			*status;
+	uint32_t		position;
+	uint32_t		duration;
+	uint8_t			volume;
+	GTimer			*timer;
+	bool			play;
+	bool			pause;
+	bool			next;
+	bool			previous;
+	bool			control;
+	char			*name;
+};
+
+static GSList *adapters = NULL;
+
+static void endpoint_request_free(struct endpoint_request *request)
+{
+	if (request->call)
+		dbus_pending_call_unref(request->call);
+
+	if (request->destroy)
+		request->destroy(request->user_data);
+
+	dbus_message_unref(request->msg);
+	g_free(request);
+}
+
+static void media_endpoint_cancel(struct endpoint_request *request)
+{
+	struct media_endpoint *endpoint = request->endpoint;
+
+	if (request->call)
+		dbus_pending_call_cancel(request->call);
+
+	endpoint->requests = g_slist_remove(endpoint->requests, request);
+
+	if (request->cb)
+		request->cb(endpoint, NULL, -1, request->user_data);
+
+	endpoint_request_free(request);
+}
+
+static void media_endpoint_cancel_all(struct media_endpoint *endpoint)
+{
+	while (endpoint->requests != NULL)
+		media_endpoint_cancel(endpoint->requests->data);
+}
+
+static void media_endpoint_destroy(struct media_endpoint *endpoint)
+{
+	DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+	media_endpoint_cancel_all(endpoint);
+
+	g_slist_free_full(endpoint->transports,
+				(GDestroyNotify) media_transport_destroy);
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), endpoint->watch);
+	g_free(endpoint->capabilities);
+	g_free(endpoint->sender);
+	g_free(endpoint->path);
+	g_free(endpoint->uuid);
+	g_free(endpoint);
+}
+
+static struct media_endpoint *media_adapter_find_endpoint(
+						struct media_adapter *adapter,
+						const char *sender,
+						const char *path,
+						const char *uuid)
+{
+	GSList *l;
+
+	for (l = adapter->endpoints; l; l = l->next) {
+		struct media_endpoint *endpoint = l->data;
+
+		if (sender && g_strcmp0(endpoint->sender, sender) != 0)
+			continue;
+
+		if (path && g_strcmp0(endpoint->path, path) != 0)
+			continue;
+
+		if (uuid && strcasecmp(endpoint->uuid, uuid) != 0)
+			continue;
+
+		return endpoint;
+	}
+
+	return NULL;
+}
+
+static void media_endpoint_remove(struct media_endpoint *endpoint)
+{
+	struct media_adapter *adapter = endpoint->adapter;
+
+	if (endpoint->sep) {
+		a2dp_remove_sep(endpoint->sep);
+		return;
+	}
+
+	info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
+			endpoint->path);
+
+	adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
+
+	if (media_adapter_find_endpoint(adapter, NULL, NULL,
+						endpoint->uuid) == NULL)
+		btd_profile_remove_custom_prop(endpoint->uuid,
+							"MediaEndpoints");
+
+	media_endpoint_destroy(endpoint);
+}
+
+static void media_endpoint_exit(DBusConnection *connection, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	endpoint->watch = 0;
+	media_endpoint_remove(endpoint);
+}
+
+static void clear_configuration(struct media_endpoint *endpoint,
+					struct media_transport *transport)
+{
+	DBusMessage *msg;
+	const char *path;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"ClearConfiguration");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		goto done;
+	}
+
+	path = media_transport_get_path(transport);
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+done:
+	endpoint->transports = g_slist_remove(endpoint->transports, transport);
+	media_transport_destroy(transport);
+}
+
+static void clear_endpoint(struct media_endpoint *endpoint)
+{
+	media_endpoint_cancel_all(endpoint);
+
+	while (endpoint->transports != NULL)
+		clear_configuration(endpoint, endpoint->transports->data);
+}
+
+static void endpoint_reply(DBusPendingCall *call, void *user_data)
+{
+	struct endpoint_request *request = user_data;
+	struct media_endpoint *endpoint = request->endpoint;
+	DBusMessage *reply;
+	DBusError err;
+	gboolean value;
+	void *ret = NULL;
+	int size = -1;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, reply)) {
+		error("Endpoint replied with an error: %s",
+				err.name);
+
+		/* Clear endpoint configuration in case of NO_REPLY error */
+		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+			clear_endpoint(endpoint);
+			dbus_message_unref(reply);
+			dbus_error_free(&err);
+			return;
+		}
+
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
+				"SelectConfiguration")) {
+		DBusMessageIter args, array;
+		uint8_t *configuration;
+
+		dbus_message_iter_init(reply, &args);
+
+		dbus_message_iter_recurse(&args, &array);
+
+		dbus_message_iter_get_fixed_array(&array, &configuration, &size);
+
+		ret = configuration;
+		goto done;
+	} else  if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
+		error("Wrong reply signature: %s", err.message);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	size = 1;
+	value = TRUE;
+	ret = &value;
+
+done:
+	dbus_message_unref(reply);
+
+	if (request->cb)
+		request->cb(endpoint, ret, size, request->user_data);
+
+	endpoint->requests = g_slist_remove(endpoint->requests, request);
+	endpoint_request_free(request);
+}
+
+static gboolean media_endpoint_async_call(DBusMessage *msg,
+					struct media_endpoint *endpoint,
+					media_endpoint_cb_t cb,
+					void *user_data,
+					GDestroyNotify destroy)
+{
+	struct endpoint_request *request;
+
+	request = g_new0(struct endpoint_request, 1);
+
+	/* Timeout should be less than avdtp request timeout (4 seconds) */
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(),
+						msg, &request->call,
+						REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		g_free(request);
+		return FALSE;
+	}
+
+	dbus_pending_call_set_notify(request->call, endpoint_reply, request,
+									NULL);
+
+	request->endpoint = endpoint;
+	request->msg = msg;
+	request->cb = cb;
+	request->destroy = destroy;
+	request->user_data = user_data;
+
+	endpoint->requests = g_slist_append(endpoint->requests, request);
+
+	DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+			dbus_message_get_destination(msg),
+			dbus_message_get_path(msg));
+
+	return TRUE;
+}
+
+static gboolean select_configuration(struct media_endpoint *endpoint,
+						uint8_t *capabilities,
+						size_t length,
+						media_endpoint_cb_t cb,
+						void *user_data,
+						GDestroyNotify destroy)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"SelectConfiguration");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return FALSE;
+	}
+
+	dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+					&capabilities, length,
+					DBUS_TYPE_INVALID);
+
+	return media_endpoint_async_call(msg, endpoint, cb, user_data, destroy);
+}
+
+static int transport_device_cmp(gconstpointer data, gconstpointer user_data)
+{
+	struct media_transport *transport = (struct media_transport *) data;
+	const struct btd_device *device = user_data;
+	const struct btd_device *dev = media_transport_get_dev(transport);
+
+	if (device == dev)
+		return 0;
+
+	return -1;
+}
+
+static struct media_transport *find_device_transport(
+					struct media_endpoint *endpoint,
+					struct btd_device *device)
+{
+	GSList *match;
+
+	match = g_slist_find_custom(endpoint->transports, device,
+							transport_device_cmp);
+	if (match == NULL)
+		return NULL;
+
+	return match->data;
+}
+
+struct a2dp_config_data {
+	struct a2dp_setup *setup;
+	a2dp_endpoint_config_t cb;
+};
+
+static gboolean set_configuration(struct media_endpoint *endpoint,
+					uint8_t *configuration, size_t size,
+					media_endpoint_cb_t cb,
+					void *user_data,
+					GDestroyNotify destroy)
+{
+	struct a2dp_config_data *data = user_data;
+	struct btd_device *device = a2dp_setup_get_device(data->setup);
+	DBusConnection *conn = btd_get_dbus_connection();
+	DBusMessage *msg;
+	const char *path;
+	DBusMessageIter iter;
+	struct media_transport *transport;
+
+	transport = find_device_transport(endpoint, device);
+
+	if (transport != NULL)
+		return FALSE;
+
+	transport = media_transport_create(device, configuration, size,
+								endpoint);
+	if (transport == NULL)
+		return FALSE;
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"SetConfiguration");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		media_transport_destroy(transport);
+		return FALSE;
+	}
+
+	endpoint->transports = g_slist_append(endpoint->transports, transport);
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	path = media_transport_get_path(transport);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	g_dbus_get_properties(conn, path, "org.bluez.MediaTransport1", &iter);
+
+	return media_endpoint_async_call(msg, endpoint, cb, user_data, destroy);
+}
+
+static void release_endpoint(struct media_endpoint *endpoint)
+{
+	DBusMessage *msg;
+
+	DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+	/* already exit */
+	if (endpoint->watch == 0)
+		goto done;
+
+	clear_endpoint(endpoint);
+
+	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+						MEDIA_ENDPOINT_INTERFACE,
+						"Release");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+done:
+	media_endpoint_remove(endpoint);
+}
+
+static const char *get_name(struct a2dp_sep *sep, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	return endpoint->sender;
+}
+
+static size_t get_capabilities(struct a2dp_sep *sep, uint8_t **capabilities,
+							void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	*capabilities = endpoint->capabilities;
+	return endpoint->size;
+}
+
+struct a2dp_select_data {
+	struct a2dp_setup *setup;
+	a2dp_endpoint_select_t cb;
+};
+
+static void select_cb(struct media_endpoint *endpoint, void *ret, int size,
+							void *user_data)
+{
+	struct a2dp_select_data *data = user_data;
+
+	data->cb(data->setup, ret, size);
+}
+
+static int select_config(struct a2dp_sep *sep, uint8_t *capabilities,
+				size_t length, struct a2dp_setup *setup,
+				a2dp_endpoint_select_t cb, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+	struct a2dp_select_data *data;
+
+	data = g_new0(struct a2dp_select_data, 1);
+	data->setup = setup;
+	data->cb = cb;
+
+	if (select_configuration(endpoint, capabilities, length,
+					select_cb, data, g_free) == TRUE)
+		return 0;
+
+	g_free(data);
+	return -ENOMEM;
+}
+
+static void config_cb(struct media_endpoint *endpoint, void *ret, int size,
+							void *user_data)
+{
+	struct a2dp_config_data *data = user_data;
+	gboolean *ret_value = ret;
+
+	data->cb(data->setup, ret_value ? *ret_value : FALSE);
+}
+
+static int set_config(struct a2dp_sep *sep, uint8_t *configuration,
+				size_t length,
+				struct a2dp_setup *setup,
+				a2dp_endpoint_config_t cb,
+				void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+	struct a2dp_config_data *data;
+
+	data = g_new0(struct a2dp_config_data, 1);
+	data->setup = setup;
+	data->cb = cb;
+
+	if (set_configuration(endpoint, configuration, length, config_cb, data,
+							g_free) == TRUE)
+		return 0;
+
+	g_free(data);
+	return -ENOMEM;
+}
+
+static void clear_config(struct a2dp_sep *sep, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	clear_endpoint(endpoint);
+}
+
+static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	if (endpoint->transports == NULL)
+		return;
+
+	media_transport_update_delay(endpoint->transports->data, delay);
+}
+
+static struct a2dp_endpoint a2dp_endpoint = {
+	.get_name = get_name,
+	.get_capabilities = get_capabilities,
+	.select_configuration = select_config,
+	.set_configuration = set_config,
+	.clear_configuration = clear_config,
+	.set_delay = set_delay
+};
+
+static void a2dp_destroy_endpoint(void *user_data)
+{
+	struct media_endpoint *endpoint = user_data;
+
+	endpoint->sep = NULL;
+	release_endpoint(endpoint);
+}
+
+static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
+						gboolean delay_reporting,
+						int *err)
+{
+	endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
+					AVDTP_SEP_TYPE_SOURCE, endpoint->codec,
+					delay_reporting, &a2dp_endpoint,
+					endpoint, a2dp_destroy_endpoint, err);
+	if (endpoint->sep == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
+						gboolean delay_reporting,
+						int *err)
+{
+	endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
+					AVDTP_SEP_TYPE_SINK, endpoint->codec,
+					delay_reporting, &a2dp_endpoint,
+					endpoint, a2dp_destroy_endpoint, err);
+	if (endpoint->sep == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static struct media_adapter *find_adapter(struct btd_device *device)
+{
+	GSList *l;
+
+	for (l = adapters; l; l = l->next) {
+		struct media_adapter *adapter = l->data;
+
+		if (adapter->btd_adapter == device_get_adapter(device))
+			return adapter;
+	}
+
+	return NULL;
+}
+
+static bool endpoint_properties_exists(const char *uuid,
+						struct btd_device *dev,
+						void *user_data)
+{
+	struct media_adapter *adapter;
+
+	adapter = find_adapter(dev);
+	if (adapter == NULL)
+		return false;
+
+	if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL)
+		return false;
+
+	return true;
+}
+
+static void append_endpoint(struct media_endpoint *endpoint,
+						DBusMessageIter *dict)
+{
+	DBusMessageIter entry, var, props;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+						&endpoint->sender);
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
+								&var);
+
+	dbus_message_iter_open_container(&var, 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,
+					&props);
+
+	dict_append_entry(&props, "Path", DBUS_TYPE_OBJECT_PATH,
+							&endpoint->path);
+	dict_append_entry(&props, "Codec", DBUS_TYPE_BYTE, &endpoint->codec);
+	dict_append_array(&props, "Capabilities", DBUS_TYPE_BYTE,
+				&endpoint->capabilities, endpoint->size);
+
+	dbus_message_iter_close_container(&var, &props);
+	dbus_message_iter_close_container(&entry, &var);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static bool endpoint_properties_get(const char *uuid,
+						struct btd_device *dev,
+						DBusMessageIter *iter,
+						void *user_data)
+{
+	struct media_adapter *adapter;
+	DBusMessageIter dict;
+	GSList *l;
+
+	adapter = find_adapter(dev);
+	if (adapter == NULL)
+		return false;
+
+	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);
+
+	for (l = adapter->endpoints; l; l = l->next) {
+		struct media_endpoint *endpoint = l->data;
+
+		if (strcasecmp(endpoint->uuid, uuid) != 0)
+			continue;
+
+		append_endpoint(endpoint, &dict);
+	}
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return true;
+}
+
+static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
+						const char *sender,
+						const char *path,
+						const char *uuid,
+						gboolean delay_reporting,
+						uint8_t codec,
+						uint8_t *capabilities,
+						int size,
+						int *err)
+{
+	struct media_endpoint *endpoint;
+	gboolean succeeded;
+
+	endpoint = g_new0(struct media_endpoint, 1);
+	endpoint->sender = g_strdup(sender);
+	endpoint->path = g_strdup(path);
+	endpoint->uuid = g_strdup(uuid);
+	endpoint->codec = codec;
+
+	if (size > 0) {
+		endpoint->capabilities = g_new(uint8_t, size);
+		memcpy(endpoint->capabilities, capabilities, size);
+		endpoint->size = size;
+	}
+
+	endpoint->adapter = adapter;
+
+	if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0)
+		succeeded = endpoint_init_a2dp_source(endpoint,
+							delay_reporting, err);
+	else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0)
+		succeeded = endpoint_init_a2dp_sink(endpoint,
+							delay_reporting, err);
+	else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+					strcasecmp(uuid, HSP_AG_UUID) == 0)
+		succeeded = TRUE;
+	else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
+					strcasecmp(uuid, HSP_HS_UUID) == 0)
+		succeeded = TRUE;
+	else {
+		succeeded = FALSE;
+
+		if (err)
+			*err = -EINVAL;
+	}
+
+	if (!succeeded) {
+		media_endpoint_destroy(endpoint);
+		return NULL;
+	}
+
+	endpoint->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+						sender, media_endpoint_exit,
+						endpoint, NULL);
+
+	if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL) {
+		btd_profile_add_custom_prop(uuid, "a{sv}", "MediaEndpoints",
+						endpoint_properties_exists,
+						endpoint_properties_get,
+						NULL);
+	}
+
+	adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
+	info("Endpoint registered: sender=%s path=%s", sender, path);
+
+	if (err)
+		*err = 0;
+	return endpoint;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+				gboolean *delay_reporting, uint8_t *codec,
+				uint8_t **capabilities, int *size)
+{
+	gboolean has_uuid = FALSE;
+	gboolean has_codec = FALSE;
+
+	while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+		int var;
+
+		dbus_message_iter_recurse(props, &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, "UUID") == 0) {
+			if (var != DBUS_TYPE_STRING)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, uuid);
+			has_uuid = TRUE;
+		} else if (strcasecmp(key, "Codec") == 0) {
+			if (var != DBUS_TYPE_BYTE)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, codec);
+			has_codec = TRUE;
+		} else if (strcasecmp(key, "DelayReporting") == 0) {
+			if (var != DBUS_TYPE_BOOLEAN)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, delay_reporting);
+		} else if (strcasecmp(key, "Capabilities") == 0) {
+			DBusMessageIter array;
+
+			if (var != DBUS_TYPE_ARRAY)
+				return -EINVAL;
+
+			dbus_message_iter_recurse(&value, &array);
+			dbus_message_iter_get_fixed_array(&array, capabilities,
+							size);
+		}
+
+		dbus_message_iter_next(props);
+	}
+
+	return (has_uuid && has_codec) ? 0 : -EINVAL;
+}
+
+static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_adapter *adapter = data;
+	DBusMessageIter args, props;
+	const char *sender, *path, *uuid;
+	gboolean delay_reporting = FALSE;
+	uint8_t codec;
+	uint8_t *capabilities;
+	int size = 0;
+	int err;
+
+	sender = dbus_message_get_sender(msg);
+
+	dbus_message_iter_init(msg, &args);
+
+	dbus_message_iter_get_basic(&args, &path);
+	dbus_message_iter_next(&args);
+
+	if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
+		return btd_error_already_exists(msg);
+
+	dbus_message_iter_recurse(&args, &props);
+	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+		return btd_error_invalid_args(msg);
+
+	if (parse_properties(&props, &uuid, &delay_reporting, &codec,
+						&capabilities, &size) < 0)
+		return btd_error_invalid_args(msg);
+
+	if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
+				codec, capabilities, size, &err) == NULL) {
+		if (err == -EPROTONOSUPPORT)
+			return btd_error_not_supported(msg);
+		else
+			return btd_error_invalid_args(msg);
+	}
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_adapter *adapter = data;
+	struct media_endpoint *endpoint;
+	const char *sender, *path;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	sender = dbus_message_get_sender(msg);
+
+	endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
+	if (endpoint == NULL)
+		return btd_error_does_not_exist(msg);
+
+	media_endpoint_remove(endpoint);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static struct media_player *media_adapter_find_player(
+						struct media_adapter *adapter,
+						const char *sender,
+						const char *path)
+{
+	GSList *l;
+
+	for (l = adapter->players; l; l = l->next) {
+		struct media_player *mp = l->data;
+
+		if (sender && g_strcmp0(mp->sender, sender) != 0)
+			continue;
+
+		if (path && g_strcmp0(mp->path, path) != 0)
+			continue;
+
+		return mp;
+	}
+
+	return NULL;
+}
+
+static void release_player(struct media_player *mp)
+{
+	DBusMessage *msg;
+
+	DBG("sender=%s path=%s", mp->sender, mp->path);
+
+	msg = dbus_message_new_method_call(mp->sender, mp->path,
+						MEDIA_PLAYER_INTERFACE,
+						"Release");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void media_player_free(gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct media_player *mp = data;
+	struct media_adapter *adapter = mp->adapter;
+
+	if (mp->player) {
+		adapter->players = g_slist_remove(adapter->players, mp);
+		release_player(mp);
+	}
+
+	g_dbus_remove_watch(conn, mp->watch);
+	g_dbus_remove_watch(conn, mp->properties_watch);
+	g_dbus_remove_watch(conn, mp->seek_watch);
+
+	if (mp->track)
+		g_hash_table_unref(mp->track);
+
+	if (mp->settings)
+		g_hash_table_unref(mp->settings);
+
+	g_timer_destroy(mp->timer);
+	g_free(mp->sender);
+	g_free(mp->path);
+	g_free(mp->status);
+	g_free(mp->name);
+	g_free(mp);
+}
+
+static void media_player_destroy(struct media_player *mp)
+{
+	struct media_adapter *adapter = mp->adapter;
+
+	DBG("sender=%s path=%s", mp->sender, mp->path);
+
+	if (mp->player) {
+		struct avrcp_player *player = mp->player;
+		mp->player = NULL;
+		adapter->players = g_slist_remove(adapter->players, mp);
+		avrcp_unregister_player(player);
+		return;
+	}
+
+	media_player_free(mp);
+}
+
+static void media_player_remove(struct media_player *mp)
+{
+	info("Player unregistered: sender=%s path=%s", mp->sender, mp->path);
+
+	media_player_destroy(mp);
+}
+
+static GList *list_settings(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (mp->settings == NULL)
+		return NULL;
+
+	return g_hash_table_get_keys(mp->settings);
+}
+
+static const char *get_setting(const char *key, void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("%s", key);
+
+	return g_hash_table_lookup(mp->settings, key);
+}
+
+static const char *get_player_name(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	return mp->name;
+}
+
+static void set_shuffle_setting(DBusMessageIter *iter, const char *value)
+{
+	const char *key = "Shuffle";
+	dbus_bool_t val;
+	DBusMessageIter var;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+						DBUS_TYPE_BOOLEAN_AS_STRING,
+						&var);
+	val = strcasecmp(value, "off") != 0;
+	dbus_message_iter_append_basic(&var, DBUS_TYPE_BOOLEAN, &val);
+	dbus_message_iter_close_container(iter, &var);
+}
+
+static const char *repeat_to_loop_status(const char *value)
+{
+	if (strcasecmp(value, "off") == 0)
+		return "None";
+	else if (strcasecmp(value, "singletrack") == 0)
+		return "Track";
+	else if (strcasecmp(value, "alltracks") == 0)
+		return "Playlist";
+	else if (strcasecmp(value, "group") == 0)
+		return "Playlist";
+
+	return NULL;
+}
+
+static void set_repeat_setting(DBusMessageIter *iter, const char *value)
+{
+	const char *key = "LoopStatus";
+	const char *val;
+	DBusMessageIter var;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+						DBUS_TYPE_STRING_AS_STRING,
+						&var);
+	val = repeat_to_loop_status(value);
+	dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &val);
+	dbus_message_iter_close_container(iter, &var);
+}
+
+static int set_setting(const char *key, const char *value, void *user_data)
+{
+	struct media_player *mp = user_data;
+	const char *iface = MEDIA_PLAYER_INTERFACE;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	const char *curval;
+
+	DBG("%s = %s", key, value);
+
+	curval = g_hash_table_lookup(mp->settings, key);
+	if (!curval)
+		return -EINVAL;
+
+	if (strcasecmp(curval, value) == 0)
+		return 0;
+
+	msg = dbus_message_new_method_call(mp->sender, mp->path,
+					DBUS_INTERFACE_PROPERTIES, "Set");
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface);
+
+	if (strcasecmp(key, "Shuffle") == 0)
+		set_shuffle_setting(&iter, value);
+	else if (strcasecmp(key, "Repeat") == 0)
+		set_repeat_setting(&iter, value);
+
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+	return 0;
+}
+
+static GList *list_metadata(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (mp->track == NULL)
+		return NULL;
+
+	return g_hash_table_get_keys(mp->track);
+}
+
+static uint64_t get_uid(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("%p", mp->track);
+
+	if (mp->track == NULL)
+		return UINT64_MAX;
+
+	return 0;
+}
+
+static const char *get_metadata(const char *key, void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("%s", key);
+
+	if (mp->track == NULL)
+		return NULL;
+
+	return g_hash_table_lookup(mp->track, key);
+}
+
+static const char *get_status(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	return mp->status;
+}
+
+static uint32_t get_position(void *user_data)
+{
+	struct media_player *mp = user_data;
+	double timedelta;
+	uint32_t sec, msec;
+
+	if (mp->status == NULL || strcasecmp(mp->status, "Playing") != 0)
+		return mp->position;
+
+	timedelta = g_timer_elapsed(mp->timer, NULL);
+
+	sec = (uint32_t) timedelta;
+	msec = (uint32_t) ((timedelta - sec) * 1000);
+
+	return mp->position + sec * 1000 + msec;
+}
+
+static uint32_t get_duration(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	return mp->duration;
+}
+
+static void set_volume(uint8_t volume, struct btd_device *dev, void *user_data)
+{
+	struct media_player *mp = user_data;
+	GSList *l;
+
+	if (mp->volume == volume)
+		return;
+
+	mp->volume = volume;
+
+	for (l = mp->adapter->endpoints; l; l = l->next) {
+		struct media_endpoint *endpoint = l->data;
+		struct media_transport *transport;
+
+		/* Volume is A2DP only */
+		if (endpoint->sep == NULL)
+			continue;
+
+		transport = find_device_transport(endpoint, dev);
+		if (transport == NULL)
+			continue;
+
+		media_transport_update_volume(transport, volume);
+	}
+}
+
+static bool media_player_send(struct media_player *mp, const char *name)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(mp->sender, mp->path,
+					MEDIA_PLAYER_INTERFACE, name);
+	if (msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return false;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+	return true;
+}
+
+static bool play(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (!mp->play || !mp->control)
+		return false;
+
+	return media_player_send(mp, "Play");
+}
+
+static bool stop(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (!mp->control)
+		return false;
+
+	return media_player_send(mp, "Stop");
+}
+
+static bool pause(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (!mp->pause || !mp->control)
+		return false;
+
+	return media_player_send(mp, "Pause");
+}
+
+static bool next(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (!mp->next || !mp->control)
+		return false;
+
+	return media_player_send(mp, "Next");
+}
+
+static bool previous(void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	DBG("");
+
+	if (!mp->previous || !mp->control)
+		return false;
+
+	return media_player_send(mp, "Previous");
+}
+
+static struct avrcp_player_cb player_cb = {
+	.list_settings = list_settings,
+	.get_setting = get_setting,
+	.set_setting = set_setting,
+	.list_metadata = list_metadata,
+	.get_uid = get_uid,
+	.get_metadata = get_metadata,
+	.get_position = get_position,
+	.get_duration = get_duration,
+	.get_status = get_status,
+	.get_name = get_player_name,
+	.set_volume = set_volume,
+	.play = play,
+	.stop = stop,
+	.pause = pause,
+	.next = next,
+	.previous = previous,
+};
+
+static void media_player_exit(DBusConnection *connection, void *user_data)
+{
+	struct media_player *mp = user_data;
+
+	mp->watch = 0;
+	media_player_remove(mp);
+}
+
+static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+	DBG("Status=%s", value);
+
+	if (g_strcmp0(mp->status, value) == 0)
+		return TRUE;
+
+	mp->position = get_position(mp);
+	g_timer_start(mp->timer);
+
+	g_free(mp->status);
+	mp->status = g_strdup(value);
+
+	avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
+
+	return TRUE;
+}
+
+static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
+{
+	uint64_t value;
+	const char *status;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64)
+			return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	value /= 1000;
+
+	if (value > get_position(mp))
+		status = "forward-seek";
+	else
+		status = "reverse-seek";
+
+	mp->position = value;
+	g_timer_start(mp->timer);
+
+	DBG("Position=%u", mp->position);
+
+	if (!mp->position) {
+		avrcp_player_event(mp->player,
+					AVRCP_EVENT_TRACK_REACHED_START, NULL);
+		return TRUE;
+	}
+
+	/*
+	 * If position is the maximum value allowed or greater than track's
+	 * duration, we send a track-reached-end event.
+	 */
+	if (mp->position == UINT32_MAX || mp->position >= mp->duration) {
+		avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
+									NULL);
+		return TRUE;
+	}
+
+	/* Send a status change to force resync the position */
+	avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status);
+
+	return TRUE;
+}
+
+static void set_metadata(struct media_player *mp, const char *key,
+							const char *value)
+{
+	DBG("%s=%s", key, value);
+	g_hash_table_replace(mp->track, g_strdup(key), g_strdup(value));
+}
+
+static gboolean parse_string_metadata(struct media_player *mp, const char *key,
+							DBusMessageIter *iter)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	set_metadata(mp, key, value);
+
+	return TRUE;
+}
+
+static gboolean parse_array_metadata(struct media_player *mp, const char *key,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter array;
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	dbus_message_iter_recurse(iter, &array);
+
+	if (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_INVALID)
+		return TRUE;
+
+	if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(&array, &value);
+
+	set_metadata(mp, key, value);
+
+	return TRUE;
+}
+
+static gboolean parse_int64_metadata(struct media_player *mp, const char *key,
+							DBusMessageIter *iter)
+{
+	uint64_t value;
+	char valstr[20];
+	int type;
+
+	type = dbus_message_iter_get_arg_type(iter);
+	if (type == DBUS_TYPE_UINT64)
+		warn("expected DBUS_TYPE_INT64 got DBUS_TYPE_UINT64");
+	else if (type != DBUS_TYPE_INT64)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	if (strcasecmp(key, "Duration") == 0) {
+		value /= 1000;
+		mp->duration = value;
+	}
+
+	snprintf(valstr, 20, "%" PRIu64, value);
+
+	set_metadata(mp, key, valstr);
+
+	return TRUE;
+}
+
+static gboolean parse_int32_metadata(struct media_player *mp, const char *key,
+							DBusMessageIter *iter)
+{
+	uint32_t value;
+	char valstr[20];
+	int type;
+
+	type = dbus_message_iter_get_arg_type(iter);
+	if (type == DBUS_TYPE_UINT32)
+		warn("expected DBUS_TYPE_INT32 got DBUS_TYPE_UINT32");
+	else if (type != DBUS_TYPE_INT32)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	snprintf(valstr, 20, "%u", value);
+
+	set_metadata(mp, key, valstr);
+
+	return TRUE;
+}
+
+static gboolean parse_player_metadata(struct media_player *mp,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter dict;
+	DBusMessageIter var;
+	int ctype;
+	gboolean title = FALSE;
+	uint64_t uid;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	if (mp->track != NULL)
+		g_hash_table_unref(mp->track);
+
+	mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+								g_free);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		DBusMessageIter entry;
+		const char *key;
+
+		if (ctype != DBUS_TYPE_DICT_ENTRY)
+			return FALSE;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			return FALSE;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+			return FALSE;
+
+		dbus_message_iter_recurse(&entry, &var);
+
+		if (strcasecmp(key, "xesam:title") == 0) {
+			if (!parse_string_metadata(mp, "Title", &var))
+				return FALSE;
+			title = TRUE;
+		} else if (strcasecmp(key, "xesam:artist") == 0) {
+			if (!parse_array_metadata(mp, "Artist", &var))
+				return FALSE;
+		} else if (strcasecmp(key, "xesam:album") == 0) {
+			if (!parse_string_metadata(mp, "Album", &var))
+				return FALSE;
+		} else if (strcasecmp(key, "xesam:genre") == 0) {
+			if (!parse_array_metadata(mp, "Genre", &var))
+				return FALSE;
+		} else if (strcasecmp(key, "mpris:length") == 0) {
+			if (!parse_int64_metadata(mp, "Duration", &var))
+				return FALSE;
+		} else if (strcasecmp(key, "xesam:trackNumber") == 0) {
+			if (!parse_int32_metadata(mp, "TrackNumber", &var))
+				return FALSE;
+		} else
+			DBG("%s not supported, ignoring", key);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (title == FALSE)
+		g_hash_table_insert(mp->track, g_strdup("Title"),
+								g_strdup(""));
+
+	mp->position = 0;
+	g_timer_start(mp->timer);
+	uid = get_uid(mp);
+
+	avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid);
+	avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL);
+
+	return TRUE;
+}
+
+static gboolean set_property(struct media_player *mp, const char *key,
+							const char *value)
+{
+	const char *curval;
+
+	curval = g_hash_table_lookup(mp->settings, key);
+	if (g_strcmp0(curval, value) == 0)
+		return TRUE;
+
+	DBG("%s=%s", key, value);
+
+	g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
+
+	avrcp_player_event(mp->player, AVRCP_EVENT_SETTINGS_CHANGED, key);
+
+	return TRUE;
+}
+
+static gboolean set_shuffle(struct media_player *mp, DBusMessageIter *iter)
+{
+	dbus_bool_t value;
+	const char *strvalue;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	strvalue = value ? "alltracks" : "off";
+
+	return set_property(mp, "Shuffle", strvalue);
+}
+
+static const char *loop_status_to_repeat(const char *value)
+{
+	if (strcasecmp(value, "None") == 0)
+		return "off";
+	else if (strcasecmp(value, "Track") == 0)
+		return "singletrack";
+	else if (strcasecmp(value, "Playlist") == 0)
+		return "alltracks";
+
+	return NULL;
+}
+
+static gboolean set_repeat(struct media_player *mp, DBusMessageIter *iter)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	value = loop_status_to_repeat(value);
+	if (value == NULL)
+		return FALSE;
+
+	return set_property(mp, "Repeat", value);
+}
+
+static gboolean set_flag(struct media_player *mp, DBusMessageIter *iter,
+								bool *var)
+{
+	dbus_bool_t value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	*var = value;
+
+	return TRUE;
+}
+
+static gboolean set_name(struct media_player *mp, DBusMessageIter *iter)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	if (g_strcmp0(mp->name, value) == 0)
+		return TRUE;
+
+	g_free(mp->name);
+
+	mp->name = g_strdup(value);
+
+	return TRUE;
+}
+
+static gboolean set_player_property(struct media_player *mp, const char *key,
+							DBusMessageIter *entry)
+{
+	DBusMessageIter var;
+
+	if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+		return FALSE;
+
+	dbus_message_iter_recurse(entry, &var);
+
+	if (strcasecmp(key, "PlaybackStatus") == 0)
+		return set_status(mp, &var);
+
+	if (strcasecmp(key, "Position") == 0)
+		return set_position(mp, &var);
+
+	if (strcasecmp(key, "Metadata") == 0)
+		return parse_player_metadata(mp, &var);
+
+	if (strcasecmp(key, "Shuffle") == 0)
+		return set_shuffle(mp, &var);
+
+	if (strcasecmp(key, "LoopStatus") == 0)
+		return set_repeat(mp, &var);
+
+	if (strcasecmp(key, "CanPlay") == 0)
+		return set_flag(mp, &var, &mp->play);
+
+	if (strcasecmp(key, "CanPause") == 0)
+		return set_flag(mp, &var, &mp->pause);
+
+	if (strcasecmp(key, "CanGoNext") == 0)
+		return set_flag(mp, &var, &mp->next);
+
+	if (strcasecmp(key, "CanGoPrevious") == 0)
+		return set_flag(mp, &var, &mp->previous);
+
+	if (strcasecmp(key, "CanControl") == 0)
+		return set_flag(mp, &var, &mp->control);
+
+	if (strcasecmp(key, "Identity") == 0)
+		return set_name(mp, &var);
+
+	DBG("%s not supported, ignoring", key);
+
+	return TRUE;
+}
+
+static gboolean parse_player_properties(struct media_player *mp,
+							DBusMessageIter *iter)
+{
+	DBusMessageIter dict;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		DBusMessageIter entry;
+		const char *key;
+
+		if (ctype != DBUS_TYPE_DICT_ENTRY)
+			return FALSE;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			return FALSE;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		if (set_player_property(mp, key, &entry) == FALSE)
+			return FALSE;
+
+		dbus_message_iter_next(&dict);
+	}
+
+	return TRUE;
+}
+
+static gboolean properties_changed(DBusConnection *connection, DBusMessage *msg,
+							void *user_data)
+{
+	struct media_player *mp = user_data;
+	DBusMessageIter iter;
+
+	DBG("sender=%s path=%s", mp->sender, mp->path);
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_next(&iter);
+
+	parse_player_properties(mp, &iter);
+
+	return TRUE;
+}
+
+static gboolean position_changed(DBusConnection *connection, DBusMessage *msg,
+							void *user_data)
+{
+	struct media_player *mp = user_data;
+	DBusMessageIter iter;
+
+	DBG("sender=%s path=%s", mp->sender, mp->path);
+
+	dbus_message_iter_init(msg, &iter);
+
+	set_position(mp, &iter);
+
+	return TRUE;
+}
+
+static struct media_player *media_player_create(struct media_adapter *adapter,
+						const char *sender,
+						const char *path,
+						int *err)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct media_player *mp;
+
+	mp = g_new0(struct media_player, 1);
+	mp->adapter = adapter;
+	mp->sender = g_strdup(sender);
+	mp->path = g_strdup(path);
+	mp->timer = g_timer_new();
+
+	mp->watch = g_dbus_add_disconnect_watch(conn, sender,
+						media_player_exit, mp,
+						NULL);
+	mp->properties_watch = g_dbus_add_properties_watch(conn, sender,
+						path, MEDIA_PLAYER_INTERFACE,
+						properties_changed,
+						mp, NULL);
+	mp->seek_watch = g_dbus_add_signal_watch(conn, sender,
+						path, MEDIA_PLAYER_INTERFACE,
+						"Seeked", position_changed,
+						mp, NULL);
+	mp->player = avrcp_register_player(adapter->btd_adapter, &player_cb,
+							mp, media_player_free);
+	if (!mp->player) {
+		if (err)
+			*err = -EPROTONOSUPPORT;
+		media_player_destroy(mp);
+		return NULL;
+	}
+
+	mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+								g_free);
+
+	adapter->players = g_slist_append(adapter->players, mp);
+
+	info("Player registered: sender=%s path=%s", sender, path);
+
+	if (err)
+		*err = 0;
+
+	return mp;
+}
+
+static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_adapter *adapter = data;
+	struct media_player *mp;
+	DBusMessageIter args;
+	const char *sender, *path;
+	int err;
+
+	sender = dbus_message_get_sender(msg);
+
+	dbus_message_iter_init(msg, &args);
+
+	dbus_message_iter_get_basic(&args, &path);
+	dbus_message_iter_next(&args);
+
+	if (media_adapter_find_player(adapter, sender, path) != NULL)
+		return btd_error_already_exists(msg);
+
+	mp = media_player_create(adapter, sender, path, &err);
+	if (mp == NULL) {
+		if (err == -EPROTONOSUPPORT)
+			return btd_error_not_supported(msg);
+		else
+			return btd_error_invalid_args(msg);
+	}
+
+	if (parse_player_properties(mp, &args) == FALSE) {
+		media_player_destroy(mp);
+		return btd_error_invalid_args(msg);
+	}
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_adapter *adapter = data;
+	struct media_player *player;
+	const char *sender, *path;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	sender = dbus_message_get_sender(msg);
+
+	player = media_adapter_find_player(adapter, sender, path);
+	if (player == NULL)
+		return btd_error_does_not_exist(msg);
+
+	media_player_remove(player);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable media_methods[] = {
+	{ GDBUS_METHOD("RegisterEndpoint",
+		GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" }),
+		NULL, register_endpoint) },
+	{ GDBUS_METHOD("UnregisterEndpoint",
+		GDBUS_ARGS({ "endpoint", "o" }), NULL, unregister_endpoint) },
+	{ GDBUS_METHOD("RegisterPlayer",
+		GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" }),
+		NULL, register_player) },
+	{ GDBUS_METHOD("UnregisterPlayer",
+		GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
+	{ },
+};
+
+static void path_free(void *data)
+{
+	struct media_adapter *adapter = data;
+
+	while (adapter->endpoints)
+		release_endpoint(adapter->endpoints->data);
+
+	while (adapter->players)
+		media_player_destroy(adapter->players->data);
+
+	adapters = g_slist_remove(adapters, adapter);
+
+	btd_adapter_unref(adapter->btd_adapter);
+	g_free(adapter);
+}
+
+int media_register(struct btd_adapter *btd_adapter)
+{
+	struct media_adapter *adapter;
+
+	adapter = g_new0(struct media_adapter, 1);
+	adapter->btd_adapter = btd_adapter_ref(btd_adapter);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					adapter_get_path(btd_adapter),
+					MEDIA_INTERFACE,
+					media_methods, NULL, NULL,
+					adapter, path_free)) {
+		error("D-Bus failed to register %s path",
+						adapter_get_path(btd_adapter));
+		path_free(adapter);
+		return -1;
+	}
+
+	adapters = g_slist_append(adapters, adapter);
+
+	return 0;
+}
+
+void media_unregister(struct btd_adapter *btd_adapter)
+{
+	GSList *l;
+
+	for (l = adapters; l; l = l->next) {
+		struct media_adapter *adapter = l->data;
+
+		if (adapter->btd_adapter == btd_adapter) {
+			g_dbus_unregister_interface(btd_get_dbus_connection(),
+						adapter_get_path(btd_adapter),
+						MEDIA_INTERFACE);
+			return;
+		}
+	}
+}
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
+{
+	return endpoint->sep;
+}
+
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
+{
+	return endpoint->uuid;
+}
+
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
+{
+	return endpoint->codec;
+}
diff --git a/bluez/repo/profiles/audio/media.h b/bluez/repo/profiles/audio/media.h
new file mode 100644
index 0000000..dd630d4
--- /dev/null
+++ b/bluez/repo/profiles/audio/media.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  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
+ *
+ */
+
+struct media_endpoint;
+
+typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint,
+					void *ret, int size, void *user_data);
+
+int media_register(struct btd_adapter *btd_adapter);
+void media_unregister(struct btd_adapter *btd_adapter);
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
diff --git a/bluez/repo/profiles/audio/player.c b/bluez/repo/profiles/audio/player.c
new file mode 100644
index 0000000..4736396
--- /dev/null
+++ b/bluez/repo/profiles/audio/player.c
@@ -0,0 +1,1885 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2012-2012  Intel Corporation
+ *
+ *  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 <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
+
+#include "player.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
+#define MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
+
+struct player_callback {
+	const struct media_player_callback *cbs;
+	void *user_data;
+};
+
+struct pending_req {
+	GDBusPendingPropertySet id;
+	const char *key;
+	const char *value;
+};
+
+struct media_item {
+	struct media_player	*player;
+	char			*path;		/* Item object path */
+	char			*name;		/* Item name */
+	player_item_type_t	type;		/* Item type */
+	player_folder_type_t	folder_type;	/* Folder type */
+	bool			playable;	/* Item playable flag */
+	uint64_t		uid;		/* Item uid */
+	GHashTable		*metadata;	/* Item metadata */
+};
+
+struct media_folder {
+	struct media_folder	*parent;
+	struct media_item	*item;		/* Folder item */
+	uint32_t		number_of_items;/* Number of items */
+	GSList			*subfolders;
+	GSList			*items;
+	DBusMessage		*msg;
+};
+
+struct media_player {
+	char			*device;	/* Device path */
+	char			*name;		/* Player name */
+	char			*type;		/* Player type */
+	char			*subtype;	/* Player subtype */
+	bool			browsable;	/* Player browsing feature */
+	bool			searchable;	/* Player searching feature */
+	struct media_folder	*scope;		/* Player current scope */
+	struct media_folder	*folder;	/* Player current folder */
+	struct media_folder	*search;	/* Player search folder */
+	struct media_folder	*playlist;	/* Player current playlist */
+	char			*path;		/* Player object path */
+	GHashTable		*settings;	/* Player settings */
+	GHashTable		*track;		/* Player current track */
+	char			*status;
+	uint32_t		position;
+	GTimer			*progress;
+	guint			process_id;
+	struct player_callback	*cb;
+	GSList			*pending;
+	GSList			*folders;
+};
+
+static void append_track(void *key, void *value, void *user_data)
+{
+	DBusMessageIter *dict = user_data;
+	const char *strkey = key;
+
+	if (strcasecmp(strkey, "Duration") == 0 ||
+			strcasecmp(strkey, "TrackNumber") == 0 ||
+			strcasecmp(strkey, "NumberOfTracks") == 0)  {
+		uint32_t num = atoi(value);
+		dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+	} else if (strcasecmp(strkey, "Item") == 0) {
+		dict_append_entry(dict, key, DBUS_TYPE_OBJECT_PATH, &value);
+	} else {
+		dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+	}
+}
+
+static struct pending_req *find_pending(struct media_player *mp,
+							const char *key)
+{
+	GSList *l;
+
+	for (l = mp->pending; l; l = l->next) {
+		struct pending_req *p = l->data;
+
+		if (strcasecmp(key, p->key) == 0)
+			return p;
+	}
+
+	return NULL;
+}
+
+static struct pending_req *pending_new(GDBusPendingPropertySet id,
+					const char *key, const char *value)
+{
+	struct pending_req *p;
+
+	p = g_new0(struct pending_req, 1);
+	p->id = id;
+	p->key = key;
+	p->value = value;
+
+	return p;
+}
+
+static uint32_t media_player_get_position(struct media_player *mp)
+{
+	double timedelta;
+	uint32_t sec, msec;
+
+	if (g_strcmp0(mp->status, "playing") != 0 ||
+						mp->position == UINT32_MAX)
+		return mp->position;
+
+	timedelta = g_timer_elapsed(mp->progress, NULL);
+
+	sec = (uint32_t) timedelta;
+	msec = (uint32_t) ((timedelta - sec) * 1000);
+
+	return mp->position + sec * 1000 + msec;
+}
+
+static gboolean get_position(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	uint32_t position;
+
+	position = media_player_get_position(mp);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &position);
+
+	return TRUE;
+}
+
+static gboolean status_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->status != NULL;
+}
+
+static gboolean get_status(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->status == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->status);
+
+	return TRUE;
+}
+
+static gboolean setting_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+	const char *value;
+
+	value = g_hash_table_lookup(mp->settings, property->name);
+
+	return value ? TRUE : FALSE;
+}
+
+static gboolean get_setting(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	const char *value;
+
+	value = g_hash_table_lookup(mp->settings, property->name);
+	if (value == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value);
+
+	return TRUE;
+}
+
+static void player_set_setting(struct media_player *mp,
+					GDBusPendingPropertySet id,
+					const char *key, const char *value)
+{
+	struct player_callback *cb = mp->cb;
+	struct pending_req *p;
+
+	if (cb == NULL || cb->cbs->set_setting == NULL) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".NotSupported",
+					"Operation is not supported");
+		return;
+	}
+
+	p = find_pending(mp, key);
+	if (p != NULL) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InProgress",
+					"Operation already in progress");
+		return;
+	}
+
+	if (!cb->cbs->set_setting(mp, key, value, cb->user_data)) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	p = pending_new(id, key, value);
+
+	mp->pending = g_slist_append(mp->pending, p);
+}
+
+static void set_setting(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct media_player *mp = data;
+	const char *value, *current;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	current = g_hash_table_lookup(mp->settings, property->name);
+	if (g_strcmp0(current, value) == 0) {
+		g_dbus_pending_property_success(id);
+		return;
+	}
+
+	player_set_setting(mp, id, property->name, value);
+}
+
+static gboolean track_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return g_hash_table_size(mp->track) != 0;
+}
+
+static gboolean get_track(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = 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);
+
+	g_hash_table_foreach(mp->track, append_track, &dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static gboolean get_device(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+								&mp->device);
+
+	return TRUE;
+}
+
+static gboolean name_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->name != NULL;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->name == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->name);
+
+	return TRUE;
+}
+
+static gboolean type_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->type != NULL;
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->type == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->type);
+
+	return TRUE;
+}
+
+static gboolean subtype_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->subtype != NULL;
+}
+
+static gboolean get_subtype(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+
+	if (mp->subtype == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->subtype);
+
+	return TRUE;
+}
+
+static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->folder != NULL;
+}
+
+static gboolean get_browsable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	dbus_bool_t value;
+
+	if (mp->folder == NULL)
+		return FALSE;
+
+	value = mp->browsable;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean searchable_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->folder != NULL;
+}
+
+static gboolean get_searchable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	dbus_bool_t value;
+
+	if (mp->folder == NULL)
+		return FALSE;
+
+	value = mp->searchable;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean playlist_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->playlist != NULL;
+}
+
+static gboolean get_playlist(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *playlist = mp->playlist;
+
+	if (playlist == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&playlist->item->path);
+
+	return TRUE;
+}
+
+static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->play == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->play(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_pause(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->pause == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->pause(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_stop(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->stop == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->stop(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_next(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->next == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->next(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_previous(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->previous == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->previous(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_fast_forward(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->fast_forward == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->fast_forward(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (cb->cbs->rewind == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->rewind(mp, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void parse_folder_list(gpointer data, gpointer user_data)
+{
+	struct media_item *item = data;
+	DBusMessageIter *array = user_data;
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+								&entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+								&item->path);
+
+	g_dbus_get_properties(btd_get_dbus_connection(), item->path,
+						MEDIA_ITEM_INTERFACE, &entry);
+
+	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)
+{
+	struct media_folder *folder = mp->scope;
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+
+	if (folder == NULL || folder->msg == NULL)
+		return;
+
+	if (err < 0) {
+		reply = btd_error_failed(folder->msg, strerror(-err));
+		goto done;
+	}
+
+	reply = dbus_message_new_method_return(folder->msg);
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_OBJECT_PATH_AS_STRING
+					DBUS_TYPE_ARRAY_AS_STRING
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&array);
+
+	g_slist_foreach(items, parse_folder_list, &array);
+	dbus_message_iter_close_container(&iter, &array);
+
+done:
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	dbus_message_unref(folder->msg);
+	folder->msg = NULL;
+}
+
+static struct media_item *
+media_player_create_subfolder(struct media_player *mp, const char *name,
+								uint64_t uid)
+{
+	struct media_folder *folder = mp->scope;
+	struct media_item *item;
+	char *path;
+
+	path = g_strdup_printf("%s/%s", folder->item->name, name);
+
+	DBG("%s", path);
+
+	item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER,
+									uid);
+	g_free(path);
+
+	return item;
+}
+
+void media_player_search_complete(struct media_player *mp, int ret)
+{
+	struct media_folder *folder = mp->scope;
+	struct media_folder *search = mp->search;
+	DBusMessage *reply;
+
+	if (folder == NULL || folder->msg == NULL)
+		return;
+
+	if (ret < 0) {
+		reply = btd_error_failed(folder->msg, strerror(-ret));
+		goto done;
+	}
+
+	if (search == NULL) {
+		search = g_new0(struct media_folder, 1);
+		search->item = media_player_create_subfolder(mp, "search", 0);
+		mp->search = search;
+		mp->folders = g_slist_prepend(mp->folders, search);
+	}
+
+	search->number_of_items = ret;
+
+	reply = g_dbus_create_reply(folder->msg,
+				DBUS_TYPE_OBJECT_PATH, &search->item->path,
+				DBUS_TYPE_INVALID);
+
+done:
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	dbus_message_unref(folder->msg);
+	folder->msg = NULL;
+}
+
+void media_player_total_items_complete(struct media_player *mp,
+							uint32_t num_of_items)
+{
+	struct media_folder *folder = mp->scope;
+
+	if (folder == NULL || folder->msg == NULL)
+		return;
+
+	if (folder->number_of_items != num_of_items) {
+		folder->number_of_items = num_of_items;
+
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				mp->path, MEDIA_FOLDER_INTERFACE,
+				"NumberOfItems");
+	}
+}
+
+static const GDBusMethodTable media_player_methods[] = {
+	{ GDBUS_METHOD("Play", NULL, NULL, media_player_play) },
+	{ GDBUS_METHOD("Pause", NULL, NULL, media_player_pause) },
+	{ GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) },
+	{ GDBUS_METHOD("Next", NULL, NULL, media_player_next) },
+	{ GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) },
+	{ GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) },
+	{ GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) },
+	{ }
+};
+
+static const GDBusSignalTable media_player_signals[] = {
+	{ }
+};
+
+static const GDBusPropertyTable media_player_properties[] = {
+	{ "Name", "s", get_name, NULL, name_exists },
+	{ "Type", "s", get_type, NULL, type_exists },
+	{ "Subtype", "s", get_subtype, NULL, subtype_exists },
+	{ "Position", "u", get_position, NULL, NULL },
+	{ "Status", "s", get_status, NULL, status_exists },
+	{ "Equalizer", "s", get_setting, set_setting, setting_exists },
+	{ "Repeat", "s", get_setting, set_setting, setting_exists },
+	{ "Shuffle", "s", get_setting, set_setting, setting_exists },
+	{ "Scan", "s", get_setting, set_setting, setting_exists },
+	{ "Track", "a{sv}", get_track, NULL, track_exists },
+	{ "Device", "o", get_device, NULL, NULL },
+	{ "Browsable", "b", get_browsable, NULL, browsable_exists },
+	{ "Searchable", "b", get_searchable, NULL, searchable_exists },
+	{ "Playlist", "o", get_playlist, NULL, playlist_exists },
+	{ }
+};
+
+static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *folder = mp->scope;
+	struct player_callback *cb = mp->cb;
+	DBusMessageIter iter;
+	const char *string;
+	int err;
+
+	dbus_message_iter_init(msg, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &string);
+
+	if (!mp->searchable || folder != mp->folder || !cb->cbs->search)
+		return btd_error_not_supported(msg);
+
+	if (folder->msg != NULL)
+		return btd_error_failed(msg, strerror(EINVAL));
+
+	err = cb->cbs->search(mp, string, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	folder->msg = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static int parse_filters(struct media_player *player, DBusMessageIter *iter,
+						uint32_t *start, uint32_t *end)
+{
+	struct media_folder *folder = player->scope;
+	DBusMessageIter dict;
+	int ctype;
+
+	*start = 0;
+	*end = folder->number_of_items ? folder->number_of_items - 1 :
+								UINT32_MAX;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		DBusMessageIter entry, var;
+		const char *key;
+
+		if (ctype != DBUS_TYPE_DICT_ENTRY)
+			return -EINVAL;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+			return -EINVAL;
+
+		dbus_message_iter_recurse(&entry, &var);
+
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT32)
+			return -EINVAL;
+
+		if (strcasecmp(key, "Start") == 0)
+			dbus_message_iter_get_basic(&var, start);
+		else if (strcasecmp(key, "End") == 0)
+			dbus_message_iter_get_basic(&var, end);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (folder->number_of_items > 0 && *end > folder->number_of_items)
+		*end = folder->number_of_items;
+
+	return 0;
+}
+
+static DBusMessage *media_folder_list_items(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *folder = mp->scope;
+	struct player_callback *cb = mp->cb;
+	DBusMessageIter iter;
+	uint32_t start, end;
+	int err;
+
+	dbus_message_iter_init(msg, &iter);
+
+	if (parse_filters(mp, &iter, &start, &end) < 0)
+		return btd_error_invalid_args(msg);
+
+	if (cb->cbs->list_items == NULL)
+		return btd_error_not_supported(msg);
+
+	if (folder->msg != NULL)
+		return btd_error_failed(msg, strerror(EBUSY));
+
+	err = cb->cbs->list_items(mp, folder->item->name, start, end,
+							cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	folder->msg = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static void media_item_free(struct media_item *item)
+{
+	if (item->metadata != NULL)
+		g_hash_table_unref(item->metadata);
+
+	g_free(item->path);
+	g_free(item->name);
+	g_free(item);
+}
+
+static void media_item_destroy(void *data)
+{
+	struct media_item *item = data;
+
+	DBG("%s", item->path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
+						MEDIA_ITEM_INTERFACE);
+
+	media_item_free(item);
+}
+
+static void media_folder_destroy(void *data)
+{
+	struct media_folder *folder = data;
+
+	g_slist_free_full(folder->subfolders, media_folder_destroy);
+	g_slist_free_full(folder->items, media_item_destroy);
+
+	if (folder->msg != NULL)
+		dbus_message_unref(folder->msg);
+
+	media_item_destroy(folder->item);
+	g_free(folder);
+}
+
+static void media_player_change_scope(struct media_player *mp,
+						struct media_folder *folder)
+{
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (mp->scope == folder)
+		return;
+
+	DBG("%s", folder->item->name);
+
+	/* Skip setting current folder if folder is current playlist/search */
+	if (folder == mp->playlist || folder == mp->search)
+		goto cleanup;
+
+	mp->folder = folder;
+
+	/* Skip item cleanup if scope is the current playlist */
+	if (mp->scope == mp->playlist)
+		goto done;
+
+cleanup:
+	g_slist_free_full(mp->scope->items, media_item_destroy);
+	mp->scope->items = NULL;
+
+	/* Destroy search folder if it exists and is not being set as scope */
+	if (mp->search != NULL && folder != mp->search) {
+		mp->folders = g_slist_remove(mp->folders, mp->search);
+		media_folder_destroy(mp->search);
+		mp->search = NULL;
+	}
+
+done:
+	mp->scope = folder;
+
+	if (cb->cbs->total_items) {
+		err = cb->cbs->total_items(mp, folder->item->name,
+							cb->user_data);
+		if (err < 0)
+			DBG("Failed to get total num of items");
+	} else {
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				mp->path, MEDIA_FOLDER_INTERFACE,
+				"NumberOfItems");
+	}
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+				MEDIA_FOLDER_INTERFACE, "Name");
+}
+
+static struct media_folder *find_folder(GSList *folders, const char *pattern)
+{
+	GSList *l;
+
+	for (l = folders; l; l = l->next) {
+		struct media_folder *folder = l->data;
+
+		if (g_str_equal(folder->item->name, pattern))
+			return folder;
+
+		if (g_str_equal(folder->item->path, pattern))
+			return folder;
+
+		folder = find_folder(folder->subfolders, pattern);
+		if (folder != NULL)
+			return folder;
+	}
+
+	return NULL;
+}
+
+static struct media_folder *media_player_find_folder(struct media_player *mp,
+							const char *pattern)
+{
+	return find_folder(mp->folders, pattern);
+}
+
+static DBusMessage *media_folder_change_folder(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *folder = mp->scope;
+	struct player_callback *cb = mp->cb;
+	const char *path;
+	int err;
+
+	if (!dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	if (folder->msg != NULL)
+		return btd_error_failed(msg, strerror(EBUSY));
+
+	folder = media_player_find_folder(mp, path);
+	if (folder == NULL)
+		return btd_error_invalid_args(msg);
+
+	if (mp->scope == folder)
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	if (folder == mp->playlist || folder == mp->folder ||
+						folder == mp->search) {
+		media_player_change_scope(mp, folder);
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
+
+	/*
+	 * ChangePath can only navigate one level up/down so check if folder
+	 * is direct child or parent of the current folder otherwise fail.
+	 */
+	if (!g_slist_find(mp->folder->subfolders, folder) &&
+				!g_slist_find(folder->subfolders, mp->folder))
+		return btd_error_invalid_args(msg);
+
+	if (cb->cbs->change_folder == NULL)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid,
+								cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	mp->scope->msg = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static gboolean folder_name_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *folder = mp->scope;
+
+	if (folder == NULL || folder->item == NULL)
+		return FALSE;
+
+	return folder->item->name != NULL;
+}
+
+static gboolean get_folder_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *folder = mp->scope;
+
+	if (folder == NULL || folder->item == NULL)
+		return FALSE;
+
+	DBG("%s", folder->item->name);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&folder->item->name);
+
+	return TRUE;
+}
+
+static gboolean items_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_player *mp = data;
+
+	return mp->scope != NULL;
+}
+
+static gboolean get_items(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_player *mp = data;
+	struct media_folder *folder = mp->scope;
+
+	if (folder == NULL)
+		return FALSE;
+
+	DBG("%u", folder->number_of_items);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
+						&folder->number_of_items);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable media_folder_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Search",
+			GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
+			GDBUS_ARGS({ "folder", "o" }),
+			media_folder_search) },
+	{ GDBUS_ASYNC_METHOD("ListItems",
+			GDBUS_ARGS({ "filter", "a{sv}" }),
+			GDBUS_ARGS({ "items", "a{oa{sv}}" }),
+			media_folder_list_items) },
+	{ GDBUS_ASYNC_METHOD("ChangeFolder",
+			GDBUS_ARGS({ "folder", "o" }), NULL,
+			media_folder_change_folder) },
+	{ }
+};
+
+static const GDBusPropertyTable media_folder_properties[] = {
+	{ "Name", "s", get_folder_name, NULL, folder_name_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "NumberOfItems", "u", get_items, NULL, items_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+static void media_player_set_scope(struct media_player *mp,
+						struct media_folder *folder)
+{
+	if (mp->scope == NULL) {
+		if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					mp->path, MEDIA_FOLDER_INTERFACE,
+					media_folder_methods,
+					NULL,
+					media_folder_properties, mp, NULL)) {
+			error("D-Bus failed to register %s on %s path",
+					MEDIA_FOLDER_INTERFACE, mp->path);
+			return;
+		}
+		mp->scope = folder;
+		return;
+	}
+
+	return media_player_change_scope(mp, folder);
+}
+
+void media_player_destroy(struct media_player *mp)
+{
+	DBG("%s", mp->path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
+						MEDIA_PLAYER_INTERFACE);
+
+	if (mp->track)
+		g_hash_table_unref(mp->track);
+
+	if (mp->settings)
+		g_hash_table_unref(mp->settings);
+
+	if (mp->process_id > 0)
+		g_source_remove(mp->process_id);
+
+	if (mp->scope)
+		g_dbus_unregister_interface(btd_get_dbus_connection(),
+						mp->path,
+						MEDIA_FOLDER_INTERFACE);
+
+	g_slist_free_full(mp->pending, g_free);
+	g_slist_free_full(mp->folders, media_folder_destroy);
+
+	g_timer_destroy(mp->progress);
+	g_free(mp->cb);
+	g_free(mp->status);
+	g_free(mp->path);
+	g_free(mp->device);
+	g_free(mp->subtype);
+	g_free(mp->type);
+	g_free(mp->name);
+	g_free(mp);
+}
+
+struct media_player *media_player_controller_create(const char *path,
+								uint16_t id)
+{
+	struct media_player *mp;
+
+	mp = g_new0(struct media_player, 1);
+	mp->device = g_strdup(path);
+	mp->path = g_strdup_printf("%s/player%u", path, id);
+	mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+							g_free, g_free);
+	mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
+							g_free, g_free);
+	mp->progress = g_timer_new();
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					media_player_methods,
+					media_player_signals,
+					media_player_properties, mp, NULL)) {
+		error("D-Bus failed to register %s path", mp->path);
+		media_player_destroy(mp);
+		return NULL;
+	}
+
+	DBG("%s", mp->path);
+
+	return mp;
+}
+
+const char *media_player_get_path(struct media_player *mp)
+{
+	return mp->path;
+}
+
+void media_player_set_duration(struct media_player *mp, uint32_t duration)
+{
+	char *value, *curval;
+
+	DBG("%u", duration);
+
+	/* Only update duration if track exists */
+	if (g_hash_table_size(mp->track) == 0)
+		return;
+
+	/* Ignore if duration is already set */
+	curval = g_hash_table_lookup(mp->track, "Duration");
+	if (curval != NULL)
+		return;
+
+	value = g_strdup_printf("%u", duration);
+
+	g_hash_table_replace(mp->track, g_strdup("Duration"), value);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Track");
+}
+
+void media_player_set_position(struct media_player *mp, uint32_t position)
+{
+	DBG("%u", position);
+
+	/* Only update duration if track exists */
+	if (g_hash_table_size(mp->track) == 0)
+		return;
+
+	mp->position = position;
+	g_timer_start(mp->progress);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+					MEDIA_PLAYER_INTERFACE, "Position");
+}
+
+void media_player_set_setting(struct media_player *mp, const char *key,
+							const char *value)
+{
+	char *curval;
+	struct pending_req *p;
+
+	DBG("%s: %s", key, value);
+
+	if (strcasecmp(key, "Error") == 0) {
+		p = g_slist_nth_data(mp->pending, 0);
+		if (p == NULL)
+			return;
+
+		g_dbus_pending_property_error(p->id, ERROR_INTERFACE ".Failed",
+									value);
+		goto send;
+	}
+
+	curval = g_hash_table_lookup(mp->settings, key);
+	if (g_strcmp0(curval, value) == 0)
+		goto done;
+
+	g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+					MEDIA_PLAYER_INTERFACE, key);
+
+done:
+	p = find_pending(mp, key);
+	if (p == NULL)
+		return;
+
+	if (strcasecmp(value, p->value) == 0)
+		g_dbus_pending_property_success(p->id);
+	else
+		g_dbus_pending_property_error(p->id,
+					ERROR_INTERFACE ".NotSupported",
+					"Operation is not supported");
+
+send:
+	mp->pending = g_slist_remove(mp->pending, p);
+	g_free(p);
+
+	return;
+}
+
+const char *media_player_get_status(struct media_player *mp)
+{
+	return mp->status;
+}
+
+void media_player_set_status(struct media_player *mp, const char *status)
+{
+	DBG("%s", status);
+
+	if (g_strcmp0(mp->status, status) == 0)
+		return;
+
+	g_free(mp->status);
+	mp->status = g_strdup(status);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+					MEDIA_PLAYER_INTERFACE, "Status");
+
+	mp->position = media_player_get_position(mp);
+	g_timer_start(mp->progress);
+}
+
+static gboolean process_metadata_changed(void *user_data)
+{
+	struct media_player *mp = user_data;
+	const char *item;
+
+	mp->process_id = 0;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Track");
+
+	item = g_hash_table_lookup(mp->track, "Item");
+	if (item == NULL)
+		return FALSE;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					item, MEDIA_ITEM_INTERFACE,
+					"Metadata");
+
+	return FALSE;
+}
+
+void media_player_set_metadata(struct media_player *mp,
+				struct media_item *item, const char *key,
+				void *data, size_t len)
+{
+	char *value, *curval;
+
+	value = g_strndup(data, len);
+
+	DBG("%s: %s", key, value);
+
+	curval = g_hash_table_lookup(mp->track, key);
+	if (g_strcmp0(curval, value) == 0) {
+		g_free(value);
+		return;
+	}
+
+	if (mp->process_id == 0) {
+		g_hash_table_remove_all(mp->track);
+		mp->process_id = g_idle_add(process_metadata_changed, mp);
+	}
+
+	g_hash_table_replace(mp->track, g_strdup(key), value);
+}
+
+void media_player_set_type(struct media_player *mp, const char *type)
+{
+	if (g_strcmp0(mp->type, type) == 0)
+		return;
+
+	DBG("%s", type);
+
+	mp->type = g_strdup(type);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Type");
+}
+
+void media_player_set_subtype(struct media_player *mp, const char *subtype)
+{
+	if (g_strcmp0(mp->subtype, subtype) == 0)
+		return;
+
+	DBG("%s", subtype);
+
+	mp->subtype = g_strdup(subtype);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Subtype");
+}
+
+void media_player_set_name(struct media_player *mp, const char *name)
+{
+	if (g_strcmp0(mp->name, name) == 0)
+		return;
+
+	DBG("%s", name);
+
+	mp->name = g_strdup(name);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Name");
+}
+
+void media_player_set_browsable(struct media_player *mp, bool enabled)
+{
+	if (mp->browsable == enabled)
+		return;
+
+	DBG("%s", enabled ? "true" : "false");
+
+	mp->browsable = enabled;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Browsable");
+}
+
+void media_player_set_searchable(struct media_player *mp, bool enabled)
+{
+	if (mp->browsable == enabled)
+		return;
+
+	DBG("%s", enabled ? "true" : "false");
+
+	mp->searchable = enabled;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					mp->path, MEDIA_PLAYER_INTERFACE,
+					"Searchable");
+}
+
+void media_player_set_folder(struct media_player *mp, const char *name,
+						uint32_t number_of_items)
+{
+	struct media_folder *folder;
+
+	DBG("%s number of items %u", name, number_of_items);
+
+	folder = media_player_find_folder(mp, name);
+	if (folder == NULL) {
+		error("Unknown folder: %s", name);
+		return;
+	}
+
+	folder->number_of_items = number_of_items;
+
+	media_player_set_scope(mp, folder);
+}
+
+void media_player_set_playlist(struct media_player *mp, const char *name)
+{
+	struct media_folder *folder;
+
+	DBG("%s", name);
+
+	folder = media_player_find_folder(mp, name);
+	if (folder == NULL) {
+		error("Unknown folder: %s", name);
+		return;
+	}
+
+	mp->playlist = folder;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+					MEDIA_PLAYER_INTERFACE, "Playlist");
+}
+
+static struct media_item *media_folder_find_item(struct media_folder *folder,
+								uint64_t uid)
+{
+	GSList *l;
+
+	if (uid == 0)
+		return NULL;
+
+	for (l = folder->items; l; l = l->next) {
+		struct media_item *item = l->data;
+
+		if (item->uid == uid)
+			return item;
+	}
+
+	return NULL;
+}
+
+static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_item *item = data;
+	struct media_player *mp = item->player;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (!item->playable || !cb->cbs->play_item)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->play_item(mp, item->path, item->uid, cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct media_item *item = data;
+	struct media_player *mp = item->player;
+	struct player_callback *cb = mp->cb;
+	int err;
+
+	if (!item->playable || !cb->cbs->play_item)
+		return btd_error_not_supported(msg);
+
+	err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid,
+							cb->user_data);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static gboolean get_player(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&item->player->path);
+
+	return TRUE;
+}
+
+static gboolean item_name_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_item *item = data;
+
+	return item->name != NULL;
+}
+
+static gboolean get_item_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+
+	if (item->name == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &item->name);
+
+	return TRUE;
+}
+
+static const char *type_to_string(uint8_t type)
+{
+	switch (type) {
+	case PLAYER_ITEM_TYPE_AUDIO:
+		return "audio";
+	case PLAYER_ITEM_TYPE_VIDEO:
+		return "video";
+	case PLAYER_ITEM_TYPE_FOLDER:
+		return "folder";
+	}
+
+	return NULL;
+}
+
+static gboolean get_item_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	const char *string;
+
+	string = type_to_string(item->type);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
+
+	return TRUE;
+}
+
+static gboolean get_playable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	dbus_bool_t value;
+
+	value = item->playable;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static const char *folder_type_to_string(uint8_t type)
+{
+	switch (type) {
+	case PLAYER_FOLDER_TYPE_MIXED:
+		return "mixed";
+	case PLAYER_FOLDER_TYPE_TITLES:
+		return "titles";
+	case PLAYER_FOLDER_TYPE_ALBUMS:
+		return "albums";
+	case PLAYER_FOLDER_TYPE_ARTISTS:
+		return "artists";
+	case PLAYER_FOLDER_TYPE_GENRES:
+		return "genres";
+	case PLAYER_FOLDER_TYPE_PLAYLISTS:
+		return "playlists";
+	case PLAYER_FOLDER_TYPE_YEARS:
+		return "years";
+	}
+
+	return NULL;
+}
+
+static gboolean folder_type_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct media_item *item = data;
+
+	return folder_type_to_string(item->folder_type) != NULL;
+}
+
+static gboolean get_folder_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	const char *string;
+
+	string = folder_type_to_string(item->folder_type);
+	if (string == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
+
+	return TRUE;
+}
+
+static gboolean metadata_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_item *item = data;
+
+	return item->metadata != NULL;
+}
+
+static void append_metadata(void *key, void *value, void *user_data)
+{
+	DBusMessageIter *dict = user_data;
+	const char *strkey = key;
+
+	if (strcasecmp(strkey, "Item") == 0)
+		return;
+
+	if (strcasecmp(strkey, "Duration") == 0 ||
+			strcasecmp(strkey, "TrackNumber") == 0 ||
+			strcasecmp(strkey, "NumberOfTracks") == 0)  {
+		uint32_t num = atoi(value);
+		dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+	} else {
+		dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+	}
+}
+
+static gboolean get_metadata(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_item *item = data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	if (g_hash_table_size(item->metadata) > 0)
+		g_hash_table_foreach(item->metadata, append_metadata, &dict);
+	else if (item->name != NULL)
+		dict_append_entry(&dict, "Title", DBUS_TYPE_STRING,
+								&item->name);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable media_item_methods[] = {
+	{ GDBUS_METHOD("Play", NULL, NULL, media_item_play) },
+	{ GDBUS_METHOD("AddtoNowPlaying", NULL, NULL,
+					media_item_add_to_nowplaying) },
+	{ }
+};
+
+static const GDBusPropertyTable media_item_properties[] = {
+	{ "Player", "o", get_player, NULL, NULL },
+	{ "Name", "s", get_item_name, NULL, item_name_exists },
+	{ "Type", "s", get_item_type, NULL, NULL },
+	{ "FolderType", "s", get_folder_type, NULL, folder_type_exists },
+	{ "Playable", "b", get_playable, NULL, NULL },
+	{ "Metadata", "a{sv}", get_metadata, NULL, metadata_exists },
+	{ }
+};
+
+void media_item_set_playable(struct media_item *item, bool value)
+{
+	if (item->playable == value)
+		return;
+
+	item->playable = value;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), item->path,
+					MEDIA_ITEM_INTERFACE, "Playable");
+}
+
+static struct media_item *media_folder_create_item(struct media_player *mp,
+						struct media_folder *folder,
+						const char *name,
+						player_item_type_t type,
+						uint64_t uid)
+{
+	struct media_item *item;
+	const char *strtype;
+
+	item = media_folder_find_item(folder, uid);
+	if (item != NULL)
+		return item;
+
+	strtype = type_to_string(type);
+	if (strtype == NULL)
+		return NULL;
+
+	DBG("%s type %s uid %" PRIu64 "", name, strtype, uid);
+
+	item = g_new0(struct media_item, 1);
+	item->player = mp;
+	item->uid = uid;
+
+	if (uid > 0)
+		item->path = g_strdup_printf("%s/item%" PRIu64 "",
+						folder->item->path, uid);
+	else
+		item->path = g_strdup_printf("%s%s", mp->path, name);
+
+	item->name = g_strdup(name);
+	item->type = type;
+	item->folder_type = PLAYER_FOLDER_TYPE_INVALID;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					item->path, MEDIA_ITEM_INTERFACE,
+					media_item_methods,
+					NULL,
+					media_item_properties, item, NULL)) {
+		error("D-Bus failed to register %s on %s path",
+					MEDIA_ITEM_INTERFACE, item->path);
+		media_item_free(item);
+		return NULL;
+	}
+
+	if (type != PLAYER_ITEM_TYPE_FOLDER) {
+		folder->items = g_slist_prepend(folder->items, item);
+		item->metadata = g_hash_table_new_full(g_str_hash, g_str_equal,
+							g_free, g_free);
+	}
+
+	DBG("%s", item->path);
+
+	return item;
+}
+
+struct media_item *media_player_create_item(struct media_player *mp,
+						const char *name,
+						player_item_type_t type,
+						uint64_t uid)
+{
+	return media_folder_create_item(mp, mp->scope, name, type, uid);
+}
+
+static struct media_folder *
+media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
+{
+	struct media_folder *folder = mp->scope;
+	GSList *l;
+
+	for (l = folder->subfolders; l; l = l->next) {
+		struct media_folder *folder = l->data;
+
+		if (folder->item->uid == uid)
+			return folder;
+	}
+
+	return NULL;
+}
+
+struct media_item *media_player_create_folder(struct media_player *mp,
+						const char *name,
+						player_folder_type_t type,
+						uint64_t uid)
+{
+	struct media_folder *folder;
+	struct media_item *item;
+
+	if (uid > 0)
+		folder = media_player_find_folder_by_uid(mp, uid);
+	else
+		folder = media_player_find_folder(mp, name);
+
+	if (folder != NULL)
+		return folder->item;
+
+	if (uid > 0)
+		item = media_player_create_subfolder(mp, name, uid);
+	else
+		item = media_player_create_item(mp, name,
+						PLAYER_ITEM_TYPE_FOLDER, uid);
+
+	if (item == NULL)
+		return NULL;
+
+	folder = g_new0(struct media_folder, 1);
+	folder->item = item;
+
+	item->folder_type = type;
+
+	if (mp->folder != NULL)
+		goto done;
+
+	mp->folder = folder;
+
+done:
+	if (uid > 0) {
+		folder->parent = mp->folder;
+		mp->folder->subfolders = g_slist_prepend(
+							mp->folder->subfolders,
+							folder);
+	} else
+		mp->folders = g_slist_prepend(mp->folders, folder);
+
+	return item;
+}
+
+void media_player_set_callbacks(struct media_player *mp,
+				const struct media_player_callback *cbs,
+				void *user_data)
+{
+	struct player_callback *cb;
+
+	if (mp->cb)
+		g_free(mp->cb);
+
+	cb = g_new0(struct player_callback, 1);
+	cb->cbs = cbs;
+	cb->user_data = user_data;
+
+	mp->cb = cb;
+}
+
+struct media_item *media_player_set_playlist_item(struct media_player *mp,
+								uint64_t uid)
+{
+	struct media_folder *folder = mp->playlist;
+	struct media_item *item;
+
+	DBG("%" PRIu64 "", uid);
+
+	if (folder == NULL || uid == 0)
+		return NULL;
+
+	item = media_folder_create_item(mp, folder, NULL,
+						PLAYER_ITEM_TYPE_AUDIO, uid);
+	if (item == NULL)
+		return NULL;
+
+	media_item_set_playable(item, true);
+
+	if (mp->track != item->metadata) {
+		g_hash_table_unref(mp->track);
+		mp->track = g_hash_table_ref(item->metadata);
+	}
+
+	if (item == g_hash_table_lookup(mp->track, "Item"))
+		return item;
+
+	if (mp->process_id == 0) {
+		g_hash_table_remove_all(mp->track);
+		mp->process_id = g_idle_add(process_metadata_changed, mp);
+	}
+
+	g_hash_table_replace(mp->track, g_strdup("Item"),
+						g_strdup(item->path));
+
+	return item;
+}
diff --git a/bluez/repo/profiles/audio/player.h b/bluez/repo/profiles/audio/player.h
new file mode 100644
index 0000000..4ad8bfe
--- /dev/null
+++ b/bluez/repo/profiles/audio/player.h
@@ -0,0 +1,115 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2012-2012  Intel Corporation
+ *
+ *
+ *  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 enum {
+	PLAYER_ITEM_TYPE_AUDIO,
+	PLAYER_ITEM_TYPE_VIDEO,
+	PLAYER_ITEM_TYPE_FOLDER,
+	PLAYER_ITEM_TYPE_INVALID,
+} player_item_type_t;
+
+typedef enum {
+	PLAYER_FOLDER_TYPE_MIXED,
+	PLAYER_FOLDER_TYPE_TITLES,
+	PLAYER_FOLDER_TYPE_ALBUMS,
+	PLAYER_FOLDER_TYPE_ARTISTS,
+	PLAYER_FOLDER_TYPE_GENRES,
+	PLAYER_FOLDER_TYPE_PLAYLISTS,
+	PLAYER_FOLDER_TYPE_YEARS,
+	PLAYER_FOLDER_TYPE_INVALID,
+} player_folder_type_t;
+
+struct media_player;
+struct media_item;
+
+struct media_player_callback {
+	bool (*set_setting) (struct media_player *mp, const char *key,
+				const char *value, void *user_data);
+	int (*play) (struct media_player *mp, void *user_data);
+	int (*pause) (struct media_player *mp, void *user_data);
+	int (*stop) (struct media_player *mp, void *user_data);
+	int (*next) (struct media_player *mp, void *user_data);
+	int (*previous) (struct media_player *mp, void *user_data);
+	int (*fast_forward) (struct media_player *mp, void *user_data);
+	int (*rewind) (struct media_player *mp, void *user_data);
+	int (*list_items) (struct media_player *mp, const char *name,
+				uint32_t start, uint32_t end, void *user_data);
+	int (*change_folder) (struct media_player *mp, const char *path,
+						uint64_t uid, void *user_data);
+	int (*search) (struct media_player *mp, const char *string,
+						void *user_data);
+	int (*play_item) (struct media_player *mp, const char *name,
+					uint64_t uid, void *user_data);
+	int (*add_to_nowplaying) (struct media_player *mp, const char *name,
+					uint64_t uid, void *user_data);
+	int (*total_items) (struct media_player *mp, const char *name,
+						void *user_data);
+};
+
+struct media_player *media_player_controller_create(const char *path,
+								uint16_t id);
+const char *media_player_get_path(struct media_player *mp);
+void media_player_destroy(struct media_player *mp);
+void media_player_set_duration(struct media_player *mp, uint32_t duration);
+void media_player_set_position(struct media_player *mp, uint32_t position);
+void media_player_set_setting(struct media_player *mp, const char *key,
+							const char *value);
+const char *media_player_get_status(struct media_player *mp);
+void media_player_set_status(struct media_player *mp, const char *status);
+void media_player_set_metadata(struct media_player *mp,
+				struct media_item *item, const char *key,
+				void *data, size_t len);
+void media_player_set_type(struct media_player *mp, const char *type);
+void media_player_set_subtype(struct media_player *mp, const char *subtype);
+void media_player_set_name(struct media_player *mp, const char *name);
+void media_player_set_browsable(struct media_player *mp, bool enabled);
+void media_player_set_searchable(struct media_player *mp, bool enabled);
+void media_player_set_folder(struct media_player *mp, const char *path,
+								uint32_t items);
+void media_player_set_playlist(struct media_player *mp, const char *name);
+struct media_item *media_player_set_playlist_item(struct media_player *mp,
+								uint64_t uid);
+
+struct media_item *media_player_create_folder(struct media_player *mp,
+						const char *name,
+						player_folder_type_t type,
+						uint64_t uid);
+struct media_item *media_player_create_item(struct media_player *mp,
+						const char *name,
+						player_item_type_t type,
+						uint64_t uid);
+
+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);
+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);
+
+void media_player_set_callbacks(struct media_player *mp,
+				const struct media_player_callback *cbs,
+				void *user_data);
diff --git a/bluez/repo/profiles/audio/sink.c b/bluez/repo/profiles/audio/sink.c
new file mode 100644
index 0000000..ac7237b
--- /dev/null
+++ b/bluez/repo/profiles/audio/sink.c
@@ -0,0 +1,462 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/service.h"
+#include "src/error.h"
+#include "src/dbus-common.h"
+#include "src/shared/queue.h"
+
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "sink.h"
+
+#define STREAM_SETUP_RETRY_TIMER 2
+
+struct sink {
+	struct btd_service *service;
+	struct avdtp *session;
+	struct avdtp_stream *stream;
+	unsigned int cb_id;
+	avdtp_session_state_t session_state;
+	avdtp_state_t stream_state;
+	sink_state_t state;
+	unsigned int connect_id;
+	unsigned int disconnect_id;
+	unsigned int avdtp_callback_id;
+};
+
+struct sink_state_callback {
+	sink_state_cb cb;
+	struct btd_service *service;
+	void *user_data;
+	unsigned int id;
+};
+
+static GSList *sink_callbacks = NULL;
+
+static char *str_state[] = {
+	"SINK_STATE_DISCONNECTED",
+	"SINK_STATE_CONNECTING",
+	"SINK_STATE_CONNECTED",
+	"SINK_STATE_PLAYING",
+};
+
+static void sink_set_state(struct sink *sink, sink_state_t new_state)
+{
+	struct btd_service *service = sink->service;
+	struct btd_device *dev = btd_service_get_device(service);
+	sink_state_t old_state = sink->state;
+	GSList *l;
+
+	sink->state = new_state;
+
+	DBG("State changed %s: %s -> %s", device_get_path(dev),
+				str_state[old_state], str_state[new_state]);
+
+	for (l = sink_callbacks; l != NULL; l = l->next) {
+		struct sink_state_callback *cb = l->data;
+
+		if (cb->service != service)
+			continue;
+
+		cb->cb(service, old_state, new_state, cb->user_data);
+	}
+
+	if (new_state != SINK_STATE_DISCONNECTED)
+		return;
+
+	if (sink->session) {
+		avdtp_unref(sink->session);
+		sink->session = NULL;
+	}
+}
+
+static void avdtp_state_callback(struct btd_device *dev,
+					struct avdtp *session,
+					avdtp_session_state_t old_state,
+					avdtp_session_state_t new_state,
+					void *user_data)
+{
+	struct sink *sink = user_data;
+
+	switch (new_state) {
+	case AVDTP_SESSION_STATE_DISCONNECTED:
+		sink_set_state(sink, SINK_STATE_DISCONNECTED);
+		break;
+	case AVDTP_SESSION_STATE_CONNECTING:
+		sink_set_state(sink, SINK_STATE_CONNECTING);
+		break;
+	case AVDTP_SESSION_STATE_CONNECTED:
+		break;
+	}
+
+	sink->session_state = new_state;
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data)
+{
+	struct btd_service *service = user_data;
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (err)
+		return;
+
+	switch (new_state) {
+	case AVDTP_STATE_IDLE:
+		btd_service_disconnecting_complete(sink->service, 0);
+
+		if (sink->disconnect_id > 0) {
+			a2dp_cancel(sink->disconnect_id);
+			sink->disconnect_id = 0;
+		}
+
+		if (sink->session) {
+			avdtp_unref(sink->session);
+			sink->session = NULL;
+		}
+		sink->stream = NULL;
+		sink->cb_id = 0;
+		break;
+	case AVDTP_STATE_OPEN:
+		btd_service_connecting_complete(sink->service, 0);
+		sink_set_state(sink, SINK_STATE_CONNECTED);
+		break;
+	case AVDTP_STATE_STREAMING:
+		sink_set_state(sink, SINK_STATE_PLAYING);
+		break;
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+	default:
+		break;
+	}
+
+	sink->stream_state = new_state;
+}
+
+static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err, void *user_data)
+{
+	struct sink *sink = user_data;
+
+	sink->connect_id = 0;
+
+	if (stream)
+		return;
+
+	avdtp_unref(sink->session);
+	sink->session = NULL;
+	if (avdtp_error_category(err) == AVDTP_ERRNO
+				&& avdtp_error_posix_errno(err) != EHOSTDOWN)
+		btd_service_connecting_complete(sink->service, -EAGAIN);
+	else
+		btd_service_connecting_complete(sink->service, -EIO);
+}
+
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+			GSList *caps, void *user_data)
+{
+	struct sink *sink = user_data;
+	int id;
+
+	sink->connect_id = 0;
+
+	id = a2dp_config(session, sep, stream_setup_complete, caps, sink);
+	if (id == 0)
+		goto failed;
+
+	sink->connect_id = id;
+	return;
+
+failed:
+	btd_service_connecting_complete(sink->service, -EIO);
+
+	avdtp_unref(sink->session);
+	sink->session = NULL;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+				void *user_data)
+{
+	struct sink *sink = user_data;
+	int id, perr;
+
+	sink->connect_id = 0;
+
+	if (err) {
+		avdtp_unref(sink->session);
+		sink->session = NULL;
+
+		perr = -avdtp_error_posix_errno(err);
+		if (perr != -EHOSTDOWN) {
+			if (avdtp_error_category(err) == AVDTP_ERRNO)
+				perr = -EAGAIN;
+			else
+				perr = -EIO;
+		}
+		goto failed;
+	}
+
+	DBG("Discovery complete");
+
+	id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL,
+						select_complete, sink);
+	if (id == 0) {
+		perr = -EIO;
+		goto failed;
+	}
+
+	sink->connect_id = id;
+	return;
+
+failed:
+	btd_service_connecting_complete(sink->service, perr);
+	avdtp_unref(sink->session);
+	sink->session = NULL;
+}
+
+gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session)
+{
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (sink->connect_id > 0 || sink->disconnect_id > 0)
+		return FALSE;
+
+	if (session && !sink->session)
+		sink->session = avdtp_ref(session);
+
+	if (!sink->session)
+		return FALSE;
+
+	sink->connect_id = a2dp_discover(sink->session, discovery_complete,
+								sink);
+	if (sink->connect_id == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+int sink_connect(struct btd_service *service)
+{
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (!sink->session)
+		sink->session = a2dp_avdtp_get(btd_service_get_device(service));
+
+	if (!sink->session) {
+		DBG("Unable to get a session");
+		return -EIO;
+	}
+
+	if (sink->connect_id > 0 || sink->disconnect_id > 0)
+		return -EBUSY;
+
+	if (sink->state == SINK_STATE_CONNECTING)
+		return -EBUSY;
+
+	if (sink->stream_state >= AVDTP_STATE_OPEN)
+		return -EALREADY;
+
+	if (!sink_setup_stream(service, NULL)) {
+		DBG("Failed to create a stream");
+		return -EIO;
+	}
+
+	DBG("stream creation in progress");
+
+	return 0;
+}
+
+static void sink_free(struct btd_service *service)
+{
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (sink->cb_id)
+		avdtp_stream_remove_cb(sink->session, sink->stream,
+					sink->cb_id);
+
+	if (sink->session)
+		avdtp_unref(sink->session);
+
+	if (sink->connect_id > 0) {
+		btd_service_connecting_complete(sink->service, -ECANCELED);
+		a2dp_cancel(sink->connect_id);
+		sink->connect_id = 0;
+	}
+
+	if (sink->disconnect_id > 0) {
+		btd_service_disconnecting_complete(sink->service, -ECANCELED);
+		a2dp_cancel(sink->disconnect_id);
+		sink->disconnect_id = 0;
+	}
+
+	avdtp_remove_state_cb(sink->avdtp_callback_id);
+	btd_service_unref(sink->service);
+
+	g_free(sink);
+}
+
+void sink_unregister(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	DBG("%s", device_get_path(dev));
+
+	sink_free(service);
+}
+
+int sink_init(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct sink *sink;
+
+	DBG("%s", device_get_path(dev));
+
+	sink = g_new0(struct sink, 1);
+
+	sink->service = btd_service_ref(service);
+
+	sink->avdtp_callback_id = avdtp_add_state_cb(dev, avdtp_state_callback,
+									sink);
+
+	btd_service_set_user_data(service, sink);
+
+	return 0;
+}
+
+gboolean sink_is_active(struct btd_service *service)
+{
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (sink->session)
+		return TRUE;
+
+	return FALSE;
+}
+
+gboolean sink_new_stream(struct btd_service *service, struct avdtp *session,
+				struct avdtp_stream *stream)
+{
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (sink->stream)
+		return FALSE;
+
+	if (!sink->session)
+		sink->session = avdtp_ref(session);
+
+	sink->stream = stream;
+
+	sink->cb_id = avdtp_stream_add_cb(session, stream,
+						stream_state_changed, service);
+
+	return TRUE;
+}
+
+int sink_disconnect(struct btd_service *service)
+{
+	struct sink *sink = btd_service_get_user_data(service);
+
+	if (!sink->session)
+		return -ENOTCONN;
+
+	/* cancel pending connect */
+	if (sink->connect_id > 0) {
+		a2dp_cancel(sink->connect_id);
+		sink->connect_id = 0;
+		btd_service_disconnecting_complete(sink->service, 0);
+
+		avdtp_unref(sink->session);
+		sink->session = NULL;
+
+		return 0;
+	}
+
+	/* disconnect already ongoing */
+	if (sink->disconnect_id > 0)
+		return -EBUSY;
+
+	if (!sink->stream)
+		return -ENOTCONN;
+
+	return avdtp_close(sink->session, sink->stream, FALSE);
+}
+
+unsigned int sink_add_state_cb(struct btd_service *service, sink_state_cb cb,
+								void *user_data)
+{
+	struct sink_state_callback *state_cb;
+	static unsigned int id = 0;
+
+	state_cb = g_new(struct sink_state_callback, 1);
+	state_cb->cb = cb;
+	state_cb->service = service;
+	state_cb->user_data = user_data;
+	state_cb->id = ++id;
+
+	sink_callbacks = g_slist_append(sink_callbacks, state_cb);
+
+	return state_cb->id;
+}
+
+gboolean sink_remove_state_cb(unsigned int id)
+{
+	GSList *l;
+
+	for (l = sink_callbacks; l != NULL; l = l->next) {
+		struct sink_state_callback *cb = l->data;
+		if (cb && cb->id == id) {
+			sink_callbacks = g_slist_remove(sink_callbacks, cb);
+			g_free(cb);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
diff --git a/bluez/repo/profiles/audio/sink.h b/bluez/repo/profiles/audio/sink.h
new file mode 100644
index 0000000..93c62a2
--- /dev/null
+++ b/bluez/repo/profiles/audio/sink.h
@@ -0,0 +1,50 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+typedef enum {
+	SINK_STATE_DISCONNECTED,
+	SINK_STATE_CONNECTING,
+	SINK_STATE_CONNECTED,
+	SINK_STATE_PLAYING,
+} sink_state_t;
+
+typedef void (*sink_state_cb) (struct btd_service *service,
+				sink_state_t old_state,
+				sink_state_t new_state,
+				void *user_data);
+
+struct btd_service;
+
+unsigned int sink_add_state_cb(struct btd_service *service, sink_state_cb cb,
+							void *user_data);
+gboolean sink_remove_state_cb(unsigned int id);
+
+int sink_init(struct btd_service *service);
+void sink_unregister(struct btd_service *service);
+gboolean sink_is_active(struct btd_service *service);
+int sink_connect(struct btd_service *service);
+gboolean sink_new_stream(struct btd_service *service, struct avdtp *session,
+				struct avdtp_stream *stream);
+gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session);
+int sink_disconnect(struct btd_service *service);
diff --git a/bluez/repo/profiles/audio/source.c b/bluez/repo/profiles/audio/source.c
new file mode 100644
index 0000000..372b132
--- /dev/null
+++ b/bluez/repo/profiles/audio/source.c
@@ -0,0 +1,454 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009  Joao Paulo Rechi Vita
+ *
+ *
+ *  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 <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/service.h"
+#include "src/error.h"
+#include "src/dbus-common.h"
+#include "src/shared/queue.h"
+
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "source.h"
+
+struct source {
+	struct btd_service *service;
+	struct avdtp *session;
+	struct avdtp_stream *stream;
+	unsigned int cb_id;
+	avdtp_session_state_t session_state;
+	avdtp_state_t stream_state;
+	source_state_t state;
+	unsigned int connect_id;
+	unsigned int disconnect_id;
+	unsigned int avdtp_callback_id;
+};
+
+struct source_state_callback {
+	source_state_cb cb;
+	struct btd_service *service;
+	void *user_data;
+	unsigned int id;
+};
+
+static GSList *source_callbacks = NULL;
+
+static char *str_state[] = {
+	"SOURCE_STATE_DISCONNECTED",
+	"SOURCE_STATE_CONNECTING",
+	"SOURCE_STATE_CONNECTED",
+	"SOURCE_STATE_PLAYING",
+};
+
+static void source_set_state(struct source *source, source_state_t new_state)
+{
+	struct btd_device *dev = btd_service_get_device(source->service);
+	source_state_t old_state = source->state;
+	GSList *l;
+
+	source->state = new_state;
+
+	DBG("State changed %s: %s -> %s", device_get_path(dev),
+				str_state[old_state], str_state[new_state]);
+
+	for (l = source_callbacks; l != NULL; l = l->next) {
+		struct source_state_callback *cb = l->data;
+
+		if (cb->service != source->service)
+			continue;
+
+		cb->cb(source->service, old_state, new_state, cb->user_data);
+	}
+
+	if (new_state != SOURCE_STATE_DISCONNECTED)
+		return;
+
+	if (source->session) {
+		avdtp_unref(source->session);
+		source->session = NULL;
+	}
+}
+
+static void avdtp_state_callback(struct btd_device *dev, struct avdtp *session,
+					avdtp_session_state_t old_state,
+					avdtp_session_state_t new_state,
+					void *user_data)
+{
+	struct source *source = user_data;
+
+	switch (new_state) {
+	case AVDTP_SESSION_STATE_DISCONNECTED:
+		source_set_state(source, SOURCE_STATE_DISCONNECTED);
+		break;
+	case AVDTP_SESSION_STATE_CONNECTING:
+		source_set_state(source, SOURCE_STATE_CONNECTING);
+		break;
+	case AVDTP_SESSION_STATE_CONNECTED:
+		break;
+	}
+
+	source->session_state = new_state;
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data)
+{
+	struct btd_service *service = user_data;
+	struct source *source = btd_service_get_user_data(service);
+
+	if (err)
+		return;
+
+	switch (new_state) {
+	case AVDTP_STATE_IDLE:
+		btd_service_disconnecting_complete(source->service, 0);
+
+		if (source->disconnect_id > 0) {
+			a2dp_cancel(source->disconnect_id);
+			source->disconnect_id = 0;
+		}
+
+		if (source->session) {
+			avdtp_unref(source->session);
+			source->session = NULL;
+		}
+		source->stream = NULL;
+		source->cb_id = 0;
+		break;
+	case AVDTP_STATE_OPEN:
+		btd_service_connecting_complete(source->service, 0);
+		source_set_state(source, SOURCE_STATE_CONNECTED);
+		break;
+	case AVDTP_STATE_STREAMING:
+		source_set_state(source, SOURCE_STATE_PLAYING);
+		break;
+	case AVDTP_STATE_CONFIGURED:
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+	default:
+		break;
+	}
+
+	source->stream_state = new_state;
+}
+
+static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err, void *user_data)
+{
+	struct source *source = user_data;
+
+	source->connect_id = 0;
+
+	if (stream)
+		return;
+
+	avdtp_unref(source->session);
+	source->session = NULL;
+	if (avdtp_error_category(err) == AVDTP_ERRNO
+				&& avdtp_error_posix_errno(err) != EHOSTDOWN)
+		btd_service_connecting_complete(source->service, -EAGAIN);
+	else
+		btd_service_connecting_complete(source->service, -EIO);
+}
+
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+			GSList *caps, void *user_data)
+{
+	struct source *source = user_data;
+	int id;
+
+	source->connect_id = 0;
+
+	if (caps == NULL)
+		goto failed;
+
+	id = a2dp_config(session, sep, stream_setup_complete, caps, source);
+	if (id == 0)
+		goto failed;
+
+	source->connect_id = id;
+	return;
+
+failed:
+	btd_service_connecting_complete(source->service, -EIO);
+
+	avdtp_unref(source->session);
+	source->session = NULL;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+				void *user_data)
+{
+	struct source *source = user_data;
+	int id, perr;
+
+	source->connect_id = 0;
+
+	if (err) {
+		avdtp_unref(source->session);
+		source->session = NULL;
+
+		perr = -avdtp_error_posix_errno(err);
+		if (perr != -EHOSTDOWN) {
+			if (avdtp_error_category(err) == AVDTP_ERRNO)
+				perr = -EAGAIN;
+			else
+				perr = -EIO;
+		}
+		goto failed;
+	}
+
+	DBG("Discovery complete");
+
+	id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
+						select_complete, source);
+	if (id == 0) {
+		perr = -EIO;
+		goto failed;
+	}
+
+	source->connect_id = id;
+	return;
+
+failed:
+	btd_service_connecting_complete(source->service, perr);
+	avdtp_unref(source->session);
+	source->session = NULL;
+}
+
+gboolean source_setup_stream(struct btd_service *service,
+							struct avdtp *session)
+{
+	struct source *source = btd_service_get_user_data(service);
+
+	if (source->connect_id > 0 || source->disconnect_id > 0)
+		return FALSE;
+
+	if (session && !source->session)
+		source->session = avdtp_ref(session);
+
+	if (!source->session)
+		return FALSE;
+
+	source->connect_id = a2dp_discover(source->session, discovery_complete,
+								source);
+	if (source->connect_id == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+int source_connect(struct btd_service *service)
+{
+	struct source *source = btd_service_get_user_data(service);
+
+	if (!source->session)
+		source->session = a2dp_avdtp_get(btd_service_get_device(service));
+
+	if (!source->session) {
+		DBG("Unable to get a session");
+		return -EIO;
+	}
+
+	if (source->connect_id > 0 || source->disconnect_id > 0)
+		return -EBUSY;
+
+	if (source->state == SOURCE_STATE_CONNECTING)
+		return -EBUSY;
+
+	if (source->stream_state >= AVDTP_STATE_OPEN)
+		return -EALREADY;
+
+	if (!source_setup_stream(service, NULL)) {
+		DBG("Failed to create a stream");
+		return -EIO;
+	}
+
+	DBG("stream creation in progress");
+
+	return 0;
+}
+
+static void source_free(struct btd_service *service)
+{
+	struct source *source = btd_service_get_user_data(service);
+
+	if (source->cb_id)
+		avdtp_stream_remove_cb(source->session, source->stream,
+					source->cb_id);
+
+	if (source->session)
+		avdtp_unref(source->session);
+
+	if (source->connect_id > 0) {
+		btd_service_connecting_complete(source->service, -ECANCELED);
+		a2dp_cancel(source->connect_id);
+		source->connect_id = 0;
+	}
+
+	if (source->disconnect_id > 0) {
+		btd_service_disconnecting_complete(source->service, -ECANCELED);
+		a2dp_cancel(source->disconnect_id);
+		source->disconnect_id = 0;
+	}
+
+	avdtp_remove_state_cb(source->avdtp_callback_id);
+	btd_service_unref(source->service);
+
+	g_free(source);
+}
+
+void source_unregister(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+
+	DBG("%s", device_get_path(dev));
+
+	source_free(service);
+}
+
+int source_init(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct source *source;
+
+	DBG("%s", device_get_path(dev));
+
+	source = g_new0(struct source, 1);
+
+	source->service = btd_service_ref(service);
+
+	source->avdtp_callback_id = avdtp_add_state_cb(dev,
+							avdtp_state_callback,
+							source);
+
+	btd_service_set_user_data(service, source);
+
+	return 0;
+}
+
+gboolean source_new_stream(struct btd_service *service, struct avdtp *session,
+				struct avdtp_stream *stream)
+{
+	struct source *source = btd_service_get_user_data(service);
+
+	if (source->stream)
+		return FALSE;
+
+	if (!source->session)
+		source->session = avdtp_ref(session);
+
+	source->stream = stream;
+
+	source->cb_id = avdtp_stream_add_cb(session, stream,
+						stream_state_changed, service);
+
+	return TRUE;
+}
+
+int source_disconnect(struct btd_service *service)
+{
+	struct source *source = btd_service_get_user_data(service);
+
+	if (!source->session)
+		return -ENOTCONN;
+
+	/* cancel pending connect */
+	if (source->connect_id > 0) {
+		a2dp_cancel(source->connect_id);
+		source->connect_id = 0;
+		btd_service_disconnecting_complete(source->service, 0);
+
+		avdtp_unref(source->session);
+		source->session = NULL;
+
+		return 0;
+	}
+
+	/* disconnect already ongoing */
+	if (source->disconnect_id > 0)
+		return -EBUSY;
+
+	if (!source->stream)
+		return -ENOTCONN;
+
+	return avdtp_close(source->session, source->stream, FALSE);
+}
+
+unsigned int source_add_state_cb(struct btd_service *service,
+					source_state_cb cb, void *user_data)
+{
+	struct source_state_callback *state_cb;
+	static unsigned int id = 0;
+
+	state_cb = g_new(struct source_state_callback, 1);
+	state_cb->cb = cb;
+	state_cb->service = service;
+	state_cb->user_data = user_data;
+	state_cb->id = ++id;
+
+	source_callbacks = g_slist_append(source_callbacks, state_cb);
+
+	return state_cb->id;
+}
+
+gboolean source_remove_state_cb(unsigned int id)
+{
+	GSList *l;
+
+	for (l = source_callbacks; l != NULL; l = l->next) {
+		struct source_state_callback *cb = l->data;
+		if (cb && cb->id == id) {
+			source_callbacks = g_slist_remove(source_callbacks, cb);
+			g_free(cb);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
diff --git a/bluez/repo/profiles/audio/source.h b/bluez/repo/profiles/audio/source.h
new file mode 100644
index 0000000..a014c68
--- /dev/null
+++ b/bluez/repo/profiles/audio/source.h
@@ -0,0 +1,51 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009  Joao Paulo Rechi Vita
+ *
+ *
+ *  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 enum {
+	SOURCE_STATE_DISCONNECTED,
+	SOURCE_STATE_CONNECTING,
+	SOURCE_STATE_CONNECTED,
+	SOURCE_STATE_PLAYING,
+} source_state_t;
+
+typedef void (*source_state_cb) (struct btd_service *service,
+				source_state_t old_state,
+				source_state_t new_state,
+				void *user_data);
+
+struct btd_service;
+
+unsigned int source_add_state_cb(struct btd_service *service,
+					source_state_cb cb, void *user_data);
+gboolean source_remove_state_cb(unsigned int id);
+
+int source_init(struct btd_service *service);
+void source_unregister(struct btd_service *service);
+int source_connect(struct btd_service *service);
+gboolean source_new_stream(struct btd_service *service, struct avdtp *session,
+				struct avdtp_stream *stream);
+gboolean source_setup_stream(struct btd_service *service,
+							struct avdtp *session);
+int source_disconnect(struct btd_service *service);
diff --git a/bluez/repo/profiles/audio/transport.c b/bluez/repo/profiles/audio/transport.c
new file mode 100644
index 0000000..4121e52
--- /dev/null
+++ b/bluez/repo/profiles/audio/transport.c
@@ -0,0 +1,968 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/dbus-common.h"
+
+#include "src/log.h"
+#include "src/error.h"
+#include "src/shared/queue.h"
+
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "sink.h"
+#include "source.h"
+#include "avrcp.h"
+
+#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
+
+typedef enum {
+	TRANSPORT_STATE_IDLE,		/* Not acquired and suspended */
+	TRANSPORT_STATE_PENDING,	/* Playing but not acquired */
+	TRANSPORT_STATE_REQUESTING,	/* Acquire in progress */
+	TRANSPORT_STATE_ACTIVE,		/* Acquired and playing */
+	TRANSPORT_STATE_SUSPENDING,     /* Release in progress */
+} transport_state_t;
+
+static char *str_state[] = {
+	"TRANSPORT_STATE_IDLE",
+	"TRANSPORT_STATE_PENDING",
+	"TRANSPORT_STATE_REQUESTING",
+	"TRANSPORT_STATE_ACTIVE",
+	"TRANSPORT_STATE_SUSPENDING",
+};
+
+struct media_request {
+	DBusMessage		*msg;
+	guint			id;
+};
+
+struct media_owner {
+	struct media_transport	*transport;
+	struct media_request	*pending;
+	char			*name;
+	guint			watch;
+};
+
+struct a2dp_transport {
+	struct avdtp		*session;
+	uint16_t		delay;
+	uint16_t		volume;
+};
+
+struct media_transport {
+	char			*path;		/* Transport object path */
+	struct btd_device	*device;	/* Transport device */
+	struct media_endpoint	*endpoint;	/* Transport endpoint */
+	struct media_owner	*owner;		/* Transport owner */
+	uint8_t			*configuration; /* Transport configuration */
+	int			size;		/* Transport configuration size */
+	int			fd;		/* Transport file descriptor */
+	uint16_t		imtu;		/* Transport input mtu */
+	uint16_t		omtu;		/* Transport output mtu */
+	transport_state_t	state;
+	guint			hs_watch;
+	guint			source_watch;
+	guint			sink_watch;
+	guint			(*resume) (struct media_transport *transport,
+					struct media_owner *owner);
+	guint			(*suspend) (struct media_transport *transport,
+					struct media_owner *owner);
+	void			(*cancel) (struct media_transport *transport,
+								guint id);
+	GDestroyNotify		destroy;
+	void			*data;
+};
+
+static GSList *transports = NULL;
+
+static const char *state2str(transport_state_t state)
+{
+	switch (state) {
+	case TRANSPORT_STATE_IDLE:
+	case TRANSPORT_STATE_REQUESTING:
+		return "idle";
+	case TRANSPORT_STATE_PENDING:
+		return "pending";
+	case TRANSPORT_STATE_ACTIVE:
+	case TRANSPORT_STATE_SUSPENDING:
+		return "active";
+	}
+
+	return NULL;
+}
+
+static gboolean state_in_use(transport_state_t state)
+{
+	switch (state) {
+	case TRANSPORT_STATE_IDLE:
+	case TRANSPORT_STATE_PENDING:
+		return FALSE;
+	case TRANSPORT_STATE_REQUESTING:
+	case TRANSPORT_STATE_ACTIVE:
+	case TRANSPORT_STATE_SUSPENDING:
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void transport_set_state(struct media_transport *transport,
+							transport_state_t state)
+{
+	transport_state_t old_state = transport->state;
+	const char *str;
+
+	if (old_state == state)
+		return;
+
+	transport->state = state;
+
+	DBG("State changed %s: %s -> %s", transport->path, str_state[old_state],
+							str_state[state]);
+
+	str = state2str(state);
+
+	if (g_strcmp0(str, state2str(old_state)) != 0)
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						transport->path,
+						MEDIA_TRANSPORT_INTERFACE,
+						"State");
+}
+
+void media_transport_destroy(struct media_transport *transport)
+{
+	char *path;
+
+	if (transport->sink_watch)
+		sink_remove_state_cb(transport->sink_watch);
+
+	if (transport->source_watch)
+		source_remove_state_cb(transport->source_watch);
+
+	path = g_strdup(transport->path);
+	g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+						MEDIA_TRANSPORT_INTERFACE);
+
+	g_free(path);
+}
+
+static struct media_request *media_request_create(DBusMessage *msg, guint id)
+{
+	struct media_request *req;
+
+	req = g_new0(struct media_request, 1);
+	req->msg = dbus_message_ref(msg);
+	req->id = id;
+
+	DBG("Request created: method=%s id=%u", dbus_message_get_member(msg),
+									id);
+
+	return req;
+}
+
+static void media_request_reply(struct media_request *req, int err)
+{
+	DBusMessage *reply;
+
+	DBG("Request %s Reply %s", dbus_message_get_member(req->msg),
+							strerror(err));
+
+	if (!err)
+		reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+	else
+		reply = g_dbus_create_error(req->msg,
+						ERROR_INTERFACE ".Failed",
+						"%s", strerror(err));
+
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+static void media_owner_remove(struct media_owner *owner)
+{
+	struct media_transport *transport = owner->transport;
+	struct media_request *req = owner->pending;
+
+	if (!req)
+		return;
+
+	DBG("Owner %s Request %s", owner->name,
+					dbus_message_get_member(req->msg));
+
+	if (req->id)
+		transport->cancel(transport, req->id);
+
+	owner->pending = NULL;
+	if (req->msg)
+		dbus_message_unref(req->msg);
+
+	g_free(req);
+}
+
+static void media_owner_free(struct media_owner *owner)
+{
+	DBG("Owner %s", owner->name);
+
+	media_owner_remove(owner);
+
+	g_free(owner->name);
+	g_free(owner);
+}
+
+static void media_transport_remove_owner(struct media_transport *transport)
+{
+	struct media_owner *owner = transport->owner;
+
+	DBG("Transport %s Owner %s", transport->path, owner->name);
+
+	/* Reply if owner has a pending request */
+	if (owner->pending)
+		media_request_reply(owner->pending, EIO);
+
+	transport->owner = NULL;
+
+	if (owner->watch)
+		g_dbus_remove_watch(btd_get_dbus_connection(), owner->watch);
+
+	media_owner_free(owner);
+
+	if (state_in_use(transport->state))
+		transport->suspend(transport, NULL);
+}
+
+static gboolean media_transport_set_fd(struct media_transport *transport,
+					int fd, uint16_t imtu, uint16_t omtu)
+{
+	if (transport->fd == fd)
+		return TRUE;
+
+	transport->fd = fd;
+	transport->imtu = imtu;
+	transport->omtu = omtu;
+
+	info("%s: fd(%d) ready", transport->path, fd);
+
+	return TRUE;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+				struct avdtp_error *err, void *user_data)
+{
+	struct media_owner *owner = user_data;
+	struct media_request *req = owner->pending;
+	struct media_transport *transport = owner->transport;
+	struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+	struct avdtp_stream *stream;
+	int fd;
+	uint16_t imtu, omtu;
+	gboolean ret;
+
+	req->id = 0;
+
+	if (err)
+		goto fail;
+
+	stream = a2dp_sep_get_stream(sep);
+	if (stream == NULL)
+		goto fail;
+
+	ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL);
+	if (ret == FALSE)
+		goto fail;
+
+	media_transport_set_fd(transport, fd, imtu, omtu);
+
+	ret = g_dbus_send_reply(btd_get_dbus_connection(), req->msg,
+						DBUS_TYPE_UNIX_FD, &fd,
+						DBUS_TYPE_UINT16, &imtu,
+						DBUS_TYPE_UINT16, &omtu,
+						DBUS_TYPE_INVALID);
+	if (ret == FALSE)
+		goto fail;
+
+	media_owner_remove(owner);
+
+	transport_set_state(transport, TRANSPORT_STATE_ACTIVE);
+
+	return;
+
+fail:
+	media_transport_remove_owner(transport);
+}
+
+static guint resume_a2dp(struct media_transport *transport,
+				struct media_owner *owner)
+{
+	struct a2dp_transport *a2dp = transport->data;
+	struct media_endpoint *endpoint = transport->endpoint;
+	struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+	guint id;
+
+	if (a2dp->session == NULL) {
+		a2dp->session = a2dp_avdtp_get(transport->device);
+		if (a2dp->session == NULL)
+			return 0;
+	}
+
+	if (state_in_use(transport->state))
+		return a2dp_resume(a2dp->session, sep, a2dp_resume_complete,
+									owner);
+
+	if (a2dp_sep_lock(sep, a2dp->session) == FALSE)
+		return 0;
+
+	id = a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner);
+
+	if (id == 0) {
+		a2dp_sep_unlock(sep, a2dp->session);
+		return 0;
+	}
+
+	if (transport->state == TRANSPORT_STATE_IDLE)
+		transport_set_state(transport, TRANSPORT_STATE_REQUESTING);
+
+	return id;
+}
+
+static void a2dp_suspend_complete(struct avdtp *session,
+				struct avdtp_error *err, void *user_data)
+{
+	struct media_owner *owner = user_data;
+	struct media_transport *transport = owner->transport;
+	struct a2dp_transport *a2dp = transport->data;
+	struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+
+	/* Release always succeeds */
+	if (owner->pending) {
+		owner->pending->id = 0;
+		media_request_reply(owner->pending, 0);
+		media_owner_remove(owner);
+	}
+
+	a2dp_sep_unlock(sep, a2dp->session);
+	transport_set_state(transport, TRANSPORT_STATE_IDLE);
+	media_transport_remove_owner(transport);
+}
+
+static guint suspend_a2dp(struct media_transport *transport,
+						struct media_owner *owner)
+{
+	struct a2dp_transport *a2dp = transport->data;
+	struct media_endpoint *endpoint = transport->endpoint;
+	struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+	if (owner != NULL)
+		return a2dp_suspend(a2dp->session, sep, a2dp_suspend_complete,
+									owner);
+
+	transport_set_state(transport, TRANSPORT_STATE_IDLE);
+	a2dp_sep_unlock(sep, a2dp->session);
+
+	return 0;
+}
+
+static void cancel_a2dp(struct media_transport *transport, guint id)
+{
+	a2dp_cancel(id);
+}
+
+static void media_owner_exit(DBusConnection *connection, void *user_data)
+{
+	struct media_owner *owner = user_data;
+
+	owner->watch = 0;
+
+	media_owner_remove(owner);
+
+	media_transport_remove_owner(owner->transport);
+}
+
+static void media_transport_set_owner(struct media_transport *transport,
+					struct media_owner *owner)
+{
+	DBG("Transport %s Owner %s", transport->path, owner->name);
+	transport->owner = owner;
+	owner->transport = transport;
+	owner->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+							owner->name,
+							media_owner_exit,
+							owner, NULL);
+}
+
+static struct media_owner *media_owner_create(DBusMessage *msg)
+{
+	struct media_owner *owner;
+
+	owner = g_new0(struct media_owner, 1);
+	owner->name = g_strdup(dbus_message_get_sender(msg));
+
+	DBG("Owner created: sender=%s", owner->name);
+
+	return owner;
+}
+
+static void media_owner_add(struct media_owner *owner,
+						struct media_request *req)
+{
+	DBG("Owner %s Request %s", owner->name,
+					dbus_message_get_member(req->msg));
+
+	owner->pending = req;
+}
+
+static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	struct media_owner *owner;
+	struct media_request *req;
+	guint id;
+
+	if (transport->owner != NULL)
+		return btd_error_not_authorized(msg);
+
+	if (transport->state >= TRANSPORT_STATE_REQUESTING)
+		return btd_error_not_authorized(msg);
+
+	owner = media_owner_create(msg);
+	id = transport->resume(transport, owner);
+	if (id == 0) {
+		media_owner_free(owner);
+		return btd_error_not_authorized(msg);
+	}
+
+	req = media_request_create(msg, id);
+	media_owner_add(owner, req);
+	media_transport_set_owner(transport, owner);
+
+	return NULL;
+}
+
+static DBusMessage *try_acquire(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct media_transport *transport = data;
+	struct media_owner *owner;
+	struct media_request *req;
+	guint id;
+
+	if (transport->owner != NULL)
+		return btd_error_not_authorized(msg);
+
+	if (transport->state >= TRANSPORT_STATE_REQUESTING)
+		return btd_error_not_authorized(msg);
+
+	if (transport->state != TRANSPORT_STATE_PENDING)
+		return btd_error_not_available(msg);
+
+	owner = media_owner_create(msg);
+	id = transport->resume(transport, owner);
+	if (id == 0) {
+		media_owner_free(owner);
+		return btd_error_not_authorized(msg);
+	}
+
+	req = media_request_create(msg, id);
+	media_owner_add(owner, req);
+	media_transport_set_owner(transport, owner);
+
+	return NULL;
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct media_transport *transport = data;
+	struct media_owner *owner = transport->owner;
+	const char *sender;
+	struct media_request *req;
+	guint id;
+
+	sender = dbus_message_get_sender(msg);
+
+	if (owner == NULL || g_strcmp0(owner->name, sender) != 0)
+		return btd_error_not_authorized(msg);
+
+	if (owner->pending) {
+		const char *member;
+
+		member = dbus_message_get_member(owner->pending->msg);
+		/* Cancel Acquire request if that exist */
+		if (g_str_equal(member, "Acquire"))
+			media_owner_remove(owner);
+		else
+			return btd_error_in_progress(msg);
+	}
+
+	transport_set_state(transport, TRANSPORT_STATE_SUSPENDING);
+
+	id = transport->suspend(transport, owner);
+	if (id == 0) {
+		media_transport_remove_owner(transport);
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
+
+	req = media_request_create(msg, id);
+	media_owner_add(owner, req);
+
+	return NULL;
+}
+
+static gboolean get_device(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	const char *path = device_get_path(transport->device);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	return TRUE;
+}
+
+static gboolean get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	const char *uuid = media_endpoint_get_uuid(transport->endpoint);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+	return TRUE;
+}
+
+static gboolean get_codec(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	uint8_t codec = media_endpoint_get_codec(transport->endpoint);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &codec);
+
+	return TRUE;
+}
+
+static gboolean get_configuration(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_BYTE_AS_STRING, &array);
+
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&transport->configuration,
+						transport->size);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean get_state(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	const char *state = state2str(transport->state);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state);
+
+	return TRUE;
+}
+
+static gboolean delay_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_transport *transport = data;
+	struct a2dp_transport *a2dp = transport->data;
+
+	return a2dp->delay != 0;
+}
+
+static gboolean get_delay(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	struct a2dp_transport *a2dp = transport->data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &a2dp->delay);
+
+	return TRUE;
+}
+
+static gboolean volume_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct media_transport *transport = data;
+	struct a2dp_transport *a2dp = transport->data;
+
+	return a2dp->volume <= 127;
+}
+
+static gboolean get_volume(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	struct a2dp_transport *a2dp = transport->data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &a2dp->volume);
+
+	return TRUE;
+}
+
+static void set_volume(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct media_transport *transport = data;
+	struct a2dp_transport *a2dp = transport->data;
+	uint16_t volume;
+	bool notify;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &volume);
+
+	if (volume > 127) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	g_dbus_pending_property_success(id);
+
+	if (a2dp->volume == volume)
+		return;
+
+	a2dp->volume = volume;
+
+	notify = transport->source_watch ? true : false;
+	if (notify)
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						transport->path,
+						MEDIA_TRANSPORT_INTERFACE,
+						"Volume");
+
+	avrcp_set_volume(transport->device, volume, notify);
+}
+
+static const GDBusMethodTable transport_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Acquire",
+			NULL,
+			GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
+							{ "mtu_w", "q" }),
+			acquire) },
+	{ GDBUS_ASYNC_METHOD("TryAcquire",
+			NULL,
+			GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
+							{ "mtu_w", "q" }),
+			try_acquire) },
+	{ GDBUS_ASYNC_METHOD("Release", NULL, NULL, release) },
+	{ },
+};
+
+static const GDBusPropertyTable transport_properties[] = {
+	{ "Device", "o", get_device },
+	{ "UUID", "s", get_uuid },
+	{ "Codec", "y", get_codec },
+	{ "Configuration", "ay", get_configuration },
+	{ "State", "s", get_state },
+	{ "Delay", "q", get_delay, NULL, delay_exists },
+	{ "Volume", "q", get_volume, set_volume, volume_exists },
+	{ }
+};
+
+static void destroy_a2dp(void *data)
+{
+	struct a2dp_transport *a2dp = data;
+
+	if (a2dp->session)
+		avdtp_unref(a2dp->session);
+
+	g_free(a2dp);
+}
+
+static void media_transport_free(void *data)
+{
+	struct media_transport *transport = data;
+
+	transports = g_slist_remove(transports, transport);
+
+	if (transport->owner)
+		media_transport_remove_owner(transport);
+
+	if (transport->destroy != NULL)
+		transport->destroy(transport->data);
+
+	g_free(transport->configuration);
+	g_free(transport->path);
+	g_free(transport);
+}
+
+static void transport_update_playing(struct media_transport *transport,
+							gboolean playing)
+{
+	DBG("%s State=%s Playing=%d", transport->path,
+					str_state[transport->state], playing);
+
+	if (playing == FALSE) {
+		if (transport->state == TRANSPORT_STATE_PENDING)
+			transport_set_state(transport, TRANSPORT_STATE_IDLE);
+		else if (transport->state == TRANSPORT_STATE_ACTIVE) {
+			/* Remove owner */
+			if (transport->owner != NULL)
+				media_transport_remove_owner(transport);
+		}
+	} else if (transport->state == TRANSPORT_STATE_IDLE)
+		transport_set_state(transport, TRANSPORT_STATE_PENDING);
+}
+
+static void sink_state_changed(struct btd_service *service,
+						sink_state_t old_state,
+						sink_state_t new_state,
+						void *user_data)
+{
+	struct media_transport *transport = user_data;
+
+	if (new_state == SINK_STATE_PLAYING)
+		transport_update_playing(transport, TRUE);
+	else
+		transport_update_playing(transport, FALSE);
+}
+
+static void source_state_changed(struct btd_service *service,
+						source_state_t old_state,
+						source_state_t new_state,
+						void *user_data)
+{
+	struct media_transport *transport = user_data;
+
+	if (new_state == SOURCE_STATE_PLAYING)
+		transport_update_playing(transport, TRUE);
+	else
+		transport_update_playing(transport, FALSE);
+}
+
+static int media_transport_init_source(struct media_transport *transport)
+{
+	struct btd_service *service;
+	struct a2dp_transport *a2dp;
+
+	service = btd_device_get_service(transport->device, A2DP_SINK_UUID);
+	if (service == NULL)
+		return -EINVAL;
+
+	a2dp = g_new0(struct a2dp_transport, 1);
+
+	transport->resume = resume_a2dp;
+	transport->suspend = suspend_a2dp;
+	transport->cancel = cancel_a2dp;
+	transport->data = a2dp;
+	transport->destroy = destroy_a2dp;
+
+	a2dp->volume = -1;
+	transport->sink_watch = sink_add_state_cb(service, sink_state_changed,
+								transport);
+
+	return 0;
+}
+
+static int media_transport_init_sink(struct media_transport *transport)
+{
+	struct btd_service *service;
+	struct a2dp_transport *a2dp;
+
+	service = btd_device_get_service(transport->device, A2DP_SOURCE_UUID);
+	if (service == NULL)
+		return -EINVAL;
+
+	a2dp = g_new0(struct a2dp_transport, 1);
+
+	transport->resume = resume_a2dp;
+	transport->suspend = suspend_a2dp;
+	transport->cancel = cancel_a2dp;
+	transport->data = a2dp;
+	transport->destroy = destroy_a2dp;
+
+	a2dp->volume = 127;
+	transport->source_watch = source_add_state_cb(service,
+							source_state_changed,
+							transport);
+
+	return 0;
+}
+
+struct media_transport *media_transport_create(struct btd_device *device,
+						uint8_t *configuration,
+						size_t size, void *data)
+{
+	struct media_endpoint *endpoint = data;
+	struct media_transport *transport;
+	const char *uuid;
+	static int fd = 0;
+
+	transport = g_new0(struct media_transport, 1);
+	transport->device = device;
+	transport->endpoint = endpoint;
+	transport->configuration = g_new(uint8_t, size);
+	memcpy(transport->configuration, configuration, size);
+	transport->size = size;
+	transport->path = g_strdup_printf("%s/fd%d", device_get_path(device),
+									fd++);
+	transport->fd = -1;
+
+	uuid = media_endpoint_get_uuid(endpoint);
+	if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
+		if (media_transport_init_source(transport) < 0)
+			goto fail;
+	} else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+		if (media_transport_init_sink(transport) < 0)
+			goto fail;
+	} else
+		goto fail;
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(),
+				transport->path, MEDIA_TRANSPORT_INTERFACE,
+				transport_methods, NULL, transport_properties,
+				transport, media_transport_free) == FALSE) {
+		error("Could not register transport %s", transport->path);
+		goto fail;
+	}
+
+	transports = g_slist_append(transports, transport);
+
+	return transport;
+
+fail:
+	media_transport_free(transport);
+	return NULL;
+}
+
+const char *media_transport_get_path(struct media_transport *transport)
+{
+	return transport->path;
+}
+
+void media_transport_update_delay(struct media_transport *transport,
+							uint16_t delay)
+{
+	struct a2dp_transport *a2dp = transport->data;
+
+	/* Check if delay really changed */
+	if (a2dp->delay == delay)
+		return;
+
+	a2dp->delay = delay;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					transport->path,
+					MEDIA_TRANSPORT_INTERFACE, "Delay");
+}
+
+struct btd_device *media_transport_get_dev(struct media_transport *transport)
+{
+	return transport->device;
+}
+
+uint16_t media_transport_get_volume(struct media_transport *transport)
+{
+	struct a2dp_transport *a2dp = transport->data;
+	return a2dp->volume;
+}
+
+void media_transport_update_volume(struct media_transport *transport,
+								uint8_t volume)
+{
+	struct a2dp_transport *a2dp = transport->data;
+
+	/* Check if volume really changed */
+	if (a2dp->volume == volume)
+		return;
+
+	a2dp->volume = volume;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					transport->path,
+					MEDIA_TRANSPORT_INTERFACE, "Volume");
+}
+
+uint8_t media_transport_get_device_volume(struct btd_device *dev)
+{
+	GSList *l;
+
+	if (dev == NULL)
+		return 128;
+
+	for (l = transports; l; l = l->next) {
+		struct media_transport *transport = l->data;
+		if (transport->device != dev)
+			continue;
+
+		/* Volume is A2DP only */
+		if (media_endpoint_get_sep(transport->endpoint))
+			return media_transport_get_volume(transport);
+	}
+
+	return 0;
+}
+
+void media_transport_update_device_volume(struct btd_device *dev,
+								uint8_t volume)
+{
+	GSList *l;
+
+	if (dev == NULL)
+		return;
+
+	for (l = transports; l; l = l->next) {
+		struct media_transport *transport = l->data;
+		if (transport->device != dev)
+			continue;
+
+		/* Volume is A2DP only */
+		if (media_endpoint_get_sep(transport->endpoint))
+			media_transport_update_volume(transport, volume);
+	}
+}
diff --git a/bluez/repo/profiles/audio/transport.h b/bluez/repo/profiles/audio/transport.h
new file mode 100644
index 0000000..505ad5c
--- /dev/null
+++ b/bluez/repo/profiles/audio/transport.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  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
+ *
+ */
+
+struct media_transport;
+
+struct media_transport *media_transport_create(struct btd_device *device,
+						uint8_t *configuration,
+						size_t size, void *data);
+
+void media_transport_destroy(struct media_transport *transport);
+const char *media_transport_get_path(struct media_transport *transport);
+struct btd_device *media_transport_get_dev(struct media_transport *transport);
+uint16_t media_transport_get_volume(struct media_transport *transport);
+void media_transport_update_delay(struct media_transport *transport,
+							uint16_t delay);
+void media_transport_update_volume(struct media_transport *transport,
+								uint8_t volume);
+void transport_get_properties(struct media_transport *transport,
+							DBusMessageIter *iter);
+
+uint8_t media_transport_get_device_volume(struct btd_device *dev);
+void media_transport_update_device_volume(struct btd_device *dev,
+								uint8_t volume);
diff --git a/bluez/repo/profiles/battery/._bas.c b/bluez/repo/profiles/battery/._bas.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/battery/._bas.c
Binary files differ
diff --git a/bluez/repo/profiles/battery/._bas.h b/bluez/repo/profiles/battery/._bas.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/battery/._bas.h
Binary files differ
diff --git a/bluez/repo/profiles/battery/bas.c b/bluez/repo/profiles/battery/bas.c
new file mode 100644
index 0000000..de369fd
--- /dev/null
+++ b/bluez/repo/profiles/battery/bas.c
@@ -0,0 +1,340 @@
+/*
+ *
+ *  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 "profiles/battery/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);
+	free(bas);
+}
+
+struct bt_bas *bt_bas_new(void *primary)
+{
+	struct bt_bas *bas;
+
+	bas = new0(struct bt_bas, 1);
+	bas->gatt_op = queue_new();
+
+	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);
+	req->user_data = user_data;
+	req->bas = bt_bas_ref(bas);
+
+	return req;
+}
+
+static void set_and_store_gatt_req(struct bt_bas *bas,
+						struct gatt_request *req,
+						unsigned int id)
+{
+	req->id = id;
+	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);
+
+	id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+	set_and_store_gatt_req(bas, req, id);
+}
+
+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);
+
+	id = gatt_read_char(attrib, handle, func, req);
+
+	set_and_store_gatt_req(bas, req, id);
+}
+
+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);
+
+	id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+	set_and_store_gatt_req(bas, req, id);
+}
+
+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);
+
+	id = gatt_discover_desc(attrib, start, end, uuid, func, req);
+	set_and_store_gatt_req(bas, req, id);
+}
+
+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;
+}
diff --git a/bluez/repo/profiles/battery/bas.h b/bluez/repo/profiles/battery/bas.h
new file mode 100644
index 0000000..3e175b5
--- /dev/null
+++ b/bluez/repo/profiles/battery/bas.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  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
+ *
+ */
+
+struct bt_bas;
+
+struct bt_bas *bt_bas_new(void *primary);
+
+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);
diff --git a/bluez/repo/profiles/cups/._cups.h b/bluez/repo/profiles/cups/._cups.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/cups/._cups.h
Binary files differ
diff --git a/bluez/repo/profiles/cups/._hcrp.c b/bluez/repo/profiles/cups/._hcrp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/cups/._hcrp.c
Binary files differ
diff --git a/bluez/repo/profiles/cups/._main.c b/bluez/repo/profiles/cups/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/cups/._main.c
Binary files differ
diff --git a/bluez/repo/profiles/cups/._sdp.c b/bluez/repo/profiles/cups/._sdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/cups/._sdp.c
Binary files differ
diff --git a/bluez/repo/profiles/cups/._spp.c b/bluez/repo/profiles/cups/._spp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/cups/._spp.c
Binary files differ
diff --git a/bluez/repo/profiles/cups/cups.h b/bluez/repo/profiles/cups/cups.h
new file mode 100644
index 0000000..f4e0c01
--- /dev/null
+++ b/bluez/repo/profiles/cups/cups.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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
+ *
+ */
+
+enum {					/**** Backend exit codes ****/
+	CUPS_BACKEND_OK = 0,		/* Job completed successfully */
+	CUPS_BACKEND_FAILED = 1,	/* Job failed, use error-policy */
+	CUPS_BACKEND_AUTH_REQUIRED = 2,	/* Job failed, authentication required */
+	CUPS_BACKEND_HOLD = 3,		/* Job failed, hold job */
+	CUPS_BACKEND_STOP = 4,		/* Job failed, stop queue */
+	CUPS_BACKEND_CANCEL = 5,	/* Job failed, cancel job */
+	CUPS_BACKEND_RETRY = 6,		/* Failure requires us to retry (BlueZ specific) */
+};
+
+int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel);
+int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm);
+
+int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class);
+int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class);
diff --git a/bluez/repo/profiles/cups/hcrp.c b/bluez/repo/profiles/cups/hcrp.c
new file mode 100644
index 0000000..edaa2cd
--- /dev/null
+++ b/bluez/repo/profiles/cups/hcrp.c
@@ -0,0 +1,367 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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 <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "cups.h"
+
+#define HCRP_PDU_CREDIT_GRANT		0x0001
+#define HCRP_PDU_CREDIT_REQUEST		0x0002
+#define HCRP_PDU_GET_LPT_STATUS		0x0005
+
+#define HCRP_STATUS_FEATURE_UNSUPPORTED	0x0000
+#define HCRP_STATUS_SUCCESS		0x0001
+#define HCRP_STATUS_CREDIT_SYNC_ERROR	0x0002
+#define HCRP_STATUS_GENERIC_FAILURE	0xffff
+
+struct hcrp_pdu_hdr {
+	uint16_t pid;
+	uint16_t tid;
+	uint16_t plen;
+} __attribute__ ((packed));
+#define HCRP_PDU_HDR_SIZE 6
+
+struct hcrp_credit_grant_cp {
+	uint32_t credit;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_GRANT_CP_SIZE 4
+
+struct hcrp_credit_grant_rp {
+	uint16_t status;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_GRANT_RP_SIZE 2
+
+struct hcrp_credit_request_rp {
+	uint16_t status;
+	uint32_t credit;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_REQUEST_RP_SIZE 6
+
+struct hcrp_get_lpt_status_rp {
+	uint16_t status;
+	uint8_t  lpt_status;
+} __attribute__ ((packed));
+#define HCRP_GET_LPT_STATUS_RP_SIZE 3
+
+static int hcrp_credit_grant(int sk, uint16_t tid, uint32_t credit)
+{
+	struct hcrp_pdu_hdr hdr;
+	struct hcrp_credit_grant_cp cp;
+	struct hcrp_credit_grant_rp rp;
+	unsigned char buf[128];
+	int len;
+
+	hdr.pid = htons(HCRP_PDU_CREDIT_GRANT);
+	hdr.tid = htons(tid);
+	hdr.plen = htons(HCRP_CREDIT_GRANT_CP_SIZE);
+	cp.credit = credit;
+	memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+	memcpy(buf + HCRP_PDU_HDR_SIZE, &cp, HCRP_CREDIT_GRANT_CP_SIZE);
+	len = write(sk, buf, HCRP_PDU_HDR_SIZE + HCRP_CREDIT_GRANT_CP_SIZE);
+	if (len < 0)
+		return len;
+
+	len = read(sk, buf, sizeof(buf));
+	if (len < 0)
+		return len;
+
+	memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+	memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_GRANT_RP_SIZE);
+
+	if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int hcrp_credit_request(int sk, uint16_t tid, uint32_t *credit)
+{
+	struct hcrp_pdu_hdr hdr;
+	struct hcrp_credit_request_rp rp;
+	unsigned char buf[128];
+	int len;
+
+	hdr.pid = htons(HCRP_PDU_CREDIT_REQUEST);
+	hdr.tid = htons(tid);
+	hdr.plen = htons(0);
+	memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+	len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+	if (len < 0)
+		return len;
+
+	len = read(sk, buf, sizeof(buf));
+	if (len < 0)
+		return len;
+
+	memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+	memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_REQUEST_RP_SIZE);
+
+	if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (credit)
+		*credit = ntohl(rp.credit);
+
+	return 0;
+}
+
+static int hcrp_get_lpt_status(int sk, uint16_t tid, uint8_t *lpt_status)
+{
+	struct hcrp_pdu_hdr hdr;
+	struct hcrp_get_lpt_status_rp rp;
+	unsigned char buf[128];
+	int len;
+
+	hdr.pid = htons(HCRP_PDU_GET_LPT_STATUS);
+	hdr.tid = htons(tid);
+	hdr.plen = htons(0);
+	memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+	len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+	if (len < 0)
+		return len;
+
+	len = read(sk, buf, sizeof(buf));
+	if (len < 0)
+		return len;
+
+	memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+	memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_GET_LPT_STATUS_RP_SIZE);
+
+	if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+		errno = EIO;
+		return -1;
+	}
+
+	if (lpt_status)
+		*lpt_status = rp.lpt_status;
+
+	return 0;
+}
+
+static inline int hcrp_get_next_tid(int tid)
+{
+	if (tid > 0xf000)
+		return 0;
+	else
+		return tid + 1;
+}
+
+int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class)
+{
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	socklen_t size;
+	unsigned char buf[2048];
+	int i, ctrl_sk, data_sk, count, len, timeout = 0;
+	unsigned int mtu;
+	uint8_t status;
+	uint16_t tid = 0;
+	uint32_t tmp, credit = 0;
+
+	if ((ctrl_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+		perror("ERROR: Can't create socket");
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+
+	if (bind(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("ERROR: Can't bind socket");
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(ctrl_psm);
+
+	if (connect(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("ERROR: Can't connect to device");
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	if ((data_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+		perror("ERROR: Can't create socket");
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+
+	if (bind(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("ERROR: Can't bind socket");
+		close(data_sk);
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(data_psm);
+
+	if (connect(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("ERROR: Can't connect to device");
+		close(data_sk);
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	fputs("STATE: -connecting-to-device\n", stderr);
+
+	memset(&opts, 0, sizeof(opts));
+	size = sizeof(opts);
+
+	if (getsockopt(data_sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+		perror("ERROR: Can't get socket options");
+		close(data_sk);
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	mtu = opts.omtu;
+
+	/* Ignore SIGTERM signals if printing from stdin */
+	if (fd == 0) {
+#ifdef HAVE_SIGSET
+		sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+		memset(&action, 0, sizeof(action));
+		sigemptyset(&action.sa_mask);
+		action.sa_handler = SIG_IGN;
+		sigaction(SIGTERM, &action, NULL);
+#else
+		signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+	}
+
+	tid = hcrp_get_next_tid(tid);
+	if (hcrp_credit_grant(ctrl_sk, tid, 0) < 0) {
+		fprintf(stderr, "ERROR: Can't grant initial credits\n");
+		close(data_sk);
+		close(ctrl_sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	for (i = 0; i < copies; i++) {
+
+		if (fd != 0) {
+			fprintf(stderr, "PAGE: 1 1\n");
+			lseek(fd, 0, SEEK_SET);
+		}
+
+		while (1) {
+			if (credit < mtu) {
+				tid = hcrp_get_next_tid(tid);
+				if (!hcrp_credit_request(ctrl_sk, tid, &tmp)) {
+					credit += tmp;
+					timeout = 0;
+				}
+			}
+
+			if (!credit) {
+				if (timeout++ > 300) {
+					tid = hcrp_get_next_tid(tid);
+					if (!hcrp_get_lpt_status(ctrl_sk, tid, &status))
+						fprintf(stderr, "ERROR: LPT status 0x%02x\n", status);
+					break;
+				}
+
+				sleep(1);
+				continue;
+			}
+
+			count = read(fd, buf, (credit > mtu) ? mtu : credit);
+			if (count <= 0)
+				break;
+
+			len = write(data_sk, buf, count);
+			if (len < 0) {
+				perror("ERROR: Error writing to device");
+				close(data_sk);
+				close(ctrl_sk);
+				return CUPS_BACKEND_FAILED;
+			}
+
+			if (len != count)
+				fprintf(stderr, "ERROR: Can't send complete data\n");
+
+			credit -= len;
+		}
+
+	}
+
+	close(data_sk);
+	close(ctrl_sk);
+
+	return CUPS_BACKEND_OK;
+}
diff --git a/bluez/repo/profiles/cups/main.c b/bluez/repo/profiles/cups/main.c
new file mode 100644
index 0000000..d46f35e
--- /dev/null
+++ b/bluez/repo/profiles/cups/main.c
@@ -0,0 +1,879 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "gdbus/gdbus.h"
+
+#include "cups.h"
+
+struct cups_device {
+	char *bdaddr;
+	char *name;
+	char *id;
+};
+
+static GSList *device_list = NULL;
+static GMainLoop *loop = NULL;
+static DBusConnection *conn = NULL;
+static gboolean doing_disco = FALSE;
+
+#define ATTRID_1284ID 0x0300
+
+struct context_data {
+	gboolean found;
+	char *id;
+};
+
+static void element_start(GMarkupParseContext *context,
+				const char *element_name,
+				const char **attribute_names,
+				const char **attribute_values,
+				gpointer user_data, GError **err)
+{
+	struct context_data *ctx_data = user_data;
+
+	if (!strcmp(element_name, "record"))
+		return;
+
+	if (!strcmp(element_name, "attribute")) {
+		int i;
+		for (i = 0; attribute_names[i]; i++) {
+			if (strcmp(attribute_names[i], "id") != 0)
+				continue;
+			if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID)
+				ctx_data->found = TRUE;
+			break;
+		}
+		return;
+	}
+
+	if (ctx_data->found  && !strcmp(element_name, "text")) {
+		int i;
+		for (i = 0; attribute_names[i]; i++) {
+			if (!strcmp(attribute_names[i], "value")) {
+				ctx_data->id = g_strdup(attribute_values[i] + 2);
+				ctx_data->found = FALSE;
+			}
+		}
+	}
+}
+
+static GMarkupParser parser = {
+	element_start, NULL, NULL, NULL, NULL
+};
+
+static char *sdp_xml_parse_record(const char *data)
+{
+	GMarkupParseContext *ctx;
+	struct context_data ctx_data;
+	int size;
+
+	size = strlen(data);
+	ctx_data.found = FALSE;
+	ctx_data.id = NULL;
+	ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL);
+
+	if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+		g_markup_parse_context_free(ctx);
+		g_free(ctx_data.id);
+		return NULL;
+	}
+
+	g_markup_parse_context_free(ctx);
+
+	return ctx_data.id;
+}
+
+static char *device_get_ieee1284_id(const char *adapter, const char *device)
+{
+	DBusMessage *message, *reply;
+	DBusMessageIter iter, reply_iter;
+	DBusMessageIter reply_iter_entry;
+	const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb";
+	const char *xml;
+	char *id = NULL;
+
+	/* Look for the service handle of the HCRP service */
+	message = dbus_message_new_method_call("org.bluez", device,
+						"org.bluez.Device1",
+						"DiscoverServices");
+	dbus_message_iter_init_append(message, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print);
+
+	reply = dbus_connection_send_with_reply_and_block(conn,
+							message, -1, NULL);
+
+	dbus_message_unref(message);
+
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init(reply, &reply_iter);
+
+	if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
+		dbus_message_unref(reply);
+		return NULL;
+	}
+
+	dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
+
+	/* Hopefully we only get one handle, or take a punt */
+	while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
+							DBUS_TYPE_DICT_ENTRY) {
+		guint32 key;
+		DBusMessageIter dict_entry;
+
+		dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
+
+		/* Key ? */
+		dbus_message_iter_get_basic(&dict_entry, &key);
+		if (!key) {
+			dbus_message_iter_next(&reply_iter_entry);
+			continue;
+		}
+
+		/* Try to get the value */
+		if (!dbus_message_iter_next(&dict_entry)) {
+			dbus_message_iter_next(&reply_iter_entry);
+			continue;
+		}
+
+		dbus_message_iter_get_basic(&dict_entry, &xml);
+
+		id = sdp_xml_parse_record(xml);
+		if (id != NULL)
+			break;
+		dbus_message_iter_next(&reply_iter_entry);
+	}
+
+	dbus_message_unref(reply);
+
+	return id;
+}
+
+static void print_printer_details(const char *name, const char *bdaddr,
+								const char *id)
+{
+	char *uri, *escaped;
+
+	escaped = g_strdelimit(g_strdup(name), "\"", '\'');
+	uri = g_strdup_printf("bluetooth://%c%c%c%c%c%c%c%c%c%c%c%c",
+				bdaddr[0], bdaddr[1],
+				bdaddr[3], bdaddr[4],
+				bdaddr[6], bdaddr[7],
+				bdaddr[9], bdaddr[10],
+				bdaddr[12], bdaddr[13],
+				bdaddr[15], bdaddr[16]);
+	printf("direct %s \"%s\" \"%s (Bluetooth)\"", uri, escaped, escaped);
+	if (id != NULL)
+		printf(" \"%s\"\n", id);
+	else
+		printf("\n");
+	g_free(escaped);
+	g_free(uri);
+}
+
+static void add_device_to_list(const char *name, const char *bdaddr,
+								const char *id)
+{
+	struct cups_device *device;
+	GSList *l;
+
+	/* Look for the device in the list */
+	for (l = device_list; l != NULL; l = l->next) {
+		device = (struct cups_device *) l->data;
+
+		if (strcmp(device->bdaddr, bdaddr) == 0) {
+			if (device->name != name) {
+				g_free(device->name);
+				device->name = g_strdup(name);
+			}
+			g_free(device->id);
+			device->id = g_strdup(id);
+			return;
+		}
+	}
+
+	/* Or add it to the list if it's not there */
+	device = g_new0(struct cups_device, 1);
+	device->bdaddr = g_strdup(bdaddr);
+	device->name = g_strdup(name);
+	device->id = g_strdup(id);
+
+	device_list = g_slist_prepend(device_list, device);
+	print_printer_details(device->name, device->bdaddr, device->id);
+}
+
+static gboolean parse_device_properties(DBusMessageIter *reply_iter,
+						char **name, char **bdaddr)
+{
+	guint32 class = 0;
+	DBusMessageIter reply_iter_entry;
+
+	if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	dbus_message_iter_recurse(reply_iter, &reply_iter_entry);
+
+	while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
+							DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter dict_entry, iter_dict_val;
+
+		dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
+
+		/* Key == Class ? */
+		dbus_message_iter_get_basic(&dict_entry, &key);
+		if (!key) {
+			dbus_message_iter_next(&reply_iter_entry);
+			continue;
+		}
+
+		if (strcmp(key, "Class") != 0 &&
+				strcmp(key, "Alias") != 0 &&
+				strcmp(key, "Address") != 0) {
+			dbus_message_iter_next(&reply_iter_entry);
+			continue;
+		}
+
+		/* Try to get the value */
+		if (!dbus_message_iter_next(&dict_entry)) {
+			dbus_message_iter_next(&reply_iter_entry);
+			continue;
+		}
+		dbus_message_iter_recurse(&dict_entry, &iter_dict_val);
+		if (strcmp(key, "Class") == 0) {
+			dbus_message_iter_get_basic(&iter_dict_val, &class);
+		} else {
+			const char *value;
+			dbus_message_iter_get_basic(&iter_dict_val, &value);
+			if (strcmp(key, "Alias") == 0) {
+				*name = g_strdup(value);
+			} else if (bdaddr) {
+				*bdaddr = g_strdup(value);
+			}
+		}
+		dbus_message_iter_next(&reply_iter_entry);
+	}
+
+	if (class == 0)
+		return FALSE;
+	if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr)
+{
+	DBusMessage *message, *reply;
+	DBusMessageIter reply_iter;
+	gboolean retval;
+
+	message = dbus_message_new_method_call("org.bluez", device_path,
+							"org.bluez.Device1",
+							"GetProperties");
+
+	reply = dbus_connection_send_with_reply_and_block(conn,
+							message, -1, NULL);
+
+	dbus_message_unref(message);
+
+	if (!reply)
+		return FALSE;
+
+	dbus_message_iter_init(reply, &reply_iter);
+
+	retval = parse_device_properties(&reply_iter, name, bdaddr);
+
+	dbus_message_unref(reply);
+
+	return retval;
+}
+
+static void remote_device_found(const char *adapter, const char *bdaddr,
+							const char *name)
+{
+	DBusMessage *message, *reply;
+	DBusMessageIter iter;
+	char *object_path = NULL;
+	char *id;
+
+	assert(adapter != NULL);
+
+	message = dbus_message_new_method_call("org.bluez", adapter,
+							"org.bluez.Adapter1",
+							"FindDevice");
+	dbus_message_iter_init_append(message, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+	reply = dbus_connection_send_with_reply_and_block(conn,
+							message, -1, NULL);
+
+	dbus_message_unref(message);
+
+	if (!reply) {
+		message = dbus_message_new_method_call("org.bluez", adapter,
+							"org.bluez.Adapter1",
+							"CreateDevice");
+		dbus_message_iter_init_append(message, &iter);
+		dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+		reply = dbus_connection_send_with_reply_and_block(conn,
+							message, -1, NULL);
+
+		dbus_message_unref(message);
+
+		if (!reply)
+			return;
+	}
+
+	if (dbus_message_get_args(reply, NULL,
+					DBUS_TYPE_OBJECT_PATH, &object_path,
+					DBUS_TYPE_INVALID) == FALSE) {
+		dbus_message_unref(reply);
+		return;
+	}
+
+	id = device_get_ieee1284_id(adapter, object_path);
+	add_device_to_list(name, bdaddr, id);
+	g_free(id);
+
+	dbus_message_unref(reply);
+}
+
+static void discovery_completed(void)
+{
+	g_slist_free(device_list);
+	device_list = NULL;
+
+	g_main_loop_quit(loop);
+}
+
+static void remote_device_disappeared(const char *bdaddr)
+{
+	GSList *l;
+
+	for (l = device_list; l != NULL; l = l->next) {
+		struct cups_device *device = l->data;
+
+		if (strcmp(device->bdaddr, bdaddr) == 0) {
+			g_free(device->name);
+			g_free(device->bdaddr);
+			g_free(device);
+			device_list = g_slist_delete_link(device_list, l);
+			return;
+		}
+	}
+}
+
+static gboolean list_known_printers(const char *adapter)
+{
+	DBusMessageIter reply_iter, iter_array;
+	DBusError error;
+	DBusMessage *message, *reply;
+
+	message = dbus_message_new_method_call("org.bluez", adapter,
+						"org.bluez.Adapter1",
+						"ListDevices");
+	if (message == NULL)
+		return FALSE;
+
+	dbus_error_init(&error);
+	reply = dbus_connection_send_with_reply_and_block(conn, message,
+								-1, &error);
+
+	dbus_message_unref(message);
+
+	if (dbus_error_is_set(&error)) {
+		dbus_error_free(&error);
+		return FALSE;
+	}
+
+	dbus_message_iter_init(reply, &reply_iter);
+	if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
+		dbus_message_unref(reply);
+		return FALSE;
+	}
+
+	dbus_message_iter_recurse(&reply_iter, &iter_array);
+	while (dbus_message_iter_get_arg_type(&iter_array) ==
+						DBUS_TYPE_OBJECT_PATH) {
+		const char *object_path;
+		char *name = NULL;
+		char *bdaddr = NULL;
+
+		dbus_message_iter_get_basic(&iter_array, &object_path);
+		if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
+			char *id;
+
+			id = device_get_ieee1284_id(adapter, object_path);
+			add_device_to_list(name, bdaddr, id);
+			g_free(id);
+		}
+		g_free(name);
+		g_free(bdaddr);
+		dbus_message_iter_next(&iter_array);
+	}
+
+	dbus_message_unref(reply);
+
+	return FALSE;
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	if (dbus_message_is_signal(message, "org.bluez.Adapter1",
+						"DeviceFound")) {
+		const char *adapter, *bdaddr;
+		char *name;
+		DBusMessageIter iter;
+
+		dbus_message_iter_init(message, &iter);
+		dbus_message_iter_get_basic(&iter, &bdaddr);
+		dbus_message_iter_next(&iter);
+
+		adapter = dbus_message_get_path(message);
+		if (parse_device_properties(&iter, &name, NULL))
+			remote_device_found(adapter, bdaddr, name);
+		g_free (name);
+	} else if (dbus_message_is_signal(message, "org.bluez.Adapter1",
+						"DeviceDisappeared")) {
+		const char *bdaddr;
+
+		dbus_message_get_args(message, NULL,
+					DBUS_TYPE_STRING, &bdaddr,
+					DBUS_TYPE_INVALID);
+		remote_device_disappeared(bdaddr);
+	} else if (dbus_message_is_signal(message, "org.bluez.Adapter1",
+						"PropertyChanged")) {
+		DBusMessageIter iter, value_iter;
+		const char *name;
+		gboolean discovering;
+
+		dbus_message_iter_init(message, &iter);
+		dbus_message_iter_get_basic(&iter, &name);
+		if (name == NULL || strcmp(name, "Discovering") != 0)
+			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+		dbus_message_iter_next(&iter);
+		dbus_message_iter_recurse(&iter, &value_iter);
+		dbus_message_iter_get_basic(&value_iter, &discovering);
+
+		if (discovering == FALSE && doing_disco) {
+			doing_disco = FALSE;
+			discovery_completed();
+		}
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean list_printers(void)
+{
+	/* 1. Connect to the bus
+	 * 2. Get the manager
+	 * 3. Get the default adapter
+	 * 4. Get a list of devices
+	 * 5. Get the class of each device
+	 * 6. Print the details from each printer device
+	 */
+	DBusError error;
+	dbus_bool_t hcid_exists;
+	DBusMessage *reply, *message;
+	DBusMessageIter reply_iter;
+	char *adapter, *match;
+
+	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+	if (conn == NULL)
+		return TRUE;
+
+	dbus_error_init(&error);
+	hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error);
+	if (dbus_error_is_set(&error)) {
+		dbus_error_free(&error);
+		return TRUE;
+	}
+
+	if (!hcid_exists)
+		return TRUE;
+
+	/* Get the default adapter */
+	message = dbus_message_new_method_call("org.bluez", "/",
+						"org.bluez.Manager",
+						"DefaultAdapter");
+	if (message == NULL) {
+		dbus_connection_unref(conn);
+		return FALSE;
+	}
+
+	reply = dbus_connection_send_with_reply_and_block(conn,
+							message, -1, &error);
+
+	dbus_message_unref(message);
+
+	if (dbus_error_is_set(&error)) {
+		dbus_error_free(&error);
+		dbus_connection_unref(conn);
+		/* No adapter */
+		return TRUE;
+	}
+
+	dbus_message_iter_init(reply, &reply_iter);
+	if (dbus_message_iter_get_arg_type(&reply_iter) !=
+						DBUS_TYPE_OBJECT_PATH) {
+		dbus_message_unref(reply);
+		dbus_connection_unref(conn);
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(&reply_iter, &adapter);
+	adapter = g_strdup(adapter);
+	dbus_message_unref(reply);
+
+	if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
+		g_free(adapter);
+		dbus_connection_unref(conn);
+		return FALSE;
+	}
+
+#define MATCH_FORMAT				\
+	"type='signal',"			\
+	"interface='org.bluez.Adapter1',"	\
+	"sender='org.bluez',"			\
+	"path='%s'"
+
+	match = g_strdup_printf(MATCH_FORMAT, adapter);
+	dbus_bus_add_match(conn, match, &error);
+	g_free(match);
+
+	/* Add the the recent devices */
+	list_known_printers(adapter);
+
+	doing_disco = TRUE;
+	message = dbus_message_new_method_call("org.bluez", adapter,
+					"org.bluez.Adapter1",
+					"StartDiscovery");
+
+	if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
+		dbus_message_unref(message);
+		dbus_connection_unref(conn);
+		g_free(adapter);
+		return FALSE;
+	}
+	dbus_message_unref(message);
+
+	loop = g_main_loop_new(NULL, TRUE);
+	g_main_loop_run(loop);
+
+	g_free(adapter);
+	dbus_connection_unref(conn);
+
+	return TRUE;
+}
+
+static gboolean print_ieee1284(const char *bdaddr)
+{
+	DBusMessage *message, *reply, *adapter_reply;
+	DBusMessageIter iter;
+	char *object_path = NULL;
+	char *adapter;
+	char *id;
+
+	adapter_reply = NULL;
+
+	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+	if (conn == NULL)
+		return FALSE;
+
+	message = dbus_message_new_method_call("org.bluez", "/",
+			"org.bluez.Manager",
+			"DefaultAdapter");
+
+	adapter_reply = dbus_connection_send_with_reply_and_block(conn,
+			message, -1, NULL);
+
+	dbus_message_unref(message);
+
+	if (!adapter_reply)
+		return FALSE;
+
+	if (dbus_message_get_args(adapter_reply, NULL,
+			DBUS_TYPE_OBJECT_PATH, &adapter,
+			DBUS_TYPE_INVALID) == FALSE) {
+		dbus_message_unref(adapter_reply);
+		return FALSE;
+	}
+
+	message = dbus_message_new_method_call("org.bluez", adapter,
+			"org.bluez.Adapter1",
+			"FindDevice");
+	dbus_message_iter_init_append(message, &iter);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+	if (adapter_reply != NULL)
+		dbus_message_unref(adapter_reply);
+
+	reply = dbus_connection_send_with_reply_and_block(conn,
+			message, -1, NULL);
+
+	dbus_message_unref(message);
+
+	if (!reply) {
+		message = dbus_message_new_method_call("org.bluez", adapter,
+				"org.bluez.Adapter1",
+				"CreateDevice");
+		dbus_message_iter_init_append(message, &iter);
+		dbus_message_iter_append_basic(&iter,
+				DBUS_TYPE_STRING, &bdaddr);
+
+		reply = dbus_connection_send_with_reply_and_block(conn,
+				message, -1, NULL);
+
+		dbus_message_unref(message);
+
+		if (!reply)
+			return FALSE;
+	}
+
+	if (dbus_message_get_args(reply, NULL,
+					DBUS_TYPE_OBJECT_PATH, &object_path,
+					DBUS_TYPE_INVALID) == FALSE) {
+		dbus_message_unref(reply);
+		return FALSE;
+	}
+
+	id = device_get_ieee1284_id(adapter, object_path);
+	if (id == NULL) {
+		dbus_message_unref(reply);
+		return FALSE;
+	}
+	printf("%s", id);
+	g_free(id);
+
+	dbus_message_unref(reply);
+
+	return TRUE;
+}
+
+/*
+ *  Usage: printer-uri job-id user title copies options [file]
+ *
+ */
+
+int main(int argc, char *argv[])
+{
+	sdp_session_t *sdp;
+	bdaddr_t bdaddr;
+	unsigned short ctrl_psm, data_psm;
+	uint8_t channel, b[6];
+	char *ptr, str[3], device[18], service[12];
+	const char *uri, *cups_class;
+	int i, err, fd, copies, proto;
+
+	/* Make sure status messages are not buffered */
+	setbuf(stderr, NULL);
+
+	/* Make sure output is not buffered */
+	setbuf(stdout, NULL);
+
+	/* Ignore SIGPIPE signals */
+#ifdef HAVE_SIGSET
+	sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+	memset(&action, 0, sizeof(action));
+	action.sa_handler = SIG_IGN;
+	sigaction(SIGPIPE, &action, NULL);
+#else
+	signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+	if (argc == 1) {
+		if (list_printers() == TRUE)
+			return CUPS_BACKEND_OK;
+		else
+			return CUPS_BACKEND_FAILED;
+	} else if (argc == 3 && strcmp(argv[1], "--get-deviceid") == 0) {
+		if (bachk(argv[2]) < 0) {
+			fprintf(stderr, "Invalid Bluetooth address '%s'\n",
+					argv[2]);
+			return CUPS_BACKEND_FAILED;
+		}
+		if (print_ieee1284(argv[2]) == FALSE)
+			return CUPS_BACKEND_FAILED;
+		return CUPS_BACKEND_OK;
+	}
+
+	if (argc < 6 || argc > 7) {
+		fprintf(stderr, "Usage: bluetooth job-id user title copies"
+				" options [file]\n");
+		fprintf(stderr, "       bluetooth --get-deviceid [bdaddr]\n");
+		return CUPS_BACKEND_FAILED;
+	}
+
+	if (argc == 6) {
+		fd = 0;
+		copies = 1;
+	} else {
+		if ((fd = open(argv[6], O_RDONLY)) < 0) {
+			perror("ERROR: Unable to open print file");
+			return CUPS_BACKEND_FAILED;
+		}
+		copies = atoi(argv[4]);
+	}
+
+	uri = getenv("DEVICE_URI");
+	if (!uri)
+		uri = argv[0];
+
+	if (strncasecmp(uri, "bluetooth://", 12)) {
+		fprintf(stderr, "ERROR: No device URI found\n");
+		return CUPS_BACKEND_FAILED;
+	}
+
+	ptr = argv[0] + 12;
+	for (i = 0; i < 6; i++) {
+		strncpy(str, ptr, 2);
+		b[i] = (uint8_t) strtol(str, NULL, 16);
+		ptr += 2;
+	}
+	sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+			b[0], b[1], b[2], b[3], b[4], b[5]);
+
+	str2ba(device, &bdaddr);
+
+	ptr = strchr(ptr, '/');
+	if (ptr) {
+		strncpy(service, ptr + 1, 12);
+
+		if (!strncasecmp(ptr + 1, "spp", 3))
+			proto = 1;
+		else if (!strncasecmp(ptr + 1, "hcrp", 4))
+			proto = 2;
+		else
+			proto = 0;
+	} else {
+		strcpy(service, "auto");
+		proto = 0;
+	}
+
+	cups_class = getenv("CLASS");
+
+	fprintf(stderr,
+		"DEBUG: %s device %s service %s fd %d copies %d class %s\n",
+			argv[0], device, service, fd, copies,
+			cups_class ? cups_class : "(none)");
+
+	fputs("STATE: +connecting-to-device\n", stderr);
+
+service_search:
+	sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
+	if (!sdp) {
+		fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
+		return CUPS_BACKEND_FAILED;
+	}
+
+	switch (proto) {
+	case 1:
+		err = sdp_search_spp(sdp, &channel);
+		break;
+	case 2:
+		err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
+		break;
+	default:
+		proto = 2;
+		err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
+		if (err) {
+			proto = 1;
+			err = sdp_search_spp(sdp, &channel);
+		}
+		break;
+	}
+
+	sdp_close(sdp);
+
+	if (err) {
+		if (cups_class) {
+			fputs("INFO: Unable to contact printer, queuing on "
+					"next printer in class...\n", stderr);
+			sleep(5);
+			return CUPS_BACKEND_FAILED;
+		}
+		sleep(20);
+		fprintf(stderr, "ERROR: Can't get service information\n");
+		goto service_search;
+	}
+
+connect:
+	switch (proto) {
+	case 1:
+		err = spp_print(BDADDR_ANY, &bdaddr, channel,
+						fd, copies, cups_class);
+		break;
+	case 2:
+		err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
+						fd, copies, cups_class);
+		break;
+	default:
+		err = CUPS_BACKEND_FAILED;
+		fprintf(stderr, "ERROR: Unsupported protocol\n");
+		break;
+	}
+
+	if (err == CUPS_BACKEND_FAILED && cups_class) {
+		fputs("INFO: Unable to contact printer, queuing on "
+					"next printer in class...\n", stderr);
+		sleep(5);
+		return CUPS_BACKEND_FAILED;
+	} else if (err == CUPS_BACKEND_RETRY) {
+		sleep(20);
+		goto connect;
+	}
+
+	if (fd != 0)
+		close(fd);
+
+	if (!err)
+		fprintf(stderr, "INFO: Ready to print\n");
+
+	return err;
+}
diff --git a/bluez/repo/profiles/cups/sdp.c b/bluez/repo/profiles/cups/sdp.c
new file mode 100644
index 0000000..de9cd4e
--- /dev/null
+++ b/bluez/repo/profiles/cups/sdp.c
@@ -0,0 +1,119 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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 <unistd.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "cups.h"
+
+int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm)
+{
+	sdp_list_t *srch, *attrs, *rsp;
+	uuid_t svclass;
+	uint16_t attr1, attr2;
+	int err;
+
+	if (!sdp)
+		return -1;
+
+	sdp_uuid16_create(&svclass, HCR_PRINT_SVCLASS_ID);
+	srch = sdp_list_append(NULL, &svclass);
+
+	attr1 = SDP_ATTR_PROTO_DESC_LIST;
+	attrs = sdp_list_append(NULL, &attr1);
+	attr2 = SDP_ATTR_ADD_PROTO_DESC_LIST;
+	attrs = sdp_list_append(attrs, &attr2);
+
+	err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+	if (err)
+		return -1;
+
+	for (; rsp; rsp = rsp->next) {
+		sdp_record_t *rec = (sdp_record_t *) rsp->data;
+		sdp_list_t *protos;
+
+		if (!sdp_get_access_protos(rec, &protos)) {
+			unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID);
+			if (psm > 0) {
+				*ctrl_psm = psm;
+			}
+		}
+
+		if (!sdp_get_add_access_protos(rec, &protos)) {
+			unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID);
+			if (psm > 0 && *ctrl_psm > 0) {
+				*data_psm = psm;
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
+
+int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel)
+{
+	sdp_list_t *srch, *attrs, *rsp;
+	uuid_t svclass;
+	uint16_t attr;
+	int err;
+
+	if (!sdp)
+		return -1;
+
+	sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+	srch = sdp_list_append(NULL, &svclass);
+
+	attr = SDP_ATTR_PROTO_DESC_LIST;
+	attrs = sdp_list_append(NULL, &attr);
+
+	err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+	if (err)
+		return -1;
+
+	for (; rsp; rsp = rsp->next) {
+		sdp_record_t *rec = (sdp_record_t *) rsp->data;
+		sdp_list_t *protos;
+
+		if (!sdp_get_access_protos(rec, &protos)) {
+			uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+			if (ch > 0) {
+				*channel = ch;
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
diff --git a/bluez/repo/profiles/cups/spp.c b/bluez/repo/profiles/cups/spp.c
new file mode 100644
index 0000000..2f1e270
--- /dev/null
+++ b/bluez/repo/profiles/cups/spp.c
@@ -0,0 +1,118 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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 <unistd.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/rfcomm.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "cups.h"
+
+int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class)
+{
+	struct sockaddr_rc addr;
+	unsigned char buf[2048];
+	int i, sk, err, len;
+
+	if ((sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+		perror("ERROR: Can't create socket");
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = 0;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("ERROR: Can't bind socket");
+		close(sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, dst);
+	addr.rc_channel = channel;
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("ERROR: Can't connect to device");
+		close(sk);
+		if (cups_class)
+			return CUPS_BACKEND_FAILED;
+		else
+			return CUPS_BACKEND_RETRY;
+	}
+
+	fputs("STATE: -connecting-to-device\n", stderr);
+
+	/* Ignore SIGTERM signals if printing from stdin */
+	if (fd == 0) {
+#ifdef HAVE_SIGSET
+		sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+		memset(&action, 0, sizeof(action));
+		sigemptyset(&action.sa_mask);
+		action.sa_handler = SIG_IGN;
+		sigaction(SIGTERM, &action, NULL);
+#else
+		signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+	}
+
+	for (i = 0; i < copies; i++) {
+
+		if (fd != 0) {
+			fprintf(stderr, "PAGE: 1 1\n");
+			lseek(fd, 0, SEEK_SET);
+		}
+
+		while ((len = read(fd, buf, sizeof(buf))) > 0) {
+			err = write(sk, buf, len);
+			if (err < 0) {
+				perror("ERROR: Error writing to device");
+				close(sk);
+				return CUPS_BACKEND_FAILED;
+			}
+		}
+
+	}
+
+	close(sk);
+
+	return CUPS_BACKEND_OK;
+}
diff --git a/bluez/repo/profiles/cyclingspeed/._cyclingspeed.c b/bluez/repo/profiles/cyclingspeed/._cyclingspeed.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/cyclingspeed/._cyclingspeed.c
Binary files differ
diff --git a/bluez/repo/profiles/cyclingspeed/cyclingspeed.c b/bluez/repo/profiles/cyclingspeed/cyclingspeed.c
new file mode 100644
index 0000000..e447725
--- /dev/null
+++ b/bluez/repo/profiles/cyclingspeed/cyclingspeed.c
@@ -0,0 +1,1266 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Tieto Poland
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/log.h"
+
+/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
+#define ATT_HDR_LEN 3
+
+#define ATT_TIMEOUT 30
+
+#define CYCLINGSPEED_INTERFACE		"org.bluez.CyclingSpeed1"
+#define CYCLINGSPEED_MANAGER_INTERFACE	"org.bluez.CyclingSpeedManager1"
+#define CYCLINGSPEED_WATCHER_INTERFACE	"org.bluez.CyclingSpeedWatcher1"
+
+#define WHEEL_REV_SUPPORT		0x01
+#define CRANK_REV_SUPPORT		0x02
+#define MULTI_SENSOR_LOC_SUPPORT	0x04
+
+#define WHEEL_REV_PRESENT	0x01
+#define CRANK_REV_PRESENT	0x02
+
+#define SET_CUMULATIVE_VALUE		0x01
+#define START_SENSOR_CALIBRATION	0x02
+#define UPDATE_SENSOR_LOC		0x03
+#define REQUEST_SUPPORTED_SENSOR_LOC	0x04
+#define RESPONSE_CODE			0x10
+
+#define RSP_SUCCESS		0x01
+#define RSP_NOT_SUPPORTED	0x02
+#define RSP_INVALID_PARAM	0x03
+#define RSP_FAILED		0x04
+
+struct csc;
+
+struct controlpoint_req {
+	struct csc		*csc;
+	uint8_t			opcode;
+	guint			timeout;
+	GDBusPendingReply	reply_id;
+	DBusMessage		*msg;
+
+	uint8_t			pending_location;
+};
+
+struct csc_adapter {
+	struct btd_adapter	*adapter;
+	GSList			*devices;	/* list of registered devices */
+	GSList			*watchers;
+};
+
+struct csc {
+	struct btd_device	*dev;
+	struct csc_adapter	*cadapter;
+
+	GAttrib			*attrib;
+	guint			attioid;
+	/* attio id for measurement characteristics value notifications */
+	guint			attio_measurement_id;
+	/* attio id for SC Control Point characteristics value indications */
+	guint			attio_controlpoint_id;
+
+	struct att_range	*svc_range;
+
+	uint16_t		measurement_ccc_handle;
+	uint16_t		controlpoint_val_handle;
+
+	uint16_t		feature;
+	gboolean		has_location;
+	uint8_t			location;
+	uint8_t			num_locations;
+	uint8_t			*locations;
+
+	struct controlpoint_req	*pending_req;
+};
+
+struct watcher {
+	struct csc_adapter	*cadapter;
+	guint			id;
+	char			*srv;
+	char			*path;
+};
+
+struct measurement {
+	struct csc	*csc;
+
+	bool		has_wheel_rev;
+	uint32_t	wheel_rev;
+	uint16_t	last_wheel_time;
+
+	bool		has_crank_rev;
+	uint16_t	crank_rev;
+	uint16_t	last_crank_time;
+};
+
+struct characteristic {
+	struct csc	*csc;
+	char		uuid[MAX_LEN_UUID_STR + 1];
+};
+
+static GSList *csc_adapters = NULL;
+
+static const char * const location_enum[] = {
+	"other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
+	"right-crank", "left-pedal", "right-pedal", "front-hub",
+	"rear-dropout", "chainstay", "rear-wheel", "rear-hub"
+};
+
+static const char *location2str(uint8_t value)
+{
+	if (value < G_N_ELEMENTS(location_enum))
+		return location_enum[value];
+
+	info("Body Sensor Location [%d] is RFU", value);
+
+	return location_enum[0];
+}
+
+static int str2location(const char *location)
+{
+	size_t i;
+
+	for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
+		if (!strcmp(location_enum[i], location))
+			return i;
+
+	return -1;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+	const struct csc_adapter *cadapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (adapter == cadapter->adapter)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct csc *csc = a;
+	const struct btd_device *dev = b;
+
+	if (dev == csc->dev)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+	const struct watcher *watcher = a;
+	const struct watcher *match = b;
+	int ret;
+
+	ret = g_strcmp0(watcher->srv, match->srv);
+	if (ret != 0)
+		return ret;
+
+	return g_strcmp0(watcher->path, match->path);
+}
+
+static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
+
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_free(watcher->path);
+	g_free(watcher->srv);
+	g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+							const char *path)
+{
+	struct watcher *match;
+	GSList *l;
+
+	match = g_new0(struct watcher, 1);
+	match->srv = g_strdup(sender);
+	match->path = g_strdup(path);
+
+	l = g_slist_find_custom(list, match, cmp_watcher);
+	destroy_watcher(match);
+
+	if (l != NULL)
+		return l->data;
+
+	return NULL;
+}
+
+static void remove_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_csc_adapter(gpointer user_data)
+{
+	struct csc_adapter *cadapter = user_data;
+
+	g_slist_free_full(cadapter->watchers, remove_watcher);
+
+	g_free(cadapter);
+}
+
+static void destroy_csc(gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	if (csc->attioid > 0)
+		btd_device_remove_attio_callback(csc->dev, csc->attioid);
+
+	if (csc->attrib != NULL) {
+		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+		g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+		g_attrib_unref(csc->attrib);
+	}
+
+	btd_device_unref(csc->dev);
+	g_free(csc->svc_range);
+	g_free(csc->locations);
+	g_free(csc);
+}
+
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	char *msg = user_data;
+
+	if (status != 0)
+		error("%s failed", msg);
+
+	g_free(msg);
+}
+
+static gboolean controlpoint_timeout(gpointer user_data)
+{
+	struct controlpoint_req *req = user_data;
+
+	if (req->opcode == UPDATE_SENSOR_LOC) {
+		g_dbus_pending_property_error(req->reply_id,
+						ERROR_INTERFACE ".Failed",
+						"Operation failed (timeout)");
+	} else if (req->opcode == SET_CUMULATIVE_VALUE) {
+		DBusMessage *reply;
+
+		reply = btd_error_failed(req->msg,
+						"Operation failed (timeout)");
+
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+		dbus_message_unref(req->msg);
+	}
+
+	req->csc->pending_req = NULL;
+	g_free(req);
+
+	return FALSE;
+}
+
+static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct controlpoint_req *req = user_data;
+
+	if (status == 0) {
+		req->timeout = g_timeout_add_seconds(ATT_TIMEOUT,
+							controlpoint_timeout,
+							req);
+		return;
+	}
+
+	error("SC Control Point write failed (opcode=%d)", req->opcode);
+
+	if (req->opcode == UPDATE_SENSOR_LOC) {
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".Failed",
+					"Operation failed (%d)", status);
+	} else if  (req->opcode == SET_CUMULATIVE_VALUE) {
+		DBusMessage *reply;
+
+		reply = btd_error_failed(req->msg, "Operation failed");
+
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+		dbus_message_unref(req->msg);
+	}
+
+	req->csc->pending_req = NULL;
+	g_free(req);
+}
+
+static void read_supported_locations(struct csc *csc)
+{
+	struct controlpoint_req *req;
+
+	req = g_new0(struct controlpoint_req, 1);
+	req->csc = csc;
+	req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
+
+	csc->pending_req = req;
+
+	gatt_write_char(csc->attrib, csc->controlpoint_val_handle,
+					&req->opcode, sizeof(req->opcode),
+					controlpoint_write_cb, req);
+}
+
+static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct csc *csc = user_data;
+	uint8_t value[2];
+	ssize_t vlen;
+
+	if (status) {
+		error("CSC Feature read failed: %s", att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, value, sizeof(value));
+	if (vlen < 0) {
+		error("Protocol error");
+		return;
+	}
+
+	if (vlen != sizeof(value)) {
+		error("Invalid value length for CSC Feature");
+		return;
+	}
+
+	csc->feature = get_le16(value);
+
+	if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
+						&& (csc->locations == NULL))
+		read_supported_locations(csc);
+}
+
+static void read_location_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct csc *csc = user_data;
+	uint8_t value;
+	ssize_t vlen;
+
+	if (status) {
+		error("Sensor Location read failed: %s", att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+	if (vlen < 0) {
+		error("Protocol error");
+		return;
+	}
+
+	if (vlen != sizeof(value)) {
+		error("Invalid value length for Sensor Location");
+		return;
+	}
+
+	csc->has_location = TRUE;
+	csc->location = value;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					device_get_path(csc->dev),
+					CYCLINGSPEED_INTERFACE, "Location");
+}
+
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
+{
+	struct characteristic *ch = user_data;
+	struct gatt_desc *desc;
+	uint8_t attr_val[2];
+	char *msg = NULL;
+
+	if (status != 0) {
+		error("Discover %s descriptors failed: %s", ch->uuid,
+							att_ecode2str(status));
+		goto done;
+	}
+
+	/* There will be only one descriptor on list and it will be CCC */
+	desc = descs->data;
+
+	if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+		ch->csc->measurement_ccc_handle = desc->handle;
+
+		if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+			put_le16(0x0000, attr_val);
+			msg = g_strdup("Disable measurement");
+		} else {
+			put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+							attr_val);
+			msg = g_strdup("Enable measurement");
+		}
+	} else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+		put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+		msg = g_strdup("Enable SC Control Point indications");
+	} else {
+		goto done;
+	}
+
+	gatt_write_char(ch->csc->attrib, desc->handle, attr_val,
+					sizeof(attr_val), char_write_cb, msg);
+
+done:
+	g_free(ch);
+}
+
+static void discover_desc(struct csc *csc, struct gatt_char *c,
+						struct gatt_char *c_next)
+{
+	struct characteristic *ch;
+	uint16_t start, end;
+	bt_uuid_t uuid;
+
+	start = c->value_handle + 1;
+
+	if (c_next != NULL) {
+		if (start == c_next->handle)
+			return;
+		end = c_next->handle - 1;
+	} else if (c->value_handle != csc->svc_range->end) {
+		end = csc->svc_range->end;
+	} else {
+		return;
+	}
+
+	ch = g_new0(struct characteristic, 1);
+	ch->csc = csc;
+	memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+	gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb,
+									ch);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+	struct watcher *w = data;
+	struct measurement *m = user_data;
+	struct csc *csc = m->csc;
+	const char *path = device_get_path(csc->dev);
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(w->srv, w->path,
+			CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	if (m->has_wheel_rev) {
+		dict_append_entry(&dict, "WheelRevolutions",
+					DBUS_TYPE_UINT32, &m->wheel_rev);
+		dict_append_entry(&dict, "LastWheelEventTime",
+					DBUS_TYPE_UINT16, &m->last_wheel_time);
+	}
+
+	if (m->has_crank_rev) {
+		dict_append_entry(&dict, "CrankRevolutions",
+					DBUS_TYPE_UINT16, &m->crank_rev);
+		dict_append_entry(&dict, "LastCrankEventTime",
+					DBUS_TYPE_UINT16, &m->last_crank_time);
+	}
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct csc *csc, const uint8_t *pdu,
+								uint16_t len)
+{
+	struct measurement m;
+	uint8_t flags;
+
+	flags = *pdu;
+
+	pdu++;
+	len--;
+
+	memset(&m, 0, sizeof(m));
+
+	if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
+		if (len < 6) {
+			error("Wheel revolutions data fields missing");
+			return;
+		}
+
+		m.has_wheel_rev = true;
+		m.wheel_rev = get_le32(pdu);
+		m.last_wheel_time = get_le16(pdu + 4);
+		pdu += 6;
+		len -= 6;
+	}
+
+	if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
+		if (len < 4) {
+			error("Crank revolutions data fields missing");
+			return;
+		}
+
+		m.has_crank_rev = true;
+		m.crank_rev = get_le16(pdu);
+		m.last_crank_time = get_le16(pdu + 2);
+		pdu += 4;
+		len -= 4;
+	}
+
+	/* Notify all registered watchers */
+	m.csc = csc;
+	g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
+}
+
+static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	/* should be at least opcode (1b) + handle (2b) */
+	if (len < 3) {
+		error("Invalid PDU received");
+		return;
+	}
+
+	process_measurement(csc, pdu + 3, len - 3);
+}
+
+static void controlpoint_property_reply(struct controlpoint_req *req,
+								uint8_t code)
+{
+	switch (code) {
+	case RSP_SUCCESS:
+		g_dbus_pending_property_success(req->reply_id);
+		break;
+
+	case RSP_NOT_SUPPORTED:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".NotSupported",
+					"Feature is not supported");
+		break;
+
+	case RSP_INVALID_PARAM:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		break;
+
+	case RSP_FAILED:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".Failed",
+					"Operation failed");
+		break;
+
+	default:
+		g_dbus_pending_property_error(req->reply_id,
+					ERROR_INTERFACE ".Failed",
+					"Operation failed (%d)", code);
+		break;
+	}
+}
+
+static void controlpoint_method_reply(struct controlpoint_req *req,
+								uint8_t code)
+{
+	DBusMessage *reply;
+
+	switch (code) {
+	case RSP_SUCCESS:
+		reply = dbus_message_new_method_return(req->msg);
+		break;
+	case RSP_NOT_SUPPORTED:
+		reply = btd_error_not_supported(req->msg);
+		break;
+	case RSP_INVALID_PARAM:
+		reply = btd_error_invalid_args(req->msg);
+		break;
+	case RSP_FAILED:
+		reply = btd_error_failed(req->msg, "Failed");
+		break;
+	default:
+		reply = btd_error_failed(req->msg, "Unknown error");
+		break;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+	dbus_message_unref(req->msg);
+}
+
+static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct csc *csc = user_data;
+	struct controlpoint_req *req = csc->pending_req;
+	uint8_t opcode;
+	uint8_t req_opcode;
+	uint8_t rsp_code;
+	uint8_t *opdu;
+	uint16_t olen;
+	size_t plen;
+
+	if (len < ATT_HDR_LEN) {
+		error("Invalid PDU received");
+		return;
+	}
+
+	/* skip ATT header */
+	pdu += ATT_HDR_LEN;
+	len -= ATT_HDR_LEN;
+
+	if (len < 1) {
+		error("Op Code missing");
+		goto done;
+	}
+
+	opcode = *pdu;
+	pdu++;
+	len--;
+
+	if (opcode != RESPONSE_CODE) {
+		DBG("Unsupported Op Code received (%d)", opcode);
+		goto done;
+	}
+
+	if (len < 2) {
+		error("Invalid Response Code PDU received");
+		goto done;
+	}
+
+	req_opcode = *pdu;
+	rsp_code = *(pdu + 1);
+	pdu += 2;
+	len -= 2;
+
+	if (req == NULL || req->opcode != req_opcode) {
+		DBG("Indication received without pending request");
+		goto done;
+	}
+
+	switch (req->opcode) {
+	case SET_CUMULATIVE_VALUE:
+		controlpoint_method_reply(req, rsp_code);
+		break;
+
+	case REQUEST_SUPPORTED_SENSOR_LOC:
+		if (rsp_code == RSP_SUCCESS) {
+			csc->num_locations = len;
+			csc->locations = g_memdup(pdu, len);
+		} else {
+			error("Failed to read Supported Sendor Locations");
+		}
+		break;
+
+	case UPDATE_SENSOR_LOC:
+		csc->location = req->pending_location;
+
+		controlpoint_property_reply(req, rsp_code);
+
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					device_get_path(csc->dev),
+					CYCLINGSPEED_INTERFACE, "Location");
+		break;
+	}
+
+	csc->pending_req = NULL;
+	g_source_remove(req->timeout);
+	g_free(req);
+
+done:
+	opdu = g_attrib_get_buffer(csc->attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+	if (olen > 0)
+		g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+	struct csc *csc = user_data;
+	uint16_t feature_val_handle = 0;
+
+	if (status) {
+		error("Discover CSCS characteristics: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (; chars; chars = chars->next) {
+		struct gatt_char *c = chars->data;
+		struct gatt_char *c_next =
+				(chars->next ? chars->next->data : NULL);
+
+		if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+			csc->attio_measurement_id =
+				g_attrib_register(csc->attrib,
+					ATT_OP_HANDLE_NOTIFY, c->value_handle,
+					measurement_notify_handler, csc, NULL);
+
+			discover_desc(csc, c, c_next);
+		} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
+			feature_val_handle = c->value_handle;
+		} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
+			DBG("Sensor Location supported");
+			gatt_read_char(csc->attrib, c->value_handle,
+							read_location_cb, csc);
+		} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
+			DBG("SC Control Point supported");
+			csc->controlpoint_val_handle = c->value_handle;
+
+			csc->attio_controlpoint_id = g_attrib_register(
+					csc->attrib, ATT_OP_HANDLE_IND,
+					c->value_handle,
+					controlpoint_ind_handler, csc, NULL);
+
+			discover_desc(csc, c, c_next);
+		}
+	}
+
+	if (feature_val_handle > 0)
+		gatt_read_char(csc->attrib, feature_val_handle,
+							read_feature_cb, csc);
+}
+
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+	struct csc *csc = data;
+	uint16_t handle = csc->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (csc->attrib == NULL || !handle)
+		return;
+
+	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+	msg = g_strdup("Enable measurement");
+
+	gatt_write_char(csc->attrib, handle, value, sizeof(value),
+							char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+	struct csc *csc = data;
+	uint16_t handle = csc->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (csc->attrib == NULL || !handle)
+		return;
+
+	put_le16(0x0000, value);
+	msg = g_strdup("Disable measurement");
+
+	gatt_write_char(csc->attrib, handle, value, sizeof(value),
+							char_write_cb, msg);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	DBG("");
+
+	csc->attrib = g_attrib_ref(attrib);
+
+	gatt_discover_char(csc->attrib, csc->svc_range->start,
+						csc->svc_range->end, NULL,
+						discover_char_cb, csc);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct csc *csc = user_data;
+
+	DBG("");
+
+	if (csc->attio_measurement_id > 0) {
+		g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+		csc->attio_measurement_id = 0;
+	}
+
+	if (csc->attio_controlpoint_id > 0) {
+		g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+		csc->attio_controlpoint_id = 0;
+	}
+
+	g_attrib_unref(csc->attrib);
+	csc->attrib = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+	struct watcher *watcher = user_data;
+	struct csc_adapter *cadapter = watcher->cadapter;
+
+	DBG("cycling watcher [%s] disconnected", watcher->path);
+
+	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+
+	if (g_slist_length(cadapter->watchers) == 0)
+		g_slist_foreach(cadapter->devices, disable_measurement, 0);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct csc_adapter *cadapter = data;
+	struct watcher *watcher;
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(cadapter->watchers, sender, path);
+	if (watcher != NULL)
+		return btd_error_already_exists(msg);
+
+	watcher = g_new0(struct watcher, 1);
+	watcher->cadapter = cadapter;
+	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+						watcher, destroy_watcher);
+	watcher->srv = g_strdup(sender);
+	watcher->path = g_strdup(path);
+
+	if (g_slist_length(cadapter->watchers) == 0)
+		g_slist_foreach(cadapter->devices, enable_measurement, 0);
+
+	cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
+
+	DBG("cycling watcher [%s] registered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct csc_adapter *cadapter = data;
+	struct watcher *watcher;
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(cadapter->watchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+
+	if (g_slist_length(cadapter->watchers) == 0)
+		g_slist_foreach(cadapter->devices, disable_measurement, 0);
+
+	DBG("cycling watcher [%s] unregistered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cyclingspeed_manager_methods[] = {
+	{ GDBUS_METHOD("RegisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			register_watcher) },
+	{ GDBUS_METHOD("UnregisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			unregister_watcher) },
+	{ }
+};
+
+static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	struct csc_adapter *cadapter;
+
+	cadapter = g_new0(struct csc_adapter, 1);
+	cadapter->adapter = adapter;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						adapter_get_path(adapter),
+						CYCLINGSPEED_MANAGER_INTERFACE,
+						cyclingspeed_manager_methods,
+						NULL, NULL, cadapter,
+						destroy_csc_adapter)) {
+		error("D-Bus failed to register %s interface",
+						CYCLINGSPEED_MANAGER_INTERFACE);
+		destroy_csc_adapter(cadapter);
+		return -EIO;
+	}
+
+	csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+
+	return 0;
+}
+
+static void csc_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct csc_adapter *cadapter;
+
+	cadapter = find_csc_adapter(adapter);
+	if (cadapter == NULL)
+		return;
+
+	csc_adapters = g_slist_remove(csc_adapters, cadapter);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(cadapter->adapter),
+					CYCLINGSPEED_MANAGER_INTERFACE);
+}
+
+static gboolean property_get_location(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	const char *loc;
+
+	if (!csc->has_location)
+		return FALSE;
+
+	loc = location2str(csc->location);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+	return TRUE;
+}
+
+static void property_set_location(const GDBusPropertyTable *property,
+					DBusMessageIter *iter,
+					GDBusPendingPropertySet id, void *data)
+{
+	struct csc *csc = data;
+	char *loc;
+	int loc_val;
+	uint8_t att_val[2];
+	struct controlpoint_req *req;
+
+	if (csc->pending_req != NULL) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InProgress",
+					"Operation already in progress");
+		return;
+	}
+
+	if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".NotSupported",
+					"Feature is not supported");
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &loc);
+
+	loc_val = str2location(loc);
+
+	if (loc_val < 0) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	req = g_new(struct controlpoint_req, 1);
+	req->csc = csc;
+	req->reply_id = id;
+	req->opcode = UPDATE_SENSOR_LOC;
+	req->pending_location = loc_val;
+
+	csc->pending_req = req;
+
+	att_val[0] = UPDATE_SENSOR_LOC;
+	att_val[1] = loc_val;
+
+	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+				sizeof(att_val), controlpoint_write_cb, req);
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct csc *csc = data;
+
+	return csc->has_location;
+}
+
+static gboolean property_get_locations(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	DBusMessageIter entry;
+	int i;
+
+	if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+		return FALSE;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &entry);
+	for (i = 0; i < csc->num_locations; i++) {
+		char *loc = g_strdup(location2str(csc->locations[i]));
+		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
+		g_free(loc);
+	}
+
+	dbus_message_iter_close_container(iter, &entry);
+
+	return TRUE;
+}
+
+static gboolean property_exists_locations(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct csc *csc = data;
+
+	return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+}
+
+static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	dbus_bool_t val;
+
+	val = !!(csc->feature & WHEEL_REV_SUPPORT);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct csc *csc = data;
+	dbus_bool_t val;
+
+	val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable cyclingspeed_device_properties[] = {
+	{ "Location", "s", property_get_location, property_set_location,
+						property_exists_location },
+	{ "SupportedLocations", "as", property_get_locations, NULL,
+						property_exists_locations },
+	{ "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
+	{ "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
+	{ }
+};
+
+static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct csc *csc = data;
+	dbus_uint32_t value;
+	struct controlpoint_req *req;
+	uint8_t att_val[5]; /* uint8 opcode + uint32 value */
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	if (csc->pending_req != NULL)
+		return btd_error_in_progress(msg);
+
+	req = g_new(struct controlpoint_req, 1);
+	req->csc = csc;
+	req->opcode = SET_CUMULATIVE_VALUE;
+	req->msg = dbus_message_ref(msg);
+
+	csc->pending_req = req;
+
+	att_val[0] = SET_CUMULATIVE_VALUE;
+	put_le32(value, att_val + 1);
+
+	gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+		sizeof(att_val), controlpoint_write_cb, req);
+
+	return NULL;
+}
+
+static const GDBusMethodTable cyclingspeed_device_methods[] = {
+	{ GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
+				GDBUS_ARGS({ "value", "u" }), NULL,
+						set_cumulative_wheel_rev) },
+	{ }
+};
+
+static int csc_device_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct btd_adapter *adapter;
+	struct csc_adapter *cadapter;
+	struct csc *csc;
+	struct gatt_primary *prim;
+
+	prim = btd_device_get_primary(device, CYCLING_SC_UUID);
+	if (prim == NULL)
+		return -EINVAL;
+
+	adapter = device_get_adapter(device);
+
+	cadapter = find_csc_adapter(adapter);
+	if (cadapter == NULL)
+		return -1;
+
+	csc = g_new0(struct csc, 1);
+	csc->dev = btd_device_ref(device);
+	csc->cadapter = cadapter;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						device_get_path(device),
+						CYCLINGSPEED_INTERFACE,
+						cyclingspeed_device_methods,
+						NULL,
+						cyclingspeed_device_properties,
+						csc, destroy_csc)) {
+		error("D-Bus failed to register %s interface",
+						CYCLINGSPEED_INTERFACE);
+		destroy_csc(csc);
+		return -EIO;
+	}
+
+	csc->svc_range = g_new0(struct att_range, 1);
+	csc->svc_range->start = prim->range.start;
+	csc->svc_range->end = prim->range.end;
+
+	cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+
+	csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+						attio_disconnected_cb, csc);
+
+	return 0;
+}
+
+static void csc_device_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct btd_adapter *adapter;
+	struct csc_adapter *cadapter;
+	struct csc *csc;
+	GSList *l;
+
+	adapter = device_get_adapter(device);
+
+	cadapter = find_csc_adapter(adapter);
+	if (cadapter == NULL)
+		return;
+
+	l = g_slist_find_custom(cadapter->devices, device, cmp_device);
+	if (l == NULL)
+		return;
+
+	csc = l->data;
+
+	cadapter->devices = g_slist_remove(cadapter->devices, csc);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						device_get_path(device),
+						CYCLINGSPEED_INTERFACE);
+}
+
+static struct btd_profile cscp_profile = {
+	.name		= "Cycling Speed and Cadence GATT Driver",
+	.remote_uuid	= CYCLING_SC_UUID,
+
+	.adapter_probe	= csc_adapter_probe,
+	.adapter_remove	= csc_adapter_remove,
+
+	.device_probe	= csc_device_probe,
+	.device_remove	= csc_device_remove,
+};
+
+static int cyclingspeed_init(void)
+{
+	return btd_profile_register(&cscp_profile);
+}
+
+static void cyclingspeed_exit(void)
+{
+	btd_profile_unregister(&cscp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
+					BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+					cyclingspeed_init, cyclingspeed_exit)
diff --git a/bluez/repo/profiles/deviceinfo/._deviceinfo.c b/bluez/repo/profiles/deviceinfo/._deviceinfo.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/deviceinfo/._deviceinfo.c
Binary files differ
diff --git a/bluez/repo/profiles/deviceinfo/._dis.c b/bluez/repo/profiles/deviceinfo/._dis.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/deviceinfo/._dis.c
Binary files differ
diff --git a/bluez/repo/profiles/deviceinfo/._dis.h b/bluez/repo/profiles/deviceinfo/._dis.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/deviceinfo/._dis.h
Binary files differ
diff --git a/bluez/repo/profiles/deviceinfo/deviceinfo.c b/bluez/repo/profiles/deviceinfo/deviceinfo.c
new file mode 100644
index 0000000..d1f51a0
--- /dev/null
+++ b/bluez/repo/profiles/deviceinfo/deviceinfo.c
@@ -0,0 +1,162 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *  Copyright (C) 2015 Google 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 "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 "attrib/gattrib.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "attrib/att.h"
+#include "src/log.h"
+
+#define PNP_ID_SIZE	7
+
+static void read_pnpid_cb(bool success, uint8_t att_ecode, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	if (!success) {
+		error("Error reading PNP_ID value: %s",
+						att_ecode2str(att_ecode));
+		return;
+	}
+
+	if (length < PNP_ID_SIZE) {
+		error("Error reading PNP_ID: Invalid pdu length received");
+		return;
+	}
+
+	btd_device_set_pnpid(device, value[0], get_le16(&value[1]),
+				get_le16(&value[3]), get_le16(&value[5]));
+}
+
+static void handle_pnpid(struct btd_device *device, uint16_t value_handle)
+{
+	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+
+	if (!bt_gatt_client_read_value(client, value_handle,
+						read_pnpid_cb, device, NULL))
+		DBG("Failed to send request to read pnpid");
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+								void *user_data)
+{
+	struct btd_device *device = user_data;
+	uint16_t value_handle;
+	bt_uuid_t uuid, pnpid_uuid;
+
+	bt_string_to_uuid(&pnpid_uuid, PNPID_UUID);
+
+	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+								&uuid)) {
+		error("Failed to obtain characteristic data");
+		return;
+	}
+
+	if (bt_uuid_cmp(&pnpid_uuid, &uuid) == 0)
+		handle_pnpid(device, 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_deviceinfo_service(struct gatt_db_attribute *attr,
+								void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	gatt_db_service_foreach_char(attr, handle_characteristic, device);
+}
+
+static int deviceinfo_driver_probe(struct btd_service *service)
+{
+	return 0;
+}
+
+static void deviceinfo_driver_remove(struct btd_service *service)
+{
+}
+
+
+static int deviceinfo_driver_accept(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_db *db = btd_device_get_gatt_db(device);
+	char addr[18];
+	bt_uuid_t deviceinfo_uuid;
+
+	ba2str(device_get_address(device), addr);
+	DBG("deviceinfo profile accept (%s)", addr);
+
+	/* Handle the device info service */
+	bt_string_to_uuid(&deviceinfo_uuid, DEVICE_INFORMATION_UUID);
+	gatt_db_foreach_service(db, &deviceinfo_uuid,
+					foreach_deviceinfo_service, device);
+
+	return 0;
+}
+
+static struct btd_profile deviceinfo_profile = {
+	.name		= "deviceinfo",
+	.remote_uuid	= DEVICE_INFORMATION_UUID,
+	.external	= true,
+	.device_probe	= deviceinfo_driver_probe,
+	.device_remove	= deviceinfo_driver_remove,
+	.accept		= deviceinfo_driver_accept,
+};
+
+static int deviceinfo_init(void)
+{
+	return btd_profile_register(&deviceinfo_profile);
+}
+
+static void deviceinfo_exit(void)
+{
+	btd_profile_unregister(&deviceinfo_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(deviceinfo, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+					deviceinfo_init, deviceinfo_exit)
diff --git a/bluez/repo/profiles/deviceinfo/dis.c b/bluez/repo/profiles/deviceinfo/dis.c
new file mode 100644
index 0000000..91c5d39
--- /dev/null
+++ b/bluez/repo/profiles/deviceinfo/dis.c
@@ -0,0 +1,293 @@
+/*
+ *
+ *  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 "profiles/deviceinfo/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 (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);
+	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);
+
+	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);
+
+	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;
+}
diff --git a/bluez/repo/profiles/deviceinfo/dis.h b/bluez/repo/profiles/deviceinfo/dis.h
new file mode 100644
index 0000000..faf27b3
--- /dev/null
+++ b/bluez/repo/profiles/deviceinfo/dis.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_dis;
+
+struct bt_dis *bt_dis_new(void *primary);
+
+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);
diff --git a/bluez/repo/profiles/gap/._gas.c b/bluez/repo/profiles/gap/._gas.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/gap/._gas.c
Binary files differ
diff --git a/bluez/repo/profiles/gap/gas.c b/bluez/repo/profiles/gap/gas.c
new file mode 100644
index 0000000..877c4fd
--- /dev/null
+++ b/bluez/repo/profiles/gap/gas.c
@@ -0,0 +1,329 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2014  Google 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 "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "lib/uuid.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"
+
+#define GAP_UUID16 0x1800
+
+/* Generic Attribute/Access Service */
+struct gas {
+	struct btd_device *device;
+	struct gatt_db *db;
+	struct bt_gatt_client *client;
+	struct gatt_db_attribute *attr;
+};
+
+static GSList *devices;
+
+static void gas_free(struct gas *gas)
+{
+	gatt_db_unref(gas->db);
+	bt_gatt_client_unref(gas->client);
+	btd_device_unref(gas->device);
+	g_free(gas);
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct gas *gas = a;
+	const struct btd_device *device = b;
+
+	return gas->device == device ? 0 : -1;
+}
+
+static char *name2utf8(const uint8_t *name, uint16_t len)
+{
+	char utf8_name[HCI_MAX_NAME_LENGTH + 2];
+	int i;
+
+	if (g_utf8_validate((const char *) name, len, NULL))
+		return g_strndup((char *) name, len);
+
+	len = MIN(len, sizeof(utf8_name) - 1);
+
+	memset(utf8_name, 0, sizeof(utf8_name));
+	strncpy(utf8_name, (char *) name, len);
+
+	/* Assume ASCII, and replace all non-ASCII with spaces */
+	for (i = 0; utf8_name[i] != '\0'; i++) {
+		if (!isascii(utf8_name[i]))
+			utf8_name[i] = ' ';
+	}
+
+	/* Remove leading and trailing whitespace characters */
+	g_strstrip(utf8_name);
+
+	return g_strdup(utf8_name);
+}
+
+static void read_device_name_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct gas *gas = user_data;
+	char *name;
+
+	if (!success) {
+		DBG("Reading device name failed with ATT errror: %u",
+								att_ecode);
+		return;
+	}
+
+	if (!length)
+		return;
+
+	name = name2utf8(value, length);
+
+	DBG("GAP Device Name: %s", name);
+
+	btd_device_device_set_name(gas->device, name);
+
+	g_free(name);
+}
+
+static void handle_device_name(struct gas *gas, uint16_t value_handle)
+{
+	if (!bt_gatt_client_read_long_value(gas->client, value_handle, 0,
+						read_device_name_cb, gas, NULL))
+		DBG("Failed to send request to read device name");
+}
+
+static void read_appearance_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct gas *gas = user_data;
+	uint16_t appearance;
+
+	if (!success) {
+		DBG("Reading appearance failed with ATT error: %u", att_ecode);
+		return;
+	}
+
+	/* The appearance value is a 16-bit unsigned integer */
+	if (length != 2) {
+		DBG("Malformed appearance value");
+		return;
+	}
+
+	appearance = get_le16(value);
+
+	DBG("GAP Appearance: 0x%04x", appearance);
+
+	device_set_appearance(gas->device, appearance);
+}
+
+static void handle_appearance(struct gas *gas, uint16_t value_handle)
+{
+	if (!bt_gatt_client_read_value(gas->client, value_handle,
+						read_appearance_cb, gas, NULL))
+		DBG("Failed to send request to read appearance");
+}
+
+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 gas *gas = user_data;
+	uint16_t value_handle;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+								&uuid)) {
+		error("Failed to obtain characteristic data");
+		return;
+	}
+
+	if (uuid_cmp(GATT_CHARAC_DEVICE_NAME, &uuid))
+		handle_device_name(gas, value_handle);
+	else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid))
+		handle_appearance(gas, value_handle);
+	else {
+		char uuid_str[MAX_LEN_UUID_STR];
+
+		/* TODO: Support peripheral privacy feature */
+
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		DBG("Unsupported characteristic: %s", uuid_str);
+	}
+}
+
+static void handle_gap_service(struct gas *gas)
+{
+	gatt_db_service_foreach_char(gas->attr, handle_characteristic, gas);
+}
+
+static int gap_driver_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gas *gas;
+	GSList *l;
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("GAP profile probe (%s)", addr);
+
+	/* Ignore, if we were probed for this device already */
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (l) {
+		error("Profile probed twice for the same device!");
+		return -1;
+	}
+
+	gas = g_new0(struct gas, 1);
+	if (!gas)
+		return -1;
+
+	gas->device = btd_device_ref(device);
+	devices = g_slist_append(devices, gas);
+
+	return 0;
+}
+
+static void gap_driver_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gas *gas;
+	GSList *l;
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("GAP profile remove (%s)", addr);
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (!l) {
+		error("GAP service not handled by profile");
+		return;
+	}
+
+	gas = l->data;
+
+	devices = g_slist_remove(devices, gas);
+	gas_free(gas);
+}
+
+static void foreach_gap_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gas *gas = user_data;
+
+	if (gas->attr) {
+		error("More than one GAP service exists for this device");
+		return;
+	}
+
+	gas->attr = attr;
+	handle_gap_service(gas);
+}
+
+static int gap_driver_accept(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_db *db = btd_device_get_gatt_db(device);
+	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+	struct gas *gas;
+	GSList *l;
+	char addr[18];
+	bt_uuid_t gap_uuid;
+
+	ba2str(device_get_address(device), addr);
+	DBG("GAP profile accept (%s)", addr);
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (!l) {
+		error("GAP service not handled by profile");
+		return -1;
+	}
+
+	gas = l->data;
+
+	/* Clean-up any old client/db and acquire the new ones */
+	gas->attr = NULL;
+	gatt_db_unref(gas->db);
+	bt_gatt_client_unref(gas->client);
+
+	gas->db = gatt_db_ref(db);
+	gas->client = bt_gatt_client_ref(client);
+
+	/* Handle the GAP services */
+	bt_uuid16_create(&gap_uuid, GAP_UUID16);
+	gatt_db_foreach_service(db, &gap_uuid, foreach_gap_service, gas);
+
+	return 0;
+}
+
+static struct btd_profile gap_profile = {
+	.name		= "gap-profile",
+	.remote_uuid	= GAP_UUID,
+	.device_probe	= gap_driver_probe,
+	.device_remove	= gap_driver_remove,
+	.accept		= gap_driver_accept
+};
+
+static int gap_init(void)
+{
+	devices = NULL;
+
+	btd_profile_register(&gap_profile);
+
+	return 0;
+}
+
+static void gap_exit(void)
+{
+	btd_profile_unregister(&gap_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gap, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+							gap_init, gap_exit)
diff --git a/bluez/repo/profiles/health/._hdp.c b/bluez/repo/profiles/health/._hdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp.c
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp.h b/bluez/repo/profiles/health/._hdp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp.h
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp_main.c b/bluez/repo/profiles/health/._hdp_main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp_main.c
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp_manager.c b/bluez/repo/profiles/health/._hdp_manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp_manager.c
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp_manager.h b/bluez/repo/profiles/health/._hdp_manager.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp_manager.h
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp_types.h b/bluez/repo/profiles/health/._hdp_types.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp_types.h
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp_util.c b/bluez/repo/profiles/health/._hdp_util.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp_util.c
Binary files differ
diff --git a/bluez/repo/profiles/health/._hdp_util.h b/bluez/repo/profiles/health/._hdp_util.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._hdp_util.h
Binary files differ
diff --git a/bluez/repo/profiles/health/._mcap.c b/bluez/repo/profiles/health/._mcap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._mcap.c
Binary files differ
diff --git a/bluez/repo/profiles/health/._mcap.h b/bluez/repo/profiles/health/._mcap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/health/._mcap.h
Binary files differ
diff --git a/bluez/repo/profiles/health/hdp.c b/bluez/repo/profiles/health/hdp.c
new file mode 100644
index 0000000..bc3b38a
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp.c
@@ -0,0 +1,2254 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/dbus-common.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/sdpd.h"
+#include "btio/btio.h"
+
+#include "hdp_types.h"
+#include "hdp_util.h"
+#include "hdp.h"
+#include "mcap.h"
+
+#define ECHO_TIMEOUT	1 /* second */
+#define HDP_ECHO_LEN	15
+
+static GSList *applications = NULL;
+static GSList *devices = NULL;
+static uint8_t next_app_id = HDP_MDEP_INITIAL;
+
+static GSList *adapters;
+
+static gboolean update_adapter(struct hdp_adapter *adapter);
+static struct hdp_device *create_health_device(struct btd_device *device);
+static void free_echo_data(struct hdp_echo_data *edata);
+
+struct hdp_create_dc {
+	DBusMessage			*msg;
+	struct hdp_application		*app;
+	struct hdp_device		*dev;
+	uint8_t				config;
+	uint8_t				mdep;
+	guint				ref;
+	mcap_mdl_operation_cb		cb;
+};
+
+struct hdp_tmp_dc_data {
+	DBusMessage			*msg;
+	struct hdp_channel		*hdp_chann;
+	guint				ref;
+	mcap_mdl_operation_cb		cb;
+};
+
+struct hdp_echo_data {
+	gboolean		echo_done;	/* Is a echo was already done */
+	gpointer		buf;		/* echo packet sent */
+	guint			tid;		/* echo timeout */
+};
+
+static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
+{
+	if (chan == NULL)
+		return NULL;
+
+	chan->ref++;
+
+	DBG("(%p): ref=%d", chan, chan->ref);
+	return chan;
+}
+
+static void free_health_channel(struct hdp_channel *chan)
+{
+	if (chan->mdep == HDP_MDEP_ECHO) {
+		free_echo_data(chan->edata);
+		chan->edata = NULL;
+	}
+
+	mcap_mdl_unref(chan->mdl);
+	hdp_application_unref(chan->app);
+	health_device_unref(chan->dev);
+	g_free(chan->path);
+	g_free(chan);
+}
+
+static void hdp_channel_unref(struct hdp_channel *chan)
+{
+	if (chan == NULL)
+		return;
+
+	chan->ref --;
+	DBG("(%p): ref=%d", chan, chan->ref);
+
+	if (chan->ref > 0)
+		return;
+
+	free_health_channel(chan);
+}
+
+static void free_hdp_create_dc(struct hdp_create_dc *dc_data)
+{
+	dbus_message_unref(dc_data->msg);
+	hdp_application_unref(dc_data->app);
+	health_device_unref(dc_data->dev);
+
+	g_free(dc_data);
+}
+
+static struct hdp_create_dc *hdp_create_data_ref(struct hdp_create_dc *dc_data)
+{
+	dc_data->ref++;
+
+	DBG("(%p): ref=%d", dc_data, dc_data->ref);
+
+	return dc_data;
+}
+
+static void hdp_create_data_unref(struct hdp_create_dc *dc_data)
+{
+	dc_data->ref--;
+
+	DBG("(%p): ref=%d", dc_data, dc_data->ref);
+
+	if (dc_data->ref > 0)
+		return;
+
+	free_hdp_create_dc(dc_data);
+}
+
+static void free_hdp_conn_dc(struct hdp_tmp_dc_data *data)
+{
+	dbus_message_unref(data->msg);
+	hdp_channel_unref(data->hdp_chann);
+
+	g_free(data);
+}
+
+static struct hdp_tmp_dc_data *hdp_tmp_dc_data_ref(struct hdp_tmp_dc_data *data)
+{
+	data->ref++;
+
+	DBG("hdp_conn_data_ref(%p): ref=%d", data, data->ref);
+
+	return data;
+}
+
+static void hdp_tmp_dc_data_unref(struct hdp_tmp_dc_data *data)
+{
+	data->ref--;
+
+	DBG("hdp_conn_data_unref(%p): ref=%d", data, data->ref);
+
+	if (data->ref > 0)
+		return;
+
+	free_hdp_conn_dc(data);
+}
+
+static int cmp_app_id(gconstpointer a, gconstpointer b)
+{
+	const struct hdp_application *app = a;
+	const uint8_t *id = b;
+
+	return app->id - *id;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+	const struct hdp_adapter *hdp_adapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (hdp_adapter->btd_adapter == adapter)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct hdp_device *hdp_device = a;
+	const struct btd_device *device = b;
+
+	if (hdp_device->dev == device)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_dev_addr(gconstpointer a, gconstpointer dst)
+{
+	const struct hdp_device *device = a;
+
+	return bacmp(device_get_address(device->dev), dst);
+}
+
+static int cmp_dev_mcl(gconstpointer a, gconstpointer mcl)
+{
+	const struct hdp_device *device = a;
+
+	if (mcl == device->mcl)
+		return 0;
+	return -1;
+}
+
+static int cmp_chan_mdlid(gconstpointer a, gconstpointer b)
+{
+	const struct hdp_channel *chan = a;
+	const uint16_t *mdlid = b;
+
+	return chan->mdlid - *mdlid;
+}
+
+static int cmp_chan_path(gconstpointer a, gconstpointer b)
+{
+	const struct hdp_channel *chan = a;
+	const char *path = b;
+
+	return g_ascii_strcasecmp(chan->path, path);
+}
+
+static int cmp_chan_mdl(gconstpointer a, gconstpointer mdl)
+{
+	const struct hdp_channel *chan = a;
+
+	if (chan->mdl == mdl)
+		return 0;
+	return -1;
+}
+
+static uint8_t get_app_id(void)
+{
+	uint8_t id = next_app_id;
+
+	do {
+		GSList *l = g_slist_find_custom(applications, &id, cmp_app_id);
+
+		if (l == NULL) {
+			next_app_id = (id % HDP_MDEP_FINAL) + 1;
+			return id;
+		} else
+			id = (id % HDP_MDEP_FINAL) + 1;
+	} while (id != next_app_id);
+
+	/* No more ids available */
+	return 0;
+}
+
+static int cmp_app(gconstpointer a, gconstpointer b)
+{
+	const struct hdp_application *app = a;
+
+	return g_strcmp0(app->path, b);
+}
+
+static gboolean set_app_path(struct hdp_application *app)
+{
+	app->id = get_app_id();
+	if (app->id == 0)
+		return FALSE;
+	app->path = g_strdup_printf(MANAGER_PATH "/health_app_%d", app->id);
+
+	return TRUE;
+};
+
+static void device_unref_mcl(struct hdp_device *hdp_device)
+{
+	if (hdp_device->mcl == NULL)
+		return;
+
+	mcap_close_mcl(hdp_device->mcl, FALSE);
+	mcap_mcl_unref(hdp_device->mcl);
+	hdp_device->mcl = NULL;
+	hdp_device->mcl_conn = FALSE;
+}
+
+static void free_health_device(struct hdp_device *device)
+{
+	if (device->dev != NULL) {
+		btd_device_unref(device->dev);
+		device->dev = NULL;
+	}
+
+	device_unref_mcl(device);
+
+	g_free(device);
+}
+
+static void remove_application(struct hdp_application *app)
+{
+	DBG("Application %s deleted", app->path);
+	hdp_application_unref(app);
+
+	g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+}
+
+static void client_disconnected(DBusConnection *conn, void *user_data)
+{
+	struct hdp_application *app = user_data;
+
+	DBG("Client disconnected from the bus, deleting hdp application");
+	applications = g_slist_remove(applications, app);
+
+	app->dbus_watcher = 0; /* Watcher shouldn't be freed in this case */
+	remove_application(app);
+}
+
+static DBusMessage *manager_create_application(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct hdp_application *app;
+	const char *name;
+	DBusMessageIter iter;
+	GError *err = NULL;
+
+	dbus_message_iter_init(msg, &iter);
+	app = hdp_get_app_config(&iter, &err);
+	if (err != NULL) {
+		g_error_free(err);
+		return btd_error_invalid_args(msg);
+	}
+
+	name = dbus_message_get_sender(msg);
+	if (name == NULL) {
+		hdp_application_unref(app);
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".HealthError",
+					"Can't get sender name");
+	}
+
+	if (!set_app_path(app)) {
+		hdp_application_unref(app);
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".HealthError",
+				"Can't get a valid id for the application");
+	}
+
+	app->oname = g_strdup(name);
+
+	applications = g_slist_prepend(applications, app);
+
+	app->dbus_watcher =
+			g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+					name, client_disconnected, app, NULL);
+	g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+
+	DBG("Health application created with id %s", app->path);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &app->path,
+							DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *manager_destroy_application(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *path;
+	struct hdp_application *app;
+	GSList *l;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	l = g_slist_find_custom(applications, path, cmp_app);
+
+	if (l == NULL)
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call, "
+					"no such application");
+
+	app = l->data;
+	applications = g_slist_remove(applications, app);
+
+	remove_application(app);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void manager_path_unregister(gpointer data)
+{
+	g_slist_foreach(applications, (GFunc) hdp_application_unref, NULL);
+
+	g_slist_free(applications);
+	applications = NULL;
+
+	g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+}
+
+static const GDBusMethodTable health_manager_methods[] = {
+	{ GDBUS_METHOD("CreateApplication",
+			GDBUS_ARGS({ "config", "a{sv}" }),
+			GDBUS_ARGS({ "application", "o" }),
+			manager_create_application) },
+	{ GDBUS_METHOD("DestroyApplication",
+			GDBUS_ARGS({ "application", "o" }), NULL,
+			manager_destroy_application) },
+	{ }
+};
+
+static gboolean channel_property_get_device(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct hdp_channel *chan = data;
+	const char *path = device_get_path(chan->dev->dev);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	return TRUE;
+}
+
+static gboolean channel_property_get_application(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct hdp_channel *chan = data;
+	const char *path = chan->app->path;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	return TRUE;
+}
+
+static gboolean channel_property_get_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct hdp_channel *chan = data;
+	const char *type;
+
+	if (chan->config == HDP_RELIABLE_DC)
+		type = "reliable";
+	else
+		type = "streaming";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
+
+	return TRUE;
+}
+
+static void hdp_tmp_dc_data_destroy(gpointer data)
+{
+	struct hdp_tmp_dc_data *hdp_conn = data;
+
+	hdp_tmp_dc_data_unref(hdp_conn);
+}
+
+static void abort_mdl_cb(GError *err, gpointer data)
+{
+	if (err != NULL)
+		error("Aborting error: %s", err->message);
+}
+
+static void hdp_mdl_reconn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_tmp_dc_data *dc_data = data;
+	DBusMessage *reply;
+	int fd;
+
+	if (err != NULL) {
+		struct hdp_channel *chan = dc_data->hdp_chann;
+		GError *gerr = NULL;
+
+		error("%s", err->message);
+		reply = g_dbus_create_error(dc_data->msg,
+					ERROR_INTERFACE ".HealthError",
+					"Cannot reconnect: %s", err->message);
+		g_dbus_send_message(conn, reply);
+
+		/* Send abort request because remote side */
+		/* is now in PENDING state */
+		if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, NULL, NULL,
+								&gerr)) {
+			error("%s", gerr->message);
+			g_error_free(gerr);
+		}
+		return;
+	}
+
+	fd = mcap_mdl_get_fd(dc_data->hdp_chann->mdl);
+	if (fd < 0) {
+		reply = g_dbus_create_error(dc_data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"Cannot get file descriptor");
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	reply = g_dbus_create_reply(dc_data->msg, DBUS_TYPE_UNIX_FD,
+							&fd, DBUS_TYPE_INVALID);
+	g_dbus_send_message(conn, reply);
+
+	g_dbus_emit_signal(conn, device_get_path(dc_data->hdp_chann->dev->dev),
+			HEALTH_DEVICE, "ChannelConnected",
+			DBUS_TYPE_OBJECT_PATH, &dc_data->hdp_chann->path,
+			DBUS_TYPE_INVALID);
+}
+
+static void hdp_get_dcpsm_cb(uint16_t dcpsm, gpointer user_data, GError *err)
+{
+	struct hdp_tmp_dc_data *hdp_conn = user_data;
+	struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+	GError *gerr = NULL;
+	uint8_t mode;
+
+	if (err != NULL) {
+		hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
+		return;
+	}
+
+	if (hdp_chann->config == HDP_RELIABLE_DC)
+		mode = L2CAP_MODE_ERTM;
+	else
+		mode = L2CAP_MODE_STREAMING;
+
+	if (mcap_connect_mdl(hdp_chann->mdl, mode, dcpsm, hdp_conn->cb,
+					hdp_tmp_dc_data_ref(hdp_conn),
+					hdp_tmp_dc_data_destroy, &gerr))
+		return;
+
+	hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
+	g_error_free(gerr);
+	hdp_tmp_dc_data_unref(hdp_conn);
+}
+
+static void device_reconnect_mdl_cb(struct mcap_mdl *mdl, GError *err,
+								gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_tmp_dc_data *dc_data = data;
+	GError *gerr = NULL;
+	DBusMessage *reply;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(dc_data->msg,
+					ERROR_INTERFACE ".HealthError",
+					"Cannot reconnect: %s", err->message);
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	dc_data->cb = hdp_mdl_reconn_cb;
+
+	if (hdp_get_dcpsm(dc_data->hdp_chann->dev, hdp_get_dcpsm_cb,
+					hdp_tmp_dc_data_ref(dc_data),
+					hdp_tmp_dc_data_destroy, &gerr))
+		return;
+
+	error("%s", gerr->message);
+
+	reply = g_dbus_create_error(dc_data->msg,
+					ERROR_INTERFACE ".HealthError",
+					"Cannot reconnect: %s", gerr->message);
+	g_dbus_send_message(conn, reply);
+	hdp_tmp_dc_data_unref(dc_data);
+	g_error_free(gerr);
+
+	/* Send abort request because remote side is now in PENDING state */
+	if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+	}
+}
+
+static DBusMessage *channel_acquire_continue(struct hdp_tmp_dc_data *data,
+								GError *err)
+{
+	DBusMessage *reply;
+	GError *gerr = NULL;
+	int fd;
+
+	if (err != NULL) {
+		return g_dbus_create_error(data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", err->message);
+	}
+
+	fd = mcap_mdl_get_fd(data->hdp_chann->mdl);
+	if (fd >= 0)
+		return g_dbus_create_reply(data->msg, DBUS_TYPE_UNIX_FD, &fd,
+							DBUS_TYPE_INVALID);
+
+	hdp_tmp_dc_data_ref(data);
+	if (mcap_reconnect_mdl(data->hdp_chann->mdl, device_reconnect_mdl_cb,
+					data, hdp_tmp_dc_data_destroy, &gerr))
+		return NULL;
+
+	reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
+					"Cannot reconnect: %s", gerr->message);
+	g_error_free(gerr);
+	hdp_tmp_dc_data_unref(data);
+
+	return reply;
+}
+
+static void channel_acquire_cb(gpointer data, GError *err)
+{
+	DBusMessage *reply;
+
+	reply = channel_acquire_continue(data, err);
+
+	if (reply != NULL)
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+static DBusMessage *channel_acquire(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct hdp_channel *chan = user_data;
+	struct hdp_tmp_dc_data *dc_data;
+	GError *gerr = NULL;
+	DBusMessage *reply;
+
+	dc_data = g_new0(struct hdp_tmp_dc_data, 1);
+	dc_data->msg = dbus_message_ref(msg);
+	dc_data->hdp_chann = hdp_channel_ref(chan);
+
+	if (chan->dev->mcl_conn) {
+		reply = channel_acquire_continue(hdp_tmp_dc_data_ref(dc_data),
+									NULL);
+		hdp_tmp_dc_data_unref(dc_data);
+		return reply;
+	}
+
+	if (hdp_establish_mcl(chan->dev, channel_acquire_cb,
+						hdp_tmp_dc_data_ref(dc_data),
+						hdp_tmp_dc_data_destroy, &gerr))
+		return NULL;
+
+	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+					"%s", gerr->message);
+	hdp_tmp_dc_data_unref(dc_data);
+	g_error_free(gerr);
+
+	return reply;
+}
+
+static void close_mdl(struct hdp_channel *hdp_chann)
+{
+	int fd;
+
+	fd = mcap_mdl_get_fd(hdp_chann->mdl);
+	if (fd < 0)
+		return;
+
+	close(fd);
+}
+
+static DBusMessage *channel_release(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct hdp_channel *hdp_chann = user_data;
+
+	close_mdl(hdp_chann);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void free_echo_data(struct hdp_echo_data *edata)
+{
+	if (edata == NULL)
+		return;
+
+	if (edata->tid > 0)
+		g_source_remove(edata->tid);
+
+	if (edata->buf != NULL)
+		g_free(edata->buf);
+
+
+	g_free(edata);
+}
+
+static void health_channel_destroy(void *data)
+{
+	struct hdp_channel *hdp_chan = data;
+	struct hdp_device *dev = hdp_chan->dev;
+
+	DBG("Destroy Health Channel %s", hdp_chan->path);
+	if (g_slist_find(dev->channels, hdp_chan) == NULL)
+		goto end;
+
+	dev->channels = g_slist_remove(dev->channels, hdp_chan);
+
+	if (hdp_chan->mdep != HDP_MDEP_ECHO)
+		g_dbus_emit_signal(btd_get_dbus_connection(),
+					device_get_path(dev->dev),
+					HEALTH_DEVICE, "ChannelDeleted",
+					DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+					DBUS_TYPE_INVALID);
+
+	if (hdp_chan == dev->fr) {
+		hdp_channel_unref(dev->fr);
+		dev->fr = NULL;
+	}
+
+end:
+	hdp_channel_unref(hdp_chan);
+}
+
+static const GDBusMethodTable health_channels_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Acquire",
+			NULL, GDBUS_ARGS({ "fd", "h" }),
+			channel_acquire) },
+	{ GDBUS_METHOD("Release", NULL, NULL, channel_release) },
+	{ }
+};
+
+static const GDBusPropertyTable health_channels_properties[] = {
+	{ "Device", "o",  channel_property_get_device },
+	{ "Application", "o", channel_property_get_application },
+	{ "Type", "s", channel_property_get_type },
+	{ }
+};
+
+static struct hdp_channel *create_channel(struct hdp_device *dev,
+						uint8_t config,
+						struct mcap_mdl *mdl,
+						uint16_t mdlid,
+						struct hdp_application *app,
+						GError **err)
+{
+	struct hdp_channel *hdp_chann;
+
+	if (dev == NULL) {
+		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+					"HDP device uninitialized");
+		return NULL;
+	}
+
+	hdp_chann = g_new0(struct hdp_channel, 1);
+	hdp_chann->config = config;
+	hdp_chann->dev = health_device_ref(dev);
+	hdp_chann->mdlid = mdlid;
+
+	if (mdl != NULL)
+		hdp_chann->mdl = mcap_mdl_ref(mdl);
+
+	if (app != NULL) {
+		hdp_chann->mdep = app->id;
+		hdp_chann->app = hdp_application_ref(app);
+	} else
+		hdp_chann->edata = g_new0(struct hdp_echo_data, 1);
+
+	hdp_chann->path = g_strdup_printf("%s/chan%d",
+					device_get_path(hdp_chann->dev->dev),
+					hdp_chann->mdlid);
+
+	dev->channels = g_slist_append(dev->channels,
+						hdp_channel_ref(hdp_chann));
+
+	if (hdp_chann->mdep == HDP_MDEP_ECHO)
+		return hdp_channel_ref(hdp_chann);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					hdp_chann->path, HEALTH_CHANNEL,
+					health_channels_methods, NULL,
+					health_channels_properties, hdp_chann,
+					health_channel_destroy)) {
+		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+					"Can't register the channel interface");
+		health_channel_destroy(hdp_chann);
+		return NULL;
+	}
+
+	return hdp_channel_ref(hdp_chann);
+}
+
+static void remove_channels(struct hdp_device *dev)
+{
+	struct hdp_channel *chan;
+	char *path;
+
+	while (dev->channels != NULL) {
+		chan = dev->channels->data;
+
+		path = g_strdup(chan->path);
+		if (!g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_CHANNEL))
+			health_channel_destroy(chan);
+		g_free(path);
+	}
+}
+
+static void close_device_con(struct hdp_device *dev, gboolean cache)
+{
+	if (dev->mcl == NULL)
+		return;
+
+	mcap_close_mcl(dev->mcl, cache);
+	dev->mcl_conn = FALSE;
+
+	if (cache)
+		return;
+
+	device_unref_mcl(dev);
+	remove_channels(dev);
+
+	if (!dev->sdp_present) {
+		const char *path;
+
+		path = device_get_path(dev->dev);
+		g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_DEVICE);
+	}
+}
+
+static int send_echo_data(int sock, const void *buf, uint32_t size)
+{
+	const uint8_t *buf_b = buf;
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = write(sock, buf_b + sent, size - sent);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+
+	return 0;
+}
+
+static gboolean serve_echo(GIOChannel *io_chan, GIOCondition cond,
+								gpointer data)
+{
+	struct hdp_channel *chan = data;
+	uint8_t buf[MCAP_DC_MTU];
+	int fd, len;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		hdp_channel_unref(chan);
+		return FALSE;
+	}
+
+	if (chan->edata->echo_done)
+		goto fail;
+
+	chan->edata->echo_done = TRUE;
+
+	fd = g_io_channel_unix_get_fd(io_chan);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		goto fail;
+
+	if (send_echo_data(fd, buf, len)  >= 0)
+		return TRUE;
+
+fail:
+	close_device_con(chan->dev, FALSE);
+	hdp_channel_unref(chan);
+	return FALSE;
+}
+
+static gboolean check_channel_conf(struct hdp_channel *chan)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+	uint8_t mode;
+	uint16_t imtu, omtu;
+	int fd;
+
+	fd = mcap_mdl_get_fd(chan->mdl);
+	if (fd < 0)
+		return FALSE;
+	io = g_io_channel_unix_new(fd);
+
+	if (!bt_io_get(io, &err,
+			BT_IO_OPT_MODE, &mode,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID)) {
+		error("Error: %s", err->message);
+		g_io_channel_unref(io);
+		g_error_free(err);
+		return FALSE;
+	}
+
+	g_io_channel_unref(io);
+
+	switch (chan->config) {
+	case HDP_RELIABLE_DC:
+		if (mode != L2CAP_MODE_ERTM)
+			return FALSE;
+		break;
+	case HDP_STREAMING_DC:
+		if (mode != L2CAP_MODE_STREAMING)
+			return FALSE;
+		break;
+	default:
+		error("Error: Connected with unknown configuration");
+		return FALSE;
+	}
+
+	DBG("MDL imtu %d omtu %d Channel imtu %d omtu %d", imtu, omtu,
+						chan->imtu, chan->omtu);
+
+	if (chan->imtu == 0)
+		chan->imtu = imtu;
+	if (chan->omtu == 0)
+		chan->omtu = omtu;
+
+	if (chan->imtu != imtu || chan->omtu != omtu)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct hdp_device *dev = data;
+	struct hdp_channel *chan;
+
+	DBG("");
+
+	if (dev->ndc == NULL)
+		return;
+
+	chan = dev->ndc;
+	if (chan->mdl == NULL)
+		chan->mdl = mcap_mdl_ref(mdl);
+
+	if (g_slist_find(dev->channels, chan) == NULL)
+		dev->channels = g_slist_prepend(dev->channels,
+							hdp_channel_ref(chan));
+
+	if (!check_channel_conf(chan)) {
+		close_mdl(chan);
+		goto end;
+	}
+
+	if (chan->mdep == HDP_MDEP_ECHO) {
+		GIOChannel *io;
+		int fd;
+
+		fd = mcap_mdl_get_fd(chan->mdl);
+		if (fd < 0)
+			goto end;
+
+		chan->edata->echo_done = FALSE;
+		io = g_io_channel_unix_new(fd);
+		g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+				serve_echo, hdp_channel_ref(chan));
+		g_io_channel_unref(io);
+		goto end;
+	}
+
+	g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(dev->dev),
+				HEALTH_DEVICE, "ChannelConnected",
+				DBUS_TYPE_OBJECT_PATH, &chan->path,
+				DBUS_TYPE_INVALID);
+
+	if (dev->fr != NULL)
+		goto end;
+
+	dev->fr = hdp_channel_ref(chan);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				device_get_path(dev->dev), HEALTH_DEVICE,
+				"MainChannel");
+
+end:
+	hdp_channel_unref(dev->ndc);
+	dev->ndc = NULL;
+}
+
+static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+	/* struct hdp_device *dev = data; */
+
+	DBG("");
+
+	/* Nothing to do */
+}
+
+static void hdp_mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct hdp_device *dev = data;
+	struct hdp_channel *chan;
+	char *path;
+	GSList *l;
+
+	DBG("");
+
+	l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
+	if (l == NULL)
+		return;
+
+	chan = l->data;
+
+	path = g_strdup(chan->path);
+	if (!g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_CHANNEL))
+		health_channel_destroy(chan);
+	g_free(path);
+}
+
+static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct hdp_device *dev = data;
+
+	DBG("");
+
+	if (dev->ndc == NULL)
+		return;
+
+	dev->ndc->mdl = mcap_mdl_ref(mdl);
+
+	if (g_slist_find(dev->channels, dev->ndc) == NULL)
+		dev->channels = g_slist_prepend(dev->channels,
+						hdp_channel_ref(dev->ndc));
+
+	if (dev->ndc->mdep != HDP_MDEP_ECHO)
+		g_dbus_emit_signal(btd_get_dbus_connection(),
+					device_get_path(dev->dev),
+					HEALTH_DEVICE, "ChannelConnected",
+					DBUS_TYPE_OBJECT_PATH, &dev->ndc->path,
+					DBUS_TYPE_INVALID);
+
+	hdp_channel_unref(dev->ndc);
+	dev->ndc = NULL;
+}
+
+static uint8_t hdp2l2cap_mode(uint8_t hdp_mode)
+{
+	return hdp_mode == HDP_STREAMING_DC ? L2CAP_MODE_STREAMING :
+								L2CAP_MODE_ERTM;
+}
+
+static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+				uint16_t mdlid, uint8_t *conf, void *data)
+{
+	struct hdp_device *dev = data;
+	struct hdp_application *app;
+	GError *err = NULL;
+	GSList *l;
+
+	DBG("Data channel request");
+
+	if (mdepid == HDP_MDEP_ECHO) {
+		switch (*conf) {
+		case HDP_NO_PREFERENCE_DC:
+			*conf = HDP_RELIABLE_DC;
+			break;
+		case HDP_RELIABLE_DC:
+			break;
+		case HDP_STREAMING_DC:
+			return MCAP_CONFIGURATION_REJECTED;
+		default:
+			/* Special case defined in HDP spec 3.4. When an invalid
+			* configuration is received we shall close the MCL when
+			* we are still processing the callback. */
+			close_device_con(dev, FALSE);
+			return MCAP_CONFIGURATION_REJECTED; /* not processed */
+		}
+
+		if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+						L2CAP_MODE_ERTM, &err)) {
+			error("Error: %s", err->message);
+			g_error_free(err);
+			return MCAP_MDL_BUSY;
+		}
+
+		dev->ndc = create_channel(dev, *conf, NULL, mdlid, NULL, NULL);
+		if (dev->ndc == NULL)
+			return MCAP_MDL_BUSY;
+
+		return MCAP_SUCCESS;
+	}
+
+	l = g_slist_find_custom(applications, &mdepid, cmp_app_id);
+	if (l == NULL)
+		return MCAP_INVALID_MDEP;
+
+	app = l->data;
+
+	/* Check if is the first dc if so,
+	* only reliable configuration is allowed */
+	switch (*conf) {
+	case HDP_NO_PREFERENCE_DC:
+		if (app->role == HDP_SINK)
+			return MCAP_CONFIGURATION_REJECTED;
+		else if (dev->fr && app->chan_type_set)
+			*conf = app->chan_type;
+		else
+			*conf = HDP_RELIABLE_DC;
+		break;
+	case HDP_STREAMING_DC:
+		if (!dev->fr || app->role == HDP_SOURCE)
+			return MCAP_CONFIGURATION_REJECTED;
+		break;
+	case HDP_RELIABLE_DC:
+		if (app->role == HDP_SOURCE)
+			return MCAP_CONFIGURATION_REJECTED;
+		break;
+	default:
+		/* Special case defined in HDP spec 3.4. When an invalid
+		* configuration is received we shall close the MCL when
+		* we are still processing the callback. */
+		close_device_con(dev, FALSE);
+		return MCAP_CONFIGURATION_REJECTED; /* not processed */
+	}
+
+	l = g_slist_find_custom(dev->channels, &mdlid, cmp_chan_mdlid);
+	if (l != NULL) {
+		struct hdp_channel *chan = l->data;
+		char *path;
+
+		path = g_strdup(chan->path);
+		g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_CHANNEL);
+		g_free(path);
+	}
+
+	if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+						hdp2l2cap_mode(*conf), &err)) {
+		error("Error: %s", err->message);
+		g_error_free(err);
+		return MCAP_MDL_BUSY;
+	}
+
+	dev->ndc = create_channel(dev, *conf, NULL, mdlid, app, NULL);
+	if (dev->ndc == NULL)
+		return MCAP_MDL_BUSY;
+
+	return MCAP_SUCCESS;
+}
+
+static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+	struct hdp_device *dev = data;
+	struct hdp_channel *chan;
+	GError *err = NULL;
+	GSList *l;
+
+	l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
+	if (l == NULL)
+		return MCAP_INVALID_MDL;
+
+	chan = l->data;
+
+	if (dev->fr == NULL && chan->config != HDP_RELIABLE_DC &&
+						chan->mdep != HDP_MDEP_ECHO)
+		return MCAP_UNSPECIFIED_ERROR;
+
+	if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+					hdp2l2cap_mode(chan->config), &err)) {
+		error("Error: %s", err->message);
+		g_error_free(err);
+		return MCAP_MDL_BUSY;
+	}
+
+	dev->ndc = hdp_channel_ref(chan);
+
+	return MCAP_SUCCESS;
+}
+
+gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err)
+{
+	gboolean ret;
+
+	if (device->mcl == NULL)
+		return FALSE;
+
+	ret = mcap_mcl_set_cb(device->mcl, device, err,
+		MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
+		MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
+		MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
+		MCAP_MDL_CB_ABORTED, hdp_mcap_mdl_aborted_cb,
+		MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb,
+		MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
+		MCAP_MDL_CB_INVALID);
+	if (ret)
+		return TRUE;
+
+	error("Can't set mcl callbacks, closing mcl");
+	close_device_con(device, TRUE);
+
+	return FALSE;
+}
+
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct hdp_device *hdp_device;
+	bdaddr_t addr;
+	GSList *l;
+
+	mcap_mcl_get_addr(mcl, &addr);
+	l = g_slist_find_custom(devices, &addr, cmp_dev_addr);
+	if (l == NULL) {
+		struct hdp_adapter *hdp_adapter = data;
+		struct btd_device *device;
+
+		device = btd_adapter_get_device(hdp_adapter->btd_adapter,
+							&addr, BDADDR_BREDR);
+		if (!device)
+			return;
+		hdp_device = create_health_device(device);
+		if (!hdp_device)
+			return;
+		devices = g_slist_append(devices, hdp_device);
+	} else
+		hdp_device = l->data;
+
+	hdp_device->mcl = mcap_mcl_ref(mcl);
+	hdp_device->mcl_conn = TRUE;
+
+	DBG("New mcl connected from  %s", device_get_path(hdp_device->dev));
+
+	hdp_set_mcl_cb(hdp_device, NULL);
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct hdp_device *hdp_device;
+	GSList *l;
+
+	l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+	if (l == NULL)
+		return;
+
+	hdp_device = l->data;
+	hdp_device->mcl_conn = TRUE;
+
+	DBG("MCL reconnected %s", device_get_path(hdp_device->dev));
+
+	hdp_set_mcl_cb(hdp_device, NULL);
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	struct hdp_device *hdp_device;
+	GSList *l;
+
+	l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+	if (l == NULL)
+		return;
+
+	hdp_device = l->data;
+	hdp_device->mcl_conn = FALSE;
+
+	DBG("Mcl disconnected %s", device_get_path(hdp_device->dev));
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+	struct hdp_device *hdp_device;
+	const char *path;
+	GSList *l;
+
+	l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+	if (l == NULL)
+		return;
+
+	hdp_device = l->data;
+	device_unref_mcl(hdp_device);
+
+	if (hdp_device->sdp_present)
+		return;
+
+	/* Because remote device hasn't announced an HDP record */
+	/* the Bluetooth daemon won't notify when the device shall */
+	/* be removed. Then we have to remove the HealthDevice */
+	/* interface manually */
+	path = device_get_path(hdp_device->dev);
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_DEVICE);
+	DBG("Mcl uncached %s", path);
+}
+
+static void check_devices_mcl(void)
+{
+	struct hdp_device *dev;
+	GSList *l, *to_delete = NULL;
+
+	for (l = devices; l; l = l->next) {
+		dev = l->data;
+		device_unref_mcl(dev);
+
+		if (!dev->sdp_present)
+			to_delete = g_slist_append(to_delete, dev);
+		else
+			remove_channels(dev);
+	}
+
+	for (l = to_delete; l; l = l->next) {
+		const char *path;
+
+		path = device_get_path(dev->dev);
+		g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_DEVICE);
+	}
+
+	g_slist_free(to_delete);
+}
+
+static void release_adapter_instance(struct hdp_adapter *hdp_adapter)
+{
+	if (hdp_adapter->mi == NULL)
+		return;
+
+	check_devices_mcl();
+	mcap_release_instance(hdp_adapter->mi);
+	mcap_instance_unref(hdp_adapter->mi);
+	hdp_adapter->mi = NULL;
+}
+
+static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
+{
+	GError *err = NULL;
+	const bdaddr_t *src;
+
+	if (applications == NULL) {
+		release_adapter_instance(hdp_adapter);
+		goto update;
+	}
+
+	if (hdp_adapter->mi != NULL)
+		goto update;
+
+	src = btd_adapter_get_address(hdp_adapter->btd_adapter);
+
+	hdp_adapter->mi = mcap_create_instance(src,
+				BT_IO_SEC_MEDIUM, 0, 0,
+				mcl_connected, mcl_reconnected,
+				mcl_disconnected, mcl_uncached,
+				NULL, /* CSP is not used by now */
+				hdp_adapter, &err);
+	if (hdp_adapter->mi == NULL) {
+		error("Error creating the MCAP instance: %s", err->message);
+		g_error_free(err);
+		return FALSE;
+	}
+
+	hdp_adapter->ccpsm = mcap_get_ctrl_psm(hdp_adapter->mi, &err);
+	if (err != NULL) {
+		error("Error getting MCAP control PSM: %s", err->message);
+		goto fail;
+	}
+
+	hdp_adapter->dcpsm = mcap_get_data_psm(hdp_adapter->mi, &err);
+	if (err != NULL) {
+		error("Error getting MCAP data PSM: %s", err->message);
+		goto fail;
+	}
+
+update:
+	if (hdp_update_sdp_record(hdp_adapter, applications))
+		return TRUE;
+	error("Error updating the SDP record");
+
+fail:
+	release_adapter_instance(hdp_adapter);
+	if (err != NULL)
+		g_error_free(err);
+
+	return FALSE;
+}
+
+int hdp_adapter_register(struct btd_adapter *adapter)
+{
+	struct hdp_adapter *hdp_adapter;
+
+	hdp_adapter = g_new0(struct hdp_adapter, 1);
+	hdp_adapter->btd_adapter = btd_adapter_ref(adapter);
+
+	if(!update_adapter(hdp_adapter))
+		goto fail;
+
+	adapters = g_slist_append(adapters, hdp_adapter);
+
+	return 0;
+
+fail:
+	btd_adapter_unref(hdp_adapter->btd_adapter);
+	g_free(hdp_adapter);
+	return -1;
+}
+
+void hdp_adapter_unregister(struct btd_adapter *adapter)
+{
+	struct hdp_adapter *hdp_adapter;
+	GSList *l;
+
+	l = g_slist_find_custom(adapters, adapter, cmp_adapter);
+
+	if (l == NULL)
+		return;
+
+	hdp_adapter = l->data;
+	adapters = g_slist_remove(adapters, hdp_adapter);
+	if (hdp_adapter->sdp_handler > 0)
+		adapter_service_remove(adapter, hdp_adapter->sdp_handler);
+	release_adapter_instance(hdp_adapter);
+	btd_adapter_unref(hdp_adapter->btd_adapter);
+	g_free(hdp_adapter);
+}
+
+static void delete_echo_channel_cb(GError *err, gpointer chan)
+{
+	if (err != NULL && err->code != MCAP_INVALID_MDL) {
+		/* TODO: Decide if more action is required here */
+		error("Error deleting echo channel: %s", err->message);
+		return;
+	}
+
+	health_channel_destroy(chan);
+}
+
+static void delete_echo_channel(struct hdp_channel *chan)
+{
+	GError *err = NULL;
+
+	if (!chan->dev->mcl_conn) {
+		error("Echo channel cannot be deleted: mcl closed");
+		return;
+	}
+
+	if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb,
+				hdp_channel_ref(chan),
+				(GDestroyNotify) hdp_channel_unref, &err))
+		return;
+
+	hdp_channel_unref(chan);
+	error("Error deleting the echo channel: %s", err->message);
+	g_error_free(err);
+
+	/* TODO: Decide if more action is required here */
+}
+
+static void abort_echo_channel_cb(GError *err, gpointer data)
+{
+	struct hdp_channel *chan = data;
+
+	if (err != NULL && err->code != MCAP_ERROR_INVALID_OPERATION) {
+		error("Aborting error: %s", err->message);
+		if (err->code == MCAP_INVALID_MDL) {
+			/* MDL is removed from MCAP so we can */
+			/* free the data channel without sending */
+			/* a MD_DELETE_MDL_REQ */
+			/* TODO review the above comment */
+			/* hdp_channel_unref(chan); */
+		}
+		return;
+	}
+
+	delete_echo_channel(chan);
+}
+
+static void destroy_create_dc_data(gpointer data)
+{
+	struct hdp_create_dc *dc_data = data;
+
+	hdp_create_data_unref(dc_data);
+}
+
+static void *generate_echo_packet(void)
+{
+	uint8_t *buf;
+	int i;
+
+	buf = g_malloc(HDP_ECHO_LEN);
+	srand(time(NULL));
+
+	for(i = 0; i < HDP_ECHO_LEN; i++)
+		buf[i] = rand() % UINT8_MAX;
+
+	return buf;
+}
+
+static gboolean check_echo(GIOChannel *io_chan, GIOCondition cond,
+								gpointer data)
+{
+	struct hdp_tmp_dc_data *hdp_conn =  data;
+	struct hdp_echo_data *edata = hdp_conn->hdp_chann->edata;
+	struct hdp_channel *chan = hdp_conn->hdp_chann;
+	uint8_t buf[MCAP_DC_MTU];
+	DBusMessage *reply;
+	gboolean value;
+	int fd, len;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		value = FALSE;
+		goto end;
+	}
+
+	fd = g_io_channel_unix_get_fd(io_chan);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len != HDP_ECHO_LEN) {
+		value = FALSE;
+		goto end;
+	}
+
+	value = (memcmp(buf, edata->buf, len) == 0);
+
+end:
+	reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_BOOLEAN, &value,
+							DBUS_TYPE_INVALID);
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	g_source_remove(edata->tid);
+	edata->tid = 0;
+	g_free(edata->buf);
+	edata->buf = NULL;
+
+	if (!value)
+		close_device_con(chan->dev, FALSE);
+	else
+		delete_echo_channel(chan);
+	hdp_tmp_dc_data_unref(hdp_conn);
+
+	return FALSE;
+}
+
+static gboolean echo_timeout(gpointer data)
+{
+	struct hdp_channel *chan = data;
+	GIOChannel *io;
+	int fd;
+
+	error("Error: Echo request timeout");
+	chan->edata->tid = 0;
+
+	fd = mcap_mdl_get_fd(chan->mdl);
+	if (fd < 0)
+		return FALSE;
+
+	io = g_io_channel_unix_new(fd);
+	g_io_channel_shutdown(io, TRUE, NULL);
+
+	return FALSE;
+}
+
+static void hdp_echo_connect_cb(struct mcap_mdl *mdl, GError *err,
+								gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_tmp_dc_data *hdp_conn =  data;
+	struct hdp_echo_data *edata;
+	GError *gerr = NULL;
+	DBusMessage *reply;
+	GIOChannel *io;
+	int fd;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(hdp_conn->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", err->message);
+		g_dbus_send_message(conn, reply);
+
+		/* Send abort request because remote */
+		/* side is now in PENDING state. */
+		if (!mcap_mdl_abort(hdp_conn->hdp_chann->mdl,
+					abort_echo_channel_cb,
+					hdp_channel_ref(hdp_conn->hdp_chann),
+					(GDestroyNotify) hdp_channel_unref,
+					&gerr)) {
+			error("%s", gerr->message);
+			g_error_free(gerr);
+			hdp_channel_unref(hdp_conn->hdp_chann);
+		}
+		return;
+	}
+
+	fd = mcap_mdl_get_fd(hdp_conn->hdp_chann->mdl);
+	if (fd < 0) {
+		reply = g_dbus_create_error(hdp_conn->msg,
+						ERROR_INTERFACE ".HealthError",
+						"Can't write in echo channel");
+		g_dbus_send_message(conn, reply);
+		delete_echo_channel(hdp_conn->hdp_chann);
+		return;
+	}
+
+	edata = hdp_conn->hdp_chann->edata;
+	edata->buf = generate_echo_packet();
+	send_echo_data(fd, edata->buf, HDP_ECHO_LEN);
+
+	io = g_io_channel_unix_new(fd);
+	g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+			check_echo, hdp_tmp_dc_data_ref(hdp_conn));
+
+	edata->tid = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+					ECHO_TIMEOUT, echo_timeout,
+					hdp_channel_ref(hdp_conn->hdp_chann),
+					(GDestroyNotify) hdp_channel_unref);
+
+	g_io_channel_unref(io);
+}
+
+static void delete_mdl_cb(GError *err, gpointer data)
+{
+	if (err != NULL)
+		error("Deleting error: %s", err->message);
+}
+
+static void abort_and_del_mdl_cb(GError *err, gpointer data)
+{
+	struct mcap_mdl *mdl = data;
+	GError *gerr = NULL;
+
+	if (err != NULL) {
+		error("%s", err->message);
+		if (err->code == MCAP_INVALID_MDL) {
+			/* MDL is removed from MCAP so we don't */
+			/* need to delete it. */
+			return;
+		}
+	}
+
+	if (!mcap_delete_mdl(mdl, delete_mdl_cb, NULL, NULL, &gerr)) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+	}
+}
+
+static void abort_mdl_connection_cb(GError *err, gpointer data)
+{
+	struct hdp_tmp_dc_data *hdp_conn = data;
+	struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+
+	if (err != NULL)
+		error("Aborting error: %s", err->message);
+
+	/* Connection operation has failed but we have to */
+	/* notify the channel created at MCAP level */
+	if (hdp_chann->mdep != HDP_MDEP_ECHO)
+		g_dbus_emit_signal(btd_get_dbus_connection(),
+					device_get_path(hdp_chann->dev->dev),
+					HEALTH_DEVICE, "ChannelConnected",
+					DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+					DBUS_TYPE_INVALID);
+}
+
+static void hdp_mdl_conn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_tmp_dc_data *hdp_conn =  data;
+	struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+	struct hdp_device *dev = hdp_chann->dev;
+	DBusMessage *reply;
+	GError *gerr = NULL;
+
+	if (err != NULL) {
+		error("%s", err->message);
+		reply = g_dbus_create_reply(hdp_conn->msg,
+					DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+					DBUS_TYPE_INVALID);
+		g_dbus_send_message(conn, reply);
+
+		/* Send abort request because remote side */
+		/* is now in PENDING state */
+		if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_connection_cb,
+					hdp_tmp_dc_data_ref(hdp_conn),
+					hdp_tmp_dc_data_destroy, &gerr)) {
+			hdp_tmp_dc_data_unref(hdp_conn);
+			error("%s", gerr->message);
+			g_error_free(gerr);
+		}
+		return;
+	}
+
+	reply = g_dbus_create_reply(hdp_conn->msg,
+					DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+					DBUS_TYPE_INVALID);
+	g_dbus_send_message(conn, reply);
+
+	g_dbus_emit_signal(conn, device_get_path(hdp_chann->dev->dev),
+				HEALTH_DEVICE, "ChannelConnected",
+				DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+				DBUS_TYPE_INVALID);
+
+	if (!check_channel_conf(hdp_chann)) {
+		close_mdl(hdp_chann);
+		return;
+	}
+
+	if (dev->fr != NULL)
+		return;
+
+	dev->fr = hdp_channel_ref(hdp_chann);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				device_get_path(dev->dev), HEALTH_DEVICE,
+				"MainChannel");
+}
+
+static void device_create_mdl_cb(struct mcap_mdl *mdl, uint8_t conf,
+						GError *err, gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_create_dc *user_data = data;
+	struct hdp_tmp_dc_data *hdp_conn;
+	struct hdp_channel *hdp_chan;
+	GError *gerr = NULL;
+	DBusMessage *reply;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(user_data->msg,
+					ERROR_INTERFACE ".HealthError",
+					"%s", err->message);
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	if (user_data->mdep != HDP_MDEP_ECHO &&
+				user_data->config == HDP_NO_PREFERENCE_DC) {
+		if (user_data->dev->fr == NULL && conf != HDP_RELIABLE_DC) {
+			g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+					"Data channel aborted, first data "
+					"channel should be reliable");
+			goto fail;
+		} else if (conf == HDP_NO_PREFERENCE_DC ||
+						conf > HDP_STREAMING_DC) {
+			g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+							"Data channel aborted, "
+							"configuration error");
+			goto fail;
+		}
+	}
+
+	hdp_chan = create_channel(user_data->dev, conf, mdl,
+							mcap_mdl_get_mdlid(mdl),
+							user_data->app, &gerr);
+	if (hdp_chan == NULL)
+		goto fail;
+
+	hdp_conn = g_new0(struct hdp_tmp_dc_data, 1);
+	hdp_conn->msg = dbus_message_ref(user_data->msg);
+	hdp_conn->hdp_chann = hdp_chan;
+	hdp_conn->cb = user_data->cb;
+	hdp_chan->mdep = user_data->mdep;
+
+	if (hdp_get_dcpsm(hdp_chan->dev, hdp_get_dcpsm_cb,
+						hdp_tmp_dc_data_ref(hdp_conn),
+						hdp_tmp_dc_data_destroy, &gerr))
+		return;
+
+	error("%s", gerr->message);
+	g_error_free(gerr);
+
+	reply = g_dbus_create_reply(hdp_conn->msg,
+					DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+					DBUS_TYPE_INVALID);
+	g_dbus_send_message(conn, reply);
+	hdp_tmp_dc_data_unref(hdp_conn);
+
+	/* Send abort request because remote side is now in PENDING state */
+	if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_connection_cb,
+					hdp_tmp_dc_data_ref(hdp_conn),
+					hdp_tmp_dc_data_destroy, &gerr)) {
+		hdp_tmp_dc_data_unref(hdp_conn);
+		error("%s", gerr->message);
+		g_error_free(gerr);
+	}
+
+	return;
+
+fail:
+	reply = g_dbus_create_error(user_data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", gerr->message);
+	g_dbus_send_message(conn, reply);
+	g_error_free(gerr);
+
+	/* Send abort request because remote side is now in PENDING */
+	/* state. Then we have to delete it because we couldn't */
+	/* register the HealthChannel interface */
+	if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl),
+				(GDestroyNotify) mcap_mdl_unref, &gerr)) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		mcap_mdl_unref(mdl);
+	}
+}
+
+static void device_create_dc_cb(gpointer user_data, GError *err)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_create_dc *data = user_data;
+	DBusMessage *reply;
+	GError *gerr = NULL;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(data->msg,
+					ERROR_INTERFACE ".HealthError",
+					"%s", err->message);
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	if (data->dev->mcl == NULL) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+				"Mcl was closed");
+		goto fail;
+	}
+
+	hdp_create_data_ref(data);
+
+	if (mcap_create_mdl(data->dev->mcl, data->mdep, data->config,
+						device_create_mdl_cb, data,
+						destroy_create_dc_data, &gerr))
+		return;
+	hdp_create_data_unref(data);
+
+fail:
+	reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
+							"%s", gerr->message);
+	g_error_free(gerr);
+	g_dbus_send_message(conn, reply);
+}
+
+static DBusMessage *device_echo(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct hdp_device *device = user_data;
+	struct hdp_create_dc *data;
+	DBusMessage *reply;
+	GError *err = NULL;
+
+	data = g_new0(struct hdp_create_dc, 1);
+	data->dev = health_device_ref(device);
+	data->mdep = HDP_MDEP_ECHO;
+	data->config = HDP_RELIABLE_DC;
+	data->msg = dbus_message_ref(msg);
+	data->cb = hdp_echo_connect_cb;
+	hdp_create_data_ref(data);
+
+	if (device->mcl_conn && device->mcl) {
+		if (mcap_create_mdl(device->mcl, data->mdep, data->config,
+						device_create_mdl_cb, data,
+						destroy_create_dc_data, &err))
+			return NULL;
+		goto fail;
+	}
+
+	if (hdp_establish_mcl(data->dev, device_create_dc_cb,
+					data, destroy_create_dc_data, &err))
+		return NULL;
+
+fail:
+	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+							"%s", err->message);
+	g_error_free(err);
+	hdp_create_data_unref(data);
+	return reply;
+}
+
+static void device_get_mdep_cb(uint8_t mdep, gpointer data, GError *err)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_create_dc *dc_data, *user_data = data;
+	DBusMessage *reply;
+	GError *gerr = NULL;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(user_data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", err->message);
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	dc_data = hdp_create_data_ref(user_data);
+	dc_data->mdep = mdep;
+
+	if (user_data->dev->mcl_conn) {
+		device_create_dc_cb(dc_data, NULL);
+		hdp_create_data_unref(dc_data);
+		return;
+	}
+
+	if (hdp_establish_mcl(dc_data->dev, device_create_dc_cb,
+					dc_data, destroy_create_dc_data, &gerr))
+		return;
+
+	reply = g_dbus_create_error(user_data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", gerr->message);
+	hdp_create_data_unref(dc_data);
+	g_error_free(gerr);
+	g_dbus_send_message(conn, reply);
+}
+
+static DBusMessage *device_create_channel(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct hdp_device *device = user_data;
+	struct hdp_application *app;
+	struct hdp_create_dc *data;
+	char *app_path, *conf;
+	DBusMessage *reply;
+	GError *err = NULL;
+	uint8_t config;
+	GSList *l;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &app_path,
+							DBUS_TYPE_STRING, &conf,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	l = g_slist_find_custom(applications, app_path, cmp_app);
+	if (l == NULL)
+		return btd_error_invalid_args(msg);
+
+	app = l->data;
+
+	if (g_ascii_strcasecmp("reliable", conf) == 0)
+		config = HDP_RELIABLE_DC;
+	else if (g_ascii_strcasecmp("streaming", conf) == 0)
+		config = HDP_STREAMING_DC;
+	else if (g_ascii_strcasecmp("any", conf) == 0)
+		config = HDP_NO_PREFERENCE_DC;
+	else
+		return btd_error_invalid_args(msg);
+
+	if (app->role == HDP_SINK && config != HDP_NO_PREFERENCE_DC)
+		return btd_error_invalid_args(msg);
+
+	if (app->role == HDP_SOURCE && config == HDP_NO_PREFERENCE_DC)
+		return btd_error_invalid_args(msg);
+
+	if (!device->fr && config == HDP_STREAMING_DC)
+		return btd_error_invalid_args(msg);
+
+	data = g_new0(struct hdp_create_dc, 1);
+	data->dev = health_device_ref(device);
+	data->config = config;
+	data->app = hdp_application_ref(app);
+	data->msg = dbus_message_ref(msg);
+	data->cb = hdp_mdl_conn_cb;
+
+	if (hdp_get_mdep(device, l->data, device_get_mdep_cb,
+						hdp_create_data_ref(data),
+						destroy_create_dc_data, &err))
+		return NULL;
+
+	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+							"%s", err->message);
+	g_error_free(err);
+	hdp_create_data_unref(data);
+	return reply;
+}
+
+static void hdp_mdl_delete_cb(GError *err, gpointer data)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_tmp_dc_data *del_data = data;
+	DBusMessage *reply;
+	char *path;
+
+	if (err != NULL && err->code != MCAP_INVALID_MDL) {
+		reply = g_dbus_create_error(del_data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", err->message);
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	path = g_strdup(del_data->hdp_chann->path);
+	g_dbus_unregister_interface(conn, path, HEALTH_CHANNEL);
+	g_free(path);
+
+	reply = g_dbus_create_reply(del_data->msg, DBUS_TYPE_INVALID);
+	g_dbus_send_message(conn, reply);
+}
+
+static void hdp_continue_del_cb(gpointer user_data, GError *err)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	struct hdp_tmp_dc_data *del_data = user_data;
+	GError *gerr = NULL;
+	DBusMessage *reply;
+
+	if (err != NULL) {
+		reply = g_dbus_create_error(del_data->msg,
+					ERROR_INTERFACE ".HealthError",
+					"%s", err->message);
+		g_dbus_send_message(conn, reply);
+		return;
+	}
+
+	if (mcap_delete_mdl(del_data->hdp_chann->mdl, hdp_mdl_delete_cb,
+						hdp_tmp_dc_data_ref(del_data),
+						hdp_tmp_dc_data_destroy, &gerr))
+			return;
+
+	reply = g_dbus_create_error(del_data->msg,
+						ERROR_INTERFACE ".HealthError",
+						"%s", gerr->message);
+	hdp_tmp_dc_data_unref(del_data);
+	g_error_free(gerr);
+	g_dbus_send_message(conn, reply);
+}
+
+static DBusMessage *device_destroy_channel(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct hdp_device *device = user_data;
+	struct hdp_tmp_dc_data *del_data;
+	struct hdp_channel *hdp_chan;
+	DBusMessage *reply;
+	GError *err = NULL;
+	char *path;
+	GSList *l;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID)){
+		return btd_error_invalid_args(msg);
+	}
+
+	l = g_slist_find_custom(device->channels, path, cmp_chan_path);
+	if (l == NULL)
+		return btd_error_invalid_args(msg);
+
+	hdp_chan = l->data;
+	del_data = g_new0(struct hdp_tmp_dc_data, 1);
+	del_data->msg = dbus_message_ref(msg);
+	del_data->hdp_chann = hdp_channel_ref(hdp_chan);
+
+	if (device->mcl_conn) {
+		if (mcap_delete_mdl(hdp_chan->mdl, hdp_mdl_delete_cb,
+						hdp_tmp_dc_data_ref(del_data),
+						hdp_tmp_dc_data_destroy, &err))
+			return NULL;
+		goto fail;
+	}
+
+	if (hdp_establish_mcl(device, hdp_continue_del_cb,
+						hdp_tmp_dc_data_ref(del_data),
+						hdp_tmp_dc_data_destroy, &err))
+		return NULL;
+
+fail:
+	reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+							"%s", err->message);
+	hdp_tmp_dc_data_unref(del_data);
+	g_error_free(err);
+	return reply;
+}
+
+static gboolean dev_property_exists_main_channel(
+				const GDBusPropertyTable *property, void *data)
+{
+	struct hdp_device *device = data;
+	return device->fr != NULL;
+}
+
+static gboolean dev_property_get_main_channel(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct hdp_device *device = data;
+
+	if (device->fr == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+							&device->fr->path);
+
+	return TRUE;
+}
+
+static void health_device_destroy(void *data)
+{
+	struct hdp_device *device = data;
+
+	DBG("Unregistered interface %s on path %s", HEALTH_DEVICE,
+						device_get_path(device->dev));
+
+	remove_channels(device);
+	if (device->ndc != NULL) {
+		hdp_channel_unref(device->ndc);
+		device->ndc = NULL;
+	}
+
+	devices = g_slist_remove(devices, device);
+	health_device_unref(device);
+}
+
+static const GDBusMethodTable health_device_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Echo",
+			NULL, GDBUS_ARGS({ "value", "b" }), device_echo) },
+	{ GDBUS_ASYNC_METHOD("CreateChannel",
+			GDBUS_ARGS({ "application", "o" },
+					{ "configuration", "s" }),
+			GDBUS_ARGS({ "channel", "o" }),
+			device_create_channel) },
+	{ GDBUS_ASYNC_METHOD("DestroyChannel",
+			GDBUS_ARGS({ "channel", "o" }), NULL,
+			device_destroy_channel) },
+	{ }
+};
+
+static const GDBusSignalTable health_device_signals[] = {
+	{ GDBUS_SIGNAL("ChannelConnected",
+			GDBUS_ARGS({ "channel", "o" })) },
+	{ GDBUS_SIGNAL("ChannelDeleted",
+			GDBUS_ARGS({ "channel", "o" })) },
+	{ }
+};
+
+static const GDBusPropertyTable health_device_properties[] = {
+	{ "MainChannel", "o", dev_property_get_main_channel, NULL,
+					dev_property_exists_main_channel },
+	{ }
+};
+
+static struct hdp_device *create_health_device(struct btd_device *device)
+{
+	struct btd_adapter *adapter = device_get_adapter(device);
+	const char *path = device_get_path(device);
+	struct hdp_device *dev;
+	GSList *l;
+
+	if (device == NULL)
+		return NULL;
+
+	dev = g_new0(struct hdp_device, 1);
+	dev->dev = btd_device_ref(device);
+	health_device_ref(dev);
+
+	l = g_slist_find_custom(adapters, adapter, cmp_adapter);
+	if (l == NULL)
+		goto fail;
+
+	dev->hdp_adapter = l->data;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					path, HEALTH_DEVICE,
+					health_device_methods,
+					health_device_signals,
+					health_device_properties,
+					dev, health_device_destroy)) {
+		error("D-Bus failed to register %s interface", HEALTH_DEVICE);
+		goto fail;
+	}
+
+	DBG("Registered interface %s on path %s", HEALTH_DEVICE, path);
+	return dev;
+
+fail:
+	health_device_unref(dev);
+	return NULL;
+}
+
+int hdp_device_register(struct btd_device *device)
+{
+	struct hdp_device *hdev;
+	GSList *l;
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (l != NULL) {
+		hdev = l->data;
+		hdev->sdp_present = TRUE;
+		return 0;
+	}
+
+	hdev = create_health_device(device);
+	if (hdev == NULL)
+		return -1;
+
+	hdev->sdp_present = TRUE;
+
+	devices = g_slist_prepend(devices, hdev);
+	return 0;
+}
+
+void hdp_device_unregister(struct btd_device *device)
+{
+	struct hdp_device *hdp_dev;
+	const char *path;
+	GSList *l;
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (l == NULL)
+		return;
+
+	hdp_dev = l->data;
+	path = device_get_path(hdp_dev->dev);
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+							path, HEALTH_DEVICE);
+}
+
+int hdp_manager_start(void)
+{
+	DBG("Starting Health manager");
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					MANAGER_PATH, HEALTH_MANAGER,
+					health_manager_methods, NULL, NULL,
+					NULL, manager_path_unregister)) {
+		error("D-Bus failed to register %s interface", HEALTH_MANAGER);
+		return -1;
+	}
+
+	return 0;
+}
+
+void hdp_manager_stop(void)
+{
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						MANAGER_PATH, HEALTH_MANAGER);
+
+	DBG("Stopped Health manager");
+}
+
+struct hdp_device *health_device_ref(struct hdp_device *hdp_dev)
+{
+	hdp_dev->ref++;
+
+	DBG("(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+	return hdp_dev;
+}
+
+void health_device_unref(struct hdp_device *hdp_dev)
+{
+	hdp_dev->ref--;
+
+	DBG("(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+	if (hdp_dev->ref > 0)
+		return;
+
+	free_health_device(hdp_dev);
+}
diff --git a/bluez/repo/profiles/health/hdp.h b/bluez/repo/profiles/health/hdp.h
new file mode 100644
index 0000000..6e78b09
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int hdp_adapter_register(struct btd_adapter *btd_adapter);
+void hdp_adapter_unregister(struct btd_adapter *btd_adapter);
+
+int hdp_device_register(struct btd_device *device);
+void hdp_device_unregister(struct btd_device *device);
+
+int hdp_manager_start(void);
+void hdp_manager_stop(void);
+
+gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err);
diff --git a/bluez/repo/profiles/health/hdp_main.c b/bluez/repo/profiles/health/hdp_main.c
new file mode 100644
index 0000000..2c4bbe2
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp_main.c
@@ -0,0 +1,45 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "hdp_manager.h"
+
+static int hdp_init(void)
+{
+	return hdp_manager_init();
+}
+
+static void hdp_exit(void)
+{
+	hdp_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(health, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hdp_init, hdp_exit)
diff --git a/bluez/repo/profiles/health/hdp_manager.c b/bluez/repo/profiles/health/hdp_manager.c
new file mode 100644
index 0000000..401adf6
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp_manager.c
@@ -0,0 +1,107 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
+
+#include "hdp_types.h"
+#include "hdp_manager.h"
+#include "hdp.h"
+
+static int hdp_adapter_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	return hdp_adapter_register(adapter);
+}
+
+static void hdp_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	hdp_adapter_unregister(adapter);
+}
+
+static int hdp_driver_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	return hdp_device_register(device);
+}
+
+static void hdp_driver_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	hdp_device_unregister(device);
+}
+
+static struct btd_profile hdp_source_profile = {
+	.name		= "hdp-source",
+	.remote_uuid	= HDP_SOURCE_UUID,
+
+	.device_probe	= hdp_driver_probe,
+	.device_remove	= hdp_driver_remove,
+
+	.adapter_probe	= hdp_adapter_probe,
+	.adapter_remove	= hdp_adapter_remove,
+};
+
+static struct btd_profile hdp_sink_profile = {
+	.name		= "hdp-sink",
+	.remote_uuid	= HDP_SINK_UUID,
+
+	.device_probe	= hdp_driver_probe,
+	.device_remove	= hdp_driver_remove,
+};
+
+int hdp_manager_init(void)
+{
+	if (hdp_manager_start() < 0)
+		return -1;
+
+	btd_profile_register(&hdp_source_profile);
+	btd_profile_register(&hdp_sink_profile);
+
+	return 0;
+}
+
+void hdp_manager_exit(void)
+{
+	btd_profile_unregister(&hdp_sink_profile);
+	btd_profile_unregister(&hdp_source_profile);
+
+	hdp_manager_stop();
+}
diff --git a/bluez/repo/profiles/health/hdp_manager.h b/bluez/repo/profiles/health/hdp_manager.h
new file mode 100644
index 0000000..1cab4d0
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp_manager.h
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int hdp_manager_init(void);
+void hdp_manager_exit(void);
diff --git a/bluez/repo/profiles/health/hdp_types.h b/bluez/repo/profiles/health/hdp_types.h
new file mode 100644
index 0000000..b34b5e0
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp_types.h
@@ -0,0 +1,120 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HDP_TYPES_H__
+#define __HDP_TYPES_H__
+
+#define MANAGER_PATH		"/org/bluez"
+
+#define HEALTH_MANAGER		"org.bluez.HealthManager1"
+#define HEALTH_DEVICE		"org.bluez.HealthDevice1"
+#define HEALTH_CHANNEL		"org.bluez.HealthChannel1"
+
+#define HDP_VERSION		0x0100
+
+#define HDP_SERVICE_NAME	"Bluez HDP"
+#define HDP_SERVICE_DSC		"A Bluez health device profile implementation"
+#define HDP_SERVICE_PROVIDER	"Bluez"
+
+#define HDP_MDEP_ECHO		0x00
+#define HDP_MDEP_INITIAL	0x01
+#define HDP_MDEP_FINAL		0x7F
+
+#define HDP_ERROR		g_quark_from_static_string("hdp-error-quark")
+
+#define HDP_NO_PREFERENCE_DC	0x00
+#define HDP_RELIABLE_DC		0x01
+#define HDP_STREAMING_DC	0x02
+
+#define HDP_SINK_ROLE_AS_STRING		"sink"
+#define HDP_SOURCE_ROLE_AS_STRING	"source"
+
+typedef enum {
+	HDP_SOURCE = 0x00,
+	HDP_SINK = 0x01
+} HdpRole;
+
+typedef enum {
+	HDP_DIC_PARSE_ERROR,
+	HDP_DIC_ENTRY_PARSE_ERROR,
+	HDP_CONNECTION_ERROR,
+	HDP_UNSPECIFIED_ERROR,
+	HDP_UNKNOWN_ERROR
+} HdpError;
+
+enum data_specs {
+	DATA_EXCHANGE_SPEC_11073 = 0x01
+};
+
+struct hdp_application {
+	char			*path;		/* The path of the application */
+	uint16_t		data_type;	/* Data type handled for this application */
+	gboolean		data_type_set;	/* Flag for dictionary parsing */
+	uint8_t			role;		/* Role of this application */
+	gboolean		role_set;	/* Flag for dictionary parsing */
+	uint8_t			chan_type;	/* QoS preferred by source applications */
+	gboolean		chan_type_set;	/* Flag for dictionary parsing */
+	char			*description;	/* Options description for SDP record */
+	uint8_t			id;		/* The identification is also the mdepid */
+	char			*oname;		/* Name of the owner application */
+	guint			dbus_watcher;	/* Watch for clients disconnection */
+	int			ref;		/* Reference counter */
+};
+
+struct hdp_adapter {
+	struct btd_adapter	*btd_adapter;	/* Bluetooth adapter */
+	struct mcap_instance	*mi;		/* Mcap instance in */
+	uint16_t		ccpsm;		/* Control channel psm */
+	uint16_t		dcpsm;		/* Data channel psm */
+	uint32_t		sdp_handler;	/* SDP record handler */
+	uint32_t		record_state;	/* Service record state */
+};
+
+struct hdp_device {
+	struct btd_device	*dev;		/* Device reference */
+	struct hdp_adapter	*hdp_adapter;	/* hdp_adapater */
+	struct mcap_mcl		*mcl;		/* The mcap control channel */
+	gboolean		mcl_conn;	/* Mcl status */
+	gboolean		sdp_present;	/* Has an sdp record */
+	GSList			*channels;	/* Data Channel list */
+	struct hdp_channel	*ndc;		/* Data channel being negotiated */
+	struct hdp_channel	*fr;		/* First reliable data channel */
+	int			ref;		/* Reference counting */
+};
+
+struct hdp_echo_data;
+
+struct hdp_channel {
+	struct hdp_device	*dev;		/* Device where this channel belongs */
+	struct hdp_application	*app;		/* Application */
+	struct mcap_mdl		*mdl;		/* The data channel reference */
+	char			*path;		/* The path of the channel */
+	uint8_t			config;		/* Channel configuration */
+	uint8_t			mdep;		/* Remote MDEP */
+	uint16_t		mdlid;		/* Data channel Id */
+	uint16_t		imtu;		/* Channel incoming MTU */
+	uint16_t		omtu;		/* Channel outgoing MTU */
+	struct hdp_echo_data	*edata;		/* private data used by echo channels */
+	int			ref;		/* Reference counter */
+};
+
+#endif /* __HDP_TYPES_H__ */
diff --git a/bluez/repo/profiles/health/hdp_util.c b/bluez/repo/profiles/health/hdp_util.c
new file mode 100644
index 0000000..b459eaa
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp_util.c
@@ -0,0 +1,1206 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/sdpd.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+
+#include "mcap.h"
+#include "hdp_types.h"
+#include "hdp.h"
+#include "hdp_util.h"
+
+typedef gboolean (*parse_item_f)(DBusMessageIter *iter, gpointer user_data,
+								GError **err);
+
+struct dict_entry_func {
+	char		*key;
+	parse_item_f	func;
+};
+
+struct get_mdep_data {
+	struct hdp_application	*app;
+	gpointer		data;
+	hdp_continue_mdep_f	func;
+	GDestroyNotify		destroy;
+};
+
+struct conn_mcl_data {
+	int			refs;
+	gpointer		data;
+	hdp_continue_proc_f	func;
+	GDestroyNotify		destroy;
+	struct hdp_device	*dev;
+};
+
+struct get_dcpsm_data {
+	gpointer		data;
+	hdp_continue_dcpsm_f	func;
+	GDestroyNotify		destroy;
+};
+
+static gboolean parse_dict_entry(struct dict_entry_func dict_context[],
+							DBusMessageIter *iter,
+							GError **err,
+							gpointer user_data)
+{
+	DBusMessageIter entry;
+	char *key;
+	int ctype, i;
+	struct dict_entry_func df;
+
+	dbus_message_iter_recurse(iter, &entry);
+	ctype = dbus_message_iter_get_arg_type(&entry);
+	if (ctype != DBUS_TYPE_STRING) {
+		g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+			"Dictionary entries should have a string as key");
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(&entry, &key);
+	dbus_message_iter_next(&entry);
+	/* Find function and call it */
+	for (i = 0, df = dict_context[0]; df.key; i++, df = dict_context[i]) {
+		if (g_ascii_strcasecmp(df.key, key) == 0)
+			return df.func(&entry, user_data, err);
+	}
+
+	g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+			"No function found for parsing value for key %s", key);
+	return FALSE;
+}
+
+static gboolean parse_dict(struct dict_entry_func dict_context[],
+							DBusMessageIter *iter,
+							GError **err,
+							gpointer user_data)
+{
+	int ctype;
+	DBusMessageIter dict;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype != DBUS_TYPE_ARRAY) {
+		g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+					"Dictionary should be an array");
+		return FALSE;
+	}
+
+	dbus_message_iter_recurse(iter, &dict);
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		if (ctype != DBUS_TYPE_DICT_ENTRY) {
+			g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+						"Dictionary array should "
+						"contain dict entries");
+			return FALSE;
+		}
+
+		/* Start parsing entry */
+		if (!parse_dict_entry(dict_context, &dict, err,
+							user_data))
+			return FALSE;
+		/* Finish entry parsing */
+
+		dbus_message_iter_next(&dict);
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_data_type(DBusMessageIter *iter, gpointer data,
+								GError **err)
+{
+	struct hdp_application *app = data;
+	DBusMessageIter *value;
+	DBusMessageIter variant;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	value = iter;
+	if (ctype == DBUS_TYPE_VARIANT) {
+		/* Get value inside the variable */
+		dbus_message_iter_recurse(iter, &variant);
+		ctype = dbus_message_iter_get_arg_type(&variant);
+		value = &variant;
+	}
+
+	if (ctype != DBUS_TYPE_UINT16) {
+		g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+			"Final value for data type should be uint16");
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(value, &app->data_type);
+	app->data_type_set = TRUE;
+	return TRUE;
+}
+
+static gboolean parse_role(DBusMessageIter *iter, gpointer data, GError **err)
+{
+	struct hdp_application *app = data;
+	DBusMessageIter *string;
+	DBusMessageIter value;
+	int ctype;
+	const char *role;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype == DBUS_TYPE_VARIANT) {
+		/* Get value inside the variable */
+		dbus_message_iter_recurse(iter, &value);
+		ctype = dbus_message_iter_get_arg_type(&value);
+		string = &value;
+	} else {
+		string = iter;
+	}
+
+	if (ctype != DBUS_TYPE_STRING) {
+		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+				"Value data spec should be variable or string");
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(string, &role);
+	if (g_ascii_strcasecmp(role, HDP_SINK_ROLE_AS_STRING) == 0) {
+		app->role = HDP_SINK;
+	} else if (g_ascii_strcasecmp(role, HDP_SOURCE_ROLE_AS_STRING) == 0) {
+		app->role = HDP_SOURCE;
+	} else {
+		g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+			"Role value should be \"source\" or \"sink\"");
+		return FALSE;
+	}
+
+	app->role_set = TRUE;
+
+	return TRUE;
+}
+
+static gboolean parse_desc(DBusMessageIter *iter, gpointer data, GError **err)
+{
+	struct hdp_application *app = data;
+	DBusMessageIter *string;
+	DBusMessageIter variant;
+	int ctype;
+	const char *desc;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype == DBUS_TYPE_VARIANT) {
+		/* Get value inside the variable */
+		dbus_message_iter_recurse(iter, &variant);
+		ctype = dbus_message_iter_get_arg_type(&variant);
+		string = &variant;
+	} else {
+		string = iter;
+	}
+
+	if (ctype != DBUS_TYPE_STRING) {
+		g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+				"Value data spec should be variable or string");
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(string, &desc);
+	app->description = g_strdup(desc);
+	return TRUE;
+}
+
+static gboolean parse_chan_type(DBusMessageIter *iter, gpointer data,
+								GError **err)
+{
+	struct hdp_application *app = data;
+	DBusMessageIter *value;
+	DBusMessageIter variant;
+	char *chan_type;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	value = iter;
+	if (ctype == DBUS_TYPE_VARIANT) {
+		/* Get value inside the variable */
+		dbus_message_iter_recurse(iter, &variant);
+		ctype = dbus_message_iter_get_arg_type(&variant);
+		value = &variant;
+	}
+
+	if (ctype != DBUS_TYPE_STRING) {
+		g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+			"Final value for channel type should be an string");
+		return FALSE;
+	}
+
+	dbus_message_iter_get_basic(value, &chan_type);
+
+	if (g_ascii_strcasecmp("reliable", chan_type) == 0)
+		app->chan_type = HDP_RELIABLE_DC;
+	else if (g_ascii_strcasecmp("streaming", chan_type) == 0)
+		app->chan_type = HDP_STREAMING_DC;
+	else {
+		g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+						"Invalid value for data type");
+		return FALSE;
+	}
+
+	app->chan_type_set = TRUE;
+
+	return TRUE;
+}
+
+static struct dict_entry_func dict_parser[] = {
+	{"DataType",		parse_data_type},
+	{"Role",		parse_role},
+	{"Description",		parse_desc},
+	{"ChannelType",		parse_chan_type},
+	{NULL, NULL}
+};
+
+struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err)
+{
+	struct hdp_application *app;
+
+	app = g_new0(struct hdp_application, 1);
+	app->ref = 1;
+	if (!parse_dict(dict_parser, iter, err, app))
+		goto fail;
+	if (!app->data_type_set || !app->role_set) {
+		g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+						"Mandatory fields aren't set");
+		goto fail;
+	}
+	return app;
+
+fail:
+	hdp_application_unref(app);
+	return NULL;
+}
+
+static gboolean is_app_role(GSList *app_list, HdpRole role)
+{
+	GSList *l;
+
+	for (l = app_list; l; l = l->next) {
+		struct hdp_application *app = l->data;
+
+		if (app->role == role)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean set_sdp_services_uuid(sdp_record_t *record, HdpRole role)
+{
+	uuid_t svc_uuid_source, svc_uuid_sink;
+	sdp_list_t *svc_list = NULL;
+
+	sdp_uuid16_create(&svc_uuid_sink, HDP_SINK_SVCLASS_ID);
+	sdp_uuid16_create(&svc_uuid_source, HDP_SOURCE_SVCLASS_ID);
+
+	sdp_get_service_classes(record, &svc_list);
+
+	if (role == HDP_SOURCE) {
+		if (!sdp_list_find(svc_list, &svc_uuid_source, sdp_uuid_cmp))
+			svc_list = sdp_list_append(svc_list, &svc_uuid_source);
+	} else if (role == HDP_SINK) {
+		if (!sdp_list_find(svc_list, &svc_uuid_sink, sdp_uuid_cmp))
+			svc_list = sdp_list_append(svc_list, &svc_uuid_sink);
+	}
+
+	if (sdp_set_service_classes(record, svc_list) < 0) {
+		sdp_list_free(svc_list, NULL);
+		return FALSE;
+	}
+
+	sdp_list_free(svc_list, NULL);
+
+	return TRUE;
+}
+
+static gboolean register_service_protocols(struct hdp_adapter *adapter,
+						sdp_record_t *sdp_record)
+{
+	gboolean ret;
+	uuid_t l2cap_uuid, mcap_c_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL, *mcap_ver = NULL;
+	uint16_t version = MCAP_VERSION;
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (l2cap_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	psm = sdp_data_alloc(SDP_UINT16, &adapter->ccpsm);
+	if (psm == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	if (sdp_list_append(l2cap_list, psm) == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (proto_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+	if (mcap_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+	if (mcap_ver == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	if (sdp_list_append(mcap_list, mcap_ver) == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	if (sdp_list_append(proto_list, mcap_list) == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (access_proto_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	ret = TRUE;
+	sdp_set_access_protos(sdp_record, access_proto_list);
+
+end:
+	if (l2cap_list != NULL)
+		sdp_list_free(l2cap_list, NULL);
+	if (mcap_list != NULL)
+		sdp_list_free(mcap_list, NULL);
+	if (proto_list != NULL)
+		sdp_list_free(proto_list, NULL);
+	if (access_proto_list != NULL)
+		sdp_list_free(access_proto_list, NULL);
+	if (psm != NULL)
+		sdp_data_free(psm);
+	if (mcap_ver != NULL)
+		sdp_data_free(mcap_ver);
+
+	return ret;
+}
+
+static gboolean register_service_profiles(sdp_record_t *sdp_record)
+{
+	gboolean ret;
+	sdp_list_t *profile_list;
+	sdp_profile_desc_t hdp_profile;
+
+	/* set hdp information */
+	sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+	hdp_profile.version = HDP_VERSION;
+	profile_list = sdp_list_append(NULL, &hdp_profile);
+	if (profile_list == NULL)
+		return FALSE;
+
+	/* set profile descriptor list */
+	if (sdp_set_profile_descs(sdp_record, profile_list) < 0)
+		ret = FALSE;
+	else
+		ret = TRUE;
+
+	sdp_list_free(profile_list, NULL);
+
+	return ret;
+}
+
+static gboolean register_service_additional_protocols(
+						struct hdp_adapter *adapter,
+						sdp_record_t *sdp_record)
+{
+	gboolean ret = TRUE;
+	uuid_t l2cap_uuid, mcap_d_uuid;
+	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+	sdp_list_t *access_proto_list = NULL;
+	sdp_data_t *psm = NULL;
+
+	/* set l2cap information */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+	if (l2cap_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	psm = sdp_data_alloc(SDP_UINT16, &adapter->dcpsm);
+	if (psm == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	if (sdp_list_append(l2cap_list, psm) == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	proto_list = sdp_list_append(NULL, l2cap_list);
+	if (proto_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	/* set mcap information */
+	sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+	mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+	if (mcap_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	if (sdp_list_append(proto_list, mcap_list) == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	/* attach protocol information to service record */
+	access_proto_list = sdp_list_append(NULL, proto_list);
+	if (access_proto_list == NULL) {
+		ret = FALSE;
+		goto end;
+	}
+
+	sdp_set_add_access_protos(sdp_record, access_proto_list);
+
+end:
+	if (l2cap_list != NULL)
+		sdp_list_free(l2cap_list, NULL);
+	if (mcap_list != NULL)
+		sdp_list_free(mcap_list, NULL);
+	if (proto_list  != NULL)
+		sdp_list_free(proto_list, NULL);
+	if (access_proto_list != NULL)
+		sdp_list_free(access_proto_list, NULL);
+	if (psm != NULL)
+		sdp_data_free(psm);
+
+	return ret;
+}
+
+static sdp_list_t *app_to_sdplist(struct hdp_application *app)
+{
+	sdp_data_t *mdepid,
+		*dtype = NULL,
+		*role = NULL,
+		*desc = NULL;
+	sdp_list_t *f_list = NULL;
+
+	mdepid = sdp_data_alloc(SDP_UINT8, &app->id);
+	if (mdepid == NULL)
+		return NULL;
+
+	dtype = sdp_data_alloc(SDP_UINT16, &app->data_type);
+	if (dtype == NULL)
+		goto fail;
+
+	role = sdp_data_alloc(SDP_UINT8, &app->role);
+	if (role == NULL)
+		goto fail;
+
+	if (app->description != NULL) {
+		desc = sdp_data_alloc(SDP_TEXT_STR8, app->description);
+		if (desc == NULL)
+			goto fail;
+	}
+
+	f_list = sdp_list_append(NULL, mdepid);
+	if (f_list == NULL)
+		goto fail;
+
+	if (sdp_list_append(f_list, dtype) == NULL)
+		goto fail;
+
+	if (sdp_list_append(f_list, role) == NULL)
+		goto fail;
+
+	if (desc != NULL)
+		if (sdp_list_append(f_list, desc) == NULL)
+			goto fail;
+
+	return f_list;
+
+fail:
+	if (f_list != NULL)
+		sdp_list_free(f_list, NULL);
+	if (mdepid != NULL)
+		sdp_data_free(mdepid);
+	if (dtype != NULL)
+		sdp_data_free(dtype);
+	if (role != NULL)
+		sdp_data_free(role);
+	if (desc != NULL)
+		sdp_data_free(desc);
+
+	return NULL;
+}
+
+static void free_hdp_list(void *list)
+{
+	sdp_list_t *hdp_list = list;
+
+	sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static gboolean register_features(struct hdp_application *app,
+						sdp_list_t **sup_features)
+{
+	sdp_list_t *hdp_feature;
+
+	hdp_feature = app_to_sdplist(app);
+	if (hdp_feature == NULL)
+		goto fail;
+
+	if (*sup_features == NULL) {
+		*sup_features = sdp_list_append(NULL, hdp_feature);
+		if (*sup_features == NULL)
+			goto fail;
+	} else if (sdp_list_append(*sup_features, hdp_feature) == NULL) {
+		goto fail;
+	}
+
+	return TRUE;
+
+fail:
+	if (hdp_feature != NULL)
+		sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free);
+	if (*sup_features != NULL)
+		sdp_list_free(*sup_features, free_hdp_list);
+	return FALSE;
+}
+
+static gboolean register_service_sup_features(GSList *app_list,
+						sdp_record_t *sdp_record)
+{
+	GSList *l;
+	sdp_list_t *sup_features = NULL;
+
+	for (l = app_list; l; l = l->next) {
+		if (!register_features(l->data, &sup_features))
+			return FALSE;
+	}
+
+	if (sdp_set_supp_feat(sdp_record, sup_features) < 0) {
+		sdp_list_free(sup_features, free_hdp_list);
+		return FALSE;
+	}
+
+	sdp_list_free(sup_features, free_hdp_list);
+
+	return TRUE;
+}
+
+static gboolean register_data_exchange_spec(sdp_record_t *record)
+{
+	sdp_data_t *spec;
+	uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+	/* As by now 11073 is the only supported we set it by default */
+
+	spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+	if (spec == NULL)
+		return FALSE;
+
+	if (sdp_attr_add(record, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+		sdp_data_free(spec);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean register_mcap_features(sdp_record_t *sdp_record)
+{
+	sdp_data_t *mcap_proc;
+	uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+	mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+	if (mcap_proc == NULL)
+		return FALSE;
+
+	if (sdp_attr_add(sdp_record, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+							mcap_proc) < 0) {
+		sdp_data_free(mcap_proc);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list)
+{
+	sdp_record_t *sdp_record;
+
+	if (adapter->sdp_handler > 0)
+		adapter_service_remove(adapter->btd_adapter,
+					adapter->sdp_handler);
+
+	if (app_list == NULL) {
+		adapter->sdp_handler = 0;
+		return TRUE;
+	}
+
+	sdp_record = sdp_record_alloc();
+	if (sdp_record == NULL)
+		return FALSE;
+
+	if (adapter->sdp_handler > 0)
+		sdp_record->handle = adapter->sdp_handler;
+	else
+		sdp_record->handle = 0xffffffff; /* Set automatically */
+
+	if (is_app_role(app_list, HDP_SINK))
+		set_sdp_services_uuid(sdp_record, HDP_SINK);
+	if (is_app_role(app_list, HDP_SOURCE))
+		set_sdp_services_uuid(sdp_record, HDP_SOURCE);
+
+	if (!register_service_protocols(adapter, sdp_record))
+		goto fail;
+	if (!register_service_profiles(sdp_record))
+		goto fail;
+	if (!register_service_additional_protocols(adapter, sdp_record))
+		goto fail;
+
+	sdp_set_info_attr(sdp_record, HDP_SERVICE_NAME, HDP_SERVICE_PROVIDER,
+							HDP_SERVICE_DSC);
+	if (!register_service_sup_features(app_list, sdp_record))
+		goto fail;
+	if (!register_data_exchange_spec(sdp_record))
+		goto fail;
+
+	register_mcap_features(sdp_record);
+
+	if (sdp_set_record_state(sdp_record, adapter->record_state++) < 0)
+		goto fail;
+
+	if (adapter_service_add(adapter->btd_adapter, sdp_record) < 0)
+		goto fail;
+	adapter->sdp_handler = sdp_record->handle;
+	return TRUE;
+
+fail:
+	if (sdp_record != NULL)
+		sdp_record_free(sdp_record);
+	return FALSE;
+}
+
+static gboolean check_role(uint8_t rec_role, uint8_t app_role)
+{
+	if ((rec_role == HDP_SINK && app_role == HDP_SOURCE) ||
+			(rec_role == HDP_SOURCE && app_role == HDP_SINK))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean get_mdep_from_rec(const sdp_record_t *rec, uint8_t role,
+				uint16_t d_type, uint8_t *mdep, char **desc)
+{
+	sdp_data_t *list, *feat;
+
+	if (desc == NULL && mdep == NULL)
+		return TRUE;
+
+	list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+	if (list == NULL || !SDP_IS_SEQ(list->dtd))
+		return FALSE;
+
+	for (feat = list->val.dataseq; feat; feat = feat->next) {
+		sdp_data_t *data_type, *mdepid, *role_t, *desc_t;
+
+		if (!SDP_IS_SEQ(feat->dtd))
+			continue;
+
+		mdepid = feat->val.dataseq;
+		if (mdepid == NULL)
+			continue;
+
+		data_type = mdepid->next;
+		if (data_type == NULL)
+			continue;
+
+		role_t = data_type->next;
+		if (role_t == NULL)
+			continue;
+
+		desc_t = role_t->next;
+
+		if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 ||
+						role_t->dtd != SDP_UINT8)
+			continue;
+
+		if (data_type->val.uint16 != d_type ||
+					!check_role(role_t->val.uint8, role))
+			continue;
+
+		if (mdep != NULL)
+			*mdep = mdepid->val.uint8;
+
+		if (desc != NULL && desc_t != NULL &&
+						SDP_IS_TEXT_STR(desc_t->dtd))
+			*desc = g_strdup(desc_t->val.str);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void get_mdep_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct get_mdep_data *mdep_data = user_data;
+	GError *gerr = NULL;
+	uint8_t mdep;
+
+	if (err < 0 || recs == NULL) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+					"Error getting remote SDP records");
+		mdep_data->func(0, mdep_data->data, gerr);
+		g_error_free(gerr);
+		return;
+	}
+
+	if (!get_mdep_from_rec(recs->data, mdep_data->app->role,
+				mdep_data->app->data_type, &mdep, NULL)) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+					"No matching MDEP found");
+		mdep_data->func(0, mdep_data->data, gerr);
+		g_error_free(gerr);
+		return;
+	}
+
+	mdep_data->func(mdep, mdep_data->data, NULL);
+}
+
+static void free_mdep_data(gpointer data)
+{
+	struct get_mdep_data *mdep_data = data;
+
+	if (mdep_data->destroy)
+		mdep_data->destroy(mdep_data->data);
+	hdp_application_unref(mdep_data->app);
+
+	g_free(mdep_data);
+}
+
+gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
+				hdp_continue_mdep_f func, gpointer data,
+				GDestroyNotify destroy, GError **err)
+{
+	struct get_mdep_data *mdep_data;
+	const bdaddr_t *src;
+	const bdaddr_t *dst;
+	uuid_t uuid;
+
+	src = btd_adapter_get_address(device_get_adapter(device->dev));
+	dst = device_get_address(device->dev);
+
+	mdep_data = g_new0(struct get_mdep_data, 1);
+	mdep_data->app = hdp_application_ref(app);
+	mdep_data->func = func;
+	mdep_data->data = data;
+	mdep_data->destroy = destroy;
+
+	bt_string2uuid(&uuid, HDP_UUID);
+	if (bt_search_service(src, dst, &uuid, get_mdep_cb, mdep_data,
+						free_mdep_data, 0) < 0) {
+		g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+						"Can't get remote SDP record");
+		g_free(mdep_data);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+	sdp_data_t *iter;
+	int proto;
+
+	if (entry == NULL || !SDP_IS_SEQ(entry->dtd))
+		return FALSE;
+
+	iter = entry->val.dataseq;
+	if (!(iter->dtd & SDP_UUID_UNSPEC))
+		return FALSE;
+
+	proto = sdp_uuid_to_proto(&iter->val.uuid);
+	if (proto != type)
+		return FALSE;
+
+	if (val == NULL)
+		return TRUE;
+
+	iter = iter->next;
+	if (iter->dtd != SDP_UINT16)
+		return FALSE;
+
+	*val = iter->val.uint16;
+
+	return TRUE;
+}
+
+static gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm,
+							guint16 *version)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (psm == NULL && version == NULL)
+		return TRUE;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+	if (pdl == NULL || !SDP_IS_SEQ(pdl->dtd))
+		return FALSE;
+
+	p0 = pdl->val.dataseq;
+	if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+		return FALSE;
+
+	p1 = p0->next;
+	if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean hdp_get_add_prot_desc_list(const sdp_record_t *rec,
+								guint16 *psm)
+{
+	sdp_data_t *pdl, *p0, *p1;
+
+	if (psm == NULL)
+		return TRUE;
+
+	pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+	if (pdl == NULL || pdl->dtd != SDP_SEQ8)
+		return FALSE;
+	pdl = pdl->val.dataseq;
+	if (pdl->dtd != SDP_SEQ8)
+		return FALSE;
+
+	p0 = pdl->val.dataseq;
+
+	if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+		return FALSE;
+	p1 = p0->next;
+	if (!get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (hdp_get_prot_desc_list(rec, ccpsm, NULL))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+	sdp_list_t *l;
+
+	for (l = recs; l; l = l->next) {
+		sdp_record_t *rec = l->data;
+
+		if (hdp_get_add_prot_desc_list(rec, dcpsm))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void con_mcl_data_unref(struct conn_mcl_data *conn_data)
+{
+	if (conn_data == NULL)
+		return;
+
+	if (--conn_data->refs > 0)
+		return;
+
+	if (conn_data->destroy)
+		conn_data->destroy(conn_data->data);
+
+	health_device_unref(conn_data->dev);
+	g_free(conn_data);
+}
+
+static void destroy_con_mcl_data(gpointer data)
+{
+	con_mcl_data_unref(data);
+}
+
+static struct conn_mcl_data *con_mcl_data_ref(struct conn_mcl_data *conn_data)
+{
+	if (conn_data == NULL)
+		return NULL;
+
+	conn_data->refs++;
+	return conn_data;
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+	struct conn_mcl_data *conn_data = data;
+	struct hdp_device *device = conn_data->dev;
+	GError *gerr = NULL;
+
+	if (err != NULL) {
+		conn_data->func(conn_data->data, err);
+		return;
+	}
+
+	if (device->mcl == NULL)
+		device->mcl = mcap_mcl_ref(mcl);
+	device->mcl_conn = TRUE;
+
+	hdp_set_mcl_cb(device, &gerr);
+
+	conn_data->func(conn_data->data, gerr);
+	if (gerr != NULL)
+		g_error_free(gerr);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct conn_mcl_data *conn_data = user_data;
+	GError *gerr = NULL;
+	uint16_t ccpsm;
+
+	if (conn_data->dev->hdp_adapter->mi == NULL) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+						"Mcap instance released");
+		goto fail;
+	}
+
+	if (err < 0 || recs == NULL) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+					"Error getting remote SDP records");
+		goto fail;
+	}
+
+	if (!get_ccpsm(recs, &ccpsm)) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+				"Can't get remote PSM for control channel");
+		goto fail;
+	}
+
+	conn_data = con_mcl_data_ref(conn_data);
+
+	if (!mcap_create_mcl(conn_data->dev->hdp_adapter->mi,
+					device_get_address(conn_data->dev->dev),
+					ccpsm, create_mcl_cb, conn_data,
+					destroy_con_mcl_data, &gerr)) {
+		con_mcl_data_unref(conn_data);
+		goto fail;
+	}
+	return;
+fail:
+	conn_data->func(conn_data->data, gerr);
+	g_error_free(gerr);
+}
+
+gboolean hdp_establish_mcl(struct hdp_device *device,
+						hdp_continue_proc_f func,
+						gpointer data,
+						GDestroyNotify destroy,
+						GError **err)
+{
+	struct conn_mcl_data *conn_data;
+	const bdaddr_t *src;
+	const bdaddr_t *dst;
+	uuid_t uuid;
+
+	src = btd_adapter_get_address(device_get_adapter(device->dev));
+	dst = device_get_address(device->dev);
+
+	conn_data = g_new0(struct conn_mcl_data, 1);
+	conn_data->refs = 1;
+	conn_data->func = func;
+	conn_data->data = data;
+	conn_data->destroy = destroy;
+	conn_data->dev = health_device_ref(device);
+
+	bt_string2uuid(&uuid, HDP_UUID);
+	if (bt_search_service(src, dst, &uuid, search_cb, conn_data,
+					destroy_con_mcl_data, 0) < 0) {
+		g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+						"Can't get remote SDP record");
+		g_free(conn_data);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void get_dcpsm_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	struct get_dcpsm_data *dcpsm_data = data;
+	GError *gerr = NULL;
+	uint16_t dcpsm;
+
+	if (err < 0 || recs == NULL) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+					"Error getting remote SDP records");
+		goto fail;
+	}
+
+	if (!get_dcpsm(recs, &dcpsm)) {
+		g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+				"Can't get remote PSM for data channel");
+		goto fail;
+	}
+
+	dcpsm_data->func(dcpsm, dcpsm_data->data, NULL);
+	return;
+
+fail:
+	dcpsm_data->func(0, dcpsm_data->data, gerr);
+	g_error_free(gerr);
+}
+
+static void free_dcpsm_data(gpointer data)
+{
+	struct get_dcpsm_data *dcpsm_data = data;
+
+	if (dcpsm_data == NULL)
+		return;
+
+	if (dcpsm_data->destroy)
+		dcpsm_data->destroy(dcpsm_data->data);
+
+	g_free(dcpsm_data);
+}
+
+gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
+							gpointer data,
+							GDestroyNotify destroy,
+							GError **err)
+{
+	struct get_dcpsm_data *dcpsm_data;
+	const bdaddr_t *src;
+	const bdaddr_t *dst;
+	uuid_t uuid;
+
+	src = btd_adapter_get_address(device_get_adapter(device->dev));
+	dst = device_get_address(device->dev);
+
+	dcpsm_data = g_new0(struct get_dcpsm_data, 1);
+	dcpsm_data->func = func;
+	dcpsm_data->data = data;
+	dcpsm_data->destroy = destroy;
+
+	bt_string2uuid(&uuid, HDP_UUID);
+	if (bt_search_service(src, dst, &uuid, get_dcpsm_cb, dcpsm_data,
+						free_dcpsm_data, 0) < 0) {
+		g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+						"Can't get remote SDP record");
+		g_free(dcpsm_data);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void hdp_free_application(struct hdp_application *app)
+{
+	if (app->dbus_watcher > 0)
+		g_dbus_remove_watch(btd_get_dbus_connection(),
+							app->dbus_watcher);
+
+	g_free(app->oname);
+	g_free(app->description);
+	g_free(app->path);
+	g_free(app);
+}
+
+struct hdp_application *hdp_application_ref(struct hdp_application *app)
+{
+	if (app == NULL)
+		return NULL;
+
+	app->ref++;
+
+	DBG("health_application_ref(%p): ref=%d", app, app->ref);
+	return app;
+}
+
+void hdp_application_unref(struct hdp_application *app)
+{
+	if (app == NULL)
+		return;
+
+	app->ref--;
+
+	DBG("health_application_unref(%p): ref=%d", app, app->ref);
+	if (app->ref > 0)
+		return;
+
+	hdp_free_application(app);
+}
diff --git a/bluez/repo/profiles/health/hdp_util.h b/bluez/repo/profiles/health/hdp_util.h
new file mode 100644
index 0000000..35e1196
--- /dev/null
+++ b/bluez/repo/profiles/health/hdp_util.h
@@ -0,0 +1,58 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HDP_UTIL_H__
+#define __HDP_UTIL_H__
+
+typedef void (*hdp_continue_mdep_f)(uint8_t mdep, gpointer user_data,
+								GError *err);
+typedef void (*hdp_continue_dcpsm_f)(uint16_t dcpsm, gpointer user_data,
+								GError *err);
+typedef void (*hdp_continue_proc_f)(gpointer user_data, GError *err);
+
+struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err);
+gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list);
+gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
+				hdp_continue_mdep_f func,
+				gpointer data, GDestroyNotify destroy,
+				GError **err);
+
+gboolean hdp_establish_mcl(struct hdp_device *device,
+						hdp_continue_proc_f func,
+						gpointer data,
+						GDestroyNotify destroy,
+						GError **err);
+
+gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
+							gpointer data,
+							GDestroyNotify destroy,
+							GError **err);
+
+
+struct hdp_application *hdp_application_ref(struct hdp_application *app);
+void hdp_application_unref(struct hdp_application *app);
+
+struct hdp_device *health_device_ref(struct hdp_device *hdp_dev);
+void health_device_unref(struct hdp_device *hdp_dev);
+
+
+#endif /* __HDP_UTIL_H__ */
diff --git a/bluez/repo/profiles/health/mcap.c b/bluez/repo/profiles/health/mcap.c
new file mode 100644
index 0000000..cc47a1e
--- /dev/null
+++ b/bluez/repo/profiles/health/mcap.c
@@ -0,0 +1,3182 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "bluetooth/l2cap.h"
+#include "btio/btio.h"
+#include "src/log.h"
+
+#include "mcap.h"
+
+#define MCAP_BTCLOCK_HALF (MCAP_BTCLOCK_FIELD / 2)
+#define CLK CLOCK_MONOTONIC
+
+#define MCAP_CSP_ERROR g_quark_from_static_string("mcap-csp-error-quark")
+#define MAX_RETRIES	10
+#define SAMPLE_COUNT	20
+
+#define RESPONSE_TIMER	6	/* seconds */
+#define MAX_CACHED	10	/* 10 devices */
+
+#define MCAP_ERROR g_quark_from_static_string("mcap-error-quark")
+
+#define RELEASE_TIMER(__mcl) do {		\
+	if (__mcl->tid) {			\
+		g_source_remove(__mcl->tid);	\
+		__mcl->tid = 0;			\
+	}					\
+} while(0)
+
+struct mcap_csp {
+	uint64_t	base_tmstamp;	/* CSP base timestamp */
+	struct timespec	base_time;	/* CSP base time when timestamp set */
+	guint		local_caps;	/* CSP-Master: have got remote caps */
+	guint		remote_caps;	/* CSP-Slave: remote master got caps */
+	guint		rem_req_acc;	/* CSP-Slave: accuracy required by master */
+	guint		ind_expected;	/* CSP-Master: indication expected */
+	uint8_t		csp_req;	/* CSP-Master: Request control flag */
+	guint		ind_timer;	/* CSP-Slave: indication timer */
+	guint		set_timer;	/* CSP-Slave: delayed set timer */
+	void		*set_data;	/* CSP-Slave: delayed set data */
+	void		*csp_priv_data;	/* CSP-Master: In-flight request data */
+};
+
+struct mcap_sync_cap_cbdata {
+	mcap_sync_cap_cb	cb;
+	gpointer		user_data;
+};
+
+struct mcap_sync_set_cbdata {
+	mcap_sync_set_cb	cb;
+	gpointer		user_data;
+};
+
+struct csp_caps {
+	int ts_acc;		/* timestamp accuracy */
+	int ts_res;		/* timestamp resolution */
+	int latency;		/* Read BT clock latency */
+	int preempt_thresh;	/* Preemption threshold for latency */
+	int syncleadtime_ms;	/* SyncLeadTime in ms */
+};
+
+struct sync_set_data {
+	uint8_t update;
+	uint32_t sched_btclock;
+	uint64_t timestamp;
+	int ind_freq;
+	gboolean role;
+};
+
+struct connect_mcl {
+	struct mcap_mcl		*mcl;		/* MCL for this operation */
+	mcap_mcl_connect_cb	connect_cb;	/* Connect callback */
+	GDestroyNotify		destroy;	/* Destroy callback */
+	gpointer		user_data;	/* Callback user data */
+};
+
+typedef union {
+	mcap_mdl_operation_cb		op;
+	mcap_mdl_operation_conf_cb	op_conf;
+	mcap_mdl_notify_cb		notify;
+} mcap_cb_type;
+
+struct mcap_mdl_op_cb {
+	struct mcap_mdl		*mdl;		/* MDL for this operation */
+	mcap_cb_type		cb;		/* Operation callback */
+	GDestroyNotify		destroy;	/* Destroy callback */
+	gpointer		user_data;	/* Callback user data */
+};
+
+/* MCAP finite state machine functions */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+
+static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = {
+	proc_req_connected,
+	proc_req_pending,
+	proc_req_active
+};
+
+static gboolean csp_caps_initialized = FALSE;
+struct csp_caps _caps;
+
+static void mcap_cache_mcl(struct mcap_mcl *mcl);
+
+static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	DBG("MCAP Unmanaged mdl connection");
+}
+
+static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	DBG("MCAP Unmanaged mdl closed");
+}
+
+static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	DBG("MCAP Unmanaged mdl deleted");
+}
+
+static void default_mdl_aborted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	DBG("MCAP Unmanaged mdl aborted");
+}
+
+static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl,
+						uint8_t mdepid, uint16_t mdlid,
+						uint8_t *conf, gpointer data)
+{
+	DBG("MCAP mdl remote connection aborted");
+	/* Due to this callback isn't managed this request won't be supported */
+	return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl,
+						gpointer data)
+{
+	DBG("MCAP mdl remote reconnection aborted");
+	/* Due to this callback isn't managed this request won't be supported */
+	return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static void set_default_cb(struct mcap_mcl *mcl)
+{
+	if (!mcl->cb)
+		mcl->cb = g_new0(struct mcap_mdl_cb, 1);
+
+	mcl->cb->mdl_connected = default_mdl_connected_cb;
+	mcl->cb->mdl_closed = default_mdl_closed_cb;
+	mcl->cb->mdl_deleted = default_mdl_deleted_cb;
+	mcl->cb->mdl_aborted = default_mdl_aborted_cb;
+	mcl->cb->mdl_conn_req = default_mdl_conn_req_cb;
+	mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb;
+}
+
+static char *error2str(uint8_t rc)
+{
+	switch (rc) {
+	case MCAP_SUCCESS:
+		return "Success";
+	case MCAP_INVALID_OP_CODE:
+		return "Invalid Op Code";
+	case MCAP_INVALID_PARAM_VALUE:
+		return "Invalid Parameter Value";
+	case MCAP_INVALID_MDEP:
+		return "Invalid MDEP";
+	case MCAP_MDEP_BUSY:
+		return "MDEP Busy";
+	case MCAP_INVALID_MDL:
+		return "Invalid MDL";
+	case MCAP_MDL_BUSY:
+		return "MDL Busy";
+	case MCAP_INVALID_OPERATION:
+		return "Invalid Operation";
+	case MCAP_RESOURCE_UNAVAILABLE:
+		return "Resource Unavailable";
+	case MCAP_UNSPECIFIED_ERROR:
+		return "Unspecified Error";
+	case MCAP_REQUEST_NOT_SUPPORTED:
+		return "Request Not Supported";
+	case MCAP_CONFIGURATION_REJECTED:
+		return "Configuration Rejected";
+	default:
+		return "Unknown Response Code";
+	}
+}
+
+static gboolean mcap_send_std_opcode(struct mcap_mcl *mcl, void *cmd,
+						uint32_t size, GError **err)
+{
+	if (mcl->state == MCL_IDLE) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"MCL is not connected");
+		return FALSE;
+	}
+
+	if (mcl->req != MCL_AVAILABLE) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE,
+							"Pending request");
+		return FALSE;
+	}
+
+	if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+				"Remote does not support standard opcodes");
+		return FALSE;
+	}
+
+	if (mcl->state == MCL_PENDING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION,
+			"Not Std Op. Codes can be sent in PENDING State");
+		return FALSE;
+	}
+
+	if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Command can't be sent, write error");
+		return FALSE;
+	}
+
+	mcl->lcmd = cmd;
+	mcl->req = MCL_WAITING_RSP;
+
+	return TRUE;
+}
+
+static void update_mcl_state(struct mcap_mcl *mcl)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	if (mcl->state == MCL_PENDING)
+		return;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+
+		if (mdl->state == MDL_CONNECTED) {
+			mcl->state = MCL_ACTIVE;
+			return;
+		}
+	}
+
+	mcl->state = MCL_CONNECTED;
+}
+
+static void shutdown_mdl(struct mcap_mdl *mdl)
+{
+	mdl->state = MDL_CLOSED;
+
+	if (mdl->wid) {
+		g_source_remove(mdl->wid);
+		mdl->wid = 0;
+	}
+
+	if (mdl->dc) {
+		g_io_channel_shutdown(mdl->dc, TRUE, NULL);
+		g_io_channel_unref(mdl->dc);
+		mdl->dc = NULL;
+	}
+}
+
+static void free_mdl(struct mcap_mdl *mdl)
+{
+	if (!mdl)
+		return;
+
+	mcap_mcl_unref(mdl->mcl);
+	g_free(mdl);
+}
+
+static int cmp_mdl_state(gconstpointer a, gconstpointer b)
+{
+	const struct mcap_mdl *mdl = a;
+	const MDLState *st = b;
+
+	if (mdl->state == *st)
+		return 0;
+	else if (mdl->state < *st)
+		return -1;
+	else
+		return 1;
+}
+
+static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op)
+{
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	if (op->mdl)
+		mcap_mdl_unref(op->mdl);
+
+	g_free(op);
+}
+
+static void free_mcl_priv_data(struct mcap_mcl *mcl)
+{
+	free_mcap_mdl_op(mcl->priv_data);
+	mcl->priv_data = NULL;
+}
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err)
+{
+	struct mcap_mdl_op_cb *con = mcl->priv_data;
+	struct mcap_mdl *mdl;
+	MDLState st;
+	GSList *l;
+
+	if (!con || !mcl->lcmd)
+		return;
+
+	switch (mcl->lcmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		st = MDL_WAITING;
+		l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+		mdl = l->data;
+		mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+		mcap_mdl_unref(mdl);
+		update_mcl_state(mcl);
+		con->cb.op_conf(NULL, 0, err, con->user_data);
+		break;
+	case MCAP_MD_ABORT_MDL_REQ:
+		st = MDL_WAITING;
+		l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+		shutdown_mdl(l->data);
+		update_mcl_state(mcl);
+		con->cb.notify(err, con->user_data);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		for (l = mcl->mdls; l; l = l->next) {
+			mdl = l->data;
+			if (mdl->state == MDL_DELETING)
+				mdl->state = (mdl->dc) ? MDL_CONNECTED :
+								MDL_CLOSED;
+		}
+		update_mcl_state(mcl);
+		con->cb.notify(err, con->user_data);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		st = MDL_WAITING;
+		l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+		shutdown_mdl(l->data);
+		update_mcl_state(mcl);
+		con->cb.op(NULL, err, con->user_data);
+		break;
+	}
+
+	free_mcl_priv_data(mcl);
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+}
+
+int mcap_send_data(int sock, const void *buf, uint32_t size)
+{
+	const uint8_t *buf_b = buf;
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = write(sock, buf_b + sent, size - sent);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+
+	return 0;
+}
+
+static int mcap_send_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+					uint16_t mdl, uint8_t *data, size_t len)
+{
+	mcap_rsp *cmd;
+	int sock, sent;
+
+	if (mcl->cc == NULL)
+		return -1;
+
+	sock = g_io_channel_unix_get_fd(mcl->cc);
+
+	cmd = g_malloc(sizeof(mcap_rsp) + len);
+	cmd->op = oc;
+	cmd->rc = rc;
+	cmd->mdl = htons(mdl);
+
+	if (data && len > 0)
+		memcpy(cmd->data, data, len);
+
+	sent = mcap_send_data(sock, cmd, sizeof(mcap_rsp) + len);
+	g_free(cmd);
+
+	return sent;
+}
+
+static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdlid == mdl->mdlid)
+			return mdl;
+	}
+
+	return NULL;
+}
+
+static uint16_t generate_mdlid(struct mcap_mcl *mcl)
+{
+	uint16_t mdlid = mcl->next_mdl;
+	struct mcap_mdl *mdl;
+
+	do {
+		mdl = get_mdl(mcl, mdlid);
+		if (!mdl) {
+			mcl->next_mdl = (mdlid % MCAP_MDLID_FINAL) + 1;
+			return mdlid;
+		} else
+			mdlid = (mdlid % MCAP_MDLID_FINAL) + 1;
+	} while (mdlid != mcl->next_mdl);
+
+	/* No more mdlids availables */
+	return 0;
+}
+
+static mcap_md_req *create_req(uint8_t op, uint16_t mdl_id)
+{
+	mcap_md_req *req_cmd;
+
+	req_cmd = g_new0(mcap_md_req, 1);
+
+	req_cmd->op = op;
+	req_cmd->mdl = htons(mdl_id);
+
+	return req_cmd;
+}
+
+static mcap_md_create_mdl_req *create_mdl_req(uint16_t mdl_id, uint8_t mdep,
+								uint8_t conf)
+{
+	mcap_md_create_mdl_req *req_mdl;
+
+	req_mdl = g_new0(mcap_md_create_mdl_req, 1);
+
+	req_mdl->op = MCAP_MD_CREATE_MDL_REQ;
+	req_mdl->mdl = htons(mdl_id);
+	req_mdl->mdep = mdep;
+	req_mdl->conf = conf;
+
+	return req_mdl;
+}
+
+static int compare_mdl(gconstpointer a, gconstpointer b)
+{
+	const struct mcap_mdl *mdla = a;
+	const struct mcap_mdl *mdlb = b;
+
+	if (mdla->mdlid == mdlb->mdlid)
+		return 0;
+	else if (mdla->mdlid < mdlb->mdlid)
+		return -1;
+	else
+		return 1;
+}
+
+static gboolean wait_response_timer(gpointer data)
+{
+	struct mcap_mcl *mcl = data;
+
+	GError *gerr = NULL;
+
+	RELEASE_TIMER(mcl);
+
+	g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Timeout waiting response");
+
+	mcap_notify_error(mcl, gerr);
+
+	g_error_free(gerr);
+	mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+	mcap_cache_mcl(mcl);
+
+	return FALSE;
+}
+
+gboolean mcap_create_mdl(struct mcap_mcl *mcl,
+				uint8_t mdepid,
+				uint8_t conf,
+				mcap_mdl_operation_conf_cb connect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err)
+{
+	struct mcap_mdl *mdl;
+	struct mcap_mdl_op_cb *con;
+	mcap_md_create_mdl_req *cmd;
+	uint16_t id;
+
+	id = generate_mdlid(mcl);
+	if (!id) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Not more mdlids available");
+		return FALSE;
+	}
+
+	mdl = g_new0(struct mcap_mdl, 1);
+	mdl->mcl = mcap_mcl_ref(mcl);
+	mdl->mdlid = id;
+	mdl->mdep_id = mdepid;
+	mdl->state = MDL_WAITING;
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mcap_mdl_ref(mdl);
+	con->cb.op_conf = connect_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+	cmd = create_mdl_req(id, mdepid, conf);
+	if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req),
+									err)) {
+		mcap_mdl_unref(con->mdl);
+		g_free(con);
+		g_free(cmd);
+		return FALSE;
+	}
+
+	mcl->state = MCL_ACTIVE;
+	mcl->priv_data = con;
+
+	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl),
+								compare_mdl);
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+									mcl);
+	return TRUE;
+}
+
+gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl,
+				mcap_mdl_operation_cb reconnect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err)
+{
+	struct mcap_mdl_op_cb *con;
+	struct mcap_mcl *mcl = mdl->mcl;
+	mcap_md_req *cmd;
+
+	if (mdl->state != MDL_CLOSED) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"MDL is not closed");
+		return FALSE;
+	}
+
+	cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid);
+	if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+		g_free(cmd);
+		return FALSE;
+	}
+
+	mdl->state = MDL_WAITING;
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mcap_mdl_ref(mdl);
+	con->cb.op = reconnect_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+	mcl->state = MCL_ACTIVE;
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+									mcl);
+	return TRUE;
+}
+
+static gboolean send_delete_req(struct mcap_mcl *mcl,
+						struct mcap_mdl_op_cb *con,
+						uint16_t mdlid,
+						GError **err)
+{
+	mcap_md_req *cmd;
+
+	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
+	if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+		g_free(cmd);
+		return FALSE;
+	}
+
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+									mcl);
+	return TRUE;
+}
+
+gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl,
+					mcap_mdl_notify_cb delete_cb,
+					gpointer user_data,
+					GDestroyNotify destroy,
+					GError **err)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+	struct mcap_mdl_op_cb *con;
+
+	DBG("MCL in state: %d", mcl->state);
+	if (!mcl->mdls) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				"There are not MDLs created");
+		return FALSE;
+	}
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl->state != MDL_WAITING)
+			mdl->state = MDL_DELETING;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = NULL;
+	con->cb.notify = delete_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+
+	if (!send_delete_req(mcl, con, MCAP_ALL_MDLIDS, err)) {
+		g_free(con);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb,
+							gpointer user_data,
+							GDestroyNotify destroy,
+							GError **err)
+{
+	struct mcap_mcl *mcl= mdl->mcl;
+	struct mcap_mdl_op_cb *con;
+	GSList *l;
+
+	l = g_slist_find(mcl->mdls, mdl);
+
+	if (!l) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+					"%s" , error2str(MCAP_INVALID_MDEP));
+		return FALSE;
+	}
+
+	if (mdl->state == MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Mdl is not created");
+		return FALSE;
+	}
+
+	mdl->state = MDL_DELETING;
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mcap_mdl_ref(mdl);
+	con->cb.notify = delete_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+	if (!send_delete_req(mcl, con, mdl->mdlid, err)) {
+		mcap_mdl_unref(con->mdl);
+		g_free(con);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb,
+							gpointer user_data,
+							GDestroyNotify destroy,
+							GError **err)
+{
+	struct mcap_mdl_op_cb *con;
+	struct mcap_mcl *mcl = mdl->mcl;
+	mcap_md_req *cmd;
+
+	if (mdl->state != MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Mdl in invalid state");
+		return FALSE;
+	}
+
+	cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid);
+	if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+		g_free(cmd);
+		return FALSE;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mcap_mdl_ref(mdl);
+	con->cb.notify = abort_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+	mcl->priv_data = con;
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+									mcl);
+	return TRUE;
+}
+
+static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr)
+{
+	struct mcap_mcl *mcl;
+
+	for (; list; list = list->next) {
+		mcl = list->data;
+
+		if (!bacmp(&mcl->addr, addr))
+			return mcl;
+	}
+
+	return NULL;
+}
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl)
+{
+	if (!mdl || mdl->state != MDL_CONNECTED)
+		return -ENOTCONN;
+
+	return g_io_channel_unix_get_fd(mdl->dc);
+}
+
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl)
+{
+	if (!mdl)
+		return MCAP_MDLID_RESERVED;
+
+	return mdl->mdlid;
+}
+
+static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested)
+{
+	gboolean save = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && cache_requested);
+
+	RELEASE_TIMER(mcl);
+
+	if (mcl->cc) {
+		g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+		g_io_channel_unref(mcl->cc);
+		mcl->cc = NULL;
+	}
+
+	if (mcl->wid) {
+		g_source_remove(mcl->wid);
+		mcl->wid = 0;
+	}
+
+	if (mcl->lcmd) {
+		g_free(mcl->lcmd);
+		mcl->lcmd = NULL;
+	}
+
+	if (mcl->priv_data)
+		free_mcl_priv_data(mcl);
+
+	g_slist_foreach(mcl->mdls, (GFunc) shutdown_mdl, NULL);
+
+	mcap_sync_stop(mcl);
+
+	mcl->state = MCL_IDLE;
+
+	if (save)
+		return;
+
+	g_slist_foreach(mcl->mdls, (GFunc) mcap_mdl_unref, NULL);
+	g_slist_free(mcl->mdls);
+	mcl->mdls = NULL;
+}
+
+static void mcap_mcl_shutdown(struct mcap_mcl *mcl)
+{
+	close_mcl(mcl, TRUE);
+}
+
+static void mcap_mcl_release(struct mcap_mcl *mcl)
+{
+	close_mcl(mcl, FALSE);
+}
+
+static void mcap_cache_mcl(struct mcap_mcl *mcl)
+{
+	GSList *l;
+	struct mcap_mcl *last;
+	int len;
+
+	if (mcl->ctrl & MCAP_CTRL_CACHED)
+		return;
+
+	mcl->mi->mcls = g_slist_remove(mcl->mi->mcls, mcl);
+
+	if (mcl->ctrl & MCAP_CTRL_NOCACHE) {
+		mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+		mcap_mcl_release(mcl);
+		mcap_mcl_unref(mcl);
+		return;
+	}
+
+	DBG("Caching MCL");
+
+	len = g_slist_length(mcl->mi->cached);
+	if (len == MAX_CACHED) {
+		/* Remove the latest cached mcl */
+		l = g_slist_last(mcl->mi->cached);
+		last = l->data;
+		mcl->mi->cached = g_slist_remove(mcl->mi->cached, last);
+		last->ctrl &= ~MCAP_CTRL_CACHED;
+		if (last->ctrl & MCAP_CTRL_CONN) {
+			/*
+			 * We have to release this MCL if connection is not
+			 * successful
+			 */
+			last->ctrl |= MCAP_CTRL_FREE;
+		} else {
+			mcap_mcl_release(last);
+			last->mi->mcl_uncached_cb(last, last->mi->user_data);
+		}
+		mcap_mcl_unref(last);
+	}
+
+	mcl->mi->cached = g_slist_prepend(mcl->mi->cached, mcl);
+	mcl->ctrl |= MCAP_CTRL_CACHED;
+	mcap_mcl_shutdown(mcl);
+}
+
+static void mcap_uncache_mcl(struct mcap_mcl *mcl)
+{
+	if (!(mcl->ctrl & MCAP_CTRL_CACHED))
+		return;
+
+	DBG("Got MCL from cache");
+
+	mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+	mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcl);
+	mcl->ctrl &= ~MCAP_CTRL_CACHED;
+	mcl->ctrl &= ~MCAP_CTRL_FREE;
+}
+
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache)
+{
+	if (!mcl)
+		return;
+
+	if (mcl->ctrl & MCAP_CTRL_FREE) {
+		mcap_mcl_release(mcl);
+		return;
+	}
+
+	if (!cache)
+		mcl->ctrl |= MCAP_CTRL_NOCACHE;
+
+	if (mcl->cc) {
+		g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+		g_io_channel_unref(mcl->cc);
+		mcl->cc = NULL;
+		mcl->state = MCL_IDLE;
+	} else if ((mcl->ctrl & MCAP_CTRL_CACHED) &&
+					(mcl->ctrl & MCAP_CTRL_NOCACHE)) {
+		mcl->ctrl &= ~MCAP_CTRL_CACHED;
+		mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+		mcap_mcl_release(mcl);
+		mcap_mcl_unref(mcl);
+	}
+}
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl)
+{
+	mcl->ref++;
+
+	DBG("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref);
+
+	return mcl;
+}
+
+void mcap_mcl_unref(struct mcap_mcl *mcl)
+{
+	mcl->ref--;
+
+	DBG("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref);
+
+	if (mcl->ref > 0)
+		return;
+
+	mcap_mcl_release(mcl);
+	mcap_instance_unref(mcl->mi);
+	g_free(mcl->cb);
+	g_free(mcl);
+}
+
+static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err,
+						McapMclCb cb1, va_list args)
+{
+	McapMclCb cb = cb1;
+	struct mcap_mdl_cb *c;
+
+	c = g_new0(struct mcap_mdl_cb, 1);
+
+	while (cb != MCAP_MDL_CB_INVALID) {
+		switch (cb) {
+		case MCAP_MDL_CB_CONNECTED:
+			c->mdl_connected = va_arg(args, mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_CLOSED:
+			c->mdl_closed = va_arg(args, mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_DELETED:
+			c->mdl_deleted = va_arg(args, mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_ABORTED:
+			c->mdl_aborted = va_arg(args, mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_REMOTE_CONN_REQ:
+			c->mdl_conn_req = va_arg(args,
+						mcap_remote_mdl_conn_req_cb);
+			break;
+		case MCAP_MDL_CB_REMOTE_RECONN_REQ:
+			c->mdl_reconn_req = va_arg(args,
+						mcap_remote_mdl_reconn_req_cb);
+			break;
+		case MCAP_MDL_CB_INVALID:
+		default:
+			g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+						"Unknown option %d", cb);
+			g_free(c);
+			return FALSE;
+		}
+		cb = va_arg(args, int);
+	}
+
+	/* Set new callbacks */
+	if (c->mdl_connected)
+		mdl_cb->mdl_connected = c->mdl_connected;
+	if (c->mdl_closed)
+		mdl_cb->mdl_closed = c->mdl_closed;
+	if (c->mdl_deleted)
+		mdl_cb->mdl_deleted = c->mdl_deleted;
+	if (c->mdl_aborted)
+		mdl_cb->mdl_aborted = c->mdl_aborted;
+	if (c->mdl_conn_req)
+		mdl_cb->mdl_conn_req = c->mdl_conn_req;
+	if (c->mdl_reconn_req)
+		mdl_cb->mdl_reconn_req = c->mdl_reconn_req;
+
+	g_free(c);
+
+	return TRUE;
+}
+
+gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data,
+					GError **gerr, McapMclCb cb1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, cb1);
+	ret = parse_set_opts(mcl->cb, gerr, cb1, args);
+	va_end(args);
+
+	if (!ret)
+		return FALSE;
+
+	mcl->cb->user_data = user_data;
+	return TRUE;
+}
+
+void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr)
+{
+	bacpy(addr, &mcl->addr);
+}
+
+static void mcap_del_mdl(gpointer elem, gpointer user_data)
+{
+	struct mcap_mdl *mdl = elem;
+	gboolean notify = *(gboolean *) user_data;
+
+	if (notify)
+		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
+
+	shutdown_mdl(mdl);
+	mcap_mdl_unref(mdl);
+}
+
+static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd,
+				uint32_t rlen, uint32_t explen, uint8_t rspcod)
+{
+	mcap_md_req *req;
+	uint16_t mdl_id;
+
+	if (rlen != explen) {
+		if (rlen >= sizeof(mcap_md_req)) {
+			req = cmd;
+			mdl_id = ntohs(req->mdl);
+		} else {
+			/* We can't get mdlid */
+			mdl_id = MCAP_MDLID_RESERVED;
+		}
+		mcap_send_cmd(mcl, rspcod, MCAP_INVALID_PARAM_VALUE, mdl_id,
+								NULL, 0);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd,
+								uint32_t len)
+{
+	mcap_md_create_mdl_req *req;
+	struct mcap_mdl *mdl;
+	uint16_t mdl_id;
+	uint8_t mdep_id;
+	uint8_t cfga, conf;
+	uint8_t rsp;
+
+	if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_create_mdl_req),
+							MCAP_MD_CREATE_MDL_RSP))
+		return;
+
+	req = cmd;
+	mdl_id = ntohs(req->mdl);
+	if (mdl_id < MCAP_MDLID_INITIAL || mdl_id > MCAP_MDLID_FINAL) {
+		mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL,
+							mdl_id, NULL, 0);
+		return;
+	}
+
+	mdep_id = req->mdep;
+	if (mdep_id > MCAP_MDEPID_FINAL) {
+		mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP,
+							mdl_id, NULL, 0);
+		return;
+	}
+
+	mdl = get_mdl(mcl, mdl_id);
+	if (mdl && (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING )) {
+		/*
+		 *  Creation request arrives for a MDL that is being managed
+		 * at current moment
+		 */
+		mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_MDL_BUSY,
+							mdl_id, NULL, 0);
+		return;
+	}
+
+	cfga = conf = req->conf;
+	/* Callback to upper layer */
+	rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf,
+							mcl->cb->user_data);
+	if (mcl->state == MCL_IDLE) {
+		/* MCL has been closed int the callback */
+		return;
+	}
+
+	if (cfga != 0 && cfga != conf) {
+		/*
+		 * Remote device set default configuration but upper profile
+		 * has changed it. Protocol Error: force closing the MCL by
+		 * remote device using UNSPECIFIED_ERROR response
+		 */
+		mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP,
+				MCAP_UNSPECIFIED_ERROR, mdl_id, NULL, 0);
+		return;
+	}
+	if (rsp != MCAP_SUCCESS) {
+		mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id,
+								NULL, 0);
+		return;
+	}
+
+	if (!mdl) {
+		mdl = g_new0(struct mcap_mdl, 1);
+		mdl->mcl = mcap_mcl_ref(mcl);
+		mdl->mdlid = mdl_id;
+		mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl),
+								compare_mdl);
+	} else if (mdl->state == MDL_CONNECTED) {
+		/*
+		 * MCAP specification says that we should close the MCL if
+		 * it is open when we receive a MD_CREATE_MDL_REQ
+		 */
+		shutdown_mdl(mdl);
+	}
+
+	mdl->mdep_id = mdep_id;
+	mdl->state = MDL_WAITING;
+
+	mcl->state = MCL_PENDING;
+	mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id,
+								&conf, 1);
+}
+
+static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, void *cmd,
+								uint32_t len)
+{
+	mcap_md_req *req;
+	struct mcap_mdl *mdl;
+	uint16_t mdl_id;
+	uint8_t rsp;
+
+	if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+						MCAP_MD_RECONNECT_MDL_RSP))
+		return;
+
+	req = cmd;
+	mdl_id = ntohs(req->mdl);
+
+	mdl = get_mdl(mcl, mdl_id);
+	if (!mdl) {
+		mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_INVALID_MDL,
+							mdl_id, NULL, 0);
+		return;
+	} else if (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING ) {
+		/*
+		 * Creation request arrives for a MDL that is being managed
+		 * at current moment
+		 */
+		mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_MDL_BUSY,
+							mdl_id, NULL, 0);
+		return;
+	}
+
+	/* Callback to upper layer */
+	rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data);
+	if (mcl->state == MCL_IDLE)
+		return;
+
+	if (rsp != MCAP_SUCCESS) {
+		mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, rsp, mdl_id,
+								NULL, 0);
+		return;
+	}
+
+	if (mdl->state == MDL_CONNECTED)
+		shutdown_mdl(mdl);
+
+	mdl->state = MDL_WAITING;
+	mcl->state = MCL_PENDING;
+	mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id,
+								NULL, 0);
+}
+
+static void process_md_abort_mdl_req(struct mcap_mcl *mcl, void *cmd,
+								uint32_t len)
+{
+	mcap_md_req *req;
+	GSList *l;
+	struct mcap_mdl *mdl, *abrt;
+	uint16_t mdl_id;
+
+	if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+							MCAP_MD_ABORT_MDL_RSP))
+		return;
+
+	req = cmd;
+	mdl_id = ntohs(req->mdl);
+	mcl->state = MCL_CONNECTED;
+	abrt = NULL;
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) {
+			abrt = mdl;
+			if (mcl->state != MCL_CONNECTED)
+				break;
+			continue;
+		}
+		if (mdl->state == MDL_CONNECTED && mcl->state != MCL_ACTIVE)
+			mcl->state = MCL_ACTIVE;
+
+		if (abrt && mcl->state == MCL_ACTIVE)
+			break;
+	}
+
+	if (!abrt) {
+		mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL,
+							mdl_id, NULL, 0);
+		return;
+	}
+
+	mcl->cb->mdl_aborted(abrt, mcl->cb->user_data);
+	abrt->state = MDL_CLOSED;
+	mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id,
+								NULL, 0);
+}
+
+static void process_md_delete_mdl_req(struct mcap_mcl *mcl, void *cmd,
+								uint32_t len)
+{
+	mcap_md_req *req;
+	struct mcap_mdl *mdl, *aux;
+	uint16_t mdlid;
+	gboolean notify;
+	GSList *l;
+
+	if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+							MCAP_MD_DELETE_MDL_RSP))
+		return;
+
+	req = cmd;
+	mdlid = ntohs(req->mdl);
+	if (mdlid == MCAP_ALL_MDLIDS) {
+		notify = TRUE;
+		g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
+		goto resp;
+	}
+
+	if (mdlid < MCAP_MDLID_INITIAL || mdlid > MCAP_MDLID_FINAL) {
+		mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL,
+								mdlid, NULL, 0);
+		return;
+	}
+
+	for (l = mcl->mdls, mdl = NULL; l; l = l->next) {
+		aux = l->data;
+		if (aux->mdlid == mdlid) {
+			mdl = aux;
+			break;
+		}
+	}
+
+	if (!mdl || mdl->state == MDL_WAITING) {
+		mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL,
+								mdlid, NULL, 0);
+		return;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	update_mcl_state(mcl);
+	notify = TRUE;
+	mcap_del_mdl(mdl, &notify);
+
+resp:
+	mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid,
+								NULL, 0);
+}
+
+static void invalid_req_state(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	uint16_t mdlr;
+
+	error("Invalid cmd received (op code = %d) in state %d", cmd[0],
+								mcl->state);
+	/*
+	 * Get previously mdlid sent to generate an appropriate
+	 * response if it is possible
+	 */
+	mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED :
+					ntohs(((mcap_md_req *) cmd)->mdl);
+	mcap_send_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr, NULL, 0);
+}
+
+/* Function used to process commands depending of MCL state */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	switch (cmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		process_md_create_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		process_md_reconnect_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		process_md_delete_mdl_req(mcl, cmd, len);
+		break;
+	default:
+		invalid_req_state(mcl, cmd, len);
+	}
+}
+
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	if (cmd[0] == MCAP_MD_ABORT_MDL_REQ)
+		process_md_abort_mdl_req(mcl, cmd, len);
+	else
+		invalid_req_state(mcl, cmd, len);
+}
+
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	switch (cmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		process_md_create_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		process_md_reconnect_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		process_md_delete_mdl_req(mcl, cmd, len);
+		break;
+	default:
+		invalid_req_state(mcl, cmd, len);
+	}
+}
+
+/* Function used to process replies */
+static gboolean check_err_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp,
+				uint32_t rlen, uint32_t len, GError **gerr)
+{
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	int err = MCAP_ERROR_FAILED;
+	gboolean close = FALSE;
+	char *msg;
+
+	if (rsp->op == MCAP_ERROR_RSP) {
+		msg = "MCAP_ERROR_RSP received";
+		close = FALSE;
+		goto fail;
+	}
+
+	/* Check if the response matches with the last request */
+	if (rlen < sizeof(mcap_rsp) || (mcl->lcmd[0] + 1) != rsp->op) {
+		msg = "Protocol error";
+		close = FALSE;
+		goto fail;
+	}
+
+	if (rlen < len) {
+		msg = "Protocol error";
+		close = FALSE;
+		goto fail;
+	}
+
+	if (rsp->mdl != cmdlast->mdl) {
+		msg = "MDLID received doesn't match with MDLID sent";
+		close = TRUE;
+		goto fail;
+	}
+
+	if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) {
+		msg = "Remote does not support opcodes";
+		mcl->ctrl &= ~MCAP_CTRL_STD_OP;
+		goto fail;
+	}
+
+	if (rsp->rc == MCAP_UNSPECIFIED_ERROR) {
+		msg = "Unspecified error";
+		close = TRUE;
+		goto fail;
+	}
+
+	if (rsp->rc != MCAP_SUCCESS) {
+		msg = error2str(rsp->rc);
+		err = rsp->rc;
+		goto fail;
+	}
+
+	return FALSE;
+
+fail:
+	g_set_error(gerr, MCAP_ERROR, err, "%s", msg);
+	return close;
+}
+
+static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl,
+						mcap_rsp *rsp, uint32_t len)
+{
+	mcap_md_create_mdl_req *cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd;
+	struct mcap_mdl_op_cb *conn = mcl->priv_data;
+	mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf;
+	gpointer user_data = conn->user_data;
+	struct mcap_mdl *mdl = conn->mdl;
+	uint8_t conf = cmdlast->conf;
+	gboolean close;
+	GError *gerr = NULL;
+
+	close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp) + 1, &gerr);
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	if (gerr)
+		goto fail;
+
+	/* Check if preferences changed */
+	if (conf != 0x00 && rsp->data[0] != conf) {
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+						"Configuration changed");
+		close = TRUE;
+		goto fail;
+	}
+
+	connect_cb(mdl, rsp->data[0], gerr, user_data);
+	return close;
+
+fail:
+	connect_cb(NULL, 0, gerr, user_data);
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	mcap_mdl_unref(mdl);
+	g_error_free(gerr);
+	update_mcl_state(mcl);
+	return close;
+}
+
+static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl,
+						mcap_rsp *rsp, uint32_t len)
+{
+	struct mcap_mdl_op_cb *reconn = mcl->priv_data;
+	mcap_mdl_operation_cb reconn_cb = reconn->cb.op;
+	gpointer user_data = reconn->user_data;
+	struct mcap_mdl *mdl = reconn->mdl;
+	GError *gerr = NULL;
+	gboolean close;
+
+	close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	reconn_cb(mdl, gerr, user_data);
+	if (!gerr)
+		return close;
+
+	g_error_free(gerr);
+	shutdown_mdl(mdl);
+	update_mcl_state(mcl);
+
+	if (rsp->rc != MCAP_INVALID_MDL)
+		return close;
+
+	/* Remove cached mdlid */
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+	mcap_mdl_unref(mdl);
+
+	return close;
+}
+
+static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl,
+						mcap_rsp *rsp, uint32_t len)
+{
+	struct mcap_mdl_op_cb *abrt = mcl->priv_data;
+	mcap_mdl_notify_cb abrt_cb = abrt->cb.notify;
+	gpointer user_data = abrt->user_data;
+	struct mcap_mdl *mdl = abrt->mdl;
+	GError *gerr = NULL;
+	gboolean close;
+
+	close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	abrt_cb(gerr, user_data);
+	shutdown_mdl(mdl);
+
+	if (len >= sizeof(mcap_rsp) && rsp->rc == MCAP_INVALID_MDL) {
+		mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+		mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+		mcap_mdl_unref(mdl);
+	}
+
+	if (gerr)
+		g_error_free(gerr);
+
+	update_mcl_state(mcl);
+
+	return close;
+}
+
+static void restore_mdl(gpointer elem, gpointer data)
+{
+	struct mcap_mdl *mdl = elem;
+
+	if (mdl->state == MDL_DELETING) {
+		if (mdl->dc)
+			mdl->state = MDL_CONNECTED;
+		else
+			mdl->state = MDL_CLOSED;
+	} else if (mdl->state == MDL_CLOSED)
+		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+}
+
+static void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp)
+{
+	if (rsp->rc != MCAP_ERROR_INVALID_MDL) {
+		restore_mdl(mdl, NULL);
+		return;
+	}
+
+	/* MDL does not exist in remote side, we can delete it */
+	mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl);
+	mcap_mdl_unref(mdl);
+}
+
+static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp,
+								uint32_t len)
+{
+	struct mcap_mdl_op_cb *del = mcl->priv_data;
+	struct mcap_mdl *mdl = del->mdl;
+	mcap_mdl_notify_cb deleted_cb = del->cb.notify;
+	gpointer user_data = del->user_data;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close;
+	gboolean notify = FALSE;
+
+	close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	if (gerr) {
+		if (mdl)
+			check_mdl_del_err(mdl, rsp);
+		else
+			g_slist_foreach(mcl->mdls, restore_mdl, NULL);
+		deleted_cb(gerr, user_data);
+		g_error_free(gerr);
+		return close;
+	}
+
+	if (mdlid == MCAP_ALL_MDLIDS) {
+		g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
+	} else {
+		mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+		update_mcl_state(mcl);
+		mcap_del_mdl(mdl, &notify);
+	}
+
+	deleted_cb(gerr, user_data);
+
+	return close;
+}
+
+static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op)
+{
+	if (mcl->priv_data != op) {
+		/*
+		 * Queued MCAP request in some callback.
+		 * We should not delete the mcl private data
+		 */
+		free_mcap_mdl_op(op);
+	} else {
+		/*
+		 * This is not a queued request. It's safe
+		 * delete the mcl private data here.
+		 */
+		free_mcl_priv_data(mcl);
+	}
+}
+
+static void proc_response(struct mcap_mcl *mcl, void *buf, uint32_t len)
+{
+	struct mcap_mdl_op_cb *op = mcl->priv_data;
+	mcap_rsp *rsp = buf;
+	gboolean close;
+
+	RELEASE_TIMER(mcl);
+
+	switch (mcl->lcmd[0] + 1) {
+	case MCAP_MD_CREATE_MDL_RSP:
+		close = process_md_create_mdl_rsp(mcl, rsp, len);
+		post_process_rsp(mcl, op);
+		break;
+	case MCAP_MD_RECONNECT_MDL_RSP:
+		close = process_md_reconnect_mdl_rsp(mcl, rsp, len);
+		post_process_rsp(mcl, op);
+		break;
+	case MCAP_MD_ABORT_MDL_RSP:
+		close = process_md_abort_mdl_rsp(mcl, rsp, len);
+		post_process_rsp(mcl, op);
+		break;
+	case MCAP_MD_DELETE_MDL_RSP:
+		close = process_md_delete_mdl_rsp(mcl, rsp, len);
+		post_process_rsp(mcl, op);
+		break;
+	default:
+		DBG("Unknown cmd response received (op code = %d)", rsp->op);
+		close = TRUE;
+		break;
+	}
+
+	if (close) {
+		mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+		mcap_cache_mcl(mcl);
+	}
+}
+
+static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	GError *gerr = NULL;
+
+	if (cmd[0] > MCAP_MD_SYNC_INFO_IND ||
+					(cmd[0] > MCAP_MD_DELETE_MDL_RSP &&
+					cmd[0] < MCAP_MD_SYNC_CAP_REQ)) {
+		error("Unknown cmd received (op code = %d)", cmd[0]);
+		mcap_send_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE,
+						MCAP_MDLID_RESERVED, NULL, 0);
+		return;
+	}
+
+	if (cmd[0] >= MCAP_MD_SYNC_CAP_REQ &&
+					cmd[0] <= MCAP_MD_SYNC_INFO_IND) {
+		proc_sync_cmd(mcl, cmd, len);
+		return;
+	}
+
+	if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+		/* In case the remote device doesn't work correctly */
+		error("Remote device does not support opcodes, cmd ignored");
+		return;
+	}
+
+	if (mcl->req == MCL_WAITING_RSP) {
+		if (cmd[0] & 0x01) {
+			/* Request arrived when a response is expected */
+			if (mcl->role == MCL_INITIATOR)
+				/* ignore */
+				return;
+			/* Initiator will ignore our last request */
+			RELEASE_TIMER(mcl);
+			mcl->req = MCL_AVAILABLE;
+			g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQ_IGNORED,
+				"Initiator sent a request with more priority");
+			mcap_notify_error(mcl, gerr);
+			proc_req[mcl->state](mcl, cmd, len);
+			return;
+		}
+		proc_response(mcl, cmd, len);
+	} else if (cmd[0] & 0x01)
+		proc_req[mcl->state](mcl, cmd, len);
+}
+
+static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+	struct mcap_mdl *mdl = data;
+	gboolean notify;
+
+	DBG("Close MDL %d", mdl->mdlid);
+
+	notify = (mdl->state == MDL_CONNECTED);
+	shutdown_mdl(mdl);
+
+	update_mcl_state(mdl->mcl);
+
+	if (notify) {
+		/*Callback to upper layer */
+		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+	}
+
+	return FALSE;
+}
+
+static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err,
+								gpointer data)
+{
+	struct mcap_mdl_op_cb *con = data;
+	struct mcap_mdl *mdl = con->mdl;
+	mcap_mdl_operation_cb cb = con->cb.op;
+	gpointer user_data = con->user_data;
+
+	DBG("mdl connect callback");
+
+	if (conn_err) {
+		DBG("ERROR: mdl connect callback");
+		mdl->state = MDL_CLOSED;
+		g_io_channel_unref(mdl->dc);
+		mdl->dc = NULL;
+		cb(mdl, conn_err, user_data);
+		return;
+	}
+
+	mdl->state = MDL_CONNECTED;
+	mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT,
+					G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) mdl_event_cb,
+					mcap_mdl_ref(mdl),
+					(GDestroyNotify) mcap_mdl_unref);
+
+	cb(mdl, conn_err, user_data);
+}
+
+gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode,
+					uint16_t dcpsm,
+					mcap_mdl_operation_cb connect_cb,
+					gpointer user_data,
+					GDestroyNotify destroy,
+					GError **err)
+{
+	struct mcap_mdl_op_cb *con;
+
+	if (mdl->state != MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+					"%s", error2str(MCAP_INVALID_MDL));
+		return FALSE;
+	}
+
+	if ((mode != L2CAP_MODE_ERTM) && (mode != L2CAP_MODE_STREAMING)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+						"Invalid MDL configuration");
+		return FALSE;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mcap_mdl_ref(mdl);
+	con->cb.op = connect_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+	mdl->dc = bt_io_connect(mcap_connect_mdl_cb, con,
+				(GDestroyNotify) free_mcap_mdl_op, err,
+				BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->mi->src,
+				BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr,
+				BT_IO_OPT_PSM, dcpsm,
+				BT_IO_OPT_MTU, MCAP_DC_MTU,
+				BT_IO_OPT_SEC_LEVEL, mdl->mcl->mi->sec,
+				BT_IO_OPT_MODE, mode,
+				BT_IO_OPT_INVALID);
+	if (!mdl->dc) {
+		DBG("MDL Connection error");
+		mdl->state = MDL_CLOSED;
+		mcap_mdl_unref(con->mdl);
+		g_free(con);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	GError *gerr = NULL;
+	struct mcap_mcl *mcl = data;
+	int sk, len;
+	uint8_t buf[MCAP_CC_MTU];
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+		goto fail;
+
+	sk = g_io_channel_unix_get_fd(chan);
+	len = read(sk, buf, sizeof(buf));
+	if (len < 0)
+		goto fail;
+
+	proc_cmd(mcl, buf, (uint32_t) len);
+	return TRUE;
+
+fail:
+	if (mcl->state != MCL_IDLE) {
+		if (mcl->req == MCL_WAITING_RSP) {
+			/* notify error in pending callback */
+			g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_MCL_CLOSED,
+								"MCL closed");
+			mcap_notify_error(mcl, gerr);
+			g_error_free(gerr);
+		}
+		mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+	}
+	mcap_cache_mcl(mcl);
+	return FALSE;
+}
+
+static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	char dstaddr[18];
+	struct connect_mcl *con = user_data;
+	struct mcap_mcl *aux, *mcl = con->mcl;
+	mcap_mcl_connect_cb connect_cb = con->connect_cb;
+	gpointer data = con->user_data;
+	GError *gerr = NULL;
+
+	mcl->ctrl &= ~MCAP_CTRL_CONN;
+
+	if (conn_err) {
+		if (mcl->ctrl & MCAP_CTRL_FREE) {
+			mcap_mcl_release(mcl);
+			mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data);
+		}
+		connect_cb(NULL, conn_err, data);
+		return;
+	}
+
+	ba2str(&mcl->addr, dstaddr);
+
+	aux = find_mcl(mcl->mi->mcls, &mcl->addr);
+	if (aux) {
+		/* Double MCL connection case */
+		error("MCL error: Device %s is already connected", dstaddr);
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+					"MCL %s is already connected", dstaddr);
+		connect_cb(NULL, gerr, data);
+		g_error_free(gerr);
+		return;
+	}
+
+	mcl->state = MCL_CONNECTED;
+	mcl->role = MCL_INITIATOR;
+	mcl->req = MCL_AVAILABLE;
+	mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+	mcap_sync_init(mcl);
+
+	if (mcl->ctrl & MCAP_CTRL_CACHED)
+		mcap_uncache_mcl(mcl);
+	else {
+		mcl->ctrl &= ~MCAP_CTRL_FREE;
+		mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls,
+							mcap_mcl_ref(mcl));
+	}
+
+	mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) mcl_control_cb,
+				mcap_mcl_ref(mcl),
+				(GDestroyNotify) mcap_mcl_unref);
+	connect_cb(mcl, gerr, data);
+}
+
+static void set_mdl_properties(GIOChannel *chan, struct mcap_mdl *mdl)
+{
+	struct mcap_mcl *mcl = mdl->mcl;
+
+	mdl->state = MDL_CONNECTED;
+	mdl->dc = g_io_channel_ref(chan);
+	mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT,
+					G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) mdl_event_cb,
+					mcap_mdl_ref(mdl),
+					(GDestroyNotify) mcap_mdl_unref);
+
+	mcl->state = MCL_ACTIVE;
+	mcl->cb->mdl_connected(mdl, mcl->cb->user_data);
+}
+
+static void mcl_io_destroy(gpointer data)
+{
+	struct connect_mcl *con = data;
+
+	mcap_mcl_unref(con->mcl);
+	if (con->destroy)
+		con->destroy(con->user_data);
+	g_free(con);
+}
+
+gboolean mcap_create_mcl(struct mcap_instance *mi,
+				const bdaddr_t *addr,
+				uint16_t ccpsm,
+				mcap_mcl_connect_cb connect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err)
+{
+	struct mcap_mcl *mcl;
+	struct connect_mcl *con;
+
+	mcl = find_mcl(mi->mcls, addr);
+	if (mcl) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+					"MCL is already connected.");
+		return FALSE;
+	}
+
+	mcl = find_mcl(mi->cached, addr);
+	if (!mcl) {
+		mcl = g_new0(struct mcap_mcl, 1);
+		mcl->mi = mcap_instance_ref(mi);
+		mcl->state = MCL_IDLE;
+		bacpy(&mcl->addr, addr);
+		set_default_cb(mcl);
+		mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1;
+	}
+
+	mcl->ctrl |= MCAP_CTRL_CONN;
+
+	con = g_new0(struct connect_mcl, 1);
+	con->mcl = mcap_mcl_ref(mcl);
+	con->connect_cb = connect_cb;
+	con->destroy = destroy;
+	con->user_data = user_data;
+
+	mcl->cc = bt_io_connect(mcap_connect_mcl_cb, con,
+				mcl_io_destroy, err,
+				BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+				BT_IO_OPT_DEST_BDADDR, addr,
+				BT_IO_OPT_PSM, ccpsm,
+				BT_IO_OPT_MTU, MCAP_CC_MTU,
+				BT_IO_OPT_SEC_LEVEL, mi->sec,
+				BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+				BT_IO_OPT_INVALID);
+	if (!mcl->cc) {
+		mcl->ctrl &= ~MCAP_CTRL_CONN;
+		if (mcl->ctrl & MCAP_CTRL_FREE) {
+			mcap_mcl_release(mcl);
+			mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data);
+		}
+		mcap_mcl_unref(con->mcl);
+		g_free(con);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void connect_dc_event_cb(GIOChannel *chan, GError *gerr,
+							gpointer user_data)
+{
+	struct mcap_instance *mi = user_data;
+	struct mcap_mcl *mcl;
+	struct mcap_mdl *mdl;
+	GError *err = NULL;
+	bdaddr_t dst;
+	GSList *l;
+
+	if (gerr)
+		return;
+
+	bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	mcl = find_mcl(mi->mcls, &dst);
+	if (!mcl || mcl->state != MCL_PENDING)
+		goto drop;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl->state == MDL_WAITING) {
+			set_mdl_properties(chan, mdl);
+			return;
+		}
+	}
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void set_mcl_conf(GIOChannel *chan, struct mcap_mcl *mcl)
+{
+	gboolean reconn;
+
+	mcl->state = MCL_CONNECTED;
+	mcl->role = MCL_ACCEPTOR;
+	mcl->req = MCL_AVAILABLE;
+	mcl->cc = g_io_channel_ref(chan);
+	mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+	mcap_sync_init(mcl);
+
+	reconn = (mcl->ctrl & MCAP_CTRL_CACHED);
+	if (reconn)
+		mcap_uncache_mcl(mcl);
+	else
+		mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls,
+							mcap_mcl_ref(mcl));
+
+	mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) mcl_control_cb,
+				mcap_mcl_ref(mcl),
+				(GDestroyNotify) mcap_mcl_unref);
+
+	/* Callback to report new MCL */
+	if (reconn)
+		mcl->mi->mcl_reconnected_cb(mcl, mcl->mi->user_data);
+	else
+		mcl->mi->mcl_connected_cb(mcl, mcl->mi->user_data);
+}
+
+static void connect_mcl_event_cb(GIOChannel *chan, GError *gerr,
+							gpointer user_data)
+{
+	struct mcap_instance *mi = user_data;
+	struct mcap_mcl *mcl;
+	bdaddr_t dst;
+	char address[18], srcstr[18];
+	GError *err = NULL;
+
+	if (gerr)
+		return;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	ba2str(&mi->src, srcstr);
+	mcl = find_mcl(mi->mcls, &dst);
+	if (mcl) {
+		error("Control channel already created with %s on adapter %s",
+				address, srcstr);
+		goto drop;
+	}
+
+	mcl = find_mcl(mi->cached, &dst);
+	if (!mcl) {
+		mcl = g_new0(struct mcap_mcl, 1);
+		mcl->mi = mcap_instance_ref(mi);
+		bacpy(&mcl->addr, &dst);
+		set_default_cb(mcl);
+		mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1;
+	}
+
+	set_mcl_conf(chan, mcl);
+
+	return;
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+struct mcap_instance *mcap_create_instance(const bdaddr_t *src,
+					BtIOSecLevel sec,
+					uint16_t ccpsm,
+					uint16_t dcpsm,
+					mcap_mcl_event_cb mcl_connected,
+					mcap_mcl_event_cb mcl_reconnected,
+					mcap_mcl_event_cb mcl_disconnected,
+					mcap_mcl_event_cb mcl_uncached,
+					mcap_info_ind_event_cb mcl_sync_info_ind,
+					gpointer user_data,
+					GError **gerr)
+{
+	struct mcap_instance *mi;
+
+	if (sec < BT_IO_SEC_MEDIUM) {
+		g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"Security level can't be minor of %d",
+				BT_IO_SEC_MEDIUM);
+		return NULL;
+	}
+
+	if (!(mcl_connected && mcl_reconnected &&
+			mcl_disconnected && mcl_uncached)) {
+		g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"The callbacks can't be null");
+		return NULL;
+	}
+
+	mi = g_new0(struct mcap_instance, 1);
+
+	bacpy(&mi->src, src);
+
+	mi->sec = sec;
+	mi->mcl_connected_cb = mcl_connected;
+	mi->mcl_reconnected_cb = mcl_reconnected;
+	mi->mcl_disconnected_cb = mcl_disconnected;
+	mi->mcl_uncached_cb = mcl_uncached;
+	mi->mcl_sync_infoind_cb = mcl_sync_info_ind;
+	mi->user_data = user_data;
+	mi->csp_enabled = FALSE;
+
+	/* Listen incoming connections in control channel */
+	mi->ccio = bt_io_listen(connect_mcl_event_cb, NULL, mi,
+				NULL, gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+				BT_IO_OPT_PSM, ccpsm,
+				BT_IO_OPT_MTU, MCAP_CC_MTU,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+				BT_IO_OPT_INVALID);
+	if (!mi->ccio) {
+		error("%s", (*gerr)->message);
+		g_free(mi);
+		return NULL;
+	}
+
+	/* Listen incoming connections in data channels */
+	mi->dcio = bt_io_listen(connect_dc_event_cb, NULL, mi,
+				NULL, gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+				BT_IO_OPT_PSM, dcpsm,
+				BT_IO_OPT_MTU, MCAP_DC_MTU,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+	if (!mi->dcio) {
+		g_io_channel_shutdown(mi->ccio, TRUE, NULL);
+		g_io_channel_unref(mi->ccio);
+		mi->ccio = NULL;
+		error("%s", (*gerr)->message);
+		g_free(mi);
+		return NULL;
+	}
+
+	/* Initialize random seed to generate mdlids for this instance */
+	srand(time(NULL));
+
+	return mcap_instance_ref(mi);
+}
+
+void mcap_release_instance(struct mcap_instance *mi)
+{
+	GSList *l;
+
+	if (!mi)
+		return;
+
+	if (mi->ccio) {
+		g_io_channel_shutdown(mi->ccio, TRUE, NULL);
+		g_io_channel_unref(mi->ccio);
+		mi->ccio = NULL;
+	}
+
+	if (mi->dcio) {
+		g_io_channel_shutdown(mi->dcio, TRUE, NULL);
+		g_io_channel_unref(mi->dcio);
+		mi->dcio = NULL;
+	}
+
+	for (l = mi->mcls; l; l = l->next) {
+		mcap_mcl_release(l->data);
+		mcap_mcl_unref(l->data);
+	}
+
+	g_slist_free(mi->mcls);
+	mi->mcls = NULL;
+
+	for (l = mi->cached; l; l = l->next) {
+		mcap_mcl_release(l->data);
+		mcap_mcl_unref(l->data);
+	}
+
+	g_slist_free(mi->cached);
+	mi->cached = NULL;
+}
+
+struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi)
+{
+	mi->ref++;
+
+	DBG("mcap_instance_ref(%p): ref=%d", mi, mi->ref);
+
+	return mi;
+}
+
+void mcap_instance_unref(struct mcap_instance *mi)
+{
+	mi->ref--;
+
+	DBG("mcap_instance_unref(%p): ref=%d", mi, mi->ref);
+
+	if (mi->ref > 0)
+		return;
+
+	mcap_release_instance(mi);
+	g_free(mi);
+}
+
+uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err)
+{
+	uint16_t lpsm;
+
+	if (!(mi && mi->ccio)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+			"Invalid MCAP instance");
+		return 0;
+	}
+
+	if (!bt_io_get(mi->ccio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID))
+		return 0;
+
+	return lpsm;
+}
+
+uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err)
+{
+	uint16_t lpsm;
+
+	if (!(mi && mi->dcio)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+			"Invalid MCAP instance");
+		return 0;
+	}
+
+	if (!bt_io_get(mi->dcio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID))
+		return 0;
+
+	return lpsm;
+}
+
+gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode,
+								GError **err)
+{
+	if (!(mi && mi->dcio)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+						"Invalid MCAP instance");
+		return FALSE;
+	}
+
+	return bt_io_set(mi->dcio, err, BT_IO_OPT_MODE, mode,
+							BT_IO_OPT_INVALID);
+}
+
+struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl)
+{
+	mdl->ref++;
+
+	DBG("mcap_mdl_ref(%p): ref=%d", mdl, mdl->ref);
+
+	return mdl;
+}
+
+void mcap_mdl_unref(struct mcap_mdl *mdl)
+{
+	mdl->ref--;
+
+	DBG("mcap_mdl_unref(%p): ref=%d", mdl, mdl->ref);
+
+	if (mdl->ref > 0)
+		return;
+
+	free_mdl(mdl);
+}
+
+
+static int send_sync_cmd(struct mcap_mcl *mcl, const void *buf, uint32_t size)
+{
+	int sock;
+
+	if (mcl->cc == NULL)
+		return -1;
+
+	sock = g_io_channel_unix_get_fd(mcl->cc);
+	return mcap_send_data(sock, buf, size);
+}
+
+static int send_unsupported_cap_req(struct mcap_mcl *mcl)
+{
+	mcap_md_sync_cap_rsp *cmd;
+	int sent;
+
+	cmd = g_new0(mcap_md_sync_cap_rsp, 1);
+	cmd->op = MCAP_MD_SYNC_CAP_RSP;
+	cmd->rc = MCAP_REQUEST_NOT_SUPPORTED;
+
+	sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+	g_free(cmd);
+
+	return sent;
+}
+
+static int send_unsupported_set_req(struct mcap_mcl *mcl)
+{
+	mcap_md_sync_set_rsp *cmd;
+	int sent;
+
+	cmd = g_new0(mcap_md_sync_set_rsp, 1);
+	cmd->op = MCAP_MD_SYNC_SET_RSP;
+	cmd->rc = MCAP_REQUEST_NOT_SUPPORTED;
+
+	sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+	g_free(cmd);
+
+	return sent;
+}
+
+static void reset_tmstamp(struct mcap_csp *csp, struct timespec *base_time,
+				uint64_t new_tmstamp)
+{
+	csp->base_tmstamp = new_tmstamp;
+	if (base_time)
+		csp->base_time = *base_time;
+	else
+		clock_gettime(CLK, &csp->base_time);
+}
+
+void mcap_sync_init(struct mcap_mcl *mcl)
+{
+	if (!mcl->mi->csp_enabled) {
+		mcl->csp = NULL;
+		return;
+	}
+
+	mcl->csp = g_new0(struct mcap_csp, 1);
+
+	mcl->csp->rem_req_acc = 10000; /* safe divisor */
+	mcl->csp->set_data = NULL;
+	mcl->csp->csp_priv_data = NULL;
+
+	reset_tmstamp(mcl->csp, NULL, 0);
+}
+
+void mcap_sync_stop(struct mcap_mcl *mcl)
+{
+	if (!mcl->csp)
+		return;
+
+	if (mcl->csp->ind_timer)
+		g_source_remove(mcl->csp->ind_timer);
+
+	if (mcl->csp->set_timer)
+		g_source_remove(mcl->csp->set_timer);
+
+	if (mcl->csp->set_data)
+		g_free(mcl->csp->set_data);
+
+	if (mcl->csp->csp_priv_data)
+		g_free(mcl->csp->csp_priv_data);
+
+	mcl->csp->ind_timer = 0;
+	mcl->csp->set_timer = 0;
+	mcl->csp->set_data = NULL;
+	mcl->csp->csp_priv_data = NULL;
+
+	g_free(mcl->csp);
+	mcl->csp = NULL;
+}
+
+static uint64_t time_us(struct timespec *tv)
+{
+	return tv->tv_sec * 1000000ll + tv->tv_nsec / 1000ll;
+}
+
+static int64_t bt2us(int bt)
+{
+	return bt * 312.5;
+}
+
+static int bt2ms(int bt)
+{
+	return bt * 312.5 / 1000;
+}
+
+static int btoffset(uint32_t btclk1, uint32_t btclk2)
+{
+	int offset = btclk2 - btclk1;
+
+	if (offset <= -MCAP_BTCLOCK_HALF)
+		offset += MCAP_BTCLOCK_FIELD;
+	else if (offset > MCAP_BTCLOCK_HALF)
+		offset -= MCAP_BTCLOCK_FIELD;
+
+	return offset;
+}
+
+static int btdiff(uint32_t btclk1, uint32_t btclk2)
+{
+	return btoffset(btclk1, btclk2);
+}
+
+static gboolean valid_btclock(uint32_t btclk)
+{
+	return btclk <= MCAP_BTCLOCK_MAX;
+}
+
+/* This call may fail; either deal with retry or use read_btclock_retry */
+static gboolean read_btclock(struct mcap_mcl *mcl, uint32_t *btclock,
+							uint16_t *btaccuracy)
+{
+	/*
+	 * FIXME: btd_adapter_read_clock(...) always return FALSE, current
+	 * code doesn't support CSP (Clock Synchronization Protocol). To avoid
+	 * build dependancy on struct 'btd_adapter', removing this code.
+	 */
+
+	return FALSE;
+}
+
+static gboolean read_btclock_retry(struct mcap_mcl *mcl, uint32_t *btclock,
+							uint16_t *btaccuracy)
+{
+	int retries = 5;
+
+	while (--retries >= 0) {
+		if (read_btclock(mcl, btclock, btaccuracy))
+			return TRUE;
+		DBG("CSP: retrying to read bt clock...");
+	}
+
+	return FALSE;
+}
+
+static gboolean get_btrole(struct mcap_mcl *mcl)
+{
+	int sock, flags;
+	socklen_t len;
+
+	if (mcl->cc == NULL)
+		return -1;
+
+	sock = g_io_channel_unix_get_fd(mcl->cc);
+	len = sizeof(flags);
+
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len))
+		DBG("CSP: could not read role");
+
+	return flags & L2CAP_LM_MASTER;
+}
+
+uint64_t mcap_get_timestamp(struct mcap_mcl *mcl,
+				struct timespec *given_time)
+{
+	struct timespec now;
+	uint64_t tmstamp;
+
+	if (!mcl->csp)
+		return MCAP_TMSTAMP_DONTSET;
+
+	if (given_time)
+		now = *given_time;
+	else
+		if (clock_gettime(CLK, &now) < 0)
+			return MCAP_TMSTAMP_DONTSET;
+
+	tmstamp = time_us(&now) - time_us(&mcl->csp->base_time)
+		+ mcl->csp->base_tmstamp;
+
+	return tmstamp;
+}
+
+uint32_t mcap_get_btclock(struct mcap_mcl *mcl)
+{
+	uint32_t btclock;
+	uint16_t accuracy;
+
+	if (!mcl->csp)
+		return MCAP_BTCLOCK_IMMEDIATE;
+
+	if (!read_btclock_retry(mcl, &btclock, &accuracy))
+		btclock = 0xffffffff;
+
+	return btclock;
+}
+
+static gboolean initialize_caps(struct mcap_mcl *mcl)
+{
+	struct timespec t1, t2;
+	int latencies[SAMPLE_COUNT];
+	int latency, avg, dev;
+	uint32_t btclock;
+	uint16_t btaccuracy;
+	int i;
+	int retries;
+
+	clock_getres(CLK, &t1);
+
+	_caps.ts_res = time_us(&t1);
+	if (_caps.ts_res < 1)
+		_caps.ts_res = 1;
+
+	_caps.ts_acc = 20; /* ppm, estimated */
+
+	/* A little exercise before measuing latency */
+	clock_gettime(CLK, &t1);
+	read_btclock_retry(mcl, &btclock, &btaccuracy);
+
+	/* Read clock a number of times and measure latency */
+	avg = 0;
+	i = 0;
+	retries = MAX_RETRIES;
+	while (i < SAMPLE_COUNT && retries > 0) {
+		clock_gettime(CLK, &t1);
+		if (!read_btclock(mcl, &btclock, &btaccuracy)) {
+			retries--;
+			continue;
+		}
+		clock_gettime(CLK, &t2);
+
+		latency = time_us(&t2) - time_us(&t1);
+		latencies[i] = latency;
+		avg += latency;
+		i++;
+	}
+
+	if (retries <= 0)
+		return FALSE;
+
+	/* Calculate average and deviation */
+	avg /= SAMPLE_COUNT;
+	dev = 0;
+	for (i = 0; i < SAMPLE_COUNT; ++i)
+		dev += abs(latencies[i] - avg);
+	dev /= SAMPLE_COUNT;
+
+	/* Calculate corrected average, without 'freak' latencies */
+	latency = 0;
+	for (i = 0; i < SAMPLE_COUNT; ++i) {
+		if (latencies[i] > (avg + dev * 6))
+			latency += avg;
+		else
+			latency += latencies[i];
+	}
+	latency /= SAMPLE_COUNT;
+
+	_caps.latency = latency;
+	_caps.preempt_thresh = latency * 4;
+	_caps.syncleadtime_ms = latency * 50 / 1000;
+
+	csp_caps_initialized = TRUE;
+	return TRUE;
+}
+
+static struct csp_caps *caps(struct mcap_mcl *mcl)
+{
+	if (!csp_caps_initialized)
+		if (!initialize_caps(mcl)) {
+			/* Temporary failure in reading BT clock */
+			return NULL;
+		}
+
+	return &_caps;
+}
+
+static int send_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t rspcode,
+			uint8_t btclockres, uint16_t synclead,
+			uint16_t tmstampres, uint16_t tmstampacc)
+{
+	mcap_md_sync_cap_rsp *rsp;
+	int sent;
+
+	rsp = g_new0(mcap_md_sync_cap_rsp, 1);
+
+	rsp->op = MCAP_MD_SYNC_CAP_RSP;
+	rsp->rc = rspcode;
+
+	rsp->btclock = btclockres;
+	rsp->sltime = htons(synclead);
+	rsp->timestnr = htons(tmstampres);
+	rsp->timestna = htons(tmstampacc);
+
+	sent = send_sync_cmd(mcl, rsp, sizeof(*rsp));
+	g_free(rsp);
+
+	return sent;
+}
+
+static void proc_sync_cap_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap_md_sync_cap_req *req;
+	uint16_t required_accuracy;
+	uint16_t our_accuracy;
+	uint32_t btclock;
+	uint16_t btres;
+
+	if (len != sizeof(mcap_md_sync_cap_req)) {
+		send_sync_cap_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+					0, 0, 0, 0);
+		return;
+	}
+
+	if (!caps(mcl)) {
+		send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+					0, 0, 0, 0);
+		return;
+	}
+
+	req = (mcap_md_sync_cap_req *) cmd;
+	required_accuracy = ntohs(req->timest);
+	our_accuracy = caps(mcl)->ts_acc;
+	btres = 0;
+
+	if (required_accuracy < our_accuracy || required_accuracy < 1) {
+		send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+					0, 0, 0, 0);
+		return;
+	}
+
+	if (!read_btclock_retry(mcl, &btclock, &btres)) {
+		send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+					0, 0, 0, 0);
+		return;
+	}
+
+	mcl->csp->remote_caps = 1;
+	mcl->csp->rem_req_acc = required_accuracy;
+
+	send_sync_cap_rsp(mcl, MCAP_SUCCESS, btres,
+				caps(mcl)->syncleadtime_ms,
+				caps(mcl)->ts_res, our_accuracy);
+}
+
+static int send_sync_set_rsp(struct mcap_mcl *mcl, uint8_t rspcode,
+			uint32_t btclock, uint64_t timestamp,
+			uint16_t tmstampres)
+{
+	mcap_md_sync_set_rsp *rsp;
+	int sent;
+
+	rsp = g_new0(mcap_md_sync_set_rsp, 1);
+
+	rsp->op = MCAP_MD_SYNC_SET_RSP;
+	rsp->rc = rspcode;
+	rsp->btclock = htonl(btclock);
+	rsp->timestst = hton64(timestamp);
+	rsp->timestsa = htons(tmstampres);
+
+	sent = send_sync_cmd(mcl, rsp, sizeof(*rsp));
+	g_free(rsp);
+
+	return sent;
+}
+
+static gboolean get_all_clocks(struct mcap_mcl *mcl, uint32_t *btclock,
+				struct timespec *base_time,
+				uint64_t *timestamp)
+{
+	int latency;
+	int retry = 5;
+	uint16_t btres;
+	struct timespec t0;
+
+	if (!caps(mcl))
+		return FALSE;
+
+	latency = caps(mcl)->preempt_thresh + 1;
+
+	while (latency > caps(mcl)->preempt_thresh && --retry >= 0) {
+
+		if (clock_gettime(CLK, &t0) < 0)
+			return FALSE;
+
+		if (!read_btclock(mcl, btclock, &btres))
+			continue;
+
+		if (clock_gettime(CLK, base_time) < 0)
+			return FALSE;
+
+		/*
+		 * Tries to detect preemption between clock_gettime
+		 * and read_btclock by measuring transaction time
+		 */
+		latency = time_us(base_time) - time_us(&t0);
+	}
+
+	if (retry < 0)
+		return FALSE;
+
+	*timestamp = mcap_get_timestamp(mcl, base_time);
+
+	return TRUE;
+}
+
+static gboolean sync_send_indication(gpointer user_data)
+{
+	struct mcap_mcl *mcl;
+	mcap_md_sync_info_ind *cmd;
+	uint32_t btclock;
+	uint64_t tmstamp;
+	struct timespec base_time;
+	int sent;
+
+	if (!user_data)
+		return FALSE;
+
+	btclock = 0;
+	mcl = user_data;
+
+	if (!caps(mcl))
+		return FALSE;
+
+	if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp))
+		return FALSE;
+
+	cmd = g_new0(mcap_md_sync_info_ind, 1);
+
+	cmd->op = MCAP_MD_SYNC_INFO_IND;
+	cmd->btclock = htonl(btclock);
+	cmd->timestst = hton64(tmstamp);
+	cmd->timestsa = htons(caps(mcl)->latency);
+
+	sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+	g_free(cmd);
+
+	return !sent;
+}
+
+static gboolean proc_sync_set_req_phase2(gpointer user_data)
+{
+	struct mcap_mcl *mcl;
+	struct sync_set_data *data;
+	uint8_t update;
+	uint32_t sched_btclock;
+	uint64_t new_tmstamp;
+	int ind_freq;
+	int role;
+	uint32_t btclock;
+	uint64_t tmstamp;
+	struct timespec base_time;
+	uint16_t tmstampacc;
+	gboolean reset;
+	int delay;
+
+	if (!user_data)
+		return FALSE;
+
+	mcl = user_data;
+
+	if (!mcl->csp->set_data)
+		return FALSE;
+
+	btclock = 0;
+	data = mcl->csp->set_data;
+	update = data->update;
+	sched_btclock = data->sched_btclock;
+	new_tmstamp = data->timestamp;
+	ind_freq = data->ind_freq;
+	role = data->role;
+
+	if (!caps(mcl)) {
+		send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+		return FALSE;
+	}
+
+	if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) {
+		send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+		return FALSE;
+	}
+
+	if (get_btrole(mcl) != role) {
+		send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0);
+		return FALSE;
+	}
+
+	reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET);
+
+	if (reset) {
+		if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE) {
+			delay = bt2us(btdiff(sched_btclock, btclock));
+			if (delay >= 0 || ((new_tmstamp - delay) > 0)) {
+				new_tmstamp += delay;
+				DBG("CSP: reset w/ delay %dus, compensated",
+									delay);
+			} else
+				DBG("CSP: reset w/ delay %dus, uncompensated",
+									delay);
+		}
+
+		reset_tmstamp(mcl->csp, &base_time, new_tmstamp);
+		tmstamp = new_tmstamp;
+	}
+
+	tmstampacc = caps(mcl)->latency + caps(mcl)->ts_acc;
+
+	if (mcl->csp->ind_timer) {
+		g_source_remove(mcl->csp->ind_timer);
+		mcl->csp->ind_timer = 0;
+	}
+
+	if (update) {
+		int when = ind_freq + caps(mcl)->syncleadtime_ms;
+		mcl->csp->ind_timer = g_timeout_add(when,
+						sync_send_indication,
+						mcl);
+	}
+
+	send_sync_set_rsp(mcl, MCAP_SUCCESS, btclock, tmstamp, tmstampacc);
+
+	/* First indication after set is immediate */
+	if (update)
+		sync_send_indication(mcl);
+
+	return FALSE;
+}
+
+static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap_md_sync_set_req *req;
+	uint32_t sched_btclock, cur_btclock;
+	uint16_t btres;
+	uint8_t update;
+	uint64_t timestamp;
+	struct sync_set_data *set_data;
+	int phase2_delay, ind_freq, when;
+
+	if (len != sizeof(mcap_md_sync_set_req)) {
+		send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+		return;
+	}
+
+	req = (mcap_md_sync_set_req *) cmd;
+	sched_btclock = ntohl(req->btclock);
+	update = req->timestui;
+	timestamp = ntoh64(req->timestst);
+	cur_btclock = 0;
+
+	if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE &&
+			!valid_btclock(sched_btclock)) {
+		send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+		return;
+	}
+
+	if (update > 1) {
+		send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+		return;
+	}
+
+	if (!mcl->csp->remote_caps) {
+		/* Remote side did not ask our capabilities yet */
+		send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+		return;
+	}
+
+	if (!caps(mcl)) {
+		send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+		return;
+	}
+
+	if (!read_btclock_retry(mcl, &cur_btclock, &btres)) {
+		send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+		return;
+	}
+
+	if (sched_btclock == MCAP_BTCLOCK_IMMEDIATE)
+		phase2_delay = 0;
+	else {
+		phase2_delay = btdiff(cur_btclock, sched_btclock);
+
+		if (phase2_delay < 0) {
+			/* can not reset in the past tense */
+			send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+						0, 0, 0);
+			return;
+		}
+
+		/* Convert to miliseconds */
+		phase2_delay = bt2ms(phase2_delay);
+
+		if (phase2_delay > 61*1000) {
+			/* More than 60 seconds in the future */
+			send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+						0, 0, 0);
+			return;
+		} else if (phase2_delay < caps(mcl)->latency / 1000) {
+			/* Too fast for us to do in time */
+			send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+						0, 0, 0);
+			return;
+		}
+	}
+
+	if (update) {
+		/*
+		 * Indication frequency: required accuracy divided by ours
+		 * Converted to milisseconds
+		 */
+		ind_freq = (1000 * mcl->csp->rem_req_acc) / caps(mcl)->ts_acc;
+
+		if (ind_freq < MAX(caps(mcl)->latency * 2 / 1000, 100)) {
+			/* Too frequent, we can't handle */
+			send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+						0, 0, 0);
+			return;
+		}
+
+		DBG("CSP: indication every %dms", ind_freq);
+	} else
+		ind_freq = 0;
+
+	if (mcl->csp->ind_timer) {
+		/* Old indications are no longer sent */
+		g_source_remove(mcl->csp->ind_timer);
+		mcl->csp->ind_timer = 0;
+	}
+
+	if (!mcl->csp->set_data)
+		mcl->csp->set_data = g_new0(struct sync_set_data, 1);
+
+	set_data = (struct sync_set_data *) mcl->csp->set_data;
+
+	set_data->update = update;
+	set_data->sched_btclock = sched_btclock;
+	set_data->timestamp = timestamp;
+	set_data->ind_freq = ind_freq;
+	set_data->role = get_btrole(mcl);
+
+	/*
+	 * TODO is there some way to schedule a call based directly on
+	 * a BT clock value, instead of this estimation that uses
+	 * the SO clock?
+	 */
+
+	if (phase2_delay > 0) {
+		when = phase2_delay + caps(mcl)->syncleadtime_ms;
+		mcl->csp->set_timer = g_timeout_add(when,
+						proc_sync_set_req_phase2,
+						mcl);
+	} else
+		proc_sync_set_req_phase2(mcl);
+
+	/* First indication is immediate */
+	if (update)
+		sync_send_indication(mcl);
+}
+
+static void proc_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap_md_sync_cap_rsp *rsp;
+	uint8_t mcap_err;
+	uint8_t btclockres;
+	uint16_t synclead;
+	uint16_t tmstampres;
+	uint16_t tmstampacc;
+	struct mcap_sync_cap_cbdata *cbdata;
+	mcap_sync_cap_cb cb;
+	gpointer user_data;
+
+	if (mcl->csp->csp_req != MCAP_MD_SYNC_CAP_REQ) {
+		DBG("CSP: got unexpected cap respose");
+		return;
+	}
+
+	if (!mcl->csp->csp_priv_data) {
+		DBG("CSP: no priv data for cap respose");
+		return;
+	}
+
+	cbdata = mcl->csp->csp_priv_data;
+	cb = cbdata->cb;
+	user_data = cbdata->user_data;
+	g_free(cbdata);
+
+	mcl->csp->csp_priv_data = NULL;
+	mcl->csp->csp_req = 0;
+
+	if (len != sizeof(mcap_md_sync_cap_rsp)) {
+		DBG("CSP: got corrupted cap respose");
+		return;
+	}
+
+	rsp = (mcap_md_sync_cap_rsp *) cmd;
+	mcap_err = rsp->rc;
+	btclockres = rsp->btclock;
+	synclead = ntohs(rsp->sltime);
+	tmstampres = ntohs(rsp->timestnr);
+	tmstampacc = ntohs(rsp->timestna);
+
+	if (!mcap_err)
+		mcl->csp->local_caps = TRUE;
+
+	cb(mcl, mcap_err, btclockres, synclead, tmstampres, tmstampacc, NULL,
+								user_data);
+}
+
+static void proc_sync_set_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap_md_sync_set_rsp *rsp;
+	uint8_t mcap_err;
+	uint32_t btclock;
+	uint64_t timestamp;
+	uint16_t accuracy;
+	struct mcap_sync_set_cbdata *cbdata;
+	mcap_sync_set_cb cb;
+	gpointer user_data;
+
+	if (mcl->csp->csp_req != MCAP_MD_SYNC_SET_REQ) {
+		DBG("CSP: got unexpected set respose");
+		return;
+	}
+
+	if (!mcl->csp->csp_priv_data) {
+		DBG("CSP: no priv data for set respose");
+		return;
+	}
+
+	cbdata = mcl->csp->csp_priv_data;
+	cb = cbdata->cb;
+	user_data = cbdata->user_data;
+	g_free(cbdata);
+
+	mcl->csp->csp_priv_data = NULL;
+	mcl->csp->csp_req = 0;
+
+	if (len != sizeof(mcap_md_sync_set_rsp)) {
+		DBG("CSP: got corrupted set respose");
+		return;
+	}
+
+	rsp = (mcap_md_sync_set_rsp *) cmd;
+	mcap_err = rsp->rc;
+	btclock = ntohl(rsp->btclock);
+	timestamp = ntoh64(rsp->timestst);
+	accuracy = ntohs(rsp->timestsa);
+
+	if (!mcap_err && !valid_btclock(btclock))
+		mcap_err = MCAP_ERROR_INVALID_ARGS;
+
+	cb(mcl, mcap_err, btclock, timestamp, accuracy, NULL, user_data);
+}
+
+static void proc_sync_info_ind(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap_md_sync_info_ind *req;
+	struct sync_info_ind_data data;
+	uint32_t btclock;
+
+	if (!mcl->csp->ind_expected) {
+		DBG("CSP: received unexpected info indication");
+		return;
+	}
+
+	if (len != sizeof(mcap_md_sync_info_ind))
+		return;
+
+	req = (mcap_md_sync_info_ind *) cmd;
+
+	btclock = ntohl(req->btclock);
+
+	if (!valid_btclock(btclock))
+		return;
+
+	data.btclock = btclock;
+	data.timestamp = ntoh64(req->timestst);
+	data.accuracy = ntohs(req->timestsa);
+
+	if (mcl->mi->mcl_sync_infoind_cb)
+		mcl->mi->mcl_sync_infoind_cb(mcl, &data);
+}
+
+void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	if (!mcl->mi->csp_enabled || !mcl->csp) {
+		switch (cmd[0]) {
+		case MCAP_MD_SYNC_CAP_REQ:
+			send_unsupported_cap_req(mcl);
+			break;
+		case MCAP_MD_SYNC_SET_REQ:
+			send_unsupported_set_req(mcl);
+			break;
+		}
+		return;
+	}
+
+	switch (cmd[0]) {
+	case MCAP_MD_SYNC_CAP_REQ:
+		proc_sync_cap_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_SYNC_CAP_RSP:
+		proc_sync_cap_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_SYNC_SET_REQ:
+		proc_sync_set_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_SYNC_SET_RSP:
+		proc_sync_set_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_SYNC_INFO_IND:
+		proc_sync_info_ind(mcl, cmd, len);
+		break;
+	}
+}
+
+void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc,
+			mcap_sync_cap_cb cb, gpointer user_data,
+			GError **err)
+{
+	struct mcap_sync_cap_cbdata *cbdata;
+	mcap_md_sync_cap_req *cmd;
+
+	if (!mcl->mi->csp_enabled || !mcl->csp) {
+		g_set_error(err,
+			MCAP_CSP_ERROR,
+			MCAP_ERROR_RESOURCE_UNAVAILABLE,
+			"CSP not enabled for the instance");
+		return;
+	}
+
+	if (mcl->csp->csp_req) {
+		g_set_error(err,
+			MCAP_CSP_ERROR,
+			MCAP_ERROR_RESOURCE_UNAVAILABLE,
+			"Pending CSP request");
+		return;
+	}
+
+	mcl->csp->csp_req = MCAP_MD_SYNC_CAP_REQ;
+	cmd = g_new0(mcap_md_sync_cap_req, 1);
+
+	cmd->op = MCAP_MD_SYNC_CAP_REQ;
+	cmd->timest = htons(reqacc);
+
+	cbdata = g_new0(struct mcap_sync_cap_cbdata, 1);
+	cbdata->cb = cb;
+	cbdata->user_data = user_data;
+	mcl->csp->csp_priv_data = cbdata;
+
+	send_sync_cmd(mcl, cmd, sizeof(*cmd));
+
+	g_free(cmd);
+}
+
+void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock,
+			uint64_t timestamp, mcap_sync_set_cb cb,
+			gpointer user_data, GError **err)
+{
+	mcap_md_sync_set_req *cmd;
+	struct mcap_sync_set_cbdata *cbdata;
+
+	if (!mcl->mi->csp_enabled || !mcl->csp) {
+		g_set_error(err,
+			MCAP_CSP_ERROR,
+			MCAP_ERROR_RESOURCE_UNAVAILABLE,
+			"CSP not enabled for the instance");
+		return;
+	}
+
+	if (!mcl->csp->local_caps) {
+		g_set_error(err,
+			MCAP_CSP_ERROR,
+			MCAP_ERROR_RESOURCE_UNAVAILABLE,
+			"Did not get CSP caps from slave yet");
+		return;
+	}
+
+	if (mcl->csp->csp_req) {
+		g_set_error(err,
+			MCAP_CSP_ERROR,
+			MCAP_ERROR_RESOURCE_UNAVAILABLE,
+			"Pending CSP request");
+		return;
+	}
+
+	mcl->csp->csp_req = MCAP_MD_SYNC_SET_REQ;
+	cmd = g_new0(mcap_md_sync_set_req, 1);
+
+	cmd->op = MCAP_MD_SYNC_SET_REQ;
+	cmd->timestui = update;
+	cmd->btclock = htonl(btclock);
+	cmd->timestst = hton64(timestamp);
+
+	mcl->csp->ind_expected = update;
+
+	cbdata = g_new0(struct mcap_sync_set_cbdata, 1);
+	cbdata->cb = cb;
+	cbdata->user_data = user_data;
+	mcl->csp->csp_priv_data = cbdata;
+
+	send_sync_cmd(mcl, cmd, sizeof(*cmd));
+
+	g_free(cmd);
+}
+
+void mcap_enable_csp(struct mcap_instance *mi)
+{
+	mi->csp_enabled = TRUE;
+}
+
+void mcap_disable_csp(struct mcap_instance *mi)
+{
+	mi->csp_enabled = FALSE;
+}
diff --git a/bluez/repo/profiles/health/mcap.h b/bluez/repo/profiles/health/mcap.h
new file mode 100644
index 0000000..69873ca
--- /dev/null
+++ b/bluez/repo/profiles/health/mcap.h
@@ -0,0 +1,437 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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
+ *
+ */
+
+#define MCAP_VERSION	0x0100	/* current version 01.00 */
+
+/* bytes to get MCAP Supported Procedures */
+#define MCAP_SUP_PROC	0x06
+
+/* maximum transmission unit for channels */
+#define MCAP_CC_MTU	48
+#define MCAP_DC_MTU	65535
+
+/* MCAP Standard Op Codes */
+#define MCAP_ERROR_RSP			0x00
+#define MCAP_MD_CREATE_MDL_REQ		0x01
+#define MCAP_MD_CREATE_MDL_RSP		0x02
+#define MCAP_MD_RECONNECT_MDL_REQ	0x03
+#define MCAP_MD_RECONNECT_MDL_RSP	0x04
+#define MCAP_MD_ABORT_MDL_REQ		0x05
+#define MCAP_MD_ABORT_MDL_RSP		0x06
+#define MCAP_MD_DELETE_MDL_REQ		0x07
+#define MCAP_MD_DELETE_MDL_RSP		0x08
+
+/* MCAP Clock Sync Op Codes */
+#define MCAP_MD_SYNC_CAP_REQ		0x11
+#define MCAP_MD_SYNC_CAP_RSP		0x12
+#define MCAP_MD_SYNC_SET_REQ		0x13
+#define MCAP_MD_SYNC_SET_RSP		0x14
+#define MCAP_MD_SYNC_INFO_IND		0x15
+
+/* MCAP Response codes */
+#define MCAP_SUCCESS			0x00
+#define MCAP_INVALID_OP_CODE		0x01
+#define MCAP_INVALID_PARAM_VALUE	0x02
+#define MCAP_INVALID_MDEP		0x03
+#define MCAP_MDEP_BUSY			0x04
+#define MCAP_INVALID_MDL		0x05
+#define MCAP_MDL_BUSY			0x06
+#define MCAP_INVALID_OPERATION		0x07
+#define MCAP_RESOURCE_UNAVAILABLE	0x08
+#define MCAP_UNSPECIFIED_ERROR		0x09
+#define MCAP_REQUEST_NOT_SUPPORTED	0x0A
+#define MCAP_CONFIGURATION_REJECTED	0x0B
+
+/* MDL IDs */
+#define MCAP_MDLID_RESERVED		0x0000
+#define MCAP_MDLID_INITIAL		0x0001
+#define MCAP_MDLID_FINAL		0xFEFF
+#define MCAP_ALL_MDLIDS			0xFFFF
+
+/* MDEP IDs */
+#define MCAP_MDEPID_INITIAL		0x00
+#define MCAP_MDEPID_FINAL		0x7F
+
+/* CSP special values */
+#define MCAP_BTCLOCK_IMMEDIATE		0xffffffffUL
+#define MCAP_TMSTAMP_DONTSET		0xffffffffffffffffULL
+#define MCAP_BTCLOCK_MAX		0x0fffffff
+#define MCAP_BTCLOCK_FIELD		(MCAP_BTCLOCK_MAX + 1)
+
+#define	MCAP_CTRL_CACHED	0x01	/* MCL is cached */
+#define	MCAP_CTRL_STD_OP	0x02	/* Support for standard op codes */
+#define	MCAP_CTRL_SYNC_OP	0x04	/* Support for synchronization commands */
+#define	MCAP_CTRL_CONN		0x08	/* MCL is in connecting process */
+#define	MCAP_CTRL_FREE		0x10	/* MCL is marked as releasable */
+#define	MCAP_CTRL_NOCACHE	0x20	/* MCL is marked as not cacheable */
+
+/*
+ * MCAP Request Packet Format
+ */
+
+typedef struct {
+	uint8_t		op;
+	uint16_t	mdl;
+	uint8_t		mdep;
+	uint8_t		conf;
+} __attribute__ ((packed)) mcap_md_create_mdl_req;
+
+typedef struct {
+	uint8_t		op;
+	uint16_t	mdl;
+} __attribute__ ((packed)) mcap_md_req;
+
+/* MCAP Response Packet Format */
+
+typedef struct {
+	uint8_t		op;
+	uint8_t		rc;
+	uint16_t	mdl;
+	uint8_t		data[0];
+} __attribute__ ((packed)) mcap_rsp;
+
+/*  MCAP Clock Synchronization Protocol */
+
+typedef struct {
+	uint8_t		op;
+	uint16_t	timest;
+} __attribute__ ((packed)) mcap_md_sync_cap_req;
+
+typedef struct {
+	uint8_t		op;
+	uint8_t		rc;
+} __attribute__ ((packed)) mcap_md_sync_rsp;
+
+typedef struct {
+	uint8_t		op;
+	uint8_t		rc;
+	uint8_t		btclock;
+	uint16_t	sltime;
+	uint16_t	timestnr;
+	uint16_t	timestna;
+} __attribute__ ((packed)) mcap_md_sync_cap_rsp;
+
+typedef struct {
+	uint8_t		op;
+	uint8_t		timestui;
+	uint32_t	btclock;
+	uint64_t	timestst;
+} __attribute__ ((packed)) mcap_md_sync_set_req;
+
+typedef struct {
+	int8_t		op;
+	uint8_t		rc;
+	uint32_t	btclock;
+	uint64_t	timestst;
+	uint16_t	timestsa;
+} __attribute__ ((packed)) mcap_md_sync_set_rsp;
+
+typedef struct {
+	uint8_t		op;
+	uint32_t	btclock;
+	uint64_t	timestst;
+	uint16_t	timestsa;
+} __attribute__ ((packed)) mcap_md_sync_info_ind;
+
+typedef enum {
+/* MCAP Error Response Codes */
+	MCAP_ERROR_INVALID_OP_CODE = 1,
+	MCAP_ERROR_INVALID_PARAM_VALUE,
+	MCAP_ERROR_INVALID_MDEP,
+	MCAP_ERROR_MDEP_BUSY,
+	MCAP_ERROR_INVALID_MDL,
+	MCAP_ERROR_MDL_BUSY,
+	MCAP_ERROR_INVALID_OPERATION,
+	MCAP_ERROR_RESOURCE_UNAVAILABLE,
+	MCAP_ERROR_UNSPECIFIED_ERROR,
+	MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+	MCAP_ERROR_CONFIGURATION_REJECTED,
+/* MCAP Internal Errors */
+	MCAP_ERROR_INVALID_ARGS,
+	MCAP_ERROR_ALREADY_EXISTS,
+	MCAP_ERROR_REQ_IGNORED,
+	MCAP_ERROR_MCL_CLOSED,
+	MCAP_ERROR_FAILED
+} McapError;
+
+typedef enum {
+	MCAP_MDL_CB_INVALID,
+	MCAP_MDL_CB_CONNECTED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_CLOSED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_DELETED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_ABORTED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_REMOTE_CONN_REQ,	/* mcap_remote_mdl_conn_req_cb */
+	MCAP_MDL_CB_REMOTE_RECONN_REQ	/* mcap_remote_mdl_reconn_req_cb */
+} McapMclCb;
+
+typedef enum {
+	MCL_CONNECTED,
+	MCL_PENDING,
+	MCL_ACTIVE,
+	MCL_IDLE
+} MCLState;
+
+typedef enum {
+	MCL_ACCEPTOR,
+	MCL_INITIATOR
+} MCLRole;
+
+typedef enum {
+	MCL_AVAILABLE,
+	MCL_WAITING_RSP
+} MCAPCtrl;
+
+typedef enum {
+	MDL_WAITING,
+	MDL_CONNECTED,
+	MDL_DELETING,
+	MDL_CLOSED
+} MDLState;
+
+struct mcap_csp;
+struct mcap_mdl_op_cb;
+struct mcap_instance;
+struct mcap_mcl;
+struct mcap_mdl;
+struct sync_info_ind_data;
+
+/************ Callbacks ************/
+
+/* MDL callbacks */
+
+typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data);
+typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf,
+						GError *err, gpointer data);
+typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err,
+						gpointer data);
+typedef void (* mcap_mdl_notify_cb) (GError *err, gpointer data);
+
+/* Next function should return an MCAP appropriate response code */
+typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl,
+						uint8_t mdepid, uint16_t mdlid,
+						uint8_t *conf, gpointer data);
+typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl,
+						gpointer data);
+
+/* MCL callbacks */
+
+typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data);
+typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err,
+								gpointer data);
+
+/* CSP callbacks */
+
+typedef void (* mcap_info_ind_event_cb) (struct mcap_mcl *mcl,
+					struct sync_info_ind_data *data);
+
+typedef void (* mcap_sync_cap_cb) (struct mcap_mcl *mcl,
+					uint8_t mcap_err,
+					uint8_t btclockres,
+					uint16_t synclead,
+					uint16_t tmstampres,
+					uint16_t tmstampacc,
+					GError *err,
+					gpointer data);
+
+typedef void (* mcap_sync_set_cb) (struct mcap_mcl *mcl,
+					uint8_t mcap_err,
+					uint32_t btclock,
+					uint64_t timestamp,
+					uint16_t accuracy,
+					GError *err,
+					gpointer data);
+
+struct mcap_mdl_cb {
+	mcap_mdl_event_cb		mdl_connected;	/* Remote device has created a MDL */
+	mcap_mdl_event_cb		mdl_closed;	/* Remote device has closed a MDL */
+	mcap_mdl_event_cb		mdl_deleted;	/* Remote device requested deleting a MDL */
+	mcap_mdl_event_cb		mdl_aborted;	/* Remote device aborted the mdl creation */
+	mcap_remote_mdl_conn_req_cb	mdl_conn_req;	/* Remote device requested creating a MDL */
+	mcap_remote_mdl_reconn_req_cb	mdl_reconn_req;	/* Remote device requested reconnecting a MDL */
+	gpointer			user_data;	/* User data */
+};
+
+struct mcap_instance {
+	bdaddr_t		src;			/* Source address */
+	GIOChannel		*ccio;			/* Control Channel IO */
+	GIOChannel		*dcio;			/* Data Channel IO */
+	GSList			*mcls;			/* MCAP instance list */
+	GSList			*cached;		/* List with all cached MCLs (MAX_CACHED macro) */
+	BtIOSecLevel		sec;			/* Security level */
+	mcap_mcl_event_cb	mcl_connected_cb;	/* New MCL connected */
+	mcap_mcl_event_cb	mcl_reconnected_cb;	/* Old MCL has been reconnected */
+	mcap_mcl_event_cb	mcl_disconnected_cb;	/* MCL disconnected */
+	mcap_mcl_event_cb	mcl_uncached_cb;	/* MCL has been removed from MCAP cache */
+	mcap_info_ind_event_cb	mcl_sync_infoind_cb;	/* (CSP Master) Received info indication */
+	gpointer		user_data;		/* Data to be provided in callbacks */
+	int			ref;			/* Reference counter */
+
+	gboolean		csp_enabled;		/* CSP: functionality enabled */
+};
+
+struct mcap_mcl {
+	struct mcap_instance	*mi;		/* MCAP instance where this MCL belongs */
+	bdaddr_t		addr;		/* Device address */
+	GIOChannel		*cc;		/* MCAP Control Channel IO */
+	guint			wid;		/* MCL Watcher id */
+	GSList			*mdls;		/* List of Data Channels shorted by mdlid */
+	MCLState		state;		/* Current MCL State */
+	MCLRole			role;		/* Initiator or acceptor of this MCL */
+	MCAPCtrl		req;		/* Request control flag */
+	struct mcap_mdl_op_cb	*priv_data;	/* Temporal data to manage responses */
+	struct mcap_mdl_cb	*cb;		/* MDL callbacks */
+	guint			tid;		/* Timer id for waiting for a response */
+	uint8_t			*lcmd;		/* Last command sent */
+	int			ref;		/* References counter */
+	uint8_t			ctrl;		/* MCL control flag */
+	uint16_t		next_mdl;	/* id used to create next MDL */
+	struct mcap_csp		*csp;		/* CSP control structure */
+};
+
+struct mcap_mdl {
+	struct mcap_mcl		*mcl;		/* MCL where this MDL belongs */
+	GIOChannel		*dc;		/* MCAP Data Channel IO */
+	guint			wid;		/* MDL Watcher id */
+	uint16_t		mdlid;		/* MDL id */
+	uint8_t			mdep_id;	/* MCAP Data End Point */
+	MDLState		state;		/* MDL state */
+	int			ref;		/* References counter */
+};
+
+struct sync_info_ind_data {
+	uint32_t	btclock;
+	uint64_t	timestamp;
+	uint16_t	accuracy;
+};
+
+/************ Operations ************/
+
+/* MDL operations */
+
+gboolean mcap_create_mdl(struct mcap_mcl *mcl,
+				uint8_t mdepid,
+				uint8_t conf,
+				mcap_mdl_operation_conf_cb connect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl,
+				mcap_mdl_operation_cb reconnect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl,
+				mcap_mdl_notify_cb delete_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+gboolean mcap_delete_mdl(struct mcap_mdl *mdl,
+				mcap_mdl_notify_cb delete_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+gboolean mcap_connect_mdl(struct mcap_mdl *mdl,
+				uint8_t mode,
+				uint16_t dcpsm,
+				mcap_mdl_operation_cb connect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+gboolean mcap_mdl_abort(struct mcap_mdl *mdl,
+				mcap_mdl_notify_cb abort_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl);
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl);
+struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl);
+void mcap_mdl_unref(struct mcap_mdl *mdl);
+
+/* MCL operations */
+
+gboolean mcap_create_mcl(struct mcap_instance *mi,
+				const bdaddr_t *addr,
+				uint16_t ccpsm,
+				mcap_mcl_connect_cb connect_cb,
+				gpointer user_data,
+				GDestroyNotify destroy,
+				GError **err);
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache);
+gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data,
+					GError **gerr, McapMclCb cb1, ...);
+void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr);
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl);
+void mcap_mcl_unref(struct mcap_mcl *mcl);
+
+/* CSP operations */
+
+void mcap_enable_csp(struct mcap_instance *mi);
+void mcap_disable_csp(struct mcap_instance *mi);
+uint64_t mcap_get_timestamp(struct mcap_mcl *mcl,
+				struct timespec *given_time);
+uint32_t mcap_get_btclock(struct mcap_mcl *mcl);
+
+void mcap_sync_cap_req(struct mcap_mcl *mcl,
+			uint16_t reqacc,
+			mcap_sync_cap_cb cb,
+			gpointer user_data,
+			GError **err);
+
+void mcap_sync_set_req(struct mcap_mcl *mcl,
+			uint8_t update,
+			uint32_t btclock,
+			uint64_t timestamp,
+			mcap_sync_set_cb cb,
+			gpointer user_data,
+			GError **err);
+
+/* MCAP main operations */
+
+struct mcap_instance *mcap_create_instance(const bdaddr_t *src,
+					BtIOSecLevel sec, uint16_t ccpsm,
+					uint16_t dcpsm,
+					mcap_mcl_event_cb mcl_connected,
+					mcap_mcl_event_cb mcl_reconnected,
+					mcap_mcl_event_cb mcl_disconnected,
+					mcap_mcl_event_cb mcl_uncached,
+					mcap_info_ind_event_cb mcl_sync_info_ind,
+					gpointer user_data,
+					GError **gerr);
+void mcap_release_instance(struct mcap_instance *mi);
+
+struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi);
+void mcap_instance_unref(struct mcap_instance *mi);
+
+uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err);
+uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err);
+
+gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode,
+								GError **err);
+
+int mcap_send_data(int sock, const void *buf, uint32_t size);
+
+void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len);
+void mcap_sync_init(struct mcap_mcl *mcl);
+void mcap_sync_stop(struct mcap_mcl *mcl);
diff --git a/bluez/repo/profiles/heartrate/._heartrate.c b/bluez/repo/profiles/heartrate/._heartrate.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/heartrate/._heartrate.c
Binary files differ
diff --git a/bluez/repo/profiles/heartrate/heartrate.c b/bluez/repo/profiles/heartrate/heartrate.c
new file mode 100644
index 0000000..9e8c499
--- /dev/null
+++ b/bluez/repo/profiles/heartrate/heartrate.c
@@ -0,0 +1,870 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Tieto Poland
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/dbus-common.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/shared/util.h"
+#include "src/service.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/log.h"
+
+#define HEART_RATE_INTERFACE		"org.bluez.HeartRate1"
+#define HEART_RATE_MANAGER_INTERFACE	"org.bluez.HeartRateManager1"
+#define HEART_RATE_WATCHER_INTERFACE	"org.bluez.HeartRateWatcher1"
+
+#define HR_VALUE_FORMAT		0x01
+#define SENSOR_CONTACT_DETECTED	0x02
+#define SENSOR_CONTACT_SUPPORT	0x04
+#define ENERGY_EXP_STATUS	0x08
+#define RR_INTERVAL		0x10
+
+struct heartrate_adapter {
+	struct btd_adapter	*adapter;
+	GSList			*devices;
+	GSList			*watchers;
+};
+
+struct heartrate {
+	struct btd_device		*dev;
+	struct heartrate_adapter	*hradapter;
+	GAttrib				*attrib;
+	guint				attioid;
+	guint				attionotid;
+
+	struct att_range		*svc_range;	/* primary svc range */
+
+	uint16_t			measurement_ccc_handle;
+	uint16_t			hrcp_val_handle;
+
+	gboolean			has_location;
+	uint8_t				location;
+};
+
+struct watcher {
+	struct heartrate_adapter	*hradapter;
+	guint				id;
+	char				*srv;
+	char				*path;
+};
+
+struct measurement {
+	struct heartrate	*hr;
+	uint16_t		value;
+	gboolean		has_energy;
+	uint16_t		energy;
+	gboolean		has_contact;
+	gboolean		contact;
+	uint16_t		num_interval;
+	uint16_t		*interval;
+};
+
+static GSList *heartrate_adapters = NULL;
+
+static const char * const location_enum[] = {
+	"other",
+	"chest",
+	"wrist",
+	"finger",
+	"hand",
+	"earlobe",
+	"foot",
+};
+
+static const char *location2str(uint8_t value)
+{
+	 if (value < G_N_ELEMENTS(location_enum))
+		return location_enum[value];
+
+	error("Body Sensor Location [%d] is RFU", value);
+
+	return NULL;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+	const struct heartrate_adapter *hradapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (adapter == hradapter->adapter)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct heartrate *hr = a;
+	const struct btd_device *dev = b;
+
+	if (dev == hr->dev)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+	const struct watcher *watcher = a;
+	const struct watcher *match = b;
+	int ret;
+
+	ret = g_strcmp0(watcher->srv, match->srv);
+	if (ret != 0)
+		return ret;
+
+	return g_strcmp0(watcher->path, match->path);
+}
+
+static struct heartrate_adapter *
+find_heartrate_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(heartrate_adapters, adapter,
+								cmp_adapter);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_free(watcher->path);
+	g_free(watcher->srv);
+	g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+							const char *path)
+{
+	struct watcher *match;
+	GSList *l;
+
+	match = g_new0(struct watcher, 1);
+	match->srv = g_strdup(sender);
+	match->path = g_strdup(path);
+
+	l = g_slist_find_custom(list, match, cmp_watcher);
+	destroy_watcher(match);
+
+	if (l != NULL)
+		return l->data;
+
+	return NULL;
+}
+
+static void destroy_heartrate(gpointer user_data)
+{
+	struct heartrate *hr = user_data;
+
+	if (hr->attioid > 0)
+		btd_device_remove_attio_callback(hr->dev, hr->attioid);
+
+	if (hr->attrib != NULL) {
+		g_attrib_unregister(hr->attrib, hr->attionotid);
+		g_attrib_unref(hr->attrib);
+	}
+
+	btd_device_unref(hr->dev);
+	g_free(hr->svc_range);
+	g_free(hr);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_heartrate_adapter(gpointer user_data)
+{
+	struct heartrate_adapter *hradapter = user_data;
+
+	g_slist_free_full(hradapter->watchers, remove_watcher);
+
+	g_free(hradapter);
+}
+
+static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	struct heartrate *hr = user_data;
+	uint8_t value;
+	ssize_t vlen;
+
+	if (status != 0) {
+		error("Body Sensor Location read failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+	if (vlen < 0) {
+		error("Protocol error");
+		return;
+	}
+
+	if (vlen != sizeof(value)) {
+		error("Invalid length for Body Sensor Location");
+		return;
+	}
+
+	hr->has_location = TRUE;
+	hr->location = value;
+}
+
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	char *msg = user_data;
+
+	if (status != 0)
+		error("%s failed", msg);
+
+	g_free(msg);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+	struct watcher *w = data;
+	struct measurement *m = user_data;
+	struct heartrate *hr = m->hr;
+	const char *path = device_get_path(hr->dev);
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(w->srv, w->path,
+			HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	dict_append_entry(&dict, "Value", DBUS_TYPE_UINT16, &m->value);
+
+	if (m->has_energy)
+		dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16,
+								&m->energy);
+
+	if (m->has_contact)
+		dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN,
+								&m->contact);
+
+	if (m->num_interval > 0)
+		dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16,
+						&m->interval, m->num_interval);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
+								uint16_t len)
+{
+	struct measurement m;
+	uint8_t flags;
+
+	flags = *pdu;
+
+	pdu++;
+	len--;
+
+	memset(&m, 0, sizeof(m));
+
+	if (flags & HR_VALUE_FORMAT) {
+		if (len < 2) {
+			error("Heart Rate Measurement field missing");
+			return;
+		}
+
+		m.value = get_le16(pdu);
+		pdu += 2;
+		len -= 2;
+	} else {
+		if (len < 1) {
+			error("Heart Rate Measurement field missing");
+			return;
+		}
+
+		m.value = *pdu;
+		pdu++;
+		len--;
+	}
+
+	if (flags & ENERGY_EXP_STATUS) {
+		if (len < 2) {
+			error("Energy Expended field missing");
+			return;
+		}
+
+		m.has_energy = TRUE;
+		m.energy = get_le16(pdu);
+		pdu += 2;
+		len -= 2;
+	}
+
+	if (flags & RR_INTERVAL) {
+		int i;
+
+		if (len == 0 || (len % 2 != 0)) {
+			error("RR-Interval field malformed");
+			return;
+		}
+
+		m.num_interval = len / 2;
+		m.interval = g_new(uint16_t, m.num_interval);
+
+		for (i = 0; i < m.num_interval; pdu += 2, i++)
+			m.interval[i] = get_le16(pdu);
+	}
+
+	if (flags & SENSOR_CONTACT_SUPPORT) {
+		m.has_contact = TRUE;
+		m.contact = !!(flags & SENSOR_CONTACT_DETECTED);
+	}
+
+	/* Notify all registered watchers */
+	m.hr = hr;
+	g_slist_foreach(hr->hradapter->watchers, update_watcher, &m);
+
+	g_free(m.interval);
+}
+
+static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+	struct heartrate *hr = user_data;
+
+	/* should be at least opcode (1b) + handle (2b) */
+	if (len < 3) {
+		error("Invalid PDU received");
+		return;
+	}
+
+	process_measurement(hr, pdu + 3, len - 3);
+}
+
+static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
+{
+	struct heartrate *hr = user_data;
+	struct gatt_desc *desc;
+	uint8_t attr_val[2];
+	char *msg;
+
+	if (status != 0) {
+		error("Discover Heart Rate Measurement descriptors failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	/* There will be only one descriptor on list and it will be CCC */
+	desc = descs->data;
+
+	hr->measurement_ccc_handle = desc->handle;
+
+	if (g_slist_length(hr->hradapter->watchers) == 0) {
+		put_le16(0x0000, attr_val);
+		msg = g_strdup("Disable measurement");
+	} else {
+		put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
+		msg = g_strdup("Enable measurement");
+	}
+
+	gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val),
+							char_write_cb, msg);
+}
+
+static void discover_measurement_ccc(struct heartrate *hr,
+				struct gatt_char *c, struct gatt_char *c_next)
+{
+	uint16_t start, end;
+	bt_uuid_t uuid;
+
+	start = c->value_handle + 1;
+
+	if (c_next != NULL) {
+		if (start == c_next->handle)
+			return;
+		end = c_next->handle - 1;
+	} else if (c->value_handle != hr->svc_range->end) {
+		end = hr->svc_range->end;
+	} else {
+		return;
+	}
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+	gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr);
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+	struct heartrate *hr = user_data;
+
+	if (status) {
+		error("Discover HRS characteristics failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (; chars; chars = chars->next) {
+		struct gatt_char *c = chars->data;
+
+		if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
+			struct gatt_char *c_next =
+				(chars->next ? chars->next->data : NULL);
+
+			hr->attionotid = g_attrib_register(hr->attrib,
+						ATT_OP_HANDLE_NOTIFY,
+						c->value_handle,
+						notify_handler, hr, NULL);
+
+			discover_measurement_ccc(hr, c, c_next);
+		} else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
+			DBG("Body Sensor Location supported");
+
+			gatt_read_char(hr->attrib, c->value_handle,
+						read_sensor_location_cb, hr);
+		} else if (g_strcmp0(c->uuid,
+					HEART_RATE_CONTROL_POINT_UUID) == 0) {
+			DBG("Heart Rate Control Point supported");
+			hr->hrcp_val_handle = c->value_handle;
+		}
+	}
+}
+
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+	struct heartrate *hr = data;
+	uint16_t handle = hr->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (hr->attrib == NULL || !handle)
+		return;
+
+	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+	msg = g_strdup("Enable measurement");
+
+	gatt_write_char(hr->attrib, handle, value, sizeof(value),
+							char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+	struct heartrate *hr = data;
+	uint16_t handle = hr->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (hr->attrib == NULL || !handle)
+		return;
+
+	put_le16(0x0000, value);
+	msg = g_strdup("Disable measurement");
+
+	gatt_write_char(hr->attrib, handle, value, sizeof(value),
+							char_write_cb, msg);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct heartrate *hr = user_data;
+
+	DBG("");
+
+	hr->attrib = g_attrib_ref(attrib);
+
+	gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
+						NULL, discover_char_cb, hr);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct heartrate *hr = user_data;
+
+	DBG("");
+
+	if (hr->attionotid > 0) {
+		g_attrib_unregister(hr->attrib, hr->attionotid);
+		hr->attionotid = 0;
+	}
+
+	g_attrib_unref(hr->attrib);
+	hr->attrib = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+	struct watcher *watcher = user_data;
+	struct heartrate_adapter *hradapter = watcher->hradapter;
+
+	DBG("heartrate watcher [%s] disconnected", watcher->path);
+
+	hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+
+	if (g_slist_length(hradapter->watchers) == 0)
+		g_slist_foreach(hradapter->devices, disable_measurement, 0);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct heartrate_adapter *hradapter = data;
+	struct watcher *watcher;
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(hradapter->watchers, sender, path);
+	if (watcher != NULL)
+		return btd_error_already_exists(msg);
+
+	watcher = g_new0(struct watcher, 1);
+	watcher->hradapter = hradapter;
+	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+						watcher, destroy_watcher);
+	watcher->srv = g_strdup(sender);
+	watcher->path = g_strdup(path);
+
+	if (g_slist_length(hradapter->watchers) == 0)
+		g_slist_foreach(hradapter->devices, enable_measurement, 0);
+
+	hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
+
+	DBG("heartrate watcher [%s] registered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct heartrate_adapter *hradapter = data;
+	struct watcher *watcher;
+	const char *sender = dbus_message_get_sender(msg);
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(hradapter->watchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+	g_dbus_remove_watch(conn, watcher->id);
+
+	if (g_slist_length(hradapter->watchers) == 0)
+		g_slist_foreach(hradapter->devices, disable_measurement, 0);
+
+	DBG("heartrate watcher [%s] unregistered", path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_manager_methods[] = {
+	{ GDBUS_METHOD("RegisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			register_watcher) },
+	{ GDBUS_METHOD("UnregisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			unregister_watcher) },
+	{ }
+};
+
+static gboolean property_get_location(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct heartrate *hr = data;
+	char *loc;
+
+	if (!hr->has_location)
+		return FALSE;
+
+	loc = g_strdup(location2str(hr->location));
+
+	if (loc == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+	g_free(loc);
+
+	return TRUE;
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct heartrate *hr = data;
+
+	if (!hr->has_location || location2str(hr->location) == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean property_get_reset_supported(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct heartrate *hr = data;
+	dbus_bool_t has_reset = !!hr->hrcp_val_handle;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &has_reset);
+
+	return TRUE;
+}
+
+static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct heartrate *hr = data;
+	uint8_t value;
+	char *vmsg;
+
+	if (!hr->hrcp_val_handle)
+		return btd_error_not_supported(msg);
+
+	if (!hr->attrib)
+		return btd_error_not_available(msg);
+
+	value = 0x01;
+	vmsg = g_strdup("Reset Control Point");
+	gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value,
+					sizeof(value), char_write_cb, vmsg);
+
+	DBG("Energy Expended Value has been reset");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_device_methods[] = {
+	{ GDBUS_METHOD("Reset", NULL, NULL, hrcp_reset) },
+	{ }
+};
+
+static const GDBusPropertyTable heartrate_device_properties[] = {
+	{ "Location", "s", property_get_location, NULL,
+						property_exists_location },
+	{ "ResetSupported", "b", property_get_reset_supported },
+	{ }
+};
+
+static int heartrate_adapter_register(struct btd_adapter *adapter)
+{
+	struct heartrate_adapter *hradapter;
+
+	hradapter = g_new0(struct heartrate_adapter, 1);
+	hradapter->adapter = adapter;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						adapter_get_path(adapter),
+						HEART_RATE_MANAGER_INTERFACE,
+						heartrate_manager_methods,
+						NULL, NULL, hradapter,
+						destroy_heartrate_adapter)) {
+		error("D-Bus failed to register %s interface",
+						HEART_RATE_MANAGER_INTERFACE);
+		destroy_heartrate_adapter(hradapter);
+		return -EIO;
+	}
+
+	heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
+
+	return 0;
+}
+
+static void heartrate_adapter_unregister(struct btd_adapter *adapter)
+{
+	struct heartrate_adapter *hradapter;
+
+	hradapter = find_heartrate_adapter(adapter);
+	if (hradapter == NULL)
+		return;
+
+	heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(hradapter->adapter),
+					HEART_RATE_MANAGER_INTERFACE);
+}
+
+static int heartrate_device_register(struct btd_device *device,
+						struct gatt_primary *prim)
+{
+	struct btd_adapter *adapter;
+	struct heartrate_adapter *hradapter;
+	struct heartrate *hr;
+
+	adapter = device_get_adapter(device);
+
+	hradapter = find_heartrate_adapter(adapter);
+
+	if (hradapter == NULL)
+		return -1;
+
+	hr = g_new0(struct heartrate, 1);
+	hr->dev = btd_device_ref(device);
+	hr->hradapter = hradapter;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						device_get_path(device),
+						HEART_RATE_INTERFACE,
+						heartrate_device_methods,
+						NULL,
+						heartrate_device_properties,
+						hr, destroy_heartrate)) {
+		error("D-Bus failed to register %s interface",
+						HEART_RATE_INTERFACE);
+		destroy_heartrate(hr);
+		return -EIO;
+	}
+
+	hr->svc_range = g_new0(struct att_range, 1);
+	hr->svc_range->start = prim->range.start;
+	hr->svc_range->end = prim->range.end;
+
+	hradapter->devices = g_slist_prepend(hradapter->devices, hr);
+
+	hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+						attio_disconnected_cb, hr);
+
+	return 0;
+}
+
+static void heartrate_device_unregister(struct btd_device *device)
+{
+	struct btd_adapter *adapter;
+	struct heartrate_adapter *hradapter;
+	struct heartrate *hr;
+	GSList *l;
+
+	adapter = device_get_adapter(device);
+
+	hradapter = find_heartrate_adapter(adapter);
+	if (hradapter == NULL)
+		return;
+
+	l = g_slist_find_custom(hradapter->devices, device, cmp_device);
+	if (l == NULL)
+		return;
+
+	hr = l->data;
+
+	hradapter->devices = g_slist_remove(hradapter->devices, hr);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				device_get_path(device), HEART_RATE_INTERFACE);
+}
+
+static int heartrate_adapter_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	return heartrate_adapter_register(adapter);
+}
+
+static void heartrate_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	heartrate_adapter_unregister(adapter);
+}
+
+static int heartrate_device_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *prim;
+
+	prim = btd_device_get_primary(device, HEART_RATE_UUID);
+	if (prim == NULL)
+		return -EINVAL;
+
+	return heartrate_device_register(device, prim);
+}
+
+static void heartrate_device_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	heartrate_device_unregister(device);
+}
+
+static struct btd_profile hrp_profile = {
+	.name		= "Heart Rate GATT Driver",
+	.remote_uuid	= HEART_RATE_UUID,
+
+	.device_probe	= heartrate_device_probe,
+	.device_remove	= heartrate_device_remove,
+
+	.adapter_probe	= heartrate_adapter_probe,
+	.adapter_remove	= heartrate_adapter_remove,
+};
+
+static int heartrate_init(void)
+{
+	return btd_profile_register(&hrp_profile);
+}
+
+static void heartrate_exit(void)
+{
+	btd_profile_unregister(&hrp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+					heartrate_init, heartrate_exit)
diff --git a/bluez/repo/profiles/iap/._main.c b/bluez/repo/profiles/iap/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/iap/._main.c
Binary files differ
diff --git a/bluez/repo/profiles/iap/main.c b/bluez/repo/profiles/iap/main.c
new file mode 100644
index 0000000..53dd9fb
--- /dev/null
+++ b/bluez/repo/profiles/iap/main.c
@@ -0,0 +1,466 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#define IAP_PATH "/org/bluez/iap"
+
+#define IAP_UUID "00000000-deca-fade-deca-deafdecacafe"
+
+#define IAP_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"%s\" />			\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0002\">				\
+			<uint32 value=\"0x00000000\" />			\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0006\">				\
+			<sequence>					\
+				<uint16 value=\"0x656e\" />		\
+				<uint16 value=\"0x006a\" />		\
+				<uint16 value=\"0x0100\" />		\
+				<uint16 value=\"0x6672\" />		\
+				<uint16 value=\"0x006a\" />		\
+				<uint16 value=\"0x0110\" />		\
+				<uint16 value=\"0x6465\" />		\
+				<uint16 value=\"0x006a\" />		\
+				<uint16 value=\"0x0120\" />		\
+				<uint16 value=\"0x6a61\" />		\
+				<uint16 value=\"0x006a\" />		\
+				<uint16 value=\"0x0130\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0008\">				\
+			<uint8 value=\"0xff\" />			\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1101\" />	\
+					<uint16 value=\"0x0100\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"Wireless iAP\" />			\
+		</attribute>						\
+	</record>"
+
+static GMainLoop *main_loop;
+
+static guint iap_source = 0;
+
+static gboolean iap_handler(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		iap_source = 0;
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0) {
+		iap_source = 0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static guint create_source(int fd, GIOFunc func)
+{
+	GIOChannel *channel;
+	guint source;
+
+	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, func, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static void remove_source(const char *path)
+{
+	if (iap_source > 0) {
+		g_source_remove(iap_source);
+		iap_source = 0;
+	}
+}
+
+static DBusMessage *release_profile(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	g_print("Profile released\n");
+
+	remove_source(IAP_PATH);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *new_connection(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *path, *device;
+	int fd;
+
+	g_print("New connection\n");
+
+	path = dbus_message_get_path(msg);
+
+	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
+				DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID);
+
+	g_print("  from %s\n", path);
+	g_print("  for device %s with fd %d\n", device, fd);
+
+	if (iap_source == 0)
+		iap_source = create_source(fd, iap_handler);
+	else
+		close(fd);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *request_disconnection(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	DBusMessageIter iter;
+	const char *path, *device;
+
+	g_print("Request disconnection\n");
+
+	path = dbus_message_get_path(msg);
+
+	dbus_message_iter_init(msg, &iter);
+	dbus_message_iter_get_basic(&iter, &device);
+
+	g_print("  from %s\n", path);
+	g_print("  for device %s\n", device);
+
+	remove_source(path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *cancel_request(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *path;
+
+	g_print("Request canceled\n");
+
+	path = dbus_message_get_path(msg);
+
+	g_print("  from %s\n", path);
+
+	remove_source(path);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_METHOD("Release", NULL, NULL, release_profile) },
+	{ GDBUS_METHOD("NewConnection",
+			GDBUS_ARGS({ "device", "o" },
+					{ "fd", "h"}, { "opts", "a{sv}"}),
+			NULL, new_connection) },
+	{ GDBUS_METHOD("RequestDisconnection",
+			GDBUS_ARGS({ "device", "o" }),
+			NULL, request_disconnection) },
+	{ GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
+	{ }
+};
+
+static void register_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = IAP_PATH;
+	const char *uuid = IAP_UUID;
+	DBusMessageIter dict, entry, value;
+	dbus_uint16_t channel;
+	char *record;
+	const char *str;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+	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_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &entry);
+	str = "Role";
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					DBUS_TYPE_STRING_AS_STRING, &value);
+	str = "server";
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+	dbus_message_iter_close_container(&entry, &value);
+	dbus_message_iter_close_container(&dict, &entry);
+
+	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &entry);
+	str = "Channel";
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					DBUS_TYPE_UINT16_AS_STRING, &value);
+	channel = 23;
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_UINT16, &channel);
+	dbus_message_iter_close_container(&entry, &value);
+	dbus_message_iter_close_container(&dict, &entry);
+
+	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+							NULL, &entry);
+	str = "ServiceRecord";
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					DBUS_TYPE_STRING_AS_STRING, &value);
+	record = g_strdup_printf(IAP_RECORD, IAP_UUID, channel);
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &record);
+	g_free(record);
+	dbus_message_iter_close_container(&entry, &value);
+	dbus_message_iter_close_container(&dict, &entry);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void register_profile_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		g_print("Failed to register profile\n");
+		return;
+	}
+
+	g_print("Profile registered\n");
+}
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+	GDBusClient *client = user_data;
+	GDBusProxy *proxy;
+
+	g_print("Bluetooth connected\n");
+
+	proxy = g_dbus_proxy_new(client, "/org/bluez",
+					"org.bluez.ProfileManager1");
+	if (!proxy)
+		return;
+
+	g_dbus_register_interface(connection, IAP_PATH,
+					"org.bluez.Profile1",
+					methods, NULL, NULL, NULL, NULL);
+
+	g_dbus_proxy_method_call(proxy, "RegisterProfile", 
+					register_profile_setup,
+					register_profile_reply, NULL, NULL);
+
+	g_dbus_proxy_unref(proxy);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	g_print("Bluetooth disconnected\n");
+
+	g_dbus_unregister_interface(connection, IAP_PATH,
+						"org.bluez.Profile1");
+}
+
+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:
+	case SIGTERM:
+		if (__terminated == 0)
+			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 option_version = FALSE;
+
+static GOptionEntry options[] = {
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit" },
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *error = NULL;
+	DBusConnection *dbus_conn;
+	GDBusClient *client;
+	guint signal;
+
+	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) {
+		g_print("%s\n", VERSION);
+		exit(0);
+	}
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+	dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+
+	signal = setup_signalfd();
+
+	client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+
+	g_dbus_client_set_connect_watch(client, connect_handler, client);
+	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
+
+	g_main_loop_run(main_loop);
+
+	g_dbus_client_unref(client);
+
+	g_source_remove(signal);
+
+	dbus_connection_unref(dbus_conn);
+	g_main_loop_unref(main_loop);
+
+	return 0;
+}
diff --git a/bluez/repo/profiles/input/._device.c b/bluez/repo/profiles/input/._device.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._device.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._device.h b/bluez/repo/profiles/input/._device.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._device.h
Binary files differ
diff --git a/bluez/repo/profiles/input/._hidp_defs.h b/bluez/repo/profiles/input/._hidp_defs.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._hidp_defs.h
Binary files differ
diff --git a/bluez/repo/profiles/input/._hog-lib.c b/bluez/repo/profiles/input/._hog-lib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._hog-lib.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._hog-lib.h b/bluez/repo/profiles/input/._hog-lib.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._hog-lib.h
Binary files differ
diff --git a/bluez/repo/profiles/input/._hog.c b/bluez/repo/profiles/input/._hog.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._hog.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._input.conf b/bluez/repo/profiles/input/._input.conf
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._input.conf
Binary files differ
diff --git a/bluez/repo/profiles/input/._manager.c b/bluez/repo/profiles/input/._manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._manager.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._server.c b/bluez/repo/profiles/input/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._server.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._server.h b/bluez/repo/profiles/input/._server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._server.h
Binary files differ
diff --git a/bluez/repo/profiles/input/._suspend-dummy.c b/bluez/repo/profiles/input/._suspend-dummy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._suspend-dummy.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._suspend-none.c b/bluez/repo/profiles/input/._suspend-none.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._suspend-none.c
Binary files differ
diff --git a/bluez/repo/profiles/input/._suspend.h b/bluez/repo/profiles/input/._suspend.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._suspend.h
Binary files differ
diff --git a/bluez/repo/profiles/input/._uhid_copy.h b/bluez/repo/profiles/input/._uhid_copy.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/input/._uhid_copy.h
Binary files differ
diff --git a/bluez/repo/profiles/input/device.c b/bluez/repo/profiles/input/device.c
new file mode 100644
index 0000000..a494ea2
--- /dev/null
+++ b/bluez/repo/profiles/input/device.c
@@ -0,0 +1,1497 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2014       Google 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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hidp.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/storage.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
+#include "src/sdp-client.h"
+#include "src/shared/uhid.h"
+
+#include "device.h"
+#include "hidp_defs.h"
+
+#define INPUT_INTERFACE "org.bluez.Input1"
+
+enum reconnect_mode_t {
+	RECONNECT_NONE = 0,
+	RECONNECT_DEVICE,
+	RECONNECT_HOST,
+	RECONNECT_ANY
+};
+
+struct input_device {
+	struct btd_service	*service;
+	struct btd_device	*device;
+	char			*path;
+	bdaddr_t		src;
+	bdaddr_t		dst;
+	uint32_t		handle;
+	GIOChannel		*ctrl_io;
+	GIOChannel		*intr_io;
+	guint			ctrl_watch;
+	guint			intr_watch;
+	guint			sec_watch;
+	struct hidp_connadd_req *req;
+	bool			disable_sdp;
+	enum reconnect_mode_t	reconnect_mode;
+	guint			reconnect_timer;
+	uint32_t		reconnect_attempt;
+	struct bt_uhid		*uhid;
+	bool			uhid_created;
+	uint8_t			report_req_pending;
+	guint			report_req_timer;
+	uint32_t		report_rsp_id;
+};
+
+static int idle_timeout = 0;
+static bool uhid_enabled = false;
+
+void input_set_idle_timeout(int timeout)
+{
+	idle_timeout = timeout;
+}
+
+void input_enable_userspace_hid(bool state)
+{
+	uhid_enabled = state;
+}
+
+static void input_device_enter_reconnect_mode(struct input_device *idev);
+static int connection_disconnect(struct input_device *idev, uint32_t flags);
+
+static void input_device_free(struct input_device *idev)
+{
+	bt_uhid_unref(idev->uhid);
+	btd_service_unref(idev->service);
+	btd_device_unref(idev->device);
+	g_free(idev->path);
+
+	if (idev->ctrl_watch > 0)
+		g_source_remove(idev->ctrl_watch);
+
+	if (idev->intr_watch > 0)
+		g_source_remove(idev->intr_watch);
+
+	if (idev->sec_watch > 0)
+		g_source_remove(idev->sec_watch);
+
+	if (idev->intr_io)
+		g_io_channel_unref(idev->intr_io);
+
+	if (idev->ctrl_io)
+		g_io_channel_unref(idev->ctrl_io);
+
+	if (idev->req) {
+		g_free(idev->req->rd_data);
+		g_free(idev->req);
+	}
+
+	if (idev->reconnect_timer > 0)
+		g_source_remove(idev->reconnect_timer);
+
+	if (idev->report_req_timer > 0)
+		g_source_remove(idev->report_req_timer);
+
+	g_free(idev);
+}
+
+static bool hidp_send_message(GIOChannel *chan, uint8_t hdr,
+					const uint8_t *data, size_t size)
+{
+	int fd;
+	ssize_t len;
+	uint8_t msg[size + 1];
+
+	if (!chan) {
+		error("BT socket not connected");
+		return false;
+	}
+
+	if (data == NULL)
+		size = 0;
+
+	msg[0] = hdr;
+	if (size > 0)
+		memcpy(&msg[1], data, size);
+	++size;
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	len = write(fd, msg, size);
+	if (len < 0) {
+		error("BT socket write error: %s (%d)", strerror(errno), errno);
+		return false;
+	}
+
+	if ((size_t) len < size) {
+		error("BT socket write error: partial write (%zd of %zu bytes)",
+								len, size);
+		return false;
+	}
+
+	return true;
+}
+
+static bool hidp_send_ctrl_message(struct input_device *idev, uint8_t hdr,
+					const uint8_t *data, size_t size)
+{
+	return hidp_send_message(idev->ctrl_io, hdr, data, size);
+}
+
+static bool hidp_send_intr_message(struct input_device *idev, uint8_t hdr,
+					const uint8_t *data, size_t size)
+{
+	return hidp_send_message(idev->intr_io, hdr, data, size);
+}
+
+static bool uhid_send_feature_answer(struct input_device *idev,
+					const uint8_t *data, size_t size,
+					uint32_t id, uint16_t err)
+{
+	struct uhid_event ev;
+	int ret;
+
+	if (data == NULL)
+		size = 0;
+
+	if (size > sizeof(ev.u.feature_answer.data))
+		size = sizeof(ev.u.feature_answer.data);
+
+	if (!idev->uhid_created) {
+		DBG("HID report (%zu bytes) dropped", size);
+		return false;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_FEATURE_ANSWER;
+	ev.u.feature_answer.id = id;
+	ev.u.feature_answer.err = err;
+	ev.u.feature_answer.size = size;
+
+	if (size > 0)
+		memcpy(ev.u.feature_answer.data, data, size);
+
+	ret = bt_uhid_send(idev->uhid, &ev);
+	if (ret < 0) {
+		error("bt_uhid_send: %s (%d)", strerror(-ret), -ret);
+		return false;
+	}
+
+	DBG("HID report (%zu bytes)", size);
+
+	return true;
+}
+
+static bool uhid_send_input_report(struct input_device *idev,
+					const uint8_t *data, size_t size)
+{
+	struct uhid_event ev;
+	int err;
+
+	if (data == NULL)
+		size = 0;
+
+	if (size > sizeof(ev.u.input.data))
+		size = sizeof(ev.u.input.data);
+
+	if (!idev->uhid_created) {
+		DBG("HID report (%zu bytes) dropped", size);
+		return false;
+	}
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+	ev.u.input.size = size;
+
+	if (size > 0)
+		memcpy(ev.u.input.data, data, size);
+
+	err = bt_uhid_send(idev->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s (%d)", strerror(-err), -err);
+		return false;
+	}
+
+	DBG("HID report (%zu bytes)", size);
+
+	return true;
+}
+
+static bool hidp_recv_intr_data(GIOChannel *chan, struct input_device *idev)
+{
+	int fd;
+	ssize_t len;
+	uint8_t hdr;
+	uint8_t data[UHID_DATA_MAX + 1];
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	len = read(fd, data, sizeof(data));
+	if (len < 0) {
+		error("BT socket read error: %s (%d)", strerror(errno), errno);
+		return false;
+	}
+
+	if (len == 0) {
+		DBG("BT socket read returned 0 bytes");
+		return true;
+	}
+
+	hdr = data[0];
+	if (hdr != (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
+		DBG("unsupported HIDP protocol header 0x%02x", hdr);
+		return true;
+	}
+
+	if (len < 2) {
+		DBG("received empty HID report");
+		return true;
+	}
+
+	uhid_send_input_report(idev, data + 1, len - 1);
+
+	return true;
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct input_device *idev = data;
+	char address[18];
+
+	if (cond & G_IO_IN) {
+		if (hidp_recv_intr_data(chan, idev) && (cond == G_IO_IN))
+			return TRUE;
+	}
+
+	ba2str(&idev->dst, address);
+
+	DBG("Device %s disconnected", address);
+
+	/* Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+	 * it's likely that ctrl_watch_cb has been queued for dispatching in
+	 * this mainloop iteration */
+	if ((cond & (G_IO_HUP | G_IO_ERR)) && idev->ctrl_watch)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	idev->intr_watch = 0;
+
+	if (idev->intr_io) {
+		g_io_channel_unref(idev->intr_io);
+		idev->intr_io = NULL;
+	}
+
+	/* Close control channel */
+	if (idev->ctrl_io && !(cond & G_IO_NVAL))
+		g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
+
+	btd_service_disconnecting_complete(idev->service, 0);
+
+	/* Enter the auto-reconnect mode if needed */
+	input_device_enter_reconnect_mode(idev);
+
+	return FALSE;
+}
+
+static void hidp_recv_ctrl_handshake(struct input_device *idev, uint8_t param)
+{
+	bool pending_req_complete = false;
+	uint8_t pending_req_type;
+
+	DBG("");
+
+	pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
+
+	switch (param) {
+	case HIDP_HSHK_SUCCESSFUL:
+		if (pending_req_type == HIDP_TRANS_SET_REPORT) {
+			DBG("SET_REPORT successful");
+			pending_req_complete = true;
+		} else
+			DBG("Spurious HIDP_HSHK_SUCCESSFUL");
+		break;
+
+	case HIDP_HSHK_NOT_READY:
+	case HIDP_HSHK_ERR_INVALID_REPORT_ID:
+	case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
+	case HIDP_HSHK_ERR_INVALID_PARAMETER:
+	case HIDP_HSHK_ERR_UNKNOWN:
+	case HIDP_HSHK_ERR_FATAL:
+		if (pending_req_type == HIDP_TRANS_GET_REPORT) {
+			DBG("GET_REPORT failed (%u)", param);
+			uhid_send_feature_answer(idev, NULL, 0,
+						idev->report_rsp_id, EIO);
+			pending_req_complete = true;
+		} else if (pending_req_type == HIDP_TRANS_SET_REPORT) {
+			DBG("SET_REPORT failed (%u)", param);
+			pending_req_complete = true;
+		} else
+			DBG("Spurious HIDP_HSHK_ERR");
+
+		if (param == HIDP_HSHK_ERR_FATAL)
+			hidp_send_ctrl_message(idev, HIDP_TRANS_HID_CONTROL |
+						HIDP_CTRL_SOFT_RESET, NULL, 0);
+		break;
+
+	default:
+		hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE |
+				HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
+		break;
+	}
+
+	if (pending_req_complete) {
+		idev->report_req_pending = 0;
+		if (idev->report_req_timer > 0) {
+			g_source_remove(idev->report_req_timer);
+			idev->report_req_timer = 0;
+		}
+		idev->report_rsp_id = 0;
+	}
+}
+
+static void hidp_recv_ctrl_hid_control(struct input_device *idev, uint8_t param)
+{
+	DBG("");
+
+	if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG)
+		connection_disconnect(idev, 0);
+}
+
+static void hidp_recv_ctrl_data(struct input_device *idev, uint8_t param,
+					const uint8_t *data, size_t size)
+{
+	uint8_t pending_req_type;
+	uint8_t pending_req_param;
+
+	DBG("");
+
+	pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
+	if (pending_req_type != HIDP_TRANS_GET_REPORT) {
+		DBG("Spurious DATA on control channel");
+		return;
+	}
+
+	pending_req_param = idev->report_req_pending & HIDP_HEADER_PARAM_MASK;
+	if (pending_req_param != param) {
+		DBG("Received DATA RTYPE doesn't match pending request RTYPE");
+		return;
+	}
+
+	switch (param) {
+	case HIDP_DATA_RTYPE_FEATURE:
+	case HIDP_DATA_RTYPE_INPUT:
+	case HIDP_DATA_RTYPE_OUPUT:
+		uhid_send_feature_answer(idev, data + 1, size - 1,
+							idev->report_rsp_id, 0);
+		break;
+
+	case HIDP_DATA_RTYPE_OTHER:
+		DBG("Received DATA_RTYPE_OTHER");
+		break;
+
+	default:
+		hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE |
+				HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
+		break;
+	}
+
+	idev->report_req_pending = 0;
+	if (idev->report_req_timer > 0) {
+		g_source_remove(idev->report_req_timer);
+		idev->report_req_timer = 0;
+	}
+	idev->report_rsp_id = 0;
+}
+
+static bool hidp_recv_ctrl_message(GIOChannel *chan, struct input_device *idev)
+{
+	int fd;
+	ssize_t len;
+	uint8_t hdr, type, param;
+	uint8_t data[UHID_DATA_MAX + 1];
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	len = read(fd, data, sizeof(data));
+	if (len < 0) {
+		error("BT socket read error: %s (%d)", strerror(errno), errno);
+		return false;
+	}
+
+	if (len == 0) {
+		DBG("BT socket read returned 0 bytes");
+		return true;
+	}
+
+	hdr = data[0];
+	type = hdr & HIDP_HEADER_TRANS_MASK;
+	param = hdr & HIDP_HEADER_PARAM_MASK;
+
+	switch (type) {
+	case HIDP_TRANS_HANDSHAKE:
+		hidp_recv_ctrl_handshake(idev, param);
+		break;
+	case HIDP_TRANS_HID_CONTROL:
+		hidp_recv_ctrl_hid_control(idev, param);
+		break;
+	case HIDP_TRANS_DATA:
+		hidp_recv_ctrl_data(idev, param, data, len);
+		break;
+	default:
+		error("unsupported HIDP control message");
+		hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE |
+				HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
+		break;
+	}
+
+	return true;
+}
+
+static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct input_device *idev = data;
+	char address[18];
+
+	if (cond & G_IO_IN) {
+		if (hidp_recv_ctrl_message(chan, idev) && (cond == G_IO_IN))
+			return TRUE;
+	}
+
+	ba2str(&idev->dst, address);
+
+	DBG("Device %s disconnected", address);
+
+	/* Checking for intr_watch avoids a double g_io_channel_shutdown since
+	 * it's likely that intr_watch_cb has been queued for dispatching in
+	 * this mainloop iteration */
+	if ((cond & (G_IO_HUP | G_IO_ERR)) && idev->intr_watch)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	idev->ctrl_watch = 0;
+
+	if (idev->ctrl_io) {
+		g_io_channel_unref(idev->ctrl_io);
+		idev->ctrl_io = NULL;
+	}
+
+	/* Close interrupt channel */
+	if (idev->intr_io && !(cond & G_IO_NVAL))
+		g_io_channel_shutdown(idev->intr_io, TRUE, NULL);
+
+	return FALSE;
+}
+
+#define REPORT_REQ_TIMEOUT  3
+
+static gboolean hidp_report_req_timeout(gpointer data)
+{
+	struct input_device *idev = data;
+	uint8_t pending_req_type;
+	const char *req_type_str;
+	char address[18];
+
+	ba2str(&idev->dst, address);
+	pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
+
+	switch (pending_req_type) {
+	case HIDP_TRANS_GET_REPORT:
+		req_type_str = "GET_REPORT";
+		break;
+	case HIDP_TRANS_SET_REPORT:
+		req_type_str = "SET_REPORT";
+		break;
+	default:
+		/* Should never happen */
+		req_type_str = "OTHER_TRANS";
+		break;
+	}
+
+	DBG("Device %s HIDP %s request timed out", address, req_type_str);
+
+	idev->report_req_pending = 0;
+	idev->report_req_timer = 0;
+	idev->report_rsp_id = 0;
+
+	return FALSE;
+}
+
+static void hidp_send_set_report(struct uhid_event *ev, void *user_data)
+{
+	struct input_device *idev = user_data;
+	uint8_t hdr;
+	bool sent;
+
+	DBG("");
+
+	switch (ev->u.output.rtype) {
+	case UHID_FEATURE_REPORT:
+		/* Send SET_REPORT on control channel */
+		if (idev->report_req_pending) {
+			DBG("Old GET_REPORT or SET_REPORT still pending");
+			return;
+		}
+
+		hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
+		sent = hidp_send_ctrl_message(idev, hdr, ev->u.output.data,
+							ev->u.output.size);
+		if (sent) {
+			idev->report_req_pending = hdr;
+			idev->report_req_timer =
+				g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
+						hidp_report_req_timeout, idev);
+		}
+		break;
+	case UHID_OUTPUT_REPORT:
+		/* Send DATA on interrupt channel */
+		hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
+		hidp_send_intr_message(idev, hdr, ev->u.output.data,
+							ev->u.output.size);
+		break;
+	default:
+		DBG("Unsupported HID report type %u", ev->u.output.rtype);
+		return;
+	}
+}
+
+static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
+{
+	struct input_device *idev = user_data;
+	uint8_t hdr;
+	bool sent;
+
+	DBG("");
+
+	if (idev->report_req_pending) {
+		DBG("Old GET_REPORT or SET_REPORT still pending");
+		uhid_send_feature_answer(idev, NULL, 0, ev->u.feature.id,
+									EBUSY);
+		return;
+	}
+
+	/* Send GET_REPORT on control channel */
+	switch (ev->u.feature.rtype) {
+	case UHID_FEATURE_REPORT:
+		hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
+		break;
+	case UHID_INPUT_REPORT:
+		hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT;
+		break;
+	case UHID_OUTPUT_REPORT:
+		hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT;
+		break;
+	default:
+		DBG("Unsupported HID report type %u", ev->u.feature.rtype);
+		return;
+	}
+
+	sent = hidp_send_ctrl_message(idev, hdr, &ev->u.feature.rnum,
+						sizeof(ev->u.feature.rnum));
+	if (sent) {
+		idev->report_req_pending = hdr;
+		idev->report_req_timer =
+			g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
+						hidp_report_req_timeout, idev);
+		idev->report_rsp_id = ev->u.feature.id;
+	}
+}
+
+static void epox_endian_quirk(unsigned char *data, int size)
+{
+	/* USAGE_PAGE (Keyboard)	05 07
+	 * USAGE_MINIMUM (0)		19 00
+	 * USAGE_MAXIMUM (65280)	2A 00 FF   <= must be FF 00
+	 * LOGICAL_MINIMUM (0)		15 00
+	 * LOGICAL_MAXIMUM (65280)	26 00 FF   <= must be FF 00
+	 */
+	unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff,
+						0x15, 0x00, 0x26, 0x00, 0xff };
+	unsigned int i;
+
+	if (!data)
+		return;
+
+	for (i = 0; i < size - sizeof(pattern); i++) {
+		if (!memcmp(data + i, pattern, sizeof(pattern))) {
+			data[i + 5] = 0xff;
+			data[i + 6] = 0x00;
+			data[i + 10] = 0xff;
+			data[i + 11] = 0x00;
+		}
+	}
+}
+
+static int create_hid_dev_name(sdp_record_t *rec, struct hidp_connadd_req *req)
+{
+	char sdesc[sizeof(req->name)];
+
+	if (sdp_get_service_desc(rec, sdesc, sizeof(sdesc)) == 0) {
+		char pname[sizeof(req->name)];
+
+		if (sdp_get_provider_name(rec, pname, sizeof(pname)) == 0 &&
+						strncmp(sdesc, pname, 5) != 0)
+			snprintf(req->name, sizeof(req->name), "%s %s", pname,
+									sdesc);
+		else
+			snprintf(req->name, sizeof(req->name), "%s", sdesc);
+	} else {
+		return sdp_get_service_name(rec, req->name, sizeof(req->name));
+	}
+
+	return 0;
+}
+
+/* See HID profile specification v1.0, "7.11.6 HIDDescriptorList" for details
+ * on the attribute format. */
+static int extract_hid_desc_data(sdp_record_t *rec,
+						struct hidp_connadd_req *req)
+{
+	sdp_data_t *d;
+
+	d = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST);
+	if (!d)
+		goto invalid_desc;
+
+	if (!SDP_IS_SEQ(d->dtd))
+		goto invalid_desc;
+
+	/* First HIDDescriptor */
+	d = d->val.dataseq;
+	if (!SDP_IS_SEQ(d->dtd))
+		goto invalid_desc;
+
+	/* ClassDescriptorType */
+	d = d->val.dataseq;
+	if (d->dtd != SDP_UINT8)
+		goto invalid_desc;
+
+	/* ClassDescriptorData */
+	d = d->next;
+	if (!d || !SDP_IS_TEXT_STR(d->dtd))
+		goto invalid_desc;
+
+	req->rd_data = g_try_malloc0(d->unitSize);
+	if (req->rd_data) {
+		memcpy(req->rd_data, d->val.str, d->unitSize);
+		req->rd_size = d->unitSize;
+		epox_endian_quirk(req->rd_data, req->rd_size);
+	}
+
+	return 0;
+
+invalid_desc:
+	error("Missing or invalid HIDDescriptorList SDP attribute");
+	return -EINVAL;
+}
+
+static int extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req)
+{
+	sdp_data_t *pdlist;
+	uint8_t attr_val;
+	int err;
+
+	err = create_hid_dev_name(rec, req);
+	if (err < 0)
+		DBG("No valid Service Name or Service Description found");
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION);
+	req->parser = pdlist ? pdlist->val.uint16 : 0x0100;
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS);
+	req->subclass = pdlist ? pdlist->val.uint8 : 0;
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
+	req->country = pdlist ? pdlist->val.uint8 : 0;
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE);
+	attr_val = pdlist ? pdlist->val.uint8 : 0;
+	if (attr_val)
+		req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG);
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE);
+	attr_val = pdlist ? pdlist->val.uint8 : 0;
+	if (attr_val)
+		req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+
+	err = extract_hid_desc_data(rec, req);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int ioctl_connadd(struct hidp_connadd_req *req)
+{
+	int ctl, err = 0;
+
+	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+	if (ctl < 0)
+		return -errno;
+
+	if (ioctl(ctl, HIDPCONNADD, req) < 0)
+		err = -errno;
+
+	close(ctl);
+
+	return err;
+}
+
+static bool ioctl_is_connected(struct input_device *idev)
+{
+	struct hidp_conninfo ci;
+	int ctl;
+
+	/* Standard HID */
+	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+	if (ctl < 0) {
+		error("Can't open HIDP control socket");
+		return false;
+	}
+
+	memset(&ci, 0, sizeof(ci));
+	bacpy(&ci.bdaddr, &idev->dst);
+	if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) {
+		error("Can't get HIDP connection info");
+		close(ctl);
+		return false;
+	}
+
+	close(ctl);
+
+	if (ci.state != BT_CONNECTED)
+		return false;
+
+	return true;
+}
+
+static int ioctl_disconnect(struct input_device *idev, uint32_t flags)
+{
+	struct hidp_conndel_req req;
+	struct hidp_conninfo ci;
+	int ctl, err = 0;
+
+	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+	if (ctl < 0) {
+		error("Can't open HIDP control socket");
+		return -errno;
+	}
+
+	memset(&ci, 0, sizeof(ci));
+	bacpy(&ci.bdaddr, &idev->dst);
+	if ((ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) ||
+						(ci.state != BT_CONNECTED)) {
+		close(ctl);
+		return -ENOTCONN;
+	}
+
+	memset(&req, 0, sizeof(req));
+	bacpy(&req.bdaddr, &idev->dst);
+	req.flags = flags;
+	if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+		err = -errno;
+		error("Can't delete the HID device: %s (%d)",
+							strerror(-err), -err);
+	}
+
+	close(ctl);
+
+	return err;
+}
+
+static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
+{
+	int err;
+	struct uhid_event ev;
+
+	if (idev->uhid_created)
+		return 0;
+
+	/* create uHID device */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	strncpy((char *) ev.u.create.name, req->name,
+						sizeof(ev.u.create.name) - 1);
+	ba2str(&idev->src, (char *) ev.u.create.phys);
+	ba2str(&idev->dst, (char *) ev.u.create.uniq);
+	ev.u.create.vendor = req->vendor;
+	ev.u.create.product = req->product;
+	ev.u.create.version = req->version;
+	ev.u.create.country = req->country;
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.rd_data = req->rd_data;
+	ev.u.create.rd_size = req->rd_size;
+
+	err = bt_uhid_send(idev->uhid, &ev);
+	if (err < 0) {
+		error("bt_uhid_send: %s", strerror(-err));
+		return err;
+	}
+
+	bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_set_report, idev);
+	bt_uhid_register(idev->uhid, UHID_FEATURE, hidp_send_get_report, idev);
+
+	idev->uhid_created = true;
+
+	return err;
+}
+
+static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
+								gpointer data)
+{
+	struct input_device *idev = data;
+	int err;
+
+	DBG("");
+
+	if (idev->uhid)
+		err = uhid_connadd(idev, idev->req);
+	else
+		err = ioctl_connadd(idev->req);
+
+	if (err < 0) {
+		error("ioctl_connadd(): %s (%d)", strerror(-err), -err);
+
+		if (idev->ctrl_io) {
+			g_io_channel_shutdown(idev->ctrl_io, FALSE, NULL);
+			g_io_channel_unref(idev->ctrl_io);
+			idev->ctrl_io = NULL;
+		}
+
+		if (idev->intr_io) {
+			g_io_channel_shutdown(idev->intr_io, FALSE, NULL);
+			g_io_channel_unref(idev->intr_io);
+			idev->intr_io = NULL;
+		}
+	}
+
+	idev->sec_watch = 0;
+
+	g_free(idev->req->rd_data);
+	g_free(idev->req);
+	idev->req = NULL;
+
+	return FALSE;
+}
+
+static int hidp_add_connection(struct input_device *idev)
+{
+	struct hidp_connadd_req *req;
+	sdp_record_t *rec;
+	char src_addr[18], dst_addr[18];
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char handle[11], *str;
+	GError *gerr = NULL;
+	int err;
+
+	req = g_new0(struct hidp_connadd_req, 1);
+	req->ctrl_sock = g_io_channel_unix_get_fd(idev->ctrl_io);
+	req->intr_sock = g_io_channel_unix_get_fd(idev->intr_io);
+	req->flags     = 0;
+	req->idle_to   = idle_timeout;
+
+	ba2str(&idev->src, src_addr);
+	ba2str(&idev->dst, dst_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", src_addr,
+								dst_addr);
+	sprintf(handle, "0x%8.8X", idev->handle);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	str = g_key_file_get_string(key_file, "ServiceRecords", handle, NULL);
+	g_key_file_free(key_file);
+
+	if (!str) {
+		error("Rejected connection from unknown device %s", dst_addr);
+		err = -EPERM;
+		goto cleanup;
+	}
+
+	rec = record_from_string(str);
+	g_free(str);
+
+	err = extract_hid_record(rec, req);
+	sdp_record_free(rec);
+	if (err < 0) {
+		error("Could not parse HID SDP record: %s (%d)", strerror(-err),
+									-err);
+		goto cleanup;
+	}
+
+	req->vendor = btd_device_get_vendor(idev->device);
+	req->product = btd_device_get_product(idev->device);
+	req->version = btd_device_get_version(idev->device);
+
+	if (device_name_known(idev->device))
+		device_get_name(idev->device, req->name, sizeof(req->name));
+
+	/* Encryption is mandatory for keyboards */
+	if (req->subclass & 0x40) {
+		if (!bt_io_set(idev->intr_io, &gerr,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID)) {
+			error("btio: %s", gerr->message);
+			g_error_free(gerr);
+			err = -EFAULT;
+			goto cleanup;
+		}
+
+		idev->req = req;
+		idev->sec_watch = g_io_add_watch(idev->intr_io, G_IO_OUT,
+							encrypt_notify, idev);
+
+		return 0;
+	}
+
+	if (idev->uhid)
+		err = uhid_connadd(idev, req);
+	else
+		err = ioctl_connadd(req);
+
+cleanup:
+	g_free(req->rd_data);
+	g_free(req);
+
+	return err;
+}
+
+static bool is_connected(struct input_device *idev)
+{
+	if (idev->uhid)
+		return (idev->intr_io != NULL && idev->ctrl_io != NULL);
+	else
+		return ioctl_is_connected(idev);
+}
+
+static int connection_disconnect(struct input_device *idev, uint32_t flags)
+{
+	if (!is_connected(idev))
+		return -ENOTCONN;
+
+	/* Standard HID disconnect */
+	if (idev->intr_io)
+		g_io_channel_shutdown(idev->intr_io, TRUE, NULL);
+	if (idev->ctrl_io)
+		g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
+
+	if (idev->uhid)
+		return 0;
+	else
+		return ioctl_disconnect(idev, flags);
+}
+
+static int input_device_connected(struct input_device *idev)
+{
+	int err;
+
+	if (idev->intr_io == NULL || idev->ctrl_io == NULL)
+		return -ENOTCONN;
+
+	err = hidp_add_connection(idev);
+	if (err < 0)
+		return err;
+
+	btd_service_connecting_complete(idev->service, 0);
+
+	return 0;
+}
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	struct input_device *idev = user_data;
+	GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	int err;
+
+	if (conn_err) {
+		err = -EIO;
+		goto failed;
+	}
+
+	err = input_device_connected(idev);
+	if (err < 0)
+		goto failed;
+
+	if (idev->uhid)
+		cond |= G_IO_IN;
+
+	idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb,
+									idev);
+
+	return;
+
+failed:
+	btd_service_connecting_complete(idev->service, err);
+
+	/* So we guarantee the interrupt channel is closed before the
+	 * control channel (if we only do unref GLib will close it only
+	 * after returning control to the mainloop */
+	if (!conn_err)
+		g_io_channel_shutdown(idev->intr_io, FALSE, NULL);
+
+	g_io_channel_unref(idev->intr_io);
+	idev->intr_io = NULL;
+
+	if (idev->ctrl_io) {
+		g_io_channel_unref(idev->ctrl_io);
+		idev->ctrl_io = NULL;
+	}
+}
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	struct input_device *idev = user_data;
+	GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	GIOChannel *io;
+	GError *err = NULL;
+
+	if (conn_err) {
+		error("%s", conn_err->message);
+		goto failed;
+	}
+
+	/* Connect to the HID interrupt channel */
+	io = bt_io_connect(interrupt_connect_cb, idev,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+				BT_IO_OPT_DEST_BDADDR, &idev->dst,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	idev->intr_io = io;
+
+	if (idev->uhid)
+		cond |= G_IO_IN;
+
+	idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb,
+									idev);
+
+	return;
+
+failed:
+	btd_service_connecting_complete(idev->service, -EIO);
+	g_io_channel_unref(idev->ctrl_io);
+	idev->ctrl_io = NULL;
+}
+
+static int dev_connect(struct input_device *idev)
+{
+	GError *err = NULL;
+	GIOChannel *io;
+
+	if (idev->disable_sdp)
+		bt_clear_cached_session(&idev->src, &idev->dst);
+
+	io = bt_io_connect(control_connect_cb, idev,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+				BT_IO_OPT_DEST_BDADDR, &idev->dst,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	idev->ctrl_io = io;
+
+	if (err == NULL)
+		return 0;
+
+	error("%s", err->message);
+	g_error_free(err);
+
+	return -EIO;
+}
+
+static gboolean input_device_auto_reconnect(gpointer user_data)
+{
+	struct input_device *idev = user_data;
+
+	DBG("path=%s, attempt=%d", idev->path, idev->reconnect_attempt);
+
+	/* Stop the recurrent reconnection attempts if the device is
+	 * reconnected or is marked for removal.
+	 */
+	if (device_is_temporary(idev->device) ||
+					btd_device_is_connected(idev->device))
+		return FALSE;
+
+	/* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */
+	if (idev->reconnect_attempt >= 6)
+		return FALSE;
+
+	/* Check if the profile is already connected. */
+	if (idev->ctrl_io)
+		return FALSE;
+
+	if (is_connected(idev))
+		return FALSE;
+
+	idev->reconnect_attempt++;
+	dev_connect(idev);
+
+	return TRUE;
+}
+
+static const char * const _reconnect_mode_str[] = {
+	"none",
+	"device",
+	"host",
+	"any"
+};
+
+static const char *reconnect_mode_to_string(const enum reconnect_mode_t mode)
+{
+	return _reconnect_mode_str[mode];
+}
+
+static void input_device_enter_reconnect_mode(struct input_device *idev)
+{
+	DBG("path=%s reconnect_mode=%s", idev->path,
+				reconnect_mode_to_string(idev->reconnect_mode));
+
+	/* Only attempt an auto-reconnect when the device is required to
+	 * accept reconnections from the host.
+	 */
+	if (idev->reconnect_mode != RECONNECT_ANY &&
+				idev->reconnect_mode != RECONNECT_HOST)
+		return;
+
+	/* If the device is temporary we are not required to reconnect
+	 * with the device. This is likely the case of a removing device.
+	 */
+	if (device_is_temporary(idev->device) ||
+					btd_device_is_connected(idev->device))
+		return;
+
+	if (idev->reconnect_timer > 0)
+		g_source_remove(idev->reconnect_timer);
+
+	DBG("registering auto-reconnect");
+	idev->reconnect_attempt = 0;
+	idev->reconnect_timer = g_timeout_add_seconds(30,
+					input_device_auto_reconnect, idev);
+
+}
+
+int input_device_connect(struct btd_service *service)
+{
+	struct input_device *idev;
+
+	DBG("");
+
+	idev = btd_service_get_user_data(service);
+
+	if (idev->ctrl_io)
+		return -EBUSY;
+
+	if (is_connected(idev))
+		return -EALREADY;
+
+	return dev_connect(idev);
+}
+
+int input_device_disconnect(struct btd_service *service)
+{
+	struct input_device *idev;
+	int err, flags;
+
+	DBG("");
+
+	idev = btd_service_get_user_data(service);
+
+	flags = device_is_temporary(idev->device) ?
+					(1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
+
+	err = connection_disconnect(idev, flags);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static bool is_device_sdp_disable(const sdp_record_t *rec)
+{
+	sdp_data_t *data;
+
+	data = sdp_data_get(rec, SDP_ATTR_HID_SDP_DISABLE);
+
+	return data && data->val.uint8;
+}
+
+static enum reconnect_mode_t hid_reconnection_mode(bool reconnect_initiate,
+						bool normally_connectable)
+{
+	if (!reconnect_initiate && !normally_connectable)
+		return RECONNECT_NONE;
+	else if (!reconnect_initiate && normally_connectable)
+		return RECONNECT_HOST;
+	else if (reconnect_initiate && !normally_connectable)
+		return RECONNECT_DEVICE;
+	else /* (reconnect_initiate && normally_connectable) */
+		return RECONNECT_ANY;
+}
+
+static void extract_hid_props(struct input_device *idev,
+					const sdp_record_t *rec)
+{
+	/* Extract HID connectability */
+	bool reconnect_initiate, normally_connectable;
+	sdp_data_t *pdlist;
+
+	/* HIDNormallyConnectable is optional and assumed FALSE
+	* if not present. */
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_RECONNECT_INITIATE);
+	reconnect_initiate = pdlist ? pdlist->val.uint8 : TRUE;
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_NORMALLY_CONNECTABLE);
+	normally_connectable = pdlist ? pdlist->val.uint8 : FALSE;
+
+	/* Update local values */
+	idev->reconnect_mode =
+		hid_reconnection_mode(reconnect_initiate, normally_connectable);
+}
+
+static struct input_device *input_device_new(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct btd_profile *p = btd_service_get_profile(service);
+	const char *path = device_get_path(device);
+	const sdp_record_t *rec = btd_device_get_record(device, p->remote_uuid);
+	struct btd_adapter *adapter = device_get_adapter(device);
+	struct input_device *idev;
+
+	if (!rec)
+		return NULL;
+
+	idev = g_new0(struct input_device, 1);
+	bacpy(&idev->src, btd_adapter_get_address(adapter));
+	bacpy(&idev->dst, device_get_address(device));
+	idev->service = btd_service_ref(service);
+	idev->device = btd_device_ref(device);
+	idev->path = g_strdup(path);
+	idev->handle = rec->handle;
+	idev->disable_sdp = is_device_sdp_disable(rec);
+
+	/* Initialize device properties */
+	extract_hid_props(idev, rec);
+
+	return idev;
+}
+
+static gboolean property_get_reconnect_mode(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct input_device *idev = data;
+	const char *str_mode = reconnect_mode_to_string(idev->reconnect_mode);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str_mode);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable input_properties[] = {
+	{ "ReconnectMode", "s", property_get_reconnect_mode },
+	{ }
+};
+
+int input_device_register(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	const char *path = device_get_path(device);
+	struct input_device *idev;
+
+	DBG("%s", path);
+
+	idev = input_device_new(service);
+	if (!idev)
+		return -EINVAL;
+
+	if (uhid_enabled) {
+		idev->uhid = bt_uhid_new_default();
+		if (!idev->uhid) {
+			error("bt_uhid_new_default: failed");
+			input_device_free(idev);
+			return -EIO;
+		}
+	}
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(),
+					idev->path, INPUT_INTERFACE,
+					NULL, NULL,
+					input_properties, idev,
+					NULL) == FALSE) {
+		error("Unable to register %s interface", INPUT_INTERFACE);
+		input_device_free(idev);
+		return -EINVAL;
+	}
+
+	btd_service_set_user_data(service, idev);
+
+	return 0;
+}
+
+static struct input_device *find_device(const bdaddr_t *src,
+					const bdaddr_t *dst)
+{
+	struct btd_device *device;
+	struct btd_service *service;
+
+	device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
+	if (device == NULL)
+		return NULL;
+
+	service = btd_device_get_service(device, HID_UUID);
+	if (service == NULL)
+		return NULL;
+
+	return btd_service_get_user_data(service);
+}
+
+void input_device_unregister(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	const char *path = device_get_path(device);
+	struct input_device *idev = btd_service_get_user_data(service);
+
+	DBG("%s", path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						idev->path, INPUT_INTERFACE);
+
+	input_device_free(idev);
+}
+
+static int input_device_connadd(struct input_device *idev)
+{
+	int err;
+
+	err = input_device_connected(idev);
+	if (err == 0)
+		return 0;
+
+	if (idev->ctrl_io) {
+		g_io_channel_shutdown(idev->ctrl_io, FALSE, NULL);
+		g_io_channel_unref(idev->ctrl_io);
+		idev->ctrl_io = NULL;
+	}
+
+	if (idev->intr_io) {
+		g_io_channel_shutdown(idev->intr_io, FALSE, NULL);
+		g_io_channel_unref(idev->intr_io);
+		idev->intr_io = NULL;
+	}
+
+	return err;
+}
+
+bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	if (find_device(src, dst))
+		return true;
+
+	return false;
+}
+
+int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+								GIOChannel *io)
+{
+	struct input_device *idev = find_device(src, dst);
+	GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+
+	DBG("idev %p psm %d", idev, psm);
+
+	if (!idev)
+		return -ENOENT;
+
+	if (uhid_enabled)
+		cond |= G_IO_IN;
+
+	switch (psm) {
+	case L2CAP_PSM_HIDP_CTRL:
+		if (idev->ctrl_io)
+			return -EALREADY;
+		idev->ctrl_io = g_io_channel_ref(io);
+		idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond,
+							ctrl_watch_cb, idev);
+		break;
+	case L2CAP_PSM_HIDP_INTR:
+		if (idev->intr_io)
+			return -EALREADY;
+		idev->intr_io = g_io_channel_ref(io);
+		idev->intr_watch = g_io_add_watch(idev->intr_io, cond,
+							intr_watch_cb, idev);
+		break;
+	}
+
+	if (idev->intr_io && idev->ctrl_io)
+		input_device_connadd(idev);
+
+	return 0;
+}
+
+int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	struct input_device *idev = find_device(src, dst);
+
+	if (!idev)
+		return -ENOENT;
+
+	if (idev->intr_io)
+		g_io_channel_shutdown(idev->intr_io, TRUE, NULL);
+
+	if (idev->ctrl_io)
+		g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
+
+	return 0;
+}
diff --git a/bluez/repo/profiles/input/device.h b/bluez/repo/profiles/input/device.h
new file mode 100644
index 0000000..51a9aee
--- /dev/null
+++ b/bluez/repo/profiles/input/device.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+#define L2CAP_PSM_HIDP_CTRL	0x11
+#define L2CAP_PSM_HIDP_INTR	0x13
+
+struct input_device;
+struct input_conn;
+
+void input_set_idle_timeout(int timeout);
+void input_enable_userspace_hid(bool state);
+
+int input_device_register(struct btd_service *service);
+void input_device_unregister(struct btd_service *service);
+
+bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst);
+int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+							GIOChannel *io);
+int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst);
+
+int input_device_connect(struct btd_service *service);
+int input_device_disconnect(struct btd_service *service);
diff --git a/bluez/repo/profiles/input/hidp_defs.h b/bluez/repo/profiles/input/hidp_defs.h
new file mode 100644
index 0000000..5dc479a
--- /dev/null
+++ b/bluez/repo/profiles/input/hidp_defs.h
@@ -0,0 +1,79 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2014  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2014       Google 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
+ *
+ */
+
+#ifndef __HIDP_DEFS_H
+#define __HIDP_DEFS_H
+
+/* HIDP header masks */
+#define HIDP_HEADER_TRANS_MASK			0xf0
+#define HIDP_HEADER_PARAM_MASK			0x0f
+
+/* HIDP transaction types */
+#define HIDP_TRANS_HANDSHAKE			0x00
+#define HIDP_TRANS_HID_CONTROL			0x10
+#define HIDP_TRANS_GET_REPORT			0x40
+#define HIDP_TRANS_SET_REPORT			0x50
+#define HIDP_TRANS_GET_PROTOCOL			0x60
+#define HIDP_TRANS_SET_PROTOCOL			0x70
+#define HIDP_TRANS_GET_IDLE			0x80
+#define HIDP_TRANS_SET_IDLE			0x90
+#define HIDP_TRANS_DATA				0xa0
+#define HIDP_TRANS_DATC				0xb0
+
+/* HIDP handshake results */
+#define HIDP_HSHK_SUCCESSFUL			0x00
+#define HIDP_HSHK_NOT_READY			0x01
+#define HIDP_HSHK_ERR_INVALID_REPORT_ID		0x02
+#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST	0x03
+#define HIDP_HSHK_ERR_INVALID_PARAMETER		0x04
+#define HIDP_HSHK_ERR_UNKNOWN			0x0e
+#define HIDP_HSHK_ERR_FATAL			0x0f
+
+/* HIDP control operation parameters */
+#define HIDP_CTRL_NOP				0x00
+#define HIDP_CTRL_HARD_RESET			0x01
+#define HIDP_CTRL_SOFT_RESET			0x02
+#define HIDP_CTRL_SUSPEND			0x03
+#define HIDP_CTRL_EXIT_SUSPEND			0x04
+#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG		0x05
+
+/* HIDP data transaction headers */
+#define HIDP_DATA_RTYPE_MASK			0x03
+#define HIDP_DATA_RSRVD_MASK			0x0c
+#define HIDP_DATA_RTYPE_OTHER			0x00
+#define HIDP_DATA_RTYPE_INPUT			0x01
+#define HIDP_DATA_RTYPE_OUPUT			0x02
+#define HIDP_DATA_RTYPE_FEATURE			0x03
+
+/* HIDP protocol header parameters */
+#define HIDP_PROTO_BOOT				0x00
+#define HIDP_PROTO_REPORT			0x01
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG		0
+#define HIDP_BOOT_PROTOCOL_MODE			1
+#define HIDP_BLUETOOTH_VENDOR_ID		9
+#define HIDP_WAITING_FOR_RETURN			10
+#define HIDP_WAITING_FOR_SEND_ACK		11
+
+#endif /* __HIDP_DEFS_H */
diff --git a/bluez/repo/profiles/input/hog-lib.c b/bluez/repo/profiles/input/hog-lib.c
new file mode 100644
index 0000000..1df1799
--- /dev/null
+++ b/bluez/repo/profiles/input/hog-lib.c
@@ -0,0 +1,1550 @@
+/*
+ *
+ *  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 "profiles/scanparam/scpp.h"
+#include "profiles/deviceinfo/dis.h"
+#include "profiles/battery/bas.h"
+#include "profiles/input/hog-lib.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;
+	bool			uhid_created;
+	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 const char *type_to_string(uint8_t type)
+{
+	switch (type) {
+	case HOG_REPORT_TYPE_INPUT:
+		return "input";
+	case HOG_REPORT_TYPE_OUTPUT:
+		return "output";
+	case HOG_REPORT_TYPE_FEATURE:
+		return "feature";
+	}
+
+	return NULL;
+}
+
+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 0x%04x: id 0x%02x type %s", report->decl->value_handle,
+				report->id, type_to_string(report->type));
+
+	/* 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 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;
+}
+
+static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
+{
+	struct report *report;
+	GSList *l;
+
+	/* Skip if report already exists */
+	l = g_slist_find_custom(hog->reports, chr, report_chrc_cmp);
+	if (l)
+		return l->data;
+
+	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);
+
+	DBG("HoG inspecting report map");
+
+	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);
+
+	hog->uhid_created = true;
+
+	DBG("HoG created uHID device");
+}
+
+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);
+
+	DBG("HoG inspecting characteristics");
+
+	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) {
+			DBG("HoG discovering report map");
+			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);
+
+	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->uhid_created) {
+		DBG("HoG discovering characteristics");
+		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/bluez/repo/profiles/input/hog-lib.h b/bluez/repo/profiles/input/hog-lib.h
new file mode 100644
index 0000000..2a9b899
--- /dev/null
+++ b/bluez/repo/profiles/input/hog-lib.h
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 bt_hog;
+
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+					uint16_t product, uint16_t version,
+					void *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 *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);
diff --git a/bluez/repo/profiles/input/hog.c b/bluez/repo/profiles/input/hog.c
new file mode 100644
index 0000000..4dba83f
--- /dev/null
+++ b/bluez/repo/profiles/input/hog.c
@@ -0,0 +1,240 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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/log.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/uhid.h"
+#include "src/shared/queue.h"
+#include "src/plugin.h"
+
+#include "suspend.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/gatt.h"
+#include "hog-lib.h"
+
+#define HOG_UUID		"00001812-0000-1000-8000-00805f9b34fb"
+
+struct hog_device {
+	guint			attioid;
+	struct btd_device	*device;
+	struct bt_hog		*hog;
+};
+
+static gboolean suspend_supported = FALSE;
+static struct queue *devices = NULL;
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct hog_device *dev = user_data;
+
+	DBG("HoG connected");
+
+	bt_hog_attach(dev->hog, attrib);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct hog_device *dev = user_data;
+
+	DBG("HoG disconnected");
+
+	bt_hog_detach(dev->hog);
+}
+
+static struct hog_device *hog_device_new(struct btd_device *device,
+						struct gatt_primary *prim)
+{
+	struct hog_device *dev;
+	char name[248];
+	uint16_t vendor, product, version;
+
+	if (device_name_known(device))
+		device_get_name(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);
+
+	DBG("name=%s vendor=0x%X, product=0x%X, version=0x%X", name, vendor,
+							product, version);
+
+	dev = new0(struct hog_device, 1);
+	dev->device = btd_device_ref(device);
+	dev->hog = bt_hog_new_default(name, vendor, product, version, prim);
+
+	/*
+	 * TODO: Remove attio callback and use .accept once using
+	 * bt_gatt_client.
+	 */
+	dev->attioid = btd_device_add_attio_callback(device,
+							attio_connected_cb,
+							attio_disconnected_cb,
+							dev);
+
+	if (!devices)
+		devices = queue_new();
+
+	queue_push_tail(devices, dev);
+
+	return dev;
+}
+
+static void hog_device_free(void *data)
+{
+	struct hog_device *dev = data;
+
+	queue_remove(devices, dev);
+	if (queue_isempty(devices)) {
+		queue_destroy(devices, NULL);
+		devices = NULL;
+	}
+
+	btd_device_remove_attio_callback(dev->device, dev->attioid);
+	btd_device_unref(dev->device);
+	bt_hog_unref(dev->hog);
+	free(dev);
+}
+
+static void set_suspend(gpointer data, gpointer user_data)
+{
+	struct hog_device *dev = data;
+	gboolean suspend = GPOINTER_TO_INT(user_data);
+
+	bt_hog_set_control_point(dev->hog, suspend);
+}
+
+static void suspend_callback(void)
+{
+	gboolean suspend = TRUE;
+
+	DBG("Suspending ...");
+
+	queue_foreach(devices, set_suspend, GINT_TO_POINTER(suspend));
+}
+
+static void resume_callback(void)
+{
+	gboolean suspend = FALSE;
+
+	DBG("Resuming ...");
+
+	queue_foreach(devices, set_suspend, GINT_TO_POINTER(suspend));
+}
+
+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;
+
+	DBG("path %s", path);
+
+	primaries = btd_device_get_primaries(device);
+	if (primaries == NULL)
+		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);
+		btd_service_set_user_data(service, dev);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static void hog_remove(struct btd_service *service)
+{
+	struct hog_device *dev = btd_service_get_user_data(service);
+	struct btd_device *device = btd_service_get_device(service);
+	const char *path = device_get_path(device);
+
+	DBG("path %s", path);
+
+	hog_device_free(dev);
+}
+
+static struct btd_profile hog_profile = {
+	.name		= "input-hog",
+	.remote_uuid	= HOG_UUID,
+	.device_probe	= hog_probe,
+	.device_remove	= hog_remove,
+};
+
+static int hog_init(void)
+{
+	int err;
+
+	err = suspend_init(suspend_callback, resume_callback);
+	if (err < 0)
+		error("Loading suspend plugin failed: %s (%d)", strerror(-err),
+									-err);
+	else
+		suspend_supported = TRUE;
+
+	return btd_profile_register(&hog_profile);
+}
+
+static void hog_exit(void)
+{
+	if (suspend_supported)
+		suspend_exit();
+
+	btd_profile_unregister(&hog_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hog, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+							hog_init, hog_exit)
diff --git a/bluez/repo/profiles/input/input.conf b/bluez/repo/profiles/input/input.conf
new file mode 100644
index 0000000..3e1d65a
--- /dev/null
+++ b/bluez/repo/profiles/input/input.conf
@@ -0,0 +1,13 @@
+# Configuration file for the input service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Set idle timeout (in minutes) before the connection will
+# be disconnect (defaults to 0 for no timeout)
+#IdleTimeout=30
+
+# Enable HID protocol handling in userspace input profile
+# Defaults to false (HIDP handled in HIDP kernel module)
+#UserspaceHID=true
diff --git a/bluez/repo/profiles/input/manager.c b/bluez/repo/profiles/input/manager.c
new file mode 100644
index 0000000..1d31b06
--- /dev/null
+++ b/bluez/repo/profiles/input/manager.c
@@ -0,0 +1,133 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <errno.h>
+#include <stdbool.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "src/log.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+
+#include "device.h"
+#include "server.h"
+
+static int hid_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	return server_start(btd_adapter_get_address(adapter));
+}
+
+static void hid_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	server_stop(btd_adapter_get_address(adapter));
+}
+
+static struct btd_profile input_profile = {
+	.name		= "input-hid",
+	.local_uuid	= HID_UUID,
+	.remote_uuid	= HID_UUID,
+
+	.auto_connect	= true,
+	.connect	= input_device_connect,
+	.disconnect	= input_device_disconnect,
+
+	.device_probe	= input_device_register,
+	.device_remove	= input_device_unregister,
+
+	.adapter_probe	= hid_server_probe,
+	.adapter_remove = hid_server_remove,
+};
+
+static GKeyFile *load_config_file(const char *file)
+{
+	GKeyFile *keyfile;
+	GError *err = NULL;
+
+	keyfile = g_key_file_new();
+
+	if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+		if (!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			error("Parsing %s failed: %s", file, err->message);
+		g_error_free(err);
+		g_key_file_free(keyfile);
+		return NULL;
+	}
+
+	return keyfile;
+}
+
+static int input_init(void)
+{
+	GKeyFile *config;
+	GError *err = NULL;
+
+	config = load_config_file(CONFIGDIR "/input.conf");
+	if (config) {
+		int idle_timeout;
+		gboolean uhid_enabled;
+
+		idle_timeout = g_key_file_get_integer(config, "General",
+							"IdleTimeout", &err);
+		if (!err) {
+			DBG("input.conf: IdleTimeout=%d", idle_timeout);
+			input_set_idle_timeout(idle_timeout * 60);
+		} else
+			g_clear_error(&err);
+
+		uhid_enabled = g_key_file_get_boolean(config, "General",
+							"UserspaceHID", &err);
+		if (!err) {
+			DBG("input.conf: UserspaceHID=%s", uhid_enabled ?
+							"true" : "false");
+			input_enable_userspace_hid(uhid_enabled);
+		} else
+			g_clear_error(&err);
+	}
+
+	btd_profile_register(&input_profile);
+
+	if (config)
+		g_key_file_free(config);
+
+	return 0;
+}
+
+static void input_exit(void)
+{
+	btd_profile_unregister(&input_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+							input_init, input_exit)
diff --git a/bluez/repo/profiles/input/server.c b/bluez/repo/profiles/input/server.c
new file mode 100644
index 0000000..eb3fcf8
--- /dev/null
+++ b/bluez/repo/profiles/input/server.c
@@ -0,0 +1,346 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <unistd.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/log.h"
+#include "src/uuid-helper.h"
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+
+#include "device.h"
+#include "server.h"
+
+struct confirm_data {
+	bdaddr_t dst;
+	GIOChannel *io;
+};
+
+static GSList *servers = NULL;
+struct input_server {
+	bdaddr_t src;
+	GIOChannel *ctrl;
+	GIOChannel *intr;
+	struct confirm_data *confirm;
+};
+
+static int server_cmp(gconstpointer s, gconstpointer user_data)
+{
+	const struct input_server *server = s;
+	const bdaddr_t *src = user_data;
+
+	return bacmp(&server->src, src);
+}
+
+struct sixaxis_data {
+	GIOChannel *chan;
+	uint16_t psm;
+};
+
+static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data)
+{
+	struct sixaxis_data *data = user_data;
+	const bdaddr_t *src;
+
+	DBG("err %d (%s)", err, strerror(-err));
+
+	if (err < 0)
+		goto fail;
+
+	src = btd_adapter_get_address(device_get_adapter(dev));
+
+	if (input_device_set_channel(src, device_get_address(dev), data->psm,
+								data->chan) < 0)
+		goto fail;
+
+	g_io_channel_unref(data->chan);
+	g_free(data);
+
+	return;
+
+fail:
+	g_io_channel_shutdown(data->chan, TRUE, NULL);
+	g_io_channel_unref(data->chan);
+	g_free(data);
+}
+
+static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst,
+						GIOChannel *chan, uint16_t psm)
+{
+	struct btd_device *device;
+	struct sixaxis_data *data;
+
+	device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
+	if (!device)
+		return;
+
+	data = g_new0(struct sixaxis_data, 1);
+	data->chan = g_io_channel_ref(chan);
+	data->psm = psm;
+
+	if (psm == L2CAP_PSM_HIDP_CTRL)
+		device_discover_services(device);
+
+	device_wait_for_svc_complete(device, sixaxis_sdp_cb, data);
+}
+
+static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	struct btd_device *device;
+	uint16_t vid, pid;
+
+	device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
+	if (!device)
+		return false;
+
+	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)
+		return true;
+
+	return false;
+}
+
+static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	uint16_t psm;
+	bdaddr_t src, dst;
+	char address[18];
+	GError *gerr = NULL;
+	int ret;
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_PSM, &psm,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	ba2str(&dst, address);
+	DBG("Incoming connection from %s on PSM %d", address, psm);
+
+	ret = input_device_set_channel(&src, &dst, psm, chan);
+	if (ret == 0)
+		return;
+
+	if (ret == -ENOENT && dev_is_sixaxis(&src, &dst)) {
+		sixaxis_browse_sdp(&src, &dst, chan, psm);
+		return;
+	}
+
+	error("Refusing input device connect: %s (%d)", strerror(-ret), -ret);
+
+	/* Send unplug virtual cable to unknown devices */
+	if (ret == -ENOENT && psm == L2CAP_PSM_HIDP_CTRL) {
+		unsigned char unplug = 0x15;
+		int sk = g_io_channel_unix_get_fd(chan);
+		if (write(sk, &unplug, sizeof(unplug)) < 0)
+			error("Unable to send virtual cable unplug");
+	}
+
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void auth_callback(DBusError *derr, void *user_data)
+{
+	struct input_server *server = user_data;
+	struct confirm_data *confirm = server->confirm;
+	GError *err = NULL;
+
+	if (derr) {
+		error("Access denied: %s", derr->message);
+		goto reject;
+	}
+
+	if (!input_device_exists(&server->src, &confirm->dst) &&
+				!dev_is_sixaxis(&server->src, &confirm->dst))
+		return;
+
+	if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		goto reject;
+	}
+
+	g_io_channel_unref(confirm->io);
+	g_free(server->confirm);
+	server->confirm = NULL;
+
+	return;
+
+reject:
+	g_io_channel_shutdown(confirm->io, TRUE, NULL);
+	g_io_channel_unref(confirm->io);
+	server->confirm = NULL;
+	input_device_close_channels(&server->src, &confirm->dst);
+	g_free(confirm);
+}
+
+static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
+{
+	struct input_server *server = user_data;
+	bdaddr_t src, dst;
+	GError *err = NULL;
+	char addr[18];
+	guint ret;
+
+	DBG("");
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	ba2str(&dst, addr);
+
+	if (server->confirm) {
+		error("Refusing connection from %s: setup in progress", addr);
+		goto drop;
+	}
+
+	if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) {
+		error("Refusing connection from %s: unknown device", addr);
+		goto drop;
+	}
+
+	server->confirm = g_new0(struct confirm_data, 1);
+	server->confirm->io = g_io_channel_ref(chan);
+	bacpy(&server->confirm->dst, &dst);
+
+	ret = btd_request_authorization(&src, &dst, HID_UUID,
+					auth_callback, server);
+	if (ret != 0)
+		return;
+
+	error("input: authorization for device %s failed", addr);
+
+	g_io_channel_unref(server->confirm->io);
+	g_free(server->confirm);
+	server->confirm = NULL;
+
+drop:
+	input_device_close_channels(&src, &dst);
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+int server_start(const bdaddr_t *src)
+{
+	struct input_server *server;
+	GError *err = NULL;
+
+	server = g_new0(struct input_server, 1);
+	bacpy(&server->src, src);
+
+	server->ctrl = bt_io_listen(connect_event_cb, NULL,
+				server, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!server->ctrl) {
+		error("Failed to listen on control channel");
+		g_error_free(err);
+		g_free(server);
+		return -1;
+	}
+
+	server->intr = bt_io_listen(NULL, confirm_event_cb,
+				server, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!server->intr) {
+		error("Failed to listen on interrupt channel");
+		g_io_channel_unref(server->ctrl);
+		g_error_free(err);
+		g_free(server);
+		return -1;
+	}
+
+	servers = g_slist_append(servers, server);
+
+	return 0;
+}
+
+void server_stop(const bdaddr_t *src)
+{
+	struct input_server *server;
+	GSList *l;
+
+	l = g_slist_find_custom(servers, src, server_cmp);
+	if (!l)
+		return;
+
+	server = l->data;
+
+	g_io_channel_shutdown(server->intr, TRUE, NULL);
+	g_io_channel_unref(server->intr);
+
+	g_io_channel_shutdown(server->ctrl, TRUE, NULL);
+	g_io_channel_unref(server->ctrl);
+
+	servers = g_slist_remove(servers, server);
+	g_free(server);
+}
diff --git a/bluez/repo/profiles/input/server.h b/bluez/repo/profiles/input/server.h
new file mode 100644
index 0000000..74159bb
--- /dev/null
+++ b/bluez/repo/profiles/input/server.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+int server_start(const bdaddr_t *src);
+void server_stop(const bdaddr_t *src);
diff --git a/bluez/repo/profiles/input/suspend-dummy.c b/bluez/repo/profiles/input/suspend-dummy.c
new file mode 100644
index 0000000..542ae25
--- /dev/null
+++ b/bluez/repo/profiles/input/suspend-dummy.c
@@ -0,0 +1,162 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "suspend.h"
+
+#define HOG_SUSPEND_FIFO	"/tmp/hogsuspend"
+
+static suspend_event suspend_cb = NULL;
+static resume_event resume_cb = NULL;
+static guint watch = 0;
+
+static int fifo_open(void);
+
+static gboolean read_fifo(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+	char buffer[12];
+	gsize offset, left, bread;
+	GIOStatus iostatus;
+
+	if (cond & (G_IO_ERR | G_IO_HUP)) {
+		/*
+		 * Both ends needs to be open simultaneously before proceeding
+		 * any input or output operation. When the remote closes the
+		 * channel, hup signal is received on this end.
+		 */
+		fifo_open();
+		return FALSE;
+	}
+
+	offset = 0;
+	left = sizeof(buffer) - 1;
+	memset(buffer, 0, sizeof(buffer));
+
+	do {
+		iostatus = g_io_channel_read_chars(io, &buffer[offset], left,
+								&bread, NULL);
+
+		offset += bread;
+		left -= bread;
+		if (left == 0)
+			break;
+	} while (iostatus == G_IO_STATUS_NORMAL);
+
+	if (g_ascii_strncasecmp("suspend", buffer, 7) == 0)
+		suspend_cb();
+	else if (g_ascii_strncasecmp("resume", buffer, 6) == 0)
+		resume_cb();
+
+	return TRUE;
+}
+
+static int fifo_open(void)
+{
+	GIOCondition condition = G_IO_IN | G_IO_ERR | G_IO_HUP;
+	GIOChannel *fifoio;
+	int fd;
+
+	fd = open(HOG_SUSPEND_FIFO, O_RDONLY | O_NONBLOCK);
+	if (fd < 0) {
+		int err = -errno;
+		error("Can't open FIFO (%s): %s(%d)", HOG_SUSPEND_FIFO,
+							strerror(-err), -err);
+		return err;
+	}
+
+	fifoio = g_io_channel_unix_new(fd);
+	g_io_channel_set_close_on_unref(fifoio, TRUE);
+
+	watch = g_io_add_watch(fifoio, condition, read_fifo, NULL);
+
+	g_io_channel_unref(fifoio);
+
+	return 0;
+}
+
+int suspend_init(suspend_event suspend, resume_event resume)
+{
+	struct stat st;
+	int ret;
+
+	DBG("");
+
+	suspend_cb = suspend;
+	resume_cb = resume;
+
+	if (stat(HOG_SUSPEND_FIFO, &st) == 0) {
+		if (!S_ISFIFO(st.st_mode)) {
+			error("Unexpected non-FIFO %s file", HOG_SUSPEND_FIFO);
+			return -EIO;
+		}
+
+		if (unlink(HOG_SUSPEND_FIFO) < 0) {
+			int err = -errno;
+			error("Failed to remove FIFO (%s): %s (%d)",
+				HOG_SUSPEND_FIFO, strerror(-err), -err);
+			return err;
+		}
+	}
+
+	if (mkfifo(HOG_SUSPEND_FIFO, S_IRUSR | S_IWUSR) < 0) {
+		int err = -errno;
+
+		error("Can't create FIFO (%s): %s (%d)", HOG_SUSPEND_FIFO,
+							strerror(-err), -err);
+		return err;
+	}
+
+	DBG("Created suspend-dummy FIFO on %s", HOG_SUSPEND_FIFO);
+
+	ret = fifo_open();
+	if (ret < 0)
+		unlink(HOG_SUSPEND_FIFO);
+
+	return ret;
+}
+
+void suspend_exit(void)
+{
+	if (watch > 0) {
+		g_source_remove(watch);
+		watch = 0;
+	}
+
+	unlink(HOG_SUSPEND_FIFO);
+}
diff --git a/bluez/repo/profiles/input/suspend-none.c b/bluez/repo/profiles/input/suspend-none.c
new file mode 100644
index 0000000..c619bb1
--- /dev/null
+++ b/bluez/repo/profiles/input/suspend-none.c
@@ -0,0 +1,42 @@
+/*
+ *
+ *  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 "src/log.h"
+#include "suspend.h"
+
+int suspend_init(suspend_event suspend, resume_event resume)
+{
+	DBG("");
+
+	return 0;
+}
+
+void suspend_exit(void)
+{
+	DBG("");
+}
diff --git a/bluez/repo/profiles/input/suspend.h b/bluez/repo/profiles/input/suspend.h
new file mode 100644
index 0000000..bfee3cf
--- /dev/null
+++ b/bluez/repo/profiles/input/suspend.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  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
+ *
+ */
+
+typedef void (*suspend_event) (void);
+typedef void (*resume_event) (void);
+
+int suspend_init(suspend_event suspend, resume_event resume);
+void suspend_exit(void);
diff --git a/bluez/repo/profiles/input/uhid_copy.h b/bluez/repo/profiles/input/uhid_copy.h
new file mode 100644
index 0000000..0ef73d4
--- /dev/null
+++ b/bluez/repo/profiles/input/uhid_copy.h
@@ -0,0 +1,199 @@
+#ifndef __UHID_H_
+#define __UHID_H_
+
+/*
+ * User-space I/O driver support for HID subsystem
+ * Copyright (c) 2012 David Herrmann
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * Public header for user-space communication. We try to keep every structure
+ * aligned but to be safe we also use __attribute__((__packed__)). Therefore,
+ * the communication should be ABI compatible even between architectures.
+ */
+
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/hid.h>
+
+enum uhid_event_type {
+	__UHID_LEGACY_CREATE,
+	UHID_DESTROY,
+	UHID_START,
+	UHID_STOP,
+	UHID_OPEN,
+	UHID_CLOSE,
+	UHID_OUTPUT,
+	__UHID_LEGACY_OUTPUT_EV,
+	__UHID_LEGACY_INPUT,
+	UHID_GET_REPORT,
+	UHID_GET_REPORT_REPLY,
+	UHID_CREATE2,
+	UHID_INPUT2,
+	UHID_SET_REPORT,
+	UHID_SET_REPORT_REPLY,
+};
+
+struct uhid_create2_req {
+	__u8 name[128];
+	__u8 phys[64];
+	__u8 uniq[64];
+	__u16 rd_size;
+	__u16 bus;
+	__u32 vendor;
+	__u32 product;
+	__u32 version;
+	__u32 country;
+	__u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+enum uhid_dev_flag {
+	UHID_DEV_NUMBERED_FEATURE_REPORTS			= (1ULL << 0),
+	UHID_DEV_NUMBERED_OUTPUT_REPORTS			= (1ULL << 1),
+	UHID_DEV_NUMBERED_INPUT_REPORTS				= (1ULL << 2),
+};
+
+struct uhid_start_req {
+	__u64 dev_flags;
+};
+
+#define UHID_DATA_MAX 4096
+
+enum uhid_report_type {
+	UHID_FEATURE_REPORT,
+	UHID_OUTPUT_REPORT,
+	UHID_INPUT_REPORT,
+};
+
+struct uhid_input2_req {
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+struct uhid_output_req {
+	__u8 data[UHID_DATA_MAX];
+	__u16 size;
+	__u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_get_report_req {
+	__u32 id;
+	__u8 rnum;
+	__u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_get_report_reply_req {
+	__u32 id;
+	__u16 err;
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+struct uhid_set_report_req {
+	__u32 id;
+	__u8 rnum;
+	__u8 rtype;
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+struct uhid_set_report_reply_req {
+	__u32 id;
+	__u16 err;
+} __attribute__((__packed__));
+
+/*
+ * Compat Layer
+ * All these commands and requests are obsolete. You should avoid using them in
+ * new code. We support them for backwards-compatibility, but you might not get
+ * access to new feature in case you use them.
+ */
+
+enum uhid_legacy_event_type {
+	UHID_CREATE			= __UHID_LEGACY_CREATE,
+	UHID_OUTPUT_EV			= __UHID_LEGACY_OUTPUT_EV,
+	UHID_INPUT			= __UHID_LEGACY_INPUT,
+	UHID_FEATURE			= UHID_GET_REPORT,
+	UHID_FEATURE_ANSWER		= UHID_GET_REPORT_REPLY,
+};
+
+/* Obsolete! Use UHID_CREATE2. */
+struct uhid_create_req {
+	__u8 name[128];
+	__u8 phys[64];
+	__u8 uniq[64];
+	__u8 *rd_data;
+	__u16 rd_size;
+
+	__u16 bus;
+	__u32 vendor;
+	__u32 product;
+	__u32 version;
+	__u32 country;
+} __attribute__((__packed__));
+
+/* Obsolete! Use UHID_INPUT2. */
+struct uhid_input_req {
+	__u8 data[UHID_DATA_MAX];
+	__u16 size;
+} __attribute__((__packed__));
+
+/* Obsolete! Kernel uses UHID_OUTPUT exclusively now. */
+struct uhid_output_ev_req {
+	__u16 type;
+	__u16 code;
+	__s32 value;
+} __attribute__((__packed__));
+
+/* Obsolete! Kernel uses ABI compatible UHID_GET_REPORT. */
+struct uhid_feature_req {
+	__u32 id;
+	__u8 rnum;
+	__u8 rtype;
+} __attribute__((__packed__));
+
+/* Obsolete! Use ABI compatible UHID_GET_REPORT_REPLY. */
+struct uhid_feature_answer_req {
+	__u32 id;
+	__u16 err;
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+/*
+ * UHID Events
+ * All UHID events from and to the kernel are encoded as "struct uhid_event".
+ * The "type" field contains a UHID_* type identifier. All payload depends on
+ * that type and can be accessed via ev->u.XYZ accordingly.
+ * If user-space writes short events, they're extended with 0s by the kernel. If
+ * the kernel writes short events, user-space shall extend them with 0s.
+ */
+
+struct uhid_event {
+	__u32 type;
+
+	union {
+		struct uhid_create_req create;
+		struct uhid_input_req input;
+		struct uhid_output_req output;
+		struct uhid_output_ev_req output_ev;
+		struct uhid_feature_req feature;
+		struct uhid_get_report_req get_report;
+		struct uhid_feature_answer_req feature_answer;
+		struct uhid_get_report_reply_req get_report_reply;
+		struct uhid_create2_req create2;
+		struct uhid_input2_req input2;
+		struct uhid_set_report_req set_report;
+		struct uhid_set_report_reply_req set_report_reply;
+		struct uhid_start_req start;
+	} u;
+} __attribute__((__packed__));
+
+#endif /* __UHID_H_ */
diff --git a/bluez/repo/profiles/network/._bnep.c b/bluez/repo/profiles/network/._bnep.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._bnep.c
Binary files differ
diff --git a/bluez/repo/profiles/network/._bnep.h b/bluez/repo/profiles/network/._bnep.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._bnep.h
Binary files differ
diff --git a/bluez/repo/profiles/network/._connection.c b/bluez/repo/profiles/network/._connection.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._connection.c
Binary files differ
diff --git a/bluez/repo/profiles/network/._connection.h b/bluez/repo/profiles/network/._connection.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._connection.h
Binary files differ
diff --git a/bluez/repo/profiles/network/._manager.c b/bluez/repo/profiles/network/._manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._manager.c
Binary files differ
diff --git a/bluez/repo/profiles/network/._network.conf b/bluez/repo/profiles/network/._network.conf
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._network.conf
Binary files differ
diff --git a/bluez/repo/profiles/network/._server.c b/bluez/repo/profiles/network/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._server.c
Binary files differ
diff --git a/bluez/repo/profiles/network/._server.h b/bluez/repo/profiles/network/._server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/network/._server.h
Binary files differ
diff --git a/bluez/repo/profiles/network/bnep.c b/bluez/repo/profiles/network/bnep.c
new file mode 100644
index 0000000..9bf0b18
--- /dev/null
+++ b/bluez/repo/profiles/network/bnep.c
@@ -0,0 +1,736 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/bnep.h"
+#include "lib/uuid.h"
+
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+
+#include "bnep.h"
+
+#define CON_SETUP_RETRIES      3
+#define CON_SETUP_TO           9
+
+static int ctl;
+
+struct __service_16 {
+	uint16_t dst;
+	uint16_t src;
+} __attribute__ ((packed));
+
+struct bnep {
+	GIOChannel	*io;
+	uint16_t	src;
+	uint16_t	dst;
+	bdaddr_t	dst_addr;
+	char	iface[16];
+	guint	attempts;
+	guint	setup_to;
+	guint	watch;
+	bnep_connect_cb	conn_cb;
+	void	*conn_data;
+	bnep_disconnect_cb disconn_cb;
+	void	*disconn_data;
+};
+
+int bnep_init(void)
+{
+	ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+	if (ctl < 0) {
+		int err = -errno;
+
+		if (err == -EPROTONOSUPPORT)
+			warn("kernel lacks bnep-protocol support");
+		else
+			error("bnep: Failed to open control socket: %s (%d)",
+							strerror(-err), -err);
+
+		return err;
+	}
+
+	return 0;
+}
+
+int bnep_cleanup(void)
+{
+	close(ctl);
+	return 0;
+}
+
+static int bnep_conndel(const bdaddr_t *dst)
+{
+	struct bnep_conndel_req req;
+
+	memset(&req, 0, sizeof(req));
+	baswap((bdaddr_t *)&req.dst, dst);
+	req.flags = 0;
+	if (ioctl(ctl, BNEPCONNDEL, &req) < 0) {
+		int err = -errno;
+		error("bnep: Failed to kill connection: %s (%d)",
+							strerror(-err), -err);
+		return err;
+	}
+	return 0;
+}
+
+static int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+	struct bnep_connadd_req req;
+
+	memset(&req, 0, sizeof(req));
+	strncpy(req.device, dev, 16);
+	req.device[15] = '\0';
+
+	req.sock = sk;
+	req.role = role;
+	req.flags = (1 << BNEP_SETUP_RESPONSE);
+	if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
+		int err = -errno;
+		error("bnep: Failed to add device %s: %s(%d)",
+						dev, strerror(-err), -err);
+		return err;
+	}
+
+	strncpy(dev, req.device, 16);
+	return 0;
+}
+
+static uint32_t bnep_getsuppfeat(void)
+{
+	uint32_t feat;
+
+	if (ioctl(ctl, BNEPGETSUPPFEAT, &feat) < 0)
+		feat = 0;
+
+	DBG("supported features: 0x%x", feat);
+
+	return feat;
+}
+
+static int bnep_if_up(const char *devname)
+{
+	struct ifreq ifr;
+	int sk, err = 0;
+
+	sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
+
+	ifr.ifr_flags |= IFF_UP;
+	ifr.ifr_flags |= IFF_MULTICAST;
+
+	if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) < 0) {
+		err = -errno;
+		error("bnep: Could not bring up %s: %s(%d)",
+						devname, strerror(-err), -err);
+	}
+
+	close(sk);
+
+	return err;
+}
+
+static int bnep_if_down(const char *devname)
+{
+	struct ifreq ifr;
+	int sk, err = 0;
+
+	sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
+
+	ifr.ifr_flags &= ~IFF_UP;
+
+	/* Bring down the interface */
+	if (ioctl(sk, SIOCSIFFLAGS, (void *) &ifr) < 0) {
+		err = -errno;
+		error("bnep: Could not bring down %s: %s(%d)",
+						devname, strerror(-err), -err);
+	}
+
+	close(sk);
+
+	return err;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	struct bnep *session = data;
+
+	if (session->disconn_cb)
+		session->disconn_cb(session->disconn_data);
+
+	return FALSE;
+}
+
+static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
+								gpointer data)
+{
+	struct bnep *session = data;
+	struct bnep_control_rsp *rsp;
+	struct timeval timeo;
+	char pkt[BNEP_MTU];
+	ssize_t r;
+	int sk;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (session->setup_to > 0) {
+		g_source_remove(session->setup_to);
+		session->setup_to = 0;
+	}
+
+	if (cond & (G_IO_HUP | G_IO_ERR)) {
+		error("bnep: Hangup or error on l2cap server socket");
+		goto failed;
+	}
+
+	sk = g_io_channel_unix_get_fd(chan);
+	memset(pkt, 0, BNEP_MTU);
+	r = read(sk, pkt, sizeof(pkt) - 1);
+	if (r < 0) {
+		error("bnep: IO Channel read error");
+		goto failed;
+	}
+
+	if (r == 0) {
+		error("bnep: No packet received on l2cap socket");
+		goto failed;
+	}
+
+	errno = EPROTO;
+
+	if ((size_t) r < sizeof(*rsp)) {
+		error("bnep: Packet received is not bnep type");
+		goto failed;
+	}
+
+	rsp = (void *) pkt;
+	if (rsp->type != BNEP_CONTROL) {
+		error("bnep: Packet received is not bnep type");
+		goto failed;
+	}
+
+	if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+		return TRUE;
+
+	r = ntohs(rsp->resp);
+	if (r != BNEP_SUCCESS) {
+		error("bnep: failed");
+		goto failed;
+	}
+
+	memset(&timeo, 0, sizeof(timeo));
+	timeo.tv_sec = 0;
+	setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+	sk = g_io_channel_unix_get_fd(session->io);
+	if (bnep_connadd(sk, session->src, session->iface) < 0)
+		goto failed;
+
+	if (bnep_if_up(session->iface) < 0) {
+		bnep_conndel(&session->dst_addr);
+		goto failed;
+	}
+
+	session->watch = g_io_add_watch(session->io,
+					G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) bnep_watchdog_cb, session);
+	g_io_channel_unref(session->io);
+	session->io = NULL;
+
+	session->conn_cb(session->iface, 0, session->conn_data);
+
+	return FALSE;
+
+failed:
+	session->conn_cb(NULL, -EIO, session->conn_data);
+
+	return FALSE;
+}
+
+static int bnep_setup_conn_req(struct bnep *session)
+{
+	struct bnep_setup_conn_req *req;
+	struct __service_16 *s;
+	unsigned char pkt[BNEP_MTU];
+	int fd;
+
+	/* Send request */
+	req = (void *) pkt;
+	req->type = BNEP_CONTROL;
+	req->ctrl = BNEP_SETUP_CONN_REQ;
+	req->uuid_size = 2;     /* 16bit UUID */
+	s = (void *) req->service;
+	s->src = htons(session->src);
+	s->dst = htons(session->dst);
+
+	fd = g_io_channel_unix_get_fd(session->io);
+	if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) {
+		error("bnep: connection req send failed: %s", strerror(errno));
+		return -errno;
+	}
+
+	session->attempts++;
+
+	return 0;
+}
+
+static gboolean bnep_conn_req_to(gpointer user_data)
+{
+	struct bnep *session = user_data;
+
+	if (session->attempts == CON_SETUP_RETRIES) {
+		error("bnep: Too many bnep connection attempts");
+	} else {
+		error("bnep: connection setup TO, retrying...");
+		if (bnep_setup_conn_req(session) == 0)
+			return TRUE;
+	}
+
+	session->conn_cb(NULL, -ETIMEDOUT, session->conn_data);
+
+	return FALSE;
+}
+
+struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
+								char *iface)
+{
+	struct bnep *session;
+	int dup_fd;
+
+	dup_fd = dup(sk);
+	if (dup_fd < 0)
+		return NULL;
+
+	session = g_new0(struct bnep, 1);
+	session->io = g_io_channel_unix_new(dup_fd);
+	session->src = local_role;
+	session->dst = remote_role;
+	strncpy(session->iface, iface, 16);
+	session->iface[15] = '\0';
+
+	g_io_channel_set_close_on_unref(session->io, TRUE);
+	session->watch = g_io_add_watch(session->io,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) bnep_setup_cb, session);
+
+	return session;
+}
+
+void bnep_free(struct bnep *session)
+{
+	if (!session)
+		return;
+
+	if (session->io) {
+		g_io_channel_shutdown(session->io, FALSE, NULL);
+		g_io_channel_unref(session->io);
+		session->io = NULL;
+	}
+
+	if (session->watch > 0) {
+		g_source_remove(session->watch);
+		session->watch = 0;
+	}
+
+	g_free(session);
+}
+
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb,
+					bnep_disconnect_cb disconn_cb,
+					void *conn_data, void *disconn_data)
+{
+	GError *gerr = NULL;
+	int err;
+
+	if (!session || !conn_cb || !disconn_cb)
+		return -EINVAL;
+
+	session->attempts = 0;
+	session->conn_cb = conn_cb;
+	session->disconn_cb = disconn_cb;
+	session->conn_data = conn_data;
+	session->disconn_data = disconn_data;
+
+	bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr,
+							BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("bnep: connect failed: %s", gerr->message);
+		g_error_free(gerr);
+		return -EINVAL;
+	}
+
+	err = bnep_setup_conn_req(session);
+	if (err < 0)
+		return err;
+
+	session->setup_to = g_timeout_add_seconds(CON_SETUP_TO,
+						bnep_conn_req_to, session);
+	return 0;
+}
+
+void bnep_disconnect(struct bnep *session)
+{
+	if (!session)
+		return;
+
+	if (session->watch > 0) {
+		g_source_remove(session->watch);
+		session->watch = 0;
+	}
+
+	if (session->io) {
+		g_io_channel_unref(session->io);
+		session->io = NULL;
+	}
+
+	bnep_if_down(session->iface);
+	bnep_conndel(&session->dst_addr);
+}
+
+static int bnep_add_to_bridge(const char *devname, const char *bridge)
+{
+	int ifindex;
+	struct ifreq ifr;
+	int sk, err = 0;
+
+	if (!devname || !bridge)
+		return -EINVAL;
+
+	ifindex = if_nametoindex(devname);
+
+	sk = socket(AF_INET, SOCK_STREAM, 0);
+	if (sk < 0)
+		return -1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+	ifr.ifr_ifindex = ifindex;
+
+	if (ioctl(sk, SIOCBRADDIF, &ifr) < 0) {
+		err = -errno;
+		error("bnep: Can't add %s to the bridge %s: %s(%d)",
+					devname, bridge, strerror(-err), -err);
+	} else {
+		info("bnep: bridge %s: interface %s added", bridge, devname);
+	}
+
+	close(sk);
+
+	return err;
+}
+
+static int bnep_del_from_bridge(const char *devname, const char *bridge)
+{
+	int ifindex;
+	struct ifreq ifr;
+	int sk, err = 0;
+
+	if (!devname || !bridge)
+		return -EINVAL;
+
+	ifindex = if_nametoindex(devname);
+
+	sk = socket(AF_INET, SOCK_STREAM, 0);
+	if (sk < 0)
+		return -1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+	ifr.ifr_ifindex = ifindex;
+
+	if (ioctl(sk, SIOCBRDELIF, &ifr) < 0) {
+		err = -errno;
+		error("bnep: Can't delete %s from the bridge %s: %s(%d)",
+					devname, bridge, strerror(-err), -err);
+	} else {
+		info("bnep: bridge %s: interface %s removed", bridge, devname);
+	}
+
+	close(sk);
+
+	return err;
+}
+
+static ssize_t bnep_send_ctrl_rsp(int sk, uint8_t ctrl, uint16_t resp)
+{
+	ssize_t sent;
+
+	switch (ctrl) {
+	case BNEP_CMD_NOT_UNDERSTOOD: {
+		struct bnep_ctrl_cmd_not_understood_cmd rsp;
+
+		rsp.type = BNEP_CONTROL;
+		rsp.ctrl = ctrl;
+		rsp.unkn_ctrl = (uint8_t) resp;
+
+		sent = send(sk, &rsp, sizeof(rsp), 0);
+		break;
+	}
+	case BNEP_FILTER_MULT_ADDR_RSP:
+	case BNEP_FILTER_NET_TYPE_RSP:
+	case BNEP_SETUP_CONN_RSP: {
+		struct bnep_control_rsp rsp;
+
+		rsp.type = BNEP_CONTROL;
+		rsp.ctrl = ctrl;
+		rsp.resp = htons(resp);
+
+		sent = send(sk, &rsp, sizeof(rsp), 0);
+		break;
+	}
+	default:
+		error("bnep: wrong response type");
+		sent = -1;
+		break;
+	}
+
+	return sent;
+}
+
+static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req,
+								uint16_t *dst)
+{
+	const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+	uint16_t src;
+	uint8_t *dest, *source;
+	uint32_t val;
+
+	if (((req->type != BNEP_CONTROL) &&
+		(req->type != (BNEP_CONTROL | BNEP_EXT_HEADER)))  ||
+					req->ctrl != BNEP_SETUP_CONN_REQ)
+		return BNEP_CONN_NOT_ALLOWED;
+
+	dest = req->service;
+	source = req->service + req->uuid_size;
+
+	switch (req->uuid_size) {
+	case 2: /* UUID16 */
+		*dst = get_be16(dest);
+		src = get_be16(source);
+		break;
+	case 16: /* UUID128 */
+		/* Check that the bytes in the UUID, except the service ID
+		 * itself, are correct. The service ID is checked in
+		 * bnep_setup_chk(). */
+		if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0)
+			return BNEP_CONN_INVALID_DST;
+		if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
+			return BNEP_CONN_INVALID_SRC;
+
+		/* Intentional no-break */
+
+	case 4: /* UUID32 */
+		val = get_be32(dest);
+		if (val > 0xffff)
+			return BNEP_CONN_INVALID_DST;
+
+		*dst = val;
+
+		val = get_be32(source);
+		if (val > 0xffff)
+			return BNEP_CONN_INVALID_SRC;
+
+		src = val;
+		break;
+	default:
+		return BNEP_CONN_INVALID_SVC;
+	}
+
+	/* Allowed PAN Profile scenarios */
+	switch (*dst) {
+	case BNEP_SVC_NAP:
+	case BNEP_SVC_GN:
+		if (src == BNEP_SVC_PANU)
+			return BNEP_SUCCESS;
+		return BNEP_CONN_INVALID_SRC;
+	case BNEP_SVC_PANU:
+		if (src == BNEP_SVC_PANU || src == BNEP_SVC_GN ||
+							src == BNEP_SVC_NAP)
+			return BNEP_SUCCESS;
+
+		return BNEP_CONN_INVALID_SRC;
+	}
+
+	return BNEP_CONN_INVALID_DST;
+}
+
+static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
+					char *iface, const bdaddr_t *addr,
+					uint8_t *setup_data, int len)
+{
+	int err, n;
+	uint16_t rsp;
+
+	n = read(sk, setup_data, len);
+	if (n != len) {
+		err = -EIO;
+		rsp = BNEP_CONN_NOT_ALLOWED;
+		goto reply;
+	}
+
+	err = bnep_connadd(sk, dst, iface);
+	if (err < 0) {
+		rsp = BNEP_CONN_NOT_ALLOWED;
+		goto reply;
+	}
+
+	err = bnep_add_to_bridge(iface, bridge);
+	if (err < 0) {
+		bnep_conndel(addr);
+		rsp = BNEP_CONN_NOT_ALLOWED;
+		goto reply;
+	}
+
+	err = bnep_if_up(iface);
+	if (err < 0) {
+		bnep_del_from_bridge(iface, bridge);
+		bnep_conndel(addr);
+		rsp = BNEP_CONN_NOT_ALLOWED;
+		goto reply;
+	}
+
+	rsp = BNEP_SUCCESS;
+
+reply:
+	if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+		err = -errno;
+		error("bnep: send ctrl rsp error: %s (%d)", strerror(-err),
+									-err);
+	}
+
+	return err;
+}
+
+int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
+						uint8_t *setup_data, int len)
+{
+	int err;
+	uint32_t feat;
+	uint16_t rsp, dst;
+	struct bnep_setup_conn_req *req = (void *) setup_data;
+
+	/* Highest known Control command ID
+	 * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+	if (req->type == BNEP_CONTROL &&
+					req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+		error("bnep: cmd not understood");
+		err = bnep_send_ctrl_rsp(sk, BNEP_CMD_NOT_UNDERSTOOD,
+								req->ctrl);
+		if (err < 0)
+			error("send not understood ctrl rsp error: %s (%d)",
+							strerror(errno), errno);
+
+		return err;
+	}
+
+	/* Processing BNEP_SETUP_CONNECTION_REQUEST_MSG */
+	rsp = bnep_setup_decode(sk, req, &dst);
+	if (rsp != BNEP_SUCCESS) {
+		err = -rsp;
+		error("bnep: error while decoding setup connection request: %d",
+									rsp);
+		goto failed;
+	}
+
+	feat = bnep_getsuppfeat();
+
+	/*
+	 * Take out setup data if kernel doesn't support handling it, especially
+	 * setup request. If kernel would have set session flags, they should
+	 * be checked and handled respectively.
+	 */
+	if (!feat || !(feat & (1 << BNEP_SETUP_RESPONSE)))
+		return bnep_server_add_legacy(sk, dst, bridge, iface, addr,
+							setup_data, len);
+
+	err = bnep_connadd(sk, dst, iface);
+	if (err < 0) {
+		rsp = BNEP_CONN_NOT_ALLOWED;
+		goto failed;
+	}
+
+	err = bnep_add_to_bridge(iface, bridge);
+	if (err < 0)
+		goto failed_conn;
+
+	err = bnep_if_up(iface);
+	if (err < 0)
+		goto failed_bridge;
+
+	return 0;
+
+failed_bridge:
+	bnep_del_from_bridge(iface, bridge);
+
+failed_conn:
+	bnep_conndel(addr);
+
+	return err;
+
+failed:
+	if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+		err = -errno;
+		error("bnep: send ctrl rsp error: %s (%d)", strerror(-err),
+									-err);
+	}
+
+	return err;
+}
+
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
+{
+	if (!bridge || !iface || !addr)
+		return;
+
+	bnep_del_from_bridge(iface, bridge);
+	bnep_if_down(iface);
+	bnep_conndel(addr);
+}
diff --git a/bluez/repo/profiles/network/bnep.h b/bluez/repo/profiles/network/bnep.h
new file mode 100644
index 0000000..e9f4c1c
--- /dev/null
+++ b/bluez/repo/profiles/network/bnep.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+struct bnep;
+
+int bnep_init(void);
+int bnep_cleanup(void);
+
+struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
+								char *iface);
+void bnep_free(struct bnep *session);
+
+typedef void (*bnep_connect_cb) (char *iface, int err, void *data);
+typedef void (*bnep_disconnect_cb) (void *data);
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb,
+					bnep_disconnect_cb disconn_cb,
+					void *conn_data, void *disconn_data);
+void bnep_disconnect(struct bnep *session);
+
+int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
+						uint8_t *setup_data, int len);
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr);
diff --git a/bluez/repo/profiles/network/connection.c b/bluez/repo/profiles/network/connection.c
new file mode 100644
index 0000000..5305ace
--- /dev/null
+++ b/bluez/repo/profiles/network/connection.c
@@ -0,0 +1,589 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/error.h"
+#include "lib/uuid.h"
+
+#include "bnep.h"
+#include "connection.h"
+
+#define NETWORK_PEER_INTERFACE "org.bluez.Network1"
+#define BNEP_INTERFACE "bnep%d"
+
+typedef enum {
+	CONNECTED,
+	CONNECTING,
+	DISCONNECTED
+} conn_state;
+
+struct network_peer {
+	struct btd_device *device;
+	GSList		*connections;
+};
+
+struct network_conn {
+	struct btd_service *service;
+	char		dev[16];	/* Interface name */
+	uint16_t	id;		/* Role: Service Class Identifier */
+	conn_state	state;
+	GIOChannel	*io;
+	guint		dc_id;
+	struct network_peer *peer;
+	DBusMessage	*connect;
+	struct bnep	*session;
+};
+
+static GSList *peers = NULL;
+
+static uint16_t get_pan_srv_id(const char *svc)
+{
+	if (!strcasecmp(svc, "panu") || !strcasecmp(svc, PANU_UUID))
+		return BNEP_SVC_PANU;
+	if (!strcasecmp(svc, "nap") || !strcasecmp(svc, NAP_UUID))
+		return BNEP_SVC_NAP;
+	if (!strcasecmp(svc, "gn") || !strcasecmp(svc, GN_UUID))
+		return BNEP_SVC_GN;
+
+	return 0;
+}
+
+static struct network_peer *find_peer(GSList *list, struct btd_device *device)
+{
+	for (; list; list = list->next) {
+		struct network_peer *peer = list->data;
+
+		if (peer->device == device)
+			return peer;
+	}
+
+	return NULL;
+}
+
+static struct network_conn *find_connection_by_state(GSList *list,
+							conn_state state)
+{
+	for (; list; list = list->next) {
+		struct network_conn *nc = list->data;
+
+		if (nc->state == state)
+			return nc;
+	}
+
+	return NULL;
+}
+
+static void bnep_disconn_cb(gpointer data)
+{
+	struct network_conn *nc = data;
+	DBusConnection *conn = btd_get_dbus_connection();
+	const char *path = device_get_path(nc->peer->device);
+
+	g_dbus_emit_property_changed(conn, path,
+					NETWORK_PEER_INTERFACE, "Connected");
+	g_dbus_emit_property_changed(conn, path,
+					NETWORK_PEER_INTERFACE, "Interface");
+	g_dbus_emit_property_changed(conn, path,
+					NETWORK_PEER_INTERFACE, "UUID");
+	device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
+	nc->dc_id = 0;
+
+	btd_service_disconnecting_complete(nc->service, 0);
+
+	info("%s disconnected", nc->dev);
+
+	nc->state = DISCONNECTED;
+	memset(nc->dev, 0, sizeof(nc->dev));
+	strncpy(nc->dev, BNEP_INTERFACE, 16);
+	nc->dev[15] = '\0';
+
+	bnep_free(nc->session);
+	nc->session = NULL;
+}
+
+static void local_connect_cb(struct network_conn *nc, int err)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+	const char *pdev = nc->dev;
+
+	if (err < 0) {
+		DBusMessage *reply = btd_error_failed(nc->connect,
+							strerror(-err));
+		g_dbus_send_message(conn, reply);
+	} else {
+		g_dbus_send_reply(conn, nc->connect, DBUS_TYPE_STRING, &pdev,
+							DBUS_TYPE_INVALID);
+	}
+
+	dbus_message_unref(nc->connect);
+	nc->connect = NULL;
+}
+
+static void cancel_connection(struct network_conn *nc, int err)
+{
+	btd_service_connecting_complete(nc->service, err);
+
+	if (nc->connect)
+		local_connect_cb(nc, err);
+
+	if (nc->io) {
+		g_io_channel_shutdown(nc->io, FALSE, NULL);
+		g_io_channel_unref(nc->io);
+		nc->io = NULL;
+	}
+
+	if (nc->state == CONNECTED)
+		bnep_disconnect(nc->session);
+
+	bnep_free(nc->session);
+	nc->session = NULL;
+
+	nc->state = DISCONNECTED;
+}
+
+static void connection_destroy(DBusConnection *conn, void *user_data)
+{
+	struct network_conn *nc = user_data;
+
+	cancel_connection(nc, -EIO);
+}
+
+static void disconnect_cb(struct btd_device *device, gboolean removal,
+				void *user_data)
+{
+	struct network_conn *nc = user_data;
+
+	info("Network: disconnect %s", device_get_path(nc->peer->device));
+
+	connection_destroy(NULL, user_data);
+}
+
+static void bnep_conn_cb(char *iface, int err, void *data)
+{
+	struct network_conn *nc = data;
+	const char *path;
+	DBusConnection *conn;
+
+	DBG("");
+
+	if (err < 0) {
+		error("connect failed %s", strerror(-err));
+		goto failed;
+	}
+
+	info("%s connected", nc->dev);
+
+	memcpy(nc->dev, iface, sizeof(nc->dev));
+	btd_service_connecting_complete(nc->service, 0);
+
+	if (nc->connect)
+		local_connect_cb(nc, 0);
+
+	conn = btd_get_dbus_connection();
+	path = device_get_path(nc->peer->device);
+
+	g_dbus_emit_property_changed(conn, path,
+					NETWORK_PEER_INTERFACE, "Connected");
+	g_dbus_emit_property_changed(conn, path,
+					NETWORK_PEER_INTERFACE, "Interface");
+	g_dbus_emit_property_changed(conn, path,
+					NETWORK_PEER_INTERFACE, "UUID");
+
+	nc->state = CONNECTED;
+	nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
+								nc, NULL);
+
+	return;
+
+failed:
+	cancel_connection(nc, -EIO);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	struct network_conn *nc = data;
+	int sk, perr;
+
+	if (err) {
+		error("%s", err->message);
+		goto failed;
+	}
+
+	sk = g_io_channel_unix_get_fd(nc->io);
+	nc->session = bnep_new(sk, BNEP_SVC_PANU, nc->id, BNEP_INTERFACE);
+	if (!nc->session)
+		goto failed;
+
+	perr = bnep_connect(nc->session, bnep_conn_cb, bnep_disconn_cb, nc, nc);
+	if (perr < 0) {
+		error("bnep connect(): %s (%d)", strerror(-perr), -perr);
+		goto failed;
+	}
+
+	if (nc->io) {
+		g_io_channel_unref(nc->io);
+		nc->io = NULL;
+	}
+
+	return;
+
+failed:
+	cancel_connection(nc, -EIO);
+}
+
+static DBusMessage *local_connect(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct network_peer *peer = data;
+	struct btd_service *service;
+	struct network_conn *nc;
+	const char *svc;
+	uint16_t id;
+	int err;
+	char uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t uuid16, uuid128;
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
+						DBUS_TYPE_INVALID) == FALSE)
+		return btd_error_invalid_args(msg);
+
+	id = get_pan_srv_id(svc);
+	bt_uuid16_create(&uuid16, id);
+	bt_uuid_to_uuid128(&uuid16, &uuid128);
+
+	if (bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR) < 0)
+		return btd_error_invalid_args(msg);
+
+	service = btd_device_get_service(peer->device, uuid_str);
+	if (service == NULL)
+		return btd_error_not_supported(msg);
+
+	nc = btd_service_get_user_data(service);
+
+	if (nc->connect != NULL)
+		return btd_error_busy(msg);
+
+	err = connection_connect(nc->service);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	nc->connect = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+/* Connect and initiate BNEP session */
+int connection_connect(struct btd_service *svc)
+{
+	struct network_conn *nc = btd_service_get_user_data(svc);
+	struct network_peer *peer = nc->peer;
+	uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid);
+	GError *err = NULL;
+	const bdaddr_t *src;
+	const bdaddr_t *dst;
+
+	DBG("id %u", id);
+
+	if (nc->state != DISCONNECTED)
+		return -EALREADY;
+
+	src = btd_adapter_get_address(device_get_adapter(peer->device));
+	dst = device_get_address(peer->device);
+
+	nc->io = bt_io_connect(connect_cb, nc,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, src,
+				BT_IO_OPT_DEST_BDADDR, dst,
+				BT_IO_OPT_PSM, BNEP_PSM,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_OMTU, BNEP_MTU,
+				BT_IO_OPT_IMTU, BNEP_MTU,
+				BT_IO_OPT_INVALID);
+	if (!nc->io)
+		return -EIO;
+
+	nc->state = CONNECTING;
+
+	return 0;
+}
+
+int connection_disconnect(struct btd_service *svc)
+{
+	struct network_conn *nc = btd_service_get_user_data(svc);
+
+	if (nc->state == DISCONNECTED)
+		return 0;
+
+	connection_destroy(NULL, nc);
+
+	return 0;
+}
+
+static DBusMessage *local_disconnect(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct network_peer *peer = data;
+	GSList *l;
+
+	for (l = peer->connections; l; l = l->next) {
+		struct network_conn *nc = l->data;
+		int err;
+
+		if (nc->state == DISCONNECTED)
+			continue;
+
+		err = connection_disconnect(nc->service);
+		if (err < 0)
+			return btd_error_failed(msg, strerror(-err));
+
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
+
+	return btd_error_not_connected(msg);
+}
+
+static gboolean
+network_property_get_connected(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct network_peer *peer = data;
+	struct network_conn *nc;
+	dbus_bool_t connected;
+
+	nc = find_connection_by_state(peer->connections, CONNECTED);
+	connected = nc != NULL ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+
+	return TRUE;
+}
+
+static gboolean network_property_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct network_peer *peer = data;
+	struct network_conn *nc;
+
+	nc = find_connection_by_state(peer->connections, CONNECTED);
+	if (nc == NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+network_property_get_interface(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct network_peer *peer = data;
+	struct network_conn *nc;
+	const char *iface;
+
+	nc = find_connection_by_state(peer->connections, CONNECTED);
+	if (nc == NULL)
+		return FALSE;
+
+	iface = nc->dev;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &iface);
+
+	return TRUE;
+}
+
+static gboolean network_property_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct network_peer *peer = data;
+	struct network_conn *nc;
+	char uuid_str[MAX_LEN_UUID_STR];
+	const char *uuid = uuid_str;
+	bt_uuid_t uuid16, uuid128;
+
+	nc = find_connection_by_state(peer->connections, CONNECTED);
+	if (nc == NULL)
+		return FALSE;
+
+	bt_uuid16_create(&uuid16, nc->id);
+	bt_uuid_to_uuid128(&uuid16, &uuid128);
+	bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+	return TRUE;
+}
+
+static void connection_free(void *data)
+{
+	struct network_conn *nc = data;
+
+	if (nc->dc_id)
+		device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
+
+	connection_destroy(NULL, nc);
+
+	if (nc->connect)
+		dbus_message_unref(nc->connect);
+
+	btd_service_unref(nc->service);
+	g_free(nc);
+}
+
+static void peer_free(struct network_peer *peer)
+{
+	g_slist_free_full(peer->connections, connection_free);
+	btd_device_unref(peer->device);
+	g_free(peer);
+}
+
+static void path_unregister(void *data)
+{
+	struct network_peer *peer = data;
+
+	DBG("Unregistered interface %s on path %s",
+		NETWORK_PEER_INTERFACE, device_get_path(peer->device));
+
+	peers = g_slist_remove(peers, peer);
+	peer_free(peer);
+}
+
+static const GDBusMethodTable connection_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Connect",
+				GDBUS_ARGS({"uuid", "s"}),
+				GDBUS_ARGS({"interface", "s"}),
+				local_connect) },
+	{ GDBUS_METHOD("Disconnect",
+			NULL, NULL, local_disconnect) },
+	{ }
+};
+
+static const GDBusPropertyTable connection_properties[] = {
+	{ "Connected", "b", network_property_get_connected },
+	{ "Interface", "s", network_property_get_interface, NULL,
+						network_property_exists },
+	{ "UUID", "s", network_property_get_uuid, NULL,
+						network_property_exists },
+	{ }
+};
+
+void connection_unregister(struct btd_service *svc)
+{
+	struct btd_device *device = btd_service_get_device(svc);
+	struct network_conn *conn = btd_service_get_user_data(svc);
+	struct network_peer *peer = conn->peer;
+	uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid);
+
+	DBG("%s id %u", device_get_path(device), id);
+
+	peer->connections = g_slist_remove(peer->connections, conn);
+	connection_free(conn);
+
+	if (peer->connections != NULL)
+		return;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						device_get_path(device),
+						NETWORK_PEER_INTERFACE);
+}
+
+static struct network_peer *create_peer(struct btd_device *device)
+{
+	struct network_peer *peer;
+	const char *path;
+
+	peer = g_new0(struct network_peer, 1);
+	peer->device = btd_device_ref(device);
+
+	path = device_get_path(device);
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(), path,
+					NETWORK_PEER_INTERFACE,
+					connection_methods,
+					NULL, connection_properties,
+					peer, path_unregister) == FALSE) {
+		error("D-Bus failed to register %s interface",
+			NETWORK_PEER_INTERFACE);
+		peer_free(peer);
+		return NULL;
+	}
+
+	DBG("Registered interface %s on path %s",
+		NETWORK_PEER_INTERFACE, path);
+
+	return peer;
+}
+
+int connection_register(struct btd_service *svc)
+{
+	struct btd_device *device = btd_service_get_device(svc);
+	struct network_peer *peer;
+	struct network_conn *nc;
+	uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid);
+
+	DBG("%s id %u", device_get_path(device), id);
+
+	peer = find_peer(peers, device);
+	if (!peer) {
+		peer = create_peer(device);
+		if (!peer)
+			return -1;
+		peers = g_slist_append(peers, peer);
+	}
+
+	nc = g_new0(struct network_conn, 1);
+	nc->id = id;
+	nc->service = btd_service_ref(svc);
+	nc->state = DISCONNECTED;
+	nc->peer = peer;
+
+	btd_service_set_user_data(svc, nc);
+
+	DBG("id %u registered", id);
+
+	peer->connections = g_slist_append(peer->connections, nc);
+
+	return 0;
+}
diff --git a/bluez/repo/profiles/network/connection.h b/bluez/repo/profiles/network/connection.h
new file mode 100644
index 0000000..4a8b43b
--- /dev/null
+++ b/bluez/repo/profiles/network/connection.h
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+int connection_register(struct btd_service *service);
+void connection_unregister(struct btd_service *service);
+int connection_connect(struct btd_service *service);
+int connection_disconnect(struct btd_service *service);
diff --git a/bluez/repo/profiles/network/manager.c b/bluez/repo/profiles/network/manager.c
new file mode 100644
index 0000000..41377fb
--- /dev/null
+++ b/bluez/repo/profiles/network/manager.c
@@ -0,0 +1,210 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/log.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+
+#include "bnep.h"
+#include "connection.h"
+#include "server.h"
+
+static gboolean conf_security = TRUE;
+
+static void read_config(const char *file)
+{
+	GKeyFile *keyfile;
+	GError *err = NULL;
+
+	keyfile = g_key_file_new();
+
+	if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+		g_clear_error(&err);
+		goto done;
+	}
+
+	conf_security = !g_key_file_get_boolean(keyfile, "General",
+						"DisableSecurity", &err);
+	if (err) {
+		DBG("%s: %s", file, err->message);
+		g_clear_error(&err);
+	}
+
+done:
+	g_key_file_free(keyfile);
+
+	DBG("Config options: Security=%s",
+				conf_security ? "true" : "false");
+}
+
+static int panu_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	return server_register(adapter, BNEP_SVC_PANU);
+}
+
+static void panu_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	server_unregister(adapter, BNEP_SVC_PANU);
+}
+
+static int gn_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	return server_register(adapter, BNEP_SVC_GN);
+}
+
+static void gn_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	server_unregister(adapter, BNEP_SVC_GN);
+}
+
+static int nap_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	return server_register(adapter, BNEP_SVC_NAP);
+}
+
+static void nap_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	server_unregister(adapter, BNEP_SVC_NAP);
+}
+
+static struct btd_profile panu_profile = {
+	.name		= "network-panu",
+	.local_uuid	= NAP_UUID,
+	.remote_uuid	= PANU_UUID,
+	.device_probe	= connection_register,
+	.device_remove	= connection_unregister,
+	.connect	= connection_connect,
+	.disconnect	= connection_disconnect,
+	.adapter_probe	= panu_server_probe,
+	.adapter_remove	= panu_server_remove,
+};
+
+static struct btd_profile gn_profile = {
+	.name		= "network-gn",
+	.local_uuid	= PANU_UUID,
+	.remote_uuid	= GN_UUID,
+	.device_probe	= connection_register,
+	.device_remove	= connection_unregister,
+	.connect	= connection_connect,
+	.disconnect	= connection_disconnect,
+	.adapter_probe	= gn_server_probe,
+	.adapter_remove	= gn_server_remove,
+};
+
+static struct btd_profile nap_profile = {
+	.name		= "network-nap",
+	.local_uuid	= PANU_UUID,
+	.remote_uuid	= NAP_UUID,
+	.device_probe	= connection_register,
+	.device_remove	= connection_unregister,
+	.connect	= connection_connect,
+	.disconnect	= connection_disconnect,
+	.adapter_probe	= nap_server_probe,
+	.adapter_remove	= nap_server_remove,
+};
+
+static int network_init(void)
+{
+	int err;
+
+	read_config(CONFIGDIR "/network.conf");
+
+	err = bnep_init();
+	if (err) {
+		if (err == -EPROTONOSUPPORT)
+			err = -ENOSYS;
+		return err;
+	}
+
+	/*
+	 * There is one socket to handle the incoming connections. NAP,
+	 * GN and PANU servers share the same PSM. The initial BNEP message
+	 * (setup connection request) contains the destination service
+	 * field that defines which service the source is connecting to.
+	 */
+
+	if (server_init(conf_security) < 0)
+		return -1;
+
+	btd_profile_register(&panu_profile);
+	btd_profile_register(&gn_profile);
+	btd_profile_register(&nap_profile);
+
+	return 0;
+}
+
+static void network_exit(void)
+{
+	btd_profile_unregister(&panu_profile);
+	btd_profile_unregister(&gn_profile);
+	btd_profile_unregister(&nap_profile);
+
+	bnep_cleanup();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(network, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, network_init, network_exit)
diff --git a/bluez/repo/profiles/network/network.conf b/bluez/repo/profiles/network/network.conf
new file mode 100644
index 0000000..5f11639
--- /dev/null
+++ b/bluez/repo/profiles/network/network.conf
@@ -0,0 +1,6 @@
+# Configuration file for the network service
+
+[General]
+
+# Disable link encryption: default=false
+#DisableSecurity=true
diff --git a/bluez/repo/profiles/network/server.c b/bluez/repo/profiles/network/server.c
new file mode 100644
index 0000000..1bff9f8
--- /dev/null
+++ b/bluez/repo/profiles/network/server.c
@@ -0,0 +1,761 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.h"
+#include "src/shared/util.h"
+
+#include "bnep.h"
+#include "server.h"
+
+#define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
+#define BNEP_INTERFACE "bnep%d"
+#define SETUP_TIMEOUT		1
+
+/* Pending Authorization */
+struct network_session {
+	bdaddr_t	dst;		/* Remote Bluetooth Address */
+	char		dev[16];	/* Interface name */
+	GIOChannel	*io;		/* Pending connect channel */
+	guint		watch;		/* BNEP socket watch */
+};
+
+struct network_adapter {
+	struct btd_adapter *adapter;	/* Adapter pointer */
+	GIOChannel	*io;		/* Bnep socket */
+	struct network_session *setup;	/* Setup in progress */
+	GSList		*servers;	/* Server register to adapter */
+};
+
+/* Main server structure */
+struct network_server {
+	bdaddr_t	src;		/* Bluetooth Local Address */
+	char		*name;		/* Server service name */
+	char		*bridge;	/* Bridge name */
+	uint32_t	record_id;	/* Service record id */
+	uint16_t	id;		/* Service class identifier */
+	GSList		*sessions;	/* Active connections */
+	struct network_adapter *na;	/* Adapter reference */
+	guint		watch_id;	/* Client service watch */
+};
+
+static GSList *adapters = NULL;
+static gboolean security = TRUE;
+
+static struct network_adapter *find_adapter(GSList *list,
+					struct btd_adapter *adapter)
+{
+	for (; list; list = list->next) {
+		struct network_adapter *na = list->data;
+
+		if (na->adapter == adapter)
+			return na;
+	}
+
+	return NULL;
+}
+
+static struct network_server *find_server(GSList *list, uint16_t id)
+{
+	for (; list; list = list->next) {
+		struct network_server *ns = list->data;
+
+		if (ns->id == id)
+			return ns;
+	}
+
+	return NULL;
+}
+
+static struct network_server *find_server_by_uuid(GSList *list,
+							const char *uuid)
+{
+	bt_uuid_t srv_uuid, bnep_uuid;
+
+	if (!bt_string_to_uuid(&srv_uuid, uuid)) {
+		for (; list; list = list->next) {
+			struct network_server *ns = list->data;
+
+			bt_uuid16_create(&bnep_uuid, ns->id);
+
+			/* UUID value compare */
+			if (!bt_uuid_cmp(&srv_uuid, &bnep_uuid))
+				return ns;
+		}
+	} else {
+		for (; list; list = list->next) {
+			struct network_server *ns = list->data;
+
+			/* String value compare */
+			switch (ns->id) {
+			case BNEP_SVC_PANU:
+				if (!strcasecmp(uuid, "panu"))
+					return ns;
+				break;
+			case BNEP_SVC_NAP:
+				if (!strcasecmp(uuid, "nap"))
+					return ns;
+				break;
+			case BNEP_SVC_GN:
+				if (!strcasecmp(uuid, "gn"))
+					return ns;
+				break;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static sdp_record_t *server_record_new(const char *name, uint16_t id)
+{
+	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+	uuid_t root_uuid, pan, l2cap, bnep;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *proto[2];
+	sdp_data_t *v, *p;
+	uint16_t psm = BNEP_PSM, version = 0x0100;
+	uint16_t security_desc = (security ? 0x0001 : 0x0000);
+	uint16_t net_access_type = 0xfffe;
+	uint32_t max_net_access_rate = 0;
+	const char *desc = "Network service";
+	sdp_record_t *record;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->attrlist = NULL;
+	record->pattern = NULL;
+
+	switch (id) {
+	case BNEP_SVC_NAP:
+		sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+		svclass = sdp_list_append(NULL, &pan);
+		sdp_set_service_classes(record, svclass);
+
+		sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+		profile[0].version = 0x0100;
+		pfseq = sdp_list_append(NULL, &profile[0]);
+		sdp_set_profile_descs(record, pfseq);
+
+		sdp_set_info_attr(record, name, NULL, desc);
+
+		sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
+					SDP_UINT16, &net_access_type);
+		sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+					SDP_UINT32, &max_net_access_rate);
+		break;
+	case BNEP_SVC_GN:
+		sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+		svclass = sdp_list_append(NULL, &pan);
+		sdp_set_service_classes(record, svclass);
+
+		sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+		profile[0].version = 0x0100;
+		pfseq = sdp_list_append(NULL, &profile[0]);
+		sdp_set_profile_descs(record, pfseq);
+
+		sdp_set_info_attr(record, name, NULL, desc);
+		break;
+	case BNEP_SVC_PANU:
+		sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+		svclass = sdp_list_append(NULL, &pan);
+		sdp_set_service_classes(record, svclass);
+
+		sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+		profile[0].version = 0x0100;
+		pfseq = sdp_list_append(NULL, &profile[0]);
+		sdp_set_profile_descs(record, pfseq);
+
+		sdp_set_info_attr(record, name, NULL, desc);
+		break;
+	default:
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	p = sdp_data_alloc(SDP_UINT16, &psm);
+	proto[0] = sdp_list_append(proto[0], p);
+	apseq    = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep);
+	v = sdp_data_alloc(SDP_UINT16, &version);
+	proto[1] = sdp_list_append(proto[1], v);
+
+	/* Supported protocols */
+	{
+		uint16_t ptype[] = {
+			0x0800,  /* IPv4 */
+			0x0806,  /* ARP */
+		};
+		sdp_data_t *head, *pseq;
+		int p;
+
+		for (p = 0, head = NULL; p < 2; p++) {
+			sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+			if (head)
+				sdp_seq_append(head, data);
+			else
+				head = data;
+		}
+		pseq = sdp_data_alloc(SDP_SEQ16, head);
+		proto[1] = sdp_list_append(proto[1], pseq);
+	}
+
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_add_lang_attr(record);
+
+	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
+				SDP_UINT16, &security_desc);
+
+	sdp_data_free(p);
+	sdp_data_free(v);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(svclass, NULL);
+	sdp_list_free(pfseq, NULL);
+
+	return record;
+}
+
+static void session_free(void *data)
+{
+	struct network_session *session = data;
+
+	if (session->watch)
+		g_source_remove(session->watch);
+
+	if (session->io)
+		g_io_channel_unref(session->io);
+
+	g_free(session);
+}
+
+static void setup_destroy(void *user_data)
+{
+	struct network_adapter *na = user_data;
+	struct network_session *setup = na->setup;
+
+	if (!setup)
+		return;
+
+	na->setup = NULL;
+
+	session_free(setup);
+}
+
+static gboolean bnep_setup(GIOChannel *chan,
+			GIOCondition cond, gpointer user_data)
+{
+	const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+	struct network_adapter *na = user_data;
+	struct network_server *ns;
+	uint8_t packet[BNEP_MTU];
+	struct bnep_setup_conn_req *req = (void *) packet;
+	uint16_t dst_role = 0;
+	uint32_t val;
+	int n, sk;
+	char *bridge = NULL;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & (G_IO_ERR | G_IO_HUP)) {
+		error("Hangup or error on BNEP socket");
+		return FALSE;
+	}
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	/*
+	 * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+	 * of kernel setup connection msg handling.
+	 */
+	n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+	if (n < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		return FALSE;
+	}
+
+	/*
+	 * Initial received data packet is BNEP_SETUP_CONNECTION_REQUEST_MSG
+	 * minimal size of this frame is 3 octets: 1 byte of BNEP Type +
+	 * 1 byte of BNEP Control Type + 1 byte of BNEP services UUID size.
+	 */
+	if (n < 3) {
+		error("To few setup connection request data received");
+		return FALSE;
+	}
+
+	switch (req->uuid_size) {
+	case 2:
+		dst_role = get_be16(req->service);
+		break;
+	case 16:
+		if (memcmp(&req->service[4], bt_base, sizeof(bt_base)) != 0)
+			break;
+
+		/* Intentional no-brake */
+
+	case 4:
+		val = get_be32(req->service);
+		if (val > 0xffff)
+			break;
+
+		dst_role = val;
+		break;
+	default:
+		break;
+	}
+
+	ns = find_server(na->servers, dst_role);
+	if (!ns || !ns->record_id || !ns->bridge)
+		error("Server error, bridge not initialized: (0x%x)", dst_role);
+	else
+		bridge = ns->bridge;
+
+	strncpy(na->setup->dev, BNEP_INTERFACE, 16);
+	na->setup->dev[15] = '\0';
+
+	if (bnep_server_add(sk, bridge, na->setup->dev, &na->setup->dst,
+							packet, n) < 0)
+		error("BNEP server cannot be added");
+
+	na->setup = NULL;
+
+	return FALSE;
+}
+
+static void connect_event(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct network_adapter *na = user_data;
+
+	if (err) {
+		error("%s", err->message);
+		setup_destroy(na);
+		return;
+	}
+
+	g_io_channel_set_close_on_unref(chan, TRUE);
+
+	na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				bnep_setup, na, setup_destroy);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+	struct network_adapter *na = user_data;
+	GError *err = NULL;
+
+	if (derr) {
+		error("Access denied: %s", derr->message);
+		goto reject;
+	}
+
+	if (!bt_io_accept(na->setup->io, connect_event, na, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		goto reject;
+	}
+
+	return;
+
+reject:
+	g_io_channel_shutdown(na->setup->io, TRUE, NULL);
+	setup_destroy(na);
+}
+
+static void confirm_event(GIOChannel *chan, gpointer user_data)
+{
+	struct network_adapter *na = user_data;
+	struct network_server *ns;
+	bdaddr_t src, dst;
+	char address[18];
+	GError *err = NULL;
+	guint ret;
+
+	bt_io_get(chan, &err,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	DBG("BNEP: incoming connect from %s", address);
+
+	if (na->setup) {
+		error("Refusing connect from %s: setup in progress", address);
+		goto drop;
+	}
+
+	ns = find_server(na->servers, BNEP_SVC_NAP);
+	if (!ns || !ns->record_id || !ns->bridge)
+		goto drop;
+
+	na->setup = g_new0(struct network_session, 1);
+	bacpy(&na->setup->dst, &dst);
+	na->setup->io = g_io_channel_ref(chan);
+
+	ret = btd_request_authorization(&src, &dst, BNEP_SVC_UUID,
+					auth_cb, na);
+	if (ret == 0) {
+		error("Refusing connect from %s", address);
+		setup_destroy(na);
+		goto drop;
+	}
+
+	return;
+
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+int server_init(gboolean secure)
+{
+	security = secure;
+
+	return 0;
+}
+
+static uint32_t register_server_record(struct network_server *ns)
+{
+	sdp_record_t *record;
+
+	record = server_record_new(ns->name, ns->id);
+	if (!record) {
+		error("Unable to allocate new service record");
+		return 0;
+	}
+
+	if (adapter_service_add(ns->na->adapter, record) < 0) {
+		error("Failed to register service record");
+		sdp_record_free(record);
+		return 0;
+	}
+
+	DBG("got record id 0x%x", record->handle);
+
+	return record->handle;
+}
+
+static void server_remove_sessions(struct network_server *ns)
+{
+	GSList *list;
+
+	for (list = ns->sessions; list; list = list->next) {
+		struct network_session *session = list->data;
+
+		if (*session->dev == '\0')
+			continue;
+
+		bnep_server_delete(ns->bridge, session->dev, &session->dst);
+	}
+
+	g_slist_free_full(ns->sessions, session_free);
+
+	ns->sessions = NULL;
+}
+
+static void server_disconnect(DBusConnection *conn, void *user_data)
+{
+	struct network_server *ns = user_data;
+
+	server_remove_sessions(ns);
+
+	ns->watch_id = 0;
+
+	if (ns->record_id) {
+		adapter_service_remove(ns->na->adapter, ns->record_id);
+		ns->record_id = 0;
+	}
+
+	g_free(ns->bridge);
+	ns->bridge = NULL;
+}
+
+static DBusMessage *register_server(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct network_adapter *na = data;
+	struct network_server *ns;
+	DBusMessage *reply;
+	const char *uuid, *bridge;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,
+				DBUS_TYPE_STRING, &bridge, DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	ns = find_server_by_uuid(na->servers, uuid);
+	if (ns == NULL)
+		return btd_error_failed(msg, "Invalid UUID");
+
+	if (ns->record_id)
+		return btd_error_already_exists(msg);
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	ns->record_id = register_server_record(ns);
+	if (!ns->record_id)
+		return btd_error_failed(msg, "SDP record registration failed");
+
+	g_free(ns->bridge);
+	ns->bridge = g_strdup(bridge);
+
+	ns->watch_id = g_dbus_add_disconnect_watch(conn,
+					dbus_message_get_sender(msg),
+					server_disconnect, ns, NULL);
+
+	return reply;
+}
+
+static DBusMessage *unregister_server(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct network_adapter *na = data;
+	struct network_server *ns;
+	DBusMessage *reply;
+	const char *uuid;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	ns = find_server_by_uuid(na->servers, uuid);
+	if (!ns)
+		return btd_error_failed(msg, "Invalid UUID");
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	g_dbus_remove_watch(conn, ns->watch_id);
+
+	server_disconnect(conn, ns);
+
+	return reply;
+}
+
+static void adapter_free(struct network_adapter *na)
+{
+	if (na->io != NULL) {
+		g_io_channel_shutdown(na->io, TRUE, NULL);
+		g_io_channel_unref(na->io);
+	}
+
+	setup_destroy(na);
+	btd_adapter_unref(na->adapter);
+	g_free(na);
+}
+
+static void server_free(void *data)
+{
+	struct network_server *ns = data;
+
+	if (!ns)
+		return;
+
+	server_remove_sessions(ns);
+
+	if (ns->record_id)
+		adapter_service_remove(ns->na->adapter, ns->record_id);
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), ns->watch_id);
+	g_free(ns->name);
+	g_free(ns->bridge);
+
+	g_free(ns);
+}
+
+static void path_unregister(void *data)
+{
+	struct network_adapter *na = data;
+
+	DBG("Unregistered interface %s on path %s",
+		NETWORK_SERVER_INTERFACE, adapter_get_path(na->adapter));
+
+	g_slist_free_full(na->servers, server_free);
+
+	adapters = g_slist_remove(adapters, na);
+	adapter_free(na);
+}
+
+static const GDBusMethodTable server_methods[] = {
+	{ GDBUS_METHOD("Register",
+			GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL,
+			register_server) },
+	{ GDBUS_METHOD("Unregister",
+			GDBUS_ARGS({ "uuid", "s" }), NULL,
+			unregister_server) },
+	{ }
+};
+
+static struct network_adapter *create_adapter(struct btd_adapter *adapter)
+{
+	struct network_adapter *na;
+	GError *err = NULL;
+
+	na = g_new0(struct network_adapter, 1);
+	na->adapter = btd_adapter_ref(adapter);
+
+	na->io = bt_io_listen(NULL, confirm_event, na, NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR,
+				btd_adapter_get_address(adapter),
+				BT_IO_OPT_PSM, BNEP_PSM,
+				BT_IO_OPT_OMTU, BNEP_MTU,
+				BT_IO_OPT_IMTU, BNEP_MTU,
+				BT_IO_OPT_SEC_LEVEL,
+				security ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+	if (!na->io) {
+		error("%s", err->message);
+		g_error_free(err);
+		adapter_free(na);
+		return NULL;
+	}
+
+	return na;
+}
+
+int server_register(struct btd_adapter *adapter, uint16_t id)
+{
+	struct network_adapter *na;
+	struct network_server *ns;
+	const char *path;
+
+	na = find_adapter(adapters, adapter);
+	if (!na) {
+		na = create_adapter(adapter);
+		if (!na)
+			return -EINVAL;
+		adapters = g_slist_append(adapters, na);
+	}
+
+	ns = find_server(na->servers, id);
+	if (ns)
+		return 0;
+
+	ns = g_new0(struct network_server, 1);
+
+	ns->name = g_strdup("Network service");
+
+	path = adapter_get_path(adapter);
+
+	if (g_slist_length(na->servers) > 0)
+		goto done;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(), path,
+						NETWORK_SERVER_INTERFACE,
+						server_methods, NULL, NULL, na,
+						path_unregister)) {
+		error("D-Bus failed to register %s interface",
+						NETWORK_SERVER_INTERFACE);
+		server_free(ns);
+		return -1;
+	}
+
+	DBG("Registered interface %s on path %s", NETWORK_SERVER_INTERFACE,
+									path);
+
+done:
+	bacpy(&ns->src, btd_adapter_get_address(adapter));
+	ns->id = id;
+	ns->na = na;
+	ns->record_id = 0;
+	na->servers = g_slist_append(na->servers, ns);
+
+	return 0;
+}
+
+int server_unregister(struct btd_adapter *adapter, uint16_t id)
+{
+	struct network_adapter *na;
+	struct network_server *ns;
+
+	na = find_adapter(adapters, adapter);
+	if (!na)
+		return -EINVAL;
+
+	ns = find_server(na->servers, id);
+	if (!ns)
+		return -EINVAL;
+
+	na->servers = g_slist_remove(na->servers, ns);
+	server_free(ns);
+
+	if (g_slist_length(na->servers) > 0)
+		return 0;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						adapter_get_path(adapter),
+						NETWORK_SERVER_INTERFACE);
+
+	return 0;
+}
diff --git a/bluez/repo/profiles/network/server.h b/bluez/repo/profiles/network/server.h
new file mode 100644
index 0000000..a76e6f7
--- /dev/null
+++ b/bluez/repo/profiles/network/server.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+int server_init(gboolean secure);
+int server_register(struct btd_adapter *adapter, uint16_t id);
+int server_unregister(struct btd_adapter *adapter, uint16_t id);
diff --git a/bluez/repo/profiles/proximity/._immalert.c b/bluez/repo/profiles/proximity/._immalert.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._immalert.c
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._immalert.h b/bluez/repo/profiles/proximity/._immalert.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._immalert.h
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._linkloss.c b/bluez/repo/profiles/proximity/._linkloss.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._linkloss.c
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._linkloss.h b/bluez/repo/profiles/proximity/._linkloss.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._linkloss.h
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._main.c b/bluez/repo/profiles/proximity/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._main.c
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._manager.c b/bluez/repo/profiles/proximity/._manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._manager.c
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._manager.h b/bluez/repo/profiles/proximity/._manager.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._manager.h
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._monitor.c b/bluez/repo/profiles/proximity/._monitor.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._monitor.c
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._monitor.h b/bluez/repo/profiles/proximity/._monitor.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._monitor.h
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._proximity.conf b/bluez/repo/profiles/proximity/._proximity.conf
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._proximity.conf
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._reporter.c b/bluez/repo/profiles/proximity/._reporter.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._reporter.c
Binary files differ
diff --git a/bluez/repo/profiles/proximity/._reporter.h b/bluez/repo/profiles/proximity/._reporter.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/proximity/._reporter.h
Binary files differ
diff --git a/bluez/repo/profiles/proximity/immalert.c b/bluez/repo/profiles/proximity/immalert.c
new file mode 100644
index 0000000..26a0ac9
--- /dev/null
+++ b/bluez/repo/profiles/proximity/immalert.c
@@ -0,0 +1,292 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  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 <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+	struct btd_adapter *adapter;
+	GSList *connected_devices;
+};
+
+struct connected_device {
+	struct btd_device *device;
+	struct imm_alert_adapter *adapter;
+	uint8_t alert_level;
+	guint callback_id;
+};
+
+static GSList *imm_alert_adapters;
+
+static int imdevice_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct connected_device *condev = a;
+	const struct btd_device *device = b;
+
+	if (condev->device == device)
+		return 0;
+
+	return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
+{
+	GSList *l = g_slist_find_custom(ia->connected_devices, device,
+								imdevice_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static int imadapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct imm_alert_adapter *imadapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (imadapter->adapter == adapter)
+		return 0;
+
+	return -1;
+}
+
+static struct imm_alert_adapter *
+find_imm_alert_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
+								imadapter_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+const char *imm_alert_get_level(struct btd_device *device)
+{
+	struct imm_alert_adapter *imadapter;
+	struct connected_device *condev;
+
+	if (!device)
+		return get_alert_level_string(NO_ALERT);
+
+	imadapter = find_imm_alert_adapter(device_get_adapter(device));
+	if (!imadapter)
+		return get_alert_level_string(NO_ALERT);
+
+	condev = find_connected_device(imadapter, device);
+	if (!condev)
+		return get_alert_level_string(NO_ALERT);
+
+	return get_alert_level_string(condev->alert_level);
+}
+
+static void imm_alert_emit_alert_signal(struct connected_device *condev,
+							uint8_t alert_level)
+{
+	const char *path, *alert_level_str;
+
+	if (!condev)
+		return;
+
+	path = device_get_path(condev->device);
+	alert_level_str = get_alert_level_string(alert_level);
+
+	DBG("alert %s remote %s", alert_level_str, path);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+			PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel");
+}
+
+static void imm_alert_remove_condev(struct connected_device *condev)
+{
+	struct imm_alert_adapter *ia;
+
+	if (!condev)
+		return;
+
+	ia = condev->adapter;
+
+	if (condev->callback_id && condev->device)
+		btd_device_remove_attio_callback(condev->device,
+							condev->callback_id);
+
+	if (condev->device)
+		btd_device_unref(condev->device);
+
+	ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
+	g_free(condev);
+}
+
+/* condev can be NULL */
+static void imm_alert_disc_cb(gpointer user_data)
+{
+	struct connected_device *condev = user_data;
+
+	if (!condev)
+		return;
+
+	DBG("immediate alert remove device %p", condev->device);
+
+	imm_alert_emit_alert_signal(condev, NO_ALERT);
+	imm_alert_remove_condev(condev);
+}
+
+static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
+				struct btd_device *device, gpointer user_data)
+{
+	uint8_t value;
+	struct imm_alert_adapter *ia = user_data;
+	struct connected_device *condev = NULL;
+
+	if (!device)
+		goto set_error;
+
+	condev = find_connected_device(ia, device);
+
+	if (a->len == 0) {
+		DBG("Illegal alert level length");
+		goto set_error;
+	}
+
+	value = a->data[0];
+	if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+		DBG("Illegal alert value");
+		goto set_error;
+	}
+
+	/* Register a disconnect cb if the alert level is non-zero */
+	if (value != NO_ALERT && !condev) {
+		condev = g_new0(struct connected_device, 1);
+		condev->device = btd_device_ref(device);
+		condev->adapter = ia;
+		condev->callback_id = btd_device_add_attio_callback(device,
+					NULL, imm_alert_disc_cb, condev);
+		ia->connected_devices = g_slist_append(ia->connected_devices,
+								condev);
+		DBG("added connected dev %p", device);
+	}
+
+	if (value != NO_ALERT) {
+		condev->alert_level = value;
+		imm_alert_emit_alert_signal(condev, value);
+	}
+
+	/*
+	 * Emit NO_ALERT if the alert level was non-zero before. This is
+	 * guaranteed when there's a condev.
+	 */
+	if (value == NO_ALERT && condev)
+		imm_alert_disc_cb(condev);
+
+	DBG("alert level set to %d by device %p", value, device);
+	return 0;
+
+set_error:
+	error("Set immediate alert level for dev %p", device);
+	/* remove alerts by erroneous devices */
+	imm_alert_disc_cb(condev);
+	return ATT_ECODE_IO;
+}
+
+void imm_alert_register(struct btd_adapter *adapter)
+{
+	gboolean svc_added;
+	bt_uuid_t uuid;
+	struct imm_alert_adapter *imadapter;
+
+	bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+
+	imadapter = g_new0(struct imm_alert_adapter, 1);
+	imadapter->adapter = adapter;
+
+	imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+
+	/* Immediate Alert Service */
+	svc_added = gatt_service_add(adapter,
+				GATT_PRIM_SVC_UUID, &uuid,
+				/* Alert level characteristic */
+				GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
+				GATT_OPT_CHR_PROPS,
+					GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+				GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+					imm_alert_alert_lvl_write, imadapter,
+				GATT_OPT_INVALID);
+
+	if (!svc_added) {
+		imm_alert_unregister(adapter);
+		return;
+	}
+
+	DBG("Immediate Alert service added");
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+	struct connected_device *condev = data;
+
+	imm_alert_remove_condev(condev);
+}
+
+void imm_alert_unregister(struct btd_adapter *adapter)
+{
+	struct imm_alert_adapter *imadapter;
+
+	imadapter = find_imm_alert_adapter(adapter);
+	if (!imadapter)
+		return;
+
+	g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+									NULL);
+
+	imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+	g_free(imadapter);
+}
diff --git a/bluez/repo/profiles/proximity/immalert.h b/bluez/repo/profiles/proximity/immalert.h
new file mode 100644
index 0000000..1a09fa9
--- /dev/null
+++ b/bluez/repo/profiles/proximity/immalert.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Texas Instruments Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
diff --git a/bluez/repo/profiles/proximity/linkloss.c b/bluez/repo/profiles/proximity/linkloss.c
new file mode 100644
index 0000000..2f1ab13
--- /dev/null
+++ b/bluez/repo/profiles/proximity/linkloss.c
@@ -0,0 +1,340 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  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 <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "attrib/att-database.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/service.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#include "reporter.h"
+#include "linkloss.h"
+
+struct link_loss_adapter {
+	struct btd_adapter *adapter;
+	uint16_t alert_lvl_value_handle;
+	GSList *connected_devices;
+};
+
+struct connected_device {
+	struct btd_device *device;
+	struct link_loss_adapter *adapter;
+	uint8_t alert_level;
+	guint callback_id;
+	guint local_disc_id;
+};
+
+static GSList *link_loss_adapters;
+
+static int lldevice_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct connected_device *llcondev = a;
+	const struct btd_device *device = b;
+
+	if (llcondev->device == device)
+		return 0;
+
+	return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct link_loss_adapter *la, struct btd_device *device)
+{
+	GSList *l = g_slist_find_custom(la->connected_devices, device,
+								lldevice_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static int lladapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct link_loss_adapter *lladapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (lladapter->adapter == adapter)
+		return 0;
+
+	return -1;
+}
+
+static struct link_loss_adapter *
+find_link_loss_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(link_loss_adapters, adapter,
+							lladapter_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+const char *link_loss_get_alert_level(struct btd_device *device)
+{
+	struct link_loss_adapter *lladapter;
+	struct connected_device *condev;
+
+	if (!device)
+		return get_alert_level_string(NO_ALERT);
+
+	lladapter = find_link_loss_adapter(device_get_adapter(device));
+	if (!lladapter)
+		return get_alert_level_string(NO_ALERT);
+
+	condev = find_connected_device(lladapter, device);
+	if (!condev)
+		return get_alert_level_string(NO_ALERT);
+
+	return get_alert_level_string(condev->alert_level);
+}
+
+static void link_loss_emit_alert_signal(struct connected_device *condev)
+{
+	const char *alert_level_str, *path;
+
+	if (!condev->device)
+		return;
+
+	path = device_get_path(condev->device);
+	alert_level_str = get_alert_level_string(condev->alert_level);
+
+	DBG("alert %s remote %s", alert_level_str, path);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+			PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel");
+}
+
+static uint8_t link_loss_alert_lvl_read(struct attribute *a,
+				struct btd_device *device, gpointer user_data)
+{
+	struct link_loss_adapter *la = user_data;
+	struct connected_device *condev;
+	uint8_t alert_level = NO_ALERT;
+
+	if (!device)
+		goto out;
+
+	condev = find_connected_device(la, device);
+	if (!condev)
+		goto out;
+
+	alert_level = condev->alert_level;
+
+out:
+	DBG("return alert level %d for dev %p", alert_level, device);
+
+	/* update the alert level according to the requesting device */
+	attrib_db_update(la->adapter, a->handle, NULL, &alert_level,
+						sizeof(alert_level), NULL);
+
+	return 0;
+}
+
+/* condev can be NULL */
+static void link_loss_remove_condev(struct connected_device *condev)
+{
+	struct link_loss_adapter *la;
+
+	if (!condev)
+		return;
+
+	la = condev->adapter;
+
+	if (condev->callback_id && condev->device)
+		btd_device_remove_attio_callback(condev->device,
+							condev->callback_id);
+
+	if (condev->local_disc_id && condev->device)
+		device_remove_disconnect_watch(condev->device,
+							condev->local_disc_id);
+
+	if (condev->device)
+		btd_device_unref(condev->device);
+
+	la->connected_devices = g_slist_remove(la->connected_devices, condev);
+	g_free(condev);
+}
+
+static void link_loss_disc_cb(gpointer user_data)
+{
+	struct connected_device *condev = user_data;
+
+	DBG("alert loss disconnect device %p", condev->device);
+
+	/* if an alert-level is set, emit a signal */
+	if (condev->alert_level != NO_ALERT)
+		link_loss_emit_alert_signal(condev);
+
+	/* we are open for more changes now */
+	link_loss_remove_condev(condev);
+}
+
+static void link_loss_local_disc(struct btd_device *device,
+					gboolean removal, void *user_data)
+{
+	struct connected_device *condev = user_data;
+
+	/* no need to alert on this device - we requested disconnection */
+	link_loss_remove_condev(condev);
+
+	DBG("alert level zeroed for locally disconnecting dev %p", device);
+}
+
+static uint8_t link_loss_alert_lvl_write(struct attribute *a,
+				struct btd_device *device, gpointer user_data)
+{
+	uint8_t value;
+	struct link_loss_adapter *la = user_data;
+	struct connected_device *condev = NULL;
+
+	if (!device)
+		goto set_error;
+
+	/* condev might remain NULL here if nothing is found */
+	condev = find_connected_device(la, device);
+
+	if (a->len == 0) {
+		DBG("Illegal alert level length");
+		goto set_error;
+	}
+
+	value = a->data[0];
+	if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+		DBG("Illegal alert value");
+		goto set_error;
+	}
+
+	/* Register a disconnect cb if the alert level is non-zero */
+	if (value != NO_ALERT && !condev) {
+		condev = g_new0(struct connected_device, 1);
+		condev->device = btd_device_ref(device);
+		condev->adapter = la;
+		condev->callback_id = btd_device_add_attio_callback(device,
+					NULL, link_loss_disc_cb, condev);
+		condev->local_disc_id = device_add_disconnect_watch(device,
+					link_loss_local_disc, condev, NULL);
+
+		la->connected_devices = g_slist_append(la->connected_devices,
+								condev);
+	} else if (value == NO_ALERT && condev) {
+		link_loss_remove_condev(condev);
+		condev = NULL;
+	}
+
+	DBG("alert level set to %d by device %p", value, device);
+
+	if (condev)
+		condev->alert_level = value;
+
+	return 0;
+
+set_error:
+	error("Set link loss alert level for dev %p", device);
+	/* reset alert level on erroneous devices */
+	link_loss_remove_condev(condev);
+	return ATT_ECODE_IO;
+}
+
+void link_loss_register(struct btd_adapter *adapter)
+{
+	gboolean svc_added;
+	bt_uuid_t uuid;
+	struct link_loss_adapter *lladapter;
+
+	bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID);
+
+	lladapter = g_new0(struct link_loss_adapter, 1);
+	lladapter->adapter = adapter;
+
+	link_loss_adapters = g_slist_append(link_loss_adapters, lladapter);
+
+	/* Link Loss Service */
+	svc_added = gatt_service_add(adapter,
+			GATT_PRIM_SVC_UUID, &uuid,
+			/* Alert level characteristic */
+			GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
+			GATT_OPT_CHR_PROPS,
+				GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+				link_loss_alert_lvl_read, lladapter,
+			GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+				link_loss_alert_lvl_write, lladapter,
+			GATT_OPT_CHR_VALUE_GET_HANDLE,
+				&lladapter->alert_lvl_value_handle,
+			GATT_OPT_INVALID);
+
+	if (!svc_added)
+		goto err;
+
+	DBG("Link Loss service added");
+	return;
+
+err:
+	error("Error adding Link Loss service");
+	link_loss_unregister(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+	struct connected_device *condev = data;
+
+	link_loss_remove_condev(condev);
+}
+
+void link_loss_unregister(struct btd_adapter *adapter)
+{
+	struct link_loss_adapter *lladapter;
+	lladapter = find_link_loss_adapter(adapter);
+	if (!lladapter)
+		return;
+
+	g_slist_foreach(lladapter->connected_devices, remove_condev_list_item,
+			NULL);
+
+	link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter);
+	g_free(lladapter);
+}
diff --git a/bluez/repo/profiles/proximity/linkloss.h b/bluez/repo/profiles/proximity/linkloss.h
new file mode 100644
index 0000000..0447def
--- /dev/null
+++ b/bluez/repo/profiles/proximity/linkloss.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Texas Instruments Corporation
+ *
+ *
+ *  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
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
diff --git a/bluez/repo/profiles/proximity/main.c b/bluez/repo/profiles/proximity/main.c
new file mode 100644
index 0000000..38a51f1
--- /dev/null
+++ b/bluez/repo/profiles/proximity/main.c
@@ -0,0 +1,81 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/plugin.h"
+#include "manager.h"
+
+static GKeyFile *config = NULL;
+
+static GKeyFile *open_config_file(const char *file)
+{
+	GError *gerr = NULL;
+	GKeyFile *keyfile;
+
+	keyfile = g_key_file_new();
+
+	g_key_file_set_list_separator(keyfile, ',');
+
+	if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) {
+		if (!g_error_matches(gerr, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			error("Parsing %s failed: %s", file, gerr->message);
+		g_error_free(gerr);
+		g_key_file_free(keyfile);
+		return NULL;
+	}
+
+	return keyfile;
+}
+
+static int proximity_init(void)
+{
+	config = open_config_file(CONFIGDIR "/proximity.conf");
+
+	if (proximity_manager_init(config) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static void proximity_exit(void)
+{
+	if (config)
+		g_key_file_free(config);
+
+	proximity_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+			proximity_init, proximity_exit)
diff --git a/bluez/repo/profiles/proximity/manager.c b/bluez/repo/profiles/proximity/manager.c
new file mode 100644
index 0000000..dbb3bda
--- /dev/null
+++ b/bluez/repo/profiles/proximity/manager.c
@@ -0,0 +1,196 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "monitor.h"
+#include "reporter.h"
+#include "manager.h"
+
+static struct enabled enabled  = {
+	.linkloss = TRUE,
+	.pathloss = TRUE,
+	.findme = TRUE,
+};
+
+static int monitor_linkloss_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *linkloss;
+
+	linkloss = btd_device_get_primary(device, LINK_LOSS_UUID);
+	if (linkloss == NULL)
+		return -1;
+
+	return monitor_register_linkloss(device, &enabled, linkloss);
+}
+
+static int monitor_immediate_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *immediate;
+
+	immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID);
+	if (immediate == NULL)
+		return -1;
+
+	return monitor_register_immediate(device, &enabled, immediate);
+}
+
+static int monitor_txpower_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *txpower;
+
+	txpower = btd_device_get_primary(device, TX_POWER_UUID);
+	if (txpower == NULL)
+		return -1;
+
+	return monitor_register_txpower(device, &enabled, txpower);
+}
+
+static void monitor_linkloss_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	monitor_unregister_linkloss(device);
+}
+
+static void monitor_immediate_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	monitor_unregister_immediate(device);
+}
+
+static void monitor_txpower_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	monitor_unregister_txpower(device);
+}
+
+static struct btd_profile pxp_monitor_linkloss_profile = {
+	.name		= "proximity-linkloss",
+	.remote_uuid	= LINK_LOSS_UUID,
+	.device_probe	= monitor_linkloss_probe,
+	.device_remove	= monitor_linkloss_remove,
+};
+
+static struct btd_profile pxp_monitor_immediate_profile = {
+	.name		= "proximity-immediate",
+	.remote_uuid	= IMMEDIATE_ALERT_UUID,
+	.device_probe	= monitor_immediate_probe,
+	.device_remove	= monitor_immediate_remove,
+};
+
+static struct btd_profile pxp_monitor_txpower_profile = {
+	.name		= "proximity-txpower",
+	.remote_uuid	= TX_POWER_UUID,
+	.device_probe	= monitor_txpower_probe,
+	.device_remove	= monitor_txpower_remove,
+};
+
+static struct btd_profile pxp_reporter_profile = {
+	.name		= "Proximity Reporter GATT Driver",
+	.remote_uuid	= GATT_UUID,
+	.device_probe	= reporter_device_probe,
+	.device_remove	= reporter_device_remove,
+
+	.adapter_probe	= reporter_adapter_probe,
+	.adapter_remove	= reporter_adapter_remove,
+};
+
+static void load_config_file(GKeyFile *config)
+{
+	char **list;
+	int i;
+
+	if (config == NULL)
+		return;
+
+	list = g_key_file_get_string_list(config, "General", "Disable",
+								NULL, NULL);
+	for (i = 0; list && list[i] != NULL; i++) {
+		if (g_str_equal(list[i], "FindMe"))
+			enabled.findme = FALSE;
+		else if (g_str_equal(list[i], "LinkLoss"))
+			enabled.linkloss = FALSE;
+		else if (g_str_equal(list[i], "PathLoss"))
+			enabled.pathloss = FALSE;
+	}
+
+	g_strfreev(list);
+}
+
+int proximity_manager_init(GKeyFile *config)
+{
+	load_config_file(config);
+
+	if (btd_profile_register(&pxp_monitor_linkloss_profile) < 0)
+		goto fail;
+
+	if (btd_profile_register(&pxp_monitor_immediate_profile) < 0)
+		goto fail;
+
+	if (btd_profile_register(&pxp_monitor_txpower_profile) < 0)
+		goto fail;
+
+	if (btd_profile_register(&pxp_reporter_profile) < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	proximity_manager_exit();
+
+	return -1;
+}
+
+void proximity_manager_exit(void)
+{
+	btd_profile_unregister(&pxp_reporter_profile);
+	btd_profile_unregister(&pxp_monitor_txpower_profile);
+	btd_profile_unregister(&pxp_monitor_immediate_profile);
+	btd_profile_unregister(&pxp_monitor_linkloss_profile);
+}
diff --git a/bluez/repo/profiles/proximity/manager.h b/bluez/repo/profiles/proximity/manager.h
new file mode 100644
index 0000000..e65c31d
--- /dev/null
+++ b/bluez/repo/profiles/proximity/manager.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int proximity_manager_init(GKeyFile *conf);
+void proximity_manager_exit(void);
diff --git a/bluez/repo/profiles/proximity/monitor.c b/bluez/repo/profiles/proximity/monitor.c
new file mode 100644
index 0000000..a583eb7
--- /dev/null
+++ b/bluez/repo/profiles/proximity/monitor.c
@@ -0,0 +1,822 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/textfile.h"
+
+#include "monitor.h"
+
+#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1"
+
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+#define IMMEDIATE_TIMEOUT	5
+#define TX_POWER_SIZE		1
+
+enum {
+	ALERT_NONE = 0,
+	ALERT_MILD,
+	ALERT_HIGH,
+};
+
+struct monitor {
+	struct btd_device *device;
+	GAttrib *attrib;
+	struct att_range *linkloss;
+	struct att_range *txpower;
+	struct att_range *immediate;
+	struct enabled enabled;
+	char *linklosslevel;		/* Link Loss Alert Level */
+	char *fallbacklevel;		/* Immediate fallback alert level */
+	char *immediatelevel;		/* Immediate Alert Level */
+	char *signallevel;		/* Path Loss RSSI level */
+	uint16_t linklosshandle;	/* Link Loss Characteristic
+					 * Value Handle */
+	uint16_t txpowerhandle;		/* Tx Characteristic Value Handle */
+	uint16_t immediatehandle;	/* Immediate Alert Value Handle */
+	guint immediateto;		/* Reset Immediate Alert to "none" */
+	guint attioid;
+};
+
+static GSList *monitors = NULL;
+
+static struct monitor *find_monitor(struct btd_device *device)
+{
+	GSList *l;
+
+	for (l = monitors; l; l = l->next) {
+		struct monitor *monitor = l->data;
+
+		if (monitor->device == device)
+			return monitor;
+	}
+
+	return NULL;
+}
+
+static void write_proximity_config(struct btd_device *device, const char *alert,
+					const char *level)
+{
+	char *filename;
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+
+	filename = btd_device_get_storage_path(device, "proximity");
+	if (!filename) {
+		warn("Unable to get proximity storage path for device");
+		return;
+	}
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	if (level)
+		g_key_file_set_string(key_file, alert, "Level", level);
+	else
+		g_key_file_remove_group(key_file, alert, NULL);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+	g_free(filename);
+	g_key_file_free(key_file);
+}
+
+static char *read_proximity_config(struct btd_device *device, const char *alert)
+{
+	char *filename;
+	GKeyFile *key_file;
+	char *str;
+
+	filename = btd_device_get_storage_path(device, "proximity");
+	if (!filename) {
+		warn("Unable to get proximity storage path for device");
+		return NULL;
+	}
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	str = g_key_file_get_string(key_file, alert, "Level", NULL);
+
+	g_free(filename);
+	g_key_file_free(key_file);
+
+	return str;
+}
+
+static uint8_t str2level(const char *level)
+{
+	if (g_strcmp0("high", level) == 0)
+		return ALERT_HIGH;
+	else if (g_strcmp0("mild", level) == 0)
+		return ALERT_MILD;
+
+	return ALERT_NONE;
+}
+
+static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	struct monitor *monitor = user_data;
+	struct btd_device *device = monitor->device;
+	const char *path = device_get_path(device);
+
+	if (status != 0) {
+		error("Link Loss Write Request failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	if (!dec_write_resp(pdu, plen)) {
+		error("Link Loss Write Request: protocol error");
+		return;
+	}
+
+	DBG("Link Loss Alert Level written");
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+				PROXIMITY_INTERFACE, "LinkLossAlertLevel");
+}
+
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct monitor *monitor = user_data;
+	struct gatt_char *chr;
+	uint8_t value = str2level(monitor->linklosslevel);
+
+	if (status) {
+		error("Discover Link Loss handle: %s", att_ecode2str(status));
+		return;
+	}
+
+	DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
+
+	/* Assume there is a single Alert Level characteristic */
+	chr = characteristics->data;
+	monitor->linklosshandle = chr->value_handle;
+
+	gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
+						linkloss_written, monitor);
+}
+
+static int write_alert_level(struct monitor *monitor)
+{
+	struct att_range *linkloss = monitor->linkloss;
+	bt_uuid_t uuid;
+
+	if (monitor->linklosshandle) {
+		uint8_t value = str2level(monitor->linklosslevel);
+
+		gatt_write_char(monitor->attrib, monitor->linklosshandle,
+					&value, 1, linkloss_written, monitor);
+		return 0;
+	}
+
+	bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+	/* FIXME: use cache (requires service changed support) ? */
+	gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
+					&uuid, char_discovered_cb, monitor);
+
+	return 0;
+}
+
+static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+							gpointer user_data)
+{
+	uint8_t value[TX_POWER_SIZE];
+	ssize_t vlen;
+
+	if (status != 0) {
+		DBG("Tx Power Level read failed: %s", att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+	if (vlen < 0) {
+		DBG("Protocol error");
+		return;
+	}
+
+	if (vlen != 1) {
+		DBG("Invalid length for TX Power value: %zd", vlen);
+		return;
+	}
+
+	DBG("Tx Power Level: %02x", (int8_t) value[0]);
+}
+
+static void tx_power_handle_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct monitor *monitor = user_data;
+	struct gatt_char *chr;
+
+	if (status) {
+		error("Discover Tx Power handle: %s", att_ecode2str(status));
+		return;
+	}
+
+	chr = characteristics->data;
+	monitor->txpowerhandle = chr->value_handle;
+
+	DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
+
+	gatt_read_char(monitor->attrib, monitor->txpowerhandle,
+							tx_power_read_cb, monitor);
+}
+
+static void read_tx_power(struct monitor *monitor)
+{
+	struct att_range *txpower = monitor->txpower;
+	bt_uuid_t uuid;
+
+	if (monitor->txpowerhandle != 0) {
+		gatt_read_char(monitor->attrib, monitor->txpowerhandle,
+						tx_power_read_cb, monitor);
+		return;
+	}
+
+	bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+
+	gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
+				&uuid, tx_power_handle_cb, monitor);
+}
+
+static gboolean immediate_timeout(gpointer user_data)
+{
+	struct monitor *monitor = user_data;
+	const char *path = device_get_path(monitor->device);
+
+	monitor->immediateto = 0;
+
+	if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+		return FALSE;
+
+	if (monitor->attrib) {
+		uint8_t value = ALERT_NONE;
+		gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
+				&value, 1, NULL, NULL);
+	}
+
+	g_free(monitor->immediatelevel);
+	monitor->immediatelevel = g_strdup("none");
+
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+				PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+
+	return FALSE;
+}
+
+static void immediate_written(gpointer user_data)
+{
+	struct monitor *monitor = user_data;
+	const char *path = device_get_path(monitor->device);
+
+	g_free(monitor->fallbacklevel);
+	monitor->fallbacklevel = NULL;
+
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+				PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+
+	monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
+						immediate_timeout, monitor);
+}
+
+static void write_immediate_alert(struct monitor *monitor)
+{
+	uint8_t value = str2level(monitor->immediatelevel);
+
+	gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
+						immediate_written, monitor);
+}
+
+static void immediate_handle_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct monitor *monitor = user_data;
+	struct gatt_char *chr;
+
+	if (status) {
+		error("Discover Immediate Alert handle: %s",
+						att_ecode2str(status));
+		return;
+	}
+
+	chr = characteristics->data;
+	monitor->immediatehandle = chr->value_handle;
+
+	DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
+
+	if (monitor->fallbacklevel)
+		write_immediate_alert(monitor);
+}
+
+static void discover_immediate_handle(struct monitor *monitor)
+{
+	struct att_range *immediate = monitor->immediate;
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+	gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
+					&uuid, immediate_handle_cb, monitor);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct monitor *monitor = user_data;
+
+	monitor->attrib = g_attrib_ref(attrib);
+
+	if (monitor->enabled.linkloss)
+		write_alert_level(monitor);
+
+	if (monitor->enabled.pathloss)
+		read_tx_power(monitor);
+
+	if (monitor->immediatehandle == 0) {
+		if(monitor->enabled.pathloss || monitor->enabled.findme)
+			discover_immediate_handle(monitor);
+	} else if (monitor->fallbacklevel)
+		write_immediate_alert(monitor);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct monitor *monitor = user_data;
+	const char *path = device_get_path(monitor->device);
+
+	g_attrib_unref(monitor->attrib);
+	monitor->attrib = NULL;
+
+	if (monitor->immediateto == 0)
+		return;
+
+	g_source_remove(monitor->immediateto);
+	monitor->immediateto = 0;
+
+	if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+		return;
+
+	g_free(monitor->immediatelevel);
+	monitor->immediatelevel = g_strdup("none");
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+				PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+}
+
+static gboolean level_is_valid(const char *level)
+{
+	return (g_str_equal("none", level) ||
+			g_str_equal("mild", level) ||
+			g_str_equal("high", level));
+}
+
+static gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct monitor *monitor = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+						&monitor->linklosslevel);
+
+	return TRUE;
+}
+
+static void property_set_link_loss_level(const GDBusPropertyTable *property,
+		DBusMessageIter *iter, GDBusPendingPropertySet id, void *data)
+{
+	struct monitor *monitor = data;
+	const char *level;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &level);
+
+	if (!level_is_valid(level)) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	if (g_strcmp0(monitor->linklosslevel, level) == 0)
+		goto done;
+
+	g_free(monitor->linklosslevel);
+	monitor->linklosslevel = g_strdup(level);
+
+	write_proximity_config(monitor->device, "LinkLossAlertLevel", level);
+
+	if (monitor->attrib)
+		write_alert_level(monitor);
+
+done:
+	g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_link_loss_level(
+				const GDBusPropertyTable *property, void *data)
+{
+	struct monitor *monitor = data;
+
+	if (!monitor->enabled.linkloss)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean property_get_immediate_alert_level(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct monitor *monitor = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+						&monitor->immediatelevel);
+
+	return TRUE;
+}
+
+static void property_set_immediate_alert_level(
+		const GDBusPropertyTable *property, DBusMessageIter *iter,
+		GDBusPendingPropertySet id, void *data)
+{
+	struct monitor *monitor = data;
+	struct btd_device *device = monitor->device;
+	const char *level;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &level);
+
+	if (!level_is_valid(level)) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	if (g_strcmp0(monitor->immediatelevel, level) == 0)
+		goto done;
+
+	if (monitor->immediateto) {
+		g_source_remove(monitor->immediateto);
+		monitor->immediateto = 0;
+	}
+
+	/* Previous Immediate Alert level if connection/write fails */
+	g_free(monitor->fallbacklevel);
+	monitor->fallbacklevel = monitor->immediatelevel;
+
+	monitor->immediatelevel = g_strdup(level);
+
+	/*
+	 * Means that Link/Path Loss are disabled or there is a pending
+	 * writting for Find Me(Immediate Alert characteristic value).
+	 * If enabled, Path Loss always registers a connection callback
+	 * when the Proximity Monitor starts.
+	 */
+	if (monitor->attioid == 0)
+		monitor->attioid = btd_device_add_attio_callback(device,
+							attio_connected_cb,
+							attio_disconnected_cb,
+							monitor);
+	else if (monitor->attrib)
+		write_immediate_alert(monitor);
+
+done:
+	g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_immediate_alert_level(
+				const GDBusPropertyTable *property, void *data)
+{
+	struct monitor *monitor = data;
+
+	if (!(monitor->enabled.findme || monitor->enabled.pathloss))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean property_get_signal_level(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct monitor *monitor = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+						&monitor->signallevel);
+
+	return TRUE;
+}
+
+static gboolean property_exists_signal_level(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct monitor *monitor = data;
+
+	if (!monitor->enabled.pathloss)
+		return FALSE;
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable monitor_device_properties[] = {
+	{ "LinkLossAlertLevel", "s", property_get_link_loss_level,
+					property_set_link_loss_level,
+					property_exists_link_loss_level },
+	{ "ImmediateAlertLevel", "s", property_get_immediate_alert_level,
+					property_set_immediate_alert_level,
+					property_exists_immediate_alert_level },
+	{ "SignalLevel", "s", property_get_signal_level, NULL,
+					property_exists_signal_level },
+	{ }
+};
+
+static void monitor_destroy(gpointer user_data)
+{
+	struct monitor *monitor = user_data;
+
+	monitors = g_slist_remove(monitors, monitor);
+
+	btd_device_unref(monitor->device);
+	g_free(monitor->linklosslevel);
+	g_free(monitor->immediatelevel);
+	g_free(monitor->signallevel);
+	g_free(monitor);
+}
+
+static struct monitor *register_monitor(struct btd_device *device)
+{
+	const char *path = device_get_path(device);
+	struct monitor *monitor;
+	char *level;
+
+	monitor = find_monitor(device);
+	if (monitor != NULL)
+		return monitor;
+
+	level = read_proximity_config(device, "LinkLossAlertLevel");
+
+	monitor = g_new0(struct monitor, 1);
+	monitor->device = btd_device_ref(device);
+	monitor->linklosslevel = (level ? : g_strdup("high"));
+	monitor->signallevel = g_strdup("unknown");
+	monitor->immediatelevel = g_strdup("none");
+
+	monitors = g_slist_append(monitors, monitor);
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(), path,
+				PROXIMITY_INTERFACE,
+				NULL, NULL, monitor_device_properties,
+				monitor, monitor_destroy) == FALSE) {
+		error("D-Bus failed to register %s interface",
+						PROXIMITY_INTERFACE);
+		monitor_destroy(monitor);
+		return NULL;
+	}
+
+	DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
+
+	return monitor;
+}
+
+static void update_monitor(struct monitor *monitor)
+{
+	if (monitor->txpower != NULL && monitor->immediate != NULL)
+		monitor->enabled.pathloss = TRUE;
+	else
+		monitor->enabled.pathloss = FALSE;
+
+	DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
+				monitor->enabled.linkloss ? "TRUE" : "FALSE",
+				monitor->enabled.pathloss ? "TRUE" : "FALSE",
+				monitor->enabled.findme ? "TRUE" : "FALSE");
+
+	if (!monitor->enabled.linkloss && !monitor->enabled.pathloss)
+		return;
+
+	if (monitor->attioid != 0)
+		return;
+
+	monitor->attioid = btd_device_add_attio_callback(monitor->device,
+							attio_connected_cb,
+							attio_disconnected_cb,
+							monitor);
+}
+
+int monitor_register_linkloss(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *linkloss)
+{
+	struct monitor *monitor;
+
+	if (!enabled->linkloss)
+		return 0;
+
+	monitor = register_monitor(device);
+	if (monitor == NULL)
+		return -1;
+
+	monitor->linkloss = g_new0(struct att_range, 1);
+	monitor->linkloss->start = linkloss->range.start;
+	monitor->linkloss->end = linkloss->range.end;
+	monitor->enabled.linkloss = TRUE;
+
+	update_monitor(monitor);
+
+	return 0;
+}
+
+int monitor_register_txpower(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *txpower)
+{
+	struct monitor *monitor;
+
+	if (!enabled->pathloss)
+		return 0;
+
+	monitor = register_monitor(device);
+	if (monitor == NULL)
+		return -1;
+
+	monitor->txpower = g_new0(struct att_range, 1);
+	monitor->txpower->start = txpower->range.start;
+	monitor->txpower->end = txpower->range.end;
+
+	update_monitor(monitor);
+
+	return 0;
+}
+
+int monitor_register_immediate(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *immediate)
+{
+	struct monitor *monitor;
+
+	if (!enabled->pathloss && !enabled->findme)
+		return 0;
+
+	monitor = register_monitor(device);
+	if (monitor == NULL)
+		return -1;
+
+	monitor->immediate = g_new0(struct att_range, 1);
+	monitor->immediate->start = immediate->range.start;
+	monitor->immediate->end = immediate->range.end;
+	monitor->enabled.findme = enabled->findme;
+
+	update_monitor(monitor);
+
+	return 0;
+}
+
+static void cleanup_monitor(struct monitor *monitor)
+{
+	struct btd_device *device = monitor->device;
+	const char *path = device_get_path(device);
+
+	if (monitor->immediate != NULL || monitor->txpower != NULL)
+		return;
+
+	if (monitor->immediateto != 0) {
+		g_source_remove(monitor->immediateto);
+		monitor->immediateto = 0;
+	}
+
+	if (monitor->linkloss != NULL)
+		return;
+
+	if (monitor->attioid != 0) {
+		btd_device_remove_attio_callback(device, monitor->attioid);
+		monitor->attioid = 0;
+	}
+
+	if (monitor->attrib != NULL) {
+		g_attrib_unref(monitor->attrib);
+		monitor->attrib = NULL;
+	}
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+							PROXIMITY_INTERFACE);
+}
+
+void monitor_unregister_linkloss(struct btd_device *device)
+{
+	struct monitor *monitor;
+
+	monitor = find_monitor(device);
+	if (monitor == NULL)
+		return;
+
+	g_free(monitor->linkloss);
+	monitor->linkloss = NULL;
+	monitor->enabled.linkloss = FALSE;
+
+	cleanup_monitor(monitor);
+}
+
+void monitor_unregister_txpower(struct btd_device *device)
+{
+	struct monitor *monitor;
+
+	monitor = find_monitor(device);
+	if (monitor == NULL)
+		return;
+
+	g_free(monitor->txpower);
+	monitor->txpower = NULL;
+	monitor->enabled.pathloss = FALSE;
+
+	cleanup_monitor(monitor);
+}
+
+void monitor_unregister_immediate(struct btd_device *device)
+{
+	struct monitor *monitor;
+
+	monitor = find_monitor(device);
+	if (monitor == NULL)
+		return;
+
+	g_free(monitor->immediate);
+	monitor->immediate = NULL;
+	monitor->enabled.findme = FALSE;
+	monitor->enabled.pathloss = FALSE;
+
+	cleanup_monitor(monitor);
+}
diff --git a/bluez/repo/profiles/proximity/monitor.h b/bluez/repo/profiles/proximity/monitor.h
new file mode 100644
index 0000000..d9a40c6
--- /dev/null
+++ b/bluez/repo/profiles/proximity/monitor.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct enabled {
+	gboolean linkloss;
+	gboolean pathloss;
+	gboolean findme;
+};
+
+int monitor_register_linkloss(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *linkloss);
+int monitor_register_txpower(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *txpower);
+int monitor_register_immediate(struct btd_device *device,
+						struct enabled *enabled,
+						struct gatt_primary *immediate);
+
+void monitor_unregister_linkloss(struct btd_device *device);
+void monitor_unregister_txpower(struct btd_device *device);
+void monitor_unregister_immediate(struct btd_device *device);
diff --git a/bluez/repo/profiles/proximity/proximity.conf b/bluez/repo/profiles/proximity/proximity.conf
new file mode 100644
index 0000000..417610f
--- /dev/null
+++ b/bluez/repo/profiles/proximity/proximity.conf
@@ -0,0 +1,9 @@
+# Configuration file for the proximity service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Configuration to allow disabling Proximity services
+# Allowed values: LinkLoss,PathLoss,FindMe
+Disable=PathLoss
diff --git a/bluez/repo/profiles/proximity/reporter.c b/bluez/repo/profiles/proximity/reporter.c
new file mode 100644
index 0000000..366d96b
--- /dev/null
+++ b/bluez/repo/profiles/proximity/reporter.c
@@ -0,0 +1,270 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/attrib-server.h"
+
+#include "reporter.h"
+#include "linkloss.h"
+#include "immalert.h"
+
+struct reporter_adapter {
+	struct btd_adapter *adapter;
+	GSList *devices;
+};
+
+static GSList *reporter_adapters;
+
+static int radapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct reporter_adapter *radapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (radapter->adapter == adapter)
+		return 0;
+
+	return -1;
+}
+
+static struct reporter_adapter *
+find_reporter_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(reporter_adapters, adapter,
+								radapter_cmp);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+const char *get_alert_level_string(uint8_t level)
+{
+	switch (level) {
+	case NO_ALERT:
+		return "none";
+	case MILD_ALERT:
+		return "mild";
+	case HIGH_ALERT:
+		return "high";
+	}
+
+	return "unknown";
+}
+
+static void register_tx_power(struct btd_adapter *adapter)
+{
+	uint16_t start_handle, h;
+	const int svc_size = 4;
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+	start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
+	if (start_handle == 0) {
+		error("Not enough free handles to register service");
+		return;
+	}
+
+	DBG("start_handle=0x%04x", start_handle);
+
+	h = start_handle;
+
+	/* Primary service definition */
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	put_le16(TX_POWER_SVC_UUID, &atval[0]);
+	attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+	/* Power level characteristic */
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_NOTIFY;
+	put_le16(h + 1, &atval[1]);
+	put_le16(POWER_LEVEL_CHR_UUID, &atval[3]);
+	attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+	/* Power level value */
+	bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+	atval[0] = 0x00;
+	attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+	/* Client characteristic configuration */
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+	atval[0] = 0x00;
+	atval[1] = 0x00;
+	attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+
+	g_assert(h - start_handle == svc_size);
+}
+
+static gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	const char *level;
+
+	level = link_loss_get_alert_level(device);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level);
+
+	return TRUE;
+}
+
+static gboolean property_get_immediate_alert_level(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	const char *level;
+
+	level = imm_alert_get_level(device);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable reporter_device_properties[] = {
+	{ "LinkLossAlertLevel", "s", property_get_link_loss_level },
+	{ "ImmediateAlertLevel", "s", property_get_immediate_alert_level },
+	{ }
+};
+
+static void unregister_reporter_device(gpointer data, gpointer user_data)
+{
+	struct btd_device *device = data;
+	struct reporter_adapter *radapter = user_data;
+	const char *path = device_get_path(device);
+
+	DBG("unregister on device %s", path);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+					PROXIMITY_REPORTER_INTERFACE);
+
+	radapter->devices = g_slist_remove(radapter->devices, device);
+	btd_device_unref(device);
+}
+
+static void register_reporter_device(struct btd_device *device,
+					struct reporter_adapter *radapter)
+{
+	const char *path = device_get_path(device);
+
+	DBG("register on device %s", path);
+
+	g_dbus_register_interface(btd_get_dbus_connection(), path,
+					PROXIMITY_REPORTER_INTERFACE,
+					NULL, NULL, reporter_device_properties,
+					device, NULL);
+
+	btd_device_ref(device);
+	radapter->devices = g_slist_prepend(radapter->devices, device);
+}
+
+int reporter_device_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct reporter_adapter *radapter;
+	struct btd_adapter *adapter = device_get_adapter(device);
+
+	radapter = find_reporter_adapter(adapter);
+	if (!radapter)
+		return -1;
+
+	register_reporter_device(device, radapter);
+
+	return 0;
+}
+
+void reporter_device_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct reporter_adapter *radapter;
+	struct btd_adapter *adapter = device_get_adapter(device);
+
+	radapter = find_reporter_adapter(adapter);
+	if (!radapter)
+		return;
+
+	unregister_reporter_device(device, radapter);
+}
+
+int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	struct reporter_adapter *radapter;
+
+	radapter = g_new0(struct reporter_adapter, 1);
+	radapter->adapter = adapter;
+
+	link_loss_register(adapter);
+	register_tx_power(adapter);
+	imm_alert_register(adapter);
+
+	reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
+	DBG("Proximity Reporter for adapter %p", adapter);
+
+	return 0;
+}
+
+void reporter_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct reporter_adapter *radapter = find_reporter_adapter(adapter);
+	if (!radapter)
+		return;
+
+	g_slist_foreach(radapter->devices, unregister_reporter_device,
+								radapter);
+
+	link_loss_unregister(adapter);
+	imm_alert_unregister(adapter);
+
+	reporter_adapters = g_slist_remove(reporter_adapters, radapter);
+	g_free(radapter);
+}
diff --git a/bluez/repo/profiles/proximity/reporter.h b/bluez/repo/profiles/proximity/reporter.h
new file mode 100644
index 0000000..a8e1aac
--- /dev/null
+++ b/bluez/repo/profiles/proximity/reporter.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter1"
+
+#define IMMEDIATE_ALERT_SVC_UUID	0x1802
+#define LINK_LOSS_SVC_UUID		0x1803
+#define TX_POWER_SVC_UUID		0x1804
+#define ALERT_LEVEL_CHR_UUID		0x2A06
+#define POWER_LEVEL_CHR_UUID		0x2A07
+
+enum {
+	NO_ALERT = 0x00,
+	MILD_ALERT = 0x01,
+	HIGH_ALERT = 0x02,
+};
+
+void reporter_device_remove(struct btd_service *service);
+int reporter_device_probe(struct btd_service *service);
+
+int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter);
+void reporter_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter);
+
+const char *get_alert_level_string(uint8_t level);
diff --git a/bluez/repo/profiles/sap/._main.c b/bluez/repo/profiles/sap/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._main.c
Binary files differ
diff --git a/bluez/repo/profiles/sap/._manager.c b/bluez/repo/profiles/sap/._manager.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._manager.c
Binary files differ
diff --git a/bluez/repo/profiles/sap/._manager.h b/bluez/repo/profiles/sap/._manager.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._manager.h
Binary files differ
diff --git a/bluez/repo/profiles/sap/._sap-dummy.c b/bluez/repo/profiles/sap/._sap-dummy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._sap-dummy.c
Binary files differ
diff --git a/bluez/repo/profiles/sap/._sap-u8500.c b/bluez/repo/profiles/sap/._sap-u8500.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._sap-u8500.c
Binary files differ
diff --git a/bluez/repo/profiles/sap/._sap.h b/bluez/repo/profiles/sap/._sap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._sap.h
Binary files differ
diff --git a/bluez/repo/profiles/sap/._server.c b/bluez/repo/profiles/sap/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._server.c
Binary files differ
diff --git a/bluez/repo/profiles/sap/._server.h b/bluez/repo/profiles/sap/._server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/sap/._server.h
Binary files differ
diff --git a/bluez/repo/profiles/sap/main.c b/bluez/repo/profiles/sap/main.c
new file mode 100644
index 0000000..cd707ff
--- /dev/null
+++ b/bluez/repo/profiles/sap/main.c
@@ -0,0 +1,43 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 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 <errno.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "manager.h"
+
+static int sap_init(void)
+{
+	return sap_manager_init();
+}
+
+static void sap_exit(void)
+{
+	sap_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(sap, VERSION,
+		BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, sap_init, sap_exit)
diff --git a/bluez/repo/profiles/sap/manager.c b/bluez/repo/profiles/sap/manager.c
new file mode 100644
index 0000000..b622397
--- /dev/null
+++ b/bluez/repo/profiles/sap/manager.c
@@ -0,0 +1,72 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 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 "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+
+#include "manager.h"
+#include "server.h"
+
+static int sap_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	DBG("path %s", adapter_get_path(adapter));
+
+	return sap_server_register(adapter);
+}
+
+static void sap_server_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	sap_server_unregister(path);
+}
+
+static struct btd_profile sap_profile = {
+	.name		= "sap-server",
+	.adapter_probe	= sap_server_probe,
+	.adapter_remove	= sap_server_remove,
+};
+
+int sap_manager_init(void)
+{
+	btd_profile_register(&sap_profile);
+
+	return 0;
+}
+
+void sap_manager_exit(void)
+{
+	btd_profile_unregister(&sap_profile);
+}
diff --git a/bluez/repo/profiles/sap/manager.h b/bluez/repo/profiles/sap/manager.h
new file mode 100644
index 0000000..6601a03
--- /dev/null
+++ b/bluez/repo/profiles/sap/manager.h
@@ -0,0 +1,22 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 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
+ */
+
+int sap_manager_init(void);
+void sap_manager_exit(void);
diff --git a/bluez/repo/profiles/sap/sap-dummy.c b/bluez/repo/profiles/sap/sap-dummy.c
new file mode 100644
index 0000000..53463ca
--- /dev/null
+++ b/bluez/repo/profiles/sap/sap-dummy.c
@@ -0,0 +1,376 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *  Copyright (C) 2011 Tieto Poland
+ *
+ *  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; 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>
+#include <stdint.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/dbus-common.h"
+#include "src/error.h"
+#include "src/log.h"
+
+#include "sap.h"
+
+#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest1"
+#define SAP_DUMMY_PATH "/org/bluez/test"
+
+enum {
+	SIM_DISCONNECTED = 0x00,
+	SIM_CONNECTED	 = 0x01,
+	SIM_POWERED_OFF	 = 0x02,
+	SIM_MISSING	 = 0x03
+};
+
+static unsigned int init_cnt = 0;
+
+static int sim_card_conn_status = SIM_DISCONNECTED;
+static void *sap_data = NULL; /* SAP server private data. */
+static gboolean ongoing_call_status = FALSE;
+static int max_msg_size_supported = 512;
+
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
+{
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status != SIM_DISCONNECTED) {
+		sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED);
+		return;
+	}
+
+	if (max_msg_size_supported > maxmsgsize) {
+		sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL);
+		return;
+	}
+
+	if (max_msg_size_supported < maxmsgsize) {
+		sap_connect_rsp(sap_device,
+					SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED);
+		return;
+	}
+
+	if (ongoing_call_status) {
+		sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL);
+		return;
+	}
+
+	sim_card_conn_status = SIM_CONNECTED;
+	sap_data = sap_device;
+
+	sap_connect_rsp(sap_device, SAP_STATUS_OK);
+	sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+}
+
+void sap_disconnect_req(void *sap_device, uint8_t linkloss)
+{
+	sim_card_conn_status = SIM_DISCONNECTED;
+	sap_data = NULL;
+	ongoing_call_status = FALSE;
+
+	DBG("status: %d", sim_card_conn_status);
+
+	if (linkloss)
+		return;
+
+	sap_disconnect_rsp(sap_device);
+}
+
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
+{
+	char apdu[] = "APDU response!";
+
+	DBG("status: %d", sim_card_conn_status);
+
+	switch (sim_card_conn_status) {
+	case SIM_MISSING:
+		sap_transfer_apdu_rsp(sap_device,
+				SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0);
+		break;
+	case SIM_POWERED_OFF:
+		sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+								NULL, 0);
+		break;
+	case SIM_DISCONNECTED:
+		sap_transfer_apdu_rsp(sap_device,
+				SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
+		break;
+	case SIM_CONNECTED:
+		sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK,
+						(uint8_t *)apdu, sizeof(apdu));
+		break;
+	}
+}
+
+void sap_transfer_atr_req(void *sap_device)
+{
+	char atr[] = "ATR response!";
+
+	DBG("status: %d", sim_card_conn_status);
+
+	switch (sim_card_conn_status) {
+	case SIM_MISSING:
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED,
+								NULL, 0);
+		break;
+	case SIM_POWERED_OFF:
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+								NULL, 0);
+		break;
+	case SIM_DISCONNECTED:
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON,
+								NULL, 0);
+		break;
+	case SIM_CONNECTED:
+		sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK,
+						(uint8_t *)atr, sizeof(atr));
+		break;
+	}
+}
+
+void sap_power_sim_off_req(void *sap_device)
+{
+	DBG("status: %d", sim_card_conn_status);
+
+	switch (sim_card_conn_status) {
+	case SIM_MISSING:
+		sap_power_sim_off_rsp(sap_device,
+					SAP_RESULT_ERROR_CARD_REMOVED);
+		break;
+	case SIM_POWERED_OFF:
+		sap_power_sim_off_rsp(sap_device,
+					SAP_RESULT_ERROR_POWERED_OFF);
+		break;
+	case SIM_DISCONNECTED:
+		sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+		break;
+	case SIM_CONNECTED:
+		sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+		sim_card_conn_status = SIM_POWERED_OFF;
+		break;
+	}
+}
+
+void sap_power_sim_on_req(void *sap_device)
+{
+	DBG("status: %d", sim_card_conn_status);
+
+	switch (sim_card_conn_status) {
+	case SIM_MISSING:
+		sap_power_sim_on_rsp(sap_device,
+					SAP_RESULT_ERROR_CARD_REMOVED);
+		break;
+	case SIM_POWERED_OFF:
+		sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+		sim_card_conn_status = SIM_CONNECTED;
+		break;
+	case SIM_DISCONNECTED:
+		sap_power_sim_on_rsp(sap_device,
+					SAP_RESULT_ERROR_NOT_ACCESSIBLE);
+		break;
+	case SIM_CONNECTED:
+		sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+		break;
+	}
+}
+
+void sap_reset_sim_req(void *sap_device)
+{
+	DBG("status: %d", sim_card_conn_status);
+
+	switch (sim_card_conn_status) {
+	case SIM_MISSING:
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED);
+		break;
+	case SIM_POWERED_OFF:
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF);
+		break;
+	case SIM_DISCONNECTED:
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+		break;
+	case SIM_CONNECTED:
+		sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
+		break;
+	}
+}
+
+void sap_transfer_card_reader_status_req(void *sap_device)
+{
+	DBG("status: %d", sim_card_conn_status);
+
+	if (sim_card_conn_status != SIM_CONNECTED) {
+		sap_transfer_card_reader_status_rsp(sap_device,
+					SAP_RESULT_ERROR_NO_REASON, 0xF1);
+		return;
+	}
+
+	sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK, 0xF1);
+}
+
+void sap_set_transport_protocol_req(void *sap_device,
+					struct sap_parameter *param)
+{
+	sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
+}
+
+static DBusMessage *ongoing_call(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	dbus_bool_t ongoing;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &ongoing,
+						DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	if (ongoing_call_status && !ongoing) {
+		/* An ongoing call has finished. Continue connection.*/
+		sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET);
+		ongoing_call_status = FALSE;
+	} else if (!ongoing_call_status && ongoing) {
+		/* An ongoing call has started.*/
+		ongoing_call_status = TRUE;
+	}
+
+	DBG("OngoingCall status set to %d", ongoing_call_status);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *max_msg_size(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	dbus_uint32_t size;
+
+	if (sim_card_conn_status == SIM_CONNECTED)
+		return btd_error_failed(msg,
+				"Can't change msg size when connected.");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	max_msg_size_supported = size;
+
+	DBG("MaxMessageSize set to %d", max_msg_size_supported);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disconnect_immediate(DBusConnection *conn, DBusMessage *msg,
+						void *data)
+{
+	if (sim_card_conn_status == SIM_DISCONNECTED)
+		return btd_error_failed(msg, "Already disconnected.");
+
+	sim_card_conn_status = SIM_DISCONNECTED;
+	sap_disconnect_ind(sap_data, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *card_status(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	dbus_uint32_t status;
+
+	DBG("status %d", sim_card_conn_status);
+
+	if (sim_card_conn_status != SIM_CONNECTED)
+		return btd_error_failed(msg,
+				"Can't change msg size when not connected.");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	switch (status) {
+	case 0: /* card removed */
+		sim_card_conn_status = SIM_MISSING;
+		sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED);
+		break;
+
+	case 1: /* card inserted */
+		if (sim_card_conn_status == SIM_MISSING) {
+			sim_card_conn_status = SIM_CONNECTED;
+			sap_status_ind(sap_data,
+					SAP_STATUS_CHANGE_CARD_INSERTED);
+		}
+		break;
+
+	case 2: /* card not longer available*/
+		sim_card_conn_status = SIM_POWERED_OFF;
+		sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE);
+		break;
+
+	default:
+		return btd_error_failed(msg,
+					"Unknown card status. Use 0, 1 or 2.");
+	}
+
+	DBG("Card status changed to %d", status);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable dummy_methods[] = {
+	{ GDBUS_EXPERIMENTAL_METHOD("OngoingCall",
+				GDBUS_ARGS({ "ongoing", "b" }), NULL,
+				ongoing_call) },
+	{ GDBUS_EXPERIMENTAL_METHOD("MaxMessageSize",
+				GDBUS_ARGS({ "size", "u" }), NULL,
+				max_msg_size) },
+	{ GDBUS_EXPERIMENTAL_METHOD("DisconnectImmediate", NULL, NULL,
+				disconnect_immediate) },
+	{ GDBUS_EXPERIMENTAL_METHOD("CardStatus",
+				GDBUS_ARGS({ "status", "" }), NULL,
+				card_status) },
+	{ }
+};
+
+int sap_init(void)
+{
+	if (init_cnt++)
+		return 0;
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(), SAP_DUMMY_PATH,
+				SAP_DUMMY_IFACE, dummy_methods, NULL, NULL,
+				NULL, NULL) == FALSE) {
+		init_cnt--;
+		return -1;
+	}
+
+	return 0;
+}
+
+void sap_exit(void)
+{
+	if (--init_cnt)
+		return;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					SAP_DUMMY_PATH, SAP_DUMMY_IFACE);
+}
diff --git a/bluez/repo/profiles/sap/sap-u8500.c b/bluez/repo/profiles/sap/sap-u8500.c
new file mode 100644
index 0000000..5417d59
--- /dev/null
+++ b/bluez/repo/profiles/sap/sap-u8500.c
@@ -0,0 +1,755 @@
+/*
+ *  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)
+{
+}
diff --git a/bluez/repo/profiles/sap/sap.h b/bluez/repo/profiles/sap/sap.h
new file mode 100644
index 0000000..16c333a
--- /dev/null
+++ b/bluez/repo/profiles/sap/sap.h
@@ -0,0 +1,180 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ *  Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ *  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; 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 <stdint.h>
+#include <glib.h>
+
+#ifdef SAP_DEBUG
+#define SAP_VDBG(fmt, arg...) DBG(fmt, arg)
+#else
+#define SAP_VDBG(fmt...)
+#endif
+
+#define SAP_VERSION 0x0101
+
+/* Connection Status - SAP v1.1 section 5.2.2 */
+enum sap_status {
+	SAP_STATUS_OK				= 0x00,
+	SAP_STATUS_CONNECTION_FAILED		= 0x01,
+	SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED	= 0x02,
+	SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL	= 0x03,
+	SAP_STATUS_OK_ONGOING_CALL		= 0x04
+};
+
+/* Disconnection Type - SAP v1.1 section 5.2.3 */
+enum sap_disconnection_type {
+	SAP_DISCONNECTION_TYPE_GRACEFUL		= 0x00,
+	SAP_DISCONNECTION_TYPE_IMMEDIATE	= 0x01
+};
+
+/* Result codes - SAP v1.1 section 5.2.4 */
+enum sap_result {
+	SAP_RESULT_OK			= 0x00,
+	SAP_RESULT_ERROR_NO_REASON	= 0x01,
+	SAP_RESULT_ERROR_NOT_ACCESSIBLE	= 0x02,
+	SAP_RESULT_ERROR_POWERED_OFF	= 0x03,
+	SAP_RESULT_ERROR_CARD_REMOVED	= 0x04,
+	SAP_RESULT_ERROR_POWERED_ON	= 0x05,
+	SAP_RESULT_ERROR_NO_DATA	= 0x06,
+	SAP_RESULT_NOT_SUPPORTED	= 0x07
+};
+
+/* Status Change - SAP v1.1 section 5.2.8 */
+enum sap_status_change {
+	SAP_STATUS_CHANGE_UNKNOWN_ERROR		= 0x00,
+	SAP_STATUS_CHANGE_CARD_RESET		= 0x01,
+	SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE	= 0x02,
+	SAP_STATUS_CHANGE_CARD_REMOVED		= 0x03,
+	SAP_STATUS_CHANGE_CARD_INSERTED		= 0x04,
+	SAP_STATUS_CHANGE_CARD_RECOVERED	= 0x05
+};
+
+/* Message format - SAP v1.1 section 5.1 */
+struct sap_parameter {
+	uint8_t id;
+	uint8_t reserved;
+	uint16_t len;
+	uint8_t val[0];
+	/*
+	 * Padding bytes 0-3 bytes
+	 */
+} __attribute__((packed));
+
+struct sap_message {
+	uint8_t id;
+	uint8_t nparam;
+	uint16_t reserved;
+	struct sap_parameter param[0];
+} __attribute__((packed));
+
+#define SAP_BUF_SIZE		512
+#define SAP_MSG_HEADER_SIZE	4
+
+enum sap_protocol {
+	SAP_CONNECT_REQ		= 0x00,
+	SAP_CONNECT_RESP	= 0x01,
+	SAP_DISCONNECT_REQ	= 0x02,
+	SAP_DISCONNECT_RESP	= 0x03,
+	SAP_DISCONNECT_IND	= 0x04,
+	SAP_TRANSFER_APDU_REQ	= 0x05,
+	SAP_TRANSFER_APDU_RESP	= 0x06,
+	SAP_TRANSFER_ATR_REQ	= 0x07,
+	SAP_TRANSFER_ATR_RESP	= 0x08,
+	SAP_POWER_SIM_OFF_REQ	= 0x09,
+	SAP_POWER_SIM_OFF_RESP	= 0x0A,
+	SAP_POWER_SIM_ON_REQ	= 0x0B,
+	SAP_POWER_SIM_ON_RESP	= 0x0C,
+	SAP_RESET_SIM_REQ	= 0x0D,
+	SAP_RESET_SIM_RESP	= 0x0E,
+	SAP_TRANSFER_CARD_READER_STATUS_REQ	= 0x0F,
+	SAP_TRANSFER_CARD_READER_STATUS_RESP	= 0x10,
+	SAP_STATUS_IND	= 0x11,
+	SAP_ERROR_RESP	= 0x12,
+	SAP_SET_TRANSPORT_PROTOCOL_REQ	= 0x13,
+	SAP_SET_TRANSPORT_PROTOCOL_RESP	= 0x14
+};
+
+/* Parameters Ids - SAP 1.1 section 5.2 */
+enum sap_param_id {
+	SAP_PARAM_ID_MAX_MSG_SIZE	= 0x00,
+	SAP_PARAM_ID_CONN_STATUS	= 0x01,
+	SAP_PARAM_ID_RESULT_CODE	= 0x02,
+	SAP_PARAM_ID_DISCONNECT_IND	= 0x03,
+	SAP_PARAM_ID_COMMAND_APDU	= 0x04,
+	SAP_PARAM_ID_COMMAND_APDU7816	= 0x10,
+	SAP_PARAM_ID_RESPONSE_APDU	= 0x05,
+	SAP_PARAM_ID_ATR		= 0x06,
+	SAP_PARAM_ID_CARD_READER_STATUS	= 0x07,
+	SAP_PARAM_ID_STATUS_CHANGE	= 0x08,
+	SAP_PARAM_ID_TRANSPORT_PROTOCOL	= 0x09
+};
+
+#define SAP_PARAM_ID_MAX_MSG_SIZE_LEN		0x02
+#define SAP_PARAM_ID_CONN_STATUS_LEN		0x01
+#define SAP_PARAM_ID_RESULT_CODE_LEN		0x01
+#define SAP_PARAM_ID_DISCONNECT_IND_LEN		0x01
+#define SAP_PARAM_ID_CARD_READER_STATUS_LEN	0x01
+#define SAP_PARAM_ID_STATUS_CHANGE_LEN		0x01
+#define SAP_PARAM_ID_TRANSPORT_PROTO_LEN	0x01
+
+/* Transport Protocol - SAP v1.1 section 5.2.9 */
+enum sap_transport_protocol {
+	SAP_TRANSPORT_PROTOCOL_T0 = 0x00,
+	SAP_TRANSPORT_PROTOCOL_T1 = 0x01
+};
+
+/*SAP driver init and exit routines. Implemented by sap-*.c */
+int sap_init(void);
+void sap_exit(void);
+
+/* SAP requests implemented by sap-*.c */
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize);
+void sap_disconnect_req(void *sap_device, uint8_t linkloss);
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param);
+void sap_transfer_atr_req(void *sap_device);
+void sap_power_sim_off_req(void *sap_device);
+void sap_power_sim_on_req(void *sap_device);
+void sap_reset_sim_req(void *sap_device);
+void sap_transfer_card_reader_status_req(void *sap_device);
+void sap_set_transport_protocol_req(void *sap_device,
+					struct sap_parameter *param);
+
+/*SAP responses to SAP requests. Implemented by server.c */
+int sap_connect_rsp(void *sap_device, uint8_t status);
+int sap_disconnect_rsp(void *sap_device);
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result,
+				uint8_t *sap_apdu_resp, uint16_t length);
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result,
+				uint8_t *sap_atr, uint16_t length);
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result);
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result);
+int sap_reset_sim_rsp(void *sap_device, uint8_t result);
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+						uint8_t status);
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result);
+
+/* Event indication. Implemented by server.c*/
+int sap_status_ind(void *sap_device, uint8_t status_change);
+int sap_disconnect_ind(void *sap_device, uint8_t disc_type);
diff --git a/bluez/repo/profiles/sap/server.c b/bluez/repo/profiles/sap/server.c
new file mode 100644
index 0000000..ce423a1
--- /dev/null
+++ b/bluez/repo/profiles/sap/server.c
@@ -0,0 +1,1423 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *  Copyright (C) 2011 Tieto Poland
+ *
+ *  Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ *  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; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/sdpd.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+
+#include "sap.h"
+#include "server.h"
+
+#define SAP_SERVER_INTERFACE	"org.bluez.SimAccess1"
+#define SAP_SERVER_CHANNEL	8
+
+#define PADDING4(x) ((4 - ((x) & 0x03)) & 0x03)
+#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
+
+#define SAP_NO_REQ 0xFF
+#define SAP_DISCONNECTION_TYPE_CLIENT 0xFF
+
+#define SAP_TIMER_GRACEFUL_DISCONNECT 30
+#define SAP_TIMER_NO_ACTIVITY 30
+
+enum {
+	SAP_STATE_DISCONNECTED,
+	SAP_STATE_CONNECT_IN_PROGRESS,
+	SAP_STATE_CONNECT_MODEM_BUSY,
+	SAP_STATE_CONNECTED,
+	SAP_STATE_GRACEFUL_DISCONNECT,
+	SAP_STATE_IMMEDIATE_DISCONNECT,
+	SAP_STATE_CLIENT_DISCONNECT
+};
+
+struct sap_connection {
+	GIOChannel *io;
+	uint32_t state;
+	uint8_t processing_req;
+	guint timer_id;
+};
+
+struct sap_server {
+	struct btd_adapter *adapter;
+	uint32_t record_id;
+	GIOChannel *listen_io;
+	struct sap_connection *conn;
+};
+
+static void start_guard_timer(struct sap_server *server, guint interval);
+static void stop_guard_timer(struct sap_server *server);
+static gboolean guard_timeout(gpointer data);
+
+static size_t add_result_parameter(uint8_t result,
+					struct sap_parameter *param)
+{
+	param->id = SAP_PARAM_ID_RESULT_CODE;
+	param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN);
+	*param->val = result;
+
+	return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN);
+}
+
+static int is_power_sim_off_req_allowed(uint8_t processing_req)
+{
+	switch (processing_req) {
+	case SAP_NO_REQ:
+	case SAP_TRANSFER_APDU_REQ:
+	case SAP_TRANSFER_ATR_REQ:
+	case SAP_POWER_SIM_ON_REQ:
+	case SAP_RESET_SIM_REQ:
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int is_reset_sim_req_allowed(uint8_t processing_req)
+{
+	switch (processing_req) {
+	case SAP_NO_REQ:
+	case SAP_TRANSFER_APDU_REQ:
+	case SAP_TRANSFER_ATR_REQ:
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int check_msg(struct sap_message *msg)
+{
+	switch (msg->id) {
+	case SAP_CONNECT_REQ:
+		if (msg->nparam != 0x01)
+			return -EBADMSG;
+
+		if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE)
+			return -EBADMSG;
+
+		if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
+			return -EBADMSG;
+
+		break;
+
+	case SAP_TRANSFER_APDU_REQ:
+		if (msg->nparam != 0x01)
+			return -EBADMSG;
+
+		if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU)
+			if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
+				return -EBADMSG;
+
+		if (msg->param->len == 0x00)
+			return -EBADMSG;
+
+		break;
+
+	case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+		if (msg->nparam != 0x01)
+			return -EBADMSG;
+
+		if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL)
+			return -EBADMSG;
+
+		if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN)
+			return -EBADMSG;
+
+		if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0)
+			if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1)
+				return -EBADMSG;
+
+		break;
+
+	case SAP_DISCONNECT_REQ:
+	case SAP_TRANSFER_ATR_REQ:
+	case SAP_POWER_SIM_OFF_REQ:
+	case SAP_POWER_SIM_ON_REQ:
+	case SAP_RESET_SIM_REQ:
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		if (msg->nparam != 0x00)
+			return -EBADMSG;
+
+		break;
+	}
+
+	return 0;
+}
+
+static sdp_record_t *create_sap_record(uint8_t channel)
+{
+	sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
+	uuid_t sap_uuid, gt_uuid, root_uuid, l2cap, rfcomm;
+	sdp_profile_desc_t profile;
+	sdp_record_t *record;
+	sdp_data_t *ch;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&sap_uuid, SAP_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &sap_uuid);
+	sdp_uuid16_create(&gt_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &gt_uuid);
+
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+	profile.version = SAP_VERSION;
+	profiles = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, profiles);
+	sdp_list_free(profiles, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm);
+	ch = sdp_data_alloc(SDP_UINT8, &channel);
+	proto[1] = sdp_list_append(proto[1], ch);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "SIM Access Server", NULL, NULL);
+
+	sdp_data_free(ch);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return record;
+}
+
+static int send_message(struct sap_connection *conn, void *buf, size_t size)
+{
+	size_t written = 0;
+	GError *gerr = NULL;
+	GIOStatus gstatus;
+
+	SAP_VDBG("conn %p, size %zu", conn, size);
+
+	gstatus = g_io_channel_write_chars(conn->io, buf, size, &written,
+									&gerr);
+	if (gstatus != G_IO_STATUS_NORMAL) {
+		if (gerr)
+			g_error_free(gerr);
+
+		error("write error (0x%02x).", gstatus);
+		return -EIO;
+	}
+
+	if (written != size) {
+		error("written %zu bytes out of %zu", written, size);
+		return -EIO;
+	}
+
+	return written;
+}
+
+static int disconnect_ind(struct sap_connection *conn, uint8_t disc_type)
+{
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_DISCONNECT_IND;
+	msg->nparam = 0x01;
+
+	/* Add disconnection type param. */
+	param->id  = SAP_PARAM_ID_DISCONNECT_IND;
+	param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+	*param->val = disc_type;
+	size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+
+	return send_message(conn, buf, size);
+}
+
+static int sap_error_rsp(struct sap_connection *conn)
+{
+	struct sap_message msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.id = SAP_ERROR_RESP;
+
+	error("SAP error (state %d pr 0x%02x).", conn->state,
+							conn->processing_req);
+
+	return send_message(conn, &msg, sizeof(msg));
+}
+
+static void connect_req(struct sap_server *server,
+				struct sap_parameter *param)
+{
+	struct sap_connection *conn = server->conn;
+	uint16_t maxmsgsize;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (!param)
+		goto error_rsp;
+
+	if (conn->state != SAP_STATE_DISCONNECTED)
+		goto error_rsp;
+
+	stop_guard_timer(server);
+
+	maxmsgsize = get_be16(&param->val);
+
+	DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
+
+	conn->state = SAP_STATE_CONNECT_IN_PROGRESS;
+
+	if (maxmsgsize <= SAP_BUF_SIZE) {
+		conn->processing_req = SAP_CONNECT_REQ;
+		sap_connect_req(server, maxmsgsize);
+	} else {
+		sap_connect_rsp(server, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED);
+	}
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static int disconnect_req(struct sap_server *server, uint8_t disc_type)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+	switch (disc_type) {
+	case SAP_DISCONNECTION_TYPE_GRACEFUL:
+		if (conn->state == SAP_STATE_DISCONNECTED ||
+				conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+				conn->state == SAP_STATE_CONNECT_MODEM_BUSY)
+			return -EPERM;
+
+		if (conn->state == SAP_STATE_CONNECTED) {
+			conn->state = SAP_STATE_GRACEFUL_DISCONNECT;
+			conn->processing_req = SAP_NO_REQ;
+
+			disconnect_ind(conn, disc_type);
+			/* Timer will disconnect if client won't do.*/
+			start_guard_timer(server,
+					SAP_TIMER_GRACEFUL_DISCONNECT);
+		}
+
+		return 0;
+
+	case SAP_DISCONNECTION_TYPE_IMMEDIATE:
+		if (conn->state == SAP_STATE_DISCONNECTED ||
+				conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+				conn->state == SAP_STATE_CONNECT_MODEM_BUSY)
+			return -EPERM;
+
+		if (conn->state == SAP_STATE_CONNECTED ||
+				conn->state == SAP_STATE_GRACEFUL_DISCONNECT) {
+			conn->state = SAP_STATE_IMMEDIATE_DISCONNECT;
+			conn->processing_req = SAP_NO_REQ;
+
+			stop_guard_timer(server);
+			disconnect_ind(conn, disc_type);
+			sap_disconnect_req(server, 0);
+		}
+
+		return 0;
+
+	case SAP_DISCONNECTION_TYPE_CLIENT:
+		if (conn->state != SAP_STATE_CONNECTED &&
+				conn->state != SAP_STATE_GRACEFUL_DISCONNECT) {
+			sap_error_rsp(conn);
+			return -EPERM;
+		}
+
+		conn->state = SAP_STATE_CLIENT_DISCONNECT;
+		conn->processing_req = SAP_NO_REQ;
+
+		stop_guard_timer(server);
+		sap_disconnect_req(server, 0);
+
+		return 0;
+
+	default:
+		error("Unknown disconnection type (0x%02x).", disc_type);
+		return -EINVAL;
+	}
+}
+
+static void transfer_apdu_req(struct sap_server *server,
+					struct sap_parameter *param)
+{
+	struct sap_connection *conn = server->conn;
+
+	SAP_VDBG("conn %p state %d", conn, conn->state);
+
+	if (!param)
+		goto error_rsp;
+
+	param->len = ntohs(param->len);
+
+	if (conn->state != SAP_STATE_CONNECTED &&
+			conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_TRANSFER_APDU_REQ;
+	sap_transfer_apdu_req(server, param);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void transfer_atr_req(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_TRANSFER_ATR_REQ;
+	sap_transfer_atr_req(server);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void power_sim_off_req(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (!is_power_sim_off_req_allowed(conn->processing_req))
+		goto error_rsp;
+
+	conn->processing_req = SAP_POWER_SIM_OFF_REQ;
+	sap_power_sim_off_req(server);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void power_sim_on_req(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_POWER_SIM_ON_REQ;
+	sap_power_sim_on_req(server);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void reset_sim_req(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (!is_reset_sim_req_allowed(conn->processing_req))
+		goto error_rsp;
+
+	conn->processing_req = SAP_RESET_SIM_REQ;
+	sap_reset_sim_req(server);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void transfer_card_reader_status_req(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p state %d", conn, conn->state);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ;
+	sap_transfer_card_reader_status_req(server);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void set_transport_protocol_req(struct sap_server *server,
+					struct sap_parameter *param)
+{
+	struct sap_connection *conn = server->conn;
+
+	if (!param)
+		goto error_rsp;
+
+	DBG("conn %p state %d param %p", conn, conn->state, param);
+
+	if (conn->state != SAP_STATE_CONNECTED)
+		goto error_rsp;
+
+	if (conn->processing_req != SAP_NO_REQ)
+		goto error_rsp;
+
+	conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ;
+	sap_set_transport_protocol_req(server, param);
+
+	return;
+
+error_rsp:
+	sap_error_rsp(conn);
+}
+
+static void start_guard_timer(struct sap_server *server, guint interval)
+{
+	struct sap_connection *conn = server->conn;
+
+	if (!conn)
+		return;
+
+	if (!conn->timer_id)
+		conn->timer_id = g_timeout_add_seconds(interval, guard_timeout,
+								server);
+	else
+		error("Timer is already active.");
+}
+
+static void stop_guard_timer(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	if (conn  && conn->timer_id) {
+		g_source_remove(conn->timer_id);
+		conn->timer_id = 0;
+	}
+}
+
+static gboolean guard_timeout(gpointer data)
+{
+	struct sap_server *server = data;
+	struct sap_connection *conn = server->conn;
+
+	if (!conn)
+		return FALSE;
+
+	DBG("conn %p state %d pr 0x%02x", conn, conn->state,
+						conn->processing_req);
+
+	conn->timer_id = 0;
+
+	switch (conn->state) {
+	case SAP_STATE_DISCONNECTED:
+		/* Client opened RFCOMM channel but didn't send CONNECT_REQ,
+		 * in fixed time or client disconnected SAP connection but
+		 * didn't closed RFCOMM channel in fixed time.*/
+		if (conn->io) {
+			g_io_channel_shutdown(conn->io, TRUE, NULL);
+			g_io_channel_unref(conn->io);
+			conn->io = NULL;
+		}
+		break;
+
+	case SAP_STATE_GRACEFUL_DISCONNECT:
+		/* Client didn't disconnect SAP connection in fixed time,
+		 * so close SAP connection immediately. */
+		disconnect_req(server, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+		break;
+
+	default:
+		error("Unexpected state (%d).", conn->state);
+		break;
+	}
+
+	return FALSE;
+}
+
+static void sap_set_connected(struct sap_server *server)
+{
+	server->conn->state = SAP_STATE_CONNECTED;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					adapter_get_path(server->adapter),
+					SAP_SERVER_INTERFACE, "Connected");
+}
+
+int sap_connect_rsp(void *sap_device, uint8_t status)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x status 0x%02x", conn->state,
+						conn->processing_req, status);
+
+	if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+		return -EPERM;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_CONNECT_RESP;
+	msg->nparam = 0x01;
+
+	/* Add connection status */
+	param->id = SAP_PARAM_ID_CONN_STATUS;
+	param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN);
+	*param->val = status;
+	size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN);
+
+
+	switch (status) {
+	case SAP_STATUS_OK:
+		sap_set_connected(server);
+		break;
+	case SAP_STATUS_OK_ONGOING_CALL:
+		DBG("ongoing call. Wait for reset indication!");
+		conn->state = SAP_STATE_CONNECT_MODEM_BUSY;
+		break;
+	case SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED: /* Add MaxMsgSize */
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
+		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 */
+	default:
+		conn->state = SAP_STATE_DISCONNECTED;
+
+		/* Timer will shutdown channel if client doesn't send
+		 * CONNECT_REQ or doesn't shutdown channel itself.*/
+		start_guard_timer(server, SAP_TIMER_NO_ACTIVITY);
+		break;
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_disconnect_rsp(void *sap_device)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	struct sap_message msg;
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	switch (conn->state) {
+	case SAP_STATE_CLIENT_DISCONNECT:
+		memset(&msg, 0, sizeof(msg));
+		msg.id = SAP_DISCONNECT_RESP;
+
+		conn->state = SAP_STATE_DISCONNECTED;
+		conn->processing_req = SAP_NO_REQ;
+
+		/* Timer will close channel if client doesn't do it.*/
+		start_guard_timer(server, SAP_TIMER_NO_ACTIVITY);
+
+		return send_message(conn, &msg, sizeof(msg));
+
+	case SAP_STATE_IMMEDIATE_DISCONNECT:
+		conn->state = SAP_STATE_DISCONNECTED;
+		conn->processing_req = SAP_NO_REQ;
+
+		if (conn->io) {
+			g_io_channel_shutdown(conn->io, TRUE, NULL);
+			g_io_channel_unref(conn->io);
+			conn->io = NULL;
+		}
+
+		return 0;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
+					uint16_t length)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	SAP_VDBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	if (conn->processing_req != SAP_TRANSFER_APDU_REQ)
+		return 0;
+
+	if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00)))
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_TRANSFER_APDU_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, param);
+
+	/* Add APDU response. */
+	if (result == SAP_RESULT_OK) {
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_RESPONSE_APDU;
+		param->len = htons(length);
+
+		size += PARAMETER_SIZE(length);
+
+		if (size > SAP_BUF_SIZE)
+			return -EOVERFLOW;
+
+		memcpy(param->val, apdu, length);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
+					uint16_t length)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state,
+			conn->processing_req, length);
+
+	if (conn->processing_req != SAP_TRANSFER_ATR_REQ)
+		return 0;
+
+	if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00)))
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_TRANSFER_ATR_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, param);
+
+	/* Add ATR response */
+	if (result == SAP_RESULT_OK) {
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_ATR;
+		param->len = htons(length);
+		size += PARAMETER_SIZE(length);
+
+		if (size > SAP_BUF_SIZE)
+			return -EOVERFLOW;
+
+		memcpy(param->val, atr, length);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	if (conn->processing_req != SAP_POWER_SIM_OFF_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_POWER_SIM_OFF_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+	if (conn->processing_req != SAP_POWER_SIM_ON_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_POWER_SIM_ON_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_reset_sim_rsp(void *sap_device, uint8_t result)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+						conn->processing_req, result);
+
+	if (conn->processing_req != SAP_RESET_SIM_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_RESET_SIM_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+						uint8_t status)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+						conn->processing_req, result);
+
+	if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, param);
+
+	/* Add card reader status. */
+	if (result == SAP_RESULT_OK) {
+		msg->nparam++;
+		param = (struct sap_parameter *) &buf[size];
+		param->id = SAP_PARAM_ID_CARD_READER_STATUS;
+		param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+		*param->val = status;
+		size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+	}
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+						conn->processing_req, result);
+
+	if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
+		return 0;
+
+	memset(buf, 0, sizeof(buf));
+	msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP;
+	msg->nparam = 0x01;
+	size += add_result_parameter(result, msg->param);
+
+	conn->processing_req = SAP_NO_REQ;
+
+	return send_message(conn, buf, size);
+}
+
+int sap_status_ind(void *sap_device, uint8_t status_change)
+{
+	struct sap_server *server = sap_device;
+	struct sap_connection *conn = server->conn;
+	char buf[SAP_BUF_SIZE];
+	struct sap_message *msg = (struct sap_message *) buf;
+	struct sap_parameter *param = (struct sap_parameter *) msg->param;
+	size_t size = sizeof(struct sap_message);
+
+	if (!conn)
+		return -EINVAL;
+
+	DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
+								status_change);
+
+	switch (conn->state) {
+	case SAP_STATE_CONNECT_MODEM_BUSY:
+		if (status_change != SAP_STATUS_CHANGE_CARD_RESET)
+			break;
+
+		/* Change state to connected after ongoing call ended */
+		sap_set_connected(server);
+		/* fall */
+	case SAP_STATE_CONNECTED:
+	case SAP_STATE_GRACEFUL_DISCONNECT:
+		memset(buf, 0, sizeof(buf));
+		msg->id = SAP_STATUS_IND;
+		msg->nparam = 0x01;
+
+		/* Add status change. */
+		param->id  = SAP_PARAM_ID_STATUS_CHANGE;
+		param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+		*param->val = status_change;
+		size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+
+		return send_message(conn, buf, size);
+	case SAP_STATE_DISCONNECTED:
+	case SAP_STATE_CONNECT_IN_PROGRESS:
+	case SAP_STATE_IMMEDIATE_DISCONNECT:
+	case SAP_STATE_CLIENT_DISCONNECT:
+		break;
+	}
+
+	return 0;
+}
+
+int sap_disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+	return disconnect_req(sap_device, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+}
+
+static int handle_cmd(struct sap_server *server, void *buf, size_t size)
+{
+	struct sap_connection *conn = server->conn;
+	struct sap_message *msg = buf;
+
+	if (!conn)
+		return -EINVAL;
+
+	if (size < sizeof(struct sap_message))
+		goto error_rsp;
+
+	if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
+					sizeof(struct sap_parameter) + 4))
+		goto error_rsp;
+
+	if (check_msg(msg) < 0)
+		goto error_rsp;
+
+	switch (msg->id) {
+	case SAP_CONNECT_REQ:
+		connect_req(server, msg->param);
+		return 0;
+	case SAP_DISCONNECT_REQ:
+		disconnect_req(server, SAP_DISCONNECTION_TYPE_CLIENT);
+		return 0;
+	case SAP_TRANSFER_APDU_REQ:
+		transfer_apdu_req(server, msg->param);
+		return 0;
+	case SAP_TRANSFER_ATR_REQ:
+		transfer_atr_req(server);
+		return 0;
+	case SAP_POWER_SIM_OFF_REQ:
+		power_sim_off_req(server);
+		return 0;
+	case SAP_POWER_SIM_ON_REQ:
+		power_sim_on_req(server);
+		return 0;
+	case SAP_RESET_SIM_REQ:
+		reset_sim_req(server);
+		return 0;
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		transfer_card_reader_status_req(server);
+		return 0;
+	case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+		set_transport_protocol_req(server, msg->param);
+		return 0;
+	default:
+		DBG("Unknown SAP message id 0x%02x.", msg->id);
+		break;
+	}
+
+error_rsp:
+	DBG("Invalid SAP message format.");
+	sap_error_rsp(conn);
+	return -EBADMSG;
+}
+
+static void sap_server_remove_conn(struct sap_server *server)
+{
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p", conn);
+
+	if (!conn)
+		return;
+
+	if (conn->io) {
+		g_io_channel_shutdown(conn->io, TRUE, NULL);
+		g_io_channel_unref(conn->io);
+	}
+
+	g_free(conn);
+	server->conn = NULL;
+}
+
+static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+	char buf[SAP_BUF_SIZE];
+	size_t bytes_read = 0;
+	GError *gerr = NULL;
+	GIOStatus gstatus;
+
+	SAP_VDBG("conn %p io %p", conn, io);
+
+	if (cond & G_IO_NVAL) {
+		DBG("ERR (G_IO_NVAL) on rfcomm socket.");
+		return FALSE;
+	}
+
+	if (cond & G_IO_ERR) {
+		DBG("ERR (G_IO_ERR) on rfcomm socket.");
+		return FALSE;
+	}
+
+	if (cond & G_IO_HUP) {
+		DBG("HUP on rfcomm socket.");
+		return FALSE;
+	}
+
+	gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1,
+							&bytes_read, &gerr);
+	if (gstatus != G_IO_STATUS_NORMAL) {
+		if (gerr)
+			g_error_free(gerr);
+
+		return TRUE;
+	}
+
+	if (handle_cmd(data, buf, bytes_read) < 0)
+		error("SAP protocol processing failure.");
+
+	return TRUE;
+}
+
+static void sap_io_destroy(void *data)
+{
+	struct sap_server *server = data;
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p", conn);
+
+	if (!conn || !conn->io)
+		return;
+
+	stop_guard_timer(server);
+
+	if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS &&
+				conn->state != SAP_STATE_CONNECT_MODEM_BUSY)
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					adapter_get_path(server->adapter),
+					SAP_SERVER_INTERFACE,
+					"Connected");
+
+	if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+			conn->state == SAP_STATE_CONNECT_MODEM_BUSY ||
+			conn->state == SAP_STATE_CONNECTED ||
+			conn->state == SAP_STATE_GRACEFUL_DISCONNECT)
+		sap_disconnect_req(server, 1);
+
+	sap_server_remove_conn(server);
+}
+
+static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
+{
+	struct sap_server *server = data;
+	struct sap_connection *conn = server->conn;
+
+	DBG("conn %p, io %p", conn, io);
+
+	if (!conn)
+		return;
+
+	/* Timer will shutdown the channel in case of lack of client
+	   activity */
+	start_guard_timer(server, SAP_TIMER_NO_ACTIVITY);
+
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+			sap_io_cb, server, sap_io_destroy);
+}
+
+static void connect_auth_cb(DBusError *derr, void *data)
+{
+	struct sap_server *server = data;
+	struct sap_connection *conn = server->conn;
+	GError *gerr = NULL;
+
+	DBG("conn %p", conn);
+
+	if (!conn)
+		return;
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Access has been denied (%s)", derr->message);
+		sap_server_remove_conn(server);
+		return;
+	}
+
+	if (!bt_io_accept(conn->io, sap_connect_cb, server, NULL, &gerr)) {
+		error("bt_io_accept: %s", gerr->message);
+		g_error_free(gerr);
+		sap_server_remove_conn(server);
+		return;
+	}
+
+	DBG("Access has been granted.");
+}
+
+static void connect_confirm_cb(GIOChannel *io, gpointer data)
+{
+	struct sap_server *server = data;
+	struct sap_connection *conn = server->conn;
+	GError *gerr = NULL;
+	bdaddr_t src, dst;
+	char dstaddr[18];
+	guint ret;
+
+	DBG("conn %p io %p", conn, io);
+
+	if (!io)
+		return;
+
+	if (conn) {
+		DBG("Another SAP connection already exists.");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	conn = g_try_new0(struct sap_connection, 1);
+	if (!conn) {
+		error("Can't allocate memory for incoming SAP connection.");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	g_io_channel_set_encoding(io, NULL, NULL);
+	g_io_channel_set_buffered(io, FALSE);
+
+	server->conn = conn;
+	conn->io = g_io_channel_ref(io);
+	conn->state = SAP_STATE_DISCONNECTED;
+
+	bt_io_get(io, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		sap_server_remove_conn(server);
+		return;
+	}
+
+	ba2str(&dst, dstaddr);
+
+	ret = btd_request_authorization(&src, &dst, SAP_UUID, connect_auth_cb,
+								server);
+	if (ret == 0) {
+		error("Authorization failure");
+		sap_server_remove_conn(server);
+		return;
+	}
+
+	DBG("Authorizing incoming SAP connection from %s", dstaddr);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct sap_server *server = data;
+
+	if (!server)
+		return btd_error_failed(msg, "Server internal error.");
+
+	DBG("conn %p", server->conn);
+
+	if (!server->conn)
+		return btd_error_failed(msg, "Client already disconnected");
+
+	if (disconnect_req(server, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0)
+		return btd_error_failed(msg, "There is no active connection");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static gboolean server_property_get_connected(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct sap_server *server = data;
+	struct sap_connection *conn = server->conn;
+	dbus_bool_t connected;
+
+	if (!conn) {
+		connected = FALSE;
+		goto append;
+	}
+
+	connected = (conn->state == SAP_STATE_CONNECTED ||
+				conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
+
+append:
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable server_methods[] = {
+	{ GDBUS_METHOD("Disconnect", NULL, NULL, disconnect) },
+	{ }
+};
+
+static const GDBusPropertyTable server_properties[] = {
+	{ "Connected", "b", server_property_get_connected },
+	{ }
+};
+
+static void server_remove(struct sap_server *server)
+{
+	if (!server)
+		return;
+
+	sap_server_remove_conn(server);
+
+	adapter_service_remove(server->adapter, server->record_id);
+
+	if (server->listen_io) {
+		g_io_channel_shutdown(server->listen_io, TRUE, NULL);
+		g_io_channel_unref(server->listen_io);
+		server->listen_io = NULL;
+	}
+
+	btd_adapter_unref(server->adapter);
+	g_free(server);
+}
+
+static void destroy_sap_interface(void *data)
+{
+	struct sap_server *server = data;
+
+	DBG("Unregistered interface %s on path %s", SAP_SERVER_INTERFACE,
+					adapter_get_path(server->adapter));
+
+	server_remove(server);
+}
+
+int sap_server_register(struct btd_adapter *adapter)
+{
+	sdp_record_t *record = NULL;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	struct sap_server *server;
+
+	if (sap_init() < 0) {
+		error("Sap driver initialization failed.");
+		return -1;
+	}
+
+	record = create_sap_record(SAP_SERVER_CHANNEL);
+	if (!record) {
+		error("Creating SAP SDP record failed.");
+		goto sdp_err;
+	}
+
+	if (adapter_service_add(adapter, record) < 0) {
+		error("Adding SAP SDP record to the SDP server failed.");
+		sdp_record_free(record);
+		goto sdp_err;
+	}
+
+	server = g_new0(struct sap_server, 1);
+	server->adapter = btd_adapter_ref(adapter);
+	server->record_id = record->handle;
+
+	io = bt_io_listen(NULL, connect_confirm_cb, server,
+			NULL, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR,
+			btd_adapter_get_address(adapter),
+			BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
+			BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+			BT_IO_OPT_MASTER, TRUE,
+			BT_IO_OPT_INVALID);
+	if (!io) {
+		error("Can't listen at channel %d.", SAP_SERVER_CHANNEL);
+		g_error_free(gerr);
+		goto server_err;
+	}
+	server->listen_io = io;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					adapter_get_path(server->adapter),
+					SAP_SERVER_INTERFACE,
+					server_methods, NULL,
+					server_properties, server,
+					destroy_sap_interface)) {
+		error("D-Bus failed to register %s interface",
+							SAP_SERVER_INTERFACE);
+		goto server_err;
+	}
+
+	DBG("server %p, listen socket 0x%02x", server,
+						g_io_channel_unix_get_fd(io));
+
+	return 0;
+
+server_err:
+	server_remove(server);
+sdp_err:
+	sap_exit();
+
+	return -1;
+}
+
+void sap_server_unregister(const char *path)
+{
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						path, SAP_SERVER_INTERFACE);
+
+	sap_exit();
+}
diff --git a/bluez/repo/profiles/sap/server.h b/bluez/repo/profiles/sap/server.h
new file mode 100644
index 0000000..4bf9296
--- /dev/null
+++ b/bluez/repo/profiles/sap/server.h
@@ -0,0 +1,22 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ *  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
+ */
+
+int sap_server_register(struct btd_adapter *adapter);
+void sap_server_unregister(const char *path);
diff --git a/bluez/repo/profiles/scanparam/._scan.c b/bluez/repo/profiles/scanparam/._scan.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/scanparam/._scan.c
Binary files differ
diff --git a/bluez/repo/profiles/scanparam/._scpp.c b/bluez/repo/profiles/scanparam/._scpp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/scanparam/._scpp.c
Binary files differ
diff --git a/bluez/repo/profiles/scanparam/._scpp.h b/bluez/repo/profiles/scanparam/._scpp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/scanparam/._scpp.h
Binary files differ
diff --git a/bluez/repo/profiles/scanparam/scan.c b/bluez/repo/profiles/scanparam/scan.c
new file mode 100644
index 0000000..4015b3f
--- /dev/null
+++ b/bluez/repo/profiles/scanparam/scan.c
@@ -0,0 +1,283 @@
+/*
+ *
+ *  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 "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/log.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 "attrib/att.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 scan {
+	struct btd_device *device;
+	struct gatt_db *db;
+	struct bt_gatt_client *client;
+	struct gatt_db_attribute *attr;
+	uint16_t iwhandle;
+	guint refresh_cb_id;
+};
+
+static GSList *devices;
+
+static void scan_free(struct scan *scan)
+{
+	bt_gatt_client_unregister_notify(scan->client, scan->refresh_cb_id);
+	gatt_db_unref(scan->db);
+	bt_gatt_client_unref(scan->client);
+	btd_device_unref(scan->device);
+	g_free(scan);
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct scan *scan = a;
+	const struct btd_device *device = b;
+
+	return scan->device == device ? 0 : -1;
+}
+
+static void write_scan_params(struct scan *scan)
+{
+	uint8_t value[4];
+
+	put_le16(SCAN_INTERVAL, &value[0]);
+	put_le16(SCAN_WINDOW, &value[2]);
+
+	bt_gatt_client_write_without_response(scan->client, scan->iwhandle,
+						false, value, sizeof(value));
+}
+
+static void refresh_value_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct scan *scan = user_data;
+
+	DBG("Server requires refresh: %d", value[3]);
+
+	if (value[3] == SERVER_REQUIRES_REFRESH)
+		write_scan_params(scan);
+}
+
+static void refresh_ccc_written_cb(uint16_t att_ecode, void *user_data)
+{
+	if (att_ecode != 0) {
+		error("Scan Refresh: notifications not enabled %s",
+						att_ecode2str(att_ecode));
+		return;
+	}
+
+	DBG("Scan Refresh: notification enabled");
+}
+
+static void handle_refresh(struct scan *scan, uint16_t value_handle)
+{
+	DBG("Scan Refresh handle: 0x%04x", value_handle);
+
+	scan->refresh_cb_id = bt_gatt_client_register_notify(scan->client,
+					value_handle, refresh_ccc_written_cb,
+						refresh_value_cb, scan,	NULL);
+}
+
+static void handle_iwin(struct scan *scan, uint16_t value_handle)
+{
+	scan->iwhandle = value_handle;
+
+	DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle);
+
+	write_scan_params(scan);
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+								void *user_data)
+{
+	struct scan *scan = user_data;
+	uint16_t value_handle;
+	bt_uuid_t uuid, scan_interval_wind_uuid, scan_refresh_uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+								&uuid)) {
+		error("Failed to obtain characteristic data");
+		return;
+	}
+
+	bt_uuid16_create(&scan_interval_wind_uuid, SCAN_INTERVAL_WIN_UUID);
+	bt_uuid16_create(&scan_refresh_uuid, SCAN_REFRESH_UUID);
+
+	if (bt_uuid_cmp(&scan_interval_wind_uuid, &uuid) == 0)
+		handle_iwin(scan, value_handle);
+	else if (bt_uuid_cmp(&scan_refresh_uuid, &uuid) == 0)
+		handle_refresh(scan, 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_scan_param_service(struct gatt_db_attribute *attr,
+								void *user_data)
+{
+	struct scan *scan = user_data;
+
+	if (scan->attr) {
+		error("More than one scan params service exists for this "
+								"device");
+		return;
+	}
+
+	scan->attr = attr;
+	gatt_db_service_foreach_char(scan->attr, handle_characteristic, scan);
+}
+
+static int scan_param_accept(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_db *db = btd_device_get_gatt_db(device);
+	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+	bt_uuid_t scan_parameters_uuid;
+	struct scan *scan;
+	GSList *l;
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("Scan Parameters Client Driver profile accept (%s)", addr);
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (!l) {
+		error("Scan Parameters service not handled by profile");
+		return -1;
+	}
+
+	scan = l->data;
+
+	/* Clean-up any old client/db and acquire the new ones */
+	scan->attr = NULL;
+	gatt_db_unref(scan->db);
+	bt_gatt_client_unref(scan->client);
+
+
+	scan->db = gatt_db_ref(db);
+	scan->client = bt_gatt_client_ref(client);
+
+	bt_string_to_uuid(&scan_parameters_uuid, SCAN_PARAMETERS_UUID);
+	gatt_db_foreach_service(db, &scan_parameters_uuid,
+					foreach_scan_param_service, scan);
+
+	return 0;
+}
+
+static void scan_param_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct scan *scan;
+	GSList *l;
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("GAP profile remove (%s)", addr);
+
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (!l) {
+		error("GAP service not handled by profile");
+		return;
+	}
+
+	scan = l->data;
+
+	devices = g_slist_remove(devices, scan);
+	scan_free(scan);
+}
+
+static int scan_param_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct scan *scan;
+	GSList *l;
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("Scan Parameters Client Driver profile probe (%s)", addr);
+
+	/* Ignore, if we were probed for this device already */
+	l = g_slist_find_custom(devices, device, cmp_device);
+	if (l) {
+		error("Profile probed twice for the same device!");
+		return -1;
+	}
+
+	scan = g_new0(struct scan, 1);
+	if (!scan)
+		return -1;
+
+	scan->device = btd_device_ref(device);
+	devices = g_slist_append(devices, scan);
+	return 0;
+}
+
+static struct btd_profile scan_profile = {
+	.name = "Scan Parameters Client Driver",
+	.remote_uuid = SCAN_PARAMETERS_UUID,
+	.device_probe = scan_param_probe,
+	.device_remove = scan_param_remove,
+	.accept = scan_param_accept,
+};
+
+static int scan_param_init(void)
+{
+	return btd_profile_register(&scan_profile);
+}
+
+static void scan_param_exit(void)
+{
+	btd_profile_unregister(&scan_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(scanparam, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+			scan_param_init, scan_param_exit)
diff --git a/bluez/repo/profiles/scanparam/scpp.c b/bluez/repo/profiles/scanparam/scpp.c
new file mode 100644
index 0000000..df65d2c
--- /dev/null
+++ b/bluez/repo/profiles/scanparam/scpp.c
@@ -0,0 +1,355 @@
+/*
+ *
+ *  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 "profiles/scanparam/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);
+
+	queue_push_head(scpp->gatt_op, UINT_TO_PTR(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);
+
+	queue_push_head(scpp->gatt_op, UINT_TO_PTR(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);
+
+	queue_push_head(scan->gatt_op, UINT_TO_PTR(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 (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/bluez/repo/profiles/scanparam/scpp.h b/bluez/repo/profiles/scanparam/scpp.h
new file mode 100644
index 0000000..048fb9f
--- /dev/null
+++ b/bluez/repo/profiles/scanparam/scpp.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  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);
diff --git a/bluez/repo/profiles/thermometer/._thermometer.c b/bluez/repo/profiles/thermometer/._thermometer.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/thermometer/._thermometer.c
Binary files differ
diff --git a/bluez/repo/profiles/thermometer/thermometer.c b/bluez/repo/profiles/thermometer/thermometer.c
new file mode 100644
index 0000000..b0fc3e0
--- /dev/null
+++ b/bluez/repo/profiles/thermometer/thermometer.c
@@ -0,0 +1,1321 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#define THERMOMETER_INTERFACE		"org.bluez.Thermometer1"
+#define THERMOMETER_MANAGER_INTERFACE	"org.bluez.ThermometerManager1"
+#define THERMOMETER_WATCHER_INTERFACE	"org.bluez.ThermometerWatcher1"
+
+/* Temperature measurement flag fields */
+#define TEMP_UNITS		0x01
+#define TEMP_TIME_STAMP		0x02
+#define TEMP_TYPE		0x04
+
+#define FLOAT_MAX_MANTISSA	16777216 /* 2^24 */
+
+#define VALID_RANGE_DESC_SIZE	4
+#define TEMPERATURE_TYPE_SIZE	1
+#define MEASUREMENT_INTERVAL_SIZE	2
+
+struct thermometer_adapter {
+	struct btd_adapter	*adapter;
+	GSList			*devices;
+	GSList			*fwatchers;	/* Final measurements */
+	GSList			*iwatchers;	/* Intermediate measurements */
+};
+
+struct thermometer {
+	struct btd_device		*dev;		/* Device reference */
+	struct thermometer_adapter	*tadapter;
+	GAttrib				*attrib;	/* GATT connection */
+	struct att_range		*svc_range;	/* Thermometer range */
+	guint				attioid;	/* Att watcher id */
+	/* attio id for Temperature Measurement value indications */
+	guint				attio_measurement_id;
+	/* attio id for Intermediate Temperature value notifications */
+	guint				attio_intermediate_id;
+	/* attio id for Measurement Interval value indications */
+	guint				attio_interval_id;
+	gboolean			intermediate;
+	uint8_t				type;
+	uint16_t			interval;
+	uint16_t			max;
+	uint16_t			min;
+	gboolean			has_type;
+	gboolean			has_interval;
+
+	uint16_t			measurement_ccc_handle;
+	uint16_t			intermediate_ccc_handle;
+	uint16_t			interval_val_handle;
+};
+
+struct characteristic {
+	struct thermometer	*t;	/* Thermometer where the char belongs */
+	char			uuid[MAX_LEN_UUID_STR + 1];
+};
+
+struct watcher {
+	struct thermometer_adapter	*tadapter;
+	guint				id;
+	char				*srv;
+	char				*path;
+};
+
+struct measurement {
+	struct thermometer	*t;
+	int16_t			exp;
+	int32_t			mant;
+	uint64_t		time;
+	gboolean		suptime;
+	char			*unit;
+	char			*type;
+	char			*value;
+};
+
+struct tmp_interval_data {
+	struct thermometer	*thermometer;
+	uint16_t		interval;
+};
+
+static GSList *thermometer_adapters = NULL;
+
+static const char * const temp_type[] = {
+	"<reserved>",
+	"armpit",
+	"body",
+	"ear",
+	"finger",
+	"intestines",
+	"mouth",
+	"rectum",
+	"toe",
+	"tympanum"
+};
+
+static const char *temptype2str(uint8_t value)
+{
+	 if (value > 0 && value < G_N_ELEMENTS(temp_type))
+		return temp_type[value];
+
+	error("Temperature type %d reserved for future use", value);
+	return NULL;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_free(watcher->path);
+	g_free(watcher->srv);
+	g_free(watcher);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+	struct watcher *watcher = user_data;
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_thermometer(gpointer user_data)
+{
+	struct thermometer *t = user_data;
+
+	if (t->attioid > 0)
+		btd_device_remove_attio_callback(t->dev, t->attioid);
+
+	if (t->attrib != NULL) {
+		g_attrib_unregister(t->attrib, t->attio_measurement_id);
+		g_attrib_unregister(t->attrib, t->attio_intermediate_id);
+		g_attrib_unregister(t->attrib, t->attio_interval_id);
+		g_attrib_unref(t->attrib);
+	}
+
+	btd_device_unref(t->dev);
+	g_free(t->svc_range);
+	g_free(t);
+}
+
+static void destroy_thermometer_adapter(gpointer user_data)
+{
+	struct thermometer_adapter *tadapter = user_data;
+
+	if (tadapter->devices != NULL)
+		g_slist_free_full(tadapter->devices, destroy_thermometer);
+
+	if (tadapter->fwatchers != NULL)
+		g_slist_free_full(tadapter->fwatchers, remove_watcher);
+
+	g_free(tadapter);
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+	const struct thermometer_adapter *tadapter = a;
+	const struct btd_adapter *adapter = b;
+
+	if (adapter == tadapter->adapter)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+	const struct thermometer *t = a;
+	const struct btd_device *dev = b;
+
+	if (dev == t->dev)
+		return 0;
+
+	return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+	const struct watcher *watcher = a;
+	const struct watcher *match = b;
+	int ret;
+
+	ret = g_strcmp0(watcher->srv, match->srv);
+	if (ret != 0)
+		return ret;
+
+	return g_strcmp0(watcher->path, match->path);
+}
+
+static struct thermometer_adapter *
+find_thermometer_adapter(struct btd_adapter *adapter)
+{
+	GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
+								cmp_adapter);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static void change_property(struct thermometer *t, const char *name,
+							gpointer value) {
+	if (g_strcmp0(name, "Intermediate") == 0) {
+		gboolean *intermediate = value;
+		if (t->intermediate == *intermediate)
+			return;
+
+		t->intermediate = *intermediate;
+	} else if (g_strcmp0(name, "Interval") == 0) {
+		uint16_t *interval = value;
+		if (t->has_interval && t->interval == *interval)
+			return;
+
+		t->has_interval = TRUE;
+		t->interval = *interval;
+	} else if (g_strcmp0(name, "Maximum") == 0) {
+		uint16_t *max = value;
+		if (t->max == *max)
+			return;
+
+		t->max = *max;
+	} else if (g_strcmp0(name, "Minimum") == 0) {
+		uint16_t *min = value;
+		if (t->min == *min)
+			return;
+
+		t->min = *min;
+	} else {
+		DBG("%s is not a thermometer property", name);
+		return;
+	}
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						device_get_path(t->dev),
+						THERMOMETER_INTERFACE, name);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+	struct watcher *w = data;
+	struct measurement *m = user_data;
+	const char *path = device_get_path(m->t->dev);
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(w->srv, w->path,
+				THERMOMETER_WATCHER_INTERFACE,
+				"MeasurementReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
+	dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
+	dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
+
+	if (m->suptime)
+		dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
+
+	dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
+	dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void recv_measurement(struct thermometer *t, struct measurement *m)
+{
+	GSList *wlist;
+
+	m->t = t;
+
+	if (g_strcmp0(m->value, "intermediate") == 0)
+		wlist = t->tadapter->iwatchers;
+	else
+		wlist = t->tadapter->fwatchers;
+
+	g_slist_foreach(wlist, update_watcher, m);
+}
+
+static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
+						uint16_t len, gboolean final)
+{
+	struct measurement m;
+	const char *type = NULL;
+	uint8_t flags;
+	uint32_t raw;
+
+	/* skip opcode and handle */
+	pdu += 3;
+	len -= 3;
+
+	if (len < 1) {
+		DBG("Mandatory flags are not provided");
+		return;
+	}
+
+	memset(&m, 0, sizeof(m));
+
+	flags = *pdu;
+
+	if (flags & TEMP_UNITS)
+		m.unit = "fahrenheit";
+	else
+		m.unit = "celsius";
+
+	pdu++;
+	len--;
+
+	if (len < 4) {
+		DBG("Mandatory temperature measurement value is not provided");
+		return;
+	}
+
+	raw = get_le32(pdu);
+	m.mant = raw & 0x00FFFFFF;
+	m.exp = ((int32_t) raw) >> 24;
+
+	if (m.mant & 0x00800000) {
+		/* convert to C2 negative value */
+		m.mant = m.mant - FLOAT_MAX_MANTISSA;
+	}
+
+	pdu += 4;
+	len -= 4;
+
+	if (flags & TEMP_TIME_STAMP) {
+		struct tm ts;
+		time_t time;
+
+		if (len < 7) {
+			DBG("Time stamp is not provided");
+			return;
+		}
+
+		ts.tm_year = get_le16(pdu) - 1900;
+		ts.tm_mon = *(pdu + 2) - 1;
+		ts.tm_mday = *(pdu + 3);
+		ts.tm_hour = *(pdu + 4);
+		ts.tm_min = *(pdu + 5);
+		ts.tm_sec = *(pdu + 6);
+		ts.tm_isdst = -1;
+
+		time = mktime(&ts);
+		m.time = (uint64_t) time;
+		m.suptime = TRUE;
+
+		pdu += 7;
+		len -= 7;
+	}
+
+	if (flags & TEMP_TYPE) {
+		if (len < 1) {
+			DBG("Temperature type is not provided");
+			return;
+		}
+
+		type = temptype2str(*pdu);
+	} else if (t->has_type) {
+		type = temptype2str(t->type);
+	}
+
+	m.type = g_strdup(type);
+	m.value = final ? "final" : "intermediate";
+
+	recv_measurement(t, &m);
+	g_free(m.type);
+}
+
+
+static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct thermometer *t = user_data;
+	uint8_t *opdu;
+	uint16_t olen;
+	size_t plen;
+
+	if (len < 3) {
+		DBG("Bad pdu received");
+		return;
+	}
+
+	proc_measurement(t, pdu, len, TRUE);
+
+	opdu = g_attrib_get_buffer(t->attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+
+	if (olen > 0)
+		g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct thermometer *t = user_data;
+
+	if (len < 3) {
+		DBG("Bad pdu received");
+		return;
+	}
+
+	proc_measurement(t, pdu, len, FALSE);
+}
+
+static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
+							gpointer user_data)
+{
+	struct thermometer *t = user_data;
+	uint16_t interval;
+	uint8_t *opdu;
+	uint16_t olen;
+	size_t plen;
+
+	if (len < 5) {
+		DBG("Bad pdu received");
+		return;
+	}
+
+	interval = get_le16(pdu + 3);
+	change_property(t, "Interval", &interval);
+
+	opdu = g_attrib_get_buffer(t->attrib, &plen);
+	olen = enc_confirmation(opdu, plen);
+
+	if (olen > 0)
+		g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct thermometer *t = user_data;
+	uint8_t value[VALID_RANGE_DESC_SIZE];
+	uint16_t max, min;
+	ssize_t vlen;
+
+	if (status != 0) {
+		DBG("Valid Range descriptor read failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, value, sizeof(value));
+	if (vlen < 0) {
+		DBG("Protocol error\n");
+		return;
+	}
+
+	if (vlen < 4) {
+		DBG("Invalid range received");
+		return;
+	}
+
+	min = get_le16(&value[0]);
+	max = get_le16(&value[2]);
+
+	if (min == 0 || min > max) {
+		DBG("Invalid range");
+		return;
+	}
+
+	change_property(t, "Maximum", &max);
+	change_property(t, "Minimum", &min);
+}
+
+static void write_ccc_cb(guint8 status, const guint8 *pdu,
+						guint16 len, gpointer user_data)
+{
+	char *msg = user_data;
+
+	if (status != 0)
+		error("%s failed", msg);
+
+	g_free(msg);
+}
+
+static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
+								uint16_t handle)
+{
+	uint8_t atval[2];
+	uint16_t val;
+	char *msg;
+
+	if (uuid == GATT_CHARAC_VALID_RANGE_UUID) {
+		if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+			gatt_read_char(ch->t->attrib, handle,
+						valid_range_desc_cb, ch->t);
+		return;
+	}
+
+	if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+		return;
+
+	if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
+		ch->t->measurement_ccc_handle = handle;
+
+		if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
+			val = 0x0000;
+			msg = g_strdup("Disable Temperature Measurement ind");
+		} else {
+			val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+			msg = g_strdup("Enable Temperature Measurement ind");
+		}
+	} else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+		ch->t->intermediate_ccc_handle = handle;
+
+		if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
+			val = 0x0000;
+			msg = g_strdup("Disable Intermediate Temperature noti");
+		} else {
+			val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
+			msg = g_strdup("Enable Intermediate Temperature noti");
+		}
+	} else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
+		val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+		msg = g_strdup("Enable Measurement Interval indication");
+	} else {
+		return;
+	}
+
+	put_le16(val, atval);
+	gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
+							write_ccc_cb, msg);
+}
+
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
+{
+	struct characteristic *ch = user_data;
+
+	if (status != 0) {
+		error("Discover all characteristic descriptors failed [%s]: %s",
+					ch->uuid, att_ecode2str(status));
+		goto done;
+	}
+
+	for ( ; descs; descs = descs->next) {
+		struct gatt_desc *desc = descs->data;
+
+		process_thermometer_desc(ch, desc->uuid16, desc->handle);
+	}
+
+done:
+	g_free(ch);
+}
+
+static void discover_desc(struct thermometer *t, struct gatt_char *c,
+						struct gatt_char *c_next)
+{
+	struct characteristic *ch;
+	uint16_t start, end;
+
+	start = c->value_handle + 1;
+
+	if (c_next != NULL) {
+		if (start == c_next->handle)
+			return;
+		end = c_next->handle - 1;
+	} else if (c->value_handle != t->svc_range->end) {
+		end = t->svc_range->end;
+	} else {
+		return;
+	}
+
+	ch = g_new0(struct characteristic, 1);
+	ch->t = t;
+	memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+	gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
+}
+
+static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct thermometer *t = user_data;
+	uint8_t value[TEMPERATURE_TYPE_SIZE];
+	ssize_t vlen;
+
+	if (status != 0) {
+		DBG("Temperature Type value read failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, value, sizeof(value));
+	if (vlen < 0) {
+		DBG("Protocol error.");
+		return;
+	}
+
+	if (vlen != 1) {
+		DBG("Invalid length for Temperature type");
+		return;
+	}
+
+	t->has_type = TRUE;
+	t->type = value[0];
+}
+
+static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct thermometer *t = user_data;
+	uint8_t value[MEASUREMENT_INTERVAL_SIZE];
+	uint16_t interval;
+	ssize_t vlen;
+
+	if (status != 0) {
+		DBG("Measurement Interval value read failed: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	vlen = dec_read_resp(pdu, len, value, sizeof(value));
+	if (vlen < 0) {
+		DBG("Protocol error\n");
+		return;
+	}
+
+	if (vlen < 2) {
+		DBG("Invalid Interval received");
+		return;
+	}
+
+	interval = get_le16(&value[0]);
+	change_property(t, "Interval", &interval);
+}
+
+static void process_thermometer_char(struct thermometer *t,
+				struct gatt_char *c, struct gatt_char *c_next)
+{
+	if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+		gboolean intermediate = TRUE;
+		change_property(t, "Intermediate", &intermediate);
+
+		t->attio_intermediate_id = g_attrib_register(t->attrib,
+					ATT_OP_HANDLE_NOTIFY, c->value_handle,
+					intermediate_notify_handler, t, NULL);
+
+		discover_desc(t, c, c_next);
+	} else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
+
+		t->attio_measurement_id = g_attrib_register(t->attrib,
+					ATT_OP_HANDLE_IND, c->value_handle,
+					measurement_ind_handler, t, NULL);
+
+		discover_desc(t, c, c_next);
+	} else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) {
+		gatt_read_char(t->attrib, c->value_handle,
+							read_temp_type_cb, t);
+	} else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
+		bool need_desc = false;
+
+		gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
+
+		if (c->properties & GATT_CHR_PROP_WRITE) {
+			t->interval_val_handle = c->value_handle;
+			need_desc = true;
+		}
+
+		if (c->properties & GATT_CHR_PROP_INDICATE) {
+			t->attio_interval_id = g_attrib_register(t->attrib,
+					ATT_OP_HANDLE_IND, c->value_handle,
+					interval_ind_handler, t, NULL);
+			need_desc = true;
+		}
+
+		if (need_desc)
+			discover_desc(t, c, c_next);
+	}
+}
+
+static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct thermometer *t = user_data;
+	GSList *l;
+
+	if (status != 0) {
+		error("Discover thermometer characteristics: %s",
+							att_ecode2str(status));
+		return;
+	}
+
+	for (l = characteristics; l; l = l->next) {
+		struct gatt_char *c = l->data;
+		struct gatt_char *c_next = (l->next ? l->next->data : NULL);
+
+		process_thermometer_char(t, c, c_next);
+	}
+}
+
+static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct tmp_interval_data *data = user_data;
+
+	if (status != 0) {
+		error("Interval Write Request failed %s",
+							att_ecode2str(status));
+		goto done;
+	}
+
+	if (!dec_write_resp(pdu, len)) {
+		error("Interval Write Request: protocol error");
+		goto done;
+	}
+
+	change_property(data->thermometer, "Interval", &data->interval);
+
+done:
+	g_free(user_data);
+}
+
+static void enable_final_measurement(gpointer data, gpointer user_data)
+{
+	struct thermometer *t = data;
+	uint16_t handle = t->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (t->attrib == NULL || !handle)
+		return;
+
+	put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
+	msg = g_strdup("Enable Temperature Measurement indications");
+
+	gatt_write_char(t->attrib, handle, value, sizeof(value),
+							write_ccc_cb, msg);
+}
+
+static void enable_intermediate_measurement(gpointer data, gpointer user_data)
+{
+	struct thermometer *t = data;
+	uint16_t handle = t->intermediate_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (t->attrib == NULL || !handle)
+		return;
+
+	put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+	msg = g_strdup("Enable Intermediate Temperature notifications");
+
+	gatt_write_char(t->attrib, handle, value, sizeof(value),
+							write_ccc_cb, msg);
+}
+
+static void disable_final_measurement(gpointer data, gpointer user_data)
+{
+	struct thermometer *t = data;
+	uint16_t handle = t->measurement_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (t->attrib == NULL || !handle)
+		return;
+
+	put_le16(0x0000, value);
+	msg = g_strdup("Disable Temperature Measurement indications");
+
+	gatt_write_char(t->attrib, handle, value, sizeof(value),
+							write_ccc_cb, msg);
+}
+
+static void disable_intermediate_measurement(gpointer data, gpointer user_data)
+{
+	struct thermometer *t = data;
+	uint16_t handle = t->intermediate_ccc_handle;
+	uint8_t value[2];
+	char *msg;
+
+	if (t->attrib == NULL || !handle)
+		return;
+
+	put_le16(0x0000, value);
+	msg = g_strdup("Disable Intermediate Temperature notifications");
+
+	gatt_write_char(t->attrib, handle, value, sizeof(value),
+							write_ccc_cb, msg);
+}
+
+static void remove_int_watcher(struct thermometer_adapter *tadapter,
+							struct watcher *w)
+{
+	if (!g_slist_find(tadapter->iwatchers, w))
+		return;
+
+	tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
+
+	if (g_slist_length(tadapter->iwatchers) == 0)
+		g_slist_foreach(tadapter->devices,
+					disable_intermediate_measurement, 0);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+	struct watcher *watcher = user_data;
+	struct thermometer_adapter *tadapter = watcher->tadapter;
+
+	DBG("Thermometer watcher %s disconnected", watcher->path);
+
+	remove_int_watcher(tadapter, watcher);
+
+	tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+
+	if (g_slist_length(tadapter->fwatchers) == 0)
+		g_slist_foreach(tadapter->devices,
+					disable_final_measurement, 0);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+							const char *path)
+{
+	struct watcher *match;
+	GSList *l;
+
+	match = g_new0(struct watcher, 1);
+	match->srv = g_strdup(sender);
+	match->path = g_strdup(path);
+
+	l = g_slist_find_custom(list, match, cmp_watcher);
+	destroy_watcher(match);
+
+	if (l != NULL)
+		return l->data;
+
+	return NULL;
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	struct thermometer_adapter *tadapter = data;
+	struct watcher *watcher;
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(tadapter->fwatchers, sender, path);
+	if (watcher != NULL)
+		return btd_error_already_exists(msg);
+
+	DBG("Thermometer watcher %s registered", path);
+
+	watcher = g_new0(struct watcher, 1);
+	watcher->srv = g_strdup(sender);
+	watcher->path = g_strdup(path);
+	watcher->tadapter = tadapter;
+	watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+						watcher, destroy_watcher);
+
+	if (g_slist_length(tadapter->fwatchers) == 0)
+		g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
+
+	tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	struct thermometer_adapter *tadapter = data;
+	struct watcher *watcher;
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(tadapter->fwatchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	DBG("Thermometer watcher %s unregistered", path);
+
+	remove_int_watcher(tadapter, watcher);
+
+	tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
+	g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+
+	if (g_slist_length(tadapter->fwatchers) == 0)
+		g_slist_foreach(tadapter->devices,
+					disable_final_measurement, 0);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	struct thermometer_adapter *ta = data;
+	struct watcher *watcher;
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(ta->fwatchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	if (find_watcher(ta->iwatchers, sender, path))
+		return btd_error_already_exists(msg);
+
+	DBG("Intermediate measurement watcher %s registered", path);
+
+	if (g_slist_length(ta->iwatchers) == 0)
+		g_slist_foreach(ta->devices,
+					enable_intermediate_measurement, 0);
+
+	ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	const char *sender = dbus_message_get_sender(msg);
+	struct thermometer_adapter *ta = data;
+	struct watcher *watcher;
+	char *path;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	watcher = find_watcher(ta->iwatchers, sender, path);
+	if (watcher == NULL)
+		return btd_error_does_not_exist(msg);
+
+	DBG("Intermediate measurement %s unregistered", path);
+
+	remove_int_watcher(ta, watcher);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static gboolean property_get_intermediate(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct thermometer *t = data;
+	dbus_bool_t val;
+
+	val = !!t->intermediate;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static gboolean property_get_interval(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct thermometer *t = data;
+
+	if (!t->has_interval)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
+
+	return TRUE;
+}
+
+static void property_set_interval(const GDBusPropertyTable *property,
+					DBusMessageIter *iter,
+					GDBusPendingPropertySet id, void *data)
+{
+	struct thermometer *t = data;
+	struct tmp_interval_data *interval_data;
+	uint16_t val;
+	uint8_t atval[2];
+
+	if (t->interval_val_handle == 0) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".NotSupported",
+					"Operation is not supported");
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &val);
+
+	if (val < t->min || val > t->max) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	put_le16(val, &atval[0]);
+
+	interval_data = g_new0(struct tmp_interval_data, 1);
+	interval_data->thermometer = t;
+	interval_data->interval = val;
+	gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval),
+					write_interval_cb, interval_data);
+
+	g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_interval(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct thermometer *t = data;
+
+	return t->has_interval;
+}
+
+static gboolean property_get_maximum(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct thermometer *t = data;
+
+	if (!t->has_interval)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
+
+	return TRUE;
+}
+
+static gboolean property_get_minimum(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct thermometer *t = data;
+
+	if (!t->has_interval)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable thermometer_properties[] = {
+	{ "Intermediate", "b", property_get_intermediate },
+	{ "Interval", "q", property_get_interval, property_set_interval,
+						property_exists_interval },
+	{ "Maximum", "q", property_get_maximum, NULL,
+						property_exists_interval },
+	{ "Minimum", "q", property_get_minimum, NULL,
+						property_exists_interval },
+	{ }
+};
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+	struct thermometer *t = user_data;
+
+	t->attrib = g_attrib_ref(attrib);
+
+	gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
+					NULL, configure_thermometer_cb, t);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+	struct thermometer *t = user_data;
+
+	DBG("GATT Disconnected");
+
+	if (t->attio_measurement_id > 0) {
+		g_attrib_unregister(t->attrib, t->attio_measurement_id);
+		t->attio_measurement_id = 0;
+	}
+
+	if (t->attio_intermediate_id > 0) {
+		g_attrib_unregister(t->attrib, t->attio_intermediate_id);
+		t->attio_intermediate_id = 0;
+	}
+
+	if (t->attio_interval_id > 0) {
+		g_attrib_unregister(t->attrib, t->attio_interval_id);
+		t->attio_interval_id = 0;
+	}
+
+	g_attrib_unref(t->attrib);
+	t->attrib = NULL;
+}
+
+static int thermometer_register(struct btd_device *device,
+						struct gatt_primary *tattr)
+{
+	const char *path = device_get_path(device);
+	struct thermometer *t;
+	struct btd_adapter *adapter;
+	struct thermometer_adapter *tadapter;
+
+	adapter = device_get_adapter(device);
+
+	tadapter = find_thermometer_adapter(adapter);
+
+	if (tadapter == NULL)
+		return -1;
+
+	t = g_new0(struct thermometer, 1);
+	t->dev = btd_device_ref(device);
+	t->tadapter = tadapter;
+	t->svc_range = g_new0(struct att_range, 1);
+	t->svc_range->start = tattr->range.start;
+	t->svc_range->end = tattr->range.end;
+
+	tadapter->devices = g_slist_prepend(tadapter->devices, t);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+				path, THERMOMETER_INTERFACE,
+				NULL, NULL, thermometer_properties,
+				t, destroy_thermometer)) {
+		error("D-Bus failed to register %s interface",
+							THERMOMETER_INTERFACE);
+		destroy_thermometer(t);
+		return -EIO;
+	}
+
+	t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+						attio_disconnected_cb, t);
+	return 0;
+}
+
+static void thermometer_unregister(struct btd_device *device)
+{
+	struct thermometer *t;
+	struct btd_adapter *adapter;
+	struct thermometer_adapter *tadapter;
+	GSList *l;
+
+	adapter = device_get_adapter(device);
+
+	tadapter = find_thermometer_adapter(adapter);
+
+	if (tadapter == NULL)
+		return;
+
+	l = g_slist_find_custom(tadapter->devices, device, cmp_device);
+	if (l == NULL)
+		return;
+
+	t = l->data;
+
+	tadapter->devices = g_slist_remove(tadapter->devices, t);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				device_get_path(t->dev), THERMOMETER_INTERFACE);
+}
+
+static const GDBusMethodTable thermometer_manager_methods[] = {
+	{ GDBUS_METHOD("RegisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			register_watcher) },
+	{ GDBUS_METHOD("UnregisterWatcher",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			unregister_watcher) },
+	{ GDBUS_METHOD("EnableIntermediateMeasurement",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			enable_intermediate) },
+	{ GDBUS_METHOD("DisableIntermediateMeasurement",
+			GDBUS_ARGS({ "agent", "o" }), NULL,
+			disable_intermediate) },
+	{ }
+};
+
+static int thermometer_adapter_register(struct btd_adapter *adapter)
+{
+	struct thermometer_adapter *tadapter;
+
+	tadapter = g_new0(struct thermometer_adapter, 1);
+	tadapter->adapter = adapter;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+						adapter_get_path(adapter),
+						THERMOMETER_MANAGER_INTERFACE,
+						thermometer_manager_methods,
+						NULL, NULL, tadapter,
+						destroy_thermometer_adapter)) {
+		error("D-Bus failed to register %s interface",
+						THERMOMETER_MANAGER_INTERFACE);
+		destroy_thermometer_adapter(tadapter);
+		return -EIO;
+	}
+
+	thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
+
+	return 0;
+}
+
+static void thermometer_adapter_unregister(struct btd_adapter *adapter)
+{
+	struct thermometer_adapter *tadapter;
+
+	tadapter = find_thermometer_adapter(adapter);
+	if (tadapter == NULL)
+		return;
+
+	thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(tadapter->adapter),
+					THERMOMETER_MANAGER_INTERFACE);
+}
+
+static int thermometer_device_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_primary *tattr;
+
+	tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
+	if (tattr == NULL)
+		return -EINVAL;
+
+	return thermometer_register(device, tattr);
+}
+
+static void thermometer_device_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+
+	thermometer_unregister(device);
+}
+
+static int thermometer_adapter_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	return thermometer_adapter_register(adapter);
+}
+
+static void thermometer_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	thermometer_adapter_unregister(adapter);
+}
+
+static struct btd_profile thermometer_profile = {
+	.name		= "Health Thermometer GATT driver",
+	.remote_uuid	= HEALTH_THERMOMETER_UUID,
+	.device_probe	= thermometer_device_probe,
+	.device_remove	= thermometer_device_remove,
+	.adapter_probe	= thermometer_adapter_probe,
+	.adapter_remove	= thermometer_adapter_remove
+};
+
+static int thermometer_init(void)
+{
+	return btd_profile_register(&thermometer_profile);
+}
+
+static void thermometer_exit(void)
+{
+	btd_profile_unregister(&thermometer_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+					thermometer_init, thermometer_exit)
diff --git a/bluez/repo/profiles/time/._server.c b/bluez/repo/profiles/time/._server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/profiles/time/._server.c
Binary files differ
diff --git a/bluez/repo/profiles/time/server.c b/bluez/repo/profiles/time/server.c
new file mode 100644
index 0000000..2289c6a
--- /dev/null
+++ b/bluez/repo/profiles/time/server.c
@@ -0,0 +1,283 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/plugin.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/shared/util.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt-service.h"
+#include "src/log.h"
+
+#define CURRENT_TIME_SVC_UUID		0x1805
+#define REF_TIME_UPDATE_SVC_UUID	0x1806
+
+#define LOCAL_TIME_INFO_CHR_UUID	0x2A0F
+#define TIME_UPDATE_CTRL_CHR_UUID	0x2A16
+#define TIME_UPDATE_STAT_CHR_UUID	0x2A17
+#define CT_TIME_CHR_UUID		0x2A2B
+
+enum {
+	UPDATE_RESULT_SUCCESSFUL = 0,
+	UPDATE_RESULT_CANCELED = 1,
+	UPDATE_RESULT_NO_CONN = 2,
+	UPDATE_RESULT_ERROR = 3,
+	UPDATE_RESULT_TIMEOUT = 4,
+	UPDATE_RESULT_NOT_ATTEMPTED = 5,
+};
+
+enum {
+	UPDATE_STATE_IDLE = 0,
+	UPDATE_STATE_PENDING = 1,
+};
+
+enum {
+	GET_REFERENCE_UPDATE = 1,
+	CANCEL_REFERENCE_UPDATE = 2,
+};
+
+static int encode_current_time(uint8_t value[10])
+{
+	struct timespec tp;
+	struct tm tm;
+
+	if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
+		int err = -errno;
+
+		error("clock_gettime: %s", strerror(-err));
+		return err;
+	}
+
+	if (localtime_r(&tp.tv_sec, &tm) == NULL) {
+		error("localtime_r() failed");
+		/* localtime_r() does not set errno */
+		return -EINVAL;
+	}
+
+	put_le16(1900 + tm.tm_year, &value[0]); /* Year */
+	value[2] = tm.tm_mon + 1; /* Month */
+	value[3] = tm.tm_mday; /* Day */
+	value[4] = tm.tm_hour; /* Hours */
+	value[5] = tm.tm_min; /* Minutes */
+	value[6] = tm.tm_sec; /* Seconds */
+	value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
+	/* From Time Profile spec: "The number of 1/256 fractions of a second."
+	 * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
+	 * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
+	value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
+	value[9] = 0x00; /* Adjust Reason */
+
+	return 0;
+}
+
+static uint8_t current_time_read(struct attribute *a,
+				 struct btd_device *device, gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint8_t value[10];
+
+	if (encode_current_time(value) < 0)
+		return ATT_ECODE_IO;
+
+	attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+	return 0;
+}
+
+static uint8_t local_time_info_read(struct attribute *a,
+				struct btd_device *device, gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint8_t value[2];
+
+	DBG("a=%p", a);
+
+	tzset();
+
+	/* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
+	 * format (offset from UTC in number of 15 minutes increments). */
+	value[0] = (uint8_t) (-1 * timezone / (60 * 15));
+
+	/* FIXME: POSIX "daylight" variable only indicates whether there
+	 * is DST for the local time or not. The offset is unknown. */
+	value[1] = daylight ? 0xff : 0x00;
+
+	attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+	return 0;
+}
+
+static gboolean register_current_time_service(struct btd_adapter *adapter)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
+
+	/* Current Time service */
+	return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+				/* CT Time characteristic */
+				GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
+				GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+							GATT_CHR_PROP_NOTIFY,
+				GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+						current_time_read, adapter,
+
+				/* Local Time Information characteristic */
+				GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
+				GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+				GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+						local_time_info_read, adapter,
+
+				GATT_OPT_INVALID);
+}
+
+static uint8_t time_update_control(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	DBG("handle 0x%04x", a->handle);
+
+	if (a->len != 1)
+		DBG("Invalid control point value size: %zu", a->len);
+
+	switch (a->data[0]) {
+	case GET_REFERENCE_UPDATE:
+		DBG("Get Reference Update");
+		break;
+	case CANCEL_REFERENCE_UPDATE:
+		DBG("Cancel Reference Update");
+		break;
+	default:
+		DBG("Unknown command: 0x%02x", a->data[0]);
+	}
+
+	return 0;
+}
+
+static uint8_t time_update_status(struct attribute *a,
+						struct btd_device *device,
+						gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint8_t value[2];
+
+	DBG("handle 0x%04x", a->handle);
+
+	value[0] = UPDATE_STATE_IDLE;
+	value[1] = UPDATE_RESULT_SUCCESSFUL;
+	attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+	return 0;
+}
+
+static gboolean register_ref_time_update_service(struct btd_adapter *adapter)
+{
+	bt_uuid_t uuid;
+
+	bt_uuid16_create(&uuid, REF_TIME_UPDATE_SVC_UUID);
+
+	/* Reference Time Update service */
+	return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+				/* Time Update control point */
+				GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID,
+				GATT_OPT_CHR_PROPS,
+					GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+				GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+						time_update_control, adapter,
+
+				/* Time Update status */
+				GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID,
+				GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+				GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+						time_update_status, adapter,
+
+				GATT_OPT_INVALID);
+}
+
+static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+
+	if (!register_current_time_service(adapter)) {
+		error("Current Time Service could not be registered");
+		return -EIO;
+	}
+
+	if (!register_ref_time_update_service(adapter)) {
+		error("Reference Time Update Service could not be registered");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void time_server_exit(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	const char *path = adapter_get_path(adapter);
+
+	DBG("path %s", path);
+}
+
+struct btd_profile time_profile = {
+	.name		= "gatt-time-server",
+	.adapter_probe	= time_server_init,
+	.adapter_remove	= time_server_exit,
+};
+
+static int time_init(void)
+{
+	return btd_profile_register(&time_profile);
+}
+
+static void time_exit(void)
+{
+	btd_profile_unregister(&time_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+			time_init, time_exit)
diff --git a/bluez/repo/src/._adapter.c b/bluez/repo/src/._adapter.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._adapter.c
Binary files differ
diff --git a/bluez/repo/src/._adapter.h b/bluez/repo/src/._adapter.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._adapter.h
Binary files differ
diff --git a/bluez/repo/src/._advertising.c b/bluez/repo/src/._advertising.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._advertising.c
Binary files differ
diff --git a/bluez/repo/src/._advertising.h b/bluez/repo/src/._advertising.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._advertising.h
Binary files differ
diff --git a/bluez/repo/src/._agent.c b/bluez/repo/src/._agent.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._agent.c
Binary files differ
diff --git a/bluez/repo/src/._agent.h b/bluez/repo/src/._agent.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._agent.h
Binary files differ
diff --git a/bluez/repo/src/._attio.h b/bluez/repo/src/._attio.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._attio.h
Binary files differ
diff --git a/bluez/repo/src/._attrib-server.c b/bluez/repo/src/._attrib-server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._attrib-server.c
Binary files differ
diff --git a/bluez/repo/src/._attrib-server.h b/bluez/repo/src/._attrib-server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._attrib-server.h
Binary files differ
diff --git a/bluez/repo/src/._backtrace.c b/bluez/repo/src/._backtrace.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._backtrace.c
Binary files differ
diff --git a/bluez/repo/src/._backtrace.h b/bluez/repo/src/._backtrace.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._backtrace.h
Binary files differ
diff --git a/bluez/repo/src/._bluetooth.conf b/bluez/repo/src/._bluetooth.conf
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._bluetooth.conf
Binary files differ
diff --git a/bluez/repo/src/._bluetooth.service.in b/bluez/repo/src/._bluetooth.service.in
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._bluetooth.service.in
Binary files differ
diff --git a/bluez/repo/src/._bluetooth.ver b/bluez/repo/src/._bluetooth.ver
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._bluetooth.ver
Binary files differ
diff --git a/bluez/repo/src/._bluetoothd.8.in b/bluez/repo/src/._bluetoothd.8.in
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._bluetoothd.8.in
Binary files differ
diff --git a/bluez/repo/src/._dbus-common.c b/bluez/repo/src/._dbus-common.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._dbus-common.c
Binary files differ
diff --git a/bluez/repo/src/._dbus-common.h b/bluez/repo/src/._dbus-common.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._dbus-common.h
Binary files differ
diff --git a/bluez/repo/src/._device.c b/bluez/repo/src/._device.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._device.c
Binary files differ
diff --git a/bluez/repo/src/._device.h b/bluez/repo/src/._device.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._device.h
Binary files differ
diff --git a/bluez/repo/src/._eir.c b/bluez/repo/src/._eir.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._eir.c
Binary files differ
diff --git a/bluez/repo/src/._eir.h b/bluez/repo/src/._eir.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._eir.h
Binary files differ
diff --git a/bluez/repo/src/._error.c b/bluez/repo/src/._error.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._error.c
Binary files differ
diff --git a/bluez/repo/src/._error.h b/bluez/repo/src/._error.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._error.h
Binary files differ
diff --git a/bluez/repo/src/._gatt-client.c b/bluez/repo/src/._gatt-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._gatt-client.c
Binary files differ
diff --git a/bluez/repo/src/._gatt-client.h b/bluez/repo/src/._gatt-client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._gatt-client.h
Binary files differ
diff --git a/bluez/repo/src/._gatt-database.c b/bluez/repo/src/._gatt-database.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._gatt-database.c
Binary files differ
diff --git a/bluez/repo/src/._gatt-database.h b/bluez/repo/src/._gatt-database.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._gatt-database.h
Binary files differ
diff --git a/bluez/repo/src/._genbuiltin b/bluez/repo/src/._genbuiltin
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._genbuiltin
Binary files differ
diff --git a/bluez/repo/src/._hcid.h b/bluez/repo/src/._hcid.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._hcid.h
Binary files differ
diff --git a/bluez/repo/src/._log.c b/bluez/repo/src/._log.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._log.c
Binary files differ
diff --git a/bluez/repo/src/._log.h b/bluez/repo/src/._log.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._log.h
Binary files differ
diff --git a/bluez/repo/src/._main.c b/bluez/repo/src/._main.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._main.c
Binary files differ
diff --git a/bluez/repo/src/._main.conf b/bluez/repo/src/._main.conf
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._main.conf
Binary files differ
diff --git a/bluez/repo/src/._org.bluez.service b/bluez/repo/src/._org.bluez.service
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._org.bluez.service
Binary files differ
diff --git a/bluez/repo/src/._oui.c b/bluez/repo/src/._oui.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._oui.c
Binary files differ
diff --git a/bluez/repo/src/._oui.h b/bluez/repo/src/._oui.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._oui.h
Binary files differ
diff --git a/bluez/repo/src/._plugin.c b/bluez/repo/src/._plugin.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._plugin.c
Binary files differ
diff --git a/bluez/repo/src/._plugin.h b/bluez/repo/src/._plugin.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._plugin.h
Binary files differ
diff --git a/bluez/repo/src/._profile.c b/bluez/repo/src/._profile.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._profile.c
Binary files differ
diff --git a/bluez/repo/src/._profile.h b/bluez/repo/src/._profile.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._profile.h
Binary files differ
diff --git a/bluez/repo/src/._rfkill.c b/bluez/repo/src/._rfkill.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._rfkill.c
Binary files differ
diff --git a/bluez/repo/src/._sdp-client.c b/bluez/repo/src/._sdp-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdp-client.c
Binary files differ
diff --git a/bluez/repo/src/._sdp-client.h b/bluez/repo/src/._sdp-client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdp-client.h
Binary files differ
diff --git a/bluez/repo/src/._sdp-xml.c b/bluez/repo/src/._sdp-xml.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdp-xml.c
Binary files differ
diff --git a/bluez/repo/src/._sdp-xml.h b/bluez/repo/src/._sdp-xml.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdp-xml.h
Binary files differ
diff --git a/bluez/repo/src/._sdpd-database.c b/bluez/repo/src/._sdpd-database.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdpd-database.c
Binary files differ
diff --git a/bluez/repo/src/._sdpd-request.c b/bluez/repo/src/._sdpd-request.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdpd-request.c
Binary files differ
diff --git a/bluez/repo/src/._sdpd-server.c b/bluez/repo/src/._sdpd-server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdpd-server.c
Binary files differ
diff --git a/bluez/repo/src/._sdpd-service.c b/bluez/repo/src/._sdpd-service.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdpd-service.c
Binary files differ
diff --git a/bluez/repo/src/._sdpd.h b/bluez/repo/src/._sdpd.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._sdpd.h
Binary files differ
diff --git a/bluez/repo/src/._service.c b/bluez/repo/src/._service.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._service.c
Binary files differ
diff --git a/bluez/repo/src/._service.h b/bluez/repo/src/._service.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._service.h
Binary files differ
diff --git a/bluez/repo/src/._shared b/bluez/repo/src/._shared
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._shared
Binary files differ
diff --git a/bluez/repo/src/._storage.c b/bluez/repo/src/._storage.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._storage.c
Binary files differ
diff --git a/bluez/repo/src/._storage.h b/bluez/repo/src/._storage.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._storage.h
Binary files differ
diff --git a/bluez/repo/src/._systemd.c b/bluez/repo/src/._systemd.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._systemd.c
Binary files differ
diff --git a/bluez/repo/src/._systemd.h b/bluez/repo/src/._systemd.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._systemd.h
Binary files differ
diff --git a/bluez/repo/src/._textfile.c b/bluez/repo/src/._textfile.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._textfile.c
Binary files differ
diff --git a/bluez/repo/src/._textfile.h b/bluez/repo/src/._textfile.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._textfile.h
Binary files differ
diff --git a/bluez/repo/src/._uinput.h b/bluez/repo/src/._uinput.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._uinput.h
Binary files differ
diff --git a/bluez/repo/src/._uuid-helper.c b/bluez/repo/src/._uuid-helper.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._uuid-helper.c
Binary files differ
diff --git a/bluez/repo/src/._uuid-helper.h b/bluez/repo/src/._uuid-helper.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/._uuid-helper.h
Binary files differ
diff --git a/bluez/repo/src/adapter.c b/bluez/repo/src/adapter.c
new file mode 100644
index 0000000..ea306e4
--- /dev/null
+++ b/bluez/repo/src/adapter.c
@@ -0,0 +1,8357 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <inttypes.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/hci.h"
+#include "bluetooth/hci_lib.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/mgmt.h"
+
+#include "gdbus/gdbus.h"
+
+#include "log.h"
+#include "textfile.h"
+
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "uuid-helper.h"
+#include "agent.h"
+#include "storage.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib-server.h"
+#include "gatt-database.h"
+#include "advertising.h"
+#include "eir.h"
+
+#define ADAPTER_INTERFACE	"org.bluez.Adapter1"
+
+#define MODE_OFF		0x00
+#define MODE_CONNECTABLE	0x01
+#define MODE_DISCOVERABLE	0x02
+#define MODE_UNKNOWN		0xff
+
+#define CONN_SCAN_TIMEOUT (3)
+#define IDLE_DISCOV_TIMEOUT (5)
+#define TEMP_DEV_TIMEOUT (3 * 60)
+#define BONDING_TIMEOUT (2 * 60)
+
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
+#define HCI_RSSI_INVALID	127
+#define DISTANCE_VAL_INVALID	0x7FFF
+#define PATHLOSS_MAX		137
+
+static DBusConnection *dbus_conn = NULL;
+
+static bool kernel_conn_control = false;
+
+static GList *adapter_list = NULL;
+static unsigned int adapter_remaining = 0;
+static bool powering_down = false;
+
+static GSList *adapters = NULL;
+
+static struct mgmt *mgmt_master = NULL;
+
+static uint8_t mgmt_version = 0;
+static uint8_t mgmt_revision = 0;
+
+static GSList *adapter_drivers = NULL;
+
+static GSList *disconnect_list = NULL;
+static GSList *conn_fail_list = NULL;
+
+struct link_key_info {
+	bdaddr_t bdaddr;
+	unsigned char key[16];
+	uint8_t type;
+	uint8_t pin_len;
+};
+
+struct smp_ltk_info {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+	uint8_t authenticated;
+	bool master;
+	uint8_t enc_size;
+	uint16_t ediv;
+	uint64_t rand;
+	uint8_t val[16];
+};
+
+struct irk_info {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+	uint8_t val[16];
+};
+
+struct conn_param {
+	bdaddr_t bdaddr;
+	uint8_t  bdaddr_type;
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t latency;
+	uint16_t timeout;
+};
+
+struct discovery_filter {
+	uint8_t type;
+	uint16_t pathloss;
+	int16_t rssi;
+	GSList *uuids;
+};
+
+struct watch_client {
+	struct btd_adapter *adapter;
+	char *owner;
+	guint watch;
+	struct discovery_filter *discovery_filter;
+};
+
+struct service_auth {
+	guint id;
+	unsigned int svc_id;
+	service_auth_cb cb;
+	void *user_data;
+	const char *uuid;
+	struct btd_device *device;
+	struct btd_adapter *adapter;
+	struct agent *agent;		/* NULL for queued auths */
+};
+
+struct btd_adapter_pin_cb_iter {
+	GSList *it;			/* current callback function */
+	unsigned int attempt;		/* numer of times it() was called */
+	/* When the iterator reaches the end, it is NULL and attempt is 0 */
+};
+
+struct btd_adapter {
+	int ref_count;
+
+	uint16_t dev_id;
+	struct mgmt *mgmt;
+
+	bdaddr_t bdaddr;		/* controller Bluetooth address */
+	uint32_t dev_class;		/* controller class of device */
+	char *name;			/* controller device name */
+	char *short_name;		/* controller short name */
+	uint32_t supported_settings;	/* controller supported settings */
+	uint32_t current_settings;	/* current controller settings */
+
+	char *path;			/* adapter object path */
+	uint8_t major_class;		/* configured major class */
+	uint8_t minor_class;		/* configured minor class */
+	char *system_name;		/* configured system name */
+	char *modalias;			/* device id (modalias) */
+	bool stored_discoverable;	/* stored discoverable mode */
+	uint32_t discoverable_timeout;	/* discoverable time(sec) */
+	uint32_t pairable_timeout;	/* pairable time(sec) */
+
+	char *current_alias;		/* current adapter name alias */
+	char *stored_alias;		/* stored adapter name alias */
+
+	bool discovering;		/* discovering property state */
+	bool filtered_discovery;	/* we are doing filtered discovery */
+	bool no_scan_restart_delay;	/* when this flag is set, restart scan
+					 * without delay */
+	uint8_t discovery_type;		/* current active discovery type */
+	uint8_t discovery_enable;	/* discovery enabled/disabled */
+	bool discovery_suspended;	/* discovery has been suspended */
+	GSList *discovery_list;		/* list of discovery clients */
+	GSList *set_filter_list;	/* list of clients that specified
+					 * filter, but don't scan yet
+					 */
+	/* current discovery filter, if any */
+	struct mgmt_cp_start_service_discovery *current_discovery_filter;
+
+	GSList *discovery_found;	/* list of found devices */
+	guint discovery_idle_timeout;	/* timeout between discovery runs */
+	guint passive_scan_timeout;	/* timeout between passive scans */
+	guint temp_devices_timeout;	/* timeout for temporary devices */
+
+	guint pairable_timeout_id;	/* pairable timeout id */
+	guint auth_idle_id;		/* Pending authorization dequeue */
+	GQueue *auths;			/* Ongoing and pending auths */
+	bool pincode_requested;		/* PIN requested during last bonding */
+	GSList *connections;		/* Connected devices */
+	GSList *devices;		/* Devices structure pointers */
+	GSList *connect_list;		/* Devices to connect when found */
+	struct btd_device *connect_le;	/* LE device waiting to be connected */
+	sdp_list_t *services;		/* Services associated to adapter */
+
+	struct btd_gatt_database *database;
+	struct btd_advertising *adv_manager;
+
+	gboolean initialized;
+
+	GSList *pin_callbacks;
+	GSList *msd_callbacks;
+
+	GSList *drivers;
+	GSList *profiles;
+
+	struct oob_handler *oob_handler;
+
+	unsigned int load_ltks_id;
+	guint load_ltks_timeout;
+
+	unsigned int confirm_name_id;
+	guint confirm_name_timeout;
+
+	unsigned int pair_device_id;
+	guint pair_device_timeout;
+
+	unsigned int db_id;		/* Service event handler for GATT db */
+
+	bool is_default;		/* true if adapter is default one */
+};
+
+static struct btd_adapter *btd_adapter_lookup(uint16_t index)
+{
+	GList *list;
+
+	for (list = g_list_first(adapter_list); list;
+						list = g_list_next(list)) {
+		struct btd_adapter *adapter = list->data;
+
+		if (adapter->dev_id == index)
+			return adapter;
+	}
+
+	return NULL;
+}
+
+struct btd_adapter *btd_adapter_get_default(void)
+{
+	GList *list;
+
+	for (list = g_list_first(adapter_list); list;
+						list = g_list_next(list)) {
+		struct btd_adapter *adapter = list->data;
+
+		if (adapter->is_default)
+			return adapter;
+	}
+
+	return NULL;
+}
+
+bool btd_adapter_is_default(struct btd_adapter *adapter)
+{
+	if (!adapter)
+		return false;
+
+	return adapter->is_default;
+}
+
+uint16_t btd_adapter_get_index(struct btd_adapter *adapter)
+{
+	if (!adapter)
+		return MGMT_INDEX_NONE;
+
+	return adapter->dev_id;
+}
+
+static gboolean process_auth_queue(gpointer user_data);
+
+static void dev_class_changed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const struct mgmt_cod *rp = param;
+	uint32_t dev_class;
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+			"Wrong size of class of device changed parameters");
+		return;
+	}
+
+	dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16);
+
+	if (dev_class == adapter->dev_class)
+		return;
+
+	DBG("Class: 0x%06x", dev_class);
+
+	adapter->dev_class = dev_class;
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Class");
+}
+
+static void set_dev_class_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to set device class: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	dev_class_changed_callback(adapter->dev_id, length, param, adapter);
+}
+
+static void set_dev_class(struct btd_adapter *adapter)
+{
+	struct mgmt_cp_set_dev_class cp;
+
+	/*
+	 * If the controller does not support BR/EDR operation,
+	 * there is no point in trying to set a major and minor
+	 * class value.
+	 *
+	 * This is an optimization for Low Energy only controllers.
+	 */
+	if (!(adapter->supported_settings & MGMT_SETTING_BREDR))
+		return;
+
+	memset(&cp, 0, sizeof(cp));
+
+	/*
+	 * Silly workaround for a really stupid kernel bug :(
+	 *
+	 * All current kernel versions assign the major and minor numbers
+	 * straight to dev_class[0] and dev_class[1] without considering
+	 * the proper bit shifting.
+	 *
+	 * To make this work, shift the value in userspace for now until
+	 * we get a fixed kernel version.
+	 */
+	cp.major = adapter->major_class & 0x1f;
+	cp.minor = adapter->minor_class << 2;
+
+	DBG("sending set device class command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEV_CLASS,
+				adapter->dev_id, sizeof(cp), &cp,
+				set_dev_class_complete, adapter, NULL) > 0)
+		return;
+
+	btd_error(adapter->dev_id,
+		"Failed to set class of device for index %u", adapter->dev_id);
+}
+
+void btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
+							uint8_t minor)
+{
+	if (adapter->major_class == major && adapter->minor_class == minor)
+		return;
+
+	DBG("class: major %u minor %u", major, minor);
+
+	adapter->major_class = major;
+	adapter->minor_class = minor;
+
+	set_dev_class(adapter);
+}
+
+static uint8_t get_mode(const char *mode)
+{
+	if (strcasecmp("off", mode) == 0)
+		return MODE_OFF;
+	else if (strcasecmp("connectable", mode) == 0)
+		return MODE_CONNECTABLE;
+	else if (strcasecmp("discoverable", mode) == 0)
+		return MODE_DISCOVERABLE;
+	else
+		return MODE_UNKNOWN;
+}
+
+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;
+
+	key_file = g_key_file_new();
+
+	if (adapter->pairable_timeout != main_opts.pairto)
+		g_key_file_set_integer(key_file, "General", "PairableTimeout",
+					adapter->pairable_timeout);
+
+	if ((adapter->current_settings & MGMT_SETTING_DISCOVERABLE) &&
+						!adapter->discoverable_timeout)
+		discoverable = TRUE;
+	else
+		discoverable = FALSE;
+
+	g_key_file_set_boolean(key_file, "General", "Discoverable",
+							discoverable);
+
+	if (adapter->discoverable_timeout != main_opts.discovto)
+		g_key_file_set_integer(key_file, "General",
+					"DiscoverableTimeout",
+					adapter->discoverable_timeout);
+
+	if (adapter->stored_alias)
+		g_key_file_set_string(key_file, "General", "Alias",
+							adapter->stored_alias);
+
+	ba2str(&adapter->bdaddr, address);
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static void trigger_pairable_timeout(struct btd_adapter *adapter);
+static void adapter_start(struct btd_adapter *adapter);
+static void adapter_stop(struct btd_adapter *adapter);
+static void trigger_passive_scanning(struct btd_adapter *adapter);
+static bool set_mode(struct btd_adapter *adapter, uint16_t opcode,
+							uint8_t mode);
+
+static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
+{
+	uint32_t changed_mask;
+
+	changed_mask = adapter->current_settings ^ settings;
+
+	adapter->current_settings = settings;
+
+	DBG("Changed settings: 0x%08x", changed_mask);
+
+	if (changed_mask & MGMT_SETTING_POWERED) {
+	        g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Powered");
+
+		if (adapter->current_settings & MGMT_SETTING_POWERED) {
+			adapter_start(adapter);
+		} else {
+			adapter_stop(adapter);
+
+			if (powering_down) {
+				adapter_remaining--;
+
+				if (!adapter_remaining)
+					btd_exit();
+			}
+		}
+	}
+
+	if (changed_mask & MGMT_SETTING_LE) {
+		if ((adapter->current_settings & MGMT_SETTING_POWERED) &&
+				(adapter->current_settings & MGMT_SETTING_LE))
+			trigger_passive_scanning(adapter);
+	}
+
+	if (changed_mask & MGMT_SETTING_CONNECTABLE)
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Connectable");
+
+	if (changed_mask & MGMT_SETTING_DISCOVERABLE) {
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Discoverable");
+		store_adapter_info(adapter);
+	}
+
+	if (changed_mask & MGMT_SETTING_BONDABLE) {
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Pairable");
+
+		trigger_pairable_timeout(adapter);
+	}
+}
+
+static void new_settings_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	uint32_t settings;
+
+	if (length < sizeof(settings)) {
+		btd_error(adapter->dev_id,
+				"Wrong size of new settings parameters");
+		return;
+	}
+
+	settings = get_le32(param);
+
+	if (settings == adapter->current_settings)
+		return;
+
+	DBG("Settings: 0x%08x", settings);
+
+	settings_changed(adapter, settings);
+}
+
+static void set_mode_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id, "Failed to set mode: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	new_settings_callback(adapter->dev_id, length, param, adapter);
+}
+
+static bool set_mode(struct btd_adapter *adapter, uint16_t opcode,
+							uint8_t mode)
+{
+	struct mgmt_mode cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+
+	DBG("sending set mode command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, opcode,
+				adapter->dev_id, sizeof(cp), &cp,
+				set_mode_complete, adapter, NULL) > 0)
+		return true;
+
+	btd_error(adapter->dev_id, "Failed to set mode for index %u",
+							adapter->dev_id);
+
+	return false;
+}
+
+static bool set_discoverable(struct btd_adapter *adapter, uint8_t mode,
+							uint16_t timeout)
+{
+	struct mgmt_cp_set_discoverable cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+	cp.timeout = htobs(timeout);
+
+	DBG("sending set mode command for index %u", adapter->dev_id);
+
+	if (kernel_conn_control) {
+		if (mode)
+			set_mode(adapter, MGMT_OP_SET_CONNECTABLE, mode);
+		else
+			/* This also disables discoverable so we're done */
+			return set_mode(adapter, MGMT_OP_SET_CONNECTABLE,
+									mode);
+	}
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DISCOVERABLE,
+				adapter->dev_id, sizeof(cp), &cp,
+				set_mode_complete, adapter, NULL) > 0)
+		return true;
+
+	btd_error(adapter->dev_id, "Failed to set mode for index %u",
+							adapter->dev_id);
+
+	return false;
+}
+
+static gboolean pairable_timeout_handler(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	adapter->pairable_timeout_id = 0;
+
+	set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x00);
+
+	return FALSE;
+}
+
+static void trigger_pairable_timeout(struct btd_adapter *adapter)
+{
+	if (adapter->pairable_timeout_id > 0) {
+		g_source_remove(adapter->pairable_timeout_id);
+		adapter->pairable_timeout_id = 0;
+	}
+
+	if (!(adapter->current_settings & MGMT_SETTING_BONDABLE))
+		return;
+
+	if (adapter->pairable_timeout > 0)
+		g_timeout_add_seconds(adapter->pairable_timeout,
+					pairable_timeout_handler, adapter);
+}
+
+static void local_name_changed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const struct mgmt_cp_set_local_name *rp = param;
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Wrong size of local name changed parameters");
+		return;
+	}
+
+	if (!g_strcmp0(adapter->short_name, (const char *) rp->short_name) &&
+			!g_strcmp0(adapter->name, (const char *) rp->name))
+		return;
+
+	DBG("Name: %s", rp->name);
+	DBG("Short name: %s", rp->short_name);
+
+	g_free(adapter->name);
+	adapter->name = g_strdup((const char *) rp->name);
+
+	g_free(adapter->short_name);
+	adapter->short_name = g_strdup((const char *) rp->short_name);
+
+	/*
+	 * Changing the name (even manually via HCI) will update the
+	 * current alias property.
+	 *
+	 * In case the name is empty, use the short name.
+	 *
+	 * There is a difference between the stored alias (which is
+	 * configured by the user) and the current alias. The current
+	 * alias is temporary for the lifetime of the daemon.
+	 */
+	if (adapter->name && adapter->name[0] != '\0') {
+		g_free(adapter->current_alias);
+		adapter->current_alias = g_strdup(adapter->name);
+	} else {
+		g_free(adapter->current_alias);
+		adapter->current_alias = g_strdup(adapter->short_name);
+	}
+
+	DBG("Current alias: %s", adapter->current_alias);
+
+	if (!adapter->current_alias)
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Alias");
+
+	attrib_gap_set(adapter, GATT_CHARAC_DEVICE_NAME,
+				(const uint8_t *) adapter->current_alias,
+					strlen(adapter->current_alias));
+}
+
+static void set_local_name_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to set local name: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	local_name_changed_callback(adapter->dev_id, length, param, adapter);
+}
+
+static int set_name(struct btd_adapter *adapter, const char *name)
+{
+	struct mgmt_cp_set_local_name cp;
+	char maxname[MAX_NAME_LENGTH + 1];
+
+	memset(maxname, 0, sizeof(maxname));
+	strncpy(maxname, name, MAX_NAME_LENGTH);
+
+	if (!g_utf8_validate(maxname, -1, NULL)) {
+		btd_error(adapter->dev_id,
+			"Name change failed: supplied name isn't valid UTF-8");
+		return -EINVAL;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	strncpy((char *) cp.name, maxname, sizeof(cp.name) - 1);
+
+	DBG("sending set local name command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_SET_LOCAL_NAME,
+				adapter->dev_id, sizeof(cp), &cp,
+				set_local_name_complete, adapter, NULL) > 0)
+		return 0;
+
+	btd_error(adapter->dev_id, "Failed to set local name for index %u",
+							adapter->dev_id);
+
+	return -EIO;
+}
+
+int adapter_set_name(struct btd_adapter *adapter, const char *name)
+{
+	if (g_strcmp0(adapter->system_name, name) == 0)
+		return 0;
+
+	DBG("name: %s", name);
+
+	g_free(adapter->system_name);
+	adapter->system_name = g_strdup(name);
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Name");
+
+	/* alias is preferred over system name */
+	if (adapter->stored_alias)
+		return 0;
+
+	DBG("alias: %s", name);
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Alias");
+
+	return set_name(adapter, name);
+}
+
+struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
+							const bdaddr_t *dst,
+							uint8_t bdaddr_type)
+{
+	struct device_addr_type addr;
+	struct btd_device *device;
+	GSList *list;
+
+	if (!adapter)
+		return NULL;
+
+	bacpy(&addr.bdaddr, dst);
+	addr.bdaddr_type = bdaddr_type;
+
+	list = g_slist_find_custom(adapter->devices, &addr,
+							device_addr_type_cmp);
+	if (!list)
+		return NULL;
+
+	device = list->data;
+
+	/*
+	 * If we're looking up based on public address and the address
+	 * was not previously used over this bearer we may need to
+	 * update LE or BR/EDR support information.
+	 */
+	if (bdaddr_type == BDADDR_BREDR)
+		device_set_bredr_support(device);
+	else
+		device_set_le_support(device, bdaddr_type);
+
+	return device;
+}
+
+static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
+{
+	if (uuid->type == SDP_UUID16)
+		sdp_uuid16_to_uuid128(uuid128, uuid);
+	else if (uuid->type == SDP_UUID32)
+		sdp_uuid32_to_uuid128(uuid128, uuid);
+	else
+		memcpy(uuid128, uuid, sizeof(*uuid));
+}
+
+static bool is_supported_uuid(const uuid_t *uuid)
+{
+	uuid_t tmp;
+
+	/* mgmt versions from 1.3 onwards support all types of UUIDs */
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 3))
+		return true;
+
+	uuid_to_uuid128(&tmp, uuid);
+
+	if (!sdp_uuid128_to_uuid(&tmp))
+		return false;
+
+	if (tmp.type != SDP_UUID16)
+		return false;
+
+	return true;
+}
+
+static void add_uuid_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id, "Failed to add UUID: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	dev_class_changed_callback(adapter->dev_id, length, param, adapter);
+
+	if (adapter->initialized)
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "UUIDs");
+}
+
+static int add_uuid(struct btd_adapter *adapter, uuid_t *uuid, uint8_t svc_hint)
+{
+	struct mgmt_cp_add_uuid cp;
+	uuid_t uuid128;
+	uint128_t uint128;
+
+	if (!is_supported_uuid(uuid)) {
+		btd_warn(adapter->dev_id,
+				"Ignoring unsupported UUID for addition");
+		return 0;
+	}
+
+	uuid_to_uuid128(&uuid128, uuid);
+
+	ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+	cp.svc_hint = svc_hint;
+
+	DBG("sending add uuid command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_UUID,
+				adapter->dev_id, sizeof(cp), &cp,
+				add_uuid_complete, adapter, NULL) > 0)
+		return 0;
+
+	btd_error(adapter->dev_id, "Failed to add UUID for index %u",
+							adapter->dev_id);
+
+	return -EIO;
+}
+
+static void remove_uuid_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id, "Failed to remove UUID: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	dev_class_changed_callback(adapter->dev_id, length, param, adapter);
+
+	if (adapter->initialized)
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "UUIDs");
+}
+
+static int remove_uuid(struct btd_adapter *adapter, uuid_t *uuid)
+{
+	struct mgmt_cp_remove_uuid cp;
+	uuid_t uuid128;
+	uint128_t uint128;
+
+	if (!is_supported_uuid(uuid)) {
+		btd_warn(adapter->dev_id,
+				"Ignoring unsupported UUID for removal");
+		return 0;
+	}
+
+	uuid_to_uuid128(&uuid128, uuid);
+
+	ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	DBG("sending remove uuid command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_UUID,
+				adapter->dev_id, sizeof(cp), &cp,
+				remove_uuid_complete, adapter, NULL) > 0)
+		return 0;
+
+	btd_error(adapter->dev_id, "Failed to remove UUID for index %u",
+							adapter->dev_id);
+
+	return -EIO;
+}
+
+static void clear_uuids_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id, "Failed to clear UUIDs: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	dev_class_changed_callback(adapter->dev_id, length, param, adapter);
+}
+
+static int clear_uuids(struct btd_adapter *adapter)
+{
+	struct mgmt_cp_remove_uuid cp;
+
+	memset(&cp, 0, sizeof(cp));
+
+	DBG("sending clear uuids command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_UUID,
+				adapter->dev_id, sizeof(cp), &cp,
+				clear_uuids_complete, adapter, NULL) > 0)
+		return 0;
+
+	btd_error(adapter->dev_id, "Failed to clear UUIDs for index %u",
+							adapter->dev_id);
+
+	return -EIO;
+}
+
+static uint8_t get_uuid_mask(uuid_t *uuid)
+{
+	if (uuid->type != SDP_UUID16)
+		return 0;
+
+	switch (uuid->value.uuid16) {
+	case DIALUP_NET_SVCLASS_ID:
+	case CIP_SVCLASS_ID:
+		return 0x42;	/* Telephony & Networking */
+	case IRMC_SYNC_SVCLASS_ID:
+	case OBEX_OBJPUSH_SVCLASS_ID:
+	case OBEX_FILETRANS_SVCLASS_ID:
+	case IRMC_SYNC_CMD_SVCLASS_ID:
+	case PBAP_PSE_SVCLASS_ID:
+		return 0x10;	/* Object Transfer */
+	case HEADSET_SVCLASS_ID:
+	case HANDSFREE_SVCLASS_ID:
+		return 0x20;	/* Audio */
+	case CORDLESS_TELEPHONY_SVCLASS_ID:
+	case INTERCOM_SVCLASS_ID:
+	case FAX_SVCLASS_ID:
+	case SAP_SVCLASS_ID:
+	/*
+	 * Setting the telephony bit for the handsfree audio gateway
+	 * role is not required by the HFP specification, but the
+	 * Nokia 616 carkit is just plain broken! It will refuse
+	 * pairing without this bit set.
+	 */
+	case HANDSFREE_AGW_SVCLASS_ID:
+		return 0x40;	/* Telephony */
+	case AUDIO_SOURCE_SVCLASS_ID:
+	case VIDEO_SOURCE_SVCLASS_ID:
+		return 0x08;	/* Capturing */
+	case AUDIO_SINK_SVCLASS_ID:
+	case VIDEO_SINK_SVCLASS_ID:
+		return 0x04;	/* Rendering */
+	case PANU_SVCLASS_ID:
+	case NAP_SVCLASS_ID:
+	case GN_SVCLASS_ID:
+		return 0x02;	/* Networking */
+	default:
+		return 0;
+	}
+}
+
+static int uuid_cmp(const void *a, const void *b)
+{
+	const sdp_record_t *rec = a;
+	const uuid_t *uuid = b;
+
+	return sdp_uuid_cmp(&rec->svclass, uuid);
+}
+
+static void adapter_service_insert(struct btd_adapter *adapter, sdp_record_t *rec)
+{
+	sdp_list_t *browse_list = NULL;
+	uuid_t browse_uuid;
+	gboolean new_uuid;
+
+	DBG("%s", adapter->path);
+
+	/* skip record without a browse group */
+	if (sdp_get_browse_groups(rec, &browse_list) < 0) {
+		DBG("skipping record without browse group");
+		return;
+	}
+
+	sdp_uuid16_create(&browse_uuid, PUBLIC_BROWSE_GROUP);
+
+	/* skip record without public browse group */
+	if (!sdp_list_find(browse_list, &browse_uuid, sdp_uuid_cmp))
+		goto done;
+
+	if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
+		new_uuid = TRUE;
+	else
+		new_uuid = FALSE;
+
+	adapter->services = sdp_list_insert_sorted(adapter->services, rec,
+								record_sort);
+
+	if (new_uuid) {
+		uint8_t svc_hint = get_uuid_mask(&rec->svclass);
+		add_uuid(adapter, &rec->svclass, svc_hint);
+	}
+
+done:
+	sdp_list_free(browse_list, free);
+}
+
+int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec)
+{
+	int ret;
+
+	DBG("%s", adapter->path);
+
+	ret = add_record_to_server(&adapter->bdaddr, rec);
+	if (ret < 0)
+		return ret;
+
+	adapter_service_insert(adapter, rec);
+
+	return 0;
+}
+
+void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle)
+{
+	sdp_record_t *rec = sdp_record_find(handle);
+
+	DBG("%s", adapter->path);
+
+	if (!rec)
+		return;
+
+	adapter->services = sdp_list_remove(adapter->services, rec);
+
+	if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
+		remove_uuid(adapter, &rec->svclass);
+
+	remove_record_from_server(rec->handle);
+}
+
+static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
+						const bdaddr_t *bdaddr,
+						uint8_t bdaddr_type)
+{
+	struct btd_device *device;
+
+	device = device_create(adapter, bdaddr, bdaddr_type);
+	if (!device)
+		return NULL;
+
+	adapter->devices = g_slist_append(adapter->devices, device);
+
+	return device;
+}
+
+static void service_auth_cancel(struct service_auth *auth)
+{
+	DBusError derr;
+
+	if (auth->svc_id > 0)
+		device_remove_svc_complete_callback(auth->device,
+								auth->svc_id);
+
+	dbus_error_init(&derr);
+	dbus_set_error_const(&derr, ERROR_INTERFACE ".Canceled", NULL);
+
+	auth->cb(&derr, auth->user_data);
+
+	dbus_error_free(&derr);
+
+	if (auth->agent != NULL) {
+		agent_cancel(auth->agent);
+		agent_unref(auth->agent);
+	}
+
+	g_free(auth);
+}
+
+void btd_adapter_remove_device(struct btd_adapter *adapter,
+				struct btd_device *dev)
+{
+	GList *l;
+
+	adapter->connect_list = g_slist_remove(adapter->connect_list, dev);
+
+	adapter->devices = g_slist_remove(adapter->devices, dev);
+
+	adapter->discovery_found = g_slist_remove(adapter->discovery_found,
+									dev);
+
+	adapter->connections = g_slist_remove(adapter->connections, dev);
+
+	if (adapter->connect_le == dev)
+		adapter->connect_le = NULL;
+
+	l = adapter->auths->head;
+	while (l != NULL) {
+		struct service_auth *auth = l->data;
+		GList *next = g_list_next(l);
+
+		if (auth->device != dev) {
+			l = next;
+			continue;
+		}
+
+		g_queue_delete_link(adapter->auths, l);
+		l = next;
+
+		service_auth_cancel(auth);
+	}
+
+	device_remove(dev, TRUE);
+}
+
+struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
+					const bdaddr_t *addr,
+					uint8_t addr_type)
+{
+	struct btd_device *device;
+
+	if (!adapter)
+		return NULL;
+
+	device = btd_adapter_find_device(adapter, addr, addr_type);
+	if (device)
+		return device;
+
+	return adapter_create_device(adapter, addr, addr_type);
+}
+
+sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter)
+{
+	return adapter->services;
+}
+
+static void passive_scanning_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const struct mgmt_cp_start_discovery *rp = param;
+
+	DBG("status 0x%02x", status);
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+			"Wrong size of start scanning return parameters");
+		return;
+	}
+
+	if (status == MGMT_STATUS_SUCCESS) {
+		adapter->discovery_type = rp->type;
+		adapter->discovery_enable = 0x01;
+	}
+}
+
+static gboolean passive_scanning_timeout(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	struct mgmt_cp_start_discovery cp;
+
+	adapter->passive_scan_timeout = 0;
+
+	cp.type = SCAN_TYPE_LE;
+
+	mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
+				adapter->dev_id, sizeof(cp), &cp,
+				passive_scanning_complete, adapter, NULL);
+
+	return FALSE;
+}
+
+static void trigger_passive_scanning(struct btd_adapter *adapter)
+{
+	if (!(adapter->current_settings & MGMT_SETTING_LE))
+		return;
+
+	DBG("");
+
+	if (adapter->passive_scan_timeout > 0) {
+		g_source_remove(adapter->passive_scan_timeout);
+		adapter->passive_scan_timeout = 0;
+	}
+
+	/*
+	 * When the kernel background scanning is available, there is
+	 * no need to start any discovery. The kernel will keep scanning
+	 * as long as devices are in its auto-connection list.
+	 */
+	if (kernel_conn_control)
+		return;
+
+	/*
+	 * If any client is running a discovery right now, then do not
+	 * even try to start passive scanning.
+	 *
+	 * The discovery procedure is using interleaved scanning and
+	 * thus will discover Low Energy devices as well.
+	 */
+	if (adapter->discovery_list)
+		return;
+
+	if (adapter->discovery_enable == 0x01)
+		return;
+
+	/*
+	 * In case the discovery is suspended (for example for an ongoing
+	 * pairing attempt), then also do not start passive scanning.
+	 */
+	if (adapter->discovery_suspended)
+		return;
+
+	/*
+	 * If the list of connectable Low Energy devices is empty,
+	 * then do not start passive scanning.
+	 */
+	if (!adapter->connect_list)
+		return;
+
+	adapter->passive_scan_timeout = g_timeout_add_seconds(CONN_SCAN_TIMEOUT,
+					passive_scanning_timeout, adapter);
+}
+
+static void stop_passive_scanning_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *dev;
+	int err;
+
+	DBG("status 0x%02x (%s)", status, mgmt_errstr(status));
+
+	dev = adapter->connect_le;
+	adapter->connect_le = NULL;
+
+	/*
+	 * When the kernel background scanning is available, there is
+	 * no need to stop any discovery. The kernel will handle the
+	 * auto-connection by itself.
+	 */
+	if (kernel_conn_control)
+		return;
+
+	/*
+	 * MGMT_STATUS_REJECTED may be returned from kernel because the passive
+	 * scan timer had expired in kernel and passive scan was disabled just
+	 * around the time we called stop_passive_scanning().
+	 */
+	if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_REJECTED) {
+		btd_error(adapter->dev_id, "Stopping passive scanning failed: %s",
+							mgmt_errstr(status));
+		return;
+	}
+
+	adapter->discovery_type = 0x00;
+	adapter->discovery_enable = 0x00;
+
+	if (!dev) {
+		DBG("Device removed while stopping passive scanning");
+		trigger_passive_scanning(adapter);
+		return;
+	}
+
+	err = device_connect_le(dev);
+	if (err < 0) {
+		btd_error(adapter->dev_id, "LE auto connection failed: %s (%d)",
+							strerror(-err), -err);
+		trigger_passive_scanning(adapter);
+	}
+}
+
+static void stop_passive_scanning(struct btd_adapter *adapter)
+{
+	struct mgmt_cp_stop_discovery cp;
+
+	DBG("");
+
+	/* If there are any normal discovery clients passive scanning
+	 * wont be running */
+	if (adapter->discovery_list)
+		return;
+
+	if (adapter->discovery_enable == 0x00)
+		return;
+
+	cp.type = adapter->discovery_type;
+
+	mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+			adapter->dev_id, sizeof(cp), &cp,
+			stop_passive_scanning_complete, adapter, NULL);
+}
+
+static void cancel_passive_scanning(struct btd_adapter *adapter)
+{
+	if (!(adapter->current_settings & MGMT_SETTING_LE))
+		return;
+
+	DBG("");
+
+	if (adapter->passive_scan_timeout > 0) {
+		g_source_remove(adapter->passive_scan_timeout);
+		adapter->passive_scan_timeout = 0;
+	}
+}
+
+static uint8_t get_scan_type(struct btd_adapter *adapter)
+{
+	uint8_t type;
+
+	if (adapter->current_settings & MGMT_SETTING_BREDR)
+		type = SCAN_TYPE_BREDR;
+	else
+		type = 0;
+
+	if (adapter->current_settings & MGMT_SETTING_LE)
+		type |= SCAN_TYPE_LE;
+
+	return type;
+}
+
+static void free_discovery_filter(struct discovery_filter *discovery_filter)
+{
+	if (!discovery_filter)
+		return;
+
+	g_slist_free_full(discovery_filter->uuids, g_free);
+	g_free(discovery_filter);
+}
+
+static void trigger_start_discovery(struct btd_adapter *adapter, guint delay);
+
+static void start_discovery_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const struct mgmt_cp_start_discovery *rp = param;
+
+	DBG("status 0x%02x", status);
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+			"Wrong size of start discovery return parameters");
+		return;
+	}
+
+	if (status == MGMT_STATUS_SUCCESS) {
+		adapter->discovery_type = rp->type;
+		adapter->discovery_enable = 0x01;
+
+		if (adapter->current_discovery_filter)
+			adapter->filtered_discovery = true;
+		else
+			adapter->filtered_discovery = false;
+
+		if (adapter->discovering)
+			return;
+
+		adapter->discovering = true;
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Discovering");
+		return;
+	}
+
+	/*
+	 * In case the restart of the discovery failed, then just trigger
+	 * it for the next idle timeout again.
+	 */
+	trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
+}
+
+static gboolean start_discovery_timeout(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	struct mgmt_cp_start_service_discovery *sd_cp;
+	uint8_t new_type;
+
+	DBG("");
+
+	adapter->discovery_idle_timeout = 0;
+
+	/* If we're doing filtered discovery, it must be quickly restarted */
+	adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
+
+	DBG("adapter->current_discovery_filter == %d",
+	    !!adapter->current_discovery_filter);
+
+	new_type = get_scan_type(adapter);
+
+	if (adapter->discovery_enable == 0x01) {
+		struct mgmt_cp_stop_discovery cp;
+
+		/*
+		 * If we're asked to start regular discovery, and there is an
+		 * already running regular discovery and it has the same type,
+		 * then just keep it.
+		 */
+		if (!adapter->current_discovery_filter &&
+		    !adapter->filtered_discovery &&
+		    adapter->discovery_type == new_type) {
+			if (adapter->discovering)
+				return FALSE;
+
+			adapter->discovering = true;
+			g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Discovering");
+			return FALSE;
+		}
+
+		/*
+		 * Otherwise the current discovery must be stopped. So
+		 * queue up a stop discovery command.
+		 *
+		 * This can happen if a passive scanning for Low Energy
+		 * devices is ongoing, or scan type is changed between
+		 * regular and filtered, or filter was updated.
+		 */
+		cp.type = adapter->discovery_type;
+		mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+					adapter->dev_id, sizeof(cp), &cp,
+					NULL, NULL, NULL);
+
+		/* Don't even bother to try to quickly start discovery
+		 * just after stopping it, it would fail with status
+		 * MGMT_BUSY. Instead discovering_callback will take
+		 * care of that.
+		 */
+		return FALSE;
+
+	}
+
+	/* Regular discovery is required */
+	if (!adapter->current_discovery_filter) {
+		struct mgmt_cp_start_discovery cp;
+
+		cp.type = new_type;
+		mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
+				adapter->dev_id, sizeof(cp), &cp,
+				start_discovery_complete, adapter, NULL);
+		return FALSE;
+	}
+
+	/* Filtered discovery is required */
+	sd_cp = adapter->current_discovery_filter;
+
+	DBG("sending MGMT_OP_START_SERVICE_DISCOVERY %d, %d, %d",
+				sd_cp->rssi, sd_cp->type, sd_cp->uuid_count);
+
+	mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY,
+		  adapter->dev_id, sizeof(*sd_cp) + sd_cp->uuid_count * 16,
+		  sd_cp, start_discovery_complete, adapter, NULL);
+
+	return FALSE;
+}
+
+static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
+{
+
+	DBG("");
+
+	cancel_passive_scanning(adapter);
+
+	if (adapter->discovery_idle_timeout > 0) {
+		g_source_remove(adapter->discovery_idle_timeout);
+		adapter->discovery_idle_timeout = 0;
+	}
+
+	/*
+	 * If the controller got powered down in between, then ensure
+	 * that we do not keep trying to restart discovery.
+	 *
+	 * This is safe-guard and should actually never trigger.
+	 */
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return;
+
+	adapter->discovery_idle_timeout = g_timeout_add_seconds(delay,
+					start_discovery_timeout, adapter);
+}
+
+static void suspend_discovery_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	DBG("status 0x%02x", status);
+
+	if (status == MGMT_STATUS_SUCCESS) {
+		adapter->discovery_type = 0x00;
+		adapter->discovery_enable = 0x00;
+		return;
+	}
+}
+
+static void suspend_discovery(struct btd_adapter *adapter)
+{
+	struct mgmt_cp_stop_discovery cp;
+
+	DBG("");
+
+	adapter->discovery_suspended = true;
+
+	/*
+	 * If there are no clients discovering right now, then there is
+	 * also nothing to suspend.
+	 */
+	if (!adapter->discovery_list)
+		return;
+
+	/*
+	 * In case of being inside the idle phase, make sure to remove
+	 * the timeout to not trigger a restart.
+	 *
+	 * The restart will be triggered when the discovery is resumed.
+	 */
+	if (adapter->discovery_idle_timeout > 0) {
+		g_source_remove(adapter->discovery_idle_timeout);
+		adapter->discovery_idle_timeout = 0;
+	}
+
+	if (adapter->discovery_enable == 0x00)
+		return;
+
+	cp.type = adapter->discovery_type;
+
+	mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+				adapter->dev_id, sizeof(cp), &cp,
+				suspend_discovery_complete, adapter, NULL);
+}
+
+static void resume_discovery(struct btd_adapter *adapter)
+{
+	DBG("");
+
+	adapter->discovery_suspended = false;
+
+	/*
+	 * If there are no clients discovering right now, then there is
+	 * also nothing to resume.
+	 */
+	if (!adapter->discovery_list)
+		return;
+
+	/*
+	 * Treat a suspended discovery session the same as extra long
+	 * idle time for a normal discovery. So just trigger the default
+	 * restart procedure.
+	 */
+	trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
+}
+
+static void discovering_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_discovering *ev = param;
+	struct btd_adapter *adapter = user_data;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small discovering event");
+		return;
+	}
+
+	DBG("hci%u type %u discovering %u method %d", adapter->dev_id, ev->type,
+				ev->discovering, adapter->filtered_discovery);
+
+	if (adapter->discovery_enable == ev->discovering)
+		return;
+
+	adapter->discovery_type = ev->type;
+	adapter->discovery_enable = ev->discovering;
+
+	/*
+	 * Check for existing discoveries triggered by client applications
+	 * and ignore all others.
+	 *
+	 * If there are no clients, then it is good idea to trigger a
+	 * passive scanning attempt.
+	 */
+	if (!adapter->discovery_list) {
+		if (!adapter->connect_le)
+			trigger_passive_scanning(adapter);
+		return;
+	}
+
+	if (adapter->discovery_suspended)
+		return;
+
+	switch (adapter->discovery_enable) {
+	case 0x00:
+		if (adapter->no_scan_restart_delay)
+			trigger_start_discovery(adapter, 0);
+		else
+			trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
+		break;
+
+	case 0x01:
+		if (adapter->discovery_idle_timeout > 0) {
+			g_source_remove(adapter->discovery_idle_timeout);
+			adapter->discovery_idle_timeout = 0;
+		}
+
+		break;
+	}
+}
+
+static void stop_discovery_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	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");
+
+		trigger_passive_scanning(adapter);
+	}
+}
+
+static int compare_sender(gconstpointer a, gconstpointer b)
+{
+	const struct watch_client *client = a;
+	const char *sender = 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);
+}
+
+static void extract_unique_uuids(gpointer data, gpointer user_data)
+{
+	char *uuid_str = data;
+	GSList **uuids = user_data;
+
+	if (!g_slist_find_custom(*uuids, uuid_str, g_strcmp))
+		*uuids = g_slist_insert_sorted(*uuids, uuid_str, g_strcmp);
+}
+
+/*
+ * This method merges all adapter filters into rssi, transport and uuids.
+ * Returns 1 if there was no filtered scan, 0 otherwise.
+ */
+static int merge_discovery_filters(struct btd_adapter *adapter, int *rssi,
+					uint8_t *transport, GSList **uuids)
+{
+	GSList *l;
+	bool empty_uuid = false;
+	bool has_regular_discovery = false;
+	bool has_filtered_discovery = false;
+
+	for (l = adapter->discovery_list; l != NULL; l = g_slist_next(l)) {
+		struct watch_client *client = l->data;
+		struct discovery_filter *item = client->discovery_filter;
+
+		if (!item) {
+			has_regular_discovery = true;
+			continue;
+		}
+
+		has_filtered_discovery = true;
+
+		*transport |= item->type;
+
+		/*
+		 * Rule for merging rssi and pathloss into rssi field of kernel
+		 * filter is as follow:
+		 * - if there's any client without proximity filter, then do no
+		 *   proximity filtering,
+		 * - if all clients specified RSSI, then use lowest value,
+		 * - if any client specified pathloss, then kernel filter should
+		 *   do no proximity, as kernel can't compute pathloss. We'll do
+		 *   filtering on our own.
+		 */
+		if (item->rssi == DISTANCE_VAL_INVALID)
+			*rssi = HCI_RSSI_INVALID;
+		else if (*rssi != HCI_RSSI_INVALID && *rssi >= item->rssi)
+			*rssi = item->rssi;
+		else if (item->pathloss != DISTANCE_VAL_INVALID)
+			*rssi = HCI_RSSI_INVALID;
+
+		if (!g_slist_length(item->uuids))
+			empty_uuid = true;
+
+		g_slist_foreach(item->uuids, extract_unique_uuids, uuids);
+	}
+
+	/* If no proximity filtering is set, disable it */
+	if (*rssi == DISTANCE_VAL_INVALID)
+		*rssi = HCI_RSSI_INVALID;
+
+	/*
+	 * Empty_uuid variable determines wether there was any filter with no
+	 * uuids. In this case someone might be looking for all devices in
+	 * certain proximity, and we need to have empty uuids in kernel filter.
+	 */
+	if (empty_uuid) {
+		g_slist_free(*uuids);
+		*uuids = NULL;
+	}
+
+	if (has_regular_discovery) {
+		if (!has_filtered_discovery)
+			return 1;
+
+		/*
+		 * It there is both regular and filtered scan running, then
+		 * clear whole fitler to report all devices.
+		 */
+		*transport = get_scan_type(adapter);
+		*rssi = HCI_RSSI_INVALID;
+		g_slist_free(*uuids);
+		*uuids = NULL;
+	}
+
+	return 0;
+}
+
+static void populate_mgmt_filter_uuids(uint8_t (*mgmt_uuids)[16], GSList *uuids)
+{
+	GSList *l;
+
+	for (l = uuids; l != NULL; l = g_slist_next(l)) {
+		bt_uuid_t uuid, u128;
+		uint128_t uint128;
+
+		bt_string_to_uuid(&uuid, l->data);
+		bt_uuid_to_uuid128(&uuid, &u128);
+
+		ntoh128((uint128_t *) u128.value.u128.data, &uint128);
+		htob128(&uint128, (uint128_t *) mgmt_uuids);
+
+		mgmt_uuids++;
+	}
+}
+
+/*
+ * This method merges all adapter filters into one that will be send to kernel.
+ * cp_ptr is set to null when regular non-filtered discovery is needed,
+ * otherwise it's pointing to filter. Returns 0 on succes, -1 on error
+ */
+static int discovery_filter_to_mgmt_cp(struct btd_adapter *adapter,
+		       struct mgmt_cp_start_service_discovery **cp_ptr)
+{
+	GSList *uuids = NULL;
+	struct mgmt_cp_start_service_discovery *cp;
+	int rssi = DISTANCE_VAL_INVALID;
+	int uuid_count;
+	uint8_t discovery_type = 0;
+
+	DBG("");
+
+	if (merge_discovery_filters(adapter, &rssi, &discovery_type, &uuids)) {
+		/* There are only regular scans, run just regular scan. */
+		*cp_ptr = NULL;
+		return 0;
+	}
+
+	uuid_count = g_slist_length(uuids);
+
+	cp = g_try_malloc(sizeof(*cp) + 16*uuid_count);
+	*cp_ptr = cp;
+	if (!cp) {
+		g_slist_free(uuids);
+		return -1;
+	}
+
+	cp->type = discovery_type;
+	cp->rssi = rssi;
+	cp->uuid_count = uuid_count;
+	populate_mgmt_filter_uuids(cp->uuids, uuids);
+
+	g_slist_free(uuids);
+	return 0;
+}
+
+static bool filters_equal(struct mgmt_cp_start_service_discovery *a,
+		   struct mgmt_cp_start_service_discovery *b) {
+	if (!a && !b)
+		return true;
+
+	if ((!a && b) || (a && !b))
+		return false;
+
+	if (a->type != b->type)
+		return false;
+
+	if (a->rssi != b->rssi)
+		return false;
+
+	/*
+	 * When we create mgmt_cp_start_service_discovery structure inside
+	 * discovery_filter_to_mgmt_cp, we always keep uuids sorted, and
+	 * unique, so we're safe to compare uuid_count, and uuids like that.
+	 */
+	if (a->uuid_count != b->uuid_count)
+		return false;
+
+	if (memcmp(a->uuids, b->uuids, 16 * a->uuid_count) != 0)
+		return false;
+
+	return true;
+}
+
+static void update_discovery_filter(struct btd_adapter *adapter)
+{
+	struct mgmt_cp_start_service_discovery *sd_cp;
+
+	DBG("");
+
+	if (discovery_filter_to_mgmt_cp(adapter, &sd_cp)) {
+		btd_error(adapter->dev_id,
+				"discovery_filter_to_mgmt_cp returned error");
+		return;
+	}
+
+	/*
+	 * If filters are equal, then don't update scan, except for when
+	 * starting discovery.
+	 */
+	if (filters_equal(adapter->current_discovery_filter, sd_cp) &&
+	    adapter->discovering != 0) {
+		DBG("filters were equal, deciding to not restart the scan.");
+		g_free(sd_cp);
+		return;
+	}
+
+	g_free(adapter->current_discovery_filter);
+	adapter->current_discovery_filter = sd_cp;
+
+	trigger_start_discovery(adapter, 0);
+}
+
+static void discovery_destroy(void *user_data)
+{
+	struct watch_client *client = user_data;
+	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);
+
+	if (client->discovery_filter) {
+		free_discovery_filter(client->discovery_filter);
+		client->discovery_filter = NULL;
+	}
+
+	g_free(client->owner);
+	g_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;
+
+	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;
+	}
+
+	discovery_cleanup(adapter);
+
+	adapter->temp_devices_timeout = g_timeout_add_seconds(TEMP_DEV_TIMEOUT,
+						remove_temp_devices, adapter);
+}
+
+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);
+}
+
+/*
+ * Returns true if client was already discovering, false otherwise. *client
+ * will point to discovering client, or client that have pre-set his filter.
+ */
+static bool get_discovery_client(struct btd_adapter *adapter,
+						const char *owner,
+						struct watch_client **client)
+{
+	GSList *list = g_slist_find_custom(adapter->discovery_list, owner,
+								compare_sender);
+	if (list) {
+		*client = list->data;
+		return true;
+	}
+
+	list = g_slist_find_custom(adapter->set_filter_list, owner,
+								compare_sender);
+	if (list) {
+		*client = list->data;
+		return false;
+	}
+
+	*client = NULL;
+	return false;
+}
+
+static DBusMessage *start_discovery(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const char *sender = dbus_message_get_sender(msg);
+	struct watch_client *client;
+	bool is_discovering;
+
+	DBG("sender %s", sender);
+
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return btd_error_not_ready(msg);
+
+	is_discovering = get_discovery_client(adapter, sender, &client);
+
+	/*
+	 * Every client can only start one discovery, if the client
+	 * already started a discovery then return an error.
+	 */
+	if (is_discovering)
+		return btd_error_busy(msg);
+
+	/*
+	 * If there was pre-set filter, just reconnect it to discovery_list,
+	 * and trigger scan.
+	 */
+	if (client) {
+		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);
+	}
+
+	client = g_new0(struct watch_client, 1);
+
+	client->adapter = adapter;
+	client->owner = g_strdup(sender);
+	client->discovery_filter = NULL;
+	client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+						discovery_disconnect, client,
+						discovery_destroy);
+	adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
+								client);
+
+	/*
+	 * 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);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+{
+	DBusMessageIter arriter;
+
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(value, &arriter);
+	while (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_INVALID) {
+		bt_uuid_t uuid, u128;
+		char uuidstr[MAX_LEN_UUID_STR + 1];
+		char *uuid_param;
+
+		if (dbus_message_iter_get_arg_type(&arriter) !=
+						DBUS_TYPE_STRING)
+			return false;
+
+		dbus_message_iter_get_basic(&arriter, &uuid_param);
+
+		if (bt_string_to_uuid(&uuid, uuid_param))
+			return false;
+
+		bt_uuid_to_uuid128(&uuid, &u128);
+		bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
+
+		*uuids = g_slist_prepend(*uuids, strdup(uuidstr));
+
+		dbus_message_iter_next(&arriter);
+	}
+
+	return true;
+}
+
+static bool parse_rssi(DBusMessageIter *value, int16_t *rssi)
+{
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_INT16)
+		return false;
+
+	dbus_message_iter_get_basic(value, rssi);
+	/* -127 <= RSSI <= +20 (spec V4.2 [Vol 2, Part E] 7.7.65.2) */
+	if (*rssi > 20 || *rssi < -127)
+		return false;
+
+	return true;
+}
+
+static bool parse_pathloss(DBusMessageIter *value, uint16_t *pathloss)
+{
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+		return false;
+
+	dbus_message_iter_get_basic(value, pathloss);
+	/* pathloss filter must be smaller that PATHLOSS_MAX */
+	if (*pathloss > PATHLOSS_MAX)
+		return false;
+
+	return true;
+}
+
+static bool parse_transport(DBusMessageIter *value, uint8_t *transport)
+{
+	char *transport_str;
+
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING)
+		return false;
+
+	dbus_message_iter_get_basic(value, &transport_str);
+
+	if (!strcmp(transport_str, "bredr"))
+		*transport = SCAN_TYPE_BREDR;
+	else if (!strcmp(transport_str, "le"))
+		*transport = SCAN_TYPE_LE;
+	else if (!strcmp(transport_str, "auto"))
+		*transport = SCAN_TYPE_DUAL;
+	else
+		return false;
+
+	return true;
+}
+
+static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value,
+						struct discovery_filter *filter)
+{
+	if (!strcmp("UUIDs", key))
+		return parse_uuids(value, &filter->uuids);
+
+	if (!strcmp("RSSI", key))
+		return parse_rssi(value, &filter->rssi);
+
+	if (!strcmp("Pathloss", key))
+		return parse_pathloss(value, &filter->pathloss);
+
+	if (!strcmp("Transport", key))
+		return parse_transport(value, &filter->type);
+
+	DBG("Unknown key parameter: %s!\n", key);
+	return false;
+}
+
+/*
+ * This method is responsible for parsing parameters to SetDiscoveryFilter. If
+ * filter in msg was empty, sets *filter to NULL. If whole parsing was
+ * successful, sets *filter to proper value.
+ * Returns false on any error, and true on success.
+ */
+static bool parse_discovery_filter_dict(struct discovery_filter **filter,
+							DBusMessage *msg)
+{
+	DBusMessageIter iter, subiter, dictiter, variantiter;
+	bool is_empty = true;
+
+	*filter = g_try_malloc(sizeof(**filter));
+	if (!*filter)
+		return false;
+
+	(*filter)->uuids = NULL;
+	(*filter)->pathloss = DISTANCE_VAL_INVALID;
+	(*filter)->rssi = DISTANCE_VAL_INVALID;
+	(*filter)->type = SCAN_TYPE_DUAL;
+
+	dbus_message_iter_init(msg, &iter);
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)
+		goto invalid_args;
+
+	dbus_message_iter_recurse(&iter, &subiter);
+	do {
+		int type = dbus_message_iter_get_arg_type(&subiter);
+		char *key;
+
+		if (type == DBUS_TYPE_INVALID)
+			break;
+
+		is_empty = false;
+		dbus_message_iter_recurse(&subiter, &dictiter);
+
+		dbus_message_iter_get_basic(&dictiter, &key);
+		if (!dbus_message_iter_next(&dictiter))
+			goto invalid_args;
+
+		if (dbus_message_iter_get_arg_type(&dictiter) !=
+							     DBUS_TYPE_VARIANT)
+			goto invalid_args;
+
+		dbus_message_iter_recurse(&dictiter, &variantiter);
+
+		if (!parse_discovery_filter_entry(key, &variantiter, *filter))
+			goto invalid_args;
+
+		dbus_message_iter_next(&subiter);
+	} while (true);
+
+	if (is_empty) {
+		g_free(*filter);
+		*filter = NULL;
+		return true;
+	}
+
+	/* only pathlos or rssi can be set, never both */
+	if ((*filter)->pathloss != DISTANCE_VAL_INVALID &&
+	    (*filter)->rssi != DISTANCE_VAL_INVALID)
+		goto invalid_args;
+
+	DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d",
+	    (*filter)->type, (*filter)->rssi, (*filter)->pathloss);
+
+	return true;
+
+invalid_args:
+	g_slist_free_full((*filter)->uuids, g_free);
+	g_free(*filter);
+	*filter = NULL;
+	return false;
+}
+
+static DBusMessage *set_discovery_filter(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	struct watch_client *client;
+	struct discovery_filter *discovery_filter;
+	const char *sender = dbus_message_get_sender(msg);
+	bool is_discovering;
+
+	DBG("sender %s", sender);
+
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return btd_error_not_ready(msg);
+
+	/* parse parameters */
+	if (!parse_discovery_filter_dict(&discovery_filter, msg))
+		return btd_error_invalid_args(msg);
+
+	is_discovering = get_discovery_client(adapter, sender, &client);
+
+	if (client) {
+		free_discovery_filter(client->discovery_filter);
+		client->discovery_filter = discovery_filter;
+
+		if (is_discovering)
+			update_discovery_filter(adapter);
+
+		if (discovery_filter || is_discovering)
+			return dbus_message_new_method_return(msg);
+
+		/* Removing pre-set filter */
+		adapter->set_filter_list = g_slist_remove(
+					      adapter->set_filter_list,
+					      client);
+		g_free(client->owner);
+		g_free(client);
+		DBG("successfully cleared pre-set filter");
+	} else if (discovery_filter) {
+		/* Client pre-setting his filter for first time */
+		client = g_new0(struct watch_client, 1);
+		client->adapter = adapter;
+		client->owner = g_strdup(sender);
+		client->discovery_filter = discovery_filter;
+		client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+						discovery_disconnect, client,
+						discovery_destroy);
+		adapter->set_filter_list = g_slist_prepend(
+					     adapter->set_filter_list, client);
+
+		DBG("successfully pre-set filter");
+	}
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *stop_discovery(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	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;
+
+	DBG("sender %s", sender);
+
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return btd_error_not_ready(msg);
+
+	list = g_slist_find_custom(adapter->discovery_list, sender,
+						compare_sender);
+	if (!list)
+		return btd_error_failed(msg, "No discovery started");
+
+	client = list->data;
+
+	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);
+
+		return dbus_message_new_method_return(msg);
+	}
+
+	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,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	char addr[18];
+	const char *str = addr;
+
+	ba2str(&adapter->bdaddr, addr);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+	return TRUE;
+}
+
+static gboolean property_get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const char *str = adapter->system_name ? : "";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+	return TRUE;
+}
+
+static gboolean property_get_alias(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const char *str;
+
+	if (adapter->current_alias)
+		str = adapter->current_alias;
+	else if (adapter->stored_alias)
+		str = adapter->stored_alias;
+	else
+		str = adapter->system_name ? : "";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+	return TRUE;
+}
+
+static void property_set_alias(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const char *name;
+	int ret;
+
+	dbus_message_iter_get_basic(iter, &name);
+
+	if (g_str_equal(name, "")  == TRUE) {
+		if (adapter->stored_alias == NULL) {
+			/* no alias set, nothing to restore */
+			g_dbus_pending_property_success(id);
+			return;
+		}
+
+		/* restore to system name */
+		ret = set_name(adapter, adapter->system_name);
+	} else {
+		if (g_strcmp0(adapter->stored_alias, name) == 0) {
+			/* alias already set, nothing to do */
+			g_dbus_pending_property_success(id);
+			return;
+		}
+
+		/* set to alias */
+		ret = set_name(adapter, name);
+	}
+
+	if (ret >= 0) {
+		g_free(adapter->stored_alias);
+
+		if (g_str_equal(name, "")  == TRUE)
+			adapter->stored_alias = NULL;
+		else
+			adapter->stored_alias = g_strdup(name);
+
+		store_adapter_info(adapter);
+
+		g_dbus_pending_property_success(id);
+		return;
+	}
+
+	if (ret == -EINVAL)
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+	else
+		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+							strerror(-ret));
+}
+
+static gboolean property_get_class(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	dbus_uint32_t val = adapter->dev_class;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val);
+
+	return TRUE;
+}
+
+static gboolean property_get_mode(struct btd_adapter *adapter,
+				uint32_t setting, DBusMessageIter *iter)
+{
+	dbus_bool_t enable;
+
+	enable = (adapter->current_settings & setting) ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &enable);
+
+	return TRUE;
+}
+
+struct property_set_data {
+	struct btd_adapter *adapter;
+	GDBusPendingPropertySet id;
+};
+
+static void property_set_mode_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct property_set_data *data = user_data;
+	struct btd_adapter *adapter = data->adapter;
+
+	DBG("%s (0x%02x)", mgmt_errstr(status), status);
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		const char *dbus_err;
+
+		btd_error(adapter->dev_id, "Failed to set mode: %s (0x%02x)",
+						mgmt_errstr(status), status);
+
+		if (status == MGMT_STATUS_RFKILLED)
+			dbus_err = ERROR_INTERFACE ".Blocked";
+		else
+			dbus_err = ERROR_INTERFACE ".Failed";
+
+		g_dbus_pending_property_error(data->id, dbus_err,
+							mgmt_errstr(status));
+		return;
+	}
+
+	g_dbus_pending_property_success(data->id);
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	new_settings_callback(adapter->dev_id, length, param, adapter);
+}
+
+static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
+						DBusMessageIter *value,
+						GDBusPendingPropertySet id)
+{
+	struct property_set_data *data;
+	struct mgmt_cp_set_discoverable cp;
+	void *param;
+	dbus_bool_t enable, current_enable;
+	uint16_t opcode, len;
+	uint8_t mode;
+
+	dbus_message_iter_get_basic(value, &enable);
+
+	if (adapter->current_settings & setting)
+		current_enable = TRUE;
+	else
+		current_enable = FALSE;
+
+	if (enable == current_enable) {
+		g_dbus_pending_property_success(id);
+		return;
+	}
+
+	mode = (enable == TRUE) ? 0x01 : 0x00;
+
+	switch (setting) {
+	case MGMT_SETTING_POWERED:
+		opcode = MGMT_OP_SET_POWERED;
+		param = &mode;
+		len = sizeof(mode);
+		break;
+	case MGMT_SETTING_DISCOVERABLE:
+		if (kernel_conn_control) {
+			if (mode) {
+				set_mode(adapter, MGMT_OP_SET_CONNECTABLE,
+									mode);
+			} else {
+				opcode = MGMT_OP_SET_CONNECTABLE;
+				param = &mode;
+				len = sizeof(mode);
+				break;
+			}
+		}
+
+		memset(&cp, 0, sizeof(cp));
+		cp.val = mode;
+		if (cp.val)
+			cp.timeout = htobs(adapter->discoverable_timeout);
+
+		opcode = MGMT_OP_SET_DISCOVERABLE;
+		param = &cp;
+		len = sizeof(cp);
+		break;
+	case MGMT_SETTING_BONDABLE:
+		opcode = MGMT_OP_SET_BONDABLE;
+		param = &mode;
+		len = sizeof(mode);
+		break;
+	default:
+		goto failed;
+	}
+
+	DBG("sending %s command for index %u", mgmt_opstr(opcode),
+							adapter->dev_id);
+
+	data = g_try_new0(struct property_set_data, 1);
+	if (!data)
+		goto failed;
+
+	data->adapter = adapter;
+	data->id = id;
+
+	if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param,
+				property_set_mode_complete, data, g_free) > 0)
+		return;
+
+	g_free(data);
+
+failed:
+	btd_error(adapter->dev_id, "Failed to set mode for index %u",
+							adapter->dev_id);
+
+	g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", NULL);
+}
+
+static gboolean property_get_powered(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	return property_get_mode(adapter, MGMT_SETTING_POWERED, iter);
+}
+
+static void property_set_powered(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (powering_down) {
+		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+							"Powering down");
+		return;
+	}
+
+	property_set_mode(adapter, MGMT_SETTING_POWERED, iter, id);
+}
+
+static gboolean property_get_discoverable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	return property_get_mode(adapter, MGMT_SETTING_DISCOVERABLE, iter);
+}
+
+static void property_set_discoverable(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (adapter->discoverable_timeout > 0 &&
+			!(adapter->current_settings & MGMT_SETTING_POWERED)) {
+		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+								"Not Powered");
+		return;
+	}
+
+	property_set_mode(adapter, MGMT_SETTING_DISCOVERABLE, iter, id);
+}
+
+static gboolean property_get_discoverable_timeout(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	dbus_uint32_t value = adapter->discoverable_timeout;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &value);
+
+	return TRUE;
+}
+
+static void property_set_discoverable_timeout(
+				const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	dbus_uint32_t value;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	adapter->discoverable_timeout = value;
+
+	g_dbus_pending_property_success(id);
+
+	store_adapter_info(adapter);
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+				ADAPTER_INTERFACE, "DiscoverableTimeout");
+
+
+	if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE)
+		set_discoverable(adapter, 0x01, adapter->discoverable_timeout);
+}
+
+static gboolean property_get_pairable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	return property_get_mode(adapter, MGMT_SETTING_BONDABLE, iter);
+}
+
+static void property_set_pairable(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	property_set_mode(adapter, MGMT_SETTING_BONDABLE, iter, id);
+}
+
+static gboolean property_get_pairable_timeout(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	dbus_uint32_t value = adapter->pairable_timeout;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &value);
+
+	return TRUE;
+}
+
+static void property_set_pairable_timeout(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	dbus_uint32_t value;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	adapter->pairable_timeout = value;
+
+	g_dbus_pending_property_success(id);
+
+	store_adapter_info(adapter);
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "PairableTimeout");
+
+	trigger_pairable_timeout(adapter);
+}
+
+static gboolean property_get_discovering(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	dbus_bool_t discovering = adapter->discovering;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &discovering);
+
+	return TRUE;
+}
+
+static void add_gatt_uuid(struct gatt_db_attribute *attrib, void *user_data)
+{
+	GHashTable *uuids = user_data;
+	bt_uuid_t uuid, u128;
+	char uuidstr[MAX_LEN_UUID_STR + 1];
+
+	if (!gatt_db_service_get_active(attrib))
+		return;
+
+	if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
+		return;
+
+	bt_uuid_to_uuid128(&uuid, &u128);
+	bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
+
+	g_hash_table_add(uuids, strdup(uuidstr));
+}
+
+static void iter_append_uuid(gpointer key, gpointer value, gpointer user_data)
+{
+	DBusMessageIter *iter = user_data;
+	const char *uuid = key;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+}
+
+static gboolean property_get_uuids(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	DBusMessageIter entry;
+	sdp_list_t *l;
+	struct gatt_db *db;
+	GHashTable *uuids;
+
+	uuids = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
+	if (!uuids)
+		return FALSE;
+
+	/* SDP records */
+	for (l = adapter->services; l != NULL; l = l->next) {
+		sdp_record_t *rec = l->data;
+		char *uuid;
+
+		uuid = bt_uuid2string(&rec->svclass);
+		if (uuid == NULL)
+			continue;
+
+		g_hash_table_add(uuids, uuid);
+	}
+
+	/* GATT services */
+	db = btd_gatt_database_get_db(adapter->database);
+	if (db)
+		gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &entry);
+	g_hash_table_foreach(uuids, iter_append_uuid, &entry);
+	dbus_message_iter_close_container(iter, &entry);
+
+	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);
+	}
+
+	device_request_disconnect(device, msg);
+
+	return NULL;
+}
+
+static const GDBusMethodTable adapter_methods[] = {
+	{ GDBUS_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("RemoveDevice",
+			GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
+	{ }
+};
+
+static const GDBusPropertyTable adapter_properties[] = {
+	{ "Address", "s", property_get_address },
+	{ "Name", "s", property_get_name },
+	{ "Alias", "s", property_get_alias, property_set_alias },
+	{ "Class", "u", property_get_class },
+	{ "Powered", "b", property_get_powered, property_set_powered },
+	{ "Discoverable", "b", property_get_discoverable,
+					property_set_discoverable },
+	{ "DiscoverableTimeout", "u", property_get_discoverable_timeout,
+					property_set_discoverable_timeout },
+	{ "Pairable", "b", property_get_pairable, property_set_pairable },
+	{ "PairableTimeout", "u", property_get_pairable_timeout,
+					property_set_pairable_timeout },
+	{ "Discovering", "b", property_get_discovering },
+	{ "UUIDs", "as", property_get_uuids },
+	{ "Modalias", "s", property_get_modalias, NULL,
+					property_exists_modalias },
+	{ }
+};
+
+static int str2buf(const char *str, uint8_t *buf, size_t blen)
+{
+	int i, dlen;
+
+	if (str == NULL)
+		return -EINVAL;
+
+	memset(buf, 0, blen);
+
+	dlen = MIN((strlen(str) / 2), blen);
+
+	for (i = 0; i < dlen; i++)
+		sscanf(str + (i * 2), "%02hhX", &buf[i]);
+
+	return 0;
+}
+
+static struct link_key_info *get_key_info(GKeyFile *key_file, const char *peer)
+{
+	struct link_key_info *info = NULL;
+	char *str;
+
+	str = g_key_file_get_string(key_file, "LinkKey", "Key", NULL);
+	if (!str || strlen(str) < 32)
+		goto failed;
+
+	info = g_new0(struct link_key_info, 1);
+
+	str2ba(peer, &info->bdaddr);
+
+	if (!strncmp(str, "0x", 2))
+		str2buf(&str[2], info->key, sizeof(info->key));
+	else
+		str2buf(&str[0], info->key, sizeof(info->key));
+
+	info->type = g_key_file_get_integer(key_file, "LinkKey", "Type", NULL);
+	info->pin_len = g_key_file_get_integer(key_file, "LinkKey", "PINLength",
+						NULL);
+
+failed:
+	g_free(str);
+
+	return info;
+}
+
+static struct smp_ltk_info *get_ltk(GKeyFile *key_file, const char *peer,
+					uint8_t peer_type, const char *group)
+{
+	struct smp_ltk_info *ltk = NULL;
+	GError *gerr = NULL;
+	bool master;
+	char *key;
+	char *rand = NULL;
+
+	key = g_key_file_get_string(key_file, group, "Key", NULL);
+	if (!key || strlen(key) < 32)
+		goto failed;
+
+	rand = g_key_file_get_string(key_file, group, "Rand", NULL);
+	if (!rand)
+		goto failed;
+
+	ltk = g_new0(struct smp_ltk_info, 1);
+
+	/* Default to assuming a master key */
+	ltk->master = true;
+
+	str2ba(peer, &ltk->bdaddr);
+	ltk->bdaddr_type = peer_type;
+
+	/*
+	 * Long term keys should respond to an identity address which can
+	 * either be a public address or a random static address. Keys
+	 * stored for resolvable random and unresolvable random addresses
+	 * are ignored.
+	 *
+	 * This is an extra sanity check for older kernel versions or older
+	 * daemons that might have been instructed to store long term keys
+	 * for these temporary addresses.
+	 */
+	if (ltk->bdaddr_type == BDADDR_LE_RANDOM &&
+					(ltk->bdaddr.b[5] & 0xc0) != 0xc0) {
+		g_free(ltk);
+		ltk = NULL;
+		goto failed;
+	}
+
+	if (!strncmp(key, "0x", 2))
+		str2buf(&key[2], ltk->val, sizeof(ltk->val));
+	else
+		str2buf(&key[0], ltk->val, sizeof(ltk->val));
+
+	if (!strncmp(rand, "0x", 2)) {
+		uint64_t rand_le;
+		str2buf(&rand[2], (uint8_t *) &rand_le, sizeof(rand_le));
+		ltk->rand = le64_to_cpu(rand_le);
+	} else {
+		sscanf(rand, "%" PRIu64, &ltk->rand);
+	}
+
+	ltk->authenticated = g_key_file_get_integer(key_file, group,
+							"Authenticated", NULL);
+	ltk->enc_size = g_key_file_get_integer(key_file, group, "EncSize",
+									NULL);
+	ltk->ediv = g_key_file_get_integer(key_file, group, "EDiv", NULL);
+
+	master = g_key_file_get_boolean(key_file, group, "Master", &gerr);
+	if (gerr)
+		g_error_free(gerr);
+	else
+		ltk->master = master;
+
+failed:
+	g_free(key);
+	g_free(rand);
+
+	return ltk;
+}
+
+static GSList *get_ltk_info(GKeyFile *key_file, const char *peer,
+							uint8_t bdaddr_type)
+{
+	struct smp_ltk_info *ltk;
+	GSList *l = NULL;
+
+	DBG("%s", peer);
+
+	ltk = get_ltk(key_file, peer, bdaddr_type, "LongTermKey");
+	if (ltk)
+		l = g_slist_append(l, ltk);
+
+	ltk = get_ltk(key_file, peer, bdaddr_type, "SlaveLongTermKey");
+	if (ltk) {
+		ltk->master = false;
+		l = g_slist_append(l, ltk);
+	}
+
+	return l;
+}
+
+static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
+							uint8_t bdaddr_type)
+{
+	struct irk_info *irk;
+	char *str;
+
+	str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL);
+	if (!str || strlen(str) < 32)
+		return NULL;
+
+	irk = g_new0(struct irk_info, 1);
+
+	str2ba(peer, &irk->bdaddr);
+	irk->bdaddr_type = bdaddr_type;
+
+	if (!strncmp(str, "0x", 2))
+		str2buf(&str[2], irk->val, sizeof(irk->val));
+	else
+		str2buf(&str[0], irk->val, sizeof(irk->val));
+
+	g_free(str);
+
+	return irk;
+}
+
+static struct conn_param *get_conn_param(GKeyFile *key_file, const char *peer,
+							uint8_t bdaddr_type)
+{
+	struct conn_param *param;
+
+	if (!g_key_file_has_group(key_file, "ConnectionParameters"))
+		return NULL;
+
+	param = g_new0(struct conn_param, 1);
+
+	param->min_interval = g_key_file_get_integer(key_file,
+							"ConnectionParameters",
+							"MinInterval", NULL);
+	param->max_interval = g_key_file_get_integer(key_file,
+							"ConnectionParameters",
+							"MaxInterval", NULL);
+	param->latency = g_key_file_get_integer(key_file,
+							"ConnectionParameters",
+							"Latency", NULL);
+	param->timeout = g_key_file_get_integer(key_file,
+							"ConnectionParameters",
+							"Timeout", NULL);
+	str2ba(peer, &param->bdaddr);
+	param->bdaddr_type = bdaddr_type;
+
+	return param;
+}
+
+static void load_link_keys_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+			"Failed to load link keys for hci%u: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("link keys loaded for hci%u", adapter->dev_id);
+}
+
+static void load_link_keys(struct btd_adapter *adapter, GSList *keys,
+							bool debug_keys)
+{
+	struct mgmt_cp_load_link_keys *cp;
+	struct mgmt_link_key_info *key;
+	size_t key_count, cp_size;
+	unsigned int id;
+	GSList *l;
+
+	/*
+	 * If the controller does not support BR/EDR operation,
+	 * there is no point in trying to load the link keys into
+	 * the kernel.
+	 *
+	 * This is an optimization for Low Energy only controllers.
+	 */
+	if (!(adapter->supported_settings & MGMT_SETTING_BREDR))
+		return;
+
+	key_count = g_slist_length(keys);
+
+	DBG("hci%u keys %zu debug_keys %d", adapter->dev_id, key_count,
+								debug_keys);
+
+	cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+	cp = g_try_malloc0(cp_size);
+	if (cp == NULL) {
+		btd_error(adapter->dev_id, "No memory for link keys for hci%u",
+							adapter->dev_id);
+		return;
+	}
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to
+	 * load an empty list into the kernel. That way it is ensured
+	 * that no old keys from a previous daemon are present.
+	 *
+	 * In addition it is also the only way to toggle the different
+	 * behavior for debug keys.
+	 */
+	cp->debug_keys = debug_keys;
+	cp->key_count = htobs(key_count);
+
+	for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
+		struct link_key_info *info = l->data;
+
+		bacpy(&key->addr.bdaddr, &info->bdaddr);
+		key->addr.type = BDADDR_BREDR;
+		key->type = info->type;
+		memcpy(key->val, info->key, 16);
+		key->pin_len = info->pin_len;
+	}
+
+	id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_LINK_KEYS,
+				adapter->dev_id, cp_size, cp,
+				load_link_keys_complete, adapter, NULL);
+
+	g_free(cp);
+
+	if (id == 0)
+		btd_error(adapter->dev_id, "Failed to load link keys for hci%u",
+							adapter->dev_id);
+}
+
+static gboolean load_ltks_timeout(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	btd_error(adapter->dev_id, "Loading LTKs timed out for hci%u",
+							adapter->dev_id);
+
+	adapter->load_ltks_timeout = 0;
+
+	mgmt_cancel(adapter->mgmt, adapter->load_ltks_id);
+	adapter->load_ltks_id = 0;
+
+	return FALSE;
+}
+
+static void load_ltks_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to load LTKs for hci%u: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+	}
+
+	adapter->load_ltks_id = 0;
+
+	g_source_remove(adapter->load_ltks_timeout);
+	adapter->load_ltks_timeout = 0;
+
+	DBG("LTKs loaded for hci%u", adapter->dev_id);
+}
+
+static void load_ltks(struct btd_adapter *adapter, GSList *keys)
+{
+	struct mgmt_cp_load_long_term_keys *cp;
+	struct mgmt_ltk_info *key;
+	size_t key_count, cp_size;
+	GSList *l;
+
+	/*
+	 * If the controller does not support Low Energy operation,
+	 * there is no point in trying to load the long term keys
+	 * into the kernel.
+	 *
+	 * While there is no harm in loading keys into the kernel,
+	 * this is an optimization to avoid a confusing warning
+	 * message when the loading of the keys timed out due to
+	 * a kernel bug (see comment below).
+	 */
+	if (!(adapter->supported_settings & MGMT_SETTING_LE))
+		return;
+
+	key_count = g_slist_length(keys);
+
+	DBG("hci%u keys %zu", adapter->dev_id, key_count);
+
+	cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+	cp = g_try_malloc0(cp_size);
+	if (cp == NULL) {
+		btd_error(adapter->dev_id, "No memory for LTKs for hci%u",
+							adapter->dev_id);
+		return;
+	}
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to
+	 * load an empty list into the kernel. That way it is ensured
+	 * that no old keys from a previous daemon are present.
+	 */
+	cp->key_count = htobs(key_count);
+
+	for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
+		struct smp_ltk_info *info = l->data;
+
+		bacpy(&key->addr.bdaddr, &info->bdaddr);
+		key->addr.type = info->bdaddr_type;
+		memcpy(key->val, info->val, sizeof(info->val));
+		key->rand = cpu_to_le64(info->rand);
+		key->ediv = cpu_to_le16(info->ediv);
+		key->type = info->authenticated;
+		key->master = info->master;
+		key->enc_size = info->enc_size;
+	}
+
+	adapter->load_ltks_id = mgmt_send(adapter->mgmt,
+					MGMT_OP_LOAD_LONG_TERM_KEYS,
+					adapter->dev_id, cp_size, cp,
+					load_ltks_complete, adapter, NULL);
+
+	g_free(cp);
+
+	if (adapter->load_ltks_id == 0) {
+		btd_error(adapter->dev_id, "Failed to load LTKs for hci%u",
+							adapter->dev_id);
+		return;
+	}
+
+	/*
+	 * This timeout handling is needed since the kernel is stupid
+	 * and forgets to send a command complete response. However in
+	 * case of failures it does send a command status.
+	 */
+	adapter->load_ltks_timeout = g_timeout_add_seconds(2,
+						load_ltks_timeout, adapter);
+}
+
+static void load_irks_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status == MGMT_STATUS_UNKNOWN_COMMAND) {
+		btd_info(adapter->dev_id,
+			"Load IRKs failed: Kernel doesn't support LE Privacy");
+		return;
+	}
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to load IRKs for hci%u: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("IRKs loaded for hci%u", adapter->dev_id);
+}
+
+static void load_irks(struct btd_adapter *adapter, GSList *irks)
+{
+	struct mgmt_cp_load_irks *cp;
+	struct mgmt_irk_info *irk;
+	size_t irk_count, cp_size;
+	unsigned int id;
+	GSList *l;
+
+	/*
+	 * If the controller does not support LE Privacy operation,
+	 * there is no support for loading identity resolving keys
+	 * into the kernel.
+	 */
+	if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY))
+		return;
+
+	irk_count = g_slist_length(irks);
+
+	DBG("hci%u irks %zu", adapter->dev_id, irk_count);
+
+	cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+	cp = g_try_malloc0(cp_size);
+	if (cp == NULL) {
+		btd_error(adapter->dev_id, "No memory for IRKs for hci%u",
+							adapter->dev_id);
+		return;
+	}
+
+	/*
+	 * Even if the list of stored keys is empty, it is important to
+	 * load an empty list into the kernel. That way we tell the
+	 * kernel that we are able to handle New IRK events.
+	 */
+	cp->irk_count = htobs(irk_count);
+
+	for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++) {
+		struct irk_info *info = l->data;
+
+		bacpy(&irk->addr.bdaddr, &info->bdaddr);
+		irk->addr.type = info->bdaddr_type;
+		memcpy(irk->val, info->val, sizeof(irk->val));
+	}
+
+	id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_IRKS, adapter->dev_id,
+			cp_size, cp, load_irks_complete, adapter, NULL);
+
+	g_free(cp);
+
+	if (id == 0)
+		btd_error(adapter->dev_id, "Failed to IRKs for hci%u",
+							adapter->dev_id);
+}
+
+static void load_conn_params_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+			"hci%u Load Connection Parameters failed: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("Connection Parameters loaded for hci%u", adapter->dev_id);
+}
+
+static void load_conn_params(struct btd_adapter *adapter, GSList *params)
+{
+	struct mgmt_cp_load_conn_param *cp;
+	struct mgmt_conn_param *param;
+	size_t param_count, cp_size;
+	unsigned int id;
+	GSList *l;
+
+	/*
+	 * If the controller does not support Low Energy operation,
+	 * there is no point in trying to load the connection
+	 * parameters into the kernel.
+	 */
+	if (!(adapter->supported_settings & MGMT_SETTING_LE))
+		return;
+
+	param_count = g_slist_length(params);
+
+	DBG("hci%u conn params %zu", adapter->dev_id, param_count);
+
+	cp_size = sizeof(*cp) + (param_count * sizeof(*param));
+
+	cp = g_try_malloc0(cp_size);
+	if (cp == NULL) {
+		btd_error(adapter->dev_id,
+			"Failed to allocate memory for connection parameters");
+		return;
+	}
+
+	cp->param_count = htobs(param_count);
+
+	for (l = params, param = cp->params; l; l = g_slist_next(l), param++) {
+		struct conn_param *info = l->data;
+
+		bacpy(&param->addr.bdaddr, &info->bdaddr);
+		param->addr.type = info->bdaddr_type;
+		param->min_interval = htobs(info->min_interval);
+		param->max_interval = htobs(info->max_interval);
+		param->latency = htobs(info->latency);
+		param->timeout = htobs(info->timeout);
+	}
+
+	id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_CONN_PARAM, adapter->dev_id,
+			cp_size, cp, load_conn_params_complete, adapter, NULL);
+
+	g_free(cp);
+
+	if (id == 0)
+		btd_error(adapter->dev_id, "Load connection parameters failed");
+}
+
+static uint8_t get_le_addr_type(GKeyFile *keyfile)
+{
+	uint8_t addr_type;
+	char *type;
+
+	type = g_key_file_get_string(keyfile, "General", "AddressType", NULL);
+	if (!type)
+		return BDADDR_LE_PUBLIC;
+
+	if (g_str_equal(type, "public"))
+		addr_type = BDADDR_LE_PUBLIC;
+	else if (g_str_equal(type, "static"))
+		addr_type = BDADDR_LE_RANDOM;
+	else
+		addr_type = BDADDR_LE_PUBLIC;
+
+	g_free(type);
+
+	return addr_type;
+}
+
+static void probe_devices(void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	device_probe_profiles(device, btd_device_get_uuids(device));
+}
+
+static void load_devices(struct btd_adapter *adapter)
+{
+	char dirname[PATH_MAX];
+	char srcaddr[18];
+	GSList *keys = NULL;
+	GSList *ltks = NULL;
+	GSList *irks = NULL;
+	GSList *params = NULL;
+	GSList *added_devices = NULL;
+	DIR *dir;
+	struct dirent *entry;
+
+	ba2str(&adapter->bdaddr, srcaddr);
+
+	snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", srcaddr);
+
+	dir = opendir(dirname);
+	if (!dir) {
+		btd_error(adapter->dev_id,
+				"Unable to open adapter storage directory: %s",
+								dirname);
+		return;
+	}
+
+	while ((entry = readdir(dir)) != NULL) {
+		struct btd_device *device;
+		char filename[PATH_MAX];
+		GKeyFile *key_file;
+		struct link_key_info *key_info;
+		GSList *list, *ltk_info;
+		struct irk_info *irk_info;
+		struct conn_param *param;
+		uint8_t bdaddr_type;
+
+		if (entry->d_type == DT_UNKNOWN)
+			entry->d_type = util_get_dt(dirname, entry->d_name);
+
+		if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0)
+			continue;
+
+		snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", srcaddr,
+				entry->d_name);
+
+		key_file = g_key_file_new();
+		g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+		key_info = get_key_info(key_file, entry->d_name);
+		if (key_info)
+			keys = g_slist_append(keys, key_info);
+
+		bdaddr_type = get_le_addr_type(key_file);
+
+		ltk_info = get_ltk_info(key_file, entry->d_name, bdaddr_type);
+		ltks = g_slist_concat(ltks, ltk_info);
+
+		irk_info = get_irk_info(key_file, entry->d_name, bdaddr_type);
+		if (irk_info)
+			irks = g_slist_append(irks, irk_info);
+
+		param = get_conn_param(key_file, entry->d_name, bdaddr_type);
+		if (param)
+			params = g_slist_append(params, param);
+
+		list = g_slist_find_custom(adapter->devices, entry->d_name,
+							device_address_cmp);
+		if (list) {
+			device = list->data;
+			goto device_exist;
+		}
+
+		device = device_create_from_storage(adapter, entry->d_name,
+							key_file);
+		if (!device)
+			goto free;
+
+		btd_device_set_temporary(device, false);
+		adapter->devices = g_slist_append(adapter->devices, device);
+
+		/* TODO: register services from pre-loaded list of primaries */
+
+		added_devices = g_slist_append(added_devices, device);
+
+device_exist:
+		if (key_info) {
+			device_set_paired(device, BDADDR_BREDR);
+			device_set_bonded(device, BDADDR_BREDR);
+		}
+
+		if (ltk_info) {
+			device_set_paired(device, bdaddr_type);
+			device_set_bonded(device, bdaddr_type);
+		}
+
+free:
+		g_key_file_free(key_file);
+	}
+
+	closedir(dir);
+
+	load_link_keys(adapter, keys, main_opts.debug_keys);
+	g_slist_free_full(keys, g_free);
+
+	load_ltks(adapter, ltks);
+	g_slist_free_full(ltks, g_free);
+	load_irks(adapter, irks);
+	g_slist_free_full(irks, g_free);
+	load_conn_params(adapter, params);
+	g_slist_free_full(params, g_free);
+
+	g_slist_free_full(added_devices, probe_devices);
+}
+
+int btd_adapter_block_address(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct mgmt_cp_block_device cp;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u %s", adapter->dev_id, addr);
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_BLOCK_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+int btd_adapter_unblock_address(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct mgmt_cp_unblock_device cp;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u %s", adapter->dev_id, addr);
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_UNBLOCK_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+static int clear_blocked(struct btd_adapter *adapter)
+{
+	return btd_adapter_unblock_address(adapter, BDADDR_ANY, 0);
+}
+
+static void probe_driver(struct btd_adapter *adapter, gpointer user_data)
+{
+	struct btd_adapter_driver *driver = user_data;
+	int err;
+
+	if (driver->probe == NULL)
+		return;
+
+	err = driver->probe(adapter);
+	if (err < 0) {
+		btd_error(adapter->dev_id, "%s: %s (%d)", driver->name,
+							strerror(-err), -err);
+		return;
+	}
+
+	adapter->drivers = g_slist_prepend(adapter->drivers, driver);
+}
+
+static void load_drivers(struct btd_adapter *adapter)
+{
+	GSList *l;
+
+	for (l = adapter_drivers; l; l = l->next)
+		probe_driver(adapter, l->data);
+}
+
+static void probe_profile(struct btd_profile *profile, void *data)
+{
+	struct btd_adapter *adapter = data;
+	int err;
+
+	if (profile->adapter_probe == NULL)
+		return;
+
+	err = profile->adapter_probe(profile, adapter);
+	if (err < 0) {
+		btd_error(adapter->dev_id, "%s: %s (%d)", profile->name,
+							strerror(-err), -err);
+		return;
+	}
+
+	adapter->profiles = g_slist_prepend(adapter->profiles, profile);
+}
+
+void adapter_add_profile(struct btd_adapter *adapter, gpointer p)
+{
+	struct btd_profile *profile = p;
+
+	if (!adapter->initialized)
+		return;
+
+	probe_profile(profile, adapter);
+
+	g_slist_foreach(adapter->devices, device_probe_profile, profile);
+}
+
+void adapter_remove_profile(struct btd_adapter *adapter, gpointer p)
+{
+	struct btd_profile *profile = p;
+
+	if (!adapter->initialized)
+		return;
+
+	if (profile->device_remove)
+		g_slist_foreach(adapter->devices, device_remove_profile, p);
+
+	adapter->profiles = g_slist_remove(adapter->profiles, profile);
+
+	if (profile->adapter_remove)
+		profile->adapter_remove(profile, adapter);
+}
+
+static void adapter_add_connection(struct btd_adapter *adapter,
+						struct btd_device *device,
+						uint8_t bdaddr_type)
+{
+	device_add_connection(device, bdaddr_type);
+
+	if (g_slist_find(adapter->connections, device)) {
+		btd_error(adapter->dev_id,
+				"Device is already marked as connected");
+		return;
+	}
+
+	adapter->connections = g_slist_append(adapter->connections, device);
+}
+
+static void get_connections_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const struct mgmt_rp_get_connections *rp = param;
+	uint16_t i, conn_count;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to get connections: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Wrong size of get connections response");
+		return;
+	}
+
+	conn_count = btohs(rp->conn_count);
+
+	DBG("Connection count: %d", conn_count);
+
+	if (conn_count * sizeof(struct mgmt_addr_info) +
+						sizeof(*rp) != length) {
+		btd_error(adapter->dev_id,
+			"Incorrect packet size for get connections response");
+		return;
+	}
+
+	for (i = 0; i < conn_count; i++) {
+		const struct mgmt_addr_info *addr = &rp->addr[i];
+		struct btd_device *device;
+		char address[18];
+
+		ba2str(&addr->bdaddr, address);
+		DBG("Adding existing connection to %s", address);
+
+		device = btd_adapter_get_device(adapter, &addr->bdaddr,
+								addr->type);
+		if (device)
+			adapter_add_connection(adapter, device, addr->type);
+	}
+}
+
+static void load_connections(struct btd_adapter *adapter)
+{
+	DBG("sending get connections command for index %u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_GET_CONNECTIONS,
+				adapter->dev_id, 0, NULL,
+				get_connections_complete, adapter, NULL) > 0)
+		return;
+
+	btd_error(adapter->dev_id, "Failed to get connections for index %u",
+							adapter->dev_id);
+}
+
+bool btd_adapter_get_pairable(struct btd_adapter *adapter)
+{
+	if (adapter->current_settings & MGMT_SETTING_BONDABLE)
+		return true;
+
+	return false;
+}
+
+bool btd_adapter_get_powered(struct btd_adapter *adapter)
+{
+	if (adapter->current_settings & MGMT_SETTING_POWERED)
+		return true;
+
+	return false;
+}
+
+bool btd_adapter_get_connectable(struct btd_adapter *adapter)
+{
+	if (adapter->current_settings & MGMT_SETTING_CONNECTABLE)
+		return true;
+
+	return false;
+}
+
+struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter)
+{
+	if (!adapter)
+		return NULL;
+
+	return adapter->database;
+}
+
+uint32_t btd_adapter_get_class(struct btd_adapter *adapter)
+{
+	return adapter->dev_class;
+}
+
+const char *btd_adapter_get_name(struct btd_adapter *adapter)
+{
+	if (adapter->stored_alias)
+		return adapter->stored_alias;
+
+	if (adapter->system_name)
+		return adapter->system_name;
+
+	return NULL;
+}
+
+int adapter_connect_list_add(struct btd_adapter *adapter,
+					struct btd_device *device)
+{
+	/*
+	 * If the adapter->connect_le device is getting added back to
+	 * the connect list it probably means that the connect attempt
+	 * failed and hence we should clear this pointer
+	 */
+	if (device == adapter->connect_le)
+		adapter->connect_le = NULL;
+
+	/*
+	 * If kernel background scanning is supported then the
+	 * adapter_auto_connect_add() function is used to maintain what to
+	 * connect.
+	 */
+	if (kernel_conn_control)
+		return 0;
+
+	if (g_slist_find(adapter->connect_list, device)) {
+		DBG("ignoring already added device %s",
+						device_get_path(device));
+		goto done;
+	}
+
+	if (!(adapter->supported_settings & MGMT_SETTING_LE)) {
+		btd_error(adapter->dev_id,
+			"Can't add %s to non-LE capable adapter connect list",
+						device_get_path(device));
+		return -ENOTSUP;
+	}
+
+	adapter->connect_list = g_slist_append(adapter->connect_list, device);
+	DBG("%s added to %s's connect_list", device_get_path(device),
+							adapter->system_name);
+
+done:
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return 0;
+
+	trigger_passive_scanning(adapter);
+
+	return 0;
+}
+
+void adapter_connect_list_remove(struct btd_adapter *adapter,
+					struct btd_device *device)
+{
+	/*
+	 * If the adapter->connect_le device is being removed from the
+	 * connect list it means the connection was successful and hence
+	 * the pointer should be cleared
+	 */
+	if (device == adapter->connect_le)
+		adapter->connect_le = NULL;
+
+	if (kernel_conn_control)
+		return;
+
+	if (!g_slist_find(adapter->connect_list, device)) {
+		DBG("device %s is not on the list, ignoring",
+						device_get_path(device));
+		return;
+	}
+
+	adapter->connect_list = g_slist_remove(adapter->connect_list, device);
+	DBG("%s removed from %s's connect_list", device_get_path(device),
+							adapter->system_name);
+
+	if (!adapter->connect_list) {
+		stop_passive_scanning(adapter);
+		return;
+	}
+
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return;
+
+	trigger_passive_scanning(adapter);
+}
+
+static void add_whitelist_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_add_device *rp = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *dev;
+	char addr[18];
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Too small Add Device complete event");
+		return;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	dev = btd_adapter_find_device(adapter, &rp->addr.bdaddr,
+							rp->addr.type);
+	if (!dev) {
+		btd_error(adapter->dev_id,
+			"Add Device complete for unknown device %s", addr);
+		return;
+	}
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+					"Failed to add device %s: %s (0x%02x)",
+					addr, mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("%s added to kernel whitelist", addr);
+}
+
+void adapter_whitelist_add(struct btd_adapter *adapter, struct btd_device *dev)
+{
+	struct mgmt_cp_add_device cp;
+
+	if (!kernel_conn_control)
+		return;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, device_get_address(dev));
+	cp.addr.type = BDADDR_BREDR;
+	cp.action = 0x01;
+
+	mgmt_send(adapter->mgmt, MGMT_OP_ADD_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				add_whitelist_complete, adapter, NULL);
+}
+
+static void remove_whitelist_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_remove_device *rp = param;
+	char addr[18];
+
+	if (length < sizeof(*rp)) {
+		error("Too small Remove Device complete event");
+		return;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to remove device %s: %s (0x%02x)",
+					addr, mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("%s removed from kernel whitelist", addr);
+}
+
+void adapter_whitelist_remove(struct btd_adapter *adapter, struct btd_device *dev)
+{
+	struct mgmt_cp_remove_device cp;
+
+	if (!kernel_conn_control)
+		return;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, device_get_address(dev));
+	cp.addr.type = BDADDR_BREDR;
+
+	mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				remove_whitelist_complete, adapter, NULL);
+}
+
+static void add_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_add_device *rp = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *dev;
+	char addr[18];
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Too small Add Device complete event");
+		return;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	dev = btd_adapter_find_device(adapter, &rp->addr.bdaddr,
+							rp->addr.type);
+	if (!dev) {
+		btd_error(adapter->dev_id,
+			"Add Device complete for unknown device %s", addr);
+		return;
+	}
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+			"Failed to add device %s (%u): %s (0x%02x)",
+			addr, rp->addr.type, mgmt_errstr(status), status);
+		adapter->connect_list = g_slist_remove(adapter->connect_list,
+									dev);
+		return;
+	}
+
+	DBG("%s (%u) added to kernel connect list", addr, rp->addr.type);
+}
+
+void adapter_auto_connect_add(struct btd_adapter *adapter,
+					struct btd_device *device)
+{
+	struct mgmt_cp_add_device cp;
+	const bdaddr_t *bdaddr;
+	uint8_t bdaddr_type;
+	unsigned int id;
+
+	if (!kernel_conn_control)
+		return;
+
+	if (g_slist_find(adapter->connect_list, device)) {
+		DBG("ignoring already added device %s",
+						device_get_path(device));
+		return;
+	}
+
+	bdaddr = device_get_address(device);
+	bdaddr_type = btd_device_get_bdaddr_type(device);
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		DBG("auto-connection feature is not avaiable for BR/EDR");
+		return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+	cp.action = 0x02;
+
+	id = mgmt_send(adapter->mgmt, MGMT_OP_ADD_DEVICE,
+			adapter->dev_id, sizeof(cp), &cp, add_device_complete,
+			adapter, NULL);
+	if (id == 0)
+		return;
+
+	adapter->connect_list = g_slist_append(adapter->connect_list, device);
+}
+
+static void remove_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_remove_device *rp = param;
+	char addr[18];
+
+	if (length < sizeof(*rp)) {
+		error("Too small Remove Device complete event");
+		return;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to remove device %s (%u): %s (0x%02x)",
+			addr, rp->addr.type, mgmt_errstr(status), status);
+		return;
+	}
+
+	DBG("%s (%u) removed from kernel connect list", addr, rp->addr.type);
+}
+
+void adapter_auto_connect_remove(struct btd_adapter *adapter,
+					struct btd_device *device)
+{
+	struct mgmt_cp_remove_device cp;
+	const bdaddr_t *bdaddr;
+	uint8_t bdaddr_type;
+	unsigned int id;
+
+	if (!kernel_conn_control)
+		return;
+
+	if (!g_slist_find(adapter->connect_list, device)) {
+		DBG("ignoring not added device %s", device_get_path(device));
+		return;
+	}
+
+	bdaddr = device_get_address(device);
+	bdaddr_type = btd_device_get_bdaddr_type(device);
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		DBG("auto-connection feature is not avaiable for BR/EDR");
+		return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+
+	id = mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE,
+			adapter->dev_id, sizeof(cp), &cp,
+			remove_device_complete, adapter, NULL);
+	if (id == 0)
+		return;
+
+	adapter->connect_list = g_slist_remove(adapter->connect_list, device);
+}
+
+static void adapter_start(struct btd_adapter *adapter)
+{
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Powered");
+
+	DBG("adapter %s has been enabled", adapter->path);
+
+	trigger_passive_scanning(adapter);
+}
+
+static void reply_pending_requests(struct btd_adapter *adapter)
+{
+	GSList *l;
+
+	if (!adapter)
+		return;
+
+	/* pending bonding */
+	for (l = adapter->devices; l; l = l->next) {
+		struct btd_device *device = l->data;
+
+		if (device_is_bonding(device, NULL))
+			device_bonding_failed(device,
+						HCI_OE_USER_ENDED_CONNECTION);
+	}
+}
+
+static void remove_driver(gpointer data, gpointer user_data)
+{
+	struct btd_adapter_driver *driver = data;
+	struct btd_adapter *adapter = user_data;
+
+	if (driver->remove)
+		driver->remove(adapter);
+}
+
+static void remove_profile(gpointer data, gpointer user_data)
+{
+	struct btd_profile *profile = data;
+	struct btd_adapter *adapter = user_data;
+
+	if (profile->adapter_remove)
+		profile->adapter_remove(profile, adapter);
+}
+
+static void unload_drivers(struct btd_adapter *adapter)
+{
+	g_slist_foreach(adapter->drivers, remove_driver, adapter);
+	g_slist_free(adapter->drivers);
+	adapter->drivers = NULL;
+
+	g_slist_foreach(adapter->profiles, remove_profile, adapter);
+	g_slist_free(adapter->profiles);
+	adapter->profiles = NULL;
+}
+
+static void free_service_auth(gpointer data, gpointer user_data)
+{
+	struct service_auth *auth = data;
+
+	g_free(auth);
+}
+
+static void adapter_free(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	DBG("%p", adapter);
+
+	if (adapter->load_ltks_timeout > 0)
+		g_source_remove(adapter->load_ltks_timeout);
+
+	if (adapter->confirm_name_timeout > 0)
+		g_source_remove(adapter->confirm_name_timeout);
+
+	if (adapter->pair_device_timeout > 0)
+		g_source_remove(adapter->pair_device_timeout);
+
+	if (adapter->auth_idle_id)
+		g_source_remove(adapter->auth_idle_id);
+
+	g_queue_foreach(adapter->auths, free_service_auth, NULL);
+	g_queue_free(adapter->auths);
+
+	/*
+	 * Unregister all handlers for this specific index since
+	 * the adapter bound to them is no longer valid.
+	 *
+	 * This also avoids having multiple instances of the same
+	 * handler in case indexes got removed and re-added.
+	 */
+	mgmt_unregister_index(adapter->mgmt, adapter->dev_id);
+
+	/*
+	 * Cancel all pending commands for this specific index
+	 * since the adapter bound to them is no longer valid.
+	 */
+	mgmt_cancel_index(adapter->mgmt, adapter->dev_id);
+
+	mgmt_unref(adapter->mgmt);
+
+	sdp_list_free(adapter->services, NULL);
+
+	g_slist_free(adapter->connections);
+
+	g_free(adapter->path);
+	g_free(adapter->name);
+	g_free(adapter->short_name);
+	g_free(adapter->system_name);
+	g_free(adapter->stored_alias);
+	g_free(adapter->current_alias);
+	free(adapter->modalias);
+	g_free(adapter);
+}
+
+struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
+{
+	__sync_fetch_and_add(&adapter->ref_count, 1);
+
+	return adapter;
+}
+
+void btd_adapter_unref(struct btd_adapter *adapter)
+{
+	if (__sync_sub_and_fetch(&adapter->ref_count, 1))
+		return;
+
+	if (!adapter->path) {
+		DBG("Freeing adapter %u", adapter->dev_id);
+
+		adapter_free(adapter);
+		return;
+	}
+
+	DBG("Freeing adapter %s", adapter->path);
+
+	g_dbus_unregister_interface(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE);
+}
+
+static void convert_names_entry(char *key, char *value, void *user_data)
+{
+	char *address = user_data;
+	char *str = key;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+
+	if (strchr(key, '#'))
+		str[17] = '\0';
+
+	if (bachk(str) != 0)
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", address, str);
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	g_key_file_set_string(key_file, "General", "Name", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+struct device_converter {
+	char *address;
+	void (*cb)(GKeyFile *key_file, void *value);
+	gboolean force;
+};
+
+static void set_device_type(GKeyFile *key_file, char type)
+{
+	char *techno;
+	char *addr_type = NULL;
+	char *str;
+
+	switch (type) {
+	case BDADDR_BREDR:
+		techno = "BR/EDR";
+		break;
+	case BDADDR_LE_PUBLIC:
+		techno = "LE";
+		addr_type = "public";
+		break;
+	case BDADDR_LE_RANDOM:
+		techno = "LE";
+		addr_type = "static";
+		break;
+	default:
+		return;
+	}
+
+	str = g_key_file_get_string(key_file, "General",
+					"SupportedTechnologies", NULL);
+	if (!str)
+		g_key_file_set_string(key_file, "General",
+					"SupportedTechnologies", techno);
+	else if (!strstr(str, techno))
+		g_key_file_set_string(key_file, "General",
+					"SupportedTechnologies", "BR/EDR;LE");
+
+	g_free(str);
+
+	if (addr_type)
+		g_key_file_set_string(key_file, "General", "AddressType",
+					addr_type);
+}
+
+static void convert_aliases_entry(GKeyFile *key_file, void *value)
+{
+	g_key_file_set_string(key_file, "General", "Alias", value);
+}
+
+static void convert_trusts_entry(GKeyFile *key_file, void *value)
+{
+	g_key_file_set_boolean(key_file, "General", "Trusted", TRUE);
+}
+
+static void convert_classes_entry(GKeyFile *key_file, void *value)
+{
+	g_key_file_set_string(key_file, "General", "Class", value);
+}
+
+static void convert_blocked_entry(GKeyFile *key_file, void *value)
+{
+	g_key_file_set_boolean(key_file, "General", "Blocked", TRUE);
+}
+
+static void convert_did_entry(GKeyFile *key_file, void *value)
+{
+	char *vendor_str, *product_str, *version_str;
+	uint16_t val;
+
+	vendor_str = strchr(value, ' ');
+	if (!vendor_str)
+		return;
+
+	*(vendor_str++) = 0;
+
+	if (g_str_equal(value, "FFFF"))
+		return;
+
+	product_str = strchr(vendor_str, ' ');
+	if (!product_str)
+		return;
+
+	*(product_str++) = 0;
+
+	version_str = strchr(product_str, ' ');
+	if (!version_str)
+		return;
+
+	*(version_str++) = 0;
+
+	val = (uint16_t) strtol(value, NULL, 16);
+	g_key_file_set_integer(key_file, "DeviceID", "Source", val);
+
+	val = (uint16_t) strtol(vendor_str, NULL, 16);
+	g_key_file_set_integer(key_file, "DeviceID", "Vendor", val);
+
+	val = (uint16_t) strtol(product_str, NULL, 16);
+	g_key_file_set_integer(key_file, "DeviceID", "Product", val);
+
+	val = (uint16_t) strtol(version_str, NULL, 16);
+	g_key_file_set_integer(key_file, "DeviceID", "Version", val);
+}
+
+static void convert_linkkey_entry(GKeyFile *key_file, void *value)
+{
+	char *type_str, *length_str, *str;
+	int val;
+
+	type_str = strchr(value, ' ');
+	if (!type_str)
+		return;
+
+	*(type_str++) = 0;
+
+	length_str = strchr(type_str, ' ');
+	if (!length_str)
+		return;
+
+	*(length_str++) = 0;
+
+	str = g_strconcat("0x", value, NULL);
+	g_key_file_set_string(key_file, "LinkKey", "Key", str);
+	g_free(str);
+
+	val = strtol(type_str, NULL, 16);
+	g_key_file_set_integer(key_file, "LinkKey", "Type", val);
+
+	val = strtol(length_str, NULL, 16);
+	g_key_file_set_integer(key_file, "LinkKey", "PINLength", val);
+}
+
+static void convert_ltk_entry(GKeyFile *key_file, void *value)
+{
+	char *auth_str, *rand_str, *str;
+	int i, ret;
+	unsigned char auth, master, enc_size;
+	unsigned short ediv;
+
+	auth_str = strchr(value, ' ');
+	if (!auth_str)
+		return;
+
+	*(auth_str++) = 0;
+
+	for (i = 0, rand_str = auth_str; i < 4; i++) {
+		rand_str = strchr(rand_str, ' ');
+		if (!rand_str || rand_str[1] == '\0')
+			return;
+
+		rand_str++;
+	}
+
+	ret = sscanf(auth_str, " %hhd %hhd %hhd %hd", &auth, &master,
+							&enc_size, &ediv);
+	if (ret < 4)
+		return;
+
+	str = g_strconcat("0x", value, NULL);
+	g_key_file_set_string(key_file, "LongTermKey", "Key", str);
+	g_free(str);
+
+	g_key_file_set_integer(key_file, "LongTermKey", "Authenticated", auth);
+	g_key_file_set_integer(key_file, "LongTermKey", "Master", master);
+	g_key_file_set_integer(key_file, "LongTermKey", "EncSize", enc_size);
+	g_key_file_set_integer(key_file, "LongTermKey", "EDiv", ediv);
+
+	str = g_strconcat("0x", rand_str, NULL);
+	g_key_file_set_string(key_file, "LongTermKey", "Rand", str);
+	g_free(str);
+}
+
+static void convert_profiles_entry(GKeyFile *key_file, void *value)
+{
+	g_strdelimit(value, " ", ';');
+	g_key_file_set_string(key_file, "General", "Services", value);
+}
+
+static void convert_appearances_entry(GKeyFile *key_file, void *value)
+{
+	g_key_file_set_string(key_file, "General", "Appearance", value);
+}
+
+static void convert_entry(char *key, char *value, void *user_data)
+{
+	struct device_converter *converter = user_data;
+	char type = BDADDR_BREDR;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+
+	if (strchr(key, '#')) {
+		key[17] = '\0';
+		type = key[18] - '0';
+	}
+
+	if (bachk(key) != 0)
+		return;
+
+	if (converter->force == FALSE) {
+		struct stat st;
+		int err;
+
+		snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s",
+				converter->address, key);
+
+		err = stat(filename, &st);
+		if (err || !S_ISDIR(st.st_mode))
+			return;
+	}
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+			converter->address, key);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	set_device_type(key_file, type);
+
+	converter->cb(key_file, value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void convert_file(char *file, char *address,
+				void (*cb)(GKeyFile *key_file, void *value),
+				gboolean force)
+{
+	char filename[PATH_MAX];
+	struct device_converter converter;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", address, file);
+
+	converter.address = address;
+	converter.cb = cb;
+	converter.force = force;
+
+	textfile_foreach(filename, convert_entry, &converter);
+}
+
+static gboolean record_has_uuid(const sdp_record_t *rec,
+				const char *profile_uuid)
+{
+	sdp_list_t *pat;
+
+	for (pat = rec->pattern; pat != NULL; pat = pat->next) {
+		char *uuid;
+		int ret;
+
+		uuid = bt_uuid2string(pat->data);
+		if (!uuid)
+			continue;
+
+		ret = strcasecmp(uuid, profile_uuid);
+
+		free(uuid);
+
+		if (ret == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void store_attribute_uuid(GKeyFile *key_file, uint16_t start,
+					uint16_t end, char *att_uuid,
+					uuid_t uuid)
+{
+	char handle[6], uuid_str[33];
+	int i;
+
+	switch (uuid.type) {
+	case SDP_UUID16:
+		sprintf(uuid_str, "%4.4X", uuid.value.uuid16);
+		break;
+	case SDP_UUID32:
+		sprintf(uuid_str, "%8.8X", uuid.value.uuid32);
+		break;
+	case SDP_UUID128:
+		for (i = 0; i < 16; i++)
+			sprintf(uuid_str + (i * 2), "%2.2X",
+					uuid.value.uuid128.data[i]);
+		break;
+	default:
+		uuid_str[0] = '\0';
+	}
+
+	sprintf(handle, "%hu", start);
+	g_key_file_set_string(key_file, handle, "UUID", att_uuid);
+	g_key_file_set_string(key_file, handle, "Value", uuid_str);
+	g_key_file_set_integer(key_file, handle, "EndGroupHandle", end);
+}
+
+static void store_sdp_record(char *local, char *peer, int handle, char *value)
+{
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char handle_str[11];
+	char *data;
+	gsize length = 0;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	sprintf(handle_str, "0x%8.8X", handle);
+	g_key_file_set_string(key_file, "ServiceRecords", handle_str, value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void convert_sdp_entry(char *key, char *value, void *user_data)
+{
+	char *src_addr = user_data;
+	char dst_addr[18];
+	char type = BDADDR_BREDR;
+	int handle, ret;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	struct stat st;
+	sdp_record_t *rec;
+	uuid_t uuid;
+	char *att_uuid, *prim_uuid;
+	uint16_t start = 0, end = 0, psm = 0;
+	int err;
+	char *data;
+	gsize length = 0;
+
+	ret = sscanf(key, "%17s#%hhu#%08X", dst_addr, &type, &handle);
+	if (ret < 3) {
+		ret = sscanf(key, "%17s#%08X", dst_addr, &handle);
+		if (ret < 2)
+			return;
+	}
+
+	if (bachk(dst_addr) != 0)
+		return;
+
+	/* Check if the device directory has been created as records should
+	 * only be converted for known devices */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", src_addr, dst_addr);
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	/* store device records in cache */
+	store_sdp_record(src_addr, dst_addr, handle, value);
+
+	/* Retrieve device record and check if there is an
+	 * attribute entry in it */
+	sdp_uuid16_create(&uuid, ATT_UUID);
+	att_uuid = bt_uuid2string(&uuid);
+
+	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	prim_uuid = bt_uuid2string(&uuid);
+
+	rec = record_from_string(value);
+
+	if (record_has_uuid(rec, att_uuid))
+		goto failed;
+
+	/* TODO: Do this through btd_gatt_database */
+	if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
+		goto failed;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
+								dst_addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	store_attribute_uuid(key_file, start, end, prim_uuid, uuid);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+	g_key_file_free(key_file);
+
+failed:
+	sdp_record_free(rec);
+	free(prim_uuid);
+	free(att_uuid);
+}
+
+static void convert_primaries_entry(char *key, char *value, void *user_data)
+{
+	char *address = user_data;
+	int device_type = -1;
+	uuid_t uuid;
+	char **services, **service, *prim_uuid;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	int ret;
+	uint16_t start, end;
+	char uuid_str[MAX_LEN_UUID_STR + 1];
+	char *data;
+	gsize length = 0;
+
+	if (strchr(key, '#')) {
+		key[17] = '\0';
+		device_type = key[18] - '0';
+	}
+
+	if (bachk(key) != 0)
+		return;
+
+	services = g_strsplit(value, " ", 0);
+	if (services == NULL)
+		return;
+
+	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	prim_uuid = bt_uuid2string(&uuid);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", address,
+									key);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	for (service = services; *service; service++) {
+		ret = sscanf(*service, "%04hX#%04hX#%s", &start, &end,
+								uuid_str);
+		if (ret < 3)
+			continue;
+
+		bt_string2uuid(&uuid, uuid_str);
+		sdp_uuid128_to_uuid(&uuid);
+
+		store_attribute_uuid(key_file, start, end, prim_uuid, uuid);
+	}
+
+	g_strfreev(services);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length == 0)
+		goto end;
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+	g_file_set_contents(filename, data, length, NULL);
+
+	if (device_type < 0)
+		goto end;
+
+	g_free(data);
+	g_key_file_free(key_file);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", address, key);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	set_device_type(key_file, device_type);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+end:
+	g_free(data);
+	free(prim_uuid);
+	g_key_file_free(key_file);
+}
+
+static void convert_ccc_entry(char *key, char *value, void *user_data)
+{
+	char *src_addr = user_data;
+	char dst_addr[18];
+	char type = BDADDR_BREDR;
+	uint16_t handle;
+	int ret, err;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	struct stat st;
+	char group[6];
+	char *data;
+	gsize length = 0;
+
+	ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle);
+	if (ret < 3)
+		return;
+
+	if (bachk(dst_addr) != 0)
+		return;
+
+	/* Check if the device directory has been created as records should
+	 * only be converted for known devices */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", src_addr, dst_addr);
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/ccc", src_addr,
+								dst_addr);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	sprintf(group, "%hu", handle);
+	g_key_file_set_string(key_file, group, "Value", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+static void convert_gatt_entry(char *key, char *value, void *user_data)
+{
+	char *src_addr = user_data;
+	char dst_addr[18];
+	char type = BDADDR_BREDR;
+	uint16_t handle;
+	int ret, err;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	struct stat st;
+	char group[6];
+	char *data;
+	gsize length = 0;
+
+	ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle);
+	if (ret < 3)
+		return;
+
+	if (bachk(dst_addr) != 0)
+		return;
+
+	/* Check if the device directory has been created as records should
+	 * only be converted for known devices */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", src_addr, dst_addr);
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/gatt", src_addr,
+								dst_addr);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	sprintf(group, "%hu", handle);
+	g_key_file_set_string(key_file, group, "Value", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+static void convert_proximity_entry(char *key, char *value, void *user_data)
+{
+	char *src_addr = user_data;
+	char *alert;
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	struct stat st;
+	int err;
+	char *data;
+	gsize length = 0;
+
+	if (!strchr(key, '#'))
+		return;
+
+	key[17] = '\0';
+	alert = &key[18];
+
+	if (bachk(key) != 0)
+		return;
+
+	/* Check if the device directory has been created as records should
+	 * only be converted for known devices */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", src_addr, key);
+
+	err = stat(filename, &st);
+	if (err || !S_ISDIR(st.st_mode))
+		return;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/proximity", src_addr,
+									key);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	g_key_file_set_string(key_file, alert, "Level", value);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+static void convert_device_storage(struct btd_adapter *adapter)
+{
+	char filename[PATH_MAX];
+	char address[18];
+
+	ba2str(&adapter->bdaddr, address);
+
+	/* Convert device's name cache */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/names", address);
+	textfile_foreach(filename, convert_names_entry, address);
+
+	/* Convert aliases */
+	convert_file("aliases", address, convert_aliases_entry, TRUE);
+
+	/* Convert trusts */
+	convert_file("trusts", address, convert_trusts_entry, TRUE);
+
+	/* Convert blocked */
+	convert_file("blocked", address, convert_blocked_entry, TRUE);
+
+	/* Convert profiles */
+	convert_file("profiles", address, convert_profiles_entry, TRUE);
+
+	/* Convert primaries */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/primaries", address);
+	textfile_foreach(filename, convert_primaries_entry, address);
+
+	/* Convert linkkeys */
+	convert_file("linkkeys", address, convert_linkkey_entry, TRUE);
+
+	/* Convert longtermkeys */
+	convert_file("longtermkeys", address, convert_ltk_entry, TRUE);
+
+	/* Convert classes */
+	convert_file("classes", address, convert_classes_entry, FALSE);
+
+	/* Convert device ids */
+	convert_file("did", address, convert_did_entry, FALSE);
+
+	/* Convert sdp */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/sdp", address);
+	textfile_foreach(filename, convert_sdp_entry, address);
+
+	/* Convert ccc */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/ccc", address);
+	textfile_foreach(filename, convert_ccc_entry, address);
+
+	/* Convert appearances */
+	convert_file("appearances", address, convert_appearances_entry, FALSE);
+
+	/* Convert gatt */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/gatt", address);
+	textfile_foreach(filename, convert_gatt_entry, address);
+
+	/* Convert proximity */
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/proximity", address);
+	textfile_foreach(filename, convert_proximity_entry, address);
+}
+
+static void convert_config(struct btd_adapter *adapter, const char *filename,
+							GKeyFile *key_file)
+{
+	char address[18];
+	char str[MAX_NAME_LENGTH + 1];
+	char config_path[PATH_MAX];
+	int timeout;
+	uint8_t mode;
+	char *data;
+	gsize length = 0;
+
+	ba2str(&adapter->bdaddr, address);
+	snprintf(config_path, PATH_MAX, STORAGEDIR "/%s/config", address);
+
+	if (read_pairable_timeout(address, &timeout) == 0)
+		g_key_file_set_integer(key_file, "General",
+						"PairableTimeout", timeout);
+
+	if (read_discoverable_timeout(address, &timeout) == 0)
+		g_key_file_set_integer(key_file, "General",
+						"DiscoverableTimeout", timeout);
+
+	if (read_on_mode(address, str, sizeof(str)) == 0) {
+		mode = get_mode(str);
+		g_key_file_set_boolean(key_file, "General", "Discoverable",
+					mode == MODE_DISCOVERABLE);
+	}
+
+	if (read_local_name(&adapter->bdaddr, str) == 0)
+		g_key_file_set_string(key_file, "General", "Alias", str);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, data, length, NULL);
+	g_free(data);
+}
+
+static void fix_storage(struct btd_adapter *adapter)
+{
+	char filename[PATH_MAX];
+	char address[18];
+	char *converted;
+
+	ba2str(&adapter->bdaddr, address);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/config", address);
+	converted = textfile_get(filename, "converted");
+	if (!converted)
+		return;
+
+	free(converted);
+
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/names", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/aliases", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/trusts", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/blocked", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/profiles", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/primaries", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/linkkeys", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/longtermkeys", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/classes", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/did", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/sdp", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/ccc", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/appearances", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/gatt", address);
+	textfile_del(filename, "converted");
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/proximity", address);
+	textfile_del(filename, "converted");
+}
+
+static void load_config(struct btd_adapter *adapter)
+{
+	GKeyFile *key_file;
+	char filename[PATH_MAX];
+	char address[18];
+	struct stat st;
+	GError *gerr = NULL;
+
+	ba2str(&adapter->bdaddr, address);
+
+	key_file = g_key_file_new();
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
+
+	if (stat(filename, &st) < 0) {
+		convert_config(adapter, filename, key_file);
+		convert_device_storage(adapter);
+	}
+
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	/* Get alias */
+	adapter->stored_alias = g_key_file_get_string(key_file, "General",
+								"Alias", NULL);
+	if (!adapter->stored_alias) {
+		/* fallback */
+		adapter->stored_alias = g_key_file_get_string(key_file,
+						"General", "Name", NULL);
+	}
+
+	/* Get pairable timeout */
+	adapter->pairable_timeout = g_key_file_get_integer(key_file, "General",
+						"PairableTimeout", &gerr);
+	if (gerr) {
+		adapter->pairable_timeout = main_opts.pairto;
+		g_error_free(gerr);
+		gerr = NULL;
+	}
+
+	/* Get discoverable mode */
+	adapter->stored_discoverable = g_key_file_get_boolean(key_file,
+					"General", "Discoverable", &gerr);
+	if (gerr) {
+		adapter->stored_discoverable = false;
+		g_error_free(gerr);
+		gerr = NULL;
+	}
+
+	/* Get discoverable timeout */
+	adapter->discoverable_timeout = g_key_file_get_integer(key_file,
+				"General", "DiscoverableTimeout", &gerr);
+	if (gerr) {
+		adapter->discoverable_timeout = main_opts.discovto;
+		g_error_free(gerr);
+		gerr = NULL;
+	}
+
+	g_key_file_free(key_file);
+}
+
+static struct btd_adapter *btd_adapter_new(uint16_t index)
+{
+	struct btd_adapter *adapter;
+
+	adapter = g_try_new0(struct btd_adapter, 1);
+	if (!adapter)
+		return NULL;
+
+	adapter->dev_id = index;
+	adapter->mgmt = mgmt_ref(mgmt_master);
+	adapter->pincode_requested = false;
+
+	/*
+	 * Setup default configuration values. These are either adapter
+	 * defaults or from a system wide configuration file.
+	 *
+	 * Some value might be overwritten later on by adapter specific
+	 * configuration. This is to make sure that sane defaults are
+	 * always present.
+	 */
+	adapter->system_name = g_strdup(main_opts.name);
+	adapter->major_class = (main_opts.class & 0x001f00) >> 8;
+	adapter->minor_class = (main_opts.class & 0x0000fc) >> 2;
+	adapter->modalias = bt_modalias(main_opts.did_source,
+						main_opts.did_vendor,
+						main_opts.did_product,
+						main_opts.did_version);
+	adapter->discoverable_timeout = main_opts.discovto;
+	adapter->pairable_timeout = main_opts.pairto;
+
+	DBG("System name: %s", adapter->system_name);
+	DBG("Major class: %u", adapter->major_class);
+	DBG("Minor class: %u", adapter->minor_class);
+	DBG("Modalias: %s", adapter->modalias);
+	DBG("Discoverable timeout: %u seconds", adapter->discoverable_timeout);
+	DBG("Pairable timeout: %u seconds", adapter->pairable_timeout);
+
+	adapter->auths = g_queue_new();
+
+	return btd_adapter_ref(adapter);
+}
+
+static void adapter_remove(struct btd_adapter *adapter)
+{
+	GSList *l;
+	struct gatt_db *db;
+
+	DBG("Removing adapter %s", adapter->path);
+
+	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;
+	}
+
+	discovery_cleanup(adapter);
+
+	g_slist_free(adapter->connect_list);
+	adapter->connect_list = NULL;
+
+	for (l = adapter->devices; l; l = l->next)
+		device_remove(l->data, FALSE);
+
+	g_slist_free(adapter->devices);
+	adapter->devices = NULL;
+
+	unload_drivers(adapter);
+
+	db = btd_gatt_database_get_db(adapter->database);
+	gatt_db_unregister(db, adapter->db_id);
+	adapter->db_id = 0;
+
+	btd_gatt_database_destroy(adapter->database);
+	adapter->database = NULL;
+
+	btd_advertising_manager_destroy(adapter->adv_manager);
+	adapter->adv_manager = NULL;
+
+	g_slist_free(adapter->pin_callbacks);
+	adapter->pin_callbacks = NULL;
+
+	g_slist_free(adapter->msd_callbacks);
+	adapter->msd_callbacks = NULL;
+}
+
+const char *adapter_get_path(struct btd_adapter *adapter)
+{
+	if (!adapter)
+		return NULL;
+
+	return adapter->path;
+}
+
+const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter)
+{
+	return &adapter->bdaddr;
+}
+
+static gboolean confirm_name_timeout(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	btd_error(adapter->dev_id, "Confirm name timed out for hci%u",
+							adapter->dev_id);
+
+	adapter->confirm_name_timeout = 0;
+
+	mgmt_cancel(adapter->mgmt, adapter->confirm_name_id);
+	adapter->confirm_name_id = 0;
+
+	return FALSE;
+}
+
+static void confirm_name_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to confirm name for hci%u: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+	}
+
+	adapter->confirm_name_id = 0;
+
+	g_source_remove(adapter->confirm_name_timeout);
+	adapter->confirm_name_timeout = 0;
+
+	DBG("Confirm name complete for hci%u", adapter->dev_id);
+}
+
+static void confirm_name(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+					uint8_t bdaddr_type, bool name_known)
+{
+	struct mgmt_cp_confirm_name cp;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%d bdaddr %s name_known %u", adapter->dev_id, addr,
+								name_known);
+
+	/*
+	 * If the kernel does not answer the confirm name command with
+	 * a command complete or command status in time, this might
+	 * race against another device found event that also requires
+	 * to confirm the name. If there is a pending command, just
+	 * cancel it to be safe here.
+	 */
+	if (adapter->confirm_name_id > 0) {
+		btd_warn(adapter->dev_id,
+				"Found pending confirm name for hci%u",
+							adapter->dev_id);
+		mgmt_cancel(adapter->mgmt, adapter->confirm_name_id);
+	}
+
+	if (adapter->confirm_name_timeout > 0) {
+		g_source_remove(adapter->confirm_name_timeout);
+		adapter->confirm_name_timeout = 0;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+	cp.name_known = name_known;
+
+	adapter->confirm_name_id = mgmt_reply(adapter->mgmt,
+					MGMT_OP_CONFIRM_NAME,
+					adapter->dev_id, sizeof(cp), &cp,
+					confirm_name_complete, adapter, NULL);
+
+	if (adapter->confirm_name_id == 0) {
+		btd_error(adapter->dev_id, "Failed to confirm name for hci%u",
+							adapter->dev_id);
+		return;
+	}
+
+	/*
+	 * This timeout handling is needed since the kernel is stupid
+	 * and forgets to send a command complete response. However in
+	 * case of failures it does send a command status.
+	 */
+	adapter->confirm_name_timeout = g_timeout_add_seconds(2,
+						confirm_name_timeout, adapter);
+}
+
+static void adapter_msd_notify(struct btd_adapter *adapter,
+							struct btd_device *dev,
+							GSList *msd_list)
+{
+	GSList *cb_l, *cb_next;
+	GSList *msd_l, *msd_next;
+
+	for (cb_l = adapter->msd_callbacks; cb_l != NULL; cb_l = cb_next) {
+		btd_msd_cb_t cb = cb_l->data;
+
+		cb_next = g_slist_next(cb_l);
+
+		for (msd_l = msd_list; msd_l != NULL; msd_l = msd_next) {
+			const struct eir_msd *msd = msd_l->data;
+
+			msd_next = g_slist_next(msd_l);
+
+			cb(adapter, dev, msd->company, msd->data,
+								msd->data_len);
+		}
+	}
+}
+
+static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
+								int8_t rssi)
+{
+	GSList *l, *m;
+	bool got_match = false;
+
+	for (l = discovery_filter; l != NULL && got_match != true;
+							l = g_slist_next(l)) {
+		struct watch_client *client = l->data;
+		struct discovery_filter *item = client->discovery_filter;
+
+		/*
+		 * If one of currently running scans is regular scan, then
+		 * return all devices as matches
+		 */
+		if (!item) {
+			got_match = true;
+			continue;
+		}
+
+		/* if someone started discovery with empty uuids, he wants all
+		 * devices in given proximity.
+		 */
+		if (!item->uuids)
+			got_match = true;
+		else {
+			for (m = item->uuids; m != NULL && got_match != true;
+							m = g_slist_next(m)) {
+				/* m->data contains string representation of
+				 * uuid.
+				 */
+				if (g_slist_find_custom(eir_data->services,
+							m->data,
+							g_strcmp) != NULL)
+					got_match = true;
+			}
+		}
+
+		if (got_match) {
+			/* we have service match, check proximity */
+			if (item->rssi == DISTANCE_VAL_INVALID ||
+			    item->rssi <= rssi ||
+			    item->pathloss == DISTANCE_VAL_INVALID ||
+			    (eir_data->tx_power != 127 &&
+			     eir_data->tx_power - rssi <= item->pathloss))
+				return true;
+
+			got_match = false;
+		}
+	}
+
+	return got_match;
+}
+
+static void update_found_devices(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					uint8_t bdaddr_type, int8_t rssi,
+					bool confirm, bool legacy,
+					bool not_connectable,
+					const uint8_t *data, uint8_t data_len)
+{
+	struct btd_device *dev;
+	struct eir_data eir_data;
+	bool name_known, discoverable;
+	char addr[18];
+
+	memset(&eir_data, 0, sizeof(eir_data));
+	eir_parse(&eir_data, data, data_len);
+
+	if (bdaddr_type == BDADDR_BREDR)
+		discoverable = true;
+	else
+		discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
+
+	ba2str(bdaddr, addr);
+
+	dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
+	if (!dev) {
+		/*
+		 * If no client has requested discovery or the device is
+		 * not marked as discoverable, then do not create new
+		 * device objects.
+		 */
+		if (!adapter->discovery_list || !discoverable) {
+			eir_data_free(&eir_data);
+			return;
+		}
+
+		dev = adapter_create_device(adapter, bdaddr, bdaddr_type);
+	}
+
+	if (!dev) {
+		btd_error(adapter->dev_id,
+			"Unable to create object for found device %s", addr);
+		eir_data_free(&eir_data);
+		return;
+	}
+
+	device_update_last_seen(dev, bdaddr_type);
+
+	/*
+	 * FIXME: We need to check for non-zero flags first because
+	 * older kernels send separate adv_ind and scan_rsp. Newer
+	 * kernels send them merged, so once we know which mgmt version
+	 * supports this we can make the non-zero check conditional.
+	 */
+	if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
+					!(eir_data.flags & EIR_BREDR_UNSUP))
+		device_set_bredr_support(dev);
+
+	if (eir_data.name != NULL && eir_data.name_complete)
+		device_store_cached_name(dev, eir_data.name);
+
+	/*
+	 * If no client has requested discovery, then only update
+	 * already paired devices (skip temporary ones).
+	 */
+	if (device_is_temporary(dev) && !adapter->discovery_list) {
+		eir_data_free(&eir_data);
+		return;
+	}
+
+	if (adapter->filtered_discovery &&
+	    !is_filter_match(adapter->discovery_list, &eir_data, rssi)) {
+		eir_data_free(&eir_data);
+		return;
+	}
+
+	device_set_legacy(dev, legacy);
+
+	if (adapter->filtered_discovery)
+		device_set_rssi_with_delta(dev, rssi, 0);
+	else
+		device_set_rssi(dev, rssi);
+
+	if (eir_data.tx_power != 127)
+		device_set_tx_power(dev, eir_data.tx_power);
+
+	if (eir_data.appearance != 0)
+		device_set_appearance(dev, eir_data.appearance);
+
+	/* Report an unknown name to the kernel even if there is a short name
+	 * known, but still update the name with the known short name. */
+	name_known = device_name_known(dev);
+
+	if (eir_data.name && (eir_data.name_complete || !name_known))
+		btd_device_device_set_name(dev, eir_data.name);
+
+	if (eir_data.class != 0)
+		device_set_class(dev, eir_data.class);
+
+	if (eir_data.did_source || eir_data.did_vendor ||
+			eir_data.did_product || eir_data.did_version)
+		btd_device_set_pnpid(dev, eir_data.did_source,
+							eir_data.did_vendor,
+							eir_data.did_product,
+							eir_data.did_version);
+
+	device_add_eir_uuids(dev, eir_data.services);
+
+	if (eir_data.msd_list) {
+		device_set_manufacturer_data(dev, eir_data.msd_list);
+		adapter_msd_notify(adapter, dev, eir_data.msd_list);
+	}
+
+	if (eir_data.sd_list)
+		device_set_service_data(dev, eir_data.sd_list);
+
+	eir_data_free(&eir_data);
+
+	/*
+	 * Only if at least one client has requested discovery, maintain
+	 * list of found devices and name confirming for legacy devices.
+	 * Otherwise, this is an event from passive discovery and we
+	 * should check if the device needs connecting to.
+	 */
+	if (!adapter->discovery_list)
+		goto connect_le;
+
+	if (g_slist_find(adapter->discovery_found, dev))
+		return;
+
+	if (confirm)
+		confirm_name(adapter, bdaddr, bdaddr_type, name_known);
+
+	adapter->discovery_found = g_slist_prepend(adapter->discovery_found,
+									dev);
+
+	return;
+
+connect_le:
+	/* Ignore non-connectable events */
+	if (not_connectable)
+		return;
+
+	/*
+	 * If we're in the process of stopping passive scanning and
+	 * connecting another (or maybe even the same) LE device just
+	 * ignore this one.
+	 */
+	if (adapter->connect_le)
+		return;
+
+	/*
+	 * If kernel background scan is used then the kernel is
+	 * responsible for connecting.
+	 */
+	if (kernel_conn_control)
+		return;
+
+	/*
+	 * If this is an LE device that's not connected and part of the
+	 * connect_list stop passive scanning so that a connection
+	 * attempt to it can be made
+	 */
+	if (bdaddr_type != BDADDR_BREDR && !btd_device_is_connected(dev) &&
+				g_slist_find(adapter->connect_list, dev)) {
+		adapter->connect_le = dev;
+		stop_passive_scanning(adapter);
+	}
+}
+
+static void device_found_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_found *ev = param;
+	struct btd_adapter *adapter = user_data;
+	const uint8_t *eir;
+	uint16_t eir_len;
+	uint32_t flags;
+	bool confirm_name;
+	bool legacy;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id,
+			"Too short device found event (%u bytes)", length);
+		return;
+	}
+
+	eir_len = btohs(ev->eir_len);
+	if (length != sizeof(*ev) + eir_len) {
+		btd_error(adapter->dev_id,
+				"Device found event size mismatch (%u != %zu)",
+					length, sizeof(*ev) + eir_len);
+		return;
+	}
+
+	if (eir_len == 0)
+		eir = NULL;
+	else
+		eir = ev->eir;
+
+	flags = btohl(ev->flags);
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+			index, addr, ev->rssi, flags, eir_len);
+
+	confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
+	legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING);
+
+	update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
+					ev->rssi, confirm_name, legacy,
+					flags & MGMT_DEV_FOUND_NOT_CONNECTABLE,
+					eir, eir_len);
+}
+
+struct agent *adapter_get_agent(struct btd_adapter *adapter)
+{
+	return agent_get(NULL);
+}
+
+static void adapter_remove_connection(struct btd_adapter *adapter,
+						struct btd_device *device,
+						uint8_t bdaddr_type)
+{
+	DBG("");
+
+	if (!g_slist_find(adapter->connections, device)) {
+		btd_error(adapter->dev_id, "No matching connection for device");
+		return;
+	}
+
+	device_remove_connection(device, bdaddr_type);
+
+	if (device_is_authenticating(device))
+		device_cancel_authentication(device, TRUE);
+
+	/* If another bearer is still connected */
+	if (btd_device_is_connected(device))
+		return;
+
+	adapter->connections = g_slist_remove(adapter->connections, device);
+
+	if (device_is_temporary(device) && !device_is_retrying(device)) {
+		const char *path = device_get_path(device);
+
+		DBG("Removing temporary device %s", path);
+		btd_adapter_remove_device(adapter, device);
+	}
+}
+
+static void adapter_stop(struct btd_adapter *adapter)
+{
+	/* check pending requests */
+	reply_pending_requests(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;
+
+		client = adapter->discovery_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);
+	}
+
+	adapter->filtered_discovery = false;
+	adapter->no_scan_restart_delay = false;
+	g_free(adapter->current_discovery_filter);
+	adapter->current_discovery_filter = NULL;
+
+	adapter->discovering = false;
+
+	while (adapter->connections) {
+		struct btd_device *device = adapter->connections->data;
+		uint8_t addr_type = btd_device_get_bdaddr_type(device);
+
+		adapter_remove_connection(adapter, device, BDADDR_BREDR);
+		if (addr_type != BDADDR_BREDR)
+			adapter_remove_connection(adapter, device, addr_type);
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+					ADAPTER_INTERFACE, "Discovering");
+
+	if (adapter->dev_class) {
+		/* the kernel should reset the class of device when powering
+		 * down, but it does not. So force it here ... */
+		adapter->dev_class = 0;
+		g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Class");
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "Powered");
+
+	DBG("adapter %s has been disabled", adapter->path);
+}
+
+int btd_register_adapter_driver(struct btd_adapter_driver *driver)
+{
+	adapter_drivers = g_slist_append(adapter_drivers, driver);
+
+	if (driver->probe == NULL)
+		return 0;
+
+	adapter_foreach(probe_driver, driver);
+
+	return 0;
+}
+
+static void unload_driver(struct btd_adapter *adapter, gpointer data)
+{
+	struct btd_adapter_driver *driver = data;
+
+	if (driver->remove)
+		driver->remove(adapter);
+
+	adapter->drivers = g_slist_remove(adapter->drivers, data);
+}
+
+void btd_unregister_adapter_driver(struct btd_adapter_driver *driver)
+{
+	adapter_drivers = g_slist_remove(adapter_drivers, driver);
+
+	adapter_foreach(unload_driver, driver);
+}
+
+static void agent_auth_cb(struct agent *agent, DBusError *derr,
+							void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	struct service_auth *auth = g_queue_pop_head(adapter->auths);
+
+	if (!auth) {
+		DBG("No pending authorization");
+		return;
+	}
+
+	auth->cb(derr, auth->user_data);
+
+	if (auth->agent)
+		agent_unref(auth->agent);
+
+	g_free(auth);
+
+	/* Stop processing if queue is empty */
+	if (g_queue_is_empty(adapter->auths)) {
+		if (adapter->auth_idle_id > 0)
+			g_source_remove(adapter->auth_idle_id);
+		return;
+	}
+
+	if (adapter->auth_idle_id > 0)
+		return;
+
+	adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
+}
+
+static gboolean process_auth_queue(gpointer user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	DBusError err;
+
+	adapter->auth_idle_id = 0;
+
+	dbus_error_init(&err);
+	dbus_set_error_const(&err, ERROR_INTERFACE ".Rejected", NULL);
+
+	while (!g_queue_is_empty(adapter->auths)) {
+		struct service_auth *auth = adapter->auths->head->data;
+		struct btd_device *device = auth->device;
+		const char *dev_path;
+
+		/* Wait services to be resolved before asking authorization */
+		if (auth->svc_id > 0)
+			return FALSE;
+
+		if (device_is_trusted(device) == TRUE) {
+			auth->cb(NULL, auth->user_data);
+			goto next;
+		}
+
+		/* If agent is set authorization is already ongoing */
+		if (auth->agent)
+			return FALSE;
+
+		auth->agent = agent_get(NULL);
+		if (auth->agent == NULL) {
+			btd_warn(adapter->dev_id,
+					"Authentication attempt without agent");
+			auth->cb(&err, auth->user_data);
+			goto next;
+		}
+
+		dev_path = device_get_path(device);
+
+		if (agent_authorize_service(auth->agent, dev_path, auth->uuid,
+					agent_auth_cb, adapter, NULL) < 0) {
+			auth->cb(&err, auth->user_data);
+			goto next;
+		}
+
+		break;
+
+next:
+		if (auth->agent)
+			agent_unref(auth->agent);
+
+		g_free(auth);
+
+		g_queue_pop_head(adapter->auths);
+	}
+
+	dbus_error_free(&err);
+
+	return FALSE;
+}
+
+static void svc_complete(struct btd_device *dev, int err, void *user_data)
+{
+	struct service_auth *auth = user_data;
+	struct btd_adapter *adapter = auth->adapter;
+
+	auth->svc_id = 0;
+
+	if (adapter->auth_idle_id != 0)
+		return;
+
+	adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
+}
+
+static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
+					const char *uuid, service_auth_cb cb,
+					void *user_data)
+{
+	struct service_auth *auth;
+	struct btd_device *device;
+	static guint id = 0;
+
+	device = btd_adapter_find_device(adapter, dst, BDADDR_BREDR);
+	if (!device)
+		return 0;
+
+	if (device_is_disconnecting(device)) {
+		DBG("Authorization request while disconnecting");
+		return 0;
+	}
+
+	/* Device connected? */
+	if (!g_slist_find(adapter->connections, device))
+		btd_error(adapter->dev_id,
+			"Authorization request for non-connected device!?");
+
+	auth = g_try_new0(struct service_auth, 1);
+	if (!auth)
+		return 0;
+
+	auth->cb = cb;
+	auth->user_data = user_data;
+	auth->uuid = uuid;
+	auth->device = device;
+	auth->adapter = adapter;
+	auth->id = ++id;
+	auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+
+	g_queue_push_tail(adapter->auths, auth);
+
+	return auth->id;
+}
+
+guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
+					const char *uuid, service_auth_cb cb,
+					void *user_data)
+{
+	struct btd_adapter *adapter;
+	GSList *l;
+
+	if (bacmp(src, BDADDR_ANY) != 0) {
+		adapter = adapter_find(src);
+		if (!adapter)
+			return 0;
+
+		return adapter_authorize(adapter, dst, uuid, cb, user_data);
+	}
+
+	for (l = adapters; l != NULL; l = g_slist_next(l)) {
+		guint id;
+
+		adapter = l->data;
+
+		id = adapter_authorize(adapter, dst, uuid, cb, user_data);
+		if (id != 0)
+			return id;
+	}
+
+	return 0;
+}
+
+static struct service_auth *find_authorization(guint id)
+{
+	GSList *l;
+	GList *l2;
+
+	for (l = adapters; l != NULL; l = g_slist_next(l)) {
+		struct btd_adapter *adapter = l->data;
+
+		for (l2 = adapter->auths->head; l2 != NULL; l2 = l2->next) {
+			struct service_auth *auth = l2->data;
+
+			if (auth->id == id)
+				return auth;
+		}
+	}
+
+	return NULL;
+}
+
+int btd_cancel_authorization(guint id)
+{
+	struct service_auth *auth;
+
+	auth = find_authorization(id);
+	if (auth == NULL)
+		return -EPERM;
+
+	if (auth->svc_id > 0)
+		device_remove_svc_complete_callback(auth->device,
+								auth->svc_id);
+
+	g_queue_remove(auth->adapter->auths, auth);
+
+	if (auth->agent) {
+		agent_cancel(auth->agent);
+		agent_unref(auth->agent);
+	}
+
+	g_free(auth);
+
+	return 0;
+}
+
+int btd_adapter_restore_powered(struct btd_adapter *adapter)
+{
+	if (adapter->current_settings & MGMT_SETTING_POWERED)
+		return 0;
+
+	set_mode(adapter, MGMT_OP_SET_POWERED, 0x01);
+
+	return 0;
+}
+
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+							btd_adapter_pin_cb_t cb)
+{
+	adapter->pin_callbacks = g_slist_prepend(adapter->pin_callbacks, cb);
+}
+
+void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
+							btd_adapter_pin_cb_t cb)
+{
+	adapter->pin_callbacks = g_slist_remove(adapter->pin_callbacks, cb);
+}
+
+void btd_adapter_unregister_msd_cb(struct btd_adapter *adapter,
+							btd_msd_cb_t cb)
+{
+	adapter->msd_callbacks = g_slist_remove(adapter->msd_callbacks, cb);
+}
+
+void btd_adapter_register_msd_cb(struct btd_adapter *adapter,
+							btd_msd_cb_t cb)
+{
+	adapter->msd_callbacks = g_slist_prepend(adapter->msd_callbacks, cb);
+}
+
+int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
+							gboolean enable)
+{
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return -EINVAL;
+
+	set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, enable ? 0x01 : 0x00);
+
+	return 0;
+}
+
+int btd_adapter_read_clock(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+				int which, int timeout, uint32_t *clock,
+				uint16_t *accuracy)
+{
+	if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+		return -EINVAL;
+
+	return -ENOSYS;
+}
+
+int btd_adapter_remove_bonding(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct mgmt_cp_unpair_device cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+	cp.disconnect = 1;
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_UNPAIR_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+static void pincode_reply_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	/* If the MGMT_OP_PIN_CODE_REPLY command is acknowledged, move the
+	 * starting time to that point. This give a better sense of time
+	 * evaluating the pincode. */
+	device_bonding_restart_timer(device);
+}
+
+int btd_adapter_pincode_reply(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					const char *pin, size_t pin_len)
+{
+	struct btd_device *device;
+	unsigned int id;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u addr %s pinlen %zu", adapter->dev_id, addr, pin_len);
+
+	if (pin == NULL) {
+		struct mgmt_cp_pin_code_neg_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = BDADDR_BREDR;
+
+		id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+					adapter->dev_id, sizeof(cp), &cp,
+					NULL, NULL, NULL);
+	} else {
+		struct mgmt_cp_pin_code_reply cp;
+
+		if (pin_len > 16)
+			return -EINVAL;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = BDADDR_BREDR;
+		cp.pin_len = pin_len;
+		memcpy(cp.pin_code, pin, pin_len);
+
+		/* Since a pincode was requested, update the starting time to
+		 * the point where the pincode is provided. */
+		device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
+		device_bonding_restart_timer(device);
+
+		id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
+					adapter->dev_id, sizeof(cp), &cp,
+					pincode_reply_complete, device, NULL);
+	}
+
+	if (id == 0)
+		return -EIO;
+
+	return 0;
+}
+
+int btd_adapter_confirm_reply(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+				gboolean success)
+{
+	struct mgmt_cp_user_confirm_reply cp;
+	uint16_t opcode;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u addr %s success %d", adapter->dev_id, addr, success);
+
+	if (success)
+		opcode = MGMT_OP_USER_CONFIRM_REPLY;
+	else
+		opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+
+	if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp,
+							NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_user_confirm_request *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char addr[18];
+	int err;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id,
+				"Too small user confirm request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr,
+							ev->confirm_hint);
+	device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", addr);
+		return;
+	}
+
+	err = device_confirm_passkey(device, btohl(ev->value),
+							ev->confirm_hint);
+	if (err < 0) {
+		btd_error(adapter->dev_id,
+				"device_confirm_passkey: %s", strerror(-err));
+		btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
+							ev->addr.type, FALSE);
+	}
+}
+
+int btd_adapter_passkey_reply(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+				uint32_t passkey)
+{
+	unsigned int id;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u addr %s passkey %06u", adapter->dev_id, addr, passkey);
+
+	if (passkey == INVALID_PASSKEY) {
+		struct mgmt_cp_user_passkey_neg_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = bdaddr_type;
+
+		id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+					adapter->dev_id, sizeof(cp), &cp,
+					NULL, NULL, NULL);
+	} else {
+		struct mgmt_cp_user_passkey_reply cp;
+
+		memset(&cp, 0, sizeof(cp));
+		bacpy(&cp.addr.bdaddr, bdaddr);
+		cp.addr.type = bdaddr_type;
+		cp.passkey = htobl(passkey);
+
+		id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_REPLY,
+					adapter->dev_id, sizeof(cp), &cp,
+					NULL, NULL, NULL);
+	}
+
+	if (id == 0)
+		return -EIO;
+
+	return 0;
+}
+
+static void user_passkey_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_user_passkey_request *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char addr[18];
+	int err;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small passkey request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u %s", index, addr);
+
+	device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", addr);
+		return;
+	}
+
+	err = device_request_passkey(device);
+	if (err < 0) {
+		btd_error(adapter->dev_id,
+				"device_request_passkey: %s", strerror(-err));
+		btd_adapter_passkey_reply(adapter, &ev->addr.bdaddr,
+					ev->addr.type, INVALID_PASSKEY);
+	}
+}
+
+static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_passkey_notify *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	uint32_t passkey;
+	char addr[18];
+	int err;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small passkey notify event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u %s", index, addr);
+
+	device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", addr);
+		return;
+	}
+
+	passkey = get_le32(&ev->passkey);
+
+	DBG("passkey %06u entered %u", passkey, ev->entered);
+
+	err = device_notify_passkey(device, passkey, ev->entered);
+	if (err < 0)
+		btd_error(adapter->dev_id,
+				"device_notify_passkey: %s", strerror(-err));
+}
+
+struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new(
+						struct btd_adapter *adapter)
+{
+	struct btd_adapter_pin_cb_iter *iter =
+				g_new0(struct btd_adapter_pin_cb_iter, 1);
+
+	iter->it = adapter->pin_callbacks;
+	iter->attempt = 1;
+
+	return iter;
+}
+
+void btd_adapter_pin_cb_iter_free(struct btd_adapter_pin_cb_iter *iter)
+{
+	g_free(iter);
+}
+
+bool btd_adapter_pin_cb_iter_end(struct btd_adapter_pin_cb_iter *iter)
+{
+	return iter->it == NULL && iter->attempt == 0;
+}
+
+static ssize_t btd_adapter_pin_cb_iter_next(
+					struct btd_adapter_pin_cb_iter *iter,
+					struct btd_adapter *adapter,
+					struct btd_device *device,
+					char *pin_buf, bool *display)
+{
+	btd_adapter_pin_cb_t cb;
+	ssize_t ret;
+
+	while (iter->it != NULL) {
+		cb = iter->it->data;
+		ret = cb(adapter, device, pin_buf, display, iter->attempt);
+		iter->attempt++;
+		if (ret > 0)
+			return ret;
+		iter->attempt = 1;
+		iter->it = g_slist_next(iter->it);
+	}
+	iter->attempt = 0;
+
+	return 0;
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	bool display = false;
+	char pin[17];
+	ssize_t pinlen;
+	char addr[18];
+	int err;
+	struct btd_adapter_pin_cb_iter *iter;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small PIN code request event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+
+	DBG("hci%u %s", adapter->dev_id, addr);
+
+	device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", addr);
+		return;
+	}
+
+	/* Flag the request of a pincode to allow a bonding retry. */
+	adapter->pincode_requested = true;
+
+	memset(pin, 0, sizeof(pin));
+
+	iter = device_bonding_iter(device);
+	if (iter == NULL)
+		pinlen = 0;
+	else
+		pinlen = btd_adapter_pin_cb_iter_next(iter, adapter, device,
+								pin, &display);
+
+	if (pinlen > 0 && (!ev->secure || pinlen == 16)) {
+		if (display && device_is_bonding(device, NULL)) {
+			err = device_notify_pincode(device, ev->secure, pin);
+			if (err < 0) {
+				btd_error(adapter->dev_id,
+						"device_notify_pin: %s",
+							strerror(-err));
+				btd_adapter_pincode_reply(adapter,
+							&ev->addr.bdaddr,
+							NULL, 0);
+			}
+		} else {
+			btd_adapter_pincode_reply(adapter, &ev->addr.bdaddr,
+								pin, pinlen);
+		}
+		return;
+	}
+
+	err = device_request_pincode(device, ev->secure);
+	if (err < 0) {
+		btd_error(adapter->dev_id, "device_request_pin: %s",
+							strerror(-err));
+		btd_adapter_pincode_reply(adapter, &ev->addr.bdaddr, NULL, 0);
+	}
+}
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+							uint8_t addr_type)
+{
+	struct mgmt_addr_info cp;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u bdaddr %s type %u", adapter->dev_id, addr, addr_type);
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+	cp.type = addr_type;
+
+	if (mgmt_reply(adapter->mgmt, MGMT_OP_CANCEL_PAIR_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+static void check_oob_bonding_complete(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr, uint8_t status)
+{
+	if (!adapter->oob_handler || !adapter->oob_handler->bonding_cb)
+		return;
+
+	if (bacmp(bdaddr, &adapter->oob_handler->remote_addr) != 0)
+		return;
+
+	adapter->oob_handler->bonding_cb(adapter, bdaddr, status,
+					adapter->oob_handler->user_data);
+
+	g_free(adapter->oob_handler);
+	adapter->oob_handler = NULL;
+}
+
+static void bonding_complete(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					uint8_t addr_type, uint8_t status)
+{
+	struct btd_device *device;
+
+	if (status == 0)
+		device = btd_adapter_get_device(adapter, bdaddr, addr_type);
+	else
+		device = btd_adapter_find_device(adapter, bdaddr, addr_type);
+
+	if (device != NULL)
+		device_bonding_complete(device, addr_type, status);
+
+	resume_discovery(adapter);
+
+	check_oob_bonding_complete(adapter, bdaddr, status);
+}
+
+/* bonding_attempt_complete() handles the end of a "bonding attempt" checking if
+ * it should begin a new attempt or complete the bonding.
+ */
+static void bonding_attempt_complete(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					uint8_t addr_type, uint8_t status)
+{
+	struct btd_device *device;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u bdaddr %s type %u status 0x%x", adapter->dev_id, addr,
+							addr_type, status);
+
+	if (status == 0)
+		device = btd_adapter_get_device(adapter, bdaddr, addr_type);
+	else
+		device = btd_adapter_find_device(adapter, bdaddr, addr_type);
+
+	if (status == MGMT_STATUS_AUTH_FAILED && adapter->pincode_requested) {
+		/* On faliure, issue a bonding_retry if possible. */
+		if (device != NULL) {
+			if (device_bonding_attempt_retry(device) == 0)
+				return;
+		}
+	}
+
+	/* Ignore disconnects during retry. */
+	if (status == MGMT_STATUS_DISCONNECTED &&
+					device && device_is_retrying(device))
+		return;
+
+	/* In any other case, finish the bonding. */
+	bonding_complete(adapter, bdaddr, addr_type, status);
+}
+
+struct pair_device_data {
+	struct btd_adapter *adapter;
+	bdaddr_t bdaddr;
+	uint8_t addr_type;
+};
+
+static void free_pair_device_data(void *user_data)
+{
+	struct pair_device_data *data = user_data;
+
+	g_free(data);
+}
+
+static gboolean pair_device_timeout(gpointer user_data)
+{
+	struct pair_device_data *data = user_data;
+	struct btd_adapter *adapter = data->adapter;
+
+	btd_error(adapter->dev_id, "Pair device timed out for hci%u",
+							adapter->dev_id);
+
+	adapter->pair_device_timeout = 0;
+
+	adapter_cancel_bonding(adapter, &data->bdaddr, data->addr_type);
+
+	return FALSE;
+}
+
+static void pair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_pair_device *rp = param;
+	struct pair_device_data *data = user_data;
+	struct btd_adapter *adapter = data->adapter;
+
+	DBG("%s (0x%02x)", mgmt_errstr(status), status);
+
+	adapter->pair_device_id = 0;
+
+	if (adapter->pair_device_timeout > 0) {
+		g_source_remove(adapter->pair_device_timeout);
+		adapter->pair_device_timeout = 0;
+	}
+
+	/* Workaround for a kernel bug
+	 *
+	 * Broken kernels may reply to device pairing command with command
+	 * status instead of command complete event e.g. if adapter was not
+	 * powered.
+	 */
+	if (status != MGMT_STATUS_SUCCESS && length < sizeof(*rp)) {
+		btd_error(adapter->dev_id, "Pair device failed: %s (0x%02x)",
+						mgmt_errstr(status), status);
+
+		bonding_attempt_complete(adapter, &data->bdaddr,
+						data->addr_type, status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id, "Too small pair device response");
+		return;
+	}
+
+	bonding_attempt_complete(adapter, &rp->addr.bdaddr, rp->addr.type,
+									status);
+}
+
+int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+					uint8_t addr_type, uint8_t io_cap)
+{
+	if (adapter->pair_device_id > 0) {
+		btd_error(adapter->dev_id,
+			"Unable pair since another pairing is in progress");
+		return -EBUSY;
+	}
+
+	suspend_discovery(adapter);
+
+	return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap);
+}
+
+/* Starts a new bonding attempt in a fresh new bonding_req or a retried one. */
+int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+					uint8_t addr_type, uint8_t io_cap)
+{
+	struct mgmt_cp_pair_device cp;
+	char addr[18];
+	struct pair_device_data *data;
+	unsigned int id;
+
+	ba2str(bdaddr, addr);
+	DBG("hci%u bdaddr %s type %d io_cap 0x%02x",
+				adapter->dev_id, addr, addr_type, io_cap);
+
+	/* Reset the pincode_requested flag for a new bonding attempt. */
+	adapter->pincode_requested = false;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = addr_type;
+	cp.io_cap = io_cap;
+
+	data = g_new0(struct pair_device_data, 1);
+	data->adapter = adapter;
+	bacpy(&data->bdaddr, bdaddr);
+	data->addr_type = addr_type;
+
+	id = mgmt_send(adapter->mgmt, MGMT_OP_PAIR_DEVICE,
+				adapter->dev_id, sizeof(cp), &cp,
+				pair_device_complete, data,
+				free_pair_device_data);
+
+	if (id == 0) {
+		btd_error(adapter->dev_id, "Failed to pair %s for hci%u",
+							addr, adapter->dev_id);
+		free_pair_device_data(data);
+		return -EIO;
+	}
+
+	adapter->pair_device_id = id;
+
+	/* Due to a bug in the kernel it is possible that a LE pairing
+	 * request never times out. Therefore, add a timer to clean up
+	 * if no response arrives
+	 */
+	adapter->pair_device_timeout = g_timeout_add_seconds(BONDING_TIMEOUT,
+						pair_device_timeout, data);
+
+	return 0;
+}
+
+static void disconnect_notify(struct btd_device *dev, uint8_t reason)
+{
+	GSList *l;
+
+	for (l = disconnect_list; l; l = g_slist_next(l)) {
+		btd_disconnect_cb disconnect_cb = l->data;
+		disconnect_cb(dev, reason);
+	}
+}
+
+static void dev_disconnected(struct btd_adapter *adapter,
+					const struct mgmt_addr_info *addr,
+					uint8_t reason)
+{
+	struct btd_device *device;
+	char dst[18];
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("Device %s disconnected, reason %u", dst, reason);
+
+	device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type);
+	if (device) {
+		adapter_remove_connection(adapter, device, addr->type);
+		disconnect_notify(device, reason);
+	}
+
+	bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
+						MGMT_STATUS_DISCONNECTED);
+}
+
+void btd_add_disconnect_cb(btd_disconnect_cb func)
+{
+	disconnect_list = g_slist_append(disconnect_list, func);
+}
+
+void btd_remove_disconnect_cb(btd_disconnect_cb func)
+{
+	disconnect_list = g_slist_remove(disconnect_list, func);
+}
+
+static void disconnect_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_disconnect *rp = param;
+	struct btd_adapter *adapter = user_data;
+
+	if (status == MGMT_STATUS_NOT_CONNECTED) {
+		btd_warn(adapter->dev_id,
+				"Disconnecting failed: already disconnected");
+	} else if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to disconnect device: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Too small device disconnect response");
+		return;
+	}
+
+	dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST);
+}
+
+int btd_adapter_disconnect_device(struct btd_adapter *adapter,
+						const bdaddr_t *bdaddr,
+						uint8_t bdaddr_type)
+
+{
+	struct mgmt_cp_disconnect cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	cp.addr.type = bdaddr_type;
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT,
+				adapter->dev_id, sizeof(cp), &cp,
+				disconnect_complete, adapter, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+static void auth_failed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_auth_failed *ev = param;
+	struct btd_adapter *adapter = user_data;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small auth failed mgmt event");
+		return;
+	}
+
+	bonding_attempt_complete(adapter, &ev->addr.bdaddr, ev->addr.type,
+								ev->status);
+}
+
+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;
+	gsize length = 0;
+	char key_str[33];
+	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);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, "LinkKey", "Key", key_str);
+
+	g_key_file_set_integer(key_file, "LinkKey", "Type", type);
+	g_key_file_set_integer(key_file, "LinkKey", "PINLength", pin_length);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static void new_link_key_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_link_key *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small new link key event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("hci%u new key for %s type %u pin_len %u store_hint %u",
+		adapter->dev_id, dst, ev->key.type, ev->key.pin_len,
+		ev->store_hint);
+
+	if (ev->key.pin_len > 16) {
+		btd_error(adapter->dev_id,
+				"Invalid PIN length (%u) in new_key event",
+							ev->key.pin_len);
+		return;
+	}
+
+	device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", dst);
+		return;
+	}
+
+	if (ev->store_hint) {
+		const struct mgmt_link_key_info *key = &ev->key;
+
+		store_link_key(adapter, device, key->val, key->type,
+								key->pin_len);
+
+		device_set_bonded(device, BDADDR_BREDR);
+	}
+
+	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)
+{
+	const char *group = master ? "LongTermKey" : "SlaveLongTermKey";
+	char adapter_addr[18];
+	char device_addr[18];
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	char *str;
+	int i;
+
+	if (master != 0x00 && master != 0x01) {
+		error("Unsupported LTK type %u", master);
+		return;
+	}
+
+	ba2str(local, adapter_addr);
+	ba2str(peer, device_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+								device_addr);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	/* Old files may contain this so remove it in case it exists */
+	g_key_file_remove_key(key_file, "LongTermKey", "Master", NULL);
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, group, "Key", key_str);
+
+	g_key_file_set_integer(key_file, group, "Authenticated",
+							authenticated);
+	g_key_file_set_integer(key_file, group, "EncSize", enc_size);
+
+	g_key_file_set_integer(key_file, group, "EDiv", ediv);
+	g_key_file_set_uint64(key_file, group, "Rand", rand);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static void new_long_term_key_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_long_term_key *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	bool persistent;
+	char dst[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small long term key event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("hci%u new LTK for %s type %u enc_size %u",
+		adapter->dev_id, dst, ev->key.type, ev->key.enc_size);
+
+	device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", dst);
+		return;
+	}
+
+	/*
+	 * Some older kernel versions set store_hint for long term keys
+	 * from resolvable and unresolvable random addresses, but there
+	 * is no point in storing these. Next time around the device
+	 * address will be invalid.
+	 *
+	 * So only for identity addresses (public and static random) use
+	 * the store_hint as an indication if the long term key should
+	 * be persistently stored.
+	 *
+	 */
+	if (addr->type == BDADDR_LE_RANDOM &&
+				(addr->bdaddr.b[5] & 0xc0) != 0xc0)
+		persistent = false;
+	else
+		persistent = !!ev->store_hint;
+
+	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;
+
+		ediv = le16_to_cpu(key->ediv);
+		rand = le64_to_cpu(key->rand);
+
+		store_longtermkey(bdaddr, &key->addr.bdaddr,
+					key->addr.type, key->val, key->master,
+					key->type, key->enc_size, ediv, rand);
+
+		device_set_bonded(device, addr->type);
+	}
+
+	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)
+{
+	const char *group;
+	char adapter_addr[18];
+	char device_addr[18];
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char key_str[33];
+	gsize length = 0;
+	gboolean auth;
+	char *str;
+	int i;
+
+	switch (type) {
+	case 0x00:
+		group = "LocalSignatureKey";
+		auth = FALSE;
+		break;
+	case 0x01:
+		group = "RemoteSignatureKey";
+		auth = FALSE;
+		break;
+	case 0x02:
+		group = "LocalSignatureKey";
+		auth = TRUE;
+		break;
+	case 0x03:
+		group = "RemoteSignatureKey";
+		auth = TRUE;
+		break;
+	default:
+		warn("Unsupported CSRK type %u", type);
+		return;
+	}
+
+	ba2str(local, adapter_addr);
+	ba2str(peer, device_addr);
+
+	snprintf(filename, sizeof(filename), STORAGEDIR "/%s/%s/info",
+						adapter_addr, device_addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	for (i = 0; i < 16; i++)
+		sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, group, "Key", key_str);
+	g_key_file_set_integer(key_file, group, "Counter", counter);
+	g_key_file_set_boolean(key_file, group, "Authenticated", auth);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_csrk *ev = param;
+	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];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small CSRK event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+
+	DBG("hci%u new CSRK for %s type %u", adapter->dev_id, dst,
+								ev->key.type);
+
+	device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", dst);
+		return;
+	}
+
+	if (!ev->store_hint)
+		return;
+
+	store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
+								key->type);
+
+	btd_device_set_temporary(device, false);
+}
+
+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;
+	char *store_data;
+	char str[33];
+	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);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	for (i = 0; i < 16; i++)
+		sprintf(str + (i * 2), "%2.2X", key[i]);
+
+	g_key_file_set_string(key_file, "IdentityResolvingKey", "Key", str);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	store_data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, store_data, length, NULL);
+	g_free(store_data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_irk *ev = param;
+	const struct mgmt_addr_info *addr = &ev->key.addr;
+	const struct mgmt_irk_info *irk = &ev->key;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device, *duplicate;
+	bool persistent;
+	char dst[18], rpa[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small New IRK event");
+		return;
+	}
+
+	ba2str(&addr->bdaddr, dst);
+	ba2str(&ev->rpa, rpa);
+
+	DBG("hci%u new IRK for %s RPA %s", adapter->dev_id, dst, rpa);
+
+	if (bacmp(&ev->rpa, BDADDR_ANY)) {
+		device = btd_adapter_get_device(adapter, &ev->rpa,
+							BDADDR_LE_RANDOM);
+		duplicate = btd_adapter_find_device(adapter, &addr->bdaddr,
+								addr->type);
+		if (duplicate == device)
+			duplicate = NULL;
+	} else {
+		device = btd_adapter_get_device(adapter, &addr->bdaddr,
+								addr->type);
+		duplicate = NULL;
+	}
+
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", dst);
+		return;
+	}
+
+	device_update_addr(device, &addr->bdaddr, addr->type);
+
+	if (duplicate)
+		device_merge_duplicate(device, duplicate);
+
+	persistent = !!ev->store_hint;
+	if (!persistent)
+		return;
+
+	store_irk(adapter, &addr->bdaddr, addr->type, irk->val);
+
+	btd_device_set_temporary(device, false);
+}
+
+static void store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer,
+				uint8_t bdaddr_type, uint16_t min_interval,
+				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);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	g_key_file_set_integer(key_file, "ConnectionParameters",
+						"MinInterval", min_interval);
+	g_key_file_set_integer(key_file, "ConnectionParameters",
+						"MaxInterval", max_interval);
+	g_key_file_set_integer(key_file, "ConnectionParameters",
+						"Latency", latency);
+	g_key_file_set_integer(key_file, "ConnectionParameters",
+						"Timeout", timeout);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	store_data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, store_data, length, NULL);
+	g_free(store_data);
+
+	g_key_file_free(key_file);
+}
+
+static void new_conn_param(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_conn_param *ev = param;
+	struct btd_adapter *adapter = user_data;
+	uint16_t min, max, latency, timeout;
+	struct btd_device *dev;
+	char dst[18];
+
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id,
+				"Too small New Connection Parameter event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, dst);
+
+	min = btohs(ev->min_interval);
+	max = btohs(ev->max_interval);
+	latency = btohs(ev->latency);
+	timeout = btohs(ev->timeout);
+
+	DBG("hci%u %s (%u) min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x",
+		adapter->dev_id, dst, ev->addr.type, min, max, latency, timeout);
+
+	dev = btd_adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+	if (!dev) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", dst);
+		return;
+	}
+
+	if (!ev->store_hint)
+		return;
+
+	store_conn_param(adapter, &ev->addr.bdaddr, ev->addr.type,
+					ev->min_interval, ev->max_interval,
+					ev->latency, ev->timeout);
+}
+
+int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
+{
+	struct mgmt_cp_set_io_capability cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.io_capability = io_cap;
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IO_CAPABILITY,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					uint8_t *hash, uint8_t *randomizer)
+{
+	struct mgmt_cp_add_remote_oob_data cp;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%d bdaddr %s", adapter->dev_id, addr);
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	memcpy(cp.hash192, hash, 16);
+
+	if (randomizer)
+		memcpy(cp.rand192, randomizer, 16);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+							const bdaddr_t *bdaddr)
+{
+	struct mgmt_cp_remove_remote_oob_data cp;
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+	DBG("hci%d bdaddr %s", adapter->dev_id, addr);
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+bool btd_adapter_ssp_enabled(struct btd_adapter *adapter)
+{
+	if (adapter->current_settings & MGMT_SETTING_SSP)
+		return true;
+
+	return false;
+}
+
+void btd_adapter_set_oob_handler(struct btd_adapter *adapter,
+						struct oob_handler *handler)
+{
+	adapter->oob_handler = handler;
+}
+
+gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter)
+{
+	return adapter->oob_handler != NULL;
+}
+
+static void read_local_oob_data_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_local_oob_data *rp = param;
+	struct btd_adapter *adapter = user_data;
+	const uint8_t *hash, *randomizer;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Read local OOB data failed: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		hash = NULL;
+		randomizer = NULL;
+	} else if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Too small read local OOB data response");
+		return;
+	} else {
+		hash = rp->hash192;
+		randomizer = rp->rand192;
+	}
+
+	if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb)
+		return;
+
+	adapter->oob_handler->read_local_cb(adapter, hash, randomizer,
+					adapter->oob_handler->user_data);
+
+	g_free(adapter->oob_handler);
+	adapter->oob_handler = NULL;
+}
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter)
+{
+	DBG("hci%u", adapter->dev_id);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_READ_LOCAL_OOB_DATA,
+			adapter->dev_id, 0, NULL, read_local_oob_data_complete,
+			adapter, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+void btd_adapter_for_each_device(struct btd_adapter *adapter,
+			void (*cb)(struct btd_device *device, void *data),
+			void *data)
+{
+	g_slist_foreach(adapter->devices, (GFunc) cb, data);
+}
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+	struct btd_adapter *adapter = (struct btd_adapter *) a;
+	const bdaddr_t *bdaddr = b;
+
+	return bacmp(&adapter->bdaddr, bdaddr);
+}
+
+static int adapter_id_cmp(gconstpointer a, gconstpointer b)
+{
+	struct btd_adapter *adapter = (struct btd_adapter *) a;
+	uint16_t id = GPOINTER_TO_UINT(b);
+
+	return adapter->dev_id == id ? 0 : -1;
+}
+
+struct btd_adapter *adapter_find(const bdaddr_t *sba)
+{
+	GSList *match;
+
+	match = g_slist_find_custom(adapters, sba, adapter_cmp);
+	if (!match)
+		return NULL;
+
+	return match->data;
+}
+
+struct btd_adapter *adapter_find_by_id(int id)
+{
+	GSList *match;
+
+	match = g_slist_find_custom(adapters, GINT_TO_POINTER(id),
+							adapter_id_cmp);
+	if (!match)
+		return NULL;
+
+	return match->data;
+}
+
+void adapter_foreach(adapter_cb func, gpointer user_data)
+{
+	g_slist_foreach(adapters, (GFunc) func, user_data);
+}
+
+static int set_did(struct btd_adapter *adapter, uint16_t vendor,
+			uint16_t product, uint16_t version, uint16_t source)
+{
+	struct mgmt_cp_set_device_id cp;
+
+	DBG("hci%u source %x vendor %x product %x version %x",
+			adapter->dev_id, source, vendor, product, version);
+
+	memset(&cp, 0, sizeof(cp));
+
+	cp.source = htobs(source);
+	cp.vendor = htobs(vendor);
+	cp.product = htobs(product);
+	cp.version = htobs(version);
+
+	if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_ID,
+				adapter->dev_id, sizeof(cp), &cp,
+				NULL, NULL, NULL) > 0)
+		return 0;
+
+	return -EIO;
+}
+
+static void services_modified(struct gatt_db_attribute *attrib, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	g_dbus_emit_property_changed(dbus_conn, adapter->path,
+						ADAPTER_INTERFACE, "UUIDs");
+}
+
+static int adapter_register(struct btd_adapter *adapter)
+{
+	struct agent *agent;
+	struct gatt_db *db;
+
+	if (powering_down)
+		return -EBUSY;
+
+	adapter->path = g_strdup_printf("/org/bluez/hci%d", adapter->dev_id);
+
+	if (!g_dbus_register_interface(dbus_conn,
+					adapter->path, ADAPTER_INTERFACE,
+					adapter_methods, NULL,
+					adapter_properties, adapter,
+					adapter_free)) {
+		btd_error(adapter->dev_id,
+				"Adapter interface init failed on path %s",
+							adapter->path);
+		g_free(adapter->path);
+		adapter->path = NULL;
+		return -EINVAL;
+	}
+
+	if (adapters == NULL)
+		adapter->is_default = true;
+
+	adapters = g_slist_append(adapters, adapter);
+
+	agent = agent_get(NULL);
+	if (agent) {
+		uint8_t io_cap = agent_get_io_capability(agent);
+		adapter_set_io_capability(adapter, io_cap);
+		agent_unref(agent);
+	}
+
+	adapter->database = btd_gatt_database_new(adapter);
+	if (!adapter->database) {
+		btd_error(adapter->dev_id,
+				"Failed to create GATT database for adapter");
+		adapters = g_slist_remove(adapters, 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_advertising_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,
+							services_modified,
+							adapter, NULL);
+
+	load_config(adapter);
+	fix_storage(adapter);
+	load_drivers(adapter);
+	btd_profile_foreach(probe_profile, adapter);
+	clear_blocked(adapter);
+	load_devices(adapter);
+
+	/* retrieve the active connections: address the scenario where
+	 * the are active connections before the daemon've started */
+	if (adapter->current_settings & MGMT_SETTING_POWERED)
+		load_connections(adapter);
+
+	adapter->initialized = TRUE;
+
+	if (main_opts.did_source) {
+		/* DeviceID record is added by sdpd-server before any other
+		 * record is registered. */
+		adapter_service_insert(adapter, sdp_record_find(0x10000));
+		set_did(adapter, main_opts.did_vendor, main_opts.did_product,
+				main_opts.did_version, main_opts.did_source);
+	}
+
+	DBG("Adapter %s registered", adapter->path);
+
+	return 0;
+}
+
+static int adapter_unregister(struct btd_adapter *adapter)
+{
+	DBG("Unregister path: %s", adapter->path);
+
+	adapters = g_slist_remove(adapters, adapter);
+
+	if (adapter->is_default && adapters != NULL) {
+		struct btd_adapter *new_default;
+
+		new_default = adapter_find_by_id(hci_get_route(NULL));
+		if (new_default == NULL)
+			new_default = adapters->data;
+
+		new_default->is_default = true;
+	}
+
+	adapter_list = g_list_remove(adapter_list, adapter);
+
+	adapter_remove(adapter);
+	btd_adapter_unref(adapter);
+
+	return 0;
+}
+
+static void disconnected_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_disconnected *ev = param;
+	struct btd_adapter *adapter = user_data;
+	uint8_t reason;
+
+	if (length < sizeof(struct mgmt_addr_info)) {
+		btd_error(adapter->dev_id,
+				"Too small device disconnected event");
+		return;
+	}
+
+	if (length < sizeof(*ev))
+		reason = MGMT_DEV_DISCONN_UNKNOWN;
+	else
+		reason = ev->reason;
+
+	dev_disconnected(adapter, &ev->addr, reason);
+}
+
+static void connected_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_connected *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	struct eir_data eir_data;
+	uint16_t eir_len;
+	char addr[18];
+	bool name_known;
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small device connected event");
+		return;
+	}
+
+	eir_len = btohs(ev->eir_len);
+	if (length < sizeof(*ev) + eir_len) {
+		btd_error(adapter->dev_id, "Too small device connected event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+
+	DBG("hci%u device %s connected eir_len %u", index, addr, eir_len);
+
+	device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (!device) {
+		btd_error(adapter->dev_id,
+				"Unable to get device object for %s", addr);
+		return;
+	}
+
+	memset(&eir_data, 0, sizeof(eir_data));
+	if (eir_len > 0)
+		eir_parse(&eir_data, ev->eir, eir_len);
+
+	if (eir_data.class != 0)
+		device_set_class(device, eir_data.class);
+
+	adapter_add_connection(adapter, device, ev->addr.type);
+
+	name_known = device_name_known(device);
+
+	if (eir_data.name && (eir_data.name_complete || !name_known)) {
+		device_store_cached_name(device, eir_data.name);
+		btd_device_device_set_name(device, eir_data.name);
+	}
+
+	if (eir_data.msd_list)
+		adapter_msd_notify(adapter, device, eir_data.msd_list);
+
+	eir_data_free(&eir_data);
+}
+
+static void device_blocked_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_blocked *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small device blocked event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u %s blocked", index, addr);
+
+	device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (device)
+		device_block(device, TRUE);
+}
+
+static void device_unblocked_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_unblocked *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small device unblocked event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	DBG("hci%u %s unblocked", index, addr);
+
+	device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (device)
+		device_unblock(device, FALSE, TRUE);
+}
+
+static void conn_fail_notify(struct btd_device *dev, uint8_t status)
+{
+	GSList *l;
+
+	for (l = conn_fail_list; l; l = g_slist_next(l)) {
+		btd_conn_fail_cb conn_fail_cb = l->data;
+		conn_fail_cb(dev, status);
+	}
+}
+
+void btd_add_conn_fail_cb(btd_conn_fail_cb func)
+{
+	conn_fail_list = g_slist_append(conn_fail_list, func);
+}
+
+void btd_remove_conn_fail_cb(btd_conn_fail_cb func)
+{
+	conn_fail_list = g_slist_remove(conn_fail_list, func);
+}
+
+static void connect_failed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_connect_failed *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small connect failed event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+
+	DBG("hci%u %s status %u", index, addr, ev->status);
+
+	device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (device) {
+		conn_fail_notify(device, ev->status);
+
+		/* If the device is in a bonding process cancel any auth request
+		 * sent to the agent before proceeding, but keep the bonding
+		 * request structure. */
+		if (device_is_bonding(device, NULL))
+			device_cancel_authentication(device, FALSE);
+	}
+
+	/* In the case of security mode 3 devices */
+	bonding_attempt_complete(adapter, &ev->addr.bdaddr, ev->addr.type,
+								ev->status);
+
+	/* If the device is scheduled to retry the bonding wait until the retry
+	 * happens. In other case, proceed with cancel the bondig.
+	 */
+	if (device && device_is_bonding(device, NULL)
+					&& !device_is_retrying(device)) {
+		device_cancel_authentication(device, TRUE);
+		device_bonding_failed(device, ev->status);
+	}
+
+	/* In the case the bonding was canceled or did exists, remove the device
+	 * when it is temporary. */
+	if (device && !device_is_bonding(device, NULL)
+						&& device_is_temporary(device))
+		btd_adapter_remove_device(adapter, device);
+}
+
+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);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+								device_addr);
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	if (type == BDADDR_BREDR) {
+		g_key_file_remove_group(key_file, "LinkKey", NULL);
+	} else {
+		g_key_file_remove_group(key_file, "LongTermKey", NULL);
+		g_key_file_remove_group(key_file, "LocalSignatureKey", NULL);
+		g_key_file_remove_group(key_file, "RemoteSignatureKey", NULL);
+		g_key_file_remove_group(key_file, "IdentityResolvingKey", NULL);
+	}
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
+static void unpaired_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_device_unpaired *ev = param;
+	struct btd_adapter *adapter = user_data;
+	struct btd_device *device;
+	char addr[18];
+
+	if (length < sizeof(*ev)) {
+		btd_error(adapter->dev_id, "Too small device unpaired event");
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+
+	DBG("hci%u addr %s", index, addr);
+
+	device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+								ev->addr.type);
+	if (!device) {
+		btd_warn(adapter->dev_id,
+			"No device object for unpaired device %s", addr);
+		return;
+	}
+
+	remove_keys(adapter, device, ev->addr.type);
+	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 void read_info_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+	const struct mgmt_rp_read_info *rp = param;
+	uint32_t missing_settings;
+	int err;
+
+	DBG("index %u status 0x%02x", adapter->dev_id, status);
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		btd_error(adapter->dev_id,
+				"Failed to read info for index %u: %s (0x%02x)",
+				adapter->dev_id, mgmt_errstr(status), status);
+		goto failed;
+	}
+
+	if (length < sizeof(*rp)) {
+		btd_error(adapter->dev_id,
+				"Too small read info complete response");
+		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.
+	 *
+	 * 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->supported_settings = btohl(rp->supported_settings);
+	adapter->current_settings = btohl(rp->current_settings);
+
+	clear_uuids(adapter);
+	clear_devices(adapter);
+
+	missing_settings = adapter->current_settings ^
+						adapter->supported_settings;
+
+	switch (main_opts.mode) {
+	case BT_MODE_DUAL:
+		if (missing_settings & MGMT_SETTING_SSP)
+			set_mode(adapter, MGMT_OP_SET_SSP, 0x01);
+		if (missing_settings & MGMT_SETTING_LE)
+			set_mode(adapter, MGMT_OP_SET_LE, 0x01);
+		if (missing_settings & MGMT_SETTING_BREDR)
+			set_mode(adapter, MGMT_OP_SET_BREDR, 0x01);
+		break;
+	case BT_MODE_BREDR:
+		if (!(adapter->supported_settings & MGMT_SETTING_BREDR)) {
+			btd_error(adapter->dev_id,
+				"Ignoring adapter withouth BR/EDR support");
+			goto failed;
+		}
+
+		if (missing_settings & MGMT_SETTING_SSP)
+			set_mode(adapter, MGMT_OP_SET_SSP, 0x01);
+		if (missing_settings & MGMT_SETTING_BREDR)
+			set_mode(adapter, MGMT_OP_SET_BREDR, 0x01);
+		if (adapter->current_settings & MGMT_SETTING_LE)
+			set_mode(adapter, MGMT_OP_SET_LE, 0x00);
+		break;
+	case BT_MODE_LE:
+		if (!(adapter->supported_settings & MGMT_SETTING_LE)) {
+			btd_error(adapter->dev_id,
+				"Ignoring adapter withouth LE support");
+			goto failed;
+		}
+
+		if (missing_settings & MGMT_SETTING_LE)
+			set_mode(adapter, MGMT_OP_SET_LE, 0x01);
+		if (adapter->current_settings & MGMT_SETTING_BREDR)
+			set_mode(adapter, MGMT_OP_SET_BREDR, 0x00);
+		break;
+	}
+
+	if (missing_settings & MGMT_SETTING_SECURE_CONN)
+		set_mode(adapter, MGMT_OP_SET_SECURE_CONN, 0x01);
+
+	if (main_opts.fast_conn &&
+			(missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
+		set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
+
+	err = adapter_register(adapter);
+	if (err < 0) {
+		btd_error(adapter->dev_id, "Unable to register new adapter");
+		goto failed;
+	}
+
+	/*
+	 * Register all event notification handlers for controller.
+	 *
+	 * The handlers are registered after a succcesful read of the
+	 * controller info. From now on they can track updates and
+	 * notifications.
+	 */
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_SETTINGS, adapter->dev_id,
+					new_settings_callback, adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED,
+						adapter->dev_id,
+						dev_class_changed_callback,
+						adapter, NULL);
+	mgmt_register(adapter->mgmt, MGMT_EV_LOCAL_NAME_CHANGED,
+						adapter->dev_id,
+						local_name_changed_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_DISCOVERING,
+						adapter->dev_id,
+						discovering_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_FOUND,
+						adapter->dev_id,
+						device_found_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_DISCONNECTED,
+						adapter->dev_id,
+						disconnected_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_CONNECTED,
+						adapter->dev_id,
+						connected_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_CONNECT_FAILED,
+						adapter->dev_id,
+						connect_failed_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_UNPAIRED,
+						adapter->dev_id,
+						unpaired_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_AUTH_FAILED,
+						adapter->dev_id,
+						auth_failed_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_LINK_KEY,
+						adapter->dev_id,
+						new_link_key_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_LONG_TERM_KEY,
+						adapter->dev_id,
+						new_long_term_key_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_CSRK,
+						adapter->dev_id,
+						new_csrk_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_IRK,
+						adapter->dev_id,
+						new_irk_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_CONN_PARAM,
+						adapter->dev_id,
+						new_conn_param,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_BLOCKED,
+						adapter->dev_id,
+						device_blocked_callback,
+						adapter, NULL);
+	mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_UNBLOCKED,
+						adapter->dev_id,
+						device_unblocked_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_PIN_CODE_REQUEST,
+						adapter->dev_id,
+						pin_code_request_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_USER_CONFIRM_REQUEST,
+						adapter->dev_id,
+						user_confirm_request_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_USER_PASSKEY_REQUEST,
+						adapter->dev_id,
+						user_passkey_request_callback,
+						adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_PASSKEY_NOTIFY,
+						adapter->dev_id,
+						user_passkey_notify_callback,
+						adapter, NULL);
+
+	set_dev_class(adapter);
+
+	set_name(adapter, btd_adapter_get_name(adapter));
+
+	if (!(adapter->current_settings & MGMT_SETTING_BONDABLE))
+		set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x01);
+
+	if (!kernel_conn_control)
+		set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x01);
+	else if (adapter->current_settings & MGMT_SETTING_CONNECTABLE)
+		set_mode(adapter, MGMT_OP_SET_CONNECTABLE, 0x00);
+
+	if (adapter->stored_discoverable && !adapter->discoverable_timeout)
+		set_discoverable(adapter, 0x01, 0);
+
+	if (adapter->current_settings & MGMT_SETTING_POWERED)
+		adapter_start(adapter);
+
+	return;
+
+failed:
+	/*
+	 * Remove adapter from list in case of a failure.
+	 *
+	 * Leaving an adapter structure around for a controller that can
+	 * not be initilized makes no sense at the moment.
+	 *
+	 * This is a simplification to avoid constant checks if the
+	 * adapter is ready to do anything.
+	 */
+	adapter_list = g_list_remove(adapter_list, adapter);
+
+	btd_adapter_unref(adapter);
+}
+
+static void index_added(uint16_t index, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct btd_adapter *adapter;
+
+	DBG("index %u", index);
+
+	adapter = btd_adapter_lookup(index);
+	if (adapter) {
+		btd_warn(adapter->dev_id,
+			"Ignoring index added for an already existing adapter");
+		return;
+	}
+
+	adapter = btd_adapter_new(index);
+	if (!adapter) {
+		btd_error(adapter->dev_id,
+			"Unable to create new adapter for index %u", index);
+		return;
+	}
+
+	/*
+	 * Protect against potential two executions of read controller info.
+	 *
+	 * In case the start of the daemon and the action of adding a new
+	 * controller coincide this function might be called twice.
+	 *
+	 * To avoid the double execution of reading the controller info,
+	 * add the adapter already to the list. If an adapter is already
+	 * present, the second notification will cause a warning. If the
+	 * command fails the adapter is removed from the list again.
+	 */
+	adapter_list = g_list_append(adapter_list, adapter);
+
+	DBG("sending read info command for index %u", index);
+
+	if (mgmt_send(mgmt_master, MGMT_OP_READ_INFO, index, 0, NULL,
+					read_info_complete, adapter, NULL) > 0)
+		return;
+
+	btd_error(adapter->dev_id,
+			"Failed to read controller info for index %u", index);
+
+	adapter_list = g_list_remove(adapter_list, adapter);
+
+	btd_adapter_unref(adapter);
+}
+
+static void index_removed(uint16_t index, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct btd_adapter *adapter;
+
+	DBG("index %u", index);
+
+	adapter = btd_adapter_lookup(index);
+	if (!adapter) {
+		warn("Ignoring index removal for a non-existent adapter");
+		return;
+	}
+
+	adapter_unregister(adapter);
+}
+
+static void read_index_list_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_index_list *rp = param;
+	uint16_t num;
+	int i;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to read index list: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of read index list response");
+		return;
+	}
+
+	num = btohs(rp->num_controllers);
+
+	DBG("Number of controllers: %d", num);
+
+	if (num * sizeof(uint16_t) + sizeof(*rp) != length) {
+		error("Incorrect packet size for index list response");
+		return;
+	}
+
+	for (i = 0; i < num; i++) {
+		uint16_t index;
+
+		index = btohs(rp->index[i]);
+
+		DBG("Found index %u", index);
+
+		/*
+		 * Pretend to be index added event notification.
+		 *
+		 * It is safe to just trigger the procedure for index
+		 * added notification. It does check against itself.
+		 */
+		index_added(index, 0, NULL, NULL);
+	}
+}
+
+static void read_commands_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_commands *rp = param;
+	uint16_t num_commands, num_events;
+	const uint16_t *opcode;
+	size_t expected_len;
+	int i;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to read supported commands: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of read commands response");
+		return;
+	}
+
+	num_commands = btohs(rp->num_commands);
+	num_events = btohs(rp->num_events);
+
+	DBG("Number of commands: %d", num_commands);
+	DBG("Number of events: %d", num_events);
+
+	expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) +
+						num_events * sizeof(uint16_t);
+
+	if (length < expected_len) {
+		error("Too small reply for supported commands: (%u != %zu)",
+							length, expected_len);
+		return;
+	}
+
+	opcode = rp->opcodes;
+
+	for (i = 0; i < num_commands; i++) {
+		uint16_t op = get_le16(opcode++);
+
+		if (op == MGMT_OP_ADD_DEVICE) {
+			DBG("enabling kernel-side connection control");
+			kernel_conn_control = true;
+		}
+	}
+}
+
+static void read_version_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_version *rp = param;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to read version information: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of read version response");
+		return;
+	}
+
+	mgmt_version = rp->version;
+	mgmt_revision = btohs(rp->revision);
+
+	info("Bluetooth management interface %u.%u initialized",
+						mgmt_version, mgmt_revision);
+
+	if (mgmt_version < 1) {
+		error("Version 1.0 or later of management interface required");
+		abort();
+	}
+
+	DBG("sending read supported commands command");
+
+	/*
+	 * It is irrelevant if this command succeeds or fails. In case of
+	 * failure safe settings are assumed.
+	 */
+	mgmt_send(mgmt_master, MGMT_OP_READ_COMMANDS,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_commands_complete, NULL, NULL);
+
+	mgmt_register(mgmt_master, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+						index_added, NULL, NULL);
+	mgmt_register(mgmt_master, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+						index_removed, NULL, NULL);
+
+	DBG("sending read index list command");
+
+	if (mgmt_send(mgmt_master, MGMT_OP_READ_INDEX_LIST,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_index_list_complete, NULL, NULL) > 0)
+		return;
+
+	error("Failed to read controller index list");
+}
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	info("%s%s", prefix, str);
+}
+
+int adapter_init(void)
+{
+	dbus_conn = btd_get_dbus_connection();
+
+	mgmt_master = mgmt_new_default();
+	if (!mgmt_master) {
+		error("Failed to access management interface");
+		return -EIO;
+	}
+
+	if (getenv("MGMT_DEBUG"))
+		mgmt_set_debug(mgmt_master, mgmt_debug, "mgmt: ", NULL);
+
+	DBG("sending read version command");
+
+	if (mgmt_send(mgmt_master, MGMT_OP_READ_VERSION,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_version_complete, NULL, NULL) > 0)
+		return 0;
+
+	error("Failed to read management version information");
+
+	return -EIO;
+}
+
+void adapter_cleanup(void)
+{
+	g_list_free(adapter_list);
+
+	while (adapters) {
+		struct btd_adapter *adapter = adapters->data;
+
+		adapter_remove(adapter);
+		adapters = g_slist_remove(adapters, adapter);
+		btd_adapter_unref(adapter);
+	}
+
+	/*
+	 * In case there is another reference active, clear out
+	 * registered handlers for index added and index removed.
+	 *
+	 * This is just an extra precaution to be safe, and in
+	 * reality should not make a difference.
+	 */
+	mgmt_unregister_index(mgmt_master, MGMT_INDEX_NONE);
+
+	/*
+	 * In case there is another reference active, cancel
+	 * all pending global commands.
+	 *
+	 * This is just an extra precaution to avoid callbacks
+	 * that potentially then could leak memory or access
+	 * an invalid structure.
+	 */
+	mgmt_cancel_index(mgmt_master, MGMT_INDEX_NONE);
+
+	mgmt_unref(mgmt_master);
+	mgmt_master = NULL;
+
+	dbus_conn = NULL;
+}
+
+void adapter_shutdown(void)
+{
+	GList *list;
+
+	DBG("");
+
+	powering_down = true;
+
+	for (list = g_list_first(adapter_list); list;
+						list = g_list_next(list)) {
+		struct btd_adapter *adapter = list->data;
+
+		if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+			continue;
+
+		set_mode(adapter, MGMT_OP_SET_POWERED, 0x00);
+
+		adapter_remaining++;
+	}
+
+	if (!adapter_remaining)
+		btd_exit();
+}
+
+/*
+ * Check if workaround for broken ATT server socket behavior is needed
+ * where we need to connect an ATT client socket before pairing to get
+ * early access to the ATT channel.
+ */
+bool btd_le_connect_before_pairing(void)
+{
+	if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 4))
+		return true;
+
+	return false;
+}
diff --git a/bluez/repo/src/adapter.h b/bluez/repo/src/adapter.h
new file mode 100644
index 0000000..0c95f5d
--- /dev/null
+++ b/bluez/repo/src/adapter.h
@@ -0,0 +1,227 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+#include <stdbool.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define MAX_NAME_LENGTH		248
+
+/* Invalid SSP passkey value used to indicate negative replies */
+#define INVALID_PASSKEY		0xffffffff
+
+struct btd_adapter;
+struct btd_device;
+
+struct btd_adapter *btd_adapter_get_default(void);
+bool btd_adapter_is_default(struct btd_adapter *adapter);
+uint16_t btd_adapter_get_index(struct btd_adapter *adapter);
+
+typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data);
+
+typedef void (*oob_read_local_cb_t) (struct btd_adapter *adapter,
+					const uint8_t *hash,
+					const uint8_t *randomizer,
+					void *user_data);
+typedef void (*oob_bonding_cb_t) (struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr, uint8_t status,
+					void *user_data);
+
+struct oob_handler {
+	oob_read_local_cb_t read_local_cb;
+	oob_bonding_cb_t bonding_cb;
+	bdaddr_t remote_addr;
+	void *user_data;
+};
+
+int adapter_init(void);
+void adapter_cleanup(void);
+void adapter_shutdown(void);
+
+typedef void (*btd_disconnect_cb) (struct btd_device *device, uint8_t reason);
+void btd_add_disconnect_cb(btd_disconnect_cb func);
+void btd_remove_disconnect_cb(btd_disconnect_cb func);
+
+typedef void (*btd_conn_fail_cb) (struct btd_device *device, uint8_t status);
+void btd_add_conn_fail_cb(btd_conn_fail_cb func);
+void btd_remove_conn_fail_cb(btd_conn_fail_cb func);
+
+struct btd_adapter *adapter_find(const bdaddr_t *sba);
+struct btd_adapter *adapter_find_by_id(int id);
+void adapter_foreach(adapter_cb func, gpointer user_data);
+
+bool btd_adapter_get_pairable(struct btd_adapter *adapter);
+bool btd_adapter_get_powered(struct btd_adapter *adapter);
+bool btd_adapter_get_connectable(struct btd_adapter *adapter);
+
+struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter);
+
+uint32_t btd_adapter_get_class(struct btd_adapter *adapter);
+const char *btd_adapter_get_name(struct btd_adapter *adapter);
+void btd_adapter_remove_device(struct btd_adapter *adapter,
+				struct btd_device *dev);
+struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
+					const bdaddr_t *addr,
+					uint8_t addr_type);
+sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter);
+
+struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
+							const bdaddr_t *dst,
+							uint8_t dst_type);
+
+const char *adapter_get_path(struct btd_adapter *adapter);
+const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter);
+int adapter_set_name(struct btd_adapter *adapter, const char *name);
+
+int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec);
+void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle);
+
+struct agent *adapter_get_agent(struct btd_adapter *adapter);
+
+struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter);
+void btd_adapter_unref(struct btd_adapter *adapter);
+
+void btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
+							uint8_t minor);
+
+struct btd_adapter_driver {
+	const char *name;
+	int (*probe) (struct btd_adapter *adapter);
+	void (*remove) (struct btd_adapter *adapter);
+};
+
+typedef void (*service_auth_cb) (DBusError *derr, void *user_data);
+
+void adapter_add_profile(struct btd_adapter *adapter, gpointer p);
+void adapter_remove_profile(struct btd_adapter *adapter, gpointer p);
+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);
+int btd_cancel_authorization(guint id);
+
+int btd_adapter_restore_powered(struct btd_adapter *adapter);
+
+typedef ssize_t (*btd_adapter_pin_cb_t) (struct btd_adapter *adapter,
+			struct btd_device *dev, char *out, bool *display,
+							unsigned int attempt);
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+						btd_adapter_pin_cb_t cb);
+void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
+						btd_adapter_pin_cb_t cb);
+
+struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new(
+						struct btd_adapter *adapter);
+void btd_adapter_pin_cb_iter_free(struct btd_adapter_pin_cb_iter *iter);
+bool btd_adapter_pin_cb_iter_end(struct btd_adapter_pin_cb_iter *iter);
+
+typedef void (*btd_msd_cb_t) (struct btd_adapter *adapter,
+							struct btd_device *dev,
+							uint16_t company,
+							const uint8_t *data,
+							uint8_t data_len);
+void btd_adapter_register_msd_cb(struct btd_adapter *adapter,
+							btd_msd_cb_t cb);
+void btd_adapter_unregister_msd_cb(struct btd_adapter *adapter,
+							btd_msd_cb_t cb);
+
+/* If TRUE, enables fast connectabe, i.e. reduces page scan interval and changes
+ * type. If FALSE, disables fast connectable, i.e. sets page scan interval and
+ * type to default values. Valid for both connectable and discoverable modes. */
+int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
+							gboolean enable);
+
+int btd_adapter_read_clock(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+				int which, int timeout, uint32_t *clock,
+				uint16_t *accuracy);
+
+int btd_adapter_block_address(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+int btd_adapter_unblock_address(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+int btd_adapter_disconnect_device(struct btd_adapter *adapter,
+							const bdaddr_t *bdaddr,
+							uint8_t bdaddr_type);
+
+int btd_adapter_remove_bonding(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+int btd_adapter_pincode_reply(struct btd_adapter *adapter,
+					const  bdaddr_t *bdaddr,
+					const char *pin, size_t pin_len);
+int btd_adapter_confirm_reply(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+				gboolean success);
+int btd_adapter_passkey_reply(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+				uint32_t passkey);
+
+int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+					uint8_t addr_type, uint8_t io_cap);
+
+int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+					uint8_t addr_type, uint8_t io_cap);
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+							uint8_t addr_type);
+
+int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap);
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter);
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+					const bdaddr_t *bdaddr,
+					uint8_t *hash, uint8_t *randomizer);
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+							const bdaddr_t *bdaddr);
+
+int btd_adapter_gatt_server_start(struct btd_adapter *adapter);
+void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
+
+bool btd_adapter_ssp_enabled(struct btd_adapter *adapter);
+
+int adapter_connect_list_add(struct btd_adapter *adapter,
+					struct btd_device *device);
+void adapter_connect_list_remove(struct btd_adapter *adapter,
+						struct btd_device *device);
+void adapter_auto_connect_add(struct btd_adapter *adapter,
+					struct btd_device *device);
+void adapter_auto_connect_remove(struct btd_adapter *adapter,
+					struct btd_device *device);
+void adapter_whitelist_add(struct btd_adapter *adapter,
+						struct btd_device *dev);
+void adapter_whitelist_remove(struct btd_adapter *adapter,
+						struct btd_device *dev);
+
+void btd_adapter_set_oob_handler(struct btd_adapter *adapter,
+						struct oob_handler *handler);
+gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter);
+
+void btd_adapter_for_each_device(struct btd_adapter *adapter,
+			void (*cb)(struct btd_device *device, void *data),
+			void *data);
+
+bool btd_le_connect_before_pairing(void);
diff --git a/bluez/repo/src/advertising.c b/bluez/repo/src/advertising.c
new file mode 100644
index 0000000..59c8c3d
--- /dev/null
+++ b/bluez/repo/src/advertising.c
@@ -0,0 +1,818 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Google 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.
+ *
+ */
+
+#include "advertising.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+#include "lib/sdp.h"
+
+#include "adapter.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "log.h"
+#include "src/shared/ad.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+
+#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
+#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
+
+struct btd_advertising {
+	struct btd_adapter *adapter;
+	struct queue *ads;
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	uint8_t max_adv_len;
+	uint8_t max_ads;
+	unsigned int instance_bitmap;
+};
+
+#define AD_TYPE_BROADCAST 0
+#define AD_TYPE_PERIPHERAL 1
+
+struct advertisement {
+	struct btd_advertising *manager;
+	char *owner;
+	char *path;
+	GDBusClient *client;
+	GDBusProxy *proxy;
+	DBusMessage *reg;
+	uint8_t type; /* Advertising type */
+	bool include_tx_power;
+	struct bt_ad *data;
+	uint8_t instance;
+};
+
+struct dbus_obj_match {
+	const char *owner;
+	const char *path;
+};
+
+static bool match_advertisement(const void *a, const void *b)
+{
+	const struct advertisement *ad = a;
+	const struct dbus_obj_match *match = b;
+
+	if (match->owner && g_strcmp0(ad->owner, match->owner))
+		return false;
+
+	if (match->path && g_strcmp0(ad->path, match->path))
+		return false;
+
+	return true;
+}
+
+static void advertisement_free(void *data)
+{
+	struct advertisement *ad = data;
+
+	if (ad->client) {
+		g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+		g_dbus_client_unref(ad->client);
+	}
+
+	bt_ad_unref(ad->data);
+
+	g_dbus_proxy_unref(ad->proxy);
+
+	if (ad->owner)
+		g_free(ad->owner);
+
+	if (ad->path)
+		g_free(ad->path);
+
+	free(ad);
+}
+
+static gboolean advertisement_free_idle_cb(void *data)
+{
+	advertisement_free(data);
+
+	return FALSE;
+}
+
+static void advertisement_release(void *data)
+{
+	struct advertisement *ad = data;
+	DBusMessage *message;
+
+	DBG("Releasing advertisement %s, %s", ad->owner, ad->path);
+
+	message = dbus_message_new_method_call(ad->owner, ad->path,
+							LE_ADVERTISEMENT_IFACE,
+							"Release");
+
+	if (!message) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), message);
+}
+
+static void advertisement_destroy(void *data)
+{
+	advertisement_release(data);
+	advertisement_free(data);
+}
+
+static void advertisement_remove(void *data)
+{
+	struct advertisement *ad = data;
+	struct mgmt_cp_remove_advertising cp;
+
+	g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+
+	cp.instance = ad->instance;
+
+	mgmt_send(ad->manager->mgmt, MGMT_OP_REMOVE_ADVERTISING,
+			ad->manager->mgmt_index, sizeof(cp), &cp, NULL, NULL,
+			NULL);
+
+	queue_remove(ad->manager->ads, ad);
+
+	util_clear_uid(&ad->manager->instance_bitmap, ad->instance);
+
+	g_idle_add(advertisement_free_idle_cb, ad);
+}
+
+static void client_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+	DBG("Client disconnected");
+
+	advertisement_remove(user_data);
+}
+
+static bool parse_advertising_type(GDBusProxy *proxy, uint8_t *type)
+{
+	DBusMessageIter iter;
+	const char *msg_type;
+
+	if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return false;
+
+	dbus_message_iter_get_basic(&iter, &msg_type);
+
+	if (!g_strcmp0(msg_type, "broadcast")) {
+		*type = AD_TYPE_BROADCAST;
+		return true;
+	}
+
+	if (!g_strcmp0(msg_type, "peripheral")) {
+		*type = AD_TYPE_PERIPHERAL;
+		return true;
+	}
+
+	return false;
+}
+
+static bool parse_advertising_service_uuids(GDBusProxy *proxy,
+					struct bt_ad *data)
+{
+	DBusMessageIter iter, ariter;
+
+	if (!g_dbus_proxy_get_property(proxy, "ServiceUUIDs", &iter))
+		return true;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &ariter);
+
+	bt_ad_clear_service_uuid(data);
+
+	while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
+		const char *uuid_str;
+		bt_uuid_t uuid;
+
+		dbus_message_iter_get_basic(&ariter, &uuid_str);
+
+		DBG("Adding ServiceUUID: %s", uuid_str);
+
+		if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+			goto fail;
+
+		if (!bt_ad_add_service_uuid(data, &uuid))
+			goto fail;
+
+		dbus_message_iter_next(&ariter);
+	}
+
+	return true;
+
+fail:
+	bt_ad_clear_service_uuid(data);
+	return false;
+}
+
+static bool parse_advertising_solicit_uuids(GDBusProxy *proxy,
+							struct bt_ad *data)
+{
+	DBusMessageIter iter, ariter;
+
+	if (!g_dbus_proxy_get_property(proxy, "SolicitUUIDs", &iter))
+		return true;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &ariter);
+
+	bt_ad_clear_solicit_uuid(data);
+
+	while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
+		const char *uuid_str;
+		bt_uuid_t uuid;
+
+		dbus_message_iter_get_basic(&ariter, &uuid_str);
+
+		DBG("Adding SolicitUUID: %s", uuid_str);
+
+		if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+			goto fail;
+
+		if (!bt_ad_add_solicit_uuid(data, &uuid))
+			goto fail;
+
+		dbus_message_iter_next(&ariter);
+	}
+
+	return true;
+
+fail:
+	bt_ad_clear_solicit_uuid(data);
+	return false;
+}
+
+static bool parse_advertising_manufacturer_data(GDBusProxy *proxy,
+							struct bt_ad *data)
+{
+	DBusMessageIter iter, entries;
+
+	if (!g_dbus_proxy_get_property(proxy, "ManufacturerData", &iter))
+		return true;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &entries);
+
+	bt_ad_clear_manufacturer_data(data);
+
+	while (dbus_message_iter_get_arg_type(&entries)
+						== DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter value, entry;
+		uint16_t manuf_id;
+		uint8_t *manuf_data;
+		int len;
+
+		dbus_message_iter_recurse(&entries, &entry);
+		dbus_message_iter_get_basic(&entry, &manuf_id);
+
+		dbus_message_iter_next(&entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
+			goto fail;
+
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+			goto fail;
+
+		dbus_message_iter_get_fixed_array(&value, &manuf_data, &len);
+
+		DBG("Adding ManufacturerData for %04x", manuf_id);
+
+		if (!bt_ad_add_manufacturer_data(data, manuf_id, manuf_data,
+									len))
+			goto fail;
+
+		dbus_message_iter_next(&entries);
+	}
+
+	return true;
+
+fail:
+	bt_ad_clear_manufacturer_data(data);
+	return false;
+}
+
+static bool parse_advertising_service_data(GDBusProxy *proxy,
+							struct bt_ad *data)
+{
+	DBusMessageIter iter, entries;
+
+	if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
+		return true;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &entries);
+
+	bt_ad_clear_service_data(data);
+
+	while (dbus_message_iter_get_arg_type(&entries)
+						== DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter value, entry;
+		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_ARRAY)
+			goto fail;
+
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+			goto fail;
+
+		dbus_message_iter_get_fixed_array(&value, &service_data, &len);
+
+		DBG("Adding ServiceData for %s", uuid_str);
+
+		if (!bt_ad_add_service_data(data, &uuid, service_data, len))
+			goto fail;
+
+		dbus_message_iter_next(&entries);
+	}
+
+	return true;
+
+fail:
+	bt_ad_clear_service_data(data);
+	return false;
+}
+
+static bool parse_advertising_include_tx_power(GDBusProxy *proxy,
+							bool *included)
+{
+	DBusMessageIter iter;
+	dbus_bool_t b;
+
+	if (!g_dbus_proxy_get_property(proxy, "IncludeTxPower", &iter))
+		return true;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+		return false;
+
+	dbus_message_iter_get_basic(&iter, &b);
+
+	*included = b;
+
+	return true;
+}
+
+static void add_adverting_complete(struct advertisement *ad, uint8_t status)
+{
+	DBusMessage *reply;
+
+	if (status) {
+		error("Failed to add advertisement: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		reply = btd_error_failed(ad->reg,
+					"Failed to register advertisement");
+		queue_remove(ad->manager->ads, ad);
+		g_idle_add(advertisement_free_idle_cb, ad);
+
+	} else
+		reply = dbus_message_new_method_return(ad->reg);
+
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	dbus_message_unref(ad->reg);
+	ad->reg = NULL;
+}
+
+static void add_advertising_callback(uint8_t status, uint16_t length,
+					  const void *param, void *user_data)
+{
+	struct advertisement *ad = 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;
+	}
+
+	ad->instance = rp->instance;
+
+	g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
+									ad);
+	DBG("Advertisement registered: %s", ad->path);
+
+done:
+	add_adverting_complete(ad, status);
+}
+
+static size_t calc_max_adv_len(struct advertisement *ad, uint32_t flags)
+{
+	size_t max = ad->manager->max_adv_len;
+
+	/*
+	 * Flags which reduce the amount of space available for advertising.
+	 * See doc/mgmt-api.txt
+	 */
+	if (flags & MGMT_ADV_FLAG_TX_POWER)
+		max -= 3;
+
+	if (flags & (MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV |
+						MGMT_ADV_FLAG_MANAGED_FLAGS))
+		max -= 3;
+
+	if (flags & MGMT_ADV_FLAG_APPEARANCE)
+		max -= 4;
+
+	return max;
+}
+
+static DBusMessage *refresh_advertisement(struct advertisement *ad)
+{
+	struct mgmt_cp_add_advertising *cp;
+	uint8_t param_len;
+	uint8_t *adv_data;
+	size_t adv_data_len;
+	uint32_t flags = 0;
+
+	DBG("Refreshing advertisement: %s", ad->path);
+
+	if (ad->type == AD_TYPE_PERIPHERAL)
+		flags = MGMT_ADV_FLAG_CONNECTABLE | MGMT_ADV_FLAG_DISCOV;
+
+	if (ad->include_tx_power)
+		flags |= MGMT_ADV_FLAG_TX_POWER;
+
+	adv_data = bt_ad_generate(ad->data, &adv_data_len);
+
+	if (!adv_data || (adv_data_len > calc_max_adv_len(ad, flags))) {
+		error("Advertising data too long or couldn't be generated.");
+
+		return g_dbus_create_error(ad->reg, ERROR_INTERFACE
+						".InvalidLength",
+						"Advertising data too long.");
+	}
+
+	param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len;
+
+	cp = malloc0(param_len);
+
+	if (!cp) {
+		error("Couldn't allocate for MGMT!");
+
+		free(adv_data);
+
+		return btd_error_failed(ad->reg, "Failed");
+	}
+
+	cp->flags = flags;
+	cp->instance = ad->instance;
+	cp->adv_data_len = adv_data_len;
+	memcpy(cp->data, adv_data, adv_data_len);
+
+	free(adv_data);
+
+	if (!mgmt_send(ad->manager->mgmt, MGMT_OP_ADD_ADVERTISING,
+					ad->manager->mgmt_index, param_len, cp,
+					add_advertising_callback, ad, NULL)) {
+		error("Failed to add Advertising Data");
+
+		free(cp);
+
+		return btd_error_failed(ad->reg, "Failed");
+	}
+
+	free(cp);
+
+	return NULL;
+}
+
+static DBusMessage *parse_advertisement(struct advertisement *ad)
+{
+	if (!parse_advertising_type(ad->proxy, &ad->type)) {
+		error("Failed to read \"Type\" property of advertisement");
+		goto fail;
+	}
+
+	if (!parse_advertising_service_uuids(ad->proxy, ad->data)) {
+		error("Property \"ServiceUUIDs\" failed to parse");
+		goto fail;
+	}
+
+	if (!parse_advertising_solicit_uuids(ad->proxy, ad->data)) {
+		error("Property \"SolicitUUIDs\" failed to parse");
+		goto fail;
+	}
+
+	if (!parse_advertising_manufacturer_data(ad->proxy, ad->data)) {
+		error("Property \"ManufacturerData\" failed to parse");
+		goto fail;
+	}
+
+	if (!parse_advertising_service_data(ad->proxy, ad->data)) {
+		error("Property \"ServiceData\" failed to parse");
+		goto fail;
+	}
+
+	if (!parse_advertising_include_tx_power(ad->proxy,
+						&ad->include_tx_power)) {
+		error("Property \"IncludeTxPower\" failed to parse");
+		goto fail;
+	}
+
+	return refresh_advertisement(ad);
+
+fail:
+	return btd_error_failed(ad->reg, "Failed to parse advertisement.");
+}
+
+static void advertisement_proxy_added(GDBusProxy *proxy, void *data)
+{
+	struct advertisement *ad = data;
+	DBusMessage *reply;
+
+	reply = parse_advertisement(ad);
+	if (!reply)
+		return;
+
+	/* Failed to publish for some reason, remove. */
+	queue_remove(ad->manager->ads, ad);
+
+	g_idle_add(advertisement_free_idle_cb, ad);
+
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+	dbus_message_unref(ad->reg);
+	ad->reg = NULL;
+}
+
+static struct advertisement *advertisement_create(DBusConnection *conn,
+					DBusMessage *msg, const char *path)
+{
+	struct advertisement *ad;
+	const char *sender = dbus_message_get_sender(msg);
+
+	if (!path || !g_str_has_prefix(path, "/"))
+		return NULL;
+
+	ad = new0(struct advertisement, 1);
+	ad->client = g_dbus_client_new_full(conn, sender, path, path);
+	if (!ad->client)
+		goto fail;
+
+	ad->owner = g_strdup(sender);
+	if (!ad->owner)
+		goto fail;
+
+	ad->path = g_strdup(path);
+	if (!ad->path)
+		goto fail;
+
+	DBG("Adding proxy for %s", path);
+	ad->proxy = g_dbus_proxy_new(ad->client, path, LE_ADVERTISEMENT_IFACE);
+	if (!ad->proxy)
+		goto fail;
+
+	g_dbus_client_set_proxy_handlers(ad->client, advertisement_proxy_added,
+								NULL, NULL, ad);
+
+	ad->reg = dbus_message_ref(msg);
+
+	ad->data = bt_ad_new();
+	if (!ad->data)
+		goto fail;
+
+	return ad;
+
+fail:
+	advertisement_free(ad);
+	return NULL;
+}
+
+static DBusMessage *register_advertisement(DBusConnection *conn,
+						DBusMessage *msg,
+						void *user_data)
+{
+	struct btd_advertising *manager = user_data;
+	DBusMessageIter args;
+	struct advertisement *ad;
+	struct dbus_obj_match match;
+	uint8_t instance;
+
+	DBG("RegisterAdvertisement");
+
+	if (!dbus_message_iter_init(msg, &args))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &match.path);
+
+	match.owner = dbus_message_get_sender(msg);
+
+	if (queue_find(manager->ads, match_advertisement, &match))
+		return btd_error_already_exists(msg);
+
+	instance = util_get_uid(&manager->instance_bitmap, manager->max_ads);
+	if (!instance)
+		return btd_error_failed(msg, "Maximum advertisements reached");
+
+	dbus_message_iter_next(&args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+		return btd_error_invalid_args(msg);
+
+	ad = advertisement_create(conn, msg, match.path);
+	if (!ad)
+		return btd_error_failed(msg,
+					"Failed to register advertisement");
+
+	DBG("Registered advertisement at path %s", match.path);
+
+	ad->instance = instance;
+	ad->manager = manager;
+
+	queue_push_tail(manager->ads, ad);
+
+	return NULL;
+}
+
+static DBusMessage *unregister_advertisement(DBusConnection *conn,
+						DBusMessage *msg,
+						void *user_data)
+{
+	struct btd_advertising *manager = user_data;
+	DBusMessageIter args;
+	struct advertisement *ad;
+	struct dbus_obj_match match;
+
+	DBG("UnregisterAdvertisement");
+
+	if (!dbus_message_iter_init(msg, &args))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &match.path);
+
+	match.owner = dbus_message_get_sender(msg);
+
+	ad = queue_find(manager->ads, match_advertisement, &match);
+	if (!ad)
+		return btd_error_does_not_exist(msg);
+
+	advertisement_remove(ad);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
+					GDBUS_ARGS({ "advertisement", "o" },
+							{ "options", "a{sv}" }),
+					NULL, register_advertisement) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
+						GDBUS_ARGS({ "service", "o" }),
+						NULL,
+						unregister_advertisement) },
+	{ }
+};
+
+static void advertising_manager_destroy(void *user_data)
+{
+	struct btd_advertising *manager = user_data;
+
+	queue_destroy(manager->ads, advertisement_destroy);
+
+	mgmt_unref(manager->mgmt);
+
+	free(manager);
+}
+
+static void read_adv_features_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_advertising *manager = user_data;
+	const struct mgmt_rp_read_adv_features *feat = param;
+
+	if (status || !param) {
+		error("Failed to read advertising features: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	if (length < sizeof(*feat)) {
+		error("Wrong size of read adv features response");
+		return;
+	}
+
+	manager->max_adv_len = feat->max_adv_data_len;
+	manager->max_ads = feat->max_instances;
+
+	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))
+		error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+}
+
+static struct btd_advertising *
+advertising_manager_create(struct btd_adapter *adapter)
+{
+	struct btd_advertising *manager;
+
+	manager = new0(struct btd_advertising, 1);
+	manager->adapter = adapter;
+
+	manager->mgmt = mgmt_new_default();
+
+	if (!manager->mgmt) {
+		error("Failed to access management interface");
+		free(manager);
+		return NULL;
+	}
+
+	manager->mgmt_index = btd_adapter_get_index(adapter);
+
+	if (!mgmt_send(manager->mgmt, MGMT_OP_READ_ADV_FEATURES,
+				manager->mgmt_index, 0, NULL,
+				read_adv_features_callback, manager, NULL)) {
+		error("Failed to read advertising features");
+		advertising_manager_destroy(manager);
+		return NULL;
+	}
+
+	manager->ads = queue_new();
+
+	return manager;
+}
+
+struct btd_advertising *
+btd_advertising_manager_new(struct btd_adapter *adapter)
+{
+	struct btd_advertising *manager;
+
+	if (!adapter)
+		return NULL;
+
+	manager = advertising_manager_create(adapter);
+	if (!manager)
+		return NULL;
+
+	DBG("LE Advertising Manager created for adapter: %s",
+						adapter_get_path(adapter));
+
+	return manager;
+}
+
+void btd_advertising_manager_destroy(struct btd_advertising *manager)
+{
+	if (!manager)
+		return;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(manager->adapter),
+					LE_ADVERTISING_MGR_IFACE);
+
+	advertising_manager_destroy(manager);
+}
diff --git a/bluez/repo/src/advertising.h b/bluez/repo/src/advertising.h
new file mode 100644
index 0000000..a4b99ff
--- /dev/null
+++ b/bluez/repo/src/advertising.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Google 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.
+ *
+ */
+
+struct btd_adapter;
+struct btd_advertising;
+
+struct btd_advertising *btd_advertising_manager_new(
+						struct btd_adapter *adapter);
+void btd_advertising_manager_destroy(struct btd_advertising *manager);
diff --git a/bluez/repo/src/agent.c b/bluez/repo/src/agent.c
new file mode 100644
index 0000000..ff44d57
--- /dev/null
+++ b/bluez/repo/src/agent.c
@@ -0,0 +1,1055 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <stdlib.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "log.h"
+#include "error.h"
+#include "hcid.h"
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "agent.h"
+#include "shared/queue.h"
+
+#define IO_CAPABILITY_DISPLAYONLY	0x00
+#define IO_CAPABILITY_DISPLAYYESNO	0x01
+#define IO_CAPABILITY_KEYBOARDONLY	0x02
+#define IO_CAPABILITY_NOINPUTNOOUTPUT	0x03
+#define IO_CAPABILITY_KEYBOARDDISPLAY	0x04
+#define IO_CAPABILITY_INVALID		0xFF
+
+#define REQUEST_TIMEOUT (60 * 1000)		/* 60 seconds */
+#define AGENT_INTERFACE "org.bluez.Agent1"
+
+static GHashTable *agent_list;
+struct queue *default_agents = NULL;
+
+typedef enum {
+	AGENT_REQUEST_PASSKEY,
+	AGENT_REQUEST_CONFIRMATION,
+	AGENT_REQUEST_AUTHORIZATION,
+	AGENT_REQUEST_PINCODE,
+	AGENT_REQUEST_AUTHORIZE_SERVICE,
+	AGENT_REQUEST_DISPLAY_PINCODE,
+} agent_request_type_t;
+
+struct agent {
+	int ref;
+	char *owner;
+	char *path;
+	uint8_t capability;
+	struct agent_request *request;
+	guint watch;
+};
+
+struct agent_request {
+	agent_request_type_t type;
+	struct agent *agent;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	void *cb;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+static void agent_release(struct agent *agent)
+{
+	DBusMessage *message;
+
+	DBG("Releasing agent %s, %s", agent->owner, agent->path);
+
+	if (agent->request)
+		agent_cancel(agent);
+
+	message = dbus_message_new_method_call(agent->owner, agent->path,
+						AGENT_INTERFACE, "Release");
+	if (message == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), message);
+}
+
+static int send_cancel_request(struct agent_request *req)
+{
+	DBusMessage *message;
+
+	DBG("Sending Cancel request to %s, %s", req->agent->owner,
+							req->agent->path);
+
+	message = dbus_message_new_method_call(req->agent->owner, req->agent->path,
+						AGENT_INTERFACE, "Cancel");
+	if (message == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	g_dbus_send_message(btd_get_dbus_connection(), message);
+
+	return 0;
+}
+
+static void agent_request_free(struct agent_request *req, gboolean destroy)
+{
+	if (req->msg)
+		dbus_message_unref(req->msg);
+	if (req->call)
+		dbus_pending_call_unref(req->call);
+	if (req->agent && req->agent->request)
+		req->agent->request = NULL;
+	if (destroy && req->destroy)
+		req->destroy(req->user_data);
+	g_free(req);
+}
+
+static void set_io_cap(struct btd_adapter *adapter, gpointer user_data)
+{
+	struct agent *agent = user_data;
+	uint8_t io_cap;
+
+	if (agent)
+		io_cap = agent->capability;
+	else
+		io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+
+	adapter_set_io_capability(adapter, io_cap);
+}
+
+static bool add_default_agent(struct agent *agent)
+{
+	if (queue_peek_head(default_agents) == agent)
+		return true;
+
+	queue_remove(default_agents, agent);
+
+	if (!queue_push_head(default_agents, agent))
+		return false;
+
+	DBG("Default agent set to %s %s", agent->owner, agent->path);
+
+	adapter_foreach(set_io_cap, agent);
+
+	return true;
+}
+
+static void remove_default_agent(struct agent *agent)
+{
+	if (queue_peek_head(default_agents) != agent) {
+		queue_remove(default_agents, agent);
+		return;
+	}
+
+	queue_remove(default_agents, agent);
+
+	agent = queue_peek_head(default_agents);
+	if (agent)
+		DBG("Default agent set to %s %s", agent->owner, agent->path);
+	else
+		DBG("Default agent cleared");
+
+	adapter_foreach(set_io_cap, agent);
+}
+
+static void agent_disconnect(DBusConnection *conn, void *user_data)
+{
+	struct agent *agent = user_data;
+
+	DBG("Agent %s disconnected", agent->owner);
+
+	if (agent->watch > 0) {
+		g_dbus_remove_watch(conn, agent->watch);
+		agent->watch = 0;
+	}
+
+	remove_default_agent(agent);
+
+	g_hash_table_remove(agent_list, agent->owner);
+}
+
+struct agent *agent_ref(struct agent *agent)
+{
+	agent->ref++;
+
+	DBG("%p: ref=%d", agent, agent->ref);
+
+	return agent;
+}
+
+void agent_unref(struct agent *agent)
+{
+	agent->ref--;
+
+	DBG("%p: ref=%d", agent, agent->ref);
+
+	if (agent->ref > 0)
+		return;
+
+	if (agent->request) {
+		DBusError err;
+		agent_pincode_cb pincode_cb;
+		agent_passkey_cb passkey_cb;
+		agent_cb cb;
+
+		dbus_error_init(&err);
+		dbus_set_error_const(&err, ERROR_INTERFACE ".Failed",
+								"Canceled");
+
+		switch (agent->request->type) {
+		case AGENT_REQUEST_PINCODE:
+			pincode_cb = agent->request->cb;
+			pincode_cb(agent, &err, NULL, agent->request->user_data);
+			break;
+		case AGENT_REQUEST_PASSKEY:
+			passkey_cb = agent->request->cb;
+			passkey_cb(agent, &err, 0, agent->request->user_data);
+			break;
+		case AGENT_REQUEST_CONFIRMATION:
+		case AGENT_REQUEST_AUTHORIZATION:
+		case AGENT_REQUEST_AUTHORIZE_SERVICE:
+		case AGENT_REQUEST_DISPLAY_PINCODE:
+		default:
+			cb = agent->request->cb;
+			cb(agent, &err, agent->request->user_data);
+		}
+
+		dbus_error_free(&err);
+
+		agent_cancel(agent);
+	}
+
+	g_free(agent->owner);
+	g_free(agent->path);
+
+	g_free(agent);
+}
+
+struct agent *agent_get(const char *owner)
+{
+	struct agent *agent;
+
+	if (owner) {
+		agent = g_hash_table_lookup(agent_list, owner);
+		if (agent)
+			return agent_ref(agent);
+	}
+
+	if (!queue_isempty(default_agents))
+		return agent_ref(queue_peek_head(default_agents));
+
+	return NULL;
+}
+
+static struct agent *agent_create( const char *name, const char *path,
+							uint8_t capability)
+{
+	struct agent *agent;
+
+	agent = g_new0(struct agent, 1);
+
+	agent->owner = g_strdup(name);
+	agent->path = g_strdup(path);
+	agent->capability = capability;
+
+	agent->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+							name, agent_disconnect,
+							agent, NULL);
+
+	return agent_ref(agent);
+}
+
+static struct agent_request *agent_request_new(struct agent *agent,
+						agent_request_type_t type,
+						void *cb,
+						void *user_data,
+						GDestroyNotify destroy)
+{
+	struct agent_request *req;
+
+	req = g_new0(struct agent_request, 1);
+
+	req->agent = agent;
+	req->type = type;
+	req->cb = cb;
+	req->user_data = user_data;
+	req->destroy = destroy;
+
+	return req;
+}
+
+int agent_cancel(struct agent *agent)
+{
+	if (!agent->request)
+		return -EINVAL;
+
+	if (agent->request->call) {
+		dbus_pending_call_cancel(agent->request->call);
+		send_cancel_request(agent->request);
+	}
+
+	agent_request_free(agent->request, TRUE);
+	agent->request = NULL;
+
+	return 0;
+}
+
+static void simple_agent_reply(DBusPendingCall *call, void *user_data)
+{
+	struct agent_request *req = user_data;
+	struct agent *agent = req->agent;
+	DBusMessage *message;
+	DBusError err;
+	agent_cb cb = req->cb;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	message = dbus_pending_call_steal_reply(call);
+
+	/* Protect from the callback freeing the agent */
+	agent_ref(agent);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		DBG("agent error reply: %s, %s", err.name, err.message);
+
+		cb(agent, &err, req->user_data);
+
+		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+			error("Timed out waiting for reply from agent");
+			agent_cancel(agent);
+			dbus_message_unref(message);
+			dbus_error_free(&err);
+			agent_unref(agent);
+			return;
+		}
+
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+		error("Wrong reply signature: %s", err.message);
+		cb(agent, &err, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	cb(agent, NULL, req->user_data);
+done:
+	dbus_message_unref(message);
+
+	agent->request = NULL;
+	agent_request_free(req, TRUE);
+	agent_unref(agent);
+}
+
+static int agent_call_authorize_service(struct agent_request *req,
+						const char *device_path,
+						const char *uuid)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+					AGENT_INTERFACE, "AuthorizeService");
+	if (!req->msg) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg,
+				DBUS_TYPE_OBJECT_PATH, &device_path,
+				DBUS_TYPE_STRING, &uuid,
+				DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(),
+						req->msg, &req->call,
+						REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+	return 0;
+}
+
+int agent_authorize_service(struct agent *agent, const char *path,
+				const char *uuid, agent_cb cb,
+				void *user_data, GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE_SERVICE, cb,
+							user_data, destroy);
+
+	err = agent_call_authorize_service(req, path, uuid);
+	if (err < 0) {
+		agent_request_free(req, FALSE);
+		return -ENOMEM;
+	}
+
+	agent->request = req;
+
+	DBG("authorize service request was sent for %s", path);
+
+	return 0;
+}
+
+static void pincode_reply(DBusPendingCall *call, void *user_data)
+{
+	struct agent_request *req = user_data;
+	struct agent *agent = req->agent;
+	agent_pincode_cb cb = req->cb;
+	DBusMessage *message;
+	DBusError err;
+	size_t len;
+	char *pin;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	message = dbus_pending_call_steal_reply(call);
+
+	/* Protect from the callback freeing the agent */
+	agent_ref(agent);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		error("Agent %s replied with an error: %s, %s",
+				agent->path, err.name, err.message);
+
+		cb(agent, &err, NULL, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	if (!dbus_message_get_args(message, &err,
+				DBUS_TYPE_STRING, &pin,
+				DBUS_TYPE_INVALID)) {
+		error("Wrong passkey reply signature: %s", err.message);
+		cb(agent, &err, NULL, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	len = strlen(pin);
+
+	if (len > 16 || len < 1) {
+		error("Invalid PIN length (%zu) from agent", len);
+		dbus_set_error_const(&err, ERROR_INTERFACE ".InvalidArgs",
+					"Invalid passkey length");
+		cb(agent, &err, NULL, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	cb(agent, NULL, pin, req->user_data);
+
+done:
+	if (message)
+		dbus_message_unref(message);
+
+	dbus_pending_call_cancel(req->call);
+	agent->request = NULL;
+	agent_request_free(req, TRUE);
+	agent_unref(agent);
+}
+
+static int pincode_request_new(struct agent_request *req, const char *device_path,
+				dbus_bool_t secure)
+{
+	struct agent *agent = req->agent;
+
+	/* TODO: Add a new method or a new param to Agent interface to request
+		secure pin. */
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+					AGENT_INTERFACE, "RequestPinCode");
+	if (req->msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+					DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg,
+					&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL);
+	return 0;
+}
+
+int agent_request_pincode(struct agent *agent, struct btd_device *device,
+				agent_pincode_cb cb, gboolean secure,
+				void *user_data, GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	const char *dev_path = device_get_path(device);
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb,
+							user_data, destroy);
+
+	err = pincode_request_new(req, dev_path, secure);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
+static void passkey_reply(DBusPendingCall *call, void *user_data)
+{
+	struct agent_request *req = user_data;
+	struct agent *agent = req->agent;
+	agent_passkey_cb cb = req->cb;
+	DBusMessage *message;
+	DBusError err;
+	uint32_t passkey;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	message = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		error("Agent replied with an error: %s, %s",
+						err.name, err.message);
+		cb(agent, &err, 0, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	if (!dbus_message_get_args(message, &err,
+				DBUS_TYPE_UINT32, &passkey,
+				DBUS_TYPE_INVALID)) {
+		error("Wrong passkey reply signature: %s", err.message);
+		cb(agent, &err, 0, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	cb(agent, NULL, passkey, req->user_data);
+
+done:
+	if (message)
+		dbus_message_unref(message);
+
+	dbus_pending_call_cancel(req->call);
+	agent->request = NULL;
+	agent_request_free(req, TRUE);
+}
+
+static int passkey_request_new(struct agent_request *req,
+				const char *device_path)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+					AGENT_INTERFACE, "RequestPasskey");
+	if (req->msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+					DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg,
+					&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
+	return 0;
+}
+
+int agent_request_passkey(struct agent *agent, struct btd_device *device,
+				agent_passkey_cb cb, void *user_data,
+				GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	const char *dev_path = device_get_path(device);
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	DBG("Calling Agent.RequestPasskey: name=%s, path=%s",
+			agent->owner, agent->path);
+
+	req = agent_request_new(agent, AGENT_REQUEST_PASSKEY, cb,
+							user_data, destroy);
+
+	err = passkey_request_new(req, dev_path);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
+static int confirmation_request_new(struct agent_request *req,
+					const char *device_path,
+					uint32_t passkey)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+				AGENT_INTERFACE, "RequestConfirmation");
+	if (req->msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg,
+				DBUS_TYPE_OBJECT_PATH, &device_path,
+				DBUS_TYPE_UINT32, &passkey,
+				DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg,
+				&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+
+	return 0;
+}
+
+int agent_request_confirmation(struct agent *agent, struct btd_device *device,
+				uint32_t passkey, agent_cb cb,
+				void *user_data, GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	const char *dev_path = device_get_path(device);
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	DBG("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u",
+			agent->owner, agent->path, passkey);
+
+	req = agent_request_new(agent, AGENT_REQUEST_CONFIRMATION, cb,
+				user_data, destroy);
+
+	err = confirmation_request_new(req, dev_path, passkey);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
+static int authorization_request_new(struct agent_request *req,
+						const char *device_path)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+				AGENT_INTERFACE, "RequestAuthorization");
+	if (req->msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg,
+				DBUS_TYPE_OBJECT_PATH, &device_path,
+				DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg,
+				&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+
+	return 0;
+}
+
+int agent_request_authorization(struct agent *agent, struct btd_device *device,
+						agent_cb cb, void *user_data,
+						GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	const char *dev_path = device_get_path(device);
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	DBG("Calling Agent.RequestAuthorization: name=%s, path=%s",
+						agent->owner, agent->path);
+
+	req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZATION, cb,
+				user_data, destroy);
+
+	err = authorization_request_new(req, dev_path);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
+int agent_display_passkey(struct agent *agent, struct btd_device *device,
+				uint32_t passkey, uint16_t entered)
+{
+	DBusMessage *message;
+	const char *dev_path = device_get_path(device);
+
+	message = dbus_message_new_method_call(agent->owner, agent->path,
+					AGENT_INTERFACE, "DisplayPasskey");
+	if (!message) {
+		error("Couldn't allocate D-Bus message");
+		return -1;
+	}
+
+	dbus_message_append_args(message,
+				DBUS_TYPE_OBJECT_PATH, &dev_path,
+				DBUS_TYPE_UINT32, &passkey,
+				DBUS_TYPE_UINT16, &entered,
+				DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message(btd_get_dbus_connection(), message)) {
+		error("D-Bus send failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void display_pincode_reply(DBusPendingCall *call, void *user_data)
+{
+	struct agent_request *req = user_data;
+	struct agent *agent = req->agent;
+	DBusMessage *message;
+	DBusError err;
+	agent_cb cb = req->cb;
+
+	/* clear agent->request early; our callback will likely try
+	 * another request */
+	agent->request = NULL;
+
+	/* steal_reply will always return non-NULL since the callback
+	 * is only called after a reply has been received */
+	message = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		error("Agent replied with an error: %s, %s",
+						err.name, err.message);
+
+		cb(agent, &err, req->user_data);
+
+		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+			agent_cancel(agent);
+			dbus_message_unref(message);
+			dbus_error_free(&err);
+			return;
+		}
+
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+		error("Wrong reply signature: %s", err.message);
+		cb(agent, &err, req->user_data);
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	cb(agent, NULL, req->user_data);
+done:
+	dbus_message_unref(message);
+
+	agent_request_free(req, TRUE);
+}
+
+static int display_pincode_request_new(struct agent_request *req,
+					const char *device_path,
+					const char *pincode)
+{
+	struct agent *agent = req->agent;
+
+	req->msg = dbus_message_new_method_call(agent->owner, agent->path,
+					AGENT_INTERFACE, "DisplayPinCode");
+	if (req->msg == NULL) {
+		error("Couldn't allocate D-Bus message");
+		return -ENOMEM;
+	}
+
+	dbus_message_append_args(req->msg,
+					DBUS_TYPE_OBJECT_PATH, &device_path,
+					DBUS_TYPE_STRING, &pincode,
+					DBUS_TYPE_INVALID);
+
+	if (g_dbus_send_message_with_reply(btd_get_dbus_connection(), req->msg,
+				&req->call, REQUEST_TIMEOUT) == FALSE) {
+		error("D-Bus send failed");
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(req->call, display_pincode_reply,
+								req, NULL);
+
+	return 0;
+}
+
+int agent_display_pincode(struct agent *agent, struct btd_device *device,
+				const char *pincode, agent_cb cb,
+				void *user_data, GDestroyNotify destroy)
+{
+	struct agent_request *req;
+	const char *dev_path = device_get_path(device);
+	int err;
+
+	if (agent->request)
+		return -EBUSY;
+
+	DBG("Calling Agent.DisplayPinCode: name=%s, path=%s, pincode=%s",
+					agent->owner, agent->path, pincode);
+
+	req = agent_request_new(agent, AGENT_REQUEST_DISPLAY_PINCODE, cb,
+							user_data, destroy);
+
+	err = display_pincode_request_new(req, dev_path, pincode);
+	if (err < 0)
+		goto failed;
+
+	agent->request = req;
+
+	return 0;
+
+failed:
+	agent_request_free(req, FALSE);
+	return err;
+}
+
+uint8_t agent_get_io_capability(struct agent *agent)
+{
+	return agent->capability;
+}
+
+static void agent_destroy(gpointer data)
+{
+	struct agent *agent = data;
+
+	DBG("agent %s", agent->owner);
+
+	if (agent->watch > 0) {
+		g_dbus_remove_watch(btd_get_dbus_connection(), agent->watch);
+		agent->watch = 0;
+		agent_release(agent);
+	}
+
+	remove_default_agent(agent);
+
+	agent_unref(agent);
+}
+
+static uint8_t parse_io_capability(const char *capability)
+{
+	if (g_str_equal(capability, ""))
+		return IO_CAPABILITY_KEYBOARDDISPLAY;
+	if (g_str_equal(capability, "DisplayOnly"))
+		return IO_CAPABILITY_DISPLAYONLY;
+	if (g_str_equal(capability, "DisplayYesNo"))
+		return IO_CAPABILITY_DISPLAYYESNO;
+	if (g_str_equal(capability, "KeyboardOnly"))
+		return IO_CAPABILITY_KEYBOARDONLY;
+	if (g_str_equal(capability, "NoInputNoOutput"))
+		return IO_CAPABILITY_NOINPUTNOOUTPUT;
+	if (g_str_equal(capability, "KeyboardDisplay"))
+		return IO_CAPABILITY_KEYBOARDDISPLAY;
+	return IO_CAPABILITY_INVALID;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct agent *agent;
+	const char *sender, *path, *capability;
+	uint8_t cap;
+
+	sender = dbus_message_get_sender(msg);
+
+	agent = g_hash_table_lookup(agent_list, sender);
+	if (agent)
+		return btd_error_already_exists(msg);
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_STRING, &capability,
+						DBUS_TYPE_INVALID) == FALSE)
+		return btd_error_invalid_args(msg);
+
+	cap = parse_io_capability(capability);
+	if (cap == IO_CAPABILITY_INVALID)
+		return btd_error_invalid_args(msg);
+
+	agent = agent_create(sender, path, cap);
+	if (!agent)
+		return btd_error_invalid_args(msg);
+
+	DBG("agent %s", agent->owner);
+
+	g_hash_table_replace(agent_list, agent->owner, agent);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct agent *agent;
+	const char *sender, *path;
+
+	sender = dbus_message_get_sender(msg);
+
+	agent = g_hash_table_lookup(agent_list, sender);
+	if (!agent)
+		return btd_error_does_not_exist(msg);
+
+	DBG("agent %s", agent->owner);
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID) == FALSE)
+		return btd_error_invalid_args(msg);
+
+	if (g_str_equal(path, agent->path) == FALSE)
+		return btd_error_does_not_exist(msg);
+
+	agent_disconnect(conn, agent);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *request_default(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct agent *agent;
+	const char *sender, *path;
+
+	sender = dbus_message_get_sender(msg);
+
+	agent = g_hash_table_lookup(agent_list, sender);
+	if (!agent)
+		return btd_error_does_not_exist(msg);
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID) == FALSE)
+		return btd_error_invalid_args(msg);
+
+	if (g_str_equal(path, agent->path) == FALSE)
+		return btd_error_does_not_exist(msg);
+
+	if (!add_default_agent(agent))
+		return btd_error_failed(msg, "Failed to set as default");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_METHOD("RegisterAgent",
+			GDBUS_ARGS({ "agent", "o"}, { "capability", "s" }),
+			NULL, register_agent) },
+	{ GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "agent", "o" }),
+			NULL, unregister_agent) },
+	{ GDBUS_METHOD("RequestDefaultAgent", GDBUS_ARGS({ "agent", "o" }),
+			NULL, request_default ) },
+	{ }
+};
+
+void btd_agent_init(void)
+{
+	agent_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+						NULL, agent_destroy);
+
+	default_agents = queue_new();
+
+	g_dbus_register_interface(btd_get_dbus_connection(),
+				"/org/bluez", "org.bluez.AgentManager1",
+				methods, NULL, NULL, NULL, NULL);
+}
+
+void btd_agent_cleanup(void)
+{
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				"/org/bluez", "org.bluez.AgentManager1");
+
+	g_hash_table_destroy(agent_list);
+	queue_destroy(default_agents, NULL);
+}
diff --git a/bluez/repo/src/agent.h b/bluez/repo/src/agent.h
new file mode 100644
index 0000000..1e46920
--- /dev/null
+++ b/bluez/repo/src/agent.h
@@ -0,0 +1,73 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+struct agent;
+
+typedef void (*agent_cb) (struct agent *agent, DBusError *err,
+				void *user_data);
+
+typedef void (*agent_pincode_cb) (struct agent *agent, DBusError *err,
+					const char *pincode, void *user_data);
+
+typedef void (*agent_passkey_cb) (struct agent *agent, DBusError *err,
+					uint32_t passkey, void *user_data);
+
+struct agent *agent_ref(struct agent *agent);
+void agent_unref(struct agent *agent);
+
+struct agent *agent_get(const char *owner);
+
+int agent_authorize_service(struct agent *agent, const char *path,
+				const char *uuid, agent_cb cb,
+				void *user_data, GDestroyNotify destroy);
+
+int agent_request_pincode(struct agent *agent, struct btd_device *device,
+				agent_pincode_cb cb, gboolean secure,
+				void *user_data, GDestroyNotify destroy);
+
+int agent_request_passkey(struct agent *agent, struct btd_device *device,
+				agent_passkey_cb cb, void *user_data,
+				GDestroyNotify destroy);
+
+int agent_request_confirmation(struct agent *agent, struct btd_device *device,
+				uint32_t passkey, agent_cb cb,
+				void *user_data, GDestroyNotify destroy);
+
+int agent_request_authorization(struct agent *agent, struct btd_device *device,
+						agent_cb cb, void *user_data,
+						GDestroyNotify destroy);
+
+int agent_display_passkey(struct agent *agent, struct btd_device *device,
+				uint32_t passkey, uint16_t entered);
+
+int agent_display_pincode(struct agent *agent, struct btd_device *device,
+				const char *pincode, agent_cb cb,
+				void *user_data, GDestroyNotify destroy);
+
+int agent_cancel(struct agent *agent);
+
+uint8_t agent_get_io_capability(struct agent *agent);
+
+void btd_agent_init(void);
+void btd_agent_cleanup(void);
diff --git a/bluez/repo/src/attio.h b/bluez/repo/src/attio.h
new file mode 100644
index 0000000..16e2873
--- /dev/null
+++ b/bluez/repo/src/attio.h
@@ -0,0 +1,33 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
+typedef void (*attio_disconnect_cb) (gpointer user_data);
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+						attio_connect_cb cfunc,
+						attio_disconnect_cb dcfunc,
+						gpointer user_data);
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
diff --git a/bluez/repo/src/attrib-server.c b/bluez/repo/src/attrib-server.c
new file mode 100644
index 0000000..4439c27
--- /dev/null
+++ b/bluez/repo/src/attrib-server.c
@@ -0,0 +1,1653 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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 <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+#include <sys/stat.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "btio/btio.h"
+#include "log.h"
+#include "backtrace.h"
+#include "adapter.h"
+#include "device.h"
+#include "src/shared/util.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "textfile.h"
+#include "storage.h"
+
+#include "attrib-server.h"
+
+static GSList *servers = NULL;
+
+struct gatt_server {
+	struct btd_adapter *adapter;
+	GIOChannel *l2cap_io;
+	GIOChannel *le_io;
+	uint32_t gatt_sdp_handle;
+	uint32_t gap_sdp_handle;
+	GList *database;
+	GSList *clients;
+	uint16_t name_handle;
+	uint16_t appearance_handle;
+};
+
+struct gatt_channel {
+	GAttrib *attrib;
+	guint mtu;
+	gboolean le;
+	guint id;
+	gboolean encrypted;
+	struct gatt_server *server;
+	guint cleanup_id;
+	struct btd_device *device;
+};
+
+struct group_elem {
+	uint16_t handle;
+	uint16_t end;
+	uint8_t *data;
+	uint16_t len;
+};
+
+static bt_uuid_t prim_uuid = {
+			.type = BT_UUID16,
+			.value.u16 = GATT_PRIM_SVC_UUID
+};
+static bt_uuid_t snd_uuid = {
+			.type = BT_UUID16,
+			.value.u16 = GATT_SND_SVC_UUID
+};
+static bt_uuid_t ccc_uuid = {
+			.type = BT_UUID16,
+			.value.u16 = GATT_CLIENT_CHARAC_CFG_UUID
+};
+
+static void attrib_free(void *data)
+{
+	struct attribute *a = data;
+
+	g_free(a->data);
+	g_free(a);
+}
+
+static void channel_free(struct gatt_channel *channel)
+{
+
+	if (channel->cleanup_id)
+		g_source_remove(channel->cleanup_id);
+
+	if (channel->device)
+		btd_device_unref(channel->device);
+
+	g_attrib_unref(channel->attrib);
+	g_free(channel);
+}
+
+static void gatt_server_free(struct gatt_server *server)
+{
+	g_list_free_full(server->database, attrib_free);
+
+	if (server->l2cap_io != NULL) {
+		g_io_channel_shutdown(server->l2cap_io, FALSE, NULL);
+		g_io_channel_unref(server->l2cap_io);
+	}
+
+	if (server->le_io != NULL) {
+		g_io_channel_shutdown(server->le_io, FALSE, NULL);
+		g_io_channel_unref(server->le_io);
+	}
+
+	g_slist_free_full(server->clients, (GDestroyNotify) channel_free);
+
+	if (server->gatt_sdp_handle > 0)
+		adapter_service_remove(server->adapter,
+					server->gatt_sdp_handle);
+
+	if (server->gap_sdp_handle > 0)
+		adapter_service_remove(server->adapter, server->gap_sdp_handle);
+
+	if (server->adapter != NULL)
+		btd_adapter_unref(server->adapter);
+
+	g_free(server);
+}
+
+static int adapter_cmp_addr(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_server *server = a;
+	const bdaddr_t *bdaddr = b;
+
+	return bacmp(btd_adapter_get_address(server->adapter), bdaddr);
+}
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_server *server = a;
+	const struct btd_adapter *adapter = b;
+
+	if (server->adapter == adapter)
+		return 0;
+
+	return -1;
+}
+
+static struct gatt_server *find_gatt_server(const bdaddr_t *bdaddr)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(servers, bdaddr, adapter_cmp_addr);
+	if (l == NULL) {
+		char addr[18];
+
+		ba2str(bdaddr, addr);
+		error("No GATT server found in %s", addr);
+		return NULL;
+	}
+
+	return l->data;
+}
+
+static sdp_record_t *server_record_new(uuid_t *uuid, uint16_t start, uint16_t end)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+	uuid_t root_uuid, proto_uuid, l2cap;
+	sdp_record_t *record;
+	sdp_data_t *psm, *sh, *eh;
+	uint16_t lp = ATT_PSM;
+
+	if (uuid == NULL)
+		return NULL;
+
+	if (start > end)
+		return NULL;
+
+	record = sdp_record_alloc();
+	if (record == NULL)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	svclass_id = sdp_list_append(NULL, uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&proto_uuid, ATT_UUID);
+	proto[1] = sdp_list_append(NULL, &proto_uuid);
+	sh = sdp_data_alloc(SDP_UINT16, &start);
+	proto[1] = sdp_list_append(proto[1], sh);
+	eh = sdp_data_alloc(SDP_UINT16, &end);
+	proto[1] = sdp_list_append(proto[1], eh);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_data_free(psm);
+	sdp_data_free(sh);
+	sdp_data_free(eh);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return record;
+}
+
+static int handle_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct attribute *attrib = a;
+	uint16_t handle = GPOINTER_TO_UINT(b);
+
+	return attrib->handle - handle;
+}
+
+static int attribute_cmp(gconstpointer a1, gconstpointer a2)
+{
+	const struct attribute *attrib1 = a1;
+	const struct attribute *attrib2 = a2;
+
+	return attrib1->handle - attrib2->handle;
+}
+
+static struct attribute *find_svc_range(struct gatt_server *server,
+					uint16_t start, uint16_t *end)
+{
+	struct attribute *attrib;
+	guint h = start;
+	GList *l;
+
+	if (end == NULL)
+		return NULL;
+
+	l = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+								handle_cmp);
+	if (!l)
+		return NULL;
+
+	attrib = l->data;
+
+	if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0 &&
+			bt_uuid_cmp(&attrib->uuid, &snd_uuid) != 0)
+		return NULL;
+
+	*end = start;
+
+	for (l = l->next; l; l = l->next) {
+		struct attribute *a = l->data;
+
+		if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+				bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
+			break;
+
+		*end = a->handle;
+	}
+
+	return attrib;
+}
+
+static uint32_t attrib_create_sdp_new(struct gatt_server *server,
+					uint16_t handle, const char *name)
+{
+	sdp_record_t *record;
+	struct attribute *a;
+	uint16_t end = 0;
+	uuid_t svc, gap_uuid;
+
+	a = find_svc_range(server, handle, &end);
+
+	if (a == NULL)
+		return 0;
+
+	if (a->len == 2)
+		sdp_uuid16_create(&svc, get_le16(a->data));
+	else if (a->len == 16) {
+		uint8_t be128[16];
+
+		/* Converting from LE to BE */
+		bswap_128(a->data, be128);
+		sdp_uuid128_create(&svc, be128);
+	} else
+		return 0;
+
+	record = server_record_new(&svc, handle, end);
+	if (record == NULL)
+		return 0;
+
+	if (name != NULL)
+		sdp_set_info_attr(record, name, "BlueZ", NULL);
+
+	sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
+	if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
+		sdp_set_url_attr(record, "http://www.bluez.org/",
+				"http://www.bluez.org/",
+				"http://www.bluez.org/");
+	}
+
+	if (adapter_service_add(server->adapter, record) == 0)
+		return record->handle;
+
+	sdp_record_free(record);
+	return 0;
+}
+
+static struct attribute *attrib_db_add_new(struct gatt_server *server,
+				uint16_t handle, bt_uuid_t *uuid,
+				int read_req, int write_req,
+				const uint8_t *value, size_t len)
+{
+	struct attribute *a;
+	guint h = handle;
+
+	DBG("handle=0x%04x", handle);
+
+	if (g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+								handle_cmp))
+		return NULL;
+
+	a = g_new0(struct attribute, 1);
+	a->len = len;
+	a->data = g_memdup(value, len);
+	a->handle = handle;
+	a->uuid = *uuid;
+	a->read_req = read_req;
+	a->write_req = write_req;
+
+	server->database = g_list_insert_sorted(server->database, a,
+								attribute_cmp);
+
+	return a;
+}
+
+static bool g_attrib_is_encrypted(GAttrib *attrib)
+{
+	BtIOSecLevel sec_level;
+	GIOChannel *io = g_attrib_get_channel(attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							     BT_IO_OPT_INVALID))
+		return FALSE;
+
+	return sec_level > BT_IO_SEC_LOW;
+}
+
+static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
+								int reqs)
+{
+	/* FIXME: currently, it is assumed an encrypted link is enough for
+	 * authentication. This will allow to enable the SMP negotiation once
+	 * it is on upstream kernel. High security level should be mapped
+	 * to authentication and medium to encryption permission. */
+	if (!channel->encrypted)
+		channel->encrypted = g_attrib_is_encrypted(channel->attrib);
+	if (reqs == ATT_AUTHENTICATION && !channel->encrypted)
+		return ATT_ECODE_AUTHENTICATION;
+	else if (reqs == ATT_AUTHORIZATION)
+		return ATT_ECODE_AUTHORIZATION;
+
+	switch (opcode) {
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+		if (reqs == ATT_NOT_PERMITTED)
+			return ATT_ECODE_READ_NOT_PERM;
+		break;
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+		if (reqs == ATT_NOT_PERMITTED)
+			return ATT_ECODE_WRITE_NOT_PERM;
+		break;
+	}
+
+	return 0;
+}
+
+static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
+						uint16_t end, bt_uuid_t *uuid,
+						uint8_t *pdu, size_t len)
+{
+	struct att_data_list *adl;
+	struct attribute *a;
+	struct group_elem *cur, *old = NULL;
+	GSList *l, *groups;
+	GList *dl, *database;
+	uint16_t length, last_handle, last_size = 0;
+	uint8_t status;
+	int i;
+
+	if (start > end || start == 0x0000)
+		return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+					ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+	/*
+	 * Only <<Primary Service>> and <<Secondary Service>> grouping
+	 * types may be used in the Read By Group Type Request.
+	 */
+
+	if (bt_uuid_cmp(uuid, &prim_uuid) != 0 &&
+		bt_uuid_cmp(uuid, &snd_uuid) != 0)
+		return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, 0x0000,
+					ATT_ECODE_UNSUPP_GRP_TYPE, pdu, len);
+
+	last_handle = end;
+	database = channel->server->database;
+	for (dl = database, groups = NULL, cur = NULL; dl; dl = dl->next) {
+
+		a = dl->data;
+
+		if (a->handle < start)
+			continue;
+
+		if (a->handle >= end)
+			break;
+
+		/* The old group ends when a new one starts */
+		if (old && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+				bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) {
+			old->end = last_handle;
+			old = NULL;
+		}
+
+		if (bt_uuid_cmp(&a->uuid, uuid) != 0) {
+			/* Still inside a service, update its last handle */
+			if (old)
+				last_handle = a->handle;
+			continue;
+		}
+
+		if (last_size && (last_size != a->len))
+			break;
+
+		status = att_check_reqs(channel, ATT_OP_READ_BY_GROUP_REQ,
+								a->read_req);
+
+		if (status == 0x00 && a->read_cb)
+			status = a->read_cb(a, channel->device,
+							a->cb_user_data);
+
+		if (status) {
+			g_slist_free_full(groups, g_free);
+			return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ,
+						a->handle, status, pdu, len);
+		}
+
+		cur = g_new0(struct group_elem, 1);
+		cur->handle = a->handle;
+		cur->data = a->data;
+		cur->len = a->len;
+
+		/* Attribute Grouping Type found */
+		groups = g_slist_append(groups, cur);
+
+		last_size = a->len;
+		old = cur;
+		last_handle = cur->handle;
+	}
+
+	if (groups == NULL)
+		return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+					ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+	if (dl == NULL)
+		cur->end = a->handle;
+	else
+		cur->end = last_handle;
+
+	length = g_slist_length(groups);
+
+	adl = att_data_list_alloc(length, last_size + 4);
+	if (adl == NULL) {
+		g_slist_free_full(groups, g_free);
+		return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+					ATT_ECODE_UNLIKELY, pdu, len);
+	}
+
+	for (i = 0, l = groups; l; l = l->next, i++) {
+		uint8_t *value;
+
+		cur = l->data;
+
+		value = (void *) adl->data[i];
+
+		put_le16(cur->handle, value);
+		put_le16(cur->end, &value[2]);
+		/* Attribute Value */
+		memcpy(&value[4], cur->data, cur->len);
+	}
+
+	length = enc_read_by_grp_resp(adl, pdu, len);
+
+	att_data_list_free(adl);
+	g_slist_free_full(groups, g_free);
+
+	return length;
+}
+
+static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start,
+						uint16_t end, bt_uuid_t *uuid,
+						uint8_t *pdu, size_t len)
+{
+	struct att_data_list *adl;
+	GSList *l, *types;
+	GList *dl, *database;
+	struct attribute *a;
+	uint16_t num, length;
+	uint8_t status;
+	int i;
+
+	if (start > end || start == 0x0000)
+		return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+					ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+	database = channel->server->database;
+	for (dl = database, length = 0, types = NULL; dl; dl = dl->next) {
+
+		a = dl->data;
+
+		if (a->handle < start)
+			continue;
+
+		if (a->handle > end)
+			break;
+
+		if (bt_uuid_cmp(&a->uuid, uuid)  != 0)
+			continue;
+
+		status = att_check_reqs(channel, ATT_OP_READ_BY_TYPE_REQ,
+								a->read_req);
+
+		if (status == 0x00 && a->read_cb)
+			status = a->read_cb(a, channel->device,
+							a->cb_user_data);
+
+		if (status) {
+			g_slist_free(types);
+			return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ,
+						a->handle, status, pdu, len);
+		}
+
+		/* All elements must have the same length */
+		if (length == 0)
+			length = a->len;
+		else if (a->len != length)
+			break;
+
+		types = g_slist_append(types, a);
+	}
+
+	if (types == NULL)
+		return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+					ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+	num = g_slist_length(types);
+
+	/* Handle length plus attribute value length */
+	length += 2;
+
+	adl = att_data_list_alloc(num, length);
+	if (adl == NULL) {
+		g_slist_free(types);
+		return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+					ATT_ECODE_UNLIKELY, pdu, len);
+	}
+
+	for (i = 0, l = types; l; i++, l = l->next) {
+		uint8_t *value;
+
+		a = l->data;
+
+		value = (void *) adl->data[i];
+
+		put_le16(a->handle, value);
+
+		/* Attribute Value */
+		memcpy(&value[2], a->data, a->len);
+	}
+
+	length = enc_read_by_type_resp(adl, pdu, len);
+
+	att_data_list_free(adl);
+	g_slist_free(types);
+
+	return length;
+}
+
+static uint16_t find_info(struct gatt_channel *channel, uint16_t start,
+				uint16_t end, uint8_t *pdu, size_t len)
+{
+	struct attribute *a;
+	struct att_data_list *adl;
+	GSList *l, *info;
+	GList *dl, *database;
+	uint8_t format, last_type = BT_UUID_UNSPEC;
+	uint16_t length, num;
+	int i;
+
+	if (start > end || start == 0x0000)
+		return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+					ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+	database = channel->server->database;
+	for (dl = database, info = NULL, num = 0; dl; dl = dl->next) {
+		a = dl->data;
+
+		if (a->handle < start)
+			continue;
+
+		if (a->handle > end)
+			break;
+
+		if (last_type == BT_UUID_UNSPEC)
+			last_type = a->uuid.type;
+
+		if (a->uuid.type != last_type)
+			break;
+
+		info = g_slist_append(info, a);
+		num++;
+
+		last_type = a->uuid.type;
+	}
+
+	if (info == NULL)
+		return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+					ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+	if (last_type == BT_UUID16) {
+		length = 2;
+		format = 0x01;
+	} else if (last_type == BT_UUID128) {
+		length = 16;
+		format = 0x02;
+	} else {
+		g_slist_free(info);
+		return 0;
+	}
+
+	adl = att_data_list_alloc(num, length + 2);
+	if (adl == NULL) {
+		g_slist_free(info);
+		return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+					ATT_ECODE_UNLIKELY, pdu, len);
+	}
+
+	for (i = 0, l = info; l; i++, l = l->next) {
+		uint8_t *value;
+
+		a = l->data;
+
+		value = (void *) adl->data[i];
+
+		put_le16(a->handle, value);
+
+		/* Attribute Value */
+		bt_uuid_to_le(&a->uuid, &value[2]);
+	}
+
+	length = enc_find_info_resp(format, adl, pdu, len);
+
+	att_data_list_free(adl);
+	g_slist_free(info);
+
+	return length;
+}
+
+static uint16_t find_by_type(struct gatt_channel *channel, uint16_t start,
+				uint16_t end, bt_uuid_t *uuid,
+				const uint8_t *value, size_t vlen,
+				uint8_t *opdu, size_t mtu)
+{
+	struct attribute *a;
+	struct att_range *range;
+	GSList *matches;
+	GList *dl, *database;
+	uint16_t len;
+
+	if (start > end || start == 0x0000)
+		return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+					ATT_ECODE_INVALID_HANDLE, opdu, mtu);
+
+	/* Searching first requested handle number */
+	database = channel->server->database;
+	for (dl = database, matches = NULL, range = NULL; dl; dl = dl->next) {
+		a = dl->data;
+
+		if (a->handle < start)
+			continue;
+
+		if (a->handle > end)
+			break;
+
+		/* Primary service? Attribute value matches? */
+		if ((bt_uuid_cmp(&a->uuid, uuid) == 0) && (a->len == vlen) &&
+					(memcmp(a->data, value, vlen) == 0)) {
+
+			range = g_new0(struct att_range, 1);
+			range->start = a->handle;
+			/* It is allowed to have end group handle the same as
+			 * start handle, for groups with only one attribute. */
+			range->end = a->handle;
+
+			matches = g_slist_append(matches, range);
+		} else if (range) {
+			/* Update the last found handle or reset the pointer
+			 * to track that a new group started: Primary or
+			 * Secondary service. */
+			if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+					bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
+				range = NULL;
+			else
+				range->end = a->handle;
+		}
+	}
+
+	if (matches == NULL)
+		return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+				ATT_ECODE_ATTR_NOT_FOUND, opdu, mtu);
+
+	len = enc_find_by_type_resp(matches, opdu, mtu);
+
+	g_slist_free_full(matches, g_free);
+
+	return len;
+}
+
+static int read_device_ccc(struct btd_device *device, uint16_t handle,
+				uint16_t *value)
+{
+	char *filename;
+	GKeyFile *key_file;
+	char group[6];
+	char *str;
+	unsigned int config;
+	int err = 0;
+
+	filename = btd_device_get_storage_path(device, "ccc");
+	if (!filename) {
+		warn("Unable to get ccc storage path for device");
+		return -ENOENT;
+	}
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	sprintf(group, "%hu", handle);
+
+	str = g_key_file_get_string(key_file, group, "Value", NULL);
+	if (!str || sscanf(str, "%04X", &config) != 1)
+		err = -ENOENT;
+	else
+		*value = config;
+
+	g_free(str);
+	g_free(filename);
+	g_key_file_free(key_file);
+
+	return err;
+}
+
+static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
+						uint8_t *pdu, size_t len)
+{
+	struct attribute *a;
+	uint8_t status;
+	GList *l;
+	uint16_t cccval;
+	guint h = handle;
+
+	l = g_list_find_custom(channel->server->database,
+					GUINT_TO_POINTER(h), handle_cmp);
+	if (!l)
+		return enc_error_resp(ATT_OP_READ_REQ, handle,
+					ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+	a = l->data;
+
+	if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
+		read_device_ccc(channel->device, handle, &cccval) == 0) {
+		uint8_t config[2];
+
+		put_le16(cccval, config);
+		return enc_read_resp(config, sizeof(config), pdu, len);
+	}
+
+	status = att_check_reqs(channel, ATT_OP_READ_REQ, a->read_req);
+
+	if (status == 0x00 && a->read_cb)
+		status = a->read_cb(a, channel->device, a->cb_user_data);
+
+	if (status)
+		return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu,
+									len);
+
+	return enc_read_resp(a->data, a->len, pdu, len);
+}
+
+static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle,
+				uint16_t offset, uint8_t *pdu, size_t len)
+{
+	struct attribute *a;
+	uint8_t status;
+	GList *l;
+	uint16_t cccval;
+	guint h = handle;
+
+	l = g_list_find_custom(channel->server->database,
+					GUINT_TO_POINTER(h), handle_cmp);
+	if (!l)
+		return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+					ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+	a = l->data;
+
+	if (a->len < offset)
+		return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+					ATT_ECODE_INVALID_OFFSET, pdu, len);
+
+	if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
+		read_device_ccc(channel->device, handle, &cccval) == 0) {
+		uint8_t config[2];
+
+		put_le16(cccval, config);
+		return enc_read_blob_resp(config, sizeof(config), offset,
+								pdu, len);
+	}
+
+	status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_req);
+
+	if (status == 0x00 && a->read_cb)
+		status = a->read_cb(a, channel->device, a->cb_user_data);
+
+	if (status)
+		return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, status,
+								pdu, len);
+
+	return enc_read_blob_resp(a->data, a->len, offset, pdu, len);
+}
+
+static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
+					const uint8_t *value, size_t vlen,
+					uint8_t *pdu, size_t len)
+{
+	struct attribute *a;
+	uint8_t status;
+	GList *l;
+	guint h = handle;
+
+	l = g_list_find_custom(channel->server->database,
+					GUINT_TO_POINTER(h), handle_cmp);
+	if (!l)
+		return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+				ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+	a = l->data;
+
+	status = att_check_reqs(channel, ATT_OP_WRITE_REQ, a->write_req);
+	if (status)
+		return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu,
+									len);
+
+	if (bt_uuid_cmp(&ccc_uuid, &a->uuid) != 0) {
+
+		attrib_db_update(channel->server->adapter, handle, NULL,
+							value, vlen, NULL);
+
+		if (a->write_cb) {
+			status = a->write_cb(a, channel->device,
+							a->cb_user_data);
+			if (status)
+				return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+							status, pdu, len);
+		}
+	} else {
+		uint16_t cccval = get_le16(value);
+		char *filename;
+		GKeyFile *key_file;
+		char group[6], value[5];
+		char *data;
+		gsize length = 0;
+
+		filename = btd_device_get_storage_path(channel->device, "ccc");
+		if (!filename) {
+			warn("Unable to get ccc storage path for device");
+			return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+						ATT_ECODE_WRITE_NOT_PERM,
+						pdu, len);
+		}
+
+		key_file = g_key_file_new();
+		g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+		sprintf(group, "%hu", handle);
+		sprintf(value, "%hX", cccval);
+		g_key_file_set_string(key_file, group, "Value", value);
+
+		data = g_key_file_to_data(key_file, &length, NULL);
+		if (length > 0) {
+			create_file(filename, S_IRUSR | S_IWUSR);
+			g_file_set_contents(filename, data, length, NULL);
+		}
+
+		g_free(data);
+		g_free(filename);
+		g_key_file_free(key_file);
+	}
+
+	return enc_write_resp(pdu);
+}
+
+static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
+						uint8_t *pdu, size_t len)
+{
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uint16_t imtu;
+
+	if (mtu < ATT_DEFAULT_LE_MTU)
+		return enc_error_resp(ATT_OP_MTU_REQ, 0,
+					ATT_ECODE_REQ_NOT_SUPP, pdu, len);
+
+	io = g_attrib_get_channel(channel->attrib);
+
+	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return enc_error_resp(ATT_OP_MTU_REQ, 0, ATT_ECODE_UNLIKELY,
+								pdu, len);
+	}
+
+	channel->mtu = MIN(mtu, imtu);
+	g_attrib_set_mtu(channel->attrib, channel->mtu);
+
+	return enc_mtu_resp(imtu, pdu, len);
+}
+
+static void channel_remove(struct gatt_channel *channel)
+{
+	channel->server->clients = g_slist_remove(channel->server->clients,
+								channel);
+	channel_free(channel);
+}
+
+static gboolean channel_watch_cb(GIOChannel *io, GIOCondition cond,
+						gpointer user_data)
+{
+	channel_remove(user_data);
+
+	return FALSE;
+}
+
+static void channel_handler(const uint8_t *ipdu, uint16_t len,
+							gpointer user_data)
+{
+	struct gatt_channel *channel = user_data;
+	uint8_t opdu[channel->mtu];
+	uint16_t length, start, end, mtu, offset;
+	bt_uuid_t uuid;
+	uint8_t status = 0;
+	size_t vlen;
+	uint8_t *value = g_attrib_get_buffer(channel->attrib, &vlen);
+
+	DBG("op 0x%02x", ipdu[0]);
+
+	if (len > vlen) {
+		error("Too much data on ATT socket");
+		status = ATT_ECODE_INVALID_PDU;
+		goto done;
+	}
+
+	switch (ipdu[0]) {
+	case ATT_OP_READ_BY_GROUP_REQ:
+		length = dec_read_by_grp_req(ipdu, len, &start, &end, &uuid);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = read_by_group(channel, start, end, &uuid, opdu,
+								channel->mtu);
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+		length = dec_read_by_type_req(ipdu, len, &start, &end, &uuid);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = read_by_type(channel, start, end, &uuid, opdu,
+								channel->mtu);
+		break;
+	case ATT_OP_READ_REQ:
+		length = dec_read_req(ipdu, len, &start);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = read_value(channel, start, opdu, channel->mtu);
+		break;
+	case ATT_OP_READ_BLOB_REQ:
+		length = dec_read_blob_req(ipdu, len, &start, &offset);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = read_blob(channel, start, offset, opdu, channel->mtu);
+		break;
+	case ATT_OP_MTU_REQ:
+		if (!channel->le) {
+			status = ATT_ECODE_REQ_NOT_SUPP;
+			goto done;
+		}
+
+		length = dec_mtu_req(ipdu, len, &mtu);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = mtu_exchange(channel, mtu, opdu, channel->mtu);
+		break;
+	case ATT_OP_FIND_INFO_REQ:
+		length = dec_find_info_req(ipdu, len, &start, &end);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = find_info(channel, start, end, opdu, channel->mtu);
+		break;
+	case ATT_OP_WRITE_REQ:
+		length = dec_write_req(ipdu, len, &start, value, &vlen);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = write_value(channel, start, value, vlen, opdu,
+								channel->mtu);
+		break;
+	case ATT_OP_WRITE_CMD:
+		length = dec_write_cmd(ipdu, len, &start, value, &vlen);
+		if (length > 0)
+			write_value(channel, start, value, vlen, opdu,
+								channel->mtu);
+		return;
+	case ATT_OP_FIND_BY_TYPE_REQ:
+		length = dec_find_by_type_req(ipdu, len, &start, &end,
+							&uuid, value, &vlen);
+		if (length == 0) {
+			status = ATT_ECODE_INVALID_PDU;
+			goto done;
+		}
+
+		length = find_by_type(channel, start, end, &uuid, value, vlen,
+							opdu, channel->mtu);
+		break;
+	case ATT_OP_HANDLE_CNF:
+		return;
+	case ATT_OP_HANDLE_IND:
+	case ATT_OP_HANDLE_NOTIFY:
+		/* The attribute client is already handling these */
+		return;
+	case ATT_OP_READ_MULTI_REQ:
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_EXEC_WRITE_REQ:
+	default:
+		DBG("Unsupported request 0x%02x", ipdu[0]);
+		status = ATT_ECODE_REQ_NOT_SUPP;
+		goto done;
+	}
+
+	if (length == 0)
+		status = ATT_ECODE_IO;
+
+done:
+	if (status)
+		length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+								channel->mtu);
+
+	g_attrib_send(channel->attrib, 0, opdu, length, NULL, NULL, NULL);
+}
+
+GAttrib *attrib_from_device(struct btd_device *device)
+{
+	struct btd_adapter *adapter = device_get_adapter(device);
+	struct gatt_server *server;
+	GSList *l;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (!l)
+		return NULL;
+
+	server = l->data;
+
+	for (l = server->clients; l; l = l->next) {
+		struct gatt_channel *channel = l->data;
+
+		if (channel->device == device)
+			return g_attrib_ref(channel->attrib);
+	}
+
+	return NULL;
+}
+
+guint attrib_channel_attach(GAttrib *attrib)
+{
+	struct gatt_server *server;
+	struct btd_device *device;
+	struct gatt_channel *channel;
+	bdaddr_t src, dst;
+	GIOChannel *io;
+	GError *gerr = NULL;
+	uint8_t bdaddr_type;
+	uint16_t cid;
+	guint mtu = 0;
+
+	io = g_attrib_get_channel(attrib);
+
+	bt_io_get(io, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST_TYPE, &bdaddr_type,
+			BT_IO_OPT_CID, &cid,
+			BT_IO_OPT_IMTU, &mtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return 0;
+	}
+
+	server = find_gatt_server(&src);
+	if (server == NULL)
+		return 0;
+
+	channel = g_new0(struct gatt_channel, 1);
+	channel->server = server;
+
+	device = btd_adapter_find_device(server->adapter, &dst, bdaddr_type);
+	if (device == NULL) {
+		error("Device object not found for attrib server");
+		g_free(channel);
+		return 0;
+	}
+
+	if (!device_is_bonded(device, bdaddr_type)) {
+		char *filename;
+
+		filename = btd_device_get_storage_path(device, "ccc");
+		if (filename) {
+			unlink(filename);
+			g_free(filename);
+		}
+	}
+
+	if (cid != ATT_CID) {
+		channel->le = FALSE;
+		channel->mtu = mtu;
+	} else {
+		channel->le = TRUE;
+		channel->mtu = ATT_DEFAULT_LE_MTU;
+	}
+
+	channel->attrib = g_attrib_ref(attrib);
+	channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_REQS,
+			GATTRIB_ALL_HANDLES, channel_handler, channel, NULL);
+
+	channel->cleanup_id = g_io_add_watch(io, G_IO_HUP, channel_watch_cb,
+								channel);
+
+	channel->device = btd_device_ref(device);
+
+	server->clients = g_slist_append(server->clients, channel);
+
+	return channel->id;
+}
+
+static struct gatt_channel *find_channel(guint id)
+{
+	GSList *l;
+
+	for (l = servers; l; l = g_slist_next(l)) {
+		struct gatt_server *server = l->data;
+		GSList *c;
+
+		for (c = server->clients; c; c = g_slist_next(c)) {
+			struct gatt_channel *channel = c->data;
+
+			if (channel->id == id)
+				return channel;
+		}
+	}
+
+	return NULL;
+}
+
+gboolean attrib_channel_detach(GAttrib *attrib, guint id)
+{
+	struct gatt_channel *channel;
+
+	channel = find_channel(id);
+	if (channel == NULL)
+		return FALSE;
+
+	g_attrib_unregister(channel->attrib, channel->id);
+	channel_remove(channel);
+
+	return TRUE;
+}
+
+static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+{
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+	uint8_t dst_type;
+	bdaddr_t src, dst;
+
+	DBG("");
+
+	if (gerr) {
+		error("%s", gerr->message);
+		return;
+	}
+
+	bt_io_get(io, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST_TYPE, &dst_type,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	adapter = adapter_find(&src);
+	if (!adapter)
+		return;
+
+	device = btd_adapter_get_device(adapter, &dst, dst_type);
+	if (!device)
+		return;
+
+	device_attach_att(device, io);
+}
+
+static gboolean register_core_services(struct gatt_server *server)
+{
+	uint8_t atval[256];
+	bt_uuid_t uuid;
+	uint16_t appearance = 0x0000;
+
+	/* GAP service: primary service definition */
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	put_le16(GENERIC_ACCESS_PROFILE_ID, &atval[0]);
+	attrib_db_add_new(server, 0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	/* GAP service: device name characteristic */
+	server->name_handle = 0x0006;
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(server->name_handle, &atval[1]);
+	put_le16(GATT_CHARAC_DEVICE_NAME, &atval[3]);
+	attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* GAP service: device name attribute */
+	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+	attrib_db_add_new(server, server->name_handle, &uuid, ATT_NONE,
+						ATT_NOT_PERMITTED, NULL, 0);
+
+	/* GAP service: device appearance characteristic */
+	server->appearance_handle = 0x0008;
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	atval[0] = GATT_CHR_PROP_READ;
+	put_le16(server->appearance_handle, &atval[1]);
+	put_le16(GATT_CHARAC_APPEARANCE, &atval[3]);
+	attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 5);
+
+	/* GAP service: device appearance attribute */
+	bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+	put_le16(appearance, &atval[0]);
+	attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE,
+						ATT_NOT_PERMITTED, atval, 2);
+	server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001,
+						"Generic Access Profile");
+	if (server->gap_sdp_handle == 0) {
+		error("Failed to register GAP service record");
+		return FALSE;
+	}
+
+	/* GATT service: primary service definition */
+	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	put_le16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
+	attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+								atval, 2);
+
+	server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010,
+						"Generic Attribute Profile");
+	if (server->gatt_sdp_handle == 0) {
+		error("Failed to register GATT service record");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+int btd_adapter_gatt_server_start(struct btd_adapter *adapter)
+{
+	struct gatt_server *server;
+	GError *gerr = NULL;
+	const bdaddr_t *addr;
+
+	DBG("Start GATT server in hci%d", btd_adapter_get_index(adapter));
+
+	server = g_new0(struct gatt_server, 1);
+	server->adapter = btd_adapter_ref(adapter);
+
+	addr = btd_adapter_get_address(server->adapter);
+
+	/* BR/EDR socket */
+	server->l2cap_io = bt_io_listen(connect_event, NULL, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, addr,
+					BT_IO_OPT_PSM, ATT_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	if (server->l2cap_io == NULL) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		gatt_server_free(server);
+		return -1;
+	}
+
+	if (!register_core_services(server)) {
+		gatt_server_free(server);
+		return -1;
+	}
+
+	/* LE socket */
+	server->le_io = bt_io_listen(connect_event, NULL,
+					&server->le_io, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, addr,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+					BT_IO_OPT_CID, ATT_CID,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	if (server->le_io == NULL) {
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		/* Doesn't have LE support, continue */
+	}
+
+	servers = g_slist_prepend(servers, server);
+	return 0;
+}
+
+void btd_adapter_gatt_server_stop(struct btd_adapter *adapter)
+{
+	struct gatt_server *server;
+	GSList *l;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return;
+
+	DBG("Stop GATT server in hci%d", btd_adapter_get_index(adapter));
+
+	server = l->data;
+	servers = g_slist_remove(servers, server);
+	gatt_server_free(server);
+}
+
+uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
+							const char *name)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return 0;
+
+	return attrib_create_sdp_new(l->data, handle, name);
+}
+
+void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle)
+{
+	adapter_service_remove(adapter, sdp_handle);
+}
+
+static uint16_t find_uuid16_avail(struct btd_adapter *adapter, uint16_t nitems)
+{
+	struct gatt_server *server;
+	uint16_t handle;
+	GSList *l;
+	GList *dl;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return 0;
+
+	server = l->data;
+	if (server->database == NULL)
+		return 0x0001;
+
+	for (dl = server->database, handle = 0x0001; dl; dl = dl->next) {
+		struct attribute *a = dl->data;
+
+		if ((bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+				bt_uuid_cmp(&a->uuid, &snd_uuid) == 0) &&
+				a->handle - handle >= nitems)
+			/* Note: the range above excludes the current handle */
+			return handle;
+
+		if (a->len == 16 && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+				bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) {
+			/* 128 bit UUID service definition */
+			return 0;
+		}
+
+		if (a->handle == 0xffff)
+			return 0;
+
+		handle = a->handle + 1;
+	}
+
+	if (0xffff - handle + 1 >= nitems)
+		return handle;
+
+	return 0;
+}
+
+static uint16_t find_uuid128_avail(struct btd_adapter *adapter, uint16_t nitems)
+{
+	uint16_t handle = 0, end = 0xffff;
+	struct gatt_server *server;
+	GList *dl;
+	GSList *l;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return 0;
+
+	server = l->data;
+	if (server->database == NULL)
+		return 0xffff - nitems + 1;
+
+	for (dl = g_list_last(server->database); dl; dl = dl->prev) {
+		struct attribute *a = dl->data;
+
+		if (handle == 0)
+			handle = a->handle;
+
+		if (bt_uuid_cmp(&a->uuid, &prim_uuid) != 0 &&
+				bt_uuid_cmp(&a->uuid, &snd_uuid) != 0)
+			continue;
+
+		if (end - handle >= nitems)
+			return end - nitems + 1;
+
+		if (a->len == 2) {
+			/* 16 bit UUID service definition */
+			return 0;
+		}
+
+		if (a->handle == 0x0001)
+			return 0;
+
+		end = a->handle - 1;
+		handle = 0;
+	}
+
+	if (end - 0x0001 >= nitems)
+		return end - nitems + 1;
+
+	return 0;
+}
+
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid,
+								uint16_t nitems)
+{
+	btd_assert(nitems > 0);
+
+	if (svc_uuid->type == BT_UUID16)
+		return find_uuid16_avail(adapter, nitems);
+	else if (svc_uuid->type == BT_UUID128)
+		return find_uuid128_avail(adapter, nitems);
+	else {
+		char uuidstr[MAX_LEN_UUID_STR];
+
+		bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
+		error("Service uuid: %s is neither a 16-bit nor a 128-bit uuid",
+								uuidstr);
+		return 0;
+	}
+}
+
+struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle,
+					bt_uuid_t *uuid, int read_req,
+					int write_req, const uint8_t *value,
+					size_t len)
+{
+	GSList *l;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return NULL;
+
+	return attrib_db_add_new(l->data, handle, uuid, read_req, write_req,
+								value, len);
+}
+
+int attrib_db_update(struct btd_adapter *adapter, uint16_t handle,
+					bt_uuid_t *uuid, const uint8_t *value,
+					size_t len, struct attribute **attr)
+{
+	struct gatt_server *server;
+	struct attribute *a;
+	GSList *l;
+	GList *dl;
+	guint h = handle;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return -ENOENT;
+
+	server = l->data;
+
+	DBG("handle=0x%04x", handle);
+
+	dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+								handle_cmp);
+	if (dl == NULL)
+		return -ENOENT;
+
+	a = dl->data;
+
+	a->data = g_try_realloc(a->data, len);
+	if (len && a->data == NULL)
+		return -ENOMEM;
+
+	a->len = len;
+	memcpy(a->data, value, len);
+
+	if (uuid != NULL)
+		a->uuid = *uuid;
+
+	if (attr)
+		*attr = a;
+
+	return 0;
+}
+
+int attrib_db_del(struct btd_adapter *adapter, uint16_t handle)
+{
+	struct gatt_server *server;
+	struct attribute *a;
+	GSList *l;
+	GList *dl;
+	guint h = handle;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return -ENOENT;
+
+	server = l->data;
+
+	DBG("handle=0x%04x", handle);
+
+	dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+								handle_cmp);
+	if (dl == NULL)
+		return -ENOENT;
+
+	a = dl->data;
+	server->database = g_list_remove(server->database, a);
+	g_free(a->data);
+	g_free(a);
+
+	return 0;
+}
+
+int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
+					const uint8_t *value, size_t len)
+{
+	struct gatt_server *server;
+	uint16_t handle;
+	GSList *l;
+
+	l = g_slist_find_custom(servers, adapter, adapter_cmp);
+	if (l == NULL)
+		return -ENOENT;
+
+	server = l->data;
+
+	/* FIXME: Missing Privacy and Reconnection Address */
+
+	switch (uuid) {
+	case GATT_CHARAC_DEVICE_NAME:
+		handle = server->name_handle;
+		break;
+	case GATT_CHARAC_APPEARANCE:
+		handle = server->appearance_handle;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	return attrib_db_update(adapter, handle, NULL, value, len, NULL);
+}
diff --git a/bluez/repo/src/attrib-server.h b/bluez/repo/src/attrib-server.h
new file mode 100644
index 0000000..063cb66
--- /dev/null
+++ b/bluez/repo/src/attrib-server.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 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
+ *
+ */
+
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid,
+							uint16_t nitems);
+struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle,
+				bt_uuid_t *uuid, int read_req,
+				int write_req, const uint8_t *value,
+				size_t len);
+int attrib_db_update(struct btd_adapter *adapter, uint16_t handle,
+					bt_uuid_t *uuid, const uint8_t *value,
+					size_t len, struct attribute **attr);
+int attrib_db_del(struct btd_adapter *adapter, uint16_t handle);
+int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
+					const uint8_t *value, size_t len);
+uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
+							const char *name);
+void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle);
+GAttrib *attrib_from_device(struct btd_device *device);
+guint attrib_channel_attach(GAttrib *attrib);
+gboolean attrib_channel_detach(GAttrib *attrib, guint id);
diff --git a/bluez/repo/src/backtrace.c b/bluez/repo/src/backtrace.c
new file mode 100644
index 0000000..c438733
--- /dev/null
+++ b/bluez/repo/src/backtrace.c
@@ -0,0 +1,135 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <unistd.h>
+#include <inttypes.h>
+
+#ifdef HAVE_BACKTRACE_SUPPORT
+#include <execinfo.h>
+#include <elfutils/libdwfl.h>
+#endif
+
+#include "src/log.h"
+#include "src/backtrace.h"
+
+void btd_backtrace_init(void)
+{
+#ifdef HAVE_BACKTRACE_SUPPORT
+	void *frames[1];
+
+	/*
+	 * initialize the backtracer, since the ctor calls dlopen(), which
+	 * calls malloc(), which isn't signal-safe.
+	 */
+	backtrace(frames, 1);
+#endif
+}
+
+void btd_backtrace(uint16_t index)
+{
+#ifdef HAVE_BACKTRACE_SUPPORT
+	char *debuginfo_path = NULL;
+	const Dwfl_Callbacks callbacks = {
+		.find_debuginfo = dwfl_standard_find_debuginfo,
+		.find_elf = dwfl_linux_proc_find_elf,
+		.debuginfo_path = &debuginfo_path,
+	};
+	Dwfl *dwfl;
+	void *frames[48];
+	int n, n_ptrs;
+
+	dwfl = dwfl_begin(&callbacks);
+
+	if (dwfl_linux_proc_report(dwfl, getpid()))
+		goto done;
+
+	dwfl_report_end(dwfl, NULL, NULL);
+
+	n_ptrs = backtrace(frames, 48);
+	if (n_ptrs < 1)
+		goto done;
+
+	btd_error(index, "++++++++ backtrace ++++++++");
+
+	for (n = 1; n < n_ptrs; n++) {
+		GElf_Addr addr = (uintptr_t) frames[n];
+		GElf_Sym sym;
+		GElf_Word shndx;
+		Dwfl_Module *module = dwfl_addrmodule(dwfl, addr);
+		Dwfl_Line *line;
+		const char *name, *modname;
+
+		if (!module) {
+			btd_error(index, "#%-2u ?? [%#" PRIx64 "]", n, addr);
+			continue;
+		}
+
+		name = dwfl_module_addrsym(module, addr, &sym, &shndx);
+		if (!name) {
+			modname = dwfl_module_info(module, NULL, NULL, NULL,
+							NULL, NULL, NULL, NULL);
+			btd_error(index, "#%-2u ?? (%s) [%#" PRIx64 "]",
+							n, modname, addr);
+			continue;
+		}
+
+		line = dwfl_module_getsrc(module, addr);
+		if (line) {
+			int lineno;
+			const char *src = dwfl_lineinfo(line, NULL, &lineno,
+							NULL, NULL, NULL);
+
+			if (src) {
+				btd_error(index, "#%-2u %s+%#" PRIx64 " "
+						"(%s:%d) [%#" PRIx64 "]",
+						n, name, addr - sym.st_value,
+							src, lineno, addr);
+				continue;
+			}
+		}
+
+		modname = dwfl_module_info(module, NULL, NULL, NULL,
+						NULL, NULL, NULL, NULL);
+		btd_error(index, "#%-2u %s+%#" PRIx64 " (%s) [%#" PRIx64 "]",
+						n, name, addr - sym.st_value,
+								modname, addr);
+	}
+
+	btd_error(index, "+++++++++++++++++++++++++++");
+
+done:
+	dwfl_end(dwfl);
+#endif
+}
+
+void btd_assertion_message_expr(const char *file, int line,
+					const char *func, const char *expr)
+{
+	btd_error(0xffff, "Assertion failed: (%s) %s:%d in %s",
+						expr, file, line, func);
+	btd_backtrace(0xffff);
+}
diff --git a/bluez/repo/src/backtrace.h b/bluez/repo/src/backtrace.h
new file mode 100644
index 0000000..b3eef6d
--- /dev/null
+++ b/bluez/repo/src/backtrace.h
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+#include <stdint.h>
+
+void btd_backtrace_init(void);
+void btd_backtrace(uint16_t index);
+
+void btd_assertion_message_expr(const char *file, int line,
+					const char *func, const char *expr);
+
+#define btd_assert(expr) do { \
+	if (expr) ; else \
+		btd_assertion_message_expr(__FILE__, __LINE__, __func__, #expr); \
+	} while (0)
diff --git a/bluez/repo/src/bluetooth.conf b/bluez/repo/src/bluetooth.conf
new file mode 100644
index 0000000..10d2d36
--- /dev/null
+++ b/bluez/repo/src/bluetooth.conf
@@ -0,0 +1,41 @@
+<!-- This configuration file specifies the required security policies
+     for Bluetooth core daemon to work. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- ../system.conf have denied everything, so we just punch some holes -->
+
+  <policy user="root">
+    <allow own="org.bluez"/>
+    <allow send_destination="org.bluez"/>
+    <allow send_interface="org.bluez.Agent1"/>
+    <allow send_interface="org.bluez.MediaEndpoint1"/>
+    <allow send_interface="org.bluez.MediaPlayer1"/>
+    <allow send_interface="org.bluez.ThermometerWatcher1"/>
+    <allow send_interface="org.bluez.AlertAgent1"/>
+    <allow send_interface="org.bluez.Profile1"/>
+    <allow send_interface="org.bluez.HeartRateWatcher1"/>
+    <allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
+    <allow send_interface="org.bluez.GattCharacteristic1"/>
+    <allow send_interface="org.bluez.GattDescriptor1"/>
+    <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
+    <allow send_interface="org.freedesktop.DBus.Properties"/>
+  </policy>
+
+  <policy at_console="true">
+    <allow send_destination="org.bluez"/>
+  </policy>
+
+  <!-- allow users of lp group (printing subsystem) to 
+       communicate with bluetoothd -->
+  <policy group="lp">
+    <allow send_destination="org.bluez"/>
+  </policy>
+
+  <policy context="default">
+    <deny send_destination="org.bluez"/>
+  </policy>
+
+</busconfig>
diff --git a/bluez/repo/src/bluetooth.service.in b/bluez/repo/src/bluetooth.service.in
new file mode 100644
index 0000000..83e4732
--- /dev/null
+++ b/bluez/repo/src/bluetooth.service.in
@@ -0,0 +1,18 @@
+[Unit]
+Description=Bluetooth service
+Documentation=man:bluetoothd(8)
+ConditionPathIsDirectory=/sys/class/bluetooth
+
+[Service]
+Type=dbus
+BusName=org.bluez
+ExecStart=@libexecdir@/bluetoothd
+NotifyAccess=main
+#WatchdogSec=10
+#Restart=on-failure
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+LimitNPROC=1
+
+[Install]
+WantedBy=bluetooth.target
+Alias=dbus-org.bluez.service
diff --git a/bluez/repo/src/bluetooth.ver b/bluez/repo/src/bluetooth.ver
new file mode 100644
index 0000000..214fa8a
--- /dev/null
+++ b/bluez/repo/src/bluetooth.ver
@@ -0,0 +1,12 @@
+{
+	global:
+		btd_*;
+		g_dbus_*;
+		info;
+		error;
+		debug;
+		baswap;
+		ba2str;
+	local:
+		*;
+};
diff --git a/bluez/repo/src/bluetoothd.8.in b/bluez/repo/src/bluetoothd.8.in
new file mode 100644
index 0000000..97ef3ec
--- /dev/null
+++ b/bluez/repo/src/bluetoothd.8.in
@@ -0,0 +1,59 @@
+.\"
+.TH "BLUETOOTHD" "8" "March 2004" "Bluetooth daemon" "System management commands"
+.SH "NAME"
+bluetoothd \- Bluetooth daemon
+
+.SH "SYNOPSIS"
+.B bluetoothd [--version] | [--help]
+
+.B bluetoothd [--nodetach] [--compat] [--experimental] [--debug=<files>] [--plugin=<plugins>] [--noplugin=<plugins>]
+
+.SH "DESCRIPTION"
+This manual page documents briefly the
+.B bluetoothd
+daemon, which manages all the Bluetooth devices.
+.B bluetoothd
+can also provide a number of services via the D-Bus message bus
+system.
+.SH "OPTIONS"
+.TP
+.B -v, --version
+Print bluetoothd version and exit.
+.TP
+.B -h, --help
+Print bluetoothd options and exit.
+.TP
+.B -n, --nodetach
+Enable logging in foreground. Directs log output to the controlling terminal \
+in addition to syslog.
+.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 \
+information from all the source files are printed. If file options are \
+present, then only debug prints from that source file are printed. The option \
+can be a pattern containing "*" and "?" characters.
+
+Example: --debug=src/adapter.c:src/agent.c
+.TP
+.B -p, --plugin=<plugin1>,<plugin2>,..
+Load these plugins only. The option can be a pattern containing "*" and "?" \
+characters.
+.TP
+.B -P, --noplugin=<plugin1>,<plugin2>,..
+Never load these plugins. The option can be a pattern containing "*" and "?" \
+characters.
+.TP
+.B -C, --compat
+Provide deprecated command line interfaces.
+.TP
+.B -E, --experimental
+Enable experimental interfaces. Those interfaces are not guaranteed to be
+compatible or present in future releases.
+.SH "FILES"
+.TP
+.I @CONFIGDIR@/main.conf
+Location of the global configuration file.
+
+.SH "AUTHOR"
+This manual page was written by Marcel Holtmann, Philipp Matthias Hahn and Fredrik Noring.
diff --git a/bluez/repo/src/dbus-common.c b/bluez/repo/src/dbus-common.c
new file mode 100644
index 0000000..adb0a7a
--- /dev/null
+++ b/bluez/repo/src/dbus-common.c
@@ -0,0 +1,244 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2005-2007  Johan Hedberg <johan.hedberg@nokia.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus/gdbus.h"
+
+#include "log.h"
+
+#include "dbus-common.h"
+
+static DBusConnection *connection = NULL;
+
+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);
+}
+
+void dict_append_basic(DBusMessageIter *dict, int key_type, const void *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, key_type, key);
+
+	append_variant(&entry, type, val);
+
+	dbus_message_iter_close_container(dict, &entry);
+
+}
+
+void dict_append_entry(DBusMessageIter *dict,
+			const char *key, int type, void *val)
+{
+	dict_append_basic(dict, DBUS_TYPE_STRING, &key, type, val);
+}
+
+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);
+}
+
+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);
+}
+
+void set_dbus_connection(DBusConnection *conn)
+{
+	connection = conn;
+}
+
+DBusConnection *btd_get_dbus_connection(void)
+{
+	return connection;
+}
+
+const char *class_to_icon(uint32_t class)
+{
+	switch ((class & 0x1f00) >> 8) {
+	case 0x01:
+		return "computer";
+	case 0x02:
+		switch ((class & 0xfc) >> 2) {
+		case 0x01:
+		case 0x02:
+		case 0x03:
+		case 0x05:
+			return "phone";
+		case 0x04:
+			return "modem";
+		}
+		break;
+	case 0x03:
+		return "network-wireless";
+	case 0x04:
+		switch ((class & 0xfc) >> 2) {
+		case 0x01:
+		case 0x02:
+			return "audio-card";	/* Headset */
+		case 0x06:
+			return "audio-card";	/* Headphone */
+		case 0x0b: /* VCR */
+		case 0x0c: /* Video Camera */
+		case 0x0d: /* Camcorder */
+			return "camera-video";
+		default:
+			return "audio-card";	/* Other audio device */
+		}
+		break;
+	case 0x05:
+		switch ((class & 0xc0) >> 6) {
+		case 0x00:
+			switch ((class & 0x1e) >> 2) {
+			case 0x01:
+			case 0x02:
+				return "input-gaming";
+			}
+			break;
+		case 0x01:
+			return "input-keyboard";
+		case 0x02:
+			switch ((class & 0x1e) >> 2) {
+			case 0x05:
+				return "input-tablet";
+			default:
+				return "input-mouse";
+			}
+		}
+		break;
+	case 0x06:
+		if (class & 0x80)
+			return "printer";
+		if (class & 0x20)
+			return "camera-photo";
+		break;
+	}
+
+	return NULL;
+}
+
+const char *gap_appearance_to_icon(uint16_t appearance)
+{
+	switch ((appearance & 0xffc0) >> 6) {
+	case 0x00:
+		return "unknown";
+	case 0x01:
+		return "phone";
+	case 0x02:
+		return "computer";
+	case 0x05:
+		return "video-display";
+	case 0x0a:
+		return "multimedia-player";
+	case 0x0b:
+		return "scanner";
+	case 0x0f: /* HID Generic */
+		switch (appearance & 0x3f) {
+		case 0x01:
+			return "input-keyboard";
+		case 0x02:
+			return "input-mouse";
+		case 0x03:
+		case 0x04:
+			return "input-gaming";
+		case 0x05:
+			return "input-tablet";
+		case 0x08:
+			return "scanner";
+		}
+		break;
+	}
+
+	return NULL;
+}
diff --git a/bluez/repo/src/dbus-common.h b/bluez/repo/src/dbus-common.h
new file mode 100644
index 0000000..2e7d51e
--- /dev/null
+++ b/bluez/repo/src/dbus-common.h
@@ -0,0 +1,39 @@
+/* *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+void dict_append_basic(DBusMessageIter *dict, int key_type, const void *key,
+						int type, void *val);
+void dict_append_entry(DBusMessageIter *dict,
+			const char *key, int type, void *val);
+
+void dict_append_basic_array(DBusMessageIter *dict, int key_type,
+					const void *key, int type, void *val,
+					int n_elements);
+void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+			void *val, int n_elements);
+
+void set_dbus_connection(DBusConnection *conn);
+DBusConnection *btd_get_dbus_connection(void);
+
+const char *class_to_icon(uint32_t class);
+const char *gap_appearance_to_icon(uint16_t appearance);
diff --git a/bluez/repo/src/device.c b/bluez/repo/src/device.c
new file mode 100644
index 0000000..1f7c895
--- /dev/null
+++ b/bluez/repo/src/device.c
@@ -0,0 +1,6128 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <dirent.h>
+#include <time.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "log.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/gatt-server.h"
+#include "src/shared/ad.h"
+#include "btio/btio.h"
+#include "lib/mgmt.h"
+#include "attrib/att.h"
+#include "hcid.h"
+#include "adapter.h"
+#include "gatt-database.h"
+#include "attrib/gattrib.h"
+#include "attio.h"
+#include "device.h"
+#include "gatt-client.h"
+#include "profile.h"
+#include "service.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "uuid-helper.h"
+#include "sdp-client.h"
+#include "attrib/gatt.h"
+#include "agent.h"
+#include "textfile.h"
+#include "storage.h"
+#include "attrib-server.h"
+#include "eir.h"
+
+#define IO_CAPABILITY_NOINPUTNOOUTPUT	0x03
+
+#define DISCONNECT_TIMER	2
+#define DISCOVERY_TIMER		1
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define RSSI_THRESHOLD		8
+
+#define GATT_PRIM_SVC_UUID_STR "2800"
+#define GATT_SND_SVC_UUID_STR  "2801"
+#define GATT_INCLUDE_UUID_STR "2802"
+#define GATT_CHARAC_UUID_STR "2803"
+
+static DBusConnection *dbus_conn = NULL;
+static unsigned service_state_cb_id;
+
+struct btd_disconnect_data {
+	guint id;
+	disconnect_watch watch;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct bonding_req {
+	DBusMessage *msg;
+	guint listener_id;
+	struct btd_device *device;
+	uint8_t bdaddr_type;
+	struct agent *agent;
+	struct btd_adapter_pin_cb_iter *cb_iter;
+	uint8_t status;
+	guint retry_timer;
+	struct timespec attempt_start_time;
+	long last_attempt_duration_ms;
+};
+
+typedef enum {
+	AUTH_TYPE_PINCODE,
+	AUTH_TYPE_PASSKEY,
+	AUTH_TYPE_CONFIRM,
+	AUTH_TYPE_NOTIFY_PASSKEY,
+	AUTH_TYPE_NOTIFY_PINCODE,
+} auth_type_t;
+
+struct authentication_req {
+	auth_type_t type;
+	struct agent *agent;
+	struct btd_device *device;
+	uint32_t passkey;
+	char *pincode;
+	gboolean secure;
+};
+
+struct browse_req {
+	DBusMessage *msg;
+	struct btd_device *device;
+	GSList *match_uuids;
+	GSList *profiles_added;
+	sdp_list_t *records;
+	int search_uuid;
+	int reconnect_attempt;
+	guint listener_id;
+	uint16_t sdp_flags;
+};
+
+struct included_search {
+	struct browse_req *req;
+	GSList *services;
+	GSList *current;
+};
+
+struct attio_data {
+	guint id;
+	attio_connect_cb cfunc;
+	attio_disconnect_cb dcfunc;
+	gpointer user_data;
+};
+
+struct svc_callback {
+	unsigned int id;
+	guint idle_id;
+	struct btd_device *dev;
+	device_svc_cb_t func;
+	void *user_data;
+};
+
+typedef void (*attio_error_cb) (const GError *gerr, gpointer user_data);
+typedef void (*attio_success_cb) (gpointer user_data);
+
+struct att_callbacks {
+	attio_error_cb err;		/* Callback for error */
+	attio_success_cb success;	/* Callback for success */
+	gpointer user_data;
+};
+
+/* Per-bearer (LE or BR/EDR) device state */
+struct bearer_state {
+	bool paired;
+	bool bonded;
+	bool connected;
+	bool svc_resolved;
+};
+
+struct csrk_info {
+	uint8_t key[16];
+	uint32_t counter;
+};
+
+struct btd_device {
+	int ref_count;
+
+	bdaddr_t	bdaddr;
+	uint8_t		bdaddr_type;
+	char		*path;
+	bool		bredr;
+	bool		le;
+	bool		pending_paired;		/* "Paired" waiting for SDP */
+	bool		svc_refreshed;
+	GSList		*svc_callbacks;
+	GSList		*eir_uuids;
+	struct bt_ad	*ad;
+	char		name[MAX_NAME_LENGTH + 1];
+	char		*alias;
+	uint32_t	class;
+	uint16_t	vendor_src;
+	uint16_t	vendor;
+	uint16_t	product;
+	uint16_t	version;
+	uint16_t	appearance;
+	char		*modalias;
+	struct btd_adapter	*adapter;
+	GSList		*uuids;
+	GSList		*primaries;		/* List of primary services */
+	GSList		*services;		/* List of btd_service */
+	GSList		*pending;		/* Pending services */
+	GSList		*watches;		/* List of disconnect_data */
+	bool		temporary;
+	guint		disconn_timer;
+	guint		discov_timer;
+	struct browse_req *browse;		/* service discover request */
+	struct bonding_req *bonding;
+	struct authentication_req *authr;	/* authentication request */
+	GSList		*disconnects;		/* disconnects message */
+	DBusMessage	*connect;		/* connect message */
+	DBusMessage	*disconnect;		/* disconnect message */
+	GAttrib		*attrib;
+	GSList		*attios;
+	GSList		*attios_offline;
+
+	struct bt_att *att;			/* The new ATT transport */
+	uint16_t att_mtu;			/* The ATT MTU */
+	unsigned int att_disconn_id;
+
+	/*
+	 * TODO: For now, device creates and owns the client-role gatt_db, but
+	 * this needs to be persisted in a more central place so that proper
+	 * attribute cache support can be built.
+	 */
+	struct gatt_db *db;			/* GATT db cache */
+	bool gatt_cache_used;			/* true if discovery skipped */
+	struct bt_gatt_client *client;		/* GATT client instance */
+	struct bt_gatt_server *server;		/* GATT server instance */
+
+	struct btd_gatt_client *client_dbus;
+
+	struct bearer_state bredr_state;
+	struct bearer_state le_state;
+
+	struct csrk_info *local_csrk;
+	struct csrk_info *remote_csrk;
+
+	sdp_list_t	*tmp_records;
+
+	time_t		bredr_seen;
+	time_t		le_seen;
+
+	gboolean	trusted;
+	gboolean	blocked;
+	gboolean	auto_connect;
+	gboolean	disable_auto_connect;
+	gboolean	general_connect;
+
+	bool		legacy;
+	int8_t		rssi;
+	int8_t		tx_power;
+
+	GIOChannel	*att_io;
+	guint		store_id;
+};
+
+static const uint16_t uuid_list[] = {
+	L2CAP_UUID,
+	PNP_INFO_SVCLASS_ID,
+	PUBLIC_BROWSE_GROUP,
+	0
+};
+
+static int device_browse_gatt(struct btd_device *device, DBusMessage *msg);
+static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);
+
+static struct bearer_state *get_state(struct btd_device *dev,
+							uint8_t bdaddr_type)
+{
+	if (bdaddr_type == BDADDR_BREDR)
+		return &dev->bredr_state;
+	else
+		return &dev->le_state;
+}
+
+static GSList *find_service_with_profile(GSList *list, struct btd_profile *p)
+{
+	GSList *l;
+
+	for (l = list; l != NULL; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+
+		if (btd_service_get_profile(service) == p)
+			return l;
+	}
+
+	return NULL;
+}
+
+static GSList *find_service_with_state(GSList *list,
+						btd_service_state_t state)
+{
+	GSList *l;
+
+	for (l = list; l != NULL; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+
+		if (btd_service_get_state(service) == state)
+			return l;
+	}
+
+	return NULL;
+}
+
+static GSList *find_service_with_uuid(GSList *list, char *uuid)
+{
+	GSList *l;
+
+	for (l = list; l != NULL; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+		struct btd_profile *profile = btd_service_get_profile(service);
+
+		if (bt_uuid_strcmp(profile->remote_uuid, uuid) == 0)
+			return l;
+	}
+
+	return NULL;
+}
+
+static void update_technologies(GKeyFile *file, struct btd_device *dev)
+{
+	const char *list[2];
+	size_t len = 0;
+
+	if (dev->bredr)
+		list[len++] = "BR/EDR";
+
+	if (dev->le) {
+		const char *type;
+
+		if (dev->bdaddr_type == BDADDR_LE_PUBLIC)
+			type = "public";
+		else
+			type = "static";
+
+		g_key_file_set_string(file, "General", "AddressType", type);
+
+		list[len++] = "LE";
+	}
+
+	g_key_file_set_string_list(file, "General", "SupportedTechnologies",
+								list, len);
+}
+
+static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
+							const char *group)
+{
+	char key[33];
+	int i;
+
+	for (i = 0; i < 16; i++)
+		sprintf(key + (i * 2), "%2.2X", csrk->key[i]);
+
+	g_key_file_set_string(key_file, group, "Key", key);
+	g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
+}
+
+static gboolean store_device_info_cb(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+	GKeyFile *key_file;
+	char filename[PATH_MAX];
+	char adapter_addr[18];
+	char device_addr[18];
+	char *str;
+	char class[9];
+	char **uuids = NULL;
+	gsize length = 0;
+
+	device->store_id = 0;
+
+	ba2str(btd_adapter_get_address(device->adapter), adapter_addr);
+	ba2str(&device->bdaddr, device_addr);
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+			device_addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	g_key_file_set_string(key_file, "General", "Name", device->name);
+
+	if (device->alias != NULL)
+		g_key_file_set_string(key_file, "General", "Alias",
+								device->alias);
+	else
+		g_key_file_remove_key(key_file, "General", "Alias", NULL);
+
+	if (device->class) {
+		sprintf(class, "0x%6.6x", device->class);
+		g_key_file_set_string(key_file, "General", "Class", class);
+	} else {
+		g_key_file_remove_key(key_file, "General", "Class", NULL);
+	}
+
+	if (device->appearance) {
+		sprintf(class, "0x%4.4x", device->appearance);
+		g_key_file_set_string(key_file, "General", "Appearance", class);
+	} else {
+		g_key_file_remove_key(key_file, "General", "Appearance", NULL);
+	}
+
+	update_technologies(key_file, device);
+
+	g_key_file_set_boolean(key_file, "General", "Trusted",
+							device->trusted);
+
+	g_key_file_set_boolean(key_file, "General", "Blocked",
+							device->blocked);
+
+	if (device->uuids) {
+		GSList *l;
+		int i;
+
+		uuids = g_new0(char *, g_slist_length(device->uuids) + 1);
+		for (i = 0, l = device->uuids; l; l = g_slist_next(l), i++)
+			uuids[i] = l->data;
+		g_key_file_set_string_list(key_file, "General", "Services",
+						(const char **)uuids, i);
+	} else {
+		g_key_file_remove_key(key_file, "General", "Services", NULL);
+	}
+
+	if (device->vendor_src) {
+		g_key_file_set_integer(key_file, "DeviceID", "Source",
+					device->vendor_src);
+		g_key_file_set_integer(key_file, "DeviceID", "Vendor",
+					device->vendor);
+		g_key_file_set_integer(key_file, "DeviceID", "Product",
+					device->product);
+		g_key_file_set_integer(key_file, "DeviceID", "Version",
+					device->version);
+	} else {
+		g_key_file_remove_group(key_file, "DeviceID", NULL);
+	}
+
+	if (device->local_csrk)
+		store_csrk(device->local_csrk, key_file, "LocalSignatureKey");
+
+	if (device->remote_csrk)
+		store_csrk(device->remote_csrk, key_file, "RemoteSignatureKey");
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+	g_free(uuids);
+
+	return FALSE;
+}
+
+static bool device_address_is_private(struct btd_device *dev)
+{
+	if (dev->bdaddr_type != BDADDR_LE_RANDOM)
+		return false;
+
+	switch (dev->bdaddr.b[5] >> 6) {
+	case 0x00:	/* Private non-resolvable */
+	case 0x01:	/* Private resolvable */
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void store_device_info(struct btd_device *device)
+{
+	if (device->temporary || device->store_id > 0)
+		return;
+
+	if (device_address_is_private(device)) {
+		warn("Can't store info for private addressed device %s",
+								device->path);
+		return;
+	}
+
+	device->store_id = g_idle_add(store_device_info_cb, device);
+}
+
+void device_store_cached_name(struct btd_device *dev, const char *name)
+{
+	char filename[PATH_MAX];
+	char s_addr[18], d_addr[18];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+
+	if (device_address_is_private(dev)) {
+		warn("Can't store name for private addressed device %s",
+								dev->path);
+		return;
+	}
+
+	ba2str(btd_adapter_get_address(dev->adapter), s_addr);
+	ba2str(&dev->bdaddr, d_addr);
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", s_addr, d_addr);
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	g_key_file_set_string(key_file, "General", "Name", name);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, data, length, NULL);
+	g_free(data);
+
+	g_key_file_free(key_file);
+}
+
+static void browse_request_free(struct browse_req *req)
+{
+	if (req->listener_id)
+		g_dbus_remove_watch(dbus_conn, req->listener_id);
+	if (req->msg)
+		dbus_message_unref(req->msg);
+	g_slist_free_full(req->profiles_added, g_free);
+	if (req->records)
+		sdp_list_free(req->records, (sdp_free_func_t) sdp_record_free);
+
+	g_free(req);
+}
+
+static void gatt_client_cleanup(struct btd_device *device)
+{
+	if (!device->client)
+		return;
+
+	bt_gatt_client_set_service_changed(device->client, NULL, NULL, NULL);
+	bt_gatt_client_set_ready_handler(device->client, NULL, NULL, NULL);
+	bt_gatt_client_unref(device->client);
+	device->client = NULL;
+}
+
+static void gatt_server_cleanup(struct btd_device *device)
+{
+	if (!device->server)
+		return;
+
+	bt_gatt_server_unref(device->server);
+	device->server = NULL;
+}
+
+static void attio_cleanup(struct btd_device *device)
+{
+	if (device->att_disconn_id)
+		bt_att_unregister_disconnect(device->att,
+							device->att_disconn_id);
+
+	if (device->att_io) {
+		g_io_channel_shutdown(device->att_io, FALSE, NULL);
+		g_io_channel_unref(device->att_io);
+		device->att_io = NULL;
+	}
+
+	gatt_client_cleanup(device);
+	gatt_server_cleanup(device);
+
+	if (device->att) {
+		bt_att_unref(device->att);
+		device->att = NULL;
+	}
+
+	if (device->attrib) {
+		GAttrib *attrib = device->attrib;
+
+		device->attrib = NULL;
+		g_attrib_cancel_all(attrib);
+		g_attrib_unref(attrib);
+	}
+}
+
+static void browse_request_cancel(struct browse_req *req)
+{
+	struct btd_device *device = req->device;
+	struct btd_adapter *adapter = device->adapter;
+
+	DBG("");
+
+	bt_cancel_discovery(btd_adapter_get_address(adapter), &device->bdaddr);
+
+	attio_cleanup(device);
+
+	device->browse = NULL;
+	browse_request_free(req);
+}
+
+static void svc_dev_remove(gpointer user_data)
+{
+	struct svc_callback *cb = user_data;
+
+	if (cb->idle_id > 0)
+		g_source_remove(cb->idle_id);
+
+	cb->func(cb->dev, -ENODEV, cb->user_data);
+
+	g_free(cb);
+}
+
+static void device_free(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+
+	btd_gatt_client_destroy(device->client_dbus);
+	device->client_dbus = NULL;
+
+	g_slist_free_full(device->uuids, g_free);
+	g_slist_free_full(device->primaries, g_free);
+	g_slist_free_full(device->attios, g_free);
+	g_slist_free_full(device->attios_offline, g_free);
+	g_slist_free_full(device->svc_callbacks, svc_dev_remove);
+
+	/* Reset callbacks since the device is going to be freed */
+	gatt_db_register(device->db, NULL, NULL, NULL, NULL);
+
+	attio_cleanup(device);
+
+	gatt_db_unref(device->db);
+
+	bt_ad_unref(device->ad);
+
+	if (device->tmp_records)
+		sdp_list_free(device->tmp_records,
+					(sdp_free_func_t) sdp_record_free);
+
+	if (device->disconn_timer)
+		g_source_remove(device->disconn_timer);
+
+	if (device->discov_timer)
+		g_source_remove(device->discov_timer);
+
+	if (device->connect)
+		dbus_message_unref(device->connect);
+
+	if (device->disconnect)
+		dbus_message_unref(device->disconnect);
+
+	DBG("%p", device);
+
+	if (device->authr) {
+		if (device->authr->agent)
+			agent_unref(device->authr->agent);
+		g_free(device->authr->pincode);
+		g_free(device->authr);
+	}
+
+	if (device->eir_uuids)
+		g_slist_free_full(device->eir_uuids, g_free);
+
+	g_free(device->local_csrk);
+	g_free(device->remote_csrk);
+	g_free(device->path);
+	g_free(device->alias);
+	free(device->modalias);
+	g_free(device);
+}
+
+bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type)
+{
+	struct bearer_state *state = get_state(device, bdaddr_type);
+
+	return state->paired;
+}
+
+bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type)
+{
+	struct bearer_state *state = get_state(device, bdaddr_type);
+
+	return state->bonded;
+}
+
+gboolean device_is_trusted(struct btd_device *device)
+{
+	return device->trusted;
+}
+
+static gboolean dev_property_get_address(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	char dstaddr[18];
+	const char *ptr = dstaddr;
+
+	ba2str(&device->bdaddr, dstaddr);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	const char *ptr = device->name;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+	return TRUE;
+}
+
+static gboolean dev_property_exists_name(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *dev = data;
+
+	return device_name_known(dev);
+}
+
+static gboolean dev_property_get_alias(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	char dstaddr[18];
+	const char *ptr;
+
+	/* Alias (fallback to name or address) */
+	if (device->alias != NULL)
+		ptr = device->alias;
+	else if (strlen(device->name) > 0) {
+		ptr = device->name;
+	} else {
+		ba2str(&device->bdaddr, dstaddr);
+		g_strdelimit(dstaddr, ":", '-');
+		ptr = dstaddr;
+	}
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+	return TRUE;
+}
+
+static void set_alias(GDBusPendingPropertySet id, const char *alias,
+								void *data)
+{
+	struct btd_device *device = data;
+
+	/* No change */
+	if ((device->alias == NULL && g_str_equal(alias, "")) ||
+					g_strcmp0(device->alias, alias) == 0) {
+		g_dbus_pending_property_success(id);
+		return;
+	}
+
+	g_free(device->alias);
+	device->alias = g_str_equal(alias, "") ? NULL : g_strdup(alias);
+
+	store_device_info(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Alias");
+
+	g_dbus_pending_property_success(id);
+}
+
+static void dev_property_set_alias(const GDBusPropertyTable *property,
+					DBusMessageIter *value,
+					GDBusPendingPropertySet id, void *data)
+{
+	const char *alias;
+
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(value, &alias);
+
+	set_alias(id, alias, data);
+}
+
+static gboolean dev_property_exists_class(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *device = data;
+
+	return device->class != 0;
+}
+
+static gboolean dev_property_get_class(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+
+	if (device->class == 0)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &device->class);
+
+	return TRUE;
+}
+
+static gboolean get_appearance(const GDBusPropertyTable *property, void *data,
+							uint16_t *appearance)
+{
+	struct btd_device *device = data;
+
+	if (dev_property_exists_class(property, data))
+		return FALSE;
+
+	if (device->appearance) {
+		*appearance = device->appearance;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean dev_property_exists_appearance(
+			const GDBusPropertyTable *property, void *data)
+{
+	uint16_t appearance;
+
+	return get_appearance(property, data, &appearance);
+}
+
+static gboolean dev_property_get_appearance(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	uint16_t appearance;
+
+	if (!get_appearance(property, data, &appearance))
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &appearance);
+
+	return TRUE;
+}
+
+static const char *get_icon(const GDBusPropertyTable *property, void *data)
+{
+	struct btd_device *device = data;
+	const char *icon = NULL;
+	uint16_t appearance;
+
+	if (device->class != 0)
+		icon = class_to_icon(device->class);
+	else if (get_appearance(property, data, &appearance))
+		icon = gap_appearance_to_icon(appearance);
+
+	return icon;
+}
+
+static gboolean dev_property_exists_icon(
+			const GDBusPropertyTable *property, void *data)
+{
+	return get_icon(property, data) != NULL;
+}
+
+static gboolean dev_property_get_icon(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	const char *icon;
+
+	icon = get_icon(property, data);
+	if (icon == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &icon);
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_paired(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *dev = data;
+	dbus_bool_t val;
+
+	if (dev->bredr_state.paired || dev->le_state.paired)
+		val = TRUE;
+	else
+		val = FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_legacy(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	dbus_bool_t val = device->legacy;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_rssi(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *dev = data;
+	dbus_int16_t val = dev->rssi;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val);
+
+	return TRUE;
+}
+
+static gboolean dev_property_exists_rssi(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *dev = data;
+
+	if (dev->rssi == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_tx_power(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *dev = data;
+	dbus_int16_t val = dev->tx_power;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val);
+
+	return TRUE;
+}
+
+static gboolean dev_property_exists_tx_power(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *dev = data;
+
+	if (dev->tx_power == 127)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void append_service_path(const char *path, void *user_data)
+{
+	DBusMessageIter *array = user_data;
+
+	dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static gboolean dev_property_get_gatt_services(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *dev = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+	btd_gatt_client_foreach_service(dev->client_dbus, append_service_path,
+									&array);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean dev_property_exists_gatt_services(
+					const GDBusPropertyTable *property,
+					void *data)
+{
+	struct btd_device *dev = data;
+
+	if (!dev->client || !bt_gatt_client_is_ready(dev->client))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_trusted(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	gboolean val = device_is_trusted(device);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+	return TRUE;
+}
+
+static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data)
+{
+	struct btd_device *device = data;
+
+	btd_device_set_trusted(device, value);
+
+	g_dbus_pending_property_success(id);
+}
+
+static void dev_property_set_trusted(const GDBusPropertyTable *property,
+					DBusMessageIter *value,
+					GDBusPendingPropertySet id, void *data)
+{
+	dbus_bool_t b;
+
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(value, &b);
+
+	set_trust(id, b, data);
+}
+
+static gboolean dev_property_get_blocked(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+							&device->blocked);
+
+	return TRUE;
+}
+
+static void set_blocked(GDBusPendingPropertySet id, gboolean value, void *data)
+{
+	struct btd_device *device = data;
+	int err;
+
+	if (value)
+		err = device_block(device, FALSE);
+	else
+		err = device_unblock(device, FALSE, FALSE);
+
+	switch (-err) {
+	case 0:
+		g_dbus_pending_property_success(id);
+		break;
+	case EINVAL:
+		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+					"Kernel lacks blacklist support");
+		break;
+	default:
+		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed",
+							strerror(-err));
+		break;
+	}
+}
+
+
+static void dev_property_set_blocked(const GDBusPropertyTable *property,
+					DBusMessageIter *value,
+					GDBusPendingPropertySet id, void *data)
+{
+	dbus_bool_t b;
+
+	if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(value, &b);
+
+	set_blocked(id, b, data);
+}
+
+static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *dev = data;
+	dbus_bool_t connected;
+
+	if (dev->bredr_state.connected || dev->le_state.connected)
+		connected = TRUE;
+	else
+		connected = FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_uuids(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *dev = data;
+	DBusMessageIter entry;
+	GSList *l;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_STRING_AS_STRING, &entry);
+
+	if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved)
+		l = dev->uuids;
+	else if (dev->eir_uuids)
+		l = dev->eir_uuids;
+	else
+		l = dev->uuids;
+
+	for (; l != NULL; l = l->next)
+		dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+							&l->data);
+
+	dbus_message_iter_close_container(iter, &entry);
+
+	return TRUE;
+}
+
+static gboolean dev_property_get_modalias(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+
+	if (!device->modalias)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+							&device->modalias);
+
+	return TRUE;
+}
+
+static gboolean dev_property_exists_modalias(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *device = data;
+
+	return device->modalias ? TRUE : FALSE;
+}
+
+static gboolean dev_property_get_adapter(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	const char *str = adapter_get_path(device->adapter);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
+
+	return TRUE;
+}
+
+static void append_manufacturer_data(void *data, void *user_data)
+{
+	struct bt_ad_manufacturer_data *md = data;
+	DBusMessageIter *dict = user_data;
+
+	dict_append_basic_array(dict, DBUS_TYPE_UINT16, &md->manufacturer_id,
+				DBUS_TYPE_BYTE, &md->data, md->len);
+}
+
+static gboolean
+dev_property_get_manufacturer_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_UINT16_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	bt_ad_foreach_manufacturer_data(device->ad, append_manufacturer_data,
+									&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static gboolean
+dev_property_manufacturer_data_exist(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *device = data;
+
+	return bt_ad_has_manufacturer_data(device->ad, NULL);
+}
+
+static void append_service_data(void *data, void *user_data)
+{
+	struct bt_ad_service_data *sd = data;
+	DBusMessageIter *dict = user_data;
+	char uuid_str[MAX_LEN_UUID_STR];
+
+	bt_uuid_to_string(&sd->uuid, uuid_str, sizeof(uuid_str));
+
+	dict_append_array(dict, uuid_str, DBUS_TYPE_BYTE, &sd->data, sd->len);
+}
+
+static gboolean
+dev_property_get_service_data(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = 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);
+
+	bt_ad_foreach_service_data(device->ad, append_service_data, &dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static gboolean
+dev_property_service_data_exist(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_device *device = data;
+
+	return bt_ad_has_service_data(device->ad, NULL);
+}
+
+static gboolean disconnect_all(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+
+	device->disconn_timer = 0;
+
+	if (device->bredr_state.connected)
+		btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+								BDADDR_BREDR);
+
+	if (device->le_state.connected)
+		btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+							device->bdaddr_type);
+
+	return FALSE;
+}
+
+int device_block(struct btd_device *device, gboolean update_only)
+{
+	int err = 0;
+
+	if (device->blocked)
+		return 0;
+
+	disconnect_all(device);
+
+	while (device->services != NULL) {
+		struct btd_service *service = device->services->data;
+
+		device->services = g_slist_remove(device->services, service);
+		service_remove(service);
+	}
+
+	if (!update_only) {
+		if (device->le)
+			err = btd_adapter_block_address(device->adapter,
+							&device->bdaddr,
+							device->bdaddr_type);
+		if (!err && device->bredr)
+			err = btd_adapter_block_address(device->adapter,
+							&device->bdaddr,
+							BDADDR_BREDR);
+	}
+
+	if (err < 0)
+		return err;
+
+	device->blocked = TRUE;
+
+	store_device_info(device);
+
+	btd_device_set_temporary(device, false);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Blocked");
+
+	return 0;
+}
+
+int device_unblock(struct btd_device *device, gboolean silent,
+							gboolean update_only)
+{
+	int err = 0;
+
+	if (!device->blocked)
+		return 0;
+
+	if (!update_only) {
+		if (device->le)
+			err = btd_adapter_unblock_address(device->adapter,
+							&device->bdaddr,
+							device->bdaddr_type);
+		if (!err && device->bredr)
+			err = btd_adapter_unblock_address(device->adapter,
+							&device->bdaddr,
+							BDADDR_BREDR);
+	}
+
+	if (err < 0)
+		return err;
+
+	device->blocked = FALSE;
+
+	store_device_info(device);
+
+	if (!silent) {
+		g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Blocked");
+		device_probe_profiles(device, device->uuids);
+	}
+
+	return 0;
+}
+
+static void browse_request_exit(DBusConnection *conn, void *user_data)
+{
+	struct browse_req *req = user_data;
+
+	DBG("Requestor exited");
+
+	browse_request_cancel(req);
+}
+
+static void bonding_request_cancel(struct bonding_req *bonding)
+{
+	struct btd_device *device = bonding->device;
+	struct btd_adapter *adapter = device->adapter;
+
+	adapter_cancel_bonding(adapter, &device->bdaddr, device->bdaddr_type);
+}
+
+static void dev_disconn_service(gpointer a, gpointer b)
+{
+	btd_service_disconnect(a);
+}
+
+void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
+{
+	if (device->bonding)
+		bonding_request_cancel(device->bonding);
+
+	if (device->browse)
+		browse_request_cancel(device->browse);
+
+	if (device->att_io) {
+		g_io_channel_shutdown(device->att_io, FALSE, NULL);
+		g_io_channel_unref(device->att_io);
+		device->att_io = NULL;
+	}
+
+	if (device->connect) {
+		DBusMessage *reply = btd_error_failed(device->connect,
+								"Cancelled");
+		g_dbus_send_message(dbus_conn, reply);
+		dbus_message_unref(device->connect);
+		device->connect = NULL;
+	}
+
+	if (btd_device_is_connected(device) && msg)
+		device->disconnects = g_slist_append(device->disconnects,
+						dbus_message_ref(msg));
+
+	if (device->disconn_timer)
+		return;
+
+	g_slist_foreach(device->services, dev_disconn_service, NULL);
+
+	g_slist_free(device->pending);
+	device->pending = NULL;
+
+	while (device->watches) {
+		struct btd_disconnect_data *data = device->watches->data;
+
+		if (data->watch)
+			/* temporary is set if device is going to be removed */
+			data->watch(device, device->temporary,
+							data->user_data);
+
+		/* Check if the watch has been removed by callback function */
+		if (!g_slist_find(device->watches, data))
+			continue;
+
+		device->watches = g_slist_remove(device->watches, data);
+		g_free(data);
+	}
+
+	if (!btd_device_is_connected(device)) {
+		if (msg)
+			g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+		return;
+	}
+
+	device->disconn_timer = g_timeout_add_seconds(DISCONNECT_TIMER,
+							disconnect_all,
+							device);
+}
+
+bool device_is_disconnecting(struct btd_device *device)
+{
+	return device->disconn_timer > 0;
+}
+
+static void device_set_auto_connect(struct btd_device *device, gboolean enable)
+{
+	char addr[18];
+
+	if (!device || !device->le)
+		return;
+
+	ba2str(&device->bdaddr, addr);
+
+	DBG("%s auto connect: %d", addr, enable);
+
+	if (device->auto_connect == enable)
+		return;
+
+	device->auto_connect = enable;
+
+	/* Disabling auto connect */
+	if (enable == FALSE) {
+		adapter_connect_list_remove(device->adapter, device);
+		adapter_auto_connect_remove(device->adapter, device);
+		return;
+	}
+
+	/* Enabling auto connect */
+	adapter_auto_connect_add(device->adapter, device);
+
+	if (device->attrib) {
+		DBG("Already connected");
+		return;
+	}
+
+	adapter_connect_list_add(device->adapter, device);
+}
+
+static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	/*
+	 * Disable connections through passive scanning until
+	 * Device1.Connect is called
+	 */
+	if (device->auto_connect) {
+		device->disable_auto_connect = TRUE;
+		device_set_auto_connect(device, FALSE);
+	}
+
+	device_request_disconnect(device, msg);
+
+	return NULL;
+}
+
+static int connect_next(struct btd_device *dev)
+{
+	struct btd_service *service;
+	int err = -ENOENT;
+
+	while (dev->pending) {
+		service = dev->pending->data;
+
+		err = btd_service_connect(service);
+		if (!err)
+			return 0;
+
+		dev->pending = g_slist_delete_link(dev->pending, dev->pending);
+	}
+
+	return err;
+}
+
+static void device_profile_connected(struct btd_device *dev,
+					struct btd_profile *profile, int err)
+{
+	struct btd_service *pending;
+	GSList *l;
+
+	DBG("%s %s (%d)", profile->name, strerror(-err), -err);
+
+	if (!err)
+		btd_device_set_temporary(dev, false);
+
+	if (dev->pending == NULL)
+		goto done;
+
+	if (!btd_device_is_connected(dev)) {
+		switch (-err) {
+		case EHOSTDOWN: /* page timeout */
+		case EHOSTUNREACH: /* adapter not powered */
+		case ECONNABORTED: /* adapter powered down */
+			goto done;
+		}
+	}
+
+
+	pending = dev->pending->data;
+	l = find_service_with_profile(dev->pending, profile);
+	if (l != NULL)
+		dev->pending = g_slist_delete_link(dev->pending, l);
+
+	/* Only continue connecting the next profile if it matches the first
+	 * pending, otherwise it will trigger another connect to the same
+	 * profile
+	 */
+	if (profile != btd_service_get_profile(pending))
+		return;
+
+	if (connect_next(dev) == 0)
+		return;
+
+done:
+	g_slist_free(dev->pending);
+	dev->pending = NULL;
+
+	if (!dev->connect)
+		return;
+
+	if (!err && dbus_message_is_method_call(dev->connect, DEVICE_INTERFACE,
+								"Connect"))
+		dev->general_connect = TRUE;
+
+	DBG("returning response to %s", dbus_message_get_sender(dev->connect));
+
+	l = find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED);
+
+	if (err && l == NULL)
+		g_dbus_send_message(dbus_conn,
+				btd_error_failed(dev->connect, strerror(-err)));
+	else {
+		/* Start passive SDP discovery to update known services */
+		if (dev->bredr && !dev->svc_refreshed)
+			device_browse_sdp(dev, NULL);
+		g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
+	}
+
+	dbus_message_unref(dev->connect);
+	dev->connect = NULL;
+}
+
+void device_add_eir_uuids(struct btd_device *dev, GSList *uuids)
+{
+	GSList *l;
+	bool added = false;
+
+	if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved)
+		return;
+
+	for (l = uuids; l != NULL; l = l->next) {
+		const char *str = l->data;
+		if (g_slist_find_custom(dev->eir_uuids, str, bt_uuid_strcmp))
+			continue;
+		added = true;
+		dev->eir_uuids = g_slist_append(dev->eir_uuids, g_strdup(str));
+	}
+
+	if (added)
+		g_dbus_emit_property_changed(dbus_conn, dev->path,
+						DEVICE_INTERFACE, "UUIDs");
+}
+
+static void add_manufacturer_data(void *data, void *user_data)
+{
+	struct eir_msd *msd = data;
+	struct btd_device *dev = user_data;
+
+	if (!bt_ad_add_manufacturer_data(dev->ad, msd->company, msd->data,
+								msd->data_len))
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, dev->path,
+					DEVICE_INTERFACE, "ManufacturerData");
+}
+
+void device_set_manufacturer_data(struct btd_device *dev, GSList *list)
+{
+	g_slist_foreach(list, add_manufacturer_data, dev);
+}
+
+static void add_service_data(void *data, void *user_data)
+{
+	struct eir_sd *sd = data;
+	struct btd_device *dev = user_data;
+	bt_uuid_t uuid;
+
+	if (bt_string_to_uuid(&uuid, sd->uuid) < 0)
+		return;
+
+	if (!bt_ad_add_service_data(dev->ad, &uuid, sd->data, sd->data_len))
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, dev->path,
+					DEVICE_INTERFACE, "ServiceData");
+}
+
+void device_set_service_data(struct btd_device *dev, GSList *list)
+{
+	g_slist_foreach(list, add_service_data, dev);
+}
+
+static struct btd_service *find_connectable_service(struct btd_device *dev,
+							const char *uuid)
+{
+	GSList *l;
+
+	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+		struct btd_profile *p = btd_service_get_profile(service);
+
+		if (!p->connect || !p->remote_uuid)
+			continue;
+
+		if (strcasecmp(uuid, p->remote_uuid) == 0)
+			return service;
+	}
+
+	return NULL;
+}
+
+static int service_prio_cmp(gconstpointer a, gconstpointer b)
+{
+	struct btd_profile *p1 = btd_service_get_profile(a);
+	struct btd_profile *p2 = btd_service_get_profile(b);
+
+	return p2->priority - p1->priority;
+}
+
+static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
+{
+	struct btd_service *service;
+	struct btd_profile *p;
+	GSList *l;
+
+	if (uuid) {
+		service = find_connectable_service(dev, uuid);
+		if (service)
+			return g_slist_prepend(dev->pending, service);
+
+		return dev->pending;
+	}
+
+	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
+		service = l->data;
+		p = btd_service_get_profile(service);
+
+		if (!p->auto_connect)
+			continue;
+
+		if (g_slist_find(dev->pending, service))
+			continue;
+
+		if (btd_service_get_state(service) !=
+						BTD_SERVICE_STATE_DISCONNECTED)
+			continue;
+
+		dev->pending = g_slist_insert_sorted(dev->pending, service,
+							service_prio_cmp);
+	}
+
+	return dev->pending;
+}
+
+int btd_device_connect_services(struct btd_device *dev, GSList *services)
+{
+	GSList *l;
+
+	if (dev->pending || dev->connect || dev->browse)
+		return -EBUSY;
+
+	if (!btd_adapter_get_powered(dev->adapter))
+		return -ENETDOWN;
+
+	if (!dev->bredr_state.svc_resolved)
+		return -ENOENT;
+
+	for (l = services; l; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+
+		dev->pending = g_slist_append(dev->pending, service);
+	}
+
+	return connect_next(dev);
+}
+
+static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
+					DBusMessage *msg, const char *uuid)
+{
+	struct bearer_state *state = get_state(dev, bdaddr_type);
+	int err;
+
+	DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)",
+						dbus_message_get_sender(msg));
+
+	if (dev->pending || dev->connect || dev->browse)
+		return btd_error_in_progress(msg);
+
+	if (!btd_adapter_get_powered(dev->adapter))
+		return btd_error_not_ready(msg);
+
+	btd_device_set_temporary(dev, false);
+
+	if (!state->svc_resolved)
+		goto resolve_services;
+
+	dev->pending = create_pending_list(dev, uuid);
+	if (!dev->pending) {
+		if (dev->svc_refreshed) {
+			if (find_service_with_state(dev->services,
+						BTD_SERVICE_STATE_CONNECTED))
+				return dbus_message_new_method_return(msg);
+			else
+				return btd_error_not_available(msg);
+		}
+
+		goto resolve_services;
+	}
+
+	err = connect_next(dev);
+	if (err < 0) {
+		if (err == -EALREADY)
+			return dbus_message_new_method_return(msg);
+		return btd_error_failed(msg, strerror(-err));
+	}
+
+	dev->connect = dbus_message_ref(msg);
+
+	return NULL;
+
+resolve_services:
+	DBG("Resolving services for %s", dev->path);
+
+	if (bdaddr_type == BDADDR_BREDR)
+		err = device_browse_sdp(dev, msg);
+	else
+		err = device_browse_gatt(dev, msg);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return NULL;
+}
+
+#define NVAL_TIME ((time_t) -1)
+#define SEEN_TRESHHOLD 300
+
+static uint8_t select_conn_bearer(struct btd_device *dev)
+{
+	time_t bredr_last = NVAL_TIME, le_last = NVAL_TIME;
+	time_t current = time(NULL);
+
+	if (dev->bredr_seen) {
+		bredr_last = current - dev->bredr_seen;
+		if (bredr_last > SEEN_TRESHHOLD)
+			bredr_last = NVAL_TIME;
+	}
+
+	if (dev->le_seen) {
+		le_last = current - dev->le_seen;
+		if (le_last > SEEN_TRESHHOLD)
+			le_last = NVAL_TIME;
+	}
+
+	if (le_last == NVAL_TIME && bredr_last == NVAL_TIME)
+		return dev->bdaddr_type;
+
+	if (dev->bredr && (!dev->le || le_last == NVAL_TIME))
+		return BDADDR_BREDR;
+
+	if (dev->le && (!dev->bredr || bredr_last == NVAL_TIME))
+		return dev->bdaddr_type;
+
+	if (bredr_last < le_last)
+		return BDADDR_BREDR;
+
+	return dev->bdaddr_type;
+}
+
+static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct btd_device *dev = user_data;
+	uint8_t bdaddr_type;
+
+	if (dev->bredr_state.connected)
+		bdaddr_type = dev->bdaddr_type;
+	else if (dev->le_state.connected && dev->bredr)
+		bdaddr_type = BDADDR_BREDR;
+	else
+		bdaddr_type = select_conn_bearer(dev);
+
+	if (bdaddr_type != BDADDR_BREDR) {
+		int err;
+
+		if (dev->le_state.connected)
+			return dbus_message_new_method_return(msg);
+
+		btd_device_set_temporary(dev, false);
+
+		if (dev->disable_auto_connect) {
+			dev->disable_auto_connect = FALSE;
+			device_set_auto_connect(dev, TRUE);
+		}
+
+		err = device_connect_le(dev);
+		if (err < 0)
+			return btd_error_failed(msg, strerror(-err));
+
+		dev->connect = dbus_message_ref(msg);
+
+		return NULL;
+	}
+
+	return connect_profiles(dev, bdaddr_type, msg, NULL);
+}
+
+static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct btd_device *dev = user_data;
+	const char *pattern;
+	char *uuid;
+	DBusMessage *reply;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	uuid = bt_name2string(pattern);
+	reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
+	free(uuid);
+
+	return reply;
+}
+
+static void device_profile_disconnected(struct btd_device *dev,
+					struct btd_profile *profile, int err)
+{
+	if (!dev->disconnect)
+		return;
+
+	if (err)
+		g_dbus_send_message(dbus_conn,
+					btd_error_failed(dev->disconnect,
+							strerror(-err)));
+	else
+		g_dbus_send_reply(dbus_conn, dev->disconnect,
+							DBUS_TYPE_INVALID);
+
+	dbus_message_unref(dev->disconnect);
+	dev->disconnect = NULL;
+}
+
+static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct btd_device *dev = user_data;
+	struct btd_service *service;
+	const char *pattern;
+	char *uuid;
+	int err;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	uuid = bt_name2string(pattern);
+	if (uuid == NULL)
+		return btd_error_invalid_args(msg);
+
+	service = find_connectable_service(dev, uuid);
+	free(uuid);
+
+	if (!service)
+		return btd_error_invalid_args(msg);
+
+	if (dev->disconnect)
+		return btd_error_in_progress(msg);
+
+	dev->disconnect = dbus_message_ref(msg);
+
+	err = btd_service_disconnect(service);
+	if (err == 0)
+		return NULL;
+
+	dbus_message_unref(dev->disconnect);
+	dev->disconnect = NULL;
+
+	if (err == -ENOTSUP)
+		return btd_error_not_supported(msg);
+
+	return btd_error_failed(msg, strerror(-err));
+}
+
+static void store_services(struct btd_device *device)
+{
+	struct btd_adapter *adapter = device->adapter;
+	char filename[PATH_MAX];
+	char src_addr[18], dst_addr[18];
+	uuid_t uuid;
+	char *prim_uuid;
+	GKeyFile *key_file;
+	GSList *l;
+	char *data;
+	gsize length = 0;
+
+	if (device_address_is_private(device)) {
+		warn("Can't store services for private addressed device %s",
+								device->path);
+		return;
+	}
+
+	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	prim_uuid = bt_uuid2string(&uuid);
+	if (prim_uuid == NULL)
+		return;
+
+	ba2str(btd_adapter_get_address(adapter), src_addr);
+	ba2str(&device->bdaddr, dst_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
+								dst_addr);
+	key_file = g_key_file_new();
+
+	for (l = device->primaries; l; l = l->next) {
+		struct gatt_primary *primary = l->data;
+		char handle[6], uuid_str[33];
+		int i;
+
+		sprintf(handle, "%hu", primary->range.start);
+
+		bt_string2uuid(&uuid, primary->uuid);
+		sdp_uuid128_to_uuid(&uuid);
+
+		switch (uuid.type) {
+		case SDP_UUID16:
+			sprintf(uuid_str, "%4.4X", uuid.value.uuid16);
+			break;
+		case SDP_UUID32:
+			sprintf(uuid_str, "%8.8X", uuid.value.uuid32);
+			break;
+		case SDP_UUID128:
+			for (i = 0; i < 16; i++)
+				sprintf(uuid_str + (i * 2), "%2.2X",
+						uuid.value.uuid128.data[i]);
+			break;
+		default:
+			uuid_str[0] = '\0';
+		}
+
+		g_key_file_set_string(key_file, handle, "UUID", prim_uuid);
+		g_key_file_set_string(key_file, handle, "Value", uuid_str);
+		g_key_file_set_integer(key_file, handle, "EndGroupHandle",
+					primary->range.end);
+	}
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	free(prim_uuid);
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+struct gatt_saver {
+	struct btd_device *device;
+	GKeyFile *key_file;
+};
+
+static void store_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+	const bt_uuid_t *uuid;
+	uint16_t handle_num;
+
+	handle_num = gatt_db_attribute_get_handle(attr);
+	sprintf(handle, "%04hx", handle_num);
+
+	uuid = gatt_db_attribute_get_type(attr);
+	bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
+	sprintf(value, "%s", uuid_str);
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+	uint16_t handle_num, value_handle;
+	uint8_t properties;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
+							&properties, &uuid)) {
+		warn("Error storing characteristic - can't get data");
+		return;
+	}
+
+	sprintf(handle, "%04hx", handle_num);
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+	sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s", value_handle,
+							properties, uuid_str);
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+
+	gatt_db_service_foreach_desc(attr, store_desc, saver);
+}
+
+static void store_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	struct gatt_db_attribute *service;
+	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
+	uint16_t handle_num, start, end;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_incl_data(attr, &handle_num, &start, &end)) {
+		warn("Error storing included service - can't get data");
+		return;
+	}
+
+	service = gatt_db_get_attribute(saver->device->db, start);
+	if (!service) {
+		warn("Error storing included service - can't find it");
+		return;
+	}
+
+	sprintf(handle, "%04hx", handle_num);
+
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+	sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start,
+								end, uuid_str);
+
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+}
+
+static void store_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_saver *saver = user_data;
+	GKeyFile *key_file = saver->key_file;
+	char uuid_str[MAX_LEN_UUID_STR], handle[6], value[256];
+	uint16_t start, end;
+	bt_uuid_t uuid;
+	bool primary;
+	char *type;
+
+	if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+								&uuid)) {
+		warn("Error storing service - can't get data");
+		return;
+	}
+
+	sprintf(handle, "%04hx", start);
+
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+	if (primary)
+		type = GATT_PRIM_SVC_UUID_STR;
+	else
+		type = GATT_SND_SVC_UUID_STR;
+
+	sprintf(value, "%s:%04hx:%s", type, end, uuid_str);
+
+	g_key_file_set_string(key_file, "Attributes", handle, value);
+
+	gatt_db_service_foreach_incl(attr, store_incl, saver);
+	gatt_db_service_foreach_char(attr, store_chrc, saver);
+}
+
+static void store_gatt_db(struct btd_device *device)
+{
+	struct btd_adapter *adapter = device->adapter;
+	char filename[PATH_MAX];
+	char src_addr[18], dst_addr[18];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+	struct gatt_saver saver;
+
+	if (device_address_is_private(device)) {
+		warn("Can't store GATT db for private addressed device %s",
+								device->path);
+		return;
+	}
+
+	ba2str(btd_adapter_get_address(adapter), src_addr);
+	ba2str(&device->bdaddr, dst_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", src_addr,
+								dst_addr);
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	saver.key_file = key_file;
+	saver.device = device;
+
+	gatt_db_foreach_service(device->db, NULL, store_service, &saver);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, data, length, NULL);
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+
+static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
+									int err)
+{
+	struct btd_device *dev = req->device;
+	DBusMessage *reply = NULL;
+
+	if (!req->msg)
+		goto done;
+
+	if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE, "Pair")) {
+		if (!device_is_paired(dev, bdaddr_type)) {
+			reply = btd_error_failed(req->msg, "Not paired");
+			goto done;
+		}
+
+		if (dev->pending_paired) {
+			g_dbus_emit_property_changed(dbus_conn, dev->path,
+						DEVICE_INTERFACE, "Paired");
+			dev->pending_paired = false;
+		}
+
+		/* Disregard browse errors in case of Pair */
+		reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+		goto done;
+	}
+
+	if (err) {
+		reply = btd_error_failed(req->msg, strerror(-err));
+		goto done;
+	}
+
+	if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE, "Connect"))
+		reply = dev_connect(dbus_conn, req->msg, dev);
+	else if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+							"ConnectProfile"))
+		reply = connect_profile(dbus_conn, req->msg, dev);
+	else
+		reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+
+done:
+	if (reply)
+		g_dbus_send_message(dbus_conn, reply);
+
+	browse_request_free(req);
+}
+
+static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
+								int err)
+{
+	struct bearer_state *state = get_state(dev, bdaddr_type);
+	struct browse_req *req = dev->browse;
+
+	DBG("%s err %d", dev->path, err);
+
+	state->svc_resolved = true;
+
+	/* Disconnection notification can happen before this function
+	 * gets called, so don't set svc_refreshed for a disconnected
+	 * device.
+	 */
+	if (state->connected)
+		dev->svc_refreshed = true;
+
+	g_slist_free_full(dev->eir_uuids, g_free);
+	dev->eir_uuids = NULL;
+
+	if (dev->pending_paired) {
+		g_dbus_emit_property_changed(dbus_conn, dev->path,
+						DEVICE_INTERFACE, "Paired");
+		dev->pending_paired = false;
+	}
+
+	while (dev->svc_callbacks) {
+		struct svc_callback *cb = dev->svc_callbacks->data;
+
+		if (cb->idle_id > 0)
+			g_source_remove(cb->idle_id);
+
+		cb->func(dev, err, cb->user_data);
+
+		dev->svc_callbacks = g_slist_delete_link(dev->svc_callbacks,
+							dev->svc_callbacks);
+		g_free(cb);
+	}
+
+	if (!dev->temporary)
+		store_device_info(dev);
+
+	if (bdaddr_type != BDADDR_BREDR && err == 0)
+		store_services(dev);
+
+	if (!req)
+		return;
+
+	dev->browse = NULL;
+	browse_request_complete(req, bdaddr_type, err);
+}
+
+static struct bonding_req *bonding_request_new(DBusMessage *msg,
+						struct btd_device *device,
+						uint8_t bdaddr_type,
+						struct agent *agent)
+{
+	struct bonding_req *bonding;
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+	DBG("Requesting bonding for %s", addr);
+
+	bonding = g_new0(struct bonding_req, 1);
+
+	bonding->msg = dbus_message_ref(msg);
+	bonding->bdaddr_type = bdaddr_type;
+
+	bonding->cb_iter = btd_adapter_pin_cb_iter_new(device->adapter);
+
+	/* Marks the bonding start time for the first attempt on request
+	 * construction. The following attempts will be updated on
+	 * device_bonding_retry. */
+	clock_gettime(CLOCK_MONOTONIC, &bonding->attempt_start_time);
+
+	if (agent)
+		bonding->agent = agent_ref(agent);
+
+	return bonding;
+}
+
+void device_bonding_restart_timer(struct btd_device *device)
+{
+	if (!device || !device->bonding)
+		return;
+
+	clock_gettime(CLOCK_MONOTONIC, &device->bonding->attempt_start_time);
+}
+
+static void bonding_request_stop_timer(struct bonding_req *bonding)
+{
+	struct timespec current;
+
+	clock_gettime(CLOCK_MONOTONIC, &current);
+
+	/* Compute the time difference in ms. */
+	bonding->last_attempt_duration_ms =
+		(current.tv_sec - bonding->attempt_start_time.tv_sec) * 1000L +
+		(current.tv_nsec - bonding->attempt_start_time.tv_nsec)
+								/ 1000000L;
+}
+
+/* Returns the duration of the last bonding attempt in milliseconds. The
+ * duration is measured starting from the latest of the following three
+ * events and finishing when the Command complete event is received for the
+ * authentication request:
+ *  - MGMT_OP_PAIR_DEVICE is sent,
+ *  - MGMT_OP_PIN_CODE_REPLY is sent and
+ *  - Command complete event is received for the sent MGMT_OP_PIN_CODE_REPLY.
+ */
+long device_bonding_last_duration(struct btd_device *device)
+{
+	struct bonding_req *bonding = device->bonding;
+
+	if (!bonding)
+		return 0;
+
+	return bonding->last_attempt_duration_ms;
+}
+
+static void create_bond_req_exit(DBusConnection *conn, void *user_data)
+{
+	struct btd_device *device = user_data;
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+	DBG("%s: requestor exited before bonding was completed", addr);
+
+	if (device->authr)
+		device_cancel_authentication(device, FALSE);
+
+	if (device->bonding) {
+		device->bonding->listener_id = 0;
+		device_request_disconnect(device, NULL);
+	}
+}
+
+static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct btd_device *device = data;
+	struct btd_adapter *adapter = device->adapter;
+	struct bearer_state *state;
+	uint8_t bdaddr_type;
+	const char *sender;
+	struct agent *agent;
+	struct bonding_req *bonding;
+	uint8_t io_cap;
+	int err;
+
+	btd_device_set_temporary(device, false);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	if (device->bonding)
+		return btd_error_in_progress(msg);
+
+	if (device->bredr_state.bonded)
+		bdaddr_type = device->bdaddr_type;
+	else if (device->le_state.bonded)
+		bdaddr_type = BDADDR_BREDR;
+	else
+		bdaddr_type = select_conn_bearer(device);
+
+	state = get_state(device, bdaddr_type);
+
+	if (state->bonded)
+		return btd_error_already_exists(msg);
+
+	sender = dbus_message_get_sender(msg);
+
+	agent = agent_get(sender);
+	if (agent)
+		io_cap = agent_get_io_capability(agent);
+	else
+		io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+
+	bonding = bonding_request_new(msg, device, bdaddr_type, agent);
+
+	if (agent)
+		agent_unref(agent);
+
+	bonding->listener_id = g_dbus_add_disconnect_watch(dbus_conn,
+						sender, create_bond_req_exit,
+						device, NULL);
+
+	device->bonding = bonding;
+	bonding->device = device;
+
+	/* Due to a bug in the kernel we might loose out on ATT commands
+	 * that arrive during the SMP procedure, so connect the ATT
+	 * channel first and only then start pairing (there's code for
+	 * this in the ATT connect callback)
+	 */
+	if (bdaddr_type != BDADDR_BREDR) {
+		if (!state->connected && btd_le_connect_before_pairing())
+			err = device_connect_le(device);
+		else
+			err = adapter_create_bonding(adapter, &device->bdaddr,
+							device->bdaddr_type,
+							io_cap);
+	} else {
+		err = adapter_create_bonding(adapter, &device->bdaddr,
+							BDADDR_BREDR, io_cap);
+	}
+
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	return NULL;
+}
+
+static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
+{
+	switch (status) {
+	case MGMT_STATUS_SUCCESS:
+		return dbus_message_new_method_return(msg);
+
+	case MGMT_STATUS_CONNECT_FAILED:
+		return dbus_message_new_error(msg,
+				ERROR_INTERFACE ".ConnectionAttemptFailed",
+				"Page Timeout");
+	case MGMT_STATUS_TIMEOUT:
+		return dbus_message_new_error(msg,
+				ERROR_INTERFACE ".AuthenticationTimeout",
+				"Authentication Timeout");
+	case MGMT_STATUS_BUSY:
+	case MGMT_STATUS_REJECTED:
+		return dbus_message_new_error(msg,
+				ERROR_INTERFACE ".AuthenticationRejected",
+				"Authentication Rejected");
+	case MGMT_STATUS_CANCELLED:
+	case MGMT_STATUS_NO_RESOURCES:
+	case MGMT_STATUS_DISCONNECTED:
+		return dbus_message_new_error(msg,
+				ERROR_INTERFACE ".AuthenticationCanceled",
+				"Authentication Canceled");
+	case MGMT_STATUS_ALREADY_PAIRED:
+		return dbus_message_new_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Already Paired");
+	default:
+		return dbus_message_new_error(msg,
+				ERROR_INTERFACE ".AuthenticationFailed",
+				"Authentication Failed");
+	}
+}
+
+static void bonding_request_free(struct bonding_req *bonding)
+{
+	if (!bonding)
+		return;
+
+	if (bonding->listener_id)
+		g_dbus_remove_watch(dbus_conn, bonding->listener_id);
+
+	if (bonding->msg)
+		dbus_message_unref(bonding->msg);
+
+	if (bonding->cb_iter)
+		g_free(bonding->cb_iter);
+
+	if (bonding->agent) {
+		agent_cancel(bonding->agent);
+		agent_unref(bonding->agent);
+		bonding->agent = NULL;
+	}
+
+	if (bonding->retry_timer)
+		g_source_remove(bonding->retry_timer);
+
+	if (bonding->device)
+		bonding->device->bonding = NULL;
+
+	g_free(bonding);
+}
+
+static void device_cancel_bonding(struct btd_device *device, uint8_t status)
+{
+	struct bonding_req *bonding = device->bonding;
+	DBusMessage *reply;
+	char addr[18];
+
+	if (!bonding)
+		return;
+
+	ba2str(&device->bdaddr, addr);
+	DBG("Canceling bonding request for %s", addr);
+
+	if (device->authr)
+		device_cancel_authentication(device, FALSE);
+
+	reply = new_authentication_return(bonding->msg, status);
+	g_dbus_send_message(dbus_conn, reply);
+
+	bonding_request_cancel(bonding);
+	bonding_request_free(bonding);
+}
+
+static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct btd_device *device = data;
+	struct bonding_req *req = device->bonding;
+
+	DBG("");
+
+	if (!req)
+		return btd_error_does_not_exist(msg);
+
+	device_cancel_bonding(device, MGMT_STATUS_CANCELLED);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable device_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
+	{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
+	{ GDBUS_ASYNC_METHOD("ConnectProfile", GDBUS_ARGS({ "UUID", "s" }),
+						NULL, connect_profile) },
+	{ GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }),
+						NULL, disconnect_profile) },
+	{ GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
+	{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
+	{ }
+};
+
+static const GDBusPropertyTable device_properties[] = {
+	{ "Address", "s", dev_property_get_address },
+	{ "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,
+					dev_property_exists_class },
+	{ "Appearance", "q", dev_property_get_appearance, NULL,
+					dev_property_exists_appearance },
+	{ "Icon", "s", dev_property_get_icon, NULL,
+					dev_property_exists_icon },
+	{ "Paired", "b", dev_property_get_paired },
+	{ "Trusted", "b", dev_property_get_trusted, dev_property_set_trusted },
+	{ "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked },
+	{ "LegacyPairing", "b", dev_property_get_legacy },
+	{ "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi },
+	{ "Connected", "b", dev_property_get_connected },
+	{ "UUIDs", "as", dev_property_get_uuids },
+	{ "Modalias", "s", dev_property_get_modalias, NULL,
+						dev_property_exists_modalias },
+	{ "Adapter", "o", dev_property_get_adapter },
+	{ "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data,
+				NULL, dev_property_manufacturer_data_exist,
+				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "ServiceData", "a{sv}", dev_property_get_service_data,
+				NULL, dev_property_service_data_exist,
+				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "TxPower", "n", dev_property_get_tx_power, NULL,
+					dev_property_exists_tx_power,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "GattServices", "ao", dev_property_get_gatt_services, NULL,
+					dev_property_exists_gatt_services,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+
+	{ }
+};
+
+uint8_t btd_device_get_bdaddr_type(struct btd_device *dev)
+{
+	return dev->bdaddr_type;
+}
+
+bool btd_device_is_connected(struct btd_device *dev)
+{
+	return dev->bredr_state.connected || dev->le_state.connected;
+}
+
+void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type)
+{
+	struct bearer_state *state = get_state(dev, bdaddr_type);
+
+	device_update_last_seen(dev, bdaddr_type);
+
+	if (state->connected) {
+		char addr[18];
+		ba2str(&dev->bdaddr, addr);
+		error("Device %s is already connected", addr);
+		return;
+	}
+
+	/* If this is the first connection over this bearer */
+	if (bdaddr_type == BDADDR_BREDR)
+		device_set_bredr_support(dev);
+	else
+		device_set_le_support(dev, bdaddr_type);
+
+	state->connected = true;
+
+	if (dev->le_state.connected && dev->bredr_state.connected)
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE,
+								"Connected");
+}
+
+void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
+{
+	struct bearer_state *state = get_state(device, bdaddr_type);
+
+	if (!state->connected)
+		return;
+
+	state->connected = false;
+	device->svc_refreshed = false;
+	device->general_connect = FALSE;
+
+	if (device->disconn_timer > 0) {
+		g_source_remove(device->disconn_timer);
+		device->disconn_timer = 0;
+	}
+
+	while (device->disconnects) {
+		DBusMessage *msg = device->disconnects->data;
+
+		g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+		device->disconnects = g_slist_remove(device->disconnects, msg);
+		dbus_message_unref(msg);
+	}
+
+	if (state->paired && !state->bonded)
+		btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+								bdaddr_type);
+
+	if (device->bredr_state.connected || device->le_state.connected)
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Connected");
+}
+
+guint device_add_disconnect_watch(struct btd_device *device,
+				disconnect_watch watch, void *user_data,
+				GDestroyNotify destroy)
+{
+	struct btd_disconnect_data *data;
+	static guint id = 0;
+
+	data = g_new0(struct btd_disconnect_data, 1);
+	data->id = ++id;
+	data->watch = watch;
+	data->user_data = user_data;
+	data->destroy = destroy;
+
+	device->watches = g_slist_append(device->watches, data);
+
+	return data->id;
+}
+
+void device_remove_disconnect_watch(struct btd_device *device, guint id)
+{
+	GSList *l;
+
+	for (l = device->watches; l; l = l->next) {
+		struct btd_disconnect_data *data = l->data;
+
+		if (data->id == id) {
+			device->watches = g_slist_remove(device->watches,
+							data);
+			if (data->destroy)
+				data->destroy(data->user_data);
+			g_free(data);
+			return;
+		}
+	}
+}
+
+static char *load_cached_name(struct btd_device *device, const char *local,
+				const char *peer)
+{
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char *str = NULL;
+	int len;
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+	key_file = g_key_file_new();
+
+	if (!g_key_file_load_from_file(key_file, filename, 0, NULL))
+		goto failed;
+
+	str = g_key_file_get_string(key_file, "General", "Name", NULL);
+	if (str) {
+		len = strlen(str);
+		if (len > HCI_MAX_NAME_LENGTH)
+			str[HCI_MAX_NAME_LENGTH] = '\0';
+	}
+
+failed:
+	g_key_file_free(key_file);
+
+	return str;
+}
+
+static struct csrk_info *load_csrk(GKeyFile *key_file, const char *group)
+{
+	struct csrk_info *csrk;
+	char *str;
+	int i;
+
+	str = g_key_file_get_string(key_file, group, "Key", NULL);
+	if (!str)
+		return NULL;
+
+	csrk = g_new0(struct csrk_info, 1);
+
+	for (i = 0; i < 16; i++) {
+		if (sscanf(str + (i * 2), "%2hhx", &csrk->key[i]) != 1)
+			goto fail;
+	}
+
+	/*
+	 * In case of older storage this will return 0 which is fine since it
+	 * didn't support signing at that point the counter should never have
+	 * been used.
+	 */
+	csrk->counter = g_key_file_get_integer(key_file, group, "Counter",
+									NULL);
+	g_free(str);
+
+	return csrk;
+
+fail:
+	g_free(str);
+	g_free(csrk);
+	return NULL;
+}
+
+static void load_info(struct btd_device *device, const char *local,
+			const char *peer, GKeyFile *key_file)
+{
+	char *str;
+	gboolean store_needed = FALSE;
+	gboolean blocked;
+	char **uuids;
+	int source, vendor, product, version;
+	char **techno, **t;
+
+	/* Load device name from storage info file, if that fails fall back to
+	 * the cache.
+	 */
+	str = g_key_file_get_string(key_file, "General", "Name", NULL);
+	if (str == NULL) {
+		str = load_cached_name(device, local, peer);
+		if (str)
+			store_needed = TRUE;
+	}
+
+	if (str) {
+		strcpy(device->name, str);
+		g_free(str);
+	}
+
+	/* Load alias */
+	device->alias = g_key_file_get_string(key_file, "General", "Alias",
+									NULL);
+
+	/* Load class */
+	str = g_key_file_get_string(key_file, "General", "Class", NULL);
+	if (str) {
+		uint32_t class;
+
+		if (sscanf(str, "%x", &class) == 1)
+			device->class = class;
+		g_free(str);
+	}
+
+	/* Load appearance */
+	str = g_key_file_get_string(key_file, "General", "Appearance", NULL);
+	if (str) {
+		device->appearance = strtol(str, NULL, 16);
+		g_free(str);
+	}
+
+	/* Load device technology */
+	techno = g_key_file_get_string_list(key_file, "General",
+					"SupportedTechnologies", NULL, NULL);
+	if (!techno)
+		goto next;
+
+	for (t = techno; *t; t++) {
+		if (g_str_equal(*t, "BR/EDR"))
+			device->bredr = true;
+		else if (g_str_equal(*t, "LE"))
+			device->le = true;
+		else
+			error("Unknown device technology");
+	}
+
+	if (!device->le) {
+		device->bdaddr_type = BDADDR_BREDR;
+	} else {
+		str = g_key_file_get_string(key_file, "General",
+						"AddressType", NULL);
+
+		if (str && g_str_equal(str, "public"))
+			device->bdaddr_type = BDADDR_LE_PUBLIC;
+		else if (str && g_str_equal(str, "static"))
+			device->bdaddr_type = BDADDR_LE_RANDOM;
+		else
+			error("Unknown LE device technology");
+
+		g_free(str);
+
+		device->local_csrk = load_csrk(key_file, "LocalSignatureKey");
+		device->remote_csrk = load_csrk(key_file, "RemoteSignatureKey");
+	}
+
+	g_strfreev(techno);
+
+next:
+	/* Load trust */
+	device->trusted = g_key_file_get_boolean(key_file, "General",
+							"Trusted", NULL);
+
+	/* Load device blocked */
+	blocked = g_key_file_get_boolean(key_file, "General", "Blocked", NULL);
+	if (blocked)
+		device_block(device, FALSE);
+
+	/* Load device profile list */
+	uuids = g_key_file_get_string_list(key_file, "General", "Services",
+						NULL, NULL);
+	if (uuids) {
+		char **uuid;
+
+		for (uuid = uuids; *uuid; uuid++) {
+			GSList *match;
+
+			match = g_slist_find_custom(device->uuids, *uuid,
+							bt_uuid_strcmp);
+			if (match)
+				continue;
+
+			device->uuids = g_slist_insert_sorted(device->uuids,
+								g_strdup(*uuid),
+								bt_uuid_strcmp);
+		}
+		g_strfreev(uuids);
+
+		/* Discovered services restored from storage */
+		device->bredr_state.svc_resolved = true;
+	}
+
+	/* Load device id */
+	source = g_key_file_get_integer(key_file, "DeviceID", "Source", NULL);
+	if (source) {
+		vendor = g_key_file_get_integer(key_file, "DeviceID",
+							"Vendor", NULL);
+
+		product = g_key_file_get_integer(key_file, "DeviceID",
+							"Product", NULL);
+
+		version = g_key_file_get_integer(key_file, "DeviceID",
+							"Version", NULL);
+
+		btd_device_set_pnpid(device, source, vendor, product, version);
+	}
+
+	if (store_needed)
+		store_device_info(device);
+}
+
+static void load_att_info(struct btd_device *device, const char *local,
+				const char *peer)
+{
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char *prim_uuid, *str;
+	char **groups, **handle, *service_uuid;
+	struct gatt_primary *prim;
+	uuid_t uuid;
+	char tmp[3];
+	int i;
+
+	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	prim_uuid = bt_uuid2string(&uuid);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", local,
+			peer);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	groups = g_key_file_get_groups(key_file, NULL);
+
+	for (handle = groups; *handle; handle++) {
+		gboolean uuid_ok;
+		int end;
+
+		str = g_key_file_get_string(key_file, *handle, "UUID", NULL);
+		if (!str)
+			continue;
+
+		uuid_ok = g_str_equal(str, prim_uuid);
+		g_free(str);
+
+		if (!uuid_ok)
+			continue;
+
+		str = g_key_file_get_string(key_file, *handle, "Value", NULL);
+		if (!str)
+			continue;
+
+		end = g_key_file_get_integer(key_file, *handle,
+						"EndGroupHandle", NULL);
+		if (end == 0) {
+			g_free(str);
+			continue;
+		}
+
+		prim = g_new0(struct gatt_primary, 1);
+		prim->range.start = atoi(*handle);
+		prim->range.end = end;
+
+		switch (strlen(str)) {
+		case 4:
+			uuid.type = SDP_UUID16;
+			sscanf(str, "%04hx", &uuid.value.uuid16);
+		break;
+		case 8:
+			uuid.type = SDP_UUID32;
+			sscanf(str, "%08x", &uuid.value.uuid32);
+			break;
+		case 32:
+			uuid.type = SDP_UUID128;
+			memset(tmp, 0, sizeof(tmp));
+			for (i = 0; i < 16; i++) {
+				memcpy(tmp, str + (i * 2), 2);
+				uuid.value.uuid128.data[i] =
+						(uint8_t) strtol(tmp, NULL, 16);
+			}
+			break;
+		default:
+			g_free(str);
+			g_free(prim);
+			continue;
+		}
+
+		service_uuid = bt_uuid2string(&uuid);
+		memcpy(prim->uuid, service_uuid, MAX_LEN_UUID_STR);
+		free(service_uuid);
+		g_free(str);
+
+		device->primaries = g_slist_append(device->primaries, prim);
+	}
+
+	g_strfreev(groups);
+	g_key_file_free(key_file);
+	free(prim_uuid);
+}
+
+static void device_register_primaries(struct btd_device *device,
+						GSList *prim_list, int psm)
+{
+	device->primaries = g_slist_concat(device->primaries, prim_list);
+}
+
+static void add_primary(struct gatt_db_attribute *attr, void *user_data)
+{
+	GSList **new_services = user_data;
+	struct gatt_primary *prim;
+	bt_uuid_t uuid;
+
+	prim = g_new0(struct gatt_primary, 1);
+	if (!prim) {
+		DBG("Failed to allocate gatt_primary structure");
+		return;
+	}
+
+	gatt_db_attribute_get_service_handles(attr, &prim->range.start,
+							&prim->range.end);
+	gatt_db_attribute_get_service_uuid(attr, &uuid);
+	bt_uuid_to_string(&uuid, prim->uuid, sizeof(prim->uuid));
+
+	*new_services = g_slist_append(*new_services, prim);
+}
+
+static int load_desc(char *handle, char *value,
+					struct gatt_db_attribute *service)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct gatt_db_attribute *att;
+	uint16_t handle_int;
+	bt_uuid_t uuid;
+
+	if (sscanf(handle, "%04hx", &handle_int) != 1)
+		return -EIO;
+
+	if (sscanf(value, "%s", uuid_str) != 1)
+		return -EIO;
+
+	bt_string_to_uuid(&uuid, uuid_str);
+
+	DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int,
+								uuid_str);
+
+	att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
+							0, NULL, NULL, NULL);
+	if (!att || gatt_db_attribute_get_handle(att) != handle_int) {
+		warn("loading descriptor to db failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_chrc(char *handle, char *value,
+					struct gatt_db_attribute *service)
+{
+	uint16_t properties, value_handle, handle_int;
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct gatt_db_attribute *att;
+	bt_uuid_t uuid;
+
+	if (sscanf(handle, "%04hx", &handle_int) != 1)
+		return -EIO;
+
+	if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", &value_handle,
+						&properties, uuid_str) != 3)
+		return -EIO;
+
+	bt_string_to_uuid(&uuid, uuid_str);
+
+	/* Log debug message. */
+	DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x,"
+				" properties 0x%04x uuid: %s", handle_int,
+				value_handle, properties, uuid_str);
+
+	att = gatt_db_service_insert_characteristic(service, value_handle,
+							&uuid, 0, properties,
+							NULL, NULL, NULL);
+	if (!att || gatt_db_attribute_get_handle(att) != value_handle) {
+		warn("saving characteristic to db failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_incl(struct gatt_db *db, char *handle, char *value,
+					struct gatt_db_attribute *service)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct gatt_db_attribute *att;
+	uint16_t start, end;
+
+	if (sscanf(handle, "%04hx", &start) != 1)
+		return -EIO;
+
+	if (sscanf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", &start, &end,
+								uuid_str) != 3)
+		return -EIO;
+
+	/* Log debug message. */
+	DBG("loading included service: 0x%04x, end: 0x%04x, uuid: %s", start,
+								end, uuid_str);
+
+	att = gatt_db_get_attribute(db, start);
+	if (!att) {
+		warn("saving included service to db failed - no such service");
+		return -EIO;
+	}
+
+	att = gatt_db_service_add_included(service, att);
+	if (!att) {
+		warn("saving included service to db failed");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_service(struct gatt_db *db, char *handle, char *value)
+{
+	struct gatt_db_attribute *att;
+	uint16_t start, end;
+	char type[MAX_LEN_UUID_STR], uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t uuid;
+	bool primary;
+
+	if (sscanf(handle, "%04hx", &start) != 1)
+		return -EIO;
+
+	if (sscanf(value, "%[^:]:%04hx:%s", type, &end, uuid_str) != 3)
+		return -EIO;
+
+	if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR))
+		primary = true;
+	else if (g_str_equal(type, GATT_SND_SVC_UUID_STR))
+		primary = false;
+	else
+		return -EIO;
+
+	bt_string_to_uuid(&uuid, uuid_str);
+
+	/* Log debug message. */
+	DBG("loading service: 0x%04x, end: 0x%04x, uuid: %s",
+							start, end, uuid_str);
+
+	att = gatt_db_insert_service(db, start, &uuid, primary,
+							end - start + 1);
+	if (!att) {
+		DBG("ERROR saving service to db!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int load_gatt_db_impl(GKeyFile *key_file, char **keys,
+							struct gatt_db *db)
+{
+	struct gatt_db_attribute *current_service;
+	char **handle, *value, type[MAX_LEN_UUID_STR];
+	int ret;
+
+	/* first load service definitions */
+	for (handle = keys; *handle; handle++) {
+		value = g_key_file_get_string(key_file, "Attributes", *handle,
+									NULL);
+
+		if (sscanf(value, "%[^:]:", type) != 1) {
+			warn("Missing Type in attribute definition");
+			g_free(value);
+			return -EIO;
+		}
+
+		if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+				g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+			ret = load_service(db, *handle, value);
+			if (ret) {
+				g_free(value);
+				return ret;
+			}
+		}
+
+		g_free(value);
+	}
+
+	current_service = NULL;
+	/* then fill them with data*/
+	for (handle = keys; *handle; handle++) {
+		value = g_key_file_get_string(key_file, "Attributes", *handle,
+									NULL);
+
+		if (sscanf(value, "%[^:]:", type) != 1) {
+			warn("Missing Type in attribute definition");
+			g_free(value);
+			return -EIO;
+		}
+
+		if (g_str_equal(type, GATT_PRIM_SVC_UUID_STR) ||
+				g_str_equal(type, GATT_SND_SVC_UUID_STR)) {
+			uint16_t tmp;
+			uint16_t start, end;
+			bool primary;
+			bt_uuid_t uuid;
+			char uuid_str[MAX_LEN_UUID_STR];
+
+			if (sscanf(*handle, "%04hx", &tmp) != 1) {
+				warn("Unable to parse attribute handle");
+				g_free(value);
+				return -EIO;
+			}
+
+			if (current_service)
+				gatt_db_service_set_active(current_service,
+									true);
+
+			current_service = gatt_db_get_attribute(db, tmp);
+
+			gatt_db_attribute_get_service_data(current_service,
+							&start, &end,
+							&primary, &uuid);
+
+			bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		} else if (g_str_equal(type, GATT_INCLUDE_UUID_STR)) {
+			ret = load_incl(db, *handle, value, current_service);
+		} else if (g_str_equal(type, GATT_CHARAC_UUID_STR)) {
+			ret = load_chrc(*handle, value, current_service);
+		} else {
+			ret = load_desc(*handle, value, current_service);
+		}
+
+		g_free(value);
+		if (ret)
+			return ret;
+	}
+
+	if (current_service)
+		gatt_db_service_set_active(current_service, true);
+
+	return 0;
+}
+
+static void load_gatt_db(struct btd_device *device, const char *local,
+							const char *peer)
+{
+	char **keys, filename[PATH_MAX];
+	GKeyFile *key_file;
+
+	DBG("Restoring %s gatt database from file", peer);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	keys = g_key_file_get_keys(key_file, "Attributes", NULL, NULL);
+
+	if (!keys) {
+		warn("No cache for %s", peer);
+		g_key_file_free(key_file);
+		return;
+	}
+
+	if (load_gatt_db_impl(key_file, keys, device->db))
+		warn("Unable to load gatt db from file for %s", peer);
+
+	g_strfreev(keys);
+	g_key_file_free(key_file);
+
+	g_slist_free_full(device->primaries, g_free);
+	device->primaries = NULL;
+	gatt_db_foreach_service(device->db, NULL, add_primary,
+							&device->primaries);
+}
+
+static void device_add_uuids(struct btd_device *device, GSList *uuids)
+{
+	GSList *l;
+	bool changed = false;
+
+	for (l = uuids; l != NULL; l = g_slist_next(l)) {
+		GSList *match = g_slist_find_custom(device->uuids, l->data,
+							bt_uuid_strcmp);
+		if (match)
+			continue;
+
+		changed = true;
+		device->uuids = g_slist_insert_sorted(device->uuids,
+						g_strdup(l->data),
+						bt_uuid_strcmp);
+	}
+
+	if (changed)
+		g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+}
+
+static bool device_match_profile(struct btd_device *device,
+					struct btd_profile *profile,
+					GSList *uuids)
+{
+	if (profile->remote_uuid == NULL)
+		return false;
+
+	if (g_slist_find_custom(uuids, profile->remote_uuid,
+							bt_uuid_strcmp) == NULL)
+		return false;
+
+	return true;
+}
+
+static void add_gatt_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct btd_device *device = user_data;
+	struct btd_service *service;
+	struct btd_profile *profile;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+	GSList *l;
+
+	gatt_db_attribute_get_service_uuid(attr, &uuid);
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+	/* Check if service was already probed */
+	l = find_service_with_uuid(device->services, uuid_str);
+	if (l)
+		goto done;
+
+	/* Add UUID and probe service */
+	btd_device_add_uuid(device, uuid_str);
+
+	/* Check if service was probed */
+	l = find_service_with_uuid(device->services, uuid_str);
+	if (!l)
+		return;
+
+done:
+	/* Mark service as active to skip discovering it again */
+	gatt_db_service_set_active(attr, true);
+
+	service = l->data;
+	profile = btd_service_get_profile(service);
+
+	/* Claim attributes of internal profiles */
+	if (!profile->external) {
+		/* Mark the service as claimed by the existing profile. */
+		gatt_db_service_set_claimed(attr, true);
+	}
+
+	/* Notify driver about the new connection */
+	service_accept(service);
+}
+
+static void device_add_gatt_services(struct btd_device *device)
+{
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+
+	if (device->blocked) {
+		DBG("Skipping profiles for blocked device %s", addr);
+		return;
+	}
+
+	gatt_db_foreach_service(device->db, NULL, add_gatt_service, device);
+}
+
+static void device_accept_gatt_profiles(struct btd_device *device)
+{
+	GSList *l;
+
+	for (l = device->services; l != NULL; l = g_slist_next(l))
+		service_accept(l->data);
+}
+
+static void device_remove_gatt_service(struct btd_device *device,
+						struct gatt_db_attribute *attr)
+{
+	struct btd_service *service;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+	GSList *l;
+
+	gatt_db_attribute_get_service_uuid(attr, &uuid);
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+	l = find_service_with_uuid(device->services, uuid_str);
+	if (!l)
+		return;
+
+	service = l->data;
+	device->services = g_slist_delete_link(device->services, l);
+	device->pending = g_slist_remove(device->pending, service);
+	service_remove(service);
+}
+
+static gboolean gatt_services_changed(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+
+	store_gatt_db(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
+								"GattServices");
+
+	return FALSE;
+}
+
+static void gatt_service_added(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct btd_device *device = user_data;
+	GSList *new_service = NULL;
+	uint16_t start, end;
+
+	if (!bt_gatt_client_is_ready(device->client))
+		return;
+
+	gatt_db_attribute_get_service_data(attr, &start, &end, NULL, NULL);
+
+	DBG("start: 0x%04x, end: 0x%04x", start, end);
+
+	/*
+	 * TODO: Remove the primaries list entirely once all profiles use
+	 * shared/gatt.
+	 */
+	add_primary(attr, &new_service);
+	if (!new_service)
+		return;
+
+	device_register_primaries(device, new_service, -1);
+
+	add_gatt_service(attr, device);
+
+	btd_gatt_client_service_added(device->client_dbus, attr);
+
+	gatt_services_changed(device);
+}
+
+static gint prim_attr_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_primary *prim = a;
+	const struct gatt_db_attribute *attr = b;
+	uint16_t start, end;
+
+	gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+	return !(prim->range.start == start && prim->range.end == end);
+}
+
+static gint prim_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_primary *prim = a;
+	const char *uuid = b;
+
+	return bt_uuid_strcmp(prim->uuid, uuid);
+}
+
+static void gatt_service_removed(struct gatt_db_attribute *attr,
+								void *user_data)
+{
+	struct btd_device *device = user_data;
+	GSList *l;
+	struct gatt_primary *prim;
+	uint16_t start, end;
+
+	/*
+	 * NOTE: shared/gatt-client clears the database in case of failure. This
+	 * triggers the service_removed callback for all affected services.
+	 * Hence, this function will be called in the following cases:
+	 *
+	 *    1. When a GATT service gets removed due to "Service Changed".
+	 *
+	 *    2. When a GATT service gets removed when the database get cleared
+	 *       upon disconnection with a non-bonded device.
+	 *
+	 *    3. When a GATT service gets removed when the database get cleared
+	 *       by shared/gatt-client when its initialization procedure fails,
+	 *       e.g. due to an ATT protocol error or an unexpected disconnect.
+	 *       In this case the gatt-client will not be ready.
+	 */
+
+	gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+	DBG("start: 0x%04x, end: 0x%04x", start, end);
+
+	/* Remove the corresponding gatt_primary */
+	l = g_slist_find_custom(device->primaries, attr, prim_attr_cmp);
+	if (!l)
+		return;
+
+	prim = l->data;
+	device->primaries = g_slist_delete_link(device->primaries, l);
+
+	/*
+	 * Remove the corresponding UUIDs entry and profile, only if this is
+	 * the last service with this UUID.
+	 */
+	l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);
+
+	if (l && !g_slist_find_custom(device->primaries, prim->uuid,
+							prim_uuid_cmp)) {
+		/*
+		 * If this happend since the db was cleared for a non-bonded
+		 * device, then don't remove the btd_service just yet. We do
+		 * this so that we can avoid re-probing the profile if the same
+		 * GATT service is found on the device on re-connection.
+		 * However, if the device is marked as temporary, then we
+		 * remove it anyway.
+		 */
+		if (device->client || device->temporary == TRUE)
+			device_remove_gatt_service(device, attr);
+
+		g_free(l->data);
+		device->uuids = g_slist_delete_link(device->uuids, l);
+		g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+	}
+
+	g_free(prim);
+
+	store_device_info(device);
+
+	btd_gatt_client_service_removed(device->client_dbus, attr);
+
+	gatt_services_changed(device);
+}
+
+static struct btd_device *device_new(struct btd_adapter *adapter,
+				const char *address)
+{
+	char *address_up;
+	struct btd_device *device;
+	const char *adapter_path = adapter_get_path(adapter);
+
+	DBG("address %s", address);
+
+	device = g_try_malloc0(sizeof(struct btd_device));
+	if (device == NULL)
+		return NULL;
+
+	device->tx_power = 127;
+
+	device->db = gatt_db_new();
+	if (!device->db) {
+		g_free(device);
+		return NULL;
+	}
+
+	device->ad = bt_ad_new();
+	if (!device->ad) {
+		device_free(device);
+		return NULL;
+	}
+
+	address_up = g_ascii_strup(address, -1);
+	device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);
+	g_strdelimit(device->path, ":", '_');
+	g_free(address_up);
+
+	str2ba(address, &device->bdaddr);
+
+	device->client_dbus = btd_gatt_client_new(device);
+	if (!device->client_dbus) {
+		error("Failed to create btd_gatt_client");
+		device_free(device);
+		return NULL;
+	}
+
+	DBG("Creating device %s", device->path);
+
+	if (g_dbus_register_interface(dbus_conn,
+					device->path, DEVICE_INTERFACE,
+					device_methods, NULL,
+					device_properties, device,
+					device_free) == FALSE) {
+		error("Unable to register device interface for %s", address);
+		device_free(device);
+		return NULL;
+	}
+
+	device->adapter = adapter;
+	device->temporary = true;
+
+	gatt_db_register(device->db, gatt_service_added, gatt_service_removed,
+								device, NULL);
+
+	return btd_device_ref(device);
+}
+
+struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
+				const char *address, GKeyFile *key_file)
+{
+	struct btd_device *device;
+	const bdaddr_t *src;
+	char srcaddr[18];
+
+	DBG("address %s", address);
+
+	device = device_new(adapter, address);
+	if (device == NULL)
+		return NULL;
+
+	src = btd_adapter_get_address(adapter);
+	ba2str(src, srcaddr);
+
+	load_info(device, srcaddr, address, key_file);
+	load_att_info(device, srcaddr, address);
+
+	return device;
+}
+
+struct btd_device *device_create(struct btd_adapter *adapter,
+				const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct btd_device *device;
+	const bdaddr_t *sba;
+	char src[18], dst[18];
+	char *str;
+
+	ba2str(bdaddr, dst);
+	DBG("dst %s", dst);
+
+	device = device_new(adapter, dst);
+	if (device == NULL)
+		return NULL;
+
+	device->bdaddr_type = bdaddr_type;
+
+	if (bdaddr_type == BDADDR_BREDR)
+		device->bredr = true;
+	else
+		device->le = true;
+
+	sba = btd_adapter_get_address(adapter);
+	ba2str(sba, src);
+
+	str = load_cached_name(device, src, dst);
+	if (str) {
+		strcpy(device->name, str);
+		g_free(str);
+	}
+
+	return device;
+}
+
+char *btd_device_get_storage_path(struct btd_device *device,
+				const char *filename)
+{
+	char srcaddr[18], dstaddr[18];
+
+	if (device_address_is_private(device)) {
+		warn("Refusing storage path for private addressed device %s",
+								device->path);
+		return NULL;
+	}
+
+	ba2str(btd_adapter_get_address(device->adapter), srcaddr);
+	ba2str(&device->bdaddr, dstaddr);
+
+	if (!filename)
+		return g_strdup_printf(STORAGEDIR "/%s/%s", srcaddr, dstaddr);
+
+	return g_strdup_printf(STORAGEDIR "/%s/%s/%s", srcaddr, dstaddr,
+							filename);
+}
+
+void btd_device_device_set_name(struct btd_device *device, const char *name)
+{
+	if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
+		return;
+
+	DBG("%s %s", device->path, name);
+
+	strncpy(device->name, name, MAX_NAME_LENGTH);
+
+	store_device_info(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Name");
+
+	if (device->alias != NULL)
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Alias");
+}
+
+void device_get_name(struct btd_device *device, char *name, size_t len)
+{
+	if (name != NULL && len > 0) {
+		strncpy(name, device->name, len - 1);
+		name[len - 1] = '\0';
+	}
+}
+
+bool device_name_known(struct btd_device *device)
+{
+	return device->name[0] != '\0';
+}
+
+void device_set_class(struct btd_device *device, uint32_t class)
+{
+	if (device->class == class)
+		return;
+
+	DBG("%s 0x%06X", device->path, class);
+
+	device->class = class;
+
+	store_device_info(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Class");
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Icon");
+}
+
+void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
+							uint8_t bdaddr_type)
+{
+	if (!bacmp(bdaddr, &device->bdaddr) &&
+					bdaddr_type == device->bdaddr_type)
+		return;
+
+	/* Since this function is only used for LE SMP Identity
+	 * Resolving purposes we can now assume LE is supported.
+	 */
+	device->le = true;
+
+	bacpy(&device->bdaddr, bdaddr);
+	device->bdaddr_type = bdaddr_type;
+
+	store_device_info(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Address");
+}
+
+void device_set_bredr_support(struct btd_device *device)
+{
+	if (device->bredr)
+		return;
+
+	device->bredr = true;
+	store_device_info(device);
+}
+
+void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type)
+{
+	if (device->le)
+		return;
+
+	device->le = true;
+	device->bdaddr_type = bdaddr_type;
+
+	store_device_info(device);
+}
+
+void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type)
+{
+	if (bdaddr_type == BDADDR_BREDR)
+		device->bredr_seen = time(NULL);
+	else
+		device->le_seen = time(NULL);
+}
+
+/* It is possible that we have two device objects for the same device in
+ * case it has first been discovered over BR/EDR and has a private
+ * address when discovered over LE for the first time. In such a case we
+ * need to inherit critical values from the duplicate so that we don't
+ * ovewrite them when writing to storage. The next time bluetoothd
+ * starts the device will show up as a single instance.
+ */
+void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup)
+{
+	GSList *l;
+
+	DBG("");
+
+	dev->bredr = dup->bredr;
+
+	dev->trusted = dup->trusted;
+	dev->blocked = dup->blocked;
+
+	for (l = dup->uuids; l; l = g_slist_next(l))
+		dev->uuids = g_slist_append(dev->uuids, g_strdup(l->data));
+
+	if (dev->name[0] == '\0')
+		strcpy(dev->name, dup->name);
+
+	if (!dev->alias)
+		dev->alias = g_strdup(dup->alias);
+
+	dev->class = dup->class;
+
+	dev->vendor_src = dup->vendor_src;
+	dev->vendor = dup->vendor;
+	dev->product = dup->product;
+	dev->version = dup->version;
+}
+
+uint32_t btd_device_get_class(struct btd_device *device)
+{
+	return device->class;
+}
+
+uint16_t btd_device_get_vendor(struct btd_device *device)
+{
+	return device->vendor;
+}
+
+uint16_t btd_device_get_vendor_src(struct btd_device *device)
+{
+	return device->vendor_src;
+}
+
+uint16_t btd_device_get_product(struct btd_device *device)
+{
+	return device->product;
+}
+
+uint16_t btd_device_get_version(struct btd_device *device)
+{
+	return device->version;
+}
+
+static void delete_folder_tree(const char *dirname)
+{
+	DIR *dir;
+	struct dirent *entry;
+	char filename[PATH_MAX];
+
+	dir = opendir(dirname);
+	if (dir == NULL)
+		return;
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (g_str_equal(entry->d_name, ".") ||
+				g_str_equal(entry->d_name, ".."))
+			continue;
+
+		if (entry->d_type == DT_UNKNOWN)
+			entry->d_type = util_get_dt(dirname, entry->d_name);
+
+		snprintf(filename, PATH_MAX, "%s/%s", dirname, entry->d_name);
+
+		if (entry->d_type == DT_DIR)
+			delete_folder_tree(filename);
+		else
+			unlink(filename);
+	}
+	closedir(dir);
+
+	rmdir(dirname);
+}
+
+static void device_remove_stored(struct btd_device *device)
+{
+	const bdaddr_t *src = btd_adapter_get_address(device->adapter);
+	char adapter_addr[18];
+	char device_addr[18];
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char *data;
+	gsize length = 0;
+
+	if (device->bredr_state.bonded) {
+		device->bredr_state.bonded = false;
+		btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+								BDADDR_BREDR);
+	}
+
+	if (device->le_state.bonded) {
+		device->le_state.bonded = false;
+		btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+							device->bdaddr_type);
+	}
+
+	device->bredr_state.paired = false;
+	device->le_state.paired = false;
+
+	if (device->blocked)
+		device_unblock(device, TRUE, FALSE);
+
+	ba2str(src, adapter_addr);
+	ba2str(&device->bdaddr, device_addr);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr,
+			device_addr);
+	delete_folder_tree(filename);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", adapter_addr,
+			device_addr);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	g_key_file_remove_group(key_file, "ServiceRecords", NULL);
+
+	data = g_key_file_to_data(key_file, &length, NULL);
+	if (length > 0) {
+		create_file(filename, S_IRUSR | S_IWUSR);
+		g_file_set_contents(filename, data, length, NULL);
+	}
+
+	g_free(data);
+	g_key_file_free(key_file);
+}
+
+void device_remove(struct btd_device *device, gboolean remove_stored)
+{
+	DBG("Removing device %s", device->path);
+
+	if (device->bonding) {
+		uint8_t status;
+
+		if (device->bredr_state.connected)
+			status = MGMT_STATUS_DISCONNECTED;
+		else
+			status = MGMT_STATUS_CONNECT_FAILED;
+
+		device_cancel_bonding(device, status);
+	}
+
+	if (device->browse)
+		browse_request_cancel(device->browse);
+
+	while (device->services != NULL) {
+		struct btd_service *service = device->services->data;
+
+		device->services = g_slist_remove(device->services, service);
+		service_remove(service);
+	}
+
+	g_slist_free(device->pending);
+	device->pending = NULL;
+
+	if (btd_device_is_connected(device))
+		disconnect_all(device);
+
+	if (device->store_id > 0) {
+		g_source_remove(device->store_id);
+		device->store_id = 0;
+
+		if (!remove_stored)
+			store_device_info_cb(device);
+	}
+
+	if (remove_stored)
+		device_remove_stored(device);
+
+	btd_device_unref(device);
+}
+
+int device_address_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct btd_device *device = a;
+	const char *address = b;
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+	return strcasecmp(addr, address);
+}
+
+int device_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct btd_device *device = a;
+	const bdaddr_t *bdaddr = b;
+
+	return bacmp(&device->bdaddr, bdaddr);
+}
+
+static bool addr_is_public(uint8_t addr_type)
+{
+	if (addr_type == BDADDR_BREDR || addr_type == BDADDR_LE_PUBLIC)
+		return true;
+
+	return false;
+}
+
+int device_addr_type_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct btd_device *dev = a;
+	const struct device_addr_type *addr = b;
+	int cmp;
+
+	cmp = bacmp(&dev->bdaddr, &addr->bdaddr);
+
+	/*
+	 * Address matches and both old and new are public addresses
+	 * (doesn't matter whether LE or BR/EDR, then consider this a
+	 * match.
+	 */
+	if (!cmp && addr_is_public(addr->bdaddr_type) &&
+					addr_is_public(dev->bdaddr_type))
+		return 0;
+
+	if (addr->bdaddr_type == BDADDR_BREDR) {
+		if (!dev->bredr)
+			return -1;
+
+		return cmp;
+	}
+
+	if (!dev->le)
+		return -1;
+
+	if (addr->bdaddr_type != dev->bdaddr_type)
+		return -1;
+
+	return cmp;
+}
+
+static gboolean record_has_uuid(const sdp_record_t *rec,
+				const char *profile_uuid)
+{
+	sdp_list_t *pat;
+
+	for (pat = rec->pattern; pat != NULL; pat = pat->next) {
+		char *uuid;
+		int ret;
+
+		uuid = bt_uuid2string(pat->data);
+		if (!uuid)
+			continue;
+
+		ret = strcasecmp(uuid, profile_uuid);
+
+		free(uuid);
+
+		if (ret == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+GSList *btd_device_get_uuids(struct btd_device *device)
+{
+	return device->uuids;
+}
+
+struct probe_data {
+	struct btd_device *dev;
+	GSList *uuids;
+};
+
+static struct btd_service *probe_service(struct btd_device *device,
+						struct btd_profile *profile,
+						GSList *uuids)
+{
+	struct btd_service *service;
+
+	if (profile->device_probe == NULL)
+		return NULL;
+
+	if (!device_match_profile(device, profile, uuids))
+		return NULL;
+
+	service = service_create(device, profile);
+
+	if (service_probe(service)) {
+		btd_service_unref(service);
+		return NULL;
+	}
+
+	if (profile->auto_connect)
+		device_set_auto_connect(device, TRUE);
+
+	return service;
+}
+
+static void dev_probe(struct btd_profile *p, void *user_data)
+{
+	struct probe_data *d = user_data;
+	struct btd_service *service;
+
+	service = probe_service(d->dev, p, d->uuids);
+	if (!service)
+		return;
+
+	d->dev->services = g_slist_append(d->dev->services, service);
+}
+
+void device_probe_profile(gpointer a, gpointer b)
+{
+	struct btd_device *device = a;
+	struct btd_profile *profile = b;
+	struct btd_service *service;
+
+	service = probe_service(device, profile, device->uuids);
+	if (!service)
+		return;
+
+	device->services = g_slist_append(device->services, service);
+
+	if (!profile->auto_connect || !device->general_connect)
+		return;
+
+	device->pending = g_slist_append(device->pending, service);
+
+	if (g_slist_length(device->pending) == 1)
+		connect_next(device);
+}
+
+void device_remove_profile(gpointer a, gpointer b)
+{
+	struct btd_device *device = a;
+	struct btd_profile *profile = b;
+	struct btd_service *service;
+	GSList *l;
+
+	l = find_service_with_profile(device->services, profile);
+	if (l == NULL)
+		return;
+
+	service = l->data;
+	device->services = g_slist_delete_link(device->services, l);
+	device->pending = g_slist_remove(device->pending, service);
+	service_remove(service);
+}
+
+void device_probe_profiles(struct btd_device *device, GSList *uuids)
+{
+	struct probe_data d = { device, uuids };
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+
+	if (device->blocked) {
+		DBG("Skipping profiles for blocked device %s", addr);
+		goto add_uuids;
+	}
+
+	DBG("Probing profiles for device %s", addr);
+
+	btd_profile_foreach(dev_probe, &d);
+
+add_uuids:
+	device_add_uuids(device, uuids);
+}
+
+static void store_sdp_record(GKeyFile *key_file, sdp_record_t *rec)
+{
+	char handle_str[11];
+	sdp_buf_t buf;
+	int size, i;
+	char *str;
+
+	sprintf(handle_str, "0x%8.8X", rec->handle);
+
+	if (sdp_gen_record_pdu(rec, &buf) < 0)
+		return;
+
+	size = buf.data_size;
+
+	str = g_malloc0(size*2+1);
+
+	for (i = 0; i < size; i++)
+		sprintf(str + (i * 2), "%02X", buf.data[i]);
+
+	g_key_file_set_string(key_file, "ServiceRecords", handle_str, str);
+
+	free(buf.data);
+	g_free(str);
+}
+
+static void store_primaries_from_sdp_record(GKeyFile *key_file,
+						sdp_record_t *rec)
+{
+	uuid_t uuid;
+	char *att_uuid, *prim_uuid;
+	uint16_t start = 0, end = 0, psm = 0;
+	char handle[6], uuid_str[33];
+	int i;
+
+	sdp_uuid16_create(&uuid, ATT_UUID);
+	att_uuid = bt_uuid2string(&uuid);
+
+	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+	prim_uuid = bt_uuid2string(&uuid);
+
+	if (!record_has_uuid(rec, att_uuid))
+		goto done;
+
+	if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
+		goto done;
+
+	sprintf(handle, "%hu", start);
+	switch (uuid.type) {
+	case SDP_UUID16:
+		sprintf(uuid_str, "%4.4X", uuid.value.uuid16);
+		break;
+	case SDP_UUID32:
+		sprintf(uuid_str, "%8.8X", uuid.value.uuid32);
+		break;
+	case SDP_UUID128:
+		for (i = 0; i < 16; i++)
+			sprintf(uuid_str + (i * 2), "%2.2X",
+					uuid.value.uuid128.data[i]);
+		break;
+	default:
+		uuid_str[0] = '\0';
+	}
+
+	g_key_file_set_string(key_file, handle, "UUID", prim_uuid);
+	g_key_file_set_string(key_file, handle, "Value", uuid_str);
+	g_key_file_set_integer(key_file, handle, "EndGroupHandle", end);
+
+done:
+	free(prim_uuid);
+	free(att_uuid);
+}
+
+static int rec_cmp(const void *a, const void *b)
+{
+	const sdp_record_t *r1 = a;
+	const sdp_record_t *r2 = b;
+
+	return r1->handle - r2->handle;
+}
+
+static int update_record(struct browse_req *req, const char *uuid,
+							sdp_record_t *rec)
+{
+	GSList *l;
+
+	/* Check for duplicates */
+	if (sdp_list_find(req->records, rec, rec_cmp))
+		return -EALREADY;
+
+	/* Copy record */
+	req->records = sdp_list_append(req->records, sdp_copy_record(rec));
+
+	/* Check if UUID is duplicated */
+	l = g_slist_find_custom(req->device->uuids, uuid, bt_uuid_strcmp);
+	if (l == NULL) {
+		l = g_slist_find_custom(req->profiles_added, uuid,
+							bt_uuid_strcmp);
+		if (l != NULL)
+			return 0;
+		req->profiles_added = g_slist_append(req->profiles_added,
+							g_strdup(uuid));
+	}
+
+	return 0;
+}
+
+static void update_bredr_services(struct browse_req *req, sdp_list_t *recs)
+{
+	struct btd_device *device = req->device;
+	sdp_list_t *seq;
+	char srcaddr[18], dstaddr[18];
+	char sdp_file[PATH_MAX];
+	char att_file[PATH_MAX];
+	GKeyFile *sdp_key_file;
+	GKeyFile *att_key_file;
+	char *data;
+	gsize length = 0;
+
+	ba2str(btd_adapter_get_address(device->adapter), srcaddr);
+	ba2str(&device->bdaddr, dstaddr);
+
+	snprintf(sdp_file, PATH_MAX, STORAGEDIR "/%s/cache/%s", srcaddr,
+								dstaddr);
+
+	sdp_key_file = g_key_file_new();
+	g_key_file_load_from_file(sdp_key_file, sdp_file, 0, NULL);
+
+	snprintf(att_file, PATH_MAX, STORAGEDIR "/%s/%s/attributes", srcaddr,
+								dstaddr);
+
+	att_key_file = g_key_file_new();
+	g_key_file_load_from_file(att_key_file, att_file, 0, NULL);
+
+	for (seq = recs; seq; seq = seq->next) {
+		sdp_record_t *rec = (sdp_record_t *) seq->data;
+		sdp_list_t *svcclass = NULL;
+		char *profile_uuid;
+
+		if (!rec)
+			break;
+
+		if (sdp_get_service_classes(rec, &svcclass) < 0)
+			continue;
+
+		/* Check for empty service classes list */
+		if (svcclass == NULL) {
+			DBG("Skipping record with no service classes");
+			continue;
+		}
+
+		/* Extract the first element and skip the remainning */
+		profile_uuid = bt_uuid2string(svcclass->data);
+		if (!profile_uuid) {
+			sdp_list_free(svcclass, free);
+			continue;
+		}
+
+		if (bt_uuid_strcmp(profile_uuid, PNP_UUID) == 0) {
+			uint16_t source, vendor, product, version;
+			sdp_data_t *pdlist;
+
+			pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE);
+			source = pdlist ? pdlist->val.uint16 : 0x0000;
+
+			pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+			vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+			pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+			product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+			pdlist = sdp_data_get(rec, SDP_ATTR_VERSION);
+			version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+			if (source || vendor || product || version)
+				btd_device_set_pnpid(device, source, vendor,
+							product, version);
+		}
+
+		if (update_record(req, profile_uuid, rec) < 0)
+			goto next;
+
+		if (sdp_key_file)
+			store_sdp_record(sdp_key_file, rec);
+
+		if (att_key_file)
+			store_primaries_from_sdp_record(att_key_file, rec);
+
+next:
+		free(profile_uuid);
+		sdp_list_free(svcclass, free);
+	}
+
+	if (sdp_key_file) {
+		data = g_key_file_to_data(sdp_key_file, &length, NULL);
+		if (length > 0) {
+			create_file(sdp_file, S_IRUSR | S_IWUSR);
+			g_file_set_contents(sdp_file, data, length, NULL);
+		}
+
+		g_free(data);
+		g_key_file_free(sdp_key_file);
+	}
+
+	if (att_key_file) {
+		data = g_key_file_to_data(att_key_file, &length, NULL);
+		if (length > 0) {
+			create_file(att_file, S_IRUSR | S_IWUSR);
+			g_file_set_contents(att_file, data, length, NULL);
+		}
+
+		g_free(data);
+		g_key_file_free(att_key_file);
+	}
+}
+
+static int primary_cmp(gconstpointer a, gconstpointer b)
+{
+	return memcmp(a, b, sizeof(struct gatt_primary));
+}
+
+static void update_gatt_uuids(struct browse_req *req, GSList *current,
+								GSList *found)
+{
+	GSList *l, *lmatch;
+
+	/* Added Profiles */
+	for (l = found; l; l = g_slist_next(l)) {
+		struct gatt_primary *prim = l->data;
+
+		/* Entry found ? */
+		lmatch = g_slist_find_custom(current, prim, primary_cmp);
+		if (lmatch)
+			continue;
+
+		/* New entry */
+		req->profiles_added = g_slist_append(req->profiles_added,
+							g_strdup(prim->uuid));
+
+		DBG("UUID Added: %s", prim->uuid);
+	}
+}
+
+static GSList *device_services_from_record(struct btd_device *device,
+							GSList *profiles)
+{
+	GSList *l, *prim_list = NULL;
+	char *att_uuid;
+	uuid_t proto_uuid;
+
+	sdp_uuid16_create(&proto_uuid, ATT_UUID);
+	att_uuid = bt_uuid2string(&proto_uuid);
+
+	for (l = profiles; l; l = l->next) {
+		const char *profile_uuid = l->data;
+		const sdp_record_t *rec;
+		struct gatt_primary *prim;
+		uint16_t start = 0, end = 0, psm = 0;
+		uuid_t prim_uuid;
+
+		rec = btd_device_get_record(device, profile_uuid);
+		if (!rec)
+			continue;
+
+		if (!record_has_uuid(rec, att_uuid))
+			continue;
+
+		if (!gatt_parse_record(rec, &prim_uuid, &psm, &start, &end))
+			continue;
+
+		prim = g_new0(struct gatt_primary, 1);
+		prim->range.start = start;
+		prim->range.end = end;
+		sdp_uuid2strn(&prim_uuid, prim->uuid, sizeof(prim->uuid));
+
+		prim_list = g_slist_append(prim_list, prim);
+	}
+
+	free(att_uuid);
+
+	return prim_list;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct browse_req *req = user_data;
+	struct btd_device *device = req->device;
+	GSList *primaries;
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+
+	if (err < 0) {
+		error("%s: error updating services: %s (%d)",
+				addr, strerror(-err), -err);
+		goto send_reply;
+	}
+
+	update_bredr_services(req, recs);
+
+	if (device->tmp_records)
+		sdp_list_free(device->tmp_records,
+					(sdp_free_func_t) sdp_record_free);
+
+	device->tmp_records = req->records;
+	req->records = NULL;
+
+	if (!req->profiles_added) {
+		DBG("%s: No service update", addr);
+		goto send_reply;
+	}
+
+	primaries = device_services_from_record(device, req->profiles_added);
+	if (primaries)
+		device_register_primaries(device, primaries, ATT_PSM);
+
+	/*
+	 * TODO: The btd_service instances for GATT services need to be
+	 * initialized with the service handles. Eventually this code should
+	 * perform ATT protocol service discovery over the ATT PSM to obtain
+	 * the full list of services and populate a client-role gatt_db over
+	 * BR/EDR.
+	 */
+	device_probe_profiles(device, req->profiles_added);
+
+	/* Propagate services changes */
+	g_dbus_emit_property_changed(dbus_conn, req->device->path,
+						DEVICE_INTERFACE, "UUIDs");
+
+send_reply:
+	device_svc_resolved(device, BDADDR_BREDR, err);
+}
+
+static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct browse_req *req = user_data;
+	struct btd_device *device = req->device;
+	struct btd_adapter *adapter = device->adapter;
+	uuid_t uuid;
+
+	/* If we have a valid response and req->search_uuid == 2, then L2CAP
+	 * UUID & PNP searching was successful -- we are done */
+	if (err < 0 || (req->search_uuid == 2 && req->records)) {
+		if (err == -ECONNRESET && req->reconnect_attempt < 1) {
+			req->search_uuid--;
+			req->reconnect_attempt++;
+		} else
+			goto done;
+	}
+
+	update_bredr_services(req, recs);
+
+	/* Search for mandatory uuids */
+	if (uuid_list[req->search_uuid]) {
+		sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+		bt_search_service(btd_adapter_get_address(adapter),
+						&device->bdaddr, &uuid,
+						browse_cb, user_data, NULL,
+						req->sdp_flags);
+		return;
+	}
+
+done:
+	search_cb(recs, err, user_data);
+}
+
+static bool device_get_auto_connect(struct btd_device *device)
+{
+	if (device->disable_auto_connect)
+		return false;
+
+	return device->auto_connect;
+}
+
+static void attio_connected(gpointer data, gpointer user_data)
+{
+	struct attio_data *attio = data;
+	GAttrib *attrib = user_data;
+
+	DBG("");
+
+	if (attio->cfunc)
+		attio->cfunc(attrib, attio->user_data);
+}
+
+static void attio_disconnected(gpointer data, gpointer user_data)
+{
+	struct attio_data *attio = data;
+
+	DBG("");
+
+	if (attio->dcfunc)
+		attio->dcfunc(attio->user_data);
+}
+
+static void att_disconnected_cb(int err, void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	DBG("");
+
+	if (device->browse)
+		goto done;
+
+	DBG("%s (%d)", strerror(err), err);
+
+	g_slist_foreach(device->attios, attio_disconnected, NULL);
+
+	btd_gatt_client_disconnected(device->client_dbus);
+
+	if (!device_get_auto_connect(device)) {
+		DBG("Automatic connection disabled");
+		goto done;
+	}
+
+	/*
+	 * Keep scanning/re-connection active if disconnection reason
+	 * is connection timeout, remote user terminated connection or local
+	 * initiated disconnection.
+	 */
+	if (err == ETIMEDOUT || err == ECONNRESET || err == ECONNABORTED)
+		adapter_connect_list_add(device->adapter, device);
+
+done:
+	attio_cleanup(device);
+}
+
+static void register_gatt_services(struct btd_device *device)
+{
+	struct browse_req *req = device->browse;
+	GSList *services = NULL;
+
+	if (!bt_gatt_client_is_ready(device->client))
+		return;
+
+	/*
+	 * TODO: Remove the primaries list entirely once all profiles use
+	 * shared/gatt.
+	 */
+	gatt_db_foreach_service(device->db, NULL, add_primary, &services);
+
+	btd_device_set_temporary(device, false);
+
+	if (req)
+		update_gatt_uuids(req, device->primaries, services);
+
+	g_slist_free_full(device->primaries, g_free);
+	device->primaries = NULL;
+
+	device_register_primaries(device, services, -1);
+
+	device_add_gatt_services(device);
+
+	device_svc_resolved(device, device->bdaddr_type, 0);
+}
+
+static void gatt_client_init(struct btd_device *device);
+
+static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
+								void *user_data)
+{
+	struct btd_device *device = user_data;
+
+	DBG("status: %s, error: %u", success ? "success" : "failed", att_ecode);
+
+	if (!success) {
+		device_svc_resolved(device, device->bdaddr_type, -EIO);
+		return;
+	}
+
+	register_gatt_services(device);
+
+	btd_gatt_client_ready(device->client_dbus);
+
+	/*
+	 * Update the GattServices property. Do this asynchronously since this
+	 * should happen after the "Characteristics" and "Descriptors"
+	 * properties of all services have been asynchronously updated by
+	 * btd_gatt_client.
+	 *
+	 * Service discovery will be skipped and exported objects won't change
+	 * if the attribute cache was populated when bt_gatt_client gets
+	 * initialized, so no need to to send this signal if that's the case.
+	 */
+	if (!device->gatt_cache_used)
+		g_idle_add(gatt_services_changed, device);
+}
+
+static void gatt_client_service_changed(uint16_t start_handle,
+							uint16_t end_handle,
+							void *user_data)
+{
+	DBG("start 0x%04x, end: 0x%04x", start_handle, end_handle);
+}
+
+static void gatt_debug(const char *str, void *user_data)
+{
+	DBG("%s", str);
+}
+
+static void gatt_client_init(struct btd_device *device)
+{
+	gatt_client_cleanup(device);
+
+	device->client = bt_gatt_client_new(device->db, device->att,
+							device->att_mtu);
+	if (!device->client) {
+		DBG("Failed to initialize");
+		return;
+	}
+
+	bt_gatt_client_set_debug(device->client, gatt_debug, NULL, NULL);
+
+	/* Notify attio so it can react to notifications */
+	g_slist_foreach(device->attios, attio_connected, device->attrib);
+
+	/*
+	 * Notify notify existing service about the new connection so they can
+	 * react to notifications while discovering services
+	 */
+	device_accept_gatt_profiles(device);
+
+	if (!bt_gatt_client_set_ready_handler(device->client,
+							gatt_client_ready_cb,
+							device, NULL)) {
+		DBG("Failed to set ready handler");
+		gatt_client_cleanup(device);
+		return;
+	}
+
+	if (!bt_gatt_client_set_service_changed(device->client,
+						gatt_client_service_changed,
+						device, NULL)) {
+		DBG("Failed to set service changed handler");
+		gatt_client_cleanup(device);
+		return;
+	}
+
+	device->gatt_cache_used = !gatt_db_isempty(device->db);
+
+	btd_gatt_client_connected(device->client_dbus);
+}
+
+static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
+{
+	if (!db) {
+		error("No local GATT database exists for this adapter");
+		return;
+	}
+
+	gatt_server_cleanup(device);
+
+	device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
+	if (!device->server)
+		error("Failed to initialize bt_gatt_server");
+
+	bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
+}
+
+static bool local_counter(uint32_t *sign_cnt, void *user_data)
+{
+	struct btd_device *dev = user_data;
+
+	if (!dev->local_csrk)
+		return false;
+
+	*sign_cnt = dev->local_csrk->counter++;
+
+	store_device_info(dev);
+
+	return true;
+}
+
+static bool remote_counter(uint32_t *sign_cnt, void *user_data)
+{
+	struct btd_device *dev = user_data;
+
+	if (!dev->remote_csrk || *sign_cnt < dev->remote_csrk->counter)
+		return false;
+
+	dev->remote_csrk->counter = *sign_cnt;
+
+	store_device_info(dev);
+
+	return true;
+}
+
+bool device_attach_att(struct btd_device *dev, GIOChannel *io)
+{
+	GError *gerr = NULL;
+	GAttrib *attrib;
+	BtIOSecLevel sec_level;
+	uint16_t mtu;
+	uint16_t cid;
+	struct btd_gatt_database *database;
+	const bdaddr_t *src, *dst;
+	char srcaddr[18], dstaddr[18];
+
+	bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
+						BT_IO_OPT_IMTU, &mtu,
+						BT_IO_OPT_CID, &cid,
+						BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return false;
+	}
+
+	if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) {
+		DBG("Elevating security level since LTK is available");
+
+		sec_level = BT_IO_SEC_MEDIUM;
+		bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level,
+							BT_IO_OPT_INVALID);
+		if (gerr) {
+			error("bt_io_set: %s", gerr->message);
+			g_error_free(gerr);
+			return false;
+		}
+	}
+
+	dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
+	attrib = g_attrib_new(io, dev->att_mtu, false);
+	if (!attrib) {
+		error("Unable to create new GAttrib instance");
+		return false;
+	}
+
+	dev->attrib = attrib;
+	dev->att = g_attrib_get_att(attrib);
+
+	bt_att_ref(dev->att);
+
+	dev->att_disconn_id = bt_att_register_disconnect(dev->att,
+						att_disconnected_cb, dev, NULL);
+	bt_att_set_close_on_unref(dev->att, true);
+
+	if (dev->local_csrk)
+		bt_att_set_local_key(dev->att, dev->local_csrk->key,
+							local_counter, dev);
+
+	if (dev->remote_csrk)
+		bt_att_set_remote_key(dev->att, dev->remote_csrk->key,
+							remote_counter, dev);
+
+	database = btd_adapter_get_database(dev->adapter);
+
+	src = btd_adapter_get_address(dev->adapter);
+	ba2str(src, srcaddr);
+
+	dst = device_get_address(dev);
+	ba2str(dst, dstaddr);
+
+	gatt_client_init(dev);
+	gatt_server_init(dev, btd_gatt_database_get_db(database));
+
+	if (gatt_db_isempty(dev->db))
+		load_gatt_db(dev, srcaddr, dstaddr);
+
+	/*
+	 * Remove the device from the connect_list and give the passive
+	 * scanning another chance to be restarted in case there are
+	 * other devices in the connect_list.
+	 */
+	adapter_connect_list_remove(dev->adapter, dev);
+
+	return true;
+}
+
+static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+	struct att_callbacks *attcb = user_data;
+	struct btd_device *device = attcb->user_data;
+	DBusMessage *reply;
+	uint8_t io_cap;
+	int err = 0;
+
+	g_io_channel_unref(device->att_io);
+	device->att_io = NULL;
+
+	if (gerr) {
+		DBG("%s", gerr->message);
+
+		if (attcb->err)
+			attcb->err(gerr, user_data);
+
+		err = -ECONNABORTED;
+		goto done;
+	}
+
+	if (!device_attach_att(device, io))
+		goto done;
+
+	if (attcb->success)
+		attcb->success(user_data);
+
+	if (!device->bonding)
+		goto done;
+
+	if (device->bonding->agent)
+		io_cap = agent_get_io_capability(device->bonding->agent);
+	else
+		io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+
+	err = adapter_create_bonding(device->adapter, &device->bdaddr,
+					device->bdaddr_type, io_cap);
+done:
+	if (device->bonding && err < 0) {
+		reply = btd_error_failed(device->bonding->msg, strerror(-err));
+		g_dbus_send_message(dbus_conn, reply);
+		bonding_request_cancel(device->bonding);
+		bonding_request_free(device->bonding);
+	}
+
+	if (device->connect) {
+		if (!device->le_state.svc_resolved && !err)
+			device_browse_gatt(device, NULL);
+
+		if (err < 0)
+			reply = btd_error_failed(device->connect,
+							strerror(-err));
+		else
+			reply = dbus_message_new_method_return(device->connect);
+
+		g_dbus_send_message(dbus_conn, reply);
+		dbus_message_unref(device->connect);
+		device->connect = NULL;
+	}
+
+	g_free(attcb);
+}
+
+static void att_error_cb(const GError *gerr, gpointer user_data)
+{
+	struct att_callbacks *attcb = user_data;
+	struct btd_device *device = attcb->user_data;
+
+	if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED))
+		return;
+
+	if (device_get_auto_connect(device)) {
+		DBG("Enabling automatic connections");
+		adapter_connect_list_add(device->adapter, device);
+	}
+}
+
+int device_connect_le(struct btd_device *dev)
+{
+	struct btd_adapter *adapter = dev->adapter;
+	struct att_callbacks *attcb;
+	BtIOSecLevel sec_level;
+	GIOChannel *io;
+	GError *gerr = NULL;
+	char addr[18];
+
+	/* There is one connection attempt going on */
+	if (dev->att_io)
+		return -EALREADY;
+
+	ba2str(&dev->bdaddr, addr);
+
+	DBG("Connection attempt to: %s", addr);
+
+	attcb = g_new0(struct att_callbacks, 1);
+	attcb->err = att_error_cb;
+	attcb->user_data = dev;
+
+	if (dev->le_state.paired)
+		sec_level = BT_IO_SEC_MEDIUM;
+	else
+		sec_level = BT_IO_SEC_LOW;
+
+	/*
+	 * This connection will help us catch any PDUs that comes before
+	 * pairing finishes
+	 */
+	io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR,
+			btd_adapter_get_address(adapter),
+			BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+			BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+			BT_IO_OPT_DEST_TYPE, dev->bdaddr_type,
+			BT_IO_OPT_CID, ATT_CID,
+			BT_IO_OPT_SEC_LEVEL, sec_level,
+			BT_IO_OPT_INVALID);
+
+	if (io == NULL) {
+		if (dev->bonding) {
+			DBusMessage *reply = btd_error_failed(
+					dev->bonding->msg, gerr->message);
+
+			g_dbus_send_message(dbus_conn, reply);
+			bonding_request_cancel(dev->bonding);
+			bonding_request_free(dev->bonding);
+		}
+
+		error("ATT bt_io_connect(%s): %s", addr, gerr->message);
+		g_error_free(gerr);
+		g_free(attcb);
+		return -EIO;
+	}
+
+	/* Keep this, so we can cancel the connection */
+	dev->att_io = io;
+
+	return 0;
+}
+
+static void att_browse_error_cb(const GError *gerr, gpointer user_data)
+{
+	struct att_callbacks *attcb = user_data;
+	struct btd_device *device = attcb->user_data;
+	struct browse_req *req = device->browse;
+
+	device->browse = NULL;
+	browse_request_complete(req, device->bdaddr_type, -ECONNABORTED);
+}
+
+static void att_browse_cb(gpointer user_data)
+{
+	DBG("ATT connection successful");
+}
+
+static struct browse_req *browse_request_new(struct btd_device *device,
+							DBusMessage *msg)
+{
+	struct browse_req *req;
+
+	if (device->browse)
+		return NULL;
+
+	req = g_new0(struct browse_req, 1);
+	req->device = device;
+
+	device->browse = req;
+
+	if (!msg)
+		return req;
+
+	req->msg = dbus_message_ref(msg);
+
+	/*
+	 * Track the request owner to cancel it automatically if the owner
+	 * exits
+	 */
+	req->listener_id = g_dbus_add_disconnect_watch(dbus_conn,
+						dbus_message_get_sender(msg),
+						browse_request_exit,
+						req, NULL);
+
+	return req;
+}
+
+static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
+{
+	struct btd_adapter *adapter = device->adapter;
+	struct att_callbacks *attcb;
+	struct browse_req *req;
+
+	req = browse_request_new(device, msg);
+	if (!req)
+		return -EBUSY;
+
+	if (device->attrib) {
+		/*
+		 * If discovery has not yet completed, then wait for gatt-client
+		 * to become ready.
+		 */
+		if (!device->le_state.svc_resolved)
+			return 0;
+
+		/*
+		 * Services have already been discovered, so signal this browse
+		 * request as resolved.
+		 */
+		device_svc_resolved(device, device->bdaddr_type, 0);
+		return 0;
+	}
+
+	attcb = g_new0(struct att_callbacks, 1);
+	attcb->err = att_browse_error_cb;
+	attcb->success = att_browse_cb;
+	attcb->user_data = device;
+
+	device->att_io = bt_io_connect(att_connect_cb,
+				attcb, NULL, NULL,
+				BT_IO_OPT_SOURCE_BDADDR,
+				btd_adapter_get_address(adapter),
+				BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+				BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+				BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+				BT_IO_OPT_CID, ATT_CID,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+				BT_IO_OPT_INVALID);
+
+	if (device->att_io == NULL) {
+		device->browse = NULL;
+		browse_request_free(req);
+		g_free(attcb);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static uint16_t get_sdp_flags(struct btd_device *device)
+{
+	uint16_t vid, pid;
+
+	vid = btd_device_get_vendor(device);
+	pid = btd_device_get_product(device);
+
+	/* Sony DualShock 4 is not respecting negotiated L2CAP MTU. This might
+	 * results in SDP response being dropped by kernel. Workaround this by
+	 * forcing SDP code to use bigger MTU while connecting.
+	 */
+	if (vid == 0x054c && pid == 0x05c4)
+		return SDP_LARGE_MTU;
+
+	if (btd_adapter_ssp_enabled(device->adapter))
+		return 0;
+
+	/* if no EIR try matching Sony DualShock 4 with name and class */
+	if (!strncmp(device->name, "Wireless Controller", MAX_NAME_LENGTH) &&
+			device->class == 0x2508)
+		return SDP_LARGE_MTU;
+
+	return 0;
+}
+
+static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
+{
+	struct btd_adapter *adapter = device->adapter;
+	struct browse_req *req;
+	uuid_t uuid;
+	int err;
+
+	req = browse_request_new(device, msg);
+	if (!req)
+		return -EBUSY;
+
+	sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+
+	req->sdp_flags = get_sdp_flags(device);
+
+	err = bt_search_service(btd_adapter_get_address(adapter),
+				&device->bdaddr, &uuid, browse_cb, req, NULL,
+				req->sdp_flags);
+	if (err < 0) {
+		browse_request_free(req);
+		return err;
+	}
+
+	return err;
+}
+
+int device_discover_services(struct btd_device *device)
+{
+	int err;
+
+	if (device->bredr)
+		err = device_browse_sdp(device, NULL);
+	else
+		err = device_browse_gatt(device, NULL);
+
+	if (err == 0 && device->discov_timer) {
+		g_source_remove(device->discov_timer);
+		device->discov_timer = 0;
+	}
+
+	return err;
+}
+
+struct btd_adapter *device_get_adapter(struct btd_device *device)
+{
+	if (!device)
+		return NULL;
+
+	return device->adapter;
+}
+
+const bdaddr_t *device_get_address(struct btd_device *device)
+{
+	return &device->bdaddr;
+}
+
+const char *device_get_path(const struct btd_device *device)
+{
+	if (!device)
+		return NULL;
+
+	return device->path;
+}
+
+gboolean device_is_temporary(struct btd_device *device)
+{
+	return device->temporary;
+}
+
+void btd_device_set_temporary(struct btd_device *device, bool temporary)
+{
+	if (!device)
+		return;
+
+	if (device->temporary == temporary)
+		return;
+
+	DBG("temporary %d", temporary);
+
+	device->temporary = temporary;
+
+	if (temporary) {
+		if (device->bredr)
+			adapter_whitelist_remove(device->adapter, device);
+		adapter_connect_list_remove(device->adapter, device);
+		return;
+	}
+
+	if (device->bredr)
+		adapter_whitelist_add(device->adapter, device);
+
+	store_device_info(device);
+}
+
+void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
+{
+	if (!device)
+		return;
+
+	if (device->trusted == trusted)
+		return;
+
+	DBG("trusted %d", trusted);
+
+	device->trusted = trusted;
+
+	store_device_info(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+					DEVICE_INTERFACE, "Trusted");
+}
+
+void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type)
+{
+	if (!device)
+		return;
+
+	DBG("");
+
+	if (bdaddr_type == BDADDR_BREDR)
+		device->bredr_state.bonded = true;
+	else
+		device->le_state.bonded = true;
+
+	btd_device_set_temporary(device, false);
+}
+
+void device_set_legacy(struct btd_device *device, bool legacy)
+{
+	if (!device)
+		return;
+
+	DBG("legacy %d", legacy);
+
+	if (device->legacy == legacy)
+		return;
+
+	device->legacy = legacy;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+					DEVICE_INTERFACE, "LegacyPairing");
+}
+
+void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi,
+							int8_t delta_threshold)
+{
+	if (!device)
+		return;
+
+	if (rssi == 0 || device->rssi == 0) {
+		if (device->rssi == rssi)
+			return;
+
+		DBG("rssi %d", rssi);
+
+		device->rssi = rssi;
+	} else {
+		int delta;
+
+		if (device->rssi > rssi)
+			delta = device->rssi - rssi;
+		else
+			delta = rssi - device->rssi;
+
+		/* only report changes of delta_threshold dBm or more */
+		if (delta < delta_threshold)
+			return;
+
+		DBG("rssi %d delta %d", rssi, delta);
+
+		device->rssi = rssi;
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "RSSI");
+}
+
+void device_set_rssi(struct btd_device *device, int8_t rssi)
+{
+	device_set_rssi_with_delta(device, rssi, RSSI_THRESHOLD);
+}
+
+void device_set_tx_power(struct btd_device *device, int8_t tx_power)
+{
+	if (!device)
+		return;
+
+	if (device->tx_power == tx_power)
+		return;
+
+	DBG("tx_power %d", tx_power);
+
+	device->tx_power = tx_power;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "TxPower");
+}
+
+static gboolean start_discovery(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+
+	if (device->bredr)
+		device_browse_sdp(device, NULL);
+	else
+		device_browse_gatt(device, NULL);
+
+	device->discov_timer = 0;
+
+	return FALSE;
+}
+
+void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type)
+{
+	struct bearer_state *state = get_state(dev, bdaddr_type);
+
+	if (state->paired)
+		return;
+
+	state->paired = true;
+
+	/* If the other bearer state was alraedy true we don't need to
+	 * send any property signals.
+	 */
+	if (dev->bredr_state.paired == dev->le_state.paired)
+		return;
+
+	if (!state->svc_resolved) {
+		dev->pending_paired = true;
+		return;
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, dev->path,
+						DEVICE_INTERFACE, "Paired");
+}
+
+void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type)
+{
+	struct bearer_state *state = get_state(dev, bdaddr_type);
+
+	if (!state->paired)
+		return;
+
+	state->paired = false;
+
+	/*
+	 * If the other bearer state is still true we don't need to
+	 * send any property signals or remove device.
+	 */
+	if (dev->bredr_state.paired != dev->le_state.paired) {
+		/* TODO disconnect only unpaired bearer */
+		if (state->connected)
+			device_request_disconnect(dev, NULL);
+
+		return;
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, dev->path,
+						DEVICE_INTERFACE, "Paired");
+
+	btd_device_set_temporary(dev, true);
+
+	if (btd_device_is_connected(dev))
+		device_request_disconnect(dev, NULL);
+	else
+		btd_adapter_remove_device(dev->adapter, dev);
+}
+
+static void device_auth_req_free(struct btd_device *device)
+{
+	struct authentication_req *authr = device->authr;
+
+	if (!authr)
+		return;
+
+	if (authr->agent)
+		agent_unref(authr->agent);
+
+	g_free(authr->pincode);
+	g_free(authr);
+
+	device->authr = NULL;
+}
+
+bool device_is_retrying(struct btd_device *device)
+{
+	struct bonding_req *bonding = device->bonding;
+
+	return bonding && bonding->retry_timer > 0;
+}
+
+void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
+								uint8_t status)
+{
+	struct bonding_req *bonding = device->bonding;
+	struct authentication_req *auth = device->authr;
+	struct bearer_state *state = get_state(device, bdaddr_type);
+
+	DBG("bonding %p status 0x%02x", bonding, status);
+
+	if (auth && auth->agent)
+		agent_cancel(auth->agent);
+
+	if (status) {
+		device_cancel_authentication(device, TRUE);
+		device_bonding_failed(device, status);
+		return;
+	}
+
+	device_auth_req_free(device);
+
+	/* If we're already paired nothing more is needed */
+	if (state->paired)
+		return;
+
+	device_set_paired(device, bdaddr_type);
+
+	/* If services are already resolved just reply to the pairing
+	 * request
+	 */
+	if (state->svc_resolved && bonding) {
+		/* Attept to store services for this device failed because it
+		 * was not paired. Now that we're paired retry. */
+		store_gatt_db(device);
+
+		g_dbus_send_reply(dbus_conn, bonding->msg, DBUS_TYPE_INVALID);
+		bonding_request_free(bonding);
+		return;
+	}
+
+	/* If we were initiators start service discovery immediately.
+	 * However if the other end was the initator wait a few seconds
+	 * before SDP. This is due to potential IOP issues if the other
+	 * end starts doing SDP at the same time as us */
+	if (bonding) {
+		DBG("Proceeding with service discovery");
+		/* If we are initiators remove any discovery timer and just
+		 * start discovering services directly */
+		if (device->discov_timer) {
+			g_source_remove(device->discov_timer);
+			device->discov_timer = 0;
+		}
+
+		if (bdaddr_type == BDADDR_BREDR)
+			device_browse_sdp(device, bonding->msg);
+		else
+			device_browse_gatt(device, bonding->msg);
+
+		bonding_request_free(bonding);
+	} else if (!state->svc_resolved) {
+		if (!device->browse && !device->discov_timer &&
+				main_opts.reverse_sdp) {
+			/* If we are not initiators and there is no currently
+			 * active discovery or discovery timer, set discovery
+			 * timer */
+			DBG("setting timer for reverse service discovery");
+			device->discov_timer = g_timeout_add_seconds(
+							DISCOVERY_TIMER,
+							start_discovery,
+							device);
+		}
+	}
+}
+
+static gboolean svc_idle_cb(gpointer user_data)
+{
+	struct svc_callback *cb = user_data;
+	struct btd_device *dev = cb->dev;
+
+	dev->svc_callbacks = g_slist_remove(dev->svc_callbacks, cb);
+
+	cb->func(cb->dev, 0, cb->user_data);
+
+	g_free(cb);
+
+	return FALSE;
+}
+
+unsigned int device_wait_for_svc_complete(struct btd_device *dev,
+							device_svc_cb_t func,
+							void *user_data)
+{
+	/* This API is only used for BR/EDR (for now) */
+	struct bearer_state *state = &dev->bredr_state;
+	static unsigned int id = 0;
+	struct svc_callback *cb;
+
+	cb = g_new0(struct svc_callback, 1);
+	cb->func = func;
+	cb->user_data = user_data;
+	cb->dev = dev;
+	cb->id = ++id;
+
+	dev->svc_callbacks = g_slist_prepend(dev->svc_callbacks, cb);
+
+	if (state->svc_resolved || !main_opts.reverse_sdp)
+		cb->idle_id = g_idle_add(svc_idle_cb, cb);
+	else if (dev->discov_timer > 0) {
+		g_source_remove(dev->discov_timer);
+		dev->discov_timer = g_idle_add(start_discovery, dev);
+	}
+
+	return cb->id;
+}
+
+bool device_remove_svc_complete_callback(struct btd_device *dev,
+							unsigned int id)
+{
+	GSList *l;
+
+	for (l = dev->svc_callbacks; l != NULL; l = g_slist_next(l)) {
+		struct svc_callback *cb = l->data;
+
+		if (cb->id != id)
+			continue;
+
+		if (cb->idle_id > 0)
+			g_source_remove(cb->idle_id);
+
+		dev->svc_callbacks = g_slist_remove(dev->svc_callbacks, cb);
+		g_free(cb);
+
+		return true;
+	}
+
+	return false;
+}
+
+gboolean device_is_bonding(struct btd_device *device, const char *sender)
+{
+	struct bonding_req *bonding = device->bonding;
+
+	if (!device->bonding)
+		return FALSE;
+
+	if (!sender)
+		return TRUE;
+
+	return g_str_equal(sender, dbus_message_get_sender(bonding->msg));
+}
+
+static gboolean device_bonding_retry(gpointer data)
+{
+	struct btd_device *device = data;
+	struct btd_adapter *adapter = device_get_adapter(device);
+	struct bonding_req *bonding = device->bonding;
+	uint8_t io_cap;
+	int err;
+
+	if (!bonding)
+		return FALSE;
+
+	DBG("retrying bonding");
+	bonding->retry_timer = 0;
+
+	/* Restart the bonding timer to the begining of the pairing. If not
+	 * pincode request/reply occurs during this retry,
+	 * device_bonding_last_duration() will return a consistent value from
+	 * this point. */
+	device_bonding_restart_timer(device);
+
+	if (bonding->agent)
+		io_cap = agent_get_io_capability(bonding->agent);
+	else
+		io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+
+	err = adapter_bonding_attempt(adapter, &device->bdaddr,
+				device->bdaddr_type, io_cap);
+	if (err < 0)
+		device_bonding_complete(device, bonding->bdaddr_type,
+							bonding->status);
+
+	return FALSE;
+}
+
+int device_bonding_attempt_retry(struct btd_device *device)
+{
+	struct bonding_req *bonding = device->bonding;
+
+	/* Ignore other failure events while retrying */
+	if (device_is_retrying(device))
+		return 0;
+
+	if (!bonding)
+		return -EINVAL;
+
+	/* Mark the end of a bonding attempt to compute the delta for the
+	 * retry. */
+	bonding_request_stop_timer(bonding);
+
+	if (btd_adapter_pin_cb_iter_end(bonding->cb_iter))
+		return -EINVAL;
+
+	DBG("scheduling retry");
+	bonding->retry_timer = g_timeout_add(3000,
+						device_bonding_retry, device);
+	return 0;
+}
+
+void device_bonding_failed(struct btd_device *device, uint8_t status)
+{
+	struct bonding_req *bonding = device->bonding;
+	DBusMessage *reply;
+
+	DBG("status %u", status);
+
+	if (!bonding)
+		return;
+
+	if (device->authr)
+		device_cancel_authentication(device, FALSE);
+
+	reply = new_authentication_return(bonding->msg, status);
+	g_dbus_send_message(dbus_conn, reply);
+
+	bonding_request_free(bonding);
+}
+
+struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device)
+{
+	if (device->bonding == NULL)
+		return NULL;
+
+	return device->bonding->cb_iter;
+}
+
+static void pincode_cb(struct agent *agent, DBusError *err, const char *pin,
+								void *data)
+{
+	struct authentication_req *auth = data;
+	struct btd_device *device = auth->device;
+
+	/* No need to reply anything if the authentication already failed */
+	if (auth->agent == NULL)
+		return;
+
+	btd_adapter_pincode_reply(device->adapter, &device->bdaddr,
+						pin, pin ? strlen(pin) : 0);
+
+	agent_unref(device->authr->agent);
+	device->authr->agent = NULL;
+}
+
+static void confirm_cb(struct agent *agent, DBusError *err, void *data)
+{
+	struct authentication_req *auth = data;
+	struct btd_device *device = auth->device;
+
+	/* No need to reply anything if the authentication already failed */
+	if (auth->agent == NULL)
+		return;
+
+	btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
+							device->bdaddr_type,
+							err ? FALSE : TRUE);
+
+	agent_unref(device->authr->agent);
+	device->authr->agent = NULL;
+}
+
+static void passkey_cb(struct agent *agent, DBusError *err,
+						uint32_t passkey, void *data)
+{
+	struct authentication_req *auth = data;
+	struct btd_device *device = auth->device;
+
+	/* No need to reply anything if the authentication already failed */
+	if (auth->agent == NULL)
+		return;
+
+	if (err)
+		passkey = INVALID_PASSKEY;
+
+	btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
+						device->bdaddr_type, passkey);
+
+	agent_unref(device->authr->agent);
+	device->authr->agent = NULL;
+}
+
+static void display_pincode_cb(struct agent *agent, DBusError *err, void *data)
+{
+	struct authentication_req *auth = data;
+	struct btd_device *device = auth->device;
+
+	pincode_cb(agent, err, auth->pincode, auth);
+
+	g_free(device->authr->pincode);
+	device->authr->pincode = NULL;
+}
+
+static struct authentication_req *new_auth(struct btd_device *device,
+					auth_type_t type, gboolean secure)
+{
+	struct authentication_req *auth;
+	struct agent *agent;
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+	DBG("Requesting agent authentication for %s", addr);
+
+	if (device->authr) {
+		error("Authentication already requested for %s", addr);
+		return NULL;
+	}
+
+	if (device->bonding && device->bonding->agent)
+		agent = agent_ref(device->bonding->agent);
+	else
+		agent = agent_get(NULL);
+
+	if (!agent) {
+		error("No agent available for request type %d", type);
+		return NULL;
+	}
+
+	auth = g_new0(struct authentication_req, 1);
+	auth->agent = agent;
+	auth->device = device;
+	auth->type = type;
+	auth->secure = secure;
+	device->authr = auth;
+
+	return auth;
+}
+
+int device_request_pincode(struct btd_device *device, gboolean secure)
+{
+	struct authentication_req *auth;
+	int err;
+
+	auth = new_auth(device, AUTH_TYPE_PINCODE, secure);
+	if (!auth)
+		return -EPERM;
+
+	err = agent_request_pincode(auth->agent, device, pincode_cb, secure,
+								auth, NULL);
+	if (err < 0) {
+		error("Failed requesting authentication");
+		device_auth_req_free(device);
+	}
+
+	return err;
+}
+
+int device_request_passkey(struct btd_device *device)
+{
+	struct authentication_req *auth;
+	int err;
+
+	auth = new_auth(device, AUTH_TYPE_PASSKEY, FALSE);
+	if (!auth)
+		return -EPERM;
+
+	err = agent_request_passkey(auth->agent, device, passkey_cb, auth,
+									NULL);
+	if (err < 0) {
+		error("Failed requesting authentication");
+		device_auth_req_free(device);
+	}
+
+	return err;
+}
+
+int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
+							uint8_t confirm_hint)
+
+{
+	struct authentication_req *auth;
+	int err;
+
+	auth = new_auth(device, AUTH_TYPE_CONFIRM, FALSE);
+	if (!auth)
+		return -EPERM;
+
+	auth->passkey = passkey;
+
+	if (confirm_hint)
+		err = agent_request_authorization(auth->agent, device,
+						confirm_cb, auth, NULL);
+	else
+		err = agent_request_confirmation(auth->agent, device, passkey,
+						confirm_cb, auth, NULL);
+
+	if (err < 0) {
+		error("Failed requesting authentication");
+		device_auth_req_free(device);
+	}
+
+	return err;
+}
+
+int device_notify_passkey(struct btd_device *device, uint32_t passkey,
+							uint8_t entered)
+{
+	struct authentication_req *auth;
+	int err;
+
+	if (device->authr) {
+		auth = device->authr;
+		if (auth->type != AUTH_TYPE_NOTIFY_PASSKEY)
+			return -EPERM;
+	} else {
+		auth = new_auth(device, AUTH_TYPE_NOTIFY_PASSKEY, FALSE);
+		if (!auth)
+			return -EPERM;
+	}
+
+	err = agent_display_passkey(auth->agent, device, passkey, entered);
+	if (err < 0) {
+		error("Failed requesting authentication");
+		device_auth_req_free(device);
+	}
+
+	return err;
+}
+
+int device_notify_pincode(struct btd_device *device, gboolean secure,
+							const char *pincode)
+{
+	struct authentication_req *auth;
+	int err;
+
+	auth = new_auth(device, AUTH_TYPE_NOTIFY_PINCODE, secure);
+	if (!auth)
+		return -EPERM;
+
+	auth->pincode = g_strdup(pincode);
+
+	err = agent_display_pincode(auth->agent, device, pincode,
+					display_pincode_cb, auth, NULL);
+	if (err < 0) {
+		error("Failed requesting authentication");
+		device_auth_req_free(device);
+	}
+
+	return err;
+}
+
+static void cancel_authentication(struct authentication_req *auth)
+{
+	struct agent *agent;
+	DBusError err;
+
+	if (!auth || !auth->agent)
+		return;
+
+	agent = auth->agent;
+	auth->agent = NULL;
+
+	dbus_error_init(&err);
+	dbus_set_error_const(&err, ERROR_INTERFACE ".Canceled", NULL);
+
+	switch (auth->type) {
+	case AUTH_TYPE_PINCODE:
+		pincode_cb(agent, &err, NULL, auth);
+		break;
+	case AUTH_TYPE_CONFIRM:
+		confirm_cb(agent, &err, auth);
+		break;
+	case AUTH_TYPE_PASSKEY:
+		passkey_cb(agent, &err, 0, auth);
+		break;
+	case AUTH_TYPE_NOTIFY_PASSKEY:
+		/* User Notify doesn't require any reply */
+		break;
+	case AUTH_TYPE_NOTIFY_PINCODE:
+		pincode_cb(agent, &err, NULL, auth);
+		break;
+	}
+
+	dbus_error_free(&err);
+}
+
+void device_cancel_authentication(struct btd_device *device, gboolean aborted)
+{
+	struct authentication_req *auth = device->authr;
+	char addr[18];
+
+	if (!auth)
+		return;
+
+	ba2str(&device->bdaddr, addr);
+	DBG("Canceling authentication request for %s", addr);
+
+	if (auth->agent)
+		agent_cancel(auth->agent);
+
+	if (!aborted)
+		cancel_authentication(auth);
+
+	device_auth_req_free(device);
+}
+
+gboolean device_is_authenticating(struct btd_device *device)
+{
+	return (device->authr != NULL);
+}
+
+struct gatt_primary *btd_device_get_primary(struct btd_device *device,
+							const char *uuid)
+{
+	GSList *match;
+
+	match = g_slist_find_custom(device->primaries, uuid, bt_uuid_strcmp);
+	if (match)
+		return match->data;
+
+	return NULL;
+}
+
+GSList *btd_device_get_primaries(struct btd_device *device)
+{
+	return device->primaries;
+}
+
+struct gatt_db *btd_device_get_gatt_db(struct btd_device *device)
+{
+	if (!device)
+		return NULL;
+
+	return device->db;
+}
+
+struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device)
+{
+	if (!device)
+		return NULL;
+
+	return device->client;
+}
+
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device)
+{
+	if (!device)
+		return NULL;
+
+	return device->server;
+}
+
+void btd_device_gatt_set_service_changed(struct btd_device *device,
+						uint16_t start, uint16_t end)
+{
+	/*
+	 * TODO: Remove this function and handle service changed via
+	 * gatt-client.
+	 */
+}
+
+void btd_device_add_uuid(struct btd_device *device, const char *uuid)
+{
+	GSList *uuid_list;
+	char *new_uuid;
+
+	if (g_slist_find_custom(device->uuids, uuid, bt_uuid_strcmp))
+		return;
+
+	new_uuid = g_strdup(uuid);
+	uuid_list = g_slist_append(NULL, new_uuid);
+
+	device_probe_profiles(device, uuid_list);
+
+	g_free(new_uuid);
+	g_slist_free(uuid_list);
+
+	store_device_info(device);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+}
+
+static sdp_list_t *read_device_records(struct btd_device *device)
+{
+	char local[18], peer[18];
+	char filename[PATH_MAX];
+	GKeyFile *key_file;
+	char **keys, **handle;
+	char *str;
+	sdp_list_t *recs = NULL;
+	sdp_record_t *rec;
+
+	ba2str(btd_adapter_get_address(device->adapter), local);
+	ba2str(&device->bdaddr, peer);
+
+	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
+
+	key_file = g_key_file_new();
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+	keys = g_key_file_get_keys(key_file, "ServiceRecords", NULL, NULL);
+
+	for (handle = keys; handle && *handle; handle++) {
+		str = g_key_file_get_string(key_file, "ServiceRecords",
+						*handle, NULL);
+		if (!str)
+			continue;
+
+		rec = record_from_string(str);
+		recs = sdp_list_append(recs, rec);
+		g_free(str);
+	}
+
+	g_strfreev(keys);
+	g_key_file_free(key_file);
+
+	return recs;
+}
+
+const sdp_record_t *btd_device_get_record(struct btd_device *device,
+							const char *uuid)
+{
+	/* Load records from storage if there is nothing in cache */
+	if (!device->tmp_records) {
+		device->tmp_records = read_device_records(device);
+		if (!device->tmp_records)
+			return NULL;
+	}
+
+	return find_record_in_list(device->tmp_records, uuid);
+}
+
+struct btd_device *btd_device_ref(struct btd_device *device)
+{
+	__sync_fetch_and_add(&device->ref_count, 1);
+
+	return device;
+}
+
+void btd_device_unref(struct btd_device *device)
+{
+	if (__sync_sub_and_fetch(&device->ref_count, 1))
+		return;
+
+	if (!device->path) {
+		error("freeing device without an object path");
+		return;
+	}
+
+	DBG("Freeing device %s", device->path);
+
+	g_dbus_unregister_interface(dbus_conn, device->path, DEVICE_INTERFACE);
+}
+
+int device_get_appearance(struct btd_device *device, uint16_t *value)
+{
+	if (device->appearance == 0)
+		return -1;
+
+	if (value)
+		*value = device->appearance;
+
+	return 0;
+}
+
+void device_set_appearance(struct btd_device *device, uint16_t value)
+{
+	const char *icon = gap_appearance_to_icon(value);
+
+	if (device->appearance == value)
+		return;
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+					DEVICE_INTERFACE, "Appearance");
+
+	if (icon)
+		g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Icon");
+
+	device->appearance = value;
+	store_device_info(device);
+}
+
+static gboolean notify_attios(gpointer user_data)
+{
+	struct btd_device *device = user_data;
+
+	DBG("");
+
+	if (device->attrib == NULL)
+		return FALSE;
+
+	g_slist_foreach(device->attios_offline, attio_connected, device->attrib);
+	device->attios = g_slist_concat(device->attios, device->attios_offline);
+	device->attios_offline = NULL;
+
+	return FALSE;
+}
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+						attio_connect_cb cfunc,
+						attio_disconnect_cb dcfunc,
+						gpointer user_data)
+{
+	struct attio_data *attio;
+	static guint attio_id = 0;
+
+	DBG("%p registered ATT connection callback", device);
+
+	attio = g_new0(struct attio_data, 1);
+	attio->id = ++attio_id;
+	attio->cfunc = cfunc;
+	attio->dcfunc = dcfunc;
+	attio->user_data = user_data;
+
+	device_set_auto_connect(device, TRUE);
+
+	/* Check if there is no GAttrib associated to the device created by a
+	 * incoming connection */
+	if (!device->attrib)
+		device->attrib = attrib_from_device(device);
+
+	if (device->attrib && cfunc) {
+		device->attios_offline = g_slist_append(device->attios_offline,
+									attio);
+		g_idle_add(notify_attios, device);
+		return attio->id;
+	}
+
+	device->attios = g_slist_append(device->attios, attio);
+
+	return attio->id;
+}
+
+static int attio_id_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct attio_data *attio = a;
+	guint id = GPOINTER_TO_UINT(b);
+
+	return attio->id - id;
+}
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
+{
+	struct attio_data *attio;
+	GSList *l;
+
+	l = g_slist_find_custom(device->attios, GUINT_TO_POINTER(id),
+								attio_id_cmp);
+	if (l) {
+		attio = l->data;
+		device->attios = g_slist_remove(device->attios, attio);
+	} else {
+		l = g_slist_find_custom(device->attios_offline,
+					GUINT_TO_POINTER(id), attio_id_cmp);
+		if (!l)
+			return FALSE;
+
+		attio = l->data;
+		device->attios_offline = g_slist_remove(device->attios_offline,
+									attio);
+	}
+
+	g_free(attio);
+
+	return TRUE;
+}
+
+void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
+			uint16_t vendor, uint16_t product, uint16_t version)
+{
+	if (device->vendor_src == source && device->version == version &&
+			device->vendor == vendor && device->product == product)
+		return;
+
+	device->vendor_src = source;
+	device->vendor = vendor;
+	device->product = product;
+	device->version = version;
+
+	free(device->modalias);
+	device->modalias = bt_modalias(source, vendor, product, version);
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "Modalias");
+
+	store_device_info(device);
+}
+
+static void service_state_changed(struct btd_service *service,
+						btd_service_state_t old_state,
+						btd_service_state_t new_state,
+						void *user_data)
+{
+	struct btd_profile *profile = btd_service_get_profile(service);
+	struct btd_device *device = btd_service_get_device(service);
+	int err = btd_service_get_error(service);
+
+	if (new_state == BTD_SERVICE_STATE_CONNECTING ||
+				new_state == BTD_SERVICE_STATE_DISCONNECTING)
+		return;
+
+	if (old_state == BTD_SERVICE_STATE_CONNECTING)
+		device_profile_connected(device, profile, err);
+	else if (old_state == BTD_SERVICE_STATE_DISCONNECTING)
+		device_profile_disconnected(device, profile, err);
+}
+
+struct btd_service *btd_device_get_service(struct btd_device *dev,
+						const char *remote_uuid)
+{
+	GSList *l;
+
+	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+		struct btd_profile *p = btd_service_get_profile(service);
+
+		if (g_str_equal(p->remote_uuid, remote_uuid))
+			return service;
+	}
+
+	return NULL;
+}
+
+void btd_device_init(void)
+{
+	dbus_conn = btd_get_dbus_connection();
+	service_state_cb_id = btd_service_add_state_cb(
+						service_state_changed, NULL);
+}
+
+void btd_device_cleanup(void)
+{
+	btd_service_remove_state_cb(service_state_cb_id);
+}
diff --git a/bluez/repo/src/device.h b/bluez/repo/src/device.h
new file mode 100644
index 0000000..db10827
--- /dev/null
+++ b/bluez/repo/src/device.h
@@ -0,0 +1,163 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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
+ *
+ */
+
+#define DEVICE_INTERFACE	"org.bluez.Device1"
+
+struct btd_device;
+
+struct btd_device *device_create(struct btd_adapter *adapter,
+				const bdaddr_t *address, uint8_t bdaddr_type);
+struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
+				const char *address, GKeyFile *key_file);
+char *btd_device_get_storage_path(struct btd_device *device,
+				const char *filename);
+
+void btd_device_device_set_name(struct btd_device *device, const char *name);
+void device_store_cached_name(struct btd_device *dev, const char *name);
+void device_get_name(struct btd_device *device, char *name, size_t len);
+bool device_name_known(struct btd_device *device);
+void device_set_class(struct btd_device *device, uint32_t class);
+void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
+							uint8_t bdaddr_type);
+void device_set_bredr_support(struct btd_device *device);
+void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type);
+void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type);
+void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup);
+uint32_t btd_device_get_class(struct btd_device *device);
+uint16_t btd_device_get_vendor(struct btd_device *device);
+uint16_t btd_device_get_vendor_src(struct btd_device *device);
+uint16_t btd_device_get_product(struct btd_device *device);
+uint16_t btd_device_get_version(struct btd_device *device);
+void device_remove(struct btd_device *device, gboolean remove_stored);
+int device_address_cmp(gconstpointer a, gconstpointer b);
+int device_bdaddr_cmp(gconstpointer a, gconstpointer b);
+
+/* Struct used by device_addr_type_cmp() */
+struct device_addr_type {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+};
+
+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);
+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,
+							const char *uuid);
+GSList *btd_device_get_primaries(struct btd_device *device);
+struct gatt_db *btd_device_get_gatt_db(struct btd_device *device);
+struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device);
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device);
+void btd_device_gatt_set_service_changed(struct btd_device *device,
+						uint16_t start, uint16_t end);
+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_probe_profile(gpointer a, gpointer b);
+void device_remove_profile(gpointer a, gpointer b);
+struct btd_adapter *device_get_adapter(struct btd_device *device);
+const bdaddr_t *device_get_address(struct btd_device *device);
+const char *device_get_path(const struct btd_device *device);
+gboolean device_is_temporary(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);
+void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type);
+void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type);
+void btd_device_set_temporary(struct btd_device *device, bool temporary);
+void btd_device_set_trusted(struct btd_device *device, gboolean trusted);
+void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type);
+void device_set_legacy(struct btd_device *device, bool legacy);
+void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi,
+							int8_t delta_threshold);
+void device_set_rssi(struct btd_device *device, int8_t rssi);
+void device_set_tx_power(struct btd_device *device, int8_t tx_power);
+bool btd_device_is_connected(struct btd_device *dev);
+uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
+bool device_is_retrying(struct btd_device *device);
+void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
+							uint8_t status);
+gboolean device_is_bonding(struct btd_device *device, const char *sender);
+void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
+void device_bonding_failed(struct btd_device *device, uint8_t status);
+struct btd_adapter_pin_cb_iter *device_bonding_iter(struct btd_device *device);
+int device_bonding_attempt_retry(struct btd_device *device);
+long device_bonding_last_duration(struct btd_device *device);
+void device_bonding_restart_timer(struct btd_device *device);
+int device_request_pincode(struct btd_device *device, gboolean secure);
+int device_request_passkey(struct btd_device *device);
+int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
+							uint8_t confirm_hint);
+int device_notify_passkey(struct btd_device *device, uint32_t passkey,
+							uint8_t entered);
+int device_notify_pincode(struct btd_device *device, gboolean secure,
+							const char *pincode);
+void device_cancel_authentication(struct btd_device *device, gboolean aborted);
+gboolean device_is_authenticating(struct btd_device *device);
+void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type);
+void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type);
+void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
+bool device_is_disconnecting(struct btd_device *device);
+
+typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
+					void *user_data);
+
+guint device_add_disconnect_watch(struct btd_device *device,
+				disconnect_watch watch, void *user_data,
+				GDestroyNotify destroy);
+void device_remove_disconnect_watch(struct btd_device *device, guint id);
+int device_get_appearance(struct btd_device *device, uint16_t *value);
+void device_set_appearance(struct btd_device *device, uint16_t value);
+
+struct btd_device *btd_device_ref(struct btd_device *device);
+void btd_device_unref(struct btd_device *device);
+
+int device_block(struct btd_device *device, gboolean update_only);
+int device_unblock(struct btd_device *device, gboolean silent,
+							gboolean update_only);
+void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
+			uint16_t vendor, uint16_t product, uint16_t version);
+
+int device_connect_le(struct btd_device *dev);
+
+typedef void (*device_svc_cb_t) (struct btd_device *dev, int err,
+							void *user_data);
+
+unsigned int device_wait_for_svc_complete(struct btd_device *dev,
+							device_svc_cb_t func,
+							void *user_data);
+bool device_remove_svc_complete_callback(struct btd_device *dev,
+							unsigned int id);
+
+struct btd_service *btd_device_get_service(struct btd_device *dev,
+						const char *remote_uuid);
+
+int device_discover_services(struct btd_device *device);
+int btd_device_connect_services(struct btd_device *dev, GSList *services);
+
+void btd_device_init(void);
+void btd_device_cleanup(void);
diff --git a/bluez/repo/src/eir.c b/bluez/repo/src/eir.c
new file mode 100644
index 0000000..c984fa5
--- /dev/null
+++ b/bluez/repo/src/eir.c
@@ -0,0 +1,586 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+
+#include "src/shared/util.h"
+#include "uuid-helper.h"
+#include "eir.h"
+
+#define EIR_OOB_MIN (2 + 6)
+
+static void sd_free(void *data)
+{
+	struct eir_sd *sd = data;
+
+	free(sd->uuid);
+	g_free(sd);
+}
+
+void eir_data_free(struct eir_data *eir)
+{
+	g_slist_free_full(eir->services, free);
+	eir->services = NULL;
+	g_free(eir->name);
+	eir->name = NULL;
+	g_free(eir->hash);
+	eir->hash = NULL;
+	g_free(eir->randomizer);
+	eir->randomizer = NULL;
+	g_slist_free_full(eir->msd_list, g_free);
+	eir->msd_list = NULL;
+	g_slist_free_full(eir->sd_list, sd_free);
+	eir->sd_list = NULL;
+}
+
+static void eir_parse_uuid16(struct eir_data *eir, const void *data,
+								uint8_t len)
+{
+	const uint16_t *uuid16 = data;
+	uuid_t service;
+	char *uuid_str;
+	unsigned int i;
+
+	service.type = SDP_UUID16;
+	for (i = 0; i < len / 2; i++, uuid16++) {
+		service.value.uuid16 = get_le16(uuid16);
+
+		uuid_str = bt_uuid2string(&service);
+		if (!uuid_str)
+			continue;
+		eir->services = g_slist_append(eir->services, uuid_str);
+	}
+}
+
+static void eir_parse_uuid32(struct eir_data *eir, const void *data,
+								uint8_t len)
+{
+	const uint32_t *uuid32 = data;
+	uuid_t service;
+	char *uuid_str;
+	unsigned int i;
+
+	service.type = SDP_UUID32;
+	for (i = 0; i < len / 4; i++, uuid32++) {
+		service.value.uuid32 = get_le32(uuid32);
+
+		uuid_str = bt_uuid2string(&service);
+		if (!uuid_str)
+			continue;
+		eir->services = g_slist_append(eir->services, uuid_str);
+	}
+}
+
+static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data,
+								uint8_t len)
+{
+	const uint8_t *uuid_ptr = data;
+	uuid_t service;
+	char *uuid_str;
+	unsigned int i;
+	int k;
+
+	service.type = SDP_UUID128;
+	for (i = 0; i < len / 16; i++) {
+		for (k = 0; k < 16; k++)
+			service.value.uuid128.data[k] = uuid_ptr[16 - k - 1];
+		uuid_str = bt_uuid2string(&service);
+		if (!uuid_str)
+			continue;
+		eir->services = g_slist_append(eir->services, uuid_str);
+		uuid_ptr += 16;
+	}
+}
+
+static char *name2utf8(const uint8_t *name, uint8_t len)
+{
+	char utf8_name[HCI_MAX_NAME_LENGTH + 2];
+	int i;
+
+	if (g_utf8_validate((const char *) name, len, NULL))
+		return g_strndup((char *) name, len);
+
+	memset(utf8_name, 0, sizeof(utf8_name));
+	strncpy(utf8_name, (char *) name, len);
+
+	/* Assume ASCII, and replace all non-ASCII with spaces */
+	for (i = 0; utf8_name[i] != '\0'; i++) {
+		if (!isascii(utf8_name[i]))
+			utf8_name[i] = ' ';
+	}
+
+	/* Remove leading and trailing whitespace characters */
+	g_strstrip(utf8_name);
+
+	return g_strdup(utf8_name);
+}
+
+static void eir_parse_msd(struct eir_data *eir, const uint8_t *data,
+								uint8_t len)
+{
+	struct eir_msd *msd;
+
+	if (len < 2 || len > 2 + sizeof(msd->data))
+		return;
+
+	msd = g_malloc(sizeof(*msd));
+	msd->company = get_le16(data);
+	msd->data_len = len - 2;
+	memcpy(&msd->data, data + 2, msd->data_len);
+
+	eir->msd_list = g_slist_append(eir->msd_list, msd);
+}
+
+static void eir_parse_sd(struct eir_data *eir, uuid_t *service,
+					const uint8_t *data, uint8_t len)
+{
+	struct eir_sd *sd;
+	char *uuid;
+
+	uuid = bt_uuid2string(service);
+	if (!uuid)
+		return;
+
+	sd = g_malloc(sizeof(*sd));
+	sd->uuid = uuid;
+	sd->data_len = len;
+	memcpy(&sd->data, data, sd->data_len);
+
+	eir->sd_list = g_slist_append(eir->sd_list, sd);
+}
+
+static void eir_parse_uuid16_data(struct eir_data *eir, const uint8_t *data,
+								uint8_t len)
+{
+	uuid_t service;
+
+	if (len < 2 || len > EIR_SD_MAX_LEN)
+		return;
+
+	service.type = SDP_UUID16;
+	service.value.uuid16 = get_le16(data);
+	eir_parse_sd(eir, &service, data + 2, len - 2);
+}
+
+static void eir_parse_uuid32_data(struct eir_data *eir, const uint8_t *data,
+								uint8_t len)
+{
+	uuid_t service;
+
+	if (len < 4 || len > EIR_SD_MAX_LEN)
+		return;
+
+	service.type = SDP_UUID32;
+	service.value.uuid32 = get_le32(data);
+	eir_parse_sd(eir, &service, data + 4, len - 4);
+}
+
+static void eir_parse_uuid128_data(struct eir_data *eir, const uint8_t *data,
+								uint8_t len)
+{
+	uuid_t service;
+	int k;
+
+	if (len < 16 || len > EIR_SD_MAX_LEN)
+		return;
+
+	service.type = SDP_UUID128;
+
+	for (k = 0; k < 16; k++)
+		service.value.uuid128.data[k] = data[16 - k - 1];
+
+	eir_parse_sd(eir, &service, data + 16, len - 16);
+}
+
+void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
+{
+	uint16_t len = 0;
+
+	eir->flags = 0;
+	eir->tx_power = 127;
+
+	/* No EIR data to parse */
+	if (eir_data == NULL)
+		return;
+
+	while (len < eir_len - 1) {
+		uint8_t field_len = eir_data[0];
+		const uint8_t *data;
+		uint8_t data_len;
+
+		/* Check for the end of EIR */
+		if (field_len == 0)
+			break;
+
+		len += field_len + 1;
+
+		/* Do not continue EIR Data parsing if got incorrect length */
+		if (len > eir_len)
+			break;
+
+		data = &eir_data[2];
+		data_len = field_len - 1;
+
+		switch (eir_data[1]) {
+		case EIR_UUID16_SOME:
+		case EIR_UUID16_ALL:
+			eir_parse_uuid16(eir, data, data_len);
+			break;
+
+		case EIR_UUID32_SOME:
+		case EIR_UUID32_ALL:
+			eir_parse_uuid32(eir, data, data_len);
+			break;
+
+		case EIR_UUID128_SOME:
+		case EIR_UUID128_ALL:
+			eir_parse_uuid128(eir, data, data_len);
+			break;
+
+		case EIR_FLAGS:
+			if (data_len > 0)
+				eir->flags = *data;
+			break;
+
+		case EIR_NAME_SHORT:
+		case EIR_NAME_COMPLETE:
+			/* Some vendors put a NUL byte terminator into
+			 * the name */
+			while (data_len > 0 && data[data_len - 1] == '\0')
+				data_len--;
+
+			g_free(eir->name);
+
+			eir->name = name2utf8(data, data_len);
+			eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE;
+			break;
+
+		case EIR_TX_POWER:
+			if (data_len < 1)
+				break;
+			eir->tx_power = (int8_t) data[0];
+			break;
+
+		case EIR_CLASS_OF_DEV:
+			if (data_len < 3)
+				break;
+			eir->class = data[0] | (data[1] << 8) |
+							(data[2] << 16);
+			break;
+
+		case EIR_GAP_APPEARANCE:
+			if (data_len < 2)
+				break;
+			eir->appearance = get_le16(data);
+			break;
+
+		case EIR_SSP_HASH:
+			if (data_len < 16)
+				break;
+			eir->hash = g_memdup(data, 16);
+			break;
+
+		case EIR_SSP_RANDOMIZER:
+			if (data_len < 16)
+				break;
+			eir->randomizer = g_memdup(data, 16);
+			break;
+
+		case EIR_DEVICE_ID:
+			if (data_len < 8)
+				break;
+
+			eir->did_source = data[0] | (data[1] << 8);
+			eir->did_vendor = data[2] | (data[3] << 8);
+			eir->did_product = data[4] | (data[5] << 8);
+			eir->did_version = data[6] | (data[7] << 8);
+			break;
+
+		case EIR_SVC_DATA16:
+			eir_parse_uuid16_data(eir, data, data_len);
+			break;
+
+		case EIR_SVC_DATA32:
+			eir_parse_uuid32_data(eir, data, data_len);
+			break;
+
+		case EIR_SVC_DATA128:
+			eir_parse_uuid128_data(eir, data, data_len);
+			break;
+
+		case EIR_MANUFACTURER_DATA:
+			eir_parse_msd(eir, data, data_len);
+			break;
+
+		}
+
+		eir_data += field_len + 1;
+	}
+}
+
+int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len)
+{
+
+	if (eir_len < EIR_OOB_MIN)
+		return -1;
+
+	if (eir_len != get_le16(eir_data))
+		return -1;
+
+	eir_data += sizeof(uint16_t);
+	eir_len -= sizeof(uint16_t);
+
+	memcpy(&eir->addr, eir_data, sizeof(bdaddr_t));
+	eir_data += sizeof(bdaddr_t);
+	eir_len -= sizeof(bdaddr_t);
+
+	/* optional OOB EIR data */
+	if (eir_len > 0)
+		eir_parse(eir, eir_data, eir_len);
+
+	return 0;
+}
+
+#define SIZEOF_UUID128 16
+
+static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr,
+							uint16_t *eir_len)
+{
+	int i, k, uuid_count = 0;
+	uint16_t len = *eir_len;
+	uint8_t *uuid128;
+	bool truncated = false;
+
+	/* Store UUIDs in place, skip 2 bytes to write type and length later */
+	uuid128 = ptr + 2;
+
+	for (; list; list = list->next) {
+		sdp_record_t *rec = list->data;
+		uuid_t *uuid = &rec->svclass;
+		uint8_t *uuid128_data = uuid->value.uuid128.data;
+
+		if (uuid->type != SDP_UUID128)
+			continue;
+
+		/* Stop if not enough space to put next UUID128 */
+		if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) {
+			truncated = true;
+			break;
+		}
+
+		/* Check for duplicates, EIR data is Little Endian */
+		for (i = 0; i < uuid_count; i++) {
+			for (k = 0; k < SIZEOF_UUID128; k++) {
+				if (uuid128[i * SIZEOF_UUID128 + k] !=
+					uuid128_data[SIZEOF_UUID128 - 1 - k])
+					break;
+			}
+			if (k == SIZEOF_UUID128)
+				break;
+		}
+
+		if (i < uuid_count)
+			continue;
+
+		/* EIR data is Little Endian */
+		for (k = 0; k < SIZEOF_UUID128; k++)
+			uuid128[uuid_count * SIZEOF_UUID128 + k] =
+				uuid128_data[SIZEOF_UUID128 - 1 - k];
+
+		len += SIZEOF_UUID128;
+		uuid_count++;
+	}
+
+	if (uuid_count > 0 || truncated) {
+		/* EIR Data length */
+		ptr[0] = (uuid_count * SIZEOF_UUID128) + 1;
+		/* EIR Data type */
+		ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL;
+		len += 2;
+		*eir_len = len;
+	}
+}
+
+int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
+			const uint8_t *hash, const uint8_t *randomizer,
+			uint16_t did_vendor, uint16_t did_product,
+			uint16_t did_version, uint16_t did_source,
+			sdp_list_t *uuids, uint8_t *data)
+{
+	sdp_list_t *l;
+	uint8_t *ptr = data;
+	uint16_t eir_optional_len = 0;
+	uint16_t eir_total_len;
+	uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2];
+	int i, uuid_count = 0;
+	bool truncated = false;
+	size_t name_len;
+
+	eir_total_len =  sizeof(uint16_t) + sizeof(bdaddr_t);
+	ptr += sizeof(uint16_t);
+
+	memcpy(ptr, addr, sizeof(bdaddr_t));
+	ptr += sizeof(bdaddr_t);
+
+	if (cod > 0) {
+		uint8_t class[3];
+
+		class[0] = (uint8_t) cod;
+		class[1] = (uint8_t) (cod >> 8);
+		class[2] = (uint8_t) (cod >> 16);
+
+		*ptr++ = 4;
+		*ptr++ = EIR_CLASS_OF_DEV;
+
+		memcpy(ptr, class, sizeof(class));
+		ptr += sizeof(class);
+
+		eir_optional_len += sizeof(class) + 2;
+	}
+
+	if (hash) {
+		*ptr++ = 17;
+		*ptr++ = EIR_SSP_HASH;
+
+		memcpy(ptr, hash, 16);
+		ptr += 16;
+
+		eir_optional_len += 16 + 2;
+	}
+
+	if (randomizer) {
+		*ptr++ = 17;
+		*ptr++ = EIR_SSP_RANDOMIZER;
+
+		memcpy(ptr, randomizer, 16);
+		ptr += 16;
+
+		eir_optional_len += 16 + 2;
+	}
+
+	name_len = strlen(name);
+
+	if (name_len > 0) {
+		/* EIR Data type */
+		if (name_len > 48) {
+			name_len = 48;
+			ptr[1] = EIR_NAME_SHORT;
+		} else
+			ptr[1] = EIR_NAME_COMPLETE;
+
+		/* EIR Data length */
+		ptr[0] = name_len + 1;
+
+		memcpy(ptr + 2, name, name_len);
+
+		eir_optional_len += (name_len + 2);
+		ptr += (name_len + 2);
+	}
+
+	if (did_vendor != 0x0000) {
+		*ptr++ = 9;
+		*ptr++ = EIR_DEVICE_ID;
+		*ptr++ = (did_source & 0x00ff);
+		*ptr++ = (did_source & 0xff00) >> 8;
+		*ptr++ = (did_vendor & 0x00ff);
+		*ptr++ = (did_vendor & 0xff00) >> 8;
+		*ptr++ = (did_product & 0x00ff);
+		*ptr++ = (did_product & 0xff00) >> 8;
+		*ptr++ = (did_version & 0x00ff);
+		*ptr++ = (did_version & 0xff00) >> 8;
+		eir_optional_len += 10;
+	}
+
+	/* Group all UUID16 types */
+	for (l = uuids; l != NULL; l = l->next) {
+		sdp_record_t *rec = l->data;
+		uuid_t *uuid = &rec->svclass;
+
+		if (uuid->type != SDP_UUID16)
+			continue;
+
+		if (uuid->value.uuid16 < 0x1100)
+			continue;
+
+		if (uuid->value.uuid16 == PNP_INFO_SVCLASS_ID)
+			continue;
+
+		/* Stop if not enough space to put next UUID16 */
+		if ((eir_optional_len + 2 + sizeof(uint16_t)) >
+				HCI_MAX_EIR_LENGTH) {
+			truncated = true;
+			break;
+		}
+
+		/* Check for duplicates */
+		for (i = 0; i < uuid_count; i++)
+			if (uuid16[i] == uuid->value.uuid16)
+				break;
+
+		if (i < uuid_count)
+			continue;
+
+		uuid16[uuid_count++] = uuid->value.uuid16;
+		eir_optional_len += sizeof(uint16_t);
+	}
+
+	if (uuid_count > 0) {
+		/* EIR Data length */
+		ptr[0] = (uuid_count * sizeof(uint16_t)) + 1;
+		/* EIR Data type */
+		ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
+
+		ptr += 2;
+		eir_optional_len += 2;
+
+		for (i = 0; i < uuid_count; i++) {
+			*ptr++ = (uuid16[i] & 0x00ff);
+			*ptr++ = (uuid16[i] & 0xff00) >> 8;
+		}
+	}
+
+	/* Group all UUID128 types */
+	if (eir_optional_len <= HCI_MAX_EIR_LENGTH - 2)
+		eir_generate_uuid128(uuids, ptr, &eir_optional_len);
+
+	eir_total_len += eir_optional_len;
+
+	/* store total length */
+	put_le16(eir_total_len, data);
+
+	return eir_total_len;
+}
diff --git a/bluez/repo/src/eir.h b/bluez/repo/src/eir.h
new file mode 100644
index 0000000..219ee79
--- /dev/null
+++ b/bluez/repo/src/eir.h
@@ -0,0 +1,104 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+
+#define EIR_FLAGS                   0x01  /* flags */
+#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT              0x08  /* shortened local name */
+#define EIR_NAME_COMPLETE           0x09  /* complete local name */
+#define EIR_TX_POWER                0x0A  /* transmit power level */
+#define EIR_CLASS_OF_DEV            0x0D  /* Class of Device */
+#define EIR_SSP_HASH                0x0E  /* SSP Hash */
+#define EIR_SSP_RANDOMIZER          0x0F  /* SSP Randomizer */
+#define EIR_DEVICE_ID               0x10  /* device ID */
+#define EIR_SOLICIT16               0x14  /* LE: Solicit UUIDs, 16-bit */
+#define EIR_SOLICIT128              0x15  /* LE: Solicit UUIDs, 128-bit */
+#define EIR_SVC_DATA16              0x16  /* LE: Service data, 16-bit UUID */
+#define EIR_PUB_TRGT_ADDR           0x17  /* LE: Public Target Address */
+#define EIR_RND_TRGT_ADDR           0x18  /* LE: Random Target Address */
+#define EIR_GAP_APPEARANCE          0x19  /* GAP appearance */
+#define EIR_SOLICIT32               0x1F  /* LE: Solicit UUIDs, 32-bit */
+#define EIR_SVC_DATA32              0x20  /* LE: Service data, 32-bit UUID */
+#define EIR_SVC_DATA128             0x21  /* LE: Service data, 128-bit UUID */
+#define EIR_MANUFACTURER_DATA       0xFF  /* Manufacturer Specific Data */
+
+/* Flags Descriptions */
+#define EIR_LIM_DISC                0x01 /* LE Limited Discoverable Mode */
+#define EIR_GEN_DISC                0x02 /* LE General Discoverable Mode */
+#define EIR_BREDR_UNSUP             0x04 /* BR/EDR Not Supported */
+#define EIR_CONTROLLER              0x08 /* Simultaneous LE and BR/EDR to Same
+					    Device Capable (Controller) */
+#define EIR_SIM_HOST                0x10 /* Simultaneous LE and BR/EDR to Same
+					    Device Capable (Host) */
+
+#define EIR_SD_MAX_LEN              238  /* 240 (EIR) - 2 (len) */
+#define EIR_MSD_MAX_LEN             236  /* 240 (EIR) - 2 (len & type) - 2 */
+
+struct eir_msd {
+	uint16_t company;
+	uint8_t data[EIR_MSD_MAX_LEN];
+	uint8_t data_len;
+};
+
+struct eir_sd {
+	char *uuid;
+	uint8_t data[EIR_SD_MAX_LEN];
+	uint8_t data_len;
+};
+
+struct eir_data {
+	GSList *services;
+	unsigned int flags;
+	char *name;
+	uint32_t class;
+	uint16_t appearance;
+	bool name_complete;
+	int8_t tx_power;
+	uint8_t *hash;
+	uint8_t *randomizer;
+	bdaddr_t addr;
+	uint16_t did_vendor;
+	uint16_t did_product;
+	uint16_t did_version;
+	uint16_t did_source;
+	GSList *msd_list;
+	GSList *sd_list;
+};
+
+void eir_data_free(struct eir_data *eir);
+void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len);
+int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len);
+int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
+			const uint8_t *hash, const uint8_t *randomizer,
+			uint16_t did_vendor, uint16_t did_product,
+			uint16_t did_version, uint16_t did_source,
+			sdp_list_t *uuids, uint8_t *data);
diff --git a/bluez/repo/src/error.c b/bluez/repo/src/error.c
new file mode 100644
index 0000000..8951707
--- /dev/null
+++ b/bluez/repo/src/error.c
@@ -0,0 +1,128 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2007-2008  Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ *  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 "gdbus/gdbus.h"
+
+#include "error.h"
+
+DBusMessage *btd_error_invalid_args(DBusMessage *msg)
+{
+	return btd_error_invalid_args_str(msg,
+					"Invalid arguments in method call");
+}
+
+DBusMessage *btd_error_invalid_args_str(DBusMessage *msg, const char *str)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+					"%s", str);
+}
+
+DBusMessage *btd_error_busy(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
+					"Operation already in progress");
+}
+
+DBusMessage *btd_error_already_exists(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists",
+					"Already Exists");
+}
+
+DBusMessage *btd_error_not_supported(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported",
+					"Operation is not supported");
+}
+
+DBusMessage *btd_error_not_connected(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected",
+					"Not Connected");
+}
+
+DBusMessage *btd_error_already_connected(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyConnected",
+					"Already Connected");
+}
+
+DBusMessage *btd_error_in_progress(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
+					"In Progress");
+}
+
+DBusMessage *btd_error_not_available(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
+					"Operation currently not available");
+}
+
+DBusMessage *btd_error_does_not_exist(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist",
+					"Does Not Exist");
+}
+
+DBusMessage *btd_error_not_authorized(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized",
+						"Operation Not Authorized");
+}
+
+DBusMessage *btd_error_not_permitted(DBusMessage *msg, const char *str)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotPermitted",
+					"%s", str);
+}
+
+DBusMessage *btd_error_no_such_adapter(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter",
+					"No such adapter");
+}
+
+DBusMessage *btd_error_agent_not_available(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".AgentNotAvailable",
+					"Agent Not Available");
+}
+
+DBusMessage *btd_error_not_ready(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady",
+					"Resource Not Ready");
+}
+
+DBusMessage *btd_error_failed(DBusMessage *msg, const char *str)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE
+					".Failed", "%s", str);
+}
diff --git a/bluez/repo/src/error.h b/bluez/repo/src/error.h
new file mode 100644
index 0000000..7c8cad0
--- /dev/null
+++ b/bluez/repo/src/error.h
@@ -0,0 +1,45 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2007-2008  Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ *  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 <dbus/dbus.h>
+
+#define ERROR_INTERFACE "org.bluez.Error"
+
+DBusMessage *btd_error_invalid_args(DBusMessage *msg);
+DBusMessage *btd_error_invalid_args_str(DBusMessage *msg, const char *str);
+DBusMessage *btd_error_busy(DBusMessage *msg);
+DBusMessage *btd_error_already_exists(DBusMessage *msg);
+DBusMessage *btd_error_not_supported(DBusMessage *msg);
+DBusMessage *btd_error_not_connected(DBusMessage *msg);
+DBusMessage *btd_error_already_connected(DBusMessage *msg);
+DBusMessage *btd_error_not_available(DBusMessage *msg);
+DBusMessage *btd_error_in_progress(DBusMessage *msg);
+DBusMessage *btd_error_does_not_exist(DBusMessage *msg);
+DBusMessage *btd_error_not_authorized(DBusMessage *msg);
+DBusMessage *btd_error_not_permitted(DBusMessage *msg, const char *str);
+DBusMessage *btd_error_no_such_adapter(DBusMessage *msg);
+DBusMessage *btd_error_agent_not_available(DBusMessage *msg);
+DBusMessage *btd_error_not_ready(DBusMessage *msg);
+DBusMessage *btd_error_failed(DBusMessage *msg, const char *str);
diff --git a/bluez/repo/src/gatt-client.c b/bluez/repo/src/gatt-client.c
new file mode 100644
index 0000000..39f6646
--- /dev/null
+++ b/bluez/repo/src/gatt-client.c
@@ -0,0 +1,1929 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google 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 <stdbool.h>
+#include <stdint.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "log.h"
+#include "error.h"
+#include "adapter.h"
+#include "device.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/util.h"
+#include "gatt-client.h"
+#include "dbus-common.h"
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#define GATT_SERVICE_IFACE		"org.bluez.GattService1"
+#define GATT_CHARACTERISTIC_IFACE	"org.bluez.GattCharacteristic1"
+#define GATT_DESCRIPTOR_IFACE		"org.bluez.GattDescriptor1"
+
+struct btd_gatt_client {
+	struct btd_device *device;
+	bool ready;
+	char devaddr[18];
+	struct gatt_db *db;
+	struct bt_gatt_client *gatt;
+
+	struct queue *services;
+	struct queue *all_notify_clients;
+};
+
+struct service {
+	struct btd_gatt_client *client;
+	bool primary;
+	uint16_t start_handle;
+	uint16_t end_handle;
+	bt_uuid_t uuid;
+	char *path;
+	struct queue *chrcs;
+	bool chrcs_ready;
+	struct queue *pending_ext_props;
+	guint idle_id;
+};
+
+struct characteristic {
+	struct service *service;
+	struct gatt_db_attribute *attr;
+	uint16_t handle;
+	uint16_t value_handle;
+	uint8_t props;
+	uint16_t ext_props;
+	uint16_t ext_props_handle;
+	bt_uuid_t uuid;
+	char *path;
+
+	unsigned int read_id;
+	unsigned int write_id;
+
+	struct queue *descs;
+
+	bool notifying;
+	struct queue *notify_clients;
+};
+
+struct descriptor {
+	struct characteristic *chrc;
+	struct gatt_db_attribute *attr;
+	uint16_t handle;
+	bt_uuid_t uuid;
+	char *path;
+
+	unsigned int read_id;
+	unsigned int write_id;
+};
+
+static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16)
+{
+	bt_uuid_t uuid16;
+
+	bt_uuid16_create(&uuid16, u16);
+
+	return bt_uuid_cmp(uuid, &uuid16) == 0;
+}
+
+static gboolean descriptor_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	char uuid[MAX_LEN_UUID_STR + 1];
+	const char *ptr = uuid;
+	struct descriptor *desc = data;
+
+	bt_uuid_to_string(&desc->uuid, uuid, sizeof(uuid));
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+	return TRUE;
+}
+
+static gboolean descriptor_get_characteristic(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct descriptor *desc = data;
+	const char *str = desc->chrc->path;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
+
+	return TRUE;
+}
+
+static void read_cb(struct gatt_db_attribute *attrib, int err,
+				const uint8_t *value, size_t length,
+				void *user_data)
+{
+	DBusMessageIter *array = user_data;
+
+	if (err)
+		return;
+
+	dbus_message_iter_append_fixed_array(array, DBUS_TYPE_BYTE, &value,
+								length);
+}
+
+static gboolean descriptor_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct descriptor *desc = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+	gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_cb, &array);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static void read_check_cb(struct gatt_db_attribute *attrib, int err,
+				const uint8_t *value, size_t length,
+				void *user_data)
+{
+	gboolean *ret = user_data;
+
+	if (err || length == 0) {
+		*ret = FALSE;
+		return;
+	}
+
+	*ret = TRUE;
+}
+
+static gboolean descriptor_value_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct descriptor *desc = data;
+	gboolean ret;
+
+	gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_check_cb, &ret);
+
+	return ret;
+}
+
+static bool parse_value_arg(DBusMessage *msg, uint8_t **value,
+							size_t *value_len)
+{
+	DBusMessageIter iter, array;
+	uint8_t *val;
+	int len;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &val, &len);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
+		return false;
+
+	if (len < 0)
+		return false;
+
+	*value = val;
+	*value_len = len;
+
+	return true;
+}
+
+typedef bool (*async_dbus_op_complete_t)(void *data);
+
+struct async_dbus_op {
+	int ref_count;
+	DBusMessage *msg;
+	void *data;
+	uint16_t offset;
+	async_dbus_op_complete_t complete;
+};
+
+static void async_dbus_op_free(void *data)
+{
+	struct async_dbus_op *op = data;
+
+	if (op->msg)
+		dbus_message_unref(op->msg);
+
+	free(op);
+}
+
+static struct async_dbus_op *async_dbus_op_ref(struct async_dbus_op *op)
+{
+	__sync_fetch_and_add(&op->ref_count, 1);
+
+	return op;
+}
+
+static void async_dbus_op_unref(void *data)
+{
+	struct async_dbus_op *op = data;
+
+	if (__sync_sub_and_fetch(&op->ref_count, 1))
+		return;
+
+	async_dbus_op_free(op);
+}
+
+static void message_append_byte_array(DBusMessage *msg, const uint8_t *bytes,
+								size_t len)
+{
+	DBusMessageIter iter, array;
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &bytes,
+									len);
+	dbus_message_iter_close_container(&iter, &array);
+}
+
+static DBusMessage *create_gatt_dbus_error(DBusMessage *msg, uint8_t att_ecode)
+{
+	switch (att_ecode) {
+	case BT_ATT_ERROR_READ_NOT_PERMITTED:
+		return btd_error_not_permitted(msg, "Read not permitted");
+	case BT_ATT_ERROR_WRITE_NOT_PERMITTED:
+		return btd_error_not_permitted(msg, "Write not permitted");
+	case BT_ATT_ERROR_AUTHENTICATION:
+	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION:
+	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+		return btd_error_not_permitted(msg, "Not paired");
+	case BT_ATT_ERROR_INVALID_OFFSET:
+		return btd_error_invalid_args_str(msg, "Invalid offset");
+	case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN:
+		return btd_error_invalid_args_str(msg, "Invalid Length");
+	case BT_ATT_ERROR_AUTHORIZATION:
+		return btd_error_not_authorized(msg);
+	case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED:
+		return btd_error_not_supported(msg);
+	case 0:
+		return btd_error_failed(msg, "Operation failed");
+	default:
+		return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+				"Operation failed with ATT error: 0x%02x",
+				att_ecode);
+	}
+
+	return NULL;
+}
+
+static void write_descriptor_cb(struct gatt_db_attribute *attr, int err,
+								void *user_data)
+{
+	struct descriptor *desc = user_data;
+
+	if (err)
+		return;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), desc->path,
+					GATT_DESCRIPTOR_IFACE, "Value");
+}
+
+static void read_op_cb(struct gatt_db_attribute *attrib, int err,
+				const uint8_t *value, size_t length,
+				void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	DBusMessage *reply;
+
+	if (err) {
+		error("Failed to read attribute");
+		return;
+	}
+
+	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
+	if (!reply) {
+		error("Failed to allocate D-Bus message reply");
+		return;
+	}
+
+	message_append_byte_array(reply, value, length);
+
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+static void desc_read_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	struct descriptor *desc = op->data;
+	struct service *service = desc->chrc->service;
+	DBusMessage *reply;
+
+	if (!success)
+		goto fail;
+
+	if (!op->offset)
+		gatt_db_attribute_reset(desc->attr);
+
+	if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0,
+					NULL, write_descriptor_cb, desc)) {
+		error("Failed to store attribute");
+		goto fail;
+	}
+
+	/*
+	 * If the value length is exactly MTU-1, then we may not have read the
+	 * entire value. Perform a long read to obtain the rest, otherwise,
+	 * we're done.
+	 */
+	if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) {
+		op->offset += length;
+		desc->read_id = bt_gatt_client_read_long_value(
+							service->client->gatt,
+							desc->handle,
+							op->offset,
+							desc_read_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+		if (desc->read_id)
+			return;
+	}
+
+	/* Read the stored data from db */
+	if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) {
+		error("Failed to read database");
+		goto fail;
+	}
+
+	desc->read_id = 0;
+
+	return;
+
+fail:
+	reply = create_gatt_dbus_error(op->msg, att_ecode);
+	desc->read_id = 0;
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	return;
+}
+
+static DBusMessage *descriptor_read_value(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct descriptor *desc = user_data;
+	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+	struct async_dbus_op *op;
+
+	if (!gatt)
+		return btd_error_failed(msg, "Not connected");
+
+	if (desc->read_id)
+		return btd_error_in_progress(msg);
+
+	op = new0(struct async_dbus_op, 1);
+	op->msg = dbus_message_ref(msg);
+	op->data = desc;
+
+	desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
+							desc_read_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+	if (desc->read_id)
+		return NULL;
+
+	async_dbus_op_free(op);
+
+	return btd_error_failed(msg, "Failed to send read request");
+}
+
+static void write_result_cb(bool success, bool reliable_error,
+					uint8_t att_ecode, void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	DBusMessage *reply;
+
+	if (op->complete && !op->complete(op->data)) {
+		reply = btd_error_failed(op->msg, "Operation failed");
+		goto done;
+	}
+
+	if (!success) {
+		if (reliable_error)
+			reply = btd_error_failed(op->msg,
+						"Reliable write failed");
+		else
+			reply = create_gatt_dbus_error(op->msg, att_ecode);
+
+		goto done;
+	}
+
+	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
+	if (!reply) {
+		error("Failed to allocate D-Bus message reply");
+		return;
+	}
+
+done:
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+
+static void write_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	write_result_cb(success, false, att_ecode, user_data);
+}
+
+static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
+					struct bt_gatt_client *gatt,
+					bool reliable, const uint8_t *value,
+					size_t value_len, void *data,
+					async_dbus_op_complete_t complete)
+{
+	struct async_dbus_op *op;
+	unsigned int id;
+
+	op = new0(struct async_dbus_op, 1);
+	op->msg = dbus_message_ref(msg);
+	op->data = data;
+	op->complete = complete;
+
+	id = bt_gatt_client_write_long_value(gatt, reliable, handle,
+							0, value, value_len,
+							write_result_cb, op,
+							async_dbus_op_free);
+
+	if (!id)
+		async_dbus_op_free(op);
+
+	return id;
+}
+
+static unsigned int start_write_request(DBusMessage *msg, uint16_t handle,
+					struct bt_gatt_client *gatt,
+					const uint8_t *value, size_t value_len,
+					void *data,
+					async_dbus_op_complete_t complete)
+{
+	struct async_dbus_op *op;
+	unsigned int id;
+
+	op = new0(struct async_dbus_op, 1);
+	op->msg = dbus_message_ref(msg);
+	op->data = data;
+	op->complete = complete;
+
+	id = bt_gatt_client_write_value(gatt, handle, value, value_len,
+							write_cb, op,
+							async_dbus_op_free);
+	if (!id)
+		async_dbus_op_free(op);
+
+	return id;
+}
+
+static bool desc_write_complete(void *data)
+{
+	struct descriptor *desc = data;
+
+	desc->write_id = 0;
+
+	/*
+	 * The descriptor might have been unregistered during the read. Return
+	 * failure.
+	 */
+	return !!desc->chrc;
+}
+
+static DBusMessage *descriptor_write_value(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct descriptor *desc = user_data;
+	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+	uint8_t *value = NULL;
+	size_t value_len = 0;
+
+	if (!gatt)
+		return btd_error_failed(msg, "Not connected");
+
+	if (desc->write_id)
+		return btd_error_in_progress(msg);
+
+	if (!parse_value_arg(msg, &value, &value_len))
+		return btd_error_invalid_args(msg);
+
+	/*
+	 * Don't allow writing to Client Characteristic Configuration
+	 * descriptors. We achieve this through the StartNotify and StopNotify
+	 * methods on GattCharacteristic1.
+	 */
+	if (uuid_cmp(&desc->uuid, GATT_CLIENT_CHARAC_CFG_UUID))
+		return btd_error_not_permitted(msg, "Write not permitted");
+
+	/*
+	 * Based on the value length and the MTU, either use a write or a long
+	 * write.
+	 */
+	if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3)
+		desc->write_id = start_write_request(msg, desc->handle,
+							gatt, value,
+							value_len, desc,
+							desc_write_complete);
+	else
+		desc->write_id = start_long_write(msg, desc->handle,
+							gatt, false, value,
+							value_len, desc,
+							desc_write_complete);
+
+	if (!desc->write_id)
+		return btd_error_failed(msg, "Failed to initiate write");
+
+	return NULL;
+}
+
+static const GDBusPropertyTable descriptor_properties[] = {
+	{ "UUID", "s", descriptor_get_uuid, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Characteristic", "o", descriptor_get_characteristic, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+static const GDBusMethodTable descriptor_methods[] = {
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
+						GDBUS_ARGS({ "value", "ay" }),
+						descriptor_read_value) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
+						GDBUS_ARGS({ "value", "ay" }),
+						NULL,
+						descriptor_write_value) },
+	{ }
+};
+
+static void descriptor_free(void *data)
+{
+	struct descriptor *desc = data;
+
+	g_free(desc->path);
+	free(desc);
+}
+
+static struct descriptor *descriptor_create(struct gatt_db_attribute *attr,
+						struct characteristic *chrc)
+{
+	struct descriptor *desc;
+
+	desc = new0(struct descriptor, 1);
+	desc->chrc = chrc;
+	desc->attr = attr;
+	desc->handle = gatt_db_attribute_get_handle(attr);
+
+	bt_uuid_to_uuid128(gatt_db_attribute_get_type(attr), &desc->uuid);
+
+	desc->path = g_strdup_printf("%s/desc%04x", chrc->path, desc->handle);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(), desc->path,
+						GATT_DESCRIPTOR_IFACE,
+						descriptor_methods, NULL,
+						descriptor_properties,
+						desc, descriptor_free)) {
+		error("Unable to register GATT descriptor with handle 0x%04x",
+								desc->handle);
+		descriptor_free(desc);
+
+		return NULL;
+	}
+
+	DBG("Exported GATT characteristic descriptor: %s", desc->path);
+
+	if (uuid_cmp(&desc->uuid, GATT_CHARAC_EXT_PROPER_UUID))
+		chrc->ext_props_handle = desc->handle;
+
+	return desc;
+}
+
+static void unregister_descriptor(void *data)
+{
+	struct descriptor *desc = data;
+	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+
+	DBG("Removing GATT descriptor: %s", desc->path);
+
+	if (desc->read_id)
+		bt_gatt_client_cancel(gatt, desc->read_id);
+
+	if (desc->write_id)
+		bt_gatt_client_cancel(gatt, desc->write_id);
+
+	desc->chrc = NULL;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), desc->path,
+							GATT_DESCRIPTOR_IFACE);
+}
+
+static gboolean characteristic_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	char uuid[MAX_LEN_UUID_STR + 1];
+	const char *ptr = uuid;
+	struct characteristic *chrc = data;
+
+	bt_uuid_to_string(&chrc->uuid, uuid, sizeof(uuid));
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+	return TRUE;
+}
+
+static gboolean characteristic_get_service(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	const char *str = chrc->service->path;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
+
+	return TRUE;
+}
+
+static gboolean characteristic_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+	gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_cb, &array);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean characteristic_value_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct characteristic *chrc = data;
+	gboolean ret;
+
+	gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_check_cb, &ret);
+
+	return ret;
+}
+
+static gboolean characteristic_get_notifying(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	dbus_bool_t notifying = chrc->notifying ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &notifying);
+
+	return TRUE;
+}
+
+struct chrc_prop_data {
+	uint8_t prop;
+	char *str;
+};
+
+static struct chrc_prop_data chrc_props[] = {
+	/* Default Properties */
+	{ BT_GATT_CHRC_PROP_BROADCAST,		"broadcast" },
+	{ BT_GATT_CHRC_PROP_READ,		"read" },
+	{ BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP,	"write-without-response" },
+	{ BT_GATT_CHRC_PROP_WRITE,		"write" },
+	{ BT_GATT_CHRC_PROP_NOTIFY,		"notify" },
+	{ BT_GATT_CHRC_PROP_INDICATE,		"indicate" },
+	{ BT_GATT_CHRC_PROP_AUTH,		"authenticated-signed-writes" },
+	{ BT_GATT_CHRC_PROP_EXT_PROP,		"extended-properties" }
+};
+
+static struct chrc_prop_data chrc_ext_props[] = {
+	/* Extended Properties */
+	{ BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE,	"reliable-write" },
+	{ BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX,	"writable-auxiliaries" }
+};
+
+static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	DBusMessageIter array;
+	unsigned i;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+	for (i = 0; i < NELEM(chrc_props); i++) {
+		if (chrc->props & chrc_props[i].prop)
+			dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+							&chrc_props[i].str);
+	}
+
+	for (i = 0; i < NELEM(chrc_ext_props); i++) {
+		if (chrc->ext_props & chrc_ext_props[i].prop)
+			dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+							&chrc_ext_props[i].str);
+	}
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
+								void *user_data)
+{
+	struct characteristic *chrc = user_data;
+
+	if (err)
+		return;
+
+	g_dbus_emit_property_changed_full(btd_get_dbus_connection(),
+				chrc->path, GATT_CHARACTERISTIC_IFACE,
+				"Value", G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH);
+
+}
+
+static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	struct characteristic *chrc = op->data;
+	struct service *service = chrc->service;
+	DBusMessage *reply;
+
+	if (!success)
+		goto fail;
+
+	if (!op->offset)
+		gatt_db_attribute_reset(chrc->attr);
+
+	if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0,
+					NULL, write_characteristic_cb, chrc)) {
+		error("Failed to store attribute");
+		goto fail;
+	}
+
+	/*
+	 * If the value length is exactly MTU-1, then we may not have read the
+	 * entire value. Perform a long read to obtain the rest, otherwise,
+	 * we're done.
+	 */
+	if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) {
+		op->offset += length;
+		chrc->read_id = bt_gatt_client_read_long_value(
+							service->client->gatt,
+							chrc->value_handle,
+							op->offset,
+							chrc_read_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+		if (chrc->read_id)
+			return;
+	}
+
+	chrc->read_id = 0;
+
+	/* Read the stored data from db */
+	if (!gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op)) {
+		error("Failed to read database");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	reply = create_gatt_dbus_error(op->msg, att_ecode);
+	chrc->read_id = 0;
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+static DBusMessage *characteristic_read_value(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	struct async_dbus_op *op;
+
+	if (!gatt)
+		return btd_error_failed(msg, "Not connected");
+
+	if (chrc->read_id)
+		return btd_error_in_progress(msg);
+
+	op = new0(struct async_dbus_op, 1);
+	op->msg = dbus_message_ref(msg);
+	op->data = chrc;
+
+	chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
+							chrc_read_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+	if (chrc->read_id)
+		return NULL;
+
+	async_dbus_op_free(op);
+
+	return btd_error_failed(msg, "Failed to send read request");
+}
+
+static bool chrc_write_complete(void *data)
+{
+	struct characteristic *chrc = data;
+
+	chrc->write_id = 0;
+
+	/*
+	 * The characteristic might have been unregistered during the read.
+	 * Return failure.
+	 */
+	return !!chrc->service;
+}
+
+static DBusMessage *characteristic_write_value(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	uint8_t *value = NULL;
+	size_t value_len = 0;
+	bool supported = false;
+
+	if (!gatt)
+		return btd_error_failed(msg, "Not connected");
+
+	if (chrc->write_id)
+		return btd_error_in_progress(msg);
+
+	if (!parse_value_arg(msg, &value, &value_len))
+		return btd_error_invalid_args(msg);
+
+	/*
+	 * Decide which write to use based on characteristic properties. For now
+	 * we don't perform signed writes since gatt-client doesn't support them
+	 * and the user can always encrypt the through pairing. The procedure to
+	 * use is determined based on the following priority:
+	 *
+	 *   * "reliable-write" property set -> reliable long-write.
+	 *   * "write" property set -> write request.
+	 *     - If value is larger than MTU - 3: long-write
+	 *   * "write-without-response" property set -> write command.
+	 */
+	if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
+		supported = true;
+		chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
+						true, value, value_len,
+						chrc, chrc_write_complete);
+		if (chrc->write_id)
+			return NULL;
+	}
+
+	if (chrc->props & BT_GATT_CHRC_PROP_WRITE) {
+		uint16_t mtu;
+
+		supported = true;
+		mtu = bt_gatt_client_get_mtu(gatt);
+		if (!mtu)
+			return btd_error_failed(msg, "No ATT transport");
+
+		if (value_len <= (unsigned) mtu - 3)
+			chrc->write_id = start_write_request(msg,
+						chrc->value_handle,
+						gatt, value, value_len,
+						chrc, chrc_write_complete);
+		else
+			chrc->write_id = start_long_write(msg,
+						chrc->value_handle, gatt,
+						false, value, value_len,
+						chrc, chrc_write_complete);
+
+		if (chrc->write_id)
+			return NULL;
+	}
+
+	if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+		goto fail;
+
+	supported = true;
+
+	if (bt_gatt_client_write_without_response(gatt,
+					chrc->value_handle,
+					chrc->props & BT_GATT_CHRC_PROP_AUTH,
+					value, value_len))
+		return dbus_message_new_method_return(msg);
+
+fail:
+	if (supported)
+		return btd_error_failed(msg, "Failed to initiate write");
+
+	return btd_error_not_supported(msg);
+}
+
+struct notify_client {
+	struct characteristic *chrc;
+	int ref_count;
+	char *owner;
+	guint watch;
+	unsigned int notify_id;
+};
+
+static void notify_client_free(struct notify_client *client)
+{
+	DBG("owner %s", client->owner);
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), client->watch);
+	bt_gatt_client_unregister_notify(client->chrc->service->client->gatt,
+							client->notify_id);
+	free(client->owner);
+	free(client);
+}
+
+static void notify_client_unref(void *data)
+{
+	struct notify_client *client = data;
+
+	DBG("owner %s", client->owner);
+
+	if (__sync_sub_and_fetch(&client->ref_count, 1))
+		return;
+
+	notify_client_free(client);
+}
+
+static struct notify_client *notify_client_ref(struct notify_client *client)
+{
+	DBG("owner %s", client->owner);
+
+	__sync_fetch_and_add(&client->ref_count, 1);
+
+	return client;
+}
+
+static bool match_notifying(const void *a, const void *b)
+{
+	const struct notify_client *client = a;
+
+	return !!client->notify_id;
+}
+
+static void update_notifying(struct characteristic *chrc)
+{
+	if (!chrc->notifying)
+		return;
+
+	if (queue_find(chrc->notify_clients, match_notifying, NULL))
+		return;
+
+	chrc->notifying = false;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						"Notifying");
+}
+
+static void notify_client_disconnect(DBusConnection *conn, void *user_data)
+{
+	struct notify_client *client = user_data;
+	struct characteristic *chrc = client->chrc;
+
+	DBG("owner %s", client->owner);
+
+	queue_remove(chrc->notify_clients, client);
+	queue_remove(chrc->service->client->all_notify_clients, client);
+
+	update_notifying(chrc);
+
+	notify_client_unref(client);
+}
+
+static struct notify_client *notify_client_create(struct characteristic *chrc,
+							const char *owner)
+{
+	struct notify_client *client;
+
+	client = new0(struct notify_client, 1);
+	client->chrc = chrc;
+	client->owner = strdup(owner);
+	if (!client->owner) {
+		free(client);
+		return NULL;
+	}
+
+	client->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+						owner, notify_client_disconnect,
+						client, NULL);
+	if (!client->watch) {
+		free(client->owner);
+		free(client);
+		return NULL;
+	}
+
+	return notify_client_ref(client);
+}
+
+static bool match_notify_sender(const void *a, const void *b)
+{
+	const struct notify_client *client = a;
+	const char *sender = b;
+
+	return strcmp(client->owner, sender) == 0;
+}
+
+static void notify_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	struct notify_client *client = op->data;
+	struct characteristic *chrc = client->chrc;
+
+	/*
+	 * Even if the value didn't change, we want to send a PropertiesChanged
+	 * signal so that we propagate the notification/indication to
+	 * applications.
+	 */
+	gatt_db_attribute_reset(chrc->attr);
+	gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
+						write_characteristic_cb, chrc);
+}
+
+static DBusMessage *create_notify_reply(struct async_dbus_op *op,
+						bool success, uint8_t att_ecode)
+{
+	DBusMessage *reply = NULL;
+
+	if (!op->msg)
+		return NULL;
+
+	if (success)
+		reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
+	else if (att_ecode)
+		reply = create_gatt_dbus_error(op->msg, att_ecode);
+	else
+		reply = btd_error_failed(op->msg,
+						"Characteristic not available");
+
+	if (!reply)
+		error("Failed to construct D-Bus message reply");
+
+	return reply;
+}
+
+static void register_notify_cb(uint16_t att_ecode, void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	struct notify_client *client = op->data;
+	struct characteristic *chrc = client->chrc;
+	DBusMessage *reply;
+
+	if (att_ecode) {
+		queue_remove(chrc->notify_clients, client);
+		queue_remove(chrc->service->client->all_notify_clients, client);
+		notify_client_free(client);
+
+		reply = create_notify_reply(op, false, att_ecode);
+
+		goto done;
+	}
+
+	if (!chrc->notifying) {
+		chrc->notifying = true;
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					chrc->path, GATT_CHARACTERISTIC_IFACE,
+					"Notifying");
+	}
+
+	reply = create_notify_reply(op, true, 0);
+
+done:
+	if (reply)
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+static DBusMessage *characteristic_start_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 async_dbus_op *op;
+	struct notify_client *client;
+
+	if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+				chrc->props & BT_GATT_CHRC_PROP_INDICATE))
+		return btd_error_not_supported(msg);
+
+	/* Each client can only have one active notify session. */
+	client = queue_find(chrc->notify_clients, match_notify_sender, sender);
+	if (client)
+		return client->notify_id ?
+				btd_error_failed(msg, "Already notifying") :
+				btd_error_in_progress(msg);
+
+	client = notify_client_create(chrc, sender);
+	if (!client)
+		return btd_error_failed(msg, "Failed allocate notify session");
+
+	queue_push_tail(chrc->notify_clients, client);
+	queue_push_tail(chrc->service->client->all_notify_clients, client);
+
+	/*
+	 * If the device is currently not connected, return success. We will
+	 * automatically try and register all clients when a GATT client becomes
+	 * ready.
+	 */
+	if (!gatt) {
+		DBusMessage *reply;
+
+		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+		if (reply)
+			return reply;
+
+		/*
+		 * Clean up and respond with an error instead of timing out to
+		 * avoid any ambiguities.
+		 */
+		error("Failed to construct D-Bus message reply");
+		goto fail;
+	}
+
+	op = new0(struct async_dbus_op, 1);
+	op->data = client;
+	op->msg = dbus_message_ref(msg);
+
+	client->notify_id = bt_gatt_client_register_notify(gatt,
+						chrc->value_handle,
+						register_notify_cb, notify_cb,
+						op, async_dbus_op_free);
+	if (client->notify_id)
+		return NULL;
+
+	async_dbus_op_free(op);
+
+fail:
+	queue_remove(chrc->notify_clients, client);
+	queue_remove(chrc->service->client->all_notify_clients, client);
+
+	/* Directly free the client */
+	notify_client_free(client);
+
+	return btd_error_failed(msg, "Failed to register notify session");
+}
+
+static DBusMessage *characteristic_stop_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 (!chrc->notifying)
+		return btd_error_failed(msg, "Not notifying");
+
+	client = queue_remove_if(chrc->notify_clients, match_notify_sender,
+							(void *) sender);
+	if (!client)
+		return btd_error_failed(msg, "No notify session started");
+
+	queue_remove(chrc->service->client->all_notify_clients, client);
+	bt_gatt_client_unregister_notify(gatt, client->notify_id);
+	update_notifying(chrc);
+
+	notify_client_unref(client);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static void append_desc_path(void *data, void *user_data)
+{
+	struct descriptor *desc = data;
+	DBusMessageIter *array = user_data;
+
+	dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+								&desc->path);
+}
+
+static gboolean characteristic_get_descriptors(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+	queue_foreach(chrc->descs, append_desc_path, &array);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable characteristic_properties[] = {
+	{ "UUID", "s", characteristic_get_uuid, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Service", "o", characteristic_get_service, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Value", "ay", characteristic_get_value, NULL,
+					characteristic_value_exists,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Notifying", "b", characteristic_get_notifying, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Flags", "as", characteristic_get_flags, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Descriptors", "ao", characteristic_get_descriptors, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+static const GDBusMethodTable characteristic_methods[] = {
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
+						GDBUS_ARGS({ "value", "ay" }),
+						characteristic_read_value) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
+						GDBUS_ARGS({ "value", "ay" }),
+						NULL,
+						characteristic_write_value) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
+						characteristic_start_notify) },
+	{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
+						characteristic_stop_notify) },
+	{ }
+};
+
+static void characteristic_free(void *data)
+{
+	struct characteristic *chrc = data;
+
+	/* List should be empty here */
+	queue_destroy(chrc->descs, NULL);
+	queue_destroy(chrc->notify_clients, NULL);
+
+	g_free(chrc->path);
+	free(chrc);
+}
+
+static struct characteristic *characteristic_create(
+						struct gatt_db_attribute *attr,
+						struct service *service)
+{
+	struct characteristic *chrc;
+	bt_uuid_t uuid;
+
+	chrc = new0(struct characteristic, 1);
+	chrc->descs = queue_new();
+	chrc->notify_clients = queue_new();
+	chrc->service = service;
+
+	gatt_db_attribute_get_char_data(attr, &chrc->handle,
+							&chrc->value_handle,
+							&chrc->props, &uuid);
+
+	chrc->attr = gatt_db_get_attribute(service->client->db,
+							chrc->value_handle);
+	if (!chrc->attr) {
+		error("Attribute 0x%04x not found", chrc->value_handle);
+		characteristic_free(chrc);
+		return NULL;
+	}
+
+	bt_uuid_to_uuid128(&uuid, &chrc->uuid);
+
+	chrc->path = g_strdup_printf("%s/char%04x", service->path,
+								chrc->handle);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						characteristic_methods, NULL,
+						characteristic_properties,
+						chrc, characteristic_free)) {
+		error("Unable to register GATT characteristic with handle "
+							"0x%04x", chrc->handle);
+		characteristic_free(chrc);
+
+		return NULL;
+	}
+
+	DBG("Exported GATT characteristic: %s", chrc->path);
+
+	return chrc;
+}
+
+static void remove_client(void *data)
+{
+	struct notify_client *ntfy_client = data;
+	struct btd_gatt_client *client = ntfy_client->chrc->service->client;
+
+	queue_remove(client->all_notify_clients, ntfy_client);
+
+	notify_client_unref(ntfy_client);
+}
+
+static void unregister_characteristic(void *data)
+{
+	struct characteristic *chrc = data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+	DBG("Removing GATT characteristic: %s", chrc->path);
+
+	if (chrc->read_id)
+		bt_gatt_client_cancel(gatt, chrc->read_id);
+
+	if (chrc->write_id)
+		bt_gatt_client_cancel(gatt, chrc->write_id);
+
+	queue_remove_all(chrc->notify_clients, NULL, NULL, remove_client);
+	queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), chrc->path,
+						GATT_CHARACTERISTIC_IFACE);
+}
+
+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	char uuid[MAX_LEN_UUID_STR + 1];
+	const char *ptr = uuid;
+	struct service *service = data;
+
+	bt_uuid_to_string(&service->uuid, uuid, sizeof(uuid));
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+	return TRUE;
+}
+
+static gboolean service_get_device(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct service *service = data;
+	const char *str = device_get_path(service->client->device);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
+
+	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 void append_chrc_path(void *data, void *user_data)
+{
+	struct characteristic *chrc = data;
+	DBusMessageIter *array = user_data;
+
+	dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+								&chrc->path);
+}
+
+static gboolean service_get_characteristics(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct service *service = data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+	if (service->chrcs_ready)
+		queue_foreach(service->chrcs, append_chrc_path, &array);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+	{ "UUID", "s", service_get_uuid, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Device", "o", service_get_device, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Primary", "b", service_get_primary, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ "Characteristics", "ao", service_get_characteristics, NULL, NULL,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+	{ }
+};
+
+static void service_free(void *data)
+{
+	struct service *service = data;
+
+	queue_destroy(service->chrcs, NULL);  /* List should be empty here */
+	queue_destroy(service->pending_ext_props, NULL);
+	g_free(service->path);
+	free(service);
+}
+
+static struct service *service_create(struct gatt_db_attribute *attr,
+						struct btd_gatt_client *client)
+{
+	struct service *service;
+	const char *device_path = device_get_path(client->device);
+	bt_uuid_t uuid;
+
+	service = new0(struct service, 1);
+	service->chrcs = queue_new();
+	service->pending_ext_props = queue_new();
+	service->client = client;
+
+	gatt_db_attribute_get_service_data(attr, &service->start_handle,
+							&service->end_handle,
+							&service->primary,
+							&uuid);
+	bt_uuid_to_uuid128(&uuid, &service->uuid);
+
+	service->path = g_strdup_printf("%s/service%04x", device_path,
+							service->start_handle);
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(), service->path,
+						GATT_SERVICE_IFACE,
+						NULL, NULL,
+						service_properties,
+						service, service_free)) {
+		error("Unable to register GATT service with handle 0x%04x for "
+							"device %s",
+							service->start_handle,
+							client->devaddr);
+		service_free(service);
+
+		return NULL;
+	}
+
+	DBG("Exported GATT service: %s", service->path);
+
+	/* Set service active so we can skip discovering next time */
+	gatt_db_service_set_active(attr, true);
+
+	/* Mark the service as claimed since it going to be exported */
+	gatt_db_service_set_claimed(attr, true);
+
+	return service;
+}
+
+static void unregister_service(void *data)
+{
+	struct service *service = data;
+
+	DBG("Removing GATT service: %s", service->path);
+
+	if (service->idle_id)
+		g_source_remove(service->idle_id);
+
+	queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
+							GATT_SERVICE_IFACE);
+}
+
+static void notify_chrcs(struct service *service)
+{
+
+	if (service->chrcs_ready ||
+				!queue_isempty(service->pending_ext_props))
+		return;
+
+	service->chrcs_ready = true;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
+							GATT_SERVICE_IFACE,
+							"Characteristics");
+}
+
+struct export_data {
+	void *root;
+	bool failed;
+};
+
+static void export_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct descriptor *desc;
+	struct export_data *data = user_data;
+	struct characteristic *charac = data->root;
+
+	if (data->failed)
+		return;
+
+	desc = descriptor_create(attr, charac);
+	if (!desc) {
+		data->failed = true;
+		return;
+	}
+
+	queue_push_tail(charac->descs, desc);
+}
+
+static void read_ext_props_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct characteristic *chrc = user_data;
+	struct service *service = chrc->service;
+
+	if (!success) {
+		error("Failed to obtain extended properties - error: 0x%02x",
+								att_ecode);
+		return;
+	}
+
+	if (!value || length != 2) {
+		error("Malformed extended properties value");
+		return;
+	}
+
+	chrc->ext_props = get_le16(value);
+	if (chrc->ext_props)
+		g_dbus_emit_property_changed(btd_get_dbus_connection(),
+						chrc->path,
+						GATT_CHARACTERISTIC_IFACE,
+						"Flags");
+
+	queue_remove(service->pending_ext_props, chrc);
+
+	notify_chrcs(service);
+}
+
+static void read_ext_props(void *data, void *user_data)
+{
+	struct characteristic *chrc = data;
+
+	bt_gatt_client_read_value(chrc->service->client->gatt,
+							chrc->ext_props_handle,
+							read_ext_props_cb,
+							chrc, NULL);
+}
+
+static bool create_descriptors(struct gatt_db_attribute *attr,
+					struct characteristic *charac)
+{
+	struct export_data data;
+
+	data.root = charac;
+	data.failed = false;
+
+	gatt_db_service_foreach_desc(attr, export_desc, &data);
+
+	return !data.failed;
+}
+
+static void export_char(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct characteristic *charac;
+	struct export_data *data = user_data;
+	struct service *service = data->root;
+
+	if (data->failed)
+		return;
+
+	charac = characteristic_create(attr, service);
+	if (!charac)
+		goto fail;
+
+	if (!create_descriptors(attr, charac)) {
+		unregister_characteristic(charac);
+		goto fail;
+	}
+
+	queue_push_tail(service->chrcs, charac);
+
+	if (charac->ext_props_handle)
+		queue_push_tail(service->pending_ext_props, charac);
+
+	return;
+
+fail:
+	data->failed = true;
+}
+
+static bool create_characteristics(struct gatt_db_attribute *attr,
+						struct service *service)
+{
+	struct export_data data;
+
+	data.root = service;
+	data.failed = false;
+
+	gatt_db_service_foreach_char(attr, export_char, &data);
+
+	if (data.failed)
+		return false;
+
+	/* Obtain extended properties */
+	queue_foreach(service->pending_ext_props, read_ext_props, NULL);
+
+	return true;
+}
+
+static gboolean set_chrcs_ready(gpointer user_data)
+{
+	struct service *service = user_data;
+
+	service->idle_id = 0;
+	notify_chrcs(service);
+
+	return FALSE;
+}
+
+static void export_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct btd_gatt_client *client = user_data;
+	struct service *service;
+
+	if (gatt_db_service_get_claimed(attr))
+		return;
+
+	service = service_create(attr, client);
+	if (!service)
+		return;
+
+	if (!create_characteristics(attr, service)) {
+		error("Exporting characteristics failed");
+		unregister_service(service);
+		return;
+	}
+
+	queue_push_tail(client->services, service);
+
+	/*
+	 * Asynchronously update the "Characteristics" property of the service.
+	 * If there are any pending reads to obtain the value of the "Extended
+	 * Properties" descriptor then wait until they are complete.
+	 */
+	if (!service->chrcs_ready && queue_isempty(service->pending_ext_props))
+		service->idle_id = g_idle_add(set_chrcs_ready, service);
+}
+
+static void create_services(struct btd_gatt_client *client)
+{
+	DBG("Exporting objects for GATT services: %s", client->devaddr);
+
+	gatt_db_foreach_service(client->db, NULL, export_service, client);
+}
+
+struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device)
+{
+	struct btd_gatt_client *client;
+	struct gatt_db *db;
+
+	if (!device)
+		return NULL;
+
+	db = btd_device_get_gatt_db(device);
+	if (!db)
+		return NULL;
+
+	client = new0(struct btd_gatt_client, 1);
+	client->services = queue_new();
+	client->all_notify_clients = queue_new();
+	client->device = device;
+	ba2str(device_get_address(device), client->devaddr);
+
+	client->db = gatt_db_ref(db);
+
+	return client;
+}
+
+void btd_gatt_client_destroy(struct btd_gatt_client *client)
+{
+	if (!client)
+		return;
+
+	queue_destroy(client->services, unregister_service);
+	queue_destroy(client->all_notify_clients, NULL);
+	bt_gatt_client_unref(client->gatt);
+	gatt_db_unref(client->db);
+	free(client);
+}
+
+static void register_notify(void *data, void *user_data)
+{
+	struct notify_client *notify_client = data;
+	struct btd_gatt_client *client = user_data;
+	struct async_dbus_op *op;
+
+	DBG("Re-register subscribed notification client");
+
+	op = new0(struct async_dbus_op, 1);
+	op->data = notify_client;
+
+	notify_client->notify_id = bt_gatt_client_register_notify(client->gatt,
+					notify_client->chrc->value_handle,
+					register_notify_cb, notify_cb,
+					op, async_dbus_op_free);
+	if (notify_client->notify_id)
+		return;
+
+	async_dbus_op_free(op);
+
+	DBG("Failed to re-register notification client");
+
+	queue_remove(notify_client->chrc->notify_clients, client);
+	queue_remove(client->all_notify_clients, client);
+
+	notify_client_free(notify_client);
+}
+
+void btd_gatt_client_ready(struct btd_gatt_client *client)
+{
+	if (!client)
+		return;
+
+	if (!client->gatt) {
+		error("GATT client not initialized");
+		return;
+	}
+
+	client->ready = true;
+
+	DBG("GATT client ready");
+
+	create_services(client);
+}
+
+void btd_gatt_client_connected(struct btd_gatt_client *client)
+{
+	struct bt_gatt_client *gatt;
+
+	gatt = btd_device_get_gatt_client(client->device);
+	if (!gatt) {
+		error("GATT client not initialized");
+		return;
+	}
+
+	DBG("Device connected.");
+
+	bt_gatt_client_unref(client->gatt);
+	client->gatt = bt_gatt_client_ref(gatt);
+
+	/*
+	 * Services have already been created before. Re-enable notifications
+	 * for any pre-registered notification sessions.
+	 */
+	queue_foreach(client->all_notify_clients, register_notify, client);
+}
+
+void btd_gatt_client_service_added(struct btd_gatt_client *client,
+					struct gatt_db_attribute *attrib)
+{
+	if (!client || !attrib || !client->ready)
+		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;
+}
+
+void btd_gatt_client_service_removed(struct btd_gatt_client *client,
+					struct gatt_db_attribute *attrib)
+{
+	uint16_t start_handle, end_handle;
+
+	if (!client || !attrib || !client->ready)
+		return;
+
+	gatt_db_attribute_get_service_handles(attrib, &start_handle,
+								&end_handle);
+
+	DBG("GATT Services Removed - start: 0x%04x, end: 0x%04x", start_handle,
+								end_handle);
+	queue_remove_all(client->services, match_service_handle,
+						UINT_TO_PTR(start_handle),
+						unregister_service);
+}
+
+static void clear_notify_id(void *data, void *user_data)
+{
+	struct notify_client *client = data;
+
+	client->notify_id = 0;
+}
+
+static void cancel_desc_ops(void *data, void *user_data)
+{
+	struct descriptor *desc = data;
+	struct bt_gatt_client *gatt = user_data;
+
+	if (desc->read_id) {
+		bt_gatt_client_cancel(gatt, desc->read_id);
+		desc->read_id = 0;
+	}
+
+	if (desc->write_id) {
+		bt_gatt_client_cancel(gatt, desc->write_id);
+		desc->write_id = 0;
+	}
+}
+
+static void cancel_chrc_ops(void *data, void *user_data)
+{
+	struct characteristic *chrc = data;
+	struct bt_gatt_client *gatt = user_data;
+
+	if (chrc->read_id) {
+		bt_gatt_client_cancel(gatt, chrc->read_id);
+		chrc->read_id = 0;
+	}
+
+	if (chrc->write_id) {
+		bt_gatt_client_cancel(gatt, chrc->write_id);
+		chrc->write_id = 0;
+	}
+
+	queue_foreach(chrc->descs, cancel_desc_ops, user_data);
+}
+
+static void cancel_ops(void *data, void *user_data)
+{
+	struct service *service = data;
+
+	queue_foreach(service->chrcs, cancel_chrc_ops, user_data);
+}
+
+void btd_gatt_client_disconnected(struct btd_gatt_client *client)
+{
+	if (!client || !client->gatt)
+		return;
+
+	DBG("Device disconnected. Cleaning up.");
+
+	/*
+	 * TODO: Once GATT over BR/EDR is properly supported, we should pass the
+	 * correct bdaddr_type based on the transport over which GATT is being
+	 * done.
+	 */
+	queue_foreach(client->all_notify_clients, clear_notify_id, NULL);
+	queue_foreach(client->services, cancel_ops, client->gatt);
+
+	bt_gatt_client_unref(client->gatt);
+	client->gatt = NULL;
+}
+
+struct foreach_service_data {
+	btd_gatt_client_service_path_t func;
+	void *user_data;
+};
+
+static void client_service_foreach(void *data, void *user_data)
+{
+	struct service *service = data;
+	struct foreach_service_data *foreach_data = user_data;
+
+	foreach_data->func(service->path, foreach_data->user_data);
+}
+
+void btd_gatt_client_foreach_service(struct btd_gatt_client *client,
+					btd_gatt_client_service_path_t func,
+					void *user_data)
+{
+	struct foreach_service_data data;
+
+	if (!client)
+		return;
+
+	data.func = func;
+	data.user_data = user_data;
+
+	queue_foreach(client->services, client_service_foreach, &data);
+}
diff --git a/bluez/repo/src/gatt-client.h b/bluez/repo/src/gatt-client.h
new file mode 100644
index 0000000..92a9255
--- /dev/null
+++ b/bluez/repo/src/gatt-client.h
@@ -0,0 +1,37 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google 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.
+ *
+ */
+
+struct btd_gatt_client;
+
+struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device);
+void btd_gatt_client_destroy(struct btd_gatt_client *client);
+
+void btd_gatt_client_ready(struct btd_gatt_client *client);
+void btd_gatt_client_connected(struct btd_gatt_client *client);
+void btd_gatt_client_service_added(struct btd_gatt_client *client,
+					struct gatt_db_attribute *attrib);
+void btd_gatt_client_service_removed(struct btd_gatt_client *client,
+					struct gatt_db_attribute *attrib);
+void btd_gatt_client_disconnected(struct btd_gatt_client *client);
+
+typedef void (*btd_gatt_client_service_path_t)(const char *service_path,
+							void *user_data);
+void btd_gatt_client_foreach_service(struct btd_gatt_client *client,
+					btd_gatt_client_service_path_t func,
+					void *user_data);
diff --git a/bluez/repo/src/gatt-database.c b/bluez/repo/src/gatt-database.c
new file mode 100644
index 0000000..e8ce7d5
--- /dev/null
+++ b/bluez/repo/src/gatt-database.c
@@ -0,0 +1,2492 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Google 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 <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "btio/btio.h"
+#include "gdbus/gdbus.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "log.h"
+#include "error.h"
+#include "adapter.h"
+#include "device.h"
+#include "gatt-database.h"
+#include "dbus-common.h"
+#include "profile.h"
+#include "service.h"
+
+#ifndef ATT_CID
+#define ATT_CID 4
+#endif
+
+#ifndef ATT_PSM
+#define ATT_PSM 31
+#endif
+
+#define GATT_MANAGER_IFACE	"org.bluez.GattManager1"
+#define GATT_SERVICE_IFACE	"org.bluez.GattService1"
+#define GATT_CHRC_IFACE		"org.bluez.GattCharacteristic1"
+#define GATT_DESC_IFACE		"org.bluez.GattDescriptor1"
+
+#define UUID_GAP	0x1800
+#define UUID_GATT	0x1801
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+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 *device_states;
+	struct queue *ccc_callbacks;
+	struct gatt_db_attribute *svc_chngd;
+	struct gatt_db_attribute *svc_chngd_ccc;
+	struct queue *services;
+	struct queue *profiles;
+};
+
+struct external_service {
+	struct btd_gatt_database *database;
+	bool failed;
+	char *owner;
+	char *path;	/* Path to GattService1 */
+	DBusMessage *reg;
+	GDBusClient *client;
+	GDBusProxy *proxy;
+	struct gatt_db_attribute *attrib;
+	uint16_t attr_cnt;
+	struct queue *chrcs;
+	struct queue *descs;
+};
+
+struct external_profile {
+	struct btd_gatt_database *database;
+	char *owner;
+	char *path;	/* Path to GattProfile1 */
+	unsigned int id;
+	struct queue *profiles; /* btd_profile list */
+};
+
+struct external_chrc {
+	struct external_service *service;
+	char *path;
+	GDBusProxy *proxy;
+	uint8_t props;
+	uint8_t ext_props;
+	struct gatt_db_attribute *attrib;
+	struct gatt_db_attribute *ccc;
+	struct queue *pending_reads;
+	struct queue *pending_writes;
+	unsigned int ntfy_cnt;
+};
+
+struct external_desc {
+	struct external_service *service;
+	char *chrc_path;
+	GDBusProxy *proxy;
+	uint32_t perm;
+	struct gatt_db_attribute *attrib;
+	bool handled;
+	struct queue *pending_reads;
+	struct queue *pending_writes;
+};
+
+struct pending_op {
+	unsigned int id;
+	struct gatt_db_attribute *attrib;
+	struct queue *owner_queue;
+	struct iovec data;
+};
+
+struct device_state {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+	struct queue *ccc_states;
+};
+
+struct ccc_state {
+	uint16_t handle;
+	uint8_t value[2];
+};
+
+struct ccc_cb_data {
+	uint16_t handle;
+	btd_gatt_database_ccc_write_t callback;
+	btd_gatt_database_destroy_t destroy;
+	void *user_data;
+};
+
+struct device_info {
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+};
+
+static void ccc_cb_free(void *data)
+{
+	struct ccc_cb_data *ccc_cb = data;
+
+	if (ccc_cb->destroy)
+		ccc_cb->destroy(ccc_cb->user_data);
+
+	free(ccc_cb);
+}
+
+static bool ccc_cb_match_service(const void *data, const void *match_data)
+{
+	const struct ccc_cb_data *ccc_cb = data;
+	const struct gatt_db_attribute *attrib = match_data;
+	uint16_t start, end;
+
+	if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
+		return false;
+
+	return ccc_cb->handle >= start && ccc_cb->handle <= end;
+}
+
+static bool ccc_cb_match_handle(const void *data, const void *match_data)
+{
+	const struct ccc_cb_data *ccc_cb = data;
+	uint16_t handle = PTR_TO_UINT(match_data);
+
+	return ccc_cb->handle == handle;
+}
+
+static bool dev_state_match(const void *a, const void *b)
+{
+	const struct device_state *dev_state = a;
+	const struct device_info *dev_info = b;
+
+	return bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0 &&
+				dev_state->bdaddr_type == dev_info->bdaddr_type;
+}
+
+static struct device_state *
+find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
+							uint8_t bdaddr_type)
+{
+	struct device_info dev_info;
+
+	memset(&dev_info, 0, sizeof(dev_info));
+
+	bacpy(&dev_info.bdaddr, bdaddr);
+	dev_info.bdaddr_type = bdaddr_type;
+
+	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(bdaddr_t *bdaddr,
+							uint8_t bdaddr_type)
+{
+	struct device_state *dev_state;
+
+	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 device_state *dev_state;
+
+	/*
+	 * 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);
+	if (dev_state)
+		return dev_state;
+
+	dev_state = device_state_create(bdaddr, bdaddr_type);
+
+	queue_push_tail(database->device_states, dev_state);
+
+	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 device_state *dev_state;
+	struct ccc_state *ccc;
+
+	dev_state = get_device_state(database, bdaddr, bdaddr_type);
+
+	ccc = find_ccc_state(dev_state, handle);
+	if (ccc)
+		return ccc;
+
+	ccc = new0(struct ccc_state, 1);
+	ccc->handle = handle;
+	queue_push_tail(dev_state->ccc_states, ccc);
+
+	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;
+
+	gatt_db_attribute_read_result(op->attrib, op->id,
+					BT_ATT_ERROR_REQUEST_NOT_SUPPORTED,
+					NULL, 0);
+	op->owner_queue = NULL;
+}
+
+static void cancel_pending_write(void *data)
+{
+	struct pending_op *op = data;
+
+	gatt_db_attribute_write_result(op->attrib, op->id,
+					BT_ATT_ERROR_REQUEST_NOT_SUPPORTED);
+	op->owner_queue = NULL;
+}
+
+static void chrc_free(void *data)
+{
+	struct external_chrc *chrc = data;
+
+	queue_destroy(chrc->pending_reads, cancel_pending_read);
+	queue_destroy(chrc->pending_writes, cancel_pending_write);
+
+	g_free(chrc->path);
+
+	g_dbus_proxy_set_property_watch(chrc->proxy, NULL, NULL);
+	g_dbus_proxy_unref(chrc->proxy);
+
+	free(chrc);
+}
+
+static void desc_free(void *data)
+{
+	struct external_desc *desc = data;
+
+	queue_destroy(desc->pending_reads, cancel_pending_read);
+	queue_destroy(desc->pending_writes, cancel_pending_write);
+
+	g_dbus_proxy_unref(desc->proxy);
+	g_free(desc->chrc_path);
+
+	free(desc);
+}
+
+static void service_free(void *data)
+{
+	struct external_service *service = data;
+
+	queue_destroy(service->chrcs, chrc_free);
+	queue_destroy(service->descs, desc_free);
+
+	gatt_db_remove_service(service->database->db, service->attrib);
+
+	if (service->client) {
+		g_dbus_client_set_disconnect_watch(service->client, NULL, NULL);
+		g_dbus_client_set_proxy_handlers(service->client, NULL, NULL,
+								NULL, NULL);
+		g_dbus_client_set_ready_watch(service->client, NULL, NULL);
+		g_dbus_proxy_unref(service->proxy);
+		g_dbus_client_unref(service->client);
+	}
+
+	if (service->reg)
+		dbus_message_unref(service->reg);
+
+	g_free(service->owner);
+	g_free(service->path);
+
+	free(service);
+}
+
+static void profile_remove(void *data)
+{
+	struct btd_profile *p = data;
+
+	DBG("Removed \"%s\"", p->name);
+
+	adapter_foreach(adapter_remove_profile, p);
+	btd_profile_unregister(p);
+
+	g_free((void *) p->name);
+	g_free((void *) p->remote_uuid);
+
+	free(p);
+}
+
+static void profile_release(struct external_profile *profile)
+{
+	DBusMessage *msg;
+
+	if (!profile->id)
+		return;
+
+	DBG("Releasing \"%s\"", profile->owner);
+
+	g_dbus_remove_watch(btd_get_dbus_connection(), profile->id);
+
+	msg = dbus_message_new_method_call(profile->owner, profile->path,
+						"org.bluez.GattProfile1",
+						"Release");
+	if (msg)
+		g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void profile_free(void *data)
+{
+	struct external_profile *profile = data;
+
+	queue_destroy(profile->profiles, profile_remove);
+
+	profile_release(profile);
+
+	g_free(profile->owner);
+	g_free(profile->path);
+
+	free(profile);
+}
+
+static void gatt_database_free(void *data)
+{
+	struct btd_gatt_database *database = data;
+
+	if (database->le_io) {
+		g_io_channel_shutdown(database->le_io, FALSE, NULL);
+		g_io_channel_unref(database->le_io);
+	}
+
+	if (database->l2cap_io) {
+		g_io_channel_shutdown(database->l2cap_io, FALSE, NULL);
+		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->device_states, device_state_free);
+	queue_destroy(database->services, service_free);
+	queue_destroy(database->profiles, profile_free);
+	queue_destroy(database->ccc_callbacks, ccc_cb_free);
+	database->device_states = NULL;
+	database->ccc_callbacks = NULL;
+
+	gatt_db_unref(database->db);
+
+	btd_adapter_unref(database->adapter);
+	free(database);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+	uint8_t dst_type;
+	bdaddr_t src, dst;
+
+	DBG("New incoming LE ATT connection");
+
+	if (gerr) {
+		error("%s", gerr->message);
+		return;
+	}
+
+	bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+						BT_IO_OPT_DEST_BDADDR, &dst,
+						BT_IO_OPT_DEST_TYPE, &dst_type,
+						BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("bt_io_get: %s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	adapter = adapter_find(&src);
+	if (!adapter)
+		return;
+
+	device = btd_adapter_get_device(adapter, &dst, dst_type);
+	if (!device)
+		return;
+
+	device_attach_att(device, io);
+}
+
+static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	uint8_t error = 0;
+	size_t len = 0;
+	const uint8_t *value = NULL;
+	const char *device_name;
+
+	DBG("GAP Device Name read request\n");
+
+	device_name = btd_adapter_get_name(database->adapter);
+	len = strlen(device_name);
+
+	if (offset > len) {
+		error = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	len -= offset;
+	value = len ? (const uint8_t *) &device_name[offset] : NULL;
+
+done:
+	gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void gap_appearance_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	uint8_t error = 0;
+	size_t len = 2;
+	const uint8_t *value = NULL;
+	uint8_t appearance[2];
+	uint32_t dev_class;
+
+	DBG("GAP Appearance read request\n");
+
+	dev_class = btd_adapter_get_class(database->adapter);
+
+	if (offset > 2) {
+		error = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	appearance[0] = dev_class & 0x00ff;
+	appearance[1] = (dev_class >> 8) & 0x001f;
+
+	len -= offset;
+	value = len ? &appearance[offset] : NULL;
+
+done:
+	gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+	uuid_t root_uuid, proto_uuid, l2cap;
+	sdp_record_t *record;
+	sdp_data_t *psm, *sh, *eh;
+	uint16_t lp = ATT_PSM;
+
+	if (uuid == NULL)
+		return NULL;
+
+	if (start > end)
+		return NULL;
+
+	record = sdp_record_alloc();
+	if (record == NULL)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	svclass_id = sdp_list_append(NULL, uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&proto_uuid, ATT_UUID);
+	proto[1] = sdp_list_append(NULL, &proto_uuid);
+	sh = sdp_data_alloc(SDP_UINT16, &start);
+	proto[1] = sdp_list_append(proto[1], sh);
+	eh = sdp_data_alloc(SDP_UINT16, &end);
+	proto[1] = sdp_list_append(proto[1], eh);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_data_free(psm);
+	sdp_data_free(sh);
+	sdp_data_free(eh);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return record;
+}
+
+static uint32_t database_add_record(struct btd_gatt_database *database,
+					uint16_t uuid,
+					struct gatt_db_attribute *attr,
+					const char *name)
+{
+	sdp_record_t *record;
+	uint16_t start, end;
+	uuid_t svc, gap_uuid;
+
+	sdp_uuid16_create(&svc, uuid);
+	gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+	record = record_new(&svc, start, end);
+	if (!record)
+		return 0;
+
+	if (name != NULL)
+		sdp_set_info_attr(record, name, "BlueZ", NULL);
+
+	sdp_uuid16_create(&gap_uuid, UUID_GAP);
+	if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
+		sdp_set_url_attr(record, "http://www.bluez.org/",
+				"http://www.bluez.org/",
+				"http://www.bluez.org/");
+	}
+
+	if (adapter_service_add(database->adapter, record) == 0)
+		return record->handle;
+
+	sdp_record_free(record);
+	return 0;
+}
+
+static void populate_gap_service(struct btd_gatt_database *database)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service;
+
+	/* 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.
+	 */
+	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+	gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+							BT_GATT_CHRC_PROP_READ,
+							gap_device_name_read_cb,
+							NULL, database);
+
+	/*
+	 * Device Appearance characteristic.
+	 */
+	bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+	gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+							BT_GATT_CHRC_PROP_READ,
+							gap_appearance_read_cb,
+							NULL, database);
+
+	gatt_db_service_set_active(service, true);
+}
+
+static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+	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;
+}
+
+static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	struct ccc_state *ccc;
+	uint16_t handle;
+	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);
+
+	DBG("CCC read called for handle: 0x%04x", handle);
+
+	if (offset > 2) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+		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;
+
+done:
+	gatt_db_attribute_read_result(attrib, id, ecode, value, len);
+}
+
+static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	struct ccc_state *ccc;
+	struct ccc_cb_data *ccc_cb;
+	uint16_t handle;
+	uint8_t ecode = 0;
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+
+	handle = gatt_db_attribute_get_handle(attrib);
+
+	DBG("CCC write called for handle: 0x%04x", handle);
+
+	if (!value || len != 2) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	if (offset > 2) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+		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) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	/* If value is identical, then just succeed */
+	if (ccc->value[0] == value[0] && ccc->value[1] == value[1])
+		goto done;
+
+	if (ccc_cb->callback)
+		ecode = ccc_cb->callback(get_le16(value), ccc_cb->user_data);
+
+	if (!ecode) {
+		ccc->value[0] = value[0];
+		ccc->value[1] = value[1];
+	}
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static struct gatt_db_attribute *
+service_add_ccc(struct gatt_db_attribute *service,
+				struct btd_gatt_database *database,
+				btd_gatt_database_ccc_write_t write_callback,
+				void *user_data,
+				btd_gatt_database_destroy_t destroy)
+{
+	struct gatt_db_attribute *ccc;
+	struct ccc_cb_data *ccc_cb;
+	bt_uuid_t uuid;
+
+	ccc_cb = new0(struct ccc_cb_data, 1);
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+	ccc = gatt_db_service_add_descriptor(service, &uuid,
+				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				gatt_ccc_read_cb, gatt_ccc_write_cb, database);
+	if (!ccc) {
+		error("Failed to create CCC entry in database");
+		free(ccc_cb);
+		return NULL;
+	}
+
+	ccc_cb->handle = gatt_db_attribute_get_handle(ccc);
+	ccc_cb->callback = write_callback;
+	ccc_cb->destroy = destroy;
+	ccc_cb->user_data = user_data;
+
+	queue_push_tail(database->ccc_callbacks, ccc_cb);
+
+	return ccc;
+}
+
+struct gatt_db_attribute *
+btd_gatt_database_add_ccc(struct btd_gatt_database *database,
+				uint16_t service_handle,
+				btd_gatt_database_ccc_write_t write_callback,
+				void *user_data,
+				btd_gatt_database_destroy_t destroy)
+{
+	struct gatt_db_attribute *service;
+
+	if (!database || !service_handle)
+		return NULL;
+
+	service = gatt_db_get_attribute(database->db, service_handle);
+	if (!service) {
+		error("No service exists with handle: 0x%04x", service_handle);
+		return NULL;
+	}
+
+	return service_add_ccc(service, database, write_callback, user_data,
+								destroy);
+}
+
+static void populate_gatt_service(struct btd_gatt_database *database)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service;
+
+	/* 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,
+				BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_INDICATE,
+				NULL, NULL, database);
+
+	database->svc_chngd_ccc = service_add_ccc(service, database, NULL, NULL,
+									NULL);
+
+	gatt_db_service_set_active(service, true);
+}
+
+static void register_core_services(struct btd_gatt_database *database)
+{
+	populate_gap_service(database);
+	populate_gatt_service(database);
+}
+
+struct notify {
+	struct btd_gatt_database *database;
+	uint16_t handle, ccc_handle;
+	const uint8_t *value;
+	uint16_t len;
+	bool indicate;
+};
+
+static void conf_cb(void *user_data)
+{
+	DBG("GATT server received confirmation");
+}
+
+static void send_notification_to_device(void *data, void *user_data)
+{
+	struct device_state *device_state = data;
+	struct notify *notify = user_data;
+	struct ccc_state *ccc;
+	struct btd_device *device;
+
+	ccc = find_ccc_state(device_state, notify->ccc_handle);
+	if (!ccc)
+		return;
+
+	if (!ccc->value[0] || (notify->indicate && !(ccc->value[0] & 0x02)))
+		return;
+
+	device = btd_adapter_get_device(notify->database->adapter,
+						&device_state->bdaddr,
+						device_state->bdaddr_type);
+	if (!device)
+		return;
+
+	/*
+	 * TODO: If the device is not connected but bonded, send the
+	 * notification/indication when it becomes connected.
+	 */
+	if (!notify->indicate) {
+		DBG("GATT server sending notification");
+		bt_gatt_server_send_notification(
+					btd_device_get_gatt_server(device),
+					notify->handle, notify->value,
+					notify->len);
+		return;
+	}
+
+	DBG("GATT server sending indication");
+	bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+							notify->handle,
+							notify->value,
+							notify->len, conf_cb,
+							NULL, NULL);
+}
+
+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)
+{
+	struct notify notify;
+
+	memset(&notify, 0, sizeof(notify));
+
+	notify.database = database;
+	notify.handle = handle;
+	notify.ccc_handle = ccc_handle;
+	notify.value = value;
+	notify.len = len;
+	notify.indicate = indicate;
+
+	queue_foreach(database->device_states, send_notification_to_device,
+								&notify);
+}
+
+static void send_service_changed(struct btd_gatt_database *database,
+					struct gatt_db_attribute *attrib)
+{
+	uint16_t start, end;
+	uint8_t value[4];
+	uint16_t handle, ccc_handle;
+
+	if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) {
+		error("Failed to obtain changed service handles");
+		return;
+	}
+
+	handle = gatt_db_attribute_get_handle(database->svc_chngd);
+	ccc_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
+
+	if (!handle || !ccc_handle) {
+		error("Failed to obtain handles for \"Service Changed\""
+							" characteristic");
+		return;
+	}
+
+	put_le16(start, value);
+	put_le16(end, value + 2);
+
+	send_notification_to_devices(database, handle, value, sizeof(value),
+							ccc_handle, true);
+}
+
+static void gatt_db_service_added(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+
+	DBG("GATT Service added to local database");
+
+	send_service_changed(database, attrib);
+}
+
+static bool ccc_match_service(const void *data, const void *match_data)
+{
+	const struct ccc_state *ccc = data;
+	const struct gatt_db_attribute *attrib = match_data;
+	uint16_t start, end;
+
+	if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
+		return false;
+
+	return ccc->handle >= start && ccc->handle <= end;
+}
+
+static void remove_device_ccc(void *data, void *user_data)
+{
+	struct device_state *state = data;
+
+	queue_remove_all(state->ccc_states, ccc_match_service, user_data, free);
+}
+
+static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+
+	DBG("Local GATT service removed");
+
+	send_service_changed(database, attrib);
+
+	queue_foreach(database->device_states, remove_device_ccc, attrib);
+	queue_remove_all(database->ccc_callbacks, ccc_cb_match_service, attrib,
+								ccc_cb_free);
+}
+
+struct svc_match_data {
+	const char *path;
+	const char *sender;
+};
+
+static bool match_service(const void *a, const void *b)
+{
+	const struct external_service *service = a;
+	const struct svc_match_data *data = b;
+
+	return g_strcmp0(service->path, data->path) == 0 &&
+				g_strcmp0(service->owner, data->sender) == 0;
+}
+
+static gboolean service_free_idle_cb(void *data)
+{
+	service_free(data);
+
+	return FALSE;
+}
+
+static void service_remove_helper(void *data)
+{
+	struct external_service *service = data;
+
+	queue_remove(service->database->services, service);
+
+	/*
+	 * Do not run in the same loop, this may be a disconnect
+	 * watch call and GDBusClient should not be destroyed.
+	 */
+	g_idle_add(service_free_idle_cb, service);
+}
+
+static void client_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+	DBG("Client disconnected");
+
+	service_remove_helper(user_data);
+}
+
+static void remove_service(void *data)
+{
+	struct external_service *service = data;
+
+	/*
+	 * Set callback to NULL to avoid potential race condition
+	 * when calling remove_service and GDBusClient unref.
+	 */
+	g_dbus_client_set_disconnect_watch(service->client, NULL, NULL);
+
+	/*
+	 * Set proxy handlers to NULL, so that this gets called only once when
+	 * the first proxy that belongs to this service gets removed.
+	 */
+	g_dbus_client_set_proxy_handlers(service->client, NULL, NULL,
+								NULL, NULL);
+
+	service_remove_helper(service);
+}
+
+static struct external_chrc *chrc_create(struct external_service *service,
+							GDBusProxy *proxy,
+							const char *path)
+{
+	struct external_chrc *chrc;
+
+	chrc = new0(struct external_chrc, 1);
+	chrc->pending_reads = queue_new();
+	chrc->pending_writes = queue_new();
+
+	chrc->path = g_strdup(path);
+	if (!chrc->path) {
+		queue_destroy(chrc->pending_reads, NULL);
+		queue_destroy(chrc->pending_writes, NULL);
+		free(chrc);
+		return NULL;
+	}
+
+	chrc->service = service;
+	chrc->proxy = g_dbus_proxy_ref(proxy);
+
+	return chrc;
+}
+
+static struct external_desc *desc_create(struct external_service *service,
+							GDBusProxy *proxy,
+							const char *chrc_path)
+{
+	struct external_desc *desc;
+
+	desc = new0(struct external_desc, 1);
+	desc->pending_reads = queue_new();
+	desc->pending_writes = queue_new();
+
+	desc->chrc_path = g_strdup(chrc_path);
+	if (!desc->chrc_path) {
+		queue_destroy(desc->pending_reads, NULL);
+		queue_destroy(desc->pending_writes, NULL);
+		free(desc);
+		return NULL;
+	}
+
+	desc->service = service;
+	desc->proxy = g_dbus_proxy_ref(proxy);
+
+	return desc;
+}
+
+static bool incr_attr_count(struct external_service *service, uint16_t incr)
+{
+	if (service->attr_cnt > UINT16_MAX - incr)
+		return false;
+
+	service->attr_cnt += incr;
+
+	return true;
+}
+
+static bool parse_path(GDBusProxy *proxy, const char *name, const char **path)
+{
+	DBusMessageIter iter;
+
+	if (!g_dbus_proxy_get_property(proxy, name, &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+		return false;
+
+	dbus_message_iter_get_basic(&iter, path);
+
+	return true;
+}
+
+static bool check_service_path(GDBusProxy *proxy,
+					struct external_service *service)
+{
+	const char *service_path;
+
+	if (!parse_path(proxy, "Service", &service_path))
+		return false;
+
+	return g_strcmp0(service_path, service->path) == 0;
+}
+
+static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
+							uint8_t *ext_props)
+{
+	const char *flag;
+
+	*props = *ext_props = 0;
+
+	do {
+		if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_STRING)
+			return false;
+
+		dbus_message_iter_get_basic(array, &flag);
+
+		if (!strcmp("broadcast", flag))
+			*props |= BT_GATT_CHRC_PROP_BROADCAST;
+		else if (!strcmp("read", flag))
+			*props |= BT_GATT_CHRC_PROP_READ;
+		else if (!strcmp("write-without-response", flag))
+			*props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP;
+		else if (!strcmp("write", flag))
+			*props |= BT_GATT_CHRC_PROP_WRITE;
+		else if (!strcmp("notify", flag))
+			*props |= BT_GATT_CHRC_PROP_NOTIFY;
+		else if (!strcmp("indicate", flag))
+			*props |= BT_GATT_CHRC_PROP_INDICATE;
+		else if (!strcmp("authenticated-signed-writes", flag))
+			*props |= BT_GATT_CHRC_PROP_AUTH;
+		else if (!strcmp("reliable-write", flag))
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
+		else if (!strcmp("writable-auxiliaries", flag))
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX;
+		else if (!strcmp("encrypt-read", flag)) {
+			*props |= BT_GATT_CHRC_PROP_READ;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_READ;
+		} else if (!strcmp("encrypt-write", flag)) {
+			*props |= BT_GATT_CHRC_PROP_WRITE;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_WRITE;
+		} else if (!strcmp("encrypt-authenticated-read", flag)) {
+			*props |= BT_GATT_CHRC_PROP_READ;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+		} else if (!strcmp("encrypt-authenticated-write", flag)) {
+			*props |= BT_GATT_CHRC_PROP_WRITE;
+			*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+		} else {
+			error("Invalid characteristic flag: %s", flag);
+			return false;
+		}
+	} while (dbus_message_iter_next(array));
+
+	if (*ext_props)
+		*props |= BT_GATT_CHRC_PROP_EXT_PROP;
+
+	return true;
+}
+
+static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm)
+{
+	const char *flag;
+
+	*perm = 0;
+
+	do {
+		if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_STRING)
+			return false;
+
+		dbus_message_iter_get_basic(array, &flag);
+
+		if (!strcmp("read", flag))
+			*perm |= BT_ATT_PERM_READ;
+		else if (!strcmp("write", flag))
+			*perm |= BT_ATT_PERM_WRITE;
+		else if (!strcmp("encrypt-read", flag))
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT;
+		else if (!strcmp("encrypt-write", flag))
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT;
+		else if (!strcmp("encrypt-authenticated-read", flag))
+			*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
+		else if (!strcmp("encrypt-authenticated-write", flag))
+			*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+		else {
+			error("Invalid descriptor flag: %s", flag);
+			return false;
+		}
+	} while (dbus_message_iter_next(array));
+
+	return true;
+}
+
+static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props,
+								uint32_t *perm)
+{
+	DBusMessageIter iter, array;
+
+	if (!g_dbus_proxy_get_property(proxy, "Flags", &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	if (perm)
+		return parse_desc_flags(&array, perm);
+
+	return parse_chrc_flags(&array, props, ext_props);
+}
+
+static void proxy_added_cb(GDBusProxy *proxy, void *user_data)
+{
+	struct external_service *service = user_data;
+	const char *iface, *path;
+
+	if (service->failed || service->attrib)
+		return;
+
+	iface = g_dbus_proxy_get_interface(proxy);
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (!g_str_has_prefix(path, service->path))
+		return;
+
+	if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) {
+		if (service->proxy)
+			return;
+
+		/*
+		 * TODO: We may want to support adding included services in a
+		 * single hierarchy.
+		 */
+		if (g_strcmp0(path, service->path) != 0) {
+			error("Multiple services added within hierarchy");
+			service->failed = true;
+			return;
+		}
+
+		/* Add 1 for the service declaration */
+		if (!incr_attr_count(service, 1)) {
+			error("Failed to increment attribute count");
+			service->failed = true;
+			return;
+		}
+
+		service->proxy = g_dbus_proxy_ref(proxy);
+	} else if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) {
+		struct external_chrc *chrc;
+
+		if (g_strcmp0(path, service->path) == 0) {
+			error("Characteristic path same as service path");
+			service->failed = true;
+			return;
+		}
+
+		chrc = chrc_create(service, proxy, path);
+		if (!chrc) {
+			service->failed = true;
+			return;
+		}
+
+		/*
+		 * Add 2 for the characteristic declaration and the value
+		 * attribute.
+		 */
+		if (!incr_attr_count(service, 2)) {
+			error("Failed to increment attribute count");
+			service->failed = true;
+			return;
+		}
+
+		/*
+		 * Parse characteristic flags (i.e. properties) here since they
+		 * are used to determine if any special descriptors should be
+		 * created.
+		 */
+		if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
+			error("Failed to parse characteristic properties");
+			service->failed = true;
+			return;
+		}
+
+		if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+				chrc->props & BT_GATT_CHRC_PROP_INDICATE) &&
+				!incr_attr_count(service, 1)) {
+			error("Failed to increment attribute count for CCC");
+			service->failed = true;
+			return;
+		}
+
+		if (chrc->ext_props && !incr_attr_count(service, 1)) {
+			error("Failed to increment attribute count for CEP");
+			service->failed = true;
+			return;
+		}
+
+		queue_push_tail(service->chrcs, chrc);
+	} else if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) {
+		struct external_desc *desc;
+		const char *chrc_path;
+
+		if (!parse_path(proxy, "Characteristic", &chrc_path)) {
+			error("Failed to obtain characteristic path for "
+								"descriptor");
+			service->failed = true;
+			return;
+		}
+
+		desc = desc_create(service, proxy, chrc_path);
+		if (!desc) {
+			service->failed = true;
+			return;
+		}
+
+		/* Add 1 for the descriptor attribute */
+		if (!incr_attr_count(service, 1)) {
+			error("Failed to increment attribute count");
+			service->failed = true;
+			return;
+		}
+
+		/*
+		 * Parse descriptors flags here since they are used to
+		 * determine the permission the descriptor should have
+		 */
+		if (!parse_flags(proxy, NULL, NULL, &desc->perm)) {
+			error("Failed to parse characteristic properties");
+			service->failed = true;
+			return;
+		}
+
+		queue_push_tail(service->descs, desc);
+	} else {
+		DBG("Ignoring unrelated interface: %s", iface);
+		return;
+	}
+
+	DBG("Object added to service - path: %s, iface: %s", path, iface);
+}
+
+static void proxy_removed_cb(GDBusProxy *proxy, void *user_data)
+{
+	struct external_service *service = user_data;
+	const char *path;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (!g_str_has_prefix(path, service->path))
+		return;
+
+	DBG("Proxy removed - removing service: %s", service->path);
+
+	remove_service(service);
+}
+
+static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid)
+{
+	DBusMessageIter iter;
+	bt_uuid_t tmp;
+	const char *uuidstr;
+
+	if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return false;
+
+	dbus_message_iter_get_basic(&iter, &uuidstr);
+
+	if (bt_string_to_uuid(uuid, uuidstr) < 0)
+		return false;
+
+	/* GAP & GATT services are created and managed by BlueZ */
+	bt_uuid16_create(&tmp, UUID_GAP);
+	if (!bt_uuid_cmp(&tmp, uuid)) {
+		error("GAP service must be handled by BlueZ");
+		return false;
+	}
+
+	bt_uuid16_create(&tmp, UUID_GATT);
+	if (!bt_uuid_cmp(&tmp, uuid)) {
+		error("GATT service must be handled by BlueZ");
+		return false;
+	}
+
+	return true;
+}
+
+static bool parse_primary(GDBusProxy *proxy, bool *primary)
+{
+	DBusMessageIter iter;
+	dbus_bool_t val;
+
+	if (!g_dbus_proxy_get_property(proxy, "Primary", &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+		return false;
+
+	dbus_message_iter_get_basic(&iter, &val);
+
+	*primary = val;
+
+	return true;
+}
+
+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" */
+
+	if (strcmp(error_name, "org.bluez.Error.NotSupported") == 0)
+		return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+	if (strcmp(error_name, "org.bluez.Error.NotAuthorized") == 0)
+		return BT_ATT_ERROR_AUTHORIZATION;
+
+	if (strcmp(error_name, "org.bluez.Error.InvalidValueLength") == 0)
+		return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+
+	if (strcmp(error_name, "org.bluez.Error.InProgress") == 0)
+		return BT_ERROR_ALREADY_IN_PROGRESS;
+
+	return 0;
+}
+
+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;
+	uint8_t *value = NULL;
+	int len = 0;
+
+	if (!op->owner_queue) {
+		DBG("Pending read was canceled when object got removed");
+		return;
+	}
+
+	dbus_error_init(&err);
+
+	if (dbus_set_error_from_message(&err, message) == TRUE) {
+		DBG("Failed to read value: %s: %s", err.name, err.message);
+		ecode = dbus_error_to_att_ecode(err.name);
+		ecode = ecode ? ecode : BT_ATT_ERROR_READ_NOT_PERMITTED;
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+		/*
+		 * Return not supported for this, as the external app basically
+		 * doesn't properly support reading from this characteristic.
+		 */
+		ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+		error("Invalid return value received for \"ReadValue\"");
+		goto done;
+	}
+
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+	if (len < 0) {
+		ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+		value = NULL;
+		len = 0;
+		goto done;
+	}
+
+	/* Truncate the value if it's too large */
+	len = MIN(BT_ATT_MAX_VALUE_LEN, len);
+	value = len ? value : NULL;
+
+done:
+	gatt_db_attribute_read_result(op->attrib, op->id, ecode, value, len);
+}
+
+static void pending_op_free(void *data)
+{
+	struct pending_op *op = data;
+
+	if (op->owner_queue)
+		queue_remove(op->owner_queue, op);
+
+	free(op);
+}
+
+static struct pending_op *pending_read_new(struct queue *owner_queue,
+					struct gatt_db_attribute *attrib,
+					unsigned int id)
+{
+	struct pending_op *op;
+
+	op = new0(struct pending_op, 1);
+
+	op->owner_queue = owner_queue;
+	op->attrib = attrib;
+	op->id = id;
+	queue_push_tail(owner_queue, op);
+
+	return op;
+}
+
+static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+						struct queue *owner_queue,
+						unsigned int id)
+{
+	struct pending_op *op;
+	uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+
+	op = pending_read_new(owner_queue, attrib, id);
+
+	if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
+						op, pending_op_free) == TRUE)
+		return;
+
+	pending_op_free(op);
+
+	gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0);
+}
+
+static void write_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+	struct pending_op *op = user_data;
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+					&op->data.iov_base, op->data.iov_len);
+	dbus_message_iter_close_container(iter, &array);
+}
+
+static void write_reply_cb(DBusMessage *message, void *user_data)
+{
+	struct pending_op *op = user_data;
+	DBusError err;
+	DBusMessageIter iter;
+	uint8_t ecode = 0;
+
+	if (!op->owner_queue) {
+		DBG("Pending write was canceled when object got removed");
+		return;
+	}
+
+	dbus_error_init(&err);
+
+	if (dbus_set_error_from_message(&err, message) == TRUE) {
+		DBG("Failed to write value: %s: %s", err.name, err.message);
+		ecode = dbus_error_to_att_ecode(err.name);
+		ecode = ecode ? ecode : BT_ATT_ERROR_WRITE_NOT_PERMITTED;
+		dbus_error_free(&err);
+		goto done;
+	}
+
+	dbus_message_iter_init(message, &iter);
+	if (dbus_message_iter_has_next(&iter)) {
+		/*
+		 * Return not supported for this, as the external app basically
+		 * doesn't properly support the "WriteValue" API.
+		 */
+		ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+		error("Invalid return value received for \"WriteValue\"");
+	}
+
+done:
+	gatt_db_attribute_write_result(op->attrib, op->id, ecode);
+}
+
+static struct pending_op *pending_write_new(struct queue *owner_queue,
+					struct gatt_db_attribute *attrib,
+					unsigned int id,
+					const uint8_t *value,
+					size_t len)
+{
+	struct pending_op *op;
+
+	op = new0(struct pending_op, 1);
+
+	op->data.iov_base = (uint8_t *) value;
+	op->data.iov_len = len;
+
+	op->owner_queue = owner_queue;
+	op->attrib = attrib;
+	op->id = id;
+	queue_push_tail(owner_queue, op);
+
+	return op;
+}
+
+static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+					struct queue *owner_queue,
+					unsigned int id,
+					const uint8_t *value, size_t len)
+{
+	struct pending_op *op;
+	uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+
+	op = pending_write_new(owner_queue, attrib, id, value, len);
+
+	if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
+						write_reply_cb, op,
+						pending_op_free) == TRUE)
+		return;
+
+	pending_op_free(op);
+
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
+{
+	uint32_t perm = 0;
+
+	if (props & BT_GATT_CHRC_PROP_WRITE ||
+			props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP ||
+			ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE ||
+			ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE ||
+			ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
+		perm |= BT_ATT_PERM_WRITE;
+
+	if (props & BT_GATT_CHRC_PROP_READ ||
+			ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ ||
+			ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
+		perm |= BT_ATT_PERM_READ;
+
+	if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ)
+		perm |= BT_ATT_PERM_READ_ENCRYPT;
+
+	if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
+		perm |= BT_ATT_PERM_WRITE_ENCRYPT;
+
+	if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
+		perm |= BT_ATT_PERM_READ_AUTHEN;
+
+	if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
+		perm |= BT_ATT_PERM_WRITE_AUTHEN;
+
+	return perm;
+}
+
+static uint8_t ccc_write_cb(uint16_t value, void *user_data)
+{
+	struct external_chrc *chrc = user_data;
+
+	DBG("External CCC write received with value: 0x%04x", value);
+
+	/* Notifications/indications disabled */
+	if (!value) {
+		if (!chrc->ntfy_cnt)
+			return 0;
+
+		if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
+			return 0;
+
+		/*
+		 * Send request to stop notifying. This is best-effort
+		 * operation, so simply ignore the return the value.
+		 */
+		g_dbus_proxy_method_call(chrc->proxy, "StopNotify", NULL,
+							NULL, NULL, NULL);
+		return 0;
+	}
+
+	if (chrc->ntfy_cnt == UINT_MAX) {
+		/* Maximum number of per-device CCC descriptors configured */
+		return BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+	}
+
+	/* Don't support undefined CCC values yet */
+	if (value > 2 ||
+		(value == 1 && !(chrc->props & BT_GATT_CHRC_PROP_NOTIFY)) ||
+		(value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
+		return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
+
+	/*
+	 * 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,
+						NULL, NULL) == FALSE)
+		return BT_ATT_ERROR_UNLIKELY;
+
+	__sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+
+	return 0;
+}
+
+static void property_changed_cb(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct external_chrc *chrc = user_data;
+	DBusMessageIter array;
+	uint8_t *value = NULL;
+	int len = 0;
+
+	if (strcmp(name, "Value"))
+		return;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+		DBG("Malformed \"Value\" property received");
+		return;
+	}
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+	if (len < 0) {
+		DBG("Malformed \"Value\" property received");
+		return;
+	}
+
+	/* Truncate the value if it's too large */
+	len = MIN(BT_ATT_MAX_VALUE_LEN, len);
+	value = len ? value : NULL;
+
+	send_notification_to_devices(chrc->service->database,
+				gatt_db_attribute_get_handle(chrc->attrib),
+				value, len,
+				gatt_db_attribute_get_handle(chrc->ccc),
+				chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+}
+
+static bool database_add_ccc(struct external_service *service,
+						struct external_chrc *chrc)
+{
+	if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY) &&
+				!(chrc->props & BT_GATT_CHRC_PROP_INDICATE))
+		return true;
+
+	chrc->ccc = service_add_ccc(service->attrib, service->database,
+						ccc_write_cb, chrc, NULL);
+	if (!chrc->ccc) {
+		error("Failed to create CCC entry for characteristic");
+		return false;
+	}
+
+	if (g_dbus_proxy_set_property_watch(chrc->proxy, property_changed_cb,
+							chrc) == FALSE) {
+		error("Failed to set up property watch for characteristic");
+		return false;
+	}
+
+	DBG("Created CCC entry for characteristic");
+
+	return true;
+}
+
+static void cep_write_cb(struct gatt_db_attribute *attrib, int err,
+								void *user_data)
+{
+	if (err)
+		DBG("Failed to store CEP value in the database");
+	else
+		DBG("Stored CEP value in the database");
+}
+
+static bool database_add_cep(struct external_service *service,
+						struct external_chrc *chrc)
+{
+	struct gatt_db_attribute *cep;
+	bt_uuid_t uuid;
+	uint8_t value[2];
+
+	if (!chrc->ext_props)
+		return true;
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID);
+	cep = gatt_db_service_add_descriptor(service->attrib, &uuid,
+							BT_ATT_PERM_READ,
+							NULL, NULL, NULL);
+	if (!cep) {
+		error("Failed to create CEP entry for characteristic");
+		return false;
+	}
+
+	memset(value, 0, sizeof(value));
+	value[0] = chrc->ext_props;
+
+	if (!gatt_db_attribute_write(cep, 0, value, sizeof(value), 0, NULL,
+							cep_write_cb, NULL)) {
+		DBG("Failed to store CEP value in the database");
+		return false;
+	}
+
+	DBG("Created CEP entry for characteristic");
+
+	return true;
+}
+
+static void desc_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct external_desc *desc = user_data;
+
+	if (desc->attrib != attrib) {
+		error("Read callback called with incorrect attribute");
+		return;
+	}
+
+	send_read(attrib, desc->proxy, desc->pending_reads, id);
+}
+
+static void desc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct external_desc *desc = user_data;
+
+	if (desc->attrib != attrib) {
+		error("Read callback called with incorrect attribute");
+		return;
+	}
+
+	send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+}
+
+static bool database_add_desc(struct external_service *service,
+						struct external_desc *desc)
+{
+	bt_uuid_t uuid;
+
+	if (!parse_uuid(desc->proxy, &uuid)) {
+		error("Failed to read \"UUID\" property of descriptor");
+		return false;
+	}
+
+	desc->attrib = gatt_db_service_add_descriptor(service->attrib, &uuid,
+							desc->perm,
+							desc_read_cb,
+							desc_write_cb, desc);
+	if (!desc->attrib) {
+		error("Failed to create descriptor entry in database");
+		return false;
+	}
+
+	desc->handled = true;
+
+	return true;
+}
+
+static void chrc_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct external_chrc *chrc = user_data;
+
+	if (chrc->attrib != attrib) {
+		error("Read callback called with incorrect attribute");
+		return;
+	}
+
+	send_read(attrib, chrc->proxy, chrc->pending_reads, id);
+}
+
+static void chrc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct external_chrc *chrc = user_data;
+
+	if (chrc->attrib != attrib) {
+		error("Write callback called with incorrect attribute");
+		return;
+	}
+
+	send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+}
+
+static bool database_add_chrc(struct external_service *service,
+						struct external_chrc *chrc)
+{
+	bt_uuid_t uuid;
+	uint32_t perm;
+	const struct queue_entry *entry;
+
+	if (!parse_uuid(chrc->proxy, &uuid)) {
+		error("Failed to read \"UUID\" property of characteristic");
+		return false;
+	}
+
+	if (!check_service_path(chrc->proxy, service)) {
+		error("Invalid service path for characteristic");
+		return false;
+	}
+
+	/*
+	 * TODO: Once shared/gatt-server properly supports permission checks,
+	 * set the permissions based on a D-Bus property of the external
+	 * characteristic.
+	 */
+	perm = permissions_from_props(chrc->props, chrc->ext_props);
+	chrc->attrib = gatt_db_service_add_characteristic(service->attrib,
+						&uuid, perm,
+						chrc->props, chrc_read_cb,
+						chrc_write_cb, chrc);
+	if (!chrc->attrib) {
+		error("Failed to create characteristic entry in database");
+		return false;
+	}
+
+	if (!database_add_ccc(service, chrc))
+		return false;
+
+	if (!database_add_cep(service, chrc))
+		return false;
+
+	/* Handle the descriptors that belong to this characteristic. */
+	for (entry = queue_get_entries(service->descs); entry;
+							entry = entry->next) {
+		struct external_desc *desc = entry->data;
+
+		if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
+			continue;
+
+		if (!database_add_desc(service, desc)) {
+			chrc->attrib = NULL;
+			error("Failed to create descriptor entry");
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool match_desc_unhandled(const void *a, const void *b)
+{
+	const struct external_desc *desc = a;
+
+	return !desc->handled;
+}
+
+static bool create_service_entry(struct external_service *service)
+{
+	bt_uuid_t uuid;
+	bool primary;
+	const struct queue_entry *entry;
+
+	if (!parse_uuid(service->proxy, &uuid)) {
+		error("Failed to read \"UUID\" property of service");
+		return false;
+	}
+
+	if (!parse_primary(service->proxy, &primary)) {
+		error("Failed to read \"Primary\" property of service");
+		return false;
+	}
+
+	service->attrib = gatt_db_add_service(service->database->db, &uuid,
+						primary, service->attr_cnt);
+	if (!service->attrib)
+		return false;
+
+	entry = queue_get_entries(service->chrcs);
+	while (entry) {
+		struct external_chrc *chrc = entry->data;
+
+		if (!database_add_chrc(service, chrc)) {
+			error("Failed to add characteristic");
+			goto fail;
+		}
+
+		entry = entry->next;
+	}
+
+	/* If there are any unhandled descriptors, return an error */
+	if (queue_find(service->descs, match_desc_unhandled, NULL)) {
+		error("Found descriptor with no matching characteristic!");
+		goto fail;
+	}
+
+	gatt_db_service_set_active(service->attrib, true);
+
+	return true;
+
+fail:
+	gatt_db_remove_service(service->database->db, service->attrib);
+	service->attrib = NULL;
+
+	return false;
+}
+
+static void client_ready_cb(GDBusClient *client, void *user_data)
+{
+	struct external_service *service = user_data;
+	DBusMessage *reply;
+	bool fail = false;
+
+	if (!service->proxy || service->failed) {
+		error("No valid external GATT objects found");
+		fail = true;
+		reply = btd_error_failed(service->reg,
+					"No valid service object found");
+		goto reply;
+	}
+
+	if (!create_service_entry(service)) {
+		error("Failed to create GATT service entry in local database");
+		fail = true;
+		reply = btd_error_failed(service->reg,
+					"Failed to create entry in database");
+		goto reply;
+	}
+
+	DBG("GATT service registered: %s", service->path);
+
+	reply = dbus_message_new_method_return(service->reg);
+
+reply:
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+	dbus_message_unref(service->reg);
+	service->reg = NULL;
+
+	if (fail)
+		remove_service(service);
+}
+
+static struct external_service *create_service(DBusConnection *conn,
+					DBusMessage *msg, const char *path)
+{
+	struct external_service *service;
+	const char *sender = dbus_message_get_sender(msg);
+
+	if (!path || !g_str_has_prefix(path, "/"))
+		return NULL;
+
+	service = new0(struct external_service, 1);
+
+	service->client = g_dbus_client_new_full(conn, sender, path, path);
+	if (!service->client)
+		goto fail;
+
+	service->owner = g_strdup(sender);
+	if (!service->owner)
+		goto fail;
+
+	service->path = g_strdup(path);
+	if (!service->path)
+		goto fail;
+
+	service->chrcs = queue_new();
+	service->descs = queue_new();
+
+	service->reg = dbus_message_ref(msg);
+
+	g_dbus_client_set_disconnect_watch(service->client,
+						client_disconnect_cb, service);
+	g_dbus_client_set_proxy_handlers(service->client, proxy_added_cb,
+							proxy_removed_cb, NULL,
+							service);
+	g_dbus_client_set_ready_watch(service->client, client_ready_cb,
+								service);
+
+	return service;
+
+fail:
+	service_free(service);
+	return NULL;
+}
+
+static DBusMessage *manager_register_service(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	const char *sender = dbus_message_get_sender(msg);
+	DBusMessageIter args;
+	const char *path;
+	struct external_service *service;
+	struct svc_match_data match_data;
+
+	if (!dbus_message_iter_init(msg, &args))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &path);
+
+	match_data.path = path;
+	match_data.sender = sender;
+
+	if (queue_find(database->services, match_service, &match_data))
+		return btd_error_already_exists(msg);
+
+	dbus_message_iter_next(&args);
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+		return btd_error_invalid_args(msg);
+
+	service = create_service(conn, msg, path);
+	if (!service)
+		return btd_error_failed(msg, "Failed to register service");
+
+	DBG("Registering service - path: %s", path);
+
+	service->database = database;
+	queue_push_tail(database->services, service);
+
+	return NULL;
+}
+
+static DBusMessage *manager_unregister_service(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	const char *sender = dbus_message_get_sender(msg);
+	const char *path;
+	DBusMessageIter args;
+	struct external_service *service;
+	struct svc_match_data match_data;
+
+	if (!dbus_message_iter_init(msg, &args))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &path);
+
+	match_data.path = path;
+	match_data.sender = sender;
+
+	service = queue_remove_if(database->services, match_service,
+								&match_data);
+	if (!service)
+		return btd_error_does_not_exist(msg);
+
+	service_free(service);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static void profile_exited(DBusConnection *conn, void *user_data)
+{
+	struct external_profile *profile = user_data;
+
+	DBG("\"%s\" exited", profile->owner);
+
+	profile->id = 0;
+
+	queue_remove(profile->database->profiles, profile);
+
+	profile_free(profile);
+}
+
+static int profile_device_probe(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	DBG("%s probed", p->name);
+
+	return 0;
+}
+
+static void profile_device_remove(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	DBG("%s removed", p->name);
+}
+
+static int profile_add(struct external_profile *profile, const char *uuid)
+{
+	struct btd_profile *p;
+
+	p = new0(struct btd_profile, 1);
+
+	/* Assign directly to avoid having extra fields */
+	p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner,
+							profile->path, uuid);
+	if (!p->name) {
+		free(p);
+		return -ENOMEM;
+	}
+
+	p->remote_uuid = (const void *) g_strdup(uuid);
+	if (!p->remote_uuid) {
+		g_free((void *) p->name);
+		free(p);
+		return -ENOMEM;
+	}
+
+	p->device_probe = profile_device_probe;
+	p->device_remove = profile_device_remove;
+	p->auto_connect = true;
+	p->external = true;
+
+	queue_push_tail(profile->profiles, p);
+
+	DBG("Added \"%s\"", p->name);
+
+	return 0;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+	struct btd_adapter *adapter = user_data;
+
+	btd_profile_register(data);
+	adapter_add_profile(adapter, data);
+}
+
+static int profile_create(DBusConnection *conn,
+				struct btd_gatt_database *database,
+				const char *sender, const char *path,
+				DBusMessageIter *iter)
+{
+	struct external_profile *profile;
+	DBusMessageIter uuids;
+
+	if (!path || !g_str_has_prefix(path, "/"))
+		return -EINVAL;
+
+	profile = new0(struct external_profile, 1);
+
+	profile->owner = g_strdup(sender);
+	if (!profile->owner)
+		goto fail;
+
+	profile->path = g_strdup(path);
+	if (!profile->path)
+		goto fail;
+
+	profile->profiles = queue_new();
+	profile->database = database;
+	profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited,
+								profile, NULL);
+
+	dbus_message_iter_recurse(iter, &uuids);
+
+	while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) {
+		const char *uuid;
+
+		dbus_message_iter_get_basic(&uuids, &uuid);
+
+		if (profile_add(profile, uuid) < 0)
+			goto fail;
+
+		dbus_message_iter_next(&uuids);
+	}
+
+	if (queue_isempty(profile->profiles))
+		goto fail;
+
+	queue_foreach(profile->profiles, add_profile, database->adapter);
+	queue_push_tail(database->profiles, profile);
+
+	return 0;
+
+fail:
+	profile_free(profile);
+	return -EINVAL;
+}
+
+static bool match_profile(const void *a, const void *b)
+{
+	const struct external_profile *profile = a;
+	const struct svc_match_data *data = b;
+
+	return g_strcmp0(profile->path, data->path) == 0 &&
+				g_strcmp0(profile->owner, data->sender) == 0;
+}
+
+static DBusMessage *manager_register_profile(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	const char *sender = dbus_message_get_sender(msg);
+	DBusMessageIter args;
+	const char *path;
+	struct svc_match_data match_data;
+
+	DBG("sender %s", sender);
+
+	if (!dbus_message_iter_init(msg, &args))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &path);
+
+	match_data.path = path;
+	match_data.sender = sender;
+
+	if (queue_find(database->profiles, match_profile, &match_data))
+		return btd_error_already_exists(msg);
+
+	dbus_message_iter_next(&args);
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+		return btd_error_invalid_args(msg);
+
+	if (profile_create(conn, database, sender, path, &args) < 0)
+		return btd_error_failed(msg, "Failed to register profile");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *manager_unregister_profile(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	const char *sender = dbus_message_get_sender(msg);
+	const char *path;
+	DBusMessageIter args;
+	struct external_profile *profile;
+	struct svc_match_data match_data;
+
+	if (!dbus_message_iter_init(msg, &args))
+		return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&args, &path);
+
+	match_data.path = path;
+	match_data.sender = sender;
+
+	profile = queue_remove_if(database->profiles, match_profile,
+								&match_data);
+	if (!profile)
+		return btd_error_does_not_exist(msg);
+
+	profile_free(profile);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable manager_methods[] = {
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
+			GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }),
+			NULL, manager_register_service) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterService",
+					GDBUS_ARGS({ "service", "o" }),
+					NULL, manager_unregister_service) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
+			GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
+			{ "options", "a{sv}" }), NULL,
+			manager_register_profile) },
+	{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile",
+					GDBUS_ARGS({ "profile", "o" }),
+					NULL, manager_unregister_profile) },
+	{ }
+};
+
+struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
+{
+	struct btd_gatt_database *database;
+	GError *gerr = NULL;
+	const bdaddr_t *addr;
+
+	if (!adapter)
+		return NULL;
+
+	database = new0(struct btd_gatt_database, 1);
+	database->adapter = btd_adapter_ref(adapter);
+	database->db = gatt_db_new();
+	database->device_states = queue_new();
+	database->services = queue_new();
+	database->profiles = queue_new();
+	database->ccc_callbacks = queue_new();
+
+	database->db_id = gatt_db_register(database->db, gatt_db_service_added,
+							gatt_db_service_removed,
+							database, NULL);
+	if (!database->db_id)
+		goto fail;
+
+	addr = btd_adapter_get_address(adapter);
+	database->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, addr,
+					BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+					BT_IO_OPT_CID, ATT_CID,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+	if (!database->le_io) {
+		error("Failed to start listening: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	/* BR/EDR socket */
+	database->l2cap_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, addr,
+					BT_IO_OPT_PSM, ATT_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+	if (database->l2cap_io == NULL) {
+		error("Failed to start listening: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(),
+						adapter_get_path(adapter),
+						GATT_MANAGER_IFACE,
+						manager_methods, NULL, NULL,
+						database, NULL))
+		DBG("GATT Manager registered for adapter: %s",
+						adapter_get_path(adapter));
+
+	register_core_services(database);
+
+	return database;
+
+fail:
+	gatt_database_free(database);
+
+	return NULL;
+}
+
+void btd_gatt_database_destroy(struct btd_gatt_database *database)
+{
+	if (!database)
+		return;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(database->adapter),
+					GATT_MANAGER_IFACE);
+
+	gatt_database_free(database);
+}
+
+struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database)
+{
+	if (!database)
+		return NULL;
+
+	return database->db;
+}
diff --git a/bluez/repo/src/gatt-database.h b/bluez/repo/src/gatt-database.h
new file mode 100644
index 0000000..163b601
--- /dev/null
+++ b/bluez/repo/src/gatt-database.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Google 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.
+ *
+ */
+
+struct btd_gatt_database;
+
+struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter);
+void btd_gatt_database_destroy(struct btd_gatt_database *database);
+
+struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
+
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+							void *user_data);
+typedef void (*btd_gatt_database_destroy_t) (void *data);
+
+struct gatt_db_attribute *
+btd_gatt_database_add_ccc(struct btd_gatt_database *database,
+				uint16_t service_handle,
+				btd_gatt_database_ccc_write_t write_callback,
+				void *user_data,
+				btd_gatt_database_destroy_t destroy);
diff --git a/bluez/repo/src/genbuiltin b/bluez/repo/src/genbuiltin
new file mode 100755
index 0000000..8b6f047
--- /dev/null
+++ b/bluez/repo/src/genbuiltin
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+	echo "extern struct bluetooth_plugin_desc __bluetooth_builtin_$i;"
+done
+
+echo
+echo "static struct bluetooth_plugin_desc *__bluetooth_builtin[] = {"
+
+for i in $*
+do
+	echo "  &__bluetooth_builtin_$i,"
+done
+
+echo "  NULL"
+echo "};"
diff --git a/bluez/repo/src/hcid.h b/bluez/repo/src/hcid.h
new file mode 100644
index 0000000..60e2b0a
--- /dev/null
+++ b/bluez/repo/src/hcid.h
@@ -0,0 +1,61 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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
+ *
+ */
+
+typedef enum {
+	BT_MODE_DUAL,
+	BT_MODE_BREDR,
+	BT_MODE_LE,
+} bt_mode_t;
+
+struct main_opts {
+	char		*name;
+	uint32_t	class;
+	uint16_t	autoto;
+	uint32_t	pairto;
+	uint32_t	discovto;
+	gboolean	reverse_sdp;
+	gboolean	name_resolv;
+	gboolean	debug_keys;
+	gboolean	fast_conn;
+
+	uint16_t	did_source;
+	uint16_t	did_vendor;
+	uint16_t	did_product;
+	uint16_t	did_version;
+
+	bt_mode_t	mode;
+};
+
+extern struct main_opts main_opts;
+
+gboolean plugin_init(const char *enable, const char *disable);
+void plugin_cleanup(void);
+
+void rfkill_init(void);
+void rfkill_exit(void);
+
+GKeyFile *btd_get_main_conf(void);
+
+void btd_exit(void);
diff --git a/bluez/repo/src/log.c b/bluez/repo/src/log.c
new file mode 100644
index 0000000..d2a20de
--- /dev/null
+++ b/bluez/repo/src/log.c
@@ -0,0 +1,331 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <syslog.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+
+#include "src/shared/util.h"
+#include "log.h"
+
+#define LOG_IDENT "bluetoothd"
+#define LOG_IDENT_LEN sizeof(LOG_IDENT)
+
+struct log_hdr {
+	uint16_t opcode;
+	uint16_t index;
+	uint16_t len;
+	uint8_t  priority;
+	uint8_t  ident_len;
+} __attribute__((packed));
+
+static int logging_fd = -1;
+
+static void logging_open(void)
+{
+	struct sockaddr_hci addr;
+	int fd;
+
+	if (logging_fd >= 0)
+		return;
+
+	fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (fd < 0)
+		return;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = HCI_DEV_NONE;
+	addr.hci_channel = HCI_CHANNEL_LOGGING;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return;
+	}
+
+	logging_fd = fd;
+}
+
+static void logging_close(void)
+{
+	if (logging_fd >= 0) {
+		close(logging_fd);
+		logging_fd = -1;
+	}
+}
+
+static void logging_log(uint16_t index, int priority,
+					const char *format, va_list ap)
+{
+	struct log_hdr hdr;
+	struct msghdr msg;
+	struct iovec iov[3];
+	uint16_t len;
+	char *str;
+
+	if (vasprintf(&str, format, ap) < 0)
+		return;
+
+	len = strlen(str) + 1;
+
+	hdr.opcode = cpu_to_le16(0x0000);
+	hdr.index = cpu_to_le16(index);
+	hdr.len = cpu_to_le16(2 + LOG_IDENT_LEN + len);
+	hdr.priority = priority;
+	hdr.ident_len = LOG_IDENT_LEN;
+
+	iov[0].iov_base = &hdr;
+	iov[0].iov_len = sizeof(hdr);
+
+	iov[1].iov_base = LOG_IDENT;
+	iov[1].iov_len = LOG_IDENT_LEN;
+
+	iov[2].iov_base = str;
+	iov[2].iov_len = len;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 3;
+
+	if (sendmsg(logging_fd, &msg, 0) < 0) {
+		if (errno != ENODEV) {
+			close(logging_fd);
+			logging_fd = -1;
+		}
+	}
+
+	free(str);
+}
+
+void error(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_ERR, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(HCI_DEV_NONE, LOG_ERR, format, ap);
+	va_end(ap);
+}
+
+void warn(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_WARNING, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(HCI_DEV_NONE, LOG_WARNING, format, ap);
+	va_end(ap);
+}
+
+void info(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_INFO, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(HCI_DEV_NONE, LOG_INFO, format, ap);
+	va_end(ap);
+}
+
+void btd_log(uint16_t index, int priority, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(priority, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(index, priority, format, ap);
+	va_end(ap);
+}
+
+void btd_error(uint16_t index, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_ERR, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(index, LOG_ERR, format, ap);
+	va_end(ap);
+}
+
+void btd_warn(uint16_t index, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_WARNING, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(index, LOG_WARNING, format, ap);
+	va_end(ap);
+}
+
+void btd_info(uint16_t index, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_INFO, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(index, LOG_INFO, format, ap);
+	va_end(ap);
+}
+
+void btd_debug(uint16_t index, const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_DEBUG, format, ap);
+	va_end(ap);
+
+	if (logging_fd < 0)
+		return;
+
+	va_start(ap, format);
+	logging_log(index, LOG_DEBUG, format, ap);
+	va_end(ap);
+}
+
+extern struct btd_debug_desc __start___debug[];
+extern struct btd_debug_desc __stop___debug[];
+
+static char **enabled = NULL;
+
+static gboolean is_enabled(struct btd_debug_desc *desc)
+{
+	int i;
+
+	if (enabled == NULL)
+		return 0;
+
+	for (i = 0; enabled[i] != NULL; i++)
+		if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+							desc->file) == TRUE)
+			return 1;
+
+	return 0;
+}
+
+void __btd_enable_debug(struct btd_debug_desc *start,
+					struct btd_debug_desc *stop)
+{
+	struct btd_debug_desc *desc;
+
+	if (start == NULL || stop == NULL)
+		return;
+
+	for (desc = start; desc < stop; desc++) {
+		if (is_enabled(desc))
+			desc->flags |= BTD_DEBUG_FLAG_PRINT;
+	}
+}
+
+void __btd_toggle_debug(void)
+{
+	struct btd_debug_desc *desc;
+
+	for (desc = __start___debug; desc < __stop___debug; desc++)
+		desc->flags |= BTD_DEBUG_FLAG_PRINT;
+}
+
+void __btd_log_init(const char *debug, int detach)
+{
+	int option = LOG_NDELAY | LOG_PID;
+
+	if (debug != NULL)
+		enabled = g_strsplit_set(debug, ":, ", 0);
+
+	__btd_enable_debug(__start___debug, __stop___debug);
+
+	logging_open();
+
+	if (!detach)
+		option |= LOG_PERROR;
+
+	openlog(LOG_IDENT, option, LOG_DAEMON);
+
+	info("Bluetooth daemon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+	closelog();
+
+	logging_close();
+
+	g_strfreev(enabled);
+}
diff --git a/bluez/repo/src/log.h b/bluez/repo/src/log.h
new file mode 100644
index 0000000..0d243ce
--- /dev/null
+++ b/bluez/repo/src/log.h
@@ -0,0 +1,73 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+#include <stdint.h>
+
+void error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void warn(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void btd_log(uint16_t index, int priority, const char *format, ...)
+					__attribute__((format(printf, 3, 4)));
+
+void btd_error(uint16_t index, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+void btd_warn(uint16_t index, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+void btd_info(uint16_t index, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+void btd_debug(uint16_t index, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+
+void __btd_log_init(const char *debug, int detach);
+void __btd_log_cleanup(void);
+void __btd_toggle_debug(void);
+
+struct btd_debug_desc {
+	const char *file;
+#define BTD_DEBUG_FLAG_DEFAULT (0)
+#define BTD_DEBUG_FLAG_PRINT   (1 << 0)
+	unsigned int flags;
+} __attribute__((aligned(8)));
+
+void __btd_enable_debug(struct btd_debug_desc *start,
+					struct btd_debug_desc *stop);
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around btd_debug() which also include the function
+ * name it is called in.
+ */
+#define DBG_IDX(idx, fmt, arg...) do { \
+	static struct btd_debug_desc __btd_debug_desc \
+	__attribute__((used, section("__debug"), aligned(8))) = { \
+		.file = __FILE__, .flags = BTD_DEBUG_FLAG_DEFAULT, \
+	}; \
+	if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \
+		btd_debug(idx, "%s:%s() " fmt, __FILE__, __func__ , ## arg); \
+} while (0)
+
+#define DBG(fmt, arg...) DBG_IDX(0xffff, fmt, ## arg)
diff --git a/bluez/repo/src/main.c b/bluez/repo/src/main.c
new file mode 100644
index 0000000..ebc93f5
--- /dev/null
+++ b/bluez/repo/src/main.c
@@ -0,0 +1,721 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/signalfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
+#include "log.h"
+#include "backtrace.h"
+
+#include "lib/uuid.h"
+#include "hcid.h"
+#include "sdpd.h"
+#include "adapter.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "agent.h"
+#include "profile.h"
+#include "systemd.h"
+
+#define BLUEZ_NAME "org.bluez"
+
+#define DEFAULT_PAIRABLE_TIMEOUT       0 /* disabled */
+#define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
+
+#define SHUTDOWN_GRACE_SECONDS 10
+
+struct main_opts main_opts;
+static GKeyFile *main_conf;
+
+static enum {
+	MPS_OFF,
+	MPS_SINGLE,
+	MPS_MULTIPLE,
+} mps = MPS_OFF;
+
+static const char * const supported_options[] = {
+	"Name",
+	"Class",
+	"DiscoverableTimeout",
+	"AlwaysPairable",
+	"PairableTimeout",
+	"AutoConnectTimeout",
+	"DeviceID",
+	"ReverseServiceDiscovery",
+	"NameResolving",
+	"DebugKeys",
+	"ControllerMode",
+	"MultiProfile",
+};
+
+GKeyFile *btd_get_main_conf(void)
+{
+	return main_conf;
+}
+
+static GKeyFile *load_config(const char *file)
+{
+	GError *err = NULL;
+	GKeyFile *keyfile;
+
+	keyfile = g_key_file_new();
+
+	g_key_file_set_list_separator(keyfile, ',');
+
+	if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+		if (!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			error("Parsing %s failed: %s", file, err->message);
+		g_error_free(err);
+		g_key_file_free(keyfile);
+		return NULL;
+	}
+
+	return keyfile;
+}
+
+static void parse_did(const char *did)
+{
+	int result;
+	uint16_t vendor, product, version , source;
+
+	/* version and source are optional */
+	version = 0x0000;
+	source = 0x0002;
+
+	result = sscanf(did, "bluetooth:%4hx:%4hx:%4hx",
+					&vendor, &product, &version);
+	if (result != EOF && result >= 2) {
+		source = 0x0001;
+		goto done;
+	}
+
+	result = sscanf(did, "usb:%4hx:%4hx:%4hx",
+					&vendor, &product, &version);
+	if (result != EOF && result >= 2)
+		goto done;
+
+	result = sscanf(did, "%4hx:%4hx:%4hx", &vendor, &product, &version);
+	if (result == EOF || result < 2)
+		return;
+
+done:
+	main_opts.did_source = source;
+	main_opts.did_vendor = vendor;
+	main_opts.did_product = product;
+	main_opts.did_version = version;
+}
+
+static void check_config(GKeyFile *config)
+{
+	const char *valid_groups[] = { "General", "Policy", NULL };
+	char **keys;
+	int i;
+
+	if (!config)
+		return;
+
+	keys = g_key_file_get_groups(config, NULL);
+
+	for (i = 0; keys != NULL && keys[i] != NULL; i++) {
+		const char **group;
+		bool match = false;
+
+		for (group = valid_groups; *group; group++) {
+			if (g_str_equal(keys[i], *group)) {
+				match = true;
+				break;
+			}
+		}
+
+		if (!match)
+			warn("Unknown group %s in main.conf", keys[i]);
+	}
+
+	g_strfreev(keys);
+
+	keys = g_key_file_get_keys(config, "General", NULL, NULL);
+
+	for (i = 0; keys != NULL && keys[i] != NULL; i++) {
+		bool found;
+		unsigned int j;
+
+		found = false;
+		for (j = 0; j < G_N_ELEMENTS(supported_options); j++) {
+			if (g_str_equal(keys[i], supported_options[j])) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found)
+			warn("Unknown key %s in main.conf", keys[i]);
+	}
+
+	g_strfreev(keys);
+}
+
+static int get_mode(const char *str)
+{
+	if (strcmp(str, "dual") == 0)
+		return BT_MODE_DUAL;
+	else if (strcmp(str, "bredr") == 0)
+		return BT_MODE_BREDR;
+	else if (strcmp(str, "le") == 0)
+		return BT_MODE_LE;
+
+	error("Unknown controller mode \"%s\"", str);
+
+	return BT_MODE_DUAL;
+}
+
+static void parse_config(GKeyFile *config)
+{
+	GError *err = NULL;
+	char *str;
+	int val;
+	gboolean boolean;
+
+	if (!config)
+		return;
+
+	check_config(config);
+
+	DBG("parsing main.conf");
+
+	val = g_key_file_get_integer(config, "General",
+						"DiscoverableTimeout", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+	} else {
+		DBG("discovto=%d", val);
+		main_opts.discovto = val;
+	}
+
+	val = g_key_file_get_integer(config, "General",
+						"PairableTimeout", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+	} else {
+		DBG("pairto=%d", val);
+		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", "Name", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+	} else {
+		DBG("name=%s", str);
+		g_free(main_opts.name);
+		main_opts.name = str;
+	}
+
+	str = g_key_file_get_string(config, "General", "Class", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+	} else {
+		DBG("class=%s", str);
+		main_opts.class = strtol(str, NULL, 16);
+		g_free(str);
+	}
+
+	str = g_key_file_get_string(config, "General", "DeviceID", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+	} else {
+		DBG("deviceid=%s", str);
+		parse_did(str);
+		g_free(str);
+	}
+
+	boolean = g_key_file_get_boolean(config, "General",
+						"ReverseServiceDiscovery", &err);
+	if (err) {
+		DBG("%s", err->message);
+		g_clear_error(&err);
+	} else
+		main_opts.reverse_sdp = boolean;
+
+	boolean = g_key_file_get_boolean(config, "General",
+						"NameResolving", &err);
+	if (err)
+		g_clear_error(&err);
+	else
+		main_opts.name_resolv = boolean;
+
+	boolean = g_key_file_get_boolean(config, "General",
+						"DebugKeys", &err);
+	if (err)
+		g_clear_error(&err);
+	else
+		main_opts.debug_keys = boolean;
+
+	str = g_key_file_get_string(config, "General", "ControllerMode", &err);
+	if (err) {
+		g_clear_error(&err);
+	} else {
+		DBG("ControllerMode=%s", str);
+		main_opts.mode = get_mode(str);
+		g_free(str);
+	}
+
+	str = g_key_file_get_string(config, "General", "MultiProfile", &err);
+	if (err) {
+		g_clear_error(&err);
+	} else {
+		DBG("MultiProfile=%s", str);
+
+		if (!strcmp(str, "single"))
+			mps = MPS_SINGLE;
+		else if (!strcmp(str, "multiple"))
+			mps = MPS_MULTIPLE;
+
+		g_free(str);
+	}
+
+	boolean = g_key_file_get_boolean(config, "General",
+						"FastConnectable", &err);
+	if (err)
+		g_clear_error(&err);
+	else
+		main_opts.fast_conn = boolean;
+}
+
+static void init_defaults(void)
+{
+	uint8_t major, minor;
+
+	/* Default HCId settings */
+	memset(&main_opts, 0, sizeof(main_opts));
+	main_opts.name = g_strdup_printf("BlueZ %s", VERSION);
+	main_opts.class = 0x000000;
+	main_opts.pairto = DEFAULT_PAIRABLE_TIMEOUT;
+	main_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT;
+	main_opts.reverse_sdp = TRUE;
+	main_opts.name_resolv = TRUE;
+	main_opts.debug_keys = FALSE;
+
+	if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
+		return;
+
+	main_opts.did_source = 0x0002;		/* USB */
+	main_opts.did_vendor = 0x1d6b;		/* Linux Foundation */
+	main_opts.did_product = 0x0246;		/* BlueZ */
+	main_opts.did_version = (major << 8 | minor);
+}
+
+static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
+				const gchar *message, gpointer user_data)
+{
+	int priority;
+
+	if (log_level & (G_LOG_LEVEL_ERROR |
+				G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING))
+		priority = 0x03;
+	else
+		priority = 0x06;
+
+	btd_log(0xffff, priority, "GLib: %s", message);
+	btd_backtrace(0xffff);
+}
+
+static GMainLoop *event_loop;
+
+void btd_exit(void)
+{
+	g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+	btd_exit();
+	return FALSE;
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	static bool terminated = false;
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		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:
+	case SIGTERM:
+		if (!terminated) {
+			info("Terminating");
+			g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+							quit_eventloop, NULL);
+
+			sd_notify(0, "STATUS=Powering down");
+			adapter_shutdown();
+		}
+
+		terminated = true;
+		break;
+	case SIGUSR2:
+		__btd_toggle_debug();
+		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);
+	sigaddset(&mask, SIGUSR2);
+
+	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 char *option_debug = NULL;
+static char *option_plugin = NULL;
+static char *option_noplugin = NULL;
+static gboolean option_compat = FALSE;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+static gboolean option_experimental = FALSE;
+
+static void free_options(void)
+{
+	g_free(option_debug);
+	option_debug = NULL;
+
+	g_free(option_plugin);
+	option_plugin = NULL;
+
+	g_free(option_noplugin);
+	option_noplugin = NULL;
+}
+
+static void disconnect_dbus(void)
+{
+	DBusConnection *conn = btd_get_dbus_connection();
+
+	if (!conn || !dbus_connection_get_is_connected(conn))
+		return;
+
+	g_dbus_detach_object_manager(conn);
+	set_dbus_connection(NULL);
+
+	dbus_connection_unref(conn);
+}
+
+static void disconnected_dbus(DBusConnection *conn, void *data)
+{
+	info("Disconnected from D-Bus. Exiting.");
+	g_main_loop_quit(event_loop);
+}
+
+static int connect_dbus(void)
+{
+	DBusConnection *conn;
+	DBusError err;
+
+	dbus_error_init(&err);
+
+	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);
+	if (!conn) {
+		if (dbus_error_is_set(&err)) {
+			g_printerr("D-Bus setup failed: %s\n", err.message);
+			dbus_error_free(&err);
+			return -EIO;
+		}
+		return -EALREADY;
+	}
+
+	set_dbus_connection(conn);
+
+	g_dbus_set_disconnect_function(conn, disconnected_dbus, NULL, NULL);
+	g_dbus_attach_object_manager(conn);
+
+	return 0;
+}
+
+static gboolean watchdog_callback(gpointer user_data)
+{
+	sd_notify(0, "WATCHDOG=1");
+
+	return TRUE;
+}
+
+static gboolean parse_debug(const char *key, const char *value,
+				gpointer user_data, GError **error)
+{
+	if (value)
+		option_debug = g_strdup(value);
+	else
+		option_debug = g_strdup("*");
+
+	return TRUE;
+}
+
+static GOptionEntry options[] = {
+	{ "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+				G_OPTION_ARG_CALLBACK, parse_debug,
+				"Specify debug options to enable", "DEBUG" },
+	{ "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+				"Specify plugins to load", "NAME,..," },
+	{ "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+				"Specify plugins not to load", "NAME,..." },
+	{ "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat,
+				"Provide deprecated command line interfaces" },
+	{ "experimental", 'E', 0, G_OPTION_ARG_NONE, &option_experimental,
+				"Enable experimental interfaces" },
+	{ "nodetach", 'n', G_OPTION_FLAG_REVERSE,
+				G_OPTION_ARG_NONE, &option_detach,
+				"Run with logging in foreground" },
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit" },
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	uint16_t sdp_mtu = 0;
+	uint32_t sdp_flags = 0;
+	int gdbus_flags = 0;
+	guint signal, watchdog;
+	const char *watchdog_usec;
+
+	init_defaults();
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+		if (err != NULL) {
+			g_printerr("%s\n", err->message);
+			g_error_free(err);
+		} 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);
+	}
+
+	umask(0077);
+
+	btd_backtrace_init();
+
+	event_loop = g_main_loop_new(NULL, FALSE);
+
+	signal = setup_signalfd();
+
+	__btd_log_init(option_debug, option_detach);
+
+	g_log_set_handler("GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
+							G_LOG_FLAG_RECURSION,
+							log_handler, NULL);
+
+	sd_notify(0, "STATUS=Starting up");
+
+	main_conf = load_config(CONFIGDIR "/main.conf");
+
+	parse_config(main_conf);
+
+	if (connect_dbus() < 0) {
+		error("Unable to get on D-Bus");
+		exit(1);
+	}
+
+	if (option_experimental)
+		gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
+
+	g_dbus_set_flags(gdbus_flags);
+
+	if (adapter_init() < 0) {
+		error("Adapter handling initialization failed");
+		exit(1);
+	}
+
+	btd_device_init();
+	btd_agent_init();
+	btd_profile_init();
+
+	if (main_opts.mode != BT_MODE_LE) {
+		if (option_compat == TRUE)
+			sdp_flags |= SDP_SERVER_COMPAT;
+
+		start_sdp_server(sdp_mtu, sdp_flags);
+
+		if (main_opts.did_source > 0)
+			register_device_id(main_opts.did_source,
+						main_opts.did_vendor,
+						main_opts.did_product,
+						main_opts.did_version);
+	}
+
+	if (mps != MPS_OFF)
+		register_mps(mps == MPS_MULTIPLE);
+
+	/* Loading plugins has to be done after D-Bus has been setup since
+	 * the plugins might wanna expose some paths on the bus. However the
+	 * best order of how to init various subsystems of the Bluetooth
+	 * daemon needs to be re-worked. */
+	plugin_init(option_plugin, option_noplugin);
+
+	/* no need to keep parsed option in memory */
+	free_options();
+
+	rfkill_init();
+
+	DBG("Entering main loop");
+
+	sd_notify(0, "STATUS=Running");
+	sd_notify(0, "READY=1");
+
+	watchdog_usec = getenv("WATCHDOG_USEC");
+	if (watchdog_usec) {
+		unsigned int seconds;
+
+		seconds = atoi(watchdog_usec) / (1000 * 1000);
+		info("Watchdog timeout is %d seconds", seconds);
+
+		watchdog = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+							seconds / 2,
+							watchdog_callback,
+							NULL, NULL);
+	} else
+		watchdog = 0;
+
+	g_main_loop_run(event_loop);
+
+	sd_notify(0, "STATUS=Quitting");
+
+	g_source_remove(signal);
+
+	plugin_cleanup();
+
+	btd_profile_cleanup();
+	btd_agent_cleanup();
+	btd_device_cleanup();
+
+	adapter_cleanup();
+
+	rfkill_exit();
+
+	if (main_opts.mode != BT_MODE_LE)
+		stop_sdp_server();
+
+	g_main_loop_unref(event_loop);
+
+	if (main_conf)
+		g_key_file_free(main_conf);
+
+	disconnect_dbus();
+
+	info("Exit");
+
+	if (watchdog > 0)
+		g_source_remove(watchdog);
+
+	__btd_log_cleanup();
+
+	return 0;
+}
diff --git a/bluez/repo/src/main.conf b/bluez/repo/src/main.conf
new file mode 100644
index 0000000..372fd8c
--- /dev/null
+++ b/bluez/repo/src/main.conf
@@ -0,0 +1,89 @@
+[General]
+
+# Default adaper name
+# Defaults to 'BlueZ X.YZ'
+#Name = BlueZ
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#Class = 0x000100
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb, 0000111f-0000-1000-8000-00805f9b34fb, 0000110a-0000-1000-8000-00805f9b34fb
+
+# ReconnectAttempts define the number of attempts to reconnect after a link
+# lost. Setting the value to 0 disables reconnecting feature.
+#ReconnectAttempts=7
+
+# ReconnectIntervals define the set of intervals in seconds to use in between
+# attempts.
+# If the number of attempts defined in ReconnectAttempts is bigger than the
+# set of intervals the last interval is repeated until the last attempt.
+#ReconnectIntervals=1, 2, 4, 8, 16, 32, 64
+
+# AutoEnable defines option to enable all controllers when they are found.
+# This includes adapters present on start as well as adapters that are plugged
+# in later on. Defaults to 'false'.
+#AutoEnable=false
diff --git a/bluez/repo/src/org.bluez.service b/bluez/repo/src/org.bluez.service
new file mode 100644
index 0000000..dd7ae8f
--- /dev/null
+++ b/bluez/repo/src/org.bluez.service
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=org.bluez
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.bluez.service
diff --git a/bluez/repo/src/oui.c b/bluez/repo/src/oui.c
new file mode 100644
index 0000000..5fe2c5c
--- /dev/null
+++ b/bluez/repo/src/oui.c
@@ -0,0 +1,74 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 "lib/bluetooth.h"
+#include "oui.h"
+
+#ifdef HAVE_UDEV_HWDB_NEW
+#include <libudev.h>
+
+char *batocomp(const bdaddr_t *ba)
+{
+	struct udev *udev;
+	struct udev_hwdb *hwdb;
+	struct udev_list_entry *head, *entry;
+	char modalias[11], *comp = NULL;
+
+	sprintf(modalias, "OUI:%2.2X%2.2X%2.2X", ba->b[5], ba->b[4], ba->b[3]);
+
+	udev = udev_new();
+	if (!udev)
+		return NULL;
+
+	hwdb = udev_hwdb_new(udev);
+	if (!hwdb)
+		goto done;
+
+	head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0);
+
+	udev_list_entry_foreach(entry, head) {
+		const char *name = udev_list_entry_get_name(entry);
+
+		if (name && !strcmp(name, "ID_OUI_FROM_DATABASE")) {
+			comp = strdup(udev_list_entry_get_value(entry));
+			break;
+		}
+	}
+
+	hwdb = udev_hwdb_unref(hwdb);
+
+done:
+	udev = udev_unref(udev);
+
+	return comp;
+}
+#else
+char *batocomp(const bdaddr_t *ba)
+{
+	return NULL;
+}
+#endif
diff --git a/bluez/repo/src/oui.h b/bluez/repo/src/oui.h
new file mode 100644
index 0000000..2ddc27f
--- /dev/null
+++ b/bluez/repo/src/oui.h
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+char *batocomp(const bdaddr_t *ba);
diff --git a/bluez/repo/src/plugin.c b/bluez/repo/src/plugin.c
new file mode 100644
index 0000000..39310a7
--- /dev/null
+++ b/bluez/repo/src/plugin.c
@@ -0,0 +1,239 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <errno.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+
+#include "btio/btio.h"
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/hcid.h"
+
+static GSList *plugins = NULL;
+
+struct bluetooth_plugin {
+	void *handle;
+	gboolean active;
+	struct bluetooth_plugin_desc *desc;
+};
+
+static int compare_priority(gconstpointer a, gconstpointer b)
+{
+	const struct bluetooth_plugin *plugin1 = a;
+	const struct bluetooth_plugin *plugin2 = b;
+
+	return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct bluetooth_plugin_desc *desc)
+{
+	struct bluetooth_plugin *plugin;
+
+	if (desc->init == NULL)
+		return FALSE;
+
+	if (g_str_equal(desc->version, VERSION) == FALSE) {
+		error("Version mismatch for %s", desc->name);
+		return FALSE;
+	}
+
+	DBG("Loading %s plugin", desc->name);
+
+	plugin = g_try_new0(struct bluetooth_plugin, 1);
+	if (plugin == NULL)
+		return FALSE;
+
+	plugin->handle = handle;
+	plugin->active = FALSE;
+	plugin->desc = desc;
+
+	__btd_enable_debug(desc->debug_start, desc->debug_stop);
+
+	plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+	return TRUE;
+}
+
+static gboolean enable_plugin(const char *name, char **cli_enable,
+							char **cli_disable)
+{
+	if (cli_disable) {
+		for (; *cli_disable; cli_disable++)
+			if (g_pattern_match_simple(*cli_disable, name))
+				break;
+		if (*cli_disable) {
+			info("Excluding (cli) %s", name);
+			return FALSE;
+		}
+	}
+
+	if (cli_enable) {
+		for (; *cli_enable; cli_enable++)
+			if (g_pattern_match_simple(*cli_enable, name))
+				break;
+		if (!*cli_enable) {
+			info("Ignoring (cli) %s", name);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+#include "src/builtin.h"
+
+gboolean plugin_init(const char *enable, const char *disable)
+{
+	GSList *list;
+	GDir *dir;
+	const char *file;
+	char **cli_disabled, **cli_enabled;
+	unsigned int i;
+
+	/* Make a call to BtIO API so its symbols got resolved before the
+	 * plugins are loaded. */
+	bt_io_error_quark();
+
+	if (enable)
+		cli_enabled = g_strsplit_set(enable, ", ", -1);
+	else
+		cli_enabled = NULL;
+
+	if (disable)
+		cli_disabled = g_strsplit_set(disable, ", ", -1);
+	else
+		cli_disabled = NULL;
+
+	DBG("Loading builtin plugins");
+
+	for (i = 0; __bluetooth_builtin[i]; i++) {
+		if (!enable_plugin(__bluetooth_builtin[i]->name, cli_enabled,
+								cli_disabled))
+			continue;
+
+		add_plugin(NULL,  __bluetooth_builtin[i]);
+	}
+
+	if (strlen(PLUGINDIR) == 0)
+		goto start;
+
+	DBG("Loading plugins %s", PLUGINDIR);
+
+	dir = g_dir_open(PLUGINDIR, 0, NULL);
+	if (!dir)
+		goto start;
+
+	while ((file = g_dir_read_name(dir)) != NULL) {
+		struct bluetooth_plugin_desc *desc;
+		void *handle;
+		char *filename;
+
+		if (g_str_has_prefix(file, "lib") == TRUE ||
+				g_str_has_suffix(file, ".so") == FALSE)
+			continue;
+
+		filename = g_build_filename(PLUGINDIR, file, NULL);
+
+		handle = dlopen(filename, RTLD_NOW);
+		if (handle == NULL) {
+			error("Can't load plugin %s: %s", filename,
+								dlerror());
+			g_free(filename);
+			continue;
+		}
+
+		g_free(filename);
+
+		desc = dlsym(handle, "bluetooth_plugin_desc");
+		if (desc == NULL) {
+			error("Can't load plugin description: %s", dlerror());
+			dlclose(handle);
+			continue;
+		}
+
+		if (!enable_plugin(desc->name, cli_enabled, cli_disabled)) {
+			dlclose(handle);
+			continue;
+		}
+
+		if (add_plugin(handle, desc) == FALSE)
+			dlclose(handle);
+	}
+
+	g_dir_close(dir);
+
+start:
+	for (list = plugins; list; list = list->next) {
+		struct bluetooth_plugin *plugin = list->data;
+		int err;
+
+		err = plugin->desc->init();
+		if (err < 0) {
+			if (err == -ENOSYS)
+				warn("System does not support %s plugin",
+							plugin->desc->name);
+			else
+				error("Failed to init %s plugin",
+							plugin->desc->name);
+			continue;
+		}
+
+		plugin->active = TRUE;
+	}
+
+	g_strfreev(cli_enabled);
+	g_strfreev(cli_disabled);
+
+	return TRUE;
+}
+
+void plugin_cleanup(void)
+{
+	GSList *list;
+
+	DBG("Cleanup plugins");
+
+	for (list = plugins; list; list = list->next) {
+		struct bluetooth_plugin *plugin = list->data;
+
+		if (plugin->active == TRUE && plugin->desc->exit)
+			plugin->desc->exit();
+
+		if (plugin->handle != NULL)
+			dlclose(plugin->handle);
+
+		g_free(plugin);
+	}
+
+	g_slist_free(plugins);
+}
diff --git a/bluez/repo/src/plugin.h b/bluez/repo/src/plugin.h
new file mode 100644
index 0000000..89c7b85
--- /dev/null
+++ b/bluez/repo/src/plugin.h
@@ -0,0 +1,54 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+#define BLUETOOTH_PLUGIN_PRIORITY_LOW      -100
+#define BLUETOOTH_PLUGIN_PRIORITY_DEFAULT     0
+#define BLUETOOTH_PLUGIN_PRIORITY_HIGH      100
+
+struct bluetooth_plugin_desc {
+	const char *name;
+	const char *version;
+	int priority;
+	int (*init) (void);
+	void (*exit) (void);
+	void *debug_start;
+	void *debug_stop;
+};
+
+#ifdef BLUETOOTH_PLUGIN_BUILTIN
+#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+		struct bluetooth_plugin_desc __bluetooth_builtin_ ## name = { \
+			#name, version, priority, init, exit \
+		};
+#else
+#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+		extern struct btd_debug_desc __start___debug[] \
+				__attribute__ ((weak, visibility("hidden"))); \
+		extern struct btd_debug_desc __stop___debug[] \
+				__attribute__ ((weak, visibility("hidden"))); \
+		extern struct bluetooth_plugin_desc bluetooth_plugin_desc \
+				__attribute__ ((visibility("default"))); \
+		struct bluetooth_plugin_desc bluetooth_plugin_desc = { \
+			#name, version, priority, init, exit, \
+			__start___debug, __stop___debug \
+		};
+#endif
diff --git a/bluez/repo/src/profile.c b/bluez/repo/src/profile.c
new file mode 100644
index 0000000..5a4f09c
--- /dev/null
+++ b/bluez/repo/src/profile.c
@@ -0,0 +1,2527 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "sdpd.h"
+#include "log.h"
+#include "error.h"
+#include "uuid-helper.h"
+#include "dbus-common.h"
+#include "sdp-client.h"
+#include "sdp-xml.h"
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+#include "service.h"
+
+#define DUN_DEFAULT_CHANNEL	1
+#define SPP_DEFAULT_CHANNEL	3
+#define HFP_HF_DEFAULT_CHANNEL	7
+#define OPP_DEFAULT_CHANNEL	9
+#define FTP_DEFAULT_CHANNEL	10
+#define BIP_DEFAULT_CHANNEL	11
+#define HSP_AG_DEFAULT_CHANNEL	12
+#define HFP_AG_DEFAULT_CHANNEL	13
+#define SYNC_DEFAULT_CHANNEL	14
+#define PBAP_DEFAULT_CHANNEL	15
+#define MAS_DEFAULT_CHANNEL	16
+#define MNS_DEFAULT_CHANNEL	17
+
+#define BTD_PROFILE_PSM_AUTO	-1
+#define BTD_PROFILE_CHAN_AUTO	-1
+
+#define HFP_HF_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x111e\" />		\
+				<uuid value=\"0x1203\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x111e\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+		<attribute id=\"0x0311\">				\
+			<uint16 value=\"0x%04x\" />			\
+		</attribute>						\
+	</record>"
+
+#define HFP_AG_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x111f\" />		\
+				<uuid value=\"0x1203\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x111e\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+		<attribute id=\"0x0311\">				\
+			<uint16 value=\"0x%04x\" />			\
+		</attribute>						\
+		<attribute id=\"0x0301\" >				\
+			<uint8 value=\"0x01\" />			\
+		</attribute>						\
+	</record>"
+
+#define HSP_AG_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1112\" />		\
+				<uuid value=\"0x1203\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1108\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+#define SPP_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1101\" />		\
+				%s					\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1101\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+#define DUN_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1103\" />		\
+				<uuid value=\"0x1201\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1103\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+#define OPP_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1105\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1105\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0303\">				\
+			<sequence>					\
+				<uint8 value=\"0x01\"/>			\
+				<uint8 value=\"0x02\"/>			\
+				<uint8 value=\"0x03\"/>			\
+				<uint8 value=\"0x04\"/>			\
+				<uint8 value=\"0x05\"/>			\
+				<uint8 value=\"0x06\"/>			\
+				<uint8 value=\"0xff\"/>			\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+#define FTP_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1106\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1106\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+#define PCE_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x112e\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1130\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+#define PSE_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x112f\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\" />	\
+					<uint8 value=\"0x%02x\" />	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1130\" />	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+		<attribute id=\"0x0314\">				\
+			<uint8 value=\"0x01\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0317\">				\
+			<uint32 value=\"0x00000003\"/>			\
+		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+	</record>"
+
+#define MAS_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1132\"/>		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\"/>	\
+					<uint8 value=\"0x%02x\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1134\"/>	\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0315\">				\
+			<uint8 value=\"0x00\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0316\">				\
+			<uint8 value=\"0x0F\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0317\">				\
+			<uint32 value=\"0x0000007f\"/>			\
+		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+	</record>"
+
+#define MNS_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1133\"/>		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\"/>	\
+					<uint8 value=\"0x%02x\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1134\"/>	\
+					<uint16 value=\"0x%04x\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0317\">				\
+			<uint32 value=\"0x0000007f\"/>			\
+		</attribute>						\
+		<attribute id=\"0x0200\">				\
+			<uint16 value=\"%u\" name=\"psm\"/>		\
+		</attribute>						\
+	</record>"
+
+#define SYNC_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"0x1104\"/>		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0003\"/>	\
+					<uint8 value=\"0x%02x\"/>	\
+				</sequence>				\
+				<sequence>				\
+					<uuid value=\"0x0008\"/>	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x1104\"/>	\
+					<uint16 value=\"0x%04x\" />	\
+				 </sequence>				\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\"/>				\
+		</attribute>						\
+		<attribute id=\"0x0301\">				\
+			<sequence>					\
+				<uint8 value=\"0x01\"/>			\
+			</sequence>					\
+		</attribute>						\
+	</record>"
+
+#define GENERIC_RECORD							\
+	"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>			\
+	<record>							\
+		<attribute id=\"0x0001\">				\
+			<sequence>					\
+				<uuid value=\"%s\" />			\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0004\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"0x0100\" />	\
+					%s				\
+				</sequence>				\
+				%s					\
+			</sequence>					\
+		</attribute>						\
+		<attribute id=\"0x0005\">				\
+			<sequence>					\
+				<uuid value=\"0x1002\" />		\
+			</sequence>					\
+		</attribute>						\
+		%s							\
+		<attribute id=\"0x0100\">				\
+			<text value=\"%s\" />				\
+		</attribute>						\
+	</record>"
+
+struct ext_io;
+
+struct ext_profile {
+	struct btd_profile p;
+
+	char *name;
+	char *owner;
+	char *path;
+	char *uuid;
+	char *service;
+	char *role;
+
+	char *record;
+	char *(*get_record)(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm);
+
+	char *remote_uuid;
+
+	guint id;
+
+	BtIOMode mode;
+	BtIOSecLevel sec_level;
+	bool authorize;
+
+	bool enable_client;
+	bool enable_server;
+
+	int local_psm;
+	int local_chan;
+
+	uint16_t remote_psm;
+	uint8_t remote_chan;
+
+	uint16_t version;
+	uint16_t features;
+
+	GSList *records;
+	GSList *servers;
+	GSList *conns;
+
+	GSList *connects;
+};
+
+struct ext_io {
+	struct ext_profile *ext;
+	int proto;
+	GIOChannel *io;
+	guint io_id;
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+	struct btd_service *service;
+
+	bool resolving;
+	bool connected;
+
+	uint16_t version;
+	uint16_t features;
+
+	uint16_t psm;
+	uint8_t chan;
+
+	guint auth_id;
+	DBusPendingCall *pending;
+};
+
+struct ext_record {
+	struct btd_adapter *adapter;
+	uint32_t handle;
+};
+
+struct btd_profile_custom_property {
+	char *uuid;
+	char *type;
+	char *name;
+	btd_profile_prop_exists exists;
+	btd_profile_prop_get get;
+	void *user_data;
+};
+
+static GSList *custom_props = NULL;
+
+static GSList *profiles = NULL;
+static GSList *ext_profiles = NULL;
+
+void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
+								void *data)
+{
+	GSList *l, *next;
+
+	for (l = profiles; l != NULL; l = next) {
+		struct btd_profile *profile = l->data;
+
+		next = g_slist_next(l);
+
+		func(profile, data);
+	}
+
+	for (l = ext_profiles; l != NULL; l = next) {
+		struct ext_profile *profile = l->data;
+
+		next = g_slist_next(l);
+
+		func(&profile->p, data);
+	}
+}
+
+int btd_profile_register(struct btd_profile *profile)
+{
+	profiles = g_slist_append(profiles, profile);
+	return 0;
+}
+
+void btd_profile_unregister(struct btd_profile *profile)
+{
+	profiles = g_slist_remove(profiles, profile);
+}
+
+static struct ext_profile *find_ext_profile(const char *owner,
+						const char *path)
+{
+	GSList *l;
+
+	for (l = ext_profiles; l != NULL; l = g_slist_next(l)) {
+		struct ext_profile *ext = l->data;
+
+		if (g_strcmp0(ext->owner, owner))
+			continue;
+
+		if (!g_strcmp0(ext->path, path))
+			return ext;
+	}
+
+	return NULL;
+}
+
+static void ext_io_destroy(gpointer p)
+{
+	struct ext_io *ext_io = p;
+
+	if (ext_io->io_id > 0)
+		g_source_remove(ext_io->io_id);
+
+	if (ext_io->io) {
+		g_io_channel_shutdown(ext_io->io, FALSE, NULL);
+		g_io_channel_unref(ext_io->io);
+	}
+
+	if (ext_io->auth_id != 0)
+		btd_cancel_authorization(ext_io->auth_id);
+
+	if (ext_io->pending) {
+		dbus_pending_call_cancel(ext_io->pending);
+		dbus_pending_call_unref(ext_io->pending);
+	}
+
+	if (ext_io->resolving)
+		bt_cancel_discovery(btd_adapter_get_address(ext_io->adapter),
+					device_get_address(ext_io->device));
+
+	if (ext_io->adapter)
+		btd_adapter_unref(ext_io->adapter);
+
+	if (ext_io->device)
+		btd_device_unref(ext_io->device);
+
+	if (ext_io->service)
+		btd_service_unref(ext_io->service);
+
+	g_free(ext_io);
+}
+
+static gboolean ext_io_disconnected(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct ext_io *conn = user_data;
+	struct ext_profile *ext = conn->ext;
+	GError *gerr = NULL;
+	char addr[18];
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	bt_io_get(io, &gerr, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID);
+	if (gerr != NULL) {
+		error("Unable to get io data for %s: %s",
+						ext->name, gerr->message);
+		g_error_free(gerr);
+		goto drop;
+	}
+
+	DBG("%s disconnected from %s", ext->name, addr);
+drop:
+	if (conn->service) {
+		if (btd_service_get_state(conn->service) ==
+						BTD_SERVICE_STATE_CONNECTING)
+			btd_service_connecting_complete(conn->service, -EIO);
+		else
+			btd_service_disconnecting_complete(conn->service, 0);
+	}
+
+	ext->conns = g_slist_remove(ext->conns, conn);
+	ext_io_destroy(conn);
+	return FALSE;
+}
+
+static void new_conn_reply(DBusPendingCall *call, void *user_data)
+{
+	struct ext_io *conn = user_data;
+	struct ext_profile *ext = conn->ext;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError err;
+
+	dbus_error_init(&err);
+	dbus_set_error_from_message(&err, reply);
+
+	dbus_message_unref(reply);
+
+	dbus_pending_call_unref(conn->pending);
+	conn->pending = NULL;
+
+	if (!dbus_error_is_set(&err)) {
+		if (conn->service)
+			btd_service_connecting_complete(conn->service, 0);
+
+		conn->connected = true;
+		return;
+	}
+
+	error("%s replied with an error: %s, %s", ext->name,
+						err.name, err.message);
+
+	if (conn->service)
+		btd_service_connecting_complete(conn->service, -ECONNREFUSED);
+
+	dbus_error_free(&err);
+
+	ext->conns = g_slist_remove(ext->conns, conn);
+	ext_io_destroy(conn);
+}
+
+static void disconn_reply(DBusPendingCall *call, void *user_data)
+{
+	struct ext_io *conn = user_data;
+	struct ext_profile *ext = conn->ext;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError err;
+
+	dbus_error_init(&err);
+	dbus_set_error_from_message(&err, reply);
+
+	dbus_message_unref(reply);
+
+	dbus_pending_call_unref(conn->pending);
+	conn->pending = NULL;
+
+	if (!dbus_error_is_set(&err)) {
+		if (conn->service)
+			btd_service_disconnecting_complete(conn->service, 0);
+
+		goto disconnect;
+	}
+
+	error("%s replied with an error: %s, %s", ext->name,
+						err.name, err.message);
+
+	if (conn->service)
+		btd_service_disconnecting_complete(conn->service,
+								-ECONNREFUSED);
+
+	dbus_error_free(&err);
+
+disconnect:
+	ext->conns = g_slist_remove(ext->conns, conn);
+	ext_io_destroy(conn);
+}
+
+struct prop_append_data {
+	DBusMessageIter *dict;
+	struct ext_io *io;
+};
+
+static void append_prop(gpointer a, gpointer b)
+{
+	struct btd_profile_custom_property *p = a;
+	struct prop_append_data *data = b;
+	DBusMessageIter entry, value, *dict = data->dict;
+	struct btd_device *dev = data->io->device;
+	struct ext_profile *ext = data->io->ext;
+	const char *uuid = ext->service ? ext->service : ext->uuid;
+
+	if (strcasecmp(p->uuid, uuid) != 0)
+		return;
+
+	if (p->exists && !p->exists(p->uuid, dev, p->user_data))
+		return;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
+								&entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type,
+								&value);
+
+	p->get(p->uuid, dev, &value, p->user_data);
+
+	dbus_message_iter_close_container(&entry, &value);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static uint16_t get_supported_features(const sdp_record_t *rec)
+{
+	sdp_data_t *data;
+
+	data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES);
+	if (!data || data->dtd != SDP_UINT16)
+		return 0;
+
+	return data->val.uint16;
+}
+
+static uint16_t get_profile_version(const sdp_record_t *rec)
+{
+	sdp_list_t *descs;
+	uint16_t version;
+
+	if (sdp_get_profile_descs(rec, &descs) < 0)
+		return 0;
+
+	if (descs && descs->data) {
+		sdp_profile_desc_t *desc = descs->data;
+		version = desc->version;
+	} else {
+		version = 0;
+	}
+
+	sdp_list_free(descs, free);
+
+	return version;
+}
+
+static bool send_new_connection(struct ext_profile *ext, struct ext_io *conn)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict;
+	struct prop_append_data data = { &dict, conn };
+	const char *remote_uuid = ext->remote_uuid;
+	const sdp_record_t *rec;
+	const char *path;
+	int fd;
+
+	msg = dbus_message_new_method_call(ext->owner, ext->path,
+							"org.bluez.Profile1",
+							"NewConnection");
+	if (!msg) {
+		error("Unable to create NewConnection call for %s", ext->name);
+		return false;
+	}
+
+	if (remote_uuid) {
+		rec = btd_device_get_record(conn->device, remote_uuid);
+		if (rec) {
+			conn->features = get_supported_features(rec);
+			conn->version = get_profile_version(rec);
+		}
+	}
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	path = device_get_path(conn->device);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	fd = g_io_channel_unix_get_fd(conn->io);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+	if (conn->version)
+		dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16,
+							&conn->version);
+
+	if (conn->features)
+		dict_append_entry(&dict, "Features", DBUS_TYPE_UINT16,
+							&conn->features);
+
+	g_slist_foreach(custom_props, append_prop, &data);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	if (!g_dbus_send_message_with_reply(btd_get_dbus_connection(),
+						msg, &conn->pending, -1)) {
+		error("%s: sending NewConnection failed", ext->name);
+		dbus_message_unref(msg);
+		return false;
+	}
+
+	dbus_message_unref(msg);
+
+	dbus_pending_call_set_notify(conn->pending, new_conn_reply, conn, NULL);
+
+	return true;
+}
+
+static void ext_connect(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct ext_io *conn = user_data;
+	struct ext_profile *ext = conn->ext;
+	GError *io_err = NULL;
+	char addr[18];
+
+	if (!bt_io_get(io, &io_err,
+				BT_IO_OPT_DEST, addr,
+				BT_IO_OPT_INVALID)) {
+		error("Unable to get connect data for %s: %s", ext->name,
+							io_err->message);
+		if (err) {
+			g_error_free(io_err);
+			io_err = NULL;
+		} else {
+			err = io_err;
+		}
+		goto drop;
+	}
+
+	if (err != NULL) {
+		error("%s failed to connect to %s: %s", ext->name, addr,
+								err->message);
+		goto drop;
+	}
+
+	DBG("%s connected to %s", ext->name, addr);
+
+	if (conn->io_id == 0) {
+		GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+		conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected,
+									conn);
+	}
+
+	if (conn->service && service_accept(conn->service) == 0) {
+		if (send_new_connection(ext, conn))
+			return;
+	}
+
+drop:
+	if (conn->service)
+		btd_service_connecting_complete(conn->service,
+						err ? -err->code : -EIO);
+
+	if (io_err)
+		g_error_free(io_err);
+
+	ext->conns = g_slist_remove(ext->conns, conn);
+	ext_io_destroy(conn);
+}
+
+static void ext_auth(DBusError *err, void *user_data)
+{
+	struct ext_io *conn = user_data;
+	struct ext_profile *ext = conn->ext;
+	GError *gerr = NULL;
+	char addr[18];
+
+	conn->auth_id = 0;
+
+	bt_io_get(conn->io, &gerr, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID);
+	if (gerr != NULL) {
+		error("Unable to get connect data for %s: %s",
+						ext->name, gerr->message);
+		g_error_free(gerr);
+		goto drop;
+	}
+
+	if (err && dbus_error_is_set(err)) {
+		error("%s rejected %s: %s", ext->name, addr, err->message);
+		goto drop;
+	}
+
+	if (!bt_io_accept(conn->io, ext_connect, conn, NULL, &gerr)) {
+		error("bt_io_accept: %s", gerr->message);
+		g_error_free(gerr);
+		goto drop;
+	}
+
+	DBG("%s authorized to connect to %s", addr, ext->name);
+
+	return;
+
+drop:
+	ext->conns = g_slist_remove(ext->conns, conn);
+	ext_io_destroy(conn);
+}
+
+static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io,
+						bdaddr_t *src, bdaddr_t *dst)
+{
+	struct btd_device *device;
+	struct btd_service *service;
+	struct ext_io *conn;
+	GIOCondition cond;
+	char addr[18];
+
+	device = btd_adapter_find_device(server->adapter, dst, BDADDR_BREDR);
+	if (device == NULL) {
+		ba2str(dst, addr);
+		error("%s device %s not found", server->ext->name, addr);
+		return NULL;
+	}
+
+	/* Do not add UUID if client role is not enabled */
+	if (!server->ext->enable_client) {
+		service = NULL;
+		goto done;
+	}
+
+	btd_device_add_uuid(device, server->ext->remote_uuid);
+	service = btd_device_get_service(device, server->ext->remote_uuid);
+	if (service == NULL) {
+		ba2str(dst, addr);
+		error("%s service not found for device %s", server->ext->name,
+									addr);
+		return NULL;
+	}
+
+done:
+	conn = g_new0(struct ext_io, 1);
+	conn->io = g_io_channel_ref(io);
+	conn->proto = server->proto;
+	conn->ext = server->ext;
+	conn->adapter = btd_adapter_ref(server->adapter);
+	conn->device = btd_device_ref(device);
+
+	if (service)
+		conn->service = btd_service_ref(service);
+
+	cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected, conn);
+
+	return conn;
+}
+
+static void ext_confirm(GIOChannel *io, gpointer user_data)
+{
+	struct ext_io *server = user_data;
+	struct ext_profile *ext = server->ext;
+	const char *uuid = ext->service ? ext->service : ext->uuid;
+	struct ext_io *conn;
+	GError *gerr = NULL;
+	bdaddr_t src, dst;
+	char addr[18];
+
+	bt_io_get(io, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, addr,
+			BT_IO_OPT_INVALID);
+	if (gerr != NULL) {
+		error("%s failed to get connect data: %s", ext->name,
+								gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	DBG("incoming connect from %s", addr);
+
+	conn = create_conn(server, io, &src, &dst);
+	if (conn == NULL)
+		return;
+
+	conn->auth_id = btd_request_authorization(&src, &dst, uuid, ext_auth,
+									conn);
+	if (conn->auth_id == 0) {
+		error("%s authorization failure", ext->name);
+		ext_io_destroy(conn);
+		return;
+	}
+
+	ext->conns = g_slist_append(ext->conns, conn);
+
+	DBG("%s authorizing connection from %s", ext->name, addr);
+}
+
+static void ext_direct_connect(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct ext_io *server = user_data;
+	struct ext_profile *ext = server->ext;
+	GError *gerr = NULL;
+	struct ext_io *conn;
+	bdaddr_t src, dst;
+
+	bt_io_get(io, &gerr,
+			BT_IO_OPT_SOURCE_BDADDR, &src,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (gerr != NULL) {
+		error("%s failed to get connect data: %s", ext->name,
+								gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	conn = create_conn(server, io, &src, &dst);
+	if (conn == NULL)
+		return;
+
+	ext->conns = g_slist_append(ext->conns, conn);
+
+	ext_connect(io, err, conn);
+}
+
+static uint32_t ext_register_record(struct ext_profile *ext,
+							struct ext_io *l2cap,
+							struct ext_io *rfcomm,
+							struct btd_adapter *a)
+{
+	sdp_record_t *rec;
+	char *dyn_record = NULL;
+	const char *record = ext->record;
+
+	if (!record && ext->get_record) {
+		dyn_record = ext->get_record(ext, l2cap, rfcomm);
+		record = dyn_record;
+	}
+
+	if (!record)
+		return 0;
+
+	rec = sdp_xml_parse_record(record, strlen(record));
+
+	g_free(dyn_record);
+
+	if (!rec) {
+		error("Unable to parse record for %s", ext->name);
+		return 0;
+	}
+
+	if (adapter_service_add(a, rec) < 0) {
+		error("Failed to register service record");
+		sdp_record_free(rec);
+		return 0;
+	}
+
+	return rec->handle;
+}
+
+static uint32_t ext_start_servers(struct ext_profile *ext,
+						struct btd_adapter *adapter)
+{
+	struct ext_io *l2cap = NULL;
+	struct ext_io *rfcomm = NULL;
+	BtIOConfirm confirm;
+	BtIOConnect connect;
+	GError *err = NULL;
+	GIOChannel *io;
+
+	if (ext->authorize) {
+		confirm = ext_confirm;
+		connect = NULL;
+	} else {
+		confirm = NULL;
+		connect = ext_direct_connect;
+	}
+
+	if (ext->local_psm) {
+		uint16_t psm;
+
+		if (ext->local_psm > 0)
+			psm = ext->local_psm;
+		else
+			psm = 0;
+
+		l2cap = g_new0(struct ext_io, 1);
+		l2cap->ext = ext;
+
+		io = bt_io_listen(connect, confirm, l2cap, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR,
+					btd_adapter_get_address(adapter),
+					BT_IO_OPT_MODE, ext->mode,
+					BT_IO_OPT_PSM, psm,
+					BT_IO_OPT_SEC_LEVEL, ext->sec_level,
+					BT_IO_OPT_INVALID);
+		if (err != NULL) {
+			error("L2CAP server failed for %s: %s",
+						ext->name, err->message);
+			g_free(l2cap);
+			l2cap = NULL;
+			g_clear_error(&err);
+			goto failed;
+		} else {
+			if (psm == 0)
+				bt_io_get(io, NULL, BT_IO_OPT_PSM, &psm,
+							BT_IO_OPT_INVALID);
+			l2cap->io = io;
+			l2cap->proto = BTPROTO_L2CAP;
+			l2cap->psm = psm;
+			l2cap->adapter = btd_adapter_ref(adapter);
+			ext->servers = g_slist_append(ext->servers, l2cap);
+			DBG("%s listening on PSM %u", ext->name, psm);
+		}
+	}
+
+	if (ext->local_chan) {
+		uint8_t chan;
+
+		if (ext->local_chan > 0)
+			chan = ext->local_chan;
+		else
+			chan = 0;
+
+		rfcomm = g_new0(struct ext_io, 1);
+		rfcomm->ext = ext;
+
+		io = bt_io_listen(connect, confirm, rfcomm, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR,
+					btd_adapter_get_address(adapter),
+					BT_IO_OPT_CHANNEL, chan,
+					BT_IO_OPT_SEC_LEVEL, ext->sec_level,
+					BT_IO_OPT_INVALID);
+		if (err != NULL) {
+			error("RFCOMM server failed for %s: %s",
+						ext->name, err->message);
+			g_free(rfcomm);
+			g_clear_error(&err);
+			goto failed;
+		} else {
+			if (chan == 0)
+				bt_io_get(io, NULL, BT_IO_OPT_CHANNEL, &chan,
+							BT_IO_OPT_INVALID);
+			rfcomm->io = io;
+			rfcomm->proto = BTPROTO_RFCOMM;
+			rfcomm->chan = chan;
+			rfcomm->adapter = btd_adapter_ref(adapter);
+			ext->servers = g_slist_append(ext->servers, rfcomm);
+			DBG("%s listening on chan %u", ext->name, chan);
+		}
+	}
+
+	return ext_register_record(ext, l2cap, rfcomm, adapter);
+
+failed:
+	if (l2cap) {
+		ext->servers = g_slist_remove(ext->servers, l2cap);
+		ext_io_destroy(l2cap);
+	}
+
+	return 0;
+}
+
+static struct ext_profile *find_ext(struct btd_profile *p)
+{
+	GSList *l;
+
+	l = g_slist_find(ext_profiles, p);
+	if (!l)
+		return NULL;
+
+	return l->data;
+}
+
+static int ext_adapter_probe(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct ext_profile *ext;
+	struct ext_record *rec;
+	uint32_t handle;
+
+	ext = find_ext(p);
+	if (!ext)
+		return -ENOENT;
+
+	DBG("\"%s\" probed", ext->name);
+
+	handle = ext_start_servers(ext, adapter);
+	if (!handle)
+		return 0;
+
+	rec = g_new0(struct ext_record, 1);
+	rec->adapter = btd_adapter_ref(adapter);
+	rec->handle = handle;
+
+	ext->records = g_slist_append(ext->records, rec);
+
+	return 0;
+}
+
+static void ext_remove_records(struct ext_profile *ext,
+						struct btd_adapter *adapter)
+{
+	GSList *l, *next;
+
+	for (l = ext->records; l != NULL; l = next) {
+		struct ext_record *r = l->data;
+
+		next = g_slist_next(l);
+
+		if (adapter && r->adapter != adapter)
+			continue;
+
+		ext->records = g_slist_remove(ext->records, r);
+
+		adapter_service_remove(adapter, r->handle);
+		btd_adapter_unref(r->adapter);
+		g_free(r);
+	}
+}
+
+static void ext_adapter_remove(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	struct ext_profile *ext;
+	GSList *l, *next;
+
+	ext = find_ext(p);
+	if (!ext)
+		return;
+
+	DBG("\"%s\" removed", ext->name);
+
+	ext_remove_records(ext, adapter);
+
+	for (l = ext->servers; l != NULL; l = next) {
+		struct ext_io *server = l->data;
+
+		next = g_slist_next(l);
+
+		if (server->adapter != adapter)
+			continue;
+
+		ext->servers = g_slist_remove(ext->servers, server);
+		ext_io_destroy(server);
+	}
+}
+
+static int ext_device_probe(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+	struct ext_profile *ext;
+
+	ext = find_ext(p);
+	if (!ext)
+		return -ENOENT;
+
+	DBG("%s probed with UUID %s", ext->name, p->remote_uuid);
+
+	return 0;
+}
+
+static struct ext_io *find_connection(struct ext_profile *ext,
+							struct btd_device *dev)
+{
+	GSList *l;
+
+	for (l = ext->conns; l != NULL; l = g_slist_next(l)) {
+		struct ext_io *conn = l->data;
+
+		if (conn->device == dev)
+			return conn;
+	}
+
+	return NULL;
+}
+
+static void ext_device_remove(struct btd_service *service)
+{
+	struct btd_profile *p = btd_service_get_profile(service);
+	struct btd_device *dev = btd_service_get_device(service);
+	struct ext_profile *ext;
+	struct ext_io *conn;
+
+	ext = find_ext(p);
+	if (!ext)
+		return;
+
+	DBG("%s", ext->name);
+
+	conn = find_connection(ext, dev);
+	if (conn) {
+		ext->conns = g_slist_remove(ext->conns, conn);
+		ext_io_destroy(conn);
+	}
+}
+
+static int connect_io(struct ext_io *conn, const bdaddr_t *src,
+							const bdaddr_t *dst)
+{
+	struct ext_profile *ext = conn->ext;
+	GError *gerr = NULL;
+	GIOChannel *io;
+
+	if (conn->psm) {
+		conn->proto = BTPROTO_L2CAP;
+		io = bt_io_connect(ext_connect, conn, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, src,
+					BT_IO_OPT_DEST_BDADDR, dst,
+					BT_IO_OPT_SEC_LEVEL, ext->sec_level,
+					BT_IO_OPT_PSM, conn->psm,
+					BT_IO_OPT_INVALID);
+	} else {
+		conn->proto = BTPROTO_RFCOMM;
+		io = bt_io_connect(ext_connect, conn, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, src,
+					BT_IO_OPT_DEST_BDADDR, dst,
+					BT_IO_OPT_SEC_LEVEL, ext->sec_level,
+					BT_IO_OPT_CHANNEL, conn->chan,
+					BT_IO_OPT_INVALID);
+	}
+
+	if (gerr != NULL) {
+		error("Unable to connect %s: %s", ext->name, gerr->message);
+		g_error_free(gerr);
+		return -EIO;
+	}
+
+	conn->io = io;
+
+	return 0;
+}
+
+static uint16_t get_goep_l2cap_psm(sdp_record_t *rec)
+{
+	sdp_data_t *data;
+
+	data = sdp_data_get(rec, SDP_ATTR_GOEP_L2CAP_PSM);
+	if (!data)
+		return 0;
+
+	if (data->dtd != SDP_UINT16)
+		return 0;
+
+	/* PSM must be odd and lsb of upper byte must be 0 */
+	if ((data->val.uint16 & 0x0101) != 0x0001)
+		return 0;
+
+	return data->val.uint16;
+}
+
+static void record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct ext_io *conn = user_data;
+	struct ext_profile *ext = conn->ext;
+	sdp_list_t *r;
+
+	conn->resolving = false;
+
+	if (err < 0) {
+		error("Unable to get %s SDP record: %s", ext->name,
+							strerror(-err));
+		goto failed;
+	}
+
+	if (!recs || !recs->data) {
+		error("No SDP records found for %s", ext->name);
+		err = -ENOTSUP;
+		goto failed;
+	}
+
+	for (r = recs; r != NULL; r = r->next) {
+		sdp_record_t *rec = r->data;
+		sdp_list_t *protos;
+		int port;
+
+		if (sdp_get_access_protos(rec, &protos) < 0) {
+			error("Unable to get proto list from %s record",
+								ext->name);
+			err = -ENOTSUP;
+			goto failed;
+		}
+
+		port = sdp_get_proto_port(protos, L2CAP_UUID);
+		if (port > 0)
+			conn->psm = port;
+
+		port = sdp_get_proto_port(protos, RFCOMM_UUID);
+		if (port > 0)
+			conn->chan = port;
+
+		if (conn->psm == 0 && sdp_get_proto_desc(protos, OBEX_UUID))
+			conn->psm = get_goep_l2cap_psm(rec);
+
+		conn->features = get_supported_features(rec);
+		conn->version = get_profile_version(rec);
+
+		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
+									NULL);
+		sdp_list_free(protos, NULL);
+
+		if (conn->chan || conn->psm)
+			break;
+	}
+
+	if (!conn->chan && !conn->psm) {
+		error("Failed to find L2CAP PSM or RFCOMM channel for %s",
+								ext->name);
+		err = -ENOTSUP;
+		goto failed;
+	}
+
+	err = connect_io(conn, btd_adapter_get_address(conn->adapter),
+					device_get_address(conn->device));
+	if (err < 0) {
+		error("Connecting %s failed: %s", ext->name, strerror(-err));
+		goto failed;
+	}
+
+	return;
+
+failed:
+	if (conn->service)
+		btd_service_connecting_complete(conn->service, err);
+
+	ext->conns = g_slist_remove(ext->conns, conn);
+	ext_io_destroy(conn);
+}
+
+static int resolve_service(struct ext_io *conn, const bdaddr_t *src,
+							const bdaddr_t *dst)
+{
+	struct ext_profile *ext = conn->ext;
+	uuid_t uuid;
+	int err;
+
+	bt_string2uuid(&uuid, ext->remote_uuid);
+	sdp_uuid128_to_uuid(&uuid);
+
+	err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL, 0);
+	if (err == 0)
+		conn->resolving = true;
+
+	return err;
+}
+
+static int ext_connect_dev(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct btd_profile *profile = btd_service_get_profile(service);
+	struct btd_adapter *adapter;
+	struct ext_io *conn;
+	struct ext_profile *ext;
+	int err;
+
+	ext = find_ext(profile);
+	if (!ext)
+		return -ENOENT;
+
+	conn = find_connection(ext, dev);
+	if (conn)
+		return -EALREADY;
+
+	adapter = device_get_adapter(dev);
+
+	conn = g_new0(struct ext_io, 1);
+	conn->ext = ext;
+
+	if (ext->remote_psm || ext->remote_chan) {
+		conn->psm = ext->remote_psm;
+		conn->chan = ext->remote_chan;
+		err = connect_io(conn, btd_adapter_get_address(adapter),
+						device_get_address(dev));
+	} else {
+		err = resolve_service(conn, btd_adapter_get_address(adapter),
+						device_get_address(dev));
+	}
+
+	if (err < 0)
+		goto failed;
+
+	conn->adapter = btd_adapter_ref(adapter);
+	conn->device = btd_device_ref(dev);
+	conn->service = btd_service_ref(service);
+
+	ext->conns = g_slist_append(ext->conns, conn);
+
+	return 0;
+
+failed:
+	g_free(conn);
+	return err;
+}
+
+static int send_disconn_req(struct ext_profile *ext, struct ext_io *conn)
+{
+	DBusMessage *msg;
+	const char *path;
+
+	msg = dbus_message_new_method_call(ext->owner, ext->path,
+						"org.bluez.Profile1",
+						"RequestDisconnection");
+	if (!msg) {
+		error("Unable to create RequestDisconnection call for %s",
+								ext->name);
+		return -ENOMEM;
+	}
+
+	path = device_get_path(conn->device);
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID);
+
+	if (!g_dbus_send_message_with_reply(btd_get_dbus_connection(),
+						msg, &conn->pending, -1)) {
+		error("%s: sending RequestDisconnection failed", ext->name);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_message_unref(msg);
+
+	dbus_pending_call_set_notify(conn->pending, disconn_reply, conn, NULL);
+
+	return 0;
+}
+
+static int ext_disconnect_dev(struct btd_service *service)
+{
+	struct btd_device *dev = btd_service_get_device(service);
+	struct btd_profile *profile = btd_service_get_profile(service);
+	struct ext_profile *ext;
+	struct ext_io *conn;
+	int err;
+
+	ext = find_ext(profile);
+	if (!ext)
+		return -ENOENT;
+
+	conn = find_connection(ext, dev);
+	if (!conn || !conn->connected)
+		return -ENOTCONN;
+
+	if (conn->pending)
+		return -EBUSY;
+
+	err = send_disconn_req(ext, conn);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static char *get_hfp_hf_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(HFP_HF_RECORD, rfcomm->chan, ext->version,
+						ext->name, ext->features);
+}
+
+static char *get_hfp_ag_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(HFP_AG_RECORD, rfcomm->chan, ext->version,
+						ext->name, ext->features);
+}
+
+static char *get_hsp_ag_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(HSP_AG_RECORD, rfcomm->chan, ext->version,
+						ext->name);
+}
+
+static char *get_spp_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	char *svc, *rec;
+
+	if (ext->service)
+		svc = g_strdup_printf("<uuid value=\"%s\" />", ext->service);
+	else
+		svc = g_strdup("");
+
+	rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, ext->version,
+								ext->name);
+	g_free(svc);
+	return rec;
+}
+
+static char *get_dun_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(DUN_RECORD, rfcomm->chan, ext->version,
+								ext->name);
+}
+
+static char *get_pce_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(PCE_RECORD, ext->version, ext->name);
+}
+
+static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
+}
+
+static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(MAS_RECORD, chan, ext->version, ext->name, psm);
+}
+
+static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(MNS_RECORD, chan, ext->version, ext->name, psm);
+}
+
+static char *get_sync_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	return g_strdup_printf(SYNC_RECORD, rfcomm->chan, ext->version,
+								ext->name);
+}
+
+static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(OPP_RECORD, chan, ext->version, psm, ext->name);
+}
+
+static char *get_ftp_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	uint16_t psm = 0;
+	uint8_t chan = 0;
+
+	if (l2cap)
+		psm = l2cap->psm;
+	if (rfcomm)
+		chan = rfcomm->chan;
+
+	return g_strdup_printf(FTP_RECORD, chan, ext->version, psm, ext->name);
+}
+
+#define RFCOMM_SEQ	"<sequence>				\
+				<uuid value=\"0x0003\" />	\
+				<uint8 value=\"0x%02x\" />	\
+			</sequence>"
+
+#define VERSION_ATTR							\
+		"<attribute id=\"0x0009\">				\
+			<sequence>					\
+				<sequence>				\
+					<uuid value=\"%s\" />		\
+					<uint16 value=\"0x%04x\" />	\
+				</sequence>				\
+			</sequence>					\
+		</attribute>"
+
+static char *get_generic_record(struct ext_profile *ext, struct ext_io *l2cap,
+							struct ext_io *rfcomm)
+{
+	char uuid_str[MAX_LEN_UUID_STR], svc_str[MAX_LEN_UUID_STR], psm[30];
+	char *rf_seq, *ver_attr, *rec;
+	uuid_t uuid;
+
+	bt_string2uuid(&uuid, ext->uuid);
+	sdp_uuid2strn(&uuid, uuid_str, sizeof(uuid_str));
+
+	if (ext->service) {
+		bt_string2uuid(&uuid, ext->service);
+		sdp_uuid2strn(&uuid, svc_str, sizeof(svc_str));
+	} else {
+		strncpy(svc_str, uuid_str, sizeof(svc_str));
+	}
+
+	if (l2cap)
+		snprintf(psm, sizeof(psm), "<uint16 value=\"0x%04x\" />",
+								l2cap->psm);
+	else
+		psm[0] = '\0';
+
+	if (rfcomm)
+		rf_seq = g_strdup_printf(RFCOMM_SEQ, rfcomm->chan);
+	else
+		rf_seq = g_strdup("");
+
+	if (ext->version)
+		ver_attr = g_strdup_printf(VERSION_ATTR, uuid_str,
+								ext->version);
+	else
+		ver_attr = g_strdup("");
+
+	rec = g_strdup_printf(GENERIC_RECORD, svc_str, psm, rf_seq, ver_attr,
+								ext->name);
+
+	g_free(rf_seq);
+	g_free(ver_attr);
+
+	return rec;
+}
+
+static struct default_settings {
+	const char	*uuid;
+	const char	*name;
+	int		priority;
+	const char	*remote_uuid;
+	int		channel;
+	int		psm;
+	BtIOMode	mode;
+	BtIOSecLevel	sec_level;
+	bool		authorize;
+	bool		auto_connect;
+	char *		(*get_record)(struct ext_profile *ext,
+					struct ext_io *l2cap,
+					struct ext_io *rfcomm);
+	uint16_t	version;
+	uint16_t	features;
+} defaults[] = {
+	{
+		.uuid		= SPP_UUID,
+		.name		= "Serial Port",
+		.channel	= SPP_DEFAULT_CHANNEL,
+		.authorize	= true,
+		.get_record	= get_spp_record,
+		.version	= 0x0102,
+	}, {
+		.uuid		= DUN_GW_UUID,
+		.name		= "Dial-Up Networking",
+		.channel	= DUN_DEFAULT_CHANNEL,
+		.authorize	= true,
+		.get_record	= get_dun_record,
+		.version	= 0x0102,
+	}, {
+		.uuid		= HFP_HS_UUID,
+		.name		= "Hands-Free unit",
+		.priority	= BTD_PROFILE_PRIORITY_HIGH,
+		.remote_uuid	= HFP_AG_UUID,
+		.channel	= HFP_HF_DEFAULT_CHANNEL,
+		.authorize	= true,
+		.auto_connect	= true,
+		.get_record	= get_hfp_hf_record,
+		.version	= 0x0105,
+	}, {
+		.uuid		= HFP_AG_UUID,
+		.name		= "Hands-Free Voice gateway",
+		.priority	= BTD_PROFILE_PRIORITY_HIGH,
+		.remote_uuid	= HFP_HS_UUID,
+		.channel	= HFP_AG_DEFAULT_CHANNEL,
+		.authorize	= true,
+		.auto_connect	= true,
+		.get_record	= get_hfp_ag_record,
+		.version	= 0x0105,
+	}, {
+		.uuid		= HSP_AG_UUID,
+		.name		= "Headset Voice gateway",
+		.priority	= BTD_PROFILE_PRIORITY_HIGH,
+		.remote_uuid	= HSP_HS_UUID,
+		.channel	= HSP_AG_DEFAULT_CHANNEL,
+		.authorize	= true,
+		.auto_connect	= true,
+		.get_record	= get_hsp_ag_record,
+		.version	= 0x0102,
+	}, {
+		.uuid		= OBEX_OPP_UUID,
+		.name		= "Object Push",
+		.channel	= OPP_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
+		.sec_level	= BT_IO_SEC_LOW,
+		.authorize	= false,
+		.get_record	= get_opp_record,
+		.version	= 0x0102,
+	}, {
+		.uuid		= OBEX_FTP_UUID,
+		.name		= "File Transfer",
+		.channel	= FTP_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
+		.authorize	= true,
+		.get_record	= get_ftp_record,
+		.version	= 0x0102,
+	}, {
+		.uuid		= OBEX_SYNC_UUID,
+		.name		= "Synchronization",
+		.channel	= SYNC_DEFAULT_CHANNEL,
+		.authorize	= true,
+		.get_record	= get_sync_record,
+		.version	= 0x0100,
+	}, {
+		.uuid		= OBEX_PSE_UUID,
+		.name		= "Phone Book Access",
+		.channel	= PBAP_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
+		.authorize	= true,
+		.get_record	= get_pse_record,
+		.version	= 0x0101,
+	}, {
+		.uuid		= OBEX_PCE_UUID,
+		.name		= "Phone Book Access Client",
+		.remote_uuid	= OBEX_PSE_UUID,
+		.authorize	= true,
+		.get_record	= get_pce_record,
+		.version	= 0x0102,
+	}, {
+		.uuid		= OBEX_MAS_UUID,
+		.name		= "Message Access",
+		.channel	= MAS_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
+		.authorize	= true,
+		.get_record	= get_mas_record,
+		.version	= 0x0100
+	}, {
+		.uuid		= OBEX_MNS_UUID,
+		.name		= "Message Notification",
+		.channel	= MNS_DEFAULT_CHANNEL,
+		.psm		= BTD_PROFILE_PSM_AUTO,
+		.mode		= BT_IO_MODE_ERTM,
+		.authorize	= true,
+		.get_record	= get_mns_record,
+		.version	= 0x0102
+	},
+};
+
+static void ext_set_defaults(struct ext_profile *ext)
+{
+	unsigned int i;
+
+	ext->mode = BT_IO_MODE_BASIC;
+	ext->sec_level = BT_IO_SEC_MEDIUM;
+	ext->authorize = true;
+	ext->enable_client = true;
+	ext->enable_server = true;
+	ext->remote_uuid = NULL;
+
+	for (i = 0; i < G_N_ELEMENTS(defaults); i++) {
+		struct default_settings *settings = &defaults[i];
+		const char *remote_uuid;
+
+		if (strcasecmp(ext->uuid, settings->uuid) != 0)
+			continue;
+
+		if (settings->remote_uuid)
+			remote_uuid = settings->remote_uuid;
+		else
+			remote_uuid = ext->uuid;
+
+		ext->remote_uuid = g_strdup(remote_uuid);
+
+		if (settings->channel)
+			ext->local_chan = settings->channel;
+
+		if (settings->psm)
+			ext->local_psm = settings->psm;
+
+		if (settings->sec_level)
+			ext->sec_level = settings->sec_level;
+
+		if (settings->mode)
+			ext->mode = settings->mode;
+
+		ext->authorize = settings->authorize;
+
+		if (settings->auto_connect)
+			ext->p.auto_connect = true;
+
+		if (settings->priority)
+			ext->p.priority = settings->priority;
+
+		if (settings->get_record)
+			ext->get_record = settings->get_record;
+
+		if (settings->version)
+			ext->version = settings->version;
+
+		if (settings->features)
+			ext->features = settings->features;
+
+		if (settings->name)
+			ext->name = g_strdup(settings->name);
+	}
+}
+
+static int parse_ext_opt(struct ext_profile *ext, const char *key,
+							DBusMessageIter *value)
+{
+	int type = dbus_message_iter_get_arg_type(value);
+	const char *str;
+	uint16_t u16;
+	dbus_bool_t b;
+
+	if (strcasecmp(key, "Name") == 0) {
+		if (type != DBUS_TYPE_STRING)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &str);
+		g_free(ext->name);
+		ext->name = g_strdup(str);
+	} else if (strcasecmp(key, "AutoConnect") == 0) {
+		if (type != DBUS_TYPE_BOOLEAN)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &b);
+		ext->p.auto_connect = b;
+	} else if (strcasecmp(key, "PSM") == 0) {
+		if (type != DBUS_TYPE_UINT16)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &u16);
+		ext->local_psm = u16 ? u16 : BTD_PROFILE_PSM_AUTO;
+	} else if (strcasecmp(key, "Channel") == 0) {
+		if (type != DBUS_TYPE_UINT16)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(value, &u16);
+		if (u16 > 31)
+			return -EINVAL;
+		ext->local_chan = u16 ? u16 : BTD_PROFILE_CHAN_AUTO;
+	} else if (strcasecmp(key, "RequireAuthentication") == 0) {
+		if (type != DBUS_TYPE_BOOLEAN)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(value, &b);
+		if (b)
+			ext->sec_level = BT_IO_SEC_MEDIUM;
+		else
+			ext->sec_level = BT_IO_SEC_LOW;
+	} else if (strcasecmp(key, "RequireAuthorization") == 0) {
+		if (type != DBUS_TYPE_BOOLEAN)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &b);
+		ext->authorize = b;
+	} else if (strcasecmp(key, "Role") == 0) {
+		if (type != DBUS_TYPE_STRING)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &str);
+		g_free(ext->role);
+		ext->role = g_strdup(str);
+
+		if (g_str_equal(ext->role, "client")) {
+			ext->enable_server = false;
+			ext->enable_client = true;
+		} else if (g_str_equal(ext->role, "server")) {
+			ext->enable_server = true;
+			ext->enable_client = false;
+		}
+	} else if (strcasecmp(key, "ServiceRecord") == 0) {
+		if (type != DBUS_TYPE_STRING)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &str);
+		g_free(ext->record);
+		ext->record = g_strdup(str);
+		ext->enable_server = true;
+	} else if (strcasecmp(key, "Version") == 0) {
+		uint16_t ver;
+
+		if (type != DBUS_TYPE_UINT16)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(value, &ver);
+		ext->version = ver;
+	} else if (strcasecmp(key, "Features") == 0) {
+		uint16_t feat;
+
+		if (type != DBUS_TYPE_UINT16)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(value, &feat);
+		ext->features = feat;
+	} else if (strcasecmp(key, "Service") == 0) {
+		if (type != DBUS_TYPE_STRING)
+			return -EINVAL;
+		dbus_message_iter_get_basic(value, &str);
+		free(ext->service);
+		ext->service = bt_name2string(str);
+	}
+
+	return 0;
+}
+
+static void set_service(struct ext_profile *ext)
+{
+	if (strcasecmp(ext->uuid, HSP_HS_UUID) == 0) {
+		ext->service = strdup(ext->uuid);
+	} else if (strcasecmp(ext->uuid, HSP_AG_UUID) == 0) {
+		ext->service = ext->uuid;
+		ext->uuid = strdup(HSP_HS_UUID);
+	} else if (strcasecmp(ext->uuid, HFP_HS_UUID) == 0) {
+		ext->service = strdup(ext->uuid);
+	} else if (strcasecmp(ext->uuid, HFP_AG_UUID) == 0) {
+		ext->service = ext->uuid;
+		ext->uuid = strdup(HFP_HS_UUID);
+	} else if (strcasecmp(ext->uuid, OBEX_SYNC_UUID) == 0 ||
+			strcasecmp(ext->uuid, OBEX_OPP_UUID) == 0 ||
+			strcasecmp(ext->uuid, OBEX_FTP_UUID) == 0) {
+		ext->service = strdup(ext->uuid);
+	} else if (strcasecmp(ext->uuid, OBEX_PSE_UUID) == 0 ||
+			strcasecmp(ext->uuid, OBEX_PCE_UUID) ==  0) {
+		ext->service = ext->uuid;
+		ext->uuid = strdup(OBEX_PBAP_UUID);
+	} else if (strcasecmp(ext->uuid, OBEX_MAS_UUID) == 0 ||
+			strcasecmp(ext->uuid, OBEX_MNS_UUID) == 0) {
+		ext->service = ext->uuid;
+		ext->uuid = strdup(OBEX_MAP_UUID);
+	}
+}
+
+static struct ext_profile *create_ext(const char *owner, const char *path,
+					const char *uuid,
+					DBusMessageIter *opts)
+{
+	struct btd_profile *p;
+	struct ext_profile *ext;
+
+	ext = g_new0(struct ext_profile, 1);
+
+	ext->uuid = bt_name2string(uuid);
+	if (ext->uuid == NULL) {
+		g_free(ext);
+		return NULL;
+	}
+
+	ext->owner = g_strdup(owner);
+	ext->path = g_strdup(path);
+
+	ext_set_defaults(ext);
+
+	while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter value, entry;
+		const char *key;
+
+		dbus_message_iter_recurse(opts, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		if (parse_ext_opt(ext, key, &value) < 0)
+			error("Invalid value for profile option %s", key);
+
+		dbus_message_iter_next(opts);
+	}
+
+	if (!ext->service)
+		set_service(ext);
+
+	if (ext->enable_server && !(ext->record || ext->get_record))
+		ext->get_record = get_generic_record;
+
+	if (!ext->name)
+		ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid);
+
+	if (!ext->remote_uuid) {
+		if (ext->service)
+			ext->remote_uuid = g_strdup(ext->service);
+		else
+			ext->remote_uuid = g_strdup(ext->uuid);
+	}
+
+	p = &ext->p;
+
+	p->name = ext->name;
+	p->local_uuid = ext->service ? ext->service : ext->uuid;
+	p->remote_uuid = ext->remote_uuid;
+	p->external = true;
+
+	if (ext->enable_server) {
+		p->adapter_probe = ext_adapter_probe;
+		p->adapter_remove = ext_adapter_remove;
+	}
+
+	if (ext->enable_client) {
+		p->device_probe = ext_device_probe;
+		p->device_remove = ext_device_remove;
+		p->connect = ext_connect_dev;
+		p->disconnect = ext_disconnect_dev;
+	}
+
+	DBG("Created \"%s\"", ext->name);
+
+	ext_profiles = g_slist_append(ext_profiles, ext);
+
+	adapter_foreach(adapter_add_profile, &ext->p);
+
+	return ext;
+}
+
+static void remove_ext(struct ext_profile *ext)
+{
+	adapter_foreach(adapter_remove_profile, &ext->p);
+
+	ext_profiles = g_slist_remove(ext_profiles, ext);
+
+	DBG("Removed \"%s\"", ext->name);
+
+	ext_remove_records(ext, NULL);
+
+	g_slist_free_full(ext->servers, ext_io_destroy);
+	g_slist_free_full(ext->conns, ext_io_destroy);
+
+	g_free(ext->remote_uuid);
+	g_free(ext->name);
+	g_free(ext->owner);
+	free(ext->uuid);
+	free(ext->service);
+	g_free(ext->role);
+	g_free(ext->path);
+	g_free(ext->record);
+
+	g_free(ext);
+}
+
+static void ext_exited(DBusConnection *conn, void *user_data)
+{
+	struct ext_profile *ext = user_data;
+
+	DBG("\"%s\" exited", ext->name);
+
+	remove_ext(ext);
+}
+
+static DBusMessage *register_profile(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *path, *sender, *uuid;
+	DBusMessageIter args, opts;
+	struct ext_profile *ext;
+
+	sender = dbus_message_get_sender(msg);
+
+	DBG("sender %s", sender);
+
+	dbus_message_iter_init(msg, &args);
+
+	dbus_message_iter_get_basic(&args, &path);
+	dbus_message_iter_next(&args);
+
+	ext = find_ext_profile(sender, path);
+	if (ext)
+		return btd_error_already_exists(msg);
+
+	dbus_message_iter_get_basic(&args, &uuid);
+	dbus_message_iter_next(&args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+		return btd_error_invalid_args(msg);
+
+	dbus_message_iter_recurse(&args, &opts);
+
+	ext = create_ext(sender, path, uuid, &opts);
+	if (!ext)
+		return btd_error_invalid_args(msg);
+
+	ext->id = g_dbus_add_disconnect_watch(conn, sender, ext_exited, ext,
+									NULL);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_profile(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	const char *path, *sender;
+	struct ext_profile *ext;
+
+	sender = dbus_message_get_sender(msg);
+
+	DBG("sender %s", sender);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID))
+		return btd_error_invalid_args(msg);
+
+	ext = find_ext_profile(sender, path);
+	if (!ext)
+		return btd_error_does_not_exist(msg);
+
+	g_dbus_remove_watch(conn, ext->id);
+	remove_ext(ext);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+	{ GDBUS_METHOD("RegisterProfile",
+			GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
+						{ "options", "a{sv}" }),
+			NULL, register_profile) },
+	{ GDBUS_METHOD("UnregisterProfile", GDBUS_ARGS({ "profile", "o" }),
+			NULL, unregister_profile) },
+	{ }
+};
+
+static struct btd_profile_custom_property *find_custom_prop(const char *uuid,
+							const char *name)
+{
+	GSList *l;
+
+	for (l = custom_props; l; l = l->next) {
+		struct btd_profile_custom_property *prop = l->data;
+
+		if (strcasecmp(prop->uuid, uuid) != 0)
+			continue;
+
+		if (g_strcmp0(prop->name, name) == 0)
+			return prop;
+	}
+
+	return NULL;
+}
+
+bool btd_profile_add_custom_prop(const char *uuid, const char *type,
+					const char *name,
+					btd_profile_prop_exists exists,
+					btd_profile_prop_get get,
+					void *user_data)
+{
+	struct btd_profile_custom_property *prop;
+
+	prop = find_custom_prop(uuid, name);
+	if (prop != NULL)
+		return false;
+
+	prop = g_new0(struct btd_profile_custom_property, 1);
+
+	prop->uuid = strdup(uuid);
+	prop->type = g_strdup(type);
+	prop->name = g_strdup(name);
+	prop->exists = exists;
+	prop->get = get;
+	prop->user_data = user_data;
+
+	custom_props = g_slist_append(custom_props, prop);
+
+	return true;
+}
+
+static void free_property(gpointer data)
+{
+	struct btd_profile_custom_property *p = data;
+
+	g_free(p->uuid);
+	g_free(p->type);
+	g_free(p->name);
+
+	g_free(p);
+}
+
+bool btd_profile_remove_custom_prop(const char *uuid, const char *name)
+{
+	struct btd_profile_custom_property *prop;
+
+	prop = find_custom_prop(uuid, name);
+	if (prop == NULL)
+		return false;
+
+	custom_props = g_slist_remove(custom_props, prop);
+	free_property(prop);
+
+	return false;
+}
+
+void btd_profile_init(void)
+{
+	g_dbus_register_interface(btd_get_dbus_connection(),
+				"/org/bluez", "org.bluez.ProfileManager1",
+				methods, NULL, NULL, NULL, NULL);
+}
+
+void btd_profile_cleanup(void)
+{
+	while (ext_profiles) {
+		struct ext_profile *ext = ext_profiles->data;
+		DBusConnection *conn = btd_get_dbus_connection();
+		DBusMessage *msg;
+
+		DBG("Releasing \"%s\"", ext->name);
+
+		g_slist_free_full(ext->conns, ext_io_destroy);
+		ext->conns = NULL;
+
+		msg = dbus_message_new_method_call(ext->owner, ext->path,
+							"org.bluez.Profile1",
+							"Release");
+		if (msg)
+			g_dbus_send_message(conn, msg);
+
+		g_dbus_remove_watch(conn, ext->id);
+		remove_ext(ext);
+
+	}
+
+	g_slist_free_full(custom_props, free_property);
+	custom_props = NULL;
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				"/org/bluez", "org.bluez.ProfileManager1");
+}
diff --git a/bluez/repo/src/profile.h b/bluez/repo/src/profile.h
new file mode 100644
index 0000000..4448a2a
--- /dev/null
+++ b/bluez/repo/src/profile.h
@@ -0,0 +1,77 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define BTD_PROFILE_PRIORITY_LOW	0
+#define BTD_PROFILE_PRIORITY_MEDIUM	1
+#define BTD_PROFILE_PRIORITY_HIGH	2
+
+struct btd_service;
+
+struct btd_profile {
+	const char *name;
+	int priority;
+
+	const char *local_uuid;
+	const char *remote_uuid;
+
+	bool auto_connect;
+	bool external;
+
+	int (*device_probe) (struct btd_service *service);
+	void (*device_remove) (struct btd_service *service);
+
+	int (*connect) (struct btd_service *service);
+	int (*disconnect) (struct btd_service *service);
+
+	int (*accept) (struct btd_service *service);
+
+	int (*adapter_probe) (struct btd_profile *p,
+						struct btd_adapter *adapter);
+	void (*adapter_remove) (struct btd_profile *p,
+						struct btd_adapter *adapter);
+};
+
+void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
+								void *data);
+
+int btd_profile_register(struct btd_profile *profile);
+void btd_profile_unregister(struct btd_profile *profile);
+
+typedef bool (*btd_profile_prop_exists)(const char *uuid,
+						struct btd_device *dev,
+						void *user_data);
+
+typedef bool (*btd_profile_prop_get)(const char *uuid,
+						struct btd_device *dev,
+						DBusMessageIter *iter,
+						void *user_data);
+
+bool btd_profile_add_custom_prop(const char *uuid, const char *type,
+					const char *name,
+					btd_profile_prop_exists exists,
+					btd_profile_prop_get get,
+					void *user_data);
+bool btd_profile_remove_custom_prop(const char *uuid, const char *name);
+
+void btd_profile_init(void);
+void btd_profile_cleanup(void);
diff --git a/bluez/repo/src/rfkill.c b/bluez/repo/src/rfkill.c
new file mode 100644
index 0000000..74eeb6a
--- /dev/null
+++ b/bluez/repo/src/rfkill.c
@@ -0,0 +1,174 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "log.h"
+#include "adapter.h"
+#include "hcid.h"
+
+enum rfkill_type {
+	RFKILL_TYPE_ALL = 0,
+	RFKILL_TYPE_WLAN,
+	RFKILL_TYPE_BLUETOOTH,
+	RFKILL_TYPE_UWB,
+	RFKILL_TYPE_WIMAX,
+	RFKILL_TYPE_WWAN,
+};
+
+enum rfkill_operation {
+	RFKILL_OP_ADD = 0,
+	RFKILL_OP_DEL,
+	RFKILL_OP_CHANGE,
+	RFKILL_OP_CHANGE_ALL,
+};
+
+struct rfkill_event {
+	uint32_t idx;
+	uint8_t  type;
+	uint8_t  op;
+	uint8_t  soft;
+	uint8_t  hard;
+};
+
+static gboolean rfkill_event(GIOChannel *chan,
+				GIOCondition cond, gpointer data)
+{
+	unsigned char buf[32];
+	struct rfkill_event *event = (void *) buf;
+	struct btd_adapter *adapter;
+	char sysname[PATH_MAX];
+	ssize_t len;
+	int fd, id;
+
+	if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	memset(buf, 0, sizeof(buf));
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0) {
+		if (errno == EAGAIN)
+			return TRUE;
+		return FALSE;
+	}
+
+	if (len != sizeof(struct rfkill_event))
+		return TRUE;
+
+	DBG("RFKILL event idx %u type %u op %u soft %u hard %u",
+					event->idx, event->type, event->op,
+						event->soft, event->hard);
+
+	if (event->soft || event->hard)
+		return TRUE;
+
+	if (event->op != RFKILL_OP_CHANGE)
+		return TRUE;
+
+	if (event->type != RFKILL_TYPE_BLUETOOTH &&
+					event->type != RFKILL_TYPE_ALL)
+		return TRUE;
+
+	snprintf(sysname, sizeof(sysname) - 1,
+			"/sys/class/rfkill/rfkill%u/name", event->idx);
+
+	fd = open(sysname, O_RDONLY);
+	if (fd < 0)
+		return TRUE;
+
+	memset(sysname, 0, sizeof(sysname));
+
+	if (read(fd, sysname, sizeof(sysname) - 1) < 4) {
+		close(fd);
+		return TRUE;
+	}
+
+	close(fd);
+
+	if (g_str_has_prefix(sysname, "hci") == FALSE)
+		return TRUE;
+
+	id = atoi(sysname + 3);
+	if (id < 0)
+		return TRUE;
+
+	adapter = adapter_find_by_id(id);
+	if (!adapter)
+		return TRUE;
+
+	DBG("RFKILL unblock for hci%d", id);
+
+	btd_adapter_restore_powered(adapter);
+
+	return TRUE;
+}
+
+static guint watch = 0;
+
+void rfkill_init(void)
+{
+	int fd;
+	GIOChannel *channel;
+
+	fd = open("/dev/rfkill", O_RDWR);
+	if (fd < 0) {
+		error("Failed to open RFKILL control device");
+		return;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	watch = g_io_add_watch(channel,
+				G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+				rfkill_event, NULL);
+
+	g_io_channel_unref(channel);
+}
+
+void rfkill_exit(void)
+{
+	if (watch == 0)
+		return;
+
+	g_source_remove(watch);
+	watch = 0;
+}
diff --git a/bluez/repo/src/sdp-client.c b/bluez/repo/src/sdp-client.c
new file mode 100644
index 0000000..413cf30
--- /dev/null
+++ b/bluez/repo/src/sdp-client.c
@@ -0,0 +1,413 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "btio/btio.h"
+#include "log.h"
+#include "sdp-client.h"
+
+/* Number of seconds to keep a sdp_session_t in the cache */
+#define CACHE_TIMEOUT 2
+
+struct cached_sdp_session {
+	bdaddr_t src;
+	bdaddr_t dst;
+	sdp_session_t *session;
+	guint timer;
+	guint io_id;
+};
+
+static GSList *cached_sdp_sessions = NULL;
+
+static void cleanup_cached_session(struct cached_sdp_session *cached)
+{
+	cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
+	sdp_close(cached->session);
+	g_free(cached);
+}
+
+static gboolean cached_session_expired(gpointer user_data)
+{
+	struct cached_sdp_session *cached = user_data;
+
+	g_source_remove(cached->io_id);
+	cleanup_cached_session(cached);
+
+	return FALSE;
+}
+
+static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src,
+							const bdaddr_t *dst)
+{
+	GSList *l;
+
+	for (l = cached_sdp_sessions; l != NULL; l = l->next) {
+		struct cached_sdp_session *c = l->data;
+		sdp_session_t *session;
+
+		if (bacmp(&c->src, src) || bacmp(&c->dst, dst))
+			continue;
+
+		g_source_remove(c->timer);
+		g_source_remove(c->io_id);
+
+		session = c->session;
+
+		cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
+		g_free(c);
+
+		return session;
+	}
+
+	return NULL;
+}
+
+static gboolean disconnect_watch(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct cached_sdp_session *cached = user_data;
+
+	g_source_remove(cached->timer);
+	cleanup_cached_session(cached);
+
+	return FALSE;
+}
+
+static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
+						sdp_session_t *session)
+{
+	struct cached_sdp_session *cached;
+	int sk;
+	GIOChannel *chan;
+
+	cached = g_new0(struct cached_sdp_session, 1);
+
+	bacpy(&cached->src, src);
+	bacpy(&cached->dst, dst);
+
+	cached->session = session;
+
+	cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached);
+
+	cached->timer = g_timeout_add_seconds(CACHE_TIMEOUT,
+						cached_session_expired,
+						cached);
+
+	/* Watch the connection state during cache timeout */
+	sk = sdp_get_socket(session);
+	chan = g_io_channel_unix_new(sk);
+
+	cached->io_id = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+						disconnect_watch, cached);
+
+	g_io_channel_unref(chan);
+}
+
+struct search_context {
+	bdaddr_t		src;
+	bdaddr_t		dst;
+	sdp_session_t		*session;
+	bt_callback_t		cb;
+	bt_destroy_t		destroy;
+	gpointer		user_data;
+	uuid_t			uuid;
+	guint			io_id;
+};
+
+static GSList *context_list = NULL;
+
+static void search_context_cleanup(struct search_context *ctxt)
+{
+	context_list = g_slist_remove(context_list, ctxt);
+
+	if (ctxt->destroy)
+		ctxt->destroy(ctxt->user_data);
+
+	g_free(ctxt);
+}
+
+static void search_completed_cb(uint8_t type, uint16_t status,
+			uint8_t *rsp, size_t size, void *user_data)
+{
+	struct search_context *ctxt = user_data;
+	sdp_list_t *recs = NULL;
+	int scanned, seqlen = 0, bytesleft = size;
+	uint8_t dataType;
+	int err = 0;
+
+	if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+		err = -EPROTO;
+		goto done;
+	}
+
+	scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
+	if (!scanned || !seqlen)
+		goto done;
+
+	rsp += scanned;
+	bytesleft -= scanned;
+	do {
+		sdp_record_t *rec;
+		int recsize;
+
+		recsize = 0;
+		rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
+		if (!rec)
+			break;
+
+		if (!recsize) {
+			sdp_record_free(rec);
+			break;
+		}
+
+		scanned += recsize;
+		rsp += recsize;
+		bytesleft -= recsize;
+
+		recs = sdp_list_append(recs, rec);
+	} while (scanned < (ssize_t) size && bytesleft > 0);
+
+done:
+	cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
+
+	if (ctxt->cb)
+		ctxt->cb(recs, err, ctxt->user_data);
+
+	if (recs)
+		sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
+
+	search_context_cleanup(ctxt);
+}
+
+static gboolean search_process_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct search_context *ctxt = user_data;
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		sdp_close(ctxt->session);
+		ctxt->session = NULL;
+
+		if (ctxt->cb)
+			ctxt->cb(NULL, -EIO, ctxt->user_data);
+
+		search_context_cleanup(ctxt);
+		return FALSE;
+	}
+
+	/* If sdp_process fails it calls search_completed_cb */
+	if (sdp_process(ctxt->session) < 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean connect_watch(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	struct search_context *ctxt = user_data;
+	sdp_list_t *search, *attrids;
+	uint32_t range = 0x0000ffff;
+	socklen_t len;
+	int sk, err, sk_err = 0;
+
+	sk = g_io_channel_unix_get_fd(chan);
+	ctxt->io_id = 0;
+
+	len = sizeof(sk_err);
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+		err = -errno;
+	else
+		err = -sk_err;
+
+	if (err != 0)
+		goto failed;
+
+	if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
+		err = -EIO;
+		goto failed;
+	}
+
+	search = sdp_list_append(NULL, &ctxt->uuid);
+	attrids = sdp_list_append(NULL, &range);
+	if (sdp_service_search_attr_async(ctxt->session,
+				search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
+		sdp_list_free(attrids, NULL);
+		sdp_list_free(search, NULL);
+		err = -EIO;
+		goto failed;
+	}
+
+	sdp_list_free(attrids, NULL);
+	sdp_list_free(search, NULL);
+
+	/* Set callback responsible for update the internal SDP transaction */
+	ctxt->io_id = g_io_add_watch(chan,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				search_process_cb, ctxt);
+	return FALSE;
+
+failed:
+	sdp_close(ctxt->session);
+	ctxt->session = NULL;
+
+	if (ctxt->cb)
+		ctxt->cb(NULL, err, ctxt->user_data);
+
+	search_context_cleanup(ctxt);
+
+	return FALSE;
+}
+
+static int create_search_context(struct search_context **ctxt,
+					const bdaddr_t *src,
+					const bdaddr_t *dst,
+					uuid_t *uuid, uint16_t flags)
+{
+	sdp_session_t *s;
+	GIOChannel *chan;
+	uint32_t prio = 1;
+	int sk;
+
+	if (!ctxt)
+		return -EINVAL;
+
+	s = get_cached_sdp_session(src, dst);
+	if (!s)
+		s = sdp_connect(src, dst, SDP_NON_BLOCKING | flags);
+
+	if (!s)
+		return -errno;
+
+	*ctxt = g_try_malloc0(sizeof(struct search_context));
+	if (!*ctxt) {
+		sdp_close(s);
+		return -ENOMEM;
+	}
+
+	bacpy(&(*ctxt)->src, src);
+	bacpy(&(*ctxt)->dst, dst);
+	(*ctxt)->session = s;
+	(*ctxt)->uuid = *uuid;
+
+	sk = sdp_get_socket(s);
+	/* Set low priority for the SDP connection not to interfere with
+	 * other potential traffic.
+	 */
+	if (setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
+		warn("Setting SDP priority failed: %s (%d)",
+						strerror(errno), errno);
+
+	chan = g_io_channel_unix_new(sk);
+	(*ctxt)->io_id = g_io_add_watch(chan,
+				G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				connect_watch, *ctxt);
+	g_io_channel_unref(chan);
+
+	return 0;
+}
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+			uuid_t *uuid, bt_callback_t cb, void *user_data,
+			bt_destroy_t destroy, uint16_t flags)
+{
+	struct search_context *ctxt = NULL;
+	int err;
+
+	if (!cb)
+		return -EINVAL;
+
+	err = create_search_context(&ctxt, src, dst, uuid, flags);
+	if (err < 0)
+		return err;
+
+	ctxt->cb	= cb;
+	ctxt->destroy	= destroy;
+	ctxt->user_data	= user_data;
+
+	context_list = g_slist_append(context_list, ctxt);
+
+	return 0;
+}
+
+static int find_by_bdaddr(gconstpointer data, gconstpointer user_data)
+{
+	const struct search_context *ctxt = data, *search = user_data;
+	int ret;
+
+	ret = bacmp(&ctxt->src, &search->src);
+	if (ret != 0)
+		return ret;
+
+	return bacmp(&ctxt->dst, &search->dst);
+}
+
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	struct search_context match, *ctxt;
+	GSList *l;
+
+	memset(&match, 0, sizeof(match));
+	bacpy(&match.src, src);
+	bacpy(&match.dst, dst);
+
+	/* Ongoing SDP Discovery */
+	l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
+	if (l == NULL)
+		return -ENOENT;
+
+	ctxt = l->data;
+
+	if (!ctxt->session)
+		return -ENOTCONN;
+
+	if (ctxt->io_id)
+		g_source_remove(ctxt->io_id);
+
+	if (ctxt->session)
+		sdp_close(ctxt->session);
+
+	search_context_cleanup(ctxt);
+
+	return 0;
+}
+
+void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+	sdp_session_t *session;
+
+	session = get_cached_sdp_session(src, dst);
+	if (session)
+		sdp_close(session);
+}
diff --git a/bluez/repo/src/sdp-client.h b/bluez/repo/src/sdp-client.h
new file mode 100644
index 0000000..9aa5a4d
--- /dev/null
+++ b/bluez/repo/src/sdp-client.h
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
+typedef void (*bt_destroy_t) (gpointer user_data);
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+			uuid_t *uuid, bt_callback_t cb, void *user_data,
+			bt_destroy_t destroy, uint16_t flags);
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
+void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst);
diff --git a/bluez/repo/src/sdp-xml.c b/bluez/repo/src/sdp-xml.c
new file mode 100644
index 0000000..0a3eb60
--- /dev/null
+++ b/bluez/repo/src/sdp-xml.c
@@ -0,0 +1,1014 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-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 <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "sdp-xml.h"
+
+#define DBG(...) (void)(0)
+#define error(...) (void)(0)
+
+#define SDP_XML_ENCODING_NORMAL	0
+#define SDP_XML_ENCODING_HEX	1
+
+#define STRBUFSIZE 1024
+#define MAXINDENT 64
+
+struct sdp_xml_data {
+	char *text;			/* Pointer to the current buffer */
+	int size;			/* Size of the current buffer */
+	sdp_data_t *data;		/* The current item being built */
+	struct sdp_xml_data *next;	/* Next item on the stack */
+	char type;			/* 0 = Text or Hexadecimal */
+	char *name;			/* Name, optional in the dtd */
+	/* TODO: What is it used for? */
+};
+
+struct context_data {
+	sdp_record_t *record;
+	sdp_data_t attr_data;
+	struct sdp_xml_data *stack_head;
+	uint16_t attr_id;
+};
+
+static int compute_seq_size(sdp_data_t *data)
+{
+	int unit_size = data->unitSize;
+	sdp_data_t *seq = data->val.dataseq;
+
+	for (; seq; seq = seq->next)
+		unit_size += seq->unitSize;
+
+	return unit_size;
+}
+
+#define DEFAULT_XML_DATA_SIZE 1024
+
+static struct sdp_xml_data *sdp_xml_data_alloc(void)
+{
+	struct sdp_xml_data *elem;
+
+	elem = malloc(sizeof(struct sdp_xml_data));
+	if (!elem)
+		return NULL;
+
+	memset(elem, 0, sizeof(struct sdp_xml_data));
+
+	/* Null terminate the text */
+	elem->size = DEFAULT_XML_DATA_SIZE;
+	elem->text = malloc(DEFAULT_XML_DATA_SIZE);
+	if (!elem->text) {
+		free(elem);
+		return NULL;
+	}
+	elem->text[0] = '\0';
+
+	return elem;
+}
+
+static struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem)
+{
+	char *newbuf;
+
+	newbuf = malloc(elem->size * 2);
+	if (!newbuf)
+		return NULL;
+
+	memcpy(newbuf, elem->text, elem->size);
+	elem->size *= 2;
+	free(elem->text);
+
+	elem->text = newbuf;
+
+	return elem;
+}
+
+static sdp_data_t *sdp_xml_parse_uuid128(const char *data)
+{
+	uint128_t val;
+	unsigned int i, j;
+
+	char buf[3];
+
+	memset(&val, 0, sizeof(val));
+
+	buf[2] = '\0';
+
+	for (j = 0, i = 0; i < strlen(data);) {
+		if (data[i] == '-') {
+			i++;
+			continue;
+		}
+
+		buf[0] = data[i];
+		buf[1] = data[i + 1];
+
+		val.data[j++] = strtoul(buf, 0, 16);
+		i += 2;
+	}
+
+	return sdp_data_alloc(SDP_UUID128, &val);
+}
+
+static sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record)
+{
+	sdp_data_t *ret;
+	char *endptr;
+	uint32_t val;
+	uint16_t val2;
+	int len;
+
+	len = strlen(data);
+
+	if (len == 36) {
+		ret = sdp_xml_parse_uuid128(data);
+		goto result;
+	}
+
+	val = strtoll(data, &endptr, 16);
+
+	/* Couldn't parse */
+	if (*endptr != '\0')
+		return NULL;
+
+	if (val > USHRT_MAX) {
+		ret = sdp_data_alloc(SDP_UUID32, &val);
+		goto result;
+	}
+
+	val2 = val;
+
+	ret = sdp_data_alloc(SDP_UUID16, &val2);
+
+result:
+	if (record && ret)
+		sdp_pattern_add_uuid(record, &ret->val.uuid);
+
+	return ret;
+}
+
+static sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd)
+{
+	char *endptr;
+	sdp_data_t *ret = NULL;
+
+	switch (dtd) {
+	case SDP_BOOL:
+	{
+		uint8_t val = 0;
+
+		if (!strcmp("true", data))
+			val = 1;
+		else if (!strcmp("false", data))
+			val = 0;
+		else
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_INT8:
+	{
+		int8_t val = strtoul(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_UINT8:
+	{
+		uint8_t val = strtoul(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_INT16:
+	{
+		int16_t val = strtoul(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_UINT16:
+	{
+		uint16_t val = strtoul(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_INT32:
+	{
+		int32_t val = strtoul(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_UINT32:
+	{
+		uint32_t val = strtoul(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_INT64:
+	{
+		int64_t val = strtoull(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_UINT64:
+	{
+		uint64_t val = strtoull(data, &endptr, 0);
+
+		/* Failed to parse */
+		if ((endptr != data) && (*endptr != '\0'))
+			return NULL;
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	case SDP_INT128:
+	case SDP_UINT128:
+	{
+		uint128_t val;
+		int i = 0;
+		char buf[3];
+
+		buf[2] = '\0';
+
+		for (; i < 32; i += 2) {
+			buf[0] = data[i];
+			buf[1] = data[i + 1];
+
+			val.data[i >> 1] = strtoul(buf, 0, 16);
+		}
+
+		ret = sdp_data_alloc(dtd, &val);
+		break;
+	}
+
+	};
+
+	return ret;
+}
+
+static char *sdp_xml_parse_string_decode(const char *data, char encoding,
+							uint32_t *length)
+{
+	int len = strlen(data);
+	char *text;
+
+	if (encoding == SDP_XML_ENCODING_NORMAL) {
+		text = strdup(data);
+		*length = len;
+	} else {
+		char buf[3], *decoded;
+		int i;
+
+		decoded = malloc((len >> 1) + 1);
+		if (!decoded)
+			return NULL;
+
+		/* Ensure the string is a power of 2 */
+		len = (len >> 1) << 1;
+
+		buf[2] = '\0';
+
+		for (i = 0; i < len; i += 2) {
+			buf[0] = data[i];
+			buf[1] = data[i + 1];
+
+			decoded[i >> 1] = strtoul(buf, 0, 16);
+		}
+
+		decoded[len >> 1] = '\0';
+		text = decoded;
+		*length = len >> 1;
+	}
+
+	return text;
+}
+
+static sdp_data_t *sdp_xml_parse_url(const char *data)
+{
+	uint8_t dtd = SDP_URL_STR8;
+	char *url;
+	uint32_t length;
+	sdp_data_t *ret;
+
+	url = sdp_xml_parse_string_decode(data,
+				SDP_XML_ENCODING_NORMAL, &length);
+	if (!url)
+		return NULL;
+
+	if (length > UCHAR_MAX)
+		dtd = SDP_URL_STR16;
+
+	ret = sdp_data_alloc_with_length(dtd, url, length);
+
+	free(url);
+
+	return ret;
+}
+
+static sdp_data_t *sdp_xml_parse_text(const char *data, char encoding)
+{
+	uint8_t dtd = SDP_TEXT_STR8;
+	char *text;
+	uint32_t length;
+	sdp_data_t *ret;
+
+	text = sdp_xml_parse_string_decode(data, encoding, &length);
+	if (!text)
+		return NULL;
+
+	if (length > UCHAR_MAX)
+		dtd = SDP_TEXT_STR16;
+
+	ret = sdp_data_alloc_with_length(dtd, text, length);
+
+	free(text);
+
+	return ret;
+}
+
+static sdp_data_t *sdp_xml_parse_nil(const char *data)
+{
+	return sdp_data_alloc(SDP_DATA_NIL, 0);
+}
+
+static sdp_data_t *sdp_xml_parse_datatype(const char *el,
+						struct sdp_xml_data *elem,
+						sdp_record_t *record)
+{
+	const char *data = elem->text;
+
+	if (!strcmp(el, "boolean"))
+		return sdp_xml_parse_int(data, SDP_BOOL);
+	else if (!strcmp(el, "uint8"))
+		return sdp_xml_parse_int(data, SDP_UINT8);
+	else if (!strcmp(el, "uint16"))
+		return sdp_xml_parse_int(data, SDP_UINT16);
+	else if (!strcmp(el, "uint32"))
+		return sdp_xml_parse_int(data, SDP_UINT32);
+	else if (!strcmp(el, "uint64"))
+		return sdp_xml_parse_int(data, SDP_UINT64);
+	else if (!strcmp(el, "uint128"))
+		return sdp_xml_parse_int(data, SDP_UINT128);
+	else if (!strcmp(el, "int8"))
+		return sdp_xml_parse_int(data, SDP_INT8);
+	else if (!strcmp(el, "int16"))
+		return sdp_xml_parse_int(data, SDP_INT16);
+	else if (!strcmp(el, "int32"))
+		return sdp_xml_parse_int(data, SDP_INT32);
+	else if (!strcmp(el, "int64"))
+		return sdp_xml_parse_int(data, SDP_INT64);
+	else if (!strcmp(el, "int128"))
+		return sdp_xml_parse_int(data, SDP_INT128);
+	else if (!strcmp(el, "uuid"))
+		return sdp_xml_parse_uuid(data, record);
+	else if (!strcmp(el, "url"))
+		return sdp_xml_parse_url(data);
+	else if (!strcmp(el, "text"))
+		return sdp_xml_parse_text(data, elem->type);
+	else if (!strcmp(el, "nil"))
+		return sdp_xml_parse_nil(data);
+
+	return NULL;
+}
+static void element_start(GMarkupParseContext *context,
+		const char *element_name, const char **attribute_names,
+		const char **attribute_values, gpointer user_data, GError **err)
+{
+	struct context_data *ctx_data = user_data;
+
+	if (!strcmp(element_name, "record"))
+		return;
+
+	if (!strcmp(element_name, "attribute")) {
+		int i;
+		for (i = 0; attribute_names[i]; i++) {
+			if (!strcmp(attribute_names[i], "id")) {
+				ctx_data->attr_id = strtol(attribute_values[i], 0, 0);
+				break;
+			}
+		}
+		DBG("New attribute 0x%04x", ctx_data->attr_id);
+		return;
+	}
+
+	if (ctx_data->stack_head) {
+		struct sdp_xml_data *newelem = sdp_xml_data_alloc();
+		newelem->next = ctx_data->stack_head;
+		ctx_data->stack_head = newelem;
+	} else {
+		ctx_data->stack_head = sdp_xml_data_alloc();
+		ctx_data->stack_head->next = NULL;
+	}
+
+	if (!strcmp(element_name, "sequence"))
+		ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL);
+	else if (!strcmp(element_name, "alternate"))
+		ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL);
+	else {
+		int i;
+		/* Parse value, name, encoding */
+		for (i = 0; attribute_names[i]; i++) {
+			if (!strcmp(attribute_names[i], "value")) {
+				int curlen = strlen(ctx_data->stack_head->text);
+				int attrlen = strlen(attribute_values[i]);
+
+				/* Ensure we're big enough */
+				while ((curlen + 1 + attrlen) > ctx_data->stack_head->size)
+					sdp_xml_data_expand(ctx_data->stack_head);
+
+				memcpy(ctx_data->stack_head->text + curlen,
+						attribute_values[i], attrlen);
+				ctx_data->stack_head->text[curlen + attrlen] = '\0';
+			}
+
+			if (!strcmp(attribute_names[i], "encoding")) {
+				if (!strcmp(attribute_values[i], "hex"))
+					ctx_data->stack_head->type = 1;
+			}
+
+			if (!strcmp(attribute_names[i], "name"))
+				ctx_data->stack_head->name = strdup(attribute_values[i]);
+		}
+
+		ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name,
+				ctx_data->stack_head, ctx_data->record);
+
+		if (ctx_data->stack_head->data == NULL)
+			error("Can't parse element %s", element_name);
+	}
+}
+
+static void sdp_xml_data_free(struct sdp_xml_data *elem)
+{
+	if (elem->data)
+		sdp_data_free(elem->data);
+
+	free(elem->name);
+	free(elem->text);
+	free(elem);
+}
+
+static void element_end(GMarkupParseContext *context,
+		const char *element_name, gpointer user_data, GError **err)
+{
+	struct context_data *ctx_data = user_data;
+	struct sdp_xml_data *elem;
+
+	if (!strcmp(element_name, "record"))
+		return;
+
+	if (!strcmp(element_name, "attribute")) {
+		if (ctx_data->stack_head && ctx_data->stack_head->data) {
+			int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
+							ctx_data->stack_head->data);
+			if (ret == -1)
+				DBG("Could not add attribute 0x%04x",
+							ctx_data->attr_id);
+
+			ctx_data->stack_head->data = NULL;
+			sdp_xml_data_free(ctx_data->stack_head);
+			ctx_data->stack_head = NULL;
+		} else {
+			DBG("No data for attribute 0x%04x", ctx_data->attr_id);
+		}
+		return;
+	}
+
+	if (!ctx_data->stack_head || !ctx_data->stack_head->data) {
+		DBG("No data for %s", element_name);
+		return;
+	}
+
+	if (!strcmp(element_name, "sequence")) {
+		ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+		if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+			ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+			ctx_data->stack_head->data->dtd = SDP_SEQ32;
+		} else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+			ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+			ctx_data->stack_head->data->dtd = SDP_SEQ16;
+		} else {
+			ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+		}
+	} else if (!strcmp(element_name, "alternate")) {
+		ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+		if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+			ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+			ctx_data->stack_head->data->dtd = SDP_ALT32;
+		} else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+			ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+			ctx_data->stack_head->data->dtd = SDP_ALT16;
+		} else {
+			ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+		}
+	}
+
+	if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
+					ctx_data->stack_head->next->data) {
+		switch (ctx_data->stack_head->next->data->dtd) {
+		case SDP_SEQ8:
+		case SDP_SEQ16:
+		case SDP_SEQ32:
+		case SDP_ALT8:
+		case SDP_ALT16:
+		case SDP_ALT32:
+			ctx_data->stack_head->next->data->val.dataseq =
+				sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
+								ctx_data->stack_head->data);
+			ctx_data->stack_head->data = NULL;
+			break;
+		}
+
+		elem = ctx_data->stack_head;
+		ctx_data->stack_head = ctx_data->stack_head->next;
+
+		sdp_xml_data_free(elem);
+	}
+}
+
+static GMarkupParser parser = {
+	element_start, element_end, NULL, NULL, NULL
+};
+
+sdp_record_t *sdp_xml_parse_record(const char *data, int size)
+{
+	GMarkupParseContext *ctx;
+	struct context_data *ctx_data;
+	sdp_record_t *record;
+
+	ctx_data = malloc(sizeof(*ctx_data));
+	if (!ctx_data)
+		return NULL;
+
+	record = sdp_record_alloc();
+	if (!record) {
+		free(ctx_data);
+		return NULL;
+	}
+
+	memset(ctx_data, 0, sizeof(*ctx_data));
+	ctx_data->record = record;
+
+	ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL);
+
+	if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+		error("XML parsing error");
+		g_markup_parse_context_free(ctx);
+		sdp_record_free(record);
+		free(ctx_data);
+		return NULL;
+	}
+
+	g_markup_parse_context_free(ctx);
+
+	free(ctx_data);
+
+	return record;
+}
+
+
+static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
+		void *data, void (*appender)(void *, const char *))
+{
+	int i, hex;
+	char buf[STRBUFSIZE];
+	char indent[MAXINDENT];
+
+	if (!value)
+		return;
+
+	if (indent_level >= MAXINDENT)
+		indent_level = MAXINDENT - 2;
+
+	for (i = 0; i < indent_level; i++)
+		indent[i] = '\t';
+
+	indent[i] = '\0';
+	buf[STRBUFSIZE - 1] = '\0';
+
+	switch (value->dtd) {
+	case SDP_DATA_NIL:
+		appender(data, indent);
+		appender(data, "<nil/>\n");
+		break;
+
+	case SDP_BOOL:
+		appender(data, indent);
+		appender(data, "<boolean value=\"");
+		appender(data, value->val.uint8 ? "true" : "false");
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UINT8:
+		appender(data, indent);
+		appender(data, "<uint8 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UINT16:
+		appender(data, indent);
+		appender(data, "<uint16 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UINT32:
+		appender(data, indent);
+		appender(data, "<uint32 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UINT64:
+		appender(data, indent);
+		appender(data, "<uint64 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UINT128:
+		appender(data, indent);
+		appender(data, "<uint128 value=\"");
+
+		for (i = 0; i < 16; i++) {
+			sprintf(&buf[i * 2], "%02x",
+				(unsigned char) value->val.uint128.data[i]);
+		}
+
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_INT8:
+		appender(data, indent);
+		appender(data, "<int8 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_INT16:
+		appender(data, indent);
+		appender(data, "<int16 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_INT32:
+		appender(data, indent);
+		appender(data, "<int32 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_INT64:
+		appender(data, indent);
+		appender(data, "<int64 value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_INT128:
+		appender(data, indent);
+		appender(data, "<int128 value=\"");
+
+		for (i = 0; i < 16; i++) {
+			sprintf(&buf[i * 2], "%02x",
+				(unsigned char) value->val.int128.data[i]);
+		}
+		appender(data, buf);
+
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UUID16:
+		appender(data, indent);
+		appender(data, "<uuid value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uuid.value.uuid16);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UUID32:
+		appender(data, indent);
+		appender(data, "<uuid value=\"");
+		snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uuid.value.uuid32);
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_UUID128:
+		appender(data, indent);
+		appender(data, "<uuid value=\"");
+
+		snprintf(buf, STRBUFSIZE - 1,
+			 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[0],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[1],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[2],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[3],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[4],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[5],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[6],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[7],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[8],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[9],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[10],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[11],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[12],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[13],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[14],
+			 (unsigned char) value->val.uuid.value.
+			 uuid128.data[15]);
+
+		appender(data, buf);
+		appender(data, "\" />\n");
+		break;
+
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+	{
+		int num_chars_to_escape = 0;
+		int length = value->unitSize - 1;
+		char *strBuf;
+
+		hex = 0;
+
+		for (i = 0; i < length; i++) {
+			if (!isprint(value->val.str[i]) &&
+					value->val.str[i] != '\0') {
+				hex = 1;
+				break;
+			}
+
+			/* XML is evil, must do this... */
+			if ((value->val.str[i] == '<') ||
+					(value->val.str[i] == '>') ||
+					(value->val.str[i] == '"') ||
+					(value->val.str[i] == '&'))
+				num_chars_to_escape++;
+		}
+
+		appender(data, indent);
+
+		appender(data, "<text ");
+
+		if (hex) {
+			appender(data, "encoding=\"hex\" ");
+			strBuf = malloc(sizeof(char)
+						 * ((value->unitSize-1) * 2 + 1));
+			if (!strBuf) {
+				DBG("No memory to convert raw data to xml");
+				return;
+			}
+
+			/* Unit Size seems to include the size for dtd
+			   It is thus off by 1
+			   This is safe for Normal strings, but not
+			   hex encoded data */
+			for (i = 0; i < (value->unitSize-1); i++)
+				sprintf(&strBuf[i*sizeof(char)*2],
+					"%02x",
+					(unsigned char) value->val.str[i]);
+
+			strBuf[(value->unitSize-1) * 2] = '\0';
+		} else {
+			int j;
+			/* escape the XML disallowed chars */
+			strBuf = malloc(sizeof(char) *
+					(value->unitSize + 1 + num_chars_to_escape * 4));
+			if (!strBuf) {
+				DBG("No memory to convert raw data to xml");
+				return;
+			}
+			for (i = 0, j = 0; i < length; i++) {
+				if (value->val.str[i] == '&') {
+					strBuf[j++] = '&';
+					strBuf[j++] = 'a';
+					strBuf[j++] = 'm';
+					strBuf[j++] = 'p';
+				} else if (value->val.str[i] == '<') {
+					strBuf[j++] = '&';
+					strBuf[j++] = 'l';
+					strBuf[j++] = 't';
+				} else if (value->val.str[i] == '>') {
+					strBuf[j++] = '&';
+					strBuf[j++] = 'g';
+					strBuf[j++] = 't';
+				} else if (value->val.str[i] == '"') {
+					strBuf[j++] = '&';
+					strBuf[j++] = 'q';
+					strBuf[j++] = 'u';
+					strBuf[j++] = 'o';
+					strBuf[j++] = 't';
+				} else if (value->val.str[i] == '\0') {
+					strBuf[j++] = ' ';
+				} else {
+					strBuf[j++] = value->val.str[i];
+				}
+			}
+
+			strBuf[j] = '\0';
+		}
+
+		appender(data, "value=\"");
+		appender(data, strBuf);
+		appender(data, "\" />\n");
+		free(strBuf);
+		break;
+	}
+
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+	{
+		char *strBuf;
+
+		appender(data, indent);
+		appender(data, "<url value=\"");
+		strBuf = strndup(value->val.str, value->unitSize - 1);
+		appender(data, strBuf);
+		free(strBuf);
+		appender(data, "\" />\n");
+		break;
+	}
+
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		appender(data, indent);
+		appender(data, "<sequence>\n");
+
+		convert_raw_data_to_xml(value->val.dataseq,
+					indent_level + 1, data, appender);
+
+		appender(data, indent);
+		appender(data, "</sequence>\n");
+
+		break;
+
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		appender(data, indent);
+
+		appender(data, "<alternate>\n");
+
+		convert_raw_data_to_xml(value->val.dataseq,
+					indent_level + 1, data, appender);
+		appender(data, indent);
+
+		appender(data, "</alternate>\n");
+
+		break;
+	}
+
+	convert_raw_data_to_xml(value->next, indent_level, data, appender);
+}
+
+struct conversion_data {
+	void *data;
+	void (*appender)(void *data, const char *);
+};
+
+static void convert_raw_attr_to_xml_func(void *val, void *data)
+{
+	struct conversion_data *cd = data;
+	sdp_data_t *value = val;
+	char buf[STRBUFSIZE];
+
+	buf[STRBUFSIZE - 1] = '\0';
+	snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n",
+		 value->attrId);
+	cd->appender(cd->data, buf);
+
+	convert_raw_data_to_xml(value, 2, cd->data, cd->appender);
+
+	cd->appender(cd->data, "\t</attribute>\n");
+}
+
+/*
+ * Will convert the sdp record to XML.  The appender and data can be used
+ * to control where to output the record (e.g. file or a data buffer).  The
+ * appender will be called repeatedly with data and the character buffer
+ * (containing parts of the generated XML) to append.
+ */
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+			void *data, void (*appender)(void *, const char *))
+{
+	struct conversion_data cd;
+
+	cd.data = data;
+	cd.appender = appender;
+
+	if (rec && rec->attrlist) {
+		appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n");
+		appender(data, "<record>\n");
+		sdp_list_foreach(rec->attrlist,
+				 convert_raw_attr_to_xml_func, &cd);
+		appender(data, "</record>\n");
+	}
+}
diff --git a/bluez/repo/src/sdp-xml.h b/bluez/repo/src/sdp-xml.h
new file mode 100644
index 0000000..80a4f44
--- /dev/null
+++ b/bluez/repo/src/sdp-xml.h
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-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
+ *
+ */
+
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+		void *user_data, void (*append_func) (void *, const char *));
+
+sdp_record_t *sdp_xml_parse_record(const char *data, int size);
diff --git a/bluez/repo/src/sdpd-database.c b/bluez/repo/src/sdpd-database.c
new file mode 100644
index 0000000..843b6d0
--- /dev/null
+++ b/bluez/repo/src/sdpd-database.c
@@ -0,0 +1,305 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "sdpd.h"
+#include "log.h"
+
+static sdp_list_t *service_db;
+static sdp_list_t *access_db;
+
+typedef struct {
+	uint32_t handle;
+	bdaddr_t device;
+} sdp_access_t;
+
+/*
+ * Ordering function called when inserting a service record.
+ * The service repository is a linked list in sorted order
+ * and the service record handle is the sort key
+ */
+int record_sort(const void *r1, const void *r2)
+{
+	const sdp_record_t *rec1 = r1;
+	const sdp_record_t *rec2 = r2;
+
+	if (!rec1 || !rec2) {
+		error("NULL RECORD LIST FATAL");
+		return -1;
+	}
+
+	return rec1->handle - rec2->handle;
+}
+
+static int access_sort(const void *r1, const void *r2)
+{
+	const sdp_access_t *rec1 = r1;
+	const sdp_access_t *rec2 = r2;
+
+	if (!rec1 || !rec2) {
+		error("NULL RECORD LIST FATAL");
+		return -1;
+	}
+
+	return rec1->handle - rec2->handle;
+}
+
+static void access_free(void *p)
+{
+	free(p);
+}
+
+/*
+ * Reset the service repository by deleting its contents
+ */
+void sdp_svcdb_reset(void)
+{
+	sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free);
+	service_db = NULL;
+
+	sdp_list_free(access_db, access_free);
+	access_db = NULL;
+}
+
+typedef struct _indexed {
+	int sock;
+	sdp_record_t *record;
+} sdp_indexed_t;
+
+static sdp_list_t *socket_index;
+
+/*
+ * collect all services registered over this socket
+ */
+void sdp_svcdb_collect_all(int sock)
+{
+	sdp_list_t *p, *q;
+
+	for (p = socket_index, q = 0; p; ) {
+		sdp_indexed_t *item = p->data;
+		if (item->sock == sock) {
+			sdp_list_t *next = p->next;
+			sdp_record_remove(item->record->handle);
+			sdp_record_free(item->record);
+			free(item);
+			if (q)
+				q->next = next;
+			else
+				socket_index = next;
+			free(p);
+			p = next;
+		} else if (item->sock > sock)
+			return;
+		else {
+			q = p;
+			p = p->next;
+		}
+	}
+}
+
+void sdp_svcdb_collect(sdp_record_t *rec)
+{
+	sdp_list_t *p, *q;
+
+	for (p = socket_index, q = 0; p; q = p, p = p->next) {
+		sdp_indexed_t *item = p->data;
+		if (rec == item->record) {
+			free(item);
+			if (q)
+				q->next = p->next;
+			else
+				socket_index = p->next;
+			free(p);
+			return;
+		}
+	}
+}
+
+static int compare_indices(const void *i1, const void *i2)
+{
+	const sdp_indexed_t *s1 = i1;
+	const sdp_indexed_t *s2 = i2;
+	return s1->sock - s2->sock;
+}
+
+void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
+{
+	sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
+
+	if (!item) {
+		SDPDBG("No memory");
+		return;
+	}
+
+	item->sock = sock;
+	item->record = record;
+	socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
+}
+
+/*
+ * Add a service record to the repository
+ */
+void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
+{
+	sdp_access_t *dev;
+
+	SDPDBG("Adding rec : 0x%lx", (long) rec);
+	SDPDBG("with handle : 0x%x", rec->handle);
+
+	service_db = sdp_list_insert_sorted(service_db, rec, record_sort);
+
+	dev = malloc(sizeof(*dev));
+	if (!dev)
+		return;
+
+	bacpy(&dev->device, device);
+	dev->handle = rec->handle;
+
+	access_db = sdp_list_insert_sorted(access_db, dev, access_sort);
+}
+
+static sdp_list_t *record_locate(uint32_t handle)
+{
+	if (service_db) {
+		sdp_list_t *p;
+		sdp_record_t r;
+
+		r.handle = handle;
+		p = sdp_list_find(service_db, &r, record_sort);
+		return p;
+	}
+
+	SDPDBG("Could not find svcRec for : 0x%x", handle);
+	return NULL;
+}
+
+static sdp_list_t *access_locate(uint32_t handle)
+{
+	if (access_db) {
+		sdp_list_t *p;
+		sdp_access_t a;
+
+		a.handle = handle;
+		p = sdp_list_find(access_db, &a, access_sort);
+		return p;
+	}
+
+	SDPDBG("Could not find access data for : 0x%x", handle);
+	return NULL;
+}
+
+/*
+ * Given a service record handle, find the record associated with it.
+ */
+sdp_record_t *sdp_record_find(uint32_t handle)
+{
+	sdp_list_t *p = record_locate(handle);
+
+	if (!p) {
+		SDPDBG("Couldn't find record for : 0x%x", handle);
+		return 0;
+	}
+
+	return p->data;
+}
+
+/*
+ * Given a service record handle, remove its record from the repository
+ */
+int sdp_record_remove(uint32_t handle)
+{
+	sdp_list_t *p = record_locate(handle);
+	sdp_record_t *r;
+	sdp_access_t *a;
+
+	if (!p) {
+		error("Remove : Couldn't find record for : 0x%x", handle);
+		return -1;
+	}
+
+	r = p->data;
+	if (r)
+		service_db = sdp_list_remove(service_db, r);
+
+	p = access_locate(handle);
+	if (p == NULL || p->data == NULL)
+		return 0;
+
+	a = p->data;
+
+	access_db = sdp_list_remove(access_db, a);
+	access_free(a);
+
+	return 0;
+}
+
+/*
+ * Return a pointer to the linked list containing the records in sorted order
+ */
+sdp_list_t *sdp_get_record_list(void)
+{
+	return service_db;
+}
+
+int sdp_check_access(uint32_t handle, bdaddr_t *device)
+{
+	sdp_list_t *p = access_locate(handle);
+	sdp_access_t *a;
+
+	if (!p)
+		return 1;
+
+	a = p->data;
+	if (!a)
+		return 1;
+
+	if (bacmp(&a->device, device) &&
+			bacmp(&a->device, BDADDR_ANY) &&
+			bacmp(device, BDADDR_ANY))
+		return 0;
+
+	return 1;
+}
+
+uint32_t sdp_next_handle(void)
+{
+	uint32_t handle = 0x10000;
+
+	while (sdp_record_find(handle))
+		handle++;
+
+	return handle;
+}
diff --git a/bluez/repo/src/sdpd-request.c b/bluez/repo/src/sdpd-request.c
new file mode 100644
index 0000000..1eefdce
--- /dev/null
+++ b/bluez/repo/src/sdpd-request.c
@@ -0,0 +1,1119 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdbool.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "src/shared/util.h"
+
+#include "sdpd.h"
+#include "log.h"
+
+typedef struct {
+	uint32_t timestamp;
+	union {
+		uint16_t maxBytesSent;
+		uint16_t lastIndexSent;
+	} cStateValue;
+} sdp_cont_state_t;
+
+#define SDP_CONT_STATE_SIZE (sizeof(uint8_t) + sizeof(sdp_cont_state_t))
+
+#define MIN(x, y) ((x) < (y)) ? (x): (y)
+
+typedef struct _sdp_cstate_list sdp_cstate_list_t;
+
+struct _sdp_cstate_list {
+	sdp_cstate_list_t *next;
+	uint32_t timestamp;
+	sdp_buf_t buf;
+};
+
+static sdp_cstate_list_t *cstates;
+
+/* FIXME: should probably remove it when it's found */
+static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate)
+{
+	sdp_cstate_list_t *p;
+
+	for (p = cstates; p; p = p->next)
+		if (p->timestamp == cstate->timestamp)
+			return &p->buf;
+	return 0;
+}
+
+static uint32_t sdp_cstate_alloc_buf(sdp_buf_t *buf)
+{
+	sdp_cstate_list_t *cstate = malloc(sizeof(sdp_cstate_list_t));
+	uint8_t *data = malloc(buf->data_size);
+
+	memcpy(data, buf->data, buf->data_size);
+	memset((char *)cstate, 0, sizeof(sdp_cstate_list_t));
+	cstate->buf.data = data;
+	cstate->buf.data_size = buf->data_size;
+	cstate->buf.buf_size = buf->data_size;
+	cstate->timestamp = sdp_get_time();
+	cstate->next = cstates;
+	cstates = cstate;
+	return cstate->timestamp;
+}
+
+/* Additional values for checking datatype (not in spec) */
+#define SDP_TYPE_UUID	0xfe
+#define SDP_TYPE_ATTRID	0xff
+
+struct attrid {
+	uint8_t dtd;
+	union {
+		uint16_t uint16;
+		uint32_t uint32;
+	};
+};
+
+/*
+ * Generic data element sequence extractor. Builds
+ * a list whose elements are those found in the
+ * sequence. The data type of elements found in the
+ * sequence is returned in the reference pDataType
+ */
+static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *pDataType, uint8_t expectedType)
+{
+	uint8_t seqType;
+	int scanned, data_size = 0;
+	short numberOfElements = 0;
+	int seqlen = 0;
+	sdp_list_t *pSeq = NULL;
+	uint8_t dataType;
+	int status = 0;
+	const uint8_t *p;
+	size_t bufsize;
+
+	scanned = sdp_extract_seqtype(buf, len, &seqType, &data_size);
+
+	SDPDBG("Seq type : %d", seqType);
+	if (!scanned || (seqType != SDP_SEQ8 && seqType != SDP_SEQ16)) {
+		error("Unknown seq type");
+		return -1;
+	}
+	p = buf + scanned;
+	bufsize = len - scanned;
+
+	SDPDBG("Data size : %d", data_size);
+
+	for (;;) {
+		char *pElem = NULL;
+		int localSeqLength = 0;
+		uuid_t *puuid;
+
+		if (bufsize < sizeof(uint8_t)) {
+			SDPDBG("->Unexpected end of buffer");
+			goto failed;
+		}
+
+		dataType = *p;
+
+		SDPDBG("Data type: 0x%02x", dataType);
+
+		if (expectedType == SDP_TYPE_UUID) {
+			if (dataType != SDP_UUID16 && dataType != SDP_UUID32 && dataType != SDP_UUID128) {
+				SDPDBG("->Unexpected Data type (expected UUID_ANY)");
+				goto failed;
+			}
+		} else if (expectedType == SDP_TYPE_ATTRID &&
+				(dataType != SDP_UINT16 && dataType != SDP_UINT32)) {
+			SDPDBG("->Unexpected Data type (expected 0x%02x or 0x%02x)",
+								SDP_UINT16, SDP_UINT32);
+			goto failed;
+		} else if (expectedType != SDP_TYPE_ATTRID && dataType != expectedType) {
+			SDPDBG("->Unexpected Data type (expected 0x%02x)", expectedType);
+			goto failed;
+		}
+
+		switch (dataType) {
+		case SDP_UINT16:
+			p += sizeof(uint8_t);
+			seqlen += sizeof(uint8_t);
+			bufsize -= sizeof(uint8_t);
+			if (bufsize < sizeof(uint16_t)) {
+				SDPDBG("->Unexpected end of buffer");
+				goto failed;
+			}
+
+			if (expectedType == SDP_TYPE_ATTRID) {
+				struct attrid *aid;
+				aid = malloc(sizeof(struct attrid));
+				aid->dtd = dataType;
+				aid->uint16 = get_be16(p);
+				pElem = (char *) aid;
+			} else {
+				uint16_t tmp;
+
+				memcpy(&tmp, p, sizeof(tmp));
+
+				pElem = malloc(sizeof(uint16_t));
+				put_be16(tmp, pElem);
+			}
+			p += sizeof(uint16_t);
+			seqlen += sizeof(uint16_t);
+			bufsize -= sizeof(uint16_t);
+			break;
+		case SDP_UINT32:
+			p += sizeof(uint8_t);
+			seqlen += sizeof(uint8_t);
+			bufsize -= sizeof(uint8_t);
+			if (bufsize < (int)sizeof(uint32_t)) {
+				SDPDBG("->Unexpected end of buffer");
+				goto failed;
+			}
+
+			if (expectedType == SDP_TYPE_ATTRID) {
+				struct attrid *aid;
+				aid = malloc(sizeof(struct attrid));
+				aid->dtd = dataType;
+				aid->uint32 = get_be32(p);
+
+				pElem = (char *) aid;
+			} else {
+				uint32_t tmp;
+
+				memcpy(&tmp, p, sizeof(tmp));
+
+				pElem = malloc(sizeof(uint32_t));
+				put_be32(tmp, pElem);
+			}
+			p += sizeof(uint32_t);
+			seqlen += sizeof(uint32_t);
+			bufsize -= sizeof(uint32_t);
+			break;
+		case SDP_UUID16:
+		case SDP_UUID32:
+		case SDP_UUID128:
+			puuid = malloc(sizeof(uuid_t));
+			status = sdp_uuid_extract(p, bufsize, puuid, &localSeqLength);
+			if (status < 0) {
+				free(puuid);
+				goto failed;
+			}
+
+			pElem = (char *) puuid;
+			seqlen += localSeqLength;
+			p += localSeqLength;
+			bufsize -= localSeqLength;
+			break;
+		default:
+			return -1;
+		}
+		if (status == 0) {
+			pSeq = sdp_list_append(pSeq, pElem);
+			numberOfElements++;
+			SDPDBG("No of elements : %d", numberOfElements);
+
+			if (seqlen == data_size)
+				break;
+			else if (seqlen > data_size || seqlen > len)
+				goto failed;
+		} else
+			free(pElem);
+	}
+	*svcReqSeq = pSeq;
+	scanned += seqlen;
+	*pDataType = dataType;
+	return scanned;
+
+failed:
+	sdp_list_free(pSeq, free);
+	return -1;
+}
+
+static int sdp_set_cstate_pdu(sdp_buf_t *buf, sdp_cont_state_t *cstate)
+{
+	uint8_t *pdata = buf->data + buf->data_size;
+	int length = 0;
+
+	if (cstate) {
+		SDPDBG("Non null sdp_cstate_t id : 0x%x", cstate->timestamp);
+		*pdata = sizeof(sdp_cont_state_t);
+		pdata += sizeof(uint8_t);
+		length += sizeof(uint8_t);
+		memcpy(pdata, cstate, sizeof(sdp_cont_state_t));
+		length += sizeof(sdp_cont_state_t);
+	} else {
+		/* set "null" continuation state */
+		*pdata = 0;
+		length += sizeof(uint8_t);
+	}
+	buf->data_size += length;
+	return length;
+}
+
+static int sdp_cstate_get(uint8_t *buffer, size_t len,
+						sdp_cont_state_t **cstate)
+{
+	uint8_t cStateSize = *buffer;
+
+	SDPDBG("Continuation State size : %d", cStateSize);
+
+	if (cStateSize == 0) {
+		*cstate = NULL;
+		return 0;
+	}
+
+	buffer++;
+	len--;
+
+	if (len < sizeof(sdp_cont_state_t))
+		return -EINVAL;
+
+	/*
+	 * Check if continuation state exists, if yes attempt
+	 * to get response remainder from cache, else send error
+	 */
+
+	*cstate = malloc(sizeof(sdp_cont_state_t));
+	if (!(*cstate))
+		return -ENOMEM;
+
+	memcpy(*cstate, buffer, sizeof(sdp_cont_state_t));
+
+	SDPDBG("Cstate TS : 0x%x", (*cstate)->timestamp);
+	SDPDBG("Bytes sent : %d", (*cstate)->cStateValue.maxBytesSent);
+
+	return 0;
+}
+
+/*
+ * The matching process is defined as "each and every UUID
+ * specified in the "search pattern" must be present in the
+ * "target pattern". Here "search pattern" is the set of UUIDs
+ * specified by the service discovery client and "target pattern"
+ * is the set of UUIDs present in a service record.
+ *
+ * Return 1 if each and every UUID in the search
+ * pattern exists in the target pattern, 0 if the
+ * match succeeds and -1 on error.
+ */
+static int sdp_match_uuid(sdp_list_t *search, sdp_list_t *pattern)
+{
+	/*
+	 * The target is a sorted list, so we need not look
+	 * at all elements to confirm existence of an element
+	 * from the search pattern
+	 */
+	int patlen = sdp_list_len(pattern);
+
+	if (patlen < sdp_list_len(search))
+		return -1;
+	for (; search; search = search->next) {
+		uuid_t *uuid128;
+		void *data = search->data;
+		sdp_list_t *list;
+		if (data == NULL)
+			return -1;
+
+		/* create 128-bit form of the search UUID */
+		uuid128 = sdp_uuid_to_uuid128((uuid_t *)data);
+		list = sdp_list_find(pattern, uuid128, sdp_uuid128_cmp);
+		bt_free(uuid128);
+		if (!list)
+			return 0;
+	}
+	return 1;
+}
+
+/*
+ * Service search request PDU. This method extracts the search pattern
+ * (a sequence of UUIDs) and calls the matching function
+ * to find matching services
+ */
+static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+	int status = 0, i, plen, mlen, mtu, scanned;
+	sdp_list_t *pattern = NULL;
+	uint16_t expected, actual, rsp_count = 0;
+	uint8_t dtd;
+	sdp_cont_state_t *cstate = NULL;
+	uint8_t *pCacheBuffer = NULL;
+	int handleSize = 0;
+	uint32_t cStateId = 0;
+	uint8_t *pTotalRecordCount, *pCurrentRecordCount;
+	uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+	size_t data_left = req->len - sizeof(sdp_pdu_hdr_t);
+
+	scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID);
+
+	if (scanned == -1) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+	pdata += scanned;
+	data_left -= scanned;
+
+	plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+	mlen = scanned + sizeof(uint16_t) + 1;
+	/* ensure we don't read past buffer */
+	if (plen < mlen || plen != mlen + *(uint8_t *)(pdata+sizeof(uint16_t))) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	if (data_left < sizeof(uint16_t)) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	expected = get_be16(pdata);
+
+	SDPDBG("Expected count: %d", expected);
+	SDPDBG("Bytes scanned : %d", scanned);
+
+	pdata += sizeof(uint16_t);
+	data_left -= sizeof(uint16_t);
+
+	/*
+	 * Check if continuation state exists, if yes attempt
+	 * to get rsp remainder from cache, else send error
+	 */
+	if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	mtu = req->mtu - sizeof(sdp_pdu_hdr_t) - sizeof(uint16_t) - sizeof(uint16_t) - SDP_CONT_STATE_SIZE;
+	actual = MIN(expected, mtu >> 2);
+
+	/* make space in the rsp buffer for total and current record counts */
+	pdata = buf->data;
+
+	/* total service record count = 0 */
+	pTotalRecordCount = pdata;
+	put_be16(0, pdata);
+	pdata += sizeof(uint16_t);
+	buf->data_size += sizeof(uint16_t);
+
+	/* current service record count = 0 */
+	pCurrentRecordCount = pdata;
+	put_be16(0, pdata);
+	pdata += sizeof(uint16_t);
+	buf->data_size += sizeof(uint16_t);
+
+	if (cstate == NULL) {
+		/* for every record in the DB, do a pattern search */
+		sdp_list_t *list = sdp_get_record_list();
+
+		handleSize = 0;
+		for (; list && rsp_count < expected; list = list->next) {
+			sdp_record_t *rec = list->data;
+
+			SDPDBG("Checking svcRec : 0x%x", rec->handle);
+
+			if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
+					sdp_check_access(rec->handle, &req->device)) {
+				rsp_count++;
+				put_be32(rec->handle, pdata);
+				pdata += sizeof(uint32_t);
+				handleSize += sizeof(uint32_t);
+			}
+		}
+
+		SDPDBG("Match count: %d", rsp_count);
+
+		buf->data_size += handleSize;
+		put_be16(rsp_count, pTotalRecordCount);
+		put_be16(rsp_count, pCurrentRecordCount);
+
+		if (rsp_count > actual) {
+			/* cache the rsp and generate a continuation state */
+			cStateId = sdp_cstate_alloc_buf(buf);
+			/*
+			 * subtract handleSize since we now send only
+			 * a subset of handles
+			 */
+			buf->data_size -= handleSize;
+		} else {
+			/* NULL continuation state */
+			sdp_set_cstate_pdu(buf, NULL);
+		}
+	}
+
+	/* under both the conditions below, the rsp buffer is not built yet */
+	if (cstate || cStateId > 0) {
+		short lastIndex = 0;
+
+		if (cstate) {
+			/*
+			 * Get the previous sdp_cont_state_t and obtain
+			 * the cached rsp
+			 */
+			sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+			if (pCache) {
+				pCacheBuffer = pCache->data;
+				/* get the rsp_count from the cached buffer */
+				rsp_count = get_be16(pCacheBuffer);
+
+				/* get index of the last sdp_record_t sent */
+				lastIndex = cstate->cStateValue.lastIndexSent;
+			} else {
+				status = SDP_INVALID_CSTATE;
+				goto done;
+			}
+		} else {
+			pCacheBuffer = buf->data;
+			lastIndex = 0;
+		}
+
+		/*
+		 * Set the local buffer pointer to after the
+		 * current record count and increment the cached
+		 * buffer pointer to beyond the counters
+		 */
+		pdata = pCurrentRecordCount + sizeof(uint16_t);
+
+		/* increment beyond the totalCount and the currentCount */
+		pCacheBuffer += 2 * sizeof(uint16_t);
+
+		if (cstate) {
+			handleSize = 0;
+			for (i = lastIndex; (i - lastIndex) < actual && i < rsp_count; i++) {
+				memcpy(pdata, pCacheBuffer + i * sizeof(uint32_t), sizeof(uint32_t));
+				pdata += sizeof(uint32_t);
+				handleSize += sizeof(uint32_t);
+			}
+		} else {
+			handleSize = actual << 2;
+			i = actual;
+		}
+
+		buf->data_size += handleSize;
+		put_be16(rsp_count, pTotalRecordCount);
+		put_be16(i - lastIndex, pCurrentRecordCount);
+
+		if (i == rsp_count) {
+			/* set "null" continuationState */
+			sdp_set_cstate_pdu(buf, NULL);
+		} else {
+			/*
+			 * there's more: set lastIndexSent to
+			 * the new value and move on
+			 */
+			sdp_cont_state_t newState;
+
+			SDPDBG("Setting non-NULL sdp_cstate_t");
+
+			if (cstate)
+				memcpy(&newState, cstate, sizeof(sdp_cont_state_t));
+			else {
+				memset(&newState, 0, sizeof(sdp_cont_state_t));
+				newState.timestamp = cStateId;
+			}
+			newState.cStateValue.lastIndexSent = i;
+			sdp_set_cstate_pdu(buf, &newState);
+		}
+	}
+
+done:
+	free(cstate);
+	if (pattern)
+		sdp_list_free(pattern, free);
+
+	return status;
+}
+
+/*
+ * Extract attribute identifiers from the request PDU.
+ * Clients could request a subset of attributes (by id)
+ * from a service record, instead of the whole set. The
+ * requested identifiers are present in the PDU form of
+ * the request
+ */
+static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf)
+{
+	sdp_buf_t pdu;
+
+	if (!rec)
+		return SDP_INVALID_RECORD_HANDLE;
+
+	if (seq == NULL) {
+		SDPDBG("Attribute sequence is NULL");
+		return 0;
+	}
+
+	SDPDBG("Entries in attr seq : %d", sdp_list_len(seq));
+
+	sdp_gen_record_pdu(rec, &pdu);
+
+	for (; seq; seq = seq->next) {
+		struct attrid *aid = seq->data;
+
+		SDPDBG("AttrDataType : %d", aid->dtd);
+
+		if (aid->dtd == SDP_UINT16) {
+			uint16_t attr = aid->uint16;
+			sdp_data_t *a = sdp_data_get(rec, attr);
+			if (a)
+				sdp_append_to_pdu(buf, a);
+		} else if (aid->dtd == SDP_UINT32) {
+			uint32_t range = aid->uint32;
+			uint16_t attr;
+			uint16_t low = (0xffff0000 & range) >> 16;
+			uint16_t high = 0x0000ffff & range;
+			sdp_data_t *data;
+
+			SDPDBG("attr range : 0x%x", range);
+			SDPDBG("Low id : 0x%x", low);
+			SDPDBG("High id : 0x%x", high);
+
+			if (low == 0x0000 && high == 0xffff && pdu.data_size <= buf->buf_size) {
+				/* copy it */
+				memcpy(buf->data, pdu.data, pdu.data_size);
+				buf->data_size = pdu.data_size;
+				break;
+			}
+			/* (else) sub-range of attributes */
+			for (attr = low; attr < high; attr++) {
+				data = sdp_data_get(rec, attr);
+				if (data)
+					sdp_append_to_pdu(buf, data);
+			}
+			data = sdp_data_get(rec, high);
+			if (data)
+				sdp_append_to_pdu(buf, data);
+		} else {
+			error("Unexpected data type : 0x%x", aid->dtd);
+			error("Expect uint16_t or uint32_t");
+			free(pdu.data);
+			return SDP_INVALID_SYNTAX;
+		}
+	}
+
+	free(pdu.data);
+
+	return 0;
+}
+
+/*
+ * A request for the attributes of a service record.
+ * First check if the service record (specified by
+ * service record handle) exists, then call the attribute
+ * streaming function
+ */
+static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+	sdp_cont_state_t *cstate = NULL;
+	uint8_t *pResponse = NULL;
+	short cstate_size = 0;
+	sdp_list_t *seq = NULL;
+	uint8_t dtd = 0;
+	int scanned = 0;
+	unsigned int max_rsp_size;
+	int status = 0, plen, mlen;
+	uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+	size_t data_left = req->len - sizeof(sdp_pdu_hdr_t);
+	uint32_t handle;
+
+	if (data_left < sizeof(uint32_t)) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	handle = get_be32(pdata);
+
+	pdata += sizeof(uint32_t);
+	data_left -= sizeof(uint32_t);
+
+	if (data_left < sizeof(uint16_t)) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	max_rsp_size = get_be16(pdata);
+
+	pdata += sizeof(uint16_t);
+	data_left -= sizeof(uint16_t);
+
+	if (data_left < sizeof(sdp_pdu_hdr_t)) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	/* extract the attribute list */
+	scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID);
+	if (scanned == -1) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+	pdata += scanned;
+	data_left -= scanned;
+
+	plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+	mlen = scanned + sizeof(uint32_t) + sizeof(uint16_t) + 1;
+	/* ensure we don't read past buffer */
+	if (plen < mlen || plen != mlen + *(uint8_t *)pdata) {
+		status = SDP_INVALID_PDU_SIZE;
+		goto done;
+	}
+
+	/*
+	 * if continuation state exists, attempt
+	 * to get rsp remainder from cache, else send error
+	 */
+	if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	SDPDBG("SvcRecHandle : 0x%x", handle);
+	SDPDBG("max_rsp_size : %d", max_rsp_size);
+
+	/*
+	 * Check that max_rsp_size is within valid range
+	 * a minimum size of 0x0007 has to be used for data field
+	 */
+	if (max_rsp_size < 0x0007) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	/*
+	 * Calculate Attribute size according to MTU
+	 * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
+	 */
+	max_rsp_size = MIN(max_rsp_size, req->mtu - sizeof(sdp_pdu_hdr_t) -
+			sizeof(uint32_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
+
+	/* pull header for AttributeList byte count */
+	buf->data += sizeof(uint16_t);
+	buf->buf_size -= sizeof(uint16_t);
+
+	if (cstate) {
+		sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+
+		SDPDBG("Obtained cached rsp : %p", pCache);
+
+		if (pCache) {
+			short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent);
+			pResponse = pCache->data;
+			memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent);
+			buf->data_size += sent;
+			cstate->cStateValue.maxBytesSent += sent;
+
+			SDPDBG("Response size : %d sending now : %d bytes sent so far : %d",
+				pCache->data_size, sent, cstate->cStateValue.maxBytesSent);
+			if (cstate->cStateValue.maxBytesSent == pCache->data_size)
+				cstate_size = sdp_set_cstate_pdu(buf, NULL);
+			else
+				cstate_size = sdp_set_cstate_pdu(buf, cstate);
+		} else {
+			status = SDP_INVALID_CSTATE;
+			error("NULL cache buffer and non-NULL continuation state");
+		}
+	} else {
+		sdp_record_t *rec = sdp_record_find(handle);
+		status = extract_attrs(rec, seq, buf);
+		if (buf->data_size > max_rsp_size) {
+			sdp_cont_state_t newState;
+
+			memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
+			newState.timestamp = sdp_cstate_alloc_buf(buf);
+			/*
+			 * Reset the buffer size to the maximum expected and
+			 * set the sdp_cont_state_t
+			 */
+			SDPDBG("Creating continuation state of size : %d", buf->data_size);
+			buf->data_size = max_rsp_size;
+			newState.cStateValue.maxBytesSent = max_rsp_size;
+			cstate_size = sdp_set_cstate_pdu(buf, &newState);
+		} else {
+			if (buf->data_size == 0)
+				sdp_append_to_buf(buf, NULL, 0);
+			cstate_size = sdp_set_cstate_pdu(buf, NULL);
+		}
+	}
+
+	/* push header */
+	buf->data -= sizeof(uint16_t);
+	buf->buf_size += sizeof(uint16_t);
+
+done:
+	free(cstate);
+	if (seq)
+		sdp_list_free(seq, free);
+	if (status)
+		return status;
+
+	/* set attribute list byte count */
+	put_be16(buf->data_size - cstate_size, buf->data);
+	buf->data_size += sizeof(uint16_t);
+	return 0;
+}
+
+/*
+ * combined service search and attribute extraction
+ */
+static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+	int status = 0, plen, totscanned;
+	uint8_t *pdata, *pResponse = NULL;
+	unsigned int max;
+	int scanned, rsp_count = 0;
+	sdp_list_t *pattern = NULL, *seq = NULL, *svcList;
+	sdp_cont_state_t *cstate = NULL;
+	short cstate_size = 0;
+	uint8_t dtd = 0;
+	sdp_buf_t tmpbuf;
+	size_t data_left;
+
+	tmpbuf.data = NULL;
+	pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+	data_left = req->len - sizeof(sdp_pdu_hdr_t);
+	scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID);
+	if (scanned == -1) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+	totscanned = scanned;
+
+	SDPDBG("Bytes scanned: %d", scanned);
+
+	pdata += scanned;
+	data_left -= scanned;
+
+	if (data_left < sizeof(uint16_t)) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	max = get_be16(pdata);
+
+	pdata += sizeof(uint16_t);
+	data_left -= sizeof(uint16_t);
+
+	SDPDBG("Max Attr expected: %d", max);
+
+	if (data_left < sizeof(sdp_pdu_hdr_t)) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	/* extract the attribute list */
+	scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID);
+	if (scanned == -1) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	pdata += scanned;
+	data_left -= scanned;
+
+	totscanned += scanned + sizeof(uint16_t) + 1;
+
+	plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+	if (plen < totscanned || plen != totscanned + *(uint8_t *)pdata) {
+		status = SDP_INVALID_PDU_SIZE;
+		goto done;
+	}
+
+	/*
+	 * if continuation state exists attempt
+	 * to get rsp remainder from cache, else send error
+	 */
+	if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	svcList = sdp_get_record_list();
+
+	tmpbuf.data = malloc(USHRT_MAX);
+	tmpbuf.data_size = 0;
+	tmpbuf.buf_size = USHRT_MAX;
+	memset(tmpbuf.data, 0, USHRT_MAX);
+
+	/*
+	 * Calculate Attribute size according to MTU
+	 * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
+	 */
+	max = MIN(max, req->mtu - sizeof(sdp_pdu_hdr_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
+
+	/* pull header for AttributeList byte count */
+	buf->data += sizeof(uint16_t);
+	buf->buf_size -= sizeof(uint16_t);
+
+	if (cstate == NULL) {
+		/* no continuation state -> create new response */
+		sdp_list_t *p;
+		for (p = svcList; p; p = p->next) {
+			sdp_record_t *rec = p->data;
+			if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
+					sdp_check_access(rec->handle, &req->device)) {
+				rsp_count++;
+				status = extract_attrs(rec, seq, &tmpbuf);
+
+				SDPDBG("Response count : %d", rsp_count);
+				SDPDBG("Local PDU size : %d", tmpbuf.data_size);
+				if (status) {
+					SDPDBG("Extract attr from record returns err");
+					break;
+				}
+				if (buf->data_size + tmpbuf.data_size < buf->buf_size) {
+					/* to be sure no relocations */
+					sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
+					tmpbuf.data_size = 0;
+					memset(tmpbuf.data, 0, USHRT_MAX);
+				} else {
+					error("Relocation needed");
+					break;
+				}
+				SDPDBG("Net PDU size : %d", buf->data_size);
+			}
+		}
+		if (buf->data_size > max) {
+			sdp_cont_state_t newState;
+
+			memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
+			newState.timestamp = sdp_cstate_alloc_buf(buf);
+			/*
+			 * Reset the buffer size to the maximum expected and
+			 * set the sdp_cont_state_t
+			 */
+			buf->data_size = max;
+			newState.cStateValue.maxBytesSent = max;
+			cstate_size = sdp_set_cstate_pdu(buf, &newState);
+		} else
+			cstate_size = sdp_set_cstate_pdu(buf, NULL);
+	} else {
+		/* continuation State exists -> get from cache */
+		sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+		if (pCache) {
+			uint16_t sent = MIN(max, pCache->data_size - cstate->cStateValue.maxBytesSent);
+			pResponse = pCache->data;
+			memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent);
+			buf->data_size += sent;
+			cstate->cStateValue.maxBytesSent += sent;
+			if (cstate->cStateValue.maxBytesSent == pCache->data_size)
+				cstate_size = sdp_set_cstate_pdu(buf, NULL);
+			else
+				cstate_size = sdp_set_cstate_pdu(buf, cstate);
+		} else {
+			status = SDP_INVALID_CSTATE;
+			SDPDBG("Non-null continuation state, but null cache buffer");
+		}
+	}
+
+	if (!rsp_count && !cstate) {
+		/* found nothing */
+		buf->data_size = 0;
+		sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
+		sdp_set_cstate_pdu(buf, NULL);
+	}
+
+	/* push header */
+	buf->data -= sizeof(uint16_t);
+	buf->buf_size += sizeof(uint16_t);
+
+	if (!status) {
+		/* set attribute list byte count */
+		put_be16(buf->data_size - cstate_size, buf->data);
+		buf->data_size += sizeof(uint16_t);
+	}
+
+done:
+	free(cstate);
+	free(tmpbuf.data);
+	if (pattern)
+		sdp_list_free(pattern, free);
+	if (seq)
+		sdp_list_free(seq, free);
+	return status;
+}
+
+/*
+ * Top level request processor. Calls the appropriate processing
+ * function based on request type. Handles service registration
+ * client requests also.
+ */
+static void process_request(sdp_req_t *req)
+{
+	sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)req->buf;
+	sdp_pdu_hdr_t *rsphdr;
+	sdp_buf_t rsp;
+	uint8_t *buf = malloc(USHRT_MAX);
+	int status = SDP_INVALID_SYNTAX;
+
+	memset(buf, 0, USHRT_MAX);
+	rsp.data = buf + sizeof(sdp_pdu_hdr_t);
+	rsp.data_size = 0;
+	rsp.buf_size = USHRT_MAX - sizeof(sdp_pdu_hdr_t);
+	rsphdr = (sdp_pdu_hdr_t *)buf;
+
+	if (ntohs(reqhdr->plen) != req->len - sizeof(sdp_pdu_hdr_t)) {
+		status = SDP_INVALID_PDU_SIZE;
+		goto send_rsp;
+	}
+	switch (reqhdr->pdu_id) {
+	case SDP_SVC_SEARCH_REQ:
+		SDPDBG("Got a svc srch req");
+		status = service_search_req(req, &rsp);
+		rsphdr->pdu_id = SDP_SVC_SEARCH_RSP;
+		break;
+	case SDP_SVC_ATTR_REQ:
+		SDPDBG("Got a svc attr req");
+		status = service_attr_req(req, &rsp);
+		rsphdr->pdu_id = SDP_SVC_ATTR_RSP;
+		break;
+	case SDP_SVC_SEARCH_ATTR_REQ:
+		SDPDBG("Got a svc srch attr req");
+		status = service_search_attr_req(req, &rsp);
+		rsphdr->pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
+		break;
+	/* Following requests are allowed only for local connections */
+	case SDP_SVC_REGISTER_REQ:
+		SDPDBG("Service register request");
+		if (req->local) {
+			status = service_register_req(req, &rsp);
+			rsphdr->pdu_id = SDP_SVC_REGISTER_RSP;
+		}
+		break;
+	case SDP_SVC_UPDATE_REQ:
+		SDPDBG("Service update request");
+		if (req->local) {
+			status = service_update_req(req, &rsp);
+			rsphdr->pdu_id = SDP_SVC_UPDATE_RSP;
+		}
+		break;
+	case SDP_SVC_REMOVE_REQ:
+		SDPDBG("Service removal request");
+		if (req->local) {
+			status = service_remove_req(req, &rsp);
+			rsphdr->pdu_id = SDP_SVC_REMOVE_RSP;
+		}
+		break;
+	default:
+		error("Unknown PDU ID : 0x%x received", reqhdr->pdu_id);
+		status = SDP_INVALID_SYNTAX;
+		break;
+	}
+
+send_rsp:
+	if (status) {
+		rsphdr->pdu_id = SDP_ERROR_RSP;
+		put_be16(status, rsp.data);
+		rsp.data_size = sizeof(uint16_t);
+	}
+
+	SDPDBG("Sending rsp. status %d", status);
+
+	rsphdr->tid  = reqhdr->tid;
+	rsphdr->plen = htons(rsp.data_size);
+
+	/* point back to the real buffer start and set the real rsp length */
+	rsp.data_size += sizeof(sdp_pdu_hdr_t);
+	rsp.data = buf;
+
+	/* stream the rsp PDU */
+	if (send(req->sock, rsp.data, rsp.data_size, 0) < 0)
+		error("send: %s (%d)", strerror(errno), errno);
+
+	SDPDBG("Bytes Sent : %d", rsp.data_size);
+
+	free(rsp.data);
+	free(req->buf);
+}
+
+void handle_internal_request(int sk, int mtu, void *data, int len)
+{
+	sdp_req_t req;
+
+	bacpy(&req.device, BDADDR_ANY);
+	bacpy(&req.bdaddr, BDADDR_LOCAL);
+	req.local = 0;
+	req.sock = sk;
+	req.mtu = mtu;
+	req.flags = 0;
+	req.buf = data;
+	req.len = len;
+
+	process_request(&req);
+}
+
+void handle_request(int sk, uint8_t *data, int len)
+{
+	struct sockaddr_l2 sa;
+	socklen_t size;
+	sdp_req_t req;
+
+	size = sizeof(sa);
+	if (getpeername(sk, (struct sockaddr *) &sa, &size) < 0) {
+		error("getpeername: %s", strerror(errno));
+		return;
+	}
+
+	if (sa.l2_family == AF_BLUETOOTH) {
+		struct l2cap_options lo;
+
+		memset(&lo, 0, sizeof(lo));
+		size = sizeof(lo);
+
+		if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &lo, &size) < 0) {
+			error("getsockopt: %s", strerror(errno));
+			return;
+		}
+
+		bacpy(&req.bdaddr, &sa.l2_bdaddr);
+		req.mtu = lo.omtu;
+		req.local = 0;
+		memset(&sa, 0, sizeof(sa));
+		size = sizeof(sa);
+
+		if (getsockname(sk, (struct sockaddr *) &sa, &size) < 0) {
+			error("getsockname: %s", strerror(errno));
+			return;
+		}
+
+		bacpy(&req.device, &sa.l2_bdaddr);
+	} else {
+		bacpy(&req.device, BDADDR_ANY);
+		bacpy(&req.bdaddr, BDADDR_LOCAL);
+		req.mtu = 2048;
+		req.local = 1;
+	}
+
+	req.sock = sk;
+	req.buf  = data;
+	req.len  = len;
+
+	process_request(&req);
+}
diff --git a/bluez/repo/src/sdpd-server.c b/bluez/repo/src/sdpd-server.c
new file mode 100644
index 0000000..c863508
--- /dev/null
+++ b/bluez/repo/src/sdpd-server.c
@@ -0,0 +1,277 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "log.h"
+#include "sdpd.h"
+
+static guint l2cap_id = 0, unix_id = 0;
+static int l2cap_sock = -1, unix_sock = -1;
+
+/*
+ * SDP server initialization on startup includes creating the
+ * l2cap and unix sockets over which discovery and registration clients
+ * access us respectively
+ */
+static int init_server(uint16_t mtu, int master, int compat)
+{
+	struct l2cap_options opts;
+	struct sockaddr_l2 l2addr;
+	struct sockaddr_un unaddr;
+	socklen_t optlen;
+
+	/* Register the public browse group root */
+	register_public_browse_group();
+
+	/* Register the SDP server's service record */
+	register_server_service();
+
+	/* Create L2CAP socket */
+	l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (l2cap_sock < 0) {
+		error("opening L2CAP socket: %s", strerror(errno));
+		return -1;
+	}
+
+	memset(&l2addr, 0, sizeof(l2addr));
+	l2addr.l2_family = AF_BLUETOOTH;
+	bacpy(&l2addr.l2_bdaddr, BDADDR_ANY);
+	l2addr.l2_psm = htobs(SDP_PSM);
+
+	if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
+		error("binding L2CAP socket: %s", strerror(errno));
+		return -1;
+	}
+
+	if (master) {
+		int opt = L2CAP_LM_MASTER;
+		if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+			error("setsockopt: %s", strerror(errno));
+			return -1;
+		}
+	}
+
+	if (mtu > 0) {
+		memset(&opts, 0, sizeof(opts));
+		optlen = sizeof(opts);
+
+		if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+			error("getsockopt: %s", strerror(errno));
+			return -1;
+		}
+
+		opts.omtu = mtu;
+		opts.imtu = mtu;
+
+		if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+			error("setsockopt: %s", strerror(errno));
+			return -1;
+		}
+	}
+
+	if (listen(l2cap_sock, 5) < 0) {
+		error("listen: %s", strerror(errno));
+		return -1;
+	}
+
+	if (!compat) {
+		unix_sock = -1;
+		return 0;
+	}
+
+	/* Create local Unix socket */
+	unix_sock = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (unix_sock < 0) {
+		error("opening UNIX socket: %s", strerror(errno));
+		return -1;
+	}
+
+	memset(&unaddr, 0, sizeof(unaddr));
+	unaddr.sun_family = AF_UNIX;
+	strcpy(unaddr.sun_path, SDP_UNIX_PATH);
+
+	unlink(unaddr.sun_path);
+
+	if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) {
+		error("binding UNIX socket: %s", strerror(errno));
+		return -1;
+	}
+
+	if (listen(unix_sock, 5) < 0) {
+		error("listen UNIX socket: %s", strerror(errno));
+		return -1;
+	}
+
+	chmod(SDP_UNIX_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+	return 0;
+}
+
+static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	sdp_pdu_hdr_t hdr;
+	uint8_t *buf;
+	int sk, len, size;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	if (cond & (G_IO_HUP | G_IO_ERR)) {
+		sdp_svcdb_collect_all(sk);
+		return FALSE;
+	}
+
+	len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
+	if (len != sizeof(sdp_pdu_hdr_t)) {
+		sdp_svcdb_collect_all(sk);
+		return FALSE;
+	}
+
+	size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
+	buf = malloc(size);
+	if (!buf)
+		return TRUE;
+
+	len = recv(sk, buf, size, 0);
+	/* Check here only that the received message is not empty.
+	 * Incorrect length of message should be processed later
+	 * inside handle_request() in order to produce ErrorResponse.
+	 */
+	if (len <= 0) {
+		sdp_svcdb_collect_all(sk);
+		free(buf);
+		return FALSE;
+	}
+
+	handle_request(sk, buf, len);
+
+	return TRUE;
+}
+
+static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	GIOChannel *io;
+	int nsk;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		return FALSE;
+
+	if (data == &l2cap_sock) {
+		struct sockaddr_l2 addr;
+		socklen_t len = sizeof(addr);
+
+		nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len);
+	} else if (data == &unix_sock) {
+		struct sockaddr_un addr;
+		socklen_t len = sizeof(addr);
+
+		nsk = accept(unix_sock, (struct sockaddr *) &addr, &len);
+	} else
+		return FALSE;
+
+	if (nsk < 0) {
+		error("Can't accept connection: %s", strerror(errno));
+		return TRUE;
+	}
+
+	io = g_io_channel_unix_new(nsk);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					io_session_event, data);
+
+	g_io_channel_unref(io);
+
+	return TRUE;
+}
+
+int start_sdp_server(uint16_t mtu, uint32_t flags)
+{
+	int compat = flags & SDP_SERVER_COMPAT;
+	int master = flags & SDP_SERVER_MASTER;
+	GIOChannel *io;
+
+	info("Starting SDP server");
+
+	if (init_server(mtu, master, compat) < 0) {
+		error("Server initialization failed");
+		return -1;
+	}
+
+	io = g_io_channel_unix_new(l2cap_sock);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	l2cap_id = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					io_accept_event, &l2cap_sock);
+	g_io_channel_unref(io);
+
+	if (compat && unix_sock > fileno(stderr)) {
+		io = g_io_channel_unix_new(unix_sock);
+		g_io_channel_set_close_on_unref(io, TRUE);
+
+		unix_id = g_io_add_watch(io,
+					G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					io_accept_event, &unix_sock);
+		g_io_channel_unref(io);
+	}
+
+	return 0;
+}
+
+void stop_sdp_server(void)
+{
+	info("Stopping SDP server");
+
+	sdp_svcdb_reset();
+
+	if (unix_id > 0)
+		g_source_remove(unix_id);
+
+	if (l2cap_id > 0)
+		g_source_remove(l2cap_id);
+
+	l2cap_id = unix_id = 0;
+	l2cap_sock = unix_sock = -1;
+}
diff --git a/bluez/repo/src/sdpd-service.c b/bluez/repo/src/sdpd-service.c
new file mode 100644
index 0000000..c3ee3eb
--- /dev/null
+++ b/bluez/repo/src/sdpd-service.c
@@ -0,0 +1,953 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "src/shared/util.h"
+#include "sdpd.h"
+#include "log.h"
+
+#define MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO		(1ULL << 0)
+#define MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO		(1ULL << 1)
+#define MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO		(1ULL << 2)
+#define MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO		(1ULL << 3)
+#define MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO		(1ULL << 4)
+#define MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO	(1ULL << 5)
+#define MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP		(1ULL << 6)
+#define MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP		(1ULL << 7)
+#define MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL	(1ULL << 8)
+#define MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL	(1ULL << 9)
+#define MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY	(1ULL << 10)
+#define MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY	(1ULL << 11)
+#define MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE	(1ULL << 12)
+#define MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE	(1ULL << 13)
+#define MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL		(1ULL << 14)
+#define MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL		(1ULL << 15)
+#define MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM	(1ULL << 16)
+#define MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM	(1ULL << 17)
+#define MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM	(1ULL << 18)
+#define MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM	(1ULL << 19)
+#define MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM	(1ULL << 20)
+#define MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM	(1ULL << 21)
+#define MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM	(1ULL << 22)
+#define MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM	(1ULL << 23)
+#define MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM	(1ULL << 24)
+#define MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM	(1ULL << 25)
+#define MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL		(1ULL << 26)
+#define MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL	(1ULL << 27)
+#define MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM	(1ULL << 28)
+#define MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM	(1ULL << 29)
+#define MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM	(1ULL << 30)
+#define MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM	(1ULL << 31)
+#define MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM	(1ULL << 32)
+#define MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM	(1ULL << 33)
+#define MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM	(1ULL << 34)
+#define MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM	(1ULL << 35)
+#define MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM	(1ULL << 36)
+#define MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM	(1ULL << 37)
+
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO	(1ULL << 0)
+#define MPMD_A2DP_SRC_AVRCP_TG_ANSWER_CALL_DURING_AUDIO		(1ULL << 1)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO (1ULL << 2)
+#define MPMD_A2DP_SRC_AVRCP_TG_OUTGOING_CALL_DURING_AUDIO	(1ULL << 3)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO	(1ULL << 4)
+#define MPMD_A2DP_SRC_AVRCP_TG_REJECT_CALL_DURING_AUDIO		(1ULL << 5)
+#define MPMD_HFP_AG_CALL_TERMINATION_DURING_AVP			(1ULL << 6)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP	(1ULL << 7)
+#define MPMD_A2DP_SRC_AVRCP_TG_TERMINATION_DURING_AVP		(1ULL << 8)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL		(1ULL << 9)
+#define MPMD_A2DP_SRC_AVRCP_TG_PRESS_PLAY_DURING_CALL		(1ULL << 10)
+#define MPMD_AVRCP_CT_NO_A2DP_SNK_START_AUDIO_AFTER_PLAY	(1ULL << 11)
+#define MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_AFTER_PLAY		(1ULL << 12)
+#define MPMD_AVRCP_CT_NO_A2DP_SNK_SUSPEND_AUDIO_AFTER_PAUSE	(1ULL << 13)
+#define MPMD_A2DP_SRC_AVRCP_TG_SUSPEND_AUDIO_AFTER_PAUSE	(1ULL << 14)
+#define MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_DURING_DATA_COMM	(1ULL << 15)
+#define MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM	(1ULL << 16)
+#define MPMD_A2DP_SRC_AVRCP_TG_START_DATA_DURING_AUDIO		(1ULL << 17)
+#define MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO	(1ULL << 18)
+
+/* Note: in spec dependency bit position starts from 1 (bit 0 unused?) */
+#define MPS_DEPS_SNIFF_MODE_DURRING_STREAMING	(1ULL << 1)
+#define MPS_DEPS_GAVDP_REQUIREMENTS		(1ULL << 2)
+#define MPS_DEPS_DIS_CONNECTION_ORDER_BEHAVIOR	(1ULL << 3)
+
+/*
+ * default MPS features are all disabled, will be updated if relevant service
+ * is (un)registered
+ */
+#define MPS_MPSD_DEFAULT_FEATURES 0
+#define MPS_MPMD_DEFAULT_FEATURES 0
+
+/*
+ * Those defines bits for all features that depend on specific profile and role.
+ * If profile is not supported then all those bits should not be set in record
+ */
+#define MPS_MPSD_HFP_AG (MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO | \
+			MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO | \
+			MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO | \
+			MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP | \
+			MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL | \
+			MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY | \
+			MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+			MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_HFP_HF (MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO | \
+			MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO | \
+			MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO | \
+			MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP | \
+			MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL | \
+			MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY | \
+			MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+			MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_A2DP_SRC (MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO | \
+			MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO | \
+			MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO | \
+			MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP | \
+			MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL | \
+			MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY | \
+			MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+			MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM | \
+			MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM | \
+			MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_A2DP_SNK (MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO | \
+			MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO | \
+			MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO | \
+			MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP | \
+			MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL | \
+			MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY | \
+			MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+			MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM | \
+			MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM | \
+			MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_AVRCP_CT MPS_MPSD_A2DP_SNK
+
+#define MPS_MPSD_AVRCP_TG MPS_MPSD_A2DP_SRC
+
+#define MPS_MPSD_DUN_GW (MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM | \
+			MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM | \
+			MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_DUN_DT (MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM | \
+			MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM | \
+			MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_PAN_NAP (MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM | \
+			MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_PAN_PANU (MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL | \
+			MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM | \
+			MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM | \
+			MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM | \
+			MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_PBAP_SRC MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM
+
+#define MPS_MPSD_PBAP_CLI MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM
+
+#define MPS_MPSD_ALL (MPS_MPSD_HFP_AG | MPS_MPSD_HFP_HF | \
+			MPS_MPSD_A2DP_SRC | MPS_MPSD_A2DP_SNK | \
+			MPS_MPSD_AVRCP_CT | MPS_MPSD_AVRCP_TG | \
+			MPS_MPSD_DUN_GW | MPS_MPSD_DUN_DT | \
+			MPS_MPSD_PAN_NAP | MPS_MPSD_PAN_PANU | \
+			MPS_MPSD_PBAP_SRC | MPS_MPSD_PBAP_CLI)
+
+#define MPS_MPMD_HFP_AG MPMD_HFP_AG_CALL_TERMINATION_DURING_AVP
+
+#define MPS_MPMD_HFP_HF ( \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL)
+
+#define MPS_MPMD_A2DP_SRC (MPMD_A2DP_SRC_AVRCP_TG_ANSWER_CALL_DURING_AUDIO | \
+			MPMD_A2DP_SRC_AVRCP_TG_OUTGOING_CALL_DURING_AUDIO | \
+			MPMD_A2DP_SRC_AVRCP_TG_REJECT_CALL_DURING_AUDIO | \
+			MPMD_A2DP_SRC_AVRCP_TG_TERMINATION_DURING_AVP | \
+			MPMD_A2DP_SRC_AVRCP_TG_PRESS_PLAY_DURING_CALL | \
+			MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_AFTER_PLAY | \
+			MPMD_A2DP_SRC_AVRCP_TG_SUSPEND_AUDIO_AFTER_PAUSE | \
+			MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_DURING_DATA_COMM | \
+			MPMD_A2DP_SRC_AVRCP_TG_START_DATA_DURING_AUDIO)
+
+#define MPS_MPMD_A2DP_SNK ( \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP | \
+		MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL | \
+		MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM | \
+		MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO)
+
+#define MPS_MPMD_AVRCP_CT MPS_MPMD_A2DP_SNK
+
+/* should be set only if CT is supported but SNK is not supported */
+#define MPS_MPMD_AVRCP_CT_ONLY ( \
+		MPMD_AVRCP_CT_NO_A2DP_SNK_START_AUDIO_AFTER_PLAY | \
+		MPMD_AVRCP_CT_NO_A2DP_SNK_SUSPEND_AUDIO_AFTER_PAUSE)
+
+#define MPS_MPMD_AVRCP_TG MPS_MPMD_A2DP_SRC
+
+#define MPS_MPMD_DUN_DT ( \
+		MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM | \
+		MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO)
+
+#define MPS_MPMD_ALL (MPS_MPMD_HFP_AG | MPS_MPMD_HFP_HF | MPS_MPMD_A2DP_SRC | \
+			MPS_MPMD_A2DP_SNK | MPS_MPMD_AVRCP_CT | \
+			MPS_MPMD_AVRCP_CT_ONLY | MPS_MPMD_AVRCP_TG | \
+			MPS_MPMD_DUN_DT)
+
+/* Assume all dependencies are supported */
+#define MPS_DEFAULT_DEPS (MPS_DEPS_SNIFF_MODE_DURRING_STREAMING | \
+			MPS_DEPS_GAVDP_REQUIREMENTS | \
+			MPS_DEPS_DIS_CONNECTION_ORDER_BEHAVIOR)
+
+static sdp_record_t *server = NULL;
+static uint32_t fixed_dbts = 0;
+
+/*
+ * List of version numbers supported by the SDP server.
+ * Add to this list when newer versions are supported.
+ */
+static sdp_version_t sdpVnumArray[1] = {
+	{ 1, 0 }
+};
+static const int sdpServerVnumEntries = 1;
+
+static uint32_t mps_handle = 0;
+static bool mps_mpmd = false;
+
+/*
+ * A simple function which returns the time of day in
+ * seconds. Used for updating the service db state
+ * attribute of the service record of the SDP server
+ */
+uint32_t sdp_get_time(void)
+{
+	/*
+	 * To handle failure in gettimeofday, so an old
+	 * value is returned and service does not fail
+	 */
+	static struct timeval tm;
+
+	gettimeofday(&tm, NULL);
+	return (uint32_t) tm.tv_sec;
+}
+
+/*
+ * The service database state is an attribute of the service record
+ * of the SDP server itself. This attribute is guaranteed to
+ * change if any of the contents of the service repository
+ * changes. This function updates the timestamp of value of
+ * the svcDBState attribute
+ * Set the SDP server DB. Simply a timestamp which is the marker
+ * when the DB was modified.
+ */
+static void update_db_timestamp(void)
+{
+	if (fixed_dbts) {
+		sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &fixed_dbts);
+		sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d);
+	} else {
+		uint32_t dbts = sdp_get_time();
+		sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts);
+		sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d);
+	}
+}
+
+void set_fixed_db_timestamp(uint32_t dbts)
+{
+	fixed_dbts = dbts;
+}
+
+void register_public_browse_group(void)
+{
+	sdp_list_t *browselist;
+	uuid_t bgscid, pbgid;
+	sdp_data_t *sdpdata;
+	sdp_record_t *browse = sdp_record_alloc();
+
+	browse->handle = SDP_SERVER_RECORD_HANDLE + 1;
+
+	sdp_record_add(BDADDR_ANY, browse);
+	sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle);
+	sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata);
+
+	sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID);
+	browselist = sdp_list_append(0, &bgscid);
+	sdp_set_service_classes(browse, browselist);
+	sdp_list_free(browselist, 0);
+
+	sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP);
+	sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID,
+				SDP_UUID16, &pbgid.value.uuid16);
+}
+
+/*
+ * The SDP server must present its own service record to
+ * the service repository. This can be accessed by service
+ * discovery clients. This method constructs a service record
+ * and stores it in the repository
+ */
+void register_server_service(void)
+{
+	sdp_list_t *classIDList;
+	uuid_t classID;
+	void **versions, **versionDTDs;
+	uint8_t dtd;
+	sdp_data_t *pData;
+	int i;
+
+	server = sdp_record_alloc();
+	server->pattern = NULL;
+
+	/* Force the record to be SDP_SERVER_RECORD_HANDLE */
+	server->handle = SDP_SERVER_RECORD_HANDLE;
+
+	sdp_record_add(BDADDR_ANY, server);
+	sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE,
+				sdp_data_alloc(SDP_UINT32, &server->handle));
+
+	sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID);
+	classIDList = sdp_list_append(0, &classID);
+	sdp_set_service_classes(server, classIDList);
+	sdp_list_free(classIDList, 0);
+
+	/*
+	 * Set the version numbers supported, these are passed as arguments
+	 * to the server on command line. Now defaults to 1.0
+	 * Build the version number sequence first
+	 */
+	versions = malloc(sdpServerVnumEntries * sizeof(void *));
+	versionDTDs = malloc(sdpServerVnumEntries * sizeof(void *));
+	dtd = SDP_UINT16;
+	for (i = 0; i < sdpServerVnumEntries; i++) {
+		uint16_t *version = malloc(sizeof(uint16_t));
+		*version = sdpVnumArray[i].major;
+		*version = (*version << 8);
+		*version |= sdpVnumArray[i].minor;
+		versions[i] = version;
+		versionDTDs[i] = &dtd;
+	}
+	pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries);
+	for (i = 0; i < sdpServerVnumEntries; i++)
+		free(versions[i]);
+	free(versions);
+	free(versionDTDs);
+	sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData);
+
+	update_db_timestamp();
+}
+
+void register_device_id(uint16_t source, uint16_t vendor,
+					uint16_t product, uint16_t version)
+{
+	const uint16_t spec = 0x0103;
+	const uint8_t primary = 1;
+	sdp_list_t *class_list, *group_list, *profile_list;
+	uuid_t class_uuid, group_uuid;
+	sdp_data_t *sdp_data, *primary_data, *source_data;
+	sdp_data_t *spec_data, *vendor_data, *product_data, *version_data;
+	sdp_profile_desc_t profile;
+	sdp_record_t *record = sdp_record_alloc();
+
+	DBG("Adding device id record for %04x:%04x:%04x:%04x",
+					source, vendor, product, version);
+
+	record->handle = sdp_next_handle();
+
+	sdp_record_add(BDADDR_ANY, record);
+	sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+	sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+	sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID);
+	class_list = sdp_list_append(0, &class_uuid);
+	sdp_set_service_classes(record, class_list);
+	sdp_list_free(class_list, NULL);
+
+	sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP);
+	group_list = sdp_list_append(NULL, &group_uuid);
+	sdp_set_browse_groups(record, group_list);
+	sdp_list_free(group_list, NULL);
+
+	sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID);
+	profile.version = spec;
+	profile_list = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, profile_list);
+	sdp_list_free(profile_list, NULL);
+
+	spec_data = sdp_data_alloc(SDP_UINT16, &spec);
+	sdp_attr_add(record, 0x0200, spec_data);
+
+	vendor_data = sdp_data_alloc(SDP_UINT16, &vendor);
+	sdp_attr_add(record, 0x0201, vendor_data);
+
+	product_data = sdp_data_alloc(SDP_UINT16, &product);
+	sdp_attr_add(record, 0x0202, product_data);
+
+	version_data = sdp_data_alloc(SDP_UINT16, &version);
+	sdp_attr_add(record, 0x0203, version_data);
+
+	primary_data = sdp_data_alloc(SDP_BOOL, &primary);
+	sdp_attr_add(record, 0x0204, primary_data);
+
+	source_data = sdp_data_alloc(SDP_UINT16, &source);
+	sdp_attr_add(record, 0x0205, source_data);
+
+	update_db_timestamp();
+}
+
+static bool class_supported(uint16_t class)
+{
+	sdp_list_t *list;
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, class);
+
+	for (list = sdp_get_record_list(); list; list = list->next) {
+		sdp_record_t *rec = list->data;
+
+		if (sdp_uuid_cmp(&rec->svclass, &uuid) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+static uint64_t mps_mpsd_features(void)
+{
+	uint64_t feat = MPS_MPSD_ALL;
+
+	if (!class_supported(HANDSFREE_AGW_SVCLASS_ID))
+		feat &= ~MPS_MPSD_HFP_AG;
+
+	if (!class_supported(HANDSFREE_SVCLASS_ID))
+		feat &= ~MPS_MPSD_HFP_HF;
+
+	if (!class_supported(AUDIO_SOURCE_SVCLASS_ID))
+		feat &= ~MPS_MPSD_A2DP_SRC;
+
+	if (!class_supported(AUDIO_SINK_SVCLASS_ID))
+		feat &= ~MPS_MPSD_A2DP_SNK;
+
+	if (!class_supported(AV_REMOTE_CONTROLLER_SVCLASS_ID))
+		feat &= ~MPS_MPSD_AVRCP_CT;
+
+	if (!class_supported(AV_REMOTE_TARGET_SVCLASS_ID))
+		feat &= ~MPS_MPSD_AVRCP_TG;
+
+	if (!class_supported(DIALUP_NET_SVCLASS_ID))
+		feat &= ~MPS_MPSD_DUN_GW;
+
+	/* TODO */
+	feat &= ~MPS_MPSD_DUN_DT;
+
+	if (!class_supported(NAP_SVCLASS_ID))
+		feat &= ~MPS_MPSD_PAN_NAP;
+
+	if (!class_supported(PANU_SVCLASS_ID))
+		feat &= ~MPS_MPSD_PAN_PANU;
+
+	if (!class_supported(PBAP_PSE_SVCLASS_ID))
+		feat &= ~MPS_MPSD_PBAP_SRC;
+
+	if (!class_supported(PBAP_PCE_SVCLASS_ID))
+		feat &= ~MPS_MPSD_PBAP_CLI;
+
+	return feat;
+}
+
+static uint64_t mps_mpmd_features(void)
+{
+	uint64_t feat = MPS_MPMD_ALL;
+
+	if (!class_supported(HANDSFREE_AGW_SVCLASS_ID))
+		feat &= ~MPS_MPMD_HFP_AG;
+
+	if (!class_supported(HANDSFREE_SVCLASS_ID))
+		feat &= ~MPS_MPMD_HFP_HF;
+
+	if (!class_supported(AUDIO_SOURCE_SVCLASS_ID))
+		feat &= ~MPS_MPMD_A2DP_SRC;
+
+	if (!class_supported(AUDIO_SINK_SVCLASS_ID))
+		feat &= ~MPS_MPMD_A2DP_SNK;
+	else
+		feat &= ~MPS_MPMD_AVRCP_CT_ONLY;
+
+	if (!class_supported(AV_REMOTE_CONTROLLER_SVCLASS_ID)) {
+		feat &= ~MPS_MPMD_AVRCP_CT;
+		feat &= ~MPS_MPMD_AVRCP_CT_ONLY;
+	}
+
+	if (!class_supported(AV_REMOTE_TARGET_SVCLASS_ID))
+		feat &= ~MPS_MPMD_AVRCP_TG;
+
+	/* TODO */
+	feat &= ~MPS_MPMD_DUN_DT;
+
+	return feat;
+}
+
+static sdp_record_t *mps_record(int mpmd)
+{
+	sdp_data_t *mpsd_features, *mpmd_features, *dependencies;
+	sdp_list_t *svclass_id, *pfseq, *root;
+	uuid_t root_uuid, svclass_uuid;
+	sdp_profile_desc_t profile;
+	sdp_record_t *record;
+	uint64_t mpsd_feat = MPS_MPSD_DEFAULT_FEATURES;
+	uint64_t mpmd_feat = MPS_MPMD_DEFAULT_FEATURES;
+	uint16_t deps = MPS_DEFAULT_DEPS;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(record, pfseq);
+	sdp_list_free(pfseq, NULL);
+
+	mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
+	sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features);
+
+	if (mpmd) {
+		mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
+		sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features);
+	}
+
+	dependencies = sdp_data_alloc(SDP_UINT16, &deps);
+	sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies);
+
+	sdp_set_info_attr(record, "Multi Profile", 0, 0);
+
+	return record;
+}
+
+void register_mps(bool mpmd)
+{
+	sdp_record_t *record;
+
+	record = mps_record(mpmd);
+	if (!record)
+		return;
+
+	if (add_record_to_server(BDADDR_ANY, record) < 0) {
+		sdp_record_free(record);
+		return;
+	}
+
+	mps_handle = record->handle;
+	mps_mpmd = mpmd;
+}
+
+static void update_mps(void)
+{
+	sdp_record_t *rec;
+	sdp_data_t *data;
+	uint64_t mpsd_feat, mpmd_feat;
+
+	if (!mps_handle)
+		return;
+
+	rec = sdp_record_find(mps_handle);
+	if (!rec)
+		return;
+
+	mpsd_feat = mps_mpsd_features();
+	data = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
+	sdp_attr_replace(rec, SDP_ATTR_MPSD_SCENARIOS, data);
+
+	if (mps_mpmd) {
+		mpmd_feat = mps_mpmd_features();
+		data = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
+		sdp_attr_replace(rec, SDP_ATTR_MPMD_SCENARIOS, data);
+	}
+}
+
+int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec)
+{
+	sdp_data_t *data;
+	sdp_list_t *pattern;
+
+	if (rec->handle == 0xffffffff) {
+		rec->handle = sdp_next_handle();
+		if (rec->handle < 0x10000)
+			return -ENOSPC;
+	} else {
+		if (sdp_record_find(rec->handle))
+			return -EEXIST;
+	}
+
+	DBG("Adding record with handle 0x%05x", rec->handle);
+
+	sdp_record_add(src, rec);
+
+	data = sdp_data_alloc(SDP_UINT32, &rec->handle);
+	sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+
+	if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
+		uuid_t uuid;
+		sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+		sdp_pattern_add_uuid(rec, &uuid);
+	}
+
+	for (pattern = rec->pattern; pattern; pattern = pattern->next) {
+		char uuid[32];
+
+		if (pattern->data == NULL)
+			continue;
+
+		sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid));
+		DBG("Record pattern UUID %s", uuid);
+	}
+
+	update_mps();
+	update_db_timestamp();
+
+	return 0;
+}
+
+int remove_record_from_server(uint32_t handle)
+{
+	sdp_record_t *rec;
+
+	/* Refuse to remove the server's own record */
+	if (handle == SDP_SERVER_RECORD_HANDLE)
+		return -EINVAL;
+
+	DBG("Removing record with handle 0x%05x", handle);
+
+	rec = sdp_record_find(handle);
+	if (!rec)
+		return -ENOENT;
+
+	if (sdp_record_remove(handle) == 0) {
+		update_mps();
+		update_db_timestamp();
+	}
+
+	sdp_record_free(rec);
+
+	return 0;
+}
+
+/* FIXME: refactor for server-side */
+static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p,
+					unsigned int bufsize,
+					uint32_t handleExpected, int *scanned)
+{
+	int extractStatus = -1, localExtractedLength = 0;
+	uint8_t dtd;
+	int seqlen = 0;
+	sdp_record_t *rec = NULL;
+	uint16_t attrId, lookAheadAttrId;
+	sdp_data_t *pAttr = NULL;
+	uint32_t handle = 0xffffffff;
+
+	*scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen);
+	p += *scanned;
+	bufsize -= *scanned;
+
+	if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) {
+		SDPDBG("Unexpected end of packet");
+		return NULL;
+	}
+
+	lookAheadAttrId = get_be16(p + sizeof(uint8_t));
+
+	SDPDBG("Look ahead attr id : %d", lookAheadAttrId);
+
+	if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
+		if (bufsize < (sizeof(uint8_t) * 2) +
+					sizeof(uint16_t) + sizeof(uint32_t)) {
+			SDPDBG("Unexpected end of packet");
+			return NULL;
+		}
+		handle = get_be32(p + sizeof(uint8_t) + sizeof(uint16_t) +
+							sizeof(uint8_t));
+		SDPDBG("SvcRecHandle : 0x%x", handle);
+		rec = sdp_record_find(handle);
+	} else if (handleExpected != 0xffffffff)
+		rec = sdp_record_find(handleExpected);
+
+	if (!rec) {
+		rec = sdp_record_alloc();
+		rec->attrlist = NULL;
+		if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
+			rec->handle = handle;
+			sdp_record_add(device, rec);
+		} else if (handleExpected != 0xffffffff) {
+			rec->handle = handleExpected;
+			sdp_record_add(device, rec);
+		}
+	} else {
+		sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
+		rec->attrlist = NULL;
+	}
+
+	while (localExtractedLength < seqlen) {
+		int attrSize = sizeof(uint8_t);
+		int attrValueLength = 0;
+
+		if (bufsize < attrSize + sizeof(uint16_t)) {
+			SDPDBG("Unexpected end of packet: Terminating extraction of attributes");
+			break;
+		}
+
+		SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
+							seqlen, localExtractedLength);
+		dtd = *(uint8_t *) p;
+
+		attrId = get_be16(p + attrSize);
+		attrSize += sizeof(uint16_t);
+
+		SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId);
+
+		pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize,
+							&attrValueLength, rec);
+
+		SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength);
+
+		attrSize += attrValueLength;
+		if (pAttr == NULL) {
+			SDPDBG("Terminating extraction of attributes");
+			break;
+		}
+		localExtractedLength += attrSize;
+		p += attrSize;
+		bufsize -= attrSize;
+		sdp_attr_replace(rec, attrId, pAttr);
+		extractStatus = 0;
+		SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
+					seqlen, localExtractedLength);
+	}
+
+	if (extractStatus == 0) {
+		SDPDBG("Successful extracting of Svc Rec attributes");
+#ifdef SDP_DEBUG
+		sdp_print_service_attr(rec->attrlist);
+#endif
+		*scanned += seqlen;
+	}
+	return rec;
+}
+
+/*
+ * Add the newly created service record to the service repository
+ */
+int service_register_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+	int scanned = 0;
+	sdp_data_t *handle;
+	uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+	int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
+	sdp_record_t *rec;
+
+	req->flags = *p++;
+	if (req->flags & SDP_DEVICE_RECORD) {
+		bacpy(&req->device, (bdaddr_t *) p);
+		p += sizeof(bdaddr_t);
+		bufsize -= sizeof(bdaddr_t);
+	}
+
+	/* save image of PDU: we need it when clients request this attribute */
+	rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned);
+	if (!rec)
+		goto invalid;
+
+	if (rec->handle == 0xffffffff) {
+		rec->handle = sdp_next_handle();
+		if (rec->handle < 0x10000) {
+			sdp_record_free(rec);
+			goto invalid;
+		}
+	} else {
+		if (sdp_record_find(rec->handle)) {
+			/* extract_pdu_server will add the record handle
+			 * if it is missing. So instead of failing, skip
+			 * the record adding to avoid duplication. */
+			goto success;
+		}
+	}
+
+	sdp_record_add(&req->device, rec);
+	if (!(req->flags & SDP_RECORD_PERSIST))
+		sdp_svcdb_set_collectable(rec, req->sock);
+
+	handle = sdp_data_alloc(SDP_UINT32, &rec->handle);
+	sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle);
+
+success:
+	/* if the browse group descriptor is NULL,
+	 * ensure that the record belongs to the ROOT group */
+	if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
+		uuid_t uuid;
+		sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+		sdp_pattern_add_uuid(rec, &uuid);
+	}
+
+	update_db_timestamp();
+
+	/* Build a rsp buffer */
+	put_be32(rec->handle, rsp->data);
+	rsp->data_size = sizeof(uint32_t);
+
+	return 0;
+
+invalid:
+	put_be16(SDP_INVALID_SYNTAX, rsp->data);
+	rsp->data_size = sizeof(uint16_t);
+
+	return -1;
+}
+
+/*
+ * Update a service record
+ */
+int service_update_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+	sdp_record_t *orec, *nrec;
+	int status = 0, scanned = 0;
+	uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+	int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
+	uint32_t handle = get_be32(p);
+
+	SDPDBG("Svc Rec Handle: 0x%x", handle);
+
+	p += sizeof(uint32_t);
+	bufsize -= sizeof(uint32_t);
+
+	orec = sdp_record_find(handle);
+
+	SDPDBG("SvcRecOld: %p", orec);
+
+	if (!orec) {
+		status = SDP_INVALID_RECORD_HANDLE;
+		goto done;
+	}
+
+	nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned);
+	if (!nrec) {
+		status = SDP_INVALID_SYNTAX;
+		goto done;
+	}
+
+	assert(nrec == orec);
+
+	update_db_timestamp();
+
+done:
+	p = rsp->data;
+	put_be16(status, p);
+	rsp->data_size = sizeof(uint16_t);
+	return status;
+}
+
+/*
+ * Remove a registered service record
+ */
+int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+	uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+	uint32_t handle = get_be32(p);
+	sdp_record_t *rec;
+	int status = 0;
+
+	/* extract service record handle */
+
+	rec = sdp_record_find(handle);
+	if (rec) {
+		sdp_svcdb_collect(rec);
+		status = sdp_record_remove(handle);
+		sdp_record_free(rec);
+		if (status == 0)
+			update_db_timestamp();
+	} else {
+		status = SDP_INVALID_RECORD_HANDLE;
+		SDPDBG("Could not find record : 0x%x", handle);
+	}
+
+	p = rsp->data;
+	put_be16(status, p);
+	rsp->data_size = sizeof(uint16_t);
+
+	return status;
+}
diff --git a/bluez/repo/src/sdpd.h b/bluez/repo/src/sdpd.h
new file mode 100644
index 0000000..49cd98a
--- /dev/null
+++ b/bluez/repo/src/sdpd.h
@@ -0,0 +1,81 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.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
+ *
+ */
+
+#ifdef SDP_DEBUG
+#include <syslog.h>
+#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
+#else
+#define SDPDBG(fmt...)
+#endif
+
+typedef struct request {
+	bdaddr_t device;
+	bdaddr_t bdaddr;
+	int      local;
+	int      sock;
+	int      mtu;
+	int      flags;
+	uint8_t  *buf;
+	int      len;
+} sdp_req_t;
+
+void handle_internal_request(int sk, int mtu, void *data, int len);
+void handle_request(int sk, uint8_t *data, int len);
+
+void set_fixed_db_timestamp(uint32_t dbts);
+
+int service_register_req(sdp_req_t *req, sdp_buf_t *rsp);
+int service_update_req(sdp_req_t *req, sdp_buf_t *rsp);
+int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp);
+
+void register_public_browse_group(void);
+void register_server_service(void);
+void register_device_id(uint16_t source, uint16_t vendor,
+					uint16_t product, uint16_t version);
+void register_mps(bool mpmd);
+
+int record_sort(const void *r1, const void *r2);
+void sdp_svcdb_reset(void);
+void sdp_svcdb_collect_all(int sock);
+void sdp_svcdb_set_collectable(sdp_record_t *rec, int sock);
+void sdp_svcdb_collect(sdp_record_t *rec);
+sdp_record_t *sdp_record_find(uint32_t handle);
+void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec);
+int sdp_record_remove(uint32_t handle);
+sdp_list_t *sdp_get_record_list(void);
+int sdp_check_access(uint32_t handle, bdaddr_t *device);
+uint32_t sdp_next_handle(void);
+
+uint32_t sdp_get_time(void);
+
+#define SDP_SERVER_COMPAT (1 << 0)
+#define SDP_SERVER_MASTER (1 << 1)
+
+int start_sdp_server(uint16_t mtu, uint32_t flags);
+void stop_sdp_server(void);
+
+int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec);
+int remove_record_from_server(uint32_t handle);
diff --git a/bluez/repo/src/service.c b/bluez/repo/src/service.c
new file mode 100644
index 0000000..f7912f5
--- /dev/null
+++ b/bluez/repo/src/service.c
@@ -0,0 +1,375 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "log.h"
+#include "backtrace.h"
+
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+#include "service.h"
+
+struct btd_service {
+	int			ref;
+	struct btd_device	*device;
+	struct btd_profile	*profile;
+	void			*user_data;
+	btd_service_state_t	state;
+	int			err;
+};
+
+struct service_state_callback {
+	btd_service_state_cb	cb;
+	void			*user_data;
+	unsigned int		id;
+};
+
+static GSList *state_callbacks = NULL;
+
+static const char *state2str(btd_service_state_t state)
+{
+	switch (state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		return "unavailable";
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		return "disconnected";
+	case BTD_SERVICE_STATE_CONNECTING:
+		return "connecting";
+	case BTD_SERVICE_STATE_CONNECTED:
+		return "connected";
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		return "disconnecting";
+	}
+
+	return NULL;
+}
+
+static void change_state(struct btd_service *service, btd_service_state_t state,
+									int err)
+{
+	btd_service_state_t old = service->state;
+	char addr[18];
+	GSList *l;
+
+	if (state == old)
+		return;
+
+	btd_assert(service->device != NULL);
+	btd_assert(service->profile != NULL);
+
+	service->state = state;
+	service->err = err;
+
+	ba2str(device_get_address(service->device), addr);
+	DBG("%p: device %s profile %s state changed: %s -> %s (%d)", service,
+					addr, service->profile->name,
+					state2str(old), state2str(state), err);
+
+	for (l = state_callbacks; l != NULL; l = g_slist_next(l)) {
+		struct service_state_callback *cb = l->data;
+
+		cb->cb(service, old, state, cb->user_data);
+	}
+}
+
+struct btd_service *btd_service_ref(struct btd_service *service)
+{
+	service->ref++;
+
+	DBG("%p: ref=%d", service, service->ref);
+
+	return service;
+}
+
+void btd_service_unref(struct btd_service *service)
+{
+	service->ref--;
+
+	DBG("%p: ref=%d", service, service->ref);
+
+	if (service->ref > 0)
+		return;
+
+	g_free(service);
+}
+
+struct btd_service *service_create(struct btd_device *device,
+						struct btd_profile *profile)
+{
+	struct btd_service *service;
+
+	service = g_try_new0(struct btd_service, 1);
+	if (!service) {
+		error("service_create: failed to alloc memory");
+		return NULL;
+	}
+
+	service->ref = 1;
+	service->device = device; /* Weak ref */
+	service->profile = profile;
+	service->state = BTD_SERVICE_STATE_UNAVAILABLE;
+
+	return service;
+}
+
+int service_probe(struct btd_service *service)
+{
+	char addr[18];
+	int err;
+
+	btd_assert(service->state == BTD_SERVICE_STATE_UNAVAILABLE);
+
+	err = service->profile->device_probe(service);
+	if (err == 0) {
+		change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0);
+		return 0;
+	}
+
+	ba2str(device_get_address(service->device), addr);
+	error("%s profile probe failed for %s", service->profile->name, addr);
+
+	return err;
+}
+
+void service_remove(struct btd_service *service)
+{
+	change_state(service, BTD_SERVICE_STATE_DISCONNECTED, -ECONNABORTED);
+	change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0);
+	service->profile->device_remove(service);
+	service->device = NULL;
+	service->profile = NULL;
+	btd_service_unref(service);
+}
+
+int service_accept(struct btd_service *service)
+{
+	char addr[18];
+	int err;
+
+	switch (service->state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		return -EINVAL;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+	case BTD_SERVICE_STATE_CONNECTED:
+		return 0;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		return -EBUSY;
+	}
+
+	if (!service->profile->accept)
+		goto done;
+
+	err = service->profile->accept(service);
+	if (!err)
+		goto done;
+
+	ba2str(device_get_address(service->device), addr);
+	error("%s profile accept failed for %s", service->profile->name, addr);
+
+	return err;
+
+done:
+	change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+	return 0;
+}
+
+int btd_service_connect(struct btd_service *service)
+{
+	struct btd_profile *profile = service->profile;
+	char addr[18];
+	int err;
+
+	if (!profile->connect)
+		return -ENOTSUP;
+
+	switch (service->state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		return -EINVAL;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		break;
+	case BTD_SERVICE_STATE_CONNECTING:
+		return 0;
+	case BTD_SERVICE_STATE_CONNECTED:
+		return -EALREADY;
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		return -EBUSY;
+	}
+
+	err = profile->connect(service);
+	if (err == 0) {
+		change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+		return 0;
+	}
+
+	ba2str(device_get_address(service->device), addr);
+	error("%s profile connect failed for %s: %s", profile->name, addr,
+								strerror(-err));
+
+	return err;
+}
+
+int btd_service_disconnect(struct btd_service *service)
+{
+	struct btd_profile *profile = service->profile;
+	char addr[18];
+	int err;
+
+	if (!profile->disconnect)
+		return -ENOTSUP;
+
+	switch (service->state) {
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+		return -EINVAL;
+	case BTD_SERVICE_STATE_DISCONNECTED:
+	case BTD_SERVICE_STATE_DISCONNECTING:
+		return -EALREADY;
+	case BTD_SERVICE_STATE_CONNECTING:
+	case BTD_SERVICE_STATE_CONNECTED:
+		break;
+	}
+
+	change_state(service, BTD_SERVICE_STATE_DISCONNECTING, 0);
+
+	err = profile->disconnect(service);
+	if (err == 0)
+		return 0;
+
+	if (err == -ENOTCONN) {
+		btd_service_disconnecting_complete(service, 0);
+		return 0;
+	}
+
+	ba2str(device_get_address(service->device), addr);
+	error("%s profile disconnect failed for %s: %s", profile->name, addr,
+								strerror(-err));
+
+	btd_service_disconnecting_complete(service, err);
+
+	return err;
+}
+
+struct btd_device *btd_service_get_device(const struct btd_service *service)
+{
+	return service->device;
+}
+
+struct btd_profile *btd_service_get_profile(const struct btd_service *service)
+{
+	return service->profile;
+}
+
+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;
+}
+
+void *btd_service_get_user_data(const struct btd_service *service)
+{
+	return service->user_data;
+}
+
+btd_service_state_t btd_service_get_state(const struct btd_service *service)
+{
+	return service->state;
+}
+
+int btd_service_get_error(const struct btd_service *service)
+{
+	return service->err;
+}
+
+unsigned int btd_service_add_state_cb(btd_service_state_cb cb, void *user_data)
+{
+	struct service_state_callback *state_cb;
+	static unsigned int id = 0;
+
+	state_cb = g_new0(struct service_state_callback, 1);
+	state_cb->cb = cb;
+	state_cb->user_data = user_data;
+	state_cb->id = ++id;
+
+	state_callbacks = g_slist_append(state_callbacks, state_cb);
+
+	return state_cb->id;
+}
+
+bool btd_service_remove_state_cb(unsigned int id)
+{
+	GSList *l;
+
+	for (l = state_callbacks; l != NULL; l = g_slist_next(l)) {
+		struct service_state_callback *cb = l->data;
+
+		if (cb && cb->id == id) {
+			state_callbacks = g_slist_remove(state_callbacks, cb);
+			g_free(cb);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+void btd_service_connecting_complete(struct btd_service *service, int err)
+{
+	if (service->state != BTD_SERVICE_STATE_CONNECTING)
+		return;
+
+	if (err == 0)
+		change_state(service, BTD_SERVICE_STATE_CONNECTED, 0);
+	else
+		change_state(service, BTD_SERVICE_STATE_DISCONNECTED, err);
+}
+
+void btd_service_disconnecting_complete(struct btd_service *service, int err)
+{
+	if (service->state != BTD_SERVICE_STATE_CONNECTED &&
+			service->state != BTD_SERVICE_STATE_DISCONNECTING)
+		return;
+
+	if (err == 0)
+		change_state(service, BTD_SERVICE_STATE_DISCONNECTED, 0);
+	else /* If disconnect fails, we assume it remains connected */
+		change_state(service, BTD_SERVICE_STATE_CONNECTED, err);
+}
diff --git a/bluez/repo/src/service.h b/bluez/repo/src/service.h
new file mode 100644
index 0000000..c1f97f6
--- /dev/null
+++ b/bluez/repo/src/service.h
@@ -0,0 +1,71 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef enum {
+	BTD_SERVICE_STATE_UNAVAILABLE, /* Not probed */
+	BTD_SERVICE_STATE_DISCONNECTED,
+	BTD_SERVICE_STATE_CONNECTING,
+	BTD_SERVICE_STATE_CONNECTED,
+	BTD_SERVICE_STATE_DISCONNECTING,
+} btd_service_state_t;
+
+struct btd_service;
+struct btd_device;
+struct btd_profile;
+
+typedef void (*btd_service_state_cb) (struct btd_service *service,
+						btd_service_state_t old_state,
+						btd_service_state_t new_state,
+						void *user_data);
+
+struct btd_service *btd_service_ref(struct btd_service *service);
+void btd_service_unref(struct btd_service *service);
+
+/* Service management functions used by the core */
+struct btd_service *service_create(struct btd_device *device,
+						struct btd_profile *profile);
+
+int service_probe(struct btd_service *service);
+void service_remove(struct btd_service *service);
+
+int service_accept(struct btd_service *service);
+
+/* Connection control API */
+int btd_service_connect(struct btd_service *service);
+int btd_service_disconnect(struct btd_service *service);
+
+/* Public member access */
+struct btd_device *btd_service_get_device(const struct btd_service *service);
+struct btd_profile *btd_service_get_profile(const struct btd_service *service);
+btd_service_state_t btd_service_get_state(const struct btd_service *service);
+int btd_service_get_error(const struct btd_service *service);
+
+unsigned int btd_service_add_state_cb(btd_service_state_cb cb,
+							void *user_data);
+bool btd_service_remove_state_cb(unsigned int id);
+
+/* Functions used by profile implementation */
+void btd_service_connecting_complete(struct btd_service *service, int err);
+void btd_service_disconnecting_complete(struct btd_service *service, int err);
+void btd_service_set_user_data(struct btd_service *service, void *user_data);
+void *btd_service_get_user_data(const struct btd_service *service);
diff --git a/bluez/repo/src/shared/._ad.c b/bluez/repo/src/shared/._ad.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._ad.c
Binary files differ
diff --git a/bluez/repo/src/shared/._ad.h b/bluez/repo/src/shared/._ad.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._ad.h
Binary files differ
diff --git a/bluez/repo/src/shared/._att-types.h b/bluez/repo/src/shared/._att-types.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._att-types.h
Binary files differ
diff --git a/bluez/repo/src/shared/._att.c b/bluez/repo/src/shared/._att.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._att.c
Binary files differ
diff --git a/bluez/repo/src/shared/._att.h b/bluez/repo/src/shared/._att.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._att.h
Binary files differ
diff --git a/bluez/repo/src/shared/._btsnoop.c b/bluez/repo/src/shared/._btsnoop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._btsnoop.c
Binary files differ
diff --git a/bluez/repo/src/shared/._btsnoop.h b/bluez/repo/src/shared/._btsnoop.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._btsnoop.h
Binary files differ
diff --git a/bluez/repo/src/shared/._crypto.c b/bluez/repo/src/shared/._crypto.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._crypto.c
Binary files differ
diff --git a/bluez/repo/src/shared/._crypto.h b/bluez/repo/src/shared/._crypto.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._crypto.h
Binary files differ
diff --git a/bluez/repo/src/shared/._ecc.c b/bluez/repo/src/shared/._ecc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._ecc.c
Binary files differ
diff --git a/bluez/repo/src/shared/._ecc.h b/bluez/repo/src/shared/._ecc.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._ecc.h
Binary files differ
diff --git a/bluez/repo/src/shared/._gap.c b/bluez/repo/src/shared/._gap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gap.c
Binary files differ
diff --git a/bluez/repo/src/shared/._gap.h b/bluez/repo/src/shared/._gap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gap.h
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-client.c b/bluez/repo/src/shared/._gatt-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-client.c
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-client.h b/bluez/repo/src/shared/._gatt-client.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-client.h
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-db.c b/bluez/repo/src/shared/._gatt-db.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-db.c
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-db.h b/bluez/repo/src/shared/._gatt-db.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-db.h
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-helpers.c b/bluez/repo/src/shared/._gatt-helpers.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-helpers.c
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-helpers.h b/bluez/repo/src/shared/._gatt-helpers.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-helpers.h
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-server.c b/bluez/repo/src/shared/._gatt-server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-server.c
Binary files differ
diff --git a/bluez/repo/src/shared/._gatt-server.h b/bluez/repo/src/shared/._gatt-server.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._gatt-server.h
Binary files differ
diff --git a/bluez/repo/src/shared/._hci-crypto.c b/bluez/repo/src/shared/._hci-crypto.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._hci-crypto.c
Binary files differ
diff --git a/bluez/repo/src/shared/._hci-crypto.h b/bluez/repo/src/shared/._hci-crypto.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._hci-crypto.h
Binary files differ
diff --git a/bluez/repo/src/shared/._hci.c b/bluez/repo/src/shared/._hci.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._hci.c
Binary files differ
diff --git a/bluez/repo/src/shared/._hci.h b/bluez/repo/src/shared/._hci.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._hci.h
Binary files differ
diff --git a/bluez/repo/src/shared/._hfp.c b/bluez/repo/src/shared/._hfp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._hfp.c
Binary files differ
diff --git a/bluez/repo/src/shared/._hfp.h b/bluez/repo/src/shared/._hfp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._hfp.h
Binary files differ
diff --git a/bluez/repo/src/shared/._io-glib.c b/bluez/repo/src/shared/._io-glib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._io-glib.c
Binary files differ
diff --git a/bluez/repo/src/shared/._io-mainloop.c b/bluez/repo/src/shared/._io-mainloop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._io-mainloop.c
Binary files differ
diff --git a/bluez/repo/src/shared/._io.h b/bluez/repo/src/shared/._io.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._io.h
Binary files differ
diff --git a/bluez/repo/src/shared/._mainloop.c b/bluez/repo/src/shared/._mainloop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._mainloop.c
Binary files differ
diff --git a/bluez/repo/src/shared/._mainloop.h b/bluez/repo/src/shared/._mainloop.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._mainloop.h
Binary files differ
diff --git a/bluez/repo/src/shared/._mgmt.c b/bluez/repo/src/shared/._mgmt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._mgmt.c
Binary files differ
diff --git a/bluez/repo/src/shared/._mgmt.h b/bluez/repo/src/shared/._mgmt.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._mgmt.h
Binary files differ
diff --git a/bluez/repo/src/shared/._pcap.c b/bluez/repo/src/shared/._pcap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._pcap.c
Binary files differ
diff --git a/bluez/repo/src/shared/._pcap.h b/bluez/repo/src/shared/._pcap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._pcap.h
Binary files differ
diff --git a/bluez/repo/src/shared/._queue.c b/bluez/repo/src/shared/._queue.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._queue.c
Binary files differ
diff --git a/bluez/repo/src/shared/._queue.h b/bluez/repo/src/shared/._queue.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._queue.h
Binary files differ
diff --git a/bluez/repo/src/shared/._ringbuf.c b/bluez/repo/src/shared/._ringbuf.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._ringbuf.c
Binary files differ
diff --git a/bluez/repo/src/shared/._ringbuf.h b/bluez/repo/src/shared/._ringbuf.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._ringbuf.h
Binary files differ
diff --git a/bluez/repo/src/shared/._tester.c b/bluez/repo/src/shared/._tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._tester.c
Binary files differ
diff --git a/bluez/repo/src/shared/._tester.h b/bluez/repo/src/shared/._tester.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._tester.h
Binary files differ
diff --git a/bluez/repo/src/shared/._timeout-glib.c b/bluez/repo/src/shared/._timeout-glib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._timeout-glib.c
Binary files differ
diff --git a/bluez/repo/src/shared/._timeout-mainloop.c b/bluez/repo/src/shared/._timeout-mainloop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._timeout-mainloop.c
Binary files differ
diff --git a/bluez/repo/src/shared/._timeout.h b/bluez/repo/src/shared/._timeout.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._timeout.h
Binary files differ
diff --git a/bluez/repo/src/shared/._uhid.c b/bluez/repo/src/shared/._uhid.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._uhid.c
Binary files differ
diff --git a/bluez/repo/src/shared/._uhid.h b/bluez/repo/src/shared/._uhid.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._uhid.h
Binary files differ
diff --git a/bluez/repo/src/shared/._util.c b/bluez/repo/src/shared/._util.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._util.c
Binary files differ
diff --git a/bluez/repo/src/shared/._util.h b/bluez/repo/src/shared/._util.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/src/shared/._util.h
Binary files differ
diff --git a/bluez/repo/src/shared/ad.c b/bluez/repo/src/shared/ad.c
new file mode 100644
index 0000000..1bf013d
--- /dev/null
+++ b/bluez/repo/src/shared/ad.c
@@ -0,0 +1,658 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Google Inc.
+ *
+ *
+ *  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 "src/shared/ad.h"
+
+#include "src/eir.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+
+#define MAX_ADV_DATA_LEN 31
+
+struct bt_ad {
+	int ref_count;
+	struct queue *service_uuids;
+	struct queue *manufacturer_data;
+	struct queue *solicit_uuids;
+	struct queue *service_data;
+};
+
+struct bt_ad *bt_ad_new(void)
+{
+	struct bt_ad *ad;
+
+	ad = new0(struct bt_ad, 1);
+	ad->service_uuids = queue_new();
+	ad->manufacturer_data = queue_new();
+	ad->solicit_uuids = queue_new();
+	ad->service_data = queue_new();
+
+	return bt_ad_ref(ad);
+}
+
+struct bt_ad *bt_ad_ref(struct bt_ad *ad)
+{
+	if (!ad)
+		return NULL;
+
+	ad->ref_count++;
+	return ad;
+}
+
+static void uuid_destroy(void *data)
+{
+	struct bt_ad_service_data *uuid_data = data;
+
+	free(uuid_data->data);
+	free(uuid_data);
+}
+
+static bool uuid_data_match(const void *data, const void *elem)
+{
+	const struct bt_ad_service_data *uuid_data = elem;
+	const bt_uuid_t *uuid = data;
+
+	return !bt_uuid_cmp(&uuid_data->uuid, uuid);
+}
+
+static void manuf_destroy(void *data)
+{
+	struct bt_ad_manufacturer_data *manuf = data;
+
+	free(manuf->data);
+	free(manuf);
+}
+
+static bool manuf_match(const void *data, const void *elem)
+{
+	const struct bt_ad_manufacturer_data *manuf = elem;
+	uint16_t manuf_id = PTR_TO_UINT(elem);
+
+	return manuf->manufacturer_id == manuf_id;
+}
+
+void bt_ad_unref(struct bt_ad *ad)
+{
+	if (!ad)
+		return;
+
+	if (__sync_sub_and_fetch(&ad->ref_count, 1))
+		return;
+
+	queue_destroy(ad->service_uuids, free);
+
+	queue_destroy(ad->manufacturer_data, manuf_destroy);
+
+	queue_destroy(ad->solicit_uuids, free);
+
+	queue_destroy(ad->service_data, uuid_destroy);
+
+	free(ad);
+}
+
+static size_t uuid_list_length(struct queue *uuid_queue)
+{
+	bool uuid16_included = false;
+	bool uuid32_included = false;
+	bool uuid128_included = false;
+	size_t length = 0;
+	const struct queue_entry *entry;
+
+	entry = queue_get_entries(uuid_queue);
+
+	while (entry) {
+		bt_uuid_t *uuid = entry->data;
+
+		length += bt_uuid_len(uuid);
+
+		if (uuid->type == BT_UUID16)
+			uuid16_included = true;
+		else if (uuid->type == BT_UUID32)
+			uuid32_included = true;
+		else
+			uuid128_included = true;
+
+		entry = entry->next;
+	}
+
+	if (uuid16_included)
+		length += 2;
+
+	if (uuid32_included)
+		length += 2;
+
+	if (uuid128_included)
+		length += 2;
+
+	return length;
+}
+
+static size_t mfg_data_length(struct queue *manuf_data)
+{
+	size_t length = 0;
+	const struct queue_entry *entry;
+
+	entry = queue_get_entries(manuf_data);
+
+	while (entry) {
+		struct bt_ad_manufacturer_data *data = entry->data;
+
+		length += 2 + sizeof(uint16_t) + data->len;
+
+		entry = entry->next;
+	}
+
+	return length;
+}
+
+static size_t uuid_data_length(struct queue *uuid_data)
+{
+	size_t length = 0;
+	const struct queue_entry *entry;
+
+	entry = queue_get_entries(uuid_data);
+
+	while (entry) {
+		struct bt_ad_service_data *data = entry->data;
+
+		length += 2 + bt_uuid_len(&data->uuid) + data->len;
+
+		entry = entry->next;
+	}
+
+	return length;
+}
+
+static size_t calculate_length(struct bt_ad *ad)
+{
+	size_t length = 0;
+
+	length += uuid_list_length(ad->service_uuids);
+
+	length += uuid_list_length(ad->solicit_uuids);
+
+	length += mfg_data_length(ad->manufacturer_data);
+
+	length += uuid_data_length(ad->service_data);
+
+	return length;
+}
+
+static void serialize_uuids(struct queue *uuids, uint8_t uuid_type,
+						uint8_t ad_type, uint8_t *buf,
+						uint8_t *pos)
+{
+	const struct queue_entry *entry = queue_get_entries(uuids);
+	bool added = false;
+	uint8_t length_pos = 0;
+
+	while (entry) {
+		bt_uuid_t *uuid = entry->data;
+
+		if (uuid->type == uuid_type) {
+			if (!added) {
+				length_pos = (*pos)++;
+				buf[(*pos)++] = ad_type;
+				added = true;
+			}
+
+			if (uuid_type != BT_UUID32)
+				bt_uuid_to_le(uuid, buf + *pos);
+			else
+				bt_put_le32(uuid->value.u32, buf + *pos);
+
+			*pos += bt_uuid_len(uuid);
+		}
+
+		entry = entry->next;
+	}
+
+	if (added)
+		buf[length_pos] = *pos - length_pos - 1;
+}
+
+static void serialize_service_uuids(struct queue *uuids, uint8_t *buf,
+								uint8_t *pos)
+{
+	serialize_uuids(uuids, BT_UUID16, EIR_UUID16_ALL, buf, pos);
+
+	serialize_uuids(uuids, BT_UUID32, EIR_UUID32_ALL, buf, pos);
+
+	serialize_uuids(uuids, BT_UUID128, EIR_UUID128_ALL, buf, pos);
+}
+
+static void serialize_solicit_uuids(struct queue *uuids, uint8_t *buf,
+								uint8_t *pos)
+{
+	serialize_uuids(uuids, BT_UUID16, EIR_SOLICIT16, buf, pos);
+
+	serialize_uuids(uuids, BT_UUID32, EIR_SOLICIT32, buf, pos);
+
+	serialize_uuids(uuids, BT_UUID128, EIR_SOLICIT128, buf, pos);
+}
+
+static void serialize_manuf_data(struct queue *manuf_data, uint8_t *buf,
+								uint8_t *pos)
+{
+	const struct queue_entry *entry = queue_get_entries(manuf_data);
+
+	while (entry) {
+		struct bt_ad_manufacturer_data *data = entry->data;
+
+		buf[(*pos)++] = data->len + 2 + 1;
+
+		buf[(*pos)++] = EIR_MANUFACTURER_DATA;
+
+		bt_put_le16(data->manufacturer_id, buf + (*pos));
+
+		*pos += 2;
+
+		memcpy(buf + *pos, data->data, data->len);
+
+		*pos += data->len;
+
+		entry = entry->next;
+	}
+}
+
+static void serialize_service_data(struct queue *service_data, uint8_t *buf,
+								uint8_t *pos)
+{
+	const struct queue_entry *entry = queue_get_entries(service_data);
+
+	while (entry) {
+		struct bt_ad_service_data *data = entry->data;
+		int uuid_len = bt_uuid_len(&data->uuid);
+
+		buf[(*pos)++] =  uuid_len + data->len + 1;
+
+		switch (uuid_len) {
+		case 2:
+			buf[(*pos)++] = EIR_SVC_DATA16;
+			break;
+		case 4:
+			buf[(*pos)++] = EIR_SVC_DATA32;
+			break;
+		case 16:
+			buf[(*pos)++] = EIR_SVC_DATA128;
+			break;
+		}
+
+		if (uuid_len != 4)
+			bt_uuid_to_le(&data->uuid, buf + *pos);
+		else
+			bt_put_le32(data->uuid.value.u32, buf + *pos);
+
+		*pos += uuid_len;
+
+		memcpy(buf + *pos, data->data, data->len);
+
+		*pos += data->len;
+
+		entry = entry->next;
+	}
+}
+
+uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length)
+{
+	uint8_t *adv_data;
+	uint8_t pos = 0;
+
+	if (!ad)
+		return NULL;
+
+	*length = calculate_length(ad);
+
+	if (*length > MAX_ADV_DATA_LEN)
+		return NULL;
+
+	adv_data = malloc0(*length);
+	if (!adv_data)
+		return NULL;
+
+	serialize_service_uuids(ad->service_uuids, adv_data, &pos);
+
+	serialize_solicit_uuids(ad->solicit_uuids, adv_data, &pos);
+
+	serialize_manuf_data(ad->manufacturer_data, adv_data, &pos);
+
+	serialize_service_data(ad->service_data, adv_data, &pos);
+
+	return adv_data;
+}
+
+static bool queue_add_uuid(struct queue *queue, const bt_uuid_t *uuid)
+{
+	bt_uuid_t *new_uuid;
+
+	if (!queue)
+		return false;
+
+	new_uuid = new0(bt_uuid_t, 1);
+
+	*new_uuid = *uuid;
+
+	if (queue_push_tail(queue, new_uuid))
+		return true;
+
+	free(new_uuid);
+
+	return false;
+}
+
+static bool uuid_match(const void *data, const void *elem)
+{
+	const bt_uuid_t *match_uuid = data;
+	const bt_uuid_t *uuid = elem;
+
+	return bt_uuid_cmp(match_uuid, uuid);
+}
+
+static bool queue_remove_uuid(struct queue *queue, bt_uuid_t *uuid)
+{
+	bt_uuid_t *removed;
+
+	if (!queue || !uuid)
+		return false;
+
+	removed = queue_remove_if(queue, uuid_match, uuid);
+
+	if (removed) {
+		free(removed);
+		return true;
+	}
+
+	return false;
+}
+
+bool bt_ad_add_service_uuid(struct bt_ad *ad, const bt_uuid_t *uuid)
+{
+	if (!ad)
+		return false;
+
+	return queue_add_uuid(ad->service_uuids, uuid);
+}
+
+bool bt_ad_remove_service_uuid(struct bt_ad *ad, bt_uuid_t *uuid)
+{
+	if (!ad)
+		return false;
+
+	return queue_remove_uuid(ad->service_uuids, uuid);
+}
+
+void bt_ad_clear_service_uuid(struct bt_ad *ad)
+{
+	if (!ad)
+		return;
+
+	queue_remove_all(ad->service_uuids, NULL, NULL, free);
+}
+
+static bool manufacturer_id_data_match(const void *data, const void *user_data)
+{
+	const struct bt_ad_manufacturer_data *m = data;
+	uint16_t id = PTR_TO_UINT(user_data);
+
+	return m->manufacturer_id == id;
+}
+
+bool bt_ad_add_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_id,
+							void *data, size_t len)
+{
+	struct bt_ad_manufacturer_data *new_data;
+
+	if (!ad)
+		return false;
+
+	if (len > (MAX_ADV_DATA_LEN - 2 - sizeof(uint16_t)))
+		return false;
+
+	new_data = queue_find(ad->manufacturer_data, manufacturer_id_data_match,
+						UINT_TO_PTR(manufacturer_id));
+	if (new_data) {
+		if (new_data->len == len && !memcmp(new_data->data, data, len))
+			return false;
+		new_data->data = realloc(new_data->data, len);
+		memcpy(new_data->data, data, len);
+		new_data->len = len;
+		return true;
+	}
+
+	new_data = new0(struct bt_ad_manufacturer_data, 1);
+	new_data->manufacturer_id = manufacturer_id;
+
+	new_data->data = malloc(len);
+	if (!new_data->data) {
+		free(new_data);
+		return false;
+	}
+
+	memcpy(new_data->data, data, len);
+
+	new_data->len = len;
+
+	if (queue_push_tail(ad->manufacturer_data, new_data))
+		return true;
+
+	manuf_destroy(new_data);
+
+	return false;
+}
+
+static bool manufacturer_data_match(const void *data, const void *user_data)
+{
+	const struct bt_ad_manufacturer_data *m1 = data;
+	const struct bt_ad_manufacturer_data *m2 = user_data;
+
+	if (m1->manufacturer_id != m2->manufacturer_id)
+		return false;
+
+	if (m1->len != m2->len)
+		return false;
+
+	return !memcmp(m1->data, m2->data, m1->len);
+}
+
+bool bt_ad_has_manufacturer_data(struct bt_ad *ad,
+				const struct bt_ad_manufacturer_data *data)
+{
+	if (!ad)
+		return false;
+
+	if (!data)
+		return !queue_isempty(ad->manufacturer_data);
+
+	return queue_find(ad->manufacturer_data, manufacturer_data_match, data);
+}
+
+void bt_ad_foreach_manufacturer_data(struct bt_ad *ad, bt_ad_func_t func,
+							void *user_data)
+{
+	if (!ad)
+		return;
+
+	queue_foreach(ad->manufacturer_data, func, user_data);
+}
+
+bool bt_ad_remove_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_id)
+{
+	struct bt_ad_manufacturer_data *data;
+
+	if (!ad)
+		return false;
+
+	data = queue_remove_if(ad->manufacturer_data, manuf_match,
+						UINT_TO_PTR(manufacturer_id));
+
+	if (!data)
+		return false;
+
+	manuf_destroy(data);
+
+	return true;
+}
+
+void bt_ad_clear_manufacturer_data(struct bt_ad *ad)
+{
+	if (!ad)
+		return;
+
+	queue_remove_all(ad->manufacturer_data, NULL, NULL, manuf_destroy);
+}
+
+bool bt_ad_add_solicit_uuid(struct bt_ad *ad, const bt_uuid_t *uuid)
+{
+	if (!ad)
+		return false;
+
+	return queue_add_uuid(ad->solicit_uuids, uuid);
+}
+
+bool bt_ad_remove_solicit_uuid(struct bt_ad *ad, bt_uuid_t *uuid)
+{
+	if (!ad)
+		return false;
+
+	return queue_remove_uuid(ad->solicit_uuids, uuid);
+}
+
+void bt_ad_clear_solicit_uuid(struct bt_ad *ad)
+{
+	if (!ad)
+		return;
+
+	queue_remove_all(ad->solicit_uuids, NULL, NULL, free);
+}
+
+
+static bool service_uuid_match(const void *data, const void *user_data)
+{
+	const struct bt_ad_service_data *s = data;
+	const bt_uuid_t *uuid = user_data;
+
+	return !bt_uuid_cmp(&s->uuid, uuid);
+}
+
+bool bt_ad_add_service_data(struct bt_ad *ad, const bt_uuid_t *uuid, void *data,
+								size_t len)
+{
+	struct bt_ad_service_data *new_data;
+
+	if (!ad)
+		return false;
+
+	if (len > (MAX_ADV_DATA_LEN - 2 - (size_t)bt_uuid_len(uuid)))
+		return false;
+
+	new_data = queue_find(ad->service_data, service_uuid_match, uuid);
+	if (new_data) {
+		if (new_data->len == len && !memcmp(new_data->data, data, len))
+			return false;
+		new_data->data = realloc(new_data->data, len);
+		memcpy(new_data->data, data, len);
+		new_data->len = len;
+		return true;
+	}
+
+	new_data = new0(struct bt_ad_service_data, 1);
+
+	new_data->uuid = *uuid;
+
+	new_data->data = malloc(len);
+	if (!new_data->data) {
+		free(new_data);
+		return false;
+	}
+
+	memcpy(new_data->data, data, len);
+
+	new_data->len = len;
+
+	if (queue_push_tail(ad->service_data, new_data))
+		return true;
+
+	uuid_destroy(new_data);
+
+	return false;
+}
+
+static bool service_data_match(const void *data, const void *user_data)
+{
+	const struct bt_ad_service_data *s1 = data;
+	const struct bt_ad_service_data *s2 = user_data;
+
+	if (bt_uuid_cmp(&s1->uuid, &s2->uuid))
+		return false;
+
+	if (s1->len != s2->len)
+		return false;
+
+	return !memcmp(s1->data, s2->data, s1->len);
+}
+
+bool bt_ad_has_service_data(struct bt_ad *ad,
+					const struct bt_ad_service_data *data)
+{
+	if (!ad)
+		return false;
+
+	if (!data)
+		return !queue_isempty(ad->service_data);
+
+	return queue_find(ad->service_data, service_data_match, data);
+}
+
+void bt_ad_foreach_service_data(struct bt_ad *ad, bt_ad_func_t func,
+							void *user_data)
+{
+	if (!ad)
+		return;
+
+	queue_foreach(ad->service_data, func, user_data);
+}
+
+bool bt_ad_remove_service_data(struct bt_ad *ad, bt_uuid_t *uuid)
+{
+	struct bt_ad_service_data *data;
+
+	if (!ad)
+		return false;
+
+	data = queue_remove_if(ad->service_data, uuid_data_match, uuid);
+
+	if (!data)
+		return false;
+
+	uuid_destroy(data);
+
+	return true;
+}
+
+void bt_ad_clear_service_data(struct bt_ad *ad)
+{
+	if (!ad)
+		return;
+
+	queue_remove_all(ad->service_data, NULL, NULL, uuid_destroy);
+}
diff --git a/bluez/repo/src/shared/ad.h b/bluez/repo/src/shared/ad.h
new file mode 100644
index 0000000..709563d
--- /dev/null
+++ b/bluez/repo/src/shared/ad.h
@@ -0,0 +1,90 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Google Inc.
+ *
+ *
+ *  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 <inttypes.h>
+#include <stdbool.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+typedef void (*bt_ad_func_t)(void *data, void *user_data);
+
+struct bt_ad;
+
+struct bt_ad_manufacturer_data {
+	uint16_t manufacturer_id;
+	uint8_t *data;
+	size_t len;
+};
+
+struct bt_ad_service_data {
+	bt_uuid_t uuid;
+	uint8_t *data;
+	size_t len;
+};
+
+struct bt_ad *bt_ad_new(void);
+
+struct bt_ad *bt_ad_ref(struct bt_ad *ad);
+
+void bt_ad_unref(struct bt_ad *ad);
+
+uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length);
+
+bool bt_ad_add_service_uuid(struct bt_ad *ad, const bt_uuid_t *uuid);
+
+bool bt_ad_remove_service_uuid(struct bt_ad *ad, bt_uuid_t *uuid);
+
+void bt_ad_clear_service_uuid(struct bt_ad *ad);
+
+bool bt_ad_add_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_data,
+						void *data, size_t len);
+
+bool bt_ad_has_manufacturer_data(struct bt_ad *ad,
+				const struct bt_ad_manufacturer_data *data);
+
+void bt_ad_foreach_manufacturer_data(struct bt_ad *ad, bt_ad_func_t func,
+							void *user_data);
+
+bool bt_ad_remove_manufacturer_data(struct bt_ad *ad, uint16_t manufacturer_id);
+
+void bt_ad_clear_manufacturer_data(struct bt_ad *ad);
+
+bool bt_ad_add_solicit_uuid(struct bt_ad *ad, const bt_uuid_t *uuid);
+
+bool bt_ad_remove_solicit_uuid(struct bt_ad *ad, bt_uuid_t *uuid);
+
+void bt_ad_clear_solicit_uuid(struct bt_ad *ad);
+
+bool bt_ad_add_service_data(struct bt_ad *ad, const bt_uuid_t *uuid, void *data,
+								size_t len);
+
+bool bt_ad_has_service_data(struct bt_ad *ad,
+					const struct bt_ad_service_data *data);
+
+void bt_ad_foreach_service_data(struct bt_ad *ad, bt_ad_func_t func,
+							void *user_data);
+
+bool bt_ad_remove_service_data(struct bt_ad *ad, bt_uuid_t *uuid);
+
+void bt_ad_clear_service_data(struct bt_ad *ad);
diff --git a/bluez/repo/src/shared/att-types.h b/bluez/repo/src/shared/att-types.h
new file mode 100644
index 0000000..c3062c0
--- /dev/null
+++ b/bluez/repo/src/shared/att-types.h
@@ -0,0 +1,147 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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 <stdint.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define BT_ATT_SECURITY_AUTO	0
+#define BT_ATT_SECURITY_LOW	1
+#define BT_ATT_SECURITY_MEDIUM	2
+#define BT_ATT_SECURITY_HIGH	3
+
+#define BT_ATT_DEFAULT_LE_MTU	23
+#define BT_ATT_MAX_LE_MTU	517
+#define BT_ATT_MAX_VALUE_LEN	512
+
+/* ATT protocol opcodes */
+#define BT_ATT_OP_ERROR_RSP			0x01
+#define BT_ATT_OP_MTU_REQ			0x02
+#define BT_ATT_OP_MTU_RSP			0x03
+#define BT_ATT_OP_FIND_INFO_REQ			0x04
+#define BT_ATT_OP_FIND_INFO_RSP			0x05
+#define BT_ATT_OP_FIND_BY_TYPE_REQ		0x06
+#define BT_ATT_OP_FIND_BY_TYPE_RSP		0x07
+#define BT_ATT_OP_READ_BY_TYPE_REQ		0x08
+#define BT_ATT_OP_READ_BY_TYPE_RSP		0x09
+#define BT_ATT_OP_READ_REQ			0x0a
+#define BT_ATT_OP_READ_RSP			0x0b
+#define BT_ATT_OP_READ_BLOB_REQ			0x0c
+#define BT_ATT_OP_READ_BLOB_RSP			0x0d
+#define BT_ATT_OP_READ_MULT_REQ			0x0e
+#define BT_ATT_OP_READ_MULT_RSP			0x0f
+#define BT_ATT_OP_READ_BY_GRP_TYPE_REQ		0x10
+#define BT_ATT_OP_READ_BY_GRP_TYPE_RSP		0x11
+#define BT_ATT_OP_WRITE_REQ			0x12
+#define BT_ATT_OP_WRITE_RSP			0x13
+#define BT_ATT_OP_WRITE_CMD			0x52
+#define BT_ATT_OP_SIGNED_WRITE_CMD		0xD2
+#define BT_ATT_OP_PREP_WRITE_REQ		0x16
+#define BT_ATT_OP_PREP_WRITE_RSP		0x17
+#define BT_ATT_OP_EXEC_WRITE_REQ		0x18
+#define BT_ATT_OP_EXEC_WRITE_RSP		0x19
+#define BT_ATT_OP_HANDLE_VAL_NOT		0x1B
+#define BT_ATT_OP_HANDLE_VAL_IND		0x1D
+#define BT_ATT_OP_HANDLE_VAL_CONF		0x1E
+
+/* Packed struct definitions for ATT protocol PDUs */
+/* TODO: Complete these definitions for all opcodes */
+struct bt_att_pdu_error_rsp {
+	uint8_t opcode;
+	uint16_t handle;
+	uint8_t ecode;
+} __packed;
+
+/* Special opcode to receive all requests (legacy servers) */
+#define BT_ATT_ALL_REQUESTS 0x00
+
+/* Error codes for Error response PDU */
+#define BT_ATT_ERROR_INVALID_HANDLE			0x01
+#define BT_ATT_ERROR_READ_NOT_PERMITTED			0x02
+#define BT_ATT_ERROR_WRITE_NOT_PERMITTED		0x03
+#define BT_ATT_ERROR_INVALID_PDU			0x04
+#define BT_ATT_ERROR_AUTHENTICATION			0x05
+#define BT_ATT_ERROR_REQUEST_NOT_SUPPORTED		0x06
+#define BT_ATT_ERROR_INVALID_OFFSET			0x07
+#define BT_ATT_ERROR_AUTHORIZATION			0x08
+#define BT_ATT_ERROR_PREPARE_QUEUE_FULL			0x09
+#define BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND		0x0A
+#define BT_ATT_ERROR_ATTRIBUTE_NOT_LONG			0x0B
+#define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE	0x0C
+#define BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN	0x0D
+#define BT_ATT_ERROR_UNLIKELY				0x0E
+#define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION		0x0F
+#define BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE		0x10
+#define BT_ATT_ERROR_INSUFFICIENT_RESOURCES		0x11
+
+/*
+ * Common Profile and Service Error Code descriptions (see Supplement to the
+ * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
+ * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
+ * following:
+ */
+#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED      0xfd
+#define BT_ERROR_ALREADY_IN_PROGRESS            0xfe
+#define BT_ERROR_OUT_OF_RANGE                   0xff
+
+/*
+ * ATT attribute permission bitfield values. Permissions are grouped as
+ * "Access", "Encryption", "Authentication", and "Authorization". A bitmask of
+ * permissions is a byte that encodes a combination of these.
+ */
+#define BT_ATT_PERM_READ		0x01
+#define BT_ATT_PERM_WRITE		0x02
+#define BT_ATT_PERM_READ_ENCRYPT	0x04
+#define BT_ATT_PERM_WRITE_ENCRYPT	0x08
+#define BT_ATT_PERM_ENCRYPT		(BT_ATT_PERM_READ_ENCRYPT | \
+					BT_ATT_PERM_WRITE_ENCRYPT)
+#define BT_ATT_PERM_READ_AUTHEN		0x10
+#define BT_ATT_PERM_WRITE_AUTHEN	0x20
+#define BT_ATT_PERM_AUTHEN		(BT_ATT_PERM_READ_AUTHEN | \
+					BT_ATT_PERM_WRITE_AUTHEN)
+#define BT_ATT_PERM_AUTHOR		0x40
+#define BT_ATT_PERM_NONE		0x80
+
+/* GATT Characteristic Properties Bitfield values */
+#define BT_GATT_CHRC_PROP_BROADCAST			0x01
+#define BT_GATT_CHRC_PROP_READ				0x02
+#define BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP		0x04
+#define BT_GATT_CHRC_PROP_WRITE				0x08
+#define BT_GATT_CHRC_PROP_NOTIFY			0x10
+#define BT_GATT_CHRC_PROP_INDICATE			0x20
+#define BT_GATT_CHRC_PROP_AUTH				0x40
+#define BT_GATT_CHRC_PROP_EXT_PROP			0x80
+
+/* GATT Characteristic Extended Properties Bitfield values */
+#define BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE		0x01
+#define BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX		0x02
+#define BT_GATT_CHRC_EXT_PROP_ENC_READ			0x04
+#define BT_GATT_CHRC_EXT_PROP_ENC_WRITE			0x08
+#define BT_GATT_CHRC_EXT_PROP_ENC	(BT_GATT_CHRC_EXT_PROP_ENC_READ | \
+					BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
+#define BT_GATT_CHRC_EXT_PROP_AUTH_READ			0x10
+#define BT_GATT_CHRC_EXT_PROP_AUTH_WRITE		0x20
+#define BT_GATT_CHRC_EXT_PROP_AUTH	(BT_GATT_CHRC_EXT_PROP_AUTH_READ | \
+					BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
diff --git a/bluez/repo/src/shared/att.c b/bluez/repo/src/shared/att.c
new file mode 100644
index 0000000..3a84783
--- /dev/null
+++ b/bluez/repo/src/shared/att.c
@@ -0,0 +1,1443 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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 <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "src/shared/io.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "src/shared/timeout.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/att.h"
+#include "src/shared/crypto.h"
+
+#define ATT_MIN_PDU_LEN			1  /* At least 1 byte for the opcode. */
+#define ATT_OP_CMD_MASK			0x40
+#define ATT_OP_SIGNED_MASK		0x80
+#define ATT_TIMEOUT_INTERVAL		30000  /* 30000 ms */
+
+/* Length of signature in write signed packet */
+#define BT_ATT_SIGNATURE_LEN		12
+
+struct att_send_op;
+
+struct bt_att {
+	int ref_count;
+	int fd;
+	struct io *io;
+	bool io_on_l2cap;
+	int io_sec_level;		/* Only used for non-L2CAP */
+
+	struct queue *req_queue;	/* Queued ATT protocol requests */
+	struct att_send_op *pending_req;
+	struct queue *ind_queue;	/* Queued ATT protocol indications */
+	struct att_send_op *pending_ind;
+	struct queue *write_queue;	/* Queue of PDUs ready to send */
+	bool writer_active;
+
+	struct queue *notify_list;	/* List of registered callbacks */
+	struct queue *disconn_list;	/* List of disconnect handlers */
+
+	bool in_req;			/* There's a pending incoming request */
+
+	uint8_t *buf;
+	uint16_t mtu;
+
+	unsigned int next_send_id;	/* IDs for "send" ops */
+	unsigned int next_reg_id;	/* IDs for registered callbacks */
+
+	bt_att_timeout_func_t timeout_callback;
+	bt_att_destroy_func_t timeout_destroy;
+	void *timeout_data;
+
+	bt_att_debug_func_t debug_callback;
+	bt_att_destroy_func_t debug_destroy;
+	void *debug_data;
+
+	struct bt_crypto *crypto;
+
+	struct sign_info *local_sign;
+	struct sign_info *remote_sign;
+};
+
+struct sign_info {
+	uint8_t key[16];
+	bt_att_counter_func_t counter;
+	void *user_data;
+};
+
+enum att_op_type {
+	ATT_OP_TYPE_REQ,
+	ATT_OP_TYPE_RSP,
+	ATT_OP_TYPE_CMD,
+	ATT_OP_TYPE_IND,
+	ATT_OP_TYPE_NOT,
+	ATT_OP_TYPE_CONF,
+	ATT_OP_TYPE_UNKNOWN,
+};
+
+static const struct {
+	uint8_t opcode;
+	enum att_op_type type;
+} att_opcode_type_table[] = {
+	{ BT_ATT_OP_ERROR_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_MTU_REQ,			ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_MTU_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_FIND_INFO_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_FIND_INFO_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_FIND_BY_TYPE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_FIND_BY_TYPE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_BY_TYPE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_BY_TYPE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_REQ,			ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_BLOB_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_BLOB_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_MULT_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_MULT_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_BY_GRP_TYPE_REQ,	ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_BY_GRP_TYPE_RSP,	ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_WRITE_REQ,			ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_WRITE_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_WRITE_CMD,			ATT_OP_TYPE_CMD },
+	{ BT_ATT_OP_SIGNED_WRITE_CMD,		ATT_OP_TYPE_CMD },
+	{ BT_ATT_OP_PREP_WRITE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_PREP_WRITE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_EXEC_WRITE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_EXEC_WRITE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_HANDLE_VAL_NOT,		ATT_OP_TYPE_NOT },
+	{ BT_ATT_OP_HANDLE_VAL_IND,		ATT_OP_TYPE_IND },
+	{ BT_ATT_OP_HANDLE_VAL_CONF,		ATT_OP_TYPE_CONF },
+	{ }
+};
+
+static enum att_op_type get_op_type(uint8_t opcode)
+{
+	int i;
+
+	for (i = 0; att_opcode_type_table[i].opcode; i++) {
+		if (att_opcode_type_table[i].opcode == opcode)
+			return att_opcode_type_table[i].type;
+	}
+
+	return ATT_OP_TYPE_UNKNOWN;
+}
+
+static const struct {
+	uint8_t req_opcode;
+	uint8_t rsp_opcode;
+} att_req_rsp_mapping_table[] = {
+	{ BT_ATT_OP_MTU_REQ,			BT_ATT_OP_MTU_RSP },
+	{ BT_ATT_OP_FIND_INFO_REQ,		BT_ATT_OP_FIND_INFO_RSP},
+	{ BT_ATT_OP_FIND_BY_TYPE_REQ,		BT_ATT_OP_FIND_BY_TYPE_RSP },
+	{ BT_ATT_OP_READ_BY_TYPE_REQ,		BT_ATT_OP_READ_BY_TYPE_RSP },
+	{ BT_ATT_OP_READ_REQ,			BT_ATT_OP_READ_RSP },
+	{ BT_ATT_OP_READ_BLOB_REQ,		BT_ATT_OP_READ_BLOB_RSP },
+	{ BT_ATT_OP_READ_MULT_REQ,		BT_ATT_OP_READ_MULT_RSP },
+	{ BT_ATT_OP_READ_BY_GRP_TYPE_REQ,	BT_ATT_OP_READ_BY_GRP_TYPE_RSP },
+	{ BT_ATT_OP_WRITE_REQ,			BT_ATT_OP_WRITE_RSP },
+	{ BT_ATT_OP_PREP_WRITE_REQ,		BT_ATT_OP_PREP_WRITE_RSP },
+	{ BT_ATT_OP_EXEC_WRITE_REQ,		BT_ATT_OP_EXEC_WRITE_RSP },
+	{ }
+};
+
+static uint8_t get_req_opcode(uint8_t rsp_opcode)
+{
+	int i;
+
+	for (i = 0; att_req_rsp_mapping_table[i].rsp_opcode; i++) {
+		if (att_req_rsp_mapping_table[i].rsp_opcode == rsp_opcode)
+			return att_req_rsp_mapping_table[i].req_opcode;
+	}
+
+	return 0;
+}
+
+struct att_send_op {
+	unsigned int id;
+	unsigned int timeout_id;
+	enum att_op_type type;
+	uint16_t opcode;
+	void *pdu;
+	uint16_t len;
+	bt_att_response_func_t callback;
+	bt_att_destroy_func_t destroy;
+	void *user_data;
+};
+
+static void destroy_att_send_op(void *data)
+{
+	struct att_send_op *op = data;
+
+	if (op->timeout_id)
+		timeout_remove(op->timeout_id);
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op->pdu);
+	free(op);
+}
+
+static void cancel_att_send_op(struct att_send_op *op)
+{
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	op->user_data = NULL;
+	op->callback = NULL;
+	op->destroy = NULL;
+}
+
+struct att_notify {
+	unsigned int id;
+	uint16_t opcode;
+	bt_att_notify_func_t callback;
+	bt_att_destroy_func_t destroy;
+	void *user_data;
+};
+
+static void destroy_att_notify(void *data)
+{
+	struct att_notify *notify = data;
+
+	if (notify->destroy)
+		notify->destroy(notify->user_data);
+
+	free(notify);
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+	const struct att_notify *notify = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notify->id == id;
+}
+
+struct att_disconn {
+	unsigned int id;
+	bool removed;
+	bt_att_disconnect_func_t callback;
+	bt_att_destroy_func_t destroy;
+	void *user_data;
+};
+
+static void destroy_att_disconn(void *data)
+{
+	struct att_disconn *disconn = data;
+
+	if (disconn->destroy)
+		disconn->destroy(disconn->user_data);
+
+	free(disconn);
+}
+
+static bool match_disconn_id(const void *a, const void *b)
+{
+	const struct att_disconn *disconn = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return disconn->id == id;
+}
+
+static bool encode_pdu(struct bt_att *att, struct att_send_op *op,
+					const void *pdu, uint16_t length)
+{
+	uint16_t pdu_len = 1;
+	struct sign_info *sign = att->local_sign;
+	uint32_t sign_cnt;
+
+	if (sign && (op->opcode & ATT_OP_SIGNED_MASK))
+		pdu_len += BT_ATT_SIGNATURE_LEN;
+
+	if (length && pdu)
+		pdu_len += length;
+
+	if (pdu_len > att->mtu)
+		return false;
+
+	op->len = pdu_len;
+	op->pdu = malloc(op->len);
+	if (!op->pdu)
+		return false;
+
+	((uint8_t *) op->pdu)[0] = op->opcode;
+	if (pdu_len > 1)
+		memcpy(op->pdu + 1, pdu, length);
+
+	if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK) || !att->crypto)
+		return true;
+
+	if (!sign->counter(&sign_cnt, sign->user_data))
+		goto fail;
+
+	if ((bt_crypto_sign_att(att->crypto, sign->key, op->pdu, 1 + length,
+				sign_cnt, &((uint8_t *) op->pdu)[1 + length])))
+		return true;
+
+	util_debug(att->debug_callback, att->debug_data,
+					"ATT unable to generate signature");
+
+fail:
+	free(op->pdu);
+	return false;
+}
+
+static struct att_send_op *create_att_send_op(struct bt_att *att,
+						uint8_t opcode,
+						const void *pdu,
+						uint16_t length,
+						bt_att_response_func_t callback,
+						void *user_data,
+						bt_att_destroy_func_t destroy)
+{
+	struct att_send_op *op;
+	enum att_op_type type;
+
+	if (length && !pdu)
+		return NULL;
+
+	type = get_op_type(opcode);
+	if (type == ATT_OP_TYPE_UNKNOWN)
+		return NULL;
+
+	/* If the opcode corresponds to an operation type that does not elicit a
+	 * response from the remote end, then no callback should have been
+	 * provided, since it will never be called.
+	 */
+	if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND)
+		return NULL;
+
+	/* Similarly, if the operation does elicit a response then a callback
+	 * must be provided.
+	 */
+	if (!callback && (type == ATT_OP_TYPE_REQ || type == ATT_OP_TYPE_IND))
+		return NULL;
+
+	op = new0(struct att_send_op, 1);
+	op->type = type;
+	op->opcode = opcode;
+	op->callback = callback;
+	op->destroy = destroy;
+	op->user_data = user_data;
+
+	if (!encode_pdu(att, op, pdu, length)) {
+		free(op);
+		return NULL;
+	}
+
+	return op;
+}
+
+static struct att_send_op *pick_next_send_op(struct bt_att *att)
+{
+	struct att_send_op *op;
+
+	/* See if any operations are already in the write queue */
+	op = queue_pop_head(att->write_queue);
+	if (op)
+		return op;
+
+	/* If there is no pending request, pick an operation from the
+	 * request queue.
+	 */
+	if (!att->pending_req) {
+		op = queue_pop_head(att->req_queue);
+		if (op)
+			return op;
+	}
+
+	/* There is either a request pending or no requests queued. If there is
+	 * no pending indication, pick an operation from the indication queue.
+	 */
+	if (!att->pending_ind) {
+		op = queue_pop_head(att->ind_queue);
+		if (op)
+			return op;
+	}
+
+	return NULL;
+}
+
+struct timeout_data {
+	struct bt_att *att;
+	unsigned int id;
+};
+
+static bool timeout_cb(void *user_data)
+{
+	struct timeout_data *timeout = user_data;
+	struct bt_att *att = timeout->att;
+	struct att_send_op *op = NULL;
+
+	if (att->pending_req && att->pending_req->id == timeout->id) {
+		op = att->pending_req;
+		att->pending_req = NULL;
+	} else if (att->pending_ind && att->pending_ind->id == timeout->id) {
+		op = att->pending_ind;
+		att->pending_ind = NULL;
+	}
+
+	if (!op)
+		return false;
+
+	util_debug(att->debug_callback, att->debug_data,
+				"Operation timed out: 0x%02x", op->opcode);
+
+	if (att->timeout_callback)
+		att->timeout_callback(op->id, op->opcode, att->timeout_data);
+
+	op->timeout_id = 0;
+	destroy_att_send_op(op);
+
+	/*
+	 * Directly terminate the connection as required by the ATT protocol.
+	 * This should trigger an io disconnect event which will clean up the
+	 * io and notify the upper layer.
+	 */
+	io_shutdown(att->io);
+
+	return false;
+}
+
+static void write_watch_destroy(void *user_data)
+{
+	struct bt_att *att = user_data;
+
+	att->writer_active = false;
+}
+
+static bool can_write_data(struct io *io, void *user_data)
+{
+	struct bt_att *att = user_data;
+	struct att_send_op *op;
+	struct timeout_data *timeout;
+	ssize_t ret;
+	struct iovec iov;
+
+	op = pick_next_send_op(att);
+	if (!op)
+		return false;
+
+	iov.iov_base = op->pdu;
+	iov.iov_len = op->len;
+
+	ret = io_send(io, &iov, 1);
+	if (ret < 0) {
+		util_debug(att->debug_callback, att->debug_data,
+					"write failed: %s", strerror(-ret));
+		if (op->callback)
+			op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0,
+							op->user_data);
+
+		destroy_att_send_op(op);
+		return true;
+	}
+
+	util_debug(att->debug_callback, att->debug_data,
+					"ATT op 0x%02x", op->opcode);
+
+	util_hexdump('<', op->pdu, ret, att->debug_callback, att->debug_data);
+
+	/* Based on the operation type, set either the pending request or the
+	 * pending indication. If it came from the write queue, then there is
+	 * no need to keep it around.
+	 */
+	switch (op->type) {
+	case ATT_OP_TYPE_REQ:
+		att->pending_req = op;
+		break;
+	case ATT_OP_TYPE_IND:
+		att->pending_ind = op;
+		break;
+	case ATT_OP_TYPE_RSP:
+		/* Set in_req to false to indicate that no request is pending */
+		att->in_req = false;
+
+		/* Fall through to the next case */
+	case ATT_OP_TYPE_CMD:
+	case ATT_OP_TYPE_NOT:
+	case ATT_OP_TYPE_CONF:
+	case ATT_OP_TYPE_UNKNOWN:
+	default:
+		destroy_att_send_op(op);
+		return true;
+	}
+
+	timeout = new0(struct timeout_data, 1);
+	timeout->att = att;
+	timeout->id = op->id;
+	op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb,
+								timeout, free);
+
+	/* Return true as there may be more operations ready to write. */
+	return true;
+}
+
+static void wakeup_writer(struct bt_att *att)
+{
+	if (att->writer_active)
+		return;
+
+	/* Set the write handler only if there is anything that can be sent
+	 * at all.
+	 */
+	if (queue_isempty(att->write_queue)) {
+		if ((att->pending_req || queue_isempty(att->req_queue)) &&
+			(att->pending_ind || queue_isempty(att->ind_queue)))
+			return;
+	}
+
+	if (!io_set_write_handler(att->io, can_write_data, att,
+							write_watch_destroy))
+		return;
+
+	att->writer_active = true;
+}
+
+static void disconn_handler(void *data, void *user_data)
+{
+	struct att_disconn *disconn = data;
+	int err = PTR_TO_INT(user_data);
+
+	if (disconn->removed)
+		return;
+
+	if (disconn->callback)
+		disconn->callback(err, disconn->user_data);
+}
+
+static bool disconnect_cb(struct io *io, void *user_data)
+{
+	struct bt_att *att = user_data;
+	int err;
+	socklen_t len;
+
+	len = sizeof(err);
+
+	if (getsockopt(att->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+		util_debug(att->debug_callback, att->debug_data,
+					"Failed to obtain disconnect error: %s",
+					strerror(errno));
+		err = 0;
+	}
+
+	util_debug(att->debug_callback, att->debug_data,
+					"Physical link disconnected: %s",
+					strerror(err));
+
+	io_destroy(att->io);
+	att->io = NULL;
+
+	bt_att_cancel_all(att);
+
+	bt_att_ref(att);
+
+	queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err));
+
+	bt_att_unregister_all(att);
+	bt_att_unref(att);
+
+	return false;
+}
+
+static bool change_security(struct bt_att *att, uint8_t ecode)
+{
+	int security;
+
+	security = bt_att_get_security(att);
+	if (security != BT_ATT_SECURITY_AUTO)
+		return false;
+
+	if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
+					security < BT_ATT_SECURITY_MEDIUM)
+		security = BT_ATT_SECURITY_MEDIUM;
+	else if (ecode == BT_ATT_ERROR_AUTHENTICATION &&
+					security < BT_ATT_SECURITY_HIGH)
+		security = BT_ATT_SECURITY_HIGH;
+	else
+		return false;
+
+	return bt_att_set_security(att, security);
+}
+
+static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
+					ssize_t pdu_len, uint8_t *opcode)
+{
+	const struct bt_att_pdu_error_rsp *rsp;
+	struct att_send_op *op = att->pending_req;
+
+	if (pdu_len != sizeof(*rsp)) {
+		*opcode = 0;
+		return false;
+	}
+
+	rsp = (void *) pdu;
+
+	*opcode = rsp->opcode;
+
+	/* Attempt to change security */
+	if (!change_security(att, rsp->ecode))
+		return false;
+
+	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);
+}
+
+static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
+								ssize_t pdu_len)
+{
+	struct att_send_op *op = att->pending_req;
+	uint8_t req_opcode;
+	uint8_t rsp_opcode;
+	uint8_t *rsp_pdu = NULL;
+	uint16_t rsp_pdu_len = 0;
+
+	/*
+	 * If no request is pending, then the response is unexpected. Disconnect
+	 * the bearer.
+	 */
+	if (!op) {
+		util_debug(att->debug_callback, att->debug_data,
+					"Received unexpected ATT response");
+		io_shutdown(att->io);
+		return;
+	}
+
+	/*
+	 * If the received response doesn't match the pending request, or if
+	 * the request is malformed, end the current request with failure.
+	 */
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		/* Return if error response cause a retry */
+		if (handle_error_rsp(att, pdu, pdu_len, &req_opcode)) {
+			wakeup_writer(att);
+			return;
+		}
+	} else if (!(req_opcode = get_req_opcode(opcode)))
+		goto fail;
+
+	if (req_opcode != op->opcode)
+		goto fail;
+
+	rsp_opcode = opcode;
+
+	if (pdu_len > 0) {
+		rsp_pdu = pdu;
+		rsp_pdu_len = pdu_len;
+	}
+
+	goto done;
+
+fail:
+	util_debug(att->debug_callback, att->debug_data,
+			"Failed to handle response PDU; opcode: 0x%02x", opcode);
+
+	rsp_opcode = BT_ATT_OP_ERROR_RSP;
+
+done:
+	if (op->callback)
+		op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data);
+
+	destroy_att_send_op(op);
+	att->pending_req = NULL;
+
+	wakeup_writer(att);
+}
+
+static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
+{
+	struct att_send_op *op = att->pending_ind;
+
+	/*
+	 * Disconnect the bearer if the confirmation is unexpected or the PDU is
+	 * invalid.
+	 */
+	if (!op || pdu_len) {
+		util_debug(att->debug_callback, att->debug_data,
+				"Received unexpected/invalid ATT confirmation");
+		io_shutdown(att->io);
+		return;
+	}
+
+	if (op->callback)
+		op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data);
+
+	destroy_att_send_op(op);
+	att->pending_ind = NULL;
+
+	wakeup_writer(att);
+}
+
+struct notify_data {
+	uint8_t opcode;
+	uint8_t *pdu;
+	ssize_t pdu_len;
+	bool handler_found;
+};
+
+static bool opcode_match(uint8_t opcode, uint8_t test_opcode)
+{
+	enum att_op_type op_type = get_op_type(test_opcode);
+
+	if (opcode == BT_ATT_ALL_REQUESTS && (op_type == ATT_OP_TYPE_REQ ||
+						op_type == ATT_OP_TYPE_CMD))
+		return true;
+
+	return opcode == test_opcode;
+}
+
+static void respond_not_supported(struct bt_att *att, uint8_t opcode)
+{
+	struct bt_att_pdu_error_rsp pdu;
+
+	pdu.opcode = opcode;
+	pdu.handle = 0x0000;
+	pdu.ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+	bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), NULL, NULL,
+									NULL);
+}
+
+static bool handle_signed(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
+								ssize_t pdu_len)
+{
+	uint8_t *signature;
+	uint32_t sign_cnt;
+	struct sign_info *sign;
+
+	/* Check if there is enough data for a signature */
+	if (pdu_len < 2 + BT_ATT_SIGNATURE_LEN)
+		goto fail;
+
+	sign = att->remote_sign;
+	if (!sign)
+		goto fail;
+
+	signature = pdu + (pdu_len - BT_ATT_SIGNATURE_LEN);
+	sign_cnt = get_le32(signature);
+
+	/* Validate counter */
+	if (!sign->counter(&sign_cnt, sign->user_data))
+		goto fail;
+
+	/* Generate signature and verify it */
+	if (!bt_crypto_sign_att(att->crypto, sign->key, pdu,
+				pdu_len - BT_ATT_SIGNATURE_LEN, sign_cnt,
+				signature))
+		goto fail;
+
+	return true;
+
+fail:
+	util_debug(att->debug_callback, att->debug_data,
+			"ATT failed to verify signature: 0x%02x", opcode);
+
+	return false;
+}
+
+static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
+								ssize_t pdu_len)
+{
+	const struct queue_entry *entry;
+	bool found;
+
+	if ((opcode & ATT_OP_SIGNED_MASK) && !att->crypto) {
+		if (!handle_signed(att, opcode, pdu, pdu_len))
+			return;
+		pdu_len -= BT_ATT_SIGNATURE_LEN;
+	}
+
+	bt_att_ref(att);
+
+	found = false;
+	entry = queue_get_entries(att->notify_list);
+
+	while (entry) {
+		struct att_notify *notify = entry->data;
+
+		entry = entry->next;
+
+		if (!opcode_match(notify->opcode, opcode))
+			continue;
+
+		found = true;
+
+		if (notify->callback)
+			notify->callback(opcode, pdu, pdu_len,
+							notify->user_data);
+
+		/* callback could remove all entries from notify list */
+		if (queue_isempty(att->notify_list))
+			break;
+	}
+
+	/*
+	 * If this was a request and no handler was registered for it, respond
+	 * with "Not Supported"
+	 */
+	if (!found && get_op_type(opcode) == ATT_OP_TYPE_REQ)
+		respond_not_supported(att, opcode);
+
+	bt_att_unref(att);
+}
+
+static bool can_read_data(struct io *io, void *user_data)
+{
+	struct bt_att *att = user_data;
+	uint8_t opcode;
+	uint8_t *pdu;
+	ssize_t bytes_read;
+
+	bytes_read = read(att->fd, att->buf, att->mtu);
+	if (bytes_read < 0)
+		return false;
+
+	util_hexdump('>', att->buf, bytes_read,
+					att->debug_callback, att->debug_data);
+
+	if (bytes_read < ATT_MIN_PDU_LEN)
+		return true;
+
+	pdu = att->buf;
+	opcode = pdu[0];
+
+	bt_att_ref(att);
+
+	/* Act on the received PDU based on the opcode type */
+	switch (get_op_type(opcode)) {
+	case ATT_OP_TYPE_RSP:
+		util_debug(att->debug_callback, att->debug_data,
+				"ATT response received: 0x%02x", opcode);
+		handle_rsp(att, opcode, pdu + 1, bytes_read - 1);
+		break;
+	case ATT_OP_TYPE_CONF:
+		util_debug(att->debug_callback, att->debug_data,
+				"ATT confirmation received: 0x%02x", opcode);
+		handle_conf(att, pdu + 1, bytes_read - 1);
+		break;
+	case ATT_OP_TYPE_REQ:
+		/*
+		 * If a request is currently pending, then the sequential
+		 * protocol was violated. Disconnect the bearer, which will
+		 * promptly notify the upper layer via disconnect handlers.
+		 */
+		if (att->in_req) {
+			util_debug(att->debug_callback, att->debug_data,
+					"Received request while another is "
+					"pending: 0x%02x", opcode);
+			io_shutdown(att->io);
+			bt_att_unref(att);
+
+			return false;
+		}
+
+		att->in_req = true;
+
+		/* Fall through to the next case */
+	case ATT_OP_TYPE_CMD:
+	case ATT_OP_TYPE_NOT:
+	case ATT_OP_TYPE_UNKNOWN:
+	case ATT_OP_TYPE_IND:
+	default:
+		/* For all other opcodes notify the upper layer of the PDU and
+		 * let them act on it.
+		 */
+		util_debug(att->debug_callback, att->debug_data,
+					"ATT PDU received: 0x%02x", opcode);
+		handle_notify(att, opcode, pdu + 1, bytes_read - 1);
+		break;
+	}
+
+	bt_att_unref(att);
+
+	return true;
+}
+
+static bool is_io_l2cap_based(int fd)
+{
+	int domain;
+	int proto;
+	int err;
+	socklen_t len;
+
+	domain = 0;
+	len = sizeof(domain);
+	err = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &len);
+	if (err < 0)
+		return false;
+
+	if (domain != AF_BLUETOOTH)
+		return false;
+
+	proto = 0;
+	len = sizeof(proto);
+	err = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &proto, &len);
+	if (err < 0)
+		return false;
+
+	return proto == BTPROTO_L2CAP;
+}
+
+static void bt_att_free(struct bt_att *att)
+{
+	if (att->pending_req)
+		destroy_att_send_op(att->pending_req);
+
+	if (att->pending_ind)
+		destroy_att_send_op(att->pending_ind);
+
+	io_destroy(att->io);
+	bt_crypto_unref(att->crypto);
+
+	queue_destroy(att->req_queue, NULL);
+	queue_destroy(att->ind_queue, NULL);
+	queue_destroy(att->write_queue, NULL);
+	queue_destroy(att->notify_list, NULL);
+	queue_destroy(att->disconn_list, NULL);
+
+	if (att->timeout_destroy)
+		att->timeout_destroy(att->timeout_data);
+
+	if (att->debug_destroy)
+		att->debug_destroy(att->debug_data);
+
+	free(att->local_sign);
+	free(att->remote_sign);
+
+	free(att->buf);
+
+	free(att);
+}
+
+struct bt_att *bt_att_new(int fd, bool ext_signed)
+{
+	struct bt_att *att;
+
+	if (fd < 0)
+		return NULL;
+
+	att = new0(struct bt_att, 1);
+	att->fd = fd;
+	att->mtu = BT_ATT_DEFAULT_LE_MTU;
+	att->buf = malloc(att->mtu);
+	if (!att->buf)
+		goto fail;
+
+	att->io = io_new(fd);
+	if (!att->io)
+		goto fail;
+
+	/* crypto is optional, if not available leave it NULL */
+	if (!ext_signed)
+		att->crypto = bt_crypto_new();
+
+	att->req_queue = queue_new();
+	att->ind_queue = queue_new();
+	att->write_queue = queue_new();
+	att->notify_list = queue_new();
+	att->disconn_list = queue_new();
+
+	if (!io_set_read_handler(att->io, can_read_data, att, NULL))
+		goto fail;
+
+	if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL))
+		goto fail;
+
+	att->io_on_l2cap = is_io_l2cap_based(att->fd);
+	if (!att->io_on_l2cap)
+		att->io_sec_level = BT_SECURITY_LOW;
+
+	return bt_att_ref(att);
+
+fail:
+	bt_att_free(att);
+
+	return NULL;
+}
+
+struct bt_att *bt_att_ref(struct bt_att *att)
+{
+	if (!att)
+		return NULL;
+
+	__sync_fetch_and_add(&att->ref_count, 1);
+
+	return att;
+}
+
+void bt_att_unref(struct bt_att *att)
+{
+	if (!att)
+		return;
+
+	if (__sync_sub_and_fetch(&att->ref_count, 1))
+		return;
+
+	bt_att_unregister_all(att);
+	bt_att_cancel_all(att);
+
+	bt_att_free(att);
+}
+
+bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
+{
+	if (!att || !att->io)
+		return false;
+
+	return io_set_close_on_destroy(att->io, do_close);
+}
+
+int bt_att_get_fd(struct bt_att *att)
+{
+	if (!att)
+		return -1;
+
+	return att->fd;
+}
+
+bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
+				void *user_data, bt_att_destroy_func_t destroy)
+{
+	if (!att)
+		return false;
+
+	if (att->debug_destroy)
+		att->debug_destroy(att->debug_data);
+
+	att->debug_callback = callback;
+	att->debug_destroy = destroy;
+	att->debug_data = user_data;
+
+	return true;
+}
+
+uint16_t bt_att_get_mtu(struct bt_att *att)
+{
+	if (!att)
+		return 0;
+
+	return att->mtu;
+}
+
+bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
+{
+	void *buf;
+
+	if (!att)
+		return false;
+
+	if (mtu < BT_ATT_DEFAULT_LE_MTU)
+		return false;
+
+	buf = malloc(mtu);
+	if (!buf)
+		return false;
+
+	free(att->buf);
+
+	att->mtu = mtu;
+	att->buf = buf;
+
+	return true;
+}
+
+bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
+						void *user_data,
+						bt_att_destroy_func_t destroy)
+{
+	if (!att)
+		return false;
+
+	if (att->timeout_destroy)
+		att->timeout_destroy(att->timeout_data);
+
+	att->timeout_callback = callback;
+	att->timeout_destroy = destroy;
+	att->timeout_data = user_data;
+
+	return true;
+}
+
+unsigned int bt_att_register_disconnect(struct bt_att *att,
+					bt_att_disconnect_func_t callback,
+					void *user_data,
+					bt_att_destroy_func_t destroy)
+{
+	struct att_disconn *disconn;
+
+	if (!att || !att->io)
+		return 0;
+
+	disconn = new0(struct att_disconn, 1);
+	disconn->callback = callback;
+	disconn->destroy = destroy;
+	disconn->user_data = user_data;
+
+	if (att->next_reg_id < 1)
+		att->next_reg_id = 1;
+
+	disconn->id = att->next_reg_id++;
+
+	if (!queue_push_tail(att->disconn_list, disconn)) {
+		free(disconn);
+		return 0;
+	}
+
+	return disconn->id;
+}
+
+bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
+{
+	struct att_disconn *disconn;
+
+	if (!att || !id)
+		return false;
+
+	disconn = queue_remove_if(att->disconn_list, match_disconn_id,
+							UINT_TO_PTR(id));
+	if (!disconn)
+		return false;
+
+	destroy_att_disconn(disconn);
+	return true;
+}
+
+unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
+				const void *pdu, uint16_t length,
+				bt_att_response_func_t callback, void *user_data,
+				bt_att_destroy_func_t destroy)
+{
+	struct att_send_op *op;
+	bool result;
+
+	if (!att || !att->io)
+		return 0;
+
+	op = create_att_send_op(att, opcode, pdu, length, callback, user_data,
+								destroy);
+	if (!op)
+		return 0;
+
+	if (att->next_send_id < 1)
+		att->next_send_id = 1;
+
+	op->id = att->next_send_id++;
+
+	/* Add the op to the correct queue based on its type */
+	switch (op->type) {
+	case ATT_OP_TYPE_REQ:
+		result = queue_push_tail(att->req_queue, op);
+		break;
+	case ATT_OP_TYPE_IND:
+		result = queue_push_tail(att->ind_queue, op);
+		break;
+	case ATT_OP_TYPE_CMD:
+	case ATT_OP_TYPE_NOT:
+	case ATT_OP_TYPE_UNKNOWN:
+	case ATT_OP_TYPE_RSP:
+	case ATT_OP_TYPE_CONF:
+	default:
+		result = queue_push_tail(att->write_queue, op);
+		break;
+	}
+
+	if (!result) {
+		free(op->pdu);
+		free(op);
+		return 0;
+	}
+
+	wakeup_writer(att);
+
+	return op->id;
+}
+
+static bool match_op_id(const void *a, const void *b)
+{
+	const struct att_send_op *op = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return op->id == id;
+}
+
+bool bt_att_cancel(struct bt_att *att, unsigned int id)
+{
+	struct att_send_op *op;
+
+	if (!att || !id)
+		return false;
+
+	if (att->pending_req && att->pending_req->id == id) {
+		/* Don't cancel the pending request; remove it's handlers */
+		cancel_att_send_op(att->pending_req);
+		return true;
+	}
+
+	if (att->pending_ind && att->pending_ind->id == id) {
+		/* Don't cancel the pending indication; remove it's handlers */
+		cancel_att_send_op(att->pending_ind);
+		return true;
+	}
+
+	op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
+	if (op)
+		goto done;
+
+	op = queue_remove_if(att->ind_queue, match_op_id, UINT_TO_PTR(id));
+	if (op)
+		goto done;
+
+	op = queue_remove_if(att->write_queue, match_op_id, UINT_TO_PTR(id));
+	if (op)
+		goto done;
+
+	if (!op)
+		return false;
+
+done:
+	destroy_att_send_op(op);
+
+	wakeup_writer(att);
+
+	return true;
+}
+
+bool bt_att_cancel_all(struct bt_att *att)
+{
+	if (!att)
+		return false;
+
+	queue_remove_all(att->req_queue, NULL, NULL, destroy_att_send_op);
+	queue_remove_all(att->ind_queue, NULL, NULL, destroy_att_send_op);
+	queue_remove_all(att->write_queue, NULL, NULL, destroy_att_send_op);
+
+	if (att->pending_req)
+		/* Don't cancel the pending request; remove it's handlers */
+		cancel_att_send_op(att->pending_req);
+
+	if (att->pending_ind)
+		/* Don't cancel the pending request; remove it's handlers */
+		cancel_att_send_op(att->pending_ind);
+
+	return true;
+}
+
+static uint8_t att_ecode_from_error(int err)
+{
+	/*
+	 * If the error fits in a single byte, treat it as an ATT protocol
+	 * error as is. Since "0" is not a valid ATT protocol error code, we map
+	 * that to UNLIKELY below.
+	 */
+	if (err > 0 && err < UINT8_MAX)
+		return err;
+
+	/*
+	 * Since we allow UNIX errnos, map them to appropriate ATT protocol
+	 * and "Common Profile and Service" error codes.
+	 */
+	switch (err) {
+	case -ENOENT:
+		return BT_ATT_ERROR_INVALID_HANDLE;
+	case -ENOMEM:
+		return BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+	case -EALREADY:
+		return BT_ERROR_ALREADY_IN_PROGRESS;
+	case -EOVERFLOW:
+		return BT_ERROR_OUT_OF_RANGE;
+	}
+
+	return BT_ATT_ERROR_UNLIKELY;
+}
+
+unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
+						uint16_t handle, int error)
+{
+	struct bt_att_pdu_error_rsp pdu;
+	uint8_t ecode;
+
+	if (!att || !opcode)
+		return 0;
+
+	ecode = att_ecode_from_error(error);
+
+	memset(&pdu, 0, sizeof(pdu));
+
+	pdu.opcode = opcode;
+	put_le16(handle, &pdu.handle);
+	pdu.ecode = ecode;
+
+	return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu),
+							NULL, NULL, NULL);
+}
+
+unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
+						bt_att_notify_func_t callback,
+						void *user_data,
+						bt_att_destroy_func_t destroy)
+{
+	struct att_notify *notify;
+
+	if (!att || !callback || !att->io)
+		return 0;
+
+	notify = new0(struct att_notify, 1);
+	notify->opcode = opcode;
+	notify->callback = callback;
+	notify->destroy = destroy;
+	notify->user_data = user_data;
+
+	if (att->next_reg_id < 1)
+		att->next_reg_id = 1;
+
+	notify->id = att->next_reg_id++;
+
+	if (!queue_push_tail(att->notify_list, notify)) {
+		free(notify);
+		return 0;
+	}
+
+	return notify->id;
+}
+
+bool bt_att_unregister(struct bt_att *att, unsigned int id)
+{
+	struct att_notify *notify;
+
+	if (!att || !id)
+		return false;
+
+	notify = queue_remove_if(att->notify_list, match_notify_id,
+							UINT_TO_PTR(id));
+	if (!notify)
+		return false;
+
+	destroy_att_notify(notify);
+	return true;
+}
+
+bool bt_att_unregister_all(struct bt_att *att)
+{
+	if (!att)
+		return false;
+
+	queue_remove_all(att->notify_list, NULL, NULL, destroy_att_notify);
+	queue_remove_all(att->disconn_list, NULL, NULL, destroy_att_disconn);
+
+	return true;
+}
+
+int bt_att_get_security(struct bt_att *att)
+{
+	struct bt_security sec;
+	socklen_t len;
+
+	if (!att)
+		return -EINVAL;
+
+	if (!att->io_on_l2cap)
+		return att->io_sec_level;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
+		return -EIO;
+
+	return sec.level;
+}
+
+bool bt_att_set_security(struct bt_att *att, int level)
+{
+	struct bt_security sec;
+
+	if (!att || level < BT_ATT_SECURITY_AUTO ||
+						level > BT_ATT_SECURITY_HIGH)
+		return false;
+
+	if (!att->io_on_l2cap) {
+		att->io_sec_level = level;
+		return true;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) < 0)
+		return false;
+
+	return true;
+}
+
+static bool sign_set_key(struct sign_info **sign, uint8_t key[16],
+				bt_att_counter_func_t func, void *user_data)
+{
+	if (!(*sign))
+		*sign = new0(struct sign_info, 1);
+
+	(*sign)->counter = func;
+	(*sign)->user_data = user_data;
+	memcpy((*sign)->key, key, 16);
+
+	return true;
+}
+
+bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
+				bt_att_counter_func_t func, void *user_data)
+{
+	if (!att)
+		return false;
+
+	return sign_set_key(&att->local_sign, sign_key, func, user_data);
+}
+
+bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
+				bt_att_counter_func_t func, void *user_data)
+{
+	if (!att)
+		return false;
+
+	return sign_set_key(&att->remote_sign, sign_key, func, user_data);
+}
+
+bool bt_att_has_crypto(struct bt_att *att)
+{
+	if (!att)
+		return false;
+
+	return att->crypto ? true : false;
+}
diff --git a/bluez/repo/src/shared/att.h b/bluez/repo/src/shared/att.h
new file mode 100644
index 0000000..2a7f87e
--- /dev/null
+++ b/bluez/repo/src/shared/att.h
@@ -0,0 +1,93 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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>
+
+#include "src/shared/att-types.h"
+
+struct bt_att;
+
+struct bt_att *bt_att_new(int fd, bool ext_signed);
+
+struct bt_att *bt_att_ref(struct bt_att *att);
+void bt_att_unref(struct bt_att *att);
+
+bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
+
+int bt_att_get_fd(struct bt_att *att);
+
+typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data);
+typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data);
+typedef void (*bt_att_destroy_func_t)(void *user_data);
+typedef void (*bt_att_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_att_timeout_func_t)(unsigned int id, uint8_t opcode,
+							void *user_data);
+typedef void (*bt_att_disconnect_func_t)(int err, void *user_data);
+typedef bool (*bt_att_counter_func_t)(uint32_t *sign_cnt, void *user_data);
+
+bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
+				void *user_data, bt_att_destroy_func_t destroy);
+
+uint16_t bt_att_get_mtu(struct bt_att *att);
+bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu);
+
+bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
+						void *user_data,
+						bt_att_destroy_func_t destroy);
+
+unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
+					const void *pdu, uint16_t length,
+					bt_att_response_func_t callback,
+					void *user_data,
+					bt_att_destroy_func_t destroy);
+bool bt_att_cancel(struct bt_att *att, unsigned int id);
+bool bt_att_cancel_all(struct bt_att *att);
+
+unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
+						uint16_t handle, int error);
+
+unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
+						bt_att_notify_func_t callback,
+						void *user_data,
+						bt_att_destroy_func_t destroy);
+bool bt_att_unregister(struct bt_att *att, unsigned int id);
+
+unsigned int bt_att_register_disconnect(struct bt_att *att,
+					bt_att_disconnect_func_t callback,
+					void *user_data,
+					bt_att_destroy_func_t destroy);
+bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id);
+
+bool bt_att_unregister_all(struct bt_att *att);
+
+int bt_att_get_security(struct bt_att *att);
+bool bt_att_set_security(struct bt_att *att, int level);
+
+bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
+			bt_att_counter_func_t func, void *user_data);
+bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
+			bt_att_counter_func_t func, void *user_data);
+bool bt_att_has_crypto(struct bt_att *att);
diff --git a/bluez/repo/src/shared/btsnoop.c b/bluez/repo/src/shared/btsnoop.c
new file mode 100644
index 0000000..cec1b21
--- /dev/null
+++ b/bluez/repo/src/shared/btsnoop.c
@@ -0,0 +1,498 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <endian.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include "src/shared/btsnoop.h"
+
+struct btsnoop_hdr {
+	uint8_t		id[8];		/* Identification Pattern */
+	uint32_t	version;	/* Version Number = 1 */
+	uint32_t	type;		/* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+	uint32_t	size;		/* Original Length */
+	uint32_t	len;		/* Included Length */
+	uint32_t	flags;		/* Packet Flags */
+	uint32_t	drops;		/* Cumulative Drops */
+	uint64_t	ts;		/* Timestamp microseconds */
+	uint8_t		data[0];	/* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
+				      0x6f, 0x6f, 0x70, 0x00 };
+
+static const uint32_t btsnoop_version = 1;
+
+struct pklg_pkt {
+	uint32_t	len;
+	uint64_t	ts;
+	uint8_t		type;
+} __attribute__ ((packed));
+#define PKLG_PKT_SIZE (sizeof(struct pklg_pkt))
+
+struct btsnoop {
+	int ref_count;
+	int fd;
+	unsigned long flags;
+	uint32_t format;
+	uint16_t index;
+	bool aborted;
+	bool pklg_format;
+	bool pklg_v2;
+};
+
+struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
+{
+	struct btsnoop *btsnoop;
+	struct btsnoop_hdr hdr;
+	ssize_t len;
+
+	btsnoop = calloc(1, sizeof(*btsnoop));
+	if (!btsnoop)
+		return NULL;
+
+	btsnoop->fd = open(path, O_RDONLY | O_CLOEXEC);
+	if (btsnoop->fd < 0) {
+		free(btsnoop);
+		return NULL;
+	}
+
+	btsnoop->flags = flags;
+
+	len = read(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+	if (len < 0 || len != BTSNOOP_HDR_SIZE)
+		goto failed;
+
+	if (!memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
+		/* Check for BTSnoop version 1 format */
+		if (be32toh(hdr.version) != btsnoop_version)
+			goto failed;
+
+		btsnoop->format = be32toh(hdr.type);
+		btsnoop->index = 0xffff;
+	} else {
+		if (!(btsnoop->flags & BTSNOOP_FLAG_PKLG_SUPPORT))
+			goto failed;
+
+		/* Check for Apple Packet Logger format */
+		if (hdr.id[0] != 0x00 ||
+				(hdr.id[1] != 0x00 && hdr.id[1] != 0x01))
+			goto failed;
+
+		btsnoop->format = BTSNOOP_FORMAT_MONITOR;
+		btsnoop->index = 0xffff;
+		btsnoop->pklg_format = true;
+		btsnoop->pklg_v2 = (hdr.id[1] == 0x01);
+
+		/* Apple Packet Logger format has no header */
+		lseek(btsnoop->fd, 0, SEEK_SET);
+	}
+
+	return btsnoop_ref(btsnoop);
+
+failed:
+	close(btsnoop->fd);
+	free(btsnoop);
+
+	return NULL;
+}
+
+struct btsnoop *btsnoop_create(const char *path, uint32_t format)
+{
+	struct btsnoop *btsnoop;
+	struct btsnoop_hdr hdr;
+	ssize_t written;
+
+	btsnoop = calloc(1, sizeof(*btsnoop));
+	if (!btsnoop)
+		return NULL;
+
+	btsnoop->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+					S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if (btsnoop->fd < 0) {
+		free(btsnoop);
+		return NULL;
+	}
+
+	btsnoop->format = format;
+	btsnoop->index = 0xffff;
+
+	memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+	hdr.version = htobe32(btsnoop_version);
+	hdr.type = htobe32(btsnoop->format);
+
+	written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+	if (written < 0) {
+		close(btsnoop->fd);
+		free(btsnoop);
+		return NULL;
+	}
+
+	return btsnoop_ref(btsnoop);
+}
+
+struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop)
+{
+	if (!btsnoop)
+		return NULL;
+
+	__sync_fetch_and_add(&btsnoop->ref_count, 1);
+
+	return btsnoop;
+}
+
+void btsnoop_unref(struct btsnoop *btsnoop)
+{
+	if (!btsnoop)
+		return;
+
+	if (__sync_sub_and_fetch(&btsnoop->ref_count, 1))
+		return;
+
+	if (btsnoop->fd >= 0)
+		close(btsnoop->fd);
+
+	free(btsnoop);
+}
+
+uint32_t btsnoop_get_format(struct btsnoop *btsnoop)
+{
+	if (!btsnoop)
+		return BTSNOOP_FORMAT_INVALID;
+
+	return btsnoop->format;
+}
+
+bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
+			uint32_t flags, const void *data, uint16_t size)
+{
+	struct btsnoop_pkt pkt;
+	uint64_t ts;
+	ssize_t written;
+
+	if (!btsnoop || !tv)
+		return false;
+
+	ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
+
+	pkt.size  = htobe32(size);
+	pkt.len   = htobe32(size);
+	pkt.flags = htobe32(flags);
+	pkt.drops = htobe32(0);
+	pkt.ts    = htobe64(ts + 0x00E03AB44A676000ll);
+
+	written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
+	if (written < 0)
+		return false;
+
+	if (data && size > 0) {
+		written = write(btsnoop->fd, data, size);
+		if (written < 0)
+			return false;
+	}
+
+	return true;
+}
+
+static uint32_t get_flags_from_opcode(uint16_t opcode)
+{
+	switch (opcode) {
+	case BTSNOOP_OPCODE_NEW_INDEX:
+	case BTSNOOP_OPCODE_DEL_INDEX:
+		break;
+	case BTSNOOP_OPCODE_COMMAND_PKT:
+		return 0x02;
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		return 0x03;
+	case BTSNOOP_OPCODE_ACL_TX_PKT:
+		return 0x00;
+	case BTSNOOP_OPCODE_ACL_RX_PKT:
+		return 0x01;
+	case BTSNOOP_OPCODE_SCO_TX_PKT:
+	case BTSNOOP_OPCODE_SCO_RX_PKT:
+		break;
+	case BTSNOOP_OPCODE_OPEN_INDEX:
+	case BTSNOOP_OPCODE_CLOSE_INDEX:
+		break;
+	}
+
+	return 0xff;
+}
+
+bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
+					uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size)
+{
+	uint32_t flags;
+
+	if (!btsnoop)
+		return false;
+
+	switch (btsnoop->format) {
+	case BTSNOOP_FORMAT_HCI:
+		if (btsnoop->index == 0xffff)
+			btsnoop->index = index;
+
+		if (index != btsnoop->index)
+			return false;
+
+		flags = get_flags_from_opcode(opcode);
+		if (flags == 0xff)
+			return false;
+		break;
+
+	case BTSNOOP_FORMAT_MONITOR:
+		flags = (index << 16) | opcode;
+		break;
+
+	default:
+		return false;
+	}
+
+	return btsnoop_write(btsnoop, tv, flags, data, size);
+}
+
+bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
+			uint16_t frequency, const void *data, uint16_t size)
+{
+	uint32_t flags;
+
+	if (!btsnoop)
+		return false;
+
+	switch (btsnoop->format) {
+	case BTSNOOP_FORMAT_SIMULATOR:
+		flags = (1 << 16) | frequency;
+		break;
+
+	default:
+		return false;
+	}
+
+	return btsnoop_write(btsnoop, tv, flags, data, size);
+}
+
+static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
+					uint16_t *index, uint16_t *opcode,
+					void *data, uint16_t *size)
+{
+	struct pklg_pkt pkt;
+	uint32_t toread;
+	uint64_t ts;
+	ssize_t len;
+
+	len = read(btsnoop->fd, &pkt, PKLG_PKT_SIZE);
+	if (len == 0)
+		return false;
+
+	if (len < 0 || len != PKLG_PKT_SIZE) {
+		btsnoop->aborted = true;
+		return false;
+	}
+
+	if (btsnoop->pklg_v2) {
+		toread = le32toh(pkt.len) - (PKLG_PKT_SIZE - 4);
+
+		ts = le64toh(pkt.ts);
+		tv->tv_sec = ts & 0xffffffff;
+		tv->tv_usec = ts >> 32;
+	} else {
+		toread = be32toh(pkt.len) - (PKLG_PKT_SIZE - 4);
+
+		ts = be64toh(pkt.ts);
+		tv->tv_sec = ts >> 32;
+		tv->tv_usec = ts & 0xffffffff;
+	}
+
+	switch (pkt.type) {
+	case 0x00:
+		*index = 0x0000;
+		*opcode = BTSNOOP_OPCODE_COMMAND_PKT;
+		break;
+	case 0x01:
+		*index = 0x0000;
+		*opcode = BTSNOOP_OPCODE_EVENT_PKT;
+		break;
+	case 0x02:
+		*index = 0x0000;
+		*opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
+		break;
+	case 0x03:
+		*index = 0x0000;
+		*opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
+		break;
+	case 0x0b:
+		*index = 0x0000;
+		*opcode = BTSNOOP_OPCODE_VENDOR_DIAG;
+		break;
+	case 0xfc:
+		*index = 0xffff;
+		*opcode = BTSNOOP_OPCODE_SYSTEM_NOTE;
+		break;
+	default:
+		*index = 0xffff;
+		*opcode = 0xffff;
+		break;
+	}
+
+	len = read(btsnoop->fd, data, toread);
+	if (len < 0) {
+		btsnoop->aborted = true;
+		return false;
+	}
+
+	*size = toread;
+
+	return true;
+}
+
+static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
+{
+	switch (type) {
+	case 0x01:
+		return BTSNOOP_OPCODE_COMMAND_PKT;
+	case 0x02:
+		if (flags & 0x01)
+			return BTSNOOP_OPCODE_ACL_RX_PKT;
+		else
+			return BTSNOOP_OPCODE_ACL_TX_PKT;
+	case 0x03:
+		if (flags & 0x01)
+			return BTSNOOP_OPCODE_SCO_RX_PKT;
+		else
+			return BTSNOOP_OPCODE_SCO_TX_PKT;
+	case 0x04:
+		return BTSNOOP_OPCODE_EVENT_PKT;
+	case 0xff:
+		if (flags & 0x02) {
+			if (flags & 0x01)
+				return BTSNOOP_OPCODE_EVENT_PKT;
+			else
+				return BTSNOOP_OPCODE_COMMAND_PKT;
+		} else {
+			if (flags & 0x01)
+				return BTSNOOP_OPCODE_ACL_RX_PKT;
+			else
+				return BTSNOOP_OPCODE_ACL_TX_PKT;
+		}
+		break;
+	}
+
+	return 0xffff;
+}
+
+bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
+					uint16_t *index, uint16_t *opcode,
+					void *data, uint16_t *size)
+{
+	struct btsnoop_pkt pkt;
+	uint32_t toread, flags;
+	uint64_t ts;
+	uint8_t pkt_type;
+	ssize_t len;
+
+	if (!btsnoop || btsnoop->aborted)
+		return false;
+
+	if (btsnoop->pklg_format)
+		return pklg_read_hci(btsnoop, tv, index, opcode, data, size);
+
+	len = read(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
+	if (len == 0)
+		return false;
+
+	if (len < 0 || len != BTSNOOP_PKT_SIZE) {
+		btsnoop->aborted = true;
+		return false;
+	}
+
+	toread = be32toh(pkt.size);
+	if (toread > BTSNOOP_MAX_PACKET_SIZE) {
+		btsnoop->aborted = true;
+		return false;
+	}
+
+	flags = be32toh(pkt.flags);
+
+	ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
+	tv->tv_sec = (ts / 1000000ll) + 946684800ll;
+	tv->tv_usec = ts % 1000000ll;
+
+	switch (btsnoop->format) {
+	case BTSNOOP_FORMAT_HCI:
+		*index = 0;
+		*opcode = get_opcode_from_flags(0xff, flags);
+		break;
+
+	case BTSNOOP_FORMAT_UART:
+		len = read(btsnoop->fd, &pkt_type, 1);
+		if (len < 0) {
+			btsnoop->aborted = true;
+			return false;
+		}
+		toread--;
+
+		*index = 0;
+		*opcode = get_opcode_from_flags(pkt_type, flags);
+		break;
+
+	case BTSNOOP_FORMAT_MONITOR:
+		*index = flags >> 16;
+		*opcode = flags & 0xffff;
+		break;
+
+	default:
+		btsnoop->aborted = true;
+		return false;
+	}
+
+	len = read(btsnoop->fd, data, toread);
+	if (len < 0) {
+		btsnoop->aborted = true;
+		return false;
+	}
+
+	*size = toread;
+
+	return true;
+}
+
+bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv,
+			uint16_t *frequency, void *data, uint16_t *size)
+{
+	return false;
+}
diff --git a/bluez/repo/src/shared/btsnoop.h b/bluez/repo/src/shared/btsnoop.h
new file mode 100644
index 0000000..d94dbae
--- /dev/null
+++ b/bluez/repo/src/shared/btsnoop.h
@@ -0,0 +1,114 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <stdint.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#define BTSNOOP_FORMAT_INVALID		0
+#define BTSNOOP_FORMAT_HCI		1001
+#define BTSNOOP_FORMAT_UART		1002
+#define BTSNOOP_FORMAT_BCSP		1003
+#define BTSNOOP_FORMAT_3WIRE		1004
+#define BTSNOOP_FORMAT_MONITOR		2001
+#define BTSNOOP_FORMAT_SIMULATOR	2002
+
+#define BTSNOOP_FLAG_PKLG_SUPPORT	(1 << 0)
+
+#define BTSNOOP_OPCODE_NEW_INDEX	0
+#define BTSNOOP_OPCODE_DEL_INDEX	1
+#define BTSNOOP_OPCODE_COMMAND_PKT	2
+#define BTSNOOP_OPCODE_EVENT_PKT	3
+#define BTSNOOP_OPCODE_ACL_TX_PKT	4
+#define BTSNOOP_OPCODE_ACL_RX_PKT	5
+#define BTSNOOP_OPCODE_SCO_TX_PKT	6
+#define BTSNOOP_OPCODE_SCO_RX_PKT	7
+#define BTSNOOP_OPCODE_OPEN_INDEX	8
+#define BTSNOOP_OPCODE_CLOSE_INDEX	9
+#define BTSNOOP_OPCODE_INDEX_INFO	10
+#define BTSNOOP_OPCODE_VENDOR_DIAG	11
+#define BTSNOOP_OPCODE_SYSTEM_NOTE	12
+#define BTSNOOP_OPCODE_USER_LOGGING	13
+
+#define BTSNOOP_MAX_PACKET_SIZE		(1486 + 4)
+
+#define BTSNOOP_TYPE_PRIMARY	0
+#define BTSNOOP_TYPE_AMP	1
+
+#define BTSNOOP_BUS_VIRTUAL	0
+#define BTSNOOP_BUS_USB		1
+#define BTSNOOP_BUS_PCCARD	2
+#define BTSNOOP_BUS_UART	3
+#define BTSNOOP_BUS_RS232	4
+#define BTSNOOP_BUS_PCI		5
+#define BTSNOOP_BUS_SDIO	6
+
+struct btsnoop_opcode_new_index {
+	uint8_t  type;
+	uint8_t  bus;
+	uint8_t  bdaddr[6];
+	char     name[8];
+} __attribute__((packed));
+
+struct btsnoop_opcode_index_info {
+	uint8_t  bdaddr[6];
+	uint16_t manufacturer;
+} __attribute__((packed));
+
+#define BTSNOOP_PRIORITY_EMERG		0
+#define BTSNOOP_PRIORITY_ALERT		1
+#define BTSNOOP_PRIORITY_CRIT		2
+#define BTSNOOP_PRIORITY_ERR		3
+#define BTSNOOP_PRIORITY_WARNING	4
+#define BTSNOOP_PRIORITY_NOTICE		5
+#define BTSNOOP_PRIORITY_INFO		6
+#define BTSNOOP_PRIORITY_DEBUG		7
+
+struct btsnoop_opcode_user_logging {
+	uint8_t  priority;
+	uint8_t  ident_len;
+} __attribute__((packed));
+
+struct btsnoop;
+
+struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
+struct btsnoop *btsnoop_create(const char *path, uint32_t format);
+
+struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
+void btsnoop_unref(struct btsnoop *btsnoop);
+
+uint32_t btsnoop_get_format(struct btsnoop *btsnoop);
+
+bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
+			uint32_t flags, const void *data, uint16_t size);
+bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
+					uint16_t index, uint16_t opcode,
+					const void *data, uint16_t size);
+bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
+			uint16_t frequency, const void *data, uint16_t size);
+
+bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
+					uint16_t *index, uint16_t *opcode,
+					void *data, uint16_t *size);
+bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv,
+			uint16_t *frequency, void *data, uint16_t *size);
diff --git a/bluez/repo/src/shared/crypto.c b/bluez/repo/src/shared/crypto.c
new file mode 100644
index 0000000..aa66dac
--- /dev/null
+++ b/bluez/repo/src/shared/crypto.c
@@ -0,0 +1,681 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 "src/shared/util.h"
+#include "src/shared/crypto.h"
+
+#ifndef HAVE_LINUX_IF_ALG_H
+#ifndef HAVE_LINUX_TYPES_H
+typedef uint8_t __u8;
+typedef uint16_t __u16;
+typedef uint32_t __u32;
+#else
+#include <linux/types.h>
+#endif
+
+struct sockaddr_alg {
+	__u16   salg_family;
+	__u8    salg_type[14];
+	__u32   salg_feat;
+	__u32   salg_mask;
+	__u8    salg_name[64];
+};
+
+struct af_alg_iv {
+	__u32   ivlen;
+	__u8    iv[0];
+};
+
+#define ALG_SET_KEY                     1
+#define ALG_SET_IV                      2
+#define ALG_SET_OP                      3
+
+#define ALG_OP_DECRYPT                  0
+#define ALG_OP_ENCRYPT                  1
+
+#define PF_ALG		38	/* Algorithm sockets.  */
+#define AF_ALG		PF_ALG
+#else
+#include <linux/if_alg.h>
+#endif
+
+#ifndef SOL_ALG
+#define SOL_ALG		279
+#endif
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX	80
+
+struct bt_crypto {
+	int ref_count;
+	int ecb_aes;
+	int urandom;
+	int cmac_aes;
+};
+
+static int urandom_setup(void)
+{
+	int fd;
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	return fd;
+}
+
+static int ecb_aes_setup(void)
+{
+	struct sockaddr_alg salg;
+	int fd;
+
+	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;
+	}
+
+	return fd;
+}
+
+static int cmac_aes_setup(void)
+{
+	struct sockaddr_alg salg;
+	int fd;
+
+	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;
+	}
+
+	return fd;
+}
+
+struct bt_crypto *bt_crypto_new(void)
+{
+	struct bt_crypto *crypto;
+
+	crypto = new0(struct bt_crypto, 1);
+
+	crypto->ecb_aes = ecb_aes_setup();
+	if (crypto->ecb_aes < 0) {
+		free(crypto);
+		return NULL;
+	}
+
+	crypto->urandom = urandom_setup();
+	if (crypto->urandom < 0) {
+		close(crypto->ecb_aes);
+		free(crypto);
+		return NULL;
+	}
+
+	crypto->cmac_aes = cmac_aes_setup();
+	if (crypto->cmac_aes < 0) {
+		close(crypto->urandom);
+		close(crypto->ecb_aes);
+		free(crypto);
+		return NULL;
+	}
+
+	return bt_crypto_ref(crypto);
+}
+
+struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto)
+{
+	if (!crypto)
+		return NULL;
+
+	__sync_fetch_and_add(&crypto->ref_count, 1);
+
+	return crypto;
+}
+
+void bt_crypto_unref(struct bt_crypto *crypto)
+{
+	if (!crypto)
+		return;
+
+	if (__sync_sub_and_fetch(&crypto->ref_count, 1))
+		return;
+
+	close(crypto->urandom);
+	close(crypto->ecb_aes);
+	close(crypto->cmac_aes);
+
+	free(crypto);
+}
+
+bool bt_crypto_random_bytes(struct bt_crypto *crypto,
+					uint8_t *buf, uint8_t num_bytes)
+{
+	ssize_t len;
+
+	if (!crypto)
+		return false;
+
+	len = read(crypto->urandom, buf, num_bytes);
+	if (len < num_bytes)
+		return false;
+
+	return true;
+}
+
+static int alg_new(int fd, const void *keyval, socklen_t keylen)
+{
+	if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0)
+		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 inline void swap_buf(const uint8_t *src, uint8_t *dst, uint16_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		dst[len - 1 - i] = src[i];
+}
+
+bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
+				const uint8_t *m, uint16_t m_len,
+				uint32_t sign_cnt, uint8_t signature[12])
+{
+	int fd;
+	int len;
+	uint8_t tmp[16], out[16];
+	uint16_t msg_len = m_len + sizeof(uint32_t);
+	uint8_t msg[msg_len];
+	uint8_t msg_s[msg_len];
+
+	if (!crypto)
+		return false;
+
+	memset(msg, 0, msg_len);
+	memcpy(msg, m, m_len);
+
+	/* Add sign_counter to the message */
+	put_le32(sign_cnt, msg + m_len);
+
+	/* The most significant octet of key corresponds to key[0] */
+	swap_buf(key, tmp, 16);
+
+	fd = alg_new(crypto->cmac_aes, tmp, 16);
+	if (fd < 0)
+		return false;
+
+	/* Swap msg before signing */
+	swap_buf(msg, msg_s, msg_len);
+
+	len = send(fd, msg_s, msg_len, 0);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	len = read(fd, out, 16);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	close(fd);
+
+	/*
+	 * As to BT spec. 4.1 Vol[3], Part C, chapter 10.4.1 sign counter should
+	 * be placed in the signature
+	 */
+	put_be32(sign_cnt, out + 8);
+
+	/*
+	 * The most significant octet of hash corresponds to out[0]  - swap it.
+	 * Then truncate in most significant bit first order to a length of
+	 * 12 octets
+	 */
+	swap_buf(out, tmp, 16);
+	memcpy(signature, tmp + 4, 12);
+
+	return true;
+}
+/*
+ * Security function e
+ *
+ * Security function e generates 128-bit encryptedData from a 128-bit key
+ * and 128-bit plaintextData using the AES-128-bit block cypher:
+ *
+ *   encryptedData = e(key, plaintextData)
+ *
+ * The most significant octet of key corresponds to key[0], the most
+ * significant octet of plaintextData corresponds to in[0] and the
+ * most significant octet of encryptedData corresponds to out[0].
+ *
+ */
+bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16],
+			const uint8_t plaintext[16], uint8_t encrypted[16])
+{
+	uint8_t tmp[16], in[16], out[16];
+	int fd;
+
+	if (!crypto)
+		return false;
+
+	/* The most significant octet of key corresponds to key[0] */
+	swap_buf(key, tmp, 16);
+
+	fd = alg_new(crypto->ecb_aes, tmp, 16);
+	if (fd < 0)
+		return false;
+
+
+	/* Most significant octet of plaintextData corresponds to in[0] */
+	swap_buf(plaintext, in, 16);
+
+	if (!alg_encrypt(fd, in, 16, out, 16)) {
+		close(fd);
+		return false;
+	}
+
+	/* Most significant octet of encryptedData corresponds to out[0] */
+	swap_buf(out, encrypted, 16);
+
+	close(fd);
+
+	return true;
+}
+
+/*
+ * Random Address Hash function ah
+ *
+ * The random address hash function ah is used to generate a hash value
+ * that is used in resolvable private addresses.
+ *
+ * The following are inputs to the random address hash function ah:
+ *
+ *   k is 128 bits
+ *   r is 24 bits
+ *   padding is 104 bits
+ *
+ * r is concatenated with padding to generate r' which is used as the
+ * 128-bit input parameter plaintextData to security function e:
+ *
+ *   r' = padding || r
+ *
+ * The least significant octet of r becomes the least significant octet
+ * of r’ and the most significant octet of padding becomes the most
+ * significant octet of r'.
+ *
+ * For example, if the 24-bit value r is 0x423456 then r' is
+ * 0x00000000000000000000000000423456.
+ *
+ * The output of the random address function ah is:
+ *
+ *   ah(k, r) = e(k, r') mod 2^24
+ *
+ * The output of the security function e is then truncated to 24 bits by
+ * taking the least significant 24 bits of the output of e as the result
+ * of ah.
+ */
+bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16],
+					const uint8_t r[3], uint8_t hash[3])
+{
+	uint8_t rp[16];
+	uint8_t encrypted[16];
+
+	if (!crypto)
+		return false;
+
+	/* r' = padding || r */
+	memcpy(rp, r, 3);
+	memset(rp + 3, 0, 13);
+
+	/* e(k, r') */
+	if (!bt_crypto_e(crypto, k, rp, encrypted))
+		return false;
+
+	/* ah(k, r) = e(k, r') mod 2^24 */
+	memcpy(hash, encrypted, 3);
+
+	return true;
+}
+
+typedef struct {
+	uint64_t a, b;
+} u128;
+
+static inline void u128_xor(const uint8_t p[16], const uint8_t q[16],
+								uint8_t r[16])
+{
+	u128 pp, qq, rr;
+
+	memcpy(&pp, p, 16);
+	memcpy(&qq, q, 16);
+
+	rr.a = pp.a ^ qq.a;
+	rr.b = pp.b ^ qq.b;
+
+	memcpy(r, &rr, 16);
+}
+
+/*
+ * Confirm value generation function c1
+ *
+ * During the pairing process confirm values are exchanged. This confirm
+ * value generation function c1 is used to generate the confirm values.
+ *
+ * The following are inputs to the confirm value generation function c1:
+ *
+ *   k is 128 bits
+ *   r is 128 bits
+ *   pres is 56 bits
+ *   preq is 56 bits
+ *   iat is 1 bit
+ *   ia is 48 bits
+ *   rat is 1 bit
+ *   ra is 48 bits
+ *   padding is 32 bits of 0
+ *
+ * iat is concatenated with 7-bits of 0 to create iat' which is 8 bits
+ * in length. iat is the least significant bit of iat'
+ *
+ * rat is concatenated with 7-bits of 0 to create rat' which is 8 bits
+ * in length. rat is the least significant bit of rat'
+ *
+ * pres, preq, rat' and iat' are concatenated to generate p1 which is
+ * XORed with r and used as 128-bit input parameter plaintextData to
+ * security function e:
+ *
+ *   p1 = pres || preq || rat' || iat'
+ *
+ * The octet of iat' becomes the least significant octet of p1 and the
+ * most significant octet of pres becomes the most significant octet of
+ * p1.
+ *
+ * ra is concatenated with ia and padding to generate p2 which is XORed
+ * with the result of the security function e using p1 as the input
+ * paremter plaintextData and is then used as the 128-bit input
+ * parameter plaintextData to security function e:
+ *
+ *   p2 = padding || ia || ra
+ *
+ * The least significant octet of ra becomes the least significant octet
+ * of p2 and the most significant octet of padding becomes the most
+ * significant octet of p2.
+ *
+ * The output of the confirm value generation function c1 is:
+ *
+ *   c1(k, r, preq, pres, iat, rat, ia, ra) = e(k, e(k, r XOR p1) XOR p2)
+ *
+ * The 128-bit output of the security function e is used as the result
+ * of confirm value generation function c1.
+ */
+bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16],
+			const uint8_t r[16], const uint8_t pres[7],
+			const uint8_t preq[7], uint8_t iat,
+			const uint8_t ia[6], uint8_t rat,
+			const uint8_t ra[6], uint8_t res[16])
+{
+	uint8_t p1[16], p2[16];
+
+	/* p1 = pres || preq || _rat || _iat */
+	p1[0] = iat;
+	p1[1] = rat;
+	memcpy(p1 + 2, preq, 7);
+	memcpy(p1 + 9, pres, 7);
+
+	/* p2 = padding || ia || ra */
+	memcpy(p2, ra, 6);
+	memcpy(p2 + 6, ia, 6);
+	memset(p2 + 12, 0, 4);
+
+	/* res = r XOR p1 */
+	u128_xor(r, p1, res);
+
+	/* res = e(k, res) */
+	if (!bt_crypto_e(crypto, k, res, res))
+		return false;
+
+	/* res = res XOR p2 */
+	u128_xor(res, p2, res);
+
+	/* res = e(k, res) */
+	return bt_crypto_e(crypto, k, res, res);
+}
+
+/*
+ * Key generation function s1
+ *
+ * The key generation function s1 is used to generate the STK during the
+ * pairing process.
+ *
+ * The following are inputs to the key generation function s1:
+ *
+ *   k is 128 bits
+ *   r1 is 128 bits
+ *   r2 is 128 bits
+ *
+ * The most significant 64-bits of r1 are discarded to generate r1' and
+ * the most significant 64-bits of r2 are discarded to generate r2'.
+ *
+ * r1' is concatenated with r2' to generate r' which is used as the
+ * 128-bit input parameter plaintextData to security function e:
+ *
+ *   r' = r1' || r2'
+ *
+ * The least significant octet of r2' becomes the least significant
+ * octet of r' and the most significant octet of r1' becomes the most
+ * significant octet of r'.
+ *
+ * The output of the key generation function s1 is:
+ *
+ *   s1(k, r1, r2) = e(k, r')
+ *
+ * The 128-bit output of the security function e is used as the result
+ * of key generation function s1.
+ */
+bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
+			const uint8_t r1[16], const uint8_t r2[16],
+			uint8_t res[16])
+{
+	memcpy(res, r2, 8);
+	memcpy(res + 8, r1, 8);
+
+	return bt_crypto_e(crypto, k, res, res);
+}
+
+static bool aes_cmac(struct bt_crypto *crypto, uint8_t key[16], uint8_t *msg,
+					size_t msg_len, uint8_t res[16])
+{
+	uint8_t key_msb[16], out[16], msg_msb[CMAC_MSG_MAX];
+	ssize_t len;
+	int fd;
+
+	if (msg_len > CMAC_MSG_MAX)
+		return false;
+
+	swap_buf(key, key_msb, 16);
+	fd = alg_new(crypto->cmac_aes, key_msb, 16);
+	if (fd < 0)
+		return false;
+
+	swap_buf(msg, msg_msb, msg_len);
+	len = send(fd, msg_msb, msg_len, 0);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	len = read(fd, out, 16);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	swap_buf(out, res, 16);
+
+	close(fd);
+
+	return true;
+}
+
+bool bt_crypto_f4(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
+				uint8_t x[16], uint8_t z, uint8_t res[16])
+{
+	uint8_t m[65];
+
+	if (!crypto)
+		return false;
+
+	m[0] = z;
+	memcpy(&m[1], v, 32);
+	memcpy(&m[33], u, 32);
+
+	return aes_cmac(crypto, x, m, sizeof(m), res);
+}
+
+bool bt_crypto_f5(struct bt_crypto *crypto, uint8_t w[32], uint8_t n1[16],
+				uint8_t n2[16], uint8_t a1[7], uint8_t a2[7],
+				uint8_t mackey[16], uint8_t ltk[16])
+{
+	uint8_t btle[4] = { 0x65, 0x6c, 0x74, 0x62 };
+	uint8_t salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60,
+			     0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c };
+	uint8_t length[2] = { 0x00, 0x01 };
+	uint8_t m[53], t[16];
+
+	if (!aes_cmac(crypto, salt, w, 32, t))
+		return false;
+
+	memcpy(&m[0], length, 2);
+	memcpy(&m[2], a2, 7);
+	memcpy(&m[9], a1, 7);
+	memcpy(&m[16], n2, 16);
+	memcpy(&m[32], n1, 16);
+	memcpy(&m[48], btle, 4);
+
+	m[52] = 0; /* Counter */
+	if (!aes_cmac(crypto, t, m, sizeof(m), mackey))
+		return false;
+
+	m[52] = 1; /* Counter */
+	return aes_cmac(crypto, t, m, sizeof(m), ltk);
+}
+
+bool bt_crypto_f6(struct bt_crypto *crypto, uint8_t w[16], uint8_t n1[16],
+			uint8_t n2[16], uint8_t r[16], uint8_t io_cap[3],
+			uint8_t a1[7], uint8_t a2[7], uint8_t res[16])
+{
+	uint8_t m[65];
+
+	memcpy(&m[0], a2, 7);
+	memcpy(&m[7], a1, 7);
+	memcpy(&m[14], io_cap, 3);
+	memcpy(&m[17], r, 16);
+	memcpy(&m[33], n2, 16);
+	memcpy(&m[49], n1, 16);
+
+	return aes_cmac(crypto, w, m, sizeof(m), res);
+}
+
+bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
+				uint8_t x[16], uint8_t y[16], uint32_t *val)
+{
+	uint8_t m[80], tmp[16];
+
+	memcpy(&m[0], y, 16);
+	memcpy(&m[16], v, 32);
+	memcpy(&m[48], u, 32);
+
+	if (!aes_cmac(crypto, x, m, sizeof(m), tmp))
+		return false;
+
+	*val = get_le32(tmp);
+	*val %= 1000000;
+
+	return true;
+}
diff --git a/bluez/repo/src/shared/crypto.h b/bluez/repo/src/shared/crypto.h
new file mode 100644
index 0000000..9ba5803
--- /dev/null
+++ b/bluez/repo/src/shared/crypto.h
@@ -0,0 +1,61 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+struct bt_crypto;
+
+struct bt_crypto *bt_crypto_new(void);
+
+struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto);
+void bt_crypto_unref(struct bt_crypto *crypto);
+
+bool bt_crypto_random_bytes(struct bt_crypto *crypto,
+					uint8_t *buf, uint8_t num_bytes);
+
+bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16],
+			const uint8_t plaintext[16], uint8_t encrypted[16]);
+bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16],
+					const uint8_t r[3], uint8_t hash[3]);
+bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16],
+			const uint8_t r[16], const uint8_t pres[7],
+			const uint8_t preq[7], uint8_t iat,
+			const uint8_t ia[6], uint8_t rat,
+			const uint8_t ra[6], uint8_t res[16]);
+bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
+			const uint8_t r1[16], const uint8_t r2[16],
+			uint8_t res[16]);
+bool bt_crypto_f4(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
+				uint8_t x[16], uint8_t z, uint8_t res[16]);
+bool bt_crypto_f5(struct bt_crypto *crypto, uint8_t w[32], uint8_t n1[16],
+				uint8_t n2[16], uint8_t a1[7], uint8_t a2[7],
+				uint8_t mackey[16], uint8_t ltk[16]);
+bool bt_crypto_f6(struct bt_crypto *crypto, uint8_t w[16], uint8_t n1[16],
+			uint8_t n2[16], uint8_t r[16], uint8_t io_cap[3],
+			uint8_t a1[7], uint8_t a2[7], uint8_t res[16]);
+bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32],
+				uint8_t x[16], uint8_t y[16], uint32_t *val);
+bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
+				const uint8_t *m, uint16_t m_len,
+				uint32_t sign_cnt, uint8_t signature[12]);
diff --git a/bluez/repo/src/shared/ecc.c b/bluez/repo/src/shared/ecc.c
new file mode 100644
index 0000000..41be02b
--- /dev/null
+++ b/bluez/repo/src/shared/ecc.c
@@ -0,0 +1,861 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+
+#include "ecc.h"
+
+/* 256-bit curve */
+#define ECC_BYTES 32
+
+/* Number of uint64_t's needed */
+#define NUM_ECC_DIGITS (ECC_BYTES / 8)
+
+struct ecc_point {
+	uint64_t x[NUM_ECC_DIGITS];
+	uint64_t y[NUM_ECC_DIGITS];
+};
+
+#define MAX_TRIES 16
+
+typedef struct {
+	uint64_t m_low;
+	uint64_t m_high;
+} uint128_t;
+
+#define CURVE_P_32 {	0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \
+			0x0000000000000000ull, 0xFFFFFFFF00000001ull }
+
+#define CURVE_G_32 { \
+		{	0xF4A13945D898C296ull, 0x77037D812DEB33A0ull,	\
+			0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \
+		{	0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull,	\
+			0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull }	\
+}
+
+#define CURVE_N_32 {	0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull,	\
+			0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull }
+
+static uint64_t curve_p[NUM_ECC_DIGITS] = CURVE_P_32;
+static struct ecc_point curve_g = CURVE_G_32;
+static uint64_t curve_n[NUM_ECC_DIGITS] = CURVE_N_32;
+
+static bool get_random_number(uint64_t *vli)
+{
+	char *ptr = (char *) vli;
+	size_t left = ECC_BYTES;
+	int fd;
+
+	fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+	if (fd < 0) {
+		fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
+		if (fd < 0)
+			return false;
+	}
+
+	while (left > 0) {
+		ssize_t ret;
+
+		ret = read(fd, ptr, left);
+		if (ret <= 0) {
+			close(fd);
+			return false;
+		}
+
+		left -= ret;
+		ptr += ret;
+	}
+
+	close(fd);
+
+	return true;
+}
+
+static void vli_clear(uint64_t *vli)
+{
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++)
+		vli[i] = 0;
+}
+
+/* Returns true if vli == 0, false otherwise. */
+static bool vli_is_zero(const uint64_t *vli)
+{
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++) {
+		if (vli[i])
+			return false;
+	}
+
+	return true;
+}
+
+/* Returns nonzero if bit bit of vli is set. */
+static uint64_t vli_test_bit(const uint64_t *vli, unsigned int bit)
+{
+	return (vli[bit / 64] & ((uint64_t) 1 << (bit % 64)));
+}
+
+/* Counts the number of 64-bit "digits" in vli. */
+static unsigned int vli_num_digits(const uint64_t *vli)
+{
+	int i;
+
+	/* Search from the end until we find a non-zero digit.
+	 * We do it in reverse because we expect that most digits will
+	 * be nonzero.
+	 */
+	for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--);
+
+	return (i + 1);
+}
+
+/* Counts the number of bits required for vli. */
+static unsigned int vli_num_bits(const uint64_t *vli)
+{
+	unsigned int i, num_digits;
+	uint64_t digit;
+
+	num_digits = vli_num_digits(vli);
+	if (num_digits == 0)
+		return 0;
+
+	digit = vli[num_digits - 1];
+	for (i = 0; digit; i++)
+		digit >>= 1;
+
+	return ((num_digits - 1) * 64 + i);
+}
+
+/* Sets dest = src. */
+static void vli_set(uint64_t *dest, const uint64_t *src)
+{
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++)
+		dest[i] = src[i];
+}
+
+/* Returns sign of left - right. */
+static int vli_cmp(const uint64_t *left, const uint64_t *right)
+{
+    int i;
+
+    for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) {
+	    if (left[i] > right[i])
+		    return 1;
+	    else if (left[i] < right[i])
+		    return -1;
+    }
+
+    return 0;
+}
+
+/* Computes result = in << c, returning carry. Can modify in place
+ * (if result == in). 0 < shift < 64.
+ */
+static uint64_t vli_lshift(uint64_t *result, const uint64_t *in,
+							unsigned int shift)
+{
+	uint64_t carry = 0;
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++) {
+		uint64_t temp = in[i];
+
+		result[i] = (temp << shift) | carry;
+		carry = temp >> (64 - shift);
+	}
+
+	return carry;
+}
+
+/* Computes vli = vli >> 1. */
+static void vli_rshift1(uint64_t *vli)
+{
+	uint64_t *end = vli;
+	uint64_t carry = 0;
+
+	vli += NUM_ECC_DIGITS;
+
+	while (vli-- > end) {
+		uint64_t temp = *vli;
+		*vli = (temp >> 1) | carry;
+		carry = temp << 63;
+	}
+}
+
+/* Computes result = left + right, returning carry. Can modify in place. */
+static uint64_t vli_add(uint64_t *result, const uint64_t *left,
+							const uint64_t *right)
+{
+	uint64_t carry = 0;
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++) {
+		uint64_t sum;
+
+		sum = left[i] + right[i] + carry;
+		if (sum != left[i])
+			carry = (sum < left[i]);
+
+		result[i] = sum;
+	}
+
+	return carry;
+}
+
+/* Computes result = left - right, returning borrow. Can modify in place. */
+static uint64_t vli_sub(uint64_t *result, const uint64_t *left,
+							const uint64_t *right)
+{
+	uint64_t borrow = 0;
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++) {
+		uint64_t diff;
+
+		diff = left[i] - right[i] - borrow;
+		if (diff != left[i])
+			borrow = (diff > left[i]);
+
+		result[i] = diff;
+	}
+
+	return borrow;
+}
+
+static uint128_t mul_64_64(uint64_t left, uint64_t right)
+{
+	uint64_t a0 = left & 0xffffffffull;
+	uint64_t a1 = left >> 32;
+	uint64_t b0 = right & 0xffffffffull;
+	uint64_t b1 = right >> 32;
+	uint64_t m0 = a0 * b0;
+	uint64_t m1 = a0 * b1;
+	uint64_t m2 = a1 * b0;
+	uint64_t m3 = a1 * b1;
+	uint128_t result;
+
+	m2 += (m0 >> 32);
+	m2 += m1;
+
+	/* Overflow */
+	if (m2 < m1)
+		m3 += 0x100000000ull;
+
+	result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
+	result.m_high = m3 + (m2 >> 32);
+
+	return result;
+}
+
+static uint128_t add_128_128(uint128_t a, uint128_t b)
+{
+	uint128_t result;
+
+	result.m_low = a.m_low + b.m_low;
+	result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low);
+
+	return result;
+}
+
+static void vli_mult(uint64_t *result, const uint64_t *left,
+							const uint64_t *right)
+{
+	uint128_t r01 = { 0, 0 };
+	uint64_t r2 = 0;
+	unsigned int i, k;
+
+	/* Compute each digit of result in sequence, maintaining the
+	 * carries.
+	 */
+	for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+		unsigned int min;
+
+		if (k < NUM_ECC_DIGITS)
+			min = 0;
+		else
+			min = (k + 1) - NUM_ECC_DIGITS;
+
+		for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) {
+			uint128_t product;
+
+			product = mul_64_64(left[i], right[k - i]);
+
+			r01 = add_128_128(r01, product);
+			r2 += (r01.m_high < product.m_high);
+		}
+
+		result[k] = r01.m_low;
+		r01.m_low = r01.m_high;
+		r01.m_high = r2;
+		r2 = 0;
+	}
+
+	result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+static void vli_square(uint64_t *result, const uint64_t *left)
+{
+	uint128_t r01 = { 0, 0 };
+	uint64_t r2 = 0;
+	int i, k;
+
+	for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+		unsigned int min;
+
+		if (k < NUM_ECC_DIGITS)
+			min = 0;
+		else
+			min = (k + 1) - NUM_ECC_DIGITS;
+
+		for (i = min; i <= k && i <= k - i; i++) {
+			uint128_t product;
+
+			product = mul_64_64(left[i], left[k - i]);
+
+			if (i < k - i) {
+				r2 += product.m_high >> 63;
+				product.m_high = (product.m_high << 1) |
+							(product.m_low >> 63);
+				product.m_low <<= 1;
+			}
+
+			r01 = add_128_128(r01, product);
+			r2 += (r01.m_high < product.m_high);
+		}
+
+		result[k] = r01.m_low;
+		r01.m_low = r01.m_high;
+		r01.m_high = r2;
+		r2 = 0;
+	}
+
+	result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+/* Computes result = (left + right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_add(uint64_t *result, const uint64_t *left,
+				const uint64_t *right, const uint64_t *mod)
+{
+	uint64_t carry;
+
+	carry = vli_add(result, left, right);
+
+	/* result > mod (result = mod + remainder), so subtract mod to
+	 * get remainder.
+	 */
+	if (carry || vli_cmp(result, mod) >= 0)
+		vli_sub(result, result, mod);
+}
+
+/* Computes result = (left - right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_sub(uint64_t *result, const uint64_t *left,
+				const uint64_t *right, const uint64_t *mod)
+{
+	uint64_t borrow = vli_sub(result, left, right);
+
+	/* In this case, p_result == -diff == (max int) - diff.
+	 * Since -x % d == d - x, we can get the correct result from
+	 * result + mod (with overflow).
+	 */
+	if (borrow)
+		vli_add(result, result, mod);
+}
+
+/* Computes result = product % curve_p
+   from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void vli_mmod_fast(uint64_t *result, const uint64_t *product)
+{
+	uint64_t tmp[NUM_ECC_DIGITS];
+	int carry;
+
+	/* t */
+	vli_set(result, product);
+
+	/* s1 */
+	tmp[0] = 0;
+	tmp[1] = product[5] & 0xffffffff00000000ull;
+	tmp[2] = product[6];
+	tmp[3] = product[7];
+	carry = vli_lshift(tmp, tmp, 1);
+	carry += vli_add(result, result, tmp);
+
+	/* s2 */
+	tmp[1] = product[6] << 32;
+	tmp[2] = (product[6] >> 32) | (product[7] << 32);
+	tmp[3] = product[7] >> 32;
+	carry += vli_lshift(tmp, tmp, 1);
+	carry += vli_add(result, result, tmp);
+
+	/* s3 */
+	tmp[0] = product[4];
+	tmp[1] = product[5] & 0xffffffff;
+	tmp[2] = 0;
+	tmp[3] = product[7];
+	carry += vli_add(result, result, tmp);
+
+	/* s4 */
+	tmp[0] = (product[4] >> 32) | (product[5] << 32);
+	tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull);
+	tmp[2] = product[7];
+	tmp[3] = (product[6] >> 32) | (product[4] << 32);
+	carry += vli_add(result, result, tmp);
+
+	/* d1 */
+	tmp[0] = (product[5] >> 32) | (product[6] << 32);
+	tmp[1] = (product[6] >> 32);
+	tmp[2] = 0;
+	tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
+	carry -= vli_sub(result, result, tmp);
+
+	/* d2 */
+	tmp[0] = product[6];
+	tmp[1] = product[7];
+	tmp[2] = 0;
+	tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull);
+	carry -= vli_sub(result, result, tmp);
+
+	/* d3 */
+	tmp[0] = (product[6] >> 32) | (product[7] << 32);
+	tmp[1] = (product[7] >> 32) | (product[4] << 32);
+	tmp[2] = (product[4] >> 32) | (product[5] << 32);
+	tmp[3] = (product[6] << 32);
+	carry -= vli_sub(result, result, tmp);
+
+	/* d4 */
+	tmp[0] = product[7];
+	tmp[1] = product[4] & 0xffffffff00000000ull;
+	tmp[2] = product[5];
+	tmp[3] = product[6] & 0xffffffff00000000ull;
+	carry -= vli_sub(result, result, tmp);
+
+	if (carry < 0) {
+		do {
+			carry += vli_add(result, result, curve_p);
+		} while (carry < 0);
+	} else {
+		while (carry || vli_cmp(curve_p, result) != 1)
+			carry -= vli_sub(result, result, curve_p);
+	}
+}
+
+/* Computes result = (left * right) % curve_p. */
+static void vli_mod_mult_fast(uint64_t *result, const uint64_t *left,
+							const uint64_t *right)
+{
+	uint64_t product[2 * NUM_ECC_DIGITS];
+
+	vli_mult(product, left, right);
+	vli_mmod_fast(result, product);
+}
+
+/* Computes result = left^2 % curve_p. */
+static void vli_mod_square_fast(uint64_t *result, const uint64_t *left)
+{
+	uint64_t product[2 * NUM_ECC_DIGITS];
+
+	vli_square(product, left);
+	vli_mmod_fast(result, product);
+}
+
+#define EVEN(vli) (!(vli[0] & 1))
+/* Computes result = (1 / p_input) % mod. All VLIs are the same size.
+ * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide"
+ * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf
+ */
+static void vli_mod_inv(uint64_t *result, const uint64_t *input,
+							const uint64_t *mod)
+{
+	uint64_t a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS];
+	uint64_t u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS];
+	uint64_t carry;
+	int cmp_result;
+
+	if (vli_is_zero(input)) {
+		vli_clear(result);
+		return;
+	}
+
+	vli_set(a, input);
+	vli_set(b, mod);
+	vli_clear(u);
+	u[0] = 1;
+	vli_clear(v);
+
+	while ((cmp_result = vli_cmp(a, b)) != 0) {
+		carry = 0;
+
+		if (EVEN(a)) {
+			vli_rshift1(a);
+
+			if (!EVEN(u))
+				carry = vli_add(u, u, mod);
+
+			vli_rshift1(u);
+			if (carry)
+				u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+		} else if (EVEN(b)) {
+			vli_rshift1(b);
+
+			if (!EVEN(v))
+				carry = vli_add(v, v, mod);
+
+			vli_rshift1(v);
+			if (carry)
+				v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+		} else if (cmp_result > 0) {
+			vli_sub(a, a, b);
+			vli_rshift1(a);
+
+			if (vli_cmp(u, v) < 0)
+				vli_add(u, u, mod);
+
+			vli_sub(u, u, v);
+			if (!EVEN(u))
+				carry = vli_add(u, u, mod);
+
+			vli_rshift1(u);
+			if (carry)
+				u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+		} else {
+			vli_sub(b, b, a);
+			vli_rshift1(b);
+
+			if (vli_cmp(v, u) < 0)
+				vli_add(v, v, mod);
+
+			vli_sub(v, v, u);
+			if (!EVEN(v))
+				carry = vli_add(v, v, mod);
+
+			vli_rshift1(v);
+			if (carry)
+				v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+		}
+	}
+
+	vli_set(result, u);
+}
+
+/* ------ Point operations ------ */
+
+/* Returns true if p_point is the point at infinity, false otherwise. */
+static bool ecc_point_is_zero(const struct ecc_point *point)
+{
+	return (vli_is_zero(point->x) && vli_is_zero(point->y));
+}
+
+/* Point multiplication algorithm using Montgomery's ladder with co-Z
+ * coordinates. From http://eprint.iacr.org/2011/338.pdf
+ */
+
+/* Double in place */
+static void ecc_point_double_jacobian(uint64_t *x1, uint64_t *y1, uint64_t *z1)
+{
+	/* t1 = x, t2 = y, t3 = z */
+	uint64_t t4[NUM_ECC_DIGITS];
+	uint64_t t5[NUM_ECC_DIGITS];
+
+	if (vli_is_zero(z1))
+		return;
+
+	vli_mod_square_fast(t4, y1);   /* t4 = y1^2 */
+	vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */
+	vli_mod_square_fast(t4, t4);   /* t4 = y1^4 */
+	vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */
+	vli_mod_square_fast(z1, z1);   /* t3 = z1^2 */
+
+	vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */
+	vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */
+	vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */
+	vli_mod_mult_fast(x1, x1, z1);    /* t1 = x1^2 - z1^4 */
+
+	vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
+	vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
+	if (vli_test_bit(x1, 0)) {
+		uint64_t carry = vli_add(x1, x1, curve_p);
+		vli_rshift1(x1);
+		x1[NUM_ECC_DIGITS - 1] |= carry << 63;
+	} else {
+		vli_rshift1(x1);
+	}
+	/* t1 = 3/2*(x1^2 - z1^4) = B */
+
+	vli_mod_square_fast(z1, x1);      /* t3 = B^2 */
+	vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */
+	vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
+	vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */
+	vli_mod_mult_fast(x1, x1, t5);    /* t1 = B * (A - x3) */
+	vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
+
+	vli_set(x1, z1);
+	vli_set(z1, y1);
+	vli_set(y1, t4);
+}
+
+/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
+static void apply_z(uint64_t *x1, uint64_t *y1, uint64_t *z)
+{
+	uint64_t t1[NUM_ECC_DIGITS];
+
+	vli_mod_square_fast(t1, z);    /* z^2 */
+	vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */
+	vli_mod_mult_fast(t1, t1, z);  /* z^3 */
+	vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */
+}
+
+/* P = (x1, y1) => 2P, (x2, y2) => P' */
+static void xycz_initial_double(uint64_t *x1, uint64_t *y1, uint64_t *x2,
+					uint64_t *y2, uint64_t *p_initial_z)
+{
+	uint64_t z[NUM_ECC_DIGITS];
+
+	vli_set(x2, x1);
+	vli_set(y2, y1);
+
+	vli_clear(z);
+	z[0] = 1;
+
+	if (p_initial_z)
+		vli_set(z, p_initial_z);
+
+	apply_z(x1, y1, z);
+
+	ecc_point_double_jacobian(x1, y1, z);
+
+	apply_z(x2, y2, z);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
+ * or P => P', Q => P + Q
+ */
+static void xycz_add(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2)
+{
+	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+	uint64_t t5[NUM_ECC_DIGITS];
+
+	vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+	vli_mod_square_fast(t5, t5);      /* t5 = (x2 - x1)^2 = A */
+	vli_mod_mult_fast(x1, x1, t5);    /* t1 = x1*A = B */
+	vli_mod_mult_fast(x2, x2, t5);    /* t3 = x2*A = C */
+	vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+	vli_mod_square_fast(t5, y2);      /* t5 = (y2 - y1)^2 = D */
+
+	vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */
+	vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */
+	vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */
+	vli_mod_mult_fast(y1, y1, x2);    /* t2 = y1*(C - B) */
+	vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */
+	vli_mod_mult_fast(y2, y2, x2);    /* t4 = (y2 - y1)*(B - x3) */
+	vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+	vli_set(x2, t5);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
+ * or P => P - Q, Q => P + Q
+ */
+static void xycz_add_c(uint64_t *x1, uint64_t *y1, uint64_t *x2, uint64_t *y2)
+{
+	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+	uint64_t t5[NUM_ECC_DIGITS];
+	uint64_t t6[NUM_ECC_DIGITS];
+	uint64_t t7[NUM_ECC_DIGITS];
+
+	vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+	vli_mod_square_fast(t5, t5);      /* t5 = (x2 - x1)^2 = A */
+	vli_mod_mult_fast(x1, x1, t5);    /* t1 = x1*A = B */
+	vli_mod_mult_fast(x2, x2, t5);    /* t3 = x2*A = C */
+	vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */
+	vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+
+	vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */
+	vli_mod_mult_fast(y1, y1, t6);    /* t2 = y1 * (C - B) */
+	vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */
+	vli_mod_square_fast(x2, y2);      /* t3 = (y2 - y1)^2 */
+	vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */
+
+	vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */
+	vli_mod_mult_fast(y2, y2, t7);    /* t4 = (y2 - y1)*(B - x3) */
+	vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+	vli_mod_square_fast(t7, t5);      /* t7 = (y2 + y1)^2 = F */
+	vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */
+	vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */
+	vli_mod_mult_fast(t6, t6, t5);    /* t6 = (y2 + y1)*(x3' - B) */
+	vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */
+
+	vli_set(x1, t7);
+}
+
+static void ecc_point_mult(struct ecc_point *result,
+				const struct ecc_point *point,
+				uint64_t *scalar, uint64_t *initial_z,
+				int num_bits)
+{
+	/* R0 and R1 */
+	uint64_t rx[2][NUM_ECC_DIGITS];
+	uint64_t ry[2][NUM_ECC_DIGITS];
+	uint64_t z[NUM_ECC_DIGITS];
+	int i, nb;
+
+	vli_set(rx[1], point->x);
+	vli_set(ry[1], point->y);
+
+	xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z);
+
+	for (i = num_bits - 2; i > 0; i--) {
+		nb = !vli_test_bit(scalar, i);
+		xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+		xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+	}
+
+	nb = !vli_test_bit(scalar, 0);
+	xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+
+	/* Find final 1/Z value. */
+	vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */
+	vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */
+	vli_mod_mult_fast(z, z, point->x);   /* xP * Yb * (X1 - X0) */
+	vli_mod_inv(z, z, curve_p);          /* 1 / (xP * Yb * (X1 - X0)) */
+	vli_mod_mult_fast(z, z, point->y);   /* yP / (xP * Yb * (X1 - X0)) */
+	vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */
+	/* End 1/Z calculation */
+
+	xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+
+	apply_z(rx[0], ry[0], z);
+
+	vli_set(result->x, rx[0]);
+	vli_set(result->y, ry[0]);
+}
+
+/* Little endian byte-array to native conversion */
+static void ecc_bytes2native(const uint8_t bytes[ECC_BYTES],
+						uint64_t native[NUM_ECC_DIGITS])
+{
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++) {
+		const uint8_t *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+		native[NUM_ECC_DIGITS - 1 - i] =
+					((uint64_t) digit[0] << 0) |
+					((uint64_t) digit[1] << 8) |
+					((uint64_t) digit[2] << 16) |
+					((uint64_t) digit[3] << 24) |
+					((uint64_t) digit[4] << 32) |
+					((uint64_t) digit[5] << 40) |
+					((uint64_t) digit[6] << 48) |
+					((uint64_t) digit[7] << 56);
+	}
+}
+
+/* Native to little endian byte-array conversion */
+static void ecc_native2bytes(const uint64_t native[NUM_ECC_DIGITS],
+						uint8_t bytes[ECC_BYTES])
+{
+	int i;
+
+	for (i = 0; i < NUM_ECC_DIGITS; i++) {
+		uint8_t *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+		digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0;
+		digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8;
+		digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16;
+		digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24;
+		digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32;
+		digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40;
+		digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48;
+		digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56;
+	}
+}
+
+bool ecc_make_key(uint8_t public_key[64], uint8_t private_key[32])
+{
+	struct ecc_point pk;
+	uint64_t priv[NUM_ECC_DIGITS];
+	unsigned tries = 0;
+
+	do {
+		if (!get_random_number(priv) || (tries++ >= MAX_TRIES))
+			return false;
+
+		if (vli_is_zero(priv))
+			continue;
+
+		/* Make sure the private key is in the range [1, n-1]. */
+		if (vli_cmp(curve_n, priv) != 1)
+			continue;
+
+		ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv));
+	} while (ecc_point_is_zero(&pk));
+
+	ecc_native2bytes(priv, private_key);
+	ecc_native2bytes(pk.x, public_key);
+	ecc_native2bytes(pk.y, &public_key[32]);
+
+	return true;
+}
+
+bool ecdh_shared_secret(const uint8_t public_key[64],
+				const uint8_t private_key[32],
+				uint8_t secret[32])
+{
+	uint64_t priv[NUM_ECC_DIGITS];
+	uint64_t rand[NUM_ECC_DIGITS];
+	struct ecc_point product, pk;
+
+	if (!get_random_number(rand))
+		return false;
+
+	ecc_bytes2native(public_key, pk.x);
+	ecc_bytes2native(&public_key[32], pk.y);
+	ecc_bytes2native(private_key, priv);
+
+	ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv));
+
+	ecc_native2bytes(product.x, secret);
+
+	return !ecc_point_is_zero(&product);
+}
diff --git a/bluez/repo/src/shared/ecc.h b/bluez/repo/src/shared/ecc.h
new file mode 100644
index 0000000..e971375
--- /dev/null
+++ b/bluez/repo/src/shared/ecc.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Create a public/private key pair.
+ * Outputs:
+ *	public_key  - Will be filled in with the public key.
+ *	private_Key - Will be filled in with the private key.
+ *
+ * Returns true if the key pair was generated successfully, false
+ * if an error occurred. They keys are with the LSB first.
+ */
+bool ecc_make_key(uint8_t public_key[64], uint8_t private_key[32]);
+
+/* Compute a shared secret given your secret key and someone else's
+ * public key.
+ * Note: It is recommended that you hash the result of ecdh_shared_secret
+ * before using it for symmetric encryption or HMAC.
+ *
+ * Inputs:
+ *	public_key  - The public key of the remote party.
+ *	private_Key - Your private key.
+ *
+ * Outputs:
+ *	secret - Will be filled in with the shared secret value.
+ *
+ * Returns true if the shared secret was generated successfully, false
+ * if an error occurred. Both input and output parameters are with the
+ * LSB first.
+ */
+bool ecdh_shared_secret(const uint8_t public_key[64],
+				const uint8_t private_key[32],
+				uint8_t secret[32]);
diff --git a/bluez/repo/src/shared/gap.c b/bluez/repo/src/shared/gap.c
new file mode 100644
index 0000000..4a21e5d
--- /dev/null
+++ b/bluez/repo/src/shared/gap.c
@@ -0,0 +1,278 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/gap.h"
+
+#define FLAG_MGMT_CONN_CONTROL	(0 << 1)
+
+struct bt_gap {
+	int ref_count;
+	uint16_t index;
+	struct mgmt *mgmt;
+
+	uint8_t mgmt_version;
+	uint16_t mgmt_revision;
+	bool mgmt_ready;
+
+	unsigned long flags;
+
+	bt_gap_ready_func_t ready_handler;
+	bt_gap_destroy_func_t ready_destroy;
+	void *ready_data;
+
+	uint8_t static_addr[6];
+	uint8_t local_irk[16];
+	struct queue *irk_list;
+};
+
+struct irk_entry {
+	uint8_t addr_type;
+	uint8_t addr[6];
+	uint8_t key[16];
+};
+
+static void irk_entry_free(void *data)
+{
+	struct irk_entry *irk = data;
+
+	free(irk);
+}
+
+static void ready_status(struct bt_gap *gap, bool status)
+{
+	gap->mgmt_ready = status;
+
+	if (gap->ready_handler)
+		gap->ready_handler(status, gap->ready_data);
+}
+
+static void read_commands_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct bt_gap *gap = user_data;
+	const struct mgmt_rp_read_commands *rp = param;
+	uint16_t num_commands, num_events;
+	const uint16_t *opcode;
+	size_t expected_len;
+	int i;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		ready_status(gap, false);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		ready_status(gap, false);
+		return;
+	}
+
+	num_commands = le16_to_cpu(rp->num_commands);
+	num_events = le16_to_cpu(rp->num_events);
+
+	expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) +
+						num_events * sizeof(uint16_t);
+
+	if (length < expected_len) {
+		ready_status(gap, false);
+		return;
+	}
+
+	opcode = rp->opcodes;
+
+	for (i = 0; i < num_commands; i++) {
+		uint16_t op = get_le16(opcode++);
+
+		if (op == MGMT_OP_ADD_DEVICE)
+			gap->flags |= FLAG_MGMT_CONN_CONTROL;
+	}
+
+	ready_status(gap, true);
+}
+
+static void read_version_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct bt_gap *gap = user_data;
+	const struct mgmt_rp_read_version *rp = param;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		ready_status(gap, false);
+		return;
+	}
+
+	if (length < sizeof(*rp)) {
+		ready_status(gap, false);
+		return;
+	}
+
+	gap->mgmt_version = rp->version;
+	gap->mgmt_revision = le16_to_cpu(rp->revision);
+
+	if (!mgmt_send(gap->mgmt, MGMT_OP_READ_COMMANDS,
+				MGMT_INDEX_NONE, 0, NULL,
+				read_commands_complete, gap, NULL)) {
+		ready_status(gap, false);
+		return;
+	}
+}
+
+struct bt_gap *bt_gap_new_default(void)
+{
+	return bt_gap_new_index(0x0000);
+}
+
+struct bt_gap *bt_gap_new_index(uint16_t index)
+{
+	struct bt_gap *gap;
+
+	if (index == MGMT_INDEX_NONE)
+		return NULL;
+
+	gap = new0(struct bt_gap, 1);
+	gap->index = index;
+
+	gap->mgmt = mgmt_new_default();
+	if (!gap->mgmt) {
+		free(gap);
+		return NULL;
+	}
+
+	gap->irk_list = queue_new();
+	gap->mgmt_ready = false;
+
+	if (!mgmt_send(gap->mgmt, MGMT_OP_READ_VERSION,
+					MGMT_INDEX_NONE, 0, NULL,
+					read_version_complete, gap, NULL)) {
+		mgmt_unref(gap->mgmt);
+		return NULL;
+	}
+
+	return bt_gap_ref(gap);
+}
+
+struct bt_gap *bt_gap_ref(struct bt_gap *gap)
+{
+	if (!gap)
+		return NULL;
+
+	__sync_fetch_and_add(&gap->ref_count, 1);
+
+	return gap;
+}
+
+void bt_gap_unref(struct bt_gap *gap)
+{
+	if (!gap)
+		return;
+
+	if (__sync_sub_and_fetch(&gap->ref_count, 1))
+		return;
+
+	gap->mgmt_ready = false;
+
+	mgmt_cancel_all(gap->mgmt);
+	mgmt_unregister_all(gap->mgmt);
+
+	if (gap->ready_destroy)
+		gap->ready_destroy(gap->ready_data);
+
+	queue_destroy(gap->irk_list, irk_entry_free);
+
+	mgmt_unref(gap->mgmt);
+
+	free(gap);
+}
+
+bool bt_gap_set_ready_handler(struct bt_gap *gap,
+				bt_gap_ready_func_t handler, void *user_data,
+				bt_gap_destroy_func_t destroy)
+{
+	if (!gap)
+		return false;
+
+	if (gap->ready_destroy)
+		gap->ready_destroy(gap->ready_data);
+
+	gap->ready_handler = handler;
+	gap->ready_destroy = destroy;
+	gap->ready_data = user_data;
+
+	return true;
+}
+
+bool bt_gap_set_static_addr(struct bt_gap *gap, uint8_t addr[6])
+{
+	if (!gap)
+		return false;
+
+	memcpy(gap->static_addr, addr, 6);
+
+	return true;
+}
+
+bool bt_gap_set_local_irk(struct bt_gap *gap, uint8_t key[16])
+{
+	if (!gap)
+		return false;
+
+	memcpy(gap->local_irk, key, 16);
+
+	return true;
+}
+
+bool bt_gap_add_peer_irk(struct bt_gap *gap, uint8_t addr_type,
+					uint8_t addr[6], uint8_t key[16])
+{
+	struct irk_entry *irk;
+
+	if (!gap)
+		return false;
+
+	if (addr_type > BT_GAP_ADDR_TYPE_LE_RANDOM)
+		return false;
+
+	irk = new0(struct irk_entry, 1);
+	irk->addr_type = addr_type;
+	memcpy(irk->addr, addr, 6);
+	memcpy(irk->key, key, 16);
+
+	if (!queue_push_tail(gap->irk_list, irk)) {
+		free(irk);
+		return false;
+	}
+
+	return true;
+}
diff --git a/bluez/repo/src/shared/gap.h b/bluez/repo/src/shared/gap.h
new file mode 100644
index 0000000..52c264a
--- /dev/null
+++ b/bluez/repo/src/shared/gap.h
@@ -0,0 +1,49 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+#define BT_GAP_ADDR_TYPE_BREDR		0x00
+#define BT_GAP_ADDR_TYPE_LE_PUBLIC	0x01
+#define BT_GAP_ADDR_TYPE_LE_RANDOM	0x02
+
+struct bt_gap;
+
+struct bt_gap *bt_gap_new_default(void);
+struct bt_gap *bt_gap_new_index(uint16_t index);
+
+struct bt_gap *bt_gap_ref(struct bt_gap *gap);
+void bt_gap_unref(struct bt_gap *gap);
+
+typedef void (*bt_gap_destroy_func_t)(void *user_data);
+typedef void (*bt_gap_ready_func_t)(bool success, void *user_data);
+
+bool bt_gap_set_ready_handler(struct bt_gap *gap,
+				bt_gap_ready_func_t handler, void *user_data,
+				bt_gap_destroy_func_t destroy);
+
+bool bt_gap_set_static_addr(struct bt_gap *gap, uint8_t addr[6]);
+bool bt_gap_set_local_irk(struct bt_gap *gap, uint8_t key[16]);
+bool bt_gap_add_peer_irk(struct bt_gap *gap, uint8_t addr_type,
+					uint8_t addr[6], uint8_t key[16]);
diff --git a/bluez/repo/src/shared/gatt-client.c b/bluez/repo/src/shared/gatt-client.c
new file mode 100644
index 0000000..06ac763
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-client.c
@@ -0,0 +1,2983 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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 "src/shared/att.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/gatt-helpers.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <sys/uio.h>
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define UUID_BYTES (BT_GATT_UUID_SIZE * sizeof(uint8_t))
+
+#define GATT_SVC_UUID	0x1801
+#define SVC_CHNGD_UUID	0x2a05
+
+struct bt_gatt_client {
+	struct bt_att *att;
+	int ref_count;
+
+	bt_gatt_client_callback_t ready_callback;
+	bt_gatt_client_destroy_func_t ready_destroy;
+	void *ready_data;
+
+	bt_gatt_client_service_changed_callback_t svc_chngd_callback;
+	bt_gatt_client_destroy_func_t svc_chngd_destroy;
+	void *svc_chngd_data;
+
+	bt_gatt_client_debug_func_t debug_callback;
+	bt_gatt_client_destroy_func_t debug_destroy;
+	void *debug_data;
+
+	struct gatt_db *db;
+	bool in_init;
+	bool ready;
+
+	/*
+	 * Queue of long write requests. An error during "prepare write"
+	 * requests can result in a cancel through "execute write". To prevent
+	 * cancelation of prepared writes to the wrong attribute and multiple
+	 * requests to the same attribute that may result in a corrupted final
+	 * value, we avoid interleaving prepared writes.
+	 */
+	struct queue *long_write_queue;
+	bool in_long_write;
+
+	unsigned int reliable_write_session_id;
+
+	/* List of registered disconnect/notification/indication callbacks */
+	struct queue *notify_list;
+	struct queue *notify_chrcs;
+	int next_reg_id;
+	unsigned int disc_id, notify_id, ind_id;
+
+	/*
+	 * Handles of the GATT Service and the Service Changed characteristic
+	 * value handle. These will have the value 0 if they are not present on
+	 * the remote peripheral.
+	 */
+	unsigned int svc_chngd_ind_id;
+	bool svc_chngd_registered;
+	struct queue *svc_chngd_queue;  /* Queued service changed events */
+	bool in_svc_chngd;
+
+	/*
+	 * List of pending read/write operations. For operations that span
+	 * across multiple PDUs, this list provides a mapping from an operation
+	 * id to an ATT request id.
+	 */
+	struct queue *pending_requests;
+	unsigned int next_request_id;
+
+	struct bt_gatt_request *discovery_req;
+	unsigned int mtu_req_id;
+};
+
+struct request {
+	struct bt_gatt_client *client;
+	bool long_write;
+	bool prep_write;
+	bool removed;
+	int ref_count;
+	unsigned int id;
+	unsigned int att_id;
+	void *data;
+	void (*destroy)(void *);
+};
+
+static struct request *request_ref(struct request *req)
+{
+	__sync_fetch_and_add(&req->ref_count, 1);
+
+	return req;
+}
+
+static struct request *request_create(struct bt_gatt_client *client)
+{
+	struct request *req;
+
+	req = new0(struct request, 1);
+
+	if (client->next_request_id < 1)
+		client->next_request_id = 1;
+
+	queue_push_tail(client->pending_requests, req);
+	req->client = client;
+	req->id = client->next_request_id++;
+
+	return request_ref(req);
+}
+
+static void request_unref(void *data)
+{
+	struct request *req = data;
+
+	if (__sync_sub_and_fetch(&req->ref_count, 1))
+		return;
+
+	if (req->destroy)
+		req->destroy(req->data);
+
+	if (!req->removed)
+		queue_remove(req->client->pending_requests, req);
+
+	free(req);
+}
+
+struct notify_chrc {
+	uint16_t value_handle;
+	uint16_t ccc_handle;
+	uint16_t properties;
+	int notify_count;  /* Reference count of registered notify callbacks */
+
+	/* Pending calls to register_notify are queued here so that they can be
+	 * processed after a write that modifies the CCC descriptor.
+	 */
+	struct queue *reg_notify_queue;
+	unsigned int ccc_write_id;
+};
+
+struct notify_data {
+	struct bt_gatt_client *client;
+	unsigned int id;
+	unsigned int att_id;
+	int ref_count;
+	struct notify_chrc *chrc;
+	bt_gatt_client_register_callback_t callback;
+	bt_gatt_client_notify_callback_t notify;
+	void *user_data;
+	bt_gatt_client_destroy_func_t destroy;
+};
+
+static struct notify_data *notify_data_ref(struct notify_data *notify_data)
+{
+	__sync_fetch_and_add(&notify_data->ref_count, 1);
+
+	return notify_data;
+}
+
+static void notify_data_unref(void *data)
+{
+	struct notify_data *notify_data = data;
+
+	if (__sync_sub_and_fetch(&notify_data->ref_count, 1))
+		return;
+
+	if (notify_data->destroy)
+		notify_data->destroy(notify_data->user_data);
+
+	free(notify_data);
+}
+
+static void find_ccc(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct gatt_db_attribute **ccc_ptr = user_data;
+	bt_uuid_t uuid;
+
+	if (*ccc_ptr)
+		return;
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+	if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
+		return;
+
+	*ccc_ptr = attr;
+}
+
+static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
+							uint16_t value_handle)
+{
+	struct gatt_db_attribute *attr, *ccc;
+	struct notify_chrc *chrc;
+	bt_uuid_t uuid;
+	uint8_t properties;
+
+	/* Check that chrc_value_handle belongs to a known characteristic */
+	attr = gatt_db_get_attribute(client->db, value_handle - 1);
+	if (!attr)
+		return NULL;
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+	if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
+		return NULL;
+
+	if (!gatt_db_attribute_get_char_data(attr, NULL, NULL,
+							&properties, NULL))
+			return NULL;
+
+	chrc = new0(struct notify_chrc, 1);
+
+	chrc->reg_notify_queue = queue_new();
+	if (!chrc->reg_notify_queue) {
+		free(chrc);
+		return NULL;
+	}
+
+	/*
+	 * Find the CCC characteristic. Some characteristics that allow
+	 * notifications may not have a CCC descriptor. We treat these as
+	 * automatically successful.
+	 */
+	ccc = NULL;
+	gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
+	if (ccc)
+		chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
+
+	chrc->value_handle = value_handle;
+	chrc->properties = properties;
+
+	queue_push_tail(client->notify_chrcs, chrc);
+
+	return chrc;
+}
+
+static void notify_chrc_free(void *data)
+{
+	struct notify_chrc *chrc = data;
+
+	queue_destroy(chrc->reg_notify_queue, notify_data_unref);
+	free(chrc);
+}
+
+static bool match_notify_data_id(const void *a, const void *b)
+{
+	const struct notify_data *notify_data = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notify_data->id == id;
+}
+
+struct handle_range {
+	uint16_t start;
+	uint16_t end;
+};
+
+static bool match_notify_data_handle_range(const void *a, const void *b)
+{
+	const struct notify_data *notify_data = a;
+	struct notify_chrc *chrc = notify_data->chrc;
+	const struct handle_range *range = b;
+
+	return chrc->value_handle >= range->start &&
+					chrc->value_handle <= range->end;
+}
+
+static bool match_notify_chrc_handle_range(const void *a, const void *b)
+{
+	const struct notify_chrc *chrc = a;
+	const struct handle_range *range = b;
+
+	return chrc->value_handle >= range->start &&
+					chrc->value_handle <= range->end;
+}
+
+static void notify_data_cleanup(void *data)
+{
+	struct notify_data *notify_data = data;
+
+	if (notify_data->att_id)
+		bt_att_cancel(notify_data->client->att, notify_data->att_id);
+
+	notify_data_unref(notify_data);
+}
+
+static void gatt_client_remove_all_notify_in_range(
+				struct bt_gatt_client *client,
+				uint16_t start_handle, uint16_t end_handle)
+{
+	struct handle_range range;
+
+	range.start = start_handle;
+	range.end = end_handle;
+
+	queue_remove_all(client->notify_list, match_notify_data_handle_range,
+						&range, notify_data_cleanup);
+}
+
+static void gatt_client_remove_notify_chrcs_in_range(
+				struct bt_gatt_client *client,
+				uint16_t start_handle, uint16_t end_handle)
+{
+	struct handle_range range;
+
+	range.start = start_handle;
+	range.end = end_handle;
+
+	queue_remove_all(client->notify_chrcs, match_notify_chrc_handle_range,
+						&range, notify_chrc_free);
+}
+
+struct discovery_op;
+
+typedef void (*discovery_op_complete_func_t)(struct discovery_op *op,
+							bool success,
+							uint8_t att_ecode);
+typedef void (*discovery_op_fail_func_t)(struct discovery_op *op);
+
+struct discovery_op {
+	struct bt_gatt_client *client;
+	struct queue *pending_svcs;
+	struct queue *pending_chrcs;
+	struct queue *tmp_queue;
+	struct gatt_db_attribute *cur_svc;
+	bool success;
+	uint16_t start;
+	uint16_t end;
+	int ref_count;
+	discovery_op_complete_func_t complete_func;
+	discovery_op_fail_func_t failure_func;
+};
+
+static void discovery_op_free(struct discovery_op *op)
+{
+	queue_destroy(op->pending_svcs, NULL);
+	queue_destroy(op->pending_chrcs, free);
+	queue_destroy(op->tmp_queue, NULL);
+	free(op);
+}
+
+static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
+				uint16_t start, uint16_t end,
+				discovery_op_complete_func_t complete_func,
+				discovery_op_fail_func_t failure_func)
+{
+	struct discovery_op *op;
+
+	op = new0(struct discovery_op, 1);
+	op->pending_svcs = queue_new();
+	op->pending_chrcs = queue_new();
+	op->tmp_queue = queue_new();
+	op->client = client;
+	op->complete_func = complete_func;
+	op->failure_func = failure_func;
+	op->start = start;
+	op->end = end;
+
+	return op;
+}
+
+static struct discovery_op *discovery_op_ref(struct discovery_op *op)
+{
+	__sync_fetch_and_add(&op->ref_count, 1);
+
+	return op;
+}
+
+static void discovery_op_unref(void *data)
+{
+	struct discovery_op *op = data;
+
+	if (__sync_sub_and_fetch(&op->ref_count, 1))
+		return;
+
+	if (!op->success)
+		op->failure_func(op);
+
+	discovery_op_free(op);
+}
+
+static void discovery_req_clear(struct bt_gatt_client *client)
+{
+	if (!client->discovery_req)
+		return;
+
+	bt_gatt_request_unref(client->discovery_req);
+	client->discovery_req = NULL;
+}
+
+static void discover_chrcs_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data);
+
+static void discover_incl_cb(bool success, uint8_t att_ecode,
+				struct bt_gatt_result *result, void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	struct bt_gatt_iter iter;
+	struct gatt_db_attribute *attr, *tmp;
+	uint16_t handle, start, end;
+	uint128_t u128;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+	unsigned int includes_count, i;
+
+	discovery_req_clear(client);
+
+	if (!success) {
+		if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
+			goto next;
+
+		goto failed;
+	}
+
+	/* Get the currently processed service */
+	attr = op->cur_svc;
+	if (!attr)
+		goto failed;
+
+	if (!result || !bt_gatt_iter_init(&iter, result))
+		goto failed;
+
+	includes_count = bt_gatt_result_included_count(result);
+	if (includes_count == 0)
+		goto failed;
+
+	util_debug(client->debug_callback, client->debug_data,
+						"Included services found: %u",
+						includes_count);
+
+	for (i = 0; i < includes_count; i++) {
+		if (!bt_gatt_iter_next_included_service(&iter, &handle, &start,
+							&end, u128.data))
+			break;
+
+		bt_uuid128_create(&uuid, u128);
+
+		/* Log debug message */
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		util_debug(client->debug_callback, client->debug_data,
+				"handle: 0x%04x, start: 0x%04x, end: 0x%04x,"
+				"uuid: %s", handle, start, end, uuid_str);
+
+		tmp = gatt_db_get_attribute(client->db, start);
+		if (!tmp)
+			goto failed;
+
+		tmp = gatt_db_service_add_included(attr, tmp);
+		if (!tmp)
+			goto failed;
+
+		/*
+		 * GATT requires that all include definitions precede
+		 * characteristic declarations. Based on the order we're adding
+		 * these entries, the correct handle must be assigned to the new
+		 * attribute.
+		 */
+		if (gatt_db_attribute_get_handle(tmp) != handle)
+			goto failed;
+	}
+
+next:
+	/* Move on to the next service */
+	attr = queue_pop_head(op->pending_svcs);
+	if (!attr) {
+		struct queue *tmp_queue;
+
+		tmp_queue = op->pending_svcs;
+		op->pending_svcs = op->tmp_queue;
+		op->tmp_queue = tmp_queue;
+
+		/*
+		 * We have processed all include definitions. Move on to
+		 * characteristics.
+		 */
+		attr = queue_pop_head(op->pending_svcs);
+		if (!attr)
+			goto failed;
+
+		if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+			goto failed;
+
+		op->cur_svc = attr;
+
+		client->discovery_req = bt_gatt_discover_characteristics(
+							client->att,
+							start, end,
+							discover_chrcs_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+		if (client->discovery_req)
+			return;
+
+		util_debug(client->debug_callback, client->debug_data,
+				"Failed to start characteristic discovery");
+		discovery_op_unref(op);
+		goto failed;
+	}
+
+	queue_push_tail(op->tmp_queue, attr);
+	op->cur_svc = attr;
+	if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+		goto failed;
+
+	if (start == end)
+		goto next;
+
+	client->discovery_req = bt_gatt_discover_included_services(client->att,
+							start, end,
+							discover_incl_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+					"Failed to start included discovery");
+	discovery_op_unref(op);
+
+failed:
+	op->success = false;
+	op->complete_func(op, false, att_ecode);
+}
+
+struct chrc {
+	uint16_t start_handle;
+	uint16_t end_handle;
+	uint16_t value_handle;
+	uint8_t properties;
+	bt_uuid_t uuid;
+};
+
+static void discover_descs_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data);
+
+static bool discover_descs(struct discovery_op *op, bool *discovering)
+{
+	struct bt_gatt_client *client = op->client;
+	struct gatt_db_attribute *attr;
+	struct chrc *chrc_data;
+	uint16_t desc_start;
+
+	*discovering = false;
+
+	while ((chrc_data = queue_pop_head(op->pending_chrcs))) {
+		attr = gatt_db_service_insert_characteristic(op->cur_svc,
+							chrc_data->value_handle,
+							&chrc_data->uuid, 0,
+							chrc_data->properties,
+							NULL, NULL, NULL);
+
+		if (!attr)
+			goto failed;
+
+		if (gatt_db_attribute_get_handle(attr) !=
+							chrc_data->value_handle)
+			goto failed;
+
+		/*
+		 * check for descriptors presence, before initializing the
+		 * desc_handle and avoid integer overflow during desc_handle
+		 * intialization.
+		 */
+		if (chrc_data->value_handle >= chrc_data->end_handle) {
+			free(chrc_data);
+			continue;
+		}
+		desc_start = chrc_data->value_handle + 1;
+
+		client->discovery_req = bt_gatt_discover_descriptors(
+							client->att, desc_start,
+							chrc_data->end_handle,
+							discover_descs_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+		if (client->discovery_req) {
+			*discovering = true;
+			goto done;
+		}
+
+		util_debug(client->debug_callback, client->debug_data,
+					"Failed to start descriptor discovery");
+		discovery_op_unref(op);
+
+		goto failed;
+	}
+
+done:
+	free(chrc_data);
+	return true;
+
+failed:
+	free(chrc_data);
+	return false;
+}
+
+static void discover_descs_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	struct bt_gatt_iter iter;
+	struct gatt_db_attribute *attr;
+	uint16_t handle, start, end;
+	uint128_t u128;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+	unsigned int desc_count;
+	bool discovering;
+
+	discovery_req_clear(client);
+
+	if (!success) {
+		if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+			success = true;
+			goto next;
+		}
+
+		goto done;
+	}
+
+	if (!result || !bt_gatt_iter_init(&iter, result))
+		goto failed;
+
+	desc_count = bt_gatt_result_descriptor_count(result);
+	if (desc_count == 0)
+		goto failed;
+
+	util_debug(client->debug_callback, client->debug_data,
+					"Descriptors found: %u", desc_count);
+
+	while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) {
+		bt_uuid128_create(&uuid, u128);
+
+		/* Log debug message */
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		util_debug(client->debug_callback, client->debug_data,
+						"handle: 0x%04x, uuid: %s",
+						handle, uuid_str);
+
+		attr = gatt_db_service_insert_descriptor(op->cur_svc, handle,
+							&uuid, 0, NULL, NULL,
+							NULL);
+		if (!attr)
+			goto failed;
+
+		if (gatt_db_attribute_get_handle(attr) != handle)
+			goto failed;
+	}
+
+next:
+	if (!discover_descs(op, &discovering))
+		goto failed;
+
+	if (discovering)
+		return;
+
+	/* Done with the current service */
+	gatt_db_service_set_active(op->cur_svc, true);
+
+	attr = queue_pop_head(op->pending_svcs);
+	if (!attr)
+		goto done;
+
+	if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+		goto failed;
+
+	if (start == end)
+		goto next;
+
+	/* Move on to the next service */
+	op->cur_svc = attr;
+
+	client->discovery_req = bt_gatt_discover_characteristics(client->att,
+							start, end,
+							discover_chrcs_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+				"Failed to start characteristic discovery");
+	discovery_op_unref(op);
+
+failed:
+	success = false;
+
+done:
+	op->success = success;
+	op->complete_func(op, success, att_ecode);
+}
+
+static void discover_chrcs_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	struct bt_gatt_iter iter;
+	struct gatt_db_attribute *attr;
+	struct chrc *chrc_data;
+	uint16_t start, end, value;
+	uint8_t properties;
+	uint128_t u128;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+	unsigned int chrc_count;
+	bool discovering;
+
+	discovery_req_clear(client);
+
+	if (!success) {
+		if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+			success = true;
+			goto next;
+		}
+
+		goto done;
+	}
+
+	if (!op->cur_svc || !result || !bt_gatt_iter_init(&iter, result))
+		goto failed;
+
+	chrc_count = bt_gatt_result_characteristic_count(result);
+	util_debug(client->debug_callback, client->debug_data,
+				"Characteristics found: %u", chrc_count);
+
+	if (chrc_count == 0)
+		goto failed;
+
+	while (bt_gatt_iter_next_characteristic(&iter, &start, &end, &value,
+						&properties, u128.data)) {
+		bt_uuid128_create(&uuid, u128);
+
+		/* Log debug message */
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		util_debug(client->debug_callback, client->debug_data,
+				"start: 0x%04x, end: 0x%04x, value: 0x%04x, "
+				"props: 0x%02x, uuid: %s",
+				start, end, value, properties, uuid_str);
+
+		chrc_data = new0(struct chrc, 1);
+
+		chrc_data->start_handle = start;
+		chrc_data->end_handle = end;
+		chrc_data->value_handle = value;
+		chrc_data->properties = properties;
+		chrc_data->uuid = uuid;
+
+		queue_push_tail(op->pending_chrcs, chrc_data);
+	}
+
+	/*
+	 * Sequentially discover descriptors for each characteristic and insert
+	 * the characteristics into the database as we proceed.
+	 */
+	if (!discover_descs(op, &discovering))
+		goto failed;
+
+	if (discovering)
+		return;
+
+next:
+	/* Done with the current service */
+	gatt_db_service_set_active(op->cur_svc, true);
+
+	attr = queue_pop_head(op->pending_svcs);
+	if (!attr)
+		goto done;
+
+	if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
+		goto failed;
+
+	if (start == end)
+		goto next;
+
+	/* Move on to the next service */
+	op->cur_svc = attr;
+
+	client->discovery_req = bt_gatt_discover_characteristics(client->att,
+							start, end,
+							discover_chrcs_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+				"Failed to start characteristic discovery");
+	discovery_op_unref(op);
+
+failed:
+	success = false;
+
+done:
+	op->success = success;
+	op->complete_func(op, success, att_ecode);
+}
+
+static void discover_secondary_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	struct bt_gatt_iter iter;
+	struct gatt_db_attribute *attr;
+	uint16_t start, end;
+	uint128_t u128;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+
+	discovery_req_clear(client);
+
+	if (!success) {
+		util_debug(client->debug_callback, client->debug_data,
+					"Secondary service discovery failed."
+					" ATT ECODE: 0x%02x", att_ecode);
+		switch (att_ecode) {
+		case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND:
+		case BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE:
+			goto next;
+		default:
+			goto done;
+		}
+	}
+
+	if (!result || !bt_gatt_iter_init(&iter, result)) {
+		success = false;
+		goto done;
+	}
+
+	util_debug(client->debug_callback, client->debug_data,
+					"Secondary services found: %u",
+					bt_gatt_result_service_count(result));
+
+	while (bt_gatt_iter_next_service(&iter, &start, &end, u128.data)) {
+		bt_uuid128_create(&uuid, u128);
+
+		/* Log debug message */
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		util_debug(client->debug_callback, client->debug_data,
+				"start: 0x%04x, end: 0x%04x, uuid: %s",
+				start, end, uuid_str);
+
+		/* Store the service */
+		attr = gatt_db_insert_service(client->db, start, &uuid, false,
+							end - start + 1);
+		if (!attr) {
+			gatt_db_clear_range(client->db, start, end);
+			attr = gatt_db_insert_service(client->db, start, &uuid,
+							false, end - start + 1);
+			if (!attr) {
+				util_debug(client->debug_callback,
+						client->debug_data,
+						"Failed to store service");
+				success = false;
+				goto done;
+			}
+		}
+
+		/* Skip if service already active */
+		if (!gatt_db_service_get_active(attr))
+			queue_push_tail(op->pending_svcs, attr);
+	}
+
+next:
+	/* Sequentially discover included services */
+	attr = queue_pop_head(op->pending_svcs);
+
+	/* Complete with success if queue is empty */
+	if (!attr) {
+		success = true;
+		goto done;
+	}
+
+	/*
+	 * Store the service in the tmp queue to be reused during
+	 * characteristics discovery later.
+	 */
+	queue_push_tail(op->tmp_queue, attr);
+	op->cur_svc = attr;
+
+	if (!gatt_db_attribute_get_service_handles(attr, &start, &end)) {
+		success = false;
+		goto done;
+	}
+
+	client->discovery_req = bt_gatt_discover_included_services(client->att,
+							start, end,
+							discover_incl_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+				"Failed to start included services discovery");
+	discovery_op_unref(op);
+
+done:
+	op->success = success;
+	op->complete_func(op, success, att_ecode);
+}
+
+static void discover_primary_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	struct bt_gatt_iter iter;
+	struct gatt_db_attribute *attr;
+	uint16_t start, end;
+	uint128_t u128;
+	bt_uuid_t uuid;
+	char uuid_str[MAX_LEN_UUID_STR];
+
+	discovery_req_clear(client);
+
+	if (!success) {
+		util_debug(client->debug_callback, client->debug_data,
+					"Primary service discovery failed."
+					" ATT ECODE: 0x%02x", att_ecode);
+		/* Reset error in case of not found */
+		if (BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+			success = true;
+			att_ecode = 0;
+		}
+		goto secondary;
+	}
+
+	if (!result || !bt_gatt_iter_init(&iter, result)) {
+		success = false;
+		goto done;
+	}
+
+	util_debug(client->debug_callback, client->debug_data,
+					"Primary services found: %u",
+					bt_gatt_result_service_count(result));
+
+	while (bt_gatt_iter_next_service(&iter, &start, &end, u128.data)) {
+		bt_uuid128_create(&uuid, u128);
+
+		/* Log debug message. */
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		util_debug(client->debug_callback, client->debug_data,
+				"start: 0x%04x, end: 0x%04x, uuid: %s",
+				start, end, uuid_str);
+
+		attr = gatt_db_insert_service(client->db, start, &uuid, true,
+							end - start + 1);
+		if (!attr) {
+			gatt_db_clear_range(client->db, start, end);
+			attr = gatt_db_insert_service(client->db, start, &uuid,
+							true, end - start + 1);
+			if (!attr) {
+				util_debug(client->debug_callback,
+						client->debug_data,
+						"Failed to store service");
+				success = false;
+				goto done;
+			}
+		}
+
+		/* Skip if service already active */
+		if (!gatt_db_service_get_active(attr))
+			queue_push_tail(op->pending_svcs, attr);
+	}
+
+secondary:
+	/*
+	 * Version 4.2 [Vol 1, Part A] page 101:
+	 * A secondary service is a service that provides auxiliary
+	 * functionality of a device and is referenced from at least one
+	 * primary service on the device.
+	 */
+	if (queue_isempty(op->pending_svcs))
+		goto done;
+
+	/* Discover secondary services */
+	client->discovery_req = bt_gatt_discover_secondary_services(client->att,
+						NULL, op->start, op->end,
+						discover_secondary_cb,
+						discovery_op_ref(op),
+						discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+				"Failed to start secondary service discovery");
+	discovery_op_unref(op);
+	success = false;
+
+done:
+	op->success = success;
+	op->complete_func(op, success, att_ecode);
+}
+
+static void notify_client_ready(struct bt_gatt_client *client, bool success,
+							uint8_t att_ecode)
+{
+	if (!client->ready_callback || client->ready)
+		return;
+
+	bt_gatt_client_ref(client);
+	client->ready = success;
+	client->ready_callback(success, att_ecode, client->ready_data);
+	bt_gatt_client_unref(client);
+}
+
+static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+
+	op->success = success;
+	client->mtu_req_id = 0;
+
+	if (!success) {
+		util_debug(client->debug_callback, client->debug_data,
+				"MTU Exchange failed. ATT ECODE: 0x%02x",
+				att_ecode);
+
+		/*
+		 * BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 546
+		 * If the Error Response is sent by the server with the Error
+		 * Code set to RequestNot Supported , the Attribute Opcode is
+		 * not supported and the default MTU shall be used.
+		 */
+		if (att_ecode == BT_ATT_ERROR_REQUEST_NOT_SUPPORTED)
+			goto discover;
+
+		client->in_init = false;
+		notify_client_ready(client, success, att_ecode);
+
+		return;
+	}
+
+	util_debug(client->debug_callback, client->debug_data,
+					"MTU exchange complete, with MTU: %u",
+					bt_att_get_mtu(client->att));
+
+discover:
+	client->discovery_req = bt_gatt_discover_all_primary_services(
+							client->att, NULL,
+							discover_primary_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+			"Failed to initiate primary service discovery");
+
+	client->in_init = false;
+	notify_client_ready(client, false, att_ecode);
+
+	discovery_op_unref(op);
+}
+
+struct service_changed_op {
+	struct bt_gatt_client *client;
+	uint16_t start_handle;
+	uint16_t end_handle;
+};
+
+static void process_service_changed(struct bt_gatt_client *client,
+							uint16_t start_handle,
+							uint16_t end_handle);
+static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data);
+
+static void complete_notify_request(void *data)
+{
+	struct notify_data *notify_data = data;
+
+	/* Increment the per-characteristic ref count of notify handlers */
+	__sync_fetch_and_add(&notify_data->chrc->notify_count, 1);
+
+	notify_data->att_id = 0;
+	notify_data->callback(0, notify_data->user_data);
+}
+
+static bool notify_data_write_ccc(struct notify_data *notify_data, bool enable,
+						bt_att_response_func_t callback)
+{
+	uint8_t pdu[4];
+	unsigned int att_id;
+
+	assert(notify_data->chrc->ccc_handle);
+	memset(pdu, 0, sizeof(pdu));
+	put_le16(notify_data->chrc->ccc_handle, pdu);
+
+	if (enable) {
+		/* Try to enable notifications and/or indications based on
+		 * whatever the characteristic supports.
+		 */
+		if (notify_data->chrc->properties & BT_GATT_CHRC_PROP_NOTIFY)
+			pdu[2] = 0x01;
+
+		if (notify_data->chrc->properties & BT_GATT_CHRC_PROP_INDICATE)
+			pdu[2] |= 0x02;
+
+		if (!pdu[2])
+			return false;
+	}
+
+	att_id = bt_att_send(notify_data->client->att, BT_ATT_OP_WRITE_REQ,
+						pdu, sizeof(pdu), callback,
+						notify_data_ref(notify_data),
+						notify_data_unref);
+	notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
+
+	return !!att_id;
+}
+
+static uint8_t process_error(const void *pdu, uint16_t length)
+{
+	const struct bt_att_pdu_error_rsp *error_pdu;
+
+	if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp))
+		return 0;
+
+	error_pdu = pdu;
+
+	return error_pdu->ecode;
+}
+
+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;
+
+	assert(!notify_data->chrc->notify_count);
+	assert(notify_data->chrc->ccc_write_id);
+
+	notify_data->chrc->ccc_write_id = 0;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		att_ecode = process_error(pdu, length);
+
+		/* Failed to enable. Complete the current request and move on to
+		 * the next one in the queue. If there was an error sending the
+		 * write request, then just move on to the next queued entry.
+		 */
+		queue_remove(notify_data->client->notify_list, notify_data);
+		notify_data->callback(att_ecode, notify_data->user_data);
+
+		while ((notify_data = queue_pop_head(
+					notify_data->chrc->reg_notify_queue))) {
+
+			if (notify_data_write_ccc(notify_data, true,
+							enable_ccc_callback))
+				return;
+		}
+
+		return;
+	}
+
+	/* Success! Report success for all remaining requests. */
+	bt_gatt_client_ref(notify_data->client);
+
+	complete_notify_request(notify_data);
+	queue_remove_all(notify_data->chrc->reg_notify_queue, NULL, NULL,
+						complete_notify_request);
+
+	bt_gatt_client_unref(notify_data->client);
+}
+
+static bool match_notify_chrc_value_handle(const void *a, const void *b)
+{
+	const struct notify_chrc *chrc = a;
+	uint16_t value_handle = PTR_TO_UINT(b);
+
+	return chrc->value_handle == value_handle;
+}
+
+static unsigned int register_notify(struct bt_gatt_client *client,
+				uint16_t handle,
+				bt_gatt_client_register_callback_t callback,
+				bt_gatt_client_notify_callback_t notify,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy)
+{
+	struct notify_data *notify_data;
+	struct notify_chrc *chrc = NULL;
+
+	/* Check if a characteristic ref count has been started already */
+	chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
+						UINT_TO_PTR(handle));
+
+	if (!chrc) {
+		/*
+		 * Create an entry if the characteristic is known and has a CCC
+		 * descriptor.
+		 */
+		chrc = notify_chrc_create(client, handle);
+		if (!chrc)
+			return 0;
+	}
+
+	/* Fail if we've hit the maximum allowed notify sessions */
+	if (chrc->notify_count == INT_MAX)
+		return 0;
+
+	notify_data = new0(struct notify_data, 1);
+	notify_data->client = client;
+	notify_data->ref_count = 1;
+	notify_data->chrc = chrc;
+	notify_data->callback = callback;
+	notify_data->notify = notify;
+	notify_data->user_data = user_data;
+	notify_data->destroy = destroy;
+
+	/* Add the handler to the bt_gatt_client's general list */
+	queue_push_tail(client->notify_list, notify_data);
+
+	/* Assign an ID to the handler. */
+	if (client->next_reg_id < 1)
+		client->next_reg_id = 1;
+
+	notify_data->id = client->next_reg_id++;
+
+	/*
+	 * If a write to the CCC descriptor is in progress, then queue this
+	 * request.
+	 */
+	if (chrc->ccc_write_id) {
+		queue_push_tail(chrc->reg_notify_queue, notify_data);
+		return notify_data->id;
+	}
+
+	/*
+	 * If the ref count is not zero, then notifications are already enabled.
+	 */
+	if (chrc->notify_count > 0 || !chrc->ccc_handle) {
+		complete_notify_request(notify_data);
+		return notify_data->id;
+	}
+
+	/* Write to the CCC descriptor */
+	if (!notify_data_write_ccc(notify_data, true, enable_ccc_callback)) {
+		queue_remove(client->notify_list, notify_data);
+		free(notify_data);
+		return 0;
+	}
+
+	return notify_data->id;
+}
+
+static void get_first_attribute(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	struct gatt_db_attribute **stored = user_data;
+
+	if (*stored)
+		return;
+
+	*stored = attrib;
+}
+
+static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
+{
+	bool success;
+	struct bt_gatt_client *client = user_data;
+
+	if (att_ecode) {
+		util_debug(client->debug_callback, client->debug_data,
+			"Failed to register handler for \"Service Changed\"");
+		success = false;
+		client->svc_chngd_ind_id = 0;
+		goto done;
+	}
+
+	client->svc_chngd_registered = true;
+	success = true;
+	util_debug(client->debug_callback, client->debug_data,
+			"Registered handler for \"Service Changed\": %u",
+			client->svc_chngd_ind_id);
+
+done:
+	notify_client_ready(client, success, att_ecode);
+}
+
+static bool register_service_changed(struct bt_gatt_client *client)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *attr = NULL;
+
+	bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
+
+	if (client->svc_chngd_ind_id)
+		return true;
+
+	gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+						get_first_attribute, &attr);
+	if (!attr)
+		return true;
+
+	/*
+	 * Register an indication handler for the "Service Changed"
+	 * characteristic and report ready only if the handler is registered
+	 * successfully.
+	 */
+	client->svc_chngd_ind_id = register_notify(client,
+					gatt_db_attribute_get_handle(attr),
+					service_changed_register_cb,
+					service_changed_cb,
+					client, NULL);
+
+	return client->svc_chngd_ind_id ? true : false;
+}
+
+static void service_changed_complete(struct discovery_op *op, bool success,
+							uint8_t att_ecode)
+{
+	struct bt_gatt_client *client = op->client;
+	struct service_changed_op *next_sc_op;
+	uint16_t start_handle = op->start;
+	uint16_t end_handle = op->end;
+
+	client->in_svc_chngd = false;
+
+	if (!success && att_ecode != BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+		util_debug(client->debug_callback, client->debug_data,
+			"Failed to discover services within changed range - "
+			"error: 0x%02x", att_ecode);
+
+		gatt_db_clear_range(client->db, start_handle, end_handle);
+	}
+
+	/* Notify the upper layer of changed services */
+	if (client->svc_chngd_callback)
+		client->svc_chngd_callback(start_handle, end_handle,
+							client->svc_chngd_data);
+
+	/* Process any queued events */
+	next_sc_op = queue_pop_head(client->svc_chngd_queue);
+	if (next_sc_op) {
+		process_service_changed(client, next_sc_op->start_handle,
+							next_sc_op->end_handle);
+		free(next_sc_op);
+		return;
+	}
+
+	if (register_service_changed(client))
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+		"Failed to re-register handler for \"Service Changed\"");
+}
+
+static void service_changed_failure(struct discovery_op *op)
+{
+	struct bt_gatt_client *client = op->client;
+
+	gatt_db_clear_range(client->db, op->start, op->end);
+}
+
+static void process_service_changed(struct bt_gatt_client *client,
+							uint16_t start_handle,
+							uint16_t end_handle)
+{
+	struct discovery_op *op;
+
+	/* On full database reset just re-run attribute discovery */
+	if (start_handle == 0x0001 && end_handle == 0xffff)
+		goto discover;
+
+	/* Invalidate and remove all effected notify callbacks */
+	gatt_client_remove_all_notify_in_range(client, start_handle,
+								end_handle);
+	gatt_client_remove_notify_chrcs_in_range(client, start_handle,
+								end_handle);
+
+	/* Remove all services that overlap the modified range since we'll
+	 * rediscover them
+	 */
+	gatt_db_clear_range(client->db, start_handle, end_handle);
+
+discover:
+	op = discovery_op_create(client, start_handle, end_handle,
+						service_changed_complete,
+						service_changed_failure);
+	if (!op)
+		goto fail;
+
+	client->discovery_req = bt_gatt_discover_primary_services(client->att,
+						NULL, start_handle, end_handle,
+						discover_primary_cb,
+						discovery_op_ref(op),
+						discovery_op_unref);
+	if (client->discovery_req) {
+		client->in_svc_chngd = true;
+		return;
+	}
+
+	discovery_op_free(op);
+
+fail:
+	util_debug(client->debug_callback, client->debug_data,
+					"Failed to initiate service discovery"
+					" after Service Changed");
+}
+
+static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+	struct service_changed_op *op;
+	uint16_t start, end;
+
+	if (length != 4)
+		return;
+
+	start = get_le16(value);
+	end = get_le16(value + 2);
+
+	if (start > end) {
+		util_debug(client->debug_callback, client->debug_data,
+			"Service Changed received with invalid handles");
+		return;
+	}
+
+	util_debug(client->debug_callback, client->debug_data,
+			"Service Changed received - start: 0x%04x end: 0x%04x",
+			start, end);
+
+	if (!client->in_svc_chngd) {
+		process_service_changed(client, start, end);
+		return;
+	}
+
+	op = new0(struct service_changed_op, 1);
+
+	op->start_handle = start;
+	op->end_handle = end;
+
+	queue_push_tail(client->svc_chngd_queue, op);
+}
+
+static void init_complete(struct discovery_op *op, bool success,
+							uint8_t att_ecode)
+{
+	struct bt_gatt_client *client = op->client;
+
+	client->in_init = false;
+
+	if (!success)
+		goto fail;
+
+	if (register_service_changed(client))
+		goto done;
+
+	util_debug(client->debug_callback, client->debug_data,
+			"Failed to register handler for \"Service Changed\"");
+	success = false;
+
+fail:
+	util_debug(client->debug_callback, client->debug_data,
+			"Failed to initialize gatt-client");
+
+	op->success = false;
+
+done:
+	notify_client_ready(client, success, att_ecode);
+}
+
+static void init_fail(struct discovery_op *op)
+{
+	struct bt_gatt_client *client = op->client;
+
+	gatt_db_clear(client->db);
+}
+
+static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
+{
+	struct discovery_op *op;
+
+	if (client->in_init || client->ready)
+		return false;
+
+	op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
+								init_fail);
+	if (!op)
+		return false;
+
+	/* Configure the MTU */
+	client->mtu_req_id = bt_gatt_exchange_mtu(client->att,
+						MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
+						exchange_mtu_cb,
+						discovery_op_ref(op),
+						discovery_op_unref);
+	if (!client->mtu_req_id) {
+		discovery_op_free(op);
+		return false;
+	}
+
+	client->in_init = true;
+
+	return true;
+}
+
+struct pdu_data {
+	const void *pdu;
+	uint16_t length;
+};
+
+static void disable_ccc_callback(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct notify_data *notify_data = user_data;
+	struct notify_data *next_data;
+
+	assert(!notify_data->chrc->notify_count);
+	assert(notify_data->chrc->ccc_write_id);
+
+	notify_data->chrc->ccc_write_id = 0;
+
+	/* This is a best effort procedure, so ignore errors and process any
+	 * queued requests.
+	 */
+	while (1) {
+		next_data = queue_pop_head(notify_data->chrc->reg_notify_queue);
+		if (!next_data || notify_data_write_ccc(notify_data, true,
+							enable_ccc_callback))
+			return;
+	}
+}
+
+static void complete_unregister_notify(void *data)
+{
+	struct notify_data *notify_data = data;
+
+	/*
+	 * If a procedure to enable the CCC is still pending, then cancel it and
+	 * return.
+	 */
+	if (notify_data->att_id) {
+		bt_att_cancel(notify_data->client->att, notify_data->att_id);
+		goto done;
+	}
+
+	if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
+						!notify_data->chrc->ccc_handle)
+		goto done;
+
+	notify_data_write_ccc(notify_data, false, disable_ccc_callback);
+
+done:
+	notify_data_unref(notify_data);
+}
+
+static void notify_handler(void *data, void *user_data)
+{
+	struct notify_data *notify_data = data;
+	struct pdu_data *pdu_data = user_data;
+	uint16_t value_handle;
+	const uint8_t *value = NULL;
+
+	value_handle = get_le16(pdu_data->pdu);
+
+	if (notify_data->chrc->value_handle != value_handle)
+		return;
+
+	if (pdu_data->length > 2)
+		value = pdu_data->pdu + 2;
+
+	/*
+	 * Even if the notify data has a pending ATT request to write to the
+	 * CCC, there is really no reason not to notify the handlers.
+	 */
+	if (notify_data->notify)
+		notify_data->notify(value_handle, value, pdu_data->length - 2,
+							notify_data->user_data);
+}
+
+static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+	struct pdu_data pdu_data;
+
+	bt_gatt_client_ref(client);
+
+	memset(&pdu_data, 0, sizeof(pdu_data));
+	pdu_data.pdu = pdu;
+	pdu_data.length = length;
+
+	queue_foreach(client->notify_list, notify_handler, &pdu_data);
+
+	if (opcode == BT_ATT_OP_HANDLE_VAL_IND)
+		bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
+							NULL, NULL, NULL);
+
+	bt_gatt_client_unref(client);
+}
+
+static void bt_gatt_client_free(struct bt_gatt_client *client)
+{
+	bt_gatt_client_cancel_all(client);
+
+	queue_destroy(client->notify_list, notify_data_cleanup);
+
+	if (client->ready_destroy)
+		client->ready_destroy(client->ready_data);
+
+	if (client->debug_destroy)
+		client->debug_destroy(client->debug_data);
+
+	if (client->att) {
+		bt_att_unregister_disconnect(client->att, client->disc_id);
+		bt_att_unregister(client->att, client->notify_id);
+		bt_att_unregister(client->att, client->ind_id);
+		bt_att_unref(client->att);
+	}
+
+	gatt_db_unref(client->db);
+
+	queue_destroy(client->svc_chngd_queue, free);
+	queue_destroy(client->long_write_queue, request_unref);
+	queue_destroy(client->notify_chrcs, notify_chrc_free);
+	queue_destroy(client->pending_requests, request_unref);
+
+	free(client);
+}
+
+static void att_disconnect_cb(int err, void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+	bool in_init = client->in_init;
+
+	client->disc_id = 0;
+
+	bt_att_unref(client->att);
+	client->att = NULL;
+
+	client->in_init = false;
+	client->ready = false;
+
+	if (in_init)
+		notify_client_ready(client, false, 0);
+}
+
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+							struct bt_att *att,
+							uint16_t mtu)
+{
+	struct bt_gatt_client *client;
+
+	if (!att || !db)
+		return NULL;
+
+	client = new0(struct bt_gatt_client, 1);
+	client->disc_id = bt_att_register_disconnect(att, att_disconnect_cb,
+								client, NULL);
+	if (!client->disc_id)
+		goto fail;
+
+	client->long_write_queue = queue_new();
+	client->svc_chngd_queue = queue_new();
+	client->notify_list = queue_new();
+	client->notify_chrcs = queue_new();
+	client->pending_requests = queue_new();
+
+	client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
+						notify_cb, client, NULL);
+	if (!client->notify_id)
+		goto fail;
+
+	client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_IND,
+						notify_cb, client, NULL);
+	if (!client->ind_id)
+		goto fail;
+
+	client->att = bt_att_ref(att);
+	client->db = gatt_db_ref(db);
+
+	if (!gatt_client_init(client, mtu))
+		goto fail;
+
+	return bt_gatt_client_ref(client);
+
+fail:
+	bt_gatt_client_free(client);
+	return NULL;
+}
+
+struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client)
+{
+	if (!client)
+		return NULL;
+
+	__sync_fetch_and_add(&client->ref_count, 1);
+
+	return client;
+}
+
+void bt_gatt_client_unref(struct bt_gatt_client *client)
+{
+	if (!client)
+		return;
+
+	if (__sync_sub_and_fetch(&client->ref_count, 1))
+		return;
+
+	bt_gatt_client_free(client);
+}
+
+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,
+					bt_gatt_client_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy)
+{
+	if (!client)
+		return false;
+
+	if (client->ready_destroy)
+		client->ready_destroy(client->ready_data);
+
+	client->ready_callback = callback;
+	client->ready_destroy = destroy;
+	client->ready_data = user_data;
+
+	return true;
+}
+
+bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
+			bt_gatt_client_service_changed_callback_t callback,
+			void *user_data,
+			bt_gatt_client_destroy_func_t destroy)
+{
+	if (!client)
+		return false;
+
+	if (client->svc_chngd_destroy)
+		client->svc_chngd_destroy(client->svc_chngd_data);
+
+	client->svc_chngd_callback = callback;
+	client->svc_chngd_destroy = destroy;
+	client->svc_chngd_data = user_data;
+
+	return true;
+}
+
+bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
+					bt_gatt_client_debug_func_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy) {
+	if (!client)
+		return false;
+
+	if (client->debug_destroy)
+		client->debug_destroy(client->debug_data);
+
+	client->debug_callback = callback;
+	client->debug_destroy = destroy;
+	client->debug_data = user_data;
+
+	return true;
+}
+
+uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client)
+{
+	if (!client || !client->att)
+		return 0;
+
+	return bt_att_get_mtu(client->att);
+}
+
+struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
+{
+	if (!client || !client->db)
+		return NULL;
+
+	return client->db;
+}
+
+static bool match_req_id(const void *a, const void *b)
+{
+	const struct request *req = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return req->id == id;
+}
+
+static void cancel_long_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
+								void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+
+	if (queue_isempty(client->long_write_queue))
+		client->in_long_write = false;
+}
+
+static bool cancel_long_write_req(struct bt_gatt_client *client,
+							struct request *req)
+{
+	uint8_t pdu = 0x00;
+
+	/*
+	 * att_id == 0 means that request has been queued and no prepare write
+	 * has been sent so far.Let's just remove if from the queue.
+	 * Otherwise execute write needs to be send.
+	 */
+	if (!req->att_id)
+		return queue_remove(client->long_write_queue, req);
+
+	return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+							sizeof(pdu),
+							cancel_long_write_cb,
+							client, NULL);
+
+}
+
+static void cancel_prep_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct bt_gatt_client *client = req->client;
+
+	client->reliable_write_session_id = 0;
+}
+
+static bool cancel_prep_write_session(struct bt_gatt_client *client,
+							struct request *req)
+{
+	uint8_t pdu = 0x00;
+
+	return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+							sizeof(pdu),
+							cancel_prep_write_cb,
+							req, request_unref);
+}
+
+static bool cancel_request(struct request *req)
+{
+	req->removed = true;
+
+	if (req->long_write)
+		return cancel_long_write_req(req->client, req);
+
+	if (req->prep_write)
+		return cancel_prep_write_session(req->client, req);
+
+	return bt_att_cancel(req->client->att, req->att_id);
+}
+
+bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
+{
+	struct request *req;
+
+	if (!client || !id || !client->att)
+		return false;
+
+	req = queue_remove_if(client->pending_requests, match_req_id,
+							UINT_TO_PTR(id));
+	if (!req)
+		return false;
+
+	return cancel_request(req);
+}
+
+bool bt_gatt_client_cancel_all(struct bt_gatt_client *client)
+{
+	if (!client || !client->att)
+		return false;
+
+	queue_remove_all(client->pending_requests, NULL, NULL,
+					(queue_destroy_func_t) cancel_request);
+
+	if (client->discovery_req) {
+		bt_gatt_request_cancel(client->discovery_req);
+		bt_gatt_request_unref(client->discovery_req);
+		client->discovery_req = NULL;
+	}
+
+	if (client->mtu_req_id)
+		bt_att_cancel(client->att, client->mtu_req_id);
+
+	return true;
+}
+
+struct read_op {
+	bt_gatt_client_read_callback_t callback;
+	void *user_data;
+	bt_gatt_client_destroy_func_t destroy;
+};
+
+static void destroy_read_op(void *data)
+{
+	struct read_op *op = data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op);
+}
+
+static void read_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct read_op *op = req->data;
+	bool success;
+	uint8_t att_ecode = 0;
+	const uint8_t *value = NULL;
+	uint16_t value_len = 0;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) {
+		success = false;
+		goto done;
+	}
+
+	success = true;
+	value_len = length;
+	if (value_len)
+		value = pdu;
+
+done:
+	if (op->callback)
+		op->callback(success, att_ecode, value, length, op->user_data);
+}
+
+unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client,
+					uint16_t value_handle,
+					bt_gatt_client_read_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy)
+{
+	struct request *req;
+	struct read_op *op;
+	uint8_t pdu[2];
+
+	if (!client)
+		return 0;
+
+	op = new0(struct read_op, 1);
+
+	req = request_create(client);
+	if (!req) {
+		free(op);
+		return 0;
+	}
+
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	req->data = op;
+	req->destroy = destroy_read_op;
+
+	put_le16(value_handle, pdu);
+
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_REQ,
+							pdu, sizeof(pdu),
+							read_cb, req,
+							request_unref);
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	return req->id;
+}
+
+static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct read_op *op = req->data;
+	uint8_t att_ecode;
+	bool success;
+
+	if (opcode != BT_ATT_OP_READ_MULT_RSP || (!pdu && length)) {
+		success = false;
+
+		if (opcode == BT_ATT_OP_ERROR_RSP)
+			att_ecode = process_error(pdu, length);
+		else
+			att_ecode = 0;
+
+		pdu = NULL;
+		length = 0;
+	} else {
+		success = true;
+		att_ecode = 0;
+	}
+
+	if (op->callback)
+		op->callback(success, att_ecode, pdu, length, op->user_data);
+}
+
+unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
+					uint16_t *handles, uint8_t num_handles,
+					bt_gatt_client_read_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy)
+{
+	uint8_t pdu[num_handles * 2];
+	struct request *req;
+	struct read_op *op;
+	int i;
+
+	if (!client)
+		return 0;
+
+	if (num_handles < 2)
+		return 0;
+
+	if (num_handles * 2 > bt_att_get_mtu(client->att) - 1)
+		return 0;
+
+	op = new0(struct read_op, 1);
+
+	req = request_create(client);
+	if (!req) {
+		free(op);
+		return 0;
+	}
+
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	req->data = op;
+	req->destroy = destroy_read_op;
+
+	for (i = 0; i < num_handles; i++)
+		put_le16(handles[i], pdu + (2 * i));
+
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_MULT_REQ,
+							pdu, sizeof(pdu),
+							read_multiple_cb, req,
+							request_unref);
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	return req->id;
+}
+
+struct read_long_op {
+	struct bt_gatt_client *client;
+	int ref_count;
+	uint16_t value_handle;
+	uint16_t offset;
+	struct iovec iov;
+	bt_gatt_client_read_callback_t callback;
+	void *user_data;
+	bt_gatt_client_destroy_func_t destroy;
+};
+
+static void destroy_read_long_op(void *data)
+{
+	struct read_long_op *op = data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op->iov.iov_base);
+	free(op);
+}
+
+static bool append_chunk(struct read_long_op *op, const uint8_t *data,
+								uint16_t len)
+{
+	void *buf;
+
+	/* Truncate if the data would exceed maximum length */
+	if (op->offset + len > BT_ATT_MAX_VALUE_LEN)
+		len = BT_ATT_MAX_VALUE_LEN - op->offset;
+
+	buf = realloc(op->iov.iov_base, op->iov.iov_len + len);
+	if (!buf)
+		return false;
+
+	op->iov.iov_base = buf;
+
+	memcpy(op->iov.iov_base + op->iov.iov_len, data, len);
+
+	op->iov.iov_len += len;
+	op->offset += len;
+
+	return true;
+}
+
+static void read_long_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct request *req = user_data;
+	struct read_long_op *op = req->data;
+	bool success;
+	uint8_t att_ecode = 0;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_READ_BLOB_RSP || (!pdu && length)) {
+		success = false;
+		goto done;
+	}
+
+	if (!length)
+		goto success;
+
+	if (!append_chunk(op, pdu, length)) {
+		success = false;
+		goto done;
+	}
+
+	if (op->offset >= BT_ATT_MAX_VALUE_LEN)
+		goto success;
+
+	if (length >= bt_att_get_mtu(op->client->att) - 1) {
+		uint8_t pdu[4];
+
+		put_le16(op->value_handle, pdu);
+		put_le16(op->offset, pdu + 2);
+
+		req->att_id = bt_att_send(op->client->att,
+							BT_ATT_OP_READ_BLOB_REQ,
+							pdu, sizeof(pdu),
+							read_long_cb,
+							request_ref(req),
+							request_unref);
+		if (req->att_id)
+			return;
+
+		request_unref(req);
+		success = false;
+		goto done;
+	}
+
+success:
+	success = true;
+
+done:
+	if (op->callback)
+		op->callback(success, att_ecode, op->iov.iov_base,
+						op->iov.iov_len, op->user_data);
+}
+
+unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
+					uint16_t value_handle, uint16_t offset,
+					bt_gatt_client_read_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy)
+{
+	struct request *req;
+	struct read_long_op *op;
+	uint8_t pdu[4];
+
+	if (!client)
+		return 0;
+
+	op = new0(struct read_long_op, 1);
+
+	req = request_create(client);
+	if (!req) {
+		free(op);
+		return 0;
+	}
+
+	op->client = client;
+	op->value_handle = value_handle;
+	op->offset = offset;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	req->data = op;
+	req->destroy = destroy_read_long_op;
+
+	put_le16(value_handle, pdu);
+	put_le16(offset, pdu + 2);
+
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_BLOB_REQ,
+							pdu, sizeof(pdu),
+							read_long_cb, req,
+							request_unref);
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	return req->id;
+}
+
+unsigned int bt_gatt_client_write_without_response(
+					struct bt_gatt_client *client,
+					uint16_t value_handle,
+					bool signed_write,
+					const uint8_t *value, uint16_t length) {
+	uint8_t pdu[2 + length];
+	struct request *req;
+	int security;
+	uint8_t op;
+
+	if (!client)
+		return 0;
+
+	req = request_create(client);
+	if (!req)
+		return 0;
+
+	/* Only use signed write if unencrypted */
+	if (signed_write) {
+		security = bt_att_get_security(client->att);
+		op = security > BT_SECURITY_LOW ?  BT_ATT_OP_WRITE_CMD :
+						BT_ATT_OP_SIGNED_WRITE_CMD;
+	} else
+		op = BT_ATT_OP_WRITE_CMD;
+
+	put_le16(value_handle, pdu);
+	memcpy(pdu + 2, value, length);
+
+	req->att_id = bt_att_send(client->att, op, pdu, sizeof(pdu), NULL, req,
+								request_unref);
+	if (!req->att_id) {
+		request_unref(req);
+		return 0;
+	}
+
+	return req->id;
+}
+
+struct write_op {
+	struct bt_gatt_client *client;
+	bt_gatt_client_callback_t callback;
+	void *user_data;
+	bt_gatt_destroy_func_t destroy;
+};
+
+static void destroy_write_op(void *data)
+{
+	struct write_op *op = data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op);
+}
+
+static void write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct write_op *op = req->data;
+	bool success = true;
+	uint8_t att_ecode = 0;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_WRITE_RSP || pdu || length)
+		success = false;
+
+done:
+	if (op->callback)
+		op->callback(success, att_ecode, op->user_data);
+}
+
+unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
+					uint16_t value_handle,
+					const uint8_t *value, uint16_t length,
+					bt_gatt_client_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy)
+{
+	struct request *req;
+	struct write_op *op;
+	uint8_t pdu[2 + length];
+
+	if (!client)
+		return 0;
+
+	op = new0(struct write_op, 1);
+
+	req = request_create(client);
+	if (!req) {
+		free(op);
+		return 0;
+	}
+
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	req->data = op;
+	req->destroy = destroy_write_op;
+
+	put_le16(value_handle, pdu);
+	memcpy(pdu + 2, value, length);
+
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_WRITE_REQ,
+							pdu, sizeof(pdu),
+							write_cb, req,
+							request_unref);
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	return req->id;
+}
+
+struct long_write_op {
+	struct bt_gatt_client *client;
+	bool reliable;
+	bool success;
+	uint8_t att_ecode;
+	bool reliable_error;
+	uint16_t value_handle;
+	uint8_t *value;
+	uint16_t length;
+	uint16_t offset;
+	uint16_t index;
+	uint16_t cur_length;
+	bt_gatt_client_write_long_callback_t callback;
+	void *user_data;
+	bt_gatt_client_destroy_func_t destroy;
+};
+
+static void long_write_op_free(void *data)
+{
+	struct long_write_op *op = data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op->value);
+	free(op);
+}
+
+static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+							void *user_data);
+static void complete_write_long_op(struct request *req, bool success,
+					uint8_t att_ecode, bool reliable_error);
+
+static void handle_next_prep_write(struct request *req)
+{
+	struct long_write_op *op = req->data;
+	bool success = true;
+	uint8_t *pdu;
+
+	pdu = malloc(op->cur_length + 4);
+	if (!pdu) {
+		success = false;
+		goto done;
+	}
+
+	put_le16(op->value_handle, pdu);
+	put_le16(op->offset + op->index, pdu + 2);
+	memcpy(pdu + 4, op->value + op->index, op->cur_length);
+
+	req->att_id = bt_att_send(op->client->att, BT_ATT_OP_PREP_WRITE_REQ,
+							pdu, op->cur_length + 4,
+							prepare_write_cb,
+							request_ref(req),
+							request_unref);
+	if (!req->att_id) {
+		request_unref(req);
+		success = false;
+	}
+
+	free(pdu);
+
+	/* If so far successful, then the operation should continue.
+	 * Otherwise, there was an error and the procedure should be
+	 * completed.
+	 */
+	if (success)
+		return;
+
+done:
+	complete_write_long_op(req, success, 0, false);
+}
+
+static void start_next_long_write(struct bt_gatt_client *client)
+{
+	struct request *req;
+
+	if (queue_isempty(client->long_write_queue)) {
+		client->in_long_write = false;
+		return;
+	}
+
+	req  = queue_pop_head(client->long_write_queue);
+	if (!req)
+		return;
+
+	handle_next_prep_write(req);
+
+	/*
+	 * send_next_prep_write adds an extra ref. Unref here to clean up if
+	 * necessary, since we also added a ref before pushing to the queue.
+	 */
+	request_unref(req);
+}
+
+static void execute_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct long_write_op *op = req->data;
+	bool success = op->success;
+	uint8_t att_ecode = op->att_ecode;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+	} else if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length)
+		success = false;
+
+	bt_gatt_client_ref(op->client);
+
+	if (op->callback)
+		op->callback(success, op->reliable_error, att_ecode,
+								op->user_data);
+
+	start_next_long_write(op->client);
+
+	bt_gatt_client_unref(op->client);
+}
+
+static void complete_write_long_op(struct request *req, bool success,
+					uint8_t att_ecode, bool reliable_error)
+{
+	struct long_write_op *op = req->data;
+	uint8_t pdu;
+
+	op->success = success;
+	op->att_ecode = att_ecode;
+	op->reliable_error = reliable_error;
+
+	if (success)
+		pdu = 0x01;  /* Write */
+	else
+		pdu = 0x00;  /* Cancel */
+
+	req->att_id = bt_att_send(op->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+							&pdu, sizeof(pdu),
+							execute_write_cb,
+							request_ref(req),
+							request_unref);
+	if (req->att_id)
+		return;
+
+	request_unref(req);
+	success = false;
+
+	bt_gatt_client_ref(op->client);
+
+	if (op->callback)
+		op->callback(success, reliable_error, att_ecode, op->user_data);
+
+	start_next_long_write(op->client);
+
+	bt_gatt_client_unref(op->client);
+}
+
+static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct long_write_op *op = req->data;
+	bool success = true;
+	bool reliable_error = false;
+	uint8_t att_ecode = 0;
+	uint16_t next_index;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_PREP_WRITE_RSP) {
+		success = false;
+		goto done;
+	}
+
+	if (op->reliable) {
+		if (!pdu || length != (op->cur_length + 4)) {
+			success = false;
+			reliable_error = true;
+			goto done;
+		}
+
+		if (get_le16(pdu) != op->value_handle ||
+				get_le16(pdu + 2) != (op->offset + op->index)) {
+			success = false;
+			reliable_error = true;
+			goto done;
+		}
+
+		if (memcmp(pdu + 4, op->value + op->index, op->cur_length)) {
+			success = false;
+			reliable_error = true;
+			goto done;
+		}
+	}
+
+	next_index = op->index + op->cur_length;
+	if (next_index == op->length) {
+		/* All bytes written */
+		goto done;
+	}
+
+	/* If the last written length was greater than or equal to what can fit
+	 * inside a PDU, then there is more data to send.
+	 */
+	if (op->cur_length >= bt_att_get_mtu(op->client->att) - 5) {
+		op->index = next_index;
+		op->cur_length = MIN(op->length - op->index,
+					bt_att_get_mtu(op->client->att) - 5);
+		handle_next_prep_write(req);
+		return;
+	}
+
+done:
+	complete_write_long_op(req, success, att_ecode, reliable_error);
+}
+
+unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+				bool reliable,
+				uint16_t value_handle, uint16_t offset,
+				const uint8_t *value, uint16_t length,
+				bt_gatt_client_write_long_callback_t callback,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy)
+{
+	struct request *req;
+	struct long_write_op *op;
+	uint8_t *pdu;
+
+	if (!client)
+		return 0;
+
+	if ((size_t)(length + offset) > UINT16_MAX)
+		return 0;
+
+	/* Don't allow writing a 0-length value using this procedure. The
+	 * upper-layer should use bt_gatt_write_value for that instead.
+	 */
+	if (!length || !value)
+		return 0;
+
+	op = new0(struct long_write_op, 1);
+	op->value = malloc(length);
+	if (!op->value) {
+		free(op);
+		return 0;
+	}
+
+	req = request_create(client);
+	if (!req) {
+		free(op->value);
+		free(op);
+		return 0;
+	}
+
+	memcpy(op->value, value, length);
+
+	op->client = client;
+	op->reliable = reliable;
+	op->value_handle = value_handle;
+	op->length = length;
+	op->offset = offset;
+	op->cur_length = MIN(length, bt_att_get_mtu(client->att) - 5);
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	req->data = op;
+	req->destroy = long_write_op_free;
+	req->long_write = true;
+
+	if (client->in_long_write || client->reliable_write_session_id > 0) {
+		queue_push_tail(client->long_write_queue, req);
+		return req->id;
+	}
+
+	pdu = malloc(op->cur_length + 4);
+	if (!pdu) {
+		free(op->value);
+		free(op);
+		return 0;
+	}
+
+	put_le16(value_handle, pdu);
+	put_le16(offset, pdu + 2);
+	memcpy(pdu + 4, op->value, op->cur_length);
+
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ,
+							pdu, op->cur_length + 4,
+							prepare_write_cb, req,
+							request_unref);
+	free(pdu);
+
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	client->in_long_write = true;
+
+	return req->id;
+}
+
+struct prep_write_op {
+	bt_gatt_client_write_long_callback_t callback;
+	void *user_data;
+	bt_gatt_destroy_func_t destroy;
+	uint8_t *pdu;
+	uint16_t pdu_len;
+};
+
+static void destroy_prep_write_op(void *data)
+{
+	struct prep_write_op *op = data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op->pdu);
+	free(op);
+}
+
+static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct prep_write_op *op = req->data;
+	bool success;
+	uint8_t att_ecode;
+	bool reliable_error;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		reliable_error = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_PREP_WRITE_RSP) {
+		success = false;
+		reliable_error = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	if (!pdu || length != op->pdu_len ||
+					memcmp(pdu, op->pdu, op->pdu_len)) {
+		success = false;
+		reliable_error = true;
+		att_ecode = 0;
+		goto done;
+	}
+
+	success = true;
+	reliable_error = false;
+	att_ecode = 0;
+
+done:
+	if (op->callback)
+		op->callback(success, reliable_error, att_ecode, op->user_data);
+}
+
+static struct request *get_reliable_request(struct bt_gatt_client *client,
+							unsigned int id)
+{
+	struct request *req;
+	struct prep_write_op *op;
+
+	op = new0(struct prep_write_op, 1);
+
+	/* Following prepare writes */
+	if (id != 0)
+		req = queue_find(client->pending_requests, match_req_id,
+							UINT_TO_PTR(id));
+	else
+		req = request_create(client);
+
+	if (!req) {
+		free(op);
+		return NULL;
+	}
+
+	req->data = op;
+
+	return req;
+}
+
+unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
+				unsigned int id, uint16_t value_handle,
+				uint16_t offset, const uint8_t *value,
+				uint16_t length,
+				bt_gatt_client_write_long_callback_t callback,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy)
+{
+	struct request *req;
+	struct prep_write_op *op;
+	uint8_t pdu[4 + length];
+
+	if (!client)
+		return 0;
+
+	if (client->in_long_write)
+		return 0;
+
+	/*
+	 * Make sure that client who owns reliable session continues with
+	 * prepare writes or this is brand new reliable session (id == 0)
+	 */
+	if (id != client->reliable_write_session_id) {
+		util_debug(client->debug_callback, client->debug_data,
+			"There is other reliable write session ongoing %u",
+			client->reliable_write_session_id);
+
+		return 0;
+	}
+
+	req = get_reliable_request(client, id);
+	if (!req)
+		return 0;
+
+	op = (struct prep_write_op *)req->data;
+
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	req->destroy = destroy_prep_write_op;
+	req->prep_write = true;
+
+	put_le16(value_handle, pdu);
+	put_le16(offset, pdu + 2);
+	memcpy(pdu + 4, value, length);
+
+	/*
+	 * Before sending command we need to remember pdu as we need to validate
+	 * it in the response. Store handle, offset and value. Therefore
+	 * increase length by 4 (handle + offset) as we need it in couple places
+	 * below
+	 */
+	length += 4;
+
+	op->pdu = malloc(length);
+	if (!op->pdu) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	memcpy(op->pdu, pdu, length);
+	op->pdu_len = length;
+
+	/*
+	 * Now we are ready to send command
+	 * Note that request_unref will be done on write execute
+	 */
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ, pdu,
+					sizeof(pdu), prep_write_cb, req,
+					NULL);
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	/*
+	 * Store first request id for prepare write and treat it as a session id
+	 * valid until write execute is done
+	 */
+	if (client->reliable_write_session_id == 0)
+		client->reliable_write_session_id = req->id;
+
+	return client->reliable_write_session_id;
+}
+
+static void exec_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct request *req = user_data;
+	struct write_op *op = req->data;
+	bool success;
+	uint8_t att_ecode;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length) {
+		success = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	success = true;
+	att_ecode = 0;
+
+done:
+	if (op->callback)
+		op->callback(success, att_ecode, op->user_data);
+
+	op->client->reliable_write_session_id = 0;
+
+	start_next_long_write(op->client);
+}
+
+unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
+					unsigned int id,
+					bt_gatt_client_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy)
+{
+	struct request *req;
+	struct write_op *op;
+	uint8_t pdu;
+
+	if (!client)
+		return 0;
+
+	if (client->in_long_write)
+		return 0;
+
+	if (client->reliable_write_session_id != id)
+		return 0;
+
+	op = new0(struct write_op, 1);
+
+	req = queue_find(client->pending_requests, match_req_id,
+							UINT_TO_PTR(id));
+	if (!req) {
+		free(op);
+		return 0;
+	}
+
+	op->client = client;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	pdu = 0x01;
+
+	req->data = op;
+	req->destroy = destroy_write_op;
+
+	req->att_id = bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+						sizeof(pdu), exec_write_cb,
+						req, request_unref);
+	if (!req->att_id) {
+		op->destroy = NULL;
+		request_unref(req);
+		return 0;
+	}
+
+	return id;
+}
+
+unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
+				uint16_t chrc_value_handle,
+				bt_gatt_client_register_callback_t callback,
+				bt_gatt_client_notify_callback_t notify,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy)
+{
+	if (!client || !client->db || !chrc_value_handle || !callback)
+		return 0;
+
+	if (client->in_svc_chngd)
+		return 0;
+
+	return register_notify(client, chrc_value_handle, callback, notify,
+							user_data, destroy);
+}
+
+bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
+							unsigned int id)
+{
+	struct notify_data *notify_data;
+
+	if (!client || !id)
+		return false;
+
+	notify_data = queue_remove_if(client->notify_list, match_notify_data_id,
+							UINT_TO_PTR(id));
+	if (!notify_data)
+		return false;
+
+	assert(notify_data->chrc->notify_count > 0);
+	assert(!notify_data->chrc->ccc_write_id);
+
+	complete_unregister_notify(notify_data);
+	return true;
+}
+
+bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level)
+{
+	if (!client)
+		return false;
+
+	return bt_att_set_security(client->att, level);
+}
+
+int bt_gatt_client_get_security(struct bt_gatt_client *client)
+{
+	if (!client)
+		return -1;
+
+	return bt_att_get_security(client->att);
+}
diff --git a/bluez/repo/src/shared/gatt-client.h b/bluez/repo/src/shared/gatt-client.h
new file mode 100644
index 0000000..befa43f
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-client.h
@@ -0,0 +1,135 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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>
+#include <stddef.h>
+
+#define BT_GATT_UUID_SIZE 16
+
+struct bt_gatt_client;
+
+struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
+							struct bt_att *att,
+							uint16_t mtu);
+
+struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
+void bt_gatt_client_unref(struct bt_gatt_client *client);
+
+typedef void (*bt_gatt_client_destroy_func_t)(void *user_data);
+typedef void (*bt_gatt_client_callback_t)(bool success, uint8_t att_ecode,
+							void *user_data);
+typedef void (*bt_gatt_client_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_gatt_client_read_callback_t)(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data);
+typedef void (*bt_gatt_client_write_long_callback_t)(bool success,
+					bool reliable_error, uint8_t att_ecode,
+					void *user_data);
+typedef void (*bt_gatt_client_notify_callback_t)(uint16_t value_handle,
+					const uint8_t *value, uint16_t length,
+					void *user_data);
+typedef void (*bt_gatt_client_register_callback_t)(uint16_t att_ecode,
+							void *user_data);
+typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle,
+							uint16_t end_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,
+					bt_gatt_client_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
+			bt_gatt_client_service_changed_callback_t callback,
+			void *user_data,
+			bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
+					bt_gatt_client_debug_func_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+
+uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client);
+struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
+
+bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id);
+bool bt_gatt_client_cancel_all(struct bt_gatt_client *client);
+
+unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client,
+					uint16_t value_handle,
+					bt_gatt_client_read_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
+					uint16_t value_handle, uint16_t offset,
+					bt_gatt_client_read_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
+					uint16_t *handles, uint8_t num_handles,
+					bt_gatt_client_read_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+
+unsigned int bt_gatt_client_write_without_response(
+					struct bt_gatt_client *client,
+					uint16_t value_handle,
+					bool signed_write,
+					const uint8_t *value, uint16_t length);
+unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
+					uint16_t value_handle,
+					const uint8_t *value, uint16_t length,
+					bt_gatt_client_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+				bool reliable,
+				uint16_t value_handle, uint16_t offset,
+				const uint8_t *value, uint16_t length,
+				bt_gatt_client_write_long_callback_t callback,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
+				unsigned int id,
+				uint16_t value_handle, uint16_t offset,
+				const uint8_t *value, uint16_t length,
+				bt_gatt_client_write_long_callback_t callback,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
+					unsigned int id,
+					bt_gatt_client_callback_t callback,
+					void *user_data,
+					bt_gatt_client_destroy_func_t destroy);
+
+unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
+				uint16_t chrc_value_handle,
+				bt_gatt_client_register_callback_t callback,
+				bt_gatt_client_notify_callback_t notify,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
+							unsigned int id);
+
+bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level);
+int bt_gatt_client_get_security(struct bt_gatt_client *client);
diff --git a/bluez/repo/src/shared/gatt-db.c b/bluez/repo/src/shared/gatt-db.c
new file mode 100644
index 0000000..cc49458
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-db.c
@@ -0,0 +1,1723 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdbool.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/timeout.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define MAX_CHAR_DECL_VALUE_LEN 19
+#define MAX_INCLUDED_VALUE_LEN 6
+#define ATTRIBUTE_TIMEOUT 5000
+
+static const bt_uuid_t primary_service_uuid = { .type = BT_UUID16,
+					.value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t secondary_service_uuid = { .type = BT_UUID16,
+					.value.u16 = GATT_SND_SVC_UUID };
+static const bt_uuid_t characteristic_uuid = { .type = BT_UUID16,
+					.value.u16 = GATT_CHARAC_UUID };
+static const bt_uuid_t included_service_uuid = { .type = BT_UUID16,
+					.value.u16 = GATT_INCLUDE_UUID };
+
+struct gatt_db {
+	int ref_count;
+	uint16_t next_handle;
+	struct queue *services;
+
+	struct queue *notify_list;
+	unsigned int next_notify_id;
+};
+
+struct notify {
+	unsigned int id;
+	gatt_db_attribute_cb_t service_added;
+	gatt_db_attribute_cb_t service_removed;
+	gatt_db_destroy_func_t destroy;
+	void *user_data;
+};
+
+struct pending_read {
+	struct gatt_db_attribute *attrib;
+	unsigned int id;
+	unsigned int timeout_id;
+	gatt_db_attribute_read_t func;
+	void *user_data;
+};
+
+struct pending_write {
+	struct gatt_db_attribute *attrib;
+	unsigned int id;
+	unsigned int timeout_id;
+	gatt_db_attribute_write_t func;
+	void *user_data;
+};
+
+struct gatt_db_attribute {
+	struct gatt_db_service *service;
+	uint16_t handle;
+	bt_uuid_t uuid;
+	uint32_t permissions;
+	uint16_t value_len;
+	uint8_t *value;
+
+	gatt_db_read_t read_func;
+	gatt_db_write_t write_func;
+	void *user_data;
+
+	unsigned int read_id;
+	struct queue *pending_reads;
+
+	unsigned int write_id;
+	struct queue *pending_writes;
+};
+
+struct gatt_db_service {
+	struct gatt_db *db;
+	bool active;
+	bool claimed;
+	uint16_t num_handles;
+	struct gatt_db_attribute **attributes;
+};
+
+static void pending_read_result(struct pending_read *p, int err,
+					const uint8_t *data, size_t length)
+{
+	if (p->timeout_id > 0)
+		timeout_remove(p->timeout_id);
+
+	p->func(p->attrib, err, data, length, p->user_data);
+
+	free(p);
+}
+
+static void pending_read_free(void *data)
+{
+	struct pending_read *p = data;
+
+	pending_read_result(p, -ECANCELED, NULL, 0);
+}
+
+static void pending_write_result(struct pending_write *p, int err)
+{
+	if (p->timeout_id > 0)
+		timeout_remove(p->timeout_id);
+
+	p->func(p->attrib, err, p->user_data);
+
+	free(p);
+}
+
+static void pending_write_free(void *data)
+{
+	struct pending_write *p = data;
+
+	pending_write_result(p, -ECANCELED);
+}
+
+static void attribute_destroy(struct gatt_db_attribute *attribute)
+{
+	/* Attribute was not initialized by user */
+	if (!attribute)
+		return;
+
+	queue_destroy(attribute->pending_reads, pending_read_free);
+	queue_destroy(attribute->pending_writes, pending_write_free);
+
+	free(attribute->value);
+	free(attribute);
+}
+
+static struct gatt_db_attribute *new_attribute(struct gatt_db_service *service,
+							uint16_t handle,
+							const bt_uuid_t *type,
+							const uint8_t *val,
+							uint16_t len)
+{
+	struct gatt_db_attribute *attribute;
+
+	attribute = new0(struct gatt_db_attribute, 1);
+
+	attribute->service = service;
+	attribute->handle = handle;
+	attribute->uuid = *type;
+	attribute->value_len = len;
+	if (len) {
+		attribute->value = malloc0(len);
+		if (!attribute->value)
+			goto failed;
+
+		memcpy(attribute->value, val, len);
+	}
+
+	attribute->pending_reads = queue_new();
+	attribute->pending_writes = queue_new();
+
+	return attribute;
+
+failed:
+	attribute_destroy(attribute);
+	return NULL;
+}
+
+struct gatt_db *gatt_db_ref(struct gatt_db *db)
+{
+	if (!db)
+		return NULL;
+
+	__sync_fetch_and_add(&db->ref_count, 1);
+
+	return db;
+}
+
+struct gatt_db *gatt_db_new(void)
+{
+	struct gatt_db *db;
+
+	db = new0(struct gatt_db, 1);
+	db->services = queue_new();
+	db->notify_list = queue_new();
+	db->next_handle = 0x0001;
+
+	return gatt_db_ref(db);
+}
+
+static void notify_destroy(void *data)
+{
+	struct notify *notify = data;
+
+	if (notify->destroy)
+		notify->destroy(notify->user_data);
+
+	free(notify);
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+	const struct notify *notify = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notify->id == id;
+}
+
+struct notify_data {
+	struct gatt_db_attribute *attr;
+	bool added;
+};
+
+static void handle_notify(void *data, void *user_data)
+{
+	struct notify *notify = data;
+	struct notify_data *notify_data = user_data;
+
+	if (notify_data->added)
+		notify->service_added(notify_data->attr, notify->user_data);
+	else
+		notify->service_removed(notify_data->attr, notify->user_data);
+}
+
+static void notify_service_changed(struct gatt_db *db,
+						struct gatt_db_service *service,
+						bool added)
+{
+	struct notify_data data;
+
+	if (queue_isempty(db->notify_list))
+		return;
+
+	data.attr = service->attributes[0];
+	data.added = added;
+
+	gatt_db_ref(db);
+
+	queue_foreach(db->notify_list, handle_notify, &data);
+
+	gatt_db_unref(db);
+}
+
+static void gatt_db_service_destroy(void *data)
+{
+	struct gatt_db_service *service = data;
+	int i;
+
+	if (service->active)
+		notify_service_changed(service->db, service, false);
+
+	for (i = 0; i < service->num_handles; i++)
+		attribute_destroy(service->attributes[i]);
+
+	free(service->attributes);
+	free(service);
+}
+
+static void gatt_db_destroy(struct gatt_db *db)
+{
+	if (!db)
+		return;
+
+	/*
+	 * Clear the notify list before clearing the services to prevent the
+	 * latter from sending service_removed events.
+	 */
+	queue_destroy(db->notify_list, notify_destroy);
+	db->notify_list = NULL;
+
+	queue_destroy(db->services, gatt_db_service_destroy);
+	free(db);
+}
+
+void gatt_db_unref(struct gatt_db *db)
+{
+	if (!db)
+		return;
+
+	if (__sync_sub_and_fetch(&db->ref_count, 1))
+		return;
+
+	gatt_db_destroy(db);
+}
+
+bool gatt_db_isempty(struct gatt_db *db)
+{
+	if (!db)
+		return true;
+
+	return queue_isempty(db->services);
+}
+
+static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst)
+{
+	bt_uuid_t uuid128;
+
+	if (uuid->type == BT_UUID16) {
+		put_le16(uuid->value.u16, dst);
+		return bt_uuid_len(uuid);
+	}
+
+	bt_uuid_to_uuid128(uuid, &uuid128);
+	bswap_128(&uuid128.value.u128, dst);
+	return bt_uuid_len(&uuid128);
+}
+
+static bool le_to_uuid(const uint8_t *src, size_t len, bt_uuid_t *uuid)
+{
+	uint128_t u128;
+
+	if (len == 2) {
+		bt_uuid16_create(uuid, get_le16(src));
+		return true;
+	}
+
+	if (len == 4) {
+		bt_uuid32_create(uuid, get_le32(src));
+		return true;
+	}
+
+	if (len != 16)
+		return false;
+
+	bswap_128(src, &u128);
+	bt_uuid128_create(uuid, u128);
+
+	return true;
+}
+
+static struct gatt_db_service *gatt_db_service_create(const bt_uuid_t *uuid,
+							uint16_t handle,
+							bool primary,
+							uint16_t num_handles)
+{
+	struct gatt_db_service *service;
+	const bt_uuid_t *type;
+	uint8_t value[16];
+	uint16_t len;
+
+	if (num_handles < 1)
+		return NULL;
+
+	service = new0(struct gatt_db_service, 1);
+	service->attributes = new0(struct gatt_db_attribute *, num_handles);
+
+	if (primary)
+		type = &primary_service_uuid;
+	else
+		type = &secondary_service_uuid;
+
+	len = uuid_to_le(uuid, value);
+
+	service->attributes[0] = new_attribute(service, handle, type, value,
+									len);
+	if (!service->attributes[0]) {
+		gatt_db_service_destroy(service);
+		return NULL;
+	}
+
+	return service;
+}
+
+
+bool gatt_db_remove_service(struct gatt_db *db,
+					struct gatt_db_attribute *attrib)
+{
+	struct gatt_db_service *service;
+
+	if (!db || !attrib)
+		return false;
+
+	service = attrib->service;
+
+	queue_remove(db->services, service);
+
+	gatt_db_service_destroy(service);
+
+	return true;
+}
+
+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;
+}
+
+static void gatt_db_service_get_handles(const struct gatt_db_service *service,
+							uint16_t *start_handle,
+							uint16_t *end_handle)
+{
+	if (start_handle)
+		*start_handle = service->attributes[0]->handle;
+
+	if (end_handle)
+		*end_handle = service->attributes[0]->handle +
+						service->num_handles - 1;
+}
+
+struct clear_range {
+	uint16_t start, end;
+};
+
+static bool match_range(const void *a, const void *b)
+{
+	const struct gatt_db_service *service = a;
+	const struct clear_range *range = b;
+	uint16_t svc_start, svc_end;
+
+	gatt_db_service_get_handles(service, &svc_start, &svc_end);
+
+	return svc_start <= range->end && svc_end >= range->start;
+}
+
+bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle)
+{
+	struct clear_range range;
+
+	if (!db || start_handle > end_handle)
+		return false;
+
+	range.start = start_handle;
+	range.end = end_handle;
+
+	queue_remove_all(db->services, match_range, &range,
+						gatt_db_service_destroy);
+
+	return true;
+}
+
+static struct gatt_db_service *find_insert_loc(struct gatt_db *db,
+						uint16_t start, uint16_t end,
+						struct gatt_db_service **after)
+{
+	const struct queue_entry *services_entry;
+	struct gatt_db_service *service;
+	uint16_t cur_start, cur_end;
+
+	*after = NULL;
+
+	services_entry = queue_get_entries(db->services);
+
+	while (services_entry) {
+		service = services_entry->data;
+
+		gatt_db_service_get_handles(service, &cur_start, &cur_end);
+
+		if (start >= cur_start && start <= cur_end)
+			return service;
+
+		if (end >= cur_start && end <= cur_end)
+			return service;
+
+		if (end < cur_start)
+			return NULL;
+
+		*after = service;
+		services_entry = services_entry->next;
+	}
+
+	return NULL;
+}
+
+struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
+							uint16_t handle,
+							const bt_uuid_t *uuid,
+							bool primary,
+							uint16_t num_handles)
+{
+	struct gatt_db_service *service, *after;
+
+	after = NULL;
+
+	if (!db || handle < 1)
+		return NULL;
+
+	if (num_handles < 1 || (handle + num_handles - 1) > UINT16_MAX)
+		return NULL;
+
+	service = find_insert_loc(db, handle, handle + num_handles - 1, &after);
+	if (service) {
+		const bt_uuid_t *type;
+		bt_uuid_t value;
+
+		if (primary)
+			type = &primary_service_uuid;
+		else
+			type = &secondary_service_uuid;
+
+		gatt_db_attribute_get_service_uuid(service->attributes[0],
+									&value);
+
+		/* Check if service match */
+		if (!bt_uuid_cmp(&service->attributes[0]->uuid, type) &&
+				!bt_uuid_cmp(&value, uuid) &&
+				service->num_handles == num_handles &&
+				service->attributes[0]->handle == handle)
+			return service->attributes[0];
+
+		return NULL;
+	}
+
+	service = gatt_db_service_create(uuid, handle, primary, num_handles);
+
+	if (!service)
+		return NULL;
+
+	if (after) {
+		if (!queue_push_after(db->services, after, service))
+			goto fail;
+	} else if (!queue_push_head(db->services, service)) {
+		goto fail;
+	}
+
+	service->db = db;
+	service->attributes[0]->handle = handle;
+	service->num_handles = num_handles;
+
+	/* Fast-forward next_handle if the new service was added to the end */
+	db->next_handle = MAX(handle + num_handles, db->next_handle);
+
+	return service->attributes[0];
+
+fail:
+	gatt_db_service_destroy(service);
+	return NULL;
+}
+
+struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
+						const bt_uuid_t *uuid,
+						bool primary,
+						uint16_t num_handles)
+{
+	return gatt_db_insert_service(db, db->next_handle, uuid, primary,
+								num_handles);
+}
+
+unsigned int gatt_db_register(struct gatt_db *db,
+					gatt_db_attribute_cb_t service_added,
+					gatt_db_attribute_cb_t service_removed,
+					void *user_data,
+					gatt_db_destroy_func_t destroy)
+{
+	struct notify *notify;
+
+	if (!db || !(service_added || service_removed))
+		return 0;
+
+	notify = new0(struct notify, 1);
+	notify->service_added = service_added;
+	notify->service_removed = service_removed;
+	notify->destroy = destroy;
+	notify->user_data = user_data;
+
+	if (db->next_notify_id < 1)
+		db->next_notify_id = 1;
+
+	notify->id = db->next_notify_id++;
+
+	if (!queue_push_tail(db->notify_list, notify)) {
+		free(notify);
+		return 0;
+	}
+
+	return notify->id;
+}
+
+bool gatt_db_unregister(struct gatt_db *db, unsigned int id)
+{
+	struct notify *notify;
+
+	if (!db || !id)
+		return false;
+
+	notify = queue_find(db->notify_list, match_notify_id, UINT_TO_PTR(id));
+	if (!notify)
+		return false;
+
+	queue_remove(db->notify_list, notify);
+	notify_destroy(notify);
+
+	return true;
+}
+
+static uint16_t get_attribute_index(struct gatt_db_service *service,
+							int end_offset)
+{
+	int i = 0;
+
+	/* Here we look for first free attribute index with given offset */
+	while (i < (service->num_handles - end_offset) &&
+						service->attributes[i])
+		i++;
+
+	return i == (service->num_handles - end_offset) ? 0 : i;
+}
+
+static uint16_t get_handle_at_index(struct gatt_db_service *service,
+								int index)
+{
+	return service->attributes[index]->handle;
+}
+
+static struct gatt_db_attribute *
+attribute_update(struct gatt_db_service *service, int index)
+{
+	uint16_t previous_handle;
+
+	/* We call this function with index > 0, because index 0 is reserved
+	 * for service declaration, and is set in add_service()
+	 */
+	previous_handle = service->attributes[index - 1]->handle;
+	service->attributes[index]->handle = previous_handle + 1;
+
+	return service->attributes[index];
+}
+
+static void set_attribute_data(struct gatt_db_attribute *attribute,
+						gatt_db_read_t read_func,
+						gatt_db_write_t write_func,
+						uint32_t permissions,
+						void *user_data)
+{
+	attribute->permissions = permissions;
+	attribute->read_func = read_func;
+	attribute->write_func = write_func;
+	attribute->user_data = user_data;
+}
+
+static struct gatt_db_attribute *
+service_insert_characteristic(struct gatt_db_service *service,
+					uint16_t handle,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					uint8_t properties,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data)
+{
+	uint8_t value[MAX_CHAR_DECL_VALUE_LEN];
+	uint16_t len = 0;
+	int i;
+
+	/* Check if handle is in within service range */
+	if (handle && handle <= service->attributes[0]->handle)
+		return NULL;
+
+	/*
+	 * It is not possible to allocate last handle for a Characteristic
+	 * since it would not have space for its value:
+	 * 3.3.2 Characteristic Value Declaration
+	 * The Characteristic Value declaration contains the value of the
+	 * characteristic. It is the first Attribute after the characteristic
+	 * declaration. All characteristic definitions shall have a
+	 * Characteristic Value declaration.
+	 */
+	if (handle == UINT16_MAX)
+		return NULL;
+
+	i = get_attribute_index(service, 1);
+	if (!i)
+		return NULL;
+
+	if (!handle)
+		handle = get_handle_at_index(service, i - 1) + 2;
+
+	value[0] = properties;
+	len += sizeof(properties);
+
+	/* We set handle of characteristic value, which will be added next */
+	put_le16(handle, &value[1]);
+	len += sizeof(uint16_t);
+	len += uuid_to_le(uuid, &value[3]);
+
+	service->attributes[i] = new_attribute(service, handle - 1,
+							&characteristic_uuid,
+							value, len);
+	if (!service->attributes[i])
+		return NULL;
+
+	i++;
+
+	service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
+	if (!service->attributes[i]) {
+		free(service->attributes[i - 1]);
+		return NULL;
+	}
+
+	set_attribute_data(service->attributes[i], read_func, write_func,
+							permissions, user_data);
+
+	return service->attributes[i];
+}
+
+struct gatt_db_attribute *
+gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
+					uint16_t handle,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					uint8_t properties,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data)
+{
+	if (!attrib || !handle)
+		return NULL;
+
+	return service_insert_characteristic(attrib->service, handle, uuid,
+						permissions, properties,
+						read_func, write_func,
+						user_data);
+}
+
+struct gatt_db_attribute *
+gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					uint8_t properties,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data)
+{
+	if (!attrib)
+		return NULL;
+
+	return service_insert_characteristic(attrib->service, 0, uuid,
+						permissions, properties,
+						read_func, write_func,
+						user_data);
+}
+
+static struct gatt_db_attribute *
+service_insert_descriptor(struct gatt_db_service *service,
+					uint16_t handle,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data)
+{
+	int i;
+
+	i = get_attribute_index(service, 0);
+	if (!i)
+		return NULL;
+
+	/* Check if handle is in within service range */
+	if (handle && handle <= service->attributes[0]->handle)
+		return NULL;
+
+	if (!handle)
+		handle = get_handle_at_index(service, i - 1) + 1;
+
+	service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
+	if (!service->attributes[i])
+		return NULL;
+
+	set_attribute_data(service->attributes[i], read_func, write_func,
+							permissions, user_data);
+
+	return service->attributes[i];
+}
+
+struct gatt_db_attribute *
+gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
+					uint16_t handle,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data)
+{
+	if (!attrib || !handle)
+		return NULL;
+
+	return service_insert_descriptor(attrib->service, handle, uuid,
+					permissions, read_func, write_func,
+					user_data);
+}
+
+struct gatt_db_attribute *
+gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data)
+{
+	if (!attrib)
+		return NULL;
+
+	return service_insert_descriptor(attrib->service, 0, uuid,
+					permissions, read_func, write_func,
+					user_data);
+}
+
+struct gatt_db_attribute *
+gatt_db_service_add_included(struct gatt_db_attribute *attrib,
+					struct gatt_db_attribute *include)
+{
+	struct gatt_db_service *service, *included;
+	uint8_t value[MAX_INCLUDED_VALUE_LEN];
+	uint16_t included_handle, len = 0;
+	int index;
+
+	if (!attrib || !include)
+		return NULL;
+
+	service = attrib->service;
+	included = include->service;
+
+	/* Adjust include to point to the first attribute */
+	if (include != included->attributes[0])
+		include = included->attributes[0];
+
+	included_handle = include->handle;
+
+	put_le16(included_handle, &value[len]);
+	len += sizeof(uint16_t);
+
+	put_le16(included_handle + included->num_handles - 1, &value[len]);
+	len += sizeof(uint16_t);
+
+	/* The Service UUID shall only be present when the UUID is a 16-bit
+	 * Bluetooth UUID. Vol 2. Part G. 3.2
+	 */
+	if (include->value_len == sizeof(uint16_t)) {
+		memcpy(&value[len], include->value, include->value_len);
+		len += include->value_len;
+	}
+
+	index = get_attribute_index(service, 0);
+	if (!index)
+		return NULL;
+
+	service->attributes[index] = new_attribute(service, 0,
+							&included_service_uuid,
+							value, len);
+	if (!service->attributes[index])
+		return NULL;
+
+	/* The Attribute Permissions shall be read only and not require
+	 * authentication or authorization. Vol 2. Part G. 3.2
+	 *
+	 * TODO handle permissions
+	 */
+	set_attribute_data(service->attributes[index], NULL, NULL, 0, NULL);
+
+	return attribute_update(service, index);
+}
+
+bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active)
+{
+	struct gatt_db_service *service;
+
+	if (!attrib)
+		return false;
+
+	service = attrib->service;
+
+	if (service->active == active)
+		return true;
+
+	service->active = active;
+
+	notify_service_changed(service->db, service, active);
+
+	return true;
+}
+
+bool gatt_db_service_get_active(struct gatt_db_attribute *attrib)
+{
+	if (!attrib)
+		return false;
+
+	return attrib->service->active;
+}
+
+bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib,
+								bool claimed)
+{
+	if (!attrib)
+		return false;
+
+	attrib->service->claimed = claimed;
+
+	return true;
+}
+
+bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib)
+{
+	if (!attrib)
+		return false;
+
+	return attrib->service->claimed;
+}
+
+void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle,
+							const bt_uuid_t type,
+							struct queue *queue)
+{
+	const struct queue_entry *services_entry;
+	struct gatt_db_service *service;
+	uint16_t grp_start, grp_end, uuid_size;
+
+	uuid_size = 0;
+
+	services_entry = queue_get_entries(db->services);
+
+	while (services_entry) {
+		service = services_entry->data;
+
+		if (!service->active)
+			goto next_service;
+
+		if (bt_uuid_cmp(&type, &service->attributes[0]->uuid))
+			goto next_service;
+
+		grp_start = service->attributes[0]->handle;
+		grp_end = grp_start + service->num_handles - 1;
+
+		if (grp_end < start_handle || grp_start > end_handle)
+			goto next_service;
+
+		if (grp_start < start_handle || grp_start > end_handle)
+			goto next_service;
+
+		if (!uuid_size)
+			uuid_size = service->attributes[0]->value_len;
+		else if (uuid_size != service->attributes[0]->value_len)
+			return;
+
+		queue_push_tail(queue, service->attributes[0]);
+
+next_service:
+		services_entry = services_entry->next;
+	}
+}
+
+struct find_by_type_value_data {
+	bt_uuid_t uuid;
+	uint16_t start_handle;
+	uint16_t end_handle;
+	gatt_db_attribute_cb_t func;
+	void *user_data;
+	const void *value;
+	size_t value_len;
+	unsigned int num_of_res;
+};
+
+static void find_by_type(void *data, void *user_data)
+{
+	struct find_by_type_value_data *search_data = user_data;
+	struct gatt_db_service *service = data;
+	struct gatt_db_attribute *attribute;
+	int i;
+
+	if (!service->active)
+		return;
+
+	for (i = 0; i < service->num_handles; i++) {
+		attribute = service->attributes[i];
+
+		if (!attribute)
+			continue;
+
+		if ((attribute->handle < search_data->start_handle) ||
+				(attribute->handle > search_data->end_handle))
+			continue;
+
+		if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
+			continue;
+
+		/* TODO: fix for read-callback based attributes */
+		if (search_data->value && memcmp(attribute->value,
+							search_data->value,
+							search_data->value_len))
+			continue;
+
+		search_data->num_of_res++;
+		search_data->func(attribute, search_data->user_data);
+	}
+}
+
+unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
+						uint16_t end_handle,
+						const bt_uuid_t *type,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	struct find_by_type_value_data data;
+
+	memset(&data, 0, sizeof(data));
+
+	data.uuid = *type;
+	data.start_handle = start_handle;
+	data.end_handle = end_handle;
+	data.func = func;
+	data.user_data = user_data;
+
+	queue_foreach(db->services, find_by_type, &data);
+
+	return data.num_of_res;
+}
+
+unsigned int gatt_db_find_by_type_value(struct gatt_db *db,
+						uint16_t start_handle,
+						uint16_t end_handle,
+						const bt_uuid_t *type,
+						const void *value,
+						size_t value_len,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	struct find_by_type_value_data data;
+
+	data.uuid = *type;
+	data.start_handle = start_handle;
+	data.end_handle = end_handle;
+	data.func = func;
+	data.user_data = user_data;
+	data.value = value;
+	data.value_len = value_len;
+
+	queue_foreach(db->services, find_by_type, &data);
+
+	return data.num_of_res;
+}
+
+struct read_by_type_data {
+	struct queue *queue;
+	bt_uuid_t uuid;
+	uint16_t start_handle;
+	uint16_t end_handle;
+};
+
+static void read_by_type(void *data, void *user_data)
+{
+	struct read_by_type_data *search_data = user_data;
+	struct gatt_db_service *service = data;
+	struct gatt_db_attribute *attribute;
+	int i;
+
+	if (!service->active)
+		return;
+
+	for (i = 0; i < service->num_handles; i++) {
+		attribute = service->attributes[i];
+		if (!attribute)
+			continue;
+
+		if (attribute->handle < search_data->start_handle)
+			continue;
+
+		if (attribute->handle > search_data->end_handle)
+			return;
+
+		if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
+			continue;
+
+		queue_push_tail(search_data->queue, attribute);
+	}
+}
+
+void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
+						uint16_t end_handle,
+						const bt_uuid_t type,
+						struct queue *queue)
+{
+	struct read_by_type_data data;
+	data.uuid = type;
+	data.start_handle = start_handle;
+	data.end_handle = end_handle;
+	data.queue = queue;
+
+	queue_foreach(db->services, read_by_type, &data);
+}
+
+
+struct find_information_data {
+	struct queue *queue;
+	uint16_t start_handle;
+	uint16_t end_handle;
+};
+
+static void find_information(void *data, void *user_data)
+{
+	struct find_information_data *search_data = user_data;
+	struct gatt_db_service *service = data;
+	struct gatt_db_attribute *attribute;
+	int i;
+
+	if (!service->active)
+		return;
+
+	/* Check if service is in range */
+	if ((service->attributes[0]->handle + service->num_handles - 1) <
+						search_data->start_handle)
+		return;
+
+	for (i = 0; i < service->num_handles; i++) {
+		attribute = service->attributes[i];
+		if (!attribute)
+			continue;
+
+		if (attribute->handle < search_data->start_handle)
+			continue;
+
+		if (attribute->handle > search_data->end_handle)
+			return;
+
+		queue_push_tail(search_data->queue, attribute);
+	}
+}
+
+void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle,
+							struct queue *queue)
+{
+	struct find_information_data data;
+
+	data.start_handle = start_handle;
+	data.end_handle = end_handle;
+	data.queue = queue;
+
+	queue_foreach(db->services, find_information, &data);
+}
+
+void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	gatt_db_foreach_service_in_range(db, uuid, func, user_data, 0x0001,
+									0xffff);
+}
+
+struct foreach_data {
+	gatt_db_attribute_cb_t func;
+	const bt_uuid_t *uuid;
+	void *user_data;
+	uint16_t start, end;
+};
+
+static void foreach_service_in_range(void *data, void *user_data)
+{
+	struct gatt_db_service *service = data;
+	struct foreach_data *foreach_data = user_data;
+	uint16_t svc_start;
+	bt_uuid_t uuid;
+
+	svc_start = get_handle_at_index(service, 0);
+
+	if (svc_start > foreach_data->end || svc_start < foreach_data->start)
+		return;
+
+	if (foreach_data->uuid) {
+		gatt_db_attribute_get_service_uuid(service->attributes[0],
+									&uuid);
+		if (bt_uuid_cmp(&uuid, foreach_data->uuid))
+			return;
+	}
+
+	foreach_data->func(service->attributes[0], foreach_data->user_data);
+}
+
+void gatt_db_foreach_service_in_range(struct gatt_db *db,
+						const bt_uuid_t *uuid,
+						gatt_db_attribute_cb_t func,
+						void *user_data,
+						uint16_t start_handle,
+						uint16_t end_handle)
+{
+	struct foreach_data data;
+
+	if (!db || !func || start_handle > end_handle)
+		return;
+
+	data.func = func;
+	data.uuid = uuid;
+	data.user_data = user_data;
+	data.start = start_handle;
+	data.end = end_handle;
+
+	queue_foreach(db->services, foreach_service_in_range, &data);
+}
+
+void gatt_db_service_foreach(struct gatt_db_attribute *attrib,
+						const bt_uuid_t *uuid,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *attr;
+	uint16_t i;
+
+	if (!attrib || !func)
+		return;
+
+	service = attrib->service;
+
+	for (i = 0; i < service->num_handles; i++) {
+		attr = service->attributes[i];
+		if (!attr)
+			continue;
+
+		if (uuid && bt_uuid_cmp(uuid, &attr->uuid))
+			continue;
+
+		func(attr, user_data);
+	}
+}
+
+void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	gatt_db_service_foreach(attrib, &characteristic_uuid, func, user_data);
+}
+
+void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *attr;
+	uint16_t i;
+
+	if (!attrib || !func)
+		return;
+
+	/* Return if this attribute is not a characteristic declaration */
+	if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
+		return;
+
+	service = attrib->service;
+
+	/* Start from the attribute following the value handle */
+	for (i = 0; i < service->num_handles; i++) {
+		if (service->attributes[i] == attrib) {
+			i += 2;
+			break;
+		}
+	}
+
+	for (; i < service->num_handles; i++) {
+		attr = service->attributes[i];
+		if (!attr)
+			continue;
+
+		/* Return if we reached the end of this characteristic */
+		if (!bt_uuid_cmp(&characteristic_uuid, &attr->uuid) ||
+			!bt_uuid_cmp(&included_service_uuid, &attr->uuid))
+			return;
+
+		func(attr, user_data);
+	}
+}
+
+void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib,
+						gatt_db_attribute_cb_t func,
+						void *user_data)
+{
+	gatt_db_service_foreach(attrib, &included_service_uuid, func,
+								user_data);
+}
+
+static bool find_service_for_handle(const void *data, const void *user_data)
+{
+	const struct gatt_db_service *service = data;
+	uint16_t handle = PTR_TO_UINT(user_data);
+	uint16_t start, end;
+
+	gatt_db_service_get_handles(service, &start, &end);
+
+	return (start <= handle) && (handle <= end);
+}
+
+struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
+							uint16_t handle)
+{
+	struct gatt_db_service *service;
+	int i;
+
+	if (!db || !handle)
+		return NULL;
+
+	service = queue_find(db->services, find_service_for_handle,
+							UINT_TO_PTR(handle));
+	if (!service)
+		return NULL;
+
+	for (i = 0; i < service->num_handles; i++) {
+		if (!service->attributes[i])
+			continue;
+
+		if (service->attributes[i]->handle == handle)
+			return service->attributes[i];
+	}
+
+	return NULL;
+}
+
+static bool find_service_with_uuid(const void *data, const void *user_data)
+{
+	const struct gatt_db_service *service = data;
+	const bt_uuid_t *uuid = user_data;
+	bt_uuid_t svc_uuid;
+
+	gatt_db_attribute_get_service_uuid(service->attributes[0], &svc_uuid);
+
+	return bt_uuid_cmp(uuid, &svc_uuid) == 0;
+}
+
+struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db,
+							const bt_uuid_t *uuid)
+{
+	struct gatt_db_service *service;
+
+	if (!db || !uuid)
+		return NULL;
+
+	service = queue_find(db->services, find_service_with_uuid, uuid);
+	if (!service)
+		return NULL;
+
+	return service->attributes[0];
+}
+
+const bt_uuid_t *gatt_db_attribute_get_type(
+					const struct gatt_db_attribute *attrib)
+{
+	if (!attrib)
+		return NULL;
+
+	return &attrib->uuid;
+}
+
+uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib)
+{
+	if (!attrib)
+		return 0;
+
+	return attrib->handle;
+}
+
+bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib,
+							bt_uuid_t *uuid)
+{
+	struct gatt_db_service *service;
+
+	if (!attrib || !uuid)
+		return false;
+
+	service = attrib->service;
+
+	if (service->attributes[0]->value_len == sizeof(uint16_t)) {
+		uint16_t value;
+
+		value = get_le16(service->attributes[0]->value);
+		bt_uuid16_create(uuid, value);
+
+		return true;
+	}
+
+	if (service->attributes[0]->value_len == sizeof(uint128_t)) {
+		uint128_t value;
+
+		bswap_128(service->attributes[0]->value, &value);
+		bt_uuid128_create(uuid, value);
+
+		return true;
+	}
+
+	return false;
+}
+
+bool gatt_db_attribute_get_service_handles(
+					const struct gatt_db_attribute *attrib,
+					uint16_t *start_handle,
+					uint16_t *end_handle)
+{
+	struct gatt_db_service *service;
+
+	if (!attrib)
+		return false;
+
+	service = attrib->service;
+
+	gatt_db_service_get_handles(service, start_handle, end_handle);
+
+	return true;
+}
+
+bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib,
+							uint16_t *start_handle,
+							uint16_t *end_handle,
+							bool *primary,
+							bt_uuid_t *uuid)
+{
+	struct gatt_db_service *service;
+	struct gatt_db_attribute *decl;
+
+	if (!attrib)
+		return false;
+
+	service = attrib->service;
+	decl = service->attributes[0];
+
+	gatt_db_service_get_handles(service, start_handle, end_handle);
+
+	if (primary)
+		*primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid);
+
+	if (!uuid)
+		return true;
+
+	/*
+	 * The service declaration attribute value is the 16 or 128 bit service
+	 * UUID.
+	 */
+	return le_to_uuid(decl->value, decl->value_len, uuid);
+}
+
+bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *value_handle,
+							uint8_t *properties,
+							bt_uuid_t *uuid)
+{
+	if (!attrib)
+		return false;
+
+	if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
+		return false;
+
+	/*
+	 * Characteristic declaration value:
+	 * 1 octet: Characteristic properties
+	 * 2 octets: Characteristic value handle
+	 * 2 or 16 octets: characteristic UUID
+	 */
+	if (!attrib->value || (attrib->value_len != 5 &&
+						attrib->value_len != 19))
+		return false;
+
+	if (handle)
+		*handle = attrib->handle;
+
+	if (properties)
+		*properties = attrib->value[0];
+
+	if (value_handle)
+		*value_handle = get_le16(attrib->value + 1);
+
+	if (!uuid)
+		return true;
+
+	return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid);
+}
+
+bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *start_handle,
+							uint16_t *end_handle)
+{
+	if (!attrib)
+		return false;
+
+	if (bt_uuid_cmp(&included_service_uuid, &attrib->uuid))
+		return false;
+
+	/*
+	 * Include definition value:
+	 * 2 octets: start handle of included service
+	 * 2 octets: end handle of included service
+	 * optional 2 octets: 16-bit Bluetooth UUID
+	 */
+	if (!attrib->value || attrib->value_len < 4 || attrib->value_len > 6)
+		return false;
+
+	/*
+	 * We only return the handles since the UUID can be easily obtained
+	 * from the corresponding attribute.
+	 */
+	if (handle)
+		*handle = attrib->handle;
+
+	if (start_handle)
+		*start_handle = get_le16(attrib->value);
+
+	if (end_handle)
+		*end_handle = get_le16(attrib->value + 2);
+
+	return true;
+}
+
+uint32_t
+gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib)
+{
+	if (!attrib)
+		return 0;
+
+	return attrib->permissions;
+}
+
+static bool read_timeout(void *user_data)
+{
+	struct pending_read *p = user_data;
+
+	p->timeout_id = 0;
+
+	queue_remove(p->attrib->pending_reads, p);
+
+	pending_read_result(p, -ETIMEDOUT, NULL, 0);
+
+	return false;
+}
+
+bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
+				uint8_t opcode, struct bt_att *att,
+				gatt_db_attribute_read_t func, void *user_data)
+{
+	uint8_t *value;
+
+	if (!attrib || !func)
+		return false;
+
+	if (attrib->read_func) {
+		struct pending_read *p;
+
+		p = new0(struct pending_read, 1);
+		p->attrib = attrib;
+		p->id = ++attrib->read_id;
+		p->timeout_id = timeout_add(ATTRIBUTE_TIMEOUT, read_timeout,
+								p, NULL);
+		p->func = func;
+		p->user_data = user_data;
+
+		queue_push_tail(attrib->pending_reads, p);
+
+		attrib->read_func(attrib, p->id, offset, opcode, att,
+							attrib->user_data);
+		return true;
+	}
+
+	/* Check boundary if value is stored in the db */
+	if (offset > attrib->value_len) {
+		func(attrib, BT_ATT_ERROR_INVALID_OFFSET, NULL, 0, user_data);
+		return true;
+	}
+
+	/* Guard against invalid access if offset equals to value length */
+	value = offset == attrib->value_len ? NULL : &attrib->value[offset];
+
+	func(attrib, 0, value, attrib->value_len - offset, user_data);
+
+	return true;
+}
+
+static bool find_pending(const void *a, const void *b)
+{
+	const struct pending_read *p = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return p->id == id;
+}
+
+bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib,
+					unsigned int id, int err,
+					const uint8_t *value, size_t length)
+{
+	struct pending_read *p;
+
+	if (!attrib || !id)
+		return false;
+
+	p = queue_remove_if(attrib->pending_reads, find_pending,
+							UINT_TO_PTR(id));
+	if (!p)
+		return false;
+
+	pending_read_result(p, err, value, length);
+
+	return true;
+}
+
+static bool write_timeout(void *user_data)
+{
+	struct pending_write *p = user_data;
+
+	p->timeout_id = 0;
+
+	queue_remove(p->attrib->pending_writes, p);
+
+	pending_write_result(p, -ETIMEDOUT);
+
+	return false;
+}
+
+bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					gatt_db_attribute_write_t func,
+					void *user_data)
+{
+	if (!attrib || !func)
+		return false;
+
+	if (attrib->write_func) {
+		struct pending_write *p;
+
+		p = new0(struct pending_write, 1);
+		p->attrib = attrib;
+		p->id = ++attrib->write_id;
+		p->timeout_id = timeout_add(ATTRIBUTE_TIMEOUT, write_timeout,
+								p, NULL);
+		p->func = func;
+		p->user_data = user_data;
+
+		queue_push_tail(attrib->pending_writes, p);
+
+		attrib->write_func(attrib, p->id, offset, value, len, opcode,
+							att, attrib->user_data);
+		return true;
+	}
+
+	/* Nothing to write just skip */
+	if (len == 0)
+		goto done;
+
+	/* For values stored in db allocate on demand */
+	if (!attrib->value || offset >= attrib->value_len ||
+				len > (unsigned) (attrib->value_len - offset)) {
+		void *buf;
+
+		buf = realloc(attrib->value, len + offset);
+		if (!buf)
+			return false;
+
+		attrib->value = buf;
+
+		/* Init data in the first allocation */
+		if (!attrib->value_len)
+			memset(attrib->value, 0, offset);
+
+		attrib->value_len = len + offset;
+	}
+
+	memcpy(&attrib->value[offset], value, len);
+
+done:
+	func(attrib, 0, user_data);
+
+	return true;
+}
+
+bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib,
+						unsigned int id, int err)
+{
+	struct pending_write *p;
+
+	if (!attrib || !id)
+		return false;
+
+	p = queue_remove_if(attrib->pending_writes, find_pending,
+							UINT_TO_PTR(id));
+	if (!p)
+		return false;
+
+	pending_write_result(p, err);
+
+	return true;
+}
+
+bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib)
+{
+	if (!attrib)
+		return false;
+
+	if (!attrib->value || !attrib->value_len)
+		return true;
+
+	free(attrib->value);
+	attrib->value = NULL;
+	attrib->value_len = 0;
+
+	return true;
+}
diff --git a/bluez/repo/src/shared/gatt-db.h b/bluez/repo/src/shared/gatt-db.h
new file mode 100644
index 0000000..96cceb9
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-db.h
@@ -0,0 +1,236 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 gatt_db;
+struct gatt_db_attribute;
+
+struct gatt_db *gatt_db_new(void);
+
+struct gatt_db *gatt_db_ref(struct gatt_db *db);
+void gatt_db_unref(struct gatt_db *db);
+
+bool gatt_db_isempty(struct gatt_db *db);
+
+struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db,
+						const bt_uuid_t *uuid,
+						bool primary,
+						uint16_t num_handles);
+
+bool gatt_db_remove_service(struct gatt_db *db,
+					struct gatt_db_attribute *attrib);
+bool gatt_db_clear(struct gatt_db *db);
+bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle);
+
+struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
+							uint16_t handle,
+							const bt_uuid_t *uuid,
+							bool primary,
+							uint16_t num_handles);
+
+typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data);
+
+typedef void (*gatt_db_write_t) (struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data);
+
+struct gatt_db_attribute *
+gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					uint8_t properties,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data);
+struct gatt_db_attribute *
+gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
+					uint16_t handle,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					uint8_t properties,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data);
+
+struct gatt_db_attribute *
+gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data);
+struct gatt_db_attribute *
+gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
+					uint16_t handle,
+					const bt_uuid_t *uuid,
+					uint32_t permissions,
+					gatt_db_read_t read_func,
+					gatt_db_write_t write_func,
+					void *user_data);
+
+struct gatt_db_attribute *
+gatt_db_service_add_included(struct gatt_db_attribute *attrib,
+					struct gatt_db_attribute *include);
+
+bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active);
+bool gatt_db_service_get_active(struct gatt_db_attribute *attrib);
+
+bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib,
+								bool claimed);
+bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib);
+
+typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib,
+							void *user_data);
+
+void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle,
+							const bt_uuid_t type,
+							struct queue *queue);
+
+unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
+						uint16_t end_handle,
+						const bt_uuid_t *type,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+
+unsigned int gatt_db_find_by_type_value(struct gatt_db *db,
+						uint16_t start_handle,
+						uint16_t end_handle,
+						const bt_uuid_t *type,
+						const void *value,
+						size_t value_len,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+
+void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle,
+							const bt_uuid_t type,
+							struct queue *queue);
+
+void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
+							uint16_t end_handle,
+							struct queue *queue);
+
+
+void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+void gatt_db_foreach_service_in_range(struct gatt_db *db,
+						const bt_uuid_t *uuid,
+						gatt_db_attribute_cb_t func,
+						void *user_data,
+						uint16_t start_handle,
+						uint16_t end_handle);
+
+void gatt_db_service_foreach(struct gatt_db_attribute *attrib,
+						const bt_uuid_t *uuid,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+void gatt_db_service_foreach_char(struct gatt_db_attribute *attrib,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+void gatt_db_service_foreach_incl(struct gatt_db_attribute *attrib,
+						gatt_db_attribute_cb_t func,
+						void *user_data);
+
+typedef void (*gatt_db_destroy_func_t)(void *user_data);
+
+unsigned int gatt_db_register(struct gatt_db *db,
+					gatt_db_attribute_cb_t service_added,
+					gatt_db_attribute_cb_t service_removed,
+					void *user_data,
+					gatt_db_destroy_func_t destroy);
+bool gatt_db_unregister(struct gatt_db *db, unsigned int id);
+
+struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
+							uint16_t handle);
+
+struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db,
+							const bt_uuid_t *uuid);
+
+const bt_uuid_t *gatt_db_attribute_get_type(
+					const struct gatt_db_attribute *attrib);
+
+uint16_t gatt_db_attribute_get_handle(const struct gatt_db_attribute *attrib);
+
+bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib,
+							bt_uuid_t *uuid);
+
+bool gatt_db_attribute_get_service_handles(
+					const struct gatt_db_attribute *attrib,
+					uint16_t *start_handle,
+					uint16_t *end_handle);
+
+bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib,
+							uint16_t *start_handle,
+							uint16_t *end_handle,
+							bool *primary,
+							bt_uuid_t *uuid);
+
+bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *value_handle,
+							uint8_t *properties,
+							bt_uuid_t *uuid);
+
+bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
+							uint16_t *handle,
+							uint16_t *start_handle,
+							uint16_t *end_handle);
+
+uint32_t
+gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib);
+
+typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib,
+						int err, const uint8_t *value,
+						size_t length, void *user_data);
+
+bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
+				uint8_t opcode, struct bt_att *att,
+				gatt_db_attribute_read_t func, void *user_data);
+
+bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib,
+					unsigned int id, int err,
+					const uint8_t *value, size_t length);
+
+typedef void (*gatt_db_attribute_write_t) (struct gatt_db_attribute *attrib,
+						int err, void *user_data);
+
+bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					gatt_db_attribute_write_t func,
+					void *user_data);
+
+bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib,
+						unsigned int id, int err);
+
+bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
diff --git a/bluez/repo/src/shared/gatt-helpers.c b/bluez/repo/src/shared/gatt-helpers.c
new file mode 100644
index 0000000..a0a5b26
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-helpers.c
@@ -0,0 +1,1514 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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 "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/gatt-helpers.h"
+#include "src/shared/util.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+struct bt_gatt_result {
+	uint8_t opcode;
+	void *pdu;
+	uint16_t pdu_len;
+	uint16_t data_len;
+
+	void *op;  /* Discovery operation data */
+
+	struct bt_gatt_result *next;
+};
+
+static struct bt_gatt_result *result_create(uint8_t opcode, const void *pdu,
+							uint16_t pdu_len,
+							uint16_t data_len,
+							void *op)
+{
+	struct bt_gatt_result *result;
+
+	result = new0(struct bt_gatt_result, 1);
+	result->pdu = malloc(pdu_len);
+	if (!result->pdu) {
+		free(result);
+		return NULL;
+	}
+
+	result->opcode = opcode;
+	result->pdu_len = pdu_len;
+	result->data_len = data_len;
+	result->op = op;
+
+	memcpy(result->pdu, pdu, pdu_len);
+
+	return result;
+}
+
+static void result_destroy(struct bt_gatt_result *result)
+{
+	struct bt_gatt_result *next;
+
+	while (result) {
+		next = result->next;
+
+		free(result->pdu);
+		free(result);
+
+		result = next;
+	}
+}
+
+static unsigned int result_element_count(struct bt_gatt_result *result)
+{
+	unsigned int count = 0;
+	struct bt_gatt_result *cur;
+
+	cur = result;
+
+	while (cur) {
+		count += cur->pdu_len / cur->data_len;
+		cur = cur->next;
+	}
+
+	return count;
+}
+
+unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result)
+{
+	if (!result)
+		return 0;
+
+	if (result->opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP &&
+			result->opcode != BT_ATT_OP_FIND_BY_TYPE_RSP)
+		return 0;
+
+	return result_element_count(result);
+}
+
+unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result)
+{
+	if (!result)
+		return 0;
+
+	if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+		return 0;
+
+	/*
+	 * Data length contains 7 or 21 octets:
+	 * 2 octets: Attribute handle
+	 * 1 octet: Characteristic properties
+	 * 2 octets: Characteristic value handle
+	 * 2 or 16 octets: characteristic UUID
+	 */
+	if (result->data_len != 21 && result->data_len != 7)
+		return 0;
+
+	return result_element_count(result);
+}
+
+unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result)
+{
+	if (!result)
+		return 0;
+
+	if (result->opcode != BT_ATT_OP_FIND_INFO_RSP)
+		return 0;
+
+	return result_element_count(result);
+}
+
+unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result)
+{
+	struct bt_gatt_result *cur;
+	unsigned int count = 0;
+
+	if (!result)
+		return 0;
+
+	if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+		return 0;
+
+	/*
+	 * Data length can be of length 6 or 8 octets:
+	 * 2 octets - include service handle
+	 * 2 octets - start handle of included service
+	 * 2 octets - end handle of included service
+	 * 2 octets (optionally) - 16 bit Bluetooth UUID
+	 */
+	if (result->data_len != 6 && result->data_len != 8)
+		return 0;
+
+	for (cur = result; cur; cur = cur->next)
+		if (cur->opcode == BT_ATT_OP_READ_BY_TYPE_RSP)
+			count += cur->pdu_len / cur->data_len;
+
+	return count;
+}
+
+bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result)
+{
+	if (!iter || !result)
+		return false;
+
+	iter->result = result;
+	iter->pos = 0;
+
+	return true;
+}
+
+static const uint8_t bt_base_uuid[16] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+};
+
+static bool convert_uuid_le(const uint8_t *src, size_t len, uint8_t dst[16])
+{
+	if (len == 16) {
+		bswap_128(src, dst);
+		return true;
+	}
+
+	if (len != 2)
+		return false;
+
+	memcpy(dst, bt_base_uuid, sizeof(bt_base_uuid));
+	dst[2] = src[1];
+	dst[3] = src[0];
+
+	return true;
+}
+
+struct bt_gatt_request {
+	struct bt_att *att;
+	unsigned int id;
+	uint16_t start_handle;
+	uint16_t end_handle;
+	int ref_count;
+	bt_uuid_t uuid;
+	uint16_t service_type;
+	struct bt_gatt_result *result_head;
+	struct bt_gatt_result *result_tail;
+	bt_gatt_request_callback_t callback;
+	void *user_data;
+	bt_gatt_destroy_func_t destroy;
+};
+
+static struct bt_gatt_result *result_append(uint8_t opcode, const void *pdu,
+						uint16_t pdu_len,
+						uint16_t data_len,
+						struct bt_gatt_request *op)
+{
+	struct bt_gatt_result *result;
+
+	result = result_create(opcode, pdu, pdu_len, data_len, op);
+	if (!result)
+		return NULL;
+
+	if (!op->result_head)
+		op->result_head = op->result_tail = result;
+	else {
+		op->result_tail->next = result;
+		op->result_tail = result;
+	}
+
+	return result;
+}
+
+bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
+				uint16_t *handle, uint16_t *start_handle,
+				uint16_t *end_handle, uint8_t uuid[16])
+{
+	struct bt_gatt_result *read_result;
+	struct bt_gatt_request *op;
+	const void *pdu_ptr;
+	int i = 0;
+
+	if (!iter || !iter->result || !handle || !start_handle || !end_handle
+								|| !uuid)
+		return false;
+
+
+	if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+		return false;
+
+	/* UUID in discovery_op is set in read_by_type and service_discovery */
+	op = iter->result->op;
+	if (op->uuid.type != BT_UUID_UNSPEC)
+		return false;
+	/*
+	 * iter->result points to READ_BY_TYPE_RSP with data length containing:
+	 * 2 octets - include service handle
+	 * 2 octets - start handle of included service
+	 * 2 octets - end handle of included service
+	 * optional 2  octets - Bluetooth UUID
+	 */
+	if (iter->result->data_len != 8 && iter->result->data_len != 6)
+		return false;
+
+	pdu_ptr = iter->result->pdu + iter->pos;
+
+	/* This result contains 16 bit UUID */
+	if (iter->result->data_len == 8) {
+		*handle = get_le16(pdu_ptr);
+		*start_handle = get_le16(pdu_ptr + 2);
+		*end_handle = get_le16(pdu_ptr + 4);
+		convert_uuid_le(pdu_ptr + 6, 2, uuid);
+
+		iter->pos += iter->result->data_len;
+
+		if (iter->pos == iter->result->pdu_len) {
+			iter->result = iter->result->next;
+			iter->pos = 0;
+		}
+
+		return true;
+	}
+
+	*handle = get_le16(pdu_ptr);
+	*start_handle = get_le16(pdu_ptr + 2);
+	*end_handle = get_le16(pdu_ptr + 4);
+	read_result = iter->result;
+
+	/*
+	 * Find READ_RSP with include service UUID.
+	 * If number of current data set in READ_BY_TYPE_RSP is n, then we must
+	 * go to n'th PDU next to current item->result
+	 */
+	for (read_result = read_result->next; read_result; i++) {
+		if (i >= (iter->pos / iter->result->data_len))
+			break;
+
+		read_result = read_result->next;
+	}
+
+	if (!read_result)
+		return false;
+
+	convert_uuid_le(read_result->pdu, read_result->data_len, uuid);
+	iter->pos += iter->result->data_len;
+	if (iter->pos == iter->result->pdu_len) {
+		iter->result = read_result->next;
+		iter->pos = 0;
+	}
+
+	return true;
+}
+
+bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
+				uint16_t *start_handle, uint16_t *end_handle,
+				uint8_t uuid[16])
+{
+	struct bt_gatt_request *op;
+	const void *pdu_ptr;
+	bt_uuid_t tmp;
+
+	if (!iter || !iter->result || !start_handle || !end_handle || !uuid)
+		return false;
+
+	op = iter->result->op;
+	pdu_ptr = iter->result->pdu + iter->pos;
+
+	switch (iter->result->opcode) {
+	case BT_ATT_OP_READ_BY_GRP_TYPE_RSP:
+		*start_handle = get_le16(pdu_ptr);
+		*end_handle = get_le16(pdu_ptr + 2);
+		convert_uuid_le(pdu_ptr + 4, iter->result->data_len - 4, uuid);
+		break;
+	case BT_ATT_OP_FIND_BY_TYPE_RSP:
+		*start_handle = get_le16(pdu_ptr);
+		*end_handle = get_le16(pdu_ptr + 2);
+
+		bt_uuid_to_uuid128(&op->uuid, &tmp);
+		memcpy(uuid, tmp.value.u128.data, 16);
+		break;
+	default:
+		return false;
+	}
+
+
+	iter->pos += iter->result->data_len;
+	if (iter->pos == iter->result->pdu_len) {
+		iter->result = iter->result->next;
+		iter->pos = 0;
+	}
+
+	return true;
+}
+
+bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
+				uint16_t *start_handle, uint16_t *end_handle,
+				uint16_t *value_handle, uint8_t *properties,
+				uint8_t uuid[16])
+{
+	struct bt_gatt_request *op;
+	const void *pdu_ptr;
+
+	if (!iter || !iter->result || !start_handle || !end_handle ||
+					!value_handle || !properties || !uuid)
+		return false;
+
+	if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+		return false;
+
+	/* UUID in discovery_op is set in read_by_type and service_discovery */
+	op = iter->result->op;
+	if (op->uuid.type != BT_UUID_UNSPEC)
+		return false;
+	/*
+	 * Data length contains 7 or 21 octets:
+	 * 2 octets: Attribute handle
+	 * 1 octet: Characteristic properties
+	 * 2 octets: Characteristic value handle
+	 * 2 or 16 octets: characteristic UUID
+	 */
+	if (iter->result->data_len != 21 && iter->result->data_len != 7)
+		return false;
+
+	pdu_ptr = iter->result->pdu + iter->pos;
+
+	*start_handle = get_le16(pdu_ptr);
+	*properties = ((uint8_t *) pdu_ptr)[2];
+	*value_handle = get_le16(pdu_ptr + 3);
+	convert_uuid_le(pdu_ptr + 5, iter->result->data_len - 5, uuid);
+
+	iter->pos += iter->result->data_len;
+	if (iter->pos == iter->result->pdu_len) {
+		iter->result = iter->result->next;
+		iter->pos = 0;
+	}
+
+	if (!iter->result) {
+		*end_handle = op->end_handle;
+		return true;
+	}
+
+	*end_handle = get_le16(iter->result->pdu + iter->pos) - 1;
+
+	return true;
+}
+
+bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
+							uint8_t uuid[16])
+{
+	const void *pdu_ptr;
+
+	if (!iter || !iter->result || !handle || !uuid)
+		return false;
+
+	if (iter->result->opcode != BT_ATT_OP_FIND_INFO_RSP)
+		return false;
+
+	pdu_ptr = iter->result->pdu + iter->pos;
+
+	*handle = get_le16(pdu_ptr);
+	convert_uuid_le(pdu_ptr + 2, iter->result->data_len - 2, uuid);
+
+	iter->pos += iter->result->data_len;
+	if (iter->pos == iter->result->pdu_len) {
+		iter->result = iter->result->next;
+		iter->pos = 0;
+	}
+
+	return true;
+}
+
+bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter,
+				uint16_t *handle, uint16_t *length,
+				const uint8_t **value)
+{
+	struct bt_gatt_request *op;
+	const void *pdu_ptr;
+
+	if (!iter || !iter->result || !handle || !length || !value)
+		return false;
+
+	if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
+		return false;
+
+	/*
+	 * Check if UUID is set, otherwise results can contain characteristic
+	 * discovery service or included service discovery results
+	 */
+	op = iter->result->op;
+	if (op->uuid.type == BT_UUID_UNSPEC)
+		return false;
+
+	pdu_ptr = iter->result->pdu + iter->pos;
+
+	*handle = get_le16(pdu_ptr);
+	*length = iter->result->data_len - 2;
+	*value = pdu_ptr + 2;
+
+	iter->pos += iter->result->data_len;
+	if (iter->pos == iter->result->pdu_len) {
+		iter->result = iter->result->next;
+		iter->pos = 0;
+	}
+
+	return true;
+}
+
+struct mtu_op {
+	struct bt_att *att;
+	uint16_t client_rx_mtu;
+	bt_gatt_result_callback_t callback;
+	void *user_data;
+	bt_gatt_destroy_func_t destroy;
+};
+
+static void destroy_mtu_op(void *user_data)
+{
+	struct mtu_op *op = user_data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op);
+}
+
+static uint8_t process_error(const void *pdu, uint16_t length)
+{
+	const struct bt_att_pdu_error_rsp *error_pdu;
+
+	if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp))
+		return 0;
+
+	error_pdu = pdu;
+
+	return error_pdu->ecode;
+}
+
+static void mtu_cb(uint8_t opcode, const void *pdu, uint16_t length,
+								void *user_data)
+{
+	struct mtu_op *op = user_data;
+	bool success = true;
+	uint8_t att_ecode = 0;
+	uint16_t server_rx_mtu;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_MTU_RSP || !pdu || length != 2) {
+		success = false;
+		goto done;
+	}
+
+	server_rx_mtu = get_le16(pdu);
+	bt_att_set_mtu(op->att, MIN(op->client_rx_mtu, server_rx_mtu));
+
+done:
+	if (op->callback)
+		op->callback(success, att_ecode, op->user_data);
+}
+
+unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
+					bt_gatt_result_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	struct mtu_op *op;
+	uint8_t pdu[2];
+	unsigned int id;
+
+	if (!att || !client_rx_mtu)
+		return false;
+
+	op = new0(struct mtu_op, 1);
+	op->att = att;
+	op->client_rx_mtu = client_rx_mtu;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+
+	put_le16(client_rx_mtu, pdu);
+
+	id = bt_att_send(att, BT_ATT_OP_MTU_REQ, pdu, sizeof(pdu), mtu_cb, op,
+								destroy_mtu_op);
+	if (!id)
+		free(op);
+
+	return id;
+}
+
+static inline int get_uuid_len(const bt_uuid_t *uuid)
+{
+	if (!uuid)
+		return 0;
+
+	return (uuid->type == BT_UUID16) ? 2 : 16;
+}
+
+struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req)
+{
+	if (!req)
+		return NULL;
+
+	__sync_fetch_and_add(&req->ref_count, 1);
+
+	return req;
+}
+
+void bt_gatt_request_unref(struct bt_gatt_request *req)
+{
+	if (!req)
+		return;
+
+	if (__sync_sub_and_fetch(&req->ref_count, 1))
+		return;
+
+	bt_gatt_request_cancel(req);
+
+	if (req->destroy)
+		req->destroy(req->user_data);
+
+	result_destroy(req->result_head);
+
+	free(req);
+}
+
+void bt_gatt_request_cancel(struct bt_gatt_request *req)
+{
+	if (!req)
+		return;
+
+	if (!req->id)
+		return;
+
+	bt_att_cancel(req->att, req->id);
+	req->id = 0;
+}
+
+static void async_req_unref(void *data)
+{
+	struct bt_gatt_request *req = data;
+
+	bt_gatt_request_unref(req);
+}
+
+static void discovery_op_complete(struct bt_gatt_request *op, bool success,
+								uint8_t ecode)
+{
+	/* Reset success if there is some result to report */
+	if (ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND && op->result_head)
+		success = true;
+
+	if (op->callback)
+		op->callback(success, ecode, success ? op->result_head : NULL,
+								op->user_data);
+
+	if (!op->id)
+		async_req_unref(op);
+	else
+		op->id = 0;
+
+}
+
+static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_request *op = user_data;
+	bool success;
+	uint8_t att_ecode = 0;
+	struct bt_gatt_result *cur_result;
+	size_t data_length;
+	size_t list_length;
+	uint16_t last_end;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	/* PDU must contain at least the following (sans opcode):
+	 * - Attr Data Length (1 octet)
+	 * - Attr Data List (at least 6 octets):
+	 *   -- 2 octets: Attribute handle
+	 *   -- 2 octets: End group handle
+	 *   -- 2 or 16 octets: service UUID
+	 */
+	if (opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP || !pdu || length < 7) {
+		success = false;
+		goto done;
+	}
+
+	data_length = ((uint8_t *) pdu)[0];
+	list_length = length - 1;
+
+	if ((data_length != 6 && data_length != 20) ||
+					(list_length % data_length)) {
+		success = false;
+		goto done;
+	}
+
+	/* PDU is correctly formatted. Get the last end handle to process the
+	 * next request and store the PDU.
+	 */
+	cur_result = result_append(opcode, pdu + 1, list_length, data_length,
+									op);
+	if (!cur_result) {
+		success = false;
+		goto done;
+	}
+
+	last_end = get_le16(pdu + length - data_length + 2);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last_end < op->start_handle) {
+		success = false;
+		goto done;
+	}
+
+	op->start_handle = last_end + 1;
+
+	if (last_end < op->end_handle) {
+		uint8_t pdu[6];
+
+		put_le16(op->start_handle, pdu);
+		put_le16(op->end_handle, pdu + 2);
+		put_le16(op->service_type, pdu + 4);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+						pdu, sizeof(pdu),
+						read_by_grp_type_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto done;
+	}
+
+	/* Some devices incorrectly return 0xffff as the end group handle when
+	 * the read-by-group-type request is performed within a smaller range.
+	 * Manually set the end group handle that we report in the result to the
+	 * end handle in the original request.
+	 */
+	if (last_end == 0xffff && last_end != op->end_handle)
+		put_le16(op->end_handle,
+				cur_result->pdu + length - data_length + 1);
+
+	success = true;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_request *op = user_data;
+	bool success;
+	uint8_t att_ecode = 0;
+	uint16_t last_end;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	/* PDU must contain 4 bytes and it must be a multiple of 4, where each
+	 * 4 bytes contain the 16-bit attribute and group end handles.
+	 */
+	if (opcode != BT_ATT_OP_FIND_BY_TYPE_RSP || !pdu || !length ||
+								length % 4) {
+		success = false;
+		goto done;
+	}
+
+	if (!result_append(opcode, pdu, length, 4, op)) {
+		success = false;
+		goto done;
+	}
+
+	/*
+	 * Each data set contains:
+	 * 2 octets with start handle
+	 * 2 octets with end handle
+	 * last_end is end handle of last data set
+	 */
+	last_end = get_le16(pdu + length - 2);
+
+	/*
+	* If last handle is lower from previous start handle then it is smth
+	* wrong. Let's stop search, otherwise we might enter infinite loop.
+	*/
+	if (last_end < op->start_handle) {
+		success = false;
+		goto done;
+	}
+
+	op->start_handle = last_end + 1;
+
+	if (last_end < op->end_handle) {
+		uint8_t pdu[6 + get_uuid_len(&op->uuid)];
+
+		put_le16(op->start_handle, pdu);
+		put_le16(op->end_handle, pdu + 2);
+		put_le16(op->service_type, pdu + 4);
+		bt_uuid_to_le(&op->uuid, pdu + 6);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_FIND_BY_TYPE_REQ,
+						pdu, sizeof(pdu),
+						find_by_type_val_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto done;
+	}
+
+	success = false;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+static struct bt_gatt_request *discover_services(struct bt_att *att,
+					bt_uuid_t *uuid,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy,
+					bool primary)
+{
+	struct bt_gatt_request *op;
+
+	if (!att)
+		return NULL;
+
+	op = new0(struct bt_gatt_request, 1);
+	op->att = att;
+	op->start_handle = start;
+	op->end_handle = end;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+	/* set service uuid to primary or secondary */
+	op->service_type = primary ? GATT_PRIM_SVC_UUID : GATT_SND_SVC_UUID;
+
+	/* If UUID is NULL, then discover all primary services */
+	if (!uuid) {
+		uint8_t pdu[6];
+
+		put_le16(start, pdu);
+		put_le16(end, pdu + 2);
+		put_le16(op->service_type, pdu + 4);
+
+		op->id = bt_att_send(att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+						pdu, sizeof(pdu),
+						read_by_grp_type_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+	} else {
+		uint8_t pdu[6 + get_uuid_len(uuid)];
+
+		if (uuid->type == BT_UUID_UNSPEC) {
+			free(op);
+			return NULL;
+		}
+
+		/* Discover by UUID */
+		op->uuid = *uuid;
+
+		put_le16(start, pdu);
+		put_le16(end, pdu + 2);
+		put_le16(op->service_type, pdu + 4);
+		bt_uuid_to_le(&op->uuid, pdu + 6);
+
+		op->id = bt_att_send(att, BT_ATT_OP_FIND_BY_TYPE_REQ,
+						pdu, sizeof(pdu),
+						find_by_type_val_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+	}
+
+	if (!op->id) {
+		free(op);
+		return NULL;
+	}
+
+	return bt_gatt_request_ref(op);
+}
+
+struct bt_gatt_request *bt_gatt_discover_all_primary_services(
+					struct bt_att *att, bt_uuid_t *uuid,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	return bt_gatt_discover_primary_services(att, uuid, 0x0001, 0xffff,
+							callback, user_data,
+							destroy);
+}
+
+struct bt_gatt_request *bt_gatt_discover_primary_services(
+					struct bt_att *att, bt_uuid_t *uuid,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	return discover_services(att, uuid, start, end, callback, user_data,
+								destroy, true);
+}
+
+struct bt_gatt_request *bt_gatt_discover_secondary_services(
+					struct bt_att *att, bt_uuid_t *uuid,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	return discover_services(att, uuid, start, end, callback, user_data,
+								destroy, false);
+}
+
+struct read_incl_data {
+	struct bt_gatt_request *op;
+	struct bt_gatt_result *result;
+	int pos;
+	int ref_count;
+};
+
+static struct read_incl_data *new_read_included(struct bt_gatt_result *res)
+{
+	struct read_incl_data *data;
+
+	data = new0(struct read_incl_data, 1);
+	data->op = bt_gatt_request_ref(res->op);
+	data->result = res;
+
+	return data;
+};
+
+static struct read_incl_data *read_included_ref(struct read_incl_data *data)
+{
+	__sync_fetch_and_add(&data->ref_count, 1);
+
+	return data;
+}
+
+static void read_included_unref(void *data)
+{
+	struct read_incl_data *read_data = data;
+
+	if (__sync_sub_and_fetch(&read_data->ref_count, 1))
+		return;
+
+	async_req_unref(read_data->op);
+
+	free(read_data);
+}
+
+static void discover_included_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data);
+
+static void read_included_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct read_incl_data *data = user_data;
+	struct bt_gatt_request *op = data->op;
+	uint8_t att_ecode = 0;
+	uint8_t read_pdu[2];
+	bool success;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) {
+		success = false;
+		goto done;
+	}
+
+	/*
+	 * UUID should be in 128 bit format, as it couldn't be read in
+	 * READ_BY_TYPE request
+	 */
+	if (length != 16) {
+		success = false;
+		goto done;
+	}
+
+	if (!result_append(opcode, pdu, length, length, op)) {
+		success = false;
+		goto done;
+	}
+
+	if (data->pos == data->result->pdu_len) {
+		uint16_t last_handle;
+		uint8_t pdu[6];
+
+		last_handle = get_le16(data->result->pdu + data->pos -
+							data->result->data_len);
+		if (last_handle == op->end_handle) {
+			success = true;
+			goto done;
+		}
+
+		put_le16(last_handle + 1, pdu);
+		put_le16(op->end_handle, pdu + 2);
+		put_le16(GATT_INCLUDE_UUID, pdu + 4);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+						pdu, sizeof(pdu),
+						discover_included_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto done;
+	}
+
+	memcpy(read_pdu, data->result->pdu + data->pos + 2, sizeof(uint16_t));
+
+	data->pos += data->result->data_len;
+
+	if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, read_pdu, sizeof(read_pdu),
+				read_included_cb, read_included_ref(data),
+				read_included_unref))
+		return;
+
+	read_included_unref(data);
+	success = false;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+static void read_included(struct read_incl_data *data)
+{
+	struct bt_gatt_request *op = data->op;
+	uint8_t pdu[2];
+
+	memcpy(pdu, data->result->pdu + 2, sizeof(uint16_t));
+
+	data->pos += data->result->data_len;
+
+	if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
+							read_included_cb,
+							read_included_ref(data),
+							read_included_unref))
+		return;
+
+	if (op->callback)
+		op->callback(false, 0, NULL, data->op->user_data);
+
+	read_included_unref(data);
+}
+
+static void discover_included_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_request *op = user_data;
+	struct bt_gatt_result *cur_result;
+	uint8_t att_ecode = 0;
+	uint16_t last_handle;
+	size_t data_length;
+	bool success;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		att_ecode = process_error(pdu, length);
+		success = false;
+		goto failed;
+	}
+
+	if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 6) {
+		success = false;
+		goto failed;
+	}
+
+	data_length = ((const uint8_t *) pdu)[0];
+
+	/*
+	 * Check if PDU contains data sets with length declared in the beginning
+	 * of frame and if this length is correct.
+	 * Data set length may be 6 or 8 octets:
+	 * 2 octets - include service handle
+	 * 2 octets - start handle of included service
+	 * 2 octets - end handle of included service
+	 * optional 2 octets - Bluetooth UUID of included service
+	 */
+	if ((data_length != 8 && data_length != 6) ||
+						(length - 1) % data_length) {
+		success = false;
+		goto failed;
+	}
+
+	cur_result = result_append(opcode, pdu + 1, length - 1, data_length,
+									op);
+	if (!cur_result) {
+		success = false;
+		goto failed;
+	}
+
+	if (data_length == 6) {
+		struct read_incl_data *data;
+
+		data = new_read_included(cur_result);
+		if (!data) {
+			success = false;
+			goto failed;
+		}
+
+		read_included(data);
+		return;
+	}
+
+	last_handle = get_le16(pdu + length - data_length);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last_handle < op->start_handle) {
+		success = false;
+		goto failed;
+	}
+
+	op->start_handle = last_handle + 1;
+	if (last_handle != op->end_handle) {
+		uint8_t pdu[6];
+
+		put_le16(op->start_handle, pdu);
+		put_le16(op->end_handle, pdu + 2);
+		put_le16(GATT_INCLUDE_UUID, pdu + 4);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+						pdu, sizeof(pdu),
+						discover_included_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto failed;
+	}
+
+	success = true;
+
+failed:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	struct bt_gatt_request *op;
+	uint8_t pdu[6];
+
+	if (!att)
+		return false;
+
+	op = new0(struct bt_gatt_request, 1);
+	op->att = att;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+	op->start_handle = start;
+	op->end_handle = end;
+
+	put_le16(start, pdu);
+	put_le16(end, pdu + 2);
+	put_le16(GATT_INCLUDE_UUID, pdu + 4);
+
+	op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+				discover_included_cb, bt_gatt_request_ref(op),
+				async_req_unref);
+	if (!op->id) {
+		free(op);
+		return NULL;
+	}
+
+	return bt_gatt_request_ref(op);
+}
+
+static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_request *op = user_data;
+	bool success;
+	uint8_t att_ecode = 0;
+	size_t data_length;
+	uint16_t last_handle;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	/* PDU must contain at least the following (sans opcode):
+	 * - Attr Data Length (1 octet)
+	 * - Attr Data List (at least 7 octets):
+	 *   -- 2 octets: Attribute handle
+	 *   -- 1 octet: Characteristic properties
+	 *   -- 2 octets: Characteristic value handle
+	 *   -- 2 or 16 octets: characteristic UUID
+	 */
+	if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 8) {
+		success = false;
+		goto done;
+	}
+
+	data_length = ((uint8_t *) pdu)[0];
+
+	if ((data_length != 7 && data_length != 21) ||
+					((length - 1) % data_length)) {
+		success = false;
+		goto done;
+	}
+
+	if (!result_append(opcode, pdu + 1, length - 1,
+							data_length, op)) {
+		success = false;
+		goto done;
+	}
+	last_handle = get_le16(pdu + length - data_length);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last_handle < op->start_handle) {
+		success = false;
+		goto done;
+	}
+
+	op->start_handle = last_handle + 1;
+
+	if (last_handle != op->end_handle) {
+		uint8_t pdu[6];
+
+		put_le16(op->start_handle, pdu);
+		put_le16(op->end_handle, pdu + 2);
+		put_le16(GATT_CHARAC_UUID, pdu + 4);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+						pdu, sizeof(pdu),
+						discover_chrcs_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto done;
+	}
+
+	success = true;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	struct bt_gatt_request *op;
+	uint8_t pdu[6];
+
+	if (!att)
+		return false;
+
+	op = new0(struct bt_gatt_request, 1);
+	op->att = att;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+	op->start_handle = start;
+	op->end_handle = end;
+
+	put_le16(start, pdu);
+	put_le16(end, pdu + 2);
+	put_le16(GATT_CHARAC_UUID, pdu + 4);
+
+	op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+				discover_chrcs_cb, bt_gatt_request_ref(op),
+				async_req_unref);
+	if (!op->id) {
+		free(op);
+		return NULL;
+	}
+
+	return bt_gatt_request_ref(op);
+}
+
+static void read_by_type_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_request *op = user_data;
+	bool success;
+	uint8_t att_ecode = 0;
+	size_t data_length;
+	uint16_t last_handle;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		att_ecode = process_error(pdu, length);
+		success = false;
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu) {
+		success = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	data_length = ((uint8_t *) pdu)[0];
+	if (((length - 1) % data_length)) {
+		success = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) {
+		success = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	last_handle = get_le16(pdu + length - data_length);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last_handle < op->start_handle) {
+		success = false;
+		goto done;
+	}
+
+	op->start_handle = last_handle + 1;
+
+	if (last_handle != op->end_handle) {
+		uint8_t pdu[4 + get_uuid_len(&op->uuid)];
+
+		put_le16(op->start_handle, pdu);
+		put_le16(op->end_handle, pdu + 2);
+		bt_uuid_to_le(&op->uuid, pdu + 4);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+						pdu, sizeof(pdu),
+						read_by_type_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto done;
+	}
+
+	success = true;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
+					const bt_uuid_t *uuid,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	struct bt_gatt_request *op;
+	uint8_t pdu[4 + get_uuid_len(uuid)];
+
+	if (!att || !uuid || uuid->type == BT_UUID_UNSPEC)
+		return false;
+
+	op = new0(struct bt_gatt_request, 1);
+	op->att = att;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+	op->start_handle = start;
+	op->end_handle = end;
+	op->uuid = *uuid;
+
+	put_le16(start, pdu);
+	put_le16(end, pdu + 2);
+	bt_uuid_to_le(uuid, pdu + 4);
+
+	op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+						read_by_type_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+	if (op->id)
+		return true;
+
+	free(op);
+	return false;
+}
+
+static void discover_descs_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_request *op = user_data;
+	bool success;
+	uint8_t att_ecode = 0;
+	uint8_t format;
+	uint16_t last_handle;
+	size_t data_length;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		success = false;
+		att_ecode = process_error(pdu, length);
+		goto done;
+	}
+
+	/* The PDU should contain the following data (sans opcode):
+	 * - Format (1 octet)
+	 * - Attr Data List (at least 4 octets):
+	 *   -- 2 octets: Attribute handle
+	 *   -- 2 or 16 octets: UUID.
+	 */
+	if (opcode != BT_ATT_OP_FIND_INFO_RSP || !pdu || length < 5) {
+		success = false;
+		goto done;
+	}
+
+	format = ((uint8_t *) pdu)[0];
+
+	if (format == 0x01)
+		data_length = 4;
+	else if (format == 0x02)
+		data_length = 18;
+	else {
+		success = false;
+		goto done;
+	}
+
+	if ((length - 1) % data_length) {
+		success = false;
+		goto done;
+	}
+
+	if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) {
+		success = false;
+		goto done;
+	}
+
+	last_handle = get_le16(pdu + length - data_length);
+
+	/*
+	 * If last handle is lower from previous start handle then it is smth
+	 * wrong. Let's stop search, otherwise we might enter infinite loop.
+	 */
+	if (last_handle < op->start_handle) {
+		success = false;
+		goto done;
+	}
+
+	op->start_handle = last_handle + 1;
+
+	if (last_handle != op->end_handle) {
+		uint8_t pdu[4];
+
+		put_le16(op->start_handle, pdu);
+		put_le16(op->end_handle, pdu + 2);
+
+		op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
+						pdu, sizeof(pdu),
+						discover_descs_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+		if (op->id)
+			return;
+
+		success = false;
+		goto done;
+	}
+
+	success = true;
+
+done:
+	discovery_op_complete(op, success, att_ecode);
+}
+
+struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy)
+{
+	struct bt_gatt_request *op;
+	uint8_t pdu[4];
+
+	if (!att)
+		return false;
+
+	op = new0(struct bt_gatt_request, 1);
+	op->att = att;
+	op->callback = callback;
+	op->user_data = user_data;
+	op->destroy = destroy;
+	op->start_handle = start;
+	op->end_handle = end;
+
+	put_le16(start, pdu);
+	put_le16(end, pdu + 2);
+
+	op->id = bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu),
+						discover_descs_cb,
+						bt_gatt_request_ref(op),
+						async_req_unref);
+	if (!op->id) {
+		free(op);
+		return NULL;
+	}
+
+	return bt_gatt_request_ref(op);
+}
diff --git a/bluez/repo/src/shared/gatt-helpers.h b/bluez/repo/src/shared/gatt-helpers.h
new file mode 100644
index 0000000..dd9dd1c
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-helpers.h
@@ -0,0 +1,116 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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
+ *
+ */
+
+/* This file defines helpers for performing client-side procedures defined by
+ * the Generic Attribute Profile.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct bt_gatt_result;
+
+struct bt_gatt_iter {
+	struct bt_gatt_result *result;
+	uint16_t pos;
+};
+
+unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result);
+unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result);
+unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result);
+unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result);
+
+bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result);
+bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
+				uint16_t *start_handle, uint16_t *end_handle,
+				uint8_t uuid[16]);
+bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
+				uint16_t *start_handle, uint16_t *end_handle,
+				uint16_t *value_handle, uint8_t *properties,
+				uint8_t uuid[16]);
+bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
+							uint8_t uuid[16]);
+bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
+				uint16_t *handle, uint16_t *start_handle,
+				uint16_t *end_handle, uint8_t uuid[16]);
+bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter,
+				uint16_t *handle, uint16_t *length,
+				const uint8_t **value);
+
+typedef void (*bt_gatt_destroy_func_t)(void *user_data);
+
+typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode,
+							void *user_data);
+typedef void (*bt_gatt_request_callback_t)(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data);
+
+struct bt_gatt_request;
+
+struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req);
+void bt_gatt_request_unref(struct bt_gatt_request *req);
+void bt_gatt_request_cancel(struct bt_gatt_request *req);
+
+unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
+					bt_gatt_result_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+
+struct bt_gatt_request *bt_gatt_discover_all_primary_services(
+					struct bt_att *att, bt_uuid_t *uuid,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+struct bt_gatt_request *bt_gatt_discover_primary_services(
+					struct bt_att *att, bt_uuid_t *uuid,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+struct bt_gatt_request *bt_gatt_discover_secondary_services(
+					struct bt_att *att, bt_uuid_t *uuid,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
+					uint16_t start, uint16_t end,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
+
+bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
+					const bt_uuid_t *uuid,
+					bt_gatt_request_callback_t callback,
+					void *user_data,
+					bt_gatt_destroy_func_t destroy);
diff --git a/bluez/repo/src/shared/gatt-server.c b/bluez/repo/src/shared/gatt-server.c
new file mode 100644
index 0000000..ba668e3
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-server.c
@@ -0,0 +1,1532 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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 <sys/uio.h>
+#include <errno.h>
+
+#include "src/shared/att.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-helpers.h"
+#include "src/shared/util.h"
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * TODO: This is an arbitrary limit. Come up with something reasonable or
+ * perhaps an API to set this value if there is a use case for it.
+ */
+#define DEFAULT_MAX_PREP_QUEUE_LEN 30
+
+struct async_read_op {
+	struct bt_gatt_server *server;
+	uint8_t opcode;
+	bool done;
+	uint8_t *pdu;
+	size_t pdu_len;
+	size_t value_len;
+	struct queue *db_data;
+};
+
+struct async_write_op {
+	struct bt_gatt_server *server;
+	uint8_t opcode;
+};
+
+struct prep_write_data {
+	struct bt_gatt_server *server;
+	uint8_t *value;
+	uint16_t handle;
+	uint16_t offset;
+	uint16_t length;
+};
+
+static void prep_write_data_destroy(void *user_data)
+{
+	struct prep_write_data *data = user_data;
+
+	free(data->value);
+	free(data);
+}
+
+struct bt_gatt_server {
+	struct gatt_db *db;
+	struct bt_att *att;
+	int ref_count;
+	uint16_t mtu;
+
+	unsigned int mtu_id;
+	unsigned int read_by_grp_type_id;
+	unsigned int read_by_type_id;
+	unsigned int find_info_id;
+	unsigned int find_by_type_value_id;
+	unsigned int write_id;
+	unsigned int write_cmd_id;
+	unsigned int read_id;
+	unsigned int read_blob_id;
+	unsigned int read_multiple_id;
+	unsigned int prep_write_id;
+	unsigned int exec_write_id;
+
+	struct queue *prep_queue;
+	unsigned int max_prep_queue_len;
+
+	struct async_read_op *pending_read_op;
+	struct async_write_op *pending_write_op;
+
+	bt_gatt_server_debug_func_t debug_callback;
+	bt_gatt_server_destroy_func_t debug_destroy;
+	void *debug_data;
+};
+
+static void bt_gatt_server_free(struct bt_gatt_server *server)
+{
+	if (server->debug_destroy)
+		server->debug_destroy(server->debug_data);
+
+	bt_att_unregister(server->att, server->mtu_id);
+	bt_att_unregister(server->att, server->read_by_grp_type_id);
+	bt_att_unregister(server->att, server->read_by_type_id);
+	bt_att_unregister(server->att, server->find_info_id);
+	bt_att_unregister(server->att, server->find_by_type_value_id);
+	bt_att_unregister(server->att, server->write_id);
+	bt_att_unregister(server->att, server->write_cmd_id);
+	bt_att_unregister(server->att, server->read_id);
+	bt_att_unregister(server->att, server->read_blob_id);
+	bt_att_unregister(server->att, server->read_multiple_id);
+	bt_att_unregister(server->att, server->prep_write_id);
+	bt_att_unregister(server->att, server->exec_write_id);
+
+	if (server->pending_read_op)
+		server->pending_read_op->server = NULL;
+
+	if (server->pending_write_op)
+		server->pending_write_op->server = NULL;
+
+	queue_destroy(server->prep_queue, prep_write_data_destroy);
+
+	gatt_db_unref(server->db);
+	bt_att_unref(server->att);
+	free(server);
+}
+
+static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid)
+{
+	uint128_t u128;
+
+	switch (len) {
+	case 2:
+		bt_uuid16_create(out_uuid, get_le16(uuid));
+		return true;
+	case 16:
+		bswap_128(uuid, &u128.data);
+		bt_uuid128_create(out_uuid, u128);
+		return true;
+	default:
+		return false;
+	}
+
+	return false;
+}
+
+static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
+					const uint8_t *value, size_t length,
+					void *user_data)
+{
+	struct iovec *iov = user_data;
+
+	iov->iov_base = (void *) value;
+	iov->iov_len = length;
+}
+
+static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
+						struct bt_att *att,
+						uint16_t mtu, uint8_t *pdu,
+						uint16_t *len)
+{
+	int iter = 0;
+	uint16_t start_handle, end_handle;
+	struct iovec value;
+	uint8_t data_val_len;
+
+	*len = 0;
+
+	while (queue_peek_head(q)) {
+		struct gatt_db_attribute *attrib = queue_pop_head(q);
+
+		value.iov_base = NULL;
+		value.iov_len = 0;
+
+		/*
+		 * This should never be deferred to the read callback for
+		 * primary/secondary service declarations.
+		 */
+		if (!gatt_db_attribute_read(attrib, 0,
+						BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+						att, attribute_read_cb,
+						&value) || !value.iov_len)
+			return false;
+
+		/*
+		 * Use the first attribute to determine the length of each
+		 * attribute data unit. Stop the list when a different attribute
+		 * value is seen.
+		 */
+		if (iter == 0) {
+			data_val_len = MIN(MIN((unsigned)mtu - 6, 251),
+								value.iov_len);
+			pdu[0] = data_val_len + 4;
+			iter++;
+		} else if (value.iov_len != data_val_len)
+			break;
+
+		/* Stop if this unit would surpass the MTU */
+		if (iter + data_val_len + 4 > mtu - 1)
+			break;
+
+		gatt_db_attribute_get_service_handles(attrib, &start_handle,
+								&end_handle);
+
+		put_le16(start_handle, pdu + iter);
+		put_le16(end_handle, pdu + iter + 2);
+		memcpy(pdu + iter + 4, value.iov_base, data_val_len);
+
+		iter += data_val_len + 4;
+	}
+
+	*len = iter;
+
+	return true;
+}
+
+static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t start, end;
+	bt_uuid_t type;
+	bt_uuid_t prim, snd;
+	uint16_t mtu = bt_att_get_mtu(server->att);
+	uint8_t rsp_pdu[mtu];
+	uint16_t rsp_len;
+	uint8_t ecode = 0;
+	uint16_t ehandle = 0;
+	struct queue *q = NULL;
+
+	if (length != 6 && length != 20) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	q = queue_new();
+
+	start = get_le16(pdu);
+	end = get_le16(pdu + 2);
+	get_uuid_le(pdu + 4, length - 4, &type);
+
+	util_debug(server->debug_callback, server->debug_data,
+				"Read By Grp Type - start: 0x%04x end: 0x%04x",
+				start, end);
+
+	if (!start || !end) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	ehandle = start;
+
+	if (start > end) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	/*
+	 * GATT defines that only the <<Primary Service>> and
+	 * <<Secondary Service>> group types can be used for the
+	 * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an
+	 * error if any other group type is given.
+	 */
+	bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
+	bt_uuid16_create(&snd, GATT_SND_SVC_UUID);
+	if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) {
+		ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE;
+		goto error;
+	}
+
+	gatt_db_read_by_group_type(server->db, start, end, type, q);
+
+	if (queue_isempty(q)) {
+		ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
+		goto error;
+	}
+
+	if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu,
+							rsp_pdu, &rsp_len)) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto error;
+	}
+
+	queue_destroy(q, NULL);
+
+	bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
+							rsp_pdu, rsp_len,
+							NULL, NULL, NULL);
+
+	return;
+
+error:
+	queue_destroy(q, NULL);
+	bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+}
+
+static void async_read_op_destroy(struct async_read_op *op)
+{
+	if (op->server)
+		op->server->pending_read_op = NULL;
+
+	queue_destroy(op->db_data, NULL);
+	free(op->pdu);
+	free(op);
+}
+
+static void process_read_by_type(struct async_read_op *op);
+
+static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr,
+						int err, const uint8_t *value,
+						size_t len, void *user_data)
+{
+	struct async_read_op *op = user_data;
+	struct bt_gatt_server *server = op->server;
+	uint16_t mtu;
+	uint16_t handle;
+
+	if (!server) {
+		async_read_op_destroy(op);
+		return;
+	}
+
+	mtu = bt_att_get_mtu(server->att);
+	handle = gatt_db_attribute_get_handle(attr);
+
+	/* Terminate the operation if there was an error */
+	if (err) {
+		bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+								handle, err);
+		async_read_op_destroy(op);
+		return;
+	}
+
+	if (op->pdu_len == 0) {
+		op->value_len = MIN(MIN((unsigned) mtu - 4, 253), len);
+		op->pdu[0] = op->value_len + 2;
+		op->pdu_len++;
+	} else if (len != op->value_len) {
+		op->done = true;
+		goto done;
+	}
+
+	/* Stop if this would surpass the MTU */
+	if (op->pdu_len + op->value_len + 2 > (unsigned) mtu - 1) {
+		op->done = true;
+		goto done;
+	}
+
+	/* Encode the current value */
+	put_le16(handle, op->pdu + op->pdu_len);
+	memcpy(op->pdu + op->pdu_len + 2, value, op->value_len);
+
+	op->pdu_len += op->value_len + 2;
+
+	if (op->pdu_len == (unsigned) mtu - 1)
+		op->done = true;
+
+done:
+	process_read_by_type(op);
+}
+
+static uint8_t check_permissions(struct bt_gatt_server *server,
+				struct gatt_db_attribute *attr, uint32_t mask)
+{
+	uint32_t perm;
+	int security;
+
+	perm = gatt_db_attribute_get_permissions(attr);
+
+	if (perm && mask & BT_ATT_PERM_READ && !(perm & BT_ATT_PERM_READ))
+		return BT_ATT_ERROR_READ_NOT_PERMITTED;
+
+	if (perm && mask & BT_ATT_PERM_WRITE && !(perm & BT_ATT_PERM_WRITE))
+		return BT_ATT_ERROR_WRITE_NOT_PERMITTED;
+
+	perm &= mask;
+	if (!perm)
+		return 0;
+
+	security = bt_att_get_security(server->att);
+	if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH)
+		return BT_ATT_ERROR_AUTHENTICATION;
+
+	if (perm & BT_ATT_PERM_ENCRYPT && security < BT_ATT_SECURITY_MEDIUM)
+		return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION;
+
+	return 0;
+}
+
+static void process_read_by_type(struct async_read_op *op)
+{
+	struct bt_gatt_server *server = op->server;
+	uint8_t ecode;
+	struct gatt_db_attribute *attr;
+
+	attr = queue_pop_head(op->db_data);
+
+	if (op->done || !attr) {
+		bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu,
+								op->pdu_len,
+								NULL, NULL,
+								NULL);
+		async_read_op_destroy(op);
+		return;
+	}
+
+	ecode = check_permissions(server, attr, BT_ATT_PERM_READ |
+						BT_ATT_PERM_READ_AUTHEN |
+						BT_ATT_PERM_READ_ENCRYPT);
+	if (ecode)
+		goto error;
+
+	if (gatt_db_attribute_read(attr, 0, op->opcode, server->att,
+					read_by_type_read_complete_cb, op))
+		return;
+
+	ecode = BT_ATT_ERROR_UNLIKELY;
+
+error:
+	bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+				gatt_db_attribute_get_handle(attr), ecode);
+	async_read_op_destroy(op);
+}
+
+static void read_by_type_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t start, end;
+	bt_uuid_t type;
+	uint16_t ehandle = 0;
+	uint8_t ecode;
+	struct queue *q = NULL;
+	struct async_read_op *op;
+
+	if (length != 6 && length != 20) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	q = queue_new();
+
+	start = get_le16(pdu);
+	end = get_le16(pdu + 2);
+	get_uuid_le(pdu + 4, length - 4, &type);
+
+	util_debug(server->debug_callback, server->debug_data,
+				"Read By Type - start: 0x%04x end: 0x%04x",
+				start, end);
+
+	if (!start || !end) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	ehandle = start;
+
+	if (start > end) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	gatt_db_read_by_type(server->db, start, end, type, q);
+
+	if (queue_isempty(q)) {
+		ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
+		goto error;
+	}
+
+	if (server->pending_read_op) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto error;
+	}
+
+	op = new0(struct async_read_op, 1);
+	op->pdu = malloc(bt_att_get_mtu(server->att));
+	if (!op->pdu) {
+		free(op);
+		ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+		goto error;
+	}
+
+	op->opcode = opcode;
+	op->server = server;
+	op->db_data = q;
+	server->pending_read_op = op;
+
+	process_read_by_type(op);
+
+	return;
+
+error:
+	bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+	queue_destroy(q, NULL);
+}
+
+static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
+						uint16_t mtu,
+						uint8_t *pdu, uint16_t *len)
+{
+	uint16_t handle;
+	struct gatt_db_attribute *attr;
+	const bt_uuid_t *type;
+	int uuid_len, cur_uuid_len;
+	int iter = 0;
+
+	*len = 0;
+
+	while (queue_peek_head(q)) {
+		attr = queue_pop_head(q);
+		handle = gatt_db_attribute_get_handle(attr);
+		type = gatt_db_attribute_get_type(attr);
+		if (!handle || !type)
+			return false;
+
+		cur_uuid_len = bt_uuid_len(type);
+
+		if (iter == 0) {
+			switch (cur_uuid_len) {
+			case 2:
+				uuid_len = 2;
+				pdu[0] = 0x01;
+				break;
+			case 4:
+			case 16:
+				uuid_len = 16;
+				pdu[0] = 0x02;
+				break;
+			default:
+				return false;
+			}
+
+			iter++;
+		} else if (cur_uuid_len != uuid_len)
+			break;
+
+		if (iter + uuid_len + 2 > mtu - 1)
+			break;
+
+		put_le16(handle, pdu + iter);
+		bt_uuid_to_le(type, pdu + iter + 2);
+
+		iter += uuid_len + 2;
+	}
+
+	*len = iter;
+
+	return true;
+}
+
+static void find_info_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t start, end;
+	uint16_t mtu = bt_att_get_mtu(server->att);
+	uint8_t rsp_pdu[mtu];
+	uint16_t rsp_len;
+	uint8_t ecode = 0;
+	uint16_t ehandle = 0;
+	struct queue *q = NULL;
+
+	if (length != 4) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	q = queue_new();
+
+	start = get_le16(pdu);
+	end = get_le16(pdu + 2);
+
+	util_debug(server->debug_callback, server->debug_data,
+					"Find Info - start: 0x%04x end: 0x%04x",
+					start, end);
+
+	if (!start || !end) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	ehandle = start;
+
+	if (start > end) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	gatt_db_find_information(server->db, start, end, q);
+
+	if (queue_isempty(q)) {
+		ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
+		goto error;
+	}
+
+	if (!encode_find_info_rsp(server->db, q, mtu, rsp_pdu, &rsp_len)) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto error;
+	}
+
+	bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len,
+							NULL, NULL, NULL);
+	queue_destroy(q, NULL);
+
+	return;
+
+error:
+	bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
+	queue_destroy(q, NULL);
+
+}
+
+struct find_by_type_val_data {
+	uint8_t *pdu;
+	uint16_t len;
+	uint16_t mtu;
+	uint8_t ecode;
+};
+
+static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	uint16_t handle, end_handle;
+	struct find_by_type_val_data *data = user_data;
+
+	if (data->ecode)
+		return;
+
+	if (data->len + 4 > data->mtu - 1)
+		return;
+
+	/*
+	 * This OP is only valid for Primary Service per the spec
+	 * page 562, so this should work.
+	 */
+	gatt_db_attribute_get_service_data(attrib, &handle, &end_handle, NULL,
+									NULL);
+
+	if (!handle || !end_handle) {
+		data->ecode = BT_ATT_ERROR_UNLIKELY;
+		return;
+	}
+
+	put_le16(handle, data->pdu + data->len);
+	put_le16(end_handle, data->pdu + data->len + 2);
+
+	data->len += 4;
+}
+
+static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t start, end, uuid16;
+	struct find_by_type_val_data data;
+	uint16_t mtu = bt_att_get_mtu(server->att);
+	uint8_t rsp_pdu[mtu];
+	uint16_t ehandle = 0;
+	bt_uuid_t uuid;
+
+	if (length < 6) {
+		data.ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	data.pdu = rsp_pdu;
+	data.len = 0;
+	data.mtu = mtu;
+	data.ecode = 0;
+
+	start = get_le16(pdu);
+	end = get_le16(pdu + 2);
+	uuid16 = get_le16(pdu + 4);
+
+	util_debug(server->debug_callback, server->debug_data,
+			"Find By Type Value - start: 0x%04x end: 0x%04x uuid: 0x%04x",
+			start, end, uuid16);
+	ehandle = start;
+	if (start > end) {
+		data.ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	bt_uuid16_create(&uuid, uuid16);
+	gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6,
+							length - 6,
+							find_by_type_val_att_cb,
+							&data);
+
+	if (!data.len)
+		data.ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
+
+	if (data.ecode)
+		goto error;
+
+	bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu,
+						data.len, NULL, NULL, NULL);
+
+	return;
+
+error:
+	bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode);
+}
+
+static void async_write_op_destroy(struct async_write_op *op)
+{
+	if (op->server)
+		op->server->pending_write_op = NULL;
+
+	free(op);
+}
+
+static void write_complete_cb(struct gatt_db_attribute *attr, int err,
+								void *user_data)
+{
+	struct async_write_op *op = user_data;
+	struct bt_gatt_server *server = op->server;
+	uint16_t handle;
+
+	if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) {
+		async_write_op_destroy(op);
+		return;
+	}
+
+	handle = gatt_db_attribute_get_handle(attr);
+
+	if (err)
+		bt_att_send_error_rsp(server->att, op->opcode, handle, err);
+	else
+		bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0,
+							NULL, NULL, NULL);
+
+	async_write_op_destroy(op);
+}
+
+static void write_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	struct gatt_db_attribute *attr;
+	uint16_t handle = 0;
+	struct async_write_op *op = NULL;
+	uint8_t ecode;
+
+	if (length < 2) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	handle = get_le16(pdu);
+	attr = gatt_db_get_attribute(server->db, handle);
+	if (!attr) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	util_debug(server->debug_callback, server->debug_data,
+				"Write %s - handle: 0x%04x",
+				(opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd",
+				handle);
+
+	ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE |
+						BT_ATT_PERM_WRITE_AUTHEN |
+						BT_ATT_PERM_WRITE_ENCRYPT);
+	if (ecode)
+		goto error;
+
+	if (server->pending_write_op) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto error;
+	}
+
+	op = new0(struct async_write_op, 1);
+	op->server = server;
+	op->opcode = opcode;
+	server->pending_write_op = op;
+
+	if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode,
+							server->att,
+							write_complete_cb, op))
+		return;
+
+	if (op)
+		async_write_op_destroy(op);
+
+	ecode = BT_ATT_ERROR_UNLIKELY;
+
+error:
+	if (opcode == BT_ATT_OP_WRITE_CMD)
+		return;
+
+	bt_att_send_error_rsp(server->att, opcode, handle, ecode);
+}
+
+static uint8_t get_read_rsp_opcode(uint8_t opcode)
+{
+
+	switch (opcode) {
+	case BT_ATT_OP_READ_REQ:
+		return BT_ATT_OP_READ_RSP;
+	case BT_ATT_OP_READ_BLOB_REQ:
+		return BT_ATT_OP_READ_BLOB_RSP;
+	default:
+		/*
+		 * Should never happen
+		 *
+		 * TODO: It would be nice to have a debug-mode assert macro
+		 * for development builds. This way bugs could be easily catched
+		 * during development and there would be self documenting code
+		 * that wouldn't be crash release builds.
+		 */
+		return 0;
+	}
+
+	return 0;
+}
+
+static void read_complete_cb(struct gatt_db_attribute *attr, int err,
+					const uint8_t *value, size_t len,
+					void *user_data)
+{
+	struct async_read_op *op = user_data;
+	struct bt_gatt_server *server = op->server;
+	uint8_t rsp_opcode;
+	uint16_t mtu;
+	uint16_t handle;
+
+	if (!server) {
+		async_read_op_destroy(op);
+		return;
+	}
+
+	mtu = bt_att_get_mtu(server->att);
+	handle = gatt_db_attribute_get_handle(attr);
+
+	if (err) {
+		bt_att_send_error_rsp(server->att, op->opcode, handle, err);
+		async_read_op_destroy(op);
+		return;
+	}
+
+	rsp_opcode = get_read_rsp_opcode(op->opcode);
+
+	bt_att_send(server->att, rsp_opcode, len ? value : NULL,
+						MIN((unsigned) mtu - 1, len),
+						NULL, NULL, NULL);
+	async_read_op_destroy(op);
+}
+
+static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
+								uint16_t handle,
+								uint16_t offset)
+{
+	struct gatt_db_attribute *attr;
+	uint8_t ecode;
+	struct async_read_op *op = NULL;
+
+	attr = gatt_db_get_attribute(server->db, handle);
+	if (!attr) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	util_debug(server->debug_callback, server->debug_data,
+			"Read %sReq - handle: 0x%04x",
+			opcode == BT_ATT_OP_READ_BLOB_REQ ? "Blob " : "",
+			handle);
+
+	ecode = check_permissions(server, attr, BT_ATT_PERM_READ |
+						BT_ATT_PERM_READ_AUTHEN |
+						BT_ATT_PERM_READ_ENCRYPT);
+	if (ecode)
+		goto error;
+
+	if (server->pending_read_op) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto error;
+	}
+
+	op = new0(struct async_read_op, 1);
+	op->opcode = opcode;
+	op->server = server;
+	server->pending_read_op = op;
+
+	if (gatt_db_attribute_read(attr, offset, opcode, server->att,
+							read_complete_cb, op))
+		return;
+
+	ecode = BT_ATT_ERROR_UNLIKELY;
+
+error:
+	if (op)
+		async_read_op_destroy(op);
+
+	bt_att_send_error_rsp(server->att, opcode, handle, ecode);
+}
+
+static void read_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t handle;
+
+	if (length != 2) {
+		bt_att_send_error_rsp(server->att, opcode, 0,
+						BT_ATT_ERROR_INVALID_PDU);
+		return;
+	}
+
+	handle = get_le16(pdu);
+
+	handle_read_req(server, opcode, handle, 0);
+}
+
+static void read_blob_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t handle, offset;
+
+	if (length != 4) {
+		bt_att_send_error_rsp(server->att, opcode, 0,
+						BT_ATT_ERROR_INVALID_PDU);
+		return;
+	}
+
+	handle = get_le16(pdu);
+	offset = get_le16(pdu + 2);
+
+	handle_read_req(server, opcode, handle, offset);
+}
+
+struct read_multiple_resp_data {
+	struct bt_gatt_server *server;
+	uint16_t *handles;
+	size_t cur_handle;
+	size_t num_handles;
+	uint8_t *rsp_data;
+	size_t length;
+	size_t mtu;
+};
+
+static void read_multiple_resp_data_free(struct read_multiple_resp_data *data)
+{
+	free(data->handles);
+	data->handles = NULL;
+
+	free(data->rsp_data);
+	data->rsp_data = NULL;
+}
+
+static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
+					const uint8_t *value, size_t len,
+					void *user_data)
+{
+	struct read_multiple_resp_data *data = user_data;
+	struct gatt_db_attribute *next_attr;
+	uint16_t handle = gatt_db_attribute_get_handle(attr);
+	uint8_t ecode;
+
+	if (err != 0) {
+		bt_att_send_error_rsp(data->server->att,
+					BT_ATT_OP_READ_MULT_REQ, handle, err);
+		read_multiple_resp_data_free(data);
+		return;
+	}
+
+	ecode = check_permissions(data->server, attr, BT_ATT_PERM_READ |
+						BT_ATT_PERM_READ_AUTHEN |
+						BT_ATT_PERM_READ_ENCRYPT);
+	if (ecode) {
+		bt_att_send_error_rsp(data->server->att,
+					BT_ATT_OP_READ_MULT_REQ, handle, ecode);
+		read_multiple_resp_data_free(data);
+		return;
+	}
+
+	len = MIN(len, data->mtu - data->length - 1);
+
+	memcpy(data->rsp_data + data->length, value, len);
+	data->length += len;
+
+	data->cur_handle++;
+
+	if ((data->length >= data->mtu - 1) ||
+				(data->cur_handle == data->num_handles)) {
+		bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP,
+				data->rsp_data, data->length, NULL, NULL, NULL);
+		read_multiple_resp_data_free(data);
+		return;
+	}
+
+	util_debug(data->server->debug_callback, data->server->debug_data,
+				"Read Multiple Req - #%zu of %zu: 0x%04x",
+				data->cur_handle + 1, data->num_handles,
+				data->handles[data->cur_handle]);
+
+	next_attr = gatt_db_get_attribute(data->server->db,
+					data->handles[data->cur_handle]);
+
+	if (!next_attr) {
+		bt_att_send_error_rsp(data->server->att,
+					BT_ATT_OP_READ_MULT_REQ,
+					data->handles[data->cur_handle],
+					BT_ATT_ERROR_INVALID_HANDLE);
+		read_multiple_resp_data_free(data);
+		return;
+	}
+
+	if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ,
+					data->server->att,
+					read_multiple_complete_cb, data)) {
+		bt_att_send_error_rsp(data->server->att,
+						BT_ATT_OP_READ_MULT_REQ,
+						data->handles[data->cur_handle],
+						BT_ATT_ERROR_UNLIKELY);
+		read_multiple_resp_data_free(data);
+	}
+}
+
+static void read_multiple_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	struct gatt_db_attribute *attr;
+	struct read_multiple_resp_data data;
+	uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+	size_t i = 0;
+
+	data.handles = NULL;
+	data.rsp_data = NULL;
+
+	if (length < 4) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	data.server = server;
+	data.num_handles = length / 2;
+	data.cur_handle = 0;
+	data.mtu = bt_att_get_mtu(server->att);
+	data.length = 0;
+	data.rsp_data = malloc(data.mtu - 1);
+
+	if (!data.rsp_data)
+		goto error;
+
+	data.handles = new0(uint16_t, data.num_handles);
+
+	for (i = 0; i < data.num_handles; i++)
+		data.handles[i] = get_le16(pdu + i * 2);
+
+	util_debug(server->debug_callback, server->debug_data,
+			"Read Multiple Req - %zu handles, 1st: 0x%04x",
+			data.num_handles, data.handles[0]);
+
+	attr = gatt_db_get_attribute(server->db, data.handles[0]);
+
+	if (!attr) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	if (gatt_db_attribute_read(attr, 0, opcode, server->att,
+					read_multiple_complete_cb, &data))
+		return;
+
+error:
+	read_multiple_resp_data_free(&data);
+	bt_att_send_error_rsp(server->att, opcode, 0, ecode);
+}
+
+static void prep_write_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	struct prep_write_data *prep_data = NULL;
+	uint16_t handle = 0;
+	uint16_t offset;
+	struct gatt_db_attribute *attr;
+	uint8_t ecode;
+
+	if (length < 4) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	if (queue_length(server->prep_queue) >= server->max_prep_queue_len) {
+		ecode = BT_ATT_ERROR_PREPARE_QUEUE_FULL;
+		goto error;
+	}
+
+	handle = get_le16(pdu);
+	offset = get_le16(pdu + 2);
+
+	attr = gatt_db_get_attribute(server->db, handle);
+	if (!attr) {
+		ecode = BT_ATT_ERROR_INVALID_HANDLE;
+		goto error;
+	}
+
+	util_debug(server->debug_callback, server->debug_data,
+				"Prep Write Req - handle: 0x%04x", handle);
+
+	ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE |
+						BT_ATT_PERM_WRITE_AUTHEN |
+						BT_ATT_PERM_WRITE_ENCRYPT);
+	if (ecode)
+		goto error;
+
+	prep_data = new0(struct prep_write_data, 1);
+	prep_data->length = length - 4;
+	if (prep_data->length) {
+		prep_data->value = malloc(prep_data->length);
+		if (!prep_data->value) {
+			ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+			goto error;
+		}
+	}
+
+	prep_data->server = server;
+	prep_data->handle = handle;
+	prep_data->offset = offset;
+	memcpy(prep_data->value, pdu + 4, prep_data->length);
+
+	queue_push_tail(server->prep_queue, prep_data);
+
+	bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL,
+								NULL, NULL);
+	return;
+
+error:
+	if (prep_data)
+		prep_write_data_destroy(prep_data);
+
+	bt_att_send_error_rsp(server->att, opcode, handle, ecode);
+
+}
+
+static void exec_next_prep_write(struct bt_gatt_server *server,
+						uint16_t ehandle, int err);
+
+static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err,
+								void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t handle = gatt_db_attribute_get_handle(attr);
+
+	exec_next_prep_write(server, handle, err);
+}
+
+static void exec_next_prep_write(struct bt_gatt_server *server,
+						uint16_t ehandle, int err)
+{
+	struct prep_write_data *next = NULL;
+	struct gatt_db_attribute *attr;
+	bool status;
+
+	if (err)
+		goto error;
+
+	next = queue_pop_head(server->prep_queue);
+	if (!next) {
+		bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
+							NULL, NULL, NULL);
+		return;
+	}
+
+	attr = gatt_db_get_attribute(server->db, next->handle);
+	if (!attr) {
+		err = BT_ATT_ERROR_UNLIKELY;
+		goto error;
+	}
+
+	status = gatt_db_attribute_write(attr, next->offset,
+						next->value, next->length,
+						BT_ATT_OP_EXEC_WRITE_REQ,
+						server->att,
+						exec_write_complete_cb, server);
+
+	prep_write_data_destroy(next);
+
+	if (status)
+		return;
+
+	err = BT_ATT_ERROR_UNLIKELY;
+
+error:
+	bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ,
+								ehandle, err);
+}
+
+static void exec_write_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint8_t flags;
+	uint8_t ecode;
+	bool write;
+
+	if (length != 1) {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	flags = ((uint8_t *) pdu)[0];
+
+	util_debug(server->debug_callback, server->debug_data,
+				"Exec Write Req - flags: 0x%02x", flags);
+
+	if (flags == 0x00)
+		write = false;
+	else if (flags == 0x01)
+		write = true;
+	else {
+		ecode = BT_ATT_ERROR_INVALID_PDU;
+		goto error;
+	}
+
+	if (!write) {
+		queue_remove_all(server->prep_queue, NULL, NULL,
+						prep_write_data_destroy);
+		bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
+							NULL, NULL, NULL);
+		return;
+	}
+
+	exec_next_prep_write(server, 0, 0);
+
+	return;
+
+error:
+	bt_att_send_error_rsp(server->att, opcode, 0, ecode);
+}
+
+static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct bt_gatt_server *server = user_data;
+	uint16_t client_rx_mtu;
+	uint16_t final_mtu;
+	uint8_t rsp_pdu[2];
+
+	if (length != 2) {
+		bt_att_send_error_rsp(server->att, opcode, 0,
+						BT_ATT_ERROR_INVALID_PDU);
+		return;
+	}
+
+	client_rx_mtu = get_le16(pdu);
+	final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU);
+
+	/* Respond with the server MTU */
+	put_le16(server->mtu, rsp_pdu);
+	bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL,
+									NULL);
+
+	/* Set MTU to be the minimum */
+	server->mtu = final_mtu;
+	bt_att_set_mtu(server->att, final_mtu);
+
+	util_debug(server->debug_callback, server->debug_data,
+			"MTU exchange complete, with MTU: %u", final_mtu);
+}
+
+static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
+{
+	/* Exchange MTU */
+	server->mtu_id = bt_att_register(server->att, BT_ATT_OP_MTU_REQ,
+								exchange_mtu_cb,
+								server, NULL);
+	if (!server->mtu_id)
+		return false;
+
+	/* Read By Group Type */
+	server->read_by_grp_type_id = bt_att_register(server->att,
+						BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+						read_by_grp_type_cb,
+						server, NULL);
+	if (!server->read_by_grp_type_id)
+		return false;
+
+	/* Read By Type */
+	server->read_by_type_id = bt_att_register(server->att,
+						BT_ATT_OP_READ_BY_TYPE_REQ,
+						read_by_type_cb,
+						server, NULL);
+	if (!server->read_by_type_id)
+		return false;
+
+	/* Find Information */
+	server->find_info_id = bt_att_register(server->att,
+							BT_ATT_OP_FIND_INFO_REQ,
+							find_info_cb,
+							server, NULL);
+	if (!server->find_info_id)
+		return false;
+
+	/* Find By Type Value */
+	server->find_by_type_value_id = bt_att_register(server->att,
+						BT_ATT_OP_FIND_BY_TYPE_REQ,
+						find_by_type_val_cb,
+						server, NULL);
+
+	if (!server->find_by_type_value_id)
+		return false;
+
+	/* Write Request */
+	server->write_id = bt_att_register(server->att, BT_ATT_OP_WRITE_REQ,
+								write_cb,
+								server, NULL);
+	if (!server->write_id)
+		return false;
+
+	/* Write Command */
+	server->write_cmd_id = bt_att_register(server->att, BT_ATT_OP_WRITE_CMD,
+								write_cb,
+								server, NULL);
+	if (!server->write_cmd_id)
+		return false;
+
+	/* Read Request */
+	server->read_id = bt_att_register(server->att, BT_ATT_OP_READ_REQ,
+								read_cb,
+								server, NULL);
+	if (!server->read_id)
+		return false;
+
+	/* Read Blob Request */
+	server->read_blob_id = bt_att_register(server->att,
+							BT_ATT_OP_READ_BLOB_REQ,
+							read_blob_cb,
+							server, NULL);
+	if (!server->read_blob_id)
+		return false;
+
+	/* Read Multiple Request */
+	server->read_multiple_id = bt_att_register(server->att,
+							BT_ATT_OP_READ_MULT_REQ,
+							read_multiple_cb,
+							server, NULL);
+
+	if (!server->read_multiple_id)
+		return false;
+
+	/* Prepare Write Request */
+	server->prep_write_id = bt_att_register(server->att,
+						BT_ATT_OP_PREP_WRITE_REQ,
+						prep_write_cb, server, NULL);
+	if (!server->prep_write_id)
+		return false;
+
+	/* Execute Write Request */
+	server->exec_write_id = bt_att_register(server->att,
+						BT_ATT_OP_EXEC_WRITE_REQ,
+						exec_write_cb, server, NULL);
+	if (!server->exec_write_id)
+		return NULL;
+
+	return true;
+}
+
+struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
+					struct bt_att *att, uint16_t mtu)
+{
+	struct bt_gatt_server *server;
+
+	if (!att || !db)
+		return NULL;
+
+	server = new0(struct bt_gatt_server, 1);
+	server->db = gatt_db_ref(db);
+	server->att = bt_att_ref(att);
+	server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU);
+	server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN;
+	server->prep_queue = queue_new();
+
+	if (!gatt_server_register_att_handlers(server)) {
+		bt_gatt_server_free(server);
+		return NULL;
+	}
+
+	return bt_gatt_server_ref(server);
+}
+
+struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server)
+{
+	if (!server)
+		return NULL;
+
+	__sync_fetch_and_add(&server->ref_count, 1);
+
+	return server;
+}
+
+void bt_gatt_server_unref(struct bt_gatt_server *server)
+{
+	if (!server)
+		return;
+
+	if (__sync_sub_and_fetch(&server->ref_count, 1))
+		return;
+
+	bt_gatt_server_free(server);
+}
+
+bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
+					bt_gatt_server_debug_func_t callback,
+					void *user_data,
+					bt_gatt_server_destroy_func_t destroy)
+{
+	if (!server)
+		return false;
+
+	if (server->debug_destroy)
+		server->debug_destroy(server->debug_data);
+
+	server->debug_callback = callback;
+	server->debug_destroy = destroy;
+	server->debug_data = user_data;
+
+	return true;
+}
+
+bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
+					uint16_t handle, const uint8_t *value,
+					uint16_t length)
+{
+	uint16_t pdu_len;
+	uint8_t *pdu;
+	bool result;
+
+	if (!server || (length && !value))
+		return false;
+
+	pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2);
+	pdu = malloc(pdu_len);
+	if (!pdu)
+		return false;
+
+	put_le16(handle, pdu);
+	memcpy(pdu + 2, value, pdu_len - 2);
+
+	result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_NOT, pdu,
+						pdu_len, NULL, NULL, NULL);
+	free(pdu);
+
+	return result;
+}
+
+struct ind_data {
+	bt_gatt_server_conf_func_t callback;
+	bt_gatt_server_destroy_func_t destroy;
+	void *user_data;
+};
+
+static void destroy_ind_data(void *user_data)
+{
+	struct ind_data *data = user_data;
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+
+	free(data);
+}
+
+static void conf_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct ind_data *data = user_data;
+
+	if (data->callback)
+		data->callback(data->user_data);
+}
+
+bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
+					uint16_t handle, const uint8_t *value,
+					uint16_t length,
+					bt_gatt_server_conf_func_t callback,
+					void *user_data,
+					bt_gatt_server_destroy_func_t destroy)
+{
+	uint16_t pdu_len;
+	uint8_t *pdu;
+	struct ind_data *data;
+	bool result;
+
+	if (!server || (length && !value))
+		return false;
+
+	pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2);
+	pdu = malloc(pdu_len);
+	if (!pdu)
+		return false;
+
+	data = new0(struct ind_data, 1);
+
+	data->callback = callback;
+	data->destroy = destroy;
+	data->user_data = user_data;
+
+	put_le16(handle, pdu);
+	memcpy(pdu + 2, value, pdu_len - 2);
+
+	result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu,
+							pdu_len, conf_cb,
+							data, destroy_ind_data);
+	if (!result)
+		destroy_ind_data(data);
+
+	free(pdu);
+
+	return result;
+}
diff --git a/bluez/repo/src/shared/gatt-server.h b/bluez/repo/src/shared/gatt-server.h
new file mode 100644
index 0000000..0e480e1
--- /dev/null
+++ b/bluez/repo/src/shared/gatt-server.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ *  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 <stdint.h>
+
+struct bt_gatt_server;
+
+struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
+					struct bt_att *att, uint16_t mtu);
+
+struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
+void bt_gatt_server_unref(struct bt_gatt_server *server);
+
+typedef void (*bt_gatt_server_destroy_func_t)(void *user_data);
+typedef void (*bt_gatt_server_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_gatt_server_conf_func_t)(void *user_data);
+
+bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
+					bt_gatt_server_debug_func_t callback,
+					void *user_data,
+					bt_gatt_server_destroy_func_t destroy);
+
+bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
+					uint16_t handle, const uint8_t *value,
+					uint16_t length);
+
+bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
+					uint16_t handle, const uint8_t *value,
+					uint16_t length,
+					bt_gatt_server_conf_func_t callback,
+					void *user_data,
+					bt_gatt_server_destroy_func_t destroy);
diff --git a/bluez/repo/src/shared/hci-crypto.c b/bluez/repo/src/shared/hci-crypto.c
new file mode 100644
index 0000000..f750747
--- /dev/null
+++ b/bluez/repo/src/shared/hci-crypto.c
@@ -0,0 +1,172 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "monitor/bt.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+#include "src/shared/hci-crypto.h"
+
+struct crypto_data {
+	uint8_t size;
+	bt_hci_crypto_func_t callback;
+	void *user_data;
+};
+
+static void le_encrypt_callback(const void *response, uint8_t size,
+							void *user_data)
+{
+	struct crypto_data *data = user_data;
+	const struct bt_hci_rsp_le_encrypt *rsp = response;
+
+	if (rsp->status) {
+		data->callback(NULL, 0, data->user_data);
+		return;
+	}
+
+	data->callback(rsp->data, data->size, data->user_data);
+}
+
+static bool le_encrypt(struct bt_hci *hci, uint8_t size,
+			const uint8_t key[16], const uint8_t plaintext[16],
+			bt_hci_crypto_func_t callback, void *user_data)
+{
+	struct crypto_data *data;
+	struct bt_hci_cmd_le_encrypt cmd;
+
+	if (!callback || !size || size > 16)
+		return false;
+
+	memcpy(cmd.key, key, 16);
+	memcpy(cmd.plaintext, plaintext, 16);
+
+	data = new0(struct crypto_data, 1);
+	data->size = size;
+	data->callback = callback;
+	data->user_data = user_data;
+
+	if (!bt_hci_send(hci, BT_HCI_CMD_LE_ENCRYPT, &cmd, sizeof(cmd),
+					le_encrypt_callback, data, free)) {
+		free(data);
+		return false;
+	}
+
+	return true;
+}
+
+static void prand_callback(const void *response, uint8_t size,
+							void *user_data)
+{
+	struct crypto_data *data = user_data;
+	const struct bt_hci_rsp_le_rand *rsp = response;
+	uint8_t prand[3];
+
+	if (rsp->status) {
+		data->callback(NULL, 0, data->user_data);
+		return;
+	}
+
+	prand[0] = (rsp->number & 0xff0000) >> 16;
+	prand[1] = (rsp->number & 0x00ff00) >> 8;
+	prand[2] = (rsp->number & 0x00003f) | 0x40;
+
+	data->callback(prand, 3, data->user_data);
+}
+
+bool bt_hci_crypto_prand(struct bt_hci *hci,
+			bt_hci_crypto_func_t callback, void *user_data)
+{
+	struct crypto_data *data;
+
+	if (!callback)
+		return false;
+
+	data = new0(struct crypto_data, 1);
+	data->callback = callback;
+	data->user_data = user_data;
+
+	if (!bt_hci_send(hci, BT_HCI_CMD_LE_RAND, NULL, 0,
+					prand_callback, data, free)) {
+		free(data);
+		return false;
+	}
+
+	return true;
+}
+
+bool bt_hci_crypto_e(struct bt_hci *hci,
+			const uint8_t key[16], const uint8_t plaintext[16],
+			bt_hci_crypto_func_t callback, void *user_data)
+{
+	return le_encrypt(hci, 16, key, plaintext, callback, user_data);
+}
+
+bool bt_hci_crypto_d1(struct bt_hci *hci,
+			const uint8_t k[16], uint16_t d, uint16_t r,
+			bt_hci_crypto_func_t callback, void *user_data)
+{
+	uint8_t dp[16];
+
+	/* d' = padding || r || d */
+	dp[0] = d & 0xff;
+	dp[1] = d >> 8;
+	dp[2] = r & 0xff;
+	dp[3] = r >> 8;
+	memset(dp + 4, 0, 12);
+
+	/* d1(k, d, r) = e(k, d') */
+	return le_encrypt(hci, 16, k, dp, callback, user_data);
+}
+
+bool bt_hci_crypto_dm(struct bt_hci *hci,
+			const uint8_t k[16], const uint8_t r[8],
+			bt_hci_crypto_func_t callback, void *user_data)
+{
+	uint8_t rp[16];
+
+	/* r' = padding || r */
+	memcpy(rp, r, 8);
+	memset(rp + 8, 0, 8);
+
+	/* dm(k, r) = e(k, r') mod 2^16 */
+	return le_encrypt(hci, 8, k, rp, callback, user_data);
+}
+
+bool bt_hci_crypto_ah(struct bt_hci *hci,
+			const uint8_t k[16], const uint8_t r[3],
+			bt_hci_crypto_func_t callback, void *user_data)
+{
+	uint8_t rp[16];
+
+	/* r' = padding || r */
+	memcpy(rp, r, 3);
+	memset(rp + 3, 0, 13);
+
+	/* ah(k, r) = e(k, r') mod 2^24 */
+	return le_encrypt(hci, 3, k, rp, callback, user_data);
+}
diff --git a/bluez/repo/src/shared/hci-crypto.h b/bluez/repo/src/shared/hci-crypto.h
new file mode 100644
index 0000000..b090c24
--- /dev/null
+++ b/bluez/repo/src/shared/hci-crypto.h
@@ -0,0 +1,45 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct bt_hci;
+
+typedef void (*bt_hci_crypto_func_t)(const void *data, uint8_t size,
+							void *user_data);
+
+bool bt_hci_crypto_prand(struct bt_hci *hci,
+			bt_hci_crypto_func_t callback, void *user_data);
+bool bt_hci_crypto_e(struct bt_hci *hci,
+			const uint8_t key[16], const uint8_t plaintext[16],
+			bt_hci_crypto_func_t callback, void *user_data);
+bool bt_hci_crypto_d1(struct bt_hci *hci,
+			const uint8_t k[16], uint16_t d, uint16_t r,
+			bt_hci_crypto_func_t callback, void *user_data);
+bool bt_hci_crypto_dm(struct bt_hci *hci,
+			const uint8_t k[16], const uint8_t r[8],
+			bt_hci_crypto_func_t callback, void *user_data);
+bool bt_hci_crypto_ah(struct bt_hci *hci,
+			const uint8_t k[16], const uint8_t r[3],
+			bt_hci_crypto_func_t callback, void *user_data);
diff --git a/bluez/repo/src/shared/hci.c b/bluez/repo/src/shared/hci.c
new file mode 100644
index 0000000..bfee4ab
--- /dev/null
+++ b/bluez/repo/src/shared/hci.c
@@ -0,0 +1,586 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <string.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/hci.h"
+
+#define BTPROTO_HCI	1
+struct sockaddr_hci {
+	sa_family_t	hci_family;
+	unsigned short	hci_dev;
+	unsigned short  hci_channel;
+};
+#define HCI_CHANNEL_RAW		0
+#define HCI_CHANNEL_USER	1
+
+#define SOL_HCI		0
+#define HCI_FILTER	2
+struct hci_filter {
+	uint32_t type_mask;
+	uint32_t event_mask[2];
+	uint16_t opcode;
+};
+
+struct bt_hci {
+	int ref_count;
+	struct io *io;
+	bool is_stream;
+	bool writer_active;
+	uint8_t num_cmds;
+	unsigned int next_cmd_id;
+	unsigned int next_evt_id;
+	struct queue *cmd_queue;
+	struct queue *rsp_queue;
+	struct queue *evt_list;
+};
+
+struct cmd {
+	unsigned int id;
+	uint16_t opcode;
+	void *data;
+	uint8_t size;
+	bt_hci_callback_func_t callback;
+	bt_hci_destroy_func_t destroy;
+	void *user_data;
+};
+
+struct evt {
+	unsigned int id;
+	uint8_t event;
+	bt_hci_callback_func_t callback;
+	bt_hci_destroy_func_t destroy;
+	void *user_data;
+};
+
+static void cmd_free(void *data)
+{
+	struct cmd *cmd = data;
+
+	if (cmd->destroy)
+		cmd->destroy(cmd->user_data);
+
+	free(cmd->data);
+	free(cmd);
+}
+
+static void evt_free(void *data)
+{
+	struct evt *evt = data;
+
+	if (evt->destroy)
+		evt->destroy(evt->user_data);
+
+	free(evt);
+}
+
+static void send_command(struct bt_hci *hci, uint16_t opcode,
+						void *data, uint8_t size)
+{
+	uint8_t type = BT_H4_CMD_PKT;
+	struct bt_hci_cmd_hdr hdr;
+	struct iovec iov[3];
+	int iovcnt;
+
+	if (hci->num_cmds < 1)
+		return;
+
+	hdr.opcode = cpu_to_le16(opcode);
+	hdr.plen = size;
+
+	iov[0].iov_base = &type;
+	iov[0].iov_len  = 1;
+	iov[1].iov_base = &hdr;
+	iov[1].iov_len  = sizeof(hdr);
+
+	if (size > 0) {
+		iov[2].iov_base = data;
+		iov[2].iov_len  = size;
+		iovcnt = 3;
+	} else
+		iovcnt = 2;
+
+	if (io_send(hci->io, iov, iovcnt) < 0)
+		return;
+
+	hci->num_cmds--;
+}
+
+static bool io_write_callback(struct io *io, void *user_data)
+{
+	struct bt_hci *hci = user_data;
+	struct cmd *cmd;
+
+	cmd = queue_pop_head(hci->cmd_queue);
+	if (cmd) {
+		send_command(hci, cmd->opcode, cmd->data, cmd->size);
+		queue_push_tail(hci->rsp_queue, cmd);
+	}
+
+	hci->writer_active = false;
+
+	return false;
+}
+
+static void wakeup_writer(struct bt_hci *hci)
+{
+	if (hci->writer_active)
+		return;
+
+	if (hci->num_cmds < 1)
+		return;
+
+	if (queue_isempty(hci->cmd_queue))
+		return;
+
+	if (!io_set_write_handler(hci->io, io_write_callback, hci, NULL))
+		return;
+
+	hci->writer_active = true;
+}
+
+static bool match_cmd_opcode(const void *a, const void *b)
+{
+	const struct cmd *cmd = a;
+	uint16_t opcode = PTR_TO_UINT(b);
+
+	return cmd->opcode == opcode;
+}
+
+static void process_response(struct bt_hci *hci, uint16_t opcode,
+					const void *data, size_t size)
+{
+	struct cmd *cmd;
+
+	if (opcode == BT_HCI_CMD_NOP)
+		goto done;
+
+	cmd = queue_remove_if(hci->rsp_queue, match_cmd_opcode,
+						UINT_TO_PTR(opcode));
+	if (!cmd)
+		return;
+
+	if (cmd->callback)
+		cmd->callback(data, size, cmd->user_data);
+
+	cmd_free(cmd);
+
+done:
+	wakeup_writer(hci);
+}
+
+static void process_notify(void *data, void *user_data)
+{
+	struct bt_hci_evt_hdr *hdr = user_data;
+	struct evt *evt = data;
+
+	if (evt->event == hdr->evt)
+		evt->callback(user_data + sizeof(struct bt_hci_evt_hdr),
+						hdr->plen, evt->user_data);
+}
+
+static void process_event(struct bt_hci *hci, const void *data, size_t size)
+{
+	const struct bt_hci_evt_hdr *hdr = data;
+	const struct bt_hci_evt_cmd_complete *cc;
+	const struct bt_hci_evt_cmd_status *cs;
+
+	if (size < sizeof(struct bt_hci_evt_hdr))
+		return;
+
+	data += sizeof(struct bt_hci_evt_hdr);
+	size -= sizeof(struct bt_hci_evt_hdr);
+
+	if (hdr->plen != size)
+		return;
+
+	switch (hdr->evt) {
+	case BT_HCI_EVT_CMD_COMPLETE:
+		if (size < sizeof(*cc))
+			return;
+		cc = data;
+		hci->num_cmds = cc->ncmd;
+		process_response(hci, le16_to_cpu(cc->opcode),
+						data + sizeof(*cc),
+						size - sizeof(*cc));
+		break;
+
+	case BT_HCI_EVT_CMD_STATUS:
+		if (size < sizeof(*cs))
+			return;
+		cs = data;
+		hci->num_cmds = cs->ncmd;
+		process_response(hci, le16_to_cpu(cs->opcode), &cs->status, 1);
+		break;
+
+	default:
+		queue_foreach(hci->evt_list, process_notify, (void *) hdr);
+		break;
+	}
+}
+
+static bool io_read_callback(struct io *io, void *user_data)
+{
+	struct bt_hci *hci = user_data;
+	uint8_t buf[512];
+	ssize_t len;
+	int fd;
+
+	fd = io_get_fd(hci->io);
+	if (fd < 0)
+		return false;
+
+	if (hci->is_stream)
+		return false;
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		return false;
+
+	if (len < 1)
+		return true;
+
+	switch (buf[0]) {
+	case BT_H4_EVT_PKT:
+		process_event(hci, buf + 1, len - 1);
+		break;
+	}
+
+	return true;
+}
+
+static struct bt_hci *create_hci(int fd)
+{
+	struct bt_hci *hci;
+
+	if (fd < 0)
+		return NULL;
+
+	hci = new0(struct bt_hci, 1);
+	hci->io = io_new(fd);
+	if (!hci->io) {
+		free(hci);
+		return NULL;
+	}
+
+	hci->is_stream = true;
+	hci->writer_active = false;
+	hci->num_cmds = 1;
+	hci->next_cmd_id = 1;
+	hci->next_evt_id = 1;
+
+	hci->cmd_queue = queue_new();
+	hci->rsp_queue = queue_new();
+	hci->evt_list = queue_new();
+
+	if (!io_set_read_handler(hci->io, io_read_callback, hci, NULL)) {
+		queue_destroy(hci->evt_list, NULL);
+		queue_destroy(hci->rsp_queue, NULL);
+		queue_destroy(hci->cmd_queue, NULL);
+		io_destroy(hci->io);
+		free(hci);
+		return NULL;
+	}
+
+	return bt_hci_ref(hci);
+}
+
+struct bt_hci *bt_hci_new(int fd)
+{
+	struct bt_hci *hci;
+
+	hci = create_hci(fd);
+	if (!hci)
+		return NULL;
+
+	return hci;
+}
+
+static int create_socket(uint16_t index, uint16_t channel)
+{
+	struct sockaddr_hci addr;
+	int fd;
+
+	fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+								BTPROTO_HCI);
+	if (fd < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = index;
+	addr.hci_channel = channel;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+struct bt_hci *bt_hci_new_user_channel(uint16_t index)
+{
+	struct bt_hci *hci;
+	int fd;
+
+	fd = create_socket(index, HCI_CHANNEL_USER);
+	if (fd < 0)
+		return NULL;
+
+	hci = create_hci(fd);
+	if (!hci) {
+		close(fd);
+		return NULL;
+	}
+
+	hci->is_stream = false;
+
+	bt_hci_set_close_on_unref(hci, true);
+
+	return hci;
+}
+
+struct bt_hci *bt_hci_new_raw_device(uint16_t index)
+{
+	struct bt_hci *hci;
+	struct hci_filter flt;
+	int fd;
+
+	fd = create_socket(index, HCI_CHANNEL_RAW);
+	if (fd < 0)
+		return NULL;
+
+	memset(&flt, 0, sizeof(flt));
+	flt.type_mask = 1 << BT_H4_EVT_PKT;
+	flt.event_mask[0] = 0xffffffff;
+	flt.event_mask[1] = 0xffffffff;
+
+	if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+		close(fd);
+		return NULL;
+	}
+
+	hci = create_hci(fd);
+	if (!hci) {
+		close(fd);
+		return NULL;
+	}
+
+	hci->is_stream = false;
+
+	bt_hci_set_close_on_unref(hci, true);
+
+	return hci;
+}
+
+struct bt_hci *bt_hci_ref(struct bt_hci *hci)
+{
+	if (!hci)
+		return NULL;
+
+	__sync_fetch_and_add(&hci->ref_count, 1);
+
+	return hci;
+}
+
+void bt_hci_unref(struct bt_hci *hci)
+{
+	if (!hci)
+		return;
+
+	if (__sync_sub_and_fetch(&hci->ref_count, 1))
+		return;
+
+	queue_destroy(hci->evt_list, evt_free);
+	queue_destroy(hci->cmd_queue, cmd_free);
+	queue_destroy(hci->rsp_queue, cmd_free);
+
+	io_destroy(hci->io);
+
+	free(hci);
+}
+
+bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close)
+{
+	if (!hci)
+		return false;
+
+	return io_set_close_on_destroy(hci->io, do_close);
+}
+
+unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode,
+				const void *data, uint8_t size,
+				bt_hci_callback_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy)
+{
+	struct cmd *cmd;
+
+	if (!hci)
+		return 0;
+
+	cmd = new0(struct cmd, 1);
+	cmd->opcode = opcode;
+	cmd->size = size;
+
+	if (cmd->size > 0) {
+		cmd->data = malloc(cmd->size);
+		if (!cmd->data) {
+			free(cmd);
+			return 0;
+		}
+
+		memcpy(cmd->data, data, cmd->size);
+	}
+
+	if (hci->next_cmd_id < 1)
+		hci->next_cmd_id = 1;
+
+	cmd->id = hci->next_cmd_id++;
+
+	cmd->callback = callback;
+	cmd->destroy = destroy;
+	cmd->user_data = user_data;
+
+	if (!queue_push_tail(hci->cmd_queue, cmd)) {
+		free(cmd->data);
+		free(cmd);
+		return 0;
+	}
+
+	wakeup_writer(hci);
+
+	return cmd->id;
+}
+
+static bool match_cmd_id(const void *a, const void *b)
+{
+	const struct cmd *cmd = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return cmd->id == id;
+}
+
+bool bt_hci_cancel(struct bt_hci *hci, unsigned int id)
+{
+	struct cmd *cmd;
+
+	if (!hci || !id)
+		return false;
+
+	cmd = queue_remove_if(hci->cmd_queue, match_cmd_id, UINT_TO_PTR(id));
+	if (!cmd) {
+		cmd = queue_remove_if(hci->rsp_queue, match_cmd_id,
+							UINT_TO_PTR(id));
+		if (!cmd)
+			return false;
+	}
+
+	cmd_free(cmd);
+
+	wakeup_writer(hci);
+
+	return true;
+}
+
+bool bt_hci_flush(struct bt_hci *hci)
+{
+	if (!hci)
+		return false;
+
+	if (hci->writer_active) {
+		io_set_write_handler(hci->io, NULL, NULL, NULL);
+		hci->writer_active = false;
+	}
+
+	queue_remove_all(hci->cmd_queue, NULL, NULL, cmd_free);
+	queue_remove_all(hci->rsp_queue, NULL, NULL, cmd_free);
+
+	return true;
+}
+
+unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event,
+				bt_hci_callback_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy)
+{
+	struct evt *evt;
+
+	if (!hci)
+		return 0;
+
+	evt = new0(struct evt, 1);
+	evt->event = event;
+
+	if (hci->next_evt_id < 1)
+		hci->next_evt_id = 1;
+
+	evt->id = hci->next_evt_id++;
+
+	evt->callback = callback;
+	evt->destroy = destroy;
+	evt->user_data = user_data;
+
+	if (!queue_push_tail(hci->evt_list, evt)) {
+		free(evt);
+		return 0;
+	}
+
+	return evt->id;
+}
+
+static bool match_evt_id(const void *a, const void *b)
+{
+	const struct evt *evt = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return evt->id == id;
+}
+
+bool bt_hci_unregister(struct bt_hci *hci, unsigned int id)
+{
+	struct evt *evt;
+
+	if (!hci || !id)
+		return false;
+
+	evt = queue_remove_if(hci->evt_list, match_evt_id, UINT_TO_PTR(id));
+	if (!evt)
+		return false;
+
+	evt_free(evt);
+
+	return true;
+}
diff --git a/bluez/repo/src/shared/hci.h b/bluez/repo/src/shared/hci.h
new file mode 100644
index 0000000..dba0f11
--- /dev/null
+++ b/bluez/repo/src/shared/hci.h
@@ -0,0 +1,53 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+typedef void (*bt_hci_destroy_func_t)(void *user_data);
+
+struct bt_hci;
+
+struct bt_hci *bt_hci_new(int fd);
+struct bt_hci *bt_hci_new_user_channel(uint16_t index);
+struct bt_hci *bt_hci_new_raw_device(uint16_t index);
+
+struct bt_hci *bt_hci_ref(struct bt_hci *hci);
+void bt_hci_unref(struct bt_hci *hci);
+
+bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close);
+
+typedef void (*bt_hci_callback_func_t)(const void *data, uint8_t size,
+							void *user_data);
+
+unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode,
+				const void *data, uint8_t size,
+				bt_hci_callback_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy);
+bool bt_hci_cancel(struct bt_hci *hci, unsigned int id);
+bool bt_hci_flush(struct bt_hci *hci);
+
+unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event,
+				bt_hci_callback_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy);
+bool bt_hci_unregister(struct bt_hci *hci, unsigned int id);
diff --git a/bluez/repo/src/shared/hfp.c b/bluez/repo/src/shared/hfp.c
new file mode 100644
index 0000000..d9f7659
--- /dev/null
+++ b/bluez/repo/src/shared/hfp.c
@@ -0,0 +1,1541 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "src/shared/util.h"
+#include "src/shared/ringbuf.h"
+#include "src/shared/queue.h"
+#include "src/shared/io.h"
+#include "src/shared/hfp.h"
+
+struct hfp_gw {
+	int ref_count;
+	int fd;
+	bool close_on_unref;
+	struct io *io;
+	struct ringbuf *read_buf;
+	struct ringbuf *write_buf;
+	struct queue *cmd_handlers;
+	bool writer_active;
+	bool result_pending;
+	hfp_command_func_t command_callback;
+	hfp_destroy_func_t command_destroy;
+	void *command_data;
+	hfp_debug_func_t debug_callback;
+	hfp_destroy_func_t debug_destroy;
+	void *debug_data;
+
+	hfp_disconnect_func_t disconnect_callback;
+	hfp_destroy_func_t disconnect_destroy;
+	void *disconnect_data;
+
+	bool in_disconnect;
+	bool destroyed;
+};
+
+struct hfp_hf {
+	int ref_count;
+	int fd;
+	bool close_on_unref;
+	struct io *io;
+	struct ringbuf *read_buf;
+	struct ringbuf *write_buf;
+
+	bool writer_active;
+	struct queue *cmd_queue;
+
+	struct queue *event_handlers;
+
+	hfp_debug_func_t debug_callback;
+	hfp_destroy_func_t debug_destroy;
+	void *debug_data;
+
+	hfp_disconnect_func_t disconnect_callback;
+	hfp_destroy_func_t disconnect_destroy;
+	void *disconnect_data;
+
+	bool in_disconnect;
+	bool destroyed;
+};
+
+struct cmd_handler {
+	char *prefix;
+	void *user_data;
+	hfp_destroy_func_t destroy;
+	hfp_result_func_t callback;
+};
+
+struct hfp_context {
+	const char *data;
+	unsigned int offset;
+};
+
+struct cmd_response {
+	hfp_response_func_t resp_cb;
+	struct hfp_context *response;
+	char *resp_data;
+	void *user_data;
+};
+
+struct event_handler {
+	char *prefix;
+	void *user_data;
+	hfp_destroy_func_t destroy;
+	hfp_hf_result_func_t callback;
+};
+
+static void destroy_cmd_handler(void *data)
+{
+	struct cmd_handler *handler = data;
+
+	if (handler->destroy)
+		handler->destroy(handler->user_data);
+
+	free(handler->prefix);
+
+	free(handler);
+}
+
+static bool match_handler_prefix(const void *a, const void *b)
+{
+	const struct cmd_handler *handler = a;
+	const char *prefix = b;
+
+	if (strcmp(handler->prefix, prefix) != 0)
+		return false;
+
+	return true;
+}
+
+static void write_watch_destroy(void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	hfp->writer_active = false;
+}
+
+static bool can_write_data(struct io *io, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+	ssize_t bytes_written;
+
+	bytes_written = ringbuf_write(hfp->write_buf, hfp->fd);
+	if (bytes_written < 0)
+		return false;
+
+	if (ringbuf_len(hfp->write_buf) > 0)
+		return true;
+
+	return false;
+}
+
+static void wakeup_writer(struct hfp_gw *hfp)
+{
+	if (hfp->writer_active)
+		return;
+
+	if (!ringbuf_len(hfp->write_buf))
+		return;
+
+	if (!io_set_write_handler(hfp->io, can_write_data,
+					hfp, write_watch_destroy))
+		return;
+
+	hfp->writer_active = true;
+}
+
+static void skip_whitespace(struct hfp_context *context)
+{
+	while (context->data[context->offset] == ' ')
+		context->offset++;
+}
+
+static void handle_unknown_at_command(struct hfp_gw *hfp,
+							const char *data)
+{
+	if (hfp->command_callback) {
+		hfp->result_pending = true;
+		hfp->command_callback(data, hfp->command_data);
+	} else {
+		hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
+	}
+}
+
+static bool handle_at_command(struct hfp_gw *hfp, const char *data)
+{
+	struct cmd_handler *handler;
+	const char *separators = ";?=\0";
+	struct hfp_context context;
+	enum hfp_gw_cmd_type type;
+	char lookup_prefix[18];
+	uint8_t pref_len = 0;
+	const char *prefix;
+	int i;
+
+	context.offset = 0;
+	context.data = data;
+
+	skip_whitespace(&context);
+
+	if (strlen(data + context.offset) < 3)
+		return false;
+
+	if (strncmp(data + context.offset, "AT", 2))
+		if (strncmp(data + context.offset, "at", 2))
+			return false;
+
+	context.offset += 2;
+	prefix = data + context.offset;
+
+	if (isalpha(prefix[0])) {
+		lookup_prefix[pref_len++] = toupper(prefix[0]);
+	} else {
+		pref_len = strcspn(prefix, separators);
+		if (pref_len > 17 || pref_len < 2)
+			return false;
+
+		for (i = 0; i < pref_len; i++)
+			lookup_prefix[i] = toupper(prefix[i]);
+	}
+
+	lookup_prefix[pref_len] = '\0';
+	context.offset += pref_len;
+
+	if (lookup_prefix[0] == 'D') {
+		type = HFP_GW_CMD_TYPE_SET;
+		goto done;
+	}
+
+	if (data[context.offset] == '=') {
+		context.offset++;
+		if (data[context.offset] == '?') {
+			context.offset++;
+			type = HFP_GW_CMD_TYPE_TEST;
+		} else {
+			type = HFP_GW_CMD_TYPE_SET;
+		}
+		goto done;
+	}
+
+	if (data[context.offset] == '?') {
+		context.offset++;
+		type = HFP_GW_CMD_TYPE_READ;
+		goto done;
+	}
+
+	type = HFP_GW_CMD_TYPE_COMMAND;
+
+done:
+
+	handler = queue_find(hfp->cmd_handlers, match_handler_prefix,
+								lookup_prefix);
+	if (!handler) {
+		handle_unknown_at_command(hfp, data);
+		return true;
+	}
+
+	hfp->result_pending = true;
+	handler->callback(&context, type, handler->user_data);
+
+	return true;
+}
+
+static void next_field(struct hfp_context *context)
+{
+	if (context->data[context->offset] == ',')
+		context->offset++;
+}
+
+bool hfp_context_get_number_default(struct hfp_context *context,
+						unsigned int *val,
+						unsigned int default_val)
+{
+	skip_whitespace(context);
+
+	if (context->data[context->offset] == ',') {
+		if (val)
+			*val = default_val;
+
+		context->offset++;
+		return true;
+	}
+
+	return hfp_context_get_number(context, val);
+}
+
+bool hfp_context_get_number(struct hfp_context *context,
+							unsigned int *val)
+{
+	unsigned int i;
+	int tmp = 0;
+
+	skip_whitespace(context);
+
+	i = context->offset;
+
+	while (context->data[i] >= '0' && context->data[i] <= '9')
+		tmp = tmp * 10 + context->data[i++] - '0';
+
+	if (i == context->offset)
+		return false;
+
+	if (val)
+		*val = tmp;
+	context->offset = i;
+
+	skip_whitespace(context);
+	next_field(context);
+
+	return true;
+}
+
+bool hfp_context_open_container(struct hfp_context *context)
+{
+	skip_whitespace(context);
+
+	/* The list shall be preceded by a left parenthesis "(") */
+	if (context->data[context->offset] != '(')
+		return false;
+
+	context->offset++;
+
+	return true;
+}
+
+bool hfp_context_close_container(struct hfp_context *context)
+{
+	skip_whitespace(context);
+
+	/* The list shall be followed by a right parenthesis (")" V250 5.7.3.1*/
+	if (context->data[context->offset] != ')')
+		return false;
+
+	context->offset++;
+
+	next_field(context);
+
+	return true;
+}
+
+bool hfp_context_get_string(struct hfp_context *context, char *buf,
+								uint8_t len)
+{
+	int i = 0;
+	const char *data = context->data;
+	unsigned int offset;
+
+	skip_whitespace(context);
+
+	if (data[context->offset] != '"')
+		return false;
+
+	offset = context->offset;
+	offset++;
+
+	while (data[offset] != '\0' && data[offset] != '"') {
+		if (i == len)
+			return false;
+
+		buf[i++] = data[offset];
+		offset++;
+	}
+
+	if (i == len)
+		return false;
+
+	buf[i] = '\0';
+
+	if (data[offset] == '"')
+		offset++;
+	else
+		return false;
+
+	context->offset = offset;
+
+	skip_whitespace(context);
+	next_field(context);
+
+	return true;
+}
+
+bool hfp_context_get_unquoted_string(struct hfp_context *context,
+							char *buf, uint8_t len)
+{
+	const char *data = context->data;
+	unsigned int offset;
+	int i = 0;
+	char c;
+
+	skip_whitespace(context);
+
+	c = data[context->offset];
+	if (c == '"' || c == ')' || c == '(')
+		return false;
+
+	offset = context->offset;
+
+	while (data[offset] != '\0' && data[offset] != ',' &&
+							data[offset] != ')') {
+		if (i == len)
+			return false;
+
+		buf[i++] = data[offset];
+		offset++;
+	}
+
+	if (i == len)
+		return false;
+
+	buf[i] = '\0';
+
+	context->offset = offset;
+
+	next_field(context);
+
+	return true;
+}
+
+bool hfp_context_has_next(struct hfp_context *context)
+{
+	return context->data[context->offset] != '\0';
+}
+
+void hfp_context_skip_field(struct hfp_context *context)
+{
+	const char *data = context->data;
+	unsigned int offset = context->offset;
+
+	while (data[offset] != '\0' && data[offset] != ',')
+		offset++;
+
+	context->offset = offset;
+	next_field(context);
+}
+
+bool hfp_context_get_range(struct hfp_context *context, uint32_t *min,
+								uint32_t *max)
+{
+	uint32_t l, h;
+	uint32_t start;
+
+	start = context->offset;
+
+	if (!hfp_context_get_number(context, &l))
+		goto failed;
+
+	if (context->data[context->offset] != '-')
+		goto failed;
+
+	context->offset++;
+
+	if (!hfp_context_get_number(context, &h))
+		goto failed;
+
+	*min = l;
+	*max = h;
+
+	next_field(context);
+
+	return true;
+
+failed:
+	context->offset = start;
+	return false;
+}
+
+static void process_input(struct hfp_gw *hfp)
+{
+	char *str, *ptr;
+	size_t len, count;
+	bool free_ptr = false;
+	bool read_again;
+
+	do {
+		str = ringbuf_peek(hfp->read_buf, 0, &len);
+		if (!str)
+			return;
+
+		ptr = memchr(str, '\r', len);
+		if (!ptr) {
+			char *str2;
+			size_t len2;
+
+			/*
+			 * If there is no more data in ringbuffer,
+			 * it's just an incomplete command.
+			 */
+			if (len == ringbuf_len(hfp->read_buf))
+				return;
+
+			str2 = ringbuf_peek(hfp->read_buf, len, &len2);
+			if (!str2)
+				return;
+
+			ptr = memchr(str2, '\r', len2);
+			if (!ptr)
+				return;
+
+			*ptr = '\0';
+
+			count = len2 + len;
+			ptr = malloc(count);
+			if (!ptr)
+				return;
+
+			memcpy(ptr, str, len);
+			memcpy(ptr + len, str2, len2);
+
+			free_ptr = true;
+			str = ptr;
+		} else {
+			count = ptr - str;
+			*ptr = '\0';
+		}
+
+		if (!handle_at_command(hfp, str))
+			/*
+			 * Command is not handled that means that was some
+			 * trash. Let's skip that and keep reading from ring
+			 * buffer.
+			 */
+			read_again = true;
+		else
+			/*
+			 * Command has been handled. If we are waiting for a
+			 * result from upper layer, we can stop reading. If we
+			 * already reply i.e. ERROR on unknown command, then we
+			 * can keep reading ring buffer. Actually ring buffer
+			 * should be empty but lets just look there.
+			 */
+			read_again = !hfp->result_pending;
+
+		ringbuf_drain(hfp->read_buf, count + 1);
+
+		if (free_ptr)
+			free(ptr);
+
+	} while (read_again);
+}
+
+static void read_watch_destroy(void *user_data)
+{
+}
+
+static bool can_read_data(struct io *io, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+	ssize_t bytes_read;
+
+	bytes_read = ringbuf_read(hfp->read_buf, hfp->fd);
+	if (bytes_read < 0)
+		return false;
+
+	if (hfp->result_pending)
+		return true;
+
+	process_input(hfp);
+
+	return true;
+}
+
+struct hfp_gw *hfp_gw_new(int fd)
+{
+	struct hfp_gw *hfp;
+
+	if (fd < 0)
+		return NULL;
+
+	hfp = new0(struct hfp_gw, 1);
+	hfp->fd = fd;
+	hfp->close_on_unref = false;
+
+	hfp->read_buf = ringbuf_new(4096);
+	if (!hfp->read_buf) {
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->write_buf = ringbuf_new(4096);
+	if (!hfp->write_buf) {
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->io = io_new(fd);
+	if (!hfp->io) {
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->cmd_handlers = queue_new();
+
+	if (!io_set_read_handler(hfp->io, can_read_data, hfp,
+							read_watch_destroy)) {
+		queue_destroy(hfp->cmd_handlers, destroy_cmd_handler);
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->writer_active = false;
+	hfp->result_pending = false;
+
+	return hfp_gw_ref(hfp);
+}
+
+struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp)
+{
+	if (!hfp)
+		return NULL;
+
+	__sync_fetch_and_add(&hfp->ref_count, 1);
+
+	return hfp;
+}
+
+void hfp_gw_unref(struct hfp_gw *hfp)
+{
+	if (!hfp)
+		return;
+
+	if (__sync_sub_and_fetch(&hfp->ref_count, 1))
+		return;
+
+	hfp_gw_set_command_handler(hfp, NULL, NULL, NULL);
+
+	io_set_write_handler(hfp->io, NULL, NULL, NULL);
+	io_set_read_handler(hfp->io, NULL, NULL, NULL);
+	io_set_disconnect_handler(hfp->io, NULL, NULL, NULL);
+
+	io_destroy(hfp->io);
+	hfp->io = NULL;
+
+	if (hfp->close_on_unref)
+		close(hfp->fd);
+
+	hfp_gw_set_debug(hfp, NULL, NULL, NULL);
+
+	ringbuf_free(hfp->read_buf);
+	hfp->read_buf = NULL;
+
+	ringbuf_free(hfp->write_buf);
+	hfp->write_buf = NULL;
+
+	queue_destroy(hfp->cmd_handlers, destroy_cmd_handler);
+	hfp->cmd_handlers = NULL;
+
+	if (!hfp->in_disconnect) {
+		free(hfp);
+		return;
+	}
+
+	hfp->destroyed = true;
+}
+
+static void read_tracing(const void *buf, size_t count, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+static void write_tracing(const void *buf, size_t count, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->debug_destroy)
+		hfp->debug_destroy(hfp->debug_data);
+
+	hfp->debug_callback = callback;
+	hfp->debug_destroy = destroy;
+	hfp->debug_data = user_data;
+
+	if (hfp->debug_callback) {
+		ringbuf_set_input_tracing(hfp->read_buf, read_tracing, hfp);
+		ringbuf_set_input_tracing(hfp->write_buf, write_tracing, hfp);
+	} else {
+		ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL);
+		ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL);
+	}
+
+	return true;
+}
+
+bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close)
+{
+	if (!hfp)
+		return false;
+
+	hfp->close_on_unref = do_close;
+
+	return true;
+}
+
+bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result)
+{
+	const char *str;
+
+	if (!hfp)
+		return false;
+
+	switch (result) {
+	case HFP_RESULT_OK:
+		str = "OK";
+		break;
+	case HFP_RESULT_ERROR:
+		str = "ERROR";
+		break;
+	case HFP_RESULT_RING:
+	case HFP_RESULT_NO_CARRIER:
+	case HFP_RESULT_BUSY:
+	case HFP_RESULT_NO_ANSWER:
+	case HFP_RESULT_DELAYED:
+	case HFP_RESULT_BLACKLISTED:
+	case HFP_RESULT_CME_ERROR:
+	case HFP_RESULT_NO_DIALTONE:
+	case HFP_RESULT_CONNECT:
+	default:
+		return false;
+	}
+
+	if (ringbuf_printf(hfp->write_buf, "\r\n%s\r\n", str) < 0)
+		return false;
+
+	wakeup_writer(hfp);
+
+	/*
+	 * There might be already something to read in the ring buffer.
+	 * If so, let's read it.
+	 */
+	if (hfp->result_pending) {
+		hfp->result_pending = false;
+		process_input(hfp);
+	}
+
+	return true;
+}
+
+bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error)
+{
+	if (!hfp)
+		return false;
+
+	if (ringbuf_printf(hfp->write_buf, "\r\n+CME ERROR: %u\r\n", error) < 0)
+		return false;
+
+	wakeup_writer(hfp);
+
+	/*
+	 * There might be already something to read in the ring buffer.
+	 * If so, let's read it.
+	 */
+	if (hfp->result_pending) {
+		hfp->result_pending = false;
+		process_input(hfp);
+	}
+
+	return true;
+}
+
+bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...)
+{
+	va_list ap;
+	char *fmt;
+	int len;
+
+	if (!hfp || !format)
+		return false;
+
+	if (asprintf(&fmt, "\r\n%s\r\n", format) < 0)
+		return false;
+
+	va_start(ap, format);
+	len = ringbuf_vprintf(hfp->write_buf, fmt, ap);
+	va_end(ap);
+
+	free(fmt);
+
+	if (len < 0)
+		return false;
+
+	if (hfp->result_pending)
+		return true;
+
+	wakeup_writer(hfp);
+
+	return true;
+}
+
+bool hfp_gw_set_command_handler(struct hfp_gw *hfp,
+				hfp_command_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->command_destroy)
+		hfp->command_destroy(hfp->command_data);
+
+	hfp->command_callback = callback;
+	hfp->command_destroy = destroy;
+	hfp->command_data = user_data;
+
+	return true;
+}
+
+bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback,
+						const char *prefix,
+						void *user_data,
+						hfp_destroy_func_t destroy)
+{
+	struct cmd_handler *handler;
+
+	handler = new0(struct cmd_handler, 1);
+	handler->callback = callback;
+	handler->user_data = user_data;
+
+	handler->prefix = strdup(prefix);
+	if (!handler->prefix) {
+		free(handler);
+		return false;
+	}
+
+	if (queue_find(hfp->cmd_handlers, match_handler_prefix,
+							handler->prefix)) {
+		destroy_cmd_handler(handler);
+		return false;
+	}
+
+	handler->destroy = destroy;
+
+	return queue_push_tail(hfp->cmd_handlers, handler);
+}
+
+bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix)
+{
+	struct cmd_handler *handler;
+	char *lookup_prefix;
+
+	lookup_prefix = strdup(prefix);
+	if (!lookup_prefix)
+		return false;
+
+	handler = queue_remove_if(hfp->cmd_handlers, match_handler_prefix,
+								lookup_prefix);
+	free(lookup_prefix);
+
+	if (!handler)
+		return false;
+
+	destroy_cmd_handler(handler);
+
+	return true;
+}
+
+static void disconnect_watch_destroy(void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	if (hfp->disconnect_destroy)
+		hfp->disconnect_destroy(hfp->disconnect_data);
+
+	if (hfp->destroyed)
+		free(hfp);
+}
+
+static bool io_disconnected(struct io *io, void *user_data)
+{
+	struct hfp_gw *hfp = user_data;
+
+	hfp->in_disconnect = true;
+
+	if (hfp->disconnect_callback)
+		hfp->disconnect_callback(hfp->disconnect_data);
+
+	hfp->in_disconnect = false;
+
+	return false;
+}
+
+bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp,
+					hfp_disconnect_func_t callback,
+					void *user_data,
+					hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->disconnect_destroy)
+		hfp->disconnect_destroy(hfp->disconnect_data);
+
+	if (!io_set_disconnect_handler(hfp->io, io_disconnected, hfp,
+						disconnect_watch_destroy)) {
+		hfp->disconnect_callback = NULL;
+		hfp->disconnect_destroy = NULL;
+		hfp->disconnect_data = NULL;
+		return false;
+	}
+
+	hfp->disconnect_callback = callback;
+	hfp->disconnect_destroy = destroy;
+	hfp->disconnect_data = user_data;
+
+	return true;
+}
+
+bool hfp_gw_disconnect(struct hfp_gw *hfp)
+{
+	if (!hfp)
+		return false;
+
+	return io_shutdown(hfp->io);
+}
+
+static bool match_handler_event_prefix(const void *a, const void *b)
+{
+	const struct event_handler *handler = a;
+	const char *prefix = b;
+
+	if (strcmp(handler->prefix, prefix) != 0)
+		return false;
+
+	return true;
+}
+
+static void destroy_event_handler(void *data)
+{
+	struct event_handler *handler = data;
+
+	if (handler->destroy)
+		handler->destroy(handler->user_data);
+
+	free(handler->prefix);
+
+	free(handler);
+}
+
+static bool hf_can_write_data(struct io *io, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+	ssize_t bytes_written;
+
+	bytes_written = ringbuf_write(hfp->write_buf, hfp->fd);
+	if (bytes_written < 0)
+		return false;
+
+	if (ringbuf_len(hfp->write_buf) > 0)
+		return true;
+
+	return false;
+}
+
+static void hf_write_watch_destroy(void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	hfp->writer_active = false;
+}
+
+static void hf_skip_whitespace(struct hfp_context *context)
+{
+	while (context->data[context->offset] == ' ')
+		context->offset++;
+}
+
+static bool is_response(const char *prefix, enum hfp_result *result,
+						enum hfp_error *cme_err,
+						struct hfp_context *context)
+{
+	if (strcmp(prefix, "OK") == 0) {
+		*result = HFP_RESULT_OK;
+		/*
+		 * Set cme_err to 0 as this is not valid when result is not
+		 * CME ERROR
+		 */
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "ERROR") == 0) {
+		*result = HFP_RESULT_ERROR;
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "NO CARRIER") == 0) {
+		*result = HFP_RESULT_NO_CARRIER;
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "NO ANSWER") == 0) {
+		*result = HFP_RESULT_NO_ANSWER;
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "BUSY") == 0) {
+		*result = HFP_RESULT_BUSY;
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "DELAYED") == 0) {
+		*result = HFP_RESULT_DELAYED;
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "BLACKLISTED") == 0) {
+		*result = HFP_RESULT_BLACKLISTED;
+		*cme_err = 0;
+		return true;
+	}
+
+	if (strcmp(prefix, "+CME ERROR") == 0) {
+		uint32_t val;
+
+		*result = HFP_RESULT_CME_ERROR;
+
+		if (hfp_context_get_number(context, &val) &&
+					val <= HFP_ERROR_NETWORK_NOT_ALLOWED)
+			*cme_err = val;
+		else
+			*cme_err = HFP_ERROR_AG_FAILURE;
+
+		return true;
+	}
+
+	return false;
+}
+
+static void hf_wakeup_writer(struct hfp_hf *hfp)
+{
+	if (hfp->writer_active)
+		return;
+
+	if (!ringbuf_len(hfp->write_buf))
+		return;
+
+	if (!io_set_write_handler(hfp->io, hf_can_write_data,
+					hfp, hf_write_watch_destroy))
+		return;
+
+	hfp->writer_active = true;
+}
+
+static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data)
+{
+	struct event_handler *handler;
+	const char *separators = ";:\0";
+	struct hfp_context context;
+	enum hfp_result result;
+	enum hfp_error cme_err;
+	char lookup_prefix[18];
+	uint8_t pref_len = 0;
+	const char *prefix;
+	int i;
+
+	context.offset = 0;
+	context.data = data;
+
+	hf_skip_whitespace(&context);
+
+	if (strlen(data + context.offset) < 2)
+		return;
+
+	prefix = data + context.offset;
+
+	pref_len = strcspn(prefix, separators);
+	if (pref_len > 17 || pref_len < 2)
+		return;
+
+	for (i = 0; i < pref_len; i++)
+		lookup_prefix[i] = toupper(prefix[i]);
+
+	lookup_prefix[pref_len] = '\0';
+	context.offset += pref_len + 1;
+
+	if (is_response(lookup_prefix, &result, &cme_err, &context)) {
+		struct cmd_response *cmd;
+
+		cmd = queue_peek_head(hfp->cmd_queue);
+		if (!cmd)
+			return;
+
+		cmd->resp_cb(result, cme_err, cmd->user_data);
+
+		queue_remove(hfp->cmd_queue, cmd);
+		free(cmd);
+
+		hf_wakeup_writer(hfp);
+		return;
+	}
+
+	handler = queue_find(hfp->event_handlers, match_handler_event_prefix,
+								lookup_prefix);
+	if (!handler)
+		return;
+
+	handler->callback(&context, handler->user_data);
+}
+
+static char *find_cr_lf(char *str, size_t len)
+{
+	char *ptr;
+	size_t count, offset;
+
+	offset = 0;
+
+	ptr = memchr(str, '\r', len);
+	while (ptr) {
+		/*
+		 * Check if there is more data after '\r'. If so check for
+		 * '\n'
+		 */
+		count = ptr - str;
+		if ((count < (len - 1)) && *(ptr + 1) == '\n')
+			return ptr;
+
+		/* There is only '\r'? Let's try to find next one */
+		offset += count + 1;
+
+		if (offset >= len)
+			return NULL;
+
+		ptr = memchr(str + offset, '\r', len - offset);
+	}
+
+	return NULL;
+}
+
+static void hf_process_input(struct hfp_hf *hfp)
+{
+	char *str, *ptr, *str2, *tmp;
+	size_t len, count, offset, len2;
+	bool free_tmp = false;
+
+	str = ringbuf_peek(hfp->read_buf, 0, &len);
+	if (!str)
+		return;
+
+	offset = 0;
+
+	ptr = find_cr_lf(str, len);
+	while (ptr) {
+		count = ptr - (str + offset);
+		if (count == 0) {
+			/* 2 is for <cr><lf> */
+			offset += 2;
+		} else {
+			*ptr = '\0';
+			hf_call_prefix_handler(hfp, str + offset);
+			offset += count + 2;
+		}
+
+		ptr = find_cr_lf(str + offset, len - offset);
+	}
+
+	/*
+	 * Just check if there is no wrapped data in ring buffer.
+	 * Should not happen too often
+	 */
+	if (len == ringbuf_len(hfp->read_buf))
+		goto done;
+
+	str2 = ringbuf_peek(hfp->read_buf, len, &len2);
+	if (!str2)
+		goto done;
+
+	ptr = find_cr_lf(str2, len2);
+	if (!ptr) {
+		/* Might happen that we wrap between \r and \n */
+		ptr = memchr(str2, '\n', len2);
+		if (!ptr)
+			goto done;
+	}
+
+	count = ptr - str2;
+
+	if (count) {
+		*ptr = '\0';
+
+		tmp = malloc(len + count);
+		if (!tmp)
+			goto done;
+
+		/* "str" here is not a string so we need to use memcpy */
+		memcpy(tmp, str, len);
+		memcpy(tmp + len, str2, count);
+
+		free_tmp = true;
+	} else {
+		str[len-1] = '\0';
+		tmp = str;
+	}
+
+	hf_call_prefix_handler(hfp, tmp);
+	offset += count;
+
+done:
+	ringbuf_drain(hfp->read_buf, offset);
+
+	if (free_tmp)
+		free(tmp);
+}
+
+static bool hf_can_read_data(struct io *io, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+	ssize_t bytes_read;
+
+	bytes_read = ringbuf_read(hfp->read_buf, hfp->fd);
+	if (bytes_read < 0)
+		return false;
+
+	hf_process_input(hfp);
+
+	return true;
+}
+
+struct hfp_hf *hfp_hf_new(int fd)
+{
+	struct hfp_hf *hfp;
+
+	if (fd < 0)
+		return NULL;
+
+	hfp = new0(struct hfp_hf, 1);
+	hfp->fd = fd;
+	hfp->close_on_unref = false;
+
+	hfp->read_buf = ringbuf_new(4096);
+	if (!hfp->read_buf) {
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->write_buf = ringbuf_new(4096);
+	if (!hfp->write_buf) {
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->io = io_new(fd);
+	if (!hfp->io) {
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	hfp->event_handlers = queue_new();
+	hfp->cmd_queue = queue_new();
+	hfp->writer_active = false;
+
+	if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp,
+							read_watch_destroy)) {
+		queue_destroy(hfp->event_handlers,
+						destroy_event_handler);
+		io_destroy(hfp->io);
+		ringbuf_free(hfp->write_buf);
+		ringbuf_free(hfp->read_buf);
+		free(hfp);
+		return NULL;
+	}
+
+	return hfp_hf_ref(hfp);
+}
+
+struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp)
+{
+	if (!hfp)
+		return NULL;
+
+	__sync_fetch_and_add(&hfp->ref_count, 1);
+
+	return hfp;
+}
+
+void hfp_hf_unref(struct hfp_hf *hfp)
+{
+	if (!hfp)
+		return;
+
+	if (__sync_sub_and_fetch(&hfp->ref_count, 1))
+		return;
+
+	io_set_write_handler(hfp->io, NULL, NULL, NULL);
+	io_set_read_handler(hfp->io, NULL, NULL, NULL);
+	io_set_disconnect_handler(hfp->io, NULL, NULL, NULL);
+
+	io_destroy(hfp->io);
+	hfp->io = NULL;
+
+	if (hfp->close_on_unref)
+		close(hfp->fd);
+
+	hfp_hf_set_debug(hfp, NULL, NULL, NULL);
+
+	ringbuf_free(hfp->read_buf);
+	hfp->read_buf = NULL;
+
+	ringbuf_free(hfp->write_buf);
+	hfp->write_buf = NULL;
+
+	queue_destroy(hfp->event_handlers, destroy_event_handler);
+	hfp->event_handlers = NULL;
+
+	queue_destroy(hfp->cmd_queue, free);
+	hfp->cmd_queue = NULL;
+
+	if (!hfp->in_disconnect) {
+		free(hfp);
+		return;
+	}
+
+	hfp->destroyed = true;
+}
+
+static void hf_read_tracing(const void *buf, size_t count,
+							void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+static void hf_write_tracing(const void *buf, size_t count,
+							void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->debug_destroy)
+		hfp->debug_destroy(hfp->debug_data);
+
+	hfp->debug_callback = callback;
+	hfp->debug_destroy = destroy;
+	hfp->debug_data = user_data;
+
+	if (hfp->debug_callback) {
+		ringbuf_set_input_tracing(hfp->read_buf, hf_read_tracing, hfp);
+		ringbuf_set_input_tracing(hfp->write_buf, hf_write_tracing,
+									hfp);
+	} else {
+		ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL);
+		ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL);
+	}
+
+	return true;
+}
+
+bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close)
+{
+	if (!hfp)
+		return false;
+
+	hfp->close_on_unref = do_close;
+
+	return true;
+}
+
+bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb,
+				void *user_data, const char *format, ...)
+{
+	va_list ap;
+	char *fmt;
+	int len;
+	struct cmd_response *cmd;
+
+	if (!hfp || !format || !resp_cb)
+		return false;
+
+	if (asprintf(&fmt, "%s\r", format) < 0)
+		return false;
+
+	cmd = new0(struct cmd_response, 1);
+
+	va_start(ap, format);
+	len = ringbuf_vprintf(hfp->write_buf, fmt, ap);
+	va_end(ap);
+
+	free(fmt);
+
+	if (len < 0) {
+		free(cmd);
+		return false;
+	}
+
+	cmd->resp_cb = resp_cb;
+	cmd->user_data = user_data;
+
+	if (!queue_push_tail(hfp->cmd_queue, cmd)) {
+		ringbuf_drain(hfp->write_buf, len);
+		free(cmd);
+		return false;
+	}
+
+	hf_wakeup_writer(hfp);
+
+	return true;
+}
+
+bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
+						const char *prefix,
+						void *user_data,
+						hfp_destroy_func_t destroy)
+{
+	struct event_handler *handler;
+
+	if (!callback)
+		return false;
+
+	handler = new0(struct event_handler, 1);
+	handler->callback = callback;
+	handler->user_data = user_data;
+
+	handler->prefix = strdup(prefix);
+	if (!handler->prefix) {
+		free(handler);
+		return false;
+	}
+
+	if (queue_find(hfp->event_handlers, match_handler_event_prefix,
+							handler->prefix)) {
+		destroy_event_handler(handler);
+		return false;
+	}
+
+	handler->destroy = destroy;
+
+	return queue_push_tail(hfp->event_handlers, handler);
+}
+
+bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix)
+{
+	struct cmd_handler *handler;
+
+	/* Cast to void as queue_remove needs that */
+	handler = queue_remove_if(hfp->event_handlers,
+						match_handler_event_prefix,
+						(void *) prefix);
+
+	if (!handler)
+		return false;
+
+	destroy_event_handler(handler);
+
+	return true;
+}
+
+static void hf_disconnect_watch_destroy(void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	if (hfp->disconnect_destroy)
+		hfp->disconnect_destroy(hfp->disconnect_data);
+
+	if (hfp->destroyed)
+		free(hfp);
+}
+
+static bool hf_io_disconnected(struct io *io, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+
+	hfp->in_disconnect = true;
+
+	if (hfp->disconnect_callback)
+		hfp->disconnect_callback(hfp->disconnect_data);
+
+	hfp->in_disconnect = false;
+
+	return false;
+}
+
+bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp,
+						hfp_disconnect_func_t callback,
+						void *user_data,
+						hfp_destroy_func_t destroy)
+{
+	if (!hfp)
+		return false;
+
+	if (hfp->disconnect_destroy)
+		hfp->disconnect_destroy(hfp->disconnect_data);
+
+	if (!io_set_disconnect_handler(hfp->io, hf_io_disconnected, hfp,
+						hf_disconnect_watch_destroy)) {
+		hfp->disconnect_callback = NULL;
+		hfp->disconnect_destroy = NULL;
+		hfp->disconnect_data = NULL;
+		return false;
+	}
+
+	hfp->disconnect_callback = callback;
+	hfp->disconnect_destroy = destroy;
+	hfp->disconnect_data = user_data;
+
+	return true;
+}
+
+bool hfp_hf_disconnect(struct hfp_hf *hfp)
+{
+	if (!hfp)
+		return false;
+
+	return io_shutdown(hfp->io);
+}
diff --git a/bluez/repo/src/shared/hfp.h b/bluez/repo/src/shared/hfp.h
new file mode 100644
index 0000000..2eb7838
--- /dev/null
+++ b/bluez/repo/src/shared/hfp.h
@@ -0,0 +1,161 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+enum hfp_result {
+	HFP_RESULT_OK		= 0,
+	HFP_RESULT_CONNECT	= 1,
+	HFP_RESULT_RING		= 2,
+	HFP_RESULT_NO_CARRIER	= 3,
+	HFP_RESULT_ERROR	= 4,
+	HFP_RESULT_NO_DIALTONE	= 6,
+	HFP_RESULT_BUSY		= 7,
+	HFP_RESULT_NO_ANSWER	= 8,
+	HFP_RESULT_DELAYED	= 9,
+	HFP_RESULT_BLACKLISTED	= 10,
+	HFP_RESULT_CME_ERROR	= 11,
+};
+
+enum hfp_error {
+	HFP_ERROR_AG_FAILURE			= 0,
+	HFP_ERROR_NO_CONNECTION_TO_PHONE	= 1,
+	HFP_ERROR_OPERATION_NOT_ALLOWED		= 3,
+	HFP_ERROR_OPERATION_NOT_SUPPORTED	= 4,
+	HFP_ERROR_PH_SIM_PIN_REQUIRED		= 5,
+	HFP_ERROR_SIM_NOT_INSERTED		= 10,
+	HFP_ERROR_SIM_PIN_REQUIRED		= 11,
+	HFP_ERROR_SIM_PUK_REQUIRED		= 12,
+	HFP_ERROR_SIM_FAILURE			= 13,
+	HFP_ERROR_SIM_BUSY			= 14,
+	HFP_ERROR_INCORRECT_PASSWORD		= 16,
+	HFP_ERROR_SIM_PIN2_REQUIRED		= 17,
+	HFP_ERROR_SIM_PUK2_REQUIRED		= 18,
+	HFP_ERROR_MEMORY_FULL			= 20,
+	HFP_ERROR_INVALID_INDEX			= 21,
+	HFP_ERROR_MEMORY_FAILURE		= 23,
+	HFP_ERROR_TEXT_STRING_TOO_LONG		= 24,
+	HFP_ERROR_INVALID_CHARS_IN_TEXT_STRING	= 25,
+	HFP_ERROR_DIAL_STRING_TO_LONG		= 26,
+	HFP_ERROR_INVALID_CHARS_IN_DIAL_STRING	= 27,
+	HFP_ERROR_NO_NETWORK_SERVICE		= 30,
+	HFP_ERROR_NETWORK_TIMEOUT		= 31,
+	HFP_ERROR_NETWORK_NOT_ALLOWED		= 32,
+};
+
+enum hfp_gw_cmd_type {
+	HFP_GW_CMD_TYPE_READ,
+	HFP_GW_CMD_TYPE_SET,
+	HFP_GW_CMD_TYPE_TEST,
+	HFP_GW_CMD_TYPE_COMMAND
+};
+
+struct hfp_context;
+
+typedef void (*hfp_result_func_t)(struct hfp_context *context,
+				enum hfp_gw_cmd_type type, void *user_data);
+
+typedef void (*hfp_destroy_func_t)(void *user_data);
+typedef void (*hfp_debug_func_t)(const char *str, void *user_data);
+
+typedef void (*hfp_command_func_t)(const char *command, void *user_data);
+typedef void (*hfp_disconnect_func_t)(void *user_data);
+
+struct hfp_gw;
+
+struct hfp_gw *hfp_gw_new(int fd);
+
+struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp);
+void hfp_gw_unref(struct hfp_gw *hfp);
+
+bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy);
+
+bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close);
+bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive);
+
+bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result);
+bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error);
+bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+
+bool hfp_gw_set_command_handler(struct hfp_gw *hfp,
+				hfp_command_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy);
+
+bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp,
+					hfp_disconnect_func_t callback,
+					void *user_data,
+					hfp_destroy_func_t destroy);
+
+bool hfp_gw_disconnect(struct hfp_gw *hfp);
+
+bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback,
+						const char *prefix,
+						void *user_data,
+						hfp_destroy_func_t destroy);
+bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix);
+
+bool hfp_context_get_number(struct hfp_context *context,
+							unsigned int *val);
+bool hfp_context_get_number_default(struct hfp_context *context,
+						unsigned int *val,
+						unsigned int default_val);
+bool hfp_context_open_container(struct hfp_context *context);
+bool hfp_context_close_container(struct hfp_context *context);
+bool hfp_context_get_string(struct hfp_context *context, char *buf,
+								uint8_t len);
+bool hfp_context_get_unquoted_string(struct hfp_context *context,
+						char *buf, uint8_t len);
+bool hfp_context_get_range(struct hfp_context *context, unsigned int *min,
+							unsigned int *max);
+bool hfp_context_has_next(struct hfp_context *context);
+void hfp_context_skip_field(struct hfp_context *context);
+
+typedef void (*hfp_hf_result_func_t)(struct hfp_context *context,
+							void *user_data);
+
+typedef void (*hfp_response_func_t)(enum hfp_result result,
+							enum hfp_error cme_err,
+							void *user_data);
+
+struct hfp_hf;
+
+struct hfp_hf *hfp_hf_new(int fd);
+
+struct hfp_hf *hfp_hf_ref(struct hfp_hf *hfp);
+void hfp_hf_unref(struct hfp_hf *hfp);
+bool hfp_hf_set_debug(struct hfp_hf *hfp, hfp_debug_func_t callback,
+				void *user_data, hfp_destroy_func_t destroy);
+bool hfp_hf_set_close_on_unref(struct hfp_hf *hfp, bool do_close);
+bool hfp_hf_set_disconnect_handler(struct hfp_hf *hfp,
+					hfp_disconnect_func_t callback,
+					void *user_data,
+					hfp_destroy_func_t destroy);
+bool hfp_hf_disconnect(struct hfp_hf *hfp);
+bool hfp_hf_register(struct hfp_hf *hfp, hfp_hf_result_func_t callback,
+					const char *prefix, void *user_data,
+					hfp_destroy_func_t destroy);
+bool hfp_hf_unregister(struct hfp_hf *hfp, const char *prefix);
+bool hfp_hf_send_command(struct hfp_hf *hfp, hfp_response_func_t resp_cb,
+				void *user_data, const char *format, ...);
diff --git a/bluez/repo/src/shared/io-glib.c b/bluez/repo/src/shared/io-glib.c
new file mode 100644
index 0000000..6687a6b
--- /dev/null
+++ b/bluez/repo/src/shared/io-glib.c
@@ -0,0 +1,290 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <glib.h>
+
+#include "src/shared/io.h"
+
+struct io_watch {
+	struct io *io;
+	guint id;
+	io_callback_func_t callback;
+	io_destroy_func_t destroy;
+	void *user_data;
+};
+
+struct io {
+	int ref_count;
+	GIOChannel *channel;
+	struct io_watch *read_watch;
+	struct io_watch *write_watch;
+	struct io_watch *disconnect_watch;
+};
+
+static struct io *io_ref(struct io *io)
+{
+	if (!io)
+		return NULL;
+
+	__sync_fetch_and_add(&io->ref_count, 1);
+
+	return io;
+}
+
+static void io_unref(struct io *io)
+{
+	if (!io)
+		return;
+
+	if (__sync_sub_and_fetch(&io->ref_count, 1))
+		return;
+
+	g_free(io);
+}
+
+struct io *io_new(int fd)
+{
+	struct io *io;
+
+	if (fd < 0)
+		return NULL;
+
+	io = g_try_new0(struct io, 1);
+	if (!io)
+		return NULL;
+
+	io->channel = g_io_channel_unix_new(fd);
+
+	g_io_channel_set_encoding(io->channel, NULL, NULL);
+	g_io_channel_set_buffered(io->channel, FALSE);
+
+	g_io_channel_set_close_on_unref(io->channel, FALSE);
+
+	return io_ref(io);
+}
+
+static void watch_destroy(void *user_data)
+{
+	struct io_watch *watch = user_data;
+	struct io *io = watch->io;
+
+	if (watch == io->read_watch)
+		io->read_watch = NULL;
+	else if (watch == io->write_watch)
+		io->write_watch = NULL;
+	else if (watch == io->disconnect_watch)
+		io->disconnect_watch = NULL;
+
+	if (watch->destroy)
+		watch->destroy(watch->user_data);
+
+	io_unref(watch->io);
+	g_free(watch);
+}
+
+void io_destroy(struct io *io)
+{
+	if (!io)
+		return;
+
+	if (io->read_watch) {
+		g_source_remove(io->read_watch->id);
+		io->read_watch = NULL;
+	}
+
+	if (io->write_watch) {
+		g_source_remove(io->write_watch->id);
+		io->write_watch = NULL;
+	}
+
+	if (io->disconnect_watch) {
+		g_source_remove(io->disconnect_watch->id);
+		io->disconnect_watch = NULL;
+	}
+
+	g_io_channel_unref(io->channel);
+	io->channel = NULL;
+
+	io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+	if (!io)
+		return -ENOTCONN;
+
+	return g_io_channel_unix_get_fd(io->channel);
+}
+
+bool io_set_close_on_destroy(struct io *io, bool do_close)
+{
+	if (!io)
+		return false;
+
+	if (do_close)
+		g_io_channel_set_close_on_unref(io->channel, TRUE);
+	else
+		g_io_channel_set_close_on_unref(io->channel, FALSE);
+
+	return true;
+}
+
+static gboolean watch_callback(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct io_watch *watch = user_data;
+	bool result, destroy;
+
+	destroy = watch == watch->io->disconnect_watch;
+
+	if (!destroy && (cond & (G_IO_ERR | G_IO_NVAL)))
+		return FALSE;
+
+	if (watch->callback)
+		result = watch->callback(watch->io, watch->user_data);
+	else
+		result = false;
+
+	return result ? TRUE : FALSE;
+}
+
+static struct io_watch *watch_new(struct io *io, GIOCondition cond,
+				io_callback_func_t callback, void *user_data,
+				io_destroy_func_t destroy)
+{
+	struct io_watch *watch;
+
+	watch = g_try_new0(struct io_watch, 1);
+	if (!watch)
+		return NULL;
+
+	watch->io = io_ref(io);
+	watch->callback = callback;
+	watch->destroy = destroy;
+	watch->user_data = user_data;
+
+	watch->id = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT,
+						cond | G_IO_ERR | G_IO_NVAL,
+						watch_callback, watch,
+						watch_destroy);
+	if (watch->id == 0) {
+		watch_destroy(watch);
+		return NULL;
+	}
+
+	return watch;
+}
+
+static bool io_set_handler(struct io *io, GIOCondition cond,
+				io_callback_func_t callback, void *user_data,
+				io_destroy_func_t destroy)
+{
+	struct io_watch **watch;
+
+	if (!io)
+		return false;
+
+	switch (cond) {
+	case G_IO_IN:
+		watch = &io->read_watch;
+		break;
+	case G_IO_OUT:
+		watch = &io->write_watch;
+		break;
+	case G_IO_HUP:
+		watch = &io->disconnect_watch;
+		break;
+	case G_IO_PRI:
+	case G_IO_ERR:
+	case G_IO_NVAL:
+	default:
+		return false;
+	}
+
+	if (*watch) {
+		g_source_remove((*watch)->id);
+		*watch = NULL;
+	}
+
+	if (!callback)
+		return true;
+
+	*watch = watch_new(io, cond, callback, user_data, destroy);
+	if (!*watch)
+		return false;
+
+	return true;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	return io_set_handler(io, G_IO_IN, callback, user_data, destroy);
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	return io_set_handler(io, G_IO_OUT, callback, user_data, destroy);
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	return io_set_handler(io, G_IO_HUP, callback, user_data, destroy);
+}
+
+ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
+{
+	int fd;
+	ssize_t ret;
+
+	if (!io || !io->channel)
+		return -ENOTCONN;
+
+	fd = io_get_fd(io);
+
+	do {
+		ret = writev(fd, iov, iovcnt);
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret < 0)
+		return -errno;
+
+	return ret;
+}
+
+bool io_shutdown(struct io *io)
+{
+	if (!io || !io->channel)
+		return false;
+
+	return g_io_channel_shutdown(io->channel, TRUE, NULL)
+							== G_IO_STATUS_NORMAL;
+}
diff --git a/bluez/repo/src/shared/io-mainloop.c b/bluez/repo/src/shared/io-mainloop.c
new file mode 100644
index 0000000..2306c34
--- /dev/null
+++ b/bluez/repo/src/shared/io-mainloop.c
@@ -0,0 +1,324 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/io.h"
+
+struct io {
+	int ref_count;
+	int fd;
+	uint32_t events;
+	bool close_on_destroy;
+	io_callback_func_t read_callback;
+	io_destroy_func_t read_destroy;
+	void *read_data;
+	io_callback_func_t write_callback;
+	io_destroy_func_t write_destroy;
+	void *write_data;
+	io_callback_func_t disconnect_callback;
+	io_destroy_func_t disconnect_destroy;
+	void *disconnect_data;
+};
+
+static struct io *io_ref(struct io *io)
+{
+	if (!io)
+		return NULL;
+
+	__sync_fetch_and_add(&io->ref_count, 1);
+
+	return io;
+}
+
+static void io_unref(struct io *io)
+{
+	if (!io)
+		return;
+
+	if (__sync_sub_and_fetch(&io->ref_count, 1))
+		return;
+
+	free(io);
+}
+
+static void io_cleanup(void *user_data)
+{
+	struct io *io = user_data;
+
+	if (io->write_destroy)
+		io->write_destroy(io->write_data);
+
+	if (io->read_destroy)
+		io->read_destroy(io->read_data);
+
+	if (io->disconnect_destroy)
+		io->disconnect_destroy(io->disconnect_data);
+
+	if (io->close_on_destroy)
+		close(io->fd);
+
+	io->fd = -1;
+}
+
+static void io_callback(int fd, uint32_t events, void *user_data)
+{
+	struct io *io = user_data;
+
+	io_ref(io);
+
+	if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) {
+		io->read_callback = NULL;
+		io->write_callback = NULL;
+
+		if (!io->disconnect_callback) {
+			mainloop_remove_fd(io->fd);
+			io_unref(io);
+			return;
+		}
+
+		if (!io->disconnect_callback(io, io->disconnect_data)) {
+			if (io->disconnect_destroy)
+				io->disconnect_destroy(io->disconnect_data);
+
+			io->disconnect_callback = NULL;
+			io->disconnect_destroy = NULL;
+			io->disconnect_data = NULL;
+
+			io->events &= ~EPOLLRDHUP;
+
+			mainloop_modify_fd(io->fd, io->events);
+		}
+	}
+
+	if ((events & EPOLLIN) && io->read_callback) {
+		if (!io->read_callback(io, io->read_data)) {
+			if (io->read_destroy)
+				io->read_destroy(io->read_data);
+
+			io->read_callback = NULL;
+			io->read_destroy = NULL;
+			io->read_data = NULL;
+
+			io->events &= ~EPOLLIN;
+
+			mainloop_modify_fd(io->fd, io->events);
+		}
+	}
+
+	if ((events & EPOLLOUT) && io->write_callback) {
+		if (!io->write_callback(io, io->write_data)) {
+			if (io->write_destroy)
+				io->write_destroy(io->write_data);
+
+			io->write_callback = NULL;
+			io->write_destroy = NULL;
+			io->write_data = NULL;
+
+			io->events &= ~EPOLLOUT;
+
+			mainloop_modify_fd(io->fd, io->events);
+		}
+	}
+
+	io_unref(io);
+}
+
+struct io *io_new(int fd)
+{
+	struct io *io;
+
+	if (fd < 0)
+		return NULL;
+
+	io = new0(struct io, 1);
+	io->fd = fd;
+	io->events = 0;
+	io->close_on_destroy = false;
+
+	if (mainloop_add_fd(io->fd, io->events, io_callback,
+						io, io_cleanup) < 0) {
+		free(io);
+		return NULL;
+	}
+
+	return io_ref(io);
+}
+
+void io_destroy(struct io *io)
+{
+	if (!io)
+		return;
+
+	io->read_callback = NULL;
+	io->write_callback = NULL;
+	io->disconnect_callback = NULL;
+
+	mainloop_remove_fd(io->fd);
+
+	io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+	if (!io)
+		return -ENOTCONN;
+
+	return io->fd;
+}
+
+bool io_set_close_on_destroy(struct io *io, bool do_close)
+{
+	if (!io)
+		return false;
+
+	io->close_on_destroy = do_close;
+
+	return true;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	uint32_t events;
+
+	if (!io || io->fd < 0)
+		return false;
+
+	if (io->read_destroy)
+		io->read_destroy(io->read_data);
+
+	if (callback)
+		events = io->events | EPOLLIN;
+	else
+		events = io->events & ~EPOLLIN;
+
+	io->read_callback = callback;
+	io->read_destroy = destroy;
+	io->read_data = user_data;
+
+	if (events == io->events)
+		return true;
+
+	if (mainloop_modify_fd(io->fd, events) < 0)
+		return false;
+
+	io->events = events;
+
+	return true;
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	uint32_t events;
+
+	if (!io || io->fd < 0)
+		return false;
+
+	if (io->write_destroy)
+		io->write_destroy(io->write_data);
+
+	if (callback)
+		events = io->events | EPOLLOUT;
+	else
+		events = io->events & ~EPOLLOUT;
+
+	io->write_callback = callback;
+	io->write_destroy = destroy;
+	io->write_data = user_data;
+
+	if (events == io->events)
+		return true;
+
+	if (mainloop_modify_fd(io->fd, events) < 0)
+		return false;
+
+	io->events = events;
+
+	return true;
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy)
+{
+	uint32_t events;
+
+	if (!io || io->fd < 0)
+		return false;
+
+	if (io->disconnect_destroy)
+		io->disconnect_destroy(io->disconnect_data);
+
+	if (callback)
+		events = io->events | EPOLLRDHUP;
+	else
+		events = io->events & ~EPOLLRDHUP;
+
+	io->disconnect_callback = callback;
+	io->disconnect_destroy = destroy;
+	io->disconnect_data = user_data;
+
+	if (events == io->events)
+		return true;
+
+	if (mainloop_modify_fd(io->fd, events) < 0)
+		return false;
+
+	io->events = events;
+
+	return true;
+}
+
+ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt)
+{
+	ssize_t ret;
+
+	if (!io || io->fd < 0)
+		return -ENOTCONN;
+
+	do {
+		ret = writev(io->fd, iov, iovcnt);
+	} while (ret < 0 && errno == EINTR);
+
+	if (ret < 0)
+		return -errno;
+
+	return ret;
+}
+
+bool io_shutdown(struct io *io)
+{
+	if (!io || io->fd < 0)
+		return false;
+
+	return shutdown(io->fd, SHUT_RDWR) == 0;
+}
diff --git a/bluez/repo/src/shared/io.h b/bluez/repo/src/shared/io.h
new file mode 100644
index 0000000..8bc1111
--- /dev/null
+++ b/bluez/repo/src/shared/io.h
@@ -0,0 +1,47 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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/uio.h>
+
+typedef void (*io_destroy_func_t)(void *data);
+
+struct io;
+
+struct io *io_new(int fd);
+void io_destroy(struct io *io);
+
+int io_get_fd(struct io *io);
+bool io_set_close_on_destroy(struct io *io, bool do_close);
+
+ssize_t io_send(struct io *io, const struct iovec *iov, int iovcnt);
+bool io_shutdown(struct io *io);
+
+typedef bool (*io_callback_func_t)(struct io *io, void *user_data);
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy);
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy);
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+				void *user_data, io_destroy_func_t destroy);
diff --git a/bluez/repo/src/shared/mainloop.c b/bluez/repo/src/shared/mainloop.c
new file mode 100644
index 0000000..9fff615
--- /dev/null
+++ b/bluez/repo/src/shared/mainloop.c
@@ -0,0 +1,402 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+
+#include "mainloop.h"
+
+#define MAX_EPOLL_EVENTS 10
+
+static int epoll_fd;
+static int epoll_terminate;
+static int exit_status;
+
+struct mainloop_data {
+	int fd;
+	uint32_t events;
+	mainloop_event_func callback;
+	mainloop_destroy_func destroy;
+	void *user_data;
+};
+
+#define MAX_MAINLOOP_ENTRIES 128
+
+static struct mainloop_data *mainloop_list[MAX_MAINLOOP_ENTRIES];
+
+struct timeout_data {
+	int fd;
+	mainloop_timeout_func callback;
+	mainloop_destroy_func destroy;
+	void *user_data;
+};
+
+struct signal_data {
+	int fd;
+	sigset_t mask;
+	mainloop_signal_func callback;
+	mainloop_destroy_func destroy;
+	void *user_data;
+};
+
+static struct signal_data *signal_data;
+
+void mainloop_init(void)
+{
+	unsigned int i;
+
+	epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+	for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++)
+		mainloop_list[i] = NULL;
+
+	epoll_terminate = 0;
+}
+
+void mainloop_quit(void)
+{
+	epoll_terminate = 1;
+}
+
+void mainloop_exit_success(void)
+{
+	exit_status = EXIT_SUCCESS;
+	epoll_terminate = 1;
+}
+
+void mainloop_exit_failure(void)
+{
+	exit_status = EXIT_FAILURE;
+	epoll_terminate = 1;
+}
+
+static void signal_callback(int fd, uint32_t events, void *user_data)
+{
+	struct signal_data *data = user_data;
+	struct signalfd_siginfo si;
+	ssize_t result;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_quit();
+		return;
+	}
+
+	result = read(fd, &si, sizeof(si));
+	if (result != sizeof(si))
+		return;
+
+	if (data->callback)
+		data->callback(si.ssi_signo, data->user_data);
+}
+
+int mainloop_run(void)
+{
+	unsigned int i;
+
+	if (signal_data) {
+		if (sigprocmask(SIG_BLOCK, &signal_data->mask, NULL) < 0)
+			return EXIT_FAILURE;
+
+		signal_data->fd = signalfd(-1, &signal_data->mask,
+						SFD_NONBLOCK | SFD_CLOEXEC);
+		if (signal_data->fd < 0)
+			return EXIT_FAILURE;
+
+		if (mainloop_add_fd(signal_data->fd, EPOLLIN,
+				signal_callback, signal_data, NULL) < 0) {
+			close(signal_data->fd);
+			return EXIT_FAILURE;
+		}
+	}
+
+	exit_status = EXIT_SUCCESS;
+
+	while (!epoll_terminate) {
+		struct epoll_event events[MAX_EPOLL_EVENTS];
+		int n, nfds;
+
+		nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
+		if (nfds < 0)
+			continue;
+
+		for (n = 0; n < nfds; n++) {
+			struct mainloop_data *data = events[n].data.ptr;
+
+			data->callback(data->fd, events[n].events,
+							data->user_data);
+		}
+	}
+
+	if (signal_data) {
+		mainloop_remove_fd(signal_data->fd);
+		close(signal_data->fd);
+
+		if (signal_data->destroy)
+			signal_data->destroy(signal_data->user_data);
+	}
+
+	for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) {
+		struct mainloop_data *data = mainloop_list[i];
+
+		mainloop_list[i] = NULL;
+
+		if (data) {
+			epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+			if (data->destroy)
+				data->destroy(data->user_data);
+
+			free(data);
+		}
+	}
+
+	close(epoll_fd);
+	epoll_fd = 0;
+
+	return exit_status;
+}
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+				void *user_data, mainloop_destroy_func destroy)
+{
+	struct mainloop_data *data;
+	struct epoll_event ev;
+	int err;
+
+	if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback)
+		return -EINVAL;
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		return -ENOMEM;
+
+	memset(data, 0, sizeof(*data));
+	data->fd = fd;
+	data->events = events;
+	data->callback = callback;
+	data->destroy = destroy;
+	data->user_data = user_data;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.events = events;
+	ev.data.ptr = data;
+
+	err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
+	if (err < 0) {
+		free(data);
+		return err;
+	}
+
+	mainloop_list[fd] = data;
+
+	return 0;
+}
+
+int mainloop_modify_fd(int fd, uint32_t events)
+{
+	struct mainloop_data *data;
+	struct epoll_event ev;
+	int err;
+
+	if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+		return -EINVAL;
+
+	data = mainloop_list[fd];
+	if (!data)
+		return -ENXIO;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.events = events;
+	ev.data.ptr = data;
+
+	err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev);
+	if (err < 0)
+		return err;
+
+	data->events = events;
+
+	return 0;
+}
+
+int mainloop_remove_fd(int fd)
+{
+	struct mainloop_data *data;
+	int err;
+
+	if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+		return -EINVAL;
+
+	data = mainloop_list[fd];
+	if (!data)
+		return -ENXIO;
+
+	mainloop_list[fd] = NULL;
+
+	err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+
+	free(data);
+
+	return err;
+}
+
+static void timeout_destroy(void *user_data)
+{
+	struct timeout_data *data = user_data;
+
+	close(data->fd);
+	data->fd = -1;
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+}
+
+static void timeout_callback(int fd, uint32_t events, void *user_data)
+{
+	struct timeout_data *data = user_data;
+	uint64_t expired;
+	ssize_t result;
+
+	if (events & (EPOLLERR | EPOLLHUP))
+		return;
+
+	result = read(data->fd, &expired, sizeof(expired));
+	if (result != sizeof(expired))
+		return;
+
+	if (data->callback)
+		data->callback(data->fd, data->user_data);
+}
+
+static inline int timeout_set(int fd, unsigned int msec)
+{
+	struct itimerspec itimer;
+	unsigned int sec = msec / 1000;
+
+	memset(&itimer, 0, sizeof(itimer));
+	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;
+
+	return timerfd_settime(fd, 0, &itimer, NULL);
+}
+
+int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
+				void *user_data, mainloop_destroy_func destroy)
+{
+	struct timeout_data *data;
+
+	if (!callback)
+		return -EINVAL;
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		return -ENOMEM;
+
+	memset(data, 0, sizeof(*data));
+	data->callback = callback;
+	data->destroy = destroy;
+	data->user_data = user_data;
+
+	data->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+	if (data->fd < 0) {
+		free(data);
+		return -EIO;
+	}
+
+	if (msec > 0) {
+		if (timeout_set(data->fd, msec) < 0) {
+			close(data->fd);
+			free(data);
+			return -EIO;
+		}
+	}
+
+	if (mainloop_add_fd(data->fd, EPOLLIN | EPOLLONESHOT,
+				timeout_callback, data, timeout_destroy) < 0) {
+		close(data->fd);
+		free(data);
+		return -EIO;
+	}
+
+	return data->fd;
+}
+
+int mainloop_modify_timeout(int id, unsigned int msec)
+{
+	if (msec > 0) {
+		if (timeout_set(id, msec) < 0)
+			return -EIO;
+	}
+
+	if (mainloop_modify_fd(id, EPOLLIN | EPOLLONESHOT) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+int mainloop_remove_timeout(int id)
+{
+	return mainloop_remove_fd(id);
+}
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+				void *user_data, mainloop_destroy_func destroy)
+{
+	struct signal_data *data;
+
+	if (!mask || !callback)
+		return -EINVAL;
+
+	data = malloc(sizeof(*data));
+	if (!data)
+		return -ENOMEM;
+
+	memset(data, 0, sizeof(*data));
+	data->callback = callback;
+	data->destroy = destroy;
+	data->user_data = user_data;
+
+	data->fd = -1;
+	memcpy(&data->mask, mask, sizeof(sigset_t));
+
+	free(signal_data);
+	signal_data = data;
+
+	return 0;
+}
diff --git a/bluez/repo/src/shared/mainloop.h b/bluez/repo/src/shared/mainloop.h
new file mode 100644
index 0000000..b83caab
--- /dev/null
+++ b/bluez/repo/src/shared/mainloop.h
@@ -0,0 +1,51 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <signal.h>
+#include <sys/epoll.h>
+
+typedef void (*mainloop_destroy_func) (void *user_data);
+
+typedef void (*mainloop_event_func) (int fd, uint32_t events, void *user_data);
+typedef void (*mainloop_timeout_func) (int id, void *user_data);
+typedef void (*mainloop_signal_func) (int signum, void *user_data);
+
+void mainloop_init(void);
+void mainloop_quit(void);
+void mainloop_exit_success(void);
+void mainloop_exit_failure(void);
+int mainloop_run(void);
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+				void *user_data, mainloop_destroy_func destroy);
+int mainloop_modify_fd(int fd, uint32_t events);
+int mainloop_remove_fd(int fd);
+
+int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
+				void *user_data, mainloop_destroy_func destroy);
+int mainloop_modify_timeout(int fd, unsigned int msec);
+int mainloop_remove_timeout(int id);
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+				void *user_data, mainloop_destroy_func destroy);
diff --git a/bluez/repo/src/shared/mgmt.c b/bluez/repo/src/shared/mgmt.c
new file mode 100644
index 0000000..277e361
--- /dev/null
+++ b/bluez/repo/src/shared/mgmt.c
@@ -0,0 +1,801 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <string.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+#include "lib/hci.h"
+
+#include "src/shared/io.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+
+struct mgmt {
+	int ref_count;
+	int fd;
+	bool close_on_unref;
+	struct io *io;
+	bool writer_active;
+	struct queue *request_queue;
+	struct queue *reply_queue;
+	struct queue *pending_list;
+	struct queue *notify_list;
+	unsigned int next_request_id;
+	unsigned int next_notify_id;
+	bool need_notify_cleanup;
+	bool in_notify;
+	void *buf;
+	uint16_t len;
+	mgmt_debug_func_t debug_callback;
+	mgmt_destroy_func_t debug_destroy;
+	void *debug_data;
+};
+
+struct mgmt_request {
+	unsigned int id;
+	uint16_t opcode;
+	uint16_t index;
+	void *buf;
+	uint16_t len;
+	mgmt_request_func_t callback;
+	mgmt_destroy_func_t destroy;
+	void *user_data;
+};
+
+struct mgmt_notify {
+	unsigned int id;
+	uint16_t event;
+	uint16_t index;
+	bool removed;
+	mgmt_notify_func_t callback;
+	mgmt_destroy_func_t destroy;
+	void *user_data;
+};
+
+static void destroy_request(void *data)
+{
+	struct mgmt_request *request = data;
+
+	if (request->destroy)
+		request->destroy(request->user_data);
+
+	free(request->buf);
+	free(request);
+}
+
+static bool match_request_id(const void *a, const void *b)
+{
+	const struct mgmt_request *request = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return request->id == id;
+}
+
+static bool match_request_index(const void *a, const void *b)
+{
+	const struct mgmt_request *request = a;
+	uint16_t index = PTR_TO_UINT(b);
+
+	return request->index == index;
+}
+
+static void destroy_notify(void *data)
+{
+	struct mgmt_notify *notify = data;
+
+	if (notify->destroy)
+		notify->destroy(notify->user_data);
+
+	free(notify);
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+	const struct mgmt_notify *notify = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notify->id == id;
+}
+
+static bool match_notify_index(const void *a, const void *b)
+{
+	const struct mgmt_notify *notify = a;
+	uint16_t index = PTR_TO_UINT(b);
+
+	return notify->index == index;
+}
+
+static bool match_notify_removed(const void *a, const void *b)
+{
+	const struct mgmt_notify *notify = a;
+
+	return notify->removed;
+}
+
+static void mark_notify_removed(void *data , void *user_data)
+{
+	struct mgmt_notify *notify = data;
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (notify->index == index || index == MGMT_INDEX_NONE)
+		notify->removed = true;
+}
+
+static void write_watch_destroy(void *user_data)
+{
+	struct mgmt *mgmt = user_data;
+
+	mgmt->writer_active = false;
+}
+
+static bool send_request(struct mgmt *mgmt, struct mgmt_request *request)
+{
+	struct iovec iov;
+	ssize_t ret;
+
+	iov.iov_base = request->buf;
+	iov.iov_len = request->len;
+
+	ret = io_send(mgmt->io, &iov, 1);
+	if (ret < 0) {
+		util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"write failed: %s", strerror(-ret));
+		if (request->callback)
+			request->callback(MGMT_STATUS_FAILED, 0, NULL,
+							request->user_data);
+		destroy_request(request);
+		return false;
+	}
+
+	util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"[0x%04x] command 0x%04x",
+				request->index, request->opcode);
+
+	util_hexdump('<', request->buf, ret, mgmt->debug_callback,
+							mgmt->debug_data);
+
+	queue_push_tail(mgmt->pending_list, request);
+
+	return true;
+}
+
+static bool can_write_data(struct io *io, void *user_data)
+{
+	struct mgmt *mgmt = user_data;
+	struct mgmt_request *request;
+	bool can_write;
+
+	request = queue_pop_head(mgmt->reply_queue);
+	if (!request) {
+		/* only reply commands can jump the queue */
+		if (!queue_isempty(mgmt->pending_list))
+			return false;
+
+		request = queue_pop_head(mgmt->request_queue);
+		if (!request)
+			return false;
+
+		can_write = false;
+	} else {
+		/* allow multiple replies to jump the queue */
+		can_write = !queue_isempty(mgmt->reply_queue);
+	}
+
+	if (!send_request(mgmt, request))
+		return true;
+
+	return can_write;
+}
+
+static void wakeup_writer(struct mgmt *mgmt)
+{
+	if (!queue_isempty(mgmt->pending_list)) {
+		/* only queued reply commands trigger wakeup */
+		if (queue_isempty(mgmt->reply_queue))
+			return;
+	}
+
+	if (mgmt->writer_active)
+		return;
+
+	mgmt->writer_active = true;
+
+	io_set_write_handler(mgmt->io, can_write_data, mgmt,
+						write_watch_destroy);
+}
+
+struct opcode_index {
+	uint16_t opcode;
+	uint16_t index;
+};
+
+static bool match_request_opcode_index(const void *a, const void *b)
+{
+	const struct mgmt_request *request = a;
+	const struct opcode_index *match = b;
+
+	return request->opcode == match->opcode &&
+					request->index == match->index;
+}
+
+static void request_complete(struct mgmt *mgmt, uint8_t status,
+					uint16_t opcode, uint16_t index,
+					uint16_t length, const void *param)
+{
+	struct opcode_index match = { .opcode = opcode, .index = index };
+	struct mgmt_request *request;
+
+	request = queue_remove_if(mgmt->pending_list,
+					match_request_opcode_index, &match);
+	if (request) {
+		if (request->callback)
+			request->callback(status, length, param,
+							request->user_data);
+
+		destroy_request(request);
+	}
+
+	wakeup_writer(mgmt);
+}
+
+struct event_index {
+	uint16_t event;
+	uint16_t index;
+	uint16_t length;
+	const void *param;
+};
+
+static void notify_handler(void *data, void *user_data)
+{
+	struct mgmt_notify *notify = data;
+	struct event_index *match = user_data;
+
+	if (notify->removed)
+		return;
+
+	if (notify->event != match->event)
+		return;
+
+	if (notify->index != match->index && notify->index != MGMT_INDEX_NONE)
+		return;
+
+	if (notify->callback)
+		notify->callback(match->index, match->length, match->param,
+							notify->user_data);
+}
+
+static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
+					uint16_t length, const void *param)
+{
+	struct event_index match = { .event = event, .index = index,
+					.length = length, .param = param };
+
+	mgmt->in_notify = true;
+
+	queue_foreach(mgmt->notify_list, notify_handler, &match);
+
+	mgmt->in_notify = false;
+
+	if (mgmt->need_notify_cleanup) {
+		queue_remove_all(mgmt->notify_list, match_notify_removed,
+							NULL, destroy_notify);
+		mgmt->need_notify_cleanup = false;
+	}
+}
+
+static bool can_read_data(struct io *io, void *user_data)
+{
+	struct mgmt *mgmt = user_data;
+	struct mgmt_hdr *hdr;
+	struct mgmt_ev_cmd_complete *cc;
+	struct mgmt_ev_cmd_status *cs;
+	ssize_t bytes_read;
+	uint16_t opcode, event, index, length;
+
+	bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
+	if (bytes_read < 0)
+		return false;
+
+	util_hexdump('>', mgmt->buf, bytes_read,
+				mgmt->debug_callback, mgmt->debug_data);
+
+	if (bytes_read < MGMT_HDR_SIZE)
+		return true;
+
+	hdr = mgmt->buf;
+	event = btohs(hdr->opcode);
+	index = btohs(hdr->index);
+	length = btohs(hdr->len);
+
+	if (bytes_read < length + MGMT_HDR_SIZE)
+		return true;
+
+	mgmt_ref(mgmt);
+
+	switch (event) {
+	case MGMT_EV_CMD_COMPLETE:
+		cc = mgmt->buf + MGMT_HDR_SIZE;
+		opcode = btohs(cc->opcode);
+
+		util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"[0x%04x] command 0x%04x complete: 0x%02x",
+						index, opcode, cc->status);
+
+		request_complete(mgmt, cc->status, opcode, index, length - 3,
+						mgmt->buf + MGMT_HDR_SIZE + 3);
+		break;
+	case MGMT_EV_CMD_STATUS:
+		cs = mgmt->buf + MGMT_HDR_SIZE;
+		opcode = btohs(cs->opcode);
+
+		util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"[0x%04x] command 0x%02x status: 0x%02x",
+						index, opcode, cs->status);
+
+		request_complete(mgmt, cs->status, opcode, index, 0, NULL);
+		break;
+	default:
+		util_debug(mgmt->debug_callback, mgmt->debug_data,
+				"[0x%04x] event 0x%04x", index, event);
+
+		process_notify(mgmt, event, index, length,
+						mgmt->buf + MGMT_HDR_SIZE);
+		break;
+	}
+
+	mgmt_unref(mgmt);
+
+	return true;
+}
+
+struct mgmt *mgmt_new(int fd)
+{
+	struct mgmt *mgmt;
+
+	if (fd < 0)
+		return NULL;
+
+	mgmt = new0(struct mgmt, 1);
+	mgmt->fd = fd;
+	mgmt->close_on_unref = false;
+
+	mgmt->len = 512;
+	mgmt->buf = malloc(mgmt->len);
+	if (!mgmt->buf) {
+		free(mgmt);
+		return NULL;
+	}
+
+	mgmt->io = io_new(fd);
+	if (!mgmt->io) {
+		free(mgmt->buf);
+		free(mgmt);
+		return NULL;
+	}
+
+	mgmt->request_queue = queue_new();
+	mgmt->reply_queue = queue_new();
+	mgmt->pending_list = queue_new();
+	mgmt->notify_list = queue_new();
+
+	if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) {
+		queue_destroy(mgmt->notify_list, NULL);
+		queue_destroy(mgmt->pending_list, NULL);
+		queue_destroy(mgmt->reply_queue, NULL);
+		queue_destroy(mgmt->request_queue, NULL);
+		io_destroy(mgmt->io);
+		free(mgmt->buf);
+		free(mgmt);
+		return NULL;
+	}
+
+	mgmt->writer_active = false;
+
+	return mgmt_ref(mgmt);
+}
+
+struct mgmt *mgmt_new_default(void)
+{
+	struct mgmt *mgmt;
+	union {
+		struct sockaddr common;
+		struct sockaddr_hci hci;
+	} addr;
+	int fd;
+
+	fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+								BTPROTO_HCI);
+	if (fd < 0)
+		return NULL;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci.hci_family = AF_BLUETOOTH;
+	addr.hci.hci_dev = HCI_DEV_NONE;
+	addr.hci.hci_channel = HCI_CHANNEL_CONTROL;
+
+	if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) {
+		close(fd);
+		return NULL;
+	}
+
+	mgmt = mgmt_new(fd);
+	if (!mgmt) {
+		close(fd);
+		return NULL;
+	}
+
+	mgmt->close_on_unref = true;
+
+	return mgmt;
+}
+
+struct mgmt *mgmt_ref(struct mgmt *mgmt)
+{
+	if (!mgmt)
+		return NULL;
+
+	__sync_fetch_and_add(&mgmt->ref_count, 1);
+
+	return mgmt;
+}
+
+void mgmt_unref(struct mgmt *mgmt)
+{
+	if (!mgmt)
+		return;
+
+	if (__sync_sub_and_fetch(&mgmt->ref_count, 1))
+		return;
+
+	mgmt_unregister_all(mgmt);
+	mgmt_cancel_all(mgmt);
+
+	queue_destroy(mgmt->reply_queue, NULL);
+	queue_destroy(mgmt->request_queue, NULL);
+
+	io_set_write_handler(mgmt->io, NULL, NULL, NULL);
+	io_set_read_handler(mgmt->io, NULL, NULL, NULL);
+
+	io_destroy(mgmt->io);
+	mgmt->io = NULL;
+
+	if (mgmt->close_on_unref)
+		close(mgmt->fd);
+
+	if (mgmt->debug_destroy)
+		mgmt->debug_destroy(mgmt->debug_data);
+
+	free(mgmt->buf);
+	mgmt->buf = NULL;
+
+	if (!mgmt->in_notify) {
+		queue_destroy(mgmt->notify_list, NULL);
+		queue_destroy(mgmt->pending_list, NULL);
+		free(mgmt);
+		return;
+	}
+}
+
+bool mgmt_set_debug(struct mgmt *mgmt, mgmt_debug_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy)
+{
+	if (!mgmt)
+		return false;
+
+	if (mgmt->debug_destroy)
+		mgmt->debug_destroy(mgmt->debug_data);
+
+	mgmt->debug_callback = callback;
+	mgmt->debug_destroy = destroy;
+	mgmt->debug_data = user_data;
+
+	return true;
+}
+
+bool mgmt_set_close_on_unref(struct mgmt *mgmt, bool do_close)
+{
+	if (!mgmt)
+		return false;
+
+	mgmt->close_on_unref = do_close;
+
+	return true;
+}
+
+static struct mgmt_request *create_request(uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy)
+{
+	struct mgmt_request *request;
+	struct mgmt_hdr *hdr;
+
+	if (!opcode)
+		return NULL;
+
+	if (length > 0 && !param)
+		return NULL;
+
+	request = new0(struct mgmt_request, 1);
+	request->len = length + MGMT_HDR_SIZE;
+	request->buf = malloc(request->len);
+	if (!request->buf) {
+		free(request);
+		return NULL;
+	}
+
+	if (length > 0)
+		memcpy(request->buf + MGMT_HDR_SIZE, param, length);
+
+	hdr = request->buf;
+	hdr->opcode = htobs(opcode);
+	hdr->index = htobs(index);
+	hdr->len = htobs(length);
+
+	request->opcode = opcode;
+	request->index = index;
+
+	request->callback = callback;
+	request->destroy = destroy;
+	request->user_data = user_data;
+
+	return request;
+}
+
+unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy)
+{
+	struct mgmt_request *request;
+
+	if (!mgmt)
+		return 0;
+
+	request = create_request(opcode, index, length, param,
+					callback, user_data, destroy);
+	if (!request)
+		return 0;
+
+	if (mgmt->next_request_id < 1)
+		mgmt->next_request_id = 1;
+
+	request->id = mgmt->next_request_id++;
+
+	if (!queue_push_tail(mgmt->request_queue, request)) {
+		free(request->buf);
+		free(request);
+		return 0;
+	}
+
+	wakeup_writer(mgmt);
+
+	return request->id;
+}
+
+unsigned int mgmt_send_nowait(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy)
+{
+	struct mgmt_request *request;
+
+	if (!mgmt)
+		return 0;
+
+	request = create_request(opcode, index, length, param,
+					callback, user_data, destroy);
+	if (!request)
+		return 0;
+
+	if (mgmt->next_request_id < 1)
+		mgmt->next_request_id = 1;
+
+	request->id = mgmt->next_request_id++;
+
+	if (!send_request(mgmt, request))
+		return 0;
+
+	return request->id;
+}
+
+unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy)
+{
+	struct mgmt_request *request;
+
+	if (!mgmt)
+		return 0;
+
+	request = create_request(opcode, index, length, param,
+					callback, user_data, destroy);
+	if (!request)
+		return 0;
+
+	if (mgmt->next_request_id < 1)
+		mgmt->next_request_id = 1;
+
+	request->id = mgmt->next_request_id++;
+
+	if (!queue_push_tail(mgmt->reply_queue, request)) {
+		free(request->buf);
+		free(request);
+		return 0;
+	}
+
+	wakeup_writer(mgmt);
+
+	return request->id;
+}
+
+bool mgmt_cancel(struct mgmt *mgmt, unsigned int id)
+{
+	struct mgmt_request *request;
+
+	if (!mgmt || !id)
+		return false;
+
+	request = queue_remove_if(mgmt->request_queue, match_request_id,
+							UINT_TO_PTR(id));
+	if (request)
+		goto done;
+
+	request = queue_remove_if(mgmt->reply_queue, match_request_id,
+							UINT_TO_PTR(id));
+	if (request)
+		goto done;
+
+	request = queue_remove_if(mgmt->pending_list, match_request_id,
+							UINT_TO_PTR(id));
+	if (!request)
+		return false;
+
+done:
+	destroy_request(request);
+
+	wakeup_writer(mgmt);
+
+	return true;
+}
+
+bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index)
+{
+	if (!mgmt)
+		return false;
+
+	queue_remove_all(mgmt->request_queue, match_request_index,
+					UINT_TO_PTR(index), destroy_request);
+	queue_remove_all(mgmt->reply_queue, match_request_index,
+					UINT_TO_PTR(index), destroy_request);
+	queue_remove_all(mgmt->pending_list, match_request_index,
+					UINT_TO_PTR(index), destroy_request);
+
+	return true;
+}
+
+bool mgmt_cancel_all(struct mgmt *mgmt)
+{
+	if (!mgmt)
+		return false;
+
+	queue_remove_all(mgmt->pending_list, NULL, NULL, destroy_request);
+	queue_remove_all(mgmt->reply_queue, NULL, NULL, destroy_request);
+	queue_remove_all(mgmt->request_queue, NULL, NULL, destroy_request);
+
+	return true;
+}
+
+unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
+				mgmt_notify_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy)
+{
+	struct mgmt_notify *notify;
+
+	if (!mgmt || !event)
+		return 0;
+
+	notify = new0(struct mgmt_notify, 1);
+	notify->event = event;
+	notify->index = index;
+
+	notify->callback = callback;
+	notify->destroy = destroy;
+	notify->user_data = user_data;
+
+	if (mgmt->next_notify_id < 1)
+		mgmt->next_notify_id = 1;
+
+	notify->id = mgmt->next_notify_id++;
+
+	if (!queue_push_tail(mgmt->notify_list, notify)) {
+		free(notify);
+		return 0;
+	}
+
+	return notify->id;
+}
+
+bool mgmt_unregister(struct mgmt *mgmt, unsigned int id)
+{
+	struct mgmt_notify *notify;
+
+	if (!mgmt || !id)
+		return false;
+
+	notify = queue_remove_if(mgmt->notify_list, match_notify_id,
+							UINT_TO_PTR(id));
+	if (!notify)
+		return false;
+
+	if (!mgmt->in_notify) {
+		destroy_notify(notify);
+		return true;
+	}
+
+	notify->removed = true;
+	mgmt->need_notify_cleanup = true;
+
+	return true;
+}
+
+bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index)
+{
+	if (!mgmt)
+		return false;
+
+	if (mgmt->in_notify) {
+		queue_foreach(mgmt->notify_list, mark_notify_removed,
+							UINT_TO_PTR(index));
+		mgmt->need_notify_cleanup = true;
+	} else
+		queue_remove_all(mgmt->notify_list, match_notify_index,
+					UINT_TO_PTR(index), destroy_notify);
+
+	return true;
+}
+
+bool mgmt_unregister_all(struct mgmt *mgmt)
+{
+	if (!mgmt)
+		return false;
+
+	if (mgmt->in_notify) {
+		queue_foreach(mgmt->notify_list, mark_notify_removed,
+						UINT_TO_PTR(MGMT_INDEX_NONE));
+		mgmt->need_notify_cleanup = true;
+	} else
+		queue_remove_all(mgmt->notify_list, NULL, NULL, destroy_notify);
+
+	return true;
+}
diff --git a/bluez/repo/src/shared/mgmt.h b/bluez/repo/src/shared/mgmt.h
new file mode 100644
index 0000000..7caeb38
--- /dev/null
+++ b/bluez/repo/src/shared/mgmt.h
@@ -0,0 +1,73 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+#define MGMT_VERSION(v, r) (((v) << 16) + (r))
+
+typedef void (*mgmt_destroy_func_t)(void *user_data);
+
+struct mgmt;
+
+struct mgmt *mgmt_new(int fd);
+struct mgmt *mgmt_new_default(void);
+
+struct mgmt *mgmt_ref(struct mgmt *mgmt);
+void mgmt_unref(struct mgmt *mgmt);
+
+typedef void (*mgmt_debug_func_t)(const char *str, void *user_data);
+
+bool mgmt_set_debug(struct mgmt *mgmt, mgmt_debug_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy);
+
+bool mgmt_set_close_on_unref(struct mgmt *mgmt, bool do_close);
+
+typedef void (*mgmt_request_func_t)(uint8_t status, uint16_t length,
+					const void *param, void *user_data);
+
+unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy);
+unsigned int mgmt_send_nowait(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy);
+unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
+				uint16_t length, const void *param,
+				mgmt_request_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy);
+bool mgmt_cancel(struct mgmt *mgmt, unsigned int id);
+bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index);
+bool mgmt_cancel_all(struct mgmt *mgmt);
+
+typedef void (*mgmt_notify_func_t)(uint16_t index, uint16_t length,
+					const void *param, void *user_data);
+
+unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
+				mgmt_notify_func_t callback,
+				void *user_data, mgmt_destroy_func_t destroy);
+bool mgmt_unregister(struct mgmt *mgmt, unsigned int id);
+bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index);
+bool mgmt_unregister_all(struct mgmt *mgmt);
diff --git a/bluez/repo/src/shared/pcap.c b/bluez/repo/src/shared/pcap.c
new file mode 100644
index 0000000..bd7675f
--- /dev/null
+++ b/bluez/repo/src/shared/pcap.c
@@ -0,0 +1,233 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <stdlib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/pcap.h"
+
+struct pcap_hdr {
+	uint32_t magic_number;	/* magic number */
+	uint16_t version_major;	/* major version number */
+	uint16_t version_minor;	/* minor version number */
+	int32_t  thiszone;	/* GMT to local correction */
+	uint32_t sigfigs;	/* accuracy of timestamps */
+	uint32_t snaplen;	/* max length of captured packets, in octets */
+	uint32_t network;	/* data link type */
+} __attribute__ ((packed));
+#define PCAP_HDR_SIZE (sizeof(struct pcap_hdr))
+
+struct pcap_pkt {
+	uint32_t ts_sec;	/* timestamp seconds */
+	uint32_t ts_usec;	/* timestamp microseconds */
+	uint32_t incl_len;	/* number of octets of packet saved in file */
+	uint32_t orig_len;	/* actual length of packet */
+} __attribute__ ((packed));
+#define PCAP_PKT_SIZE (sizeof(struct pcap_pkt))
+
+struct pcap_ppi {
+	uint8_t  version;	/* version, currently 0 */
+	uint8_t  flags;		/* flags */
+	uint16_t len;		/* length of entire message */
+	uint32_t dlt;		/* data link type */
+} __attribute__ ((packed));
+#define PCAP_PPI_SIZE (sizeof(struct pcap_ppi))
+
+struct pcap {
+	int ref_count;
+	int fd;
+	uint32_t type;
+	uint32_t snaplen;
+};
+
+struct pcap *pcap_open(const char *path)
+{
+	struct pcap *pcap;
+	struct pcap_hdr hdr;
+	ssize_t len;
+
+	pcap = calloc(1, sizeof(*pcap));
+	if (!pcap)
+		return NULL;
+
+	pcap->fd = open(path, O_RDONLY | O_CLOEXEC);
+	if (pcap->fd < 0) {
+		free(pcap);
+		return NULL;
+	}
+
+	len = read(pcap->fd, &hdr, PCAP_HDR_SIZE);
+	if (len < 0 || len != PCAP_HDR_SIZE)
+		goto failed;
+
+	if (hdr.magic_number != 0xa1b2c3d4)
+		goto failed;
+
+	if (hdr.version_major != 2 || hdr.version_minor != 4)
+		goto failed;
+
+	pcap->snaplen = hdr.snaplen;
+	pcap->type = hdr.network;
+
+	return pcap_ref(pcap);
+
+failed:
+	close(pcap->fd);
+	free(pcap);
+
+	return NULL;
+}
+
+struct pcap *pcap_ref(struct pcap *pcap)
+{
+	if (!pcap)
+		return NULL;
+
+	__sync_fetch_and_add(&pcap->ref_count, 1);
+
+	return pcap;
+}
+
+void pcap_unref(struct pcap *pcap)
+{
+	if (!pcap)
+		return;
+
+	if (__sync_sub_and_fetch(&pcap->ref_count, 1))
+		return;
+
+	if (pcap->fd >= 0)
+		close(pcap->fd);
+
+	free(pcap);
+}
+
+uint32_t pcap_get_type(struct pcap *pcap)
+{
+	if (!pcap)
+		return PCAP_TYPE_INVALID;
+
+	return pcap->type;
+}
+
+uint32_t pcap_get_snaplen(struct pcap *pcap)
+{
+	if (!pcap)
+		return 0;
+
+	return pcap->snaplen;
+}
+
+bool pcap_read(struct pcap *pcap, struct timeval *tv,
+				void *data, uint32_t size, uint32_t *len)
+{
+	struct pcap_pkt pkt;
+	uint32_t toread;
+	ssize_t bytes_read;
+
+	if (!pcap)
+		return false;
+
+	bytes_read = read(pcap->fd, &pkt, PCAP_PKT_SIZE);
+	if (bytes_read != PCAP_PKT_SIZE)
+		return false;
+
+	if (pkt.incl_len > size)
+		toread = size;
+	else
+		toread = pkt.incl_len;
+
+	bytes_read = read(pcap->fd, data, toread);
+	if (bytes_read < 0)
+		return false;
+
+	if (tv) {
+		tv->tv_sec = pkt.ts_sec;
+		tv->tv_usec = pkt.ts_usec;
+	}
+
+	if (len)
+		*len = toread;
+
+	return true;
+}
+
+bool pcap_read_ppi(struct pcap *pcap, struct timeval *tv, uint32_t *type,
+					void *data, uint32_t size,
+					uint32_t *offset, uint32_t *len)
+{
+	struct pcap_pkt pkt;
+	struct pcap_ppi ppi;
+	uint16_t pph_len;
+	uint32_t toread;
+	ssize_t bytes_read;
+
+	if (!pcap)
+		return false;
+
+	bytes_read = read(pcap->fd, &pkt, PCAP_PKT_SIZE);
+	if (bytes_read != PCAP_PKT_SIZE)
+		return false;
+
+	if (pkt.incl_len > size)
+		toread = size;
+	else
+		toread = pkt.incl_len;
+
+	bytes_read = read(pcap->fd, &ppi, PCAP_PPI_SIZE);
+	if (bytes_read != PCAP_PPI_SIZE)
+		return false;
+
+	if (ppi.flags)
+		return false;
+
+	pph_len = le16_to_cpu(ppi.len);
+	if (pph_len < PCAP_PPI_SIZE)
+		return false;
+
+	bytes_read = read(pcap->fd, data, toread - PCAP_PPI_SIZE);
+	if (bytes_read < 0)
+		return false;
+
+	if (tv) {
+		tv->tv_sec = pkt.ts_sec;
+		tv->tv_usec = pkt.ts_usec;
+	}
+
+	if (type)
+		*type = le32_to_cpu(ppi.dlt);
+
+	if (offset)
+		*offset = pph_len - PCAP_PPI_SIZE;
+
+	if (len)
+		*len = toread - pph_len;
+
+	return true;
+}
diff --git a/bluez/repo/src/shared/pcap.h b/bluez/repo/src/shared/pcap.h
new file mode 100644
index 0000000..b47de62
--- /dev/null
+++ b/bluez/repo/src/shared/pcap.h
@@ -0,0 +1,47 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <stdint.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#define PCAP_TYPE_INVALID		0
+#define PCAP_TYPE_USER0			147
+#define PCAP_TYPE_PPI			192
+#define PCAP_TYPE_BLUETOOTH_LE_LL	251
+
+struct pcap;
+
+struct pcap *pcap_open(const char *path);
+
+struct pcap *pcap_ref(struct pcap *pcap);
+void pcap_unref(struct pcap *pcap);
+
+uint32_t pcap_get_type(struct pcap *pcap);
+uint32_t pcap_get_snaplen(struct pcap *pcap);
+
+bool pcap_read(struct pcap *pcap, struct timeval *tv,
+				void *data, uint32_t size, uint32_t *len);
+bool pcap_read_ppi(struct pcap *pcap, struct timeval *tv, uint32_t *type,
+					void *data, uint32_t size,
+					uint32_t *offset, uint32_t *len);
diff --git a/bluez/repo/src/shared/queue.c b/bluez/repo/src/shared/queue.c
new file mode 100644
index 0000000..5ddb832
--- /dev/null
+++ b/bluez/repo/src/shared/queue.c
@@ -0,0 +1,383 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 "src/shared/util.h"
+#include "src/shared/queue.h"
+
+struct queue {
+	int ref_count;
+	struct queue_entry *head;
+	struct queue_entry *tail;
+	unsigned int entries;
+};
+
+static struct queue *queue_ref(struct queue *queue)
+{
+	if (!queue)
+		return NULL;
+
+	__sync_fetch_and_add(&queue->ref_count, 1);
+
+	return queue;
+}
+
+static void queue_unref(struct queue *queue)
+{
+	if (__sync_sub_and_fetch(&queue->ref_count, 1))
+		return;
+
+	free(queue);
+}
+
+struct queue *queue_new(void)
+{
+	struct queue *queue;
+
+	queue = new0(struct queue, 1);
+	queue->head = NULL;
+	queue->tail = NULL;
+	queue->entries = 0;
+
+	return queue_ref(queue);
+}
+
+void queue_destroy(struct queue *queue, queue_destroy_func_t destroy)
+{
+	if (!queue)
+		return;
+
+	queue_remove_all(queue, NULL, NULL, destroy);
+
+	queue_unref(queue);
+}
+
+static struct queue_entry *queue_entry_new(void *data)
+{
+	struct queue_entry *entry;
+
+	entry = new0(struct queue_entry, 1);
+	entry->data = data;
+
+	return entry;
+}
+
+bool queue_push_tail(struct queue *queue, void *data)
+{
+	struct queue_entry *entry;
+
+	if (!queue)
+		return false;
+
+	entry = queue_entry_new(data);
+
+	if (queue->tail)
+		queue->tail->next = entry;
+
+	queue->tail = entry;
+
+	if (!queue->head)
+		queue->head = entry;
+
+	queue->entries++;
+
+	return true;
+}
+
+bool queue_push_head(struct queue *queue, void *data)
+{
+	struct queue_entry *entry;
+
+	if (!queue)
+		return false;
+
+	entry = queue_entry_new(data);
+
+	entry->next = queue->head;
+
+	queue->head = entry;
+
+	if (!queue->tail)
+		queue->tail = entry;
+
+	queue->entries++;
+
+	return true;
+}
+
+bool queue_push_after(struct queue *queue, void *entry, void *data)
+{
+	struct queue_entry *qentry, *tmp, *new_entry;
+
+	qentry = NULL;
+
+	if (!queue)
+		return false;
+
+	for (tmp = queue->head; tmp; tmp = tmp->next) {
+		if (tmp->data == entry) {
+			qentry = tmp;
+			break;
+		}
+	}
+
+	if (!qentry)
+		return false;
+
+	new_entry = queue_entry_new(data);
+
+	new_entry->next = qentry->next;
+
+	if (!qentry->next)
+		queue->tail = new_entry;
+
+	qentry->next = new_entry;
+	queue->entries++;
+
+	return true;
+}
+
+void *queue_pop_head(struct queue *queue)
+{
+	struct queue_entry *entry;
+	void *data;
+
+	if (!queue || !queue->head)
+		return NULL;
+
+	entry = queue->head;
+
+	if (!queue->head->next) {
+		queue->head = NULL;
+		queue->tail = NULL;
+	} else
+		queue->head = queue->head->next;
+
+	data = entry->data;
+
+	free(entry);
+	queue->entries--;
+
+	return data;
+}
+
+void *queue_peek_head(struct queue *queue)
+{
+	if (!queue || !queue->head)
+		return NULL;
+
+	return queue->head->data;
+}
+
+void *queue_peek_tail(struct queue *queue)
+{
+	if (!queue || !queue->tail)
+		return NULL;
+
+	return queue->tail->data;
+}
+
+void queue_foreach(struct queue *queue, queue_foreach_func_t function,
+							void *user_data)
+{
+	struct queue_entry *entry;
+
+	if (!queue || !function)
+		return;
+
+	entry = queue->head;
+	if (!entry)
+		return;
+
+	queue_ref(queue);
+	while (entry && queue->head && queue->ref_count > 1) {
+		struct queue_entry *next;
+
+		next = entry->next;
+		function(entry->data, user_data);
+		entry = next;
+	}
+	queue_unref(queue);
+}
+
+static bool direct_match(const void *a, const void *b)
+{
+	return a == b;
+}
+
+void *queue_find(struct queue *queue, queue_match_func_t function,
+							const void *match_data)
+{
+	struct queue_entry *entry;
+
+	if (!queue)
+		return NULL;
+
+	if (!function)
+		function = direct_match;
+
+	for (entry = queue->head; entry; entry = entry->next)
+		if (function(entry->data, match_data))
+			return entry->data;
+
+	return NULL;
+}
+
+bool queue_remove(struct queue *queue, void *data)
+{
+	struct queue_entry *entry, *prev;
+
+	if (!queue)
+		return false;
+
+	for (entry = queue->head, prev = NULL; entry;
+					prev = entry, entry = entry->next) {
+		if (entry->data != data)
+			continue;
+
+		if (prev)
+			prev->next = entry->next;
+		else
+			queue->head = entry->next;
+
+		if (!entry->next)
+			queue->tail = prev;
+
+		free(entry);
+		queue->entries--;
+
+		return true;
+	}
+
+	return false;
+}
+
+void *queue_remove_if(struct queue *queue, queue_match_func_t function,
+							void *user_data)
+{
+	struct queue_entry *entry, *prev = NULL;
+
+	if (!queue || !function)
+		return NULL;
+
+	entry = queue->head;
+
+	while (entry) {
+		if (function(entry->data, user_data)) {
+			void *data;
+
+			if (prev)
+				prev->next = entry->next;
+			else
+				queue->head = entry->next;
+
+			if (!entry->next)
+				queue->tail = prev;
+
+			data = entry->data;
+
+			free(entry);
+			queue->entries--;
+
+			return data;
+		} else {
+			prev = entry;
+			entry = entry->next;
+		}
+	}
+
+	return NULL;
+}
+
+unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function,
+				void *user_data, queue_destroy_func_t destroy)
+{
+	struct queue_entry *entry;
+	unsigned int count = 0;
+
+	if (!queue)
+		return 0;
+
+	entry = queue->head;
+
+	if (function) {
+		while (entry) {
+			void *data;
+			unsigned int entries = queue->entries;
+
+			data = queue_remove_if(queue, function, user_data);
+			if (entries == queue->entries)
+				break;
+
+			if (destroy)
+				destroy(data);
+
+			count++;
+		}
+	} else {
+		queue->head = NULL;
+		queue->tail = NULL;
+		queue->entries = 0;
+
+		while (entry) {
+			struct queue_entry *tmp = entry;
+
+			entry = entry->next;
+
+			if (destroy)
+				destroy(tmp->data);
+
+			free(tmp);
+			count++;
+		}
+	}
+
+	return count;
+}
+
+const struct queue_entry *queue_get_entries(struct queue *queue)
+{
+	if (!queue)
+		return NULL;
+
+	return queue->head;
+}
+
+unsigned int queue_length(struct queue *queue)
+{
+	if (!queue)
+		return 0;
+
+	return queue->entries;
+}
+
+bool queue_isempty(struct queue *queue)
+{
+	if (!queue)
+		return true;
+
+	return queue->entries == 0;
+}
diff --git a/bluez/repo/src/shared/queue.h b/bluez/repo/src/shared/queue.h
new file mode 100644
index 0000000..8cd817c
--- /dev/null
+++ b/bluez/repo/src/shared/queue.h
@@ -0,0 +1,64 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+typedef void (*queue_destroy_func_t)(void *data);
+
+struct queue;
+
+struct queue_entry {
+	void *data;
+	struct queue_entry *next;
+};
+
+struct queue *queue_new(void);
+void queue_destroy(struct queue *queue, queue_destroy_func_t destroy);
+
+bool queue_push_tail(struct queue *queue, void *data);
+bool queue_push_head(struct queue *queue, void *data);
+bool queue_push_after(struct queue *queue, void *entry, void *data);
+void *queue_pop_head(struct queue *queue);
+void *queue_peek_head(struct queue *queue);
+void *queue_peek_tail(struct queue *queue);
+
+typedef void (*queue_foreach_func_t)(void *data, void *user_data);
+
+void queue_foreach(struct queue *queue, queue_foreach_func_t function,
+							void *user_data);
+
+typedef bool (*queue_match_func_t)(const void *data, const void *match_data);
+
+void *queue_find(struct queue *queue, queue_match_func_t function,
+							const void *match_data);
+
+bool queue_remove(struct queue *queue, void *data);
+void *queue_remove_if(struct queue *queue, queue_match_func_t function,
+							void *user_data);
+unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function,
+				void *user_data, queue_destroy_func_t destroy);
+
+const struct queue_entry *queue_get_entries(struct queue *queue);
+
+unsigned int queue_length(struct queue *queue);
+bool queue_isempty(struct queue *queue);
diff --git a/bluez/repo/src/shared/ringbuf.c b/bluez/repo/src/shared/ringbuf.c
new file mode 100644
index 0000000..8e7c50e
--- /dev/null
+++ b/bluez/repo/src/shared/ringbuf.c
@@ -0,0 +1,309 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <string.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+
+#include "src/shared/util.h"
+#include "src/shared/ringbuf.h"
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+struct ringbuf {
+	void *buffer;
+	size_t size;
+	size_t in;
+	size_t out;
+	ringbuf_tracing_func_t in_tracing;
+	void *in_data;
+};
+
+#define RINGBUF_RESET 0
+
+/* Find last (most siginificant) set bit */
+static inline unsigned int fls(unsigned int x)
+{
+	return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+/* Round up to nearest power of two */
+static inline unsigned int align_power2(unsigned int u)
+{
+	return 1 << fls(u - 1);
+}
+
+struct ringbuf *ringbuf_new(size_t size)
+{
+	struct ringbuf *ringbuf;
+	size_t real_size;
+
+	if (size < 2 || size > UINT_MAX)
+		return NULL;
+
+	/* Find the next power of two for size */
+	real_size = align_power2(size);
+
+	ringbuf = new0(struct ringbuf, 1);
+	ringbuf->buffer = malloc(real_size);
+	if (!ringbuf->buffer) {
+		free(ringbuf);
+		return NULL;
+	}
+
+	ringbuf->size = real_size;
+	ringbuf->in = RINGBUF_RESET;
+	ringbuf->out = RINGBUF_RESET;
+
+	return ringbuf;
+}
+
+void ringbuf_free(struct ringbuf *ringbuf)
+{
+	if (!ringbuf)
+		return;
+
+	free(ringbuf->buffer);
+	free(ringbuf);
+}
+
+bool ringbuf_set_input_tracing(struct ringbuf *ringbuf,
+			ringbuf_tracing_func_t callback, void *user_data)
+{
+	if (!ringbuf)
+		return false;
+
+	ringbuf->in_tracing = callback;
+	ringbuf->in_data = user_data;
+
+	return true;
+}
+
+size_t ringbuf_capacity(struct ringbuf *ringbuf)
+{
+	if (!ringbuf)
+		return 0;
+
+	return ringbuf->size;
+}
+
+size_t ringbuf_len(struct ringbuf *ringbuf)
+{
+	if (!ringbuf)
+		return 0;
+
+	return ringbuf->in - ringbuf->out;
+}
+
+size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count)
+{
+	size_t len;
+
+	if (!ringbuf)
+		return 0;
+
+	len = MIN(count, ringbuf->in - ringbuf->out);
+	if (!len)
+		return 0;
+
+	ringbuf->out += len;
+
+	if (ringbuf->out == ringbuf->in) {
+		ringbuf->in = RINGBUF_RESET;
+		ringbuf->out = RINGBUF_RESET;
+	}
+
+	return len;
+}
+
+void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap)
+{
+	if (!ringbuf)
+		return NULL;
+
+	offset = (ringbuf->out + offset) & (ringbuf->size - 1);
+
+	if (len_nowrap) {
+		size_t len = ringbuf->in - ringbuf->out;
+		*len_nowrap = MIN(len, ringbuf->size - offset);
+	}
+
+	return ringbuf->buffer + offset;
+}
+
+ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd)
+{
+	size_t len, offset, end;
+	struct iovec iov[2];
+	ssize_t consumed;
+
+	if (!ringbuf || fd < 0)
+		return -1;
+
+	/* Determine how much data is available */
+	len = ringbuf->in - ringbuf->out;
+	if (!len)
+		return 0;
+
+	/* Grab data from buffer starting at offset until the end */
+	offset = ringbuf->out & (ringbuf->size - 1);
+	end = MIN(len, ringbuf->size - offset);
+
+	iov[0].iov_base = ringbuf->buffer + offset;
+	iov[0].iov_len = end;
+
+	/* Use second vector for remainder from the beginning */
+	iov[1].iov_base = ringbuf->buffer;
+	iov[1].iov_len = len - end;
+
+	consumed = writev(fd, iov, 2);
+	if (consumed < 0)
+		return -1;
+
+	ringbuf->out += consumed;
+
+	if (ringbuf->out == ringbuf->in) {
+		ringbuf->in = RINGBUF_RESET;
+		ringbuf->out = RINGBUF_RESET;
+	}
+
+	return consumed;
+}
+
+size_t ringbuf_avail(struct ringbuf *ringbuf)
+{
+	if (!ringbuf)
+		return 0;
+
+	return ringbuf->size - ringbuf->in + ringbuf->out;
+}
+
+int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, format);
+	len = ringbuf_vprintf(ringbuf, format, ap);
+	va_end(ap);
+
+	return len;
+}
+
+int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap)
+{
+	size_t avail, offset, end;
+	char *str;
+	int len;
+
+	if (!ringbuf || !format)
+		return -1;
+
+	/* Determine maximum length available for string */
+	avail = ringbuf->size - ringbuf->in + ringbuf->out;
+	if (!avail)
+		return -1;
+
+	len = vasprintf(&str, format, ap);
+	if (len < 0)
+		return -1;
+
+	if ((size_t) len > avail) {
+		free(str);
+		return -1;
+	}
+
+	/* Determine possible length of string before wrapping */
+	offset = ringbuf->in & (ringbuf->size - 1);
+	end = MIN((size_t) len, ringbuf->size - offset);
+	memcpy(ringbuf->buffer + offset, str, end);
+
+	if (ringbuf->in_tracing)
+		ringbuf->in_tracing(ringbuf->buffer + offset, end,
+							ringbuf->in_data);
+
+	if (len - end > 0) {
+		/* Put the remainder of string at the beginning */
+		memcpy(ringbuf->buffer, str + end, len - end);
+
+		if (ringbuf->in_tracing)
+			ringbuf->in_tracing(ringbuf->buffer, len - end,
+							ringbuf->in_data);
+	}
+
+	free(str);
+
+	ringbuf->in += len;
+
+	return len;
+}
+
+ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd)
+{
+	size_t avail, offset, end;
+	struct iovec iov[2];
+	ssize_t consumed;
+
+	if (!ringbuf || fd < 0)
+		return -1;
+
+	/* Determine how much can actually be consumed */
+	avail = ringbuf->size - ringbuf->in + ringbuf->out;
+	if (!avail)
+		return -1;
+
+	/* Determine how much to consume before wrapping */
+	offset = ringbuf->in & (ringbuf->size - 1);
+	end = MIN(avail, ringbuf->size - offset);
+
+	iov[0].iov_base = ringbuf->buffer + offset;
+	iov[0].iov_len = end;
+
+	/* Now put the remainder into the second vector */
+	iov[1].iov_base = ringbuf->buffer;
+	iov[1].iov_len = avail - end;
+
+	consumed = readv(fd, iov, 2);
+	if (consumed < 0)
+		return -1;
+
+	if (ringbuf->in_tracing) {
+		size_t len = MIN((size_t) consumed, end);
+		ringbuf->in_tracing(ringbuf->buffer + offset, len,
+							ringbuf->in_data);
+		if (consumed - len > 0)
+			ringbuf->in_tracing(ringbuf->buffer, consumed - len,
+							ringbuf->in_data);
+	}
+
+	ringbuf->in += consumed;
+
+	return consumed;
+}
diff --git a/bluez/repo/src/shared/ringbuf.h b/bluez/repo/src/shared/ringbuf.h
new file mode 100644
index 0000000..adf471a
--- /dev/null
+++ b/bluez/repo/src/shared/ringbuf.h
@@ -0,0 +1,50 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+typedef void (*ringbuf_tracing_func_t)(const void *buf, size_t count,
+							void *user_data);
+
+struct ringbuf;
+
+struct ringbuf *ringbuf_new(size_t size);
+void ringbuf_free(struct ringbuf *ringbuf);
+
+bool ringbuf_set_input_tracing(struct ringbuf *ringbuf,
+			ringbuf_tracing_func_t callback, void *user_data);
+
+size_t ringbuf_capacity(struct ringbuf *ringbuf);
+
+size_t ringbuf_len(struct ringbuf *ringbuf);
+size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count);
+void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap);
+ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd);
+
+size_t ringbuf_avail(struct ringbuf *ringbuf);
+int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...)
+					__attribute__((format(printf, 2, 3)));
+int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap);
+ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd);
diff --git a/bluez/repo/src/shared/tester.c b/bluez/repo/src/shared/tester.c
new file mode 100644
index 0000000..80d6511
--- /dev/null
+++ b/bluez/repo/src/shared/tester.c
@@ -0,0 +1,841 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#include <glib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+#define COLOR_OFF	"\x1B[0m"
+#define COLOR_BLACK	"\x1B[0;30m"
+#define COLOR_RED	"\x1B[0;31m"
+#define COLOR_GREEN	"\x1B[0;32m"
+#define COLOR_YELLOW	"\x1B[0;33m"
+#define COLOR_BLUE	"\x1B[0;34m"
+#define COLOR_MAGENTA	"\x1B[0;35m"
+#define COLOR_CYAN	"\x1B[0;36m"
+#define COLOR_WHITE	"\x1B[0;37m"
+#define COLOR_HIGHLIGHT	"\x1B[1;39m"
+
+#define print_text(color, fmt, args...) \
+		printf(color fmt COLOR_OFF "\n", ## args)
+
+#define print_summary(label, color, value, fmt, args...) \
+			printf("%-52s " color "%-10s" COLOR_OFF fmt "\n", \
+							label, value, ## args)
+
+#define print_progress(name, color, fmt, args...) \
+		printf(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \
+				color fmt COLOR_OFF "\n", name, ## args)
+
+enum test_result {
+	TEST_RESULT_NOT_RUN,
+	TEST_RESULT_PASSED,
+	TEST_RESULT_FAILED,
+	TEST_RESULT_TIMED_OUT,
+};
+
+enum test_stage {
+	TEST_STAGE_INVALID,
+	TEST_STAGE_PRE_SETUP,
+	TEST_STAGE_SETUP,
+	TEST_STAGE_RUN,
+	TEST_STAGE_TEARDOWN,
+	TEST_STAGE_POST_TEARDOWN,
+};
+
+struct test_case {
+	char *name;
+	enum test_result result;
+	enum test_stage stage;
+	const void *test_data;
+	tester_data_func_t pre_setup_func;
+	tester_data_func_t setup_func;
+	tester_data_func_t test_func;
+	tester_data_func_t teardown_func;
+	tester_data_func_t post_teardown_func;
+	gdouble start_time;
+	gdouble end_time;
+	unsigned int timeout;
+	unsigned int timeout_id;
+	unsigned int teardown_id;
+	tester_destroy_func_t destroy;
+	void *user_data;
+};
+
+static GMainLoop *main_loop;
+
+static GList *test_list;
+static GList *test_current;
+static GTimer *test_timer;
+
+static gboolean option_version = FALSE;
+static gboolean option_quiet = FALSE;
+static gboolean option_debug = FALSE;
+static gboolean option_list = FALSE;
+static const char *option_prefix = NULL;
+
+static void test_destroy(gpointer data)
+{
+	struct test_case *test = data;
+
+	if (test->timeout_id > 0)
+		g_source_remove(test->timeout_id);
+
+	if (test->teardown_id > 0)
+		g_source_remove(test->teardown_id);
+
+	if (test->destroy)
+		test->destroy(test->user_data);
+
+	free(test->name);
+	free(test);
+}
+
+void tester_print(const char *format, ...)
+{
+	va_list ap;
+
+	if (tester_use_quiet())
+		return;
+
+	printf("  %s", COLOR_WHITE);
+	va_start(ap, format);
+	vprintf(format, ap);
+	va_end(ap);
+	printf("%s\n", COLOR_OFF);
+}
+
+void tester_debug(const char *format, ...)
+{
+	va_list ap;
+
+	if (!tester_use_debug())
+		return;
+
+	printf("  %s", COLOR_WHITE);
+	va_start(ap, format);
+	vprintf(format, ap);
+	va_end(ap);
+	printf("%s\n", COLOR_OFF);
+}
+
+void tester_warn(const char *format, ...)
+{
+	va_list ap;
+
+	printf("  %s", COLOR_WHITE);
+	va_start(ap, format);
+	vprintf(format, ap);
+	va_end(ap);
+	printf("%s\n", COLOR_OFF);
+}
+
+static void default_pre_setup(const void *test_data)
+{
+	tester_pre_setup_complete();
+}
+
+static void default_setup(const void *test_data)
+{
+	tester_setup_complete();
+}
+
+static void default_teardown(const void *test_data)
+{
+	tester_teardown_complete();
+}
+
+static void default_post_teardown(const void *test_data)
+{
+	tester_post_teardown_complete();
+}
+
+void tester_add_full(const char *name, const void *test_data,
+				tester_data_func_t pre_setup_func,
+				tester_data_func_t setup_func,
+				tester_data_func_t test_func,
+				tester_data_func_t teardown_func,
+				tester_data_func_t post_teardown_func,
+				unsigned int timeout,
+				void *user_data, tester_destroy_func_t destroy)
+{
+	struct test_case *test;
+
+	if (!test_func)
+		return;
+
+	if (option_prefix && !g_str_has_prefix(name, option_prefix)) {
+		if (destroy)
+			destroy(user_data);
+		return;
+	}
+
+	if (option_list) {
+		printf("%s\n", name);
+		if (destroy)
+			destroy(user_data);
+		return;
+	}
+
+	test = new0(struct test_case, 1);
+	test->name = strdup(name);
+	test->result = TEST_RESULT_NOT_RUN;
+	test->stage = TEST_STAGE_INVALID;
+
+	test->test_data = test_data;
+
+	if (pre_setup_func)
+		test->pre_setup_func = pre_setup_func;
+	else
+		test->pre_setup_func = default_pre_setup;
+
+	if (setup_func)
+		test->setup_func = setup_func;
+	else
+		test->setup_func = default_setup;
+
+	test->test_func = test_func;
+
+	if (teardown_func)
+		test->teardown_func = teardown_func;
+	else
+		test->teardown_func = default_teardown;
+
+	if (post_teardown_func)
+		test->post_teardown_func = post_teardown_func;
+	else
+		test->post_teardown_func = default_post_teardown;
+
+	test->timeout = timeout;
+
+	test->destroy = destroy;
+	test->user_data = user_data;
+
+	test_list = g_list_append(test_list, test);
+}
+
+void tester_add(const char *name, const void *test_data,
+					tester_data_func_t setup_func,
+					tester_data_func_t test_func,
+					tester_data_func_t teardown_func)
+{
+	tester_add_full(name, test_data, NULL, setup_func, test_func,
+					teardown_func, NULL, 0, NULL, NULL);
+}
+
+void *tester_get_data(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return NULL;
+
+	test = test_current->data;
+
+	return test->user_data;
+}
+
+static int tester_summarize(void)
+{
+	unsigned int not_run = 0, passed = 0, failed = 0;
+	gdouble execution_time;
+	GList *list;
+
+	printf("\n");
+	print_text(COLOR_HIGHLIGHT, "");
+	print_text(COLOR_HIGHLIGHT, "Test Summary");
+	print_text(COLOR_HIGHLIGHT, "------------");
+
+	for (list = g_list_first(test_list); list; list = g_list_next(list)) {
+		struct test_case *test = list->data;
+		gdouble exec_time;
+
+		exec_time = test->end_time - test->start_time;
+
+		switch (test->result) {
+		case TEST_RESULT_NOT_RUN:
+			print_summary(test->name, COLOR_YELLOW, "Not Run", "");
+			not_run++;
+			break;
+		case TEST_RESULT_PASSED:
+			print_summary(test->name, COLOR_GREEN, "Passed",
+						"%8.3f seconds", exec_time);
+			passed++;
+			break;
+		case TEST_RESULT_FAILED:
+			print_summary(test->name, COLOR_RED, "Failed",
+						"%8.3f seconds", exec_time);
+			failed++;
+			break;
+		case TEST_RESULT_TIMED_OUT:
+			print_summary(test->name, COLOR_RED, "Timed out",
+						"%8.3f seconds", exec_time);
+			failed++;
+			break;
+		}
+        }
+
+	printf("\nTotal: %d, "
+		COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", "
+		COLOR_RED "Failed: %d" COLOR_OFF ", "
+		COLOR_YELLOW "Not Run: %d" COLOR_OFF "\n",
+			not_run + passed + failed, passed,
+			(not_run + passed + failed) ?
+			(float) passed * 100 / (not_run + passed + failed) : 0,
+			failed, not_run);
+
+	execution_time = g_timer_elapsed(test_timer, NULL);
+	printf("Overall execution time: %.3g seconds\n", execution_time);
+
+	return failed;
+}
+
+static gboolean teardown_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->teardown_id = 0;
+	test->stage = TEST_STAGE_TEARDOWN;
+
+	print_progress(test->name, COLOR_MAGENTA, "teardown");
+	test->teardown_func(test->test_data);
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+	VALGRIND_DO_ADDED_LEAK_CHECK;
+#endif
+
+	return FALSE;
+}
+
+static gboolean test_timeout(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->timeout_id = 0;
+
+	if (!test_current)
+		return FALSE;
+
+	test->result = TEST_RESULT_TIMED_OUT;
+	print_progress(test->name, COLOR_RED, "test timed out");
+
+	g_idle_add(teardown_callback, test);
+
+	return FALSE;
+}
+
+static void next_test_case(void)
+{
+	struct test_case *test;
+
+	if (test_current)
+		test_current = g_list_next(test_current);
+	else
+		test_current = test_list;
+
+	if (!test_current) {
+		g_timer_stop(test_timer);
+
+		g_main_loop_quit(main_loop);
+		return;
+	}
+
+	test = test_current->data;
+
+	printf("\n");
+	print_progress(test->name, COLOR_BLACK, "init");
+
+	test->start_time = g_timer_elapsed(test_timer, NULL);
+
+	if (test->timeout > 0)
+		test->timeout_id = g_timeout_add_seconds(test->timeout,
+							test_timeout, test);
+
+	test->stage = TEST_STAGE_PRE_SETUP;
+
+	test->pre_setup_func(test->test_data);
+}
+
+static gboolean setup_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->stage = TEST_STAGE_SETUP;
+
+	print_progress(test->name, COLOR_BLUE, "setup");
+	test->setup_func(test->test_data);
+
+	return FALSE;
+}
+
+static gboolean run_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->stage = TEST_STAGE_RUN;
+
+	print_progress(test->name, COLOR_BLACK, "run");
+	test->test_func(test->test_data);
+
+	return FALSE;
+}
+
+static gboolean done_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->end_time = g_timer_elapsed(test_timer, NULL);
+
+	print_progress(test->name, COLOR_BLACK, "done");
+	next_test_case();
+
+	return FALSE;
+}
+
+void tester_pre_setup_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_PRE_SETUP)
+		return;
+
+	g_idle_add(setup_callback, test);
+}
+
+void tester_pre_setup_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_PRE_SETUP)
+		return;
+
+	print_progress(test->name, COLOR_RED, "pre setup failed");
+
+	g_idle_add(done_callback, test);
+}
+
+void tester_setup_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_SETUP)
+		return;
+
+	print_progress(test->name, COLOR_BLUE, "setup complete");
+
+	g_idle_add(run_callback, test);
+}
+
+void tester_setup_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_SETUP)
+		return;
+
+	test->stage = TEST_STAGE_POST_TEARDOWN;
+
+	if (test->timeout_id > 0) {
+		g_source_remove(test->timeout_id);
+		test->timeout_id = 0;
+	}
+
+	print_progress(test->name, COLOR_RED, "setup failed");
+	print_progress(test->name, COLOR_MAGENTA, "teardown");
+
+	test->post_teardown_func(test->test_data);
+}
+
+static void test_result(enum test_result result)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_RUN)
+		return;
+
+	if (test->timeout_id > 0) {
+		g_source_remove(test->timeout_id);
+		test->timeout_id = 0;
+	}
+
+	test->result = result;
+	switch (result) {
+	case TEST_RESULT_PASSED:
+		print_progress(test->name, COLOR_GREEN, "test passed");
+		break;
+	case TEST_RESULT_FAILED:
+		print_progress(test->name, COLOR_RED, "test failed");
+		break;
+	case TEST_RESULT_NOT_RUN:
+		print_progress(test->name, COLOR_YELLOW, "test not run");
+		break;
+	case TEST_RESULT_TIMED_OUT:
+		print_progress(test->name, COLOR_RED, "test timed out");
+		break;
+	}
+
+	if (test->teardown_id > 0)
+		return;
+
+	test->teardown_id = g_idle_add(teardown_callback, test);
+}
+
+void tester_test_passed(void)
+{
+	test_result(TEST_RESULT_PASSED);
+}
+
+void tester_test_failed(void)
+{
+	test_result(TEST_RESULT_FAILED);
+}
+
+void tester_test_abort(void)
+{
+	test_result(TEST_RESULT_NOT_RUN);
+}
+
+void tester_teardown_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_TEARDOWN)
+		return;
+
+	test->stage = TEST_STAGE_POST_TEARDOWN;
+
+	test->post_teardown_func(test->test_data);
+}
+
+void tester_teardown_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_TEARDOWN)
+		return;
+
+	test->stage = TEST_STAGE_POST_TEARDOWN;
+
+	tester_post_teardown_failed();
+}
+
+void tester_post_teardown_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_POST_TEARDOWN)
+		return;
+
+	print_progress(test->name, COLOR_MAGENTA, "teardown complete");
+
+	g_idle_add(done_callback, test);
+}
+
+void tester_post_teardown_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_POST_TEARDOWN)
+		return;
+
+	print_progress(test->name, COLOR_RED, "teardown failed");
+
+	g_idle_add(done_callback, test);
+}
+
+static gboolean start_tester(gpointer user_data)
+{
+	test_timer = g_timer_new();
+
+	next_test_case();
+
+	return FALSE;
+}
+
+struct wait_data {
+	unsigned int seconds;
+	struct test_case *test;
+	tester_wait_func_t func;
+	void *user_data;
+};
+
+static gboolean wait_callback(gpointer user_data)
+{
+	struct wait_data *wait = user_data;
+	struct test_case *test = wait->test;
+
+	wait->seconds--;
+
+	if (wait->seconds > 0) {
+		print_progress(test->name, COLOR_BLACK, "%u seconds left",
+								wait->seconds);
+		return TRUE;
+	}
+
+	print_progress(test->name, COLOR_BLACK, "waiting done");
+
+	wait->func(wait->user_data);
+
+	free(wait);
+
+	return FALSE;
+}
+
+void tester_wait(unsigned int seconds, tester_wait_func_t func,
+							void *user_data)
+{
+	struct test_case *test;
+	struct wait_data *wait;
+
+	if (!func || seconds < 1)
+		return;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	wait = new0(struct wait_data, 1);
+	wait->seconds = seconds;
+	wait->test = test;
+	wait->func = func;
+	wait->user_data = user_data;
+
+	g_timeout_add(1000, wait_callback, wait);
+
+	print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds);
+}
+
+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:
+	case SIGTERM:
+		if (!terminated)
+			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;
+}
+
+bool tester_use_quiet(void)
+{
+	return option_quiet == TRUE ? true : false;
+}
+
+bool tester_use_debug(void)
+{
+	return option_debug == TRUE ? true : false;
+}
+
+static GOptionEntry options[] = {
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit" },
+	{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &option_quiet,
+				"Run tests without logging" },
+	{ "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+				"Run tests with debug output" },
+	{ "list", 'l', 0, G_OPTION_ARG_NONE, &option_list,
+				"Only list the tests to be run" },
+	{ "prefix", 'p', 0, G_OPTION_ARG_STRING, &option_prefix,
+				"Run tests matching provided prefix" },
+	{ NULL },
+};
+
+void tester_init(int *argc, char ***argv)
+{
+	GOptionContext *context;
+	GError *error = NULL;
+
+	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) {
+		g_print("%s\n", VERSION);
+		exit(EXIT_SUCCESS);
+	}
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	test_list = NULL;
+	test_current = NULL;
+}
+
+int tester_run(void)
+{
+	guint signal;
+	int ret;
+
+	if (!main_loop)
+		return EXIT_FAILURE;
+
+	if (option_list) {
+		g_main_loop_unref(main_loop);
+		return EXIT_SUCCESS;
+	}
+
+	signal = setup_signalfd();
+
+	g_idle_add(start_tester, NULL);
+	g_main_loop_run(main_loop);
+
+	g_source_remove(signal);
+
+	g_main_loop_unref(main_loop);
+
+	ret = tester_summarize();
+
+	g_list_free_full(test_list, test_destroy);
+
+	return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/bluez/repo/src/shared/tester.h b/bluez/repo/src/shared/tester.h
new file mode 100644
index 0000000..83ef5de
--- /dev/null
+++ b/bluez/repo/src/shared/tester.h
@@ -0,0 +1,77 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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>
+
+void tester_init(int *argc, char ***argv);
+int tester_run(void);
+
+bool tester_use_quiet(void);
+bool tester_use_debug(void);
+
+void tester_print(const char *format, ...)
+				__attribute__((format(printf, 1, 2)));
+void tester_warn(const char *format, ...)
+				__attribute__((format(printf, 1, 2)));
+void tester_debug(const char *format, ...)
+				__attribute__((format(printf, 1, 2)));
+
+typedef void (*tester_destroy_func_t)(void *user_data);
+typedef void (*tester_data_func_t)(const void *test_data);
+
+void tester_add_full(const char *name, const void *test_data,
+				tester_data_func_t pre_setup_func,
+				tester_data_func_t setup_func,
+				tester_data_func_t test_func,
+				tester_data_func_t teardown_func,
+				tester_data_func_t post_teardown_func,
+				unsigned int timeout,
+				void *user_data, tester_destroy_func_t destroy);
+
+void tester_add(const char *name, const void *test_data,
+					tester_data_func_t setup_func,
+					tester_data_func_t test_func,
+					tester_data_func_t teardown_func);
+
+void *tester_get_data(void);
+
+void tester_pre_setup_complete(void);
+void tester_pre_setup_failed(void);
+
+void tester_setup_complete(void);
+void tester_setup_failed(void);
+
+void tester_test_passed(void);
+void tester_test_failed(void);
+void tester_test_abort(void);
+
+void tester_teardown_complete(void);
+void tester_teardown_failed(void);
+
+void tester_post_teardown_complete(void);
+void tester_post_teardown_failed(void);
+
+typedef void (*tester_wait_func_t)(void *user_data);
+
+void tester_wait(unsigned int seconds, tester_wait_func_t func,
+							void *user_data);
diff --git a/bluez/repo/src/shared/timeout-glib.c b/bluez/repo/src/shared/timeout-glib.c
new file mode 100644
index 0000000..4163bce
--- /dev/null
+++ b/bluez/repo/src/shared/timeout-glib.c
@@ -0,0 +1,78 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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.
+ *
+ */
+
+#include "timeout.h"
+
+#include <glib.h>
+
+struct timeout_data {
+	timeout_func_t func;
+	timeout_destroy_func_t destroy;
+	void *user_data;
+};
+
+static gboolean timeout_callback(gpointer user_data)
+{
+	struct timeout_data *data  = user_data;
+
+	if (data->func(data->user_data))
+		return TRUE;
+
+	return FALSE;
+}
+
+static void timeout_destroy(gpointer user_data)
+{
+	struct timeout_data *data = user_data;
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+
+	g_free(data);
+}
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+			void *user_data, timeout_destroy_func_t destroy)
+{
+	struct timeout_data *data;
+	guint id;
+
+	data = g_try_new0(struct timeout_data, 1);
+	if (!data)
+		return 0;
+
+	data->func = func;
+	data->destroy = destroy;
+	data->user_data = user_data;
+
+	id = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout, timeout_callback,
+						data, timeout_destroy);
+	if (!id)
+		g_free(data);
+
+	return id;
+}
+
+void timeout_remove(unsigned int id)
+{
+	GSource *source = g_main_context_find_source_by_id(NULL, id);
+
+	if (source)
+		g_source_destroy(source);
+}
diff --git a/bluez/repo/src/shared/timeout-mainloop.c b/bluez/repo/src/shared/timeout-mainloop.c
new file mode 100644
index 0000000..971124a
--- /dev/null
+++ b/bluez/repo/src/shared/timeout-mainloop.c
@@ -0,0 +1,82 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "mainloop.h"
+#include "util.h"
+#include "timeout.h"
+
+struct timeout_data {
+	int id;
+	timeout_func_t func;
+	timeout_destroy_func_t destroy;
+	unsigned int timeout;
+	void *user_data;
+};
+
+static void timeout_callback(int id, void *user_data)
+{
+	struct timeout_data *data = user_data;
+
+	if (data->func(data->user_data) &&
+			!mainloop_modify_timeout(data->id, data->timeout))
+		return;
+
+	mainloop_remove_timeout(data->id);
+}
+
+static void timeout_destroy(void *user_data)
+{
+	struct timeout_data *data = user_data;
+
+	if (data->destroy)
+		data->destroy(data->user_data);
+
+	free(data);
+}
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+			void *user_data, timeout_destroy_func_t destroy)
+{
+	struct timeout_data *data;
+
+	data = new0(struct timeout_data, 1);
+	data->func = func;
+	data->user_data = user_data;
+	data->timeout = timeout;
+	data->destroy = destroy;
+
+	data->id = mainloop_add_timeout(timeout, timeout_callback, data,
+							timeout_destroy);
+	if (data->id < 0) {
+		free(data);
+		return 0;
+	}
+
+	return (unsigned int) data->id;
+}
+
+void timeout_remove(unsigned int id)
+{
+	if (!id)
+		return;
+
+	mainloop_remove_timeout((int) id);
+}
diff --git a/bluez/repo/src/shared/timeout.h b/bluez/repo/src/shared/timeout.h
new file mode 100644
index 0000000..4930ce1
--- /dev/null
+++ b/bluez/repo/src/shared/timeout.h
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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.
+ *
+ */
+
+#include <stdbool.h>
+
+typedef bool (*timeout_func_t)(void *user_data);
+typedef void (*timeout_destroy_func_t)(void *user_data);
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+			void *user_data, timeout_destroy_func_t destroy);
+void timeout_remove(unsigned int id);
diff --git a/bluez/repo/src/shared/uhid.c b/bluez/repo/src/shared/uhid.c
new file mode 100644
index 0000000..1c684cd
--- /dev/null
+++ b/bluez/repo/src/shared/uhid.c
@@ -0,0 +1,238 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/uhid.h"
+
+#define UHID_DEVICE_FILE "/dev/uhid"
+
+struct bt_uhid {
+	int ref_count;
+	struct io *io;
+	unsigned int notify_id;
+	struct queue *notify_list;
+};
+
+struct uhid_notify {
+	unsigned int id;
+	uint32_t event;
+	bt_uhid_callback_t func;
+	void *user_data;
+};
+
+static void uhid_free(struct bt_uhid *uhid)
+{
+	if (uhid->io)
+		io_destroy(uhid->io);
+
+	if (uhid->notify_list)
+		queue_destroy(uhid->notify_list, free);
+
+	free(uhid);
+}
+
+static void notify_handler(void *data, void *user_data)
+{
+	struct uhid_notify *notify = data;
+	struct uhid_event *ev = user_data;
+
+	if (notify->event != ev->type)
+		return;
+
+	if (notify->func)
+		notify->func(ev, notify->user_data);
+}
+
+static bool uhid_read_handler(struct io *io, void *user_data)
+{
+	struct bt_uhid *uhid = user_data;
+	int fd;
+	ssize_t len;
+	struct uhid_event ev;
+
+	fd = io_get_fd(io);
+	if (fd < 0)
+		return false;
+
+	memset(&ev, 0, sizeof(ev));
+
+	len = read(fd, &ev, sizeof(ev));
+	if (len < 0)
+		return false;
+
+	if ((size_t) len < sizeof(ev.type))
+		return false;
+
+	queue_foreach(uhid->notify_list, notify_handler, &ev);
+
+	return true;
+}
+
+struct bt_uhid *bt_uhid_new_default(void)
+{
+	struct bt_uhid *uhid;
+	int fd;
+
+	fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		return NULL;
+
+	uhid = bt_uhid_new(fd);
+	if (!uhid) {
+		close(fd);
+		return NULL;
+	}
+
+	io_set_close_on_destroy(uhid->io, true);
+
+	return uhid;
+}
+
+struct bt_uhid *bt_uhid_new(int fd)
+{
+	struct bt_uhid *uhid;
+
+	uhid = new0(struct bt_uhid, 1);
+	uhid->io = io_new(fd);
+	if (!uhid->io)
+		goto failed;
+
+	uhid->notify_list = queue_new();
+
+	if (!io_set_read_handler(uhid->io, uhid_read_handler, uhid, NULL))
+		goto failed;
+
+	return bt_uhid_ref(uhid);
+
+failed:
+	uhid_free(uhid);
+	return NULL;
+}
+
+struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid)
+{
+	if (!uhid)
+		return NULL;
+
+	__sync_fetch_and_add(&uhid->ref_count, 1);
+
+	return uhid;
+}
+
+void bt_uhid_unref(struct bt_uhid *uhid)
+{
+	if (!uhid)
+		return;
+
+	if (__sync_sub_and_fetch(&uhid->ref_count, 1))
+		return;
+
+	uhid_free(uhid);
+}
+
+bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close)
+{
+	if (!uhid || !uhid->io)
+		return false;
+
+	io_set_close_on_destroy(uhid->io, do_close);
+
+	return true;
+}
+
+unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
+				bt_uhid_callback_t func, void *user_data)
+{
+	struct uhid_notify *notify;
+
+	if (!uhid)
+		return 0;
+
+	notify = new0(struct uhid_notify, 1);
+	notify->id = uhid->notify_id++;
+	notify->event = event;
+	notify->func = func;
+	notify->user_data = user_data;
+
+	if (!queue_push_tail(uhid->notify_list, notify)) {
+		free(notify);
+		return 0;
+	}
+
+	return notify->id;
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+	const struct uhid_notify *notify = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return notify->id == id;
+}
+
+bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id)
+{
+	struct uhid_notify *notify;
+
+	if (!uhid || !id)
+		return false;
+
+	notify = queue_remove_if(uhid->notify_list, match_notify_id,
+							UINT_TO_PTR(id));
+	if (!notify)
+		return false;
+
+	free(notify);
+	return true;
+}
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
+{
+	ssize_t len;
+	struct iovec iov;
+
+	if (!uhid->io)
+		return -ENOTCONN;
+
+	iov.iov_base = (void *) ev;
+	iov.iov_len = sizeof(*ev);
+
+	len = io_send(uhid->io, &iov, 1);
+	if (len < 0)
+		return -errno;
+
+	/* uHID kernel driver does not handle partial writes */
+	return len != sizeof(*ev) ? -EIO : 0;
+}
diff --git a/bluez/repo/src/shared/uhid.h b/bluez/repo/src/shared/uhid.h
new file mode 100644
index 0000000..459a249
--- /dev/null
+++ b/bluez/repo/src/shared/uhid.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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>
+
+#include "profiles/input/uhid_copy.h"
+
+struct bt_uhid;
+
+struct bt_uhid *bt_uhid_new_default(void);
+struct bt_uhid *bt_uhid_new(int fd);
+
+struct bt_uhid *bt_uhid_ref(struct bt_uhid *uhid);
+void bt_uhid_unref(struct bt_uhid *uhid);
+
+bool bt_uhid_set_close_on_unref(struct bt_uhid *uhid, bool do_close);
+
+typedef void (*bt_uhid_callback_t)(struct uhid_event *ev, void *user_data);
+unsigned int bt_uhid_register(struct bt_uhid *uhid, uint32_t event,
+				bt_uhid_callback_t func, void *user_data);
+bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id);
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev);
diff --git a/bluez/repo/src/shared/util.c b/bluez/repo/src/shared/util.c
new file mode 100644
index 0000000..7878552
--- /dev/null
+++ b/bluez/repo/src/shared/util.c
@@ -0,0 +1,153 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <ctype.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <string.h>
+
+#include "src/shared/util.h"
+
+void *btd_malloc(size_t size)
+{
+	if (__builtin_expect(!!size, 1)) {
+		void *ptr;
+
+		ptr = malloc(size);
+		if (ptr)
+			return ptr;
+
+		fprintf(stderr, "failed to allocate %zu bytes\n", size);
+		abort();
+	}
+
+	return NULL;
+}
+
+void util_debug(util_debug_func_t function, void *user_data,
+						const char *format, ...)
+{
+	char str[78];
+	va_list ap;
+
+	if (!function || !format)
+		return;
+
+	va_start(ap, format);
+	vsnprintf(str, sizeof(str), format, ap);
+	va_end(ap);
+
+	function(str, user_data);
+}
+
+void util_hexdump(const char dir, const unsigned char *buf, size_t len,
+				util_debug_func_t function, void *user_data)
+{
+	static const char hexdigits[] = "0123456789abcdef";
+	char str[68];
+	size_t i;
+
+	if (!function || !len)
+		return;
+
+	str[0] = dir;
+
+	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';
+			function(str, user_data);
+			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';
+		function(str, user_data);
+	}
+}
+
+/* Helper for getting the dirent type in case readdir returns DT_UNKNOWN */
+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);
+	if (lstat(filename, &st) == 0 && S_ISDIR(st.st_mode))
+		return DT_DIR;
+
+	return DT_UNKNOWN;
+}
+
+/* Helpers for bitfield operations */
+
+/* Find unique id in range from 1 to max but no bigger then
+ * sizeof(int) * 8. ffs() is used since it is POSIX standard
+ */
+uint8_t util_get_uid(unsigned int *bitmap, uint8_t max)
+{
+	uint8_t id;
+
+	id = ffs(~*bitmap);
+
+	if (!id || id > max)
+		return 0;
+
+	*bitmap |= 1 << (id - 1);
+
+	return id;
+}
+
+/* Clear id bit in bitmap */
+void util_clear_uid(unsigned int *bitmap, uint8_t id)
+{
+	if (!id)
+		return;
+
+	*bitmap &= ~(1 << (id - 1));
+}
diff --git a/bluez/repo/src/shared/util.h b/bluez/repo/src/shared/util.h
new file mode 100644
index 0000000..ff705d0
--- /dev/null
+++ b/bluez/repo/src/shared/util.h
@@ -0,0 +1,174 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  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 <stdint.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <byteswap.h>
+#include <string.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
+#define cpu_to_le16(val) (val)
+#define cpu_to_le32(val) (val)
+#define cpu_to_le64(val) (val)
+#define be16_to_cpu(val) bswap_16(val)
+#define be32_to_cpu(val) bswap_32(val)
+#define be64_to_cpu(val) bswap_64(val)
+#define cpu_to_be16(val) bswap_16(val)
+#define cpu_to_be32(val) bswap_32(val)
+#define cpu_to_be64(val) bswap_64(val)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
+#define cpu_to_le16(val) bswap_16(val)
+#define cpu_to_le32(val) bswap_32(val)
+#define cpu_to_le64(val) bswap_64(val)
+#define be16_to_cpu(val) (val)
+#define be32_to_cpu(val) (val)
+#define be64_to_cpu(val) (val)
+#define cpu_to_be16(val) (val)
+#define cpu_to_be32(val) (val)
+#define cpu_to_be64(val) (val)
+#else
+#error "Unknown byte order"
+#endif
+
+#define get_unaligned(ptr)			\
+__extension__ ({				\
+	struct __attribute__((packed)) {	\
+		__typeof__(*(ptr)) __v;		\
+	} *__p = (__typeof__(__p)) (ptr);	\
+	__p->__v;				\
+})
+
+#define put_unaligned(val, ptr)			\
+do {						\
+	struct __attribute__((packed)) {	\
+		__typeof__(*(ptr)) __v;		\
+	} *__p = (__typeof__(__p)) (ptr);	\
+	__p->__v = (val);			\
+} while (0)
+
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
+
+#define new0(type, count)			\
+	(type *) (__extension__ ({		\
+		size_t __n = (size_t) (count);	\
+		size_t __s = sizeof(type);	\
+		void *__p;			\
+		__p = btd_malloc(__n * __s);	\
+		memset(__p, 0, __n * __s);	\
+		__p;				\
+	}))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+#define malloc0(n) (calloc((n), 1))
+
+void *btd_malloc(size_t size);
+
+typedef void (*util_debug_func_t)(const char *str, void *user_data);
+
+void util_debug(util_debug_func_t function, void *user_data,
+						const char *format, ...)
+					__attribute__((format(printf, 3, 4)));
+
+void util_hexdump(const char dir, const unsigned char *buf, size_t len,
+				util_debug_func_t function, void *user_data);
+
+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);
+
+static inline uint8_t get_u8(const void *ptr)
+{
+	return *((uint8_t *) ptr);
+}
+
+static inline uint16_t get_le16(const void *ptr)
+{
+	return le16_to_cpu(get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint16_t get_be16(const void *ptr)
+{
+	return be16_to_cpu(get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint32_t get_le32(const void *ptr)
+{
+	return le32_to_cpu(get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+	return be32_to_cpu(get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint64_t get_le64(const void *ptr)
+{
+	return le64_to_cpu(get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint64_t get_be64(const void *ptr)
+{
+	return be64_to_cpu(get_unaligned((const uint64_t *) ptr));
+}
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+	put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
+}
+
+static inline void put_be16(uint16_t val, const void *ptr)
+{
+	put_unaligned(cpu_to_be16(val), (uint16_t *) ptr);
+}
+
+static inline void put_le32(uint32_t val, void *dst)
+{
+	put_unaligned(cpu_to_le32(val), (uint32_t *) dst);
+}
+
+static inline void put_be32(uint32_t val, void *dst)
+{
+	put_unaligned(cpu_to_be32(val), (uint32_t *) dst);
+}
+
+static inline void put_le64(uint64_t val, void *dst)
+{
+	put_unaligned(cpu_to_le64(val), (uint64_t *) dst);
+}
+
+static inline void put_be64(uint64_t val, void *dst)
+{
+	put_unaligned(cpu_to_be64(val), (uint64_t *) dst);
+}
diff --git a/bluez/repo/src/storage.c b/bluez/repo/src/storage.c
new file mode 100644
index 0000000..734a9e0
--- /dev/null
+++ b/bluez/repo/src/storage.c
@@ -0,0 +1,198 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "textfile.h"
+#include "uuid-helper.h"
+#include "storage.h"
+
+/* When all services should trust a remote device */
+#define GLOBAL_TRUST "[all]"
+
+struct match {
+	GSList *keys;
+	char *pattern;
+};
+
+static inline int create_filename(char *buf, size_t size,
+				const bdaddr_t *bdaddr, const char *name)
+{
+	char addr[18];
+
+	ba2str(bdaddr, addr);
+
+	return create_name(buf, size, STORAGEDIR, addr, name);
+}
+
+int read_discoverable_timeout(const char *src, int *timeout)
+{
+	char filename[PATH_MAX], *str;
+
+	create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+	str = textfile_get(filename, "discovto");
+	if (!str)
+		return -ENOENT;
+
+	if (sscanf(str, "%d", timeout) != 1) {
+		free(str);
+		return -ENOENT;
+	}
+
+	free(str);
+
+	return 0;
+}
+
+int read_pairable_timeout(const char *src, int *timeout)
+{
+	char filename[PATH_MAX], *str;
+
+	create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+	str = textfile_get(filename, "pairto");
+	if (!str)
+		return -ENOENT;
+
+	if (sscanf(str, "%d", timeout) != 1) {
+		free(str);
+		return -ENOENT;
+	}
+
+	free(str);
+
+	return 0;
+}
+
+int read_on_mode(const char *src, char *mode, int length)
+{
+	char filename[PATH_MAX], *str;
+
+	create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+	str = textfile_get(filename, "onmode");
+	if (!str)
+		return -ENOENT;
+
+	strncpy(mode, str, length);
+	mode[length - 1] = '\0';
+
+	free(str);
+
+	return 0;
+}
+
+int read_local_name(const bdaddr_t *bdaddr, char *name)
+{
+	char filename[PATH_MAX], *str;
+	int len;
+
+	create_filename(filename, PATH_MAX, bdaddr, "config");
+
+	str = textfile_get(filename, "name");
+	if (!str)
+		return -ENOENT;
+
+	len = strlen(str);
+	if (len > HCI_MAX_NAME_LENGTH)
+		str[HCI_MAX_NAME_LENGTH] = '\0';
+	strcpy(name, str);
+
+	free(str);
+
+	return 0;
+}
+
+sdp_record_t *record_from_string(const char *str)
+{
+	sdp_record_t *rec;
+	int size, i, len;
+	uint8_t *pdata;
+	char tmp[3];
+
+	size = strlen(str)/2;
+	pdata = g_malloc0(size);
+
+	tmp[2] = 0;
+	for (i = 0; i < size; i++) {
+		memcpy(tmp, str + (i * 2), 2);
+		pdata[i] = (uint8_t) strtol(tmp, NULL, 16);
+	}
+
+	rec = sdp_extract_pdu(pdata, size, &len);
+	g_free(pdata);
+
+	return rec;
+}
+
+sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid)
+{
+	sdp_list_t *seq;
+
+	for (seq = recs; seq; seq = seq->next) {
+		sdp_record_t *rec = (sdp_record_t *) seq->data;
+		sdp_list_t *svcclass = NULL;
+		char *uuid_str;
+
+		if (sdp_get_service_classes(rec, &svcclass) < 0)
+			continue;
+
+		/* Extract the uuid */
+		uuid_str = bt_uuid2string(svcclass->data);
+		if (!uuid_str) {
+			sdp_list_free(svcclass, free);
+			continue;
+		}
+
+		if (!strcasecmp(uuid_str, uuid)) {
+			sdp_list_free(svcclass, free);
+			free(uuid_str);
+			return rec;
+		}
+
+		sdp_list_free(svcclass, free);
+		free(uuid_str);
+	}
+	return NULL;
+}
diff --git a/bluez/repo/src/storage.h b/bluez/repo/src/storage.h
new file mode 100644
index 0000000..1c0ad57
--- /dev/null
+++ b/bluez/repo/src/storage.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-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
+ *
+ */
+
+int read_discoverable_timeout(const char *src, int *timeout);
+int read_pairable_timeout(const char *src, int *timeout);
+int read_on_mode(const char *src, char *mode, int length);
+int read_local_name(const bdaddr_t *bdaddr, char *name);
+sdp_record_t *record_from_string(const char *str);
+sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid);
diff --git a/bluez/repo/src/systemd.c b/bluez/repo/src/systemd.c
new file mode 100644
index 0000000..cf3da1c
--- /dev/null
+++ b/bluez/repo/src/systemd.c
@@ -0,0 +1,107 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "systemd.h"
+
+int sd_listen_fds(int unset_environment)
+{
+	return 0;
+}
+
+int sd_notify(int unset_environment, const char *state)
+{
+	const char *sock;
+	struct sockaddr_un addr;
+	struct msghdr msghdr;
+	struct iovec iovec;
+	int fd, err;
+
+	if (!state) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	sock = getenv("NOTIFY_SOCKET");
+	if (!sock)
+		return 0;
+
+	/* check for abstract socket or absolute path */
+	if (sock[0] != '@' && sock[0] != '/') {
+		err = -EINVAL;
+		goto done;
+	}
+
+	fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		err = -errno;
+		goto done;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, sock, sizeof(addr.sun_path));
+
+	if (addr.sun_path[0] == '@')
+		addr.sun_path[0] = '\0';
+
+	memset(&iovec, 0, sizeof(iovec));
+	iovec.iov_base = (char *) state;
+	iovec.iov_len = strlen(state);
+
+	memset(&msghdr, 0, sizeof(msghdr));
+	msghdr.msg_name = &addr;
+	msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) +
+								strlen(sock);
+
+	if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
+		msghdr.msg_namelen = sizeof(struct sockaddr_un);
+
+	msghdr.msg_iov = &iovec;
+	msghdr.msg_iovlen = 1;
+
+	if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
+		err = -errno;
+	else
+		err = 1;
+
+	close(fd);
+
+done:
+	if (unset_environment)
+		unsetenv("NOTIFY_SOCKET");
+
+	return err;
+}
diff --git a/bluez/repo/src/systemd.h b/bluez/repo/src/systemd.h
new file mode 100644
index 0000000..0ef7c82
--- /dev/null
+++ b/bluez/repo/src/systemd.h
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define SD_LISTEN_FDS_START 3
+
+int sd_listen_fds(int unset_environment);
+
+int sd_notify(int unset_environment, const char *state);
diff --git a/bluez/repo/src/textfile.c b/bluez/repo/src/textfile.c
new file mode 100644
index 0000000..7267f3a
--- /dev/null
+++ b/bluez/repo/src/textfile.c
@@ -0,0 +1,475 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include "textfile.h"
+
+static int create_dirs(const char *filename, const mode_t mode)
+{
+	struct stat st;
+	char dir[PATH_MAX + 1], *prev, *next;
+	int err;
+
+	err = stat(filename, &st);
+	if (!err && S_ISREG(st.st_mode))
+		return 0;
+
+	memset(dir, 0, PATH_MAX + 1);
+	strcat(dir, "/");
+
+	prev = strchr(filename, '/');
+
+	while (prev) {
+		next = strchr(prev + 1, '/');
+		if (!next)
+			break;
+
+		if (next - prev == 1) {
+			prev = next;
+			continue;
+		}
+
+		strncat(dir, prev + 1, next - prev);
+		mkdir(dir, mode);
+
+		prev = next;
+	}
+
+	return 0;
+}
+
+int create_file(const char *filename, const mode_t mode)
+{
+	int fd;
+
+	create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR);
+
+	fd = open(filename, O_RDWR | O_CREAT, mode);
+	if (fd < 0)
+		return fd;
+
+	close(fd);
+
+	return 0;
+}
+
+int create_name(char *buf, size_t size, const char *path, const char *address, const char *name)
+{
+	return snprintf(buf, size, "%s/%s/%s", path, address, name);
+}
+
+static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase)
+{
+	char *ptr = map;
+	size_t ptrlen = size;
+
+	while (ptrlen > len + 1) {
+		int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len);
+		if (cmp == 0) {
+			if (ptr == map && *(ptr + len) == ' ')
+				return ptr;
+
+			if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') &&
+							*(ptr + len) == ' ')
+				return ptr;
+		}
+
+		if (icase) {
+			char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1);
+			char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1);
+
+			if (!p1)
+				ptr = p2;
+			else if (!p2)
+				ptr = p1;
+			else
+				ptr = (p1 < p2) ? p1 : p2;
+		} else
+			ptr = memchr(ptr + 1, *key, ptrlen - 1);
+
+		if (!ptr)
+			return NULL;
+
+		ptrlen = size - (ptr - map);
+	}
+
+	return NULL;
+}
+
+static inline int write_key_value(int fd, const char *key, const char *value)
+{
+	char *str;
+	size_t size;
+	int err = 0;
+
+	size = strlen(key) + strlen(value) + 2;
+
+	str = malloc(size + 1);
+	if (!str)
+		return ENOMEM;
+
+	sprintf(str, "%s %s\n", key, value);
+
+	if (write(fd, str, size) < 0)
+		err = -errno;
+
+	free(str);
+
+	return err;
+}
+
+static char *strnpbrk(const char *s, ssize_t len, const char *accept)
+{
+	const char *p = s;
+	const char *end;
+
+	end = s + len - 1;
+
+	while (p <= end && *p) {
+		const char *a = accept;
+
+		while (*a) {
+			if (*p == *a)
+				return (char *) p;
+			a++;
+		}
+
+		p++;
+	}
+
+	return NULL;
+}
+
+static int write_key(const char *pathname, const char *key, const char *value, int icase)
+{
+	struct stat st;
+	char *map, *off, *end, *str;
+	off_t size;
+	size_t base;
+	int fd, len, err = 0;
+
+	fd = open(pathname, O_RDWR);
+	if (fd < 0)
+		return -errno;
+
+	if (flock(fd, LOCK_EX) < 0) {
+		err = -errno;
+		goto close;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		err = -errno;
+		goto unlock;
+	}
+
+	size = st.st_size;
+
+	if (!size) {
+		if (value) {
+			lseek(fd, size, SEEK_SET);
+			err = write_key_value(fd, key, value);
+		}
+		goto unlock;
+	}
+
+	map = mmap(NULL, size, PROT_READ | PROT_WRITE,
+					MAP_PRIVATE | MAP_LOCKED, fd, 0);
+	if (!map || map == MAP_FAILED) {
+		err = -errno;
+		goto unlock;
+	}
+
+	len = strlen(key);
+	off = find_key(map, size, key, len, icase);
+	if (!off) {
+		munmap(map, size);
+		if (value) {
+			lseek(fd, size, SEEK_SET);
+			err = write_key_value(fd, key, value);
+		}
+		goto unlock;
+	}
+
+	base = off - map;
+
+	end = strnpbrk(off, size, "\r\n");
+	if (!end) {
+		err = -EILSEQ;
+		goto unmap;
+	}
+
+	if (value && ((ssize_t) strlen(value) == end - off - len - 1) &&
+			!strncmp(off + len + 1, value, end - off - len - 1))
+		goto unmap;
+
+	len = strspn(end, "\r\n");
+	end += len;
+
+	len = size - (end - map);
+	if (!len) {
+		munmap(map, size);
+		if (ftruncate(fd, base) < 0) {
+			err = -errno;
+			goto unlock;
+		}
+		lseek(fd, base, SEEK_SET);
+		if (value)
+			err = write_key_value(fd, key, value);
+
+		goto unlock;
+	}
+
+	if (len < 0 || len > size) {
+		err = -EILSEQ;
+		goto unmap;
+	}
+
+	str = malloc(len);
+	if (!str) {
+		err = -errno;
+		goto unmap;
+	}
+
+	memcpy(str, end, len);
+
+	munmap(map, size);
+	if (ftruncate(fd, base) < 0) {
+		err = -errno;
+		free(str);
+		goto unlock;
+	}
+	lseek(fd, base, SEEK_SET);
+	if (value)
+		err = write_key_value(fd, key, value);
+
+	if (write(fd, str, len) < 0)
+		err = -errno;
+
+	free(str);
+
+	goto unlock;
+
+unmap:
+	munmap(map, size);
+
+unlock:
+	flock(fd, LOCK_UN);
+
+close:
+	fdatasync(fd);
+
+	close(fd);
+	errno = -err;
+
+	return err;
+}
+
+static char *read_key(const char *pathname, const char *key, int icase)
+{
+	struct stat st;
+	char *map, *off, *end, *str = NULL;
+	off_t size; size_t len;
+	int fd, err = 0;
+
+	fd = open(pathname, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	if (flock(fd, LOCK_SH) < 0) {
+		err = -errno;
+		goto close;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		err = -errno;
+		goto unlock;
+	}
+
+	size = st.st_size;
+
+	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+	if (!map || map == MAP_FAILED) {
+		err = -errno;
+		goto unlock;
+	}
+
+	len = strlen(key);
+	off = find_key(map, size, key, len, icase);
+	if (!off) {
+		err = -EILSEQ;
+		goto unmap;
+	}
+
+	end = strnpbrk(off, size - (off - map), "\r\n");
+	if (!end) {
+		err = -EILSEQ;
+		goto unmap;
+	}
+
+	str = malloc(end - off - len);
+	if (!str) {
+		err = -EILSEQ;
+		goto unmap;
+	}
+
+	memset(str, 0, end - off - len);
+	strncpy(str, off + len + 1, end - off - len - 1);
+
+unmap:
+	munmap(map, size);
+
+unlock:
+	flock(fd, LOCK_UN);
+
+close:
+	close(fd);
+	errno = -err;
+
+	return str;
+}
+
+int textfile_put(const char *pathname, const char *key, const char *value)
+{
+	return write_key(pathname, key, value, 0);
+}
+
+int textfile_del(const char *pathname, const char *key)
+{
+	return write_key(pathname, key, NULL, 0);
+}
+
+char *textfile_get(const char *pathname, const char *key)
+{
+	return read_key(pathname, key, 0);
+}
+
+int textfile_foreach(const char *pathname, textfile_cb func, void *data)
+{
+	struct stat st;
+	char *map, *off, *end, *key, *value;
+	off_t size; size_t len;
+	int fd, err = 0;
+
+	fd = open(pathname, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+
+	if (flock(fd, LOCK_SH) < 0) {
+		err = -errno;
+		goto close;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		err = -errno;
+		goto unlock;
+	}
+
+	size = st.st_size;
+
+	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+	if (!map || map == MAP_FAILED) {
+		err = -errno;
+		goto unlock;
+	}
+
+	off = map;
+
+	while (size - (off - map) > 0) {
+		end = strnpbrk(off, size - (off - map), " ");
+		if (!end) {
+			err = -EILSEQ;
+			break;
+		}
+
+		len = end - off;
+
+		key = malloc(len + 1);
+		if (!key) {
+			err = -errno;
+			break;
+		}
+
+		memset(key, 0, len + 1);
+		memcpy(key, off, len);
+
+		off = end + 1;
+
+		if (size - (off - map) < 0) {
+			err = -EILSEQ;
+			free(key);
+			break;
+		}
+
+		end = strnpbrk(off, size - (off - map), "\r\n");
+		if (!end) {
+			err = -EILSEQ;
+			free(key);
+			break;
+		}
+
+		len = end - off;
+
+		value = malloc(len + 1);
+		if (!value) {
+			err = -errno;
+			free(key);
+			break;
+		}
+
+		memset(value, 0, len + 1);
+		memcpy(value, off, len);
+
+		func(key, value, data);
+
+		free(key);
+		free(value);
+
+		off = end + 1;
+	}
+
+	munmap(map, size);
+
+unlock:
+	flock(fd, LOCK_UN);
+
+close:
+	close(fd);
+	errno = -err;
+
+	return 0;
+}
diff --git a/bluez/repo/src/textfile.h b/bluez/repo/src/textfile.h
new file mode 100644
index 0000000..f01629e
--- /dev/null
+++ b/bluez/repo/src/textfile.h
@@ -0,0 +1,34 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+int create_file(const char *filename, const mode_t mode);
+int create_name(char *buf, size_t size, const char *path,
+				const char *address, const char *name);
+
+int textfile_put(const char *pathname, const char *key, const char *value);
+int textfile_del(const char *pathname, const char *key);
+char *textfile_get(const char *pathname, const char *key);
+
+typedef void (*textfile_cb) (char *key, char *value, void *data);
+
+int textfile_foreach(const char *pathname, textfile_cb func, void *data);
diff --git a/bluez/repo/src/uinput.h b/bluez/repo/src/uinput.h
new file mode 100644
index 0000000..20e0941
--- /dev/null
+++ b/bluez/repo/src/uinput.h
@@ -0,0 +1,724 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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
+ *
+ */
+
+#ifndef __UINPUT_H
+#define __UINPUT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+/* Events */
+
+#define EV_SYN			0x00
+#define EV_KEY			0x01
+#define EV_REL			0x02
+#define EV_ABS			0x03
+#define EV_MSC			0x04
+#define EV_LED			0x11
+#define EV_SND			0x12
+#define EV_REP			0x14
+#define EV_FF			0x15
+#define EV_PWR			0x16
+#define EV_FF_STATUS		0x17
+#define EV_MAX			0x1f
+
+/* Synchronization events */
+
+#define SYN_REPORT		0
+#define SYN_CONFIG		1
+
+/*
+ * Keys and buttons
+ *
+ * Most of the keys/buttons are modelled after USB HUT 1.12
+ * (see http://www.usb.org/developers/hidpage).
+ * Abbreviations in the comments:
+ * AC - Application Control
+ * AL - Application Launch Button
+ * SC - System Control
+ */
+
+#define KEY_RESERVED		0
+#define KEY_ESC			1
+#define KEY_1			2
+#define KEY_2			3
+#define KEY_3			4
+#define KEY_4			5
+#define KEY_5			6
+#define KEY_6			7
+#define KEY_7			8
+#define KEY_8			9
+#define KEY_9			10
+#define KEY_0			11
+#define KEY_MINUS		12
+#define KEY_EQUAL		13
+#define KEY_BACKSPACE		14
+#define KEY_TAB			15
+#define KEY_Q			16
+#define KEY_W			17
+#define KEY_E			18
+#define KEY_R			19
+#define KEY_T			20
+#define KEY_Y			21
+#define KEY_U			22
+#define KEY_I			23
+#define KEY_O			24
+#define KEY_P			25
+#define KEY_LEFTBRACE		26
+#define KEY_RIGHTBRACE		27
+#define KEY_ENTER		28
+#define KEY_LEFTCTRL		29
+#define KEY_A			30
+#define KEY_S			31
+#define KEY_D			32
+#define KEY_F			33
+#define KEY_G			34
+#define KEY_H			35
+#define KEY_J			36
+#define KEY_K			37
+#define KEY_L			38
+#define KEY_SEMICOLON		39
+#define KEY_APOSTROPHE		40
+#define KEY_GRAVE		41
+#define KEY_LEFTSHIFT		42
+#define KEY_BACKSLASH		43
+#define KEY_Z			44
+#define KEY_X			45
+#define KEY_C			46
+#define KEY_V			47
+#define KEY_B			48
+#define KEY_N			49
+#define KEY_M			50
+#define KEY_COMMA		51
+#define KEY_DOT			52
+#define KEY_SLASH		53
+#define KEY_RIGHTSHIFT		54
+#define KEY_KPASTERISK		55
+#define KEY_LEFTALT		56
+#define KEY_SPACE		57
+#define KEY_CAPSLOCK		58
+#define KEY_F1			59
+#define KEY_F2			60
+#define KEY_F3			61
+#define KEY_F4			62
+#define KEY_F5			63
+#define KEY_F6			64
+#define KEY_F7			65
+#define KEY_F8			66
+#define KEY_F9			67
+#define KEY_F10			68
+#define KEY_NUMLOCK		69
+#define KEY_SCROLLLOCK		70
+#define KEY_KP7			71
+#define KEY_KP8			72
+#define KEY_KP9			73
+#define KEY_KPMINUS		74
+#define KEY_KP4			75
+#define KEY_KP5			76
+#define KEY_KP6			77
+#define KEY_KPPLUS		78
+#define KEY_KP1			79
+#define KEY_KP2			80
+#define KEY_KP3			81
+#define KEY_KP0			82
+#define KEY_KPDOT		83
+
+#define KEY_ZENKAKUHANKAKU	85
+#define KEY_102ND		86
+#define KEY_F11			87
+#define KEY_F12			88
+#define KEY_RO			89
+#define KEY_KATAKANA		90
+#define KEY_HIRAGANA		91
+#define KEY_HENKAN		92
+#define KEY_KATAKANAHIRAGANA	93
+#define KEY_MUHENKAN		94
+#define KEY_KPJPCOMMA		95
+#define KEY_KPENTER		96
+#define KEY_RIGHTCTRL		97
+#define KEY_KPSLASH		98
+#define KEY_SYSRQ		99
+#define KEY_RIGHTALT		100
+#define KEY_LINEFEED		101
+#define KEY_HOME		102
+#define KEY_UP			103
+#define KEY_PAGEUP		104
+#define KEY_LEFT		105
+#define KEY_RIGHT		106
+#define KEY_END			107
+#define KEY_DOWN		108
+#define KEY_PAGEDOWN		109
+#define KEY_INSERT		110
+#define KEY_DELETE		111
+#define KEY_MACRO		112
+#define KEY_MUTE		113
+#define KEY_VOLUMEDOWN		114
+#define KEY_VOLUMEUP		115
+#define KEY_POWER		116	/* SC System Power Down */
+#define KEY_KPEQUAL		117
+#define KEY_KPPLUSMINUS		118
+#define KEY_PAUSE		119
+
+#define KEY_KPCOMMA		121
+#define KEY_HANGEUL		122
+#define KEY_HANGUEL		KEY_HANGEUL
+#define KEY_HANJA		123
+#define KEY_YEN			124
+#define KEY_LEFTMETA		125
+#define KEY_RIGHTMETA		126
+#define KEY_COMPOSE		127
+
+#define KEY_STOP		128	/* AC Stop */
+#define KEY_AGAIN		129
+#define KEY_PROPS		130	/* AC Properties */
+#define KEY_UNDO		131	/* AC Undo */
+#define KEY_FRONT		132
+#define KEY_COPY		133	/* AC Copy */
+#define KEY_OPEN		134	/* AC Open */
+#define KEY_PASTE		135	/* AC Paste */
+#define KEY_FIND		136	/* AC Search */
+#define KEY_CUT			137	/* AC Cut */
+#define KEY_HELP		138	/* AL Integrated Help Center */
+#define KEY_MENU		139	/* Menu (show menu) */
+#define KEY_CALC		140	/* AL Calculator */
+#define KEY_SETUP		141
+#define KEY_SLEEP		142	/* SC System Sleep */
+#define KEY_WAKEUP		143	/* System Wake Up */
+#define KEY_FILE		144	/* AL Local Machine Browser */
+#define KEY_SENDFILE		145
+#define KEY_DELETEFILE		146
+#define KEY_XFER		147
+#define KEY_PROG1		148
+#define KEY_PROG2		149
+#define KEY_WWW			150	/* AL Internet Browser */
+#define KEY_MSDOS		151
+#define KEY_COFFEE		152	/* AL Terminal Lock/Screensaver */
+#define KEY_SCREENLOCK		KEY_COFFEE
+#define KEY_DIRECTION		153
+#define KEY_CYCLEWINDOWS	154
+#define KEY_MAIL		155
+#define KEY_BOOKMARKS		156	/* AC Bookmarks */
+#define KEY_COMPUTER		157
+#define KEY_BACK		158	/* AC Back */
+#define KEY_FORWARD		159	/* AC Forward */
+#define KEY_CLOSECD		160
+#define KEY_EJECTCD		161
+#define KEY_EJECTCLOSECD	162
+#define KEY_NEXTSONG		163
+#define KEY_PLAYPAUSE		164
+#define KEY_PREVIOUSSONG	165
+#define KEY_STOPCD		166
+#define KEY_RECORD		167
+#define KEY_REWIND		168
+#define KEY_PHONE		169	/* Media Select Telephone */
+#define KEY_ISO			170
+#define KEY_CONFIG		171	/* AL Consumer Control Configuration */
+#define KEY_HOMEPAGE		172	/* AC Home */
+#define KEY_REFRESH		173	/* AC Refresh */
+#define KEY_EXIT		174	/* AC Exit */
+#define KEY_MOVE		175
+#define KEY_EDIT		176
+#define KEY_SCROLLUP		177
+#define KEY_SCROLLDOWN		178
+#define KEY_KPLEFTPAREN		179
+#define KEY_KPRIGHTPAREN	180
+#define KEY_NEW			181	/* AC New */
+#define KEY_REDO		182	/* AC Redo/Repeat */
+
+#define KEY_F13			183
+#define KEY_F14			184
+#define KEY_F15			185
+#define KEY_F16			186
+#define KEY_F17			187
+#define KEY_F18			188
+#define KEY_F19			189
+#define KEY_F20			190
+#define KEY_F21			191
+#define KEY_F22			192
+#define KEY_F23			193
+#define KEY_F24			194
+
+#define KEY_PLAYCD		200
+#define KEY_PAUSECD		201
+#define KEY_PROG3		202
+#define KEY_PROG4		203
+#define KEY_SUSPEND		205
+#define KEY_CLOSE		206	/* AC Close */
+#define KEY_PLAY		207
+#define KEY_FASTFORWARD		208
+#define KEY_BASSBOOST		209
+#define KEY_PRINT		210	/* AC Print */
+#define KEY_HP			211
+#define KEY_CAMERA		212
+#define KEY_SOUND		213
+#define KEY_QUESTION		214
+#define KEY_EMAIL		215
+#define KEY_CHAT		216
+#define KEY_SEARCH		217
+#define KEY_CONNECT		218
+#define KEY_FINANCE		219	/* AL Checkbook/Finance */
+#define KEY_SPORT		220
+#define KEY_SHOP		221
+#define KEY_ALTERASE		222
+#define KEY_CANCEL		223	/* AC Cancel */
+#define KEY_BRIGHTNESSDOWN	224
+#define KEY_BRIGHTNESSUP	225
+#define KEY_MEDIA		226
+
+#define KEY_SWITCHVIDEOMODE	227	/* Cycle between available video
+					   outputs (Monitor/LCD/TV-out/etc) */
+#define KEY_KBDILLUMTOGGLE	228
+#define KEY_KBDILLUMDOWN	229
+#define KEY_KBDILLUMUP		230
+
+#define KEY_SEND		231	/* AC Send */
+#define KEY_REPLY		232	/* AC Reply */
+#define KEY_FORWARDMAIL		233	/* AC Forward Msg */
+#define KEY_SAVE		234	/* AC Save */
+#define KEY_DOCUMENTS		235
+
+#define KEY_BATTERY		236
+
+#define KEY_BLUETOOTH		237
+#define KEY_WLAN		238
+#define KEY_UWB			239
+
+#define KEY_UNKNOWN		240
+
+#define KEY_VIDEO_NEXT		241	/* drive next video source */
+#define KEY_VIDEO_PREV		242	/* drive previous video source */
+#define KEY_BRIGHTNESS_CYCLE	243	/* brightness up, after max is min */
+#define KEY_BRIGHTNESS_ZERO	244	/* brightness off, use ambient */
+#define KEY_DISPLAY_OFF		245	/* display device to off state */
+
+#define KEY_WIMAX		246
+
+/* Range 248 - 255 is reserved for special needs of AT keyboard driver */
+
+#define BTN_MISC		0x100
+#define BTN_0			0x100
+#define BTN_1			0x101
+#define BTN_2			0x102
+#define BTN_3			0x103
+#define BTN_4			0x104
+#define BTN_5			0x105
+#define BTN_6			0x106
+#define BTN_7			0x107
+#define BTN_8			0x108
+#define BTN_9			0x109
+
+#define BTN_MOUSE		0x110
+#define BTN_LEFT		0x110
+#define BTN_RIGHT		0x111
+#define BTN_MIDDLE		0x112
+#define BTN_SIDE		0x113
+#define BTN_EXTRA		0x114
+#define BTN_FORWARD		0x115
+#define BTN_BACK		0x116
+#define BTN_TASK		0x117
+
+#define BTN_JOYSTICK		0x120
+#define BTN_TRIGGER		0x120
+#define BTN_THUMB		0x121
+#define BTN_THUMB2		0x122
+#define BTN_TOP			0x123
+#define BTN_TOP2		0x124
+#define BTN_PINKIE		0x125
+#define BTN_BASE		0x126
+#define BTN_BASE2		0x127
+#define BTN_BASE3		0x128
+#define BTN_BASE4		0x129
+#define BTN_BASE5		0x12a
+#define BTN_BASE6		0x12b
+#define BTN_DEAD		0x12f
+
+#define BTN_GAMEPAD		0x130
+#define BTN_A			0x130
+#define BTN_B			0x131
+#define BTN_C			0x132
+#define BTN_X			0x133
+#define BTN_Y			0x134
+#define BTN_Z			0x135
+#define BTN_TL			0x136
+#define BTN_TR			0x137
+#define BTN_TL2			0x138
+#define BTN_TR2			0x139
+#define BTN_SELECT		0x13a
+#define BTN_START		0x13b
+#define BTN_MODE		0x13c
+#define BTN_THUMBL		0x13d
+#define BTN_THUMBR		0x13e
+
+#define BTN_DIGI		0x140
+#define BTN_TOOL_PEN		0x140
+#define BTN_TOOL_RUBBER		0x141
+#define BTN_TOOL_BRUSH		0x142
+#define BTN_TOOL_PENCIL		0x143
+#define BTN_TOOL_AIRBRUSH	0x144
+#define BTN_TOOL_FINGER		0x145
+#define BTN_TOOL_MOUSE		0x146
+#define BTN_TOOL_LENS		0x147
+#define BTN_TOUCH		0x14a
+#define BTN_STYLUS		0x14b
+#define BTN_STYLUS2		0x14c
+#define BTN_TOOL_DOUBLETAP	0x14d
+#define BTN_TOOL_TRIPLETAP	0x14e
+
+#define BTN_WHEEL		0x150
+#define BTN_GEAR_DOWN		0x150
+#define BTN_GEAR_UP		0x151
+
+#define KEY_OK			0x160
+#define KEY_SELECT		0x161
+#define KEY_GOTO		0x162
+#define KEY_CLEAR		0x163
+#define KEY_POWER2		0x164
+#define KEY_OPTION		0x165
+#define KEY_INFO		0x166	/* AL OEM Features/Tips/Tutorial */
+#define KEY_TIME		0x167
+#define KEY_VENDOR		0x168
+#define KEY_ARCHIVE		0x169
+#define KEY_PROGRAM		0x16a	/* Media Select Program Guide */
+#define KEY_CHANNEL		0x16b
+#define KEY_FAVORITES		0x16c
+#define KEY_EPG			0x16d
+#define KEY_PVR			0x16e	/* Media Select Home */
+#define KEY_MHP			0x16f
+#define KEY_LANGUAGE		0x170
+#define KEY_TITLE		0x171
+#define KEY_SUBTITLE		0x172
+#define KEY_ANGLE		0x173
+#define KEY_ZOOM		0x174
+#define KEY_MODE		0x175
+#define KEY_KEYBOARD		0x176
+#define KEY_SCREEN		0x177
+#define KEY_PC			0x178	/* Media Select Computer */
+#define KEY_TV			0x179	/* Media Select TV */
+#define KEY_TV2			0x17a	/* Media Select Cable */
+#define KEY_VCR			0x17b	/* Media Select VCR */
+#define KEY_VCR2		0x17c	/* VCR Plus */
+#define KEY_SAT			0x17d	/* Media Select Satellite */
+#define KEY_SAT2		0x17e
+#define KEY_CD			0x17f	/* Media Select CD */
+#define KEY_TAPE		0x180	/* Media Select Tape */
+#define KEY_RADIO		0x181
+#define KEY_TUNER		0x182	/* Media Select Tuner */
+#define KEY_PLAYER		0x183
+#define KEY_TEXT		0x184
+#define KEY_DVD			0x185	/* Media Select DVD */
+#define KEY_AUX			0x186
+#define KEY_MP3			0x187
+#define KEY_AUDIO		0x188
+#define KEY_VIDEO		0x189
+#define KEY_DIRECTORY		0x18a
+#define KEY_LIST		0x18b
+#define KEY_MEMO		0x18c	/* Media Select Messages */
+#define KEY_CALENDAR		0x18d
+#define KEY_RED			0x18e
+#define KEY_GREEN		0x18f
+#define KEY_YELLOW		0x190
+#define KEY_BLUE		0x191
+#define KEY_CHANNELUP		0x192	/* Channel Increment */
+#define KEY_CHANNELDOWN		0x193	/* Channel Decrement */
+#define KEY_FIRST		0x194
+#define KEY_LAST		0x195	/* Recall Last */
+#define KEY_AB			0x196
+#define KEY_NEXT		0x197
+#define KEY_RESTART		0x198
+#define KEY_SLOW		0x199
+#define KEY_SHUFFLE		0x19a
+#define KEY_BREAK		0x19b
+#define KEY_PREVIOUS		0x19c
+#define KEY_DIGITS		0x19d
+#define KEY_TEEN		0x19e
+#define KEY_TWEN		0x19f
+#define KEY_VIDEOPHONE		0x1a0	/* Media Select Video Phone */
+#define KEY_GAMES		0x1a1	/* Media Select Games */
+#define KEY_ZOOMIN		0x1a2	/* AC Zoom In */
+#define KEY_ZOOMOUT		0x1a3	/* AC Zoom Out */
+#define KEY_ZOOMRESET		0x1a4	/* AC Zoom */
+#define KEY_WORDPROCESSOR	0x1a5	/* AL Word Processor */
+#define KEY_EDITOR		0x1a6	/* AL Text Editor */
+#define KEY_SPREADSHEET		0x1a7	/* AL Spreadsheet */
+#define KEY_GRAPHICSEDITOR	0x1a8	/* AL Graphics Editor */
+#define KEY_PRESENTATION	0x1a9	/* AL Presentation App */
+#define KEY_DATABASE		0x1aa	/* AL Database App */
+#define KEY_NEWS		0x1ab	/* AL Newsreader */
+#define KEY_VOICEMAIL		0x1ac	/* AL Voicemail */
+#define KEY_ADDRESSBOOK		0x1ad	/* AL Contacts/Address Book */
+#define KEY_MESSENGER		0x1ae	/* AL Instant Messaging */
+#define KEY_DISPLAYTOGGLE	0x1af	/* Turn display (LCD) on and off */
+#define KEY_SPELLCHECK		0x1b0   /* AL Spell Check */
+#define KEY_LOGOFF		0x1b1   /* AL Logoff */
+
+#define KEY_DOLLAR		0x1b2
+#define KEY_EURO		0x1b3
+
+#define KEY_FRAMEBACK		0x1b4	/* Consumer - transport controls */
+#define KEY_FRAMEFORWARD	0x1b5
+#define KEY_CONTEXT_MENU	0x1b6	/* GenDesc - system context menu */
+#define KEY_MEDIA_REPEAT	0x1b7	/* Consumer - transport control */
+
+#define KEY_DEL_EOL		0x1c0
+#define KEY_DEL_EOS		0x1c1
+#define KEY_INS_LINE		0x1c2
+#define KEY_DEL_LINE		0x1c3
+
+#define KEY_FN			0x1d0
+#define KEY_FN_ESC		0x1d1
+#define KEY_FN_F1		0x1d2
+#define KEY_FN_F2		0x1d3
+#define KEY_FN_F3		0x1d4
+#define KEY_FN_F4		0x1d5
+#define KEY_FN_F5		0x1d6
+#define KEY_FN_F6		0x1d7
+#define KEY_FN_F7		0x1d8
+#define KEY_FN_F8		0x1d9
+#define KEY_FN_F9		0x1da
+#define KEY_FN_F10		0x1db
+#define KEY_FN_F11		0x1dc
+#define KEY_FN_F12		0x1dd
+#define KEY_FN_1		0x1de
+#define KEY_FN_2		0x1df
+#define KEY_FN_D		0x1e0
+#define KEY_FN_E		0x1e1
+#define KEY_FN_F		0x1e2
+#define KEY_FN_S		0x1e3
+#define KEY_FN_B		0x1e4
+
+#define KEY_BRL_DOT1		0x1f1
+#define KEY_BRL_DOT2		0x1f2
+#define KEY_BRL_DOT3		0x1f3
+#define KEY_BRL_DOT4		0x1f4
+#define KEY_BRL_DOT5		0x1f5
+#define KEY_BRL_DOT6		0x1f6
+#define KEY_BRL_DOT7		0x1f7
+#define KEY_BRL_DOT8		0x1f8
+#define KEY_BRL_DOT9		0x1f9
+#define KEY_BRL_DOT10		0x1fa
+
+/* We avoid low common keys in module aliases so they don't get huge. */
+#define KEY_MIN_INTERESTING	KEY_MUTE
+#define KEY_MAX			0x1ff
+#define KEY_CNT			(KEY_MAX+1)
+
+/*
+ * Relative axes
+ */
+
+#define REL_X			0x00
+#define REL_Y			0x01
+#define REL_Z			0x02
+#define REL_RX			0x03
+#define REL_RY			0x04
+#define REL_RZ			0x05
+#define REL_HWHEEL		0x06
+#define REL_DIAL		0x07
+#define REL_WHEEL		0x08
+#define REL_MISC		0x09
+#define REL_MAX			0x0f
+#define REL_CNT			(REL_MAX+1)
+
+/*
+ * Absolute axes
+ */
+
+#define ABS_X			0x00
+#define ABS_Y			0x01
+#define ABS_Z			0x02
+#define ABS_RX			0x03
+#define ABS_RY			0x04
+#define ABS_RZ			0x05
+#define ABS_THROTTLE		0x06
+#define ABS_RUDDER		0x07
+#define ABS_WHEEL		0x08
+#define ABS_GAS			0x09
+#define ABS_BRAKE		0x0a
+#define ABS_HAT0X		0x10
+#define ABS_HAT0Y		0x11
+#define ABS_HAT1X		0x12
+#define ABS_HAT1Y		0x13
+#define ABS_HAT2X		0x14
+#define ABS_HAT2Y		0x15
+#define ABS_HAT3X		0x16
+#define ABS_HAT3Y		0x17
+#define ABS_PRESSURE		0x18
+#define ABS_DISTANCE		0x19
+#define ABS_TILT_X		0x1a
+#define ABS_TILT_Y		0x1b
+#define ABS_TOOL_WIDTH		0x1c
+#define ABS_VOLUME		0x20
+#define ABS_MISC		0x28
+#define ABS_MAX			0x3f
+#define ABS_CNT			(ABS_MAX+1)
+
+/*
+ * Switch events
+ */
+
+#define SW_LID			0x00  /* set = lid shut */
+#define SW_TABLET_MODE		0x01  /* set = tablet mode */
+#define SW_HEADPHONE_INSERT	0x02  /* set = inserted */
+#define SW_RFKILL_ALL		0x03  /* rfkill master switch, type "any"
+					 set = radio enabled */
+#define SW_RADIO		SW_RFKILL_ALL	/* deprecated */
+#define SW_MICROPHONE_INSERT	0x04  /* set = inserted */
+#define SW_DOCK			0x05  /* set = plugged into dock */
+#define SW_MAX			0x0f
+#define SW_CNT			(SW_MAX+1)
+
+/*
+ * Misc events
+ */
+
+#define MSC_SERIAL		0x00
+#define MSC_PULSELED		0x01
+#define MSC_GESTURE		0x02
+#define MSC_RAW			0x03
+#define MSC_SCAN		0x04
+#define MSC_MAX			0x07
+#define MSC_CNT			(MSC_MAX+1)
+
+/*
+ * LEDs
+ */
+
+#define LED_NUML		0x00
+#define LED_CAPSL		0x01
+#define LED_SCROLLL		0x02
+#define LED_COMPOSE		0x03
+#define LED_KANA		0x04
+#define LED_SLEEP		0x05
+#define LED_SUSPEND		0x06
+#define LED_MUTE		0x07
+#define LED_MISC		0x08
+#define LED_MAIL		0x09
+#define LED_CHARGING		0x0a
+#define LED_MAX			0x0f
+#define LED_CNT			(LED_MAX+1)
+
+/*
+ * Autorepeat values
+ */
+
+#define REP_DELAY		0x00
+#define REP_PERIOD		0x01
+#define REP_MAX			0x01
+
+/*
+ * Sounds
+ */
+
+#define SND_CLICK		0x00
+#define SND_BELL		0x01
+#define SND_TONE		0x02
+#define SND_MAX			0x07
+#define SND_CNT			(SND_MAX+1)
+
+/*
+ * IDs.
+ */
+
+#define ID_BUS			0
+#define ID_VENDOR		1
+#define ID_PRODUCT		2
+#define ID_VERSION		3
+
+#define BUS_PCI			0x01
+#define BUS_ISAPNP		0x02
+#define BUS_USB			0x03
+#define BUS_HIL			0x04
+#define BUS_BLUETOOTH		0x05
+#define BUS_VIRTUAL		0x06
+
+#define BUS_ISA			0x10
+#define BUS_I8042		0x11
+#define BUS_XTKBD		0x12
+#define BUS_RS232		0x13
+#define BUS_GAMEPORT		0x14
+#define BUS_PARPORT		0x15
+#define BUS_AMIGA		0x16
+#define BUS_ADB			0x17
+#define BUS_I2C			0x18
+#define BUS_HOST		0x19
+#define BUS_GSC			0x1A
+#define BUS_ATARI		0x1B
+
+/* User input interface */
+
+#define UINPUT_IOCTL_BASE	'U'
+
+#define UI_DEV_CREATE		_IO(UINPUT_IOCTL_BASE, 1)
+#define UI_DEV_DESTROY		_IO(UINPUT_IOCTL_BASE, 2)
+
+#define UI_SET_EVBIT		_IOW(UINPUT_IOCTL_BASE, 100, int)
+#define UI_SET_KEYBIT		_IOW(UINPUT_IOCTL_BASE, 101, int)
+#define UI_SET_RELBIT		_IOW(UINPUT_IOCTL_BASE, 102, int)
+#define UI_SET_ABSBIT		_IOW(UINPUT_IOCTL_BASE, 103, int)
+#define UI_SET_MSCBIT		_IOW(UINPUT_IOCTL_BASE, 104, int)
+#define UI_SET_LEDBIT		_IOW(UINPUT_IOCTL_BASE, 105, int)
+#define UI_SET_SNDBIT		_IOW(UINPUT_IOCTL_BASE, 106, int)
+#define UI_SET_FFBIT		_IOW(UINPUT_IOCTL_BASE, 107, int)
+#define UI_SET_PHYS		_IOW(UINPUT_IOCTL_BASE, 108, char*)
+#define UI_SET_SWBIT		_IOW(UINPUT_IOCTL_BASE, 109, int)
+
+#ifndef NBITS
+#define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1)
+#endif
+
+#define UINPUT_MAX_NAME_SIZE	80
+
+struct uinput_id {
+	uint16_t bustype;
+	uint16_t vendor;
+	uint16_t product;
+	uint16_t version;
+};
+
+struct uinput_dev {
+	char name[UINPUT_MAX_NAME_SIZE];
+	struct uinput_id id;
+	int ff_effects_max;
+	int absmax[ABS_MAX + 1];
+	int absmin[ABS_MAX + 1];
+	int absfuzz[ABS_MAX + 1];
+	int absflat[ABS_MAX + 1];
+};
+
+struct uinput_event {
+	struct timeval time;
+	uint16_t type;
+	uint16_t code;
+	int32_t value;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UINPUT_H */
diff --git a/bluez/repo/src/uuid-helper.c b/bluez/repo/src/uuid-helper.c
new file mode 100644
index 0000000..d751817
--- /dev/null
+++ b/bluez/repo/src/uuid-helper.c
@@ -0,0 +1,247 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "uuid-helper.h"
+
+char *bt_modalias(uint16_t source, uint16_t vendor,
+					uint16_t product, uint16_t version)
+{
+	char *str;
+	int err;
+
+	switch (source) {
+	case 0x0001:
+		err = asprintf(&str, "%s:v%04Xp%04Xd%04X",
+					"bluetooth", vendor, product, version);
+		break;
+	case 0x0002:
+		err = asprintf(&str, "%s:v%04Xp%04Xd%04X",
+					"usb", vendor, product, version);
+		break;
+	default:
+		return NULL;
+	}
+
+	if (err < 0)
+		return NULL;
+
+	return str;
+}
+
+char *bt_uuid2string(uuid_t *uuid)
+{
+	char *str;
+	uuid_t uuid128;
+	unsigned int data0;
+	unsigned short data1;
+	unsigned short data2;
+	unsigned short data3;
+	unsigned int data4;
+	unsigned short data5;
+	int err;
+
+	if (!uuid)
+		return NULL;
+
+	switch (uuid->type) {
+	case SDP_UUID16:
+		sdp_uuid16_to_uuid128(&uuid128, uuid);
+		break;
+	case SDP_UUID32:
+		sdp_uuid32_to_uuid128(&uuid128, uuid);
+		break;
+	case SDP_UUID128:
+		memcpy(&uuid128, uuid, sizeof(uuid_t));
+		break;
+	default:
+		/* Type of UUID unknown */
+		return NULL;
+	}
+
+	memcpy(&data0, &uuid128.value.uuid128.data[0], 4);
+	memcpy(&data1, &uuid128.value.uuid128.data[4], 2);
+	memcpy(&data2, &uuid128.value.uuid128.data[6], 2);
+	memcpy(&data3, &uuid128.value.uuid128.data[8], 2);
+	memcpy(&data4, &uuid128.value.uuid128.data[10], 4);
+	memcpy(&data5, &uuid128.value.uuid128.data[14], 2);
+
+	err = asprintf(&str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+			ntohl(data0), ntohs(data1),
+			ntohs(data2), ntohs(data3),
+			ntohl(data4), ntohs(data5));
+	if (err < 0)
+		return NULL;
+
+	return str;
+}
+
+static struct {
+	const char	*name;
+	uint16_t	class;
+} bt_services[] = {
+	{ "pbap",	PBAP_SVCLASS_ID			},
+	{ "sap",	SAP_SVCLASS_ID			},
+	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID	},
+	{ "bpp",	BASIC_PRINTING_SVCLASS_ID	},
+	{ "bip",	IMAGING_SVCLASS_ID		},
+	{ "synch",	IRMC_SYNC_SVCLASS_ID		},
+	{ "dun",	DIALUP_NET_SVCLASS_ID		},
+	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID		},
+	{ "fax",	FAX_SVCLASS_ID			},
+	{ "spp",	SERIAL_PORT_SVCLASS_ID		},
+	{ "hsp",	HEADSET_SVCLASS_ID		},
+	{ "hsp-hs",	HEADSET_SVCLASS_ID		},
+	{ "hsp-ag",	HEADSET_AGW_SVCLASS_ID		},
+	{ "hfp",	HANDSFREE_SVCLASS_ID		},
+	{ "hfp-hf",	HANDSFREE_SVCLASS_ID		},
+	{ "hfp-ag",	HANDSFREE_AGW_SVCLASS_ID	},
+	{ "pbap-pce",	PBAP_PCE_SVCLASS_ID		},
+	{ "pbap-pse",	PBAP_PSE_SVCLASS_ID		},
+	{ "map-mse",	MAP_MSE_SVCLASS_ID		},
+	{ "map-mas",	MAP_MSE_SVCLASS_ID		},
+	{ "map-mce",	MAP_MCE_SVCLASS_ID		},
+	{ "map-mns",	MAP_MCE_SVCLASS_ID		},
+	{ "gnss",	GNSS_SERVER_SVCLASS_ID		},
+	{ }
+};
+
+static uint16_t name2class(const char *pattern)
+{
+	int i;
+
+	for (i = 0; bt_services[i].name; i++) {
+		if (strcasecmp(bt_services[i].name, pattern) == 0)
+			return bt_services[i].class;
+	}
+
+	return 0;
+}
+
+static inline bool is_uuid128(const char *string)
+{
+	return (strlen(string) == 36 &&
+			string[8] == '-' &&
+			string[13] == '-' &&
+			string[18] == '-' &&
+			string[23] == '-');
+}
+
+static int string2uuid16(uuid_t *uuid, const char *string)
+{
+	int length = strlen(string);
+	char *endptr = NULL;
+	uint16_t u16;
+
+	if (length != 4 && length != 6)
+		return -EINVAL;
+
+	u16 = strtol(string, &endptr, 16);
+	if (endptr && *endptr == '\0') {
+		sdp_uuid16_create(uuid, u16);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+char *bt_name2string(const char *pattern)
+{
+	uuid_t uuid;
+	uint16_t uuid16;
+	int i;
+
+	/* UUID 128 string format */
+	if (is_uuid128(pattern))
+		return strdup(pattern);
+
+	/* Friendly service name format */
+	uuid16 = name2class(pattern);
+	if (uuid16)
+		goto proceed;
+
+	/* HEX format */
+	uuid16 = strtol(pattern, NULL, 16);
+	for (i = 0; bt_services[i].class; i++) {
+		if (bt_services[i].class == uuid16)
+			goto proceed;
+	}
+
+	return NULL;
+
+proceed:
+	sdp_uuid16_create(&uuid, uuid16);
+
+	return bt_uuid2string(&uuid);
+}
+
+int bt_string2uuid(uuid_t *uuid, const char *string)
+{
+	uint32_t data0, data4;
+	uint16_t data1, data2, data3, data5;
+
+	if (is_uuid128(string) &&
+			sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+				&data0, &data1, &data2, &data3, &data4, &data5) == 6) {
+		uint8_t val[16];
+
+		data0 = htonl(data0);
+		data1 = htons(data1);
+		data2 = htons(data2);
+		data3 = htons(data3);
+		data4 = htonl(data4);
+		data5 = htons(data5);
+
+		memcpy(&val[0], &data0, 4);
+		memcpy(&val[4], &data1, 2);
+		memcpy(&val[6], &data2, 2);
+		memcpy(&val[8], &data3, 2);
+		memcpy(&val[10], &data4, 4);
+		memcpy(&val[14], &data5, 2);
+
+		sdp_uuid128_create(uuid, val);
+
+		return 0;
+	} else {
+		uint16_t class = name2class(string);
+		if (class) {
+			sdp_uuid16_create(uuid, class);
+			return 0;
+		}
+
+		return string2uuid16(uuid, string);
+	}
+}
diff --git a/bluez/repo/src/uuid-helper.h b/bluez/repo/src/uuid-helper.h
new file mode 100644
index 0000000..c0d7f9e
--- /dev/null
+++ b/bluez/repo/src/uuid-helper.h
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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
+ *
+ */
+
+char *bt_modalias(uint16_t source, uint16_t vendor,
+					uint16_t product, uint16_t version);
+char *bt_uuid2string(uuid_t *uuid);
+char *bt_name2string(const char *string);
+int bt_string2uuid(uuid_t *uuid, const char *string);
diff --git a/bluez/repo/test/._bluezutils.py b/bluez/repo/test/._bluezutils.py
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._bluezutils.py
Binary files differ
diff --git a/bluez/repo/test/._dbusdef.py b/bluez/repo/test/._dbusdef.py
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._dbusdef.py
Binary files differ
diff --git a/bluez/repo/test/._example-advertisement b/bluez/repo/test/._example-advertisement
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._example-advertisement
Binary files differ
diff --git a/bluez/repo/test/._example-gatt-client b/bluez/repo/test/._example-gatt-client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._example-gatt-client
Binary files differ
diff --git a/bluez/repo/test/._example-gatt-server b/bluez/repo/test/._example-gatt-server
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._example-gatt-server
Binary files differ
diff --git a/bluez/repo/test/._exchange-business-cards b/bluez/repo/test/._exchange-business-cards
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._exchange-business-cards
Binary files differ
diff --git a/bluez/repo/test/._ftp-client b/bluez/repo/test/._ftp-client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._ftp-client
Binary files differ
diff --git a/bluez/repo/test/._get-managed-objects b/bluez/repo/test/._get-managed-objects
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._get-managed-objects
Binary files differ
diff --git a/bluez/repo/test/._get-obex-capabilities b/bluez/repo/test/._get-obex-capabilities
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._get-obex-capabilities
Binary files differ
diff --git a/bluez/repo/test/._list-devices b/bluez/repo/test/._list-devices
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._list-devices
Binary files differ
diff --git a/bluez/repo/test/._list-folders b/bluez/repo/test/._list-folders
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._list-folders
Binary files differ
diff --git a/bluez/repo/test/._map-client b/bluez/repo/test/._map-client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._map-client
Binary files differ
diff --git a/bluez/repo/test/._monitor-bluetooth b/bluez/repo/test/._monitor-bluetooth
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._monitor-bluetooth
Binary files differ
diff --git a/bluez/repo/test/._opp-client b/bluez/repo/test/._opp-client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._opp-client
Binary files differ
diff --git a/bluez/repo/test/._pbap-client b/bluez/repo/test/._pbap-client
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._pbap-client
Binary files differ
diff --git a/bluez/repo/test/._sap_client.py b/bluez/repo/test/._sap_client.py
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._sap_client.py
Binary files differ
diff --git a/bluez/repo/test/._service-did.xml b/bluez/repo/test/._service-did.xml
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._service-did.xml
Binary files differ
diff --git a/bluez/repo/test/._service-ftp.xml b/bluez/repo/test/._service-ftp.xml
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._service-ftp.xml
Binary files differ
diff --git a/bluez/repo/test/._service-opp.xml b/bluez/repo/test/._service-opp.xml
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._service-opp.xml
Binary files differ
diff --git a/bluez/repo/test/._service-record.dtd b/bluez/repo/test/._service-record.dtd
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._service-record.dtd
Binary files differ
diff --git a/bluez/repo/test/._service-spp.xml b/bluez/repo/test/._service-spp.xml
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._service-spp.xml
Binary files differ
diff --git a/bluez/repo/test/._simple-agent b/bluez/repo/test/._simple-agent
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._simple-agent
Binary files differ
diff --git a/bluez/repo/test/._simple-endpoint b/bluez/repo/test/._simple-endpoint
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._simple-endpoint
Binary files differ
diff --git a/bluez/repo/test/._simple-obex-agent b/bluez/repo/test/._simple-obex-agent
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._simple-obex-agent
Binary files differ
diff --git a/bluez/repo/test/._simple-player b/bluez/repo/test/._simple-player
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._simple-player
Binary files differ
diff --git a/bluez/repo/test/._test-adapter b/bluez/repo/test/._test-adapter
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-adapter
Binary files differ
diff --git a/bluez/repo/test/._test-alert b/bluez/repo/test/._test-alert
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-alert
Binary files differ
diff --git a/bluez/repo/test/._test-cyclingspeed b/bluez/repo/test/._test-cyclingspeed
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-cyclingspeed
Binary files differ
diff --git a/bluez/repo/test/._test-device b/bluez/repo/test/._test-device
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-device
Binary files differ
diff --git a/bluez/repo/test/._test-discovery b/bluez/repo/test/._test-discovery
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-discovery
Binary files differ
diff --git a/bluez/repo/test/._test-gatt-profile b/bluez/repo/test/._test-gatt-profile
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-gatt-profile
Binary files differ
diff --git a/bluez/repo/test/._test-health b/bluez/repo/test/._test-health
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-health
Binary files differ
diff --git a/bluez/repo/test/._test-health-sink b/bluez/repo/test/._test-health-sink
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-health-sink
Binary files differ
diff --git a/bluez/repo/test/._test-heartrate b/bluez/repo/test/._test-heartrate
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-heartrate
Binary files differ
diff --git a/bluez/repo/test/._test-hfp b/bluez/repo/test/._test-hfp
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-hfp
Binary files differ
diff --git a/bluez/repo/test/._test-manager b/bluez/repo/test/._test-manager
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-manager
Binary files differ
diff --git a/bluez/repo/test/._test-nap b/bluez/repo/test/._test-nap
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-nap
Binary files differ
diff --git a/bluez/repo/test/._test-network b/bluez/repo/test/._test-network
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-network
Binary files differ
diff --git a/bluez/repo/test/._test-profile b/bluez/repo/test/._test-profile
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-profile
Binary files differ
diff --git a/bluez/repo/test/._test-proximity b/bluez/repo/test/._test-proximity
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-proximity
Binary files differ
diff --git a/bluez/repo/test/._test-sap-server b/bluez/repo/test/._test-sap-server
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-sap-server
Binary files differ
diff --git a/bluez/repo/test/._test-thermometer b/bluez/repo/test/._test-thermometer
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/test/._test-thermometer
Binary files differ
diff --git a/bluez/repo/test/bluezutils.py b/bluez/repo/test/bluezutils.py
new file mode 100644
index 0000000..de08cbd
--- /dev/null
+++ b/bluez/repo/test/bluezutils.py
@@ -0,0 +1,47 @@
+import dbus
+
+SERVICE_NAME = "org.bluez"
+ADAPTER_INTERFACE = SERVICE_NAME + ".Adapter1"
+DEVICE_INTERFACE = SERVICE_NAME + ".Device1"
+
+def get_managed_objects():
+	bus = dbus.SystemBus()
+	manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+				"org.freedesktop.DBus.ObjectManager")
+	return manager.GetManagedObjects()
+
+def find_adapter(pattern=None):
+	return find_adapter_in_objects(get_managed_objects(), pattern)
+
+def find_adapter_in_objects(objects, pattern=None):
+	bus = dbus.SystemBus()
+	for path, ifaces in objects.iteritems():
+		adapter = ifaces.get(ADAPTER_INTERFACE)
+		if adapter is None:
+			continue
+		if not pattern or pattern == adapter["Address"] or \
+							path.endswith(pattern):
+			obj = bus.get_object(SERVICE_NAME, path)
+			return dbus.Interface(obj, ADAPTER_INTERFACE)
+	raise Exception("Bluetooth adapter not found")
+
+def find_device(device_address, adapter_pattern=None):
+	return find_device_in_objects(get_managed_objects(), device_address,
+								adapter_pattern)
+
+def find_device_in_objects(objects, device_address, adapter_pattern=None):
+	bus = dbus.SystemBus()
+	path_prefix = ""
+	if adapter_pattern:
+		adapter = find_adapter_in_objects(objects, adapter_pattern)
+		path_prefix = adapter.object_path
+	for path, ifaces in objects.iteritems():
+		device = ifaces.get(DEVICE_INTERFACE)
+		if device is None:
+			continue
+		if (device["Address"] == device_address and
+						path.startswith(path_prefix)):
+			obj = bus.get_object(SERVICE_NAME, path)
+			return dbus.Interface(obj, DEVICE_INTERFACE)
+
+	raise Exception("Bluetooth device not found")
diff --git a/bluez/repo/test/dbusdef.py b/bluez/repo/test/dbusdef.py
new file mode 100644
index 0000000..f1cd35a
--- /dev/null
+++ b/bluez/repo/test/dbusdef.py
@@ -0,0 +1,15 @@
+import dbus
+import bluezutils
+
+bus = dbus.SystemBus()
+
+
+dummy = dbus.Interface(bus.get_object('org.bluez', '/'), 'org.freedesktop.DBus.Introspectable')
+
+#print dummy.Introspect()
+
+
+try:
+	adapter = bluezutils.find_adapter()
+except:
+	pass
diff --git a/bluez/repo/test/example-advertisement b/bluez/repo/test/example-advertisement
new file mode 100755
index 0000000..ce6e40f
--- /dev/null
+++ b/bluez/repo/test/example-advertisement
@@ -0,0 +1,178 @@
+#!/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
+        self.include_tx_power = 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')
+        if self.include_tx_power is not None:
+            properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
+        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, 'peripheral')
+        self.add_service_uuid('180D')
+        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.include_tx_power = True
+
+
+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
+
+    adapter_props = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+                                   "org.freedesktop.DBus.Properties");
+
+    adapter_props.Set("org.bluez.Adapter1", "Powered", dbus.Boolean(1))
+
+    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()
diff --git a/bluez/repo/test/example-gatt-client b/bluez/repo/test/example-gatt-client
new file mode 100755
index 0000000..724a45d
--- /dev/null
+++ b/bluez/repo/test/example-gatt-client
@@ -0,0 +1,218 @@
+#!/usr/bin/python
+
+import argparse
+import dbus
+import gobject
+import sys
+
+from dbus.mainloop.glib import DBusGMainLoop
+
+bus = None
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+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'
+
+HR_SVC_UUID =        '0000180d-0000-1000-8000-00805f9b34fb'
+HR_MSRMT_UUID =      '00002a37-0000-1000-8000-00805f9b34fb'
+BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
+HR_CTRL_PT_UUID =    '00002a39-0000-1000-8000-00805f9b34fb'
+
+# The objects that we interact with.
+hr_service = None
+hr_msrmt_chrc = None
+body_snsr_loc_chrc = None
+hr_ctrl_pt_chrc = None
+
+
+def generic_error_cb(error):
+    print('D-Bus call failed: ' + str(error))
+    mainloop.quit()
+
+
+def body_sensor_val_to_str(val):
+    if val == 0:
+        return 'Other'
+    if val == 1:
+        return 'Chest'
+    if val == 2:
+        return 'Wrist'
+    if val == 3:
+        return 'Finger'
+    if val == 4:
+        return 'Hand'
+    if val == 5:
+        return 'Ear Lobe'
+    if val == 6:
+        return 'Foot'
+
+    return 'Reserved value'
+
+
+def sensor_contact_val_to_str(val):
+    if val == 0 or val == 1:
+        return 'not supported'
+    if val == 2:
+        return 'no contact detected'
+    if val == 3:
+        return 'contact detected'
+
+    return 'invalid value'
+
+
+def body_sensor_val_cb(value):
+    if len(value) != 1:
+        print('Invalid body sensor location value: ' + repr(value))
+        return
+
+    print('Body sensor location value: ' + body_sensor_val_to_str(value[0]))
+
+
+def hr_msrmt_start_notify_cb():
+    print('HR Measurement notifications enabled')
+
+
+def hr_msrmt_changed_cb(iface, changed_props, invalidated_props):
+    if iface != GATT_CHRC_IFACE:
+        return
+
+    if not len(changed_props):
+        return
+
+    value = changed_props.get('Value', None)
+    if not value:
+        return
+
+    print('New HR Measurement')
+
+    flags = value[0]
+    value_format = flags & 0x01
+    sc_status = (flags >> 1) & 0x03
+    ee_status = flags & 0x08
+
+    if value_format == 0x00:
+        hr_msrmt = value[1]
+        next_ind = 2
+    else:
+        hr_msrmt = value[1] | (value[2] << 8)
+        next_ind = 3
+
+    print('\tHR: ' + str(int(hr_msrmt)))
+    print('\tSensor Contact status: ' +
+          sensor_contact_val_to_str(sc_status))
+
+    if ee_status:
+        print('\tEnergy Expended: ' + str(int(value[next_ind])))
+
+
+def start_client():
+    # Read the Body Sensor Location value and print it asynchronously.
+    body_snsr_loc_chrc[0].ReadValue(reply_handler=body_sensor_val_cb,
+                                    error_handler=generic_error_cb,
+                                    dbus_interface=GATT_CHRC_IFACE)
+
+    # Listen to PropertiesChanged signals from the Heart Measurement
+    # Characteristic.
+    hr_msrmt_prop_iface = dbus.Interface(hr_msrmt_chrc[0], DBUS_PROP_IFACE)
+    hr_msrmt_prop_iface.connect_to_signal("PropertiesChanged",
+                                          hr_msrmt_changed_cb)
+
+    # Subscribe to Heart Rate Measurement notifications.
+    hr_msrmt_chrc[0].StartNotify(reply_handler=hr_msrmt_start_notify_cb,
+                                 error_handler=generic_error_cb,
+                                 dbus_interface=GATT_CHRC_IFACE)
+
+
+def process_chrc(chrc_path):
+    chrc = bus.get_object(BLUEZ_SERVICE_NAME, chrc_path)
+    chrc_props = chrc.GetAll(GATT_CHRC_IFACE,
+                             dbus_interface=DBUS_PROP_IFACE)
+
+    uuid = chrc_props['UUID']
+
+    if uuid == HR_MSRMT_UUID:
+        global hr_msrmt_chrc
+        hr_msrmt_chrc = (chrc, chrc_props)
+    elif uuid == BODY_SNSR_LOC_UUID:
+        global body_snsr_loc_chrc
+        body_snsr_loc_chrc = (chrc, chrc_props)
+    elif uuid == HR_CTRL_PT_UUID:
+        global hr_ctrl_pt_chrc
+        hr_ctrl_pt_chrc = (chrc, chrc_props)
+    else:
+        print('Unrecognized characteristic: ' + uuid)
+
+    return True
+
+
+def process_hr_service(service_path):
+    service = bus.get_object(BLUEZ_SERVICE_NAME, service_path)
+    service_props = service.GetAll(GATT_SERVICE_IFACE,
+                                   dbus_interface=DBUS_PROP_IFACE)
+
+    uuid = service_props['UUID']
+
+    if uuid != HR_SVC_UUID:
+        print('Service is not a Heart Rate Service: ' + uuid)
+        return False
+
+    # Process the characteristics.
+    chrc_paths = service_props['Characteristics']
+    for chrc_path in chrc_paths:
+        process_chrc(chrc_path)
+
+    global hr_service
+    hr_service = (service, service_props, service_path)
+
+    return True
+
+
+def interfaces_removed_cb(object_path, interfaces):
+    if not hr_service:
+        return
+
+    if object_path == hr_service[2]:
+        print('Service was removed')
+        mainloop.quit()
+
+
+def main():
+    # Prase the service path from the arguments.
+    parser = argparse.ArgumentParser(
+            description='D-Bus Heart Rate Service client example')
+    parser.add_argument('service_path', metavar='<service-path>',
+                        type=dbus.ObjectPath, nargs=1,
+                        help='GATT service object path')
+    args = parser.parse_args()
+    service_path = args.service_path[0]
+
+    # Set up the main loop.
+    DBusGMainLoop(set_as_default=True)
+    global bus
+    bus = dbus.SystemBus()
+    global mainloop
+    mainloop = gobject.MainLoop()
+
+    om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE)
+    om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
+
+    try:
+        if not process_hr_service(service_path):
+            sys.exit(1)
+    except dbus.DBusException as e:
+        print e.message
+        sys.exit(1)
+
+    print 'Heart Rate Service ready'
+
+    start_client()
+
+    mainloop.run()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/bluez/repo/test/example-gatt-server b/bluez/repo/test/example-gatt-server
new file mode 100755
index 0000000..47219b8
--- /dev/null
+++ b/bluez/repo/test/example-gatt-server
@@ -0,0 +1,581 @@
+#!/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, flags, characteristic):
+        self.path = characteristic.path + '/desc' + str(index)
+        self.bus = bus
+        self.uuid = uuid
+        self.flags = flags
+        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,
+                        'Flags': self.flags,
+                }
+        }
+
+    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))
+        self.add_characteristic(TestEncryptCharacteristic(bus, 1, 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,
+                ['read', 'write'],
+                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,
+                ['read', 'write'],
+                characteristic)
+
+    def ReadValue(self):
+        return self.value
+
+    def WriteValue(self, value):
+        if not self.writable:
+            raise NotPermittedException()
+        self.value = value
+
+class TestEncryptCharacteristic(Characteristic):
+    """
+    Dummy test characteristic requiring encryption.
+
+    """
+    TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef3'
+
+    def __init__(self, bus, index, service):
+        Characteristic.__init__(
+                self, bus, index,
+                self.TEST_CHRC_UUID,
+                ['encrypt-read', 'encrypt-write'],
+                service)
+        self.value = []
+        self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+        self.add_descriptor(
+                CharacteristicUserDescriptionDescriptor(bus, 3, 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 TestEncryptDescriptor(Descriptor):
+    """
+    Dummy test descriptor requiring encryption. Returns a static value.
+
+    """
+    TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef4'
+
+    def __init__(self, bus, index, characteristic):
+        Descriptor.__init__(
+                self, bus, index,
+                self.TEST_DESC_UUID,
+                ['encrypt-read', 'encrypt-write'],
+                characteristic)
+
+    def ReadValue(self):
+        return [
+                dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+        ]
+
+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()
diff --git a/bluez/repo/test/exchange-business-cards b/bluez/repo/test/exchange-business-cards
new file mode 100755
index 0000000..6805cf7
--- /dev/null
+++ b/bluez/repo/test/exchange-business-cards
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SessionBus()
+client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"),
+					"org.bluez.obex.Client")
+
+if (len(sys.argv) < 4):
+	print "Usage: %s <device> <clientfile> <file>" % (sys.argv[0])
+	sys.exit(1)
+
+print "Creating Session"
+path = client.CreateSession(sys.argv[1], { "Target": "OPP" })
+opp = dbus.Interface(bus.get_object("org.bluez.obex", path),
+					"org.bluez.obex.ObjectPush")
+
+opp.ExchangeBusinessCards(sys.argv[2], sys.argv[3])
diff --git a/bluez/repo/test/ftp-client b/bluez/repo/test/ftp-client
new file mode 100755
index 0000000..4540602
--- /dev/null
+++ b/bluez/repo/test/ftp-client
@@ -0,0 +1,180 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import os.path
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE='org.bluez.obex.Client1'
+SESSION_INTERFACE='org.bluez.obex.Session1'
+FILE_TRASNFER_INTERFACE='org.bluez.obex.FileTransfer1'
+TRANSFER_INTERFACE='org.bluez.obex.Transfer1'
+
+def parse_options():
+	parser.add_option("-d", "--device", dest="device",
+			help="Device to connect", metavar="DEVICE")
+	parser.add_option("-c", "--chdir", dest="new_dir",
+			help="Change current directory to DIR", metavar="DIR")
+	parser.add_option("-l", "--list", action="store_true", dest="list_dir",
+			help="List the current directory")
+	parser.add_option("-g", "--get", dest="get_file",
+			help="Get FILE", metavar="FILE")
+	parser.add_option("-p", "--put", dest="put_file",
+			help="Put FILE", metavar="FILE")
+	parser.add_option("-y", "--copy", dest="copy_file",
+			help="Copy FILE", metavar="FILE")
+	parser.add_option("-m", "--move", dest="move_file",
+			help="Move FILE", metavar="FILE")
+	parser.add_option("-n", "--destname", dest="dest_file",
+			help="Destination FILE", metavar="FILE")
+	parser.add_option("-r", "--remove", dest="remove_file",
+			help="Remove FILE", metavar="FILE")
+	parser.add_option("-v", "--verbose", action="store_true",
+			dest="verbose")
+
+	return parser.parse_args()
+
+class FtpClient:
+	def __init__(self, session_path, verbose=False):
+		self.transferred = 0
+		self.transfer_path = None
+		self.transfer_size = 0
+		self.verbose = verbose
+		bus = dbus.SessionBus()
+		obj = bus.get_object(BUS_NAME, session_path)
+		self.session = dbus.Interface(obj, SESSION_INTERFACE)
+		self.ftp = dbus.Interface(obj, FILE_TRASNFER_INTERFACE)
+		bus.add_signal_receiver(self.properties_changed,
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged",
+			path_keyword="path")
+
+	def create_transfer_reply(self, path, properties):
+		self.transfer_path = path
+		self.transfer_size = properties["Size"]
+		if self.verbose:
+			print("Transfer created: %s" % path)
+
+	def generic_reply(self):
+		if self.verbose:
+			print("Operation succeeded")
+
+	def error(self, err):
+		print(err)
+		mainloop.quit()
+
+	def properties_changed(self, interface, properties, invalidated, path):
+		if path != self.transfer_path:
+			return
+
+		if "Status" in properties and \
+				(properties['Status'] == 'complete' or \
+				properties['Status'] == 'error'):
+			if self.verbose:
+				print("Transfer %s" % properties['Status'])
+			mainloop.quit()
+			return
+
+		if "Transferred" not in properties:
+			return
+
+		value = properties["Transferred"]
+		speed = (value - self.transferred) / 1000
+		print("Transfer progress %d/%d at %d kBps" % (value,
+							self.transfer_size,
+							speed))
+		self.transferred = value
+
+	def change_folder(self, new_dir):
+		for node in new_dir.split("/"):
+			self.ftp.ChangeFolder(node)
+
+	def list_folder(self):
+		for i in self.ftp.ListFolder():
+			if i["Type"] == "folder":
+				print("%s/" % (i["Name"]))
+			else:
+				print("%s" % (i["Name"]))
+
+	def put_file(self, filename):
+		self.ftp.PutFile(os.path.abspath(filename),
+				os.path.basename(filename),
+				reply_handler=self.create_transfer_reply,
+				error_handler=self.error)
+
+	def get_file(self, filename):
+		self.ftp.GetFile(os.path.abspath(filename),
+				os.path.basename(filename),
+				reply_handler=self.create_transfer_reply,
+				error_handler=self.error)
+
+	def remove_file(self, filename):
+		self.ftp.Delete(filename,
+				reply_handler=self.generic_reply,
+				error_handler=self.error)
+
+	def move_file(self, filename, destname):
+		self.ftp.MoveFile(filename, destname,
+				reply_handler=self.generic_reply,
+				error_handler=self.error)
+
+	def copy_file(self, filename, destname):
+		self.ftp.CopyFile(filename, destname,
+				reply_handler=self.generic_reply,
+				error_handler=self.error)
+
+if  __name__ == '__main__':
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	parser = OptionParser()
+
+	(options, args) = parse_options()
+
+	if not options.device:
+		parser.print_help()
+		sys.exit(0)
+
+	bus = dbus.SessionBus()
+	mainloop = GObject.MainLoop()
+
+	client = dbus.Interface(bus.get_object(BUS_NAME, PATH,),
+							CLIENT_INTERFACE)
+
+	print("Creating Session")
+	path = client.CreateSession(options.device, { "Target": "ftp" })
+
+	ftp_client = FtpClient(path, options.verbose)
+
+	if options.new_dir:
+		ftp_client.change_folder(options.new_dir)
+
+	if options.list_dir:
+		ftp_client.list_folder()
+
+	if options.get_file:
+		ftp_client.get_file(options.get_file)
+
+	if options.put_file:
+		ftp_client.put_file(options.put_file)
+
+	if options.move_file:
+		ftp_client.move_file(options.move_file, options.dest_file)
+
+	if options.copy_file:
+		ftp_client.copy_file(options.copy_file, options.dest_file)
+
+	if options.remove_file:
+		ftp_client.remove_file(options.remove_file)
+
+	mainloop.run()
diff --git a/bluez/repo/test/get-managed-objects b/bluez/repo/test/get-managed-objects
new file mode 100755
index 0000000..3156f65
--- /dev/null
+++ b/bluez/repo/test/get-managed-objects
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+					"org.freedesktop.DBus.ObjectManager")
+
+objects = manager.GetManagedObjects()
+
+for path in objects.keys():
+	print("[ %s ]" % (path))
+
+	interfaces = objects[path]
+
+	for interface in interfaces.keys():
+		if interface in ["org.freedesktop.DBus.Introspectable",
+					"org.freedesktop.DBus.Properties"]:
+			continue
+
+		print("    %s" % (interface))
+
+		properties = interfaces[interface]
+
+		for key in properties.keys():
+			print("      %s = %s" % (key, properties[key]))
diff --git a/bluez/repo/test/get-obex-capabilities b/bluez/repo/test/get-obex-capabilities
new file mode 100755
index 0000000..e8afbad
--- /dev/null
+++ b/bluez/repo/test/get-obex-capabilities
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SessionBus()
+client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"),
+					"org.bluez.obex.Client")
+
+if (len(sys.argv) < 3):
+	print "Usage: %s <device> <target>" % (sys.argv[0])
+	sys.exit(1)
+
+print "Creating Session"
+session_path = client.CreateSession(sys.argv[1], { "Target": sys.argv[2] })
+session = dbus.Interface(bus.get_object("org.bluez.obex", session_path),
+					"org.bluez.obex.Session")
+
+print session.GetCapabilities()
diff --git a/bluez/repo/test/list-devices b/bluez/repo/test/list-devices
new file mode 100755
index 0000000..0aac217
--- /dev/null
+++ b/bluez/repo/test/list-devices
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+					"org.freedesktop.DBus.ObjectManager")
+
+def extract_objects(object_list):
+	list = ""
+	for object in object_list:
+		val = str(object)
+		list = list + val[val.rfind("/") + 1:] + " "
+	return list
+
+def extract_uuids(uuid_list):
+	list = ""
+	for uuid in uuid_list:
+		if (uuid.endswith("-0000-1000-8000-00805f9b34fb")):
+			if (uuid.startswith("0000")):
+				val = "0x" + uuid[4:8]
+			else:
+				val = "0x" + uuid[0:8]
+		else:
+			val = str(uuid)
+		list = list + val + " "
+	return list
+
+objects = manager.GetManagedObjects()
+
+all_devices = (str(path) for path, interfaces in objects.iteritems() if
+					"org.bluez.Device1" in interfaces.keys())
+
+for path, interfaces in objects.iteritems():
+	if "org.bluez.Adapter1" not in interfaces.keys():
+		continue
+
+	print("[ " + path + " ]")
+
+	properties = interfaces["org.bluez.Adapter1"]
+	for key in properties.keys():
+		value = properties[key]
+		if (key == "UUIDs"):
+			list = extract_uuids(value)
+			print("    %s = %s" % (key, list))
+		else:
+			print("    %s = %s" % (key, value))
+
+	device_list = [d for d in all_devices if d.startswith(path + "/")]
+
+	for dev_path in device_list:
+		print("    [ " + dev_path + " ]")
+
+		dev = objects[dev_path]
+		properties = dev["org.bluez.Device1"]
+
+		for key in properties.keys():
+			value = properties[key]
+			if (key == "UUIDs"):
+				list = extract_uuids(value)
+				print("        %s = %s" % (key, list))
+			elif (key == "Class"):
+				print("        %s = 0x%06x" % (key, value))
+			elif (key == "Vendor"):
+				print("        %s = 0x%04x" % (key, value))
+			elif (key == "Product"):
+				print("        %s = 0x%04x" % (key, value))
+			elif (key == "Version"):
+				print("        %s = 0x%04x" % (key, value))
+			else:
+				print("        %s = %s" % (key, value))
+
+	print("")
diff --git a/bluez/repo/test/list-folders b/bluez/repo/test/list-folders
new file mode 100755
index 0000000..7321a15
--- /dev/null
+++ b/bluez/repo/test/list-folders
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+
+def list_folder(folder):
+	bus = dbus.SessionBus()
+	client = dbus.Interface(bus.get_object("org.bluez.obex",
+						"/org/bluez/obex"),
+						"org.bluez.obex.Client")
+
+	path = client.CreateSession(sys.argv[1], { "Target": "ftp" })
+
+	ftp = dbus.Interface(bus.get_object("org.bluez.obex", path),
+				"org.bluez.obex.FileTransfer")
+
+	if folder:
+		for node in folder.split("/"):
+			ftp.ChangeFolder(node)
+
+	for i in ftp.ListFolder():
+		if i["Type"] == "folder":
+			print "%s/" % (i["Name"])
+		else:
+			print "%s" % (i["Name"])
+
+
+if __name__ == '__main__':
+
+	if len(sys.argv) < 2:
+		print "Usage: %s <device> [folder]" % (sys.argv[0])
+		sys.exit(1)
+
+	folder = None
+	if len(sys.argv) == 3:
+		folder = sys.argv[2]
+
+	list_folder(folder)
diff --git a/bluez/repo/test/map-client b/bluez/repo/test/map-client
new file mode 100755
index 0000000..b9695da
--- /dev/null
+++ b/bluez/repo/test/map-client
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import os
+from pprint import pformat
+import sys
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE = 'org.bluez.obex.Client1'
+SESSION_INTERFACE = 'org.bluez.obex.Session1'
+MESSAGE_ACCESS_INTERFACE = 'org.bluez.obex.MessageAccess1'
+MESSAGE_INTERFACE = 'org.bluez.obex.Message1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+def unwrap(x):
+    """Hack to unwrap D-Bus values, so that they're easier to read when
+    printed. Taken from d-feet """
+
+    if isinstance(x, list):
+        return map(unwrap, x)
+
+    if isinstance(x, tuple):
+        return tuple(map(unwrap, x))
+
+    if isinstance(x, dict):
+        return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()])
+
+    for t in [unicode, str, long, int, float, bool]:
+        if isinstance(x, t):
+            return t(x)
+
+    return x
+
+def parse_options():
+	parser.add_option("-d", "--device", dest="device",
+			help="Device to connect", metavar="DEVICE")
+	parser.add_option("-c", "--chdir", dest="new_dir",
+			help="Change current directory to DIR", metavar="DIR")
+	parser.add_option("-l", "--lsdir", action="store_true", dest="ls_dir",
+			help="List folders in current directory")
+	parser.add_option("-v", "--verbose", action="store_true", dest="verbose")
+	parser.add_option("-L", "--lsmsg", action="store", dest="ls_msg",
+			help="List messages in supplied CWD subdir")
+	parser.add_option("-g", "--get", action="store", dest="get_msg",
+			help="Get message contents")
+	parser.add_option("-p", "--push", action="store", dest="push_msg",
+			help="Push message")
+	parser.add_option("--get-properties", action="store", dest="get_msg_properties",
+			help="Get message properties")
+	parser.add_option("--mark-read", action="store", dest="mark_msg_read",
+			help="Marks the messages as read")
+	parser.add_option("--mark-unread", action="store", dest="mark_msg_unread",
+			help="Marks the messages as unread")
+	parser.add_option("--mark-deleted", action="store", dest="mark_msg_deleted",
+			help="Deletes the message from the folder")
+	parser.add_option("--mark-undeleted", action="store", dest="mark_msg_undeleted",
+			help="Undeletes the message")
+	parser.add_option("-u", "--update-inbox", action="store_true", dest="update_inbox",
+			help="Checks for new mails")
+
+	return parser.parse_args()
+
+def set_folder(session, new_dir):
+	session.SetFolder(new_dir)
+
+class MapClient:
+	def __init__(self, session_path, verbose=False):
+		self.progress = 0
+		self.transfer_path = None
+		self.props = dict()
+		self.verbose = verbose
+		self.path = session_path
+		bus = dbus.SessionBus()
+		obj = bus.get_object(BUS_NAME, session_path)
+		self.session = dbus.Interface(obj, SESSION_INTERFACE)
+		self.map = dbus.Interface(obj, MESSAGE_ACCESS_INTERFACE)
+		bus.add_signal_receiver(self.properties_changed,
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged",
+			path_keyword="path")
+
+	def create_transfer_reply(self, path, properties):
+		self.transfer_path = path
+		self.props[path] = properties
+		if self.verbose:
+			print("Transfer created: %s (file %s)" % (path,
+							properties["Filename"]))
+
+	def generic_reply(self):
+		if self.verbose:
+			print("Operation succeeded")
+
+	def error(self, err):
+		print(err)
+		mainloop.quit()
+
+	def transfer_complete(self, path):
+		if self.verbose:
+			print("Transfer finished")
+		properties = self.props.get(path)
+		if properties == None:
+			return
+		f = open(properties["Filename"], "r")
+		os.remove(properties["Filename"])
+		print(f.readlines())
+
+	def transfer_error(self, path):
+		print("Transfer %s error" % path)
+		mainloop.quit()
+
+	def properties_changed(self, interface, properties, invalidated, path):
+		req = self.props.get(path)
+		if req == None:
+			return
+
+		if properties['Status'] == 'complete':
+			self.transfer_complete(path)
+			return
+
+		if properties['Status'] == 'error':
+			self.transfer_error(path)
+			return
+
+	def set_folder(self, new_dir):
+		self.map.SetFolder(new_dir)
+
+	def list_folders(self):
+		for i in self.map.ListFolders(dict()):
+			print("%s/" % (i["Name"]))
+
+	def list_messages(self, folder):
+		ret = self.map.ListMessages(folder, dict())
+		print(pformat(unwrap(ret)))
+
+	def get_message(self, handle):
+		self.map.ListMessages("", dict())
+		path = self.path + "/message" + handle
+		obj = bus.get_object(BUS_NAME, path)
+		msg = dbus.Interface(obj, MESSAGE_INTERFACE)
+		msg.Get("", True, reply_handler=self.create_transfer_reply,
+						error_handler=self.error)
+
+	def push_message(self, filename):
+		self.map.PushMessage(filename, "telecom/msg/outbox", dict(),
+				reply_handler=self.create_transfer_reply,
+				error_handler=self.error)
+
+	def get_message_properties(self, handle):
+		self.map.ListMessages("", dict())
+		path = self.path + "/message" + handle
+		obj = bus.get_object(BUS_NAME, path)
+		msg = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
+		ret = msg.GetAll(MESSAGE_INTERFACE)
+		print(pformat(unwrap(ret)))
+
+	def set_message_property(self, handle, prop, flag):
+		self.map.ListMessages("", dict())
+		path = self.path + "/message" + handle
+		obj = bus.get_object(BUS_NAME, path)
+		msg = dbus.Interface(obj, MESSAGE_INTERFACE)
+		msg.SetProperty (prop, flag);
+
+	def update_inbox(self):
+		self.map.UpdateInbox()
+
+
+if  __name__ == '__main__':
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	parser = OptionParser()
+
+	(options, args) = parse_options()
+
+	if not options.device:
+		parser.print_help()
+		exit(0)
+
+	bus = dbus.SessionBus()
+	mainloop = GObject.MainLoop()
+
+	client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+							CLIENT_INTERFACE)
+
+	print("Creating Session")
+	path = client.CreateSession(options.device, { "Target": "map" })
+
+	map_client = MapClient(path, options.verbose)
+
+	if options.new_dir:
+		map_client.set_folder(options.new_dir)
+
+	if options.ls_dir:
+		map_client.list_folders()
+
+	if options.ls_msg is not None:
+		map_client.list_messages(options.ls_msg)
+
+	if options.get_msg is not None:
+		map_client.get_message(options.get_msg)
+
+	if options.push_msg is not None:
+		map_client.push_message(options.push_msg)
+
+	if options.get_msg_properties is not None:
+		map_client.get_message_properties(options.get_msg_properties)
+
+	if options.mark_msg_read is not None:
+		map_client.set_message_property(options.mark_msg_read, "Read", True)
+
+	if options.mark_msg_unread is not None:
+		map_client.set_message_property(options.mark_msg_unread, "Read", False)
+
+	if options.mark_msg_deleted is not None:
+		map_client.set_message_property(options.mark_msg_deleted, "Deleted", True)
+
+	if options.mark_msg_undeleted is not None:
+		map_client.set_message_property(options.mark_msg_undeleted, "Deleted", False)
+
+	if options.update_inbox:
+		map_client.update_inbox()
+
+	mainloop.run()
diff --git a/bluez/repo/test/monitor-bluetooth b/bluez/repo/test/monitor-bluetooth
new file mode 100755
index 0000000..d9b5472
--- /dev/null
+++ b/bluez/repo/test/monitor-bluetooth
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+relevant_ifaces = [ "org.bluez.Adapter1", "org.bluez.Device1" ]
+
+def property_changed(interface, changed, invalidated, path):
+	iface = interface[interface.rfind(".") + 1:]
+	for name, value in changed.iteritems():
+		val = str(value)
+		print("{%s.PropertyChanged} [%s] %s = %s" % (iface, path, name,
+									val))
+
+def interfaces_added(path, interfaces):
+	for iface, props in interfaces.iteritems():
+		if not(iface in relevant_ifaces):
+			continue
+		print("{Added %s} [%s]" % (iface, path))
+		for name, value in props.iteritems():
+			print("      %s = %s" % (name, value))
+
+def interfaces_removed(path, interfaces):
+	for iface in interfaces:
+		if not(iface in relevant_ifaces):
+			continue
+		print("{Removed %s} [%s]" % (iface, path))
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged",
+			path_keyword="path")
+
+	bus.add_signal_receiver(interfaces_added, bus_name="org.bluez",
+			dbus_interface="org.freedesktop.DBus.ObjectManager",
+			signal_name="InterfacesAdded")
+
+	bus.add_signal_receiver(interfaces_removed, bus_name="org.bluez",
+			dbus_interface="org.freedesktop.DBus.ObjectManager",
+			signal_name="InterfacesRemoved")
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/test/opp-client b/bluez/repo/test/opp-client
new file mode 100755
index 0000000..62d5b84
--- /dev/null
+++ b/bluez/repo/test/opp-client
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import os.path
+import sys
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE='org.bluez.obex.Client1'
+SESSION_INTERFACE='org.bluez.obex.Session1'
+OBJECT_PUSH_INTERFACE='org.bluez.obex.ObjectPush1'
+TRANSFER_INTERFACE='org.bluez.obex.Transfer1'
+
+def parse_options():
+	parser.add_option("-d", "--device", dest="device",
+			help="Device to connect", metavar="DEVICE")
+	parser.add_option("-p", "--pull", dest="pull_to_file",
+			help="Pull vcard and store in FILE", metavar="FILE")
+	parser.add_option("-s", "--send", dest="send_file",
+			help="Send FILE", metavar="FILE")
+	parser.add_option("-v", "--verbose", action="store_true",
+			dest="verbose")
+
+	return parser.parse_args()
+
+class OppClient:
+	def __init__(self, session_path, verbose=False):
+		self.transferred = 0
+		self.transfer_path = None
+		self.verbose = verbose
+		bus = dbus.SessionBus()
+		obj = bus.get_object(BUS_NAME, session_path)
+		self.session = dbus.Interface(obj, SESSION_INTERFACE)
+		self.opp = dbus.Interface(obj, OBJECT_PUSH_INTERFACE)
+		bus.add_signal_receiver(self.properties_changed,
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged",
+			path_keyword="path")
+
+	def create_transfer_reply(self, path, properties):
+		self.transfer_path = path
+		self.transfer_size = properties["Size"]
+		if self.verbose:
+			print("Transfer created: %s" % path)
+
+	def error(self, err):
+		print(err)
+		mainloop.quit()
+
+	def properties_changed(self, interface, properties, invalidated, path):
+		if path != self.transfer_path:
+			return
+
+		if "Status" in properties and \
+				(properties["Status"] == "complete" or \
+				properties["Status"] == "error"):
+			if self.verbose:
+				print("Transfer %s" % properties["Status"])
+			mainloop.quit()
+			return
+
+		if "Transferred" not in properties:
+			return
+
+		value = properties["Transferred"]
+		speed = (value - self.transferred) / 1000
+		print("Transfer progress %d/%d at %d kBps" % (value,
+							self.transfer_size,
+							speed))
+		self.transferred = value
+
+	def pull_business_card(self, filename):
+		self.opp.PullBusinessCard(os.path.abspath(filename),
+				reply_handler=self.create_transfer_reply,
+				error_handler=self.error)
+
+	def send_file(self, filename):
+		self.opp.SendFile(os.path.abspath(filename),
+				reply_handler=self.create_transfer_reply,
+				error_handler=self.error)
+
+if  __name__ == '__main__':
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	parser = OptionParser()
+
+	(options, args) = parse_options()
+
+	if not options.device:
+		parser.print_help()
+		sys.exit(0)
+
+	bus = dbus.SessionBus()
+	mainloop = GObject.MainLoop()
+
+	client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+							CLIENT_INTERFACE)
+
+	print("Creating Session")
+	path = client.CreateSession(options.device, { "Target": "OPP" })
+
+	opp_client = OppClient(path, options.verbose)
+
+	if options.pull_to_file:
+		opp_client.pull_business_card(options.pull_to_file)
+
+	if options.send_file:
+		opp_client.send_file(options.send_file)
+
+	mainloop.run()
diff --git a/bluez/repo/test/pbap-client b/bluez/repo/test/pbap-client
new file mode 100755
index 0000000..16a786b
--- /dev/null
+++ b/bluez/repo/test/pbap-client
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import os
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE = 'org.bluez.obex.Client1'
+SESSION_INTERFACE = 'org.bluez.obex.Session1'
+PHONEBOOK_ACCESS_INTERFACE = 'org.bluez.obex.PhonebookAccess1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+class Transfer:
+	def __init__(self, callback_func):
+		self.callback_func = callback_func
+		self.path = None
+		self.filename = None
+
+class PbapClient:
+	def __init__(self, session_path):
+		self.transfers = 0
+		self.props = dict()
+		self.flush_func = None
+		bus = dbus.SessionBus()
+		obj = bus.get_object(BUS_NAME, session_path)
+		self.session = dbus.Interface(obj, SESSION_INTERFACE)
+		self.pbap = dbus.Interface(obj, PHONEBOOK_ACCESS_INTERFACE)
+		bus.add_signal_receiver(self.properties_changed,
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged",
+			path_keyword="path")
+
+	def register(self, path, properties, transfer):
+		transfer.path = path
+		transfer.filename = properties["Filename"]
+		self.props[path] = transfer
+		print("Transfer created: %s (file %s)" % (path,
+							transfer.filename))
+
+	def error(self, err):
+		print(err)
+		mainloop.quit()
+
+	def transfer_complete(self, path):
+		req = self.props.get(path)
+		if req == None:
+			return
+		self.transfers -= 1
+		print("Transfer %s complete" % path)
+		try:
+			f = open(req.filename, "r")
+			os.remove(req.filename)
+			lines = f.readlines()
+			del self.props[path]
+			req.callback_func(lines)
+		except:
+			pass
+
+		if (len(self.props) == 0) and (self.transfers == 0):
+			if self.flush_func != None:
+				f = self.flush_func
+				self.flush_func = None
+				f()
+
+	def transfer_error(self, path):
+		print("Transfer %s error" % path)
+		mainloop.quit()
+
+	def properties_changed(self, interface, properties, invalidated, path):
+		req = self.props.get(path)
+		if req == None:
+			return
+
+		if properties['Status'] == 'complete':
+			self.transfer_complete(path)
+			return
+
+		if properties['Status'] == 'error':
+			self.transfer_error(path)
+			return
+
+	def pull(self, vcard, params, func):
+		req = Transfer(func)
+		self.pbap.Pull(vcard, "", params,
+			reply_handler=lambda o, p: self.register(o, p, req),
+			error_handler=self.error)
+		self.transfers += 1
+
+	def pull_all(self, params, func):
+		req = Transfer(func)
+		self.pbap.PullAll("", params,
+			reply_handler=lambda o, p: self.register(o, p, req),
+			error_handler=self.error)
+		self.transfers += 1
+
+	def flush_transfers(self, func):
+		if (len(self.props) == 0) and (self.transfers == 0):
+			return
+		self.flush_func = func
+
+	def interface(self):
+		return self.pbap
+
+if  __name__ == '__main__':
+
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SessionBus()
+	mainloop = GObject.MainLoop()
+
+	client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+							CLIENT_INTERFACE)
+
+	if (len(sys.argv) < 2):
+		print("Usage: %s <device>" % (sys.argv[0]))
+		sys.exit(1)
+
+	print("Creating Session")
+	session_path = client.CreateSession(sys.argv[1], { "Target": "PBAP" })
+
+	pbap_client = PbapClient(session_path)
+
+	def process_result(lines, header):
+		if header != None:
+			print(header)
+		for line in lines:
+			print(line),
+		print
+
+	def test_paths(paths):
+		if len(paths) == 0:
+			print
+			print("FINISHED")
+			mainloop.quit()
+			return
+
+		path = paths[0]
+
+		print("\n--- Select Phonebook %s ---\n" % (path))
+		pbap_client.interface().Select("int", path)
+
+		print("\n--- GetSize ---\n")
+		ret = pbap_client.interface().GetSize()
+		print("Size = %d\n" % (ret))
+
+		print("\n--- List vCard ---\n")
+		try:
+			ret = pbap_client.interface().List(dbus.Dictionary())
+		except:
+			ret = []
+
+		params = dbus.Dictionary({ "Format" : "vcard30",
+						"Fields" : ["PHOTO"] })
+		for item in ret:
+			print("%s : %s" % (item[0], item[1]))
+			pbap_client.pull(item[0], params,
+					lambda x: process_result(x, None))
+
+		pbap_client.pull_all(params, lambda x: process_result(x,
+							"\n--- PullAll ---\n"))
+
+		pbap_client.flush_transfers(lambda: test_paths(paths[1:]))
+
+	test_paths(["PB", "ICH", "OCH", "MCH", "CCH", "SPD", "FAV"])
+
+	mainloop.run()
diff --git a/bluez/repo/test/sap_client.py b/bluez/repo/test/sap_client.py
new file mode 100644
index 0000000..413424c
--- /dev/null
+++ b/bluez/repo/test/sap_client.py
@@ -0,0 +1,943 @@
+""" Copyright (C) 2010-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 """
+
+from array import array
+from bluetooth import *
+import time
+import re
+
+class SAPParam:
+    """ SAP Parameter Class """
+
+    MaxMsgSize = 0x00
+    ConnectionStatus = 0x01
+    ResultCode = 0x02
+    DisconnectionType = 0x03
+    CommandAPDU = 0x04
+    ResponseAPDU = 0x05
+    ATR = 0x06
+    CardReaderStatus = 0x07
+    StatusChange = 0x08
+    TransportProtocol = 0x09
+    CommandAPDU7816 = 0x10
+
+    def __init__(self, name, id, value = None):
+        self.name = name
+        self.id = id
+        self.value = value
+
+    def _padding(self,  buf):
+        pad = array('B')
+        while ( (len(buf) + len(pad)) % 4 ) != 0:
+            pad.append(0)
+        return pad
+
+    def _basicCheck(self,  buf):
+        if len(buf) < 4 or (len(buf) % 4) != 0 or buf[1] != 0:
+                return (-1,  -1)
+        if buf[0] != self.id:
+            return (-1,  -1)
+        plen = buf[2] * 256 + buf[3] + 4
+        if plen > len(buf):
+            return (-1,  -1)
+        pad = plen
+        while (pad % 4) != 0:
+            if buf[pad] != 0:
+                return (-1,  -1)
+            pad+=1
+        return (plen,  pad)
+
+    def getID(self):
+        return self.id
+
+    def getValue(self):
+        return self.value
+
+    def getContent(self):
+        return "%s(id=0x%.2X), value=%s \n" %  (self.name,  self.id, self.value)
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        a[1] = 0	# reserved
+        a[2] = 0	# length
+        a[3] = 1	# length
+        a.append(self.value)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1:
+            return -1
+        self.id = buf[0]
+        self.value = buf[4]
+        return p[1]
+
+
+class SAPParam_MaxMsgSize(SAPParam):
+    """MaxMsgSize Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"MaxMsgSize",  SAPParam.MaxMsgSize, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value > 0xFFFF:
+             self.value = 0xFFFF
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        a[3] = 2
+        a.append(self.value / 256)
+        a.append(self.value % 256)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1 :
+            return -1
+        self.value = buf[4] * 256 + buf[5]
+        return p[1]
+
+class SAPParam_CommandAPDU(SAPParam):
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CommandAPDU",  SAPParam.CommandAPDU, array('B'))
+        else:
+            SAPParam.__init__(self, "CommandAPDU",  SAPParam.CommandAPDU, array('B', value))
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        plen = len(self.value)
+        a[2] = plen / 256
+        a[3] = plen % 256
+        a.extend(self.value)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1:
+            return -1
+        self.value = buf[4:p[0]]
+        return p[1]
+
+class SAPParam_ResponseAPDU(SAPParam_CommandAPDU):
+    """ResponseAPDU Param """
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "ResponseAPDU",  SAPParam.ResponseAPDU, array('B'))
+        else:
+            SAPParam.__init__(self, "ResponseAPDU",  SAPParam.ResponseAPDU, array('B', value))
+
+class SAPParam_ATR(SAPParam_CommandAPDU):
+    """ATR Param """
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "ATR",  SAPParam.ATR, array('B'))
+        else:
+            SAPParam.__init__(self, "ATR",  SAPParam.ATR, array('B', value))
+
+class SAPParam_CommandAPDU7816(SAPParam_CommandAPDU):
+    """Command APDU7816 Param."""
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CommandAPDU7816",  SAPParam.CommandAPDU7816, array('B'))
+        else:
+            SAPParam.__init__(self, "CommandAPDU7816",  SAPParam.CommandAPDU7816, array('B', value))
+
+
+class SAPParam_ConnectionStatus(SAPParam):
+    """Connection status Param."""
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"ConnectionStatus",  SAPParam.ConnectionStatus, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04):
+            print "Warning. ConnectionStatus value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_ResultCode(SAPParam):
+    """ Result Code Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"ResultCode",  SAPParam.ResultCode, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04,  0x05,  0x06,  0x07):
+            print "Warning. ResultCode value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_DisconnectionType(SAPParam):
+    """Disconnection Type Param."""
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"DisconnectionType",  SAPParam.DisconnectionType, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01):
+            print "Warning. DisconnectionType value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_CardReaderStatus(SAPParam_CommandAPDU):
+    """Card reader Status Param."""
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CardReaderStatus",  SAPParam.CardReaderStatus, array('B'))
+        else:
+            SAPParam.__init__(self, "CardReaderStatus",  SAPParam.CardReaderStatus, array('B', value))
+
+class SAPParam_StatusChange(SAPParam):
+    """Status Change Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"StatusChange",  SAPParam.StatusChange, value)
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04,  0x05):
+            print "Warning. StatusChange value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_TransportProtocol(SAPParam):
+    """Transport Protocol Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"TransportProtocol",  SAPParam.TransportProtocol, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01):
+            print "Warning. TransportProtoco value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPMessage:
+
+    CONNECT_REQ = 0x00
+    CONNECT_RESP = 0x01
+    DISCONNECT_REQ = 0x02
+    DISCONNECT_RESP =0x03
+    DISCONNECT_IND = 0x04
+    TRANSFER_APDU_REQ = 0x05
+    TRANSFER_APDU_RESP = 0x06
+    TRANSFER_ATR_REQ = 0x07
+    TRANSFER_ATR_RESP = 0x08
+    POWER_SIM_OFF_REQ = 0x09
+    POWER_SIM_OFF_RESP = 0x0A
+    POWER_SIM_ON_REQ = 0x0B
+    POWER_SIM_ON_RESP = 0x0C
+    RESET_SIM_REQ = 0x0D
+    RESET_SIM_RESP = 0x0E
+    TRANSFER_CARD_READER_STATUS_REQ = 0x0F
+    TRANSFER_CARD_READER_STATUS_RESP = 0x10
+    STATUS_IND = 0x11
+    ERROR_RESP = 0x12
+    SET_TRANSPORT_PROTOCOL_REQ = 0x13
+    SET_TRANSPORT_PROTOCOL_RESP = 0x14
+
+    def __init__(self,  name,  id):
+        self.name = name
+        self.id = id
+        self.params = []
+        self.buf = array('B')
+
+    def _basicCheck(self,  buf):
+        if len(buf) < 4 or (len(buf) % 4) != 0 :
+            return False
+
+        if buf[0] != self.id:
+            return False
+
+        return True
+
+    def getID(self):
+        return self.id
+
+    def getContent(self):
+        s = "%s(id=0x%.2X) " % (self.name,  self.id)
+        if len( self.buf): s = s + "[%s]" % re.sub("(.{2})", "0x\\1 " , self.buf.tostring().encode("hex").upper(), re.DOTALL)
+        s = s + "\n\t"
+        for p in self.params:
+            s = s + "\t" + p.getContent()
+        return s
+
+    def getParams(self):
+        return self.params
+
+    def addParam(self,  param):
+        self.params.append(param)
+
+    def serialize(self):
+        ret = array('B', '\00\00\00\00')
+        ret[0] = self.id
+        ret[1] = len(self.params)
+        ret[2] = 0	# reserved
+        ret[3] = 0	# reserved
+        for p in self.params:
+            ret.extend(p.serialize())
+
+        self.buf = ret
+        return ret
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        return len(buf) == 4 and buf[1] == 0 and self._basicCheck(buf)
+
+
+class SAPMessage_CONNECT_REQ(SAPMessage):
+    def __init__(self,  MaxMsgSize = None):
+        SAPMessage.__init__(self,"CONNECT_REQ",  SAPMessage.CONNECT_REQ)
+        if MaxMsgSize is not None:
+            self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.MaxMsgSize:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_MaxMsgSize()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_CONNECT_RESP(SAPMessage):
+    def __init__(self,  ConnectionStatus = None,  MaxMsgSize = None):
+        SAPMessage.__init__(self,"CONNECT_RESP",  SAPMessage.CONNECT_RESP)
+        if ConnectionStatus is not None:
+            self.addParam(SAPParam_ConnectionStatus(ConnectionStatus))
+            if MaxMsgSize is not None:
+                self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ConnectionStatus:
+                if self.params[0].getValue() ==  0x02:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ConnectionStatus()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_MaxMsgSize()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_DISCONNECT_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"DISCONNECT_REQ",  SAPMessage.DISCONNECT_REQ)
+
+class SAPMessage_DISCONNECT_RESP(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"DISCONNECT_RESP",  SAPMessage.DISCONNECT_RESP)
+
+class SAPMessage_DISCONNECT_IND(SAPMessage):
+    def __init__(self,  Type = None):
+        SAPMessage.__init__(self,"DISCONNECT_IND",  SAPMessage.DISCONNECT_IND)
+        if Type is not None:
+            self.addParam(SAPParam_DisconnectionType(Type))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.DisconnectionType:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_DisconnectionType()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+
+class SAPMessage_TRANSFER_APDU_REQ(SAPMessage):
+    def __init__(self,  APDU = None,  T = False):
+        SAPMessage.__init__(self,"TRANSFER_APDU_REQ",  SAPMessage.TRANSFER_APDU_REQ)
+        if APDU is not None:
+            if T :
+                self.addParam(SAPParam_CommandAPDU(APDU))
+            else:
+                self.addParam(SAPParam_CommandAPDU7816(APDU))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.CommandAPDU or self.params[0].getID() == SAPParam.CommandAPDU7816:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+
+            p = SAPParam_CommandAPDU()
+            p2 = SAPParam_CommandAPDU7816()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+            elif p2.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p2)
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_APDU_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  Response = None):
+        SAPMessage.__init__(self,"TRANSFER_APDU_RESP",  SAPMessage.TRANSFER_APDU_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if Response is not None:
+                self.addParam(SAPParam_ResponseAPDU(Response))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_ResponseAPDU()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_ATR_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"TRANSFER_ATR_REQ",  SAPMessage.TRANSFER_ATR_REQ)
+
+class SAPMessage_TRANSFER_ATR_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  ATR = None):
+        SAPMessage.__init__(self,"TRANSFER_ATR_RESP",  SAPMessage.TRANSFER_ATR_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if ATR is not None:
+                self.addParam(SAPParam_ATR(ATR))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+
+            if  r != -1:
+
+                self.addParam(p)
+                if buf[1] == 2:
+
+                    p = SAPParam_ATR()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_POWER_SIM_OFF_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"POWER_SIM_OFF_REQ",  SAPMessage.POWER_SIM_OFF_REQ)
+
+class SAPMessage_POWER_SIM_OFF_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"POWER_SIM_OFF_RESP",  SAPMessage.POWER_SIM_OFF_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.ResultCode:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_POWER_SIM_ON_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"POWER_SIM_ON_REQ",  SAPMessage.POWER_SIM_ON_REQ)
+
+class SAPMessage_POWER_SIM_ON_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"POWER_SIM_ON_RESP",  SAPMessage.POWER_SIM_ON_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_RESET_SIM_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"RESET_SIM_REQ",  SAPMessage.RESET_SIM_REQ)
+
+class SAPMessage_RESET_SIM_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"RESET_SIM_RESP",  SAPMessage.RESET_SIM_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_STATUS_IND(SAPMessage):
+    def __init__(self,  StatusChange = None):
+        SAPMessage.__init__(self,"STATUS_IND",  SAPMessage.STATUS_IND)
+        if StatusChange is not None:
+            self.addParam(SAPParam_StatusChange(StatusChange))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.StatusChange:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_StatusChange()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_REQ",  SAPMessage.TRANSFER_CARD_READER_STATUS_REQ)
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  Status = None):
+        SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_RESP",  SAPMessage.TRANSFER_CARD_READER_STATUS_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if Status is not None:
+                self.addParam(SAPParam_CardReaderStatus(Status))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_CardReaderStatus()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_ERROR_RESP(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"ERROR_RESP",  SAPMessage.ERROR_RESP)
+
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(SAPMessage):
+    def __init__(self,  protocol = None):
+        SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_REQ",  SAPMessage.SET_TRANSPORT_PROTOCOL_REQ)
+        if protocol is not None:
+            self.addParam(SAPParam_TransportProtocol(protocol))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.TransportProtocol:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_TransportProtocol()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_RESP",  SAPMessage.SET_TRANSPORT_PROTOCOL_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+
+class SAPClient:
+
+    CONNECTED = 1
+    DISCONNECTED = 0
+
+    uuid = "0000112D-0000-1000-8000-00805F9B34FB"
+    bufsize = 1024
+    timeout = 20
+    state = DISCONNECTED
+
+    def __init__(self,  host = None,  port = None):
+        self.sock = None
+
+        if host is None or is_valid_address(host):
+            self.host = host
+        else:
+            raise BluetoothError ("%s is not a valid BT address." % host)
+            self.host = None
+            return
+
+        if port is None:
+            self.__discover()
+        else:
+            self.port = port
+
+        self.__connectRFCOMM()
+
+    def __del__(self):
+        self.__disconnectRFCOMM()
+
+    def __disconnectRFCOMM(self):
+        if self.sock is not None:
+            self.sock.close()
+            self.state = self.DISCONNECTED
+
+    def __discover(self):
+        service_matches = find_service(self.uuid, self.host)
+
+        if len(service_matches) == 0:
+            raise BluetoothError ("No SAP service found")
+            return
+
+        first_match = service_matches[0]
+        self.port = first_match["port"]
+        self.host = first_match["host"]
+
+        print "SAP Service found on %s(%s)" % first_match["name"] % self.host
+
+    def __connectRFCOMM(self):
+        self.sock=BluetoothSocket( RFCOMM )
+        self.sock.connect((self.host, self.port))
+        self.sock.settimeout(self.timeout)
+        self.state = self.CONNECTED
+
+    def __sendMsg(self, msg):
+        if isinstance(msg,  SAPMessage):
+            s = msg.serialize()
+            print "\tTX: " + msg.getContent()
+            return self.sock.send(s.tostring())
+
+    def __rcvMsg(self,  msg):
+        if isinstance(msg,  SAPMessage):
+            print "\tRX Wait: %s(id = 0x%.2x)" % (msg.name, msg.id)
+            data = self.sock.recv(self.bufsize)
+            if data:
+                if msg.deserialize(array('B',data)):
+                    print "\tRX: len(%d) %s" % (len(data), msg.getContent())
+                    return msg
+                else:
+                    print "msg: %s" % array('B',data)
+                    raise BluetoothError ("Message deserialization failed.")
+            else:
+                raise BluetoothError ("Timeout. No data received.")
+
+    def connect(self):
+        self.__connectRFCOMM()
+
+    def disconnect(self):
+        self.__disconnectRFCOMM()
+
+    def isConnected(self):
+        return self.state
+
+    def proc_connect(self):
+        try:
+            self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+            params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+            if params[0].getValue() in (0x00,  0x04):
+                pass
+            elif params[0].getValue() == 0x02:
+                self.bufsize = params[1].getValue()
+
+                self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+                params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+                if params[0].getValue() not in (0x00,  0x04):
+                    return False
+            else:
+                return False
+
+            params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+            if params[0].getValue() == 0x00:
+                return False
+            elif params[0].getValue() == 0x01:
+                """OK, Card reset"""
+                return self.proc_transferATR()
+            elif params[0].getValue() == 0x02:
+                """T0 not supported"""
+                if self.proc_transferATR():
+                    return self.proc_setTransportProtocol(1)
+                else:
+                    return False
+            else:
+                return False
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_disconnectByClient(self, timeout=0):
+        try:
+            self.__sendMsg(SAPMessage_DISCONNECT_REQ())
+            self.__rcvMsg(SAPMessage_DISCONNECT_RESP())
+            time.sleep(timeout) # let srv to close rfcomm
+            self.__disconnectRFCOMM()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_disconnectByServer(self, timeout=0):
+        try:
+            params = self.__rcvMsg(SAPMessage_DISCONNECT_IND()).getParams()
+
+            """graceful"""
+            if params[0].getValue() == 0x00:
+                if not self.proc_transferAPDU():
+                    return False
+
+            return self.proc_disconnectByClient(timeout)
+
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferAPDU(self,  apdu = "Sample APDU command"):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_APDU_REQ(apdu))
+            params = self.__rcvMsg(SAPMessage_TRANSFER_APDU_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferATR(self):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_ATR_REQ())
+            params = self.__rcvMsg(SAPMessage_TRANSFER_ATR_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_powerSimOff(self):
+        try:
+            self.__sendMsg(SAPMessage_POWER_SIM_OFF_REQ())
+            params = self.__rcvMsg(SAPMessage_POWER_SIM_OFF_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_powerSimOn(self):
+        try:
+            self.__sendMsg(SAPMessage_POWER_SIM_ON_REQ())
+            params = self.__rcvMsg(SAPMessage_POWER_SIM_ON_RESP()).getParams()
+            if params[0].getValue() == 0x00:
+                return self.proc_transferATR()
+
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_resetSim(self):
+        try:
+            self.__sendMsg(SAPMessage_RESET_SIM_REQ())
+            params = self.__rcvMsg(SAPMessage_RESET_SIM_RESP()).getParams()
+            if params[0].getValue() == 0x00:
+                return self.proc_transferATR()
+
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_reportStatus(self):
+        try:
+            params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferCardReaderStatus(self):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_REQ())
+            params = self.__rcvMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_RESP()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_errorResponse(self):
+        try:
+            """ send malformed message, no mandatory maxmsgsize parameter"""
+            self.__sendMsg(SAPMessage_CONNECT_REQ())
+
+            params = self.__rcvMsg(SAPMessage_ERROR_RESP()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_setTransportProtocol(self,  protocol = 0):
+        try:
+            self.__sendMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(protocol))
+            params = self.__rcvMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_RESP()).getParams()
+
+            if params[0].getValue() == 0x00:
+                params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+                if params[0].getValue() in (0x01,  0x02):
+                    return self.proc_transferATR()
+                else:
+                    return True
+                    """return False ???"""
+            elif params[0].getValue == 0x07:
+                """not supported"""
+                return True
+                """return False ???"""
+            else:
+                return False
+
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+if __name__ == "__main__":
+    pass
diff --git a/bluez/repo/test/service-did.xml b/bluez/repo/test/service-did.xml
new file mode 100644
index 0000000..52eb68c
--- /dev/null
+++ b/bluez/repo/test/service-did.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1200"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0200">
+    <uint16 value="0x0102" name="id"/>
+  </attribute>
+
+  <attribute id="0x0201">
+    <uint16 value="0x0a12" name="vendor"/>
+  </attribute>
+
+  <attribute id="0x0202">
+    <uint16 value="0x4711" name="product"/>
+  </attribute>
+
+  <attribute id="0x0203">
+    <uint16 value="0x0000" name="version"/>
+  </attribute>
+
+  <attribute id="0x0204">
+    <boolean value="true"/>
+  </attribute>
+
+  <attribute id="0x0205">
+    <uint16 value="0x0002" name="source"/>
+  </attribute>
+</record>
diff --git a/bluez/repo/test/service-ftp.xml b/bluez/repo/test/service-ftp.xml
new file mode 100644
index 0000000..1bda885
--- /dev/null
+++ b/bluez/repo/test/service-ftp.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1106"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0004">
+    <sequence>
+      <sequence>
+        <uuid value="0x0100"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0003"/>
+        <uint8 value="23" name="channel"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0008"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0009">
+    <sequence>
+      <sequence>
+        <uuid value="0x1106"/>
+        <uint16 value="0x0100" name="version"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0100">
+    <text value="OBEX File Transfer" name="name"/>
+  </attribute>
+</record>
diff --git a/bluez/repo/test/service-opp.xml b/bluez/repo/test/service-opp.xml
new file mode 100644
index 0000000..351b4a4
--- /dev/null
+++ b/bluez/repo/test/service-opp.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1105"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0004">
+    <sequence>
+      <sequence>
+        <uuid value="0x0100"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0003"/>
+        <uint8 value="23" name="channel"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0008"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0009">
+    <sequence>
+      <sequence>
+        <uuid value="0x1105"/>
+        <uint16 value="0x0100" name="version"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0100">
+    <text value="OBEX Object Push" name="name"/>
+  </attribute>
+
+  <attribute id="0x0303">
+    <sequence>
+      <uint8 value="0x01"/>
+      <uint8 value="0x01"/>
+      <uint8 value="0x02"/>
+      <uint8 value="0x03"/>
+      <uint8 value="0x04"/>
+      <uint8 value="0x05"/>
+      <uint8 value="0x06"/>
+      <uint8 value="0xff"/>
+    </sequence>
+  </attribute>
+</record>
diff --git a/bluez/repo/test/service-record.dtd b/bluez/repo/test/service-record.dtd
new file mode 100644
index 0000000..f53be5d
--- /dev/null
+++ b/bluez/repo/test/service-record.dtd
@@ -0,0 +1,66 @@
+<!ELEMENT record (attribute)*>
+
+<!ELEMENT attribute (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|nil)+>
+<!ATTLIST attribute id CDATA #REQUIRED>
+
+<!ELEMENT sequence (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|uint128|int8|int16|int32|int64|int128|nil)+>
+
+<!ELEMENT alternate (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|uint128|int8|int16|int32|int64|int128|nil)+>
+
+<!ELEMENT text EMPTY>
+<!ATTLIST text value CDATA #REQUIRED>
+<!ATTLIST text name CDATA>
+<!ATTLIST text encoding (normal|hex) "normal">
+
+<!ELEMENT url EMPTY>
+<!ATTLIST url value CDATA #REQUIRED>
+<!ATTLIST url name CDATA>
+
+<!ELEMENT uuid EMPTY>
+<!ATTLIST uuid value CDATA #REQUIRED>
+
+<!ELEMENT boolean EMPTY>
+<!ATTLIST boolean value CDATA #REQUIRED>
+<!ATTLIST boolean name CDATA>
+
+<!ELEMENT uint8 EMPTY>
+<!ATTLIST uint8 value CDATA #REQUIRED>
+<!ATTLIST uint8 name CDATA>
+
+<!ELEMENT uint16 EMPTY>
+<!ATTLIST uint16 value CDATA #REQUIRED>
+<!ATTLIST uint16 name CDATA>
+
+<!ELEMENT uint32 EMPTY>
+<!ATTLIST uint32 value CDATA #REQUIRED>
+<!ATTLIST uint32 name CDATA>
+
+<!ELEMENT uint64 EMPTY>
+<!ATTLIST uint64 value CDATA #REQUIRED>
+<!ATTLIST uint64 name CDATA>
+
+<!ELEMENT uint128 EMPTY>
+<!ATTLIST uint128 value CDATA #REQUIRED>
+<!ATTLIST uint128 name CDATA>
+
+<!ELEMENT int8 EMPTY>
+<!ATTLIST int8 value CDATA #REQUIRED>
+<!ATTLIST int8 name CDATA>
+
+<!ELEMENT int16 EMPTY>
+<!ATTLIST int16 value CDATA #REQUIRED>
+<!ATTLIST int16 name CDATA>
+
+<!ELEMENT int32 EMPTY>
+<!ATTLIST int32 value CDATA #REQUIRED>
+<!ATTLIST int32 name CDATA>
+
+<!ELEMENT int64 EMPTY>
+<!ATTLIST int64 value CDATA #REQUIRED>
+<!ATTLIST int64 name CDATA>
+
+<!ELEMENT int128 EMPTY>
+<!ATTLIST int128 value CDATA #REQUIRED>
+<!ATTLIST int128 name CDATA>
+
+<!ELEMENT nil EMPTY>
diff --git a/bluez/repo/test/service-spp.xml b/bluez/repo/test/service-spp.xml
new file mode 100644
index 0000000..2b156c3
--- /dev/null
+++ b/bluez/repo/test/service-spp.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1101"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0004">
+    <sequence>
+      <sequence>
+        <uuid value="0x0100"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0003"/>
+        <uint8 value="23" name="channel"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0100">
+    <text value="COM5" name="name"/>
+  </attribute>
+</record>
diff --git a/bluez/repo/test/simple-agent b/bluez/repo/test/simple-agent
new file mode 100755
index 0000000..a69299a
--- /dev/null
+++ b/bluez/repo/test/simple-agent
@@ -0,0 +1,183 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+BUS_NAME = 'org.bluez'
+AGENT_INTERFACE = 'org.bluez.Agent1'
+AGENT_PATH = "/test/agent"
+
+bus = None
+device_obj = None
+dev_path = None
+
+def ask(prompt):
+	try:
+		return raw_input(prompt)
+	except:
+		return input(prompt)
+
+def set_trusted(path):
+	props = dbus.Interface(bus.get_object("org.bluez", path),
+					"org.freedesktop.DBus.Properties")
+	props.Set("org.bluez.Device1", "Trusted", True)
+
+def dev_connect(path):
+	dev = dbus.Interface(bus.get_object("org.bluez", path),
+							"org.bluez.Device1")
+	dev.Connect()
+
+class Rejected(dbus.DBusException):
+	_dbus_error_name = "org.bluez.Error.Rejected"
+
+class Agent(dbus.service.Object):
+	exit_on_release = True
+
+	def set_exit_on_release(self, exit_on_release):
+		self.exit_on_release = exit_on_release
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		if self.exit_on_release:
+			mainloop.quit()
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="os", out_signature="")
+	def AuthorizeService(self, device, uuid):
+		print("AuthorizeService (%s, %s)" % (device, uuid))
+		authorize = ask("Authorize connection (yes/no): ")
+		if (authorize == "yes"):
+			return
+		raise Rejected("Connection rejected by user")
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="o", out_signature="s")
+	def RequestPinCode(self, device):
+		print("RequestPinCode (%s)" % (device))
+		set_trusted(device)
+		return ask("Enter PIN Code: ")
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="o", out_signature="u")
+	def RequestPasskey(self, device):
+		print("RequestPasskey (%s)" % (device))
+		set_trusted(device)
+		passkey = ask("Enter passkey: ")
+		return dbus.UInt32(passkey)
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="ouq", out_signature="")
+	def DisplayPasskey(self, device, passkey, entered):
+		print("DisplayPasskey (%s, %06u entered %u)" %
+						(device, passkey, entered))
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="os", out_signature="")
+	def DisplayPinCode(self, device, pincode):
+		print("DisplayPinCode (%s, %s)" % (device, pincode))
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="ou", out_signature="")
+	def RequestConfirmation(self, device, passkey):
+		print("RequestConfirmation (%s, %06d)" % (device, passkey))
+		confirm = ask("Confirm passkey (yes/no): ")
+		if (confirm == "yes"):
+			set_trusted(device)
+			return
+		raise Rejected("Passkey doesn't match")
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="o", out_signature="")
+	def RequestAuthorization(self, device):
+		print("RequestAuthorization (%s)" % (device))
+		auth = ask("Authorize? (yes/no): ")
+		if (auth == "yes"):
+			return
+		raise Rejected("Pairing rejected")
+
+	@dbus.service.method(AGENT_INTERFACE,
+					in_signature="", out_signature="")
+	def Cancel(self):
+		print("Cancel")
+
+def pair_reply():
+	print("Device paired")
+	set_trusted(dev_path)
+	dev_connect(dev_path)
+	mainloop.quit()
+
+def pair_error(error):
+	err_name = error.get_dbus_name()
+	if err_name == "org.freedesktop.DBus.Error.NoReply" and device_obj:
+		print("Timed out. Cancelling pairing")
+		device_obj.CancelPairing()
+	else:
+		print("Creating device failed: %s" % (error))
+
+
+	mainloop.quit()
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	capability = "KeyboardDisplay"
+
+	parser = OptionParser()
+	parser.add_option("-i", "--adapter", action="store",
+					type="string",
+					dest="adapter_pattern",
+					default=None)
+	parser.add_option("-c", "--capability", action="store",
+					type="string", dest="capability")
+	parser.add_option("-t", "--timeout", action="store",
+					type="int", dest="timeout",
+					default=60000)
+	(options, args) = parser.parse_args()
+	if options.capability:
+		capability  = options.capability
+
+	path = "/test/agent"
+	agent = Agent(bus, path)
+
+	mainloop = GObject.MainLoop()
+
+	obj = bus.get_object(BUS_NAME, "/org/bluez");
+	manager = dbus.Interface(obj, "org.bluez.AgentManager1")
+	manager.RegisterAgent(path, capability)
+
+	print("Agent registered")
+
+	# Fix-up old style invocation (BlueZ 4)
+	if len(args) > 0 and args[0].startswith("hci"):
+		options.adapter_pattern = args[0]
+		del args[:1]
+
+	if len(args) > 0:
+		device = bluezutils.find_device(args[0],
+						options.adapter_pattern)
+		dev_path = device.object_path
+		agent.set_exit_on_release(False)
+		device.Pair(reply_handler=pair_reply, error_handler=pair_error,
+								timeout=60000)
+		device_obj = device
+	else:
+		manager.RequestDefaultAgent(path)
+
+	mainloop.run()
+
+	#adapter.UnregisterAgent(path)
+	#print("Agent unregistered")
diff --git a/bluez/repo/test/simple-endpoint b/bluez/repo/test/simple-endpoint
new file mode 100755
index 0000000..0164cff
--- /dev/null
+++ b/bluez/repo/test/simple-endpoint
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB"
+A2DP_SINK_UUID = "0000110B-0000-1000-8000-00805F9B34FB"
+HFP_AG_UUID = "0000111F-0000-1000-8000-00805F9B34FB"
+HFP_HF_UUID = "0000111E-0000-1000-8000-00805F9B34FB"
+HSP_AG_UUID = "00001112-0000-1000-8000-00805F9B34FB"
+
+SBC_CODEC = dbus.Byte(0x00)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+#Subbands: 4 8
+#Blocks: 4 8 12 16
+#Bitpool Range: 2-64
+SBC_CAPABILITIES = dbus.Array([dbus.Byte(0xff), dbus.Byte(0xff), dbus.Byte(2), dbus.Byte(64)])
+# JointStereo 44.1Khz Subbands: Blocks: 16 Bitpool Range: 2-32
+SBC_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x15), dbus.Byte(2), dbus.Byte(32)])
+
+MP3_CODEC = dbus.Byte(0x01)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 32Khz 44.1Khz 48Khz
+#CRC: YES
+#Layer: 3
+#Bit Rate: All except Free format
+#VBR: Yes
+#Payload Format: RFC-2250
+MP3_CAPABILITIES = dbus.Array([dbus.Byte(0x3f), dbus.Byte(0x07), dbus.Byte(0xff), dbus.Byte(0xfe)])
+# JointStereo 44.1Khz Layer: 3 Bit Rate: VBR Format: RFC-2250
+MP3_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x02), dbus.Byte(0x00), dbus.Byte(0x80)])
+
+PCM_CODEC = dbus.Byte(0x00)
+PCM_CONFIGURATION = dbus.Array([], signature="ay")
+
+CVSD_CODEC = dbus.Byte(0x01)
+
+class Rejected(dbus.DBusException):
+	_dbus_error_name = "org.bluez.Error.Rejected"
+
+class Endpoint(dbus.service.Object):
+	exit_on_release = True
+	configuration = SBC_CONFIGURATION
+
+	def set_exit_on_release(self, exit_on_release):
+		self.exit_on_release = exit_on_release
+
+	def default_configuration(self, configuration):
+		self.configuration = configuration
+
+	@dbus.service.method("org.bluez.MediaEndpoint1",
+					in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		if self.exit_on_release:
+			mainloop.quit()
+
+	@dbus.service.method("org.bluez.MediaEndpoint1",
+					in_signature="", out_signature="")
+	def ClearConfiguration(self):
+		print("ClearConfiguration")
+
+	@dbus.service.method("org.bluez.MediaEndpoint1",
+					in_signature="oay", out_signature="")
+	def SetConfiguration(self, transport, config):
+		print("SetConfiguration (%s, %s)" % (transport, config))
+		return
+
+	@dbus.service.method("org.bluez.MediaEndpoint1",
+					in_signature="ay", out_signature="ay")
+	def SelectConfiguration(self, caps):
+		print("SelectConfiguration (%s)" % (caps))
+		return self.configuration
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	if len(sys.argv) > 1:
+		path = bluezutils.find_adapter(sys.argv[1]).object_path
+	else:
+		path = bluezutils.find_adapter().object_path
+
+	media = dbus.Interface(bus.get_object("org.bluez", path),
+						"org.bluez.Media1")
+
+	path = "/test/endpoint"
+	endpoint = Endpoint(bus, path)
+	mainloop = GObject.MainLoop()
+
+	properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+					"Codec" : SBC_CODEC,
+					"DelayReporting" : True,
+					"Capabilities" : SBC_CAPABILITIES })
+
+	if len(sys.argv) > 2:
+		if sys.argv[2] == "sbcsink":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+							"Codec" : SBC_CODEC,
+							"DelayReporting" : True,
+							"Capabilities" : SBC_CAPABILITIES })
+		if sys.argv[2] == "mp3source":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+							"Codec" : MP3_CODEC,
+							"Capabilities" : MP3_CAPABILITIES })
+			endpoint.default_configuration(MP3_CONFIGURATION)
+		if sys.argv[2] == "mp3sink":
+			properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+							"Codec" : MP3_CODEC,
+							"Capabilities" : MP3_CAPABILITIES })
+			endpoint.default_configuration(MP3_CONFIGURATION)
+		if sys.argv[2] == "hfpag" or sys.argv[2] == "hspag":
+			properties = dbus.Dictionary({ "UUID" : HFP_AG_UUID,
+							"Codec" : PCM_CODEC,
+							"Capabilities" :  PCM_CONFIGURATION })
+			endpoint.default_configuration(dbus.Array([]))
+		if sys.argv[2] == "hfphf":
+			properties = dbus.Dictionary({ "UUID" : HFP_HF_UUID,
+							"Codec" : CVSD_CODEC,
+							"Capabilities" :  PCM_CONFIGURATION })
+			endpoint.default_configuration(dbus.Array([]))
+
+	print(properties)
+
+	media.RegisterEndpoint(path, properties)
+
+	mainloop.run()
diff --git a/bluez/repo/test/simple-obex-agent b/bluez/repo/test/simple-obex-agent
new file mode 100755
index 0000000..05ec4ed
--- /dev/null
+++ b/bluez/repo/test/simple-obex-agent
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME = 'org.bluez.obex'
+PATH = '/org/bluez/obex'
+AGENT_MANAGER_INTERFACE = 'org.bluez.obex.AgentManager1'
+AGENT_INTERFACE = 'org.bluez.obex.Agent1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+def ask(prompt):
+	try:
+		return raw_input(prompt)
+	except:
+		return input(prompt)
+
+class Agent(dbus.service.Object):
+	def __init__(self, conn=None, obj_path=None):
+		dbus.service.Object.__init__(self, conn, obj_path)
+		self.pending_auth = False
+
+	@dbus.service.method(AGENT_INTERFACE, in_signature="o",
+							out_signature="s")
+	def AuthorizePush(self, path):
+		transfer = dbus.Interface(bus.get_object(BUS_NAME, path),
+					'org.freedesktop.DBus.Properties')
+		properties = transfer.GetAll(TRANSFER_INTERFACE);
+
+		self.pending_auth = True
+		auth = ask("Authorize (%s, %s) (Y/n):" % (path,
+							properties['Name']))
+
+		if auth == "n" or auth == "N":
+			self.pending_auth = False
+			raise dbus.DBusException(
+					"org.bluez.obex.Error.Rejected: "
+					"Not Authorized")
+
+		self.pending_auth = False
+
+		return properties['Name']
+
+	@dbus.service.method(AGENT_INTERFACE, in_signature="",
+							out_signature="")
+	def Cancel(self):
+		print("Authorization Canceled")
+		self.pending_auth = False
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SessionBus()
+	manager = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+						AGENT_MANAGER_INTERFACE)
+
+	path = "/test/agent"
+	agent = Agent(bus, path)
+
+	mainloop = GObject.MainLoop()
+
+	manager.RegisterAgent(path)
+	print("Agent registered")
+
+	cont = True
+	while cont:
+		try:
+			mainloop.run()
+		except KeyboardInterrupt:
+			if agent.pending_auth:
+				agent.Cancel()
+			elif len(transfers) > 0:
+				for a in transfers:
+					a.cancel()
+			else:
+				cont = False
+
+	# manager.UnregisterAgent(path)
+	# print "Agent unregistered"
diff --git a/bluez/repo/test/simple-player b/bluez/repo/test/simple-player
new file mode 100755
index 0000000..02754c2
--- /dev/null
+++ b/bluez/repo/test/simple-player
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import os
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+class Player(dbus.service.Object):
+	properties = None
+	metadata = None
+
+	def set_object(self, obj = None):
+		if obj != None:
+			bus = dbus.SystemBus()
+			mp = dbus.Interface(bus.get_object("org.bluez", obj),
+						"org.bluez.MediaPlayer1")
+			prop = dbus.Interface(bus.get_object("org.bluez", obj),
+						"org.freedesktop.DBus.Properties")
+
+			self.properties = prop.GetAll("org.bluez.MediaPlayer1")
+
+			bus.add_signal_receiver(self.properties_changed,
+				path = obj,
+				dbus_interface = "org.freedesktop.DBus.Properties",
+				signal_name = "PropertiesChanged")
+		else:
+			track = dbus.Dictionary({
+					"xesam:title" : "Title",
+					"xesam:artist" : ["Artist"],
+					"xesam:album" : "Album",
+					"xesam:genre" : ["Genre"],
+					"xesam:trackNumber" : dbus.Int32(1),
+					"mpris:length" : dbus.Int64(10000) },
+					signature="sv")
+
+			self.properties = dbus.Dictionary({
+					"PlaybackStatus" : "playing",
+					"Identity" : "SimplePlayer",
+					"LoopStatus" : "None",
+					"Rate" : dbus.Double(1.0),
+					"Shuffle" : dbus.Boolean(False),
+					"Metadata" : track,
+					"Volume" : dbus.Double(1.0),
+					"Position" : dbus.Int64(0),
+					"MinimumRate" : dbus.Double(1.0),
+					"MaximumRate" : dbus.Double(1.0),
+					"CanGoNext" : dbus.Boolean(False),
+					"CanGoPrevious" : dbus.Boolean(False),
+					"CanPlay" : dbus.Boolean(False),
+					"CanSeek" : dbus.Boolean(False),
+					"CanControl" : dbus.Boolean(False),
+					},
+					signature="sv")
+
+			handler = InputHandler(self)
+			GObject.io_add_watch(sys.stdin, GObject.IO_IN,
+							handler.handle)
+
+	@dbus.service.method("org.freedesktop.DBus.Properties",
+					in_signature="ssv", out_signature="")
+	def Set(self, interface, key, value):
+		print("Set (%s, %s)" % (key, value), file=sys.stderr)
+		return
+
+	@dbus.service.signal("org.freedesktop.DBus.Properties",
+							signature="sa{sv}as")
+	def PropertiesChanged(self, interface, properties,
+						invalidated = dbus.Array()):
+		"""PropertiesChanged(interface, properties, invalidated)
+
+		Send a PropertiesChanged signal. 'properties' is a dictionary
+		containing string parameters as specified in doc/media-api.txt.
+		"""
+		pass
+
+	def help(self, func):
+		help(self.__class__.__dict__[func])
+
+	def properties_changed(self, interface, properties, invalidated):
+		print("properties_changed(%s, %s)" % (properties, invalidated))
+
+		self.PropertiesChanged(interface, properties, invalidated)
+
+class InputHandler:
+	commands = { 'PropertiesChanged': '(interface, properties)',
+			'help': '(cmd)' }
+	def __init__(self, player):
+		self.player = player
+		print('\n\nAvailable commands:')
+		for cmd in self.commands:
+			print('\t', cmd, self.commands[cmd], sep='')
+
+		print("\nUse python syntax to pass arguments to available methods.\n" \
+                "E.g.: PropertiesChanged({'Metadata' : {'Title': 'My title', \
+		'Album': 'my album' }})")
+		self.prompt()
+
+	def prompt(self):
+		print('\n>>> ', end='')
+		sys.stdout.flush()
+
+	def handle(self, fd, condition):
+		s = os.read(fd.fileno(), 1024).strip()
+		try:
+			cmd = s[:s.find('(')]
+			if not cmd in self.commands:
+				print("Unknown command ", cmd)
+		except ValueError:
+			print("Malformed command")
+			return True
+
+		try:
+			exec "self.player.%s" % s
+		except Exception as e:
+			print(e)
+			pass
+		self.prompt()
+		return True
+
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	if len(sys.argv) > 1:
+		path = bluezutils.find_adapter(sys.argv[1]).object_path
+	else:
+		path = bluezutils.find_adapter().object_path
+
+	media = dbus.Interface(bus.get_object("org.bluez", path),
+						"org.bluez.Media1")
+
+	path = "/test/player"
+	player = Player(bus, path)
+	mainloop = GObject.MainLoop()
+
+	if len(sys.argv) > 2:
+		player.set_object(sys.argv[2])
+	else:
+		player.set_object()
+
+	print('Register media player with:\n\tProperties: %s' \
+						% (player.properties))
+
+	media.RegisterPlayer(dbus.ObjectPath(path), player.properties)
+
+	mainloop.run()
diff --git a/bluez/repo/test/test-adapter b/bluez/repo/test/test-adapter
new file mode 100755
index 0000000..959a437
--- /dev/null
+++ b/bluez/repo/test/test-adapter
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import sys
+import time
+import dbus
+import bluezutils
+
+bus = dbus.SystemBus()
+
+option_list = [
+		make_option("-i", "--device", action="store",
+				type="string", dest="dev_id"),
+		]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+adapter_path = bluezutils.find_adapter(options.dev_id).object_path
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+					"org.freedesktop.DBus.Properties")
+
+if (len(args) < 1):
+	print("Usage: %s <command>" % (sys.argv[0]))
+	print("")
+	print("  address")
+	print("  list")
+	print("  name")
+	print("  alias [alias]")
+	print("  powered [on/off]")
+	print("  pairable [on/off]")
+	print("  pairabletimeout [timeout]")
+	print("  discoverable [on/off]")
+	print("  discoverabletimeout [timeout]")
+	print("  discovering")
+	sys.exit(1)
+
+if (args[0] == "address"):
+	addr = adapter.Get("org.bluez.Adapter1", "Address")
+	print(addr)
+	sys.exit(0)
+
+if (args[0] == "name"):
+	name = adapter.Get("org.bluez.Adapter1", "Name")
+	print(name)
+	sys.exit(0)
+
+if (args[0] == "alias"):
+	if (len(args) < 2):
+		alias = adapter.Get("org.bluez.Adapter1", "Alias")
+		print(alias)
+	else:
+		adapter.Set("org.bluez.Adapter1", "Alias", args[1])
+	sys.exit(0)
+
+if (args[0] == "list"):
+	if (len(args) < 2):
+		om = dbus.Interface(bus.get_object("org.bluez", "/"),
+					"org.freedesktop.DBus.ObjectManager")
+		objects = om.GetManagedObjects()
+		for path, interfaces in objects.iteritems():
+			if "org.bluez.Adapter1" not in interfaces:
+				continue
+
+			print(" [ %s ]" % (path))
+
+			props = interfaces["org.bluez.Adapter1"]
+
+			for (key, value) in props.items():
+				if (key == "Class"):
+					print("    %s = 0x%06x" % (key, value))
+				else:
+					print("    %s = %s" % (key, value))
+			print()
+	sys.exit(0)
+
+if (args[0] == "powered"):
+	if (len(args) < 2):
+		powered = adapter.Get("org.bluez.Adapter1", "Powered")
+		print(powered)
+	else:
+		if (args[1] == "on"):
+			value = dbus.Boolean(1)
+		elif (args[1] == "off"):
+			value = dbus.Boolean(0)
+		else:
+			value = dbus.Boolean(args[1])
+		adapter.Set("org.bluez.Adapter1", "Powered", value)
+	sys.exit(0)
+
+if (args[0] == "pairable"):
+	if (len(args) < 2):
+		pairable = adapter.Get("org.bluez.Adapter1", "Pairable")
+		print(pairable)
+	else:
+		if (args[1] == "on"):
+			value = dbus.Boolean(1)
+		elif (args[1] == "off"):
+			value = dbus.Boolean(0)
+		else:
+			value = dbus.Boolean(args[1])
+		adapter.Set("org.bluez.Adapter1", "Pairable", value)
+	sys.exit(0)
+
+if (args[0] == "pairabletimeout"):
+	if (len(args) < 2):
+		pt = adapter.Get("org.bluez.Adapter1", "PairableTimeout")
+		print(pt)
+	else:
+		timeout = dbus.UInt32(args[1])
+		adapter.Set("org.bluez.Adapter1", "PairableTimeout", timeout)
+	sys.exit(0)
+
+if (args[0] == "discoverable"):
+	if (len(args) < 2):
+		discoverable = adapter.Get("org.bluez.Adapter1", "Discoverable")
+		print(discoverable)
+	else:
+		if (args[1] == "on"):
+			value = dbus.Boolean(1)
+		elif (args[1] == "off"):
+			value = dbus.Boolean(0)
+		else:
+			value = dbus.Boolean(args[1])
+		adapter.Set("org.bluez.Adapter1", "Discoverable", value)
+	sys.exit(0)
+
+if (args[0] == "discoverabletimeout"):
+	if (len(args) < 2):
+		dt = adapter.Get("org.bluez.Adapter1", "DiscoverableTimeout")
+		print(dt)
+	else:
+		to = dbus.UInt32(args[1])
+		adapter.Set("org.bluez.Adapter1", "DiscoverableTimeout", to)
+	sys.exit(0)
+
+if (args[0] == "discovering"):
+	discovering = adapter.Get("org.bluez.Adapter1", "Discovering")
+	print(discovering)
+	sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/bluez/repo/test/test-alert b/bluez/repo/test/test-alert
new file mode 100755
index 0000000..43b3cf3
--- /dev/null
+++ b/bluez/repo/test/test-alert
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import optparse
+import os
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME = 'org.bluez'
+ALERT_INTERFACE = 'org.bluez.Alert1'
+ALERT_AGENT_INTERFACE = 'org.bluez.AlertAgent1'
+BLUEZ_OBJECT_PATH = '/org/bluez'
+TEST_OBJECT_PATH = '/org/bluez/test'
+
+class AlertAgent(dbus.service.Object):
+	def __init__(self, bus, object_path, alert, mainloop):
+		dbus.service.Object.__init__(self, bus, object_path)
+		self.alert = alert
+		self.mainloop = mainloop
+
+	@dbus.service.method(ALERT_AGENT_INTERFACE, in_signature='',
+							out_signature='')
+	def MuteOnce(self):
+		print('method MuteOnce() was called')
+		self.alert.NewAlert('ringer', 1, 'not active')
+
+	@dbus.service.method(ALERT_AGENT_INTERFACE, in_signature='s',
+							out_signature='')
+	def SetRinger(self, mode):
+		print('method SetRinger(%s) was called' % mode)
+		self.alert.NewAlert('ringer', 1, mode)
+
+	@dbus.service.method(ALERT_AGENT_INTERFACE, in_signature='',
+							out_signature='')
+	def Release(self):
+		print('method Release() was called')
+		self.mainloop.quit()
+
+def print_command_line(options):
+	if not options.verbose:
+		return False
+
+	print('-w: ' + str(options.wait))
+
+	if options.times:
+		print('-t: ' + str(options.times))
+
+	if options.register:
+		print('-r: ' + options.register)
+	else:
+		print('-r: ' + str(None))
+
+	if options.new_alert:
+		print('-n:')
+		for i in options.new_alert:
+			print('    ' + i[0] + ', ' + i[1] + ', ' + i[2])
+	else:
+		print('-n: ' + str(None))
+
+	if options.unread_alert:
+		print('-u:')
+		for i in options.unread_alert:
+			print('    ' + i[0] + ', ' + i[1])
+	else:
+		print('-u: ' + str(None))
+
+	print()
+
+	return True
+
+def read_count(param):
+	try:
+		return int(param)
+	except ValueError:
+		print('<count> must be integer, not \"%s\"' % param)
+		sys.exit(1)
+
+def new_alert(alert, params):
+	if not params:
+		return False
+
+	for param in params:
+		category = param[0]
+		count = read_count(param[1])
+		description = param[2]
+
+		alert.NewAlert(category, count, description)
+
+def unread_alert(alert, params):
+	if not params:
+		return False
+
+	for param in params:
+		category = param[0]
+		count = read_count(param[1])
+
+		alert.UnreadAlert(category, count)
+
+option_list = [
+	optparse.make_option('-v', None,
+			action = 'store_true',
+			default = False,
+			dest = 'verbose',
+			help = 'verbose'),
+
+	optparse.make_option('-w', None,
+			action = 'store_true',
+			default = False,
+			dest = 'wait',
+			help = 'wait for dbus events'),
+
+	optparse.make_option('-t', None,
+			action = 'store',
+			default = 1,
+			type = "int",
+			dest = 'times',
+			help = 'repeat UnreadAlert/NewAlert <times> times',
+			metavar = '<times>'),
+
+	optparse.make_option('-r', None,
+			action = 'store',
+			dest = 'register',
+			type = 'string',
+			metavar = '<category>',
+			help = 'register alert'),
+
+	optparse.make_option('-n', None,
+			action = 'append',
+			dest = 'new_alert',
+			type = 'string',
+			nargs = 3,
+			metavar = '<category> <count> <description>',
+			help = 'send new alert'),
+
+	optparse.make_option('-u', None,
+			action = 'append',
+			dest = 'unread_alert',
+			type = 'string',
+			nargs = 2,
+			metavar = '<category> <count>',
+			help = 'send unread alert'),
+]
+
+parser = optparse.OptionParser(option_list=option_list)
+parser.disable_interspersed_args()
+(options, args) = parser.parse_args()
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = GObject.MainLoop()
+alert = dbus.Interface(bus.get_object(BUS_NAME, BLUEZ_OBJECT_PATH),
+								ALERT_INTERFACE)
+alert_agent = AlertAgent(bus, TEST_OBJECT_PATH, alert, mainloop)
+
+print_command_line(options)
+
+if not (options.register or options.new_alert or options.unread_alert or
+								options.wait):
+	parser.print_usage()
+	sys.exit(1)
+
+if options.register:
+	alert.RegisterAlert(options.register, TEST_OBJECT_PATH)
+
+times = 0
+while times < options.times:
+	times += 1
+
+	new_alert(alert, options.new_alert)
+	unread_alert(alert, options.unread_alert)
+
+if not options.wait:
+	sys.exit(0)
+
+try:
+	mainloop.run()
+except:
+	pass
diff --git a/bluez/repo/test/test-cyclingspeed b/bluez/repo/test/test-cyclingspeed
new file mode 100755
index 0000000..393f79c
--- /dev/null
+++ b/bluez/repo/test/test-cyclingspeed
@@ -0,0 +1,197 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Cycling Speed and Cadence test script
+'''
+
+from optparse import OptionParser, make_option
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+BUS_NAME = 'org.bluez'
+CYCLINGSPEED_MANAGER_INTERFACE = 'org.bluez.CyclingSpeedManager1'
+CYCLINGSPEED_WATCHER_INTERFACE = 'org.bluez.CyclingSpeedWatcher1'
+CYCLINGSPEED_INTERFACE = 'org.bluez.CyclingSpeed1'
+
+class MeasurementQ:
+	def __init__(self, wrap_v):
+		self._now = [None, None]
+		self._prev = [None, None]
+		self._wrap_v = wrap_v
+
+	def can_calc(self):
+		return ((self._now[0] is not None)
+			and (self._now[1] is not None)
+			and (self._prev[0] is not None)
+			and (self._prev[1] is not None))
+
+	def delta_v(self):
+		delta = self._now[0] - self._prev[0]
+		if (delta < 0) and (self._wrap_v):
+			delta = delta + 65536
+		return delta
+
+	def delta_t(self):
+		delta = self._now[1] - self._prev[1]
+		if delta < 0:
+			delta = delta + 65536
+		return delta
+
+	def put(self, data):
+		self._prev = self._now
+		self._now = data
+
+class Watcher(dbus.service.Object):
+	_wheel = MeasurementQ(False)
+	_crank = MeasurementQ(True)
+	_circumference = None
+
+	def enable_calc(self, v):
+		self._circumference = v
+
+	@dbus.service.method(CYCLINGSPEED_WATCHER_INTERFACE,
+					in_signature="oa{sv}", out_signature="")
+	def MeasurementReceived(self, device, measure):
+		print("Measurement received from %s" % device)
+
+		rev = None
+		evt = None
+		if "WheelRevolutions" in measure:
+			rev = measure["WheelRevolutions"]
+			print("WheelRevolutions: ", measure["WheelRevolutions"])
+		if "LastWheelEventTime" in measure:
+			evt = measure["LastWheelEventTime"]
+			print("LastWheelEventTime: ", measure["LastWheelEventTime"])
+		self._wheel.put( [rev, evt] )
+
+		rev = None
+		evt = None
+		if "CrankRevolutions" in measure:
+			rev = measure["CrankRevolutions"]
+			print("CrankRevolutions: ", measure["CrankRevolutions"])
+		if "LastCrankEventTime" in measure:
+			evt = measure["LastCrankEventTime"]
+			print("LastCrankEventTime: ", measure["LastCrankEventTime"])
+		self._crank.put( [rev, evt] )
+
+		if self._circumference is None:
+			return
+
+		if self._wheel.can_calc():
+			delta_v = self._wheel.delta_v()
+			delta_t = self._wheel.delta_t()
+
+			if (delta_v >= 0) and (delta_t > 0):
+				speed = delta_v * self._circumference * 1024 / delta_t # mm/s
+				speed = speed * 0.0036 # mm/s -> km/h
+				print("(calculated) Speed: %.2f km/h" % speed)
+
+		if self._crank.can_calc():
+			delta_v = self._crank.delta_v()
+			delta_t = self._crank.delta_t()
+
+			if delta_t > 0:
+				cadence = delta_v * 1024 / delta_t
+				print("(calculated) Cadence: %d rpm" % cadence)
+
+def properties_changed(interface, changed, invalidated):
+	if "Location" in changed:
+		print("Sensor location: %s" % changed["Location"])
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	option_list = [
+		make_option("-i", "--adapter", action="store",
+			type="string", dest="adapter"),
+		make_option("-b", "--device", action="store",
+			type="string", dest="address"),
+		make_option("-c", "--circumference", action="store",
+			type="int", dest="circumference"),
+		]
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	if not options.address:
+		print("Usage: %s [-i <adapter>] -b <bdaddr> [-c <value>] [cmd]" % (sys.argv[0]))
+		print("Possible commands:")
+		print("\tShowSupportedLocations")
+		print("\tSetLocation <location>")
+		print("\tSetCumulativeWheelRevolutions <value>")
+		sys.exit(1)
+
+	managed_objects = bluezutils.get_managed_objects()
+	adapter = bluezutils.find_adapter_in_objects(managed_objects,
+								options.adapter)
+	adapter_path = adapter.object_path
+
+	device = bluezutils.find_device_in_objects(managed_objects,
+								options.address,
+								options.adapter)
+	device_path = device.object_path
+
+	cscmanager = dbus.Interface(bus.get_object(BUS_NAME, adapter_path),
+						CYCLINGSPEED_MANAGER_INTERFACE)
+
+	watcher_path = "/test/watcher"
+	watcher = Watcher(bus, watcher_path)
+	if options.circumference:
+		watcher.enable_calc(options.circumference)
+	cscmanager.RegisterWatcher(watcher_path)
+
+	csc = dbus.Interface(bus.get_object(BUS_NAME, device_path),
+							CYCLINGSPEED_INTERFACE)
+
+	bus.add_signal_receiver(properties_changed, bus_name=BUS_NAME,
+				path=device_path,
+				dbus_interface="org.freedesktop.DBus.Properties",
+				signal_name="PropertiesChanged")
+
+	device_prop = dbus.Interface(bus.get_object(BUS_NAME, device_path),
+					"org.freedesktop.DBus.Properties")
+
+	properties = device_prop.GetAll(CYCLINGSPEED_INTERFACE)
+
+	if "Location" in properties:
+		print("Sensor location: %s" % properties["Location"])
+	else:
+		print("Sensor location is not supported")
+
+	if len(args) > 0:
+		if args[0] == "ShowSupportedLocations":
+			if properties["MultipleLocationsSupported"]:
+				print("Supported locations: ", properties["SupportedLocations"])
+			else:
+				print("Multiple sensor locations not supported")
+
+		elif args[0] == "SetLocation":
+			if properties["MultipleLocationsSupported"]:
+				device_prop.Set(CYCLINGSPEED_INTERFACE, "Location", args[1])
+			else:
+				print("Multiple sensor locations not supported")
+
+		elif args[0] == "SetCumulativeWheelRevolutions":
+			if properties["WheelRevolutionDataSupported"]:
+				csc.SetCumulativeWheelRevolutions(dbus.UInt32(args[1]))
+			else:
+				print("Wheel revolution data not supported")
+
+		else:
+			print("Unknown command")
+			sys.exit(1)
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/test/test-device b/bluez/repo/test/test-device
new file mode 100755
index 0000000..b490d53
--- /dev/null
+++ b/bluez/repo/test/test-device
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import re
+import sys
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = GObject.MainLoop()
+
+option_list = [
+		make_option("-i", "--device", action="store",
+				type="string", dest="dev_id"),
+		]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if (len(args) < 1):
+	print("Usage: %s <command>" % (sys.argv[0]))
+	print("")
+	print("  list")
+	print("  create <address>")
+	print("  remove <address|path>")
+	print("  connect <address> [profile]")
+	print("  disconnect <address> [profile]")
+	print("  class <address>")
+	print("  name <address>")
+	print("  alias <address> [alias]")
+	print("  trusted <address> [yes/no]")
+	print("  blocked <address> [yes/no]")
+	sys.exit(1)
+
+if (args[0] == "list"):
+	adapter = bluezutils.find_adapter(options.dev_id)
+	adapter_path = adapter.object_path
+
+	om = dbus.Interface(bus.get_object("org.bluez", "/"),
+					"org.freedesktop.DBus.ObjectManager")
+	objects = om.GetManagedObjects()
+
+	for path, interfaces in objects.iteritems():
+		if "org.bluez.Device1" not in interfaces:
+			continue
+		properties = interfaces["org.bluez.Device1"]
+		if properties["Adapter"] != adapter_path:
+			continue;
+		print("%s %s" % (properties["Address"], properties["Alias"]))
+
+	sys.exit(0)
+
+def create_device_reply(device):
+	print("New device (%s)" % device)
+	mainloop.quit()
+	sys.exit(0)
+
+def create_device_error(error):
+	print("Creating device failed: %s" % error)
+	mainloop.quit()
+	sys.exit(1)
+
+if (args[0] == "create"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		adapter = bluezutils.find_adapter(options.dev_id)
+		adapter.CreateDevice(args[1],
+				reply_handler=create_device_reply,
+				error_handler=create_device_error)
+	mainloop.run()
+
+if (args[0] == "remove"):
+	if (len(args) < 2):
+		print("Need address or object path parameter")
+	else:
+		managed_objects = bluezutils.get_managed_objects()
+		adapter = bluezutils.find_adapter_in_objects(managed_objects,
+								options.dev_id)
+		try:
+			dev = bluezutils.find_device_in_objects(managed_objects,
+								args[1],
+								options.dev_id)
+			path = dev.object_path
+		except:
+			path = args[1]
+		adapter.RemoveDevice(path)
+	sys.exit(0)
+
+if (args[0] == "connect"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		if (len(args) > 2):
+			device.ConnectProfile(args[2])
+		else:
+			device.Connect()
+	sys.exit(0)
+
+if (args[0] == "disconnect"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		if (len(args) > 2):
+			device.DisconnectProfile(args[2])
+		else:
+			device.Disconnect()
+	sys.exit(0)
+
+if (args[0] == "class"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		path = device.object_path
+		props = dbus.Interface(bus.get_object("org.bluez", path),
+					"org.freedesktop.DBus.Properties")
+		cls = props.Get("org.bluez.Device1", "Class")
+		print("0x%06x" % cls)
+	sys.exit(0)
+
+if (args[0] == "name"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		path = device.object_path
+		props = dbus.Interface(bus.get_object("org.bluez", path),
+					"org.freedesktop.DBus.Properties")
+		name = props.Get("org.bluez.Device1", "Name")
+		print(name)
+	sys.exit(0)
+
+if (args[0] == "alias"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		path = device.object_path
+		props = dbus.Interface(bus.get_object("org.bluez", path),
+					"org.freedesktop.DBus.Properties")
+		if (len(args) < 3):
+			alias = props.Get("org.bluez.Device1", "Alias")
+			print(alias)
+		else:
+			props.Set("org.bluez.Device1", "Alias", args[2])
+	sys.exit(0)
+
+if (args[0] == "trusted"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		path = device.object_path
+		props = dbus.Interface(bus.get_object("org.bluez", path),
+					"org.freedesktop.DBus.Properties")
+		if (len(args) < 3):
+			trusted = props.Get("org.bluez.Device1", "Trusted")
+			print(trusted)
+		else:
+			if (args[2] == "yes"):
+				value = dbus.Boolean(1)
+			elif (args[2] == "no"):
+				value = dbus.Boolean(0)
+			else:
+				value = dbus.Boolean(args[2])
+			props.Set("org.bluez.Device1", "Trusted", value)
+	sys.exit(0)
+
+if (args[0] == "blocked"):
+	if (len(args) < 2):
+		print("Need address parameter")
+	else:
+		device = bluezutils.find_device(args[1], options.dev_id)
+		path = device.object_path
+		props = dbus.Interface(bus.get_object("org.bluez", path),
+					"org.freedesktop.DBus.Properties")
+		if (len(args) < 3):
+			blocked = props.Get("org.bluez.Device1", "Blocked")
+			print(blocked)
+		else:
+			if (args[2] == "yes"):
+				value = dbus.Boolean(1)
+			elif (args[2] == "no"):
+				value = dbus.Boolean(0)
+			else:
+				value = dbus.Boolean(args[2])
+			props.Set("org.bluez.Device1", "Blocked", value)
+	sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/bluez/repo/test/test-discovery b/bluez/repo/test/test-discovery
new file mode 100755
index 0000000..9a2c6b9
--- /dev/null
+++ b/bluez/repo/test/test-discovery
@@ -0,0 +1,150 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+compact = False
+devices = {}
+
+def print_compact(address, properties):
+	name = ""
+	address = "<unknown>"
+
+	for key, value in properties.iteritems():
+		if type(value) is dbus.String:
+			value = unicode(value).encode('ascii', 'replace')
+		if (key == "Name"):
+			name = value
+		elif (key == "Address"):
+			address = value
+
+	if "Logged" in properties:
+		flag = "*"
+	else:
+		flag = " "
+
+	print("%s%s %s" % (flag, address, name))
+
+	properties["Logged"] = True
+
+def print_normal(address, properties):
+	print("[ " + address + " ]")
+
+	for key in properties.keys():
+		value = properties[key]
+		if type(value) is dbus.String:
+			value = unicode(value).encode('ascii', 'replace')
+		if (key == "Class"):
+			print("    %s = 0x%06x" % (key, value))
+		else:
+			print("    %s = %s" % (key, value))
+
+	print()
+
+	properties["Logged"] = True
+
+def skip_dev(old_dev, new_dev):
+	if not "Logged" in old_dev:
+		return False
+	if "Name" in old_dev:
+		return True
+	if not "Name" in new_dev:
+		return True
+	return False
+
+def interfaces_added(path, interfaces):
+	properties = interfaces["org.bluez.Device1"]
+	if not properties:
+		return
+
+	if path in devices:
+		dev = devices[path]
+
+		if compact and skip_dev(dev, properties):
+			return
+		devices[path] = dict(devices[path].items() + properties.items())
+	else:
+		devices[path] = properties
+
+	if "Address" in devices[path]:
+		address = properties["Address"]
+	else:
+		address = "<unknown>"
+
+	if compact:
+		print_compact(address, devices[path])
+	else:
+		print_normal(address, devices[path])
+
+def properties_changed(interface, changed, invalidated, path):
+	if interface != "org.bluez.Device1":
+		return
+
+	if path in devices:
+		dev = devices[path]
+
+		if compact and skip_dev(dev, changed):
+			return
+		devices[path] = dict(devices[path].items() + changed.items())
+	else:
+		devices[path] = changed
+
+	if "Address" in devices[path]:
+		address = devices[path]["Address"]
+	else:
+		address = "<unknown>"
+
+	if compact:
+		print_compact(address, devices[path])
+	else:
+		print_normal(address, devices[path])
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	option_list = [
+			make_option("-i", "--device", action="store",
+					type="string", dest="dev_id"),
+			make_option("-c", "--compact",
+					action="store_true", dest="compact"),
+			]
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	adapter = bluezutils.find_adapter(options.dev_id)
+
+	if options.compact:
+		compact = True;
+
+	bus.add_signal_receiver(interfaces_added,
+			dbus_interface = "org.freedesktop.DBus.ObjectManager",
+			signal_name = "InterfacesAdded")
+
+	bus.add_signal_receiver(properties_changed,
+			dbus_interface = "org.freedesktop.DBus.Properties",
+			signal_name = "PropertiesChanged",
+			arg0 = "org.bluez.Device1",
+			path_keyword = "path")
+
+	om = dbus.Interface(bus.get_object("org.bluez", "/"),
+				"org.freedesktop.DBus.ObjectManager")
+	objects = om.GetManagedObjects()
+	for path, interfaces in objects.iteritems():
+		if "org.bluez.Device1" in interfaces:
+			devices[path] = interfaces["org.bluez.Device1"]
+
+	adapter.StartDiscovery()
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/test/test-gatt-profile b/bluez/repo/test/test-gatt-profile
new file mode 100755
index 0000000..ad320b1
--- /dev/null
+++ b/bluez/repo/test/test-gatt-profile
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import os
+import sys
+import uuid
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+class GattProfile(dbus.service.Object):
+	@dbus.service.method("org.bluez.GattProfile1",
+					in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		mainloop.quit()
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	path = bluezutils.find_adapter().object_path
+
+	manager = dbus.Interface(bus.get_object("org.bluez", path),
+				"org.bluez.GattManager1")
+
+	option_list = [
+			make_option("-u", "--uuid", action="store",
+					type="string", dest="uuid",
+					default=None),
+			make_option("-p", "--path", action="store",
+					type="string", dest="path",
+					default="/foo/bar/profile"),
+			]
+
+	opts = dbus.Dictionary({ }, signature='sv')
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	profile = GattProfile(bus, options.path)
+
+	mainloop = GObject.MainLoop()
+
+	if not options.uuid:
+		options.uuid = str(uuid.uuid4())
+
+	uuids = { options.uuid }
+	manager.RegisterProfile(options.path, uuids, opts)
+
+	mainloop.run()
diff --git a/bluez/repo/test/test-health b/bluez/repo/test/test-health
new file mode 100755
index 0000000..24afa79
--- /dev/null
+++ b/bluez/repo/test/test-health
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+# -*- coding: utf-8 -*-
+
+import sys
+import dbus
+import dbus.service
+from dbus.mainloop.glib import DBusGMainLoop
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME = 'org.bluez'
+PATH = '/org/bluez'
+ADAPTER_INTERFACE = 'org.bluez.Adapter1'
+HEALTH_MANAGER_INTERFACE = 'org.bluez.HealthManager1'
+HEALTH_DEVICE_INTERFACE = 'org.bluez.HealthDevice1'
+
+DBusGMainLoop(set_as_default=True)
+loop = GObject.MainLoop()
+
+bus = dbus.SystemBus()
+
+def sig_received(*args, **kwargs):
+	if "member" not in kwargs:
+		return
+	if "path" not in kwargs:
+		return;
+	sig_name = kwargs["member"]
+	path = kwargs["path"]
+	print(sig_name)
+	print(path)
+	if sig_name == "PropertyChanged":
+		k, v = args
+		print(k)
+		print(v)
+	else:
+		ob = args[0]
+		print(ob)
+
+
+def enter_mainloop():
+	bus.add_signal_receiver(sig_received, bus_name=BUS_NAME,
+				dbus_interface=HEALTH_DEVICE_INTERFACE,
+				path_keyword="path",
+				member_keyword="member",
+				interface_keyword="interface")
+
+	try:
+		print("Entering main lopp, push Ctrl+C for finish")
+
+		mainloop = GObject.MainLoop()
+		mainloop.run()
+	except KeyboardInterrupt:
+		pass
+	finally:
+		print("Exiting, bye")
+
+hdp_manager = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+						HEALTH_MANAGER_INTERFACE)
+
+role = None
+while role == None:
+	print("Select 1. source or 2. sink: ",)
+	try:
+		sel = int(sys.stdin.readline())
+		if sel == 1:
+			role = "source"
+		elif sel == 2:
+			role = "sink"
+		else:
+			raise ValueError
+	except (TypeError, ValueError):
+		print("Wrong selection, try again: ",)
+	except KeyboardInterrupt:
+		sys.exit()
+
+dtype = None
+while dtype == None:
+	print("Select a data type: ",)
+	try:
+		sel = int(sys.stdin.readline())
+		if (sel < 0) or (sel > 65535):
+			raise ValueError
+		dtype = sel;
+	except (TypeError, ValueError):
+		print("Wrong selection, try again: ",)
+	except KeyboardInterrupt:
+		sys.exit()
+
+pref = None
+if role == "source":
+	while pref == None:
+		try:
+			print("Select a preferred data channel type 1.",)
+			print("reliable 2. streaming: ",)
+			sel = int(sys.stdin.readline())
+			if sel == 1:
+				pref = "reliable"
+			elif sel == 2:
+				pref = "streaming"
+			else:
+				raise ValueError
+
+		except (TypeError, ValueError):
+			print("Wrong selection, try again")
+		except KeyboardInterrupt:
+			sys.exit()
+
+	app_path = hdp_manager.CreateApplication({
+					"DataType": dbus.types.UInt16(dtype),
+					"Role": role,
+					"Description": "Test Source",
+					"ChannelType": pref})
+else:
+	app_path = hdp_manager.CreateApplication({
+					"DataType": dbus.types.UInt16(dtype),
+					"Description": "Test sink",
+					"Role": role})
+
+print("New application created:", app_path)
+
+con = None
+while con == None:
+	try:
+		print("Connect to a remote device (y/n)? ",)
+		sel = sys.stdin.readline()
+		if sel in ("y\n", "yes\n", "Y\n", "YES\n"):
+			con = True
+		elif sel in ("n\n", "no\n", "N\n", "NO\n"):
+			con = False
+		else:
+			print("Wrong selection, try again.")
+	except KeyboardInterrupt:
+		sys.exit()
+
+if not con:
+	enter_mainloop()
+	sys.exit()
+
+manager = dbus.Interface(bus.get_object(BUS_NAME, "/"),
+					"org.freedesktop.DBus.ObjectManager")
+
+objects = manager.GetManagedObjects()
+adapters = []
+
+for path, ifaces in objects.iteritems():
+	if ifaces.has_key(ADAPTER_INTERFACE):
+		adapters.append(path)
+
+i = 1
+for ad in adapters:
+	print("%d. %s" % (i, ad))
+	i = i + 1
+
+print("Select an adapter: ",)
+select = None
+while select == None:
+	try:
+		pos = int(sys.stdin.readline()) - 1
+		if pos < 0:
+			raise TypeError
+		select = adapters[pos]
+	except (TypeError, IndexError, ValueError):
+		print("Wrong selection, try again: ",)
+	except KeyboardInterrupt:
+		sys.exit()
+
+adapter = dbus.Interface(bus.get_object(BUS_NAME, select), ADAPTER_INTERFACE)
+
+devices = []
+for path, interfaces in objects.iteritems():
+	if "org.bluez.Device1" not in interfaces:
+		continue
+	properties = interfaces["org.bluez.Device1"]
+	if properties["Adapter"] != select:
+		continue;
+
+	if HEALTH_DEVICE_INTERFACE not in interfaces:
+		continue
+	devices.append(path)
+
+if len(devices) == 0:
+	print("No devices available")
+	sys.exit()
+
+i = 1
+for dev in devices:
+	print("%d. %s" % (i, dev))
+	i = i + 1
+
+print("Select a device: ",)
+select = None
+while select == None:
+	try:
+		pos = int(sys.stdin.readline()) - 1
+		if pos < 0:
+			raise TypeError
+		select = devices[pos]
+	except (TypeError, IndexError, ValueError):
+		print("Wrong selection, try again: ",)
+	except KeyboardInterrupt:
+		sys.exit()
+
+device = dbus.Interface(bus.get_object(BUS_NAME, select),
+					HEALTH_DEVICE_INTERFACE)
+
+echo = None
+while echo == None:
+	try:
+		print("Perform an echo (y/n)? ",)
+		sel = sys.stdin.readline()
+		if sel in ("y\n", "yes\n", "Y\n", "YES\n"):
+			echo = True
+		elif sel in ("n\n", "no\n", "N\n", "NO\n"):
+			echo = False
+		else:
+			print("Wrong selection, try again.")
+	except KeyboardInterrupt:
+		sys.exit()
+
+if echo:
+	if device.Echo():
+		print("Echo was ok")
+	else:
+		print("Echo war wrong, exiting")
+		sys.exit()
+
+print("Connecting to device %s" % (select))
+
+if role == "source":
+	chan = device.CreateChannel(app_path, "reliable")
+else:
+	chan = device.CreateChannel(app_path, "any")
+
+print(chan)
+
+enter_mainloop()
+
+hdp_manager.DestroyApplication(app_path)
diff --git a/bluez/repo/test/test-health-sink b/bluez/repo/test/test-health-sink
new file mode 100755
index 0000000..37e630a
--- /dev/null
+++ b/bluez/repo/test/test-health-sink
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+# -*- coding: utf-8 -*-
+
+import sys
+import dbus
+import dbus.service
+from dbus.mainloop.glib import DBusGMainLoop
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME = 'org.bluez'
+PATH = '/org/bluez'
+ADAPTER_INTERFACE = 'org.bluez.Adapter1'
+HEALTH_MANAGER_INTERFACE = 'org.bluez.HealthManager1'
+HEALTH_DEVICE_INTERFACE = 'org.bluez.HealthDevice1'
+
+DBusGMainLoop(set_as_default=True)
+loop = GObject.MainLoop()
+
+bus = dbus.SystemBus()
+
+type = 4103
+if len(sys.argv) > 1:
+	type = int(sys.argv[1])
+
+hdp_manager = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+						HEALTH_MANAGER_INTERFACE)
+app_path = hdp_manager.CreateApplication({"DataType": dbus.types.UInt16(type),
+					"Role": "sink"})
+
+print(app_path)
+
+manager = dbus.Interface(bus.get_object(BUS_NAME, "/"),
+					"org.freedesktop.DBus.ObjectManager")
+
+objects = manager.GetManagedObjects()
+adapters = []
+
+for path, ifaces in objects.iteritems():
+	if ifaces.has_key(ADAPTER_INTERFACE):
+		adapters.append(path)
+
+i = 1
+for ad in adapters:
+	print("%d. %s" % (i, ad))
+	i = i + 1
+
+print("Select an adapter: ",)
+select = None
+while select == None:
+	try:
+		pos = int(sys.stdin.readline()) - 1
+		if pos < 0:
+			raise TypeError
+		select = adapters[pos]
+	except (TypeError, IndexError, ValueError):
+		print("Wrong selection, try again: ",)
+	except KeyboardInterrupt:
+		sys.exit()
+
+adapter =  dbus.Interface(bus.get_object(BUS_NAME, select),
+						ADAPTER_INTERFACE)
+
+devices = []
+for path, interfaces in objects.iteritems():
+	if "org.bluez.Device1" not in interfaces:
+		continue
+	properties = interfaces["org.bluez.Device1"]
+	if properties["Adapter"] != select:
+		continue;
+
+	if HEALTH_DEVICE_INTERFACE not in interfaces:
+		continue
+	devices.append(path)
+
+if len(devices) == 0:
+	print("No devices available")
+	sys.exit()
+
+i = 1
+for dev in devices:
+	print("%d. %s" % (i, dev))
+	i = i + 1
+
+print("Select a device: ",)
+select = None
+while select == None:
+	try:
+		pos = int(sys.stdin.readline()) - 1
+		if pos < 0:
+			raise TypeError
+		select = devices[pos]
+	except (TypeError, IndexError, ValueError):
+		print("Wrong selection, try again: ",)
+	except KeyboardInterrupt:
+		sys.exit()
+
+print("Connecting to %s" % (select))
+device = dbus.Interface(bus.get_object(BUS_NAME, select),
+						HEALTH_DEVICE_INTERFACE)
+
+chan = device.CreateChannel(app_path, "Any")
+
+print(chan)
+
+print("Push Enter for finishing")
+sys.stdin.readline()
+
+hdp_manager.DestroyApplication(app_path)
diff --git a/bluez/repo/test/test-heartrate b/bluez/repo/test/test-heartrate
new file mode 100755
index 0000000..5e4e7e5
--- /dev/null
+++ b/bluez/repo/test/test-heartrate
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Heart Rate Monitor test script
+'''
+
+from optparse import OptionParser, make_option
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+BUS_NAME = 'org.bluez'
+HEARTRATE_MANAGER_INTERFACE = 'org.bluez.HeartRateManager1'
+HEARTRATE_WATCHER_INTERFACE = 'org.bluez.HeartRateWatcher1'
+HEARTRATE_INTERFACE = 'org.bluez.HeartRate1'
+
+class Watcher(dbus.service.Object):
+	@dbus.service.method(HEARTRATE_WATCHER_INTERFACE,
+					in_signature="oa{sv}", out_signature="")
+	def MeasurementReceived(self, device, measure):
+		print("Measurement received from %s" % device)
+		print("Value: ", measure["Value"])
+
+		if "Energy" in measure:
+			print("Energy: ", measure["Energy"])
+
+		if "Contact" in measure:
+			print("Contact: ", measure["Contact"])
+
+		if "Interval" in measure:
+			for i in measure["Interval"]:
+				print("Interval: ", i)
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	option_list = [
+		make_option("-i", "--adapter", action="store",
+			type="string", dest="adapter"),
+		make_option("-b", "--device", action="store",
+			type="string", dest="address"),
+		]
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	if not options.address:
+		print("Usage: %s [-i <adapter>] -b <bdaddr> [cmd]" % (sys.argv[0]))
+		print("Possible commands:")
+		print("\tReset")
+		sys.exit(1)
+
+	managed_objects = bluezutils.get_managed_objects()
+	adapter = bluezutils.find_adapter_in_objects(managed_objects,
+								options.adapter)
+	adapter_path = adapter.object_path
+
+	heartrateManager = dbus.Interface(bus.get_object(BUS_NAME,
+				adapter_path), HEARTRATE_MANAGER_INTERFACE)
+
+	path = "/test/watcher"
+	heartrateManager.RegisterWatcher(path)
+
+	device = bluezutils.find_device_in_objects(managed_objects,
+								options.address,
+								options.adapter)
+	device_path = device.object_path
+
+	heartrate = dbus.Interface(bus.get_object(BUS_NAME, device_path),
+							HEARTRATE_INTERFACE)
+
+	watcher = Watcher(bus, path)
+
+	dev_prop = dbus.Interface(bus.get_object(BUS_NAME, device_path),
+					"org.freedesktop.DBus.Properties")
+
+	properties = dev_prop.GetAll(HEARTRATE_INTERFACE)
+
+	if "Location" in properties:
+		print("Sensor location: %s" % properties["Location"])
+	else:
+		print("Sensor location is not supported")
+
+	if len(args) > 0:
+		if args[0] == "Reset":
+			reset_sup = properties["ResetSupported"]
+			if reset_sup:
+				heartrate.Reset()
+			else:
+				print("Reset not supported")
+				sys.exit(1)
+		else:
+			print("unknown command")
+			sys.exit(1)
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/test/test-hfp b/bluez/repo/test/test-hfp
new file mode 100755
index 0000000..a806043
--- /dev/null
+++ b/bluez/repo/test/test-hfp
@@ -0,0 +1,248 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import os
+from socket import SOCK_SEQPACKET, socket
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+mainloop = None
+audio_supported = True
+
+try:
+	from socket import AF_BLUETOOTH, BTPROTO_SCO
+except:
+	print("WARNING: python compiled without Bluetooth support"
+					" - audio will not be available")
+	audio_supported = False
+
+BUF_SIZE = 1024
+
+BDADDR_ANY = '00:00:00:00:00:00'
+
+HF_NREC			= 0x0001
+HF_3WAY			= 0x0002
+HF_CLI			= 0x0004
+HF_VOICE_RECOGNITION	= 0x0008
+HF_REMOTE_VOL		= 0x0010
+HF_ENHANCED_STATUS	= 0x0020
+HF_ENHANCED_CONTROL	= 0x0040
+HF_CODEC_NEGOTIATION	= 0x0080
+
+AG_3WAY			= 0x0001
+AG_NREC			= 0x0002
+AG_VOICE_RECOGNITION	= 0x0004
+AG_INBAND_RING		= 0x0008
+AG_VOICE_TAG		= 0x0010
+AG_REJECT_CALL		= 0x0020
+AG_ENHANCED_STATUS	= 0x0040
+AG_ENHANCED_CONTROL	= 0x0080
+AG_EXTENDED_RESULT	= 0x0100
+AG_CODEC_NEGOTIATION	= 0x0200
+
+HF_FEATURES = (HF_3WAY | HF_CLI | HF_VOICE_RECOGNITION |
+			HF_REMOTE_VOL | HF_ENHANCED_STATUS |
+			HF_ENHANCED_CONTROL | HF_CODEC_NEGOTIATION)
+
+AVAIL_CODECS = "1,2"
+
+class HfpConnection:
+	slc_complete = False
+	fd = None
+	io_id = 0
+	version = 0
+	features = 0
+	pending = None
+
+	def disconnect(self):
+		if (self.fd >= 0):
+			os.close(self.fd)
+			self.fd = -1
+			glib.source_remove(self.io_id)
+			self.io_id = 0
+
+	def slc_completed(self):
+		print("SLC establisment complete")
+		self.slc_complete = True
+
+	def slc_next_cmd(self, cmd):
+		if not cmd:
+			self.send_cmd("AT+BRSF=%u" % (HF_FEATURES))
+		elif (cmd.startswith("AT+BRSF")):
+			if (self.features & AG_CODEC_NEGOTIATION and
+					HF_FEATURES & HF_CODEC_NEGOTIATION):
+				self.send_cmd("AT+BAC=%s" % (AVAIL_CODECS))
+			else:
+				self.send_cmd("AT+CIND=?")
+		elif (cmd.startswith("AT+BAC")):
+			self.send_cmd("AT+CIND=?")
+		elif (cmd.startswith("AT+CIND=?")):
+			self.send_cmd("AT+CIND?")
+		elif (cmd.startswith("AT+CIND?")):
+			self.send_cmd("AT+CMER=3,0,0,1")
+		elif (cmd.startswith("AT+CMER=")):
+			if (HF_FEATURES & HF_3WAY and self.features & AG_3WAY):
+				self.send_cmd("AT+CHLD=?")
+			else:
+				self.slc_completed()
+		elif (cmd.startswith("AT+CHLD=?")):
+			self.slc_completed()
+		else:
+			print("Unknown SLC command completed: %s" % (cmd))
+
+	def io_cb(self, fd, cond):
+		buf = os.read(fd, BUF_SIZE)
+		buf = buf.strip()
+
+		print("Received: %s" % (buf))
+
+		if (buf == "OK" or buf == "ERROR"):
+			cmd = self.pending
+			self.pending = None
+
+			if (not self.slc_complete):
+				self.slc_next_cmd(cmd)
+
+			return True
+
+		parts = buf.split(':')
+
+		if (parts[0] == "+BRSF"):
+			self.features = int(parts[1])
+
+		return True
+
+	def send_cmd(self, cmd):
+		if (self.pending):
+			print("ERROR: Another command is pending")
+			return
+
+		print("Sending: %s" % (cmd))
+
+		os.write(self.fd, cmd + "\r\n")
+		self.pending = cmd
+
+	def __init__(self, fd, version, features):
+		self.fd = fd
+		self.version = version
+		self.features = features
+
+		print("Version 0x%04x Features 0x%04x" % (version, features))
+
+		self.io_id = glib.io_add_watch(fd, glib.IO_IN, self.io_cb)
+
+		self.slc_next_cmd(None)
+
+class HfpProfile(dbus.service.Object):
+	sco_socket = None
+	io_id = 0
+	conns = {}
+
+	def sco_cb(self, sock, cond):
+		(sco, peer) = sock.accept()
+		print("New SCO connection from %s" % (peer))
+
+	def init_sco(self, sock):
+		self.sco_socket = sock
+		self.io_id = glib.io_add_watch(sock, glib.IO_IN, self.sco_cb)
+
+	def __init__(self, bus, path, sco):
+		dbus.service.Object.__init__(self, bus, path)
+
+		if sco:
+			self.init_sco(sco)
+
+	@dbus.service.method("org.bluez.Profile1",
+					in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		mainloop.quit()
+
+	@dbus.service.method("org.bluez.Profile1",
+					in_signature="", out_signature="")
+	def Cancel(self):
+		print("Cancel")
+
+	@dbus.service.method("org.bluez.Profile1",
+				in_signature="o", out_signature="")
+	def RequestDisconnection(self, path):
+		conn = self.conns.pop(path)
+		conn.disconnect()
+
+	@dbus.service.method("org.bluez.Profile1",
+				in_signature="oha{sv}", out_signature="")
+	def NewConnection(self, path, fd, properties):
+		fd = fd.take()
+		version = 0x0105
+		features = 0
+		print("NewConnection(%s, %d)" % (path, fd))
+		for key in properties.keys():
+			if key == "Version":
+				version = properties[key]
+			elif key == "Features":
+				features = properties[key]
+
+		conn = HfpConnection(fd, version, features)
+
+		self.conns[path] = conn
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	manager = dbus.Interface(bus.get_object("org.bluez",
+				"/org/bluez"), "org.bluez.ProfileManager1")
+
+	option_list = [
+			make_option("-p", "--path", action="store",
+					type="string", dest="path",
+					default="/bluez/test/hfp"),
+			make_option("-n", "--name", action="store",
+					type="string", dest="name",
+					default=None),
+			make_option("-C", "--channel", action="store",
+					type="int", dest="channel",
+					default=None),
+			]
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	mainloop = GObject.MainLoop()
+
+	opts = {
+			"Version" : dbus.UInt16(0x0106),
+			"Features" : dbus.UInt16(HF_FEATURES),
+		}
+
+	if (options.name):
+		opts["Name"] = options.name
+
+	if (options.channel is not None):
+		opts["Channel"] = dbus.UInt16(options.channel)
+
+	if audio_supported:
+		sco = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)
+		sco.bind(BDADDR_ANY)
+		sco.listen(1)
+	else:
+		sco = None
+
+	profile = HfpProfile(bus, options.path, sco)
+
+	manager.RegisterProfile(options.path, "hfp-hf", opts)
+
+	print("Profile registered - waiting for connections")
+
+	mainloop.run()
diff --git a/bluez/repo/test/test-manager b/bluez/repo/test/test-manager
new file mode 100755
index 0000000..4f5994f
--- /dev/null
+++ b/bluez/repo/test/test-manager
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+def interfaces_added(path, interfaces):
+	if interfaces.get("org.bluez.Adapter1") != None:
+		print("Adapter with path %s added" % (path))
+
+def interfaces_removed(path, interfaces):
+	if "org.bluez.Adapter1" in interfaces:
+		print("Adapter with path %s removed" % (path))
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	bus.add_signal_receiver(interfaces_added, bus_name="org.bluez",
+			dbus_interface="org.freedesktop.DBus.ObjectManager",
+			signal_name="InterfacesAdded")
+
+	bus.add_signal_receiver(interfaces_removed, bus_name="org.bluez",
+			dbus_interface="org.freedesktop.DBus.ObjectManager",
+			signal_name="InterfacesRemoved")
+
+	try:
+		path = bluezutils.find_adapter().object_path
+		print("Adapter found at path %s" % (path))
+	except:
+		print("No adapter found")
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/test/test-nap b/bluez/repo/test/test-nap
new file mode 100755
index 0000000..ab67a75
--- /dev/null
+++ b/bluez/repo/test/test-nap
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import sys
+import time
+import dbus
+import bluezutils
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+bus = dbus.SystemBus()
+
+option_list = [
+		make_option("-i", "--device", action="store",
+				type="string", dest="dev_id"),
+		]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+adapter_path = bluezutils.find_adapter(options.dev_id).object_path
+server = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+						"org.bluez.NetworkServer1")
+
+service = "nap"
+
+if (len(args) < 1):
+	bridge = "tether"
+else:
+	bridge = args[0]
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+mainloop = GObject.MainLoop()
+
+server.Register(service, bridge)
+
+print("Server for %s registered for %s" % (service, bridge))
+
+print("Press CTRL-C to disconnect")
+
+mainloop.run()
diff --git a/bluez/repo/test/test-network b/bluez/repo/test/test-network
new file mode 100755
index 0000000..6f09486
--- /dev/null
+++ b/bluez/repo/test/test-network
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import sys
+import time
+import dbus
+import bluezutils
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+						"org.bluez.Manager")
+
+option_list = [
+		make_option("-i", "--device", action="store",
+				type="string", dest="dev_id"),
+		]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if (len(args) < 1):
+	print("Usage: %s <address> [service]" % (sys.argv[0]))
+	sys.exit(1)
+
+# Fix-up in case of "connect" invocation that other scripts use
+if args[0] == "connect":
+	del args[:1]
+
+if (len(args) < 2):
+	service = "panu"
+else:
+	service = args[1]
+
+device = bluezutils.find_device(args[0], options.dev_id)
+
+network = dbus.Interface(bus.get_object("org.bluez", device.object_path),
+						"org.bluez.Network1")
+
+iface = network.Connect(service)
+
+print("Connected to %s service %s, interface %s" % (args[0], service, iface))
+
+print("Press CTRL-C to disconnect")
+
+try:
+	time.sleep(1000)
+	print("Terminating connection")
+except:
+	pass
+
+network.Disconnect()
diff --git a/bluez/repo/test/test-profile b/bluez/repo/test/test-profile
new file mode 100755
index 0000000..2791580
--- /dev/null
+++ b/bluez/repo/test/test-profile
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser, make_option
+import os
+import sys
+import uuid
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+class Profile(dbus.service.Object):
+	fd = -1
+
+	@dbus.service.method("org.bluez.Profile1",
+					in_signature="", out_signature="")
+	def Release(self):
+		print("Release")
+		mainloop.quit()
+
+	@dbus.service.method("org.bluez.Profile1",
+					in_signature="", out_signature="")
+	def Cancel(self):
+		print("Cancel")
+
+	@dbus.service.method("org.bluez.Profile1",
+				in_signature="oha{sv}", out_signature="")
+	def NewConnection(self, path, fd, properties):
+		self.fd = fd.take()
+		print("NewConnection(%s, %d)" % (path, self.fd))
+		for key in properties.keys():
+			if key == "Version" or key == "Features":
+				print("  %s = 0x%04x" % (key, properties[key]))
+			else:
+				print("  %s = %s" % (key, properties[key]))
+
+	@dbus.service.method("org.bluez.Profile1",
+				in_signature="o", out_signature="")
+	def RequestDisconnection(self, path):
+		print("RequestDisconnection(%s)" % (path))
+
+		if (self.fd > 0):
+			os.close(self.fd)
+			self.fd = -1
+
+if __name__ == '__main__':
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	manager = dbus.Interface(bus.get_object("org.bluez",
+				"/org/bluez"), "org.bluez.ProfileManager1")
+
+	option_list = [
+			make_option("-u", "--uuid", action="store",
+					type="string", dest="uuid",
+					default=None),
+			make_option("-p", "--path", action="store",
+					type="string", dest="path",
+					default="/foo/bar/profile"),
+			make_option("-n", "--name", action="store",
+					type="string", dest="name",
+					default=None),
+			make_option("-s", "--server",
+					action="store_const",
+					const="server", dest="role"),
+			make_option("-c", "--client",
+					action="store_const",
+					const="client", dest="role"),
+			make_option("-a", "--auto-connect",
+					action="store_true",
+					dest="auto_connect", default=False),
+			make_option("-P", "--PSM", action="store",
+					type="int", dest="psm",
+					default=None),
+			make_option("-C", "--channel", action="store",
+					type="int", dest="channel",
+					default=None),
+			make_option("-r", "--record", action="store",
+					type="string", dest="record",
+					default=None),
+			make_option("-S", "--service", action="store",
+					type="string", dest="service",
+					default=None),
+			]
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	profile = Profile(bus, options.path)
+
+	mainloop = GObject.MainLoop()
+
+	opts = {
+			"AutoConnect" :	options.auto_connect,
+		}
+
+	if (options.name):
+		opts["Name"] = options.name
+
+	if (options.role):
+		opts["Role"] = options.role
+
+	if (options.psm is not None):
+		opts["PSM"] = dbus.UInt16(options.psm)
+
+	if (options.channel is not None):
+		opts["Channel"] = dbus.UInt16(options.channel)
+
+	if (options.record):
+		opts["ServiceRecord"] = options.record
+
+	if (options.service):
+		opts["Service"] = options.service
+
+	if not options.uuid:
+		options.uuid = str(uuid.uuid4())
+
+	manager.RegisterProfile(options.path, options.uuid, opts)
+
+	mainloop.run()
diff --git a/bluez/repo/test/test-proximity b/bluez/repo/test/test-proximity
new file mode 100755
index 0000000..66b7bc2
--- /dev/null
+++ b/bluez/repo/test/test-proximity
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Proximity Monitor test script
+'''
+
+from optparse import OptionParser, make_option
+import sys
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+BUS_NAME = 'org.bluez'
+PROXIMITY_MONITOR_INTERFACE = 'org.bluez.ProximityMonitor1'
+
+def properties_changed(interface, changed, invalidated):
+	if interface != PROXIMITY_MONITOR_INTERFACE:
+		return
+
+	for name, value in changed.iteritems():
+		print("Property %s changed:  %s" % (name, str(value)))
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	option_list = [
+		make_option("-i", "--adapter", action="store",
+			type="string", dest="dev_id"),
+		make_option("-b", "--device", action="store",
+			type="string", dest="address"),
+
+		]
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	if (len(args) < 1):
+		print("Usage: %s <command>" % (sys.argv[0]))
+		print("")
+		print("  -b MAC LinkLossAlertLevel <none|mild|high>")
+		print("  -b MAC ImmediateAlertLevel <none|mild|high>")
+		sys.exit(1)
+
+	device = bluezutils.find_device(options.address, options.dev_id)
+	device_path = device.object_path
+
+	bus.add_signal_receiver(properties_changed, bus_name=BUS_NAME,
+			path=device_path,
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged")
+
+	proximity = dbus.Interface(bus.get_object(BUS_NAME, device_path),
+						PROXIMITY_MONITOR_INTERFACE)
+
+	device_prop = dbus.Interface(bus.get_object(BUS_NAME, device_path),
+					"org.freedesktop.DBus.Properties")
+
+	print("Proximity SetProperty('%s', '%s')" % (args[0], args[1]))
+	device_prop.Set(PROXIMITY_MONITOR_INTERFACE, args[0], args[1])
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/test/test-sap-server b/bluez/repo/test/test-sap-server
new file mode 100755
index 0000000..ff178af
--- /dev/null
+++ b/bluez/repo/test/test-sap-server
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from sap_client import *
+import time
+import sys
+
+def connect_disconnect_by_client(sap):
+
+    print("[Test] Connect - Disconnect by client \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if sap.proc_disconnectByClient():
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+
+def connect_disconnect_by_server_gracefully(sap, timeout=0):
+
+    print("[Test] Connect - Disconnect by server with timer \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if sap.proc_disconnectByServer(timeout):
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+
+def connect_txAPDU_disconnect_by_client(sap):
+
+    print("[Test] Connect - TX APDU - Disconnect by client \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if not sap.proc_transferAPDU():
+                print("NOT OK 1")
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print("NOT OK 2")
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print("NOT OK 3")
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print("NOT OK 4")
+                return 1
+
+            if sap.proc_disconnectByClient():
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+def connect_rfcomm_only_and_wait_for_close_by_server(sap):
+
+    print("[Test] Connect rfcomm only  - Disconnect by server timeout \n")
+
+    if not sap.isConnected():
+       sap.connect()
+
+    time.sleep(40)
+    print("OK")
+
+def power_sim_off_on(sap):
+
+    print("[Test] Powe sim off \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if not sap.proc_resetSim():
+                print("NOT OK")
+                return 1
+
+            if not sap.proc_powerSimOff():
+                print("NOT OK")
+                return 1
+
+            if not sap.proc_powerSimOn():
+                print("NOT OK")
+                return 1
+
+            if sap.proc_disconnectByClient():
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+
+if __name__ == "__main__":
+
+    host = None  # server bd_addr
+    port = 8  # sap server port
+
+    if (len(sys.argv) < 2):
+        print("Usage: %s <address> [port]" % (sys.argv[0]))
+        sys.exit(1)
+
+    host = sys.argv[1]
+
+    if (len(sys.argv) == 3):
+        port = sys.argv[2]
+
+    try:
+        s = SAPClient(host, port)
+    except BluetoothError as e:
+        print("Error: " + str(e))
+        sys.exit(1)
+
+    connect_disconnect_by_client(s)
+    connect_disconnect_by_server_gracefully(s)
+    connect_disconnect_by_server_gracefully(s, 40)  #  wait 40 sec for srv to close rfcomm sock
+    connect_rfcomm_only_and_wait_for_close_by_server(s)
+    connect_txAPDU_disconnect_by_client(s)
+    power_sim_off_on(s)
diff --git a/bluez/repo/test/test-thermometer b/bluez/repo/test/test-thermometer
new file mode 100755
index 0000000..7e67c23
--- /dev/null
+++ b/bluez/repo/test/test-thermometer
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Thermometer test script
+'''
+
+from optparse import OptionParser, make_option
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
+
+BUS_NAME = 'org.bluez'
+THERMOMETER_MANAGER_INTERFACE = 'org.bluez.ThermometerManager1'
+THERMOMETER_WATCHER_INTERFACE = 'org.bluez.ThermometerWatcher1'
+THERMOMETER_INTERFACE = 'org.bluez.Thermometer1'
+
+class Watcher(dbus.service.Object):
+	@dbus.service.method(THERMOMETER_WATCHER_INTERFACE,
+					in_signature="oa{sv}", out_signature="")
+	def MeasurementReceived(self, device, measure):
+		print("%s measurement received from %s" % (measure["Measurement"], device))
+		print("Exponent: ", measure["Exponent"])
+		print("Mantissa: ", measure["Mantissa"])
+		print("Unit: ", measure["Unit"])
+
+		if "Time" in measure:
+			print("Time: ", measure["Time"])
+
+		if "Type" in measure:
+			print("Type: ", measure["Type"])
+
+def properties_changed(interface, changed, invalidated):
+	if interface != THERMOMETER_INTERFACE:
+		return
+	for name, value in changed.iteritems():
+		print("Property %s changed:  %s" % (name, str(value)))
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+	bus = dbus.SystemBus()
+
+	option_list = [
+		make_option("-i", "--adapter", action="store",
+			type="string", dest="adapter"),
+		make_option("-b", "--device", action="store",
+			type="string", dest="address"),
+		]
+
+	parser = OptionParser(option_list=option_list)
+
+	(options, args) = parser.parse_args()
+
+	if not options.address:
+		print("Usage: %s [-i <adapter>] -b <bdaddr> [command]" % (sys.argv[0]))
+		print("Possible commands:")
+		print("\tEnableIntermediateMeasurement")
+		sys.exit(1)
+
+	managed_objects = bluezutils.get_managed_objects()
+	adapter = bluezutils.find_adapter_in_objects(managed_objects,
+								options.adapter)
+	adapter_path = adapter.object_path
+
+	thermometer_manager = dbus.Interface(bus.get_object(BUS_NAME,
+				adapter_path), THERMOMETER_MANAGER_INTERFACE)
+
+	device = bluezutils.find_device_in_objects(managed_objects,
+								options.address,
+								options.adapter)
+	device_path = device.object_path
+
+	bus.add_signal_receiver(properties_changed, bus_name=BUS_NAME,
+			path=device_path,
+			dbus_interface="org.freedesktop.DBus.Properties",
+			signal_name="PropertiesChanged")
+
+	path = "/test/watcher"
+	watcher = Watcher(bus, path)
+
+	thermometer_manager.RegisterWatcher(path)
+
+	if len(args) > 0:
+		if args[0] == "EnableIntermediateMeasurement":
+			thermometer_manager.EnableIntermediateMeasurement(path)
+		else:
+			print("unknown command")
+			sys.exit(1)
+
+	mainloop = GObject.MainLoop()
+	mainloop.run()
diff --git a/bluez/repo/tools/.DS_Store b/bluez/repo/tools/.DS_Store
new file mode 100644
index 0000000..5008ddf
--- /dev/null
+++ b/bluez/repo/tools/.DS_Store
Binary files differ
diff --git a/bluez/repo/tools/._.DS_Store b/bluez/repo/tools/._.DS_Store
new file mode 100644
index 0000000..09fa6bd
--- /dev/null
+++ b/bluez/repo/tools/._.DS_Store
Binary files differ
diff --git a/bluez/repo/tools/._3dsp.c b/bluez/repo/tools/._3dsp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._3dsp.c
Binary files differ
diff --git a/bluez/repo/tools/._amptest.c b/bluez/repo/tools/._amptest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._amptest.c
Binary files differ
diff --git a/bluez/repo/tools/._avinfo.c b/bluez/repo/tools/._avinfo.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._avinfo.c
Binary files differ
diff --git a/bluez/repo/tools/._avtest.c b/bluez/repo/tools/._avtest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._avtest.c
Binary files differ
diff --git a/bluez/repo/tools/._bccmd.1 b/bluez/repo/tools/._bccmd.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bccmd.1
Binary files differ
diff --git a/bluez/repo/tools/._bccmd.c b/bluez/repo/tools/._bccmd.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bccmd.c
Binary files differ
diff --git a/bluez/repo/tools/._bdaddr.1 b/bluez/repo/tools/._bdaddr.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bdaddr.1
Binary files differ
diff --git a/bluez/repo/tools/._bdaddr.c b/bluez/repo/tools/._bdaddr.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bdaddr.c
Binary files differ
diff --git a/bluez/repo/tools/._bluetooth-player.c b/bluez/repo/tools/._bluetooth-player.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bluetooth-player.c
Binary files differ
diff --git a/bluez/repo/tools/._bnep-tester.c b/bluez/repo/tools/._bnep-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bnep-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._bneptest.c b/bluez/repo/tools/._bneptest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._bneptest.c
Binary files differ
diff --git a/bluez/repo/tools/._btattach.1 b/bluez/repo/tools/._btattach.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btattach.1
Binary files differ
diff --git a/bluez/repo/tools/._btattach.c b/bluez/repo/tools/._btattach.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btattach.c
Binary files differ
diff --git a/bluez/repo/tools/._btgatt-client.c b/bluez/repo/tools/._btgatt-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btgatt-client.c
Binary files differ
diff --git a/bluez/repo/tools/._btgatt-server.c b/bluez/repo/tools/._btgatt-server.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btgatt-server.c
Binary files differ
diff --git a/bluez/repo/tools/._btinfo.c b/bluez/repo/tools/._btinfo.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btinfo.c
Binary files differ
diff --git a/bluez/repo/tools/._btiotest.c b/bluez/repo/tools/._btiotest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btiotest.c
Binary files differ
diff --git a/bluez/repo/tools/._btmgmt.c b/bluez/repo/tools/._btmgmt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btmgmt.c
Binary files differ
diff --git a/bluez/repo/tools/._btproxy.c b/bluez/repo/tools/._btproxy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btproxy.c
Binary files differ
diff --git a/bluez/repo/tools/._btsnoop.c b/bluez/repo/tools/._btsnoop.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._btsnoop.c
Binary files differ
diff --git a/bluez/repo/tools/._check-selftest.c b/bluez/repo/tools/._check-selftest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._check-selftest.c
Binary files differ
diff --git a/bluez/repo/tools/._ciptool.1 b/bluez/repo/tools/._ciptool.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._ciptool.1
Binary files differ
diff --git a/bluez/repo/tools/._ciptool.c b/bluez/repo/tools/._ciptool.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._ciptool.c
Binary files differ
diff --git a/bluez/repo/tools/._cltest.c b/bluez/repo/tools/._cltest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._cltest.c
Binary files differ
diff --git a/bluez/repo/tools/._create-image.c b/bluez/repo/tools/._create-image.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._create-image.c
Binary files differ
diff --git a/bluez/repo/tools/._csr.c b/bluez/repo/tools/._csr.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr.c
Binary files differ
diff --git a/bluez/repo/tools/._csr.h b/bluez/repo/tools/._csr.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr.h
Binary files differ
diff --git a/bluez/repo/tools/._csr_3wire.c b/bluez/repo/tools/._csr_3wire.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr_3wire.c
Binary files differ
diff --git a/bluez/repo/tools/._csr_bcsp.c b/bluez/repo/tools/._csr_bcsp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr_bcsp.c
Binary files differ
diff --git a/bluez/repo/tools/._csr_h4.c b/bluez/repo/tools/._csr_h4.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr_h4.c
Binary files differ
diff --git a/bluez/repo/tools/._csr_hci.c b/bluez/repo/tools/._csr_hci.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr_hci.c
Binary files differ
diff --git a/bluez/repo/tools/._csr_usb.c b/bluez/repo/tools/._csr_usb.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._csr_usb.c
Binary files differ
diff --git a/bluez/repo/tools/._eddystone.c b/bluez/repo/tools/._eddystone.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._eddystone.c
Binary files differ
diff --git a/bluez/repo/tools/._example.psr b/bluez/repo/tools/._example.psr
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._example.psr
Binary files differ
diff --git a/bluez/repo/tools/._gap-tester.c b/bluez/repo/tools/._gap-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._gap-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._gatt-service.c b/bluez/repo/tools/._gatt-service.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._gatt-service.c
Binary files differ
diff --git a/bluez/repo/tools/._hci-tester.c b/bluez/repo/tools/._hci-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hci-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach.1 b/bluez/repo/tools/._hciattach.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach.1
Binary files differ
diff --git a/bluez/repo/tools/._hciattach.c b/bluez/repo/tools/._hciattach.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach.h b/bluez/repo/tools/._hciattach.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach.h
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_ath3k.c b/bluez/repo/tools/._hciattach_ath3k.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_ath3k.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_bcm43xx.c b/bluez/repo/tools/._hciattach_bcm43xx.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_bcm43xx.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_intel.c b/bluez/repo/tools/._hciattach_intel.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_intel.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_qualcomm.c b/bluez/repo/tools/._hciattach_qualcomm.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_qualcomm.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_st.c b/bluez/repo/tools/._hciattach_st.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_st.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_ti.c b/bluez/repo/tools/._hciattach_ti.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_ti.c
Binary files differ
diff --git a/bluez/repo/tools/._hciattach_tialt.c b/bluez/repo/tools/._hciattach_tialt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciattach_tialt.c
Binary files differ
diff --git a/bluez/repo/tools/._hciconfig.1 b/bluez/repo/tools/._hciconfig.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciconfig.1
Binary files differ
diff --git a/bluez/repo/tools/._hciconfig.c b/bluez/repo/tools/._hciconfig.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hciconfig.c
Binary files differ
diff --git a/bluez/repo/tools/._hcidump.1 b/bluez/repo/tools/._hcidump.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hcidump.1
Binary files differ
diff --git a/bluez/repo/tools/._hcidump.c b/bluez/repo/tools/._hcidump.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hcidump.c
Binary files differ
diff --git a/bluez/repo/tools/._hcieventmask.c b/bluez/repo/tools/._hcieventmask.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hcieventmask.c
Binary files differ
diff --git a/bluez/repo/tools/._hcisecfilter.c b/bluez/repo/tools/._hcisecfilter.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hcisecfilter.c
Binary files differ
diff --git a/bluez/repo/tools/._hcitool.1 b/bluez/repo/tools/._hcitool.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hcitool.1
Binary files differ
diff --git a/bluez/repo/tools/._hcitool.c b/bluez/repo/tools/._hcitool.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hcitool.c
Binary files differ
diff --git a/bluez/repo/tools/._hex2hcd.c b/bluez/repo/tools/._hex2hcd.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hex2hcd.c
Binary files differ
diff --git a/bluez/repo/tools/._hid2hci.1 b/bluez/repo/tools/._hid2hci.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hid2hci.1
Binary files differ
diff --git a/bluez/repo/tools/._hid2hci.c b/bluez/repo/tools/._hid2hci.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hid2hci.c
Binary files differ
diff --git a/bluez/repo/tools/._hid2hci.rules b/bluez/repo/tools/._hid2hci.rules
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hid2hci.rules
Binary files differ
diff --git a/bluez/repo/tools/._hwdb.c b/bluez/repo/tools/._hwdb.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._hwdb.c
Binary files differ
diff --git a/bluez/repo/tools/._ibeacon.c b/bluez/repo/tools/._ibeacon.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._ibeacon.c
Binary files differ
diff --git a/bluez/repo/tools/._l2cap-tester.c b/bluez/repo/tools/._l2cap-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._l2cap-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._l2ping.1 b/bluez/repo/tools/._l2ping.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._l2ping.1
Binary files differ
diff --git a/bluez/repo/tools/._l2ping.c b/bluez/repo/tools/._l2ping.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._l2ping.c
Binary files differ
diff --git a/bluez/repo/tools/._l2test.c b/bluez/repo/tools/._l2test.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._l2test.c
Binary files differ
diff --git a/bluez/repo/tools/._magic.btsnoop b/bluez/repo/tools/._magic.btsnoop
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._magic.btsnoop
Binary files differ
diff --git a/bluez/repo/tools/._mcaptest.c b/bluez/repo/tools/._mcaptest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._mcaptest.c
Binary files differ
diff --git a/bluez/repo/tools/._mgmt-tester.c b/bluez/repo/tools/._mgmt-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._mgmt-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._mpris-proxy.c b/bluez/repo/tools/._mpris-proxy.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._mpris-proxy.c
Binary files differ
diff --git a/bluez/repo/tools/._nokfw.c b/bluez/repo/tools/._nokfw.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._nokfw.c
Binary files differ
diff --git a/bluez/repo/tools/._obex-client-tool.c b/bluez/repo/tools/._obex-client-tool.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._obex-client-tool.c
Binary files differ
diff --git a/bluez/repo/tools/._obex-server-tool.c b/bluez/repo/tools/._obex-server-tool.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._obex-server-tool.c
Binary files differ
diff --git a/bluez/repo/tools/._obexctl.c b/bluez/repo/tools/._obexctl.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._obexctl.c
Binary files differ
diff --git a/bluez/repo/tools/._oobtest.c b/bluez/repo/tools/._oobtest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._oobtest.c
Binary files differ
diff --git a/bluez/repo/tools/._parse_companies.pl b/bluez/repo/tools/._parse_companies.pl
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._parse_companies.pl
Binary files differ
diff --git a/bluez/repo/tools/._parser b/bluez/repo/tools/._parser
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._parser
Binary files differ
diff --git a/bluez/repo/tools/._rctest.1 b/bluez/repo/tools/._rctest.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._rctest.1
Binary files differ
diff --git a/bluez/repo/tools/._rctest.c b/bluez/repo/tools/._rctest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._rctest.c
Binary files differ
diff --git a/bluez/repo/tools/._rfcomm-tester.c b/bluez/repo/tools/._rfcomm-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._rfcomm-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._rfcomm.1 b/bluez/repo/tools/._rfcomm.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._rfcomm.1
Binary files differ
diff --git a/bluez/repo/tools/._rfcomm.c b/bluez/repo/tools/._rfcomm.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._rfcomm.c
Binary files differ
diff --git a/bluez/repo/tools/._sco-tester.c b/bluez/repo/tools/._sco-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._sco-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._scotest.c b/bluez/repo/tools/._scotest.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._scotest.c
Binary files differ
diff --git a/bluez/repo/tools/._sdptool.1 b/bluez/repo/tools/._sdptool.1
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._sdptool.1
Binary files differ
diff --git a/bluez/repo/tools/._sdptool.c b/bluez/repo/tools/._sdptool.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._sdptool.c
Binary files differ
diff --git a/bluez/repo/tools/._seq2bseq.c b/bluez/repo/tools/._seq2bseq.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._seq2bseq.c
Binary files differ
diff --git a/bluez/repo/tools/._smp-tester.c b/bluez/repo/tools/._smp-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._smp-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._test-runner.c b/bluez/repo/tools/._test-runner.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._test-runner.c
Binary files differ
diff --git a/bluez/repo/tools/._ubcsp.c b/bluez/repo/tools/._ubcsp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._ubcsp.c
Binary files differ
diff --git a/bluez/repo/tools/._ubcsp.h b/bluez/repo/tools/._ubcsp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._ubcsp.h
Binary files differ
diff --git a/bluez/repo/tools/._update_compids.sh b/bluez/repo/tools/._update_compids.sh
new file mode 100755
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._update_compids.sh
Binary files differ
diff --git a/bluez/repo/tools/._userchan-tester.c b/bluez/repo/tools/._userchan-tester.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._userchan-tester.c
Binary files differ
diff --git a/bluez/repo/tools/._valgrind.supp b/bluez/repo/tools/._valgrind.supp
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/._valgrind.supp
Binary files differ
diff --git a/bluez/repo/tools/3dsp.c b/bluez/repo/tools/3dsp.c
new file mode 100644
index 0000000..686fe13
--- /dev/null
+++ b/bluez/repo/tools/3dsp.c
@@ -0,0 +1,662 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+#define LT_ADDR 0x01
+#define PKT_TYPE 0x0008		/* 0x0008 = EDR + DM1, 0xff1e = BR only */
+#define SERVICE_DATA 0x00
+
+struct broadcast_message {
+	uint32_t frame_sync_instant;
+	uint16_t bluetooth_clock_phase;
+	uint16_t left_open_offset;
+	uint16_t left_close_offset;
+	uint16_t right_open_offset;
+	uint16_t right_close_offset;
+	uint16_t frame_sync_period;
+	uint8_t  frame_sync_period_fraction;
+} __attribute__ ((packed));
+
+struct brcm_evt_sync_train_received {
+	uint8_t  status;
+	uint8_t  bdaddr[6];
+	uint32_t offset;
+	uint8_t  map[10];
+	uint8_t  service_data;
+	uint8_t  lt_addr;
+	uint32_t instant;
+	uint16_t interval;
+} __attribute__ ((packed));
+
+static struct bt_hci *hci_dev;
+
+static bool reset_on_init = false;
+static bool reset_on_shutdown = false;
+
+static bool shutdown_timeout(void *user_data)
+{
+	mainloop_quit();
+
+	return false;
+}
+
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
+{
+	unsigned int id = PTR_TO_UINT(user_data);
+
+	timeout_remove(id);
+	mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+	unsigned int id;
+
+	bt_hci_flush(hci_dev);
+
+	if (reset_on_shutdown) {
+		id = timeout_add(5000, shutdown_timeout, NULL, NULL);
+
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+				shutdown_complete, UINT_TO_PTR(id), NULL);
+	} else
+		mainloop_quit();
+}
+
+static void inquiry_started(const void *data, uint8_t size, void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		printf("Failed to search for 3D display\n");
+		shutdown_device();
+		return;
+	}
+
+	printf("Searching for 3D display\n");
+}
+
+static void start_inquiry(void)
+{
+	struct bt_hci_cmd_inquiry cmd;
+
+	cmd.lap[0] = 0x33;
+	cmd.lap[1] = 0x8b;
+	cmd.lap[2] = 0x9e;
+	cmd.length = 0x08;
+	cmd.num_resp = 0x00;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd),
+						inquiry_started, NULL, NULL);
+}
+
+static void set_slave_broadcast_receive(const void *data, uint8_t size,
+							void *user_data)
+{
+	printf("Slave broadcast receiption enabled\n");
+}
+
+static void sync_train_received(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_evt_sync_train_received *evt = data;
+	struct bt_hci_cmd_set_slave_broadcast_receive cmd;
+
+	if (evt->status) {
+		printf("Failed to synchronize with 3D display\n");
+		start_inquiry();
+		return;
+	}
+
+	if (evt->lt_addr != LT_ADDR) {
+		printf("Ignoring synchronization for non 3D display\n");
+		return;
+	}
+
+	cmd.enable = 0x01;
+	memcpy(cmd.bdaddr, evt->bdaddr, 6);
+	cmd.lt_addr = evt->lt_addr;
+	cmd.interval = evt->interval;
+	cmd.offset = evt->offset;
+	cmd.instant = evt->instant;
+	cmd.timeout = cpu_to_le16(0xfffe);
+	cmd.accuracy = 250;
+	cmd.skip = 20;
+	cmd.pkt_type = cpu_to_le16(PKT_TYPE);
+	memcpy(cmd.map, evt->map, 10);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE,
+				&cmd, sizeof(cmd),
+				set_slave_broadcast_receive, NULL, NULL);
+}
+
+static void brcm_sync_train_received(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct brcm_evt_sync_train_received *evt = data;
+	struct bt_hci_cmd_set_slave_broadcast_receive cmd;
+
+	if (evt->status) {
+		printf("Failed to synchronize with 3D display\n");
+		start_inquiry();
+		return;
+	}
+
+	if (evt->lt_addr != LT_ADDR) {
+		printf("Ignoring synchronization for non 3D display\n");
+		return;
+	}
+
+	cmd.enable = 0x01;
+	memcpy(cmd.bdaddr, evt->bdaddr, 6);
+	cmd.lt_addr = evt->lt_addr;
+	cmd.interval = evt->interval;
+	cmd.offset = evt->offset;
+	cmd.instant = evt->instant;
+	cmd.timeout = cpu_to_le16(0xfffe);
+	cmd.accuracy = 250;
+	cmd.skip = 20;
+	cmd.pkt_type = cpu_to_le16(PKT_TYPE);
+	memcpy(cmd.map, evt->map, 10);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE,
+				&cmd, sizeof(cmd),
+				set_slave_broadcast_receive, NULL, NULL);
+}
+
+static void truncated_page_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_evt_truncated_page_complete *evt = data;
+	struct bt_hci_cmd_receive_sync_train cmd;
+
+	if (evt->status) {
+		printf("Failed to contact 3D display\n");
+		shutdown_device();
+		return;
+	}
+
+	printf("Attempt to synchronize with 3D display\n");
+
+	memcpy(cmd.bdaddr, evt->bdaddr, 6);
+	cmd.timeout = cpu_to_le16(0x4000);
+	cmd.window = cpu_to_le16(0x0100);
+	cmd.interval = cpu_to_le16(0x0080);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_RECEIVE_SYNC_TRAIN, &cmd, sizeof(cmd),
+							NULL, NULL, NULL);
+}
+
+static void slave_broadcast_timeout(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_evt_slave_broadcast_timeout *evt = data;
+	struct bt_hci_cmd_receive_sync_train cmd;
+
+	printf("Re-synchronizing with 3D display\n");
+
+	memcpy(cmd.bdaddr, evt->bdaddr, 6);
+	cmd.timeout = cpu_to_le16(0x4000);
+	cmd.window = cpu_to_le16(0x0100);
+	cmd.interval = cpu_to_le16(0x0080);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_RECEIVE_SYNC_TRAIN, &cmd, sizeof(cmd),
+							NULL, NULL, NULL);
+}
+
+static void slave_broadcast_receive(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_evt_slave_broadcast_receive *evt = data;
+	struct bt_hci_cmd_read_clock cmd;
+
+	if (evt->status != 0x00)
+		return;
+
+	if (le32_to_cpu(evt->clock) != 0x00000000)
+		return;
+
+	cmd.handle = cpu_to_le16(0x0000);
+	cmd.type = 0x00;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_CLOCK, &cmd, sizeof(cmd),
+							NULL, NULL, NULL);
+}
+
+static void ext_inquiry_result(const void *data, uint8_t size, void *user_data)
+{
+	const struct bt_hci_evt_ext_inquiry_result *evt = data;
+
+	if (evt->dev_class[0] != 0x3c || evt->dev_class[1] != 0x04
+					|| evt->dev_class[2] != 0x08)
+		return;
+
+	if (evt->data[0]) {
+		struct bt_hci_cmd_truncated_page cmd;
+
+		printf("Found 3D display\n");
+
+		bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY_CANCEL, NULL, 0,
+							NULL, NULL, NULL);
+
+		memcpy(cmd.bdaddr, evt->bdaddr, 6);
+		cmd.pscan_rep_mode = evt->pscan_rep_mode;
+		cmd.clock_offset = evt->clock_offset;
+
+		bt_hci_send(hci_dev, BT_HCI_CMD_TRUNCATED_PAGE,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+	}
+}
+
+static void inquiry_complete(const void *data, uint8_t size, void *user_data)
+{
+	printf("No 3D display found\n");
+
+	start_inquiry();
+}
+
+static void read_local_version(const void *data, uint8_t size, void *user_data)
+{
+	const struct bt_hci_rsp_read_local_version *rsp = data;
+
+	if (rsp->status) {
+		printf("Failed to read local version information\n");
+		shutdown_device();
+		return;
+	}
+
+	if (rsp->manufacturer == 15) {
+		printf("Enabling receiver workaround for Broadcom\n");
+
+		bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
+					brcm_sync_train_received, NULL, NULL);
+	} else {
+		bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
+					sync_train_received, NULL, NULL);
+	}
+}
+
+static void start_glasses(void)
+{
+	uint8_t evtmask1[] = { 0x03, 0xe0, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00 };
+	uint8_t evtmask2[] = { 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	uint8_t inqmode = 0x02;
+
+	if (reset_on_init) {
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+							NULL, NULL, NULL);
+		bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8,
+							NULL, NULL, NULL);
+	}
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+					read_local_version, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8,
+							NULL, NULL, NULL);
+	bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_INQUIRY_MODE, &inqmode, 1,
+							NULL, NULL, NULL);
+
+	bt_hci_register(hci_dev, BT_HCI_EVT_INQUIRY_COMPLETE,
+						inquiry_complete, NULL, NULL);
+	bt_hci_register(hci_dev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
+						ext_inquiry_result, NULL, NULL);
+
+	bt_hci_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE,
+					truncated_page_complete, NULL, NULL);
+	bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_TIMEOUT,
+					slave_broadcast_timeout, NULL, NULL);
+	bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_RECEIVE,
+					slave_broadcast_receive, NULL, NULL);
+
+	start_inquiry();
+}
+
+static bool sync_train_active = false;
+
+static void sync_train_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	sync_train_active = false;
+}
+
+static void start_sync_train(void)
+{
+	struct bt_hci_cmd_write_sync_train_params cmd;
+
+	if (sync_train_active)
+		return;
+
+	printf("Starting new synchronization train\n");
+
+	cmd.min_interval = cpu_to_le16(0x0050);
+	cmd.max_interval = cpu_to_le16(0x00a0);
+	cmd.timeout = cpu_to_le32(0x0002ee00);		/* 120 sec */
+	cmd.service_data = SERVICE_DATA;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_START_SYNC_TRAIN, NULL, 0,
+							NULL, NULL, NULL);
+
+	sync_train_active = true;
+}
+
+static void conn_request(const void *data, uint8_t size, void *user_data)
+{
+	const struct bt_hci_evt_conn_request *evt = data;
+	struct bt_hci_cmd_accept_conn_request cmd;
+
+	printf("Incoming connection from 3D glasses\n");
+
+	memcpy(cmd.bdaddr, evt->bdaddr, 6);
+	cmd.role = 0x00;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd),
+							NULL, NULL, NULL);
+
+	start_sync_train();
+}
+
+static void slave_page_response_timeout(const void *data, uint8_t size,
+							void *user_data)
+{
+	printf("Incoming truncated page received\n");
+
+	start_sync_train();
+}
+
+static void slave_broadcast_channel_map_change(const void *data, uint8_t size,
+								void *user_data)
+{
+	printf("Broadcast channel map changed\n");
+
+	start_sync_train();
+}
+
+static void inquiry_resp_tx_power(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data;
+	struct bt_hci_cmd_write_ext_inquiry_response cmd;
+	uint8_t inqdata[] = { 0x03, 0x3d, 0x03, 0x43, 0x02, 0x0a, 0x00, 0x00 };
+	uint8_t devclass[] = { 0x3c, 0x04, 0x08 };
+	uint8_t scanmode = 0x03;
+
+	inqdata[6] = (uint8_t) rsp->level;
+
+	cmd.fec = 0x00;
+	memset(cmd.data, 0, sizeof(cmd.data));
+	memcpy(cmd.data, inqdata, sizeof(inqdata));
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_CLASS_OF_DEV, devclass, 3,
+							NULL, NULL, NULL);
+	bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SCAN_ENABLE, &scanmode, 1,
+							NULL, NULL, NULL);
+}
+
+static void read_clock(const void *data, uint8_t size, void *user_data)
+{
+	const struct bt_hci_rsp_read_clock *rsp = data;
+	struct broadcast_message msg;
+	uint8_t bcastdata[sizeof(msg) + 3] = { LT_ADDR, 0x03, 0x11, };
+
+	if (rsp->status) {
+		printf("Failed to read local clock information\n");
+		shutdown_device();
+		return;
+	}
+
+	msg.frame_sync_instant = rsp->clock;
+	msg.bluetooth_clock_phase = rsp->accuracy;
+	msg.left_open_offset = cpu_to_le16(50);
+	msg.left_close_offset = cpu_to_le16(300);
+	msg.right_open_offset = cpu_to_le16(350);
+	msg.right_close_offset = cpu_to_le16(600);
+	msg.frame_sync_period = cpu_to_le16(650);
+	msg.frame_sync_period_fraction = 0;
+	memcpy(bcastdata + 3, &msg, sizeof(msg));
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA,
+			bcastdata, sizeof(bcastdata), NULL, NULL, NULL);
+}
+
+static void set_slave_broadcast(const void *data, uint8_t size, void *user_data)
+{
+	const struct bt_hci_rsp_set_slave_broadcast *rsp = data;
+	struct bt_hci_cmd_read_clock cmd;
+
+	if (rsp->status) {
+		printf("Failed to set slave broadcast transmission\n");
+		shutdown_device();
+		return;
+	}
+
+	cmd.handle = cpu_to_le16(0x0000);
+	cmd.type = 0x00;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_CLOCK, &cmd, sizeof(cmd),
+						read_clock, NULL, NULL);
+}
+
+static void start_display(void)
+{
+	struct bt_hci_cmd_set_slave_broadcast cmd;
+	uint8_t evtmask1[] = { 0x1c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	uint8_t evtmask2[] = { 0x00, 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	uint8_t sspmode = 0x01;
+	uint8_t ltaddr = LT_ADDR;
+
+	if (reset_on_init) {
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+							NULL, NULL, NULL);
+		bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8,
+							NULL, NULL, NULL);
+	}
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8,
+							NULL, NULL, NULL);
+	bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &sspmode, 1,
+							NULL, NULL, NULL);
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_RESERVED_LT_ADDR, &ltaddr, 1,
+							NULL, NULL, NULL);
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0,
+							NULL, NULL, NULL);
+
+	bt_hci_register(hci_dev, BT_HCI_EVT_CONN_REQUEST,
+						conn_request, NULL, NULL);
+
+	bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT,
+				slave_page_response_timeout, NULL, NULL);
+	bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_CHANNEL_MAP_CHANGE,
+				slave_broadcast_channel_map_change, NULL, NULL);
+	bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_COMPLETE,
+					sync_train_complete, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, NULL, 0,
+					inquiry_resp_tx_power, NULL, NULL);
+
+	cmd.enable = 0x01;
+	cmd.lt_addr = LT_ADDR;
+	cmd.lpo_allowed = 0x01;
+	cmd.pkt_type = cpu_to_le16(PKT_TYPE);
+	cmd.min_interval = cpu_to_le16(0x0050);		/* 50 ms */
+	cmd.max_interval = cpu_to_le16(0x00a0);		/* 100 ms */
+	cmd.timeout = cpu_to_le16(0xfffe);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST, &cmd, sizeof(cmd),
+					set_slave_broadcast, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	static bool terminated = false;
+
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		if (!terminated) {
+			shutdown_device();
+			terminated = true;
+		}
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("3dsp - 3D Synchronization Profile testing\n"
+		"Usage:\n");
+	printf("\t3dsp [options]\n");
+	printf("options:\n"
+		"\t-D, --display          Use display role\n"
+		"\t-G, --glasses          Use glasses role\n"
+		"\t-i, --index <num>      Use specified controller\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "display", no_argument,       NULL, 'D' },
+	{ "glasses", no_argument,       NULL, 'G' },
+	{ "index",   required_argument, NULL, 'i' },
+	{ "raw",     no_argument,       NULL, 'r' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	bool display_role = false, glasses_role = false;
+	uint16_t index = 0;
+	const char *str;
+	bool use_raw = false;
+	sigset_t mask;
+	int exit_status;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "DGi:rvh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'D':
+			display_role = true;
+			break;
+		case 'G':
+			glasses_role = true;
+			break;
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				usage();
+				return EXIT_FAILURE;
+			}
+			index = atoi(str);
+			break;
+		case 'r':
+			use_raw = true;
+			break;
+		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;
+	}
+
+	if (display_role == glasses_role) {
+		fprintf(stderr, "Specify either display or glasses role\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("3D Synchronization Profile testing ver %s\n", VERSION);
+
+	if (use_raw) {
+		hci_dev = bt_hci_new_raw_device(index);
+		if (!hci_dev) {
+			fprintf(stderr, "Failed to open HCI raw device\n");
+			return EXIT_FAILURE;
+		}
+	} else {
+		hci_dev = bt_hci_new_user_channel(index);
+		if (!hci_dev) {
+			fprintf(stderr, "Failed to open HCI user channel\n");
+			return EXIT_FAILURE;
+		}
+
+		reset_on_init = true;
+		reset_on_shutdown = true;
+	}
+
+	if (display_role)
+		start_display();
+	else if (glasses_role)
+		start_glasses();
+
+	exit_status = mainloop_run();
+
+	bt_hci_unref(hci_dev);
+
+	return exit_status;
+}
diff --git a/bluez/repo/tools/amptest.c b/bluez/repo/tools/amptest.c
new file mode 100644
index 0000000..5574707
--- /dev/null
+++ b/bluez/repo/tools/amptest.c
@@ -0,0 +1,668 @@
+/*
+ *
+ *  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 <unistd.h>
+#include <alloca.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+static int activate_amp_controller(int dev_id)
+{
+	struct hci_dev_info di;
+	struct hci_filter flt;
+	int fd;
+
+	printf("hci%d: Activating controller\n", dev_id);
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open raw HCI socket");
+		return -1;
+	}
+
+	di.dev_id = dev_id;
+
+	if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0) {
+		perror("Failed to get HCI device info");
+		close(fd);
+		return -1;
+	}
+
+	if (!hci_test_bit(HCI_UP, &di.flags)) {
+		if (ioctl(fd, HCIDEVUP, dev_id) < 0) {
+			if (errno != EALREADY) {
+				perror("Failed to bring up HCI device");
+				close(fd);
+				return -1;
+			}
+		}
+	}
+
+	close(fd);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return -1;
+	}
+
+	hci_filter_clear(&flt);
+	hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+	hci_filter_set_event(EVT_CHANNEL_SELECTED, &flt);
+	hci_filter_set_event(EVT_PHYSICAL_LINK_COMPLETE, &flt);
+	hci_filter_set_event(EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE, &flt);
+
+	if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+		perror("Failed to setup HCI device filter");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static bool read_local_amp_info(int dev_id, uint16_t *max_assoc_len)
+{
+	read_local_amp_info_rp rp;
+	struct hci_request rq;
+	int fd;
+
+	printf("hci%d: Reading local AMP information\n", dev_id);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return false;
+	}
+
+	memset(&rp, 0, sizeof(rp));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_READ_LOCAL_AMP_INFO;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_AMP_INFO_RP_SIZE;
+
+	if (hci_send_req(fd, &rq, 1000) < 0) {
+		perror("Failed sending HCI request");
+		hci_close_dev(fd);
+		return false;
+	}
+
+	if (rp.status) {
+		fprintf(stderr, "Failed HCI command: 0x%02x\n", rp.status);
+		hci_close_dev(fd);
+		return false;
+	}
+
+	printf("\tAMP status: 0x%02x\n", rp.amp_status);
+	printf("\tController type: 0x%02x\n", rp.controller_type);
+	printf("\tMax ASSOC length: %d\n", btohs(rp.max_amp_assoc_length));
+
+	*max_assoc_len = btohs(rp.max_amp_assoc_length);
+
+	hci_close_dev(fd);
+
+	return true;
+}
+
+static bool read_local_amp_assoc(int dev_id, uint8_t phy_handle,
+							uint16_t max_assoc_len,
+							uint8_t *assoc_data,
+							uint16_t *assoc_len)
+{
+	read_local_amp_assoc_cp cp;
+	read_local_amp_assoc_rp rp;
+	struct hci_request rq;
+	int fd;
+
+	printf("hci%d: Reading local AMP association\n", dev_id);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return false;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = phy_handle;
+	cp.length_so_far = htobs(0);
+	cp.assoc_length = htobs(max_assoc_len);
+	memset(&rp, 0, sizeof(rp));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_READ_LOCAL_AMP_ASSOC;
+	rq.cparam = &cp;
+	rq.clen   = READ_LOCAL_AMP_ASSOC_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = READ_LOCAL_AMP_ASSOC_RP_SIZE;
+
+	if (hci_send_req(fd, &rq, 1000) < 0) {
+		perror("Failed sending HCI request");
+		hci_close_dev(fd);
+		return false;
+	}
+
+	if (rp.status) {
+		fprintf(stderr, "Failed HCI command: 0x%02x\n", rp.status);
+		hci_close_dev(fd);
+		return false;
+	}
+
+	printf("\tRemain ASSOC length: %d\n", btohs(rp.length));
+
+	*assoc_len = btohs(rp.length);
+	memcpy(assoc_data, rp.fragment, *assoc_len);
+
+	hci_close_dev(fd);
+
+	return true;
+}
+
+static bool write_remote_amp_assoc(int dev_id, uint8_t phy_handle,
+							uint8_t *assoc_data,
+							uint16_t assoc_len)
+{
+	write_remote_amp_assoc_cp cp;
+	write_remote_amp_assoc_rp rp;
+	struct hci_request rq;
+	int fd;
+
+	printf("hci%d: Writing remote AMP association\n", dev_id);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return false;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = phy_handle;
+	cp.length_so_far = htobs(0);
+	cp.remaining_length = htobs(assoc_len);
+	memcpy(cp.fragment, assoc_data, assoc_len);
+	memset(&rp, 0, sizeof(rp));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_STATUS_PARAM;
+	rq.ocf    = OCF_WRITE_REMOTE_AMP_ASSOC;
+	rq.cparam = &cp;
+	rq.clen   = 5 + assoc_len;
+	rq.rparam = &rp;
+	rq.rlen   = WRITE_REMOTE_AMP_ASSOC_RP_SIZE;
+
+	if (hci_send_req(fd, &rq, 1000) < 0) {
+		perror("Failed sending HCI request");
+		hci_close_dev(fd);
+		return false;
+	}
+
+	if (rp.status) {
+		fprintf(stderr, "Failed HCI command: 0x%02x\n", rp.status);
+		hci_close_dev(fd);
+		return false;
+	}
+
+	hci_close_dev(fd);
+
+	return true;
+}
+
+static bool channel_selected_event(int dev_id, int fd, uint8_t phy_handle)
+{
+	printf("hci%d: Waiting for channel selected event\n", dev_id);
+
+	while (1) {
+		uint8_t buf[HCI_MAX_EVENT_SIZE];
+		hci_event_hdr *hdr;
+		struct pollfd p;
+		int n, len;
+
+		p.fd = fd;
+		p.events = POLLIN;
+
+		n = poll(&p, 1, 10000);
+		if (n < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+
+			perror("Failed to poll HCI device");
+			return false;
+		}
+
+		if (n == 0) {
+			fprintf(stderr, "Failure to receive event\n");
+			return false;
+		}
+
+		len = read(fd, buf, sizeof(buf));
+		if (len < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+
+			perror("Failed to read from HCI device");
+			return false;
+		}
+
+		hdr = (void *) (buf + 1);
+
+		if (hdr->evt == EVT_CHANNEL_SELECTED)
+			break;
+	}
+
+	return true;
+}
+
+static bool create_physical_link(int dev_id, uint8_t phy_handle)
+{
+	create_physical_link_cp cp;
+	evt_cmd_status evt;
+	struct hci_request rq;
+	int i, fd;
+
+	printf("hci%d: Creating physical link\n", dev_id);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return false;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = phy_handle;
+	cp.key_length = 32;
+	cp.key_type = 0x03;
+	for (i = 0; i < cp.key_length; i++)
+		cp.key[i] = 0x23;
+	memset(&evt, 0, sizeof(evt));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_CREATE_PHYSICAL_LINK;
+	rq.event  = EVT_CMD_STATUS;
+	rq.cparam = &cp;
+	rq.clen   = CREATE_PHYSICAL_LINK_CP_SIZE;
+	rq.rparam = &evt;
+	rq.rlen   = EVT_CMD_STATUS_SIZE;
+
+	if (hci_send_req(fd, &rq, 1000) < 0) {
+		perror("Failed sending HCI request");
+		hci_close_dev(fd);
+		return false;
+	}
+
+	if (evt.status) {
+		fprintf(stderr, "Failed HCI command: 0x%02x\n", evt.status);
+		hci_close_dev(fd);
+		return false;
+	}
+
+	hci_close_dev(fd);
+
+	return true;
+}
+
+static bool accept_physical_link(int dev_id, uint8_t phy_handle)
+{
+	accept_physical_link_cp cp;
+	evt_cmd_status evt;
+	struct hci_request rq;
+	int i, fd;
+
+	printf("hci%d: Accepting physical link\n", dev_id);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return false;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = phy_handle;
+	cp.key_length = 32;
+	cp.key_type = 0x03;
+	for (i = 0; i < cp.key_length; i++)
+		cp.key[i] = 0x23;
+	memset(&evt, 0, sizeof(evt));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_ACCEPT_PHYSICAL_LINK;
+	rq.event  = EVT_CMD_STATUS;
+	rq.cparam = &cp;
+	rq.clen   = ACCEPT_PHYSICAL_LINK_CP_SIZE;
+	rq.rparam = &evt;
+	rq.rlen   = EVT_CMD_STATUS_SIZE;
+
+	if (hci_send_req(fd, &rq, 1000) < 0) {
+		perror("Failed sending HCI request");
+		hci_close_dev(fd);
+		return false;
+	}
+
+	if (evt.status) {
+		fprintf(stderr, "Failed HCI command: 0x%02x\n", evt.status);
+		hci_close_dev(fd);
+		return false;
+	}
+
+	hci_close_dev(fd);
+
+	return true;
+}
+
+static bool disconnect_physical_link(int dev_id, uint8_t phy_handle,
+							uint8_t reason)
+{
+	disconnect_physical_link_cp cp;
+	evt_cmd_status evt;
+	struct hci_request rq;
+	int fd;
+
+	printf("hci%d: Disconnecting physical link\n", dev_id);
+
+	fd = hci_open_dev(dev_id);
+	if (fd < 0) {
+		perror("Failed to open HCI device");
+		return false;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle = phy_handle;
+	cp.reason = reason;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_DISCONNECT_PHYSICAL_LINK;
+	rq.event  = EVT_CMD_STATUS;
+	rq.cparam = &cp;
+	rq.clen   = DISCONNECT_PHYSICAL_LINK_CP_SIZE;
+	rq.rparam = &evt;
+	rq.rlen   = EVT_CMD_STATUS_SIZE;
+
+	if (hci_send_req(fd, &rq, 1000) < 0) {
+		perror("Failed sending HCI request");
+		hci_close_dev(fd);
+		return false;
+	}
+
+	if (evt.status) {
+		fprintf(stderr, "Failed HCI command: 0x%02x\n", evt.status);
+		hci_close_dev(fd);
+		return false;
+	}
+
+	hci_close_dev(fd);
+
+	return true;
+}
+
+static bool physical_link_complete_event(int dev_id, int fd,
+							uint8_t phy_handle)
+{
+	printf("hci%d: Waiting for physical link complete event\n", dev_id);
+
+	while (1) {
+		uint8_t buf[HCI_MAX_EVENT_SIZE];
+		hci_event_hdr *hdr;
+		int len;
+
+		len = read(fd, buf, sizeof(buf));
+		if (len < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+
+			perror("Failed to read from HCI device");
+			return false;
+		}
+
+		hdr = (void *) (buf + 1);
+
+		if (hdr->evt == EVT_PHYSICAL_LINK_COMPLETE)
+			break;
+	}
+
+	return true;
+}
+
+static bool disconnect_physical_link_complete_event(int dev_id, int fd,
+							uint8_t phy_handle)
+{
+	printf("hci%d: Waiting for physical link disconnect event\n", dev_id);
+
+	while (1) {
+		uint8_t buf[HCI_MAX_EVENT_SIZE];
+		hci_event_hdr *hdr;
+		int len;
+
+		len = read(fd, buf, sizeof(buf));
+		if (len < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+
+			perror("Failed to read from HCI device");
+			return false;
+		}
+
+		hdr = (void *) (buf + 1);
+
+		if (hdr->evt == EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE)
+			break;
+	}
+
+	return true;
+}
+
+static int amp1_dev_id = -1;
+static int amp2_dev_id = -1;
+
+static bool find_amp_controller(void)
+{
+	struct hci_dev_list_req *dl;
+	struct hci_dev_req *dr;
+	int fd, i;
+	bool result;
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open raw HCI socket");
+		return false;
+	}
+
+	dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
+	if (!dl) {
+		perror("Failed allocate HCI device request memory");
+		close(fd);
+		return false;
+	}
+
+	dl->dev_num = HCI_MAX_DEV;
+	dr = dl->dev_req;
+
+	if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) {
+		perror("Failed to get HCI device list");
+		result = false;
+		goto done;
+	}
+
+	for (i = 0; i< dl->dev_num; i++) {
+		struct hci_dev_info di;
+
+		di.dev_id = (dr + i)->dev_id;
+
+		if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0)
+			continue;
+
+		if (((di.type & 0x30) >> 4) != HCI_AMP)
+			continue;
+
+		if (amp1_dev_id < 0)
+			amp1_dev_id = di.dev_id;
+		else if (amp2_dev_id < 0) {
+			if (di.dev_id < amp1_dev_id) {
+				amp2_dev_id = amp1_dev_id;
+				amp1_dev_id = di.dev_id;
+			} else
+				amp2_dev_id = di.dev_id;
+		}
+	}
+
+	result = true;
+
+done:
+	free(dl);
+	close(fd);
+	return result;
+}
+
+int main(int argc ,char *argv[])
+{
+	int amp1_event_fd, amp2_event_fd;
+	uint16_t amp1_max_assoc_len, amp2_max_assoc_len;
+	uint8_t *amp1_assoc_data, *amp2_assoc_data;
+	uint16_t amp1_assoc_len, amp2_assoc_len;
+	uint8_t amp1_phy_handle, amp2_phy_handle;
+
+	if (!find_amp_controller())
+		return EXIT_FAILURE;
+
+	if (amp1_dev_id < 0 || amp2_dev_id < 0) {
+		fprintf(stderr, "Two AMP controllers are required\n");
+		return EXIT_FAILURE;
+	}
+
+	printf("hci%d: AMP initiator\n", amp1_dev_id);
+	printf("hci%d: AMP acceptor\n", amp2_dev_id);
+
+	amp1_event_fd = activate_amp_controller(amp1_dev_id);
+	if (amp1_event_fd < 0)
+		return EXIT_FAILURE;
+
+	amp2_event_fd = activate_amp_controller(amp2_dev_id);
+	if (amp2_event_fd < 0) {
+		hci_close_dev(amp1_event_fd);
+		return EXIT_FAILURE;
+	}
+
+	if (!read_local_amp_info(amp1_dev_id, &amp1_max_assoc_len))
+		return EXIT_FAILURE;
+
+	amp1_assoc_data = alloca(amp1_max_assoc_len);
+
+	printf("--> AMP_Get_Info_Request (Amp_ID B)\n");
+
+	if (!read_local_amp_info(amp2_dev_id, &amp2_max_assoc_len))
+		return EXIT_FAILURE;
+
+	amp2_assoc_data = alloca(amp2_max_assoc_len);
+
+	printf("<-- AMP_Get_Info_Response (Amp_ID B, Status)\n");
+
+	printf("--> AMP_Get_AMP_Assoc_Request (Amp_ID B)\n");
+
+	if (!read_local_amp_assoc(amp2_dev_id, 0x00, amp2_max_assoc_len,
+					amp2_assoc_data, &amp2_assoc_len))
+		return EXIT_FAILURE;
+
+	printf("<-- AMP_Get_AMP_Assoc_Response (Amp_ID B, AMP_Assoc B)\n");
+
+	amp1_phy_handle = 0x04;
+
+	if (!create_physical_link(amp1_dev_id, amp1_phy_handle))
+		return EXIT_FAILURE;
+
+	if (!write_remote_amp_assoc(amp1_dev_id, amp1_phy_handle,
+					amp2_assoc_data, amp2_assoc_len))
+		return EXIT_FAILURE;
+
+	printf("hci%d: Signal MAC to scan\n", amp1_dev_id);
+
+	printf("hci%d: Signal MAC to start\n", amp1_dev_id);
+
+	if (!channel_selected_event(amp1_dev_id, amp1_event_fd,
+							amp1_phy_handle))
+		return EXIT_FAILURE;
+
+	if (!read_local_amp_assoc(amp1_dev_id, amp1_phy_handle,
+					amp1_max_assoc_len,
+					amp1_assoc_data, &amp1_assoc_len))
+		return EXIT_FAILURE;
+
+	printf("--> AMP_Create_Physical_Link_Request (Remote-Amp-ID B, AMP_Assoc A)\n");
+
+	amp2_phy_handle = 0x05;
+
+	if (!accept_physical_link(amp2_dev_id, amp2_phy_handle))
+		return EXIT_FAILURE;
+
+	if (!write_remote_amp_assoc(amp2_dev_id, amp2_phy_handle,
+					amp1_assoc_data, amp1_assoc_len))
+		return EXIT_FAILURE;
+
+	printf("hci%d: Signal MAC to start\n", amp2_dev_id);
+
+	printf("<-- AMP_Create_Physical_Link_Response (Local-Amp-ID B, Status)\n");
+
+	if (!physical_link_complete_event(amp2_dev_id, amp2_event_fd,
+							amp2_phy_handle))
+		return EXIT_FAILURE;
+
+	if (!physical_link_complete_event(amp1_dev_id, amp1_event_fd,
+							amp1_phy_handle))
+		return EXIT_FAILURE;
+
+	/* physical link established */
+
+	if (!disconnect_physical_link(amp1_dev_id, amp1_phy_handle, 0x13))
+		return EXIT_FAILURE;
+
+	if (!disconnect_physical_link_complete_event(amp1_dev_id,
+							amp1_event_fd,
+							amp1_phy_handle))
+		return EXIT_FAILURE;
+
+	if (!disconnect_physical_link_complete_event(amp2_dev_id,
+							amp2_event_fd,
+							amp2_phy_handle))
+		return EXIT_FAILURE;
+
+	hci_close_dev(amp2_event_fd);
+	hci_close_dev(amp1_event_fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/avinfo.c b/bluez/repo/tools/avinfo.c
new file mode 100644
index 0000000..31c4e10
--- /dev/null
+++ b/bluez/repo/tools/avinfo.c
@@ -0,0 +1,711 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia 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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+
+#include "profiles/audio/a2dp-codecs.h"
+
+#define AVDTP_PSM			25
+
+/* Commands */
+#define AVDTP_DISCOVER			0x01
+#define AVDTP_GET_CAPABILITIES		0x02
+
+#define AVDTP_PKT_TYPE_SINGLE		0x00
+
+#define AVDTP_MSG_TYPE_COMMAND		0x00
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT		0x01
+#define AVDTP_REPORTING			0x02
+#define AVDTP_RECOVERY			0x03
+#define AVDTP_CONTENT_PROTECTION	0x04
+#define AVDTP_HEADER_COMPRESSION	0x05
+#define AVDTP_MULTIPLEXING		0x06
+#define AVDTP_MEDIA_CODEC		0x07
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE		0x00
+#define AVDTP_SEP_TYPE_SINK		0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO		0x00
+#define AVDTP_MEDIA_TYPE_VIDEO		0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA	0x02
+
+/* Content Protection types definitions */
+#define AVDTP_CONTENT_PROTECTION_TYPE_DTCP	0x0001
+#define AVDTP_CONTENT_PROTECTION_TYPE_SCMS_T	0x0002
+
+struct avdtp_service_capability {
+	uint8_t category;
+	uint8_t length;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t rfa0:1;
+	uint8_t inuse:1;
+	uint8_t seid:6;
+	uint8_t rfa2:3;
+	uint8_t type:1;
+	uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid_req {
+	struct avdtp_header header;
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct avdtp_media_codec_capability {
+	uint8_t rfa0:4;
+	uint8_t media_type:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t seid:6;
+	uint8_t inuse:1;
+	uint8_t rfa0:1;
+	uint8_t media_type:4;
+	uint8_t type:1;
+	uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid_req {
+	struct avdtp_header header;
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_media_codec_capability {
+	uint8_t media_type:4;
+	uint8_t rfa0:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct discover_resp {
+	struct avdtp_header header;
+	struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+	struct avdtp_header header;
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct avdtp_content_protection_capability {
+	uint16_t content_protection_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+static void print_aptx(a2dp_aptx_t *aptx)
+{
+	printf("\t\tVendor Specific Value (aptX)");
+
+	printf("\n\t\t\tFrequencies: ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_16000)
+		printf("16kHz ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_32000)
+		printf("32kHz ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_44100)
+		printf("44.1kHz ");
+	if (aptx->frequency & APTX_SAMPLING_FREQ_48000)
+		printf("48kHz ");
+
+	printf("\n\t\t\tChannel modes: ");
+	if (aptx->channel_mode & APTX_CHANNEL_MODE_MONO)
+		printf("Mono ");
+	if (aptx->channel_mode & APTX_CHANNEL_MODE_STEREO)
+		printf("Stereo ");
+
+	printf("\n");
+}
+
+static void print_ldac(a2dp_ldac_t *ldac)
+{
+	printf("\t\tVendor Specific Value (LDAC)");
+
+	printf("\n\t\t\tUnknown: %02x %02x", ldac->unknown[0],
+							ldac->unknown[1]);
+
+	printf("\n");
+}
+
+static void print_vendor(a2dp_vendor_codec_t *vendor)
+{
+	uint32_t vendor_id = btohl(vendor->vendor_id);
+	uint16_t codec_id = btohs(vendor->codec_id);
+
+	printf("\tMedia Codec: Vendor Specific A2DP Codec");
+
+	printf("\n\t\tVendor ID 0x%08x", vendor_id);
+
+	printf("\n\t\tVendor Specific Codec ID 0x%04x\n", codec_id);
+
+	if (vendor_id == APTX_VENDOR_ID && codec_id == APTX_CODEC_ID)
+		print_aptx((void *) vendor);
+	else if (vendor_id == LDAC_VENDOR_ID && codec_id == LDAC_CODEC_ID)
+		print_ldac((void *) vendor);
+}
+
+static void print_mpeg24(a2dp_aac_t *aac)
+{
+	unsigned freq = AAC_GET_FREQUENCY(*aac);
+	unsigned bitrate = AAC_GET_BITRATE(*aac);
+
+	printf("\tMedia Codec: MPEG24\n\t\tObject Types: ");
+
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC)
+		printf("MPEG-2 AAC LC ");
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC)
+		printf("MPEG-4 AAC LC ");
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LTP)
+		printf("MPEG-4 AAC LTP ");
+	if (aac->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_SCA)
+		printf("MPEG-4 AAC scalable ");
+
+	printf("\n\t\tFrequencies: ");
+	if (freq & AAC_SAMPLING_FREQ_8000)
+		printf("8kHz ");
+	if (freq & AAC_SAMPLING_FREQ_11025)
+		printf("11.025kHz ");
+	if (freq & AAC_SAMPLING_FREQ_12000)
+		printf("12kHz ");
+	if (freq & AAC_SAMPLING_FREQ_16000)
+		printf("16kHz ");
+	if (freq & AAC_SAMPLING_FREQ_22050)
+		printf("22.05kHz ");
+	if (freq & AAC_SAMPLING_FREQ_24000)
+		printf("24kHz ");
+	if (freq & AAC_SAMPLING_FREQ_32000)
+		printf("32kHz ");
+	if (freq & AAC_SAMPLING_FREQ_44100)
+		printf("44.1kHz ");
+	if (freq & AAC_SAMPLING_FREQ_48000)
+		printf("48kHz ");
+	if (freq & AAC_SAMPLING_FREQ_64000)
+		printf("64kHz ");
+	if (freq & AAC_SAMPLING_FREQ_88200)
+		printf("88.2kHz ");
+	if (freq & AAC_SAMPLING_FREQ_96000)
+		printf("96kHz ");
+
+	printf("\n\t\tChannels: ");
+	if (aac->channels & AAC_CHANNELS_1)
+		printf("1 ");
+	if (aac->channels & AAC_CHANNELS_2)
+		printf("2 ");
+
+	printf("\n\t\tBitrate: %u", bitrate);
+
+	printf("\n\t\tVBR: %s", aac->vbr ? "Yes\n" : "No\n");
+}
+
+static void print_mpeg12(a2dp_mpeg_t *mpeg)
+{
+	printf("\tMedia Codec: MPEG12\n\t\tChannel Modes: ");
+
+	if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO)
+		printf("Mono ");
+	if (mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL)
+		printf("DualChannel ");
+	if (mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO)
+		printf("Stereo ");
+	if (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO)
+		printf("JointStereo");
+
+	printf("\n\t\tFrequencies: ");
+	if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000)
+		printf("16Khz ");
+	if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050)
+		printf("22.05Khz ");
+	if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000)
+		printf("24Khz ");
+	if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000)
+		printf("32Khz ");
+	if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100)
+		printf("44.1Khz ");
+	if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000)
+		printf("48Khz ");
+
+	printf("\n\t\tCRC: %s", mpeg->crc ? "Yes" : "No");
+
+	printf("\n\t\tLayer: ");
+	if (mpeg->layer & MPEG_LAYER_MP1)
+		printf("1 ");
+	if (mpeg->layer & MPEG_LAYER_MP2)
+		printf("2 ");
+	if (mpeg->layer & MPEG_LAYER_MP3)
+		printf("3 ");
+
+	printf("\n\t\tBit Rate: ");
+	if (mpeg->bitrate & MPEG_BIT_RATE_FREE)
+		printf("Free format");
+	else {
+		if (mpeg->bitrate & MPEG_BIT_RATE_32000)
+			printf("32kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_40000)
+			printf("40kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_48000)
+			printf("48kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_56000)
+			printf("56kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_64000)
+			printf("64kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_80000)
+			printf("80kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_96000)
+			printf("96kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_112000)
+			printf("112kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_128000)
+			printf("128kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_160000)
+			printf("160kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_192000)
+			printf("192kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_224000)
+			printf("224kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_256000)
+			printf("256kbps ");
+		if (mpeg->bitrate & MPEG_BIT_RATE_320000)
+			printf("320kbps ");
+	}
+
+	printf("\n\t\tVBR: %s", mpeg->bitrate & MPEG_BIT_RATE_VBR ? "Yes" :
+		"No");
+
+	printf("\n\t\tPayload Format: ");
+	if (mpeg->mpf)
+		printf("RFC-2250 RFC-3119\n");
+	else
+		printf("RFC-2250\n");
+}
+
+static void print_sbc(a2dp_sbc_t *sbc)
+{
+	printf("\tMedia Codec: SBC\n\t\tChannel Modes: ");
+
+	if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO)
+		printf("Mono ");
+	if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+		printf("DualChannel ");
+	if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO)
+		printf("Stereo ");
+	if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+		printf("JointStereo");
+
+	printf("\n\t\tFrequencies: ");
+	if (sbc->frequency & SBC_SAMPLING_FREQ_16000)
+		printf("16Khz ");
+	if (sbc->frequency & SBC_SAMPLING_FREQ_32000)
+		printf("32Khz ");
+	if (sbc->frequency & SBC_SAMPLING_FREQ_44100)
+		printf("44.1Khz ");
+	if (sbc->frequency & SBC_SAMPLING_FREQ_48000)
+		printf("48Khz ");
+
+	printf("\n\t\tSubbands: ");
+	if (sbc->allocation_method & SBC_SUBBANDS_4)
+		printf("4 ");
+	if (sbc->allocation_method & SBC_SUBBANDS_8)
+		printf("8");
+
+	printf("\n\t\tBlocks: ");
+	if (sbc->block_length & SBC_BLOCK_LENGTH_4)
+		printf("4 ");
+	if (sbc->block_length & SBC_BLOCK_LENGTH_8)
+		printf("8 ");
+	if (sbc->block_length & SBC_BLOCK_LENGTH_12)
+		printf("12 ");
+	if (sbc->block_length & SBC_BLOCK_LENGTH_16)
+		printf("16 ");
+
+	printf("\n\t\tBitpool Range: %d-%d\n",
+				sbc->min_bitpool, sbc->max_bitpool);
+}
+
+static void print_media_codec(struct avdtp_media_codec_capability *cap)
+{
+	switch (cap->media_codec_type) {
+	case A2DP_CODEC_SBC:
+		print_sbc((void *) cap->data);
+		break;
+	case A2DP_CODEC_MPEG12:
+		print_mpeg12((void *) cap->data);
+		break;
+	case A2DP_CODEC_MPEG24:
+		print_mpeg24((void *) cap->data);
+		break;
+	case A2DP_CODEC_VENDOR:
+		print_vendor((void *) cap->data);
+		break;
+	default:
+		printf("\tMedia Codec: Unknown\n");
+	}
+}
+
+static void print_content_protection(
+				struct avdtp_content_protection_capability *cap)
+{
+	printf("\tContent Protection: ");
+
+	switch (btohs(cap->content_protection_type)) {
+	case AVDTP_CONTENT_PROTECTION_TYPE_DTCP:
+		printf("DTCP");
+		break;
+	case AVDTP_CONTENT_PROTECTION_TYPE_SCMS_T:
+		printf("SCMS-T");
+		break;
+	default:
+		printf("Unknown");
+	}
+
+	printf("\n");
+}
+
+static void print_caps(void *data, int size)
+{
+	int processed;
+
+	for (processed = 0; processed + 2 < size;) {
+		struct avdtp_service_capability *cap;
+
+		cap = data;
+
+		if (processed + 2 + cap->length > size) {
+			printf("Invalid capability data in getcap resp\n");
+			break;
+		}
+
+		switch (cap->category) {
+		case AVDTP_MEDIA_TRANSPORT:
+		case AVDTP_REPORTING:
+		case AVDTP_RECOVERY:
+		case AVDTP_MULTIPLEXING:
+			/* FIXME: Add proper functions */
+			break;
+		case AVDTP_MEDIA_CODEC:
+			print_media_codec((void *) cap->data);
+			break;
+		case AVDTP_CONTENT_PROTECTION:
+			print_content_protection((void *) cap->data);
+			break;
+		}
+
+		processed += 2 + cap->length;
+		data += 2 + cap->length;
+	}
+}
+
+static void init_request(struct avdtp_header *header, int request_id)
+{
+	static int transaction = 0;
+
+	header->packet_type = AVDTP_PKT_TYPE_SINGLE;
+	header->message_type = AVDTP_MSG_TYPE_COMMAND;
+	header->transaction = transaction;
+	header->signal_id = request_id;
+
+	/* clear rfa bits */
+	header->rfa0 = 0;
+
+	transaction = (transaction + 1) % 16;
+}
+
+static ssize_t avdtp_send(int sk, void *data, int len)
+{
+	ssize_t ret;
+
+	ret = send(sk, data, len, 0);
+
+	if (ret < 0)
+		ret = -errno;
+	else if (ret != len)
+		ret = -EIO;
+
+	if (ret < 0) {
+		printf("Unable to send message: %s (%zd)\n",
+						strerror(-ret), -ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static ssize_t avdtp_receive(int sk, void *data, int len)
+{
+	ssize_t ret;
+
+	ret = recv(sk, data, len, 0);
+
+	if (ret < 0) {
+		printf("Unable to receive message: %s (%d)\n",
+						strerror(errno), errno);
+		return -errno;
+	}
+
+	return ret;
+}
+
+static ssize_t avdtp_get_caps(int sk, int seid)
+{
+	struct seid_req req;
+	char buffer[1024];
+	struct getcap_resp *caps = (void *) buffer;
+	ssize_t ret;
+
+	memset(&req, 0, sizeof(req));
+	init_request(&req.header, AVDTP_GET_CAPABILITIES);
+	req.acp_seid = seid;
+
+	ret = avdtp_send(sk, &req, sizeof(req));
+	if (ret < 0)
+		return ret;
+
+	memset(&buffer, 0, sizeof(buffer));
+	ret = avdtp_receive(sk, caps, sizeof(buffer));
+	if (ret < 0)
+		return ret;
+
+	if ((size_t) ret < (sizeof(struct getcap_resp) + 4 +
+			sizeof(struct avdtp_media_codec_capability))) {
+		printf("Invalid capabilities\n");
+		return -1;
+	}
+
+	print_caps(caps, ret);
+
+	return 0;
+}
+
+static ssize_t avdtp_discover(int sk)
+{
+	struct avdtp_header req;
+	char buffer[256];
+	struct discover_resp *discover = (void *) buffer;
+	int seps, i;
+	ssize_t ret;
+
+	memset(&req, 0, sizeof(req));
+	init_request(&req, AVDTP_DISCOVER);
+
+	ret = avdtp_send(sk, &req, sizeof(req));
+	if (ret < 0)
+		return ret;
+
+	memset(&buffer, 0, sizeof(buffer));
+	ret = avdtp_receive(sk, discover, sizeof(buffer));
+	if (ret < 0)
+		return ret;
+
+	seps = (ret - sizeof(struct avdtp_header)) / sizeof(struct seid_info);
+	for (i = 0; i < seps; i++) {
+		const char *type, *media;
+
+		switch (discover->seps[i].type) {
+		case AVDTP_SEP_TYPE_SOURCE:
+			type = "Source";
+			break;
+		case AVDTP_SEP_TYPE_SINK:
+			type = "Sink";
+			break;
+		default:
+			type = "Invalid";
+		}
+
+		switch (discover->seps[i].media_type) {
+		case AVDTP_MEDIA_TYPE_AUDIO:
+			media = "Audio";
+			break;
+		case AVDTP_MEDIA_TYPE_VIDEO:
+			media = "Video";
+			break;
+		case AVDTP_MEDIA_TYPE_MULTIMEDIA:
+			media = "Multimedia";
+			break;
+		default:
+			media = "Invalid";
+		}
+
+		printf("Stream End-Point #%d: %s %s %s\n",
+					discover->seps[i].seid, media, type,
+					discover->seps[i].inuse ? "*" : "");
+
+		avdtp_get_caps(sk, discover->seps[i].seid);
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst)
+{
+	struct sockaddr_l2 l2a;
+	int sk;
+
+	memset(&l2a, 0, sizeof(l2a));
+	l2a.l2_family = AF_BLUETOOTH;
+	bacpy(&l2a.l2_bdaddr, src);
+
+	sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sk < 0) {
+		printf("Cannot create L2CAP socket. %s(%d)\n", strerror(errno),
+				errno);
+		return -errno;
+	}
+
+	if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) {
+		printf("Bind failed. %s (%d)\n", strerror(errno), errno);
+		close(sk);
+		return -errno;
+	}
+
+	memset(&l2a, 0, sizeof(l2a));
+	l2a.l2_family = AF_BLUETOOTH;
+	bacpy(&l2a.l2_bdaddr, dst);
+	l2a.l2_psm = htobs(AVDTP_PSM);
+
+	if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) {
+		printf("Connect failed. %s(%d)\n", strerror(errno), errno);
+		close(sk);
+		return -errno;
+	}
+
+	return sk;
+}
+
+static void usage(void)
+{
+	printf("avinfo - Audio/Video Info Tool ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tavinfo [options] <remote address>\n");
+	printf("Options:\n"
+		"\t-h\t\tDisplay help\n"
+		"\t-i\t\tSpecify source interface\n");
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "device",	1, 0, 'i' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	bdaddr_t src, dst;
+	int opt, sk, dev_id;
+
+	if (argc < 2) {
+		usage();
+		exit(0);
+	}
+
+	bacpy(&src, BDADDR_ANY);
+	dev_id = hci_get_route(&src);
+	if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) {
+		printf("Cannot find any local adapter\n");
+		exit(-1);
+	}
+
+	while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &src);
+			else
+				str2ba(optarg, &src);
+			break;
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	printf("Connecting ... \n");
+
+	if (bachk(argv[optind]) < 0) {
+		printf("Invalid argument\n");
+		exit(1);
+	}
+
+	str2ba(argv[optind], &dst);
+	sk = l2cap_connect(&src, &dst);
+	if (sk < 0)
+		exit(1);
+
+	if (avdtp_discover(sk) < 0)
+		exit(1);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/avtest.c b/bluez/repo/tools/avtest.c
new file mode 100644
index 0000000..70c9b65
--- /dev/null
+++ b/bluez/repo/tools/avtest.c
@@ -0,0 +1,869 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+
+#define AVDTP_PKT_TYPE_SINGLE		0x00
+#define AVDTP_PKT_TYPE_START		0x01
+#define AVDTP_PKT_TYPE_CONTINUE		0x02
+#define AVDTP_PKT_TYPE_END		0x03
+
+#define AVDTP_MSG_TYPE_COMMAND		0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT	0x01
+#define AVDTP_MSG_TYPE_ACCEPT		0x02
+#define AVDTP_MSG_TYPE_REJECT		0x03
+
+#define AVDTP_DISCOVER			0x01
+#define AVDTP_GET_CAPABILITIES		0x02
+#define AVDTP_SET_CONFIGURATION		0x03
+#define AVDTP_GET_CONFIGURATION		0x04
+#define AVDTP_RECONFIGURE		0x05
+#define AVDTP_OPEN			0x06
+#define AVDTP_START			0x07
+#define AVDTP_CLOSE			0x08
+#define AVDTP_SUSPEND			0x09
+#define AVDTP_ABORT			0x0A
+
+#define AVDTP_SEP_TYPE_SOURCE		0x00
+#define AVDTP_SEP_TYPE_SINK		0x01
+
+#define AVDTP_MEDIA_TYPE_AUDIO		0x00
+#define AVDTP_MEDIA_TYPE_VIDEO		0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA	0x02
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t rfa0:1;
+	uint8_t inuse:1;
+	uint8_t seid:6;
+	uint8_t rfa2:3;
+	uint8_t type:1;
+	uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t no_of_packets;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avctp_header {
+	uint8_t ipid:1;
+	uint8_t cr:1;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t seid:6;
+	uint8_t inuse:1;
+	uint8_t rfa0:1;
+	uint8_t media_type:4;
+	uint8_t type:1;
+	uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t no_of_packets;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avctp_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t cr:1;
+	uint8_t ipid:1;
+	uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+#define AVCTP_COMMAND		0
+#define AVCTP_RESPONSE		1
+
+#define AVCTP_PACKET_SINGLE	0
+
+static const unsigned char media_transport[] = {
+		0x01,	/* Media transport category */
+		0x00,
+		0x07,	/* Media codec category */
+		0x06,
+		0x00,	/* Media type audio */
+		0x00,	/* Codec SBC */
+		0x22,	/* 44.1 kHz, stereo */
+		0x15,	/* 16 blocks, 8 subbands */
+		0x02,
+		0x33,
+};
+
+static int media_sock = -1;
+
+static void dump_avctp_header(struct avctp_header *hdr)
+{
+	printf("TL %d PT %d CR %d IPID %d PID 0x%04x\n", hdr->transaction,
+			hdr->packet_type, hdr->cr, hdr->ipid, ntohs(hdr->pid));
+}
+
+static void dump_avdtp_header(struct avdtp_header *hdr)
+{
+	printf("TL %d PT %d MT %d SI %d\n", hdr->transaction,
+			hdr->packet_type, hdr->message_type, hdr->signal_id);
+}
+
+static void dump_buffer(const unsigned char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		printf("%02x ", buf[i]);
+	printf("\n");
+}
+
+static void process_avdtp(int srv_sk, int sk, unsigned char reject,
+								int fragment)
+{
+	unsigned char buf[672];
+	ssize_t len;
+
+	while (1) {
+		struct avdtp_header *hdr = (void *) buf;
+
+		len = read(sk, buf, sizeof(buf));
+		if (len <= 0) {
+			perror("Read failed");
+			break;
+		}
+
+		dump_buffer(buf, len);
+		dump_avdtp_header(hdr);
+
+		if (hdr->packet_type != AVDTP_PKT_TYPE_SINGLE) {
+			fprintf(stderr, "Only single packets are supported\n");
+			break;
+		}
+
+		if (hdr->message_type != AVDTP_MSG_TYPE_COMMAND) {
+			fprintf(stderr, "Ignoring non-command messages\n");
+			continue;
+		}
+
+		switch (hdr->signal_id) {
+		case AVDTP_DISCOVER:
+			if (reject == AVDTP_DISCOVER) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[2] = 0x29; /* Unsupported configuration */
+				printf("Rejecting discover command\n");
+				len = write(sk, buf, 3);
+			} else {
+				struct seid_info *sei = (void *) (buf + 2);
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				buf[2] = 0x00;
+				buf[3] = 0x00;
+				sei->seid = 0x01;
+				sei->type = AVDTP_SEP_TYPE_SINK;
+				sei->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+				printf("Accepting discover command\n");
+				len = write(sk, buf, 4);
+			}
+			break;
+
+		case AVDTP_GET_CAPABILITIES:
+			if (reject == AVDTP_GET_CAPABILITIES) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[2] = 0x29; /* Unsupported configuration */
+				printf("Rejecting get capabilties command\n");
+				len = write(sk, buf, 3);
+			} else if (fragment) {
+				struct avdtp_start_header *start = (void *) buf;
+
+				printf("Sending fragmented reply to getcap\n");
+
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+
+				/* Start packet */
+				hdr->packet_type = AVDTP_PKT_TYPE_START;
+				start->signal_id = AVDTP_GET_CAPABILITIES;
+				start->no_of_packets = 3;
+				memcpy(&buf[3], media_transport,
+						sizeof(media_transport));
+				len = write(sk, buf,
+						3 + sizeof(media_transport));
+
+				/* Continue packet */
+				hdr->packet_type = AVDTP_PKT_TYPE_CONTINUE;
+				memcpy(&buf[1], media_transport,
+						sizeof(media_transport));
+				len = write(sk, buf,
+						1 + sizeof(media_transport));
+
+				/* End packet */
+				hdr->packet_type = AVDTP_PKT_TYPE_END;
+				memcpy(&buf[1], media_transport,
+						sizeof(media_transport));
+				len = write(sk, buf,
+						1 + sizeof(media_transport));
+			} else {
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				memcpy(&buf[2], media_transport,
+						sizeof(media_transport));
+				printf("Accepting get capabilities command\n");
+				len = write(sk, buf,
+						2 + sizeof(media_transport));
+			}
+			break;
+
+		case AVDTP_SET_CONFIGURATION:
+			if (reject == AVDTP_SET_CONFIGURATION) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[2] = buf[4];
+				buf[3] = 0x13; /* SEP In Use */
+				printf("Rejecting set configuration command\n");
+				len = write(sk, buf, 4);
+			} else {
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				printf("Accepting set configuration command\n");
+				len = write(sk, buf, 2);
+			}
+			break;
+
+		case AVDTP_GET_CONFIGURATION:
+			if (reject == AVDTP_GET_CONFIGURATION) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[2] = 0x12; /* Bad ACP SEID */
+				printf("Rejecting get configuration command\n");
+				len = write(sk, buf, 3);
+			} else {
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				printf("Accepting get configuration command\n");
+				len = write(sk, buf, 2);
+			}
+			break;
+
+		case AVDTP_OPEN:
+			if (reject == AVDTP_OPEN) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[2] = 0x31; /* Bad State */
+				printf("Rejecting open command\n");
+				len = write(sk, buf, 3);
+			} else {
+				struct sockaddr_l2 addr;
+				socklen_t optlen;
+
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				printf("Accepting open command\n");
+				len = write(sk, buf, 2);
+
+				memset(&addr, 0, sizeof(addr));
+				optlen = sizeof(addr);
+
+				media_sock = accept(srv_sk,
+						(struct sockaddr *) &addr,
+								&optlen);
+				if (media_sock < 0) {
+					perror("Accept failed");
+					break;
+				}
+			}
+			break;
+
+		case AVDTP_START:
+			if (reject == AVDTP_ABORT)
+				printf("Ignoring start to cause abort");
+			else if (reject == AVDTP_START) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[3] = 0x31; /* Bad State */
+				printf("Rejecting start command\n");
+				len = write(sk, buf, 4);
+			} else {
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				printf("Accepting start command\n");
+				len = write(sk, buf, 2);
+			}
+			break;
+
+		case AVDTP_CLOSE:
+			if (reject == AVDTP_CLOSE) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[2] = 0x31; /* Bad State */
+				printf("Rejecting close command\n");
+				len = write(sk, buf, 3);
+			} else {
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				printf("Accepting close command\n");
+				len = write(sk, buf, 2);
+				if (media_sock >= 0) {
+					close(media_sock);
+					media_sock = -1;
+				}
+			}
+			break;
+
+		case AVDTP_SUSPEND:
+			if (reject == AVDTP_SUSPEND) {
+				hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+				buf[3] = 0x31; /* Bad State */
+				printf("Rejecting suspend command\n");
+				len = write(sk, buf, 4);
+			} else {
+				hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+				printf("Accepting suspend command\n");
+				len = write(sk, buf, 2);
+			}
+			break;
+
+		case AVDTP_ABORT:
+			hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+			printf("Accepting abort command\n");
+			len = write(sk, buf, 2);
+			if (media_sock >= 0) {
+				close(media_sock);
+				media_sock = -1;
+			}
+			break;
+
+		default:
+			buf[1] = 0x00;
+			printf("Unknown command\n");
+			len = write(sk, buf, 2);
+			break;
+		}
+	}
+}
+
+static void process_avctp(int sk, int reject)
+{
+	unsigned char buf[672];
+	ssize_t len;
+
+	while (1) {
+		struct avctp_header *hdr = (void *) buf;
+
+		len = read(sk, buf, sizeof(buf));
+		if (len <= 0) {
+			perror("Read failed");
+			break;
+		}
+
+		dump_buffer(buf, len);
+
+		if (len >= AVCTP_HEADER_LENGTH)
+			dump_avctp_header(hdr);
+	}
+}
+
+static int set_minimum_mtu(int sk)
+{
+	struct l2cap_options l2o;
+	socklen_t optlen;
+
+	memset(&l2o, 0, sizeof(l2o));
+	optlen = sizeof(l2o);
+
+	if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &optlen) < 0) {
+		perror("getsockopt");
+		return -1;
+	}
+
+	l2o.imtu = 48;
+	l2o.omtu = 48;
+
+	if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+		perror("setsockopt");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void do_listen(const bdaddr_t *src, unsigned char reject, int fragment)
+{
+	struct sockaddr_l2 addr;
+	socklen_t optlen;
+	int sk, nsk;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sk < 0) {
+		perror("Can't create socket");
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(25);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Can't bind socket");
+		goto error;
+	}
+
+	if (fragment)
+		set_minimum_mtu(sk);
+
+	if (listen(sk, 10)) {
+		perror("Can't listen on the socket");
+		goto error;
+	}
+
+	while (1) {
+		memset(&addr, 0, sizeof(addr));
+		optlen = sizeof(addr);
+
+		nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+		if (nsk < 0) {
+			perror("Accept failed");
+			continue;
+		}
+
+		process_avdtp(sk, nsk, reject, fragment);
+
+		if (media_sock >= 0) {
+			close(media_sock);
+			media_sock = -1;
+		}
+
+		close(nsk);
+	}
+
+error:
+	close(sk);
+}
+
+static int do_connect(const bdaddr_t *src, const bdaddr_t *dst, int avctp,
+								int fragment)
+{
+	struct sockaddr_l2 addr;
+	int sk, err;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sk < 0) {
+		perror("Can't create socket");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Can't bind socket");
+		goto error;
+	}
+
+	if (fragment)
+		set_minimum_mtu(sk);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(avctp ? 23 : 25);
+
+	err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		perror("Unable to connect");
+		goto error;
+	}
+
+	return sk;
+
+error:
+	close(sk);
+	return -1;
+}
+
+static void do_avdtp_send(int sk, const bdaddr_t *src, const bdaddr_t *dst,
+				unsigned char cmd, int invalid, int preconf)
+{
+	unsigned char buf[672];
+	struct avdtp_header *hdr = (void *) buf;
+	ssize_t len;
+
+	memset(buf, 0, sizeof(buf));
+
+	switch (cmd) {
+	case AVDTP_DISCOVER:
+		if (invalid)
+			hdr->message_type = 0x01;
+		else
+			hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_DISCOVER;
+		len = write(sk, buf, 2);
+		break;
+
+	case AVDTP_GET_CAPABILITIES:
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_GET_CAPABILITIES;
+		buf[2] = 1 << 2; /* SEID 1 */
+		len = write(sk, buf, invalid ? 2 : 3);
+		break;
+
+	case AVDTP_SET_CONFIGURATION:
+		if (preconf)
+			do_avdtp_send(sk, src, dst, cmd, 0, 0);
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_SET_CONFIGURATION;
+		buf[2] = 1 << 2; /* ACP SEID */
+		buf[3] = 1 << 2; /* INT SEID */
+		memcpy(&buf[4], media_transport, sizeof(media_transport));
+		if (invalid)
+			buf[5] = 0x01; /* LOSC != 0 */
+		len = write(sk, buf, 4 + sizeof(media_transport));
+		break;
+
+	case AVDTP_GET_CONFIGURATION:
+		if (preconf)
+			do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_GET_CONFIGURATION;
+		if (invalid)
+			buf[2] = 13 << 2; /* Invalid ACP SEID */
+		else
+			buf[2] = 1 << 2; /* Valid ACP SEID */
+		len = write(sk, buf, 3);
+		break;
+
+	case AVDTP_OPEN:
+		if (preconf)
+			do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_OPEN;
+		buf[2] = 1 << 2; /* ACP SEID */
+		len = write(sk, buf, 3);
+		break;
+
+	case AVDTP_START:
+		if (preconf)
+			do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+		if (!invalid)
+			do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_START;
+		buf[2] = 1 << 2; /* ACP SEID */
+		len = write(sk, buf, 3);
+		break;
+
+	case AVDTP_CLOSE:
+		if (preconf) {
+			do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+			do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+		}
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_CLOSE;
+		if (invalid)
+			buf[2] = 13 << 2; /* Invalid ACP SEID */
+		else
+			buf[2] = 1 << 2; /* Valid ACP SEID */
+		len = write(sk, buf, 3);
+		break;
+
+	case AVDTP_SUSPEND:
+		if (invalid)
+			do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, preconf);
+		else
+			do_avdtp_send(sk, src, dst, AVDTP_START, 0, preconf);
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_SUSPEND;
+		buf[2] = 1 << 2; /* ACP SEID */
+		len = write(sk, buf, 3);
+		break;
+
+	case AVDTP_ABORT:
+		do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 1);
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = AVDTP_ABORT;
+		buf[2] = 1 << 2; /* ACP SEID */
+		len = write(sk, buf, 3);
+		break;
+
+	default:
+		hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+		hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+		hdr->signal_id = cmd;
+		len = write(sk, buf, 2);
+		break;
+	}
+
+	do {
+		len = read(sk, buf, sizeof(buf));
+
+		dump_buffer(buf, len);
+		dump_avdtp_header(hdr);
+	} while (len < 2 || (hdr->message_type != AVDTP_MSG_TYPE_ACCEPT &&
+				hdr->message_type != AVDTP_MSG_TYPE_REJECT &&
+				hdr->message_type != AVDTP_MSG_TYPE_GEN_REJECT));
+
+	if (cmd == AVDTP_OPEN && len >= 2 &&
+				hdr->message_type == AVDTP_MSG_TYPE_ACCEPT)
+		media_sock = do_connect(src, dst, 0, 0);
+}
+
+static void do_avctp_send(int sk, int invalid)
+{
+	unsigned char buf[672];
+	struct avctp_header *hdr = (void *) buf;
+	unsigned char play_pressed[] = { 0x00, 0x48, 0x7c, 0x44, 0x00 };
+	ssize_t len;
+
+	memset(buf, 0, sizeof(buf));
+
+	hdr->packet_type = AVCTP_PACKET_SINGLE;
+	hdr->cr = AVCTP_COMMAND;
+	if (invalid)
+		hdr->pid = 0xffff;
+	else
+		hdr->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+	memcpy(&buf[AVCTP_HEADER_LENGTH], play_pressed, sizeof(play_pressed));
+
+	len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
+
+	len = read(sk, buf, sizeof(buf));
+
+	dump_buffer(buf, len);
+	if (len >= AVCTP_HEADER_LENGTH)
+		dump_avctp_header(hdr);
+}
+
+static void usage(void)
+{
+	printf("avtest - Audio/Video testing ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tavtest [options] [remote address]\n");
+	printf("Options:\n"
+		"\t--device <hcidev>    HCI device\n"
+		"\t--reject <command>   Reject command\n"
+		"\t--send <command>     Send command\n"
+		"\t--preconf            Configure stream before actual command\n"
+		"\t--wait <N>           Wait N seconds before exiting\n"
+		"\t--fragment           Use minimum MTU and fragmented messages\n"
+		"\t--invalid <command>  Send invalid command\n");
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "device",	1, 0, 'i' },
+	{ "reject",	1, 0, 'r' },
+	{ "send",	1, 0, 's' },
+	{ "invalid",	1, 0, 'f' },
+	{ "preconf",	0, 0, 'c' },
+	{ "fragment",   0, 0, 'F' },
+	{ "avctp",	0, 0, 'C' },
+	{ "wait",	1, 0, 'w' },
+	{ 0, 0, 0, 0 }
+};
+
+static unsigned char parse_cmd(const char *arg)
+{
+	if (!strncmp(arg, "discov", 6))
+		return AVDTP_DISCOVER;
+	else if (!strncmp(arg, "capa", 4))
+		return AVDTP_GET_CAPABILITIES;
+	else if (!strncmp(arg, "getcapa", 7))
+		return AVDTP_GET_CAPABILITIES;
+	else if (!strncmp(arg, "setconf", 7))
+		return AVDTP_SET_CONFIGURATION;
+	else if (!strncmp(arg, "getconf", 7))
+		return AVDTP_GET_CONFIGURATION;
+	else if (!strncmp(arg, "open", 4))
+		return AVDTP_OPEN;
+	else if (!strncmp(arg, "start", 5))
+		return AVDTP_START;
+	else if (!strncmp(arg, "close", 5))
+		return AVDTP_CLOSE;
+	else if (!strncmp(arg, "suspend", 7))
+		return AVDTP_SUSPEND;
+	else if (!strncmp(arg, "abort", 7))
+		return AVDTP_ABORT;
+	else
+		return atoi(arg);
+}
+
+enum {
+	MODE_NONE, MODE_REJECT, MODE_SEND,
+};
+
+int main(int argc, char *argv[])
+{
+	unsigned char cmd = 0x00;
+	bdaddr_t src, dst;
+	int opt, mode = MODE_NONE, sk, invalid = 0, preconf = 0, fragment = 0;
+	int avctp = 0, wait_before_exit = 0;
+
+	bacpy(&src, BDADDR_ANY);
+	bacpy(&dst, BDADDR_ANY);
+
+	while ((opt = getopt_long(argc, argv, "+i:r:s:f:hcFCw:",
+						main_options, NULL)) != EOF) {
+		switch (opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &src);
+			else
+				str2ba(optarg, &src);
+			break;
+
+		case 'r':
+			mode = MODE_REJECT;
+			cmd = parse_cmd(optarg);
+			break;
+
+		case 'f':
+			invalid = 1;
+			/* Intentionally missing break */
+
+		case 's':
+			mode = MODE_SEND;
+			cmd = parse_cmd(optarg);
+			break;
+
+		case 'c':
+			preconf = 1;
+			break;
+
+		case 'F':
+			fragment = 1;
+			break;
+
+		case 'C':
+			avctp = 1;
+			break;
+
+		case 'w':
+			wait_before_exit = atoi(optarg);
+			break;
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	if (argv[optind])
+		str2ba(argv[optind], &dst);
+
+	if (avctp) {
+		avctp = mode;
+		mode = MODE_SEND;
+	}
+
+	switch (mode) {
+	case MODE_REJECT:
+		do_listen(&src, cmd, fragment);
+		break;
+	case MODE_SEND:
+		sk = do_connect(&src, &dst, avctp, fragment);
+		if (sk < 0)
+			exit(1);
+		if (avctp) {
+			if (avctp == MODE_SEND)
+				do_avctp_send(sk, invalid);
+			else
+				process_avctp(sk, cmd);
+		} else
+			do_avdtp_send(sk, &src, &dst, cmd, invalid, preconf);
+		if (wait_before_exit) {
+			printf("Waiting %d seconds before exiting\n", wait_before_exit);
+			sleep(wait_before_exit);
+		}
+		if (media_sock >= 0)
+			close(media_sock);
+		close(sk);
+		break;
+	default:
+		fprintf(stderr, "No operating mode specified!\n");
+		exit(1);
+	}
+
+	return 0;
+}
diff --git a/bluez/repo/tools/bccmd.1 b/bluez/repo/tools/bccmd.1
new file mode 100644
index 0000000..26c83a6
--- /dev/null
+++ b/bluez/repo/tools/bccmd.1
@@ -0,0 +1,130 @@
+.TH BCCMD 1 "Jun 20 2006" BlueZ "Linux System Administration"
+.SH NAME
+bccmd \- Utility for the CSR BCCMD interface
+.SH SYNOPSIS
+.B bccmd
+.br
+.B bccmd [-t <transport>] [-d <device>] <command> [<args>]
+.br
+.B bccmd [-h --help]
+.br
+.SH DESCRIPTION
+.B
+bccmd
+issues BlueCore commands to
+.B
+Cambridge Silicon Radio
+devices. If run without the <command> argument, a short help page will be displayed.
+.SH OPTIONS
+.TP
+.BI -t\ <transport>
+Specify the communication transport. Valid options are:
+.RS
+.TP
+.BI HCI
+Local device with Host Controller Interface (default).
+.TP
+.BI USB
+Direct USB connection.
+.TP
+.BI BCSP
+Blue Core Serial Protocol.
+.TP
+.BI H4
+H4 serial protocol.
+.TP
+.BI 3WIRE
+3WIRE protocol (not implemented).
+.SH
+.TP
+.BI -d\ <dev>
+Specify a particular device to operate on. If not specified, default is the first available HCI device
+or /dev/ttyS0 for serial transports.
+.SH COMMANDS
+.TP
+.BI builddef
+Get build definitions
+.TP
+.BI keylen\ <handle>
+Get current crypt key length
+.TP
+.BI clock
+Get local Bluetooth clock
+.TP
+.BI rand
+Get random number
+.TP
+.BI chiprev
+Get chip revision
+.TP
+.BI buildname
+Get the full build name
+.TP
+.BI panicarg
+Get panic code argument
+.TP
+.BI faultarg
+Get fault code argument
+.TP
+.BI coldreset
+Perform cold reset
+.TP
+.BI warmreset
+Perform warm reset
+.TP
+.BI disabletx
+Disable TX on the device
+.TP
+.BI enabletx
+Enable TX on the device
+.TP
+.BI singlechan\ <channel>
+Lock radio on specific channel
+.TP
+.BI hoppingon
+Revert to channel hopping
+.TP
+.BI rttxdata1\ <decimal\ freq\ MHz>\ <level>
+TXData1 radio test
+.TP
+.BI radiotest\ <decimal\ freq\ MHz>\ <level>\ <id>
+Run radio tests, tests 4, 6 and 7 are transmit tests
+.TP
+.BI memtypes
+Get memory types
+.TP
+.BI psget\ [-r]\ [-s\ <stores>]\ <key>
+Get value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psset\ [-r]\ [-s\ <stores>]\ <key>\ <value>
+Set value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psclr\ [-r]\ [-s\ <stores>]\ <key>
+Clear value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI pslist\ [-r]\ [-s\ <stores>]
+List all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psread\ [-r]\ [-s\ <stores>]
+Read all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psload\ [-r]\ [-s\ <stores>]\ <file>
+Load all PS keys from PSR file.
+-r sends a warm reset afterwards
+.TP
+.BI pscheck\ [-r]\ [-s\ <stores>]\ <file>
+Check syntax of PSR file.
+-r sends a warm reset afterwards
+.SH KEYS
+bdaddr country devclass keymin keymax features commands version
+remver hciextn mapsco baudrate hostintf anafreq anaftrim usbvid
+usbpid dfupid bootmode
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/bluez/repo/tools/bccmd.c b/bluez/repo/tools/bccmd.c
new file mode 100644
index 0000000..4d15f3f
--- /dev/null
+++ b/bluez/repo/tools/bccmd.c
@@ -0,0 +1,1271 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <stdlib.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "csr.h"
+
+#define CSR_TRANSPORT_UNKNOWN	0
+#define CSR_TRANSPORT_HCI	1
+#define CSR_TRANSPORT_USB	2
+#define CSR_TRANSPORT_BCSP	3
+#define CSR_TRANSPORT_H4	4
+#define CSR_TRANSPORT_3WIRE	5
+
+#define CSR_STORES_PSI		(0x0001)
+#define CSR_STORES_PSF		(0x0002)
+#define CSR_STORES_PSROM	(0x0004)
+#define CSR_STORES_PSRAM	(0x0008)
+#define CSR_STORES_DEFAULT	(CSR_STORES_PSI | CSR_STORES_PSF)
+
+#define CSR_TYPE_NULL		0
+#define CSR_TYPE_COMPLEX	1
+#define CSR_TYPE_UINT8		2
+#define CSR_TYPE_UINT16		3
+#define CSR_TYPE_UINT32		4
+
+#define CSR_TYPE_ARRAY		CSR_TYPE_COMPLEX
+#define CSR_TYPE_BDADDR		CSR_TYPE_COMPLEX
+
+static inline int transport_open(int transport, char *device, speed_t bcsp_rate)
+{
+	switch (transport) {
+	case CSR_TRANSPORT_HCI:
+		return csr_open_hci(device);
+	case CSR_TRANSPORT_USB:
+		return csr_open_usb(device);
+	case CSR_TRANSPORT_BCSP:
+		return csr_open_bcsp(device, bcsp_rate);
+	case CSR_TRANSPORT_H4:
+		return csr_open_h4(device);
+	case CSR_TRANSPORT_3WIRE:
+		return csr_open_3wire(device);
+	default:
+		fprintf(stderr, "Unsupported transport\n");
+		return -1;
+	}
+}
+
+static inline int transport_read(int transport, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	switch (transport) {
+	case CSR_TRANSPORT_HCI:
+		return csr_read_hci(varid, value, length);
+	case CSR_TRANSPORT_USB:
+		return csr_read_usb(varid, value, length);
+	case CSR_TRANSPORT_BCSP:
+		return csr_read_bcsp(varid, value, length);
+	case CSR_TRANSPORT_H4:
+		return csr_read_h4(varid, value, length);
+	case CSR_TRANSPORT_3WIRE:
+		return csr_read_3wire(varid, value, length);
+	default:
+		errno = EOPNOTSUPP;
+		return -1;
+	}
+}
+
+static inline int transport_write(int transport, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	switch (transport) {
+	case CSR_TRANSPORT_HCI:
+		return csr_write_hci(varid, value, length);
+	case CSR_TRANSPORT_USB:
+		return csr_write_usb(varid, value, length);
+	case CSR_TRANSPORT_BCSP:
+		return csr_write_bcsp(varid, value, length);
+	case CSR_TRANSPORT_H4:
+		return csr_write_h4(varid, value, length);
+	case CSR_TRANSPORT_3WIRE:
+		return csr_write_3wire(varid, value, length);
+	default:
+		errno = EOPNOTSUPP;
+		return -1;
+	}
+}
+
+static inline void transport_close(int transport)
+{
+	switch (transport) {
+	case CSR_TRANSPORT_HCI:
+		csr_close_hci();
+		break;
+	case CSR_TRANSPORT_USB:
+		csr_close_usb();
+		break;
+	case CSR_TRANSPORT_BCSP:
+		csr_close_bcsp();
+		break;
+	case CSR_TRANSPORT_H4:
+		csr_close_h4();
+		break;
+	case CSR_TRANSPORT_3WIRE:
+		csr_close_3wire();
+		break;
+	}
+}
+
+static struct {
+	uint16_t pskey;
+	int type;
+	int size;
+	char *str;
+} storage[] = {
+	{ CSR_PSKEY_BDADDR,                   CSR_TYPE_BDADDR,  8,  "bdaddr"   },
+	{ CSR_PSKEY_COUNTRYCODE,              CSR_TYPE_UINT16,  0,  "country"  },
+	{ CSR_PSKEY_CLASSOFDEVICE,            CSR_TYPE_UINT32,  0,  "devclass" },
+	{ CSR_PSKEY_ENC_KEY_LMIN,             CSR_TYPE_UINT16,  0,  "keymin"   },
+	{ CSR_PSKEY_ENC_KEY_LMAX,             CSR_TYPE_UINT16,  0,  "keymax"   },
+	{ CSR_PSKEY_LOCAL_SUPPORTED_FEATURES, CSR_TYPE_ARRAY,   8,  "features" },
+	{ CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS, CSR_TYPE_ARRAY,   18, "commands" },
+	{ CSR_PSKEY_HCI_LMP_LOCAL_VERSION,    CSR_TYPE_UINT16,  0,  "version"  },
+	{ CSR_PSKEY_LMP_REMOTE_VERSION,       CSR_TYPE_UINT8,   0,  "remver"   },
+	{ CSR_PSKEY_HOSTIO_USE_HCI_EXTN,      CSR_TYPE_UINT16,  0,  "hciextn"  },
+	{ CSR_PSKEY_HOSTIO_MAP_SCO_PCM,       CSR_TYPE_UINT16,  0,  "mapsco"   },
+	{ CSR_PSKEY_UART_BAUDRATE,            CSR_TYPE_UINT16,  0,  "baudrate" },
+	{ CSR_PSKEY_HOST_INTERFACE,           CSR_TYPE_UINT16,  0,  "hostintf" },
+	{ CSR_PSKEY_ANA_FREQ,                 CSR_TYPE_UINT16,  0,  "anafreq"  },
+	{ CSR_PSKEY_ANA_FTRIM,                CSR_TYPE_UINT16,  0,  "anaftrim" },
+	{ CSR_PSKEY_USB_VENDOR_ID,            CSR_TYPE_UINT16,  0,  "usbvid"   },
+	{ CSR_PSKEY_USB_PRODUCT_ID,           CSR_TYPE_UINT16,  0,  "usbpid"   },
+	{ CSR_PSKEY_USB_DFU_PRODUCT_ID,       CSR_TYPE_UINT16,  0,  "dfupid"   },
+	{ CSR_PSKEY_INITIAL_BOOTMODE,         CSR_TYPE_UINT16,  0,  "bootmode" },
+	{ 0x0000 },
+};
+
+static char *storestostr(uint16_t stores)
+{
+	switch (stores) {
+	case 0x0000:
+		return "Default";
+	case 0x0001:
+		return "psi";
+	case 0x0002:
+		return "psf";
+	case 0x0004:
+		return "psrom";
+	case 0x0008:
+		return "psram";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *memorytostr(uint16_t type)
+{
+	switch (type) {
+	case 0x0000:
+		return "Flash memory";
+	case 0x0001:
+		return "EEPROM";
+	case 0x0002:
+		return "RAM (transient)";
+	case 0x0003:
+		return "ROM (or \"read-only\" flash memory)";
+	default:
+		return "Unknown";
+	}
+}
+
+#define OPT_RANGE(min, max) \
+		if (argc < (min)) { errno = EINVAL; return -1; } \
+		if (argc > (max)) { errno = E2BIG; return -1; }
+
+static struct option help_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static int opt_help(int argc, char *argv[], int *help)
+{
+	int opt;
+
+	while ((opt=getopt_long(argc, argv, "+h", help_options, NULL)) != EOF) {
+		switch (opt) {
+		case 'h':
+			if (help)
+				*help = 1;
+			break;
+		}
+	}
+
+	return optind;
+}
+
+#define OPT_HELP(range, help) \
+		opt_help(argc, argv, (help)); \
+		argc -= optind; argv += optind; optind = 0; \
+		OPT_RANGE((range), (range))
+
+static int cmd_builddef(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t def = 0x0000, nextdef = 0x0000;
+	int err = 0;
+
+	OPT_HELP(0, NULL);
+
+	printf("Build definitions:\n");
+
+	while (1) {
+		memset(array, 0, sizeof(array));
+		array[0] = def & 0xff;
+		array[1] = def >> 8;
+
+		err = transport_read(transport, CSR_VARID_GET_NEXT_BUILDDEF, array, 8);
+		if (err < 0)
+			break;
+
+		nextdef = array[2] | (array[3] << 8);
+
+		if (nextdef == 0x0000)
+			break;
+
+		def = nextdef;
+
+		printf("0x%04x - %s\n", def, csr_builddeftostr(def));
+	}
+
+	return err;
+}
+
+static int cmd_keylen(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t handle, keylen;
+	int err;
+
+	OPT_HELP(1, NULL);
+
+	handle = atoi(argv[0]);
+
+	memset(array, 0, sizeof(array));
+	array[0] = handle & 0xff;
+	array[1] = handle >> 8;
+
+	err = transport_read(transport, CSR_VARID_CRYPT_KEY_LENGTH, array, 8);
+	if (err < 0)
+		return -1;
+
+	keylen = array[2] | (array[3] << 8);
+
+	printf("Crypt key length: %d bit\n", keylen * 8);
+
+	return 0;
+}
+
+static int cmd_clock(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint32_t clock;
+	int err;
+
+	OPT_HELP(0, NULL);
+
+	memset(array, 0, sizeof(array));
+
+	err = transport_read(transport, CSR_VARID_BT_CLOCK, array, 8);
+	if (err < 0)
+		return -1;
+
+	clock = array[2] | (array[3] << 8) | (array[0] << 16) | (array[1] << 24);
+
+	printf("Bluetooth clock: 0x%04x (%d)\n", clock, clock);
+
+	return 0;
+}
+
+static int cmd_rand(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t rand;
+	int err;
+
+	OPT_HELP(0, NULL);
+
+	memset(array, 0, sizeof(array));
+
+	err = transport_read(transport, CSR_VARID_RAND, array, 8);
+	if (err < 0)
+		return -1;
+
+	rand = array[0] | (array[1] << 8);
+
+	printf("Random number: 0x%02x (%d)\n", rand, rand);
+
+	return 0;
+}
+
+static int cmd_chiprev(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t rev;
+	char *str;
+	int err;
+
+	OPT_HELP(0, NULL);
+
+	memset(array, 0, sizeof(array));
+
+	err = transport_read(transport, CSR_VARID_CHIPREV, array, 8);
+	if (err < 0)
+		return -1;
+
+	rev = array[0] | (array[1] << 8);
+
+	switch (rev) {
+	case 0x64:
+		str = "BC1 ES";
+		break;
+	case 0x65:
+		str = "BC1";
+		break;
+	case 0x89:
+		str = "BC2-External A";
+		break;
+	case 0x8a:
+		str = "BC2-External B";
+		break;
+	case 0x28:
+		str = "BC2-ROM";
+		break;
+	case 0x43:
+		str = "BC3-Multimedia";
+		break;
+	case 0x15:
+		str = "BC3-ROM";
+		break;
+	case 0xe2:
+		str = "BC3-Flash";
+		break;
+	case 0x26:
+		str = "BC4-External";
+		break;
+	case 0x30:
+		str = "BC4-ROM";
+		break;
+	default:
+		str = "NA";
+		break;
+	}
+
+	printf("Chip revision: 0x%04x (%s)\n", rev, str);
+
+	return 0;
+}
+
+static int cmd_buildname(int transport, int argc, char *argv[])
+{
+	uint8_t array[131];
+	char name[64];
+	unsigned int i;
+	int err;
+
+	OPT_HELP(0, NULL);
+
+	memset(array, 0, sizeof(array));
+
+	err = transport_read(transport, CSR_VARID_READ_BUILD_NAME, array, 128);
+	if (err < 0)
+		return -1;
+
+	for (i = 0; i < sizeof(name); i++)
+		name[i] = array[(i * 2) + 4];
+
+	printf("Build name: %s\n", name);
+
+	return 0;
+}
+
+static int cmd_panicarg(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t error;
+	int err;
+
+	OPT_HELP(0, NULL);
+
+	memset(array, 0, sizeof(array));
+
+	err = transport_read(transport, CSR_VARID_PANIC_ARG, array, 8);
+	if (err < 0)
+		return -1;
+
+	error = array[0] | (array[1] << 8);
+
+	printf("Panic code: 0x%02x (%s)\n", error,
+					error < 0x100 ? "valid" : "invalid");
+
+	return 0;
+}
+
+static int cmd_faultarg(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t error;
+	int err;
+
+	OPT_HELP(0, NULL);
+
+	memset(array, 0, sizeof(array));
+
+	err = transport_read(transport, CSR_VARID_FAULT_ARG, array, 8);
+	if (err < 0)
+		return -1;
+
+	error = array[0] | (array[1] << 8);
+
+	printf("Fault code: 0x%02x (%s)\n", error,
+					error < 0x100 ? "valid" : "invalid");
+
+	return 0;
+}
+
+static int cmd_coldreset(int transport, int argc, char *argv[])
+{
+	return transport_write(transport, CSR_VARID_COLD_RESET, NULL, 0);
+}
+
+static int cmd_warmreset(int transport, int argc, char *argv[])
+{
+	return transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+}
+
+static int cmd_disabletx(int transport, int argc, char *argv[])
+{
+	return transport_write(transport, CSR_VARID_DISABLE_TX, NULL, 0);
+}
+
+static int cmd_enabletx(int transport, int argc, char *argv[])
+{
+	return transport_write(transport, CSR_VARID_ENABLE_TX, NULL, 0);
+}
+
+static int cmd_singlechan(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t channel;
+
+	OPT_HELP(1, NULL);
+
+	channel = atoi(argv[0]);
+
+	if (channel > 2401 && channel < 2481)
+		channel -= 2402;
+
+	if (channel > 78) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	memset(array, 0, sizeof(array));
+	array[0] = channel & 0xff;
+	array[1] = channel >> 8;
+
+	return transport_write(transport, CSR_VARID_SINGLE_CHAN, array, 8);
+}
+
+static int cmd_hoppingon(int transport, int argc, char *argv[])
+{
+	return transport_write(transport, CSR_VARID_HOPPING_ON, NULL, 0);
+}
+
+static int cmd_rttxdata1(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t freq, level;
+
+	OPT_HELP(2, NULL);
+
+	freq = atoi(argv[0]);
+
+	if (!strncasecmp(argv[1], "0x", 2))
+		level = strtol(argv[1], NULL, 16);
+	else
+		level = atoi(argv[1]);
+
+	memset(array, 0, sizeof(array));
+	array[0] = 0x04;
+	array[1] = 0x00;
+	array[2] = freq & 0xff;
+	array[3] = freq >> 8;
+	array[4] = level & 0xff;
+	array[5] = level >> 8;
+
+	return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
+}
+
+static int cmd_radiotest(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t freq, level, test;
+
+	OPT_HELP(3, NULL);
+
+	freq = atoi(argv[0]);
+
+	if (!strncasecmp(argv[1], "0x", 2))
+		level = strtol(argv[1], NULL, 16);
+	else
+		level = atoi(argv[1]);
+
+	test = atoi(argv[2]);
+
+	memset(array, 0, sizeof(array));
+	array[0] = test & 0xff;
+	array[1] = test >> 8;
+	array[2] = freq & 0xff;
+	array[3] = freq >> 8;
+	array[4] = level & 0xff;
+	array[5] = level >> 8;
+
+	return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
+}
+
+static int cmd_memtypes(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t type, stores[4] = { 0x0001, 0x0002, 0x0004, 0x0008 };
+	int i, err;
+
+	OPT_HELP(0, NULL);
+
+	for (i = 0; i < 4; i++) {
+		memset(array, 0, sizeof(array));
+		array[0] = stores[i] & 0xff;
+		array[1] = stores[i] >> 8;
+
+		err = transport_read(transport, CSR_VARID_PS_MEMORY_TYPE, array, 8);
+		if (err < 0)
+			continue;
+
+		type = array[2] + (array[3] << 8);
+
+		printf("%s (0x%04x) = %s (%d)\n", storestostr(stores[i]),
+					stores[i], memorytostr(type), type);
+	}
+
+	return 0;
+}
+
+static struct option pskey_options[] = {
+	{ "stores",	1, 0, 's' },
+	{ "reset",	0, 0, 'r' },
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static int opt_pskey(int argc, char *argv[], uint16_t *stores, int *reset, int *help)
+{
+	int opt;
+
+	while ((opt=getopt_long(argc, argv, "+s:rh", pskey_options, NULL)) != EOF) {
+		switch (opt) {
+		case 's':
+			if (!stores)
+				break;
+			if (!strcasecmp(optarg, "default"))
+				*stores = 0x0000;
+			else if (!strcasecmp(optarg, "implementation"))
+				*stores = 0x0001;
+			else if (!strcasecmp(optarg, "factory"))
+				*stores = 0x0002;
+			else if (!strcasecmp(optarg, "rom"))
+				*stores = 0x0004;
+			else if (!strcasecmp(optarg, "ram"))
+				*stores = 0x0008;
+			else if (!strcasecmp(optarg, "psi"))
+				*stores = 0x0001;
+			else if (!strcasecmp(optarg, "psf"))
+				*stores = 0x0002;
+			else if (!strcasecmp(optarg, "psrom"))
+				*stores = 0x0004;
+			else if (!strcasecmp(optarg, "psram"))
+				*stores = 0x0008;
+			else if (!strncasecmp(optarg, "0x", 2))
+				*stores = strtol(optarg, NULL, 16);
+			else
+				*stores = atoi(optarg);
+			break;
+
+		case 'r':
+			if (reset)
+				*reset = 1;
+			break;
+
+		case 'h':
+			if (help)
+				*help = 1;
+			break;
+		}
+	}
+
+	return optind;
+}
+
+#define OPT_PSKEY(min, max, stores, reset, help) \
+		opt_pskey(argc, argv, (stores), (reset), (help)); \
+		argc -= optind; argv += optind; optind = 0; \
+		OPT_RANGE((min), (max))
+
+static int cmd_psget(int transport, int argc, char *argv[])
+{
+	uint8_t array[128];
+	uint16_t pskey, length, value, stores = CSR_STORES_DEFAULT;
+	uint32_t val32;
+	int i, err, reset = 0;
+
+	memset(array, 0, sizeof(array));
+
+	OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+	if (strncasecmp(argv[0], "0x", 2)) {
+		pskey = atoi(argv[0]);
+
+		for (i = 0; storage[i].pskey; i++) {
+			if (strcasecmp(storage[i].str, argv[0]))
+				continue;
+
+			pskey = storage[i].pskey;
+			break;
+		}
+	} else
+		pskey = strtol(argv[0] + 2, NULL, 16);
+
+	memset(array, 0, sizeof(array));
+	array[0] = pskey & 0xff;
+	array[1] = pskey >> 8;
+	array[2] = stores & 0xff;
+	array[3] = stores >> 8;
+
+	err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+	if (err < 0)
+		return err;
+
+	length = array[2] + (array[3] << 8);
+	if (length + 6 > (int) sizeof(array) / 2)
+		return -EIO;
+
+	memset(array, 0, sizeof(array));
+	array[0] = pskey & 0xff;
+	array[1] = pskey >> 8;
+	array[2] = length & 0xff;
+	array[3] = length >> 8;
+	array[4] = stores & 0xff;
+	array[5] = stores >> 8;
+
+	err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
+	if (err < 0)
+		return err;
+
+	switch (length) {
+	case 1:
+		value = array[6] | (array[7] << 8);
+		printf("%s: 0x%04x (%d)\n", csr_pskeytostr(pskey), value, value);
+		break;
+
+	case 2:
+		val32 = array[8] | (array[9] << 8) | (array[6] << 16) | (array[7] << 24);
+		printf("%s: 0x%08x (%d)\n", csr_pskeytostr(pskey), val32, val32);
+		break;
+
+	default:
+		printf("%s:", csr_pskeytostr(pskey));
+		for (i = 0; i < length; i++)
+			printf(" 0x%02x%02x", array[(i * 2) + 6], array[(i * 2) + 7]);
+		printf("\n");
+		break;
+	}
+
+	if (reset)
+		transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+	return err;
+}
+
+static int cmd_psset(int transport, int argc, char *argv[])
+{
+	uint8_t array[128];
+	uint16_t pskey, length, value, stores = CSR_STORES_PSRAM;
+	uint32_t val32;
+	int i, err, reset = 0;
+
+	memset(array, 0, sizeof(array));
+
+	OPT_PSKEY(2, 81, &stores, &reset, NULL);
+
+	if (strncasecmp(argv[0], "0x", 2)) {
+		pskey = atoi(argv[0]);
+
+		for (i = 0; storage[i].pskey; i++) {
+			if (strcasecmp(storage[i].str, argv[0]))
+				continue;
+
+			pskey = storage[i].pskey;
+			break;
+		}
+	} else
+		pskey = strtol(argv[0] + 2, NULL, 16);
+
+	memset(array, 0, sizeof(array));
+	array[0] = pskey & 0xff;
+	array[1] = pskey >> 8;
+	array[2] = stores & 0xff;
+	array[3] = stores >> 8;
+
+	err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+	if (err < 0)
+		return err;
+
+	length = array[2] + (array[3] << 8);
+	if (length + 6 > (int) sizeof(array) / 2)
+		return -EIO;
+
+	memset(array, 0, sizeof(array));
+	array[0] = pskey & 0xff;
+	array[1] = pskey >> 8;
+	array[2] = length & 0xff;
+	array[3] = length >> 8;
+	array[4] = stores & 0xff;
+	array[5] = stores >> 8;
+
+	argc--;
+	argv++;
+
+	switch (length) {
+	case 1:
+		if (argc != 1) {
+			errno = E2BIG;
+			return -1;
+		}
+
+		if (!strncasecmp(argv[0], "0x", 2))
+			value = strtol(argv[0] + 2, NULL, 16);
+		else
+			value = atoi(argv[0]);
+
+		array[6] = value & 0xff;
+		array[7] = value >> 8;
+		break;
+
+	case 2:
+		if (argc != 1) {
+			errno = E2BIG;
+			return -1;
+		}
+
+		if (!strncasecmp(argv[0], "0x", 2))
+			val32 = strtol(argv[0] + 2, NULL, 16);
+		else
+			val32 = atoi(argv[0]);
+
+		array[6] = (val32 & 0xff0000) >> 16;
+		array[7] = val32 >> 24;
+		array[8] = val32 & 0xff;
+		array[9] = (val32 & 0xff00) >> 8;
+		break;
+
+	default:
+		if (argc != length * 2) {
+			errno = EINVAL;
+			return -1;
+		}
+
+		for (i = 0; i < length * 2; i++)
+			if (!strncasecmp(argv[0], "0x", 2))
+				array[i + 6] = strtol(argv[i] + 2, NULL, 16);
+			else
+				array[i + 6] = atoi(argv[i]);
+		break;
+	}
+
+	err = transport_write(transport, CSR_VARID_PS, array, (length + 3) * 2);
+	if (err < 0)
+		return err;
+
+	if (reset)
+		transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+	return err;
+}
+
+static int cmd_psclr(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t pskey, stores = CSR_STORES_PSRAM;
+	int i, err, reset = 0;
+
+	OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+	if (strncasecmp(argv[0], "0x", 2)) {
+		pskey = atoi(argv[0]);
+
+		for (i = 0; storage[i].pskey; i++) {
+			if (strcasecmp(storage[i].str, argv[0]))
+				continue;
+
+			pskey = storage[i].pskey;
+			break;
+		}
+	} else
+		pskey = strtol(argv[0] + 2, NULL, 16);
+
+	memset(array, 0, sizeof(array));
+	array[0] = pskey & 0xff;
+	array[1] = pskey >> 8;
+	array[2] = stores & 0xff;
+	array[3] = stores >> 8;
+
+	err = transport_write(transport, CSR_VARID_PS_CLR_STORES, array, 8);
+	if (err < 0)
+		return err;
+
+	if (reset)
+		transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+	return err;
+}
+
+static int cmd_pslist(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
+	int err, reset = 0;
+
+	OPT_PSKEY(0, 0, &stores, &reset, NULL);
+
+	while (1) {
+		memset(array, 0, sizeof(array));
+		array[0] = pskey & 0xff;
+		array[1] = pskey >> 8;
+		array[2] = stores & 0xff;
+		array[3] = stores >> 8;
+
+		err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
+		if (err < 0)
+			break;
+
+		pskey = array[4] + (array[5] << 8);
+		if (pskey == 0x0000)
+			break;
+
+		memset(array, 0, sizeof(array));
+		array[0] = pskey & 0xff;
+		array[1] = pskey >> 8;
+		array[2] = stores & 0xff;
+		array[3] = stores >> 8;
+
+		err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+		if (err < 0)
+			continue;
+
+		length = array[2] + (array[3] << 8);
+
+		printf("0x%04x - %s (%d bytes)\n", pskey,
+					csr_pskeytostr(pskey), length * 2);
+	}
+
+	if (reset)
+		transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+	return 0;
+}
+
+static int cmd_psread(int transport, int argc, char *argv[])
+{
+	uint8_t array[256];
+	uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
+	char *str, val[7];
+	int i, err, reset = 0;
+
+	OPT_PSKEY(0, 0, &stores, &reset, NULL);
+
+	while (1) {
+		memset(array, 0, sizeof(array));
+		array[0] = pskey & 0xff;
+		array[1] = pskey >> 8;
+		array[2] = stores & 0xff;
+		array[3] = stores >> 8;
+
+		err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
+		if (err < 0)
+			break;
+
+		pskey = array[4] + (array[5] << 8);
+		if (pskey == 0x0000)
+			break;
+
+		memset(array, 0, sizeof(array));
+		array[0] = pskey & 0xff;
+		array[1] = pskey >> 8;
+		array[2] = stores & 0xff;
+		array[3] = stores >> 8;
+
+		err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+		if (err < 0)
+			continue;
+
+		length = array[2] + (array[3] << 8);
+		if (length + 6 > (int) sizeof(array) / 2)
+			continue;
+
+		memset(array, 0, sizeof(array));
+		array[0] = pskey & 0xff;
+		array[1] = pskey >> 8;
+		array[2] = length & 0xff;
+		array[3] = length >> 8;
+		array[4] = stores & 0xff;
+		array[5] = stores >> 8;
+
+		err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
+		if (err < 0)
+			continue;
+
+		str = csr_pskeytoval(pskey);
+		if (!strcasecmp(str, "UNKNOWN")) {
+			sprintf(val, "0x%04x", pskey);
+			str = NULL;
+		}
+
+		printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
+						str ? str : val, pskey);
+		for (i = 0; i < length; i++)
+			printf(" %02x%02x", array[(i * 2) + 7], array[(i * 2) + 6]);
+		printf("\n");
+	}
+
+	if (reset)
+		transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+	return 0;
+}
+
+static int cmd_psload(int transport, int argc, char *argv[])
+{
+	uint8_t array[256];
+	uint16_t pskey, length, size, stores = CSR_STORES_PSRAM;
+	char *str, val[7];
+	int err, reset = 0;
+
+	OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+	psr_read(argv[0]);
+
+	memset(array, 0, sizeof(array));
+	size = sizeof(array) - 6;
+
+	while (psr_get(&pskey, array + 6, &size) == 0) {
+		str = csr_pskeytoval(pskey);
+		if (!strcasecmp(str, "UNKNOWN")) {
+			sprintf(val, "0x%04x", pskey);
+			str = NULL;
+		}
+
+		printf("Loading %s%s ... ", str ? "PSKEY_" : "",
+							str ? str : val);
+		fflush(stdout);
+
+		length = size / 2;
+
+		array[0] = pskey & 0xff;
+		array[1] = pskey >> 8;
+		array[2] = length & 0xff;
+		array[3] = length >> 8;
+		array[4] = stores & 0xff;
+		array[5] = stores >> 8;
+
+		err = transport_write(transport, CSR_VARID_PS, array, size + 6);
+
+		printf("%s\n", err < 0 ? "failed" : "done");
+
+		memset(array, 0, sizeof(array));
+		size = sizeof(array) - 6;
+	}
+
+	if (reset)
+		transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+	return 0;
+}
+
+static int cmd_pscheck(int transport, int argc, char *argv[])
+{
+	uint8_t array[256];
+	uint16_t pskey, size;
+	int i;
+
+	OPT_HELP(1, NULL);
+
+	psr_read(argv[0]);
+
+	while (psr_get(&pskey, array, &size) == 0) {
+		printf("0x%04x =", pskey);
+		for (i = 0; i < size; i++)
+			printf(" 0x%02x", array[i]);
+		printf("\n");
+	}
+
+	return 0;
+}
+
+static int cmd_adc(int transport, int argc, char *argv[])
+{
+	uint8_t array[8];
+	uint16_t mux, value;
+	int err;
+
+	OPT_HELP(1, NULL);
+
+	if (!strncasecmp(argv[0], "0x", 2))
+		mux = strtol(argv[0], NULL, 16);
+	else
+		mux = atoi(argv[0]);
+
+	/* Request an ADC read from a particular mux'ed input */
+	memset(array, 0, sizeof(array));
+	array[0] = mux & 0xff;
+	array[1] = mux >> 8;
+
+	err = transport_write(transport, CSR_VARID_ADC, array, 2);
+	if (err < 0) {
+		errno = -err;
+		return -1;
+	}
+
+	/* have to wait a short while, then read result */
+	usleep(50000);
+	err = transport_read(transport, CSR_VARID_ADC_RES, array, 8);
+	if (err < 0) {
+		errno = -err;
+		return -1;
+	}
+
+	mux = array[0] | (array[1] << 8);
+	value = array[4] | (array[5] << 8);
+
+	printf("ADC value from Mux 0x%02x : 0x%04x (%s)\n", mux, value,
+					array[2] == 1 ? "valid" : "invalid");
+
+	return 0;
+}
+
+static struct {
+	char *str;
+	int (*func)(int transport, int argc, char *argv[]);
+	char *arg;
+	char *doc;
+} commands[] = {
+	{ "builddef",  cmd_builddef,  "",                    "Get build definitions"          },
+	{ "keylen",    cmd_keylen,    "<handle>",            "Get current crypt key length"   },
+	{ "clock",     cmd_clock,     "",                    "Get local Bluetooth clock"      },
+	{ "rand",      cmd_rand,      "",                    "Get random number"              },
+	{ "chiprev",   cmd_chiprev,   "",                    "Get chip revision"              },
+	{ "buildname", cmd_buildname, "",                    "Get the full build name"        },
+	{ "panicarg",  cmd_panicarg,  "",                    "Get panic code argument"        },
+	{ "faultarg",  cmd_faultarg,  "",                    "Get fault code argument"        },
+	{ "coldreset", cmd_coldreset, "",                    "Perform cold reset"             },
+	{ "warmreset", cmd_warmreset, "",                    "Perform warm reset"             },
+	{ "disabletx", cmd_disabletx, "",                    "Disable TX on the device"       },
+	{ "enabletx",  cmd_enabletx,  "",                    "Enable TX on the device"        },
+	{ "singlechan",cmd_singlechan,"<channel>",           "Lock radio on specific channel" },
+	{ "hoppingon", cmd_hoppingon, "",                    "Revert to channel hopping"      },
+	{ "rttxdata1", cmd_rttxdata1, "<freq> <level>",      "TXData1 radio test"             },
+	{ "radiotest", cmd_radiotest, "<freq> <level> <id>", "Run radio tests"                },
+	{ "memtypes",  cmd_memtypes,  NULL,                  "Get memory types"               },
+	{ "psget",     cmd_psget,     "<key>",               "Get value for PS key"           },
+	{ "psset",     cmd_psset,     "<key> <value>",       "Set value for PS key"           },
+	{ "psclr",     cmd_psclr,     "<key>",               "Clear value for PS key"         },
+	{ "pslist",    cmd_pslist,    NULL,                  "List all PS keys"               },
+	{ "psread",    cmd_psread,    NULL,                  "Read all PS keys"               },
+	{ "psload",    cmd_psload,    "<file>",              "Load all PS keys from PSR file" },
+	{ "pscheck",   cmd_pscheck,   "<file>",              "Check PSR file"                 },
+	{ "adc",       cmd_adc,       "<mux>",               "Read ADC value of <mux> input"  },
+	{ NULL }
+};
+
+static void usage(void)
+{
+	int i, pos = 0;
+
+	printf("bccmd - Utility for the CSR BCCMD interface\n\n");
+	printf("Usage:\n"
+		"\tbccmd [options] <command>\n\n");
+
+	printf("Options:\n"
+		"\t-t <transport>     Select the transport\n"
+		"\t-d <device>        Select the device\n"
+		"\t-b <bcsp rate>     Select the bcsp transfer rate\n"
+		"\t-h, --help         Display help\n"
+		"\n");
+
+	printf("Transports:\n"
+		"\tHCI USB BCSP H4 3WIRE\n\n");
+
+	printf("Commands:\n");
+	for (i = 0; commands[i].str; i++)
+		printf("\t%-10s %-20s\t%s\n", commands[i].str,
+		commands[i].arg ? commands[i].arg : " ",
+		commands[i].doc);
+	printf("\n");
+
+	printf("Keys:\n\t");
+	for (i = 0; storage[i].pskey; i++) {
+		printf("%s ", storage[i].str);
+		pos += strlen(storage[i].str) + 1;
+		if (pos > 60) {
+			printf("\n\t");
+			pos = 0;
+		}
+	}
+	printf("\n");
+}
+
+static struct option main_options[] = {
+	{ "transport",	1, 0, 't' },
+	{ "device",	1, 0, 'd' },
+	{ "bcsprate", 1, 0, 'b'},
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	char *device = NULL;
+	int i, err, opt, transport = CSR_TRANSPORT_HCI;
+	speed_t bcsp_rate = B38400;
+
+	while ((opt=getopt_long(argc, argv, "+t:d:i:b:h", main_options, NULL)) != EOF) {
+		switch (opt) {
+		case 't':
+			if (!strcasecmp(optarg, "hci"))
+				transport = CSR_TRANSPORT_HCI;
+			else if (!strcasecmp(optarg, "usb"))
+				transport = CSR_TRANSPORT_USB;
+			else if (!strcasecmp(optarg, "bcsp"))
+				transport = CSR_TRANSPORT_BCSP;
+			else if (!strcasecmp(optarg, "h4"))
+				transport = CSR_TRANSPORT_H4;
+			else if (!strcasecmp(optarg, "h5"))
+				transport = CSR_TRANSPORT_3WIRE;
+			else if (!strcasecmp(optarg, "3wire"))
+				transport = CSR_TRANSPORT_3WIRE;
+			else if (!strcasecmp(optarg, "twutl"))
+				transport = CSR_TRANSPORT_3WIRE;
+			else
+				transport = CSR_TRANSPORT_UNKNOWN;
+			break;
+
+		case 'd':
+		case 'i':
+			device = strdup(optarg);
+			break;
+		case 'b':
+			switch (atoi(optarg)) {
+			case 9600: bcsp_rate = B9600; break;
+			case 19200: bcsp_rate = B19200; break;
+			case 38400: bcsp_rate = B38400; break;
+			case 57600: bcsp_rate = B57600; break;
+			case 115200: bcsp_rate = B115200; break;
+			case 230400: bcsp_rate = B230400; break;
+			case 460800: bcsp_rate = B460800; break;
+			case 500000: bcsp_rate = B500000; break;
+			case 576000: bcsp_rate = B576000; break;
+			case 921600: bcsp_rate = B921600; break;
+			case 1000000: bcsp_rate = B1000000; break;
+			case 1152000: bcsp_rate = B1152000; break;
+			case 1500000: bcsp_rate = B1500000; break;
+			case 2000000: bcsp_rate = B2000000; break;
+#ifdef B2500000
+			case 2500000: bcsp_rate = B2500000; break;
+#endif
+#ifdef B3000000
+			case 3000000: bcsp_rate = B3000000; break;
+#endif
+#ifdef B3500000
+			case 3500000: bcsp_rate = B3500000; break;
+#endif
+#ifdef B4000000
+			case 4000000: bcsp_rate = B4000000; break;
+#endif
+			default:
+				printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n");
+				bcsp_rate = B38400;
+			}
+			break;
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		usage();
+		exit(1);
+	}
+
+	if (transport_open(transport, device, bcsp_rate) < 0)
+		exit(1);
+
+	free(device);
+
+	for (i = 0; commands[i].str; i++) {
+		if (strcasecmp(commands[i].str, argv[0]))
+			continue;
+
+		err = commands[i].func(transport, argc, argv);
+
+		transport_close(transport);
+
+		if (err < 0) {
+			fprintf(stderr, "Can't execute command: %s (%d)\n",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		exit(0);
+	}
+
+	fprintf(stderr, "Unsupported command\n");
+
+	transport_close(transport);
+
+	exit(1);
+}
diff --git a/bluez/repo/tools/bdaddr.1 b/bluez/repo/tools/bdaddr.1
new file mode 100644
index 0000000..efb77d2
--- /dev/null
+++ b/bluez/repo/tools/bdaddr.1
@@ -0,0 +1,68 @@
+.TH BDADDR 1 "Sep 27 2005" BlueZ "Linux System Administration"
+.SH NAME
+bdaddr \- Utility for changing the Bluetooth device address
+.SH SYNOPSIS
+.B bdaddr
+.br
+.B bdaddr -h
+.br
+.B bdaddr [-i <dev>] [-r] [-t] [new bdaddr]
+
+.SH DESCRIPTION
+.LP
+.B
+bdaddr
+is used to query or set the local Bluetooth device address (BD_ADDR). If run
+with no arguments,
+.B
+bdaddr
+prints the chip manufacturer's name, and the current BD_ADDR. If the IEEE OUI
+index file "oui.txt" is installed on the system, the BD_ADDR owner will be
+displayed. If the optional [new bdaddr] argument is given, the device will be
+reprogrammed with that address. This can either be permanent or temporary, as
+specified by the -t flag. In both cases, the device must be reset before the
+new address will become active. This can be done with a 'soft' reset by
+specifying the -r flag, or a 'hard' reset by removing and replugging the
+device. A 'hard' reset will cause the address to revert to the current
+non-volatile value.
+.PP
+.B
+bdaddr
+uses manufacturer specific commands to set the address, and is therefore
+device specific. For this reason, not all devices are supported, and not all
+options are supported on all devices.
+Current supported manufacturers are:
+.B Ericsson, Cambridge Silicon Radio (CSR), Texas Instruments (TI), Zeevo
+and
+.B ST Microelectronics (ST)
+
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i\ <dev>
+Specify a particular device to operate on. If not specified, default is the
+first available device.
+.TP
+.BI -r
+Reset device and make new BD_ADDR active.
+.B
+CSR
+devices only.
+.TP
+.BI -t
+Temporary change. Do not write to non-volatile memory.
+.B
+CSR
+devices only.
+.SH FILES
+.TP
+.I
+/usr/share/misc/oui.txt
+IEEE Organizationally Unique Identifier master file.
+Manually update from: http://standards.ieee.org/regauth/oui/oui.txt
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/bluez/repo/tools/bdaddr.c b/bluez/repo/tools/bdaddr.c
new file mode 100644
index 0000000..952e990
--- /dev/null
+++ b/bluez/repo/tools/bdaddr.c
@@ -0,0 +1,479 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/oui.h"
+
+static int transient = 0;
+
+static int generic_reset_device(int dd)
+{
+	bdaddr_t bdaddr;
+	int err;
+
+	err = hci_send_cmd(dd, 0x03, 0x0003, 0, NULL);
+	if (err < 0)
+		return err;
+
+	return hci_read_bd_addr(dd, &bdaddr, 10000);
+}
+
+#define OCF_ERICSSON_WRITE_BD_ADDR	0x000d
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) ericsson_write_bd_addr_cp;
+
+static int ericsson_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	struct hci_request rq;
+	ericsson_write_bd_addr_cp cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = OCF_ERICSSON_WRITE_BD_ADDR;
+	rq.cparam = &cp;
+	rq.clen   = sizeof(cp);
+	rq.rparam = NULL;
+	rq.rlen   = 0;
+
+	if (hci_send_req(dd, &rq, 1000) < 0)
+		return -1;
+
+	return 0;
+}
+
+#define OCF_ERICSSON_STORE_IN_FLASH	0x0022
+typedef struct {
+	uint8_t		user_id;
+	uint8_t		flash_length;
+	uint8_t		flash_data[253];
+} __attribute__ ((packed)) ericsson_store_in_flash_cp;
+
+static int ericsson_store_in_flash(int dd, uint8_t user_id, uint8_t flash_length, uint8_t *flash_data)
+{
+	struct hci_request rq;
+	ericsson_store_in_flash_cp cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.user_id = user_id;
+	cp.flash_length = flash_length;
+	if (flash_length > 0)
+		memcpy(cp.flash_data, flash_data, flash_length);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = OCF_ERICSSON_STORE_IN_FLASH;
+	rq.cparam = &cp;
+	rq.clen   = sizeof(cp);
+	rq.rparam = NULL;
+	rq.rlen   = 0;
+
+	if (hci_send_req(dd, &rq, 1000) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int csr_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	unsigned char cmd[] = { 0x02, 0x00, 0x0c, 0x00, 0x11, 0x47, 0x03, 0x70,
+				0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	if (transient)
+		cmd[14] = 0x08;
+
+	cmd[16] = bdaddr->b[2];
+	cmd[17] = 0x00;
+	cmd[18] = bdaddr->b[0];
+	cmd[19] = bdaddr->b[1];
+	cmd[20] = bdaddr->b[3];
+	cmd[21] = 0x00;
+	cmd[22] = bdaddr->b[4];
+	cmd[23] = bdaddr->b[5];
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int csr_reset_device(int dd)
+{
+	unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00,
+				0x00, 0x00, 0x01, 0x40, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	if (transient)
+		cmd[6] = 0x02;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	return 0;
+}
+
+#define OCF_TI_WRITE_BD_ADDR		0x0006
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) ti_write_bd_addr_cp;
+
+static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	struct hci_request rq;
+	ti_write_bd_addr_cp cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = OCF_TI_WRITE_BD_ADDR;
+	rq.cparam = &cp;
+	rq.clen   = sizeof(cp);
+	rq.rparam = NULL;
+	rq.rlen   = 0;
+
+	if (hci_send_req(dd, &rq, 1000) < 0)
+		return -1;
+
+	return 0;
+}
+
+#define OCF_BCM_WRITE_BD_ADDR		0x0001
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) bcm_write_bd_addr_cp;
+
+static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	struct hci_request rq;
+	bcm_write_bd_addr_cp cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = OCF_BCM_WRITE_BD_ADDR;
+	rq.cparam = &cp;
+	rq.clen   = sizeof(cp);
+	rq.rparam = NULL;
+	rq.rlen   = 0;
+
+	if (hci_send_req(dd, &rq, 1000) < 0)
+		return -1;
+
+	return 0;
+}
+
+#define OCF_ZEEVO_WRITE_BD_ADDR		0x0001
+typedef struct {
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) zeevo_write_bd_addr_cp;
+
+static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	struct hci_request rq;
+	zeevo_write_bd_addr_cp cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = OCF_ZEEVO_WRITE_BD_ADDR;
+	rq.cparam = &cp;
+	rq.clen   = sizeof(cp);
+	rq.rparam = NULL;
+	rq.rlen   = 0;
+
+	if (hci_send_req(dd, &rq, 1000) < 0)
+		return -1;
+
+	return 0;
+}
+
+#define OCF_MRVL_WRITE_BD_ADDR		0x0022
+typedef struct {
+	uint8_t		parameter_id;
+	uint8_t		bdaddr_len;
+	bdaddr_t	bdaddr;
+} __attribute__ ((packed)) mrvl_write_bd_addr_cp;
+
+static int mrvl_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	mrvl_write_bd_addr_cp cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.parameter_id = 0xFE;
+	cp.bdaddr_len = 6;
+	bacpy(&cp.bdaddr, bdaddr);
+
+	if (hci_send_cmd(dd, OGF_VENDOR_CMD, OCF_MRVL_WRITE_BD_ADDR,
+							sizeof(cp), &cp) < 0)
+		return -1;
+
+	sleep(1);
+	return 0;
+}
+
+static int st_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+	return ericsson_store_in_flash(dd, 0xfe, 6, (uint8_t *) bdaddr);
+}
+
+static struct {
+	uint16_t compid;
+	int (*write_bd_addr)(int dd, bdaddr_t *bdaddr);
+	int (*reset_device)(int dd);
+} vendor[] = {
+	{ 0,		ericsson_write_bd_addr,	NULL			},
+	{ 10,		csr_write_bd_addr,	csr_reset_device	},
+	{ 13,		ti_write_bd_addr,	NULL			},
+	{ 15,		bcm_write_bd_addr,	generic_reset_device	},
+	{ 18,		zeevo_write_bd_addr,	NULL			},
+	{ 48,		st_write_bd_addr,	generic_reset_device	},
+	{ 57,		ericsson_write_bd_addr,	generic_reset_device	},
+	{ 72,		mrvl_write_bd_addr,	generic_reset_device	},
+	{ 65535,	NULL,			NULL			},
+};
+
+static void usage(void)
+{
+	printf("bdaddr - Utility for changing the Bluetooth device address\n\n");
+	printf("Usage:\n"
+		"\tbdaddr [-i <dev>] [-r] [-t] [new bdaddr]\n");
+}
+
+static struct option main_options[] = {
+	{ "device",	1, 0, 'i' },
+	{ "reset",	0, 0, 'r' },
+	{ "transient",	0, 0, 't' },
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	struct hci_dev_info di;
+	struct hci_version ver;
+	bdaddr_t bdaddr;
+	char addr[18], *comp;
+	int i, dd, opt, dev = 0, reset = 0;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while ((opt=getopt_long(argc, argv, "+i:rth", main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'i':
+			dev = hci_devid(optarg);
+			if (dev < 0) {
+				perror("Invalid device");
+				exit(1);
+			}
+			break;
+
+		case 'r':
+			reset = 1;
+			break;
+
+		case 't':
+			transient = 1;
+			break;
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	dd = hci_open_dev(dev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (hci_devinfo(dev, &di) < 0) {
+		fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		hci_close_dev(dd);
+		exit(1);
+	}
+
+	if (hci_read_local_version(dd, &ver, 1000) < 0) {
+		fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		hci_close_dev(dd);
+		exit(1);
+	}
+
+	if (!bacmp(&di.bdaddr, BDADDR_ANY)) {
+		if (hci_read_bd_addr(dd, &bdaddr, 1000) < 0) {
+			fprintf(stderr, "Can't read address for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+			hci_close_dev(dd);
+			exit(1);
+		}
+	} else
+		bacpy(&bdaddr, &di.bdaddr);
+
+	printf("Manufacturer:   %s (%d)\n",
+			bt_compidtostr(ver.manufacturer), ver.manufacturer);
+
+	comp = batocomp(&bdaddr);
+
+	ba2str(&bdaddr, addr);
+	printf("Device address: %s", addr);
+
+	if (comp) {
+		printf(" (%s)\n", comp);
+		free(comp);
+	} else
+		printf("\n");
+
+	if (argc < 1) {
+		hci_close_dev(dd);
+		exit(0);
+	}
+
+	str2ba(argv[0], &bdaddr);
+	if (!bacmp(&bdaddr, BDADDR_ANY)) {
+		hci_close_dev(dd);
+		exit(0);
+	}
+
+	for (i = 0; vendor[i].compid != 65535; i++)
+		if (ver.manufacturer == vendor[i].compid) {
+			comp = batocomp(&bdaddr);
+
+			ba2str(&bdaddr, addr);
+			printf("New BD address: %s", addr);
+
+			if (comp) {
+				printf(" (%s)\n\n", comp);
+				free(comp);
+			} else
+				printf("\n\n");
+
+
+			if (vendor[i].write_bd_addr(dd, &bdaddr) < 0) {
+				fprintf(stderr, "Can't write new address\n");
+				hci_close_dev(dd);
+				exit(1);
+			}
+
+			printf("Address changed - ");
+
+			if (reset && vendor[i].reset_device) {
+				if (vendor[i].reset_device(dd) < 0) {
+					printf("Reset device manually\n");
+				} else {
+					ioctl(dd, HCIDEVRESET, dev);
+					printf("Device reset successfully\n");
+				}
+			} else {
+				printf("Reset device now\n");
+			}
+
+			//ioctl(dd, HCIDEVRESET, dev);
+			//ioctl(dd, HCIDEVDOWN, dev);
+			//ioctl(dd, HCIDEVUP, dev);
+
+			hci_close_dev(dd);
+			exit(0);
+		}
+
+	hci_close_dev(dd);
+
+	printf("\n");
+	fprintf(stderr, "Unsupported manufacturer\n");
+
+	exit(1);
+}
diff --git a/bluez/repo/tools/bluetooth-player.c b/bluez/repo/tools/bluetooth-player.c
new file mode 100644
index 0000000..9e19997
--- /dev/null
+++ b/bluez/repo/tools/bluetooth-player.c
@@ -0,0 +1,1454 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+#include "client/display.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 "[bluetooth]" COLOR_OFF "# "
+#define PROMPT_OFF	"[bluetooth]# "
+
+#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#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;
+static GSList *folders = NULL;
+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();
+}
+
+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);
+}
+
+static bool check_default_player(void)
+{
+	if (!default_player) {
+		rl_printf("No default player available\n");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void play_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 play: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Play successful\n");
+}
+
+static GDBusProxy *find_item(const char *path)
+{
+	GSList *l;
+
+	for (l = items; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static void cmd_play_item(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	proxy = find_item(argv[1]);
+	if (proxy == NULL) {
+		rl_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");
+		return;
+	}
+
+	rl_printf("Attempting to play %s\n", argv[1]);
+}
+
+static void cmd_play(int argc, char *argv[])
+{
+	if (argc > 1)
+		return cmd_play_item(argc, argv);
+
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "Play", NULL, play_reply,
+							NULL, NULL) == FALSE) {
+		rl_printf("Failed to play\n");
+		return;
+	}
+
+	rl_printf("Attempting to play\n");
+}
+
+static void pause_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 pause: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Pause successful\n");
+}
+
+static void cmd_pause(int argc, char *argv[])
+{
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "Pause", NULL,
+					pause_reply, NULL, NULL) == FALSE) {
+		rl_printf("Failed to play\n");
+		return;
+	}
+
+	rl_printf("Attempting to pause\n");
+}
+
+static void stop_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 stop: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Stop successful\n");
+}
+
+static void cmd_stop(int argc, char *argv[])
+{
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "Stop", NULL, stop_reply,
+							NULL, NULL) == FALSE) {
+		rl_printf("Failed to stop\n");
+		return;
+	}
+
+	rl_printf("Attempting to stop\n");
+}
+
+static void next_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 jump to next: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Next successful\n");
+}
+
+static void cmd_next(int argc, char *argv[])
+{
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "Next", NULL, next_reply,
+							NULL, NULL) == FALSE) {
+		rl_printf("Failed to jump to next\n");
+		return;
+	}
+
+	rl_printf("Attempting to jump to next\n");
+}
+
+static void previous_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 jump to previous: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Previous successful\n");
+}
+
+static void cmd_previous(int argc, char *argv[])
+{
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "Previous", NULL,
+					previous_reply, NULL, NULL) == FALSE) {
+		rl_printf("Failed to jump to previous\n");
+		return;
+	}
+
+	rl_printf("Attempting to jump to previous\n");
+}
+
+static void fast_forward_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 fast forward: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("FastForward successful\n");
+}
+
+static void cmd_fast_forward(int argc, char *argv[])
+{
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "FastForward", NULL,
+				fast_forward_reply, NULL, NULL) == FALSE) {
+		rl_printf("Failed to jump to previous\n");
+		return;
+	}
+
+	rl_printf("Fast forward playback\n");
+}
+
+static void rewind_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 rewind: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Rewind successful\n");
+}
+
+static void cmd_rewind(int argc, char *argv[])
+{
+	if (!check_default_player())
+		return;
+
+	if (g_dbus_proxy_method_call(default_player, "Rewind", NULL,
+					rewind_reply, NULL, NULL) == FALSE) {
+		rl_printf("Failed to rewind\n");
+		return;
+	}
+
+	rl_printf("Rewind playback\n");
+}
+
+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);
+	else
+		rl_printf("Changing %s succeeded\n", str);
+}
+
+static void cmd_equalizer(int argc, char *argv[])
+{
+	char *value;
+	DBusMessageIter iter;
+
+	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");
+		return;
+	}
+
+	value = g_strdup(argv[1]);
+
+	if (g_dbus_proxy_set_property_basic(default_player, "Equalizer",
+						DBUS_TYPE_STRING, &value,
+						generic_callback, value,
+						g_free) == FALSE) {
+		rl_printf("Failed to setting equalizer\n");
+		g_free(value);
+		return;
+	}
+
+	rl_printf("Attempting to set equalizer\n");
+}
+
+static void cmd_repeat(int argc, char *argv[])
+{
+	char *value;
+	DBusMessageIter iter;
+
+	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");
+		return;
+	}
+
+	value = g_strdup(argv[1]);
+
+	if (g_dbus_proxy_set_property_basic(default_player, "Repeat",
+						DBUS_TYPE_STRING, &value,
+						generic_callback, value,
+						g_free) == FALSE) {
+		rl_printf("Failed to set repeat\n");
+		g_free(value);
+		return;
+	}
+
+	rl_printf("Attempting to set repeat\n");
+}
+
+static void cmd_shuffle(int argc, char *argv[])
+{
+	char *value;
+	DBusMessageIter iter;
+
+	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");
+		return;
+	}
+
+	value = g_strdup(argv[1]);
+
+	if (g_dbus_proxy_set_property_basic(default_player, "Shuffle",
+						DBUS_TYPE_STRING, &value,
+						generic_callback, value,
+						g_free) == FALSE) {
+		rl_printf("Failed to set shuffle\n");
+		g_free(value);
+		return;
+	}
+
+	rl_printf("Attempting to set shuffle\n");
+}
+
+static void cmd_scan(int argc, char *argv[])
+{
+	char *value;
+	DBusMessageIter iter;
+
+	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");
+		return;
+	}
+
+	value = g_strdup(argv[1]);
+
+	if (g_dbus_proxy_set_property_basic(default_player, "Shuffle",
+						DBUS_TYPE_STRING, &value,
+						generic_callback, value,
+						g_free) == FALSE) {
+		rl_printf("Failed to set scan\n");
+		g_free(value);
+		return;
+	}
+
+	rl_printf("Attempting to set scan\n");
+}
+
+static char *proxy_description(GDBusProxy *proxy, const char *title,
+						const char *description)
+{
+	const char *path;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	return g_strdup_printf("%s%s%s%s %s ",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					title, path);
+}
+
+static void print_player(GDBusProxy *proxy, const char *description)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Player", description);
+
+	rl_printf("%s%s\n", str, default_player == proxy ? "[default]" : "");
+
+	g_free(str);
+}
+
+static void cmd_list(int argc, char *arg[])
+{
+	GSList *l;
+
+	for (l = players; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+		print_player(proxy, NULL);
+	}
+}
+
+static GDBusProxy *find_player(const char *path)
+{
+	GSList *l;
+
+	for (l = players; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+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;
+	const char *valstr;
+	DBusMessageIter subiter;
+
+	if (iter == NULL) {
+		rl_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);
+		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);
+		break;
+	case DBUS_TYPE_BOOLEAN:
+		dbus_message_iter_get_basic(iter, &valbool);
+		rl_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);
+		break;
+	case DBUS_TYPE_UINT16:
+		dbus_message_iter_get_basic(iter, &valu16);
+		rl_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);
+		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);
+		dbus_message_iter_get_basic(&subiter, &valstr);
+		dbus_message_iter_next(&subiter);
+		print_iter(label, valstr, &subiter);
+		break;
+	default:
+		rl_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 GDBusProxy *find_folder(const char *path)
+{
+	GSList *l;
+
+	for (l = folders; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+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]);
+		return;
+	}
+
+	rl_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "Player");
+	print_property(proxy, "Name");
+	print_property(proxy, "Type");
+	print_property(proxy, "FolderType");
+	print_property(proxy, "Playable");
+	print_property(proxy, "Metadata");
+}
+
+static void cmd_show(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+	GDBusProxy *folder;
+	GDBusProxy *item;
+	DBusMessageIter iter;
+	const char *path;
+
+	if (argc < 2) {
+		if (check_default_player() == FALSE)
+			return;
+
+		proxy = default_player;
+	} else {
+		proxy = find_player(argv[1]);
+		if (!proxy) {
+			rl_printf("Player %s not available\n", argv[1]);
+			return;
+		}
+	}
+
+	rl_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "Name");
+	print_property(proxy, "Repeat");
+	print_property(proxy, "Equalizer");
+	print_property(proxy, "Shuffle");
+	print_property(proxy, "Scan");
+	print_property(proxy, "Status");
+	print_property(proxy, "Position");
+	print_property(proxy, "Track");
+
+	folder = find_folder(g_dbus_proxy_get_path(proxy));
+	if (folder == NULL)
+		return;
+
+	rl_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(folder, "Name");
+	print_property(folder, "NumberOfItems");
+
+	if (!g_dbus_proxy_get_property(proxy, "Playlist", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	item = find_item(path);
+	if (item == NULL)
+		return;
+
+	rl_printf("Playlist %s\n", path);
+
+	print_property(item, "Name");
+}
+
+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]);
+		return;
+	}
+
+	if (default_player == proxy)
+		return;
+
+	default_player = proxy,
+	print_player(proxy, NULL);
+}
+
+static void change_folder_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 change folder: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("ChangeFolder successful\n");
+}
+
+static void change_folder_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *path = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+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");
+		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");
+		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");
+		return;
+	}
+
+	rl_printf("Attempting to change folder\n");
+}
+
+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 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);
+}
+
+struct list_items_args {
+	int start;
+	int end;
+};
+
+static void list_items_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct list_items_args *args = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	if (args->start < 0)
+		goto done;
+
+	dict_append_entry(&dict, "Start", DBUS_TYPE_UINT32, &args->start);
+
+	if (args->end < 0)
+		goto done;
+
+	dict_append_entry(&dict, "End", DBUS_TYPE_UINT32, &args->end);
+
+done:
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void list_items_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 list items: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("ListItems successful\n");
+}
+
+static void cmd_list_items(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+	struct list_items_args *args;
+
+	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");
+		return;
+	}
+
+	args = g_new0(struct list_items_args, 1);
+	args->start = -1;
+	args->end = -1;
+
+	if (argc < 2)
+		goto done;
+
+	errno = 0;
+	args->start = strtol(argv[1], NULL, 10);
+	if (errno != 0) {
+		rl_printf("%s(%d)\n", strerror(errno), errno);
+		g_free(args);
+		return;
+	}
+
+	if (argc < 3)
+		goto done;
+
+	errno = 0;
+	args->end = strtol(argv[2], NULL, 10);
+	if (errno != 0) {
+		rl_printf("%s(%d)\n", strerror(errno), errno);
+		g_free(args);
+		return;
+	}
+
+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");
+		g_free(args);
+		return;
+	}
+
+	rl_printf("Attempting to list items\n");
+}
+
+static void search_setup(DBusMessageIter *iter, void *user_data)
+{
+	char *string = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void search_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 search: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Search successful\n");
+}
+
+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");
+		return;
+	}
+
+	string = g_strdup(argv[1]);
+
+	if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
+				search_reply, string, g_free) == FALSE) {
+		rl_printf("Failed to search\n");
+		g_free(string);
+		return;
+	}
+
+	rl_printf("Attempting to search\n");
+}
+
+static void add_to_nowplaying_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 queue: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_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]);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "AddtoNowPlaying", NULL,
+					add_to_nowplaying_reply, NULL,
+					NULL) == FALSE) {
+		rl_printf("Failed to play\n");
+		return;
+	}
+
+	rl_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[] = {
+	{ "list",         NULL,       cmd_list, "List available players" },
+	{ "show",         "[player]", cmd_show, "Player information" },
+	{ "select",       "<player>", cmd_select, "Select default player" },
+	{ "play",         "[item]",   cmd_play, "Start playback" },
+	{ "pause",        NULL,       cmd_pause, "Pause playback" },
+	{ "stop",         NULL,       cmd_stop, "Stop playback" },
+	{ "next",         NULL,       cmd_next, "Jump to next item" },
+	{ "previous",     NULL,       cmd_previous, "Jump to previous item" },
+	{ "fast-forward", NULL,       cmd_fast_forward,
+						"Fast forward playback" },
+	{ "rewind",       NULL,       cmd_rewind, "Rewind playback" },
+	{ "equalizer",    "<on/off>", cmd_equalizer,
+						"Enable/Disable equalizer"},
+	{ "repeat",       "<singletrack/alltrack/group/off>", cmd_repeat,
+						"Set repeat mode"},
+	{ "shuffle",      "<alltracks/group/off>", cmd_shuffle,
+						"Set shuffle mode"},
+	{ "scan",         "<alltracks/group/off>", cmd_scan,
+						"Set scan mode"},
+	{ "change-folder", "<item>",  cmd_change_folder,
+						"Change current folder" },
+	{ "list-items", "[start] [end]",  cmd_list_items,
+					"List items of current folder" },
+	{ "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);
+
+	if (default_player == NULL)
+		default_player = proxy;
+
+	print_player(proxy, COLORED_NEW);
+}
+
+static void print_folder(GDBusProxy *proxy, const char *description)
+{
+	const char *path;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	rl_printf("%s%s%sFolder %s\n", description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					path);
+}
+
+static void folder_added(GDBusProxy *proxy)
+{
+	folders = g_slist_append(folders, proxy);
+
+	print_folder(proxy, COLORED_NEW);
+}
+
+static void print_item(GDBusProxy *proxy, const char *description)
+{
+	const char *path, *name;
+	DBusMessageIter iter;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (g_dbus_proxy_get_property(proxy, "Name", &iter))
+		dbus_message_iter_get_basic(&iter, &name);
+	else
+		name = "<unknown>";
+
+	rl_printf("%s%s%sItem %s %s\n", description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					path, name);
+}
+
+static void item_added(GDBusProxy *proxy)
+{
+	items = g_slist_append(items, proxy);
+
+	print_item(proxy, COLORED_NEW);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
+		player_added(proxy);
+	else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
+		folder_added(proxy);
+	else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
+		item_added(proxy);
+}
+
+static void player_removed(GDBusProxy *proxy)
+{
+	print_player(proxy, COLORED_DEL);
+
+	if (default_player == proxy)
+		default_player = NULL;
+
+	players = g_slist_remove(players, proxy);
+}
+
+static void folder_removed(GDBusProxy *proxy)
+{
+	folders = g_slist_remove(folders, proxy);
+
+	print_folder(proxy, COLORED_DEL);
+}
+
+static void item_removed(GDBusProxy *proxy)
+{
+	items = g_slist_remove(items, proxy);
+
+	print_item(proxy, COLORED_DEL);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
+		player_removed(proxy);
+	if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
+		folder_removed(proxy);
+	if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
+		item_removed(proxy);
+}
+
+static void player_property_changed(GDBusProxy *proxy, const char *name,
+						DBusMessageIter *iter)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Player", COLORED_CHG);
+	print_iter(str, name, iter);
+	g_free(str);
+}
+
+static void folder_property_changed(GDBusProxy *proxy, const char *name,
+						DBusMessageIter *iter)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Folder", COLORED_CHG);
+	print_iter(str, name, iter);
+	g_free(str);
+}
+
+static void item_property_changed(GDBusProxy *proxy, const char *name,
+						DBusMessageIter *iter)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Item", COLORED_CHG);
+	print_iter(str, name, iter);
+	g_free(str);
+}
+
+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, BLUEZ_MEDIA_PLAYER_INTERFACE))
+		player_property_changed(proxy, name, iter);
+	else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
+		folder_property_changed(proxy, name, iter);
+	else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
+		item_property_changed(proxy, name, iter);
+}
+
+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);
+	}
+
+	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);
+	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
+
+	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+							property_changed, NULL);
+
+	g_main_loop_run(main_loop);
+
+	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/bluez/repo/tools/bnep-tester.c b/bluez/repo/tools/bnep-tester.c
new file mode 100644
index 0000000..ec4ad26
--- /dev/null
+++ b/bluez/repo/tools/bnep-tester.c
@@ -0,0 +1,309 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+
+struct test_data {
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	const void *test_data;
+	unsigned int io_id;
+	uint16_t conn_handle;
+};
+
+struct rfcomm_client_data {
+	uint8_t server_channel;
+	uint8_t client_channel;
+	int expected_connect_err;
+	const uint8_t *send_data;
+	const uint8_t *read_data;
+	uint16_t data_len;
+};
+
+struct rfcomm_server_data {
+	uint8_t server_channel;
+	uint8_t client_channel;
+	bool expected_status;
+	const uint8_t *send_data;
+	const uint8_t *read_data;
+	uint16_t data_len;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (data->io_id > 0) {
+		g_source_remove(data->io_id);
+		data->io_id = 0;
+	}
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	free(data);
+}
+
+static void client_connectable_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+		break;
+	default:
+		return;
+	}
+
+	tester_print("Client set connectable status 0x%02x", status);
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data);
+	bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void setup_powered_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_client_callback,
+			NULL, NULL);
+}
+
+static void test_basic(const void *test_data)
+{
+	int sk;
+
+	sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+	if (sk < 0) {
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		tester_test_failed();
+		return;
+	}
+
+	close(sk);
+
+	tester_test_passed();
+}
+
+#define test_bnep(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDR; \
+		user->test_data = data; \
+		user->io_id = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_bnep("Basic BNEP Socket - Success", NULL,
+					setup_powered_client, test_basic);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/bneptest.c b/bluez/repo/tools/bneptest.c
new file mode 100644
index 0000000..1404252
--- /dev/null
+++ b/bluez/repo/tools/bneptest.c
@@ -0,0 +1,706 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015 Intel Corporation
+ *
+ *
+ *  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 <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "lib/bnep.h"
+#include "profiles/network/bnep.h"
+
+enum {
+	MODE_LISTEN,
+	MODE_CONNECT,
+};
+
+static GMainLoop *mloop;
+static GIOChannel *bnep_io;
+static struct bnep *session;
+
+static int mode;
+static bool no_close_after_disconn;
+static int send_frame_timeout;
+
+static bdaddr_t src_addr, dst_addr;
+static char iface[16];
+static char bridge[16];
+static bool send_ctrl_msg_type_set = false;
+static uint8_t ctrl_msg_type = 0x00;
+static bool send_bnep_msg_type_set = false;
+static uint8_t bnep_msg_type = 0x00;
+static int ctrl_msg_retransmition_nb = 0;
+static int bnep_msg_retransmission_nb = 0;
+static uint16_t local_role = BNEP_SVC_PANU;
+static uint16_t remote_role = BNEP_SVC_NAP;
+static uint16_t ntw_proto_down_range = 0x0000;
+static uint16_t ntw_proto_up_range = 0xdc05;
+static uint16_t ntw_proto_type = 0x0000;
+static uint8_t mcast_addr_down_range[6];
+static uint8_t mcast_addr_up_range[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static uint8_t src_hw_addr[6];
+static uint8_t dst_hw_addr[6];
+static uint8_t general_frame_payload[] = "abcdef0123456789_bnep_test_data";
+
+static int set_forward_delay(int sk)
+{
+	unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0, 0, 0 };
+	struct ifreq ifr;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+	ifr.ifr_data = (char *) args;
+
+	if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+		error("setting forward delay failed: %d (%s)",
+							errno, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int nap_create_bridge(void)
+{
+	int sk, err;
+
+	sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (sk < 0)
+		return -EOPNOTSUPP;
+
+	if (ioctl(sk, SIOCBRADDBR, bridge) < 0) {
+		if (errno != EEXIST) {
+			close(sk);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	err = set_forward_delay(sk);
+	if (err < 0) {
+		printf("failed to set forward delay\n");
+		ioctl(sk, SIOCBRDELBR, bridge);
+	}
+
+	close(sk);
+
+	return err;
+}
+
+static int cleanup(void)
+{
+	bnep_cleanup();
+
+	if (mode == MODE_LISTEN)
+		bnep_server_delete(bridge, iface, &dst_addr);
+
+	if (bnep_io) {
+		g_io_channel_shutdown(bnep_io, TRUE, NULL);
+		g_io_channel_unref(bnep_io);
+		bnep_io = NULL;
+	}
+
+	return 0;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	printf("%s\n", __func__);
+
+	if (no_close_after_disconn)
+		return FALSE;
+
+	/* Cleanup since it's called when disconnected l2cap */
+	if (cleanup() < 0) {
+		printf("cleanup went wrong...\n");
+		return FALSE;
+	}
+
+	g_main_loop_quit(mloop);
+	return FALSE;
+}
+
+static ssize_t send_compressed_frame(int sk, uint8_t type)
+{
+	uint8_t frame[100];
+
+	printf("%s\n", __func__);
+
+	if (send_frame_timeout > 0) {
+		printf("waiting %d seconds before sending msg\n",
+							send_frame_timeout);
+		sleep(send_frame_timeout);
+	}
+
+	frame[0] = type;
+	memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+	memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+	frame[13] = ntw_proto_type & 0xff;
+	frame[14] = (ntw_proto_type >> 8);
+	memcpy(&frame[15], general_frame_payload,
+						sizeof(general_frame_payload));
+
+	/* TODO - set frame payload by user */
+	return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_general_frame(int sk)
+{
+	uint8_t frame[100];
+
+	printf("%s\n", __func__);
+
+	if (send_frame_timeout > 0) {
+		printf("waiting %d seconds before sending msg\n",
+							send_frame_timeout);
+		sleep(send_frame_timeout);
+	}
+
+	frame[0] = BNEP_GENERAL;
+	memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+	memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+	frame[13] = ntw_proto_type & 0xff;
+	frame[14] = (ntw_proto_type >> 8);
+	memcpy(&frame[15], general_frame_payload,
+						sizeof(general_frame_payload));
+
+	/* TODO - set frame payload by user */
+	return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_ctrl_frame(int sk)
+{
+	/*
+	 * Max buff size = type(1byte) + ctrl(1byte) + len(2byte) +
+	 * mcast_addr_down(6byte) + mcast_addr_up(6byte)
+	 */
+	uint8_t buff[16];
+	struct bnep_set_filter_req *frame = (void *) buff;
+	int err;
+
+	printf("%s\n", __func__);
+
+	if (send_frame_timeout > 0) {
+		printf("waiting %d seconds before sending msg\n",
+						send_frame_timeout);
+		sleep(send_frame_timeout);
+	}
+
+	switch (ctrl_msg_type) {
+	case BNEP_FILTER_NET_TYPE_SET:
+		frame->type = BNEP_CONTROL;
+		frame->ctrl = ctrl_msg_type;
+		frame->len = htons(sizeof(ntw_proto_down_range) +
+						sizeof(ntw_proto_up_range));
+		memcpy(frame->list, &ntw_proto_down_range,
+						sizeof(ntw_proto_down_range));
+		memcpy(frame->list + sizeof(ntw_proto_down_range),
+			&ntw_proto_up_range, sizeof(ntw_proto_up_range));
+
+		err = send(sk, frame, sizeof(*frame) +
+						sizeof(ntw_proto_down_range) +
+						sizeof(ntw_proto_up_range), 0);
+		break;
+	case BNEP_FILTER_MULT_ADDR_SET:
+		frame->type = BNEP_CONTROL;
+		frame->ctrl = ctrl_msg_type;
+		frame->len = htons(sizeof(mcast_addr_down_range) +
+						sizeof(mcast_addr_up_range));
+		memcpy(frame->list, mcast_addr_down_range,
+						sizeof(mcast_addr_down_range));
+		memcpy(frame->list + sizeof(mcast_addr_down_range),
+			mcast_addr_up_range, sizeof(mcast_addr_up_range));
+
+		err = send(sk, frame, sizeof(*frame) +
+					sizeof(mcast_addr_down_range) +
+					sizeof(mcast_addr_up_range), 0);
+		break;
+	default:
+		err = -1;
+		break;
+	}
+
+	return err;
+}
+
+static int send_bnep_frame(int sk)
+{
+	int err;
+
+	switch (bnep_msg_type) {
+	case BNEP_GENERAL:
+		err = send_general_frame(sk);
+		break;
+	case BNEP_COMPRESSED:
+		err = send_compressed_frame(sk, BNEP_COMPRESSED);
+		break;
+	case BNEP_COMPRESSED_SRC_ONLY:
+		err = send_compressed_frame(sk,
+					BNEP_COMPRESSED_SRC_ONLY);
+		break;
+	case BNEP_COMPRESSED_DST_ONLY:
+		err = send_compressed_frame(sk,
+					BNEP_COMPRESSED_DST_ONLY);
+		break;
+	default:
+		printf("wrong bnep_msg_type 0x%02x\n", bnep_msg_type);
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static void handle_bnep_msg_send(int sk)
+{
+	if (send_ctrl_msg_type_set) {
+		do {
+			if (send_ctrl_frame(sk) < 0)
+				printf("sending ctrl frame error: %s (%d)\n",
+							strerror(errno), errno);
+		} while (ctrl_msg_retransmition_nb--);
+	}
+
+	if (send_bnep_msg_type_set) {
+		do {
+			if (send_bnep_frame(sk) < 0)
+				printf("sending bnep frame error: %s (%d)\n",
+							strerror(errno), errno);
+		} while (bnep_msg_retransmission_nb--);
+	}
+}
+
+static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	uint8_t packet[BNEP_MTU];
+	int sk, n, err;
+
+	printf("%s\n", __func__);
+
+	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		error("hangup or error or inval on BNEP socket");
+		return FALSE;
+	}
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+	n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+	if (n < 0) {
+		error("read(): %s(%d)", strerror(errno), errno);
+		return FALSE;
+	}
+
+	err = nap_create_bridge();
+	if (err < 0) {
+		error("failed to create bridge: %s (%d)", strerror(-err), err);
+		return FALSE;
+	}
+
+	if (bnep_server_add(sk, (err < 0) ? NULL : bridge, iface, &dst_addr,
+							packet, n) < 0) {
+		printf("server_connadd failed\n");
+		cleanup();
+		return FALSE;
+	}
+
+	g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, bnep_watchdog_cb,
+									NULL);
+
+	handle_bnep_msg_send(sk);
+
+	g_io_channel_unref(bnep_io);
+	bnep_io = NULL;
+
+	return FALSE;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	printf("%s\n", __func__);
+
+	if (err) {
+		error("%s", err->message);
+		return;
+	}
+
+	g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							setup_bnep_cb, NULL);
+}
+
+static void connected_client_cb(char *iface, int err, void *data)
+{
+	int sk = PTR_TO_INT(data);
+
+	printf("%s\n", __func__);
+
+	handle_bnep_msg_send(sk);
+}
+
+static void disconnected_client_cb(void *data)
+{
+	printf("%s\n", __func__);
+
+	if (no_close_after_disconn)
+		return;
+
+	/* Cleanup since it's called when disconnected l2cap */
+	if (cleanup() < 0) {
+		printf("cleanup went wrong...\n");
+		return;
+	}
+
+	g_main_loop_quit(mloop);
+}
+
+static void connect_client_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	int perr;
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(bnep_io);
+
+	session = bnep_new(sk, local_role, remote_role, bridge);
+	if (!session) {
+		printf("cannot create bnep session\n");
+		return;
+	}
+
+	perr = bnep_connect(session, connected_client_cb,
+				disconnected_client_cb, INT_TO_PTR(sk), NULL);
+	if (perr < 0)
+		printf("cannot initiate bnep connection\n");
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+	GError *err = NULL;
+	char address[18];
+
+	printf("%s\n", __func__);
+
+	bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst_addr, BT_IO_OPT_DEST,
+						address, BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return;
+	}
+
+	printf("incoming connection from: %s\n", address);
+
+	bnep_io = g_io_channel_ref(chan);
+	g_io_channel_set_close_on_unref(bnep_io, TRUE);
+
+	if (!bt_io_accept(bnep_io, connect_cb, NULL, NULL, &err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
+		g_io_channel_unref(bnep_io);
+	}
+}
+
+static int bnep_server_listen(void)
+{
+	GError *gerr = NULL;
+
+	printf("%s\n", __func__);
+
+	bnep_io = bt_io_listen(NULL, confirm_cb, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+					BT_IO_OPT_PSM, BNEP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_OMTU, BNEP_MTU,
+					BT_IO_OPT_IMTU, BNEP_MTU,
+					BT_IO_OPT_INVALID);
+	if (!bnep_io) {
+		printf("can't start server listening: err %s\n", gerr->message);
+		g_error_free(gerr);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bnep_client_connect(void)
+{
+	GError *gerr = NULL;
+	char bdastr[18];
+
+	printf("%s\n", __func__);
+
+	ba2str(&dst_addr, bdastr);
+	printf("connecting %s\n", bdastr);
+
+	bnep_io = bt_io_connect(connect_client_cb, NULL, NULL, &gerr,
+					BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+					BT_IO_OPT_DEST_BDADDR, &dst_addr,
+					BT_IO_OPT_PSM, BNEP_PSM,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_OMTU, BNEP_MTU,
+					BT_IO_OPT_IMTU, BNEP_MTU,
+					BT_IO_OPT_INVALID);
+	if (!bnep_io) {
+		printf("cannot connect: err %s\n", gerr->message);
+		g_error_free(gerr);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void exit_handler(int sig)
+{
+	printf("got sig = %d, cleaning up...\n", sig);
+
+	if (cleanup() < 0)
+		printf("cleanup failure...\n");
+	else
+		printf("cleanup successful - exit\n");
+
+	exit(0);
+}
+
+static void usage(void)
+{
+	printf("bneptest - BNEP testing ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tbneptest [-i] -b <bridge name> -n <iface name>"
+				" <connection mode> [send_ctrl_cmd] [options]\n"
+		"\t-i hci dev number <hci number>, def. 0\n"
+		"\t-b bridge name <string>\n"
+		"\t-n interface name <string>\n");
+	printf("Connect Mode:\n"
+		"\t-c connect <dst_addr>\n"
+		"\t-r remote role <16 bit svc value>\n"
+		"\t-l local role <16 bit svc valu>\n");
+	printf("Listen Mode:\n"
+		"\t-s start server listening\n");
+	printf("Send control command:\n"
+		"\t-t send message type <control msg type>, def. 0\n"
+		"\t-e start network protocol type range <16 bit val>, def. 0\n"
+		"\t-d end network protocol type range <16 bit val>, def. 1500\n"
+		"\t-g start multicast addr range <xx:xx:xx:xx:xx:xx>, def. 0\n"
+		"\t-j end multicast addr range <xx:xx:xx:xx:xx:xx>, def. f\n"
+		"\t-y number of ctrl frame retransmission <integer>, def. 0\n"
+		"\t-u number of bnep frame retransmission <integer>, def. 0\n");
+	printf("Send bnep generic frame:\n"
+		"\t-w send bnep generic frame <bnep generic type>, def. 0\n"
+		"\t-k set src mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n"
+		"\t-f set dst mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n");
+	printf("Options:\n"
+		"\t-T send message timeout after setup <seconds>\n"
+		"\t-N don't close bneptest after disconnect\n");
+}
+
+static struct option main_options[] = {
+	{ "device",			1, 0, 'i' },
+	{ "listen",			0, 0, 's' },
+	{ "connect",			1, 0, 'c' },
+	{ "snd_ctrl_msg_type",		1, 0, 't' },
+	{ "snd_bnep_msg_type",		1, 0, 'w' },
+	{ "src_hw_addr",		1, 0, 'k' },
+	{ "dst_hw_addr",		1, 0, 'f' },
+	{ "send_timeout",		1, 0, 'T' },
+	{ "ntw_proto_down_range",	1, 0, 'd' },
+	{ "ntw_proto_up_range",		1, 0, 'e' },
+	{ "mcast_addr_down_range",	1, 0, 'g' },
+	{ "mcast_addr_up_range",	1, 0, 'j' },
+	{ "local_role",			1, 0, 'l' },
+	{ "remote_role",		1, 0, 'r' },
+	{ "bridge name",		1, 0, 'b' },
+	{ "iface name",			1, 0, 'n' },
+	{ "no_close",			0, 0, 'N' },
+	{ "retrans_ctrl_nb",		0, 0, 'y' },
+	{ "retrans_bnep_nb",		0, 0, 'u' },
+	{ "help",			0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	int opt, i;
+	int err;
+	bool is_set_b_name = false, is_set_i_name = false;
+
+	DBG("");
+
+	signal(SIGINT, exit_handler);
+
+	hci_devba(0, &src_addr);
+	bacpy(&src_addr, BDADDR_ANY);
+
+	mloop = g_main_loop_new(NULL, FALSE);
+	if (!mloop) {
+		printf("cannot create main loop\n");
+
+		exit(1);
+	}
+
+	while ((opt = getopt_long(argc, argv,
+				"+i:c:b:n:t:T:d:e:g:j:k:f:w:l:r:y:u:Nsh",
+				main_options, NULL)) != EOF) {
+		switch (opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &src_addr);
+			else
+				str2ba(optarg, &src_addr);
+			break;
+		case 's':
+			mode = MODE_LISTEN;
+			break;
+		case 'c':
+			str2ba(optarg, &dst_addr);
+			mode = MODE_CONNECT;
+			break;
+		case 't':
+			send_ctrl_msg_type_set = true;
+			ctrl_msg_type = atoi(optarg);
+			break;
+		case 'w':
+			send_bnep_msg_type_set = true;
+			bnep_msg_type = atoi(optarg);
+			break;
+		case 'k':
+			for (i = 0; i <= 5; i++, optarg += 3)
+				src_hw_addr[i] = strtol(optarg, NULL, 16);
+			break;
+		case 'f':
+			for (i = 0; i <= 5; i++, optarg += 3)
+				dst_hw_addr[i] = strtol(optarg, NULL, 16);
+			break;
+		case 'T':
+			send_frame_timeout = atoi(optarg);
+			break;
+		case 'd':
+			ntw_proto_down_range = htons(atoi(optarg));
+			break;
+		case 'e':
+			ntw_proto_up_range = htons(atoi(optarg));
+			break;
+		case 'g':
+			for (i = 5; i >= 0; i--, optarg += 3)
+				mcast_addr_down_range[i] =
+						strtol(optarg, NULL, 16);
+			break;
+		case 'j':
+			for (i = 5; i >= 0; i--, optarg += 3)
+				mcast_addr_up_range[i] =
+						strtol(optarg, NULL, 16);
+			break;
+		case 'l':
+			local_role = atoi(optarg);
+			break;
+		case 'r':
+			remote_role = atoi(optarg);
+			break;
+		case 'b':
+			strncpy(bridge, optarg, 16);
+			bridge[15] = '\0';
+			is_set_b_name = true;
+			break;
+		case 'n':
+			strncpy(iface, optarg, 14);
+			strcat(iface, "\%d");
+			iface[15] = '\0';
+			is_set_i_name = true;
+			break;
+		case 'N':
+			no_close_after_disconn = true;
+			break;
+		case 'y':
+			ctrl_msg_retransmition_nb = atoi(optarg);
+			break;
+		case 'u':
+			bnep_msg_retransmission_nb = atoi(optarg);
+			break;
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	if (!is_set_b_name || !is_set_i_name) {
+		printf("bridge, interface name must be set!\n");
+		exit(1);
+	}
+
+	switch (mode) {
+	case MODE_CONNECT:
+		err = bnep_init();
+		if (err < 0) {
+			printf("cannot initialize bnep\n");
+			exit(1);
+		}
+		err = bnep_client_connect();
+		if (err < 0)
+			exit(1);
+
+		break;
+	case MODE_LISTEN:
+		err = bnep_init();
+		if (err < 0) {
+			printf("cannot initialize bnep\n");
+			exit(1);
+		}
+		err = bnep_server_listen();
+		if (err < 0)
+			exit(1);
+
+		break;
+	default:
+		printf("connect/listen mode not set, exit...\n");
+		exit(1);
+	}
+
+	g_main_loop_run(mloop);
+
+	printf("Done\n");
+
+	g_main_loop_unref(mloop);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/btattach.1 b/bluez/repo/tools/btattach.1
new file mode 100644
index 0000000..ffd653d
--- /dev/null
+++ b/bluez/repo/tools/btattach.1
@@ -0,0 +1,53 @@
+.TH "btattach" "1" "November 2015" "BlueZ" "Linux System Administration"
+.SH NAME
+btattach \- attach serial devices to BlueZ stack
+
+.SH SYNOPSIS
+.B btattach
+.RB [\| \-B
+.IR device \|]
+.RB [\| \-A
+.IR device \|]
+.RB [\| \-P
+.IR protocol \|]
+.RB [\| \-R \|]
+
+.SH DESCRIPTION
+.LP
+btattach is used to attach a serial UART to the Bluetooth stack as a
+transport interface.
+
+.SH OPTIONS
+.TP
+.BI \-B " device" , " " \--bredr " device"
+Attach a BR/EDR controller.
+.TP
+.BI \-A " device" , " " \--amp " device"
+Attach an AMP controller.
+.TP
+.BI \-P " protocol" , " " \--protocol " protocol"
+Specify the protocol type for talking to the device.
+Supported values are:
+.RS
+.IP \(bu 2
+.B h4
+.IP \(bu 2
+.B bcsp
+.IP \(bu 2
+.B 3wire
+.IP \(bu 2
+.B h4ds
+.IP \(bu 2
+.B ll
+.IP \(bu 2
+.B ath3k
+.IP \(bu 2
+.B intel
+.IP \(bu 2
+.B bcm
+.IP \(bu 2
+.B qca
+.RE
+.TP
+.B \-R
+Set the device into raw mode (the kernel and bluetoothd will ignore it).
diff --git a/bluez/repo/tools/btattach.c b/bluez/repo/tools/btattach.c
new file mode 100644
index 0000000..a025bb0
--- /dev/null
+++ b/bluez/repo/tools/btattach.c
@@ -0,0 +1,333 @@
+/*
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+static int open_serial(const char *path)
+{
+	struct termios ti;
+	int fd, saved_ldisc, ldisc = N_HCI;
+
+	fd = open(path, O_RDWR | O_NOCTTY);
+	if (fd < 0) {
+		perror("Failed to open serial port");
+		return -1;
+	}
+
+	if (tcflush(fd, TCIOFLUSH) < 0) {
+		perror("Failed to flush serial port");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) {
+		perror("Failed get serial line discipline");
+		close(fd);
+		return -1;
+	}
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= (B115200 | CLOCAL | CREAD);
+
+	/* Set flow control */
+	ti.c_cflag |= CRTSCTS;
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		perror("Failed to set serial port settings");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
+		perror("Failed set serial line discipline");
+		close(fd);
+		return -1;
+	}
+
+	printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc);
+
+	return fd;
+}
+
+static void local_version_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_version *rsp = data;
+
+	printf("Manufacturer: %u\n", le16_to_cpu(rsp->manufacturer));
+}
+
+static int attach_proto(const char *path, unsigned int proto,
+						unsigned int flags)
+{
+	int fd, dev_id;
+
+	fd = open_serial(path);
+	if (fd < 0)
+		return -1;
+
+	if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+		perror("Failed to set flags");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) {
+		perror("Failed to set protocol");
+		close(fd);
+		return -1;
+	}
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE);
+	if (dev_id < 0) {
+		perror("Failed to get device id");
+		close(fd);
+		return -1;
+	}
+
+	printf("Device index %d attached\n", dev_id);
+
+	if (flags & (1 << HCI_UART_RAW_DEVICE)) {
+		unsigned int attempts = 6;
+		struct bt_hci *hci;
+
+		while (attempts-- > 0) {
+			hci = bt_hci_new_user_channel(dev_id);
+			if (hci)
+				break;
+
+			usleep(250 * 1000);
+		}
+
+		if (!hci) {
+			fprintf(stderr, "Failed to open HCI user channel\n");
+			close(fd);
+			return -1;
+		}
+
+		bt_hci_send(hci, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+					local_version_callback, hci,
+					(bt_hci_destroy_func_t) bt_hci_unref);
+	}
+
+	return fd;
+}
+
+static void uart_callback(int fd, uint32_t events, void *user_data)
+{
+	printf("UART callback handling\n");
+}
+
+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("btattach - Bluetooth serial utility\n"
+		"Usage:\n");
+	printf("\tbtattach [options]\n");
+	printf("options:\n"
+		"\t-B, --bredr <device>   Attach BR/EDR controller\n"
+		"\t-A, --amp <device>     Attach AMP controller\n"
+		"\t-P, --protocol <proto> Specify protocol type\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "bredr",    required_argument, NULL, 'B' },
+	{ "amp",      required_argument, NULL, 'A' },
+	{ "protocol", required_argument, NULL, 'P' },
+	{ "version",  no_argument,       NULL, 'v' },
+	{ "help",     no_argument,       NULL, 'h' },
+	{ }
+};
+
+static const struct {
+	const char *name;
+	unsigned int id;
+} proto_table[] = {
+	{ "h4",    HCI_UART_H4    },
+	{ "bcsp",  HCI_UART_BCSP  },
+	{ "3wire", HCI_UART_3WIRE },
+	{ "h4ds",  HCI_UART_H4DS  },
+	{ "ll",    HCI_UART_LL    },
+	{ "ath3k", HCI_UART_ATH3K },
+	{ "intel", HCI_UART_INTEL },
+	{ "bcm",   HCI_UART_BCM   },
+	{ "qca",   HCI_UART_QCA   },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *bredr_path = NULL, *amp_path = NULL, *proto = NULL;
+	bool raw_device = false;
+	sigset_t mask;
+	int exit_status, count = 0, proto_id = HCI_UART_H4;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "B:A:P:Rvh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'B':
+			bredr_path = optarg;
+			break;
+		case 'A':
+			amp_path = optarg;
+			break;
+		case 'P':
+			proto = optarg;
+			break;
+		case 'R':
+			raw_device = true;
+			break;
+		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);
+
+	if (proto) {
+		unsigned int i;
+
+		for (i = 0; proto_table[i].name; i++) {
+			if (!strcmp(proto_table[i].name, proto)) {
+				proto_id = proto_table[i].id;
+				break;
+			}
+		}
+
+		if (!proto_table[i].name) {
+			fprintf(stderr, "Invalid protocol\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (bredr_path) {
+		unsigned long flags;
+		int fd;
+
+		printf("Attaching BR/EDR controller to %s\n", bredr_path);
+
+		flags = (1 << HCI_UART_RESET_ON_INIT);
+
+		if (raw_device)
+			flags = (1 << HCI_UART_RAW_DEVICE);
+
+		fd = attach_proto(bredr_path, proto_id, flags);
+		if (fd >= 0) {
+			mainloop_add_fd(fd, 0, uart_callback, NULL, NULL);
+			count++;
+		}
+	}
+
+	if (amp_path) {
+		unsigned long flags;
+		int fd;
+
+		printf("Attaching AMP controller to %s\n", amp_path);
+
+		flags = (1 << HCI_UART_RESET_ON_INIT) |
+			(1 << HCI_UART_CREATE_AMP);
+
+		if (raw_device)
+			flags = (1 << HCI_UART_RAW_DEVICE);
+
+		fd = attach_proto(amp_path, proto_id, flags);
+		if (fd >= 0) {
+			mainloop_add_fd(fd, 0, uart_callback, NULL, NULL);
+			count++;
+		}
+	}
+
+	if (count < 1) {
+		fprintf(stderr, "No controller attached\n");
+		return EXIT_FAILURE;
+	}
+
+	exit_status = mainloop_run();
+
+	return exit_status;
+}
diff --git a/bluez/repo/tools/btgatt-client.c b/bluez/repo/tools/btgatt-client.c
new file mode 100644
index 0000000..0f6a1bd
--- /dev/null
+++ b/bluez/repo/tools/btgatt-client.c
@@ -0,0 +1,1671 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google 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 <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/uuid.h"
+
+#include "src/shared/mainloop.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"
+
+#define ATT_CID 4
+
+#define PRLOG(...) \
+	printf(__VA_ARGS__); print_prompt();
+
+#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_MAGENTA	"\x1B[0;95m"
+#define COLOR_BOLDGRAY	"\x1B[1;30m"
+#define COLOR_BOLDWHITE	"\x1B[1;37m"
+
+static bool verbose = false;
+
+struct client {
+	int fd;
+	struct bt_att *att;
+	struct gatt_db *db;
+	struct bt_gatt_client *gatt;
+
+	unsigned int reliable_session_id;
+};
+
+static void print_prompt(void)
+{
+	printf(COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
+	fflush(stdout);
+}
+
+static const char *ecode_to_string(uint8_t ecode)
+{
+	switch (ecode) {
+	case BT_ATT_ERROR_INVALID_HANDLE:
+		return "Invalid Handle";
+	case BT_ATT_ERROR_READ_NOT_PERMITTED:
+		return "Read Not Permitted";
+	case BT_ATT_ERROR_WRITE_NOT_PERMITTED:
+		return "Write Not Permitted";
+	case BT_ATT_ERROR_INVALID_PDU:
+		return "Invalid PDU";
+	case BT_ATT_ERROR_AUTHENTICATION:
+		return "Authentication Required";
+	case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED:
+		return "Request Not Supported";
+	case BT_ATT_ERROR_INVALID_OFFSET:
+		return "Invalid Offset";
+	case BT_ATT_ERROR_AUTHORIZATION:
+		return "Authorization Required";
+	case BT_ATT_ERROR_PREPARE_QUEUE_FULL:
+		return "Prepare Write Queue Full";
+	case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND:
+		return "Attribute Not Found";
+	case BT_ATT_ERROR_ATTRIBUTE_NOT_LONG:
+		return "Attribute Not Long";
+	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+		return "Insuficient Encryption Key Size";
+	case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN:
+		return "Invalid Attribute value len";
+	case BT_ATT_ERROR_UNLIKELY:
+		return "Unlikely Error";
+	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION:
+		return "Insufficient Encryption";
+	case BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE:
+		return "Group type Not Supported";
+	case BT_ATT_ERROR_INSUFFICIENT_RESOURCES:
+		return "Insufficient Resources";
+	case BT_ERROR_CCC_IMPROPERLY_CONFIGURED:
+		return "CCC Improperly Configured";
+	case BT_ERROR_ALREADY_IN_PROGRESS:
+		return "Procedure Already in Progress";
+	case BT_ERROR_OUT_OF_RANGE:
+		return "Out of Range";
+	default:
+		return "Unknown error type";
+	}
+}
+
+static void att_disconnect_cb(int err, void *user_data)
+{
+	printf("Device disconnected: %s\n", strerror(err));
+
+	mainloop_quit();
+}
+
+static void att_debug_cb(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix, str);
+}
+
+static void gatt_debug_cb(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
+}
+
+static void ready_cb(bool success, uint8_t att_ecode, void *user_data);
+static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
+							void *user_data);
+
+static void log_service_event(struct gatt_db_attribute *attr, const char *str)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t uuid;
+	uint16_t start, end;
+
+	gatt_db_attribute_get_service_uuid(attr, &uuid);
+	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+	gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+	PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
+								start, end);
+}
+
+static void service_added_cb(struct gatt_db_attribute *attr, void *user_data)
+{
+	log_service_event(attr, "Service Added");
+}
+
+static void service_removed_cb(struct gatt_db_attribute *attr, void *user_data)
+{
+	log_service_event(attr, "Service Removed");
+}
+
+static struct client *client_create(int fd, uint16_t mtu)
+{
+	struct client *cli;
+
+	cli = new0(struct client, 1);
+	if (!cli) {
+		fprintf(stderr, "Failed to allocate memory for client\n");
+		return NULL;
+	}
+
+	cli->att = bt_att_new(fd, false);
+	if (!cli->att) {
+		fprintf(stderr, "Failed to initialze ATT transport layer\n");
+		bt_att_unref(cli->att);
+		free(cli);
+		return NULL;
+	}
+
+	if (!bt_att_set_close_on_unref(cli->att, true)) {
+		fprintf(stderr, "Failed to set up ATT transport layer\n");
+		bt_att_unref(cli->att);
+		free(cli);
+		return NULL;
+	}
+
+	if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
+								NULL)) {
+		fprintf(stderr, "Failed to set ATT disconnect handler\n");
+		bt_att_unref(cli->att);
+		free(cli);
+		return NULL;
+	}
+
+	cli->fd = fd;
+	cli->db = gatt_db_new();
+	if (!cli->db) {
+		fprintf(stderr, "Failed to create GATT database\n");
+		bt_att_unref(cli->att);
+		free(cli);
+		return NULL;
+	}
+
+	cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
+	if (!cli->gatt) {
+		fprintf(stderr, "Failed to create GATT client\n");
+		gatt_db_unref(cli->db);
+		bt_att_unref(cli->att);
+		free(cli);
+		return NULL;
+	}
+
+	gatt_db_register(cli->db, service_added_cb, service_removed_cb,
+								NULL, NULL);
+
+	if (verbose) {
+		bt_att_set_debug(cli->att, att_debug_cb, "att: ", NULL);
+		bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",
+									NULL);
+	}
+
+	bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
+	bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
+									NULL);
+
+	/* bt_gatt_client already holds a reference */
+	gatt_db_unref(cli->db);
+
+	return cli;
+}
+
+static void client_destroy(struct client *cli)
+{
+	bt_gatt_client_unref(cli->gatt);
+	bt_att_unref(cli->att);
+	free(cli);
+}
+
+static void print_uuid(const bt_uuid_t *uuid)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t uuid128;
+
+	bt_uuid_to_uuid128(uuid, &uuid128);
+	bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
+
+	printf("%s\n", uuid_str);
+}
+
+static void print_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct client *cli = user_data;
+	uint16_t handle, start, end;
+	struct gatt_db_attribute *service;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
+		return;
+
+	service = gatt_db_get_attribute(cli->db, start);
+	if (!service)
+		return;
+
+	gatt_db_attribute_get_service_uuid(service, &uuid);
+
+	printf("\t  " COLOR_GREEN "include" COLOR_OFF " - handle: "
+					"0x%04x, - start: 0x%04x, end: 0x%04x,"
+					"uuid: ", handle, start, end);
+	print_uuid(&uuid);
+}
+
+static void print_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+	printf("\t\t  " COLOR_MAGENTA "descr" COLOR_OFF
+					" - handle: 0x%04x, uuid: ",
+					gatt_db_attribute_get_handle(attr));
+	print_uuid(gatt_db_attribute_get_type(attr));
+}
+
+static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+	uint16_t handle, value_handle;
+	uint8_t properties;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, &handle,
+								&value_handle,
+								&properties,
+								&uuid))
+		return;
+
+	printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
+					" - start: 0x%04x, value: 0x%04x, "
+					"props: 0x%02x, uuid: ",
+					handle, value_handle, properties);
+	print_uuid(&uuid);
+
+	gatt_db_service_foreach_desc(attr, print_desc, NULL);
+}
+
+static void print_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct client *cli = user_data;
+	uint16_t start, end;
+	bool primary;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+									&uuid))
+		return;
+
+	printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+				"end: 0x%04x, type: %s, uuid: ",
+				start, end, primary ? "primary" : "secondary");
+	print_uuid(&uuid);
+
+	gatt_db_service_foreach_incl(attr, print_incl, cli);
+	gatt_db_service_foreach_char(attr, print_chrc, NULL);
+
+	printf("\n");
+}
+
+static void print_services(struct client *cli)
+{
+	printf("\n");
+
+	gatt_db_foreach_service(cli->db, NULL, print_service, cli);
+}
+
+static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
+{
+	printf("\n");
+
+	gatt_db_foreach_service(cli->db, uuid, print_service, cli);
+}
+
+static void print_services_by_handle(struct client *cli, uint16_t handle)
+{
+	printf("\n");
+
+	/* TODO: Filter by handle */
+	gatt_db_foreach_service(cli->db, NULL, print_service, cli);
+}
+
+static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	struct client *cli = user_data;
+
+	if (!success) {
+		PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
+								att_ecode);
+		return;
+	}
+
+	PRLOG("GATT discovery procedures complete\n");
+
+	print_services(cli);
+	print_prompt();
+}
+
+static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
+								void *user_data)
+{
+	struct client *cli = user_data;
+
+	printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n",
+						start_handle, end_handle);
+
+	gatt_db_foreach_service_in_range(cli->db, NULL, print_service, cli,
+						start_handle, end_handle);
+	print_prompt();
+}
+
+static void services_usage(void)
+{
+	printf("Usage: services [options]\nOptions:\n"
+		"\t -u, --uuid <uuid>\tService UUID\n"
+		"\t -a, --handle <handle>\tService start handle\n"
+		"\t -h, --help\t\tShow help message\n"
+		"e.g.:\n"
+		"\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n");
+}
+
+static bool parse_args(char *str, int expected_argc,  char **argv, int *argc)
+{
+	char **ap;
+
+	for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
+		if (**ap == '\0')
+			continue;
+
+		(*argc)++;
+		ap++;
+
+		if (*argc > expected_argc)
+			return false;
+	}
+
+	return true;
+}
+
+static void cmd_services(struct client *cli, char *cmd_str)
+{
+	char *argv[3];
+	int argc = 0;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 2, argv, &argc)) {
+		services_usage();
+		return;
+	}
+
+	if (!argc) {
+		print_services(cli);
+		return;
+	}
+
+	if (argc != 2) {
+		services_usage();
+		return;
+	}
+
+	if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--uuid")) {
+		bt_uuid_t tmp, uuid;
+
+		if (bt_string_to_uuid(&tmp, argv[1]) < 0) {
+			printf("Invalid UUID: %s\n", argv[1]);
+			return;
+		}
+
+		bt_uuid_to_uuid128(&tmp, &uuid);
+
+		print_services_by_uuid(cli, &uuid);
+	} else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--handle")) {
+		uint16_t handle;
+		char *endptr = NULL;
+
+		handle = strtol(argv[1], &endptr, 0);
+		if (!endptr || *endptr != '\0') {
+			printf("Invalid start handle: %s\n", argv[1]);
+			return;
+		}
+
+		print_services_by_handle(cli, handle);
+	} else
+		services_usage();
+}
+
+static void read_multiple_usage(void)
+{
+	printf("Usage: read-multiple <handle_1> <handle_2> ...\n");
+}
+
+static void read_multiple_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	int i;
+
+	if (!success) {
+		PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode);
+		return;
+	}
+
+	printf("\nRead multiple value (%u bytes):", length);
+
+	for (i = 0; i < length; i++)
+		printf("%02x ", value[i]);
+
+	PRLOG("\n");
+}
+
+static void cmd_read_multiple(struct client *cli, char *cmd_str)
+{
+	int argc = 0;
+	uint16_t *value;
+	char *argv[512];
+	int i;
+	char *endptr = NULL;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, sizeof(argv), argv, &argc) || argc < 2) {
+		read_multiple_usage();
+		return;
+	}
+
+	value = malloc(sizeof(uint16_t) * argc);
+	if (!value) {
+		printf("Failed to construct value\n");
+		return;
+	}
+
+	for (i = 0; i < argc; i++) {
+		value[i] = strtol(argv[i], &endptr, 0);
+		if (endptr == argv[i] || *endptr != '\0' || !value[i]) {
+			printf("Invalid value byte: %s\n", argv[i]);
+			free(value);
+			return;
+		}
+	}
+
+	if (!bt_gatt_client_read_multiple(cli->gatt, value, argc,
+						read_multiple_cb, NULL, NULL))
+		printf("Failed to initiate read multiple procedure\n");
+
+	free(value);
+}
+
+static void read_value_usage(void)
+{
+	printf("Usage: read-value <value_handle>\n");
+}
+
+static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	int i;
+
+	if (!success) {
+		PRLOG("\nRead request failed: %s (0x%02x)\n",
+				ecode_to_string(att_ecode), att_ecode);
+		return;
+	}
+
+	printf("\nRead value");
+
+	if (length == 0) {
+		PRLOG(": 0 bytes\n");
+		return;
+	}
+
+	printf(" (%u bytes): ", length);
+
+	for (i = 0; i < length; i++)
+		printf("%02x ", value[i]);
+
+	PRLOG("\n");
+}
+
+static void cmd_read_value(struct client *cli, char *cmd_str)
+{
+	char *argv[2];
+	int argc = 0;
+	uint16_t handle;
+	char *endptr = NULL;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
+		read_value_usage();
+		return;
+	}
+
+	handle = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !handle) {
+		printf("Invalid value handle: %s\n", argv[0]);
+		return;
+	}
+
+	if (!bt_gatt_client_read_value(cli->gatt, handle, read_cb,
+								NULL, NULL))
+		printf("Failed to initiate read value procedure\n");
+}
+
+static void read_long_value_usage(void)
+{
+	printf("Usage: read-long-value <value_handle> <offset>\n");
+}
+
+static void cmd_read_long_value(struct client *cli, char *cmd_str)
+{
+	char *argv[3];
+	int argc = 0;
+	uint16_t handle;
+	uint16_t offset;
+	char *endptr = NULL;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) {
+		read_long_value_usage();
+		return;
+	}
+
+	handle = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !handle) {
+		printf("Invalid value handle: %s\n", argv[0]);
+		return;
+	}
+
+	endptr = NULL;
+	offset = strtol(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0') {
+		printf("Invalid offset: %s\n", argv[1]);
+		return;
+	}
+
+	if (!bt_gatt_client_read_long_value(cli->gatt, handle, offset, read_cb,
+								NULL, NULL))
+		printf("Failed to initiate read long value procedure\n");
+}
+
+static void write_value_usage(void)
+{
+	printf("Usage: write-value [options] <value_handle> <value>\n"
+		"Options:\n"
+		"\t-w, --without-response\tWrite without response\n"
+		"\t-s, --signed-write\tSigned write command\n"
+		"e.g.:\n"
+		"\twrite-value 0x0001 00 01 00\n");
+}
+
+static struct option write_value_options[] = {
+	{ "without-response",	0, 0, 'w' },
+	{ "signed-write",	0, 0, 's' },
+	{ }
+};
+
+static void write_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	if (success) {
+		PRLOG("\nWrite successful\n");
+	} else {
+		PRLOG("\nWrite failed: %s (0x%02x)\n",
+				ecode_to_string(att_ecode), att_ecode);
+	}
+}
+
+static void cmd_write_value(struct client *cli, char *cmd_str)
+{
+	int opt, i;
+	char *argvbuf[516];
+	char **argv = argvbuf;
+	int argc = 1;
+	uint16_t handle;
+	char *endptr = NULL;
+	int length;
+	uint8_t *value = NULL;
+	bool without_response = false;
+	bool signed_write = false;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+		printf("Too many arguments\n");
+		write_value_usage();
+		return;
+	}
+
+	optind = 0;
+	argv[0] = "write-value";
+	while ((opt = getopt_long(argc, argv, "+ws", write_value_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'w':
+			without_response = true;
+			break;
+		case 's':
+			signed_write = true;
+			break;
+		default:
+			write_value_usage();
+			return;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		write_value_usage();
+		return;
+	}
+
+	handle = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !handle) {
+		printf("Invalid handle: %s\n", argv[0]);
+		return;
+	}
+
+	length = argc - 1;
+
+	if (length > 0) {
+		if (length > UINT16_MAX) {
+			printf("Write value too long\n");
+			return;
+		}
+
+		value = malloc(length);
+		if (!value) {
+			printf("Failed to construct write value\n");
+			return;
+		}
+
+		for (i = 1; i < argc; i++) {
+			if (strlen(argv[i]) != 2) {
+				printf("Invalid value byte: %s\n",
+								argv[i]);
+				goto done;
+			}
+
+			value[i-1] = strtol(argv[i], &endptr, 0);
+			if (endptr == argv[i] || *endptr != '\0'
+							|| errno == ERANGE) {
+				printf("Invalid value byte: %s\n",
+								argv[i]);
+				goto done;
+			}
+		}
+	}
+
+	if (without_response) {
+		if (!bt_gatt_client_write_without_response(cli->gatt, handle,
+						signed_write, value, length)) {
+			printf("Failed to initiate write without response "
+								"procedure\n");
+			goto done;
+		}
+
+		printf("Write command sent\n");
+		goto done;
+	}
+
+	if (!bt_gatt_client_write_value(cli->gatt, handle, value, length,
+								write_cb,
+								NULL, NULL))
+		printf("Failed to initiate write procedure\n");
+
+done:
+	free(value);
+}
+
+static void write_long_value_usage(void)
+{
+	printf("Usage: write-long-value [options] <value_handle> <offset> "
+				"<value>\n"
+				"Options:\n"
+				"\t-r, --reliable-write\tReliable write\n"
+				"e.g.:\n"
+				"\twrite-long-value 0x0001 0 00 01 00\n");
+}
+
+static struct option write_long_value_options[] = {
+	{ "reliable-write",	0, 0, 'r' },
+	{ }
+};
+
+static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode,
+								void *user_data)
+{
+	if (success) {
+		PRLOG("Write successful\n");
+	} else if (reliable_error) {
+		PRLOG("Reliable write not verified\n");
+	} else {
+		PRLOG("\nWrite failed: %s (0x%02x)\n",
+				ecode_to_string(att_ecode), att_ecode);
+	}
+}
+
+static void cmd_write_long_value(struct client *cli, char *cmd_str)
+{
+	int opt, i;
+	char *argvbuf[516];
+	char **argv = argvbuf;
+	int argc = 1;
+	uint16_t handle;
+	uint16_t offset;
+	char *endptr = NULL;
+	int length;
+	uint8_t *value = NULL;
+	bool reliable_writes = false;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+		printf("Too many arguments\n");
+		write_value_usage();
+		return;
+	}
+
+	optind = 0;
+	argv[0] = "write-long-value";
+	while ((opt = getopt_long(argc, argv, "+r", write_long_value_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'r':
+			reliable_writes = true;
+			break;
+		default:
+			write_long_value_usage();
+			return;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 2) {
+		write_long_value_usage();
+		return;
+	}
+
+	handle = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !handle) {
+		printf("Invalid handle: %s\n", argv[0]);
+		return;
+	}
+
+	endptr = NULL;
+	offset = strtol(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0' || errno == ERANGE) {
+		printf("Invalid offset: %s\n", argv[1]);
+		return;
+	}
+
+	length = argc - 2;
+
+	if (length > 0) {
+		if (length > UINT16_MAX) {
+			printf("Write value too long\n");
+			return;
+		}
+
+		value = malloc(length);
+		if (!value) {
+			printf("Failed to construct write value\n");
+			return;
+		}
+
+		for (i = 2; i < argc; i++) {
+			if (strlen(argv[i]) != 2) {
+				printf("Invalid value byte: %s\n",
+								argv[i]);
+				free(value);
+				return;
+			}
+
+			value[i-2] = strtol(argv[i], &endptr, 0);
+			if (endptr == argv[i] || *endptr != '\0'
+							|| errno == ERANGE) {
+				printf("Invalid value byte: %s\n",
+								argv[i]);
+				free(value);
+				return;
+			}
+		}
+	}
+
+	if (!bt_gatt_client_write_long_value(cli->gatt, reliable_writes, handle,
+							offset, value, length,
+							write_long_cb,
+							NULL, NULL))
+		printf("Failed to initiate long write procedure\n");
+
+	free(value);
+}
+
+static void write_prepare_usage(void)
+{
+	printf("Usage: write-prepare [options] <value_handle> <offset> "
+				"<value>\n"
+				"Options:\n"
+				"\t-s, --session-id\tSession id\n"
+				"e.g.:\n"
+				"\twrite-prepare -s 1 0x0001 00 01 00\n");
+}
+
+static struct option write_prepare_options[] = {
+	{ "session-id",		1, 0, 's' },
+	{ }
+};
+
+static void cmd_write_prepare(struct client *cli, char *cmd_str)
+{
+	int opt, i;
+	char *argvbuf[516];
+	char **argv = argvbuf;
+	int argc = 0;
+	unsigned int id = 0;
+	uint16_t handle;
+	uint16_t offset;
+	char *endptr = NULL;
+	unsigned int length;
+	uint8_t *value = NULL;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+		printf("Too many arguments\n");
+		write_value_usage();
+		return;
+	}
+
+	/* Add command name for getopt_long */
+	argc++;
+	argv[0] = "write-prepare";
+
+	optind = 0;
+	while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 's':
+			if (!optarg) {
+				write_prepare_usage();
+				return;
+			}
+
+			id = atoi(optarg);
+
+			break;
+		default:
+			write_prepare_usage();
+			return;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 3) {
+		write_prepare_usage();
+		return;
+	}
+
+	if (cli->reliable_session_id != id) {
+		printf("Session id != Ongoing session id (%u!=%u)\n", id,
+						cli->reliable_session_id);
+		return;
+	}
+
+	handle = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !handle) {
+		printf("Invalid handle: %s\n", argv[0]);
+		return;
+	}
+
+	endptr = NULL;
+	offset = strtol(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0' || errno == ERANGE) {
+		printf("Invalid offset: %s\n", argv[1]);
+		return;
+	}
+
+	/*
+	 * First two arguments are handle and offset. What remains is the value
+	 * length
+	 */
+	length = argc - 2;
+
+	if (length == 0)
+		goto done;
+
+	if (length > UINT16_MAX) {
+		printf("Write value too long\n");
+		return;
+	}
+
+	value = malloc(length);
+	if (!value) {
+		printf("Failed to allocate memory for value\n");
+		return;
+	}
+
+	for (i = 2; i < argc; i++) {
+		if (strlen(argv[i]) != 2) {
+			printf("Invalid value byte: %s\n", argv[i]);
+			free(value);
+			return;
+		}
+
+		value[i-2] = strtol(argv[i], &endptr, 0);
+		if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) {
+			printf("Invalid value byte: %s\n", argv[i]);
+			free(value);
+			return;
+		}
+	}
+
+done:
+	cli->reliable_session_id =
+			bt_gatt_client_prepare_write(cli->gatt, id,
+							handle, offset,
+							value, length,
+							write_long_cb, NULL,
+							NULL);
+	if (!cli->reliable_session_id)
+		printf("Failed to proceed prepare write\n");
+	else
+		printf("Prepare write success.\n"
+				"Session id: %d to be used on next write\n",
+						cli->reliable_session_id);
+
+	free(value);
+}
+
+static void write_execute_usage(void)
+{
+	printf("Usage: write-execute <session_id> <execute>\n"
+				"e.g.:\n"
+				"\twrite-execute 1 0\n");
+}
+
+static void cmd_write_execute(struct client *cli, char *cmd_str)
+{
+	char *argvbuf[516];
+	char **argv = argvbuf;
+	int argc = 0;
+	char *endptr = NULL;
+	unsigned int session_id;
+	bool execute;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 514, argv, &argc)) {
+		printf("Too many arguments\n");
+		write_value_usage();
+		return;
+	}
+
+	if (argc < 2) {
+		write_execute_usage();
+		return;
+	}
+
+	session_id = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0') {
+		printf("Invalid session id: %s\n", argv[0]);
+		return;
+	}
+
+	if (session_id != cli->reliable_session_id) {
+		printf("Invalid session id: %u != %u\n", session_id,
+						cli->reliable_session_id);
+		return;
+	}
+
+	execute = !!strtol(argv[1], &endptr, 0);
+	if (!endptr || *endptr != '\0') {
+		printf("Invalid execute: %s\n", argv[1]);
+		return;
+	}
+
+	if (execute) {
+		if (!bt_gatt_client_write_execute(cli->gatt, session_id,
+							write_cb, NULL, NULL))
+			printf("Failed to proceed write execute\n");
+	} else {
+		bt_gatt_client_cancel(cli->gatt, session_id);
+	}
+
+	cli->reliable_session_id = 0;
+}
+
+static void register_notify_usage(void)
+{
+	printf("Usage: register-notify <chrc value handle>\n");
+}
+
+static void notify_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	int i;
+
+	printf("\n\tHandle Value Not/Ind: 0x%04x - ", value_handle);
+
+	if (length == 0) {
+		PRLOG("(0 bytes)\n");
+		return;
+	}
+
+	printf("(%u bytes): ", length);
+
+	for (i = 0; i < length; i++)
+		printf("%02x ", value[i]);
+
+	PRLOG("\n");
+}
+
+static void register_notify_cb(uint16_t att_ecode, void *user_data)
+{
+	if (att_ecode) {
+		PRLOG("Failed to register notify handler "
+					"- error code: 0x%02x\n", att_ecode);
+		return;
+	}
+
+	PRLOG("Registered notify handler!");
+}
+
+static void cmd_register_notify(struct client *cli, char *cmd_str)
+{
+	char *argv[2];
+	int argc = 0;
+	uint16_t value_handle;
+	unsigned int id;
+	char *endptr = NULL;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
+		register_notify_usage();
+		return;
+	}
+
+	value_handle = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !value_handle) {
+		printf("Invalid value handle: %s\n", argv[0]);
+		return;
+	}
+
+	id = bt_gatt_client_register_notify(cli->gatt, value_handle,
+							register_notify_cb,
+							notify_cb, NULL, NULL);
+	if (!id) {
+		printf("Failed to register notify handler\n");
+		return;
+	}
+
+	PRLOG("Registering notify handler with id: %u\n", id);
+}
+
+static void unregister_notify_usage(void)
+{
+	printf("Usage: unregister-notify <notify id>\n");
+}
+
+static void cmd_unregister_notify(struct client *cli, char *cmd_str)
+{
+	char *argv[2];
+	int argc = 0;
+	unsigned int id;
+	char *endptr = NULL;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
+		unregister_notify_usage();
+		return;
+	}
+
+	id = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || !id) {
+		printf("Invalid notify id: %s\n", argv[0]);
+		return;
+	}
+
+	if (!bt_gatt_client_unregister_notify(cli->gatt, id)) {
+		printf("Failed to unregister notify handler with id: %u\n", id);
+		return;
+	}
+
+	printf("Unregistered notify handler with id: %u\n", id);
+}
+
+static void set_security_usage(void)
+{
+	printf("Usage: set_security <level>\n"
+		"level: 1-3\n"
+		"e.g.:\n"
+		"\tset-sec-level 2\n");
+}
+
+static void cmd_set_security(struct client *cli, char *cmd_str)
+{
+	char *argvbuf[1];
+	char **argv = argvbuf;
+	int argc = 0;
+	char *endptr = NULL;
+	int level;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	if (!parse_args(cmd_str, 1, argv, &argc)) {
+		printf("Too many arguments\n");
+		set_security_usage();
+		return;
+	}
+
+	if (argc < 1) {
+		set_security_usage();
+		return;
+	}
+
+	level = strtol(argv[0], &endptr, 0);
+	if (!endptr || *endptr != '\0' || level < 1 || level > 3) {
+		printf("Invalid level: %s\n", argv[0]);
+		return;
+	}
+
+	if (!bt_gatt_client_set_security(cli->gatt, level))
+		printf("Could not set sec level\n");
+	else
+		printf("Setting security level %d success\n", level);
+}
+
+static void cmd_get_security(struct client *cli, char *cmd_str)
+{
+	int level;
+
+	if (!bt_gatt_client_is_ready(cli->gatt)) {
+		printf("GATT client not initialized\n");
+		return;
+	}
+
+	level = bt_gatt_client_get_security(cli->gatt);
+	if (level < 0)
+		printf("Could not set sec level\n");
+	else
+		printf("Security level: %u\n", level);
+}
+
+static bool convert_sign_key(char *optarg, uint8_t key[16])
+{
+	int i;
+
+	if (strlen(optarg) != 32) {
+		printf("sign-key length is invalid\n");
+		return false;
+	}
+
+	for (i = 0; i < 16; i++) {
+		if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1)
+			return false;
+	}
+
+	return true;
+}
+
+static void set_sign_key_usage(void)
+{
+	printf("Usage: set-sign-key [options]\nOptions:\n"
+		"\t -c, --sign-key <csrk>\tCSRK\n"
+		"e.g.:\n"
+		"\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
+}
+
+static bool local_counter(uint32_t *sign_cnt, void *user_data)
+{
+	static uint32_t cnt = 0;
+
+	*sign_cnt = cnt++;
+
+	return true;
+}
+
+static void cmd_set_sign_key(struct client *cli, char *cmd_str)
+{
+	char *argv[3];
+	int argc = 0;
+	uint8_t key[16];
+
+	memset(key, 0, 16);
+
+	if (!parse_args(cmd_str, 2, argv, &argc)) {
+		set_sign_key_usage();
+		return;
+	}
+
+	if (argc != 2) {
+		set_sign_key_usage();
+		return;
+	}
+
+	if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
+		if (convert_sign_key(argv[1], key))
+			bt_att_set_local_key(cli->att, key, local_counter, cli);
+	} else
+		set_sign_key_usage();
+}
+
+static void cmd_help(struct client *cli, char *cmd_str);
+
+typedef void (*command_func_t)(struct client *cli, char *cmd_str);
+
+static struct {
+	char *cmd;
+	command_func_t func;
+	char *doc;
+} command[] = {
+	{ "help", cmd_help, "\tDisplay help message" },
+	{ "services", cmd_services, "\tShow discovered services" },
+	{ "read-value", cmd_read_value,
+				"\tRead a characteristic or descriptor value" },
+	{ "read-long-value", cmd_read_long_value,
+		"\tRead a long characteristic or desctriptor value" },
+	{ "read-multiple", cmd_read_multiple, "\tRead Multiple" },
+	{ "write-value", cmd_write_value,
+			"\tWrite a characteristic or descriptor value" },
+	{ "write-long-value", cmd_write_long_value,
+			"Write long characteristic or descriptor value" },
+	{ "write-prepare", cmd_write_prepare,
+			"\tWrite prepare characteristic or descriptor value" },
+	{ "write-execute", cmd_write_execute,
+			"\tExecute already prepared write" },
+	{ "register-notify", cmd_register_notify,
+			"\tSubscribe to not/ind from a characteristic" },
+	{ "unregister-notify", cmd_unregister_notify,
+						"Unregister a not/ind session"},
+	{ "set-security", cmd_set_security,
+				"\tSet security level on le connection"},
+	{ "get-security", cmd_get_security,
+				"\tGet security level on le connection"},
+	{ "set-sign-key", cmd_set_sign_key,
+				"\tSet signing key for signed write command"},
+	{ }
+};
+
+static void cmd_help(struct client *cli, char *cmd_str)
+{
+	int i;
+
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+}
+
+static void prompt_read_cb(int fd, uint32_t events, void *user_data)
+{
+	ssize_t read;
+	size_t len = 0;
+	char *line = NULL;
+	char *cmd = NULL, *args;
+	struct client *cli = user_data;
+	int i;
+
+	if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+		mainloop_quit();
+		return;
+	}
+
+	if ((read = getline(&line, &len, stdin)) == -1)
+		return;
+
+	if (read <= 1) {
+		cmd_help(cli, NULL);
+		print_prompt();
+		return;
+	}
+
+	line[read-1] = '\0';
+	args = line;
+
+	while ((cmd = strsep(&args, " \t")))
+		if (*cmd != '\0')
+			break;
+
+	if (!cmd)
+		goto failed;
+
+	for (i = 0; command[i].cmd; i++) {
+		if (strcmp(command[i].cmd, cmd) == 0)
+			break;
+	}
+
+	if (command[i].cmd)
+		command[i].func(cli, args);
+	else
+		fprintf(stderr, "Unknown command: %s\n", line);
+
+failed:
+	print_prompt();
+
+	free(line);
+}
+
+static void signal_cb(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
+									int sec)
+{
+	int sock;
+	struct sockaddr_l2 srcaddr, dstaddr;
+	struct bt_security btsec;
+
+	if (verbose) {
+		char srcaddr_str[18], dstaddr_str[18];
+
+		ba2str(src, srcaddr_str);
+		ba2str(dst, dstaddr_str);
+
+		printf("btgatt-client: Opening L2CAP LE connection on ATT "
+					"channel:\n\t src: %s\n\tdest: %s\n",
+					srcaddr_str, dstaddr_str);
+	}
+
+	sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sock < 0) {
+		perror("Failed to create L2CAP socket");
+		return -1;
+	}
+
+	/* Set up source address */
+	memset(&srcaddr, 0, sizeof(srcaddr));
+	srcaddr.l2_family = AF_BLUETOOTH;
+	srcaddr.l2_cid = htobs(ATT_CID);
+	srcaddr.l2_bdaddr_type = 0;
+	bacpy(&srcaddr.l2_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
+		perror("Failed to bind L2CAP socket");
+		close(sock);
+		return -1;
+	}
+
+	/* Set the security level */
+	memset(&btsec, 0, sizeof(btsec));
+	btsec.level = sec;
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
+							sizeof(btsec)) != 0) {
+		fprintf(stderr, "Failed to set L2CAP security level\n");
+		close(sock);
+		return -1;
+	}
+
+	/* Set up destination address */
+	memset(&dstaddr, 0, sizeof(dstaddr));
+	dstaddr.l2_family = AF_BLUETOOTH;
+	dstaddr.l2_cid = htobs(ATT_CID);
+	dstaddr.l2_bdaddr_type = dst_type;
+	bacpy(&dstaddr.l2_bdaddr, dst);
+
+	printf("Connecting to device...");
+	fflush(stdout);
+
+	if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
+		perror(" Failed to connect");
+		close(sock);
+		return -1;
+	}
+
+	printf(" Done\n");
+
+	return sock;
+}
+
+static void usage(void)
+{
+	printf("btgatt-client\n");
+	printf("Usage:\n\tbtgatt-client [options]\n");
+
+	printf("Options:\n"
+		"\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
+		"\t-d, --dest <addr>\t\tSpecify the destination address\n"
+		"\t-t, --type [random|public] \tSpecify the LE address type\n"
+		"\t-m, --mtu <mtu> \t\tThe ATT MTU to use\n"
+		"\t-s, --security-level <sec> \tSet security level (low|"
+								"medium|high)\n"
+		"\t-v, --verbose\t\t\tEnable extra logging\n"
+		"\t-h, --help\t\t\tDisplay help\n");
+}
+
+static struct option main_options[] = {
+	{ "index",		1, 0, 'i' },
+	{ "dest",		1, 0, 'd' },
+	{ "type",		1, 0, 't' },
+	{ "mtu",		1, 0, 'm' },
+	{ "security-level",	1, 0, 's' },
+	{ "verbose",		0, 0, 'v' },
+	{ "help",		0, 0, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	int sec = BT_SECURITY_LOW;
+	uint16_t mtu = 0;
+	uint8_t dst_type = BDADDR_LE_PUBLIC;
+	bool dst_addr_given = false;
+	bdaddr_t src_addr, dst_addr;
+	int dev_id = -1;
+	int fd;
+	sigset_t mask;
+	struct client *cli;
+
+	while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
+						main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		case 'v':
+			verbose = true;
+			break;
+		case 's':
+			if (strcmp(optarg, "low") == 0)
+				sec = BT_SECURITY_LOW;
+			else if (strcmp(optarg, "medium") == 0)
+				sec = BT_SECURITY_MEDIUM;
+			else if (strcmp(optarg, "high") == 0)
+				sec = BT_SECURITY_HIGH;
+			else {
+				fprintf(stderr, "Invalid security level\n");
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'm': {
+			int arg;
+
+			arg = atoi(optarg);
+			if (arg <= 0) {
+				fprintf(stderr, "Invalid MTU: %d\n", arg);
+				return EXIT_FAILURE;
+			}
+
+			if (arg > UINT16_MAX) {
+				fprintf(stderr, "MTU too large: %d\n", arg);
+				return EXIT_FAILURE;
+			}
+
+			mtu = (uint16_t)arg;
+			break;
+		}
+		case 't':
+			if (strcmp(optarg, "random") == 0)
+				dst_type = BDADDR_LE_RANDOM;
+			else if (strcmp(optarg, "public") == 0)
+				dst_type = BDADDR_LE_PUBLIC;
+			else {
+				fprintf(stderr,
+					"Allowed types: random, public\n");
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'd':
+			if (str2ba(optarg, &dst_addr) < 0) {
+				fprintf(stderr, "Invalid remote address: %s\n",
+									optarg);
+				return EXIT_FAILURE;
+			}
+
+			dst_addr_given = true;
+			break;
+
+		case 'i':
+			dev_id = hci_devid(optarg);
+			if (dev_id < 0) {
+				perror("Invalid adapter");
+				return EXIT_FAILURE;
+			}
+
+			break;
+		default:
+			fprintf(stderr, "Invalid option: %c\n", opt);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!argc) {
+		usage();
+		return EXIT_SUCCESS;
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc) {
+		usage();
+		return EXIT_SUCCESS;
+	}
+
+	if (dev_id == -1)
+		bacpy(&src_addr, BDADDR_ANY);
+	else if (hci_devba(dev_id, &src_addr) < 0) {
+		perror("Adapter not available");
+		return EXIT_FAILURE;
+	}
+
+	if (!dst_addr_given) {
+		fprintf(stderr, "Destination address required!\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	fd = l2cap_le_att_connect(&src_addr, &dst_addr, dst_type, sec);
+	if (fd < 0)
+		return EXIT_FAILURE;
+
+	cli = client_create(fd, mtu);
+	if (!cli) {
+		close(fd);
+		return EXIT_FAILURE;
+	}
+
+	if (mainloop_add_fd(fileno(stdin),
+				EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
+				prompt_read_cb, cli, NULL) < 0) {
+		fprintf(stderr, "Failed to initialize console\n");
+		return EXIT_FAILURE;
+	}
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_cb, NULL, NULL);
+
+	print_prompt();
+
+	mainloop_run();
+
+	printf("\n\nShutting down...\n");
+
+	client_destroy(cli);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/btgatt-server.c b/bluez/repo/tools/btgatt-server.c
new file mode 100644
index 0000000..292b584
--- /dev/null
+++ b/bluez/repo/tools/btgatt-server.c
@@ -0,0 +1,1265 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google 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 <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/uuid.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/timeout.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+
+#define UUID_GAP			0x1800
+#define UUID_GATT			0x1801
+#define UUID_HEART_RATE			0x180d
+#define UUID_HEART_RATE_MSRMT		0x2a37
+#define UUID_HEART_RATE_BODY		0x2a38
+#define UUID_HEART_RATE_CTRL		0x2a39
+
+#define ATT_CID 4
+
+#define PRLOG(...) \
+	do { \
+		printf(__VA_ARGS__); \
+		print_prompt(); \
+	} while (0)
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#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_MAGENTA	"\x1B[0;95m"
+#define COLOR_BOLDGRAY	"\x1B[1;30m"
+#define COLOR_BOLDWHITE	"\x1B[1;37m"
+
+static const char test_device_name[] = "Very Long Test Device Name For Testing "
+				"ATT Protocol Operations On GATT Server";
+static bool verbose = false;
+
+struct server {
+	int fd;
+	struct bt_att *att;
+	struct gatt_db *db;
+	struct bt_gatt_server *gatt;
+
+	uint8_t *device_name;
+	size_t name_len;
+
+	uint16_t gatt_svc_chngd_handle;
+	bool svc_chngd_enabled;
+
+	uint16_t hr_handle;
+	uint16_t hr_msrmt_handle;
+	uint16_t hr_energy_expended;
+	bool hr_visible;
+	bool hr_msrmt_enabled;
+	int hr_ee_count;
+	unsigned int hr_timeout_id;
+};
+
+static void print_prompt(void)
+{
+	printf(COLOR_BLUE "[GATT server]" COLOR_OFF "# ");
+	fflush(stdout);
+}
+
+static void att_disconnect_cb(int err, void *user_data)
+{
+	printf("Device disconnected: %s\n", strerror(err));
+
+	mainloop_quit();
+}
+
+static void att_debug_cb(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix,
+									str);
+}
+
+static void gatt_debug_cb(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
+}
+
+static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t error = 0;
+	size_t len = 0;
+	const uint8_t *value = NULL;
+
+	PRLOG("GAP Device Name Read called\n");
+
+	len = server->name_len;
+
+	if (offset > len) {
+		error = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	len -= offset;
+	value = len ? &server->device_name[offset] : NULL;
+
+done:
+	gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void gap_device_name_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t error = 0;
+
+	PRLOG("GAP Device Name Write called\n");
+
+	/* If the value is being completely truncated, clean up and return */
+	if (!(offset + len)) {
+		free(server->device_name);
+		server->device_name = NULL;
+		server->name_len = 0;
+		goto done;
+	}
+
+	/* Implement this as a variable length attribute value. */
+	if (offset > server->name_len) {
+		error = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (offset + len != server->name_len) {
+		uint8_t *name;
+
+		name = realloc(server->device_name, offset + len);
+		if (!name) {
+			error = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+			goto done;
+		}
+
+		server->device_name = name;
+		server->name_len = offset + len;
+	}
+
+	if (value)
+		memcpy(server->device_name + offset, value, len);
+
+done:
+	gatt_db_attribute_write_result(attrib, id, error);
+}
+
+static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	uint8_t value[2];
+
+	PRLOG("Device Name Extended Properties Read called\n");
+
+	value[0] = BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
+	value[1] = 0;
+
+	gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void gatt_service_changed_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	PRLOG("Service Changed Read called\n");
+
+	gatt_db_attribute_read_result(attrib, id, 0, NULL, 0);
+}
+
+static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t value[2];
+
+	PRLOG("Service Changed CCC Read called\n");
+
+	value[0] = server->svc_chngd_enabled ? 0x02 : 0x00;
+	value[1] = 0x00;
+
+	gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void gatt_svc_chngd_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t ecode = 0;
+
+	PRLOG("Service Changed CCC Write called\n");
+
+	if (!value || len != 2) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	if (offset) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (value[0] == 0x00)
+		server->svc_chngd_enabled = false;
+	else if (value[0] == 0x02)
+		server->svc_chngd_enabled = true;
+	else
+		ecode = 0x80;
+
+	PRLOG("Service Changed Enabled: %s\n",
+				server->svc_chngd_enabled ? "true" : "false");
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void hr_msrmt_ccc_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t value[2];
+
+	value[0] = server->hr_msrmt_enabled ? 0x01 : 0x00;
+	value[1] = 0x00;
+
+	gatt_db_attribute_read_result(attrib, id, 0, value, 2);
+}
+
+static bool hr_msrmt_cb(void *user_data)
+{
+	struct server *server = user_data;
+	bool expended_present = !(server->hr_ee_count % 10);
+	uint16_t len = 2;
+	uint8_t pdu[4];
+	uint32_t cur_ee;
+
+	pdu[0] = 0x06;
+	pdu[1] = 90 + (rand() % 40);
+
+	if (expended_present) {
+		pdu[0] |= 0x08;
+		put_le16(server->hr_energy_expended, pdu + 2);
+		len += 2;
+	}
+
+	bt_gatt_server_send_notification(server->gatt,
+						server->hr_msrmt_handle,
+						pdu, len);
+
+
+	cur_ee = server->hr_energy_expended;
+	server->hr_energy_expended = MIN(UINT16_MAX, cur_ee + 10);
+	server->hr_ee_count++;
+
+	return true;
+}
+
+static void update_hr_msrmt_simulation(struct server *server)
+{
+	if (!server->hr_msrmt_enabled || !server->hr_visible) {
+		timeout_remove(server->hr_timeout_id);
+		return;
+	}
+
+	server->hr_timeout_id = timeout_add(1000, hr_msrmt_cb, server, NULL);
+}
+
+static void hr_msrmt_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t ecode = 0;
+
+	if (!value || len != 2) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	if (offset) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (value[0] == 0x00)
+		server->hr_msrmt_enabled = false;
+	else if (value[0] == 0x01) {
+		if (server->hr_msrmt_enabled) {
+			PRLOG("HR Measurement Already Enabled\n");
+			goto done;
+		}
+
+		server->hr_msrmt_enabled = true;
+	} else
+		ecode = 0x80;
+
+	PRLOG("HR: Measurement Enabled: %s\n",
+				server->hr_msrmt_enabled ? "true" : "false");
+
+	update_hr_msrmt_simulation(server);
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void hr_control_point_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct server *server = user_data;
+	uint8_t ecode = 0;
+
+	if (!value || len != 1) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	if (offset) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (value[0] == 1) {
+		PRLOG("HR: Energy Expended value reset\n");
+		server->hr_energy_expended = 0;
+	}
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void confirm_write(struct gatt_db_attribute *attr, int err,
+							void *user_data)
+{
+	if (!err)
+		return;
+
+	fprintf(stderr, "Error caching attribute %p - err: %d\n", attr, err);
+	exit(1);
+}
+
+static void populate_gap_service(struct server *server)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service, *tmp;
+	uint16_t appearance;
+
+	/* Add the GAP service */
+	bt_uuid16_create(&uuid, UUID_GAP);
+	service = gatt_db_add_service(server->db, &uuid, true, 6);
+
+	/*
+	 * Device Name characteristic. Make the value dynamically read and
+	 * written via callbacks.
+	 */
+	bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+	gatt_db_service_add_characteristic(service, &uuid,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					BT_GATT_CHRC_PROP_READ,
+					gap_device_name_read_cb,
+					gap_device_name_write_cb,
+					server);
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID);
+	gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ,
+					gap_device_name_ext_prop_read_cb,
+					NULL, server);
+
+	/*
+	 * Appearance characteristic. Reads and writes should obtain the value
+	 * from the database.
+	 */
+	bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+	tmp = gatt_db_service_add_characteristic(service, &uuid,
+							BT_ATT_PERM_READ,
+							BT_GATT_CHRC_PROP_READ,
+							NULL, NULL, server);
+
+	/*
+	 * Write the appearance value to the database, since we're not using a
+	 * callback.
+	 */
+	put_le16(128, &appearance);
+	gatt_db_attribute_write(tmp, 0, (void *) &appearance,
+							sizeof(appearance),
+							BT_ATT_OP_WRITE_REQ,
+							NULL, confirm_write,
+							NULL);
+
+	gatt_db_service_set_active(service, true);
+}
+
+static void populate_gatt_service(struct server *server)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service, *svc_chngd;
+
+	/* Add the GATT service */
+	bt_uuid16_create(&uuid, UUID_GATT);
+	service = gatt_db_add_service(server->db, &uuid, true, 4);
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+	svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
+			BT_ATT_PERM_READ,
+			BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_INDICATE,
+			gatt_service_changed_cb,
+			NULL, server);
+	server->gatt_svc_chngd_handle = gatt_db_attribute_get_handle(svc_chngd);
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+	gatt_db_service_add_descriptor(service, &uuid,
+				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				gatt_svc_chngd_ccc_read_cb,
+				gatt_svc_chngd_ccc_write_cb, server);
+
+	gatt_db_service_set_active(service, true);
+}
+
+static void populate_hr_service(struct server *server)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *service, *hr_msrmt, *body;
+	uint8_t body_loc = 1;  /* "Chest" */
+
+	/* Add Heart Rate Service */
+	bt_uuid16_create(&uuid, UUID_HEART_RATE);
+	service = gatt_db_add_service(server->db, &uuid, true, 8);
+	server->hr_handle = gatt_db_attribute_get_handle(service);
+
+	/* HR Measurement Characteristic */
+	bt_uuid16_create(&uuid, UUID_HEART_RATE_MSRMT);
+	hr_msrmt = gatt_db_service_add_characteristic(service, &uuid,
+						BT_ATT_PERM_NONE,
+						BT_GATT_CHRC_PROP_NOTIFY,
+						NULL, NULL, NULL);
+	server->hr_msrmt_handle = gatt_db_attribute_get_handle(hr_msrmt);
+
+	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+	gatt_db_service_add_descriptor(service, &uuid,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					hr_msrmt_ccc_read_cb,
+					hr_msrmt_ccc_write_cb, server);
+
+	/*
+	 * Body Sensor Location Characteristic. Make reads obtain the value from
+	 * the database.
+	 */
+	bt_uuid16_create(&uuid, UUID_HEART_RATE_BODY);
+	body = gatt_db_service_add_characteristic(service, &uuid,
+						BT_ATT_PERM_READ,
+						BT_GATT_CHRC_PROP_READ,
+						NULL, NULL, server);
+	gatt_db_attribute_write(body, 0, (void *) &body_loc, sizeof(body_loc),
+							BT_ATT_OP_WRITE_REQ,
+							NULL, confirm_write,
+							NULL);
+
+	/* HR Control Point Characteristic */
+	bt_uuid16_create(&uuid, UUID_HEART_RATE_CTRL);
+	gatt_db_service_add_characteristic(service, &uuid,
+						BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_WRITE,
+						NULL, hr_control_point_write_cb,
+						server);
+
+	if (server->hr_visible)
+		gatt_db_service_set_active(service, true);
+}
+
+static void populate_db(struct server *server)
+{
+	populate_gap_service(server);
+	populate_gatt_service(server);
+	populate_hr_service(server);
+}
+
+static struct server *server_create(int fd, uint16_t mtu, bool hr_visible)
+{
+	struct server *server;
+	size_t name_len = strlen(test_device_name);
+
+	server = new0(struct server, 1);
+	if (!server) {
+		fprintf(stderr, "Failed to allocate memory for server\n");
+		return NULL;
+	}
+
+	server->att = bt_att_new(fd, false);
+	if (!server->att) {
+		fprintf(stderr, "Failed to initialze ATT transport layer\n");
+		goto fail;
+	}
+
+	if (!bt_att_set_close_on_unref(server->att, true)) {
+		fprintf(stderr, "Failed to set up ATT transport layer\n");
+		goto fail;
+	}
+
+	if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL,
+									NULL)) {
+		fprintf(stderr, "Failed to set ATT disconnect handler\n");
+		goto fail;
+	}
+
+	server->name_len = name_len + 1;
+	server->device_name = malloc(name_len + 1);
+	if (!server->device_name) {
+		fprintf(stderr, "Failed to allocate memory for device name\n");
+		goto fail;
+	}
+
+	memcpy(server->device_name, test_device_name, name_len);
+	server->device_name[name_len] = '\0';
+
+	server->fd = fd;
+	server->db = gatt_db_new();
+	if (!server->db) {
+		fprintf(stderr, "Failed to create GATT database\n");
+		goto fail;
+	}
+
+	server->gatt = bt_gatt_server_new(server->db, server->att, mtu);
+	if (!server->gatt) {
+		fprintf(stderr, "Failed to create GATT server\n");
+		goto fail;
+	}
+
+	server->hr_visible = hr_visible;
+
+	if (verbose) {
+		bt_att_set_debug(server->att, att_debug_cb, "att: ", NULL);
+		bt_gatt_server_set_debug(server->gatt, gatt_debug_cb,
+							"server: ", NULL);
+	}
+
+	/* Random seed for generating fake Heart Rate measurements */
+	srand(time(NULL));
+
+	/* bt_gatt_server already holds a reference */
+	populate_db(server);
+
+	return server;
+
+fail:
+	gatt_db_unref(server->db);
+	free(server->device_name);
+	bt_att_unref(server->att);
+	free(server);
+
+	return NULL;
+}
+
+static void server_destroy(struct server *server)
+{
+	timeout_remove(server->hr_timeout_id);
+	bt_gatt_server_unref(server->gatt);
+	gatt_db_unref(server->db);
+}
+
+static void usage(void)
+{
+	printf("btgatt-server\n");
+	printf("Usage:\n\tbtgatt-server [options]\n");
+
+	printf("Options:\n"
+		"\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
+		"\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n"
+		"\t-s, --security-level <sec>\tSet security level (low|"
+								"medium|high)\n"
+		"\t-t, --type [random|public] \t The source address type\n"
+		"\t-v, --verbose\t\t\tEnable extra logging\n"
+		"\t-r, --heart-rate\t\tEnable Heart Rate service\n"
+		"\t-h, --help\t\t\tDisplay help\n");
+}
+
+static struct option main_options[] = {
+	{ "index",		1, 0, 'i' },
+	{ "mtu",		1, 0, 'm' },
+	{ "security-level",	1, 0, 's' },
+	{ "type",		1, 0, 't' },
+	{ "verbose",		0, 0, 'v' },
+	{ "heart-rate",		0, 0, 'r' },
+	{ "help",		0, 0, 'h' },
+	{ }
+};
+
+static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
+							uint8_t src_type)
+{
+	int sk, nsk;
+	struct sockaddr_l2 srcaddr, addr;
+	socklen_t optlen;
+	struct bt_security btsec;
+	char ba[18];
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sk < 0) {
+		perror("Failed to create L2CAP socket");
+		return -1;
+	}
+
+	/* Set up source address */
+	memset(&srcaddr, 0, sizeof(srcaddr));
+	srcaddr.l2_family = AF_BLUETOOTH;
+	srcaddr.l2_cid = htobs(ATT_CID);
+	srcaddr.l2_bdaddr_type = src_type;
+	bacpy(&srcaddr.l2_bdaddr, src);
+
+	if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
+		perror("Failed to bind L2CAP socket");
+		goto fail;
+	}
+
+	/* Set the security level */
+	memset(&btsec, 0, sizeof(btsec));
+	btsec.level = sec;
+	if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &btsec,
+							sizeof(btsec)) != 0) {
+		fprintf(stderr, "Failed to set L2CAP security level\n");
+		goto fail;
+	}
+
+	if (listen(sk, 10) < 0) {
+		perror("Listening on socket failed");
+		goto fail;
+	}
+
+	printf("Started listening on ATT channel. Waiting for connections\n");
+
+	memset(&addr, 0, sizeof(addr));
+	optlen = sizeof(addr);
+	nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+	if (nsk < 0) {
+		perror("Accept failed");
+		goto fail;
+	}
+
+	ba2str(&addr.l2_bdaddr, ba);
+	printf("Connect from %s\n", ba);
+	close(sk);
+
+	return nsk;
+
+fail:
+	close(sk);
+	return -1;
+}
+
+static void notify_usage(void)
+{
+	printf("Usage: notify [options] <value_handle> <value>\n"
+					"Options:\n"
+					"\t -i, --indicate\tSend indication\n"
+					"e.g.:\n"
+					"\tnotify 0x0001 00 01 00\n");
+}
+
+static struct option notify_options[] = {
+	{ "indicate",	0, 0, 'i' },
+	{ }
+};
+
+static bool parse_args(char *str, int expected_argc,  char **argv, int *argc)
+{
+	char **ap;
+
+	for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
+		if (**ap == '\0')
+			continue;
+
+		(*argc)++;
+		ap++;
+
+		if (*argc > expected_argc)
+			return false;
+	}
+
+	return true;
+}
+
+static void conf_cb(void *user_data)
+{
+	PRLOG("Received confirmation\n");
+}
+
+static void cmd_notify(struct server *server, char *cmd_str)
+{
+	int opt, i;
+	char *argvbuf[516];
+	char **argv = argvbuf;
+	int argc = 1;
+	uint16_t handle;
+	char *endptr = NULL;
+	int length;
+	uint8_t *value = NULL;
+	bool indicate = false;
+
+	if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+		printf("Too many arguments\n");
+		notify_usage();
+		return;
+	}
+
+	optind = 0;
+	argv[0] = "notify";
+	while ((opt = getopt_long(argc, argv, "+i", notify_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'i':
+			indicate = true;
+			break;
+		default:
+			notify_usage();
+			return;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		notify_usage();
+		return;
+	}
+
+	handle = strtol(argv[0], &endptr, 16);
+	if (!endptr || *endptr != '\0' || !handle) {
+		printf("Invalid handle: %s\n", argv[0]);
+		return;
+	}
+
+	length = argc - 1;
+
+	if (length > 0) {
+		if (length > UINT16_MAX) {
+			printf("Value too long\n");
+			return;
+		}
+
+		value = malloc(length);
+		if (!value) {
+			printf("Failed to construct value\n");
+			return;
+		}
+
+		for (i = 1; i < argc; i++) {
+			if (strlen(argv[i]) != 2) {
+				printf("Invalid value byte: %s\n",
+								argv[i]);
+				goto done;
+			}
+
+			value[i-1] = strtol(argv[i], &endptr, 16);
+			if (endptr == argv[i] || *endptr != '\0'
+							|| errno == ERANGE) {
+				printf("Invalid value byte: %s\n",
+								argv[i]);
+				goto done;
+			}
+		}
+	}
+
+	if (indicate) {
+		if (!bt_gatt_server_send_indication(server->gatt, handle,
+							value, length,
+							conf_cb, NULL, NULL))
+			printf("Failed to initiate indication\n");
+	} else if (!bt_gatt_server_send_notification(server->gatt, handle,
+								value, length))
+		printf("Failed to initiate notification\n");
+
+done:
+	free(value);
+}
+
+static void heart_rate_usage(void)
+{
+	printf("Usage: heart-rate on|off\n");
+}
+
+static void cmd_heart_rate(struct server *server, char *cmd_str)
+{
+	bool enable;
+	uint8_t pdu[4];
+	struct gatt_db_attribute *attr;
+
+	if (!cmd_str) {
+		heart_rate_usage();
+		return;
+	}
+
+	if (strcmp(cmd_str, "on") == 0)
+		enable = true;
+	else if (strcmp(cmd_str, "off") == 0)
+		enable = false;
+	else {
+		heart_rate_usage();
+		return;
+	}
+
+	if (enable == server->hr_visible) {
+		printf("Heart Rate Service already %s\n",
+						enable ? "visible" : "hidden");
+		return;
+	}
+
+	server->hr_visible = enable;
+	attr = gatt_db_get_attribute(server->db, server->hr_handle);
+	gatt_db_service_set_active(attr, server->hr_visible);
+	update_hr_msrmt_simulation(server);
+
+	if (!server->svc_chngd_enabled)
+		return;
+
+	put_le16(server->hr_handle, pdu);
+	put_le16(server->hr_handle + 7, pdu + 2);
+
+	server->hr_msrmt_enabled = false;
+	update_hr_msrmt_simulation(server);
+
+	bt_gatt_server_send_indication(server->gatt,
+						server->gatt_svc_chngd_handle,
+						pdu, 4, conf_cb, NULL, NULL);
+}
+
+static void print_uuid(const bt_uuid_t *uuid)
+{
+	char uuid_str[MAX_LEN_UUID_STR];
+	bt_uuid_t uuid128;
+
+	bt_uuid_to_uuid128(uuid, &uuid128);
+	bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
+
+	printf("%s\n", uuid_str);
+}
+
+static void print_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct server *server = user_data;
+	uint16_t handle, start, end;
+	struct gatt_db_attribute *service;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
+		return;
+
+	service = gatt_db_get_attribute(server->db, start);
+	if (!service)
+		return;
+
+	gatt_db_attribute_get_service_uuid(service, &uuid);
+
+	printf("\t  " COLOR_GREEN "include" COLOR_OFF " - handle: "
+					"0x%04x, - start: 0x%04x, end: 0x%04x,"
+					"uuid: ", handle, start, end);
+	print_uuid(&uuid);
+}
+
+static void print_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+	printf("\t\t  " COLOR_MAGENTA "descr" COLOR_OFF
+					" - handle: 0x%04x, uuid: ",
+					gatt_db_attribute_get_handle(attr));
+	print_uuid(gatt_db_attribute_get_type(attr));
+}
+
+static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+	uint16_t handle, value_handle;
+	uint8_t properties;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, &handle,
+								&value_handle,
+								&properties,
+								&uuid))
+		return;
+
+	printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
+					" - start: 0x%04x, value: 0x%04x, "
+					"props: 0x%02x, uuid: ",
+					handle, value_handle, properties);
+	print_uuid(&uuid);
+
+	gatt_db_service_foreach_desc(attr, print_desc, NULL);
+}
+
+static void print_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct server *server = user_data;
+	uint16_t start, end;
+	bool primary;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+									&uuid))
+		return;
+
+	printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+				"end: 0x%04x, type: %s, uuid: ",
+				start, end, primary ? "primary" : "secondary");
+	print_uuid(&uuid);
+
+	gatt_db_service_foreach_incl(attr, print_incl, server);
+	gatt_db_service_foreach_char(attr, print_chrc, NULL);
+
+	printf("\n");
+}
+
+static void cmd_services(struct server *server, char *cmd_str)
+{
+	gatt_db_foreach_service(server->db, NULL, print_service, server);
+}
+
+static bool convert_sign_key(char *optarg, uint8_t key[16])
+{
+	int i;
+
+	if (strlen(optarg) != 32) {
+		printf("sign-key length is invalid\n");
+		return false;
+	}
+
+	for (i = 0; i < 16; i++) {
+		if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1)
+			return false;
+	}
+
+	return true;
+}
+
+static void set_sign_key_usage(void)
+{
+	printf("Usage: set-sign-key [options]\nOptions:\n"
+		"\t -c, --sign-key <remote csrk>\tRemote CSRK\n"
+		"e.g.:\n"
+		"\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
+}
+
+static bool remote_counter(uint32_t *sign_cnt, void *user_data)
+{
+	static uint32_t cnt = 0;
+
+	if (*sign_cnt < cnt)
+		return false;
+
+	cnt = *sign_cnt;
+
+	return true;
+}
+
+static void cmd_set_sign_key(struct server *server, char *cmd_str)
+{
+	char *argv[3];
+	int argc = 0;
+	uint8_t key[16];
+
+	memset(key, 0, 16);
+
+	if (!parse_args(cmd_str, 2, argv, &argc)) {
+		set_sign_key_usage();
+		return;
+	}
+
+	if (argc != 2) {
+		set_sign_key_usage();
+		return;
+	}
+
+	if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
+		if (convert_sign_key(argv[1], key))
+			bt_att_set_remote_key(server->att, key, remote_counter,
+									server);
+	} else
+		set_sign_key_usage();
+}
+
+static void cmd_help(struct server *server, char *cmd_str);
+
+typedef void (*command_func_t)(struct server *server, char *cmd_str);
+
+static struct {
+	char *cmd;
+	command_func_t func;
+	char *doc;
+} command[] = {
+	{ "help", cmd_help, "\tDisplay help message" },
+	{ "notify", cmd_notify, "\tSend handle-value notification" },
+	{ "heart-rate", cmd_heart_rate, "\tHide/Unhide Heart Rate Service" },
+	{ "services", cmd_services, "\tEnumerate all services" },
+	{ "set-sign-key", cmd_set_sign_key,
+			"\tSet remote signing key for signed write command"},
+	{ }
+};
+
+static void cmd_help(struct server *server, char *cmd_str)
+{
+	int i;
+
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+}
+
+static void prompt_read_cb(int fd, uint32_t events, void *user_data)
+{
+	ssize_t read;
+	size_t len = 0;
+	char *line = NULL;
+	char *cmd = NULL, *args;
+	struct server *server = user_data;
+	int i;
+
+	if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+		mainloop_quit();
+		return;
+	}
+
+	read = getline(&line, &len, stdin);
+	if (read < 0)
+		return;
+
+	if (read <= 1) {
+		cmd_help(server, NULL);
+		print_prompt();
+		return;
+	}
+
+	line[read-1] = '\0';
+	args = line;
+
+	while ((cmd = strsep(&args, " \t")))
+		if (*cmd != '\0')
+			break;
+
+	if (!cmd)
+		goto failed;
+
+	for (i = 0; command[i].cmd; i++) {
+		if (strcmp(command[i].cmd, cmd) == 0)
+			break;
+	}
+
+	if (command[i].cmd)
+		command[i].func(server, args);
+	else
+		fprintf(stderr, "Unknown command: %s\n", line);
+
+failed:
+	print_prompt();
+
+	free(line);
+}
+
+static void signal_cb(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	default:
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	bdaddr_t src_addr;
+	int dev_id = -1;
+	int fd;
+	int sec = BT_SECURITY_LOW;
+	uint8_t src_type = BDADDR_LE_PUBLIC;
+	uint16_t mtu = 0;
+	sigset_t mask;
+	bool hr_visible = false;
+	struct server *server;
+
+	while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:",
+						main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		case 'v':
+			verbose = true;
+			break;
+		case 'r':
+			hr_visible = true;
+			break;
+		case 's':
+			if (strcmp(optarg, "low") == 0)
+				sec = BT_SECURITY_LOW;
+			else if (strcmp(optarg, "medium") == 0)
+				sec = BT_SECURITY_MEDIUM;
+			else if (strcmp(optarg, "high") == 0)
+				sec = BT_SECURITY_HIGH;
+			else {
+				fprintf(stderr, "Invalid security level\n");
+				return EXIT_FAILURE;
+			}
+			break;
+		case 't':
+			if (strcmp(optarg, "random") == 0)
+				src_type = BDADDR_LE_RANDOM;
+			else if (strcmp(optarg, "public") == 0)
+				src_type = BDADDR_LE_PUBLIC;
+			else {
+				fprintf(stderr,
+					"Allowed types: random, public\n");
+				return EXIT_FAILURE;
+			}
+			break;
+		case 'm': {
+			int arg;
+
+			arg = atoi(optarg);
+			if (arg <= 0) {
+				fprintf(stderr, "Invalid MTU: %d\n", arg);
+				return EXIT_FAILURE;
+			}
+
+			if (arg > UINT16_MAX) {
+				fprintf(stderr, "MTU too large: %d\n", arg);
+				return EXIT_FAILURE;
+			}
+
+			mtu = (uint16_t) arg;
+			break;
+		}
+		case 'i':
+			dev_id = hci_devid(optarg);
+			if (dev_id < 0) {
+				perror("Invalid adapter");
+				return EXIT_FAILURE;
+			}
+
+			break;
+		default:
+			fprintf(stderr, "Invalid option: %c\n", opt);
+			return EXIT_FAILURE;
+		}
+	}
+
+	argc -= optind;
+	argv -= optind;
+	optind = 0;
+
+	if (argc) {
+		usage();
+		return EXIT_SUCCESS;
+	}
+
+	if (dev_id == -1)
+		bacpy(&src_addr, BDADDR_ANY);
+	else if (hci_devba(dev_id, &src_addr) < 0) {
+		perror("Adapter not available");
+		return EXIT_FAILURE;
+	}
+
+	fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
+	if (fd < 0) {
+		fprintf(stderr, "Failed to accept L2CAP ATT connection\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	server = server_create(fd, mtu, hr_visible);
+	if (!server) {
+		close(fd);
+		return EXIT_FAILURE;
+	}
+
+	if (mainloop_add_fd(fileno(stdin),
+				EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
+				prompt_read_cb, server, NULL) < 0) {
+		fprintf(stderr, "Failed to initialize console\n");
+		server_destroy(server);
+
+		return EXIT_FAILURE;
+	}
+
+	printf("Running GATT server\n");
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_cb, NULL, NULL);
+
+	print_prompt();
+
+	mainloop_run();
+
+	printf("\n\nShutting down...\n");
+
+	server_destroy(server);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/btinfo.c b/bluez/repo/tools/btinfo.c
new file mode 100644
index 0000000..fd11ac4
--- /dev/null
+++ b/bluez/repo/tools/btinfo.c
@@ -0,0 +1,365 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+#define BTPROTO_HCI	1
+
+struct hci_dev_stats {
+	uint32_t err_rx;
+	uint32_t err_tx;
+	uint32_t cmd_tx;
+	uint32_t evt_rx;
+	uint32_t acl_tx;
+	uint32_t acl_rx;
+	uint32_t sco_tx;
+	uint32_t sco_rx;
+	uint32_t byte_rx;
+	uint32_t byte_tx;
+};
+
+struct hci_dev_info {
+	uint16_t dev_id;
+	char     name[8];
+	uint8_t  bdaddr[6];
+	uint32_t flags;
+	uint8_t  type;
+	uint8_t  features[8];
+	uint32_t pkt_type;
+	uint32_t link_policy;
+	uint32_t link_mode;
+	uint16_t acl_mtu;
+	uint16_t acl_pkts;
+	uint16_t sco_mtu;
+	uint16_t sco_pkts;
+	struct   hci_dev_stats stat;
+};
+
+#define HCIDEVUP	_IOW('H', 201, int)
+#define HCIDEVDOWN	_IOW('H', 202, int)
+#define HCIGETDEVINFO	_IOR('H', 211, int)
+
+#define HCI_UP		(1 << 0)
+
+#define HCI_BREDR	0x00
+#define HCI_AMP		0x01
+
+static struct hci_dev_info hci_info;
+static uint8_t hci_type;
+static struct bt_hci *hci_dev;
+
+static bool reset_on_init = false;
+static bool reset_on_shutdown = false;
+
+static bool shutdown_timeout(void *user_data)
+{
+	mainloop_quit();
+
+	return false;
+}
+
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
+{
+	unsigned int id = PTR_TO_UINT(user_data);
+
+	timeout_remove(id);
+	mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+	unsigned int id;
+
+	bt_hci_flush(hci_dev);
+
+	if (reset_on_shutdown) {
+		id = timeout_add(5000, shutdown_timeout, NULL, NULL);
+
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+				shutdown_complete, UINT_TO_PTR(id), NULL);
+	} else
+		mainloop_quit();
+}
+
+static void local_version_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_version *rsp = data;
+
+	printf("HCI version: %u\n", rsp->hci_ver);
+	printf("HCI revision: %u\n", le16_to_cpu(rsp->hci_rev));
+
+	switch (hci_type) {
+	case HCI_BREDR:
+		printf("LMP version: %u\n", rsp->lmp_ver);
+		printf("LMP subversion: %u\n", le16_to_cpu(rsp->lmp_subver));
+		break;
+	case HCI_AMP:
+		printf("PAL version: %u\n", rsp->lmp_ver);
+		printf("PAL subversion: %u\n", le16_to_cpu(rsp->lmp_subver));
+		break;
+	}
+
+	printf("Manufacturer: %u\n", le16_to_cpu(rsp->manufacturer));
+}
+
+static void local_commands_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	shutdown_device();
+}
+
+static void local_features_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0,
+					local_commands_callback, NULL, NULL);
+}
+
+static bool cmd_local(int argc, char *argv[])
+{
+	if (reset_on_init)
+		bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+						NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+					local_version_callback, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+					local_features_callback, NULL, NULL);
+
+	return true;
+}
+
+typedef bool (*cmd_func_t)(int argc, char *argv[]);
+
+static const struct {
+	const char *name;
+	cmd_func_t func;
+	const char *help;
+} cmd_table[] = {
+	{ "local", cmd_local, "Print local controller details" },
+	{ }
+};
+
+static void signal_callback(int signum, void *user_data)
+{
+	static bool terminated = false;
+
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		if (!terminated) {
+			shutdown_device();
+			terminated = true;
+		}
+		break;
+	}
+}
+
+static void usage(void)
+{
+	int i;
+
+	printf("btinfo - Bluetooth device testing tool\n"
+		"Usage:\n");
+	printf("\tbtinfo [options] <command>\n");
+	printf("options:\n"
+		"\t-i, --index <num>      Use specified controller\n"
+		"\t-h, --help             Show help options\n");
+	printf("commands:\n");
+	for (i = 0; cmd_table[i].name; i++)
+		printf("\t%-25s%s\n", cmd_table[i].name, cmd_table[i].help);
+}
+
+static const struct option main_options[] = {
+	{ "index",   required_argument, NULL, 'i' },
+	{ "reset",   no_argument,       NULL, 'r' },
+	{ "raw",     no_argument,       NULL, 'R' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	cmd_func_t func = NULL;
+	uint16_t index = 0;
+	const char *str;
+	bool use_raw = false;
+	bool power_down = false;
+	sigset_t mask;
+	int fd, i, exit_status;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "i:rRvh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				usage();
+				return EXIT_FAILURE;
+			}
+			index = atoi(str);
+			break;
+		case 'r':
+			reset_on_init = true;
+			break;
+		case 'R':
+			use_raw = true;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (argc - optind < 1) {
+		fprintf(stderr, "Missing command argument\n");
+		return EXIT_FAILURE;
+	}
+
+	for (i = 0; cmd_table[i].name; i++) {
+		if (!strcmp(cmd_table[i].name, argv[optind])) {
+			func = cmd_table[i].func;
+			break;
+		}
+	}
+
+	if (!func) {
+		fprintf(stderr, "Unsupported command specified\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Bluetooth information utility ver %s\n", VERSION);
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open HCI raw socket");
+		return EXIT_FAILURE;
+	}
+
+	memset(&hci_info, 0, sizeof(hci_info));
+	hci_info.dev_id = index;
+
+	if (ioctl(fd, HCIGETDEVINFO, (void *) &hci_info) < 0) {
+		perror("Failed to get HCI device information");
+		close(fd);
+		return EXIT_FAILURE;
+	}
+
+	if (use_raw && !(hci_info.flags & HCI_UP)) {
+		printf("Powering on controller\n");
+
+		if (ioctl(fd, HCIDEVUP, hci_info.dev_id) < 0) {
+			perror("Failed to power on controller");
+			close(fd);
+			return EXIT_FAILURE;
+		}
+
+		power_down = true;
+	}
+
+	close(fd);
+
+	hci_type = (hci_info.type & 0x30) >> 4;
+
+	if (use_raw) {
+		hci_dev = bt_hci_new_raw_device(index);
+		if (!hci_dev) {
+			fprintf(stderr, "Failed to open HCI raw device\n");
+			return EXIT_FAILURE;
+		}
+	} else {
+		hci_dev = bt_hci_new_user_channel(index);
+		if (!hci_dev) {
+			fprintf(stderr, "Failed to open HCI user channel\n");
+			return EXIT_FAILURE;
+		}
+
+		reset_on_init = true;
+		reset_on_shutdown = true;
+	}
+
+	if (!func(argc - optind - 1, argv + optind + 1)) {
+		bt_hci_unref(hci_dev);
+		return EXIT_FAILURE;
+	}
+
+	exit_status = mainloop_run();
+
+	bt_hci_unref(hci_dev);
+
+	if (use_raw && power_down) {
+		fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+		if (fd >= 0) {
+			printf("Powering down controller\n");
+
+			if (ioctl(fd, HCIDEVDOWN, hci_info.dev_id) < 0)
+				perror("Failed to power down controller");
+
+			close(fd);
+		}
+	}
+
+	return exit_status;
+}
diff --git a/bluez/repo/tools/btiotest.c b/bluez/repo/tools/btiotest.c
new file mode 100644
index 0000000..10e78d5
--- /dev/null
+++ b/bluez/repo/tools/btiotest.c
@@ -0,0 +1,663 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+
+#include "btio/btio.h"
+
+#define DEFAULT_ACCEPT_TIMEOUT 2
+static int opt_update_sec = 0;
+
+struct io_data {
+	guint ref;
+	GIOChannel *io;
+	int reject;
+	int disconn;
+	int accept;
+	int voice;
+};
+
+static void io_data_unref(struct io_data *data)
+{
+	data->ref--;
+
+	if (data->ref)
+		return;
+
+	if (data->io)
+		g_io_channel_unref(data->io);
+
+	g_free(data);
+}
+
+static struct io_data *io_data_ref(struct io_data *data)
+{
+	data->ref++;
+	return data;
+}
+
+static struct io_data *io_data_new(GIOChannel *io, int reject, int disconn,
+								int accept)
+{
+	struct io_data *data;
+
+	data = g_new0(struct io_data, 1);
+	data->io = io;
+	data->reject = reject;
+	data->disconn = disconn;
+	data->accept = accept;
+
+	return io_data_ref(data);
+}
+
+static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+	printf("Disconnected\n");
+	return FALSE;
+}
+
+static gboolean disconn_timeout(gpointer user_data)
+{
+	struct io_data *data = user_data;
+
+	printf("Disconnecting\n");
+
+	g_io_channel_shutdown(data->io, TRUE, NULL);
+
+	return FALSE;
+}
+
+static void update_sec_level(struct io_data *data)
+{
+	GError *err = NULL;
+	int sec_level;
+
+	if (!bt_io_get(data->io, &err, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID)) {
+		printf("bt_io_get(OPT_SEC_LEVEL): %s\n", err->message);
+		g_clear_error(&err);
+		return;
+	}
+
+	printf("sec_level=%d\n", sec_level);
+
+	if (opt_update_sec == sec_level)
+		return;
+
+	if (!bt_io_set(data->io, &err, BT_IO_OPT_SEC_LEVEL, opt_update_sec,
+							BT_IO_OPT_INVALID)) {
+		printf("bt_io_set(OPT_SEC_LEVEL): %s\n", err->message);
+		g_clear_error(&err);
+	}
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+	struct io_data *data = user_data;
+	GIOCondition cond;
+	char addr[18];
+	uint16_t handle, omtu, imtu;
+	uint8_t cls[3], key_size;
+
+	if (err) {
+		printf("Connecting failed: %s\n", err->message);
+		return;
+	}
+
+	if (!bt_io_get(io, &err,
+			BT_IO_OPT_DEST, addr,
+			BT_IO_OPT_HANDLE, &handle,
+			BT_IO_OPT_CLASS, cls,
+			BT_IO_OPT_INVALID)) {
+		printf("Unable to get destination address: %s\n",
+								err->message);
+		g_clear_error(&err);
+		strcpy(addr, "(unknown)");
+	}
+
+	printf("Successfully connected to %s. handle=%u, class=%02x%02x%02x\n",
+			addr, handle, cls[0], cls[1], cls[2]);
+
+	if (!bt_io_get(io, &err, BT_IO_OPT_OMTU, &omtu,
+					BT_IO_OPT_IMTU, &imtu,
+					BT_IO_OPT_INVALID)) {
+		printf("Unable to get MTU sizes: %s\n", err->message);
+		g_clear_error(&err);
+	} else
+		printf("imtu=%u, omtu=%u\n", imtu, omtu);
+
+	if (!bt_io_get(io, &err, BT_IO_OPT_KEY_SIZE, &key_size,
+							BT_IO_OPT_INVALID)) {
+		printf("Unable to get Key size: %s\n", err->message);
+		g_clear_error(&err);
+	} else
+		printf("key_size=%u\n", key_size);
+
+	if (data->disconn == 0) {
+		g_io_channel_shutdown(io, TRUE, NULL);
+		printf("Disconnected\n");
+		return;
+	}
+
+	if (data->io == NULL)
+		data->io = g_io_channel_ref(io);
+
+	if (data->disconn > 0) {
+		io_data_ref(data);
+		g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
+					disconn_timeout, data,
+					(GDestroyNotify) io_data_unref);
+	}
+
+
+	io_data_ref(data);
+
+	if (opt_update_sec > 0)
+		update_sec_level(data);
+
+	cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
+					(GDestroyNotify) io_data_unref);
+}
+
+static gboolean confirm_timeout(gpointer user_data)
+{
+	struct io_data *data = user_data;
+
+	if (data->reject >= 0) {
+		printf("Rejecting connection\n");
+		g_io_channel_shutdown(data->io, TRUE, NULL);
+		return FALSE;
+	}
+
+	printf("Accepting connection\n");
+
+	io_data_ref(data);
+
+	if (opt_update_sec > 0)
+		update_sec_level(data);
+
+	if (!bt_io_accept(data->io, connect_cb, data,
+				(GDestroyNotify) io_data_unref, NULL)) {
+		printf("bt_io_accept() failed\n");
+		io_data_unref(data);
+	}
+
+	return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+	char addr[18];
+	struct io_data *data = user_data;
+	GError *err = NULL;
+
+	if (!bt_io_get(io, &err, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID)) {
+		printf("bt_io_get(OPT_DEST): %s\n", err->message);
+		g_clear_error(&err);
+	} else
+		printf("Got confirmation request for %s\n", addr);
+
+	if (data->accept < 0 && data->reject < 0)
+		return;
+
+	if (data->reject == 0) {
+		printf("Rejecting connection\n");
+		g_io_channel_shutdown(io, TRUE, NULL);
+		return;
+	}
+
+	if (data->voice) {
+		if (!bt_io_set(io, &err, BT_IO_OPT_VOICE, data->voice,
+							BT_IO_OPT_INVALID)) {
+			printf("bt_io_set(OPT_VOICE): %s\n", err->message);
+			g_clear_error(&err);
+		}
+	}
+
+	data->io = g_io_channel_ref(io);
+	io_data_ref(data);
+
+	if (data->accept == 0) {
+		if (!bt_io_accept(io, connect_cb, data,
+					(GDestroyNotify) io_data_unref,
+					&err)) {
+			printf("bt_io_accept() failed: %s\n", err->message);
+			g_clear_error(&err);
+			io_data_unref(data);
+			return;
+		}
+	} else {
+		int seconds = (data->reject > 0) ?
+						data->reject : data->accept;
+		g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
+					confirm_timeout, data,
+					(GDestroyNotify) io_data_unref);
+	}
+}
+
+static void l2cap_connect(const char *src, const char *dst, uint8_t addr_type,
+				uint16_t psm, uint16_t cid, int disconn,
+				int sec, int prio)
+{
+	struct io_data *data;
+	GError *err = NULL;
+	uint8_t src_type;
+
+	printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
+
+	data = io_data_new(NULL, -1, disconn, -1);
+
+	if (addr_type != BDADDR_BREDR)
+		src_type = BDADDR_LE_PUBLIC;
+	else
+		src_type = BDADDR_BREDR;
+
+	if (src)
+		data->io = bt_io_connect(connect_cb, data,
+					(GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_SOURCE, src,
+					BT_IO_OPT_SOURCE_TYPE, src_type,
+					BT_IO_OPT_DEST, dst,
+					BT_IO_OPT_DEST_TYPE, addr_type,
+					BT_IO_OPT_PSM, psm,
+					BT_IO_OPT_CID, cid,
+					BT_IO_OPT_SEC_LEVEL, sec,
+					BT_IO_OPT_PRIORITY, prio,
+					BT_IO_OPT_INVALID);
+	else
+		data->io = bt_io_connect(connect_cb, data,
+					(GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_SOURCE_TYPE, src_type,
+					BT_IO_OPT_DEST, dst,
+					BT_IO_OPT_DEST_TYPE, addr_type,
+					BT_IO_OPT_PSM, psm,
+					BT_IO_OPT_CID, cid,
+					BT_IO_OPT_SEC_LEVEL, sec,
+					BT_IO_OPT_PRIORITY, prio,
+					BT_IO_OPT_INVALID);
+
+	if (!data->io) {
+		printf("Connecting to %s failed: %s\n", dst, err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void l2cap_listen(const char *src, uint8_t addr_type, uint16_t psm,
+				uint16_t cid, int defer, int reject,
+				int disconn, int accept, int sec,
+				gboolean master)
+{
+	struct io_data *data;
+	BtIOConnect conn;
+	BtIOConfirm cfm;
+	GIOChannel *l2_srv;
+	GError *err = NULL;
+
+	if (defer) {
+		conn = NULL;
+		cfm = confirm_cb;
+	} else {
+		conn = connect_cb;
+		cfm = NULL;
+	}
+
+	if (cid)
+		printf("Listening on L2CAP CID 0x%04x (%u)\n", cid, cid);
+	else
+		printf("Listening on L2CAP PSM 0x%04x (%u)\n", psm, psm);
+
+
+	data = io_data_new(NULL, reject, disconn, accept);
+
+	if (src)
+		l2_srv = bt_io_listen(conn, cfm, data,
+					(GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_SOURCE, src,
+					BT_IO_OPT_SOURCE_TYPE, addr_type,
+					BT_IO_OPT_PSM, psm,
+					BT_IO_OPT_CID, cid,
+					BT_IO_OPT_SEC_LEVEL, sec,
+					BT_IO_OPT_MASTER, master,
+					BT_IO_OPT_INVALID);
+	else
+		l2_srv = bt_io_listen(conn, cfm, data,
+					(GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_SOURCE_TYPE, addr_type,
+					BT_IO_OPT_PSM, psm,
+					BT_IO_OPT_CID, cid,
+					BT_IO_OPT_SEC_LEVEL, sec,
+					BT_IO_OPT_MASTER, master,
+					BT_IO_OPT_INVALID);
+
+	if (!l2_srv) {
+		printf("Listening failed: %s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+	g_io_channel_unref(l2_srv);
+}
+
+static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
+						int disconn, int sec)
+{
+	struct io_data *data;
+	GError *err = NULL;
+
+	printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
+
+	data = io_data_new(NULL, -1, disconn, -1);
+
+	if (src)
+		data->io = bt_io_connect(connect_cb, data,
+						(GDestroyNotify) io_data_unref,
+						&err,
+						BT_IO_OPT_SOURCE, src,
+						BT_IO_OPT_DEST, dst,
+						BT_IO_OPT_CHANNEL, ch,
+						BT_IO_OPT_SEC_LEVEL, sec,
+						BT_IO_OPT_INVALID);
+	else
+		data->io = bt_io_connect(connect_cb, data,
+						(GDestroyNotify) io_data_unref,
+						&err,
+						BT_IO_OPT_DEST, dst,
+						BT_IO_OPT_CHANNEL, ch,
+						BT_IO_OPT_SEC_LEVEL, sec,
+						BT_IO_OPT_INVALID);
+
+	if (!data->io) {
+		printf("Connecting to %s failed: %s\n", dst, err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
+				int reject, int disconn, int accept,
+				int sec, gboolean master)
+{
+	struct io_data *data;
+	BtIOConnect conn;
+	BtIOConfirm cfm;
+	GIOChannel *rc_srv;
+	GError *err = NULL;
+
+	if (defer) {
+		conn = NULL;
+		cfm = confirm_cb;
+	} else {
+		conn = connect_cb;
+		cfm = NULL;
+	}
+
+	data = io_data_new(NULL, reject, disconn, accept);
+
+	if (src)
+		rc_srv = bt_io_listen(conn, cfm,
+					data, (GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_SOURCE, src,
+					BT_IO_OPT_CHANNEL, ch,
+					BT_IO_OPT_SEC_LEVEL, sec,
+					BT_IO_OPT_MASTER, master,
+					BT_IO_OPT_INVALID);
+	else
+		rc_srv = bt_io_listen(conn, cfm,
+					data, (GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_CHANNEL, ch,
+					BT_IO_OPT_SEC_LEVEL, sec,
+					BT_IO_OPT_MASTER, master,
+					BT_IO_OPT_INVALID);
+
+	if (!rc_srv) {
+		printf("Listening failed: %s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+	bt_io_get(rc_srv, &err, BT_IO_OPT_CHANNEL, &ch, BT_IO_OPT_INVALID);
+
+	printf("Listening on RFCOMM channel %u\n", ch);
+
+	g_io_channel_unref(rc_srv);
+}
+
+static void sco_connect(const char *src, const char *dst, int disconn,
+								int voice)
+{
+	struct io_data *data;
+	GError *err = NULL;
+
+	printf("Connecting SCO to %s\n", dst);
+
+	data = io_data_new(NULL, -1, disconn, -1);
+
+	if (src)
+		data->io = bt_io_connect(connect_cb, data,
+						(GDestroyNotify) io_data_unref,
+						&err,
+						BT_IO_OPT_SOURCE, src,
+						BT_IO_OPT_DEST, dst,
+						BT_IO_OPT_VOICE, voice,
+						BT_IO_OPT_INVALID);
+	else
+		data->io = bt_io_connect(connect_cb, data,
+						(GDestroyNotify) io_data_unref,
+						&err,
+						BT_IO_OPT_DEST, dst,
+						BT_IO_OPT_VOICE, voice,
+						BT_IO_OPT_INVALID);
+
+	if (!data->io) {
+		printf("Connecting to %s failed: %s\n", dst, err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void sco_listen(const char *src, gboolean defer, int reject,
+				int disconn, int accept, int voice)
+{
+	struct io_data *data;
+	BtIOConnect conn;
+	BtIOConfirm cfm;
+	GIOChannel *sco_srv;
+	GError *err = NULL;
+
+	printf("Listening for SCO connections\n");
+
+	if (defer) {
+		conn = NULL;
+		cfm = confirm_cb;
+	} else {
+		conn = connect_cb;
+		cfm = NULL;
+	}
+
+	data = io_data_new(NULL, reject, disconn, accept);
+
+	data->voice = voice;
+
+	if (src)
+		sco_srv = bt_io_listen(conn, cfm, data,
+					(GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_SOURCE, src,
+					BT_IO_OPT_VOICE, voice,
+					BT_IO_OPT_INVALID);
+	else
+		sco_srv = bt_io_listen(conn, cfm, data,
+					(GDestroyNotify) io_data_unref,
+					&err,
+					BT_IO_OPT_VOICE, voice,
+					BT_IO_OPT_INVALID);
+
+	if (!sco_srv) {
+		printf("Listening failed: %s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+	g_io_channel_unref(sco_srv);
+}
+
+static int opt_channel = -1;
+static int opt_psm = 0;
+static gboolean opt_sco = FALSE;
+static gboolean opt_defer = FALSE;
+static gint opt_voice = 0;
+static char *opt_dev = NULL;
+static int opt_reject = -1;
+static int opt_disconn = -1;
+static int opt_accept = DEFAULT_ACCEPT_TIMEOUT;
+static int opt_sec = 0;
+static gboolean opt_master = FALSE;
+static int opt_priority = 0;
+static int opt_cid = 0;
+static guint8 opt_addr_type = 0;
+
+static GMainLoop *main_loop;
+
+static GOptionEntry options[] = {
+	{ "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
+				"RFCOMM channel" },
+	{ "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+				"L2CAP PSM" },
+	{ "cid", 'j', 0, G_OPTION_ARG_INT, &opt_cid,
+				"L2CAP CID" },
+	{ "addr-type", 't', 0, G_OPTION_ARG_INT, &opt_addr_type,
+				"Address type "
+				"(0 BR/EDR 1 LE Public 2 LE Random" },
+	{ "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
+				"Use SCO" },
+	{ "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
+				"Use DEFER_SETUP for incoming connections" },
+	{ "voice", 'V', 0, G_OPTION_ARG_INT, &opt_voice,
+				"Voice setting "
+				"(0x0060 CVSD, 0x0003 Transparent)" },
+	{ "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
+				"Security level" },
+	{ "update-sec-level", 'U', 0, G_OPTION_ARG_INT, &opt_update_sec,
+				"Update security level" },
+	{ "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
+				"Which HCI device to use" },
+	{ "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
+				"Reject connection after N seconds" },
+	{ "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
+				"Disconnect connection after N seconds" },
+	{ "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
+				"Accept connection after N seconds" },
+	{ "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
+				"Master role switch (incoming connections)" },
+	{ "priority", 'P', 0, G_OPTION_ARG_INT, &opt_priority,
+				"Transmission priority: Setting a priority "
+				"outside the range 0 to 6 requires the"
+				"CAP_NET_ADMIN capability." },
+	{ NULL },
+};
+
+static void sig_term(int sig)
+{
+	g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (!g_option_context_parse(context, &argc, &argv, NULL))
+		exit(EXIT_FAILURE);
+
+	g_option_context_free(context);
+
+	printf("accept=%d reject=%d discon=%d defer=%d sec=%d update_sec=%d"
+		" prio=%d voice=0x%04x\n", opt_accept, opt_reject, opt_disconn,
+		opt_defer, opt_sec, opt_update_sec, opt_priority, opt_voice);
+
+	if (opt_psm || opt_cid) {
+		if (argc > 1)
+			l2cap_connect(opt_dev, argv[1], opt_addr_type,
+					opt_psm, opt_cid, opt_disconn,
+					opt_sec, opt_priority);
+		else
+			l2cap_listen(opt_dev, opt_addr_type, opt_psm, opt_cid,
+					opt_defer, opt_reject, opt_disconn,
+					opt_accept, opt_sec, opt_master);
+	}
+
+	if (opt_channel != -1) {
+		if (argc > 1)
+			rfcomm_connect(opt_dev, argv[1], opt_channel,
+							opt_disconn, opt_sec);
+		else
+			rfcomm_listen(opt_dev, opt_channel, opt_defer,
+					opt_reject, opt_disconn, opt_accept,
+					opt_sec, opt_master);
+	}
+
+	if (opt_sco) {
+		if (argc > 1)
+			sco_connect(opt_dev, argv[1], opt_disconn, opt_voice);
+		else
+			sco_listen(opt_dev, opt_defer, opt_reject,
+					opt_disconn, opt_accept, opt_voice);
+	}
+
+	signal(SIGTERM, sig_term);
+	signal(SIGINT, sig_term);
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(main_loop);
+
+	g_main_loop_unref(main_loop);
+
+	printf("Exiting\n");
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/bluez/repo/tools/btmgmt.c b/bluez/repo/tools/btmgmt.c
new file mode 100644
index 0000000..f82461e
--- /dev/null
+++ b/bluez/repo/tools/btmgmt.c
@@ -0,0 +1,4737 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <wordexp.h>
+#include <ctype.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "src/uuid-helper.h"
+#include "lib/mgmt.h"
+
+#include "client/display.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
+static struct mgmt *mgmt = NULL;
+static uint16_t mgmt_index = MGMT_INDEX_NONE;
+
+static bool discovery = false;
+static bool resolve_names = true;
+static bool interactive = false;
+
+static char *saved_prompt = NULL;
+static int saved_point = 0;
+
+static struct {
+	uint16_t index;
+	uint16_t req;
+	struct mgmt_addr_info addr;
+} prompt = {
+	.index = MGMT_INDEX_NONE,
+};
+
+static int pending_index = 0;
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#define PROMPT_ON	COLOR_BLUE "[mgmt]" COLOR_OFF "# "
+
+static void update_prompt(uint16_t index)
+{
+	char str[32];
+
+	if (index == MGMT_INDEX_NONE)
+		snprintf(str, sizeof(str), "%s# ",
+					COLOR_BLUE "[mgmt]" COLOR_OFF);
+	else
+		snprintf(str, sizeof(str),
+				COLOR_BLUE "[hci%u]" COLOR_OFF "# ", index);
+
+	if (saved_prompt) {
+		free(saved_prompt);
+		saved_prompt = strdup(str);
+		return;
+	}
+
+	rl_set_prompt(str);
+}
+
+static void noninteractive_quit(int status)
+{
+	if (interactive)
+		return;
+
+	if (status == EXIT_SUCCESS)
+		mainloop_exit_success();
+	else
+		mainloop_exit_failure();
+}
+
+#define print(fmt, arg...) do { \
+	if (interactive) \
+		rl_printf(fmt "\n", ## arg); \
+	else \
+		printf(fmt "\n", ## arg); \
+} while (0)
+
+#define error(fmt, arg...) do { \
+	if (interactive) \
+		rl_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
+	else \
+		fprintf(stderr, fmt "\n", ## arg); \
+} while (0)
+
+static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen)
+{
+	size_t i, len;
+
+	len = MIN((strlen(hexstr) / 2), buflen);
+	memset(buf, 0, len);
+
+	for (i = 0; i < len; i++)
+		sscanf(hexstr + (i * 2), "%02hhX", &buf[i]);
+
+	return len;
+}
+
+static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str,
+								size_t strlen)
+{
+	size_t i;
+
+	for (i = 0; i < buflen && i < (strlen / 2); i++)
+		sprintf(str + (i * 2), "%02x", buf[i]);
+
+	return i;
+}
+
+static void print_eir(const uint8_t *eir, uint16_t eir_len)
+{
+	uint16_t parsed = 0;
+	char str[33];
+
+	while (parsed < eir_len - 1) {
+		uint8_t field_len = eir[0];
+
+		if (field_len == 0)
+			break;
+
+		parsed += field_len + 1;
+
+		if (parsed > eir_len)
+			break;
+
+		switch (eir[1]) {
+		case 0x01:
+			print("Flags: 0x%02x", eir[2]);
+			break;
+		case 0x0d:
+			print("Class of Device: 0x%02x%02x%02x",
+						eir[4], eir[3], eir[2]);
+			break;
+		case 0x0e:
+			bin2hex(eir + 2, 16, str, sizeof(str));
+			print("SSP Hash C-192: %s", str);
+			break;
+		case 0x0f:
+			bin2hex(eir + 2, 16, str, sizeof(str));
+			print("SSP Rand R-192: %s", str);
+			break;
+		case 0x1b:
+			ba2str((bdaddr_t *) (eir + 2), str);
+			print("LE Device Address: %s (%s)", str,
+					eir[8] ? "random" : "public");
+			break;
+		case 0x1c:
+			print("LE Role: 0x%02x", eir[2]);
+			break;
+		case 0x1d:
+			bin2hex(eir + 2, 16, str, sizeof(str));
+			print("SSP Hash C-256: %s", str);
+			break;
+		case 0x1e:
+			bin2hex(eir + 2, 16, str, sizeof(str));
+			print("SSP Rand R-256: %s", str);
+			break;
+		case 0x22:
+			bin2hex(eir + 2, 16, str, sizeof(str));
+			print("LE SC Confirmation Value: %s", str);
+			break;
+		case 0x23:
+			bin2hex(eir + 2, 16, str, sizeof(str));
+			print("LE SC Random Value: %s", str);
+			break;
+		default:
+			print("Type %u: %u byte%s", eir[1], field_len - 1,
+					(field_len - 1) == 1 ? "" : "s");
+			break;
+		}
+
+		eir += field_len + 1;
+	}
+}
+
+static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
+{
+	char identity_path[PATH_MAX];
+	char *addr, *key;
+	unsigned int type;
+	int n;
+	FILE *fp;
+
+	snprintf(identity_path, sizeof(identity_path),
+			"/sys/kernel/debug/bluetooth/hci%u/identity", index);
+
+	fp = fopen(identity_path, "r");
+	if (!fp) {
+		error("Failed to open identity file: %s", strerror(errno));
+		return false;
+	}
+
+	n = fscanf(fp, "%m[0-9a-f:] (type %u) %m[0-9a-f]", &addr, &type, &key);
+
+	fclose(fp);
+
+	if (n != 3)
+		return false;
+
+	str2ba(addr, &irk->addr.bdaddr);
+	hex2bin(key, irk->val, sizeof(irk->val));
+
+	free(addr);
+	free(key);
+
+	switch (type) {
+	case 0:
+		irk->addr.type = BDADDR_LE_PUBLIC;
+		break;
+	case 1:
+		irk->addr.type = BDADDR_LE_RANDOM;
+		break;
+	default:
+		error("Invalid address type %u", type);
+		return false;
+	}
+
+	return true;
+}
+
+static void controller_error(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	const struct mgmt_ev_controller_error *ev = param;
+
+	if (len < sizeof(*ev)) {
+		error("Too short (%u bytes) controller error event", len);
+		return;
+	}
+
+	print("hci%u error 0x%02x", index, ev->error_code);
+}
+
+static void index_added(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	print("hci%u added", index);
+}
+
+static void index_removed(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	print("hci%u removed", index);
+}
+
+static void unconf_index_added(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	print("hci%u added (unconfigured)", index);
+}
+
+static void unconf_index_removed(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	print("hci%u removed (unconfigured)", index);
+}
+
+static void ext_index_added(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	const struct mgmt_ev_ext_index_added *ev = param;
+
+	print("hci%u added (type %u bus %u)", index, ev->type, ev->bus);
+}
+
+static void ext_index_removed(uint16_t index, uint16_t len,
+				const void *param, void *user_data)
+{
+	const struct mgmt_ev_ext_index_removed *ev = param;
+
+	print("hci%u removed (type %u bus %u)", index, ev->type, ev->bus);
+}
+
+static const char *options_str[] = {
+				"external",
+				"public-address",
+};
+
+static const char *options2str(uint32_t options)
+{
+	static char str[256];
+	unsigned i;
+	int off;
+
+	off = 0;
+	str[0] = '\0';
+
+	for (i = 0; i < NELEM(options_str); i++) {
+		if ((options & (1 << i)) != 0)
+			off += snprintf(str + off, sizeof(str) - off, "%s ",
+							options_str[i]);
+	}
+
+	return str;
+}
+
+static void new_config_options(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const uint32_t *ev = param;
+
+	if (len < sizeof(*ev)) {
+		error("Too short new_config_options event (%u)", len);
+		return;
+	}
+
+	print("hci%u new_config_options: %s", index, options2str(get_le32(ev)));
+}
+
+static const char *settings_str[] = {
+				"powered",
+				"connectable",
+				"fast-connectable",
+				"discoverable",
+				"bondable",
+				"link-security",
+				"ssp",
+				"br/edr",
+				"hs",
+				"le",
+				"advertising",
+				"secure-conn",
+				"debug-keys",
+				"privacy",
+				"configuration",
+				"static-addr",
+};
+
+static const char *settings2str(uint32_t settings)
+{
+	static char str[256];
+	unsigned i;
+	int off;
+
+	off = 0;
+	str[0] = '\0';
+
+	for (i = 0; i < NELEM(settings_str); i++) {
+		if ((settings & (1 << i)) != 0)
+			off += snprintf(str + off, sizeof(str) - off, "%s ",
+							settings_str[i]);
+	}
+
+	return str;
+}
+
+static void new_settings(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const uint32_t *ev = param;
+
+	if (len < sizeof(*ev)) {
+		error("Too short new_settings event (%u)", len);
+		return;
+	}
+
+	print("hci%u new_settings: %s", index, settings2str(get_le32(ev)));
+}
+
+static void discovering(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_discovering *ev = param;
+
+	if (len < sizeof(*ev)) {
+		error("Too short (%u bytes) discovering event", len);
+		return;
+	}
+
+	print("hci%u type %u discovering %s", index, ev->type,
+					ev->discovering ? "on" : "off");
+
+	if (ev->discovering == 0 && discovery)
+		return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void new_link_key(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_new_link_key *ev = param;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid new_link_key length (%u bytes)", len);
+		return;
+	}
+
+	ba2str(&ev->key.addr.bdaddr, addr);
+	print("hci%u new_link_key %s type 0x%02x pin_len %d store_hint %u",
+		index, addr, ev->key.type, ev->key.pin_len, ev->store_hint);
+}
+
+static const char *typestr(uint8_t type)
+{
+	static const char *str[] = { "BR/EDR", "LE Public", "LE Random" };
+
+	if (type <= BDADDR_LE_RANDOM)
+		return str[type];
+
+	return "(unknown)";
+}
+
+static void connected(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_device_connected *ev = param;
+	uint16_t eir_len;
+	char addr[18];
+
+	if (len < sizeof(*ev)) {
+		error("Invalid connected event length (%u bytes)", len);
+		return;
+	}
+
+	eir_len = get_le16(&ev->eir_len);
+	if (len != sizeof(*ev) + eir_len) {
+		error("Invalid connected event length (%u != eir_len %u)",
+								len, eir_len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s type %s connected eir_len %u", index, addr,
+					typestr(ev->addr.type), eir_len);
+}
+
+static void release_prompt(void)
+{
+	if (!interactive)
+		return;
+
+	memset(&prompt, 0, sizeof(prompt));
+	prompt.index = MGMT_INDEX_NONE;
+
+	if (!saved_prompt)
+		return;
+
+	/* 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;
+}
+
+static void disconnected(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_device_disconnected *ev = param;
+	char addr[18];
+	uint8_t reason;
+
+	if (len < sizeof(struct mgmt_addr_info)) {
+		error("Invalid disconnected event length (%u bytes)", len);
+		return;
+	}
+
+	if (!memcmp(&ev->addr, &prompt.addr, sizeof(ev->addr)))
+		release_prompt();
+
+	if (len < sizeof(*ev))
+		reason = MGMT_DEV_DISCONN_UNKNOWN;
+	else
+		reason = ev->reason;
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s type %s disconnected with reason %u",
+			index, addr, typestr(ev->addr.type), reason);
+}
+
+static void conn_failed(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_connect_failed *ev = param;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid connect_failed event length (%u bytes)", len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s type %s connect failed (status 0x%02x, %s)",
+			index, addr, typestr(ev->addr.type), ev->status,
+			mgmt_errstr(ev->status));
+}
+
+static void auth_failed(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_auth_failed *ev = param;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid auth_failed event length (%u bytes)", len);
+		return;
+	}
+
+	if (!memcmp(&ev->addr, &prompt.addr, sizeof(ev->addr)))
+		release_prompt();
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s auth failed with status 0x%02x (%s)",
+			index, addr, ev->status, mgmt_errstr(ev->status));
+}
+
+static void class_of_dev_changed(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_class_of_dev_changed *ev = param;
+
+	if (len != sizeof(*ev)) {
+		error("Invalid class_of_dev_changed length (%u bytes)", len);
+		return;
+	}
+
+	print("hci%u class of device changed: 0x%02x%02x%02x", index,
+			ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]);
+}
+
+static void local_name_changed(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_local_name_changed *ev = param;
+
+	if (len != sizeof(*ev)) {
+		error("Invalid local_name_changed length (%u bytes)", len);
+		return;
+	}
+
+	print("hci%u name changed: %s", index, ev->name);
+}
+
+static void confirm_name_rsp(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_confirm_name *rp = param;
+	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("confirm_name failed with status 0x%02x (%s)", status,
+							mgmt_errstr(status));
+		return;
+	}
+
+	if (len != sizeof(*rp)) {
+		error("confirm_name rsp length %u instead of %zu",
+							len, sizeof(*rp));
+		return;
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status != 0)
+		error("confirm_name for %s failed: 0x%02x (%s)",
+			addr, status, mgmt_errstr(status));
+	else
+		print("confirm_name succeeded for %s", addr);
+}
+
+static char *eir_get_name(const uint8_t *eir, uint16_t eir_len)
+{
+	uint8_t parsed = 0;
+
+	if (eir_len < 2)
+		return NULL;
+
+	while (parsed < eir_len - 1) {
+		uint8_t field_len = eir[0];
+
+		if (field_len == 0)
+			break;
+
+		parsed += field_len + 1;
+
+		if (parsed > eir_len)
+			break;
+
+		/* Check for short of complete name */
+		if (eir[1] == 0x09 || eir[1] == 0x08)
+			return strndup((char *) &eir[2], field_len - 1);
+
+		eir += field_len + 1;
+	}
+
+	return NULL;
+}
+
+static unsigned int eir_get_flags(const uint8_t *eir, uint16_t eir_len)
+{
+	uint8_t parsed = 0;
+
+	if (eir_len < 2)
+		return 0;
+
+	while (parsed < eir_len - 1) {
+		uint8_t field_len = eir[0];
+
+		if (field_len == 0)
+			break;
+
+		parsed += field_len + 1;
+
+		if (parsed > eir_len)
+			break;
+
+		/* Check for flags */
+		if (eir[1] == 0x01)
+			return eir[2];
+
+		eir += field_len + 1;
+	}
+
+	return 0;
+}
+
+static void device_found(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_device_found *ev = param;
+	struct mgmt *mgmt = user_data;
+	uint16_t eir_len;
+	uint32_t flags;
+
+	if (len < sizeof(*ev)) {
+		error("Too short device_found length (%u bytes)", len);
+		return;
+	}
+
+	flags = btohl(ev->flags);
+
+	eir_len = get_le16(&ev->eir_len);
+	if (len != sizeof(*ev) + eir_len) {
+		error("dev_found: expected %zu bytes, got %u bytes",
+						sizeof(*ev) + eir_len, len);
+		return;
+	}
+
+	if (discovery) {
+		char addr[18], *name;
+
+		ba2str(&ev->addr.bdaddr, addr);
+		print("hci%u dev_found: %s type %s rssi %d "
+			"flags 0x%04x ", index, addr,
+			typestr(ev->addr.type), ev->rssi, flags);
+
+		if (ev->addr.type != BDADDR_BREDR)
+			print("AD flags 0x%02x ",
+					eir_get_flags(ev->eir, eir_len));
+
+		name = eir_get_name(ev->eir, eir_len);
+		if (name)
+			print("name %s", name);
+		else
+			print("eir_len %u", eir_len);
+
+		free(name);
+	}
+
+	if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) {
+		struct mgmt_cp_confirm_name cp;
+
+		memset(&cp, 0, sizeof(cp));
+		memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+		if (resolve_names)
+			cp.name_known = 0;
+		else
+			cp.name_known = 1;
+
+		mgmt_reply(mgmt, MGMT_OP_CONFIRM_NAME, index, sizeof(cp), &cp,
+						confirm_name_rsp, NULL, NULL);
+	}
+}
+
+static void pin_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("PIN Code reply failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("PIN Reply successful");
+}
+
+static int mgmt_pin_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr,
+					const char *pin, size_t len)
+{
+	struct mgmt_cp_pin_code_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(cp.addr));
+	cp.pin_len = len;
+	memcpy(cp.pin_code, pin, len);
+
+	return mgmt_reply(mgmt, MGMT_OP_PIN_CODE_REPLY, index, sizeof(cp), &cp,
+							pin_rsp, NULL, NULL);
+}
+
+static void pin_neg_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("PIN Neg reply failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("PIN Negative Reply successful");
+}
+
+static int mgmt_pin_neg_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr)
+{
+	struct mgmt_cp_pin_code_neg_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(cp.addr));
+
+	return mgmt_reply(mgmt, MGMT_OP_PIN_CODE_NEG_REPLY, index,
+				sizeof(cp), &cp, pin_neg_rsp, NULL, NULL);
+}
+
+static void confirm_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("User Confirm reply failed. status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("User Confirm Reply successful");
+}
+
+static int mgmt_confirm_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr)
+{
+	struct mgmt_cp_user_confirm_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(*addr));
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_REPLY, index,
+				sizeof(cp), &cp, confirm_rsp, NULL, NULL);
+}
+
+static void confirm_neg_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("Confirm Neg reply failed. status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("User Confirm Negative Reply successful");
+}
+
+static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr)
+{
+	struct mgmt_cp_user_confirm_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(*addr));
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
+				sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL);
+}
+
+static void passkey_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("User Passkey reply failed. status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("User Passkey Reply successful");
+}
+
+static int mgmt_passkey_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr,
+					uint32_t passkey)
+{
+	struct mgmt_cp_user_passkey_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(*addr));
+	put_le32(passkey, &cp.passkey);
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_REPLY, index,
+				sizeof(cp), &cp, passkey_rsp, NULL, NULL);
+}
+
+static void passkey_neg_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("Passkey Neg reply failed. status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("User Passkey Negative Reply successful");
+}
+
+static int mgmt_passkey_neg_reply(struct mgmt *mgmt, uint16_t index,
+					const struct mgmt_addr_info *addr)
+{
+	struct mgmt_cp_user_passkey_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, addr, sizeof(*addr));
+
+	return mgmt_reply(mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY, index,
+				sizeof(cp), &cp, passkey_neg_rsp, NULL, NULL);
+}
+
+static bool prompt_input(const char *input)
+{
+	size_t len;
+
+	if (!prompt.req)
+		return false;
+
+	len = strlen(input);
+
+	switch (prompt.req) {
+	case MGMT_EV_PIN_CODE_REQUEST:
+		if (len)
+			mgmt_pin_reply(mgmt, prompt.index, &prompt.addr,
+								input, len);
+		else
+			mgmt_pin_neg_reply(mgmt, prompt.index, &prompt.addr);
+		break;
+	case MGMT_EV_USER_PASSKEY_REQUEST:
+		if (strlen(input) > 0)
+			mgmt_passkey_reply(mgmt, prompt.index, &prompt.addr,
+								atoi(input));
+		else
+			mgmt_passkey_neg_reply(mgmt, prompt.index,
+								&prompt.addr);
+		break;
+	case MGMT_EV_USER_CONFIRM_REQUEST:
+		if (input[0] == 'y' || input[0] == 'Y')
+			mgmt_confirm_reply(mgmt, prompt.index, &prompt.addr);
+		else
+			mgmt_confirm_neg_reply(mgmt, prompt.index,
+								&prompt.addr);
+		break;
+	}
+
+	release_prompt();
+
+	return true;
+}
+
+static void interactive_prompt(const char *msg)
+{
+	if (saved_prompt)
+		return;
+
+	saved_prompt = strdup(rl_prompt);
+	if (!saved_prompt)
+		return;
+
+	saved_point = rl_point;
+
+	rl_set_prompt("");
+	rl_redisplay();
+
+	rl_set_prompt(msg);
+
+	rl_replace_line("", 0);
+	rl_redisplay();
+}
+
+static size_t get_input(char *buf, size_t buf_len)
+{
+	size_t len;
+
+	if (!fgets(buf, buf_len, stdin))
+		return 0;
+
+	len = strlen(buf);
+
+	/* Remove trailing white-space */
+	while (len && isspace(buf[len - 1]))
+		buf[--len] = '\0';
+
+	return len;
+}
+
+static void ask(uint16_t index, uint16_t req, const struct mgmt_addr_info *addr,
+						const char *fmt, ...)
+{
+	char msg[256], buf[18];
+	va_list ap;
+	int off;
+
+	prompt.index = index;
+	prompt.req = req;
+	memcpy(&prompt.addr, addr, sizeof(*addr));
+
+	va_start(ap, fmt);
+	off = vsnprintf(msg, sizeof(msg), fmt, ap);
+	va_end(ap);
+
+	snprintf(msg + off, sizeof(msg) - off, " %s ",
+					COLOR_BOLDGRAY ">>" COLOR_OFF);
+
+	if (interactive) {
+		interactive_prompt(msg);
+		va_end(ap);
+		return;
+	}
+
+	printf("%s", msg);
+	fflush(stdout);
+
+	memset(buf, 0, sizeof(buf));
+	get_input(buf, sizeof(buf));
+	prompt_input(buf);
+}
+
+static void request_pin(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid pin_code request length (%u bytes)", len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s request PIN", index, addr);
+
+	ask(index, MGMT_EV_PIN_CODE_REQUEST, &ev->addr,
+				"PIN Request (press enter to reject)");
+}
+
+static void user_confirm(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_confirm_request *ev = param;
+	uint32_t val;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid user_confirm request length (%u)", len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	val = get_le32(&ev->value);
+
+	print("hci%u %s User Confirm %06u hint %u", index, addr,
+							val, ev->confirm_hint);
+
+	if (ev->confirm_hint)
+		ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr,
+				"Accept pairing with %s (yes/no)", addr);
+	else
+		ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr,
+			"Confirm value %06u for %s (yes/no)", val, addr);
+}
+
+static void request_passkey(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_passkey_request *ev = param;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid passkey request length (%u bytes)", len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s request passkey", index, addr);
+
+	ask(index, MGMT_EV_USER_PASSKEY_REQUEST, &ev->addr,
+			"Passkey Request (press enter to reject)");
+}
+
+static void passkey_notify(uint16_t index, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_passkey_notify *ev = param;
+	char addr[18];
+
+	if (len != sizeof(*ev)) {
+		error("Invalid passkey request length (%u bytes)", len);
+		return;
+	}
+
+	ba2str(&ev->addr.bdaddr, addr);
+	print("hci%u %s request passkey", index, addr);
+
+	print("Passkey Notify: %06u (entered %u)", get_le32(&ev->passkey),
+								ev->entered);
+}
+
+static void local_oob_data_updated(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_local_oob_data_updated *ev = param;
+	uint16_t eir_len;
+
+	if (len < sizeof(*ev)) {
+		error("Too small (%u bytes) local_oob_updated event", len);
+		return;
+	}
+
+	eir_len = le16_to_cpu(ev->eir_len);
+	if (len != sizeof(*ev) + eir_len) {
+		error("local_oob_updated: expected %zu bytes, got %u bytes",
+						sizeof(*ev) + eir_len, len);
+		return;
+	}
+
+	print("hci%u oob data updated: type %u len %u", index,
+						ev->type, eir_len);
+}
+
+static void advertising_added(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_advertising_added *ev = param;
+
+	if (len < sizeof(*ev)) {
+		error("Too small (%u bytes) advertising_added event", len);
+		return;
+	}
+
+	print("hci%u advertising_added: instance %u", index, ev->instance);
+}
+
+static void advertising_removed(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_advertising_removed *ev = param;
+
+	if (len < sizeof(*ev)) {
+		error("Too small (%u bytes) advertising_removed event", len);
+		return;
+	}
+
+	print("hci%u advertising_removed: instance %u", index, ev->instance);
+}
+
+static void version_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_version *rp = param;
+
+	if (status != 0) {
+		error("Reading mgmt version failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small version reply (%u bytes)", len);
+		goto done;
+	}
+
+	print("MGMT Version %u, revision %u", rp->version,
+						get_le16(&rp->revision));
+
+done:
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	if (mgmt_send(mgmt, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE,
+				0, NULL, version_rsp, NULL, NULL) == 0) {
+		error("Unable to send read_version cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void commands_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_commands *rp = param;
+	uint16_t num_commands, num_events;
+	const uint16_t *opcode;
+	size_t expected_len;
+	int i;
+
+	if (status != 0) {
+		error("Read Supported Commands failed: status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small commands reply (%u bytes)", len);
+		goto done;
+	}
+
+	num_commands = get_le16(&rp->num_commands);
+	num_events = get_le16(&rp->num_events);
+
+	expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) +
+						num_events * sizeof(uint16_t);
+
+	if (len < expected_len) {
+		error("Too small commands reply (%u != %zu)",
+							len, expected_len);
+		goto done;
+	}
+
+	opcode = rp->opcodes;
+
+	print("%u commands:", num_commands);
+	for (i = 0; i < num_commands; i++) {
+		uint16_t op = get_le16(opcode++);
+		print("\t%s (0x%04x)", mgmt_opstr(op), op);
+	}
+
+	print("%u events:", num_events);
+	for (i = 0; i < num_events; i++) {
+		uint16_t ev = get_le16(opcode++);
+		print("\t%s (0x%04x)", mgmt_evstr(ev), ev);
+	}
+
+done:
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	if (mgmt_send(mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE,
+				0, NULL, commands_rsp, NULL, NULL) == 0) {
+		error("Unable to send read_commands cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_config_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t supported_options, missing_options;
+
+	if (status != 0) {
+		error("Reading hci%u config failed with status 0x%02x (%s)",
+					index, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small info reply (%u bytes)", len);
+		goto done;
+	}
+
+	print("hci%u:\tUnconfigured controller", index);
+
+	print("\tmanufacturer %u", le16_to_cpu(rp->manufacturer));
+
+	supported_options = le32_to_cpu(rp->supported_options);
+	print("\tsupported options: %s", options2str(supported_options));
+
+	missing_options = le32_to_cpu(rp->missing_options);
+	print("\tmissing options: %s", options2str(missing_options));
+
+done:
+	pending_index--;
+
+	if (pending_index > 0)
+		return;
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_unconf_index_list *rp = param;
+	uint16_t count;
+	unsigned int i;
+
+	if (status != 0) {
+		error("Reading index list failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small index list reply (%u bytes)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	count = le16_to_cpu(rp->num_controllers);
+
+	if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
+		error("Index count (%u) doesn't match reply length (%u)",
+								count, len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Unconfigured index list with %u item%s",
+					count, count != 1 ? "s" : "");
+
+	for (i = 0; i < count; i++) {
+		uint16_t index = le16_to_cpu(rp->index[i]);
+
+		if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+				config_info_rsp, UINT_TO_PTR(index), NULL)) {
+			error("Unable to send read_config_info cmd");
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+
+		pending_index++;
+	}
+
+	if (!count)
+		noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	if (index == MGMT_INDEX_NONE) {
+		if (!mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
+					MGMT_INDEX_NONE, 0, NULL,
+					unconf_index_rsp, mgmt, NULL)) {
+			error("Unable to send unconf_index_list cmd");
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+
+		return;
+	}
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+				config_info_rsp, UINT_TO_PTR(index), NULL)) {
+		error("Unable to send read_config_info cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void config_options_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_config_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t supported_options, missing_options;
+
+	if (status != 0) {
+		error("Reading hci%u config failed with status 0x%02x (%s)",
+					index, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small info reply (%u bytes)", len);
+		goto done;
+	}
+
+	print("hci%u:\tConfiguration options", index);
+
+	supported_options = le32_to_cpu(rp->supported_options);
+	print("\tsupported options: %s", options2str(supported_options));
+
+	missing_options = le32_to_cpu(rp->missing_options);
+	print("\tmissing options: %s", options2str(missing_options));
+
+done:
+	pending_index--;
+
+	if (pending_index > 0)
+		return;
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void info_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t supported_settings, current_settings;
+	char addr[18];
+
+	if (status != 0) {
+		error("Reading hci%u info failed with status 0x%02x (%s)",
+					index, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small info reply (%u bytes)", len);
+		goto done;
+	}
+
+	print("hci%u:\tPrimary controller", index);
+
+	ba2str(&rp->bdaddr, addr);
+	print("\taddr %s version %u manufacturer %u class 0x%02x%02x%02x",
+			addr, rp->version, le16_to_cpu(rp->manufacturer),
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+
+	supported_settings = le32_to_cpu(rp->supported_settings);
+	print("\tsupported settings: %s", settings2str(supported_settings));
+
+	current_settings = le32_to_cpu(rp->current_settings);
+	print("\tcurrent settings: %s", settings2str(current_settings));
+
+	print("\tname %s", rp->name);
+	print("\tshort name %s", rp->short_name);
+
+	if (supported_settings & MGMT_SETTING_CONFIGURATION) {
+		if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+					index, 0, NULL, config_options_rsp,
+					UINT_TO_PTR(index), NULL)) {
+			error("Unable to send read_config cmd");
+			goto done;
+		}
+		return;
+	}
+
+done:
+	pending_index--;
+
+	if (pending_index > 0)
+		return;
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void index_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_index_list *rp = param;
+	struct mgmt *mgmt = user_data;
+	uint16_t count;
+	unsigned int i;
+
+	if (status != 0) {
+		error("Reading index list failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small index list reply (%u bytes)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	count = le16_to_cpu(rp->num_controllers);
+
+	if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
+		error("Index count (%u) doesn't match reply length (%u)",
+								count, len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Index list with %u item%s", count, count != 1 ? "s" : "");
+
+	for (i = 0; i < count; i++) {
+		uint16_t index = le16_to_cpu(rp->index[i]);
+
+		if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+					info_rsp, UINT_TO_PTR(index), NULL)) {
+			error("Unable to send read_info cmd");
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+
+		pending_index++;
+	}
+
+	if (!count)
+		noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	if (index == MGMT_INDEX_NONE) {
+		if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+					MGMT_INDEX_NONE, 0, NULL,
+					index_rsp, mgmt, NULL)) {
+			error("Unable to send index_list cmd");
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+
+		return;
+	}
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
+						UINT_TO_PTR(index), NULL)) {
+		error("Unable to send read_info cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void ext_index_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_ext_index_list *rp = param;
+	uint16_t count, index_filter = PTR_TO_UINT(user_data);
+	unsigned int i;
+
+	if (status != 0) {
+		error("Reading ext index list failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small ext index list reply (%u bytes)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	count = get_le16(&rp->num_controllers);
+
+	if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) {
+		error("Index count (%u) doesn't match reply length (%u)",
+								count, len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Extended index list with %u item%s",
+					count, count != 1 ? "s" : "");
+
+	for (i = 0; i < count; i++) {
+		uint16_t index = le16_to_cpu(rp->entry[i].index);
+		char *busstr = hci_bustostr(rp->entry[i].bus);
+
+		if (index_filter != MGMT_INDEX_NONE && index_filter != index)
+			continue;
+
+		switch (rp->entry[i].type) {
+		case 0x00:
+			print("Primary controller (hci%u,%s)", index, busstr);
+			if (!mgmt_send(mgmt, MGMT_OP_READ_INFO,
+						index, 0, NULL, info_rsp,
+						UINT_TO_PTR(index), NULL)) {
+				error("Unable to send read_info cmd");
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			pending_index++;
+			break;
+		case 0x01:
+			print("Unconfigured controller (hci%u,%s)",
+								index, busstr);
+			if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+						index, 0, NULL, config_info_rsp,
+						UINT_TO_PTR(index), NULL)) {
+				error("Unable to send read_config cmd");
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			pending_index++;
+			break;
+		case 0x02:
+			print("AMP controller (hci%u,%s)", index, busstr);
+			break;
+		default:
+			print("Type %u controller (hci%u,%s)",
+					rp->entry[i].type, index, busstr);
+			break;
+		}
+	}
+
+	print("");
+
+	if (!count)
+		noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_extinfo(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST,
+				MGMT_INDEX_NONE, 0, NULL,
+				ext_index_rsp, UINT_TO_PTR(index), NULL)) {
+		error("Unable to send ext_index_list cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void auto_power_enable_rsp(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	print("Successfully enabled controller with index %u", index);
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void auto_power_info_rsp(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t supported_settings, current_settings, missing_settings;
+	uint8_t val = 0x01;
+
+	if (status) {
+		error("Reading info failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	supported_settings = le32_to_cpu(rp->supported_settings);
+	current_settings = le32_to_cpu(rp->current_settings);
+	missing_settings = current_settings ^ supported_settings;
+
+	if (missing_settings & MGMT_SETTING_BREDR)
+		mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, sizeof(val), &val,
+							NULL, NULL, NULL);
+
+	if (missing_settings & MGMT_SETTING_SSP)
+		mgmt_send(mgmt, MGMT_OP_SET_SSP, index, sizeof(val), &val,
+							NULL, NULL, NULL);
+
+	if (missing_settings & MGMT_SETTING_LE)
+		mgmt_send(mgmt, MGMT_OP_SET_LE, index, sizeof(val), &val,
+							NULL, NULL, NULL);
+
+	if (missing_settings & MGMT_SETTING_SECURE_CONN)
+		mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index,
+							sizeof(val), &val,
+							NULL, NULL, NULL);
+
+	if (missing_settings & MGMT_SETTING_BONDABLE)
+		mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, sizeof(val), &val,
+							NULL, NULL, NULL);
+
+	if (current_settings & MGMT_SETTING_POWERED)
+		return noninteractive_quit(EXIT_SUCCESS);
+
+	if (!mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, sizeof(val), &val,
+						auto_power_enable_rsp,
+						UINT_TO_PTR(index), NULL)) {
+		error("Unable to send set powerd cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void auto_power_index_evt(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index_filter = PTR_TO_UINT(user_data);
+
+	if (index != index_filter)
+		return;
+
+	print("New controller with index %u", index);
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+						auto_power_info_rsp,
+						UINT_TO_PTR(index), NULL)) {
+		error("Unable to send read info cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void auto_power_index_rsp(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_index_list *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint16_t i, count;
+	bool found = false;
+
+	if (status) {
+		error("Reading index list failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	count = le16_to_cpu(rp->num_controllers);
+	for (i = 0; i < count; i++) {
+		if (le16_to_cpu(rp->index[i]) == index)
+			found = true;
+	}
+
+	if (!found) {
+		print("Waiting for index %u to appear", index);
+
+		mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index,
+						auto_power_index_evt,
+						UINT_TO_PTR(index), NULL);
+		return;
+	}
+
+	print("Found controller with index %u", index);
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+						auto_power_info_rsp,
+						UINT_TO_PTR(index), NULL)) {
+		error("Unable to send read info cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_auto_power(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+						auto_power_index_rsp,
+						UINT_TO_PTR(index), NULL)) {
+		error("Unable to send read index list cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+/* Wrapper to get the index and opcode to the response callback */
+struct command_data {
+	uint16_t id;
+	uint16_t op;
+	void (*callback) (uint16_t id, uint16_t op, uint8_t status,
+					uint16_t len, const void *param);
+};
+
+static void cmd_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	struct command_data *data = user_data;
+
+	data->callback(data->op, data->id, status, len, param);
+}
+
+static unsigned int send_cmd(struct mgmt *mgmt, uint16_t op, uint16_t id,
+				uint16_t len, const void *param,
+				void (*cb)(uint16_t id, uint16_t op,
+						uint8_t status, uint16_t len,
+						const void *param))
+{
+	struct command_data *data;
+	unsigned int send_id;
+
+	data = new0(struct command_data, 1);
+	if (!data)
+		return 0;
+
+	data->id = id;
+	data->op = op;
+	data->callback = cb;
+
+	send_id = mgmt_send(mgmt, op, id, len, param, cmd_rsp, data, free);
+	if (send_id == 0)
+		free(data);
+
+	return send_id;
+}
+
+static void setting_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
+							const void *param)
+{
+	const uint32_t *rp = param;
+
+	if (status != 0) {
+		error("%s for hci%u failed with status 0x%02x (%s)",
+			mgmt_opstr(op), id, status, mgmt_errstr(status));
+		goto done;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small %s response (%u bytes)",
+							mgmt_opstr(op), len);
+		goto done;
+	}
+
+	print("hci%u %s complete, settings: %s", id, mgmt_opstr(op),
+						settings2str(get_le32(rp)));
+
+done:
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
+							int argc, char **argv)
+{
+	uint8_t val;
+
+	if (argc < 2) {
+		print("Specify \"on\" or \"off\"");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+		val = 1;
+	else if (strcasecmp(argv[1], "off") == 0)
+		val = 0;
+	else
+		val = atoi(argv[1]);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (send_cmd(mgmt, op, index, sizeof(val), &val, setting_rsp) == 0) {
+		error("Unable to send %s cmd", mgmt_opstr(op));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_power(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_POWERED, argc, argv);
+}
+
+static void cmd_discov(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_set_discoverable cp;
+
+	if (argc < 2) {
+		print("Usage: %s <yes/no/limited> [timeout]", argv[0]);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+		cp.val = 1;
+	else if (strcasecmp(argv[1], "off") == 0)
+		cp.val = 0;
+	else if (strcasecmp(argv[1], "limited") == 0)
+		cp.val = 2;
+	else
+		cp.val = atoi(argv[1]);
+
+	if (argc > 2)
+		cp.timeout = htobs(atoi(argv[2]));
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (send_cmd(mgmt, MGMT_OP_SET_DISCOVERABLE, index, sizeof(cp), &cp,
+							setting_rsp) == 0) {
+		error("Unable to send set_discoverable cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_connectable(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_CONNECTABLE, argc, argv);
+}
+
+static void cmd_fast_conn(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_FAST_CONNECTABLE, argc, argv);
+}
+
+static void cmd_bondable(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_BONDABLE, argc, argv);
+}
+
+static void cmd_linksec(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_LINK_SECURITY, argc, argv);
+}
+
+static void cmd_ssp(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_SSP, argc, argv);
+}
+
+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\"");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+		val = 1;
+	else if (strcasecmp(argv[1], "off") == 0)
+		val = 0;
+	else if (strcasecmp(argv[1], "only") == 0)
+		val = 2;
+	else
+		val = atoi(argv[1]);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (send_cmd(mgmt, MGMT_OP_SET_SECURE_CONN, index,
+					sizeof(val), &val, setting_rsp) == 0) {
+		error("Unable to send set_secure_conn cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_hs(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_HS, argc, argv);
+}
+
+static void cmd_le(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_LE, argc, argv);
+}
+
+static void cmd_advertising(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_ADVERTISING, argc, argv);
+}
+
+static void cmd_bredr(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_BREDR, argc, argv);
+}
+
+static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_set_privacy cp;
+
+	if (argc < 2) {
+		print("Specify \"on\" or \"off\"");
+		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;
+
+	if (argc > 2) {
+		if (hex2bin(argv[2], cp.irk,
+					sizeof(cp.irk)) != sizeof(cp.irk)) {
+			error("Invalid key format");
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	} else {
+		int fd;
+
+		fd = open("/dev/urandom", O_RDONLY);
+		if (fd < 0) {
+			error("open(/dev/urandom): %s", strerror(errno));
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+
+		if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) {
+			error("Reading from urandom failed");
+			close(fd);
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+
+		close(fd);
+	}
+
+	if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+							setting_rsp) == 0) {
+		error("Unable to send Set Privacy command");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
+							const void *param)
+{
+	const struct mgmt_ev_class_of_dev_changed *rp = param;
+
+	if (len == 0 && status != 0) {
+		error("%s failed, status 0x%02x (%s)",
+				mgmt_opstr(op), status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Unexpected %s len %u", mgmt_opstr(op), len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op),
+		rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+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]);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	class[0] = atoi(argv[1]);
+	class[1] = atoi(argv[2]);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (send_cmd(mgmt, MGMT_OP_SET_DEV_CLASS, index, sizeof(class), class,
+							class_rsp) == 0) {
+		error("Unable to send set_dev_class cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void disconnect_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_disconnect *rp = param;
+	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("Disconnect failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Invalid disconnect response length (%u)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status == 0)
+		print("%s disconnected", addr);
+	else
+		error("Disconnecting %s failed with status 0x%02x (%s)",
+				addr, status, mgmt_errstr(status));
+
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_disconnect cp;
+	uint8_t type = BDADDR_BREDR;
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", disconnect_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			disconnect_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			disconnect_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		disconnect_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+
+	if (mgmt_send(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp,
+					disconnect_rsp, NULL, NULL) == 0) {
+		error("Unable to send disconnect cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void con_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_get_connections *rp = param;
+	uint16_t count, i;
+
+	if (len < sizeof(*rp)) {
+		error("Too small (%u bytes) get_connections rsp", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	count = get_le16(&rp->conn_count);
+	if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) {
+		error("Invalid get_connections length (count=%u, len=%u)",
+								count, len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	for (i = 0; i < count; i++) {
+		char addr[18];
+
+		ba2str(&rp->addr[i].bdaddr, addr);
+
+		print("%s type %s", addr, typestr(rp->addr[i].type));
+	}
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_con(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (mgmt_send(mgmt, MGMT_OP_GET_CONNECTIONS, index, 0, NULL,
+						con_rsp, NULL, NULL) == 0) {
+		error("Unable to send get_connections cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void find_service_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("Start Service Discovery failed: status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Service discovery started");
+	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' },
+	{ "bredr-only",	no_argument, 0, 'b' },
+	{ "uuid",	required_argument, 0, 'u' },
+	{ "rssi",	required_argument, 0, 'r' },
+	{ 0, 0, 0, 0 }
+};
+
+static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
+{
+	if (uuid->type == SDP_UUID16)
+		sdp_uuid16_to_uuid128(uuid128, uuid);
+	else if (uuid->type == SDP_UUID32)
+		sdp_uuid32_to_uuid128(uuid128, uuid);
+	else
+		memcpy(uuid128, uuid, sizeof(*uuid));
+}
+
+#define MAX_UUIDS 4
+
+static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
+			     char **argv)
+{
+	struct mgmt_cp_start_service_discovery *cp;
+	uint8_t buf[sizeof(*cp) + 16 * MAX_UUIDS];
+	uuid_t uuid;
+	uint128_t uint128;
+	uuid_t uuid128;
+	uint8_t type = SCAN_TYPE_DUAL;
+	int8_t rssi;
+	uint16_t count;
+	int opt;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	rssi = 127;
+	count = 0;
+
+	if (argc == 1) {
+		find_service_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	while ((opt = getopt_long(argc, argv, "+lbu:r:p:h",
+					find_service_options, NULL)) != -1) {
+		switch (opt) {
+		case 'l':
+			type &= ~SCAN_TYPE_BREDR;
+			type |= SCAN_TYPE_LE;
+			break;
+		case 'b':
+			type |= SCAN_TYPE_BREDR;
+			type &= ~SCAN_TYPE_LE;
+			break;
+		case 'u':
+			if (count == MAX_UUIDS) {
+				print("Max %u UUIDs supported", MAX_UUIDS);
+				optind = 0;
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+
+			if (bt_string2uuid(&uuid, optarg) < 0) {
+				print("Invalid UUID: %s", optarg);
+				optind = 0;
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			cp = (void *) buf;
+			uuid_to_uuid128(&uuid128, &uuid);
+			ntoh128((uint128_t *) uuid128.value.uuid128.data,
+				&uint128);
+			htob128(&uint128, (uint128_t *) cp->uuids[count++]);
+			break;
+		case 'r':
+			rssi = atoi(optarg);
+			break;
+		case 'h':
+			find_service_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			find_service_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc > 0) {
+		find_service_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	cp = (void *) buf;
+	cp->type = type;
+	cp->rssi = rssi;
+	cp->uuid_count = cpu_to_le16(count);
+
+	if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index,
+				sizeof(*cp) + count * 16, cp,
+				find_service_rsp, NULL, NULL) == 0) {
+		error("Unable to send start_service_discovery cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void find_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0) {
+		error("Unable to start discovery. status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Discovery started");
+	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' },
+	{ "bredr-only",	1, 0, 'b' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_start_discovery cp;
+	uint8_t type = SCAN_TYPE_DUAL;
+	int opt;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	while ((opt = getopt_long(argc, argv, "+lbh", find_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'l':
+			type &= ~SCAN_TYPE_BREDR;
+			type |= SCAN_TYPE_LE;
+			break;
+		case 'b':
+			type |= SCAN_TYPE_BREDR;
+			type &= ~SCAN_TYPE_LE;
+			break;
+		case 'h':
+			find_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			find_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.type = type;
+
+	if (mgmt_send(mgmt, MGMT_OP_START_DISCOVERY, index, sizeof(cp), &cp,
+						find_rsp, NULL, NULL) == 0) {
+		error("Unable to send start_discovery cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+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",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_SUCCESS);
+	}
+
+	printf("Discovery stopped\n");
+	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' },
+	{ "bredr-only",	1, 0, 'b' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
+			  char **argv)
+{
+	struct mgmt_cp_stop_discovery cp;
+	uint8_t type = SCAN_TYPE_DUAL;
+	int opt;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	while ((opt = getopt_long(argc, argv, "+lbh", stop_find_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'l':
+			type &= ~SCAN_TYPE_BREDR;
+			type |= SCAN_TYPE_LE;
+			break;
+		case 'b':
+			type |= SCAN_TYPE_BREDR;
+			type &= ~SCAN_TYPE_LE;
+			break;
+		case 'h':
+		default:
+			stop_find_usage();
+			optind = 0;
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.type = type;
+
+	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);
+	}
+}
+
+static void name_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Unable to set local name with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+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]);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	strncpy((char *) cp.name, argv[1], HCI_MAX_NAME_LENGTH);
+	if (argc > 2)
+		strncpy((char *) cp.short_name, argv[2],
+					MGMT_MAX_SHORT_NAME_LENGTH);
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_LOCAL_NAME, index, sizeof(cp), &cp,
+						name_rsp, NULL, NULL) == 0) {
+		error("Unable to send set_name cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void pair_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_pair_device *rp = param;
+	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("Pairing failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Unexpected pair_rsp len %u", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!memcmp(&rp->addr, &prompt.addr, sizeof(rp->addr)))
+		release_prompt();
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status)
+		error("Pairing with %s (%s) failed. status 0x%02x (%s)",
+			addr, typestr(rp->addr.type), status,
+			mgmt_errstr(status));
+	else
+		print("Paired with %s (%s)", addr, typestr(rp->addr.type));
+
+	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' },
+	{ "type",	1, 0, 't' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_pair_device cp;
+	uint8_t cap = 0x01;
+	uint8_t type = BDADDR_BREDR;
+	char addr[18];
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'c':
+			cap = strtol(optarg, NULL, 0);
+			break;
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			pair_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			pair_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		pair_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+	cp.io_cap = cap;
+
+	ba2str(&cp.addr.bdaddr, addr);
+	print("Pairing with %s (%s)", addr, typestr(cp.addr.type));
+
+	if (mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp,
+						pair_rsp, NULL, NULL) == 0) {
+		error("Unable to send pair_device cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cancel_pair_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_addr_info *rp = param;
+	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("Cancel Pairing failed with 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Unexpected cancel_pair_rsp len %u", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	ba2str(&rp->bdaddr, addr);
+
+	if (status)
+		error("Cancel Pairing with %s (%s) failed. 0x%02x (%s)",
+			addr, typestr(rp->type), status,
+			mgmt_errstr(status));
+	else
+		print("Pairing Cancelled with %s", addr);
+
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_addr_info cp;
+	uint8_t type = BDADDR_BREDR;
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", cancel_pair_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			cancel_pair_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			cancel_pair_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		cancel_pair_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.bdaddr);
+	cp.type = type;
+
+	if (mgmt_send(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp,
+					cancel_pair_rsp, NULL, NULL) == 0) {
+		error("Unable to send cancel_pair_device cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void unpair_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_unpair_device *rp = param;
+	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("Unpair device failed. status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Unexpected unpair_device_rsp len %u", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status)
+		error("Unpairing %s failed. status 0x%02x (%s)",
+				addr, status, mgmt_errstr(status));
+	else
+		print("%s unpaired", addr);
+
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_unpair_device cp;
+	uint8_t type = BDADDR_BREDR;
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", unpair_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			unpair_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			unpair_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		unpair_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+	cp.disconnect = 1;
+
+	if (mgmt_send(mgmt, MGMT_OP_UNPAIR_DEVICE, index, sizeof(cp), &cp,
+						unpair_rsp, NULL, NULL) == 0) {
+		error("Unable to send unpair_device cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void keys_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Load keys failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Keys successfully loaded");
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_load_link_keys cp;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index, sizeof(cp), &cp,
+						keys_rsp, NULL, NULL) == 0) {
+		error("Unable to send load_keys cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void ltks_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Load keys failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Long term keys successfully loaded");
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_ltks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_load_long_term_keys cp;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp,
+						ltks_rsp, NULL, NULL) == 0) {
+		error("Unable to send load_ltks cmd");
+		return noninteractive_quit(EXIT_SUCCESS);
+	}
+}
+
+static void irks_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Load IRKs failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Identity Resolving Keys successfully loaded");
+
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+#define MAX_IRKS 4
+
+static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_load_irks *cp;
+	uint8_t buf[sizeof(*cp) + 23 * MAX_IRKS];
+	uint16_t count, local_index;
+	int opt;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp = (void *) buf;
+	count = 0;
+
+	while ((opt = getopt_long(argc, argv, "+l:h",
+					irks_options, NULL)) != -1) {
+		switch (opt) {
+		case 'l':
+			if (count >= MAX_IRKS) {
+				error("Number of IRKs exceeded");
+				optind = 0;
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			if (strlen(optarg) > 3 &&
+					strncasecmp(optarg, "hci", 3) == 0)
+				local_index = atoi(optarg + 3);
+			else
+				local_index = atoi(optarg);
+			if (!load_identity(local_index, &cp->irks[count])) {
+				error("Unable to load identity");
+				optind = 0;
+				return noninteractive_quit(EXIT_FAILURE);
+			}
+			count++;
+			break;
+		case 'h':
+			irks_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			irks_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc > 0) {
+		irks_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	cp->irk_count = cpu_to_le16(count);
+
+	if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
+					sizeof(*cp) + count * 23, cp,
+					irks_rsp, NULL, NULL) == 0) {
+		error("Unable to send load_irks cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
+							const void *param)
+{
+	const struct mgmt_addr_info *rp = param;
+	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("%s failed, status 0x%02x (%s)",
+				mgmt_opstr(op), status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Unexpected %s len %u", mgmt_opstr(op), len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	ba2str(&rp->bdaddr, addr);
+
+	if (status)
+		error("%s %s (%s) failed. status 0x%02x (%s)",
+				mgmt_opstr(op), addr, typestr(rp->type),
+				status, mgmt_errstr(status));
+	else
+		print("%s %s succeeded", mgmt_opstr(op), addr);
+
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+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;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", block_options,
+							NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			block_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			block_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		block_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+
+	if (send_cmd(mgmt, MGMT_OP_BLOCK_DEVICE, index, sizeof(cp), &cp,
+							block_rsp) == 0) {
+		error("Unable to send block_device cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+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;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", block_options,
+							NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			unblock_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			unblock_usage();
+			optind = 0;
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		unblock_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+
+	if (send_cmd(mgmt, MGMT_OP_UNBLOCK_DEVICE, index, sizeof(cp), &cp,
+							block_rsp) == 0) {
+		error("Unable to send unblock_device cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_add_uuid(struct mgmt *mgmt, uint16_t index, int argc,
+							char **argv)
+{
+	struct mgmt_cp_add_uuid cp;
+	uint128_t uint128;
+	uuid_t uuid, uuid128;
+
+	if (argc < 3) {
+		print("UUID and service hint needed");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (bt_string2uuid(&uuid, argv[1]) < 0) {
+		print("Invalid UUID: %s", argv[1]);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	memset(&cp, 0, sizeof(cp));
+
+	uuid_to_uuid128(&uuid128, &uuid);
+	ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	cp.svc_hint = atoi(argv[2]);
+
+	if (send_cmd(mgmt, MGMT_OP_ADD_UUID, index, sizeof(cp), &cp,
+							class_rsp) == 0) {
+		error("Unable to send add_uuid cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_remove_uuid(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	struct mgmt_cp_remove_uuid cp;
+	uint128_t uint128;
+	uuid_t uuid, uuid128;
+
+	if (argc < 2) {
+		print("UUID needed");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (bt_string2uuid(&uuid, argv[1]) < 0) {
+		print("Invalid UUID: %s", argv[1]);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	memset(&cp, 0, sizeof(cp));
+
+	uuid_to_uuid128(&uuid128, &uuid);
+	ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+	htob128(&uint128, (uint128_t *) cp.uuid);
+
+	if (send_cmd(mgmt, MGMT_OP_REMOVE_UUID, index, sizeof(cp), &cp,
+							class_rsp) == 0) {
+		error("Unable to send remove_uuid cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_clr_uuids(struct mgmt *mgmt, uint16_t index, int argc,
+								char **argv)
+{
+	char *uuid_any = "00000000-0000-0000-0000-000000000000";
+	char *rm_argv[] = { "rm-uuid", uuid_any, NULL };
+
+	cmd_remove_uuid(mgmt, index, 2, rm_argv);
+}
+
+static void local_oob_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_local_oob_data *rp = param;
+	char str[33];
+
+	if (status != 0) {
+		error("Read Local OOB Data failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small (%u bytes) read_local_oob rsp", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bin2hex(rp->hash192, 16, str, sizeof(str));
+	print("Hash C from P-192: %s", str);
+
+	bin2hex(rp->rand192, 16, str, sizeof(str));
+	print("Randomizer R with P-192: %s", str);
+
+	if (len < sizeof(*rp))
+		return noninteractive_quit(EXIT_SUCCESS);
+
+	bin2hex(rp->hash256, 16, str, sizeof(str));
+	print("Hash C from P-256: %s", str);
+
+	bin2hex(rp->rand256, 16, str, sizeof(str));
+	print("Randomizer R with P-256: %s", str);
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_local_oob(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
+					local_oob_rsp, NULL, NULL) == 0) {
+		error("Unable to send read_local_oob cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void remote_oob_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_addr_info *rp = param;
+	char addr[18];
+
+	if (status != 0) {
+		error("Add Remote OOB Data failed: 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return;
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small (%u bytes) add_remote_oob rsp", len);
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	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' },
+	{ "type",	1, 0, 't' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_add_remote_oob_data cp;
+	int opt;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.addr.type = BDADDR_BREDR;
+
+	while ((opt = getopt_long(argc, argv, "+t:r:R:h:H:",
+					remote_oob_opt, NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			cp.addr.type = strtol(optarg, NULL, 0);
+			break;
+		case 'r':
+			hex2bin(optarg, cp.rand192, 16);
+			break;
+		case 'h':
+			hex2bin(optarg, cp.hash192, 16);
+			break;
+		case 'R':
+			hex2bin(optarg, cp.rand256, 16);
+			break;
+		case 'H':
+			hex2bin(optarg, cp.hash256, 16);
+			break;
+		default:
+			remote_oob_usage();
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		remote_oob_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	str2ba(argv[0], &cp.addr.bdaddr);
+
+	print("Adding OOB data for %s (%s)", argv[0], typestr(cp.addr.type));
+
+	if (mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index,
+				sizeof(cp), &cp, remote_oob_rsp,
+				NULL, NULL) == 0) {
+		error("Unable to send add_remote_oob cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void did_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Set Device ID failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Device ID successfully set");
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void did_usage(void)
+{
+	print("Usage: did <source>:<vendor>:<product>:<version>");
+	print("       possible source values: bluetooth, usb");
+}
+
+static void cmd_did(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	struct mgmt_cp_set_device_id cp;
+	uint16_t vendor, product, version , source;
+	int result;
+
+	if (argc < 2) {
+		did_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	result = sscanf(argv[1], "bluetooth:%4hx:%4hx:%4hx", &vendor, &product,
+								&version);
+	if (result == 3) {
+		source = 0x0001;
+		goto done;
+	}
+
+	result = sscanf(argv[1], "usb:%4hx:%4hx:%4hx", &vendor, &product,
+								&version);
+	if (result == 3) {
+		source = 0x0002;
+		goto done;
+	}
+
+	did_usage();
+	return noninteractive_quit(EXIT_FAILURE);
+
+done:
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp.source = htobs(source);
+	cp.vendor = htobs(vendor);
+	cp.product = htobs(product);
+	cp.version = htobs(version);
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_DEVICE_ID, index, sizeof(cp), &cp,
+						did_rsp, NULL, NULL) == 0) {
+		error("Unable to send set_device_id cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void static_addr_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Set static address failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Static address successfully set");
+
+	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();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	str2ba(argv[1], &cp.bdaddr);
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, sizeof(cp), &cp,
+					static_addr_rsp, NULL, NULL) == 0) {
+		error("Unable to send set_static_address cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void options_rsp(uint16_t op, uint16_t id, uint8_t status,
+					uint16_t len, const void *param)
+{
+	const uint32_t *rp = param;
+
+	if (status != 0) {
+		error("%s for hci%u failed with status 0x%02x (%s)",
+			mgmt_opstr(op), id, status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small %s response (%u bytes)",
+							mgmt_opstr(op), len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("hci%u %s complete, options: %s", id, mgmt_opstr(op),
+						options2str(get_le32(rp)));
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_public_addr(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_set_public_address cp;
+
+	if (argc < 2) {
+		print("Usage: public-addr <address>");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	str2ba(argv[1], &cp.bdaddr);
+
+	if (send_cmd(mgmt, MGMT_OP_SET_PUBLIC_ADDRESS, index, sizeof(cp), &cp,
+							options_rsp) == 0) {
+		error("Unable to send Set Public Address cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_ext_config(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_set_external_config cp;
+
+	if (argc < 2) {
+		print("Specify \"on\" or \"off\"");
+		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;
+
+	if (send_cmd(mgmt, MGMT_OP_SET_EXTERNAL_CONFIG, index, sizeof(cp), &cp,
+							options_rsp) == 0) {
+		error("Unable to send Set External Config cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_debug_keys(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	cmd_setting(mgmt, index, MGMT_OP_SET_DEBUG_KEYS, argc, argv);
+}
+
+static void conn_info_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_get_conn_info *rp = param;	char addr[18];
+
+	if (len == 0 && status != 0) {
+		error("Get Conn Info failed, status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Unexpected Get Conn Info len %u", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	ba2str(&rp->addr.bdaddr, addr);
+
+	if (status) {
+		error("Get Conn Info for %s (%s) failed. status 0x%02x (%s)",
+						addr, typestr(rp->addr.type),
+						status, mgmt_errstr(status));
+	} else {
+		print("Connection Information for %s (%s)",
+						addr, typestr(rp->addr.type));
+		print("\tRSSI %d\tTX power %d\tmaximum TX power %d",
+				rp->rssi, rp->tx_power, rp->max_tx_power);
+	}
+
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_conn_info(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_get_conn_info cp;
+	uint8_t type = BDADDR_BREDR;
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", conn_info_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			conn_info_usage();
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			conn_info_usage();
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		conn_info_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+
+	if (mgmt_send(mgmt, MGMT_OP_GET_CONN_INFO, index, sizeof(cp), &cp,
+					conn_info_rsp, NULL, NULL) == 0) {
+		error("Unable to send get_conn_info cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void io_cap_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Could not set IO Capability with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("IO Capabilities successfully set");
+
+	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)
+{
+	struct mgmt_cp_set_io_capability cp;
+	uint8_t cap;
+
+	if (argc < 2) {
+		io_cap_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cap = strtol(argv[1], NULL, 0);
+	memset(&cp, 0, sizeof(cp));
+	cp.io_capability = cap;
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_IO_CAPABILITY, index, sizeof(cp), &cp,
+					io_cap_rsp, NULL, NULL) == 0) {
+		error("Unable to send set-io-cap cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void scan_params_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Set scan parameters failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Scan parameters successfully set");
+
+	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();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp.interval = strtol(argv[1], NULL, 0);
+	cp.window = strtol(argv[2], NULL, 0);
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp,
+					scan_params_rsp, NULL, NULL) == 0) {
+		error("Unable to send set_scan_params cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void clock_info_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_get_clock_info *rp = param;
+
+	if (len < sizeof(*rp)) {
+		error("Unexpected Get Clock Info len %u", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (status) {
+		error("Get Clock Info failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Local Clock:   %u", le32_to_cpu(rp->local_clock));
+	print("Piconet Clock: %u", le32_to_cpu(rp->piconet_clock));
+	print("Accurary:      %u", le16_to_cpu(rp->accuracy));
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_clock_info(struct mgmt *mgmt, uint16_t index,
+							int argc, char **argv)
+{
+	struct mgmt_cp_get_clock_info cp;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (argc > 1)
+		str2ba(argv[1], &cp.addr.bdaddr);
+
+	if (mgmt_send(mgmt, MGMT_OP_GET_CLOCK_INFO, index, sizeof(cp), &cp,
+					clock_info_rsp, NULL, NULL) == 0) {
+		error("Unable to send get_clock_info cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void add_device_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Add device failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	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' },
+	{ "type",	1, 0, 't' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_add_device(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_add_device cp;
+	uint8_t action = 0x00;
+	uint8_t type = BDADDR_BREDR;
+	char addr[18];
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+a:t:h", add_device_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 'a':
+			action = strtol(optarg, NULL, 0);
+			break;
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			add_device_usage();
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			add_device_usage();
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		add_device_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+	cp.action = action;
+
+	ba2str(&cp.addr.bdaddr, addr);
+	print("Adding device with %s (%s)", addr, typestr(cp.addr.type));
+
+	if (mgmt_send(mgmt, MGMT_OP_ADD_DEVICE, index, sizeof(cp), &cp,
+					add_device_rsp, NULL, NULL) == 0) {
+		error("Unable to send add device command");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void remove_device_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	if (status != 0)
+		error("Remove device failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	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' },
+	{ 0, 0, 0, 0 }
+};
+
+static void cmd_del_device(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_remove_device cp;
+	uint8_t type = BDADDR_BREDR;
+	char addr[18];
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+t:h", del_device_options,
+								NULL)) != -1) {
+		switch (opt) {
+		case 't':
+			type = strtol(optarg, NULL, 0);
+			break;
+		case 'h':
+			del_device_usage();
+			return noninteractive_quit(EXIT_SUCCESS);
+		default:
+			del_device_usage();
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		del_device_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+	str2ba(argv[0], &cp.addr.bdaddr);
+	cp.addr.type = type;
+
+	ba2str(&cp.addr.bdaddr, addr);
+	print("Removing device with %s (%s)", addr, typestr(cp.addr.type));
+
+	if (mgmt_send(mgmt, MGMT_OP_REMOVE_DEVICE, index, sizeof(cp), &cp,
+					remove_device_rsp, NULL, NULL) == 0) {
+		error("Unable to send remove device command");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_clr_devices(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	char *bdaddr_any = "00:00:00:00:00:00";
+	char *rm_argv[] = { "del-device", bdaddr_any, NULL };
+
+	cmd_del_device(mgmt, index, 2, rm_argv);
+}
+
+static void local_oob_ext_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+	uint16_t eir_len;
+
+	if (status != 0) {
+		error("Read Local OOB Ext Data failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small (%u bytes) read_local_oob_ext rsp", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	eir_len = le16_to_cpu(rp->eir_len);
+	if (len != sizeof(*rp) + eir_len) {
+		error("local_oob_ext: expected %zu bytes, got %u bytes",
+						sizeof(*rp) + eir_len, len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print_eir(rp->eir, eir_len);
+
+	noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_bredr_oob(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_read_local_oob_ext_data cp;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp.type = SCAN_TYPE_BREDR;
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+					index, sizeof(cp), &cp,
+					local_oob_ext_rsp, NULL, NULL)) {
+		error("Unable to send read_local_oob_ext cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_le_oob(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_read_local_oob_ext_data cp;
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp.type = SCAN_TYPE_LE;
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+					index, sizeof(cp), &cp,
+					local_oob_ext_rsp, NULL, NULL)) {
+		error("Unable to send read_local_oob_ext cmd");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static const char *adv_flags_str[] = {
+				"connectable",
+				"general-discoverable",
+				"limited-discoverable",
+				"managed-flags",
+				"tx-power",
+				"scan-rsp-appearance",
+				"scan-rsp-local-name",
+};
+
+static const char *adv_flags2str(uint32_t flags)
+{
+	static char str[256];
+	unsigned i;
+	int off;
+
+	off = 0;
+	str[0] = '\0';
+
+	for (i = 0; i < NELEM(adv_flags_str); i++) {
+		if ((flags & (1 << i)) != 0)
+			off += snprintf(str + off, sizeof(str) - off, "%s ",
+							adv_flags_str[i]);
+	}
+
+	return str;
+}
+
+static void adv_features_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_adv_features *rp = param;
+	uint32_t supported_flags;
+
+	if (status != 0) {
+		error("Reading adv features failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small adv features reply (%u bytes)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp) + rp->num_instances * sizeof(uint8_t)) {
+		error("Instances count (%u) doesn't match reply length (%u)",
+							rp->num_instances, len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	supported_flags = le32_to_cpu(rp->supported_flags);
+	print("Supported flags: %s", adv_flags2str(supported_flags));
+	print("Max advertising data len: %u", rp->max_adv_data_len);
+	print("Max scan response data len: %u", rp->max_scan_rsp_len);
+	print("Max instances: %u", rp->max_instances);
+
+	print("Instances list with %u item%s", rp->num_instances,
+					rp->num_instances != 1 ? "s" : "");
+
+	return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_advinfo(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL,
+					adv_features_rsp, NULL, NULL)) {
+		error("Unable to send advertising features command");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void adv_size_info_rsp(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_get_adv_size_info *rp = param;
+	uint32_t flags;
+
+	if (status != 0) {
+		error("Reading adv size info failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len < sizeof(*rp)) {
+		error("Too small adv size info reply (%u bytes)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	flags = le32_to_cpu(rp->flags);
+	print("Instance: %u", rp->instance);
+	print("Flags: %s", adv_flags2str(flags));
+	print("Max advertising data len: %u", rp->max_adv_data_len);
+	print("Max scan response data len: %u", rp->max_scan_rsp_len);
+
+	return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void advsize_usage(void)
+{
+	print("Usage: advsize [options] <instance_id>\nOptions:\n"
+		"\t -c, --connectable         \"connectable\" flag\n"
+		"\t -g, --general-discov      \"general-discoverable\" flag\n"
+		"\t -l, --limited-discov      \"limited-discoverable\" flag\n"
+		"\t -m, --managed-flags       \"managed-flags\" flag\n"
+		"\t -p, --tx-power            \"tx-power\" flag");
+}
+
+static struct option advsize_options[] = {
+	{ "help",		0, 0, 'h' },
+	{ "connectable",	0, 0, 'c' },
+	{ "general-discov",	0, 0, 'g' },
+	{ "limited-discov",	0, 0, 'l' },
+	{ "managed-flags",	0, 0, 'm' },
+	{ "tx-power",		0, 0, 'p' },
+	{ 0, 0, 0, 0}
+};
+
+static void cmd_advsize(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	struct mgmt_cp_get_adv_size_info cp;
+	uint8_t instance;
+	uint32_t flags = 0;
+	int opt;
+
+	while ((opt = getopt_long(argc, argv, "+cglmph",
+						advsize_options, NULL)) != -1) {
+		switch (opt) {
+		case 'c':
+			flags |= MGMT_ADV_FLAG_CONNECTABLE;
+			break;
+		case 'g':
+			flags |= MGMT_ADV_FLAG_DISCOV;
+			break;
+		case 'l':
+			flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+			break;
+		case 'm':
+			flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+			break;
+		case 'p':
+			flags |= MGMT_ADV_FLAG_TX_POWER;
+			break;
+		default:
+			advsize_usage();
+			return noninteractive_quit(EXIT_FAILURE);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc != 1) {
+		advsize_usage();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	instance = strtol(argv[0], NULL, 0);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+
+	cp.instance = instance;
+	cp.flags = cpu_to_le32(flags);
+
+	if (!mgmt_send(mgmt, MGMT_OP_GET_ADV_SIZE_INFO, index, sizeof(cp), &cp,
+					adv_size_info_rsp, NULL, NULL)) {
+		error("Unable to send advertising size info command");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void add_adv_rsp(uint8_t status, uint16_t len, const void *param,
+								void *user_data)
+{
+	const struct mgmt_rp_add_advertising *rp = param;
+
+	if (status != 0) {
+		error("Add Advertising failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Invalid Add Advertising response length (%u)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Instance added: %u", rp->instance);
+
+	return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void add_adv_usage(void)
+{
+	print("Usage: add-adv [options] <instance_id>\nOptions:\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"
+		"\t -t, --timeout <timeout>   Timeout in seconds\n"
+		"\t -D, --duration <duration> Duration in seconds\n"
+		"\t -c, --connectable         \"connectable\" flag\n"
+		"\t -g, --general-discov      \"general-discoverable\" flag\n"
+		"\t -l, --limited-discov      \"limited-discoverable\" flag\n"
+		"\t -m, --managed-flags       \"managed-flags\" flag\n"
+		"\t -p, --tx-power            \"tx-power\" flag\n"
+		"e.g.:\n"
+		"\tadd-adv -u 180d -u 180f -d 080954657374204C45 1");
+}
+
+static struct option add_adv_options[] = {
+	{ "help",		0, 0, 'h' },
+	{ "uuid",		1, 0, 'u' },
+	{ "adv-data",		1, 0, 'd' },
+	{ "scan-rsp",		1, 0, 's' },
+	{ "timeout",		1, 0, 't' },
+	{ "duration",		1, 0, 'D' },
+	{ "connectable",	0, 0, 'c' },
+	{ "general-discov",	0, 0, 'g' },
+	{ "limited-discov",	0, 0, 'l' },
+	{ "managed-flags",	0, 0, 'm' },
+	{ "tx-power",		0, 0, 'p' },
+	{ 0, 0, 0, 0}
+};
+
+static bool parse_bytes(char *optarg, uint8_t **bytes, size_t *len)
+{
+	unsigned i;
+
+	if (!optarg) {
+		add_adv_usage();
+		return false;
+	}
+
+	*len = strlen(optarg);
+
+	if (*len % 2) {
+		error("Malformed data");
+		return false;
+	}
+
+	*len /= 2;
+	if (*len > UINT8_MAX) {
+		error("Data too long");
+		return false;
+	}
+
+	*bytes = malloc(*len);
+	if (!*bytes) {
+		error("Failed to allocate memory");
+		return false;
+	}
+
+	for (i = 0; i < *len; i++) {
+		if (sscanf(optarg + (i * 2), "%2hhx", *bytes + i) != 1) {
+			error("Invalid data");
+			free(*bytes);
+			*bytes = NULL;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+#define MAX_AD_UUID_BYTES 32
+
+static void cmd_add_adv(struct mgmt *mgmt, uint16_t index,
+							int argc, char **argv)
+{
+	struct mgmt_cp_add_advertising *cp = NULL;
+	int opt;
+	uint8_t *adv_data = NULL, *scan_rsp = NULL;
+	size_t adv_len = 0, scan_rsp_len = 0;
+	size_t cp_len;
+	uint8_t uuids[MAX_AD_UUID_BYTES];
+	size_t uuid_bytes = 0;
+	uint8_t uuid_type = 0;
+	uint16_t timeout = 0, duration = 0;
+	uint8_t instance;
+	uuid_t uuid;
+	bool success = false;
+	bool quit = true;
+	uint32_t flags = 0;
+
+	while ((opt = getopt_long(argc, argv, "+u:d:s:t:D:cglmph",
+						add_adv_options, NULL)) != -1) {
+		switch (opt) {
+		case 'u':
+			if (bt_string2uuid(&uuid, optarg) < 0) {
+				print("Invalid UUID: %s", optarg);
+				goto done;
+			}
+
+			if (uuid_type && uuid_type != uuid.type) {
+				print("UUID types must be consistent");
+				goto done;
+			}
+
+			if (uuid.type == SDP_UUID16) {
+				if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) {
+					print("Too many UUIDs");
+					goto done;
+				}
+
+				put_le16(uuid.value.uuid16, uuids + uuid_bytes);
+				uuid_bytes += 2;
+			} else if (uuid.type == SDP_UUID128) {
+				if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) {
+					print("Too many UUIDs");
+					goto done;
+				}
+
+				bswap_128(uuid.value.uuid128.data,
+							uuids + uuid_bytes);
+				uuid_bytes += 16;
+			} else {
+				printf("Unsupported UUID type");
+				goto done;
+			}
+
+			if (!uuid_type)
+				uuid_type = uuid.type;
+
+			break;
+		case 'd':
+			if (adv_len) {
+				print("Only one adv-data option allowed");
+				goto done;
+			}
+
+			if (!parse_bytes(optarg, &adv_data, &adv_len))
+				goto done;
+			break;
+		case 's':
+			if (scan_rsp_len) {
+				print("Only one scan-rsp option allowed");
+				goto done;
+			}
+
+			if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len))
+				goto done;
+			break;
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		case 'D':
+			duration = strtol(optarg, NULL, 0);
+			break;
+		case 'c':
+			flags |= MGMT_ADV_FLAG_CONNECTABLE;
+			break;
+		case 'g':
+			flags |= MGMT_ADV_FLAG_DISCOV;
+			break;
+		case 'l':
+			flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+			break;
+		case 'm':
+			flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+			break;
+		case 'p':
+			flags |= MGMT_ADV_FLAG_TX_POWER;
+			break;
+		case 'h':
+			success = true;
+		default:
+			add_adv_usage();
+			goto done;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc != 1) {
+		add_adv_usage();
+		goto done;
+	}
+
+	if (uuid_bytes)
+		uuid_bytes += 2;
+
+	instance = strtol(argv[0], NULL, 0);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len;
+	cp = malloc0(cp_len);
+	if (!cp)
+		goto done;
+
+	cp->instance = instance;
+	put_le32(flags, &cp->flags);
+	put_le16(timeout, &cp->timeout);
+	put_le16(duration, &cp->duration);
+	cp->adv_data_len = adv_len + uuid_bytes;
+	cp->scan_rsp_len = scan_rsp_len;
+
+	if (uuid_bytes) {
+		cp->data[0] = uuid_bytes - 1;
+		cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07;
+		memcpy(cp->data + 2, uuids, uuid_bytes - 2);
+	}
+
+	memcpy(cp->data + uuid_bytes, adv_data, adv_len);
+	memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len);
+
+	if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp,
+						add_adv_rsp, NULL, NULL)) {
+		error("Unable to send \"Add Advertising\" command");
+		goto done;
+	}
+
+	quit = false;
+
+done:
+	free(adv_data);
+	free(scan_rsp);
+	free(cp);
+
+	if (quit)
+		noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param,
+								void *user_data)
+{
+	const struct mgmt_rp_remove_advertising *rp = param;
+
+	if (status != 0) {
+		error("Remove Advertising failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (len != sizeof(*rp)) {
+		error("Invalid Remove Advertising response length (%u)", len);
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	print("Instance removed: %u", rp->instance);
+
+	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();
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+
+	instance = strtol(argv[1], NULL, 0);
+
+	if (index == MGMT_INDEX_NONE)
+		index = 0;
+
+	memset(&cp, 0, sizeof(cp));
+
+	cp.instance = instance;
+
+	if (!mgmt_send(mgmt, MGMT_OP_REMOVE_ADVERTISING, index, sizeof(cp), &cp,
+						rm_adv_rsp, NULL, NULL)) {
+		error("Unable to send \"Remove Advertising\" command");
+		return noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
+static void cmd_clr_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+	char *all_instances = "0";
+	char *rm_argv[] = { "rm-adv", all_instances, NULL };
+
+	cmd_rm_adv(mgmt, index, 2, rm_argv);
+}
+
+struct cmd_info {
+	char *cmd;
+	void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
+	char *doc;
+	char * (*gen) (const char *text, int state);
+	void (*disp) (char **matches, int num_matches, int max_length);
+};
+
+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"	},
+};
+
+static void cmd_quit(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	mainloop_exit_success();
+}
+
+static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index)
+{
+	mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, index_added,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, index, index_removed,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DISCOVERING, index, discovering,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, new_link_key,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, connected,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, disconnected,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_CONNECT_FAILED, index, conn_failed,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed,
+								NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, index,
+					class_of_dev_changed, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index,
+					local_name_changed, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found,
+								mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, request_pin,
+								mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm,
+								mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index,
+						request_passkey, mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index,
+						passkey_notify, mgmt, NULL);
+	mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_ADDED, index,
+					unconf_index_added, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_REMOVED, index,
+					unconf_index_removed, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index,
+					new_config_options, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, index,
+					ext_index_added, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, index,
+					ext_index_removed, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_LOCAL_OOB_DATA_UPDATED, index,
+					local_oob_data_updated, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index,
+						advertising_added, NULL, NULL);
+	mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index,
+					advertising_removed, NULL, NULL);
+}
+
+static void cmd_select(struct mgmt *mgmt, uint16_t index,
+						int argc, char **argv)
+{
+	if (argc != 2) {
+		error("Usage: select <index>");
+		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]);
+
+	register_mgmt_callbacks(mgmt, mgmt_index);
+
+	print("Selected index %u", mgmt_index);
+
+	update_prompt(mgmt_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"	},
+};
+
+static char *cmd_generator(const char *text, int state)
+{
+	static size_t i, j, len;
+	const char *cmd;
+
+	if (!state) {
+		i = 0;
+		j = 0;
+		len = strlen(text);
+	}
+
+	while (i < NELEM(all_cmd)) {
+		cmd = all_cmd[i++].cmd;
+
+		if (!strncmp(cmd, text, len))
+			return strdup(cmd);
+	}
+
+	while (j < NELEM(interactive_cmd)) {
+		cmd = interactive_cmd[j++].cmd;
+
+		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) {
+		unsigned int i;
+
+		for (i = 0; i < NELEM(all_cmd); i++) {
+			struct cmd_info *c = &all_cmd[i];
+
+			if (strncmp(c->cmd, rl_line_buffer, start - 1))
+				continue;
+
+			if (!c->gen)
+				continue;
+
+			rl_completion_display_matches_hook = c->disp;
+			matches = rl_completion_matches(text, c->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 struct cmd_info *find_cmd(const char *cmd, struct cmd_info table[],
+							size_t cmd_count)
+{
+	size_t i;
+
+	for (i = 0; i < cmd_count; i++) {
+		if (!strcmp(table[i].cmd, cmd))
+			return &table[i];
+	}
+
+	return NULL;
+}
+
+static void rl_handler(char *input)
+{
+	struct cmd_info *c;
+	wordexp_t w;
+	char *cmd, **argv;
+	size_t argc, i;
+
+	if (!input) {
+		rl_insert_text("quit");
+		rl_redisplay();
+		rl_crlf();
+		mainloop_quit();
+		return;
+	}
+
+	if (!strlen(input))
+		goto done;
+
+	if (prompt_input(input))
+		goto done;
+
+	add_history(input);
+
+	if (wordexp(input, &w, WRDE_NOCMD))
+		goto done;
+
+	if (w.we_wordc == 0)
+		goto free_we;
+
+	cmd = w.we_wordv[0];
+	argv = w.we_wordv;
+	argc = w.we_wordc;
+
+	c = find_cmd(cmd, all_cmd, NELEM(all_cmd));
+	if (!c && interactive)
+		c = find_cmd(cmd, interactive_cmd, NELEM(interactive_cmd));
+
+	if (c && c->func) {
+		c->func(mgmt, mgmt_index, argc, argv);
+		goto free_we;
+	}
+
+	if (strcmp(cmd, "help")) {
+		print("Invalid command");
+		goto free_we;
+	}
+
+	print("Available commands:");
+
+	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 (!interactive)
+		goto free_we;
+
+	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 ? : "");
+	}
+
+free_we:
+	wordfree(&w);
+done:
+	free(input);
+}
+
+static void usage(void)
+{
+	unsigned int i;
+
+	printf("btmgmt ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tbtmgmt [options] <command> [command parameters]\n");
+
+	printf("Options:\n"
+		"\t--index <id>\tSpecify adapter index\n"
+		"\t--verbose\tEnable extra logging\n"
+		"\t--help\tDisplay help\n");
+
+	printf("Commands:\n");
+	for (i = 0; i < NELEM(all_cmd); i++)
+		printf("\t%-15s\t%s\n", all_cmd[i].cmd, all_cmd[i].doc);
+
+	printf("\n"
+		"For more information on the usage of each command use:\n"
+		"\tbtmgmt <command> --help\n" );
+}
+
+static struct option main_options[] = {
+	{ "index",	1, 0, 'i' },
+	{ "verbose",	0, 0, 'v' },
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static bool prompt_read(struct io *io, void *user_data)
+{
+	rl_callback_read_char();
+	return true;
+}
+
+static struct io *setup_stdin(void)
+{
+	struct io *io;
+
+	io = io_new(STDIN_FILENO);
+	if (!io)
+		return io;
+
+	io_set_read_handler(io, prompt_read, NULL, NULL);
+
+	return io;
+}
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	print("%s%s", prefix, str);
+}
+
+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);
+			break;
+		case 'h':
+		default:
+			usage();
+			return 0;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	mainloop_init();
+
+	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);
+
+	if (argc > 0) {
+		struct cmd_info *c;
+
+		c = find_cmd(argv[0], all_cmd, NELEM(all_cmd));
+		if (!c) {
+			fprintf(stderr, "Unknown command: %s\n", argv[0]);
+			mgmt_unref(mgmt);
+			return EXIT_FAILURE;
+		}
+
+		c->func(mgmt, index, argc, argv);
+	}
+
+	register_mgmt_callbacks(mgmt, index);
+
+	/* Interactive mode */
+	if (!argc)
+		input = setup_stdin();
+	else
+		input = NULL;
+
+	if (input) {
+		interactive = true;
+
+		rl_attempted_completion_function = cmd_completion;
+
+		rl_erase_empty_line = 1;
+		rl_callback_handler_install(NULL, rl_handler);
+
+		update_prompt(index);
+		rl_redisplay();
+	}
+
+	mgmt_index = index;
+
+	status = mainloop_run();
+
+	if (input) {
+		io_destroy(input);
+
+		rl_message("");
+		rl_callback_handler_remove();
+	}
+
+	mgmt_cancel_all(mgmt);
+	mgmt_unregister_all(mgmt);
+	mgmt_unref(mgmt);
+
+	return status;
+}
diff --git a/bluez/repo/tools/btproxy.c b/bluez/repo/tools/btproxy.c
new file mode 100644
index 0000000..43de037
--- /dev/null
+++ b/bluez/repo/tools/btproxy.c
@@ -0,0 +1,904 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "src/shared/util.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/ecc.h"
+#include "monitor/bt.h"
+
+#define HCI_BREDR	0x00
+#define HCI_AMP		0x01
+
+#define BTPROTO_HCI	1
+struct sockaddr_hci {
+	sa_family_t	hci_family;
+	unsigned short	hci_dev;
+	unsigned short  hci_channel;
+};
+#define HCI_CHANNEL_USER	1
+
+static uint16_t hci_index = 0;
+static bool client_active = false;
+static bool debug_enabled = false;
+static bool emulate_ecc = false;
+
+static void hexdump_print(const char *str, void *user_data)
+{
+	printf("%s%s\n", (char *) user_data, str);
+}
+
+struct proxy {
+	/* Receive commands, ACL and SCO data */
+	int host_fd;
+	uint8_t host_buf[4096];
+	uint16_t host_len;
+	bool host_shutdown;
+
+	/* Receive events, ACL and SCO data */
+	int dev_fd;
+	uint8_t dev_buf[4096];
+	uint16_t dev_len;
+	bool dev_shutdown;
+
+	/* ECC emulation */
+	uint8_t event_mask[8];
+	uint8_t local_sk256[32];
+};
+
+static bool write_packet(int fd, const void *data, size_t size,
+							void *user_data)
+{
+	while (size > 0) {
+		ssize_t written;
+
+		written = write(fd, data, size);
+		if (written < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+			return false;
+		}
+
+		if (debug_enabled)
+			util_hexdump('<', data, written, hexdump_print,
+								user_data);
+
+		data += written;
+		size -= written;
+	}
+
+	return true;
+}
+
+static void host_write_packet(struct proxy *proxy, void *buf, uint16_t len)
+{
+	if (!write_packet(proxy->dev_fd, buf, len, "D: ")) {
+		fprintf(stderr, "Write to device descriptor failed\n");
+		mainloop_remove_fd(proxy->dev_fd);
+	}
+}
+
+static void dev_write_packet(struct proxy *proxy, void *buf, uint16_t len)
+{
+	if (!write_packet(proxy->host_fd, buf, len, "H: ")) {
+		fprintf(stderr, "Write to host descriptor failed\n");
+		mainloop_remove_fd(proxy->host_fd);
+	}
+}
+
+static void cmd_status(struct proxy *proxy, uint8_t status, uint16_t opcode)
+{
+	size_t buf_size = 1 + sizeof(struct bt_hci_evt_hdr) +
+					sizeof(struct bt_hci_evt_cmd_status);
+	void *buf = alloca(buf_size);
+	struct bt_hci_evt_hdr *hdr = buf + 1;
+	struct bt_hci_evt_cmd_status *cs = buf + 1 + sizeof(*hdr);
+
+	*((uint8_t *) buf) = BT_H4_EVT_PKT;
+
+	hdr->evt = BT_HCI_EVT_CMD_STATUS;
+	hdr->plen = sizeof(*cs);
+
+	cs->status = status;
+	cs->ncmd = 0x01;
+	cs->opcode = cpu_to_le16(opcode);
+
+	dev_write_packet(proxy, buf, buf_size);
+}
+
+static void le_meta_event(struct proxy *proxy, uint8_t event,
+						void *data, uint8_t len)
+{
+	size_t buf_size = 1 + sizeof(struct bt_hci_evt_hdr) + 1 + len;
+	void *buf = alloca(buf_size);
+	struct bt_hci_evt_hdr *hdr = buf + 1;
+
+	*((uint8_t *) buf) = BT_H4_EVT_PKT;
+
+	hdr->evt = BT_HCI_EVT_LE_META_EVENT;
+	hdr->plen = 1 + len;
+
+	*((uint8_t *) (buf + 1 + sizeof(*hdr))) = event;
+
+	if (len > 0)
+		memcpy(buf + 1 + sizeof(*hdr) + 1, data, len);
+
+	dev_write_packet(proxy, buf, buf_size);
+}
+
+static void host_emulate_ecc(struct proxy *proxy, void *buf, uint16_t len)
+{
+	uint8_t pkt_type = *((uint8_t *) buf);
+	struct bt_hci_cmd_hdr *hdr = buf + 1;
+	struct bt_hci_cmd_le_set_event_mask *lsem;
+	struct bt_hci_cmd_le_generate_dhkey *lgd;
+	struct bt_hci_evt_le_read_local_pk256_complete lrlpkc;
+	struct bt_hci_evt_le_generate_dhkey_complete lgdc;
+
+	if (pkt_type != BT_H4_CMD_PKT) {
+		host_write_packet(proxy, buf, len);
+		return;
+	}
+
+	switch (le16_to_cpu(hdr->opcode)) {
+	case BT_HCI_CMD_LE_SET_EVENT_MASK:
+		lsem = buf + 1 + sizeof(*hdr);
+		memcpy(proxy->event_mask, lsem->mask, 8);
+
+		lsem->mask[0] &= ~0x80;		/* P-256 Public Key Complete */
+		lsem->mask[1] &= ~0x01;		/* Generate DHKey Complete */
+
+		host_write_packet(proxy, buf, len);
+		break;
+
+	case BT_HCI_CMD_LE_READ_LOCAL_PK256:
+		if (!ecc_make_key(lrlpkc.local_pk256, proxy->local_sk256)) {
+			cmd_status(proxy, BT_HCI_ERR_COMMAND_DISALLOWED,
+					BT_HCI_CMD_LE_READ_LOCAL_PK256);
+			break;
+		}
+		cmd_status(proxy, BT_HCI_ERR_SUCCESS,
+					BT_HCI_CMD_LE_READ_LOCAL_PK256);
+
+		if (!(proxy->event_mask[0] & 0x80))
+			break;
+
+		lrlpkc.status = BT_HCI_ERR_SUCCESS;
+		le_meta_event(proxy, BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE,
+						&lrlpkc, sizeof(lrlpkc));
+		break;
+
+	case BT_HCI_CMD_LE_GENERATE_DHKEY:
+		lgd = buf + 1 + sizeof(*hdr);
+		if (!ecdh_shared_secret(lgd->remote_pk256, proxy->local_sk256,
+								lgdc.dhkey)) {
+			cmd_status(proxy, BT_HCI_ERR_COMMAND_DISALLOWED,
+						BT_HCI_CMD_LE_GENERATE_DHKEY);
+			break;
+		}
+		cmd_status(proxy, BT_HCI_ERR_SUCCESS,
+					BT_HCI_CMD_LE_GENERATE_DHKEY);
+
+		if (!(proxy->event_mask[1] & 0x01))
+			break;
+
+		lgdc.status = BT_HCI_ERR_SUCCESS;
+		le_meta_event(proxy, BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE,
+							&lgdc, sizeof(lgdc));
+		break;
+
+	default:
+		host_write_packet(proxy, buf, len);
+		break;
+	}
+}
+
+static void dev_emulate_ecc(struct proxy *proxy, void *buf, uint16_t len)
+{
+	uint8_t pkt_type = *((uint8_t *) buf);
+	struct bt_hci_evt_hdr *hdr = buf + 1;
+	struct bt_hci_evt_cmd_complete *cc;
+	struct bt_hci_rsp_read_local_commands *rlc;
+
+	if (pkt_type != BT_H4_EVT_PKT) {
+		dev_write_packet(proxy, buf, len);
+		return;
+	}
+
+	switch (hdr->evt) {
+	case BT_HCI_EVT_CMD_COMPLETE:
+		cc = buf + 1 + sizeof(*hdr);
+
+		switch (le16_to_cpu(cc->opcode)) {
+		case BT_HCI_CMD_READ_LOCAL_COMMANDS:
+			rlc = buf + 1 + sizeof(*hdr) + sizeof(*cc);
+			rlc->commands[34] |= 0x02;	/* P-256 Public Key */
+			rlc->commands[34] |= 0x04;	/* Generate DHKey */
+			break;
+		}
+
+		dev_write_packet(proxy, buf, len);
+		break;
+
+	default:
+		dev_write_packet(proxy, buf, len);
+		break;
+	}
+}
+
+static void host_read_destroy(void *user_data)
+{
+	struct proxy *proxy = user_data;
+
+	printf("Closing host descriptor\n");
+
+	if (proxy->host_shutdown)
+		shutdown(proxy->host_fd, SHUT_RDWR);
+
+	close(proxy->host_fd);
+	proxy->host_fd = -1;
+
+	if (proxy->dev_fd < 0) {
+		client_active = false;
+		free(proxy);
+	} else
+		mainloop_remove_fd(proxy->dev_fd);
+}
+
+static void host_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct proxy *proxy = user_data;
+	struct bt_hci_cmd_hdr *cmd_hdr;
+	struct bt_hci_acl_hdr *acl_hdr;
+	struct bt_hci_sco_hdr *sco_hdr;
+	ssize_t len;
+	uint16_t pktlen;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		fprintf(stderr, "Error from host descriptor\n");
+		mainloop_remove_fd(proxy->host_fd);
+		return;
+	}
+
+	if (events & EPOLLRDHUP) {
+		fprintf(stderr, "Remote hangup of host descriptor\n");
+		mainloop_remove_fd(proxy->host_fd);
+		return;
+	}
+
+	len = read(proxy->host_fd, proxy->host_buf + proxy->host_len,
+				sizeof(proxy->host_buf) - proxy->host_len);
+	if (len < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			return;
+
+		fprintf(stderr, "Read from host descriptor failed\n");
+		mainloop_remove_fd(proxy->host_fd);
+		return;
+	}
+
+	if (debug_enabled)
+		util_hexdump('>', proxy->host_buf + proxy->host_len, len,
+						hexdump_print, "H: ");
+
+	proxy->host_len += len;
+
+process_packet:
+	if (proxy->host_len < 1)
+		return;
+
+	switch (proxy->host_buf[0]) {
+	case BT_H4_CMD_PKT:
+		if (proxy->host_len < 1 + sizeof(*cmd_hdr))
+			return;
+
+		cmd_hdr = (void *) (proxy->host_buf + 1);
+		pktlen = 1 + sizeof(*cmd_hdr) + cmd_hdr->plen;
+		break;
+	case BT_H4_ACL_PKT:
+		if (proxy->host_len < 1 + sizeof(*acl_hdr))
+			return;
+
+		acl_hdr = (void *) (proxy->host_buf + 1);
+		pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen);
+		break;
+	case BT_H4_SCO_PKT:
+		if (proxy->host_len < 1 + sizeof(*sco_hdr))
+			return;
+
+		sco_hdr = (void *) (proxy->host_buf + 1);
+		pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen;
+		break;
+	case 0xff:
+		/* Notification packet from /dev/vhci - ignore */
+		proxy->host_len = 0;
+		return;
+	default:
+		fprintf(stderr, "Received unknown host packet type 0x%02x\n",
+							proxy->host_buf[0]);
+		mainloop_remove_fd(proxy->host_fd);
+		return;
+	}
+
+	if (proxy->host_len < pktlen)
+		return;
+
+	if (emulate_ecc)
+		host_emulate_ecc(proxy, proxy->host_buf, pktlen);
+	else
+		host_write_packet(proxy, proxy->host_buf, pktlen);
+
+	if (proxy->host_len > pktlen) {
+		memmove(proxy->host_buf, proxy->host_buf + pktlen,
+						proxy->host_len - pktlen);
+		proxy->host_len -= pktlen;
+		goto process_packet;
+	}
+
+	proxy->host_len = 0;
+}
+
+static void dev_read_destroy(void *user_data)
+{
+	struct proxy *proxy = user_data;
+
+	printf("Closing device descriptor\n");
+
+	if (proxy->dev_shutdown)
+		shutdown(proxy->dev_fd, SHUT_RDWR);
+
+	close(proxy->dev_fd);
+	proxy->dev_fd = -1;
+
+	if (proxy->host_fd < 0) {
+		client_active = false;
+		free(proxy);
+	} else
+		mainloop_remove_fd(proxy->host_fd);
+}
+
+static void dev_read_callback(int fd, uint32_t events, void *user_data)
+{
+	struct proxy *proxy = user_data;
+	struct bt_hci_evt_hdr *evt_hdr;
+	struct bt_hci_acl_hdr *acl_hdr;
+	struct bt_hci_sco_hdr *sco_hdr;
+	ssize_t len;
+	uint16_t pktlen;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		fprintf(stderr, "Error from device descriptor\n");
+		mainloop_remove_fd(proxy->dev_fd);
+		return;
+	}
+
+	if (events & EPOLLRDHUP) {
+		fprintf(stderr, "Remote hangup of device descriptor\n");
+		mainloop_remove_fd(proxy->host_fd);
+		return;
+	}
+
+	len = read(proxy->dev_fd, proxy->dev_buf + proxy->dev_len,
+				sizeof(proxy->dev_buf) - proxy->dev_len);
+	if (len < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			return;
+
+		fprintf(stderr, "Read from device descriptor failed\n");
+		mainloop_remove_fd(proxy->dev_fd);
+		return;
+	}
+
+	if (debug_enabled)
+		util_hexdump('>', proxy->dev_buf + proxy->dev_len, len,
+						hexdump_print, "D: ");
+
+	proxy->dev_len += len;
+
+process_packet:
+	if (proxy->dev_len < 1)
+		return;
+
+	switch (proxy->dev_buf[0]) {
+	case BT_H4_EVT_PKT:
+		if (proxy->dev_len < 1 + sizeof(*evt_hdr))
+			return;
+
+		evt_hdr = (void *) (proxy->dev_buf + 1);
+		pktlen = 1 + sizeof(*evt_hdr) + evt_hdr->plen;
+		break;
+	case BT_H4_ACL_PKT:
+		if (proxy->dev_len < 1 + sizeof(*acl_hdr))
+			return;
+
+		acl_hdr = (void *) (proxy->dev_buf + 1);
+		pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen);
+		break;
+	case BT_H4_SCO_PKT:
+		if (proxy->dev_len < 1 + sizeof(*sco_hdr))
+			return;
+
+		sco_hdr = (void *) (proxy->dev_buf + 1);
+		pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen;
+		break;
+	default:
+		fprintf(stderr, "Received unknown device packet type 0x%02x\n",
+							proxy->dev_buf[0]);
+		mainloop_remove_fd(proxy->dev_fd);
+		return;
+	}
+
+	if (proxy->dev_len < pktlen)
+		return;
+
+	if (emulate_ecc)
+		dev_emulate_ecc(proxy, proxy->dev_buf, pktlen);
+	else
+		dev_write_packet(proxy, proxy->dev_buf, pktlen);
+
+	if (proxy->dev_len > pktlen) {
+		memmove(proxy->dev_buf, proxy->dev_buf + pktlen,
+						proxy->dev_len - pktlen);
+		proxy->dev_len -= pktlen;
+		goto process_packet;
+	}
+
+	proxy->dev_len = 0;
+}
+
+static bool setup_proxy(int host_fd, bool host_shutdown,
+						int dev_fd, bool dev_shutdown)
+{
+	struct proxy *proxy;
+
+	proxy = new0(struct proxy, 1);
+	if (!proxy)
+		return false;
+
+	if (emulate_ecc)
+		printf("Enabling ECC emulation\n");
+
+	proxy->host_fd = host_fd;
+	proxy->host_shutdown = host_shutdown;
+
+	proxy->dev_fd = dev_fd;
+	proxy->dev_shutdown = dev_shutdown;
+
+	mainloop_add_fd(proxy->host_fd, EPOLLIN | EPOLLRDHUP,
+				host_read_callback, proxy, host_read_destroy);
+
+	mainloop_add_fd(proxy->dev_fd, EPOLLIN | EPOLLRDHUP,
+				dev_read_callback, proxy, dev_read_destroy);
+
+	return true;
+}
+
+static int open_channel(uint16_t index)
+{
+	struct sockaddr_hci addr;
+	int fd;
+
+	printf("Opening user channel for hci%u\n", hci_index);
+
+	fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open Bluetooth socket");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = index;
+	addr.hci_channel = HCI_CHANNEL_USER;
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(fd);
+		perror("Failed to bind Bluetooth socket");
+		return -1;
+	}
+
+	return fd;
+}
+
+static void server_callback(int fd, uint32_t events, void *user_data)
+{
+	union {
+		struct sockaddr common;
+		struct sockaddr_un sun;
+		struct sockaddr_in sin;
+	} addr;
+	socklen_t len;
+	int host_fd, dev_fd;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		mainloop_quit();
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	len = sizeof(addr);
+
+	if (getsockname(fd, &addr.common, &len) < 0) {
+		perror("Failed to get socket name");
+		return;
+	}
+
+	host_fd = accept(fd, &addr.common, &len);
+	if (host_fd < 0) {
+		perror("Failed to accept client socket");
+		return;
+	}
+
+	if (client_active) {
+		fprintf(stderr, "Active client already present\n");
+		close(host_fd);
+		return;
+	}
+
+	dev_fd = open_channel(hci_index);
+	if (dev_fd < 0) {
+		close(host_fd);
+		return;
+	}
+
+	printf("New client connected\n");
+
+	if (!setup_proxy(host_fd, true, dev_fd, false)) {
+		close(dev_fd);
+		close(host_fd);
+		return;
+	}
+
+	client_active = true;
+}
+
+static int open_unix(const char *path)
+{
+	struct sockaddr_un addr;
+	int fd;
+
+	unlink(path);
+
+	fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to open Unix server socket");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strcpy(addr.sun_path, path);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind Unix server socket");
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, 1) < 0) {
+		perror("Failed to listen Unix server socket");
+		close(fd);
+		return -1;
+	}
+
+	if (chmod(path, 0666) < 0)
+		perror("Failed to change mode");
+
+	return fd;
+}
+
+static int open_tcp(const char *address, unsigned int port)
+{
+	struct sockaddr_in addr;
+	int fd, opt = 1;
+
+	fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to open TCP server socket");
+		return -1;
+	}
+
+	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = inet_addr(address);
+	addr.sin_port = htons(port);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind TCP server socket");
+		close(fd);
+		return -1;
+	}
+
+	if (listen(fd, 1) < 0) {
+		perror("Failed to listen TCP server socket");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int connect_tcp(const char *address, unsigned int port)
+{
+	struct sockaddr_in addr;
+	int fd;
+
+	fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) {
+		perror("Failed to open TCP client socket");
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = inet_addr(address);
+	addr.sin_port = htons(port);
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to connect TCP client socket");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int open_vhci(uint8_t type)
+{
+	uint8_t create_req[2] = { 0xff, type };
+	ssize_t written;
+	int fd;
+
+	fd = open("/dev/vhci", O_RDWR | O_CLOEXEC);
+	if (fd < 0) {
+		perror("Failed to open /dev/vhci device");
+		return -1;
+	}
+
+	written = write(fd, create_req, sizeof(create_req));
+	if (written < 0) {
+		perror("Failed to set device type");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("btproxy - Bluetooth controller proxy\n"
+		"Usage:\n");
+	printf("\tbtproxy [options]\n");
+	printf("Options:\n"
+		"\t-c, --connect <address>     Connect to server\n"
+		"\t-l, --listen [address]      Use TCP server\n"
+		"\t-u, --unix [path]           Use Unix server\n"
+		"\t-p, --port <port>           Use specified TCP port\n"
+		"\t-i, --index <num>           Use specified controller\n"
+		"\t-a, --amp                   Create AMP controller\n"
+		"\t-e, --ecc                   Emulate ECC support\n"
+		"\t-d, --debug                 Enable debugging output\n"
+		"\t-h, --help                  Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "redirect", no_argument,       NULL, 'r' },
+	{ "connect",  required_argument, NULL, 'c' },
+	{ "listen",   optional_argument, NULL, 'l' },
+	{ "unix",     optional_argument, NULL, 'u' },
+	{ "port",     required_argument, NULL, 'p' },
+	{ "index",    required_argument, NULL, 'i' },
+	{ "amp",      no_argument,       NULL, 'a' },
+	{ "ecc",      no_argument,       NULL, 'e' },
+	{ "debug",    no_argument,       NULL, 'd' },
+	{ "version",  no_argument,       NULL, 'v' },
+	{ "help",     no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *connect_address = NULL;
+	const char *server_address = NULL;
+	const char *unix_path = NULL;
+	unsigned short tcp_port = 0xb1ee;	/* 45550 */
+	bool use_redirect = false;
+	uint8_t type = HCI_BREDR;
+	const char *str;
+	sigset_t mask;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "rc:l::u::p:i:aedvh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'r':
+			use_redirect = true;
+			break;
+		case 'c':
+			connect_address = optarg;
+			break;
+		case 'l':
+			if (optarg)
+				server_address = optarg;
+			else
+				server_address = "0.0.0.0";
+			break;
+		case 'u':
+			if (optarg)
+				unix_path = optarg;
+			else
+				unix_path = "/tmp/bt-server-bredr";
+			break;
+		case 'p':
+			tcp_port = atoi(optarg);
+			break;
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				usage();
+				return EXIT_FAILURE;
+			}
+			hci_index = atoi(str);
+			break;
+		case 'a':
+			type = HCI_AMP;
+			break;
+		case 'e':
+			emulate_ecc = true;
+			break;
+		case 'd':
+			debug_enabled = true;
+			break;
+		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;
+	}
+
+	if (unix_path && (server_address || use_redirect)) {
+		fprintf(stderr, "Invalid to specify TCP and Unix servers\n");
+		return EXIT_FAILURE;
+	}
+
+	if (connect_address && (unix_path || server_address || use_redirect)) {
+		fprintf(stderr, "Invalid to specify client and server mode\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	if (connect_address || use_redirect) {
+		int host_fd, dev_fd;
+
+		if (use_redirect) {
+			printf("Creating local redirect\n");
+
+			dev_fd = open_channel(hci_index);
+		} else {
+			printf("Connecting to %s:%u\n", connect_address,
+								tcp_port);
+
+			dev_fd = connect_tcp(connect_address, tcp_port);
+		}
+
+		if (dev_fd < 0)
+			return EXIT_FAILURE;
+
+		printf("Opening virtual device\n");
+
+		host_fd = open_vhci(type);
+		if (host_fd < 0) {
+			close(dev_fd);
+			return EXIT_FAILURE;
+		}
+
+		if (!setup_proxy(host_fd, false, dev_fd, true)) {
+			close(dev_fd);
+			close(host_fd);
+			return EXIT_FAILURE;
+		}
+	} else {
+		int server_fd;
+
+		if (unix_path) {
+			printf("Listening on %s\n", unix_path);
+
+			server_fd = open_unix(unix_path);
+		} else if (server_address) {
+			printf("Listening on %s:%u\n", server_address,
+								tcp_port);
+
+			server_fd = open_tcp(server_address, tcp_port);
+		} else {
+			fprintf(stderr, "Missing emulator device\n");
+			return EXIT_FAILURE;
+		}
+
+		if (server_fd < 0)
+			return EXIT_FAILURE;
+
+		mainloop_add_fd(server_fd, EPOLLIN, server_callback,
+							NULL, NULL);
+	}
+
+	return mainloop_run();
+}
diff --git a/bluez/repo/tools/btsnoop.c b/bluez/repo/tools/btsnoop.c
new file mode 100644
index 0000000..3eb8082
--- /dev/null
+++ b/bluez/repo/tools/btsnoop.c
@@ -0,0 +1,609 @@
+/*
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <getopt.h>
+#include <endian.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include "src/shared/btsnoop.h"
+
+struct btsnoop_hdr {
+	uint8_t		id[8];		/* Identification Pattern */
+	uint32_t	version;	/* Version Number = 1 */
+	uint32_t	type;		/* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+	uint32_t	size;		/* Original Length */
+	uint32_t	len;		/* Included Length */
+	uint32_t	flags;		/* Packet Flags */
+	uint32_t	drops;		/* Cumulative Drops */
+	uint64_t	ts;		/* Timestamp microseconds */
+	uint8_t		data[0];	/* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
+				      0x6f, 0x6f, 0x70, 0x00 };
+
+static const uint32_t btsnoop_version = 1;
+
+static int create_btsnoop(const char *path)
+{
+	struct btsnoop_hdr hdr;
+	ssize_t written;
+	int fd;
+
+	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+				S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if (fd < 0) {
+		perror("failed to output file");
+		return -1;
+	}
+
+	memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+	hdr.version = htobe32(btsnoop_version);
+	hdr.type = htobe32(2001);
+
+	written = write(fd, &hdr, BTSNOOP_HDR_SIZE);
+	if (written < 0) {
+		perror("failed to write output header");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int open_btsnoop(const char *path, uint32_t *type)
+{
+	struct btsnoop_hdr hdr;
+	ssize_t len;
+	int fd;
+
+	fd = open(path, O_RDONLY | O_CLOEXEC);
+	if (fd < 0) {
+		perror("failed to open input file");
+		return -1;
+	}
+
+	len = read(fd, &hdr, BTSNOOP_HDR_SIZE);
+	if (len < 0 || len != BTSNOOP_HDR_SIZE) {
+		perror("failed to read input header");
+		close(fd);
+		return -1;
+	}
+
+	if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
+		fprintf(stderr, "not a valid btsnoop header\n");
+		close(fd);
+		return -1;
+	}
+
+	if (be32toh(hdr.version) != btsnoop_version) {
+		fprintf(stderr, "invalid btsnoop version\n");
+		close(fd);
+		return -1;
+	}
+
+	if (type)
+		*type = be32toh(hdr.type);
+
+	return fd;
+}
+
+#define MAX_MERGE 8
+
+static void command_merge(const char *output, int argc, char *argv[])
+{
+	struct btsnoop_pkt input_pkt[MAX_MERGE];
+	unsigned char buf[2048];
+	int output_fd, input_fd[MAX_MERGE], num_input = 0;
+	int i, select_input;
+	ssize_t len, written;
+	uint32_t toread, flags;
+	uint16_t index, opcode;
+
+	if (argc > MAX_MERGE) {
+		fprintf(stderr, "only up to %d files allowed\n", MAX_MERGE);
+		return;
+	}
+
+	for (i = 0; i < argc; i++) {
+		uint32_t type;
+		int fd;
+
+		fd = open_btsnoop(argv[i], &type);
+		if (fd < 0)
+			break;
+
+		if (type != 1002) {
+			fprintf(stderr, "unsupported link data type %u\n",
+									type);
+			close(fd);
+			break;
+		}
+
+		input_fd[num_input++] = fd;
+	}
+
+	if (num_input != argc) {
+		fprintf(stderr, "failed to open all input files\n");
+		goto close_input;
+	}
+
+	output_fd = create_btsnoop(output);
+	if (output_fd < 0)
+		goto close_input;
+
+	for (i = 0; i < num_input; i++) {
+		len = read(input_fd[i], &input_pkt[i], BTSNOOP_PKT_SIZE);
+		if (len < 0 || len != BTSNOOP_PKT_SIZE) {
+			close(input_fd[i]);
+			input_fd[i] = -1;
+		}
+	}
+
+next_packet:
+	select_input = -1;
+
+	for (i = 0; i < num_input; i++) {
+		uint64_t ts;
+
+		if (input_fd[i] < 0)
+			continue;
+
+		if (select_input < 0) {
+			select_input = i;
+			continue;
+		}
+
+		ts = be64toh(input_pkt[i].ts);
+
+		if (ts < be64toh(input_pkt[select_input].ts))
+			select_input = i;
+	}
+
+	if (select_input < 0)
+		goto close_output;
+
+	toread = be32toh(input_pkt[select_input].size);
+	flags = be32toh(input_pkt[select_input].flags);
+
+	len = read(input_fd[select_input], buf, toread);
+	if (len < 0 || len != (ssize_t) toread) {
+		close(input_fd[select_input]);
+		input_fd[select_input] = -1;
+		goto next_packet;
+	}
+
+	written = htobe32(toread - 1);
+	input_pkt[select_input].size = written;
+	input_pkt[select_input].len = written;
+
+	switch (buf[0]) {
+	case 0x01:
+		opcode = BTSNOOP_OPCODE_COMMAND_PKT;
+		break;
+	case 0x02:
+		if (flags & 0x01)
+			opcode = BTSNOOP_OPCODE_ACL_RX_PKT;
+		else
+			opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
+		break;
+	case 0x03:
+		if (flags & 0x01)
+			opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
+		else
+			opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
+		break;
+	case 0x04:
+		opcode = BTSNOOP_OPCODE_EVENT_PKT;
+		break;
+	default:
+		goto skip_write;
+	}
+
+	index = select_input;
+	input_pkt[select_input].flags = htobe32((index << 16) | opcode);
+
+	written = write(output_fd, &input_pkt[select_input], BTSNOOP_PKT_SIZE);
+	if (written != BTSNOOP_PKT_SIZE) {
+		fprintf(stderr, "write of packet header failed\n");
+		goto close_output;
+	}
+
+	written = write(output_fd, buf + 1, toread - 1);
+	if (written != (ssize_t) toread - 1) {
+		fprintf(stderr, "write of packet data failed\n");
+		goto close_output;
+	}
+
+skip_write:
+	len = read(input_fd[select_input],
+				&input_pkt[select_input], BTSNOOP_PKT_SIZE);
+	if (len < 0 || len != BTSNOOP_PKT_SIZE) {
+		close(input_fd[select_input]);
+		input_fd[select_input] = -1;
+	}
+
+	goto next_packet;
+
+close_output:
+	close(output_fd);
+
+close_input:
+	for (i = 0; i < num_input; i++)
+		close(input_fd[i]);
+}
+
+static void command_extract_eir(const char *input)
+{
+	struct btsnoop_pkt pkt;
+	unsigned char buf[2048];
+	ssize_t len;
+	uint32_t type, toread, flags;
+	uint16_t opcode;
+	int fd, count = 0;
+
+	fd = open_btsnoop(input, &type);
+	if (fd < 0)
+		return;
+
+	if (type != 2001) {
+		fprintf(stderr, "unsupported link data type %u\n", type);
+		close(fd);
+		return;
+	}
+
+next_packet:
+	len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
+	if (len < 0 || len != BTSNOOP_PKT_SIZE)
+		goto close_input;
+
+	toread = be32toh(pkt.size);
+	flags = be32toh(pkt.flags);
+
+	opcode = flags & 0x00ff;
+
+	len = read(fd, buf, toread);
+	if (len < 0 || len != (ssize_t) toread) {
+		fprintf(stderr, "failed to read packet data\n");
+		goto close_input;
+	}
+
+	switch (opcode) {
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		/* extended inquiry result event */
+		if (buf[0] == 0x2f) {
+			uint8_t *eir_ptr, eir_len, i;
+
+			eir_len = buf[1] - 15;
+			eir_ptr = buf + 17;
+
+			if (eir_len < 1 || eir_len > 240)
+				break;
+
+			printf("\t[Extended Inquiry Data with %u bytes]\n",
+								eir_len);
+			printf("\t\t");
+			for (i = 0; i < eir_len; i++) {
+				printf("0x%02x", eir_ptr[i]);
+				if (((i + 1) % 8) == 0) {
+					if (i < eir_len - 1)
+						printf(",\n\t\t");
+				} else {
+					if (i < eir_len - 1)
+						printf(", ");
+				}
+			}
+			printf("\n");
+
+			count++;
+		}
+		break;
+	}
+
+	goto next_packet;
+
+close_input:
+	close(fd);
+}
+
+static void command_extract_ad(const char *input)
+{
+	struct btsnoop_pkt pkt;
+	unsigned char buf[2048];
+	ssize_t len;
+	uint32_t type, toread, flags;
+	uint16_t opcode;
+	int fd, count = 0;
+
+	fd = open_btsnoop(input, &type);
+	if (fd < 0)
+		return;
+
+	if (type != 2001) {
+		fprintf(stderr, "unsupported link data type %u\n", type);
+		close(fd);
+		return;
+	}
+
+next_packet:
+	len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
+	if (len < 0 || len != BTSNOOP_PKT_SIZE)
+		goto close_input;
+
+	toread = be32toh(pkt.size);
+	flags = be32toh(pkt.flags);
+
+	opcode = flags & 0x00ff;
+
+	len = read(fd, buf, toread);
+	if (len < 0 || len != (ssize_t) toread) {
+		fprintf(stderr, "failed to read packet data\n");
+		goto close_input;
+	}
+
+	switch (opcode) {
+	case BTSNOOP_OPCODE_EVENT_PKT:
+		/* advertising report */
+		if (buf[0] == 0x3e && buf[2] == 0x02) {
+			uint8_t *ad_ptr, ad_len, i;
+
+			ad_len = buf[12];
+			ad_ptr = buf + 13;
+
+			if (ad_len < 1 || ad_len > 40)
+				break;
+
+			printf("\t[Advertising Data with %u bytes]\n", ad_len);
+			printf("\t\t");
+			for (i = 0; i < ad_len; i++) {
+				printf("0x%02x", ad_ptr[i]);
+				if (((i + 1) % 8) == 0) {
+					if (i < ad_len - 1)
+						printf(",\n\t\t");
+				} else {
+					if (i < ad_len - 1)
+						printf(", ");
+				}
+			}
+			printf("\n");
+
+			count++;
+		}
+		break;
+	}
+
+	goto next_packet;
+
+close_input:
+	close(fd);
+}
+static const uint8_t conn_complete[] = { 0x04, 0x03, 0x0B, 0x00 };
+static const uint8_t disc_complete[] = { 0x04, 0x05, 0x04, 0x00 };
+
+static void command_extract_sdp(const char *input)
+{
+	struct btsnoop_pkt pkt;
+	unsigned char buf[2048];
+	ssize_t len;
+	uint32_t type, toread;
+	uint16_t current_cid = 0x0000;
+	uint8_t pdu_buf[512];
+	uint16_t pdu_len = 0;
+	bool pdu_first = false;
+	int fd, count = 0;
+
+	fd = open_btsnoop(input, &type);
+	if (fd < 0)
+		return;
+
+	if (type != 1002) {
+		fprintf(stderr, "unsupported link data type %u\n", type);
+		close(fd);
+		return;
+	}
+
+next_packet:
+	len = read(fd, &pkt, BTSNOOP_PKT_SIZE);
+	if (len < 0 || len != BTSNOOP_PKT_SIZE)
+		goto close_input;
+
+	toread = be32toh(pkt.size);
+
+	len = read(fd, buf, toread);
+	if (len < 0 || len != (ssize_t) toread) {
+		fprintf(stderr, "failed to read packet data\n");
+		goto close_input;
+	}
+
+	if (buf[0] == 0x02) {
+		uint8_t acl_flags;
+
+		/* first 4 bytes are handle and data len */
+		acl_flags = buf[2] >> 4;
+
+		/* use only packet with ACL start flag */
+		if (acl_flags & 0x02) {
+			if (current_cid == 0x0040 && pdu_len > 0) {
+				int i;
+				if (!pdu_first)
+					printf(",\n");
+				printf("\t\traw_pdu(");
+				for (i = 0; i < pdu_len; i++) {
+					printf("0x%02x", pdu_buf[i]);
+					if (((i + 1) % 8) == 0) {
+						if (i < pdu_len - 1)
+							printf(",\n\t\t\t");
+					} else {
+						if (i < pdu_len - 1)
+							printf(", ");
+					}
+				}
+				printf(")");
+				pdu_first = false;
+			}
+
+			/* next 4 bytes are data len and cid */
+			current_cid = buf[8] << 8 | buf[7];
+			memcpy(pdu_buf, buf + 9, len - 9);
+			pdu_len = len - 9;
+		} else if (acl_flags & 0x01) {
+			memcpy(pdu_buf + pdu_len, buf + 5, len - 5);
+			pdu_len += len - 5;
+		}
+	}
+
+	if ((size_t) len > sizeof(conn_complete)) {
+		if (memcmp(buf, conn_complete, sizeof(conn_complete)) == 0) {
+			printf("\tdefine_test(\"/test/%u\",\n", ++count);
+			pdu_first = true;
+		}
+	}
+
+	if ((size_t) len > sizeof(disc_complete)) {
+		if (memcmp(buf, disc_complete, sizeof(disc_complete)) == 0) {
+			printf(");\n");
+		}
+	}
+
+	goto next_packet;
+
+close_input:
+	close(fd);
+}
+
+static void usage(void)
+{
+	printf("btsnoop trace file handling tool\n"
+		"Usage:\n");
+	printf("\tbtsnoop <command> [files]\n");
+	printf("commands:\n"
+		"\t-m, --merge <output>   Merge multiple btsnoop files\n"
+		"\t-e, --extract <input>  Extract data from btsnoop file\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "merge",   required_argument, NULL, 'm' },
+	{ "extract", required_argument, NULL, 'e' },
+	{ "type",    required_argument, NULL, 't' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+enum { INVALID, MERGE, EXTRACT };
+
+int main(int argc, char *argv[])
+{
+	const char *output_path = NULL;
+	const char *input_path = NULL;
+	const char *type = NULL;
+	unsigned short command = INVALID;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'm':
+			command = MERGE;
+			output_path = optarg;
+			break;
+		case 'e':
+			command = EXTRACT;
+			input_path = optarg;
+			break;
+		case 't':
+			type = optarg;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	switch (command) {
+	case MERGE:
+		if (argc - optind < 1) {
+			fprintf(stderr, "input files required\n");
+			return EXIT_FAILURE;
+		}
+
+		command_merge(output_path, argc - optind, argv + optind);
+		break;
+
+	case EXTRACT:
+		if (argc - optind > 0) {
+			fprintf(stderr, "extra arguments not allowed\n");
+			return EXIT_FAILURE;
+		}
+
+		if (!type) {
+			fprintf(stderr, "no extract type specified\n");
+			return EXIT_FAILURE;
+		}
+
+		if (!strcasecmp(type, "eir"))
+			command_extract_eir(input_path);
+		else if (!strcasecmp(type, "ad"))
+			command_extract_ad(input_path);
+		else if (!strcasecmp(type, "sdp"))
+			command_extract_sdp(input_path);
+		else
+			fprintf(stderr, "extract type not supported\n");
+		break;
+
+	default:
+		usage();
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/check-selftest.c b/bluez/repo/tools/check-selftest.c
new file mode 100644
index 0000000..6006b80
--- /dev/null
+++ b/bluez/repo/tools/check-selftest.c
@@ -0,0 +1,71 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+static void check_result(const char *name, const char *pathname)
+{
+	FILE *fp;
+	int i;
+
+	for (i = 0; i < 50; i++) {
+		struct stat st;
+
+		if (!stat(pathname, &st)) {
+			printf("Found %s selftest result\n", name);
+			break;
+		}
+
+		usleep(25 * 1000);
+	}
+
+	fp = fopen(pathname, "re");
+	if (fp) {
+		char result[32], *ptr;
+
+		ptr = fgets(result, sizeof(result), fp);
+		fclose(fp);
+
+		ptr = strpbrk(result, "\r\n");
+		if (ptr)
+			*ptr = '\0';
+
+		printf("%s: %s\n", name, result);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	check_result("ECDH", "/sys/kernel/debug/bluetooth/selftest_ecdh");
+	check_result("SMP",  "/sys/kernel/debug/bluetooth/selftest_smp");
+
+	return 0;
+}
diff --git a/bluez/repo/tools/ciptool.1 b/bluez/repo/tools/ciptool.1
new file mode 100644
index 0000000..65d903d
--- /dev/null
+++ b/bluez/repo/tools/ciptool.1
@@ -0,0 +1,68 @@
+.\"
+.\"	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH CIPTOOL 1 "JUNE 6, 2003" "" ""
+
+.SH NAME
+ciptool \- Bluetooth Common ISDN Access Profile (CIP)
+.SH SYNOPSIS
+.BR "ciptool
+[
+.I options
+] <
+.I command
+>
+.SH DESCRIPTION
+.B ciptool
+is used to set up, maintain, and inspect the CIP configuration
+of the Bluetooth subsystem in the Linux kernel.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.I
+hciX
+, which must be the name or the address of an installed Bluetooth
+device. If not specified, the command will be use the first
+available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI show
+Display information about the connected devices.
+.TP
+.BI search
+Search for Bluetooth devices and connect to first one that
+offers CIP support.
+.TP
+.BI connect " <bdaddr> [psm]"
+Connect the local device to the remote Bluetooth device on the
+specified PSM number. If no PSM is specified, it will use the
+SDP to retrieve it from the remote device.
+.TP
+.BI release " [bdaddr]"
+Release a connection to the specific device. If no address is
+given and only one device is connected this will be released.
+.TP
+.BI loopback " <bdaddr> [psm]"
+Create a connection to the remote device for Bluetooth testing.
+This command will not provide a CAPI controller, because it is
+only for testing the CAPI Message Transport Protocol.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/bluez/repo/tools/ciptool.c b/bluez/repo/tools/ciptool.c
new file mode 100644
index 0000000..e60493d
--- /dev/null
+++ b/bluez/repo/tools/ciptool.c
@@ -0,0 +1,493 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/cmtp.h"
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+	return;
+}
+
+static void sig_term(int sig)
+{
+	__io_canceled = 1;
+}
+
+static char *cmtp_state[] = {
+	"unknown",
+	"connected",
+	"open",
+	"bound",
+	"listening",
+	"connecting",
+	"connecting",
+	"config",
+	"disconnecting",
+	"closed"
+};
+
+static char *cmtp_flagstostr(uint32_t flags)
+{
+	static char str[100] = "";
+
+	strcat(str, "[");
+
+	if (flags & (1 << CMTP_LOOPBACK))
+		strcat(str, "loopback");
+
+	strcat(str, "]");
+
+	return str;
+}
+
+static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm)
+{
+	sdp_session_t *s;
+	sdp_list_t *srch, *attrs, *rsp;
+	uuid_t svclass;
+	uint16_t attr;
+	int err;
+
+	if (!(s = sdp_connect(src, dst, 0)))
+		return -1;
+
+	sdp_uuid16_create(&svclass, CIP_SVCLASS_ID);
+	srch = sdp_list_append(NULL, &svclass);
+
+	attr = SDP_ATTR_PROTO_DESC_LIST;
+	attrs = sdp_list_append(NULL, &attr);
+
+	err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+	sdp_close(s);
+
+	if (err)
+		return 0;
+
+	for (; rsp; rsp = rsp->next) {
+		sdp_record_t *rec = (sdp_record_t *) rsp->data;
+		sdp_list_t *protos;
+
+		if (!sdp_get_access_protos(rec, &protos)) {
+			unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID);
+			if (p > 0) {
+				*psm = p;
+				return 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags)
+{
+	struct cmtp_connadd_req req;
+	struct hci_dev_info di;
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	socklen_t size;
+	int sk;
+
+	hci_devinfo(dev_id, &di);
+	if (!(di.link_policy & HCI_LP_RSWITCH)) {
+		printf("Local device is not accepting role switch\n");
+	}
+
+	if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+		perror("Can't create L2CAP socket");
+		exit(1);
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+
+	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		perror("Can't bind L2CAP socket");
+		close(sk);
+		exit(1);
+	}
+
+	memset(&opts, 0, sizeof(opts));
+	size = sizeof(opts);
+
+	if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+		perror("Can't get L2CAP options");
+		close(sk);
+		exit(1);
+	}
+
+	opts.imtu = CMTP_DEFAULT_MTU;
+	opts.omtu = CMTP_DEFAULT_MTU;
+	opts.flush_to = 0xffff;
+
+	if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+		perror("Can't set L2CAP options");
+		close(sk);
+		exit(1);
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(psm);
+
+	if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		perror("Can't connect L2CAP socket");
+		close(sk);
+		exit(1);
+	}
+
+	req.sock = sk;
+	req.flags = flags;
+
+	if (ioctl(ctl, CMTPCONNADD, &req) < 0) {
+		perror("Can't create connection");
+		exit(1);
+	}
+
+	return sk;
+}
+
+static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	struct cmtp_connlist_req req;
+	struct cmtp_conninfo ci[16];
+	char addr[18];
+	unsigned int i;
+
+	req.cnum = 16;
+	req.ci   = ci;
+
+	if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) {
+		perror("Can't get connection list");
+		exit(1);
+	}
+
+	for (i = 0; i < req.cnum; i++) {
+		ba2str(&ci[i].bdaddr, addr);
+		printf("%d %s %s %s\n", ci[i].num, addr,
+			cmtp_state[ci[i].state],
+			ci[i].flags ? cmtp_flagstostr(ci[i].flags) : "");
+	}
+}
+
+static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	inquiry_info *info = NULL;
+	bdaddr_t src, dst;
+	unsigned short psm;
+	int i, dev_id, num_rsp, length, flags;
+	char addr[18];
+	uint8_t class[3];
+
+	ba2str(bdaddr, addr);
+	dev_id = hci_devid(addr);
+	if (dev_id < 0) {
+		dev_id = hci_get_route(NULL);
+		hci_devba(dev_id, &src);
+	} else
+		bacpy(&src, bdaddr);
+
+	length  = 8;	/* ~10 seconds */
+	num_rsp = 0;
+	flags   = 0;
+
+	printf("Searching ...\n");
+
+	num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+	for (i = 0; i < num_rsp; i++) {
+		memcpy(class, (info+i)->dev_class, 3);
+		if ((class[1] == 2) && ((class[0] / 4) == 5)) {
+			bacpy(&dst, &(info+i)->bdaddr);
+			ba2str(&dst, addr);
+
+			printf("\tChecking service for %s\n", addr);
+			if (!get_psm(&src, &dst, &psm))
+				continue;
+
+			bt_free(info);
+
+			printf("\tConnecting to device %s\n", addr);
+			do_connect(ctl, dev_id, &src, &dst, psm, 0);
+			return;
+		}
+	}
+
+	bt_free(info);
+	fprintf(stderr, "\tNo devices in range or visible\n");
+	exit(1);
+}
+
+static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	bdaddr_t src, dst;
+	unsigned short psm;
+	int dev_id;
+	char addr[18];
+
+	if (argc < 2)
+		return;
+
+	str2ba(argv[1], &dst);
+
+	ba2str(bdaddr, addr);
+	dev_id = hci_devid(addr);
+	if (dev_id < 0) {
+		dev_id = hci_get_route(&dst);
+		hci_devba(dev_id, &src);
+	} else
+		bacpy(&src, bdaddr);
+
+	if (argc < 3) {
+		if (!get_psm(&src, &dst, &psm))
+			psm = 4099;
+	} else
+		psm = atoi(argv[2]);
+
+	do_connect(ctl, dev_id, &src, &dst, psm, 0);
+}
+
+static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	struct cmtp_conndel_req req;
+	struct cmtp_connlist_req cl;
+	struct cmtp_conninfo ci[16];
+
+	if (argc < 2) {
+		cl.cnum = 16;
+		cl.ci   = ci;
+
+		if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) {
+			perror("Can't get connection list");
+			exit(1);
+		}
+
+		if (cl.cnum == 0)
+			return;
+
+		if (cl.cnum != 1) {
+			fprintf(stderr, "You have to specifiy the device address.\n");
+			exit(1);
+		}
+
+		bacpy(&req.bdaddr, &ci[0].bdaddr);
+	} else
+		str2ba(argv[1], &req.bdaddr);
+
+	if (ioctl(ctl, CMTPCONNDEL, &req) < 0) {
+		perror("Can't release connection");
+		exit(1);
+	}
+}
+
+static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	struct cmtp_conndel_req req;
+	struct sigaction sa;
+	struct pollfd p;
+	sigset_t sigs;
+	bdaddr_t src, dst;
+	unsigned short psm;
+	int dev_id, sk;
+	char addr[18];
+
+	if (argc < 2)
+		return;
+
+	str2ba(argv[1], &dst);
+
+	ba2str(bdaddr, addr);
+	dev_id = hci_devid(addr);
+	if (dev_id < 0) {
+		dev_id = hci_get_route(&dst);
+		hci_devba(dev_id, &src);
+	} else
+		bacpy(&src, bdaddr);
+
+	ba2str(&dst, addr);
+	printf("Connecting to %s in loopback mode\n", addr);
+
+	if (argc < 3) {
+		if (!get_psm(&src, &dst, &psm))
+			psm = 4099;
+	} else
+		psm = atoi(argv[2]);
+
+	sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
+
+	printf("Press CTRL-C for hangup\n");
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &sa, NULL);
+	sigaction(SIGPIPE, &sa, NULL);
+
+	sa.sa_handler = sig_term;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	sa.sa_handler = sig_hup;
+	sigaction(SIGHUP, &sa, NULL);
+
+	sigfillset(&sigs);
+	sigdelset(&sigs, SIGCHLD);
+	sigdelset(&sigs, SIGPIPE);
+	sigdelset(&sigs, SIGTERM);
+	sigdelset(&sigs, SIGINT);
+	sigdelset(&sigs, SIGHUP);
+
+	p.fd = sk;
+	p.events = POLLERR | POLLHUP;
+
+	while (!__io_canceled) {
+		p.revents = 0;
+		if (ppoll(&p, 1, NULL, &sigs) > 0)
+			break;
+	}
+
+	bacpy(&req.bdaddr, &dst);
+	ioctl(ctl, CMTPCONNDEL, &req);
+}
+
+static struct {
+	char *cmd;
+	char *alt;
+	void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv);
+	char *opt;
+	char *doc;
+} command[] = {
+	{ "show",     "list",       cmd_show,     0,          "Show remote connections"      },
+	{ "search",   "scan",       cmd_search,   0,          "Search for a remote device"   },
+	{ "connect",  "create",     cmd_create,   "<bdaddr>", "Connect a remote device"      },
+	{ "release",  "disconnect", cmd_release,  "[bdaddr]", "Disconnect the remote device" },
+	{ "loopback", "test",       cmd_loopback, "<bdaddr>", "Loopback test of a device"    },
+	{ NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+	int i;
+
+	printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n");
+
+	printf("Usage:\n"
+		"\tciptool [options] [command]\n"
+		"\n");
+
+	printf("Options:\n"
+		"\t-i [hciX|bdaddr]   Local HCI device or BD Address\n"
+		"\t-h, --help         Display help\n"
+		"\n");
+
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-8s %-10s\t%s\n", command[i].cmd,
+		command[i].opt ? command[i].opt : " ",
+		command[i].doc);
+	printf("\n");
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "device",	1, 0, 'i' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	bdaddr_t bdaddr;
+	int i, opt, ctl;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+		switch(opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &bdaddr);
+			else
+				str2ba(optarg, &bdaddr);
+			break;
+		case 'h':
+			usage();
+			exit(0);
+		default:
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		usage();
+		return 0;
+	}
+
+	if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) {
+		perror("Can't open CMTP control socket");
+		exit(1);
+	}
+
+	for (i = 0; command[i].cmd; i++) {
+		if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+			continue;
+		command[i].func(ctl, &bdaddr, argc, argv);
+		close(ctl);
+		exit(0);
+	}
+
+	usage();
+
+	close(ctl);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/cltest.c b/bluez/repo/tools/cltest.c
new file mode 100644
index 0000000..95fa7b6
--- /dev/null
+++ b/bluez/repo/tools/cltest.c
@@ -0,0 +1,278 @@
+/*
+ *
+ *  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 <unistd.h>
+#include <alloca.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+
+#include "src/shared/mainloop.h"
+
+static bool send_message(const bdaddr_t *src, const bdaddr_t *dst,
+							uint16_t psm)
+{
+	const unsigned char buf[] = { 0x42, 0x23 };
+	struct sockaddr_l2 addr;
+	ssize_t len;
+	int fd;
+
+	fd = socket(PF_BLUETOOTH, SOCK_DGRAM | SOCK_CLOEXEC, BTPROTO_L2CAP);
+	if (fd < 0) {
+		perror("Failed to create transmitter socket");
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind transmitter socket");
+		close(fd);
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(psm);
+
+	if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to connect transmitter socket");
+		close(fd);
+		return false;
+	}
+
+	len = send(fd, buf, sizeof(buf), 0);
+	if (len < 0) {
+		perror("Failed to send message");
+		close(fd);
+		return false;
+	}
+
+	return true;
+}
+
+static void receiver_callback(int fd, uint32_t events, void *user_data)
+{
+	unsigned char buf[512];
+	struct sockaddr_l2 addr;
+	socklen_t addrlen = sizeof(addr);
+	char str[18];
+	ssize_t len, i;
+
+	if (events & (EPOLLERR | EPOLLHUP)) {
+		close(fd);
+		mainloop_remove_fd(fd);
+		return;
+	}
+
+	len = recvfrom(fd, buf, sizeof(buf), 0,
+				(struct sockaddr *) &addr, &addrlen);
+	if (len < 0) {
+		perror("Failed to receive data");
+		return;
+	}
+
+	if (addrlen > 0) {
+		ba2str(&addr.l2_bdaddr, str);
+		printf("RX Address: %s PSM: %d CID: %d\n", str,
+				btohs(addr.l2_psm), btohs(addr.l2_cid));
+	}
+
+	printf("RX Data:");
+	for (i = 0; i < len; i++)
+		printf(" 0x%02x", buf[i]);
+	printf("\n");
+}
+
+static bool create_receiver(const bdaddr_t *bdaddr, uint16_t psm)
+{
+	struct sockaddr_l2 addr;
+	int fd;
+
+	fd = socket(PF_BLUETOOTH, SOCK_DGRAM | SOCK_CLOEXEC, BTPROTO_L2CAP);
+	if (fd < 0) {
+		perror("Failed to create receiver socket");
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, bdaddr);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Failed to bind receiver socket");
+		close(fd);
+		return false;
+	}
+
+	mainloop_add_fd(fd, EPOLLIN, receiver_callback, NULL, NULL);
+
+	return true;
+}
+
+static bool activate_controller(int fd, struct hci_dev_info *di)
+{
+	if (!hci_test_bit(HCI_UP, &di->flags)) {
+		char addr[18];
+
+		ba2str(&di->bdaddr, addr);
+		printf("Activating controller %s\n", addr);
+
+		if (ioctl(fd, HCIDEVUP, di->dev_id) < 0) {
+			if (errno != EALREADY) {
+				perror("Failed to bring up HCI device");
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+static bool enable_connections(int fd, struct hci_dev_info *di)
+{
+	if (!hci_test_bit(HCI_PSCAN, &di->flags)) {
+		struct hci_dev_req dr;
+		char addr[18];
+
+		ba2str(&di->bdaddr, addr);
+		printf("Enabling connections on %s\n", addr);
+
+		dr.dev_id  = di->dev_id;
+		dr.dev_opt = SCAN_PAGE;
+
+		if (ioctl(fd, HCISETSCAN, (unsigned long) &dr) < 0) {
+			perror("Failed to enable connections");
+			return false;
+		}
+	}
+	return true;
+}
+
+static bdaddr_t bdaddr_src;
+static bdaddr_t bdaddr_dst;
+
+static bool find_controllers(void)
+{
+	struct hci_dev_list_req *dl;
+	struct hci_dev_req *dr;
+	bool result;
+	int fd, i;
+
+	fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (fd < 0) {
+		perror("Failed to open raw HCI socket");
+		return false;
+	}
+
+	dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
+	if (!dl) {
+		perror("Failed allocate HCI device request memory");
+		close(fd);
+		return false;
+	}
+
+	dl->dev_num = HCI_MAX_DEV;
+	dr = dl->dev_req;
+
+	if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) {
+		perror("Failed to get HCI device list");
+		result = false;
+		goto done;
+	}
+
+	result = true;
+
+	for (i = 0; i< dl->dev_num && result; i++) {
+		struct hci_dev_info di;
+
+		di.dev_id = (dr + i)->dev_id;
+
+		if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0)
+			continue;
+
+		if (((di.type & 0x30) >> 4) != HCI_BREDR)
+			continue;
+
+		if (!bacmp(&bdaddr_src, BDADDR_ANY)) {
+			bacpy(&bdaddr_src, &di.bdaddr);
+			result = activate_controller(fd, &di);
+		} else if (!bacmp(&bdaddr_dst, BDADDR_ANY)) {
+			bacpy(&bdaddr_dst, &di.bdaddr);
+			result = activate_controller(fd, &di);
+			if (result)
+				result = enable_connections(fd, &di);
+		}
+	}
+
+done:
+	free(dl);
+	close(fd);
+	return result;
+}
+
+int main(int argc ,char *argv[])
+{
+	char addr_src[18], addr_dst[18];
+
+	bacpy(&bdaddr_src, BDADDR_ANY);
+	bacpy(&bdaddr_dst, BDADDR_ANY);
+
+	if (!find_controllers())
+		return EXIT_FAILURE;
+
+	if (!bacmp(&bdaddr_src, BDADDR_ANY) ||
+				!bacmp(&bdaddr_dst, BDADDR_ANY)) {
+		fprintf(stderr, "Two controllers are required\n");
+		return EXIT_FAILURE;
+	}
+
+	ba2str(&bdaddr_src, addr_src);
+	ba2str(&bdaddr_dst, addr_dst);
+
+	printf("%s -> %s\n", addr_src, addr_dst);
+
+	mainloop_init();
+
+	create_receiver(&bdaddr_dst, 0x0021);
+	send_message(&bdaddr_src, &bdaddr_dst, 0x0021);
+
+	return mainloop_run();
+}
diff --git a/bluez/repo/tools/create-image.c b/bluez/repo/tools/create-image.c
new file mode 100644
index 0000000..d94f99d
--- /dev/null
+++ b/bluez/repo/tools/create-image.c
@@ -0,0 +1,205 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <inttypes.h>
+
+/*
+ * The "new" ASCII format uses 8-byte hexadecimal fields for all numbers and
+ * separates device numbers into separate fields for major and minor numbers.
+ *
+ *	struct cpio_newc_header {
+ *		char    c_magic[6];
+ *		char    c_ino[8];
+ *		char    c_mode[8];
+ *		char    c_uid[8];
+ *		char    c_gid[8];
+ *		char    c_nlink[8];
+ *		char    c_mtime[8];
+ *		char    c_filesize[8];
+ *		char    c_devmajor[8];
+ *		char    c_devminor[8];
+ *		char    c_rdevmajor[8];
+ *		char    c_rdevminor[8];
+ *		char    c_namesize[8];
+ *		char    c_check[8];
+ *	};
+ *
+ */
+
+#define HDR_FMT "%s%08X%08X%08X%08X%08X%08X%08jX%08X%08X%08X%08X%08X%08X%s"
+
+#define HDR_MAGIC "070701"
+
+static unsigned int ino_cnt = 721;
+
+#define REG_EXE	S_IFREG | \
+		S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+
+static const struct {
+	const char *source;
+	const char *target;
+	mode_t mode;
+} file_list[] = {
+	{ "tools/test-runner", "init", REG_EXE },
+	{ }
+};
+
+static void write_block(FILE *fp, const char *pathname, unsigned int ino,
+						mode_t mode, const char *name)
+{
+	int i, pad, namelen = strlen(name);
+	struct stat st;
+	void *map;
+	int fd;
+
+	if (!pathname) {
+		fd = -1;
+		map = NULL;
+		st.st_size = 0;
+		goto done;
+	}
+
+	fd = open(pathname, O_RDONLY | O_CLOEXEC);
+	if (fd < 0) {
+		fd = -1;
+		map = NULL;
+		st.st_size = 0;
+		goto done;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		close(fd);
+		fd = -1;
+		map = NULL;
+		st.st_size = 0;
+		goto done;
+	}
+
+	map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (!map || map == MAP_FAILED) {
+		close(fd);
+		fd = -1;
+		map = NULL;
+		st.st_size = 0;
+        }
+
+done:
+	fprintf(fp, HDR_FMT, HDR_MAGIC, ino, mode, 0, 0, 1, 0,
+		(uintmax_t) st.st_size, 0, 0, 0, 0, namelen + 1, 0, name);
+
+	pad = 4 - ((110 + namelen) % 4);
+	for (i = 0; i < pad; i++)
+		fputc(0, fp);
+
+	if (st.st_size > 0) {
+		fwrite(map, st.st_size, 1, fp);
+
+		pad = 3 - ((st.st_size + 3) % 4);
+		for (i = 0; i < pad; i++)
+			fputc(0, fp);
+
+		munmap(map, st.st_size);
+		close(fd);
+	}
+}
+
+static void usage(void)
+{
+	printf("create-image - CPIO image creation utility\n"
+		"Usage:\n");
+	printf("\tcreate-image [options]\n");
+	printf("Options:\n"
+		"\t-o, --output <image>   Output CPIO image\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "output",  required_argument, NULL, 'o' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *output_pathname = NULL;
+	FILE *fp;
+	int i;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "o:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'o':
+			output_pathname = optarg;
+			break;
+		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;
+	}
+
+	if (!output_pathname) {
+		fprintf(stderr, "Failed to specify output file\n");
+		return EXIT_FAILURE;
+	}
+
+	fp = fopen(output_pathname, "we");
+
+	for (i = 0; file_list[i].source; i++)
+		write_block(fp, file_list[i].source, ino_cnt++,
+				file_list[i].mode, file_list[i].target);
+
+	write_block(fp, NULL, ino_cnt++, 0, "TRAILER!!!");
+
+	fclose(fp);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/csr.c b/bluez/repo/tools/csr.c
new file mode 100644
index 0000000..2c09189
--- /dev/null
+++ b/bluez/repo/tools/csr.c
@@ -0,0 +1,2855 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "csr.h"
+
+struct psr_data {
+	uint16_t pskey;
+	uint8_t *value;
+	uint8_t size;
+	struct psr_data *next;
+};
+
+static struct psr_data *head = NULL, *tail = NULL;
+
+static struct {
+	uint16_t id;
+	char *str;
+} csr_map[] = {
+	{   66, "HCI 9.8"	},
+	{   97, "HCI 10.3"	},
+	{  101, "HCI 10.5"	},
+	{  111,	"HCI 11.0"	},
+	{  112,	"HCI 11.1"	},
+	{  114,	"HCI 11.2"	},
+	{  115,	"HCI 11.3"	},
+	{  117,	"HCI 12.0"	},
+	{  119,	"HCI 12.1"	},
+	{  133,	"HCI 12.2"	},
+	{  134,	"HCI 12.3"	},
+	{  162,	"HCI 12.4"	},
+	{  165,	"HCI 12.5"	},
+	{  169,	"HCI 12.6"	},
+	{  188,	"HCI 12.7"	},
+	{  218,	"HCI 12.8"	},
+	{  283,	"HCI 12.9"	},
+	{  203,	"HCI 13.2"	},
+	{  204,	"HCI 13.2"	},
+	{  210,	"HCI 13.3"	},
+	{  211,	"HCI 13.3"	},
+	{  213,	"HCI 13.4"	},
+	{  214,	"HCI 13.4"	},
+	{  225,	"HCI 13.5"	},
+	{  226,	"HCI 13.5"	},
+	{  237,	"HCI 13.6"	},
+	{  238,	"HCI 13.6"	},
+	{  242,	"HCI 14.0"	},
+	{  243,	"HCI 14.0"	},
+	{  244,	"HCI 14.0"	},
+	{  245,	"HCI 14.0"	},
+	{  254,	"HCI 13.7"	},
+	{  255,	"HCI 13.7"	},
+	{  264,	"HCI 14.1"	},
+	{  265,	"HCI 14.1"	},
+	{  267,	"HCI 14.2"	},
+	{  268,	"HCI 14.2"	},
+	{  272,	"HCI 14.3"	},
+	{  273,	"HCI 14.3"	},
+	{  274,	"HCI 13.8"	},
+	{  275,	"HCI 13.8"	},
+	{  286,	"HCI 13.9"	},
+	{  287,	"HCI 13.9"	},
+	{  309,	"HCI 13.10"	},
+	{  310,	"HCI 13.10"	},
+	{  313,	"HCI 14.4"	},
+	{  314,	"HCI 14.4"	},
+	{  323,	"HCI 14.5"	},
+	{  324,	"HCI 14.5"	},
+	{  336,	"HCI 14.6"	},
+	{  337,	"HCI 14.6"	},
+	{  351,	"HCI 13.11"	},
+	{  352,	"HCI 13.11"	},
+	{  362,	"HCI 15.0"	},
+	{  363,	"HCI 15.0"	},
+	{  364,	"HCI 15.0"	},
+	{  365,	"HCI 15.0"	},
+	{  373,	"HCI 14.7"	},
+	{  374,	"HCI 14.7"	},
+	{  379,	"HCI 15.1"	},
+	{  380,	"HCI 15.1"	},
+	{  381,	"HCI 15.1"	},
+	{  382,	"HCI 15.1"	},
+	{  392,	"HCI 15.2"	},
+	{  393,	"HCI 15.2"	},
+	{  394,	"HCI 15.2"	},
+	{  395,	"HCI 15.2"	},
+	{  436,	"HCI 16.0"	},
+	{  437,	"HCI 16.0"	},
+	{  438,	"HCI 16.0"	},
+	{  439,	"HCI 16.0"	},
+	{  443,	"HCI 15.3"	},
+	{  444,	"HCI 15.3"	},
+	{  465,	"HCI 16.1"	},
+	{  466,	"HCI 16.1"	},
+	{  467,	"HCI 16.1"	},
+	{  468,	"HCI 16.1"	},
+	{  487,	"HCI 14.8"	},
+	{  488,	"HCI 14.8"	},
+	{  492,	"HCI 16.2"	},
+	{  493,	"HCI 16.2"	},
+	{  495,	"HCI 16.2"	},
+	{  496,	"HCI 16.2"	},
+	{  502,	"HCI 16.1.1"	},
+	{  503,	"HCI 16.1.1"	},
+	{  504,	"HCI 16.1.1"	},
+	{  505,	"HCI 16.1.1"	},
+	{  506,	"HCI 16.1.2"	},
+	{  507,	"HCI 16.1.2"	},
+	{  508,	"HCI 16.1.2"	},
+	{  509,	"HCI 16.1.2"	},
+	{  516,	"HCI 16.3"	},
+	{  517,	"HCI 16.3"	},
+	{  518,	"HCI 16.3"	},
+	{  519,	"HCI 16.3"	},
+	{  523,	"HCI 16.4"	},
+	{  524,	"HCI 16.4"	},
+	{  525,	"HCI 16.4"	},
+	{  526,	"HCI 16.4"	},
+	{  553,	"HCI 15.3"	},
+	{  554,	"HCI 15.3"	},
+	{  562,	"HCI 16.5"	},
+	{  563,	"HCI 16.5"	},
+	{  564,	"HCI 16.5"	},
+	{  565,	"HCI 16.5"	},
+	{  593,	"HCI 17.0"	},
+	{  594,	"HCI 17.0"	},
+	{  595,	"HCI 17.0"	},
+	{  599,	"HCI 17.0"	},
+	{  600,	"HCI 17.0"	},
+	{  608,	"HCI 13.10.1"	},
+	{  609,	"HCI 13.10.1"	},
+	{  613,	"HCI 17.1"	},
+	{  614,	"HCI 17.1"	},
+	{  615,	"HCI 17.1"	},
+	{  616,	"HCI 17.1"	},
+	{  618,	"HCI 17.1"	},
+	{  624,	"HCI 17.2"	},
+	{  625,	"HCI 17.2"	},
+	{  626,	"HCI 17.2"	},
+	{  627,	"HCI 17.2"	},
+	{  637,	"HCI 16.6"	},
+	{  638,	"HCI 16.6"	},
+	{  639,	"HCI 16.6"	},
+	{  640,	"HCI 16.6"	},
+	{  642,	"HCI 13.10.2"	},
+	{  643,	"HCI 13.10.2"	},
+	{  644,	"HCI 13.10.3"	},
+	{  645,	"HCI 13.10.3"	},
+	{  668,	"HCI 13.10.4"	},
+	{  669,	"HCI 13.10.4"	},
+	{  681,	"HCI 16.7"	},
+	{  682,	"HCI 16.7"	},
+	{  683,	"HCI 16.7"	},
+	{  684,	"HCI 16.7"	},
+	{  704,	"HCI 16.8"	},
+	{  718,	"HCI 16.4.1"	},
+	{  719,	"HCI 16.4.1"	},
+	{  720,	"HCI 16.4.1"	},
+	{  721,	"HCI 16.4.1"	},
+	{  722,	"HCI 16.7.1"	},
+	{  723,	"HCI 16.7.1"	},
+	{  724,	"HCI 16.7.1"	},
+	{  725,	"HCI 16.7.1"	},
+	{  731,	"HCI 16.7.2"	},
+	{  732,	"HCI 16.7.2"	},
+	{  733,	"HCI 16.7.2"	},
+	{  734,	"HCI 16.7.2"	},
+	{  735,	"HCI 16.4.2"	},
+	{  736,	"HCI 16.4.2"	},
+	{  737,	"HCI 16.4.2"	},
+	{  738,	"HCI 16.4.2"	},
+	{  750,	"HCI 16.7.3"	},
+	{  751,	"HCI 16.7.3"	},
+	{  752,	"HCI 16.7.3"	},
+	{  753,	"HCI 16.7.3"	},
+	{  760,	"HCI 16.7.4"	},
+	{  761,	"HCI 16.7.4"	},
+	{  762,	"HCI 16.7.4"	},
+	{  763,	"HCI 16.7.4"	},
+	{  770,	"HCI 16.9"	},
+	{  771,	"HCI 16.9"	},
+	{  772,	"HCI 16.9"	},
+	{  773,	"HCI 16.9"	},
+	{  774,	"HCI 17.3"	},
+	{  775,	"HCI 17.3"	},
+	{  776,	"HCI 17.3"	},
+	{  777,	"HCI 17.3"	},
+	{  781,	"HCI 16.7.5"	},
+	{  786,	"HCI 16.10"	},
+	{  787,	"HCI 16.10"	},
+	{  788,	"HCI 16.10"	},
+	{  789,	"HCI 16.10"	},
+	{  791,	"HCI 16.4.3"	},
+	{  792,	"HCI 16.4.3"	},
+	{  793,	"HCI 16.4.3"	},
+	{  794,	"HCI 16.4.3"	},
+	{  798,	"HCI 16.11"	},
+	{  799,	"HCI 16.11"	},
+	{  800,	"HCI 16.11"	},
+	{  801,	"HCI 16.11"	},
+	{  806,	"HCI 16.7.5"	},
+	{  807,	"HCI 16.12"	},
+	{  808,	"HCI 16.12"	},
+	{  809,	"HCI 16.12"	},
+	{  810,	"HCI 16.12"	},
+	{  817,	"HCI 16.13"	},
+	{  818,	"HCI 16.13"	},
+	{  819,	"HCI 16.13"	},
+	{  820,	"HCI 16.13"	},
+	{  823,	"HCI 13.10.5"	},
+	{  824,	"HCI 13.10.5"	},
+	{  826,	"HCI 16.14"	},
+	{  827,	"HCI 16.14"	},
+	{  828,	"HCI 16.14"	},
+	{  829,	"HCI 16.14"	},
+	{  843,	"HCI 17.3.1"	},
+	{  856,	"HCI 17.3.2"	},
+	{  857,	"HCI 17.3.2"	},
+	{  858,	"HCI 17.3.2"	},
+	{ 1120, "HCI 17.11"	},
+	{ 1168, "HCI 18.1"	},
+	{ 1169, "HCI 18.1"	},
+	{ 1241, "HCI 18.x"	},
+	{ 1298, "HCI 18.2"	},
+	{ 1354, "HCI 18.2"	},
+	{ 1392, "HCI 18.2"	},
+	{ 1393, "HCI 18.2"	},
+	{ 1501, "HCI 18.2"	},
+	{ 1503, "HCI 18.2"	},
+	{ 1504, "HCI 18.2"	},
+	{ 1505, "HCI 18.2"	},
+	{ 1506, "HCI 18.2"	},
+	{ 1520, "HCI 18.2"	},
+	{ 1586, "HCI 18.2"	},
+	{ 1591, "HCI 18.2"	},
+	{ 1592, "HCI 18.2"	},
+	{ 1593, "HCI 18.2.1"	},
+	{ 1733, "HCI 18.3"	},
+	{ 1734, "HCI 18.3"	},
+	{ 1735, "HCI 18.3"	},
+	{ 1737, "HCI 18.3"	},
+	{ 1915, "HCI 19.2"	},
+	{ 1916, "HCI 19.2"	},
+	{ 1958, "HCI 19.2"	},
+	{ 1981, "Unified 20a"	},
+	{ 1982, "Unified 20a"	},
+	{ 1989, "HCI 18.4"	},
+	{ 2062, "Unified 20a1"	},
+	{ 2063, "Unified 20a1"	},
+	{ 2067, "Unified 18f"	},
+	{ 2068, "Unified 18f"	},
+	{ 2243, "Unified 18e"	},
+	{ 2244, "Unified 18e"	},
+	{ 2258, "Unified 20d"	},
+	{ 2259, "Unified 20d"	},
+	{ 2361, "Unified 20e"	},
+	{ 2362, "Unified 20e"	},
+	{ 2386, "Unified 21a"	},
+	{ 2387, "Unified 21a"	},
+	{ 2423, "Unified 21a"	},
+	{ 2424, "Unified 21a"	},
+	{ 2623, "Unified 21c"	},
+	{ 2624, "Unified 21c"	},
+	{ 2625, "Unified 21c"	},
+	{ 2626, "Unified 21c"	},
+	{ 2627, "Unified 21c"	},
+	{ 2628, "Unified 21c"	},
+	{ 2629, "Unified 21c"	},
+	{ 2630, "Unified 21c"	},
+	{ 2631, "Unified 21c"	},
+	{ 2632, "Unified 21c"	},
+	{ 2633, "Unified 21c"	},
+	{ 2634, "Unified 21c"	},
+	{ 2635, "Unified 21c"	},
+	{ 2636, "Unified 21c"	},
+	{ 2649, "Unified 21c"	},
+	{ 2650, "Unified 21c"	},
+	{ 2651, "Unified 21c"	},
+	{ 2652, "Unified 21c"	},
+	{ 2653, "Unified 21c"	},
+	{ 2654, "Unified 21c"	},
+	{ 2655, "Unified 21c"	},
+	{ 2656, "Unified 21c"	},
+	{ 2658, "Unified 21c"	},
+	{ 3057, "Unified 21d"	},
+	{ 3058, "Unified 21d"	},
+	{ 3059, "Unified 21d"	},
+	{ 3060, "Unified 21d"	},
+	{ 3062, "Unified 21d"	},
+	{ 3063, "Unified 21d"	},
+	{ 3064, "Unified 21d"	},
+	{ 3164, "Unified 21e"	},
+	{ 3413, "Unified 21f"	},
+	{ 3414, "Unified 21f"	},
+	{ 3415, "Unified 21f"	},
+	{ 3424, "Unified 21f"	},
+	{ 3454, "Unified 21f"	},
+	{ 3684, "Unified 21f"	},
+	{ 3764, "Unified 21f"	},
+	{ 4276, "Unified 22b"	},
+	{ 4277, "Unified 22b"	},
+	{ 4279, "Unified 22b"	},
+	{ 4281, "Unified 22b"	},
+	{ 4282, "Unified 22b"	},
+	{ 4283, "Unified 22b"	},
+	{ 4284, "Unified 22b"	},
+	{ 4285, "Unified 22b"	},
+	{ 4289, "Unified 22b"	},
+	{ 4290, "Unified 22b"	},
+	{ 4291, "Unified 22b"	},
+	{ 4292, "Unified 22b"	},
+	{ 4293, "Unified 22b"	},
+	{ 4294, "Unified 22b"	},
+	{ 4295, "Unified 22b"	},
+	{ 4363, "Unified 22c"	},
+	{ 4373, "Unified 22c"	},
+	{ 4374, "Unified 22c"	},
+	{ 4532, "Unified 22d"	},
+	{ 4533, "Unified 22d"	},
+	{ 4698, "Unified 23c"	},
+	{ 4839, "Unified 23c"	},
+	{ 4841, "Unified 23c"	},
+	{ 4866, "Unified 23c"	},
+	{ 4867, "Unified 23c"	},
+	{ 4868, "Unified 23c"	},
+	{ 4869, "Unified 23c"	},
+	{ 4870, "Unified 23c"	},
+	{ 4871, "Unified 23c"	},
+	{ 4872, "Unified 23c"	},
+	{ 4874, "Unified 23c"	},
+	{ 4875, "Unified 23c"	},
+	{ 4876, "Unified 23c"	},
+	{ 4877, "Unified 23c"	},
+	{ 2526, "Marcel 1 (2005-09-26)"	},
+	{ 2543, "Marcel 2 (2005-09-28)"	},
+	{ 2622, "Marcel 3 (2005-10-27)"	},
+	{ 3326, "Marcel 4 (2006-06-16)"	},
+	{ 3612, "Marcel 5 (2006-10-24)"	},
+	{ 4509, "Marcel 6 (2007-06-11)"	},
+	{ 5417, "Marcel 7 (2008-08-26)" },
+	{  195, "Sniff 1 (2001-11-27)"	},
+	{  220, "Sniff 2 (2002-01-03)"	},
+	{  269, "Sniff 3 (2002-02-22)"	},
+	{  270, "Sniff 4 (2002-02-26)"	},
+	{  284, "Sniff 5 (2002-03-12)"	},
+	{  292, "Sniff 6 (2002-03-20)"	},
+	{  305, "Sniff 7 (2002-04-12)"	},
+	{  306, "Sniff 8 (2002-04-12)"	},
+	{  343, "Sniff 9 (2002-05-02)"	},
+	{  346, "Sniff 10 (2002-05-03)"	},
+	{  355, "Sniff 11 (2002-05-16)"	},
+	{  256, "Sniff 11 (2002-05-16)"	},
+	{  390, "Sniff 12 (2002-06-26)"	},
+	{  450, "Sniff 13 (2002-08-16)"	},
+	{  451, "Sniff 13 (2002-08-16)"	},
+	{  533, "Sniff 14 (2002-10-11)"	},
+	{  580, "Sniff 15 (2002-11-14)"	},
+	{  623, "Sniff 16 (2002-12-12)"	},
+	{  678, "Sniff 17 (2003-01-29)"	},
+	{  847, "Sniff 18 (2003-04-17)"	},
+	{  876, "Sniff 19 (2003-06-10)"	},
+	{  997, "Sniff 22 (2003-09-05)"	},
+	{ 1027, "Sniff 23 (2003-10-03)"	},
+	{ 1029, "Sniff 24 (2003-10-03)"	},
+	{ 1112, "Sniff 25 (2003-12-03)"	},
+	{ 1113, "Sniff 25 (2003-12-03)"	},
+	{ 1133, "Sniff 26 (2003-12-18)"	},
+	{ 1134, "Sniff 26 (2003-12-18)"	},
+	{ 1223, "Sniff 27 (2004-03-08)"	},
+	{ 1224, "Sniff 27 (2004-03-08)"	},
+	{ 1319, "Sniff 31 (2004-04-22)"	},
+	{ 1320, "Sniff 31 (2004-04-22)"	},
+	{ 1427, "Sniff 34 (2004-06-16)"	},
+	{ 1508, "Sniff 35 (2004-07-19)"	},
+	{ 1509, "Sniff 35 (2004-07-19)"	},
+	{ 1587, "Sniff 36 (2004-08-18)"	},
+	{ 1588, "Sniff 36 (2004-08-18)"	},
+	{ 1641, "Sniff 37 (2004-09-16)"	},
+	{ 1642, "Sniff 37 (2004-09-16)"	},
+	{ 1699, "Sniff 38 (2004-10-07)"	},
+	{ 1700, "Sniff 38 (2004-10-07)"	},
+	{ 1752, "Sniff 39 (2004-11-02)"	},
+	{ 1753, "Sniff 39 (2004-11-02)"	},
+	{ 1759, "Sniff 40 (2004-11-03)"	},
+	{ 1760, "Sniff 40 (2004-11-03)"	},
+	{ 1761, "Sniff 40 (2004-11-03)"	},
+	{ 2009, "Sniff 41 (2005-04-06)"	},
+	{ 2010, "Sniff 41 (2005-04-06)"	},
+	{ 2011, "Sniff 41 (2005-04-06)"	},
+	{ 2016, "Sniff 42 (2005-04-11)"	},
+	{ 2017, "Sniff 42 (2005-04-11)"	},
+	{ 2018, "Sniff 42 (2005-04-11)"	},
+	{ 2023, "Sniff 43 (2005-04-14)"	},
+	{ 2024, "Sniff 43 (2005-04-14)"	},
+	{ 2025, "Sniff 43 (2005-04-14)"	},
+	{ 2032, "Sniff 44 (2005-04-18)"	},
+	{ 2033, "Sniff 44 (2005-04-18)"	},
+	{ 2034, "Sniff 44 (2005-04-18)"	},
+	{ 2288, "Sniff 45 (2005-07-08)"	},
+	{ 2289, "Sniff 45 (2005-07-08)"	},
+	{ 2290, "Sniff 45 (2005-07-08)"	},
+	{ 2388, "Sniff 46 (2005-08-17)"	},
+	{ 2389, "Sniff 46 (2005-08-17)"	},
+	{ 2390, "Sniff 46 (2005-08-17)"	},
+	{ 2869, "Sniff 47 (2006-02-15)"	},
+	{ 2870, "Sniff 47 (2006-02-15)"	},
+	{ 2871, "Sniff 47 (2006-02-15)"	},
+	{ 3214, "Sniff 48 (2006-05-16)"	},
+	{ 3215, "Sniff 48 (2006-05-16)"	},
+	{ 3216, "Sniff 48 (2006-05-16)"	},
+	{ 3356, "Sniff 49 (2006-07-17)"	},
+	{ 3529, "Sniff 50 (2006-09-21)"	},
+	{ 3546, "Sniff 51 (2006-09-29)"	},
+	{ 3683, "Sniff 52 (2006-11-03)"	},
+	{    0, }
+};
+
+char *csr_builddeftostr(uint16_t def)
+{
+	switch (def) {
+	case 0x0000:
+		return "NONE";
+	case 0x0001:
+		return "CHIP_BASE_BC01";
+	case 0x0002:
+		return "CHIP_BASE_BC02";
+	case 0x0003:
+		return "CHIP_BC01B";
+	case 0x0004:
+		return "CHIP_BC02_EXTERNAL";
+	case 0x0005:
+		return "BUILD_HCI";
+	case 0x0006:
+		return "BUILD_RFCOMM";
+	case 0x0007:
+		return "BT_VER_1_1";
+	case 0x0008:
+		return "TRANSPORT_ALL";
+	case 0x0009:
+		return "TRANSPORT_BCSP";
+	case 0x000a:
+		return "TRANSPORT_H4";
+	case 0x000b:
+		return "TRANSPORT_USB";
+	case 0x000c:
+		return "MAX_CRYPT_KEY_LEN_56";
+	case 0x000d:
+		return "MAX_CRYPT_KEY_LEN_128";
+	case 0x000e:
+		return "TRANSPORT_USER";
+	case 0x000f:
+		return "CHIP_BC02_KATO";
+	case 0x0010:
+		return "TRANSPORT_NONE";
+	case 0x0012:
+		return "REQUIRE_8MBIT";
+	case 0x0013:
+		return "RADIOTEST";
+	case 0x0014:
+		return "RADIOTEST_LITE";
+	case 0x0015:
+		return "INSTALL_FLASH";
+	case 0x0016:
+		return "INSTALL_EEPROM";
+	case 0x0017:
+		return "INSTALL_COMBO_DOT11";
+	case 0x0018:
+		return "LOWPOWER_TX";
+	case 0x0019:
+		return "TRANSPORT_TWUTL";
+	case 0x001a:
+		return "COMPILER_GCC";
+	case 0x001b:
+		return "CHIP_BC02_CLOUSEAU";
+	case 0x001c:
+		return "CHIP_BC02_TOULOUSE";
+	case 0x001d:
+		return "CHIP_BASE_BC3";
+	case 0x001e:
+		return "CHIP_BC3_NICKNACK";
+	case 0x001f:
+		return "CHIP_BC3_KALIMBA";
+	case 0x0020:
+		return "INSTALL_HCI_MODULE";
+	case 0x0021:
+		return "INSTALL_L2CAP_MODULE";
+	case 0x0022:
+		return "INSTALL_DM_MODULE";
+	case 0x0023:
+		return "INSTALL_SDP_MODULE";
+	case 0x0024:
+		return "INSTALL_RFCOMM_MODULE";
+	case 0x0025:
+		return "INSTALL_HIDIO_MODULE";
+	case 0x0026:
+		return "INSTALL_PAN_MODULE";
+	case 0x0027:
+		return "INSTALL_IPV4_MODULE";
+	case 0x0028:
+		return "INSTALL_IPV6_MODULE";
+	case 0x0029:
+		return "INSTALL_TCP_MODULE";
+	case 0x002a:
+		return "BT_VER_1_2";
+	case 0x002b:
+		return "INSTALL_UDP_MODULE";
+	case 0x002c:
+		return "REQUIRE_0_WAIT_STATES";
+	case 0x002d:
+		return "CHIP_BC3_PADDYWACK";
+	case 0x002e:
+		return "CHIP_BC4_COYOTE";
+	case 0x002f:
+		return "CHIP_BC4_ODDJOB";
+	case 0x0030:
+		return "TRANSPORT_H4DS";
+	case 0x0031:
+		return "CHIP_BASE_BC4";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+char *csr_buildidtostr(uint16_t id)
+{
+	static char str[12];
+	int i;
+
+	for (i = 0; csr_map[i].id; i++)
+		if (csr_map[i].id == id)
+			return csr_map[i].str;
+
+	snprintf(str, 11, "Build %d", id);
+	return str;
+}
+
+char *csr_chipvertostr(uint16_t ver, uint16_t rev)
+{
+	switch (ver) {
+	case 0x00:
+		return "BlueCore01a";
+	case 0x01:
+		switch (rev) {
+		case 0x64:
+			return "BlueCore01b (ES)";
+		case 0x65:
+		default:
+			return "BlueCore01b";
+		}
+	case 0x02:
+		switch (rev) {
+		case 0x89:
+			return "BlueCore02-External (ES2)";
+		case 0x8a:
+			return "BlueCore02-External";
+		case 0x28:
+			return "BlueCore02-ROM/Audio/Flash";
+		default:
+			return "BlueCore02";
+		}
+	case 0x03:
+		switch (rev) {
+		case 0x43:
+			return "BlueCore3-MM";
+		case 0x15:
+			return "BlueCore3-ROM";
+		case 0xe2:
+			return "BlueCore3-Flash";
+		case 0x26:
+			return "BlueCore4-External";
+		case 0x30:
+			return "BlueCore4-ROM";
+		default:
+			return "BlueCore3 or BlueCore4";
+		}
+	default:
+		return "Unknown";
+	}
+}
+
+char *csr_pskeytostr(uint16_t pskey)
+{
+	switch (pskey) {
+	case CSR_PSKEY_BDADDR:
+		return "Bluetooth address";
+	case CSR_PSKEY_COUNTRYCODE:
+		return "Country code";
+	case CSR_PSKEY_CLASSOFDEVICE:
+		return "Class of device";
+	case CSR_PSKEY_DEVICE_DRIFT:
+		return "Device drift";
+	case CSR_PSKEY_DEVICE_JITTER:
+		return "Device jitter";
+	case CSR_PSKEY_MAX_ACLS:
+		return "Maximum ACL links";
+	case CSR_PSKEY_MAX_SCOS:
+		return "Maximum SCO links";
+	case CSR_PSKEY_MAX_REMOTE_MASTERS:
+		return "Maximum remote masters";
+	case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY:
+		return "Support master and slave roles simultaneously";
+	case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN:
+		return "Maximum HCI ACL packet length";
+	case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN:
+		return "Maximum HCI SCO packet length";
+	case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS:
+		return "Maximum number of HCI ACL packets";
+	case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS:
+		return "Maximum number of HCI SCO packets";
+	case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK:
+		return "Flow control low water mark";
+	case CSR_PSKEY_LC_MAX_TX_POWER:
+		return "Maximum transmit power";
+	case CSR_PSKEY_TX_GAIN_RAMP:
+		return "Transmit gain ramp rate";
+	case CSR_PSKEY_LC_POWER_TABLE:
+		return "Radio power table";
+	case CSR_PSKEY_LC_PEER_POWER_PERIOD:
+		return "Peer transmit power control interval";
+	case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK:
+		return "Flow control pool low water mark";
+	case CSR_PSKEY_LC_DEFAULT_TX_POWER:
+		return "Default transmit power";
+	case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE:
+		return "RSSI at bottom of golden receive range";
+	case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK:
+		return "Combo: PIO lines and logic to disable transmit";
+	case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK:
+		return "Combo: priority activity PIO lines and logic";
+	case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE:
+		return "Combo: 802.11b channel number base PIO line";
+	case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS:
+		return "Combo: channels to block either side of 802.11b";
+	case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI:
+		return "Maximum transmit power when peer has no RSSI";
+	case CSR_PSKEY_LC_CONNECTION_RX_WINDOW:
+		return "Receive window size during connections";
+	case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE:
+		return "Combo: which TX packets shall we protect";
+	case CSR_PSKEY_LC_ENHANCED_POWER_TABLE:
+		return "Radio power table";
+	case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG:
+		return "RSSI configuration for use with wideband RSSI";
+	case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD:
+		return "Combo: How much notice will we give the Combo Card";
+	case CSR_PSKEY_BT_CLOCK_INIT:
+		return "Initial value of Bluetooth clock";
+	case CSR_PSKEY_TX_MR_MOD_DELAY:
+		return "TX Mod delay";
+	case CSR_PSKEY_RX_MR_SYNC_TIMING:
+		return "RX MR Sync Timing";
+	case CSR_PSKEY_RX_MR_SYNC_CONFIG:
+		return "RX MR Sync Configuration";
+	case CSR_PSKEY_LC_LOST_SYNC_SLOTS:
+		return "Time in ms for lost sync in low power modes";
+	case CSR_PSKEY_RX_MR_SAMP_CONFIG:
+		return "RX MR Sync Configuration";
+	case CSR_PSKEY_AGC_HYST_LEVELS:
+		return "AGC hysteresis levels";
+	case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL:
+		return "ANA_RX_LVL at low signal strengths";
+	case CSR_PSKEY_AGC_IQ_LVL_VALUES:
+		return "ANA_IQ_LVL values for AGC algorithmn";
+	case CSR_PSKEY_MR_FTRIM_OFFSET_12DB:
+		return "ANA_RX_FTRIM offset when using 12 dB IF atten ";
+	case CSR_PSKEY_MR_FTRIM_OFFSET_6DB:
+		return "ANA_RX_FTRIM offset when using 6 dB IF atten ";
+	case CSR_PSKEY_NO_CAL_ON_BOOT:
+		return "Do not calibrate radio on boot";
+	case CSR_PSKEY_RSSI_HI_TARGET:
+		return "RSSI high target";
+	case CSR_PSKEY_PREFERRED_MIN_ATTENUATION:
+		return "Preferred minimum attenuator setting";
+	case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE:
+		return "Combo: Treat all packets as high priority";
+	case CSR_PSKEY_LC_MULTISLOT_HOLDOFF:
+		return "Time till single slot packets are used for resync";
+	case CSR_PSKEY_FREE_KEY_PIGEON_HOLE:
+		return "Link key store bitfield";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR0:
+		return "Bluetooth address + link key 0";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR1:
+		return "Bluetooth address + link key 1";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR2:
+		return "Bluetooth address + link key 2";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR3:
+		return "Bluetooth address + link key 3";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR4:
+		return "Bluetooth address + link key 4";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR5:
+		return "Bluetooth address + link key 5";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR6:
+		return "Bluetooth address + link key 6";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR7:
+		return "Bluetooth address + link key 7";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR8:
+		return "Bluetooth address + link key 8";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR9:
+		return "Bluetooth address + link key 9";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR10:
+		return "Bluetooth address + link key 10";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR11:
+		return "Bluetooth address + link key 11";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR12:
+		return "Bluetooth address + link key 12";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR13:
+		return "Bluetooth address + link key 13";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR14:
+		return "Bluetooth address + link key 14";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR15:
+		return "Bluetooth address + link key 15";
+	case CSR_PSKEY_ENC_KEY_LMIN:
+		return "Minimum encryption key length";
+	case CSR_PSKEY_ENC_KEY_LMAX:
+		return "Maximum encryption key length";
+	case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES:
+		return "Local supported features block";
+	case CSR_PSKEY_LM_USE_UNIT_KEY:
+		return "Allow use of unit key for authentication?";
+	case CSR_PSKEY_HCI_NOP_DISABLE:
+		return "Disable the HCI Command_Status event on boot";
+	case CSR_PSKEY_LM_MAX_EVENT_FILTERS:
+		return "Maximum number of event filters";
+	case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST:
+		return "Allow LM to use enc_mode=2";
+	case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE:
+		return "LM sends two LMP_accepted messages in test mode";
+	case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME:
+		return "Maximum time we hold a device around page";
+	case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME:
+		return "LM period for AFH adaption";
+	case CSR_PSKEY_AFH_OPTIONS:
+		return "Options to configure AFH";
+	case CSR_PSKEY_AFH_RSSI_RUN_PERIOD:
+		return "AFH RSSI reading period";
+	case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME:
+		return "AFH good channel adding time";
+	case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL:
+		return "Complete link if acr barge-in role switch refused";
+	case CSR_PSKEY_MAX_PRIVATE_KEYS:
+		return "Max private link keys stored";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0:
+		return "Bluetooth address + link key 0";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1:
+		return "Bluetooth address + link key 1";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2:
+		return "Bluetooth address + link key 2";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3:
+		return "Bluetooth address + link key 3";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4:
+		return "Bluetooth address + link key 4";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5:
+		return "Bluetooth address + link key 5";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6:
+		return "Bluetooth address + link key 6";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7:
+		return "Bluetooth address + link key 7";
+	case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS:
+		return "Local supported commands";
+	case CSR_PSKEY_LM_MAX_ABSENCE_INDEX:
+		return "Maximum absence index allowed";
+	case CSR_PSKEY_DEVICE_NAME:
+		return "Local device's \"user friendly\" name";
+	case CSR_PSKEY_AFH_RSSI_THRESHOLD:
+		return "AFH RSSI threshold";
+	case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL:
+		return "Scan interval in slots for casual scanning";
+	case CSR_PSKEY_AFH_MIN_MAP_CHANGE:
+		return "The minimum amount to change an AFH map by";
+	case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD:
+		return "AFH RSSI reading period when in low power mode";
+	case CSR_PSKEY_HCI_LMP_LOCAL_VERSION:
+		return "The HCI and LMP version reported locally";
+	case CSR_PSKEY_LMP_REMOTE_VERSION:
+		return "The LMP version reported remotely";
+	case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER:
+		return "Maximum number of queued HCI Hardware Error Events";
+	case CSR_PSKEY_DFU_ATTRIBUTES:
+		return "DFU attributes";
+	case CSR_PSKEY_DFU_DETACH_TO:
+		return "DFU detach timeout";
+	case CSR_PSKEY_DFU_TRANSFER_SIZE:
+		return "DFU transfer size";
+	case CSR_PSKEY_DFU_ENABLE:
+		return "DFU enable";
+	case CSR_PSKEY_DFU_LIN_REG_ENABLE:
+		return "Linear Regulator enabled at boot in DFU mode";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB:
+		return "DFU encryption VM application public key MSB";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB:
+		return "DFU encryption VM application public key LSB";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH:
+		return "DFU encryption VM application M dash";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB:
+		return "DFU encryption VM application public key R2N MSB";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB:
+		return "DFU encryption VM application public key R2N LSB";
+	case CSR_PSKEY_BCSP_LM_PS_BLOCK:
+		return "BCSP link establishment block";
+	case CSR_PSKEY_HOSTIO_FC_PS_BLOCK:
+		return "HCI flow control block";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0:
+		return "Host transport channel 0 settings (BCSP ACK)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1:
+		return "Host transport channel 1 settings (BCSP-LE)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2:
+		return "Host transport channel 2 settings (BCCMD)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3:
+		return "Host transport channel 3 settings (HQ)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4:
+		return "Host transport channel 4 settings (DM)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5:
+		return "Host transport channel 5 settings (HCI CMD/EVT)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6:
+		return "Host transport channel 6 settings (HCI ACL)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7:
+		return "Host transport channel 7 settings (HCI SCO)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8:
+		return "Host transport channel 8 settings (L2CAP)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9:
+		return "Host transport channel 9 settings (RFCOMM)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10:
+		return "Host transport channel 10 settings (SDP)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11:
+		return "Host transport channel 11 settings (TEST)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12:
+		return "Host transport channel 12 settings (DFU)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13:
+		return "Host transport channel 13 settings (VM)";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14:
+		return "Host transport channel 14 settings";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15:
+		return "Host transport channel 15 settings";
+	case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT:
+		return "UART reset counter timeout";
+	case CSR_PSKEY_HOSTIO_USE_HCI_EXTN:
+		return "Use hci_extn to route non-hci channels";
+	case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC:
+		return "Use command-complete flow control for hci_extn";
+	case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE:
+		return "Maximum hci_extn payload size";
+	case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT:
+		return "BCSP link establishment conf message count";
+	case CSR_PSKEY_HOSTIO_MAP_SCO_PCM:
+		return "Map SCO over PCM";
+	case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC:
+		return "PCM interface synchronisation is difficult";
+	case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD:
+		return "Break poll period (microseconds)";
+	case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE:
+		return "Minimum SCO packet size sent to host over UART HCI";
+	case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC:
+		return "Map SCO over the built-in codec";
+	case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST:
+		return "High frequency boost for PCM when transmitting CVSD";
+	case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST:
+		return "High frequency boost for PCM when receiving CVSD";
+	case CSR_PSKEY_PCM_CONFIG32:
+		return "PCM interface settings bitfields";
+	case CSR_PSKEY_USE_OLD_BCSP_LE:
+		return "Use the old version of BCSP link establishment";
+	case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER:
+		return "CVSD uses the new filter if available";
+	case CSR_PSKEY_PCM_FORMAT:
+		return "PCM data format";
+	case CSR_PSKEY_CODEC_OUT_GAIN:
+		return "Audio output gain when using built-in codec";
+	case CSR_PSKEY_CODEC_IN_GAIN:
+		return "Audio input gain when using built-in codec";
+	case CSR_PSKEY_CODEC_PIO:
+		return "PIO to enable when built-in codec is enabled";
+	case CSR_PSKEY_PCM_LOW_JITTER_CONFIG:
+		return "PCM interface settings for low jitter master mode";
+	case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS:
+		return "Thresholds for SCO PCM buffers";
+	case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS:
+		return "Thresholds for SCO HCI buffers";
+	case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT:
+		return "Route SCO data to specified slot in pcm frame";
+	case CSR_PSKEY_UART_BAUDRATE:
+		return "UART Baud rate";
+	case CSR_PSKEY_UART_CONFIG_BCSP:
+		return "UART configuration when using BCSP";
+	case CSR_PSKEY_UART_CONFIG_H4:
+		return "UART configuration when using H4";
+	case CSR_PSKEY_UART_CONFIG_H5:
+		return "UART configuration when using H5";
+	case CSR_PSKEY_UART_CONFIG_USR:
+		return "UART configuration when under VM control";
+	case CSR_PSKEY_UART_TX_CRCS:
+		return "Use CRCs for BCSP or H5";
+	case CSR_PSKEY_UART_ACK_TIMEOUT:
+		return "Acknowledgement timeout for BCSP and H5";
+	case CSR_PSKEY_UART_TX_MAX_ATTEMPTS:
+		return "Max times to send reliable BCSP or H5 message";
+	case CSR_PSKEY_UART_TX_WINDOW_SIZE:
+		return "Transmit window size for BCSP and H5";
+	case CSR_PSKEY_UART_HOST_WAKE:
+		return "UART host wakeup";
+	case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT:
+		return "Host interface performance control.";
+	case CSR_PSKEY_PCM_ALWAYS_ENABLE:
+		return "PCM port is always enable when chip is running";
+	case CSR_PSKEY_UART_HOST_WAKE_SIGNAL:
+		return "Signal to use for uart host wakeup protocol";
+	case CSR_PSKEY_UART_CONFIG_H4DS:
+		return "UART configuration when using H4DS";
+	case CSR_PSKEY_H4DS_WAKE_DURATION:
+		return "How long to spend waking the host when using H4DS";
+	case CSR_PSKEY_H4DS_MAXWU:
+		return "Maximum number of H4DS Wake-Up messages to send";
+	case CSR_PSKEY_H4DS_LE_TIMER_PERIOD:
+		return "H4DS Link Establishment Tsync and Tconf period";
+	case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD:
+		return "H4DS Twu timer period";
+	case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD:
+		return "H4DS Tuart_idle timer period";
+	case CSR_PSKEY_ANA_FTRIM:
+		return "Crystal frequency trim";
+	case CSR_PSKEY_WD_TIMEOUT:
+		return "Watchdog timeout (microseconds)";
+	case CSR_PSKEY_WD_PERIOD:
+		return "Watchdog period (microseconds)";
+	case CSR_PSKEY_HOST_INTERFACE:
+		return "Host interface";
+	case CSR_PSKEY_HQ_HOST_TIMEOUT:
+		return "HQ host command timeout";
+	case CSR_PSKEY_HQ_ACTIVE:
+		return "Enable host query task?";
+	case CSR_PSKEY_BCCMD_SECURITY_ACTIVE:
+		return "Enable configuration security";
+	case CSR_PSKEY_ANA_FREQ:
+		return "Crystal frequency";
+	case CSR_PSKEY_PIO_PROTECT_MASK:
+		return "Access to PIO pins";
+	case CSR_PSKEY_PMALLOC_SIZES:
+		return "pmalloc sizes array";
+	case CSR_PSKEY_UART_BAUD_RATE:
+		return "UART Baud rate (pre 18)";
+	case CSR_PSKEY_UART_CONFIG:
+		return "UART configuration bitfield";
+	case CSR_PSKEY_STUB:
+		return "Stub";
+	case CSR_PSKEY_TXRX_PIO_CONTROL:
+		return "TX and RX PIO control";
+	case CSR_PSKEY_ANA_RX_LEVEL:
+		return "ANA_RX_LVL register initial value";
+	case CSR_PSKEY_ANA_RX_FTRIM:
+		return "ANA_RX_FTRIM register initial value";
+	case CSR_PSKEY_PSBC_DATA_VERSION:
+		return "Persistent store version";
+	case CSR_PSKEY_PCM0_ATTENUATION:
+		return "Volume control on PCM channel 0";
+	case CSR_PSKEY_LO_LVL_MAX:
+		return "Maximum value of LO level control register";
+	case CSR_PSKEY_LO_ADC_AMPL_MIN:
+		return "Minimum value of the LO amplitude measured on the ADC";
+	case CSR_PSKEY_LO_ADC_AMPL_MAX:
+		return "Maximum value of the LO amplitude measured on the ADC";
+	case CSR_PSKEY_IQ_TRIM_CHANNEL:
+		return "IQ calibration channel";
+	case CSR_PSKEY_IQ_TRIM_GAIN:
+		return "IQ calibration gain";
+	case CSR_PSKEY_IQ_TRIM_ENABLE:
+		return "IQ calibration enable";
+	case CSR_PSKEY_TX_OFFSET_HALF_MHZ:
+		return "Transmit offset";
+	case CSR_PSKEY_GBL_MISC_ENABLES:
+		return "Global miscellaneous hardware enables";
+	case CSR_PSKEY_UART_SLEEP_TIMEOUT:
+		return "Time in ms to deep sleep if nothing received";
+	case CSR_PSKEY_DEEP_SLEEP_STATE:
+		return "Deep sleep state usage";
+	case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM:
+		return "IQ phase enable";
+	case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD:
+		return "Time for which HCI handle is frozen after link removal";
+	case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES:
+		return "Maximum number of frozen HCI handles";
+	case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY:
+		return "Delay from freezing buf handle to deleting page table";
+	case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS:
+		return "IQ PIO settings";
+	case CSR_PSKEY_USE_EXTERNAL_CLOCK:
+		return "Device uses an external clock";
+	case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS:
+		return "Exit deep sleep on CTS line activity";
+	case CSR_PSKEY_FC_HC2H_FLUSH_DELAY:
+		return "Delay from disconnect to flushing HC->H FC tokens";
+	case CSR_PSKEY_RX_HIGHSIDE:
+		return "Disable the HIGHSIDE bit in ANA_CONFIG";
+	case CSR_PSKEY_TX_PRE_LVL:
+		return "TX pre-amplifier level";
+	case CSR_PSKEY_RX_SINGLE_ENDED:
+		return "RX single ended";
+	case CSR_PSKEY_TX_FILTER_CONFIG:
+		return "TX filter configuration";
+	case CSR_PSKEY_CLOCK_REQUEST_ENABLE:
+		return "External clock request enable";
+	case CSR_PSKEY_RX_MIN_ATTEN:
+		return "Minimum attenuation allowed for receiver";
+	case CSR_PSKEY_XTAL_TARGET_AMPLITUDE:
+		return "Crystal target amplitude";
+	case CSR_PSKEY_PCM_MIN_CPU_CLOCK:
+		return "Minimum CPU clock speed with PCM port running";
+	case CSR_PSKEY_HOST_INTERFACE_PIO_USB:
+		return "USB host interface selection PIO line";
+	case CSR_PSKEY_CPU_IDLE_MODE:
+		return "CPU idle mode when radio is active";
+	case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS:
+		return "Deep sleep clears the UART RTS line";
+	case CSR_PSKEY_RF_RESONANCE_TRIM:
+		return "Frequency trim for IQ and LNA resonant circuits";
+	case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE:
+		return "PIO line to wake the chip from deep sleep";
+	case CSR_PSKEY_DRAIN_BORE_TIMERS:
+		return "Energy consumption measurement settings";
+	case CSR_PSKEY_DRAIN_TX_POWER_BASE:
+		return "Energy consumption measurement settings";
+	case CSR_PSKEY_MODULE_ID:
+		return "Module serial number";
+	case CSR_PSKEY_MODULE_DESIGN:
+		return "Module design ID";
+	case CSR_PSKEY_MODULE_SECURITY_CODE:
+		return "Module security code";
+	case CSR_PSKEY_VM_DISABLE:
+		return "VM disable";
+	case CSR_PSKEY_MOD_MANUF0:
+		return "Module manufactuer data 0";
+	case CSR_PSKEY_MOD_MANUF1:
+		return "Module manufactuer data 1";
+	case CSR_PSKEY_MOD_MANUF2:
+		return "Module manufactuer data 2";
+	case CSR_PSKEY_MOD_MANUF3:
+		return "Module manufactuer data 3";
+	case CSR_PSKEY_MOD_MANUF4:
+		return "Module manufactuer data 4";
+	case CSR_PSKEY_MOD_MANUF5:
+		return "Module manufactuer data 5";
+	case CSR_PSKEY_MOD_MANUF6:
+		return "Module manufactuer data 6";
+	case CSR_PSKEY_MOD_MANUF7:
+		return "Module manufactuer data 7";
+	case CSR_PSKEY_MOD_MANUF8:
+		return "Module manufactuer data 8";
+	case CSR_PSKEY_MOD_MANUF9:
+		return "Module manufactuer data 9";
+	case CSR_PSKEY_DUT_VM_DISABLE:
+		return "VM disable when entering radiotest modes";
+	case CSR_PSKEY_USR0:
+		return "User configuration data 0";
+	case CSR_PSKEY_USR1:
+		return "User configuration data 1";
+	case CSR_PSKEY_USR2:
+		return "User configuration data 2";
+	case CSR_PSKEY_USR3:
+		return "User configuration data 3";
+	case CSR_PSKEY_USR4:
+		return "User configuration data 4";
+	case CSR_PSKEY_USR5:
+		return "User configuration data 5";
+	case CSR_PSKEY_USR6:
+		return "User configuration data 6";
+	case CSR_PSKEY_USR7:
+		return "User configuration data 7";
+	case CSR_PSKEY_USR8:
+		return "User configuration data 8";
+	case CSR_PSKEY_USR9:
+		return "User configuration data 9";
+	case CSR_PSKEY_USR10:
+		return "User configuration data 10";
+	case CSR_PSKEY_USR11:
+		return "User configuration data 11";
+	case CSR_PSKEY_USR12:
+		return "User configuration data 12";
+	case CSR_PSKEY_USR13:
+		return "User configuration data 13";
+	case CSR_PSKEY_USR14:
+		return "User configuration data 14";
+	case CSR_PSKEY_USR15:
+		return "User configuration data 15";
+	case CSR_PSKEY_USR16:
+		return "User configuration data 16";
+	case CSR_PSKEY_USR17:
+		return "User configuration data 17";
+	case CSR_PSKEY_USR18:
+		return "User configuration data 18";
+	case CSR_PSKEY_USR19:
+		return "User configuration data 19";
+	case CSR_PSKEY_USR20:
+		return "User configuration data 20";
+	case CSR_PSKEY_USR21:
+		return "User configuration data 21";
+	case CSR_PSKEY_USR22:
+		return "User configuration data 22";
+	case CSR_PSKEY_USR23:
+		return "User configuration data 23";
+	case CSR_PSKEY_USR24:
+		return "User configuration data 24";
+	case CSR_PSKEY_USR25:
+		return "User configuration data 25";
+	case CSR_PSKEY_USR26:
+		return "User configuration data 26";
+	case CSR_PSKEY_USR27:
+		return "User configuration data 27";
+	case CSR_PSKEY_USR28:
+		return "User configuration data 28";
+	case CSR_PSKEY_USR29:
+		return "User configuration data 29";
+	case CSR_PSKEY_USR30:
+		return "User configuration data 30";
+	case CSR_PSKEY_USR31:
+		return "User configuration data 31";
+	case CSR_PSKEY_USR32:
+		return "User configuration data 32";
+	case CSR_PSKEY_USR33:
+		return "User configuration data 33";
+	case CSR_PSKEY_USR34:
+		return "User configuration data 34";
+	case CSR_PSKEY_USR35:
+		return "User configuration data 35";
+	case CSR_PSKEY_USR36:
+		return "User configuration data 36";
+	case CSR_PSKEY_USR37:
+		return "User configuration data 37";
+	case CSR_PSKEY_USR38:
+		return "User configuration data 38";
+	case CSR_PSKEY_USR39:
+		return "User configuration data 39";
+	case CSR_PSKEY_USR40:
+		return "User configuration data 40";
+	case CSR_PSKEY_USR41:
+		return "User configuration data 41";
+	case CSR_PSKEY_USR42:
+		return "User configuration data 42";
+	case CSR_PSKEY_USR43:
+		return "User configuration data 43";
+	case CSR_PSKEY_USR44:
+		return "User configuration data 44";
+	case CSR_PSKEY_USR45:
+		return "User configuration data 45";
+	case CSR_PSKEY_USR46:
+		return "User configuration data 46";
+	case CSR_PSKEY_USR47:
+		return "User configuration data 47";
+	case CSR_PSKEY_USR48:
+		return "User configuration data 48";
+	case CSR_PSKEY_USR49:
+		return "User configuration data 49";
+	case CSR_PSKEY_USB_VERSION:
+		return "USB specification version number";
+	case CSR_PSKEY_USB_DEVICE_CLASS_CODES:
+		return "USB device class codes";
+	case CSR_PSKEY_USB_VENDOR_ID:
+		return "USB vendor identifier";
+	case CSR_PSKEY_USB_PRODUCT_ID:
+		return "USB product identifier";
+	case CSR_PSKEY_USB_MANUF_STRING:
+		return "USB manufacturer string";
+	case CSR_PSKEY_USB_PRODUCT_STRING:
+		return "USB product string";
+	case CSR_PSKEY_USB_SERIAL_NUMBER_STRING:
+		return "USB serial number string";
+	case CSR_PSKEY_USB_CONFIG_STRING:
+		return "USB configuration string";
+	case CSR_PSKEY_USB_ATTRIBUTES:
+		return "USB attributes bitmap";
+	case CSR_PSKEY_USB_MAX_POWER:
+		return "USB device maximum power consumption";
+	case CSR_PSKEY_USB_BT_IF_CLASS_CODES:
+		return "USB Bluetooth interface class codes";
+	case CSR_PSKEY_USB_LANGID:
+		return "USB language strings supported";
+	case CSR_PSKEY_USB_DFU_CLASS_CODES:
+		return "USB DFU class codes block";
+	case CSR_PSKEY_USB_DFU_PRODUCT_ID:
+		return "USB DFU product ID";
+	case CSR_PSKEY_USB_PIO_DETACH:
+		return "USB detach/attach PIO line";
+	case CSR_PSKEY_USB_PIO_WAKEUP:
+		return "USB wakeup PIO line";
+	case CSR_PSKEY_USB_PIO_PULLUP:
+		return "USB D+ pullup PIO line";
+	case CSR_PSKEY_USB_PIO_VBUS:
+		return "USB VBus detection PIO Line";
+	case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT:
+		return "Timeout for assertion of USB PIO wake signal";
+	case CSR_PSKEY_USB_PIO_RESUME:
+		return "PIO signal used in place of bus resume";
+	case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES:
+		return "USB Bluetooth SCO interface class codes";
+	case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL:
+		return "USB PIO levels to set when suspended";
+	case CSR_PSKEY_USB_SUSPEND_PIO_DIR:
+		return "USB PIO I/O directions to set when suspended";
+	case CSR_PSKEY_USB_SUSPEND_PIO_MASK:
+		return "USB PIO lines to be set forcibly in suspend";
+	case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE:
+		return "The maxmimum packet size for USB endpoint 0";
+	case CSR_PSKEY_USB_CONFIG:
+		return "USB config params for new chips (>bc2)";
+	case CSR_PSKEY_RADIOTEST_ATTEN_INIT:
+		return "Radio test initial attenuator";
+	case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME:
+		return "IQ first calibration period in test";
+	case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME:
+		return "IQ subsequent calibration period in test";
+	case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE:
+		return "LO_LVL calibration enable";
+	case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION:
+		return "Disable modulation during radiotest transmissions";
+	case CSR_PSKEY_RFCOMM_FCON_THRESHOLD:
+		return "RFCOMM aggregate flow control on threshold";
+	case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD:
+		return "RFCOMM aggregate flow control off threshold";
+	case CSR_PSKEY_IPV6_STATIC_ADDR:
+		return "Static IPv6 address";
+	case CSR_PSKEY_IPV4_STATIC_ADDR:
+		return "Static IPv4 address";
+	case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN:
+		return "Static IPv6 prefix length";
+	case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR:
+		return "Static IPv6 router address";
+	case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK:
+		return "Static IPv4 subnet mask";
+	case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR:
+		return "Static IPv4 router address";
+	case CSR_PSKEY_MDNS_NAME:
+		return "Multicast DNS name";
+	case CSR_PSKEY_FIXED_PIN:
+		return "Fixed PIN";
+	case CSR_PSKEY_MDNS_PORT:
+		return "Multicast DNS port";
+	case CSR_PSKEY_MDNS_TTL:
+		return "Multicast DNS TTL";
+	case CSR_PSKEY_MDNS_IPV4_ADDR:
+		return "Multicast DNS IPv4 address";
+	case CSR_PSKEY_ARP_CACHE_TIMEOUT:
+		return "ARP cache timeout";
+	case CSR_PSKEY_HFP_POWER_TABLE:
+		return "HFP power table";
+	case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS:
+		return "Energy consumption estimation timer counters";
+	case CSR_PSKEY_DRAIN_BORE_COUNTERS:
+		return "Energy consumption estimation counters";
+	case CSR_PSKEY_LOOP_FILTER_TRIM:
+		return "Trim value to optimise loop filter";
+	case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK:
+		return "Energy consumption estimation current peak";
+	case CSR_PSKEY_VM_E2_CACHE_LIMIT:
+		return "Maximum RAM for caching EEPROM VM application";
+	case CSR_PSKEY_FORCE_16MHZ_REF_PIO:
+		return "PIO line to force 16 MHz reference to be assumed";
+	case CSR_PSKEY_CDMA_LO_REF_LIMITS:
+		return "Local oscillator frequency reference limits for CDMA";
+	case CSR_PSKEY_CDMA_LO_ERROR_LIMITS:
+		return "Local oscillator frequency error limits for CDMA";
+	case CSR_PSKEY_CLOCK_STARTUP_DELAY:
+		return "Clock startup delay in milliseconds";
+	case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR:
+		return "Deep sleep clock correction factor";
+	case CSR_PSKEY_TEMPERATURE_CALIBRATION:
+		return "Temperature in deg C for a given internal setting";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA:
+		return "Temperature for given internal PA adjustment";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL:
+		return "Temperature for a given TX_PRE_LVL adjustment";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB:
+		return "Temperature for a given TX_BB adjustment";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM:
+		return "Temperature for given crystal trim adjustment";
+	case CSR_PSKEY_TEST_DELTA_OFFSET:
+		return "Frequency offset applied to synthesiser in test mode";
+	case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET:
+		return "Receiver dynamic level offset depending on channel";
+	case CSR_PSKEY_TEST_FORCE_OFFSET:
+		return "Force use of exact value in PSKEY_TEST_DELTA_OFFSET";
+	case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS:
+		return "Trap bad division ratios in radio frequency tables";
+	case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS:
+		return "LO frequency reference limits for CDMA in radiotest";
+	case CSR_PSKEY_INITIAL_BOOTMODE:
+		return "Initial device bootmode";
+	case CSR_PSKEY_ONCHIP_HCI_CLIENT:
+		return "HCI traffic routed internally";
+	case CSR_PSKEY_RX_ATTEN_BACKOFF:
+		return "Receiver attenuation back-off";
+	case CSR_PSKEY_RX_ATTEN_UPDATE_RATE:
+		return "Receiver attenuation update rate";
+	case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS:
+		return "Local oscillator tuning voltage limits for tx and rx";
+	case CSR_PSKEY_MIN_WAIT_STATES:
+		return "Flash wait state indicator";
+	case CSR_PSKEY_RSSI_CORRECTION:
+		return "RSSI correction factor.";
+	case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT:
+		return "Scheduler performance control.";
+	case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK:
+		return "Deep sleep uses external 32 kHz clock source";
+	case CSR_PSKEY_TRIM_RADIO_FILTERS:
+		return "Trim rx and tx radio filters if true.";
+	case CSR_PSKEY_TRANSMIT_OFFSET:
+		return "Transmit offset in units of 62.5 kHz";
+	case CSR_PSKEY_USB_VM_CONTROL:
+		return "VM application will supply USB descriptors";
+	case CSR_PSKEY_MR_ANA_RX_FTRIM:
+		return "Medium rate value for the ANA_RX_FTRIM register";
+	case CSR_PSKEY_I2C_CONFIG:
+		return "I2C configuration";
+	case CSR_PSKEY_IQ_LVL_RX:
+		return "IQ demand level for reception";
+	case CSR_PSKEY_MR_TX_FILTER_CONFIG:
+		return "TX filter configuration used for enhanced data rate";
+	case CSR_PSKEY_MR_TX_CONFIG2:
+		return "TX filter configuration used for enhanced data rate";
+	case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET:
+		return "Don't reset bootmode if USB host resets";
+	case CSR_PSKEY_LC_USE_THROTTLING:
+		return "Adjust packet selection on packet error rate";
+	case CSR_PSKEY_CHARGER_TRIM:
+		return "Trim value for the current charger";
+	case CSR_PSKEY_CLOCK_REQUEST_FEATURES:
+		return "Clock request is tristated if enabled";
+	case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1:
+		return "Transmit offset / 62.5 kHz for class 1 radios";
+	case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO:
+		return "PIO line asserted in class1 operation to avoid PA";
+	case CSR_PSKEY_MR_PIO_CONFIG:
+		return "PIO line asserted in class1 operation to avoid PA";
+	case CSR_PSKEY_UART_CONFIG2:
+		return "The UART Sampling point";
+	case CSR_PSKEY_CLASS1_IQ_LVL:
+		return "IQ demand level for class 1 power level";
+	case CSR_PSKEY_CLASS1_TX_CONFIG2:
+		return "TX filter configuration used for class 1 tx power";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1:
+		return "Temperature for given internal PA adjustment";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1:
+		return "Temperature for given internal PA adjustment";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR:
+		return "Temperature adjustment for TX_PRE_LVL in EDR";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER:
+		return "Temperature for a given TX_BB in EDR header";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD:
+		return "Temperature for a given TX_BB in EDR payload";
+	case CSR_PSKEY_RX_MR_EQ_TAPS:
+		return "Adjust receiver configuration for EDR";
+	case CSR_PSKEY_TX_PRE_LVL_CLASS1:
+		return "TX pre-amplifier level in class 1 operation";
+	case CSR_PSKEY_ANALOGUE_ATTENUATOR:
+		return "TX analogue attenuator setting";
+	case CSR_PSKEY_MR_RX_FILTER_TRIM:
+		return "Trim for receiver used in EDR.";
+	case CSR_PSKEY_MR_RX_FILTER_RESPONSE:
+		return "Filter response for receiver used in EDR.";
+	case CSR_PSKEY_PIO_WAKEUP_STATE:
+		return "PIO deep sleep wake up state ";
+	case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP:
+		return "TX IF atten off temperature when using EDR.";
+	case CSR_PSKEY_LO_DIV_LATCH_BYPASS:
+		return "Bypass latch for LO dividers";
+	case CSR_PSKEY_LO_VCO_STANDBY:
+		return "Use standby mode for the LO VCO";
+	case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT:
+		return "Slow clock sampling filter constant";
+	case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER:
+		return "Slow clock filter fractional threshold";
+	case CSR_PSKEY_USB_ATTRIBUTES_POWER:
+		return "USB self powered";
+	case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP:
+		return "USB responds to wake-up";
+	case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT:
+		return "DFU manifestation tolerant";
+	case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD:
+		return "DFU can upload";
+	case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD:
+		return "DFU can download";
+	case CSR_PSKEY_UART_CONFIG_STOP_BITS:
+		return "UART: stop bits";
+	case CSR_PSKEY_UART_CONFIG_PARITY_BIT:
+		return "UART: parity bit";
+	case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN:
+		return "UART: hardware flow control";
+	case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN:
+		return "UART: RTS auto-enabled";
+	case CSR_PSKEY_UART_CONFIG_RTS:
+		return "UART: RTS asserted";
+	case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN:
+		return "UART: TX zero enable";
+	case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN:
+		return "UART: enable BCSP-specific hardware";
+	case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY:
+		return "UART: RX rate delay";
+	case CSR_PSKEY_UART_SEQ_TIMEOUT:
+		return "UART: BCSP ack timeout";
+	case CSR_PSKEY_UART_SEQ_RETRIES:
+		return "UART: retry limit in sequencing layer";
+	case CSR_PSKEY_UART_SEQ_WINSIZE:
+		return "UART: BCSP transmit window size";
+	case CSR_PSKEY_UART_USE_CRC_ON_TX:
+		return "UART: use BCSP CRCs";
+	case CSR_PSKEY_UART_HOST_INITIAL_STATE:
+		return "UART: initial host state";
+	case CSR_PSKEY_UART_HOST_ATTENTION_SPAN:
+		return "UART: host attention span";
+	case CSR_PSKEY_UART_HOST_WAKEUP_TIME:
+		return "UART: host wakeup time";
+	case CSR_PSKEY_UART_HOST_WAKEUP_WAIT:
+		return "UART: host wakeup wait";
+	case CSR_PSKEY_BCSP_LM_MODE:
+		return "BCSP link establishment mode";
+	case CSR_PSKEY_BCSP_LM_SYNC_RETRIES:
+		return "BCSP link establishment sync retries";
+	case CSR_PSKEY_BCSP_LM_TSHY:
+		return "BCSP link establishment Tshy";
+	case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS:
+		return "DFU mode UART: stop bits";
+	case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT:
+		return "DFU mode UART: parity bit";
+	case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN:
+		return "DFU mode UART: hardware flow control";
+	case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN:
+		return "DFU mode UART: RTS auto-enabled";
+	case CSR_PSKEY_UART_DFU_CONFIG_RTS:
+		return "DFU mode UART: RTS asserted";
+	case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN:
+		return "DFU mode UART: TX zero enable";
+	case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN:
+		return "DFU mode UART: enable BCSP-specific hardware";
+	case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY:
+		return "DFU mode UART: RX rate delay";
+	case CSR_PSKEY_AMUX_AIO0:
+		return "Multiplexer for AIO 0";
+	case CSR_PSKEY_AMUX_AIO1:
+		return "Multiplexer for AIO 1";
+	case CSR_PSKEY_AMUX_AIO2:
+		return "Multiplexer for AIO 2";
+	case CSR_PSKEY_AMUX_AIO3:
+		return "Multiplexer for AIO 3";
+	case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED:
+		return "Local Name (simplified)";
+	case CSR_PSKEY_EXTENDED_STUB:
+		return "Extended stub";
+	default:
+		return "Unknown";
+	}
+}
+
+char *csr_pskeytoval(uint16_t pskey)
+{
+	switch (pskey) {
+	case CSR_PSKEY_BDADDR:
+		return "BDADDR";
+	case CSR_PSKEY_COUNTRYCODE:
+		return "COUNTRYCODE";
+	case CSR_PSKEY_CLASSOFDEVICE:
+		return "CLASSOFDEVICE";
+	case CSR_PSKEY_DEVICE_DRIFT:
+		return "DEVICE_DRIFT";
+	case CSR_PSKEY_DEVICE_JITTER:
+		return "DEVICE_JITTER";
+	case CSR_PSKEY_MAX_ACLS:
+		return "MAX_ACLS";
+	case CSR_PSKEY_MAX_SCOS:
+		return "MAX_SCOS";
+	case CSR_PSKEY_MAX_REMOTE_MASTERS:
+		return "MAX_REMOTE_MASTERS";
+	case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY:
+		return "ENABLE_MASTERY_WITH_SLAVERY";
+	case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN:
+		return "H_HC_FC_MAX_ACL_PKT_LEN";
+	case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN:
+		return "H_HC_FC_MAX_SCO_PKT_LEN";
+	case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS:
+		return "H_HC_FC_MAX_ACL_PKTS";
+	case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS:
+		return "H_HC_FC_MAX_SCO_PKTS";
+	case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK:
+		return "LC_FC_BUFFER_LOW_WATER_MARK";
+	case CSR_PSKEY_LC_MAX_TX_POWER:
+		return "LC_MAX_TX_POWER";
+	case CSR_PSKEY_TX_GAIN_RAMP:
+		return "TX_GAIN_RAMP";
+	case CSR_PSKEY_LC_POWER_TABLE:
+		return "LC_POWER_TABLE";
+	case CSR_PSKEY_LC_PEER_POWER_PERIOD:
+		return "LC_PEER_POWER_PERIOD";
+	case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK:
+		return "LC_FC_POOLS_LOW_WATER_MARK";
+	case CSR_PSKEY_LC_DEFAULT_TX_POWER:
+		return "LC_DEFAULT_TX_POWER";
+	case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE:
+		return "LC_RSSI_GOLDEN_RANGE";
+	case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK:
+		return "LC_COMBO_DISABLE_PIO_MASK";
+	case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK:
+		return "LC_COMBO_PRIORITY_PIO_MASK";
+	case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE:
+		return "LC_COMBO_DOT11_CHANNEL_PIO_BASE";
+	case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS:
+		return "LC_COMBO_DOT11_BLOCK_CHANNELS";
+	case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI:
+		return "LC_MAX_TX_POWER_NO_RSSI";
+	case CSR_PSKEY_LC_CONNECTION_RX_WINDOW:
+		return "LC_CONNECTION_RX_WINDOW";
+	case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE:
+		return "LC_COMBO_DOT11_TX_PROTECTION_MODE";
+	case CSR_PSKEY_LC_ENHANCED_POWER_TABLE:
+		return "LC_ENHANCED_POWER_TABLE";
+	case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG:
+		return "LC_WIDEBAND_RSSI_CONFIG";
+	case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD:
+		return "LC_COMBO_DOT11_PRIORITY_LEAD";
+	case CSR_PSKEY_BT_CLOCK_INIT:
+		return "BT_CLOCK_INIT";
+	case CSR_PSKEY_TX_MR_MOD_DELAY:
+		return "TX_MR_MOD_DELAY";
+	case CSR_PSKEY_RX_MR_SYNC_TIMING:
+		return "RX_MR_SYNC_TIMING";
+	case CSR_PSKEY_RX_MR_SYNC_CONFIG:
+		return "RX_MR_SYNC_CONFIG";
+	case CSR_PSKEY_LC_LOST_SYNC_SLOTS:
+		return "LC_LOST_SYNC_SLOTS";
+	case CSR_PSKEY_RX_MR_SAMP_CONFIG:
+		return "RX_MR_SAMP_CONFIG";
+	case CSR_PSKEY_AGC_HYST_LEVELS:
+		return "AGC_HYST_LEVELS";
+	case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL:
+		return "RX_LEVEL_LOW_SIGNAL";
+	case CSR_PSKEY_AGC_IQ_LVL_VALUES:
+		return "AGC_IQ_LVL_VALUES";
+	case CSR_PSKEY_MR_FTRIM_OFFSET_12DB:
+		return "MR_FTRIM_OFFSET_12DB";
+	case CSR_PSKEY_MR_FTRIM_OFFSET_6DB:
+		return "MR_FTRIM_OFFSET_6DB";
+	case CSR_PSKEY_NO_CAL_ON_BOOT:
+		return "NO_CAL_ON_BOOT";
+	case CSR_PSKEY_RSSI_HI_TARGET:
+		return "RSSI_HI_TARGET";
+	case CSR_PSKEY_PREFERRED_MIN_ATTENUATION:
+		return "PREFERRED_MIN_ATTENUATION";
+	case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE:
+		return "LC_COMBO_DOT11_PRIORITY_OVERRIDE";
+	case CSR_PSKEY_LC_MULTISLOT_HOLDOFF:
+		return "LC_MULTISLOT_HOLDOFF";
+	case CSR_PSKEY_FREE_KEY_PIGEON_HOLE:
+		return "FREE_KEY_PIGEON_HOLE";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR0:
+		return "LINK_KEY_BD_ADDR0";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR1:
+		return "LINK_KEY_BD_ADDR1";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR2:
+		return "LINK_KEY_BD_ADDR2";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR3:
+		return "LINK_KEY_BD_ADDR3";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR4:
+		return "LINK_KEY_BD_ADDR4";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR5:
+		return "LINK_KEY_BD_ADDR5";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR6:
+		return "LINK_KEY_BD_ADDR6";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR7:
+		return "LINK_KEY_BD_ADDR7";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR8:
+		return "LINK_KEY_BD_ADDR8";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR9:
+		return "LINK_KEY_BD_ADDR9";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR10:
+		return "LINK_KEY_BD_ADDR10";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR11:
+		return "LINK_KEY_BD_ADDR11";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR12:
+		return "LINK_KEY_BD_ADDR12";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR13:
+		return "LINK_KEY_BD_ADDR13";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR14:
+		return "LINK_KEY_BD_ADDR14";
+	case CSR_PSKEY_LINK_KEY_BD_ADDR15:
+		return "LINK_KEY_BD_ADDR15";
+	case CSR_PSKEY_ENC_KEY_LMIN:
+		return "ENC_KEY_LMIN";
+	case CSR_PSKEY_ENC_KEY_LMAX:
+		return "ENC_KEY_LMAX";
+	case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES:
+		return "LOCAL_SUPPORTED_FEATURES";
+	case CSR_PSKEY_LM_USE_UNIT_KEY:
+		return "LM_USE_UNIT_KEY";
+	case CSR_PSKEY_HCI_NOP_DISABLE:
+		return "HCI_NOP_DISABLE";
+	case CSR_PSKEY_LM_MAX_EVENT_FILTERS:
+		return "LM_MAX_EVENT_FILTERS";
+	case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST:
+		return "LM_USE_ENC_MODE_BROADCAST";
+	case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE:
+		return "LM_TEST_SEND_ACCEPTED_TWICE";
+	case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME:
+		return "LM_MAX_PAGE_HOLD_TIME";
+	case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME:
+		return "AFH_ADAPTATION_RESPONSE_TIME";
+	case CSR_PSKEY_AFH_OPTIONS:
+		return "AFH_OPTIONS";
+	case CSR_PSKEY_AFH_RSSI_RUN_PERIOD:
+		return "AFH_RSSI_RUN_PERIOD";
+	case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME:
+		return "AFH_REENABLE_CHANNEL_TIME";
+	case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL:
+		return "NO_DROP_ON_ACR_MS_FAIL";
+	case CSR_PSKEY_MAX_PRIVATE_KEYS:
+		return "MAX_PRIVATE_KEYS";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0:
+		return "PRIVATE_LINK_KEY_BD_ADDR0";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1:
+		return "PRIVATE_LINK_KEY_BD_ADDR1";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2:
+		return "PRIVATE_LINK_KEY_BD_ADDR2";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3:
+		return "PRIVATE_LINK_KEY_BD_ADDR3";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4:
+		return "PRIVATE_LINK_KEY_BD_ADDR4";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5:
+		return "PRIVATE_LINK_KEY_BD_ADDR5";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6:
+		return "PRIVATE_LINK_KEY_BD_ADDR6";
+	case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7:
+		return "PRIVATE_LINK_KEY_BD_ADDR7";
+	case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS:
+		return "LOCAL_SUPPORTED_COMMANDS";
+	case CSR_PSKEY_LM_MAX_ABSENCE_INDEX:
+		return "LM_MAX_ABSENCE_INDEX";
+	case CSR_PSKEY_DEVICE_NAME:
+		return "DEVICE_NAME";
+	case CSR_PSKEY_AFH_RSSI_THRESHOLD:
+		return "AFH_RSSI_THRESHOLD";
+	case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL:
+		return "LM_CASUAL_SCAN_INTERVAL";
+	case CSR_PSKEY_AFH_MIN_MAP_CHANGE:
+		return "AFH_MIN_MAP_CHANGE";
+	case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD:
+		return "AFH_RSSI_LP_RUN_PERIOD";
+	case CSR_PSKEY_HCI_LMP_LOCAL_VERSION:
+		return "HCI_LMP_LOCAL_VERSION";
+	case CSR_PSKEY_LMP_REMOTE_VERSION:
+		return "LMP_REMOTE_VERSION";
+	case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER:
+		return "HOLD_ERROR_MESSAGE_NUMBER";
+	case CSR_PSKEY_DFU_ATTRIBUTES:
+		return "DFU_ATTRIBUTES";
+	case CSR_PSKEY_DFU_DETACH_TO:
+		return "DFU_DETACH_TO";
+	case CSR_PSKEY_DFU_TRANSFER_SIZE:
+		return "DFU_TRANSFER_SIZE";
+	case CSR_PSKEY_DFU_ENABLE:
+		return "DFU_ENABLE";
+	case CSR_PSKEY_DFU_LIN_REG_ENABLE:
+		return "DFU_LIN_REG_ENABLE";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB:
+		return "DFUENC_VMAPP_PK_MODULUS_MSB";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB:
+		return "DFUENC_VMAPP_PK_MODULUS_LSB";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH:
+		return "DFUENC_VMAPP_PK_M_DASH";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB:
+		return "DFUENC_VMAPP_PK_R2N_MSB";
+	case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB:
+		return "DFUENC_VMAPP_PK_R2N_LSB";
+	case CSR_PSKEY_BCSP_LM_PS_BLOCK:
+		return "BCSP_LM_PS_BLOCK";
+	case CSR_PSKEY_HOSTIO_FC_PS_BLOCK:
+		return "HOSTIO_FC_PS_BLOCK";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0:
+		return "HOSTIO_PROTOCOL_INFO0";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1:
+		return "HOSTIO_PROTOCOL_INFO1";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2:
+		return "HOSTIO_PROTOCOL_INFO2";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3:
+		return "HOSTIO_PROTOCOL_INFO3";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4:
+		return "HOSTIO_PROTOCOL_INFO4";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5:
+		return "HOSTIO_PROTOCOL_INFO5";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6:
+		return "HOSTIO_PROTOCOL_INFO6";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7:
+		return "HOSTIO_PROTOCOL_INFO7";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8:
+		return "HOSTIO_PROTOCOL_INFO8";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9:
+		return "HOSTIO_PROTOCOL_INFO9";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10:
+		return "HOSTIO_PROTOCOL_INFO10";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11:
+		return "HOSTIO_PROTOCOL_INFO11";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12:
+		return "HOSTIO_PROTOCOL_INFO12";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13:
+		return "HOSTIO_PROTOCOL_INFO13";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14:
+		return "HOSTIO_PROTOCOL_INFO14";
+	case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15:
+		return "HOSTIO_PROTOCOL_INFO15";
+	case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT:
+		return "HOSTIO_UART_RESET_TIMEOUT";
+	case CSR_PSKEY_HOSTIO_USE_HCI_EXTN:
+		return "HOSTIO_USE_HCI_EXTN";
+	case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC:
+		return "HOSTIO_USE_HCI_EXTN_CCFC";
+	case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE:
+		return "HOSTIO_HCI_EXTN_PAYLOAD_SIZE";
+	case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT:
+		return "BCSP_LM_CNF_CNT_LIMIT";
+	case CSR_PSKEY_HOSTIO_MAP_SCO_PCM:
+		return "HOSTIO_MAP_SCO_PCM";
+	case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC:
+		return "HOSTIO_AWKWARD_PCM_SYNC";
+	case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD:
+		return "HOSTIO_BREAK_POLL_PERIOD";
+	case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE:
+		return "HOSTIO_MIN_UART_HCI_SCO_SIZE";
+	case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC:
+		return "HOSTIO_MAP_SCO_CODEC";
+	case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST:
+		return "PCM_CVSD_TX_HI_FREQ_BOOST";
+	case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST:
+		return "PCM_CVSD_RX_HI_FREQ_BOOST";
+	case CSR_PSKEY_PCM_CONFIG32:
+		return "PCM_CONFIG32";
+	case CSR_PSKEY_USE_OLD_BCSP_LE:
+		return "USE_OLD_BCSP_LE";
+	case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER:
+		return "PCM_CVSD_USE_NEW_FILTER";
+	case CSR_PSKEY_PCM_FORMAT:
+		return "PCM_FORMAT";
+	case CSR_PSKEY_CODEC_OUT_GAIN:
+		return "CODEC_OUT_GAIN";
+	case CSR_PSKEY_CODEC_IN_GAIN:
+		return "CODEC_IN_GAIN";
+	case CSR_PSKEY_CODEC_PIO:
+		return "CODEC_PIO";
+	case CSR_PSKEY_PCM_LOW_JITTER_CONFIG:
+		return "PCM_LOW_JITTER_CONFIG";
+	case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS:
+		return "HOSTIO_SCO_PCM_THRESHOLDS";
+	case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS:
+		return "HOSTIO_SCO_HCI_THRESHOLDS";
+	case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT:
+		return "HOSTIO_MAP_SCO_PCM_SLOT";
+	case CSR_PSKEY_UART_BAUDRATE:
+		return "UART_BAUDRATE";
+	case CSR_PSKEY_UART_CONFIG_BCSP:
+		return "UART_CONFIG_BCSP";
+	case CSR_PSKEY_UART_CONFIG_H4:
+		return "UART_CONFIG_H4";
+	case CSR_PSKEY_UART_CONFIG_H5:
+		return "UART_CONFIG_H5";
+	case CSR_PSKEY_UART_CONFIG_USR:
+		return "UART_CONFIG_USR";
+	case CSR_PSKEY_UART_TX_CRCS:
+		return "UART_TX_CRCS";
+	case CSR_PSKEY_UART_ACK_TIMEOUT:
+		return "UART_ACK_TIMEOUT";
+	case CSR_PSKEY_UART_TX_MAX_ATTEMPTS:
+		return "UART_TX_MAX_ATTEMPTS";
+	case CSR_PSKEY_UART_TX_WINDOW_SIZE:
+		return "UART_TX_WINDOW_SIZE";
+	case CSR_PSKEY_UART_HOST_WAKE:
+		return "UART_HOST_WAKE";
+	case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT:
+		return "HOSTIO_THROTTLE_TIMEOUT";
+	case CSR_PSKEY_PCM_ALWAYS_ENABLE:
+		return "PCM_ALWAYS_ENABLE";
+	case CSR_PSKEY_UART_HOST_WAKE_SIGNAL:
+		return "UART_HOST_WAKE_SIGNAL";
+	case CSR_PSKEY_UART_CONFIG_H4DS:
+		return "UART_CONFIG_H4DS";
+	case CSR_PSKEY_H4DS_WAKE_DURATION:
+		return "H4DS_WAKE_DURATION";
+	case CSR_PSKEY_H4DS_MAXWU:
+		return "H4DS_MAXWU";
+	case CSR_PSKEY_H4DS_LE_TIMER_PERIOD:
+		return "H4DS_LE_TIMER_PERIOD";
+	case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD:
+		return "H4DS_TWU_TIMER_PERIOD";
+	case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD:
+		return "H4DS_UART_IDLE_TIMER_PERIOD";
+	case CSR_PSKEY_ANA_FTRIM:
+		return "ANA_FTRIM";
+	case CSR_PSKEY_WD_TIMEOUT:
+		return "WD_TIMEOUT";
+	case CSR_PSKEY_WD_PERIOD:
+		return "WD_PERIOD";
+	case CSR_PSKEY_HOST_INTERFACE:
+		return "HOST_INTERFACE";
+	case CSR_PSKEY_HQ_HOST_TIMEOUT:
+		return "HQ_HOST_TIMEOUT";
+	case CSR_PSKEY_HQ_ACTIVE:
+		return "HQ_ACTIVE";
+	case CSR_PSKEY_BCCMD_SECURITY_ACTIVE:
+		return "BCCMD_SECURITY_ACTIVE";
+	case CSR_PSKEY_ANA_FREQ:
+		return "ANA_FREQ";
+	case CSR_PSKEY_PIO_PROTECT_MASK:
+		return "PIO_PROTECT_MASK";
+	case CSR_PSKEY_PMALLOC_SIZES:
+		return "PMALLOC_SIZES";
+	case CSR_PSKEY_UART_BAUD_RATE:
+		return "UART_BAUD_RATE";
+	case CSR_PSKEY_UART_CONFIG:
+		return "UART_CONFIG";
+	case CSR_PSKEY_STUB:
+		return "STUB";
+	case CSR_PSKEY_TXRX_PIO_CONTROL:
+		return "TXRX_PIO_CONTROL";
+	case CSR_PSKEY_ANA_RX_LEVEL:
+		return "ANA_RX_LEVEL";
+	case CSR_PSKEY_ANA_RX_FTRIM:
+		return "ANA_RX_FTRIM";
+	case CSR_PSKEY_PSBC_DATA_VERSION:
+		return "PSBC_DATA_VERSION";
+	case CSR_PSKEY_PCM0_ATTENUATION:
+		return "PCM0_ATTENUATION";
+	case CSR_PSKEY_LO_LVL_MAX:
+		return "LO_LVL_MAX";
+	case CSR_PSKEY_LO_ADC_AMPL_MIN:
+		return "LO_ADC_AMPL_MIN";
+	case CSR_PSKEY_LO_ADC_AMPL_MAX:
+		return "LO_ADC_AMPL_MAX";
+	case CSR_PSKEY_IQ_TRIM_CHANNEL:
+		return "IQ_TRIM_CHANNEL";
+	case CSR_PSKEY_IQ_TRIM_GAIN:
+		return "IQ_TRIM_GAIN";
+	case CSR_PSKEY_IQ_TRIM_ENABLE:
+		return "IQ_TRIM_ENABLE";
+	case CSR_PSKEY_TX_OFFSET_HALF_MHZ:
+		return "TX_OFFSET_HALF_MHZ";
+	case CSR_PSKEY_GBL_MISC_ENABLES:
+		return "GBL_MISC_ENABLES";
+	case CSR_PSKEY_UART_SLEEP_TIMEOUT:
+		return "UART_SLEEP_TIMEOUT";
+	case CSR_PSKEY_DEEP_SLEEP_STATE:
+		return "DEEP_SLEEP_STATE";
+	case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM:
+		return "IQ_ENABLE_PHASE_TRIM";
+	case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD:
+		return "HCI_HANDLE_FREEZE_PERIOD";
+	case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES:
+		return "MAX_FROZEN_HCI_HANDLES";
+	case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY:
+		return "PAGETABLE_DESTRUCTION_DELAY";
+	case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS:
+		return "IQ_TRIM_PIO_SETTINGS";
+	case CSR_PSKEY_USE_EXTERNAL_CLOCK:
+		return "USE_EXTERNAL_CLOCK";
+	case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS:
+		return "DEEP_SLEEP_WAKE_CTS";
+	case CSR_PSKEY_FC_HC2H_FLUSH_DELAY:
+		return "FC_HC2H_FLUSH_DELAY";
+	case CSR_PSKEY_RX_HIGHSIDE:
+		return "RX_HIGHSIDE";
+	case CSR_PSKEY_TX_PRE_LVL:
+		return "TX_PRE_LVL";
+	case CSR_PSKEY_RX_SINGLE_ENDED:
+		return "RX_SINGLE_ENDED";
+	case CSR_PSKEY_TX_FILTER_CONFIG:
+		return "TX_FILTER_CONFIG";
+	case CSR_PSKEY_CLOCK_REQUEST_ENABLE:
+		return "CLOCK_REQUEST_ENABLE";
+	case CSR_PSKEY_RX_MIN_ATTEN:
+		return "RX_MIN_ATTEN";
+	case CSR_PSKEY_XTAL_TARGET_AMPLITUDE:
+		return "XTAL_TARGET_AMPLITUDE";
+	case CSR_PSKEY_PCM_MIN_CPU_CLOCK:
+		return "PCM_MIN_CPU_CLOCK";
+	case CSR_PSKEY_HOST_INTERFACE_PIO_USB:
+		return "HOST_INTERFACE_PIO_USB";
+	case CSR_PSKEY_CPU_IDLE_MODE:
+		return "CPU_IDLE_MODE";
+	case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS:
+		return "DEEP_SLEEP_CLEAR_RTS";
+	case CSR_PSKEY_RF_RESONANCE_TRIM:
+		return "RF_RESONANCE_TRIM";
+	case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE:
+		return "DEEP_SLEEP_PIO_WAKE";
+	case CSR_PSKEY_DRAIN_BORE_TIMERS:
+		return "DRAIN_BORE_TIMERS";
+	case CSR_PSKEY_DRAIN_TX_POWER_BASE:
+		return "DRAIN_TX_POWER_BASE";
+	case CSR_PSKEY_MODULE_ID:
+		return "MODULE_ID";
+	case CSR_PSKEY_MODULE_DESIGN:
+		return "MODULE_DESIGN";
+	case CSR_PSKEY_MODULE_SECURITY_CODE:
+		return "MODULE_SECURITY_CODE";
+	case CSR_PSKEY_VM_DISABLE:
+		return "VM_DISABLE";
+	case CSR_PSKEY_MOD_MANUF0:
+		return "MOD_MANUF0";
+	case CSR_PSKEY_MOD_MANUF1:
+		return "MOD_MANUF1";
+	case CSR_PSKEY_MOD_MANUF2:
+		return "MOD_MANUF2";
+	case CSR_PSKEY_MOD_MANUF3:
+		return "MOD_MANUF3";
+	case CSR_PSKEY_MOD_MANUF4:
+		return "MOD_MANUF4";
+	case CSR_PSKEY_MOD_MANUF5:
+		return "MOD_MANUF5";
+	case CSR_PSKEY_MOD_MANUF6:
+		return "MOD_MANUF6";
+	case CSR_PSKEY_MOD_MANUF7:
+		return "MOD_MANUF7";
+	case CSR_PSKEY_MOD_MANUF8:
+		return "MOD_MANUF8";
+	case CSR_PSKEY_MOD_MANUF9:
+		return "MOD_MANUF9";
+	case CSR_PSKEY_DUT_VM_DISABLE:
+		return "DUT_VM_DISABLE";
+	case CSR_PSKEY_USR0:
+		return "USR0";
+	case CSR_PSKEY_USR1:
+		return "USR1";
+	case CSR_PSKEY_USR2:
+		return "USR2";
+	case CSR_PSKEY_USR3:
+		return "USR3";
+	case CSR_PSKEY_USR4:
+		return "USR4";
+	case CSR_PSKEY_USR5:
+		return "USR5";
+	case CSR_PSKEY_USR6:
+		return "USR6";
+	case CSR_PSKEY_USR7:
+		return "USR7";
+	case CSR_PSKEY_USR8:
+		return "USR8";
+	case CSR_PSKEY_USR9:
+		return "USR9";
+	case CSR_PSKEY_USR10:
+		return "USR10";
+	case CSR_PSKEY_USR11:
+		return "USR11";
+	case CSR_PSKEY_USR12:
+		return "USR12";
+	case CSR_PSKEY_USR13:
+		return "USR13";
+	case CSR_PSKEY_USR14:
+		return "USR14";
+	case CSR_PSKEY_USR15:
+		return "USR15";
+	case CSR_PSKEY_USR16:
+		return "USR16";
+	case CSR_PSKEY_USR17:
+		return "USR17";
+	case CSR_PSKEY_USR18:
+		return "USR18";
+	case CSR_PSKEY_USR19:
+		return "USR19";
+	case CSR_PSKEY_USR20:
+		return "USR20";
+	case CSR_PSKEY_USR21:
+		return "USR21";
+	case CSR_PSKEY_USR22:
+		return "USR22";
+	case CSR_PSKEY_USR23:
+		return "USR23";
+	case CSR_PSKEY_USR24:
+		return "USR24";
+	case CSR_PSKEY_USR25:
+		return "USR25";
+	case CSR_PSKEY_USR26:
+		return "USR26";
+	case CSR_PSKEY_USR27:
+		return "USR27";
+	case CSR_PSKEY_USR28:
+		return "USR28";
+	case CSR_PSKEY_USR29:
+		return "USR29";
+	case CSR_PSKEY_USR30:
+		return "USR30";
+	case CSR_PSKEY_USR31:
+		return "USR31";
+	case CSR_PSKEY_USR32:
+		return "USR32";
+	case CSR_PSKEY_USR33:
+		return "USR33";
+	case CSR_PSKEY_USR34:
+		return "USR34";
+	case CSR_PSKEY_USR35:
+		return "USR35";
+	case CSR_PSKEY_USR36:
+		return "USR36";
+	case CSR_PSKEY_USR37:
+		return "USR37";
+	case CSR_PSKEY_USR38:
+		return "USR38";
+	case CSR_PSKEY_USR39:
+		return "USR39";
+	case CSR_PSKEY_USR40:
+		return "USR40";
+	case CSR_PSKEY_USR41:
+		return "USR41";
+	case CSR_PSKEY_USR42:
+		return "USR42";
+	case CSR_PSKEY_USR43:
+		return "USR43";
+	case CSR_PSKEY_USR44:
+		return "USR44";
+	case CSR_PSKEY_USR45:
+		return "USR45";
+	case CSR_PSKEY_USR46:
+		return "USR46";
+	case CSR_PSKEY_USR47:
+		return "USR47";
+	case CSR_PSKEY_USR48:
+		return "USR48";
+	case CSR_PSKEY_USR49:
+		return "USR49";
+	case CSR_PSKEY_USB_VERSION:
+		return "USB_VERSION";
+	case CSR_PSKEY_USB_DEVICE_CLASS_CODES:
+		return "USB_DEVICE_CLASS_CODES";
+	case CSR_PSKEY_USB_VENDOR_ID:
+		return "USB_VENDOR_ID";
+	case CSR_PSKEY_USB_PRODUCT_ID:
+		return "USB_PRODUCT_ID";
+	case CSR_PSKEY_USB_MANUF_STRING:
+		return "USB_MANUF_STRING";
+	case CSR_PSKEY_USB_PRODUCT_STRING:
+		return "USB_PRODUCT_STRING";
+	case CSR_PSKEY_USB_SERIAL_NUMBER_STRING:
+		return "USB_SERIAL_NUMBER_STRING";
+	case CSR_PSKEY_USB_CONFIG_STRING:
+		return "USB_CONFIG_STRING";
+	case CSR_PSKEY_USB_ATTRIBUTES:
+		return "USB_ATTRIBUTES";
+	case CSR_PSKEY_USB_MAX_POWER:
+		return "USB_MAX_POWER";
+	case CSR_PSKEY_USB_BT_IF_CLASS_CODES:
+		return "USB_BT_IF_CLASS_CODES";
+	case CSR_PSKEY_USB_LANGID:
+		return "USB_LANGID";
+	case CSR_PSKEY_USB_DFU_CLASS_CODES:
+		return "USB_DFU_CLASS_CODES";
+	case CSR_PSKEY_USB_DFU_PRODUCT_ID:
+		return "USB_DFU_PRODUCT_ID";
+	case CSR_PSKEY_USB_PIO_DETACH:
+		return "USB_PIO_DETACH";
+	case CSR_PSKEY_USB_PIO_WAKEUP:
+		return "USB_PIO_WAKEUP";
+	case CSR_PSKEY_USB_PIO_PULLUP:
+		return "USB_PIO_PULLUP";
+	case CSR_PSKEY_USB_PIO_VBUS:
+		return "USB_PIO_VBUS";
+	case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT:
+		return "USB_PIO_WAKE_TIMEOUT";
+	case CSR_PSKEY_USB_PIO_RESUME:
+		return "USB_PIO_RESUME";
+	case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES:
+		return "USB_BT_SCO_IF_CLASS_CODES";
+	case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL:
+		return "USB_SUSPEND_PIO_LEVEL";
+	case CSR_PSKEY_USB_SUSPEND_PIO_DIR:
+		return "USB_SUSPEND_PIO_DIR";
+	case CSR_PSKEY_USB_SUSPEND_PIO_MASK:
+		return "USB_SUSPEND_PIO_MASK";
+	case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE:
+		return "USB_ENDPOINT_0_MAX_PACKET_SIZE";
+	case CSR_PSKEY_USB_CONFIG:
+		return "USB_CONFIG";
+	case CSR_PSKEY_RADIOTEST_ATTEN_INIT:
+		return "RADIOTEST_ATTEN_INIT";
+	case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME:
+		return "RADIOTEST_FIRST_TRIM_TIME";
+	case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME:
+		return "RADIOTEST_SUBSEQUENT_TRIM_TIME";
+	case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE:
+		return "RADIOTEST_LO_LVL_TRIM_ENABLE";
+	case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION:
+		return "RADIOTEST_DISABLE_MODULATION";
+	case CSR_PSKEY_RFCOMM_FCON_THRESHOLD:
+		return "RFCOMM_FCON_THRESHOLD";
+	case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD:
+		return "RFCOMM_FCOFF_THRESHOLD";
+	case CSR_PSKEY_IPV6_STATIC_ADDR:
+		return "IPV6_STATIC_ADDR";
+	case CSR_PSKEY_IPV4_STATIC_ADDR:
+		return "IPV4_STATIC_ADDR";
+	case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN:
+		return "IPV6_STATIC_PREFIX_LEN";
+	case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR:
+		return "IPV6_STATIC_ROUTER_ADDR";
+	case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK:
+		return "IPV4_STATIC_SUBNET_MASK";
+	case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR:
+		return "IPV4_STATIC_ROUTER_ADDR";
+	case CSR_PSKEY_MDNS_NAME:
+		return "MDNS_NAME";
+	case CSR_PSKEY_FIXED_PIN:
+		return "FIXED_PIN";
+	case CSR_PSKEY_MDNS_PORT:
+		return "MDNS_PORT";
+	case CSR_PSKEY_MDNS_TTL:
+		return "MDNS_TTL";
+	case CSR_PSKEY_MDNS_IPV4_ADDR:
+		return "MDNS_IPV4_ADDR";
+	case CSR_PSKEY_ARP_CACHE_TIMEOUT:
+		return "ARP_CACHE_TIMEOUT";
+	case CSR_PSKEY_HFP_POWER_TABLE:
+		return "HFP_POWER_TABLE";
+	case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS:
+		return "DRAIN_BORE_TIMER_COUNTERS";
+	case CSR_PSKEY_DRAIN_BORE_COUNTERS:
+		return "DRAIN_BORE_COUNTERS";
+	case CSR_PSKEY_LOOP_FILTER_TRIM:
+		return "LOOP_FILTER_TRIM";
+	case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK:
+		return "DRAIN_BORE_CURRENT_PEAK";
+	case CSR_PSKEY_VM_E2_CACHE_LIMIT:
+		return "VM_E2_CACHE_LIMIT";
+	case CSR_PSKEY_FORCE_16MHZ_REF_PIO:
+		return "FORCE_16MHZ_REF_PIO";
+	case CSR_PSKEY_CDMA_LO_REF_LIMITS:
+		return "CDMA_LO_REF_LIMITS";
+	case CSR_PSKEY_CDMA_LO_ERROR_LIMITS:
+		return "CDMA_LO_ERROR_LIMITS";
+	case CSR_PSKEY_CLOCK_STARTUP_DELAY:
+		return "CLOCK_STARTUP_DELAY";
+	case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR:
+		return "DEEP_SLEEP_CORRECTION_FACTOR";
+	case CSR_PSKEY_TEMPERATURE_CALIBRATION:
+		return "TEMPERATURE_CALIBRATION";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA:
+		return "TEMPERATURE_VS_DELTA_INTERNAL_PA";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL:
+		return "TEMPERATURE_VS_DELTA_TX_PRE_LVL";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB:
+		return "TEMPERATURE_VS_DELTA_TX_BB";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM:
+		return "TEMPERATURE_VS_DELTA_ANA_FTRIM";
+	case CSR_PSKEY_TEST_DELTA_OFFSET:
+		return "TEST_DELTA_OFFSET";
+	case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET:
+		return "RX_DYNAMIC_LVL_OFFSET";
+	case CSR_PSKEY_TEST_FORCE_OFFSET:
+		return "TEST_FORCE_OFFSET";
+	case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS:
+		return "RF_TRAP_BAD_DIVISION_RATIOS";
+	case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS:
+		return "RADIOTEST_CDMA_LO_REF_LIMITS";
+	case CSR_PSKEY_INITIAL_BOOTMODE:
+		return "INITIAL_BOOTMODE";
+	case CSR_PSKEY_ONCHIP_HCI_CLIENT:
+		return "ONCHIP_HCI_CLIENT";
+	case CSR_PSKEY_RX_ATTEN_BACKOFF:
+		return "RX_ATTEN_BACKOFF";
+	case CSR_PSKEY_RX_ATTEN_UPDATE_RATE:
+		return "RX_ATTEN_UPDATE_RATE";
+	case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS:
+		return "SYNTH_TXRX_THRESHOLDS";
+	case CSR_PSKEY_MIN_WAIT_STATES:
+		return "MIN_WAIT_STATES";
+	case CSR_PSKEY_RSSI_CORRECTION:
+		return "RSSI_CORRECTION";
+	case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT:
+		return "SCHED_THROTTLE_TIMEOUT";
+	case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK:
+		return "DEEP_SLEEP_USE_EXTERNAL_CLOCK";
+	case CSR_PSKEY_TRIM_RADIO_FILTERS:
+		return "TRIM_RADIO_FILTERS";
+	case CSR_PSKEY_TRANSMIT_OFFSET:
+		return "TRANSMIT_OFFSET";
+	case CSR_PSKEY_USB_VM_CONTROL:
+		return "USB_VM_CONTROL";
+	case CSR_PSKEY_MR_ANA_RX_FTRIM:
+		return "MR_ANA_RX_FTRIM";
+	case CSR_PSKEY_I2C_CONFIG:
+		return "I2C_CONFIG";
+	case CSR_PSKEY_IQ_LVL_RX:
+		return "IQ_LVL_RX";
+	case CSR_PSKEY_MR_TX_FILTER_CONFIG:
+		return "MR_TX_FILTER_CONFIG";
+	case CSR_PSKEY_MR_TX_CONFIG2:
+		return "MR_TX_CONFIG2";
+	case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET:
+		return "USB_DONT_RESET_BOOTMODE_ON_HOST_RESET";
+	case CSR_PSKEY_LC_USE_THROTTLING:
+		return "LC_USE_THROTTLING";
+	case CSR_PSKEY_CHARGER_TRIM:
+		return "CHARGER_TRIM";
+	case CSR_PSKEY_CLOCK_REQUEST_FEATURES:
+		return "CLOCK_REQUEST_FEATURES";
+	case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1:
+		return "TRANSMIT_OFFSET_CLASS1";
+	case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO:
+		return "TX_AVOID_PA_CLASS1_PIO";
+	case CSR_PSKEY_MR_PIO_CONFIG:
+		return "MR_PIO_CONFIG";
+	case CSR_PSKEY_UART_CONFIG2:
+		return "UART_CONFIG2";
+	case CSR_PSKEY_CLASS1_IQ_LVL:
+		return "CLASS1_IQ_LVL";
+	case CSR_PSKEY_CLASS1_TX_CONFIG2:
+		return "CLASS1_TX_CONFIG2";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1:
+		return "TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1:
+		return "TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR:
+		return "TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER:
+		return "TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER";
+	case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD:
+		return "TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD";
+	case CSR_PSKEY_RX_MR_EQ_TAPS:
+		return "RX_MR_EQ_TAPS";
+	case CSR_PSKEY_TX_PRE_LVL_CLASS1:
+		return "TX_PRE_LVL_CLASS1";
+	case CSR_PSKEY_ANALOGUE_ATTENUATOR:
+		return "ANALOGUE_ATTENUATOR";
+	case CSR_PSKEY_MR_RX_FILTER_TRIM:
+		return "MR_RX_FILTER_TRIM";
+	case CSR_PSKEY_MR_RX_FILTER_RESPONSE:
+		return "MR_RX_FILTER_RESPONSE";
+	case CSR_PSKEY_PIO_WAKEUP_STATE:
+		return "PIO_WAKEUP_STATE";
+	case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP:
+		return "MR_TX_IF_ATTEN_OFF_TEMP";
+	case CSR_PSKEY_LO_DIV_LATCH_BYPASS:
+		return "LO_DIV_LATCH_BYPASS";
+	case CSR_PSKEY_LO_VCO_STANDBY:
+		return "LO_VCO_STANDBY";
+	case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT:
+		return "SLOW_CLOCK_FILTER_SHIFT";
+	case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER:
+		return "SLOW_CLOCK_FILTER_DIVIDER";
+	case CSR_PSKEY_USB_ATTRIBUTES_POWER:
+		return "USB_ATTRIBUTES_POWER";
+	case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP:
+		return "USB_ATTRIBUTES_WAKEUP";
+	case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT:
+		return "DFU_ATTRIBUTES_MANIFESTATION_TOLERANT";
+	case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD:
+		return "DFU_ATTRIBUTES_CAN_UPLOAD";
+	case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD:
+		return "DFU_ATTRIBUTES_CAN_DOWNLOAD";
+	case CSR_PSKEY_UART_CONFIG_STOP_BITS:
+		return "UART_CONFIG_STOP_BITS";
+	case CSR_PSKEY_UART_CONFIG_PARITY_BIT:
+		return "UART_CONFIG_PARITY_BIT";
+	case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN:
+		return "UART_CONFIG_FLOW_CTRL_EN";
+	case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN:
+		return "UART_CONFIG_RTS_AUTO_EN";
+	case CSR_PSKEY_UART_CONFIG_RTS:
+		return "UART_CONFIG_RTS";
+	case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN:
+		return "UART_CONFIG_TX_ZERO_EN";
+	case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN:
+		return "UART_CONFIG_NON_BCSP_EN";
+	case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY:
+		return "UART_CONFIG_RX_RATE_DELAY";
+	case CSR_PSKEY_UART_SEQ_TIMEOUT:
+		return "UART_SEQ_TIMEOUT";
+	case CSR_PSKEY_UART_SEQ_RETRIES:
+		return "UART_SEQ_RETRIES";
+	case CSR_PSKEY_UART_SEQ_WINSIZE:
+		return "UART_SEQ_WINSIZE";
+	case CSR_PSKEY_UART_USE_CRC_ON_TX:
+		return "UART_USE_CRC_ON_TX";
+	case CSR_PSKEY_UART_HOST_INITIAL_STATE:
+		return "UART_HOST_INITIAL_STATE";
+	case CSR_PSKEY_UART_HOST_ATTENTION_SPAN:
+		return "UART_HOST_ATTENTION_SPAN";
+	case CSR_PSKEY_UART_HOST_WAKEUP_TIME:
+		return "UART_HOST_WAKEUP_TIME";
+	case CSR_PSKEY_UART_HOST_WAKEUP_WAIT:
+		return "UART_HOST_WAKEUP_WAIT";
+	case CSR_PSKEY_BCSP_LM_MODE:
+		return "BCSP_LM_MODE";
+	case CSR_PSKEY_BCSP_LM_SYNC_RETRIES:
+		return "BCSP_LM_SYNC_RETRIES";
+	case CSR_PSKEY_BCSP_LM_TSHY:
+		return "BCSP_LM_TSHY";
+	case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS:
+		return "UART_DFU_CONFIG_STOP_BITS";
+	case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT:
+		return "UART_DFU_CONFIG_PARITY_BIT";
+	case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN:
+		return "UART_DFU_CONFIG_FLOW_CTRL_EN";
+	case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN:
+		return "UART_DFU_CONFIG_RTS_AUTO_EN";
+	case CSR_PSKEY_UART_DFU_CONFIG_RTS:
+		return "UART_DFU_CONFIG_RTS";
+	case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN:
+		return "UART_DFU_CONFIG_TX_ZERO_EN";
+	case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN:
+		return "UART_DFU_CONFIG_NON_BCSP_EN";
+	case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY:
+		return "UART_DFU_CONFIG_RX_RATE_DELAY";
+	case CSR_PSKEY_AMUX_AIO0:
+		return "AMUX_AIO0";
+	case CSR_PSKEY_AMUX_AIO1:
+		return "AMUX_AIO1";
+	case CSR_PSKEY_AMUX_AIO2:
+		return "AMUX_AIO2";
+	case CSR_PSKEY_AMUX_AIO3:
+		return "AMUX_AIO3";
+	case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED:
+		return "LOCAL_NAME_SIMPLIFIED";
+	case CSR_PSKEY_EXTENDED_STUB:
+		return "EXTENDED_STUB";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid)
+{
+	unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00,
+				seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	switch (varid) {
+	case CSR_VARID_COLD_RESET:
+	case CSR_VARID_WARM_RESET:
+	case CSR_VARID_COLD_HALT:
+	case CSR_VARID_WARM_HALT:
+		return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, sizeof(cmd) + 1, cp);
+	}
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8,
+				seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+	memcpy(cp + 11, value, length);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + length + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8,
+				seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+	memcpy(cp + 11, value, length);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + length + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	memcpy(value, rp + 11, length);
+
+	return 0;
+}
+
+int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value)
+{
+	unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00,
+				seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	*value = rp[11] + (rp[12] << 8);
+
+	return 0;
+}
+
+int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value)
+{
+	unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00,
+				seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	*value = ((rp[11] + (rp[12] << 8)) << 16) + (rp[13] + (rp[14] << 8));
+
+	return 0;
+}
+
+int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length)
+{
+	unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8,
+				seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00,
+				pskey & 0xff, pskey >> 8,
+				(length / 2) & 0xff, (length / 2) >> 8,
+				stores & 0xff, stores >> 8, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + length - 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	memcpy(value, rp + 17, length);
+
+	return 0;
+}
+
+int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length)
+{
+	unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8,
+				seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00,
+				pskey & 0xff, pskey >> 8,
+				(length / 2) & 0xff, (length / 2) >> 8,
+				stores & 0xff, stores >> 8, 0x00, 0x00 };
+
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+
+	memset(&cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+
+	memcpy(cp + 17, value, length);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = sizeof(cmd) + length - 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value)
+{
+	uint8_t array[2] = { 0x00, 0x00 };
+	int err;
+
+	err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 2);
+
+	*value = array[0] + (array[1] << 8);
+
+	return err;
+}
+
+int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value)
+{
+	uint8_t array[2] = { value & 0xff, value >> 8 };
+
+	return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 2);
+}
+
+int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value)
+{
+	uint8_t array[4] = { 0x00, 0x00, 0x00, 0x00 };
+	int err;
+
+	err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 4);
+
+	*value = ((array[0] + (array[1] << 8)) << 16) +
+						(array[2] + (array[3] << 8));
+
+	return err;
+}
+
+int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value)
+{
+	uint8_t array[4] = { (value & 0xff0000) >> 16, value >> 24,
+					value & 0xff, (value & 0xff00) >> 8 };
+
+	return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 4);
+}
+
+int psr_put(uint16_t pskey, uint8_t *value, uint16_t size)
+{
+	struct psr_data *item;
+
+	item = malloc(sizeof(*item));
+	if (!item)
+		return -ENOMEM;
+
+	item->pskey = pskey;
+
+	if (size > 0) {
+		item->value = malloc(size);
+		if (!item->value) {
+			free(item);
+			return -ENOMEM;
+		}
+
+		memcpy(item->value, value, size);
+		item->size = size;
+	} else {
+		item->value = NULL;
+		item->size = 0;
+	}
+
+	item->next = NULL;
+
+	if (!head)
+		head = item;
+	else
+		tail->next = item;
+
+	tail = item;
+
+	return 0;
+}
+
+int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size)
+{
+	struct psr_data *item = head;
+
+	if (!head)
+		return -ENOENT;
+
+	*pskey = item->pskey;
+
+	if (item->value) {
+		if (value && item->size > 0)
+			memcpy(value, item->value, item->size);
+		free(item->value);
+		*size = item->size;
+	} else
+		*size = 0;
+
+	if (head == tail)
+		tail = NULL;
+
+	head = head->next;
+	free(item);
+
+	return 0;
+}
+
+static int parse_line(char *str)
+{
+	uint8_t array[256];
+	uint16_t value, pskey, length = 0;
+	char *off, *end;
+
+	pskey = strtol(str + 1, NULL, 16);
+	off = strstr(str, "=");
+	if (!off)
+		return -EIO;
+
+	off++;
+
+	while (1) {
+		value = strtol(off, &end, 16);
+		if (value == 0 && off == end)
+			break;
+
+		array[length++] = value & 0xff;
+		array[length++] = value >> 8;
+
+		if (*end == '\0')
+			break;
+
+		off = end + 1;
+	}
+
+	return psr_put(pskey, array, length);
+}
+
+int psr_read(const char *filename)
+{
+	struct stat st;
+	char *str, *map, *off, *end;
+	int fd, err = 0;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	if (fstat(fd, &st) < 0) {
+		err = -errno;
+		goto close;
+	}
+
+	map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (!map || map == MAP_FAILED) {
+		err = -errno;
+		goto close;
+	}
+
+	off = map;
+
+	while (1) {
+		if (*off == '\r' || *off == '\n') {
+			off++;
+			continue;
+		}
+
+		end = strpbrk(off, "\r\n");
+		if (!end)
+			break;
+
+		str = malloc(end - off + 1);
+		if (!str)
+			break;
+
+		memset(str, 0, end - off + 1);
+		strncpy(str, off, end - off);
+		if (*str == '&')
+			parse_line(str);
+
+		free(str);
+		off = end + 1;
+	}
+
+	munmap(map, st.st_size);
+
+close:
+	close(fd);
+
+	return err;
+}
+
+int psr_print(void)
+{
+	uint8_t array[256];
+	uint16_t pskey, length;
+	char *str, val[7];
+	int i;
+
+	while (1) {
+		if (psr_get(&pskey, array, &length) < 0)
+			break;
+
+		str = csr_pskeytoval(pskey);
+		if (!strcasecmp(str, "UNKNOWN")) {
+			sprintf(val, "0x%04x", pskey);
+			str = NULL;
+		}
+
+		printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
+						str ? str : val, pskey);
+		for (i = 0; i < length / 2; i++)
+			printf(" %02x%02x", array[i * 2 + 1], array[i * 2]);
+		printf("\n");
+	}
+
+	return 0;
+}
diff --git a/bluez/repo/tools/csr.h b/bluez/repo/tools/csr.h
new file mode 100644
index 0000000..cc245a5
--- /dev/null
+++ b/bluez/repo/tools/csr.h
@@ -0,0 +1,555 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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
+ *
+ */
+
+#include <stdint.h>
+#include <termios.h>
+
+#define CSR_VARID_PS_CLR_ALL			0x000b	/* valueless */
+#define CSR_VARID_PS_FACTORY_SET		0x000c	/* valueless */
+#define CSR_VARID_PS_CLR_ALL_STORES		0x082d	/* uint16 */
+#define CSR_VARID_BC01_STATUS			0x2801	/* uint16 */
+#define CSR_VARID_BUILDID			0x2819	/* uint16 */
+#define CSR_VARID_CHIPVER			0x281a	/* uint16 */
+#define CSR_VARID_CHIPREV			0x281b	/* uint16 */
+#define CSR_VARID_INTERFACE_VERSION		0x2825	/* uint16 */
+#define CSR_VARID_RAND				0x282a	/* uint16 */
+#define CSR_VARID_MAX_CRYPT_KEY_LENGTH		0x282c	/* uint16 */
+#define CSR_VARID_CHIPANAREV			0x2836	/* uint16 */
+#define CSR_VARID_BUILDID_LOADER		0x2838	/* uint16 */
+#define CSR_VARID_BT_CLOCK			0x2c00	/* uint32 */
+#define CSR_VARID_PS_NEXT			0x3005	/* complex */
+#define CSR_VARID_PS_SIZE			0x3006	/* complex */
+#define CSR_VARID_ADC_RES			0x3007	/* complex */
+#define CSR_VARID_CRYPT_KEY_LENGTH		0x3008	/* complex */
+#define CSR_VARID_PICONET_INSTANCE		0x3009	/* complex */
+#define CSR_VARID_GET_CLR_EVT			0x300a	/* complex */
+#define CSR_VARID_GET_NEXT_BUILDDEF		0x300b	/* complex */
+#define CSR_VARID_PS_MEMORY_TYPE		0x3012	/* complex */
+#define CSR_VARID_READ_BUILD_NAME		0x301c	/* complex */
+#define CSR_VARID_COLD_RESET			0x4001	/* valueless */
+#define CSR_VARID_WARM_RESET			0x4002	/* valueless */
+#define CSR_VARID_COLD_HALT			0x4003	/* valueless */
+#define CSR_VARID_WARM_HALT			0x4004	/* valueless */
+#define CSR_VARID_INIT_BT_STACK			0x4005	/* valueless */
+#define CSR_VARID_ACTIVATE_BT_STACK		0x4006	/* valueless */
+#define CSR_VARID_ENABLE_TX			0x4007	/* valueless */
+#define CSR_VARID_DISABLE_TX			0x4008	/* valueless */
+#define CSR_VARID_RECAL				0x4009	/* valueless */
+#define CSR_VARID_PS_FACTORY_RESTORE		0x400d	/* valueless */
+#define CSR_VARID_PS_FACTORY_RESTORE_ALL	0x400e	/* valueless */
+#define CSR_VARID_PS_DEFRAG_RESET		0x400f	/* valueless */
+#define CSR_VARID_KILL_VM_APPLICATION		0x4010	/* valueless */
+#define CSR_VARID_HOPPING_ON			0x4011	/* valueless */
+#define CSR_VARID_CANCEL_PAGE			0x4012	/* valueless */
+#define CSR_VARID_PS_CLR			0x4818	/* uint16 */
+#define CSR_VARID_MAP_SCO_PCM			0x481c	/* uint16 */
+#define CSR_VARID_ADC				0x4829	/* uint16 */
+#define CSR_VARID_SINGLE_CHAN			0x482e	/* uint16 */
+#define CSR_VARID_RADIOTEST			0x5004	/* complex */
+#define CSR_VARID_PS_CLR_STORES			0x500c	/* complex */
+#define CSR_VARID_NO_VARIABLE			0x6000	/* valueless */
+#define CSR_VARID_CONFIG_UART			0x6802	/* uint16 */
+#define CSR_VARID_PANIC_ARG			0x6805	/* uint16 */
+#define CSR_VARID_FAULT_ARG			0x6806	/* uint16 */
+#define CSR_VARID_MAX_TX_POWER			0x6827	/* int8 */
+#define CSR_VARID_DEFAULT_TX_POWER		0x682b	/* int8 */
+#define CSR_VARID_PS				0x7003	/* complex */
+
+#define CSR_PSKEY_BDADDR					0x0001	/* bdaddr / uint16[] = { 0x00A5A5, 0x5b, 0x0002 } */
+#define CSR_PSKEY_COUNTRYCODE					0x0002	/* uint16 */
+#define CSR_PSKEY_CLASSOFDEVICE					0x0003	/* bdcod */
+#define CSR_PSKEY_DEVICE_DRIFT					0x0004	/* uint16 */
+#define CSR_PSKEY_DEVICE_JITTER					0x0005	/* uint16 */
+#define CSR_PSKEY_MAX_ACLS					0x000d	/* uint16 */
+#define CSR_PSKEY_MAX_SCOS					0x000e	/* uint16 */
+#define CSR_PSKEY_MAX_REMOTE_MASTERS				0x000f	/* uint16 */
+#define CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY			0x0010	/* bool */
+#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN			0x0011	/* uint16 */
+#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN			0x0012	/* uint8 */
+#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS				0x0013	/* uint16 */
+#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS				0x0014	/* uint16 */
+#define CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK			0x0015	/* lc_fc_lwm */
+#define CSR_PSKEY_LC_MAX_TX_POWER				0x0017	/* int16 */
+#define CSR_PSKEY_TX_GAIN_RAMP					0x001d	/* uint16 */
+#define CSR_PSKEY_LC_POWER_TABLE				0x001e	/* power_setting[] */
+#define CSR_PSKEY_LC_PEER_POWER_PERIOD				0x001f	/* TIME */
+#define CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK			0x0020	/* lc_fc_lwm */
+#define CSR_PSKEY_LC_DEFAULT_TX_POWER				0x0021	/* int16 */
+#define CSR_PSKEY_LC_RSSI_GOLDEN_RANGE				0x0022	/* uint8 */
+#define CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK			0x0028	/* uint16[] */
+#define CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK			0x0029	/* uint16[] */
+#define CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE		0x002a	/* uint16 */
+#define CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS			0x002b	/* uint16 */
+#define CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI			0x002d	/* int8 */
+#define CSR_PSKEY_LC_CONNECTION_RX_WINDOW			0x002e	/* uint16 */
+#define CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE		0x0030	/* uint16 */
+#define CSR_PSKEY_LC_ENHANCED_POWER_TABLE			0x0031	/* enhanced_power_setting[] */
+#define CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG			0x0032	/* wideband_rssi_config */
+#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD			0x0033	/* uint16 */
+#define CSR_PSKEY_BT_CLOCK_INIT					0x0034	/* uint32 */
+#define CSR_PSKEY_TX_MR_MOD_DELAY				0x0038	/* uint8 */
+#define CSR_PSKEY_RX_MR_SYNC_TIMING				0x0039	/* uint16 */
+#define CSR_PSKEY_RX_MR_SYNC_CONFIG				0x003a	/* uint16 */
+#define CSR_PSKEY_LC_LOST_SYNC_SLOTS				0x003b	/* uint16 */
+#define CSR_PSKEY_RX_MR_SAMP_CONFIG				0x003c	/* uint16 */
+#define CSR_PSKEY_AGC_HYST_LEVELS				0x003d	/* agc_hyst_config */
+#define CSR_PSKEY_RX_LEVEL_LOW_SIGNAL				0x003e	/* uint16 */
+#define CSR_PSKEY_AGC_IQ_LVL_VALUES				0x003f	/* IQ_LVL_VAL[] */
+#define CSR_PSKEY_MR_FTRIM_OFFSET_12DB				0x0040	/* uint16 */
+#define CSR_PSKEY_MR_FTRIM_OFFSET_6DB				0x0041	/* uint16 */
+#define CSR_PSKEY_NO_CAL_ON_BOOT				0x0042	/* bool */
+#define CSR_PSKEY_RSSI_HI_TARGET				0x0043	/* uint8 */
+#define CSR_PSKEY_PREFERRED_MIN_ATTENUATION			0x0044	/* uint8 */
+#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE		0x0045	/* bool */
+#define CSR_PSKEY_LC_MULTISLOT_HOLDOFF				0x0047	/* TIME */
+#define CSR_PSKEY_FREE_KEY_PIGEON_HOLE				0x00c9	/* uint16 */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR0				0x00ca	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR1				0x00cb	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR2				0x00cc	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR3				0x00cd	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR4				0x00ce	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR5				0x00cf	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR6				0x00d0	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR7				0x00d1	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR8				0x00d2	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR9				0x00d3	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR10				0x00d4	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR11				0x00d5	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR12				0x00d6	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR13				0x00d7	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR14				0x00d8	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR15				0x00d9	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_ENC_KEY_LMIN					0x00da	/* uint16 */
+#define CSR_PSKEY_ENC_KEY_LMAX					0x00db	/* uint16 */
+#define CSR_PSKEY_LOCAL_SUPPORTED_FEATURES			0x00ef	/* uint16[] = { 0xffff, 0xfe8f, 0xf99b, 0x8000 }*/
+#define CSR_PSKEY_LM_USE_UNIT_KEY				0x00f0	/* bool */
+#define CSR_PSKEY_HCI_NOP_DISABLE				0x00f2	/* bool */
+#define CSR_PSKEY_LM_MAX_EVENT_FILTERS				0x00f4	/* uint8 */
+#define CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST			0x00f5	/* bool */
+#define CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE			0x00f6	/* bool */
+#define CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME				0x00f7	/* uint16 */
+#define CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME			0x00f8	/* uint16 */
+#define CSR_PSKEY_AFH_OPTIONS					0x00f9	/* uint16 */
+#define CSR_PSKEY_AFH_RSSI_RUN_PERIOD				0x00fa	/* uint16 */
+#define CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME			0x00fb	/* uint16 */
+#define CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL			0x00fc	/* bool */
+#define CSR_PSKEY_MAX_PRIVATE_KEYS				0x00fd	/* uint8 */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0			0x00fe	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1			0x00ff	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2			0x0100	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3			0x0101	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4			0x0102	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5			0x0103	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6			0x0104	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7			0x0105	/* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS			0x0106	/* uint16[] = { 0xffff, 0x03ff, 0xfffe, 0xffff, 0xffff, 0xffff, 0x0ff3, 0xfff8, 0x003f } */
+#define CSR_PSKEY_LM_MAX_ABSENCE_INDEX				0x0107	/* uint8 */
+#define CSR_PSKEY_DEVICE_NAME					0x0108	/* uint16[] */
+#define CSR_PSKEY_AFH_RSSI_THRESHOLD				0x0109	/* uint16 */
+#define CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL			0x010a	/* uint16 */
+#define CSR_PSKEY_AFH_MIN_MAP_CHANGE				0x010b	/* uint16[] */
+#define CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD			0x010c	/* uint16 */
+#define CSR_PSKEY_HCI_LMP_LOCAL_VERSION				0x010d	/* uint16 */
+#define CSR_PSKEY_LMP_REMOTE_VERSION				0x010e	/* uint8 */
+#define CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER			0x0113	/* uint16 */
+#define CSR_PSKEY_DFU_ATTRIBUTES				0x0136	/* uint8 */
+#define CSR_PSKEY_DFU_DETACH_TO					0x0137	/* uint16 */
+#define CSR_PSKEY_DFU_TRANSFER_SIZE				0x0138	/* uint16 */
+#define CSR_PSKEY_DFU_ENABLE					0x0139	/* bool */
+#define CSR_PSKEY_DFU_LIN_REG_ENABLE				0x013a	/* bool */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB			0x015e	/* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB			0x015f	/* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH			0x0160	/* uint16 */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB			0x0161	/* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB			0x0162	/* uint16[] */
+#define CSR_PSKEY_BCSP_LM_PS_BLOCK				0x0192	/* BCSP_LM_PS_BLOCK */
+#define CSR_PSKEY_HOSTIO_FC_PS_BLOCK				0x0193	/* HOSTIO_FC_PS_BLOCK */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO0				0x0194	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO1				0x0195	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO2				0x0196	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO3				0x0197	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO4				0x0198	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO5				0x0199	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO6				0x019a	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO7				0x019b	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO8				0x019c	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO9				0x019d	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO10			0x019e	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO11			0x019f	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO12			0x01a0	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO13			0x01a1	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO14			0x01a2	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO15			0x01a3	/* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT			0x01a4	/* TIME */
+#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN				0x01a5	/* bool */
+#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC			0x01a6	/* bool */
+#define CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE			0x01a7	/* uint16 */
+#define CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT				0x01aa	/* uint16 */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM				0x01ab	/* bool */
+#define CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC			0x01ac	/* bool */
+#define CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD			0x01ad	/* TIME */
+#define CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE			0x01ae	/* uint16 */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_CODEC				0x01b0	/* bool */
+#define CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST			0x01b1	/* uint16 */
+#define CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST			0x01b2	/* uint16 */
+#define CSR_PSKEY_PCM_CONFIG32					0x01b3	/* uint32 */
+#define CSR_PSKEY_USE_OLD_BCSP_LE				0x01b4	/* uint16 */
+#define CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER			0x01b5	/* bool */
+#define CSR_PSKEY_PCM_FORMAT					0x01b6	/* uint16 */
+#define CSR_PSKEY_CODEC_OUT_GAIN				0x01b7	/* uint16 */
+#define CSR_PSKEY_CODEC_IN_GAIN					0x01b8	/* uint16 */
+#define CSR_PSKEY_CODEC_PIO					0x01b9	/* uint16 */
+#define CSR_PSKEY_PCM_LOW_JITTER_CONFIG				0x01ba	/* uint32 */
+#define CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS			0x01bb	/* uint16[] */
+#define CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS			0x01bc	/* uint16[] */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT			0x01bd	/* uint16 */
+#define CSR_PSKEY_UART_BAUDRATE					0x01be	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG_BCSP				0x01bf	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H4				0x01c0	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H5				0x01c1	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG_USR				0x01c2	/* uint16 */
+#define CSR_PSKEY_UART_TX_CRCS					0x01c3	/* bool */
+#define CSR_PSKEY_UART_ACK_TIMEOUT				0x01c4	/* uint16 */
+#define CSR_PSKEY_UART_TX_MAX_ATTEMPTS				0x01c5	/* uint16 */
+#define CSR_PSKEY_UART_TX_WINDOW_SIZE				0x01c6	/* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKE				0x01c7	/* uint16[] */
+#define CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT			0x01c8	/* TIME */
+#define CSR_PSKEY_PCM_ALWAYS_ENABLE				0x01c9	/* bool */
+#define CSR_PSKEY_UART_HOST_WAKE_SIGNAL				0x01ca	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H4DS				0x01cb	/* uint16 */
+#define CSR_PSKEY_H4DS_WAKE_DURATION				0x01cc	/* uint16 */
+#define CSR_PSKEY_H4DS_MAXWU					0x01cd	/* uint16 */
+#define CSR_PSKEY_H4DS_LE_TIMER_PERIOD				0x01cf	/* uint16 */
+#define CSR_PSKEY_H4DS_TWU_TIMER_PERIOD				0x01d0	/* uint16 */
+#define CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD			0x01d1	/* uint16 */
+#define CSR_PSKEY_ANA_FTRIM					0x01f6	/* uint16 */
+#define CSR_PSKEY_WD_TIMEOUT					0x01f7	/* TIME */
+#define CSR_PSKEY_WD_PERIOD					0x01f8	/* TIME */
+#define CSR_PSKEY_HOST_INTERFACE				0x01f9	/* phys_bus */
+#define CSR_PSKEY_HQ_HOST_TIMEOUT				0x01fb	/* TIME */
+#define CSR_PSKEY_HQ_ACTIVE					0x01fc	/* bool */
+#define CSR_PSKEY_BCCMD_SECURITY_ACTIVE				0x01fd	/* bool */
+#define CSR_PSKEY_ANA_FREQ					0x01fe	/* uint16 */
+#define CSR_PSKEY_PIO_PROTECT_MASK				0x0202	/* uint16 */
+#define CSR_PSKEY_PMALLOC_SIZES					0x0203	/* uint16[] */
+#define CSR_PSKEY_UART_BAUD_RATE				0x0204	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG					0x0205	/* uint16 */
+#define CSR_PSKEY_STUB						0x0207	/* uint16 */
+#define CSR_PSKEY_TXRX_PIO_CONTROL				0x0209	/* uint16 */
+#define CSR_PSKEY_ANA_RX_LEVEL					0x020b	/* uint16 */
+#define CSR_PSKEY_ANA_RX_FTRIM					0x020c	/* uint16 */
+#define CSR_PSKEY_PSBC_DATA_VERSION				0x020d	/* uint16 */
+#define CSR_PSKEY_PCM0_ATTENUATION				0x020f	/* uint16 */
+#define CSR_PSKEY_LO_LVL_MAX					0x0211	/* uint16 */
+#define CSR_PSKEY_LO_ADC_AMPL_MIN				0x0212	/* uint16 */
+#define CSR_PSKEY_LO_ADC_AMPL_MAX				0x0213	/* uint16 */
+#define CSR_PSKEY_IQ_TRIM_CHANNEL				0x0214	/* uint16 */
+#define CSR_PSKEY_IQ_TRIM_GAIN					0x0215	/* uint16 */
+#define CSR_PSKEY_IQ_TRIM_ENABLE				0x0216	/* iq_trim_enable_flag */
+#define CSR_PSKEY_TX_OFFSET_HALF_MHZ				0x0217	/* int16 */
+#define CSR_PSKEY_GBL_MISC_ENABLES				0x0221	/* uint16 */
+#define CSR_PSKEY_UART_SLEEP_TIMEOUT				0x0222	/* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_STATE				0x0229	/* deep_sleep_state */
+#define CSR_PSKEY_IQ_ENABLE_PHASE_TRIM				0x022d	/* bool */
+#define CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD			0x0237	/* TIME */
+#define CSR_PSKEY_MAX_FROZEN_HCI_HANDLES			0x0238	/* uint16 */
+#define CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY			0x0239	/* TIME */
+#define CSR_PSKEY_IQ_TRIM_PIO_SETTINGS				0x023a	/* uint8 */
+#define CSR_PSKEY_USE_EXTERNAL_CLOCK				0x023b	/* bool */
+#define CSR_PSKEY_DEEP_SLEEP_WAKE_CTS				0x023c	/* uint16 */
+#define CSR_PSKEY_FC_HC2H_FLUSH_DELAY				0x023d	/* TIME */
+#define CSR_PSKEY_RX_HIGHSIDE					0x023e	/* bool */
+#define CSR_PSKEY_TX_PRE_LVL					0x0240	/* uint8 */
+#define CSR_PSKEY_RX_SINGLE_ENDED				0x0242	/* bool */
+#define CSR_PSKEY_TX_FILTER_CONFIG				0x0243	/* uint32 */
+#define CSR_PSKEY_CLOCK_REQUEST_ENABLE				0x0246	/* uint16 */
+#define CSR_PSKEY_RX_MIN_ATTEN					0x0249	/* uint16 */
+#define CSR_PSKEY_XTAL_TARGET_AMPLITUDE				0x024b	/* uint8 */
+#define CSR_PSKEY_PCM_MIN_CPU_CLOCK				0x024d	/* uint16 */
+#define CSR_PSKEY_HOST_INTERFACE_PIO_USB			0x0250	/* uint16 */
+#define CSR_PSKEY_CPU_IDLE_MODE					0x0251	/* cpu_idle_mode */
+#define CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS				0x0252	/* bool */
+#define CSR_PSKEY_RF_RESONANCE_TRIM				0x0254	/* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_PIO_WAKE				0x0255	/* uint16 */
+#define CSR_PSKEY_DRAIN_BORE_TIMERS				0x0256	/* uint32[] */
+#define CSR_PSKEY_DRAIN_TX_POWER_BASE				0x0257	/* uint16 */
+#define CSR_PSKEY_MODULE_ID					0x0259	/* uint32 */
+#define CSR_PSKEY_MODULE_DESIGN					0x025a	/* uint16 */
+#define CSR_PSKEY_MODULE_SECURITY_CODE				0x025c	/* uint16[] */
+#define CSR_PSKEY_VM_DISABLE					0x025d	/* bool */
+#define CSR_PSKEY_MOD_MANUF0					0x025e	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF1					0x025f	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF2					0x0260	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF3					0x0261	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF4					0x0262	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF5					0x0263	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF6					0x0264	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF7					0x0265	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF8					0x0266	/* uint16[] */
+#define CSR_PSKEY_MOD_MANUF9					0x0267	/* uint16[] */
+#define CSR_PSKEY_DUT_VM_DISABLE				0x0268	/* bool */
+#define CSR_PSKEY_USR0						0x028a	/* uint16[] */
+#define CSR_PSKEY_USR1						0x028b	/* uint16[] */
+#define CSR_PSKEY_USR2						0x028c	/* uint16[] */
+#define CSR_PSKEY_USR3						0x028d	/* uint16[] */
+#define CSR_PSKEY_USR4						0x028e	/* uint16[] */
+#define CSR_PSKEY_USR5						0x028f	/* uint16[] */
+#define CSR_PSKEY_USR6						0x0290	/* uint16[] */
+#define CSR_PSKEY_USR7						0x0291	/* uint16[] */
+#define CSR_PSKEY_USR8						0x0292	/* uint16[] */
+#define CSR_PSKEY_USR9						0x0293	/* uint16[] */
+#define CSR_PSKEY_USR10						0x0294	/* uint16[] */
+#define CSR_PSKEY_USR11						0x0295	/* uint16[] */
+#define CSR_PSKEY_USR12						0x0296	/* uint16[] */
+#define CSR_PSKEY_USR13						0x0297	/* uint16[] */
+#define CSR_PSKEY_USR14						0x0298	/* uint16[] */
+#define CSR_PSKEY_USR15						0x0299	/* uint16[] */
+#define CSR_PSKEY_USR16						0x029a	/* uint16[] */
+#define CSR_PSKEY_USR17						0x029b	/* uint16[] */
+#define CSR_PSKEY_USR18						0x029c	/* uint16[] */
+#define CSR_PSKEY_USR19						0x029d	/* uint16[] */
+#define CSR_PSKEY_USR20						0x029e	/* uint16[] */
+#define CSR_PSKEY_USR21						0x029f	/* uint16[] */
+#define CSR_PSKEY_USR22						0x02a0	/* uint16[] */
+#define CSR_PSKEY_USR23						0x02a1	/* uint16[] */
+#define CSR_PSKEY_USR24						0x02a2	/* uint16[] */
+#define CSR_PSKEY_USR25						0x02a3	/* uint16[] */
+#define CSR_PSKEY_USR26						0x02a4	/* uint16[] */
+#define CSR_PSKEY_USR27						0x02a5	/* uint16[] */
+#define CSR_PSKEY_USR28						0x02a6	/* uint16[] */
+#define CSR_PSKEY_USR29						0x02a7	/* uint16[] */
+#define CSR_PSKEY_USR30						0x02a8	/* uint16[] */
+#define CSR_PSKEY_USR31						0x02a9	/* uint16[] */
+#define CSR_PSKEY_USR32						0x02aa	/* uint16[] */
+#define CSR_PSKEY_USR33						0x02ab	/* uint16[] */
+#define CSR_PSKEY_USR34						0x02ac	/* uint16[] */
+#define CSR_PSKEY_USR35						0x02ad	/* uint16[] */
+#define CSR_PSKEY_USR36						0x02ae	/* uint16[] */
+#define CSR_PSKEY_USR37						0x02af	/* uint16[] */
+#define CSR_PSKEY_USR38						0x02b0	/* uint16[] */
+#define CSR_PSKEY_USR39						0x02b1	/* uint16[] */
+#define CSR_PSKEY_USR40						0x02b2	/* uint16[] */
+#define CSR_PSKEY_USR41						0x02b3	/* uint16[] */
+#define CSR_PSKEY_USR42						0x02b4	/* uint16[] */
+#define CSR_PSKEY_USR43						0x02b5	/* uint16[] */
+#define CSR_PSKEY_USR44						0x02b6	/* uint16[] */
+#define CSR_PSKEY_USR45						0x02b7	/* uint16[] */
+#define CSR_PSKEY_USR46						0x02b8	/* uint16[] */
+#define CSR_PSKEY_USR47						0x02b9	/* uint16[] */
+#define CSR_PSKEY_USR48						0x02ba	/* uint16[] */
+#define CSR_PSKEY_USR49						0x02bb	/* uint16[] */
+#define CSR_PSKEY_USB_VERSION					0x02bc	/* uint16 */
+#define CSR_PSKEY_USB_DEVICE_CLASS_CODES			0x02bd	/* usbclass */
+#define CSR_PSKEY_USB_VENDOR_ID					0x02be	/* uint16 */
+#define CSR_PSKEY_USB_PRODUCT_ID				0x02bf	/* uint16 */
+#define CSR_PSKEY_USB_MANUF_STRING				0x02c1	/* unicodestring */
+#define CSR_PSKEY_USB_PRODUCT_STRING				0x02c2	/* unicodestring */
+#define CSR_PSKEY_USB_SERIAL_NUMBER_STRING			0x02c3	/* unicodestring */
+#define CSR_PSKEY_USB_CONFIG_STRING				0x02c4	/* unicodestring */
+#define CSR_PSKEY_USB_ATTRIBUTES				0x02c5	/* uint8 */
+#define CSR_PSKEY_USB_MAX_POWER					0x02c6	/* uint16 */
+#define CSR_PSKEY_USB_BT_IF_CLASS_CODES				0x02c7	/* usbclass */
+#define CSR_PSKEY_USB_LANGID					0x02c9	/* uint16 */
+#define CSR_PSKEY_USB_DFU_CLASS_CODES				0x02ca	/* usbclass */
+#define CSR_PSKEY_USB_DFU_PRODUCT_ID				0x02cb	/* uint16 */
+#define CSR_PSKEY_USB_PIO_DETACH				0x02ce	/* uint16 */
+#define CSR_PSKEY_USB_PIO_WAKEUP				0x02cf	/* uint16 */
+#define CSR_PSKEY_USB_PIO_PULLUP				0x02d0	/* uint16 */
+#define CSR_PSKEY_USB_PIO_VBUS					0x02d1	/* uint16 */
+#define CSR_PSKEY_USB_PIO_WAKE_TIMEOUT				0x02d2	/* uint16 */
+#define CSR_PSKEY_USB_PIO_RESUME				0x02d3	/* uint16 */
+#define CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES			0x02d4	/* usbclass */
+#define CSR_PSKEY_USB_SUSPEND_PIO_LEVEL				0x02d5	/* uint16 */
+#define CSR_PSKEY_USB_SUSPEND_PIO_DIR				0x02d6	/* uint16 */
+#define CSR_PSKEY_USB_SUSPEND_PIO_MASK				0x02d7	/* uint16 */
+#define CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE		0x02d8	/* uint8 */
+#define CSR_PSKEY_USB_CONFIG					0x02d9	/* uint16 */
+#define CSR_PSKEY_RADIOTEST_ATTEN_INIT				0x0320	/* uint16 */
+#define CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME			0x0326	/* TIME */
+#define CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME		0x0327	/* TIME */
+#define CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE			0x0328	/* bool */
+#define CSR_PSKEY_RADIOTEST_DISABLE_MODULATION			0x032c	/* bool */
+#define CSR_PSKEY_RFCOMM_FCON_THRESHOLD				0x0352	/* uint16 */
+#define CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD			0x0353	/* uint16 */
+#define CSR_PSKEY_IPV6_STATIC_ADDR				0x0354	/* uint16[] */
+#define CSR_PSKEY_IPV4_STATIC_ADDR				0x0355	/* uint32 */
+#define CSR_PSKEY_IPV6_STATIC_PREFIX_LEN			0x0356	/* uint8 */
+#define CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR			0x0357	/* uint16[] */
+#define CSR_PSKEY_IPV4_STATIC_SUBNET_MASK			0x0358	/* uint32 */
+#define CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR			0x0359	/* uint32 */
+#define CSR_PSKEY_MDNS_NAME					0x035a	/* char[] */
+#define CSR_PSKEY_FIXED_PIN					0x035b	/* uint8[] */
+#define CSR_PSKEY_MDNS_PORT					0x035c	/* uint16 */
+#define CSR_PSKEY_MDNS_TTL					0x035d	/* uint8 */
+#define CSR_PSKEY_MDNS_IPV4_ADDR				0x035e	/* uint32 */
+#define CSR_PSKEY_ARP_CACHE_TIMEOUT				0x035f	/* uint16 */
+#define CSR_PSKEY_HFP_POWER_TABLE				0x0360	/* uint16[] */
+#define CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS			0x03e7	/* uint32[] */
+#define CSR_PSKEY_DRAIN_BORE_COUNTERS				0x03e6	/* uint32[] */
+#define CSR_PSKEY_LOOP_FILTER_TRIM				0x03e4	/* uint16 */
+#define CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK			0x03e3	/* uint32[] */
+#define CSR_PSKEY_VM_E2_CACHE_LIMIT				0x03e2	/* uint16 */
+#define CSR_PSKEY_FORCE_16MHZ_REF_PIO				0x03e1	/* uint16 */
+#define CSR_PSKEY_CDMA_LO_REF_LIMITS				0x03df	/* uint16 */
+#define CSR_PSKEY_CDMA_LO_ERROR_LIMITS				0x03de	/* uint16 */
+#define CSR_PSKEY_CLOCK_STARTUP_DELAY				0x03dd	/* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR			0x03dc	/* int16 */
+#define CSR_PSKEY_TEMPERATURE_CALIBRATION			0x03db	/* temperature_calibration */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA		0x03da	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL		0x03d9	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB			0x03d8	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM		0x03d7	/* temperature_calibration[] */
+#define CSR_PSKEY_TEST_DELTA_OFFSET				0x03d6	/* uint16 */
+#define CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET				0x03d4	/* uint16 */
+#define CSR_PSKEY_TEST_FORCE_OFFSET				0x03d3	/* bool */
+#define CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS			0x03cf	/* uint16 */
+#define CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS			0x03ce	/* uint16 */
+#define CSR_PSKEY_INITIAL_BOOTMODE				0x03cd	/* int16 */
+#define CSR_PSKEY_ONCHIP_HCI_CLIENT				0x03cc	/* bool */
+#define CSR_PSKEY_RX_ATTEN_BACKOFF				0x03ca	/* uint16 */
+#define CSR_PSKEY_RX_ATTEN_UPDATE_RATE				0x03c9	/* uint16 */
+#define CSR_PSKEY_SYNTH_TXRX_THRESHOLDS				0x03c7	/* uint16 */
+#define CSR_PSKEY_MIN_WAIT_STATES				0x03c6	/* uint16 */
+#define CSR_PSKEY_RSSI_CORRECTION				0x03c5	/* int8 */
+#define CSR_PSKEY_SCHED_THROTTLE_TIMEOUT			0x03c4	/* TIME */
+#define CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK			0x03c3	/* bool */
+#define CSR_PSKEY_TRIM_RADIO_FILTERS				0x03c2	/* uint16 */
+#define CSR_PSKEY_TRANSMIT_OFFSET				0x03c1	/* int16 */
+#define CSR_PSKEY_USB_VM_CONTROL				0x03c0	/* bool */
+#define CSR_PSKEY_MR_ANA_RX_FTRIM				0x03bf	/* uint16 */
+#define CSR_PSKEY_I2C_CONFIG					0x03be	/* uint16 */
+#define CSR_PSKEY_IQ_LVL_RX					0x03bd	/* uint16 */
+#define CSR_PSKEY_MR_TX_FILTER_CONFIG				0x03bb	/* uint32 */
+#define CSR_PSKEY_MR_TX_CONFIG2					0x03ba	/* uint16 */
+#define CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET		0x03b9	/* bool */
+#define CSR_PSKEY_LC_USE_THROTTLING				0x03b8	/* bool */
+#define CSR_PSKEY_CHARGER_TRIM					0x03b7	/* uint16 */
+#define CSR_PSKEY_CLOCK_REQUEST_FEATURES			0x03b6	/* uint16 */
+#define CSR_PSKEY_TRANSMIT_OFFSET_CLASS1			0x03b4	/* int16 */
+#define CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO			0x03b3	/* uint16 */
+#define CSR_PSKEY_MR_PIO_CONFIG					0x03b2	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG2					0x03b1	/* uint8 */
+#define CSR_PSKEY_CLASS1_IQ_LVL					0x03b0	/* uint16 */
+#define CSR_PSKEY_CLASS1_TX_CONFIG2				0x03af	/* uint16 */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1	0x03ae	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1	0x03ad	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR		0x03ac	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER		0x03ab	/* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD		0x03aa	/* temperature_calibration[] */
+#define CSR_PSKEY_RX_MR_EQ_TAPS					0x03a9	/* uint16[] */
+#define CSR_PSKEY_TX_PRE_LVL_CLASS1				0x03a8	/* uint8 */
+#define CSR_PSKEY_ANALOGUE_ATTENUATOR				0x03a7	/* bool */
+#define CSR_PSKEY_MR_RX_FILTER_TRIM				0x03a6	/* uint16 */
+#define CSR_PSKEY_MR_RX_FILTER_RESPONSE				0x03a5	/* int16[] */
+#define CSR_PSKEY_PIO_WAKEUP_STATE				0x039f	/* uint16 */
+#define CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP			0x0394	/* int16 */
+#define CSR_PSKEY_LO_DIV_LATCH_BYPASS				0x0393	/* bool */
+#define CSR_PSKEY_LO_VCO_STANDBY				0x0392	/* bool */
+#define CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT			0x0391	/* uint16 */
+#define CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER			0x0390	/* uint16 */
+#define CSR_PSKEY_USB_ATTRIBUTES_POWER				0x03f2	/* bool */
+#define CSR_PSKEY_USB_ATTRIBUTES_WAKEUP				0x03f3	/* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT		0x03f4	/* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD			0x03f5	/* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD			0x03f6	/* bool */
+#define CSR_PSKEY_UART_CONFIG_STOP_BITS				0x03fc	/* bool */
+#define CSR_PSKEY_UART_CONFIG_PARITY_BIT			0x03fd	/* uint16 */
+#define CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN			0x03fe	/* bool */
+#define CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN			0x03ff	/* bool */
+#define CSR_PSKEY_UART_CONFIG_RTS				0x0400	/* bool */
+#define CSR_PSKEY_UART_CONFIG_TX_ZERO_EN			0x0401	/* bool */
+#define CSR_PSKEY_UART_CONFIG_NON_BCSP_EN			0x0402	/* bool */
+#define CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY			0x0403	/* uint16 */
+#define CSR_PSKEY_UART_SEQ_TIMEOUT				0x0405	/* uint16 */
+#define CSR_PSKEY_UART_SEQ_RETRIES				0x0406	/* uint16 */
+#define CSR_PSKEY_UART_SEQ_WINSIZE				0x0407	/* uint16 */
+#define CSR_PSKEY_UART_USE_CRC_ON_TX				0x0408	/* bool */
+#define CSR_PSKEY_UART_HOST_INITIAL_STATE			0x0409	/* hwakeup_state */
+#define CSR_PSKEY_UART_HOST_ATTENTION_SPAN			0x040a	/* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKEUP_TIME				0x040b	/* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKEUP_WAIT				0x040c	/* uint16 */
+#define CSR_PSKEY_BCSP_LM_MODE					0x0410	/* uint16 */
+#define CSR_PSKEY_BCSP_LM_SYNC_RETRIES				0x0411	/* uint16 */
+#define CSR_PSKEY_BCSP_LM_TSHY					0x0412	/* uint16 */
+#define CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS			0x0417	/* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT			0x0418	/* uint16 */
+#define CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN			0x0419	/* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN			0x041a	/* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RTS				0x041b	/* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN			0x041c	/* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN			0x041d	/* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY			0x041e	/* uint16 */
+#define CSR_PSKEY_AMUX_AIO0					0x041f	/* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO1					0x0420	/* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO2					0x0421	/* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO3					0x0422	/* ana_amux_sel */
+#define CSR_PSKEY_LOCAL_NAME_SIMPLIFIED				0x0423	/* local_name_complete */
+#define CSR_PSKEY_EXTENDED_STUB					0x2001	/* uint16 */
+
+char *csr_builddeftostr(uint16_t def);
+char *csr_buildidtostr(uint16_t id);
+char *csr_chipvertostr(uint16_t ver, uint16_t rev);
+char *csr_pskeytostr(uint16_t pskey);
+char *csr_pskeytoval(uint16_t pskey);
+
+int csr_open_hci(char *device);
+int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_hci(void);
+
+int csr_open_usb(char *device);
+int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_usb(void);
+
+int csr_open_bcsp(char *device, speed_t bcsp_rate);
+int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_bcsp(void);
+
+int csr_open_h4(char *device);
+int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_h4(void);
+
+int csr_open_3wire(char *device);
+int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_3wire(void);
+
+int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid);
+int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length);
+int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length);
+int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value);
+int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value);
+int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length);
+int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length);
+int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value);
+int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value);
+int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value);
+int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value);
+
+int psr_put(uint16_t pskey, uint8_t *value, uint16_t size);
+int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size);
+int psr_read(const char *filename);
+int psr_print(void);
diff --git a/bluez/repo/tools/csr_3wire.c b/bluez/repo/tools/csr_3wire.c
new file mode 100644
index 0000000..33fcf38
--- /dev/null
+++ b/bluez/repo/tools/csr_3wire.c
@@ -0,0 +1,62 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <stdint.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+int csr_open_3wire(char *device)
+{
+	fprintf(stderr, "Transport not implemented\n");
+
+	return -1;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	errno = EIO;
+
+	return -1;
+}
+
+int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_3wire(void)
+{
+}
diff --git a/bluez/repo/tools/csr_bcsp.c b/bluez/repo/tools/csr_bcsp.c
new file mode 100644
index 0000000..f7afe53
--- /dev/null
+++ b/bluez/repo/tools/csr_bcsp.c
@@ -0,0 +1,256 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#include "csr.h"
+#include "ubcsp.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int fd = -1;
+
+static struct ubcsp_packet send_packet;
+static uint8_t send_buffer[512];
+
+static struct ubcsp_packet receive_packet;
+static uint8_t receive_buffer[512];
+
+int csr_open_bcsp(char *device, speed_t bcsp_rate)
+{
+	struct termios ti;
+	uint8_t delay, activity = 0x00;
+	int timeout = 0;
+
+	if (!device)
+		device = "/dev/ttyS0";
+
+	fd = open(device, O_RDWR | O_NOCTTY);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open serial port: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (tcgetattr(fd, &ti) < 0) {
+		fprintf(stderr, "Can't get port settings: %s (%d)\n",
+						strerror(errno), errno);
+		close(fd);
+		return -1;
+	}
+
+	cfmakeraw(&ti);
+
+	ti.c_cflag |=  CLOCAL;
+	ti.c_cflag &= ~CRTSCTS;
+	ti.c_cflag |=  PARENB;
+	ti.c_cflag &= ~PARODD;
+	ti.c_cflag &= ~CSIZE;
+	ti.c_cflag |=  CS8;
+	ti.c_cflag &= ~CSTOPB;
+
+	ti.c_cc[VMIN] = 1;
+	ti.c_cc[VTIME] = 0;
+
+	cfsetospeed(&ti, bcsp_rate);
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		fprintf(stderr, "Can't change port settings: %s (%d)\n",
+						strerror(errno), errno);
+		close(fd);
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
+		fprintf(stderr, "Can't set non blocking mode: %s (%d)\n",
+						strerror(errno), errno);
+		close(fd);
+		return -1;
+	}
+
+	memset(&send_packet, 0, sizeof(send_packet));
+	memset(&receive_packet, 0, sizeof(receive_packet));
+
+	ubcsp_initialize();
+
+	send_packet.length = 512;
+	send_packet.payload = send_buffer;
+
+	receive_packet.length = 512;
+	receive_packet.payload = receive_buffer;
+
+	ubcsp_receive_packet(&receive_packet);
+
+	while (1) {
+		delay = ubcsp_poll(&activity);
+
+		if (activity & UBCSP_PACKET_SENT)
+			break;
+
+		if (delay) {
+			usleep(delay * 100);
+
+			if (timeout++ > 5000) {
+				fprintf(stderr, "Initialization timed out\n");
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+void put_uart(uint8_t ch)
+{
+	if (write(fd, &ch, 1) < 0)
+		fprintf(stderr, "UART write error\n");
+}
+
+uint8_t get_uart(uint8_t *ch)
+{
+	int res = read(fd, ch, 1);
+	return res > 0 ? res : 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	unsigned char cp[254], rp[254];
+	uint8_t cmd[10];
+	uint16_t size;
+	uint8_t delay, activity = 0x00;
+	int timeout = 0, sent = 0;
+
+	size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+	cmd[0] = command & 0xff;
+	cmd[1] = command >> 8;
+	cmd[2] = size & 0xff;
+	cmd[3] = size >> 8;
+	cmd[4] = seqnum & 0xff;
+	cmd[5] = seqnum >> 8;
+	cmd[6] = varid & 0xff;
+	cmd[7] = varid >> 8;
+	cmd[8] = 0x00;
+	cmd[9] = 0x00;
+
+	memset(cp, 0, sizeof(cp));
+	cp[0] = 0x00;
+	cp[1] = 0xfc;
+	cp[2] = (size * 2) + 1;
+	cp[3] = 0xc2;
+	memcpy(cp + 4, cmd, sizeof(cmd));
+	memcpy(cp + 14, value, length);
+
+	receive_packet.length = 512;
+	ubcsp_receive_packet(&receive_packet);
+
+	send_packet.channel  = 5;
+	send_packet.reliable = 1;
+	send_packet.length   = (size * 2) + 4;
+	memcpy(send_packet.payload, cp, (size * 2) + 4);
+
+	ubcsp_send_packet(&send_packet);
+
+	while (1) {
+		delay = ubcsp_poll(&activity);
+
+		if (activity & UBCSP_PACKET_SENT) {
+			switch (varid) {
+			case CSR_VARID_COLD_RESET:
+			case CSR_VARID_WARM_RESET:
+			case CSR_VARID_COLD_HALT:
+			case CSR_VARID_WARM_HALT:
+				return 0;
+			}
+
+			sent = 1;
+			timeout = 0;
+		}
+
+		if (activity & UBCSP_PACKET_RECEIVED) {
+			if (sent && receive_packet.channel == 5 &&
+					receive_packet.payload[0] == 0xff) {
+				memcpy(rp, receive_packet.payload,
+							receive_packet.length);
+				break;
+			}
+
+			receive_packet.length = 512;
+			ubcsp_receive_packet(&receive_packet);
+			timeout = 0;
+		}
+
+		if (delay) {
+			usleep(delay * 100);
+
+			if (timeout++ > 5000) {
+				fprintf(stderr, "Operation timed out\n");
+				errno = ETIMEDOUT;
+				return -1;
+			}
+		}
+	}
+
+	if (rp[0] != 0xff || rp[2] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[11] + (rp[12] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	memcpy(value, rp + 13, length);
+
+	return 0;
+}
+
+int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_bcsp(void)
+{
+	close(fd);
+}
diff --git a/bluez/repo/tools/csr_h4.c b/bluez/repo/tools/csr_h4.c
new file mode 100644
index 0000000..3371770
--- /dev/null
+++ b/bluez/repo/tools/csr_h4.c
@@ -0,0 +1,165 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int fd = -1;
+
+int csr_open_h4(char *device)
+{
+	struct termios ti;
+
+	if (!device)
+		device = "/dev/ttyS0";
+
+	fd = open(device, O_RDWR | O_NOCTTY);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open serial port: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (tcgetattr(fd, &ti) < 0) {
+		fprintf(stderr, "Can't get port settings: %s (%d)\n",
+						strerror(errno), errno);
+		close(fd);
+		return -1;
+	}
+
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= CLOCAL;
+	ti.c_cflag |= CRTSCTS;
+
+	cfsetospeed(&ti, B38400);
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		fprintf(stderr, "Can't change port settings: %s (%d)\n",
+						strerror(errno), errno);
+		close(fd);
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	unsigned char cp[254], rp[254];
+	uint8_t cmd[10];
+	uint16_t size;
+	int len, offset = 3;
+
+	size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+	cmd[0] = command & 0xff;
+	cmd[1] = command >> 8;
+	cmd[2] = size & 0xff;
+	cmd[3] = size >> 8;
+	cmd[4] = seqnum & 0xff;
+	cmd[5] = seqnum >> 8;
+	cmd[6] = varid & 0xff;
+	cmd[7] = varid >> 8;
+	cmd[8] = 0x00;
+	cmd[9] = 0x00;
+
+	memset(cp, 0, sizeof(cp));
+	cp[0] = 0x01;
+	cp[1] = 0x00;
+	cp[2] = 0xfc;
+	cp[3] = (size * 2) + 1;
+	cp[4] = 0xc2;
+	memcpy(cp + 5, cmd, sizeof(cmd));
+	memcpy(cp + 15, value, length);
+
+	if (write(fd, cp, (size * 2) + 5) < 0)
+		return -1;
+
+	switch (varid) {
+	case CSR_VARID_COLD_RESET:
+	case CSR_VARID_WARM_RESET:
+	case CSR_VARID_COLD_HALT:
+	case CSR_VARID_WARM_HALT:
+		return 0;
+	}
+
+	do {
+		if (read(fd, rp, 1) < 1)
+			return -1;
+	} while (rp[0] != 0x04);
+
+	if (read(fd, rp + 1, 2) < 2)
+		return -1;
+
+	do {
+		len = read(fd, rp + offset, sizeof(rp) - offset);
+		offset += len;
+	} while (offset < rp[2] + 3);
+
+	if (rp[0] != 0x04 || rp[1] != 0xff || rp[3] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[12] + (rp[13] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	memcpy(value, rp + 14, length);
+
+	return 0;
+}
+
+int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_h4(void)
+{
+	close(fd);
+}
diff --git a/bluez/repo/tools/csr_hci.c b/bluez/repo/tools/csr_hci.c
new file mode 100644
index 0000000..d2e4ab9
--- /dev/null
+++ b/bluez/repo/tools/csr_hci.c
@@ -0,0 +1,160 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <string.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int dd = -1;
+
+int csr_open_hci(char *device)
+{
+	struct hci_dev_info di;
+	struct hci_version ver;
+	int dev = 0;
+
+	if (device) {
+		dev = hci_devid(device);
+		if (dev < 0) {
+			fprintf(stderr, "Device not available\n");
+			return -1;
+		}
+	}
+
+	dd = hci_open_dev(dev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		return -1;
+	}
+
+	if (hci_devinfo(dev, &di) < 0) {
+		fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		hci_close_dev(dd);
+		return -1;
+	}
+
+	if (hci_read_local_version(dd, &ver, 1000) < 0) {
+		fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		hci_close_dev(dd);
+		return -1;
+	}
+
+	if (ver.manufacturer != 10) {
+		fprintf(stderr, "Unsupported manufacturer\n");
+		hci_close_dev(dd);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+	unsigned char cp[254], rp[254];
+	struct hci_request rq;
+	uint8_t cmd[10];
+	uint16_t size;
+
+	size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+	cmd[0] = command & 0xff;
+	cmd[1] = command >> 8;
+	cmd[2] = size & 0xff;
+	cmd[3] = size >> 8;
+	cmd[4] = seqnum & 0xff;
+	cmd[5] = seqnum >> 8;
+	cmd[6] = varid & 0xff;
+	cmd[7] = varid >> 8;
+	cmd[8] = 0x00;
+	cmd[9] = 0x00;
+
+	memset(cp, 0, sizeof(cp));
+	cp[0] = 0xc2;
+	memcpy(cp + 1, cmd, sizeof(cmd));
+	memcpy(cp + 11, value, length);
+
+	switch (varid) {
+	case CSR_VARID_COLD_RESET:
+	case CSR_VARID_WARM_RESET:
+	case CSR_VARID_COLD_HALT:
+	case CSR_VARID_WARM_HALT:
+		return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, (size * 2) + 1, cp);
+	}
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x00;
+	rq.event  = EVT_VENDOR;
+	rq.cparam = cp;
+	rq.clen   = (size * 2) + 1;
+	rq.rparam = rp;
+	rq.rlen   = sizeof(rp);
+
+	if (hci_send_req(dd, &rq, 2000) < 0)
+		return -1;
+
+	if (rp[0] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[9] + (rp[10] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	memcpy(value, rp + 11, length);
+
+	return 0;
+}
+
+int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_hci(void)
+{
+	hci_close_dev(dd);
+}
diff --git a/bluez/repo/tools/csr_usb.c b/bluez/repo/tools/csr_usb.c
new file mode 100644
index 0000000..a1d7324
--- /dev/null
+++ b/bluez/repo/tools/csr_usb.c
@@ -0,0 +1,303 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+
+#include "csr.h"
+
+#define USB_TYPE_CLASS			(0x01 << 5)
+
+#define USB_RECIP_DEVICE		0x00
+
+#define USB_ENDPOINT_IN			0x80
+#define USB_ENDPOINT_OUT		0x00
+
+struct usbfs_ctrltransfer {
+	uint8_t  bmRequestType;
+	uint8_t  bRequest;
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+	uint32_t timeout;	/* in milliseconds */
+	void *data;		/* pointer to data */
+};
+
+struct usbfs_bulktransfer {
+	unsigned int ep;
+	unsigned int len;
+	unsigned int timeout;   /* in milliseconds */
+	void *data;		/* pointer to data */
+};
+
+#define USBFS_IOCTL_CONTROL	_IOWR('U', 0, struct usbfs_ctrltransfer)
+#define USBFS_IOCTL_BULK	_IOWR('U', 2, struct usbfs_bulktransfer)
+#define USBFS_IOCTL_CLAIMINTF	_IOR('U', 15, unsigned int)
+#define USBFS_IOCTL_RELEASEINTF	_IOR('U', 16, unsigned int)
+
+static int read_value(const char *name, const char *attr, const char *format)
+{
+	char path[PATH_MAX];
+	FILE *file;
+	int n, value;
+
+	snprintf(path, sizeof(path), "/sys/bus/usb/devices/%s/%s", name, attr);
+
+	file = fopen(path, "r");
+	if (!file)
+		return -1;
+
+	n = fscanf(file, format, &value);
+	if (n != 1) {
+		fclose(file);
+		return -1;
+	}
+
+	fclose(file);
+	return value;
+}
+
+static char *check_device(const char *name)
+{
+	char path[PATH_MAX];
+	int busnum, devnum, vendor, product;
+
+	busnum = read_value(name, "busnum", "%d");
+	if (busnum < 0)
+		return NULL;
+
+	devnum = read_value(name, "devnum", "%d");
+	if (devnum < 0)
+		return NULL;
+
+	snprintf(path, sizeof(path), "/dev/bus/usb/%03u/%03u", busnum, devnum);
+
+	vendor = read_value(name, "idVendor", "%04x");
+	if (vendor < 0)
+		return NULL;
+
+	product = read_value(name, "idProduct", "%04x");
+	if (product < 0)
+		return NULL;
+
+	if (vendor != 0x0a12 || product != 0x0001)
+		return NULL;
+
+	return strdup(path);
+}
+
+static char *find_device(void)
+{
+	char *path = NULL;
+	DIR *dir;
+
+	dir = opendir("/sys/bus/usb/devices");
+	if (!dir)
+		return NULL;
+
+	while (1) {
+		struct dirent *d;
+
+		d = readdir(dir);
+		if (!d)
+			break;
+
+		if ((!isdigit(d->d_name[0]) && strncmp(d->d_name, "usb", 3))
+						|| strchr(d->d_name, ':'))
+			continue;
+
+		path = check_device(d->d_name);
+		if (path)
+			break;
+	}
+
+	closedir(dir);
+
+	return path;
+}
+
+static uint16_t seqnum = 0x0000;
+static int handle = -1;
+
+int csr_open_usb(char *device)
+{
+	int interface = 0;
+	char *path;
+
+	path = find_device();
+	if (!path) {
+		fprintf(stderr, "Device not available\n");
+		return -1;
+	}
+
+	handle = open(path, O_RDWR, O_CLOEXEC | O_NONBLOCK);
+
+	free(path);
+
+	if (handle < 0) {
+		fprintf(stderr, "Can't open device: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	if (ioctl(handle, USBFS_IOCTL_CLAIMINTF, &interface) < 0) {
+		fprintf(stderr, "Can't claim interface: %s (%d)\n",
+						strerror(errno), errno);
+		close(handle);
+		handle = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int control_write(int fd, void *data, unsigned short size)
+{
+	struct usbfs_ctrltransfer transfer;
+
+	transfer.bmRequestType = USB_TYPE_CLASS | USB_ENDPOINT_OUT |
+							USB_RECIP_DEVICE;
+	transfer.bRequest = 0;
+	transfer.wValue = 0;
+	transfer.wIndex = 0;
+	transfer.wLength = size,
+	transfer.timeout = 2000;
+	transfer.data = data;
+
+	if (ioctl(fd, USBFS_IOCTL_CONTROL, &transfer) < 0) {
+		fprintf(stderr, "Control transfer failed: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int interrupt_read(int fd, unsigned char endpoint,
+					void *data, unsigned short size)
+{
+	struct usbfs_bulktransfer transfer;
+
+	transfer.ep = endpoint;
+	transfer.len = size,
+	transfer.timeout = 20;
+	transfer.data = data;
+
+	return ioctl(fd, USBFS_IOCTL_BULK, &transfer);
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid,
+					uint8_t *value, uint16_t length)
+{
+	unsigned char cp[254], rp[254];
+	uint8_t cmd[10];
+	uint16_t size;
+	int len, offset = 0;
+
+	size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+	cmd[0] = command & 0xff;
+	cmd[1] = command >> 8;
+	cmd[2] = size & 0xff;
+	cmd[3] = size >> 8;
+	cmd[4] = seqnum & 0xff;
+	cmd[5] = seqnum >> 8;
+	cmd[6] = varid & 0xff;
+	cmd[7] = varid >> 8;
+	cmd[8] = 0x00;
+	cmd[9] = 0x00;
+
+	memset(cp, 0, sizeof(cp));
+	cp[0] = 0x00;
+	cp[1] = 0xfc;
+	cp[2] = (size * 2) + 1;
+	cp[3] = 0xc2;
+	memcpy(cp + 4, cmd, sizeof(cmd));
+	memcpy(cp + 14, value, length);
+
+	interrupt_read(handle, USB_ENDPOINT_IN | 0x01, rp, sizeof(rp));
+
+	control_write(handle, cp, (size * 2) + 4);
+
+	switch (varid) {
+	case CSR_VARID_COLD_RESET:
+	case CSR_VARID_WARM_RESET:
+	case CSR_VARID_COLD_HALT:
+	case CSR_VARID_WARM_HALT:
+		return 0;
+	}
+
+	do {
+		len = interrupt_read(handle, USB_ENDPOINT_IN | 0x01,
+					rp + offset, sizeof(rp) - offset);
+		if (len < 0)
+			break;
+		offset += len;
+	} while (len > 0);
+
+	if (rp[0] != 0xff || rp[2] != 0xc2) {
+		errno = EIO;
+		return -1;
+	}
+
+	if ((rp[11] + (rp[12] << 8)) != 0) {
+		errno = ENXIO;
+		return -1;
+	}
+
+	memcpy(value, rp + 13, length);
+
+	return 0;
+}
+
+int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length)
+{
+	return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_usb(void)
+{
+	int interface = 0;
+
+	ioctl(handle, USBFS_IOCTL_RELEASEINTF, &interface);
+
+	close(handle);
+	handle = -1;
+}
diff --git a/bluez/repo/tools/eddystone.c b/bluez/repo/tools/eddystone.c
new file mode 100644
index 0000000..23a34f9
--- /dev/null
+++ b/bluez/repo/tools/eddystone.c
@@ -0,0 +1,319 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+static int urandom_fd;
+static struct bt_hci *hci_dev;
+
+static bool shutdown_timeout(void *user_data)
+{
+	mainloop_quit();
+
+	return false;
+}
+
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
+{
+	unsigned int id = PTR_TO_UINT(user_data);
+
+	timeout_remove(id);
+	mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+	uint8_t enable = 0x00;
+	unsigned int id;
+
+	bt_hci_flush(hci_dev);
+
+	id = timeout_add(5000, shutdown_timeout, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+					&enable, 1, NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+				shutdown_complete, UINT_TO_PTR(id), NULL);
+}
+
+static void set_random_address(void)
+{
+	struct bt_hci_cmd_le_set_random_address cmd;
+	ssize_t len;
+
+	len = read(urandom_fd, cmd.addr, sizeof(cmd.addr));
+	if (len < 0 || len != sizeof(cmd.addr)) {
+		fprintf(stderr, "Failed to read random data\n");
+		return;
+	}
+
+	/* Clear top most significant bits */
+	cmd.addr[5] &= 0x3f;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
+static void set_adv_parameters(void)
+{
+	struct bt_hci_cmd_le_set_adv_parameters cmd;
+
+	cmd.min_interval = cpu_to_le16(0x0800);
+	cmd.max_interval = cpu_to_le16(0x0800);
+	cmd.type = 0x03;		/* Non-connectable advertising */
+	cmd.own_addr_type = 0x01;	/* Use random address */
+	cmd.direct_addr_type = 0x00;
+	memset(cmd.direct_addr, 0, 6);
+	cmd.channel_map = 0x07;
+	cmd.filter_policy = 0x00;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
+static void set_adv_enable(void)
+{
+	uint8_t enable = 0x01;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+					&enable, 1, NULL, NULL, NULL);
+}
+
+static void adv_data_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to set advertising data\n");
+		shutdown_device();
+		return;
+	}
+
+	set_random_address();
+	set_adv_parameters();
+	set_adv_enable();
+}
+
+static void adv_tx_power_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
+	struct bt_hci_cmd_le_set_adv_data cmd;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read advertising TX power\n");
+		shutdown_device();
+		return;
+	}
+
+	cmd.data[0] = 0x02;		/* Field length */
+	cmd.data[1] = 0x01;		/* Flags */
+	cmd.data[2] = 0x02;		/* LE General Discoverable Mode */
+	cmd.data[2] |= 0x04;		/* BR/EDR Not Supported */
+
+	cmd.data[3] = 0x03;		/* Field length */
+	cmd.data[4] = 0x03;		/* 16-bit Service UUID list */
+	cmd.data[5] = 0xaa;		/* Eddystone UUID */
+	cmd.data[6] = 0xfe;
+
+	cmd.data[7] = 0x0c;		/* Field length */
+	cmd.data[8] = 0x16;		/* 16-bit Service UUID data */
+	cmd.data[9] = 0xaa;		/* Eddystone UUID */
+	cmd.data[10] = 0xfe;
+	cmd.data[11] = 0x10;		/* Eddystone-URL frame type */
+	cmd.data[12] = 0x00;		/* Calibrated Tx power at 0m */
+	cmd.data[13] = 0x00;		/* URL Scheme Prefix http://www. */
+	cmd.data[14] = 'b';
+	cmd.data[15] = 'l';
+	cmd.data[16] = 'u';
+	cmd.data[17] = 'e';
+	cmd.data[18] = 'z';
+	cmd.data[19] = 0x01;		/* .org/ */
+
+	cmd.data[20] = 0x00;		/* Field terminator */
+	memset(cmd.data + 21, 0, 9);
+
+	cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3] + 1 + cmd.data[7];
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
+					adv_data_callback, NULL, NULL);
+}
+
+static void local_features_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_features *rsp = data;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read local features\n");
+		shutdown_device();
+		return;
+	}
+
+	if (!(rsp->features[4] & 0x40)) {
+		fprintf(stderr, "Controller without Low Energy support\n");
+		shutdown_device();
+		return;
+	}
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
+					adv_tx_power_callback, NULL, NULL);
+}
+
+static void start_eddystone(void)
+{
+	bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+					local_features_callback, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	static bool terminated = false;
+
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		if (!terminated) {
+			shutdown_device();
+			terminated = true;
+		}
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("eddystone - Low Energy Eddystone testing tool\n"
+		"Usage:\n");
+	printf("\teddystone [options]\n");
+	printf("Options:\n"
+		"\t-i, --index <num>      Use specified controller\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "index",   required_argument, NULL, 'i' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	uint16_t index = 0;
+	const char *str;
+	sigset_t mask;
+	int exit_status;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				usage();
+				return EXIT_FAILURE;
+			}
+			index = atoi(str);
+			break;
+		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;
+	}
+
+	urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (urandom_fd < 0) {
+		fprintf(stderr, "Failed to open /dev/urandom device\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Low Energy Eddystone utility ver %s\n", VERSION);
+
+	hci_dev = bt_hci_new_user_channel(index);
+	if (!hci_dev) {
+		fprintf(stderr, "Failed to open HCI user channel\n");
+		exit_status = EXIT_FAILURE;
+		goto done;
+	}
+
+	start_eddystone();
+
+	exit_status = mainloop_run();
+
+	bt_hci_unref(hci_dev);
+
+done:
+	close(urandom_fd);
+
+	return exit_status;
+}
diff --git a/bluez/repo/tools/example.psr b/bluez/repo/tools/example.psr
new file mode 100644
index 0000000..bbbec73
--- /dev/null
+++ b/bluez/repo/tools/example.psr
@@ -0,0 +1,12 @@
+// PSKEY_BDADDR
+&0001 = 0001 2821 005b 6789
+// PSKEY_ANA_FTRIM
+&01f6 = 0025
+// PSKEY_HOST_INTERFACE
+&01f9 = 0001
+// PSKEY_UART_BAUD_RATE
+&0204 = 01d8
+// PSKEY_ANA_FREQ
+&01fe = 0004
+// PSKEY_UART_CONFIG
+&0205 = 0006
diff --git a/bluez/repo/tools/gap-tester.c b/bluez/repo/tools/gap-tester.c
new file mode 100644
index 0000000..2aa4042
--- /dev/null
+++ b/bluez/repo/tools/gap-tester.c
@@ -0,0 +1,139 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gdbus/gdbus.h"
+
+#include "src/shared/tester.h"
+#include "emulator/hciemu.h"
+
+static DBusConnection *dbus_conn = NULL;
+static GDBusClient *dbus_client = NULL;
+static GDBusProxy *adapter_proxy = NULL;
+
+static struct hciemu *hciemu_stack = NULL;
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+	tester_print("Connected to daemon");
+
+	hciemu_stack = hciemu_new(HCIEMU_TYPE_BREDRLE);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	tester_print("Disconnected from daemon");
+
+	dbus_connection_unref(dbus_conn);
+	dbus_conn = NULL;
+
+	tester_teardown_complete();
+}
+
+static gboolean compare_string_property(GDBusProxy *proxy, const char *name,
+							const char *value)
+{
+	DBusMessageIter iter;
+	const char *str;
+
+	if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
+		return FALSE;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(&iter, &str);
+
+	return g_str_equal(str, value);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (g_str_equal(interface, "org.bluez.Adapter1") == TRUE) {
+		if (compare_string_property(proxy, "Address",
+				hciemu_get_address(hciemu_stack)) == TRUE) {
+			adapter_proxy = proxy;
+			tester_print("Found adapter");
+
+			tester_setup_complete();
+		}
+	}
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (g_str_equal(interface, "org.bluez.Adapter1") == TRUE) {
+		if (adapter_proxy == proxy) {
+			adapter_proxy = NULL;
+			tester_print("Adapter removed");
+
+			g_dbus_client_unref(dbus_client);
+			dbus_client = NULL;
+		}
+	}
+}
+
+static void test_setup(const void *test_data)
+{
+	dbus_conn = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
+
+	dbus_client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+
+	g_dbus_client_set_connect_watch(dbus_client, connect_handler, NULL);
+	g_dbus_client_set_disconnect_watch(dbus_client,
+						disconnect_handler, NULL);
+
+	g_dbus_client_set_proxy_handlers(dbus_client, proxy_added,
+						proxy_removed, NULL, NULL);
+}
+
+static void test_run(const void *test_data)
+{
+	tester_test_passed();
+}
+
+static void test_teardown(const void *test_data)
+{
+	hciemu_unref(hciemu_stack);
+	hciemu_stack = NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("Adapter setup", NULL, test_setup, test_run, test_teardown);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/gatt-service.c b/bluez/repo/tools/gatt-service.c
new file mode 100644
index 0000000..9baa9e1
--- /dev/null
+++ b/bluez/repo/tools/gatt-service.c
@@ -0,0 +1,537 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/signalfd.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/error.h"
+
+#define GATT_MGR_IFACE			"org.bluez.GattManager1"
+#define GATT_SERVICE_IFACE		"org.bluez.GattService1"
+#define GATT_CHR_IFACE			"org.bluez.GattCharacteristic1"
+#define GATT_DESCRIPTOR_IFACE		"org.bluez.GattDescriptor1"
+
+/* Immediate Alert Service UUID */
+#define IAS_UUID			"00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID		"00002a06-0000-1000-8000-00805f9b34fb"
+
+/* Random UUID for testing purpose */
+#define READ_WRITE_DESCRIPTOR_UUID	"8260c653-1a54-426b-9e36-e84c238bc669"
+
+static GMainLoop *main_loop;
+static GSList *services;
+static DBusConnection *connection;
+
+struct characteristic {
+	char *uuid;
+	char *path;
+	uint8_t *value;
+	int vlen;
+	const char **props;
+};
+
+struct descriptor {
+	char *uuid;
+	char *path;
+	uint8_t *value;
+	int vlen;
+};
+
+/*
+ * Alert Level support Write Without Response only. Supported
+ * properties are defined at doc/gatt-api.txt. See "Flags"
+ * property of the GattCharacteristic1.
+ */
+static const char *ias_alert_level_props[] = { "write-without-response", NULL };
+
+static gboolean desc_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct descriptor *desc = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
+
+	return TRUE;
+}
+
+static gboolean desc_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct descriptor *desc = user_data;
+	DBusMessageIter array;
+
+	printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_BYTE_AS_STRING, &array);
+
+	if (desc->vlen && desc->value)
+		dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&desc->value, desc->vlen);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static void desc_set_value(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct descriptor *desc = user_data;
+	DBusMessageIter array;
+	const uint8_t *value;
+	int vlen;
+
+	printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &vlen);
+
+	g_free(desc->value);
+	desc->value = g_memdup(value, vlen);
+	desc->vlen = vlen;
+
+	g_dbus_pending_property_success(id);
+
+	g_dbus_emit_property_changed(connection, desc->path,
+					GATT_DESCRIPTOR_IFACE, "Value");
+
+}
+
+static const GDBusPropertyTable desc_properties[] = {
+	{ "UUID",		"s",	desc_get_uuid },
+	{ "Value",		"ay",	desc_get_value, desc_set_value, NULL },
+	{ }
+};
+
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct characteristic *chr = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+	return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct characteristic *chr = user_data;
+	DBusMessageIter array;
+
+	printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_BYTE_AS_STRING, &array);
+
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+						&chr->value, chr->vlen);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static gboolean chr_get_props(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chr = data;
+	DBusMessageIter array;
+	int i;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &array);
+
+	for (i = 0; chr->props[i]; i++)
+		dbus_message_iter_append_basic(&array,
+					DBUS_TYPE_STRING, &chr->props[i]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static void chr_set_value(const GDBusPropertyTable *property,
+				DBusMessageIter *iter,
+				GDBusPendingPropertySet id, void *user_data)
+{
+	struct characteristic *chr = user_data;
+	DBusMessageIter array;
+	uint8_t *value;
+	int len;
+
+	printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+		printf("Invalid value for Set('Value'...)\n");
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_recurse(iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+	g_free(chr->value);
+	chr->value = g_memdup(value, len);
+	chr->vlen = len;
+
+	g_dbus_pending_property_success(id);
+	g_dbus_emit_property_changed(connection, chr->path,
+						GATT_CHR_IFACE, "Value");
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+	{ "UUID",	"s",	chr_get_uuid },
+	{ "Value", "ay", chr_get_value, chr_set_value, NULL },
+	{ "Flags", "as", chr_get_props, NULL, NULL },
+	{ }
+};
+
+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	const char *uuid = user_data;
+
+	printf("Get UUID: %s\n", uuid);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+	return TRUE;
+}
+
+static gboolean service_get_includes(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	const char *uuid = user_data;
+
+	printf("Get Includes: %s\n", uuid);
+
+	return TRUE;
+}
+
+static gboolean service_exist_includes(const GDBusPropertyTable *property,
+							void *user_data)
+{
+	const char *uuid = user_data;
+
+	printf("Exist Includes: %s\n", uuid);
+
+	return FALSE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+	{ "UUID", "s", service_get_uuid },
+	{ "Includes", "ao", service_get_includes, NULL,
+					service_exist_includes },
+	{ }
+};
+
+static void chr_iface_destroy(gpointer user_data)
+{
+	struct characteristic *chr = user_data;
+
+	g_free(chr->uuid);
+	g_free(chr->value);
+	g_free(chr->path);
+	g_free(chr);
+}
+
+static void desc_iface_destroy(gpointer user_data)
+{
+	struct descriptor *desc = user_data;
+
+	g_free(desc->uuid);
+	g_free(desc->value);
+	g_free(desc->path);
+	g_free(desc);
+}
+
+static gboolean register_characteristic(const char *chr_uuid,
+						const uint8_t *value, int vlen,
+						const char **props,
+						const char *desc_uuid,
+						const char *service_path)
+{
+	struct characteristic *chr;
+	struct descriptor *desc;
+	static int id = 1;
+
+	chr = g_new0(struct characteristic, 1);
+	chr->uuid = g_strdup(chr_uuid);
+	chr->value = g_memdup(value, vlen);
+	chr->vlen = vlen;
+	chr->props = props;
+	chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+	if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE,
+					NULL, NULL, chr_properties,
+					chr, chr_iface_destroy)) {
+		printf("Couldn't register characteristic interface\n");
+		chr_iface_destroy(chr);
+		return FALSE;
+	}
+
+	if (!desc_uuid)
+		return TRUE;
+
+	desc = g_new0(struct descriptor, 1);
+	desc->uuid = g_strdup(desc_uuid);
+	desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++);
+
+	if (!g_dbus_register_interface(connection, desc->path,
+					GATT_DESCRIPTOR_IFACE,
+					NULL, NULL, desc_properties,
+					desc, desc_iface_destroy)) {
+		printf("Couldn't register descriptor interface\n");
+		g_dbus_unregister_interface(connection, chr->path,
+							GATT_CHR_IFACE);
+
+		desc_iface_destroy(desc);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static char *register_service(const char *uuid)
+{
+	static int id = 1;
+	char *path;
+
+	path = g_strdup_printf("/service%d", id++);
+	if (!g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
+				NULL, NULL, service_properties,
+				g_strdup(uuid), g_free)) {
+		printf("Couldn't register service interface\n");
+		g_free(path);
+		return NULL;
+	}
+
+	return path;
+}
+
+static void create_services()
+{
+	char *service_path;
+	uint8_t level = 0;
+
+	service_path = register_service(IAS_UUID);
+	if (!service_path)
+		return;
+
+	/* Add Alert Level Characteristic to Immediate Alert Service */
+	if (!register_characteristic(ALERT_LEVEL_CHR_UUID,
+						&level, sizeof(level),
+						ias_alert_level_props,
+						READ_WRITE_DESCRIPTOR_UUID,
+						service_path)) {
+		printf("Couldn't register Alert Level characteristic (IAS)\n");
+		g_dbus_unregister_interface(connection, service_path,
+							GATT_SERVICE_IFACE);
+		g_free(service_path);
+		return;
+	}
+
+	services = g_slist_prepend(services, service_path);
+	printf("Registered service: %s\n", service_path);
+}
+
+static void register_external_service_reply(DBusPendingCall *call,
+							void *user_data)
+{
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	dbus_error_init(&derr);
+	dbus_set_error_from_message(&derr, reply);
+
+	if (dbus_error_is_set(&derr))
+		printf("RegisterService: %s\n", derr.message);
+	else
+		printf("RegisterService: OK\n");
+
+	dbus_message_unref(reply);
+	dbus_error_free(&derr);
+}
+
+static void register_external_service(gpointer a, gpointer b)
+{
+	DBusConnection *conn = b;
+	const char *path = a;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	DBusMessageIter iter, dict;
+
+	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
+					GATT_MGR_IFACE, "RegisterService");
+	if (!msg) {
+		printf("Couldn't allocate D-Bus message\n");
+		return;
+	}
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+	/* TODO: Add options dictionary */
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
+		dbus_message_unref(msg);
+		return;
+	}
+
+	dbus_pending_call_set_notify(call, register_external_service_reply,
+								NULL, NULL);
+
+	dbus_pending_call_unref(call);
+}
+
+static void connect_handler(DBusConnection *conn, void *user_data)
+{
+	g_slist_foreach(services, register_external_service, conn);
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	static bool __terminated = false;
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		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:
+	case SIGTERM:
+		if (!__terminated) {
+			printf("Terminating\n");
+			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;
+}
+
+int main(int argc, char *argv[])
+{
+	GDBusClient *client;
+	guint signal;
+
+	signal = setup_signalfd();
+	if (signal == 0)
+		return -errno;
+
+	connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	g_dbus_attach_object_manager(connection);
+
+	printf("gatt-service unique name: %s\n",
+				dbus_bus_get_unique_name(connection));
+
+	create_services();
+
+	client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
+
+	g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+
+	g_main_loop_run(main_loop);
+
+	g_dbus_client_unref(client);
+
+	g_source_remove(signal);
+
+	g_slist_free_full(services, g_free);
+	dbus_connection_unref(connection);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hci-tester.c b/bluez/repo/tools/hci-tester.c
new file mode 100644
index 0000000..75d09f0
--- /dev/null
+++ b/bluez/repo/tools/hci-tester.c
@@ -0,0 +1,953 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "monitor/bt.h"
+#include "src/shared/hci.h"
+#include "src/shared/util.h"
+#include "src/shared/ecc.h"
+#include "src/shared/tester.h"
+
+struct user_data {
+	const void *test_data;
+	uint16_t index_ut;
+	uint16_t index_lt;
+	struct bt_hci *hci_ut;		/* Upper Tester / IUT */
+	struct bt_hci *hci_lt;		/* Lower Tester / Reference */
+
+	uint8_t bdaddr_ut[6];
+	uint8_t bdaddr_lt[6];
+	uint16_t handle_ut;
+};
+
+struct le_keys {
+	uint8_t remote_sk[32];
+	uint8_t local_pk[64];
+} key_test_data;
+
+static void swap_buf(const uint8_t *src, uint8_t *dst, uint16_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		dst[len - 1 - i] = src[i];
+}
+
+static void test_debug(const char *str, void *user_data)
+{
+	tester_debug("%s", str);
+}
+
+static void test_pre_setup_lt_address(const void *data, uint8_t size,
+							void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+	if (rsp->status) {
+		tester_warn("Read lower tester address failed (0x%02x)",
+								rsp->status);
+		tester_pre_setup_failed();
+		return;
+	}
+
+	memcpy(user->bdaddr_lt, rsp->bdaddr, 6);
+
+	tester_pre_setup_complete();
+}
+
+static void test_pre_setup_lt_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("Reset lower tester failed (0x%02x)", status);
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_READ_BD_ADDR, NULL, 0,
+				test_pre_setup_lt_address, NULL, NULL)) {
+		tester_warn("Failed to read lower tester address");
+		tester_pre_setup_failed();
+		return;
+	}
+}
+
+static void test_pre_setup_ut_address(const void *data, uint8_t size,
+							void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+	if (rsp->status) {
+		tester_warn("Read upper tester address failed (0x%02x)",
+								rsp->status);
+		tester_pre_setup_failed();
+		return;
+	}
+
+	memcpy(user->bdaddr_ut, rsp->bdaddr, 6);
+
+	user->hci_lt = bt_hci_new_user_channel(user->index_lt);
+	if (!user->hci_lt) {
+		tester_warn("Failed to setup lower tester user channel");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_RESET, NULL, 0,
+				test_pre_setup_lt_complete, NULL, NULL)) {
+		tester_warn("Failed to reset lower tester");
+		tester_pre_setup_failed();
+		return;
+	}
+}
+
+static void test_pre_setup_ut_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("Reset upper tester failed (0x%02x)", status);
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (user->index_lt == 0xffff) {
+		tester_pre_setup_complete();
+		return;
+	}
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_BD_ADDR, NULL, 0,
+				test_pre_setup_ut_address, NULL, NULL)) {
+		tester_warn("Failed to read upper tester address");
+		tester_pre_setup_failed();
+		return;
+	}
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+
+	user->hci_ut = bt_hci_new_user_channel(user->index_ut);
+	if (!user->hci_ut) {
+		tester_warn("Failed to setup upper tester user channel");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_RESET, NULL, 0,
+				test_pre_setup_ut_complete, NULL, NULL)) {
+		tester_warn("Failed to reset upper tester");
+		tester_pre_setup_failed();
+		return;
+	}
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+
+	bt_hci_unref(user->hci_lt);
+	user->hci_lt = NULL;
+
+	bt_hci_unref(user->hci_ut);
+	user->hci_ut = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void user_data_free(void *data)
+{
+	struct user_data *user = data;
+
+	free(user);
+}
+
+#define test_hci(name, data, setup, func, teardown) \
+	do { \
+		struct user_data *user; \
+		user = calloc(1, sizeof(struct user_data)); \
+		if (!user) \
+			break; \
+		user->test_data = data; \
+		user->index_ut = 0; \
+		user->index_lt = 1; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, teardown, \
+				test_post_teardown, 30, user, user_data_free); \
+	} while (0)
+
+#define test_hci_local(name, data, setup, func) \
+	do { \
+		struct user_data *user; \
+		user = calloc(1, sizeof(struct user_data)); \
+		if (!user) \
+			break; \
+		user->test_data = data; \
+		user->index_ut = 0; \
+		user->index_lt = 0xffff; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 30, user, user_data_free); \
+	} while (0)
+
+static void setup_features_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_features *rsp = data;
+
+	if (rsp->status) {
+		tester_warn("Failed to get HCI features (0x%02x)", rsp->status);
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_features(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+					setup_features_complete, NULL, NULL)) {
+		tester_warn("Failed to send HCI features command");
+		tester_setup_failed();
+		return;
+	}
+}
+
+static void test_reset(const void *test_data)
+{
+	tester_test_passed();
+}
+
+static void test_command_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("HCI command failed (0x%02x)", status);
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+}
+
+static void test_command(uint16_t opcode)
+{
+	struct user_data *user = tester_get_data();
+
+	if (!bt_hci_send(user->hci_ut, opcode, NULL, 0,
+					test_command_complete, NULL, NULL)) {
+		tester_warn("Failed to send HCI command 0x%04x", opcode);
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_read_local_version_information(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_LOCAL_VERSION);
+}
+
+static void test_read_local_supported_commands(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_LOCAL_COMMANDS);
+}
+
+static void test_read_local_supported_features(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_LOCAL_FEATURES);
+}
+
+static void test_local_extended_features_complete(const void *data,
+						uint8_t size, void *user_data)
+{
+	const struct bt_hci_rsp_read_local_ext_features *rsp = data;
+
+	if (rsp->status) {
+		tester_warn("Failed to get HCI extended features (0x%02x)",
+								rsp->status);
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+}
+
+static void test_read_local_extended_features(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_read_local_ext_features cmd;
+
+	cmd.page = 0x00;
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES,
+					&cmd, sizeof(cmd),
+					test_local_extended_features_complete,
+								NULL, NULL)) {
+		tester_warn("Failed to send HCI extended features command");
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_read_buffer_size(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_BUFFER_SIZE);
+}
+
+static void test_read_country_code(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_COUNTRY_CODE);
+}
+
+static void test_read_bd_addr(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_BD_ADDR);
+}
+
+static void test_read_local_supported_codecs(const void *test_data)
+{
+	test_command(BT_HCI_CMD_READ_LOCAL_CODECS);
+}
+
+static void test_le_read_white_list_size(const void *test_data)
+{
+	test_command(BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE);
+}
+
+static void test_le_clear_white_list(const void *test_data)
+{
+	test_command(BT_HCI_CMD_LE_CLEAR_WHITE_LIST);
+}
+
+static void test_le_encrypt_complete(const void *data, uint8_t size,
+								void *user_data)
+{
+	const struct bt_hci_rsp_le_encrypt *rsp = data;
+	uint8_t sample[16] = {
+		0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3,
+		0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f
+	};
+	uint8_t enc_data[16];
+
+	if (rsp->status) {
+		tester_warn("Failed HCI LE Encrypt (0x%02x)", rsp->status);
+		tester_test_failed();
+		return;
+	}
+
+	swap_buf(rsp->data, enc_data, 16);
+	util_hexdump('>', enc_data, 16, test_debug, NULL);
+
+	if (!memcmp(sample, enc_data, 16))
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+/* Data are taken from RFC 4493 Test Vectors */
+static void test_le_encrypt(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_le_encrypt cmd;
+	uint8_t key[16] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	uint8_t plaintext[16] = { 0 };
+
+	/* Swap bytes since our interface has LE interface, opposed to
+	 * common crypto interface
+	 */
+	swap_buf(key, cmd.key, 16);
+	swap_buf(plaintext, cmd.plaintext, 16);
+
+	util_hexdump('<', cmd.key, 16, test_debug, NULL);
+	util_hexdump('<', cmd.plaintext, 16, test_debug, NULL);
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_ENCRYPT, &cmd, sizeof(cmd),
+					test_le_encrypt_complete, NULL, NULL)) {
+		tester_warn("Failed to send HCI LE Encrypt command");
+		tester_test_failed();
+		return;
+	}
+
+}
+
+static void test_le_rand(const void *test_data)
+{
+	test_command(BT_HCI_CMD_LE_RAND);
+}
+
+static void test_le_read_local_pk_complete(const void *data, uint8_t size,
+								void *user_data)
+{
+	const uint8_t *event = data;
+	const struct bt_hci_evt_le_read_local_pk256_complete *evt;
+	struct le_keys *keys = user_data;
+
+	if (*event != BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE) {
+		tester_warn("Failed Read Local PK256 command");
+		tester_test_failed();
+		return;
+	}
+
+	evt = (void *)(event + 1);
+	if (evt->status) {
+		tester_warn("HCI Read Local PK complete failed (0x%02x)",
+								evt->status);
+		tester_test_failed();
+		return;
+	}
+
+	memcpy(keys->local_pk, evt->local_pk256, 64);
+
+	util_hexdump('>', evt->local_pk256, 64, test_debug, NULL);
+
+	tester_test_passed();
+}
+
+static void test_le_read_local_pk_status(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("Failed to send DHKey gen cmd (0x%02x)", status);
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_le_read_local_pk(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+
+	bt_hci_register(user->hci_ut, BT_HCI_EVT_LE_META_EVENT,
+				test_le_read_local_pk_complete,
+				(void *)test_data, NULL);
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_READ_LOCAL_PK256, NULL,
+				0, test_le_read_local_pk_status,
+				NULL, NULL)) {
+		tester_warn("Failed to send HCI LE Read Local PK256 command");
+		tester_test_failed();
+		return;
+	}
+}
+
+static void setup_le_read_local_pk_complete(const void *data, uint8_t size,
+								void *user_data)
+{
+	const uint8_t *event = data;
+	const struct bt_hci_evt_le_read_local_pk256_complete *evt;
+	struct le_keys *keys = user_data;
+
+	if (*event != BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE) {
+		tester_warn("Failed Read Local PK256 command");
+		tester_setup_failed();
+		return;
+	}
+
+	evt = (void *)(event + 1);
+	if (evt->status) {
+		tester_warn("HCI Read Local PK complete failed (0x%02x)",
+								evt->status);
+		tester_setup_failed();
+		return;
+	}
+
+	memcpy(keys->local_pk, evt->local_pk256, 64);
+
+	util_hexdump('>', evt->local_pk256, 64, test_debug, NULL);
+
+	tester_setup_complete();
+}
+
+static void setup_le_read_local_pk_status(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("Failed to send DHKey gen cmd (0x%02x)", status);
+		tester_setup_failed();
+		return;
+	}
+}
+
+static void setup_le_generate_dhkey(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+
+	bt_hci_register(user->hci_ut, BT_HCI_EVT_LE_META_EVENT,
+				setup_le_read_local_pk_complete,
+				(void *)test_data, NULL);
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_READ_LOCAL_PK256, NULL,
+				0, setup_le_read_local_pk_status,
+				NULL, NULL)) {
+		tester_warn("Failed to send HCI LE Read Local PK256 command");
+		tester_setup_failed();
+		return;
+	}
+}
+
+static void test_le_generate_dhkey_complete(const void *data, uint8_t size,
+								void *user_data)
+{
+	const uint8_t *event = data;
+	const struct bt_hci_evt_le_generate_dhkey_complete *evt;
+	struct le_keys *keys = user_data;
+	uint8_t dhkey[32];
+
+	if (*event != BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE) {
+		tester_warn("Failed DHKey generation command");
+		tester_test_failed();
+		return;
+	}
+
+	evt = (void *)(event + 1);
+	if (evt->status) {
+		tester_warn("HCI Generate DHKey complete failed (0x%02x)",
+								evt->status);
+		tester_test_failed();
+		return;
+	}
+
+	util_hexdump('>', evt->dhkey, 32, test_debug, NULL);
+
+
+	util_hexdump('S', keys->remote_sk, 32, test_debug, NULL);
+	util_hexdump('P', keys->local_pk, 64, test_debug, NULL);
+
+	/* Generate DHKey ourself with local public key and remote
+	 * private key we got when generated public / private key
+	 * pair for BT_HCI_CMD_LE_GENERATE_DHKEY argument.
+	 */
+	ecdh_shared_secret(keys->local_pk, keys->remote_sk, dhkey);
+
+	util_hexdump('D', dhkey, 32, test_debug, NULL);
+
+	if (!memcmp(dhkey, evt->dhkey, 32))
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+static void test_le_generate_dhkey_status(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("Failed to send DHKey gen cmd (0x%02x)", status);
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_le_generate_dhkey(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_le_generate_dhkey cmd;
+	struct le_keys *keys = (void *)test_data;
+
+	ecc_make_key(cmd.remote_pk256, keys->remote_sk);
+
+	/* Unregister handler for META event */
+	bt_hci_unregister(user->hci_ut, 1);
+
+	bt_hci_register(user->hci_ut, BT_HCI_EVT_LE_META_EVENT,
+				test_le_generate_dhkey_complete, keys,
+				NULL);
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_GENERATE_DHKEY, &cmd,
+				sizeof(cmd), test_le_generate_dhkey_status,
+				NULL, NULL)) {
+		tester_warn("Failed to send HCI LE Encrypt command");
+		tester_test_failed();
+		return;
+	}
+
+}
+
+static void test_inquiry_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_evt_inquiry_complete *evt = data;
+
+	if (evt->status) {
+		tester_warn("HCI inquiry complete failed (0x%02x)",
+							evt->status);
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+}
+
+static void test_inquiry_status(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("HCI inquiry command failed (0x%02x)", status);
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_inquiry_liac(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_inquiry cmd;
+
+	bt_hci_register(user->hci_ut, BT_HCI_EVT_INQUIRY_COMPLETE,
+					test_inquiry_complete, NULL, NULL);
+
+	cmd.lap[0] = 0x00;
+	cmd.lap[1] = 0x8b;
+	cmd.lap[2] = 0x9e;
+	cmd.length = 0x08;
+	cmd.num_resp = 0x00;
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd),
+					test_inquiry_status, NULL, NULL)) {
+		tester_warn("Failed to send HCI inquiry command");
+		tester_test_failed();
+		return;
+	}
+}
+
+static void setup_lt_connectable_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("Failed to set HCI scan enable (0x%02x)", status);
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_lt_connect_request_accept(const void *data, uint8_t size,
+							void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	const struct bt_hci_evt_conn_request *evt = data;
+	struct bt_hci_cmd_accept_conn_request cmd;
+
+	memcpy(cmd.bdaddr, evt->bdaddr, 6);
+	cmd.role = 0x01;
+
+	if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_ACCEPT_CONN_REQUEST,
+					&cmd, sizeof(cmd), NULL, NULL, NULL)) {
+		tester_warn("Failed to send HCI accept connection command");
+		return;
+	}
+}
+
+static void setup_lt_connectable(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_write_scan_enable cmd;
+
+	bt_hci_register(user->hci_lt, BT_HCI_EVT_CONN_REQUEST,
+				setup_lt_connect_request_accept, NULL, NULL);
+
+	cmd.enable = 0x02;
+
+	if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_WRITE_SCAN_ENABLE,
+				&cmd, sizeof(cmd),
+				setup_lt_connectable_complete, NULL, NULL)) {
+		tester_warn("Failed to send HCI scan enable command");
+		tester_setup_failed();
+		return;
+	}
+}
+
+static void test_create_connection_disconnect(void *user_data)
+{
+	tester_test_passed();
+}
+
+static void test_create_connection_complete(const void *data, uint8_t size,
+							void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	const struct bt_hci_evt_conn_complete *evt = data;
+
+	if (evt->status) {
+		tester_warn("HCI create connection complete failed (0x%02x)",
+								evt->status);
+		tester_test_failed();
+		return;
+	}
+
+	user->handle_ut = le16_to_cpu(evt->handle);
+
+	tester_wait(2, test_create_connection_disconnect, NULL);
+}
+
+static void test_create_connection_status(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("HCI create connection command failed (0x%02x)",
+								status);
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_create_connection(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_create_conn cmd;
+
+	bt_hci_register(user->hci_ut, BT_HCI_EVT_CONN_COMPLETE,
+				test_create_connection_complete, NULL, NULL);
+
+	memcpy(cmd.bdaddr, user->bdaddr_lt, 6);
+	cmd.pkt_type = cpu_to_le16(0x0008);
+	cmd.pscan_rep_mode = 0x02;
+	cmd.pscan_mode = 0x00;
+	cmd.clock_offset = cpu_to_le16(0x0000);
+	cmd.role_switch = 0x01;
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_CREATE_CONN,
+						&cmd, sizeof(cmd),
+						test_create_connection_status,
+								NULL, NULL)) {
+		tester_warn("Failed to send HCI create connection command");
+		tester_test_failed();
+		return;
+	}
+}
+
+static void teardown_timeout(void *user_data)
+{
+	tester_teardown_complete();
+}
+
+static void teardown_disconnect_status(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		tester_warn("HCI disconnect failed (0x%02x)", status);
+		tester_teardown_failed();
+		return;
+	}
+
+	tester_wait(1, teardown_timeout, NULL);
+}
+
+static void teardown_connection(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_disconnect cmd;
+
+	cmd.handle = cpu_to_le16(user->handle_ut);
+	cmd.reason = 0x13;
+
+	if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_DISCONNECT,
+						&cmd, sizeof(cmd),
+						teardown_disconnect_status,
+								NULL, NULL)) {
+		tester_warn("Failed to send HCI disconnect command");
+		tester_test_failed();
+		return;
+	}
+}
+
+static void test_adv_report(const void *data, uint8_t size, void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	uint8_t subevent = *((uint8_t *) data);
+	const struct bt_hci_evt_le_adv_report *lar = data + 1;
+
+	switch (subevent) {
+	case BT_HCI_EVT_LE_ADV_REPORT:
+		if (!memcmp(lar->addr, user->bdaddr_ut, 6))
+			tester_setup_complete();
+		break;
+	}
+}
+
+static void setup_advertising_initiated(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_set_event_mask sem;
+	struct bt_hci_cmd_le_set_event_mask lsem;
+	struct bt_hci_cmd_le_set_scan_enable lsse;
+	struct bt_hci_cmd_le_set_adv_parameters lsap;
+	struct bt_hci_cmd_le_set_adv_enable lsae;
+
+	bt_hci_register(user->hci_lt, BT_HCI_EVT_LE_META_EVENT,
+					test_adv_report, NULL, NULL);
+
+	memset(sem.mask, 0, 8);
+	sem.mask[1] |= 0x20;	/* Command Complete */
+	sem.mask[1] |= 0x40;	/* Command Status */
+	sem.mask[7] |= 0x20;	/* LE Meta */
+
+	bt_hci_send(user->hci_lt, BT_HCI_CMD_SET_EVENT_MASK,
+					&sem, sizeof(sem), NULL, NULL, NULL);
+
+	memset(lsem.mask, 0, 8);
+	lsem.mask[0] |= 0x02;	/* LE Advertising Report */
+
+	bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_EVENT_MASK,
+					&lsem, sizeof(lsem), NULL, NULL, NULL);
+
+	lsse.enable = 0x01;
+	lsse.filter_dup = 0x00;
+
+	bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+					&lsse, sizeof(lsse), NULL, NULL, NULL);
+
+	lsap.min_interval = cpu_to_le16(0x0800);
+	lsap.max_interval = cpu_to_le16(0x0800);
+	lsap.type = 0x03;
+	lsap.own_addr_type = 0x00;
+	lsap.direct_addr_type = 0x00;
+	memset(lsap.direct_addr, 0, 6);
+	lsap.channel_map = 0x07;
+	lsap.filter_policy = 0x00;
+
+	bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+					&lsap, sizeof(lsap), NULL, NULL, NULL);
+
+	lsae.enable = 0x01;
+
+	bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+					&lsae, sizeof(lsae), NULL, NULL, NULL);
+}
+
+static void test_reset_in_advertising_state_timeout(void *user_data)
+{
+	struct user_data *user = tester_get_data();
+	struct bt_hci_cmd_le_set_adv_enable lsae;
+	struct bt_hci_cmd_le_set_scan_enable lsse;
+
+	lsae.enable = 0x00;
+
+	bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+					&lsae, sizeof(lsae), NULL, NULL, NULL);
+
+	lsse.enable = 0x00;
+	lsse.filter_dup = 0x00;
+
+	bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+					&lsse, sizeof(lsse), NULL, NULL, NULL);
+
+	tester_test_passed();
+}
+
+static void test_reset_in_advertising_state(const void *test_data)
+{
+	struct user_data *user = tester_get_data();
+
+	bt_hci_send(user->hci_ut, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+	tester_wait(5, test_reset_in_advertising_state_timeout, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_hci_local("Reset", NULL, NULL, test_reset);
+
+	test_hci_local("Read Local Version Information", NULL, NULL,
+				test_read_local_version_information);
+	test_hci_local("Read Local Supported Commands", NULL, NULL,
+				test_read_local_supported_commands);
+	test_hci_local("Read Local Supported Features", NULL, NULL,
+				test_read_local_supported_features);
+	test_hci_local("Read Local Extended Features", NULL,
+				setup_features,
+				test_read_local_extended_features);
+	test_hci_local("Read Buffer Size", NULL, NULL,
+				test_read_buffer_size);
+	test_hci_local("Read Country Code", NULL, NULL,
+				test_read_country_code);
+	test_hci_local("Read BD_ADDR", NULL, NULL,
+				test_read_bd_addr);
+	test_hci_local("Read Local Supported Codecs", NULL, NULL,
+				test_read_local_supported_codecs);
+
+	test_hci_local("LE Read White List Size", NULL, NULL,
+				test_le_read_white_list_size);
+	test_hci_local("LE Clear White List", NULL, NULL,
+				test_le_clear_white_list);
+	test_hci_local("LE Encrypt", NULL, NULL,
+				test_le_encrypt);
+	test_hci_local("LE Rand", NULL, NULL,
+				test_le_rand);
+	test_hci_local("LE Read Local PK", &key_test_data, NULL,
+				test_le_read_local_pk);
+	test_hci_local("LE Generate DHKey", &key_test_data,
+				setup_le_generate_dhkey,
+				test_le_generate_dhkey);
+
+	test_hci_local("Inquiry (LIAC)", NULL, NULL, test_inquiry_liac);
+
+	test_hci("Create Connection", NULL,
+				setup_lt_connectable,
+				test_create_connection,
+				teardown_connection);
+
+	test_hci("TP/DSU/BV-02-C Reset in Advertising State", NULL,
+				setup_advertising_initiated,
+				test_reset_in_advertising_state, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/hciattach.1 b/bluez/repo/tools/hciattach.1
new file mode 100644
index 0000000..d506034
--- /dev/null
+++ b/bluez/repo/tools/hciattach.1
@@ -0,0 +1,158 @@
+.TH HCIATTACH 1 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciattach \- attach serial devices via UART HCI to BlueZ stack
+.SH SYNOPSIS
+.B hciattach
+.RB [\| \-b \|]
+.RB [\| \-n \|]
+.RB [\| \-p \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-s
+.IR speed \|]
+.RB [\| \-l \|]
+.RB [\| \-r \|]
+.I tty
+.IR type \||\| id
+.I speed
+.I flow
+.I bdaddr
+.SH DESCRIPTION
+.LP
+Hciattach is used to attach a serial UART to the Bluetooth stack as HCI
+transport interface.
+.SH OPTIONS
+.TP
+.B \-b
+Send break.
+.TP
+.B \-n
+Don't detach from controlling terminal.
+.TP
+.B \-p
+Print the PID when detaching.
+.TP
+.BI \-t " timeout"
+Specify an initialization timeout.  (Default is 5 seconds.)
+.TP
+.BI \-s " speed"
+Specify an initial speed instead of the hardware default.
+.TP
+.B \-l
+List all available configurations.
+.TP
+.B \-r
+Set the HCI device into raw mode (the kernel and bluetoothd will ignore it).
+.TP
+.I tty
+This specifies the serial device to attach. A leading
+.B /dev
+can be omitted. Examples:
+.B /dev/ttyS1
+.B ttyS2
+.TP
+.IR type \||\| id
+The
+.I type
+or
+.I id
+of the Bluetooth device that is to be attached, i.e. vendor or other device
+specific identifier. Currently supported types are
+.RS
+.TP
+.B type
+.B description
+.TP
+.B any
+Unspecified HCI_UART interface, no vendor specific options
+.TP
+.B ericsson
+Ericsson based modules
+.TP
+.B digi
+Digianswer based cards
+.TP
+.B xircom
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+.B csr
+CSR Casira serial adapter or BrainBoxes serial dongle (BL642)
+.TP
+.B bboxes
+BrainBoxes PCMCIA card (BL620)
+.TP
+.B swave
+Silicon Wave kits
+.TP
+.B bcsp
+Serial adapters using CSR chips with BCSP serial protocol
+.TP
+.B ath3k
+Atheros AR300x based serial Bluetooth device
+.TP
+.B intel
+Intel Bluetooth device
+.RE
+
+Supported IDs are (manufacturer id, product id)
+.RS
+.TP
+.B 0x0105, 0x080a
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+.B 0x0160, 0x0002
+BrainBoxes PCMCIA card (BL620)
+.RE
+
+.TP
+.I speed
+The
+.I speed
+specifies the UART speed to use. Baudrates higher than 115.200bps require
+vendor specific initializations that are not implemented for all types of
+devices. In general the following speeds are supported:
+
+.B 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+
+Supported vendor devices are automatically initialised to their respective
+best settings.
+.TP
+.I flow
+If the keyword
+.I flow
+is appended to the list of options then hardware flow control is forced on
+the serial link (
+.B CRTSCTS
+). All above mentioned device types have
+.B flow
+set by default. To force no flow control use
+.B noflow
+instead.
+.TP
+.I sleep
+Enables hardware specific power management feature. If
+.I sleep
+is appended to the list of options then this feature is enabled. To disable
+this feature use
+.B nosleep
+instead.
+All above mentioned device types have
+.B nosleep
+set by default.
+
+Note: This option will only be valid for hardware which support
+hardware specific power management enable option from host.
+.TP
+.I bdaddr
+The
+.I bdaddr
+specifies the Bluetooth Address to use.  Some devices (like the STLC2500)
+do not store the Bluetooth address in hardware memory.  Instead it must
+be uploaded during the initialization process.  If this argument
+is specified, then the address will be used to initialize the device.
+Otherwise, a default address will be used.
+
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com>
+.PP
+Manual page by Nils Faerber <nils@kernelconcepts.de>
diff --git a/bluez/repo/tools/hciattach.c b/bluez/repo/tools/hciattach.c
new file mode 100644
index 0000000..59a76a7
--- /dev/null
+++ b/bluez/repo/tools/hciattach.c
@@ -0,0 +1,1491 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+struct uart_t {
+	char *type;
+	int  m_id;
+	int  p_id;
+	int  proto;
+	int  init_speed;
+	int  speed;
+	int  flags;
+	int  pm;
+	char *bdaddr;
+	int  (*init) (int fd, struct uart_t *u, struct termios *ti);
+	int  (*post) (int fd, struct uart_t *u, struct termios *ti);
+};
+
+#define FLOW_CTL	0x0001
+#define AMP_DEV		0x0002
+#define ENABLE_PM	1
+#define DISABLE_PM	0
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+	__io_canceled = 1;
+}
+
+static void sig_alarm(int sig)
+{
+	fprintf(stderr, "Initialization timed out.\n");
+	exit(1);
+}
+
+int uart_speed(int s)
+{
+	switch (s) {
+	case 9600:
+		return B9600;
+	case 19200:
+		return B19200;
+	case 38400:
+		return B38400;
+	case 57600:
+		return B57600;
+	case 115200:
+		return B115200;
+	case 230400:
+		return B230400;
+	case 460800:
+		return B460800;
+	case 500000:
+		return B500000;
+	case 576000:
+		return B576000;
+	case 921600:
+		return B921600;
+	case 1000000:
+		return B1000000;
+	case 1152000:
+		return B1152000;
+	case 1500000:
+		return B1500000;
+	case 2000000:
+		return B2000000;
+#ifdef B2500000
+	case 2500000:
+		return B2500000;
+#endif
+#ifdef B3000000
+	case 3000000:
+		return B3000000;
+#endif
+#ifdef B3500000
+	case 3500000:
+		return B3500000;
+#endif
+#ifdef B3710000
+	case 3710000:
+		return B3710000;
+#endif
+#ifdef B4000000
+	case 4000000:
+		return B4000000;
+#endif
+	default:
+		return B57600;
+	}
+}
+
+int set_speed(int fd, struct termios *ti, int speed)
+{
+	if (cfsetospeed(ti, uart_speed(speed)) < 0)
+		return -errno;
+
+	if (cfsetispeed(ti, uart_speed(speed)) < 0)
+		return -errno;
+
+	if (tcsetattr(fd, TCSANOW, ti) < 0)
+		return -errno;
+
+	return 0;
+}
+
+/*
+ * Read an HCI event from the given file descriptor.
+ */
+int read_hci_event(int fd, unsigned char* buf, int size)
+{
+	int remain, r;
+	int count = 0;
+
+	if (size <= 0)
+		return -1;
+
+	/* The first byte identifies the packet type. For HCI event packets, it
+	 * should be 0x04, so we read until we get to the 0x04. */
+	while (1) {
+		r = read(fd, buf, 1);
+		if (r <= 0)
+			return -1;
+		if (buf[0] == 0x04)
+			break;
+	}
+	count++;
+
+	/* The next two bytes are the event code and parameter total length. */
+	while (count < 3) {
+		r = read(fd, buf + count, 3 - count);
+		if (r <= 0)
+			return -1;
+		count += r;
+	}
+
+	/* Now we read the parameters. */
+	if (buf[2] < (size - 3))
+		remain = buf[2];
+	else
+		remain = size - 3;
+
+	while ((count - 3) < remain) {
+		r = read(fd, buf + count, remain - (count - 3));
+		if (r <= 0)
+			return -1;
+		count += r;
+	}
+
+	return count;
+}
+
+/*
+ * Ericsson specific initialization
+ */
+static int ericsson(int fd, struct uart_t *u, struct termios *ti)
+{
+	struct timespec tm = {0, 50000};
+	char cmd[5];
+
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x09;
+	cmd[2] = 0xfc;
+	cmd[3] = 0x01;
+
+	switch (u->speed) {
+	case 57600:
+		cmd[4] = 0x03;
+		break;
+	case 115200:
+		cmd[4] = 0x02;
+		break;
+	case 230400:
+		cmd[4] = 0x01;
+		break;
+	case 460800:
+		cmd[4] = 0x00;
+		break;
+	case 921600:
+		cmd[4] = 0x20;
+		break;
+	case 2000000:
+		cmd[4] = 0x25;
+		break;
+	case 3000000:
+		cmd[4] = 0x27;
+		break;
+	case 4000000:
+		cmd[4] = 0x2B;
+		break;
+	default:
+		cmd[4] = 0x03;
+		u->speed = 57600;
+		fprintf(stderr, "Invalid speed requested, using %d bps instead\n", u->speed);
+		break;
+	}
+
+	/* Send initialization command */
+	if (write(fd, cmd, 5) != 5) {
+		perror("Failed to write init command");
+		return -1;
+	}
+
+	nanosleep(&tm, NULL);
+	return 0;
+}
+
+/*
+ * Digianswer specific initialization
+ */
+static int digi(int fd, struct uart_t *u, struct termios *ti)
+{
+	struct timespec tm = {0, 50000};
+	char cmd[5];
+
+	/* DigiAnswer set baud rate command */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x07;
+	cmd[2] = 0xfc;
+	cmd[3] = 0x01;
+
+	switch (u->speed) {
+	case 57600:
+		cmd[4] = 0x08;
+		break;
+	case 115200:
+		cmd[4] = 0x09;
+		break;
+	default:
+		cmd[4] = 0x09;
+		u->speed = 115200;
+		break;
+	}
+
+	/* Send initialization command */
+	if (write(fd, cmd, 5) != 5) {
+		perror("Failed to write init command");
+		return -1;
+	}
+
+	nanosleep(&tm, NULL);
+	return 0;
+}
+
+static int texas(int fd, struct uart_t *u, struct termios *ti)
+{
+	return texas_init(fd, &u->speed, ti);
+}
+
+static int texas2(int fd, struct uart_t *u, struct termios *ti)
+{
+	return texas_post(fd, ti);
+}
+
+static int texasalt(int fd, struct uart_t *u, struct termios *ti)
+{
+	return texasalt_init(fd, u->speed, ti);
+}
+
+static int ath3k_ps(int fd, struct uart_t *u, struct termios *ti)
+{
+	return ath3k_init(fd, u->speed, u->init_speed, u->bdaddr, ti);
+}
+
+static int ath3k_pm(int fd, struct uart_t *u, struct termios *ti)
+{
+	return ath3k_post(fd, u->pm);
+}
+
+static int qualcomm(int fd, struct uart_t *u, struct termios *ti)
+{
+	return qualcomm_init(fd, u->speed, ti, u->bdaddr);
+}
+
+static int intel(int fd, struct uart_t *u, struct termios *ti)
+{
+	return intel_init(fd, u->init_speed, &u->speed, ti);
+}
+
+static int bcm43xx(int fd, struct uart_t *u, struct termios *ti)
+{
+	return bcm43xx_init(fd, u->init_speed, u->speed, ti, u->bdaddr);
+}
+
+static int read_check(int fd, void *buf, int count)
+{
+	int res;
+
+	do {
+		res = read(fd, buf, count);
+		if (res != -1) {
+			buf += res;
+			count -= res;
+		}
+	} while (count && (errno == 0 || errno == EINTR));
+
+	if (count)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * BCSP specific initialization
+ */
+static int serial_fd;
+static int bcsp_max_retries = 10;
+
+static void bcsp_tshy_sig_alarm(int sig)
+{
+	unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
+	static int retries = 0;
+
+	if (retries < bcsp_max_retries) {
+		retries++;
+		if (write(serial_fd, &bcsp_sync_pkt, 10) < 0)
+			return;
+		alarm(1);
+		return;
+	}
+
+	tcflush(serial_fd, TCIOFLUSH);
+	fprintf(stderr, "BCSP initialization timed out\n");
+	exit(1);
+}
+
+static void bcsp_tconf_sig_alarm(int sig)
+{
+	unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
+	static int retries = 0;
+
+	if (retries < bcsp_max_retries){
+		retries++;
+		if (write(serial_fd, &bcsp_conf_pkt, 10) < 0)
+			return;
+		alarm(1);
+		return;
+	}
+
+	tcflush(serial_fd, TCIOFLUSH);
+	fprintf(stderr, "BCSP initialization timed out\n");
+	exit(1);
+}
+
+static int bcsp(int fd, struct uart_t *u, struct termios *ti)
+{
+	unsigned char byte, bcsph[4], bcspp[4],
+		bcsp_sync_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xac,0xaf,0xef,0xee,0xc0},
+		bcsp_conf_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xde,0xad,0xd0,0xd0,0xc0},
+		bcspsync[4]     = {0xda, 0xdc, 0xed, 0xed},
+		bcspsyncresp[4] = {0xac,0xaf,0xef,0xee},
+		bcspconf[4]     = {0xad,0xef,0xac,0xed},
+		bcspconfresp[4] = {0xde,0xad,0xd0,0xd0};
+	struct sigaction sa;
+	int len;
+
+	if (set_speed(fd, ti, u->speed) < 0) {
+		perror("Can't set default baud rate");
+		return -1;
+	}
+
+	ti->c_cflag |= PARENB;
+	ti->c_cflag &= ~(PARODD);
+
+	if (tcsetattr(fd, TCSANOW, ti) < 0) {
+		perror("Can't set port settings");
+		return -1;
+	}
+
+	alarm(0);
+
+	serial_fd = fd;
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags = SA_NOCLDSTOP;
+	sa.sa_handler = bcsp_tshy_sig_alarm;
+	sigaction(SIGALRM, &sa, NULL);
+
+	/* State = shy */
+
+	bcsp_tshy_sig_alarm(0);
+	while (1) {
+		do {
+			if (read_check(fd, &byte, 1) == -1){
+				perror("Failed to read");
+				return -1;
+			}
+		} while (byte != 0xC0);
+
+		do {
+			if ( read_check(fd, &bcsph[0], 1) == -1){
+				perror("Failed to read");
+				return -1;
+			}
+		} while (bcsph[0] == 0xC0);
+
+		if ( read_check(fd, &bcsph[1], 3) == -1){
+			perror("Failed to read");
+			return -1;
+		}
+
+		if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
+			continue;
+		if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
+			continue;
+
+		if (read_check(fd, &bcspp, 4) == -1){
+			perror("Failed to read");
+			return -1;
+		}
+
+		if (!memcmp(bcspp, bcspsync, 4)) {
+			if (write(fd, &bcsp_sync_resp_pkt,10) < 0)
+				return -1;
+		} else if (!memcmp(bcspp, bcspsyncresp, 4))
+			break;
+	}
+
+	/* State = curious */
+
+	alarm(0);
+	sa.sa_handler = bcsp_tconf_sig_alarm;
+	sigaction(SIGALRM, &sa, NULL);
+	alarm(1);
+
+	while (1) {
+		do {
+			if (read_check(fd, &byte, 1) == -1){
+				perror("Failed to read");
+				return -1;
+			}
+		} while (byte != 0xC0);
+
+		do {
+			if (read_check(fd, &bcsph[0], 1) == -1){
+				perror("Failed to read");
+				return -1;
+			}
+		} while (bcsph[0] == 0xC0);
+
+		if (read_check(fd, &bcsph[1], 3) == -1){
+			perror("Failed to read");
+			return -1;
+		}
+
+		if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
+			continue;
+
+		if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
+			continue;
+
+		if (read_check(fd, &bcspp, 4) == -1){
+			perror("Failed to read");
+			return -1;
+		}
+
+		if (!memcmp(bcspp, bcspsync, 4))
+			len = write(fd, &bcsp_sync_resp_pkt, 10);
+		else if (!memcmp(bcspp, bcspconf, 4))
+			len = write(fd, &bcsp_conf_resp_pkt, 10);
+		else if (!memcmp(bcspp, bcspconfresp,  4))
+			break;
+		else
+			continue;
+
+		if (len < 0)
+			return -errno;
+	}
+
+	/* State = garrulous */
+
+	return 0;
+}
+
+/*
+ * CSR specific initialization
+ * Inspired strongly by code in OpenBT and experimentations with Brainboxes
+ * Pcmcia card.
+ * Jean Tourrilhes <jt@hpl.hp.com> - 14.11.01
+ */
+static int csr(int fd, struct uart_t *u, struct termios *ti)
+{
+	struct timespec tm = {0, 10000000};	/* 10ms - be generous */
+	unsigned char cmd[30];		/* Command */
+	unsigned char resp[30];		/* Response */
+	int  clen = 0;		/* Command len */
+	static int csr_seq = 0;	/* Sequence number of command */
+	int  divisor;
+
+	/* It seems that if we set the CSR UART speed straight away, it
+	 * won't work, the CSR UART gets into a state where we can't talk
+	 * to it anymore.
+	 * On the other hand, doing a read before setting the CSR speed
+	 * seems to be ok.
+	 * Therefore, the strategy is to read the build ID (useful for
+	 * debugging) and only then set the CSR UART speed. Doing like
+	 * this is more complex but at least it works ;-)
+	 * The CSR UART control may be slow to wake up or something because
+	 * every time I read its speed, its bogus...
+	 * Jean II */
+
+	/* Try to read the build ID of the CSR chip */
+	clen = 5 + (5 + 6) * 2;
+	/* HCI header */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x00;		/* CSR command */
+	cmd[2] = 0xfc;		/* MANUFACTURER_SPEC */
+	cmd[3] = 1 + (5 + 6) * 2;	/* len */
+	/* CSR MSG header */
+	cmd[4] = 0xC2;		/* first+last+channel=BCC */
+	/* CSR BCC header */
+	cmd[5] = 0x00;		/* type = GET-REQ */
+	cmd[6] = 0x00;		/* - msB */
+	cmd[7] = 5 + 4;		/* len */
+	cmd[8] = 0x00;		/* - msB */
+	cmd[9] = csr_seq & 0xFF;/* seq num */
+	cmd[10] = (csr_seq >> 8) & 0xFF;	/* - msB */
+	csr_seq++;
+	cmd[11] = 0x19;		/* var_id = CSR_CMD_BUILD_ID */
+	cmd[12] = 0x28;		/* - msB */
+	cmd[13] = 0x00;		/* status = STATUS_OK */
+	cmd[14] = 0x00;		/* - msB */
+	/* CSR BCC payload */
+	memset(cmd + 15, 0, 6 * 2);
+
+	/* Send command */
+	do {
+		if (write(fd, cmd, clen) != clen) {
+			perror("Failed to write init command (GET_BUILD_ID)");
+			return -1;
+		}
+
+		/* Read reply. */
+		if (read_hci_event(fd, resp, 100) < 0) {
+			perror("Failed to read init response (GET_BUILD_ID)");
+			return -1;
+		}
+
+	/* Event code 0xFF is for vendor-specific events, which is
+	 * what we're looking for. */
+	} while (resp[1] != 0xFF);
+
+#ifdef CSR_DEBUG
+	{
+	char temp[512];
+	int i;
+	for (i=0; i < rlen; i++)
+		sprintf(temp + (i*3), "-%02X", resp[i]);
+	fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1);
+	// In theory, it should look like :
+	// 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00
+	}
+#endif
+	/* Display that to user */
+	fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n",
+		resp[15] & 0xFF, resp[14] & 0xFF);
+
+	/* Try to read the current speed of the CSR chip */
+	clen = 5 + (5 + 4)*2;
+	/* -- HCI header */
+	cmd[3] = 1 + (5 + 4)*2;	/* len */
+	/* -- CSR BCC header -- */
+	cmd[9] = csr_seq & 0xFF;	/* seq num */
+	cmd[10] = (csr_seq >> 8) & 0xFF;	/* - msB */
+	csr_seq++;
+	cmd[11] = 0x02;		/* var_id = CONFIG_UART */
+	cmd[12] = 0x68;		/* - msB */
+
+#ifdef CSR_DEBUG
+	/* Send command */
+	do {
+		if (write(fd, cmd, clen) != clen) {
+			perror("Failed to write init command (GET_BUILD_ID)");
+			return -1;
+		}
+
+		/* Read reply. */
+		if (read_hci_event(fd, resp, 100) < 0) {
+			perror("Failed to read init response (GET_BUILD_ID)");
+			return -1;
+		}
+
+	/* Event code 0xFF is for vendor-specific events, which is
+	 * what we're looking for. */
+	} while (resp[1] != 0xFF);
+
+	{
+	char temp[512];
+	int i;
+	for (i=0; i < rlen; i++)
+		sprintf(temp + (i*3), "-%02X", resp[i]);
+	fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1);
+	}
+#endif
+
+	if (u->speed > 1500000) {
+		fprintf(stderr, "Speed %d too high. Remaining at %d baud\n",
+			u->speed, u->init_speed);
+		u->speed = u->init_speed;
+	} else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
+		/* Unknown speed. Why oh why can't we just pass an int to the kernel? */
+		fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
+			u->speed, u->init_speed);
+		u->speed = u->init_speed;
+	}
+	if (u->speed == u->init_speed)
+		return 0;
+
+	/* Now, create the command that will set the UART speed */
+	/* CSR BCC header */
+	cmd[5] = 0x02;			/* type = SET-REQ */
+	cmd[6] = 0x00;			/* - msB */
+	cmd[9] = csr_seq & 0xFF;	/* seq num */
+	cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */
+	csr_seq++;
+
+	divisor = (u->speed*64+7812)/15625;
+
+	/* No parity, one stop bit -> divisor |= 0x0000; */
+	cmd[15] = (divisor) & 0xFF;		/* divider */
+	cmd[16] = (divisor >> 8) & 0xFF;	/* - msB */
+	/* The rest of the payload will be 0x00 */
+
+#ifdef CSR_DEBUG
+	{
+	char temp[512];
+	int i;
+	for(i = 0; i < clen; i++)
+		sprintf(temp + (i*3), "-%02X", cmd[i]);
+	fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1);
+	// In theory, it should look like :
+	// 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00
+	// 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00
+	}
+#endif
+
+	/* Send the command to set the CSR UART speed */
+	if (write(fd, cmd, clen) != clen) {
+		perror("Failed to write init command (SET_UART_SPEED)");
+		return -1;
+	}
+
+	nanosleep(&tm, NULL);
+	return 0;
+}
+
+/*
+ * Silicon Wave specific initialization
+ * Thomas Moser <thomas.moser@tmoser.ch>
+ */
+static int swave(int fd, struct uart_t *u, struct termios *ti)
+{
+	struct timespec tm = { 0, 500000 };
+	char cmd[10], rsp[100];
+	int r;
+
+	// Silicon Wave set baud rate command
+	// see HCI Vendor Specific Interface from Silicon Wave
+	// first send a "param access set" command to set the
+	// appropriate data fields in RAM. Then send a "HCI Reset
+	// Subcommand", e.g. "soft reset" to make the changes effective.
+
+	cmd[0] = HCI_COMMAND_PKT;	// it's a command packet
+	cmd[1] = 0x0B;			// OCF 0x0B	= param access set
+	cmd[2] = 0xfc;			// OGF bx111111 = vendor specific
+	cmd[3] = 0x06;			// 6 bytes of data following
+	cmd[4] = 0x01;			// param sub command
+	cmd[5] = 0x11;			// tag 17 = 0x11 = HCI Transport Params
+	cmd[6] = 0x03;			// length of the parameter following
+	cmd[7] = 0x01;			// HCI Transport flow control enable
+	cmd[8] = 0x01;			// HCI Transport Type = UART
+
+	switch (u->speed) {
+	case 19200:
+		cmd[9] = 0x03;
+		break;
+	case 38400:
+		cmd[9] = 0x02;
+		break;
+	case 57600:
+		cmd[9] = 0x01;
+		break;
+	case 115200:
+		cmd[9] = 0x00;
+		break;
+	default:
+		u->speed = 115200;
+		cmd[9] = 0x00;
+		break;
+	}
+
+	/* Send initialization command */
+	if (write(fd, cmd, 10) != 10) {
+		perror("Failed to write init command");
+		return -1;
+	}
+
+	// We should wait for a "GET Event" to confirm the success of
+	// the baud rate setting. Wait some time before reading. Better:
+	// read with timeout, parse data
+	// until correct answer, else error handling ... todo ...
+
+	nanosleep(&tm, NULL);
+
+	r = read(fd, rsp, sizeof(rsp));
+	if (r > 0) {
+		// guess it's okay, but we should parse the reply. But since
+		// I don't react on an error anyway ... todo
+		// Response packet format:
+		//  04	Event
+		//  FF	Vendor specific
+		//  07	Parameter length
+		//  0B	Subcommand
+		//  01	Setevent
+		//  11	Tag specifying HCI Transport Layer Parameter
+		//  03	length
+		//  01	flow on
+		//  01 	Hci Transport type = Uart
+		//  xx	Baud rate set (see above)
+	} else {
+		// ups, got error.
+		return -1;
+	}
+
+	// we probably got the reply. Now we must send the "soft reset"
+	// which is standard HCI RESET.
+
+	cmd[0] = HCI_COMMAND_PKT;	// it's a command packet
+	cmd[1] = 0x03;
+	cmd[2] = 0x0c;
+	cmd[3] = 0x00;
+
+	/* Send reset command */
+	if (write(fd, cmd, 4) != 4) {
+		perror("Can't write Silicon Wave reset cmd.");
+		return -1;
+	}
+
+	nanosleep(&tm, NULL);
+
+	// now the uart baud rate on the silicon wave module is set and effective.
+	// change our own baud rate as well. Then there is a reset event coming in
+ 	// on the *new* baud rate. This is *undocumented*! The packet looks like this:
+	// 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
+	// subcommand class". So: change to new baud rate, read with timeout, parse
+	// data, error handling. BTW: all param access in Silicon Wave is done this way.
+	// Maybe this code would belong in a separate file, or at least code reuse...
+
+	return 0;
+}
+
+/*
+ * ST Microelectronics specific initialization
+ * Marcel Holtmann <marcel@holtmann.org>
+ */
+static int st(int fd, struct uart_t *u, struct termios *ti)
+{
+	struct timespec tm = {0, 50000};
+	char cmd[5];
+
+	/* ST Microelectronics set baud rate command */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x46;			// OCF = Hci_Cmd_ST_Set_Uart_Baud_Rate
+	cmd[2] = 0xfc;			// OGF = Vendor specific
+	cmd[3] = 0x01;
+
+	switch (u->speed) {
+	case 9600:
+		cmd[4] = 0x09;
+		break;
+	case 19200:
+		cmd[4] = 0x0b;
+		break;
+	case 38400:
+		cmd[4] = 0x0d;
+		break;
+	case 57600:
+		cmd[4] = 0x0e;
+		break;
+	case 115200:
+		cmd[4] = 0x10;
+		break;
+	case 230400:
+		cmd[4] = 0x12;
+		break;
+	case 460800:
+		cmd[4] = 0x13;
+		break;
+	case 921600:
+		cmd[4] = 0x14;
+		break;
+	default:
+		cmd[4] = 0x10;
+		u->speed = 115200;
+		break;
+	}
+
+	/* Send initialization command */
+	if (write(fd, cmd, 5) != 5) {
+		perror("Failed to write init command");
+		return -1;
+	}
+
+	nanosleep(&tm, NULL);
+	return 0;
+}
+
+static int stlc2500(int fd, struct uart_t *u, struct termios *ti)
+{
+	bdaddr_t bdaddr;
+	unsigned char resp[10];
+	int n;
+	int rvalue;
+
+	/* STLC2500 has an ericsson core */
+	rvalue = ericsson(fd, u, ti);
+	if (rvalue != 0)
+		return rvalue;
+
+#ifdef STLC2500_DEBUG
+	fprintf(stderr, "Setting speed\n");
+#endif
+	if (set_speed(fd, ti, u->speed) < 0) {
+		perror("Can't set baud rate");
+		return -1;
+	}
+
+#ifdef STLC2500_DEBUG
+	fprintf(stderr, "Speed set...\n");
+#endif
+
+	/* Read reply */
+	if ((n = read_hci_event(fd, resp, 10)) < 0) {
+		fprintf(stderr, "Failed to set baud rate on chip\n");
+		return -1;
+	}
+
+#ifdef STLC2500_DEBUG
+	for (i = 0; i < n; i++) {
+		fprintf(stderr, "resp[%d] = %02x\n", i, resp[i]);
+	}
+#endif
+
+	str2ba(u->bdaddr, &bdaddr);
+	return stlc2500_init(fd, &bdaddr);
+}
+
+static int bgb2xx(int fd, struct uart_t *u, struct termios *ti)
+{
+	bdaddr_t bdaddr;
+
+	str2ba(u->bdaddr, &bdaddr);
+
+	return bgb2xx_init(fd, &bdaddr);
+}
+
+/*
+ * Broadcom specific initialization
+ * Extracted from Jungo openrg
+ */
+static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
+{
+	int n;
+	unsigned char cmd[30], resp[30];
+
+	/* Reset the BT Chip */
+	memset(cmd, 0, sizeof(cmd));
+	memset(resp, 0, sizeof(resp));
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x03;
+	cmd[2] = 0x0c;
+	cmd[3] = 0x00;
+
+	/* Send command */
+	if (write(fd, cmd, 4) != 4) {
+		fprintf(stderr, "Failed to write reset command\n");
+		return -1;
+	}
+
+	/* Read reply */
+	if ((n = read_hci_event(fd, resp, 4)) < 0) {
+		fprintf(stderr, "Failed to reset chip\n");
+		return -1;
+	}
+
+	if (u->bdaddr != NULL) {
+		/* Set BD_ADDR */
+		memset(cmd, 0, sizeof(cmd));
+		memset(resp, 0, sizeof(resp));
+		cmd[0] = HCI_COMMAND_PKT;
+		cmd[1] = 0x01;
+		cmd[2] = 0xfc;
+		cmd[3] = 0x06;
+		str2ba(u->bdaddr, (bdaddr_t *) (cmd + 4));
+
+		/* Send command */
+		if (write(fd, cmd, 10) != 10) {
+			fprintf(stderr, "Failed to write BD_ADDR command\n");
+			return -1;
+		}
+
+		/* Read reply */
+		if ((n = read_hci_event(fd, resp, 10)) < 0) {
+			fprintf(stderr, "Failed to set BD_ADDR\n");
+			return -1;
+		}
+	}
+
+	/* Read the local version info */
+	memset(cmd, 0, sizeof(cmd));
+	memset(resp, 0, sizeof(resp));
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x01;
+	cmd[2] = 0x10;
+	cmd[3] = 0x00;
+
+	/* Send command */
+	if (write(fd, cmd, 4) != 4) {
+		fprintf(stderr, "Failed to write \"read local version\" "
+			"command\n");
+		return -1;
+	}
+
+	/* Read reply */
+	if ((n = read_hci_event(fd, resp, 4)) < 0) {
+		fprintf(stderr, "Failed to read local version\n");
+		return -1;
+	}
+
+	/* Read the local supported commands info */
+	memset(cmd, 0, sizeof(cmd));
+	memset(resp, 0, sizeof(resp));
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x02;
+	cmd[2] = 0x10;
+	cmd[3] = 0x00;
+
+	/* Send command */
+	if (write(fd, cmd, 4) != 4) {
+		fprintf(stderr, "Failed to write \"read local supported "
+						"commands\" command\n");
+		return -1;
+	}
+
+	/* Read reply */
+	if ((n = read_hci_event(fd, resp, 4)) < 0) {
+		fprintf(stderr, "Failed to read local supported commands\n");
+		return -1;
+	}
+
+	/* Set the baud rate */
+	memset(cmd, 0, sizeof(cmd));
+	memset(resp, 0, sizeof(resp));
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x18;
+	cmd[2] = 0xfc;
+	cmd[3] = 0x02;
+	switch (u->speed) {
+	case 57600:
+		cmd[4] = 0x00;
+		cmd[5] = 0xe6;
+		break;
+	case 230400:
+		cmd[4] = 0x22;
+		cmd[5] = 0xfa;
+		break;
+	case 460800:
+		cmd[4] = 0x22;
+		cmd[5] = 0xfd;
+		break;
+	case 921600:
+		cmd[4] = 0x55;
+		cmd[5] = 0xff;
+		break;
+	default:
+		/* Default is 115200 */
+		cmd[4] = 0x00;
+		cmd[5] = 0xf3;
+		break;
+	}
+	fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
+		cmd[4], cmd[5]);
+
+	/* Send command */
+	if (write(fd, cmd, 6) != 6) {
+		fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+		return -1;
+	}
+
+	if ((n = read_hci_event(fd, resp, 6)) < 0) {
+		fprintf(stderr, "Failed to set baud rate\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+struct uart_t uart[] = {
+	{ "any",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+	{ "ericsson",   0x0000, 0x0000, HCI_UART_H4,   57600,  115200,
+				FLOW_CTL, DISABLE_PM, NULL, ericsson },
+
+	{ "digi",       0x0000, 0x0000, HCI_UART_H4,   9600,   115200,
+				FLOW_CTL, DISABLE_PM, NULL, digi     },
+
+	{ "bcsp",       0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200,
+				0, DISABLE_PM, NULL, bcsp     },
+
+	/* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
+	{ "xircom",     0x0105, 0x080a, HCI_UART_H4,   115200, 115200,
+				FLOW_CTL, DISABLE_PM,  NULL, NULL     },
+
+	/* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
+	{ "csr",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+	/* BrainBoxes PCMCIA card (BL620) */
+	{ "bboxes",     0x0160, 0x0002, HCI_UART_H4,   115200, 460800,
+				FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+	/* Silicon Wave kits */
+	{ "swave",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, swave    },
+
+	/* Texas Instruments Bluelink (BRF) modules */
+	{ "texas",      0x0000, 0x0000, HCI_UART_LL,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, texas,    texas2 },
+
+	{ "texasalt",   0x0000, 0x0000, HCI_UART_LL,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL   },
+
+	/* ST Microelectronics minikits based on STLC2410/STLC2415 */
+	{ "st",         0x0000, 0x0000, HCI_UART_H4,    57600, 115200,
+				FLOW_CTL, DISABLE_PM,  NULL, st       },
+
+	/* ST Microelectronics minikits based on STLC2500 */
+	{ "stlc2500",   0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+			FLOW_CTL, DISABLE_PM, "00:80:E1:00:AB:BA", stlc2500 },
+
+	/* Philips generic Ericsson IP core based */
+	{ "philips",    0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+	/* Philips BGB2xx Module */
+	{ "bgb2xx",    0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+			FLOW_CTL, DISABLE_PM, "BD:B2:10:00:AB:BA", bgb2xx },
+
+	/* Sphinx Electronics PICO Card */
+	{ "picocard",   0x025e, 0x1000, HCI_UART_H4, 115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+	/* Inventel BlueBird Module */
+	{ "inventel",   0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+	/* COM One Platinium Bluetooth PC Card */
+	{ "comone",     0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200,
+				0, DISABLE_PM,  NULL, bcsp     },
+
+	/* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */
+	{ "tdk",        0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200,
+				0, DISABLE_PM, NULL, bcsp     },
+
+	/* Socket Bluetooth CF Card (Rev G) */
+	{ "socket",     0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400,
+				0, DISABLE_PM, NULL, bcsp     },
+
+	/* 3Com Bluetooth Card (Version 3.0) */
+	{ "3com",       0x0101, 0x0041, HCI_UART_H4,   115200, 115200,
+				FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+	/* AmbiCom BT2000C Bluetooth PC/CF Card */
+	{ "bt2000c",    0x022d, 0x2000, HCI_UART_H4,    57600, 460800,
+				FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+	/* Zoom Bluetooth PCMCIA Card */
+	{ "zoom",       0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+				0, DISABLE_PM, NULL, bcsp     },
+
+	/* Sitecom CN-504 PCMCIA Card */
+	{ "sitecom",    0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+				0, DISABLE_PM, NULL, bcsp     },
+
+	/* Billionton PCBTC1 PCMCIA Card */
+	{ "billionton", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+				0, DISABLE_PM, NULL, bcsp     },
+
+	/* Broadcom BCM2035 */
+	{ "bcm2035",    0x0A5C, 0x2035, HCI_UART_H4,   115200, 460800,
+				FLOW_CTL, DISABLE_PM, NULL, bcm2035  },
+
+	/* Broadcom BCM43XX */
+	{ "bcm43xx",    0x0000, 0x0000, HCI_UART_H4,   115200, 3000000,
+				FLOW_CTL, DISABLE_PM, NULL, bcm43xx, NULL  },
+
+	{ "ath3k",    0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200,
+			FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm  },
+
+	/* QUALCOMM BTS */
+	{ "qualcomm",   0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+			FLOW_CTL, DISABLE_PM, NULL, qualcomm, NULL },
+
+	/* Intel Bluetooth Module */
+	{ "intel",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+			FLOW_CTL, DISABLE_PM, NULL, intel, NULL },
+
+	/* Three-wire UART */
+	{ "3wire",      0x0000, 0x0000, HCI_UART_3WIRE, 115200, 115200,
+			0, DISABLE_PM, NULL, NULL, NULL },
+
+	/* AMP controller UART */
+	{ "amp",	0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+			AMP_DEV, DISABLE_PM, NULL, NULL, NULL },
+
+	{ NULL, 0 }
+};
+
+static struct uart_t * get_by_id(int m_id, int p_id)
+{
+	int i;
+	for (i = 0; uart[i].type; i++) {
+		if (uart[i].m_id == m_id && uart[i].p_id == p_id)
+			return &uart[i];
+	}
+	return NULL;
+}
+
+static struct uart_t * get_by_type(char *type)
+{
+	int i;
+	for (i = 0; uart[i].type; i++) {
+		if (!strcmp(uart[i].type, type))
+			return &uart[i];
+	}
+	return NULL;
+}
+
+/* Initialize UART driver */
+static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
+{
+	struct termios ti;
+	int fd, i;
+	unsigned long flags = 0;
+
+	if (raw)
+		flags |= 1 << HCI_UART_RAW_DEVICE;
+
+	if (u->flags & AMP_DEV)
+		flags |= 1 << HCI_UART_CREATE_AMP;
+
+	fd = open(dev, O_RDWR | O_NOCTTY);
+	if (fd < 0) {
+		perror("Can't open serial port");
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (tcgetattr(fd, &ti) < 0) {
+		perror("Can't get port settings");
+		goto fail;
+	}
+
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= CLOCAL;
+	if (u->flags & FLOW_CTL)
+		ti.c_cflag |= CRTSCTS;
+	else
+		ti.c_cflag &= ~CRTSCTS;
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		perror("Can't set port settings");
+		goto fail;
+	}
+
+	/* Set initial baudrate */
+	if (set_speed(fd, &ti, u->init_speed) < 0) {
+		perror("Can't set initial baud rate");
+		goto fail;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (send_break) {
+		tcsendbreak(fd, 0);
+		usleep(500000);
+	}
+
+	if (u->init && u->init(fd, u, &ti) < 0)
+		goto fail;
+
+	tcflush(fd, TCIOFLUSH);
+
+	/* Set actual baudrate */
+	if (set_speed(fd, &ti, u->speed) < 0) {
+		perror("Can't set baud rate");
+		goto fail;
+	}
+
+	/* Set TTY to N_HCI line discipline */
+	i = N_HCI;
+	if (ioctl(fd, TIOCSETD, &i) < 0) {
+		perror("Can't set line discipline");
+		goto fail;
+	}
+
+	if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+		perror("Can't set UART flags");
+		goto fail;
+	}
+
+	if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+		perror("Can't set device");
+		goto fail;
+	}
+
+	if (u->post && u->post(fd, u, &ti) < 0)
+		goto fail;
+
+	return fd;
+
+fail:
+	close(fd);
+	return -1;
+}
+
+static void usage(void)
+{
+	printf("hciattach - HCI UART driver initialization utility\n");
+	printf("Usage:\n");
+	printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed]"
+			" <tty> <type | id> [speed] [flow|noflow]"
+			" [sleep|nosleep] [bdaddr]\n");
+	printf("\thciattach -l\n");
+}
+
+int main(int argc, char *argv[])
+{
+	struct uart_t *u = NULL;
+	int detach, printpid, raw, opt, i, n, ld, err;
+	int to = 10;
+	int init_speed = 0;
+	int send_break = 0;
+	pid_t pid;
+	struct sigaction sa;
+	struct pollfd p;
+	sigset_t sigs;
+	char dev[PATH_MAX];
+
+	detach = 1;
+	printpid = 0;
+	raw = 0;
+
+	while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {
+		switch(opt) {
+		case 'b':
+			send_break = 1;
+			break;
+
+		case 'n':
+			detach = 0;
+			break;
+
+		case 'p':
+			printpid = 1;
+			break;
+
+		case 't':
+			to = atoi(optarg);
+			break;
+
+		case 's':
+			init_speed = atoi(optarg);
+			break;
+
+		case 'l':
+			for (i = 0; uart[i].type; i++) {
+				printf("%-10s0x%04x,0x%04x\n", uart[i].type,
+							uart[i].m_id, uart[i].p_id);
+			}
+			exit(0);
+
+		case 'r':
+			raw = 1;
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	n = argc - optind;
+	if (n < 2) {
+		usage();
+		exit(1);
+	}
+
+	for (n = 0; optind < argc; n++, optind++) {
+		char *opt;
+
+		opt = argv[optind];
+
+		switch(n) {
+		case 0:
+			dev[0] = 0;
+			if (!strchr(opt, '/'))
+				strcpy(dev, "/dev/");
+
+			if (strlen(opt) > PATH_MAX - (strlen(dev) + 1)) {
+				fprintf(stderr, "Invalid serial device\n");
+				exit(1);
+			}
+
+			strcat(dev, opt);
+			break;
+
+		case 1:
+			if (strchr(argv[optind], ',')) {
+				int m_id, p_id;
+				sscanf(argv[optind], "%x,%x", &m_id, &p_id);
+				u = get_by_id(m_id, p_id);
+			} else {
+				u = get_by_type(opt);
+			}
+
+			if (!u) {
+				fprintf(stderr, "Unknown device type or id\n");
+				exit(1);
+			}
+
+			break;
+
+		case 2:
+			u->speed = atoi(argv[optind]);
+			break;
+
+		case 3:
+			if (!strcmp("flow", argv[optind]))
+				u->flags |=  FLOW_CTL;
+			else
+				u->flags &= ~FLOW_CTL;
+			break;
+
+		case 4:
+			if (!strcmp("sleep", argv[optind]))
+				u->pm = ENABLE_PM;
+			else
+				u->pm = DISABLE_PM;
+			break;
+
+		case 5:
+			u->bdaddr = argv[optind];
+			break;
+		}
+	}
+
+	if (!u) {
+		fprintf(stderr, "Unknown device type or id\n");
+		exit(1);
+	}
+
+	/* If user specified a initial speed, use that instead of
+	   the hardware's default */
+	if (init_speed)
+		u->init_speed = init_speed;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sa.sa_handler = sig_alarm;
+	sigaction(SIGALRM, &sa, NULL);
+
+	/* 10 seconds should be enough for initialization */
+	alarm(to);
+	bcsp_max_retries = to;
+
+	n = init_uart(dev, u, send_break, raw);
+	if (n < 0) {
+		perror("Can't initialize device");
+		exit(1);
+	}
+
+	printf("Device setup complete\n");
+
+	alarm(0);
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &sa, NULL);
+	sigaction(SIGPIPE, &sa, NULL);
+
+	sa.sa_handler = sig_term;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	sa.sa_handler = sig_hup;
+	sigaction(SIGHUP, &sa, NULL);
+
+	if (detach) {
+		if ((pid = fork())) {
+			if (printpid)
+				printf("%d\n", pid);
+			return 0;
+		}
+
+		for (i = 0; i < 20; i++)
+			if (i != n)
+				close(i);
+	}
+
+	p.fd = n;
+	p.events = POLLERR | POLLHUP;
+
+	sigfillset(&sigs);
+	sigdelset(&sigs, SIGCHLD);
+	sigdelset(&sigs, SIGPIPE);
+	sigdelset(&sigs, SIGTERM);
+	sigdelset(&sigs, SIGINT);
+	sigdelset(&sigs, SIGHUP);
+
+	while (!__io_canceled) {
+		p.revents = 0;
+		err = ppoll(&p, 1, NULL, &sigs);
+		if (err < 0 && errno == EINTR)
+			continue;
+		if (err)
+			break;
+	}
+
+	/* Restore TTY line discipline */
+	ld = N_TTY;
+	if (ioctl(n, TIOCSETD, &ld) < 0) {
+		perror("Can't restore line discipline");
+		exit(1);
+	}
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hciattach.h b/bluez/repo/tools/hciattach.h
new file mode 100644
index 0000000..4279a33
--- /dev/null
+++ b/bluez/repo/tools/hciattach.h
@@ -0,0 +1,68 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-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
+ *
+ */
+
+#include <termios.h>
+
+#ifndef N_HCI
+#define N_HCI	15
+#endif
+
+#define HCIUARTSETPROTO		_IOW('U', 200, int)
+#define HCIUARTGETPROTO		_IOR('U', 201, int)
+#define HCIUARTGETDEVICE	_IOR('U', 202, int)
+#define HCIUARTSETFLAGS		_IOW('U', 203, int)
+#define HCIUARTGETFLAGS		_IOR('U', 204, int)
+
+#define HCI_UART_H4	0
+#define HCI_UART_BCSP	1
+#define HCI_UART_3WIRE	2
+#define HCI_UART_H4DS	3
+#define HCI_UART_LL	4
+#define HCI_UART_ATH3K  5
+#define HCI_UART_INTEL	6
+#define HCI_UART_BCM	7
+#define HCI_UART_QCA	8
+
+#define HCI_UART_RAW_DEVICE	0
+#define HCI_UART_RESET_ON_INIT	1
+#define HCI_UART_CREATE_AMP	2
+#define HCI_UART_INIT_PENDING	3
+#define HCI_UART_EXT_CONFIG	4
+#define HCI_UART_VND_DETECT	5
+
+int read_hci_event(int fd, unsigned char *buf, int size);
+int set_speed(int fd, struct termios *ti, int speed);
+int uart_speed(int speed);
+
+int texas_init(int fd, int *speed, struct termios *ti);
+int texas_post(int fd, struct termios *ti);
+int texasalt_init(int fd, int speed, struct termios *ti);
+int stlc2500_init(int fd, bdaddr_t *bdaddr);
+int bgb2xx_init(int dd, bdaddr_t *bdaddr);
+int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
+						struct termios *ti);
+int ath3k_post(int fd, int pm);
+int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
+int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
+int bcm43xx_init(int fd, int def_speed, int speed, struct termios *ti,
+		const char *bdaddr);
diff --git a/bluez/repo/tools/hciattach_ath3k.c b/bluez/repo/tools/hciattach_ath3k.c
new file mode 100644
index 0000000..a76b448
--- /dev/null
+++ b/bluez/repo/tools/hciattach_ath3k.c
@@ -0,0 +1,1035 @@
+/*
+ *  Copyright (c) 2009-2010 Atheros Communications 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+#define TRUE    1
+#define FALSE   0
+
+#define FW_PATH "/lib/firmware/ar3k/"
+
+struct ps_cfg_entry {
+	uint32_t id;
+	uint32_t len;
+	uint8_t *data;
+};
+
+struct ps_entry_type {
+	unsigned char type;
+	unsigned char array;
+};
+
+#define MAX_TAGS              50
+#define PS_HDR_LEN            4
+#define HCI_VENDOR_CMD_OGF    0x3F
+#define HCI_PS_CMD_OCF        0x0B
+
+struct ps_cfg_entry ps_list[MAX_TAGS];
+
+static void load_hci_ps_hdr(uint8_t *cmd, uint8_t ps_op, int len, int index)
+{
+	hci_command_hdr *ch = (void *)cmd;
+
+	ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+						HCI_PS_CMD_OCF));
+	ch->plen = len + PS_HDR_LEN;
+	cmd += HCI_COMMAND_HDR_SIZE;
+
+	cmd[0] = ps_op;
+	cmd[1] = index;
+	cmd[2] = index >> 8;
+	cmd[3] = len;
+}
+
+#define PS_EVENT_LEN 100
+
+/*
+ * Send HCI command and wait for command complete event.
+ * The event buffer has to be freed by the caller.
+ */
+static int send_hci_cmd_sync(int dev, uint8_t *cmd, int len, uint8_t **event)
+{
+	int err;
+	uint8_t *hci_event;
+	uint8_t pkt_type = HCI_COMMAND_PKT;
+
+	if (len == 0)
+		return len;
+
+	if (write(dev, &pkt_type, 1) != 1)
+		return -EILSEQ;
+	if (write(dev, (unsigned char *)cmd, len) != len)
+		return -EILSEQ;
+
+	hci_event = (uint8_t *)malloc(PS_EVENT_LEN);
+	if (!hci_event)
+		return -ENOMEM;
+
+	err = read_hci_event(dev, (unsigned char *)hci_event, PS_EVENT_LEN);
+	if (err > 0) {
+		*event = hci_event;
+	} else {
+		free(hci_event);
+		return -EILSEQ;
+	}
+
+	return len;
+}
+
+#define HCI_EV_SUCCESS        0x00
+
+static int read_ps_event(uint8_t *event, uint16_t ocf)
+{
+	hci_event_hdr *eh;
+	uint16_t opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, ocf));
+
+	event++;
+
+	eh = (void *)event;
+	event += HCI_EVENT_HDR_SIZE;
+
+	if (eh->evt == EVT_CMD_COMPLETE) {
+		evt_cmd_complete *cc = (void *)event;
+
+		event += EVT_CMD_COMPLETE_SIZE;
+
+		if (cc->opcode == opcode && event[0] == HCI_EV_SUCCESS)
+			return 0;
+		else
+			return -EILSEQ;
+	}
+
+	return -EILSEQ;
+}
+
+static int write_cmd(int fd, uint8_t *buffer, int len)
+{
+	uint8_t *event;
+	int err;
+
+	err = send_hci_cmd_sync(fd, buffer, len, &event);
+	if (err < 0)
+		return err;
+
+	err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+	free(event);
+
+	return err;
+}
+
+#define PS_WRITE           1
+#define PS_RESET           2
+#define WRITE_PATCH        8
+#define ENABLE_PATCH       11
+
+#define HCI_PS_CMD_HDR_LEN 7
+
+#define PS_RESET_PARAM_LEN 6
+#define HCI_MAX_CMD_SIZE   260
+#define PS_RESET_CMD_LEN   (HCI_PS_CMD_HDR_LEN + PS_RESET_PARAM_LEN)
+
+#define PS_ID_MASK         0xFF
+
+/* Sends PS commands using vendor specficic HCI commands */
+static int write_ps_cmd(int fd, uint8_t opcode, uint32_t ps_param)
+{
+	uint8_t cmd[HCI_MAX_CMD_SIZE];
+	uint32_t i;
+
+	switch (opcode) {
+	case ENABLE_PATCH:
+		load_hci_ps_hdr(cmd, opcode, 0, 0x00);
+
+		if (write_cmd(fd, cmd, HCI_PS_CMD_HDR_LEN) < 0)
+			return -EILSEQ;
+		break;
+
+	case PS_RESET:
+		load_hci_ps_hdr(cmd, opcode, PS_RESET_PARAM_LEN, 0x00);
+
+		cmd[7] = 0x00;
+		cmd[PS_RESET_CMD_LEN - 2] = ps_param & PS_ID_MASK;
+		cmd[PS_RESET_CMD_LEN - 1] = (ps_param >> 8) & PS_ID_MASK;
+
+		if (write_cmd(fd, cmd, PS_RESET_CMD_LEN) < 0)
+			return -EILSEQ;
+		break;
+
+	case PS_WRITE:
+		for (i = 0; i < ps_param; i++) {
+			load_hci_ps_hdr(cmd, opcode, ps_list[i].len,
+							ps_list[i].id);
+
+			memcpy(&cmd[HCI_PS_CMD_HDR_LEN], ps_list[i].data,
+							ps_list[i].len);
+
+			if (write_cmd(fd, cmd, ps_list[i].len +
+						HCI_PS_CMD_HDR_LEN) < 0)
+				return -EILSEQ;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+#define __is_delim(ch) ((ch) == ':')
+#define MAX_PREAMBLE_LEN 4
+
+/* Parse PS entry preamble of format [X:X] for main type and subtype */
+static int get_ps_type(char *ptr, int index, char *type, char *sub_type)
+{
+	int i;
+	int delim = FALSE;
+
+	if (index > MAX_PREAMBLE_LEN)
+		return -EILSEQ;
+
+	for (i = 1; i < index; i++) {
+		if (__is_delim(ptr[i])) {
+			delim = TRUE;
+			continue;
+		}
+
+		if (isalpha(ptr[i])) {
+			if (delim == FALSE)
+				(*type) = toupper(ptr[i]);
+			else
+				(*sub_type) = toupper(ptr[i]);
+		}
+	}
+
+	return 0;
+}
+
+#define ARRAY   'A'
+#define STRING  'S'
+#define DECIMAL 'D'
+#define BINARY  'B'
+
+#define PS_HEX           0
+#define PS_DEC           1
+
+static int get_input_format(char *buf, struct ps_entry_type *format)
+{
+	char *ptr = NULL;
+	char type = '\0';
+	char sub_type = '\0';
+
+	format->type = PS_HEX;
+	format->array = TRUE;
+
+	if (strstr(buf, "[") != buf)
+		return 0;
+
+	ptr = strstr(buf, "]");
+	if (!ptr)
+		return -EILSEQ;
+
+	if (get_ps_type(buf, ptr - buf, &type, &sub_type) < 0)
+		return -EILSEQ;
+
+	/* Check is data type is of array */
+	if (type == ARRAY || sub_type == ARRAY)
+		format->array = TRUE;
+
+	if (type == STRING || sub_type == STRING)
+		format->array = FALSE;
+
+	if (type == DECIMAL || type == BINARY)
+		format->type = PS_DEC;
+	else
+		format->type = PS_HEX;
+
+	return 0;
+}
+
+#define UNDEFINED 0xFFFF
+
+static unsigned int read_data_in_section(char *buf, struct ps_entry_type type)
+{
+	char *ptr = buf;
+
+	if (!buf)
+		return UNDEFINED;
+
+	if (buf == strstr(buf, "[")) {
+		ptr = strstr(buf, "]");
+		if (!ptr)
+			return UNDEFINED;
+
+		ptr++;
+	}
+
+	if (type.type == PS_HEX && type.array != TRUE)
+		return strtol(ptr, NULL, 16);
+
+	return UNDEFINED;
+}
+
+struct tag_info {
+	unsigned section;
+	unsigned line_count;
+	unsigned char_cnt;
+	unsigned byte_count;
+};
+
+static inline int update_char_count(const char *buf)
+{
+	char *end_ptr;
+
+	if (strstr(buf, "[") == buf) {
+		end_ptr = strstr(buf, "]");
+		if (!end_ptr)
+			return 0;
+		else
+			return (end_ptr - buf) + 1;
+	}
+
+	return 0;
+}
+
+/* Read PS entries as string, convert and add to Hex array */
+static void update_tag_data(struct ps_cfg_entry *tag,
+				struct tag_info *info, const char *ptr)
+{
+	char buf[3];
+
+	buf[2] = '\0';
+
+	strncpy(buf, &ptr[info->char_cnt], 2);
+	tag->data[info->byte_count] = strtol(buf, NULL, 16);
+	info->char_cnt += 3;
+	info->byte_count++;
+
+	strncpy(buf, &ptr[info->char_cnt], 2);
+	tag->data[info->byte_count] = strtol(buf, NULL, 16);
+	info->char_cnt += 3;
+	info->byte_count++;
+}
+
+#define PS_UNDEF   0
+#define PS_ID      1
+#define PS_LEN     2
+#define PS_DATA    3
+
+#define PS_MAX_LEN         500
+#define LINE_SIZE_MAX      (PS_MAX_LEN * 2)
+#define ENTRY_PER_LINE     16
+
+#define __check_comment(buf) (((buf)[0] == '/') && ((buf)[1] == '/'))
+#define __skip_space(str)      while (*(str) == ' ') ((str)++)
+
+static int ath_parse_ps(FILE *stream)
+{
+	char buf[LINE_SIZE_MAX + 1];
+	char *ptr;
+	uint8_t tag_cnt = 0;
+	int16_t byte_count = 0;
+	struct ps_entry_type format;
+	struct tag_info status = { 0, 0, 0, 0 };
+
+	do {
+		int read_count;
+		struct ps_cfg_entry *tag;
+
+		ptr = fgets(buf, LINE_SIZE_MAX, stream);
+		if (!ptr)
+			break;
+
+		__skip_space(ptr);
+		if (__check_comment(ptr))
+			continue;
+
+		/* Lines with a '#' will be followed by new PS entry */
+		if (ptr == strstr(ptr, "#")) {
+			if (status.section != PS_UNDEF) {
+				return -EILSEQ;
+			} else {
+				status.section = PS_ID;
+				continue;
+			}
+		}
+
+		tag = &ps_list[tag_cnt];
+
+		switch (status.section) {
+		case PS_ID:
+			if (get_input_format(ptr, &format) < 0)
+				return -EILSEQ;
+
+			tag->id = read_data_in_section(ptr, format);
+			status.section = PS_LEN;
+			break;
+
+		case PS_LEN:
+			if (get_input_format(ptr, &format) < 0)
+				return -EILSEQ;
+
+			byte_count = read_data_in_section(ptr, format);
+			if (byte_count > PS_MAX_LEN)
+				return -EILSEQ;
+
+			tag->len = byte_count;
+			tag->data = (uint8_t *)malloc(byte_count);
+
+			status.section = PS_DATA;
+			status.line_count = 0;
+			break;
+
+		case PS_DATA:
+			if (status.line_count == 0)
+				if (get_input_format(ptr, &format) < 0)
+					return -EILSEQ;
+
+			__skip_space(ptr);
+
+			status.char_cnt = update_char_count(ptr);
+
+			read_count = (byte_count > ENTRY_PER_LINE) ?
+					ENTRY_PER_LINE : byte_count;
+
+			if (format.type == PS_HEX && format.array == TRUE) {
+				while (read_count > 0) {
+					update_tag_data(tag, &status, ptr);
+					read_count -= 2;
+				}
+
+				if (byte_count > ENTRY_PER_LINE)
+					byte_count -= ENTRY_PER_LINE;
+				else
+					byte_count = 0;
+			}
+
+			status.line_count++;
+
+			if (byte_count == 0)
+				memset(&status, 0x00, sizeof(struct tag_info));
+
+			if (status.section == PS_UNDEF)
+				tag_cnt++;
+
+			if (tag_cnt == MAX_TAGS)
+				return -EILSEQ;
+			break;
+		}
+	} while (ptr);
+
+	return tag_cnt;
+}
+
+#define MAX_PATCH_CMD 244
+struct patch_entry {
+	int16_t len;
+	uint8_t data[MAX_PATCH_CMD];
+};
+
+#define SET_PATCH_RAM_ID	0x0D
+#define SET_PATCH_RAM_CMD_SIZE	11
+#define ADDRESS_LEN		4
+static int set_patch_ram(int dev, char *patch_loc, int len)
+{
+	int err;
+	uint8_t cmd[20];
+	int i, j;
+	char loc_byte[3];
+	uint8_t *event;
+	uint8_t *loc_ptr = &cmd[7];
+
+	if (!patch_loc)
+		return -1;
+
+	loc_byte[2] = '\0';
+
+	load_hci_ps_hdr(cmd, SET_PATCH_RAM_ID, ADDRESS_LEN, 0);
+
+	for (i = 0, j = 3; i < 4; i++, j--) {
+		loc_byte[0] = patch_loc[0];
+		loc_byte[1] = patch_loc[1];
+		loc_ptr[j] = strtol(loc_byte, NULL, 16);
+		patch_loc += 2;
+	}
+
+	err = send_hci_cmd_sync(dev, cmd, SET_PATCH_RAM_CMD_SIZE, &event);
+	if (err < 0)
+		return err;
+
+	err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+	free(event);
+
+	return err;
+}
+
+#define PATCH_LOC_KEY    "DA:"
+#define PATCH_LOC_STRING_LEN    8
+static int ps_patch_download(int fd, FILE *stream)
+{
+	char byte[3];
+	char ptr[MAX_PATCH_CMD + 1];
+	int byte_cnt;
+	int patch_count = 0;
+	char patch_loc[PATCH_LOC_STRING_LEN + 1];
+
+	byte[2] = '\0';
+
+	while (fgets(ptr, MAX_PATCH_CMD, stream)) {
+		if (strlen(ptr) <= 1)
+			continue;
+		else if (strstr(ptr, PATCH_LOC_KEY) == ptr) {
+			strncpy(patch_loc, &ptr[sizeof(PATCH_LOC_KEY) - 1],
+							PATCH_LOC_STRING_LEN);
+			if (set_patch_ram(fd, patch_loc, sizeof(patch_loc)) < 0)
+				return -1;
+		} else if (isxdigit(ptr[0]))
+			break;
+		else
+			return -1;
+	}
+
+	byte_cnt = strtol(ptr, NULL, 16);
+
+	while (byte_cnt > 0) {
+		int i;
+		uint8_t cmd[HCI_MAX_CMD_SIZE];
+		struct patch_entry patch;
+
+		if (byte_cnt > MAX_PATCH_CMD)
+			patch.len = MAX_PATCH_CMD;
+		else
+			patch.len = byte_cnt;
+
+		for (i = 0; i < patch.len; i++) {
+			if (!fgets(byte, 3, stream))
+				return -1;
+
+			patch.data[i] = strtoul(byte, NULL, 16);
+		}
+
+		load_hci_ps_hdr(cmd, WRITE_PATCH, patch.len, patch_count);
+		memcpy(&cmd[HCI_PS_CMD_HDR_LEN], patch.data, patch.len);
+
+		if (write_cmd(fd, cmd, patch.len + HCI_PS_CMD_HDR_LEN) < 0)
+			return -1;
+
+		patch_count++;
+		byte_cnt = byte_cnt - MAX_PATCH_CMD;
+	}
+
+	if (write_ps_cmd(fd, ENABLE_PATCH, 0) < 0)
+		return -1;
+
+	return patch_count;
+}
+
+#define PS_RAM_SIZE 2048
+
+static int ps_config_download(int fd, int tag_count)
+{
+	if (write_ps_cmd(fd, PS_RESET, PS_RAM_SIZE) < 0)
+		return -1;
+
+	if (tag_count > 0)
+		if (write_ps_cmd(fd, PS_WRITE, tag_count) < 0)
+			return -1;
+	return 0;
+}
+
+#define PS_ASIC_FILE			"PS_ASIC.pst"
+#define PS_FPGA_FILE			"PS_FPGA.pst"
+
+static void get_ps_file_name(uint32_t devtype, uint32_t rom_version,
+							char *path)
+{
+	char *filename;
+
+	if (devtype == 0xdeadc0de)
+		filename = PS_ASIC_FILE;
+	else
+		filename = PS_FPGA_FILE;
+
+	snprintf(path, MAXPATHLEN, "%s%x/%s", FW_PATH, rom_version, filename);
+}
+
+#define PATCH_FILE        "RamPatch.txt"
+#define FPGA_ROM_VERSION  0x99999999
+#define ROM_DEV_TYPE      0xdeadc0de
+
+static void get_patch_file_name(uint32_t dev_type, uint32_t rom_version,
+				uint32_t build_version, char *path)
+{
+	if (rom_version == FPGA_ROM_VERSION && dev_type != ROM_DEV_TYPE &&
+					dev_type != 0 && build_version == 1)
+		path[0] = '\0';
+	else
+		snprintf(path, MAXPATHLEN, "%s%x/%s",
+				FW_PATH, rom_version, PATCH_FILE);
+}
+
+#define VERIFY_CRC   9
+#define PS_REGION    1
+#define PATCH_REGION 2
+
+static int get_ath3k_crc(int dev)
+{
+	uint8_t cmd[7];
+	uint8_t *event;
+	int err;
+
+	load_hci_ps_hdr(cmd, VERIFY_CRC, 0, PS_REGION | PATCH_REGION);
+
+	err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);
+	if (err < 0)
+		return err;
+	/* Send error code if CRC check patched */
+	if (read_ps_event(event, HCI_PS_CMD_OCF) >= 0)
+		err = -EILSEQ;
+
+	free(event);
+
+	return err;
+}
+
+#define DEV_REGISTER      0x4FFC
+#define GET_DEV_TYPE_OCF  0x05
+
+static int get_device_type(int dev, uint32_t *code)
+{
+	uint8_t cmd[8];
+	uint8_t *event;
+	uint32_t reg;
+	int err;
+	uint8_t *ptr = cmd;
+	hci_command_hdr *ch = (void *)cmd;
+
+	ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+						GET_DEV_TYPE_OCF));
+	ch->plen = 5;
+	ptr += HCI_COMMAND_HDR_SIZE;
+
+	ptr[0] = (uint8_t)DEV_REGISTER;
+	ptr[1] = (uint8_t)DEV_REGISTER >> 8;
+	ptr[2] = (uint8_t)DEV_REGISTER >> 16;
+	ptr[3] = (uint8_t)DEV_REGISTER >> 24;
+	ptr[4] = 0x04;
+
+	err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);
+	if (err < 0)
+		return err;
+
+	err = read_ps_event(event, GET_DEV_TYPE_OCF);
+	if (err < 0)
+		goto cleanup;
+
+	reg = event[10];
+	reg = (reg << 8) | event[9];
+	reg = (reg << 8) | event[8];
+	reg = (reg << 8) | event[7];
+	*code = reg;
+
+cleanup:
+	free(event);
+
+	return err;
+}
+
+#define GET_VERSION_OCF 0x1E
+
+static int read_ath3k_version(int pConfig, uint32_t *rom_version,
+					uint32_t *build_version)
+{
+	uint8_t cmd[3];
+	uint8_t *event;
+	int err;
+	int status;
+	hci_command_hdr *ch = (void *)cmd;
+
+	ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+						GET_VERSION_OCF));
+	ch->plen = 0;
+
+	err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);
+	if (err < 0)
+		return err;
+
+	err = read_ps_event(event, GET_VERSION_OCF);
+	if (err < 0)
+		goto cleanup;
+
+	status = event[10];
+	status = (status << 8) | event[9];
+	status = (status << 8) | event[8];
+	status = (status << 8) | event[7];
+	*rom_version = status;
+
+	status = event[14];
+	status = (status << 8) | event[13];
+	status = (status << 8) | event[12];
+	status = (status << 8) | event[11];
+	*build_version = status;
+
+cleanup:
+	free(event);
+
+	return err;
+}
+
+static void convert_bdaddr(char *str_bdaddr, char *bdaddr)
+{
+	char bdbyte[3];
+	char *str_byte = str_bdaddr;
+	int i, j;
+	int colon_present = 0;
+
+	if (strstr(str_bdaddr, ":"))
+		colon_present = 1;
+
+	bdbyte[2] = '\0';
+
+	/* Reverse the BDADDR to LSB first */
+	for (i = 0, j = 5; i < 6; i++, j--) {
+		bdbyte[0] = str_byte[0];
+		bdbyte[1] = str_byte[1];
+		bdaddr[j] = strtol(bdbyte, NULL, 16);
+
+		if (colon_present == 1)
+			str_byte += 3;
+		else
+			str_byte += 2;
+	}
+}
+
+static int write_bdaddr(int pConfig, char *bdaddr)
+{
+	uint8_t *event;
+	int err;
+	uint8_t cmd[13];
+	uint8_t *ptr = cmd;
+	hci_command_hdr *ch = (void *)cmd;
+
+	memset(cmd, 0, sizeof(cmd));
+
+	ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+						HCI_PS_CMD_OCF));
+	ch->plen = 10;
+	ptr += HCI_COMMAND_HDR_SIZE;
+
+	ptr[0] = 0x01;
+	ptr[1] = 0x01;
+	ptr[2] = 0x00;
+	ptr[3] = 0x06;
+
+	convert_bdaddr(bdaddr, (char *)&ptr[4]);
+
+	err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);
+	if (err < 0)
+		return err;
+
+	err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+	free(event);
+
+	return err;
+}
+
+#define BDADDR_FILE "ar3kbdaddr.pst"
+
+static void write_bdaddr_from_file(int rom_version, int fd)
+{
+	FILE *stream;
+	char bdaddr[PATH_MAX];
+	char bdaddr_file[PATH_MAX];
+
+	snprintf(bdaddr_file, MAXPATHLEN, "%s%x/%s",
+			FW_PATH, rom_version, BDADDR_FILE);
+
+	stream = fopen(bdaddr_file, "r");
+	if (!stream)
+		return;
+
+	if (fgets(bdaddr, PATH_MAX - 1, stream))
+		write_bdaddr(fd, bdaddr);
+
+	fclose(stream);
+}
+
+static int ath_ps_download(int fd)
+{
+	int err = 0;
+	int tag_count;
+	int patch_count = 0;
+	uint32_t rom_version = 0;
+	uint32_t build_version = 0;
+	uint32_t dev_type = 0;
+	char patch_file[PATH_MAX];
+	char ps_file[PATH_MAX];
+	FILE *stream;
+
+	/*
+	 * Verfiy firmware version. depending on it select the PS
+	 * config file to download.
+	 */
+	if (get_device_type(fd, &dev_type) < 0) {
+		err = -EILSEQ;
+		goto download_cmplete;
+	}
+
+	if (read_ath3k_version(fd, &rom_version, &build_version) < 0) {
+		err = -EILSEQ;
+		goto download_cmplete;
+	}
+
+	/* Do not download configuration if CRC passes */
+	if (get_ath3k_crc(fd) < 0) {
+		err = 0;
+		goto download_cmplete;
+	}
+
+	get_ps_file_name(dev_type, rom_version, ps_file);
+	get_patch_file_name(dev_type, rom_version, build_version, patch_file);
+
+	stream = fopen(ps_file, "r");
+	if (!stream) {
+		perror("firmware file open error\n");
+		err = -EILSEQ;
+		goto download_cmplete;
+	}
+	tag_count = ath_parse_ps(stream);
+
+	fclose(stream);
+
+	if (tag_count < 0) {
+		err = -EILSEQ;
+		goto download_cmplete;
+	}
+
+	stream = fopen(patch_file, "r");
+	if(stream) {
+		patch_count = ps_patch_download(fd, stream);
+		fclose(stream);
+
+		if (patch_count < 0) {
+			err = -EILSEQ;
+			goto download_cmplete;
+		}
+	}
+
+	err = ps_config_download(fd, tag_count);
+
+download_cmplete:
+	if (!err)
+		write_bdaddr_from_file(rom_version, fd);
+
+	return err;
+}
+
+#define HCI_SLEEP_CMD_OCF     0x04
+
+/*
+ * Atheros AR300x specific initialization post callback
+ */
+int ath3k_post(int fd, int pm)
+{
+	int dev_id, dd;
+	struct timespec tm = { 0, 50000 };
+
+	sleep(1);
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+	if (dev_id < 0) {
+		perror("cannot get device id");
+		return dev_id;
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		return dd;
+	}
+
+	if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+		perror("hci down:Power management Disabled");
+		hci_close_dev(dd);
+		return -1;
+	}
+
+	/* send vendor specific command with Sleep feature Enabled */
+	if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0)
+		perror("PM command failed, power management Disabled");
+
+	nanosleep(&tm, NULL);
+	hci_close_dev(dd);
+
+	return 0;
+}
+
+#define HCI_VENDOR_CMD_OGF    0x3F
+#define HCI_PS_CMD_OCF        0x0B
+#define HCI_CHG_BAUD_CMD_OCF  0x0C
+
+#define WRITE_BDADDR_CMD_LEN 14
+#define WRITE_BAUD_CMD_LEN   6
+#define MAX_CMD_LEN          WRITE_BDADDR_CMD_LEN
+
+static int set_cntrlr_baud(int fd, int speed)
+{
+	int baud;
+	struct timespec tm = { 0, 500000 };
+	unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];
+	unsigned char *ptr = cmd + 1;
+	hci_command_hdr *ch = (void *)ptr;
+
+	cmd[0] = HCI_COMMAND_PKT;
+
+	/* set controller baud rate to user specified value */
+	ptr = cmd + 1;
+	ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+						HCI_CHG_BAUD_CMD_OCF));
+	ch->plen = 2;
+	ptr += HCI_COMMAND_HDR_SIZE;
+
+	baud = speed/100;
+	ptr[0] = (char)baud;
+	ptr[1] = (char)(baud >> 8);
+
+	if (write(fd, cmd, WRITE_BAUD_CMD_LEN) != WRITE_BAUD_CMD_LEN) {
+		perror("Failed to write change baud rate command");
+		return -ETIMEDOUT;
+	}
+
+	nanosleep(&tm, NULL);
+
+	if (read_hci_event(fd, rsp, sizeof(rsp)) < 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+/*
+ * Atheros AR300x specific initialization and configuration file
+ * download
+ */
+int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
+						struct termios *ti)
+{
+	int r;
+	int err = 0;
+	struct timespec tm = { 0, 500000 };
+	unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];
+	unsigned char *ptr = cmd + 1;
+	hci_command_hdr *ch = (void *)ptr;
+
+	cmd[0] = HCI_COMMAND_PKT;
+
+	/* set both controller and host baud rate to maximum possible value */
+	err = set_cntrlr_baud(fd, speed);
+	if (err < 0)
+		return err;
+
+	err = set_speed(fd, ti, speed);
+	if (err < 0) {
+		perror("Can't set required baud rate");
+		return err;
+	}
+
+	/* Download PS and patch */
+	r = ath_ps_download(fd);
+	if (r < 0) {
+		perror("Failed to Download configuration");
+		err = -ETIMEDOUT;
+		goto failed;
+	}
+
+	/* Write BDADDR */
+	if (bdaddr) {
+		ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+							HCI_PS_CMD_OCF));
+		ch->plen = 10;
+		ptr += HCI_COMMAND_HDR_SIZE;
+
+		ptr[0] = 0x01;
+		ptr[1] = 0x01;
+		ptr[2] = 0x00;
+		ptr[3] = 0x06;
+		str2ba(bdaddr, (bdaddr_t *)(ptr + 4));
+
+		if (write(fd, cmd, WRITE_BDADDR_CMD_LEN) !=
+					WRITE_BDADDR_CMD_LEN) {
+			perror("Failed to write BD_ADDR command\n");
+			err = -ETIMEDOUT;
+			goto failed;
+		}
+
+		if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {
+			perror("Failed to set BD_ADDR\n");
+			err = -ETIMEDOUT;
+			goto failed;
+		}
+	}
+
+	/* Send HCI Reset */
+	cmd[1] = 0x03;
+	cmd[2] = 0x0C;
+	cmd[3] = 0x00;
+
+	r = write(fd, cmd, 4);
+	if (r != 4) {
+		err = -ETIMEDOUT;
+		goto failed;
+	}
+
+	nanosleep(&tm, NULL);
+	if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {
+		err = -ETIMEDOUT;
+		goto failed;
+	}
+
+	err = set_cntrlr_baud(fd, speed);
+	if (err < 0)
+		return err;
+
+failed:
+	if (err < 0) {
+		set_cntrlr_baud(fd, init_speed);
+		set_speed(fd, ti, init_speed);
+	}
+
+	return err;
+}
diff --git a/bluez/repo/tools/hciattach_bcm43xx.c b/bluez/repo/tools/hciattach_bcm43xx.c
new file mode 100644
index 0000000..81f38cb
--- /dev/null
+++ b/bluez/repo/tools/hciattach_bcm43xx.c
@@ -0,0 +1,394 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <time.h>
+#include <limits.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+#ifndef FIRMWARE_DIR
+#define FIRMWARE_DIR "/etc/firmware"
+#endif
+
+#define FW_EXT ".hcd"
+
+#define BCM43XX_CLOCK_48 1
+#define BCM43XX_CLOCK_24 2
+
+#define CMD_SUCCESS 0x00
+
+#define CC_MIN_SIZE 7
+
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+
+static int bcm43xx_read_local_name(int fd, char *name, size_t size)
+{
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x14, 0x0C, 0x00 };
+	unsigned char *resp;
+	unsigned int name_len;
+
+	resp = malloc(size + CC_MIN_SIZE);
+	if (!resp)
+		return -1;
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+		fprintf(stderr, "Failed to write read local name command\n");
+		goto fail;
+	}
+
+	if (read_hci_event(fd, resp, size) < CC_MIN_SIZE) {
+		fprintf(stderr, "Failed to read local name, invalid HCI event\n");
+		goto fail;
+	}
+
+	if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+		fprintf(stderr, "Failed to read local name, command failure\n");
+		goto fail;
+	}
+
+	name_len = resp[2] - 1;
+
+	strncpy(name, (char *) &resp[7], MIN(name_len, size));
+	name[size - 1] = 0;
+
+	free(resp);
+	return 0;
+
+fail:
+	free(resp);
+	return -1;
+}
+
+static int bcm43xx_reset(int fd)
+{
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x03, 0x0C, 0x00 };
+	unsigned char resp[CC_MIN_SIZE];
+
+	if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+		fprintf(stderr, "Failed to write reset command\n");
+		return -1;
+	}
+
+	if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+		fprintf(stderr, "Failed to reset chip, invalid HCI event\n");
+		return -1;
+	}
+
+	if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+		fprintf(stderr, "Failed to reset chip, command failure\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bcm43xx_set_bdaddr(int fd, const char *bdaddr)
+{
+	unsigned char cmd[] =
+		{ HCI_COMMAND_PKT, 0x01, 0xfc, 0x06, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00 };
+	unsigned char resp[CC_MIN_SIZE];
+
+	printf("Set BDADDR UART: %s\n", bdaddr);
+
+	if (str2ba(bdaddr, (bdaddr_t *) (&cmd[4])) < 0) {
+		fprintf(stderr, "Incorrect bdaddr\n");
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+		fprintf(stderr, "Failed to write set bdaddr command\n");
+		return -1;
+	}
+
+	if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+		fprintf(stderr, "Failed to set bdaddr, invalid HCI event\n");
+		return -1;
+	}
+
+	if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+		fprintf(stderr, "Failed to set bdaddr, command failure\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bcm43xx_set_clock(int fd, unsigned char clock)
+{
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x45, 0xfc, 0x01, 0x00 };
+	unsigned char resp[CC_MIN_SIZE];
+
+	printf("Set Controller clock (%d)\n", clock);
+
+	cmd[4] = clock;
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+		fprintf(stderr, "Failed to write update clock command\n");
+		return -1;
+	}
+
+	if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+		fprintf(stderr, "Failed to update clock, invalid HCI event\n");
+		return -1;
+	}
+
+	if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+		fprintf(stderr, "Failed to update clock, command failure\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bcm43xx_set_speed(int fd, struct termios *ti, uint32_t speed)
+{
+	unsigned char cmd[] =
+		{ HCI_COMMAND_PKT, 0x18, 0xfc, 0x06, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00 };
+	unsigned char resp[CC_MIN_SIZE];
+
+	if (speed > 3000000 && bcm43xx_set_clock(fd, BCM43XX_CLOCK_48))
+		return -1;
+
+	printf("Set Controller UART speed to %d bit/s\n", speed);
+
+	cmd[6] = (uint8_t) (speed);
+	cmd[7] = (uint8_t) (speed >> 8);
+	cmd[8] = (uint8_t) (speed >> 16);
+	cmd[9] = (uint8_t) (speed >> 24);
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+		fprintf(stderr, "Failed to write update baudrate command\n");
+		return -1;
+	}
+
+	if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+		fprintf(stderr, "Failed to update baudrate, invalid HCI event\n");
+		return -1;
+	}
+
+	if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+		fprintf(stderr, "Failed to update baudrate, command failure\n");
+		return -1;
+	}
+
+	if (set_speed(fd, ti, speed) < 0) {
+		perror("Can't set host baud rate");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bcm43xx_load_firmware(int fd, const char *fw)
+{
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x2e, 0xfc, 0x00 };
+	struct timespec tm_mode = { 0, 50000 };
+	struct timespec tm_ready = { 0, 2000000 };
+	unsigned char resp[CC_MIN_SIZE];
+	unsigned char tx_buf[1024];
+	int len, fd_fw, n;
+
+	printf("Flash firmware %s\n", fw);
+
+	fd_fw = open(fw, O_RDONLY);
+	if (fd_fw < 0) {
+		fprintf(stderr, "Unable to open firmware (%s)\n", fw);
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+		fprintf(stderr, "Failed to write download mode command\n");
+		goto fail;
+	}
+
+	if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+		fprintf(stderr, "Failed to load firmware, invalid HCI event\n");
+		goto fail;
+	}
+
+	if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+		fprintf(stderr, "Failed to load firmware, command failure\n");
+		goto fail;
+	}
+
+	/* Wait 50ms to let the firmware placed in download mode */
+	nanosleep(&tm_mode, NULL);
+
+	tcflush(fd, TCIOFLUSH);
+
+	while ((n = read(fd_fw, &tx_buf[1], 3))) {
+		if (n < 0) {
+			fprintf(stderr, "Failed to read firmware\n");
+			goto fail;
+		}
+
+		tx_buf[0] = HCI_COMMAND_PKT;
+
+		len = tx_buf[3];
+
+		if (read(fd_fw, &tx_buf[4], len) < 0) {
+			fprintf(stderr, "Failed to read firmware\n");
+			goto fail;
+		}
+
+		if (write(fd, tx_buf, len + 4) != (len + 4)) {
+			fprintf(stderr, "Failed to write firmware\n");
+			goto fail;
+		}
+
+		read_hci_event(fd, resp, sizeof(resp));
+		tcflush(fd, TCIOFLUSH);
+	}
+
+	/* Wait for firmware ready */
+	nanosleep(&tm_ready, NULL);
+
+	close(fd_fw);
+	return 0;
+
+fail:
+	close(fd_fw);
+	return -1;
+}
+
+static int bcm43xx_locate_patch(const char *dir_name,
+		const char *chip_name, char *location)
+{
+	DIR *dir;
+	int ret = -1;
+
+	dir = opendir(dir_name);
+	if (!dir) {
+		fprintf(stderr, "Cannot open directory '%s': %s\n",
+				dir_name, strerror(errno));
+		return -1;
+	}
+
+	/* Recursively look for a BCM43XX*.hcd */
+	while (1) {
+		struct dirent *entry = readdir(dir);
+		if (!entry)
+			break;
+
+		if (entry->d_type & DT_DIR) {
+			char path[PATH_MAX];
+
+			if (!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, "."))
+				continue;
+
+			snprintf(path, PATH_MAX, "%s/%s", dir_name, entry->d_name);
+
+			ret = bcm43xx_locate_patch(path, chip_name, location);
+			if (!ret)
+				break;
+		} else if (!strncmp(chip_name, entry->d_name, strlen(chip_name))) {
+			unsigned int name_len = strlen(entry->d_name);
+			size_t curs_ext = name_len - sizeof(FW_EXT) + 1;
+
+			if (curs_ext > name_len)
+				break;
+
+			if (strncmp(FW_EXT, &entry->d_name[curs_ext], sizeof(FW_EXT)))
+				break;
+
+			/* found */
+			snprintf(location, PATH_MAX, "%s/%s", dir_name, entry->d_name);
+			ret = 0;
+			break;
+		}
+	}
+
+	closedir(dir);
+
+	return ret;
+}
+
+int bcm43xx_init(int fd, int def_speed, int speed, struct termios *ti,
+		const char *bdaddr)
+{
+	char chip_name[20];
+	char fw_path[PATH_MAX];
+
+	printf("bcm43xx_init\n");
+
+	if (bcm43xx_reset(fd))
+		return -1;
+
+	if (bcm43xx_read_local_name(fd, chip_name, sizeof(chip_name)))
+		return -1;
+
+	if (bcm43xx_locate_patch(FIRMWARE_DIR, chip_name, fw_path)) {
+		fprintf(stderr, "Patch not found, continue anyway\n");
+	} else {
+		if (bcm43xx_set_speed(fd, ti, speed))
+			return -1;
+
+		if (bcm43xx_load_firmware(fd, fw_path))
+			return -1;
+
+		/* Controller speed has been reset to def speed */
+		if (set_speed(fd, ti, def_speed) < 0) {
+			perror("Can't set host baud rate");
+			return -1;
+		}
+
+		if (bcm43xx_reset(fd))
+			return -1;
+	}
+
+	if (bdaddr)
+		bcm43xx_set_bdaddr(fd, bdaddr);
+
+	if (bcm43xx_set_speed(fd, ti, speed))
+		return -1;
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hciattach_intel.c b/bluez/repo/tools/hciattach_intel.c
new file mode 100644
index 0000000..2650dcb
--- /dev/null
+++ b/bluez/repo/tools/hciattach_intel.c
@@ -0,0 +1,595 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+#ifdef INTEL_DEBUG
+#define DBGPRINT(fmt, args...)	printf("DBG: " fmt "\n", ## args)
+#define PRINT_PACKET(buf, len, msg)	{	\
+	int i;					\
+	printf("%s\n", msg);			\
+	for (i = 0; i < len; i++)		\
+		printf("%02X ", buf[i]);	\
+	printf("\n");				\
+	}
+#else
+#define DBGPRINT(fmt, args...)
+#define PRINT_PACKET(buf, len, msg)
+#endif
+
+#define PATCH_SEQ_EXT           ".bseq"
+#define PATCH_FILE_PATH         "/lib/firmware/intel/"
+#define PATCH_MAX_LEN           260
+#define PATCH_TYPE_CMD          1
+#define PATCH_TYPE_EVT          2
+
+#define INTEL_VER_PARAM_LEN     9
+#define INTEL_MFG_PARAM_LEN     2
+
+/**
+ * A data structure for a patch entry.
+ */
+struct patch_entry {
+	int type;
+	int len;
+	unsigned char data[PATCH_MAX_LEN];
+};
+
+/**
+ * A structure for patch context
+ */
+struct patch_ctx {
+	int dev;
+	int fd;
+	int patch_error;
+	int reset_enable_patch;
+};
+
+/**
+ * Send HCI command to the controller
+ */
+static int intel_write_cmd(int dev, unsigned char *buf, int len)
+{
+	int ret;
+
+	PRINT_PACKET(buf, len, "<----- SEND CMD: ");
+
+	ret = write(dev, buf, len);
+	if (ret < 0)
+		return -errno;
+
+	if (ret != len)
+		return -1;
+
+	return ret;
+}
+
+/**
+ * Read the event from the controller
+ */
+static int intel_read_evt(int dev, unsigned char *buf, int len)
+{
+	int ret;
+
+	ret = read_hci_event(dev, buf, len);
+	if (ret < 0)
+		return -1;
+
+	PRINT_PACKET(buf, ret, "-----> READ EVT: ");
+
+	return ret;
+}
+
+/**
+ * Validate HCI events
+ */
+static int validate_events(struct patch_entry *event,
+		struct patch_entry *entry)
+{
+	if (event == NULL || entry == NULL) {
+		DBGPRINT("invalid patch entry parameters");
+		return -1;
+	}
+
+	if (event->len != entry->len) {
+		DBGPRINT("lengths are mismatched:[%d|%d]",
+				event->len, entry->len);
+		return -1;
+	}
+
+	if (memcmp(event->data, entry->data, event->len)) {
+		DBGPRINT("data is mismatched");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Read the next patch entry one line at a time
+ */
+static int get_next_patch_entry(int fd, struct patch_entry *entry)
+{
+	int size;
+	char rb;
+
+	if (read(fd, &rb, 1) <= 0)
+		return 0;
+
+	entry->type = rb;
+
+	switch (entry->type) {
+	case PATCH_TYPE_CMD:
+		entry->data[0] = HCI_COMMAND_PKT;
+
+		if (read(fd, &entry->data[1], 3) < 0)
+			return -1;
+
+		size = (int)entry->data[3];
+
+		if (read(fd, &entry->data[4], size) < 0)
+			return -1;
+
+		entry->len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + size;
+
+		break;
+
+	case PATCH_TYPE_EVT:
+		entry->data[0] = HCI_EVENT_PKT;
+
+		if (read(fd, &entry->data[1], 2) < 0)
+			return -1;
+
+		size = (int)entry->data[2];
+
+		if (read(fd, &entry->data[3], size) < 0)
+			return -1;
+
+		entry->len = HCI_TYPE_LEN + HCI_EVENT_HDR_SIZE + size;
+
+		break;
+
+	default:
+		fprintf(stderr, "invalid patch entry(%d)\n", entry->type);
+		return -1;
+	}
+
+	return entry->len;
+}
+
+/**
+ * Download the patch set to the controller and verify the event
+ */
+static int intel_download_patch(struct patch_ctx *ctx)
+{
+	int ret;
+	struct patch_entry entry;
+	struct patch_entry event;
+
+	DBGPRINT("start patch downloading");
+
+	do {
+		ret = get_next_patch_entry(ctx->fd, &entry);
+		if (ret <= 0) {
+			ctx->patch_error = 1;
+			break;
+		}
+
+		switch (entry.type) {
+		case PATCH_TYPE_CMD:
+			ret = intel_write_cmd(ctx->dev,
+					entry.data,
+					entry.len);
+			if (ret <= 0) {
+				fprintf(stderr, "failed to send cmd(%d)\n",
+						ret);
+				return ret;
+			}
+			break;
+
+		case PATCH_TYPE_EVT:
+			ret = intel_read_evt(ctx->dev, event.data,
+					sizeof(event.data));
+			if (ret <= 0) {
+				fprintf(stderr, "failed to read evt(%d)\n",
+						ret);
+				return ret;
+			}
+			event.len = ret;
+
+			if (validate_events(&event, &entry) < 0) {
+				DBGPRINT("events are mismatched");
+				ctx->patch_error = 1;
+				return -1;
+			}
+			break;
+
+		default:
+			fprintf(stderr, "unknown patch type(%d)\n",
+					entry.type);
+			return -1;
+		}
+	} while (1);
+
+	return ret;
+}
+
+static int open_patch_file(struct patch_ctx *ctx, char *fw_ver)
+{
+	char patch_file[PATH_MAX];
+
+	snprintf(patch_file, PATH_MAX, "%s%s%s", PATCH_FILE_PATH,
+			fw_ver, PATCH_SEQ_EXT);
+	DBGPRINT("PATCH_FILE: %s", patch_file);
+
+	ctx->fd = open(patch_file, O_RDONLY);
+	if (ctx->fd < 0) {
+		DBGPRINT("cannot open patch file. go to post patch");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Prepare the controller for patching.
+ */
+static int pre_patch(struct patch_ctx *ctx)
+{
+	int ret, i;
+	struct patch_entry entry;
+	char fw_ver[INTEL_VER_PARAM_LEN * 2];
+
+	DBGPRINT("start pre_patch");
+
+	entry.data[0] = HCI_COMMAND_PKT;
+	entry.data[1] = 0x11;
+	entry.data[2] = 0xFC;
+	entry.data[3] = 0x02;
+	entry.data[4] = 0x01;
+	entry.data[5] = 0x00;
+	entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+	ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+	entry.len = ret;
+
+	if (entry.data[6] != 0x00) {
+		DBGPRINT("command failed. status=%02x", entry.data[6]);
+		ctx->patch_error = 1;
+		return -1;
+	}
+
+	entry.data[0] = HCI_COMMAND_PKT;
+	entry.data[1] = 0x05;
+	entry.data[2] = 0xFC;
+	entry.data[3] = 0x00;
+	entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE;
+
+	ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+	entry.len = ret;
+
+	if (entry.data[6] != 0x00) {
+		DBGPRINT("command failed. status=%02x", entry.data[6]);
+		ctx->patch_error = 1;
+		return -1;
+	}
+
+	for (i = 0; i < INTEL_VER_PARAM_LEN; i++)
+		sprintf(&fw_ver[i*2], "%02x", entry.data[7+i]);
+
+	if (open_patch_file(ctx, fw_ver) < 0) {
+		ctx->patch_error = 1;
+		return -1;
+	}
+
+	return ret;
+}
+
+/*
+ * check the event is startup event
+ */
+static int is_startup_evt(unsigned char *buf)
+{
+	if (buf[1] == 0xFF && buf[2] == 0x01 && buf[3] == 0x00)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Finalize the patch process and reset the controller
+ */
+static int post_patch(struct patch_ctx *ctx)
+{
+	int ret;
+	struct patch_entry entry;
+
+	DBGPRINT("start post_patch");
+
+	entry.data[0] = HCI_COMMAND_PKT;
+	entry.data[1] = 0x11;
+	entry.data[2] = 0xFC;
+	entry.data[3] = 0x02;
+	entry.data[4] = 0x00;
+	if (ctx->reset_enable_patch)
+		entry.data[5] = 0x02;
+	else
+		entry.data[5] = 0x01;
+
+	entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+	ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+	entry.len = ret;
+
+	if (entry.data[6] != 0x00) {
+		fprintf(stderr, "cmd failed. st=%02x\n", entry.data[6]);
+		return -1;
+	}
+
+	do {
+		ret = intel_read_evt(ctx->dev, entry.data,
+					sizeof(entry.data));
+		if (ret < 0) {
+			fprintf(stderr, "failed to read cmd(%d)\n", ret);
+			return ret;
+		}
+		entry.len = ret;
+	} while (!is_startup_evt(entry.data));
+
+	return ret;
+}
+
+/**
+ * Main routine that handles the device patching process.
+ */
+static int intel_patch_device(struct patch_ctx *ctx)
+{
+	int ret;
+
+	ret = pre_patch(ctx);
+	if (ret < 0) {
+		if (!ctx->patch_error) {
+			fprintf(stderr, "I/O error: pre_patch failed\n");
+			return ret;
+		}
+
+		DBGPRINT("patch failed. proceed to post patch");
+		goto post_patch;
+	}
+
+	ret = intel_download_patch(ctx);
+	if (ret < 0) {
+		if (!ctx->patch_error) {
+			fprintf(stderr, "I/O error: download_patch failed\n");
+			close(ctx->fd);
+			return ret;
+		}
+	} else {
+		DBGPRINT("patch done");
+		ctx->reset_enable_patch = 1;
+	}
+
+	close(ctx->fd);
+
+post_patch:
+	ret = post_patch(ctx);
+	if (ret < 0) {
+		fprintf(stderr, "post_patch failed(%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int set_rts(int dev, int rtsval)
+{
+	int arg;
+
+	if (ioctl(dev, TIOCMGET, &arg) < 0) {
+		perror("cannot get TIOCMGET");
+		return -errno;
+	}
+	if (rtsval)
+		arg |= TIOCM_RTS;
+	else
+		arg &= ~TIOCM_RTS;
+
+	if (ioctl(dev, TIOCMSET, &arg) == -1) {
+		perror("cannot set TIOCMGET");
+		return -errno;
+	}
+
+	return 0;
+}
+
+static unsigned char get_intel_speed(int speed)
+{
+	switch (speed) {
+	case 9600:
+		return 0x00;
+	case 19200:
+		return 0x01;
+	case 38400:
+		return 0x02;
+	case 57600:
+		return 0x03;
+	case 115200:
+		return 0x04;
+	case 230400:
+		return 0x05;
+	case 460800:
+		return 0x06;
+	case 921600:
+		return 0x07;
+	case 1843200:
+		return 0x08;
+	case 3250000:
+		return 0x09;
+	case 2000000:
+		return 0x0A;
+	case 3000000:
+		return 0x0B;
+	default:
+		return 0xFF;
+	}
+}
+
+/**
+ * if it failed to change to new baudrate, it will rollback
+ * to initial baudrate
+ */
+static int change_baudrate(int dev, int init_speed, int *speed,
+				struct termios *ti)
+{
+	int ret;
+	unsigned char br;
+	unsigned char cmd[5];
+	unsigned char evt[7];
+
+	DBGPRINT("start baudrate change");
+
+	ret = set_rts(dev, 0);
+	if (ret < 0) {
+		fprintf(stderr, "failed to clear RTS\n");
+		return ret;
+	}
+
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x06;
+	cmd[2] = 0xFC;
+	cmd[3] = 0x01;
+
+	br = get_intel_speed(*speed);
+	if (br == 0xFF) {
+		fprintf(stderr, "speed %d is not supported\n", *speed);
+		return -1;
+	}
+	cmd[4] = br;
+
+	ret = intel_write_cmd(dev, cmd, sizeof(cmd));
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	/*
+	 *  wait for buffer to be consumed by the controller
+	 */
+	usleep(300000);
+
+	if (set_speed(dev, ti, *speed) < 0) {
+		fprintf(stderr, "can't set to new baud rate\n");
+		return -1;
+	}
+
+	ret = set_rts(dev, 1);
+	if (ret < 0) {
+		fprintf(stderr, "failed to set RTS\n");
+		return ret;
+	}
+
+	ret = intel_read_evt(dev, evt, sizeof(evt));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+
+	if (evt[4] != 0x00) {
+		fprintf(stderr,
+			"failed to change speed. use default speed %d\n",
+			init_speed);
+		*speed = init_speed;
+	}
+
+	return 0;
+}
+
+/**
+ * An entry point for Intel specific initialization
+ */
+int intel_init(int dev, int init_speed, int *speed, struct termios *ti)
+{
+	int ret = 0;
+	struct patch_ctx ctx;
+
+	if (change_baudrate(dev, init_speed, speed, ti) < 0)
+		return -1;
+
+	ctx.dev = dev;
+	ctx.patch_error = 0;
+	ctx.reset_enable_patch = 0;
+
+	ret = intel_patch_device(&ctx);
+	if (ret < 0)
+		fprintf(stderr, "failed to initialize the device");
+
+	return ret;
+}
diff --git a/bluez/repo/tools/hciattach_qualcomm.c b/bluez/repo/tools/hciattach_qualcomm.c
new file mode 100644
index 0000000..22ac629
--- /dev/null
+++ b/bluez/repo/tools/hciattach_qualcomm.c
@@ -0,0 +1,274 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+#define FAILIF(x, args...) do { \
+	if (x) { \
+		fprintf(stderr, ##args); \
+		return -1; \
+	} \
+} while (0)
+
+typedef struct {
+	uint8_t uart_prefix;
+	hci_event_hdr hci_hdr;
+	evt_cmd_complete cmd_complete;
+	uint8_t status;
+	uint8_t data[16];
+} __attribute__((packed)) command_complete_t;
+
+static int read_command_complete(int fd,
+					unsigned short opcode,
+					unsigned char len)
+{
+	command_complete_t resp;
+	unsigned char vsevent[512];
+	int n;
+
+	/* Read reply. */
+	n = read_hci_event(fd, vsevent, sizeof(vsevent));
+	FAILIF(n < 0, "Failed to read response");
+
+	FAILIF(vsevent[1] != 0xFF, "Failed to read response");
+
+	n = read_hci_event(fd, (unsigned char *)&resp, sizeof(resp));
+	FAILIF(n < 0, "Failed to read response");
+
+	/* event must be event-complete */
+	FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE,
+		"Error in response: not a cmd-complete event, "
+		"but 0x%02x!\n", resp.hci_hdr.evt);
+
+	FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+		"Error in response: plen is not >= 4, but 0x%02x!\n",
+		resp.hci_hdr.plen);
+
+	/* cmd-complete event: opcode */
+	FAILIF(resp.cmd_complete.opcode != 0,
+		"Error in response: opcode is 0x%04x, not 0!",
+		resp.cmd_complete.opcode);
+
+	return resp.status == 0 ? 0 : -1;
+}
+
+static int qualcomm_load_firmware(int fd, const char *firmware, const char *bdaddr_s)
+{
+
+	int fw = open(firmware, O_RDONLY);
+
+	fprintf(stdout, "Opening firmware file: %s\n", firmware);
+
+	FAILIF(fw < 0,
+		"Could not open firmware file %s: %s (%d).\n",
+		firmware, strerror(errno), errno);
+
+	fprintf(stdout, "Uploading firmware...\n");
+	do {
+		/* Read each command and wait for a response. */
+		unsigned char data[1024];
+		unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+		hci_command_hdr *cmd = (hci_command_hdr *) (cmdp + 1);
+		int nr;
+
+		nr = read(fw, cmdp, sizeof(cmdp));
+		if (!nr)
+			break;
+
+		FAILIF(nr != sizeof(cmdp),
+			"Could not read H4 + HCI header!\n");
+		FAILIF(*cmdp != HCI_COMMAND_PKT,
+			"Command is not an H4 command packet!\n");
+
+		FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+				"Could not read %d bytes of data \
+				for command with opcode %04x!\n",
+				cmd->plen, cmd->opcode);
+
+		if ((data[0] == 1) && (data[1] == 2) && (data[2] == 6)) {
+			bdaddr_t bdaddr;
+			if (bdaddr_s != NULL) {
+				str2ba(bdaddr_s, &bdaddr);
+				memcpy(&data[3], &bdaddr, sizeof(bdaddr_t));
+			}
+		}
+
+		{
+			int nw;
+			struct iovec iov_cmd[2];
+			iov_cmd[0].iov_base = cmdp;
+			iov_cmd[0].iov_len = sizeof(cmdp);
+			iov_cmd[1].iov_base = data;
+			iov_cmd[1].iov_len = cmd->plen;
+			nw = writev(fd, iov_cmd, 2);
+			FAILIF(nw != (int) sizeof(cmdp) + cmd->plen,
+				"Could not send entire command \
+				(sent only %d bytes)!\n",
+				nw);
+		}
+
+		/* Wait for response */
+		if (read_command_complete(fd, cmd->opcode, cmd->plen) < 0)
+			return -1;
+	} while (1);
+	fprintf(stdout, "Firmware upload successful.\n");
+
+	close(fw);
+
+	return 0;
+}
+
+int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr)
+{
+	struct timespec tm = {0, 50000};
+	char cmd[5];
+	unsigned char resp[100];		/* Response */
+	char fw[100];
+	int n;
+
+	memset(resp, 0, 100);
+
+	/* Get Manufacturer and LMP version */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x01;
+	cmd[2] = 0x10;
+	cmd[3] = 0x00;
+
+	do {
+		n = write(fd, cmd, 4);
+		if (n < 4) {
+			perror("Failed to write init command");
+			return -1;
+		}
+
+		/* Read reply. */
+		if (read_hci_event(fd, resp, 100) < 0) {
+			perror("Failed to read init response");
+			return -1;
+		}
+
+		/* Wait for command complete event for our Opcode */
+	} while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+	/* Verify manufacturer */
+	if ((resp[11] & 0xFF) != 0x1d)
+		fprintf(stderr,
+			"WARNING : module's manufacturer is not Qualcomm\n");
+
+	/* Print LMP version */
+	fprintf(stderr,
+		"Qualcomm module LMP version : 0x%02x\n", resp[10] & 0xFF);
+
+	/* Print LMP subversion */
+	{
+		unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+
+		fprintf(stderr, "Qualcomm module LMP sub-version : 0x%04x\n",
+								lmp_subv);
+	}
+
+	/* Get SoC type */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x00;
+	cmd[2] = 0xFC;
+	cmd[3] = 0x01;
+	cmd[4] = 0x06;
+
+	do {
+		n = write(fd, cmd, 5);
+		if (n < 5) {
+			perror("Failed to write vendor init command");
+			return -1;
+		}
+
+		/* Read reply. */
+		if ((n = read_hci_event(fd, resp, 100)) < 0) {
+			perror("Failed to read vendor init response");
+			return -1;
+		}
+
+	} while (resp[3] != 0 && resp[4] != 2);
+
+	snprintf(fw, sizeof(fw), "/etc/firmware/%c%c%c%c%c%c_%c%c%c%c.bin",
+				resp[18], resp[19], resp[20], resp[21],
+				resp[22], resp[23],
+				resp[32], resp[33], resp[34], resp[35]);
+
+	/* Wait for command complete event for our Opcode */
+	if (read_hci_event(fd, resp, 100) < 0) {
+		perror("Failed to read init response");
+		return -1;
+	}
+
+	qualcomm_load_firmware(fd, fw, bdaddr);
+
+	/* Reset */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x03;
+	cmd[2] = 0x0C;
+	cmd[3] = 0x00;
+
+	do {
+		n = write(fd, cmd, 4);
+		if (n < 4) {
+			perror("Failed to write reset command");
+			return -1;
+		}
+
+		/* Read reply. */
+		if ((n = read_hci_event(fd, resp, 100)) < 0) {
+			perror("Failed to read reset response");
+			return -1;
+		}
+
+	} while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+	nanosleep(&tm, NULL);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hciattach_st.c b/bluez/repo/tools/hciattach_st.c
new file mode 100644
index 0000000..474545a
--- /dev/null
+++ b/bluez/repo/tools/hciattach_st.c
@@ -0,0 +1,278 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+
+#include "lib/bluetooth.h"
+
+#include "hciattach.h"
+
+static int debug = 0;
+
+static int do_command(int fd, uint8_t ogf, uint16_t ocf,
+			uint8_t *cparam, int clen, uint8_t *rparam, int rlen)
+{
+	//uint16_t opcode = (uint16_t) ((ocf & 0x03ff) | (ogf << 10));
+	unsigned char cp[260], rp[260];
+	int len, size, offset = 3;
+
+	cp[0] = 0x01;
+	cp[1] = ocf & 0xff;
+	cp[2] = ogf << 2 | ocf >> 8;
+	cp[3] = clen;
+
+	if (clen > 0)
+		memcpy(cp + 4, cparam, clen);
+
+	if (debug) {
+		int i;
+		printf("[<");
+		for (i = 0; i < clen + 4; i++)
+			printf(" %02x", cp[i]);
+		printf("]\n");
+	}
+
+	if (write(fd, cp, clen + 4) < 0)
+		return -1;
+
+	do {
+		if (read(fd, rp, 1) < 1)
+			return -1;
+	} while (rp[0] != 0x04);
+
+	if (read(fd, rp + 1, 2) < 2)
+		return -1;
+
+	do {
+		len = read(fd, rp + offset, sizeof(rp) - offset);
+		offset += len;
+	} while (offset < rp[2] + 3);
+
+	if (debug) {
+		int i;
+		printf("[>");
+		for (i = 0; i < offset; i++)
+			printf(" %02x", rp[i]);
+		printf("]\n");
+	}
+
+	if (rp[0] != 0x04) {
+		errno = EIO;
+		return -1;
+	}
+
+	switch (rp[1]) {
+	case 0x0e:	/* command complete */
+		if (rp[6] != 0x00)
+			return -ENXIO;
+		offset = 3 + 4;
+		size = rp[2] - 4;
+		break;
+	case 0x0f:	/* command status */
+		/* fall through */
+	default:
+		offset = 3;
+		size = rp[2];
+		break;
+	}
+
+	if (!rparam || rlen < size)
+		return -ENXIO;
+
+	memcpy(rparam, rp + offset, size);
+
+	return size;
+}
+
+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];
+	unsigned char cmd[256];
+	unsigned char buf[256];
+	uint8_t seqnum = 0;
+	int fd, size, len, found_fw_file;
+
+	memset(filename, 0, sizeof(filename));
+
+	snprintf(prefix, sizeof(prefix), "STLC2500_R%d_%02d_",
+						version >> 8, version & 0xff);
+
+	strcpy(pathname, "/lib/firmware");
+	dir = opendir(pathname);
+	if (!dir) {
+		strcpy(pathname, ".");
+		dir = opendir(pathname);
+		if (!dir)
+			return -errno;
+	}
+
+	found_fw_file = 0;
+	while (1) {
+		d = readdir(dir);
+		if (!d)
+			break;
+
+		if (strncmp(d->d_name + strlen(d->d_name) - strlen(suffix),
+						suffix, strlen(suffix)))
+			continue;
+
+		if (strncmp(d->d_name, prefix, strlen(prefix)))
+			continue;
+
+		snprintf(filename, sizeof(filename), "%s/%s",
+							pathname, d->d_name);
+		found_fw_file = 1;
+	}
+
+	closedir(dir);
+
+	if (!found_fw_file)
+		return -ENOENT;
+
+	printf("Loading file %s\n", filename);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		perror("Can't open firmware file");
+		return -errno;
+	}
+
+	while (1) {
+		size = read(fd, cmd + 1, 254);
+		if (size <= 0)
+			break;
+
+		cmd[0] = seqnum;
+
+		len = do_command(dd, 0xff, 0x002e, cmd, size + 1, buf, sizeof(buf));
+		if (len < 1)
+			break;
+
+		if (buf[0] != seqnum) {
+			fprintf(stderr, "Sequence number mismatch\n");
+			break;
+		}
+
+		seqnum++;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
+int stlc2500_init(int dd, bdaddr_t *bdaddr)
+{
+	unsigned char cmd[16];
+	unsigned char buf[254];
+	uint16_t version;
+	int len;
+	int err;
+
+	/* Hci_Cmd_Ericsson_Read_Revision_Information */
+	len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	printf("%s\n", buf);
+
+	/* HCI_Read_Local_Version_Information */
+	len = do_command(dd, 0x04, 0x0001, NULL, 0, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	version = buf[2] << 8 | buf[1];
+
+	err = load_file(dd, version, ".ptc");
+	if (err < 0) {
+		if (err == -ENOENT)
+			fprintf(stderr, "No ROM patch file loaded.\n");
+		else
+			return -1;
+	}
+
+	err = load_file(dd, buf[2] << 8 | buf[1], ".ssf");
+	if (err < 0) {
+		if (err == -ENOENT)
+			fprintf(stderr, "No static settings file loaded.\n");
+		else
+			return -1;
+	}
+
+	cmd[0] = 0xfe;
+	cmd[1] = 0x06;
+	bacpy((bdaddr_t *) (cmd + 2), bdaddr);
+
+	/* Hci_Cmd_ST_Store_In_NVDS */
+	len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	/* HCI_Reset : applies parameters*/
+	len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	return 0;
+}
+
+int bgb2xx_init(int dd, bdaddr_t *bdaddr)
+{
+	unsigned char cmd[16];
+	unsigned char buf[254];
+	int len;
+
+	len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	printf("%s\n", buf);
+
+	cmd[0] = 0xfe;
+	cmd[1] = 0x06;
+	bacpy((bdaddr_t *) (cmd + 2), bdaddr);
+
+	len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf));
+	if (len < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hciattach_ti.c b/bluez/repo/tools/hciattach_ti.c
new file mode 100644
index 0000000..828dd61
--- /dev/null
+++ b/bluez/repo/tools/hciattach_ti.c
@@ -0,0 +1,533 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2007-2008  Texas Instruments, Inc.
+ *  Copyright (C) 2005-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 <unistd.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+#ifdef HCIATTACH_DEBUG
+#define DPRINTF(x...)	printf(x)
+#else
+#define DPRINTF(x...)
+#endif
+
+#define HCIUARTGETDEVICE	_IOR('U', 202, int)
+
+#define MAKEWORD(a, b)  ((uint16_t)(((uint8_t)(a)) | ((uint16_t)((uint8_t)(b))) << 8))
+
+#define TI_MANUFACTURER_ID	13
+
+#define FIRMWARE_DIRECTORY	"/lib/firmware/ti-connectivity/"
+
+#define ACTION_SEND_COMMAND	1
+#define ACTION_WAIT_EVENT	2
+#define ACTION_SERIAL		3
+#define ACTION_DELAY		4
+#define ACTION_RUN_SCRIPT	5
+#define ACTION_REMARKS		6
+
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_1	0x0c
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_2	0xfd
+#define BRF_DEEP_SLEEP_OPCODE		\
+	(BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8))
+
+#define FILE_HEADER_MAGIC	0x42535442
+
+/*
+ * BRF Firmware header
+ */
+struct bts_header {
+	uint32_t	magic;
+	uint32_t	version;
+	uint8_t	future[24];
+	uint8_t	actions[0];
+}__attribute__ ((packed));
+
+/*
+ * BRF Actions structure
+ */
+struct bts_action {
+	uint16_t	type;
+	uint16_t	size;
+	uint8_t	data[0];
+} __attribute__ ((packed));
+
+struct bts_action_send {
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+struct bts_action_wait {
+	uint32_t msec;
+	uint32_t size;
+	uint8_t data[0];
+}__attribute__ ((packed));
+
+struct bts_action_delay {
+	uint32_t msec;
+}__attribute__ ((packed));
+
+struct bts_action_serial {
+	uint32_t baud;
+	uint32_t flow_control;
+}__attribute__ ((packed));
+
+static FILE *bts_load_script(const char *file_name, uint32_t *version)
+{
+	struct bts_header header;
+	FILE *fp;
+
+	fp = fopen(file_name, "rb");
+	if (!fp) {
+		perror("can't open firmware file");
+		return NULL;
+	}
+
+	if (1 != fread(&header, sizeof(struct bts_header), 1, fp)) {
+		perror("can't read firmware file");
+		goto errclose;
+	}
+
+	if (header.magic != FILE_HEADER_MAGIC) {
+		fprintf(stderr, "%s not a legal TI firmware file\n", file_name);
+		goto errclose;
+	}
+
+	if (NULL != version)
+		*version = header.version;
+
+	return fp;
+
+errclose:
+	fclose(fp);
+
+	return NULL;
+}
+
+static unsigned long bts_fetch_action(FILE *fp, unsigned char *action_buf,
+				unsigned long buf_size, uint16_t *action_type)
+{
+	struct bts_action action_hdr;
+	unsigned long nread;
+
+	if (!fp)
+		return 0;
+
+	if (1 != fread(&action_hdr, sizeof(struct bts_action), 1, fp))
+		return 0;
+
+	if (action_hdr.size > buf_size) {
+		fprintf(stderr, "bts_next_action: not enough space to read next action\n");
+		return 0;
+	}
+
+	nread = fread(action_buf, sizeof(uint8_t), action_hdr.size, fp);
+	if (nread != (action_hdr.size)) {
+		fprintf(stderr, "bts_next_action: fread failed to read next action\n");
+		return 0;
+	}
+
+	*action_type = action_hdr.type;
+
+	return nread * sizeof(uint8_t);
+}
+
+static void bts_unload_script(FILE *fp)
+{
+	if (fp)
+		fclose(fp);
+}
+
+static int is_it_texas(const uint8_t *respond)
+{
+	uint16_t manufacturer_id;
+
+	manufacturer_id = MAKEWORD(respond[11], respond[12]);
+
+	return TI_MANUFACTURER_ID == manufacturer_id ? 1 : 0;
+}
+
+static const char *get_firmware_name(const uint8_t *respond)
+{
+	static char firmware_file_name[PATH_MAX] = {0};
+	uint16_t version = 0, chip = 0, min_ver = 0, maj_ver = 0;
+
+	version = MAKEWORD(respond[13], respond[14]);
+	chip =  (version & 0x7C00) >> 10;
+	min_ver = (version & 0x007F);
+	maj_ver = (version & 0x0380) >> 7;
+
+	if (version & 0x8000)
+		maj_ver |= 0x0008;
+
+	sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+
+	return firmware_file_name;
+}
+
+static void brf_delay(struct bts_action_delay *delay)
+{
+	usleep(1000 * delay->msec);
+}
+
+static int brf_set_serial_params(struct bts_action_serial *serial_action,
+						int fd, int *speed, struct termios *ti)
+{
+	fprintf(stderr, "texas: changing baud rate to %u, flow control to %u\n",
+				serial_action->baud, serial_action->flow_control );
+	tcflush(fd, TCIOFLUSH);
+
+	if (serial_action->flow_control)
+		ti->c_cflag |= CRTSCTS;
+	else
+		ti->c_cflag &= ~CRTSCTS;
+
+	if (tcsetattr(fd, TCSANOW, ti) < 0) {
+		perror("Can't set port settings");
+		return -1;
+	}
+
+	tcflush(fd, TCIOFLUSH);
+
+	if (set_speed(fd, ti, serial_action->baud) < 0) {
+		perror("Can't set baud rate");
+		return -1;
+	}
+
+	if (speed)
+		*speed = serial_action->baud;
+
+	return 0;
+}
+
+static int brf_send_command_socket(int fd, struct bts_action_send *send_action)
+{
+	char response[1024] = {0};
+	hci_command_hdr *cmd = (hci_command_hdr *) send_action->data;
+	uint16_t opcode = cmd->opcode;
+
+	struct hci_request rq;
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = cmd_opcode_ogf(opcode);
+	rq.ocf    = cmd_opcode_ocf(opcode);
+	rq.event  = EVT_CMD_COMPLETE;
+	rq.cparam = &send_action->data[3];
+	rq.clen   = send_action->data[2];
+	rq.rparam = response;
+	rq.rlen   = sizeof(response);
+
+	if (hci_send_req(fd, &rq, 15) < 0) {
+		perror("Cannot send hci command to socket");
+		return -1;
+	}
+
+	/* verify success */
+	if (response[0]) {
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int brf_send_command_file(int fd, struct bts_action_send *send_action,
+								long size)
+{
+	unsigned char response[1024] = {0};
+	long ret = 0;
+
+	/* send command */
+	if (size != write(fd, send_action, size)) {
+		perror("Texas: Failed to write action command");
+		return -1;
+	}
+
+	/* read response */
+	ret = read_hci_event(fd, response, sizeof(response));
+	if (ret < 0) {
+		perror("texas: failed to read command response");
+		return -1;
+	}
+
+	/* verify success */
+	if (ret < 7 || 0 != response[6]) {
+		fprintf( stderr, "TI init command failed.\n" );
+		errno = EIO;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int brf_send_command(int fd, struct bts_action_send *send_action,
+						long size, int hcill_installed)
+{
+	int ret = 0;
+	char *fixed_action;
+
+	/* remove packet type when giving to socket API */
+	if (hcill_installed) {
+		fixed_action = ((char *) send_action) + 1;
+		ret = brf_send_command_socket(fd, (struct bts_action_send *) fixed_action);
+	} else {
+		ret = brf_send_command_file(fd, send_action, size);
+	}
+
+	return ret;
+}
+
+static int brf_do_action(uint16_t brf_type, uint8_t *brf_action, long brf_size,
+				int fd, int *speed, struct termios *ti, int hcill_installed)
+{
+	int ret = 0;
+
+	switch (brf_type) {
+	case ACTION_SEND_COMMAND:
+		DPRINTF("W");
+		ret = brf_send_command(fd,
+					(struct bts_action_send *) brf_action,
+					brf_size, hcill_installed);
+		break;
+	case ACTION_WAIT_EVENT:
+		DPRINTF("R");
+		break;
+	case ACTION_SERIAL:
+		DPRINTF("S");
+		ret = brf_set_serial_params((struct bts_action_serial *) brf_action, fd, speed, ti);
+		break;
+	case ACTION_DELAY:
+		DPRINTF("D");
+		brf_delay((struct bts_action_delay *) brf_action);
+		break;
+	case ACTION_REMARKS:
+		DPRINTF("C");
+		break;
+	default:
+		fprintf(stderr, "brf_init: unknown firmware action type (%d)\n", brf_type);
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * tests whether a given brf action is a HCI_VS_Sleep_Mode_Configurations cmd
+ */
+static int brf_action_is_deep_sleep(uint8_t *brf_action, long brf_size,
+							uint16_t brf_type)
+{
+	uint16_t opcode;
+
+	if (brf_type != ACTION_SEND_COMMAND)
+		return 0;
+
+	if (brf_size < 3)
+		return 0;
+
+	if (brf_action[0] != HCI_COMMAND_PKT)
+		return 0;
+
+	/* HCI data is little endian */
+	opcode = brf_action[1] | (brf_action[2] << 8);
+
+	if (opcode != BRF_DEEP_SLEEP_OPCODE)
+		return 0;
+
+	/* action is deep sleep configuration command ! */
+	return 1;
+}
+
+/*
+ * This function is called twice.
+ * The first time it is called, it loads the brf script, and executes its
+ * commands until it reaches a deep sleep command (or its end).
+ * The second time it is called, it assumes HCILL protocol is set up,
+ * and sends rest of brf script via the supplied socket.
+ */
+static int brf_do_script(int fd, int *speed, struct termios *ti, const char *bts_file)
+{
+	int ret = 0,  hcill_installed = bts_file ? 0 : 1;
+	uint32_t vers;
+	static FILE *brf_script_file = NULL;
+	static uint8_t brf_action[512];
+	static long brf_size;
+	static uint16_t brf_type;
+
+	/* is it the first time we are called ? */
+	if (0 == hcill_installed) {
+		DPRINTF("Sending script to serial device\n");
+		brf_script_file = bts_load_script(bts_file, &vers );
+		if (!brf_script_file) {
+			fprintf(stderr, "Warning: cannot find BTS file: %s\n",
+					bts_file);
+			return 0;
+		}
+
+		fprintf( stderr, "Loaded BTS script version %u\n", vers );
+
+		brf_size = bts_fetch_action(brf_script_file, brf_action,
+						sizeof(brf_action), &brf_type);
+		if (brf_size == 0) {
+			fprintf(stderr, "Warning: BTS file is empty !");
+			return 0;
+		}
+	}
+	else {
+		DPRINTF("Sending script to bluetooth socket\n");
+	}
+
+	/* execute current action and continue to parse brf script file */
+	while (brf_size != 0) {
+		ret = brf_do_action(brf_type, brf_action, brf_size,
+						fd, speed, ti, hcill_installed);
+		if (ret == -1)
+			break;
+
+		brf_size = bts_fetch_action(brf_script_file, brf_action,
+						sizeof(brf_action), &brf_type);
+
+		/* if this is the first time we run (no HCILL yet) */
+		/* and a deep sleep command is encountered */
+		/* we exit */
+		if (!hcill_installed &&
+				brf_action_is_deep_sleep(brf_action,
+							brf_size, brf_type))
+			return 0;
+	}
+
+	bts_unload_script(brf_script_file);
+	brf_script_file = NULL;
+	DPRINTF("\n");
+
+	return ret;
+}
+
+int texas_init(int fd, int *speed, struct termios *ti)
+{
+	struct timespec tm = {0, 50000};
+	char cmd[4];
+	unsigned char resp[100];		/* Response */
+	const char *bts_file;
+	int n;
+
+	memset(resp,'\0', 100);
+
+	/* It is possible to get software version with manufacturer specific
+	   HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+	   is if this is point-to-point or point-to-multipoint module */
+
+	/* Get Manufacturer and LMP version */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x01;
+	cmd[2] = 0x10;
+	cmd[3] = 0x00;
+
+	do {
+		n = write(fd, cmd, 4);
+		if (n < 0) {
+			perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+			return -1;
+		}
+		if (n < 4) {
+			fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+			return -1;
+		}
+
+		/* Read reply. */
+		if (read_hci_event(fd, resp, 100) < 0) {
+			perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+			return -1;
+		}
+
+		/* Wait for command complete event for our Opcode */
+	} while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+	/* Verify manufacturer */
+	if (! is_it_texas(resp)) {
+		fprintf(stderr,"ERROR: module's manufacturer is not Texas Instruments\n");
+		return -1;
+	}
+
+	fprintf(stderr, "Found a Texas Instruments' chip!\n");
+
+	bts_file = get_firmware_name(resp);
+	fprintf(stderr, "Firmware file : %s\n", bts_file);
+
+	n = brf_do_script(fd, speed, ti, bts_file);
+
+	nanosleep(&tm, NULL);
+
+	return n;
+}
+
+int texas_post(int fd, struct termios *ti)
+{
+	int dev_id, dd, ret = 0;
+
+	sleep(1);
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+	if (dev_id < 0) {
+		perror("cannot get device id");
+		return -1;
+	}
+
+	DPRINTF("\nAdded device hci%d\n", dev_id);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		return -1;
+	}
+
+	if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+		fprintf(stderr, "Can't init device hci%d: %s (%d)", dev_id,
+							strerror(errno), errno);
+		hci_close_dev(dd);
+		return -1;
+	}
+
+	ret = brf_do_script(dd, NULL, ti, NULL);
+
+	hci_close_dev(dd);
+
+	return ret;
+}
diff --git a/bluez/repo/tools/hciattach_tialt.c b/bluez/repo/tools/hciattach_tialt.c
new file mode 100644
index 0000000..f6ef068
--- /dev/null
+++ b/bluez/repo/tools/hciattach_tialt.c
@@ -0,0 +1,243 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "hciattach.h"
+
+#define FAILIF(x, args...) do {   \
+	if (x) {					  \
+		fprintf(stderr, ##args);  \
+		return -1;				  \
+	}							  \
+} while(0)
+
+typedef struct {
+	uint8_t uart_prefix;
+	hci_event_hdr hci_hdr;
+	evt_cmd_complete cmd_complete;
+	uint8_t status;
+	uint8_t data[16];
+} __attribute__((packed)) command_complete_t;
+
+static int read_command_complete(int fd, unsigned short opcode, unsigned char len) {
+	command_complete_t resp;
+	/* Read reply. */
+	FAILIF(read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0,
+		   "Failed to read response");
+
+	/* Parse speed-change reply */
+	FAILIF(resp.uart_prefix != HCI_EVENT_PKT,
+		   "Error in response: not an event packet, but 0x%02x!\n",
+		   resp.uart_prefix);
+
+	FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */
+		   "Error in response: not a cmd-complete event, "
+		   "but 0x%02x!\n", resp.hci_hdr.evt);
+
+	FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+		   "Error in response: plen is not >= 4, but 0x%02x!\n",
+		   resp.hci_hdr.plen);
+
+	/* cmd-complete event: opcode */
+	FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode,
+		   "Error in response: opcode is 0x%04x, not 0x%04x!",
+		   resp.cmd_complete.opcode, opcode);
+
+	return resp.status == 0 ? 0 : -1;
+}
+
+typedef struct {
+	uint8_t uart_prefix;
+	hci_command_hdr hci_hdr;
+	uint32_t speed;
+} __attribute__((packed)) texas_speed_change_cmd_t;
+
+static int texas_change_speed(int fd, uint32_t speed)
+{
+	return 0;
+}
+
+static int texas_load_firmware(int fd, const char *firmware) {
+
+	int fw = open(firmware, O_RDONLY);
+
+	fprintf(stdout, "Opening firmware file: %s\n", firmware);
+
+	FAILIF(fw < 0,
+		   "Could not open firmware file %s: %s (%d).\n",
+		   firmware, strerror(errno), errno);
+
+	fprintf(stdout, "Uploading firmware...\n");
+	do {
+		/* Read each command and wait for a response. */
+		unsigned char data[1024];
+		unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+		hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1);
+		int nr;
+		nr = read(fw, cmdp, sizeof(cmdp));
+		if (!nr)
+			break;
+		FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n");
+		FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n");
+
+		FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+			   "Could not read %d bytes of data for command with opcode %04x!\n",
+			   cmd->plen,
+			   cmd->opcode);
+
+		{
+			int nw;
+#if 0
+			fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n",
+					cmd->opcode,
+					cmd->plen);
+#endif
+			struct iovec iov_cmd[2];
+			iov_cmd[0].iov_base = cmdp;
+			iov_cmd[0].iov_len	= sizeof(cmdp);
+			iov_cmd[1].iov_base = data;
+			iov_cmd[1].iov_len	= cmd->plen;
+			nw = writev(fd, iov_cmd, 2);
+			FAILIF(nw != (int) sizeof(cmd) +	cmd->plen,
+				   "Could not send entire command (sent only %d bytes)!\n",
+				   nw);
+		}
+
+		/* Wait for response */
+		if (read_command_complete(fd,
+								  cmd->opcode,
+								  cmd->plen) < 0) {
+			return -1;
+		}
+
+	} while(1);
+	fprintf(stdout, "Firmware upload successful.\n");
+
+	close(fw);
+	return 0;
+}
+
+int texasalt_init(int fd, int speed, struct termios *ti)
+{
+	struct timespec tm = {0, 50000};
+	char cmd[4];
+	unsigned char resp[100];		/* Response */
+	int n;
+
+	memset(resp,'\0', 100);
+
+	/* It is possible to get software version with manufacturer specific
+	   HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+	   is if this is point-to-point or point-to-multipoint module */
+
+	/* Get Manufacturer and LMP version */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x01;
+	cmd[2] = 0x10;
+	cmd[3] = 0x00;
+
+	do {
+		n = write(fd, cmd, 4);
+		if (n < 0) {
+			perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+			return -1;
+		}
+		if (n < 4) {
+			fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+			return -1;
+		}
+
+		/* Read reply. */
+		if (read_hci_event(fd, resp, 100) < 0) {
+			perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+			return -1;
+		}
+
+		/* Wait for command complete event for our Opcode */
+	} while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+	/* Verify manufacturer */
+	if ((resp[11] & 0xFF) != 0x0d)
+		fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n");
+
+	/* Print LMP version */
+	fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF);
+
+	/* Print LMP subversion */
+	{
+		unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+		unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10;
+		static const char *c_brf_chip[8] = {
+			"unknown",
+			"unknown",
+			"brf6100",
+			"brf6150",
+			"brf6300",
+			"brf6350",
+			"unknown",
+			"wl1271"
+		};
+		char fw[100];
+
+		fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv);
+
+		fprintf(stderr,
+				"\tinternal version freeze: %d\n"
+				"\tsoftware version: %d\n"
+				"\tchip: %s (%d)\n",
+				lmp_subv & 0x7f,
+				((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7),
+				((brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]),
+				brf_chip);
+
+		sprintf(fw, "/etc/firmware/%s.bin", c_brf_chip[brf_chip]);
+		texas_load_firmware(fd, fw);
+
+		texas_change_speed(fd, speed);
+	}
+	nanosleep(&tm, NULL);
+	return 0;
+}
diff --git a/bluez/repo/tools/hciconfig.1 b/bluez/repo/tools/hciconfig.1
new file mode 100644
index 0000000..633ffa3
--- /dev/null
+++ b/bluez/repo/tools/hciconfig.1
@@ -0,0 +1,272 @@
+.TH HCICONFIG 1 "Nov 11 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciconfig \- configure Bluetooth devices
+.SH SYNOPSIS
+.B hciconfig
+.B \-h
+.br
+.B hciconfig
+.RB [\| \-a \|]
+.br
+.B hciconfig
+.RB [\| \-a \|]
+.B hciX
+.RI [\| command
+.RI [\| "command parameters" \|]\|]
+
+.SH DESCRIPTION
+.LP
+.B hciconfig
+is used to configure Bluetooth devices.
+.I hciX
+is the name of a Bluetooth device installed in the system. If
+.I hciX
+is not given,
+.B hciconfig
+prints name and basic information about all the Bluetooth devices installed in
+the system. If
+.I hciX
+is given but no command is given, it prints basic information on device
+.I hciX
+only. Basic information is
+interface type, BD address, ACL MTU, SCO MTU, flags (up, init, running, raw,
+page scan enabled, inquiry scan enabled, inquiry, authentication enabled,
+encryption enabled).
+.SH OPTIONS
+.TP
+.B \-h, \-\-help
+Gives a list of possible commands.
+.TP
+.B \-a, \-\-all
+Other than the basic info, print features, packet type, link policy, link mode,
+name, class, version.
+.SH COMMANDS
+.TP
+.B up
+Open and initialize HCI device.
+.TP
+.B down
+Close HCI device.
+.TP
+.B reset
+Reset HCI device.
+.TP
+.B rstat
+Reset statistic counters.
+.TP
+.B auth
+Enable authentication (sets device to security mode 3).
+.TP
+.B noauth
+Disable authentication.
+.TP
+.B encrypt
+Enable encryption (sets device to security mode 3).
+.TP
+.B noencrypt
+Disable encryption.
+.TP
+.B secmgr
+Enable security manager (current kernel support is limited).
+.TP
+.B nosecmgr
+Disable security manager.
+.TP
+.B piscan
+Enable page and inquiry scan.
+.TP
+.B noscan
+Disable page and inquiry scan.
+.TP
+.B iscan
+Enable inquiry scan, disable page scan.
+.TP
+.B pscan
+Enable page scan, disable inquiry scan.
+.TP
+\fBptype\fP [\fItype\fP]
+With no
+.I type
+, displays the current packet types. Otherwise, all the packet types specified
+by
+.I type
+are set.
+.I type
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+.TP
+.BI name " [name]"
+With no
+.IR name ,
+prints local name. Otherwise, sets local name to
+.IR name .
+.TP
+.BI class " [class]"
+With no
+.IR class ,
+prints class of device. Otherwise, sets class of device to
+.IR class .
+.I
+class
+is a 24-bit hex number describing the class of device, as specified in section
+1.2 of the Bluetooth Assigned Numers document.
+.TP
+.BI voice " [voice]"
+With no
+.IR voice ,
+prints voice setting. Otherwise, sets voice setting to
+.IR voice .
+.I voice
+is a 16-bit hex number describing the voice setting.
+.TP
+.BI iac " [iac]"
+With no
+.IR iac ,
+prints the current IAC setting. Otherwise, sets the IAC to
+.IR iac .
+.TP
+.BI inqtpl " [level]"
+With no
+.IR level ,
+prints out the current inquiry transmit power level. Otherwise, sets
+inquiry transmit power level to
+.IR level .
+.TP
+.BI inqmode " [mode]"
+With no
+.IR mode ,
+prints out the current inquiry mode. Otherwise, sets inquiry mode to
+.IR mode .
+.TP
+.BI inqdata " [data]"
+With no
+.IR name ,
+prints out the current inquiry data. Otherwise, sets inquiry data to
+.IR data .
+.TP
+.BI inqtype " [type]"
+With no
+.IR type ,
+prints out the current inquiry scan type. Otherwise, sets inquiry scan type to
+.IR type .
+.TP
+\fBinqparams\fP [\fIwin\fP:\fIint\fP]
+With no
+.IR win : int ,
+prints inquiry scan window and interval. Otherwise, sets inquiry scan window
+to
+.I win
+slots and inquiry scan interval to
+.I int
+slots.
+.TP
+\fBpageparms\fP [\fIwin\fP:\fIint\fP]
+With no
+.IR win : int ,
+prints page scan window and interval. Otherwise, sets page scan window to
+.I win
+slots and page scan interval to
+.I int
+slots.
+.TP
+.BI pageto " [to]"
+With no
+.IR to ,
+prints page timeout. Otherwise, sets page timeout
+to .I
+to
+slots.
+.TP
+.BI afhmode " [mode]"
+With no
+.IR mode ,
+prints out the current AFH mode. Otherwise, sets AFH mode to
+.IR mode .
+.TP
+.BI sspmode " [mode]"
+With no
+.IR mode ,
+prints out the current Simple Pairing mode. Otherwise, sets Simple Pairing mode to
+.IR mode .
+.TP
+\fBaclmtu\fP \fImtu\fP:\fIpkt\fP
+Sets ACL MTU to
+to
+.I mtu
+bytes and ACL buffer size to
+.I pkt
+packets.
+.TP
+\fBscomtu\fP \fImtu\fP:\fIpkt\fP
+Sets SCO MTU to
+.I mtu
+bytes and SCO buffer size to
+.I pkt
+packets.
+.TP
+.BI delkey " <bdaddr>"
+This command deletes the stored link key for
+.I bdaddr
+from the device.
+.TP
+.BI oobdata
+Get local OOB data (invalidates previously read data).
+.TP
+.BI commands
+Display supported commands.
+.TP
+.BI features
+Display device features.
+.TP
+.BI version
+Display version information.
+.TP
+.BI revision
+Display revision information.
+.TP
+.BI lm " [mode]"
+With no
+.I mode
+, prints link mode.
+.B MASTER
+or
+.B SLAVE
+mean, respectively, to ask to become master or to remain slave when a
+connection request comes in. The additional keyword
+.B ACCEPT
+means that baseband  connections will be accepted even if there are no
+listening
+.I AF_BLUETOOTH
+sockets.
+.I mode
+is
+.B NONE
+or a comma-separated list of keywords, where possible keywords are
+.B MASTER
+and
+.B "ACCEPT" .
+.B NONE
+sets link policy to the default behaviour of remaining slave and not accepting
+baseband connections when there are no listening
+.I AF_BLUETOOTH
+sockets. If
+.B MASTER
+is present, the device will ask to become master if a connection request comes
+in. If
+.B ACCEPT
+is present, the device will accept baseband connections even when there are no
+listening
+.I AF_BLUETOOTH
+sockets.
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/bluez/repo/tools/hciconfig.c b/bluez/repo/tools/hciconfig.c
new file mode 100644
index 0000000..9029f7d
--- /dev/null
+++ b/bluez/repo/tools/hciconfig.c
@@ -0,0 +1,2064 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/textfile.h"
+#include "src/shared/util.h"
+#include "tools/csr.h"
+
+static struct hci_dev_info di;
+static int all;
+
+static void print_dev_hdr(struct hci_dev_info *di);
+static void print_dev_info(int ctl, struct hci_dev_info *di);
+
+static void print_dev_list(int ctl, int flags)
+{
+	struct hci_dev_list_req *dl;
+	struct hci_dev_req *dr;
+	int i;
+
+	if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +
+		sizeof(uint16_t)))) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+	dl->dev_num = HCI_MAX_DEV;
+	dr = dl->dev_req;
+
+	if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
+		perror("Can't get device list");
+		free(dl);
+		exit(1);
+	}
+
+	for (i = 0; i< dl->dev_num; i++) {
+		di.dev_id = (dr+i)->dev_id;
+		if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)
+			continue;
+		print_dev_info(ctl, &di);
+	}
+
+	free(dl);
+}
+
+static void print_pkt_type(struct hci_dev_info *di)
+{
+	char *str;
+	str = hci_ptypetostr(di->pkt_type);
+	printf("\tPacket type: %s\n", str);
+	bt_free(str);
+}
+
+static void print_link_policy(struct hci_dev_info *di)
+{
+	printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
+}
+
+static void print_link_mode(struct hci_dev_info *di)
+{
+	char *str;
+	str =  hci_lmtostr(di->link_mode);
+	printf("\tLink mode: %s\n", str);
+	bt_free(str);
+}
+
+static void print_dev_features(struct hci_dev_info *di, int format)
+{
+	printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+				"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		di->features[0], di->features[1], di->features[2],
+		di->features[3], di->features[4], di->features[5],
+		di->features[6], di->features[7]);
+
+	if (format) {
+		char *tmp = lmp_featurestostr(di->features, "\t\t", 63);
+		printf("%s\n", tmp);
+		bt_free(tmp);
+	}
+}
+
+static void print_le_states(uint64_t states)
+{
+	int i;
+	const char *le_states[] = {
+		"Non-connectable Advertising State" ,
+		"Scannable Advertising State",
+		"Connectable Advertising State",
+		"Directed Advertising State",
+		"Passive Scanning State",
+		"Active Scanning State",
+		"Initiating State/Connection State in Master Role",
+		"Connection State in the Slave Role",
+		"Non-connectable Advertising State and Passive Scanning State combination",
+		"Scannable Advertising State and Passive Scanning State combination",
+		"Connectable Advertising State and Passive Scanning State combination",
+		"Directed Advertising State and Passive Scanning State combination",
+		"Non-connectable Advertising State and Active Scanning State combination",
+		"Scannable Advertising State and Active Scanning State combination",
+		"Connectable Advertising State and Active Scanning State combination",
+		"Directed Advertising State and Active Scanning State combination",
+		"Non-connectable Advertising State and Initiating State combination",
+		"Scannable Advertising State and Initiating State combination",
+		"Non-connectable Advertising State and Master Role combination",
+		"Scannable Advertising State and Master Role combination",
+		"Non-connectable Advertising State and Slave Role combination",
+		"Scannable Advertising State and Slave Role combination",
+		"Passive Scanning State and Initiating State combination",
+		"Active Scanning State and Initiating State combination",
+		"Passive Scanning State and Master Role combination",
+		"Active Scanning State and Master Role combination",
+		"Passive Scanning State and Slave Role combination",
+		"Active Scanning State and Slave Role combination",
+		"Initiating State and Master Role combination/Master Role and Master Role combination",
+		NULL
+	};
+
+	printf("Supported link layer states:\n");
+	for (i = 0; le_states[i]; i++) {
+		const char *status;
+
+		status = states & (1 << i) ? "YES" : "NO ";
+		printf("\t%s %s\n", status, le_states[i]);
+	}
+}
+
+static void cmd_rstat(int ctl, int hdev, char *opt)
+{
+	/* Reset HCI device stat counters */
+	if (ioctl(ctl, HCIDEVRESTAT, hdev) < 0) {
+		fprintf(stderr, "Can't reset stats counters hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_scan(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr;
+
+	dr.dev_id  = hdev;
+	dr.dev_opt = SCAN_DISABLED;
+	if (!strcmp(opt, "iscan"))
+		dr.dev_opt = SCAN_INQUIRY;
+	else if (!strcmp(opt, "pscan"))
+		dr.dev_opt = SCAN_PAGE;
+	else if (!strcmp(opt, "piscan"))
+		dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;
+
+	if (ioctl(ctl, HCISETSCAN, (unsigned long) &dr) < 0) {
+		fprintf(stderr, "Can't set scan mode on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_le_addr(int ctl, int hdev, char *opt)
+{
+	struct hci_request rq;
+	le_set_random_address_cp cp;
+	uint8_t status;
+	int dd, err, ret;
+
+	if (!opt)
+		return;
+
+	if (hdev < 0)
+		hdev = hci_get_route(NULL);
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		err = -errno;
+		fprintf(stderr, "Could not open device: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+
+	memset(&cp, 0, sizeof(cp));
+
+	str2ba(opt, &cp.bdaddr);
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_RANDOM_ADDRESS;
+	rq.cparam = &cp;
+	rq.clen = LE_SET_RANDOM_ADDRESS_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	ret = hci_send_req(dd, &rq, 1000);
+	if (status || ret < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't set random address for hci%d: "
+				"%s (%d)\n", hdev, strerror(-err), -err);
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_le_adv(int ctl, int hdev, char *opt)
+{
+	struct hci_request rq;
+	le_set_advertise_enable_cp advertise_cp;
+	le_set_advertising_parameters_cp adv_params_cp;
+	uint8_t status;
+	int dd, ret;
+
+	if (hdev < 0)
+		hdev = hci_get_route(NULL);
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	memset(&adv_params_cp, 0, sizeof(adv_params_cp));
+	adv_params_cp.min_interval = htobs(0x0800);
+	adv_params_cp.max_interval = htobs(0x0800);
+	if (opt)
+		adv_params_cp.advtype = atoi(opt);
+	adv_params_cp.chan_map = 7;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_ADVERTISING_PARAMETERS;
+	rq.cparam = &adv_params_cp;
+	rq.clen = LE_SET_ADVERTISING_PARAMETERS_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	ret = hci_send_req(dd, &rq, 1000);
+	if (ret < 0)
+		goto done;
+
+	memset(&advertise_cp, 0, sizeof(advertise_cp));
+	advertise_cp.enable = 0x01;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+	rq.cparam = &advertise_cp;
+	rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	ret = hci_send_req(dd, &rq, 1000);
+
+done:
+	hci_close_dev(dd);
+
+	if (ret < 0) {
+		fprintf(stderr, "Can't set advertise mode on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (status) {
+		fprintf(stderr,
+			"LE set advertise enable on hci%d returned status %d\n",
+								hdev, status);
+		exit(1);
+	}
+}
+
+static void cmd_no_le_adv(int ctl, int hdev, char *opt)
+{
+	struct hci_request rq;
+	le_set_advertise_enable_cp advertise_cp;
+	uint8_t status;
+	int dd, ret;
+
+	if (hdev < 0)
+		hdev = hci_get_route(NULL);
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	memset(&advertise_cp, 0, sizeof(advertise_cp));
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf = OGF_LE_CTL;
+	rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+	rq.cparam = &advertise_cp;
+	rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+	rq.rparam = &status;
+	rq.rlen = 1;
+
+	ret = hci_send_req(dd, &rq, 1000);
+
+	hci_close_dev(dd);
+
+	if (ret < 0) {
+		fprintf(stderr, "Can't set advertise mode on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (status) {
+		fprintf(stderr, "LE set advertise enable on hci%d returned status %d\n",
+						hdev, status);
+		exit(1);
+	}
+}
+
+static void cmd_le_states(int ctl, int hdev, char *opt)
+{
+	le_read_supported_states_rp rp;
+	struct hci_request rq;
+	int err, dd;
+
+	if (hdev < 0)
+		hdev = hci_get_route(NULL);
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	memset(&rp, 0, sizeof(rp));
+	memset(&rq, 0, sizeof(rq));
+
+	rq.ogf    = OGF_LE_CTL;
+	rq.ocf    = OCF_LE_READ_SUPPORTED_STATES;
+	rq.rparam = &rp;
+	rq.rlen   = LE_READ_SUPPORTED_STATES_RP_SIZE;
+
+	err = hci_send_req(dd, &rq, 1000);
+
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		fprintf(stderr, "Can't read LE supported states on hci%d:"
+				" %s(%d)\n", hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (rp.status) {
+		fprintf(stderr, "Read LE supported states on hci%d"
+				" returned status %d\n", hdev, rp.status);
+		exit(1);
+	}
+
+	print_le_states(rp.states);
+}
+
+static void cmd_iac(int ctl, int hdev, char *opt)
+{
+	int s = hci_open_dev(hdev);
+
+	if (s < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+	if (opt) {
+		int l = strtoul(opt, 0, 16);
+		uint8_t lap[3];
+		if (!strcasecmp(opt, "giac")) {
+			l = 0x9e8b33;
+		} else if (!strcasecmp(opt, "liac")) {
+			l = 0x9e8b00;
+		} else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+			printf("Invalid access code 0x%x\n", l);
+			exit(1);
+		}
+		lap[0] = (l & 0xff);
+		lap[1] = (l >> 8) & 0xff;
+		lap[2] = (l >> 16) & 0xff;
+		if (hci_write_current_iac_lap(s, 1, lap, 1000) < 0) {
+			printf("Failed to set IAC on hci%d: %s\n", hdev, strerror(errno));
+			exit(1);
+		}
+	} else {
+		uint8_t lap[3 * MAX_IAC_LAP];
+		int i, j;
+		uint8_t n;
+		if (hci_read_current_iac_lap(s, &n, lap, 1000) < 0) {
+			printf("Failed to read IAC from hci%d: %s\n", hdev, strerror(errno));
+			exit(1);
+		}
+		print_dev_hdr(&di);
+		printf("\tIAC: ");
+		for (i = 0; i < n; i++) {
+			printf("0x");
+			for (j = 3; j--; )
+				printf("%02x", lap[j + 3 * i]);
+			if (i < n - 1)
+				printf(", ");
+		}
+		printf("\n");
+	}
+	close(s);
+}
+
+static void cmd_auth(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr;
+
+	dr.dev_id = hdev;
+	if (!strcmp(opt, "auth"))
+		dr.dev_opt = AUTH_ENABLED;
+	else
+		dr.dev_opt = AUTH_DISABLED;
+
+	if (ioctl(ctl, HCISETAUTH, (unsigned long) &dr) < 0) {
+		fprintf(stderr, "Can't set auth on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_encrypt(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr;
+
+	dr.dev_id = hdev;
+	if (!strcmp(opt, "encrypt"))
+		dr.dev_opt = ENCRYPT_P2P;
+	else
+		dr.dev_opt = ENCRYPT_DISABLED;
+
+	if (ioctl(ctl, HCISETENCRYPT, (unsigned long) &dr) < 0) {
+		fprintf(stderr, "Can't set encrypt on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_up(int ctl, int hdev, char *opt)
+{
+	/* Start HCI device */
+	if (ioctl(ctl, HCIDEVUP, hdev) < 0) {
+		if (errno == EALREADY)
+			return;
+		fprintf(stderr, "Can't init device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_down(int ctl, int hdev, char *opt)
+{
+	/* Stop HCI device */
+	if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) {
+		fprintf(stderr, "Can't down device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_reset(int ctl, int hdev, char *opt)
+{
+	/* Reset HCI device */
+#if 0
+	if (ioctl(ctl, HCIDEVRESET, hdev) < 0 ){
+		fprintf(stderr, "Reset failed for device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+#endif
+	cmd_down(ctl, hdev, "down");
+	cmd_up(ctl, hdev, "up");
+}
+
+static void cmd_ptype(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr;
+
+	dr.dev_id = hdev;
+
+	if (hci_strtoptype(opt, &dr.dev_opt)) {
+		if (ioctl(ctl, HCISETPTYPE, (unsigned long) &dr) < 0) {
+			fprintf(stderr, "Can't set pkttype on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		print_dev_hdr(&di);
+		print_pkt_type(&di);
+	}
+}
+
+static void cmd_lp(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr;
+
+	dr.dev_id = hdev;
+
+	if (hci_strtolp(opt, &dr.dev_opt)) {
+		if (ioctl(ctl, HCISETLINKPOL, (unsigned long) &dr) < 0) {
+			fprintf(stderr, "Can't set link policy on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		print_dev_hdr(&di);
+		print_link_policy(&di);
+	}
+}
+
+static void cmd_lm(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr;
+
+	dr.dev_id = hdev;
+
+	if (hci_strtolm(opt, &dr.dev_opt)) {
+		if (ioctl(ctl, HCISETLINKMODE, (unsigned long) &dr) < 0) {
+			fprintf(stderr, "Can't set default link mode on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		print_dev_hdr(&di);
+		print_link_mode(&di);
+	}
+}
+
+static void cmd_aclmtu(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr = { .dev_id = hdev };
+	uint16_t mtu, mpkt;
+
+	if (!opt)
+		return;
+
+	if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
+		return;
+
+	dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+
+	if (ioctl(ctl, HCISETACLMTU, (unsigned long) &dr) < 0) {
+		fprintf(stderr, "Can't set ACL mtu on hci%d: %s(%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_scomtu(int ctl, int hdev, char *opt)
+{
+	struct hci_dev_req dr = { .dev_id = hdev };
+	uint16_t mtu, mpkt;
+
+	if (!opt)
+		return;
+
+	if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
+		return;
+
+	dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+
+	if (ioctl(ctl, HCISETSCOMTU, (unsigned long) &dr) < 0) {
+		fprintf(stderr, "Can't set SCO mtu on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+}
+
+static void cmd_features(int ctl, int hdev, char *opt)
+{
+	uint8_t features[8], max_page = 0;
+	char *tmp;
+	int i, dd;
+
+	if (!(di.features[7] & LMP_EXT_FEAT)) {
+		print_dev_hdr(&di);
+		print_dev_features(&di, 1);
+		return;
+	}
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (hci_read_local_ext_features(dd, 0, &max_page, features, 1000) < 0) {
+		fprintf(stderr, "Can't read extended features hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR))
+		max_page = 1;
+
+	print_dev_hdr(&di);
+	printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+				"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		(max_page > 0) ? " page 0" : "",
+		features[0], features[1], features[2], features[3],
+		features[4], features[5], features[6], features[7]);
+
+	tmp = lmp_featurestostr(di.features, "\t\t", 63);
+	printf("%s\n", tmp);
+	bt_free(tmp);
+
+	for (i = 1; i <= max_page; i++) {
+		if (hci_read_local_ext_features(dd, i, NULL,
+							features, 1000) < 0)
+			continue;
+
+		printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+					"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+			features[0], features[1], features[2], features[3],
+			features[4], features[5], features[6], features[7]);
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_name(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		if (hci_write_local_name(dd, opt, 2000) < 0) {
+			fprintf(stderr, "Can't change local name on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		char name[249];
+		int i;
+
+		if (hci_read_local_name(dd, sizeof(name), name, 1000) < 0) {
+			fprintf(stderr, "Can't read local name on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		for (i = 0; i < 248 && name[i]; i++) {
+			if ((unsigned char) name[i] < 32 || name[i] == 127)
+				name[i] = '.';
+		}
+
+		name[248] = '\0';
+
+		print_dev_hdr(&di);
+		printf("\tName: '%s'\n", name);
+	}
+
+	hci_close_dev(dd);
+}
+
+/*
+ * see http://www.bluetooth.org/assigned-numbers/baseband.htm --- all
+ * strings are reproduced verbatim
+ */
+static char *get_minor_device_name(int major, int minor)
+{
+	switch (major) {
+	case 0:	/* misc */
+		return "";
+	case 1:	/* computer */
+		switch (minor) {
+		case 0:
+			return "Uncategorized";
+		case 1:
+			return "Desktop workstation";
+		case 2:
+			return "Server";
+		case 3:
+			return "Laptop";
+		case 4:
+			return "Handheld";
+		case 5:
+			return "Palm";
+		case 6:
+			return "Wearable";
+		}
+		break;
+	case 2:	/* phone */
+		switch (minor) {
+		case 0:
+			return "Uncategorized";
+		case 1:
+			return "Cellular";
+		case 2:
+			return "Cordless";
+		case 3:
+			return "Smart phone";
+		case 4:
+			return "Wired modem or voice gateway";
+		case 5:
+			return "Common ISDN Access";
+		case 6:
+			return "Sim Card Reader";
+		}
+		break;
+	case 3:	/* lan access */
+		if (minor == 0)
+			return "Uncategorized";
+		switch (minor / 8) {
+		case 0:
+			return "Fully available";
+		case 1:
+			return "1-17% utilized";
+		case 2:
+			return "17-33% utilized";
+		case 3:
+			return "33-50% utilized";
+		case 4:
+			return "50-67% utilized";
+		case 5:
+			return "67-83% utilized";
+		case 6:
+			return "83-99% utilized";
+		case 7:
+			return "No service available";
+		}
+		break;
+	case 4:	/* audio/video */
+		switch (minor) {
+		case 0:
+			return "Uncategorized";
+		case 1:
+			return "Device conforms to the Headset profile";
+		case 2:
+			return "Hands-free";
+			/* 3 is reserved */
+		case 4:
+			return "Microphone";
+		case 5:
+			return "Loudspeaker";
+		case 6:
+			return "Headphones";
+		case 7:
+			return "Portable Audio";
+		case 8:
+			return "Car Audio";
+		case 9:
+			return "Set-top box";
+		case 10:
+			return "HiFi Audio Device";
+		case 11:
+			return "VCR";
+		case 12:
+			return "Video Camera";
+		case 13:
+			return "Camcorder";
+		case 14:
+			return "Video Monitor";
+		case 15:
+			return "Video Display and Loudspeaker";
+		case 16:
+			return "Video Conferencing";
+			/* 17 is reserved */
+		case 18:
+			return "Gaming/Toy";
+		}
+		break;
+	case 5:	/* peripheral */ {
+		static char cls_str[48];
+
+		cls_str[0] = '\0';
+
+		switch (minor & 48) {
+		case 16:
+			strncpy(cls_str, "Keyboard", sizeof(cls_str));
+			break;
+		case 32:
+			strncpy(cls_str, "Pointing device", sizeof(cls_str));
+			break;
+		case 48:
+			strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+			break;
+		}
+		if ((minor & 15) && (strlen(cls_str) > 0))
+			strcat(cls_str, "/");
+
+		switch (minor & 15) {
+		case 0:
+			break;
+		case 1:
+			strncat(cls_str, "Joystick",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 2:
+			strncat(cls_str, "Gamepad",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 3:
+			strncat(cls_str, "Remote control",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 4:
+			strncat(cls_str, "Sensing device",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 5:
+			strncat(cls_str, "Digitizer tablet",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 6:
+			strncat(cls_str, "Card reader",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		default:
+			strncat(cls_str, "(reserved)",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		}
+		if (strlen(cls_str) > 0)
+			return cls_str;
+		break;
+	}
+	case 6:	/* imaging */
+		if (minor & 4)
+			return "Display";
+		if (minor & 8)
+			return "Camera";
+		if (minor & 16)
+			return "Scanner";
+		if (minor & 32)
+			return "Printer";
+		break;
+	case 7: /* wearable */
+		switch (minor) {
+		case 1:
+			return "Wrist Watch";
+		case 2:
+			return "Pager";
+		case 3:
+			return "Jacket";
+		case 4:
+			return "Helmet";
+		case 5:
+			return "Glasses";
+		}
+		break;
+	case 8: /* toy */
+		switch (minor) {
+		case 1:
+			return "Robot";
+		case 2:
+			return "Vehicle";
+		case 3:
+			return "Doll / Action Figure";
+		case 4:
+			return "Controller";
+		case 5:
+			return "Game";
+		}
+		break;
+	case 63:	/* uncategorised */
+		return "";
+	}
+	return "Unknown (reserved) minor device class";
+}
+
+static void cmd_class(int ctl, int hdev, char *opt)
+{
+	static const char *services[] = { "Positioning",
+					"Networking",
+					"Rendering",
+					"Capturing",
+					"Object Transfer",
+					"Audio",
+					"Telephony",
+					"Information" };
+	static const char *major_devices[] = { "Miscellaneous",
+					"Computer",
+					"Phone",
+					"LAN Access",
+					"Audio/Video",
+					"Peripheral",
+					"Imaging",
+					"Uncategorized" };
+	int s = hci_open_dev(hdev);
+
+	if (s < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+	if (opt) {
+		uint32_t cod = strtoul(opt, NULL, 16);
+		if (hci_write_class_of_dev(s, cod, 2000) < 0) {
+			fprintf(stderr, "Can't write local class of device on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t cls[3];
+		if (hci_read_class_of_dev(s, cls, 1000) < 0) {
+			fprintf(stderr, "Can't read class of device on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+		print_dev_hdr(&di);
+		printf("\tClass: 0x%02x%02x%02x\n", cls[2], cls[1], cls[0]);
+		printf("\tService Classes: ");
+		if (cls[2]) {
+			unsigned int i;
+			int first = 1;
+			for (i = 0; i < (sizeof(services) / sizeof(*services)); i++)
+				if (cls[2] & (1 << i)) {
+					if (!first)
+						printf(", ");
+					printf("%s", services[i]);
+					first = 0;
+				}
+		} else
+			printf("Unspecified");
+		printf("\n\tDevice Class: ");
+		if ((cls[1] & 0x1f) >= sizeof(major_devices) / sizeof(*major_devices))
+			printf("Invalid Device Class!\n");
+		else
+			printf("%s, %s\n", major_devices[cls[1] & 0x1f],
+				get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
+	}
+
+	hci_close_dev(s);
+}
+
+static void cmd_voice(int ctl, int hdev, char *opt)
+{
+	static char *icf[] = {	"Linear",
+				"u-Law",
+				"A-Law",
+				"Reserved" };
+
+	static char *idf[] = {	"1's complement",
+				"2's complement",
+				"Sign-Magnitude",
+				"Reserved" };
+
+	static char *iss[] = {	"8 bit",
+				"16 bit" };
+
+	static char *acf[] = {	"CVSD",
+				"u-Law",
+				"A-Law",
+				"Reserved" };
+
+	int s = hci_open_dev(hdev);
+
+	if (s < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+	if (opt) {
+		uint16_t vs = htobs(strtoul(opt, NULL, 16));
+		if (hci_write_voice_setting(s, vs, 2000) < 0) {
+			fprintf(stderr, "Can't write voice setting on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint16_t vs;
+		uint8_t ic;
+		if (hci_read_voice_setting(s, &vs, 1000) < 0) {
+			fprintf(stderr, "Can't read voice setting on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+		vs = htobs(vs);
+		ic = (vs & 0x0300) >> 8;
+		print_dev_hdr(&di);
+		printf("\tVoice setting: 0x%04x%s\n", vs,
+			((vs & 0x03fc) == 0x0060) ? " (Default Condition)" : "");
+		printf("\tInput Coding: %s\n", icf[ic]);
+		printf("\tInput Data Format: %s\n", idf[(vs & 0xc0) >> 6]);
+
+		if (!ic) {
+			printf("\tInput Sample Size: %s\n",
+				iss[(vs & 0x20) >> 5]);
+			printf("\t# of bits padding at MSB: %d\n",
+				(vs & 0x1c) >> 2);
+		}
+		printf("\tAir Coding Format: %s\n", acf[vs & 0x03]);
+	}
+
+	hci_close_dev(s);
+}
+
+static void cmd_delkey(int ctl, int hdev, char *opt)
+{
+	bdaddr_t bdaddr;
+	uint8_t all;
+	int dd;
+
+	if (!opt)
+		return;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (!strcasecmp(opt, "all")) {
+		bacpy(&bdaddr, BDADDR_ANY);
+		all = 1;
+	} else {
+		str2ba(opt, &bdaddr);
+		all = 0;
+	}
+
+	if (hci_delete_stored_link_key(dd, &bdaddr, all, 1000) < 0) {
+		fprintf(stderr, "Can't delete stored link key on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_oob_data(int ctl, int hdev, char *opt)
+{
+	uint8_t hash[16], randomizer[16];
+	int i, dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (hci_read_local_oob_data(dd, hash, randomizer, 1000) < 0) {
+		fprintf(stderr, "Can't read local OOB data on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	print_dev_hdr(&di);
+	printf("\tOOB Hash:  ");
+	for (i = 0; i < 16; i++)
+		printf(" %02x", hash[i]);
+	printf("\n\tRandomizer:");
+	for (i = 0; i < 16; i++)
+		printf(" %02x", randomizer[i]);
+	printf("\n");
+
+	hci_close_dev(dd);
+}
+
+static void cmd_commands(int ctl, int hdev, char *opt)
+{
+	uint8_t cmds[64];
+	char *str;
+	int i, n, dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (hci_read_local_commands(dd, cmds, 1000) < 0) {
+		fprintf(stderr, "Can't read support commands on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	print_dev_hdr(&di);
+	for (i = 0; i < 64; i++) {
+		if (!cmds[i])
+			continue;
+
+		printf("%s Octet %-2d = 0x%02x (Bit",
+			i ? "\t\t ": "\tCommands:", i, cmds[i]);
+		for (n = 0; n < 8; n++)
+			if (cmds[i] & (1 << n))
+				printf(" %d", n);
+		printf(")\n");
+	}
+
+	str = hci_commandstostr(cmds, "\t", 71);
+	printf("%s\n", str);
+	bt_free(str);
+
+	hci_close_dev(dd);
+}
+
+static void cmd_version(int ctl, int hdev, char *opt)
+{
+	struct hci_version ver;
+	char *hciver, *lmpver;
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (hci_read_local_version(dd, &ver, 1000) < 0) {
+		fprintf(stderr, "Can't read version info hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	hciver = hci_vertostr(ver.hci_ver);
+	if (((di.type & 0x30) >> 4) == HCI_BREDR)
+		lmpver = lmp_vertostr(ver.lmp_ver);
+	else
+		lmpver = pal_vertostr(ver.lmp_ver);
+
+	print_dev_hdr(&di);
+	printf("\tHCI Version: %s (0x%x)  Revision: 0x%x\n"
+		"\t%s Version: %s (0x%x)  Subversion: 0x%x\n"
+		"\tManufacturer: %s (%d)\n",
+		hciver ? hciver : "n/a", ver.hci_ver, ver.hci_rev,
+		(((di.type & 0x30) >> 4) == HCI_BREDR) ? "LMP" : "PAL",
+		lmpver ? lmpver : "n/a", ver.lmp_ver, ver.lmp_subver,
+		bt_compidtostr(ver.manufacturer), ver.manufacturer);
+
+	if (hciver)
+		bt_free(hciver);
+	if (lmpver)
+		bt_free(lmpver);
+
+	hci_close_dev(dd);
+}
+
+static void cmd_inq_tpl(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		int8_t level = atoi(opt);
+
+		if (hci_write_inquiry_transmit_power_level(dd, level, 2000) < 0) {
+			fprintf(stderr, "Can't set inquiry transmit power level on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		int8_t level;
+
+		if (hci_read_inq_response_tx_power_level(dd, &level, 1000) < 0) {
+			fprintf(stderr, "Can't read inquiry transmit power level on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tInquiry transmit power level: %d\n", level);
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_inq_mode(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		uint8_t mode = atoi(opt);
+
+		if (hci_write_inquiry_mode(dd, mode, 2000) < 0) {
+			fprintf(stderr, "Can't set inquiry mode on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t mode;
+
+		if (hci_read_inquiry_mode(dd, &mode, 1000) < 0) {
+			fprintf(stderr, "Can't read inquiry mode on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tInquiry mode: ");
+		switch (mode) {
+		case 0:
+			printf("Standard Inquiry\n");
+			break;
+		case 1:
+			printf("Inquiry with RSSI\n");
+			break;
+		case 2:
+			printf("Inquiry with RSSI or Extended Inquiry\n");
+			break;
+		default:
+			printf("Unknown (0x%02x)\n", mode);
+			break;
+		}
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_inq_data(int ctl, int hdev, char *opt)
+{
+	int i, dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		uint8_t fec = 0, data[HCI_MAX_EIR_LENGTH];
+		char tmp[3];
+		int i, size;
+
+		memset(data, 0, sizeof(data));
+
+		memset(tmp, 0, sizeof(tmp));
+		size = (strlen(opt) + 1) / 2;
+		if (size > HCI_MAX_EIR_LENGTH)
+			size = HCI_MAX_EIR_LENGTH;
+
+		for (i = 0; i < size; i++) {
+			memcpy(tmp, opt + (i * 2), 2);
+			data[i] = strtol(tmp, NULL, 16);
+		}
+
+		if (hci_write_ext_inquiry_response(dd, fec, data, 2000) < 0) {
+			fprintf(stderr, "Can't set extended inquiry response on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t fec, data[HCI_MAX_EIR_LENGTH], len, type, *ptr;
+		char *str;
+
+		if (hci_read_ext_inquiry_response(dd, &fec, data, 1000) < 0) {
+			fprintf(stderr, "Can't read extended inquiry response on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tFEC %s\n\t\t", fec ? "enabled" : "disabled");
+		for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
+			printf("%02x%s%s", data[i], (i + 1) % 8 ? "" : " ",
+				(i + 1) % 16 ? " " : (i < 239 ? "\n\t\t" : "\n"));
+
+		ptr = data;
+		while (*ptr) {
+			len = *ptr++;
+			type = *ptr++;
+			switch (type) {
+			case 0x01:
+				printf("\tFlags:");
+				for (i = 0; i < len - 1; i++)
+					printf(" 0x%2.2x", *((uint8_t *) (ptr + i)));
+				printf("\n");
+				break;
+			case 0x02:
+			case 0x03:
+				printf("\t%s service classes:",
+					type == 0x02 ? "Shortened" : "Complete");
+				for (i = 0; i < (len - 1) / 2; i++) {
+					uint16_t val = get_le16((ptr + (i * 2)));
+					printf(" 0x%4.4x", val);
+				}
+				printf("\n");
+				break;
+			case 0x08:
+			case 0x09:
+				str = malloc(len);
+				if (str) {
+					snprintf(str, len, "%s", ptr);
+					for (i = 0; i < len - 1; i++) {
+						if ((unsigned char) str[i] < 32 || str[i] == 127)
+							str[i] = '.';
+					}
+					printf("\t%s local name: \'%s\'\n",
+						type == 0x08 ? "Shortened" : "Complete", str);
+					free(str);
+				}
+				break;
+			case 0x0a:
+				printf("\tTX power level: %d\n", *((int8_t *) ptr));
+				break;
+			case 0x10:
+				printf("\tDevice ID with %d bytes data\n",
+								len - 1);
+				break;
+			default:
+				printf("\tUnknown type 0x%02x with %d bytes data\n",
+								type, len - 1);
+				break;
+			}
+
+			ptr += (len - 1);
+		}
+
+		printf("\n");
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_inq_type(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		uint8_t type = atoi(opt);
+
+		if (hci_write_inquiry_scan_type(dd, type, 2000) < 0) {
+			fprintf(stderr, "Can't set inquiry scan type on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t type;
+
+		if (hci_read_inquiry_scan_type(dd, &type, 1000) < 0) {
+			fprintf(stderr, "Can't read inquiry scan type on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tInquiry scan type: %s\n",
+			type == 1 ? "Interlaced Inquiry Scan" : "Standard Inquiry Scan");
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_inq_parms(int ctl, int hdev, char *opt)
+{
+	struct hci_request rq;
+	int s;
+
+	if ((s = hci_open_dev(hdev)) < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	memset(&rq, 0, sizeof(rq));
+
+	if (opt) {
+		unsigned int window, interval;
+		write_inq_activity_cp cp;
+
+		if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
+			printf("Invalid argument format\n");
+			exit(1);
+		}
+
+		rq.ogf = OGF_HOST_CTL;
+		rq.ocf = OCF_WRITE_INQ_ACTIVITY;
+		rq.cparam = &cp;
+		rq.clen = WRITE_INQ_ACTIVITY_CP_SIZE;
+
+		cp.window = htobs((uint16_t) window);
+		cp.interval = htobs((uint16_t) interval);
+
+		if (window < 0x12 || window > 0x1000)
+			printf("Warning: inquiry window out of range!\n");
+
+		if (interval < 0x12 || interval > 0x1000)
+			printf("Warning: inquiry interval out of range!\n");
+
+		if (hci_send_req(s, &rq, 2000) < 0) {
+			fprintf(stderr, "Can't set inquiry parameters name on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint16_t window, interval;
+		read_inq_activity_rp rp;
+
+		rq.ogf = OGF_HOST_CTL;
+		rq.ocf = OCF_READ_INQ_ACTIVITY;
+		rq.rparam = &rp;
+		rq.rlen = READ_INQ_ACTIVITY_RP_SIZE;
+
+		if (hci_send_req(s, &rq, 1000) < 0) {
+			fprintf(stderr, "Can't read inquiry parameters on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+		if (rp.status) {
+			printf("Read inquiry parameters on hci%d returned status %d\n",
+							hdev, rp.status);
+			exit(1);
+		}
+		print_dev_hdr(&di);
+
+		window   = btohs(rp.window);
+		interval = btohs(rp.interval);
+		printf("\tInquiry interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
+				interval, (float)interval * 0.625, window, (float)window * 0.625);
+	}
+
+	hci_close_dev(s);
+}
+
+static void cmd_page_parms(int ctl, int hdev, char *opt)
+{
+	struct hci_request rq;
+	int s;
+
+	if ((s = hci_open_dev(hdev)) < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	memset(&rq, 0, sizeof(rq));
+
+	if (opt) {
+		unsigned int window, interval;
+		write_page_activity_cp cp;
+
+		if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
+			printf("Invalid argument format\n");
+			exit(1);
+		}
+
+		rq.ogf = OGF_HOST_CTL;
+		rq.ocf = OCF_WRITE_PAGE_ACTIVITY;
+		rq.cparam = &cp;
+		rq.clen = WRITE_PAGE_ACTIVITY_CP_SIZE;
+
+		cp.window = htobs((uint16_t) window);
+		cp.interval = htobs((uint16_t) interval);
+
+		if (window < 0x12 || window > 0x1000)
+			printf("Warning: page window out of range!\n");
+
+		if (interval < 0x12 || interval > 0x1000)
+			printf("Warning: page interval out of range!\n");
+
+		if (hci_send_req(s, &rq, 2000) < 0) {
+			fprintf(stderr, "Can't set page parameters name on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint16_t window, interval;
+		read_page_activity_rp rp;
+
+		rq.ogf = OGF_HOST_CTL;
+		rq.ocf = OCF_READ_PAGE_ACTIVITY;
+		rq.rparam = &rp;
+		rq.rlen = READ_PAGE_ACTIVITY_RP_SIZE;
+
+		if (hci_send_req(s, &rq, 1000) < 0) {
+			fprintf(stderr, "Can't read page parameters on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+		if (rp.status) {
+			printf("Read page parameters on hci%d returned status %d\n",
+							hdev, rp.status);
+			exit(1);
+		}
+		print_dev_hdr(&di);
+
+		window   = btohs(rp.window);
+		interval = btohs(rp.interval);
+		printf("\tPage interval: %u slots (%.2f ms), "
+			"window: %u slots (%.2f ms)\n",
+			interval, (float)interval * 0.625,
+			window, (float)window * 0.625);
+	}
+
+	hci_close_dev(s);
+}
+
+static void cmd_page_to(int ctl, int hdev, char *opt)
+{
+	struct hci_request rq;
+	int s;
+
+	if ((s = hci_open_dev(hdev)) < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	memset(&rq, 0, sizeof(rq));
+
+	if (opt) {
+		unsigned int timeout;
+		write_page_timeout_cp cp;
+
+		if (sscanf(opt,"%5u", &timeout) != 1) {
+			printf("Invalid argument format\n");
+			exit(1);
+		}
+
+		rq.ogf = OGF_HOST_CTL;
+		rq.ocf = OCF_WRITE_PAGE_TIMEOUT;
+		rq.cparam = &cp;
+		rq.clen = WRITE_PAGE_TIMEOUT_CP_SIZE;
+
+		cp.timeout = htobs((uint16_t) timeout);
+
+		if (timeout < 0x01 || timeout > 0xFFFF)
+			printf("Warning: page timeout out of range!\n");
+
+		if (hci_send_req(s, &rq, 2000) < 0) {
+			fprintf(stderr, "Can't set page timeout on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint16_t timeout;
+		read_page_timeout_rp rp;
+
+		rq.ogf = OGF_HOST_CTL;
+		rq.ocf = OCF_READ_PAGE_TIMEOUT;
+		rq.rparam = &rp;
+		rq.rlen = READ_PAGE_TIMEOUT_RP_SIZE;
+
+		if (hci_send_req(s, &rq, 1000) < 0) {
+			fprintf(stderr, "Can't read page timeout on hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+			exit(1);
+		}
+		if (rp.status) {
+			printf("Read page timeout on hci%d returned status %d\n",
+							hdev, rp.status);
+			exit(1);
+		}
+		print_dev_hdr(&di);
+
+		timeout = btohs(rp.timeout);
+		printf("\tPage timeout: %u slots (%.2f ms)\n",
+				timeout, (float)timeout * 0.625);
+	}
+
+	hci_close_dev(s);
+}
+
+static void cmd_afh_mode(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		uint8_t mode = atoi(opt);
+
+		if (hci_write_afh_mode(dd, mode, 2000) < 0) {
+			fprintf(stderr, "Can't set AFH mode on hci%d: %s (%d)\n",
+					hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t mode;
+
+		if (hci_read_afh_mode(dd, &mode, 1000) < 0) {
+			fprintf(stderr, "Can't read AFH mode on hci%d: %s (%d)\n",
+					hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tAFH mode: %s\n", mode == 1 ? "Enabled" : "Disabled");
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_ssp_mode(int ctl, int hdev, char *opt)
+{
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (opt) {
+		uint8_t mode = atoi(opt);
+
+		if (hci_write_simple_pairing_mode(dd, mode, 2000) < 0) {
+			fprintf(stderr, "Can't set Simple Pairing mode on hci%d: %s (%d)\n",
+					hdev, strerror(errno), errno);
+			exit(1);
+		}
+	} else {
+		uint8_t mode;
+
+		if (hci_read_simple_pairing_mode(dd, &mode, 1000) < 0) {
+			fprintf(stderr, "Can't read Simple Pairing mode on hci%d: %s (%d)\n",
+					hdev, strerror(errno), errno);
+			exit(1);
+		}
+
+		print_dev_hdr(&di);
+		printf("\tSimple Pairing mode: %s\n",
+			mode == 1 ? "Enabled" : "Disabled");
+	}
+
+	hci_close_dev(dd);
+}
+
+static void print_rev_ericsson(int dd)
+{
+	struct hci_request rq;
+	unsigned char buf[102];
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x000f;
+	rq.cparam = NULL;
+	rq.clen   = 0;
+	rq.rparam = &buf;
+	rq.rlen   = sizeof(buf);
+
+	if (hci_send_req(dd, &rq, 1000) < 0) {
+		printf("\nCan't read revision info: %s (%d)\n",
+			strerror(errno), errno);
+		return;
+	}
+
+	printf("\t%s\n", buf + 1);
+}
+
+static void print_rev_csr(int dd, uint16_t rev)
+{
+	uint16_t buildid, chipver, chiprev, maxkeylen, mapsco;
+
+	if (csr_read_varid_uint16(dd, 0, CSR_VARID_BUILDID, &buildid) < 0) {
+		printf("\t%s\n", csr_buildidtostr(rev));
+		return;
+	}
+
+	printf("\t%s\n", csr_buildidtostr(buildid));
+
+	if (!csr_read_varid_uint16(dd, 1, CSR_VARID_CHIPVER, &chipver)) {
+		if (csr_read_varid_uint16(dd, 2, CSR_VARID_CHIPREV, &chiprev) < 0)
+			chiprev = 0;
+		printf("\tChip version: %s\n", csr_chipvertostr(chipver, chiprev));
+	}
+
+	if (!csr_read_varid_uint16(dd, 3, CSR_VARID_MAX_CRYPT_KEY_LENGTH, &maxkeylen))
+		printf("\tMax key size: %d bit\n", maxkeylen * 8);
+
+	if (!csr_read_pskey_uint16(dd, 4, CSR_PSKEY_HOSTIO_MAP_SCO_PCM, 0x0000, &mapsco))
+		printf("\tSCO mapping:  %s\n", mapsco ? "PCM" : "HCI");
+}
+
+static void print_rev_digianswer(int dd)
+{
+	struct hci_request rq;
+	unsigned char req[] = { 0x07 };
+	unsigned char buf[102];
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_VENDOR_CMD;
+	rq.ocf    = 0x000e;
+	rq.cparam = req;
+	rq.clen   = sizeof(req);
+	rq.rparam = &buf;
+	rq.rlen   = sizeof(buf);
+
+	if (hci_send_req(dd, &rq, 1000) < 0) {
+		printf("\nCan't read revision info: %s (%d)\n",
+			strerror(errno), errno);
+		return;
+	}
+
+	printf("\t%s\n", buf + 1);
+}
+
+static void print_rev_broadcom(uint16_t hci_rev, uint16_t lmp_subver)
+{
+	printf("\tFirmware %d.%d / %d\n",
+		hci_rev & 0xff, lmp_subver >> 8, lmp_subver & 0xff);
+}
+
+static void print_rev_avm(uint16_t hci_rev, uint16_t lmp_subver)
+{
+	if (lmp_subver == 0x01)
+		printf("\tFirmware 03.%d.%d\n", hci_rev >> 8, hci_rev & 0xff);
+	else
+		printf("\tUnknown type\n");
+}
+
+static void cmd_revision(int ctl, int hdev, char *opt)
+{
+	struct hci_version ver;
+	int dd;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		return;
+	}
+
+	if (hci_read_local_version(dd, &ver, 1000) < 0) {
+		fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		return;
+	}
+
+	print_dev_hdr(&di);
+	switch (ver.manufacturer) {
+	case 0:
+	case 37:
+	case 48:
+		print_rev_ericsson(dd);
+		break;
+	case 10:
+		print_rev_csr(dd, ver.hci_rev);
+		break;
+	case 12:
+		print_rev_digianswer(dd);
+		break;
+	case 15:
+		print_rev_broadcom(ver.hci_rev, ver.lmp_subver);
+		break;
+	case 31:
+		print_rev_avm(ver.hci_rev, ver.lmp_subver);
+		break;
+	default:
+		printf("\tUnsupported manufacturer\n");
+		break;
+	}
+
+	hci_close_dev(dd);
+
+	return;
+}
+
+static void cmd_block(int ctl, int hdev, char *opt)
+{
+	bdaddr_t bdaddr;
+	int dd;
+
+	if (!opt)
+		return;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	str2ba(opt, &bdaddr);
+
+	if (ioctl(dd, HCIBLOCKADDR, &bdaddr) < 0) {
+		perror("ioctl(HCIBLOCKADDR)");
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+static void cmd_unblock(int ctl, int hdev, char *opt)
+{
+	bdaddr_t bdaddr;
+	int dd;
+
+	if (!opt)
+		return;
+
+	dd = hci_open_dev(hdev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						hdev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (!strcasecmp(opt, "all"))
+		bacpy(&bdaddr, BDADDR_ANY);
+	else
+		str2ba(opt, &bdaddr);
+
+	if (ioctl(dd, HCIUNBLOCKADDR, &bdaddr) < 0) {
+		perror("ioctl(HCIUNBLOCKADDR)");
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+static void print_dev_hdr(struct hci_dev_info *di)
+{
+	static int hdr = -1;
+	char addr[18];
+
+	if (hdr == di->dev_id)
+		return;
+	hdr = di->dev_id;
+
+	ba2str(&di->bdaddr, addr);
+
+	printf("%s:\tType: %s  Bus: %s\n", di->name,
+					hci_typetostr((di->type & 0x30) >> 4),
+					hci_bustostr(di->type & 0x0f));
+	printf("\tBD Address: %s  ACL MTU: %d:%d  SCO MTU: %d:%d\n",
+					addr, di->acl_mtu, di->acl_pkts,
+						di->sco_mtu, di->sco_pkts);
+}
+
+static void print_dev_info(int ctl, struct hci_dev_info *di)
+{
+	struct hci_dev_stats *st = &di->stat;
+	char *str;
+
+	print_dev_hdr(di);
+
+	str = hci_dflagstostr(di->flags);
+	printf("\t%s\n", str);
+	bt_free(str);
+
+	printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
+		st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
+
+	printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
+		st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
+
+	if (all && !hci_test_bit(HCI_RAW, &di->flags)) {
+		print_dev_features(di, 0);
+
+		if (((di->type & 0x30) >> 4) == HCI_BREDR) {
+			print_pkt_type(di);
+			print_link_policy(di);
+			print_link_mode(di);
+
+			if (hci_test_bit(HCI_UP, &di->flags)) {
+				cmd_name(ctl, di->dev_id, NULL);
+				cmd_class(ctl, di->dev_id, NULL);
+			}
+		}
+
+		if (hci_test_bit(HCI_UP, &di->flags))
+			cmd_version(ctl, di->dev_id, NULL);
+	}
+
+	printf("\n");
+}
+
+static struct {
+	char *cmd;
+	void (*func)(int ctl, int hdev, char *opt);
+	char *opt;
+	char *doc;
+} command[] = {
+	{ "up",		cmd_up,		0,		"Open and initialize HCI device" },
+	{ "down",	cmd_down,	0,		"Close HCI device" },
+	{ "reset",	cmd_reset,	0,		"Reset HCI device" },
+	{ "rstat",	cmd_rstat,	0,		"Reset statistic counters" },
+	{ "auth",	cmd_auth,	0,		"Enable Authentication" },
+	{ "noauth",	cmd_auth,	0,		"Disable Authentication" },
+	{ "encrypt",	cmd_encrypt,	0,		"Enable Encryption" },
+	{ "noencrypt",	cmd_encrypt,	0,		"Disable Encryption" },
+	{ "piscan",	cmd_scan,	0,		"Enable Page and Inquiry scan" },
+	{ "noscan",	cmd_scan,	0,		"Disable scan" },
+	{ "iscan",	cmd_scan,	0,		"Enable Inquiry scan" },
+	{ "pscan",	cmd_scan,	0,		"Enable Page scan" },
+	{ "ptype",	cmd_ptype,	"[type]",	"Get/Set default packet type" },
+	{ "lm",		cmd_lm,		"[mode]",	"Get/Set default link mode"   },
+	{ "lp",		cmd_lp,		"[policy]",	"Get/Set default link policy" },
+	{ "name",	cmd_name,	"[name]",	"Get/Set local name" },
+	{ "class",	cmd_class,	"[class]",	"Get/Set class of device" },
+	{ "voice",	cmd_voice,	"[voice]",	"Get/Set voice setting" },
+	{ "iac",	cmd_iac,	"[iac]",	"Get/Set inquiry access code" },
+	{ "inqtpl",	cmd_inq_tpl,	"[level]",	"Get/Set inquiry transmit power level" },
+	{ "inqmode",	cmd_inq_mode,	"[mode]",	"Get/Set inquiry mode" },
+	{ "inqdata",	cmd_inq_data,	"[data]",	"Get/Set inquiry data" },
+	{ "inqtype",	cmd_inq_type,	"[type]",	"Get/Set inquiry scan type" },
+	{ "inqparms",	cmd_inq_parms,	"[win:int]",	"Get/Set inquiry scan window and interval" },
+	{ "pageparms",	cmd_page_parms,	"[win:int]",	"Get/Set page scan window and interval" },
+	{ "pageto",	cmd_page_to,	"[to]",		"Get/Set page timeout" },
+	{ "afhmode",	cmd_afh_mode,	"[mode]",	"Get/Set AFH mode" },
+	{ "sspmode",	cmd_ssp_mode,	"[mode]",	"Get/Set Simple Pairing Mode" },
+	{ "aclmtu",	cmd_aclmtu,	"<mtu:pkt>",	"Set ACL MTU and number of packets" },
+	{ "scomtu",	cmd_scomtu,	"<mtu:pkt>",	"Set SCO MTU and number of packets" },
+	{ "delkey",	cmd_delkey,	"<bdaddr>",	"Delete link key from the device" },
+	{ "oobdata",	cmd_oob_data,	0,		"Get local OOB data" },
+	{ "commands",	cmd_commands,	0,		"Display supported commands" },
+	{ "features",	cmd_features,	0,		"Display device features" },
+	{ "version",	cmd_version,	0,		"Display version information" },
+	{ "revision",	cmd_revision,	0,		"Display revision information" },
+	{ "block",	cmd_block,	"<bdaddr>",	"Add a device to the blacklist" },
+	{ "unblock",	cmd_unblock,	"<bdaddr>",	"Remove a device from the blacklist" },
+	{ "lerandaddr", cmd_le_addr,	"<bdaddr>",	"Set LE Random Address" },
+	{ "leadv",	cmd_le_adv,	"[type]",	"Enable LE advertising"
+		"\n\t\t\t0 - Connectable undirected advertising (default)"
+		"\n\t\t\t3 - Non connectable undirected advertising"},
+	{ "noleadv",	cmd_no_le_adv,	0,		"Disable LE advertising" },
+	{ "lestates",	cmd_le_states,	0,		"Display the supported LE states" },
+	{ NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+	int i;
+
+	printf("hciconfig - HCI device configuration utility\n");
+	printf("Usage:\n"
+		"\thciconfig\n"
+		"\thciconfig [-a] hciX [command ...]\n");
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-10s %-8s\t%s\n", command[i].cmd,
+		command[i].opt ? command[i].opt : " ",
+		command[i].doc);
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "all",	0, 0, 'a' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	int opt, ctl, i, cmd = 0;
+
+	while ((opt = getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'a':
+			all = 1;
+			break;
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	/* Open HCI socket  */
+	if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+		perror("Can't open HCI socket.");
+		exit(1);
+	}
+
+	if (argc < 1) {
+		print_dev_list(ctl, 0);
+		exit(0);
+	}
+
+	di.dev_id = atoi(argv[0] + 3);
+	argc--; argv++;
+
+	if (ioctl(ctl, HCIGETDEVINFO, (void *) &di)) {
+		perror("Can't get device info");
+		exit(1);
+	}
+
+	while (argc > 0) {
+		for (i = 0; command[i].cmd; i++) {
+			if (strncmp(command[i].cmd,
+					*argv, strlen(command[i].cmd)))
+				continue;
+
+			if (command[i].opt) {
+				argc--; argv++;
+			}
+
+			command[i].func(ctl, di.dev_id, *argv);
+			cmd = 1;
+			break;
+		}
+
+		if (command[i].cmd == 0)
+			fprintf(stderr, "Warning: unknown command - \"%s\"\n",
+					*argv);
+
+		argc--; argv++;
+	}
+
+	if (!cmd)
+		print_dev_info(ctl, &di);
+
+	close(ctl);
+	return 0;
+}
diff --git a/bluez/repo/tools/hcidump.1 b/bluez/repo/tools/hcidump.1
new file mode 100644
index 0000000..5c1441b
--- /dev/null
+++ b/bluez/repo/tools/hcidump.1
@@ -0,0 +1,118 @@
+.TH HCIDUMP 1 "Nov 12 2002" BlueZ "Linux System Administration"
+.SH NAME
+hcidump \- Parse HCI data
+.SH SYNOPSIS
+.B hcidump [-h]
+.br
+.B hcidump [option [option...]] [filter]
+
+.SH DESCRIPTION
+.LP
+.B
+hcidump
+reads raw HCI data coming from and going to a Bluetooth device (which can be
+specified with the option
+.BR -i ,
+default is the first available one) and prints to screen commands, events and
+data in a human-readable form. Optionally, the dump can be written to a file
+rather than parsed, and the dump file can be parsed in a subsequent moment.
+.SH OPTIONS
+.TP
+.BI -h
+Prints usage info and exits
+.TP
+.BI -i " <hciX>"
+Data is read from
+.IR hciX ,
+which must be the name of an installed Bluetooth device. If not specified,
+and if
+.B
+-r
+option is not set, data is read from the first available Bluetooth device.
+.TP
+.BI -l " <len>" "\fR,\fP \-\^\-snap-len=" "<len>"
+Sets max length of processed packets to
+.IR len .
+.TP
+.BI -p " <psm>" "\fR,\fP \-\^\-psm=" "<psm>"
+Sets default Protocol Service Multiplexer to
+.IR psm .
+.TP
+.BI -m " <compid>" "\fR,\fP \-\^\-manufacturer=" "<compid>"
+Sets default company id for manufacturer to
+.IR compid .
+.TP
+.BI -w " <file>" "\fR,\fP \-\^\-save-dump=" "<file>"
+Parse output is not printed to screen, instead data read from device is saved in file
+.IR file .
+The saved dump file can be subsequently parsed with option
+.BR -r .
+.TP
+.BI -r " <file>" "\fR,\fP \-\^\-read-dump=" "<file>"
+Data is not read from a Bluetooth device, but from file
+.IR file .
+.I
+file
+is created with option
+.BR -t ", " "\-\^\-timestamp"
+Prepend a time stamp to every packet.
+.TP
+.BR -a ", " "\-\^\-ascii"
+For every packet, not only is the packet type displayed, but also all data in ASCII.
+.TP
+.BR -x ", " "\-\^\-hex"
+For every packet, not only is the packet type displayed, but also all data in hex.
+.TP
+.BR -X ", " "\-\^\-ext"
+For every packet, not only is the packet type displayed, but also all data in hex and ASCII.
+.TP
+.BR -R ", " "\-\^\-raw"
+For every packet, only the raw data is displayed.
+.TP
+.BR -C ", " "\-\^\-cmtp=" "<psm>"
+Sets the PSM value for the CAPI Message Transport Protocol.
+.TP
+.BR -H ", " "\-\^\-hcrp=" "<psm>"
+Sets the PSM value for the Hardcopy Control Channel.
+.TP
+.BR -O ", " "\-\^\-obex=" "<channel>"
+Sets the RFCOMM channel value for the Object Exchange Protocol.
+.TP
+.BR -P ", " "\-\^\-ppp=" "<channel>"
+Sets the RFCOMM channel value for the Point-to-Point Protocol.
+.TP
+.BR -D ", " "\-\^\-pppdump=" "<file>"
+Extract PPP traffic with pppdump format.
+.TP
+.BR -A ", " "\-\^\-audio=" "<file>"
+Extract SCO audio data.
+.TP
+.BR -Y ", " "\-\^\-novendor"
+Don't display any vendor commands or events and don't show any pin code or link key in plain text.
+.SH FILTERS
+.B
+filter
+is a space-separated list of packet categories: available categories are
+.IR lmp ,
+.IR hci ,
+.IR sco ,
+.IR l2cap ,
+.IR rfcomm ,
+.IR sdp ,
+.IR bnep ,
+.IR cmtp ,
+.IR hidp ,
+.IR hcrp ,
+.IR avdtp ,
+.IR avctp ,
+.IR obex ,
+.IR capi
+and
+.IR ppp .
+If filters are used, only packets belonging to the specified categories are
+dumped. By default, all packets are dumped.
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com>
+and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/bluez/repo/tools/hcidump.c b/bluez/repo/tools/hcidump.c
new file mode 100644
index 0000000..af8f592
--- /dev/null
+++ b/bluez/repo/tools/hcidump.c
@@ -0,0 +1,821 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "parser/parser.h"
+#include "parser/sdp.h"
+
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#define SNAP_LEN	HCI_MAX_FRAME_SIZE
+
+/* Modes */
+enum {
+	PARSE,
+	READ,
+	WRITE,
+	PPPDUMP,
+	AUDIO
+};
+
+/* Default options */
+static int  snap_len = SNAP_LEN;
+static int  mode = PARSE;
+static char *dump_file = NULL;
+static char *pppdump_file = NULL;
+static char *audio_file = NULL;
+
+struct hcidump_hdr {
+	uint16_t	len;
+	uint8_t		in;
+	uint8_t		pad;
+	uint32_t	ts_sec;
+	uint32_t	ts_usec;
+} __attribute__ ((packed));
+#define HCIDUMP_HDR_SIZE (sizeof(struct hcidump_hdr))
+
+struct btsnoop_hdr {
+	uint8_t		id[8];		/* Identification Pattern */
+	uint32_t	version;	/* Version Number = 1 */
+	uint32_t	type;		/* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+	uint32_t	size;		/* Original Length */
+	uint32_t	len;		/* Included Length */
+	uint32_t	flags;		/* Packet Flags */
+	uint32_t	drops;		/* Cumulative Drops */
+	uint64_t	ts;		/* Timestamp microseconds */
+	uint8_t		data[0];	/* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
+
+static uint32_t btsnoop_version = 0;
+static uint32_t btsnoop_type = 0;
+
+struct pktlog_hdr {
+	uint32_t	len;
+	uint64_t	ts;
+	uint8_t		type;
+} __attribute__ ((packed));
+#define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr))
+
+static inline int read_n(int fd, char *buf, int len)
+{
+	int t = 0, w;
+
+	while (len > 0) {
+		if ((w = read(fd, buf, len)) < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+		if (!w)
+			return 0;
+		len -= w; buf += w; t += w;
+	}
+	return t;
+}
+
+static inline int write_n(int fd, char *buf, int len)
+{
+	int t = 0, w;
+
+	while (len > 0) {
+		if ((w = write(fd, buf, len)) < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+		if (!w)
+			return 0;
+		len -= w; buf += w; t += w;
+	}
+	return t;
+}
+
+static int process_frames(int dev, int sock, int fd, unsigned long flags)
+{
+	struct cmsghdr *cmsg;
+	struct msghdr msg;
+	struct iovec  iv;
+	struct hcidump_hdr *dh;
+	struct btsnoop_pkt *dp;
+	struct frame frm;
+	struct pollfd fds[2];
+	int nfds = 0;
+	char *buf;
+	char ctrl[100];
+	int len, hdr_size = HCIDUMP_HDR_SIZE;
+
+	if (sock < 0)
+		return -1;
+
+	if (snap_len < SNAP_LEN)
+		snap_len = SNAP_LEN;
+
+	if (flags & DUMP_BTSNOOP)
+		hdr_size = BTSNOOP_PKT_SIZE;
+
+	buf = malloc(snap_len + hdr_size);
+	if (!buf) {
+		perror("Can't allocate data buffer");
+		return -1;
+	}
+
+	dh = (void *) buf;
+	dp = (void *) buf;
+	frm.data = buf + hdr_size;
+
+	if (dev == HCI_DEV_NONE)
+		printf("system: ");
+	else
+		printf("device: hci%d ", dev);
+
+	printf("snap_len: %d filter: 0x%lx\n", snap_len, parser.filter);
+
+	memset(&msg, 0, sizeof(msg));
+
+	fds[nfds].fd = sock;
+	fds[nfds].events = POLLIN;
+	fds[nfds].revents = 0;
+	nfds++;
+
+	while (1) {
+		int i, n = poll(fds, nfds, -1);
+		if (n <= 0)
+			continue;
+
+		for (i = 0; i < nfds; i++) {
+			if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) {
+				if (fds[i].fd == sock)
+					printf("device: disconnected\n");
+				else
+					printf("client: disconnect\n");
+				return 0;
+			}
+		}
+
+		iv.iov_base = frm.data;
+		iv.iov_len  = snap_len;
+
+		msg.msg_iov = &iv;
+		msg.msg_iovlen = 1;
+		msg.msg_control = ctrl;
+		msg.msg_controllen = 100;
+
+		len = recvmsg(sock, &msg, MSG_DONTWAIT);
+		if (len < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+			perror("Receive failed");
+			return -1;
+		}
+
+		/* Process control message */
+		frm.data_len = len;
+		frm.dev_id = dev;
+		frm.in = 0;
+		frm.pppdump_fd = parser.pppdump_fd;
+		frm.audio_fd   = parser.audio_fd;
+
+		cmsg = CMSG_FIRSTHDR(&msg);
+		while (cmsg) {
+			int dir;
+			switch (cmsg->cmsg_type) {
+			case HCI_CMSG_DIR:
+				memcpy(&dir, CMSG_DATA(cmsg), sizeof(int));
+				frm.in = (uint8_t) dir;
+				break;
+			case HCI_CMSG_TSTAMP:
+				memcpy(&frm.ts, CMSG_DATA(cmsg),
+						sizeof(struct timeval));
+				break;
+			}
+			cmsg = CMSG_NXTHDR(&msg, cmsg);
+		}
+
+		frm.ptr = frm.data;
+		frm.len = frm.data_len;
+
+		switch (mode) {
+		case WRITE:
+			/* Save or send dump */
+			if (flags & DUMP_BTSNOOP) {
+				uint64_t ts;
+				uint8_t pkt_type = ((uint8_t *) frm.data)[0];
+				dp->size = htobe32(frm.data_len);
+				dp->len  = dp->size;
+				dp->flags = be32toh(frm.in & 0x01);
+				dp->drops = 0;
+				ts = (frm.ts.tv_sec - 946684800ll) * 1000000ll + frm.ts.tv_usec;
+				dp->ts = htobe64(ts + 0x00E03AB44A676000ll);
+				if (pkt_type == HCI_COMMAND_PKT ||
+						pkt_type == HCI_EVENT_PKT)
+					dp->flags |= be32toh(0x02);
+			} else {
+				dh->len = htobs(frm.data_len);
+				dh->in  = frm.in;
+				dh->ts_sec  = htobl(frm.ts.tv_sec);
+				dh->ts_usec = htobl(frm.ts.tv_usec);
+			}
+
+			if (write_n(fd, buf, frm.data_len + hdr_size) < 0) {
+				perror("Write error");
+				return -1;
+			}
+			break;
+
+		default:
+			/* Parse and print */
+			parse(&frm);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void read_dump(int fd)
+{
+	struct hcidump_hdr dh;
+	struct btsnoop_pkt dp;
+	struct pktlog_hdr ph;
+	struct frame frm;
+	int err;
+
+	frm.data = malloc(HCI_MAX_FRAME_SIZE);
+	if (!frm.data) {
+		perror("Can't allocate data buffer");
+		exit(1);
+	}
+
+	while (1) {
+		if (parser.flags & DUMP_PKTLOG)
+			err = read_n(fd, (void *) &ph, PKTLOG_HDR_SIZE);
+		else if (parser.flags & DUMP_BTSNOOP)
+			err = read_n(fd, (void *) &dp, BTSNOOP_PKT_SIZE);
+		else
+			err = read_n(fd, (void *) &dh, HCIDUMP_HDR_SIZE);
+
+		if (err < 0)
+			goto failed;
+		if (!err)
+			goto done;
+
+		if (parser.flags & DUMP_PKTLOG) {
+			switch (ph.type) {
+			case 0x00:
+				((uint8_t *) frm.data)[0] = HCI_COMMAND_PKT;
+				frm.in = 0;
+				break;
+			case 0x01:
+				((uint8_t *) frm.data)[0] = HCI_EVENT_PKT;
+				frm.in = 1;
+				break;
+			case 0x02:
+				((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT;
+				frm.in = 0;
+				break;
+			case 0x03:
+				((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT;
+				frm.in = 1;
+				break;
+			default:
+				lseek(fd, be32toh(ph.len) - 9, SEEK_CUR);
+				continue;
+			}
+
+			frm.data_len = be32toh(ph.len) - 8;
+			err = read_n(fd, frm.data + 1, frm.data_len - 1);
+		} else if (parser.flags & DUMP_BTSNOOP) {
+			uint32_t opcode;
+			uint8_t pkt_type;
+
+			switch (btsnoop_type) {
+			case 1001:
+				if (be32toh(dp.flags) & 0x02) {
+					if (be32toh(dp.flags) & 0x01)
+						pkt_type = HCI_EVENT_PKT;
+					else
+						pkt_type = HCI_COMMAND_PKT;
+				} else
+					pkt_type = HCI_ACLDATA_PKT;
+
+				((uint8_t *) frm.data)[0] = pkt_type;
+
+				frm.data_len = be32toh(dp.len) + 1;
+				err = read_n(fd, frm.data + 1, frm.data_len - 1);
+				break;
+
+			case 1002:
+				frm.data_len = be32toh(dp.len);
+				err = read_n(fd, frm.data, frm.data_len);
+				break;
+
+			case 2001:
+				opcode = be32toh(dp.flags) & 0xffff;
+
+				switch (opcode) {
+				case 2:
+					pkt_type = HCI_COMMAND_PKT;
+					frm.in = 0;
+					break;
+				case 3:
+					pkt_type = HCI_EVENT_PKT;
+					frm.in = 1;
+					break;
+				case 4:
+					pkt_type = HCI_ACLDATA_PKT;
+					frm.in = 0;
+					break;
+				case 5:
+					pkt_type = HCI_ACLDATA_PKT;
+					frm.in = 1;
+					break;
+				case 6:
+					pkt_type = HCI_SCODATA_PKT;
+					frm.in = 0;
+					break;
+				case 7:
+					pkt_type = HCI_SCODATA_PKT;
+					frm.in = 1;
+					break;
+				default:
+					pkt_type = 0xff;
+					break;
+				}
+
+				((uint8_t *) frm.data)[0] = pkt_type;
+
+				frm.data_len = be32toh(dp.len) + 1;
+				err = read_n(fd, frm.data + 1, frm.data_len - 1);
+			}
+		} else {
+			frm.data_len = btohs(dh.len);
+			err = read_n(fd, frm.data, frm.data_len);
+		}
+
+		if (err < 0)
+			goto failed;
+		if (!err)
+			goto done;
+
+		frm.ptr = frm.data;
+		frm.len = frm.data_len;
+
+		if (parser.flags & DUMP_PKTLOG) {
+			uint64_t ts;
+			ts = be64toh(ph.ts);
+			frm.ts.tv_sec = ts >> 32;
+			frm.ts.tv_usec = ts & 0xffffffff;
+		} else if (parser.flags & DUMP_BTSNOOP) {
+			uint64_t ts;
+			frm.in = be32toh(dp.flags) & 0x01;
+			ts = be64toh(dp.ts) - 0x00E03AB44A676000ll;
+			frm.ts.tv_sec = (ts / 1000000ll) + 946684800ll;
+			frm.ts.tv_usec = ts % 1000000ll;
+		} else {
+			frm.in = dh.in;
+			frm.ts.tv_sec  = btohl(dh.ts_sec);
+			frm.ts.tv_usec = btohl(dh.ts_usec);
+		}
+
+		parse(&frm);
+	}
+
+done:
+	free(frm.data);
+	return;
+
+failed:
+	perror("Read failed");
+	free(frm.data);
+	exit(1);
+}
+
+static int open_file(char *file, int mode, unsigned long flags)
+{
+	unsigned char buf[BTSNOOP_HDR_SIZE];
+	struct btsnoop_hdr *hdr = (struct btsnoop_hdr *) buf;
+	int fd, len, open_flags;
+
+	if (mode == WRITE || mode == PPPDUMP || mode == AUDIO)
+		open_flags = O_WRONLY | O_CREAT | O_TRUNC;
+	else
+		open_flags = O_RDONLY;
+
+	fd = open(file, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if (fd < 0) {
+		perror("Can't open dump file");
+		exit(1);
+	}
+
+	if (mode == READ) {
+		len = read(fd, buf, BTSNOOP_HDR_SIZE);
+		if (len != BTSNOOP_HDR_SIZE) {
+			lseek(fd, 0, SEEK_SET);
+			return fd;
+		}
+
+		if (!memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id))) {
+			parser.flags |= DUMP_BTSNOOP;
+
+			btsnoop_version = be32toh(hdr->version);
+			btsnoop_type = be32toh(hdr->type);
+
+			printf("btsnoop version: %d datalink type: %d\n",
+						btsnoop_version, btsnoop_type);
+
+			if (btsnoop_version != 1) {
+				fprintf(stderr, "Unsupported BTSnoop version\n");
+				exit(1);
+			}
+
+			if (btsnoop_type != 1001 && btsnoop_type != 1002 &&
+							btsnoop_type != 2001) {
+				fprintf(stderr, "Unsupported BTSnoop datalink type\n");
+				exit(1);
+			}
+		} else {
+			if (buf[0] == 0x00 && buf[1] == 0x00) {
+				parser.flags |= DUMP_PKTLOG;
+				printf("packet logger data format\n");
+			}
+
+			parser.flags &= ~DUMP_BTSNOOP;
+			lseek(fd, 0, SEEK_SET);
+			return fd;
+		}
+	} else {
+		if (flags & DUMP_BTSNOOP) {
+			btsnoop_version = 1;
+			btsnoop_type = 1002;
+
+			memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id));
+			hdr->version = htobe32(btsnoop_version);
+			hdr->type = htobe32(btsnoop_type);
+
+			printf("btsnoop version: %d datalink type: %d\n",
+						btsnoop_version, btsnoop_type);
+
+			len = write(fd, buf, BTSNOOP_HDR_SIZE);
+			if (len < 0) {
+				perror("Can't create dump header");
+				exit(1);
+			}
+
+			if (len != BTSNOOP_HDR_SIZE) {
+				fprintf(stderr, "Header size mismatch\n");
+				exit(1);
+			}
+		}
+	}
+
+	return fd;
+}
+
+static int open_socket(int dev, unsigned long flags)
+{
+	struct sockaddr_hci addr;
+	struct hci_filter flt;
+	int sk, opt;
+
+	/* Create HCI socket */
+	sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+	if (sk < 0) {
+		perror("Can't create raw socket");
+		return -1;
+	}
+
+	opt = 1;
+	if (setsockopt(sk, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
+		perror("Can't enable data direction info");
+		goto fail;
+	}
+
+	opt = 1;
+	if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+		perror("Can't enable time stamp");
+		goto fail;
+	}
+
+	/* Setup filter */
+	hci_filter_clear(&flt);
+	hci_filter_all_ptypes(&flt);
+	hci_filter_all_events(&flt);
+	if (setsockopt(sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+		perror("Can't set filter");
+		goto fail;
+	}
+
+	/* Bind socket to the HCI device */
+	memset(&addr, 0, sizeof(addr));
+	addr.hci_family = AF_BLUETOOTH;
+	addr.hci_dev = dev;
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		printf("Can't attach to device hci%d. %s(%d)\n",
+					dev, strerror(errno), errno);
+		goto fail;
+	}
+
+	return sk;
+
+fail:
+	close(sk);
+	return -1;
+}
+
+static struct {
+	char *name;
+	int  flag;
+} filters[] = {
+	{ "lmp",	FILT_LMP	},
+	{ "hci",	FILT_HCI	},
+	{ "sco",	FILT_SCO	},
+	{ "l2cap",	FILT_L2CAP	},
+	{ "a2mp",	FILT_A2MP	},
+	{ "rfcomm",	FILT_RFCOMM	},
+	{ "sdp",	FILT_SDP	},
+	{ "bnep",	FILT_BNEP	},
+	{ "cmtp",	FILT_CMTP	},
+	{ "hidp",	FILT_HIDP	},
+	{ "hcrp",	FILT_HCRP	},
+	{ "att",	FILT_ATT	},
+	{ "smp",	FILT_SMP	},
+	{ "avdtp",	FILT_AVDTP	},
+	{ "avctp",	FILT_AVCTP	},
+	{ "obex",	FILT_OBEX	},
+	{ "capi",	FILT_CAPI	},
+	{ "ppp",	FILT_PPP	},
+	{ "sap",	FILT_SAP	},
+	{ "csr",	FILT_CSR	},
+	{ "dga",	FILT_DGA	},
+	{ 0 }
+};
+
+static unsigned long parse_filter(int argc, char **argv)
+{
+	unsigned long filter = 0;
+	int i,n;
+
+	for (i = 0; i < argc; i++) {
+		for (n = 0; filters[n].name; n++) {
+			if (!strcasecmp(filters[n].name, argv[i])) {
+				filter |= filters[n].flag;
+				break;
+			}
+		}
+	}
+
+	return filter;
+}
+
+static void usage(void)
+{
+	printf(
+	"Usage: hcidump [OPTION...] [filter]\n"
+	"  -i, --device=hci_dev       HCI device\n"
+	"  -l, --snap-len=len         Snap len (in bytes)\n"
+	"  -p, --psm=psm              Default PSM\n"
+	"  -m, --manufacturer=compid  Default manufacturer\n"
+	"  -w, --save-dump=file       Save dump to a file\n"
+	"  -r, --read-dump=file       Read dump from a file\n"
+	"  -t, --ts                   Display time stamps\n"
+	"  -a, --ascii                Dump data in ascii\n"
+	"  -x, --hex                  Dump data in hex\n"
+	"  -X, --ext                  Dump data in hex and ascii\n"
+	"  -R, --raw                  Dump raw data\n"
+	"  -C, --cmtp=psm             PSM for CMTP\n"
+	"  -H, --hcrp=psm             PSM for HCRP\n"
+	"  -O, --obex=port            Channel/PSM for OBEX\n"
+	"  -P, --ppp=channel          Channel for PPP\n"
+	"  -S, --sap=channel          Channel for SAP\n"
+	"  -D, --pppdump=file         Extract PPP traffic\n"
+	"  -A, --audio=file           Extract SCO audio data\n"
+	"  -Y, --novendor             No vendor commands or events\n"
+	"  -h, --help                 Give this help list\n"
+	"  -v, --version              Give version information\n"
+	"      --usage                Give a short usage message\n"
+	);
+}
+
+static struct option main_options[] = {
+	{ "device",		1, 0, 'i' },
+	{ "snap-len",		1, 0, 'l' },
+	{ "psm",		1, 0, 'p' },
+	{ "manufacturer",	1, 0, 'm' },
+	{ "save-dump",		1, 0, 'w' },
+	{ "read-dump",		1, 0, 'r' },
+	{ "timestamp",		0, 0, 't' },
+	{ "ascii",		0, 0, 'a' },
+	{ "hex",		0, 0, 'x' },
+	{ "ext",		0, 0, 'X' },
+	{ "raw",		0, 0, 'R' },
+	{ "cmtp",		1, 0, 'C' },
+	{ "hcrp",		1, 0, 'H' },
+	{ "obex",		1, 0, 'O' },
+	{ "ppp",		1, 0, 'P' },
+	{ "sap",		1, 0, 'S' },
+	{ "pppdump",		1, 0, 'D' },
+	{ "audio",		1, 0, 'A' },
+	{ "novendor",		0, 0, 'Y' },
+	{ "help",		0, 0, 'h' },
+	{ "version",		0, 0, 'v' },
+	{ 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	unsigned long flags = 0;
+	unsigned long filter = 0;
+	int device = 0;
+	int defpsm = 0;
+	int defcompid = DEFAULT_COMPID;
+	int opt, pppdump_fd = -1, audio_fd = -1;
+	uint16_t obex_port;
+
+	while ((opt = getopt_long(argc, argv,
+				"i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv",
+				main_options, NULL)) != -1) {
+		switch(opt) {
+		case 'i':
+			if (strcasecmp(optarg, "none") && strcasecmp(optarg, "system"))
+				device = atoi(optarg + 3);
+			else
+				device = HCI_DEV_NONE;
+			break;
+
+		case 'l':
+			snap_len = atoi(optarg);
+			break;
+
+		case 'p':
+			defpsm = atoi(optarg);
+			break;
+
+		case 'm':
+			defcompid = atoi(optarg);
+			break;
+
+		case 'w':
+			mode = WRITE;
+			dump_file = strdup(optarg);
+			break;
+
+		case 'r':
+			mode = READ;
+			dump_file = strdup(optarg);
+			break;
+
+		case 't':
+			flags |= DUMP_TSTAMP;
+			break;
+
+		case 'a':
+			flags |= DUMP_ASCII;
+			break;
+
+		case 'x':
+			flags |= DUMP_HEX;
+			break;
+
+		case 'X':
+			flags |= DUMP_EXT;
+			break;
+
+		case 'R':
+			flags |= DUMP_RAW;
+			break;
+
+		case 'C':
+			set_proto(0, atoi(optarg), 0, SDP_UUID_CMTP);
+			break;
+
+		case 'H':
+			set_proto(0, atoi(optarg), 0, SDP_UUID_HARDCOPY_CONTROL_CHANNEL);
+			break;
+
+		case 'O':
+			obex_port = atoi(optarg);
+			if (obex_port > 31)
+				set_proto(0, obex_port, 0, SDP_UUID_OBEX);
+			else
+				set_proto(0, 0, obex_port, SDP_UUID_OBEX);
+			break;
+
+		case 'P':
+			set_proto(0, 0, atoi(optarg), SDP_UUID_LAN_ACCESS_PPP);
+			break;
+
+		case 'S':
+			set_proto(0, 0, atoi(optarg), SDP_UUID_SIM_ACCESS);
+			break;
+
+		case 'D':
+			pppdump_file = strdup(optarg);
+			break;
+
+		case 'A':
+			audio_file = strdup(optarg);
+			break;
+
+		case 'Y':
+			flags |= DUMP_NOVENDOR;
+			break;
+
+		case 'v':
+			printf("%s\n", VERSION);
+			exit(0);
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	printf("HCI sniffer - Bluetooth packet analyzer ver %s\n", VERSION);
+
+	if (argc > 0)
+		filter = parse_filter(argc, argv);
+
+	/* Default settings */
+	if (!filter)
+		filter = ~0L;
+
+	if (pppdump_file)
+		pppdump_fd = open_file(pppdump_file, PPPDUMP, flags);
+
+	if (audio_file)
+		audio_fd = open_file(audio_file, AUDIO, flags);
+
+	switch (mode) {
+	case PARSE:
+		flags |= DUMP_VERBOSE;
+		init_parser(flags, filter, defpsm, defcompid,
+							pppdump_fd, audio_fd);
+		process_frames(device, open_socket(device, flags), -1, flags);
+		break;
+
+	case READ:
+		flags |= DUMP_VERBOSE;
+		init_parser(flags, filter, defpsm, defcompid,
+							pppdump_fd, audio_fd);
+		read_dump(open_file(dump_file, mode, flags));
+		break;
+
+	case WRITE:
+		flags |= DUMP_BTSNOOP;
+		process_frames(device, open_socket(device, flags),
+				open_file(dump_file, mode, flags), flags);
+		break;
+	}
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hcieventmask.c b/bluez/repo/tools/hcieventmask.c
new file mode 100644
index 0000000..b5f818d
--- /dev/null
+++ b/bluez/repo/tools/hcieventmask.c
@@ -0,0 +1,130 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-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 <stdlib.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+static struct option main_options[] = {
+	{ "device",	1, 0, 'i' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	uint8_t events[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00 };
+	struct hci_dev_info di;
+	struct hci_version ver;
+	int dd, opt, dev = 0;
+
+	while ((opt=getopt_long(argc, argv, "+i:", main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'i':
+			dev = hci_devid(optarg);
+			if (dev < 0) {
+				perror("Invalid device");
+				exit(1);
+			}
+			break;
+		}
+	}
+
+	dd = hci_open_dev(dev);
+	if (dd < 0) {
+		fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		exit(1);
+	}
+
+	if (hci_devinfo(dev, &di) < 0) {
+		fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		hci_close_dev(dd);
+		exit(1);
+	}
+
+	if (hci_read_local_version(dd, &ver, 1000) < 0) {
+		fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+						dev, strerror(errno), errno);
+		hci_close_dev(dd);
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+
+	if (ver.hci_ver > 1) {
+		if (di.features[5] & LMP_SNIFF_SUBR)
+			events[5] |= 0x20;
+
+		if (di.features[5] & LMP_PAUSE_ENC)
+			events[5] |= 0x80;
+
+		if (di.features[6] & LMP_EXT_INQ)
+			events[5] |= 0x40;
+
+		if (di.features[6] & LMP_NFLUSH_PKTS)
+			events[7] |= 0x01;
+
+		if (di.features[7] & LMP_LSTO)
+			events[6] |= 0x80;
+
+		if (di.features[6] & LMP_SIMPLE_PAIR) {
+			events[6] |= 0x01;	/* IO Capability Request */
+			events[6] |= 0x02;	/* IO Capability Response */
+			events[6] |= 0x04;	/* User Confirmation Request */
+			events[6] |= 0x08;	/* User Passkey Request */
+			events[6] |= 0x10;	/* Remote OOB Data Request */
+			events[6] |= 0x20;	/* Simple Pairing Complete */
+			events[7] |= 0x04;	/* User Passkey Notification */
+			events[7] |= 0x08;	/* Keypress Notification */
+			events[7] |= 0x10;	/* Remote Host Supported
+						 * Features Notification */
+		}
+
+		if (di.features[4] & LMP_LE)
+			events[7] |= 0x20;
+
+		if (di.features[6] & LMP_LE_BREDR)
+			events[7] |= 0x20;
+	}
+
+	printf("Setting event mask:\n");
+	printf("\thcitool cmd 0x%02x 0x%04x  "
+					"0x%02x 0x%02x 0x%02x 0x%02x "
+					"0x%02x 0x%02x 0x%02x 0x%02x\n",
+				OGF_HOST_CTL, OCF_SET_EVENT_MASK,
+				events[0], events[1], events[2], events[3],
+				events[4], events[5], events[6], events[7]);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hcisecfilter.c b/bluez/repo/tools/hcisecfilter.c
new file mode 100644
index 0000000..18c9033
--- /dev/null
+++ b/bluez/repo/tools/hcisecfilter.c
@@ -0,0 +1,155 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+int main(int argc, char *argv[])
+{
+	uint32_t type_mask;
+	uint32_t event_mask[2];
+	uint32_t ocf_mask[4];
+
+	/* Packet types */
+	memset(&type_mask, 0, sizeof(type_mask));
+	hci_set_bit(HCI_EVENT_PKT, &type_mask);
+
+	printf("Type mask:        { 0x%02x }\n", type_mask);
+
+	/* Events */
+	memset(event_mask, 0, sizeof(event_mask));
+	hci_set_bit(EVT_INQUIRY_COMPLETE,			event_mask);
+	hci_set_bit(EVT_INQUIRY_RESULT,				event_mask);
+	hci_set_bit(EVT_CONN_COMPLETE,				event_mask);
+	hci_set_bit(EVT_CONN_REQUEST,				event_mask);
+	hci_set_bit(EVT_DISCONN_COMPLETE,			event_mask);
+	hci_set_bit(EVT_AUTH_COMPLETE,				event_mask);
+	hci_set_bit(EVT_REMOTE_NAME_REQ_COMPLETE,		event_mask);
+	hci_set_bit(EVT_ENCRYPT_CHANGE,				event_mask);
+	hci_set_bit(EVT_READ_REMOTE_FEATURES_COMPLETE,		event_mask);
+	hci_set_bit(EVT_READ_REMOTE_VERSION_COMPLETE,		event_mask);
+	hci_set_bit(EVT_CMD_COMPLETE,				event_mask);
+	hci_set_bit(EVT_CMD_STATUS,				event_mask);
+	hci_set_bit(EVT_READ_CLOCK_OFFSET_COMPLETE,		event_mask);
+	hci_set_bit(EVT_INQUIRY_RESULT_WITH_RSSI,		event_mask);
+	hci_set_bit(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE,	event_mask);
+	hci_set_bit(EVT_SYNC_CONN_COMPLETE,			event_mask);
+	hci_set_bit(EVT_SYNC_CONN_CHANGED,			event_mask);
+	hci_set_bit(EVT_EXTENDED_INQUIRY_RESULT,		event_mask);
+
+	printf("Event mask:       { 0x%08x, 0x%08x }\n",
+					event_mask[0], event_mask[1]);
+
+	/* OGF_LINK_CTL */
+	memset(ocf_mask, 0, sizeof(ocf_mask));
+	hci_set_bit(OCF_INQUIRY,			ocf_mask);
+	hci_set_bit(OCF_INQUIRY_CANCEL,			ocf_mask);
+	hci_set_bit(OCF_REMOTE_NAME_REQ,		ocf_mask);
+	hci_set_bit(OCF_REMOTE_NAME_REQ_CANCEL,		ocf_mask);
+	hci_set_bit(OCF_READ_REMOTE_FEATURES,		ocf_mask);
+	hci_set_bit(OCF_READ_REMOTE_EXT_FEATURES,	ocf_mask);
+	hci_set_bit(OCF_READ_REMOTE_VERSION,		ocf_mask);
+	hci_set_bit(OCF_READ_CLOCK_OFFSET,		ocf_mask);
+	hci_set_bit(OCF_READ_LMP_HANDLE,		ocf_mask);
+
+	printf("OGF_LINK_CTL:     { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+			ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+	/* OGF_LINK_POLICY */
+	memset(ocf_mask, 0, sizeof(ocf_mask));
+	hci_set_bit(OCF_ROLE_DISCOVERY,			ocf_mask);
+	hci_set_bit(OCF_READ_LINK_POLICY,		ocf_mask);
+	hci_set_bit(OCF_READ_DEFAULT_LINK_POLICY,	ocf_mask);
+
+	printf("OGF_LINK_POLICY:  { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+			ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+	/* OGF_HOST_CTL */
+	memset(ocf_mask, 0, sizeof(ocf_mask));
+	hci_set_bit(OCF_READ_PIN_TYPE,			ocf_mask);
+	hci_set_bit(OCF_READ_LOCAL_NAME,		ocf_mask);
+	hci_set_bit(OCF_READ_CONN_ACCEPT_TIMEOUT,	ocf_mask);
+	hci_set_bit(OCF_READ_PAGE_TIMEOUT,		ocf_mask);
+	hci_set_bit(OCF_READ_SCAN_ENABLE,		ocf_mask);
+	hci_set_bit(OCF_READ_PAGE_ACTIVITY,		ocf_mask);
+	hci_set_bit(OCF_READ_INQ_ACTIVITY,		ocf_mask);
+	hci_set_bit(OCF_READ_AUTH_ENABLE,		ocf_mask);
+	hci_set_bit(OCF_READ_ENCRYPT_MODE,		ocf_mask);
+	hci_set_bit(OCF_READ_CLASS_OF_DEV,		ocf_mask);
+	hci_set_bit(OCF_READ_VOICE_SETTING,		ocf_mask);
+	hci_set_bit(OCF_READ_AUTOMATIC_FLUSH_TIMEOUT,	ocf_mask);
+	hci_set_bit(OCF_READ_NUM_BROADCAST_RETRANS,	ocf_mask);
+	hci_set_bit(OCF_READ_HOLD_MODE_ACTIVITY,	ocf_mask);
+	hci_set_bit(OCF_READ_TRANSMIT_POWER_LEVEL,	ocf_mask);
+	hci_set_bit(OCF_READ_LINK_SUPERVISION_TIMEOUT,	ocf_mask);
+	hci_set_bit(OCF_READ_NUM_SUPPORTED_IAC,		ocf_mask);
+	hci_set_bit(OCF_READ_CURRENT_IAC_LAP,		ocf_mask);
+	hci_set_bit(OCF_READ_PAGE_SCAN_PERIOD_MODE,	ocf_mask);
+	hci_set_bit(OCF_READ_PAGE_SCAN_MODE,		ocf_mask);
+	hci_set_bit(OCF_READ_INQUIRY_SCAN_TYPE,		ocf_mask);
+	hci_set_bit(OCF_READ_INQUIRY_MODE,		ocf_mask);
+	hci_set_bit(OCF_READ_PAGE_SCAN_TYPE,		ocf_mask);
+	hci_set_bit(OCF_READ_AFH_MODE,			ocf_mask);
+	hci_set_bit(OCF_READ_EXT_INQUIRY_RESPONSE,	ocf_mask);
+	hci_set_bit(OCF_READ_SIMPLE_PAIRING_MODE,	ocf_mask);
+	hci_set_bit(OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL,	ocf_mask);
+	hci_set_bit(OCF_READ_DEFAULT_ERROR_DATA_REPORTING,	ocf_mask);
+
+	printf("OGF_HOST_CTL:     { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+			ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+	/* OGF_INFO_PARAM */
+	memset(ocf_mask, 0, sizeof(ocf_mask));
+	hci_set_bit(OCF_READ_LOCAL_VERSION,		ocf_mask);
+	hci_set_bit(OCF_READ_LOCAL_COMMANDS,		ocf_mask);
+	hci_set_bit(OCF_READ_LOCAL_FEATURES,		ocf_mask);
+	hci_set_bit(OCF_READ_LOCAL_EXT_FEATURES,	ocf_mask);
+	hci_set_bit(OCF_READ_BUFFER_SIZE,		ocf_mask);
+	hci_set_bit(OCF_READ_COUNTRY_CODE,		ocf_mask);
+	hci_set_bit(OCF_READ_BD_ADDR,			ocf_mask);
+
+	printf("OGF_INFO_PARAM:   { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+			ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+	/* OGF_STATUS_PARAM */
+	memset(ocf_mask, 0, sizeof(ocf_mask));
+	hci_set_bit(OCF_READ_FAILED_CONTACT_COUNTER,	ocf_mask);
+	hci_set_bit(OCF_READ_LINK_QUALITY,		ocf_mask);
+	hci_set_bit(OCF_READ_RSSI,			ocf_mask);
+	hci_set_bit(OCF_READ_AFH_MAP,			ocf_mask);
+	hci_set_bit(OCF_READ_CLOCK,			ocf_mask);
+
+	printf("OGF_STATUS_PARAM: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+			ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hcitool.1 b/bluez/repo/tools/hcitool.1
new file mode 100644
index 0000000..7d06556
--- /dev/null
+++ b/bluez/repo/tools/hcitool.1
@@ -0,0 +1,255 @@
+.TH HCITOOL 1 "Nov 12 2002" BlueZ "Linux System Administration"
+.SH NAME
+hcitool \- configure Bluetooth connections
+.SH SYNOPSIS
+.B hcitool [-h]
+.br
+.B hcitool [-i <hciX>] [command [command parameters]]
+
+.SH DESCRIPTION
+.LP
+.B
+hcitool
+is used to configure Bluetooth connections and send some special command to
+Bluetooth devices. If no
+.B
+command
+is given, or if the option
+.B
+-h
+is used,
+.B
+hcitool
+prints some usage information and exits.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands
+.TP
+.BI -i " <hciX>"
+The command is applied to device
+.I
+hciX
+, which must be the name of an installed Bluetooth device. If not specified,
+the command will be sent to the first available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI dev
+Display local devices
+.TP
+.BI inq
+Inquire remote devices. For each discovered device, Bluetooth device address,
+clock offset and class are printed.
+.TP
+.BI scan
+Inquire remote devices. For each discovered device, device name are printed.
+.TP
+.BI name " <bdaddr>"
+Print device name of remote device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI info " <bdaddr>"
+Print device name, version and supported features of remote device with
+Bluetooth address
+.IR bdaddr .
+.TP
+.BI spinq
+Start periodic inquiry process. No inquiry results are printed.
+.TP
+.BI epinq
+Exit periodic inquiry process.
+.TP
+.BI cmd " <ogf> <ocf> [parameters]"
+Submit an arbitrary HCI command to local device.
+.IR ogf ,
+.IR ocf
+and
+.IR parameters
+are hexadecimal bytes.
+.TP
+.BI con
+Display active baseband connections
+.TP
+.BI cc " [--role=m|s] [--pkt-type=<ptype>] <bdaddr>"
+Create baseband connection to remote device with Bluetooth address
+.IR bdaddr .
+Option
+.I
+--pkt-type
+specifies a list of allowed packet types.
+.I
+<ptype>
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+Default is to allow all packet types. Option
+.I
+--role
+can have value
+.I
+m
+(do not allow role switch, stay master) or
+.I
+s
+(allow role switch, become slave if the peer asks to become master). Default is
+.IR m .
+.TP
+.BI dc " <bdaddr> [reason]"
+Delete baseband connection from remote device with Bluetooth address
+.IR bdaddr .
+The reason can be one of the Bluetooth HCI error codes. Default is
+.IR 19
+for user ended connections. The value must be given in decimal.
+.TP
+.BI sr " <bdaddr> <role>"
+Switch role for the baseband connection from the remote device to
+.BR master
+or
+.BR slave .
+.TP
+.BI cpt " <bdaddr> <packet types>"
+Change packet types for baseband connection to device with Bluetooth address
+.IR bdaddr .
+.I
+packet types
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+.TP
+.BI rssi " <bdaddr>"
+Display received signal strength information for the connection to the device
+with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lq " <bdaddr>"
+Display link quality for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI tpl " <bdaddr> [type]"
+Display transmit power level for the connection to the device with Bluetooth address
+.IR bdaddr .
+The type can be
+.BR 0
+for the current transmit power level (which is default) or
+.BR 1
+for the maximum transmit power level.
+.TP
+.BI afh " <bdaddr>"
+Display AFH channel map for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lp " <bdaddr> [value]"
+With no
+.IR value ,
+displays link policy settings for the connection to the device with Bluetooth address
+.IR bdaddr .
+If
+.IR value
+is given, sets the link policy settings for that connection to
+.IR value .
+Possible values are RSWITCH, HOLD, SNIFF and PARK.
+.TP
+.BI lst " <bdaddr> [value]"
+With no
+.IR value ,
+displays link supervision timeout for the connection to the device with Bluetooth address
+.IR bdaddr .
+If
+.I
+value
+is given, sets the link supervision timeout for that connection to
+.I
+value
+slots, or to infinite if
+.I
+value
+is 0.
+.TP
+.BI auth " <bdaddr>"
+Request authentication for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI enc " <bdaddr> [encrypt enable]"
+Enable or disable the encryption for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI key " <bdaddr>"
+Change the connection link key for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clkoff " <bdaddr>"
+Read the clock offset for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clock " [bdaddr] [which clock]"
+Read the clock for the device with Bluetooth address
+.IR bdaddr .
+The clock can be
+.BR 0
+for the local clock or
+.BR 1
+for the piconet clock (which is default).
+.TP
+.BI lescan " [--privacy] [--passive] [--whitelist] [--discovery=g|l] \
+[--duplicates]"
+Start LE scan
+.TP
+.BI leinfo " [--static] [--random] <bdaddr>"
+Get LE remote information
+.TP
+.BI lewladd " [--random] <bdaddr>"
+Add device to LE White List
+.TP
+.BI lewlrm " <bdaddr>"
+Remove device from LE White List
+.TP
+.BI lewlsz
+Read size of LE White List
+.TP
+.BI lewlclr
+Clear LE White List
+.TP
+.BI lerladd " [--local irk] [--peer irk] [--random] <bdaddr>"
+Add device to LE Resolving List
+.TP
+.BI lerlrm " <bdaddr>"
+Remove device from LE Resolving List
+.TP
+.BI lerlclr
+Clear LE Resolving List
+.TP
+.BI lerlsz
+Read size of LE Resolving List
+.TP
+.BI lerlon
+Enable LE Address Resolution
+.TP
+.BI lerloff
+Disable LE Address Resolution
+.TP
+.BI lecc " [--static] [--random] <bdaddr> | [--whitelist]"
+Create a LE Connection
+.TP
+.BI ledc " <handle> [reason]"
+Disconnect a LE Connection
+.TP
+.BI lecup " <handle> <min> <max> <latency> <timeout>"
+LE Connection Update
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/bluez/repo/tools/hcitool.c b/bluez/repo/tools/hcitool.c
new file mode 100644
index 0000000..02c4ebe
--- /dev/null
+++ b/bluez/repo/tools/hcitool.c
@@ -0,0 +1,3501 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <signal.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/oui.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Unofficial value, might still change */
+#define LE_LINK		0x80
+
+#define FLAGS_AD_TYPE 0x01
+#define FLAGS_LIMITED_MODE_BIT 0x01
+#define FLAGS_GENERAL_MODE_BIT 0x02
+
+#define EIR_FLAGS                   0x01  /* flags */
+#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT              0x08  /* shortened local name */
+#define EIR_NAME_COMPLETE           0x09  /* complete local name */
+#define EIR_TX_POWER                0x0A  /* transmit power level */
+#define EIR_DEVICE_ID               0x10  /* device ID */
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
+
+static volatile int signal_received = 0;
+
+static void usage(void);
+
+static int str2buf(const char *str, uint8_t *buf, size_t blen)
+{
+	int i, dlen;
+
+	if (str == NULL)
+		return -EINVAL;
+
+	memset(buf, 0, blen);
+
+	dlen = MIN((strlen(str) / 2), blen);
+
+	for (i = 0; i < dlen; i++)
+		sscanf(str + (i * 2), "%02hhX", &buf[i]);
+
+	return 0;
+}
+
+static int dev_info(int s, int dev_id, long arg)
+{
+	struct hci_dev_info di = { .dev_id = dev_id };
+	char addr[18];
+
+	if (ioctl(s, HCIGETDEVINFO, (void *) &di))
+		return 0;
+
+	ba2str(&di.bdaddr, addr);
+	printf("\t%s\t%s\n", di.name, addr);
+	return 0;
+}
+
+static void helper_arg(int min_num_arg, int max_num_arg, int *argc,
+			char ***argv, const char *usage)
+{
+	*argc -= optind;
+	/* too many arguments, but when "max_num_arg < min_num_arg" then no
+		 limiting (prefer "max_num_arg=-1" to gen infinity)
+	*/
+	if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) {
+		fprintf(stderr, "%s: too many arguments (maximal: %i)\n",
+				*argv[0], max_num_arg);
+		printf("%s", usage);
+		exit(1);
+	}
+
+	/* print usage */
+	if (*argc < min_num_arg) {
+		fprintf(stderr, "%s: too few arguments (minimal: %i)\n",
+				*argv[0], min_num_arg);
+		printf("%s", usage);
+		exit(0);
+	}
+
+	*argv += optind;
+}
+
+static char *type2str(uint8_t type)
+{
+	switch (type) {
+	case SCO_LINK:
+		return "SCO";
+	case ACL_LINK:
+		return "ACL";
+	case ESCO_LINK:
+		return "eSCO";
+	case LE_LINK:
+		return "LE";
+	default:
+		return "Unknown";
+	}
+}
+
+static int conn_list(int s, int dev_id, long arg)
+{
+	struct hci_conn_list_req *cl;
+	struct hci_conn_info *ci;
+	int id = arg;
+	int i;
+
+	if (id != -1 && dev_id != id)
+		return 0;
+
+	if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+	cl->dev_id = dev_id;
+	cl->conn_num = 10;
+	ci = cl->conn_info;
+
+	if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+		perror("Can't get connection list");
+		exit(1);
+	}
+
+	for (i = 0; i < cl->conn_num; i++, ci++) {
+		char addr[18];
+		char *str;
+		ba2str(&ci->bdaddr, addr);
+		str = hci_lmtostr(ci->link_mode);
+		printf("\t%s %s %s handle %d state %d lm %s\n",
+			ci->out ? "<" : ">", type2str(ci->type),
+			addr, ci->handle, ci->state, str);
+		bt_free(str);
+	}
+
+	free(cl);
+	return 0;
+}
+
+static int find_conn(int s, int dev_id, long arg)
+{
+	struct hci_conn_list_req *cl;
+	struct hci_conn_info *ci;
+	int i;
+
+	if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+	cl->dev_id = dev_id;
+	cl->conn_num = 10;
+	ci = cl->conn_info;
+
+	if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+		perror("Can't get connection list");
+		exit(1);
+	}
+
+	for (i = 0; i < cl->conn_num; i++, ci++)
+		if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) {
+			free(cl);
+			return 1;
+		}
+
+	free(cl);
+	return 0;
+}
+
+static void hex_dump(char *pref, int width, unsigned char *buf, int len)
+{
+	register int i,n;
+
+	for (i = 0, n = 1; i < len; i++, n++) {
+		if (n == 1)
+			printf("%s", pref);
+		printf("%2.2X ", buf[i]);
+		if (n == width) {
+			printf("\n");
+			n = 0;
+		}
+	}
+	if (i && n!=1)
+		printf("\n");
+}
+
+static char *get_minor_device_name(int major, int minor)
+{
+	switch (major) {
+	case 0:	/* misc */
+		return "";
+	case 1:	/* computer */
+		switch (minor) {
+		case 0:
+			return "Uncategorized";
+		case 1:
+			return "Desktop workstation";
+		case 2:
+			return "Server";
+		case 3:
+			return "Laptop";
+		case 4:
+			return "Handheld";
+		case 5:
+			return "Palm";
+		case 6:
+			return "Wearable";
+		}
+		break;
+	case 2:	/* phone */
+		switch (minor) {
+		case 0:
+			return "Uncategorized";
+		case 1:
+			return "Cellular";
+		case 2:
+			return "Cordless";
+		case 3:
+			return "Smart phone";
+		case 4:
+			return "Wired modem or voice gateway";
+		case 5:
+			return "Common ISDN Access";
+		case 6:
+			return "Sim Card Reader";
+		}
+		break;
+	case 3:	/* lan access */
+		if (minor == 0)
+			return "Uncategorized";
+		switch (minor / 8) {
+		case 0:
+			return "Fully available";
+		case 1:
+			return "1-17% utilized";
+		case 2:
+			return "17-33% utilized";
+		case 3:
+			return "33-50% utilized";
+		case 4:
+			return "50-67% utilized";
+		case 5:
+			return "67-83% utilized";
+		case 6:
+			return "83-99% utilized";
+		case 7:
+			return "No service available";
+		}
+		break;
+	case 4:	/* audio/video */
+		switch (minor) {
+		case 0:
+			return "Uncategorized";
+		case 1:
+			return "Device conforms to the Headset profile";
+		case 2:
+			return "Hands-free";
+			/* 3 is reserved */
+		case 4:
+			return "Microphone";
+		case 5:
+			return "Loudspeaker";
+		case 6:
+			return "Headphones";
+		case 7:
+			return "Portable Audio";
+		case 8:
+			return "Car Audio";
+		case 9:
+			return "Set-top box";
+		case 10:
+			return "HiFi Audio Device";
+		case 11:
+			return "VCR";
+		case 12:
+			return "Video Camera";
+		case 13:
+			return "Camcorder";
+		case 14:
+			return "Video Monitor";
+		case 15:
+			return "Video Display and Loudspeaker";
+		case 16:
+			return "Video Conferencing";
+			/* 17 is reserved */
+		case 18:
+			return "Gaming/Toy";
+		}
+		break;
+	case 5:	/* peripheral */ {
+		static char cls_str[48]; cls_str[0] = 0;
+
+		switch (minor & 48) {
+		case 16:
+			strncpy(cls_str, "Keyboard", sizeof(cls_str));
+			break;
+		case 32:
+			strncpy(cls_str, "Pointing device", sizeof(cls_str));
+			break;
+		case 48:
+			strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+			break;
+		}
+		if ((minor & 15) && (strlen(cls_str) > 0))
+			strcat(cls_str, "/");
+
+		switch (minor & 15) {
+		case 0:
+			break;
+		case 1:
+			strncat(cls_str, "Joystick",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 2:
+			strncat(cls_str, "Gamepad",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 3:
+			strncat(cls_str, "Remote control",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 4:
+			strncat(cls_str, "Sensing device",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 5:
+			strncat(cls_str, "Digitizer tablet",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		case 6:
+			strncat(cls_str, "Card reader",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		default:
+			strncat(cls_str, "(reserved)",
+					sizeof(cls_str) - strlen(cls_str) - 1);
+			break;
+		}
+		if (strlen(cls_str) > 0)
+			return cls_str;
+		break;
+	}
+	case 6:	/* imaging */
+		if (minor & 4)
+			return "Display";
+		if (minor & 8)
+			return "Camera";
+		if (minor & 16)
+			return "Scanner";
+		if (minor & 32)
+			return "Printer";
+		break;
+	case 7: /* wearable */
+		switch (minor) {
+		case 1:
+			return "Wrist Watch";
+		case 2:
+			return "Pager";
+		case 3:
+			return "Jacket";
+		case 4:
+			return "Helmet";
+		case 5:
+			return "Glasses";
+		}
+		break;
+	case 8: /* toy */
+		switch (minor) {
+		case 1:
+			return "Robot";
+		case 2:
+			return "Vehicle";
+		case 3:
+			return "Doll / Action Figure";
+		case 4:
+			return "Controller";
+		case 5:
+			return "Game";
+		}
+		break;
+	case 63:	/* uncategorised */
+		return "";
+	}
+	return "Unknown (reserved) minor device class";
+}
+
+static char *major_classes[] = {
+	"Miscellaneous", "Computer", "Phone", "LAN Access",
+	"Audio/Video", "Peripheral", "Imaging", "Uncategorized"
+};
+
+/* Display local devices */
+
+static struct option dev_options[] = {
+	{ "help",	0, 0, 'h' },
+	{0, 0, 0, 0 }
+};
+
+static const char *dev_help =
+	"Usage:\n"
+	"\tdev\n";
+
+static void cmd_dev(int dev_id, int argc, char **argv)
+{
+	int opt;
+
+	for_each_opt(opt, dev_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", dev_help);
+			return;
+		}
+	}
+	helper_arg(0, 0, &argc, &argv, dev_help);
+
+	printf("Devices:\n");
+
+	hci_for_each_dev(HCI_UP, dev_info, 0);
+}
+
+/* Inquiry */
+
+static struct option inq_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "length",	1, 0, 'l' },
+	{ "numrsp",	1, 0, 'n' },
+	{ "iac",	1, 0, 'i' },
+	{ "flush",	0, 0, 'f' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *inq_help =
+	"Usage:\n"
+	"\tinq [--length=N] maximum inquiry duration in 1.28 s units\n"
+	"\t    [--numrsp=N] specify maximum number of inquiry responses\n"
+	"\t    [--iac=lap]  specify the inquiry access code\n"
+	"\t    [--flush]    flush the inquiry cache\n";
+
+static void cmd_inq(int dev_id, int argc, char **argv)
+{
+	inquiry_info *info = NULL;
+	uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+	int num_rsp, length, flags;
+	char addr[18];
+	int i, l, opt;
+
+	length  = 8;	/* ~10 seconds */
+	num_rsp = 0;
+	flags   = 0;
+
+	for_each_opt(opt, inq_options, NULL) {
+		switch (opt) {
+		case 'l':
+			length = atoi(optarg);
+			break;
+
+		case 'n':
+			num_rsp = atoi(optarg);
+			break;
+
+		case 'i':
+			l = strtoul(optarg, 0, 16);
+			if (!strcasecmp(optarg, "giac")) {
+				l = 0x9e8b33;
+			} else if (!strcasecmp(optarg, "liac")) {
+				l = 0x9e8b00;
+			} if (l < 0x9e8b00 || l > 0x9e8b3f) {
+				printf("Invalid access code 0x%x\n", l);
+				exit(1);
+			}
+			lap[0] = (l & 0xff);
+			lap[1] = (l >> 8) & 0xff;
+			lap[2] = (l >> 16) & 0xff;
+			break;
+
+		case 'f':
+			flags |= IREQ_CACHE_FLUSH;
+			break;
+
+		default:
+			printf("%s", inq_help);
+			return;
+		}
+	}
+	helper_arg(0, 0, &argc, &argv, inq_help);
+
+	printf("Inquiring ...\n");
+
+	num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+	if (num_rsp < 0) {
+		perror("Inquiry failed.");
+		exit(1);
+	}
+
+	for (i = 0; i < num_rsp; i++) {
+		ba2str(&(info+i)->bdaddr, addr);
+		printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",
+			addr, btohs((info+i)->clock_offset),
+			(info+i)->dev_class[2],
+			(info+i)->dev_class[1],
+			(info+i)->dev_class[0]);
+	}
+
+	bt_free(info);
+}
+
+/* Device scanning */
+
+static struct option scan_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "length",	1, 0, 'l' },
+	{ "numrsp",	1, 0, 'n' },
+	{ "iac",	1, 0, 'i' },
+	{ "flush",	0, 0, 'f' },
+	{ "class",	0, 0, 'C' },
+	{ "info",	0, 0, 'I' },
+	{ "oui",	0, 0, 'O' },
+	{ "all",	0, 0, 'A' },
+	{ "ext",	0, 0, 'A' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *scan_help =
+	"Usage:\n"
+	"\tscan [--length=N] [--numrsp=N] [--iac=lap] [--flush] [--class] [--info] [--oui] [--refresh]\n";
+
+static void cmd_scan(int dev_id, int argc, char **argv)
+{
+	inquiry_info *info = NULL;
+	uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+	int num_rsp, length, flags;
+	uint8_t cls[3], features[8];
+	char addr[18], name[249], *comp;
+	struct hci_version version;
+	struct hci_dev_info di;
+	struct hci_conn_info_req *cr;
+	int extcls = 0, extinf = 0, extoui = 0;
+	int i, n, l, opt, dd, cc;
+
+	length  = 8;	/* ~10 seconds */
+	num_rsp = 0;
+	flags   = 0;
+
+	for_each_opt(opt, scan_options, NULL) {
+		switch (opt) {
+		case 'l':
+			length = atoi(optarg);
+			break;
+
+		case 'n':
+			num_rsp = atoi(optarg);
+			break;
+
+		case 'i':
+			l = strtoul(optarg, 0, 16);
+			if (!strcasecmp(optarg, "giac")) {
+				l = 0x9e8b33;
+			} else if (!strcasecmp(optarg, "liac")) {
+				l = 0x9e8b00;
+			} else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+				printf("Invalid access code 0x%x\n", l);
+				exit(1);
+			}
+			lap[0] = (l & 0xff);
+			lap[1] = (l >> 8) & 0xff;
+			lap[2] = (l >> 16) & 0xff;
+			break;
+
+		case 'f':
+			flags |= IREQ_CACHE_FLUSH;
+			break;
+
+		case 'C':
+			extcls = 1;
+			break;
+
+		case 'I':
+			extinf = 1;
+			break;
+
+		case 'O':
+			extoui = 1;
+			break;
+
+		case 'A':
+			extcls = 1;
+			extinf = 1;
+			extoui = 1;
+			break;
+
+		default:
+			printf("%s", scan_help);
+			return;
+		}
+	}
+	helper_arg(0, 0, &argc, &argv, scan_help);
+
+	if (dev_id < 0) {
+		dev_id = hci_get_route(NULL);
+		if (dev_id < 0) {
+			perror("Device is not available");
+			exit(1);
+		}
+	}
+
+	if (hci_devinfo(dev_id, &di) < 0) {
+		perror("Can't get device info");
+		exit(1);
+	}
+
+	printf("Scanning ...\n");
+	num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+	if (num_rsp < 0) {
+		perror("Inquiry failed");
+		exit(1);
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		free(info);
+		exit(1);
+	}
+
+	if (extcls || extinf || extoui)
+		printf("\n");
+
+	for (i = 0; i < num_rsp; i++) {
+		uint16_t handle = 0;
+
+		if (!extcls && !extinf && !extoui) {
+			ba2str(&(info+i)->bdaddr, addr);
+
+			if (hci_read_remote_name_with_clock_offset(dd,
+					&(info+i)->bdaddr,
+					(info+i)->pscan_rep_mode,
+					(info+i)->clock_offset | 0x8000,
+					sizeof(name), name, 100000) < 0)
+				strcpy(name, "n/a");
+
+			for (n = 0; n < 248 && name[n]; n++) {
+				if ((unsigned char) name[i] < 32 || name[i] == 127)
+					name[i] = '.';
+			}
+
+			name[248] = '\0';
+
+			printf("\t%s\t%s\n", addr, name);
+			continue;
+		}
+
+		ba2str(&(info+i)->bdaddr, addr);
+		printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr,
+			(info+i)->pscan_rep_mode, btohs((info+i)->clock_offset));
+
+		if (extoui) {
+			comp = batocomp(&(info+i)->bdaddr);
+			if (comp) {
+				char oui[9];
+				ba2oui(&(info+i)->bdaddr, oui);
+				printf("OUI company:\t%s (%s)\n", comp, oui);
+				free(comp);
+			}
+		}
+
+		cc = 0;
+
+		if (extinf) {
+			cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+			if (cr) {
+				bacpy(&cr->bdaddr, &(info+i)->bdaddr);
+				cr->type = ACL_LINK;
+				if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+					handle = 0;
+					cc = 1;
+				} else {
+					handle = htobs(cr->conn_info->handle);
+					cc = 0;
+				}
+				free(cr);
+			}
+
+			if (cc) {
+				if (hci_create_connection(dd, &(info+i)->bdaddr,
+						htobs(di.pkt_type & ACL_PTYPE_MASK),
+						(info+i)->clock_offset | 0x8000,
+						0x01, &handle, 25000) < 0) {
+					handle = 0;
+					cc = 0;
+				}
+			}
+		}
+
+		if (hci_read_remote_name_with_clock_offset(dd,
+					&(info+i)->bdaddr,
+					(info+i)->pscan_rep_mode,
+					(info+i)->clock_offset | 0x8000,
+					sizeof(name), name, 100000) < 0) {
+		} else {
+			for (n = 0; n < 248 && name[n]; n++) {
+				if ((unsigned char) name[i] < 32 || name[i] == 127)
+					name[i] = '.';
+			}
+
+			name[248] = '\0';
+		}
+
+		if (strlen(name) > 0)
+			printf("Device name:\t%s\n", name);
+
+		if (extcls) {
+			memcpy(cls, (info+i)->dev_class, 3);
+			printf("Device class:\t");
+			if ((cls[1] & 0x1f) > sizeof(major_classes) / sizeof(char *))
+				printf("Invalid");
+			else
+				printf("%s, %s", major_classes[cls[1] & 0x1f],
+					get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
+			printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]);
+		}
+
+		if (extinf && handle > 0) {
+			if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+				char *ver = lmp_vertostr(version.lmp_ver);
+				printf("Manufacturer:\t%s (%d)\n",
+					bt_compidtostr(version.manufacturer),
+					version.manufacturer);
+				printf("LMP version:\t%s (0x%x) [subver 0x%x]\n",
+					ver ? ver : "n/a",
+					version.lmp_ver, version.lmp_subver);
+				if (ver)
+					bt_free(ver);
+			}
+
+			if (hci_read_remote_features(dd, handle, features, 20000) == 0) {
+				char *tmp = lmp_featurestostr(features, "\t\t", 63);
+				printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x"
+					" 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+					features[0], features[1],
+					features[2], features[3],
+					features[4], features[5],
+					features[6], features[7]);
+				printf("%s\n", tmp);
+				bt_free(tmp);
+			}
+
+			if (cc) {
+				usleep(10000);
+				hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+			}
+		}
+
+		printf("\n");
+	}
+
+	bt_free(info);
+
+	hci_close_dev(dd);
+}
+
+/* Remote name */
+
+static struct option name_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *name_help =
+	"Usage:\n"
+	"\tname <bdaddr>\n";
+
+static void cmd_name(int dev_id, int argc, char **argv)
+{
+	bdaddr_t bdaddr;
+	char name[248];
+	int opt, dd;
+
+	for_each_opt(opt, name_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", name_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, name_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_get_route(&bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Device is not available.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+		printf("%s\n", name);
+
+	hci_close_dev(dd);
+}
+
+/* Info about remote device */
+
+static struct option info_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *info_help =
+	"Usage:\n"
+	"\tinfo <bdaddr>\n";
+
+static void cmd_info(int dev_id, int argc, char **argv)
+{
+	bdaddr_t bdaddr;
+	uint16_t handle;
+	uint8_t features[8], max_page = 0;
+	char name[249], *comp, *tmp;
+	struct hci_version version;
+	struct hci_dev_info di;
+	struct hci_conn_info_req *cr;
+	int i, opt, dd, cc = 0;
+
+	for_each_opt(opt, info_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", info_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, info_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0)
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(&bdaddr);
+
+	if (dev_id < 0) {
+		fprintf(stderr, "Device is not available or not connected.\n");
+		exit(1);
+	}
+
+	if (hci_devinfo(dev_id, &di) < 0) {
+		perror("Can't get device info");
+		exit(1);
+	}
+
+	printf("Requesting information ...\n");
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't get connection info");
+		close(dd);
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		if (hci_create_connection(dd, &bdaddr,
+					htobs(di.pkt_type & ACL_PTYPE_MASK),
+					0, 0x01, &handle, 25000) < 0) {
+			perror("Can't create connection");
+			free(cr);
+			close(dd);
+			exit(1);
+		}
+		sleep(1);
+		cc = 1;
+	} else
+		handle = htobs(cr->conn_info->handle);
+
+	free(cr);
+
+	printf("\tBD Address:  %s\n", argv[0]);
+
+	comp = batocomp(&bdaddr);
+	if (comp) {
+		char oui[9];
+		ba2oui(&bdaddr, oui);
+		printf("\tOUI Company: %s (%s)\n", comp, oui);
+		free(comp);
+	}
+
+	if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+		printf("\tDevice Name: %s\n", name);
+
+	if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+		char *ver = lmp_vertostr(version.lmp_ver);
+		printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n"
+			"\tManufacturer: %s (%d)\n",
+			ver ? ver : "n/a",
+			version.lmp_ver,
+			version.lmp_subver,
+			bt_compidtostr(version.manufacturer),
+			version.manufacturer);
+		if (ver)
+			bt_free(ver);
+	}
+
+	memset(features, 0, sizeof(features));
+	hci_read_remote_features(dd, handle, features, 20000);
+
+	if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT))
+		hci_read_remote_ext_features(dd, handle, 0, &max_page,
+							features, 20000);
+
+	if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR))
+		max_page = 1;
+
+	printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+				"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		(max_page > 0) ? " page 0" : "",
+		features[0], features[1], features[2], features[3],
+		features[4], features[5], features[6], features[7]);
+
+	tmp = lmp_featurestostr(features, "\t\t", 63);
+	printf("%s\n", tmp);
+	bt_free(tmp);
+
+	for (i = 1; i <= max_page; i++) {
+		if (hci_read_remote_ext_features(dd, handle, i, NULL,
+							features, 20000) < 0)
+			continue;
+
+		printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+					"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+			features[0], features[1], features[2], features[3],
+			features[4], features[5], features[6], features[7]);
+	}
+
+	if (cc) {
+		usleep(10000);
+		hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+	}
+
+	hci_close_dev(dd);
+}
+
+/* Start periodic inquiry */
+
+static struct option spinq_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *spinq_help =
+	"Usage:\n"
+	"\tspinq\n";
+
+static void cmd_spinq(int dev_id, int argc, char **argv)
+{
+	uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+	struct hci_request rq;
+	periodic_inquiry_cp cp;
+	int opt, dd;
+
+	for_each_opt(opt, spinq_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", spinq_help);
+			return;
+		}
+	}
+	helper_arg(0, 0, &argc, &argv, spinq_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Device open failed");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(cp.lap, lap, 3);
+	cp.max_period = htobs(16);
+	cp.min_period = htobs(10);
+	cp.length     = 8;
+	cp.num_rsp    = 0;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_PERIODIC_INQUIRY;
+	rq.cparam = &cp;
+	rq.clen   = PERIODIC_INQUIRY_CP_SIZE;
+
+	if (hci_send_req(dd, &rq, 100) < 0) {
+		perror("Periodic inquiry failed");
+		exit(EXIT_FAILURE);
+	}
+
+	hci_close_dev(dd);
+}
+
+/* Exit periodic inquiry */
+
+static struct option epinq_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *epinq_help =
+	"Usage:\n"
+	"\tepinq\n";
+
+static void cmd_epinq(int dev_id, int argc, char **argv)
+{
+	int opt, dd;
+
+	for_each_opt(opt, epinq_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", epinq_help);
+			return;
+		}
+	}
+	helper_arg(0, 0, &argc, &argv, epinq_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Device open failed");
+		exit(EXIT_FAILURE);
+	}
+
+	if (hci_send_cmd(dd, OGF_LINK_CTL,
+				OCF_EXIT_PERIODIC_INQUIRY, 0, NULL) < 0) {
+		perror("Exit periodic inquiry failed");
+		exit(EXIT_FAILURE);
+	}
+
+	hci_close_dev(dd);
+}
+
+/* Send arbitrary HCI commands */
+
+static struct option cmd_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *cmd_help =
+	"Usage:\n"
+	"\tcmd <ogf> <ocf> [parameters]\n"
+	"Example:\n"
+	"\tcmd 0x03 0x0013 0x41 0x42 0x43 0x44\n";
+
+static void cmd_cmd(int dev_id, int argc, char **argv)
+{
+	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+	struct hci_filter flt;
+	hci_event_hdr *hdr;
+	int i, opt, len, dd;
+	uint16_t ocf;
+	uint8_t ogf;
+
+	for_each_opt(opt, cmd_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", cmd_help);
+			return;
+		}
+	}
+	helper_arg(2, -1, &argc, &argv, cmd_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	errno = 0;
+	ogf = strtol(argv[0], NULL, 16);
+	ocf = strtol(argv[1], NULL, 16);
+	if (errno == ERANGE || (ogf > 0x3f) || (ocf > 0x3ff)) {
+		printf("%s", cmd_help);
+		return;
+	}
+
+	for (i = 2, len = 0; i < argc && len < (int) sizeof(buf); i++, len++)
+		*ptr++ = (uint8_t) strtol(argv[i], NULL, 16);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Device open failed");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Setup filter */
+	hci_filter_clear(&flt);
+	hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+	hci_filter_all_events(&flt);
+	if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+		perror("HCI filter setup failed");
+		exit(EXIT_FAILURE);
+	}
+
+	printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len);
+	hex_dump("  ", 20, buf, len); fflush(stdout);
+
+	if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) {
+		perror("Send failed");
+		exit(EXIT_FAILURE);
+	}
+
+	len = read(dd, buf, sizeof(buf));
+	if (len < 0) {
+		perror("Read failed");
+		exit(EXIT_FAILURE);
+	}
+
+	hdr = (void *)(buf + 1);
+	ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+	len -= (1 + HCI_EVENT_HDR_SIZE);
+
+	printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen);
+	hex_dump("  ", 20, ptr, len); fflush(stdout);
+
+	hci_close_dev(dd);
+}
+
+/* Display active connections */
+
+static struct option con_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *con_help =
+	"Usage:\n"
+	"\tcon\n";
+
+static void cmd_con(int dev_id, int argc, char **argv)
+{
+	int opt;
+
+	for_each_opt(opt, con_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", con_help);
+			return;
+		}
+	}
+	helper_arg(0, 0, &argc, &argv, con_help);
+
+	printf("Connections:\n");
+
+	hci_for_each_dev(HCI_UP, conn_list, dev_id);
+}
+
+/* Create connection */
+
+static struct option cc_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "role",	1, 0, 'r' },
+	{ "ptype",	1, 0, 'p' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *cc_help =
+	"Usage:\n"
+	"\tcc [--role=m|s] [--ptype=pkt_types] <bdaddr>\n"
+	"Example:\n"
+	"\tcc --ptype=dm1,dh3,dh5 01:02:03:04:05:06\n"
+	"\tcc --role=m 01:02:03:04:05:06\n";
+
+static void cmd_cc(int dev_id, int argc, char **argv)
+{
+	bdaddr_t bdaddr;
+	uint16_t handle;
+	uint8_t role;
+	unsigned int ptype;
+	int dd, opt;
+
+	role = 0x01;
+	ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
+
+	for_each_opt(opt, cc_options, NULL) {
+		switch (opt) {
+		case 'p':
+			hci_strtoptype(optarg, &ptype);
+			break;
+
+		case 'r':
+			role = optarg[0] == 'm' ? 0 : 1;
+			break;
+
+		default:
+			printf("%s", cc_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, cc_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_get_route(&bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Device is not available.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	if (hci_create_connection(dd, &bdaddr, htobs(ptype),
+				htobs(0x0000), role, &handle, 25000) < 0)
+		perror("Can't create connection");
+
+	hci_close_dev(dd);
+}
+
+/* Close connection */
+
+static struct option dc_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *dc_help =
+	"Usage:\n"
+	"\tdc <bdaddr> [reason]\n";
+
+static void cmd_dc(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint8_t reason;
+	int opt, dd;
+
+	for_each_opt(opt, dc_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", dc_help);
+			return;
+		}
+	}
+	helper_arg(1, 2, &argc, &argv, dc_help);
+
+	str2ba(argv[0], &bdaddr);
+	reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_disconnect(dd, htobs(cr->conn_info->handle),
+						reason, 10000) < 0)
+		perror("Disconnect failed");
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Role switch */
+
+static struct option sr_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *sr_help =
+	"Usage:\n"
+	"\tsr <bdaddr> <role>\n";
+
+static void cmd_sr(int dev_id, int argc, char **argv)
+{
+	bdaddr_t bdaddr;
+	uint8_t role;
+	int opt, dd;
+
+	for_each_opt(opt, sr_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", sr_help);
+			return;
+		}
+	}
+	helper_arg(2, 2, &argc, &argv, sr_help);
+
+	str2ba(argv[0], &bdaddr);
+	switch (argv[1][0]) {
+	case 'm':
+		role = 0;
+		break;
+	case 's':
+		role = 1;
+		break;
+	default:
+		role = atoi(argv[1]);
+		break;
+	}
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	if (hci_switch_role(dd, &bdaddr, role, 10000) < 0) {
+		perror("Switch role request failed");
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+/* Read RSSI */
+
+static struct option rssi_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *rssi_help =
+	"Usage:\n"
+	"\trssi <bdaddr>\n";
+
+static void cmd_rssi(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	int8_t rssi;
+	int opt, dd;
+
+	for_each_opt(opt, rssi_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", rssi_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, rssi_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_read_rssi(dd, htobs(cr->conn_info->handle), &rssi, 1000) < 0) {
+		perror("Read RSSI failed");
+		exit(1);
+	}
+
+	printf("RSSI return value: %d\n", rssi);
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Get link quality */
+
+static struct option lq_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lq_help =
+	"Usage:\n"
+	"\tlq <bdaddr>\n";
+
+static void cmd_lq(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint8_t lq;
+	int opt, dd;
+
+	for_each_opt(opt, lq_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lq_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, lq_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_read_link_quality(dd, htobs(cr->conn_info->handle), &lq, 1000) < 0) {
+		perror("HCI read_link_quality request failed");
+		exit(1);
+	}
+
+	printf("Link quality: %d\n", lq);
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Get transmit power level */
+
+static struct option tpl_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *tpl_help =
+	"Usage:\n"
+	"\ttpl <bdaddr> [type]\n";
+
+static void cmd_tpl(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint8_t type;
+	int8_t level;
+	int opt, dd;
+
+	for_each_opt(opt, tpl_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", tpl_help);
+			return;
+		}
+	}
+	helper_arg(1, 2, &argc, &argv, tpl_help);
+
+	str2ba(argv[0], &bdaddr);
+	type = (argc > 1) ? atoi(argv[1]) : 0;
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_read_transmit_power_level(dd, htobs(cr->conn_info->handle), type, &level, 1000) < 0) {
+		perror("HCI read transmit power level request failed");
+		exit(1);
+	}
+
+	printf("%s transmit power level: %d\n",
+		(type == 0) ? "Current" : "Maximum", level);
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Get AFH channel map */
+
+static struct option afh_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *afh_help =
+	"Usage:\n"
+	"\tafh <bdaddr>\n";
+
+static void cmd_afh(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint16_t handle;
+	uint8_t mode, map[10];
+	int opt, dd;
+
+	for_each_opt(opt, afh_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", afh_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, afh_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	handle = htobs(cr->conn_info->handle);
+
+	if (hci_read_afh_map(dd, handle, &mode, map, 1000) < 0) {
+		perror("HCI read AFH map request failed");
+		exit(1);
+	}
+
+	if (mode == 0x01) {
+		int i;
+		printf("AFH map: 0x");
+		for (i = 0; i < 10; i++)
+			printf("%02x", map[i]);
+		printf("\n");
+	} else
+		printf("AFH disabled\n");
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Set connection packet type */
+
+static struct option cpt_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *cpt_help =
+	"Usage:\n"
+	"\tcpt <bdaddr> <packet_types>\n";
+
+static void cmd_cpt(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	struct hci_request rq;
+	set_conn_ptype_cp cp;
+	evt_conn_ptype_changed rp;
+	bdaddr_t bdaddr;
+	unsigned int ptype;
+	int dd, opt;
+
+	for_each_opt(opt, cpt_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", cpt_help);
+			return;
+		}
+	}
+	helper_arg(2, 2, &argc, &argv, cpt_help);
+
+	str2ba(argv[0], &bdaddr);
+	hci_strtoptype(argv[1], &ptype);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	cp.handle   = htobs(cr->conn_info->handle);
+	cp.pkt_type = ptype;
+
+	memset(&rq, 0, sizeof(rq));
+	rq.ogf    = OGF_LINK_CTL;
+	rq.ocf    = OCF_SET_CONN_PTYPE;
+	rq.cparam = &cp;
+	rq.clen   = SET_CONN_PTYPE_CP_SIZE;
+	rq.rparam = &rp;
+	rq.rlen   = EVT_CONN_PTYPE_CHANGED_SIZE;
+	rq.event  = EVT_CONN_PTYPE_CHANGED;
+
+	if (hci_send_req(dd, &rq, 100) < 0) {
+		perror("Packet type change failed");
+		exit(1);
+	}
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Get/Set link policy settings */
+
+static struct option lp_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lp_help =
+	"Usage:\n"
+	"\tlp <bdaddr> [link policy]\n";
+
+static void cmd_lp(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint16_t policy;
+	int opt, dd;
+
+	for_each_opt(opt, lp_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lp_help);
+			return;
+		}
+	}
+	helper_arg(1, 2, &argc, &argv, lp_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (argc == 1) {
+		char *str;
+		if (hci_read_link_policy(dd, htobs(cr->conn_info->handle),
+							&policy, 1000) < 0) {
+			perror("HCI read_link_policy_settings request failed");
+			exit(1);
+		}
+
+		policy = btohs(policy);
+		str = hci_lptostr(policy);
+		if (str) {
+			printf("Link policy settings: %s\n", str);
+			bt_free(str);
+		} else {
+			fprintf(stderr, "Invalig settings\n");
+			exit(1);
+		}
+	} else {
+		unsigned int val;
+		if (hci_strtolp(argv[1], &val) < 0) {
+			fprintf(stderr, "Invalig arguments\n");
+			exit(1);
+		}
+		policy = val;
+
+		if (hci_write_link_policy(dd, htobs(cr->conn_info->handle),
+						htobs(policy), 1000) < 0) {
+			perror("HCI write_link_policy_settings request failed");
+			exit(1);
+		}
+	}
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Get/Set link supervision timeout */
+
+static struct option lst_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lst_help =
+	"Usage:\n"
+	"\tlst <bdaddr> [new value in slots]\n";
+
+static void cmd_lst(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint16_t timeout;
+	int opt, dd;
+
+	for_each_opt(opt, lst_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lst_help);
+			return;
+		}
+	}
+	helper_arg(1, 2, &argc, &argv, lst_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (argc == 1) {
+		if (hci_read_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+							&timeout, 1000) < 0) {
+			perror("HCI read_link_supervision_timeout request failed");
+			exit(1);
+		}
+
+		timeout = btohs(timeout);
+
+		if (timeout)
+			printf("Link supervision timeout: %u slots (%.2f msec)\n",
+				timeout, (float) timeout * 0.625);
+		else
+			printf("Link supervision timeout never expires\n");
+	} else {
+		timeout = strtol(argv[1], NULL, 10);
+
+		if (hci_write_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+							htobs(timeout), 1000) < 0) {
+			perror("HCI write_link_supervision_timeout request failed");
+			exit(1);
+		}
+	}
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Request authentication */
+
+static struct option auth_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *auth_help =
+	"Usage:\n"
+	"\tauth <bdaddr>\n";
+
+static void cmd_auth(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	int opt, dd;
+
+	for_each_opt(opt, auth_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", auth_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, auth_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+		perror("HCI authentication request failed");
+		exit(1);
+	}
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Activate encryption */
+
+static struct option enc_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *enc_help =
+	"Usage:\n"
+	"\tenc <bdaddr> [encrypt enable]\n";
+
+static void cmd_enc(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint8_t encrypt;
+	int opt, dd;
+
+	for_each_opt(opt, enc_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", enc_help);
+			return;
+		}
+	}
+	helper_arg(1, 2, &argc, &argv, enc_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	encrypt = (argc > 1) ? atoi(argv[1]) : 1;
+
+	if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) {
+		perror("HCI set encryption request failed");
+		exit(1);
+	}
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Change connection link key */
+
+static struct option key_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *key_help =
+	"Usage:\n"
+	"\tkey <bdaddr>\n";
+
+static void cmd_key(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	int opt, dd;
+
+	for_each_opt(opt, key_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", key_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, key_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_change_link_key(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+		perror("Changing link key failed");
+		exit(1);
+	}
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Read clock offset */
+
+static struct option clkoff_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *clkoff_help =
+	"Usage:\n"
+	"\tclkoff <bdaddr>\n";
+
+static void cmd_clkoff(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint16_t offset;
+	int opt, dd;
+
+	for_each_opt(opt, clkoff_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", clkoff_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, clkoff_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+	if (!cr) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	bacpy(&cr->bdaddr, &bdaddr);
+	cr->type = ACL_LINK;
+	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+		perror("Get connection info failed");
+		exit(1);
+	}
+
+	if (hci_read_clock_offset(dd, htobs(cr->conn_info->handle), &offset, 1000) < 0) {
+		perror("Reading clock offset failed");
+		exit(1);
+	}
+
+	printf("Clock offset: 0x%4.4x\n", btohs(offset));
+
+	free(cr);
+
+	hci_close_dev(dd);
+}
+
+/* Read clock */
+
+static struct option clock_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *clock_help =
+	"Usage:\n"
+	"\tclock [bdaddr] [which clock]\n";
+
+static void cmd_clock(int dev_id, int argc, char **argv)
+{
+	struct hci_conn_info_req *cr;
+	bdaddr_t bdaddr;
+	uint8_t which;
+	uint32_t handle, clock;
+	uint16_t accuracy;
+	int opt, dd;
+
+	for_each_opt(opt, clock_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", clock_help);
+			return;
+		}
+	}
+	helper_arg(0, 2, &argc, &argv, clock_help);
+
+	if (argc > 0)
+		str2ba(argv[0], &bdaddr);
+	else
+		bacpy(&bdaddr, BDADDR_ANY);
+
+	if (dev_id < 0 && !bacmp(&bdaddr, BDADDR_ANY))
+		dev_id = hci_get_route(NULL);
+
+	if (dev_id < 0) {
+		dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+		if (dev_id < 0) {
+			fprintf(stderr, "Not connected.\n");
+			exit(1);
+		}
+	}
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		exit(1);
+	}
+
+	if (bacmp(&bdaddr, BDADDR_ANY)) {
+		cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+		if (!cr) {
+			perror("Can't allocate memory");
+			exit(1);
+		}
+
+		bacpy(&cr->bdaddr, &bdaddr);
+		cr->type = ACL_LINK;
+		if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+			perror("Get connection info failed");
+			free(cr);
+			exit(1);
+		}
+
+		handle = htobs(cr->conn_info->handle);
+		which = (argc > 1) ? atoi(argv[1]) : 0x01;
+
+		free(cr);
+	} else {
+		handle = 0x00;
+		which = 0x00;
+	}
+
+	if (hci_read_clock(dd, handle, which, &clock, &accuracy, 1000) < 0) {
+		perror("Reading clock failed");
+		exit(1);
+	}
+
+	accuracy = btohs(accuracy);
+
+	printf("Clock:    0x%4.4x\n", btohl(clock));
+	printf("Accuracy: %.2f msec\n", (float) accuracy * 0.3125);
+
+	hci_close_dev(dd);
+}
+
+static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
+{
+	size_t offset;
+
+	if (!flags || !data)
+		return -EINVAL;
+
+	offset = 0;
+	while (offset < size) {
+		uint8_t len = data[offset];
+		uint8_t type;
+
+		/* Check if it is the end of the significant part */
+		if (len == 0)
+			break;
+
+		if (len + offset > size)
+			break;
+
+		type = data[offset + 1];
+
+		if (type == FLAGS_AD_TYPE) {
+			*flags = data[offset + 2];
+			return 0;
+		}
+
+		offset += 1 + len;
+	}
+
+	return -ENOENT;
+}
+
+static int check_report_filter(uint8_t procedure, le_advertising_info *info)
+{
+	uint8_t flags;
+
+	/* If no discovery procedure is set, all reports are treat as valid */
+	if (procedure == 0)
+		return 1;
+
+	/* Read flags AD type value from the advertising report if it exists */
+	if (read_flags(&flags, info->data, info->length))
+		return 0;
+
+	switch (procedure) {
+	case 'l': /* Limited Discovery Procedure */
+		if (flags & FLAGS_LIMITED_MODE_BIT)
+			return 1;
+		break;
+	case 'g': /* General Discovery Procedure */
+		if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
+			return 1;
+		break;
+	default:
+		fprintf(stderr, "Unknown discovery procedure\n");
+	}
+
+	return 0;
+}
+
+static void sigint_handler(int sig)
+{
+	signal_received = sig;
+}
+
+static void eir_parse_name(uint8_t *eir, size_t eir_len,
+						char *buf, size_t buf_len)
+{
+	size_t offset;
+
+	offset = 0;
+	while (offset < eir_len) {
+		uint8_t field_len = eir[0];
+		size_t name_len;
+
+		/* Check for the end of EIR */
+		if (field_len == 0)
+			break;
+
+		if (offset + field_len > eir_len)
+			goto failed;
+
+		switch (eir[1]) {
+		case EIR_NAME_SHORT:
+		case EIR_NAME_COMPLETE:
+			name_len = field_len - 1;
+			if (name_len > buf_len)
+				goto failed;
+
+			memcpy(buf, &eir[2], name_len);
+			return;
+		}
+
+		offset += field_len + 1;
+		eir += field_len + 1;
+	}
+
+failed:
+	snprintf(buf, buf_len, "(unknown)");
+}
+
+static int print_advertising_devices(int dd, uint8_t filter_type)
+{
+	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+	struct hci_filter nf, of;
+	struct sigaction sa;
+	socklen_t olen;
+	int len;
+
+	olen = sizeof(of);
+	if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
+		printf("Could not get socket options\n");
+		return -1;
+	}
+
+	hci_filter_clear(&nf);
+	hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+	hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+
+	if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+		printf("Could not set socket options\n");
+		return -1;
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags = SA_NOCLDSTOP;
+	sa.sa_handler = sigint_handler;
+	sigaction(SIGINT, &sa, NULL);
+
+	while (1) {
+		evt_le_meta_event *meta;
+		le_advertising_info *info;
+		char addr[18];
+
+		while ((len = read(dd, buf, sizeof(buf))) < 0) {
+			if (errno == EINTR && signal_received == SIGINT) {
+				len = 0;
+				goto done;
+			}
+
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+			goto done;
+		}
+
+		ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+		len -= (1 + HCI_EVENT_HDR_SIZE);
+
+		meta = (void *) ptr;
+
+		if (meta->subevent != 0x02)
+			goto done;
+
+		/* Ignoring multiple reports */
+		info = (le_advertising_info *) (meta->data + 1);
+		if (check_report_filter(filter_type, info)) {
+			char name[30];
+
+			memset(name, 0, sizeof(name));
+
+			ba2str(&info->bdaddr, addr);
+			eir_parse_name(info->data, info->length,
+							name, sizeof(name) - 1);
+
+			printf("%s %s\n", addr, name);
+		}
+	}
+
+done:
+	setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+
+	if (len < 0)
+		return -1;
+
+	return 0;
+}
+
+static struct option lescan_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "static",	0, 0, 's' },
+	{ "privacy",	0, 0, 'p' },
+	{ "passive",	0, 0, 'P' },
+	{ "whitelist",	0, 0, 'w' },
+	{ "discovery",	1, 0, 'd' },
+	{ "duplicates",	0, 0, 'D' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lescan_help =
+	"Usage:\n"
+	"\tlescan [--privacy] enable privacy\n"
+	"\tlescan [--passive] set scan type passive (default active)\n"
+	"\tlescan [--whitelist] scan for address in the whitelist only\n"
+	"\tlescan [--discovery=g|l] enable general or limited discovery"
+		"procedure\n"
+	"\tlescan [--duplicates] don't filter duplicates\n";
+
+static void cmd_lescan(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	uint8_t own_type = LE_PUBLIC_ADDRESS;
+	uint8_t scan_type = 0x01;
+	uint8_t filter_type = 0;
+	uint8_t filter_policy = 0x00;
+	uint16_t interval = htobs(0x0010);
+	uint16_t window = htobs(0x0010);
+	uint8_t filter_dup = 0x01;
+
+	for_each_opt(opt, lescan_options, NULL) {
+		switch (opt) {
+		case 's':
+			own_type = LE_RANDOM_ADDRESS;
+			break;
+		case 'p':
+			own_type = LE_RANDOM_ADDRESS;
+			break;
+		case 'P':
+			scan_type = 0x00; /* Passive */
+			break;
+		case 'w':
+			filter_policy = 0x01; /* Whitelist */
+			break;
+		case 'd':
+			filter_type = optarg[0];
+			if (filter_type != 'g' && filter_type != 'l') {
+				fprintf(stderr, "Unknown discovery procedure\n");
+				exit(1);
+			}
+
+			interval = htobs(0x0012);
+			window = htobs(0x0012);
+			break;
+		case 'D':
+			filter_dup = 0x00;
+			break;
+		default:
+			printf("%s", lescan_help);
+			return;
+		}
+	}
+	helper_arg(0, 1, &argc, &argv, lescan_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
+						own_type, filter_policy, 10000);
+	if (err < 0) {
+		perror("Set scan parameters failed");
+		exit(1);
+	}
+
+	err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);
+	if (err < 0) {
+		perror("Enable scan failed");
+		exit(1);
+	}
+
+	printf("LE Scan ...\n");
+
+	err = print_advertising_devices(dd, filter_type);
+	if (err < 0) {
+		perror("Could not receive advertising events");
+		exit(1);
+	}
+
+	err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);
+	if (err < 0) {
+		perror("Disable scan failed");
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+static struct option leinfo_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "static",	0, 0, 's' },
+	{ "random",	0, 0, 'r' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *leinfo_help =
+	"Usage:\n"
+	"\tleinfo [--static] [--random] <bdaddr>\n";
+
+static void cmd_leinfo(int dev_id, int argc, char **argv)
+{
+	bdaddr_t bdaddr;
+	uint16_t handle;
+	uint8_t features[8];
+	struct hci_version version;
+	uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
+	uint16_t min_interval, supervision_timeout, window;
+	uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
+	int opt, err, dd;
+
+	own_bdaddr_type = LE_PUBLIC_ADDRESS;
+	peer_bdaddr_type = LE_PUBLIC_ADDRESS;
+
+	for_each_opt(opt, leinfo_options, NULL) {
+		switch (opt) {
+		case 's':
+			own_bdaddr_type = LE_RANDOM_ADDRESS;
+			break;
+		case 'r':
+			peer_bdaddr_type = LE_RANDOM_ADDRESS;
+			break;
+		default:
+			printf("%s", leinfo_help);
+			return;
+		}
+	}
+	helper_arg(1, 1, &argc, &argv, leinfo_help);
+
+	str2ba(argv[0], &bdaddr);
+
+	printf("Requesting information ...\n");
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	interval = htobs(0x0004);
+	window = htobs(0x0004);
+	initiator_filter = 0;
+	min_interval = htobs(0x000F);
+	max_interval = htobs(0x000F);
+	latency = htobs(0x0000);
+	supervision_timeout = htobs(0x0C80);
+	min_ce_length = htobs(0x0000);
+	max_ce_length = htobs(0x0000);
+
+	err = hci_le_create_conn(dd, interval, window, initiator_filter,
+			peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
+			max_interval, latency, supervision_timeout,
+			min_ce_length, max_ce_length, &handle, 25000);
+	if (err < 0) {
+		perror("Could not create connection");
+		exit(1);
+	}
+
+	printf("\tHandle: %d (0x%04x)\n", handle, handle);
+
+	if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+		char *ver = lmp_vertostr(version.lmp_ver);
+		printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n"
+			"\tManufacturer: %s (%d)\n",
+			ver ? ver : "n/a",
+			version.lmp_ver,
+			version.lmp_subver,
+			bt_compidtostr(version.manufacturer),
+			version.manufacturer);
+		if (ver)
+			bt_free(ver);
+	}
+
+	memset(features, 0, sizeof(features));
+	hci_le_read_remote_features(dd, handle, features, 20000);
+
+	printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+				"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+		features[0], features[1], features[2], features[3],
+		features[4], features[5], features[6], features[7]);
+
+	usleep(10000);
+	hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+
+	hci_close_dev(dd);
+}
+
+static struct option lecc_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "static",	0, 0, 's' },
+	{ "random",	0, 0, 'r' },
+	{ "whitelist",	0, 0, 'w' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lecc_help =
+	"Usage:\n"
+	"\tlecc [--static] [--random] <bdaddr>\n"
+	"\tlecc --whitelist\n";
+
+static void cmd_lecc(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	bdaddr_t bdaddr;
+	uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
+	uint16_t min_interval, supervision_timeout, window, handle;
+	uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
+
+	own_bdaddr_type = LE_PUBLIC_ADDRESS;
+	peer_bdaddr_type = LE_PUBLIC_ADDRESS;
+	initiator_filter = 0; /* Use peer address */
+
+	for_each_opt(opt, lecc_options, NULL) {
+		switch (opt) {
+		case 's':
+			own_bdaddr_type = LE_RANDOM_ADDRESS;
+			break;
+		case 'r':
+			peer_bdaddr_type = LE_RANDOM_ADDRESS;
+			break;
+		case 'w':
+			initiator_filter = 0x01; /* Use white list */
+			break;
+		default:
+			printf("%s", lecc_help);
+			return;
+		}
+	}
+	helper_arg(0, 1, &argc, &argv, lecc_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	memset(&bdaddr, 0, sizeof(bdaddr_t));
+	if (argv[0])
+		str2ba(argv[0], &bdaddr);
+
+	interval = htobs(0x0004);
+	window = htobs(0x0004);
+	min_interval = htobs(0x000F);
+	max_interval = htobs(0x000F);
+	latency = htobs(0x0000);
+	supervision_timeout = htobs(0x0C80);
+	min_ce_length = htobs(0x0001);
+	max_ce_length = htobs(0x0001);
+
+	err = hci_le_create_conn(dd, interval, window, initiator_filter,
+			peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
+			max_interval, latency, supervision_timeout,
+			min_ce_length, max_ce_length, &handle, 25000);
+	if (err < 0) {
+		perror("Could not create connection");
+		exit(1);
+	}
+
+	printf("Connection handle %d\n", handle);
+
+	hci_close_dev(dd);
+}
+
+static struct option lewladd_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "random",	0, 0, 'r' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lewladd_help =
+	"Usage:\n"
+	"\tlewladd [--random] <bdaddr>\n";
+
+static void cmd_lewladd(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type = LE_PUBLIC_ADDRESS;
+
+	for_each_opt(opt, lewladd_options, NULL) {
+		switch (opt) {
+		case 'r':
+			bdaddr_type = LE_RANDOM_ADDRESS;
+			break;
+		default:
+			printf("%s", lewladd_help);
+			return;
+		}
+	}
+
+	helper_arg(1, 1, &argc, &argv, lewladd_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	str2ba(argv[0], &bdaddr);
+
+	err = hci_le_add_white_list(dd, &bdaddr, bdaddr_type, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't add to white list: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+}
+
+static struct option lewlrm_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lewlrm_help =
+	"Usage:\n"
+	"\tlewlrm <bdaddr>\n";
+
+static void cmd_lewlrm(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	bdaddr_t bdaddr;
+
+	for_each_opt(opt, lewlrm_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lewlrm_help);
+			return;
+		}
+	}
+
+	helper_arg(1, 1, &argc, &argv, lewlrm_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	str2ba(argv[0], &bdaddr);
+
+	err = hci_le_rm_white_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = errno;
+		fprintf(stderr, "Can't remove from white list: %s(%d)\n",
+							strerror(err), err);
+		exit(1);
+	}
+}
+
+static struct option lewlsz_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lewlsz_help =
+	"Usage:\n"
+	"\tlewlsz\n";
+
+static void cmd_lewlsz(int dev_id, int argc, char **argv)
+{
+	int err, dd, opt;
+	uint8_t size;
+
+	for_each_opt(opt, lewlsz_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lewlsz_help);
+			return;
+		}
+	}
+
+	helper_arg(0, 0, &argc, &argv, lewlsz_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_read_white_list_size(dd, &size, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't read white list size: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+
+	printf("White list size: %d\n", size);
+}
+
+static struct option lewlclr_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lewlclr_help =
+	"Usage:\n"
+	"\tlewlclr\n";
+
+static void cmd_lewlclr(int dev_id, int argc, char **argv)
+{
+	int err, dd, opt;
+
+	for_each_opt(opt, lewlclr_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lewlclr_help);
+			return;
+		}
+	}
+
+	helper_arg(0, 0, &argc, &argv, lewlclr_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_clear_white_list(dd, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't clear white list: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+}
+
+static struct option lerladd_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "random",	0, 0, 'r' },
+	{ "local",	1, 0, 'l' },
+	{ "peer",	1, 0, 'p' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lerladd_help =
+	"Usage:\n"
+	"\tlerladd [--local irk] [--peer irk] [--random] <bdaddr>\n";
+
+static void cmd_lerladd(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type = LE_PUBLIC_ADDRESS;
+	uint8_t local_irk[16], peer_irk[16];
+
+	memset(local_irk, 0, 16);
+	memset(peer_irk, 0, 16);
+
+	for_each_opt(opt, lerladd_options, NULL) {
+		switch (opt) {
+		case 'r':
+			bdaddr_type = LE_RANDOM_ADDRESS;
+			break;
+		case 'l':
+			str2buf(optarg, local_irk, 16);
+			break;
+		case 'p':
+			str2buf(optarg, peer_irk, 16);
+			break;
+		default:
+			printf("%s", lerladd_help);
+			return;
+		}
+	}
+
+	helper_arg(1, 1, &argc, &argv, lerladd_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	str2ba(argv[0], &bdaddr);
+
+	err = hci_le_add_resolving_list(dd, &bdaddr, bdaddr_type,
+						peer_irk, local_irk, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't add to resolving list: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+}
+
+static struct option lerlrm_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lerlrm_help =
+	"Usage:\n"
+	"\tlerlrm <bdaddr>\n";
+
+static void cmd_lerlrm(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	bdaddr_t bdaddr;
+
+	for_each_opt(opt, lerlrm_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lerlrm_help);
+			return;
+		}
+	}
+
+	helper_arg(1, 1, &argc, &argv, lerlrm_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	str2ba(argv[0], &bdaddr);
+
+	err = hci_le_rm_resolving_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = errno;
+		fprintf(stderr, "Can't remove from resolving list: %s(%d)\n",
+							strerror(err), err);
+		exit(1);
+	}
+}
+
+static struct option lerlclr_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lerlclr_help =
+	"Usage:\n"
+	"\tlerlclr\n";
+
+static void cmd_lerlclr(int dev_id, int argc, char **argv)
+{
+	int err, dd, opt;
+
+	for_each_opt(opt, lerlclr_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lerlclr_help);
+			return;
+		}
+	}
+
+	helper_arg(0, 0, &argc, &argv, lerlclr_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_clear_resolving_list(dd, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't clear resolving list: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+}
+
+static struct option lerlsz_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lerlsz_help =
+	"Usage:\n"
+	"\tlerlsz\n";
+
+static void cmd_lerlsz(int dev_id, int argc, char **argv)
+{
+	int err, dd, opt;
+	uint8_t size;
+
+	for_each_opt(opt, lerlsz_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lerlsz_help);
+			return;
+		}
+	}
+
+	helper_arg(0, 0, &argc, &argv, lerlsz_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_read_resolving_list_size(dd, &size, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't read resolving list size: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+
+	printf("Resolving list size: %d\n", size);
+}
+
+static struct option lerlon_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lerlon_help =
+	"Usage:\n"
+	"\tlerlon\n";
+
+static void cmd_lerlon(int dev_id, int argc, char **argv)
+{
+	int err, dd, opt;
+
+	for_each_opt(opt, lerlon_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lerlon_help);
+			return;
+		}
+	}
+
+	helper_arg(0, 0, &argc, &argv, lerlon_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_set_address_resolution_enable(dd, 0x01, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't set address resolution enable: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+}
+
+static struct option lerloff_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lerloff_help =
+	"Usage:\n"
+	"\tlerloff\n";
+
+static void cmd_lerloff(int dev_id, int argc, char **argv)
+{
+	int err, dd, opt;
+
+	for_each_opt(opt, lerloff_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", lerloff_help);
+			return;
+		}
+	}
+
+	helper_arg(0, 0, &argc, &argv, lerloff_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	err = hci_le_set_address_resolution_enable(dd, 0x00, 1000);
+	hci_close_dev(dd);
+
+	if (err < 0) {
+		err = -errno;
+		fprintf(stderr, "Can't set address resolution enable: %s(%d)\n",
+							strerror(-err), -err);
+		exit(1);
+	}
+}
+
+static struct option ledc_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *ledc_help =
+	"Usage:\n"
+	"\tledc <handle> [reason]\n";
+
+static void cmd_ledc(int dev_id, int argc, char **argv)
+{
+	int err, opt, dd;
+	uint16_t handle;
+	uint8_t reason;
+
+	for_each_opt(opt, ledc_options, NULL) {
+		switch (opt) {
+		default:
+			printf("%s", ledc_help);
+			return;
+		}
+	}
+	helper_arg(1, 2, &argc, &argv, ledc_help);
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("Could not open device");
+		exit(1);
+	}
+
+	handle = atoi(argv[0]);
+
+	reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+	err = hci_disconnect(dd, handle, reason, 10000);
+	if (err < 0) {
+		perror("Could not disconnect");
+		exit(1);
+	}
+
+	hci_close_dev(dd);
+}
+
+static struct option lecup_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "handle",	1, 0, 'H' },
+	{ "min",	1, 0, 'm' },
+	{ "max",	1, 0, 'M' },
+	{ "latency",	1, 0, 'l' },
+	{ "timeout",	1, 0, 't' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *lecup_help =
+	"Usage:\n"
+	"\tlecup <handle> <min> <max> <latency> <timeout>\n"
+	"\tOptions:\n"
+	"\t    --handle=<0xXXXX>  LE connection handle\n"
+	"\t    --min=<interval>   Range: 0x0006 to 0x0C80\n"
+	"\t    --max=<interval>   Range: 0x0006 to 0x0C80\n"
+	"\t    --latency=<range>  Slave latency. Range: 0x0000 to 0x03E8\n"
+	"\t    --timeout=<time>   N * 10ms. Range: 0x000A to 0x0C80\n"
+	"\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
+	"\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
+
+static void cmd_lecup(int dev_id, int argc, char **argv)
+{
+	uint16_t handle = 0, min, max, latency, timeout;
+	int opt, dd;
+	int options = 0;
+
+	/* Aleatory valid values */
+	min = 0x0C8;
+	max = 0x0960;
+	latency = 0x0007;
+	timeout = 0x0C80;
+
+	for_each_opt(opt, lecup_options, NULL) {
+		switch (opt) {
+		case 'H':
+			handle = strtoul(optarg, NULL, 0);
+			break;
+		case 'm':
+			min = strtoul(optarg, NULL, 0);
+			break;
+		case 'M':
+			max = strtoul(optarg, NULL, 0);
+			break;
+		case 'l':
+			latency = strtoul(optarg, NULL, 0);
+			break;
+		case 't':
+			timeout = strtoul(optarg, NULL, 0);
+			break;
+		default:
+			printf("%s", lecup_help);
+			return;
+		}
+
+		options = 1;
+	}
+
+	if (options == 0) {
+		helper_arg(5, 5, &argc, &argv, lecup_help);
+
+		handle = strtoul(argv[0], NULL, 0);
+		min = strtoul(argv[1], NULL, 0);
+		max = strtoul(argv[2], NULL, 0);
+		latency = strtoul(argv[3], NULL, 0);
+		timeout = strtoul(argv[4], NULL, 0);
+	}
+
+	if (handle == 0) {
+		printf("%s", lecup_help);
+		return;
+	}
+
+	if (dev_id < 0)
+		dev_id = hci_get_route(NULL);
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		fprintf(stderr, "HCI device open failed\n");
+		exit(1);
+	}
+
+	if (hci_le_conn_update(dd, htobs(handle), htobs(min), htobs(max),
+				htobs(latency), htobs(timeout), 5000) < 0) {
+		int err = -errno;
+		fprintf(stderr, "Could not change connection params: %s(%d)\n",
+							strerror(-err), -err);
+	}
+
+	hci_close_dev(dd);
+}
+
+static struct {
+	char *cmd;
+	void (*func)(int dev_id, int argc, char **argv);
+	char *doc;
+} command[] = {
+	{ "dev",      cmd_dev,     "Display local devices"                },
+	{ "inq",      cmd_inq,     "Inquire remote devices"               },
+	{ "scan",     cmd_scan,    "Scan for remote devices"              },
+	{ "name",     cmd_name,    "Get name from remote device"          },
+	{ "info",     cmd_info,    "Get information from remote device"   },
+	{ "spinq",    cmd_spinq,   "Start periodic inquiry"               },
+	{ "epinq",    cmd_epinq,   "Exit periodic inquiry"                },
+	{ "cmd",      cmd_cmd,     "Submit arbitrary HCI commands"        },
+	{ "con",      cmd_con,     "Display active connections"           },
+	{ "cc",       cmd_cc,      "Create connection to remote device"   },
+	{ "dc",       cmd_dc,      "Disconnect from remote device"        },
+	{ "sr",       cmd_sr,      "Switch master/slave role"             },
+	{ "cpt",      cmd_cpt,     "Change connection packet type"        },
+	{ "rssi",     cmd_rssi,    "Display connection RSSI"              },
+	{ "lq",       cmd_lq,      "Display link quality"                 },
+	{ "tpl",      cmd_tpl,     "Display transmit power level"         },
+	{ "afh",      cmd_afh,     "Display AFH channel map"              },
+	{ "lp",       cmd_lp,      "Set/display link policy settings"     },
+	{ "lst",      cmd_lst,     "Set/display link supervision timeout" },
+	{ "auth",     cmd_auth,    "Request authentication"               },
+	{ "enc",      cmd_enc,     "Set connection encryption"            },
+	{ "key",      cmd_key,     "Change connection link key"           },
+	{ "clkoff",   cmd_clkoff,  "Read clock offset"                    },
+	{ "clock",    cmd_clock,   "Read local or remote clock"           },
+	{ "lescan",   cmd_lescan,  "Start LE scan"                        },
+	{ "leinfo",   cmd_leinfo,  "Get LE remote information"            },
+	{ "lewladd",  cmd_lewladd, "Add device to LE White List"          },
+	{ "lewlrm",   cmd_lewlrm,  "Remove device from LE White List"     },
+	{ "lewlsz",   cmd_lewlsz,  "Read size of LE White List"           },
+	{ "lewlclr",  cmd_lewlclr, "Clear LE White List"                  },
+	{ "lerladd",  cmd_lerladd, "Add device to LE Resolving List"      },
+	{ "lerlrm",   cmd_lerlrm,  "Remove device from LE Resolving List" },
+	{ "lerlclr",  cmd_lerlclr, "Clear LE Resolving List"              },
+	{ "lerlsz",   cmd_lerlsz,  "Read size of LE Resolving List"       },
+	{ "lerlon",   cmd_lerlon,  "Enable LE Address Resolution"         },
+	{ "lerloff",  cmd_lerloff, "Disable LE Address Resolution"        },
+	{ "lecc",     cmd_lecc,    "Create a LE Connection"               },
+	{ "ledc",     cmd_ledc,    "Disconnect a LE Connection"           },
+	{ "lecup",    cmd_lecup,   "LE Connection Update"                 },
+	{ NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+	int i;
+
+	printf("hcitool - HCI Tool ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\thcitool [options] <command> [command parameters]\n");
+	printf("Options:\n"
+		"\t--help\tDisplay help\n"
+		"\t-i dev\tHCI device\n");
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-4s\t%s\n", command[i].cmd,
+		command[i].doc);
+	printf("\n"
+		"For more information on the usage of each command use:\n"
+		"\thcitool <command> --help\n" );
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "device",	1, 0, 'i' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	int opt, i, dev_id = -1;
+	bdaddr_t ba;
+
+	while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+		switch (opt) {
+		case 'i':
+			dev_id = hci_devid(optarg);
+			if (dev_id < 0) {
+				perror("Invalid device");
+				exit(1);
+			}
+			break;
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		usage();
+		exit(0);
+	}
+
+	if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
+		perror("Device is not available");
+		exit(1);
+	}
+
+	for (i = 0; command[i].cmd; i++) {
+		if (strncmp(command[i].cmd,
+				argv[0], strlen(command[i].cmd)))
+			continue;
+
+		command[i].func(dev_id, argc, argv);
+		break;
+	}
+
+	if (command[i].cmd == 0) {
+		fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
+		exit(1);
+	}
+
+	return 0;
+}
diff --git a/bluez/repo/tools/hex2hcd.c b/bluez/repo/tools/hex2hcd.c
new file mode 100644
index 0000000..943531c
--- /dev/null
+++ b/bluez/repo/tools/hex2hcd.c
@@ -0,0 +1,446 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  Intel Corporation
+ *
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+
+static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr)
+{
+	const char *ptr = line + 1;
+	char str[3];
+	size_t len;
+	uint8_t *buf;
+	uint32_t addr;
+	uint8_t sum = 0;
+	int n = 0;
+
+	if (line[0] != ':') {
+		fprintf(stderr, "Invalid record start code (%c)\n", line[0]);
+		return -EINVAL;
+	}
+
+	len = strlen(line);
+	if (len < 11) {
+		fprintf(stderr, "Record information is too short\n");
+		return -EILSEQ;
+	}
+
+	buf = malloc((len / 2) + 3);
+	if (!buf) {
+		fprintf(stderr, "Failed to allocate memory for record data\n");
+		return -ENOMEM;
+	}
+
+	while (1) {
+		str[0] = *ptr++;
+		str[1] = *ptr++;
+		str[2] = '\0';
+
+		buf[3 + n] = strtol(str, NULL, 16);
+
+		if (*ptr == '\r' || *ptr == '\n')
+			break;
+
+		sum += buf[3 + n++];
+	}
+
+	sum = 0x100 - (sum & 0xff);
+
+	if (n < 4 || buf[3] + 4 != n) {
+		fprintf(stderr, "Record length is not matching data\n");
+		free(buf);
+		return -EILSEQ;
+	}
+
+	if (buf[3 + n] != sum) {
+		fprintf(stderr, "Checksum mismatch\n");
+		free(buf);
+		return -EILSEQ;
+	}
+
+	switch (buf[6]) {
+	case 0x00:
+		addr = (*upper_addr << 16) + (buf[4] << 8) + buf[5];
+
+		buf[0] = 0x4c;
+		buf[1] = 0xfc;
+		buf[2] = n;
+
+		buf[3] = (addr & 0x000000ff);
+		buf[4] = (addr & 0x0000ff00) >> 8;
+		buf[5] = (addr & 0x00ff0000) >> 16;
+		buf[6] = (addr & 0xff000000) >> 24;
+
+		if (write(fd, buf, n + 3) < 0) {
+			perror("Failed to write data record");
+			free(buf);
+			return -errno;
+		}
+		break;
+	case 0x01:
+		buf[0] = 0x4e;
+		buf[1] = 0xfc;
+		buf[2] = 0x04;
+
+		buf[3] = 0xff;
+		buf[4] = 0xff;
+		buf[5] = 0xff;
+		buf[6] = 0xff;
+
+		if (write(fd, buf, 7) < 0) {
+			perror("Failed to write end record");
+			free(buf);
+			return -errno;
+		}
+		break;
+	case 0x04:
+		*upper_addr = (buf[7] << 8) + buf[8];
+		break;
+	default:
+		fprintf(stderr, "Unsupported record type (%02X)\n", buf[3]);
+		free(buf);
+		return -EILSEQ;
+	}
+
+	free(buf);
+
+	return len;
+}
+
+static void convert_file(const char *input_path, const char *output_path)
+{
+	uint16_t upper_addr = 0x0000;
+	size_t line_size = 1024;
+	char line_buffer[line_size];
+	char *path;
+	const char *ptr;
+	FILE *fp;
+	struct stat st;
+	off_t cur = 0;
+	int fd;
+
+	if (output_path) {
+		path = strdup(output_path);
+		if (!path) {
+			perror("Failed to allocate string");
+			return;
+		}
+	} else {
+		ptr = strrchr(input_path, '.');
+		if (ptr) {
+			path = malloc(ptr - input_path + 6);
+			if (!path) {
+				perror("Failed to allocate string");
+				return;
+			}
+			strncpy(path, input_path, ptr - input_path);
+			strcpy(path + (ptr - input_path), ".hcd");
+		} else {
+			if (asprintf(&path, "%s.hcd", input_path) < 0) {
+				perror("Failed to allocate string");
+				return;
+			}
+		}
+	}
+
+	printf("Converting %s to %s\n", input_path, path);
+
+	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+	free(path);
+
+	if (fd < 0) {
+		perror("Failed to create output file");
+		return;
+	}
+
+	if (stat(input_path, &st) < 0) {
+		fprintf(stderr, "Failed get file size\n");
+		close(fd);
+		return;
+	}
+
+	if (st.st_size == 0) {
+		fprintf(stderr, "Empty file\n");
+		close(fd);
+		return;
+	}
+
+	fp = fopen(input_path, "r");
+	if (!fp) {
+		fprintf(stderr, "Failed to open input file\n");
+		close(fd);
+		return;
+	}
+
+	while (1) {
+		char *str;
+		ssize_t len;
+
+		str = fgets(line_buffer, line_size - 1, fp);
+		if (!str)
+			break;
+
+		len = process_record(fd, str, &upper_addr);
+		if (len < 0)
+			goto done;
+
+		cur += len;
+	}
+
+	if (cur != st.st_size) {
+		fprintf(stderr, "Data length does not match file length\n");
+		goto done;
+	}
+
+done:
+	fclose(fp);
+
+	close(fd);
+}
+
+struct ver_data {
+	uint16_t num;
+	char name[20];
+	char major[4];
+	char minor[4];
+	char build[4];
+	struct ver_data *next;
+};
+
+static struct ver_data *ver_list = NULL;
+
+static void ver_parse_file(const char *pathname)
+{
+	struct ver_data *ver, *tmp, *prev;
+	char dummy1[5], dummy2[5];
+
+	if (strlen(pathname) < 7)
+		return;
+
+	if (strncmp(pathname, "BCM", 3))
+		return;
+
+	ver = malloc(sizeof(*ver));
+	if (!ver)
+		return;
+
+	memset(ver, 0, sizeof(*ver));
+
+	if (sscanf(pathname, "%[A-Z0-9]_%3c.%3c.%3c.%4c.%4c.hex",
+					ver->name, ver->major, ver->minor,
+					ver->build, dummy1, dummy2) != 6) {
+		printf("\t/* failed to parse %s */\n", pathname);
+		free(ver);
+		return;
+	}
+
+	ver->num = atoi(ver->build) + (atoi(ver->minor) << 8) +
+						(atoi(ver->major) << 13);
+
+	if (!ver_list) {
+		ver_list = ver;
+		return;
+	}
+
+	for (tmp = ver_list, prev = NULL; tmp; prev = tmp, tmp = tmp->next) {
+		if (ver->num == tmp->num) {
+			free(ver);
+			return;
+		}
+
+		if (ver->num < tmp->num) {
+			if (prev) {
+				prev->next = ver;
+				ver->next = tmp;
+			} else {
+				ver->next = ver_list;
+				ver_list = ver;
+			}
+			return;
+		}
+	}
+
+	prev->next = ver;
+}
+
+static void ver_parse_entry(const char *pathname)
+{
+	struct stat st;
+	int fd;
+
+	fd = open(pathname, O_RDONLY);
+	if (fd < 0) {
+		printf("\t/* failed to open %s */\n", pathname);
+		return;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		printf("\t/* failed to stat %s */\n", pathname);
+		goto done;
+	}
+
+	if (S_ISREG(st.st_mode)) {
+		ver_parse_file(basename(pathname));
+		goto done;
+	}
+
+	if (S_ISDIR(st.st_mode)) {
+		DIR *dir;
+
+		dir = fdopendir(fd);
+		if (!dir)
+			goto done;
+
+		while (1) {
+			struct dirent *d;
+
+			d = readdir(dir);
+			if (!d)
+				break;
+
+			if (d->d_type == DT_REG)
+				ver_parse_file(d->d_name);
+		}
+
+		closedir(dir);
+	}
+
+done:
+	close(fd);
+}
+
+static void ver_print_table(int argc, char *argv[])
+{
+	struct ver_data *ver;
+
+	printf("static const struct {\n");
+	printf("\tuint16_t ver;\n");
+	printf("\tconst char *str\n");
+	printf("} table[] = {\n");
+
+	if (argc > 0) {
+		int i;
+
+		for (i = 0; i < argc; i++)
+			ver_parse_entry(argv[i]);
+	} else
+		ver_parse_entry(".");
+
+	for (ver = ver_list; ver; ) {
+		struct ver_data *tmp = ver;
+
+		printf("\t{ 0x%4.4x, \"%s\"\t},\t/* %s.%s.%s */\n",
+					ver->num, ver->name,
+					ver->major, ver->minor, ver->build);
+
+		ver = ver->next;
+		free(tmp);
+	}
+
+	printf("	{ }\n");
+	printf("};\n");
+}
+
+static void usage(void)
+{
+	printf("Broadcom Bluetooth firmware converter\n"
+		"Usage:\n");
+	printf("\thex2hcd [options] <file>\n");
+	printf("Options:\n"
+		"\t-o, --output <file>    Provide firmware output file\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "table",   no_argument,       NULL, 'T' },
+	{ "output",  required_argument, NULL, 'o' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *output_path = NULL;
+	bool print_table = false;
+	int i;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "To:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'T':
+			print_table = true;
+			break;
+		case 'o':
+			output_path = optarg;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (print_table) {
+		ver_print_table(argc - optind, argv + optind);
+		return EXIT_SUCCESS;
+	}
+
+	if (argc - optind < 1) {
+		fprintf(stderr, "No input firmware files provided\n");
+		return EXIT_FAILURE;
+	}
+
+	if (output_path && argc - optind > 1) {
+		fprintf(stderr, "Only single input firmware supported\n");
+		return EXIT_FAILURE;
+	}
+
+	for (i = optind; i < argc; i++)
+		convert_file(argv[i], output_path);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/hid2hci.1 b/bluez/repo/tools/hid2hci.1
new file mode 100644
index 0000000..c6876a3
--- /dev/null
+++ b/bluez/repo/tools/hid2hci.1
@@ -0,0 +1,46 @@
+.\"
+.\"	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH HID2HCI 1 "MAY 15, 2009" "" ""
+
+.SH NAME
+hid2hci \- Bluetooth HID to HCI mode switching utility
+.SH SYNOPSIS
+.BR "hid2hci
+[
+.I options
+]
+.SH DESCRIPTION
+.B hid2hci
+is used to set up switch supported Bluetooth devices into the HCI
+mode and back.
+.SH OPTIONS
+.TP
+.B --mode= [hid, hci]
+Sets the mode to switch the device into
+.TP
+.B --method= [csr, csr2, logitech-hid, dell]
+Which vendor method to use for switching the device.
+.TP
+.B --devpath=
+Specifies the device path in /sys
+.TP
+.B --help
+Gives a list of possible options.
+.TP
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/bluez/repo/tools/hid2hci.c b/bluez/repo/tools/hid2hci.c
new file mode 100644
index 0000000..8f060f2
--- /dev/null
+++ b/bluez/repo/tools/hid2hci.c
@@ -0,0 +1,446 @@
+/*
+ * hid2hci : switch the radio on devices that support
+ *           it from HID to HCI and back
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Mario Limonciello <mario_limonciello@dell.com>
+ *  Copyright (C) 2009-2011  Kay Sievers <kay.sievers@vrfy.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 <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <getopt.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/hiddev.h>
+
+#include "libudev.h"
+
+#define USB_REQ_SET_CONFIGURATION	0x09
+
+#define USB_TYPE_CLASS			(0x01 << 5)
+#define USB_TYPE_VENDOR			(0x02 << 5)
+
+#define USB_RECIP_DEVICE		0x00
+#define USB_RECIP_INTERFACE		0x01
+
+#define USB_ENDPOINT_OUT		0x00
+
+struct usbfs_ctrltransfer {
+	uint8_t  bmRequestType;
+	uint8_t  bRequest;
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+	uint32_t timeout;	/* in milliseconds */
+	const void *data;	/* pointer to data */
+};
+
+
+#define USBFS_DISCONNECT_IF_DRIVER	0x01
+#define USBFS_DISCONNECT_EXCEPT_DRIVER	0x02
+
+struct usbfs_disconnect{
+	unsigned int interface;
+	unsigned int flags;
+	char driver[256];
+};
+
+#define USBFS_IOCTL_CONTROL	_IOWR('U', 0, struct usbfs_ctrltransfer)
+#define USBFS_IOCTL_DISCONNECT	_IOR('U', 27, struct usbfs_disconnect)
+
+static int control_message(int fd, int requesttype, int request,
+					int value, int index,
+					const uint8_t *bytes, int size, int timeout)
+{
+	struct usbfs_ctrltransfer transfer;
+
+	transfer.bmRequestType = requesttype;
+	transfer.bRequest = request;
+	transfer.wValue = value;
+	transfer.wIndex = index;
+	transfer.wLength = size,
+	transfer.timeout = timeout;
+	transfer.data = bytes;
+
+	if (ioctl(fd, USBFS_IOCTL_CONTROL, &transfer) < 0) {
+		fprintf(stderr, "Control transfer failed: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+enum mode {
+	HCI = 0,
+	HID = 1,
+};
+
+static int usb_switch_csr(int fd, enum mode mode)
+{
+	int err;
+
+	err = control_message(fd, USB_ENDPOINT_OUT | USB_TYPE_VENDOR  |
+							USB_RECIP_DEVICE,
+						0, mode, 0, NULL, 0, 10000);
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else if (errno == ETIMEDOUT)
+		err = 0;
+
+	return err;
+}
+
+static int usb_switch_csr2(int fd, enum mode mode)
+{
+	int err = 0;
+	struct usbfs_disconnect disconnect;
+	const uint8_t report[] = {
+		0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+	switch (mode) {
+	case HCI:
+		/* send report as is */
+		disconnect.interface = 0;
+		disconnect.flags = USBFS_DISCONNECT_EXCEPT_DRIVER;
+		strcpy(disconnect.driver, "usbfs");
+
+		if (ioctl(fd, USBFS_IOCTL_DISCONNECT, &disconnect) < 0) {
+			fprintf(stderr, "Can't claim interface: %s (%d)\n",
+				strerror(errno), errno);
+			return -1;
+		}
+
+		/* Set_report request with
+		 * report id: 0x01, report type: feature (0x03)
+		 * on interface 0
+		 */
+		err = control_message(fd,
+				      USB_ENDPOINT_OUT | USB_TYPE_CLASS |
+				      USB_RECIP_INTERFACE,
+				      USB_REQ_SET_CONFIGURATION,
+				      0x01 | (0x03 << 8),
+				      0, report, sizeof(report), 5000);
+		/* unable to detect whether the previous state
+		 * already was HCI (EALREADY)
+		 */
+		break;
+	case HID:
+		/* currently unknown how to switch to HID */
+		fprintf(stderr,
+			"csr2: Switching to hid mode is not implemented\n");
+		err = -1;
+		break;
+	}
+
+	return err;
+}
+
+static int hid_logitech_send_report(int fd, const char *buf, size_t size)
+{
+	struct hiddev_report_info rinfo;
+	struct hiddev_usage_ref uref;
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < size; i++) {
+		memset(&uref, 0, sizeof(uref));
+		uref.report_type = HID_REPORT_TYPE_OUTPUT;
+		uref.report_id   = 0x10;
+		uref.field_index = 0;
+		uref.usage_index = i;
+		uref.usage_code  = 0xff000001;
+		uref.value       = buf[i] & 0x000000ff;
+		err = ioctl(fd, HIDIOCSUSAGE, &uref);
+		if (err < 0)
+			return err;
+	}
+
+	memset(&rinfo, 0, sizeof(rinfo));
+	rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+	rinfo.report_id   = 0x10;
+	rinfo.num_fields  = 1;
+	err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+	return err;
+}
+
+static int hid_switch_logitech(const char *filename)
+{
+	char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
+	char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
+	char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
+	int fd;
+	int err = -1;
+
+	fd = open(filename, O_RDWR);
+	if (fd < 0)
+		return err;
+
+	err = ioctl(fd, HIDIOCINITREPORT, 0);
+	if (err < 0)
+		goto out;
+
+	err = hid_logitech_send_report(fd, rep1, sizeof(rep1));
+	if (err < 0)
+		goto out;
+
+	err = hid_logitech_send_report(fd, rep2, sizeof(rep2));
+	if (err < 0)
+		goto out;
+
+	err = hid_logitech_send_report(fd, rep3, sizeof(rep3));
+out:
+	close(fd);
+	return err;
+}
+
+static int usb_switch_dell(int fd, enum mode mode)
+{
+	uint8_t report[] = { 0x7f, 0x00, 0x00, 0x00 };
+	struct usbfs_disconnect disconnect;
+	int err;
+
+	switch (mode) {
+	case HCI:
+		report[1] = 0x13;
+		break;
+	case HID:
+		report[1] = 0x14;
+		break;
+	}
+
+	disconnect.interface = 0;
+	disconnect.flags = USBFS_DISCONNECT_EXCEPT_DRIVER;
+	strcpy(disconnect.driver, "usbfs");
+
+	if (ioctl(fd, USBFS_IOCTL_DISCONNECT, &disconnect) < 0) {
+		fprintf(stderr, "Can't claim interface: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	err = control_message(fd, USB_ENDPOINT_OUT | USB_TYPE_CLASS |
+							USB_RECIP_INTERFACE,
+				USB_REQ_SET_CONFIGURATION,
+				0x7f | (0x03 << 8), 0,
+				report, sizeof(report), 5000);
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	return err;
+}
+
+static int find_device(struct udev_device *udev_dev)
+{
+	char path[PATH_MAX];
+	const char *busnum_str, *devnum_str;
+	int busnum, devnum;
+	int fd;
+
+	busnum_str = udev_device_get_sysattr_value(udev_dev, "busnum");
+	if (!busnum_str)
+		return -1;
+	busnum = strtol(busnum_str, NULL, 10);
+
+	devnum_str = udev_device_get_sysattr_value(udev_dev, "devnum");
+	if (!devnum_str)
+		return -1;
+	devnum = strtol(devnum_str, NULL, 10);
+
+	snprintf(path, sizeof(path), "/dev/bus/usb/%03d/%03d", busnum, devnum);
+
+	fd = open(path, O_RDWR, O_CLOEXEC);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open device: %s (%d)\n",
+						strerror(errno), errno);
+		return -1;
+	}
+
+	return fd;
+}
+
+static void usage(const char *error)
+{
+	if (error)
+		fprintf(stderr,"\n%s\n", error);
+	else
+		printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
+
+	printf("Usage: hid2hci [options]\n"
+		"  --mode=       mode to switch to [hid|hci] (default hci)\n"
+		"  --devpath=    sys device path\n"
+		"  --method=     method to use to switch [csr|csr2|logitech-hid|dell]\n"
+		"  --help\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+	static const struct option options[] = {
+		{ "help", no_argument, NULL, 'h' },
+		{ "mode", required_argument, NULL, 'm' },
+		{ "devpath", required_argument, NULL, 'p' },
+		{ "method", required_argument, NULL, 'M' },
+		{ }
+	};
+	enum method {
+		METHOD_UNDEF,
+		METHOD_CSR,
+		METHOD_LOGITECH_HID,
+		METHOD_DELL,
+	} method = METHOD_UNDEF;
+	struct udev *udev;
+	struct udev_device *udev_dev = NULL;
+	char syspath[PATH_MAX];
+	int (*usb_switch)(int fd, enum mode mode) = NULL;
+	enum mode mode = HCI;
+	const char *devpath = NULL;
+	int err = -1;
+	int rc = 1;
+
+	for (;;) {
+		int option;
+
+		option = getopt_long(argc, argv, "m:p:M:h", options, NULL);
+		if (option == -1)
+			break;
+
+		switch (option) {
+		case 'm':
+			if (!strcmp(optarg, "hid")) {
+				mode = HID;
+			} else if (!strcmp(optarg, "hci")) {
+				mode = HCI;
+			} else {
+				usage("error: undefined radio mode\n");
+				exit(1);
+			}
+			break;
+		case 'p':
+			devpath = optarg;
+			break;
+		case 'M':
+			if (!strcmp(optarg, "csr")) {
+				method = METHOD_CSR;
+				usb_switch = usb_switch_csr;
+			} else if (!strcmp(optarg, "csr2")) {
+				method = METHOD_CSR;
+				usb_switch = usb_switch_csr2;
+			} else if (!strcmp(optarg, "logitech-hid")) {
+				method = METHOD_LOGITECH_HID;
+			} else if (!strcmp(optarg, "dell")) {
+				method = METHOD_DELL;
+				usb_switch = usb_switch_dell;
+			} else {
+				usage("error: undefined switching method\n");
+				exit(1);
+			}
+			break;
+		case 'h':
+			usage(NULL);
+		}
+	}
+
+	if (!devpath || method == METHOD_UNDEF) {
+		usage("error: --devpath= and --method= must be defined\n");
+		exit(1);
+	}
+
+	udev = udev_new();
+	if (udev == NULL)
+		goto exit;
+
+	snprintf(syspath, sizeof(syspath), "/sys/%s", devpath);
+	udev_dev = udev_device_new_from_syspath(udev, syspath);
+	if (udev_dev == NULL) {
+		fprintf(stderr, "error: could not find '%s'\n", devpath);
+		goto exit;
+	}
+
+	switch (method) {
+	case METHOD_CSR:
+	case METHOD_DELL: {
+		struct udev_device *dev;
+		int handle;
+		const char *type;
+
+		/* get the parent usb_device if needed */
+		dev = udev_dev;
+		type = udev_device_get_devtype(dev);
+		if (type == NULL || strcmp(type, "usb_device") != 0) {
+			dev = udev_device_get_parent_with_subsystem_devtype(dev,
+							"usb", "usb_device");
+			if (dev == NULL) {
+				fprintf(stderr, "error: could not find usb_device for '%s'\n", devpath);
+				goto exit;
+			}
+		}
+
+		handle = find_device(dev);
+		if (handle < 0) {
+			fprintf(stderr, "error: unable to handle '%s'\n",
+				udev_device_get_syspath(dev));
+			goto exit;
+		}
+		err = usb_switch(handle, mode);
+		close(handle);
+		break;
+	}
+	case METHOD_LOGITECH_HID: {
+		const char *device;
+
+		device = udev_device_get_devnode(udev_dev);
+		if (device == NULL) {
+			fprintf(stderr, "error: could not find hiddev device node\n");
+			goto exit;
+		}
+		err = hid_switch_logitech(device);
+		break;
+	}
+	case METHOD_UNDEF:
+	default:
+		break;
+	}
+
+	if (err < 0)
+		fprintf(stderr, "error: switching device '%s' failed.\n",
+			udev_device_get_syspath(udev_dev));
+exit:
+	udev_device_unref(udev_dev);
+	udev_unref(udev);
+	return rc;
+}
diff --git a/bluez/repo/tools/hid2hci.rules b/bluez/repo/tools/hid2hci.rules
new file mode 100644
index 0000000..db6bb03
--- /dev/null
+++ b/bluez/repo/tools/hid2hci.rules
@@ -0,0 +1,28 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="hid2hci_end"
+SUBSYSTEM!="usb*", GOTO="hid2hci_end"
+
+# Variety of Dell Bluetooth devices - match on a mouse device that is
+# self powered and where a HID report needs to be sent to switch modes
+# Known supported devices: 413c:8154, 413c:8158, 413c:8162
+ATTR{bInterfaceClass}=="03", ATTR{bInterfaceSubClass}=="01", ATTR{bInterfaceProtocol}=="02", \
+  ATTRS{bDeviceClass}=="00", ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", \
+  RUN+="hid2hci --method=dell --devpath=%p", ENV{HID2HCI_SWITCH}="1"
+
+# Logitech devices
+KERNEL=="hiddev*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[345abce]|c71[34bc]", \
+  RUN+="hid2hci --method=logitech-hid --devpath=%p"
+
+ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"
+
+# When a Dell device recovers from S3, the mouse child needs to be repoked
+# Unfortunately the only event seen is the BT device disappearing, so the mouse
+# device needs to be chased down on the USB bus.
+ATTR{bDeviceClass}=="e0", ATTR{bDeviceSubClass}=="01", ATTR{bDeviceProtocol}=="01", ATTR{idVendor}=="413c", \
+  ENV{REMOVE_CMD}="/sbin/udevadm trigger --action=change --subsystem-match=usb --property-match=HID2HCI_SWITCH=1"
+
+# CSR devices
+ATTR{idVendor}=="0a12|0458|05ac", ATTR{idProduct}=="1000", RUN+="hid2hci --method=csr --devpath=%p"
+
+LABEL="hid2hci_end"
diff --git a/bluez/repo/tools/hwdb.c b/bluez/repo/tools/hwdb.c
new file mode 100644
index 0000000..8a42dce
--- /dev/null
+++ b/bluez/repo/tools/hwdb.c
@@ -0,0 +1,82 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-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 "lib/bluetooth.h"
+
+static const struct {
+	uint16_t vendor;
+	uint16_t product;
+	const char *str;
+} product_table[] = {
+	{ 0x0078, 0x0001, "Nike+ FuelBand"	},
+	{ 0x0097, 0x0002, "COOKOO watch"	},
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	uint16_t id;
+
+	printf("# This file is part of systemd.\n");
+	printf("#\n");
+	printf("# Data imported from:\n");
+	printf("#  http://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm\n");
+
+	for (id = 0;; id++) {
+		const char *str;
+		int i;
+
+		str = bt_compidtostr(id);
+		if (!str)
+			break;
+
+		if (!strcmp(str, "internal use"))
+			break;
+
+		if (!strcmp(str, "not assigned"))
+			continue;
+
+		printf("\n");
+		printf("bluetooth:v%04X*\n", id);
+		printf(" ID_VENDOR_FROM_DATABASE=%s\n", str);
+
+		for (i = 0; product_table[i].str; i++) {
+			if (product_table[i].vendor != id)
+				continue;
+
+			printf("\n");
+			printf("bluetooth:v%04Xp%04X*\n",
+						id, product_table[i].product);
+			printf(" ID_PRODUCT_FROM_DATABASE=%s\n",
+							product_table[i].str);
+		}
+	}
+
+	return 0;
+}
diff --git a/bluez/repo/tools/ibeacon.c b/bluez/repo/tools/ibeacon.c
new file mode 100644
index 0000000..9d48e66
--- /dev/null
+++ b/bluez/repo/tools/ibeacon.c
@@ -0,0 +1,312 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+static int urandom_fd;
+static struct bt_hci *hci_dev;
+
+static bool shutdown_timeout(void *user_data)
+{
+	mainloop_quit();
+
+	return false;
+}
+
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
+{
+	unsigned int id = PTR_TO_UINT(user_data);
+
+	timeout_remove(id);
+	mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+	uint8_t enable = 0x00;
+	unsigned int id;
+
+	bt_hci_flush(hci_dev);
+
+	id = timeout_add(5000, shutdown_timeout, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+					&enable, 1, NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+				shutdown_complete, UINT_TO_PTR(id), NULL);
+}
+
+static void set_random_address(void)
+{
+	struct bt_hci_cmd_le_set_random_address cmd;
+	ssize_t len;
+
+	len = read(urandom_fd, cmd.addr, sizeof(cmd.addr));
+	if (len < 0 || len != sizeof(cmd.addr)) {
+		fprintf(stderr, "Failed to read random data\n");
+		return;
+	}
+
+	/* Clear top most significant bits */
+	cmd.addr[5] &= 0x3f;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
+static void set_adv_parameters(void)
+{
+	struct bt_hci_cmd_le_set_adv_parameters cmd;
+
+	cmd.min_interval = cpu_to_le16(0x0800);
+	cmd.max_interval = cpu_to_le16(0x0800);
+	cmd.type = 0x03;		/* Non-connectable advertising */
+	cmd.own_addr_type = 0x01;	/* Use random address */
+	cmd.direct_addr_type = 0x00;
+	memset(cmd.direct_addr, 0, 6);
+	cmd.channel_map = 0x07;
+	cmd.filter_policy = 0x00;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+					&cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
+static void set_adv_enable(void)
+{
+	uint8_t enable = 0x01;
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+					&enable, 1, NULL, NULL, NULL);
+}
+
+static void adv_data_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	uint8_t status = *((uint8_t *) data);
+
+	if (status) {
+		fprintf(stderr, "Failed to set advertising data\n");
+		shutdown_device();
+		return;
+	}
+
+	set_random_address();
+	set_adv_parameters();
+	set_adv_enable();
+}
+
+static void adv_tx_power_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
+	struct bt_hci_cmd_le_set_adv_data cmd;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read advertising TX power\n");
+		shutdown_device();
+		return;
+	}
+
+	cmd.data[0] = 0x02;		/* Field length */
+	cmd.data[1] = 0x01;		/* Flags */
+	cmd.data[2] = 0x02;		/* LE General Discoverable Mode */
+	cmd.data[2] |= 0x04;		/* BR/EDR Not Supported */
+
+	cmd.data[3] = 0x1a;		/* Field length */
+	cmd.data[4] = 0xff;		/* Vendor field */
+	cmd.data[5] = 0x4c;		/* Apple (76) - LSB */
+	cmd.data[6] = 0x00;		/* Apple (76) - MSB */
+	cmd.data[7] = 0x02;		/* iBeacon type */
+	cmd.data[8] = 0x15;		/* Length */
+	memset(cmd.data + 9, 0, 16);	/* UUID */
+	cmd.data[25] = 0x00;		/* Major - LSB */
+	cmd.data[26] = 0x00;		/* Major - MSB */
+	cmd.data[27] = 0x00;		/* Minor - LSB */
+	cmd.data[28] = 0x00;		/* Minor - MSB */
+	cmd.data[29] = 0xc5;		/* TX power level */
+
+	cmd.data[30] = 0x00;		/* Field terminator */
+
+	cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3];
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
+					adv_data_callback, NULL, NULL);
+}
+
+static void local_features_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_features *rsp = data;
+
+	if (rsp->status) {
+		fprintf(stderr, "Failed to read local features\n");
+		shutdown_device();
+		return;
+	}
+
+	if (!(rsp->features[4] & 0x40)) {
+		fprintf(stderr, "Controller without Low Energy support\n");
+		shutdown_device();
+		return;
+	}
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
+					adv_tx_power_callback, NULL, NULL);
+}
+
+static void start_ibeacon(void)
+{
+	bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+	bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+					local_features_callback, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	static bool terminated = false;
+
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		if (!terminated) {
+			shutdown_device();
+			terminated = true;
+		}
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("ibeacon - Low Energy iBeacon testing tool\n"
+		"Usage:\n");
+	printf("\tibeacon [options]\n");
+	printf("Options:\n"
+		"\t-i, --index <num>      Use specified controller\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "index",   required_argument, NULL, 'i' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	uint16_t index = 0;
+	const char *str;
+	sigset_t mask;
+	int exit_status;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				usage();
+				return EXIT_FAILURE;
+			}
+			index = atoi(str);
+			break;
+		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;
+	}
+
+	urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (urandom_fd < 0) {
+		fprintf(stderr, "Failed to open /dev/urandom device\n");
+		return EXIT_FAILURE;
+	}
+
+	mainloop_init();
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+	printf("Low Energy iBeacon utility ver %s\n", VERSION);
+
+	hci_dev = bt_hci_new_user_channel(index);
+	if (!hci_dev) {
+		fprintf(stderr, "Failed to open HCI user channel\n");
+		exit_status = EXIT_FAILURE;
+		goto done;
+	}
+
+	start_ibeacon();
+
+	exit_status = mainloop_run();
+
+	bt_hci_unref(hci_dev);
+
+done:
+	close(urandom_fd);
+
+	return exit_status;
+}
diff --git a/bluez/repo/tools/l2cap-tester.c b/bluez/repo/tools/l2cap-tester.c
new file mode 100644
index 0000000..945f82c
--- /dev/null
+++ b/bluez/repo/tools/l2cap-tester.c
@@ -0,0 +1,1931 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+
+struct test_data {
+	const void *test_data;
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	unsigned int io_id;
+	uint16_t handle;
+	uint16_t scid;
+	uint16_t dcid;
+	int sk;
+	int sk2;
+};
+
+struct l2cap_data {
+	uint16_t client_psm;
+	uint16_t server_psm;
+	uint16_t cid;
+	int expect_err;
+
+	uint8_t send_cmd_code;
+	const void *send_cmd;
+	uint16_t send_cmd_len;
+	uint8_t expect_cmd_code;
+	const void *expect_cmd;
+	uint16_t expect_cmd_len;
+
+	uint16_t data_len;
+	const void *read_data;
+	const void *write_data;
+
+	bool enable_ssp;
+	uint8_t client_io_cap;
+	int sec_level;
+	bool reject_ssp;
+
+	bool expect_pin;
+	uint8_t pin_len;
+	const void *pin;
+	uint8_t client_pin_len;
+	const void *client_pin;
+
+	bool addr_type_avail;
+	uint8_t addr_type;
+
+	uint8_t *client_bdaddr;
+	bool server_not_advertising;
+	bool direct_advertising;
+	bool close_one_socket;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (data->io_id > 0) {
+		g_source_remove(data->io_id);
+		data->io_id = 0;
+	}
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	free(data);
+}
+
+#define test_l2cap_bredr(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDR; \
+		user->io_id = 0; \
+		user->test_data = data; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+#define test_l2cap_le(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_LE; \
+		user->io_id = 0; \
+		user->test_data = data; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */
+
+static const struct l2cap_data client_connect_success_test = {
+	.client_psm = 0x1001,
+	.server_psm = 0x1001,
+};
+
+static const struct l2cap_data client_connect_ssp_success_test_1 = {
+	.client_psm = 0x1001,
+	.server_psm = 0x1001,
+	.enable_ssp = true,
+};
+
+static const struct l2cap_data client_connect_ssp_success_test_2 = {
+	.client_psm = 0x1001,
+	.server_psm = 0x1001,
+	.enable_ssp = true,
+	.sec_level  = BT_SECURITY_HIGH,
+	.client_io_cap = 0x04,
+};
+
+static const struct l2cap_data client_connect_pin_success_test = {
+	.client_psm = 0x1001,
+	.server_psm = 0x1001,
+	.sec_level  = BT_SECURITY_MEDIUM,
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+};
+
+static uint8_t l2_data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct l2cap_data client_connect_read_success_test = {
+	.client_psm = 0x1001,
+	.server_psm = 0x1001,
+	.read_data = l2_data,
+	.data_len = sizeof(l2_data),
+};
+
+static const struct l2cap_data client_connect_write_success_test = {
+	.client_psm = 0x1001,
+	.server_psm = 0x1001,
+	.write_data = l2_data,
+	.data_len = sizeof(l2_data),
+};
+
+static const struct l2cap_data client_connect_nval_psm_test_1 = {
+	.client_psm = 0x1001,
+	.expect_err = ECONNREFUSED,
+};
+
+static const struct l2cap_data client_connect_nval_psm_test_2 = {
+	.client_psm = 0x0001,
+	.expect_err = ECONNREFUSED,
+};
+
+static const struct l2cap_data client_connect_nval_psm_test_3 = {
+	.client_psm = 0x0001,
+	.expect_err = ECONNREFUSED,
+	.enable_ssp = true,
+};
+
+static const uint8_t l2cap_connect_req[] = { 0x01, 0x10, 0x41, 0x00 };
+
+static const struct l2cap_data l2cap_server_success_test = {
+	.server_psm = 0x1001,
+	.send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+	.send_cmd = l2cap_connect_req,
+	.send_cmd_len = sizeof(l2cap_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+};
+
+static const struct l2cap_data l2cap_server_read_success_test = {
+	.server_psm = 0x1001,
+	.send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+	.send_cmd = l2cap_connect_req,
+	.send_cmd_len = sizeof(l2cap_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+	.read_data = l2_data,
+	.data_len = sizeof(l2_data),
+};
+
+static const struct l2cap_data l2cap_server_write_success_test = {
+	.server_psm = 0x1001,
+	.send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+	.send_cmd = l2cap_connect_req,
+	.send_cmd_len = sizeof(l2cap_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+	.write_data = l2_data,
+	.data_len = sizeof(l2_data),
+};
+
+static const uint8_t l2cap_sec_block_rsp[] = {	0x00, 0x00,	/* dcid */
+						0x41, 0x00,	/* scid */
+						0x03, 0x00,	/* Sec Block */
+						0x00, 0x00	/* status */
+					};
+
+static const struct l2cap_data l2cap_server_sec_block_test = {
+	.server_psm = 0x1001,
+	.send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+	.send_cmd = l2cap_connect_req,
+	.send_cmd_len = sizeof(l2cap_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+	.expect_cmd = l2cap_sec_block_rsp,
+	.expect_cmd_len = sizeof(l2cap_sec_block_rsp),
+	.enable_ssp = true,
+};
+
+static const uint8_t l2cap_nval_psm_rsp[] = {	0x00, 0x00,	/* dcid */
+						0x41, 0x00,	/* scid */
+						0x02, 0x00,	/* nval PSM */
+						0x00, 0x00	/* status */
+					};
+
+static const struct l2cap_data l2cap_server_nval_psm_test = {
+	.send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+	.send_cmd = l2cap_connect_req,
+	.send_cmd_len = sizeof(l2cap_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+	.expect_cmd = l2cap_nval_psm_rsp,
+	.expect_cmd_len = sizeof(l2cap_nval_psm_rsp),
+};
+
+static const uint8_t l2cap_nval_conn_req[] = { 0x00 };
+static const uint8_t l2cap_nval_pdu_rsp[] = { 0x00, 0x00 };
+
+static const struct l2cap_data l2cap_server_nval_pdu_test1 = {
+	.send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+	.send_cmd = l2cap_nval_conn_req,
+	.send_cmd_len = sizeof(l2cap_nval_conn_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT,
+	.expect_cmd = l2cap_nval_pdu_rsp,
+	.expect_cmd_len = sizeof(l2cap_nval_pdu_rsp),
+};
+
+static const uint8_t l2cap_nval_dc_req[] = { 0x12, 0x34, 0x56, 0x78 };
+static const uint8_t l2cap_nval_cid_rsp[] = { 0x02, 0x00,
+						0x12, 0x34, 0x56, 0x78 };
+
+static const struct l2cap_data l2cap_server_nval_cid_test1 = {
+	.send_cmd_code = BT_L2CAP_PDU_DISCONN_REQ,
+	.send_cmd = l2cap_nval_dc_req,
+	.send_cmd_len = sizeof(l2cap_nval_dc_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT,
+	.expect_cmd = l2cap_nval_cid_rsp,
+	.expect_cmd_len = sizeof(l2cap_nval_cid_rsp),
+};
+
+static const uint8_t l2cap_nval_cfg_req[] = { 0x12, 0x34, 0x00, 0x00 };
+static const uint8_t l2cap_nval_cfg_rsp[] = { 0x02, 0x00,
+						0x12, 0x34, 0x00, 0x00 };
+
+static const struct l2cap_data l2cap_server_nval_cid_test2 = {
+	.send_cmd_code = BT_L2CAP_PDU_CONFIG_REQ,
+	.send_cmd = l2cap_nval_cfg_req,
+	.send_cmd_len = sizeof(l2cap_nval_cfg_req),
+	.expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT,
+	.expect_cmd = l2cap_nval_cfg_rsp,
+	.expect_cmd_len = sizeof(l2cap_nval_cfg_rsp),
+};
+
+static const struct l2cap_data le_client_connect_success_test_1 = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+};
+
+static const struct l2cap_data le_client_connect_adv_success_test_1 = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.direct_advertising = true,
+};
+
+static const struct l2cap_data le_client_connect_success_test_2 = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.sec_level  = BT_SECURITY_MEDIUM,
+};
+
+static const uint8_t cmd_reject_rsp[] = { 0x01, 0x01, 0x02, 0x00, 0x00, 0x00 };
+
+static const struct l2cap_data le_client_connect_reject_test_1 = {
+	.client_psm = 0x0080,
+	.send_cmd = cmd_reject_rsp,
+	.send_cmd_len = sizeof(cmd_reject_rsp),
+	.expect_err = ECONNREFUSED,
+};
+
+static const struct l2cap_data le_client_connect_reject_test_2 = {
+	.client_psm = 0x0080,
+	.addr_type_avail = true,
+	.addr_type = BDADDR_LE_PUBLIC,
+};
+
+static uint8_t nonexisting_bdaddr[] = {0x00, 0xAA, 0x01, 0x02, 0x03, 0x00};
+static const struct l2cap_data le_client_close_socket_test_1 = {
+	.client_psm = 0x0080,
+	.client_bdaddr = nonexisting_bdaddr,
+};
+
+static const struct l2cap_data le_client_close_socket_test_2 = {
+	.client_psm = 0x0080,
+	.server_not_advertising = true,
+};
+
+static const struct l2cap_data le_client_two_sockets_same_client = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.server_not_advertising = true,
+};
+
+static const struct l2cap_data le_client_two_sockets_close_one = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.server_not_advertising = true,
+	.close_one_socket = true,
+};
+
+static const struct l2cap_data le_client_connect_nval_psm_test = {
+	.client_psm = 0x0080,
+	.expect_err = ECONNREFUSED,
+};
+
+static const uint8_t le_connect_req[] = {	0x80, 0x00, /* PSM */
+						0x41, 0x00, /* SCID */
+						0x20, 0x00, /* MTU */
+						0x20, 0x00, /* MPS */
+						0x05, 0x00, /* Credits */
+};
+
+static const uint8_t le_connect_rsp[] = {	0x40, 0x00, /* DCID */
+						0xa0, 0x02, /* MTU */
+						0xe6, 0x00, /* MPS */
+						0x0a, 0x00, /* Credits */
+						0x00, 0x00, /* Result */
+};
+
+static const struct l2cap_data le_server_success_test = {
+	.server_psm = 0x0080,
+	.send_cmd_code = BT_L2CAP_PDU_LE_CONN_REQ,
+	.send_cmd = le_connect_req,
+	.send_cmd_len = sizeof(le_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_LE_CONN_RSP,
+	.expect_cmd = le_connect_rsp,
+	.expect_cmd_len = sizeof(le_connect_rsp),
+};
+
+static const uint8_t nval_le_connect_req[] = {	0x80, 0x00, /* PSM */
+						0x01, 0x00, /* SCID */
+						0x20, 0x00, /* MTU */
+						0x20, 0x00, /* MPS */
+						0x05, 0x00, /* Credits */
+};
+
+static const uint8_t nval_le_connect_rsp[] = {	0x00, 0x00, /* DCID */
+						0x00, 0x00, /* MTU */
+						0x00, 0x00, /* MPS */
+						0x00, 0x00, /* Credits */
+						0x09, 0x00, /* Result */
+};
+
+static const struct l2cap_data le_server_nval_scid_test = {
+	.server_psm = 0x0080,
+	.send_cmd_code = BT_L2CAP_PDU_LE_CONN_REQ,
+	.send_cmd = nval_le_connect_req,
+	.send_cmd_len = sizeof(nval_le_connect_req),
+	.expect_cmd_code = BT_L2CAP_PDU_LE_CONN_RSP,
+	.expect_cmd = nval_le_connect_rsp,
+	.expect_cmd_len = sizeof(nval_le_connect_rsp),
+};
+
+static const struct l2cap_data le_att_client_connect_success_test_1 = {
+	.cid = 0x0004,
+	.sec_level = BT_SECURITY_LOW,
+};
+
+static const struct l2cap_data le_att_server_success_test_1 = {
+	.cid = 0x0004,
+};
+
+static void client_cmd_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *test = data->test_data;
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		tester_print("Client set connectable status 0x%02x", status);
+		if (!status && test && test->enable_ssp) {
+			bthost_write_ssp_mode(bthost, 0x01);
+			return;
+		}
+		break;
+	case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+		tester_print("Client enable SSP status 0x%02x", status);
+		break;
+	default:
+		return;
+	}
+
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void server_cmd_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+		tester_print("Server enable SSP status 0x%02x", status);
+		break;
+	default:
+		return;
+	}
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_cmd_complete, user_data);
+
+	if (data->hciemu_type == HCIEMU_TYPE_LE) {
+		if (!l2data || !l2data->server_not_advertising)
+			bthost_set_adv_enable(bthost, 0x01);
+		else
+			tester_setup_complete();
+	} else {
+		bthost_write_scan_enable(bthost, 0x03);
+	}
+}
+
+static void setup_powered_server_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *test = data->test_data;
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	if (!test->enable_ssp) {
+		tester_setup_complete();
+		return;
+	}
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, server_cmd_complete, user_data);
+	bthost_write_ssp_mode(bthost, 0x01);
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_confirm_request *ev = param;
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *test = data->test_data;
+	struct mgmt_cp_user_confirm_reply cp;
+	uint16_t opcode;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+	if (test->reject_ssp)
+		opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+	else
+		opcode = MGMT_OP_USER_CONFIRM_REPLY;
+
+	mgmt_reply(data->mgmt, opcode, data->mgmt_index, sizeof(cp), &cp,
+							NULL, NULL, NULL);
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct test_data *data = user_data;
+	const struct l2cap_data *test = data->test_data;
+	struct mgmt_cp_pin_code_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+	if (!test->pin) {
+		mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+				data->mgmt_index, sizeof(cp.addr), &cp.addr,
+				NULL, NULL, NULL);
+		return;
+	}
+
+	cp.pin_len = test->pin_len;
+	memcpy(cp.pin_code, test->pin, test->pin_len);
+
+	mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_REPLY, data->mgmt_index,
+			sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void bthost_send_rsp(const void *buf, uint16_t len, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	struct bthost *bthost;
+
+	if (l2data->expect_cmd_len && len != l2data->expect_cmd_len) {
+		tester_test_failed();
+		return;
+	}
+
+	if (l2data->expect_cmd && memcmp(buf, l2data->expect_cmd,
+						l2data->expect_cmd_len)) {
+		tester_test_failed();
+		return;
+	}
+
+	if (!l2data->send_cmd)
+		return;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_send_cid(bthost, data->handle, data->dcid,
+				l2data->send_cmd, l2data->send_cmd_len);
+}
+
+static void send_rsp_new_conn(uint16_t handle, void *user_data)
+{
+	struct test_data *data = user_data;
+	struct bthost *bthost;
+
+	tester_print("New connection with handle 0x%04x", handle);
+
+	data->handle = handle;
+
+	if (data->hciemu_type == HCIEMU_TYPE_LE)
+		data->dcid = 0x0005;
+	else
+		data->dcid = 0x0001;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_add_cid_hook(bthost, data->handle, data->dcid,
+						bthost_send_rsp, NULL);
+}
+
+static void setup_powered_common(void)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *test = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	unsigned char param[] = { 0x01 };
+
+	mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST,
+			data->mgmt_index, user_confirm_request_callback,
+			NULL, NULL);
+
+	if (test && (test->pin || test->expect_pin))
+		mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST,
+				data->mgmt_index, pin_code_request_callback,
+				data, NULL);
+
+	if (test && test->client_io_cap)
+		bthost_set_io_capability(bthost, test->client_io_cap);
+
+	if (test && test->client_pin)
+		bthost_set_pin_code(bthost, test->client_pin,
+							test->client_pin_len);
+	if (test && test->reject_ssp)
+		bthost_set_reject_user_confirm(bthost, true);
+
+	if (data->hciemu_type == HCIEMU_TYPE_LE)
+		mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	if (test && test->enable_ssp)
+		mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_BONDABLE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+}
+
+static void setup_powered_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *test = data->test_data;
+	unsigned char param[] = { 0x01 };
+
+	setup_powered_common();
+
+	tester_print("Powering on controller");
+
+	if (test && (test->expect_cmd || test->send_cmd)) {
+		struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+		bthost_set_connect_cb(bthost, send_rsp_new_conn, data);
+	}
+
+	if (test && test->direct_advertising)
+		mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING,
+				data->mgmt_index, sizeof(param), param,
+				NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_client_callback,
+			NULL, NULL);
+}
+
+static void setup_powered_server(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	setup_powered_common();
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	if (data->hciemu_type != HCIEMU_TYPE_BREDR)
+		mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING,
+				data->mgmt_index, sizeof(param), param, NULL,
+				NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_server_callback,
+			NULL, NULL);
+}
+
+static void test_basic(const void *test_data)
+{
+	int sk;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+	if (sk < 0) {
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		tester_test_failed();
+		return;
+	}
+
+	close(sk);
+
+	tester_test_passed();
+}
+
+static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	char buf[1024];
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(io);
+	if (read(sk, buf, l2data->data_len) != l2data->data_len) {
+		tester_warn("Unable to read %u bytes", l2data->data_len);
+		tester_test_failed();
+		return FALSE;
+	}
+
+	if (memcmp(buf, l2data->read_data, l2data->data_len))
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	char buf[1024];
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(io);
+	if (read(sk, buf, l2data->data_len) != l2data->data_len) {
+		tester_warn("Unable to read %u bytes", l2data->data_len);
+		tester_test_failed();
+		return FALSE;
+	}
+
+	if (memcmp(buf, l2data->read_data, l2data->data_len))
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return FALSE;
+}
+
+static void bthost_received_data(const void *buf, uint16_t len,
+							void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+
+	if (len != l2data->data_len) {
+		tester_test_failed();
+		return;
+	}
+
+	if (memcmp(buf, l2data->write_data, l2data->data_len))
+		tester_test_failed();
+	else
+		tester_test_passed();
+}
+
+static void server_bthost_received_data(const void *buf, uint16_t len,
+							void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+
+	if (len != l2data->data_len) {
+		tester_test_failed();
+		return;
+	}
+
+	if (memcmp(buf, l2data->write_data, l2data->data_len))
+		tester_test_failed();
+	else
+		tester_test_passed();
+}
+
+static bool check_mtu(struct test_data *data, int sk)
+{
+	const struct l2cap_data *l2data = data->test_data;
+	struct l2cap_options l2o;
+	socklen_t len;
+
+	memset(&l2o, 0, sizeof(l2o));
+
+	if (data->hciemu_type == HCIEMU_TYPE_LE &&
+				(l2data->client_psm || l2data->server_psm)) {
+		/* LE CoC enabled kernels should support BT_RCVMTU and
+		 * BT_SNDMTU.
+		 */
+		len = sizeof(l2o.imtu);
+		if (getsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU,
+							&l2o.imtu, &len) < 0) {
+			tester_warn("getsockopt(BT_RCVMTU): %s (%d)",
+					strerror(errno), errno);
+			return false;
+		}
+
+		len = sizeof(l2o.omtu);
+		if (getsockopt(sk, SOL_BLUETOOTH, BT_SNDMTU,
+							&l2o.omtu, &len) < 0) {
+			tester_warn("getsockopt(BT_SNDMTU): %s (%d)",
+					strerror(errno), errno);
+			return false;
+		}
+	} else {
+		/* For non-LE CoC enabled kernels we need to fall back to
+		 * L2CAP_OPTIONS, so test support for it as well */
+		len = sizeof(l2o);
+		if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+			 tester_warn("getsockopt(L2CAP_OPTIONS): %s (%d)",
+						strerror(errno), errno);
+			 return false;
+		 }
+	}
+
+	return true;
+}
+
+static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	int err, sk_err, sk;
+	socklen_t len = sizeof(sk_err);
+
+	data->io_id = 0;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+		err = -errno;
+	else
+		err = -sk_err;
+
+	if (err < 0) {
+		tester_warn("Connect failed: %s (%d)", strerror(-err), -err);
+		goto failed;
+	}
+
+	tester_print("Successfully connected");
+
+	if (!check_mtu(data, sk)) {
+		tester_test_failed();
+		return FALSE;
+	}
+
+	if (l2data->read_data) {
+		struct bthost *bthost;
+
+		bthost = hciemu_client_get_host(data->hciemu);
+		g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
+
+		bthost_send_cid(bthost, data->handle, data->dcid,
+					l2data->read_data, l2data->data_len);
+
+		return FALSE;
+	} else if (l2data->write_data) {
+		struct bthost *bthost;
+		ssize_t ret;
+
+		bthost = hciemu_client_get_host(data->hciemu);
+		bthost_add_cid_hook(bthost, data->handle, data->dcid,
+					bthost_received_data, NULL);
+
+		ret = write(sk, l2data->write_data, l2data->data_len);
+		if (ret != l2data->data_len) {
+			tester_warn("Unable to write all data");
+			tester_test_failed();
+		}
+
+		return FALSE;
+	}
+
+failed:
+	if (-err != l2data->expect_err)
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return FALSE;
+}
+
+static int create_l2cap_sock(struct test_data *data, uint16_t psm,
+						uint16_t cid, int sec_level)
+{
+	const struct l2cap_data *l2data = data->test_data;
+	const uint8_t *master_bdaddr;
+	struct sockaddr_l2 addr;
+	int sk, err;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK,
+							BTPROTO_L2CAP);
+	if (sk < 0) {
+		err = -errno;
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		return err;
+	}
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		close(sk);
+		return -ENODEV;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm = htobs(psm);
+	addr.l2_cid = htobs(cid);
+	bacpy(&addr.l2_bdaddr, (void *) master_bdaddr);
+
+	if (l2data && l2data->addr_type_avail)
+		addr.l2_bdaddr_type = l2data->addr_type;
+	else if (data->hciemu_type == HCIEMU_TYPE_LE)
+		addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
+	else
+		addr.l2_bdaddr_type = BDADDR_BREDR;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = -errno;
+		tester_warn("Can't bind socket: %s (%d)", strerror(errno),
+									errno);
+		close(sk);
+		return err;
+	}
+
+	if (sec_level) {
+		struct bt_security sec;
+
+		memset(&sec, 0, sizeof(sec));
+		sec.level = sec_level;
+
+		if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) < 0) {
+			err = -errno;
+			tester_warn("Can't set security level: %s (%d)",
+						strerror(errno), errno);
+			close(sk);
+			return err;
+		}
+	}
+
+	return sk;
+}
+
+static int connect_l2cap_impl(int sk, const uint8_t *bdaddr,
+				uint8_t bdaddr_type, uint16_t psm, uint16_t cid)
+{
+	struct sockaddr_l2 addr;
+	int err;
+
+	if (!bdaddr) {
+		tester_warn("No client bdaddr");
+		return -ENODEV;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, (void *) bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+	addr.l2_psm = htobs(psm);
+	addr.l2_cid = htobs(cid);
+
+	err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
+		err = -errno;
+		tester_warn("Can't connect socket: %s (%d)", strerror(errno),
+									errno);
+		return err;
+	}
+
+	return 0;
+}
+
+static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm,
+								uint16_t cid)
+{
+	const struct l2cap_data *l2data = data->test_data;
+	const uint8_t *client_bdaddr;
+	uint8_t bdaddr_type;
+
+	if (l2data->client_bdaddr != NULL)
+		client_bdaddr = l2data->client_bdaddr;
+	else
+		client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+
+	if (!client_bdaddr) {
+		tester_warn("No client bdaddr");
+		return -ENODEV;
+	}
+
+	if (l2data && l2data->addr_type_avail)
+		bdaddr_type = l2data->addr_type;
+	else if (data->hciemu_type == HCIEMU_TYPE_LE)
+		bdaddr_type = BDADDR_LE_PUBLIC;
+	else
+		bdaddr_type = BDADDR_BREDR;
+
+	return connect_l2cap_impl(sk, client_bdaddr, bdaddr_type, psm, cid);
+}
+
+static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
+							void *user_data)
+{
+	struct test_data *data = user_data;
+
+	data->dcid = cid;
+	data->handle = handle;
+}
+
+static void direct_adv_cmd_complete(uint16_t opcode, const void *param,
+						uint8_t len, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct bt_hci_cmd_le_set_adv_parameters *cp;
+	const uint8_t *expect_bdaddr;
+
+	if (opcode != BT_HCI_CMD_LE_SET_ADV_PARAMETERS)
+		return;
+
+	tester_print("Received advertising parameters HCI command");
+
+	cp = param;
+
+	/* Advertising as client should be direct advertising */
+	if (cp->type != 0x01) {
+		tester_warn("Invalid advertising type");
+		tester_test_failed();
+		return;
+	}
+
+	expect_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	if (memcmp(expect_bdaddr, cp->direct_addr, 6)) {
+		tester_warn("Invalid direct address in adv params");
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+}
+
+static void test_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	GIOChannel *io;
+	int sk;
+
+	if (l2data->server_psm) {
+		struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+		if (!l2data->data_len)
+			bthost_add_l2cap_server(bthost, l2data->server_psm,
+						NULL, NULL);
+		else
+			bthost_add_l2cap_server(bthost, l2data->server_psm,
+						client_l2cap_connect_cb, data);
+	}
+
+	if (l2data->direct_advertising)
+		hciemu_add_master_post_command_hook(data->hciemu,
+						direct_adv_cmd_complete, NULL);
+
+	sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level);
+	if (sk < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	if (connect_l2cap_sock(data, sk, l2data->client_psm,
+							l2data->cid) < 0) {
+		close(sk);
+		tester_test_failed();
+		return;
+	}
+
+	io = g_io_channel_unix_new(sk);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	data->io_id = g_io_add_watch(io, G_IO_OUT, l2cap_connect_cb, NULL);
+
+	g_io_channel_unref(io);
+
+	tester_print("Connect in progress");
+}
+
+static void test_connect_reject(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	int sk;
+
+	sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level);
+	if (sk < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	if (connect_l2cap_sock(data, sk, l2data->client_psm,
+							l2data->cid) < 0)
+		tester_test_passed();
+	else
+		tester_test_failed();
+
+	close(sk);
+}
+
+static void connect_socket(const uint8_t *client_bdaddr, int *sk_holder,
+							GIOFunc connect_cb)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	GIOChannel *io;
+	int sk;
+
+	sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level);
+	if (sk < 0) {
+		tester_print("Error in create_l2cap_sock");
+		tester_test_failed();
+		return;
+	}
+
+	*sk_holder = sk;
+
+	if (connect_l2cap_impl(sk, client_bdaddr, BDADDR_LE_PUBLIC,
+			l2data->client_psm, l2data->cid) < 0) {
+		tester_print("Error in connect_l2cap_sock");
+		close(sk);
+		tester_test_failed();
+		return;
+	}
+
+	if (connect_cb) {
+		io = g_io_channel_unix_new(sk);
+		g_io_channel_set_close_on_unref(io, TRUE);
+
+		data->io_id = g_io_add_watch(io, G_IO_OUT, connect_cb, NULL);
+
+		g_io_channel_unref(io);
+	}
+
+	tester_print("Connect in progress, sk = %d", sk);
+}
+
+static gboolean test_close_socket_1_part_3(gpointer arg)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Checking whether scan was properly stopped...");
+
+	if (data->sk != -1) {
+		tester_print("Error - scan was not enabled yet");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	if (hciemu_get_master_le_scan_enable(data->hciemu)) {
+		tester_print("Delayed check whether scann is off failed");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	tester_test_passed();
+	return FALSE;
+}
+
+static gboolean test_close_socket_1_part_2(gpointer args)
+{
+	struct test_data *data = tester_get_data();
+	int sk = data->sk;
+
+	tester_print("Will close socket during scan phase...");
+
+	/* We tried to conect to LE device that is not advertising. It
+	 * was added to kernel whitelist, and scan was started. We
+	 * should be still scanning.
+	 */
+	if (!hciemu_get_master_le_scan_enable(data->hciemu)) {
+		tester_print("Error - should be still scanning");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	/* Calling close() should remove device from  whitelist, and stop
+	 * the scan.
+	 */
+	if (close(sk) < 0) {
+		tester_print("Error when closing socket");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	data->sk = -1;
+	/* tester_test_passed will be called when scan is stopped. */
+	return FALSE;
+}
+
+static gboolean test_close_socket_2_part_3(gpointer arg)
+{
+	struct test_data *data = tester_get_data();
+	int sk = data->sk;
+	int err;
+
+	/* Scan should be already over, we're trying to create connection */
+	if (hciemu_get_master_le_scan_enable(data->hciemu)) {
+		tester_print("Error - should no longer scan");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	/* Calling close() should eventually cause CMD_LE_CREATE_CONN_CANCEL */
+	err = close(sk);
+	if (err < 0) {
+		tester_print("Error when closing socket");
+		tester_test_failed();
+		return FALSE;
+	}
+
+	/* CMD_LE_CREATE_CONN_CANCEL will trigger test pass. */
+	return FALSE;
+}
+
+static bool test_close_socket_cc_hook(const void *data, uint16_t len,
+							void *user_data)
+{
+	return false;
+}
+
+static gboolean test_close_socket_2_part_2(gpointer arg)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	/* Make sure CMD_LE_CREATE_CONN will not immediately result in
+	 * BT_HCI_EVT_CONN_COMPLETE.
+	 */
+	hciemu_add_hook(data->hciemu, HCIEMU_HOOK_PRE_EVT,
+		BT_HCI_CMD_LE_CREATE_CONN, test_close_socket_cc_hook, NULL);
+
+	/* Advertise once. After that, kernel should stop scanning, and trigger
+	 * BT_HCI_CMD_LE_CREATE_CONN_CANCEL.
+	 */
+	bthost_set_adv_enable(bthost, 0x01);
+	bthost_set_adv_enable(bthost, 0x00);
+	return FALSE;
+}
+
+static void test_close_socket_scan_enabled(void)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+
+	if (l2data == &le_client_close_socket_test_1)
+		g_idle_add(test_close_socket_1_part_2, NULL);
+	else if (l2data == &le_client_close_socket_test_2)
+		g_idle_add(test_close_socket_2_part_2, NULL);
+}
+
+static void test_close_socket_scan_disabled(void)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+
+	if (l2data == &le_client_close_socket_test_1)
+		g_idle_add(test_close_socket_1_part_3, NULL);
+	else if (l2data == &le_client_close_socket_test_2)
+		g_idle_add(test_close_socket_2_part_3, NULL);
+}
+
+static void test_close_socket_conn_cancel(void)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+
+	if (l2data == &le_client_close_socket_test_2)
+		tester_test_passed();
+}
+
+static void test_close_socket_router(uint16_t opcode, const void *param,
+					uint8_t length, void *user_data)
+{
+	/* tester_print("HCI Command 0x%04x length %u", opcode, length); */
+	if (opcode == BT_HCI_CMD_LE_SET_SCAN_ENABLE) {
+		const struct bt_hci_cmd_le_set_scan_enable *scan_params = param;
+
+		if (scan_params->enable == true)
+			test_close_socket_scan_enabled();
+		else
+			test_close_socket_scan_disabled();
+	} else if (opcode == BT_HCI_CMD_LE_CREATE_CONN_CANCEL) {
+		test_close_socket_conn_cancel();
+	}
+}
+
+static void test_close_socket(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	const uint8_t *client_bdaddr;
+
+	hciemu_add_master_post_command_hook(data->hciemu,
+					test_close_socket_router, data);
+
+	if (l2data->client_bdaddr != NULL)
+		client_bdaddr = l2data->client_bdaddr;
+	else
+		client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+
+	connect_socket(client_bdaddr, &data->sk, NULL);
+}
+
+static uint8_t test_two_sockets_connect_cb_cnt;
+static gboolean test_two_sockets_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	int err, sk_err, sk;
+	socklen_t len = sizeof(sk_err);
+
+	data->io_id = 0;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+		err = -errno;
+	else
+		err = -sk_err;
+
+	if (err < 0) {
+		tester_warn("Connect failed: %s (%d)", strerror(-err), -err);
+		tester_test_failed();
+		return FALSE;
+	}
+
+	tester_print("Successfully connected");
+	test_two_sockets_connect_cb_cnt++;
+
+	if (test_two_sockets_connect_cb_cnt == 2) {
+		close(data->sk);
+		close(data->sk2);
+		tester_test_passed();
+	}
+
+	if (l2data->close_one_socket && test_two_sockets_connect_cb_cnt == 1) {
+		close(data->sk2);
+		tester_test_passed();
+	}
+
+	return FALSE;
+}
+
+static gboolean enable_advertising(gpointer args)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	bthost_set_adv_enable(bthost, 0x01);
+	return FALSE;
+}
+
+static void test_connect_two_sockets_part_2(void)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	const uint8_t *client_bdaddr;
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	connect_socket(client_bdaddr, &data->sk2, test_two_sockets_connect_cb);
+
+	if (l2data->close_one_socket) {
+		tester_print("Closing first socket! %d", data->sk);
+		close(data->sk);
+	}
+
+	g_idle_add(enable_advertising, NULL);
+}
+
+static uint8_t test_scan_enable_counter;
+static void test_connect_two_sockets_router(uint16_t opcode, const void *param,
+					uint8_t length, void *user_data)
+{
+	const struct bt_hci_cmd_le_set_scan_enable *scan_params = param;
+
+	tester_print("HCI Command 0x%04x length %u", opcode, length);
+	if (opcode == BT_HCI_CMD_LE_SET_SCAN_ENABLE &&
+						scan_params->enable == true) {
+		test_scan_enable_counter++;
+		if (test_scan_enable_counter == 1)
+			test_connect_two_sockets_part_2();
+		else if (test_scan_enable_counter == 2)
+			g_idle_add(enable_advertising, NULL);
+	}
+}
+
+static void test_connect_two_sockets(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	const uint8_t *client_bdaddr;
+
+	test_two_sockets_connect_cb_cnt = 0;
+	test_scan_enable_counter = 0;
+
+	hciemu_add_master_post_command_hook(data->hciemu,
+				test_connect_two_sockets_router, data);
+
+	if (l2data->server_psm) {
+		struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+		if (!l2data->data_len)
+			bthost_add_l2cap_server(bthost, l2data->server_psm,
+						NULL, NULL);
+	}
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	if (l2data->close_one_socket)
+		connect_socket(client_bdaddr, &data->sk, NULL);
+	else
+		connect_socket(client_bdaddr, &data->sk,
+						test_two_sockets_connect_cb);
+}
+
+static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	int sk, new_sk;
+
+	data->io_id = 0;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	new_sk = accept(sk, NULL, NULL);
+	if (new_sk < 0) {
+		tester_warn("accept failed: %s (%u)", strerror(errno), errno);
+		tester_test_failed();
+		return FALSE;
+	}
+
+	if (!check_mtu(data, new_sk)) {
+		tester_test_failed();
+		return FALSE;
+	}
+
+	if (l2data->read_data) {
+		struct bthost *bthost;
+		GIOChannel *new_io;
+
+		new_io = g_io_channel_unix_new(new_sk);
+		g_io_channel_set_close_on_unref(new_io, TRUE);
+
+		bthost = hciemu_client_get_host(data->hciemu);
+		g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL);
+		bthost_send_cid(bthost, data->handle, data->dcid,
+					l2data->read_data, l2data->data_len);
+
+		g_io_channel_unref(new_io);
+
+		return FALSE;
+	} else if (l2data->write_data) {
+		struct bthost *bthost;
+		ssize_t ret;
+
+		bthost = hciemu_client_get_host(data->hciemu);
+		bthost_add_cid_hook(bthost, data->handle, data->scid,
+					server_bthost_received_data, NULL);
+
+		ret = write(new_sk, l2data->write_data, l2data->data_len);
+		close(new_sk);
+
+		if (ret != l2data->data_len) {
+			tester_warn("Unable to write all data");
+			tester_test_failed();
+		}
+
+		return FALSE;
+	}
+
+	tester_print("Successfully connected");
+
+	close(new_sk);
+
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
+							void *user_data)
+{
+	struct test_data *test_data = user_data;
+	const struct l2cap_data *l2data = test_data->test_data;
+
+	tester_print("Client received response code 0x%02x", code);
+
+	if (code != l2data->expect_cmd_code) {
+		tester_warn("Unexpected L2CAP response code (expected 0x%02x)",
+						l2data->expect_cmd_code);
+		return;
+	}
+
+	if (code == BT_L2CAP_PDU_CONN_RSP) {
+
+		const struct bt_l2cap_pdu_conn_rsp *rsp = data;
+		if (len == sizeof(rsp) && !rsp->result && !rsp->status)
+			return;
+
+		test_data->dcid = rsp->dcid;
+		test_data->scid = rsp->scid;
+
+		if (l2data->data_len)
+			return;
+	}
+
+	if (!l2data->expect_cmd) {
+		tester_test_passed();
+		return;
+	}
+
+	if (l2data->expect_cmd_len != len) {
+		tester_warn("Unexpected L2CAP response length (%u != %u)",
+						len, l2data->expect_cmd_len);
+		goto failed;
+	}
+
+	if (memcmp(l2data->expect_cmd, data, len) != 0) {
+		tester_warn("Unexpected L2CAP response");
+		goto failed;
+	}
+
+	tester_test_passed();
+	return;
+
+failed:
+	tester_test_failed();
+}
+
+static void send_req_new_conn(uint16_t handle, void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct l2cap_data *l2data = data->test_data;
+	struct bthost *bthost;
+
+	tester_print("New client connection with handle 0x%04x", handle);
+
+	data->handle = handle;
+
+	if (l2data->send_cmd) {
+		bthost_l2cap_rsp_cb cb;
+
+		if (l2data->expect_cmd_code)
+			cb = client_l2cap_rsp;
+		else
+			cb = NULL;
+
+		tester_print("Sending L2CAP Request from client");
+
+		bthost = hciemu_client_get_host(data->hciemu);
+		bthost_l2cap_req(bthost, handle, l2data->send_cmd_code,
+					l2data->send_cmd, l2data->send_cmd_len,
+					cb, data);
+	}
+}
+
+static void test_server(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct l2cap_data *l2data = data->test_data;
+	const uint8_t *master_bdaddr;
+	uint8_t addr_type;
+	struct bthost *bthost;
+	GIOChannel *io;
+	int sk;
+
+	if (l2data->server_psm || l2data->cid) {
+		sk = create_l2cap_sock(data, l2data->server_psm,
+					l2data->cid, l2data->sec_level);
+		if (sk < 0) {
+			tester_test_failed();
+			return;
+		}
+
+		if (listen(sk, 5) < 0) {
+			tester_warn("listening on socket failed: %s (%u)",
+					strerror(errno), errno);
+			tester_test_failed();
+			close(sk);
+			return;
+		}
+
+		io = g_io_channel_unix_new(sk);
+		g_io_channel_set_close_on_unref(io, TRUE);
+
+		data->io_id = g_io_add_watch(io, G_IO_IN, l2cap_listen_cb,
+									NULL);
+		g_io_channel_unref(io);
+
+		tester_print("Listening for connections");
+	}
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_connect_cb(bthost, send_req_new_conn, data);
+
+	if (data->hciemu_type == HCIEMU_TYPE_BREDR)
+		addr_type = BDADDR_BREDR;
+	else
+		addr_type = BDADDR_LE_PUBLIC;
+
+	bthost_hci_connect(bthost, master_bdaddr, addr_type);
+}
+
+static void test_getpeername_not_connected(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct sockaddr_l2 addr;
+	socklen_t len;
+	int sk;
+
+	sk = create_l2cap_sock(data, 0, 0, 0);
+	if (sk < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	len = sizeof(addr);
+	if (getpeername(sk, (struct sockaddr *) &addr, &len) == 0) {
+		tester_warn("getpeername succeeded on non-connected socket");
+		tester_test_failed();
+		goto done;
+	}
+
+	if (errno != ENOTCONN) {
+		tester_warn("Unexpexted getpeername error: %s (%d)",
+						strerror(errno), errno);
+		tester_test_failed();
+		goto done;
+	}
+
+	tester_test_passed();
+
+done:
+	close(sk);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_l2cap_bredr("Basic L2CAP Socket - Success", NULL,
+					setup_powered_client, test_basic);
+	test_l2cap_bredr("Non-connected getpeername - Failure", NULL,
+					setup_powered_client,
+					test_getpeername_not_connected);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client - Success",
+					&client_connect_success_test,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client SSP - Success 1",
+					&client_connect_ssp_success_test_1,
+					setup_powered_client, test_connect);
+	test_l2cap_bredr("L2CAP BR/EDR Client SSP - Success 2",
+					&client_connect_ssp_success_test_2,
+					setup_powered_client, test_connect);
+	test_l2cap_bredr("L2CAP BR/EDR Client PIN Code - Success",
+					&client_connect_pin_success_test,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client - Read Success",
+					&client_connect_read_success_test,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client - Write Success",
+					&client_connect_write_success_test,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 1",
+					&client_connect_nval_psm_test_1,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 2",
+					&client_connect_nval_psm_test_2,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 3",
+					&client_connect_nval_psm_test_3,
+					setup_powered_client, test_connect);
+
+	test_l2cap_bredr("L2CAP BR/EDR Server - Success",
+					&l2cap_server_success_test,
+					setup_powered_server, test_server);
+
+	test_l2cap_bredr("L2CAP BR/EDR Server - Read Success",
+					&l2cap_server_read_success_test,
+					setup_powered_server, test_server);
+
+	test_l2cap_bredr("L2CAP BR/EDR Server - Write Success",
+					&l2cap_server_write_success_test,
+					setup_powered_server, test_server);
+
+	test_l2cap_bredr("L2CAP BR/EDR Server - Security Block",
+					&l2cap_server_sec_block_test,
+					setup_powered_server, test_server);
+
+	test_l2cap_bredr("L2CAP BR/EDR Server - Invalid PSM",
+					&l2cap_server_nval_psm_test,
+					setup_powered_server, test_server);
+	test_l2cap_bredr("L2CAP BR/EDR Server - Invalid PDU",
+				&l2cap_server_nval_pdu_test1,
+				setup_powered_server, test_server);
+	test_l2cap_bredr("L2CAP BR/EDR Server - Invalid Disconnect CID",
+				&l2cap_server_nval_cid_test1,
+				setup_powered_server, test_server);
+	test_l2cap_bredr("L2CAP BR/EDR Server - Invalid Config CID",
+				&l2cap_server_nval_cid_test2,
+				setup_powered_server, test_server);
+
+	test_l2cap_le("L2CAP LE Client - Success",
+				&le_client_connect_success_test_1,
+				setup_powered_client, test_connect);
+	test_l2cap_le("L2CAP LE Client, Direct Advertising - Success",
+				&le_client_connect_adv_success_test_1,
+				setup_powered_client, test_connect);
+	test_l2cap_le("L2CAP LE Client SMP - Success",
+				&le_client_connect_success_test_2,
+				setup_powered_client, test_connect);
+	test_l2cap_le("L2CAP LE Client - Command Reject",
+					&le_client_connect_reject_test_1,
+					setup_powered_client, test_connect);
+	test_l2cap_bredr("L2CAP LE Client - Connection Reject",
+				&le_client_connect_reject_test_2,
+				setup_powered_client, test_connect_reject);
+
+	test_l2cap_le("L2CAP LE Client - Close socket 1",
+				&le_client_close_socket_test_1,
+				setup_powered_client,
+				test_close_socket);
+
+	test_l2cap_le("L2CAP LE Client - Close socket 2",
+				&le_client_close_socket_test_2,
+				setup_powered_client,
+				test_close_socket);
+
+	test_l2cap_le("L2CAP LE Client - Open two sockets",
+				&le_client_two_sockets_same_client,
+				setup_powered_client,
+				test_connect_two_sockets);
+
+	test_l2cap_le("L2CAP LE Client - Open two sockets close one",
+				&le_client_two_sockets_close_one,
+				setup_powered_client,
+				test_connect_two_sockets);
+
+	test_l2cap_le("L2CAP LE Client - Invalid PSM",
+					&le_client_connect_nval_psm_test,
+					setup_powered_client, test_connect);
+	test_l2cap_le("L2CAP LE Server - Success", &le_server_success_test,
+					setup_powered_server, test_server);
+	test_l2cap_le("L2CAP LE Server - Nval SCID", &le_server_nval_scid_test,
+					setup_powered_server, test_server);
+
+
+	test_l2cap_le("L2CAP LE ATT Client - Success",
+				&le_att_client_connect_success_test_1,
+				setup_powered_client, test_connect);
+	test_l2cap_le("L2CAP LE ATT Server - Success",
+				&le_att_server_success_test_1,
+				setup_powered_server, test_server);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/l2ping.1 b/bluez/repo/tools/l2ping.1
new file mode 100644
index 0000000..4d09b05
--- /dev/null
+++ b/bluez/repo/tools/l2ping.1
@@ -0,0 +1,76 @@
+.TH L2PING 1 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+l2ping \- Send L2CAP echo request and receive answer
+.SH SYNOPSIS
+.B l2ping
+.RB [\| \-i
+.IR <hciX> \|]
+.RB [\| \-s
+.IR size \|]
+.RB [\| \-c
+.IR count \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-d
+.IR delay \|]
+.RB [\| \-f \|]
+.RB [\| \-r \|]
+.RB [\| \-v \|]
+.I bd_addr
+
+.SH DESCRIPTION
+.LP
+L2ping sends a L2CAP echo request to the Bluetooth MAC address
+.I bd_addr
+given in dotted hex notation.
+.SH OPTIONS
+.TP
+.BI \-i " <hciX>"
+The command is applied to device
+.BI
+hciX
+, which must be the name of an installed Bluetooth device (X = 0, 1, 2, ...)
+If not specified, the command will be sent to the first available Bluetooth
+device.
+.TP
+.BI \-s " size"
+The
+.I size
+of the data packets to be sent.
+.TP
+.BI \-c " count"
+Send
+.I count
+number of packets then exit.
+.TP
+.BI \-t " timeout"
+Wait
+.I timeout
+seconds for the response.
+.TP
+.BI \-d " delay"
+Wait
+.I delay
+seconds between pings.
+.TP
+.B \-f
+Kind of flood ping. Use with care! It reduces the delay time between packets
+to 0.
+.TP
+.B \-r
+Reverse ping (gnip?). Send echo response instead of echo request.
+.TP
+.B \-v
+Verify response payload is identical to request payload. It is not required for
+remote stacks to return the request payload, but most stacks do (including
+Bluez).
+.TP
+.I bd_addr
+The Bluetooth MAC address to be pinged in dotted hex notation like
+.B 01:02:03:ab:cd:ef
+or
+.B 01:EF:cd:aB:02:03
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Nils Faerber <nils@kernelconcepts.de>, Adam Laurie <adam@algroup.co.uk>.
diff --git a/bluez/repo/tools/l2ping.c b/bluez/repo/tools/l2ping.c
new file mode 100644
index 0000000..fa97fe3
--- /dev/null
+++ b/bluez/repo/tools/l2ping.c
@@ -0,0 +1,324 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+
+/* Defaults */
+static bdaddr_t bdaddr;
+static int size    = 44;
+static int ident   = 200;
+static int delay   = 1;
+static int count   = -1;
+static int timeout = 10;
+static int reverse = 0;
+static int verify = 0;
+
+/* Stats */
+static int sent_pkt = 0;
+static int recv_pkt = 0;
+
+static float tv2fl(struct timeval tv)
+{
+	return (float)(tv.tv_sec*1000.0) + (float)(tv.tv_usec/1000.0);
+}
+
+static void stat(int sig)
+{
+	int loss = sent_pkt ? (float)((sent_pkt-recv_pkt)/(sent_pkt/100.0)) : 0;
+	printf("%d sent, %d received, %d%% loss\n", sent_pkt, recv_pkt, loss);
+	exit(0);
+}
+
+static void ping(char *svr)
+{
+	struct sigaction sa;
+	struct sockaddr_l2 addr;
+	socklen_t optlen;
+	unsigned char *send_buf;
+	unsigned char *recv_buf;
+	char str[18];
+	int i, sk, lost;
+	uint8_t id;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = stat;
+	sigaction(SIGINT, &sa, NULL);
+
+	send_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+	recv_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+	if (!send_buf || !recv_buf) {
+		perror("Can't allocate buffer");
+		exit(1);
+	}
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+	if (sk < 0) {
+		perror("Can't create socket");
+		goto error;
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, &bdaddr);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Can't bind socket");
+		goto error;
+	}
+
+	/* Connect to remote device */
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	str2ba(svr, &addr.l2_bdaddr);
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Can't connect");
+		goto error;
+	}
+
+	/* Get local address */
+	memset(&addr, 0, sizeof(addr));
+	optlen = sizeof(addr);
+
+	if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+		perror("Can't get local address");
+		goto error;
+	}
+
+	ba2str(&addr.l2_bdaddr, str);
+	printf("Ping: %s from %s (data size %d) ...\n", svr, str, size);
+
+	/* Initialize send buffer */
+	for (i = 0; i < size; i++)
+		send_buf[L2CAP_CMD_HDR_SIZE + i] = (i % 40) + 'A';
+
+	id = ident;
+
+	while (count == -1 || count-- > 0) {
+		struct timeval tv_send, tv_recv, tv_diff;
+		l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *) send_buf;
+		l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *) recv_buf;
+
+		/* Build command header */
+		send_cmd->ident = id;
+		send_cmd->len   = htobs(size);
+
+		if (reverse)
+			send_cmd->code = L2CAP_ECHO_RSP;
+		else
+			send_cmd->code = L2CAP_ECHO_REQ;
+
+		gettimeofday(&tv_send, NULL);
+
+		/* Send Echo Command */
+		if (send(sk, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) {
+			perror("Send failed");
+			goto error;
+		}
+
+		/* Wait for Echo Response */
+		lost = 0;
+		while (1) {
+			struct pollfd pf[1];
+			int err;
+
+			pf[0].fd = sk;
+			pf[0].events = POLLIN;
+
+			if ((err = poll(pf, 1, timeout * 1000)) < 0) {
+				perror("Poll failed");
+				goto error;
+			}
+
+			if (!err) {
+				lost = 1;
+				break;
+			}
+
+			if ((err = recv(sk, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0)) < 0) {
+				perror("Recv failed");
+				goto error;
+			}
+
+			if (!err){
+				printf("Disconnected\n");
+				goto error;
+			}
+
+			recv_cmd->len = btohs(recv_cmd->len);
+
+			/* Check for our id */
+			if (recv_cmd->ident != id)
+				continue;
+
+			/* Check type */
+			if (!reverse && recv_cmd->code == L2CAP_ECHO_RSP)
+				break;
+
+			if (recv_cmd->code == L2CAP_COMMAND_REJ) {
+				printf("Peer doesn't support Echo packets\n");
+				goto error;
+			}
+
+		}
+		sent_pkt++;
+
+		if (!lost) {
+			recv_pkt++;
+
+			gettimeofday(&tv_recv, NULL);
+			timersub(&tv_recv, &tv_send, &tv_diff);
+
+			if (verify) {
+				/* Check payload length */
+				if (recv_cmd->len != size) {
+					fprintf(stderr, "Received %d bytes, expected %d\n",
+						   recv_cmd->len, size);
+					goto error;
+				}
+
+				/* Check payload */
+				if (memcmp(&send_buf[L2CAP_CMD_HDR_SIZE],
+						   &recv_buf[L2CAP_CMD_HDR_SIZE], size)) {
+					fprintf(stderr, "Response payload different.\n");
+					goto error;
+				}
+			}
+
+			printf("%d bytes from %s id %d time %.2fms\n", recv_cmd->len, svr,
+				   id - ident, tv2fl(tv_diff));
+
+			if (delay)
+				sleep(delay);
+		} else {
+			printf("no response from %s: id %d\n", svr, id - ident);
+		}
+
+		if (++id > 254)
+			id = ident;
+	}
+	stat(0);
+	free(send_buf);
+	free(recv_buf);
+	return;
+
+error:
+	close(sk);
+	free(send_buf);
+	free(recv_buf);
+	exit(1);
+}
+
+static void usage(void)
+{
+	printf("l2ping - L2CAP ping\n");
+	printf("Usage:\n");
+	printf("\tl2ping [-i device] [-s size] [-c count] [-t timeout] [-d delay] [-f] [-r] [-v] <bdaddr>\n");
+	printf("\t-f  Flood ping (delay = 0)\n");
+	printf("\t-r  Reverse ping\n");
+	printf("\t-v  Verify request and response payload\n");
+}
+
+int main(int argc, char *argv[])
+{
+	int opt;
+
+	/* Default options */
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while ((opt=getopt(argc,argv,"i:d:s:c:t:frv")) != EOF) {
+		switch(opt) {
+		case 'i':
+			if (!strncasecmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &bdaddr);
+			else
+				str2ba(optarg, &bdaddr);
+			break;
+
+		case 'd':
+			delay = atoi(optarg);
+			break;
+
+		case 'f':
+			/* Kinda flood ping */
+			delay = 0;
+			break;
+
+		case 'r':
+			/* Use responses instead of requests */
+			reverse = 1;
+			break;
+
+		case 'v':
+			verify = 1;
+			break;
+
+		case 'c':
+			count = atoi(optarg);
+			break;
+
+		case 't':
+			timeout = atoi(optarg);
+			break;
+
+		case 's':
+			size = atoi(optarg);
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	if (!(argc - optind)) {
+		usage();
+		exit(1);
+	}
+
+	ping(argv[optind]);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/l2test.c b/bluez/repo/tools/l2test.c
new file mode 100644
index 0000000..d4e3ae6
--- /dev/null
+++ b/bluez/repo/tools/l2test.c
@@ -0,0 +1,1682 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <stdbool.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+
+#include "src/shared/util.h"
+
+#define NIBBLE_TO_ASCII(c)  ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
+
+#define BREDR_DEFAULT_PSM	0x1011
+#define LE_DEFAULT_PSM		0x0080
+
+/* Test modes */
+enum {
+	SEND,
+	RECV,
+	RECONNECT,
+	MULTY,
+	DUMP,
+	CONNECT,
+	CRECV,
+	LSEND,
+	SENDDUMP,
+	LSENDDUMP,
+	LSENDRECV,
+	CSENDRECV,
+	INFOREQ,
+	PAIRING,
+};
+
+static unsigned char *buf;
+
+/* Default mtu */
+static int imtu = 672;
+static int omtu = 0;
+
+/* Default FCS option */
+static int fcs = 0x01;
+
+/* Default Transmission Window */
+static int txwin_size = 63;
+
+/* Default Max Transmission */
+static int max_transmit = 3;
+
+/* Default data size */
+static long data_size = -1;
+static long buffer_size = 2048;
+
+/* Default addr and psm and cid */
+static bdaddr_t bdaddr;
+static unsigned short psm = 0;
+static unsigned short cid = 0;
+
+/* Default number of frames to send (-1 = infinite) */
+static int num_frames = -1;
+
+/* Default number of consecutive frames before the delay */
+static int count = 1;
+
+/* Default delay after sending count number of frames */
+static unsigned long send_delay = 0;
+
+/* Default delay before receiving */
+static unsigned long recv_delay = 0;
+
+/* Default delay before disconnecting */
+static unsigned long disc_delay = 0;
+
+/* Initial sequence value when sending frames */
+static int seq_start = 0;
+
+static const char *filename = NULL;
+
+static int rfcmode = 0;
+static int master = 0;
+static int auth = 0;
+static int encr = 0;
+static int secure = 0;
+static int socktype = SOCK_SEQPACKET;
+static int linger = 0;
+static int reliable = 0;
+static int timestamp = 0;
+static int defer_setup = 0;
+static int priority = -1;
+static int rcvbuf = 0;
+static int chan_policy = -1;
+static int bdaddr_type = 0;
+
+struct lookup_table {
+	const char *name;
+	int flag;
+};
+
+static struct lookup_table l2cap_modes[] = {
+	{ "basic",	L2CAP_MODE_BASIC	},
+	/* Not implemented
+	{ "flowctl",	L2CAP_MODE_FLOWCTL	},
+	{ "retrans",	L2CAP_MODE_RETRANS	},
+	*/
+	{ "ertm",	L2CAP_MODE_ERTM		},
+	{ "streaming",	L2CAP_MODE_STREAMING	},
+	{ 0 }
+};
+
+static struct lookup_table chan_policies[] = {
+	{ "bredr",	BT_CHANNEL_POLICY_BREDR_ONLY		},
+	{ "bredr_pref",	BT_CHANNEL_POLICY_BREDR_PREFERRED	},
+	{ "amp_pref",	BT_CHANNEL_POLICY_AMP_PREFERRED		},
+	{ NULL,		0					},
+};
+
+static struct lookup_table bdaddr_types[] = {
+	{ "bredr",	BDADDR_BREDR		},
+	{ "le_public",	BDADDR_LE_PUBLIC	},
+	{ "le_random",	BDADDR_LE_RANDOM	},
+	{ NULL,		0			},
+};
+
+static int get_lookup_flag(struct lookup_table *table, char *name)
+{
+	int i;
+
+	for (i = 0; table[i].name; i++)
+		if (!strcasecmp(table[i].name, name))
+			return table[i].flag;
+
+	return -1;
+}
+
+static const char *get_lookup_str(struct lookup_table *table, int flag)
+{
+	int i;
+
+	for (i = 0; table[i].name; i++)
+		if (table[i].flag == flag)
+			return table[i].name;
+
+	return NULL;
+}
+
+static void print_lookup_values(struct lookup_table *table, char *header)
+{
+	int i;
+
+	printf("%s\n", header);
+
+	for (i = 0; table[i].name; i++)
+		printf("\t%s\n", table[i].name);
+}
+
+static float tv2fl(struct timeval tv)
+{
+	return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static char *ltoh(unsigned long c, char *s)
+{
+	int c1;
+
+	c1     = (c >> 28) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = (c >> 24) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = (c >> 20) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = (c >> 16) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = (c >> 12) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = (c >>  8) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = (c >>  4) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = c & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	*s     = 0;
+	return s;
+}
+
+static char *ctoh(char c, char *s)
+{
+	char c1;
+
+	c1     = (c >> 4) & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	c1     = c & 0x0f;
+	*(s++) = NIBBLE_TO_ASCII (c1);
+	*s     = 0;
+	return s;
+}
+
+static void hexdump(unsigned char *s, unsigned long l)
+{
+	char bfr[80];
+	char *pb;
+	unsigned long i, n = 0;
+
+	if (l == 0)
+		return;
+
+	while (n < l) {
+		pb = bfr;
+		pb = ltoh (n, pb);
+		*(pb++) = ':';
+		*(pb++) = ' ';
+		for (i = 0; i < 16; i++) {
+			if (n + i >= l) {
+				*(pb++) = ' ';
+				*(pb++) = ' ';
+			} else
+				pb = ctoh (*(s + i), pb);
+			*(pb++) = ' ';
+		}
+		*(pb++) = ' ';
+		for (i = 0; i < 16; i++) {
+			if (n + i >= l)
+				break;
+			else
+				*(pb++) = (isprint (*(s + i)) ? *(s + i) : '.');
+		}
+		*pb = 0;
+		n += 16;
+		s += 16;
+		puts(bfr);
+	}
+}
+
+static int getopts(int sk, struct l2cap_options *opts, bool connected)
+{
+	socklen_t optlen;
+	int err;
+
+	memset(opts, 0, sizeof(*opts));
+
+	if (bdaddr_type == BDADDR_BREDR) {
+		optlen = sizeof(*opts);
+		return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen);
+	}
+
+	optlen = sizeof(opts->imtu);
+	err = getsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu, &optlen);
+	if (err < 0 || !connected)
+		return err;
+
+	optlen = sizeof(opts->omtu);
+	return getsockopt(sk, SOL_BLUETOOTH, BT_SNDMTU, &opts->omtu, &optlen);
+}
+
+static int setopts(int sk, struct l2cap_options *opts)
+{
+	if (bdaddr_type == BDADDR_BREDR)
+		return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts,
+								sizeof(*opts));
+
+	return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu,
+							sizeof(opts->imtu));
+}
+
+static int do_connect(char *svr)
+{
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	struct l2cap_conninfo conn;
+	socklen_t optlen;
+	int sk, opt;
+	char ba[18];
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		return -1;
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, &bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+	if (cid)
+		addr.l2_cid = htobs(cid);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Get default options */
+	if (getopts(sk, &opts, false) < 0) {
+		syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
+						strerror(errno), errno);
+		goto error;
+	}
+
+	/* Set new options */
+	opts.omtu = omtu;
+	opts.imtu = imtu;
+	opts.mode = rfcmode;
+
+	opts.fcs = fcs;
+	opts.txwin_size = txwin_size;
+	opts.max_tx = max_transmit;
+
+	if (setopts(sk, &opts) < 0) {
+		syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+#if 0
+	/* Enable SO_TIMESTAMP */
+	if (timestamp) {
+		int t = 1;
+
+		if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+			syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+	}
+#endif
+
+	if (chan_policy != -1) {
+		if (setsockopt(sk, SOL_BLUETOOTH, BT_CHANNEL_POLICY,
+				&chan_policy, sizeof(chan_policy)) < 0) {
+			syslog(LOG_ERR, "Can't enable chan policy : %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+	}
+
+	/* Enable SO_LINGER */
+	if (linger) {
+		struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+		if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+			syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+	}
+
+	/* Set link mode */
+	opt = 0;
+	if (reliable)
+		opt |= L2CAP_LM_RELIABLE;
+	if (master)
+		opt |= L2CAP_LM_MASTER;
+	if (auth)
+		opt |= L2CAP_LM_AUTH;
+	if (encr)
+		opt |= L2CAP_LM_ENCRYPT;
+	if (secure)
+		opt |= L2CAP_LM_SECURE;
+
+	if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+		syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Set receive buffer size */
+	if (rcvbuf && setsockopt(sk, SOL_SOCKET, SO_RCVBUF,
+						&rcvbuf, sizeof(rcvbuf)) < 0) {
+		syslog(LOG_ERR, "Can't set socket rcv buf size: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	optlen = sizeof(rcvbuf);
+	if (getsockopt(sk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket rcv buf size: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Connect to remote device */
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	str2ba(svr, &addr.l2_bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+	if (cid)
+		addr.l2_cid = htobs(cid);
+	else if (psm)
+		addr.l2_psm = htobs(psm);
+	else
+		goto error;
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+		syslog(LOG_ERR, "Can't connect: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Get current options */
+	if (getopts(sk, &opts, true) < 0) {
+		syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Get connection information */
+	memset(&conn, 0, sizeof(conn));
+	optlen = sizeof(conn);
+
+	if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &priority,
+						sizeof(priority)) < 0) {
+		syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	if (getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Check for remote address */
+	memset(&addr, 0, sizeof(addr));
+	optlen = sizeof(addr);
+
+	if (getpeername(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	ba2str(&addr.l2_bdaddr, ba);
+	syslog(LOG_INFO, "Connected to %s (%s, psm %d, scid %d)", ba,
+		get_lookup_str(bdaddr_types, addr.l2_bdaddr_type),
+		addr.l2_psm, addr.l2_cid);
+
+	/* Check for socket address */
+	memset(&addr, 0, sizeof(addr));
+	optlen = sizeof(addr);
+
+	if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	ba2str(&addr.l2_bdaddr, ba);
+	syslog(LOG_INFO, "Local device %s (%s, psm %d, scid %d)", ba,
+		get_lookup_str(bdaddr_types, addr.l2_bdaddr_type),
+		addr.l2_psm, addr.l2_cid);
+
+	syslog(LOG_INFO, "Options [imtu %d, omtu %d, flush_to %d, "
+		"mode %d, handle %d, class 0x%02x%02x%02x, priority %d, rcvbuf %d]",
+		opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
+		conn.dev_class[2], conn.dev_class[1], conn.dev_class[0], opt,
+		rcvbuf);
+
+	omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
+	imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
+
+	return sk;
+
+error:
+	close(sk);
+	return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	struct l2cap_conninfo conn;
+	socklen_t optlen;
+	int sk, nsk, opt;
+	char ba[18];
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, &bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+	if (cid)
+		addr.l2_cid = htobs(cid);
+	else if (psm)
+		addr.l2_psm = htobs(psm);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Set link mode */
+	opt = 0;
+	if (reliable)
+		opt |= L2CAP_LM_RELIABLE;
+	if (master)
+		opt |= L2CAP_LM_MASTER;
+	if (auth)
+		opt |= L2CAP_LM_AUTH;
+	if (encr)
+		opt |= L2CAP_LM_ENCRYPT;
+	if (secure)
+		opt |= L2CAP_LM_SECURE;
+
+	if (opt && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+		syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Get default options */
+	if (getopts(sk, &opts, false) < 0) {
+		syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Set new options */
+	opts.omtu = omtu;
+	opts.imtu = imtu;
+	if (rfcmode > 0)
+		opts.mode = rfcmode;
+
+	opts.fcs = fcs;
+	opts.txwin_size = txwin_size;
+	opts.max_tx = max_transmit;
+
+	if (setopts(sk, &opts) < 0) {
+		syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	if (socktype == SOCK_DGRAM) {
+		handler(sk);
+		close(sk);
+		return;
+	}
+
+	/* Enable deferred setup */
+	opt = defer_setup;
+
+	if (opt && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+						&opt, sizeof(opt)) < 0) {
+		syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+
+	/* Listen for connections */
+	if (listen(sk, 10)) {
+		syslog(LOG_ERR, "Can not listen on the socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Check for socket address */
+	memset(&addr, 0, sizeof(addr));
+	optlen = sizeof(addr);
+
+	if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	psm = btohs(addr.l2_psm);
+	cid = btohs(addr.l2_cid);
+
+	syslog(LOG_INFO, "Waiting for connection on psm %d ...", psm);
+
+	while (1) {
+		memset(&addr, 0, sizeof(addr));
+		optlen = sizeof(addr);
+
+		nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+		if (nsk < 0) {
+			syslog(LOG_ERR, "Accept failed: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+		if (fork()) {
+			/* Parent */
+			close(nsk);
+			continue;
+		}
+		/* Child */
+
+		/* Set receive buffer size */
+		if (rcvbuf && setsockopt(nsk, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
+							sizeof(rcvbuf)) < 0) {
+			syslog(LOG_ERR, "Can't set rcv buf size: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+
+		optlen = sizeof(rcvbuf);
+		if (getsockopt(nsk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen)
+									< 0) {
+			syslog(LOG_ERR, "Can't get rcv buf size: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+
+		/* Get current options */
+		if (getopts(nsk, &opts, true) < 0) {
+			syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
+							strerror(errno), errno);
+			if (!defer_setup) {
+				close(nsk);
+				goto error;
+			}
+		}
+
+		/* Get connection information */
+		memset(&conn, 0, sizeof(conn));
+		optlen = sizeof(conn);
+
+		if (getsockopt(nsk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
+			syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
+							strerror(errno), errno);
+			if (!defer_setup) {
+				close(nsk);
+				goto error;
+			}
+		}
+
+		if (priority > 0 && setsockopt(nsk, SOL_SOCKET, SO_PRIORITY,
+					&priority, sizeof(priority)) < 0) {
+			syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+						strerror(errno), errno);
+			close(nsk);
+			goto error;
+		}
+
+		optlen = sizeof(priority);
+		if (getsockopt(nsk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+			syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+
+		ba2str(&addr.l2_bdaddr, ba);
+		syslog(LOG_INFO, "Connect from %s (%s, psm %d, dcid %d)", ba,
+				get_lookup_str(bdaddr_types, addr.l2_bdaddr_type),
+				addr.l2_psm, addr.l2_cid);
+
+		/* Check for socket address */
+		memset(&addr, 0, sizeof(addr));
+		optlen = sizeof(addr);
+
+		if (getsockname(nsk, (struct sockaddr *) &addr, &optlen) < 0) {
+			syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+
+		ba2str(&addr.l2_bdaddr, ba);
+		syslog(LOG_INFO, "Local device %s (%s, psm %d, scid %d)", ba,
+				get_lookup_str(bdaddr_types, addr.l2_bdaddr_type),
+				addr.l2_psm, addr.l2_cid);
+
+		syslog(LOG_INFO, "Options [imtu %d, omtu %d, "
+				"flush_to %d, mode %d, handle %d, "
+				"class 0x%02x%02x%02x, priority %d, rcvbuf %d]",
+				opts.imtu, opts.omtu, opts.flush_to,
+				opts.mode, conn.hci_handle, conn.dev_class[2],
+				conn.dev_class[1], conn.dev_class[0], opt,
+				rcvbuf);
+
+		omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
+		imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
+
+#if 0
+		/* Enable SO_TIMESTAMP */
+		if (timestamp) {
+			int t = 1;
+
+			if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+				syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+							strerror(errno), errno);
+				goto error;
+			}
+		}
+#endif
+
+		/* Enable SO_LINGER */
+		if (linger) {
+			struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+			if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+				syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+							strerror(errno), errno);
+				close(nsk);
+				goto error;
+			}
+		}
+
+		/* Handle deferred setup */
+		if (defer_setup) {
+			syslog(LOG_INFO, "Waiting for %d seconds",
+							abs(defer_setup) - 1);
+			sleep(abs(defer_setup) - 1);
+
+			if (defer_setup < 0) {
+				close(nsk);
+				goto error;
+			}
+		}
+
+		handler(nsk);
+		close(sk);
+
+		syslog(LOG_INFO, "Disconnect: %m");
+		exit(0);
+	}
+
+error:
+	close(sk);
+	exit(1);
+}
+
+static void dump_mode(int sk)
+{
+	socklen_t optlen;
+	int opt, len;
+
+	if (data_size < 0)
+		data_size = imtu;
+
+	if (defer_setup) {
+		len = read(sk, buf, data_size);
+		if (len < 0)
+			syslog(LOG_ERR, "Initial read error: %s (%d)",
+						strerror(errno), errno);
+		else
+			syslog(LOG_INFO, "Initial bytes %d", len);
+	}
+
+	syslog(LOG_INFO, "Receiving ...");
+	while (1) {
+		fd_set rset;
+
+		FD_ZERO(&rset);
+		FD_SET(sk, &rset);
+
+		if (select(sk + 1, &rset, NULL, NULL, NULL) < 0)
+			return;
+
+		if (!FD_ISSET(sk, &rset))
+			continue;
+
+		len = read(sk, buf, data_size);
+		if (len <= 0) {
+			if (len < 0) {
+				if (reliable && (errno == ECOMM)) {
+					syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.");
+					optlen = sizeof(opt);
+					if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
+						syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
+							strerror(errno), errno);
+						return;
+					}
+					continue;
+				} else {
+					syslog(LOG_ERR, "Read error: %s(%d)",
+							strerror(errno), errno);
+				}
+			}
+			return;
+		}
+
+		syslog(LOG_INFO, "Recevied %d bytes", len);
+		hexdump(buf, len);
+	}
+}
+
+static void recv_mode(int sk)
+{
+	struct timeval tv_beg, tv_end, tv_diff;
+	struct pollfd p;
+	char ts[30];
+	long total;
+	uint32_t seq;
+	socklen_t optlen;
+	int opt, len;
+
+	if (data_size < 0)
+		data_size = imtu;
+
+	if (defer_setup) {
+		len = read(sk, buf, data_size);
+		if (len < 0)
+			syslog(LOG_ERR, "Initial read error: %s (%d)",
+						strerror(errno), errno);
+		else
+			syslog(LOG_INFO, "Initial bytes %d", len);
+	}
+
+	if (recv_delay)
+		usleep(recv_delay);
+
+	syslog(LOG_INFO, "Receiving ...");
+
+	memset(ts, 0, sizeof(ts));
+
+	p.fd = sk;
+	p.events = POLLIN | POLLERR | POLLHUP;
+
+	seq = 0;
+	while (1) {
+		gettimeofday(&tv_beg, NULL);
+		total = 0;
+		while (total < data_size) {
+			uint32_t sq;
+			uint16_t l;
+			int i;
+
+			p.revents = 0;
+			if (poll(&p, 1, -1) <= 0)
+				return;
+
+			if (p.revents & (POLLERR | POLLHUP))
+				return;
+
+			len = recv(sk, buf, data_size, 0);
+			if (len < 0) {
+				if (reliable && (errno == ECOMM)) {
+					syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.\n");
+					optlen = sizeof(opt);
+					if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
+						syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
+							strerror(errno), errno);
+						return;
+					}
+					continue;
+				} else {
+					syslog(LOG_ERR, "Read failed: %s (%d)",
+						strerror(errno), errno);
+				}
+			}
+
+			if (len < 6)
+				break;
+
+			if (timestamp) {
+				struct timeval tv;
+
+				if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
+					timestamp = 0;
+					memset(ts, 0, sizeof(ts));
+				} else {
+					sprintf(ts, "[%ld.%ld] ",
+							tv.tv_sec, tv.tv_usec);
+				}
+			}
+
+			/* Check sequence */
+			sq = get_le32(buf);
+			if (seq != sq) {
+				syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
+				seq = sq;
+			}
+			seq++;
+
+			/* Check length */
+			l = get_le16(buf + 4);
+			if (len != l) {
+				syslog(LOG_INFO, "size missmatch: %d -> %d", len, l);
+				continue;
+			}
+
+			/* Verify data */
+			for (i = 6; i < len; i++) {
+				if (buf[i] != 0x7f)
+					syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
+			}
+
+			total += len;
+		}
+		gettimeofday(&tv_end, NULL);
+
+		timersub(&tv_end, &tv_beg, &tv_diff);
+
+		syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
+			tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+	}
+}
+
+static void do_send(int sk)
+{
+	uint32_t seq;
+	int i, fd, len, buflen, size, sent;
+
+	syslog(LOG_INFO, "Sending ...");
+
+	if (data_size < 0)
+		data_size = omtu;
+
+	if (filename) {
+		fd = open(filename, O_RDONLY);
+		if (fd < 0) {
+			syslog(LOG_ERR, "Open failed: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		sent = 0;
+		size = read(fd, buf, data_size);
+		while (size > 0) {
+			buflen = (size > omtu) ? omtu : size;
+
+			len = send(sk, buf + sent, buflen, 0);
+
+			sent += len;
+			size -= len;
+		}
+
+		close(fd);
+		return;
+	} else {
+		for (i = 6; i < data_size; i++)
+			buf[i] = 0x7f;
+	}
+
+	if (!count && send_delay)
+		usleep(send_delay);
+
+	seq = seq_start;
+	while ((num_frames == -1) || (num_frames-- > 0)) {
+		put_le32(seq, buf);
+		put_le16(data_size, buf + 4);
+
+		seq++;
+
+		sent = 0;
+		size = data_size;
+		while (size > 0) {
+			buflen = (size > omtu) ? omtu : size;
+
+			len = send(sk, buf, buflen, 0);
+			if (len < 0 || len != buflen) {
+				syslog(LOG_ERR, "Send failed: %s (%d)",
+							strerror(errno), errno);
+				exit(1);
+			}
+
+			sent += len;
+			size -= len;
+		}
+
+		if (num_frames && send_delay && count &&
+						!(seq % (count + seq_start)))
+			usleep(send_delay);
+	}
+}
+
+static void send_mode(int sk)
+{
+	do_send(sk);
+
+	if (disc_delay)
+		usleep(disc_delay);
+
+	syslog(LOG_INFO, "Closing channel ...");
+	if (shutdown(sk, SHUT_RDWR) < 0)
+		syslog(LOG_INFO, "Close failed: %m");
+	else
+		syslog(LOG_INFO, "Done");
+}
+
+static void senddump_mode(int sk)
+{
+	do_send(sk);
+
+	dump_mode(sk);
+}
+
+static void send_and_recv_mode(int sk)
+{
+	int flags;
+
+	if ((flags = fcntl(sk, F_GETFL, 0)) < 0)
+		flags = 0;
+	fcntl(sk, F_SETFL, flags | O_NONBLOCK);
+
+	/* fork for duplex channel */
+	if (fork())
+		send_mode(sk);
+	else
+		recv_mode(sk);
+	return;
+}
+
+static void reconnect_mode(char *svr)
+{
+	while (1) {
+		int sk = do_connect(svr);
+		close(sk);
+	}
+}
+
+static void connect_mode(char *svr)
+{
+	struct pollfd p;
+	int sk;
+
+	if ((sk = do_connect(svr)) < 0)
+		exit(1);
+
+	p.fd = sk;
+	p.events = POLLERR | POLLHUP;
+
+	while (1) {
+		p.revents = 0;
+		if (poll(&p, 1, 500))
+			break;
+	}
+
+	syslog(LOG_INFO, "Disconnected");
+
+	close(sk);
+}
+
+static void multi_connect_mode(int argc, char *argv[])
+{
+	int i, n, sk;
+
+	while (1) {
+		for (n = 0; n < argc; n++) {
+			for (i = 0; i < count; i++) {
+				if (fork())
+					continue;
+
+				/* Child */
+				sk = do_connect(argv[n]);
+				usleep(500);
+				close(sk);
+				exit(0);
+			}
+		}
+		sleep(4);
+	}
+}
+
+static void info_request(char *svr)
+{
+	unsigned char buf[48];
+	l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
+	l2cap_info_req *req = (l2cap_info_req *) (buf + L2CAP_CMD_HDR_SIZE);
+	l2cap_info_rsp *rsp = (l2cap_info_rsp *) (buf + L2CAP_CMD_HDR_SIZE);
+	uint16_t mtu;
+	uint32_t channels, mask = 0x0000;
+	struct sockaddr_l2 addr;
+	int sk, err;
+
+	sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+	if (sk < 0) {
+		perror("Can't create socket");
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, &bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Can't bind socket");
+		goto failed;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	str2ba(svr, &addr.l2_bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+		perror("Can't connect socket");
+		goto failed;
+	}
+
+	memset(buf, 0, sizeof(buf));
+	cmd->code  = L2CAP_INFO_REQ;
+	cmd->ident = 141;
+	cmd->len   = htobs(2);
+	req->type  = htobs(0x0001);
+
+	if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+		perror("Can't send info request");
+		goto failed;
+	}
+
+	err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 2, 0);
+	if (err < 0) {
+		perror("Can't receive info response");
+		goto failed;
+	}
+
+	switch (btohs(rsp->result)) {
+	case 0x0000:
+		memcpy(&mtu, rsp->data, sizeof(mtu));
+		printf("Connectionless MTU size is %d\n", btohs(mtu));
+		break;
+	case 0x0001:
+		printf("Connectionless MTU is not supported\n");
+		break;
+	}
+
+	memset(buf, 0, sizeof(buf));
+	cmd->code  = L2CAP_INFO_REQ;
+	cmd->ident = 142;
+	cmd->len   = htobs(2);
+	req->type  = htobs(0x0002);
+
+	if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+		perror("Can't send info request");
+		goto failed;
+	}
+
+	err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 4, 0);
+	if (err < 0) {
+		perror("Can't receive info response");
+		goto failed;
+	}
+
+	switch (btohs(rsp->result)) {
+	case 0x0000:
+		memcpy(&mask, rsp->data, sizeof(mask));
+		printf("Extended feature mask is 0x%04x\n", btohl(mask));
+		if (mask & L2CAP_FEAT_FLOWCTL)
+			printf("  Flow control mode\n");
+		if (mask & L2CAP_FEAT_RETRANS)
+			printf("  Retransmission mode\n");
+		if (mask & L2CAP_FEAT_BIDIR_QOS)
+			printf("  Bi-directional QoS\n");
+		if (mask & L2CAP_FEAT_ERTM)
+			printf("  Enhanced Retransmission mode\n");
+		if (mask & L2CAP_FEAT_STREAMING)
+			printf("  Streaming mode\n");
+		if (mask & L2CAP_FEAT_FCS)
+			printf("  FCS Option\n");
+		if (mask & L2CAP_FEAT_EXT_FLOW)
+			printf("  Extended Flow Specification\n");
+		if (mask & L2CAP_FEAT_FIXED_CHAN)
+			printf("  Fixed Channels\n");
+		if (mask & L2CAP_FEAT_EXT_WINDOW)
+			printf("  Extended Window Size\n");
+		if (mask & L2CAP_FEAT_UCD)
+			printf("  Unicast Connectionless Data Reception\n");
+		break;
+	case 0x0001:
+		printf("Extended feature mask is not supported\n");
+		break;
+	}
+
+	if (!(mask & 0x80))
+		goto failed;
+
+	memset(buf, 0, sizeof(buf));
+	cmd->code  = L2CAP_INFO_REQ;
+	cmd->ident = 143;
+	cmd->len   = htobs(2);
+	req->type  = htobs(0x0003);
+
+	if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+		perror("Can't send info request");
+		goto failed;
+	}
+
+	err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 8, 0);
+	if (err < 0) {
+		perror("Can't receive info response");
+		goto failed;
+	}
+
+	switch (btohs(rsp->result)) {
+	case 0x0000:
+		memcpy(&channels, rsp->data, sizeof(channels));
+		printf("Fixed channels list is 0x%04x\n", btohl(channels));
+		break;
+	case 0x0001:
+		printf("Fixed channels list is not supported\n");
+		break;
+	}
+
+failed:
+	close(sk);
+}
+
+static void do_pairing(char *svr)
+{
+	struct sockaddr_l2 addr;
+	int sk, opt;
+
+	sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+	if (sk < 0) {
+		perror("Can't create socket");
+		return;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, &bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("Can't bind socket");
+		goto failed;
+	}
+
+	if (secure)
+		opt = L2CAP_LM_SECURE;
+	else
+		opt = L2CAP_LM_ENCRYPT;
+
+	if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+		perror("Can't set link mode");
+		goto failed;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	str2ba(svr, &addr.l2_bdaddr);
+	addr.l2_bdaddr_type = bdaddr_type;
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+		perror("Can't connect socket");
+		goto failed;
+	}
+
+	printf("Pairing successful\n");
+
+failed:
+	close(sk);
+}
+
+static void usage(void)
+{
+	printf("l2test - L2CAP testing\n"
+		"Usage:\n");
+	printf("\tl2test <mode> [options] [bdaddr]\n");
+	printf("Modes:\n"
+		"\t-r listen and receive\n"
+		"\t-w listen and send\n"
+		"\t-d listen and dump incoming data\n"
+		"\t-x listen, then send, then dump incoming data\n"
+		"\t-t listen, then send and receive at the same time\n"
+		"\t-q connect, then send and receive at the same time\n"
+		"\t-s connect and send\n"
+		"\t-u connect and receive\n"
+		"\t-n connect and be silent\n"
+		"\t-y connect, then send, then dump incoming data\n"
+		"\t-c connect, disconnect, connect, ...\n"
+		"\t-m multiple connects\n"
+		"\t-p trigger dedicated bonding\n"
+		"\t-z information request\n");
+
+	printf("Options:\n"
+		"\t[-b bytes] [-i device] [-P psm] [-J cid]\n"
+		"\t[-I imtu] [-O omtu]\n"
+		"\t[-L seconds] enable SO_LINGER\n"
+		"\t[-W seconds] enable deferred setup\n"
+		"\t[-B filename] use data packets from file\n"
+		"\t[-N num] send num frames (default = infinite)\n"
+		"\t[-C num] send num frames before delay (default = 1)\n"
+		"\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+		"\t[-K milliseconds] delay before receiving (default = 0)\n"
+		"\t[-g milliseconds] delay before disconnecting (default = 0)\n"
+		"\t[-X mode] l2cap mode (help for list, default = basic)\n"
+		"\t[-a policy] chan policy (help for list, default = bredr)\n"
+		"\t[-F fcs] use CRC16 check (default = 1)\n"
+		"\t[-Q num] Max Transmit value (default = 3)\n"
+		"\t[-Z size] Transmission Window size (default = 63)\n"
+		"\t[-Y priority] socket priority\n"
+		"\t[-H size] Maximum receive buffer size\n"
+		"\t[-R] reliable mode\n"
+		"\t[-G] use connectionless channel (datagram)\n"
+		"\t[-U] use sock stream\n"
+		"\t[-A] request authentication\n"
+		"\t[-E] request encryption\n"
+		"\t[-S] secure connection\n"
+		"\t[-M] become master\n"
+		"\t[-T] enable timestamps\n"
+		"\t[-V type] address type (help for list, default = bredr)\n"
+		"\t[-e seq] initial sequence value (default = 0)\n");
+}
+
+int main(int argc, char *argv[])
+{
+	struct sigaction sa;
+	int opt, sk, mode = RECV, need_addr = 0;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while ((opt = getopt(argc, argv, "a:b:cde:g:i:mnpqrstuwxyz"
+		"AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) {
+		switch (opt) {
+		case 'r':
+			mode = RECV;
+			break;
+
+		case 's':
+			mode = SEND;
+			need_addr = 1;
+			break;
+
+		case 'w':
+			mode = LSEND;
+			break;
+
+		case 'u':
+			mode = CRECV;
+			need_addr = 1;
+			break;
+
+		case 'd':
+			mode = DUMP;
+			break;
+
+		case 'c':
+			mode = RECONNECT;
+			need_addr = 1;
+			break;
+
+		case 'n':
+			mode = CONNECT;
+			need_addr = 1;
+			break;
+
+		case 'm':
+			mode = MULTY;
+			need_addr = 1;
+			break;
+
+		case 't':
+			mode = LSENDRECV;
+			break;
+
+		case 'q':
+			mode = CSENDRECV;
+			need_addr = 1;
+			break;
+
+		case 'x':
+			mode = LSENDDUMP;
+			break;
+
+		case 'y':
+			mode = SENDDUMP;
+			break;
+
+		case 'z':
+			mode = INFOREQ;
+			need_addr = 1;
+			break;
+
+		case 'p':
+			mode = PAIRING;
+			need_addr = 1;
+			break;
+
+		case 'b':
+			data_size = atoi(optarg);
+			break;
+
+		case 'i':
+			if (!strncasecmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &bdaddr);
+			else
+				str2ba(optarg, &bdaddr);
+			break;
+
+		case 'P':
+			psm = atoi(optarg);
+			break;
+
+		case 'I':
+			imtu = atoi(optarg);
+			break;
+
+		case 'O':
+			omtu = atoi(optarg);
+			break;
+
+		case 'L':
+			linger = atoi(optarg);
+			break;
+
+		case 'W':
+			defer_setup = atoi(optarg);
+			break;
+
+		case 'B':
+			filename = optarg;
+			break;
+
+		case 'N':
+			num_frames = atoi(optarg);
+			break;
+
+		case 'C':
+			count = atoi(optarg);
+			break;
+
+		case 'D':
+			send_delay = atoi(optarg) * 1000;
+			break;
+
+		case 'K':
+			recv_delay = atoi(optarg) * 1000;
+			break;
+
+		case 'X':
+			rfcmode = get_lookup_flag(l2cap_modes, optarg);
+
+			if (rfcmode == -1) {
+				print_lookup_values(l2cap_modes,
+						"List L2CAP modes:");
+				exit(1);
+			}
+
+			break;
+
+		case 'a':
+			chan_policy = get_lookup_flag(chan_policies, optarg);
+
+			if (chan_policy == -1) {
+				print_lookup_values(chan_policies,
+						"List L2CAP chan policies:");
+				exit(1);
+			}
+
+			break;
+
+		case 'Y':
+			priority = atoi(optarg);
+			break;
+
+		case 'F':
+			fcs = atoi(optarg);
+			break;
+
+		case 'R':
+			reliable = 1;
+			break;
+
+		case 'M':
+			master = 1;
+			break;
+
+		case 'A':
+			auth = 1;
+			break;
+
+		case 'E':
+			encr = 1;
+			break;
+
+		case 'S':
+			secure = 1;
+			break;
+
+		case 'G':
+			socktype = SOCK_DGRAM;
+			break;
+
+		case 'U':
+			socktype = SOCK_STREAM;
+			break;
+
+		case 'T':
+			timestamp = 1;
+			break;
+
+		case 'Q':
+			max_transmit = atoi(optarg);
+			break;
+
+		case 'Z':
+			txwin_size = atoi(optarg);
+			break;
+
+		case 'J':
+			cid = atoi(optarg);
+			break;
+
+		case 'H':
+			rcvbuf = atoi(optarg);
+			break;
+
+		case 'V':
+			bdaddr_type = get_lookup_flag(bdaddr_types, optarg);
+
+			if (bdaddr_type == -1) {
+				print_lookup_values(bdaddr_types,
+						"List Address types:");
+				exit(1);
+			}
+
+			break;
+
+		case 'e':
+			seq_start = atoi(optarg);
+			break;
+
+		case 'g':
+			disc_delay = atoi(optarg) * 1000;
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	if (!psm) {
+		if (bdaddr_type == BDADDR_BREDR)
+			psm = BREDR_DEFAULT_PSM;
+		else
+			psm = LE_DEFAULT_PSM;
+	}
+
+	if (need_addr && !(argc - optind)) {
+		usage();
+		exit(1);
+	}
+
+	if (data_size < 0)
+		buffer_size = (omtu > imtu) ? omtu : imtu;
+	else
+		buffer_size = data_size;
+
+	if (!(buf = malloc(buffer_size))) {
+		perror("Can't allocate data buffer");
+		exit(1);
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = SIG_IGN;
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sigaction(SIGCHLD, &sa, NULL);
+
+	openlog("l2test", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+	switch (mode) {
+		case RECV:
+			do_listen(recv_mode);
+			break;
+
+		case CRECV:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			recv_mode(sk);
+			break;
+
+		case DUMP:
+			do_listen(dump_mode);
+			break;
+
+		case SEND:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			send_mode(sk);
+			break;
+
+		case LSEND:
+			do_listen(send_mode);
+			break;
+
+		case RECONNECT:
+			reconnect_mode(argv[optind]);
+			break;
+
+		case MULTY:
+			multi_connect_mode(argc - optind, argv + optind);
+			break;
+
+		case CONNECT:
+			connect_mode(argv[optind]);
+			break;
+
+		case SENDDUMP:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			senddump_mode(sk);
+			break;
+
+		case LSENDDUMP:
+			do_listen(senddump_mode);
+			break;
+
+		case LSENDRECV:
+			do_listen(send_and_recv_mode);
+			break;
+
+		case CSENDRECV:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+
+			send_and_recv_mode(sk);
+			break;
+
+		case INFOREQ:
+			info_request(argv[optind]);
+			exit(0);
+
+		case PAIRING:
+			do_pairing(argv[optind]);
+			exit(0);
+	}
+
+	syslog(LOG_INFO, "Exit");
+
+	closelog();
+
+	return 0;
+}
diff --git a/bluez/repo/tools/magic.btsnoop b/bluez/repo/tools/magic.btsnoop
new file mode 100644
index 0000000..ebb845d
--- /dev/null
+++ b/bluez/repo/tools/magic.btsnoop
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# BTSnoop:  file(1) magic for BTSnoop files
+#
+# From <marcel@holtmann.org>
+0	string		btsnoop\0		BTSnoop
+>8	belong		x			version %ld,
+>>8	belong		!1
+>>>12	belong		x			type %ld
+>8	belong		=1
+>>12	belong		<1001			type %ld
+>>12	belong		>1000
+>>>12	belong		=1001			Unencapsulated HCI
+>>>12	belong		=1002			HCI UART (H4)
+>>>12	belong		=1003			HCI BCSP
+>>>12	belong		=1004			HCI Serial (H5)
+>>>12	belong		>1004
+>>>>12	belong		<2001			type %ld
+>>>>12	belong		=2001			Bluetooth monitor
+>>>>12	belong		=2002			Bluetooth simulator
+>>>>12	belong		>2002			type %ld
diff --git a/bluez/repo/tools/mcaptest.c b/bluez/repo/tools/mcaptest.c
new file mode 100644
index 0000000..3092873
--- /dev/null
+++ b/bluez/repo/tools/mcaptest.c
@@ -0,0 +1,496 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "btio/btio.h"
+#include "lib/l2cap.h"
+#include "profiles/health/mcap.h"
+
+enum {
+	MODE_NONE,
+	MODE_CONNECT,
+	MODE_LISTEN,
+};
+
+static GMainLoop *mloop;
+
+static int ccpsm = 0x1003, dcpsm = 0x1005;
+
+static struct mcap_instance *mcap = NULL;
+static struct mcap_mdl *mdl = NULL;
+static uint16_t mdlid;
+
+static int control_mode = MODE_LISTEN;
+static int data_mode = MODE_LISTEN;
+
+static int mdl_conn_req_result = MCAP_SUCCESS;
+
+static gboolean send_synccap_req = FALSE;
+static gboolean mcl_disconnect = FALSE;
+static gboolean mdl_disconnect = FALSE;
+static int mcl_disconnect_timeout = -1;
+static int mdl_disconnect_timeout = -1;
+
+static struct mcap_mcl *mcl = NULL;
+
+static gboolean no_close = FALSE;
+
+#define REQ_CLOCK_ACC 0x1400
+
+static void mdl_close(struct mcap_mdl *mdl)
+{
+	int fd = -1;
+
+	printf("%s\n", __func__);
+
+	if (mdl_disconnect_timeout >= 0)
+		sleep(mdl_disconnect_timeout);
+
+	fd = mcap_mdl_get_fd(mdl);
+
+	if (fd > 0)
+		close(fd);
+}
+
+static void mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+	printf("%s\n", __func__);
+
+	if (mdl_disconnect)
+		mdl_close(mdl);
+}
+
+static void mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+	printf("%s\n", __func__);
+
+	if (mcl_disconnect && mcl_disconnect_timeout >= 0) {
+		sleep(mcl_disconnect_timeout);
+
+		printf("Closing MCAP communication link\n");
+		mcap_close_mcl(mcl, TRUE);
+
+		if (no_close)
+			return;
+
+		g_main_loop_quit(mloop);
+	}
+}
+
+static void mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+	/* TODO */
+	printf("%s\n", __func__);
+
+	/* Disconnecting MDL latency timeout */
+	if (mdl_disconnect_timeout >= 0)
+		sleep(mdl_disconnect_timeout);
+}
+
+static void mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+	/* TODO */
+	printf("%s\n", __func__);
+}
+
+static uint8_t mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+				uint16_t mdlid, uint8_t *conf, void *data)
+{
+	int ret;
+
+	printf("%s\n", __func__);
+
+	ret = mdl_conn_req_result;
+
+	mdl_conn_req_result = MCAP_SUCCESS;
+
+	return ret;
+}
+
+static uint8_t mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+	printf("%s\n", __func__);
+
+	return MCAP_SUCCESS;
+}
+
+static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
+								gpointer data);
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	GError *gerr = NULL;
+
+	printf("%s\n", __func__);
+
+	if (data_mode == MODE_CONNECT) {
+		mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
+		if (gerr) {
+			printf("Could not connect MDL: %s\n", gerr->message);
+			g_error_free(gerr);
+		}
+	}
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+	/* TODO */
+	printf("%s\n", __func__);
+
+	if (no_close)
+		return;
+
+	g_main_loop_quit(mloop);
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+	/* TODO */
+	printf("%s\n", __func__);
+}
+
+static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
+{
+	mdlid = mcap_mdl_get_mdlid(mdl);
+
+	printf("%s\n", __func__);
+
+	if (mdlid == MCAP_MDLID_RESERVED)
+		printf("MCAP mdlid is reserved");
+	else
+		printf("MDL %d connected\n", mdlid);
+}
+
+static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
+								gpointer data)
+{
+	GError *err = NULL;
+
+	printf("%s\n", __func__);
+
+	if (gerr) {
+		printf("MDL error: %s\n", gerr->message);
+
+		if (!no_close)
+			g_main_loop_quit(mloop);
+
+		return;
+	}
+
+	if (mdl)
+		mcap_mdl_unref(mdl);
+
+	mdl = mcap_mdl_ref(mcap_mdl);
+
+	if (!mcap_connect_mdl(mdl, L2CAP_MODE_ERTM, dcpsm, connect_mdl_cb, NULL,
+								NULL, &err)) {
+		printf("Error connecting to mdl: %s\n", err->message);
+		g_error_free(err);
+
+		if (no_close)
+			return;
+
+		g_main_loop_quit(mloop);
+	}
+}
+
+static void sync_cap_cb(struct mcap_mcl *mcl, uint8_t mcap_err,
+			uint8_t btclockres, uint16_t synclead,
+			uint16_t tmstampres, uint16_t tmstampacc, GError *err,
+			gpointer data)
+{
+	/* TODO */
+	printf("%s\n", __func__);
+}
+
+static void trigger_mdl_action(int mode)
+{
+	GError *gerr = NULL;
+	gboolean ret;
+
+	ret = mcap_mcl_set_cb(mcl, NULL, &gerr,
+		MCAP_MDL_CB_CONNECTED, mdl_connected_cb,
+		MCAP_MDL_CB_CLOSED, mdl_closed_cb,
+		MCAP_MDL_CB_DELETED, mdl_deleted_cb,
+		MCAP_MDL_CB_ABORTED, mdl_aborted_cb,
+		MCAP_MDL_CB_REMOTE_CONN_REQ, mdl_conn_req_cb,
+		MCAP_MDL_CB_REMOTE_RECONN_REQ, mdl_reconn_req_cb,
+		MCAP_MDL_CB_INVALID);
+
+	if (!ret && gerr) {
+		printf("MCL cannot handle connection %s\n",
+							gerr->message);
+		g_error_free(gerr);
+	}
+
+	if (mode == MODE_CONNECT) {
+		printf("Creating MCAP Data End Point\n");
+		mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
+		if (gerr) {
+			printf("Could not connect MDL: %s\n", gerr->message);
+			g_error_free(gerr);
+		}
+	}
+
+	if (send_synccap_req && mcap->csp_enabled) {
+		mcap_sync_init(mcl);
+
+		mcap_sync_cap_req(mcl, REQ_CLOCK_ACC, sync_cap_cb, NULL, &gerr);
+		if (gerr) {
+			printf("MCAP Sync req error: %s\n", gerr->message);
+			g_error_free(gerr);
+		}
+	}
+}
+
+static void mcl_connected(struct mcap_mcl *mcap_mcl, gpointer data)
+{
+	printf("%s\n", __func__);
+
+	if (mcl) {
+		mcap_sync_stop(mcl);
+		mcap_mcl_unref(mcl);
+	}
+
+	mcl = mcap_mcl_ref(mcap_mcl);
+	trigger_mdl_action(data_mode);
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcap_mcl, GError *err, gpointer data)
+{
+	printf("%s\n", __func__);
+
+	if (err) {
+		printf("Could not connect MCL: %s\n", err->message);
+
+		if (!no_close)
+			g_main_loop_quit(mloop);
+
+		return;
+	}
+
+	if (mcl) {
+		mcap_sync_stop(mcl);
+		mcap_mcl_unref(mcl);
+	}
+
+	mcl = mcap_mcl_ref(mcap_mcl);
+	trigger_mdl_action(data_mode);
+}
+
+static void usage(void)
+{
+	printf("mcaptest - MCAP testing ver %s\n", VERSION);
+	printf("Usage:\n"
+		"\tmcaptest <control_mode> <data_mode> [options]\n");
+	printf("Control Link Mode:\n"
+		"\t-c connect <dst_addr>\n"
+		"\t-b close control link after closing data link\n"
+		"\t-e <timeout> disconnect MCL and quit after MDL is closed\n"
+		"\t-g send clock sync capability request if MCL connected\n");
+	printf("Data Link Mode:\n"
+		"\t-d connect\n"
+		"\t-a close data link immediately after being connected"
+		"\t-f <timeout> disconnect MDL after it's connected\n"
+		"\t-u send \'Unavailable\' on first MDL connection request\n");
+	printf("Options:\n"
+		"\t-n don't exit after mcl disconnect/err receive\n"
+		"\t-i <hcidev>        HCI device\n"
+		"\t-C <control_ch>    Control channel PSM\n"
+		"\t-D <data_ch>       Data channel PSM\n");
+}
+
+static struct option main_options[] = {
+	{ "help",		0, 0, 'h' },
+	{ "device",		1, 0, 'i' },
+	{ "connect_cl",		1, 0, 'c' },
+	{ "disconnect_cl",	1, 0, 'e' },
+	{ "synccap_req",	0, 0, 'g' },
+	{ "connect_dl",		0, 0, 'd' },
+	{ "disconnect_da",	0, 0, 'a' },
+	{ "disconnect_ca",	0, 0, 'b' },
+	{ "disconnect_dl",	1, 0, 'f' },
+	{ "unavailable_dl",	0, 0, 'u' },
+	{ "no exit mcl dis/err",0, 0, 'n' },
+	{ "control_ch",		1, 0, 'C' },
+	{ "data_ch",		1, 0, 'D' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	GError *err = NULL;
+	bdaddr_t src, dst;
+	int opt;
+	char bdastr[18];
+
+	hci_devba(0, &src);
+	bacpy(&dst, BDADDR_ANY);
+
+	mloop = g_main_loop_new(NULL, FALSE);
+	if (!mloop) {
+		printf("Cannot create main loop\n");
+
+		exit(1);
+	}
+
+	while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghunab",
+						main_options, NULL)) != EOF) {
+		switch (opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &src);
+			else
+				str2ba(optarg, &src);
+
+			break;
+
+		case 'c':
+			control_mode = MODE_CONNECT;
+			str2ba(optarg, &dst);
+
+			break;
+
+		case 'd':
+			data_mode = MODE_CONNECT;
+
+			break;
+
+		case 'a':
+			mdl_disconnect = TRUE;
+
+			break;
+
+		case 'b':
+			mcl_disconnect = TRUE;
+
+			break;
+
+		case 'e':
+			mcl_disconnect_timeout = atoi(optarg);
+
+			break;
+
+		case 'f':
+			mdl_disconnect_timeout = atoi(optarg);
+
+			break;
+
+		case 'g':
+			send_synccap_req = TRUE;
+
+			break;
+
+		case 'u':
+			mdl_conn_req_result = MCAP_RESOURCE_UNAVAILABLE;
+
+			break;
+
+		case 'n':
+			no_close = TRUE;
+
+			break;
+
+		case 'C':
+			ccpsm = atoi(optarg);
+
+			break;
+
+		case 'D':
+			dcpsm = atoi(optarg);
+
+			break;
+
+		case 'h':
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	mcap = mcap_create_instance(&src, BT_IO_SEC_MEDIUM, ccpsm, dcpsm,
+					mcl_connected, mcl_reconnected,
+					mcl_disconnected, mcl_uncached,
+					NULL, /* CSP is not used right now */
+					NULL, &err);
+
+	if (!mcap) {
+		printf("MCAP instance creation failed %s\n", err->message);
+		g_error_free(err);
+
+		exit(1);
+	}
+
+	mcap_enable_csp(mcap);
+
+	switch (control_mode) {
+	case MODE_CONNECT:
+		ba2str(&dst, bdastr);
+		printf("Connecting to %s\n", bdastr);
+
+		mcap_create_mcl(mcap, &dst, ccpsm, create_mcl_cb, NULL, NULL,
+									&err);
+
+		if (err) {
+			printf("MCAP create error %s\n", err->message);
+			g_error_free(err);
+
+			exit(1);
+		}
+
+		break;
+	case MODE_LISTEN:
+		printf("Listening for control channel connection\n");
+
+		break;
+	case MODE_NONE:
+	default:
+		goto done;
+	}
+
+	g_main_loop_run(mloop);
+
+done:
+	printf("Done\n");
+
+	if (mcap)
+		mcap_instance_unref(mcap);
+
+	g_main_loop_unref(mloop);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/mgmt-tester.c b/bluez/repo/tools/mgmt-tester.c
new file mode 100644
index 0000000..494d436
--- /dev/null
+++ b/bluez/repo/tools/mgmt-tester.c
@@ -0,0 +1,7035 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/ioctl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+
+struct test_data {
+	tester_data_func_t test_setup;
+	const void *test_data;
+	uint8_t expected_version;
+	uint16_t expected_manufacturer;
+	uint32_t expected_supported_settings;
+	uint32_t initial_settings;
+	struct mgmt *mgmt;
+	struct mgmt *mgmt_alt;
+	unsigned int mgmt_settings_id;
+	unsigned int mgmt_alt_settings_id;
+	unsigned int mgmt_alt_ev_id;
+	unsigned int mgmt_discov_ev_id;
+	uint8_t mgmt_version;
+	uint16_t mgmt_revision;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	int unmet_conditions;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_version_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_version *rp = param;
+
+	tester_print("Read Version callback");
+	tester_print("  Status: %s (0x%02x)", mgmt_errstr(status), status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	data->mgmt_version = rp->version;
+	data->mgmt_revision = btohs(rp->revision);
+
+	tester_print("  Version %u.%u",
+				data->mgmt_version, data->mgmt_revision);
+}
+
+static void read_commands_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	tester_print("Read Commands callback");
+	tester_print("  Status: %s (0x%02x)", mgmt_errstr(status), status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+	struct bthost *bthost;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: %s (0x%02x)", mgmt_errstr(status), status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (rp->version != data->expected_version) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (manufacturer != data->expected_manufacturer) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (supported_settings != data->expected_supported_settings) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (current_settings != data->initial_settings) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (rp->dev_class[0] != 0x00 || rp->dev_class[1] != 0x00 ||
+						rp->dev_class[2] != 0x00) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_notify_ready(bthost, tester_pre_setup_complete);
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+	mgmt_unregister_index(data->mgmt_alt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	mgmt_unref(data->mgmt_alt);
+	data->mgmt_alt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: %s (0x%02x)", mgmt_errstr(status), status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+	}
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	data->mgmt_alt = mgmt_new_default();
+	if (!data->mgmt_alt) {
+		tester_warn("Failed to setup alternate management interface");
+		tester_pre_setup_failed();
+
+		mgmt_unref(data->mgmt);
+		data->mgmt = NULL;
+		return;
+	}
+
+	if (tester_use_debug()) {
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+		mgmt_set_debug(data->mgmt_alt, mgmt_debug, "mgmt-alt: ", NULL);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL,
+					read_version_callback, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE, 0, NULL,
+					read_commands_callback, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_add_condition(struct test_data *data)
+{
+	data->unmet_conditions++;
+
+	tester_print("Test condition added, total %d", data->unmet_conditions);
+}
+
+static void test_condition_complete(struct test_data *data)
+{
+	data->unmet_conditions--;
+
+	tester_print("Test condition complete, %d left",
+						data->unmet_conditions);
+
+	if (data->unmet_conditions > 0)
+		return;
+
+	tester_test_passed();
+}
+
+#define test_bredrle_full(name, data, setup, func, timeout) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
+		user->test_setup = setup; \
+		user->test_data = data; \
+		user->expected_version = 0x08; \
+		user->expected_manufacturer = 0x003f; \
+		user->expected_supported_settings = 0x0000bfff; \
+		user->initial_settings = 0x00000080; \
+		user->unmet_conditions = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, test_setup, func, NULL, \
+				test_post_teardown, timeout, user, free); \
+	} while (0)
+
+#define test_bredrle(name, data, setup, func) \
+	test_bredrle_full(name, data, setup, func, 2)
+
+#define test_bredr20(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_LEGACY; \
+		user->test_setup = setup; \
+		user->test_data = data; \
+		user->expected_version = 0x03; \
+		user->expected_manufacturer = 0x003f; \
+		user->expected_supported_settings = 0x000010bf; \
+		user->initial_settings = 0x00000080; \
+		user->unmet_conditions = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, test_setup, func, NULL, \
+				test_post_teardown, 2, user, free); \
+	} while (0)
+
+#define test_bredr(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDR; \
+		user->test_setup = setup; \
+		user->test_data = data; \
+		user->expected_version = 0x05; \
+		user->expected_manufacturer = 0x003f; \
+		user->expected_supported_settings = 0x000011ff; \
+		user->initial_settings = 0x00000080; \
+		user->unmet_conditions = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, test_setup, func, NULL, \
+				test_post_teardown, 2, user, free); \
+	} while (0)
+
+#define test_le(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_LE; \
+		user->test_setup = setup; \
+		user->test_data = data; \
+		user->expected_version = 0x08; \
+		user->expected_manufacturer = 0x003f; \
+		user->expected_supported_settings = 0x0000be1b; \
+		user->initial_settings = 0x00000200; \
+		user->unmet_conditions = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, test_setup, func, NULL, \
+				test_post_teardown, 2, user, free); \
+	} while (0)
+
+static void controller_setup(const void *test_data)
+{
+	tester_test_passed();
+}
+
+struct generic_data {
+	const uint16_t *setup_settings;
+	bool setup_nobredr;
+	bool setup_limited_discov;
+	uint16_t setup_expect_hci_command;
+	const void *setup_expect_hci_param;
+	uint8_t setup_expect_hci_len;
+	uint16_t setup_send_opcode;
+	const void *setup_send_param;
+	uint16_t setup_send_len;
+	bool send_index_none;
+	uint16_t send_opcode;
+	const void *send_param;
+	uint16_t send_len;
+	const void * (*send_func)(uint16_t *len);
+	uint8_t expect_status;
+	bool expect_ignore_param;
+	const void *expect_param;
+	uint16_t expect_len;
+	const void * (*expect_func)(uint16_t *len);
+	uint32_t expect_settings_set;
+	uint32_t expect_settings_unset;
+	uint16_t expect_alt_ev;
+	const void *expect_alt_ev_param;
+	bool (*verify_alt_ev_func)(const void *param, uint16_t length);
+	uint16_t expect_alt_ev_len;
+	uint16_t expect_hci_command;
+	const void *expect_hci_param;
+	uint8_t expect_hci_len;
+	const void * (*expect_hci_func)(uint8_t *len);
+	bool expect_pin;
+	uint8_t pin_len;
+	const void *pin;
+	uint8_t client_pin_len;
+	const void *client_pin;
+	bool client_enable_ssp;
+	uint8_t io_cap;
+	uint8_t client_io_cap;
+	uint8_t client_auth_req;
+	bool reject_confirm;
+	bool client_reject_confirm;
+	bool just_works;
+	bool client_enable_le;
+	bool client_enable_sc;
+	bool expect_sc_key;
+	bool force_power_off;
+	bool addr_type_avail;
+	uint8_t addr_type;
+	bool set_adv;
+	const uint8_t *adv_data;
+	uint8_t adv_data_len;
+};
+
+static const char dummy_data[] = { 0x00 };
+
+static const struct generic_data invalid_command_test = {
+	.send_opcode = 0xffff,
+	.expect_status = MGMT_STATUS_UNKNOWN_COMMAND,
+};
+
+static const struct generic_data read_version_success_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_VERSION,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_len = 3,
+};
+
+static const struct generic_data read_version_invalid_param_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_VERSION,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_version_invalid_index_test = {
+	.send_opcode = MGMT_OP_READ_VERSION,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_commands_invalid_param_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_COMMANDS,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_commands_invalid_index_test = {
+	.send_opcode = MGMT_OP_READ_COMMANDS,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_index_list_invalid_param_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_INDEX_LIST,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_index_list_invalid_index_test = {
+	.send_opcode = MGMT_OP_READ_INDEX_LIST,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_info_invalid_param_test = {
+	.send_opcode = MGMT_OP_READ_INFO,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_info_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_INFO,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_unconf_index_list_invalid_param_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_unconf_index_list_invalid_index_test = {
+	.send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_config_info_invalid_param_test = {
+	.send_opcode = MGMT_OP_READ_CONFIG_INFO,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_config_info_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_CONFIG_INFO,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_ext_index_list_invalid_param_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_ext_index_list_invalid_index_test = {
+	.send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const char set_powered_on_param[] = { 0x01 };
+static const char set_powered_invalid_param[] = { 0x02 };
+static const char set_powered_garbage_param[] = { 0x01, 0x00 };
+static const char set_powered_settings_param[] = { 0x81, 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_powered_on_success_test = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_settings_param,
+	.expect_len = sizeof(set_powered_settings_param),
+	.expect_settings_set = MGMT_SETTING_POWERED,
+};
+
+static const struct generic_data set_powered_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_powered_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_invalid_param,
+	.send_len = sizeof(set_powered_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_powered_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_garbage_param,
+	.send_len = sizeof(set_powered_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_powered_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static uint16_t settings_powered_advertising_privacy[] = {
+						MGMT_OP_SET_PRIVACY,
+						MGMT_OP_SET_ADVERTISING,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const char set_adv_off_param[] = { 0x00 };
+
+static const struct generic_data set_powered_on_privacy_adv_test = {
+	.setup_settings = settings_powered_advertising_privacy,
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_off_param,
+	.send_len = sizeof(set_adv_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_ignore_param = true,
+};
+
+static const uint16_t settings_powered[] = { MGMT_OP_SET_POWERED, 0 };
+
+static const char set_powered_off_param[] = { 0x00 };
+static const char set_powered_off_settings_param[] = { 0x80, 0x00, 0x00, 0x00 };
+static const char set_powered_off_class_of_dev[] = { 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_powered_off_success_test = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_off_param,
+	.send_len = sizeof(set_powered_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_off_settings_param,
+	.expect_len = sizeof(set_powered_off_settings_param),
+	.expect_settings_unset = MGMT_SETTING_POWERED,
+};
+
+static const struct generic_data set_powered_off_class_test = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_off_param,
+	.send_len = sizeof(set_powered_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_off_settings_param,
+	.expect_len = sizeof(set_powered_off_settings_param),
+	.expect_settings_unset = MGMT_SETTING_POWERED,
+	.expect_alt_ev = MGMT_EV_CLASS_OF_DEV_CHANGED,
+	.expect_alt_ev_param = set_powered_off_class_of_dev,
+	.expect_alt_ev_len = sizeof(set_powered_off_class_of_dev),
+};
+
+static const struct generic_data set_powered_off_invalid_param_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_powered_off_invalid_param_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_invalid_param,
+	.send_len = sizeof(set_powered_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_powered_off_invalid_param_test_3 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_garbage_param,
+	.send_len = sizeof(set_powered_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char set_connectable_on_param[] = { 0x01 };
+static const char set_connectable_invalid_param[] = { 0x02 };
+static const char set_connectable_garbage_param[] = { 0x01, 0x00 };
+static const char set_connectable_settings_param_1[] = { 0x82, 0x00, 0x00, 0x00 };
+static const char set_connectable_settings_param_2[] = { 0x83, 0x00, 0x00, 0x00 };
+static const char set_connectable_scan_enable_param[] = { 0x02 };
+
+static const struct generic_data set_connectable_on_success_test_1 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_settings_param_1,
+	.expect_len = sizeof(set_connectable_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_CONNECTABLE,
+};
+
+static const struct generic_data set_connectable_on_success_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_settings_param_2,
+	.expect_len = sizeof(set_connectable_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_connectable_scan_enable_param,
+	.expect_hci_len = sizeof(set_connectable_scan_enable_param),
+};
+
+static const struct generic_data set_connectable_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_connectable_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_invalid_param,
+	.send_len = sizeof(set_connectable_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_connectable_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_garbage_param,
+	.send_len = sizeof(set_connectable_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_connectable_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static uint16_t settings_powered_advertising[] = { MGMT_OP_SET_ADVERTISING,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const char set_connectable_le_settings_param_1[] = { 0x02, 0x02, 0x00, 0x00 };
+static const char set_connectable_le_settings_param_2[] = { 0x03, 0x02, 0x00, 0x00 };
+static const char set_connectable_le_settings_param_3[] = { 0x03, 0x06, 0x00, 0x00 };
+
+static const struct generic_data set_connectable_on_le_test_1 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_le_settings_param_1,
+	.expect_len = sizeof(set_connectable_le_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_CONNECTABLE,
+};
+
+static const struct generic_data set_connectable_on_le_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_le_settings_param_2,
+	.expect_len = sizeof(set_connectable_le_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_CONNECTABLE,
+};
+
+static uint8_t set_connectable_on_adv_param[] = {
+		0x00, 0x08,				/* min_interval */
+		0x00, 0x08,				/* max_interval */
+		0x00,					/* type */
+		0x00,					/* own_addr_type */
+		0x00,					/* direct_addr_type */
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* direct_addr */
+		0x07,					/* channel_map */
+		0x00,					/* filter_policy */
+};
+
+static const struct generic_data set_connectable_on_le_test_3 = {
+	.setup_settings = settings_powered_advertising,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_le_settings_param_3,
+	.expect_len = sizeof(set_connectable_le_settings_param_3),
+	.expect_settings_set = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_on_adv_param,
+	.expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const uint16_t settings_connectable[] = { MGMT_OP_SET_CONNECTABLE, 0 };
+static const uint16_t settings_powered_connectable[] = {
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_POWERED, 0 };
+static const uint16_t settings_powered_discoverable[] = {
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_DISCOVERABLE,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const char set_connectable_off_param[] = { 0x00 };
+static const char set_connectable_off_settings_1[] = { 0x80, 0x00, 0x00, 0x00 };
+static const char set_connectable_off_settings_2[] = { 0x81, 0x00, 0x00, 0x00 };
+static const char set_connectable_off_scan_enable_param[] = { 0x00 };
+
+static const struct generic_data set_connectable_off_success_test_1 = {
+	.setup_settings = settings_connectable,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_settings_1,
+	.expect_len = sizeof(set_connectable_off_settings_1),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+};
+
+static const struct generic_data set_connectable_off_success_test_2 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_settings_2,
+	.expect_len = sizeof(set_connectable_off_settings_2),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_connectable_off_scan_enable_param,
+	.expect_hci_len = sizeof(set_connectable_off_scan_enable_param),
+};
+
+static const struct generic_data set_connectable_off_success_test_3 = {
+	.setup_settings = settings_powered_discoverable,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_settings_2,
+	.expect_len = sizeof(set_connectable_off_settings_2),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_connectable_off_scan_enable_param,
+	.expect_hci_len = sizeof(set_connectable_off_scan_enable_param),
+};
+
+static const struct generic_data set_connectable_off_success_test_4 = {
+	.setup_settings = settings_powered_discoverable,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_settings_2,
+	.expect_len = sizeof(set_connectable_off_settings_2),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_connectable_scan_enable_param,
+	.expect_hci_len = sizeof(set_connectable_scan_enable_param),
+};
+
+static const char set_connectable_off_le_settings_1[] = { 0x00, 0x02, 0x00, 0x00 };
+static const char set_connectable_off_le_settings_2[] = { 0x01, 0x06, 0x00, 0x00 };
+
+static uint16_t settings_le_connectable[] = { MGMT_OP_SET_LE,
+						MGMT_OP_SET_CONNECTABLE, 0 };
+
+static const struct generic_data set_connectable_off_le_test_1 = {
+	.setup_settings = settings_le_connectable,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_le_settings_1,
+	.expect_len = sizeof(set_connectable_off_le_settings_1),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+};
+
+static uint16_t settings_powered_le_connectable_advertising[] = {
+					MGMT_OP_SET_LE,
+					MGMT_OP_SET_CONNECTABLE,
+					MGMT_OP_SET_ADVERTISING,
+					MGMT_OP_SET_POWERED, 0 };
+
+static uint8_t set_connectable_off_adv_param[] = {
+		0x00, 0x08,				/* min_interval */
+		0x00, 0x08,				/* max_interval */
+		0x03,					/* type */
+		0x01,					/* own_addr_type */
+		0x00,					/* direct_addr_type */
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* direct_addr */
+		0x07,					/* channel_map */
+		0x00,					/* filter_policy */
+};
+
+static const struct generic_data set_connectable_off_le_test_2 = {
+	.setup_settings = settings_powered_le_connectable_advertising,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_le_settings_2,
+	.expect_len = sizeof(set_connectable_off_le_settings_2),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_off_adv_param,
+	.expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static uint16_t settings_powered_le_discoverable[] = {
+					MGMT_OP_SET_LE,
+					MGMT_OP_SET_CONNECTABLE,
+					MGMT_OP_SET_POWERED,
+					MGMT_OP_SET_DISCOVERABLE, 0 };
+
+static uint16_t settings_powered_le_discoverable_advertising[] = {
+					MGMT_OP_SET_LE,
+					MGMT_OP_SET_CONNECTABLE,
+					MGMT_OP_SET_ADVERTISING,
+					MGMT_OP_SET_POWERED,
+					MGMT_OP_SET_DISCOVERABLE, 0 };
+
+static const struct generic_data set_connectable_off_le_test_3 = {
+	.setup_settings = settings_powered_le_discoverable_advertising,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_le_settings_2,
+	.expect_len = sizeof(set_connectable_off_le_settings_2),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_off_adv_param,
+	.expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const struct generic_data set_connectable_off_le_test_4 = {
+	.setup_settings = settings_powered_le_discoverable_advertising,
+	.setup_limited_discov = true,
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_off_le_settings_2,
+	.expect_len = sizeof(set_connectable_off_le_settings_2),
+	.expect_settings_unset = MGMT_SETTING_CONNECTABLE,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_off_adv_param,
+	.expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const char set_fast_conn_on_param[] = { 0x01 };
+static const char set_fast_conn_on_settings_1[] = { 0x87, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_2[] = { 0x85, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_3[] = { 0x84, 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_fast_conn_on_success_test_1 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+	.send_param = set_fast_conn_on_param,
+	.send_len = sizeof(set_fast_conn_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_fast_conn_on_settings_1,
+	.expect_len = sizeof(set_fast_conn_on_settings_1),
+	.expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
+static const struct generic_data set_fast_conn_on_success_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+	.send_param = set_fast_conn_on_param,
+	.send_len = sizeof(set_fast_conn_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_fast_conn_on_settings_2,
+	.expect_len = sizeof(set_fast_conn_on_settings_2),
+	.expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
+static const struct generic_data set_fast_conn_on_success_test_3 = {
+	.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+	.send_param = set_fast_conn_on_param,
+	.send_len = sizeof(set_fast_conn_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_fast_conn_on_settings_3,
+	.expect_len = sizeof(set_fast_conn_on_settings_3),
+	.expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
+static const struct generic_data set_fast_conn_on_not_supported_test_1 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+	.send_param = set_fast_conn_on_param,
+	.send_len = sizeof(set_fast_conn_on_param),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const char set_fast_conn_nval_param[] = { 0xff };
+
+static const struct generic_data set_fast_conn_nval_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+	.send_param = set_fast_conn_nval_param,
+	.send_len = sizeof(set_fast_conn_nval_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char set_bondable_on_param[] = { 0x01 };
+static const char set_bondable_invalid_param[] = { 0x02 };
+static const char set_bondable_garbage_param[] = { 0x01, 0x00 };
+static const char set_bondable_settings_param[] = { 0x90, 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_bondable_on_success_test = {
+	.send_opcode = MGMT_OP_SET_BONDABLE,
+	.send_param = set_bondable_on_param,
+	.send_len = sizeof(set_bondable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_bondable_settings_param,
+	.expect_len = sizeof(set_bondable_settings_param),
+	.expect_settings_set = MGMT_SETTING_BONDABLE,
+};
+
+static const struct generic_data set_bondable_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_BONDABLE,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_bondable_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_BONDABLE,
+	.send_param = set_bondable_invalid_param,
+	.send_len = sizeof(set_bondable_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_bondable_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_BONDABLE,
+	.send_param = set_bondable_garbage_param,
+	.send_len = sizeof(set_bondable_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_bondable_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_BONDABLE,
+	.send_param = set_bondable_on_param,
+	.send_len = sizeof(set_bondable_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const uint8_t set_discoverable_on_param[] = { 0x01, 0x00, 0x00 };
+static const uint8_t set_discoverable_timeout_param[] = { 0x01, 0x0a, 0x00 };
+static const uint8_t set_discoverable_invalid_param[] = { 0x02, 0x00, 0x00 };
+static const uint8_t set_discoverable_off_param[] = { 0x00, 0x00, 0x00 };
+static const uint8_t set_discoverable_offtimeout_param[] = { 0x00, 0x01, 0x00 };
+static const uint8_t set_discoverable_garbage_param[] = { 0x01, 0x00, 0x00, 0x00 };
+static const uint8_t set_discoverable_on_settings_param_1[] = { 0x8a, 0x00, 0x00, 0x00 };
+static const uint8_t set_discoverable_on_settings_param_2[] = { 0x8b, 0x00, 0x00, 0x00 };
+static const uint8_t set_discoverable_off_settings_param_1[] = { 0x82, 0x00, 0x00, 0x00 };
+static const uint8_t set_discoverable_off_settings_param_2[] = { 0x83, 0x00, 0x00, 0x00 };
+static const uint8_t set_discoverable_on_scan_enable_param[] = { 0x03 };
+static const uint8_t set_discoverable_off_scan_enable_param[] = { 0x02 };
+
+static const struct generic_data set_discoverable_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_discoverable_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_invalid_param,
+	.send_len = sizeof(set_discoverable_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_discoverable_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_garbage_param,
+	.send_len = sizeof(set_discoverable_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_discoverable_on_invalid_param_test_4 = {
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_offtimeout_param,
+	.send_len = sizeof(set_discoverable_offtimeout_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_discoverable_on_not_powered_test_1 = {
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_timeout_param,
+	.send_len = sizeof(set_discoverable_timeout_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+};
+
+static const struct generic_data set_discoverable_on_not_powered_test_2 = {
+	.setup_settings = settings_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_timeout_param,
+	.send_len = sizeof(set_discoverable_timeout_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+};
+
+static const struct generic_data set_discoverable_on_rejected_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_on_param,
+	.send_len = sizeof(set_discoverable_on_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data set_discoverable_on_rejected_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_on_param,
+	.send_len = sizeof(set_discoverable_on_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data set_discoverable_on_rejected_test_3 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_timeout_param,
+	.send_len = sizeof(set_discoverable_timeout_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data set_discoverable_on_success_test_1 = {
+	.setup_settings = settings_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_on_param,
+	.send_len = sizeof(set_discoverable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_on_settings_param_1,
+	.expect_len = sizeof(set_discoverable_on_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_DISCOVERABLE,
+};
+
+static const struct generic_data set_discoverable_on_success_test_2 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_on_param,
+	.send_len = sizeof(set_discoverable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_on_settings_param_2,
+	.expect_len = sizeof(set_discoverable_on_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_DISCOVERABLE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_discoverable_on_scan_enable_param,
+	.expect_hci_len = sizeof(set_discoverable_on_scan_enable_param),
+};
+
+static uint8_t set_discov_on_le_param[] = { 0x0b, 0x06, 0x00, 0x00 };
+static uint8_t set_discov_adv_data[32] = { 0x06, 0x02, 0x01, 0x06,
+								0x02, 0x0a, };
+
+static const struct generic_data set_discov_on_le_success_1 = {
+	.setup_settings = settings_powered_le_connectable_advertising,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_on_param,
+	.send_len = sizeof(set_discoverable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discov_on_le_param,
+	.expect_len = sizeof(set_discov_on_le_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_discov_adv_data,
+	.expect_hci_len = sizeof(set_discov_adv_data),
+};
+
+static const struct generic_data set_discoverable_off_success_test_1 = {
+	.setup_settings = settings_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_off_param,
+	.send_len = sizeof(set_discoverable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_off_settings_param_1,
+	.expect_len = sizeof(set_discoverable_off_settings_param_1),
+};
+
+static const struct generic_data set_discoverable_off_success_test_2 = {
+	.setup_settings = settings_powered_discoverable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_discoverable_off_param,
+	.send_len = sizeof(set_discoverable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_off_settings_param_2,
+	.expect_len = sizeof(set_discoverable_off_settings_param_2),
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_discoverable_off_scan_enable_param,
+	.expect_hci_len = sizeof(set_discoverable_off_scan_enable_param),
+};
+
+static const uint8_t set_limited_discov_on_param[] = { 0x02, 0x01, 0x00 };
+
+static const struct generic_data set_limited_discov_on_success_1 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_limited_discov_on_param,
+	.send_len = sizeof(set_limited_discov_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_on_settings_param_2,
+	.expect_len = sizeof(set_discoverable_on_settings_param_2),
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_discoverable_on_scan_enable_param,
+	.expect_hci_len = sizeof(set_discoverable_on_scan_enable_param),
+};
+
+static uint8_t write_current_iac_lap_limited[] = { 0x01, 0x00, 0x8b, 0x9e };
+
+static const struct generic_data set_limited_discov_on_success_2 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_limited_discov_on_param,
+	.send_len = sizeof(set_limited_discov_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_on_settings_param_2,
+	.expect_len = sizeof(set_discoverable_on_settings_param_2),
+	.expect_hci_command = BT_HCI_CMD_WRITE_CURRENT_IAC_LAP,
+	.expect_hci_param = write_current_iac_lap_limited,
+	.expect_hci_len = sizeof(write_current_iac_lap_limited),
+};
+
+static uint8_t write_cod_limited[] = { 0x00, 0x20, 0x00 };
+
+static const struct generic_data set_limited_discov_on_success_3 = {
+	.setup_settings = settings_powered_connectable,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_limited_discov_on_param,
+	.send_len = sizeof(set_limited_discov_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discoverable_on_settings_param_2,
+	.expect_len = sizeof(set_discoverable_on_settings_param_2),
+	.expect_hci_command = BT_HCI_CMD_WRITE_CLASS_OF_DEV,
+	.expect_hci_param = write_cod_limited,
+	.expect_hci_len = sizeof(write_cod_limited),
+};
+
+static uint8_t set_limited_discov_adv_data[32] = { 0x06, 0x02, 0x01, 0x05,
+								0x02, 0x0a, };
+
+static const struct generic_data set_limited_discov_on_le_success_1 = {
+	.setup_settings = settings_powered_le_connectable_advertising,
+	.send_opcode = MGMT_OP_SET_DISCOVERABLE,
+	.send_param = set_limited_discov_on_param,
+	.send_len = sizeof(set_limited_discov_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_discov_on_le_param,
+	.expect_len = sizeof(set_discov_on_le_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_limited_discov_adv_data,
+	.expect_hci_len = sizeof(set_limited_discov_adv_data),
+};
+
+static uint16_t settings_link_sec[] = { MGMT_OP_SET_LINK_SECURITY, 0 };
+
+static const char set_link_sec_on_param[] = { 0x01 };
+static const char set_link_sec_invalid_param[] = { 0x02 };
+static const char set_link_sec_garbage_param[] = { 0x01, 0x00 };
+static const char set_link_sec_settings_param_1[] = { 0xa0, 0x00, 0x00, 0x00 };
+static const char set_link_sec_settings_param_2[] = { 0xa1, 0x00, 0x00, 0x00 };
+static const char set_link_sec_auth_enable_param[] = { 0x01 };
+
+static const struct generic_data set_link_sec_on_success_test_1 = {
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_on_param,
+	.send_len = sizeof(set_link_sec_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_link_sec_settings_param_1,
+	.expect_len = sizeof(set_link_sec_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_LINK_SECURITY,
+};
+
+static const struct generic_data set_link_sec_on_success_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_on_param,
+	.send_len = sizeof(set_link_sec_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_link_sec_settings_param_2,
+	.expect_len = sizeof(set_link_sec_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_LINK_SECURITY,
+	.expect_hci_command = BT_HCI_CMD_WRITE_AUTH_ENABLE,
+	.expect_hci_param = set_link_sec_auth_enable_param,
+	.expect_hci_len = sizeof(set_link_sec_auth_enable_param),
+};
+
+static const struct generic_data set_link_sec_on_success_test_3 = {
+	.setup_settings = settings_link_sec,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_link_sec_settings_param_2,
+	.expect_len = sizeof(set_link_sec_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_LINK_SECURITY,
+	.expect_hci_command = BT_HCI_CMD_WRITE_AUTH_ENABLE,
+	.expect_hci_param = set_link_sec_auth_enable_param,
+	.expect_hci_len = sizeof(set_link_sec_auth_enable_param),
+};
+
+static const struct generic_data set_link_sec_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_link_sec_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_invalid_param,
+	.send_len = sizeof(set_link_sec_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_link_sec_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_garbage_param,
+	.send_len = sizeof(set_link_sec_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_link_sec_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_on_param,
+	.send_len = sizeof(set_link_sec_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const uint16_t settings_powered_link_sec[] = {
+						MGMT_OP_SET_LINK_SECURITY,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const char set_link_sec_off_param[] = { 0x00 };
+static const char set_link_sec_off_settings_1[] = { 0x80, 0x00, 0x00, 0x00 };
+static const char set_link_sec_off_settings_2[] = { 0x81, 0x00, 0x00, 0x00 };
+static const char set_link_sec_off_auth_enable_param[] = { 0x00 };
+
+static const struct generic_data set_link_sec_off_success_test_1 = {
+	.setup_settings = settings_link_sec,
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_off_param,
+	.send_len = sizeof(set_link_sec_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_link_sec_off_settings_1,
+	.expect_len = sizeof(set_link_sec_off_settings_1),
+	.expect_settings_unset = MGMT_SETTING_LINK_SECURITY,
+};
+
+static const struct generic_data set_link_sec_off_success_test_2 = {
+	.setup_settings = settings_powered_link_sec,
+	.send_opcode = MGMT_OP_SET_LINK_SECURITY,
+	.send_param = set_link_sec_off_param,
+	.send_len = sizeof(set_link_sec_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_link_sec_off_settings_2,
+	.expect_len = sizeof(set_link_sec_off_settings_2),
+	.expect_settings_unset = MGMT_SETTING_LINK_SECURITY,
+	.expect_hci_command = BT_HCI_CMD_WRITE_AUTH_ENABLE,
+	.expect_hci_param = set_link_sec_off_auth_enable_param,
+	.expect_hci_len = sizeof(set_link_sec_off_auth_enable_param),
+};
+
+static uint16_t settings_ssp[] = { MGMT_OP_SET_SSP, 0 };
+
+static const char set_ssp_on_param[] = { 0x01 };
+static const char set_ssp_invalid_param[] = { 0x02 };
+static const char set_ssp_garbage_param[] = { 0x01, 0x00 };
+static const char set_ssp_settings_param_1[] = { 0xc0, 0x00, 0x00, 0x00 };
+static const char set_ssp_settings_param_2[] = { 0xc1, 0x00, 0x00, 0x00 };
+static const char set_ssp_on_write_ssp_mode_param[] = { 0x01 };
+
+static const struct generic_data set_ssp_on_success_test_1 = {
+	.send_opcode = MGMT_OP_SET_SSP,
+	.send_param = set_ssp_on_param,
+	.send_len = sizeof(set_ssp_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_ssp_settings_param_1,
+	.expect_len = sizeof(set_ssp_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_SSP,
+};
+
+static const struct generic_data set_ssp_on_success_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_SSP,
+	.send_param = set_ssp_on_param,
+	.send_len = sizeof(set_ssp_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_ssp_settings_param_2,
+	.expect_len = sizeof(set_ssp_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_SSP,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE,
+	.expect_hci_param = set_ssp_on_write_ssp_mode_param,
+	.expect_hci_len = sizeof(set_ssp_on_write_ssp_mode_param),
+};
+
+static const struct generic_data set_ssp_on_success_test_3 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_ssp_settings_param_2,
+	.expect_len = sizeof(set_ssp_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_SSP,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE,
+	.expect_hci_param = set_ssp_on_write_ssp_mode_param,
+	.expect_hci_len = sizeof(set_ssp_on_write_ssp_mode_param),
+};
+
+static const struct generic_data set_ssp_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_SSP,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_ssp_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_SSP,
+	.send_param = set_ssp_invalid_param,
+	.send_len = sizeof(set_ssp_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_ssp_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_SSP,
+	.send_param = set_ssp_garbage_param,
+	.send_len = sizeof(set_ssp_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_ssp_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_SSP,
+	.send_param = set_ssp_on_param,
+	.send_len = sizeof(set_ssp_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP,
+						MGMT_OP_SET_POWERED, 0 };
+
+static uint16_t settings_powered_sc[] = { MGMT_OP_SET_SSP,
+						MGMT_OP_SET_SECURE_CONN,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const char set_sc_on_param[] = { 0x01 };
+static const char set_sc_only_on_param[] = { 0x02 };
+static const char set_sc_invalid_param[] = { 0x03 };
+static const char set_sc_garbage_param[] = { 0x01, 0x00 };
+static const char set_sc_settings_param_1[] = { 0xc0, 0x08, 0x00, 0x00 };
+static const char set_sc_settings_param_2[] = { 0xc1, 0x08, 0x00, 0x00 };
+static const char set_sc_on_write_sc_support_param[] = { 0x01 };
+
+static const struct generic_data set_sc_on_success_test_1 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_on_param,
+	.send_len = sizeof(set_sc_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_sc_settings_param_1,
+	.expect_len = sizeof(set_sc_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_SECURE_CONN,
+};
+
+static const struct generic_data set_sc_on_success_test_2 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_on_param,
+	.send_len = sizeof(set_sc_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_sc_settings_param_2,
+	.expect_len = sizeof(set_sc_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_SECURE_CONN,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT,
+	.expect_hci_param = set_sc_on_write_sc_support_param,
+	.expect_hci_len = sizeof(set_sc_on_write_sc_support_param),
+};
+
+static const struct generic_data set_sc_on_invalid_param_test_1 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_sc_on_invalid_param_test_2 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_invalid_param,
+	.send_len = sizeof(set_sc_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_sc_on_invalid_param_test_3 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_garbage_param,
+	.send_len = sizeof(set_sc_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_sc_on_invalid_index_test = {
+	.setup_settings = settings_ssp,
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_on_param,
+	.send_len = sizeof(set_sc_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data set_sc_on_not_supported_test_1 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_on_param,
+	.send_len = sizeof(set_sc_on_param),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data set_sc_on_not_supported_test_2 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_on_param,
+	.send_len = sizeof(set_sc_on_param),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data set_sc_only_on_success_test_1 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_only_on_param,
+	.send_len = sizeof(set_sc_only_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_sc_settings_param_1,
+	.expect_len = sizeof(set_sc_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_SECURE_CONN,
+};
+
+static const struct generic_data set_sc_only_on_success_test_2 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_SET_SECURE_CONN,
+	.send_param = set_sc_only_on_param,
+	.send_len = sizeof(set_sc_only_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_sc_settings_param_2,
+	.expect_len = sizeof(set_sc_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_SECURE_CONN,
+	.expect_hci_command = BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT,
+	.expect_hci_param = set_sc_on_write_sc_support_param,
+	.expect_hci_len = sizeof(set_sc_on_write_sc_support_param),
+};
+
+static const char set_hs_on_param[] = { 0x01 };
+static const char set_hs_invalid_param[] = { 0x02 };
+static const char set_hs_garbage_param[] = { 0x01, 0x00 };
+static const char set_hs_settings_param_1[] = { 0xc0, 0x01, 0x00, 0x00 };
+
+static const struct generic_data set_hs_on_success_test = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_HS,
+	.send_param = set_hs_on_param,
+	.send_len = sizeof(set_hs_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_hs_settings_param_1,
+	.expect_len = sizeof(set_hs_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_HS,
+};
+
+static const struct generic_data set_hs_on_invalid_param_test_1 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_HS,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_hs_on_invalid_param_test_2 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_HS,
+	.send_param = set_hs_invalid_param,
+	.send_len = sizeof(set_hs_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_hs_on_invalid_param_test_3 = {
+	.setup_settings = settings_ssp,
+	.send_opcode = MGMT_OP_SET_HS,
+	.send_param = set_hs_garbage_param,
+	.send_len = sizeof(set_hs_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_hs_on_invalid_index_test = {
+	.setup_settings = settings_ssp,
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_HS,
+	.send_param = set_hs_on_param,
+	.send_len = sizeof(set_hs_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static uint16_t settings_le[] = { MGMT_OP_SET_LE, 0 };
+
+static const char set_le_on_param[] = { 0x01 };
+static const char set_le_off_param[] = { 0x00 };
+static const char set_le_invalid_param[] = { 0x02 };
+static const char set_le_garbage_param[] = { 0x01, 0x00 };
+static const char set_le_settings_param_1[] = { 0x80, 0x02, 0x00, 0x00 };
+static const char set_le_settings_param_2[] = { 0x81, 0x02, 0x00, 0x00 };
+static const char set_le_on_write_le_host_param[] = { 0x01, 0x00 };
+
+static const struct generic_data set_le_on_success_test_1 = {
+	.send_opcode = MGMT_OP_SET_LE,
+	.send_param = set_le_on_param,
+	.send_len = sizeof(set_le_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_le_settings_param_1,
+	.expect_len = sizeof(set_le_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_LE,
+};
+
+static const struct generic_data set_le_on_success_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_LE,
+	.send_param = set_le_on_param,
+	.send_len = sizeof(set_le_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_le_settings_param_2,
+	.expect_len = sizeof(set_le_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_LE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED,
+	.expect_hci_param = set_le_on_write_le_host_param,
+	.expect_hci_len = sizeof(set_le_on_write_le_host_param),
+};
+
+static const struct generic_data set_le_on_success_test_3 = {
+	.setup_settings = settings_le,
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_le_settings_param_2,
+	.expect_len = sizeof(set_le_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_LE,
+	.expect_hci_command = BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED,
+	.expect_hci_param = set_le_on_write_le_host_param,
+	.expect_hci_len = sizeof(set_le_on_write_le_host_param),
+};
+
+static const struct generic_data set_le_on_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_LE,
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_le_on_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_SET_LE,
+	.send_param = set_le_invalid_param,
+	.send_len = sizeof(set_le_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_le_on_invalid_param_test_3 = {
+	.send_opcode = MGMT_OP_SET_LE,
+	.send_param = set_le_garbage_param,
+	.send_len = sizeof(set_le_garbage_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_le_on_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_SET_LE,
+	.send_param = set_le_on_param,
+	.send_len = sizeof(set_le_on_param),
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static uint16_t settings_powered_le[] = { MGMT_OP_SET_LE,
+					MGMT_OP_SET_POWERED, 0 };
+
+static const char set_adv_on_param[] = { 0x01 };
+static const char set_adv_settings_param_1[] = { 0x80, 0x06, 0x00, 0x00 };
+static const char set_adv_settings_param_2[] = { 0x81, 0x06, 0x00, 0x00 };
+static const char set_adv_on_set_adv_enable_param[] = { 0x01 };
+static const char set_adv_on_set_adv_disable_param[] = { 0x00 };
+
+static const struct generic_data set_adv_on_success_test_1 = {
+	.setup_settings = settings_le,
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_adv_settings_param_1,
+	.expect_len = sizeof(set_adv_settings_param_1),
+	.expect_settings_set = MGMT_SETTING_ADVERTISING,
+};
+
+static const struct generic_data set_adv_on_success_test_2 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_adv_settings_param_2,
+	.expect_len = sizeof(set_adv_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_ADVERTISING,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+	.expect_hci_param = set_adv_on_set_adv_enable_param,
+	.expect_hci_len = sizeof(set_adv_on_set_adv_enable_param),
+};
+
+static const struct generic_data set_adv_on_rejected_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const char set_bredr_off_param[] = { 0x00 };
+static const char set_bredr_on_param[] = { 0x01 };
+static const char set_bredr_invalid_param[] = { 0x02 };
+static const char set_bredr_settings_param_1[] = { 0x00, 0x02, 0x00, 0x00 };
+static const char set_bredr_settings_param_2[] = { 0x80, 0x02, 0x00, 0x00 };
+static const char set_bredr_settings_param_3[] = { 0x81, 0x02, 0x00, 0x00 };
+
+static const struct generic_data set_bredr_off_success_test_1 = {
+	.setup_settings = settings_le,
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_off_param,
+	.send_len = sizeof(set_bredr_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_bredr_settings_param_1,
+	.expect_len = sizeof(set_bredr_settings_param_1),
+	.expect_settings_unset = MGMT_SETTING_BREDR,
+};
+
+static const struct generic_data set_bredr_on_success_test_1 = {
+	.setup_settings = settings_le,
+	.setup_nobredr = true,
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_on_param,
+	.send_len = sizeof(set_bredr_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_bredr_settings_param_2,
+	.expect_len = sizeof(set_bredr_settings_param_2),
+	.expect_settings_set = MGMT_SETTING_BREDR,
+};
+
+static const struct generic_data set_bredr_on_success_test_2 = {
+	.setup_settings = settings_powered_le,
+	.setup_nobredr = true,
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_on_param,
+	.send_len = sizeof(set_bredr_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_bredr_settings_param_3,
+	.expect_len = sizeof(set_bredr_settings_param_3),
+	.expect_settings_set = MGMT_SETTING_BREDR,
+};
+
+static const struct generic_data set_bredr_off_notsupp_test = {
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_off_param,
+	.send_len = sizeof(set_bredr_off_param),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data set_bredr_off_failure_test_1 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_off_param,
+	.send_len = sizeof(set_bredr_off_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data set_bredr_off_failure_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_off_param,
+	.send_len = sizeof(set_bredr_off_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data set_bredr_off_failure_test_3 = {
+	.setup_settings = settings_le,
+	.send_opcode = MGMT_OP_SET_BREDR,
+	.send_param = set_bredr_invalid_param,
+	.send_len = sizeof(set_bredr_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char set_local_name_param[260] = { 'T', 'e', 's', 't', ' ',
+						'n', 'a', 'm', 'e' };
+static const char write_local_name_hci[248] = { 'T', 'e', 's', 't', ' ',
+						'n', 'a', 'm', 'e' };
+static const char write_eir_local_name_hci_1[241] = { 0x00,
+		0x0a, 0x09, 'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e',
+		0x02, 0x0a, 0x00, };
+
+static const struct generic_data set_local_name_test_1 = {
+	.send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.send_param = set_local_name_param,
+	.send_len = sizeof(set_local_name_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_local_name_param,
+	.expect_len = sizeof(set_local_name_param),
+	.expect_alt_ev = MGMT_EV_LOCAL_NAME_CHANGED,
+	.expect_alt_ev_param = set_local_name_param,
+	.expect_alt_ev_len = sizeof(set_local_name_param),
+};
+
+static const struct generic_data set_local_name_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.send_param = set_local_name_param,
+	.send_len = sizeof(set_local_name_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_local_name_param,
+	.expect_len = sizeof(set_local_name_param),
+	.expect_hci_command = BT_HCI_CMD_WRITE_LOCAL_NAME,
+	.expect_hci_param = write_local_name_hci,
+	.expect_hci_len = sizeof(write_local_name_hci),
+	.expect_alt_ev = MGMT_EV_LOCAL_NAME_CHANGED,
+	.expect_alt_ev_param = set_local_name_param,
+	.expect_alt_ev_len = sizeof(set_local_name_param),
+};
+
+static const struct generic_data set_local_name_test_3 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_SET_LOCAL_NAME,
+	.send_param = set_local_name_param,
+	.send_len = sizeof(set_local_name_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_local_name_param,
+	.expect_len = sizeof(set_local_name_param),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_local_name_hci_1,
+	.expect_hci_len = sizeof(write_eir_local_name_hci_1),
+	.expect_alt_ev = MGMT_EV_LOCAL_NAME_CHANGED,
+	.expect_alt_ev_param = set_local_name_param,
+	.expect_alt_ev_len = sizeof(set_local_name_param),
+};
+
+static const char start_discovery_invalid_param[] = { 0x00 };
+static const char start_discovery_bredr_param[] = { 0x01 };
+static const char start_discovery_le_param[] = { 0x06 };
+static const char start_discovery_bredrle_param[] = { 0x07 };
+static const char start_discovery_valid_hci[] = { 0x01, 0x01 };
+static const char start_discovery_evt[] = { 0x07, 0x01 };
+static const char start_discovery_le_evt[] = { 0x06, 0x01 };
+
+static const struct generic_data start_discovery_not_powered_test_1 = {
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_bredr_param,
+	.send_len = sizeof(start_discovery_bredr_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.expect_param = start_discovery_bredr_param,
+	.expect_len = sizeof(start_discovery_bredr_param),
+};
+
+static const struct generic_data start_discovery_invalid_param_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_invalid_param,
+	.send_len = sizeof(start_discovery_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = start_discovery_invalid_param,
+	.expect_len = sizeof(start_discovery_invalid_param),
+};
+
+static const struct generic_data start_discovery_not_supported_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_le_param,
+	.send_len = sizeof(start_discovery_le_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+	.expect_param = start_discovery_le_param,
+	.expect_len = sizeof(start_discovery_le_param),
+};
+
+static const struct generic_data start_discovery_valid_param_test_1 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_bredrle_param,
+	.send_len = sizeof(start_discovery_bredrle_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_discovery_bredrle_param,
+	.expect_len = sizeof(start_discovery_bredrle_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+	.expect_hci_param = start_discovery_valid_hci,
+	.expect_hci_len = sizeof(start_discovery_valid_hci),
+	.expect_alt_ev = MGMT_EV_DISCOVERING,
+	.expect_alt_ev_param = start_discovery_evt,
+	.expect_alt_ev_len = sizeof(start_discovery_evt),
+};
+
+static const struct generic_data start_discovery_valid_param_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_le_param,
+	.send_len = sizeof(start_discovery_le_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_discovery_le_param,
+	.expect_len = sizeof(start_discovery_le_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+	.expect_hci_param = start_discovery_valid_hci,
+	.expect_hci_len = sizeof(start_discovery_valid_hci),
+	.expect_alt_ev = MGMT_EV_DISCOVERING,
+	.expect_alt_ev_param = start_discovery_le_evt,
+	.expect_alt_ev_len = sizeof(start_discovery_le_evt),
+};
+
+static const struct generic_data start_discovery_valid_param_power_off_1 = {
+	.setup_settings = settings_le,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_bredrle_param,
+	.send_len = sizeof(start_discovery_bredrle_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.force_power_off = true,
+	.expect_param = start_discovery_bredrle_param,
+	.expect_len = sizeof(start_discovery_bredrle_param),
+};
+
+static const char stop_discovery_bredrle_param[] = { 0x07 };
+static const char stop_discovery_bredrle_invalid_param[] = { 0x06 };
+static const char stop_discovery_valid_hci[] = { 0x00, 0x00 };
+static const char stop_discovery_evt[] = { 0x07, 0x00 };
+static const char stop_discovery_bredr_param[] = { 0x01 };
+static const char stop_discovery_bredr_discovering[] = { 0x01, 0x00 };
+
+static const struct generic_data stop_discovery_success_test_1 = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_START_DISCOVERY,
+	.setup_send_param = start_discovery_bredrle_param,
+	.setup_send_len = sizeof(start_discovery_bredrle_param),
+	.send_opcode = MGMT_OP_STOP_DISCOVERY,
+	.send_param = stop_discovery_bredrle_param,
+	.send_len = sizeof(stop_discovery_bredrle_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = stop_discovery_bredrle_param,
+	.expect_len = sizeof(stop_discovery_bredrle_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+	.expect_hci_param = stop_discovery_valid_hci,
+	.expect_hci_len = sizeof(stop_discovery_valid_hci),
+	.expect_alt_ev = MGMT_EV_DISCOVERING,
+	.expect_alt_ev_param = stop_discovery_evt,
+	.expect_alt_ev_len = sizeof(stop_discovery_evt),
+};
+
+static const struct generic_data stop_discovery_bredr_success_test_1 = {
+	.setup_settings = settings_powered,
+	.setup_send_opcode = MGMT_OP_START_DISCOVERY,
+	.setup_send_param = start_discovery_bredr_param,
+	.setup_send_len = sizeof(start_discovery_bredr_param),
+	.send_opcode = MGMT_OP_STOP_DISCOVERY,
+	.send_param = stop_discovery_bredr_param,
+	.send_len = sizeof(stop_discovery_bredr_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = stop_discovery_bredr_param,
+	.expect_len = sizeof(stop_discovery_bredr_param),
+	.expect_hci_command = BT_HCI_CMD_INQUIRY_CANCEL,
+	.expect_alt_ev = MGMT_EV_DISCOVERING,
+	.expect_alt_ev_param = stop_discovery_bredr_discovering,
+	.expect_alt_ev_len = sizeof(stop_discovery_bredr_discovering),
+};
+
+static const struct generic_data stop_discovery_rejected_test_1 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_STOP_DISCOVERY,
+	.send_param = stop_discovery_bredrle_param,
+	.send_len = sizeof(stop_discovery_bredrle_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+	.expect_param = stop_discovery_bredrle_param,
+	.expect_len = sizeof(stop_discovery_bredrle_param),
+};
+
+static const struct generic_data stop_discovery_invalid_param_test_1 = {
+	.setup_settings = settings_powered_le,
+	.setup_send_opcode = MGMT_OP_START_DISCOVERY,
+	.setup_send_param = start_discovery_bredrle_param,
+	.setup_send_len = sizeof(start_discovery_bredrle_param),
+	.send_opcode = MGMT_OP_STOP_DISCOVERY,
+	.send_param = stop_discovery_bredrle_invalid_param,
+	.send_len = sizeof(stop_discovery_bredrle_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = stop_discovery_bredrle_invalid_param,
+	.expect_len = sizeof(stop_discovery_bredrle_invalid_param),
+};
+
+static const char start_service_discovery_invalid_param[] = { 0x00, 0x00, 0x00, 0x00 };
+static const char start_service_discovery_invalid_resp[] = { 0x00 };
+static const char start_service_discovery_bredr_param[] = { 0x01, 0x00, 0x00, 0x00};
+static const char start_service_discovery_bredr_resp[] = { 0x01 };
+static const char start_service_discovery_le_param[] = { 0x06, 0x00, 0x01, 0x00,
+			0xfa, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00,
+			0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const char start_service_discovery_le_resp[] = { 0x06 };
+static const char start_service_discovery_bredrle_param[] = { 0x07, 0x00, 0x01, 0x00,
+			0xfa, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00,
+			0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const char start_service_discovery_bredrle_resp[] = { 0x07 };
+static const char start_service_discovery_valid_hci[] = { 0x01, 0x01 };
+static const char start_service_discovery_evt[] = { 0x07, 0x01 };
+static const char start_service_discovery_le_evt[] = { 0x06, 0x01 };
+
+static const struct generic_data start_service_discovery_not_powered_test_1 = {
+	.send_opcode = MGMT_OP_START_SERVICE_DISCOVERY,
+	.send_param = start_service_discovery_bredr_param,
+	.send_len = sizeof(start_service_discovery_bredr_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.expect_param = start_service_discovery_bredr_resp,
+	.expect_len = sizeof(start_service_discovery_bredr_resp),
+};
+
+static const struct generic_data start_service_discovery_invalid_param_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_START_SERVICE_DISCOVERY,
+	.send_param = start_service_discovery_invalid_param,
+	.send_len = sizeof(start_service_discovery_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = start_service_discovery_invalid_resp,
+	.expect_len = sizeof(start_service_discovery_invalid_resp),
+};
+
+static const struct generic_data start_service_discovery_not_supported_test_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_START_SERVICE_DISCOVERY,
+	.send_param = start_service_discovery_le_param,
+	.send_len = sizeof(start_service_discovery_le_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+	.expect_param = start_service_discovery_le_resp,
+	.expect_len = sizeof(start_service_discovery_le_resp),
+};
+
+static const struct generic_data start_service_discovery_valid_param_test_1 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_START_SERVICE_DISCOVERY,
+	.send_param = start_service_discovery_bredrle_param,
+	.send_len = sizeof(start_service_discovery_bredrle_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_service_discovery_bredrle_resp,
+	.expect_len = sizeof(start_service_discovery_bredrle_resp),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+	.expect_hci_param = start_service_discovery_valid_hci,
+	.expect_hci_len = sizeof(start_service_discovery_valid_hci),
+	.expect_alt_ev = MGMT_EV_DISCOVERING,
+	.expect_alt_ev_param = start_service_discovery_evt,
+	.expect_alt_ev_len = sizeof(start_service_discovery_evt),
+};
+
+static const struct generic_data start_service_discovery_valid_param_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_START_SERVICE_DISCOVERY,
+	.send_param = start_service_discovery_le_param,
+	.send_len = sizeof(start_service_discovery_le_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_service_discovery_le_resp,
+	.expect_len = sizeof(start_service_discovery_le_resp),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+	.expect_hci_param = start_service_discovery_valid_hci,
+	.expect_hci_len = sizeof(start_service_discovery_valid_hci),
+	.expect_alt_ev = MGMT_EV_DISCOVERING,
+	.expect_alt_ev_param = start_service_discovery_le_evt,
+	.expect_alt_ev_len = sizeof(start_service_discovery_le_evt),
+};
+
+static const char set_dev_class_valid_param[] = { 0x01, 0x0c };
+static const char set_dev_class_zero_rsp[] = { 0x00, 0x00, 0x00 };
+static const char set_dev_class_valid_rsp[] = { 0x0c, 0x01, 0x00 };
+static const char set_dev_class_valid_hci[] = { 0x0c, 0x01, 0x00 };
+static const char set_dev_class_invalid_param[] = { 0x01, 0x01 };
+
+static const struct generic_data set_dev_class_valid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_DEV_CLASS,
+	.send_param = set_dev_class_valid_param,
+	.send_len = sizeof(set_dev_class_valid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+};
+
+static const struct generic_data set_dev_class_valid_param_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_DEV_CLASS,
+	.send_param = set_dev_class_valid_param,
+	.send_len = sizeof(set_dev_class_valid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_valid_rsp,
+	.expect_len = sizeof(set_dev_class_valid_rsp),
+	.expect_alt_ev = MGMT_EV_CLASS_OF_DEV_CHANGED,
+	.expect_alt_ev_param = set_dev_class_valid_rsp,
+	.expect_alt_ev_len = sizeof(set_dev_class_valid_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_CLASS_OF_DEV,
+	.expect_hci_param = set_dev_class_valid_hci,
+	.expect_hci_len = sizeof(set_dev_class_valid_hci),
+};
+
+static const struct generic_data set_dev_class_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_DEV_CLASS,
+	.send_param = set_dev_class_invalid_param,
+	.send_len = sizeof(set_dev_class_invalid_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char add_spp_uuid_param[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00,
+			0x00 };
+static const char add_dun_uuid_param[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00,
+			0x00 };
+static const char add_sync_uuid_param[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00,
+			0x00 };
+static const char add_opp_uuid_param[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x05, 0x11, 0x00, 0x00,
+			0x00 };
+static const char write_eir_uuid16_hci[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x03, 0x03, 0x01, 0x11 };
+static const char write_eir_multi_uuid16_hci_1[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x09, 0x03, 0x01, 0x11, 0x03,
+			0x11, 0x04, 0x11, 0x05, 0x11 };
+static const char write_eir_multi_uuid16_hci_2[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0xeb, 0x02, 0x00, 0x20, 0x01,
+			0x20, 0x02, 0x20, 0x03, 0x20, 0x04, 0x20, 0x05,
+			0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09,
+			0x20, 0x0a, 0x20, 0x0b, 0x20, 0x0c, 0x20, 0x0d,
+			0x20, 0x0e, 0x20, 0x0f, 0x20, 0x10, 0x20, 0x11,
+			0x20, 0x12, 0x20, 0x13, 0x20, 0x14, 0x20, 0x15,
+			0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19,
+			0x20, 0x1a, 0x20, 0x1b, 0x20, 0x1c, 0x20, 0x1d,
+			0x20, 0x1e, 0x20, 0x1f, 0x20, 0x20, 0x20, 0x21,
+			0x20, 0x22, 0x20, 0x23, 0x20, 0x24, 0x20, 0x25,
+			0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29,
+			0x20, 0x2a, 0x20, 0x2b, 0x20, 0x2c, 0x20, 0x2d,
+			0x20, 0x2e, 0x20, 0x2f, 0x20, 0x30, 0x20, 0x31,
+			0x20, 0x32, 0x20, 0x33, 0x20, 0x34, 0x20, 0x35,
+			0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39,
+			0x20, 0x3a, 0x20, 0x3b, 0x20, 0x3c, 0x20, 0x3d,
+			0x20, 0x3e, 0x20, 0x3f, 0x20, 0x40, 0x20, 0x41,
+			0x20, 0x42, 0x20, 0x43, 0x20, 0x44, 0x20, 0x45,
+			0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49,
+			0x20, 0x4a, 0x20, 0x4b, 0x20, 0x4c, 0x20, 0x4d,
+			0x20, 0x4e, 0x20, 0x4f, 0x20, 0x50, 0x20, 0x51,
+			0x20, 0x52, 0x20, 0x53, 0x20, 0x54, 0x20, 0x55,
+			0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59,
+			0x20, 0x5a, 0x20, 0x5b, 0x20, 0x5c, 0x20, 0x5d,
+			0x20, 0x5e, 0x20, 0x5f, 0x20, 0x60, 0x20, 0x61,
+			0x20, 0x62, 0x20, 0x63, 0x20, 0x64, 0x20, 0x65,
+			0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69,
+			0x20, 0x6a, 0x20, 0x6b, 0x20, 0x6c, 0x20, 0x6d,
+			0x20, 0x6e, 0x20, 0x6f, 0x20, 0x70, 0x20, 0x71,
+			0x20, 0x72, 0x20, 0x73, 0x20, 0x74, 0x20, 0x00 };
+static const char add_uuid32_param_1[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12,
+			0x00 };
+static const char add_uuid32_param_2[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0xef, 0xcd, 0xbc, 0x9a,
+			0x00 };
+static const char add_uuid32_param_3[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0xff, 0xee, 0xdd, 0xcc,
+			0x00 };
+static const char add_uuid32_param_4[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
+			0x00 };
+static const char write_eir_uuid32_hci[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x05, 0x05, 0x78, 0x56, 0x34,
+			0x12 };
+static const char write_eir_uuid32_multi_hci[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x11, 0x05, 0x78, 0x56, 0x34,
+			0x12, 0xef, 0xcd, 0xbc, 0x9a, 0xff, 0xee, 0xdd,
+			0xcc, 0x11, 0x22, 0x33, 0x44 };
+static const char write_eir_uuid32_multi_hci_2[] = { 0x00,
+			0x02, 0x0a, 0x00, 0xe9, 0x04, 0xff, 0xff, 0xff,
+			0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+			0xff, 0xfc, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff,
+			0xff, 0xfa, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff,
+			0xff, 0xf8, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff,
+			0xff, 0xf6, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xff,
+			0xff, 0xf4, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff,
+			0xff, 0xf2, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff,
+			0xff, 0xf0, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff,
+			0xff, 0xee, 0xff, 0xff, 0xff, 0xed, 0xff, 0xff,
+			0xff, 0xec, 0xff, 0xff, 0xff, 0xeb, 0xff, 0xff,
+			0xff, 0xea, 0xff, 0xff, 0xff, 0xe9, 0xff, 0xff,
+			0xff, 0xe8, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff,
+			0xff, 0xe6, 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff,
+			0xff, 0xe4, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff,
+			0xff, 0xe2, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff,
+			0xff, 0xe0, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff,
+			0xff, 0xde, 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff,
+			0xff, 0xdc, 0xff, 0xff, 0xff, 0xdb, 0xff, 0xff,
+			0xff, 0xda, 0xff, 0xff, 0xff, 0xd9, 0xff, 0xff,
+			0xff, 0xd8, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff,
+			0xff, 0xd6, 0xff, 0xff, 0xff, 0xd5, 0xff, 0xff,
+			0xff, 0xd4, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff,
+			0xff, 0xd2, 0xff, 0xff, 0xff, 0xd1, 0xff, 0xff,
+			0xff, 0xd0, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff,
+			0xff, 0xce, 0xff, 0xff, 0xff, 0xcd, 0xff, 0xff,
+			0xff, 0xcc, 0xff, 0xff, 0xff, 0xcb, 0xff, 0xff,
+			0xff, 0xca, 0xff, 0xff, 0xff, 0xc9, 0xff, 0xff,
+			0xff, 0xc8, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff,
+			0xff, 0xc6, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 };
+static const char add_uuid128_param_1[] = {
+			0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+			0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+			0x00 };
+static const char add_uuid128_param_2[] = {
+			0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+			0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
+			0x00 };
+static const char write_eir_uuid128_hci[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x11, 0x07, 0x00, 0x11, 0x22,
+			0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
+			0xbb, 0xcc, 0xdd, 0xee, 0xff };
+static const char write_eir_uuid128_multi_hci[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x21, 0x07, 0x00, 0x11, 0x22,
+			0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
+			0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xff, 0xee, 0xdd,
+			0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55,
+			0x44, 0x33, 0x22, 0x11 };
+static const char write_eir_uuid128_multi_hci_2[] = { 0x00,
+			0x02, 0x0a, 0x00, 0xe1, 0x07, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x01, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x02, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x03, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x04, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x05, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x06, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x07, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x08, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x09, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x0a, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x0b, 0x11, 0x22, 0x33,
+			0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+			0xcc, 0xdd, 0xee, 0xff, 0x0c, 0xff, 0xee, 0xdd,
+			0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55,
+			0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const char write_eir_uuid_mix_hci[241] = { 0x00,
+			0x02, 0x0a, 0x00, 0x05, 0x03, 0x01, 0x11, 0x03,
+			0x11, 0x09, 0x05, 0x78, 0x56, 0x34, 0x12, 0xef,
+			0xcd, 0xbc, 0x9a, 0x21, 0x07, 0x00, 0x11, 0x22,
+			0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
+			0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xff, 0xee, 0xdd,
+			0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55,
+			0x44, 0x33, 0x22, 0x11 };
+
+static const struct generic_data add_uuid16_test_1 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_spp_uuid_param,
+	.send_len = sizeof(add_spp_uuid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid16_hci,
+	.expect_hci_len = sizeof(write_eir_uuid16_hci),
+};
+
+static const struct generic_data add_multi_uuid16_test_1 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_opp_uuid_param,
+	.send_len = sizeof(add_opp_uuid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_multi_uuid16_hci_1,
+	.expect_hci_len = sizeof(write_eir_multi_uuid16_hci_1),
+};
+
+static const struct generic_data add_multi_uuid16_test_2 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_opp_uuid_param,
+	.send_len = sizeof(add_opp_uuid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_multi_uuid16_hci_2,
+	.expect_hci_len = sizeof(write_eir_multi_uuid16_hci_2),
+};
+
+static const struct generic_data add_uuid32_test_1 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid32_param_1,
+	.send_len = sizeof(add_uuid32_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid32_hci,
+	.expect_hci_len = sizeof(write_eir_uuid32_hci),
+};
+
+static const struct generic_data add_uuid32_multi_test_1 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid32_param_4,
+	.send_len = sizeof(add_uuid32_param_4),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid32_multi_hci,
+	.expect_hci_len = sizeof(write_eir_uuid32_multi_hci),
+};
+
+static const struct generic_data add_uuid32_multi_test_2 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid32_param_4,
+	.send_len = sizeof(add_uuid32_param_4),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid32_multi_hci_2,
+	.expect_hci_len = sizeof(write_eir_uuid32_multi_hci_2),
+};
+
+static const struct generic_data add_uuid128_test_1 = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid128_param_1,
+	.send_len = sizeof(add_uuid128_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid128_hci,
+	.expect_hci_len = sizeof(write_eir_uuid128_hci),
+};
+
+static const struct generic_data add_uuid128_multi_test_1 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid128_param_2,
+	.send_len = sizeof(add_uuid32_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid128_multi_hci,
+	.expect_hci_len = sizeof(write_eir_uuid128_multi_hci),
+};
+
+static const struct generic_data add_uuid128_multi_test_2 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid128_param_2,
+	.send_len = sizeof(add_uuid128_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid128_multi_hci_2,
+	.expect_hci_len = sizeof(write_eir_uuid128_multi_hci_2),
+};
+
+static const struct generic_data add_uuid_mix_test_1 = {
+	.send_opcode = MGMT_OP_ADD_UUID,
+	.send_param = add_uuid128_param_2,
+	.send_len = sizeof(add_uuid128_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_dev_class_zero_rsp,
+	.expect_len = sizeof(set_dev_class_zero_rsp),
+	.expect_hci_command = BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+	.expect_hci_param = write_eir_uuid_mix_hci,
+	.expect_hci_len = sizeof(write_eir_uuid_mix_hci),
+};
+
+static const char load_link_keys_valid_param_1[] = { 0x00, 0x00, 0x00 };
+static const char load_link_keys_valid_param_2[] = { 0x01, 0x00, 0x00 };
+static const char load_link_keys_invalid_param_1[] = { 0x02, 0x00, 0x00 };
+static const char load_link_keys_invalid_param_2[] = { 0x00, 0x01, 0x00 };
+/* Invalid bdaddr type */
+static const char load_link_keys_invalid_param_3[] = { 0x00, 0x01, 0x00,
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05,		/* addr */
+	0x01,						/* addr type */
+	0x00,						/* key type */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* value (1/2) */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2) */
+	0x04,						/* PIN length */
+};
+
+static const struct generic_data load_link_keys_success_test_1 = {
+	.send_opcode = MGMT_OP_LOAD_LINK_KEYS,
+	.send_param = load_link_keys_valid_param_1,
+	.send_len = sizeof(load_link_keys_valid_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const struct generic_data load_link_keys_success_test_2 = {
+	.send_opcode = MGMT_OP_LOAD_LINK_KEYS,
+	.send_param = load_link_keys_valid_param_2,
+	.send_len = sizeof(load_link_keys_valid_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const struct generic_data load_link_keys_invalid_params_test_1 = {
+	.send_opcode = MGMT_OP_LOAD_LINK_KEYS,
+	.send_param = load_link_keys_invalid_param_1,
+	.send_len = sizeof(load_link_keys_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data load_link_keys_invalid_params_test_2 = {
+	.send_opcode = MGMT_OP_LOAD_LINK_KEYS,
+	.send_param = load_link_keys_invalid_param_2,
+	.send_len = sizeof(load_link_keys_invalid_param_2),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data load_link_keys_invalid_params_test_3 = {
+	.send_opcode = MGMT_OP_LOAD_LINK_KEYS,
+	.send_param = load_link_keys_invalid_param_3,
+	.send_len = sizeof(load_link_keys_invalid_param_3),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char load_ltks_valid_param_1[] = { 0x00, 0x00 };
+/* Invalid key count */
+static const char load_ltks_invalid_param_1[] = { 0x01, 0x00 };
+/* Invalid addr type */
+static const char load_ltks_invalid_param_2[] = {
+	0x01, 0x00,					/* count */
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05,		/* bdaddr */
+	0x00,						/* addr type */
+	0x00,						/* authenticated */
+	0x00,						/* master */
+	0x00,						/* encryption size */
+	0x00, 0x00,					/* diversifier */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* rand */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* value (1/2) */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* value (2/2) */
+};
+/* Invalid master value */
+static const char load_ltks_invalid_param_3[] = {
+	0x01, 0x00,					/* count */
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05,		/* bdaddr */
+	0x01,						/* addr type */
+	0x00,						/* authenticated */
+	0x02,						/* master */
+	0x00,						/* encryption size */
+	0x00, 0x00,					/* diversifier */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* rand */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* value (1/2) */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* value (2/2) */
+};
+
+static const struct generic_data load_ltks_success_test_1 = {
+	.send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS,
+	.send_param = load_ltks_valid_param_1,
+	.send_len = sizeof(load_ltks_valid_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const struct generic_data load_ltks_invalid_params_test_1 = {
+	.send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS,
+	.send_param = load_ltks_invalid_param_1,
+	.send_len = sizeof(load_ltks_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data load_ltks_invalid_params_test_2 = {
+	.send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS,
+	.send_param = load_ltks_invalid_param_2,
+	.send_len = sizeof(load_ltks_invalid_param_2),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data load_ltks_invalid_params_test_3 = {
+	.send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS,
+	.send_param = load_ltks_invalid_param_3,
+	.send_len = sizeof(load_ltks_invalid_param_3),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char load_ltks_invalid_param_4[22] = { 0x1d, 0x07 };
+static const struct generic_data load_ltks_invalid_params_test_4 = {
+	.send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS,
+	.send_param = load_ltks_invalid_param_4,
+	.send_len = sizeof(load_ltks_invalid_param_4),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char set_io_cap_invalid_param_1[] = { 0xff };
+
+static const struct generic_data set_io_cap_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_SET_IO_CAPABILITY,
+	.send_param = set_io_cap_invalid_param_1,
+	.send_len = sizeof(set_io_cap_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char pair_device_param[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
+static const char pair_device_rsp[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 };
+static const char pair_device_invalid_param_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff, 0x00 };
+static const char pair_device_invalid_param_rsp_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char pair_device_invalid_param_2[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x05 };
+static const char pair_device_invalid_param_rsp_2[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 };
+
+static const struct generic_data pair_device_not_powered_test_1 = {
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_param = pair_device_param,
+	.send_len = sizeof(pair_device_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.expect_param = pair_device_rsp,
+	.expect_len = sizeof(pair_device_rsp),
+};
+
+static const struct generic_data pair_device_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_param = pair_device_invalid_param_1,
+	.send_len = sizeof(pair_device_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = pair_device_invalid_param_rsp_1,
+	.expect_len = sizeof(pair_device_invalid_param_rsp_1),
+};
+
+static const struct generic_data pair_device_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_param = pair_device_invalid_param_2,
+	.send_len = sizeof(pair_device_invalid_param_2),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = pair_device_invalid_param_rsp_2,
+	.expect_len = sizeof(pair_device_invalid_param_rsp_2),
+};
+
+static const void *pair_device_send_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	static uint8_t param[8];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+
+	if (test->addr_type_avail)
+		param[6] = test->addr_type;
+	else if (data->hciemu_type == HCIEMU_TYPE_LE)
+		param[6] = 0x01; /* Address type */
+	else
+		param[6] = 0x00; /* Address type */
+	param[7] = test->io_cap;
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const void *pair_device_expect_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	static uint8_t param[7];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+
+	if (test->addr_type_avail)
+		param[6] = test->addr_type;
+	else if (data->hciemu_type == HCIEMU_TYPE_LE)
+		param[6] = 0x01; /* Address type */
+	else
+		param[6] = 0x00; /* Address type */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static uint16_t settings_powered_bondable[] = { MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_POWERED, 0 };
+static uint8_t auth_req_param[] = { 0x2a, 0x00 };
+static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */
+
+static const struct generic_data pair_device_success_test_1 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED,
+	.expect_hci_param = auth_req_param,
+	.expect_hci_len = sizeof(auth_req_param),
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+};
+
+static uint16_t settings_powered_bondable_linksec[] = { MGMT_OP_SET_BONDABLE,
+							MGMT_OP_SET_POWERED,
+							MGMT_OP_SET_LINK_SECURITY,
+							0 };
+
+static const struct generic_data pair_device_success_test_2 = {
+	.setup_settings = settings_powered_bondable_linksec,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED,
+	.expect_hci_param = auth_req_param,
+	.expect_hci_len = sizeof(auth_req_param),
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_legacy_nonbondable_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_power_off_test_1 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.force_power_off = true,
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.expect_func = pair_device_expect_param_func,
+};
+
+static const void *client_bdaddr_param_func(uint8_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t bdaddr[6];
+
+	memcpy(bdaddr, hciemu_get_client_bdaddr(data->hciemu), 6);
+
+	*len = sizeof(bdaddr);
+
+	return bdaddr;
+}
+
+static const struct generic_data pair_device_not_supported_test_1 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+	.expect_func = pair_device_expect_param_func,
+	.addr_type_avail = true,
+	.addr_type = BDADDR_BREDR,
+};
+
+static const struct generic_data pair_device_not_supported_test_2 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+	.expect_func = pair_device_expect_param_func,
+	.addr_type_avail = true,
+	.addr_type = BDADDR_LE_PUBLIC,
+};
+
+static uint16_t settings_powered_bondable_le[] = { MGMT_OP_SET_LE,
+							MGMT_OP_SET_BONDABLE,
+							MGMT_OP_SET_POWERED,
+							0 };
+
+static const struct generic_data pair_device_reject_transport_not_enabled_1 = {
+	.setup_settings = settings_powered_bondable_le,
+	.setup_nobredr = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_REJECTED,
+	.expect_func = pair_device_expect_param_func,
+	.addr_type_avail = true,
+	.addr_type = BDADDR_BREDR,
+};
+
+static const struct generic_data pair_device_reject_transport_not_enabled_2 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_REJECTED,
+	.expect_func = pair_device_expect_param_func,
+	.addr_type_avail = true,
+	.addr_type = BDADDR_LE_PUBLIC,
+};
+
+static const struct generic_data pair_device_reject_test_1 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.expect_pin = true,
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_reject_test_2 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED,
+	.expect_hci_param = auth_req_param,
+	.expect_hci_len = sizeof(auth_req_param),
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_reject_test_3 = {
+	.setup_settings = settings_powered_bondable_linksec,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.expect_pin = true,
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_reject_test_4 = {
+	.setup_settings = settings_powered_bondable_linksec,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+};
+
+static uint16_t settings_powered_bondable_ssp[] = {	MGMT_OP_SET_BONDABLE,
+							MGMT_OP_SET_SSP,
+							MGMT_OP_SET_POWERED,
+							0 };
+
+static const struct generic_data pair_device_ssp_test_1 = {
+	.setup_settings = settings_powered_bondable_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* NoInputNoOutput */
+};
+
+static const void *client_io_cap_param_func(uint8_t *len)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	static uint8_t param[9];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	memcpy(&param[6], test->expect_hci_param, 3);
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+const uint8_t no_bonding_io_cap[] = { 0x03, 0x00, 0x00 };
+static const struct generic_data pair_device_ssp_test_2 = {
+	.setup_settings = settings_powered_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+	.expect_hci_func = client_io_cap_param_func,
+	.expect_hci_param = no_bonding_io_cap,
+	.expect_hci_len = sizeof(no_bonding_io_cap),
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* NoInputNoOutput */
+};
+
+const uint8_t bonding_io_cap[] = { 0x03, 0x00, 0x02 };
+static const struct generic_data pair_device_ssp_test_3 = {
+	.setup_settings = settings_powered_bondable_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+	.expect_hci_func = client_io_cap_param_func,
+	.expect_hci_param = bonding_io_cap,
+	.expect_hci_len = sizeof(bonding_io_cap),
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* NoInputNoOutput */
+};
+
+static const struct generic_data pair_device_ssp_test_4 = {
+	.setup_settings = settings_powered_bondable_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+const uint8_t mitm_no_bonding_io_cap[] = { 0x01, 0x00, 0x01 };
+static const struct generic_data pair_device_ssp_test_5 = {
+	.setup_settings = settings_powered_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+	.expect_hci_func = client_io_cap_param_func,
+	.expect_hci_param = mitm_no_bonding_io_cap,
+	.expect_hci_len = sizeof(mitm_no_bonding_io_cap),
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+const uint8_t mitm_bonding_io_cap[] = { 0x01, 0x00, 0x03 };
+static const struct generic_data pair_device_ssp_test_6 = {
+	.setup_settings = settings_powered_bondable_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+	.expect_hci_func = client_io_cap_param_func,
+	.expect_hci_param = mitm_bonding_io_cap,
+	.expect_hci_len = sizeof(mitm_bonding_io_cap),
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static const struct generic_data pair_device_ssp_reject_1 = {
+	.setup_settings = settings_powered_bondable_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+	.client_auth_req = 0x01, /* No Bonding - MITM */
+	.reject_confirm = true,
+};
+
+static const struct generic_data pair_device_ssp_reject_2 = {
+	.setup_settings = settings_powered_bondable_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+	.client_reject_confirm = true,
+};
+
+static const struct generic_data pair_device_ssp_nonbondable_1 = {
+	.setup_settings = settings_powered_ssp,
+	.client_enable_ssp = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static const struct generic_data pair_device_le_success_test_1 = {
+	.setup_settings = settings_powered_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.just_works = true,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+};
+
+static bool ltk_is_authenticated(const struct mgmt_ltk_info *ltk)
+{
+	switch (ltk->type) {
+	case 0x01:
+	case 0x03:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool ltk_is_sc(const struct mgmt_ltk_info *ltk)
+{
+	switch (ltk->type) {
+	case 0x02:
+	case 0x03:
+	case 0x04:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool verify_ltk(const void *param, uint16_t length)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const struct mgmt_ev_new_long_term_key *ev = param;
+
+	if (length != sizeof(struct mgmt_ev_new_long_term_key)) {
+		tester_warn("Invalid new ltk length %u != %zu", length,
+				sizeof(struct mgmt_ev_new_long_term_key));
+		return false;
+	}
+
+	if (test->just_works && ltk_is_authenticated(&ev->key)) {
+		tester_warn("Authenticated key for just-works");
+		return false;
+	}
+
+	if (!test->just_works && !ltk_is_authenticated(&ev->key)) {
+		tester_warn("Unauthenticated key for MITM");
+		return false;
+	}
+
+	if (test->expect_sc_key && !ltk_is_sc(&ev->key)) {
+		tester_warn("Non-LE SC key for SC pairing");
+		return false;
+	}
+
+	if (!test->expect_sc_key && ltk_is_sc(&ev->key)) {
+		tester_warn("SC key for Non-SC pairing");
+		return false;
+	}
+
+	return true;
+}
+
+static const struct generic_data pair_device_le_success_test_2 = {
+	.setup_settings = settings_powered_bondable,
+	.io_cap = 0x02, /* KeyboardOnly */
+	.client_io_cap = 0x04, /* KeyboardDisplay */
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static uint16_t settings_powered_sc_bondable_le_ssp[] = {
+						MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_LE,
+						MGMT_OP_SET_SSP,
+						MGMT_OP_SET_SECURE_CONN,
+						MGMT_OP_SET_POWERED,
+						0 };
+
+static const struct generic_data pair_device_smp_bredr_test_1 = {
+	.setup_settings = settings_powered_sc_bondable_le_ssp,
+	.client_enable_ssp = true,
+	.client_enable_le = true,
+	.client_enable_sc = true,
+	.expect_sc_key = true,
+	.just_works = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* NoInputNoOutput */
+};
+
+static const struct generic_data pair_device_smp_bredr_test_2 = {
+	.setup_settings = settings_powered_sc_bondable_le_ssp,
+	.client_enable_ssp = true,
+	.client_enable_le = true,
+	.client_enable_sc = true,
+	.expect_sc_key = true,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static const struct generic_data pair_device_le_reject_test_1 = {
+	.setup_settings = settings_powered_bondable,
+	.io_cap = 0x02, /* KeyboardOnly */
+	.client_io_cap = 0x04, /* KeyboardDisplay */
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.expect_status = MGMT_STATUS_AUTH_FAILED,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_auth_failed),
+	.reject_confirm = true,
+};
+
+static uint16_t settings_powered_sc_bondable[] = { MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_SECURE_CONN,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pair_device_le_sc_legacy_test_1 = {
+	.setup_settings = settings_powered_sc_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.just_works = true,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static const struct generic_data pair_device_le_sc_success_test_1 = {
+	.setup_settings = settings_powered_sc_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.just_works = true,
+	.client_enable_sc = true,
+	.expect_sc_key = true,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static const struct generic_data pair_device_le_sc_success_test_2 = {
+	.setup_settings = settings_powered_sc_bondable,
+	.send_opcode = MGMT_OP_PAIR_DEVICE,
+	.send_func = pair_device_send_param_func,
+	.client_enable_sc = true,
+	.expect_sc_key = true,
+	.io_cap = 0x02, /* KeyboardOnly */
+	.client_io_cap = 0x02, /* KeyboardOnly */
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = pair_device_expect_param_func,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static uint16_t settings_powered_connectable_bondable[] = {
+						MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_legacy_1 = {
+	.setup_settings = settings_powered_connectable_bondable,
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+};
+
+static const struct generic_data pairing_acceptor_legacy_2 = {
+	.setup_settings = settings_powered_connectable_bondable,
+	.expect_pin = true,
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+};
+
+static const struct generic_data pairing_acceptor_legacy_3 = {
+	.setup_settings = settings_powered_connectable,
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+};
+
+static uint16_t settings_powered_connectable_bondable_linksec[] = {
+						MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_LINK_SECURITY,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_linksec_1 = {
+	.setup_settings = settings_powered_connectable_bondable_linksec,
+	.pin = pair_device_pin,
+	.pin_len = sizeof(pair_device_pin),
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+};
+
+static const struct generic_data pairing_acceptor_linksec_2 = {
+	.setup_settings = settings_powered_connectable_bondable_linksec,
+	.expect_pin = true,
+	.client_pin = pair_device_pin,
+	.client_pin_len = sizeof(pair_device_pin),
+	.expect_alt_ev = MGMT_EV_CONNECT_FAILED,
+	.expect_alt_ev_len = 8,
+};
+
+static uint16_t settings_powered_connectable_bondable_ssp[] = {
+						MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_SSP,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_ssp_1 = {
+	.setup_settings = settings_powered_connectable_bondable_ssp,
+	.client_enable_ssp = true,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* NoInputNoOutput */
+	.just_works = true,
+};
+
+static const struct generic_data pairing_acceptor_ssp_2 = {
+	.setup_settings = settings_powered_connectable_bondable_ssp,
+	.client_enable_ssp = true,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static const struct generic_data pairing_acceptor_ssp_3 = {
+	.setup_settings = settings_powered_connectable_bondable_ssp,
+	.client_enable_ssp = true,
+	.expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+	.expect_alt_ev_len = 26,
+	.expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+	.expect_hci_func = client_bdaddr_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+	.just_works = true,
+};
+
+static const void *client_io_cap_reject_param_func(uint8_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[7];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x18; /* Pairing Not Allowed */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static uint16_t settings_powered_connectable_ssp[] = {
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_SSP,
+						MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_ssp_4 = {
+	.setup_settings = settings_powered_connectable_ssp,
+	.client_enable_ssp = true,
+	.expect_alt_ev = MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = 8,
+	.expect_hci_command = BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY,
+	.expect_hci_func = client_io_cap_reject_param_func,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+	.client_auth_req = 0x02, /* Dedicated Bonding - No MITM */
+};
+
+static uint16_t settings_powered_sc_bondable_connectable_le_ssp[] = {
+						MGMT_OP_SET_BONDABLE,
+						MGMT_OP_SET_CONNECTABLE,
+						MGMT_OP_SET_LE,
+						MGMT_OP_SET_SSP,
+						MGMT_OP_SET_SECURE_CONN,
+						MGMT_OP_SET_POWERED,
+						0 };
+
+static const struct generic_data pairing_acceptor_smp_bredr_1 = {
+	.setup_settings = settings_powered_sc_bondable_connectable_le_ssp,
+	.client_enable_ssp = true,
+	.client_enable_le = true,
+	.client_enable_sc = true,
+	.expect_sc_key = true,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+	.just_works = true,
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* No InputNoOutput */
+	.client_auth_req = 0x00, /* No Bonding - No MITM */
+};
+
+static const struct generic_data pairing_acceptor_smp_bredr_2 = {
+	.setup_settings = settings_powered_sc_bondable_connectable_le_ssp,
+	.client_enable_ssp = true,
+	.client_enable_le = true,
+	.client_enable_sc = true,
+	.expect_sc_key = true,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+	.io_cap = 0x01, /* DisplayYesNo */
+	.client_io_cap = 0x01, /* DisplayYesNo */
+	.client_auth_req = 0x02, /* Dedicated Bonding - No MITM */
+};
+
+static uint16_t settings_powered_bondable_connectable_advertising[] = {
+					MGMT_OP_SET_BONDABLE,
+					MGMT_OP_SET_CONNECTABLE,
+					MGMT_OP_SET_ADVERTISING,
+					MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_le_1 = {
+	.setup_settings = settings_powered_bondable_connectable_advertising,
+	.io_cap = 0x03, /* NoInputNoOutput */
+	.client_io_cap = 0x03, /* NoInputNoOutput */
+	.just_works = true,
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static const struct generic_data pairing_acceptor_le_2 = {
+	.setup_settings = settings_powered_bondable_connectable_advertising,
+	.io_cap = 0x04, /* KeyboardDisplay */
+	.client_io_cap = 0x04, /* KeyboardDisplay */
+	.client_auth_req = 0x05, /* Bonding - MITM */
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static const struct generic_data pairing_acceptor_le_3 = {
+	.setup_settings = settings_powered_bondable_connectable_advertising,
+	.io_cap = 0x04, /* KeyboardDisplay */
+	.client_io_cap = 0x04, /* KeyboardDisplay */
+	.expect_alt_ev =  MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_auth_failed),
+	.reject_confirm = true,
+};
+
+static const struct generic_data pairing_acceptor_le_4 = {
+	.setup_settings = settings_powered_bondable_connectable_advertising,
+	.io_cap = 0x02, /* KeyboardOnly */
+	.client_io_cap = 0x04, /* KeyboardDisplay */
+	.client_auth_req = 0x05, /* Bonding - MITM */
+	.expect_alt_ev =  MGMT_EV_NEW_LONG_TERM_KEY,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+	.verify_alt_ev_func = verify_ltk,
+};
+
+static const struct generic_data pairing_acceptor_le_5 = {
+	.setup_settings = settings_powered_bondable_connectable_advertising,
+	.io_cap = 0x02, /* KeyboardOnly */
+	.client_io_cap = 0x04, /* KeyboardDisplay */
+	.client_auth_req = 0x05, /* Bonding - MITM */
+	.reject_confirm = true,
+	.expect_alt_ev =  MGMT_EV_AUTH_FAILED,
+	.expect_alt_ev_len = sizeof(struct mgmt_ev_auth_failed),
+};
+
+static const char unpair_device_param[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
+static const char unpair_device_rsp[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 };
+static const char unpair_device_invalid_param_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff, 0x00 };
+static const char unpair_device_invalid_param_rsp_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char unpair_device_invalid_param_2[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x02 };
+static const char unpair_device_invalid_param_rsp_2[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 };
+
+static const struct generic_data unpair_device_not_powered_test_1 = {
+	.send_opcode = MGMT_OP_UNPAIR_DEVICE,
+	.send_param = unpair_device_param,
+	.send_len = sizeof(unpair_device_param),
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.expect_param = unpair_device_rsp,
+	.expect_len = sizeof(unpair_device_rsp),
+};
+
+static const struct generic_data unpair_device_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_UNPAIR_DEVICE,
+	.send_param = unpair_device_invalid_param_1,
+	.send_len = sizeof(unpair_device_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = unpair_device_invalid_param_rsp_1,
+	.expect_len = sizeof(unpair_device_invalid_param_rsp_1),
+};
+
+static const struct generic_data unpair_device_invalid_param_test_2 = {
+	.send_opcode = MGMT_OP_UNPAIR_DEVICE,
+	.send_param = unpair_device_invalid_param_2,
+	.send_len = sizeof(unpair_device_invalid_param_2),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = unpair_device_invalid_param_rsp_2,
+	.expect_len = sizeof(unpair_device_invalid_param_rsp_2),
+};
+
+static const char disconnect_invalid_param_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char disconnect_invalid_param_rsp_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+
+static const struct generic_data disconnect_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_DISCONNECT,
+	.send_param = disconnect_invalid_param_1,
+	.send_len = sizeof(disconnect_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = disconnect_invalid_param_rsp_1,
+	.expect_len = sizeof(disconnect_invalid_param_rsp_1),
+};
+
+static const char block_device_invalid_param_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char block_device_invalid_param_rsp_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+
+static const struct generic_data block_device_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_BLOCK_DEVICE,
+	.send_param = block_device_invalid_param_1,
+	.send_len = sizeof(block_device_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = block_device_invalid_param_rsp_1,
+	.expect_len = sizeof(block_device_invalid_param_rsp_1),
+};
+
+static const char unblock_device_invalid_param_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+static const char unblock_device_invalid_param_rsp_1[] = {
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+
+static const struct generic_data unblock_device_invalid_param_test_1 = {
+	.send_opcode = MGMT_OP_UNBLOCK_DEVICE,
+	.send_param = unblock_device_invalid_param_1,
+	.send_len = sizeof(unblock_device_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+	.expect_param = unblock_device_invalid_param_rsp_1,
+	.expect_len = sizeof(unblock_device_invalid_param_rsp_1),
+};
+
+static const char set_static_addr_valid_param[] = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0xc0 };
+static const char set_static_addr_settings[] = { 0x00, 0x82, 0x00, 0x00 };
+
+static const struct generic_data set_static_addr_success_test = {
+	.send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
+	.send_param = set_static_addr_valid_param,
+	.send_len = sizeof(set_static_addr_valid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_static_addr_settings,
+	.expect_len = sizeof(set_static_addr_settings),
+	.expect_settings_set = MGMT_SETTING_STATIC_ADDRESS,
+};
+
+static const char set_static_addr_settings_dual[] = { 0x80, 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_static_addr_success_test_2 = {
+	.send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
+	.send_param = set_static_addr_valid_param,
+	.send_len = sizeof(set_static_addr_valid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_static_addr_settings_dual,
+	.expect_len = sizeof(set_static_addr_settings_dual),
+};
+
+static const struct generic_data set_static_addr_failure_test = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
+	.send_param = set_static_addr_valid_param,
+	.send_len = sizeof(set_static_addr_valid_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data set_static_addr_failure_test_2 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
+	.send_param = set_static_addr_valid_param,
+	.send_len = sizeof(set_static_addr_valid_param),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const char set_scan_params_valid_param[] = { 0x60, 0x00, 0x30, 0x00 };
+
+static const struct generic_data set_scan_params_success_test = {
+	.send_opcode = MGMT_OP_SET_SCAN_PARAMS,
+	.send_param = set_scan_params_valid_param,
+	.send_len = sizeof(set_scan_params_valid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const char load_irks_empty_list[] = { 0x00, 0x00 };
+
+static const struct generic_data load_irks_success1_test = {
+	.send_opcode = MGMT_OP_LOAD_IRKS,
+	.send_param = load_irks_empty_list,
+	.send_len = sizeof(load_irks_empty_list),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const char load_irks_one_irk[] = { 0x01, 0x00,
+			0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x01,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct generic_data load_irks_success2_test = {
+	.send_opcode = MGMT_OP_LOAD_IRKS,
+	.send_param = load_irks_one_irk,
+	.send_len = sizeof(load_irks_one_irk),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const char load_irks_nval_addr_type[] = { 0x01, 0x00,
+			0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct generic_data load_irks_nval_param1_test = {
+	.send_opcode = MGMT_OP_LOAD_IRKS,
+	.send_param = load_irks_nval_addr_type,
+	.send_len = sizeof(load_irks_nval_addr_type),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char load_irks_nval_rand_addr[] = { 0x01, 0x00,
+			0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x02,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct generic_data load_irks_nval_param2_test = {
+	.send_opcode = MGMT_OP_LOAD_IRKS,
+	.send_param = load_irks_nval_rand_addr,
+	.send_len = sizeof(load_irks_nval_rand_addr),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char load_irks_nval_len[] = { 0x02, 0x00, 0xff, 0xff };
+
+static const struct generic_data load_irks_nval_param3_test = {
+	.send_opcode = MGMT_OP_LOAD_IRKS,
+	.send_param = load_irks_nval_len,
+	.send_len = sizeof(load_irks_nval_len),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data load_irks_not_supported_test = {
+	.send_opcode = MGMT_OP_LOAD_IRKS,
+	.send_param = load_irks_empty_list,
+	.send_len = sizeof(load_irks_empty_list),
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const char set_privacy_valid_param[] = { 0x01,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+static const char set_privacy_settings_param[] = { 0x80, 0x20, 0x00, 0x00 };
+
+static const struct generic_data set_privacy_success_test = {
+	.send_opcode = MGMT_OP_SET_PRIVACY,
+	.send_param = set_privacy_valid_param,
+	.send_len = sizeof(set_privacy_valid_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_privacy_settings_param,
+	.expect_len = sizeof(set_privacy_settings_param),
+	.expect_settings_set = MGMT_SETTING_PRIVACY,
+};
+
+static const struct generic_data set_privacy_powered_test = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_SET_PRIVACY,
+	.send_param = set_privacy_valid_param,
+	.send_len = sizeof(set_privacy_valid_param),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const char set_privacy_nval_param[] = { 0xff,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+static const struct generic_data set_privacy_nval_param_test = {
+	.send_opcode = MGMT_OP_SET_PRIVACY,
+	.send_param = set_privacy_nval_param,
+	.send_len = sizeof(set_privacy_nval_param),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const void *get_conn_info_send_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[7];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const void *get_conn_info_expect_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[10];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+	param[7] = 0xff; /* RSSI (= -1) */
+	param[8] = 0xff; /* TX power (= -1) */
+	param[9] = 0x04; /* max TX power */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const void *get_conn_info_error_expect_param_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[10];
+
+	/* All unset parameters shall be 0 in case of error */
+	memset(param, 0, sizeof(param));
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const struct generic_data get_conn_info_succes1_test = {
+	.setup_settings = settings_powered_connectable_bondable_ssp,
+	.send_opcode = MGMT_OP_GET_CONN_INFO,
+	.send_func = get_conn_info_send_param_func,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_func = get_conn_info_expect_param_func,
+};
+
+static const struct generic_data get_conn_info_ncon_test = {
+	.setup_settings = settings_powered_connectable_bondable_ssp,
+	.send_opcode = MGMT_OP_GET_CONN_INFO,
+	.send_func = get_conn_info_send_param_func,
+	.expect_status = MGMT_STATUS_NOT_CONNECTED,
+	.expect_func = get_conn_info_error_expect_param_func,
+};
+
+static const void *get_conn_info_expect_param_power_off_func(uint16_t *len)
+{
+	struct test_data *data = tester_get_data();
+	static uint8_t param[10];
+
+	memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+	param[6] = 0x00; /* Address type */
+	param[7] = 127; /* RSSI */
+	param[8] = 127; /* TX power */
+	param[9] = 127; /* max TX power */
+
+	*len = sizeof(param);
+
+	return param;
+}
+
+static const struct generic_data get_conn_info_power_off_test = {
+	.setup_settings = settings_powered_connectable_bondable_ssp,
+	.send_opcode = MGMT_OP_GET_CONN_INFO,
+	.send_func = get_conn_info_send_param_func,
+	.force_power_off = true,
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+	.expect_func = get_conn_info_expect_param_power_off_func,
+};
+
+static const uint8_t load_conn_param_nval_1[16] = { 0x12, 0x11 };
+static const struct generic_data load_conn_params_fail_1 = {
+	.send_opcode = MGMT_OP_LOAD_CONN_PARAM,
+	.send_param = load_conn_param_nval_1,
+	.send_len = sizeof(load_conn_param_nval_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t add_device_nval_1[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x00,
+					0x00,
+};
+static const uint8_t add_device_rsp[] =  {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x00,
+};
+static const struct generic_data add_device_fail_1 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_nval_1,
+	.send_len = sizeof(add_device_nval_1),
+	.expect_param = add_device_rsp,
+	.expect_len = sizeof(add_device_rsp),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t add_device_nval_2[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x00,
+					0x02,
+};
+static const struct generic_data add_device_fail_2 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_nval_2,
+	.send_len = sizeof(add_device_nval_2),
+	.expect_param = add_device_rsp,
+	.expect_len = sizeof(add_device_rsp),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t add_device_nval_3[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x00,
+					0xff,
+};
+static const struct generic_data add_device_fail_3 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_nval_3,
+	.send_len = sizeof(add_device_nval_3),
+	.expect_param = add_device_rsp,
+	.expect_len = sizeof(add_device_rsp),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t add_device_nval_4[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x02,
+					0x02,
+};
+static const uint8_t add_device_rsp_4[] =  {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x02,
+};
+static const struct generic_data add_device_fail_4 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_nval_4,
+	.send_len = sizeof(add_device_nval_4),
+	.expect_param = add_device_rsp_4,
+	.expect_len = sizeof(add_device_rsp_4),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t add_device_success_param_1[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x00,
+					0x01,
+};
+static const struct generic_data add_device_success_1 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_success_param_1,
+	.send_len = sizeof(add_device_success_param_1),
+	.expect_param = add_device_rsp,
+	.expect_len = sizeof(add_device_rsp),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_ADDED,
+	.expect_alt_ev_param = add_device_success_param_1,
+	.expect_alt_ev_len = sizeof(add_device_success_param_1),
+};
+
+static const uint8_t add_device_success_param_2[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x01,
+					0x00,
+};
+static const uint8_t add_device_rsp_le[] =  {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x01,
+};
+static const struct generic_data add_device_success_2 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_success_param_2,
+	.send_len = sizeof(add_device_success_param_2),
+	.expect_param = add_device_rsp_le,
+	.expect_len = sizeof(add_device_rsp_le),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_ADDED,
+	.expect_alt_ev_param = add_device_success_param_2,
+	.expect_alt_ev_len = sizeof(add_device_success_param_2),
+};
+
+static const uint8_t add_device_success_param_3[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x01,
+					0x02,
+};
+static const struct generic_data add_device_success_3 = {
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_success_param_3,
+	.send_len = sizeof(add_device_success_param_3),
+	.expect_param = add_device_rsp_le,
+	.expect_len = sizeof(add_device_rsp_le),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_ADDED,
+	.expect_alt_ev_param = add_device_success_param_3,
+	.expect_alt_ev_len = sizeof(add_device_success_param_3),
+};
+
+static const struct generic_data add_device_success_4 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_success_param_1,
+	.send_len = sizeof(add_device_success_param_1),
+	.expect_param = add_device_rsp,
+	.expect_len = sizeof(add_device_rsp),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_ADDED,
+	.expect_alt_ev_param = add_device_success_param_1,
+	.expect_alt_ev_len = sizeof(add_device_success_param_1),
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_connectable_scan_enable_param,
+	.expect_hci_len = sizeof(set_connectable_scan_enable_param),
+};
+
+static const uint8_t le_scan_enable[] = { 0x01, 0x01 };
+static const struct generic_data add_device_success_5 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_DEVICE,
+	.send_param = add_device_success_param_2,
+	.send_len = sizeof(add_device_success_param_2),
+	.expect_param = add_device_rsp_le,
+	.expect_len = sizeof(add_device_rsp_le),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_ADDED,
+	.expect_alt_ev_param = add_device_success_param_2,
+	.expect_alt_ev_len = sizeof(add_device_success_param_2),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+	.expect_hci_param = le_scan_enable,
+	.expect_hci_len = sizeof(le_scan_enable),
+};
+
+static const uint8_t remove_device_nval_1[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0xff,
+};
+static const struct generic_data remove_device_fail_1 = {
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_nval_1,
+	.send_len = sizeof(remove_device_nval_1),
+	.expect_param = remove_device_nval_1,
+	.expect_len = sizeof(remove_device_nval_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t remove_device_param_1[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x00,
+};
+static const struct generic_data remove_device_fail_2 = {
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_1,
+	.send_len = sizeof(remove_device_param_1),
+	.expect_param = remove_device_param_1,
+	.expect_len = sizeof(remove_device_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const uint8_t remove_device_param_3[] = {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x02,
+};
+static const struct generic_data remove_device_fail_3 = {
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_3,
+	.send_len = sizeof(remove_device_param_3),
+	.expect_param = remove_device_param_3,
+	.expect_len = sizeof(remove_device_param_3),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data remove_device_success_1 = {
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_1,
+	.send_len = sizeof(remove_device_param_1),
+	.expect_param = remove_device_param_1,
+	.expect_len = sizeof(remove_device_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_REMOVED,
+	.expect_alt_ev_param = remove_device_param_1,
+	.expect_alt_ev_len = sizeof(remove_device_param_1),
+};
+
+static const struct generic_data remove_device_success_2 = {
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_1,
+	.send_len = sizeof(remove_device_param_1),
+	.expect_param = remove_device_param_1,
+	.expect_len = sizeof(remove_device_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_REMOVED,
+	.expect_alt_ev_param = remove_device_param_1,
+	.expect_alt_ev_len = sizeof(remove_device_param_1),
+	.expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE,
+	.expect_hci_param = set_connectable_off_scan_enable_param,
+	.expect_hci_len = sizeof(set_connectable_off_scan_enable_param),
+};
+
+static const struct generic_data remove_device_success_3 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_1,
+	.send_len = sizeof(remove_device_param_1),
+	.expect_param = remove_device_param_1,
+	.expect_len = sizeof(remove_device_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_REMOVED,
+	.expect_alt_ev_param = remove_device_param_1,
+	.expect_alt_ev_len = sizeof(remove_device_param_1),
+};
+
+static const uint8_t remove_device_param_2[] =  {
+					0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+					0x01,
+};
+static const struct generic_data remove_device_success_4 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_2,
+	.send_len = sizeof(remove_device_param_2),
+	.expect_param = remove_device_param_2,
+	.expect_len = sizeof(remove_device_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_REMOVED,
+	.expect_alt_ev_param = remove_device_param_2,
+	.expect_alt_ev_len = sizeof(remove_device_param_2),
+};
+
+static const struct generic_data remove_device_success_5 = {
+	.send_opcode = MGMT_OP_REMOVE_DEVICE,
+	.send_param = remove_device_param_2,
+	.send_len = sizeof(remove_device_param_2),
+	.expect_param = remove_device_param_2,
+	.expect_len = sizeof(remove_device_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_DEVICE_REMOVED,
+	.expect_alt_ev_param = remove_device_param_2,
+	.expect_alt_ev_len = sizeof(remove_device_param_2),
+};
+
+static const struct generic_data read_adv_features_invalid_param_test = {
+	.send_opcode = MGMT_OP_READ_ADV_FEATURES,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_adv_features_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_ADV_FEATURES,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const uint8_t read_adv_features_rsp_1[] =  {
+	0x1f, 0x00, 0x00, 0x00,	/* supported flags */
+	0x1f,			/* max_adv_data_len */
+	0x1f,			/* max_scan_rsp_len */
+	0x05,			/* max_instances */
+	0x00,			/* num_instances */
+};
+
+static const struct generic_data read_adv_features_success_1 = {
+	.send_opcode = MGMT_OP_READ_ADV_FEATURES,
+	.expect_param = read_adv_features_rsp_1,
+	.expect_len = sizeof(read_adv_features_rsp_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const uint8_t read_adv_features_rsp_2[] =  {
+	0x1f, 0x00, 0x00, 0x00,	/* supported flags */
+	0x1f,			/* max_adv_data_len */
+	0x1f,			/* max_scan_rsp_len */
+	0x05,			/* max_instances */
+	0x01,			/* num_instances */
+	0x01,			/* instance identifiers */
+};
+
+static const struct generic_data read_adv_features_success_2 = {
+	.send_opcode = MGMT_OP_READ_ADV_FEATURES,
+	.expect_param = read_adv_features_rsp_2,
+	.expect_len = sizeof(read_adv_features_rsp_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+};
+
+/* simple add advertising command */
+static const uint8_t add_advertising_param_uuid[] = {
+	0x01,			/* adv instance */
+	0x00, 0x00, 0x00, 0x00,	/* flags: none */
+	0x00, 0x00,		/* duration: default */
+	0x00, 0x00,		/* timeout: none */
+	0x09,			/* adv data len */
+	0x00,			/* scan rsp len */
+	/* adv data: */
+	0x03,			/* AD len */
+	0x02,			/* AD type: some 16 bit service class UUIDs */
+	0x0d, 0x18,		/* heart rate monitor */
+	0x04,			/* AD len */
+	0xff,			/* AD type: manufacturer specific data */
+	0x01, 0x02, 0x03,	/* custom advertising data */
+};
+
+/* add advertising with scan response data */
+static const uint8_t add_advertising_param_scanrsp[] = {
+	/* instance, flags, duration, timeout, adv data len: same as before */
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x0a,			/* scan rsp len */
+	/* adv data: same as before */
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+	/* scan rsp data: */
+	0x03,			/* AD len */
+	0x19,			/* AD type: external appearance */
+	0x40, 0x03,		/* some custom appearance */
+	0x05,			/* AD len */
+	0x03,			/* AD type: all 16 bit service class UUIDs */
+	0x0d, 0x18,		/* heart rate monitor */
+	0x0f, 0x18,		/* battery service */
+};
+
+/* add advertising with timeout */
+static const uint8_t add_advertising_param_timeout[] = {
+	/* instance, flags, duration: same as before */
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x05, 0x00,		/* timeout: 5 seconds */
+	/* adv data: same as before */
+	0x09, 0x00, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+/* add advertising with connectable flag */
+static const uint8_t add_advertising_param_connectable[] = {
+	0x01,			/* adv instance */
+	0x01, 0x00, 0x00, 0x00,	/* flags: connectable*/
+	/* duration, timeout, adv/scan data: same as before */
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+/* add advertising with general discoverable flag */
+static const uint8_t add_advertising_param_general_discov[] = {
+	0x01,			/* adv instance */
+	0x02, 0x00, 0x00, 0x00,	/* flags: general discoverable*/
+	/* duration, timeout, adv/scan data: same as before */
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+/* add advertising with limited discoverable flag */
+static const uint8_t add_advertising_param_limited_discov[] = {
+	0x01,			/* adv instance */
+	0x04, 0x00, 0x00, 0x00,	/* flags: limited discoverable */
+	/* duration, timeout, adv/scan data: same as before */
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+/* add advertising with managed flags */
+static const uint8_t add_advertising_param_managed[] = {
+	0x01,			/* adv instance */
+	0x08, 0x00, 0x00, 0x00,	/* flags: managed flags */
+	/* duration, timeout, adv/scan data: same as before */
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+/* add advertising with tx power flag */
+static const uint8_t add_advertising_param_txpwr[] = {
+	0x01,			/* adv instance */
+	0x10, 0x00, 0x00, 0x00,	/* flags: tx power */
+	/* duration, timeout, adv/scan data: same as before */
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+/* add advertising command for a second instance */
+static const uint8_t add_advertising_param_test2[] = {
+	0x02,				/* adv instance */
+	0x00, 0x00, 0x00, 0x00,		/* flags: none */
+	0x00, 0x00,			/* duration: default */
+	0x01, 0x00,			/* timeout: 1 second */
+	0x07,				/* adv data len */
+	0x00,				/* scan rsp len */
+	/* adv data: */
+	0x06,				/* AD len */
+	0x08,				/* AD type: shortened local name */
+	0x74, 0x65, 0x73, 0x74, 0x32,	/* "test2" */
+};
+
+static const uint8_t advertising_instance1_param[] = {
+	0x01,
+};
+
+static const uint8_t advertising_instance2_param[] = {
+	0x02,
+};
+
+static const uint8_t set_adv_data_uuid[] = {
+	/* adv data len */
+	0x09,
+	/* advertise heart rate monitor and manufacturer specific data */
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_test1[] = {
+	0x07,				/* adv data len */
+	0x06,				/* AD len */
+	0x08,				/* AD type: shortened local name */
+	0x74, 0x65, 0x73, 0x74, 0x31,	/* "test1" */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_test2[] = {
+	0x07,				/* adv data len */
+	0x06,				/* AD len */
+	0x08,				/* AD type: shortened local name */
+	0x74, 0x65, 0x73, 0x74, 0x32,	/* "test2" */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_txpwr[] = {
+	0x03,			/* adv data len */
+	0x02, 			/* AD len */
+	0x0a,			/* AD type: tx power */
+	0x00,			/* tx power */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_general_discov[] = {
+	0x0c,			/* adv data len */
+	0x02,			/* AD len */
+	0x01,			/* AD type: flags */
+	0x02,			/* general discoverable */
+	0x03,			/* AD len */
+	0x02,			/* AD type: some 16bit service class UUIDs */
+	0x0d, 0x18,		/* heart rate monitor */
+	0x04,			/* AD len */
+	0xff,			/* AD type: manufacturer specific data */
+	0x01, 0x02, 0x03,	/* custom advertising data */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_limited_discov[] = {
+	0x0c,			/* adv data len */
+	0x02,			/* AD len */
+	0x01,			/* AD type: flags */
+	0x01,			/* limited discoverable */
+	/* rest: same as before */
+	0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_uuid_txpwr[] = {
+	0x0c,			/* adv data len */
+	0x03,			/* AD len */
+	0x02,			/* AD type: some 16bit service class UUIDs */
+	0x0d, 0x18,		/* heart rate monitor */
+	0x04,			/* AD len */
+	0xff,			/* AD type: manufacturer specific data */
+	0x01, 0x02, 0x03,	/* custom advertising data */
+	0x02,			/* AD len */
+	0x0a,			/* AD type: tx power */
+	0x00,			/* tx power */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_scan_rsp_uuid[] = {
+	0x0a,			/* scan rsp data len */
+	0x03,			/* AD len */
+	0x19,			/* AD type: external appearance */
+	0x40, 0x03,		/* some custom appearance */
+	0x05,			/* AD len */
+	0x03,			/* AD type: all 16 bit service class UUIDs */
+	0x0d, 0x18, 0x0f, 0x18,	/* heart rate monitor, battery service */
+	/* padding */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00,
+};
+
+static const uint8_t add_advertising_invalid_param_1[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+	0x03, 0x03, 0x0d, 0x18,
+	0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_2[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x04, 0x03, 0x0d, 0x18,
+	0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_3[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x03, 0x0d, 0x18,
+	0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_4[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+	0x03, 0x03, 0x0d, 0x18,
+	0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_5[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00,
+	0x03, 0x03, 0x0d, 0x18,
+	0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+	0x15, 0x16, 0x17, 0x18,
+};
+
+static const uint8_t add_advertising_invalid_param_6[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+	0x03, 0x03, 0x0d, 0x18,
+	0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_7[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x04, 0x03, 0x0d, 0x18,
+	0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_8[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x03, 0x03, 0x0d, 0x18,
+	0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_9[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x03, 0x03, 0x0d, 0x18,
+	0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_10[] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
+	0x03, 0x03, 0x0d, 0x18,
+	0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+	0x15, 0x16, 0x17, 0x18,
+};
+
+static const struct generic_data add_advertising_fail_1 = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_uuid,
+	.send_len = sizeof(add_advertising_param_uuid),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_fail_2 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_1,
+	.send_len = sizeof(add_advertising_invalid_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_3 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_2,
+	.send_len = sizeof(add_advertising_invalid_param_2),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_4 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_3,
+	.send_len = sizeof(add_advertising_invalid_param_3),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_5 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_4,
+	.send_len = sizeof(add_advertising_invalid_param_4),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_6 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_5,
+	.send_len = sizeof(add_advertising_invalid_param_5),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_7 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_6,
+	.send_len = sizeof(add_advertising_invalid_param_6),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_8 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_7,
+	.send_len = sizeof(add_advertising_invalid_param_7),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_9 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_8,
+	.send_len = sizeof(add_advertising_invalid_param_8),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_10 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_9,
+	.send_len = sizeof(add_advertising_invalid_param_9),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_11 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_invalid_param_10,
+	.send_len = sizeof(add_advertising_invalid_param_10),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_12 = {
+	.setup_settings = settings_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_timeout,
+	.send_len = sizeof(add_advertising_param_timeout),
+	.expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_success_1 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_uuid,
+	.send_len = sizeof(add_advertising_param_uuid),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_uuid,
+	.expect_hci_len = sizeof(set_adv_data_uuid),
+};
+
+static const char set_powered_adv_instance_settings_param[] = {
+	0x81, 0x02, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_success_pwron_data = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_adv_instance_settings_param,
+	.expect_len = sizeof(set_powered_adv_instance_settings_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_test1,
+	.expect_hci_len = sizeof(set_adv_data_test1),
+};
+
+static const struct generic_data add_advertising_success_pwron_enabled = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_on_param,
+	.send_len = sizeof(set_powered_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_adv_instance_settings_param,
+	.expect_len = sizeof(set_powered_adv_instance_settings_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+	.expect_hci_param = set_adv_on_set_adv_enable_param,
+	.expect_hci_len = sizeof(set_adv_on_set_adv_enable_param),
+};
+
+static const struct generic_data add_advertising_success_4 = {
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_on_param,
+	.send_len = sizeof(set_adv_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_adv_settings_param_2,
+	.expect_len = sizeof(set_adv_settings_param_2),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_txpwr,
+	.expect_hci_len = sizeof(set_adv_data_txpwr),
+};
+
+static const struct generic_data add_advertising_success_5 = {
+	.send_opcode = MGMT_OP_SET_ADVERTISING,
+	.send_param = set_adv_off_param,
+	.send_len = sizeof(set_adv_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_adv_instance_settings_param,
+	.expect_len = sizeof(set_powered_adv_instance_settings_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_test1,
+	.expect_hci_len = sizeof(set_adv_data_test1),
+};
+
+static const struct generic_data add_advertising_success_6 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scanrsp,
+	.send_len = sizeof(add_advertising_param_scanrsp),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_uuid,
+	.expect_hci_len = sizeof(set_adv_data_uuid),
+};
+
+static const struct generic_data add_advertising_success_7 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scanrsp,
+	.send_len = sizeof(add_advertising_param_scanrsp),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+	.expect_hci_param = set_scan_rsp_uuid,
+	.expect_hci_len = sizeof(set_scan_rsp_uuid),
+};
+
+static const struct generic_data add_advertising_success_8 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_connectable,
+	.send_len = sizeof(add_advertising_param_connectable),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_on_adv_param,
+	.expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_9 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_general_discov,
+	.send_len = sizeof(add_advertising_param_general_discov),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_general_discov,
+	.expect_hci_len = sizeof(set_adv_data_general_discov),
+};
+
+static const struct generic_data add_advertising_success_10 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_limited_discov,
+	.send_len = sizeof(add_advertising_param_limited_discov),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_limited_discov,
+	.expect_hci_len = sizeof(set_adv_data_limited_discov),
+};
+
+static const struct generic_data add_advertising_success_11 = {
+	.setup_settings = settings_powered_le_discoverable,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_managed,
+	.send_len = sizeof(add_advertising_param_managed),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_general_discov,
+	.expect_hci_len = sizeof(set_adv_data_general_discov),
+};
+
+static const struct generic_data add_advertising_success_12 = {
+	.setup_settings = settings_powered_le_discoverable,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_txpwr,
+	.send_len = sizeof(add_advertising_param_txpwr),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_uuid_txpwr,
+	.expect_hci_len = sizeof(set_adv_data_uuid_txpwr),
+};
+
+static uint16_t settings_powered_le_connectable[] = {
+						MGMT_OP_SET_POWERED,
+						MGMT_OP_SET_LE,
+						MGMT_OP_SET_CONNECTABLE, 0 };
+
+static uint8_t set_connectable_off_scan_adv_param[] = {
+		0x00, 0x08,				/* min_interval */
+		0x00, 0x08,				/* max_interval */
+		0x02,					/* type */
+		0x01,					/* own_addr_type */
+		0x00,					/* direct_addr_type */
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* direct_addr */
+		0x07,					/* channel_map */
+		0x00,					/* filter_policy */
+};
+
+static const struct generic_data add_advertising_success_13 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_scanrsp,
+	.send_len = sizeof(add_advertising_param_scanrsp),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_off_scan_adv_param,
+	.expect_hci_len = sizeof(set_connectable_off_scan_adv_param),
+};
+
+static const struct generic_data add_advertising_success_14 = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_uuid,
+	.send_len = sizeof(add_advertising_param_uuid),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_off_adv_param,
+	.expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const struct generic_data add_advertising_success_15 = {
+	.setup_settings = settings_powered_le_connectable,
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_uuid,
+	.send_len = sizeof(add_advertising_param_uuid),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_on_adv_param,
+	.expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const char set_connectable_settings_param_3[] = {
+						0x83, 0x02, 0x00, 0x00 };
+
+static const struct generic_data add_advertising_success_16 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_on_param,
+	.send_len = sizeof(set_connectable_on_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_connectable_settings_param_3,
+	.expect_len = sizeof(set_connectable_settings_param_3),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_on_adv_param,
+	.expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_17 = {
+	.send_opcode = MGMT_OP_SET_CONNECTABLE,
+	.send_param = set_connectable_off_param,
+	.send_len = sizeof(set_connectable_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_le_settings_param_2,
+	.expect_len = sizeof(set_le_settings_param_2),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+	.expect_hci_param = set_connectable_off_adv_param,
+	.expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const char set_powered_off_le_settings_param[] = {
+	0x80, 0x02, 0x00, 0x00
+};
+
+static const struct generic_data add_advertising_power_off = {
+	.send_opcode = MGMT_OP_SET_POWERED,
+	.send_param = set_powered_off_param,
+	.send_len = sizeof(set_powered_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_powered_off_le_settings_param,
+	.expect_len = sizeof(set_powered_off_le_settings_param),
+	.expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+};
+
+static const char set_le_settings_param_off[] = { 0x81, 0x00, 0x00, 0x00 };
+
+static const struct generic_data add_advertising_le_off = {
+	.send_opcode = MGMT_OP_SET_LE,
+	.send_param = set_le_off_param,
+	.send_len = sizeof(set_le_off_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = set_le_settings_param_off,
+	.expect_len = sizeof(set_le_settings_param_off),
+	.expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+};
+
+static const struct generic_data add_advertising_success_18 = {
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_uuid,
+	.send_len = sizeof(add_advertising_param_uuid),
+	.expect_param = advertising_instance1_param,
+	.expect_len = sizeof(advertising_instance1_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_uuid,
+	.expect_hci_len = sizeof(set_adv_data_uuid),
+};
+
+static const struct generic_data add_advertising_timeout_expired = {
+	.expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+	.expect_hci_param = set_adv_on_set_adv_disable_param,
+	.expect_hci_len = sizeof(set_adv_on_set_adv_disable_param),
+};
+
+static const uint8_t remove_advertising_param_1[] = {
+	0x01,
+};
+
+static const uint8_t remove_advertising_param_2[] = {
+	0x00,
+};
+
+static const struct generic_data remove_advertising_fail_1 = {
+	.send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+	.send_param = remove_advertising_param_1,
+	.send_len = sizeof(remove_advertising_param_1),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data remove_advertising_success_1 = {
+	.send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+	.send_param = remove_advertising_param_1,
+	.send_len = sizeof(remove_advertising_param_1),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = remove_advertising_param_1,
+	.expect_len = sizeof(remove_advertising_param_1),
+	.expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+	.expect_hci_param = set_adv_off_param,
+	.expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data remove_advertising_success_2 = {
+	.send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+	.send_param = remove_advertising_param_2,
+	.send_len = sizeof(remove_advertising_param_2),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = remove_advertising_param_2,
+	.expect_len = sizeof(remove_advertising_param_2),
+	.expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+	.expect_hci_param = set_adv_off_param,
+	.expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data multi_advertising_switch = {
+	.expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+	.expect_alt_ev_param = advertising_instance1_param,
+	.expect_alt_ev_len = sizeof(advertising_instance1_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_test2,
+	.expect_hci_len = sizeof(set_adv_data_test2),
+};
+
+static const struct generic_data multi_advertising_add_second = {
+	.send_opcode = MGMT_OP_ADD_ADVERTISING,
+	.send_param = add_advertising_param_test2,
+	.send_len = sizeof(add_advertising_param_test2),
+	.expect_param = advertising_instance2_param,
+	.expect_len = sizeof(advertising_instance2_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+	.expect_alt_ev_param = advertising_instance2_param,
+	.expect_alt_ev_len = sizeof(advertising_instance2_param),
+	.expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+	.expect_hci_param = set_adv_data_test2,
+	.expect_hci_len = sizeof(set_adv_data_test2),
+};
+
+/* based on G-Tag ADV_DATA */
+static const uint8_t adv_data_invalid_significant_len[] = { 0x02, 0x01, 0x06,
+		0x0d, 0xff, 0x80, 0x01, 0x02, 0x15, 0x12, 0x34, 0x80, 0x91,
+		0xd0, 0xf2, 0xbb, 0xc5, 0x03, 0x02, 0x0f, 0x18, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char device_found_valid[] = { 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00,
+		0x01, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x02, 0x01,
+		0x06, 0x0d, 0xff, 0x80, 0x01, 0x02, 0x15, 0x12, 0x34, 0x80,
+		0x91, 0xd0, 0xf2, 0xbb, 0xc5, 0x03, 0x02, 0x0f, 0x18 };
+
+static const struct generic_data device_found_gtag = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_le_param,
+	.send_len = sizeof(start_discovery_le_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_discovery_le_param,
+	.expect_len = sizeof(start_discovery_le_param),
+	.expect_alt_ev = MGMT_EV_DEVICE_FOUND,
+	.expect_alt_ev_param = device_found_valid,
+	.expect_alt_ev_len = sizeof(device_found_valid),
+	.set_adv = true,
+	.adv_data_len = sizeof(adv_data_invalid_significant_len),
+	.adv_data = adv_data_invalid_significant_len,
+};
+
+static const uint8_t adv_data_invalid_field_len[] = { 0x02, 0x01, 0x01,
+		0x05, 0x09, 0x74, 0x65, 0x73, 0x74,
+		0xa0, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+static const char device_found_valid2[] = { 0x00, 0x00, 0x01, 0x01, 0xaa, 0x00,
+		0x01, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x02, 0x01,
+		0x01, 0x05, 0x09, 0x74, 0x65, 0x73, 0x74};
+
+static const struct generic_data device_found_invalid_field = {
+	.setup_settings = settings_powered_le,
+	.send_opcode = MGMT_OP_START_DISCOVERY,
+	.send_param = start_discovery_le_param,
+	.send_len = sizeof(start_discovery_le_param),
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_param = start_discovery_le_param,
+	.expect_len = sizeof(start_discovery_le_param),
+	.expect_alt_ev = MGMT_EV_DEVICE_FOUND,
+	.expect_alt_ev_param = device_found_valid2,
+	.expect_alt_ev_len = sizeof(device_found_valid2),
+	.set_adv = true,
+	.adv_data_len = sizeof(adv_data_invalid_field_len),
+	.adv_data = adv_data_invalid_field_len,
+};
+
+static const struct generic_data read_local_oob_not_powered_test = {
+	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+	.expect_status = MGMT_STATUS_NOT_POWERED,
+};
+
+static const struct generic_data read_local_oob_invalid_param_test = {
+	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+	.send_param = dummy_data,
+	.send_len = sizeof(dummy_data),
+	.expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_local_oob_invalid_index_test = {
+	.send_index_none = true,
+	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+	.expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_local_oob_legacy_pairing_test = {
+	.setup_settings = settings_powered,
+	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+	.expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data read_local_oob_success_ssp_test = {
+	.setup_settings = settings_powered_ssp,
+	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_ignore_param = true,
+	.expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_DATA,
+};
+
+static const struct generic_data read_local_oob_success_sc_test = {
+	.setup_settings = settings_powered_sc,
+	.send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+	.expect_status = MGMT_STATUS_SUCCESS,
+	.expect_ignore_param = true,
+	.expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA,
+};
+
+static void client_cmd_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+	case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+		tester_print("Client set connectable: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		if (!status && test->client_enable_ssp) {
+			bthost_write_ssp_mode(bthost, 0x01);
+			return;
+		}
+		break;
+	case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+		tester_print("Client enable SSP: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		break;
+	default:
+		return;
+	}
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_bthost(void)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_cmd_complete, data);
+	if (data->hciemu_type == HCIEMU_TYPE_LE)
+		bthost_set_adv_enable(bthost, 0x01);
+	else
+		bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void setup_pairing_acceptor(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+
+	if (!test->io_cap)
+		return;
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_IO_CAPABILITY, data->mgmt_index,
+					sizeof(test->io_cap), &test->io_cap,
+					NULL, NULL, NULL);
+
+	setup_bthost();
+}
+
+static void setup_powered_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	setup_bthost();
+}
+
+static void setup_class(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+	unsigned char class_param[] = { 0x01, 0x0c };
+
+	tester_print("Setting device class and powering on");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_DEV_CLASS, data->mgmt_index,
+				sizeof(class_param), class_param,
+				NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void discovering_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_ev_discovering *ev = param;
+
+	mgmt_unregister(data->mgmt, data->mgmt_discov_ev_id);
+
+	if (length != sizeof(*ev)) {
+		tester_warn("Incorrect discovering event length");
+		tester_setup_failed();
+		return;
+	}
+
+	if (!ev->discovering) {
+		tester_warn("Unexpected discovery stopped event");
+		tester_setup_failed();
+		return;
+	}
+
+	tester_setup_complete();
+}
+
+static void setup_discovery_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Discovery started");
+}
+
+static void setup_start_discovery(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const void *send_param = test->setup_send_param;
+	uint16_t send_len = test->setup_send_len;
+	unsigned int id;
+
+	id = mgmt_register(data->mgmt, MGMT_EV_DISCOVERING, data->mgmt_index,
+			   discovering_event, NULL, NULL);
+	data->mgmt_discov_ev_id = id;
+
+	mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index,
+				send_len, send_param, setup_discovery_callback,
+				NULL, NULL);
+}
+
+static void setup_multi_uuid32(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller (with 32-bit UUID)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_uuid32_param_1), add_uuid32_param_1,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_uuid32_param_2), add_uuid32_param_2,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_uuid32_param_3), add_uuid32_param_3,
+				NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_multi_uuid32_2(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+	unsigned char uuid_param[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00 };
+	int i;
+
+	tester_print("Powering on controller (with many 32-bit UUIDs)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	for (i = 0; i < 58; i++) {
+		uint32_t val = htobl(0xffffffff - i);
+		memcpy(&uuid_param[12], &val, sizeof(val));
+		mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(uuid_param), uuid_param,
+				NULL, NULL, NULL);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_multi_uuid128(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller (with 128-bit UUID)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+			sizeof(add_uuid128_param_1), add_uuid128_param_1,
+			NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_multi_uuid128_2(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+	unsigned char uuid_param[] = {
+			0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+			0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
+			0x00 };
+	int i;
+
+	tester_print("Powering on controller (with many 128-bit UUIDs)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	for (i = 0; i < 13; i++) {
+		uuid_param[15] = i;
+		mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(uuid_param), uuid_param,
+				NULL, NULL, NULL);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_multi_uuid16(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller (with SPP UUID)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_spp_uuid_param), add_spp_uuid_param,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_dun_uuid_param), add_dun_uuid_param,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+			sizeof(add_sync_uuid_param), add_sync_uuid_param,
+			NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_multi_uuid16_2(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+	unsigned char uuid_param[] = {
+			0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+			0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00 };
+	int i;
+
+	tester_print("Powering on controller (with many 16-bit UUIDs)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	for (i = 0; i < 117; i++) {
+		uint16_t val = htobs(i + 0x2000);
+		memcpy(&uuid_param[12], &val, sizeof(val));
+		mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(uuid_param), uuid_param,
+				NULL, NULL, NULL);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_uuid_mix(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller (with mixed UUIDs)");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_spp_uuid_param), add_spp_uuid_param,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_uuid32_param_1), add_uuid32_param_1,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+			sizeof(add_uuid128_param_1), add_uuid128_param_1,
+			NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_dun_uuid_param), add_dun_uuid_param,
+				NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_ADD_UUID, data->mgmt_index,
+				sizeof(add_uuid32_param_2), add_uuid32_param_2,
+				NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_add_device(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+	const unsigned char *add_param;
+	size_t add_param_len;
+
+	tester_print("Powering on controller (with added device)");
+
+	if (data->hciemu_type == HCIEMU_TYPE_LE) {
+		add_param = add_device_success_param_2;
+		add_param_len = sizeof(add_device_success_param_2);
+	} else {
+		add_param = add_device_success_param_1;
+		add_param_len = sizeof(add_device_success_param_1);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_DEVICE, data->mgmt_index,
+			add_param_len, add_param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void setup_add_advertising_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct mgmt_rp_add_advertising *rp =
+				(struct mgmt_rp_add_advertising *) param;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Add Advertising setup complete (instance %d)",
+								rp->instance);
+
+	setup_bthost();
+}
+
+#define TESTER_ADD_ADV_DATA_LEN 7
+
+static void setup_add_adv_param(struct mgmt_cp_add_advertising *cp,
+							uint8_t instance)
+{
+	memset(cp, 0, sizeof(*cp));
+	cp->instance = instance;
+	cp->adv_data_len = TESTER_ADD_ADV_DATA_LEN;
+	cp->data[0] = TESTER_ADD_ADV_DATA_LEN - 1; /* AD len */
+	cp->data[1] = 0x08; /* AD type: shortened local name */
+	cp->data[2] = 't';  /* adv data ... */
+	cp->data[3] = 'e';
+	cp->data[4] = 's';
+	cp->data[5] = 't';
+	cp->data[6] = '0' + instance;
+}
+
+static void setup_add_advertising_not_powered(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Adding advertising instance while unpowered");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_add_advertising(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Adding advertising instance while powered");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_add_advertising_connectable(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Adding advertising instance while connectable");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_add_advertising_timeout(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Adding advertising instance with timeout");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+	cp->timeout = 1;
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_add_advertising_duration(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Adding instance with long timeout/short duration");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+	cp->duration = 1;
+	cp->timeout = 30;
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_power_cycle_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param_off[] = { 0x00 };
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param_off), &param_off,
+						NULL, NULL, NULL);
+
+	setup_bthost();
+}
+
+static void setup_add_advertising_power_cycle(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param_on[] = { 0x01 };
+
+	tester_print("Adding instance without timeout and power cycle");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param_on), &param_on,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param_on), &param_on,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_power_cycle_callback,
+						NULL, NULL);
+}
+
+static void setup_set_and_add_advertising(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Set and add advertising instance");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_multi_adv_second_instance(uint8_t status, uint16_t length,
+		const void *param, void *user_data) {
+	struct mgmt_rp_add_advertising *rp =
+				(struct mgmt_rp_add_advertising *) param;
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Add Advertising setup complete (instance %d)",
+								rp->instance);
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 2);
+	cp->timeout = 1;
+	cp->duration = 1;
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_add_advertising_callback,
+						NULL, NULL);
+}
+
+static void setup_multi_adv(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_add_advertising *cp;
+	unsigned char adv_param[sizeof(*cp) + TESTER_ADD_ADV_DATA_LEN];
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Adding two instances with timeout 1 and duration 1");
+
+	cp = (struct mgmt_cp_add_advertising *) adv_param;
+	setup_add_adv_param(cp, 1);
+	cp->timeout = 1;
+	cp->duration = 1;
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+						sizeof(param), &param,
+						NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+						sizeof(adv_param), adv_param,
+						setup_multi_adv_second_instance,
+						NULL, NULL);
+}
+
+static void setup_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Initial settings completed");
+
+	if (data->test_setup)
+		data->test_setup(data);
+	else
+		setup_bthost();
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct test_data *data = user_data;
+	const struct generic_data *test = data->test_data;
+	struct mgmt_cp_pin_code_reply cp;
+
+	test_condition_complete(data);
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+	if (!test->pin) {
+		mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+				data->mgmt_index, sizeof(cp.addr), &cp.addr,
+				NULL, NULL, NULL);
+		return;
+	}
+
+	cp.pin_len = test->pin_len;
+	memcpy(cp.pin_code, test->pin, test->pin_len);
+
+	mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_REPLY, data->mgmt_index,
+			sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_confirm_request *ev = param;
+	struct test_data *data = user_data;
+	const struct generic_data *test = data->test_data;
+	struct mgmt_cp_user_confirm_reply cp;
+	uint16_t opcode;
+
+	if (test->just_works) {
+		tester_warn("User Confirmation received for just-works case");
+		tester_test_failed();
+		return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+	if (test->reject_confirm)
+		opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+	else
+		opcode = MGMT_OP_USER_CONFIRM_REPLY;
+
+	mgmt_reply(data->mgmt, opcode, data->mgmt_index, sizeof(cp), &cp,
+							NULL, NULL, NULL);
+}
+
+static void user_passkey_request_callback(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_passkey_request *ev = param;
+	struct test_data *data = user_data;
+	const struct generic_data *test = data->test_data;
+	struct mgmt_cp_user_passkey_reply cp;
+
+	if (test->just_works) {
+		tester_warn("User Passkey Request for just-works case");
+		tester_test_failed();
+		return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+	if (test->reject_confirm) {
+		mgmt_reply(data->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+				data->mgmt_index, sizeof(cp.addr), &cp.addr,
+				NULL, NULL, NULL);
+		return;
+	}
+
+	mgmt_reply(data->mgmt, MGMT_OP_USER_PASSKEY_REPLY, data->mgmt_index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void test_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const uint16_t *cmd;
+
+	if (!test)
+		goto proceed;
+
+	if (test->pin || test->expect_pin) {
+		mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST,
+				data->mgmt_index, pin_code_request_callback,
+				data, NULL);
+		test_add_condition(data);
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST,
+			data->mgmt_index, user_confirm_request_callback,
+			data, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_USER_PASSKEY_REQUEST,
+			data->mgmt_index, user_passkey_request_callback,
+			data, NULL);
+
+	if (test->client_pin)
+		bthost_set_pin_code(bthost, test->client_pin,
+							test->client_pin_len);
+
+	if (test->client_io_cap)
+		bthost_set_io_capability(bthost, test->client_io_cap);
+
+	if (test->client_auth_req)
+		bthost_set_auth_req(bthost, test->client_auth_req);
+	else if (!test->just_works)
+		bthost_set_auth_req(bthost, 0x01);
+
+	if (test->client_reject_confirm)
+		bthost_set_reject_user_confirm(bthost, true);
+
+	if (test->client_enable_le)
+		bthost_write_le_host_supported(bthost, 0x01);
+
+	if (test->client_enable_sc)
+		bthost_set_sc_support(bthost, 0x01);
+
+proceed:
+	if (!test || !test->setup_settings) {
+		if (data->test_setup)
+			data->test_setup(data);
+		else
+			tester_setup_complete();
+		return;
+	}
+
+	for (cmd = test->setup_settings; *cmd; cmd++) {
+		unsigned char simple_param[] = { 0x01 };
+		unsigned char discov_param[] = { 0x01, 0x00, 0x00 };
+		unsigned char privacy_param[] = { 0x01,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+		unsigned char *param = simple_param;
+		size_t param_size = sizeof(simple_param);
+		mgmt_request_func_t func = NULL;
+
+		/* If this is the last command (next one is 0) request
+		 * for a callback. */
+		if (!cmd[1])
+			func = setup_complete;
+
+		if (*cmd == MGMT_OP_SET_DISCOVERABLE) {
+			if (test->setup_limited_discov) {
+				discov_param[0] = 0x02;
+				discov_param[1] = 0x01;
+			}
+			param_size = sizeof(discov_param);
+			param = discov_param;
+		}
+
+		if (*cmd == MGMT_OP_SET_PRIVACY) {
+			param_size = sizeof(privacy_param);
+			param = privacy_param;
+		}
+
+		if (*cmd == MGMT_OP_SET_LE && test->setup_nobredr) {
+			unsigned char off[] = { 0x00 };
+			mgmt_send(data->mgmt, *cmd, data->mgmt_index,
+					param_size, param, NULL, NULL, NULL);
+			mgmt_send(data->mgmt, MGMT_OP_SET_BREDR,
+					data->mgmt_index, sizeof(off), off,
+					func, data, NULL);
+		} else {
+			mgmt_send(data->mgmt, *cmd, data->mgmt_index,
+					param_size, param, func, data, NULL);
+		}
+	}
+}
+
+static void command_generic_new_settings(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("New settings event received");
+
+	mgmt_unregister(data->mgmt, data->mgmt_settings_id);
+
+	tester_test_failed();
+}
+
+static void command_generic_new_settings_alt(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	uint32_t settings;
+
+	if (length != 4) {
+		tester_warn("Invalid parameter size for new settings event");
+		tester_test_failed();
+		return;
+	}
+
+	settings = get_le32(param);
+
+	tester_print("New settings 0x%08x received", settings);
+
+	if (test->expect_settings_unset) {
+		if ((settings & test->expect_settings_unset) != 0)
+			return;
+		goto done;
+	}
+
+	if (!test->expect_settings_set)
+		return;
+
+	if ((settings & test->expect_settings_set) != test->expect_settings_set)
+		return;
+
+done:
+	tester_print("Unregistering new settings notification");
+
+	mgmt_unregister(data->mgmt_alt, data->mgmt_alt_settings_id);
+
+	test_condition_complete(data);
+}
+
+static bool verify_alt_ev(const void *param, uint16_t length)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+
+	if (length != test->expect_alt_ev_len) {
+		tester_warn("Invalid length %u != %u", length,
+						test->expect_alt_ev_len);
+		return false;
+	}
+
+	if (test->expect_alt_ev_param &&
+			memcmp(test->expect_alt_ev_param, param, length)) {
+		tester_warn("Event parameters do not match");
+		return false;
+	}
+
+	return true;
+}
+
+static void command_generic_event_alt(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	bool (*verify)(const void *param, uint16_t length);
+
+	tester_print("New %s event received", mgmt_evstr(test->expect_alt_ev));
+
+	mgmt_unregister(data->mgmt_alt, data->mgmt_alt_ev_id);
+
+	if (test->verify_alt_ev_func)
+		verify = test->verify_alt_ev_func;
+	else
+		verify = verify_alt_ev;
+
+	if (!verify(param, length)) {
+		tester_warn("Incorrect %s event parameters",
+					mgmt_evstr(test->expect_alt_ev));
+		tester_test_failed();
+		return;
+	}
+
+	test_condition_complete(data);
+}
+
+static void command_generic_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const void *expect_param = test->expect_param;
+	uint16_t expect_len = test->expect_len;
+
+	tester_print("%s (0x%04x): %s (0x%02x)", mgmt_opstr(test->send_opcode),
+			test->send_opcode, mgmt_errstr(status), status);
+
+	if (status != test->expect_status) {
+		tester_test_failed();
+		return;
+	}
+
+	if (!test->expect_ignore_param) {
+		if (test->expect_func)
+			expect_param = test->expect_func(&expect_len);
+
+		if (length != expect_len) {
+			tester_warn("Invalid cmd response parameter size");
+			tester_test_failed();
+			return;
+		}
+
+		if (expect_param && expect_len > 0 &&
+					memcmp(param, expect_param, length)) {
+			tester_warn("Unexpected cmd response parameter value");
+			tester_test_failed();
+			return;
+		}
+	}
+
+	test_condition_complete(data);
+}
+
+static void command_hci_callback(uint16_t opcode, const void *param,
+					uint8_t length, void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct generic_data *test = data->test_data;
+	const void *expect_hci_param = test->expect_hci_param;
+	uint8_t expect_hci_len = test->expect_hci_len;
+
+	tester_print("HCI Command 0x%04x length %u", opcode, length);
+
+	if (opcode != test->expect_hci_command)
+		return;
+
+	if (test->expect_hci_func)
+		expect_hci_param = test->expect_hci_func(&expect_hci_len);
+
+	if (length != expect_hci_len) {
+		tester_warn("Invalid parameter size for HCI command");
+		tester_test_failed();
+		return;
+	}
+
+	if (memcmp(param, expect_hci_param, length) != 0) {
+		tester_warn("Unexpected HCI command parameter value");
+		tester_test_failed();
+		return;
+	}
+
+	test_condition_complete(data);
+}
+
+static bool power_off(uint16_t index)
+{
+	int sk, err;
+
+	sk = hci_open_dev(index);
+	if (sk < 0)
+		return false;
+
+	err = ioctl(sk, HCIDEVDOWN, index);
+
+	hci_close_dev(sk);
+
+	if (err < 0)
+		return false;
+
+	return true;
+}
+
+static void test_command_generic(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const void *send_param = test->send_param;
+	uint16_t send_len = test->send_len;
+	unsigned int id;
+	uint16_t index;
+
+	index = test->send_index_none ? MGMT_INDEX_NONE : data->mgmt_index;
+
+	if (test->expect_settings_set || test->expect_settings_unset) {
+		tester_print("Registering new settings notification");
+
+		id = mgmt_register(data->mgmt, MGMT_EV_NEW_SETTINGS, index,
+				command_generic_new_settings, NULL, NULL);
+		data->mgmt_settings_id = id;
+
+		id = mgmt_register(data->mgmt_alt, MGMT_EV_NEW_SETTINGS, index,
+				command_generic_new_settings_alt, NULL, NULL);
+		data->mgmt_alt_settings_id = id;
+		test_add_condition(data);
+	}
+
+	if (test->expect_alt_ev) {
+		tester_print("Registering %s notification",
+					mgmt_evstr(test->expect_alt_ev));
+		id = mgmt_register(data->mgmt_alt, test->expect_alt_ev, index,
+					command_generic_event_alt, NULL, NULL);
+		data->mgmt_alt_ev_id = id;
+		test_add_condition(data);
+	}
+
+	if (test->expect_hci_command) {
+		tester_print("Registering HCI command callback");
+		hciemu_add_master_post_command_hook(data->hciemu,
+						command_hci_callback, data);
+		test_add_condition(data);
+	}
+
+	if (test->send_opcode == 0x0000) {
+		tester_print("Executing no-op test");
+		return;
+	}
+
+	tester_print("Sending %s (0x%04x)", mgmt_opstr(test->send_opcode),
+							test->send_opcode);
+
+	if (test->send_func)
+		send_param = test->send_func(&send_len);
+
+	if (test->force_power_off) {
+		mgmt_send_nowait(data->mgmt, test->send_opcode, index,
+					send_len, send_param,
+					command_generic_callback, NULL, NULL);
+		power_off(data->mgmt_index);
+	} else {
+		mgmt_send(data->mgmt, test->send_opcode, index, send_len,
+					send_param, command_generic_callback,
+					NULL, NULL);
+	}
+
+	test_add_condition(data);
+}
+
+static void check_scan(void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (hciemu_get_master_le_scan_enable(data->hciemu)) {
+		tester_warn("LE scan still enabled");
+		tester_test_failed();
+		return;
+	}
+
+	if (hciemu_get_master_scan_enable(data->hciemu)) {
+		tester_warn("BR/EDR scan still enabled");
+		tester_test_failed();
+		return;
+	}
+
+	test_condition_complete(data);
+}
+
+static void test_remove_device(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	test_command_generic(test_data);
+	tester_wait(1, check_scan, NULL);
+	test_add_condition(data);
+}
+
+static void test_device_found(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	if ((data->hciemu_type == HCIEMU_TYPE_LE) ||
+				(data->hciemu_type == HCIEMU_TYPE_BREDRLE)) {
+		if (test->set_adv)
+			bthost_set_adv_data(bthost, test->adv_data,
+							test->adv_data_len);
+
+		bthost_set_adv_enable(bthost, 0x01);
+	}
+
+	if (data->hciemu_type != HCIEMU_TYPE_LE)
+		bthost_write_scan_enable(bthost, 0x03);
+
+	test_command_generic(test_data);
+}
+
+static void pairing_new_conn(uint16_t handle, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	tester_print("New connection with handle 0x%04x", handle);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+
+	bthost_request_auth(bthost, handle);
+}
+
+static void test_pairing_acceptor(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const uint8_t *master_bdaddr;
+	struct bthost *bthost;
+	uint8_t addr_type;
+
+	if (test->expect_alt_ev) {
+		unsigned int id;
+
+		tester_print("Registering %s notification",
+					mgmt_evstr(test->expect_alt_ev));
+		id = mgmt_register(data->mgmt_alt, test->expect_alt_ev,
+					data->mgmt_index,
+					command_generic_event_alt, NULL, NULL);
+		data->mgmt_alt_ev_id = id;
+		test_add_condition(data);
+	}
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_connect_cb(bthost, pairing_new_conn, data);
+
+	if (data->hciemu_type == HCIEMU_TYPE_BREDRLE)
+		addr_type = BDADDR_BREDR;
+	else
+		addr_type = BDADDR_LE_PUBLIC;
+
+	bthost_hci_connect(bthost, master_bdaddr, addr_type);
+}
+
+static void connected_event(uint16_t index, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct generic_data *test = data->test_data;
+	const void *send_param = test->send_param;
+	uint16_t send_len = test->send_len;
+
+	tester_print("Sending %s 0x%04x", mgmt_opstr(test->send_opcode),
+							test->send_opcode);
+
+	if (test->send_func)
+		send_param = test->send_func(&send_len);
+
+	if (test->force_power_off) {
+		mgmt_send_nowait(data->mgmt, test->send_opcode, index,
+					send_len, send_param,
+					command_generic_callback, NULL, NULL);
+		power_off(data->mgmt_index);
+	} else {
+		mgmt_send(data->mgmt, test->send_opcode, index, send_len,
+					send_param, command_generic_callback,
+					NULL, NULL);
+	}
+
+	test_add_condition(data);
+
+	/* Complete MGMT_EV_DEVICE_CONNECTED *after* adding new one */
+	test_condition_complete(data);
+}
+
+static void test_command_generic_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned int id;
+	const uint8_t *master_bdaddr;
+	uint8_t addr_type;
+	struct bthost *bthost;
+
+	tester_print("Registering %s notification",
+					mgmt_evstr(MGMT_EV_DEVICE_CONNECTED));
+	id = mgmt_register(data->mgmt_alt, MGMT_EV_DEVICE_CONNECTED,
+				data->mgmt_index, connected_event,
+				NULL, NULL);
+	data->mgmt_alt_ev_id = id;
+	test_add_condition(data);
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	addr_type = data->hciemu_type == HCIEMU_TYPE_BREDRLE ? BDADDR_BREDR :
+							BDADDR_LE_PUBLIC;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_hci_connect(bthost, master_bdaddr, addr_type);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_bredrle("Controller setup",
+				NULL, NULL, controller_setup);
+	test_bredr("Controller setup (BR/EDR-only)",
+				NULL, NULL, controller_setup);
+	test_le("Controller setup (LE)",
+				NULL, NULL, controller_setup);
+
+	test_bredrle("Invalid command",
+				&invalid_command_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Read version - Success",
+				&read_version_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Read version - Invalid parameters",
+				&read_version_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read version - Invalid index",
+				&read_version_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read commands - Invalid parameters",
+				&read_commands_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read commands - Invalid index",
+				&read_commands_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read index list - Invalid parameters",
+				&read_index_list_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read index list - Invalid index",
+				&read_index_list_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read info - Invalid parameters",
+				&read_info_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read info - Invalid index",
+				&read_info_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read unconfigured index list - Invalid parameters",
+				&read_unconf_index_list_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read unconfigured index list - Invalid index",
+				&read_unconf_index_list_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read configuration info - Invalid parameters",
+				&read_config_info_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read configuration info - Invalid index",
+				&read_config_info_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read extended index list - Invalid parameters",
+				&read_ext_index_list_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read extended index list - Invalid index",
+				&read_ext_index_list_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set powered on - Success",
+				&set_powered_on_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set powered on - Invalid parameters 1",
+				&set_powered_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set powered on - Invalid parameters 2",
+				&set_powered_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set powered on - Invalid parameters 3",
+				&set_powered_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set powered on - Invalid index",
+				&set_powered_on_invalid_index_test,
+				NULL, test_command_generic);
+	test_le("Set powered on - Privacy and Advertising",
+				&set_powered_on_privacy_adv_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set powered off - Success",
+				&set_powered_off_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set powered off - Class of Device",
+				&set_powered_off_class_test,
+				setup_class, test_command_generic);
+	test_bredrle("Set powered off - Invalid parameters 1",
+				&set_powered_off_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set powered off - Invalid parameters 2",
+				&set_powered_off_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set powered off - Invalid parameters 3",
+				&set_powered_off_invalid_param_test_3,
+				NULL, test_command_generic);
+
+	test_bredrle("Set connectable on - Success 1",
+				&set_connectable_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable on - Success 2",
+				&set_connectable_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable on - Invalid parameters 1",
+				&set_connectable_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable on - Invalid parameters 2",
+				&set_connectable_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable on - Invalid parameters 3",
+				&set_connectable_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable on - Invalid index",
+				&set_connectable_on_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_le("Set connectable on (LE) - Success 1",
+				&set_connectable_on_le_test_1,
+				NULL, test_command_generic);
+	test_le("Set connectable on (LE) - Success 2",
+				&set_connectable_on_le_test_2,
+				NULL, test_command_generic);
+	test_le("Set connectable on (LE) - Success 3",
+				&set_connectable_on_le_test_3,
+				NULL, test_command_generic);
+
+	test_bredrle("Set connectable off - Success 1",
+				&set_connectable_off_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable off - Success 2",
+				&set_connectable_off_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable off - Success 3",
+				&set_connectable_off_success_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set connectable off - Success 4",
+				&set_connectable_off_success_test_4,
+				setup_add_device, test_command_generic);
+
+	test_le("Set connectable off (LE) - Success 1",
+				&set_connectable_off_le_test_1,
+				NULL, test_command_generic);
+	test_le("Set connectable off (LE) - Success 2",
+				&set_connectable_off_le_test_2,
+				NULL, test_command_generic);
+	test_le("Set connectable off (LE) - Success 3",
+				&set_connectable_off_le_test_3,
+				NULL, test_command_generic);
+	test_le("Set connectable off (LE) - Success 4",
+				&set_connectable_off_le_test_4,
+				NULL, test_command_generic);
+
+	test_bredrle("Set fast connectable on - Success 1",
+				&set_fast_conn_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set fast connectable on - Success 2",
+				&set_fast_conn_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set fast connectable on - Success 3",
+				&set_fast_conn_on_success_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set fast connectable on - Invalid Params 1",
+				&set_fast_conn_nval_param_test_1,
+				NULL, test_command_generic);
+	test_le("Set fast connectable on - Not Supported 1",
+				&set_fast_conn_on_not_supported_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Set bondable on - Success",
+				&set_bondable_on_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set bondable on - Invalid parameters 1",
+				&set_bondable_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set bondable on - Invalid parameters 2",
+				&set_bondable_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set bondable on - Invalid parameters 3",
+				&set_bondable_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set bondable on - Invalid index",
+				&set_bondable_on_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set discoverable on - Invalid parameters 1",
+				&set_discoverable_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Invalid parameters 2",
+				&set_discoverable_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Invalid parameters 3",
+				&set_discoverable_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Invalid parameters 4",
+				&set_discoverable_on_invalid_param_test_4,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Not powered 1",
+				&set_discoverable_on_not_powered_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Not powered 2",
+				&set_discoverable_on_not_powered_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Rejected 1",
+				&set_discoverable_on_rejected_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Rejected 2",
+				&set_discoverable_on_rejected_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Rejected 3",
+				&set_discoverable_on_rejected_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Success 1",
+				&set_discoverable_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable on - Success 2",
+				&set_discoverable_on_success_test_2,
+				NULL, test_command_generic);
+	test_le("Set discoverable on (LE) - Success 1",
+				&set_discov_on_le_success_1,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable off - Success 1",
+				&set_discoverable_off_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set discoverable off - Success 2",
+				&set_discoverable_off_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set limited discoverable on - Success 1",
+				&set_limited_discov_on_success_1,
+				NULL, test_command_generic);
+	test_bredrle("Set limited discoverable on - Success 2",
+				&set_limited_discov_on_success_2,
+				NULL, test_command_generic);
+	test_bredrle("Set limited discoverable on - Success 3",
+				&set_limited_discov_on_success_3,
+				NULL, test_command_generic);
+	test_le("Set limited discoverable on (LE) - Success 1",
+				&set_limited_discov_on_le_success_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Set link security on - Success 1",
+				&set_link_sec_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set link security on - Success 2",
+				&set_link_sec_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set link security on - Success 3",
+				&set_link_sec_on_success_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set link security on - Invalid parameters 1",
+				&set_link_sec_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set link security on - Invalid parameters 2",
+				&set_link_sec_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set link security on - Invalid parameters 3",
+				&set_link_sec_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set link security on - Invalid index",
+				&set_link_sec_on_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set link security off - Success 1",
+				&set_link_sec_off_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set link security off - Success 2",
+				&set_link_sec_off_success_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Set SSP on - Success 1",
+				&set_ssp_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set SSP on - Success 2",
+				&set_ssp_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set SSP on - Success 3",
+				&set_ssp_on_success_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set SSP on - Invalid parameters 1",
+				&set_ssp_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set SSP on - Invalid parameters 2",
+				&set_ssp_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set SSP on - Invalid parameters 3",
+				&set_ssp_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set SSP on - Invalid index",
+				&set_ssp_on_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Secure Connections on - Success 1",
+				&set_sc_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Secure Connections on - Success 2",
+				&set_sc_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Secure Connections on - Invalid params 1",
+				&set_sc_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Secure Connections on - Invalid params 2",
+				&set_sc_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Secure Connections on - Invalid params 3",
+				&set_sc_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set Secure Connections on - Invalid index",
+				&set_sc_on_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredr("Set Secure Connections on - Not supported 1",
+				&set_sc_on_not_supported_test_1,
+				NULL, test_command_generic);
+	test_bredr("Set Secure Connections on - Not supported 2",
+				&set_sc_on_not_supported_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Secure Connections Only on - Success 1",
+				&set_sc_only_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Secure Connections Only on - Success 2",
+				&set_sc_only_on_success_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Set High Speed on - Success",
+				&set_hs_on_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set High Speed on - Invalid parameters 1",
+				&set_hs_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set High Speed on - Invalid parameters 2",
+				&set_hs_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set High Speed on - Invalid parameters 3",
+				&set_hs_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set High Speed on - Invalid index",
+				&set_hs_on_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Low Energy on - Success 1",
+				&set_le_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Low Energy on - Success 2",
+				&set_le_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Low Energy on - Success 3",
+				&set_le_on_success_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set Low Energy on - Invalid parameters 1",
+				&set_le_on_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Low Energy on - Invalid parameters 2",
+				&set_le_on_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Low Energy on - Invalid parameters 3",
+				&set_le_on_invalid_param_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Set Low Energy on - Invalid index",
+				&set_le_on_invalid_index_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Advertising on - Success 1",
+				&set_adv_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Advertising on - Success 2",
+				&set_adv_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Advertising on - Rejected 1",
+				&set_adv_on_rejected_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Set BR/EDR off - Success 1",
+				&set_bredr_off_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set BR/EDR on - Success 1",
+				&set_bredr_on_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set BR/EDR on - Success 2",
+				&set_bredr_on_success_test_2,
+				NULL, test_command_generic);
+	test_bredr("Set BR/EDR off - Not Supported 1",
+				&set_bredr_off_notsupp_test,
+				NULL, test_command_generic);
+	test_le("Set BR/EDR off - Not Supported 2",
+				&set_bredr_off_notsupp_test,
+				NULL, test_command_generic);
+	test_bredrle("Set BR/EDR off - Rejected 1",
+				&set_bredr_off_failure_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set BR/EDR off - Rejected 2",
+				&set_bredr_off_failure_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set BR/EDR off - Invalid Parameters 1",
+				&set_bredr_off_failure_test_3,
+				NULL, test_command_generic);
+
+	test_bredr("Set Local Name - Success 1",
+				&set_local_name_test_1,
+				NULL, test_command_generic);
+	test_bredr("Set Local Name - Success 2",
+				&set_local_name_test_2,
+				NULL, test_command_generic);
+	test_bredr("Set Local Name - Success 3",
+				&set_local_name_test_3,
+				NULL, test_command_generic);
+
+	test_bredrle("Start Discovery - Not powered 1",
+				&start_discovery_not_powered_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Start Discovery - Invalid parameters 1",
+				&start_discovery_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Start Discovery - Not supported 1",
+				&start_discovery_not_supported_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Start Discovery - Success 1",
+				&start_discovery_valid_param_test_1,
+				NULL, test_command_generic);
+	test_le("Start Discovery - Success 2",
+				&start_discovery_valid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Start Discovery - Power Off 1",
+				&start_discovery_valid_param_power_off_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Stop Discovery - Success 1",
+				&stop_discovery_success_test_1,
+				setup_start_discovery, test_command_generic);
+	test_bredr("Stop Discovery - BR/EDR (Inquiry) Success 1",
+				&stop_discovery_bredr_success_test_1,
+				setup_start_discovery, test_command_generic);
+	test_bredrle("Stop Discovery - Rejected 1",
+				&stop_discovery_rejected_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Stop Discovery - Invalid parameters 1",
+				&stop_discovery_invalid_param_test_1,
+				setup_start_discovery, test_command_generic);
+
+	test_bredrle("Start Service Discovery - Not powered 1",
+				&start_service_discovery_not_powered_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Start Service Discovery - Invalid parameters 1",
+				&start_service_discovery_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Start Service Discovery - Not supported 1",
+				&start_service_discovery_not_supported_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Start Service Discovery - Success 1",
+				&start_service_discovery_valid_param_test_1,
+				NULL, test_command_generic);
+	test_le("Start Service Discovery - Success 2",
+				&start_service_discovery_valid_param_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Device Class - Success 1",
+				&set_dev_class_valid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Set Device Class - Success 2",
+				&set_dev_class_valid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Device Class - Invalid parameters 1",
+				&set_dev_class_invalid_param_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Add UUID - UUID-16 1",
+				&add_uuid16_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Add UUID - UUID-16 multiple 1",
+				&add_multi_uuid16_test_1,
+				setup_multi_uuid16, test_command_generic);
+	test_bredrle("Add UUID - UUID-16 partial 1",
+				&add_multi_uuid16_test_2,
+				setup_multi_uuid16_2, test_command_generic);
+	test_bredrle("Add UUID - UUID-32 1",
+				&add_uuid32_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Add UUID - UUID-32 multiple 1",
+				&add_uuid32_multi_test_1,
+				setup_multi_uuid32, test_command_generic);
+	test_bredrle("Add UUID - UUID-32 partial 1",
+				&add_uuid32_multi_test_2,
+				setup_multi_uuid32_2, test_command_generic);
+	test_bredrle("Add UUID - UUID-128 1",
+				&add_uuid128_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Add UUID - UUID-128 multiple 1",
+				&add_uuid128_multi_test_1,
+				setup_multi_uuid128, test_command_generic);
+	test_bredrle("Add UUID - UUID-128 partial 1",
+				&add_uuid128_multi_test_2,
+				setup_multi_uuid128_2, test_command_generic);
+	test_bredrle("Add UUID - UUID mix",
+				&add_uuid_mix_test_1,
+				setup_uuid_mix, test_command_generic);
+
+	test_bredrle("Load Link Keys - Empty List Success 1",
+				&load_link_keys_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Load Link Keys - Empty List Success 2",
+				&load_link_keys_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Load Link Keys - Invalid Parameters 1",
+				&load_link_keys_invalid_params_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Load Link Keys - Invalid Parameters 2",
+				&load_link_keys_invalid_params_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Load Link Keys - Invalid Parameters 3",
+				&load_link_keys_invalid_params_test_3,
+				NULL, test_command_generic);
+
+	test_bredrle("Load Long Term Keys - Success 1",
+				&load_ltks_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Load Long Term Keys - Invalid Parameters 1",
+				&load_ltks_invalid_params_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Load Long Term Keys - Invalid Parameters 2",
+				&load_ltks_invalid_params_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Load Long Term Keys - Invalid Parameters 3",
+				&load_ltks_invalid_params_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Load Long Term Keys - Invalid Parameters 4",
+				&load_ltks_invalid_params_test_4,
+				NULL, test_command_generic);
+
+	test_bredrle("Set IO Capability - Invalid Params 1",
+				&set_io_cap_invalid_param_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Pair Device - Not Powered 1",
+				&pair_device_not_powered_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Power off 1",
+				&pair_device_power_off_test_1,
+				NULL, test_command_generic);
+	test_le("Pair Device - Incorrect transport reject 1",
+				&pair_device_not_supported_test_1,
+				NULL, test_command_generic);
+	test_bredr("Pair Device - Incorrect transport reject 2",
+				&pair_device_not_supported_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Reject on not enabled transport 1",
+				&pair_device_reject_transport_not_enabled_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Reject on not enabled transport 2",
+				&pair_device_reject_transport_not_enabled_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Invalid Parameters 1",
+				&pair_device_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Invalid Parameters 2",
+				&pair_device_invalid_param_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Legacy Success 1",
+				&pair_device_success_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Legacy Non-bondable 1",
+				&pair_device_legacy_nonbondable_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Sec Mode 3 Success 1",
+				&pair_device_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Legacy Reject 1",
+				&pair_device_reject_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Legacy Reject 2",
+				&pair_device_reject_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Sec Mode 3 Reject 1",
+				&pair_device_reject_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - Sec Mode 3 Reject 2",
+				&pair_device_reject_test_4,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Just-Works Success 1",
+				&pair_device_ssp_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Just-Works Success 2",
+				&pair_device_ssp_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Just-Works Success 3",
+				&pair_device_ssp_test_3,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Confirm Success 1",
+				&pair_device_ssp_test_4,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Confirm Success 2",
+				&pair_device_ssp_test_5,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Confirm Success 3",
+				&pair_device_ssp_test_6,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Confirm Reject 1",
+				&pair_device_ssp_reject_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Confirm Reject 2",
+				&pair_device_ssp_reject_2,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SSP Non-bondable 1",
+				&pair_device_ssp_nonbondable_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SMP over BR/EDR Success 1",
+				&pair_device_smp_bredr_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Pair Device - SMP over BR/EDR Success 2",
+				&pair_device_smp_bredr_test_2,
+				NULL, test_command_generic);
+	test_le("Pair Device - LE Success 1",
+				&pair_device_le_success_test_1,
+				NULL, test_command_generic);
+	test_le("Pair Device - LE Success 2",
+				&pair_device_le_success_test_2,
+				NULL, test_command_generic);
+	test_le("Pair Device - LE Reject 1",
+				&pair_device_le_reject_test_1,
+				NULL, test_command_generic);
+	test_le("Pair Device - LE SC Legacy 1",
+				&pair_device_le_sc_legacy_test_1,
+				NULL, test_command_generic);
+	test_le("Pair Device - LE SC Success 1",
+				&pair_device_le_sc_success_test_1,
+				NULL, test_command_generic);
+	test_le("Pair Device - LE SC Success 2",
+				&pair_device_le_sc_success_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Pairing Acceptor - Legacy 1",
+				&pairing_acceptor_legacy_1, NULL,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - Legacy 2",
+				&pairing_acceptor_legacy_2, NULL,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - Legacy 3",
+				&pairing_acceptor_legacy_3, NULL,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - Link Sec 1",
+				&pairing_acceptor_linksec_1, NULL,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - Link Sec 2",
+				&pairing_acceptor_linksec_2, NULL,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SSP 1",
+				&pairing_acceptor_ssp_1, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SSP 2",
+				&pairing_acceptor_ssp_2, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SSP 3",
+				&pairing_acceptor_ssp_3, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SSP 4",
+				&pairing_acceptor_ssp_4, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SMP over BR/EDR 1",
+				&pairing_acceptor_smp_bredr_1,
+				setup_pairing_acceptor, test_pairing_acceptor);
+	test_bredrle("Pairing Acceptor - SMP over BR/EDR 2",
+				&pairing_acceptor_smp_bredr_2,
+				setup_pairing_acceptor, test_pairing_acceptor);
+	test_le("Pairing Acceptor - LE 1",
+				&pairing_acceptor_le_1, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_le("Pairing Acceptor - LE 2",
+				&pairing_acceptor_le_2, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_le("Pairing Acceptor - LE 3",
+				&pairing_acceptor_le_3, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_le("Pairing Acceptor - LE 4",
+				&pairing_acceptor_le_4, setup_pairing_acceptor,
+				test_pairing_acceptor);
+	test_le("Pairing Acceptor - LE 5",
+				&pairing_acceptor_le_5, setup_pairing_acceptor,
+				test_pairing_acceptor);
+
+	test_bredrle("Unpair Device - Not Powered 1",
+				&unpair_device_not_powered_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Unpair Device - Invalid Parameters 1",
+				&unpair_device_invalid_param_test_1,
+				NULL, test_command_generic);
+	test_bredrle("Unpair Device - Invalid Parameters 2",
+				&unpair_device_invalid_param_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Disconnect - Invalid Parameters 1",
+				&disconnect_invalid_param_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Block Device - Invalid Parameters 1",
+				&block_device_invalid_param_test_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Unblock Device - Invalid Parameters 1",
+				&unblock_device_invalid_param_test_1,
+				NULL, test_command_generic);
+
+	test_le("Set Static Address - Success 1",
+				&set_static_addr_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set Static Address - Success 2",
+				&set_static_addr_success_test_2,
+				NULL, test_command_generic);
+	test_bredrle("Set Static Address - Failure 1",
+				&set_static_addr_failure_test,
+				NULL, test_command_generic);
+	test_bredr("Set Static Address - Failure 2",
+				&set_static_addr_failure_test_2,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Scan Parameters - Success",
+				&set_scan_params_success_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Load IRKs - Success 1",
+				&load_irks_success1_test,
+				NULL, test_command_generic);
+	test_bredrle("Load IRKs - Success 2",
+				&load_irks_success2_test,
+				NULL, test_command_generic);
+	test_bredrle("Load IRKs - Invalid Parameters 1",
+				&load_irks_nval_param1_test,
+				NULL, test_command_generic);
+	test_bredrle("Load IRKs - Invalid Parameters 2",
+				&load_irks_nval_param2_test,
+				NULL, test_command_generic);
+	test_bredrle("Load IRKs - Invalid Parameters 3",
+				&load_irks_nval_param3_test,
+				NULL, test_command_generic);
+	test_bredr("Load IRKs - Not Supported",
+				&load_irks_not_supported_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Set Privacy - Success",
+				&set_privacy_success_test,
+				NULL, test_command_generic);
+	test_bredrle("Set Privacy - Rejected",
+				&set_privacy_powered_test,
+				NULL, test_command_generic);
+	test_bredrle("Set Privacy - Invalid Parameters",
+				&set_privacy_nval_param_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Get Conn Info - Success",
+				&get_conn_info_succes1_test, NULL,
+				test_command_generic_connect);
+	test_bredrle("Get Conn Info - Not Connected",
+				&get_conn_info_ncon_test, NULL,
+				test_command_generic);
+	test_bredrle("Get Conn Info - Power off",
+				&get_conn_info_power_off_test, NULL,
+				test_command_generic_connect);
+
+	test_bredrle("Load Connection Parameters - Invalid Params 1",
+				&load_conn_params_fail_1,
+				NULL, test_command_generic);
+
+	test_bredrle("Add Device - Invalid Params 1",
+				&add_device_fail_1,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Invalid Params 2",
+				&add_device_fail_2,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Invalid Params 3",
+				&add_device_fail_3,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Invalid Params 4",
+				&add_device_fail_4,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Success 1",
+				&add_device_success_1,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Success 2",
+				&add_device_success_2,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Success 3",
+				&add_device_success_3,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Success 4",
+				&add_device_success_4,
+				NULL, test_command_generic);
+	test_bredrle("Add Device - Success 5",
+				&add_device_success_5,
+				NULL, test_command_generic);
+
+	test_bredrle("Remove Device - Invalid Params 1",
+				&remove_device_fail_1,
+				NULL, test_command_generic);
+	test_bredrle("Remove Device - Invalid Params 2",
+				&remove_device_fail_2,
+				NULL, test_command_generic);
+	test_bredrle("Remove Device - Invalid Params 3",
+				&remove_device_fail_3,
+				NULL, test_command_generic);
+	test_bredrle("Remove Device - Success 1",
+				&remove_device_success_1,
+				setup_add_device, test_command_generic);
+	test_bredrle("Remove Device - Success 2",
+				&remove_device_success_2,
+				setup_add_device, test_command_generic);
+	test_bredrle("Remove Device - Success 3",
+				&remove_device_success_3,
+				setup_add_device, test_remove_device);
+	test_le("Remove Device - Success 4",
+				&remove_device_success_4,
+				setup_add_device, test_remove_device);
+	test_le("Remove Device - Success 5",
+				&remove_device_success_5,
+				setup_add_device, test_remove_device);
+
+	test_bredrle("Read Advertising Features - Invalid parameters",
+				&read_adv_features_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read Advertising Features - Invalid index",
+				&read_adv_features_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredrle("Read Advertising Features - Success 1 (No instance)",
+				&read_adv_features_success_1,
+				NULL, test_command_generic);
+	test_bredrle("Read Advertising Features - Success 2 (One instance)",
+				&read_adv_features_success_2,
+				setup_add_advertising,
+				test_command_generic);
+
+	test_bredrle("Add Advertising - Failure: LE off",
+					&add_advertising_fail_1,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 1 (AD too long)",
+					&add_advertising_fail_2,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 2 (Malformed len)",
+					&add_advertising_fail_3,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 3 (Malformed len)",
+					&add_advertising_fail_4,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 4 (Malformed len)",
+					&add_advertising_fail_5,
+					NULL, test_command_generic);
+	test_le("Add Advertising - Invalid Params 5 (AD too long)",
+					&add_advertising_fail_6,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 6 (ScRsp too long)",
+					&add_advertising_fail_7,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 7 (Malformed len)",
+					&add_advertising_fail_8,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 8 (Malformed len)",
+					&add_advertising_fail_9,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Invalid Params 9 (Malformed len)",
+					&add_advertising_fail_10,
+					NULL, test_command_generic);
+	test_le("Add Advertising - Invalid Params 10 (ScRsp too long)",
+					&add_advertising_fail_11,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Rejected (Timeout, !Powered)",
+					&add_advertising_fail_12,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 1 (Powered, Add Adv Inst)",
+					&add_advertising_success_1,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 2 (!Powered, Add Adv Inst)",
+					&add_advertising_success_pwron_data,
+					setup_add_advertising_not_powered,
+					test_command_generic);
+	test_bredrle("Add Advertising - Success 3 (!Powered, Adv Enable)",
+					&add_advertising_success_pwron_enabled,
+					setup_add_advertising_not_powered,
+					test_command_generic);
+	test_bredrle("Add Advertising - Success 4 (Set Adv on override)",
+					&add_advertising_success_4,
+					setup_add_advertising,
+					test_command_generic);
+	test_bredrle("Add Advertising - Success 5 (Set Adv off override)",
+					&add_advertising_success_5,
+					setup_set_and_add_advertising,
+					test_command_generic);
+	test_bredrle("Add Advertising - Success 6 (Scan Rsp Dta, Adv ok)",
+					&add_advertising_success_6,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 7 (Scan Rsp Dta, Scan ok) ",
+					&add_advertising_success_7,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 8 (Connectable Flag)",
+					&add_advertising_success_8,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 9 (General Discov Flag)",
+					&add_advertising_success_9,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 10 (Limited Discov Flag)",
+					&add_advertising_success_10,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 11 (Managed Flags)",
+					&add_advertising_success_11,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 12 (TX Power Flag)",
+					&add_advertising_success_12,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 13 (ADV_SCAN_IND)",
+					&add_advertising_success_13,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 14 (ADV_NONCONN_IND)",
+					&add_advertising_success_14,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 15 (ADV_IND)",
+					&add_advertising_success_15,
+					NULL, test_command_generic);
+	test_bredrle("Add Advertising - Success 16 (Connectable -> on)",
+					&add_advertising_success_16,
+					setup_add_advertising,
+					test_command_generic);
+	test_bredrle("Add Advertising - Success 17 (Connectable -> off)",
+					&add_advertising_success_17,
+					setup_add_advertising_connectable,
+					test_command_generic);
+	/* Adv instances with a timeout do NOT survive a power cycle. */
+	test_bredrle("Add Advertising - Success 18 (Power -> off, Remove)",
+					&add_advertising_power_off,
+					setup_add_advertising_timeout,
+					test_command_generic);
+	/* Adv instances without timeout survive a power cycle. */
+	test_bredrle("Add Advertising - Success 19 (Power -> off, Keep)",
+					&add_advertising_success_pwron_data,
+					setup_add_advertising_power_cycle,
+					test_command_generic);
+	/* Changing an advertising instance while it is still being
+	 * advertised will immediately update the advertised data if
+	 * there is no other instance to switch to.
+	 */
+	test_bredrle("Add Advertising - Success 20 (Add Adv override)",
+					&add_advertising_success_18,
+					setup_add_advertising,
+					test_command_generic);
+	/* An instance should be removed when its timeout has been reached.
+	 * Advertising will also be disabled if this was the last instance.
+	 */
+	test_bredrle_full("Add Advertising - Success 21 (Timeout expires)",
+					&add_advertising_timeout_expired,
+					setup_add_advertising_timeout,
+					test_command_generic, 3);
+	/* LE off will clear (remove) all instances. */
+	test_bredrle("Add Advertising - Success 22 (LE -> off, Remove)",
+					&add_advertising_le_off,
+					setup_add_advertising,
+					test_command_generic);
+
+
+	test_bredrle("Remove Advertising - Invalid Params 1",
+					&remove_advertising_fail_1,
+					NULL, test_command_generic);
+	test_bredrle("Remove Advertising - Success 1",
+						&remove_advertising_success_1,
+						setup_add_advertising,
+						test_command_generic);
+	test_bredrle("Remove Advertising - Success 2",
+						&remove_advertising_success_2,
+						setup_add_advertising,
+						test_command_generic);
+
+	/* When advertising two instances, the instances should be
+	 * advertised in a round-robin fashion.
+	 */
+	test_bredrle("Multi Advertising - Success 1 (Instance Switch)",
+					&multi_advertising_switch,
+					setup_multi_adv,
+					test_command_generic);
+	/* Adding a new instance when one is already being advertised
+	 * will switch to the new instance after the first has reached
+	 * its duration. A long timeout has been set to
+	 */
+	test_bredrle_full("Multi Advertising - Success 2 (Add Second Inst)",
+					&multi_advertising_add_second,
+					setup_add_advertising_duration,
+					test_command_generic, 3);
+
+	test_bredrle("Read Local OOB Data - Not powered",
+				&read_local_oob_not_powered_test,
+				NULL, test_command_generic);
+	test_bredrle("Read Local OOB Data - Invalid parameters",
+				&read_local_oob_invalid_param_test,
+				NULL, test_command_generic);
+	test_bredrle("Read Local OOB Data - Invalid index",
+				&read_local_oob_invalid_index_test,
+				NULL, test_command_generic);
+	test_bredr20("Read Local OOB Data - Legacy pairing",
+				&read_local_oob_legacy_pairing_test,
+				NULL, test_command_generic);
+	test_bredrle("Read Local OOB Data - Success SSP",
+				&read_local_oob_success_ssp_test,
+				NULL, test_command_generic);
+	test_bredrle("Read Local OOB Data - Success SC",
+				&read_local_oob_success_sc_test,
+				NULL, test_command_generic);
+
+	test_bredrle("Device Found - Advertising data - Zero padded",
+				&device_found_gtag,
+				NULL, test_device_found);
+	test_bredrle("Device Found - Advertising data - Invalid field",
+				&device_found_invalid_field,
+				NULL, test_device_found);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/mpris-proxy.c b/bluez/repo/tools/mpris-proxy.c
new file mode 100644
index 0000000..bf8148f
--- /dev/null
+++ b/bluez/repo/tools/mpris-proxy.c
@@ -0,0 +1,2594 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#define BLUEZ_BUS_NAME "org.bluez"
+#define BLUEZ_PATH "/org/bluez"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
+#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
+#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
+#define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
+#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
+#define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
+#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
+#define MPRIS_TRACKLIST_INTERFACE "org.mpris.MediaPlayer2.TrackList"
+#define MPRIS_PLAYLISTS_INTERFACE "org.mpris.MediaPlayer2.Playlists"
+#define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
+#define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
+
+static GMainLoop *main_loop;
+static GDBusProxy *adapter = NULL;
+static DBusConnection *sys = NULL;
+static DBusConnection *session = NULL;
+static GDBusClient *client = NULL;
+static GSList *players = NULL;
+static GSList *transports = NULL;
+
+static gboolean option_version = FALSE;
+static gboolean option_export = FALSE;
+
+struct tracklist {
+	GDBusProxy *proxy;
+	GSList *items;
+};
+
+struct player {
+	char *bus_name;
+	DBusConnection *conn;
+	GDBusProxy *proxy;
+	GDBusProxy *folder;
+	GDBusProxy *device;
+	GDBusProxy *transport;
+	GDBusProxy *playlist;
+	struct tracklist *tracklist;
+};
+
+typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata);
+
+static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
+								void *val);
+
+static void sig_term(int sig)
+{
+	g_main_loop_quit(main_loop);
+}
+
+static DBusMessage *get_all(DBusConnection *conn, const char *name)
+{
+	DBusMessage *msg, *reply;
+	DBusError err;
+	const char *iface = MPRIS_PLAYER_INTERFACE;
+
+	msg = dbus_message_new_method_call(name, MPRIS_PLAYER_PATH,
+					DBUS_INTERFACE_PROPERTIES, "GetAll");
+	if (!msg) {
+		fprintf(stderr, "Can't allocate new method call\n");
+		return NULL;
+	}
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface,
+					DBUS_TYPE_INVALID);
+
+	dbus_error_init(&err);
+
+	reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+	dbus_message_unref(msg);
+
+	if (!reply) {
+		if (dbus_error_is_set(&err)) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		}
+		return NULL;
+	}
+
+	return reply;
+}
+
+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_array(DBusMessageIter *dict, const char *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, DBUS_TYPE_STRING, &key);
+
+	append_array_variant(&entry, type, val, n_elements);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_basic(DBusMessageIter *base, DBusMessageIter *iter,
+								int type)
+{
+	const void *value;
+
+	dbus_message_iter_get_basic(iter, &value);
+	dbus_message_iter_append_basic(base, type, &value);
+}
+
+static void append_iter(DBusMessageIter *base, DBusMessageIter *iter);
+static void append_container(DBusMessageIter *base, DBusMessageIter *iter,
+								int type)
+{
+	DBusMessageIter iter_sub, base_sub;
+	char *sig;
+
+	dbus_message_iter_recurse(iter, &iter_sub);
+
+	switch (type) {
+	case DBUS_TYPE_ARRAY:
+	case DBUS_TYPE_VARIANT:
+		sig = dbus_message_iter_get_signature(&iter_sub);
+		break;
+	default:
+		sig = NULL;
+		break;
+	}
+
+	dbus_message_iter_open_container(base, type, sig, &base_sub);
+
+	if (sig != NULL)
+		dbus_free(sig);
+
+	append_iter(&base_sub, &iter_sub);
+
+	dbus_message_iter_close_container(base, &base_sub);
+}
+
+static void append_iter(DBusMessageIter *base, DBusMessageIter *iter)
+{
+	int type;
+
+	while ((type = dbus_message_iter_get_arg_type(iter)) !=
+							DBUS_TYPE_INVALID) {
+		if (dbus_type_is_basic(type))
+			append_basic(base, iter, type);
+		else if (dbus_type_is_container(type))
+			append_container(base, iter, type);
+
+		dbus_message_iter_next(iter);
+	}
+}
+
+static void dict_append_iter(DBusMessageIter *dict, const char *key,
+						DBusMessageIter *iter)
+{
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+						NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	append_iter(&entry, iter);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
+						DBusMessageIter *metadata)
+{
+	if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+		return -EINVAL;
+
+	dict_append_iter(metadata, key, entry);
+
+	return 0;
+}
+
+static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata,
+						parse_metadata_func func)
+{
+	DBusMessageIter dict;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(args);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(args, &dict);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		DBusMessageIter entry;
+		const char *key;
+
+		if (ctype != DBUS_TYPE_DICT_ENTRY)
+			return -EINVAL;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			return -EINVAL;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		if (func(&entry, key, metadata) < 0)
+			return -EINVAL;
+
+		dbus_message_iter_next(&dict);
+	}
+
+	return 0;
+}
+
+static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict,
+						parse_metadata_func func)
+{
+	DBusMessageIter value, metadata;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
+								&value);
+
+	dbus_message_iter_open_container(&value, 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, &metadata);
+
+	parse_metadata(dict, &metadata, func);
+
+	dbus_message_iter_close_container(&value, &metadata);
+	dbus_message_iter_close_container(iter, &value);
+}
+
+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);
+
+	if (strcasecmp(key, "Metadata") == 0)
+		append_metadata(&entry, val, parse_metadata_entry);
+	else
+		append_variant(&entry, type, val);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static char *sender2path(const char *sender)
+{
+	char *path;
+
+	path = g_strconcat("/", sender, NULL);
+	return g_strdelimit(path, ":.", '_');
+}
+
+static void copy_reply(DBusPendingCall *call, void *user_data)
+{
+	DBusMessage *msg = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusMessage *copy;
+	DBusMessageIter args, iter;
+
+	copy = dbus_message_new_method_return(msg);
+	if (copy == NULL) {
+		dbus_message_unref(reply);
+		return;
+	}
+
+	dbus_message_iter_init_append(copy, &iter);
+
+	if (!dbus_message_iter_init(reply, &args))
+		goto done;
+
+	append_iter(&iter, &args);
+
+	dbus_connection_send(sys, copy, NULL);
+
+done:
+	dbus_message_unref(copy);
+	dbus_message_unref(reply);
+}
+
+static DBusHandlerResult player_message(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	char *owner = data;
+	DBusMessage *copy;
+	DBusMessageIter args, iter;
+	DBusPendingCall *call;
+
+	dbus_message_iter_init(msg, &args);
+
+	copy = dbus_message_new_method_call(owner,
+					MPRIS_PLAYER_PATH,
+					dbus_message_get_interface(msg),
+					dbus_message_get_member(msg));
+	if (copy == NULL)
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_iter_init_append(copy, &iter);
+	append_iter(&iter, &args);
+
+	if (!dbus_connection_send_with_reply(session, copy, &call, -1))
+		goto done;
+
+	dbus_message_ref(msg);
+	dbus_pending_call_set_notify(call, copy_reply, msg, NULL);
+	dbus_pending_call_unref(call);
+
+done:
+	dbus_message_unref(copy);
+
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static struct player *find_player_by_bus_name(const char *name)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+
+		if (strcmp(player->bus_name, name) == 0)
+			return player;
+	}
+
+	return NULL;
+}
+
+static const DBusObjectPathVTable player_table = {
+	.message_function = player_message,
+};
+
+static void add_player(DBusConnection *conn, const char *name,
+							const char *sender)
+{
+	DBusMessage *reply = NULL;
+	DBusMessage *msg;
+	DBusMessageIter iter, args;
+	DBusError err;
+	char *path, *owner;
+	struct player *player;
+
+	if (!adapter)
+		return;
+
+	player = find_player_by_bus_name(name);
+	if (player == NULL) {
+		reply = get_all(conn, name);
+		if (reply == NULL)
+			return;
+		dbus_message_iter_init(reply, &args);
+	}
+
+	msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
+					g_dbus_proxy_get_path(adapter),
+					BLUEZ_MEDIA_INTERFACE,
+					"RegisterPlayer");
+	if (!msg) {
+		fprintf(stderr, "Can't allocate new method call\n");
+		return;
+	}
+
+	path = sender2path(sender);
+	dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+	if (owner != NULL)
+		goto done;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+	if (player != NULL) {
+		if (!g_dbus_get_properties(player->conn,
+						MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						&iter))
+			goto done;
+	} else {
+		append_iter(&iter, &args);
+		dbus_message_unref(reply);
+	}
+
+	dbus_error_init(&err);
+
+	owner = strdup(sender);
+
+	if (!dbus_connection_register_object_path(sys, path, &player_table,
+								owner)) {
+		fprintf(stderr, "Can't register object path for player\n");
+		free(owner);
+		goto done;
+	}
+
+	reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err);
+	if (!reply) {
+		fprintf(stderr, "Can't register player\n");
+		free(owner);
+		if (dbus_error_is_set(&err)) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		}
+	}
+
+done:
+	if (reply)
+		dbus_message_unref(reply);
+	dbus_message_unref(msg);
+	g_free(path);
+}
+
+static void remove_player(DBusConnection *conn, const char *sender)
+{
+	DBusMessage *msg;
+	char *path, *owner;
+
+	if (!adapter)
+		return;
+
+	path = sender2path(sender);
+	dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+	if (owner == NULL) {
+		g_free(path);
+		return;
+	}
+
+	msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
+					g_dbus_proxy_get_path(adapter),
+					BLUEZ_MEDIA_INTERFACE,
+					"UnregisterPlayer");
+	if (!msg) {
+		fprintf(stderr, "Can't allocate new method call\n");
+		g_free(path);
+		return;
+	}
+
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID);
+
+	dbus_connection_send(sys, msg, NULL);
+
+	dbus_connection_unregister_object_path(sys, path);
+
+	dbus_message_unref(msg);
+	g_free(path);
+	g_free(owner);
+}
+
+static gboolean player_signal(DBusConnection *conn, DBusMessage *msg,
+								void *user_data)
+{
+	DBusMessage *signal;
+	DBusMessageIter iter, args;
+	char *path, *owner;
+
+	dbus_message_iter_init(msg, &iter);
+
+	path = sender2path(dbus_message_get_sender(msg));
+	dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+	if (owner == NULL)
+		goto done;
+
+	signal = dbus_message_new_signal(path, dbus_message_get_interface(msg),
+						dbus_message_get_member(msg));
+	if (signal == NULL) {
+		fprintf(stderr, "Unable to allocate new %s.%s signal",
+						dbus_message_get_interface(msg),
+						dbus_message_get_member(msg));
+		goto done;
+	}
+
+	dbus_message_iter_init_append(signal, &args);
+
+	append_iter(&args, &iter);
+
+	dbus_connection_send(sys, signal, NULL);
+	dbus_message_unref(signal);
+
+done:
+	g_free(path);
+
+	return TRUE;
+}
+
+static gboolean name_owner_changed(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	const char *name, *old, *new;
+
+	if (!dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_STRING, &name,
+					DBUS_TYPE_STRING, &old,
+					DBUS_TYPE_STRING, &new,
+					DBUS_TYPE_INVALID)) {
+		fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+
+	if (!g_str_has_prefix(name, "org.mpris"))
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (*new == '\0') {
+		printf("player %s at %s disappear\n", name, old);
+		remove_player(conn, old);
+	} else if (option_export || find_player_by_bus_name(name) == NULL) {
+		printf("player %s at %s found\n", name, new);
+		add_player(conn, name, new);
+	}
+
+	return TRUE;
+}
+
+static char *get_name_owner(DBusConnection *conn, const char *name)
+{
+	DBusMessage *msg, *reply;
+	DBusError err;
+	char *owner;
+
+	msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+					DBUS_INTERFACE_DBUS, "GetNameOwner");
+
+	if (!msg) {
+		fprintf(stderr, "Can't allocate new method call\n");
+		return NULL;
+	}
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
+							DBUS_TYPE_INVALID);
+
+	dbus_error_init(&err);
+
+	reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+	dbus_message_unref(msg);
+
+	if (!reply) {
+		if (dbus_error_is_set(&err)) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		}
+		return NULL;
+	}
+
+	if (!dbus_message_get_args(reply, NULL,
+					DBUS_TYPE_STRING, &owner,
+					DBUS_TYPE_INVALID)) {
+		dbus_message_unref(reply);
+		return NULL;
+	}
+
+	owner = g_strdup(owner);
+
+	dbus_message_unref(reply);
+
+	dbus_connection_flush(conn);
+
+	return owner;
+}
+
+static void parse_list_names(DBusConnection *conn, DBusMessageIter *args)
+{
+	DBusMessageIter array;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(args);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(args, &array);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&array)) !=
+							DBUS_TYPE_INVALID) {
+		const char *name;
+		char *owner;
+
+		if (ctype != DBUS_TYPE_STRING)
+			goto next;
+
+		dbus_message_iter_get_basic(&array, &name);
+
+		if (!g_str_has_prefix(name, "org.mpris"))
+			goto next;
+
+		owner = get_name_owner(conn, name);
+
+		if (owner == NULL)
+			goto next;
+
+		printf("player %s at %s found\n", name, owner);
+
+		add_player(conn, name, owner);
+
+		g_free(owner);
+next:
+		dbus_message_iter_next(&array);
+	}
+}
+
+static void list_names(DBusConnection *conn)
+{
+	DBusMessage *msg, *reply;
+	DBusMessageIter iter;
+	DBusError err;
+
+	msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+					DBUS_INTERFACE_DBUS, "ListNames");
+
+	if (!msg) {
+		fprintf(stderr, "Can't allocate new method call\n");
+		return;
+	}
+
+	dbus_error_init(&err);
+
+	reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+	dbus_message_unref(msg);
+
+	if (!reply) {
+		if (dbus_error_is_set(&err)) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		}
+		return;
+	}
+
+	dbus_message_iter_init(reply, &iter);
+
+	parse_list_names(conn, &iter);
+
+	dbus_message_unref(reply);
+
+	dbus_connection_flush(conn);
+}
+
+static void usage(void)
+{
+	printf("Bluetooth mpris-player ver %s\n\n", VERSION);
+
+	printf("Usage:\n");
+}
+
+static GOptionEntry options[] = {
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit" },
+	{ "export", 'e', 0, G_OPTION_ARG_NONE, &option_export,
+				"Export remote players" },
+	{ NULL },
+};
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+	printf("org.bluez appeared\n");
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	printf("org.bluez disappeared\n");
+}
+
+static void unregister_tracklist(struct player *player)
+{
+	struct tracklist *tracklist = player->tracklist;
+
+	g_slist_free(tracklist->items);
+	g_dbus_proxy_unref(tracklist->proxy);
+	g_free(tracklist);
+	player->tracklist = NULL;
+}
+
+static void player_free(void *data)
+{
+	struct player *player = data;
+
+	if (player->tracklist != NULL)
+		unregister_tracklist(player);
+
+	if (player->conn) {
+		dbus_connection_close(player->conn);
+		dbus_connection_unref(player->conn);
+	}
+
+	g_dbus_proxy_unref(player->device);
+	g_dbus_proxy_unref(player->proxy);
+
+	if (player->transport)
+		g_dbus_proxy_unref(player->transport);
+
+	if (player->playlist)
+		g_dbus_proxy_unref(player->playlist);
+
+	g_free(player->bus_name);
+	g_free(player);
+}
+
+struct pending_call {
+	struct player *player;
+	DBusMessage *msg;
+};
+
+static void pending_call_free(void *data)
+{
+	struct pending_call *p = data;
+
+	if (p->msg)
+		dbus_message_unref(p->msg);
+
+	g_free(p);
+}
+
+static void player_reply(DBusMessage *message, void *user_data)
+{
+	struct pending_call *p = user_data;
+	struct player *player = p->player;
+	DBusMessage *msg = p->msg;
+	DBusMessage *reply;
+	DBusError err;
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		fprintf(stderr, "error: %s", err.name);
+		reply = g_dbus_create_error(msg, err.name, "%s", err.message);
+		dbus_error_free(&err);
+	} else
+		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	g_dbus_send_message(player->conn, reply);
+}
+
+static void player_control(struct player *player, DBusMessage *msg,
+							const char *name)
+{
+	struct pending_call *p;
+
+	p = g_new0(struct pending_call, 1);
+	p->player = player;
+	p->msg = dbus_message_ref(msg);
+
+	g_dbus_proxy_method_call(player->proxy, name, NULL, player_reply,
+						p, pending_call_free);
+}
+
+static const char *status_to_playback(const char *status)
+{
+	if (strcasecmp(status, "playing") == 0)
+		return "Playing";
+	else if (strcasecmp(status, "paused") == 0)
+		return "Paused";
+	else
+		return "Stopped";
+}
+
+static const char *player_get_status(struct player *player)
+{
+	const char *status;
+	DBusMessageIter value;
+
+	if (g_dbus_proxy_get_property(player->proxy, "Status", &value)) {
+		dbus_message_iter_get_basic(&value, &status);
+		return status_to_playback(status);
+	}
+
+	if (player->transport == NULL)
+		goto done;
+
+	if (!g_dbus_proxy_get_property(player->transport, "State", &value))
+		goto done;
+
+	dbus_message_iter_get_basic(&value, &status);
+
+	if (strcasecmp(status, "active") == 0)
+		return "Playing";
+
+done:
+	return "Stopped";
+}
+
+static DBusMessage *player_toggle(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+	const char *status;
+
+	status = player_get_status(player);
+
+	if (strcasecmp(status, "Playing") == 0)
+		player_control(player, msg, "Pause");
+	else
+		player_control(player, msg, "Play");
+
+	return NULL;
+}
+
+static DBusMessage *player_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Play");
+
+	return NULL;
+}
+
+static DBusMessage *player_pause(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Pause");
+
+	return NULL;
+}
+
+static DBusMessage *player_stop(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Stop");
+
+	return NULL;
+}
+
+static DBusMessage *player_next(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Next");
+
+	return NULL;
+}
+
+static DBusMessage *player_previous(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Previous");
+
+	return NULL;
+}
+
+static gboolean status_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct player *player = data;
+
+	return player_get_status(player) != NULL;
+}
+
+static gboolean get_status(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	const char *status;
+
+	status = player_get_status(player);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+	return TRUE;
+}
+
+static gboolean repeat_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Repeat", &iter);
+}
+
+static const char *repeat_to_loopstatus(const char *value)
+{
+	if (strcasecmp(value, "off") == 0)
+		return "None";
+	else if (strcasecmp(value, "singletrack") == 0)
+		return "Track";
+	else if (strcasecmp(value, "alltracks") == 0)
+		return "Playlist";
+
+	return NULL;
+}
+
+static gboolean get_repeat(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value;
+	const char *status;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Repeat", &value))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&value, &status);
+
+	status = repeat_to_loopstatus(status);
+	if (status == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+	return TRUE;
+}
+
+static const char *loopstatus_to_repeat(const char *value)
+{
+	if (strcasecmp(value, "None") == 0)
+		return "off";
+	else if (strcasecmp(value, "Track") == 0)
+		return "singletrack";
+	else if (strcasecmp(value, "Playlist") == 0)
+		return "alltracks";
+
+	return NULL;
+}
+
+static void property_result(const DBusError *err, void *user_data)
+{
+	GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data);
+
+	if (!dbus_error_is_set(err))
+		return g_dbus_pending_property_success(id);
+
+	g_dbus_pending_property_error(id, err->name, err->message);
+}
+
+static void set_repeat(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct player *player = data;
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	value = loopstatus_to_repeat(value);
+	if (value == NULL) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	g_dbus_proxy_set_property_basic(player->proxy, "Repeat",
+					DBUS_TYPE_STRING, &value,
+					property_result, GUINT_TO_POINTER(id),
+					NULL);
+}
+
+static gboolean get_double(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	double value = 1.0;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
+
+	return TRUE;
+}
+
+static gboolean shuffle_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Shuffle", &iter);
+}
+
+static gboolean get_shuffle(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value;
+	const char *string;
+	dbus_bool_t shuffle;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Shuffle", &value))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&value, &string);
+
+	shuffle = strcmp(string, "off") != 0;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &shuffle);
+
+	return TRUE;
+}
+
+static void set_shuffle(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct player *player = data;
+	dbus_bool_t shuffle;
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &shuffle);
+	value = shuffle ? "alltracks" : "off";
+
+	g_dbus_proxy_set_property_basic(player->proxy, "Shuffle",
+					DBUS_TYPE_STRING, &value,
+					property_result, GUINT_TO_POINTER(id),
+					NULL);
+}
+
+static gboolean position_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Position", &iter);
+}
+
+static gboolean get_position(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter var;
+	uint32_t position;
+	int64_t value;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Position", &var))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&var, &position);
+
+	value = position * 1000ll;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &value);
+
+	return TRUE;
+}
+
+static gboolean track_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Track", &iter);
+}
+
+static gboolean parse_string_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	dict_append_entry(metadata, key, DBUS_TYPE_STRING, &value);
+
+	return TRUE;
+}
+
+static gboolean parse_array_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	char **value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	value = dbus_malloc0(sizeof(char *));
+
+	dbus_message_iter_get_basic(iter, &(value[0]));
+
+	dict_append_array(metadata, key, DBUS_TYPE_STRING, &value, 1);
+
+	dbus_free(value);
+
+	return TRUE;
+}
+
+static gboolean parse_int64_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	uint32_t duration;
+	int64_t value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &duration);
+
+	value = duration * 1000ll;
+
+	dict_append_entry(metadata, key, DBUS_TYPE_INT64, &value);
+
+	return TRUE;
+}
+
+static gboolean parse_int32_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	uint32_t value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	dict_append_entry(metadata, key, DBUS_TYPE_INT32, &value);
+
+	return TRUE;
+}
+
+static gboolean parse_path_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	dict_append_entry(metadata, key, DBUS_TYPE_OBJECT_PATH, &value);
+
+	return TRUE;
+}
+
+static int parse_track_entry(DBusMessageIter *entry, const char *key,
+						DBusMessageIter *metadata)
+{
+	DBusMessageIter var;
+
+	if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(entry, &var);
+
+	if (strcasecmp(key, "Title") == 0) {
+		if (!parse_string_metadata(&var, "xesam:title", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Artist") == 0) {
+		if (!parse_array_metadata(&var, "xesam:artist", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Album") == 0) {
+		if (!parse_string_metadata(&var, "xesam:album", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Genre") == 0) {
+		if (!parse_array_metadata(&var, "xesam:genre", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Duration") == 0) {
+		if (!parse_int64_metadata(&var, "mpris:length", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "TrackNumber") == 0) {
+		if (!parse_int32_metadata(&var, "xesam:trackNumber", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Item") == 0) {
+		if (!parse_path_metadata(&var, "mpris:trackid", metadata))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static gboolean get_track(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter var, metadata;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Track", &var))
+		return FALSE;
+
+	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, &metadata);
+
+	parse_metadata(&var, &metadata, parse_track_entry);
+
+	dbus_message_iter_close_container(iter, &metadata);
+
+	return TRUE;
+}
+
+static gboolean get_enable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = TRUE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+
+static gboolean get_volume(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	double value = 0.0;
+	uint16_t volume;
+	DBusMessageIter var;
+
+	if (player->transport == NULL)
+		goto done;
+
+	if (!g_dbus_proxy_get_property(player->transport, "Volume", &var))
+		goto done;
+
+	dbus_message_iter_get_basic(&var, &volume);
+
+	value = (double) volume / 127;
+
+done:
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable player_methods[] = {
+	{ GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
+	{ GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
+	{ GDBUS_ASYNC_METHOD("Pause", NULL, NULL, player_pause) },
+	{ GDBUS_ASYNC_METHOD("Stop", NULL, NULL, player_stop) },
+	{ GDBUS_ASYNC_METHOD("Next", NULL, NULL, player_next) },
+	{ GDBUS_ASYNC_METHOD("Previous", NULL, NULL, player_previous) },
+	{ }
+};
+
+static const GDBusSignalTable player_signals[] = {
+	{ GDBUS_SIGNAL("Seeked", GDBUS_ARGS({"Position", "x"})) },
+	{ }
+};
+
+static const GDBusPropertyTable player_properties[] = {
+	{ "PlaybackStatus", "s", get_status, NULL, status_exists },
+	{ "LoopStatus", "s", get_repeat, set_repeat, repeat_exists },
+	{ "Rate", "d", get_double, NULL, NULL },
+	{ "MinimumRate", "d", get_double, NULL, NULL },
+	{ "MaximumRate", "d", get_double, NULL, NULL },
+	{ "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
+	{ "Position", "x", get_position, NULL, position_exists },
+	{ "Metadata", "a{sv}", get_track, NULL, track_exists },
+	{ "Volume", "d", get_volume, NULL, NULL },
+	{ "CanGoNext", "b", get_enable, NULL, NULL },
+	{ "CanGoPrevious", "b", get_enable, NULL, NULL },
+	{ "CanPlay", "b", get_enable, NULL, NULL },
+	{ "CanPause", "b", get_enable, NULL, NULL },
+	{ "CanSeek", "b", get_enable, NULL, NULL },
+	{ "CanControl", "b", get_enable, NULL, NULL },
+	{ }
+};
+
+static gboolean get_disable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter var;
+	const char *alias;
+	char *name;
+
+	if (!g_dbus_proxy_get_property(player->device, "Alias", &var))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&var, &alias);
+
+	if (g_dbus_proxy_get_property(player->proxy, "Name", &var)) {
+		dbus_message_iter_get_basic(&var, &name);
+		name = g_strconcat(alias, " ", name, NULL);
+	} else
+		name = g_strdup(alias);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+
+	g_free(name);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable mpris_methods[] = {
+	{ }
+};
+
+static gboolean get_tracklist(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	dbus_bool_t value;
+
+	value = player->tracklist != NULL ? TRUE : FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable mpris_properties[] = {
+	{ "CanQuit", "b", get_disable, NULL, NULL },
+	{ "Fullscreen", "b", get_disable, NULL, NULL },
+	{ "CanSetFullscreen", "b", get_disable, NULL, NULL },
+	{ "CanRaise", "b", get_disable, NULL, NULL },
+	{ "HasTrackList", "b", get_tracklist, NULL, NULL },
+	{ "Identity", "s", get_name, NULL, NULL },
+	{ }
+};
+
+static GDBusProxy *find_item(struct player *player, const char *path)
+{
+	struct tracklist *tracklist = player->tracklist;
+	GSList *l;
+
+	for (l = tracklist->items; l; l = l->next) {
+		GDBusProxy *proxy = l->data;
+		const char *p = g_dbus_proxy_get_path(proxy);
+
+		if (g_str_equal(path, p))
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static void append_item_metadata(void *data, void *user_data)
+{
+	GDBusProxy *item = data;
+	DBusMessageIter *iter = user_data;
+	DBusMessageIter var, metadata;
+	const char *path = g_dbus_proxy_get_path(item);
+
+	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, &metadata);
+
+	dict_append_entry(&metadata, "mpris:trackid", DBUS_TYPE_OBJECT_PATH,
+									&path);
+
+	if (g_dbus_proxy_get_property(item, "Metadata", &var))
+		parse_metadata(&var, &metadata, parse_track_entry);
+
+	dbus_message_iter_close_container(iter, &metadata);
+
+	return;
+}
+
+static DBusMessage *tracklist_get_metadata(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct player *player = data;
+	DBusMessage *reply;
+	DBusMessageIter args, array;
+	GSList *l = NULL;
+
+	dbus_message_iter_init(msg, &args);
+
+	if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+	dbus_message_iter_recurse(&args, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) ==
+						DBUS_TYPE_OBJECT_PATH) {
+		const char *path;
+		GDBusProxy *item;
+
+		dbus_message_iter_get_basic(&array, &path);
+
+		item = find_item(player, path);
+		if (item == NULL)
+			return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+		l = g_slist_append(l, item);
+
+		dbus_message_iter_next(&array);
+	}
+
+	reply = dbus_message_new_method_return(msg);
+
+	dbus_message_iter_init_append(reply, &args);
+
+	dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_ARRAY_AS_STRING
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&array);
+
+	g_slist_foreach(l, append_item_metadata, &array);
+
+	dbus_message_iter_close_container(&args, &array);
+
+	return reply;
+}
+
+static void item_play_reply(DBusMessage *message, void *user_data)
+{
+	struct pending_call *p = user_data;
+	struct player *player = p->player;
+	DBusMessage *msg = p->msg;
+	DBusMessage *reply;
+	DBusError err;
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		fprintf(stderr, "error: %s", err.name);
+		reply = g_dbus_create_error(msg, err.name, "%s", err.message);
+		dbus_error_free(&err);
+	} else
+		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	g_dbus_send_message(player->conn, reply);
+}
+
+static void item_play(struct player *player, DBusMessage *msg,
+							GDBusProxy *item)
+{
+	struct pending_call *p;
+
+	p = g_new0(struct pending_call, 1);
+	p->player = player;
+	p->msg = dbus_message_ref(msg);
+
+	g_dbus_proxy_method_call(item, "Play", NULL, item_play_reply,
+						p, pending_call_free);
+}
+
+static DBusMessage *tracklist_goto(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct player *player = data;
+	GDBusProxy *item;
+	const char *path;
+
+	if (!dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments");
+
+	item = find_item(player, path);
+	if (item == NULL)
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments");
+
+	item_play(player, msg, item);
+
+	return NULL;
+}
+
+static DBusMessage *tracklist_add_track(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented",
+					"Not implemented");
+}
+
+static DBusMessage *tracklist_remove_track(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented",
+					"Not implemented");
+}
+
+static const GDBusMethodTable tracklist_methods[] = {
+	{ GDBUS_METHOD("GetTracksMetadata",
+			GDBUS_ARGS({ "tracks", "ao" }),
+			GDBUS_ARGS({ "metadata", "aa{sv}" }),
+			tracklist_get_metadata) },
+	{ GDBUS_METHOD("AddTrack",
+			GDBUS_ARGS({ "uri", "s" }, { "after", "o" },
+						{ "current", "b" }),
+			NULL,
+			tracklist_add_track) },
+	{ GDBUS_METHOD("RemoveTrack",
+			GDBUS_ARGS({ "track", "o" }), NULL,
+			tracklist_remove_track) },
+	{ GDBUS_ASYNC_METHOD("GoTo",
+			GDBUS_ARGS({ "track", "o" }), NULL,
+			tracklist_goto) },
+	{ },
+};
+
+static const GDBusSignalTable tracklist_signals[] = {
+	{ GDBUS_SIGNAL("TrackAdded", GDBUS_ARGS({"metadata", "a{sv}"},
+						{"after", "o"})) },
+	{ GDBUS_SIGNAL("TrackRemoved", GDBUS_ARGS({"track", "o"})) },
+	{ GDBUS_SIGNAL("TrackMetadataChanged", GDBUS_ARGS({"track", "o"},
+						{"metadata", "a{sv}"})) },
+	{ }
+};
+
+static gboolean tracklist_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct player *player = data;
+
+	return player->tracklist != NULL;
+}
+
+static void append_path(gpointer data, gpointer user_data)
+{
+	GDBusProxy *proxy = data;
+	DBusMessageIter *iter = user_data;
+	const char *path = g_dbus_proxy_get_path(proxy);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static gboolean get_tracks(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	struct tracklist *tracklist = player->tracklist;
+	DBusMessageIter value;
+
+	if (tracklist == NULL)
+		return FALSE;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_OBJECT_PATH_AS_STRING,
+					&value);
+	g_slist_foreach(player->tracklist->items, append_path, &value);
+	dbus_message_iter_close_container(iter, &value);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable tracklist_properties[] = {
+	{ "Tracks", "ao", get_tracks, NULL, tracklist_exists },
+	{ "CanEditTracks", "b", get_disable, NULL, NULL },
+	{ }
+};
+
+static void list_items_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void change_folder_reply(DBusMessage *message, void *user_data)
+{
+	struct player *player = user_data;
+	struct tracklist *tracklist = player->tracklist;
+	DBusError err;
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		fprintf(stderr, "error: %s", err.name);
+		return;
+	}
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYLISTS_INTERFACE,
+						"ActivePlaylist");
+
+	g_dbus_proxy_method_call(tracklist->proxy, "ListItems",
+					list_items_setup, NULL, NULL, NULL);
+}
+
+static void change_folder_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct player *player = user_data;
+	const char *path;
+
+	path = g_dbus_proxy_get_path(player->playlist);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static DBusMessage *playlist_activate(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct player *player = data;
+	struct tracklist *tracklist = player->tracklist;
+	const char *path;
+
+	if (player->playlist == NULL || tracklist == NULL)
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+	if (!dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+	if (!g_str_equal(path, g_dbus_proxy_get_path(player->playlist)))
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+	g_dbus_proxy_method_call(tracklist->proxy, "ChangeFolder",
+				change_folder_setup, change_folder_reply,
+				player, NULL);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *playlist_get(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+	uint32_t index, count;
+	const char *order;
+	dbus_bool_t reverse;
+	DBusMessage *reply;
+	DBusMessageIter iter, entry, value, name;
+	const char *string, *path;
+	const char *empty = "";
+
+	if (player->playlist == NULL)
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+	if (!dbus_message_get_args(msg, NULL,
+					DBUS_TYPE_UINT32, &index,
+					DBUS_TYPE_UINT32, &count,
+					DBUS_TYPE_STRING, &order,
+					DBUS_TYPE_BOOLEAN, &reverse,
+					DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid Arguments");
+
+	path = g_dbus_proxy_get_path(player->playlist);
+
+	reply = dbus_message_new_method_return(msg);
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(oss)",
+								&entry);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_STRUCT, NULL,
+								&value);
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_OBJECT_PATH, &path);
+	if (g_dbus_proxy_get_property(player->playlist, "Name", &name)) {
+		dbus_message_iter_get_basic(&name, &string);
+		dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
+								&string);
+	} else {
+		dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
+								&path);
+	}
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &empty);
+	dbus_message_iter_close_container(&entry, &value);
+	dbus_message_iter_close_container(&iter, &entry);
+
+	return reply;
+}
+
+static const GDBusMethodTable playlist_methods[] = {
+	{ GDBUS_METHOD("ActivatePlaylist",
+			GDBUS_ARGS({ "playlist", "o" }), NULL,
+			playlist_activate) },
+	{ GDBUS_METHOD("GetPlaylists",
+			GDBUS_ARGS({ "index", "u" }, { "maxcount", "u"},
+					{ "order", "s" }, { "reverse", "b" }),
+			GDBUS_ARGS({ "playlists", "a(oss)"}),
+			playlist_get) },
+	{ },
+};
+
+static gboolean playlist_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct player *player = data;
+
+	return player->playlist != NULL;
+}
+
+static gboolean get_playlist_count(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	uint32_t count = 1;
+
+	if (player->playlist == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &count);
+
+	return TRUE;
+}
+
+static gboolean get_orderings(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	DBusMessageIter value;
+	const char *order = "User";
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_OBJECT_PATH_AS_STRING,
+					&value);
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &order);
+	dbus_message_iter_close_container(iter, &value);
+
+	return TRUE;
+}
+
+static gboolean get_active_playlist(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value, entry;
+	dbus_bool_t enabled = TRUE;
+	const char *path, *empty = "";
+
+	if (player->playlist == NULL)
+		return FALSE;
+
+	path = g_dbus_proxy_get_path(player->playlist);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+							NULL, &value);
+	dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &enabled);
+	dbus_message_iter_open_container(&value, DBUS_TYPE_STRUCT, NULL,
+								&entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &path);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &empty);
+	dbus_message_iter_close_container(&value, &entry);
+	dbus_message_iter_close_container(iter, &value);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable playlist_properties[] = {
+	{ "PlaylistCount", "u", get_playlist_count, NULL, playlist_exists },
+	{ "Orderings", "as", get_orderings, NULL, NULL },
+	{ "ActivePlaylist", "(b(oss))", get_active_playlist, NULL,
+							playlist_exists },
+	{ }
+};
+
+#define a_z "abcdefghijklmnopqrstuvwxyz"
+#define A_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define _0_9 "_0123456789"
+
+static char *mpris_busname(char *name)
+{
+	if (g_ascii_isdigit(name[0]))
+		return g_strconcat(MPRIS_BUS_NAME, "bt_",
+				g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
+	else
+		return g_strconcat(MPRIS_BUS_NAME,
+				g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
+}
+
+static GDBusProxy *find_transport_by_path(const char *path)
+{
+	GSList *l;
+
+	for (l = transports; l; l = l->next) {
+		GDBusProxy *transport = l->data;
+		DBusMessageIter iter;
+		const char *value;
+
+		if (!g_dbus_proxy_get_property(transport, "Device", &iter))
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &value);
+
+		if (strcmp(path, value) == 0)
+			return transport;
+	}
+
+	return NULL;
+}
+
+static struct player *find_player(GDBusProxy *proxy)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+		const char *path, *p;
+
+		if (player->proxy == proxy)
+			return player;
+
+		path = g_dbus_proxy_get_path(proxy);
+		p = g_dbus_proxy_get_path(player->proxy);
+		if (g_str_equal(path, p))
+			return player;
+	}
+
+	return NULL;
+}
+
+static void register_tracklist(GDBusProxy *proxy)
+{
+	struct player *player;
+	struct tracklist *tracklist;
+
+	player = find_player(proxy);
+	if (player == NULL)
+		return;
+
+	if (player->tracklist != NULL)
+		return;
+
+	tracklist = g_new0(struct tracklist, 1);
+	tracklist->proxy = g_dbus_proxy_ref(proxy);
+
+	player->tracklist = tracklist;
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_INTERFACE,
+						"HasTrackList");
+
+	if (player->playlist == NULL)
+		return;
+
+	g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder",
+				change_folder_setup, change_folder_reply,
+				player, NULL);
+}
+
+static void register_player(GDBusProxy *proxy)
+{
+	struct player *player;
+	DBusMessageIter iter;
+	const char *path, *alias, *name;
+	char *busname;
+	GDBusProxy *device, *transport;
+
+	if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	device = g_dbus_proxy_new(client, path, "org.bluez.Device1");
+	if (device == NULL)
+		return;
+
+	if (!g_dbus_proxy_get_property(device, "Alias", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &alias);
+
+	if (g_dbus_proxy_get_property(proxy, "Name", &iter)) {
+		dbus_message_iter_get_basic(&iter, &name);
+		busname = g_strconcat(alias, " ", name, NULL);
+	} else
+		busname = g_strdup(alias);
+
+	player = g_new0(struct player, 1);
+	player->bus_name = mpris_busname(busname);
+	player->proxy = g_dbus_proxy_ref(proxy);
+	player->device = device;
+
+	g_free(busname);
+
+	players = g_slist_prepend(players, player);
+
+	printf("Player %s created\n", player->bus_name);
+
+	player->conn = g_dbus_setup_private(DBUS_BUS_SESSION, player->bus_name,
+									NULL);
+	if (!session) {
+		fprintf(stderr, "Could not register bus name %s",
+							player->bus_name);
+		goto fail;
+	}
+
+	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_INTERFACE,
+						mpris_methods,
+						NULL,
+						mpris_properties,
+						player, NULL)) {
+		fprintf(stderr, "Could not register interface %s",
+						MPRIS_INTERFACE);
+		goto fail;
+	}
+
+	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						player_methods,
+						player_signals,
+						player_properties,
+						player, player_free)) {
+		fprintf(stderr, "Could not register interface %s",
+						MPRIS_PLAYER_INTERFACE);
+		goto fail;
+	}
+
+	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_TRACKLIST_INTERFACE,
+						tracklist_methods,
+						tracklist_signals,
+						tracklist_properties,
+						player, NULL)) {
+		fprintf(stderr, "Could not register interface %s",
+						MPRIS_TRACKLIST_INTERFACE);
+		goto fail;
+	}
+
+	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYLISTS_INTERFACE,
+						playlist_methods,
+						NULL,
+						playlist_properties,
+						player, NULL)) {
+		fprintf(stderr, "Could not register interface %s",
+						MPRIS_PLAYLISTS_INTERFACE);
+		goto fail;
+	}
+
+	transport = find_transport_by_path(path);
+	if (transport)
+		player->transport = g_dbus_proxy_ref(transport);
+
+	return;
+
+fail:
+	players = g_slist_remove(players, player);
+	player_free(player);
+}
+
+static struct player *find_player_by_device(const char *device)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+		const char *path = g_dbus_proxy_get_path(player->device);
+
+		if (g_strcmp0(device, path) == 0)
+			return player;
+	}
+
+	return NULL;
+}
+
+static void register_transport(GDBusProxy *proxy)
+{
+	struct player *player;
+	DBusMessageIter iter;
+	const char *path;
+
+	if (g_slist_find(transports, proxy) != NULL)
+		return;
+
+	if (!g_dbus_proxy_get_property(proxy, "Volume", &iter))
+		return;
+
+	if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	transports = g_slist_append(transports, proxy);
+
+	player = find_player_by_device(path);
+	if (player == NULL || player->transport != NULL)
+		return;
+
+	player->transport = g_dbus_proxy_ref(proxy);
+}
+
+static struct player *find_player_by_item(const char *item)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+		const char *path = g_dbus_proxy_get_path(player->proxy);
+
+		if (g_str_has_prefix(item, path))
+			return player;
+	}
+
+	return NULL;
+}
+
+static void register_playlist(struct player *player, GDBusProxy *proxy)
+{
+	const char *path;
+	DBusMessageIter iter;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Playlist", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	if (!g_str_equal(path, g_dbus_proxy_get_path(proxy)))
+		return;
+
+	player->playlist = g_dbus_proxy_ref(proxy);
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYLISTS_INTERFACE,
+						"PlaylistCount");
+
+	if (player->tracklist == NULL)
+		return;
+
+	g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder",
+				change_folder_setup, change_folder_reply,
+				player, NULL);
+}
+
+static void register_item(struct player *player, GDBusProxy *proxy)
+{
+	struct tracklist *tracklist;
+	const char *path, *playlist;
+	DBusMessage *signal;
+	DBusMessageIter iter, args, metadata;
+	GSList *l;
+	GDBusProxy *after;
+
+	if (player->playlist == NULL) {
+		register_playlist(player, proxy);
+		return;
+	}
+
+	tracklist = player->tracklist;
+	if (tracklist == NULL)
+		return;
+
+	path = g_dbus_proxy_get_path(proxy);
+	playlist = g_dbus_proxy_get_path(player->playlist);
+	if (!g_str_has_prefix(path, playlist))
+		return;
+
+	l = g_slist_last(tracklist->items);
+	tracklist->items = g_slist_append(tracklist->items, proxy);
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_TRACKLIST_INTERFACE,
+						"Tracks");
+
+	if (l == NULL)
+		return;
+
+	signal = dbus_message_new_signal(MPRIS_PLAYER_PATH,
+					MPRIS_TRACKLIST_INTERFACE,
+					"TrackAdded");
+	if (!signal) {
+		fprintf(stderr, "Unable to allocate new %s.TrackAdded signal",
+						MPRIS_TRACKLIST_INTERFACE);
+		return;
+	}
+
+	dbus_message_iter_init_append(signal, &args);
+
+	if (!g_dbus_proxy_get_property(proxy, "Metadata", &iter)) {
+		dbus_message_unref(signal);
+		return;
+	}
+
+	dbus_message_iter_open_container(&args, 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, &metadata);
+
+	parse_metadata(&iter, &metadata, parse_track_entry);
+
+	dbus_message_iter_close_container(&args, &metadata);
+
+	after = l->data;
+	path = g_dbus_proxy_get_path(after);
+	dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path);
+
+	g_dbus_send_message(player->conn, signal);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+	const char *path;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (!strcmp(interface, BLUEZ_ADAPTER_INTERFACE)) {
+		if (adapter != NULL)
+			return;
+
+		printf("Bluetooth Adapter %s found\n", path);
+		adapter = proxy;
+		list_names(session);
+	} else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
+		printf("Bluetooth Player %s found\n", path);
+		register_player(proxy);
+	} else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
+		printf("Bluetooth Transport %s found\n", path);
+		register_transport(proxy);
+	} else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) {
+		printf("Bluetooth Folder %s found\n", path);
+		register_tracklist(proxy);
+	} else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) {
+		struct player *player;
+
+		player = find_player_by_item(path);
+		if (player == NULL)
+			return;
+
+		printf("Bluetooth Item %s found\n", path);
+		register_item(player, proxy);
+	}
+}
+
+static void unregister_player(struct player *player)
+{
+	players = g_slist_remove(players, player);
+
+	if (player->tracklist != NULL) {
+		g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYLISTS_INTERFACE);
+		g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_TRACKLIST_INTERFACE);
+	}
+
+	g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_INTERFACE);
+
+	g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE);
+}
+
+static struct player *find_player_by_transport(GDBusProxy *proxy)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+
+		if (player->transport == proxy)
+			return player;
+	}
+
+	return NULL;
+}
+
+static void unregister_transport(GDBusProxy *proxy)
+{
+	struct player *player;
+
+	if (g_slist_find(transports, proxy) == NULL)
+		return;
+
+	transports = g_slist_remove(transports, proxy);
+
+	player = find_player_by_transport(proxy);
+	if (player == NULL)
+		return;
+
+	g_dbus_proxy_unref(player->transport);
+	player->transport = NULL;
+}
+
+static void unregister_item(struct player *player, GDBusProxy *proxy)
+{
+	struct tracklist *tracklist = player->tracklist;
+	const char *path;
+
+	if (tracklist == NULL)
+		return;
+
+	if (g_slist_find(tracklist->items, proxy) == NULL)
+		return;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	tracklist->items = g_slist_remove(tracklist->items, proxy);
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_TRACKLIST_INTERFACE,
+						"Tracks");
+
+	g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
+				MPRIS_TRACKLIST_INTERFACE, "TrackRemoved",
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID);
+}
+
+static void remove_players(DBusConnection *conn)
+{
+	char **paths;
+	int i;
+
+	dbus_connection_list_registered(conn, "/", &paths);
+
+	for (i = 0; paths[i]; i++) {
+		char *path;
+		void *data;
+
+		path = g_strdup_printf("/%s", paths[i]);
+		dbus_connection_get_object_path_data(sys, path, &data);
+		dbus_connection_unregister_object_path(sys, path);
+
+		g_free(path);
+		g_free(data);
+	}
+
+	dbus_free_string_array(paths);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+	const char *path;
+
+	if (adapter == NULL)
+		return;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+	path = g_dbus_proxy_get_path(proxy);
+
+	if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) {
+		if (adapter != proxy)
+			return;
+		printf("Bluetooth Adapter %s removed\n", path);
+		adapter = NULL;
+		remove_players(sys);
+	} else if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) {
+		struct player *player;
+
+		player = find_player(proxy);
+		if (player == NULL)
+			return;
+
+		printf("Bluetooth Player %s removed\n", path);
+		unregister_player(player);
+	} else if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
+		printf("Bluetooth Transport %s removed\n", path);
+		unregister_transport(proxy);
+	} else if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0) {
+		struct player *player;
+
+		player = find_player_by_item(path);
+		if (player == NULL)
+			return;
+
+		printf("Bluetooth Item %s removed\n", path);
+		unregister_item(player, proxy);
+	}
+}
+
+static const char *property_to_mpris(const char *property)
+{
+	if (strcasecmp(property, "Repeat") == 0)
+		return "LoopStatus";
+	else if (strcasecmp(property, "Shuffle") == 0)
+		return "Shuffle";
+	else if (strcasecmp(property, "Status") == 0)
+		return "PlaybackStatus";
+	else if (strcasecmp(property, "Position") == 0)
+		return "Position";
+	else if (strcasecmp(property, "Track") == 0)
+		return "Metadata";
+
+	return NULL;
+}
+
+static void player_property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct player *player;
+	const char *property;
+	uint32_t position;
+	uint64_t value;
+
+	player = find_player(proxy);
+	if (player == NULL)
+		return;
+
+	property = property_to_mpris(name);
+	if (property == NULL)
+		return;
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						property);
+
+	if (strcasecmp(name, "Position") != 0)
+		return;
+
+	dbus_message_iter_get_basic(iter, &position);
+
+	value = position * 1000ll;
+
+	g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
+					MPRIS_PLAYER_INTERFACE, "Seeked",
+					DBUS_TYPE_INT64, &value,
+					DBUS_TYPE_INVALID);
+}
+
+static void transport_property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct player *player;
+	DBusMessageIter var;
+	const char *path;
+
+	if (strcasecmp(name, "Volume") != 0 && strcasecmp(name, "State") != 0)
+		return;
+
+	if (!g_dbus_proxy_get_property(proxy, "Device", &var))
+		return;
+
+	dbus_message_iter_get_basic(&var, &path);
+
+	player = find_player_by_device(path);
+	if (player == NULL)
+		return;
+
+	if (strcasecmp(name, "State") == 0) {
+		if (!g_dbus_proxy_get_property(player->proxy, "Status", &var))
+			g_dbus_emit_property_changed(player->conn,
+						MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						"PlaybackStatus");
+		return;
+	}
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						name);
+}
+
+static void item_property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct player *player;
+	DBusMessage *signal;
+	DBusMessageIter args;
+	const char *path;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	player = find_player_by_item(path);
+	if (player == NULL)
+		return;
+
+	if (strcasecmp(name, "Metadata") != 0)
+		return;
+
+	signal = dbus_message_new_signal(MPRIS_PLAYER_PATH,
+					MPRIS_TRACKLIST_INTERFACE,
+					"TrackMetadataChanged");
+	if (!signal) {
+		fprintf(stderr, "Unable to allocate new %s.TrackAdded signal",
+						MPRIS_TRACKLIST_INTERFACE);
+		return;
+	}
+
+	dbus_message_iter_init_append(signal, &args);
+
+	dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path);
+
+	append_iter(&args, iter);
+
+	g_dbus_send_message(player->conn, signal);
+}
+
+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, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0)
+		return player_property_changed(proxy, name, iter, user_data);
+
+	if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0)
+		return transport_property_changed(proxy, name, iter,
+								user_data);
+
+	if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0)
+		return item_property_changed(proxy, name, iter, user_data);
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *error = NULL;
+	guint owner_watch, properties_watch, signal_watch;
+	struct sigaction sa;
+
+	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) {
+		usage();
+		exit(0);
+	}
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	sys = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+	if (!sys) {
+		fprintf(stderr, "Can't get on system bus");
+		exit(1);
+	}
+
+	session = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
+	if (!session) {
+		fprintf(stderr, "Can't get on session bus");
+		exit(1);
+	}
+
+	owner_watch = g_dbus_add_signal_watch(session, NULL, NULL,
+						DBUS_INTERFACE_DBUS,
+						"NameOwnerChanged",
+						name_owner_changed,
+						NULL, NULL);
+
+	properties_watch = g_dbus_add_properties_watch(session, NULL, NULL,
+							MPRIS_PLAYER_INTERFACE,
+							player_signal,
+							NULL, NULL);
+
+	signal_watch = g_dbus_add_signal_watch(session, NULL, NULL,
+							MPRIS_PLAYER_INTERFACE,
+							NULL, player_signal,
+							NULL, NULL);
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sa.sa_handler = sig_term;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	client = g_dbus_client_new(sys, BLUEZ_BUS_NAME, BLUEZ_PATH);
+
+	g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
+
+	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+						property_changed, NULL);
+
+	g_main_loop_run(main_loop);
+
+	g_dbus_remove_watch(session, owner_watch);
+	g_dbus_remove_watch(session, properties_watch);
+	g_dbus_remove_watch(session, signal_watch);
+
+	g_dbus_client_unref(client);
+
+	dbus_connection_unref(session);
+	dbus_connection_unref(sys);
+
+	g_main_loop_unref(main_loop);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/nokfw.c b/bluez/repo/tools/nokfw.c
new file mode 100644
index 0000000..20ff846
--- /dev/null
+++ b/bluez/repo/tools/nokfw.c
@@ -0,0 +1,247 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  Intel Corporation
+ *
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+struct neg_cmd {
+	uint8_t  ack;
+	uint16_t baud;
+	uint16_t unused1;
+	uint8_t  proto;
+	uint16_t sys_clk;
+	uint16_t unused2;
+} __attribute__ ((packed));
+
+struct alive_pkt {
+	uint8_t  mid;
+	uint8_t  unused;
+} __attribute__ ((packed));
+
+static void print_cmd(uint16_t opcode, const uint8_t *buf, uint8_t plen)
+{
+	switch (opcode) {
+	case 0x0c43:
+		printf(" Write_Inquiry_Scan_Type [type=%u]", buf[0]);
+		break;
+	case 0x0c47:
+		printf(" Write_Page_Scan_Type [type=%u]", buf[0]);
+		break;
+	case 0xfc01:
+		printf(" Write_BD_ADDR [bdaddr=%02x:%02x:%02x:%02x:%02x:%02x]",
+			buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]);
+		break;
+	case 0xfc0b:
+		printf(" Write_Local_Supported_Features");
+		printf(" [features=%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x]",
+					buf[0], buf[1], buf[2], buf[3],
+					buf[4], buf[5], buf[6], buf[7]);
+		break;
+	case 0xfc0a:
+		printf(" Super_Peek_Poke [type=%u]", buf[0]);
+		break;
+	case 0xfc15:
+		printf(" FM_RDS_Command [register=0x%02x,mode=%u]",
+							buf[0], buf[1]);
+		break;
+	case 0xfc18:
+		printf(" Update_UART_Baud_Rate");
+		break;
+	case 0xfc1c:
+		printf(" Write_SCO_PCM_Int_Param");
+		break;
+	case 0xfc1e:
+		printf(" Write_PCM_Data_Format_Param");
+		break;
+	case 0xfc22:
+		printf(" Write_SCO_Time_Slot [slot=%u]", buf[0]);
+		break;
+	case 0xfc41:
+		printf(" Write_Collaboration_Mode");
+		break;
+	case 0xfc4c:
+		printf(" Write_RAM [address=0x%08x]",
+			buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
+		break;
+	case 0xfc4e:
+		printf(" Launch_RAM [address=0x%08x]",
+			buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
+		break;
+	case 0xfc61:
+		printf(" Write_PCM_Pins");
+		break;
+	}
+}
+
+static void analyze_memory(const uint8_t *buf, size_t len)
+{
+	const uint8_t *ptr = buf;
+	const struct neg_cmd *neg;
+	const struct alive_pkt *alive;
+	uint16_t pkt_len, opcode;
+	uint8_t pkt_type, plen;
+
+	while (ptr < buf + len) {
+		pkt_len = ptr[0] | ptr[1] << 8;
+		pkt_type = ptr[2];
+
+		printf("len=%-3u type=%u,", pkt_len, pkt_type);
+
+		switch (pkt_type) {
+		case 0x01:
+			opcode = ptr[3] | ptr[4] << 8;
+			plen = ptr[5];
+			printf("%-5s opcode=0x%04x plen=%-3u", "cmd",
+							opcode, plen);
+			print_cmd(opcode, ptr + 6, plen);
+			break;
+		case 0x06:
+			plen = ptr[3];
+			printf("%-5s plen=%-2u", "neg", plen);
+			neg = (void *) (ptr + 4);
+			printf(" [ack=%u baud=%u proto=0x%02x sys_clk=%u]",
+				neg->ack, neg->baud, neg->proto, neg->sys_clk);
+			break;
+		case 0x07:
+			plen = ptr[3];
+			printf("%-5s plen=%-2u", "alive", plen);
+			alive = (void *) (ptr + 4);
+			printf(" [mid=0x%02x]", alive->mid);
+			break;
+		case 0x08:
+			opcode = ptr[3] | ptr[4] << 8;
+			plen = ptr[5];
+			printf("%-5s opcode=0x%04x plen=%-3u", "radio",
+							opcode, plen);
+			print_cmd(opcode, ptr + 6, plen);
+			break;
+		default:
+			printf("unknown");
+			break;
+		}
+
+		printf("\n");
+
+		ptr += pkt_len + 2;
+	}
+}
+
+static void analyze_file(const char *pathname)
+{
+	struct stat st;
+	void *map;
+	int fd;
+
+	printf("Analyzing %s\n", pathname);
+
+	fd = open(pathname, O_RDONLY | O_CLOEXEC);
+	if (fd < 0) {
+		perror("Failed to open file");
+		return;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		fprintf(stderr, "Failed get file size\n");
+		close(fd);
+		return;
+	}
+
+	if (st.st_size == 0) {
+		fprintf(stderr, "Empty file\n");
+		close(fd);
+		return;
+	}
+
+	map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (!map || map == MAP_FAILED) {
+		fprintf(stderr, "Failed to map file\n");
+		close(fd);
+		return;
+        }
+
+	analyze_memory(map, st.st_size);
+
+	munmap(map, st.st_size);
+	close(fd);
+}
+
+static void usage(void)
+{
+	printf("Nokia Bluetooth firmware analyzer\n"
+		"Usage:\n");
+	printf("\tnokfw [options] <file>\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[])
+{
+	int i;
+
+	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 < 1) {
+		fprintf(stderr, "No input firmware files provided\n");
+		return EXIT_FAILURE;
+	}
+
+	for (i = optind; i < argc; i++)
+		analyze_file(argv[i]);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/obex-client-tool.c b/bluez/repo/tools/obex-client-tool.c
new file mode 100644
index 0000000..d0ba8a6
--- /dev/null
+++ b/bluez/repo/tools/obex-client-tool.c
@@ -0,0 +1,470 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "gobex/gobex.h"
+#include "btio/btio.h"
+
+static GMainLoop *main_loop = NULL;
+static GObex *obex = NULL;
+
+static gboolean option_packet = FALSE;
+static gboolean option_bluetooth = FALSE;
+static char *option_source = NULL;
+static char *option_dest = NULL;
+static int option_channel = -1;
+static int option_imtu = -1;
+static int option_omtu = -1;
+
+static void sig_term(int sig)
+{
+	g_print("Terminating due to signal %d\n", sig);
+	g_main_loop_quit(main_loop);
+}
+
+static GOptionEntry options[] = {
+	{ "unix", 'u', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
+			&option_bluetooth, "Use a UNIX socket" },
+	{ "bluetooth", 'b', 0, G_OPTION_ARG_NONE,
+			&option_bluetooth, "Use Bluetooth" },
+	{ "source", 's', 0, G_OPTION_ARG_STRING,
+			&option_source, "Bluetooth adapter address",
+			"00:..." },
+	{ "destination", 'd', 0, G_OPTION_ARG_STRING,
+			&option_dest, "Remote bluetooth address",
+			"00:..." },
+	{ "channel", 'c', 0, G_OPTION_ARG_INT,
+			&option_channel, "Transport channel", "CHANNEL" },
+	{ "packet", 'p', 0, G_OPTION_ARG_NONE,
+			&option_packet, "Packet based transport" },
+	{ "stream", 's', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
+			&option_packet, "Stream based transport" },
+	{ "input-mtu", 'i', 0, G_OPTION_ARG_INT,
+			&option_imtu, "Transport input MTU", "MTU" },
+	{ "output-mtu", 'o', 0, G_OPTION_ARG_INT,
+			&option_omtu, "Transport output MTU", "MTU" },
+	{ NULL },
+};
+
+static void conn_complete(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	if (err != NULL)
+		g_print("Connect failed: %s\n", err->message);
+	else
+		g_print("Connect succeeded\n");
+}
+
+static void cmd_connect(int argc, char **argv)
+{
+	g_obex_connect(obex, conn_complete, NULL, NULL, G_OBEX_HDR_INVALID);
+}
+
+struct transfer_data {
+	int fd;
+};
+
+static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
+{
+	struct transfer_data *data = user_data;
+
+	if (err != NULL)
+		g_printerr("failed: %s\n", err->message);
+	else
+		g_print("transfer succeeded\n");
+
+	close(data->fd);
+	g_free(data);
+}
+
+static gssize put_data_cb(void *buf, gsize len, gpointer user_data)
+{
+	struct transfer_data *data = user_data;
+
+	return read(data->fd, buf, len);
+}
+
+static void cmd_put(int argc, char **argv)
+{
+	struct transfer_data *data;
+	GError *err = NULL;
+	int fd;
+
+	if (argc < 2) {
+		g_printerr("Filename required\n");
+		return;
+	}
+
+	fd = open(argv[1], O_RDONLY | O_NOCTTY, 0);
+	if (fd < 0) {
+		g_printerr("open: %s\n", strerror(errno));
+		return;
+	}
+
+	data = g_new0(struct transfer_data, 1);
+	data->fd = fd;
+
+	g_obex_put_req(obex, put_data_cb, transfer_complete, data, &err,
+						G_OBEX_HDR_NAME, argv[1],
+						G_OBEX_HDR_INVALID);
+	if (err != NULL) {
+		g_printerr("put failed: %s\n", err->message);
+		g_error_free(err);
+		close(data->fd);
+		g_free(data);
+	}
+}
+
+static gboolean get_data_cb(const void *buf, gsize len, gpointer user_data)
+{
+	struct transfer_data *data = user_data;
+
+	if (write(data->fd, buf, len) < 0) {
+		g_printerr("write: %s\n", strerror(errno));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void cmd_get(int argc, char **argv)
+{
+	struct transfer_data *data;
+	GError *err = NULL;
+	int fd;
+
+	if (argc < 2) {
+		g_printerr("Filename required\n");
+		return;
+	}
+
+	fd = open(argv[1], O_WRONLY | O_CREAT | O_NOCTTY, 0600);
+	if (fd < 0) {
+		g_printerr("open: %s\n", strerror(errno));
+		return;
+	}
+
+	data = g_new0(struct transfer_data, 1);
+	data->fd = fd;
+
+	g_obex_get_req(obex, get_data_cb, transfer_complete, data, &err,
+						G_OBEX_HDR_NAME, argv[1],
+						G_OBEX_HDR_INVALID);
+	if (err != NULL) {
+		g_printerr("get failed: %s\n", err->message);
+		g_error_free(err);
+		close(data->fd);
+		g_free(data);
+	}
+}
+
+static void cmd_help(int argc, char **argv);
+
+static void cmd_exit(int argc, char **argv)
+{
+	g_main_loop_quit(main_loop);
+}
+
+static struct {
+	const char *cmd;
+	void (*func)(int argc, char **argv);
+	const char *params;
+	const char *desc;
+} commands[] = {
+	{ "help",	cmd_help,	"",		"Show this help"},
+	{ "exit",	cmd_exit,	"",		"Exit application" },
+	{ "quit",	cmd_exit,	"",		"Exit application" },
+	{ "connect",	cmd_connect,	"[target]",	"OBEX Connect" },
+	{ "put",	cmd_put,	"<file>",	"Send a file" },
+	{ "get",	cmd_get,	"<file>",	"Receive a file" },
+	{ NULL },
+};
+
+static void cmd_help(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; commands[i].cmd; i++)
+		printf("%-15s %-30s %s\n", commands[i].cmd,
+				commands[i].params, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+	char **argvp;
+	int argcp;
+	int i;
+
+	if (line_read == NULL) {
+		g_print("\n");
+		g_main_loop_quit(main_loop);
+		return;
+	}
+
+	line_read = g_strstrip(line_read);
+
+	if (*line_read == '\0') {
+		free(line_read);
+		return;
+	}
+
+	add_history(line_read);
+
+	g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+	free(line_read);
+
+	for (i = 0; commands[i].cmd; i++)
+		if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+			break;
+
+	if (commands[i].cmd)
+		commands[i].func(argcp, argvp);
+	else
+		g_print("%s: command not found\n", argvp[0]);
+
+	g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+							gpointer user_data)
+{
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_main_loop_quit(main_loop);
+		return FALSE;
+	}
+
+	rl_callback_read_char();
+
+	return TRUE;
+}
+
+static void disconn_func(GObex *obex, GError *err, gpointer user_data)
+{
+	g_printerr("Disconnected: %s\n", err ? err->message : "(no error)");
+	g_main_loop_quit(main_loop);
+}
+
+static void transport_connect(GIOChannel *io, GObexTransportType transport)
+{
+	GIOChannel *input;
+	GIOCondition events;
+
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	obex = g_obex_new(io, transport, option_imtu, option_omtu);
+	g_obex_set_disconnect_function(obex, disconn_func, NULL);
+
+	input = g_io_channel_unix_new(STDIN_FILENO);
+	g_io_channel_set_close_on_unref(input, TRUE);
+	events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch(input, events, prompt_read, NULL);
+	g_io_channel_unref(input);
+	rl_callback_handler_install("client> ", parse_line);
+}
+
+static GIOChannel *unix_connect(GObexTransportType transport)
+{
+	GIOChannel *io;
+	struct sockaddr_un addr = {
+		AF_UNIX, "\0/gobex/server"
+	};
+	int sk, err, sock_type;
+
+	if (option_packet)
+		sock_type = SOCK_SEQPACKET;
+	else
+		sock_type = SOCK_STREAM;
+
+	sk = socket(PF_LOCAL, sock_type, 0);
+	if (sk < 0) {
+		err = errno;
+		g_printerr("Can't create unix socket: %s (%d)\n",
+						strerror(err), err);
+		return NULL;
+	}
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = errno;
+		g_printerr("connect: %s (%d)\n", strerror(err), err);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sk);
+
+	g_print("Unix socket created: %d\n", sk);
+
+	transport_connect(io, transport);
+
+	return io;
+}
+
+static void conn_callback(GIOChannel *io, GError *err, gpointer user_data)
+{
+	GObexTransportType transport = GPOINTER_TO_UINT(user_data);
+
+	if (err != NULL) {
+		g_printerr("%s\n", err->message);
+		return;
+	}
+
+	g_print("Bluetooth socket connected\n");
+
+	transport_connect(io, transport);
+}
+
+static GIOChannel *l2cap_connect(GObexTransportType transport, GError **err)
+{
+	if (option_source)
+		return bt_io_connect(conn_callback,
+					GUINT_TO_POINTER(transport),
+					NULL, err,
+					BT_IO_OPT_SOURCE, option_source,
+					BT_IO_OPT_DEST, option_dest,
+					BT_IO_OPT_PSM, option_channel,
+					BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
+					BT_IO_OPT_OMTU, option_omtu,
+					BT_IO_OPT_IMTU, option_imtu,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	return bt_io_connect(conn_callback,
+					GUINT_TO_POINTER(transport),
+					NULL, err,
+					BT_IO_OPT_DEST, option_dest,
+					BT_IO_OPT_PSM, option_channel,
+					BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
+					BT_IO_OPT_OMTU, option_omtu,
+					BT_IO_OPT_IMTU, option_imtu,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+}
+
+static GIOChannel *rfcomm_connect(GObexTransportType transport, GError **err)
+{
+	if (option_source)
+		return bt_io_connect(conn_callback,
+					GUINT_TO_POINTER(transport),
+					NULL, err,
+					BT_IO_OPT_SOURCE, option_source,
+					BT_IO_OPT_DEST, option_dest,
+					BT_IO_OPT_CHANNEL, option_channel,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+
+	return bt_io_connect(conn_callback,
+					GUINT_TO_POINTER(transport),
+					NULL, err,
+					BT_IO_OPT_DEST, option_dest,
+					BT_IO_OPT_CHANNEL, option_channel,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+					BT_IO_OPT_INVALID);
+}
+
+static GIOChannel *bluetooth_connect(GObexTransportType transport)
+{
+	GIOChannel *io;
+	GError *err = NULL;
+
+	if (option_dest == NULL || option_channel < 0)
+		return NULL;
+
+	if (option_channel > 31)
+		io = l2cap_connect(transport, &err);
+	else
+		io = rfcomm_connect(transport, &err);
+
+	if (io != NULL)
+		return io;
+
+	g_printerr("%s\n", err->message);
+	g_error_free(err);
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	struct sigaction sa;
+	GIOChannel *io;
+	GObexTransportType transport;
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	g_option_context_parse(context, &argc, &argv, &err);
+	if (err != NULL) {
+		g_printerr("%s\n", err->message);
+		g_error_free(err);
+		g_option_context_free(context);
+		exit(EXIT_FAILURE);
+	}
+
+	if (option_packet)
+		transport = G_OBEX_TRANSPORT_PACKET;
+	else
+		transport = G_OBEX_TRANSPORT_STREAM;
+
+	if (option_bluetooth)
+		io = bluetooth_connect(transport);
+	else
+		io = unix_connect(transport);
+
+	if (io == NULL) {
+		g_option_context_free(context);
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = sig_term;
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(main_loop);
+
+	rl_callback_handler_remove();
+	clear_history();
+	g_obex_unref(obex);
+	g_option_context_free(context);
+	g_main_loop_unref(main_loop);
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/bluez/repo/tools/obex-server-tool.c b/bluez/repo/tools/obex-server-tool.c
new file mode 100644
index 0000000..a16c46f
--- /dev/null
+++ b/bluez/repo/tools/obex-server-tool.c
@@ -0,0 +1,458 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gobex/gobex.h"
+#include "btio/btio.h"
+
+static GMainLoop *main_loop = NULL;
+
+static GSList *clients = NULL;
+
+static gboolean option_packet = FALSE;
+static gboolean option_bluetooth = FALSE;
+static int option_channel = -1;
+static int option_imtu = -1;
+static int option_omtu = -1;
+static char *option_root = NULL;
+
+static void sig_term(int sig)
+{
+	g_print("Terminating due to signal %d\n", sig);
+	g_main_loop_quit(main_loop);
+}
+
+static GOptionEntry options[] = {
+	{ "unix", 'u', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
+			&option_bluetooth, "Use a UNIX socket" },
+	{ "bluetooth", 'b', 0, G_OPTION_ARG_NONE,
+			&option_bluetooth, "Use Bluetooth" },
+	{ "channel", 'c', 0, G_OPTION_ARG_INT,
+			&option_channel, "Transport channel", "CHANNEL" },
+	{ "packet", 'p', 0, G_OPTION_ARG_NONE,
+			&option_packet, "Packet based transport" },
+	{ "stream", 's', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
+			&option_packet, "Stream based transport" },
+	{ "root", 'r', 0, G_OPTION_ARG_STRING,
+			&option_root, "Root dir", "/..." },
+	{ "input-mtu", 'i', 0, G_OPTION_ARG_INT,
+			&option_imtu, "Transport input MTU", "MTU" },
+	{ "output-mtu", 'o', 0, G_OPTION_ARG_INT,
+			&option_omtu, "Transport output MTU", "MTU" },
+	{ NULL },
+};
+
+static void disconn_func(GObex *obex, GError *err, gpointer user_data)
+{
+	g_print("Client disconnected: %s\n", err ? err->message : "<no err>");
+	clients = g_slist_remove(clients, obex);
+	g_obex_unref(obex);
+}
+
+struct transfer_data {
+	int fd;
+};
+
+static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
+{
+	struct transfer_data *data = user_data;
+
+	if (err != NULL)
+		g_printerr("transfer failed: %s\n", err->message);
+	else
+		g_print("transfer succeeded\n");
+
+	close(data->fd);
+	g_free(data);
+}
+
+static gboolean recv_data(const void *buf, gsize len, gpointer user_data)
+{
+	struct transfer_data *data = user_data;
+
+	g_print("received %zu bytes of data\n", len);
+
+	if (write(data->fd, buf, len) < 0) {
+		g_printerr("write: %s\n", strerror(errno));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void handle_put(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	GError *err = NULL;
+	GObexHeader *hdr;
+	const char *type, *name;
+	struct transfer_data *data;
+	gsize type_len;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TYPE);
+	if (hdr != NULL) {
+		g_obex_header_get_bytes(hdr, (const guint8 **) &type,
+								&type_len);
+		if (type[type_len - 1] != '\0') {
+			g_printerr("non-nul terminated type header\n");
+			type = NULL;
+		}
+	} else
+		type = NULL;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_NAME);
+	if (hdr != NULL)
+		g_obex_header_get_unicode(hdr, &name);
+	else
+		name = NULL;
+
+	g_print("put type \"%s\" name \"%s\"\n", type ? type : "",
+							name ? name : "");
+
+	data = g_new0(struct transfer_data, 1);
+
+	data->fd = open(name, O_WRONLY | O_CREAT | O_NOCTTY, 0600);
+	if (data->fd < 0) {
+		g_printerr("open(%s): %s\n", name, strerror(errno));
+		g_free(data);
+		g_obex_send_rsp(obex, G_OBEX_RSP_FORBIDDEN, NULL,
+							G_OBEX_HDR_INVALID);
+		return;
+	}
+
+	g_obex_put_rsp(obex, req, recv_data, transfer_complete, data, &err,
+							G_OBEX_HDR_INVALID);
+	if (err != NULL) {
+		g_printerr("Unable to send response: %s\n", err->message);
+		g_error_free(err);
+		g_free(data);
+	}
+}
+
+static gssize send_data(void *buf, gsize len, gpointer user_data)
+{
+	struct transfer_data *data = user_data;
+	gssize ret;
+
+	ret = read(data->fd, buf, len);
+	g_print("sending %zu bytes of data\n", ret);
+
+	return ret;
+}
+
+static void handle_get(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	GError *err = NULL;
+	struct transfer_data *data;
+	const char *type, *name;
+	GObexHeader *hdr;
+	gsize type_len;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_TYPE);
+	if (hdr != NULL) {
+		g_obex_header_get_bytes(hdr, (const guint8 **) &type,
+								&type_len);
+		if (type[type_len - 1] != '\0') {
+			g_printerr("non-nul terminated type header\n");
+			type = NULL;
+		}
+	} else
+		type = NULL;
+
+	hdr = g_obex_packet_get_header(req, G_OBEX_HDR_NAME);
+	if (hdr != NULL)
+		g_obex_header_get_unicode(hdr, &name);
+	else
+		name = NULL;
+
+	g_print("get type \"%s\" name \"%s\"\n", type ? type : "",
+							name ? name : "");
+
+	data = g_new0(struct transfer_data, 1);
+
+	data->fd = open(name, O_RDONLY | O_NOCTTY, 0);
+	if (data->fd < 0) {
+		g_printerr("open(%s): %s\n", name, strerror(errno));
+		g_free(data);
+		g_obex_send_rsp(obex, G_OBEX_RSP_FORBIDDEN, NULL,
+							G_OBEX_HDR_INVALID);
+		return;
+	}
+
+	g_obex_get_rsp(obex, send_data, transfer_complete, data, &err,
+							G_OBEX_HDR_INVALID);
+	if (err != NULL) {
+		g_printerr("Unable to send response: %s\n", err->message);
+		g_error_free(err);
+		g_free(data);
+	}
+}
+
+static void handle_connect(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	GObexPacket *rsp;
+
+	g_print("connect\n");
+
+	rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
+	g_obex_send(obex, rsp, NULL);
+}
+
+static void transport_accept(GIOChannel *io)
+{
+	GObex *obex;
+	GObexTransportType transport;
+
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	if (option_packet)
+		transport = G_OBEX_TRANSPORT_PACKET;
+	else
+		transport = G_OBEX_TRANSPORT_STREAM;
+
+	obex = g_obex_new(io, transport, option_imtu, option_omtu);
+	g_obex_set_disconnect_function(obex, disconn_func, NULL);
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put, NULL);
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get, NULL);
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT, handle_connect,
+									NULL);
+	clients = g_slist_append(clients, obex);
+}
+
+static gboolean unix_accept(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	struct sockaddr_un addr;
+	socklen_t addrlen;
+	int sk, cli_sk;
+	GIOChannel *io;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR)) {
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return FALSE;
+	}
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	memset(&addr, 0, sizeof(addr));
+	addrlen = sizeof(addr);
+
+	cli_sk = accept(sk, (struct sockaddr *) &addr, &addrlen);
+	if (cli_sk < 0) {
+		g_printerr("accept: %s (%d)\n", strerror(errno), errno);
+		return TRUE;
+	}
+
+	g_print("Accepted new client connection on unix socket (fd=%d)\n",
+								cli_sk);
+
+	io = g_io_channel_unix_new(cli_sk);
+
+	transport_accept(io);
+	g_io_channel_unref(io);
+
+	return TRUE;
+}
+
+static void bluetooth_accept(GIOChannel *io, GError *err, gpointer data)
+{
+	if (err) {
+		g_printerr("accept: %s\n", err->message);
+		return;
+	}
+
+	g_print("Accepted new client connection on bluetooth socket\n");
+
+	transport_accept(io);
+}
+
+static gboolean bluetooth_watch(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	g_io_channel_shutdown(chan, TRUE, NULL);
+	return FALSE;
+}
+
+static GIOChannel *l2cap_listen(GError **err)
+{
+	return bt_io_listen(bluetooth_accept, NULL, NULL,
+					NULL, err,
+					BT_IO_OPT_PSM, option_channel,
+					BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
+					BT_IO_OPT_OMTU, option_omtu,
+					BT_IO_OPT_IMTU, option_imtu,
+					BT_IO_OPT_INVALID);
+}
+
+static GIOChannel *rfcomm_listen(GError **err)
+{
+	return bt_io_listen(bluetooth_accept, NULL, NULL,
+					NULL, err,
+					BT_IO_OPT_CHANNEL, option_channel,
+					BT_IO_OPT_INVALID);
+}
+
+static guint bluetooth_listen(void)
+{
+	GIOChannel *io;
+	guint id;
+	GError *err = NULL;
+
+	if (option_channel == -1) {
+		g_printerr("Bluetooth channel not set\n");
+		return 0;
+	}
+
+	if (option_packet || option_channel > 31)
+		io = l2cap_listen(&err);
+	else
+		io = rfcomm_listen(&err);
+
+	if (io == NULL) {
+		g_printerr("%s\n", err->message);
+		g_error_free(err);
+		return 0;
+	}
+
+	g_print("Bluetooth socket created\n");
+
+	id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							bluetooth_watch, NULL);
+
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_unref(io);
+
+	return id;
+}
+
+static guint unix_listen(void)
+{
+	GIOChannel *io;
+	struct sockaddr_un addr = {
+		AF_UNIX, "\0/gobex/server"
+	};
+	int sk, err, sock_type;
+	guint id;
+
+	if (option_packet)
+		sock_type = SOCK_SEQPACKET;
+	else
+		sock_type = SOCK_STREAM;
+
+	sk = socket(PF_LOCAL, sock_type, 0);
+	if (sk < 0) {
+		err = errno;
+		g_printerr("Can't create unix socket: %s (%d)\n",
+						strerror(err), err);
+		return 0;
+	}
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		g_printerr("Can't bind unix socket: %s (%d)\n",
+						strerror(errno), errno);
+		close(sk);
+		return 0;
+	}
+
+	if (listen(sk, 1) < 0) {
+		g_printerr("Can't listen on unix socket: %s (%d)\n",
+						strerror(errno), errno);
+		close(sk);
+		return 0;
+	}
+
+	g_print("Unix socket created: %d\n", sk);
+
+	io = g_io_channel_unix_new(sk);
+	id = g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							unix_accept, NULL);
+
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_unref(io);
+
+	return id;
+}
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *err = NULL;
+	struct sigaction sa;
+	guint server_id;
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	g_option_context_parse(context, &argc, &argv, &err);
+	if (err != NULL) {
+		g_printerr("%s\n", err->message);
+		g_error_free(err);
+		exit(EXIT_FAILURE);
+	}
+
+	if (option_root && chdir(option_root) < 0) {
+		perror("chdir:");
+		exit(EXIT_FAILURE);
+	}
+
+	if (option_bluetooth)
+		server_id = bluetooth_listen();
+	else
+		server_id = unix_listen();
+
+	if (server_id == 0)
+		exit(EXIT_FAILURE);
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = sig_term;
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(main_loop);
+
+	g_source_remove(server_id);
+	g_slist_free_full(clients, (GDestroyNotify) g_obex_unref);
+	g_option_context_free(context);
+	g_main_loop_unref(main_loop);
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/bluez/repo/tools/obexctl.c b/bluez/repo/tools/obexctl.c
new file mode 100644
index 0000000..86c81d5
--- /dev/null
+++ b/bluez/repo/tools/obexctl.c
@@ -0,0 +1,2556 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <inttypes.h>
+#include <wordexp.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+#include "client/display.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 "[obex]" COLOR_OFF "# "
+#define PROMPT_OFF	"[obex]# "
+
+#define OBEX_SESSION_INTERFACE "org.bluez.obex.Session1"
+#define OBEX_TRANSFER_INTERFACE "org.bluez.obex.Transfer1"
+#define OBEX_CLIENT_INTERFACE "org.bluez.obex.Client1"
+#define OBEX_OPP_INTERFACE "org.bluez.obex.ObjectPush1"
+#define OBEX_FTP_INTERFACE "org.bluez.obex.FileTransfer1"
+#define OBEX_PBAP_INTERFACE "org.bluez.obex.PhonebookAccess1"
+#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;
+static GSList *opps = NULL;
+static GSList *ftps = NULL;
+static GSList *pbaps = NULL;
+static GSList *maps = NULL;
+static GSList *msgs = NULL;
+static GSList *transfers = NULL;
+static GDBusProxy *client = NULL;
+
+struct transfer_data {
+	uint64_t transferred;
+	uint64_t size;
+};
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+	rl_set_prompt(PROMPT_ON);
+	printf("\r");
+	rl_on_new_line();
+	rl_redisplay();
+}
+
+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);
+}
+
+static void connect_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 connect: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Connection successful\n");
+}
+
+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 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);
+}
+
+struct connect_args {
+	char *dev;
+	char *target;
+};
+
+static void connect_args_free(void *data)
+{
+	struct connect_args *args = data;
+
+	g_free(args->dev);
+	g_free(args->target);
+	g_free(args);
+}
+
+static void connect_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct connect_args *args = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->dev);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	if (args->target == NULL)
+		goto done;
+
+	dict_append_entry(&dict, "Target", DBUS_TYPE_STRING, &args->target);
+
+done:
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+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");
+		return;
+	}
+
+	if (argc > 2)
+		target = argv[2];
+
+	args = g_new0(struct connect_args, 1);
+	args->dev = g_strdup(argv[1]);
+	args->target = g_strdup(target);
+
+	if (g_dbus_proxy_method_call(client, "CreateSession", connect_setup,
+			connect_reply, args, connect_args_free) == FALSE) {
+		rl_printf("Failed to connect\n");
+		return;
+	}
+
+	rl_printf("Attempting to connect to %s\n", argv[1]);
+}
+
+static void disconnect_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 disconnect: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Disconnection successful\n");
+}
+
+static void disconnect_setup(DBusMessageIter *iter, void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+	const char *path;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static GDBusProxy *find_session(const char *path)
+{
+	GSList *l;
+
+	for (l = sessions; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static void cmd_disconnect(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (argc > 1)
+		proxy = find_session(argv[1]);
+	else
+		proxy = default_session;
+
+	if (proxy == NULL) {
+		rl_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");
+		return;
+	}
+
+	rl_printf("Attempting to disconnect to %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static char *proxy_description(GDBusProxy *proxy, const char *title,
+						const char *description)
+{
+	const char *path;
+
+	path = g_dbus_proxy_get_path(proxy);
+
+	return g_strdup_printf("%s%s%s%s %s ",
+					description ? "[" : "",
+					description ? : "",
+					description ? "] " : "",
+					title, path);
+}
+
+static void print_proxy(GDBusProxy *proxy, const char *title,
+							const char *description)
+{
+	char *str;
+
+	str = proxy_description(proxy, title, description);
+
+	rl_printf("%s%s\n", str, default_session == proxy ? "[default]" : "");
+
+	g_free(str);
+}
+
+static void cmd_list(int argc, char *arg[])
+{
+	GSList *l;
+
+	for (l = sessions; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+		print_proxy(proxy, "Session", NULL);
+	}
+}
+
+static bool check_default_session(void)
+{
+	if (!default_session) {
+		rl_printf("No default session available\n");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void print_iter(const char *label, const char *name,
+						DBusMessageIter *iter)
+{
+	dbus_bool_t valbool;
+	dbus_uint64_t valu64;
+	dbus_uint32_t valu32;
+	dbus_uint16_t valu16;
+	dbus_int16_t vals16;
+	const char *valstr;
+	DBusMessageIter subiter;
+
+	if (iter == NULL) {
+		rl_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);
+		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);
+		break;
+	case DBUS_TYPE_BOOLEAN:
+		dbus_message_iter_get_basic(iter, &valbool);
+		rl_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);
+		break;
+	case DBUS_TYPE_UINT32:
+		dbus_message_iter_get_basic(iter, &valu32);
+		rl_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);
+		break;
+	case DBUS_TYPE_INT16:
+		dbus_message_iter_get_basic(iter, &vals16);
+		rl_printf("%s%s: %d\n", label, name, vals16);
+		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);
+		dbus_message_iter_get_basic(&subiter, &valstr);
+		dbus_message_iter_next(&subiter);
+		print_iter(label, valstr, &subiter);
+		break;
+	default:
+		rl_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 cmd_show(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (argc < 2) {
+		if (check_default_session() == FALSE)
+			return;
+
+		proxy = default_session;
+	} else {
+		proxy = find_session(argv[1]);
+		if (!proxy) {
+			rl_printf("Session %s not available\n", argv[1]);
+			return;
+		}
+	}
+
+	rl_printf("Session %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "Destination");
+	print_property(proxy, "Target");
+}
+
+static void set_default_session(GDBusProxy *proxy)
+{
+	char *desc;
+	DBusMessageIter iter;
+
+	default_session = proxy;
+
+	if (!g_dbus_proxy_get_property(proxy, "Destination", &iter)) {
+		desc = g_strdup(PROMPT_ON);
+		goto done;
+	}
+
+	dbus_message_iter_get_basic(&iter, &desc);
+	desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
+
+done:
+	rl_set_prompt(desc);
+	rl_redisplay();
+	g_free(desc);
+}
+
+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]);
+		return;
+	}
+
+	if (default_session == proxy)
+		return;
+
+	set_default_session(proxy);
+
+	print_proxy(proxy, "Session", NULL);
+}
+
+static GDBusProxy *find_transfer(const char *path)
+{
+	GSList *l;
+
+	for (l = transfers; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static GDBusProxy *find_message(const char *path)
+{
+	GSList *l;
+
+	for (l = msgs; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static void transfer_info(GDBusProxy *proxy, int argc, char *argv[])
+{
+	rl_printf("Transfer %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "Session");
+	print_property(proxy, "Name");
+	print_property(proxy, "Type");
+	print_property(proxy, "Status");
+	print_property(proxy, "Time");
+	print_property(proxy, "Size");
+	print_property(proxy, "Transferred");
+	print_property(proxy, "Filename");
+}
+
+static void message_info(GDBusProxy *proxy, int argc, char *argv[])
+{
+	rl_printf("Message %s\n", g_dbus_proxy_get_path(proxy));
+
+	print_property(proxy, "Folder");
+	print_property(proxy, "Subject");
+	print_property(proxy, "Timestamp");
+	print_property(proxy, "Sender");
+	print_property(proxy, "SenderAddress");
+	print_property(proxy, "ReplyTo");
+	print_property(proxy, "Recipient");
+	print_property(proxy, "RecipientAddress");
+	print_property(proxy, "Type");
+	print_property(proxy, "Size");
+	print_property(proxy, "Status");
+	print_property(proxy, "Priority");
+	print_property(proxy, "Read");
+	print_property(proxy, "Deleted");
+	print_property(proxy, "Sent");
+	print_property(proxy, "Protected");
+}
+
+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);
+		return;
+	}
+
+	proxy = find_message(argv[1]);
+	if (proxy) {
+		message_info(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Object %s not available\n", argv[1]);
+}
+
+static void cancel_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 cancel: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_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]);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "Cancel", NULL, cancel_reply, NULL,
+							NULL) == FALSE) {
+		rl_printf("Failed to cancel transfer\n");
+		return;
+	}
+
+	rl_printf("Attempting to cancel transfer %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void suspend_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 suspend: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_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]);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "Suspend", NULL, suspend_reply,
+						NULL, NULL) == FALSE) {
+		rl_printf("Failed to suspend transfer\n");
+		return;
+	}
+
+	rl_printf("Attempting to suspend transfer %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void resume_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 resume: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_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]);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "Resume", NULL, resume_reply,
+						NULL, NULL) == FALSE) {
+		rl_printf("Failed to resume transfer\n");
+		return;
+	}
+
+	rl_printf("Attempting to resume transfer %s\n",
+						g_dbus_proxy_get_path(proxy));
+}
+
+static GDBusProxy *find_opp(const char *path)
+{
+	GSList *l;
+
+	for (l = opps; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static GDBusProxy *find_map(const char *path)
+{
+	GSList *l;
+
+	for (l = maps; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static void print_dict_iter(DBusMessageIter *iter)
+{
+	DBusMessageIter dict;
+	int ctype;
+
+	ctype = dbus_message_iter_get_arg_type(iter);
+	if (ctype != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(iter, &dict);
+
+	while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+							DBUS_TYPE_INVALID) {
+		DBusMessageIter entry;
+		const char *key;
+
+		if (ctype != DBUS_TYPE_DICT_ENTRY)
+			return;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			return;
+
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+
+		print_iter("\t", key, &entry);
+
+		dbus_message_iter_next(&dict);
+	}
+}
+
+static void print_transfer_iter(DBusMessageIter *iter)
+{
+	const char *path;
+
+	dbus_message_iter_get_basic(iter, &path);
+
+	rl_printf("Transfer %s\n", path);
+
+	dbus_message_iter_next(iter);
+
+	print_dict_iter(iter);
+}
+
+static void send_reply(DBusMessage *message, void *user_data)
+{
+	DBusMessageIter iter;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to send/pull: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	print_transfer_iter(&iter);
+}
+
+static void send_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *file = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
+}
+
+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");
+		return;
+	}
+
+	rl_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");
+		return;
+	}
+
+	rl_printf("Attempting to pull %s from %s\n", argv[1],
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void push_reply(DBusMessage *message, void *user_data)
+{
+	DBusMessageIter iter;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to PushMessage: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	print_transfer_iter(&iter);
+}
+
+static void push_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *file = user_data;
+	const char *folder = "";
+	DBusMessageIter dict;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void 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");
+		return;
+	}
+
+	rl_printf("Attempting to send %s to %s\n", argv[1],
+						g_dbus_proxy_get_path(proxy));
+}
+
+static void cmd_send(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (!check_default_session())
+		return;
+
+	proxy = find_opp(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		opp_send(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_map(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		map_send(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Command not supported\n");
+}
+
+static void cmd_pull(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (!check_default_session())
+		return;
+
+	proxy = find_opp(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		opp_pull(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Command not supported\n");
+}
+
+static void change_folder_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 ChangeFolder: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("ChangeFolder successful\n");
+}
+
+static void change_folder_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *folder = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
+}
+
+static void select_reply(DBusMessage *message, void *user_data)
+{
+	DBusMessageIter iter;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to Select: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	rl_printf("Select successful\n");
+}
+
+static void select_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *folder = user_data;
+	const char *location = "int";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &location);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
+}
+
+static void setfolder_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 SetFolder: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("SetFolder successful\n");
+}
+
+static void setfolder_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *folder = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
+}
+
+static GDBusProxy *find_ftp(const char *path)
+{
+	GSList *l;
+
+	for (l = ftps; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static GDBusProxy *find_pbap(const char *path)
+{
+	GSList *l;
+
+	for (l = pbaps; l; l = g_slist_next(l)) {
+		GDBusProxy *proxy = l->data;
+
+		if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+			return proxy;
+	}
+
+	return NULL;
+}
+
+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");
+		return;
+	}
+
+	rl_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");
+		return;
+	}
+
+	rl_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");
+		return;
+	}
+
+	rl_printf("Attempting to SetFolder to %s\n", argv[1]);
+}
+
+static void cmd_cd(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (!check_default_session())
+		return;
+
+	proxy = find_ftp(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		ftp_cd(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_pbap(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		pbap_cd(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_map(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		map_cd(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Command not supported\n");
+}
+
+static void list_folder_reply(DBusMessage *message, void *user_data)
+{
+	DBusMessageIter iter, array;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to ListFolder: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+		print_dict_iter(&array);
+		dbus_message_iter_next(&array);
+	}
+}
+
+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");
+		return;
+	}
+
+	rl_printf("Attempting to ListFolder\n");
+}
+
+static void parse_list_reply(DBusMessage *message)
+{
+	DBusMessageIter iter, array;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+		DBusMessageIter entry;
+		const char *vcard;
+
+		if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRUCT)
+			return;
+
+		dbus_message_iter_recurse(&array, &entry);
+
+		dbus_message_iter_get_basic(&entry, &vcard);
+		dbus_message_iter_next(&entry);
+		print_iter("\t", vcard, &entry);
+		dbus_message_iter_next(&array);
+	}
+}
+
+static void list_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 List: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	parse_list_reply(message);
+}
+
+static void list_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void search_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 Search: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	parse_list_reply(message);
+}
+
+static void search_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *value = user_data;
+	const char *field;
+	DBusMessageIter dict;
+
+	field = isalpha(value[0]) ? "name" : "number";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &field);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void 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");
+		return;
+	}
+
+	rl_printf("Attempting to Search\n");
+}
+
+static void list_folders_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+	DBusMessageIter iter, array;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to ListFolders: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+		print_dict_iter(&array);
+		dbus_message_iter_next(&array);
+	}
+}
+
+static void list_folders_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void list_messages_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+	DBusMessageIter iter, array;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to ListFolders: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return;
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	while ((dbus_message_iter_get_arg_type(&array)) ==
+						DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+		const char *obj;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &obj);
+		rl_printf("\t%s\n", obj);
+		dbus_message_iter_next(&array);
+	}
+}
+
+static void list_messages_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *folder = user_data;
+	DBusMessageIter dict;
+
+	if (strcmp(folder, "*") == 0)
+		folder = "";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void 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");
+		return;
+	}
+
+	rl_printf("Attempting to List\n");
+}
+
+static void get_size_reply(DBusMessage *message, void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+	DBusError error;
+	DBusMessageIter iter;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to GetSize: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	print_iter("\t", "Size", &iter);
+
+	pbap_list(proxy, 0, NULL);
+}
+
+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");
+		return;
+	}
+
+	rl_printf("Attempting to GetSize\n");
+}
+
+static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
+{
+	if (argc > 1) {
+		if (strcmp("-l", argv[1]))
+			pbap_search(proxy, argc, argv);
+		else
+			pbap_get_size(proxy, argc, argv);
+		return;
+	}
+
+	pbap_list(proxy, argc, argv);
+}
+
+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");
+		return;
+	}
+
+	rl_printf("Attempting to ListMessages\n");
+}
+
+static void map_ls(GDBusProxy *proxy, int argc, char *argv[])
+{
+	if (argc > 1) {
+		map_ls_messages(proxy, argc, argv);
+		return;
+	}
+
+	if (g_dbus_proxy_method_call(proxy, "ListFolders", list_folders_setup,
+						list_folders_reply, NULL,
+						NULL) == FALSE) {
+		rl_printf("Failed to ListFolders\n");
+		return;
+	}
+
+	rl_printf("Attempting to ListFolders\n");
+}
+
+static void cmd_ls(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (!check_default_session())
+		return;
+
+	proxy = find_ftp(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		ftp_ls(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_pbap(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		pbap_ls(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_map(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		map_ls(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Command not supported\n");
+}
+
+struct cp_args {
+	char *source;
+	char *target;
+};
+
+static void cp_free(void *data)
+{
+	struct cp_args *args = data;
+
+	g_free(args->source);
+	g_free(args->target);
+	g_free(args);
+}
+
+static struct cp_args *cp_new(char *argv[])
+{
+	struct cp_args *args;
+	const char *source;
+	const char *target;
+
+	source = rindex(argv[1], ':');
+	if (source == NULL)
+		source = argv[1];
+	else
+		source++;
+
+	target = rindex(argv[2], ':');
+	if (target == NULL)
+		target = argv[2];
+	else
+		target++;
+
+	args = g_new0(struct cp_args, 1);
+	args->source = g_strdup(source);
+	args->target = g_strdup(target);
+
+	return args;
+}
+
+static void cp_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct cp_args *args = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->source);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->target);
+}
+
+static void copy_file_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 CopyFile: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("CopyFile successful\n");
+}
+
+static void ftp_copy(GDBusProxy *proxy, int argc, char *argv[])
+{
+	struct cp_args *args;
+
+	args = cp_new(argv);
+
+	if (g_dbus_proxy_method_call(proxy, "CopyFile", cp_setup,
+				copy_file_reply, args, cp_free) == FALSE) {
+		rl_printf("Failed to CopyFile\n");
+		return;
+	}
+
+	rl_printf("Attempting to CopyFile\n");
+}
+
+static void get_file_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+	DBusMessageIter iter;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to GetFile: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	print_transfer_iter(&iter);
+}
+
+static void get_file_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct cp_args *args = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->target);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->source);
+}
+
+static void ftp_get(GDBusProxy *proxy, int argc, char *argv[])
+{
+	struct cp_args *args;
+
+	if (rindex(argv[2], ':') == NULL)
+		return ftp_copy(proxy, argc, argv);
+
+	args = cp_new(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");
+		return;
+	}
+
+	rl_printf("Attempting to GetFile\n");
+}
+
+static void put_file_reply(DBusMessage *message, void *user_data)
+{
+	DBusError error;
+	DBusMessageIter iter;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to PutFile: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	print_transfer_iter(&iter);
+}
+
+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");
+		return;
+	}
+
+	args = cp_new(argv);
+
+	if (g_dbus_proxy_method_call(proxy, "PutFile", cp_setup, put_file_reply,
+						args, cp_free) == FALSE) {
+		rl_printf("Failed to PutFile\n");
+		return;
+	}
+
+	rl_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);
+
+	return ftp_put(proxy, argc, argv);
+}
+
+static void pull_all_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 PullAll: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+
+	rl_printf("PullAll successful\n");
+}
+
+static void pull_all_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *file = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void 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");
+		return;
+	}
+
+	rl_printf("Attempting to PullAll\n");
+}
+
+static void pull_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 Pull: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+
+	rl_printf("Pull successful\n");
+}
+
+static void pull_setup(DBusMessageIter *iter, void *user_data)
+{
+	struct cp_args *args = user_data;
+	DBusMessageIter dict;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->source);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &args->target);
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+					&dict);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void pbap_pull(GDBusProxy *proxy, int argc, char *argv[])
+{
+	struct cp_args *args;
+
+	args = cp_new(argv);
+
+	if (g_dbus_proxy_method_call(proxy, "Pull", pull_setup, pull_reply,
+						args, cp_free) == FALSE) {
+		rl_printf("Failed to Pull\n");
+		return;
+	}
+
+	rl_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)
+		return pbap_pull_all(proxy, argc, argv);
+
+	return pbap_pull(proxy, argc, argv);
+}
+
+static void get_reply(DBusMessage *message, void *user_data)
+{
+	DBusMessageIter iter;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	if (dbus_set_error_from_message(&error, message) == TRUE) {
+		rl_printf("Failed to Get: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	print_transfer_iter(&iter);
+}
+
+static void get_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *file = user_data;
+	dbus_bool_t attachment = TRUE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &attachment);
+}
+
+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");
+		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");
+		return;
+	}
+
+	rl_printf("Attempting to Get\n");
+}
+
+static void cmd_cp(int argc, char *argv[])
+{
+
+	GDBusProxy *proxy;
+
+	if (!check_default_session())
+		return;
+
+	proxy = find_ftp(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		ftp_cp(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_pbap(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		pbap_cp(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_map(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		map_cp(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Command not supported\n");
+}
+
+static void move_file_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 MoveFile: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("MoveFile successful\n");
+}
+
+static void cmd_mv(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+	struct cp_args *args;
+
+	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");
+		return;
+	}
+
+	args = cp_new(argv);
+
+	if (g_dbus_proxy_method_call(proxy, "MoveFile", cp_setup,
+				move_file_reply, args, cp_free) == FALSE) {
+		rl_printf("Failed to MoveFile\n");
+		return;
+	}
+
+	rl_printf("Attempting to MoveFile\n");
+}
+
+static void delete_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 Delete: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("Delete successful\n");
+}
+
+static void delete_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *file = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &file);
+}
+
+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");
+		return;
+	}
+
+	rl_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);
+	else
+		rl_printf("Set Deleted successful\n");
+}
+
+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");
+		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");
+		return;
+	}
+
+	rl_printf("Attempting to set Deleted\n");
+}
+
+static void cmd_rm(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	if (!check_default_session())
+		return;
+
+	proxy = find_ftp(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		ftp_rm(proxy, argc, argv);
+		return;
+	}
+
+	proxy = find_map(g_dbus_proxy_get_path(default_session));
+	if (proxy) {
+		map_rm(proxy, argc, argv);
+		return;
+	}
+
+	rl_printf("Command not supported\n");
+}
+
+static void create_folder_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 CreateFolder: %s\n", error.name);
+		dbus_error_free(&error);
+		return;
+	}
+
+	rl_printf("CreateFolder successful\n");
+}
+
+static void create_folder_setup(DBusMessageIter *iter, void *user_data)
+{
+	const char *folder = user_data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &folder);
+}
+
+static void cmd_mkdir(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+
+	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");
+		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");
+		return;
+	}
+
+	rl_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[] = {
+	{ "connect",      "<dev> [uuid]", cmd_connect, "Connect session" },
+	{ "disconnect",   "[session]", cmd_disconnect, "Disconnect session" },
+	{ "list",         NULL,       cmd_list, "List available sessions" },
+	{ "show",         "[session]", cmd_show, "Session information" },
+	{ "select",       "<session>", cmd_select, "Select default session" },
+	{ "info",         "<object>", cmd_info, "Object information" },
+	{ "cancel",       "<transfer>", cmd_cancel, "Cancel transfer" },
+	{ "suspend",      "<transfer>", cmd_suspend, "Suspend transfer" },
+	{ "resume",       "<transfer>", cmd_resume, "Resume transfer" },
+	{ "send",         "<file>",   cmd_send, "Send file" },
+	{ "pull",	  "<file>",   cmd_pull,
+					"Pull Vobject & stores in file" },
+	{ "cd",           "<path>",   cmd_cd, "Change current folder" },
+	{ "ls",           "<options>", cmd_ls, "List current folder" },
+	{ "cp",          "<source file> <destination file>",   cmd_cp,
+				"Copy source file to destination file" },
+	{ "mv",          "<source file> <destination file>",   cmd_mv,
+				"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)
+		client = proxy;
+
+	print_proxy(proxy, "Client", COLORED_NEW);
+}
+
+static void session_added(GDBusProxy *proxy)
+{
+	sessions = g_slist_append(sessions, proxy);
+
+	if (default_session == NULL)
+		set_default_session(proxy);
+
+	print_proxy(proxy, "Session", COLORED_NEW);
+}
+
+static void print_transferred(struct transfer_data *data, const char *str,
+							DBusMessageIter *iter)
+{
+	dbus_uint64_t valu64;
+	uint64_t speed;
+	int seconds, minutes;
+
+	dbus_message_iter_get_basic(iter, &valu64);
+	speed = valu64 - data->transferred;
+	data->transferred = valu64;
+
+	if (data->size == 0) {
+		rl_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);
+}
+
+static void transfer_property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct transfer_data *data = user_data;
+	char *str;
+
+	str = proxy_description(proxy, "Transfer", COLORED_CHG);
+
+	if (strcmp(name, "Transferred") == 0) {
+		print_transferred(data, str, iter);
+		goto done;
+	}
+
+	if (strcmp(name, "Size") == 0)
+		dbus_message_iter_get_basic(iter, &data->size);
+
+	print_iter(str, name, iter);
+
+done:
+	g_free(str);
+}
+
+static void transfer_destroy(GDBusProxy *proxy, void *user_data)
+{
+	struct transfer_data *data = user_data;
+
+	g_free(data);
+}
+
+static void transfer_added(GDBusProxy *proxy)
+{
+	struct transfer_data *data;
+	DBusMessageIter iter;
+
+	transfers = g_slist_append(transfers, proxy);
+
+	print_proxy(proxy, "Transfer", COLORED_NEW);
+
+	data = g_new0(struct transfer_data, 1);
+
+	if (g_dbus_proxy_get_property(proxy, "Transfered", &iter))
+		dbus_message_iter_get_basic(&iter, &data->transferred);
+
+	if (g_dbus_proxy_get_property(proxy, "Size", &iter))
+		dbus_message_iter_get_basic(&iter, &data->size);
+
+	g_dbus_proxy_set_property_watch(proxy, transfer_property_changed, data);
+	g_dbus_proxy_set_removed_watch(proxy, transfer_destroy, data);
+}
+
+static void opp_added(GDBusProxy *proxy)
+{
+	opps = g_slist_append(opps, proxy);
+
+	print_proxy(proxy, "ObjectPush", COLORED_NEW);
+}
+
+static void ftp_added(GDBusProxy *proxy)
+{
+	ftps = g_slist_append(ftps, proxy);
+
+	print_proxy(proxy, "FileTransfer", COLORED_NEW);
+}
+
+static void pbap_added(GDBusProxy *proxy)
+{
+	pbaps = g_slist_append(pbaps, proxy);
+
+	print_proxy(proxy, "PhonebookAccess", COLORED_NEW);
+}
+
+static void map_added(GDBusProxy *proxy)
+{
+	maps = g_slist_append(maps, proxy);
+
+	print_proxy(proxy, "MessageAccess", COLORED_NEW);
+}
+
+static void msg_added(GDBusProxy *proxy)
+{
+	msgs = g_slist_append(msgs, proxy);
+
+	print_proxy(proxy, "Message", COLORED_NEW);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (!strcmp(interface, OBEX_CLIENT_INTERFACE))
+		client_added(proxy);
+	else if (!strcmp(interface, OBEX_SESSION_INTERFACE))
+		session_added(proxy);
+	else if (!strcmp(interface, OBEX_TRANSFER_INTERFACE))
+		transfer_added(proxy);
+	else if (!strcmp(interface, OBEX_OPP_INTERFACE))
+		opp_added(proxy);
+	else if (!strcmp(interface, OBEX_FTP_INTERFACE))
+		ftp_added(proxy);
+	else if (!strcmp(interface, OBEX_PBAP_INTERFACE))
+		pbap_added(proxy);
+	else if (!strcmp(interface, OBEX_MAP_INTERFACE))
+		map_added(proxy);
+	else if (!strcmp(interface, OBEX_MSG_INTERFACE))
+		msg_added(proxy);
+}
+
+static void client_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "Client", COLORED_DEL);
+
+	if (client == proxy)
+		client = NULL;
+}
+
+static void session_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "Session", COLORED_DEL);
+
+	if (default_session == proxy)
+		set_default_session(NULL);
+
+	sessions = g_slist_remove(sessions, proxy);
+}
+
+static void transfer_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "Transfer", COLORED_DEL);
+
+	transfers = g_slist_remove(transfers, proxy);
+}
+
+static void opp_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "ObjectPush", COLORED_DEL);
+
+	opps = g_slist_remove(opps, proxy);
+}
+
+static void ftp_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "FileTransfer", COLORED_DEL);
+
+	ftps = g_slist_remove(ftps, proxy);
+}
+
+static void pbap_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "PhonebookAccess", COLORED_DEL);
+
+	pbaps = g_slist_remove(pbaps, proxy);
+}
+
+static void map_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "MessageAccess", COLORED_DEL);
+
+	maps = g_slist_remove(maps, proxy);
+}
+
+static void msg_removed(GDBusProxy *proxy)
+{
+	print_proxy(proxy, "Message", COLORED_DEL);
+
+	msgs = g_slist_remove(msgs, proxy);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (!strcmp(interface, OBEX_CLIENT_INTERFACE))
+		client_removed(proxy);
+	else if (!strcmp(interface, OBEX_SESSION_INTERFACE))
+		session_removed(proxy);
+	else if (!strcmp(interface, OBEX_TRANSFER_INTERFACE))
+		transfer_removed(proxy);
+	else if (!strcmp(interface, OBEX_OPP_INTERFACE))
+		opp_removed(proxy);
+	else if (!strcmp(interface, OBEX_FTP_INTERFACE))
+		ftp_removed(proxy);
+	else if (!strcmp(interface, OBEX_PBAP_INTERFACE))
+		pbap_removed(proxy);
+	else if (!strcmp(interface, OBEX_MAP_INTERFACE))
+		map_removed(proxy);
+	else if (!strcmp(interface, OBEX_MSG_INTERFACE))
+		msg_removed(proxy);
+}
+
+static void session_property_changed(GDBusProxy *proxy, const char *name,
+						DBusMessageIter *iter)
+{
+	char *str;
+
+	str = proxy_description(proxy, "Session", COLORED_CHG);
+	print_iter(str, name, iter);
+	g_free(str);
+}
+
+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, OBEX_SESSION_INTERFACE))
+		session_property_changed(proxy, name, iter);
+}
+
+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);
+	}
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+	dbus_conn = g_dbus_setup_bus(DBUS_BUS_SESSION, 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.obex",
+							"/org/bluez/obex");
+
+	g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
+
+	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+							property_changed, NULL);
+
+	g_main_loop_run(main_loop);
+
+	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/bluez/repo/tools/oobtest.c b/bluez/repo/tools/oobtest.c
new file mode 100644
index 0000000..e77320b
--- /dev/null
+++ b/bluez/repo/tools/oobtest.c
@@ -0,0 +1,1157 @@
+/*
+ *
+ *  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 "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/crypto.h"
+
+#define REMOTE_IRK	"\x69\x30\xde\xc3\x8f\x84\x74\x14" \
+			"\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
+
+static bool use_bredr = false;
+static bool use_le = false;
+static bool use_sc = false;
+static bool use_sconly = false;
+static bool use_legacy = false;
+static bool use_random = false;
+static bool use_privacy = false;
+static bool use_debug = false;
+static bool use_cross = false;
+static bool provide_tk = false;
+static bool provide_p192 = false;
+static bool provide_p256 = false;
+static bool provide_initiator = false;
+static bool provide_acceptor = false;
+
+static struct mgmt *mgmt;
+static uint16_t index1 = MGMT_INDEX_NONE;
+static uint16_t index2 = MGMT_INDEX_NONE;
+static bdaddr_t bdaddr1;
+static bdaddr_t bdaddr2;
+static uint8_t oob_tk[16];
+
+static void pin_code_request_event(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_pin_code_request *ev = param;
+	struct mgmt_cp_pin_code_reply cp;
+	char str[18];
+
+	ba2str(&ev->addr.bdaddr, str);
+
+	printf("[Index %u]\n", index);
+	printf("  Pin code request: %s\n", str);
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+	cp.pin_len = 4;
+	memset(cp.pin_code, '0', 4);
+
+	mgmt_reply(mgmt, MGMT_OP_PIN_CODE_REPLY, index, sizeof(cp), &cp,
+							NULL, NULL, NULL);
+}
+
+static void new_link_key_event(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_link_key *ev = param;
+	const char *type;
+	char str[18];
+	int i;
+
+	ba2str(&ev->key.addr.bdaddr, str);
+
+	switch (ev->key.type) {
+	case 0x00:
+		type = "Legacy";
+		break;
+	case 0x01:
+		type = "Local Unit";
+		break;
+	case 0x02:
+		type = "Remote Unit";
+		break;
+	case 0x03:
+		type = "Debug";
+		break;
+	case 0x04:
+		type = "Unauthenticated, P-192";
+		break;
+	case 0x05:
+		type = "Authenticated, P-192";
+		break;
+	case 0x06:
+		type = "Changed";
+		break;
+	case 0x07:
+		type = "Unauthenticated, P-256";
+		break;
+	case 0x08:
+		type = "Authenticated, P-256";
+		break;
+	default:
+		type = "<unknown>";
+		break;
+	}
+
+	printf("[Index %u]\n", index);
+	printf("  New link key: %s\n", str);
+	printf("  Type: %s (%u)\n", type, ev->key.type);
+	printf("  Key: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", ev->key.val[i]);
+	printf("\n");
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_ev_new_long_term_key *ev = param;
+	const char *type;
+	char str[18];
+	int i;
+
+	ba2str(&ev->key.addr.bdaddr, str);
+
+	switch (ev->key.type) {
+	case 0x00:
+		if (ev->key.master)
+			type = "Unauthenticated, Master";
+		else
+			type = "Unauthenticated, Slave";
+		break;
+	case 0x01:
+		if (ev->key.master)
+			type = "Authenticated, Master";
+		else
+			type = "Authenticated, Slave";
+		break;
+	case 0x02:
+		type = "Unauthenticated, P-256";
+		break;
+	case 0x03:
+		type = "Authenticated, P-256";
+		break;
+	case 0x04:
+		type = "Debug";
+		break;
+	default:
+		type = "<unknown>";
+		break;
+	}
+
+	printf("[Index %u]\n", index);
+	printf("  New long term key: %s\n", str);
+	printf("  Type: %s (%u)\n", type, ev->key.type);
+	printf("  Key: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", ev->key.val[i]);
+	printf("\n");
+}
+
+static void pair_device_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Pair device from index %u failed: %s\n",
+						index, mgmt_errstr(status));
+	}
+
+	mainloop_quit();
+}
+
+static void pair_device(uint16_t index, const bdaddr_t *bdaddr)
+{
+	struct mgmt_cp_pair_device cp;
+	char str[18];
+
+	ba2str(bdaddr, str);
+
+	printf("[Index %u]\n", index);
+	printf("  Starting pairing: %s\n", str);
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	if (use_bredr)
+		cp.addr.type = BDADDR_BREDR;
+	else if (use_random)
+		cp.addr.type = BDADDR_LE_RANDOM;
+	else
+		cp.addr.type = BDADDR_LE_PUBLIC;
+	cp.io_cap = 0x03;
+
+	mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp,
+						pair_device_complete,
+						UINT_TO_PTR(index), NULL);
+}
+
+static void add_remote_oob_data_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_addr_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	char str[18];
+
+	if (status) {
+		fprintf(stderr, "Adding OOB data for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+	}
+
+	ba2str(&rp->bdaddr, str);
+
+	printf("[Index %u]\n", index);
+	printf("  Remote data added: %s\n", str);
+
+	if (index == index1) {
+		uint8_t val = 0x01;
+
+		mgmt_send(mgmt, MGMT_OP_SET_CONNECTABLE, index2, 1, &val,
+							NULL, NULL, NULL);
+
+		if (use_le)
+			mgmt_send(mgmt, MGMT_OP_SET_ADVERTISING, index2,
+						1, &val, NULL, NULL, NULL);
+
+		pair_device(index1, &bdaddr2);
+	}
+}
+
+static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr,
+				const uint8_t *hash192, const uint8_t *rand192,
+				const uint8_t *hash256, const uint8_t *rand256)
+{
+	struct mgmt_cp_add_remote_oob_data cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, bdaddr);
+	if (use_bredr)
+		cp.addr.type = BDADDR_BREDR;
+	else if (use_random)
+		cp.addr.type = BDADDR_LE_RANDOM;
+	else
+		cp.addr.type = BDADDR_LE_PUBLIC;
+	if (hash192) {
+		memcpy(cp.hash192, hash192, 16);
+		if (rand192)
+			memcpy(cp.rand192, rand192, 16);
+		else
+			memset(cp.rand192, 0, 16);
+	} else {
+		memset(cp.hash192, 0, 16);
+		memset(cp.rand192, 0, 16);
+	}
+	if (hash256 && rand256) {
+		memcpy(cp.hash256, hash256, 16);
+		memcpy(cp.rand256, rand256, 16);
+	} else {
+		memset(cp.hash256, 0, 16);
+		memset(cp.rand256, 0, 16);
+	}
+
+	mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index, sizeof(cp), &cp,
+						add_remote_oob_data_complete,
+						UINT_TO_PTR(index), NULL);
+}
+
+static void read_oob_data_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_local_oob_data *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	const uint8_t *hash192, *rand192, *hash256, *rand256;
+	int i;
+
+	if (status) {
+		fprintf(stderr, "Reading OOB data for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("[Index %u]\n", index);
+
+	hash192 = NULL;
+	rand192 = NULL;
+	hash256 = NULL;
+	rand256 = NULL;
+
+	if (index == index1 && !provide_initiator) {
+		printf("  Skipping initiator OOB data\n");
+		goto done;
+	} else if (index == index2 && !provide_acceptor) {
+		printf("  Skipping acceptor OOB data\n");
+		goto done;
+	}
+
+	if (provide_p192) {
+		hash192 = rp->hash192;
+		rand192 = rp->rand192;
+	}
+
+	printf("  Hash C from P-192: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp->hash192[i]);
+	printf("\n");
+
+	printf("  Randomizer R with P-192: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp->rand192[i]);
+	printf("\n");
+
+	if (len < sizeof(*rp))
+		goto done;
+
+	if (provide_p256) {
+		hash256 = rp->hash256;
+		rand256 = rp->rand256;
+	}
+
+	printf("  Hash C from P-256: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp->hash256[i]);
+	printf("\n");
+
+	printf("  Randomizer R with P-256: ");
+	for (i = 0; i < 16; i++)
+		printf("%02x", rp->rand256[i]);
+	printf("\n");
+
+done:
+	if (index == index1)
+		add_remote_oob_data(index2, &bdaddr1,
+					hash192, rand192, hash256, rand256);
+	else if (index == index2)
+		add_remote_oob_data(index1, &bdaddr2,
+					hash192, rand192, hash256, rand256);
+}
+
+static void read_oob_ext_data_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint16_t eir_len, parsed;
+	const uint8_t *eir, *tk, *hash256, *rand256;
+	int i;
+
+	if (status) {
+		fprintf(stderr, "Reading OOB data for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	printf("[Index %u]\n", index);
+
+	eir_len = le16_to_cpu(rp->eir_len);
+	printf("  OOB data len: %u\n", eir_len);
+
+	if (provide_tk)
+		tk = oob_tk;
+	else
+		tk = NULL;
+
+	hash256 = NULL;
+	rand256 = NULL;
+
+	if (index == index1 && !provide_initiator) {
+		printf("  Skipping initiator OOB data\n");
+		goto done;
+	} else if (index == index2 && !provide_acceptor) {
+		printf("  Skipping acceptor OOB data\n");
+		goto done;
+	}
+
+	if (eir_len < 2)
+		goto done;
+
+	eir = rp->eir;
+	parsed = 0;
+
+	while (parsed < eir_len - 1) {
+		uint8_t field_len = eir[0];
+
+		if (field_len == 0)
+			break;
+
+		parsed += field_len + 1;
+
+		if (parsed > eir_len)
+			break;
+
+		/* LE Bluetooth Device Address */
+		if (eir[1] == 0x1b) {
+			char str[18];
+
+			ba2str((bdaddr_t *) (eir + 2), str);
+			printf("  Device address: %s (%s)\n", str,
+						eir[8] ? "random" : "public");
+		}
+
+		/* LE Role */
+		if (eir[1] == 0x1c)
+			printf("  Role: 0x%02x\n", eir[2]);
+
+		/* LE Secure Connections Confirmation Value */
+		if (eir[1] == 0x22) {
+			hash256 = eir + 2;
+
+			printf("  Hash C from P-256: ");
+			for (i = 0; i < 16; i++)
+				printf("%02x", hash256[i]);
+			printf("\n");
+		}
+
+		/* LE Secure Connections Random Value */
+		if (eir[1] == 0x23) {
+			rand256 = eir + 2;
+
+			printf("  Randomizer R with P-256: ");
+			for (i = 0; i < 16; i++)
+				printf("%02x", rand256[i]);
+			printf("\n");
+		}
+
+		eir += field_len + 1;
+	}
+
+done:
+	if (index == index1)
+		add_remote_oob_data(index2, &bdaddr1,
+					tk, NULL, hash256, rand256);
+	else if (index == index2)
+		add_remote_oob_data(index1, &bdaddr2,
+					tk, NULL, hash256, rand256);
+}
+
+static void set_powered_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t settings;
+	uint8_t val;
+
+	if (status) {
+		fprintf(stderr, "Powering on for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	settings = get_le32(param);
+
+	if (!(settings & MGMT_SETTING_POWERED)) {
+		fprintf(stderr, "Controller is not powered\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_debug) {
+		if (index == index1) {
+			val = 0x02;
+			mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+							NULL, NULL, NULL);
+		} else if (index == index2) {
+			val = 0x01;
+			mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+							NULL, NULL, NULL);
+		}
+	}
+
+	if (use_bredr && (provide_p192 || provide_p256)) {
+		mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
+						read_oob_data_complete,
+						UINT_TO_PTR(index), NULL);
+	} else if (use_le && provide_p256) {
+		uint8_t type = (1 << BDADDR_LE_PUBLIC) |
+						(1 << BDADDR_LE_RANDOM);
+
+		mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index,
+						sizeof(type), &type,
+						read_oob_ext_data_complete,
+						UINT_TO_PTR(index), NULL);
+	} else if (use_le && provide_tk) {
+		const uint8_t *tk = oob_tk;
+
+		if (index == index1)
+			add_remote_oob_data(index2, &bdaddr1,
+						tk, NULL, NULL, NULL);
+		else if (index == index2)
+			add_remote_oob_data(index1, &bdaddr2,
+						tk, NULL, NULL, NULL);
+	} else {
+		if (index == index1)
+			add_remote_oob_data(index2, &bdaddr1,
+						NULL, NULL, NULL, NULL);
+		else if (index == index2)
+			add_remote_oob_data(index1, &bdaddr2,
+						NULL, NULL, NULL, NULL);
+	}
+}
+
+static void clear_link_keys(uint16_t index)
+{
+	struct mgmt_cp_load_link_keys cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.debug_keys = 0x00;
+	cp.key_count = cpu_to_le16(0);
+
+	mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void clear_long_term_keys(uint16_t index)
+{
+	struct mgmt_cp_load_long_term_keys cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.key_count = cpu_to_le16(0);
+
+	mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void clear_identity_resolving_keys(uint16_t index)
+{
+	struct mgmt_cp_load_irks cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.irk_count = cpu_to_le16(0);
+
+	mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void clear_remote_oob_data(uint16_t index)
+{
+	struct mgmt_cp_remove_remote_oob_data cp;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.addr.bdaddr, BDADDR_ANY);
+	cp.addr.type = BDADDR_BREDR;
+
+	mgmt_send(mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA, index,
+					sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void set_powered_down_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Power down for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_bredr_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Setting BR/EDR for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_le_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Setting LE for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_ssp_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Simple Pairing for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_static_address_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Static address for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_secure_conn_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Secure connections for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Setting privacy for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_debug_keys_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Setting debug keys for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void set_bondable_complete(uint8_t status, uint16_t len,
+					const void *param, void *user_data)
+{
+	uint16_t index = PTR_TO_UINT(user_data);
+
+	if (status) {
+		fprintf(stderr, "Setting bondable for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+}
+
+static void read_info(uint8_t status, uint16_t len, const void *param,
+							void *user_data)
+{
+	const struct mgmt_rp_read_info *rp = param;
+	uint16_t index = PTR_TO_UINT(user_data);
+	uint32_t supported_settings;
+	uint8_t val;
+	char str[18];
+
+	if (status) {
+		fprintf(stderr, "Reading info for index %u failed: %s\n",
+						index, mgmt_errstr(status));
+		mainloop_quit();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, str);
+
+	printf("[Index %u]\n", index);
+	printf("  Address: %s\n", str);
+
+	if (index == index1)
+		bacpy(&bdaddr1, &rp->bdaddr);
+	else if (index == index2)
+		bacpy(&bdaddr2, &rp->bdaddr);
+
+	supported_settings = le32_to_cpu(rp->supported_settings);
+
+	if (use_bredr && !(supported_settings & MGMT_SETTING_BREDR)) {
+		fprintf(stderr, "BR/EDR support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (!use_legacy && !(supported_settings & MGMT_SETTING_SSP)) {
+		fprintf(stderr, "Secure Simple Pairing support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_le && !(supported_settings & MGMT_SETTING_LE)) {
+		fprintf(stderr, "Low Energy support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_sc && !(supported_settings & MGMT_SETTING_SECURE_CONN)) {
+		fprintf(stderr, "Secure Connections support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_sconly && !(supported_settings & MGMT_SETTING_SECURE_CONN)) {
+		fprintf(stderr, "Secure Connections Only support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_privacy && !(supported_settings & MGMT_SETTING_PRIVACY)) {
+		fprintf(stderr, "Privacy support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) {
+		fprintf(stderr, "Debug keys support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_cross && (!(supported_settings & MGMT_SETTING_BREDR) ||
+				!(supported_settings & MGMT_SETTING_LE))) {
+		fprintf(stderr, "Dual-mode support is support missing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (provide_tk) {
+		const uint8_t *tk = oob_tk;
+		int i;
+
+		printf("  TK Value: ");
+		for (i = 0; i < 16; i++)
+			printf("%02x", tk[i]);
+		printf("\n");
+	}
+
+	mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index,
+						pin_code_request_event,
+						UINT_TO_PTR(index), NULL);
+
+	mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index,
+						new_link_key_event,
+						UINT_TO_PTR(index), NULL);
+
+	mgmt_register(mgmt, MGMT_EV_NEW_LONG_TERM_KEY, index,
+						new_long_term_key_event,
+						UINT_TO_PTR(index), NULL);
+
+	val = 0x00;
+	mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
+						set_powered_down_complete,
+						UINT_TO_PTR(index), NULL);
+
+	clear_link_keys(index);
+	clear_long_term_keys(index);
+	clear_identity_resolving_keys(index);
+	clear_remote_oob_data(index);
+
+	if (use_bredr) {
+		val = 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
+						set_bredr_complete,
+						UINT_TO_PTR(index), NULL);
+
+		val = use_cross ? 0x01 : 0x00;
+		mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
+						set_le_complete,
+						UINT_TO_PTR(index), NULL);
+
+		val = use_legacy ? 0x00 : 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
+						set_ssp_complete,
+						UINT_TO_PTR(index), NULL);
+	} else if (use_le) {
+		val = 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
+						set_le_complete,
+						UINT_TO_PTR(index), NULL);
+
+		val = use_cross ? 0x01 : 0x00;
+		mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
+						set_bredr_complete,
+						UINT_TO_PTR(index), NULL);
+
+		if (use_cross) {
+			val = use_legacy ? 0x00 : 0x01;
+			mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
+						set_ssp_complete,
+						UINT_TO_PTR(index), NULL);
+		}
+	} else {
+		fprintf(stderr, "Invalid transport for pairing\n");
+		mainloop_quit();
+		return;
+	}
+
+	if (use_random) {
+		bdaddr_t bdaddr;
+
+		str2ba("c0:00:aa:bb:00:00", &bdaddr);
+		bdaddr.b[0] = index;
+
+		mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+						set_static_address_complete,
+						UINT_TO_PTR(index), NULL);
+
+		if (index == index1)
+			bacpy(&bdaddr1, &bdaddr);
+		else if (index == index2)
+			bacpy(&bdaddr2, &bdaddr);
+	} else {
+		bdaddr_t bdaddr;
+
+		bacpy(&bdaddr, BDADDR_ANY);
+
+		mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+						set_static_address_complete,
+						UINT_TO_PTR(index), NULL);
+	}
+
+	if (use_sc) {
+		val = 0x01;
+		mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+						set_secure_conn_complete,
+						UINT_TO_PTR(index), NULL);
+	} else if (use_sconly) {
+		val = 0x02;
+		mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+						set_secure_conn_complete,
+						UINT_TO_PTR(index), NULL);
+	} else {
+		val = 0x00;
+		mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+						set_secure_conn_complete,
+						UINT_TO_PTR(index), NULL);
+	}
+
+	if (use_privacy) {
+		struct mgmt_cp_set_privacy cp;
+
+		if (index == index2) {
+			cp.privacy = 0x01;
+			memcpy(cp.irk, REMOTE_IRK, sizeof(cp.irk));
+		} else {
+			cp.privacy = 0x00;
+			memset(cp.irk, 0, sizeof(cp.irk));
+		}
+
+		mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+						set_privacy_complete,
+						UINT_TO_PTR(index), NULL);
+	} else {
+		struct mgmt_cp_set_privacy cp;
+
+		cp.privacy = 0x00;
+		memset(cp.irk, 0, sizeof(cp.irk));
+
+		mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+						set_privacy_complete,
+						UINT_TO_PTR(index), NULL);
+	}
+
+	val = 0x00;
+	mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+						set_debug_keys_complete,
+						UINT_TO_PTR(index), NULL);
+
+	val = 0x01;
+	mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val,
+						set_bondable_complete,
+						UINT_TO_PTR(index), NULL);
+
+	val = 0x01;
+	mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
+						set_powered_complete,
+						UINT_TO_PTR(index), 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_quit();
+		return;
+	}
+
+	count = le16_to_cpu(rp->num_controllers);
+
+	if (count < 2) {
+		fprintf(stderr, "At least 2 controllers are required\n");
+		mainloop_quit();
+		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 initiator\n", index1);
+	printf("Selecting index %u for acceptor\n", index2);
+
+	if (provide_tk) {
+		struct bt_crypto *crypto;
+
+		printf("Generating Security Manager TK Value\n");
+
+		crypto = bt_crypto_new();
+		bt_crypto_random_bytes(crypto, oob_tk, 16);
+		bt_crypto_unref(crypto);
+	}
+
+	mgmt_send(mgmt, MGMT_OP_READ_INFO, index1, 0, NULL,
+				read_info, UINT_TO_PTR(index1), NULL);
+	mgmt_send(mgmt, MGMT_OP_READ_INFO, index2, 0, NULL,
+				read_info, UINT_TO_PTR(index2), NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+	switch (signum) {
+	case SIGINT:
+	case SIGTERM:
+		mainloop_quit();
+		break;
+	}
+}
+
+static void usage(void)
+{
+	printf("oobtest - Out-of-band pairing testing\n"
+		"Usage:\n");
+	printf("\toobtest [options]\n");
+	printf("options:\n"
+		"\t-B, --bredr            Use BR/EDR transport\n"
+		"\t-L, --le               Use LE transport\n"
+		"\t-S, --sc               Use Secure Connections\n"
+		"\t-O, --sconly           Use Secure Connections Only\n"
+		"\t-P, --legacy           Use Legacy Pairing\n"
+		"\t-R, --random           Use Static random address\n"
+		"\t-Y, --privacy          Use LE privacy feature\n"
+		"\t-D, --debug            Use Pairing debug keys\n"
+		"\t-C, --cross            Use cross-transport pairing\n"
+		"\t-0, --tk               Provide LE legacy OOB data\n"
+		"\t-1, --p192             Provide P-192 OOB data\n"
+		"\t-2, --p256             Provide P-256 OOB data\n"
+		"\t-I, --initiator        Initiator provides OOB data\n"
+		"\t-A, --acceptor         Acceptor provides OOB data\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "bredr",     no_argument,       NULL, 'B' },
+	{ "le",        no_argument,       NULL, 'L' },
+	{ "sc",        no_argument,       NULL, 'S' },
+	{ "sconly",    no_argument,       NULL, 'O' },
+	{ "legacy",    no_argument,       NULL, 'P' },
+	{ "random",    no_argument,       NULL, 'R' },
+	{ "static",    no_argument,       NULL, 'R' },
+	{ "privacy",   no_argument,       NULL, 'Y' },
+	{ "debug",     no_argument,       NULL, 'D' },
+	{ "cross",     no_argument,       NULL, 'C' },
+	{ "dual",      no_argument,       NULL, 'C' },
+	{ "tk",        no_argument,       NULL, '0' },
+	{ "p192",      no_argument,       NULL, '1' },
+	{ "p256",      no_argument,       NULL, '2' },
+	{ "initiator", no_argument,       NULL, 'I' },
+	{ "acceptor",  no_argument,       NULL, 'A' },
+	{ "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, "BLSOPRYDC012IAvh",
+						main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'B':
+			use_bredr = true;
+			break;
+		case 'L':
+			use_le = true;
+			break;
+		case 'S':
+			use_sc = true;
+			break;
+		case 'O':
+			use_sconly = true;
+			break;
+		case 'P':
+			use_legacy = true;
+			break;
+		case 'R':
+			use_random = true;
+			break;
+		case 'Y':
+			use_privacy = true;
+			break;
+		case 'D':
+			use_debug = true;
+			break;
+		case 'C':
+			use_cross = true;
+			break;
+		case '0':
+			provide_tk = true;
+			break;
+		case '1':
+			provide_p192 = true;
+			break;
+		case '2':
+			provide_p256 = true;
+			break;
+		case 'I':
+			provide_initiator = true;
+			break;
+		case 'A':
+			provide_acceptor = true;
+			break;
+		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;
+	}
+
+	if (use_bredr == use_le) {
+		fprintf(stderr, "Specify either --bredr or --le\n");
+		return EXIT_FAILURE;
+	}
+
+	if (use_legacy && !use_bredr) {
+		fprintf(stderr, "Specify --legacy with --bredr\n");
+		return EXIT_FAILURE;
+	}
+
+	if (use_privacy && !use_le && !use_cross ) {
+		fprintf(stderr, "Specify --privacy with --le or --cross\n");
+		return EXIT_FAILURE;
+	}
+
+	if (use_random && !use_le) {
+		fprintf(stderr, "Specify --random with --le\n");
+		return EXIT_FAILURE;
+	}
+
+	if (use_random && use_cross) {
+		fprintf(stderr, "Only --random or --cross can be used\n");
+		return EXIT_FAILURE;
+	}
+
+	if (use_sc && use_sconly) {
+		fprintf(stderr, "Only --sc or --sconly can be used\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();
+
+done:
+	mgmt_unref(mgmt);
+
+	return exit_status;
+}
diff --git a/bluez/repo/tools/parse_companies.pl b/bluez/repo/tools/parse_companies.pl
new file mode 100755
index 0000000..6dc358e
--- /dev/null
+++ b/bluez/repo/tools/parse_companies.pl
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+# parse companies from
+# https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
+
+use strict;
+# use URI::Encode qw(uri_decode);
+
+my %known_entities = (
+    'nbsp' => ' ',
+    'eacute' => 'é',
+    'auml' => 'ä',
+);
+
+# better to use URI::Encode if you have it
+sub uri_decode {
+    my $name = $_[0];
+    foreach my $entity (keys %known_entities) {
+        my $to = $known_entities{$entity};
+        $name =~ s/&$entity;/$to/g;
+    }
+    foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
+        if ($entity ne 'amp') {
+            print "Unable to convert &$entity;, giving up\n";
+            exit 1;
+        }
+    }
+    $name =~ s/&amp;/&/ig;
+    $name =~ s/&nbsp;/ /ig;
+    return $name;
+}
+
+# never parse HTML with regex!
+# except when you should
+
+my $identifier;
+my $next_is_name = 0;
+
+while (<>) {
+    s/\xe2\x80\x8b//g; # kill zero width space
+
+    # grab identifier (in hex)
+    if (/\<td.*(0x[0-9A-F]{4})/i) {
+        $identifier = $1;
+        $next_is_name = 1;
+
+    # next <td> should be company name
+    } elsif ($next_is_name && m|\<td.*\>(.*)\</td\>|) {
+        my $name = uri_decode($1);
+        $name =~ s/^\s+//g; # kill leading
+        $name =~ s/\s+$//g; # and trailing space
+        my $id = hex($identifier);
+        if ($id != 65535) {
+            print "\tcase $id:\n";
+            print "\t\treturn \"$name\";\n";
+        }
+        $next_is_name = 0;
+    }
+}
diff --git a/bluez/repo/tools/parser/._amp.c b/bluez/repo/tools/parser/._amp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._amp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._att.c b/bluez/repo/tools/parser/._att.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._att.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._avctp.c b/bluez/repo/tools/parser/._avctp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._avctp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._avdtp.c b/bluez/repo/tools/parser/._avdtp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._avdtp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._avrcp.c b/bluez/repo/tools/parser/._avrcp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._avrcp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._bnep.c b/bluez/repo/tools/parser/._bnep.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._bnep.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._bpa.c b/bluez/repo/tools/parser/._bpa.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._bpa.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._capi.c b/bluez/repo/tools/parser/._capi.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._capi.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._cmtp.c b/bluez/repo/tools/parser/._cmtp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._cmtp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._csr.c b/bluez/repo/tools/parser/._csr.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._csr.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._ericsson.c b/bluez/repo/tools/parser/._ericsson.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._ericsson.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._hci.c b/bluez/repo/tools/parser/._hci.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._hci.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._hcrp.c b/bluez/repo/tools/parser/._hcrp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._hcrp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._hidp.c b/bluez/repo/tools/parser/._hidp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._hidp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._l2cap.c b/bluez/repo/tools/parser/._l2cap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._l2cap.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._l2cap.h b/bluez/repo/tools/parser/._l2cap.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._l2cap.h
Binary files differ
diff --git a/bluez/repo/tools/parser/._lmp.c b/bluez/repo/tools/parser/._lmp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._lmp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._obex.c b/bluez/repo/tools/parser/._obex.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._obex.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._parser.c b/bluez/repo/tools/parser/._parser.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._parser.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._parser.h b/bluez/repo/tools/parser/._parser.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._parser.h
Binary files differ
diff --git a/bluez/repo/tools/parser/._ppp.c b/bluez/repo/tools/parser/._ppp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._ppp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._rfcomm.c b/bluez/repo/tools/parser/._rfcomm.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._rfcomm.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._rfcomm.h b/bluez/repo/tools/parser/._rfcomm.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._rfcomm.h
Binary files differ
diff --git a/bluez/repo/tools/parser/._sap.c b/bluez/repo/tools/parser/._sap.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._sap.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._sdp.c b/bluez/repo/tools/parser/._sdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._sdp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._sdp.h b/bluez/repo/tools/parser/._sdp.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._sdp.h
Binary files differ
diff --git a/bluez/repo/tools/parser/._smp.c b/bluez/repo/tools/parser/._smp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._smp.c
Binary files differ
diff --git a/bluez/repo/tools/parser/._tcpip.c b/bluez/repo/tools/parser/._tcpip.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/tools/parser/._tcpip.c
Binary files differ
diff --git a/bluez/repo/tools/parser/amp.c b/bluez/repo/tools/parser/amp.c
new file mode 100644
index 0000000..158ca4a
--- /dev/null
+++ b/bluez/repo/tools/parser/amp.c
@@ -0,0 +1,126 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation.
+ *
+ *  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 "parser.h"
+#include "lib/amp.h"
+
+static void amp_dump_chanlist(int level, struct amp_tlv *tlv, char *prefix)
+{
+	struct amp_chan_list *chan_list = (void *) tlv->val;
+	struct amp_country_triplet *triplet;
+	int i, num;
+
+	num = (tlv->len - sizeof(*chan_list)) / sizeof(*triplet);
+
+	printf("%s (number of triplets %d)\n", prefix, num);
+
+	p_indent(level+2, 0);
+
+	printf("Country code: %c%c%c\n", chan_list->country_code[0],
+		chan_list->country_code[1], chan_list->country_code[2]);
+
+	for (i = 0; i < num; i++) {
+		triplet = &chan_list->triplets[i];
+
+		p_indent(level+2, 0);
+
+		if (triplet->chans.first_channel >= 201) {
+			printf("Reg ext id %d reg class %d coverage class %d\n",
+						triplet->ext.reg_extension_id,
+						triplet->ext.reg_class,
+						triplet->ext.coverage_class);
+		} else {
+			if (triplet->chans.num_channels == 1)
+				printf("Channel %d max power %d\n",
+						triplet->chans.first_channel,
+						triplet->chans.max_power);
+			else
+				printf("Channels %d - %d max power %d\n",
+						triplet->chans.first_channel,
+						triplet->chans.first_channel +
+						triplet->chans.num_channels,
+						triplet->chans.max_power);
+		}
+	}
+}
+
+void amp_assoc_dump(int level, uint8_t *assoc, uint16_t len)
+{
+	struct amp_tlv *tlv = (void *) assoc;
+
+	p_indent(level, 0);
+	printf("Assoc data [len %d]:\n", len);
+
+	while (len > sizeof(*tlv)) {
+		uint16_t tlvlen = btohs(tlv->len);
+		struct amp_pal_ver *ver;
+
+		p_indent(level+1, 0);
+
+		switch (tlv->type) {
+		case A2MP_MAC_ADDR_TYPE:
+			if (tlvlen != 6)
+				break;
+			printf("MAC: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+					tlv->val[0], tlv->val[1], tlv->val[2],
+					tlv->val[3], tlv->val[4], tlv->val[5]);
+			break;
+
+		case A2MP_PREF_CHANLIST_TYPE:
+			amp_dump_chanlist(level, tlv, "Preferred Chan List");
+			break;
+
+		case A2MP_CONNECTED_CHAN:
+			amp_dump_chanlist(level, tlv, "Connected Chan List");
+			break;
+
+		case A2MP_PAL_CAP_TYPE:
+			if (tlvlen != 4)
+				break;
+			printf("PAL CAP: %2.2x %2.2x %2.2x %2.2x\n",
+					tlv->val[0], tlv->val[1], tlv->val[2],
+					tlv->val[3]);
+			break;
+
+		case A2MP_PAL_VER_INFO:
+			if (tlvlen != 5)
+				break;
+			ver = (struct amp_pal_ver *) tlv->val;
+			printf("PAL VER: %2.2x Comp ID: %4.4x SubVer: %4.4x\n",
+					ver->ver, btohs(ver->company_id),
+					btohs(ver->sub_ver));
+			break;
+
+		default:
+			printf("Unrecognized type %d\n", tlv->type);
+			break;
+		}
+
+		len -= tlvlen + sizeof(*tlv);
+		assoc += tlvlen + sizeof(*tlv);
+		tlv = (struct amp_tlv *) assoc;
+	}
+}
diff --git a/bluez/repo/tools/parser/att.c b/bluez/repo/tools/parser/att.c
new file mode 100644
index 0000000..82766c7
--- /dev/null
+++ b/bluez/repo/tools/parser/att.c
@@ -0,0 +1,636 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  André Dieb Martins <andre.dieb@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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define GATT_PRIM_SVC_UUID		0x2800
+#define GATT_SND_SVC_UUID		0x2801
+#define GATT_INCLUDE_UUID		0x2802
+#define GATT_CHARAC_UUID		0x2803
+
+#define GATT_CHARAC_DEVICE_NAME			0x2A00
+#define GATT_CHARAC_APPEARANCE			0x2A01
+#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG	0x2A02
+#define GATT_CHARAC_RECONNECTION_ADDRESS	0x2A03
+#define GATT_CHARAC_PERIPHERAL_PREF_CONN	0x2A04
+#define GATT_CHARAC_SERVICE_CHANGED		0x2A05
+
+#define GATT_CHARAC_EXT_PROPER_UUID	0x2900
+#define GATT_CHARAC_USER_DESC_UUID	0x2901
+#define GATT_CLIENT_CHARAC_CFG_UUID	0x2902
+#define GATT_SERVER_CHARAC_CFG_UUID	0x2903
+#define GATT_CHARAC_FMT_UUID		0x2904
+#define GATT_CHARAC_AGREG_FMT_UUID	0x2905
+
+
+
+/* Attribute Protocol Opcodes */
+#define ATT_OP_ERROR			0x01
+#define ATT_OP_MTU_REQ			0x02
+#define ATT_OP_MTU_RESP			0x03
+#define ATT_OP_FIND_INFO_REQ		0x04
+#define ATT_OP_FIND_INFO_RESP		0x05
+#define ATT_OP_FIND_BY_TYPE_REQ		0x06
+#define ATT_OP_FIND_BY_TYPE_RESP	0x07
+#define ATT_OP_READ_BY_TYPE_REQ		0x08
+#define ATT_OP_READ_BY_TYPE_RESP	0x09
+#define ATT_OP_READ_REQ			0x0A
+#define ATT_OP_READ_RESP		0x0B
+#define ATT_OP_READ_BLOB_REQ		0x0C
+#define ATT_OP_READ_BLOB_RESP		0x0D
+#define ATT_OP_READ_MULTI_REQ		0x0E
+#define ATT_OP_READ_MULTI_RESP		0x0F
+#define ATT_OP_READ_BY_GROUP_REQ	0x10
+#define ATT_OP_READ_BY_GROUP_RESP	0x11
+#define ATT_OP_WRITE_REQ		0x12
+#define ATT_OP_WRITE_RESP		0x13
+#define ATT_OP_WRITE_CMD		0x52
+#define ATT_OP_PREP_WRITE_REQ		0x16
+#define ATT_OP_PREP_WRITE_RESP		0x17
+#define ATT_OP_EXEC_WRITE_REQ		0x18
+#define ATT_OP_EXEC_WRITE_RESP		0x19
+#define ATT_OP_HANDLE_NOTIFY		0x1B
+#define ATT_OP_HANDLE_IND		0x1D
+#define ATT_OP_HANDLE_CNF		0x1E
+#define ATT_OP_SIGNED_WRITE_CMD		0xD2
+
+/* Error codes for Error response PDU */
+#define ATT_ECODE_INVALID_HANDLE		0x01
+#define ATT_ECODE_READ_NOT_PERM			0x02
+#define ATT_ECODE_WRITE_NOT_PERM		0x03
+#define ATT_ECODE_INVALID_PDU			0x04
+#define ATT_ECODE_INSUFF_AUTHEN			0x05
+#define ATT_ECODE_REQ_NOT_SUPP			0x06
+#define ATT_ECODE_INVALID_OFFSET		0x07
+#define ATT_ECODE_INSUFF_AUTHO			0x08
+#define ATT_ECODE_PREP_QUEUE_FULL		0x09
+#define ATT_ECODE_ATTR_NOT_FOUND		0x0A
+#define ATT_ECODE_ATTR_NOT_LONG			0x0B
+#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE		0x0C
+#define ATT_ECODE_INVAL_ATTR_VALUE_LEN		0x0D
+#define ATT_ECODE_UNLIKELY			0x0E
+#define ATT_ECODE_INSUFF_ENC			0x0F
+#define ATT_ECODE_UNSUPP_GRP_TYPE		0x10
+#define ATT_ECODE_INSUFF_RESOURCES		0x11
+#define ATT_ECODE_IO				0xFF
+
+
+/* Attribute Protocol Opcodes */
+static const char *attop2str(uint8_t op)
+{
+	switch (op) {
+	case ATT_OP_ERROR:
+		return "Error";
+	case ATT_OP_MTU_REQ:
+		return "MTU req";
+	case ATT_OP_MTU_RESP:
+		return "MTU resp";
+	case ATT_OP_FIND_INFO_REQ:
+		return "Find Information req";
+	case ATT_OP_FIND_INFO_RESP:
+		return "Find Information resp";
+	case ATT_OP_FIND_BY_TYPE_REQ:
+		return "Find By Type req";
+	case ATT_OP_FIND_BY_TYPE_RESP:
+		return "Find By Type resp";
+	case ATT_OP_READ_BY_TYPE_REQ:
+		return "Read By Type req";
+	case ATT_OP_READ_BY_TYPE_RESP:
+		return "Read By Type resp";
+	case ATT_OP_READ_REQ:
+		return "Read req";
+	case ATT_OP_READ_RESP:
+		return "Read resp";
+	case ATT_OP_READ_BLOB_REQ:
+		return "Read Blob req";
+	case ATT_OP_READ_BLOB_RESP:
+		return "Read Blob resp";
+	case ATT_OP_READ_MULTI_REQ:
+		return "Read Multi req";
+	case ATT_OP_READ_MULTI_RESP:
+		return "Read Multi resp";
+	case ATT_OP_READ_BY_GROUP_REQ:
+		return "Read By Group req";
+	case ATT_OP_READ_BY_GROUP_RESP:
+		return "Read By Group resp";
+	case ATT_OP_WRITE_REQ:
+		return "Write req";
+	case ATT_OP_WRITE_RESP:
+		return "Write resp";
+	case ATT_OP_WRITE_CMD:
+		return "Write cmd";
+	case ATT_OP_PREP_WRITE_REQ:
+		return "Prepare Write req";
+	case ATT_OP_PREP_WRITE_RESP:
+		return "Prepare Write resp";
+	case ATT_OP_EXEC_WRITE_REQ:
+		return "Exec Write req";
+	case ATT_OP_EXEC_WRITE_RESP:
+		return "Exec Write resp";
+	case ATT_OP_HANDLE_NOTIFY:
+		return "Handle notify";
+	case ATT_OP_HANDLE_IND:
+		return "Handle indicate";
+	case ATT_OP_HANDLE_CNF:
+		return "Handle CNF";
+	case ATT_OP_SIGNED_WRITE_CMD:
+		return "Signed Write Cmd";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char * atterror2str(uint8_t err)
+{
+	switch (err) {
+	case ATT_ECODE_INVALID_HANDLE:
+		return "Invalid handle";
+	case ATT_ECODE_READ_NOT_PERM:
+		return "Read not permitted";
+	case ATT_ECODE_WRITE_NOT_PERM:
+		return "Write not permitted";
+	case ATT_ECODE_INVALID_PDU:
+		return "Invalid PDU";
+	case ATT_ECODE_INSUFF_AUTHEN:
+		return "Insufficient authentication";
+	case ATT_ECODE_REQ_NOT_SUPP:
+		return "Request not supported";
+	case ATT_ECODE_INVALID_OFFSET:
+		return "Invalid offset";
+	case ATT_ECODE_INSUFF_AUTHO:
+		return "Insufficient authorization";
+	case ATT_ECODE_PREP_QUEUE_FULL:
+		return "Prepare queue full";
+	case ATT_ECODE_ATTR_NOT_FOUND:
+		return "Attribute not found";
+	case ATT_ECODE_ATTR_NOT_LONG:
+		return "Attribute not long";
+	case ATT_ECODE_INSUFF_ENCR_KEY_SIZE:
+		return "Insufficient encryption key size";
+	case ATT_ECODE_INVAL_ATTR_VALUE_LEN:
+		return "Invalid attribute value length";
+	case ATT_ECODE_UNLIKELY:
+		return "Unlikely error";
+	case ATT_ECODE_INSUFF_ENC:
+		return "Insufficient encryption";
+	case ATT_ECODE_UNSUPP_GRP_TYPE:
+		return "Unsupported group type";
+	case ATT_ECODE_INSUFF_RESOURCES:
+		return "Insufficient resources";
+	case ATT_ECODE_IO:
+		return "Application Error";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *uuid2str(uint16_t uuid)
+{
+	switch (uuid) {
+	case GATT_PRIM_SVC_UUID:
+		return "GATT Primary Service";
+	case GATT_SND_SVC_UUID:
+		return "GATT Secondary Service";
+	case GATT_INCLUDE_UUID:
+		return "GATT Include";
+	case GATT_CHARAC_UUID:
+		return "GATT Characteristic";
+	case GATT_CHARAC_DEVICE_NAME:
+		return "GATT(type) Device Name";
+	case GATT_CHARAC_APPEARANCE:
+		return "GATT(type) Appearance";
+	case GATT_CHARAC_PERIPHERAL_PRIV_FLAG:
+		return "GATT(type) Peripheral Privacy Flag";
+	case GATT_CHARAC_RECONNECTION_ADDRESS:
+		return "GATT(type) Characteristic Reconnection Address";
+	case GATT_CHARAC_PERIPHERAL_PREF_CONN:
+		return "GATT(type) Characteristic Preferred Connection Parameters";
+	case GATT_CHARAC_SERVICE_CHANGED:
+		return "GATT(type) Characteristic Service Changed";
+	case GATT_CHARAC_EXT_PROPER_UUID:
+		return "GATT(desc) Characteristic Extended Properties";
+	case GATT_CHARAC_USER_DESC_UUID:
+		return "GATT(desc) User Description";
+	case GATT_CLIENT_CHARAC_CFG_UUID:
+		return "GATT(desc) Client Characteristic Configuration";
+	case GATT_SERVER_CHARAC_CFG_UUID:
+		return "GATT(desc) Server Characteristic Configuration";
+	case GATT_CHARAC_FMT_UUID:
+		return "GATT(desc) Format";
+	case GATT_CHARAC_AGREG_FMT_UUID:
+		return "GATT(desc) Aggregate Format";
+	default:
+		return "Unknown";
+	}
+}
+
+static void att_error_dump(int level, struct frame *frm)
+{
+	uint8_t op = p_get_u8(frm);
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+	uint8_t err = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("Error: %s (%d)\n", atterror2str(err), err);
+
+	p_indent(level, frm);
+	printf("%s (0x%.2x) on handle 0x%4.4x\n", attop2str(op), op, handle);
+}
+
+static void att_mtu_req_dump(int level, struct frame *frm)
+{
+	uint16_t client_rx_mtu = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("client rx mtu %d\n", client_rx_mtu);
+}
+
+static void att_mtu_resp_dump(int level, struct frame *frm)
+{
+	uint16_t server_rx_mtu = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("server rx mtu %d\n", server_rx_mtu);
+}
+
+static void att_find_info_req_dump(int level, struct frame *frm)
+{
+	uint16_t start = btohs(htons(p_get_u16(frm)));
+	uint16_t end = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("start 0x%4.4x, end 0x%4.4x\n", start, end);
+}
+
+static void print_uuid128(struct frame *frm)
+{
+	uint8_t uuid[16];
+	int i;
+
+	for (i = 0; i < 16; i++)
+		uuid[15 - i] = p_get_u8(frm);
+
+	for (i = 0; i < 16; i++) {
+		printf("%02x", uuid[i]);
+		if (i == 3 || i == 5 || i == 7 || i == 9)
+			printf("-");
+	}
+}
+
+static void att_find_info_resp_dump(int level, struct frame *frm)
+{
+	uint8_t fmt = p_get_u8(frm);
+
+	p_indent(level, frm);
+
+	if (fmt == 0x01) {
+		printf("format: uuid-16\n");
+
+		while (frm->len > 0) {
+			uint16_t handle = btohs(htons(p_get_u16(frm)));
+			uint16_t uuid = btohs(htons(p_get_u16(frm)));
+			p_indent(level + 1, frm);
+			printf("handle 0x%4.4x, uuid 0x%4.4x (%s)\n", handle, uuid,
+					uuid2str(uuid));
+		}
+	} else {
+		printf("format: uuid-128\n");
+
+		while (frm->len > 0) {
+			uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+			p_indent(level + 1, frm);
+			printf("handle 0x%4.4x, uuid ", handle);
+			print_uuid128(frm);
+			printf("\n");
+		}
+	}
+}
+
+static void att_find_by_type_req_dump(int level, struct frame *frm)
+{
+	uint16_t start = btohs(htons(p_get_u16(frm)));
+	uint16_t end = btohs(htons(p_get_u16(frm)));
+	uint16_t uuid = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("start 0x%4.4x, end 0x%4.4x, uuid 0x%4.4x\n", start, end, uuid);
+
+	p_indent(level, frm);
+	printf("value");
+	while (frm->len > 0)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void att_find_by_type_resp_dump(int level, struct frame *frm)
+{
+	while (frm->len > 0) {
+		uint16_t uuid = btohs(htons(p_get_u16(frm)));
+		uint16_t end = btohs(htons(p_get_u16(frm)));
+
+		p_indent(level, frm);
+		printf("Found attr 0x%4.4x, group end handle 0x%4.4x\n",
+								uuid, end);
+	}
+}
+
+static void att_read_by_type_req_dump(int level, struct frame *frm)
+{
+	uint16_t start = btohs(htons(p_get_u16(frm)));
+	uint16_t end = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("start 0x%4.4x, end 0x%4.4x\n", start, end);
+
+	p_indent(level, frm);
+	if (frm->len == 2) {
+		printf("type-uuid 0x%4.4x\n", btohs(htons(p_get_u16(frm))));
+	} else if (frm->len == 16) {
+		printf("type-uuid ");
+		print_uuid128(frm);
+		printf("\n");
+	} else {
+		printf("malformed uuid (expected 2 or 16 octets)\n");
+		p_indent(level, frm);
+		raw_dump(level, frm);
+	}
+}
+
+static void att_read_by_type_resp_dump(int level, struct frame *frm)
+{
+	uint8_t length = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("length: %d\n", length);
+
+	while (frm->len > 0) {
+		uint16_t handle = btohs(htons(p_get_u16(frm)));
+		int val_len = length - 2;
+		int i;
+
+		p_indent(level + 1, frm);
+		printf("handle 0x%4.4x, value ", handle);
+		for (i = 0; i < val_len; i++) {
+			printf("0x%.2x ", p_get_u8(frm));
+		}
+		printf("\n");
+	}
+}
+
+static void att_read_req_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle 0x%4.4x\n", handle);
+}
+
+static void att_read_blob_req_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+	uint16_t offset = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle 0x%4.4x offset 0x%4.4x\n", handle, offset);
+}
+
+static void att_read_blob_resp_dump(int level, struct frame *frm)
+{
+	p_indent(level, frm);
+	printf("value");
+
+	while (frm->len > 0)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void att_read_multi_req_dump(int level, struct frame *frm)
+{
+	p_indent(level, frm);
+	printf("Handles\n");
+
+	while (frm->len > 0) {
+		p_indent(level, frm);
+		printf("handle 0x%4.4x\n", btohs(htons(p_get_u16(frm))));
+	}
+}
+
+static void att_read_multi_resp_dump(int level, struct frame *frm)
+{
+	p_indent(level, frm);
+	printf("values");
+
+	while (frm->len > 0)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void att_read_by_group_resp_dump(int level, struct frame *frm)
+{
+	uint8_t length = p_get_u8(frm);
+
+	while (frm->len > 0) {
+		uint16_t attr_handle = btohs(htons(p_get_u16(frm)));
+		uint16_t end_grp_handle = btohs(htons(p_get_u16(frm)));
+		uint8_t remaining = length - 4;
+
+		p_indent(level, frm);
+		printf("attr handle 0x%4.4x, end group handle 0x%4.4x\n",
+						attr_handle, end_grp_handle);
+
+		p_indent(level, frm);
+		printf("value");
+		while (remaining > 0) {
+			printf(" 0x%2.2x", p_get_u8(frm));
+			remaining--;
+		}
+		printf("\n");
+	}
+}
+
+static void att_write_req_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle 0x%4.4x value ", handle);
+
+	while (frm->len > 0)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void att_signed_write_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+	int value_len = frm->len - 12; /* handle:2 already accounted, sig: 12 */
+
+	p_indent(level, frm);
+	printf("handle 0x%4.4x value ", handle);
+
+	while (value_len--)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+
+	p_indent(level, frm);
+	printf("auth signature ");
+	while (frm->len > 0)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void att_prep_write_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+	uint16_t val_offset = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("attr handle 0x%4.4x, value offset 0x%4.4x\n", handle,
+								val_offset);
+
+	p_indent(level, frm);
+	printf("part attr value ");
+	while (frm->len > 0)
+		printf(" 0x%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void att_exec_write_req_dump(int level, struct frame *frm)
+{
+	uint8_t flags = p_get_u8(frm);
+
+	p_indent(level, frm);
+	if (flags == 0x00)
+		printf("cancel all prepared writes ");
+	else
+		printf("immediatelly write all pending prepared values ");
+
+	printf("(0x%2.2x)\n", flags);
+}
+
+static void att_handle_notify_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle 0x%4.4x\n", handle);
+
+	p_indent(level, frm);
+	printf("value ");
+	while (frm->len > 0)
+		printf("0x%.2x ", p_get_u8(frm));
+	printf("\n");
+}
+
+void att_dump(int level, struct frame *frm)
+{
+	uint8_t op;
+
+	op = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("ATT: %s (0x%.2x)\n", attop2str(op), op);
+
+	switch (op) {
+		case ATT_OP_ERROR:
+			att_error_dump(level + 1, frm);
+			break;
+		case ATT_OP_MTU_REQ:
+			att_mtu_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_MTU_RESP:
+			att_mtu_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_FIND_INFO_REQ:
+			att_find_info_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_FIND_INFO_RESP:
+			att_find_info_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_FIND_BY_TYPE_REQ:
+			att_find_by_type_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_FIND_BY_TYPE_RESP:
+			att_find_by_type_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_BY_TYPE_REQ:
+		case ATT_OP_READ_BY_GROUP_REQ: /* exact same parsing */
+			att_read_by_type_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_BY_TYPE_RESP:
+			att_read_by_type_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_REQ:
+			att_read_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_RESP:
+			raw_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_BLOB_REQ:
+			att_read_blob_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_BLOB_RESP:
+			att_read_blob_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_MULTI_REQ:
+			att_read_multi_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_MULTI_RESP:
+			att_read_multi_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_READ_BY_GROUP_RESP:
+			att_read_by_group_resp_dump(level + 1, frm);
+			break;
+		case ATT_OP_WRITE_REQ:
+		case ATT_OP_WRITE_CMD:
+			att_write_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_SIGNED_WRITE_CMD:
+			att_signed_write_dump(level + 1, frm);
+			break;
+		case ATT_OP_PREP_WRITE_REQ:
+		case ATT_OP_PREP_WRITE_RESP:
+			att_prep_write_dump(level + 1, frm);
+			break;
+		case ATT_OP_EXEC_WRITE_REQ:
+			att_exec_write_req_dump(level + 1, frm);
+			break;
+		case ATT_OP_HANDLE_NOTIFY:
+			att_handle_notify_dump(level + 1, frm);
+			break;
+		default:
+			raw_dump(level, frm);
+			break;
+	}
+}
diff --git a/bluez/repo/tools/parser/avctp.c b/bluez/repo/tools/parser/avctp.c
new file mode 100644
index 0000000..70c5f66
--- /dev/null
+++ b/bluez/repo/tools/parser/avctp.c
@@ -0,0 +1,72 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+#include "sdp.h"
+
+static char *pt2str(uint8_t hdr)
+{
+	switch (hdr & 0x0c) {
+	case 0x00:
+		return "";
+	case 0x04:
+		return "Start";
+	case 0x08:
+		return "Cont";
+	case 0x0c:
+		return "End";
+	default:
+		return "Unk";
+	}
+}
+
+void avctp_dump(int level, struct frame *frm, uint16_t psm)
+{
+	uint8_t hdr;
+	uint16_t pid;
+
+	p_indent(level, frm);
+
+	hdr = p_get_u8(frm);
+	pid = p_get_u16(frm);
+
+	printf("AVCTP %s: %s %s: pt 0x%02x transaction %d pid 0x%04x\n",
+				psm == 23 ? "Control" : "Browsing",
+				hdr & 0x02 ? "Response" : "Command",
+				pt2str(hdr), hdr & 0x0c, hdr >> 4, pid);
+
+	if (pid == SDP_UUID_AV_REMOTE || pid == SDP_UUID_AV_REMOTE_TARGET)
+		avrcp_dump(level + 1, frm, hdr, psm);
+	else
+		raw_dump(level + 1, frm);
+}
diff --git a/bluez/repo/tools/parser/avdtp.c b/bluez/repo/tools/parser/avdtp.c
new file mode 100644
index 0000000..4f53553
--- /dev/null
+++ b/bluez/repo/tools/parser/avdtp.c
@@ -0,0 +1,679 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+static char *si2str(uint8_t si)
+{
+	switch (si & 0x7f) {
+	case 0x01:
+		return "Discover";
+	case 0x02:
+		return "Capabilities";
+	case 0x03:
+		return "Set config";
+	case 0x04:
+		return "Get config";
+	case 0x05:
+		return "Reconfigure";
+	case 0x06:
+		return "Open";
+	case 0x07:
+		return "Start";
+	case 0x08:
+		return "Close";
+	case 0x09:
+		return "Suspend";
+	case 0x0a:
+		return "Abort";
+	case 0x0b:
+		return "Security";
+	case 0x0c:
+		return "All Capabilities";
+	case 0x0d:
+		return "Delay Report";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *pt2str(uint8_t hdr)
+{
+	switch (hdr & 0x0c) {
+	case 0x00:
+		return "Single";
+	case 0x04:
+		return "Start";
+	case 0x08:
+		return "Cont";
+	case 0x0c:
+		return "End";
+	default:
+		return "Unk";
+	}
+}
+
+static char *mt2str(uint8_t hdr)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		return "cmd";
+	case 0x02:
+		return "rsp";
+	case 0x03:
+		return "rej";
+	default:
+		return "rfd";
+	}
+}
+
+static char *media2str(uint8_t type)
+{
+	switch (type) {
+	case 0:
+		return "Audio";
+	case 1:
+		return "Video";
+	case 2:
+		return "Multimedia";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *codec2str(uint8_t type, uint8_t codec)
+{
+	switch (type) {
+	case 0:
+		switch (codec) {
+		case 0:
+			return "SBC";
+		case 1:
+			return "MPEG-1,2 Audio";
+		case 2:
+			return "MPEG-2,4 AAC";
+		case 4:
+			return "ATRAC family";
+		case 255:
+			return "non-A2DP";
+		default:
+			return "Reserved";
+		}
+		break;
+	case 1:
+		switch (codec) {
+		case 1:
+			return "H.263 baseline";
+		case 2:
+			return "MPEG-4 Visual Simple Profile";
+		case 3:
+			return "H.263 profile 3";
+		case 4:
+			return "H.263 profile 8";
+		case 255:
+			return "Non-VDP";
+		default:
+			return "Reserved";
+		}
+		break;
+	}
+	return "Unknown";
+}
+
+static char *vndcodec2str(uint32_t vendor, uint16_t vndcodec)
+{
+	if (vendor == 0x0000004f && vndcodec == 0x0001)
+		return "aptX";
+	else if (vendor == 0x0000012d && vndcodec == 0x00aa)
+		return "LDAC";
+	return "Unknown";
+}
+
+static char *cat2str(uint8_t cat)
+{
+	switch (cat) {
+	case 1:
+		return "Media Transport";
+	case 2:
+		return "Reporting";
+	case 3:
+		return "Recovery";
+	case 4:
+		return "Content Protection";
+	case 5:
+		return "Header Compression";
+	case 6:
+		return "Multiplexing";
+	case 7:
+		return "Media Codec";
+	case 8:
+		return "Delay Reporting";
+	default:
+		return "Reserved";
+	}
+}
+
+static void errorcode(int level, struct frame *frm)
+{
+	uint8_t code;
+
+	p_indent(level, frm);
+	code = p_get_u8(frm);
+	printf("Error code %d\n", code);
+}
+
+static void acp_seid(int level, struct frame *frm)
+{
+	uint8_t seid;
+
+	p_indent(level, frm);
+	seid = p_get_u8(frm);
+	printf("ACP SEID %d\n", seid >> 2);
+}
+
+static void acp_int_seid(int level, struct frame *frm)
+{
+	uint8_t acp_seid, int_seid;
+
+	p_indent(level, frm);
+	acp_seid = p_get_u8(frm);
+	int_seid = p_get_u8(frm);
+	printf("ACP SEID %d - INT SEID %d\n", acp_seid >> 2, int_seid >> 2);
+}
+
+static void capabilities(int level, struct frame *frm)
+{
+	uint8_t cat, len;
+
+	while (frm->len > 1) {
+		p_indent(level, frm);
+		cat = p_get_u8(frm);
+		len = p_get_u8(frm);
+
+		if (cat == 7) {
+			uint8_t type, codec;
+			uint16_t tmp, freq, vndcodec = 0;
+			uint32_t bitrate, vendor = 0;
+			int i;
+
+			type  = p_get_u8(frm);
+			codec = p_get_u8(frm);
+
+			if (codec == 255) {
+				vendor = btohl(htonl(p_get_u32(frm)));
+				vndcodec = btohs(htons(p_get_u16(frm)));
+
+				printf("%s - %s (%s)\n", cat2str(cat),
+						codec2str(type, codec),
+						vndcodec2str(vendor, vndcodec));
+			} else {
+				printf("%s - %s\n", cat2str(cat),
+							codec2str(type, codec));
+			}
+
+			switch (codec) {
+			case 0:
+				tmp = p_get_u8(frm);
+				p_indent(level + 1, frm);
+				if (tmp & 0x80)
+					printf("16kHz ");
+				if (tmp & 0x40)
+					printf("32kHz ");
+				if (tmp & 0x20)
+					printf("44.1kHz ");
+				if (tmp & 0x10)
+					printf("48kHz ");
+				printf("\n");
+				p_indent(level + 1, frm);
+				if (tmp & 0x08)
+					printf("Mono ");
+				if (tmp & 0x04)
+					printf("DualChannel ");
+				if (tmp & 0x02)
+					printf("Stereo ");
+				if (tmp & 0x01)
+					printf("JointStereo ");
+				printf("\n");
+				tmp = p_get_u8(frm);
+				p_indent(level + 1, frm);
+				if (tmp & 0x80)
+					printf("4 ");
+				if (tmp & 0x40)
+					printf("8 ");
+				if (tmp & 0x20)
+					printf("12 ");
+				if (tmp & 0x10)
+					printf("16 ");
+				printf("Blocks\n");
+				p_indent(level + 1, frm);
+				if (tmp & 0x08)
+					printf("4 ");
+				if (tmp & 0x04)
+					printf("8 ");
+				printf("Subbands\n");
+				p_indent(level + 1, frm);
+				if (tmp & 0x02)
+					printf("SNR ");
+				if (tmp & 0x01)
+					printf("Loudness ");
+				printf("\n");
+				tmp = p_get_u8(frm);
+				p_indent(level + 1, frm);
+				printf("Bitpool Range %d-%d\n", tmp, p_get_u8(frm));
+				break;
+			case 1:
+				tmp = p_get_u8(frm);
+				p_indent(level + 1, frm);
+				printf("Layers: ");
+				if (tmp & 0x80)
+					printf("1 ");
+				if (tmp & 0x40)
+					printf("2 ");
+				if (tmp & 0x20)
+					printf("3 ");
+				printf("\n");
+				p_indent(level + 1, frm);
+				printf("CRC Protection: %s\n",
+						tmp & 0x10 ? "Yes" : "No");
+				p_indent(level + 1, frm);
+				if (tmp & 0x08)
+					printf("Mono ");
+				if (tmp & 0x04)
+					printf("DualChannel ");
+				if (tmp & 0x02)
+					printf("Stereo ");
+				if (tmp & 0x01)
+					printf("JointStereo ");
+				printf("\n");
+				tmp = p_get_u8(frm);
+				p_indent(level + 1, frm);
+				printf("Media Payload Format: RFC-2250 %s\n",
+						tmp & 0x40 ? "RFC-3119" : "");
+				p_indent(level + 1, frm);
+				if (tmp & 0x20)
+					printf("16kHz ");
+				if (tmp & 0x10)
+					printf("22.05kHz ");
+				if (tmp & 0x08)
+					printf("24kHz ");
+				if (tmp & 0x04)
+					printf("32kHz ");
+				if (tmp & 0x02)
+					printf("44.1kHz ");
+				if (tmp & 0x01)
+					printf("48kHz ");
+				printf("\n");
+				tmp = p_get_u16(frm);
+				p_indent(level + 1, frm);
+				printf("VBR: %s\n",
+						tmp & 0x8000 ? "Yes" : "No");
+				p_indent(level + 1, frm);
+				printf("Bit Rate Indexes: ");
+				if (tmp & 0x8000) {
+					printf("n/a");
+				} else {
+					for (i = 0; i < 15; i++, tmp >>= 1)
+						if (tmp & 0x0001)
+							printf("%d ", i);
+				}
+				printf("\n");
+				break;
+			case 2:
+				tmp = p_get_u8(frm);
+				p_indent(level + 1, frm);
+				if (tmp & 0x80)
+					printf("MPEG-2 AAC LC ");
+				if (tmp & 0x40)
+					printf("MPEG-4 AAC LC ");
+				if (tmp & 0x20)
+					printf("MPEG-4 AAC LTP ");
+				if (tmp & 0x10)
+					printf("MPEG-4 AAC scalable ");
+				printf("\n");
+				tmp = p_get_u16(frm);
+				freq = tmp >> 4;
+				p_indent(level + 1, frm);
+				if (freq & 0x0800)
+					printf("8kHz ");
+				if (freq & 0x0400)
+					printf("11.025kHz ");
+				if (freq & 0x0200)
+					printf("12kHz ");
+				if (freq & 0x0100)
+					printf("16kHz ");
+				if (freq & 0x0080)
+					printf("22.05kHz ");
+				if (freq & 0x0040)
+					printf("24kHz ");
+				if (freq & 0x0020)
+					printf("32kHz ");
+				if (freq & 0x0010)
+					printf("44.1kHz ");
+				if (freq & 0x0008)
+					printf("48kHz ");
+				if (freq & 0x0004)
+					printf("64kHz ");
+				if (freq & 0x0002)
+					printf("88.2kHz ");
+				if (freq & 0x0001)
+					printf("96kHz ");
+				printf("\n");
+				tmp >>= 2;
+				p_indent(level + 1, frm);
+				if (tmp & 0x02)
+					printf("1 ");
+				if (tmp & 0x01)
+					printf("2 ");
+				printf("Channels\n");
+				tmp = p_get_u8(frm);
+				bitrate = ((tmp & 0x7f) << 16) | p_get_u16(frm);
+				p_indent(level + 1, frm);
+				printf("%ubps ", bitrate);
+				printf("%s\n", tmp & 0x80 ? "VBR" : "");
+				break;
+			case 255:
+				if (vendor == 0x0000004f &&
+							vndcodec == 0x0001) {
+					tmp = p_get_u8(frm);
+					p_indent(level + 1, frm);
+					if (tmp & 0x80)
+						printf("16kHz ");
+					if (tmp & 0x40)
+						printf("32kHz ");
+					if (tmp & 0x20)
+						printf("44.1kHz ");
+					if (tmp & 0x10)
+						printf("48kHz ");
+					printf("\n");
+					p_indent(level + 1, frm);
+					if (tmp & 0x02)
+						printf("Stereo ");
+					if (tmp & 0x01)
+						printf("Mono ");
+					printf("\n");
+					break;
+				} else {
+					hex_dump(level + 1, frm, len - 8);
+					frm->ptr += (len - 8);
+					frm->len -= (len - 8);
+				}
+				break;
+			default:
+				hex_dump(level + 1, frm, len - 2);
+				frm->ptr += (len - 2);
+				frm->len -= (len - 2);
+				break;
+			}
+		} else {
+			printf("%s\n", cat2str(cat));
+			hex_dump(level + 1, frm, len);
+
+			frm->ptr += len;
+			frm->len -= len;
+		}
+	}
+}
+
+static inline void discover(int level, uint8_t hdr, struct frame *frm)
+{
+	uint8_t seid, type;
+
+	switch (hdr & 0x03) {
+	case 0x02:
+		while (frm->len > 1) {
+			p_indent(level, frm);
+			seid = p_get_u8(frm);
+			type = p_get_u8(frm);
+			printf("ACP SEID %d - %s %s%s\n",
+				seid >> 2, media2str(type >> 4),
+				type & 0x08 ? "Sink" : "Source",
+				seid & 0x02 ? " (InUse)" : "");
+		}
+		break;
+	case 0x03:
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void get_capabilities(int level, uint8_t hdr, struct frame *frm)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_seid(level, frm);
+		break;
+	case 0x02:
+		capabilities(level, frm);
+		break;
+	case 0x03:
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void set_configuration(int level, uint8_t hdr, struct frame *frm)
+{
+	uint8_t cat;
+
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_int_seid(level, frm);
+		capabilities(level, frm);
+		break;
+	case 0x03:
+		p_indent(level, frm);
+		cat = p_get_u8(frm);
+		printf("%s\n", cat2str(cat));
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void get_configuration(int level, uint8_t hdr, struct frame *frm)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_seid(level, frm);
+	case 0x02:
+		capabilities(level, frm);
+		break;
+	case 0x03:
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void reconfigure(int level, uint8_t hdr, struct frame *frm)
+{
+	uint8_t cat;
+
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_seid(level, frm);
+		capabilities(level, frm);
+		break;
+	case 0x03:
+		p_indent(level, frm);
+		cat = p_get_u8(frm);
+		printf("%s\n", cat2str(cat));
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void open_close_stream(int level, uint8_t hdr, struct frame *frm)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_seid(level, frm);
+		break;
+	case 0x03:
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void start_suspend_stream(int level, uint8_t hdr, struct frame *frm)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		while (frm->len > 0)
+			acp_seid(level, frm);
+		break;
+	case 0x03:
+		acp_seid(level, frm);
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void abort_streaming(int level, uint8_t hdr, struct frame *frm)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_seid(level, frm);
+		break;
+	}
+}
+
+static inline void security(int level, uint8_t hdr, struct frame *frm)
+{
+	switch (hdr & 0x03) {
+	case 0x00:
+		acp_seid(level, frm);
+	case 0x02:
+		hex_dump(level + 1, frm, frm->len);
+		frm->ptr += frm->len;
+		frm->len = 0;
+		break;
+	case 0x03:
+		errorcode(level, frm);
+		break;
+	}
+}
+
+static inline void delay_report(int level, uint8_t hdr, struct frame *frm)
+{
+	uint8_t seid;
+	uint16_t delay;
+
+	switch (hdr & 0x03) {
+	case 0x00:
+		p_indent(level, frm);
+		seid = p_get_u8(frm);
+		delay = p_get_u16(frm);
+		printf("ACP SEID %d delay %u.%ums\n", seid >> 2,
+						delay / 10, delay % 10);
+		break;
+	case 0x03:
+		errorcode(level, frm);
+		break;
+	}
+}
+
+void avdtp_dump(int level, struct frame *frm)
+{
+	uint8_t hdr, sid, nsp, type;
+	uint16_t seqn;
+	uint32_t time, ssrc;
+
+	switch (frm->num) {
+	case 1:
+		p_indent(level, frm);
+		hdr = p_get_u8(frm);
+
+		nsp = (hdr & 0x0c) == 0x04 ? p_get_u8(frm) : 0;
+		sid = hdr & 0x08 ? 0x00 : p_get_u8(frm);
+
+		printf("AVDTP(s): %s %s: transaction %d nsp 0x%02x\n",
+			hdr & 0x08 ? pt2str(hdr) : si2str(sid),
+			mt2str(hdr), hdr >> 4, nsp);
+
+		switch (sid & 0x7f) {
+		case 0x01:
+			discover(level + 1, hdr, frm);
+			break;
+		case 0x02:
+		case 0x0c:
+			get_capabilities(level + 1, hdr, frm);
+			break;
+		case 0x03:
+			set_configuration(level + 1, hdr, frm);
+			break;
+		case 0x04:
+			get_configuration(level + 1, hdr, frm);
+			break;
+		case 0x05:
+			reconfigure(level + 1, hdr, frm);
+			break;
+		case 0x06:
+			open_close_stream(level + 1, hdr, frm);
+			break;
+		case 0x07:
+			start_suspend_stream(level + 1, hdr, frm);
+			break;
+		case 0x08:
+			open_close_stream(level + 1, hdr, frm);
+			break;
+		case 0x09:
+			start_suspend_stream(level + 1, hdr, frm);
+			break;
+		case 0x0a:
+			abort_streaming(level + 1, hdr, frm);
+			break;
+		case 0x0b:
+			security(level + 1, hdr, frm);
+			break;
+		case 0x0d:
+			delay_report(level + 1, hdr, frm);
+			break;
+		}
+
+		break;
+
+	case 2:
+		p_indent(level, frm);
+		hdr  = p_get_u8(frm);
+		type = p_get_u8(frm);
+		seqn = p_get_u16(frm);
+		time = p_get_u32(frm);
+		ssrc = p_get_u32(frm);
+
+		printf("AVDTP(m): ver %d %s%scc %d %spt %d seqn %d time %d ssrc %d\n",
+			hdr >> 6, hdr & 0x20 ? "pad " : "", hdr & 0x10 ? "ext " : "",
+			hdr & 0xf, type & 0x80 ? "mark " : "", type & 0x7f, seqn, time, ssrc);
+		break;
+	}
+
+	raw_dump(level, frm);
+}
diff --git a/bluez/repo/tools/parser/avrcp.c b/bluez/repo/tools/parser/avrcp.c
new file mode 100644
index 0000000..5f29272
--- /dev/null
+++ b/bluez/repo/tools/parser/avrcp.c
@@ -0,0 +1,2294 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 Intel Corporation.
+ *
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "parser.h"
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL		0x0
+#define AVC_CTYPE_STATUS		0x1
+#define AVC_CTYPE_SPECIFIC_INQUIRY	0x2
+#define AVC_CTYPE_NOTIFY		0x3
+#define AVC_CTYPE_GENERAL_INQUIRY	0x4
+#define AVC_CTYPE_NOT_IMPLEMENTED	0x8
+#define AVC_CTYPE_ACCEPTED		0x9
+#define AVC_CTYPE_REJECTED		0xA
+#define AVC_CTYPE_IN_TRANSITION		0xB
+#define AVC_CTYPE_STABLE		0xC
+#define AVC_CTYPE_CHANGED		0xD
+#define AVC_CTYPE_INTERIM		0xF
+
+/* subunit type */
+#define AVC_SUBUNIT_MONITOR		0x00
+#define AVC_SUBUNIT_AUDIO		0x01
+#define AVC_SUBUNIT_PRINTER		0x02
+#define AVC_SUBUNIT_DISC		0x03
+#define AVC_SUBUNIT_TAPE		0x04
+#define AVC_SUBUNIT_TURNER		0x05
+#define AVC_SUBUNIT_CA			0x06
+#define AVC_SUBUNIT_CAMERA		0x07
+#define AVC_SUBUNIT_PANEL		0x09
+#define AVC_SUBUNIT_BULLETIN_BOARD	0x0a
+#define AVC_SUBUNIT_CAMERA_STORAGE	0x0b
+#define AVC_SUBUNIT_VENDOR_UNIQUE	0x0c
+#define AVC_SUBUNIT_EXTENDED		0x1e
+#define AVC_SUBUNIT_UNIT		0x1f
+
+/* opcodes */
+#define AVC_OP_VENDORDEP		0x00
+#define AVC_OP_UNITINFO			0x30
+#define AVC_OP_SUBUNITINFO		0x31
+#define AVC_OP_PASSTHROUGH		0x7c
+
+/* operands in passthrough commands */
+#define AVC_PANEL_VOLUME_UP		0x41
+#define AVC_PANEL_VOLUME_DOWN		0x42
+#define AVC_PANEL_MUTE			0x43
+#define AVC_PANEL_PLAY			0x44
+#define AVC_PANEL_STOP			0x45
+#define AVC_PANEL_PAUSE			0x46
+#define AVC_PANEL_RECORD		0x47
+#define AVC_PANEL_REWIND		0x48
+#define AVC_PANEL_FAST_FORWARD		0x49
+#define AVC_PANEL_EJECT			0x4a
+#define AVC_PANEL_FORWARD		0x4b
+#define AVC_PANEL_BACKWARD		0x4c
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE	0x00
+#define AVRCP_PACKET_TYPE_START		0x01
+#define AVRCP_PACKET_TYPE_CONTINUING	0x02
+#define AVRCP_PACKET_TYPE_END		0x03
+
+/* pdu ids */
+#define AVRCP_GET_CAPABILITIES		0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES	0x11
+#define AVRCP_LIST_PLAYER_VALUES	0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE	0x13
+#define AVRCP_SET_PLAYER_VALUE		0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT	0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT	0x16
+#define AVRCP_DISPLAYABLE_CHARSET	0x17
+#define AVRCP_CT_BATTERY_STATUS		0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES	0x20
+#define AVRCP_GET_PLAY_STATUS		0x30
+#define AVRCP_REGISTER_NOTIFICATION	0x31
+#define AVRCP_REQUEST_CONTINUING	0x40
+#define AVRCP_ABORT_CONTINUING		0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME	0x50
+#define AVRCP_SET_ADDRESSED_PLAYER	0x60
+#define AVRCP_SET_BROWSED_PLAYER	0x70
+#define AVRCP_GET_FOLDER_ITEMS		0x71
+#define AVRCP_CHANGE_PATH		0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES	0x73
+#define AVRCP_PLAY_ITEM			0x74
+#define AVRCP_SEARCH			0x80
+#define AVRCP_ADD_TO_NOW_PLAYING	0x90
+#define AVRCP_GENERAL_REJECT		0xA0
+
+/* notification events */
+#define AVRCP_EVENT_PLAYBACK_STATUS_CHANGED		0x01
+#define AVRCP_EVENT_TRACK_CHANGED			0x02
+#define AVRCP_EVENT_TRACK_REACHED_END			0x03
+#define AVRCP_EVENT_TRACK_REACHED_START			0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED		0x05
+#define AVRCP_EVENT_BATT_STATUS_CHANGED			0x06
+#define AVRCP_EVENT_SYSTEM_STATUS_CHANGED		0x07
+#define AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED	0x08
+#define AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED		0x09
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED		0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED		0x0b
+#define AVRCP_EVENT_UIDS_CHANGED			0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED			0x0d
+
+/* error statuses */
+#define AVRCP_STATUS_INVALID_COMMAND			0x00
+#define AVRCP_STATUS_INVALID_PARAMETER			0x01
+#define AVRCP_STATUS_NOT_FOUND				0x02
+#define AVRCP_STATUS_INTERNAL_ERROR			0x03
+#define AVRCP_STATUS_SUCCESS				0x04
+#define AVRCP_STATUS_UID_CHANGED			0x05
+#define AVRCP_STATUS_INVALID_DIRECTION			0x07
+#define AVRCP_STATUS_NOT_DIRECTORY			0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST			0x09
+#define AVRCP_STATUS_INVALID_SCOPE			0x0a
+#define AVRCP_STATUS_OUT_OF_BOUNDS			0x0b
+#define AVRCP_STATUS_IS_DIRECTORY			0x0c
+#define AVRCP_STATUS_MEDIA_IN_USE			0x0d
+#define AVRCP_STATUS_NOW_PLAYING_LIST_FULL		0x0e
+#define AVRCP_STATUS_SEARCH_NOT_SUPPORTED		0x0f
+#define AVRCP_STATUS_SEARCH_IN_PROGRESS			0x10
+#define AVRCP_STATUS_INVALID_PLAYER_ID			0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE		0x12
+#define AVRCP_STATUS_PLAYER_NOT_ADDRESSED		0x13
+#define AVRCP_STATUS_NO_VALID_SEARCH_RESULTS		0x14
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS		0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED		0x16
+
+/* player attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL		0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER	0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE	0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE		0x03
+#define AVRCP_ATTRIBUTE_SCAN		0x04
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL	0x0
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE	0x1
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST	0x2
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM	0x3
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK	0x4
+#define AVRCP_MEDIA_ATTRIBUTE_TOTAL	0x5
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE	0x6
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION	0x7
+
+/* play status */
+#define AVRCP_PLAY_STATUS_STOPPED	0x00
+#define AVRCP_PLAY_STATUS_PLAYING	0x01
+#define AVRCP_PLAY_STATUS_PAUSED	0x02
+#define AVRCP_PLAY_STATUS_FWD_SEEK	0x03
+#define AVRCP_PLAY_STATUS_REV_SEEK	0x04
+#define AVRCP_PLAY_STATUS_ERROR		0xFF
+
+/* media scope */
+#define AVRCP_MEDIA_PLAYER_LIST		0x00
+#define AVRCP_MEDIA_PLAYER_VFS		0x01
+#define AVRCP_MEDIA_SEARCH		0x02
+#define AVRCP_MEDIA_NOW_PLAYING		0x03
+
+static struct avrcp_continuing {
+	uint16_t num;
+	uint16_t size;
+} avrcp_continuing;
+
+static const char *ctype2str(uint8_t ctype)
+{
+	switch (ctype & 0x0f) {
+	case AVC_CTYPE_CONTROL:
+		return "Control";
+	case AVC_CTYPE_STATUS:
+		return "Status";
+	case AVC_CTYPE_SPECIFIC_INQUIRY:
+		return "Specific Inquiry";
+	case AVC_CTYPE_NOTIFY:
+		return "Notify";
+	case AVC_CTYPE_GENERAL_INQUIRY:
+		return "General Inquiry";
+	case AVC_CTYPE_NOT_IMPLEMENTED:
+		return "Not Implemented";
+	case AVC_CTYPE_ACCEPTED:
+		return "Accepted";
+	case AVC_CTYPE_REJECTED:
+		return "Rejected";
+	case AVC_CTYPE_IN_TRANSITION:
+		return "In Transition";
+	case AVC_CTYPE_STABLE:
+		return "Stable";
+	case AVC_CTYPE_CHANGED:
+		return "Changed";
+	case AVC_CTYPE_INTERIM:
+		return "Interim";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *opcode2str(uint8_t opcode)
+{
+	switch (opcode) {
+	case AVC_OP_VENDORDEP:
+		return "Vendor Dependent";
+	case AVC_OP_UNITINFO:
+		return "Unit Info";
+	case AVC_OP_SUBUNITINFO:
+		return "Subunit Info";
+	case AVC_OP_PASSTHROUGH:
+		return "Passthrough";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *pt2str(uint8_t pt)
+{
+	switch (pt) {
+	case AVRCP_PACKET_TYPE_SINGLE:
+		return "Single";
+	case AVRCP_PACKET_TYPE_START:
+		return "Start";
+	case AVRCP_PACKET_TYPE_CONTINUING:
+		return "Continuing";
+	case AVRCP_PACKET_TYPE_END:
+		return "End";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *pdu2str(uint8_t pduid)
+{
+	switch (pduid) {
+	case AVRCP_GET_CAPABILITIES:
+		return "GetCapabilities";
+	case AVRCP_LIST_PLAYER_ATTRIBUTES:
+		return "ListPlayerApplicationSettingAttributes";
+	case AVRCP_LIST_PLAYER_VALUES:
+		return "ListPlayerApplicationSettingValues";
+	case AVRCP_GET_CURRENT_PLAYER_VALUE:
+		return "GetCurrentPlayerApplicationSettingValue";
+	case AVRCP_SET_PLAYER_VALUE:
+		return "SetPlayerApplicationSettingValue";
+	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
+		return "GetPlayerApplicationSettingAttributeText";
+	case AVRCP_GET_PLAYER_VALUE_TEXT:
+		return "GetPlayerApplicationSettingValueText";
+	case AVRCP_DISPLAYABLE_CHARSET:
+		return "InformDisplayableCharacterSet";
+	case AVRCP_CT_BATTERY_STATUS:
+		return "InformBatteryStatusOfCT";
+	case AVRCP_GET_ELEMENT_ATTRIBUTES:
+		return "GetElementAttributes";
+	case AVRCP_GET_PLAY_STATUS:
+		return "GetPlayStatus";
+	case AVRCP_REGISTER_NOTIFICATION:
+		return "RegisterNotification";
+	case AVRCP_REQUEST_CONTINUING:
+		return "RequestContinuingResponse";
+	case AVRCP_ABORT_CONTINUING:
+		return "AbortContinuingResponse";
+	case AVRCP_SET_ABSOLUTE_VOLUME:
+		return "SetAbsoluteVolume";
+	case AVRCP_SET_ADDRESSED_PLAYER:
+		return "SetAddressedPlayer";
+	case AVRCP_SET_BROWSED_PLAYER:
+		return "SetBrowsedPlayer";
+	case AVRCP_GET_FOLDER_ITEMS:
+		return "GetFolderItems";
+	case AVRCP_CHANGE_PATH:
+		return "ChangePath";
+	case AVRCP_GET_ITEM_ATTRIBUTES:
+		return "GetItemAttributes";
+	case AVRCP_PLAY_ITEM:
+		return "PlayItem";
+	case AVRCP_SEARCH:
+		return "Search";
+	case AVRCP_ADD_TO_NOW_PLAYING:
+		return "AddToNowPlaying";
+	case AVRCP_GENERAL_REJECT:
+		return "GeneralReject";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *cap2str(uint8_t cap)
+{
+	switch (cap) {
+	case 0x2:
+		return "CompanyID";
+	case 0x3:
+		return "EventsID";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *event2str(uint8_t event)
+{
+	switch (event) {
+	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+		return "EVENT_PLAYBACK_STATUS_CHANGED";
+	case AVRCP_EVENT_TRACK_CHANGED:
+		return "EVENT_TRACK_CHANGED";
+	case AVRCP_EVENT_TRACK_REACHED_END:
+		return "EVENT_TRACK_REACHED_END";
+	case AVRCP_EVENT_TRACK_REACHED_START:
+		return "EVENT_TRACK_REACHED_START";
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		return "EVENT_PLAYBACK_POS_CHANGED";
+	case AVRCP_EVENT_BATT_STATUS_CHANGED:
+		return "EVENT_BATT_STATUS_CHANGED";
+	case AVRCP_EVENT_SYSTEM_STATUS_CHANGED:
+		return "EVENT_SYSTEM_STATUS_CHANGED";
+	case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
+		return "EVENT_PLAYER_APPLICATION_SETTING_CHANGED";
+	case AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED:
+		return "EVENT_NOW_PLAYING_CONTENT_CHANGED";
+	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		return "EVENT_AVAILABLE_PLAYERS_CHANGED";
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		return "EVENT_ADDRESSED_PLAYER_CHANGED";
+	case AVRCP_EVENT_UIDS_CHANGED:
+		return "EVENT_UIDS_CHANGED";
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		return "EVENT_VOLUME_CHANGED";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *error2str(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_STATUS_INVALID_COMMAND:
+		return "Invalid Command";
+	case AVRCP_STATUS_INVALID_PARAMETER:
+		return "Invalid Parameter";
+	case AVRCP_STATUS_NOT_FOUND:
+		return "Not Found";
+	case AVRCP_STATUS_INTERNAL_ERROR:
+		return "Internal Error";
+	case AVRCP_STATUS_SUCCESS:
+		return "Success";
+	case AVRCP_STATUS_UID_CHANGED:
+		return "UID Changed";
+	case AVRCP_STATUS_INVALID_DIRECTION:
+		return "Invalid Direction";
+	case AVRCP_STATUS_NOT_DIRECTORY:
+		return "Not a Directory";
+	case AVRCP_STATUS_DOES_NOT_EXIST:
+		return "Does Not Exist";
+	case AVRCP_STATUS_INVALID_SCOPE:
+		return "Invalid Scope";
+	case AVRCP_STATUS_OUT_OF_BOUNDS:
+		return "Range Out of Bonds";
+	case AVRCP_STATUS_MEDIA_IN_USE:
+		return "Media in Use";
+	case AVRCP_STATUS_IS_DIRECTORY:
+		return "UID is a Directory";
+	case AVRCP_STATUS_NOW_PLAYING_LIST_FULL:
+		return "Now Playing List Full";
+	case AVRCP_STATUS_SEARCH_NOT_SUPPORTED:
+		return "Seach Not Supported";
+	case AVRCP_STATUS_SEARCH_IN_PROGRESS:
+		return "Search in Progress";
+	case AVRCP_STATUS_INVALID_PLAYER_ID:
+		return "Invalid Player ID";
+	case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
+		return "Player Not Browsable";
+	case AVRCP_STATUS_PLAYER_NOT_ADDRESSED:
+		return "Player Not Addressed";
+	case AVRCP_STATUS_NO_VALID_SEARCH_RESULTS:
+		return "No Valid Search Result";
+	case AVRCP_STATUS_NO_AVAILABLE_PLAYERS:
+		return "No Available Players";
+	case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED:
+		return "Addressed Player Changed";
+	default:
+		return "Unknown";
+	}
+}
+
+static void avrcp_rejected_dump(int level, struct frame *frm, uint16_t len)
+{
+	uint8_t status;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	status = p_get_u8(frm);
+	printf("Error: 0x%02x (%s)\n", status, error2str(status));
+}
+
+static void avrcp_get_capabilities_dump(int level, struct frame *frm, uint16_t len)
+{
+	uint8_t cap;
+	uint8_t count;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	cap = p_get_u8(frm);
+	printf("CapabilityID: 0x%02x (%s)\n", cap, cap2str(cap));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	count = p_get_u8(frm);
+	printf("CapabilityCount: 0x%02x\n", count);
+
+	switch (cap) {
+	case 0x2:
+		for (; count > 0; count--) {
+			int i;
+
+			p_indent(level, frm);
+
+			printf("%s: 0x", cap2str(cap));
+			for (i = 0; i < 3; i++)
+				printf("%02x", p_get_u8(frm));
+			printf("\n");
+		}
+		break;
+	case 0x3:
+		for (; count > 0; count--) {
+			uint8_t event;
+
+			p_indent(level, frm);
+
+			event = p_get_u8(frm);
+			printf("%s: 0x%02x (%s)\n", cap2str(cap), event,
+							event2str(event));
+		}
+		break;
+	default:
+		raw_dump(level, frm);
+	}
+}
+
+static const char *attr2str(uint8_t attr)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_ILEGAL:
+		return "Illegal";
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		return "Equalizer ON/OFF Status";
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		return "Repeat Mode Status";
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		return "Shuffle ON/OFF Status";
+	case AVRCP_ATTRIBUTE_SCAN:
+		return "Scan ON/OFF Status";
+	default:
+		return "Unknown";
+	}
+}
+
+static void avrcp_list_player_attributes_dump(int level, struct frame *frm,
+								uint16_t len)
+{
+	uint8_t num;
+
+	if (len == 0)
+		return;
+
+	p_indent(level, frm);
+
+	num = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		p_indent(level, frm);
+
+		attr = p_get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+	}
+}
+
+static const char *value2str(uint8_t attr, uint8_t value)
+{
+	switch (attr) {
+	case AVRCP_ATTRIBUTE_ILEGAL:
+		return "Illegal";
+	case AVRCP_ATTRIBUTE_EQUALIZER:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "ON";
+		default:
+			return "Reserved";
+		}
+	case AVRCP_ATTRIBUTE_REPEAT_MODE:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "Single Track Repeat";
+		case 0x03:
+			return "All Track Repeat";
+		case 0x04:
+			return "Group Repeat";
+		default:
+			return "Reserved";
+		}
+	case AVRCP_ATTRIBUTE_SHUFFLE:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "All Track Suffle";
+		case 0x03:
+			return "Group Suffle";
+		default:
+			return "Reserved";
+		}
+	case AVRCP_ATTRIBUTE_SCAN:
+		switch (value) {
+		case 0x01:
+			return "OFF";
+		case 0x02:
+			return "All Track Scan";
+		case 0x03:
+			return "Group Scan";
+		default:
+			return "Reserved";
+		}
+	default:
+		return "Unknown";
+	}
+}
+
+static void avrcp_list_player_values_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	static uint8_t attr = 0; /* Remember attribute */
+	uint8_t num;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	attr = p_get_u8(frm);
+	printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+	return;
+
+response:
+	num = p_get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t value;
+
+		p_indent(level, frm);
+
+		value = p_get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+	}
+}
+
+static void avrcp_get_current_player_value_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t num;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	num = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		p_indent(level, frm);
+
+		attr = p_get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+	}
+
+	return;
+
+response:
+	num = p_get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr, value;
+
+		p_indent(level, frm);
+
+		attr = p_get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+		p_indent(level, frm);
+
+		value = p_get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+	}
+}
+
+static void avrcp_set_player_value_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t num;
+
+	p_indent(level, frm);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		return;
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	num = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t attr, value;
+
+		p_indent(level, frm);
+
+		attr = p_get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+		p_indent(level, frm);
+
+		value = p_get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+	}
+}
+
+static const char *charset2str(uint16_t charset)
+{
+	switch (charset) {
+	case 1:
+	case 2:
+		return "Reserved";
+	case 3:
+		return "ASCII";
+	case 4:
+		return "ISO_8859-1";
+	case 5:
+		return "ISO_8859-2";
+	case 6:
+		return "ISO_8859-3";
+	case 7:
+		return "ISO_8859-4";
+	case 8:
+		return "ISO_8859-5";
+	case 9:
+		return "ISO_8859-6";
+	case 10:
+		return "ISO_8859-7";
+	case 11:
+		return "ISO_8859-8";
+	case 12:
+		return "ISO_8859-9";
+	case 106:
+		return "UTF-8";
+	default:
+		return "Unknown";
+	}
+}
+
+static void avrcp_get_player_attribute_text_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t num;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	num = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	for (; num > 0; num--) {
+		uint8_t attr;
+
+		p_indent(level, frm);
+
+		attr = p_get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+	}
+
+	return;
+
+response:
+	for (; num > 0; num--) {
+		uint8_t attr, len;
+		uint16_t charset;
+
+		p_indent(level, frm);
+
+		attr = p_get_u8(frm);
+		printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+		p_indent(level, frm);
+
+		charset = p_get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+
+		len = p_get_u8(frm);
+		printf("StringLength: 0x%02x\n", len);
+
+		p_indent(level, frm);
+
+		printf("String: ");
+		for (; len > 0; len--) {
+			uint8_t c = p_get_u8(frm);
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+}
+
+static void avrcp_get_player_value_text_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	static uint8_t attr = 0; /* Remember attribute */
+	uint8_t num;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	attr = p_get_u8(frm);
+	printf("AttributeID: 0x%02x (%s)\n", attr, attr2str(attr));
+
+	p_indent(level, frm);
+
+	num = p_get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t value;
+
+		p_indent(level, frm);
+
+		value = p_get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+	}
+
+	return;
+
+response:
+	num = p_get_u8(frm);
+	printf("ValueCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint8_t value, len;
+		uint16_t charset;
+
+		p_indent(level, frm);
+
+		value = p_get_u8(frm);
+		printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+
+		p_indent(level, frm);
+
+		charset = p_get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+
+		len = p_get_u8(frm);
+		printf("StringLength: 0x%02x\n", len);
+
+		p_indent(level, frm);
+
+		printf("String: ");
+		for (; len > 0; len--) {
+			uint8_t c = p_get_u8(frm);
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+}
+
+static void avrcp_displayable_charset(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t num;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		return;
+
+	p_indent(level, frm);
+
+	if (len < 2) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	num = p_get_u8(frm);
+	printf("CharsetCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint16_t charset;
+
+		p_indent(level, frm);
+
+		charset = p_get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+	}
+}
+
+static const char *status2str(uint8_t status)
+{
+	switch (status) {
+	case 0x0:
+		return "NORMAL";
+	case 0x1:
+		return "WARNING";
+	case 0x2:
+		return "CRITICAL";
+	case 0x3:
+		return "EXTERNAL";
+	case 0x4:
+		return "FULL_CHARGE";
+	default:
+		return "Reserved";
+	}
+}
+
+static void avrcp_ct_battery_status_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t status;
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		return;
+
+	p_indent(level, frm);
+
+	status = p_get_u8(frm);
+	printf("BatteryStatus: 0x%02x (%s)\n", status, status2str(status));
+}
+
+static const char *mediattr2str(uint32_t attr)
+{
+	switch (attr) {
+	case AVRCP_MEDIA_ATTRIBUTE_ILLEGAL:
+		return "Illegal";
+	case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+		return "Title";
+	case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+		return "Artist";
+	case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+		return "Album";
+	case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+		return "Track";
+	case AVRCP_MEDIA_ATTRIBUTE_TOTAL:
+		return "Track Total";
+	case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+		return "Genre";
+	case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+		return "Track duration";
+	default:
+		return "Reserved";
+	}
+}
+
+static void avrcp_get_element_attributes_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len,
+						uint8_t pt)
+{
+	uint64_t id;
+	uint8_t num;
+
+	p_indent(level, frm);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (len < 9) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	id = p_get_u64(frm);
+	printf("Identifier: 0x%jx (%s)\n", id, id ? "Reserved" : "PLAYING");
+
+	p_indent(level, frm);
+
+	num = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x\n", num);
+
+	for (; num > 0; num--) {
+		uint32_t attr;
+
+		p_indent(level, frm);
+
+		attr = p_get_u32(frm);
+		printf("Attribute: 0x%08x (%s)\n", attr, mediattr2str(attr));
+	}
+
+	return;
+
+response:
+	if (pt == AVRCP_PACKET_TYPE_SINGLE || pt == AVRCP_PACKET_TYPE_START) {
+		if (len < 1) {
+			printf("PDU Malformed\n");
+			raw_dump(level, frm);
+			return;
+		}
+
+		num = p_get_u8(frm);
+		avrcp_continuing.num = num;
+		printf("AttributeCount: 0x%02x\n", num);
+		len--;
+	} else {
+		num = avrcp_continuing.num;
+
+		if (avrcp_continuing.size > 0) {
+			uint16_t size;
+
+			if (avrcp_continuing.size > len) {
+				size = len;
+				avrcp_continuing.size -= len;
+			} else {
+				size = avrcp_continuing.size;
+				avrcp_continuing.size = 0;
+			}
+
+			printf("ContinuingAttributeValue: ");
+			for (; size > 0; size--) {
+				uint8_t c = p_get_u8(frm);
+				printf("%1c", isprint(c) ? c : '.');
+			}
+			printf("\n");
+
+			len -= size;
+		}
+	}
+
+	while (num > 0 && len > 0) {
+		uint32_t attr;
+		uint16_t charset, attrlen;
+
+		p_indent(level, frm);
+
+		attr = p_get_u32(frm);
+		printf("Attribute: 0x%08x (%s)\n", attr, mediattr2str(attr));
+
+		p_indent(level, frm);
+
+		charset = p_get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+		attrlen = p_get_u16(frm);
+		printf("AttributeValueLength: 0x%04x\n", attrlen);
+
+		len -= sizeof(attr) + sizeof(charset) + sizeof(attrlen);
+		num--;
+
+		p_indent(level, frm);
+
+		printf("AttributeValue: ");
+		for (; attrlen > 0 && len > 0; attrlen--, len--) {
+			uint8_t c = p_get_u8(frm);
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+
+		if (attrlen > 0)
+			avrcp_continuing.size = attrlen;
+	}
+
+	avrcp_continuing.num = num;
+}
+
+static const char *playstatus2str(uint8_t status)
+{
+	switch (status) {
+	case AVRCP_PLAY_STATUS_STOPPED:
+		return "STOPPED";
+	case AVRCP_PLAY_STATUS_PLAYING:
+		return "PLAYING";
+	case AVRCP_PLAY_STATUS_PAUSED:
+		return "PAUSED";
+	case AVRCP_PLAY_STATUS_FWD_SEEK:
+		return "FWD_SEEK";
+	case AVRCP_PLAY_STATUS_REV_SEEK:
+		return "REV_SEEK";
+	case AVRCP_PLAY_STATUS_ERROR:
+		return "ERROR";
+	default:
+		return "Unknown";
+	}
+}
+
+static void avrcp_get_play_status_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint32_t interval;
+	uint8_t status;
+
+	if (ctype <= AVC_CTYPE_GENERAL_INQUIRY)
+		return;
+
+	p_indent(level, frm);
+
+	if (len < 9) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	interval = p_get_u32(frm);
+	printf("SongLength: 0x%08x (%u miliseconds)\n", interval, interval);
+
+	p_indent(level, frm);
+
+	interval = p_get_u32(frm);
+	printf("SongPosition: 0x%08x (%u miliconds)\n", interval, interval);
+
+	p_indent(level, frm);
+
+	status = p_get_u8(frm);
+	printf("PlayStatus: 0x%02x (%s)\n", status, playstatus2str(status));
+}
+
+static void avrcp_register_notification_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t event, status;
+	uint16_t uid;
+	uint32_t interval;
+	uint64_t id;
+
+	p_indent(level, frm);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (len < 5) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	event = p_get_u8(frm);
+	printf("EventID: 0x%02x (%s)\n", event, event2str(event));
+
+	p_indent(level, frm);
+
+	interval = p_get_u32(frm);
+	printf("Interval: 0x%08x (%u seconds)\n", interval, interval);
+
+	return;
+
+response:
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	event = p_get_u8(frm);
+	printf("EventID: 0x%02x (%s)\n", event, event2str(event));
+
+	p_indent(level, frm);
+
+	switch (event) {
+	case AVRCP_EVENT_PLAYBACK_STATUS_CHANGED:
+		status = p_get_u8(frm);
+		printf("PlayStatus: 0x%02x (%s)\n", status,
+						playstatus2str(status));
+		break;
+	case AVRCP_EVENT_TRACK_CHANGED:
+		id = p_get_u64(frm);
+		printf("Identifier: 0x%16" PRIx64 " (%" PRIu64 ")\n", id, id);
+		break;
+	case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+		interval = p_get_u32(frm);
+		printf("Position: 0x%08x (%u miliseconds)\n", interval,
+								interval);
+		break;
+	case AVRCP_EVENT_BATT_STATUS_CHANGED:
+		status = p_get_u8(frm);
+		printf("BatteryStatus: 0x%02x (%s)\n", status,
+							status2str(status));
+		break;
+	case AVRCP_EVENT_SYSTEM_STATUS_CHANGED:
+		status = p_get_u8(frm);
+		printf("SystemStatus: 0x%02x ", status);
+		switch (status) {
+		case 0x00:
+			printf("(POWER_ON)\n");
+			break;
+		case 0x01:
+			printf("(POWER_OFF)\n");
+			break;
+		case 0x02:
+			printf("(UNPLUGGED)\n");
+			break;
+		default:
+			printf("(UNKOWN)\n");
+			break;
+		}
+		break;
+	case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
+		status = p_get_u8(frm);
+		printf("AttributeCount: 0x%02x\n", status);
+
+		for (; status > 0; status--) {
+			uint8_t attr, value;
+
+			p_indent(level, frm);
+
+			attr = p_get_u8(frm);
+			printf("AttributeID: 0x%02x (%s)\n", attr,
+							attr2str(attr));
+
+			p_indent(level, frm);
+
+			value = p_get_u8(frm);
+			printf("ValueID: 0x%02x (%s)\n", value,
+						value2str(attr, value));
+		}
+		break;
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		status = p_get_u8(frm) & 0x7F;
+		printf("Volume: %.2f%% (%d/127)\n", status/1.27, status);
+		break;
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		uid = p_get_u16(frm);
+		printf("PlayerID: 0x%04x (%u)\n", uid, uid);
+
+
+		p_indent(level, frm);
+
+		uid = p_get_u16(frm);
+		printf("UIDCounter: 0x%04x (%u)\n", uid, uid);
+		break;
+	case AVRCP_EVENT_UIDS_CHANGED:
+		uid = p_get_u16(frm);
+		printf("UIDCounter: 0x%04x (%u)\n", uid, uid);
+		break;
+	}
+}
+
+static void avrcp_set_absolute_volume_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint8_t value;
+
+	p_indent(level, frm);
+
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	value = p_get_u8(frm) & 0x7F;
+	printf("Volume: %.2f%% (%d/127)\n", value/1.27, value);
+}
+
+static void avrcp_set_addressed_player(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint16_t id;
+	uint8_t status;
+
+	p_indent(level, frm);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (len < 2) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	id = p_get_u16(frm);
+	printf("PlayerID: 0x%04x (%u)\n", id, id);
+	return;
+
+response:
+	if (len < 1) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+}
+
+static void avrcp_set_browsed_player_dump(int level, struct frame *frm,
+						uint8_t hdr, uint16_t len)
+{
+	uint32_t items;
+	uint16_t id, uids, charset;
+	uint8_t status, folders;
+
+	p_indent(level, frm);
+
+	if (hdr & 0x02)
+		goto response;
+
+	if (len < 2) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	id = p_get_u16(frm);
+	printf("PlayerID: 0x%04x (%u)\n", id, id);
+	return;
+
+response:
+	if (len != 1 && len < 10) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	uids = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uids, uids);
+
+	p_indent(level, frm);
+
+	items = p_get_u32(frm);
+	printf("Number of Items: 0x%08x (%u)\n", items, items);
+
+	p_indent(level, frm);
+
+	charset = p_get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	folders = p_get_u8(frm);
+	printf("Folder Depth: 0x%02x (%u)\n", folders, folders);
+
+	for (; folders > 0; folders--) {
+		uint16_t len;
+
+		p_indent(level, frm);
+
+		len = p_get_u8(frm);
+		printf("Folder: ");
+		for (; len > 0; len--) {
+			uint8_t c = p_get_u8(frm);
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+}
+
+static const char *scope2str(uint8_t scope)
+{
+	switch (scope) {
+	case AVRCP_MEDIA_PLAYER_LIST:
+		return "Media Player List";
+	case AVRCP_MEDIA_PLAYER_VFS:
+		return "Media Player Virtual Filesystem";
+	case AVRCP_MEDIA_SEARCH:
+		return "Search";
+	case AVRCP_MEDIA_NOW_PLAYING:
+		return "Now Playing";
+	default:
+		return "Unknown";
+	}
+}
+
+static void avrcp_play_item_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint64_t uid;
+	uint32_t uidcounter;
+	uint8_t scope, status;
+
+	p_indent(level, frm);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (len < 11) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	scope = p_get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	uid = p_get_u64(frm);
+	printf("UID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	uidcounter = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+}
+
+static void avrcp_add_to_now_playing_dump(int level, struct frame *frm,
+						uint8_t ctype, uint16_t len)
+{
+	uint64_t uid;
+	uint32_t uidcounter;
+	uint8_t scope, status;
+
+	p_indent(level, frm);
+
+	if (ctype > AVC_CTYPE_GENERAL_INQUIRY)
+		goto response;
+
+	if (len < 11) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	scope = p_get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	uid = p_get_u64(frm);
+	printf("UID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	uidcounter = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+}
+
+static void avrcp_pdu_dump(int level, struct frame *frm, uint8_t ctype)
+{
+	uint8_t pduid, pt;
+	uint16_t len;
+
+	p_indent(level, frm);
+
+	pduid = p_get_u8(frm);
+	pt = p_get_u8(frm);
+	len = p_get_u16(frm);
+
+	printf("AVRCP: %s: pt %s len 0x%04x\n", pdu2str(pduid),
+							pt2str(pt), len);
+
+	if (len != frm->len) {
+		p_indent(level, frm);
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	if (ctype == AVC_CTYPE_REJECTED) {
+		avrcp_rejected_dump(level + 1, frm, len);
+		return;
+	}
+
+	switch (pduid) {
+	case AVRCP_GET_CAPABILITIES:
+		avrcp_get_capabilities_dump(level + 1, frm, len);
+		break;
+	case AVRCP_LIST_PLAYER_ATTRIBUTES:
+		avrcp_list_player_attributes_dump(level + 1, frm, len);
+		break;
+	case AVRCP_LIST_PLAYER_VALUES:
+		avrcp_list_player_values_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_GET_CURRENT_PLAYER_VALUE:
+		avrcp_get_current_player_value_dump(level + 1, frm, ctype,
+									len);
+		break;
+	case AVRCP_SET_PLAYER_VALUE:
+		avrcp_set_player_value_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_GET_PLAYER_ATTRIBUTE_TEXT:
+		avrcp_get_player_attribute_text_dump(level + 1, frm, ctype,
+									len);
+		break;
+	case AVRCP_GET_PLAYER_VALUE_TEXT:
+		avrcp_get_player_value_text_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_DISPLAYABLE_CHARSET:
+		avrcp_displayable_charset(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_CT_BATTERY_STATUS:
+		avrcp_ct_battery_status_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_GET_ELEMENT_ATTRIBUTES:
+		avrcp_get_element_attributes_dump(level + 1, frm, ctype, len,
+									pt);
+		break;
+	case AVRCP_GET_PLAY_STATUS:
+		avrcp_get_play_status_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_REGISTER_NOTIFICATION:
+		avrcp_register_notification_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_SET_ABSOLUTE_VOLUME:
+		avrcp_set_absolute_volume_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_SET_ADDRESSED_PLAYER:
+		avrcp_set_addressed_player(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_PLAY_ITEM:
+		avrcp_play_item_dump(level + 1, frm, ctype, len);
+		break;
+	case AVRCP_ADD_TO_NOW_PLAYING:
+		avrcp_add_to_now_playing_dump(level + 1, frm, ctype, len);
+		break;
+	default:
+		raw_dump(level, frm);
+	}
+}
+
+static char *op2str(uint8_t op)
+{
+	switch (op & 0x7f) {
+	case AVC_PANEL_VOLUME_UP:
+		return "VOLUME UP";
+	case AVC_PANEL_VOLUME_DOWN:
+		return "VOLUME DOWN";
+	case AVC_PANEL_MUTE:
+		return "MUTE";
+	case AVC_PANEL_PLAY:
+		return "PLAY";
+	case AVC_PANEL_STOP:
+		return "STOP";
+	case AVC_PANEL_PAUSE:
+		return "PAUSE";
+	case AVC_PANEL_RECORD:
+		return "RECORD";
+	case AVC_PANEL_REWIND:
+		return "REWIND";
+	case AVC_PANEL_FAST_FORWARD:
+		return "FAST FORWARD";
+	case AVC_PANEL_EJECT:
+		return "EJECT";
+	case AVC_PANEL_FORWARD:
+		return "FORWARD";
+	case AVC_PANEL_BACKWARD:
+		return "BACKWARD";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+static void avrcp_passthrough_dump(int level, struct frame *frm)
+{
+	uint8_t op, len;
+
+	p_indent(level, frm);
+
+	op = p_get_u8(frm);
+	printf("Operation: 0x%02x (%s %s)\n", op, op2str(op),
+					op & 0x80 ? "Released" : "Pressed");
+
+	p_indent(level, frm);
+
+	len = p_get_u8(frm);
+
+	printf("Lenght: 0x%02x\n", len);
+
+	raw_dump(level, frm);
+}
+
+static const char *subunit2str(uint8_t subunit)
+{
+	switch (subunit) {
+	case AVC_SUBUNIT_MONITOR:
+		return "Monitor";
+	case AVC_SUBUNIT_AUDIO:
+		return "Audio";
+	case AVC_SUBUNIT_PRINTER:
+		return "Printer";
+	case AVC_SUBUNIT_DISC:
+		return "Disc";
+	case AVC_SUBUNIT_TAPE:
+		return "Tape";
+	case AVC_SUBUNIT_TURNER:
+		return "Turner";
+	case AVC_SUBUNIT_CA:
+		return "CA";
+	case AVC_SUBUNIT_CAMERA:
+		return "Camera";
+	case AVC_SUBUNIT_PANEL:
+		return "Panel";
+	case AVC_SUBUNIT_BULLETIN_BOARD:
+		return "Bulleting Board";
+	case AVC_SUBUNIT_CAMERA_STORAGE:
+		return "Camera Storage";
+	case AVC_SUBUNIT_VENDOR_UNIQUE:
+		return "Vendor Unique";
+	case AVC_SUBUNIT_EXTENDED:
+		return "Extended to next byte";
+	case AVC_SUBUNIT_UNIT:
+		return "Unit";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *playertype2str(uint8_t type)
+{
+	switch (type & 0x0F) {
+	case 0x01:
+		return "Audio";
+	case 0x02:
+		return "Video";
+	case 0x03:
+		return "Audio, Video";
+	case 0x04:
+		return "Audio Broadcasting";
+	case 0x05:
+		return "Audio, Audio Broadcasting";
+	case 0x06:
+		return "Video, Audio Broadcasting";
+	case 0x07:
+		return "Audio, Video, Audio Broadcasting";
+	case 0x08:
+		return "Video Broadcasting";
+	case 0x09:
+		return "Audio, Video Broadcasting";
+	case 0x0A:
+		return "Video, Video Broadcasting";
+	case 0x0B:
+		return "Audio, Video, Video Broadcasting";
+	case 0x0C:
+		return "Audio Broadcasting, Video Broadcasting";
+	case 0x0D:
+		return "Audio, Audio Broadcasting, Video Broadcasting";
+	case 0x0E:
+		return "Video, Audio Broadcasting, Video Broadcasting";
+	case 0x0F:
+		return "Audio, Video, Audio Broadcasting, Video Broadcasting";
+	}
+
+	return "None";
+}
+
+static const char *playersubtype2str(uint32_t subtype)
+{
+	switch (subtype & 0x03) {
+	case 0x01:
+		return "Audio Book";
+	case 0x02:
+		return "Podcast";
+	case 0x03:
+		return "Audio Book, Podcast";
+	}
+
+	return "None";
+}
+
+static void avrcp_media_player_item_dump(int level, struct frame *frm,
+								uint16_t len)
+{
+	uint16_t id, charset, namelen;
+	uint8_t type, status;
+	uint32_t subtype;
+	uint64_t features[2];
+
+	p_indent(level, frm);
+
+	if (len < 28) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	id = p_get_u16(frm);
+	printf("PlayerID: 0x%04x (%u)\n", id, id);
+
+	p_indent(level, frm);
+
+	type = p_get_u8(frm);
+	printf("PlayerType: 0x%04x (%s)\n", type, playertype2str(type));
+
+	p_indent(level, frm);
+
+	subtype = p_get_u32(frm);
+	printf("PlayerSubtype: 0x%08x (%s)\n", subtype,
+						playersubtype2str(subtype));
+
+	p_indent(level, frm);
+
+	status = p_get_u8(frm);
+	printf("PlayStatus: 0x%02x (%s)\n", status, playstatus2str(status));
+
+	p_indent(level, frm);
+
+	p_get_u128(frm, &features[0], &features[1]);
+	printf("Features: 0x%16" PRIx64 "%16" PRIx64 "\n", features[1],
+								features[0]);
+
+	p_indent(level, frm);
+
+	charset = p_get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = p_get_u16(frm);
+	printf("NameLength: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("Name: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = p_get_u8(frm);
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+}
+
+static const char *foldertype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "Mixed";
+	case 0x01:
+		return "Titles";
+	case 0x02:
+		return "Albuns";
+	case 0x03:
+		return "Artists";
+	case 0x04:
+		return "Genres";
+	case 0x05:
+		return "Playlists";
+	case 0x06:
+		return "Years";
+	}
+
+	return "Reserved";
+}
+
+static void avrcp_folder_item_dump(int level, struct frame *frm, uint16_t len)
+{
+	uint8_t type, playable;
+	uint16_t charset, namelen;
+	uint64_t uid;
+
+	p_indent(level, frm);
+
+	if (len < 14) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	uid = p_get_u64(frm);
+	printf("FolderUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	type = p_get_u8(frm);
+	printf("FolderType: 0x%02x (%s)\n", type, foldertype2str(type));
+
+	p_indent(level, frm);
+
+	playable = p_get_u8(frm);
+	printf("IsPlayable: 0x%02x (%s)\n", playable,
+					playable & 0x01 ? "True" : "False");
+
+	p_indent(level, frm);
+
+	charset = p_get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = p_get_u16(frm);
+	printf("NameLength: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("Name: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = p_get_u8(frm);
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+}
+
+static const char *elementtype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "Audio";
+	case 0x01:
+		return "Video";
+	}
+
+	return "Reserved";
+}
+
+static void avrcp_attribute_entry_list_dump(int level, struct frame *frm,
+								uint8_t count)
+{
+	for (; count > 0; count--) {
+		uint32_t attr;
+		uint16_t charset;
+		uint8_t len;
+
+		p_indent(level, frm);
+
+		attr = p_get_u32(frm);
+		printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+
+		p_indent(level, frm);
+
+		charset = p_get_u16(frm);
+		printf("CharsetID: 0x%04x (%s)\n", charset,
+							charset2str(charset));
+
+		p_indent(level, frm);
+
+		len = p_get_u16(frm);
+		printf("AttributeLength: 0x%04x (%u)\n", len, len);
+
+		p_indent(level, frm);
+
+		printf("AttributeValue: ");
+		for (; len > 0; len--) {
+			uint8_t c = p_get_u8(frm);
+			printf("%1c", isprint(c) ? c : '.');
+		}
+		printf("\n");
+	}
+}
+
+static void avrcp_media_element_item_dump(int level, struct frame *frm,
+								uint16_t len)
+{
+	uint64_t uid;
+	uint16_t charset, namelen;
+	uint8_t type, count;
+
+	p_indent(level, frm);
+
+	if (len < 14) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	uid = p_get_u64(frm);
+	printf("ElementUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	type = p_get_u8(frm);
+	printf("ElementType: 0x%02x (%s)\n", type, elementtype2str(type));
+
+	p_indent(level, frm);
+
+	charset = p_get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = p_get_u16(frm);
+	printf("NameLength: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("Name: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = p_get_u8(frm);
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+
+	p_indent(level, frm);
+
+	count = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+	avrcp_attribute_entry_list_dump(level, frm, count);
+}
+
+static void avrcp_get_folder_items_dump(int level, struct frame *frm,
+						uint8_t hdr, uint16_t len)
+{
+	uint8_t scope, count, status;
+	uint32_t start, end;
+	uint16_t uid, num;
+
+	p_indent(level, frm);
+
+	if (hdr & 0x02)
+		goto response;
+
+	if (len < 10) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	scope = p_get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	start = p_get_u32(frm);
+	printf("StartItem: 0x%08x (%u)\n", start, start);
+
+	p_indent(level, frm);
+
+	end = p_get_u32(frm);
+	printf("EndItem: 0x%08x (%u)\n", end, end);
+
+	p_indent(level, frm);
+
+	count = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+	for (; count > 0; count--) {
+		uint32_t attr;
+
+		p_indent(level, frm);
+
+		attr = p_get_u32(frm);
+		printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+	}
+
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	uid = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uid, uid);
+
+	p_indent(level, frm);
+
+	num = p_get_u16(frm);
+	printf("Number of Items: 0x%04x (%u)\n", num, num);
+
+	for (; num > 0; num--) {
+		uint8_t type;
+		uint16_t len;
+
+		p_indent(level, frm);
+
+		type = p_get_u8(frm);
+		len = p_get_u16(frm);
+		switch (type) {
+		case 0x01:
+			printf("Item: 0x01 (Media Player)) ");
+			printf("Length: 0x%04x (%u)\n", len, len);
+			avrcp_media_player_item_dump(level, frm, len);
+			break;
+		case 0x02:
+			printf("Item: 0x02 (Folder) ");
+			printf("Length: 0x%04x (%u)\n", len, len);
+			avrcp_folder_item_dump(level, frm, len);
+			break;
+		case 0x03:
+			printf("Item: 0x03 (Media Element) ");
+			printf("Length: 0x%04x (%u)\n", len, len);
+			avrcp_media_element_item_dump(level, frm, len);
+			break;
+		}
+	}
+}
+
+static const char *dir2str(uint8_t dir)
+{
+	switch (dir) {
+	case 0x00:
+		return "Folder Up";
+	case 0x01:
+		return "Folder Down";
+	}
+
+	return "Reserved";
+}
+
+static void avrcp_change_path_dump(int level, struct frame *frm, uint8_t hdr,
+								uint16_t len)
+{
+	uint64_t uid;
+	uint32_t items;
+	uint16_t uidcounter;
+	uint8_t dir, status;
+
+	p_indent(level, frm);
+
+	if (hdr & 0x02)
+		goto response;
+
+	if (len < 11) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	uidcounter = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	p_indent(level, frm);
+
+	dir = p_get_u8(frm);
+	printf("Direction: 0x%02x (%s)\n", dir, dir2str(dir));
+
+	p_indent(level, frm);
+
+	uid = p_get_u64(frm);
+	printf("FolderUID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	items = p_get_u32(frm);
+	printf("Number of Items: 0x%04x (%u)", items, items);
+}
+
+static void avrcp_get_item_attributes_dump(int level, struct frame *frm,
+						uint8_t hdr, uint16_t len)
+{
+	uint64_t uid;
+	uint32_t uidcounter;
+	uint8_t scope, count, status;
+
+	p_indent(level, frm);
+
+	if (hdr & 0x02)
+		goto response;
+
+	if (len < 12) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	scope = p_get_u8(frm);
+	printf("Scope: 0x%02x (%s)\n", scope, scope2str(scope));
+
+	p_indent(level, frm);
+
+	uid = p_get_u64(frm);
+	printf("UID: 0x%16" PRIx64 " (%" PRIu64 ")\n", uid, uid);
+
+	p_indent(level, frm);
+
+	uidcounter = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	p_indent(level, frm);
+
+	count = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+	for (; count > 0; count--) {
+		uint32_t attr;
+
+		p_indent(level, frm);
+
+		attr = p_get_u32(frm);
+		printf("AttributeID: 0x%08x (%s)\n", attr, mediattr2str(attr));
+	}
+
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	count = p_get_u8(frm);
+	printf("AttributeCount: 0x%02x (%u)\n", count, count);
+
+	avrcp_attribute_entry_list_dump(level, frm, count);
+}
+
+static void avrcp_search_dump(int level, struct frame *frm, uint8_t hdr,
+								uint16_t len)
+{
+	uint32_t uidcounter, items;
+	uint16_t charset, namelen;
+	uint8_t status;
+
+	p_indent(level, frm);
+
+	if (hdr & 0x02)
+		goto response;
+
+	if (len < 4) {
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	charset = p_get_u16(frm);
+	printf("CharsetID: 0x%04x (%s)\n", charset, charset2str(charset));
+
+	p_indent(level, frm);
+
+	namelen = p_get_u16(frm);
+	printf("Length: 0x%04x (%u)\n", namelen, namelen);
+
+	p_indent(level, frm);
+
+	printf("String: ");
+	for (; namelen > 0; namelen--) {
+		uint8_t c = p_get_u8(frm);
+		printf("%1c", isprint(c) ? c : '.');
+	}
+	printf("\n");
+
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+
+	if (len == 1)
+		return;
+
+	p_indent(level, frm);
+
+	uidcounter = p_get_u16(frm);
+	printf("UIDCounter: 0x%04x (%u)\n", uidcounter, uidcounter);
+
+	p_indent(level, frm);
+
+	items = p_get_u32(frm);
+	printf("Number of Items: 0x%04x (%u)", items, items);
+}
+
+static void avrcp_general_reject_dump(int level, struct frame *frm,
+						uint8_t hdr, uint16_t len)
+{
+	uint8_t status;
+
+	p_indent(level, frm);
+
+	if (hdr & 0x02)
+		goto response;
+
+	printf("PDU Malformed\n");
+	raw_dump(level, frm);
+	return;
+
+response:
+	status = p_get_u8(frm);
+	printf("Status: 0x%02x (%s)\n", status, error2str(status));
+}
+
+static void avrcp_browsing_dump(int level, struct frame *frm, uint8_t hdr)
+{
+	uint8_t pduid;
+	uint16_t len;
+
+	pduid = p_get_u8(frm);
+	len = p_get_u16(frm);
+
+	printf("AVRCP: %s: len 0x%04x\n", pdu2str(pduid), len);
+
+	if (len != frm->len) {
+		p_indent(level, frm);
+		printf("PDU Malformed\n");
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (pduid) {
+	case AVRCP_SET_BROWSED_PLAYER:
+		avrcp_set_browsed_player_dump(level + 1, frm, hdr, len);
+		break;
+	case AVRCP_GET_FOLDER_ITEMS:
+		avrcp_get_folder_items_dump(level + 1, frm, hdr, len);
+		break;
+	case AVRCP_CHANGE_PATH:
+		avrcp_change_path_dump(level + 1, frm, hdr, len);
+		break;
+	case AVRCP_GET_ITEM_ATTRIBUTES:
+		avrcp_get_item_attributes_dump(level + 1, frm, hdr, len);
+		break;
+	case AVRCP_SEARCH:
+		avrcp_search_dump(level + 1, frm, hdr, len);
+		break;
+	case AVRCP_GENERAL_REJECT:
+		avrcp_general_reject_dump(level + 1, frm, hdr, len);
+		break;
+	default:
+		raw_dump(level, frm);
+	}
+}
+
+static void avrcp_control_dump(int level, struct frame *frm)
+{
+	uint8_t ctype, address, subunit, opcode, company[3];
+	int i;
+
+	ctype = p_get_u8(frm);
+	address = p_get_u8(frm);
+	opcode = p_get_u8(frm);
+
+	printf("AV/C: %s: address 0x%02x opcode 0x%02x\n", ctype2str(ctype),
+							address, opcode);
+
+	p_indent(level + 1, frm);
+
+	subunit = address >> 3;
+	printf("Subunit: %s\n", subunit2str(subunit));
+
+	p_indent(level + 1, frm);
+
+	printf("Opcode: %s\n", opcode2str(opcode));
+
+	/* Skip non-panel subunit packets */
+	if (subunit != AVC_SUBUNIT_PANEL) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	/* Not implemented should not contain any operand */
+	if (ctype == AVC_CTYPE_NOT_IMPLEMENTED) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (opcode) {
+	case AVC_OP_PASSTHROUGH:
+		avrcp_passthrough_dump(level + 1, frm);
+		break;
+	case AVC_OP_VENDORDEP:
+		p_indent(level + 1, frm);
+
+		printf("Company ID: 0x");
+		for (i = 0; i < 3; i++) {
+			company[i] = p_get_u8(frm);
+			printf("%02x", company[i]);
+		}
+		printf("\n");
+
+		avrcp_pdu_dump(level + 1, frm, ctype);
+		break;
+	default:
+		raw_dump(level, frm);
+	}
+}
+
+void avrcp_dump(int level, struct frame *frm, uint8_t hdr, uint16_t psm)
+{
+	p_indent(level, frm);
+
+	switch (psm) {
+		case 0x17:
+			avrcp_control_dump(level, frm);
+			break;
+		case 0x1B:
+			avrcp_browsing_dump(level, frm, hdr);
+			break;
+		default:
+			raw_dump(level, frm);
+	}
+}
diff --git a/bluez/repo/tools/parser/bnep.c b/bluez/repo/tools/parser/bnep.c
new file mode 100644
index 0000000..7c549f4
--- /dev/null
+++ b/bluez/repo/tools/parser/bnep.c
@@ -0,0 +1,317 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Takashi Sasai <sasai@sm.sony.co.jp>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <net/ethernet.h>
+
+#include "parser.h"
+
+/* BNEP Type */
+#define BNEP_GENERAL_ETHERNET			0x00
+#define BNEP_CONTROL				0x01
+#define BNEP_COMPRESSED_ETHERNET		0x02
+#define BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY	0x03
+#define BNEP_COMPRESSED_ETHERNET_DEST_ONLY	0x04
+
+/* BNEP Control Packet Type */
+#define BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD	0x00
+#define BNEP_SETUP_CONNECTION_REQUEST_MSG	0x01
+#define BNEP_SETUP_CONNECTION_RESPONSE_MSG	0x02
+#define BNEP_FILTER_NET_TYPE_SET_MSG		0x03
+#define BNEP_FILTER_NET_TYPE_RESPONSE_MSG	0x04
+#define BNEP_FILTER_MULT_ADDR_SET_MSG		0x05
+#define BNEP_FILTER_MULT_ADDR_RESPONSE_MSG	0x06
+
+/* BNEP Extension Type */
+#define BNEP_EXTENSION_CONTROL			0x00
+
+#ifndef ETHERTYPE_IPV6
+#define ETHERTYPE_IPV6 ETH_P_IPV6
+#endif
+
+static char *get_macaddr(struct frame *frm)
+{
+	static char str[20];
+	unsigned char *buf = frm->ptr;
+
+	sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+		buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+	frm->ptr += 6;
+	frm->len -= 6;
+
+	return str;
+}
+
+static void bnep_control(int level, struct frame *frm, int header_length)
+{
+	uint8_t uuid_size;
+	int i, length;
+	char *s;
+	uint32_t uuid = 0;
+	uint8_t type = p_get_u8(frm);
+
+	p_indent(++level, frm);
+	switch (type) {
+	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
+		printf("Not Understood(0x%02x) type 0x%02x\n", type, p_get_u8(frm));
+		break;
+
+	case BNEP_SETUP_CONNECTION_REQUEST_MSG:
+		uuid_size = p_get_u8(frm);
+		printf("Setup Req(0x%02x) size 0x%02x ", type, uuid_size);
+		switch (uuid_size) {
+		case 2:
+			uuid = p_get_u16(frm);
+			printf("dst 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			uuid = p_get_u16(frm);
+			printf(" src 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			printf("\n");
+			break;
+		case 4:
+			uuid = p_get_u32(frm);
+			printf("dst 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			uuid = p_get_u32(frm);
+			printf(" src 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			printf("\n");
+			break;
+		case 16:
+			uuid = p_get_u32(frm);
+			printf("dst 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			frm->ptr += 12;
+			frm->len -= 12;
+			uuid = p_get_u32(frm);
+			printf(" src 0x%x", uuid);
+			if ((s = get_uuid_name(uuid)) != 0)
+				printf("(%s)", s);
+			printf("\n");
+			frm->ptr += 12;
+			frm->len -= 12;
+			break;
+		default:
+			frm->ptr += (uuid_size * 2);
+			frm->len -= (uuid_size * 2);
+			break;
+		}
+		break;
+
+	case BNEP_SETUP_CONNECTION_RESPONSE_MSG:
+		printf("Setup Rsp(0x%02x) res 0x%04x\n",
+						type, p_get_u16(frm));
+		break;
+
+	case BNEP_FILTER_NET_TYPE_SET_MSG:
+		length = p_get_u16(frm);
+		printf("Filter NetType Set(0x%02x) len 0x%04x\n",
+							type, length);
+		for (i = 0; i < length / 4; i++) {
+			p_indent(level + 1, frm);
+			printf("0x%04x - ", p_get_u16(frm));
+			printf("0x%04x\n", p_get_u16(frm));
+		}
+		break;
+
+	case BNEP_FILTER_NET_TYPE_RESPONSE_MSG:
+		printf("Filter NetType Rsp(0x%02x) res 0x%04x\n",
+							type, p_get_u16(frm));
+		break;
+
+	case BNEP_FILTER_MULT_ADDR_SET_MSG:
+		length = p_get_u16(frm);
+		printf("Filter MultAddr Set(0x%02x) len 0x%04x\n",
+							type, length);
+		for (i = 0; i < length / 12; i++) {
+			p_indent(level + 1, frm);
+			printf("%s - ", get_macaddr(frm));
+			printf("%s\n", get_macaddr(frm));
+		}
+		break;
+
+	case BNEP_FILTER_MULT_ADDR_RESPONSE_MSG:
+		printf("Filter MultAddr Rsp(0x%02x) res 0x%04x\n",
+							type, p_get_u16(frm));
+		break;
+
+	default:
+		printf("Unknown control type(0x%02x)\n", type);
+		raw_ndump(level + 1, frm, header_length - 1);
+		frm->ptr += header_length - 1;
+		frm->len -= header_length - 1;
+		return;
+	}
+}
+
+static void bnep_eval_extension(int level, struct frame *frm)
+{
+	uint8_t type = p_get_u8(frm);
+	uint8_t length = p_get_u8(frm);
+	int extension = type & 0x80;
+
+	p_indent(level, frm);
+
+	switch (type & 0x7f) {
+	case BNEP_EXTENSION_CONTROL:
+		printf("Ext Control(0x%02x|%s) len 0x%02x\n",
+				type & 0x7f, extension ? "1" : "0", length);
+		bnep_control(level, frm, length);
+		break;
+
+	default:
+		printf("Ext Unknown(0x%02x|%s) len 0x%02x\n",
+				type & 0x7f, extension ? "1" : "0", length);
+		raw_ndump(level + 1, frm, length);
+		frm->ptr += length;
+		frm->len -= length;
+	}
+
+	if (extension)
+		bnep_eval_extension(level, frm);
+}
+
+void bnep_dump(int level, struct frame *frm)
+{
+	uint8_t type = p_get_u8(frm);
+	uint16_t proto = 0x0000;
+	int extension = type & 0x80;
+
+	p_indent(level, frm);
+
+	switch (type & 0x7f) {
+	case BNEP_CONTROL:
+		printf("BNEP: Control(0x%02x|%s)\n",
+					type & 0x7f, extension ? "1" : "0");
+		bnep_control(level, frm, -1);
+		break;
+
+	case BNEP_COMPRESSED_ETHERNET:
+		printf("BNEP: Compressed(0x%02x|%s)\n",
+					type & 0x7f, extension ? "1" : "0");
+		p_indent(++level, frm);
+		proto = p_get_u16(frm);
+		printf("[proto 0x%04x]\n", proto);
+		break;
+
+	case BNEP_GENERAL_ETHERNET:
+		printf("BNEP: General ethernet(0x%02x|%s)\n",
+					type & 0x7f, extension ? "1" : "0");
+		p_indent(++level, frm);
+		printf("dst %s ", get_macaddr(frm));
+		printf("src %s ", get_macaddr(frm));
+		proto = p_get_u16(frm);
+		printf("[proto 0x%04x]\n", proto);
+		break;
+
+	case BNEP_COMPRESSED_ETHERNET_DEST_ONLY:
+		printf("BNEP: Compressed DestOnly(0x%02x|%s)\n",
+					type & 0x7f, extension ? "1" : "0");
+		p_indent(++level, frm);
+		printf("dst %s ", get_macaddr(frm));
+		proto = p_get_u16(frm);
+		printf("[proto 0x%04x]\n", proto);
+		break;
+
+	case BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY:
+		printf("BNEP: Compressed SrcOnly(0x%02x|%s)\n",
+					type & 0x7f, extension ? "1" : "0");
+		p_indent(++level, frm);
+		printf("src %s ", get_macaddr(frm));
+		proto = p_get_u16(frm);
+		printf("[proto 0x%04x]\n", proto);
+		break;
+
+	default:
+		printf("(Unknown packet type)\n");
+		return;
+	}
+
+	/* Extension info */
+	if (extension)
+		bnep_eval_extension(++level, frm);
+
+	/* Control packet => No payload info */
+	if ((type & 0x7f) == BNEP_CONTROL)
+		return;
+
+	/* 802.1p header */
+	if (proto == 0x8100) {
+		p_indent(level, frm);
+		printf("802.1p Header: 0x%04x ", p_get_u16(frm));
+		proto = p_get_u16(frm);
+		printf("[proto 0x%04x]\n", proto);
+	}
+
+	if (!(parser.flags & DUMP_VERBOSE)) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (proto) {
+	case ETHERTYPE_ARP:
+		p_indent(++level, frm);
+		printf("ARP: ");
+		arp_dump(level, frm);
+		break;
+
+	case ETHERTYPE_REVARP:
+		p_indent(++level, frm);
+		printf("RARP: ");
+		arp_dump(level, frm);
+		break;
+
+	case ETHERTYPE_IP:
+		p_indent(++level, frm);
+		printf("IP: ");
+		ip_dump(level, frm);
+		break;
+
+	case ETHERTYPE_IPV6:
+		p_indent(++level, frm);
+		printf("IPV6: ");
+		ip_dump(level, frm);
+		break;
+
+	default:
+		raw_dump(level, frm);
+		break;
+	}
+}
diff --git a/bluez/repo/tools/parser/bpa.c b/bluez/repo/tools/parser/bpa.c
new file mode 100644
index 0000000..74a3457
--- /dev/null
+++ b/bluez/repo/tools/parser/bpa.c
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define BPA_U8(frm)  (p_get_u8(frm))
+#define BPA_U16(frm) (btohs(htons(p_get_u16(frm))))
+#define BPA_U32(frm) (btohl(htonl(p_get_u32(frm))))
+
+void bpa_dump(int level, struct frame *frm)
+{
+	uint8_t id, status, channel;
+	uint16_t num, len;
+	uint32_t time;
+
+	id = p_get_u8(frm);
+	num = p_get_u16(frm);
+	len = BPA_U16(frm);
+
+	status  = p_get_u8(frm);
+	time    = p_get_u32(frm);
+	channel = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("BPA: id %d num %d len %u status 0x%02x time %d channel %d\n",
+		id, num, len, status, time, channel);
+
+	raw_dump(level, frm);
+}
diff --git a/bluez/repo/tools/parser/capi.c b/bluez/repo/tools/parser/capi.c
new file mode 100644
index 0000000..b8d05c2
--- /dev/null
+++ b/bluez/repo/tools/parser/capi.c
@@ -0,0 +1,843 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define CAPI_U8(frm)  (p_get_u8(frm))
+#define CAPI_U16(frm) (btohs(htons(p_get_u16(frm))))
+#define CAPI_U32(frm) (btohl(htonl(p_get_u32(frm))))
+
+static char *cmd2str(uint8_t cmd)
+{
+	switch (cmd) {
+	case 0x01:
+		return "ALERT";
+	case 0x02:
+		return "CONNECT";
+	case 0x03:
+		return "CONNECT_ACTIVE";
+	case 0x04:
+		return "DISCONNECT";
+	case 0x05:
+		return "LISTEN";
+	case 0x08:
+		return "INFO";
+	case 0x20:
+		return "INTEROPERABILITY";
+	case 0x41:
+		return "SELECT_B_PROTOCOL";
+	case 0x80:
+		return "FACILITY";
+	case 0x82:
+		return "CONNECT_B3";
+	case 0x83:
+		return "CONNECT_B3_ACTIVE";
+	case 0x84:
+		return "DISCONNECT_B3";
+	case 0x86:
+		return "DATA_B3";
+	case 0x87:
+		return "RESET_B3";
+	case 0x88:
+		return "CONNECT_B3_T90_ACTIVE";
+	case 0xff:
+		return "MANUFACTURER";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static char *subcmd2str(uint8_t subcmd)
+{
+	switch (subcmd) {
+	case 0x80:
+		return "REQ";
+	case 0x81:
+		return "CONF";
+	case 0x82:
+		return "IND";
+	case 0x83:
+		return "RESP";
+	default:
+		return "UNKN";
+	}
+}
+
+static char *interopsel2str(uint16_t sel)
+{
+	switch (sel) {
+	case 0x0000:
+		return "USB Device Management";
+	case 0x0001:
+		return "Bluetooth Device Management";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *func2str(uint16_t func)
+{
+	switch (func) {
+	case 0:
+		return "Register";
+	case 1:
+		return "Release";
+	case 2:
+		return "Get_Profile";
+	case 3:
+		return "Get_Manufacturer";
+	case 4:
+		return "Get_Version";
+	case 5:
+		return "Get_Serial_Number";
+	case 6:
+		return "Manufacturer";
+	case 7:
+		return "Echo_Loopback";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *facilitysel2str(uint16_t sel)
+{
+	switch (sel) {
+	case 0x0000:
+		return "Handset";
+	case 0x0001:
+		return "DTMF";
+	case 0x0002:
+		return "V.42 bis";
+	case 0x0003:
+		return "Supplementary Services";
+	case 0x0004:
+		return "Power management wakeup";
+	case 0x0005:
+		return "Line Interconnect";
+	case 0x0006:
+		return "DTMF";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *info2str(uint16_t info)
+{
+	switch (info) {
+	case 0x0000:
+		return "No error";
+	case 0x0001:
+		return "NCPI not supported by current protocol, NCPI ignored";
+	case 0x0002:
+		return "Flags not supported by current protocol, flags ignored";
+	case 0x2001:
+		return "Message not supported in current state";
+	case 0x2002:
+		return "Incorrect Controller/PLCI/NCCI";
+	case 0x2003:
+		return "No PLCI available";
+	case 0x2004:
+		return "No NCCI available";
+	case 0x2005:
+		return "No Listen resources available";
+	case 0x2007:
+		return "Illegal message parameter coding";
+	case 0x2008:
+		return "No interconnection resources available";
+	case 0x3001:
+		return "B1 protocol not supported";
+	case 0x3002:
+		return "B2 protocol not supported";
+	case 0x3003:
+		return "B3 protocol not supported";
+	case 0x3004:
+		return "B1 protocol parameter not supported";
+	case 0x3005:
+		return "B2 protocol parameter not supported";
+	case 0x3006:
+		return "B3 protocol parameter not supported";
+	case 0x3007:
+		return "B protocol combination not supported";
+	case 0x3008:
+		return "NCPI not supported";
+	case 0x3009:
+		return "CIP Value unknown";
+	case 0x300A:
+		return "Flags not supported (reserved bits)";
+	case 0x300B:
+		return "Facility not supported";
+	case 0x300C:
+		return "Data length not supported by current protocol";
+	case 0x300D:
+		return "Reset procedure not supported by current protocol";
+	case 0x300F:
+		return "Unsupported interoperability";
+	case 0x3011:
+		return "Facility specific function not supported";
+	case 0x3301:
+		return "Protocol error, Layer 1";
+	case 0x3302:
+		return "Protocol error, Layer 2";
+	case 0x3303:
+		return "Protocol error, Layer 3";
+	case 0x3304:
+		return "Another application got that call";
+	case 0x3305:
+		return "Cleared by Call Control Supervision";
+	case 0x3400:
+		/* The cause value received from the network in a cause
+		 * information element (Octet 4) is indicated in the field 00 */
+		return "Disconnect cause from the network in accordance with Q.850/ETS 300 102-1";
+	default:
+		return "Unknown";
+	}
+}
+
+static void profile(int level, struct frame *frm)
+{
+	uint16_t nctr, nchn;
+	uint32_t value;
+
+	nctr = CAPI_U16(frm);
+	nchn = CAPI_U16(frm);
+
+	if (nchn > 0) {
+		p_indent(level, frm);
+		printf("Controller: %d\n", nctr);
+		p_indent(level, frm);
+		printf("Number of B-channels: %d\n", nchn);
+
+		value = CAPI_U32(frm);
+		p_indent(level, frm);
+		printf("Global options: 0x%04x\n", value);
+		value = CAPI_U32(frm);
+		p_indent(level, frm);
+		printf("B1 protocol support: 0x%08x\n", value);
+		value = CAPI_U32(frm);
+		p_indent(level, frm);
+		printf("B2 protocol support: 0x%08x\n", value);
+		value = CAPI_U32(frm);
+		p_indent(level, frm);
+		printf("B3 protocol support: 0x%08x\n", value);
+
+		frm->ptr += 24;
+		frm->len -= 24;
+
+		p_indent(level, frm);
+		printf("Manufacturer-specific information:\n");
+		hex_dump(level, frm, 20);
+	} else {
+		p_indent(level, frm);
+		printf("Number of controllers: %d\n", nctr);
+	}
+}
+
+static void cmd_common(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint32_t val;
+	uint16_t info, ncci;
+	uint8_t ctr, plci;
+
+	val = CAPI_U32(frm);
+	ctr = val & 0xff;
+	plci = (val & 0xff00) >> 8;
+	ncci = (val & 0xffff0000) >> 16;
+
+	p_indent(level, frm);
+	printf("Controller: %d %s\n", ctr & 0x7f, ctr & 0x80 ? "Ext." : "Int.");
+
+	if (plci > 0) {
+		p_indent(level, frm);
+		printf("PLCI: 0x%02x\n", plci);
+	}
+
+	if (ncci > 0) {
+		p_indent(level, frm);
+		printf("NCCI: 0x%04x\n", ncci);
+	}
+
+	if (subcmd == 0x81) {
+		info = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Info: 0x%04x (%s)\n", info, info2str(info));
+	}
+}
+
+static void cmd_alert(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x80) {
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Additional info:\n");
+			hex_dump(level, frm, len);
+		}
+	}
+}
+
+static void cmd_connect(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint16_t cip;
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x81)
+		return;
+
+	cip = CAPI_U16(frm);
+	p_indent(level, frm);
+	printf("CIP value: 0x%04x\n", cip);
+
+	len = CAPI_U8(frm);
+	frm->ptr += len;
+	frm->len -= len;
+	len = CAPI_U8(frm);
+	frm->ptr += len;
+	frm->len -= len;
+	len = CAPI_U8(frm);
+	frm->ptr += len;
+	frm->len -= len;
+	len = CAPI_U8(frm);
+	frm->ptr += len;
+	frm->len -= len;
+
+	raw_dump(level, frm);
+}
+
+static void cmd_disconnect(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint16_t reason;
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x80) {
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Additional info:\n");
+			hex_dump(level, frm, len);
+		}
+	}
+
+	if (subcmd == 0x82) {
+		reason = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Reason: 0x%04x (%s)\n", reason, info2str(reason));
+	}
+}
+
+static void cmd_connect_active(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x82) {
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Connected number:\n");
+			hex_dump(level, frm, len);
+		}
+
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Connected subaddress:\n");
+			hex_dump(level, frm, len);
+		}
+
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("LLC:\n");
+			hex_dump(level, frm, len);
+		}
+	}
+}
+
+static void cmd_listen(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint32_t mask;
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x80) {
+		mask = CAPI_U32(frm);
+		p_indent(level, frm);
+		printf("Info mask: 0x%08x\n", mask);
+
+		mask = CAPI_U32(frm);
+		p_indent(level, frm);
+		printf("CIP mask:  0x%08x", mask);
+
+		mask = CAPI_U32(frm);
+		if (mask > 0)
+			printf(" 0x%08x\n", mask);
+		else
+			printf("\n");
+
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Calling party number:\n");
+			hex_dump(level, frm, len);
+		}
+		frm->ptr += len;
+		frm->len -= len;
+
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Calling party subaddress:\n");
+			hex_dump(level, frm, len);
+		}
+		frm->ptr += len;
+		frm->len -= len;
+	}
+}
+
+static void cmd_info(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint8_t len;
+	uint16_t info;
+
+	cmd_common(level, subcmd, frm);
+
+	switch (subcmd) {
+	case 0x80:
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Called party number:\n");
+			hex_dump(level, frm, len);
+		}
+		frm->ptr += len;
+		frm->len -= len;
+
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Additional info:\n");
+			hex_dump(level, frm, len);
+		}
+		break;
+
+	case 0x82:
+		info = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Info number: %d\n", info);
+
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("Info element:\n");
+			hex_dump(level, frm, len);
+		}
+		break;
+	}
+}
+
+static void cmd_interoperability(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint16_t sel, func, info;
+	uint16_t nconn, datablkcnt, datablklen;
+	uint32_t ctr, value, major, minor;
+
+	info = (subcmd == 0x81) ? CAPI_U16(frm) : 0;
+	sel = CAPI_U16(frm);
+	CAPI_U8(frm);
+	if (subcmd != 0x83) {
+		func = CAPI_U16(frm);
+		CAPI_U8(frm);
+	} else
+		func = 0;
+
+	p_indent(level, frm);
+	printf("Selector: 0x%04x (%s)\n", sel, interopsel2str(sel));
+
+	switch (sel) {
+	case 0x0001:
+		p_indent(level, frm);
+		printf("Function: %d (%s)\n", func, func2str(func));
+
+		switch (subcmd) {
+		case 0x80:
+			switch (func) {
+			case 0:
+				nconn = CAPI_U16(frm);
+				p_indent(level + 1, frm);
+				printf("maxLogicalConnections: %d\n", nconn);
+				datablkcnt = CAPI_U16(frm);
+				p_indent(level + 1, frm);
+				printf("maxBDataBlocks: %d\n", datablkcnt);
+				datablklen = CAPI_U16(frm);
+				p_indent(level + 1, frm);
+				printf("maxBDataLen: %d\n", datablklen);
+				break;
+			case 2:
+			case 3:
+			case 4:
+			case 5:
+				ctr = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Controller: %d\n", ctr);
+				break;
+			default:
+				raw_dump(level + 1, frm);
+				break;
+			}
+			break;
+
+		case 0x81:
+			switch (func) {
+			case 0:
+			case 1:
+				info = CAPI_U16(frm);
+				p_indent(level + 1, frm);
+				printf("Info: 0x%04x (%s)\n", info, info2str(info));
+				break;
+			case 2:
+				info = CAPI_U16(frm);
+				p_indent(level + 1, frm);
+				printf("Info: 0x%04x (%s)\n", info, info2str(info));
+				CAPI_U8(frm);
+				profile(level + 1, frm);
+				break;
+			case 3:
+				info = CAPI_U16(frm);
+				p_indent(level + 1, frm);
+				printf("Info: 0x%04x (%s)\n", info, info2str(info));
+				ctr = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Controller: %d\n", ctr);
+				CAPI_U8(frm);
+				p_indent(level + 1, frm);
+				printf("Identification: \"%s\"\n", (char *) frm->ptr);
+				break;
+			case 4:
+				value = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Return value: 0x%04x\n", value);
+				ctr = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Controller: %d\n", ctr);
+				p_indent(level + 1, frm);
+				major = CAPI_U32(frm);
+				minor = CAPI_U32(frm);
+				printf("CAPI: %d.%d\n", major, minor);
+				major = CAPI_U32(frm);
+				minor = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Manufacture: %u.%01x%01x-%02u (%d.%d)\n",
+					(major & 0xf0) >> 4, (major & 0x0f) << 4,
+					(minor & 0xf0) >> 4, minor & 0x0f,
+					major, minor);
+				break;
+			case 5:
+				value = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Return value: 0x%04x\n", value);
+				ctr = CAPI_U32(frm);
+				p_indent(level + 1, frm);
+				printf("Controller: %d\n", ctr);
+				CAPI_U8(frm);
+				p_indent(level + 1, frm);
+				printf("Serial number: %.7s\n", (char *) frm->ptr);
+				break;
+			default:
+				raw_dump(level + 1, frm);
+				break;
+			}
+			break;
+
+		default:
+			raw_dump(level, frm);
+			break;
+		}
+		break;
+
+	default:
+		p_indent(level, frm);
+		printf("Function: %d\n", func);
+		if (subcmd == 0x81) {
+			p_indent(level, frm);
+			printf("Info: 0x%04x (%s)\n", info, info2str(info));
+		}
+		raw_dump(level + 1, frm);
+		break;
+	}
+}
+
+static void cmd_facility(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint16_t sel;
+
+	cmd_common(level, subcmd, frm);
+
+	sel = CAPI_U16(frm);
+	CAPI_U8(frm);
+
+	p_indent(level, frm);
+	printf("Selector: 0x%04x (%s)\n", sel, facilitysel2str(sel));
+
+	raw_dump(level, frm);
+}
+
+static void cmd_connect_b3(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint16_t reject;
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x81)
+		return;
+
+	if (subcmd == 0x83) {
+		reject = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Reject: 0x%04x (%s)\n", reject, info2str(reject));
+	}
+
+	len = CAPI_U8(frm);
+	if (len > 0) {
+		p_indent(level, frm);
+		printf("NCPI:\n");
+		hex_dump(level, frm, len);
+	}
+}
+
+static void cmd_connect_b3_active(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x82) {
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("NCPI:\n");
+			hex_dump(level, frm, len);
+		}
+	}
+}
+
+static void cmd_disconnect_b3(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint16_t reason;
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x82) {
+		reason = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Reason: 0x%04x (%s)\n", reason, info2str(reason));
+	}
+
+	if (subcmd == 0x80 || subcmd == 0x82) {
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("NCPI:\n");
+			hex_dump(level, frm, len);
+		}
+	}
+}
+
+static void cmd_data_b3(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint32_t data;
+	uint16_t length, handle, flags, info;
+
+	cmd_common(level, 0x00, frm);
+
+	if (subcmd == 0x81 || subcmd == 0x83) {
+		handle = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Data handle: 0x%04x\n", handle);
+
+		if (subcmd == 0x81) {
+			info = CAPI_U16(frm);
+			p_indent(level, frm);
+			printf("Info: 0x%04x (%s)\n", info, info2str(info));
+		}
+	} else {
+		data = CAPI_U32(frm);
+
+		length = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Data length: 0x%04x (%d bytes)\n", length, length);
+
+		handle = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Data handle: 0x%04x\n", handle);
+
+		flags = CAPI_U16(frm);
+		p_indent(level, frm);
+		printf("Flags: 0x%04x\n", flags);
+
+		if (data == 0)
+			(void) p_get_u64(frm);
+
+		raw_dump(level, frm);
+	}
+}
+
+static void cmd_reset_b3(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint8_t len;
+
+	cmd_common(level, subcmd, frm);
+
+	if (subcmd == 0x80 || subcmd == 0x82) {
+		len = CAPI_U8(frm);
+		if (len > 0) {
+			p_indent(level, frm);
+			printf("NCPI:\n");
+			hex_dump(level, frm, len);
+		}
+	}
+}
+
+static void cmd_manufacturer(int level, uint8_t subcmd, struct frame *frm)
+{
+	uint32_t ctr, class, func;
+	uint16_t len;
+	unsigned char *id;
+
+	ctr = CAPI_U32(frm);
+	p_indent(level, frm);
+	printf("Controller: %d\n", ctr);
+
+	id = (unsigned char *) frm->ptr;
+	p_indent(level, frm);
+	if (isprint(id[0]) && isprint(id[1]) && isprint(id[2]) && isprint(id[3]))
+		printf("Manufacturer: %.4s", id);
+	else
+		printf("Manufacturer: 0x%02x 0x%02x 0x%02x 0x%02x",
+						id[0], id[1], id[2], id[3]);
+	frm->ptr += 4;
+	frm->len -= 4;
+
+	if (!strncmp((char *) id, "AVM!", 4)) {
+		class = CAPI_U32(frm);
+		func = CAPI_U32(frm);
+		len = CAPI_U8(frm);
+		if (len == 0xff)
+			len = CAPI_U16(frm);
+
+		printf(" [class %d func %d len %d]\n", class, func, len);
+	} else
+		printf("\n");
+
+	raw_dump(level, frm);
+}
+
+void capi_dump(int level, struct frame *frm)
+{
+	uint16_t len, appl, msgnum;
+	uint8_t cmd, subcmd;
+
+	len = CAPI_U16(frm) - 8;
+	appl = CAPI_U16(frm);
+	cmd = CAPI_U8(frm);
+	subcmd = CAPI_U8(frm);
+	msgnum = CAPI_U16(frm);
+
+	p_indent(level, frm);
+
+	printf("CAPI_%s_%s: appl %d msgnum %d len %d\n",
+			cmd2str(cmd), subcmd2str(subcmd), appl, msgnum, len);
+
+	switch (cmd) {
+	case 0x01:
+		cmd_alert(level + 1, subcmd, frm);
+		break;
+	case 0x02:
+		cmd_connect(level + 1, subcmd, frm);
+		break;
+	case 0x03:
+		cmd_connect_active(level + 1, subcmd, frm);
+		break;
+	case 0x04:
+		cmd_disconnect(level + 1, subcmd, frm);
+		break;
+	case 0x05:
+		cmd_listen(level + 1, subcmd, frm);
+		break;
+	case 0x08:
+		cmd_info(level + 1, subcmd, frm);
+		break;
+	case 0x20:
+		cmd_interoperability(level + 1, subcmd, frm);
+		break;
+	case 0x80:
+		cmd_facility(level + 1, subcmd, frm);
+		break;
+	case 0x82:
+		cmd_connect_b3(level + 1, subcmd, frm);
+		break;
+	case 0x83:
+	case 0x88:
+		cmd_connect_b3_active(level + 1, subcmd, frm);
+		break;
+	case 0x84:
+		cmd_disconnect_b3(level + 1, subcmd, frm);
+		break;
+	case 0x86:
+		cmd_data_b3(level + 1, subcmd, frm);
+		break;
+	case 0x87:
+		cmd_reset_b3(level + 1, subcmd, frm);
+		break;
+	case 0xff:
+		cmd_manufacturer(level + 1, subcmd, frm);
+		break;
+	default:
+		raw_dump(level, frm);
+		frm->ptr += len;
+		frm->len -= len;
+		break;
+	}
+}
diff --git a/bluez/repo/tools/parser/cmtp.c b/bluez/repo/tools/parser/cmtp.c
new file mode 100644
index 0000000..ed5d13b
--- /dev/null
+++ b/bluez/repo/tools/parser/cmtp.c
@@ -0,0 +1,208 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define TABLE_SIZE 10
+
+static struct {
+	uint16_t handle;
+	uint16_t cid;
+	struct frame msg[16];
+} table[TABLE_SIZE];
+
+static void add_segment(uint8_t bid, struct frame *frm, int len)
+{
+	uint16_t handle = frm->handle, cid = frm->cid;
+	struct frame *msg;
+	void *data;
+	int i, pos = -1;
+
+	if (bid > 15)
+		return;
+
+	for (i = 0; i < TABLE_SIZE; i++) {
+		if (table[i].handle == handle && table[i].cid == cid) {
+			pos = i;
+			break;
+		}
+
+		if (pos < 0 && !table[i].handle && !table[i].cid)
+			pos = i;
+	}
+
+	if (pos < 0)
+		return;
+
+	table[pos].handle = handle;
+	table[pos].cid    = cid;
+	msg = &table[pos].msg[bid];
+
+	data = malloc(msg->data_len + len);
+	if (!data)
+		return;
+
+	if (msg->data_len > 0)
+		memcpy(data, msg->data, msg->data_len);
+
+	memcpy(data + msg->data_len, frm->ptr, len);
+	free(msg->data);
+	msg->data = data;
+	msg->data_len += len;
+	msg->ptr = msg->data;
+	msg->len = msg->data_len;
+	msg->in  = frm->in;
+	msg->ts  = frm->ts;
+	msg->handle = handle;
+	msg->cid    = cid;
+}
+
+static void free_segment(uint8_t bid, struct frame *frm)
+{
+	uint16_t handle = frm->handle, cid = frm->cid;
+	struct frame *msg;
+	int i, len = 0, pos = -1;
+
+	if (bid > 15)
+		return;
+
+	for (i = 0; i < TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid) {
+			pos = i;
+			break;
+		}
+
+	if (pos < 0)
+		return;
+
+	msg = &table[pos].msg[bid];
+
+	if (msg->data)
+		free(msg->data);
+
+	msg->data = NULL;
+	msg->data_len = 0;
+
+	for (i = 0; i < 16; i++)
+		len += table[pos].msg[i].data_len;
+
+	if (!len) {
+		table[pos].handle = 0;
+		table[pos].cid = 0;
+	}
+}
+
+static struct frame *get_segment(uint8_t bid, struct frame *frm)
+{
+	uint16_t handle = frm->handle, cid = frm->cid;
+	int i;
+
+	if (bid > 15)
+		return NULL;
+
+	for (i = 0; i < TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			return &table[i].msg[bid];
+
+	return NULL;
+}
+
+static char *bst2str(uint8_t bst)
+{
+	switch (bst) {
+	case 0x00:
+		return "complete CAPI Message";
+	case 0x01:
+		return "segmented CAPI Message";
+	case 0x02:
+		return "error";
+	case 0x03:
+		return "reserved";
+	default:
+		return "unknown";
+	}
+}
+
+void cmtp_dump(int level, struct frame *frm)
+{
+	struct frame *msg;
+	uint8_t hdr, bid;
+	uint16_t len;
+
+	while (frm->len > 0) {
+
+		hdr = p_get_u8(frm);
+		bid = (hdr & 0x3c) >> 2;
+
+		switch ((hdr & 0xc0) >> 6) {
+		case 0x01:
+			len = p_get_u8(frm);
+			break;
+		case 0x02:
+			len = htons(p_get_u16(frm));
+			break;
+		default:
+			len = 0;
+			break;
+		}
+
+		p_indent(level, frm);
+
+		printf("CMTP: %s: id %d len %d\n", bst2str(hdr & 0x03), bid, len);
+
+		switch (hdr & 0x03) {
+		case 0x00:
+			add_segment(bid, frm, len);
+			msg = get_segment(bid, frm);
+			if (!msg)
+				break;
+
+			if (!p_filter(FILT_CAPI))
+				capi_dump(level + 1, msg);
+			else
+				raw_dump(level, msg);
+
+			free_segment(bid, frm);
+			break;
+		case 0x01:
+			add_segment(bid, frm, len);
+			break;
+		default:
+			free_segment(bid, frm);
+			break;
+		}
+
+		frm->ptr += len;
+		frm->len -= len;
+	}
+}
diff --git a/bluez/repo/tools/parser/csr.c b/bluez/repo/tools/parser/csr.c
new file mode 100644
index 0000000..a0a4eb5
--- /dev/null
+++ b/bluez/repo/tools/parser/csr.c
@@ -0,0 +1,625 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define CSR_U8(frm)  (p_get_u8(frm))
+#define CSR_U16(frm) (btohs(htons(p_get_u16(frm))))
+#define CSR_U32(frm) ((CSR_U16(frm) << 16) + CSR_U16(frm))
+#define CSR_S16(frm) (btohs(htons(p_get_u16(frm))))
+
+static char *type2str(uint16_t type)
+{
+	switch (type) {
+	case 0x0000:
+		return "Get req";
+	case 0x0001:
+		return "Get rsp";
+	case 0x0002:
+		return "Set req";
+	default:
+		return "Reserved";
+	}
+}
+
+static inline void valueless_dump(int level, char *str, struct frame *frm)
+{
+	p_indent(level, frm);
+	printf("%s\n", str);
+}
+
+static inline void complex_dump(int level, char *str, struct frame *frm)
+{
+	p_indent(level, frm);
+	printf("%s\n", str);
+
+	raw_dump(level, frm);
+}
+
+static inline void bool_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t value;
+
+	value = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: value %s (%d)\n", str, value ? "TRUE" : "FALSE", value);
+}
+
+static inline void int8_dump(int level, char *str, struct frame *frm)
+{
+	int16_t value;
+
+	value = CSR_S16(frm);
+
+	p_indent(level, frm);
+	printf("%s: value %d (0x%2.2x)\n", str, value, value);
+}
+
+static inline void int16_dump(int level, char *str, struct frame *frm)
+{
+	int16_t value;
+
+	value = CSR_S16(frm);
+
+	p_indent(level, frm);
+	printf("%s: value %d (0x%2.2x)\n", str, value, value);
+}
+
+static inline void uint16_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t value;
+
+	value = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: value %d (0x%4.4x)\n", str, value, value);
+}
+
+static inline void uint32_dump(int level, char *str, struct frame *frm)
+{
+	uint32_t value;
+
+	value = CSR_U32(frm);
+
+	p_indent(level, frm);
+	printf("%s: value %d (0x%4.4x)\n", str, value, value);
+}
+
+static inline void bdaddr_dump(int level, char *str, struct frame *frm)
+{
+	char addr[18];
+
+	p_ba2str(frm->ptr, addr);
+
+	p_indent(level, frm);
+	printf("%s: bdaddr %s\n", str, addr);
+}
+
+static inline void features_dump(int level, char *str, struct frame *frm)
+{
+	unsigned char features[8];
+	int i;
+
+	memcpy(features, frm->ptr, 8);
+
+	p_indent(level, frm);
+	printf("%s: features", str);
+	for (i = 0; i < 8; i++)
+		printf(" 0x%02x", features[i]);
+	printf("\n");
+}
+
+static inline void commands_dump(int level, char *str, struct frame *frm)
+{
+	unsigned char commands[64];
+	unsigned int i;
+
+	memcpy(commands, frm->ptr, frm->len);
+
+	p_indent(level, frm);
+	printf("%s: commands", str);
+	for (i = 0; i < frm->len; i++)
+		printf(" 0x%02x", commands[i]);
+	printf("\n");
+}
+
+static inline void handle_length_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t handle, length;
+
+	handle = CSR_U16(frm);
+	length = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: handle %d length %d\n", str, handle, length);
+}
+
+static inline void handle_clock_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t handle;
+	uint32_t clock;
+
+	handle = CSR_U16(frm);
+	clock  = CSR_U32(frm);
+
+	p_indent(level, frm);
+	printf("%s: handle %d clock 0x%4.4x\n", str, handle, clock);
+}
+
+static inline void radiotest_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t testid;
+
+	testid = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: test id %d\n", str, testid);
+
+	raw_dump(level, frm);
+}
+
+static inline void psmemtype_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t store, type;
+
+	store = CSR_U16(frm);
+	type  = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: store 0x%4.4x type %d\n", str, store, type);
+}
+
+static inline void psnext_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t key, stores, next;
+
+	key    = CSR_U16(frm);
+	stores = CSR_U16(frm);
+	next   = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: key 0x%4.4x stores 0x%4.4x next 0x%4.4x\n", str, key, stores, next);
+}
+
+static inline void pssize_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t key, length;
+
+	key    = CSR_U16(frm);
+	length = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: key 0x%4.4x %s 0x%4.4x\n", str, key,
+				frm->in ? "len" : "stores", length);
+}
+
+static inline void psstores_dump(int level, char *str, struct frame *frm)
+{
+	uint16_t key, stores;
+
+	key    = CSR_U16(frm);
+	stores = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("%s: key 0x%4.4x stores 0x%4.4x\n", str, key, stores);
+}
+
+static inline void pskey_dump(int level, struct frame *frm)
+{
+	uint16_t key, length, stores;
+
+	key    = CSR_U16(frm);
+	length = CSR_U16(frm);
+	stores = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("PSKEY: key 0x%4.4x len %d stores 0x%4.4x\n", key, length, stores);
+
+	switch (key) {
+	case 0x0001:
+		bdaddr_dump(level + 1, "BDADDR", frm);
+		break;
+	case 0x0002:
+		uint16_dump(level + 1, "COUNTRYCODE", frm);
+		break;
+	case 0x0003:
+		uint32_dump(level + 1, "CLASSOFDEVICE", frm);
+		break;
+	case 0x0004:
+		uint16_dump(level + 1, "DEVICE_DRIFT", frm);
+		break;
+	case 0x0005:
+		uint16_dump(level + 1, "DEVICE_JITTER", frm);
+		break;
+	case 0x000d:
+		uint16_dump(level + 1, "MAX_ACLS", frm);
+		break;
+	case 0x000e:
+		uint16_dump(level + 1, "MAX_SCOS", frm);
+		break;
+	case 0x000f:
+		uint16_dump(level + 1, "MAX_REMOTE_MASTERS", frm);
+		break;
+	case 0x00da:
+		uint16_dump(level + 1, "ENC_KEY_LMIN", frm);
+		break;
+	case 0x00db:
+		uint16_dump(level + 1, "ENC_KEY_LMAX", frm);
+		break;
+	case 0x00ef:
+		features_dump(level + 1, "LOCAL_SUPPORTED_FEATURES", frm);
+		break;
+	case 0x0106:
+		commands_dump(level + 1, "LOCAL_SUPPORTED_COMMANDS", frm);
+		break;
+	case 0x010d:
+		uint16_dump(level + 1, "HCI_LMP_LOCAL_VERSION", frm);
+		break;
+	case 0x010e:
+		uint16_dump(level + 1, "LMP_REMOTE_VERSION", frm);
+		break;
+	case 0x01a5:
+		bool_dump(level + 1, "HOSTIO_USE_HCI_EXTN", frm);
+		break;
+	case 0x01ab:
+		bool_dump(level + 1, "HOSTIO_MAP_SCO_PCM", frm);
+		break;
+	case 0x01be:
+		uint16_dump(level + 1, "UART_BAUDRATE", frm);
+		break;
+	case 0x01f6:
+		uint16_dump(level + 1, "ANA_FTRIM", frm);
+		break;
+	case 0x01f9:
+		uint16_dump(level + 1, "HOST_INTERFACE", frm);
+		break;
+	case 0x01fe:
+		uint16_dump(level + 1, "ANA_FREQ", frm);
+		break;
+	case 0x02be:
+		uint16_dump(level + 1, "USB_VENDOR_ID", frm);
+		break;
+	case 0x02bf:
+		uint16_dump(level + 1, "USB_PRODUCT_ID", frm);
+		break;
+	case 0x02cb:
+		uint16_dump(level + 1, "USB_DFU_PRODUCT_ID", frm);
+		break;
+	case 0x03cd:
+		int16_dump(level + 1, "INITIAL_BOOTMODE", frm);
+		break;
+	default:
+		raw_dump(level + 1, frm);
+		break;
+	}
+}
+
+static inline void bccmd_dump(int level, struct frame *frm)
+{
+	uint16_t type, length, seqno, varid, status;
+
+	type   = CSR_U16(frm);
+	length = CSR_U16(frm);
+	seqno  = CSR_U16(frm);
+	varid  = CSR_U16(frm);
+	status = CSR_U16(frm);
+
+	p_indent(level, frm);
+	printf("BCCMD: %s: len %d seqno %d varid 0x%4.4x status %d\n",
+			type2str(type), length, seqno, varid, status);
+
+	if (!(parser.flags & DUMP_VERBOSE)) {
+		raw_dump(level + 1, frm);
+		return;
+	}
+
+	switch (varid) {
+	case 0x000b:
+		valueless_dump(level + 1, "PS_CLR_ALL", frm);
+		break;
+	case 0x000c:
+		valueless_dump(level + 1, "PS_FACTORY_SET", frm);
+		break;
+	case 0x082d:
+		uint16_dump(level + 1, "PS_CLR_ALL_STORES", frm);
+		break;
+	case 0x2801:
+		uint16_dump(level + 1, "BC01_STATUS", frm);
+		break;
+	case 0x2819:
+		uint16_dump(level + 1, "BUILDID", frm);
+		break;
+	case 0x281a:
+		uint16_dump(level + 1, "CHIPVER", frm);
+		break;
+	case 0x281b:
+		uint16_dump(level + 1, "CHIPREV", frm);
+		break;
+	case 0x2825:
+		uint16_dump(level + 1, "INTERFACE_VERSION", frm);
+		break;
+	case 0x282a:
+		uint16_dump(level + 1, "RAND", frm);
+		break;
+	case 0x282c:
+		uint16_dump(level + 1, "MAX_CRYPT_KEY_LENGTH", frm);
+		break;
+	case 0x2833:
+		uint16_dump(level + 1, "E2_APP_SIZE", frm);
+		break;
+	case 0x2836:
+		uint16_dump(level + 1, "CHIPANAREV", frm);
+		break;
+	case 0x2838:
+		uint16_dump(level + 1, "BUILDID_LOADER", frm);
+		break;
+	case 0x2c00:
+		uint32_dump(level + 1, "BT_CLOCK", frm);
+		break;
+	case 0x3005:
+		psnext_dump(level + 1, "PS_NEXT", frm);
+		break;
+	case 0x3006:
+		pssize_dump(level + 1, "PS_SIZE", frm);
+		break;
+	case 0x3008:
+		handle_length_dump(level + 1, "CRYPT_KEY_LENGTH", frm);
+		break;
+	case 0x3009:
+		handle_clock_dump(level + 1, "PICONET_INSTANCE", frm);
+		break;
+	case 0x300a:
+		complex_dump(level + 1, "GET_CLR_EVT", frm);
+		break;
+	case 0x300b:
+		complex_dump(level + 1, "GET_NEXT_BUILDDEF", frm);
+		break;
+	case 0x300e:
+		complex_dump(level + 1, "E2_DEVICE", frm);
+		break;
+	case 0x300f:
+		complex_dump(level + 1, "E2_APP_DATA", frm);
+		break;
+	case 0x3012:
+		psmemtype_dump(level + 1, "PS_MEMORY_TYPE", frm);
+		break;
+	case 0x301c:
+		complex_dump(level + 1, "READ_BUILD_NAME", frm);
+		break;
+	case 0x4001:
+		valueless_dump(level + 1, "COLD_RESET", frm);
+		break;
+	case 0x4002:
+		valueless_dump(level + 1, "WARM_RESET", frm);
+		break;
+	case 0x4003:
+		valueless_dump(level + 1, "COLD_HALT", frm);
+		break;
+	case 0x4004:
+		valueless_dump(level + 1, "WARM_HALT", frm);
+		break;
+	case 0x4005:
+		valueless_dump(level + 1, "INIT_BT_STACK", frm);
+		break;
+	case 0x4006:
+		valueless_dump(level + 1, "ACTIVATE_BT_STACK", frm);
+		break;
+	case 0x4007:
+		valueless_dump(level + 1, "ENABLE_TX", frm);
+		break;
+	case 0x4008:
+		valueless_dump(level + 1, "DISABLE_TX", frm);
+		break;
+	case 0x4009:
+		valueless_dump(level + 1, "RECAL", frm);
+		break;
+	case 0x400d:
+		valueless_dump(level + 1, "PS_FACTORY_RESTORE", frm);
+		break;
+	case 0x400e:
+		valueless_dump(level + 1, "PS_FACTORY_RESTORE_ALL", frm);
+		break;
+	case 0x400f:
+		valueless_dump(level + 1, "PS_DEFRAG_RESET", frm);
+		break;
+	case 0x4011:
+		valueless_dump(level + 1, "HOPPING_ON", frm);
+		break;
+	case 0x4012:
+		valueless_dump(level + 1, "CANCEL_PAGE", frm);
+		break;
+	case 0x4818:
+		uint16_dump(level + 1, "PS_CLR", frm);
+		break;
+	case 0x481c:
+		uint16_dump(level + 1, "MAP_SCO_PCM", frm);
+		break;
+	case 0x482e:
+		uint16_dump(level + 1, "SINGLE_CHAN", frm);
+		break;
+	case 0x5004:
+		radiotest_dump(level + 1, "RADIOTEST", frm);
+		break;
+	case 0x500c:
+		psstores_dump(level + 1, "PS_CLR_STORES", frm);
+		break;
+	case 0x6000:
+		valueless_dump(level + 1, "NO_VARIABLE", frm);
+		break;
+	case 0x6802:
+		uint16_dump(level + 1, "CONFIG_UART", frm);
+		break;
+	case 0x6805:
+		uint16_dump(level + 1, "PANIC_ARG", frm);
+		break;
+	case 0x6806:
+		uint16_dump(level + 1, "FAULT_ARG", frm);
+		break;
+	case 0x6827:
+		int8_dump(level + 1, "MAX_TX_POWER", frm);
+		break;
+	case 0x682b:
+		int8_dump(level + 1, "DEFAULT_TX_POWER", frm);
+		break;
+	case 0x7003:
+		pskey_dump(level + 1, frm);
+		break;
+	default:
+		raw_dump(level + 1, frm);
+		break;
+	}
+}
+
+static char *cid2str(uint8_t cid)
+{
+	switch (cid & 0x3f) {
+	case 0:
+		return "BCSP Internal";
+	case 1:
+		return "BCSP Link";
+	case 2:
+		return "BCCMD";
+	case 3:
+		return "HQ";
+	case 4:
+		return "Device Mgt";
+	case 5:
+		return "HCI Cmd/Evt";
+	case 6:
+		return "HCI ACL";
+	case 7:
+		return "HCI SCO";
+	case 8:
+		return "L2CAP";
+	case 9:
+		return "RFCOMM";
+	case 10:
+		return "SDP";
+	case 11:
+		return "Debug";
+	case 12:
+		return "DFU";
+	case 13:
+		return "VM";
+	case 14:
+		return "Unused";
+	case 15:
+		return "Reserved";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *frag2str(uint8_t frag)
+{
+	switch (frag & 0xc0) {
+	case 0x00:
+		return " middle fragment";
+	case 0x40:
+		return " first fragment";
+	case 0x80:
+		return " last fragment";
+	default:
+		return "";
+	}
+}
+
+void csr_dump(int level, struct frame *frm)
+{
+	uint8_t desc, cid, type;
+	uint16_t handle, master, addr;
+
+	desc = CSR_U8(frm);
+
+	cid = desc & 0x3f;
+
+	switch (cid) {
+	case 2:
+		bccmd_dump(level, frm);
+		break;
+
+	case 20:
+		type = CSR_U8(frm);
+
+		if (!p_filter(FILT_LMP)) {
+			switch (type) {
+			case 0x0f:
+				frm->handle =  ((uint8_t *) frm->ptr)[17];
+				frm->master = 0;
+				frm->len--;
+				lmp_dump(level, frm);
+				return;
+			case 0x10:
+				frm->handle = ((uint8_t *) frm->ptr)[17];
+				frm->master = 1;
+				frm->len--;
+				lmp_dump(level, frm);
+				return;
+			case 0x12:
+				handle = CSR_U16(frm);
+				master = CSR_U16(frm);
+				addr = CSR_U16(frm);
+				p_indent(level, frm);
+				printf("FHS: handle %d addr %d (%s)\n", handle,
+					addr, master ? "master" : "slave");
+				if (!master) {
+					char addr[18];
+					p_ba2str((bdaddr_t *) frm->ptr, addr);
+					p_indent(level + 1, frm);
+					printf("bdaddr %s class "
+						"0x%2.2x%2.2x%2.2x\n", addr,
+						((uint8_t *) frm->ptr)[8],
+						((uint8_t *) frm->ptr)[7],
+						((uint8_t *) frm->ptr)[6]);
+				}
+				return;
+			case 0x7b:
+				p_indent(level, frm);
+				printf("LMP(r): duplicate (same SEQN)\n");
+				return;
+			}
+		}
+
+		p_indent(level, frm);
+		printf("CSR: Debug (type 0x%2.2x)\n", type);
+		raw_dump(level, frm);
+		break;
+
+	default:
+		p_indent(level, frm);
+		printf("CSR: %s (channel %d)%s\n", cid2str(cid), cid, frag2str(desc));
+		raw_dump(level, frm);
+		break;
+	}
+}
diff --git a/bluez/repo/tools/parser/ericsson.c b/bluez/repo/tools/parser/ericsson.c
new file mode 100644
index 0000000..3d52411
--- /dev/null
+++ b/bluez/repo/tools/parser/ericsson.c
@@ -0,0 +1,53 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "parser.h"
+
+void ericsson_dump(int level, struct frame *frm)
+{
+	uint8_t event = p_get_u8(frm);
+	uint8_t *buf = (uint8_t *) frm->ptr;
+
+	if (event != 0x10) {
+		p_indent(level, frm);
+		printf("Ericsson: event 0x%2.2x\n", event);
+		raw_dump(level, frm);
+	}
+
+	frm->master = !(buf[0] & 0x01);
+	frm->handle = buf[1] | (buf[2] << 8);
+
+	buf[5] = (buf[5] << 1) | (buf[3] & 0x01);
+
+	frm->ptr += 5;
+	frm->len -= 5;
+
+	lmp_dump(level, frm);
+}
diff --git a/bluez/repo/tools/parser/hci.c b/bluez/repo/tools/parser/hci.c
new file mode 100644
index 0000000..8c7bd25
--- /dev/null
+++ b/bluez/repo/tools/parser/hci.c
@@ -0,0 +1,4140 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/amp.h"
+
+static uint16_t manufacturer = DEFAULT_COMPID;
+
+static inline uint16_t get_manufacturer(void)
+{
+	return (manufacturer == DEFAULT_COMPID ? parser.defcompid : manufacturer);
+}
+
+#define EVENT_NUM 77
+static char *event_str[EVENT_NUM + 1] = {
+	"Unknown",
+	"Inquiry Complete",
+	"Inquiry Result",
+	"Connect Complete",
+	"Connect Request",
+	"Disconn Complete",
+	"Auth Complete",
+	"Remote Name Req Complete",
+	"Encrypt Change",
+	"Change Connection Link Key Complete",
+	"Master Link Key Complete",
+	"Read Remote Supported Features",
+	"Read Remote Ver Info Complete",
+	"QoS Setup Complete",
+	"Command Complete",
+	"Command Status",
+	"Hardware Error",
+	"Flush Occurred",
+	"Role Change",
+	"Number of Completed Packets",
+	"Mode Change",
+	"Return Link Keys",
+	"PIN Code Request",
+	"Link Key Request",
+	"Link Key Notification",
+	"Loopback Command",
+	"Data Buffer Overflow",
+	"Max Slots Change",
+	"Read Clock Offset Complete",
+	"Connection Packet Type Changed",
+	"QoS Violation",
+	"Page Scan Mode Change",
+	"Page Scan Repetition Mode Change",
+	"Flow Specification Complete",
+	"Inquiry Result with RSSI",
+	"Read Remote Extended Features",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Synchronous Connect Complete",
+	"Synchronous Connect Changed",
+	"Sniff Subrate",
+	"Extended Inquiry Result",
+	"Encryption Key Refresh Complete",
+	"IO Capability Request",
+	"IO Capability Response",
+	"User Confirmation Request",
+	"User Passkey Request",
+	"Remote OOB Data Request",
+	"Simple Pairing Complete",
+	"Unknown",
+	"Link Supervision Timeout Change",
+	"Enhanced Flush Complete",
+	"Unknown",
+	"User Passkey Notification",
+	"Keypress Notification",
+	"Remote Host Supported Features Notification",
+	"LE Meta Event",
+	"Unknown",
+	"Physical Link Complete",
+	"Channel Selected",
+	"Disconnection Physical Link Complete",
+	"Physical Link Loss Early Warning",
+	"Physical Link Recovery",
+	"Logical Link Complete",
+	"Disconnection Logical Link Complete",
+	"Flow Spec Modify Complete",
+	"Number Of Completed Data Blocks",
+	"AMP Start Test",
+	"AMP Test End",
+	"AMP Receiver Report",
+	"Short Range Mode Change Complete",
+	"AMP Status Change",
+};
+
+#define LE_EV_NUM 5
+static char *ev_le_meta_str[LE_EV_NUM + 1] = {
+	"Unknown",
+	"LE Connection Complete",
+	"LE Advertising Report",
+	"LE Connection Update Complete",
+	"LE Read Remote Used Features Complete",
+	"LE Long Term Key Request",
+};
+
+#define CMD_LINKCTL_NUM 60
+static char *cmd_linkctl_str[CMD_LINKCTL_NUM + 1] = {
+	"Unknown",
+	"Inquiry",
+	"Inquiry Cancel",
+	"Periodic Inquiry Mode",
+	"Exit Periodic Inquiry Mode",
+	"Create Connection",
+	"Disconnect",
+	"Add SCO Connection",
+	"Create Connection Cancel",
+	"Accept Connection Request",
+	"Reject Connection Request",
+	"Link Key Request Reply",
+	"Link Key Request Negative Reply",
+	"PIN Code Request Reply",
+	"PIN Code Request Negative Reply",
+	"Change Connection Packet Type",
+	"Unknown",
+	"Authentication Requested",
+	"Unknown",
+	"Set Connection Encryption",
+	"Unknown",
+	"Change Connection Link Key",
+	"Unknown",
+	"Master Link Key",
+	"Unknown",
+	"Remote Name Request",
+	"Remote Name Request Cancel",
+	"Read Remote Supported Features",
+	"Read Remote Extended Features",
+	"Read Remote Version Information",
+	"Unknown",
+	"Read Clock Offset",
+	"Read LMP Handle",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Setup Synchronous Connection",
+	"Accept Synchronous Connection",
+	"Reject Synchronous Connection",
+	"IO Capability Request Reply",
+	"User Confirmation Request Reply",
+	"User Confirmation Request Negative Reply",
+	"User Passkey Request Reply",
+	"User Passkey Request Negative Reply",
+	"Remote OOB Data Request Reply",
+	"Unknown",
+	"Unknown",
+	"Remote OOB Data Request Negative Reply",
+	"IO Capability Request Negative Reply",
+	"Create Physical Link",
+	"Accept Physical Link",
+	"Disconnect Physical Link",
+	"Create Logical Link",
+	"Accept Logical Link",
+	"Disconnect Logical Link",
+	"Logical Link Cancel",
+	"Flow Spec Modify",
+};
+
+#define CMD_LINKPOL_NUM 17
+static char *cmd_linkpol_str[CMD_LINKPOL_NUM + 1] = {
+	"Unknown",
+	"Hold Mode",
+	"Unknown",
+	"Sniff Mode",
+	"Exit Sniff Mode",
+	"Park State",
+	"Exit Park State",
+	"QoS Setup",
+	"Unknown",
+	"Role Discovery",
+	"Unknown",
+	"Switch Role",
+	"Read Link Policy Settings",
+	"Write Link Policy Settings",
+	"Read Default Link Policy Settings",
+	"Write Default Link Policy Settings",
+	"Flow Specification",
+	"Sniff Subrating",
+};
+
+#define CMD_HOSTCTL_NUM 109
+static char *cmd_hostctl_str[CMD_HOSTCTL_NUM + 1] = {
+	"Unknown",
+	"Set Event Mask",
+	"Unknown",
+	"Reset",
+	"Unknown",
+	"Set Event Filter",
+	"Unknown",
+	"Unknown",
+	"Flush",
+	"Read PIN Type ",
+	"Write PIN Type",
+	"Create New Unit Key",
+	"Unknown",
+	"Read Stored Link Key",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Write Stored Link Key",
+	"Delete Stored Link Key",
+	"Write Local Name",
+	"Read Local Name",
+	"Read Connection Accept Timeout",
+	"Write Connection Accept Timeout",
+	"Read Page Timeout",
+	"Write Page Timeout",
+	"Read Scan Enable",
+	"Write Scan Enable",
+	"Read Page Scan Activity",
+	"Write Page Scan Activity",
+	"Read Inquiry Scan Activity",
+	"Write Inquiry Scan Activity",
+	"Read Authentication Enable",
+	"Write Authentication Enable",
+	"Read Encryption Mode",
+	"Write Encryption Mode",
+	"Read Class of Device",
+	"Write Class of Device",
+	"Read Voice Setting",
+	"Write Voice Setting",
+	"Read Automatic Flush Timeout",
+	"Write Automatic Flush Timeout",
+	"Read Num Broadcast Retransmissions",
+	"Write Num Broadcast Retransmissions",
+	"Read Hold Mode Activity ",
+	"Write Hold Mode Activity",
+	"Read Transmit Power Level",
+	"Read Synchronous Flow Control Enable",
+	"Write Synchronous Flow Control Enable",
+	"Unknown",
+	"Set Host Controller To Host Flow Control",
+	"Unknown",
+	"Host Buffer Size",
+	"Unknown",
+	"Host Number of Completed Packets",
+	"Read Link Supervision Timeout",
+	"Write Link Supervision Timeout",
+	"Read Number of Supported IAC",
+	"Read Current IAC LAP",
+	"Write Current IAC LAP",
+	"Read Page Scan Period Mode",
+	"Write Page Scan Period Mode",
+	"Read Page Scan Mode",
+	"Write Page Scan Mode",
+	"Set AFH Host Channel Classification",
+	"Unknown",
+	"Unknown",
+	"Read Inquiry Scan Type",
+	"Write Inquiry Scan Type",
+	"Read Inquiry Mode",
+	"Write Inquiry Mode",
+	"Read Page Scan Type",
+	"Write Page Scan Type",
+	"Read AFH Channel Assessment Mode",
+	"Write AFH Channel Assessment Mode",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Read Extended Inquiry Response",
+	"Write Extended Inquiry Response",
+	"Refresh Encryption Key",
+	"Unknown",
+	"Read Simple Pairing Mode",
+	"Write Simple Pairing Mode",
+	"Read Local OOB Data",
+	"Read Inquiry Response Transmit Power Level",
+	"Write Inquiry Transmit Power Level",
+	"Read Default Erroneous Data Reporting",
+	"Write Default Erroneous Data Reporting",
+	"Unknown",
+	"Unknown",
+	"Unknown",
+	"Enhanced Flush",
+	"Unknown",
+	"Read Logical Link Accept Timeout",
+	"Write Logical Link Accept Timeout",
+	"Set Event Mask Page 2",
+	"Read Location Data",
+	"Write Location Data",
+	"Read Flow Control Mode",
+	"Write Flow Control Mode",
+	"Read Enhanced Transmit Power Level",
+	"Read Best Effort Flush Timeout",
+	"Write Best Effort Flush Timeout",
+	"Short Range Mode",
+	"Read LE Host Supported",
+	"Write LE Host Supported",
+};
+
+#define CMD_INFO_NUM 10
+static char *cmd_info_str[CMD_INFO_NUM + 1] = {
+	"Unknown",
+	"Read Local Version Information",
+	"Read Local Supported Commands",
+	"Read Local Supported Features",
+	"Read Local Extended Features",
+	"Read Buffer Size",
+	"Unknown",
+	"Read Country Code",
+	"Unknown",
+	"Read BD ADDR",
+	"Read Data Block Size",
+};
+
+#define CMD_STATUS_NUM 11
+static char *cmd_status_str[CMD_STATUS_NUM + 1] = {
+	"Unknown",
+	"Read Failed Contact Counter",
+	"Reset Failed Contact Counter",
+	"Read Link Quality",
+	"Unknown",
+	"Read RSSI",
+	"Read AFH Channel Map",
+	"Read Clock",
+	"Read Encryption Key Size",
+	"Read Local AMP Info",
+	"Read Local AMP ASSOC",
+	"Write Remote AMP ASSOC"
+};
+
+#define CMD_TESTING_NUM 4
+static char *cmd_testing_str[CMD_TESTING_NUM + 1] = {
+	"Unknown",
+	"Read Loopback Mode",
+	"Write Loopback Mode",
+	"Enable Device Under Test mode",
+	"Unknown",
+};
+
+#define CMD_LE_NUM 31
+static char *cmd_le_str[CMD_LE_NUM + 1] = {
+	"Unknown",
+	"LE Set Event Mask",
+	"LE Read Buffer Size",
+	"LE Read Local Supported Features",
+	"Unknown",
+	"LE Set Random Address",
+	"LE Set Advertising Parameters",
+	"LE Read Advertising Channel Tx Power",
+	"LE Set Advertising Data",
+	"LE Set Scan Response Data",
+	"LE Set Advertise Enable",
+	"LE Set Scan Parameters",
+	"LE Set Scan Enable",
+	"LE Create Connection",
+	"LE Create Connection Cancel",
+	"LE Read White List Size",
+	"LE Clear White List",
+	"LE Add Device To White List",
+	"LE Remove Device From White List",
+	"LE Connection Update",
+	"LE Set Host Channel Classification",
+	"LE Read Channel Map",
+	"LE Read Remote Used Features",
+	"LE Encrypt",
+	"LE Rand",
+	"LE Start Encryption",
+	"LE Long Term Key Request Reply",
+	"LE Long Term Key Request Negative Reply",
+	"LE Read Supported States",
+	"LE Receiver Test",
+	"LE Transmitter Test",
+	"LE Test End",
+};
+
+#define ERROR_CODE_NUM 63
+static char *error_code_str[ERROR_CODE_NUM + 1] = {
+	"Success",
+	"Unknown HCI Command",
+	"Unknown Connection Identifier",
+	"Hardware Failure",
+	"Page Timeout",
+	"Authentication Failure",
+	"PIN or Key Missing",
+	"Memory Capacity Exceeded",
+	"Connection Timeout",
+	"Connection Limit Exceeded",
+	"Synchronous Connection to a Device Exceeded",
+	"ACL Connection Already Exists",
+	"Command Disallowed",
+	"Connection Rejected due to Limited Resources",
+	"Connection Rejected due to Security Reasons",
+	"Connection Rejected due to Unacceptable BD_ADDR",
+	"Connection Accept Timeout Exceeded",
+	"Unsupported Feature or Parameter Value",
+	"Invalid HCI Command Parameters",
+	"Remote User Terminated Connection",
+	"Remote Device Terminated Connection due to Low Resources",
+	"Remote Device Terminated Connection due to Power Off",
+	"Connection Terminated by Local Host",
+	"Repeated Attempts",
+	"Pairing Not Allowed",
+	"Unknown LMP PDU",
+	"Unsupported Remote Feature / Unsupported LMP Feature",
+	"SCO Offset Rejected",
+	"SCO Interval Rejected",
+	"SCO Air Mode Rejected",
+	"Invalid LMP Parameters / Invalid LL Parameters",
+	"Unspecified Error",
+	"Unsupported LMP Parameter Value / Unsupported LL Parameter Value",
+	"Role Change Not Allowed",
+	"LMP Response Timeout",
+	"LMP Error Transaction Collision",
+	"LMP PDU Not Allowed",
+	"Encryption Mode Not Acceptable",
+	"Link Key Can Not be Changed",
+	"Requested QoS Not Supported",
+	"Instant Passed",
+	"Pairing with Unit Key Not Supported",
+	"Different Transaction Collision",
+	"Reserved",
+	"QoS Unacceptable Parameter",
+	"QoS Rejected",
+	"Channel Classification Not Supported",
+	"Insufficient Security",
+	"Parameter out of Mandatory Range",
+	"Reserved",
+	"Role Switch Pending",
+	"Reserved",
+	"Reserved Slot Violation",
+	"Role Switch Failed",
+	"Extended Inquiry Response Too Large",
+	"Simple Pairing Not Supported by Host",
+	"Host Busy - Pairing",
+	"Connection Rejected due to No Suitable Channel Found",
+	"Controller Busy",
+	"Unacceptable Connection Parameters",
+	"Directed Advertising Timeout",
+	"Connection Terminated Due to MIC Failure",
+	"Connection Failed to be Established",
+	"MAC Connection Failed",
+};
+
+static char *status2str(uint8_t status)
+{
+	char *str;
+
+	if (status <= ERROR_CODE_NUM)
+		str = error_code_str[status];
+	else
+		str = "Unknown";
+
+	return str;
+}
+
+static char *opcode2str(uint16_t opcode)
+{
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+	char *cmd;
+
+	switch (ogf) {
+	case OGF_INFO_PARAM:
+		if (ocf <= CMD_INFO_NUM)
+			cmd = cmd_info_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_HOST_CTL:
+		if (ocf <= CMD_HOSTCTL_NUM)
+			cmd = cmd_hostctl_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_LINK_CTL:
+		if (ocf <= CMD_LINKCTL_NUM)
+			cmd = cmd_linkctl_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_LINK_POLICY:
+		if (ocf <= CMD_LINKPOL_NUM)
+			cmd = cmd_linkpol_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_STATUS_PARAM:
+		if (ocf <= CMD_STATUS_NUM)
+			cmd = cmd_status_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_TESTING_CMD:
+		if (ocf <= CMD_TESTING_NUM)
+			cmd = cmd_testing_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_LE_CTL:
+		if (ocf <= CMD_LE_NUM)
+			cmd = cmd_le_str[ocf];
+		else
+			cmd = "Unknown";
+		break;
+
+	case OGF_VENDOR_CMD:
+		cmd = "Vendor";
+		break;
+
+	default:
+		cmd = "Unknown";
+		break;
+	}
+
+	return cmd;
+}
+
+static char *linktype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "SCO";
+	case 0x01:
+		return "ACL";
+	case 0x02:
+		return "eSCO";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *role2str(uint8_t role)
+{
+	switch (role) {
+	case 0x00:
+		return "Master";
+	case 0x01:
+		return "Slave";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *mode2str(uint8_t mode)
+{
+	switch (mode) {
+	case 0x00:
+		return "Active";
+	case 0x01:
+		return "Hold";
+	case 0x02:
+		return "Sniff";
+	case 0x03:
+		return "Park";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *airmode2str(uint8_t mode)
+{
+	switch (mode) {
+	case 0x00:
+		return "u-law log";
+	case 0x01:
+		return "A-law log";
+	case 0x02:
+		return "CVSD";
+	case 0x04:
+		return "Transparent data";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *bdaddrtype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "Public";
+	case 0x01:
+		return "Random";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *evttype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "ADV_IND - Connectable undirected advertising";
+	case 0x01:
+		return "ADV_DIRECT_IND - Connectable directed advertising";
+	case 0x02:
+		return "ADV_SCAN_IND - Scannable undirected advertising";
+	case 0x03:
+		return "ADV_NONCONN_IND - Non connectable undirected advertising";
+	case 0x04:
+		return "SCAN_RSP - Scan Response";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *keytype2str(uint8_t type)
+{
+	switch (type) {
+	case 0x00:
+		return "Combination Key";
+	case 0x01:
+		return "Local Unit Key";
+	case 0x02:
+		return "Remote Unit Key";
+	case 0x03:
+		return "Debug Combination Key";
+	case 0x04:
+		return "Unauthenticated Combination Key";
+	case 0x05:
+		return "Authenticated Combination Key";
+	case 0x06:
+		return "Changed Combination Key";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *capability2str(uint8_t capability)
+{
+	switch (capability) {
+	case 0x00:
+		return "DisplayOnly";
+	case 0x01:
+		return "DisplayYesNo";
+	case 0x02:
+		return "KeyboardOnly";
+	case 0x03:
+		return "NoInputNoOutput";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *authentication2str(uint8_t authentication)
+{
+	switch (authentication) {
+	case 0x00:
+		return "No Bonding (No MITM Protection)";
+	case 0x01:
+		return "No Bonding (MITM Protection)";
+	case 0x02:
+		return "Dedicated Bonding (No MITM Protection)";
+	case 0x03:
+		return "Dedicated Bonding (MITM Protection)";
+	case 0x04:
+		return "General Bonding (No MITM Protection)";
+	case 0x05:
+		return "General Bonding (MITM Protection)";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *eventmask2str(const uint8_t mask[8])
+{
+	int i;
+
+	for (i = 0; i < 7; i++) {
+		if (mask[i] != 0x00)
+			return "Reserved";
+	}
+
+	switch (mask[7]) {
+	case 0x00:
+		return "No LE events specified";
+	case 0x01:
+		return "LE Connection Complete Event";
+	case 0x02:
+		return "LE Advertising Report Event";
+	case 0x04:
+		return "LE Connection Update Complete Event";
+	case 0x08:
+		return "LE Read Remote Used Features Complete Event";
+	case 0x10:
+		return "LE Long Term Key Request Event";
+	case 0x1F:
+		return "Default";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *lefeatures2str(const uint8_t features[8])
+{
+	if (features[0] & 0x01)
+		return "Link Layer supports LE Encryption";
+
+	return "RFU";
+}
+
+static char *filterpolicy2str(uint8_t policy)
+{
+	switch (policy) {
+	case 0x00:
+		return "Allow scan from any, connection from any";
+	case 0x01:
+		return "Allow scan from white list, connection from any";
+	case 0x02:
+		return "Allow scan from any, connection from white list";
+	case 0x03:
+		return "Allow scan and connection from white list";
+	default:
+		return "Reserved";
+	}
+}
+
+static inline void ext_inquiry_data_dump(int level, struct frame *frm,
+						uint8_t *data)
+{
+	uint8_t len = data[0];
+	uint8_t type;
+	char *str;
+	int i;
+
+	if (len == 0)
+		return;
+
+	type = data[1];
+	data += 2;
+	len -= 1;
+
+	switch (type) {
+	case 0x01:
+		p_indent(level, frm);
+		printf("Flags:");
+		for (i = 0; i < len; i++)
+			printf(" 0x%2.2x", data[i]);
+		printf("\n");
+		break;
+
+	case 0x02:
+	case 0x03:
+		p_indent(level, frm);
+		printf("%s service classes:",
+				type == 0x02 ? "Shortened" : "Complete");
+
+		for (i = 0; i < len / 2; i++)
+			printf(" 0x%4.4x", get_le16(data + i * 2));
+
+		printf("\n");
+		break;
+
+	case 0x08:
+	case 0x09:
+		str = malloc(len + 1);
+		if (str) {
+			snprintf(str, len + 1, "%s", (char *) data);
+			for (i = 0; i < len; i++)
+				if (!isprint(str[i]))
+					str[i] = '.';
+			p_indent(level, frm);
+			printf("%s local name: \'%s\'\n",
+				type == 0x08 ? "Shortened" : "Complete", str);
+			free(str);
+		}
+		break;
+
+	case 0x0a:
+		p_indent(level, frm);
+		printf("TX power level: %d\n", *((uint8_t *) data));
+		break;
+
+	default:
+		p_indent(level, frm);
+		printf("Unknown type 0x%02x with %d bytes data\n",
+							type, len);
+		break;
+	}
+}
+
+static inline void ext_inquiry_response_dump(int level, struct frame *frm)
+{
+	void *ptr = frm->ptr;
+	uint32_t len = frm->len;
+	uint8_t *data;
+	uint8_t length;
+
+	data = frm->ptr;
+	length = p_get_u8(frm);
+
+	while (length > 0) {
+		ext_inquiry_data_dump(level, frm, data);
+
+		frm->ptr += length;
+		frm->len -= length;
+
+		data = frm->ptr;
+		length = p_get_u8(frm);
+	}
+
+	frm->ptr = ptr +
+		(EXTENDED_INQUIRY_INFO_SIZE - INQUIRY_INFO_WITH_RSSI_SIZE);
+	frm->len = len +
+		(EXTENDED_INQUIRY_INFO_SIZE - INQUIRY_INFO_WITH_RSSI_SIZE);
+}
+
+static inline void bdaddr_command_dump(int level, struct frame *frm)
+{
+	bdaddr_t *bdaddr = frm->ptr;
+	char addr[18];
+
+	frm->ptr += sizeof(bdaddr_t);
+	frm->len -= sizeof(bdaddr_t);
+
+	p_indent(level, frm);
+	p_ba2str(bdaddr, addr);
+	printf("bdaddr %s\n", addr);
+
+	raw_dump(level, frm);
+}
+
+static inline void generic_command_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle %d\n", handle);
+
+	raw_dump(level, frm);
+}
+
+static inline void generic_write_mode_dump(int level, struct frame *frm)
+{
+	uint8_t mode = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("mode 0x%2.2x\n", mode);
+}
+
+static inline void inquiry_dump(int level, struct frame *frm)
+{
+	inquiry_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("lap 0x%2.2x%2.2x%2.2x len %d num %d\n",
+		cp->lap[2], cp->lap[1], cp->lap[0], cp->length, cp->num_rsp);
+}
+
+static inline void periodic_inquiry_dump(int level, struct frame *frm)
+{
+	periodic_inquiry_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("max %d min %d lap 0x%2.2x%2.2x%2.2x len %d num %d\n",
+		btohs(cp->max_period), btohs(cp->min_period),
+		cp->lap[2], cp->lap[1], cp->lap[0], cp->length, cp->num_rsp);
+}
+
+static inline void create_conn_dump(int level, struct frame *frm)
+{
+	create_conn_cp *cp = frm->ptr;
+	uint16_t ptype = btohs(cp->pkt_type);
+	uint16_t clkoffset = btohs(cp->clock_offset);
+	char addr[18], *str;
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s ptype 0x%4.4x rswitch 0x%2.2x clkoffset 0x%4.4x%s\n",
+		addr, ptype, cp->role_switch,
+		clkoffset & 0x7fff, clkoffset & 0x8000 ? " (valid)" : "");
+
+	str = hci_ptypetostr(ptype);
+	if (str) {
+		p_indent(level, frm);
+		printf("Packet type: %s\n", str);
+		free(str);
+	}
+}
+
+static inline void disconnect_dump(int level, struct frame *frm)
+{
+	disconnect_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d reason 0x%2.2x\n", btohs(cp->handle), cp->reason);
+
+	p_indent(level, frm);
+	printf("Reason: %s\n", status2str(cp->reason));
+}
+
+static inline void add_sco_dump(int level, struct frame *frm)
+{
+	add_sco_cp *cp = frm->ptr;
+	uint16_t ptype = btohs(cp->pkt_type);
+	char *str;
+
+	p_indent(level, frm);
+	printf("handle %d ptype 0x%4.4x\n", btohs(cp->handle), ptype);
+
+	str = hci_ptypetostr(ptype);
+	if (str) {
+		p_indent(level, frm);
+		printf("Packet type: %s\n", str);
+		free(str);
+	}
+}
+
+static inline void accept_conn_req_dump(int level, struct frame *frm)
+{
+	accept_conn_req_cp *cp = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s role 0x%2.2x\n", addr, cp->role);
+
+	p_indent(level, frm);
+	printf("Role: %s\n", role2str(cp->role));
+}
+
+static inline void reject_conn_req_dump(int level, struct frame *frm)
+{
+	reject_conn_req_cp *cp = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s reason 0x%2.2x\n", addr, cp->reason);
+
+	p_indent(level, frm);
+	printf("Reason: %s\n", status2str(cp->reason));
+}
+
+static inline void pin_code_reply_dump(int level, struct frame *frm)
+{
+	pin_code_reply_cp *cp = frm->ptr;
+	char addr[18], pin[17];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	memset(pin, 0, sizeof(pin));
+	if (parser.flags & DUMP_NOVENDOR)
+		memset(pin, '*', cp->pin_len);
+	else
+		memcpy(pin, cp->pin_code, cp->pin_len);
+	printf("bdaddr %s len %d pin \'%s\'\n", addr, cp->pin_len, pin);
+}
+
+static inline void link_key_reply_dump(int level, struct frame *frm)
+{
+	link_key_reply_cp *cp = frm->ptr;
+	char addr[18];
+	int i;
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s key ", addr);
+	for (i = 0; i < 16; i++)
+		if (parser.flags & DUMP_NOVENDOR)
+			printf("**");
+		else
+			printf("%2.2X", cp->link_key[i]);
+	printf("\n");
+}
+
+static inline void pin_code_neg_reply_dump(int level, struct frame *frm)
+{
+	bdaddr_t *bdaddr = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(bdaddr, addr);
+	printf("bdaddr %s\n", addr);
+}
+
+static inline void user_passkey_reply_dump(int level, struct frame *frm)
+{
+	user_passkey_reply_cp *cp = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s passkey %d\n", addr, btohl(cp->passkey));
+}
+
+static inline void remote_oob_data_reply_dump(int level, struct frame *frm)
+{
+	remote_oob_data_reply_cp *cp = frm->ptr;
+	char addr[18];
+	int i;
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s\n", addr);
+
+	p_indent(level, frm);
+	printf("hash 0x");
+	for (i = 0; i < 16; i++)
+		printf("%02x", cp->hash[i]);
+	printf("\n");
+
+	p_indent(level, frm);
+	printf("randomizer 0x");
+	for (i = 0; i < 16; i++)
+			printf("%02x", cp->randomizer[i]);
+	printf("\n");
+}
+
+static inline void io_capability_reply_dump(int level, struct frame *frm)
+{
+	io_capability_reply_cp *cp = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s capability 0x%2.2x oob 0x%2.2x auth 0x%2.2x\n",
+					addr, cp->capability, cp->oob_data,
+							cp->authentication);
+
+	p_indent(level, frm);
+	printf("Capability: %s (OOB data %s)\n",
+			capability2str(cp->capability),
+			cp->oob_data == 0x00 ? "not present" : "available");
+
+	p_indent(level, frm);
+	printf("Authentication: %s\n", authentication2str(cp->authentication));
+}
+
+static inline void set_conn_encrypt_dump(int level, struct frame *frm)
+{
+	set_conn_encrypt_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d encrypt 0x%2.2x\n", btohs(cp->handle), cp->encrypt);
+}
+
+static inline void remote_name_req_dump(int level, struct frame *frm)
+{
+	remote_name_req_cp *cp = frm->ptr;
+	uint16_t clkoffset = btohs(cp->clock_offset);
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s mode %d clkoffset 0x%4.4x%s\n",
+		addr, cp->pscan_rep_mode,
+		clkoffset & 0x7fff, clkoffset & 0x8000 ? " (valid)" : "");
+}
+
+static inline void master_link_key_dump(int level, struct frame *frm)
+{
+	master_link_key_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("flag %d\n", cp->key_flag);
+}
+
+static inline void read_remote_ext_features_dump(int level, struct frame *frm)
+{
+	read_remote_ext_features_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d page %d\n", btohs(cp->handle), cp->page_num);
+}
+
+static inline void setup_sync_conn_dump(int level, struct frame *frm)
+{
+	setup_sync_conn_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d voice setting 0x%4.4x ptype 0x%4.4x\n",
+		btohs(cp->handle), btohs(cp->voice_setting),
+		btohs(cp->pkt_type));
+}
+
+static inline void create_physical_link_dump(int level, struct frame *frm)
+{
+	create_physical_link_cp *cp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("phy handle 0x%2.2x key length %d key type %d\n",
+		cp->handle, cp->key_length, cp->key_type);
+	p_indent(level, frm);
+	printf("key ");
+	for (i = 0; i < cp->key_length && cp->key_length <= 32; i++)
+		printf("%2.2x", cp->key[i]);
+	printf("\n");
+}
+
+static inline void create_logical_link_dump(int level, struct frame *frm)
+{
+	create_logical_link_cp *cp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("phy handle 0x%2.2x\n", cp->handle);
+
+	p_indent(level, frm);
+	printf("tx_flow ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", cp->tx_flow[i]);
+	printf("\n");
+
+	p_indent(level, frm);
+	printf("rx_flow ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", cp->rx_flow[i]);
+	printf("\n");
+}
+
+static inline void hold_mode_dump(int level, struct frame *frm)
+{
+	hold_mode_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d max %d min %d\n", btohs(cp->handle),
+			btohs(cp->max_interval), btohs(cp->min_interval));
+}
+
+static inline void sniff_mode_dump(int level, struct frame *frm)
+{
+	sniff_mode_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d max %d min %d attempt %d timeout %d\n",
+		btohs(cp->handle), btohs(cp->max_interval),
+		btohs(cp->min_interval), btohs(cp->attempt), btohs(cp->timeout));
+}
+
+static inline void qos_setup_dump(int level, struct frame *frm)
+{
+	qos_setup_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d flags 0x%2.2x\n", btohs(cp->handle), cp->flags);
+
+	p_indent(level, frm);
+	printf("Service type: %d\n", cp->qos.service_type);
+	p_indent(level, frm);
+	printf("Token rate: %d\n", btohl(cp->qos.token_rate));
+	p_indent(level, frm);
+	printf("Peak bandwith: %d\n", btohl(cp->qos.peak_bandwidth));
+	p_indent(level, frm);
+	printf("Latency: %d\n", btohl(cp->qos.latency));
+	p_indent(level, frm);
+	printf("Delay variation: %d\n", btohl(cp->qos.delay_variation));
+}
+
+static inline void write_link_policy_dump(int level, struct frame *frm)
+{
+	write_link_policy_cp *cp = frm->ptr;
+	uint16_t policy = btohs(cp->policy);
+	char *str;
+
+	p_indent(level, frm);
+	printf("handle %d policy 0x%2.2x\n", btohs(cp->handle), policy);
+
+	str = hci_lptostr(policy);
+	if (str) {
+		p_indent(level, frm);
+		printf("Link policy: %s\n", str);
+		free(str);
+	}
+}
+
+static inline void write_default_link_policy_dump(int level, struct frame *frm)
+{
+	uint16_t policy = btohs(htons(p_get_u16(frm)));
+	char *str;
+
+	p_indent(level, frm);
+	printf("policy 0x%2.2x\n", policy);
+
+	str = hci_lptostr(policy);
+	if (str) {
+		p_indent(level, frm);
+		printf("Link policy: %s\n", str);
+		free(str);
+	}
+}
+
+static inline void sniff_subrating_dump(int level, struct frame *frm)
+{
+	sniff_subrating_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d\n", btohs(cp->handle));
+
+	p_indent(level, frm);
+	printf("max latency %d\n", btohs(cp->max_latency));
+
+	p_indent(level, frm);
+	printf("min timeout remote %d local %d\n",
+		btohs(cp->min_remote_timeout), btohs(cp->min_local_timeout));
+}
+
+static inline void set_event_mask_dump(int level, struct frame *frm)
+{
+	set_event_mask_cp *cp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("Mask: 0x");
+	for (i = 0; i < 8; i++)
+		printf("%2.2x", cp->mask[i]);
+	printf("\n");
+}
+
+static inline void set_event_flt_dump(int level, struct frame *frm)
+{
+	set_event_flt_cp *cp = frm->ptr;
+	uint8_t dev_class[3], dev_mask[3];
+	char addr[18];
+
+	p_indent(level, frm);
+	printf("type %d condition %d\n", cp->flt_type,
+				(cp->flt_type == 0) ? 0 : cp->cond_type);
+
+	switch (cp->flt_type) {
+	case FLT_CLEAR_ALL:
+		p_indent(level, frm);
+		printf("Clear all filters\n");
+		break;
+	case FLT_INQ_RESULT:
+		p_indent(level, frm);
+		printf("Inquiry result");
+		switch (cp->cond_type) {
+		case INQ_RESULT_RETURN_ALL:
+			printf(" for all devices\n");
+			break;
+		case INQ_RESULT_RETURN_CLASS:
+			memcpy(dev_class, cp->condition, 3);
+			memcpy(dev_mask, cp->condition + 3, 3);
+			printf(" with class 0x%2.2x%2.2x%2.2x mask 0x%2.2x%2.2x%2.2x\n",
+				dev_class[2], dev_class[1], dev_class[0],
+				dev_mask[2], dev_mask[1], dev_mask[0]);
+			break;
+		case INQ_RESULT_RETURN_BDADDR:
+			p_ba2str((bdaddr_t *) cp->condition, addr);
+			printf(" with bdaddr %s\n", addr);
+			break;
+		default:
+			printf("\n");
+			break;
+		}
+		break;
+	case FLT_CONN_SETUP:
+		p_indent(level, frm);
+		printf("Connection setup");
+		switch (cp->cond_type) {
+		case CONN_SETUP_ALLOW_ALL:
+		case CONN_SETUP_ALLOW_CLASS:
+		case CONN_SETUP_ALLOW_BDADDR:
+		default:
+			printf("\n");
+			break;
+		}
+		break;
+	}
+}
+
+static inline void write_pin_type_dump(int level, struct frame *frm)
+{
+	write_pin_type_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("type %d\n", cp->pin_type);
+}
+
+static inline void request_stored_link_key_dump(int level, struct frame *frm)
+{
+	read_stored_link_key_cp *cp = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s all %d\n", addr, cp->read_all);
+}
+
+static inline void return_link_keys_dump(int level, struct frame *frm)
+{
+	uint8_t num = p_get_u8(frm);
+	uint8_t key[16];
+	char addr[18];
+	int i, n;
+
+	for (n = 0; n < num; n++) {
+		p_ba2str(frm->ptr, addr);
+		memcpy(key, frm->ptr + 6, 16);
+
+		p_indent(level, frm);
+		printf("bdaddr %s key ", addr);
+		for (i = 0; i < 16; i++)
+			if (parser.flags & DUMP_NOVENDOR)
+				printf("**");
+			else
+				printf("%2.2X", key[i]);
+		printf("\n");
+
+		frm->ptr += 2;
+		frm->len -= 2;
+	}
+}
+
+static inline void change_local_name_dump(int level, struct frame *frm)
+{
+	change_local_name_cp *cp = frm->ptr;
+	char name[249];
+	int i;
+
+	memset(name, 0, sizeof(name));
+	for (i = 0; i < 248 && cp->name[i]; i++)
+		if (isprint(cp->name[i]))
+			name[i] = cp->name[i];
+		else
+			name[i] = '.';
+
+	p_indent(level, frm);
+	printf("name \'%s\'\n", name);
+}
+
+static inline void write_class_of_dev_dump(int level, struct frame *frm)
+{
+	write_class_of_dev_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("class 0x%2.2x%2.2x%2.2x\n",
+			cp->dev_class[2], cp->dev_class[1], cp->dev_class[0]);
+}
+
+static inline void write_voice_setting_dump(int level, struct frame *frm)
+{
+	write_voice_setting_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("voice setting 0x%4.4x\n", btohs(cp->voice_setting));
+}
+
+static inline void write_current_iac_lap_dump(int level, struct frame *frm)
+{
+	write_current_iac_lap_cp *cp = frm->ptr;
+	int i;
+
+	for (i = 0; i < cp->num_current_iac; i++) {
+		p_indent(level, frm);
+		printf("IAC 0x%2.2x%2.2x%2.2x", cp->lap[i][2], cp->lap[i][1], cp->lap[i][0]);
+		if (cp->lap[i][2] == 0x9e && cp->lap[i][1] == 0x8b) {
+			switch (cp->lap[i][0]) {
+			case 0x00:
+				printf(" (Limited Inquiry Access Code)");
+				break;
+			case 0x33:
+				printf(" (General Inquiry Access Code)");
+				break;
+			}
+		}
+		printf("\n");
+	}
+}
+
+static inline void write_scan_enable_dump(int level, struct frame *frm)
+{
+	uint8_t enable = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("enable %d\n", enable);
+}
+
+static inline void write_page_timeout_dump(int level, struct frame *frm)
+{
+	write_page_timeout_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("timeout %d\n", btohs(cp->timeout));
+}
+
+static inline void write_page_activity_dump(int level, struct frame *frm)
+{
+	write_page_activity_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("interval %d window %d\n", btohs(cp->interval), btohs(cp->window));
+}
+
+static inline void write_inquiry_scan_type_dump(int level, struct frame *frm)
+{
+	write_inquiry_scan_type_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("type %d\n", cp->type);
+}
+
+static inline void write_inquiry_mode_dump(int level, struct frame *frm)
+{
+	write_inquiry_mode_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("mode %d\n", cp->mode);
+}
+
+static inline void set_afh_classification_dump(int level, struct frame *frm)
+{
+	set_afh_classification_cp *cp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("map 0x");
+	for (i = 0; i < 10; i++)
+		printf("%02x", cp->map[i]);
+	printf("\n");
+}
+
+static inline void write_link_supervision_timeout_dump(int level, struct frame *frm)
+{
+	write_link_supervision_timeout_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d timeout %d\n",
+				btohs(cp->handle), btohs(cp->timeout));
+}
+
+static inline void write_ext_inquiry_response_dump(int level, struct frame *frm)
+{
+	write_ext_inquiry_response_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("fec 0x%2.2x\n", cp->fec);
+
+	frm->ptr++;
+	frm->len--;
+
+	ext_inquiry_response_dump(level, frm);
+}
+
+static inline void write_inquiry_transmit_power_level_dump(int level, struct frame *frm)
+{
+	write_inquiry_transmit_power_level_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("level %d\n", cp->level);
+}
+
+static inline void write_default_error_data_reporting_dump(int level, struct frame *frm)
+{
+	write_default_error_data_reporting_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("reporting %d\n", cp->reporting);
+}
+
+static inline void enhanced_flush_dump(int level, struct frame *frm)
+{
+	enhanced_flush_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d type %d\n", btohs(cp->handle), cp->type);
+}
+
+static inline void send_keypress_notify_dump(int level, struct frame *frm)
+{
+	send_keypress_notify_cp *cp = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s type %d\n", addr, cp->type);
+}
+
+static inline void request_transmit_power_level_dump(int level, struct frame *frm)
+{
+	read_transmit_power_level_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d type %d (%s)\n",
+					btohs(cp->handle), cp->type,
+					cp->type ? "maximum" : "current");
+}
+
+static inline void request_local_ext_features_dump(int level, struct frame *frm)
+{
+	read_local_ext_features_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("page %d\n", cp->page_num);
+}
+
+static inline void request_clock_dump(int level, struct frame *frm)
+{
+	read_clock_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d which %d (%s)\n",
+					btohs(cp->handle), cp->which_clock,
+					cp->which_clock ? "piconet" : "local");
+}
+
+static inline void host_buffer_size_dump(int level, struct frame *frm)
+{
+	host_buffer_size_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("ACL MTU %d:%d SCO MTU %d:%d\n",
+				btohs(cp->acl_mtu), btohs(cp->acl_max_pkt),
+				cp->sco_mtu, btohs(cp->sco_max_pkt));
+}
+
+static inline void num_comp_pkts_dump(int level, struct frame *frm)
+{
+	uint8_t num = p_get_u8(frm);
+	uint16_t handle, packets;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		handle = btohs(htons(p_get_u16(frm)));
+		packets = btohs(htons(p_get_u16(frm)));
+
+		p_indent(level, frm);
+		printf("handle %d packets %d\n", handle, packets);
+	}
+}
+
+static inline void le_create_connection_dump(int level, struct frame *frm)
+{
+	char addr[18];
+	le_create_connection_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	p_ba2str(&cp->peer_bdaddr, addr);
+	printf("bdaddr %s type %d\n", addr, cp->peer_bdaddr_type);
+	p_indent(level, frm);
+	printf("interval %u window %u initiator_filter %u\n",
+		btohs(cp->interval), btohs(cp->window), cp->initiator_filter);
+	p_indent(level, frm);
+	printf("own_bdaddr_type %u min_interval %u max_interval %u\n",
+			cp->own_bdaddr_type, btohs(cp->min_interval),
+			btohs(cp->max_interval));
+	p_indent(level, frm);
+	printf("latency %u supervision_to %u min_ce %u max_ce %u\n",
+			btohs(cp->latency), btohs(cp->supervision_timeout),
+			btohs(cp->min_ce_length), btohs(cp->max_ce_length));
+}
+
+static inline void le_set_event_mask_dump(int level, struct frame *frm)
+{
+	int i;
+	le_set_event_mask_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("mask 0x");
+	for (i = 0; i < 8; i++)
+		printf("%.2x", cp->mask[i]);
+
+	printf(" (%s)\n", eventmask2str(cp->mask));
+}
+
+static inline void le_set_random_address_dump(int level, struct frame *frm)
+{
+	char addr[18];
+	le_set_random_address_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	p_ba2str(&cp->bdaddr, addr);
+	printf("bdaddr %s\n", addr);
+}
+
+
+static inline void le_set_advertising_parameters_dump(int level, struct frame *frm)
+{
+	char addr[18];
+	le_set_advertising_parameters_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("min %.3fms, max %.3fms\n", btohs(cp->min_interval) * 0.625,
+			btohs(cp->max_interval) * 0.625);
+
+	p_indent(level, frm);
+	printf("type 0x%02x (%s) ownbdaddr 0x%02x (%s)\n", cp->advtype,
+			evttype2str(cp->advtype), cp->own_bdaddr_type,
+			bdaddrtype2str(cp->own_bdaddr_type));
+
+	p_indent(level, frm);
+	p_ba2str(&cp->direct_bdaddr, addr);
+	printf("directbdaddr 0x%02x (%s) %s\n", cp->direct_bdaddr_type,
+			bdaddrtype2str(cp->direct_bdaddr_type), addr);
+
+	p_indent(level, frm);
+	printf("channelmap 0x%02x filterpolicy 0x%02x (%s)\n",
+			cp->chan_map, cp->filter, filterpolicy2str(cp->filter));
+}
+
+static inline void le_set_scan_parameters_dump(int level, struct frame *frm)
+{
+	le_set_scan_parameters_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("type 0x%02x (%s)\n", cp->type,
+		cp->type == 0x00 ? "passive" : "active");
+
+	p_indent(level, frm);
+	printf("interval %.3fms window %.3fms\n", btohs(cp->interval) * 0.625,
+		btohs(cp->window) * 0.625);
+
+	p_indent(level, frm);
+	printf("own address: 0x%02x (%s) policy: %s\n", cp->own_bdaddr_type,
+			bdaddrtype2str(cp->own_bdaddr_type),
+		(cp->filter == 0x00 ? "All" :
+			(cp->filter == 0x01 ? "white list only" : "reserved")));
+}
+
+static inline void le_set_scan_enable_dump(int level, struct frame *frm)
+{
+	le_set_scan_enable_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("value 0x%02x (%s)\n", cp->enable,
+		(cp->enable == 0x00 ? "scanning disabled" :
+		"scanning enabled"));
+
+	p_indent(level, frm);
+	printf("filter duplicates 0x%02x (%s)\n", cp->filter_dup,
+		(cp->filter_dup == 0x00 ? "disabled" : "enabled"));
+}
+
+static inline void write_remote_amp_assoc_cmd_dump(int level,
+							struct frame *frm)
+{
+	write_remote_amp_assoc_cp *cp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle 0x%2.2x len_so_far %d remaining_len %d\n", cp->handle,
+				cp->length_so_far, cp->remaining_length);
+
+	amp_assoc_dump(level + 1, cp->fragment, frm->len - 5);
+}
+
+static inline void command_dump(int level, struct frame *frm)
+{
+	hci_command_hdr *hdr = frm->ptr;
+	uint16_t opcode = btohs(hdr->opcode);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+
+	if (p_filter(FILT_HCI))
+		return;
+
+	if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR))
+		return;
+
+	p_indent(level, frm);
+	printf("HCI Command: %s (0x%2.2x|0x%4.4x) plen %d\n",
+				opcode2str(opcode), ogf, ocf, hdr->plen);
+
+	frm->ptr += HCI_COMMAND_HDR_SIZE;
+	frm->len -= HCI_COMMAND_HDR_SIZE;
+
+	if (ogf == OGF_VENDOR_CMD) {
+		if (ocf == 0 && get_manufacturer() == 10) {
+			csr_dump(level + 1, frm);
+			return;
+		}
+	}
+
+	if (!(parser.flags & DUMP_VERBOSE)) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (ogf) {
+	case OGF_LINK_CTL:
+		switch (ocf) {
+		case OCF_INQUIRY:
+			inquiry_dump(level + 1, frm);
+			return;
+		case OCF_PERIODIC_INQUIRY:
+			periodic_inquiry_dump(level + 1, frm);
+			return;
+		case OCF_INQUIRY_CANCEL:
+		case OCF_EXIT_PERIODIC_INQUIRY:
+			return;
+		case OCF_CREATE_CONN:
+			create_conn_dump(level + 1, frm);
+			return;
+		case OCF_DISCONNECT:
+			disconnect_dump(level + 1, frm);
+			return;
+		case OCF_CREATE_CONN_CANCEL:
+		case OCF_REMOTE_NAME_REQ_CANCEL:
+		case OCF_ACCEPT_SYNC_CONN_REQ:
+			bdaddr_command_dump(level + 1, frm);
+			return;
+		case OCF_ADD_SCO:
+		case OCF_SET_CONN_PTYPE:
+			add_sco_dump(level + 1, frm);
+			return;
+		case OCF_ACCEPT_CONN_REQ:
+			accept_conn_req_dump(level + 1, frm);
+			return;
+		case OCF_REJECT_CONN_REQ:
+		case OCF_REJECT_SYNC_CONN_REQ:
+		case OCF_IO_CAPABILITY_NEG_REPLY:
+			reject_conn_req_dump(level + 1, frm);
+			return;
+		case OCF_PIN_CODE_REPLY:
+			pin_code_reply_dump(level + 1, frm);
+			return;
+		case OCF_LINK_KEY_REPLY:
+			link_key_reply_dump(level + 1, frm);
+			return;
+		case OCF_PIN_CODE_NEG_REPLY:
+		case OCF_LINK_KEY_NEG_REPLY:
+		case OCF_USER_CONFIRM_REPLY:
+		case OCF_USER_CONFIRM_NEG_REPLY:
+		case OCF_USER_PASSKEY_NEG_REPLY:
+		case OCF_REMOTE_OOB_DATA_NEG_REPLY:
+			pin_code_neg_reply_dump(level + 1, frm);
+			return;
+		case OCF_USER_PASSKEY_REPLY:
+			user_passkey_reply_dump(level + 1, frm);
+			return;
+		case OCF_REMOTE_OOB_DATA_REPLY:
+			remote_oob_data_reply_dump(level + 1, frm);
+			return;
+		case OCF_IO_CAPABILITY_REPLY:
+			io_capability_reply_dump(level + 1, frm);
+			return;
+		case OCF_SET_CONN_ENCRYPT:
+			set_conn_encrypt_dump(level + 1, frm);
+			return;
+		case OCF_AUTH_REQUESTED:
+		case OCF_CHANGE_CONN_LINK_KEY:
+		case OCF_READ_REMOTE_FEATURES:
+		case OCF_READ_REMOTE_VERSION:
+		case OCF_READ_CLOCK_OFFSET:
+		case OCF_READ_LMP_HANDLE:
+		case OCF_DISCONNECT_LOGICAL_LINK:
+			generic_command_dump(level + 1, frm);
+			return;
+		case OCF_MASTER_LINK_KEY:
+			master_link_key_dump(level + 1, frm);
+			return;
+		case OCF_READ_REMOTE_EXT_FEATURES:
+			read_remote_ext_features_dump(level + 1, frm);
+			return;
+		case OCF_REMOTE_NAME_REQ:
+			remote_name_req_dump(level + 1, frm);
+			return;
+		case OCF_SETUP_SYNC_CONN:
+			setup_sync_conn_dump(level + 1, frm);
+			return;
+		case OCF_CREATE_PHYSICAL_LINK:
+		case OCF_ACCEPT_PHYSICAL_LINK:
+			create_physical_link_dump(level + 1, frm);
+			return;
+		case OCF_CREATE_LOGICAL_LINK:
+		case OCF_ACCEPT_LOGICAL_LINK:
+			create_logical_link_dump(level + 1, frm);
+			return;
+		}
+		break;
+
+	case OGF_LINK_POLICY:
+		switch (ocf) {
+		case OCF_HOLD_MODE:
+		case OCF_PARK_MODE:
+			hold_mode_dump(level + 1, frm);
+			return;
+		case OCF_SNIFF_MODE:
+			sniff_mode_dump(level + 1, frm);
+			return;
+		case OCF_EXIT_SNIFF_MODE:
+		case OCF_EXIT_PARK_MODE:
+		case OCF_ROLE_DISCOVERY:
+		case OCF_READ_LINK_POLICY:
+			generic_command_dump(level + 1, frm);
+			return;
+		case OCF_READ_DEFAULT_LINK_POLICY:
+			return;
+		case OCF_SWITCH_ROLE:
+			accept_conn_req_dump(level + 1, frm);
+			return;
+		case OCF_QOS_SETUP:
+			qos_setup_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_LINK_POLICY:
+			write_link_policy_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_DEFAULT_LINK_POLICY:
+			write_default_link_policy_dump(level + 1, frm);
+			return;
+		case OCF_SNIFF_SUBRATING:
+			sniff_subrating_dump(level + 1, frm);
+			return;
+		}
+		break;
+
+	case OGF_HOST_CTL:
+		switch (ocf) {
+		case OCF_RESET:
+		case OCF_CREATE_NEW_UNIT_KEY:
+			return;
+		case OCF_SET_EVENT_MASK:
+		case OCF_SET_EVENT_MASK_PAGE_2:
+			set_event_mask_dump(level + 1, frm);
+			return;
+		case OCF_SET_EVENT_FLT:
+			set_event_flt_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_PIN_TYPE:
+			write_pin_type_dump(level + 1, frm);
+			return;
+		case OCF_READ_STORED_LINK_KEY:
+		case OCF_DELETE_STORED_LINK_KEY:
+			request_stored_link_key_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_STORED_LINK_KEY:
+			return_link_keys_dump(level + 1, frm);
+			return;
+		case OCF_CHANGE_LOCAL_NAME:
+			change_local_name_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_CLASS_OF_DEV:
+			write_class_of_dev_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_VOICE_SETTING:
+			write_voice_setting_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_CURRENT_IAC_LAP:
+			write_current_iac_lap_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_SCAN_ENABLE:
+		case OCF_WRITE_AUTH_ENABLE:
+		case OCF_SET_CONTROLLER_TO_HOST_FC:
+			write_scan_enable_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT:
+		case OCF_WRITE_CONN_ACCEPT_TIMEOUT:
+		case OCF_WRITE_PAGE_TIMEOUT:
+			write_page_timeout_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_PAGE_ACTIVITY:
+		case OCF_WRITE_INQ_ACTIVITY:
+			write_page_activity_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_INQUIRY_SCAN_TYPE:
+			write_inquiry_scan_type_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_ENCRYPT_MODE:
+		case OCF_WRITE_INQUIRY_MODE:
+		case OCF_WRITE_AFH_MODE:
+			write_inquiry_mode_dump(level + 1, frm);
+			return;
+		case OCF_SET_AFH_CLASSIFICATION:
+			set_afh_classification_dump(level + 1, frm);
+			return;
+		case OCF_READ_TRANSMIT_POWER_LEVEL:
+			request_transmit_power_level_dump(level + 1, frm);
+			return;
+		case OCF_HOST_BUFFER_SIZE:
+			host_buffer_size_dump(level + 1, frm);
+			return;
+		case OCF_HOST_NUM_COMP_PKTS:
+			num_comp_pkts_dump(level + 1, frm);
+			return;
+		case OCF_FLUSH:
+		case OCF_READ_LINK_SUPERVISION_TIMEOUT:
+		case OCF_REFRESH_ENCRYPTION_KEY:
+		case OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT:
+			generic_command_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_LINK_SUPERVISION_TIMEOUT:
+			write_link_supervision_timeout_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_EXT_INQUIRY_RESPONSE:
+			write_ext_inquiry_response_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_SIMPLE_PAIRING_MODE:
+		case OCF_WRITE_FLOW_CONTROL_MODE:
+			generic_write_mode_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL:
+			write_inquiry_transmit_power_level_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING:
+			write_default_error_data_reporting_dump(level + 1, frm);
+			return;
+		case OCF_ENHANCED_FLUSH:
+			enhanced_flush_dump(level + 1, frm);
+			return;
+		case OCF_SEND_KEYPRESS_NOTIFY:
+			send_keypress_notify_dump(level + 1, frm);
+			return;
+		}
+		break;
+
+	case OGF_INFO_PARAM:
+		switch (ocf) {
+		case OCF_READ_LOCAL_EXT_FEATURES:
+			request_local_ext_features_dump(level + 1, frm);
+			return;
+		}
+		break;
+
+	case OGF_STATUS_PARAM:
+		switch (ocf) {
+		case OCF_READ_LINK_QUALITY:
+		case OCF_READ_RSSI:
+		case OCF_READ_AFH_MAP:
+			generic_command_dump(level + 1, frm);
+			return;
+		case OCF_READ_CLOCK:
+			request_clock_dump(level + 1, frm);
+			return;
+		case OCF_WRITE_REMOTE_AMP_ASSOC:
+			write_remote_amp_assoc_cmd_dump(level + 1, frm);
+			return;
+		}
+		break;
+
+	case OGF_TESTING_CMD:
+		switch (ocf) {
+		case OCF_WRITE_LOOPBACK_MODE:
+		case OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE:
+			generic_write_mode_dump(level + 1, frm);
+			return;
+		}
+		break;
+
+	case OGF_LE_CTL:
+		switch (ocf) {
+		case OCF_LE_SET_EVENT_MASK:
+			le_set_event_mask_dump(level + 1, frm);
+			return;
+		case OCF_LE_READ_BUFFER_SIZE:
+		case OCF_LE_READ_LOCAL_SUPPORTED_FEATURES:
+		case OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER:
+			return;
+		case OCF_LE_SET_RANDOM_ADDRESS:
+			le_set_random_address_dump(level + 1, frm);
+			return;
+		case OCF_LE_SET_ADVERTISING_PARAMETERS:
+			le_set_advertising_parameters_dump(level + 1, frm);
+			return;
+		case OCF_LE_SET_SCAN_PARAMETERS:
+			le_set_scan_parameters_dump(level + 1, frm);
+			return;
+		case OCF_LE_SET_SCAN_ENABLE:
+			le_set_scan_enable_dump(level + 1, frm);
+			return;
+		case OCF_LE_CREATE_CONN:
+			le_create_connection_dump(level + 1, frm);
+			return;
+		}
+		break;
+	}
+
+	raw_dump(level, frm);
+}
+
+static inline void status_response_dump(int level, struct frame *frm)
+{
+	uint8_t status = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", status);
+
+	if (status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(status));
+	}
+
+	raw_dump(level, frm);
+}
+
+static inline void handle_response_dump(int level, struct frame *frm)
+{
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("handle %d\n", handle);
+
+	raw_dump(level, frm);
+}
+
+static inline void bdaddr_response_dump(int level, struct frame *frm)
+{
+	uint8_t status = p_get_u8(frm);
+	bdaddr_t *bdaddr = frm->ptr;
+	char addr[18];
+
+	frm->ptr += sizeof(bdaddr_t);
+	frm->len -= sizeof(bdaddr_t);
+
+	p_indent(level, frm);
+	p_ba2str(bdaddr, addr);
+	printf("status 0x%2.2x bdaddr %s\n", status, addr);
+
+	if (status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(status));
+	}
+
+	raw_dump(level, frm);
+}
+
+static inline void read_data_block_size_dump(int level, struct frame *frm)
+{
+	read_data_block_size_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", rp->status);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("Max ACL %d Block len %d Num blocks %d\n",
+			btohs(rp->max_acl_len), btohs(rp->data_block_len),
+							btohs(rp->num_blocks));
+	}
+}
+
+static inline void generic_response_dump(int level, struct frame *frm)
+{
+	uint8_t status = p_get_u8(frm);
+	uint16_t handle = btohs(htons(p_get_u16(frm)));
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", status, handle);
+
+	if (status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(status));
+	}
+
+	raw_dump(level, frm);
+}
+
+static inline void status_mode_dump(int level, struct frame *frm)
+{
+	uint8_t status = p_get_u8(frm);
+	uint8_t mode = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x mode 0x%2.2x\n", status, mode);
+
+	if (status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(status));
+	}
+}
+
+static inline void read_link_policy_dump(int level, struct frame *frm)
+{
+	read_link_policy_rp *rp = frm->ptr;
+	uint16_t policy = btohs(rp->policy);
+	char *str;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d policy 0x%2.2x\n",
+				rp->status, btohs(rp->handle), policy);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		str = hci_lptostr(policy);
+		if (str) {
+			p_indent(level, frm);
+			printf("Link policy: %s\n", str);
+			free(str);
+		}
+	}
+}
+
+static inline void read_default_link_policy_dump(int level, struct frame *frm)
+{
+	uint8_t status = p_get_u8(frm);
+	uint16_t policy = btohs(htons(p_get_u16(frm)));
+	char *str;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x policy 0x%2.2x\n", status, policy);
+
+	if (status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(status));
+	} else {
+		str = hci_lptostr(policy);
+		if (str) {
+			p_indent(level, frm);
+			printf("Link policy: %s\n", str);
+			free(str);
+		}
+	}
+}
+
+static inline void read_pin_type_dump(int level, struct frame *frm)
+{
+	read_pin_type_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x type %d\n", rp->status, rp->pin_type);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_stored_link_key_dump(int level, struct frame *frm)
+{
+	read_stored_link_key_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x max %d num %d\n",
+				rp->status, rp->max_keys, rp->num_keys);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void write_stored_link_key_dump(int level, struct frame *frm)
+{
+	write_stored_link_key_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x written %d\n", rp->status, rp->num_keys);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void delete_stored_link_key_dump(int level, struct frame *frm)
+{
+	delete_stored_link_key_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x deleted %d\n", rp->status, btohs(rp->num_keys));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_local_name_dump(int level, struct frame *frm)
+{
+	read_local_name_rp *rp = frm->ptr;
+	char name[249];
+	int i;
+
+	memset(name, 0, sizeof(name));
+	for (i = 0; i < 248 && rp->name[i]; i++)
+		if (isprint(rp->name[i]))
+			name[i] = rp->name[i];
+		else
+			name[i] = '.';
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x name \'%s\'\n", rp->status, name);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_class_of_dev_dump(int level, struct frame *frm)
+{
+	read_class_of_dev_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x class 0x%2.2x%2.2x%2.2x\n", rp->status,
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_voice_setting_dump(int level, struct frame *frm)
+{
+	read_voice_setting_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x voice setting 0x%4.4x\n",
+					rp->status, btohs(rp->voice_setting));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_current_iac_lap_dump(int level, struct frame *frm)
+{
+	read_current_iac_lap_rp *rp = frm->ptr;
+	int i;
+
+	for (i = 0; i < rp->num_current_iac; i++) {
+		p_indent(level, frm);
+		printf("IAC 0x%2.2x%2.2x%2.2x", rp->lap[i][2], rp->lap[i][1], rp->lap[i][0]);
+		if (rp->lap[i][2] == 0x9e && rp->lap[i][1] == 0x8b) {
+			switch (rp->lap[i][0]) {
+			case 0x00:
+				printf(" (Limited Inquiry Access Code)");
+				break;
+			case 0x33:
+				printf(" (General Inquiry Access Code)");
+				break;
+			}
+		}
+		printf("\n");
+	}
+}
+
+static inline void read_scan_enable_dump(int level, struct frame *frm)
+{
+	uint8_t status = p_get_u8(frm);
+	uint8_t enable = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x enable %d\n", status, enable);
+
+	if (status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(status));
+	}
+}
+
+static inline void read_page_timeout_dump(int level, struct frame *frm)
+{
+	read_page_timeout_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x timeout %d\n", rp->status, btohs(rp->timeout));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_page_activity_dump(int level, struct frame *frm)
+{
+	read_page_activity_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x interval %d window %d\n",
+			rp->status, btohs(rp->interval), btohs(rp->window));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_inquiry_scan_type_dump(int level, struct frame *frm)
+{
+	read_inquiry_scan_type_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x type %d\n", rp->status, rp->type);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_inquiry_mode_dump(int level, struct frame *frm)
+{
+	read_inquiry_mode_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x mode %d\n", rp->status, rp->mode);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_link_supervision_timeout_dump(int level, struct frame *frm)
+{
+	read_link_supervision_timeout_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d timeout %d\n",
+			rp->status, btohs(rp->handle), btohs(rp->timeout));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_transmit_power_level_dump(int level, struct frame *frm)
+{
+	read_transmit_power_level_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d level %d\n",
+				rp->status, btohs(rp->handle), rp->level);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_ext_inquiry_response_dump(int level, struct frame *frm)
+{
+	read_ext_inquiry_response_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x fec 0x%2.2x\n", rp->status, rp->fec);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		frm->ptr += 2;
+		frm->len -= 2;
+
+		ext_inquiry_response_dump(level, frm);
+	}
+}
+
+static inline void read_inquiry_transmit_power_level_dump(int level, struct frame *frm)
+{
+	read_inquiry_transmit_power_level_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x level %d\n", rp->status, rp->level);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_default_error_data_reporting_dump(int level, struct frame *frm)
+{
+	read_default_error_data_reporting_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x reporting %d\n", rp->status, rp->reporting);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_local_oob_data_dump(int level, struct frame *frm)
+{
+	read_local_oob_data_rp *rp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", rp->status);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("hash 0x");
+		for (i = 0; i < 16; i++)
+			printf("%02x", rp->hash[i]);
+		printf("\n");
+
+		p_indent(level, frm);
+		printf("randomizer 0x");
+		for (i = 0; i < 16; i++)
+			printf("%02x", rp->randomizer[i]);
+		printf("\n");
+	}
+}
+
+static inline void read_local_version_dump(int level, struct frame *frm)
+{
+	read_local_version_rp *rp = frm->ptr;
+	uint16_t manufacturer = btohs(rp->manufacturer);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", rp->status);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		char *lmpver = lmp_vertostr(rp->lmp_ver);
+		char *hciver = hci_vertostr(rp->hci_ver);
+
+		p_indent(level, frm);
+		printf("HCI Version: %s (0x%x) HCI Revision: 0x%x\n",
+					hciver ? hciver : "n/a",
+					rp->hci_ver, btohs(rp->hci_rev));
+		p_indent(level, frm);
+		printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n",
+					lmpver ? lmpver : "n/a",
+					rp->lmp_ver, btohs(rp->lmp_subver));
+		p_indent(level, frm);
+		printf("Manufacturer: %s (%d)\n",
+				bt_compidtostr(manufacturer), manufacturer);
+
+		if (lmpver)
+			free(lmpver);
+		if (hciver)
+			free(hciver);
+	}
+}
+
+static inline void read_local_commands_dump(int level, struct frame *frm)
+{
+	read_local_commands_rp *rp = frm->ptr;
+	int i, max = 0;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", rp->status);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		for (i = 0; i < 64; i++)
+			if (rp->commands[i])
+				max = i + 1;
+		p_indent(level, frm);
+		printf("Commands: ");
+		for (i = 0; i < (max > 32 ? 32 : max); i++)
+			printf("%2.2x", rp->commands[i]);
+		printf("\n");
+		if (max > 32) {
+			p_indent(level, frm);
+			printf("          ");
+			for (i = 32; i < max; i++)
+				printf("%2.2x", rp->commands[i]);
+			printf("\n");
+		}
+	}
+}
+
+static inline void read_local_features_dump(int level, struct frame *frm)
+{
+	read_local_features_rp *rp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", rp->status);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("Features:");
+		for (i = 0; i < 8; i++)
+			printf(" 0x%2.2x", rp->features[i]);
+		printf("\n");
+	}
+}
+
+static inline void read_local_ext_features_dump(int level, struct frame *frm)
+{
+	read_local_ext_features_rp *rp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x page %d max %d\n",
+		rp->status, rp->page_num, rp->max_page_num);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("Features:");
+		for (i = 0; i < 8; i++)
+			 printf(" 0x%2.2x", rp->features[i]);
+		printf("\n");
+	}
+}
+
+static inline void read_buffer_size_dump(int level, struct frame *frm)
+{
+	read_buffer_size_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x\n", rp->status);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("ACL MTU %d:%d SCO MTU %d:%d\n",
+				btohs(rp->acl_mtu), btohs(rp->acl_max_pkt),
+				rp->sco_mtu, btohs(rp->sco_max_pkt));
+	}
+}
+
+static inline void read_link_quality_dump(int level, struct frame *frm)
+{
+	read_link_quality_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d lq %d\n",
+			rp->status, btohs(rp->handle), rp->link_quality);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_rssi_dump(int level, struct frame *frm)
+{
+	read_rssi_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d rssi %d\n",
+				rp->status, btohs(rp->handle), rp->rssi);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_afh_map_dump(int level, struct frame *frm)
+{
+	read_afh_map_rp *rp = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d mode %d\n",
+				rp->status, btohs(rp->handle), rp->mode);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("AFH map: 0x");
+		for (i = 0; i < 10; i++)
+			printf("%2.2x", rp->map[i]);
+		printf("\n");
+	}
+}
+
+static inline void read_clock_dump(int level, struct frame *frm)
+{
+	read_clock_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d clock 0x%4.4x accuracy %d\n",
+					rp->status, btohs(rp->handle),
+					btohl(rp->clock), btohs(rp->accuracy));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void read_local_amp_info_dump(int level, struct frame *frm)
+{
+	read_local_amp_info_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x amp status 0x%2.2x\n",
+			rp->status, rp->amp_status);
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		p_indent(level, frm);
+		printf("total bandwidth %d, max guaranteed bandwidth %d\n",
+			btohl(rp->total_bandwidth),
+			btohl(rp->max_guaranteed_bandwidth));
+		p_indent(level, frm);
+		printf("min latency %d, max PDU %d, controller type 0x%2.2x\n",
+			btohl(rp->min_latency), btohl(rp->max_pdu_size),
+			rp->controller_type);
+		p_indent(level, frm);
+		printf("pal caps 0x%4.4x, max assoc len %d\n",
+			btohs(rp->pal_caps), btohs(rp->max_amp_assoc_length));
+		p_indent(level, frm);
+		printf("max flush timeout %d, best effort flush timeout %d\n",
+			btohl(rp->max_flush_timeout),
+			btohl(rp->best_effort_flush_timeout));
+	}
+}
+
+static inline void read_local_amp_assoc_dump(int level, struct frame *frm)
+{
+	read_local_amp_assoc_rp *rp = frm->ptr;
+	uint16_t len = btohs(rp->length);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle 0x%2.2x remaining len %d\n",
+			rp->status, rp->handle, len);
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	} else {
+		amp_assoc_dump(level + 1, rp->fragment, len);
+	}
+}
+
+static inline void write_remote_amp_assoc_dump(int level, struct frame *frm)
+{
+	write_remote_amp_assoc_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle 0x%2.2x\n", rp->status, rp->handle);
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void le_read_buffer_size_response_dump(int level, struct frame *frm)
+{
+	le_read_buffer_size_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x pktlen 0x%4.4x maxpkt 0x%2.2x\n", rp->status,
+			rp->pkt_len, rp->max_pkt);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void le_read_local_supported_features_dump(int level, struct frame *frm)
+{
+	int i;
+	le_read_local_supported_features_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x features 0x", rp->status);
+	for (i = 0; i < 8; i++)
+		printf("%2.2x", rp->features[i]);
+	printf(" (%s)\n", lefeatures2str(rp->features));
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void le_read_advertising_channel_tx_power_dump(int level, struct frame *frm)
+{
+	le_read_advertising_channel_tx_power_rp *rp = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x level 0x%x (dBm)\n", rp->status, rp->level);
+
+	if (rp->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(rp->status));
+	}
+}
+
+static inline void cmd_complete_dump(int level, struct frame *frm)
+{
+	evt_cmd_complete *evt = frm->ptr;
+	uint16_t opcode = btohs(evt->opcode);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+
+	if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR))
+		return;
+
+	p_indent(level, frm);
+	printf("%s (0x%2.2x|0x%4.4x) ncmd %d\n",
+				opcode2str(opcode), ogf, ocf, evt->ncmd);
+
+	frm->ptr += EVT_CMD_COMPLETE_SIZE;
+	frm->len -= EVT_CMD_COMPLETE_SIZE;
+
+	if (!(parser.flags & DUMP_VERBOSE)) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (ogf) {
+	case OGF_LINK_CTL:
+		switch (ocf) {
+		case OCF_INQUIRY_CANCEL:
+		case OCF_PERIODIC_INQUIRY:
+		case OCF_EXIT_PERIODIC_INQUIRY:
+		case OCF_READ_REMOTE_EXT_FEATURES:
+			status_response_dump(level, frm);
+			return;
+		case OCF_CREATE_CONN_CANCEL:
+		case OCF_REMOTE_NAME_REQ_CANCEL:
+		case OCF_PIN_CODE_REPLY:
+		case OCF_LINK_KEY_REPLY:
+		case OCF_PIN_CODE_NEG_REPLY:
+		case OCF_LINK_KEY_NEG_REPLY:
+		case OCF_USER_CONFIRM_REPLY:
+		case OCF_USER_CONFIRM_NEG_REPLY:
+		case OCF_USER_PASSKEY_REPLY:
+		case OCF_USER_PASSKEY_NEG_REPLY:
+		case OCF_REMOTE_OOB_DATA_REPLY:
+		case OCF_REMOTE_OOB_DATA_NEG_REPLY:
+		case OCF_IO_CAPABILITY_REPLY:
+		case OCF_IO_CAPABILITY_NEG_REPLY:
+			bdaddr_response_dump(level, frm);
+			return;
+		}
+		break;
+
+	case OGF_LINK_POLICY:
+		switch (ocf) {
+		case OCF_READ_LINK_POLICY:
+			read_link_policy_dump(level, frm);
+			return;
+		case OCF_WRITE_LINK_POLICY:
+		case OCF_SNIFF_SUBRATING:
+			generic_response_dump(level, frm);
+			return;
+		case OCF_READ_DEFAULT_LINK_POLICY:
+			read_default_link_policy_dump(level, frm);
+			return;
+		case OCF_WRITE_DEFAULT_LINK_POLICY:
+			status_response_dump(level, frm);
+			return;
+		}
+		break;
+
+	case OGF_HOST_CTL:
+		switch (ocf) {
+		case OCF_READ_PIN_TYPE:
+			read_pin_type_dump(level, frm);
+			return;
+		case OCF_READ_STORED_LINK_KEY:
+			read_stored_link_key_dump(level, frm);
+			return;
+		case OCF_WRITE_STORED_LINK_KEY:
+			write_stored_link_key_dump(level, frm);
+			return;
+		case OCF_DELETE_STORED_LINK_KEY:
+			delete_stored_link_key_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_NAME:
+			read_local_name_dump(level, frm);
+			return;
+		case OCF_READ_CLASS_OF_DEV:
+			read_class_of_dev_dump(level, frm);
+			return;
+		case OCF_READ_VOICE_SETTING:
+			read_voice_setting_dump(level, frm);
+			return;
+		case OCF_READ_CURRENT_IAC_LAP:
+			read_current_iac_lap_dump(level, frm);
+			return;
+		case OCF_READ_SCAN_ENABLE:
+		case OCF_READ_AUTH_ENABLE:
+			read_scan_enable_dump(level, frm);
+			return;
+		case OCF_READ_CONN_ACCEPT_TIMEOUT:
+		case OCF_READ_PAGE_TIMEOUT:
+		case OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT:
+			read_page_timeout_dump(level, frm);
+			return;
+		case OCF_READ_PAGE_ACTIVITY:
+		case OCF_READ_INQ_ACTIVITY:
+			read_page_activity_dump(level, frm);
+			return;
+		case OCF_READ_INQUIRY_SCAN_TYPE:
+			read_inquiry_scan_type_dump(level, frm);
+			return;
+		case OCF_READ_ENCRYPT_MODE:
+		case OCF_READ_INQUIRY_MODE:
+		case OCF_READ_AFH_MODE:
+			read_inquiry_mode_dump(level, frm);
+			return;
+		case OCF_READ_LINK_SUPERVISION_TIMEOUT:
+			read_link_supervision_timeout_dump(level, frm);
+			return;
+		case OCF_READ_TRANSMIT_POWER_LEVEL:
+			read_transmit_power_level_dump(level, frm);
+			return;
+		case OCF_READ_EXT_INQUIRY_RESPONSE:
+			read_ext_inquiry_response_dump(level, frm);
+			return;
+		case OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL:
+			read_inquiry_transmit_power_level_dump(level, frm);
+			return;
+		case OCF_READ_DEFAULT_ERROR_DATA_REPORTING:
+			read_default_error_data_reporting_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_OOB_DATA:
+			read_local_oob_data_dump(level, frm);
+			return;
+		case OCF_READ_SIMPLE_PAIRING_MODE:
+		case OCF_READ_FLOW_CONTROL_MODE:
+			status_mode_dump(level, frm);
+			return;
+		case OCF_FLUSH:
+		case OCF_WRITE_LINK_SUPERVISION_TIMEOUT:
+			generic_response_dump(level, frm);
+			return;
+		case OCF_RESET:
+		case OCF_SET_EVENT_MASK:
+		case OCF_SET_EVENT_FLT:
+		case OCF_WRITE_PIN_TYPE:
+		case OCF_CREATE_NEW_UNIT_KEY:
+		case OCF_CHANGE_LOCAL_NAME:
+		case OCF_WRITE_CLASS_OF_DEV:
+		case OCF_WRITE_VOICE_SETTING:
+		case OCF_WRITE_CURRENT_IAC_LAP:
+		case OCF_WRITE_SCAN_ENABLE:
+		case OCF_WRITE_AUTH_ENABLE:
+		case OCF_WRITE_ENCRYPT_MODE:
+		case OCF_WRITE_CONN_ACCEPT_TIMEOUT:
+		case OCF_WRITE_PAGE_TIMEOUT:
+		case OCF_WRITE_PAGE_ACTIVITY:
+		case OCF_WRITE_INQ_ACTIVITY:
+		case OCF_WRITE_INQUIRY_SCAN_TYPE:
+		case OCF_WRITE_INQUIRY_MODE:
+		case OCF_WRITE_AFH_MODE:
+		case OCF_SET_AFH_CLASSIFICATION:
+		case OCF_WRITE_EXT_INQUIRY_RESPONSE:
+		case OCF_WRITE_SIMPLE_PAIRING_MODE:
+		case OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL:
+		case OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING:
+		case OCF_SET_CONTROLLER_TO_HOST_FC:
+		case OCF_HOST_BUFFER_SIZE:
+		case OCF_REFRESH_ENCRYPTION_KEY:
+		case OCF_SEND_KEYPRESS_NOTIFY:
+		case OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT:
+		case OCF_SET_EVENT_MASK_PAGE_2:
+		case OCF_WRITE_LOCATION_DATA:
+		case OCF_WRITE_FLOW_CONTROL_MODE:
+		case OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT:
+		case OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT:
+			status_response_dump(level, frm);
+			return;
+		}
+		break;
+
+	case OGF_INFO_PARAM:
+		switch (ocf) {
+		case OCF_READ_LOCAL_VERSION:
+			read_local_version_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_COMMANDS:
+			read_local_commands_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_FEATURES:
+			read_local_features_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_EXT_FEATURES:
+			read_local_ext_features_dump(level, frm);
+			return;
+		case OCF_READ_BUFFER_SIZE:
+			read_buffer_size_dump(level, frm);
+			return;
+		case OCF_READ_BD_ADDR:
+			bdaddr_response_dump(level, frm);
+			return;
+		case OCF_READ_DATA_BLOCK_SIZE:
+			read_data_block_size_dump(level, frm);
+			return;
+		}
+		break;
+
+	case OGF_STATUS_PARAM:
+		switch (ocf) {
+		case OCF_READ_FAILED_CONTACT_COUNTER:
+		case OCF_RESET_FAILED_CONTACT_COUNTER:
+			status_response_dump(level, frm);
+			return;
+		case OCF_READ_LINK_QUALITY:
+			read_link_quality_dump(level, frm);
+			return;
+		case OCF_READ_RSSI:
+			read_rssi_dump(level, frm);
+			return;
+		case OCF_READ_AFH_MAP:
+			read_afh_map_dump(level, frm);
+			return;
+		case OCF_READ_CLOCK:
+			read_clock_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_AMP_INFO:
+			read_local_amp_info_dump(level, frm);
+			return;
+		case OCF_READ_LOCAL_AMP_ASSOC:
+			read_local_amp_assoc_dump(level, frm);
+			return;
+		case OCF_WRITE_REMOTE_AMP_ASSOC:
+			write_remote_amp_assoc_dump(level, frm);
+			return;
+		}
+		break;
+
+	case OGF_TESTING_CMD:
+		switch (ocf) {
+		case OCF_READ_LOOPBACK_MODE:
+			status_mode_dump(level, frm);
+			return;
+		case OCF_WRITE_LOOPBACK_MODE:
+		case OCF_ENABLE_DEVICE_UNDER_TEST_MODE:
+		case OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE:
+			status_response_dump(level, frm);
+			return;
+		}
+		break;
+
+	case OGF_LE_CTL:
+		switch (ocf) {
+		case OCF_LE_SET_EVENT_MASK:
+		case OCF_LE_SET_RANDOM_ADDRESS:
+		case OCF_LE_SET_ADVERTISING_PARAMETERS:
+		case OCF_LE_SET_ADVERTISING_DATA:
+		case OCF_LE_SET_SCAN_RESPONSE_DATA:
+		case OCF_LE_SET_ADVERTISE_ENABLE:
+		case OCF_LE_SET_SCAN_PARAMETERS:
+		case OCF_LE_SET_SCAN_ENABLE:
+		case OCF_LE_CREATE_CONN:
+		case OCF_LE_CLEAR_WHITE_LIST:
+		case OCF_LE_ADD_DEVICE_TO_WHITE_LIST:
+		case OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST:
+		case OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION:
+		case OCF_LE_RECEIVER_TEST:
+		case OCF_LE_TRANSMITTER_TEST:
+			status_response_dump(level, frm);
+			return;
+		case OCF_LE_READ_BUFFER_SIZE:
+			le_read_buffer_size_response_dump(level, frm);
+			return;
+		case OCF_LE_READ_LOCAL_SUPPORTED_FEATURES:
+			le_read_local_supported_features_dump(level, frm);
+			return;
+		case OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER:
+			le_read_advertising_channel_tx_power_dump(level, frm);
+			return;
+		}
+		break;
+	}
+
+	raw_dump(level, frm);
+}
+
+static inline void cmd_status_dump(int level, struct frame *frm)
+{
+	evt_cmd_status *evt = frm->ptr;
+	uint16_t opcode = btohs(evt->opcode);
+	uint16_t ogf = cmd_opcode_ogf(opcode);
+	uint16_t ocf = cmd_opcode_ocf(opcode);
+
+	if (ogf == OGF_VENDOR_CMD && (parser.flags & DUMP_NOVENDOR))
+		return;
+
+	p_indent(level, frm);
+	printf("%s (0x%2.2x|0x%4.4x) status 0x%2.2x ncmd %d\n",
+			opcode2str(opcode), ogf, ocf, evt->status, evt->ncmd);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void hardware_error_dump(int level, struct frame *frm)
+{
+	evt_hardware_error *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("code %d\n", evt->code);
+}
+
+static inline void inq_result_dump(int level, struct frame *frm)
+{
+	uint8_t num = p_get_u8(frm);
+	char addr[18];
+	int i;
+
+	for (i = 0; i < num; i++) {
+		inquiry_info *info = frm->ptr;
+
+		p_ba2str(&info->bdaddr, addr);
+
+		p_indent(level, frm);
+		printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x\n",
+			addr, info->pscan_rep_mode, btohs(info->clock_offset),
+			info->dev_class[2], info->dev_class[1], info->dev_class[0]);
+
+		frm->ptr += INQUIRY_INFO_SIZE;
+		frm->len -= INQUIRY_INFO_SIZE;
+	}
+}
+
+static inline void conn_complete_dump(int level, struct frame *frm)
+{
+	evt_conn_complete *evt = frm->ptr;
+	char addr[18];
+
+	p_ba2str(&evt->bdaddr, addr);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d bdaddr %s type %s encrypt 0x%2.2x\n",
+			evt->status, btohs(evt->handle), addr,
+			linktype2str(evt->link_type), evt->encr_mode);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void conn_request_dump(int level, struct frame *frm)
+{
+	evt_conn_request *evt = frm->ptr;
+	char addr[18];
+
+	p_ba2str(&evt->bdaddr, addr);
+
+	p_indent(level, frm);
+	printf("bdaddr %s class 0x%2.2x%2.2x%2.2x type %s\n",
+			addr, evt->dev_class[2], evt->dev_class[1],
+			evt->dev_class[0], linktype2str(evt->link_type));
+}
+
+static inline void disconn_complete_dump(int level, struct frame *frm)
+{
+	evt_disconn_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d reason 0x%2.2x\n",
+				evt->status, btohs(evt->handle), evt->reason);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else if (evt->reason > 0) {
+		p_indent(level, frm);
+		printf("Reason: %s\n", status2str(evt->reason));
+	}
+}
+
+static inline void remote_name_req_complete_dump(int level, struct frame *frm)
+{
+	evt_remote_name_req_complete *evt = frm->ptr;
+	char addr[18], name[249];
+	int i;
+
+	p_ba2str(&evt->bdaddr, addr);
+
+	memset(name, 0, sizeof(name));
+	for (i = 0; i < 248 && evt->name[i]; i++)
+		if (isprint(evt->name[i]))
+			name[i] = evt->name[i];
+		else
+			name[i] = '.';
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x bdaddr %s name '%s'\n", evt->status, addr, name);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void master_link_key_complete_dump(int level, struct frame *frm)
+{
+	evt_master_link_key_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d flag %d\n",
+				evt->status, btohs(evt->handle), evt->key_flag);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void encrypt_change_dump(int level, struct frame *frm)
+{
+	evt_encrypt_change *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d encrypt 0x%2.2x\n",
+				evt->status, btohs(evt->handle), evt->encrypt);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void read_remote_features_complete_dump(int level, struct frame *frm)
+{
+	evt_read_remote_features_complete *evt = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Features:");
+		for (i = 0; i < 8; i++)
+			printf(" 0x%2.2x", evt->features[i]);
+		printf("\n");
+	}
+}
+
+static inline void read_remote_version_complete_dump(int level, struct frame *frm)
+{
+	evt_read_remote_version_complete *evt = frm->ptr;
+	uint16_t manufacturer = btohs(evt->manufacturer);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		char *lmpver = lmp_vertostr(evt->lmp_ver);
+
+		p_indent(level, frm);
+		printf("LMP Version: %s (0x%x) LMP Subversion: 0x%x\n",
+			lmpver ? lmpver : "n/a", evt->lmp_ver,
+			btohs(evt->lmp_subver));
+		p_indent(level, frm);
+		printf("Manufacturer: %s (%d)\n",
+			bt_compidtostr(manufacturer), manufacturer);
+
+		if (lmpver)
+			free(lmpver);
+	}
+}
+
+static inline void qos_setup_complete_dump(int level, struct frame *frm)
+{
+	evt_qos_setup_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d flags %d\n",
+				evt->status, btohs(evt->handle), evt->flags);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Service type: %d\n", evt->qos.service_type);
+		p_indent(level, frm);
+		printf("Token rate: %d\n", btohl(evt->qos.token_rate));
+		p_indent(level, frm);
+		printf("Peak bandwith: %d\n", btohl(evt->qos.peak_bandwidth));
+		p_indent(level, frm);
+		printf("Latency: %d\n", btohl(evt->qos.latency));
+		p_indent(level, frm);
+		printf("Delay variation: %d\n", btohl(evt->qos.delay_variation));
+	}
+}
+
+static inline void role_change_dump(int level, struct frame *frm)
+{
+	evt_role_change *evt = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("status 0x%2.2x bdaddr %s role 0x%2.2x\n",
+						evt->status, addr, evt->role);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Role: %s\n", role2str(evt->role));
+	}
+}
+
+static inline void mode_change_dump(int level, struct frame *frm)
+{
+	evt_mode_change *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d mode 0x%2.2x interval %d\n",
+		evt->status, btohs(evt->handle), evt->mode, btohs(evt->interval));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Mode: %s\n", mode2str(evt->mode));
+	}
+}
+
+static inline void pin_code_req_dump(int level, struct frame *frm)
+{
+	evt_pin_code_req *evt = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("bdaddr %s\n", addr);
+}
+
+static inline void link_key_notify_dump(int level, struct frame *frm)
+{
+	evt_link_key_notify *evt = frm->ptr;
+	char addr[18];
+	int i;
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("bdaddr %s key ", addr);
+	for (i = 0; i < 16; i++)
+		if (parser.flags & DUMP_NOVENDOR)
+			printf("**");
+		else
+			printf("%2.2X", evt->link_key[i]);
+	printf(" type %d\n", evt->key_type);
+
+	p_indent(level, frm);
+	printf("Type: %s\n", keytype2str(evt->key_type));
+}
+
+static inline void max_slots_change_dump(int level, struct frame *frm)
+{
+	evt_max_slots_change *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d slots %d\n", btohs(evt->handle), evt->max_slots);
+}
+
+static inline void data_buffer_overflow_dump(int level, struct frame *frm)
+{
+	evt_data_buffer_overflow *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("type %s\n", linktype2str(evt->link_type));
+}
+
+static inline void read_clock_offset_complete_dump(int level, struct frame *frm)
+{
+	evt_read_clock_offset_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d clkoffset 0x%4.4x\n",
+		evt->status, btohs(evt->handle), btohs(evt->clock_offset));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void conn_ptype_changed_dump(int level, struct frame *frm)
+{
+	evt_conn_ptype_changed *evt = frm->ptr;
+	uint16_t ptype = btohs(evt->ptype);
+	char *str;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d ptype 0x%4.4x\n",
+				evt->status, btohs(evt->handle), ptype);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		str = hci_ptypetostr(ptype);
+		if (str) {
+			p_indent(level, frm);
+			printf("Packet type: %s\n", str);
+			free(str);
+		}
+	}
+}
+
+static inline void pscan_rep_mode_change_dump(int level, struct frame *frm)
+{
+	evt_pscan_rep_mode_change *evt = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("bdaddr %s mode %d\n", addr, evt->pscan_rep_mode);
+}
+
+static inline void flow_spec_complete_dump(int level, struct frame *frm)
+{
+	evt_flow_spec_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle 0x%4.4x flags %d %s\n",
+				evt->status, btohs(evt->handle), evt->flags,
+				evt->direction == 0 ? "outgoing" : "incoming");
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Service type: %d\n", evt->qos.service_type);
+		p_indent(level, frm);
+		printf("Token rate: %d\n", btohl(evt->qos.token_rate));
+		p_indent(level, frm);
+		printf("Peak bandwith: %d\n", btohl(evt->qos.peak_bandwidth));
+		p_indent(level, frm);
+		printf("Latency: %d\n", btohl(evt->qos.latency));
+		p_indent(level, frm);
+		printf("Delay variation: %d\n", btohl(evt->qos.delay_variation));
+	}
+}
+
+static inline void inq_result_with_rssi_dump(int level, struct frame *frm)
+{
+	uint8_t num = p_get_u8(frm);
+	char addr[18];
+	int i;
+
+	if (!num)
+		return;
+
+	if (frm->len / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) {
+		for (i = 0; i < num; i++) {
+			inquiry_info_with_rssi_and_pscan_mode *info = frm->ptr;
+
+			p_indent(level, frm);
+
+			p_ba2str(&info->bdaddr, addr);
+			printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n",
+				addr, info->pscan_rep_mode, btohs(info->clock_offset),
+				info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi);
+
+			frm->ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
+			frm->len -= INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
+		}
+	} else {
+		for (i = 0; i < num; i++) {
+			inquiry_info_with_rssi *info = frm->ptr;
+
+			p_indent(level, frm);
+
+			p_ba2str(&info->bdaddr, addr);
+			printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n",
+				addr, info->pscan_rep_mode, btohs(info->clock_offset),
+				info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi);
+
+			frm->ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
+			frm->len -= INQUIRY_INFO_WITH_RSSI_SIZE;
+		}
+	}
+}
+
+static inline void read_remote_ext_features_complete_dump(int level, struct frame *frm)
+{
+	evt_read_remote_ext_features_complete *evt = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d page %d max %d\n",
+					evt->status, btohs(evt->handle),
+					evt->page_num, evt->max_page_num);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Features:");
+		for (i = 0; i < 8; i++)
+			printf(" 0x%2.2x", evt->features[i]);
+		printf("\n");
+	}
+}
+
+static inline void sync_conn_complete_dump(int level, struct frame *frm)
+{
+	evt_sync_conn_complete *evt = frm->ptr;
+	char addr[18];
+
+	p_ba2str(&evt->bdaddr, addr);
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d bdaddr %s type %s\n",
+					evt->status, btohs(evt->handle), addr,
+					evt->link_type == 0 ? "SCO" : "eSCO");
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Air mode: %s\n", airmode2str(evt->air_mode));
+	}
+}
+
+static inline void sync_conn_changed_dump(int level, struct frame *frm)
+{
+	evt_sync_conn_changed *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void sniff_subrating_event_dump(int level, struct frame *frm)
+{
+	evt_sniff_subrating *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", evt->status, btohs(evt->handle));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else {
+		p_indent(level, frm);
+		printf("max latency transmit %d receive %d\n",
+					btohs(evt->max_tx_latency),
+					btohs(evt->max_rx_latency));
+
+		p_indent(level, frm);
+		printf("min timeout remote %d local %d\n",
+					btohs(evt->min_remote_timeout),
+					btohs(evt->min_local_timeout));
+	}
+}
+
+static inline void extended_inq_result_dump(int level, struct frame *frm)
+{
+	uint8_t num = p_get_u8(frm);
+	char addr[18];
+	int i;
+
+	for (i = 0; i < num; i++) {
+		extended_inquiry_info *info = frm->ptr;
+
+		p_ba2str(&info->bdaddr, addr);
+
+		p_indent(level, frm);
+		printf("bdaddr %s mode %d clkoffset 0x%4.4x class 0x%2.2x%2.2x%2.2x rssi %d\n",
+			addr, info->pscan_rep_mode, btohs(info->clock_offset),
+			info->dev_class[2], info->dev_class[1], info->dev_class[0], info->rssi);
+
+		frm->ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
+		frm->len -= INQUIRY_INFO_WITH_RSSI_SIZE;
+
+		ext_inquiry_response_dump(level, frm);
+	}
+}
+
+static inline void link_supervision_timeout_changed_dump(int level, struct frame *frm)
+{
+	evt_link_supervision_timeout_changed *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("handle %d timeout %d\n",
+				btohs(evt->handle), btohs(evt->timeout));
+}
+
+static inline void user_passkey_notify_dump(int level, struct frame *frm)
+{
+	evt_user_passkey_notify *evt = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("bdaddr %s passkey %d\n", addr, btohl(evt->passkey));
+}
+
+static inline void keypress_notify_dump(int level, struct frame *frm)
+{
+	evt_keypress_notify *evt = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("bdaddr %s type %d\n", addr, evt->type);
+}
+
+static inline void remote_host_features_notify_dump(int level, struct frame *frm)
+{
+	evt_remote_host_features_notify *evt = frm->ptr;
+	char addr[18];
+	int i;
+
+	p_indent(level, frm);
+	p_ba2str(&evt->bdaddr, addr);
+	printf("bdaddr %s\n", addr);
+
+	p_indent(level, frm);
+	printf("Features:");
+	for (i = 0; i < 8; i++)
+		printf(" 0x%2.2x", evt->features[i]);
+	printf("\n");
+}
+
+static inline void evt_le_conn_complete_dump(int level, struct frame *frm)
+{
+	evt_le_connection_complete *evt = frm->ptr;
+	char addr[18];
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d, role %s\n",
+					evt->status, btohs(evt->handle),
+					evt->role ? "slave" : "master");
+
+	p_indent(level, frm);
+	p_ba2str(&evt->peer_bdaddr, addr);
+	printf("bdaddr %s (%s)\n", addr, bdaddrtype2str(evt->peer_bdaddr_type));
+}
+
+static inline void evt_le_advertising_report_dump(int level, struct frame *frm)
+{
+	uint8_t num_reports = p_get_u8(frm);
+	const uint8_t RSSI_SIZE = 1;
+
+	while (num_reports--) {
+		char addr[18];
+		le_advertising_info *info = frm->ptr;
+		int offset = 0;
+
+		p_ba2str(&info->bdaddr, addr);
+
+		p_indent(level, frm);
+		printf("%s (%d)\n", evttype2str(info->evt_type), info->evt_type);
+
+		p_indent(level, frm);
+		printf("bdaddr %s (%s)\n", addr,
+					bdaddrtype2str(info->bdaddr_type));
+
+		while (offset < info->length) {
+			int eir_data_len = info->data[offset];
+
+			ext_inquiry_data_dump(level, frm, &info->data[offset]);
+
+			offset += eir_data_len + 1;
+		}
+
+		frm->ptr += LE_ADVERTISING_INFO_SIZE + info->length;
+		frm->len -= LE_ADVERTISING_INFO_SIZE + info->length;
+
+		p_indent(level, frm);
+		printf("RSSI: %d\n", ((int8_t *) frm->ptr)[frm->len - 1]);
+
+		frm->ptr += RSSI_SIZE;
+		frm->len -= RSSI_SIZE;
+	}
+}
+
+static inline void evt_le_conn_update_complete_dump(int level,
+							struct frame *frm)
+{
+	evt_le_connection_update_complete *uevt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", uevt->status, btohs(uevt->handle));
+
+	p_indent(level, frm);
+	printf("interval %.2fms, latency %.2fms, superv. timeout %.2fms\n",
+			btohs(uevt->interval) * 1.25, btohs(uevt->latency) * 1.25,
+			btohs(uevt->supervision_timeout) * 10.0);
+}
+
+static inline void evt_le_read_remote_used_features_complete_dump(int level, struct frame *frm)
+{
+	int i;
+	evt_le_read_remote_used_features_complete *revt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle %d\n", revt->status, btohs(revt->handle));
+
+	if (revt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(revt->status));
+	} else {
+		p_indent(level, frm);
+		printf("Features:");
+		for (i = 0; i < 8; i++)
+			printf(" 0x%2.2x", revt->features[i]);
+		printf("\n");
+	}
+}
+
+static inline void le_meta_ev_dump(int level, struct frame *frm)
+{
+	evt_le_meta_event *mevt = frm->ptr;
+	uint8_t subevent;
+
+	subevent = mevt->subevent;
+
+	frm->ptr += EVT_LE_META_EVENT_SIZE;
+	frm->len -= EVT_LE_META_EVENT_SIZE;
+
+	p_indent(level, frm);
+	printf("%s\n", ev_le_meta_str[subevent]);
+
+	switch (mevt->subevent) {
+	case EVT_LE_CONN_COMPLETE:
+		evt_le_conn_complete_dump(level + 1, frm);
+		break;
+	case EVT_LE_ADVERTISING_REPORT:
+		evt_le_advertising_report_dump(level + 1, frm);
+		break;
+	case EVT_LE_CONN_UPDATE_COMPLETE:
+		evt_le_conn_update_complete_dump(level + 1, frm);
+		break;
+	case EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE:
+		evt_le_read_remote_used_features_complete_dump(level + 1, frm);
+		break;
+	default:
+		raw_dump(level, frm);
+		break;
+	}
+}
+
+static inline void phys_link_complete_dump(int level, struct frame *frm)
+{
+	evt_physical_link_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x phy handle 0x%2.2x\n", evt->status, evt->handle);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void disconn_phys_link_complete_dump(int level, struct frame *frm)
+{
+	evt_disconn_physical_link_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle 0x%2.2x reason 0x%2.2x\n",
+				evt->status, evt->handle, evt->reason);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	} else if (evt->reason > 0) {
+		p_indent(level, frm);
+		printf("Reason: %s\n", status2str(evt->reason));
+	}
+}
+
+static inline void phys_link_loss_warning_dump(int level, struct frame *frm)
+{
+	evt_physical_link_loss_warning *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("phy handle 0x%2.2x reason 0x%2.2x\n", evt->handle, evt->reason);
+}
+
+static inline void phys_link_handle_dump(int level, struct frame *frm)
+{
+	evt_physical_link_recovery *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("phy handle 0x%2.2x\n", evt->handle);
+}
+
+static inline void logical_link_complete_dump(int level, struct frame *frm)
+{
+	evt_logical_link_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x log handle 0x%4.4x phy handle 0x%2.2x"
+							" tx_flow_id %d\n",
+			evt->status, btohs(evt->log_handle), evt->handle,
+			evt->tx_flow_id);
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void flow_spec_modify_dump(int level, struct frame *frm)
+{
+	evt_flow_spec_modify_complete *evt = frm->ptr;
+
+	p_indent(level, frm);
+	printf("status 0x%2.2x handle 0x%4.4x\n",
+					evt->status, btohs(evt->handle));
+
+	if (evt->status > 0) {
+		p_indent(level, frm);
+		printf("Error: %s\n", status2str(evt->status));
+	}
+}
+
+static inline void num_completed_blocks_dump(int level, struct frame *frm)
+{
+	evt_num_completed_blocks *evt = frm->ptr;
+	int i;
+
+	p_indent(level, frm);
+	printf("Total num blocks %d Num handles %d\n",
+			btohs(evt->total_num_blocks), evt->num_handles);
+
+	for (i = 0; i < evt->num_handles; i++) {
+		cmplt_handle *h = &evt->handles[i];
+
+		p_indent(level + 1, frm);
+		printf("Handle 0x%4.4x: Num complt pkts %d Num complt blks %d\n",
+				btohs(h->handle), btohs(h->num_cmplt_pkts),
+				btohs(h->num_cmplt_blks));
+	}
+}
+
+static inline void event_dump(int level, struct frame *frm)
+{
+	hci_event_hdr *hdr = frm->ptr;
+	uint8_t event = hdr->evt;
+
+	if (p_filter(FILT_HCI))
+		return;
+
+	if (event <= EVENT_NUM) {
+		p_indent(level, frm);
+		printf("HCI Event: %s (0x%2.2x) plen %d\n",
+					event_str[hdr->evt], hdr->evt, hdr->plen);
+	} else if (hdr->evt == EVT_TESTING) {
+		p_indent(level, frm);
+		printf("HCI Event: Testing (0x%2.2x) plen %d\n", hdr->evt, hdr->plen);
+	} else if (hdr->evt == EVT_VENDOR) {
+		uint16_t manufacturer;
+
+		if (parser.flags & DUMP_NOVENDOR)
+			return;
+
+		p_indent(level, frm);
+		printf("HCI Event: Vendor (0x%2.2x) plen %d\n", hdr->evt, hdr->plen);
+
+		manufacturer = get_manufacturer();
+
+		switch (manufacturer) {
+		case 0:
+		case 37:
+		case 48:
+			frm->ptr += HCI_EVENT_HDR_SIZE;
+			frm->len -= HCI_EVENT_HDR_SIZE;
+			ericsson_dump(level + 1, frm);
+			return;
+		case 10:
+			frm->ptr += HCI_EVENT_HDR_SIZE;
+			frm->len -= HCI_EVENT_HDR_SIZE;
+			csr_dump(level + 1, frm);
+			return;
+		}
+	} else {
+		p_indent(level, frm);
+		printf("HCI Event: code 0x%2.2x plen %d\n", hdr->evt, hdr->plen);
+	}
+
+	frm->ptr += HCI_EVENT_HDR_SIZE;
+	frm->len -= HCI_EVENT_HDR_SIZE;
+
+	if (event == EVT_CMD_COMPLETE) {
+		evt_cmd_complete *cc = frm->ptr;
+		if (cc->opcode == cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION)) {
+			read_local_version_rp *rp = frm->ptr + EVT_CMD_COMPLETE_SIZE;
+			manufacturer = rp->manufacturer;
+		}
+	}
+
+	if (event == EVT_DISCONN_COMPLETE) {
+		evt_disconn_complete *evt = frm->ptr;
+		l2cap_clear(btohs(evt->handle));
+	}
+
+	if (!(parser.flags & DUMP_VERBOSE)) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (event) {
+	case EVT_LOOPBACK_COMMAND:
+		command_dump(level + 1, frm);
+		break;
+	case EVT_CMD_COMPLETE:
+		cmd_complete_dump(level + 1, frm);
+		break;
+	case EVT_CMD_STATUS:
+		cmd_status_dump(level + 1, frm);
+		break;
+	case EVT_HARDWARE_ERROR:
+		hardware_error_dump(level + 1, frm);
+		break;
+	case EVT_FLUSH_OCCURRED:
+	case EVT_QOS_VIOLATION:
+		handle_response_dump(level + 1, frm);
+		break;
+	case EVT_INQUIRY_COMPLETE:
+		status_response_dump(level + 1, frm);
+		break;
+	case EVT_INQUIRY_RESULT:
+		inq_result_dump(level + 1, frm);
+		break;
+	case EVT_CONN_COMPLETE:
+		conn_complete_dump(level + 1, frm);
+		break;
+	case EVT_CONN_REQUEST:
+		conn_request_dump(level + 1, frm);
+		break;
+	case EVT_DISCONN_COMPLETE:
+	case EVT_DISCONNECT_LOGICAL_LINK_COMPLETE:
+		disconn_complete_dump(level + 1, frm);
+		break;
+	case EVT_AUTH_COMPLETE:
+	case EVT_CHANGE_CONN_LINK_KEY_COMPLETE:
+		generic_response_dump(level + 1, frm);
+		break;
+	case EVT_MASTER_LINK_KEY_COMPLETE:
+		master_link_key_complete_dump(level + 1, frm);
+		break;
+	case EVT_REMOTE_NAME_REQ_COMPLETE:
+		remote_name_req_complete_dump(level + 1, frm);
+		break;
+	case EVT_ENCRYPT_CHANGE:
+		encrypt_change_dump(level + 1, frm);
+		break;
+	case EVT_READ_REMOTE_FEATURES_COMPLETE:
+		read_remote_features_complete_dump(level + 1, frm);
+		break;
+	case EVT_READ_REMOTE_VERSION_COMPLETE:
+		read_remote_version_complete_dump(level + 1, frm);
+		break;
+	case EVT_QOS_SETUP_COMPLETE:
+		qos_setup_complete_dump(level + 1, frm);
+		break;
+	case EVT_ROLE_CHANGE:
+		role_change_dump(level + 1, frm);
+		break;
+	case EVT_NUM_COMP_PKTS:
+		num_comp_pkts_dump(level + 1, frm);
+		break;
+	case EVT_MODE_CHANGE:
+		mode_change_dump(level + 1, frm);
+		break;
+	case EVT_RETURN_LINK_KEYS:
+		return_link_keys_dump(level + 1, frm);
+		break;
+	case EVT_PIN_CODE_REQ:
+	case EVT_LINK_KEY_REQ:
+	case EVT_IO_CAPABILITY_REQUEST:
+	case EVT_USER_PASSKEY_REQUEST:
+	case EVT_REMOTE_OOB_DATA_REQUEST:
+		pin_code_req_dump(level + 1, frm);
+		break;
+	case EVT_LINK_KEY_NOTIFY:
+		link_key_notify_dump(level + 1, frm);
+		break;
+	case EVT_DATA_BUFFER_OVERFLOW:
+		data_buffer_overflow_dump(level + 1, frm);
+		break;
+	case EVT_MAX_SLOTS_CHANGE:
+		max_slots_change_dump(level + 1, frm);
+		break;
+	case EVT_READ_CLOCK_OFFSET_COMPLETE:
+		read_clock_offset_complete_dump(level + 1, frm);
+		break;
+	case EVT_CONN_PTYPE_CHANGED:
+		conn_ptype_changed_dump(level + 1, frm);
+		break;
+	case EVT_PSCAN_REP_MODE_CHANGE:
+		pscan_rep_mode_change_dump(level + 1, frm);
+		break;
+	case EVT_FLOW_SPEC_COMPLETE:
+		flow_spec_complete_dump(level + 1, frm);
+		break;
+	case EVT_INQUIRY_RESULT_WITH_RSSI:
+		inq_result_with_rssi_dump(level + 1, frm);
+		break;
+	case EVT_READ_REMOTE_EXT_FEATURES_COMPLETE:
+		read_remote_ext_features_complete_dump(level + 1, frm);
+		break;
+	case EVT_SYNC_CONN_COMPLETE:
+		sync_conn_complete_dump(level + 1, frm);
+		break;
+	case EVT_SYNC_CONN_CHANGED:
+		sync_conn_changed_dump(level + 1, frm);
+		break;
+	case EVT_SNIFF_SUBRATING:
+		sniff_subrating_event_dump(level + 1, frm);
+		break;
+	case EVT_EXTENDED_INQUIRY_RESULT:
+		extended_inq_result_dump(level + 1, frm);
+		break;
+	case EVT_ENCRYPTION_KEY_REFRESH_COMPLETE:
+		generic_response_dump(level + 1, frm);
+		break;
+	case EVT_SIMPLE_PAIRING_COMPLETE:
+		bdaddr_response_dump(level + 1, frm);
+		break;
+	case EVT_LINK_SUPERVISION_TIMEOUT_CHANGED:
+		link_supervision_timeout_changed_dump(level + 1, frm);
+		break;
+	case EVT_ENHANCED_FLUSH_COMPLETE:
+		generic_command_dump(level + 1, frm);
+		break;
+	case EVT_IO_CAPABILITY_RESPONSE:
+		io_capability_reply_dump(level + 1, frm);
+		break;
+	case EVT_USER_CONFIRM_REQUEST:
+	case EVT_USER_PASSKEY_NOTIFY:
+		user_passkey_notify_dump(level + 1, frm);
+		break;
+	case EVT_KEYPRESS_NOTIFY:
+		keypress_notify_dump(level + 1, frm);
+		break;
+	case EVT_REMOTE_HOST_FEATURES_NOTIFY:
+		remote_host_features_notify_dump(level + 1, frm);
+		break;
+	case EVT_LE_META_EVENT:
+		le_meta_ev_dump(level + 1, frm);
+		break;
+	case EVT_PHYSICAL_LINK_COMPLETE:
+		phys_link_complete_dump(level + 1, frm);
+		break;
+	case EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE:
+		disconn_phys_link_complete_dump(level + 1, frm);
+		break;
+	case EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING:
+		phys_link_loss_warning_dump(level + 1, frm);
+		break;
+	case EVT_PHYSICAL_LINK_RECOVERY:
+	case EVT_CHANNEL_SELECTED:
+		phys_link_handle_dump(level + 1, frm);
+		break;
+	case EVT_LOGICAL_LINK_COMPLETE:
+		logical_link_complete_dump(level + 1, frm);
+		break;
+	case EVT_FLOW_SPEC_MODIFY_COMPLETE:
+		flow_spec_modify_dump(level + 1, frm);
+		break;
+	case EVT_NUMBER_COMPLETED_BLOCKS:
+		num_completed_blocks_dump(level + 1, frm);
+		break;
+	default:
+		raw_dump(level, frm);
+		break;
+	}
+}
+
+static inline void acl_dump(int level, struct frame *frm)
+{
+	hci_acl_hdr *hdr = (void *) frm->ptr;
+	uint16_t handle = btohs(hdr->handle);
+	uint16_t dlen = btohs(hdr->dlen);
+	uint8_t flags = acl_flags(handle);
+
+	if (!p_filter(FILT_HCI)) {
+		p_indent(level, frm);
+		printf("ACL data: handle %d flags 0x%2.2x dlen %d\n",
+			acl_handle(handle), flags, dlen);
+		level++;
+	}
+
+	frm->ptr += HCI_ACL_HDR_SIZE;
+	frm->len -= HCI_ACL_HDR_SIZE;
+	frm->flags  = flags;
+	frm->handle = acl_handle(handle);
+
+	if (parser.filter & ~FILT_HCI)
+		l2cap_dump(level, frm);
+	else
+		raw_dump(level, frm);
+}
+
+static inline void sco_dump(int level, struct frame *frm)
+{
+	hci_sco_hdr *hdr = (void *) frm->ptr;
+	uint16_t handle = btohs(hdr->handle);
+	uint8_t flags = acl_flags(handle);
+	int len;
+
+	if (frm->audio_fd > fileno(stderr)) {
+		len = write(frm->audio_fd, frm->ptr + HCI_SCO_HDR_SIZE, hdr->dlen);
+		if (len < 0)
+			return;
+	}
+
+	if (!p_filter(FILT_SCO)) {
+		p_indent(level, frm);
+		printf("SCO data: handle %d flags 0x%2.2x dlen %d\n",
+				acl_handle(handle), flags, hdr->dlen);
+		level++;
+
+		frm->ptr += HCI_SCO_HDR_SIZE;
+		frm->len -= HCI_SCO_HDR_SIZE;
+		raw_dump(level, frm);
+	}
+}
+
+static inline void vendor_dump(int level, struct frame *frm)
+{
+	if (p_filter(FILT_HCI))
+		return;
+
+	if (frm->dev_id == HCI_DEV_NONE) {
+		uint16_t device = btohs(htons(p_get_u16(frm)));
+		uint16_t proto = btohs(htons(p_get_u16(frm)));
+		uint16_t type = btohs(htons(p_get_u16(frm)));
+		uint16_t plen = btohs(htons(p_get_u16(frm)));
+
+		p_indent(level, frm);
+
+		printf("System %s: device hci%d proto 0x%2.2x type 0x%2.2x plen %d\n",
+			frm->in ? "event" : "command", device, proto, type, plen);
+
+		raw_dump(level, frm);
+		return;
+	}
+
+	if (parser.flags & DUMP_NOVENDOR)
+		return;
+
+	if (get_manufacturer() == 12) {
+		bpa_dump(level, frm);
+		return;
+	}
+
+	p_indent(level, frm);
+	printf("Vendor data: len %d\n", frm->len);
+	raw_dump(level, frm);
+}
+
+void hci_dump(int level, struct frame *frm)
+{
+	uint8_t type = *(uint8_t *)frm->ptr;
+
+	frm->ptr++; frm->len--;
+
+	switch (type) {
+	case HCI_COMMAND_PKT:
+		command_dump(level, frm);
+		break;
+
+	case HCI_EVENT_PKT:
+		event_dump(level, frm);
+		break;
+
+	case HCI_ACLDATA_PKT:
+		acl_dump(level, frm);
+		break;
+
+	case HCI_SCODATA_PKT:
+		sco_dump(level, frm);
+		break;
+
+	case HCI_VENDOR_PKT:
+		vendor_dump(level, frm);
+		break;
+
+	default:
+		if (p_filter(FILT_HCI))
+			break;
+
+		p_indent(level, frm);
+		printf("Unknown: type 0x%2.2x len %d\n", type, frm->len);
+		raw_dump(level, frm);
+		break;
+	}
+}
diff --git a/bluez/repo/tools/parser/hcrp.c b/bluez/repo/tools/parser/hcrp.c
new file mode 100644
index 0000000..444ec23
--- /dev/null
+++ b/bluez/repo/tools/parser/hcrp.c
@@ -0,0 +1,113 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+static char *pid2str(uint16_t pid)
+{
+	switch (pid) {
+	case 0x0001:
+		return "CreditGrant";
+	case 0x0002:
+		return "CreditRequest";
+	case 0x0003:
+		return "CreditReturn";
+	case 0x0004:
+		return "CreditQuery";
+	case 0x0005:
+		return "GetLPTStatus";
+	case 0x0006:
+		return "Get1284ID";
+	case 0x0007:
+		return "SoftReset";
+	case 0x0008:
+		return "HardRest";
+	case 0x0009:
+		return "RegisterNotification";
+	case 0x000A:
+		return "NotificationConnectionAlive";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *status2str(uint16_t status)
+{
+	switch (status) {
+	case 0x0000:
+		return "Feature unsupported";
+	case 0x0001:
+		return "Success";
+	case 0x0002:
+		return "Credit synchronization error";
+	case 0xFFFF:
+		return "Generic error";
+	default:
+		return "Unknown";
+	}
+}
+
+void hcrp_dump(int level, struct frame *frm)
+{
+	uint16_t pid, tid, plen, status;
+	uint32_t credits;
+
+	pid = p_get_u16(frm);
+	tid = p_get_u16(frm);
+	plen = p_get_u16(frm);
+
+	p_indent(level, frm);
+
+	printf("HCRP %s %s: tid 0x%x plen %d",
+			pid2str(pid), frm->in ? "rsp" : "cmd",  tid, plen);
+
+	if (frm->in) {
+		status = p_get_u16(frm);
+		printf(" status %d (%s)\n", status, status2str(status));
+	} else
+		printf("\n");
+
+	if (pid == 0x0001 && !frm->in) {
+		credits = p_get_u32(frm);
+		p_indent(level + 1, frm);
+		printf("credits %d\n", credits);
+	}
+
+	if (pid == 0x0002 && frm->in) {
+		credits = p_get_u32(frm);
+		p_indent(level + 1, frm);
+		printf("credits %d\n", credits);
+	}
+
+	raw_dump(level + 1, frm);
+}
diff --git a/bluez/repo/tools/parser/hidp.c b/bluez/repo/tools/parser/hidp.c
new file mode 100644
index 0000000..1adf8ed
--- /dev/null
+++ b/bluez/repo/tools/parser/hidp.c
@@ -0,0 +1,168 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+static char *type2str(uint8_t head)
+{
+	switch (head & 0xf0) {
+	case 0x00:
+		return "Handshake";
+	case 0x10:
+		return "Control";
+	case 0x40:
+		return "Get report";
+	case 0x50:
+		return "Set report";
+	case 0x60:
+		return "Get protocol";
+	case 0x70:
+		return "Set protocol";
+	case 0x80:
+		return "Get idle";
+	case 0x90:
+		return "Set idle";
+	case 0xa0:
+		return "Data";
+	case 0xb0:
+		return "Data continuation";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *result2str(uint8_t head)
+{
+	switch (head & 0x0f) {
+	case 0x00:
+		return "Successful";
+	case 0x01:
+		return "Not ready";
+	case 0x02:
+		return "Invalid report ID";
+	case 0x03:
+		return "Unsupported request";
+	case 0x04:
+		return "Invalid parameter";
+	case 0x0e:
+		return "Unknown";
+	case 0x0f:
+		return "Fatal";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *operation2str(uint8_t head)
+{
+	switch (head & 0x0f) {
+	case 0x00:
+		return "No operation";
+	case 0x01:
+		return "Hard reset";
+	case 0x02:
+		return "Soft reset";
+	case 0x03:
+		return "Suspend";
+	case 0x04:
+		return "Exit suspend";
+	case 0x05:
+		return "Virtual cable unplug";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *report2str(uint8_t head)
+{
+	switch (head & 0x03) {
+	case 0x00:
+		return "Other report";
+	case 0x01:
+		return "Input report";
+	case 0x02:
+		return "Output report";
+	case 0x03:
+		return "Feature report";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *protocol2str(uint8_t head)
+{
+	switch (head & 0x01) {
+	case 0x00:
+		return "Boot protocol";
+	case 0x01:
+		return "Report protocol";
+	default:
+		return "Reserved";
+	}
+}
+
+void hidp_dump(int level, struct frame *frm)
+{
+	uint8_t hdr;
+	char *param;
+
+	hdr = p_get_u8(frm);
+
+	switch (hdr & 0xf0) {
+	case 0x00:
+		param = result2str(hdr);
+		break;
+	case 0x10:
+		param = operation2str(hdr);
+		break;
+	case 0x60:
+	case 0x70:
+		param = protocol2str(hdr);
+		break;
+	case 0x40:
+	case 0x50:
+	case 0xa0:
+	case 0xb0:
+		param = report2str(hdr);
+		break;
+	default:
+		param = "";
+		break;
+	}
+
+	p_indent(level, frm);
+
+	printf("HIDP: %s: %s\n", type2str(hdr), param);
+
+	raw_dump(level, frm);
+}
diff --git a/bluez/repo/tools/parser/l2cap.c b/bluez/repo/tools/parser/l2cap.c
new file mode 100644
index 0000000..a057964
--- /dev/null
+++ b/bluez/repo/tools/parser/l2cap.c
@@ -0,0 +1,1635 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "parser.h"
+#include "sdp.h"
+#include "l2cap.h"
+#include "lib/hci.h"
+#include "lib/a2mp.h"
+#include "lib/amp.h"
+
+typedef struct {
+	uint16_t handle;
+	struct frame frm;
+} handle_info;
+#define HANDLE_TABLE_SIZE 10
+
+static handle_info handle_table[HANDLE_TABLE_SIZE];
+
+typedef struct {
+	uint16_t handle;
+	uint16_t cid;
+	uint16_t psm;
+	uint16_t num;
+	uint8_t mode;
+	uint8_t ext_ctrl;
+} cid_info;
+#define CID_TABLE_SIZE 20
+
+static cid_info cid_table[2][CID_TABLE_SIZE];
+
+#define SCID cid_table[0]
+#define DCID cid_table[1]
+
+/* Can we move this to l2cap.h? */
+struct features {
+	char	*name;
+	int	flag;
+};
+
+static struct features l2cap_features[] = {
+	{ "Flow control mode",			L2CAP_FEAT_FLOWCTL	},
+	{ "Retransmission mode",		L2CAP_FEAT_RETRANS	},
+	{ "Bi-directional QoS",			L2CAP_FEAT_BIDIR_QOS	},
+	{ "Enhanced Retransmission mode",	L2CAP_FEAT_ERTM		},
+	{ "Streaming mode",			L2CAP_FEAT_STREAMING	},
+	{ "FCS Option",				L2CAP_FEAT_FCS		},
+	{ "Extended Flow Specification",	L2CAP_FEAT_EXT_FLOW	},
+	{ "Fixed Channels",			L2CAP_FEAT_FIXED_CHAN	},
+	{ "Extended Window Size",		L2CAP_FEAT_EXT_WINDOW	},
+	{ "Unicast Connectless Data Reception",	L2CAP_FEAT_UCD		},
+	{ 0 }
+};
+
+static struct features l2cap_fix_chan[] = {
+	{ "L2CAP Signalling Channel",		L2CAP_FC_L2CAP		},
+	{ "L2CAP Connless",			L2CAP_FC_CONNLESS	},
+	{ "AMP Manager Protocol",		L2CAP_FC_A2MP		},
+	{ 0 }
+};
+
+static struct frame *add_handle(uint16_t handle)
+{
+	register handle_info *t = handle_table;
+	register int i;
+
+	for (i = 0; i < HANDLE_TABLE_SIZE; i++)
+		if (!t[i].handle) {
+			t[i].handle = handle;
+			return &t[i].frm;
+		}
+	return NULL;
+}
+
+static struct frame *get_frame(uint16_t handle)
+{
+	register handle_info *t = handle_table;
+	register int i;
+
+	for (i = 0; i < HANDLE_TABLE_SIZE; i++)
+		if (t[i].handle == handle)
+			return &t[i].frm;
+
+	return add_handle(handle);
+}
+
+static void add_cid(int in, uint16_t handle, uint16_t cid, uint16_t psm)
+{
+	register cid_info *table = cid_table[in];
+	register int i, pos = -1;
+	uint16_t num = 1;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++) {
+		if ((pos < 0 && !table[i].cid) || table[i].cid == cid)
+			pos = i;
+		if (table[i].psm == psm)
+			num++;
+	}
+
+	if (pos >= 0) {
+		table[pos].handle = handle;
+		table[pos].cid    = cid;
+		table[pos].psm    = psm;
+		table[pos].num    = num;
+		table[pos].mode   = 0;
+	}
+}
+
+static void del_cid(int in, uint16_t dcid, uint16_t scid)
+{
+	register int t, i;
+	uint16_t cid[2];
+
+	if (!in) {
+		cid[0] = dcid;
+		cid[1] = scid;
+	} else {
+		cid[0] = scid;
+		cid[1] = dcid;
+	}
+
+	for (t = 0; t < 2; t++) {
+		for (i = 0; i < CID_TABLE_SIZE; i++)
+			if (cid_table[t][i].cid == cid[t]) {
+				cid_table[t][i].handle = 0;
+				cid_table[t][i].cid    = 0;
+				cid_table[t][i].psm    = 0;
+				cid_table[t][i].num    = 0;
+				cid_table[t][i].mode   = 0;
+				break;
+			}
+	}
+}
+
+static void del_handle(uint16_t handle)
+{
+	register int t, i;
+
+	for (t = 0; t < 2; t++) {
+		for (i = 0; i < CID_TABLE_SIZE; i++)
+			if (cid_table[t][i].handle == handle) {
+				cid_table[t][i].handle = 0;
+				cid_table[t][i].cid    = 0;
+				cid_table[t][i].psm    = 0;
+				cid_table[t][i].num    = 0;
+				cid_table[t][i].mode   = 0;
+				break;
+			}
+	}
+}
+static uint16_t get_psm(int in, uint16_t handle, uint16_t cid)
+{
+	register cid_info *table = cid_table[in];
+	register int i;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			return table[i].psm;
+	return parser.defpsm;
+}
+
+static uint16_t get_num(int in, uint16_t handle, uint16_t cid)
+{
+	register cid_info *table = cid_table[in];
+	register int i;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			return table[i].num;
+	return 0;
+}
+
+static void set_mode(int in, uint16_t handle, uint16_t cid, uint8_t mode)
+{
+	register cid_info *table = cid_table[in];
+	register int i;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			table[i].mode = mode;
+}
+
+static uint8_t get_mode(int in, uint16_t handle, uint16_t cid)
+{
+	register cid_info *table = cid_table[in];
+	register int i;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			return table[i].mode;
+	return 0;
+}
+
+static void set_ext_ctrl(int in, uint16_t handle, uint16_t cid,
+							uint8_t ext_ctrl)
+{
+	register cid_info *table = cid_table[in];
+	register int i;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			table[i].ext_ctrl = ext_ctrl;
+}
+
+static uint8_t get_ext_ctrl(int in, uint16_t handle, uint16_t cid)
+{
+	register cid_info *table = cid_table[in];
+	register int i;
+
+	for (i = 0; i < CID_TABLE_SIZE; i++)
+		if (table[i].handle == handle && table[i].cid == cid)
+			return table[i].ext_ctrl;
+	return 0;
+}
+
+static uint32_t get_val(uint8_t *ptr, uint8_t len)
+{
+	switch (len) {
+	case 1:
+		return *ptr;
+	case 2:
+		return get_le16(ptr);
+	case 4:
+		return get_le32(ptr);
+	}
+	return 0;
+}
+
+static char *reason2str(uint16_t reason)
+{
+	switch (reason) {
+	case 0x0000:
+		return "Command not understood";
+	case 0x0001:
+		return "Signalling MTU exceeded";
+	case 0x0002:
+		return "Invalid CID in request";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *a2mpreason2str(uint16_t reason)
+{
+	switch (reason) {
+	case A2MP_COMMAND_NOT_RECOGNIZED:
+		return "Command not recognized";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *connresult2str(uint16_t result)
+{
+	switch (result) {
+	case 0x0000:
+		return "Connection successful";
+	case 0x0001:
+		return "Connection pending";
+	case 0x0002:
+		return "Connection refused - PSM not supported";
+	case 0x0003:
+		return "Connection refused - security block";
+	case 0x0004:
+		return "Connection refused - no resources available";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *status2str(uint16_t status)
+{
+	switch (status) {
+	case 0x0000:
+		return "No futher information available";
+	case 0x0001:
+		return "Authentication pending";
+	case 0x0002:
+		return "Authorization pending";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *confresult2str(uint16_t result)
+{
+	switch (result) {
+	case L2CAP_CONF_SUCCESS:
+		return "Success";
+	case L2CAP_CONF_UNACCEPT:
+		return "Failure - unacceptable parameters";
+	case L2CAP_CONF_REJECT:
+		return "Failure - rejected (no reason provided)";
+	case L2CAP_CONF_UNKNOWN:
+		return "Failure - unknown options";
+	case L2CAP_CONF_PENDING:
+		return "Pending";
+	case L2CAP_CONF_EFS_REJECT:
+		return "Failure - flowspec reject";
+	default:
+		return "Reserved";
+	}
+}
+static char *inforesult2str(uint16_t result)
+{
+	switch (result) {
+	case 0x0000:
+		return "Success";
+	case 0x0001:
+		return "Not supported";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *type2str(uint8_t type)
+{
+	switch (type) {
+	case L2CAP_SERVTYPE_NOTRAFFIC:
+		return "No traffic";
+	case L2CAP_SERVTYPE_BESTEFFORT:
+		return "Best Effort";
+	case L2CAP_SERVTYPE_GUARANTEED:
+		return "Guaranteed";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *mode2str(uint8_t mode)
+{
+	switch (mode) {
+	case 0x00:
+		return "Basic";
+	case 0x01:
+		return "Retransmission";
+	case 0x02:
+		return "Flow control";
+	case 0x03:
+		return "Enhanced Retransmission";
+	case 0x04:
+		return "Streaming";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *fcs2str(uint8_t fcs)
+{
+	switch (fcs) {
+	case 0x00:
+		return "No FCS";
+	case 0x01:
+		return "CRC16 Check";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *sar2str(uint8_t sar)
+{
+	switch (sar) {
+	case L2CAP_SAR_UNSEGMENTED:
+		return "Unsegmented";
+	case L2CAP_SAR_START:
+		return "Start";
+	case L2CAP_SAR_END:
+		return "End";
+	case L2CAP_SAR_CONTINUE:
+		return "Continuation";
+	default:
+		return "Bad SAR";
+
+	}
+}
+
+static char *supervisory2str(uint8_t supervisory)
+{
+	switch (supervisory) {
+	case L2CAP_SUPER_RR:
+		return "Receiver Ready (RR)";
+	case L2CAP_SUPER_REJ:
+		return "Reject (REJ)";
+	case L2CAP_SUPER_RNR:
+		return "Receiver Not Ready (RNR)";
+	case L2CAP_SUPER_SREJ:
+		return "Select Reject (SREJ)";
+	default:
+		return "Bad Supervisory";
+	}
+}
+
+static char *ampctrltype2str(uint8_t type)
+{
+	switch (type) {
+	case HCI_BREDR:
+		return "BR-EDR";
+	case HCI_AMP:
+		return "802.11 AMP";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *ampctrlstatus2str(uint8_t status)
+{
+	switch (status) {
+	case AMP_CTRL_POWERED_DOWN:
+		return "Powered down";
+	case AMP_CTRL_BLUETOOTH_ONLY:
+		return "Bluetooth only";
+	case AMP_CTRL_NO_CAPACITY:
+		return "No capacity";
+	case AMP_CTRL_LOW_CAPACITY:
+		return "Low capacity";
+	case AMP_CTRL_MEDIUM_CAPACITY:
+		return "Medium capacity";
+	case AMP_CTRL_HIGH_CAPACITY:
+		return "High capacity";
+	case AMP_CTRL_FULL_CAPACITY:
+		return "Full capacity";
+	default:
+		return "Reserved";
+
+	}
+}
+
+static char *a2mpstatus2str(uint8_t status)
+{
+	switch (status) {
+	case A2MP_STATUS_SUCCESS:
+		return "Success";
+	case A2MP_STATUS_INVALID_CTRL_ID:
+		return "Invalid Controller ID";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *a2mpcplstatus2str(uint8_t status)
+{
+	switch (status) {
+	case A2MP_STATUS_SUCCESS:
+		return "Success";
+	case A2MP_STATUS_INVALID_CTRL_ID:
+		return "Invalid Controller ID";
+	case A2MP_STATUS_UNABLE_START_LINK_CREATION:
+		return "Failed - Unable to start link creation";
+	case A2MP_STATUS_COLLISION_OCCURED:
+		return "Failed - Collision occured";
+	case A2MP_STATUS_DISCONN_REQ_RECVD:
+		return "Failed - Disconnect physical link received";
+	case A2MP_STATUS_PHYS_LINK_EXISTS:
+		return "Failed - Physical link already exists";
+	case A2MP_STATUS_SECURITY_VIOLATION:
+		return "Failed - Security violation";
+	default:
+		return "Reserved";
+	}
+}
+
+static char *a2mpdplstatus2str(uint8_t status)
+{
+	switch (status) {
+	case A2MP_STATUS_SUCCESS:
+		return "Success";
+	case A2MP_STATUS_INVALID_CTRL_ID:
+		return "Invalid Controller ID";
+	case A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS:
+		return "Failed - No Physical Link exists";
+	default:
+		return "Reserved";
+	}
+}
+
+static inline void command_rej(int level, struct frame *frm)
+{
+	l2cap_cmd_rej *h = frm->ptr;
+	uint16_t reason = btohs(h->reason);
+	uint32_t cid;
+
+	printf("Command rej: reason %d", reason);
+
+	switch (reason) {
+	case 0x0001:
+		printf(" mtu %d\n", get_val(frm->ptr + L2CAP_CMD_REJ_SIZE, 2));
+		break;
+	case 0x0002:
+		cid = get_val(frm->ptr + L2CAP_CMD_REJ_SIZE, 4);
+		printf(" dcid 0x%4.4x scid 0x%4.4x\n", cid & 0xffff, cid >> 16);
+		break;
+	default:
+		printf("\n");
+		break;
+	}
+
+	p_indent(level + 1, frm);
+	printf("%s\n", reason2str(reason));
+}
+
+static inline void conn_req(int level, struct frame *frm)
+{
+	l2cap_conn_req *h = frm->ptr;
+	uint16_t psm = btohs(h->psm);
+	uint16_t scid = btohs(h->scid);
+
+	add_cid(frm->in, frm->handle, scid, psm);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Connect req: psm %d scid 0x%4.4x\n", psm, scid);
+}
+
+static inline void conn_rsp(int level, struct frame *frm)
+{
+	l2cap_conn_rsp *h = frm->ptr;
+	uint16_t scid = btohs(h->scid);
+	uint16_t dcid = btohs(h->dcid);
+	uint16_t result = btohs(h->result);
+	uint16_t status = btohs(h->status);
+	uint16_t psm;
+
+	switch (h->result) {
+	case L2CAP_CR_SUCCESS:
+		if ((psm = get_psm(!frm->in, frm->handle, scid)))
+			add_cid(frm->in, frm->handle, dcid, psm);
+		break;
+
+	case L2CAP_CR_PEND:
+		break;
+
+	default:
+		del_cid(frm->in, dcid, scid);
+		break;
+	}
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Connect rsp: dcid 0x%4.4x scid 0x%4.4x result %d status %d\n",
+		dcid, scid, result, status);
+
+	p_indent(level + 1, frm);
+	printf("%s", connresult2str(result));
+
+	if (result == 0x0001)
+		printf(" - %s\n", status2str(status));
+	else
+		printf("\n");
+}
+
+static void conf_rfc(void *ptr, int len, int in, uint16_t handle,
+								uint16_t cid)
+{
+	uint8_t mode;
+
+	mode = *((uint8_t *) ptr);
+	set_mode(!in, handle, cid, mode);
+
+	printf("RFC 0x%02x (%s", mode, mode2str(mode));
+	if (mode >= 0x01 && mode <= 0x04) {
+		uint8_t txwin, maxtrans;
+		uint16_t rto, mto, mps;
+		txwin = *((uint8_t *) (ptr + 1));
+		maxtrans = *((uint8_t *) (ptr + 2));
+		rto = get_le16(ptr + 3);
+		mto = get_le16(ptr + 5);
+		mps = get_le16(ptr + 7);
+		printf(", TxWin %d, MaxTx %d, RTo %d, MTo %d, MPS %d",
+					txwin, maxtrans, rto, mto, mps);
+	}
+	printf(")");
+}
+
+static void conf_efs(void *ptr)
+{
+	uint8_t id, ser_type;
+	uint16_t max_sdu;
+	uint32_t sdu_itime, access_lat, flush_to;
+
+	id = get_val(ptr, sizeof(id));
+	ser_type = get_val(ptr + 1, sizeof(ser_type));
+	max_sdu = get_val(ptr + 2, sizeof(max_sdu));
+	sdu_itime = get_val(ptr + 4, sizeof(sdu_itime));
+	access_lat = get_val(ptr + 8, sizeof(access_lat));
+	flush_to = get_val(ptr + 12, sizeof(flush_to));
+
+	printf("EFS (Id 0x%02x, SerType %s, MaxSDU 0x%04x, SDUitime 0x%08x, "
+			"AccLat 0x%08x, FlushTO 0x%08x)",
+			id, type2str(ser_type), max_sdu, sdu_itime,
+			access_lat, flush_to);
+}
+
+static void conf_fcs(void *ptr, int len)
+{
+	uint8_t fcs;
+
+	fcs = *((uint8_t *) ptr);
+	printf("FCS Option");
+	if (len > 0)
+		printf(" 0x%2.2x (%s)", fcs, fcs2str(fcs));
+}
+
+static void conf_opt(int level, void *ptr, int len, int in, uint16_t handle,
+								uint16_t cid)
+{
+	int indent = 0;
+	p_indent(level, 0);
+	while (len > 0) {
+		l2cap_conf_opt *h = ptr;
+
+		ptr += L2CAP_CONF_OPT_SIZE + h->len;
+		len -= L2CAP_CONF_OPT_SIZE + h->len;
+
+		if (h->type & 0x80)
+			printf("[");
+
+		if (indent++) {
+			printf("\n");
+			p_indent(level, 0);
+		}
+
+		switch (h->type & 0x7f) {
+		case L2CAP_CONF_MTU:
+			set_mode(in, handle, cid, 0x00);
+			printf("MTU");
+			if (h->len > 0)
+				printf(" %d", get_val(h->val, h->len));
+			break;
+
+		case L2CAP_CONF_FLUSH_TO:
+			printf("FlushTO");
+			if (h->len > 0)
+				printf(" %d", get_val(h->val, h->len));
+			break;
+
+		case L2CAP_CONF_QOS:
+			printf("QoS");
+			if (h->len > 0)
+				printf(" 0x%02x (%s)", *(h->val + 1), type2str(*(h->val + 1)));
+			break;
+
+		case L2CAP_CONF_RFC:
+			conf_rfc(h->val, h->len, in, handle, cid);
+			break;
+
+		case L2CAP_CONF_FCS:
+			conf_fcs(h->val, h->len);
+			break;
+
+		case L2CAP_CONF_EFS:
+			conf_efs(h->val);
+			break;
+
+		case L2CAP_CONF_EWS:
+			printf("EWS");
+			if (h->len > 0)
+				printf(" %d", get_val(h->val, h->len));
+			set_ext_ctrl(in, handle, cid, 1);
+			break;
+
+		default:
+			printf("Unknown (type %2.2x, len %d)", h->type & 0x7f, h->len);
+			break;
+		}
+
+		if (h->type & 0x80)
+			printf("] ");
+		else
+			printf(" ");
+	}
+	printf("\n");
+}
+
+static void conf_list(int level, uint8_t *list, int len)
+{
+	int i;
+
+	p_indent(level, 0);
+	for (i = 0; i < len; i++) {
+		switch (list[i] & 0x7f) {
+		case L2CAP_CONF_MTU:
+			printf("MTU ");
+			break;
+		case L2CAP_CONF_FLUSH_TO:
+			printf("FlushTo ");
+			break;
+		case L2CAP_CONF_QOS:
+			printf("QoS ");
+			break;
+		case L2CAP_CONF_RFC:
+			printf("RFC ");
+			break;
+		case L2CAP_CONF_FCS:
+			printf("FCS ");
+			break;
+		case L2CAP_CONF_EFS:
+			printf("EFS ");
+			break;
+		case L2CAP_CONF_EWS:
+			printf("EWS ");
+			break;
+		default:
+			printf("%2.2x ", list[i] & 0x7f);
+			break;
+		}
+	}
+	printf("\n");
+}
+
+static inline void conf_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_conf_req *h = frm->ptr;
+	uint16_t dcid = btohs(h->dcid);
+	int clen = btohs(cmd->len) - L2CAP_CONF_REQ_SIZE;
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Config req: dcid 0x%4.4x flags 0x%2.2x clen %d\n",
+			dcid, btohs(h->flags), clen);
+
+	if (clen > 0)
+		conf_opt(level + 1, h->data, clen, frm->in, frm->handle,
+									dcid);
+}
+
+static inline void conf_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_conf_rsp *h = frm->ptr;
+	uint16_t scid = btohs(h->scid);
+	uint16_t result = btohs(h->result);
+	int clen = btohs(cmd->len) - L2CAP_CONF_RSP_SIZE;
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Config rsp: scid 0x%4.4x flags 0x%2.2x result %d clen %d\n",
+			scid, btohs(h->flags), result, clen);
+
+	if (clen > 0) {
+		if (result) {
+			p_indent(level + 1, frm);
+			printf("%s\n", confresult2str(result));
+		}
+		if (result == 0x0003)
+			conf_list(level + 1, h->data, clen);
+		else
+			conf_opt(level + 1, h->data, clen, frm->in,
+							frm->handle, scid);
+	} else {
+		p_indent(level + 1, frm);
+		printf("%s\n", confresult2str(result));
+	}
+}
+
+static inline void disconn_req(int level, struct frame *frm)
+{
+	l2cap_disconn_req *h = frm->ptr;
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Disconn req: dcid 0x%4.4x scid 0x%4.4x\n",
+			btohs(h->dcid), btohs(h->scid));
+}
+
+static inline void disconn_rsp(int level, struct frame *frm)
+{
+	l2cap_disconn_rsp *h = frm->ptr;
+	uint16_t dcid = btohs(h->dcid);
+	uint16_t scid = btohs(h->scid);
+
+	del_cid(frm->in, dcid, scid);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Disconn rsp: dcid 0x%4.4x scid 0x%4.4x\n",
+			btohs(h->dcid), btohs(h->scid));
+}
+
+static inline void echo_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Echo req: dlen %d\n", btohs(cmd->len));
+	raw_dump(level, frm);
+}
+
+static inline void echo_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Echo rsp: dlen %d\n", btohs(cmd->len));
+	raw_dump(level, frm);
+}
+
+static void info_opt(int level, int type, void *ptr, int len)
+{
+	uint32_t mask;
+	uint64_t fc_mask;
+	int i;
+
+	p_indent(level, 0);
+
+	switch (type) {
+	case 0x0001:
+		printf("Connectionless MTU %d\n", get_val(ptr, len));
+		break;
+	case 0x0002:
+		mask = get_val(ptr, len);
+		printf("Extended feature mask 0x%4.4x\n", mask);
+		if (parser.flags & DUMP_VERBOSE)
+			for (i=0; l2cap_features[i].name; i++)
+				if (mask & l2cap_features[i].flag) {
+					p_indent(level + 1, 0);
+					printf("%s\n", l2cap_features[i].name);
+				}
+		break;
+	case 0x0003:
+		fc_mask = get_le64(ptr);
+		printf("Fixed channel list 0x%8.8" PRIx64 "\n", fc_mask);
+		if (parser.flags & DUMP_VERBOSE)
+			for (i=0; l2cap_fix_chan[i].name; i++)
+				if (fc_mask & l2cap_fix_chan[i].flag) {
+					p_indent(level + 1, 0);
+					printf("%s\n", l2cap_fix_chan[i].name);
+				}
+		break;
+	default:
+		printf("Unknown (len %d)\n", len);
+		break;
+	}
+}
+
+static inline void info_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_info_req *h = frm->ptr;
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Info req: type %d\n", btohs(h->type));
+}
+
+static inline void info_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_info_rsp *h = frm->ptr;
+	uint16_t type = btohs(h->type);
+	uint16_t result = btohs(h->result);
+	int ilen = btohs(cmd->len) - L2CAP_INFO_RSP_SIZE;
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Info rsp: type %d result %d\n", type, result);
+
+	if (ilen > 0) {
+		info_opt(level + 1, type, h->data, ilen);
+	} else {
+		p_indent(level + 1, frm);
+		printf("%s\n", inforesult2str(result));
+	}
+}
+
+static void l2cap_ctrl_ext_parse(int level, struct frame *frm, uint32_t ctrl)
+{
+	p_indent(level, frm);
+
+	printf("%s:", ctrl & L2CAP_EXT_CTRL_FRAME_TYPE ? "S-frame" : "I-frame");
+
+	if (ctrl & L2CAP_EXT_CTRL_FRAME_TYPE) {
+		printf(" %s", supervisory2str((ctrl & L2CAP_EXT_CTRL_SUPERVISE_MASK) >>
+					L2CAP_EXT_CTRL_SUPER_SHIFT));
+
+		if (ctrl & L2CAP_EXT_CTRL_POLL)
+			printf(" P-bit");
+	} else {
+		uint8_t sar = (ctrl & L2CAP_EXT_CTRL_SAR_MASK) >>
+			L2CAP_EXT_CTRL_SAR_SHIFT;
+		printf(" %s", sar2str(sar));
+		if (sar == L2CAP_SAR_START) {
+			uint16_t len;
+			len = get_le16(frm->ptr);
+			frm->ptr += L2CAP_SDULEN_SIZE;
+			frm->len -= L2CAP_SDULEN_SIZE;
+			printf(" (len %d)", len);
+		}
+		printf(" TxSeq %d", (ctrl & L2CAP_EXT_CTRL_TXSEQ_MASK) >>
+				L2CAP_EXT_CTRL_TXSEQ_SHIFT);
+	}
+
+	printf(" ReqSeq %d", (ctrl & L2CAP_EXT_CTRL_REQSEQ_MASK) >>
+			L2CAP_EXT_CTRL_REQSEQ_SHIFT);
+
+	if (ctrl & L2CAP_EXT_CTRL_FINAL)
+		printf(" F-bit");
+}
+
+static void l2cap_ctrl_parse(int level, struct frame *frm, uint32_t ctrl)
+{
+	p_indent(level, frm);
+
+	printf("%s:", ctrl & L2CAP_CTRL_FRAME_TYPE ? "S-frame" : "I-frame");
+
+	if (ctrl & 0x01) {
+		printf(" %s", supervisory2str((ctrl & L2CAP_CTRL_SUPERVISE_MASK) >>
+					L2CAP_CTRL_SUPER_SHIFT));
+
+		if (ctrl & L2CAP_CTRL_POLL)
+			printf(" P-bit");
+	} else {
+		uint8_t sar = (ctrl & L2CAP_CTRL_SAR_MASK) >> L2CAP_CTRL_SAR_SHIFT;
+		printf(" %s", sar2str(sar));
+		if (sar == L2CAP_SAR_START) {
+			uint16_t len;
+			len = get_le16(frm->ptr);
+			frm->ptr += L2CAP_SDULEN_SIZE;
+			frm->len -= L2CAP_SDULEN_SIZE;
+			printf(" (len %d)", len);
+		}
+		printf(" TxSeq %d", (ctrl & L2CAP_CTRL_TXSEQ_MASK) >> L2CAP_CTRL_TXSEQ_SHIFT);
+	}
+
+	printf(" ReqSeq %d", (ctrl & L2CAP_CTRL_REQSEQ_MASK) >> L2CAP_CTRL_REQSEQ_SHIFT);
+
+	if (ctrl & L2CAP_CTRL_FINAL)
+		printf(" F-bit");
+}
+
+static inline void create_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_create_req *h = frm->ptr;
+	uint16_t psm = btohs(h->psm);
+	uint16_t scid = btohs(h->scid);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Create chan req: psm 0x%4.4x scid 0x%4.4x ctrl id %d\n",
+							psm, scid, h->id);
+}
+
+static inline void create_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_create_rsp *h = frm->ptr;
+	uint16_t scid = btohs(h->scid);
+	uint16_t dcid = btohs(h->dcid);
+	uint16_t result = btohs(h->result);
+	uint16_t status = btohs(h->status);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Create chan rsp: dcid 0x%4.4x scid 0x%4.4x result %d status %d\n",
+						dcid, scid, result, status);
+}
+
+static inline void move_req(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_move_req *h = frm->ptr;
+	uint16_t icid = btohs(h->icid);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Move chan req: icid 0x%4.4x ctrl id %d\n", icid, h->id);
+}
+
+static inline void move_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_move_rsp *h = frm->ptr;
+	uint16_t icid = btohs(h->icid);
+	uint16_t result = btohs(h->result);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Move chan rsp: icid 0x%4.4x result %d\n", icid, result);
+}
+
+static inline void move_cfm(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_move_cfm *h = frm->ptr;
+	uint16_t icid = btohs(h->icid);
+	uint16_t result = btohs(h->result);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Move chan cfm: icid 0x%4.4x result %d\n", icid, result);
+}
+
+static inline void move_cfm_rsp(int level, l2cap_cmd_hdr *cmd, struct frame *frm)
+{
+	l2cap_move_cfm_rsp *h = frm->ptr;
+	uint16_t icid = btohs(h->icid);
+
+	if (p_filter(FILT_L2CAP))
+		return;
+
+	printf("Move chan cfm rsp: icid 0x%4.4x\n", icid);
+}
+
+static inline void a2mp_command_rej(int level, struct frame *frm)
+{
+	struct a2mp_command_rej *h = frm->ptr;
+	uint16_t reason = btohs(h->reason);
+
+	printf("Command Reject: reason %d\n", reason);
+	p_indent(level + 1, 0);
+	printf("%s\n", a2mpreason2str(reason));
+}
+
+static inline void a2mp_discover_req(int level, struct frame *frm, uint16_t len)
+{
+	struct a2mp_discover_req *h = frm->ptr;
+	uint16_t mtu = btohs(h->mtu);
+	uint8_t	 *octet = (uint8_t *)&(h->mask);
+	uint16_t mask;
+	uint8_t  extension;
+
+	printf("Discover req: mtu/mps %d ", mtu);
+	len -= 2;
+
+	printf("mask:");
+
+	do {
+		len -= 2;
+		mask = get_le16(octet);
+		printf(" 0x%4.4x", mask);
+
+		extension = octet[1] & 0x80;
+		octet += 2;
+	} while ((extension != 0) && (len >= 2));
+
+	printf("\n");
+}
+
+static inline void a2mp_ctrl_list_dump(int level, struct a2mp_ctrl *list, uint16_t len)
+{
+	p_indent(level, 0);
+	printf("Controller list:\n");
+
+	while (len >= 3) {
+		p_indent(level + 1, 0);
+		printf("id %d type %d (%s) status 0x%2.2x (%s)\n",
+			   list->id, list->type, ampctrltype2str(list->type), list->status, ampctrlstatus2str(list->status));
+		list++;
+		len -= 3;
+	}
+
+}
+
+static inline void a2mp_discover_rsp(int level, struct frame *frm, uint16_t len)
+{
+	struct a2mp_discover_rsp *h = frm->ptr;
+	uint16_t mtu = btohs(h->mtu);
+	uint8_t	 *octet = (uint8_t *)&(h->mask);
+	uint16_t mask;
+	uint8_t  extension;
+
+	printf("Discover rsp: mtu/mps %d ", mtu);
+	len -= 2;
+
+	printf("mask:");
+
+	do {
+		len -= 2;
+		mask = get_le16(octet);
+		printf(" 0x%4.4x", mask);
+
+		extension = octet[1] & 0x80;
+		octet += 2;
+	} while ((extension != 0) && (len >= 2));
+
+	printf("\n");
+
+	if (len >= 3) {
+		a2mp_ctrl_list_dump(level + 1, (struct a2mp_ctrl *) octet, len);
+	}
+}
+
+static inline void a2mp_change_notify(int level, struct frame *frm, uint16_t len)
+{
+	struct a2mp_ctrl *list = frm->ptr;
+
+	printf("Change Notify\n");
+
+	if (len >= 3) {
+		a2mp_ctrl_list_dump(level + 1, list, len);
+	}
+}
+
+static inline void a2mp_change_rsp(int level, struct frame *frm)
+{
+	printf("Change Response\n");
+}
+
+static inline void a2mp_info_req(int level, struct frame *frm)
+{
+	struct a2mp_info_req *h = frm->ptr;
+
+	printf("Get Info req: id %d\n", h->id);
+}
+
+static inline void a2mp_info_rsp(int level, struct frame *frm)
+{
+	struct a2mp_info_rsp *h = frm->ptr;
+
+	printf("Get Info rsp: id %d status %d (%s)\n",
+		   h->id, h->status, a2mpstatus2str(h->status));
+
+	p_indent(level + 1, 0);
+	printf("Total bandwidth %d\n", btohl(h->total_bw));
+	p_indent(level + 1, 0);
+	printf("Max guaranteed bandwidth %d\n", btohl(h->max_bw));
+	p_indent(level + 1, 0);
+	printf("Min latency %d\n", btohl(h->min_latency));
+	p_indent(level + 1, 0);
+	printf("Pal capabilities 0x%4.4x\n", btohs(h->pal_caps));
+	p_indent(level + 1, 0);
+	printf("Assoc size %d\n", btohs(h->assoc_size));
+}
+
+static inline void a2mp_assoc_req(int level, struct frame *frm)
+{
+	struct a2mp_assoc_req *h = frm->ptr;
+
+	printf("Get AMP Assoc req: id %d\n", h->id);
+}
+
+static inline void a2mp_assoc_rsp(int level, struct frame *frm, uint16_t len)
+{
+	struct a2mp_assoc_rsp *h = frm->ptr;
+
+	printf("Get AMP Assoc rsp: id %d status (%d) %s\n",
+			h->id, h->status, a2mpstatus2str(h->status));
+	amp_assoc_dump(level + 1, h->assoc_data, len - sizeof(*h));
+}
+
+static inline void a2mp_create_req(int level, struct frame *frm, uint16_t len)
+{
+	struct a2mp_create_req *h = frm->ptr;
+
+	printf("Create Physical Link req: local id %d remote id %d\n",
+		   h->local_id, h->remote_id);
+	amp_assoc_dump(level + 1, h->assoc_data, len - sizeof(*h));
+}
+
+static inline void a2mp_create_rsp(int level, struct frame *frm)
+{
+	struct a2mp_create_rsp *h = frm->ptr;
+
+	printf("Create Physical Link rsp: local id %d remote id %d status %d\n",
+		   h->local_id, h->remote_id, h->status);
+	p_indent(level+1, 0);
+	printf("%s\n", a2mpcplstatus2str(h->status));
+}
+
+static inline void a2mp_disconn_req(int level, struct frame *frm)
+{
+	struct a2mp_disconn_req *h = frm->ptr;
+
+	printf("Disconnect Physical Link req: local id %d remote id %d\n",
+		   h->local_id, h->remote_id);
+}
+
+static inline void a2mp_disconn_rsp(int level, struct frame *frm)
+{
+	struct a2mp_disconn_rsp *h = frm->ptr;
+
+	printf("Disconnect Physical Link rsp: local id %d remote id %d status %d\n",
+		   h->local_id, h->remote_id, h->status);
+	p_indent(level+1, 0);
+	printf("%s\n", a2mpdplstatus2str(h->status));
+}
+
+static void l2cap_parse(int level, struct frame *frm)
+{
+	l2cap_hdr *hdr = (void *)frm->ptr;
+	uint16_t dlen = btohs(hdr->len);
+	uint16_t cid  = btohs(hdr->cid);
+	uint16_t psm;
+
+	frm->ptr += L2CAP_HDR_SIZE;
+	frm->len -= L2CAP_HDR_SIZE;
+
+	if (cid == 0x1) {
+		/* Signaling channel */
+
+		while (frm->len >= L2CAP_CMD_HDR_SIZE) {
+			l2cap_cmd_hdr *hdr = frm->ptr;
+
+			frm->ptr += L2CAP_CMD_HDR_SIZE;
+			frm->len -= L2CAP_CMD_HDR_SIZE;
+
+			if (!p_filter(FILT_L2CAP)) {
+				p_indent(level, frm);
+				printf("L2CAP(s): ");
+			}
+
+			switch (hdr->code) {
+			case L2CAP_COMMAND_REJ:
+				command_rej(level, frm);
+				break;
+
+			case L2CAP_CONN_REQ:
+				conn_req(level, frm);
+				break;
+
+			case L2CAP_CONN_RSP:
+				conn_rsp(level, frm);
+				break;
+
+			case L2CAP_CONF_REQ:
+				conf_req(level, hdr, frm);
+				break;
+
+			case L2CAP_CONF_RSP:
+				conf_rsp(level, hdr, frm);
+				break;
+
+			case L2CAP_DISCONN_REQ:
+				disconn_req(level, frm);
+				break;
+
+			case L2CAP_DISCONN_RSP:
+				disconn_rsp(level, frm);
+				break;
+
+			case L2CAP_ECHO_REQ:
+				echo_req(level, hdr, frm);
+				break;
+
+			case L2CAP_ECHO_RSP:
+				echo_rsp(level, hdr, frm);
+				break;
+
+			case L2CAP_INFO_REQ:
+				info_req(level, hdr, frm);
+				break;
+
+			case L2CAP_INFO_RSP:
+				info_rsp(level, hdr, frm);
+				break;
+
+			case L2CAP_CREATE_REQ:
+				create_req(level, hdr, frm);
+				break;
+
+			case L2CAP_CREATE_RSP:
+				create_rsp(level, hdr, frm);
+				break;
+
+			case L2CAP_MOVE_REQ:
+				move_req(level, hdr, frm);
+				break;
+
+			case L2CAP_MOVE_RSP:
+				move_rsp(level, hdr, frm);
+				break;
+
+			case L2CAP_MOVE_CFM:
+				move_cfm(level, hdr, frm);
+				break;
+
+			case L2CAP_MOVE_CFM_RSP:
+				move_cfm_rsp(level, hdr, frm);
+				break;
+
+			default:
+				if (p_filter(FILT_L2CAP))
+					break;
+				printf("code 0x%2.2x ident %d len %d\n", 
+					hdr->code, hdr->ident, btohs(hdr->len));
+				raw_dump(level, frm);
+			}
+
+			if (frm->len > btohs(hdr->len)) {
+				frm->len -= btohs(hdr->len);
+				frm->ptr += btohs(hdr->len);
+			} else
+				frm->len = 0;
+		}
+	} else if (cid == 0x2) {
+		/* Connectionless channel */
+
+		if (p_filter(FILT_L2CAP))
+			return;
+
+		psm = get_le16(frm->ptr);
+		frm->ptr += 2;
+		frm->len -= 2;
+
+		p_indent(level, frm);
+		printf("L2CAP(c): len %d psm %d\n", dlen, psm);
+		raw_dump(level, frm);
+	} else if (cid == 0x3) {
+		/* AMP Manager channel */
+
+		if (p_filter(FILT_A2MP))
+			return;
+
+		/* Adjust for ERTM control bytes */
+		frm->ptr += 2;
+		frm->len -= 2;
+
+		while (frm->len >= A2MP_HDR_SIZE) {
+			struct a2mp_hdr *hdr = frm->ptr;
+
+			frm->ptr += A2MP_HDR_SIZE;
+			frm->len -= A2MP_HDR_SIZE;
+
+			p_indent(level, frm);
+			printf("A2MP: ");
+
+			switch (hdr->code) {
+			case A2MP_COMMAND_REJ:
+				a2mp_command_rej(level, frm);
+				break;
+			case A2MP_DISCOVER_REQ:
+				a2mp_discover_req(level, frm, btohs(hdr->len));
+				break;
+			case A2MP_DISCOVER_RSP:
+				a2mp_discover_rsp(level, frm, btohs(hdr->len));
+				break;
+			case A2MP_CHANGE_NOTIFY:
+				a2mp_change_notify(level, frm, btohs(hdr->len));
+				break;
+			case A2MP_CHANGE_RSP:
+				a2mp_change_rsp(level, frm);
+				break;
+			case A2MP_INFO_REQ:
+				a2mp_info_req(level, frm);
+				break;
+			case A2MP_INFO_RSP:
+				a2mp_info_rsp(level, frm);
+				break;
+			case A2MP_ASSOC_REQ:
+				a2mp_assoc_req(level, frm);
+				break;
+			case A2MP_ASSOC_RSP:
+				a2mp_assoc_rsp(level, frm, btohs(hdr->len));
+				break;
+			case A2MP_CREATE_REQ:
+				a2mp_create_req(level, frm, btohs(hdr->len));
+				break;
+			case A2MP_CREATE_RSP:
+				a2mp_create_rsp(level, frm);
+				break;
+			case A2MP_DISCONN_REQ:
+				a2mp_disconn_req(level, frm);
+				break;
+			case A2MP_DISCONN_RSP:
+				a2mp_disconn_rsp(level, frm);
+				break;
+			default:
+				printf("code 0x%2.2x ident %d len %d\n",
+					   hdr->code, hdr->ident, btohs(hdr->len));
+				raw_dump(level, frm);
+			}
+			if (frm->len > btohs(hdr->len)) {
+				frm->len -= btohs(hdr->len);
+				frm->ptr += btohs(hdr->len);
+			} else
+				frm->len = 0;
+		}
+	} else if (cid == 0x04) {
+		if (!p_filter(FILT_ATT))
+			att_dump(level, frm);
+		else
+			raw_dump(level + 1, frm);
+	} else if (cid == 0x06) {
+		if (!p_filter(FILT_SMP))
+			smp_dump(level, frm);
+		else
+			raw_dump(level + 1, frm);
+	} else {
+		/* Connection oriented channel */
+
+		uint8_t mode = get_mode(!frm->in, frm->handle, cid);
+		uint8_t ext_ctrl = get_ext_ctrl(!frm->in, frm->handle, cid);
+		uint16_t psm = get_psm(!frm->in, frm->handle, cid);
+		uint16_t fcs = 0;
+		uint32_t proto, ctrl = 0;
+
+		frm->cid = cid;
+		frm->num = get_num(!frm->in, frm->handle, cid);
+
+		if (mode > 0) {
+			if (ext_ctrl) {
+				ctrl = get_val(frm->ptr, 4);
+				frm->ptr += 4;
+				frm->len -= 6;
+			} else {
+				ctrl = get_val(frm->ptr, 2);
+				frm->ptr += 2;
+				frm->len -= 4;
+			}
+			fcs = get_le16(frm->ptr + frm->len);
+		}
+
+		if (!p_filter(FILT_L2CAP)) {
+			p_indent(level, frm);
+			printf("L2CAP(d): cid 0x%4.4x len %d", cid, dlen);
+			if (mode > 0) {
+				if (ext_ctrl)
+					printf(" ext_ctrl 0x%8.8x fcs 0x%4.4x", ctrl, fcs);
+				else
+					printf(" ctrl 0x%4.4x fcs 0x%4.4x", ctrl, fcs);
+			}
+
+			printf(" [psm %d]\n", psm);
+			level++;
+			if (mode > 0) {
+				if (ext_ctrl)
+					l2cap_ctrl_ext_parse(level, frm, ctrl);
+				else
+					l2cap_ctrl_parse(level, frm, ctrl);
+
+				printf("\n");
+			}
+		}
+
+		switch (psm) {
+		case 0x01:
+			if (!p_filter(FILT_SDP))
+				sdp_dump(level + 1, frm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		case 0x03:
+			if (!p_filter(FILT_RFCOMM))
+				rfcomm_dump(level, frm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		case 0x0f:
+			if (!p_filter(FILT_BNEP))
+				bnep_dump(level, frm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		case 0x11:
+		case 0x13:
+			if (!p_filter(FILT_HIDP))
+				hidp_dump(level, frm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		case 0x17:
+		case 0x1B:
+			if (!p_filter(FILT_AVCTP))
+				avctp_dump(level, frm, psm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		case 0x19:
+			if (!p_filter(FILT_AVDTP))
+				avdtp_dump(level, frm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		case 0x1f:
+			if (!p_filter(FILT_ATT))
+				att_dump(level, frm);
+			else
+				raw_dump(level + 1, frm);
+			break;
+
+		default:
+			proto = get_proto(frm->handle, psm, 0);
+
+			switch (proto) {
+			case SDP_UUID_CMTP:
+				if (!p_filter(FILT_CMTP))
+					cmtp_dump(level, frm);
+				else
+					raw_dump(level + 1, frm);
+				break;
+
+			case SDP_UUID_HARDCOPY_CONTROL_CHANNEL:
+				if (!p_filter(FILT_HCRP))
+					hcrp_dump(level, frm);
+				else
+					raw_dump(level + 1, frm);
+				break;
+
+			case SDP_UUID_OBEX:
+				if (!p_filter(FILT_OBEX))
+					obex_dump(level, frm);
+				else
+					raw_dump(level + 1, frm);
+				break;
+
+			default:
+				if (p_filter(FILT_L2CAP))
+					break;
+
+				raw_dump(level, frm);
+				break;
+			}
+			break;
+		}
+	}
+}
+
+void l2cap_dump(int level, struct frame *frm)
+{
+	struct frame *fr;
+	l2cap_hdr *hdr;
+	uint16_t dlen;
+
+	if ((frm->flags & ACL_START) || frm->flags == ACL_START_NO_FLUSH) {
+		hdr  = frm->ptr;
+		dlen = btohs(hdr->len);
+
+		if (dlen + L2CAP_HDR_SIZE < (int) frm->len) {
+			/* invalid frame */
+			raw_dump(level,frm);
+			return;
+		}
+
+		if ((int) frm->len == (dlen + L2CAP_HDR_SIZE)) {
+			/* Complete frame */
+			l2cap_parse(level, frm);
+			return;
+		}
+
+		if (!(fr = get_frame(frm->handle))) {
+			fprintf(stderr, "Not enough connection handles\n");
+			raw_dump(level, frm);
+			return;
+		}
+
+		if (fr->data)
+			free(fr->data);
+
+		if (!(fr->data = malloc(dlen + L2CAP_HDR_SIZE))) {
+			perror("Can't allocate L2CAP reassembly buffer");
+			return;
+		}
+		memcpy(fr->data, frm->ptr, frm->len);
+		fr->data_len   = dlen + L2CAP_HDR_SIZE;
+		fr->len        = frm->len;
+		fr->ptr        = fr->data;
+		fr->dev_id     = frm->dev_id;
+		fr->in         = frm->in;
+		fr->ts         = frm->ts;
+		fr->handle     = frm->handle;
+		fr->cid        = frm->cid;
+		fr->num        = frm->num;
+		fr->dlci       = frm->dlci;
+		fr->channel    = frm->channel;
+		fr->pppdump_fd = frm->pppdump_fd;
+		fr->audio_fd   = frm->audio_fd;
+	} else {
+		if (!(fr = get_frame(frm->handle))) {
+			fprintf(stderr, "Not enough connection handles\n");
+			raw_dump(level, frm);
+			return;
+		}
+
+		if (!fr->data) {
+			/* Unexpected fragment */
+			raw_dump(level, frm);
+			return;
+		}
+
+		if (frm->len > (fr->data_len - fr->len)) {
+			/* Bad fragment */
+			raw_dump(level, frm);
+			free(fr->data); fr->data = NULL;
+			return;
+		}
+
+		memcpy(fr->data + fr->len, frm->ptr, frm->len);
+		fr->len += frm->len;
+
+		if (fr->len == fr->data_len) {
+			/* Complete frame */
+			l2cap_parse(level, fr);
+
+			free(fr->data); fr->data = NULL;
+			return;
+		}
+	}
+}
+
+void l2cap_clear(uint16_t handle)
+{
+	del_handle(handle);
+}
diff --git a/bluez/repo/tools/parser/l2cap.h b/bluez/repo/tools/parser/l2cap.h
new file mode 100644
index 0000000..788aef0
--- /dev/null
+++ b/bluez/repo/tools/parser/l2cap.h
@@ -0,0 +1,272 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __L2CAP_H
+#define __L2CAP_H
+
+/* L2CAP command codes */
+#define L2CAP_COMMAND_REJ	0x01
+#define L2CAP_CONN_REQ		0x02
+#define L2CAP_CONN_RSP		0x03
+#define L2CAP_CONF_REQ		0x04
+#define L2CAP_CONF_RSP		0x05
+#define L2CAP_DISCONN_REQ	0x06
+#define L2CAP_DISCONN_RSP	0x07
+#define L2CAP_ECHO_REQ		0x08
+#define L2CAP_ECHO_RSP		0x09
+#define L2CAP_INFO_REQ		0x0a
+#define L2CAP_INFO_RSP		0x0b
+#define L2CAP_CREATE_REQ	0x0c
+#define L2CAP_CREATE_RSP	0x0d
+#define L2CAP_MOVE_REQ		0x0e
+#define L2CAP_MOVE_RSP		0x0f
+#define L2CAP_MOVE_CFM		0x10
+#define L2CAP_MOVE_CFM_RSP	0x11
+
+/* L2CAP extended feature mask */
+#define L2CAP_FEAT_FLOWCTL	0x00000001
+#define L2CAP_FEAT_RETRANS	0x00000002
+#define L2CAP_FEAT_BIDIR_QOS	0x00000004
+#define L2CAP_FEAT_ERTM		0x00000008
+#define L2CAP_FEAT_STREAMING	0x00000010
+#define L2CAP_FEAT_FCS		0x00000020
+#define L2CAP_FEAT_EXT_FLOW	0x00000040
+#define L2CAP_FEAT_FIXED_CHAN	0x00000080
+#define L2CAP_FEAT_EXT_WINDOW	0x00000100
+#define L2CAP_FEAT_UCD		0x00000200
+
+/* L2CAP Control Field bit masks */
+#define L2CAP_CTRL_SAR_MASK		0xC000
+#define L2CAP_CTRL_REQSEQ_MASK		0x3F00
+#define L2CAP_CTRL_TXSEQ_MASK		0x007E
+#define L2CAP_CTRL_SUPERVISE_MASK	0x000C
+
+#define L2CAP_CTRL_RETRANS		0x0080
+#define L2CAP_CTRL_FINAL		0x0080
+#define L2CAP_CTRL_POLL			0x0010
+#define L2CAP_CTRL_FRAME_TYPE		0x0001 /* I- or S-Frame */
+
+#define L2CAP_CTRL_TXSEQ_SHIFT		1
+#define L2CAP_CTRL_SUPER_SHIFT		2
+#define L2CAP_CTRL_REQSEQ_SHIFT		8
+#define L2CAP_CTRL_SAR_SHIFT		14
+
+#define L2CAP_EXT_CTRL_TXSEQ_MASK	0xFFFC0000
+#define L2CAP_EXT_CTRL_SAR_MASK		0x00030000
+#define L2CAP_EXT_CTRL_SUPERVISE_MASK	0x00030000
+#define L2CAP_EXT_CTRL_REQSEQ_MASK	0x0000FFFC
+
+#define L2CAP_EXT_CTRL_POLL		0x00040000
+#define L2CAP_EXT_CTRL_FINAL		0x00000002
+#define L2CAP_EXT_CTRL_FRAME_TYPE	0x00000001 /* I- or S-Frame */
+
+#define L2CAP_EXT_CTRL_REQSEQ_SHIFT	2
+#define L2CAP_EXT_CTRL_SAR_SHIFT	16
+#define L2CAP_EXT_CTRL_SUPER_SHIFT	16
+#define L2CAP_EXT_CTRL_TXSEQ_SHIFT	18
+
+/* L2CAP Supervisory Function */
+#define L2CAP_SUPER_RR		0x00
+#define L2CAP_SUPER_REJ		0x01
+#define L2CAP_SUPER_RNR		0x02
+#define L2CAP_SUPER_SREJ	0x03
+
+/* L2CAP Segmentation and Reassembly */
+#define L2CAP_SAR_UNSEGMENTED	0x00
+#define L2CAP_SAR_START		0x01
+#define L2CAP_SAR_END		0x02
+#define L2CAP_SAR_CONTINUE	0x03
+
+#define L2CAP_SDULEN_SIZE	2
+
+/* L2CAP fixed channels */
+#define L2CAP_FC_L2CAP		0x02
+#define L2CAP_FC_CONNLESS	0x04
+#define L2CAP_FC_A2MP		0x08
+
+/* L2CAP structures */
+typedef struct {
+	uint16_t	len;
+	uint16_t	cid;
+} __attribute__ ((packed)) l2cap_hdr;
+#define L2CAP_HDR_SIZE 4
+
+typedef struct {
+	uint8_t		code;
+	uint8_t		ident;
+	uint16_t	len;
+} __attribute__ ((packed)) l2cap_cmd_hdr;
+#define L2CAP_CMD_HDR_SIZE 4
+
+typedef struct {
+	uint16_t	reason;
+} __attribute__ ((packed)) l2cap_cmd_rej;
+#define L2CAP_CMD_REJ_SIZE 2
+
+typedef struct {
+	uint16_t	psm;
+	uint16_t	scid;
+} __attribute__ ((packed)) l2cap_conn_req;
+#define L2CAP_CONN_REQ_SIZE 4
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+	uint16_t	result;
+	uint16_t	status;
+} __attribute__ ((packed)) l2cap_conn_rsp;
+#define L2CAP_CONN_RSP_SIZE 8
+
+/* connect result */
+#define L2CAP_CR_SUCCESS	0x0000
+#define L2CAP_CR_PEND		0x0001
+#define L2CAP_CR_BAD_PSM	0x0002
+#define L2CAP_CR_SEC_BLOCK	0x0003
+#define L2CAP_CR_NO_MEM		0x0004
+
+/* connect status */
+#define L2CAP_CS_NO_INFO	0x0000
+#define L2CAP_CS_AUTHEN_PEND	0x0001
+#define L2CAP_CS_AUTHOR_PEND	0x0002
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	flags;
+	uint8_t		data[0];
+} __attribute__ ((packed)) l2cap_conf_req;
+#define L2CAP_CONF_REQ_SIZE 4
+
+typedef struct {
+	uint16_t	scid;
+	uint16_t	flags;
+	uint16_t	result;
+	uint8_t		data[0];
+} __attribute__ ((packed)) l2cap_conf_rsp;
+#define L2CAP_CONF_RSP_SIZE 6
+
+#define L2CAP_CONF_SUCCESS	0x0000
+#define L2CAP_CONF_UNACCEPT	0x0001
+#define L2CAP_CONF_REJECT	0x0002
+#define L2CAP_CONF_UNKNOWN	0x0003
+#define L2CAP_CONF_PENDING	0x0004
+#define L2CAP_CONF_EFS_REJECT	0x0005
+
+typedef struct {
+	uint8_t		type;
+	uint8_t		len;
+	uint8_t		val[0];
+} __attribute__ ((packed)) l2cap_conf_opt;
+#define L2CAP_CONF_OPT_SIZE 2
+
+#define L2CAP_CONF_MTU		0x01
+#define L2CAP_CONF_FLUSH_TO	0x02
+#define L2CAP_CONF_QOS		0x03
+#define L2CAP_CONF_RFC		0x04
+#define L2CAP_CONF_FCS		0x05
+#define L2CAP_CONF_EFS		0x06
+#define L2CAP_CONF_EWS		0x07
+
+#define L2CAP_CONF_MAX_SIZE	22
+
+#define L2CAP_MODE_BASIC	0x00
+#define L2CAP_MODE_RETRANS	0x01
+#define L2CAP_MODE_FLOWCTL	0x02
+#define L2CAP_MODE_ERTM		0x03
+#define L2CAP_MODE_STREAMING	0x04
+
+#define L2CAP_SERVTYPE_NOTRAFFIC	0x00
+#define L2CAP_SERVTYPE_BESTEFFORT	0x01
+#define L2CAP_SERVTYPE_GUARANTEED	0x02
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+} __attribute__ ((packed)) l2cap_disconn_req;
+#define L2CAP_DISCONN_REQ_SIZE 4
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+} __attribute__ ((packed)) l2cap_disconn_rsp;
+#define L2CAP_DISCONN_RSP_SIZE 4
+
+typedef struct {
+	uint16_t	type;
+} __attribute__ ((packed)) l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE 2
+
+typedef struct {
+	uint16_t	type;
+	uint16_t	result;
+	uint8_t		data[0];
+} __attribute__ ((packed)) l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE 4
+
+/* info type */
+#define L2CAP_IT_CL_MTU		0x0001
+#define L2CAP_IT_FEAT_MASK	0x0002
+
+/* info result */
+#define L2CAP_IR_SUCCESS	0x0000
+#define L2CAP_IR_NOTSUPP	0x0001
+
+typedef struct {
+	uint16_t	psm;
+	uint16_t	scid;
+	uint8_t		id;
+} __attribute__ ((packed)) l2cap_create_req;
+#define L2CAP_CREATE_REQ_SIZE 5
+
+typedef struct {
+	uint16_t	dcid;
+	uint16_t	scid;
+	uint16_t	result;
+	uint16_t	status;
+} __attribute__ ((packed)) l2cap_create_rsp;
+#define L2CAP_CREATE_RSP_SIZE 8
+
+typedef struct {
+	uint16_t	icid;
+	uint8_t		id;
+} __attribute__ ((packed)) l2cap_move_req;
+#define L2CAP_MOVE_REQ_SIZE 3
+
+typedef struct {
+	uint16_t	icid;
+	uint16_t	result;
+} __attribute__ ((packed)) l2cap_move_rsp;
+#define L2CAP_MOVE_RSP_SIZE 4
+
+typedef struct {
+	uint16_t	icid;
+	uint16_t	result;
+} __attribute__ ((packed)) l2cap_move_cfm;
+#define L2CAP_MOVE_CFM_SIZE 4
+
+typedef struct {
+	uint16_t	icid;
+} __attribute__ ((packed)) l2cap_move_cfm_rsp;
+#define L2CAP_MOVE_CFM_RSP_SIZE 2
+
+#endif /* __L2CAP_H */
diff --git a/bluez/repo/tools/parser/lmp.c b/bluez/repo/tools/parser/lmp.c
new file mode 100644
index 0000000..3d2772c
--- /dev/null
+++ b/bluez/repo/tools/parser/lmp.c
@@ -0,0 +1,1362 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/socket.h>
+
+#include "parser.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#define LMP_U8(frm)  (p_get_u8(frm))
+#define LMP_U16(frm) (btohs(htons(p_get_u16(frm))))
+#define LMP_U32(frm) (btohl(htonl(p_get_u32(frm))))
+
+static enum {
+	IN_RAND,
+	COMB_KEY_M,
+	COMB_KEY_S,
+	AU_RAND_M,
+	AU_RAND_S,
+	SRES_M,
+	SRES_S,
+} pairing_state = IN_RAND;
+
+static struct {
+	uint8_t in_rand[16];
+	uint8_t comb_key_m[16];
+	uint8_t comb_key_s[16];
+	uint8_t au_rand_m[16];
+	uint8_t au_rand_s[16];
+	uint8_t sres_m[4];
+	uint8_t sres_s[4];
+} pairing_data;
+
+static inline void pairing_data_dump(void)
+{
+	int i;
+
+	p_indent(6, NULL);
+	printf("IN_RAND  ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", pairing_data.in_rand[i]);
+	printf("\n");
+
+	p_indent(6, NULL);
+	printf("COMB_KEY ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", pairing_data.comb_key_m[i]);
+	printf(" (M)\n");
+
+	p_indent(6, NULL);
+	printf("COMB_KEY ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", pairing_data.comb_key_s[i]);
+	printf(" (S)\n");
+
+	p_indent(6, NULL);
+	printf("AU_RAND  ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", pairing_data.au_rand_m[i]);
+	printf(" SRES ");
+	for (i = 0; i < 4; i++)
+		printf("%2.2x", pairing_data.sres_m[i]);
+	printf(" (M)\n");
+
+	p_indent(6, NULL);
+	printf("AU_RAND  ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", pairing_data.au_rand_s[i]);
+	printf(" SRES ");
+	for (i = 0; i < 4; i++)
+		printf("%2.2x", pairing_data.sres_s[i]);
+	printf(" (S)\n");
+}
+
+static inline void in_rand(struct frame *frm)
+{
+	uint8_t *val = frm->ptr;
+
+	memcpy(pairing_data.in_rand, val, 16);
+	pairing_state = COMB_KEY_M;
+}
+
+static inline void comb_key(struct frame *frm)
+{
+	uint8_t *val = frm->ptr;
+
+	switch (pairing_state) {
+	case COMB_KEY_M:
+		memcpy(pairing_data.comb_key_m, val, 16);
+		pairing_state = COMB_KEY_S;
+		break;
+	case COMB_KEY_S:
+		memcpy(pairing_data.comb_key_s, val, 16);
+		pairing_state = AU_RAND_M;
+		break;
+	case IN_RAND:
+	case AU_RAND_M:
+	case AU_RAND_S:
+	case SRES_M:
+	case SRES_S:
+	default:
+		pairing_state = IN_RAND;
+		break;
+	}
+}
+
+static inline void au_rand(struct frame *frm)
+{
+	uint8_t *val = frm->ptr;
+
+	switch (pairing_state) {
+	case AU_RAND_M:
+		memcpy(pairing_data.au_rand_m, val, 16);
+		pairing_state = SRES_M;
+		break;
+	case AU_RAND_S:
+		memcpy(pairing_data.au_rand_s, val, 16);
+		pairing_state = SRES_S;
+		break;
+	case COMB_KEY_M:
+	case COMB_KEY_S:
+	case IN_RAND:
+	case SRES_M:
+	case SRES_S:
+	default:
+		pairing_state = IN_RAND;
+		break;
+	}
+}
+
+static inline void sres(struct frame *frm)
+{
+	uint8_t *val = frm->ptr;
+
+	switch (pairing_state) {
+	case SRES_M:
+		memcpy(pairing_data.sres_m, val, 4);
+		pairing_state = AU_RAND_S;
+		break;
+	case SRES_S:
+		memcpy(pairing_data.sres_s, val, 4);
+		pairing_state = IN_RAND;
+		pairing_data_dump();
+		break;
+	case COMB_KEY_M:
+	case COMB_KEY_S:
+	case IN_RAND:
+	case AU_RAND_M:
+	case AU_RAND_S:
+	default:
+		pairing_state = IN_RAND;
+		break;
+	}
+}
+
+static char *opcode2str(uint16_t opcode)
+{
+	switch (opcode) {
+	case 1:
+		return "name_req";
+	case 2:
+		return "name_res";
+	case 3:
+		return "accepted";
+	case 4:
+		return "not_accepted";
+	case 5:
+		return "clkoffset_req";
+	case 6:
+		return "clkoffset_res";
+	case 7:
+		return "detach";
+	case 8:
+		return "in_rand";
+	case 9:
+		return "comb_key";
+	case 10:
+		return "unit_key";
+	case 11:
+		return "au_rand";
+	case 12:
+		return "sres";
+	case 13:
+		return "temp_rand";
+	case 14:
+		return "temp_key";
+	case 15:
+		return "encryption_mode_req";
+	case 16:
+		return "encryption_key_size_req";
+	case 17:
+		return "start_encryption_req";
+	case 18:
+		return "stop_encryption_req";
+	case 19:
+		return "switch_req";
+	case 20:
+		return "hold";
+	case 21:
+		return "hold_req";
+	case 22:
+		return "sniff";
+	case 23:
+		return "sniff_req";
+	case 24:
+		return "unsniff_req";
+	case 25:
+		return "park_req";
+	case 26:
+		return "park";
+	case 27:
+		return "set_broadcast_scan_window";
+	case 28:
+		return "modify_beacon";
+	case 29:
+		return "unpark_BD_ADDR_req";
+	case 30:
+		return "unpark_PM_ADDR_req";
+	case 31:
+		return "incr_power_req";
+	case 32:
+		return "decr_power_req";
+	case 33:
+		return "max_power";
+	case 34:
+		return "min_power";
+	case 35:
+		return "auto_rate";
+	case 36:
+		return "preferred_rate";
+	case 37:
+		return "version_req";
+	case 38:
+		return "version_res";
+	case 39:
+		return "feature_req";
+	case 40:
+		return "feature_res";
+	case 41:
+		return "quality_of_service";
+	case 42:
+		return "quality_of_service_req";
+	case 43:
+		return "SCO_link_req";
+	case 44:
+		return "remove_SCO_link_req";
+	case 45:
+		return "max_slot";
+	case 46:
+		return "max_slot_req";
+	case 47:
+		return "timing_accuracy_req";
+	case 48:
+		return "timing_accuracy_res";
+	case 49:
+		return "setup_complete";
+	case 50:
+		return "use_semi_permanent_key";
+	case 51:
+		return "host_connection_req";
+	case 52:
+		return "slot_offset";
+	case 53:
+		return "page_mode_req";
+	case 54:
+		return "page_scan_mode_req";
+	case 55:
+		return "supervision_timeout";
+	case 56:
+		return "test_activate";
+	case 57:
+		return "test_control";
+	case 58:
+		return "encryption_key_size_mask_req";
+	case 59:
+		return "encryption_key_size_mask_res";
+	case 60:
+		return "set_AFH";
+	case 61:
+		return "encapsulated_header";
+	case 62:
+		return "encapsulated_payload";
+	case 63:
+		return "simple_pairing_confirm";
+	case 64:
+		return "simple_pairing_number";
+	case 65:
+		return "DHkey_check";
+	case 127 + (1 << 7):
+		return "accepted_ext";
+	case 127 + (2 << 7):
+		return "not_accepted_ext";
+	case 127 + (3 << 7):
+		return "features_req_ext";
+	case 127 + (4 << 7):
+		return "features_res_ext";
+	case 127 + (11 << 7):
+		return "packet_type_table_req";
+	case 127 + (12 << 7):
+		return "eSCO_link_req";
+	case 127 + (13 << 7):
+		return "remove_eSCO_link_req";
+	case 127 + (16 << 7):
+		return "channel_classification_req";
+	case 127 + (17 << 7):
+		return "channel_classification";
+	case 127 + (21 << 7):
+		return "sniff_subrating_req";
+	case 127 + (22 << 7):
+		return "sniff_subrating_res";
+	case 127 + (23 << 7):
+		return "pause_encryption_req";
+	case 127 + (24 << 7):
+		return "resume_encryption_req";
+	case 127 + (25 << 7):
+		return "IO_capability_req";
+	case 127 + (26 << 7):
+		return "IO_capability_res";
+	case 127 + (27 << 7):
+		return "numeric_comparison_failed";
+	case 127 + (28 << 7):
+		return "passkey_failed";
+	case 127 + (29 << 7):
+		return "oob_failed";
+	case 127 + (30 << 7):
+		return "keypress_notification";
+	default:
+		return "unknown";
+	}
+}
+
+static inline void name_req_dump(int level, struct frame *frm)
+{
+	uint8_t offset = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("name offset %d\n", offset);
+}
+
+static inline void name_res_dump(int level, struct frame *frm)
+{
+	uint8_t offset = LMP_U8(frm);
+	uint8_t length = LMP_U8(frm);
+	uint8_t *name = frm->ptr;
+	int i, size;
+
+	frm->ptr += 14;
+	frm->len -= 14;
+
+	p_indent(level, frm);
+	printf("name offset %d\n", offset);
+
+	p_indent(level, frm);
+	printf("name length %d\n", length);
+
+	size = length - offset;
+	if (size > 14)
+		size = 14;
+
+	p_indent(level, frm);
+	printf("name fragment '");
+	for (i = 0; i < size; i++)
+		if (isprint(name[i]))
+			printf("%c", name[i]);
+		else
+			printf(".");
+	printf("'\n");
+}
+
+static inline void accepted_dump(int level, struct frame *frm)
+{
+	uint8_t opcode = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("op code %d (%s)\n", opcode, opcode2str(opcode));
+}
+
+static inline void not_accepted_dump(int level, struct frame *frm)
+{
+	uint8_t opcode = LMP_U8(frm);
+	uint8_t error = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("op code %d (%s)\n", opcode, opcode2str(opcode));
+
+	p_indent(level, frm);
+	printf("error code 0x%2.2x\n", error);
+}
+
+static inline void clkoffset_dump(int level, struct frame *frm)
+{
+	uint16_t clkoffset = LMP_U16(frm);
+
+	p_indent(level, frm);
+	printf("clock offset 0x%4.4x\n", clkoffset);
+}
+
+static inline void detach_dump(int level, struct frame *frm)
+{
+	uint8_t error = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("error code 0x%2.2x\n", error);
+}
+
+static inline void random_number_dump(int level, struct frame *frm)
+{
+	uint8_t *number = frm->ptr;
+	int i;
+
+	frm->ptr += 16;
+	frm->len -= 16;
+
+	p_indent(level, frm);
+	printf("random number ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", number[i]);
+	printf("\n");
+}
+
+static inline void key_dump(int level, struct frame *frm)
+{
+	uint8_t *key = frm->ptr;
+	int i;
+
+	frm->ptr += 16;
+	frm->len -= 16;
+
+	p_indent(level, frm);
+	printf("key ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", key[i]);
+	printf("\n");
+}
+
+static inline void auth_resp_dump(int level, struct frame *frm)
+{
+	uint8_t *resp = frm->ptr;
+	int i;
+
+	frm->ptr += 4;
+	frm->ptr -= 4;
+
+	p_indent(level, frm);
+	printf("authentication response ");
+	for (i = 0; i < 4; i++)
+		printf("%2.2x", resp[i]);
+	printf("\n");
+}
+
+static inline void encryption_mode_req_dump(int level, struct frame *frm)
+{
+	uint8_t mode = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("encryption mode %d\n", mode);
+}
+
+static inline void encryption_key_size_req_dump(int level, struct frame *frm)
+{
+	uint8_t keysize = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("key size %d\n", keysize);
+}
+
+static inline void switch_req_dump(int level, struct frame *frm)
+{
+	uint32_t instant = LMP_U32(frm);
+
+	p_indent(level, frm);
+	printf("switch instant 0x%4.4x\n", instant);
+}
+
+static inline void hold_dump(int level, struct frame *frm)
+{
+	uint16_t time = LMP_U16(frm);
+	uint32_t instant = LMP_U32(frm);
+
+	p_indent(level, frm);
+	printf("hold time 0x%4.4x\n", time);
+
+	p_indent(level, frm);
+	printf("hold instant 0x%4.4x\n", instant);
+}
+
+static inline void sniff_req_dump(int level, struct frame *frm)
+{
+	uint8_t timing = LMP_U8(frm);
+	uint16_t dsniff = LMP_U16(frm);
+	uint16_t tsniff = LMP_U16(frm);
+	uint16_t attempt = LMP_U16(frm);
+	uint16_t timeout = LMP_U16(frm);
+
+	p_indent(level, frm);
+	printf("timing control flags 0x%2.2x\n", timing);
+
+	p_indent(level, frm);
+	printf("D_sniff %d T_sniff %d\n", dsniff, tsniff);
+
+	p_indent(level, frm);
+	printf("sniff attempt %d\n", attempt);
+
+	p_indent(level, frm);
+	printf("sniff timeout %d\n", timeout);
+}
+
+static inline void park_req_dump(int level, struct frame *frm)
+{
+	uint8_t timing = LMP_U8(frm);
+	uint16_t db = LMP_U16(frm);
+	uint16_t tb = LMP_U16(frm);
+	uint8_t nb = LMP_U8(frm);
+	uint8_t xb = LMP_U8(frm);
+	uint8_t pmaddr = LMP_U8(frm);
+	uint8_t araddr = LMP_U8(frm);
+	uint8_t nbsleep = LMP_U8(frm);
+	uint8_t dbsleep = LMP_U8(frm);
+	uint8_t daccess = LMP_U8(frm);
+	uint8_t taccess = LMP_U8(frm);
+	uint8_t nslots = LMP_U8(frm);
+	uint8_t npoll = LMP_U8(frm);
+	uint8_t access = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("timing control flags 0x%2.2x\n", timing);
+
+	p_indent(level, frm);
+	printf("D_B %d T_B %d N_B %d X_B %d\n", db, tb, nb, xb);
+
+	p_indent(level, frm);
+	printf("PM_ADDR %d AR_ADDR %d\n", pmaddr, araddr);
+
+	p_indent(level, frm);
+	printf("N_Bsleep %d D_Bsleep %d\n", nbsleep, dbsleep);
+
+	p_indent(level, frm);
+	printf("D_access %d T_access %d\n", daccess, taccess);
+
+	p_indent(level, frm);
+	printf("N_acc-slots %d N_poll %d\n", nslots, npoll);
+
+	p_indent(level, frm);
+	printf("M_access %d\n", access & 0x0f);
+
+	p_indent(level, frm);
+	printf("access scheme 0x%2.2x\n", access >> 4);
+}
+
+static inline void modify_beacon_dump(int level, struct frame *frm)
+{
+	uint8_t timing = LMP_U8(frm);
+	uint16_t db = LMP_U16(frm);
+	uint16_t tb = LMP_U16(frm);
+	uint8_t nb = LMP_U8(frm);
+	uint8_t xb = LMP_U8(frm);
+	uint8_t daccess = LMP_U8(frm);
+	uint8_t taccess = LMP_U8(frm);
+	uint8_t nslots = LMP_U8(frm);
+	uint8_t npoll = LMP_U8(frm);
+	uint8_t access = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("timing control flags 0x%2.2x\n", timing);
+
+	p_indent(level, frm);
+	printf("D_B %d T_B %d N_B %d X_B %d\n", db, tb, nb, xb);
+
+	p_indent(level, frm);
+	printf("D_access %d T_access %d\n", daccess, taccess);
+
+	p_indent(level, frm);
+	printf("N_acc-slots %d N_poll %d\n", nslots, npoll);
+
+	p_indent(level, frm);
+	printf("M_access %d\n", access & 0x0f);
+
+	p_indent(level, frm);
+	printf("access scheme 0x%2.2x\n", access >> 4);
+}
+
+static inline void power_req_dump(int level, struct frame *frm)
+{
+	uint8_t val = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("future use 0x%2.2x\n", val);
+}
+
+static inline void preferred_rate_dump(int level, struct frame *frm)
+{
+	uint8_t rate = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("data rate 0x%2.2x\n", rate);
+
+	p_indent(level, frm);
+	printf("Basic: ");
+
+	printf("%suse FEC, ", rate & 0x01 ? "do not " : "");
+
+	switch ((rate >> 1) & 0x03) {
+	case 0x00:
+		printf("no packet-size preference\n");
+		break;
+	case 0x01:
+		printf("use 1-slot packets\n");
+		break;
+	case 0x02:
+		printf("use 3-slot packets\n");
+		break;
+	case 0x03:
+		printf("use 5-slot packets\n");
+		break;
+	}
+
+	p_indent(level, frm);
+	printf("EDR: ");
+
+	switch ((rate >> 3) & 0x03) {
+	case 0x00:
+		printf("use DM1 packets, ");
+		break;
+	case 0x01:
+		printf("use 2 Mbps packets, ");
+		break;
+	case 0x02:
+		printf("use 3 Mbps packets, ");
+		break;
+	case 0x03:
+		printf("reserved, \n");
+		break;
+	}
+
+	switch ((rate >> 5) & 0x03) {
+	case 0x00:
+		printf("no packet-size preference\n");
+		break;
+	case 0x01:
+		printf("use 1-slot packets\n");
+		break;
+	case 0x02:
+		printf("use 3-slot packets\n");
+		break;
+	case 0x03:
+		printf("use 5-slot packets\n");
+		break;
+	}
+}
+
+static inline void version_dump(int level, struct frame *frm)
+{
+	uint8_t ver = LMP_U8(frm);
+	uint16_t compid = LMP_U16(frm);
+	uint16_t subver = LMP_U16(frm);
+	char *tmp;
+
+	p_indent(level, frm);
+	tmp = lmp_vertostr(ver);
+	printf("VersNr %d (%s)\n", ver, tmp);
+	bt_free(tmp);
+
+	p_indent(level, frm);
+	printf("CompId %d (%s)\n", compid, bt_compidtostr(compid));
+
+	p_indent(level, frm);
+	printf("SubVersNr %d\n", subver);
+}
+
+static inline void features_dump(int level, struct frame *frm)
+{
+	uint8_t *features = frm->ptr;
+	int i;
+
+	frm->ptr += 8;
+	frm->len -= 8;
+
+	p_indent(level, frm);
+	printf("features");
+	for (i = 0; i < 8; i++)
+		printf(" 0x%2.2x", features[i]);
+	printf("\n");
+}
+
+static inline void set_afh_dump(int level, struct frame *frm)
+{
+	uint32_t instant = LMP_U32(frm);
+	uint8_t mode = LMP_U8(frm);
+	uint8_t *map = frm->ptr;
+	int i;
+
+	frm->ptr += 10;
+	frm->len -= 10;
+
+	p_indent(level, frm);
+	printf("AFH_instant 0x%04x\n", instant);
+
+	p_indent(level, frm);
+	printf("AFH_mode %d\n", mode);
+
+	p_indent(level, frm);
+	printf("AFH_channel_map 0x");
+	for (i = 0; i < 10; i++)
+		printf("%2.2x", map[i]);
+	printf("\n");
+}
+
+static inline void encapsulated_header_dump(int level, struct frame *frm)
+{
+	uint8_t major = LMP_U8(frm);
+	uint8_t minor = LMP_U8(frm);
+	uint8_t length = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("major type %d minor type %d payload length %d\n",
+						major, minor, length);
+
+	if (major == 1 && minor == 1) {
+		p_indent(level, frm);
+		printf("P-192 Public Key\n");
+	}
+}
+
+static inline void encapsulated_payload_dump(int level, struct frame *frm)
+{
+	uint8_t *value = frm->ptr;
+	int i;
+
+	frm->ptr += 16;
+	frm->len -= 16;
+
+	p_indent(level, frm);
+	printf("data ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", value[i]);
+	printf("\n");
+}
+
+static inline void simple_pairing_confirm_dump(int level, struct frame *frm)
+{
+	uint8_t *value = frm->ptr;
+	int i;
+
+	frm->ptr += 16;
+	frm->len -= 16;
+
+	p_indent(level, frm);
+	printf("commitment value ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", value[i]);
+	printf("\n");
+}
+
+static inline void simple_pairing_number_dump(int level, struct frame *frm)
+{
+	uint8_t *value = frm->ptr;
+	int i;
+
+	frm->ptr += 16;
+	frm->len -= 16;
+
+	p_indent(level, frm);
+	printf("nounce value ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", value[i]);
+	printf("\n");
+}
+
+static inline void dhkey_check_dump(int level, struct frame *frm)
+{
+	uint8_t *value = frm->ptr;
+	int i;
+
+	frm->ptr += 16;
+	frm->len -= 16;
+
+	p_indent(level, frm);
+	printf("confirmation value ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", value[i]);
+	printf("\n");
+}
+
+static inline void accepted_ext_dump(int level, struct frame *frm)
+{
+	uint16_t opcode = LMP_U8(frm) + (LMP_U8(frm) << 7);
+
+	p_indent(level, frm);
+	printf("op code %d/%d (%s)\n", opcode & 0x7f, opcode >> 7, opcode2str(opcode));
+}
+
+static inline void not_accepted_ext_dump(int level, struct frame *frm)
+{
+	uint16_t opcode = LMP_U8(frm) + (LMP_U8(frm) << 7);
+	uint8_t error = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("op code %d/%d (%s)\n", opcode & 0x7f, opcode >> 7, opcode2str(opcode));
+
+	p_indent(level, frm);
+	printf("error code 0x%2.2x\n", error);
+}
+
+static inline void features_ext_dump(int level, struct frame *frm)
+{
+	uint8_t page = LMP_U8(frm);
+	uint8_t max = LMP_U8(frm);
+	uint8_t *features = frm->ptr;
+	int i;
+
+	frm->ptr += 8;
+	frm->len -= 8;
+
+	p_indent(level, frm);
+	printf("features page %d\n", page);
+
+	p_indent(level, frm);
+	printf("max supported page %d\n", max);
+
+	p_indent(level, frm);
+	printf("extended features");
+	for (i = 0; i < 8; i++)
+		printf(" 0x%2.2x", features[i]);
+	printf("\n");
+}
+
+static inline void quality_of_service_dump(int level, struct frame *frm)
+{
+	uint16_t interval = LMP_U16(frm);
+	uint8_t nbc = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("poll interval %d\n", interval);
+
+	p_indent(level, frm);
+	printf("N_BC %d\n", nbc);
+}
+
+static inline void sco_link_req_dump(int level, struct frame *frm)
+{
+	uint8_t handle = LMP_U8(frm);
+	uint8_t timing = LMP_U8(frm);
+	uint8_t dsco = LMP_U8(frm);
+	uint8_t tsco = LMP_U8(frm);
+	uint8_t packet = LMP_U8(frm);
+	uint8_t airmode = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("SCO handle %d\n", handle);
+
+	p_indent(level, frm);
+	printf("timing control flags 0x%2.2x\n", timing);
+
+	p_indent(level, frm);
+	printf("D_SCO %d T_SCO %d\n", dsco, tsco);
+
+	p_indent(level, frm);
+	printf("SCO packet 0x%2.2x\n", packet);
+
+	p_indent(level, frm);
+	printf("air mode 0x%2.2x\n", airmode);
+}
+
+static inline void remove_sco_link_req_dump(int level, struct frame *frm)
+{
+	uint8_t handle = LMP_U8(frm);
+	uint8_t error = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("SCO handle %d\n", handle);
+
+	p_indent(level, frm);
+	printf("error code 0x%2.2x\n", error);
+}
+
+static inline void max_slots_dump(int level, struct frame *frm)
+{
+	uint8_t slots = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("max slots %d\n", slots);
+}
+
+static inline void timing_accuracy_dump(int level, struct frame *frm)
+{
+	uint8_t drift = LMP_U8(frm);
+	uint8_t jitter = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("drift %d\n", drift);
+
+	p_indent(level, frm);
+	printf("jitter %d\n", jitter);
+}
+
+static inline void slot_offset_dump(int level, struct frame *frm)
+{
+	uint16_t offset = LMP_U16(frm);
+	char addr[18];
+
+	p_ba2str((bdaddr_t *) frm->ptr, addr);
+
+	p_indent(level, frm);
+	printf("slot offset %d\n", offset);
+
+	p_indent(level, frm);
+	printf("BD_ADDR %s\n", addr);
+}
+
+static inline void page_mode_dump(int level, struct frame *frm)
+{
+	uint8_t scheme = LMP_U8(frm);
+	uint8_t settings = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("page scheme %d\n", scheme);
+
+	p_indent(level, frm);
+	printf("page scheme settings %d\n", settings);
+}
+
+static inline void supervision_timeout_dump(int level, struct frame *frm)
+{
+	uint16_t timeout = LMP_U16(frm);
+
+	p_indent(level, frm);
+	printf("supervision timeout %d\n", timeout);
+}
+
+static inline void test_control_dump(int level, struct frame *frm)
+{
+	uint8_t scenario = LMP_U8(frm);
+	uint8_t hopping = LMP_U8(frm);
+	uint8_t txfreq = LMP_U8(frm);
+	uint8_t rxfreq = LMP_U8(frm);
+	uint8_t power = LMP_U8(frm);
+	uint8_t poll = LMP_U8(frm);
+	uint8_t packet = LMP_U8(frm);
+	uint16_t length = LMP_U16(frm);
+
+	p_indent(level, frm);
+	printf("test scenario %d\n", scenario);
+
+	p_indent(level, frm);
+	printf("hopping mode %d\n", hopping);
+
+	p_indent(level, frm);
+	printf("TX frequency %d\n", txfreq);
+
+	p_indent(level, frm);
+	printf("RX frequency %d\n", rxfreq);
+
+	p_indent(level, frm);
+	printf("power control mode %d\n", power);
+
+	p_indent(level, frm);
+	printf("poll period %d\n", poll);
+
+	p_indent(level, frm);
+	printf("poll period %d\n", poll);
+
+	p_indent(level, frm);
+	printf("packet type 0x%2.2x\n", packet);
+
+	p_indent(level, frm);
+	printf("length of test data %d\n", length);
+}
+
+static inline void encryption_key_size_mask_res_dump(int level, struct frame *frm)
+{
+	uint16_t mask = LMP_U16(frm);
+
+	p_indent(level, frm);
+	printf("key size mask 0x%4.4x\n", mask);
+}
+
+static inline void packet_type_table_dump(int level, struct frame *frm)
+{
+	uint8_t type = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("packet type table %d ", type);
+	switch (type) {
+	case 0:
+		printf("(1Mbps only)\n");
+		break;
+	case 1:
+		printf("(2/3Mbps)\n");
+		break;
+	default:
+		printf("(Reserved)\n");
+		break;
+	}
+}
+
+static inline void esco_link_req_dump(int level, struct frame *frm)
+{
+	uint8_t handle = LMP_U8(frm);
+	uint8_t ltaddr = LMP_U8(frm);
+	uint8_t timing = LMP_U8(frm);
+	uint8_t desco = LMP_U8(frm);
+	uint8_t tesco = LMP_U8(frm);
+	uint8_t wesco = LMP_U8(frm);
+	uint8_t mspkt = LMP_U8(frm);
+	uint8_t smpkt = LMP_U8(frm);
+	uint16_t mslen = LMP_U16(frm);
+	uint16_t smlen = LMP_U16(frm);
+	uint8_t airmode = LMP_U8(frm);
+	uint8_t negstate = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("eSCO handle %d\n", handle);
+
+	p_indent(level, frm);
+	printf("eSCO LT_ADDR %d\n", ltaddr);
+
+	p_indent(level, frm);
+	printf("timing control flags 0x%2.2x\n", timing);
+
+	p_indent(level, frm);
+	printf("D_eSCO %d T_eSCO %d W_eSCO %d\n", desco, tesco, wesco);
+
+	p_indent(level, frm);
+	printf("eSCO M->S packet type 0x%2.2x length %d\n", mspkt, mslen);
+
+	p_indent(level, frm);
+	printf("eSCO S->M packet type 0x%2.2x length %d\n", smpkt, smlen);
+
+	p_indent(level, frm);
+	printf("air mode 0x%2.2x\n", airmode);
+
+	p_indent(level, frm);
+	printf("negotiation state 0x%2.2x\n", negstate);
+}
+
+static inline void remove_esco_link_req_dump(int level, struct frame *frm)
+{
+	uint8_t handle = LMP_U8(frm);
+	uint8_t error = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("eSCO handle %d\n", handle);
+
+	p_indent(level, frm);
+	printf("error code 0x%2.2x\n", error);
+}
+
+static inline void channel_classification_req_dump(int level, struct frame *frm)
+{
+	uint8_t mode = LMP_U8(frm);
+	uint16_t min = LMP_U16(frm);
+	uint16_t max = LMP_U16(frm);
+
+	p_indent(level, frm);
+	printf("AFH reporting mode %d\n", mode);
+
+	p_indent(level, frm);
+	printf("AFH min interval 0x%4.4x\n", min);
+
+	p_indent(level, frm);
+	printf("AFH max interval 0x%4.4x\n", max);
+}
+
+static inline void channel_classification_dump(int level, struct frame *frm)
+{
+	uint8_t *map = frm->ptr;
+	int i;
+
+	frm->ptr += 10;
+	frm->len -= 10;
+
+	p_indent(level, frm);
+	printf("AFH channel classification 0x");
+	for (i = 0; i < 10; i++)
+		printf("%2.2x", map[i]);
+	printf("\n");
+}
+
+static inline void sniff_subrating_dump(int level, struct frame *frm)
+{
+	uint8_t subrate = LMP_U8(frm);
+	uint16_t timeout = LMP_U16(frm);
+	uint32_t instant = LMP_U32(frm);
+
+	p_indent(level, frm);
+	printf("max subrate %d\n", subrate);
+
+	p_indent(level, frm);
+	printf("min sniff timeout %d\n", timeout);
+
+	p_indent(level, frm);
+	printf("subrate instant 0x%4.4x\n", instant);
+}
+
+static inline void io_capability_dump(int level, struct frame *frm)
+{
+	uint8_t capability = LMP_U8(frm);
+	uint8_t oob_data = LMP_U8(frm);
+	uint8_t authentication = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("capability 0x%2.2x oob 0x%2.2x auth 0x%2.2x\n",
+				capability, oob_data, authentication);
+}
+
+static inline void keypress_notification_dump(int level, struct frame *frm)
+{
+	uint8_t value = LMP_U8(frm);
+
+	p_indent(level, frm);
+	printf("notification value %d\n", value);
+}
+
+void lmp_dump(int level, struct frame *frm)
+{
+	uint8_t tmp, tid;
+	uint16_t opcode;
+
+	p_indent(level, frm);
+
+	tmp = LMP_U8(frm);
+	tid = tmp & 0x01;
+	opcode = (tmp & 0xfe) >> 1;
+	if (opcode > 123) {
+		tmp = LMP_U8(frm);
+		opcode += tmp << 7;
+	}
+
+	printf("LMP(%c): %s(%c): ", frm->master ? 's' : 'r',
+				opcode2str(opcode), tid ? 's' : 'm');
+
+	if (opcode > 123)
+		printf("op code %d/%d", opcode & 0x7f, opcode >> 7);
+	else
+		printf("op code %d", opcode);
+
+	if (frm->handle > 17)
+		printf(" handle %d\n", frm->handle);
+	else
+		printf("\n");
+
+	if (!(parser.flags & DUMP_VERBOSE)) {
+		raw_dump(level, frm);
+		return;
+	}
+
+	switch (opcode) {
+	case 1:
+		name_req_dump(level + 1, frm);
+		return;
+	case 2:
+		name_res_dump(level + 1, frm);
+		return;
+	case 3:
+		accepted_dump(level + 1, frm);
+		return;
+	case 4:
+		not_accepted_dump(level + 1, frm);
+		return;
+	case 6:
+		clkoffset_dump(level + 1, frm);
+		return;
+	case 7:
+		detach_dump(level + 1, frm);
+		return;
+	case 8:
+		in_rand(frm);
+		random_number_dump(level + 1, frm);
+		return;
+	case 9:
+		comb_key(frm);
+		random_number_dump(level + 1, frm);
+		return;
+	case 11:
+		au_rand(frm);
+		random_number_dump(level + 1, frm);
+		return;
+	case 12:
+		sres(frm);
+		auth_resp_dump(level + 1, frm);
+		return;
+	case 13:
+	case 17:
+		random_number_dump(level + 1, frm);
+		return;
+	case 10:
+	case 14:
+		key_dump(level + 1, frm);
+		return;
+	case 15:
+		encryption_mode_req_dump(level + 1, frm);
+		return;
+	case 16:
+		encryption_key_size_req_dump(level + 1, frm);
+		return;
+	case 19:
+		switch_req_dump(level + 1, frm);
+		return;
+	case 20:
+	case 21:
+		hold_dump(level + 1, frm);
+		return;
+	case 23:
+		sniff_req_dump(level + 1, frm);
+		return;
+	case 25:
+		park_req_dump(level + 1, frm);
+		return;
+	case 28:
+		modify_beacon_dump(level + 1, frm);
+		return;
+	case 31:
+	case 32:
+		power_req_dump(level + 1, frm);
+		return;
+	case 36:
+		preferred_rate_dump(level + 1, frm);
+		return;
+	case 37:
+	case 38:
+		version_dump(level + 1, frm);
+		return;
+	case 39:
+	case 40:
+		features_dump(level + 1, frm);
+		return;
+	case 41:
+	case 42:
+		quality_of_service_dump(level + 1, frm);
+		return;
+	case 43:
+		sco_link_req_dump(level + 1, frm);
+		return;
+	case 44:
+		remove_sco_link_req_dump(level + 1, frm);
+		return;
+	case 45:
+	case 46:
+		max_slots_dump(level + 1, frm);
+		return;
+	case 48:
+		timing_accuracy_dump(level + 1, frm);
+		return;
+	case 52:
+		slot_offset_dump(level + 1, frm);
+		return;
+	case 53:
+	case 54:
+		page_mode_dump(level + 1, frm);
+		return;
+	case 55:
+		supervision_timeout_dump(level + 1, frm);
+		return;
+	case 57:
+		test_control_dump(level + 1, frm);
+		return;
+	case 59:
+		encryption_key_size_mask_res_dump(level + 1, frm);
+		return;
+	case 60:
+		set_afh_dump(level + 1, frm);
+		return;
+	case 61:
+		encapsulated_header_dump(level + 1, frm);
+		return;
+	case 62:
+		encapsulated_payload_dump(level + 1, frm);
+		return;
+	case 63:
+		simple_pairing_confirm_dump(level + 1, frm);
+		return;
+	case 64:
+		simple_pairing_number_dump(level + 1, frm);
+		return;
+	case 65:
+		dhkey_check_dump(level + 1, frm);
+		return;
+	case 5:
+	case 18:
+	case 24:
+	case 33:
+	case 34:
+	case 35:
+	case 47:
+	case 49:
+	case 50:
+	case 51:
+	case 56:
+	case 58:
+	case 127 + (23 << 7):
+	case 127 + (24 << 7):
+	case 127 + (27 << 7):
+	case 127 + (28 << 7):
+	case 127 + (29 << 7):
+		return;
+	case 127 + (1 << 7):
+		accepted_ext_dump(level + 1, frm);
+		return;
+	case 127 + (2 << 7):
+		not_accepted_ext_dump(level + 1, frm);
+		return;
+	case 127 + (3 << 7):
+	case 127 + (4 << 7):
+		features_ext_dump(level + 1, frm);
+		return;
+	case 127 + (11 << 7):
+		packet_type_table_dump(level + 1, frm);
+		return;
+	case 127 + (12 << 7):
+		esco_link_req_dump(level + 1, frm);
+		return;
+	case 127 + (13 << 7):
+		remove_esco_link_req_dump(level + 1, frm);
+		return;
+	case 127 + (16 << 7):
+		channel_classification_req_dump(level + 1, frm);
+		return;
+	case 127 + (17 << 7):
+		channel_classification_dump(level + 1, frm);
+		return;
+	case 127 + (21 << 7):
+	case 127 + (22 << 7):
+		sniff_subrating_dump(level + 1, frm);
+		return;
+	case 127 + (25 << 7):
+	case 127 + (26 << 7):
+		io_capability_dump(level + 1, frm);
+		return;
+	case 127 + (30 << 7):
+		keypress_notification_dump(level + 1, frm);
+		return;
+	}
+
+	raw_dump(level, frm);
+}
diff --git a/bluez/repo/tools/parser/obex.c b/bluez/repo/tools/parser/obex.c
new file mode 100644
index 0000000..0612c30
--- /dev/null
+++ b/bluez/repo/tools/parser/obex.c
@@ -0,0 +1,351 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+static char *opcode2str(uint8_t opcode)
+{
+	switch (opcode & 0x7f) {
+	case 0x00:
+		return "Connect";
+	case 0x01:
+		return "Disconnect";
+	case 0x02:
+		return "Put";
+	case 0x03:
+		return "Get";
+	case 0x04:
+		return "Reserved";
+	case 0x05:
+		return "SetPath";
+	case 0x06:
+		return "Action";
+	case 0x07:
+		return "Session";
+	case 0x7f:
+		return "Abort";
+	case 0x10:
+		return "Continue";
+	case 0x20:
+		return "Success";
+	case 0x21:
+		return "Created";
+	case 0x22:
+		return "Accepted";
+	case 0x23:
+		return "Non-authoritative information";
+	case 0x24:
+		return "No content";
+	case 0x25:
+		return "Reset content";
+	case 0x26:
+		return "Partial content";
+	case 0x30:
+		return "Multiple choices";
+	case 0x31:
+		return "Moved permanently";
+	case 0x32:
+		return "Moved temporarily";
+	case 0x33:
+		return "See other";
+	case 0x34:
+		return "Not modified";
+	case 0x35:
+		return "Use Proxy";
+	case 0x40:
+		return "Bad request";
+	case 0x41:
+		return "Unauthorized";
+	case 0x42:
+		return "Payment required";
+	case 0x43:
+		return "Forbidden";
+	case 0x44:
+		return "Not found";
+	case 0x45:
+		return "Method not allowed";
+	case 0x46:
+		return "Not acceptable";
+	case 0x47:
+		return "Proxy authentication required";
+	case 0x48:
+		return "Request timeout";
+	case 0x49:
+		return "Conflict";
+	case 0x4a:
+		return "Gone";
+	case 0x4b:
+		return "Length required";
+	case 0x4c:
+		return "Precondition failed";
+	case 0x4d:
+		return "Requested entity too large";
+	case 0x4e:
+		return "Requested URL too large";
+	case 0x4f:
+		return "Unsupported media type";
+	case 0x50:
+		return "Internal server error";
+	case 0x51:
+		return "Not implemented";
+	case 0x52:
+		return "Bad gateway";
+	case 0x53:
+		return "Service unavailable";
+	case 0x54:
+		return "Gateway timeout";
+	case 0x55:
+		return "HTTP version not supported";
+	case 0x60:
+		return "Database full";
+	case 0x61:
+		return "Database locked";
+	default:
+		return "Unknown";
+	}
+}
+
+static char *hi2str(uint8_t hi)
+{
+	switch (hi & 0x3f) {
+	case 0x00:
+		return "Count";
+	case 0x01:
+		return "Name";
+	case 0x02:
+		return "Type";
+	case 0x03:
+		return "Length";
+	case 0x04:
+		return "Time";
+	case 0x05:
+		return "Description";
+	case 0x06:
+		return "Target";
+	case 0x07:
+		return "HTTP";
+	case 0x08:
+		return "Body";
+	case 0x09:
+		return "End of Body";
+	case 0x0a:
+		return "Who";
+	case 0x0b:
+		return "Connection ID";
+	case 0x0c:
+		return "App. Parameters";
+	case 0x0d:
+		return "Auth. Challenge";
+	case 0x0e:
+		return "Auth. Response";
+	case 0x0f:
+		return "Creator ID";
+	case 0x10:
+		return "WAN UUID";
+	case 0x11:
+		return "Object Class";
+	case 0x12:
+		return "Session Parameters";
+	case 0x13:
+		return "Session Sequence Number";
+	case 0x14:
+		return "Action ID";
+	case 0x15:
+		return "DestName";
+	case 0x16:
+		return "Permission";
+	case 0x17:
+		return "Single Response Mode";
+	case 0x18:
+		return "Single Response Mode Parameters";
+	default:
+		return "Unknown";
+	}
+}
+
+static void parse_headers(int level, struct frame *frm)
+{
+	uint8_t hi, hv8;
+	uint16_t len;
+	uint32_t hv32;
+
+	while (frm->len > 0) {
+		hi = p_get_u8(frm);
+
+		p_indent(level, frm);
+
+		printf("%s (0x%02x)", hi2str(hi), hi);
+		switch (hi & 0xc0) {
+		case 0x00:	/* Unicode */
+			if (frm->len < 2) {
+				printf("\n");
+				return;
+			}
+
+			len = p_get_u16(frm) - 3;
+			printf(" = Unicode length %d\n", len);
+
+			if (frm->len < len)
+				return;
+
+			raw_ndump(level, frm, len);
+			frm->ptr += len;
+			frm->len -= len;
+			break;
+
+		case 0x40:	/* Byte sequence */
+			if (frm->len < 2) {
+				printf("\n");
+				return;
+			}
+
+			len = p_get_u16(frm) - 3;
+			printf(" = Sequence length %d\n", len);
+
+			if (frm->len < len)
+				return;
+
+			raw_ndump(level, frm, len);
+			frm->ptr += len;
+			frm->len -= len;
+			break;
+
+		case 0x80:	/* One byte */
+			if (frm->len < 1) {
+				printf("\n");
+				return;
+			}
+
+			hv8 = p_get_u8(frm);
+			printf(" = %d\n", hv8);
+			break;
+
+		case 0xc0:	/* Four bytes */
+			if (frm->len < 4) {
+				printf("\n");
+				return;
+			}
+
+			hv32 = p_get_u32(frm);
+			printf(" = %u\n", hv32);
+			break;
+		}
+	}
+}
+
+void obex_dump(int level, struct frame *frm)
+{
+	uint8_t last_opcode, opcode, status;
+	uint8_t version, flags, constants;
+	uint16_t length, pktlen;
+
+	frm = add_frame(frm);
+
+	while (frm->len > 2) {
+		opcode = p_get_u8(frm);
+		length = p_get_u16(frm);
+		status = opcode & 0x7f;
+
+		if ((int) frm->len < length - 3) {
+			frm->ptr -= 3;
+			frm->len += 3;
+			return;
+		}
+
+		p_indent(level, frm);
+
+		last_opcode = get_opcode(frm->handle, frm->dlci);
+
+		if (!(opcode & 0x70)) {
+			printf("OBEX: %s cmd(%c): len %d",
+					opcode2str(opcode),
+					opcode & 0x80 ? 'f' : 'c', length);
+			set_opcode(frm->handle, frm->dlci, opcode);
+		} else {
+			printf("OBEX: %s rsp(%c): status %x%02d len %d",
+					opcode2str(last_opcode),
+					opcode & 0x80 ? 'f' : 'c',
+					status >> 4, status & 0xf, length);
+			opcode = last_opcode;
+		}
+
+		if (get_status(frm->handle, frm->dlci) == 0x10)
+			printf(" (continue)");
+
+		set_status(frm->handle, frm->dlci, status);
+
+		if (frm->len == 0) {
+			printf("\n");
+			break;
+		}
+
+		switch (opcode & 0x7f) {
+		case 0x00:	/* Connect */
+			if (frm->len < 4) {
+				printf("\n");
+				return;
+			}
+
+			version = p_get_u8(frm);
+			flags   = p_get_u8(frm);
+			pktlen  = p_get_u16(frm);
+			printf(" version %d.%d flags %d mtu %d\n",
+				version >> 4, version & 0xf, flags, pktlen);
+			break;
+
+		case 0x05:	/* SetPath */
+			if (frm->len < 2) {
+				printf("\n");
+				return;
+			}
+
+			flags     = p_get_u8(frm);
+			constants = p_get_u8(frm);
+			printf(" flags %d constants %d\n", flags, constants);
+			break;
+
+		default:
+			printf("\n");
+			break;
+		}
+
+		if ((status & 0x70) && (parser.flags & DUMP_VERBOSE)) {
+			p_indent(level, frm);
+			printf("Status %x%02d = %s\n",
+					status >> 4, status & 0xf,
+							opcode2str(status));
+		}
+
+		parse_headers(level, frm);
+	}
+}
diff --git a/bluez/repo/tools/parser/parser.c b/bluez/repo/tools/parser/parser.c
new file mode 100644
index 0000000..de8dbe8
--- /dev/null
+++ b/bluez/repo/tools/parser/parser.c
@@ -0,0 +1,343 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+#include "rfcomm.h"
+
+struct parser_t parser;
+
+void init_parser(unsigned long flags, unsigned long filter,
+		unsigned short defpsm, unsigned short defcompid,
+		int pppdump_fd, int audio_fd)
+{
+	if ((flags & DUMP_RAW) && !(flags & DUMP_TYPE_MASK))
+		flags |= DUMP_HEX;
+
+	parser.flags      = flags;
+	parser.filter     = filter;
+	parser.defpsm     = defpsm;
+	parser.defcompid  = defcompid;
+	parser.state      = 0;
+	parser.pppdump_fd = pppdump_fd;
+	parser.audio_fd   = audio_fd;
+}
+
+#define PROTO_TABLE_SIZE 20
+
+static struct {
+	uint16_t handle;
+	uint16_t psm;
+	uint8_t  channel;
+	uint32_t proto;
+} proto_table[PROTO_TABLE_SIZE];
+
+void set_proto(uint16_t handle, uint16_t psm, uint8_t channel, uint32_t proto)
+{
+	int i, pos = -1;
+
+	if (psm > 0 && psm < 0x1000 && !channel)
+		return;
+
+	if (!psm && channel)
+		psm = RFCOMM_PSM; 
+
+	for (i = 0; i < PROTO_TABLE_SIZE; i++) {
+		if (proto_table[i].handle == handle && proto_table[i].psm == psm && proto_table[i].channel == channel) {
+			pos = i;
+			break;
+		}
+
+		if (pos < 0 && !proto_table[i].handle && !proto_table[i].psm && !proto_table[i].channel)
+			pos = i;
+	}
+
+	if (pos < 0)
+		return;
+
+	proto_table[pos].handle  = handle;
+	proto_table[pos].psm     = psm;
+	proto_table[pos].channel = channel;
+	proto_table[pos].proto   = proto;
+}
+
+uint32_t get_proto(uint16_t handle, uint16_t psm, uint8_t channel)
+{
+	int i, pos = -1;
+
+	if (!psm && channel)
+		psm = RFCOMM_PSM;
+
+	for (i = 0; i < PROTO_TABLE_SIZE; i++) {
+		if (proto_table[i].handle == handle && proto_table[i].psm == psm && proto_table[i].channel == channel)
+			return proto_table[i].proto;
+
+		if (!proto_table[i].handle) {
+			if (proto_table[i].psm == psm && proto_table[i].channel == channel)
+				pos = i;
+		}
+	}
+
+	return (pos < 0) ? 0 : proto_table[pos].proto;
+}
+
+#define FRAME_TABLE_SIZE 20
+
+static struct {
+	uint16_t handle;
+	uint8_t dlci;
+	uint8_t opcode;
+	uint8_t status;
+	struct frame frm;
+} frame_table[FRAME_TABLE_SIZE];
+
+void del_frame(uint16_t handle, uint8_t dlci)
+{
+	int i;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++)
+		if (frame_table[i].handle == handle &&
+					frame_table[i].dlci == dlci) {
+			frame_table[i].handle = 0;
+			frame_table[i].dlci   = 0;
+			frame_table[i].opcode = 0;
+			frame_table[i].status = 0;
+			if (frame_table[i].frm.data)
+				free(frame_table[i].frm.data);
+			memset(&frame_table[i].frm, 0, sizeof(struct frame));
+			break;
+		}
+}
+
+struct frame *add_frame(struct frame *frm)
+{
+	struct frame *fr;
+	void *data;
+	int i, pos = -1;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++) {
+		if (frame_table[i].handle == frm->handle &&
+					frame_table[i].dlci == frm->dlci) {
+			pos = i;
+			break;
+		}
+
+		if (pos < 0 && !frame_table[i].handle && !frame_table[i].dlci)
+			pos = i;
+	}
+
+	if (pos < 0)
+		return frm;
+
+	frame_table[pos].handle = frm->handle;
+	frame_table[pos].dlci   = frm->dlci;
+	fr = &frame_table[pos].frm;
+
+	data = malloc(fr->len + frm->len);
+	if (!data) {
+		perror("Can't allocate frame stream buffer");
+		del_frame(frm->handle, frm->dlci);
+		return frm;
+	}
+
+	if (fr->len > 0)
+		memcpy(data, fr->ptr, fr->len);
+
+	if (frm->len > 0)
+		memcpy(data + fr->len, frm->ptr, frm->len);
+
+	if (fr->data)
+		free(fr->data);
+
+	fr->data       = data;
+	fr->data_len   = fr->len + frm->len;
+	fr->len        = fr->data_len;
+	fr->ptr        = fr->data;
+	fr->dev_id     = frm->dev_id;
+	fr->in         = frm->in;
+	fr->ts         = frm->ts;
+	fr->handle     = frm->handle;
+	fr->cid        = frm->cid;
+	fr->num        = frm->num;
+	fr->dlci       = frm->dlci;
+	fr->channel    = frm->channel;
+	fr->pppdump_fd = frm->pppdump_fd;
+	fr->audio_fd   = frm->audio_fd;
+
+	return fr;
+}
+
+uint8_t get_opcode(uint16_t handle, uint8_t dlci)
+{
+	int i;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++)
+		if (frame_table[i].handle == handle &&
+					frame_table[i].dlci == dlci)
+			return frame_table[i].opcode;
+
+	return 0x00;
+}
+
+void set_opcode(uint16_t handle, uint8_t dlci, uint8_t opcode)
+{
+	int i;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++)
+		if (frame_table[i].handle == handle && 
+					frame_table[i].dlci == dlci) {
+			frame_table[i].opcode = opcode;
+			break;
+		}
+}
+
+uint8_t get_status(uint16_t handle, uint8_t dlci)
+{
+	int i;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++)
+		if (frame_table[i].handle == handle &&
+					frame_table[i].dlci == dlci)
+			return frame_table[i].status;
+
+	return 0x00;
+}
+
+void set_status(uint16_t handle, uint8_t dlci, uint8_t status)
+{
+	int i;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++)
+		if (frame_table[i].handle == handle &&
+					frame_table[i].dlci == dlci) {
+			frame_table[i].status = status;
+			break;
+		}
+}
+
+void ascii_dump(int level, struct frame *frm, int num)
+{
+	unsigned char *buf = frm->ptr;
+	register int i, n;
+
+	if ((num < 0) || (num > (int) frm->len))
+		num = frm->len;
+
+	for (i = 0, n = 1; i < num; i++, n++) {
+		if (n == 1)
+			p_indent(level, frm);
+		printf("%1c ", isprint(buf[i]) ? buf[i] : '.');
+		if (n == DUMP_WIDTH) {
+			printf("\n");
+			n = 0;
+		}
+	}
+	if (i && n != 1)
+		printf("\n");
+}
+
+void hex_dump(int level, struct frame *frm, int num)
+{
+	unsigned char *buf = frm->ptr;
+	register int i, n;
+
+	if ((num < 0) || (num > (int) frm->len))
+		num = frm->len;
+
+	for (i = 0, n = 1; i < num; i++, n++) {
+		if (n == 1)
+			p_indent(level, frm);
+		printf("%2.2X ", buf[i]);
+		if (n == DUMP_WIDTH) {
+			printf("\n");
+			n = 0;
+		}
+	}
+	if (i && n != 1)
+		printf("\n");
+}
+
+void ext_dump(int level, struct frame *frm, int num)
+{
+	unsigned char *buf = frm->ptr;
+	register int i, n = 0, size;
+
+	if ((num < 0) || (num > (int) frm->len))
+		num = frm->len;
+
+	while (num > 0) {
+		p_indent(level, frm);
+		printf("%04x: ", n);
+
+		size = num > 16 ? 16 : num;
+
+		for (i = 0; i < size; i++)
+			printf("%02x%s", buf[i], (i + 1) % 8 ? " " : "  ");
+		for (i = size; i < 16; i++)
+			printf("  %s", (i + 1) % 8 ? " " : "  ");
+
+		for (i = 0; i < size; i++)
+			printf("%1c", isprint(buf[i]) ? buf[i] : '.');
+		printf("\n");
+
+		buf  += size;
+		num  -= size;
+		n    += size;
+	}
+}
+
+void raw_ndump(int level, struct frame *frm, int num)
+{
+	if (!frm->len)
+		return;
+
+	switch (parser.flags & DUMP_TYPE_MASK) {
+	case DUMP_ASCII:
+		ascii_dump(level, frm, num);
+		break;
+
+	case DUMP_HEX:
+		hex_dump(level, frm, num);
+		break;
+
+	case DUMP_EXT:
+		ext_dump(level, frm, num);
+		break;
+	}
+}
+
+void raw_dump(int level, struct frame *frm)
+{
+	raw_ndump(level, frm, -1);
+}
diff --git a/bluez/repo/tools/parser/parser.h b/bluez/repo/tools/parser/parser.h
new file mode 100644
index 0000000..b7e1d75
--- /dev/null
+++ b/bluez/repo/tools/parser/parser.h
@@ -0,0 +1,264 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __PARSER_H
+#define __PARSER_H
+
+#include <time.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include "lib/bluetooth.h"
+#include "src/shared/util.h"
+
+struct frame {
+	void		*data;
+	uint32_t	data_len;
+	void		*ptr;
+	uint32_t	len;
+	uint16_t	dev_id;
+	uint8_t		in;
+	uint8_t		master;
+	uint16_t	handle;
+	uint16_t	cid;
+	uint16_t	num;
+	uint8_t		dlci;
+	uint8_t		channel;
+	unsigned long	flags;
+	struct timeval	ts;
+	int		pppdump_fd;
+	int		audio_fd;
+};
+
+/* Parser flags */
+#define DUMP_WIDTH	20
+
+#define DUMP_ASCII	0x0001
+#define DUMP_HEX	0x0002
+#define DUMP_EXT	0x0004
+#define DUMP_RAW	0x0008
+#define DUMP_BPA	0x0010
+#define DUMP_TSTAMP	0x0100
+#define DUMP_VERBOSE	0x0200
+#define DUMP_BTSNOOP	0x1000
+#define DUMP_PKTLOG	0x2000
+#define DUMP_NOVENDOR	0x4000
+#define DUMP_TYPE_MASK	(DUMP_ASCII | DUMP_HEX | DUMP_EXT)
+
+/* Parser filter */
+#define FILT_LMP	0x0001
+#define FILT_HCI	0x0002
+#define FILT_SCO	0x0004
+#define FILT_L2CAP	0x0008
+#define FILT_RFCOMM	0x0010
+#define FILT_SDP	0x0020
+#define FILT_BNEP	0x0040
+#define FILT_CMTP	0x0080
+#define FILT_HIDP	0x0100
+#define FILT_HCRP	0x0200
+#define FILT_AVDTP	0x0400
+#define FILT_AVCTP	0x0800
+#define FILT_ATT 	0x1000
+#define FILT_SMP	0x2000
+#define FILT_A2MP	0x4000
+
+#define FILT_OBEX	0x00010000
+#define FILT_CAPI	0x00020000
+#define FILT_PPP	0x00040000
+#define FILT_SAP	0x00080000
+#define FILT_ERICSSON	0x10000000
+#define FILT_CSR	0x1000000a
+#define FILT_DGA	0x1000000c
+
+#define STRUCT_OFFSET(type, member)  ((uint8_t *)&(((type *)NULL)->member) - \
+                                     (uint8_t *)((type *)NULL))
+
+#define STRUCT_END(type, member)     (STRUCT_OFFSET(type, member) + \
+                                     sizeof(((type *)NULL)->member))
+
+#define DEFAULT_COMPID	65535
+
+struct parser_t {
+	unsigned long flags;
+	unsigned long filter;
+	unsigned short defpsm;
+	unsigned short defcompid;
+	int state;
+	int pppdump_fd;
+	int audio_fd;
+};
+
+extern struct parser_t parser;
+
+void init_parser(unsigned long flags, unsigned long filter,
+		unsigned short defpsm, unsigned short defcompid,
+		int pppdump_fd, int audio_fd);
+
+static inline int p_filter(unsigned long f)
+{
+	return !(parser.filter & f);
+}
+
+static inline void p_indent(int level, struct frame *f)
+{
+	if (level < 0) {
+		parser.state = 0;
+		return;
+	}
+
+	if (!parser.state) {
+		if (parser.flags & DUMP_TSTAMP) {
+			if (parser.flags & DUMP_VERBOSE) {
+				struct tm tm;
+				time_t t = f->ts.tv_sec;
+				localtime_r(&t, &tm);
+				printf("%04d-%02d-%02d %02d:%02d:%02d.%06lu ",
+					tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+					tm.tm_hour, tm.tm_min, tm.tm_sec, f->ts.tv_usec);
+			} else
+				printf("%8lu.%06lu ", f->ts.tv_sec, f->ts.tv_usec);
+		}
+		printf("%c ", (f->in ? '>' : '<'));
+		parser.state = 1;
+	} else 
+		printf("  ");
+
+	if (level)
+		printf("%*c", (level*2), ' ');
+}
+
+static inline void p_ba2str(const bdaddr_t *ba, char *str)
+{
+	if (parser.flags & DUMP_NOVENDOR) {
+		uint8_t b[6];
+
+		baswap((bdaddr_t *) b, ba);
+		sprintf(str, "%2.2X:%2.2X:%2.2X:*:*:*", b[0], b[1], b[2]);
+	} else
+		ba2str(ba, str);
+}
+
+/* get_uXX functions do byte swaping */
+
+static inline uint8_t p_get_u8(struct frame *frm)
+{
+	uint8_t *u8_ptr = frm->ptr;
+	frm->ptr += 1;
+	frm->len -= 1;
+	return *u8_ptr;
+}
+
+static inline uint16_t p_get_u16(struct frame *frm)
+{
+	uint16_t *u16_ptr = frm->ptr;
+	frm->ptr += 2;
+	frm->len -= 2;
+	return get_be16(u16_ptr);
+}
+
+static inline uint32_t p_get_u32(struct frame *frm)
+{
+	uint32_t *u32_ptr = frm->ptr;
+	frm->ptr += 4;
+	frm->len -= 4;
+	return get_be32(u32_ptr);
+}
+
+static inline uint64_t p_get_u64(struct frame *frm)
+{
+	uint64_t *u64_ptr = frm->ptr;
+	uint64_t u64 = get_unaligned(u64_ptr), tmp;
+	frm->ptr += 8;
+	frm->len -= 8;
+	tmp = ntohl(u64 & 0xffffffff);
+	u64 = (tmp << 32) | ntohl(u64 >> 32);
+	return u64;
+}
+
+static inline void p_get_u128(struct frame *frm, uint64_t *l, uint64_t *h)
+{
+	*h = p_get_u64(frm);
+	*l = p_get_u64(frm);
+}
+
+char *get_uuid_name(int uuid);
+
+void set_proto(uint16_t handle, uint16_t psm, uint8_t channel, uint32_t proto);
+uint32_t get_proto(uint16_t handle, uint16_t psm, uint8_t channel);
+
+struct frame *add_frame(struct frame *frm);
+void del_frame(uint16_t handle, uint8_t dlci);
+
+uint8_t get_opcode(uint16_t handle, uint8_t dlci);
+void set_opcode(uint16_t handle, uint8_t dlci, uint8_t opcode);
+
+uint8_t get_status(uint16_t handle, uint8_t dlci);
+void set_status(uint16_t handle, uint8_t dlci, uint8_t status);
+
+void l2cap_clear(uint16_t handle);
+
+void ascii_dump(int level, struct frame *frm, int num);
+void hex_dump(int level, struct frame *frm, int num);
+void ext_dump(int level, struct frame *frm, int num);
+void raw_dump(int level, struct frame *frm);
+void raw_ndump(int level, struct frame *frm, int num);
+
+void lmp_dump(int level, struct frame *frm);
+void hci_dump(int level, struct frame *frm);
+void l2cap_dump(int level, struct frame *frm);
+void rfcomm_dump(int level, struct frame *frm);
+void sdp_dump(int level, struct frame *frm);
+void bnep_dump(int level, struct frame *frm);
+void cmtp_dump(int level, struct frame *frm);
+void hidp_dump(int level, struct frame *frm);
+void hcrp_dump(int level, struct frame *frm);
+void avdtp_dump(int level, struct frame *frm);
+void avctp_dump(int level, struct frame *frm, uint16_t psm);
+void avrcp_dump(int level, struct frame *frm, uint8_t hdr, uint16_t psm);
+void att_dump(int level, struct frame *frm);
+void smp_dump(int level, struct frame *frm);
+void sap_dump(int level, struct frame *frm);
+
+void obex_dump(int level, struct frame *frm);
+void capi_dump(int level, struct frame *frm);
+void ppp_dump(int level, struct frame *frm);
+void arp_dump(int level, struct frame *frm);
+void ip_dump(int level, struct frame *frm);
+void ericsson_dump(int level, struct frame *frm);
+void csr_dump(int level, struct frame *frm);
+void bpa_dump(int level, struct frame *frm);
+
+void amp_assoc_dump(int level, uint8_t *assoc, uint16_t len);
+
+static inline void parse(struct frame *frm)
+{
+	p_indent(-1, NULL);
+	if (parser.flags & DUMP_RAW)
+		raw_dump(0, frm);
+	else
+		hci_dump(0, frm);
+	fflush(stdout);
+}
+
+#endif /* __PARSER_H */
diff --git a/bluez/repo/tools/parser/ppp.c b/bluez/repo/tools/parser/ppp.c
new file mode 100644
index 0000000..3847ff3
--- /dev/null
+++ b/bluez/repo/tools/parser/ppp.c
@@ -0,0 +1,239 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define PPP_U8(frm)  (get_u8(frm))
+#define PPP_U16(frm) (btohs(htons(get_u16(frm))))
+#define PPP_U32(frm) (btohl(htonl(get_u32(frm))))
+
+static int ppp_traffic = 0;
+
+static unsigned char ppp_magic1[] = { 0x7e, 0xff, 0x03, 0xc0, 0x21 };
+static unsigned char ppp_magic2[] = { 0x7e, 0xff, 0x7d, 0x23, 0xc0, 0x21 };
+static unsigned char ppp_magic3[] = { 0x7e, 0x7d, 0xdf, 0x7d, 0x23, 0xc0, 0x21 };
+
+static inline int check_for_ppp_traffic(unsigned char *data, int size)
+{
+	unsigned int i;
+
+	for (i = 0; i < size - sizeof(ppp_magic1); i++)
+		if (!memcmp(data + i, ppp_magic1, sizeof(ppp_magic1))) {
+			ppp_traffic = 1;
+			return i;
+		}
+
+	for (i = 0; i < size - sizeof(ppp_magic2); i++)
+		if (!memcmp(data + i, ppp_magic2, sizeof(ppp_magic2))) {
+			ppp_traffic = 1;
+			return i;
+		}
+
+	for (i = 0; i < size - sizeof(ppp_magic3); i++)
+		if (!memcmp(data + i, ppp_magic3, sizeof(ppp_magic3))) {
+			ppp_traffic = 1;
+			return i;
+		}
+
+	return -1;
+}
+
+static inline char *dir2str(uint8_t in)
+{
+	return in ? "DCE" : "DTE";
+}
+
+static inline char *proto2str(uint16_t proto)
+{
+	switch (proto) {
+	case 0x0001:
+		return "Padding Protocol";
+	case 0x0021:
+		return "IP";
+	case 0x8021:
+		return "IP Control Protocol";
+	case 0x80fd:
+		return "Compression Control Protocol";
+	case 0xc021:
+		return "Link Control Protocol";
+	case 0xc023:
+		return "Password Authentication Protocol";
+	case 0xc025:
+		return "Link Quality Report";
+	case 0xc223:
+		return "Challenge Handshake Authentication Protocol";
+	default:
+		return "Unknown Protocol";
+	}
+}
+
+static void hdlc_dump(int level, struct frame *frm)
+{
+	uint8_t addr = p_get_u8(frm);
+	uint8_t ctrl = p_get_u8(frm);
+	uint16_t fcs, proto;
+
+	fcs = get_unaligned((uint16_t *) (frm->ptr + frm->len - 2));
+	frm->len -= 2;
+
+	p_indent(level, frm);
+
+	if (addr != 0xff || ctrl != 0x03) {
+		frm->ptr -= 2;
+		frm->len += 2;
+		printf("HDLC: %s: len %d fcs 0x%04x\n",
+				dir2str(frm->in), frm->len, fcs);
+	} else
+		printf("HDLC: %s: addr 0x%02x ctrl 0x%02x len %d fcs 0x%04x\n",
+				dir2str(frm->in), addr, ctrl, frm->len, fcs);
+
+	if (*((uint8_t *) frm->ptr) & 0x80)
+		proto = p_get_u16(frm);
+	else
+		proto = p_get_u8(frm);
+
+	p_indent(level + 1, frm);
+	printf("PPP: %s (0x%04x): len %d\n", proto2str(proto), proto, frm->len);
+
+	raw_dump(level + 1, frm);
+}
+
+static inline void unslip_frame(int level, struct frame *frm, int len)
+{
+	struct frame msg;
+	unsigned char *data, *ptr;
+	int i, p = 0;
+
+	data = malloc(len * 2);
+	if (!data)
+		return;
+
+	ptr = frm->ptr;
+
+	for (i = 0; i < len; i++) {
+		if (ptr[i] == 0x7d) {
+			data[p++] = ptr[i + 1] ^ 0x20;
+			i++;
+		} else
+			data[p++] = ptr[i];
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.data     = data;
+	msg.data_len = len * 2;
+	msg.ptr      = msg.data;
+	msg.len      = p;
+	msg.in       = frm->in;
+	msg.ts       = frm->ts;
+	msg.handle   = frm->handle;
+	msg.cid      = frm->cid;
+
+	hdlc_dump(level, &msg);
+
+	free(data);
+}
+
+void ppp_dump(int level, struct frame *frm)
+{
+	void *ptr, *end;
+	int err, len, pos = 0;
+
+	if (frm->pppdump_fd > fileno(stderr)) {
+		unsigned char id;
+		uint16_t len = htons(frm->len);
+		uint32_t ts = htonl(frm->ts.tv_sec & 0xffffffff);
+
+		id = 0x07;
+		err = write(frm->pppdump_fd, &id, 1);
+		if (err < 0)
+			return;
+
+		err = write(frm->pppdump_fd, &ts, 4);
+		if (err < 0)
+			return;
+
+		id = frm->in ? 0x02 : 0x01;
+		err = write(frm->pppdump_fd, &id, 1);
+		if (err < 0)
+			return;
+		err = write(frm->pppdump_fd, &len, 2);
+		if (err < 0)
+			return;
+		err = write(frm->pppdump_fd, frm->ptr, frm->len);
+		if (err < 0)
+			return;
+	}
+
+	if (!ppp_traffic) {
+		pos = check_for_ppp_traffic(frm->ptr, frm->len);
+		if (pos < 0) {
+			raw_dump(level, frm);
+			return;
+		}
+
+		if (pos > 0) {
+			raw_ndump(level, frm, pos);
+			frm->ptr += pos;
+			frm->len -= pos;
+		}
+	}
+
+	frm = add_frame(frm);
+
+	while (frm->len > 0) {
+		ptr = memchr(frm->ptr, 0x7e, frm->len);
+		if (!ptr)
+			break;
+
+		if (frm->ptr != ptr) {
+			frm->len -= (ptr - frm->ptr);
+			frm->ptr = ptr;
+		}
+
+		end = memchr(frm->ptr + 1, 0x7e, frm->len - 1);
+		if (!end)
+			break;
+
+		len = end - ptr - 1;
+
+		frm->ptr++;
+		frm->len--;
+
+		if (len > 0) {
+			unslip_frame(level, frm, len);
+
+			frm->ptr += len;
+			frm->len -= len;
+		}
+	}
+}
diff --git a/bluez/repo/tools/parser/rfcomm.c b/bluez/repo/tools/parser/rfcomm.c
new file mode 100644
index 0000000..ea8dfad
--- /dev/null
+++ b/bluez/repo/tools/parser/rfcomm.c
@@ -0,0 +1,360 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Wayne Lee <waynelee@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+#include "rfcomm.h"
+#include "sdp.h"
+
+static char *cr_str[] = {
+	"RSP",
+	"CMD"
+};
+
+#define CR_STR(mcc_head) cr_str[mcc_head->type.cr]
+#define GET_DLCI(addr) ((addr.server_chn << 1) | (addr.d & 1))
+
+static void print_rfcomm_hdr(long_frame_head* head, uint8_t *ptr, int len)
+{
+	address_field addr = head->addr;
+	uint8_t ctr = head->control;
+	uint16_t ilen = head->length.bits.len;
+	uint8_t pf, dlci, fcs;
+
+	dlci     = GET_DLCI(addr);
+	pf       = GET_PF(ctr);
+	fcs      = *(ptr + len - 1);
+
+	printf("cr %d dlci %d pf %d ilen %d fcs 0x%x ", addr.cr, dlci, pf, ilen, fcs); 
+}
+
+static void print_mcc(mcc_long_frame_head* mcc_head)
+{
+	printf("mcc_len %d\n", mcc_head->length.bits.len);
+}
+
+static inline void mcc_test(int level, uint8_t *ptr, int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	printf("TEST %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+
+	p_indent(level, 0);
+	printf("%*cTest data: 0x ", level, ' ');
+
+	while (len > 1) {
+		printf("%2.2x ", (uint8_t)*ptr);
+		len--;
+		ptr++;
+	}
+
+	printf("\n");
+}
+static inline void mcc_fcon(int level, uint8_t *ptr, int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	printf("FCON %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+}
+
+static inline void mcc_fcoff(int level, uint8_t *ptr, int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	printf("FCOFF %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+}
+
+static inline void mcc_msc(int level, uint8_t *ptr, unsigned int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	msc_msg *msc = (void*) (ptr - STRUCT_END(msc_msg, mcc_s_head));
+
+	printf("MSC %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+	p_indent(level, 0);
+	printf("dlci %d fc %d rtc %d rtr %d ic %d dv %d",
+		GET_DLCI(msc->dlci), msc->v24_sigs.fc, msc->v24_sigs.rtc, 
+		msc->v24_sigs.rtr, msc->v24_sigs.ic, msc->v24_sigs.dv );
+
+	/* Assuming that break_signals field is _not declared_ in struct msc_msg... */
+	if (len > STRUCT_OFFSET(msc_msg, fcs) - STRUCT_END(msc_msg, v24_sigs)) {
+		break_signals *brk = (break_signals *)
+			(ptr + STRUCT_END(msc_msg, v24_sigs));
+		printf(" b1 %d b2 %d b3 %d len %d\n",
+			brk->b1, brk->b2, brk->b3, brk->len);
+	} else
+		printf("\n");
+}
+
+static inline void mcc_rpn(int level, uint8_t *ptr, unsigned int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	rpn_msg *rpn = (void *) (ptr - STRUCT_END(rpn_msg, mcc_s_head));
+
+	printf("RPN %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+
+	p_indent(level, 0);
+	printf("dlci %d ", GET_DLCI(rpn->dlci));
+
+	/* Assuming that rpn_val is _declared_ as a member of rpn_msg... */
+	if (len <= STRUCT_OFFSET(rpn_msg, rpn_val) - STRUCT_END(rpn_msg, mcc_s_head)) {
+		printf("\n");
+		return;
+	}
+
+	printf("br %d db %d sb %d p %d pt %d xi %d xo %d\n",
+		rpn->rpn_val.bit_rate, rpn->rpn_val.data_bits, 
+		rpn->rpn_val.stop_bit, rpn->rpn_val.parity,
+		rpn->rpn_val.parity_type, rpn->rpn_val.xon_input,
+		rpn->rpn_val.xon_output);
+
+	p_indent(level, 0);
+	printf("rtri %d rtro %d rtci %d rtco %d xon %d xoff %d pm 0x%04x\n",
+		rpn->rpn_val.rtr_input, rpn->rpn_val.rtr_output,
+		rpn->rpn_val.rtc_input, rpn->rpn_val.rtc_output,
+		rpn->rpn_val.xon, rpn->rpn_val.xoff, btohs(rpn->rpn_val.pm));
+}
+
+static inline void mcc_rls(int level, uint8_t *ptr, int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	rls_msg* rls = (void*) (ptr - STRUCT_END(rls_msg, mcc_s_head));
+
+	printf("RLS %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+	printf("dlci %d error: %d", GET_DLCI(rls->dlci), rls->error);
+}
+
+static inline void mcc_pn(int level, uint8_t *ptr, int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+	pn_msg *pn = (void*) (ptr - STRUCT_END(pn_msg, mcc_s_head));
+
+	printf("PN %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+
+	p_indent(level, 0);
+	printf("dlci %d frame_type %d credit_flow %d pri %d ack_timer %d\n",
+		pn->dlci, pn->frame_type, pn->credit_flow, pn->prior, pn->ack_timer);
+	p_indent(level, 0);
+	printf("frame_size %d max_retrans %d credits %d\n",
+		btohs(pn->frame_size), pn->max_nbrof_retrans, pn->credits);
+}
+
+static inline void mcc_nsc(int level, uint8_t *ptr, int len,
+				long_frame_head *head, mcc_long_frame_head *mcc_head)
+{
+
+	nsc_msg *nsc = (void*) (ptr - STRUCT_END(nsc_msg, mcc_s_head));
+
+	printf("NSC %s: ", CR_STR(mcc_head));
+	print_rfcomm_hdr(head, ptr, len);
+	print_mcc(mcc_head);
+
+	p_indent(level, 0);
+	printf("cr %d, mcc_cmd_type %x\n", 
+		nsc->command_type.cr, nsc->command_type.type );
+}
+
+static inline void mcc_frame(int level, struct frame *frm, long_frame_head *head)
+{
+	mcc_short_frame_head *mcc_short_head_p = frm->ptr;
+	mcc_long_frame_head mcc_head;
+	uint8_t hdr_size;
+
+	if ( mcc_short_head_p->length.ea == EA ) {
+		mcc_head.type = mcc_short_head_p->type;
+		mcc_head.length.bits.len = mcc_short_head_p->length.len;
+		hdr_size = sizeof(mcc_short_frame_head);
+	} else {
+		mcc_head = *(mcc_long_frame_head *)frm->ptr;
+		mcc_head.length.val = btohs(mcc_head.length.val);
+		hdr_size = sizeof(mcc_long_frame_head);
+	}
+
+	frm->ptr += hdr_size;
+	frm->len -= hdr_size;
+
+	p_indent(level, frm);
+	printf("RFCOMM(s): ");
+
+	switch (mcc_head.type.type) {
+	case TEST:
+		mcc_test(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case FCON:
+		mcc_fcon(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case FCOFF:
+		mcc_fcoff(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case MSC:
+		mcc_msc(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case RPN:
+		mcc_rpn(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case RLS:
+		mcc_rls(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case PN:
+		mcc_pn(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	case NSC:
+		mcc_nsc(level, frm->ptr, frm->len, head, &mcc_head);
+		break;
+	default:
+		printf("MCC message type 0x%02x: ", mcc_head.type.type);
+		print_rfcomm_hdr(head, frm->ptr, frm->len);
+		printf("\n");
+
+		frm->len--;
+		raw_dump(level, frm); 
+	}
+}
+
+static inline void uih_frame(int level, struct frame *frm, long_frame_head *head)
+{
+	uint32_t proto;
+
+	if (!head->addr.server_chn) {
+		mcc_frame(level, frm, head); 
+	} else {
+		p_indent(level, frm);
+		printf("RFCOMM(d): UIH: ");
+		print_rfcomm_hdr(head, frm->ptr, frm->len);
+		if (GET_PF(head->control)) {
+			printf("credits %d\n", *(uint8_t *)(frm->ptr));
+			frm->ptr++;
+			frm->len--;
+		} else
+			printf("\n");
+
+		frm->len--;
+		frm->dlci = GET_DLCI(head->addr);
+		frm->channel = head->addr.server_chn;
+
+		proto = get_proto(frm->handle, RFCOMM_PSM, frm->channel);
+
+		if (frm->len > 0) {
+			switch (proto) {
+			case SDP_UUID_OBEX:
+				if (!p_filter(FILT_OBEX))
+					obex_dump(level + 1, frm);
+				else
+					raw_dump(level, frm);
+				break;
+
+			case SDP_UUID_LAN_ACCESS_PPP:
+			case SDP_UUID_DIALUP_NETWORKING:
+				if (!p_filter(FILT_PPP))
+					ppp_dump(level + 1, frm);
+				else
+					raw_dump(level, frm);
+				break;
+
+			case SDP_UUID_SIM_ACCESS:
+				if (!p_filter(FILT_SAP))
+					sap_dump(level + 1, frm);
+				else
+					raw_dump(level, frm);
+				break;
+
+			default:
+				if (p_filter(FILT_RFCOMM))
+					break;
+
+				raw_dump(level, frm);
+				break;
+			}
+		}
+	}
+}
+
+void rfcomm_dump(int level, struct frame *frm)
+{
+	uint8_t hdr_size, ctr_type;
+	short_frame_head *short_head_p = (void *) frm->ptr;
+	long_frame_head head;
+
+	if (short_head_p->length.ea == EA) {
+		head.addr = short_head_p->addr;
+		head.control = short_head_p->control;
+		head.length.bits.len = short_head_p->length.len;
+		hdr_size = sizeof(short_frame_head);
+	} else {
+		head = *(long_frame_head *) frm->ptr;
+		head.length.val = btohs(head.length.val);
+		hdr_size = sizeof(long_frame_head);
+	}
+
+	frm->ptr += hdr_size;
+	frm->len -= hdr_size;
+
+	ctr_type = CLR_PF(head.control);
+
+	if (ctr_type == UIH) {
+		uih_frame(level, frm, &head);
+	} else {
+		p_indent(level, frm); 
+		printf("RFCOMM(s): ");
+
+		switch (ctr_type) {
+		case SABM:
+			printf("SABM: ");
+			break;
+		case UA:
+			printf("UA: ");
+			break;
+		case DM:
+			printf("DM: ");
+			break;
+		case DISC:
+			printf("DISC: ");
+			del_frame(frm->handle, GET_DLCI(head.addr));
+			break;
+		default:
+			printf("ERR: ");
+		}
+		print_rfcomm_hdr(&head, frm->ptr, frm->len);
+		printf("\n");
+	}
+}
diff --git a/bluez/repo/tools/parser/rfcomm.h b/bluez/repo/tools/parser/rfcomm.h
new file mode 100644
index 0000000..a9faa0b
--- /dev/null
+++ b/bluez/repo/tools/parser/rfcomm.h
@@ -0,0 +1,494 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Wayne Lee <waynelee@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __RFCOMM_H
+#define __RFCOMM_H
+
+#include <endian.h>
+
+#define RFCOMM_PSM 3
+
+#define TRUE  1
+#define FALSE 0
+
+#define RFCOMM_MAX_CONN 10
+#define BT_NBR_DATAPORTS RFCOMM_MAX_CONN
+
+#define GET_BIT(pos,bitfield) ((bitfield[(pos)/32]) & (1 << ((pos) % 32)))
+#define SET_BIT(pos,bitfield) ((bitfield[(pos)/32]) |= (1 << ((pos) % 32))) 
+#define CLR_BIT(pos,bitfield) ((bitfield[(pos)/32]) &= ((1 << ((pos) % 32)) ^ (~0)))
+
+/* Sets the P/F-bit in the control field */
+#define SET_PF(ctr) ((ctr) | (1 << 4)) 
+/* Clears the P/F-bit in the control field */
+#define CLR_PF(ctr) ((ctr) & 0xef)
+/* Returns the P/F-bit */
+#define GET_PF(ctr) (((ctr) >> 4) & 0x1)
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+/* Endian-swapping macros for structs */
+#define swap_long_frame(x) ((x)->h.length.val = le16_to_cpu((x)->h.length.val))
+#define swap_mcc_long_frame(x) (swap_long_frame(x))
+
+/* Used for UIH packets */
+#define SHORT_CRC_CHECK 2
+/* Used for all packet exepts for the UIH packets */
+#define LONG_CRC_CHECK 3
+/* Short header for short UIH packets */
+#define SHORT_HDR 2
+/* Long header for long UIH packets */
+#define LONG_HDR 3
+
+/* FIXME: Should this one be defined here? */
+#define SHORT_PAYLOAD_SIZE 127
+/* Used for setting the EA field in different packets, really neccessary? */
+#define EA 1
+/* Yes the FCS size is only one byte */
+#define FCS_SIZE 1
+
+#define RFCOMM_MAX_HDR_SIZE 5
+
+#define MAX_CREDITS   30
+#define START_CREDITS 7
+#define MIN_CREDITS   6
+
+#define DEF_RFCOMM_MTU 127
+
+/* The values in the control field when sending ordinary rfcomm packets */
+#define SABM 0x2f	/* set asynchronous balanced mode */
+#define UA   0x63	/* unnumbered acknolodgement */
+#define DM   0x0f	/* disconnected mode */
+#define DISC 0x43	/* disconnect */
+#define UIH  0xef	/* unnumbered information with header check (only) */
+#define UI   0x03	/* unnumbered information (with all data check) */
+
+#define SABM_SIZE 4
+#define UA_SIZE   4
+
+/* The values in the type field in a multiplexer command packet */
+#define PN    (0x80 >> 2)	/* parameter negotiation */
+#define PSC   (0x40 >> 2)	/* power saving control */
+#define CLD   (0xc0 >> 2)	/* close down */
+#define TEST  (0x20 >> 2)	/* test */
+#define FCON  (0xa0 >> 2)	/* flow control on */
+#define FCOFF (0x60 >> 2)	/* flow control off */
+#define MSC   (0xe0 >> 2)	/* modem status command */
+#define NSC   (0x10 >> 2)	/* not supported command response */
+#define RPN   (0x90 >> 2)	/* remote port negotiation */
+#define RLS   (0x50 >> 2)	/* remote line status */
+#define SNC   (0xd0 >> 2)	/* service negotiation command */
+
+/* Define of some V.24 signals modem control signals in RFCOMM */
+#define DV  0x80	/* data valid */
+#define IC  0x40	/* incoming call */
+#define RTR 0x08	/* ready to receive */
+#define RTC 0x04	/* ready to communicate */
+#define FC  0x02	/* flow control (unable to accept frames) */
+
+#define CTRL_CHAN 0	/* The control channel is defined as DLCI 0 in rfcomm */
+#define MCC_CMD 1	 /* Multiplexer command */
+#define MCC_RSP 0	 /* Multiplexer response */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct parameter_mask {
+	uint8_t bit_rate:1;
+	uint8_t data_bits:1;
+	uint8_t stop_bit:1;
+	uint8_t parity:1;
+	uint8_t parity_type:1;
+	uint8_t xon:1;
+	uint8_t xoff:1;
+	uint8_t res1:1;
+	uint8_t xon_input:1;
+	uint8_t xon_output:1;
+	uint8_t rtr_input:1;
+	uint8_t rtr_output:1;
+	uint8_t rtc_input:1;
+	uint8_t rtc_output:1;
+	uint8_t res2:2;
+} __attribute__ ((packed)) parameter_mask;
+
+typedef struct rpn_values {
+	uint8_t bit_rate;
+	uint8_t data_bits:2;
+	uint8_t stop_bit:1;
+	uint8_t parity:1;
+	uint8_t parity_type:2;
+	uint8_t res1:2;
+	uint8_t xon_input:1;
+	uint8_t xon_output:1;
+	uint8_t rtr_input:1;
+	uint8_t rtr_output:1;
+	uint8_t rtc_input:1;
+	uint8_t rtc_output:1;
+	uint8_t res2:2;
+	uint8_t xon;
+	uint8_t xoff;
+	uint16_t pm;
+	//parameter_mask pm;
+} __attribute__ ((packed)) rpn_values;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct parameter_mask {
+	uint8_t res1:1;
+	uint8_t xoff:1;
+	uint8_t xon:1;
+	uint8_t parity_type:1;
+	uint8_t parity:1;
+	uint8_t stop_bit:1;
+	uint8_t data_bits:1;
+	uint8_t bit_rate:1;
+	uint8_t res2:2;
+	uint8_t rtc_output:1;
+	uint8_t rtc_input:1;
+	uint8_t rtr_output:1;
+	uint8_t rtr_input:1;
+	uint8_t xon_output:1;
+	uint8_t xon_input:1;
+
+} __attribute__ ((packed)) parameter_mask;
+
+typedef struct rpn_values {
+	uint8_t bit_rate;
+	uint8_t res1:2;
+	uint8_t parity_type:2;
+	uint8_t parity:1;
+	uint8_t stop_bit:1;
+	uint8_t data_bits:2;
+	uint8_t res2:2;
+	uint8_t rtc_output:1;
+	uint8_t rtc_input:1;
+	uint8_t rtr_output:1;
+	uint8_t rtr_input:1;
+	uint8_t xon_output:1;
+	uint8_t xon_input:1;
+	uint8_t xon;
+	uint8_t xoff;
+	uint16_t pm;
+	//parameter_mask pm;
+} __attribute__ ((packed)) rpn_values;
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* Typedefinitions of stuctures used for creating and parsing packets, for a
+ * further description of the structures please se the bluetooth core
+ * specification part F:1 and the ETSI TS 07.10 specification  */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct address_field {
+	uint8_t ea:1;
+	uint8_t cr:1;
+	uint8_t d:1;
+	uint8_t server_chn:5;
+} __attribute__ ((packed)) address_field;
+
+typedef struct short_length {
+	uint8_t ea:1;
+	uint8_t len:7;
+} __attribute__ ((packed)) short_length;
+
+typedef union long_length {
+	struct bits {
+		uint8_t ea:1;
+		unsigned short len:15;
+	} __attribute__ ((packed)) bits ;
+	uint16_t val ;
+} __attribute__ ((packed)) long_length;
+
+typedef struct short_frame_head {
+	address_field addr;
+	uint8_t control;
+	short_length length;
+} __attribute__ ((packed)) short_frame_head;
+
+typedef struct short_frame {
+	short_frame_head h;
+	uint8_t data[0]; 
+} __attribute__ ((packed)) short_frame;
+
+typedef struct long_frame_head {
+	address_field addr;
+	uint8_t control;
+	long_length length;
+	uint8_t data[0];
+} __attribute__ ((packed)) long_frame_head;
+
+typedef struct long_frame {
+	long_frame_head h;
+	uint8_t data[0];
+} __attribute__ ((packed)) long_frame;
+
+/* Typedefinitions for structures used for the multiplexer commands */
+typedef struct mcc_type {
+	uint8_t ea:1;
+	uint8_t cr:1;
+	uint8_t type:6;
+} __attribute__ ((packed)) mcc_type;
+
+typedef struct mcc_short_frame_head {
+	mcc_type type;
+	short_length length;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_short_frame_head;
+
+typedef struct mcc_short_frame {
+	mcc_short_frame_head h;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_short_frame;
+
+typedef struct mcc_long_frame_head {
+	mcc_type type;
+	long_length length;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_long_frame_head;
+
+typedef struct mcc_long_frame {
+	mcc_long_frame_head h;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_long_frame;
+
+/* MSC-command */
+typedef struct v24_signals {
+	uint8_t ea:1;
+	uint8_t fc:1;
+	uint8_t rtc:1;
+	uint8_t rtr:1;
+	uint8_t reserved:2;
+	uint8_t ic:1;
+	uint8_t dv:1;
+} __attribute__ ((packed)) v24_signals;
+
+typedef struct break_signals {
+	uint8_t ea:1;
+	uint8_t b1:1;
+	uint8_t b2:1;
+	uint8_t b3:1;
+	uint8_t len:4;
+} __attribute__ ((packed)) break_signals;
+
+typedef struct msc_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	v24_signals v24_sigs;
+	//break_signals break_sigs;
+	uint8_t fcs;
+} __attribute__ ((packed)) msc_msg;
+
+typedef struct rpn_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	rpn_values rpn_val;
+	uint8_t fcs;
+} __attribute__ ((packed)) rpn_msg;
+
+/* RLS-command */  
+typedef struct rls_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	uint8_t error:4;
+	uint8_t res:4;
+	uint8_t fcs;
+} __attribute__ ((packed)) rls_msg;
+
+/* PN-command */
+typedef struct pn_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+/* The res1, res2 and res3 values have to be set to 0 by the sender */
+	uint8_t dlci:6;
+	uint8_t res1:2;
+	uint8_t frame_type:4;
+	uint8_t credit_flow:4;
+	uint8_t prior:6;
+	uint8_t res2:2;
+	uint8_t ack_timer;
+	uint16_t frame_size:16;
+	uint8_t max_nbrof_retrans;
+	uint8_t credits;
+	uint8_t fcs;
+} __attribute__ ((packed)) pn_msg;
+
+/* NSC-command */
+typedef struct nsc_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	mcc_type command_type;
+	uint8_t fcs;
+} __attribute__ ((packed)) nsc_msg;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct address_field {
+	uint8_t server_chn:5;
+	uint8_t d:1;
+	uint8_t cr:1;
+	uint8_t ea:1;
+} __attribute__ ((packed)) address_field;
+
+typedef struct short_length {
+	uint8_t len:7;
+	uint8_t ea:1;
+} __attribute__ ((packed)) short_length;
+
+typedef union long_length {
+	struct bits {
+		unsigned short len:15;
+		uint8_t ea:1;
+	} __attribute__ ((packed)) bits;
+	uint16_t val;
+} __attribute__ ((packed)) long_length;
+
+typedef struct short_frame_head {
+	address_field addr;
+	uint8_t control;
+	short_length length;
+} __attribute__ ((packed)) short_frame_head;
+
+typedef struct short_frame {
+	short_frame_head h;
+	uint8_t data[0];
+} __attribute__ ((packed)) short_frame;
+
+typedef struct long_frame_head {
+	address_field addr;
+	uint8_t control;
+	long_length length;
+	uint8_t data[0];
+} __attribute__ ((packed)) long_frame_head;
+
+typedef struct long_frame {
+	long_frame_head h;
+	uint8_t data[0];
+} __attribute__ ((packed)) long_frame;
+
+typedef struct mcc_type {
+	uint8_t type:6;
+	uint8_t cr:1;
+	uint8_t ea:1;
+} __attribute__ ((packed)) mcc_type;
+
+typedef struct mcc_short_frame_head {
+	mcc_type type;
+	short_length length;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_short_frame_head;
+
+typedef struct mcc_short_frame {
+	mcc_short_frame_head h;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_short_frame;
+
+typedef struct mcc_long_frame_head {
+	mcc_type type;
+	long_length length;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_long_frame_head;
+
+typedef struct mcc_long_frame {
+	mcc_long_frame_head h;
+	uint8_t value[0];
+} __attribute__ ((packed)) mcc_long_frame;
+
+typedef struct v24_signals {
+	uint8_t dv:1;
+	uint8_t ic:1;
+	uint8_t reserved:2;
+	uint8_t rtr:1;
+	uint8_t rtc:1;
+	uint8_t fc:1;
+	uint8_t ea:1;
+} __attribute__ ((packed)) v24_signals;
+
+typedef struct break_signals {
+	uint8_t len:4;
+	uint8_t b3:1;
+	uint8_t b2:1;
+	uint8_t b1:1;
+	uint8_t ea:1;
+} __attribute__ ((packed)) break_signals;
+
+typedef struct msc_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	v24_signals v24_sigs;
+	//break_signals break_sigs;
+	uint8_t fcs;
+} __attribute__ ((packed)) msc_msg;
+
+typedef struct rpn_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	rpn_values rpn_val;
+	uint8_t fcs;
+} __attribute__ ((packed)) rpn_msg;
+
+typedef struct rls_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	uint8_t res:4;
+	uint8_t error:4;
+	uint8_t fcs;
+} __attribute__ ((packed)) rls_msg;
+
+typedef struct pn_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	uint8_t res1:2;
+	uint8_t dlci:6;
+	uint8_t credit_flow:4;
+	uint8_t frame_type:4;
+	uint8_t res2:2;
+	uint8_t prior:6;
+	uint8_t ack_timer;
+	uint16_t frame_size:16;
+	uint8_t max_nbrof_retrans;
+	uint8_t credits;
+	uint8_t fcs;
+} __attribute__ ((packed)) pn_msg;
+
+typedef struct nsc_msg {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	mcc_type command_type;
+	uint8_t fcs;
+} __attribute__ ((packed)) nsc_msg;
+
+#else
+#error "Unknown byte order"
+#error Processor endianness unknown!
+#endif
+
+#endif /* __RFCOMM_H */
diff --git a/bluez/repo/tools/parser/sap.c b/bluez/repo/tools/parser/sap.c
new file mode 100644
index 0000000..f51f0cc
--- /dev/null
+++ b/bluez/repo/tools/parser/sap.c
@@ -0,0 +1,335 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Tieto Poland
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+#define PADDING4(x) ((4 - ((x) & 0x03)) & 0x03)
+
+#define SAP_CONNECT_REQ				0x00
+#define SAP_CONNECT_RESP			0x01
+#define SAP_DISCONNECT_REQ			0x02
+#define SAP_DISCONNECT_RESP			0x03
+#define SAP_DISCONNECT_IND			0x04
+#define SAP_TRANSFER_APDU_REQ			0x05
+#define SAP_TRANSFER_APDU_RESP			0x06
+#define SAP_TRANSFER_ATR_REQ			0x07
+#define SAP_TRANSFER_ATR_RESP			0x08
+#define SAP_POWER_SIM_OFF_REQ			0x09
+#define SAP_POWER_SIM_OFF_RESP			0x0A
+#define SAP_POWER_SIM_ON_REQ			0x0B
+#define SAP_POWER_SIM_ON_RESP			0x0C
+#define SAP_RESET_SIM_REQ			0x0D
+#define SAP_RESET_SIM_RESP			0x0E
+#define SAP_TRANSFER_CARD_READER_STATUS_REQ	0x0F
+#define SAP_TRANSFER_CARD_READER_STATUS_RESP	0x10
+#define SAP_STATUS_IND				0x11
+#define SAP_ERROR_RESP				0x12
+#define SAP_SET_TRANSPORT_PROTOCOL_REQ		0x13
+#define SAP_SET_TRANSPORT_PROTOCOL_RESP		0x14
+
+#define SAP_PARAM_ID_MAX_MSG_SIZE	0x00
+#define SAP_PARAM_ID_CONN_STATUS	0x01
+#define SAP_PARAM_ID_RESULT_CODE	0x02
+#define SAP_PARAM_ID_DISCONNECT_IND	0x03
+#define SAP_PARAM_ID_COMMAND_APDU	0x04
+#define SAP_PARAM_ID_COMMAND_APDU7816	0x10
+#define SAP_PARAM_ID_RESPONSE_APDU	0x05
+#define SAP_PARAM_ID_ATR		0x06
+#define SAP_PARAM_ID_CARD_READER_STATUS	0x07
+#define SAP_PARAM_ID_STATUS_CHANGE	0x08
+#define SAP_PARAM_ID_TRANSPORT_PROTOCOL	0x09
+
+#define SAP_STATUS_OK				0x00
+#define SAP_STATUS_CONNECTION_FAILED		0x01
+#define SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED	0x02
+#define SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL	0x03
+#define SAP_STATUS_OK_ONGOING_CALL		0x04
+
+#define SAP_DISCONNECTION_TYPE_GRACEFUL		0x00
+#define SAP_DISCONNECTION_TYPE_IMMEDIATE	0x01
+#define SAP_DISCONNECTION_TYPE_CLIENT		0xFF
+
+#define SAP_RESULT_OK			0x00
+#define SAP_RESULT_ERROR_NO_REASON	0x01
+#define SAP_RESULT_ERROR_NOT_ACCESSIBLE	0x02
+#define SAP_RESULT_ERROR_POWERED_OFF	0x03
+#define SAP_RESULT_ERROR_CARD_REMOVED	0x04
+#define SAP_RESULT_ERROR_POWERED_ON	0x05
+#define SAP_RESULT_ERROR_NO_DATA	0x06
+#define SAP_RESULT_NOT_SUPPORTED	0x07
+
+#define SAP_STATUS_CHANGE_UNKNOWN_ERROR		0x00
+#define SAP_STATUS_CHANGE_CARD_RESET		0x01
+#define SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE	0x02
+#define SAP_STATUS_CHANGE_CARD_REMOVED		0x03
+#define SAP_STATUS_CHANGE_CARD_INSERTED		0x04
+#define SAP_STATUS_CHANGE_CARD_RECOVERED	0x05
+
+#define SAP_TRANSPORT_PROTOCOL_T0	0x00
+#define SAP_TRANSPORT_PROTOCOL_T1	0x01
+
+static const char *msg2str(uint8_t msg)
+{
+	switch (msg) {
+	case SAP_CONNECT_REQ:
+		return "Connect Req";
+	case SAP_CONNECT_RESP:
+		return "Connect Resp";
+	case SAP_DISCONNECT_REQ:
+		return "Disconnect Req";
+	case SAP_DISCONNECT_RESP:
+		return "Disconnect Resp";
+	case SAP_DISCONNECT_IND:
+		return "Disconnect Ind";
+	case SAP_TRANSFER_APDU_REQ:
+		return "Transfer APDU Req";
+	case SAP_TRANSFER_APDU_RESP:
+		return "Transfer APDU Resp";
+	case SAP_TRANSFER_ATR_REQ:
+		return "Transfer ATR Req";
+	case SAP_TRANSFER_ATR_RESP:
+		return "Transfer ATR Resp";
+	case SAP_POWER_SIM_OFF_REQ:
+		return "Power SIM Off Req";
+	case SAP_POWER_SIM_OFF_RESP:
+		return "Power SIM Off Resp";
+	case SAP_POWER_SIM_ON_REQ:
+		return "Power SIM On Req";
+	case SAP_POWER_SIM_ON_RESP:
+		return "Power SIM On Resp";
+	case SAP_RESET_SIM_REQ:
+		return "Reset SIM Req";
+	case SAP_RESET_SIM_RESP:
+		return "Reset SIM Resp";
+	case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+		return "Transfer Card Reader Status Req";
+	case SAP_TRANSFER_CARD_READER_STATUS_RESP:
+		return "Transfer Card Reader Status Resp";
+	case SAP_STATUS_IND:
+		return "Status Ind";
+	case SAP_ERROR_RESP:
+		return "Error Resp";
+	case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+		return "Set Transport Protocol Req";
+	case SAP_SET_TRANSPORT_PROTOCOL_RESP:
+		return "Set Transport Protocol Resp";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *param2str(uint8_t param)
+{
+	switch (param) {
+	case SAP_PARAM_ID_MAX_MSG_SIZE:
+		return "MaxMsgSize";
+	case SAP_PARAM_ID_CONN_STATUS:
+		return "ConnectionStatus";
+	case SAP_PARAM_ID_RESULT_CODE:
+		return "ResultCode";
+	case SAP_PARAM_ID_DISCONNECT_IND:
+		return "DisconnectionType";
+	case SAP_PARAM_ID_COMMAND_APDU:
+		return "CommandAPDU";
+	case SAP_PARAM_ID_COMMAND_APDU7816:
+		return "CommandAPDU7816";
+	case SAP_PARAM_ID_RESPONSE_APDU:
+		return "ResponseAPDU";
+	case SAP_PARAM_ID_ATR:
+		return "ATR";
+	case SAP_PARAM_ID_CARD_READER_STATUS:
+		return "CardReaderStatus";
+	case SAP_PARAM_ID_STATUS_CHANGE:
+		return "StatusChange";
+	case SAP_PARAM_ID_TRANSPORT_PROTOCOL:
+		return "TransportProtocol";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *status2str(uint8_t status)
+{
+	switch (status) {
+	case  SAP_STATUS_OK:
+		return "OK, Server can fulfill requirements";
+	case  SAP_STATUS_CONNECTION_FAILED:
+		return "Error, Server unable to establish connection";
+	case  SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED:
+		return "Error, Server does not support maximum message size";
+	case  SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL:
+		return "Error, maximum message size by Client is too small";
+	case  SAP_STATUS_OK_ONGOING_CALL:
+		return "OK, ongoing call";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *disctype2str(uint8_t disctype)
+{
+	switch (disctype) {
+	case  SAP_DISCONNECTION_TYPE_GRACEFUL:
+		return "Graceful";
+	case  SAP_DISCONNECTION_TYPE_IMMEDIATE:
+		return "Immediate";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *result2str(uint8_t result)
+{
+	switch (result) {
+	case  SAP_RESULT_OK:
+		return "OK, request processed correctly";
+	case  SAP_RESULT_ERROR_NO_REASON:
+		return "Error, no reason defined";
+	case SAP_RESULT_ERROR_NOT_ACCESSIBLE:
+		return "Error, card not accessible";
+	case  SAP_RESULT_ERROR_POWERED_OFF:
+		return "Error, card (already) powered off";
+	case  SAP_RESULT_ERROR_CARD_REMOVED:
+		return "Error, card removed";
+	case  SAP_RESULT_ERROR_POWERED_ON:
+		return "Error, card already powered on";
+	case  SAP_RESULT_ERROR_NO_DATA:
+		return "Error, data not available";
+	case  SAP_RESULT_NOT_SUPPORTED:
+		return "Error, not supported";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *statuschg2str(uint8_t statuschg)
+{
+	switch (statuschg) {
+	case  SAP_STATUS_CHANGE_UNKNOWN_ERROR:
+		return "Unknown Error";
+	case  SAP_STATUS_CHANGE_CARD_RESET:
+		return "Card reset";
+	case  SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE:
+		return "Card not accessible";
+	case  SAP_STATUS_CHANGE_CARD_REMOVED:
+		return "Card removed";
+	case  SAP_STATUS_CHANGE_CARD_INSERTED:
+		return "Card inserted";
+	case  SAP_STATUS_CHANGE_CARD_RECOVERED:
+		return "Card recovered";
+	default:
+		return "Reserved";
+	}
+}
+
+static const char *prot2str(uint8_t prot)
+{
+	switch (prot) {
+	case SAP_TRANSPORT_PROTOCOL_T0:
+		return "T=0";
+	case SAP_TRANSPORT_PROTOCOL_T1:
+		return "T=1";
+	default:
+		return "Reserved";
+	}
+}
+
+static void parse_parameters(int level, struct frame *frm)
+{
+	uint8_t param;
+	uint16_t len;
+	uint8_t pv8;
+
+	while (frm->len > 3) {
+		p_indent(level, frm);
+
+		param = p_get_u8(frm);
+		p_get_u8(frm);
+		len = p_get_u16(frm);
+
+		printf("%s (0x%02x) len %d = ", param2str(param), param, len);
+
+		switch (param) {
+		case SAP_PARAM_ID_MAX_MSG_SIZE:
+			printf("%d\n", p_get_u16(frm));
+			break;
+		case SAP_PARAM_ID_CONN_STATUS:
+			pv8 = p_get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, status2str(pv8));
+			break;
+		case SAP_PARAM_ID_RESULT_CODE:
+		case SAP_PARAM_ID_CARD_READER_STATUS:
+			pv8 = p_get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, result2str(pv8));
+			break;
+		case SAP_PARAM_ID_DISCONNECT_IND:
+			pv8 = p_get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, disctype2str(pv8));
+			break;
+		case SAP_PARAM_ID_STATUS_CHANGE:
+			pv8 = p_get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, statuschg2str(pv8));
+			break;
+		case SAP_PARAM_ID_TRANSPORT_PROTOCOL:
+			pv8 = p_get_u8(frm);
+			printf("0x%02x (%s)\n", pv8, prot2str(pv8));
+			break;
+		default:
+			printf("\n");
+			raw_ndump(level + 1, frm, len);
+			frm->ptr += len;
+			frm->len -= len;
+		}
+
+		/* Skip padding */
+		frm->ptr += PADDING4(len);
+		frm->len -= PADDING4(len);
+	}
+}
+
+void sap_dump(int level, struct frame *frm)
+{
+	uint8_t msg, params;
+
+	msg = p_get_u8(frm);
+	params = p_get_u8(frm);
+
+	/* Skip reserved field */
+	p_get_u16(frm);
+
+	p_indent(level, frm);
+
+	printf("SAP: %s: params %d\n", msg2str(msg), params);
+
+	parse_parameters(level, frm);
+}
diff --git a/bluez/repo/tools/parser/sdp.c b/bluez/repo/tools/parser/sdp.c
new file mode 100644
index 0000000..dba9a36
--- /dev/null
+++ b/bluez/repo/tools/parser/sdp.c
@@ -0,0 +1,804 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Ricky Yuen <ryuen@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+#include "sdp.h"
+
+#define SDP_ERROR_RSP                                  0x01
+#define SDP_SERVICE_SEARCH_REQ                         0x02
+#define SDP_SERVICE_SEARCH_RSP                         0x03
+#define SDP_SERVICE_ATTR_REQ                           0x04
+#define SDP_SERVICE_ATTR_RSP                           0x05
+#define SDP_SERVICE_SEARCH_ATTR_REQ                    0x06
+#define SDP_SERVICE_SEARCH_ATTR_RSP                    0x07
+
+typedef struct {
+	uint8_t  pid;
+	uint16_t tid;
+	uint16_t len;
+} __attribute__ ((packed)) sdp_pdu_hdr;
+#define SDP_PDU_HDR_SIZE 5
+
+/* Data element type descriptor */
+#define SDP_DE_NULL   0
+#define SDP_DE_UINT   1
+#define SDP_DE_INT    2
+#define SDP_DE_UUID   3
+#define SDP_DE_STRING 4
+#define SDP_DE_BOOL   5
+#define SDP_DE_SEQ    6
+#define SDP_DE_ALT    7
+#define SDP_DE_URL    8
+
+/* Data element size index lookup table */
+typedef struct {
+	int addl_bits;
+	int num_bytes;
+} sdp_siz_idx_lookup_table_t;
+
+static sdp_siz_idx_lookup_table_t sdp_siz_idx_lookup_table[] = {
+	{ 0, 1  }, /* Size index = 0 */
+	{ 0, 2  }, /*              1 */
+	{ 0, 4  }, /*              2 */
+	{ 0, 8  }, /*              3 */
+	{ 0, 16 }, /*              4 */
+	{ 1, 1  }, /*              5 */
+	{ 1, 2  }, /*              6 */
+	{ 1, 4  }, /*              7 */
+};
+
+/* UUID name lookup table */
+typedef struct {
+	int   uuid;
+	char* name;
+} sdp_uuid_nam_lookup_table_t;
+
+static sdp_uuid_nam_lookup_table_t sdp_uuid_nam_lookup_table[] = {
+	{ SDP_UUID_SDP,                      "SDP"          },
+	{ SDP_UUID_UDP,                      "UDP"          },
+	{ SDP_UUID_RFCOMM,                   "RFCOMM"       },
+	{ SDP_UUID_TCP,                      "TCP"          },
+	{ SDP_UUID_TCS_BIN,                  "TCS-BIN"      },
+	{ SDP_UUID_TCS_AT,                   "TCS-AT"       },
+	{ SDP_UUID_OBEX,                     "OBEX"         },
+	{ SDP_UUID_IP,                       "IP"           },
+	{ SDP_UUID_FTP,                      "FTP"          },
+	{ SDP_UUID_HTTP,                     "HTTP"         },
+	{ SDP_UUID_WSP,                      "WSP"          },
+	{ SDP_UUID_L2CAP,                    "L2CAP"        },
+	{ SDP_UUID_BNEP,                     "BNEP"         }, /* PAN */
+	{ SDP_UUID_HIDP,                     "HIDP"         }, /* HID */
+	{ SDP_UUID_AVCTP,                    "AVCTP"        }, /* AVCTP */
+	{ SDP_UUID_AVDTP,                    "AVDTP"        }, /* AVDTP */
+	{ SDP_UUID_CMTP,                     "CMTP"         }, /* CIP */
+	{ SDP_UUID_UDI_C_PLANE,              "UDI_C-Plane"  }, /* UDI */
+	{ SDP_UUID_SERVICE_DISCOVERY_SERVER, "SDServer"     },
+	{ SDP_UUID_BROWSE_GROUP_DESCRIPTOR,  "BrwsGrpDesc"  },
+	{ SDP_UUID_PUBLIC_BROWSE_GROUP,      "PubBrwsGrp"   },
+	{ SDP_UUID_SERIAL_PORT,              "SP"           },
+	{ SDP_UUID_LAN_ACCESS_PPP,           "LAN"          },
+	{ SDP_UUID_DIALUP_NETWORKING,        "DUN"          },
+	{ SDP_UUID_IR_MC_SYNC,               "IRMCSync"     },
+	{ SDP_UUID_OBEX_OBJECT_PUSH,         "OBEXObjPush"  },
+	{ SDP_UUID_OBEX_FILE_TRANSFER,       "OBEXObjTrnsf" },
+	{ SDP_UUID_IR_MC_SYNC_COMMAND,       "IRMCSyncCmd"  },
+	{ SDP_UUID_HEADSET,                  "Headset"      },
+	{ SDP_UUID_CORDLESS_TELEPHONY,       "CordlessTel"  },
+	{ SDP_UUID_AUDIO_SOURCE,             "AudioSource"  }, /* A2DP */
+	{ SDP_UUID_AUDIO_SINK,               "AudioSink"    }, /* A2DP */
+	{ SDP_UUID_AV_REMOTE_TARGET,         "AVRemTarget"  }, /* AVRCP */
+	{ SDP_UUID_ADVANCED_AUDIO,           "AdvAudio"     }, /* A2DP */
+	{ SDP_UUID_AV_REMOTE,                "AVRemote"     }, /* AVRCP */
+	{ SDP_UUID_AV_REMOTE_CONTROLLER,     "AVRemCt"      }, /* AVRCP */
+	{ SDP_UUID_INTERCOM,                 "Intercom"     },
+	{ SDP_UUID_FAX,                      "Fax"          },
+	{ SDP_UUID_HEADSET_AUDIO_GATEWAY,    "Headset AG"   },
+	{ SDP_UUID_WAP,                      "WAP"          },
+	{ SDP_UUID_WAP_CLIENT,               "WAP Client"   },
+	{ SDP_UUID_PANU,                     "PANU"         }, /* PAN */
+	{ SDP_UUID_NAP,                      "NAP"          }, /* PAN */
+	{ SDP_UUID_GN,                       "GN"           }, /* PAN */
+	{ SDP_UUID_DIRECT_PRINTING,          "DirectPrint"  }, /* BPP */
+	{ SDP_UUID_REFERENCE_PRINTING,       "RefPrint"     }, /* BPP */
+	{ SDP_UUID_IMAGING,                  "Imaging"      }, /* BIP */
+	{ SDP_UUID_IMAGING_RESPONDER,        "ImagingResp"  }, /* BIP */
+	{ SDP_UUID_HANDSFREE,                "Handsfree"    },
+	{ SDP_UUID_HANDSFREE_AUDIO_GATEWAY,  "Handsfree AG" },
+	{ SDP_UUID_DIRECT_PRINTING_REF_OBJS, "RefObjsPrint" }, /* BPP */
+	{ SDP_UUID_REFLECTED_UI,             "ReflectedUI"  }, /* BPP */
+	{ SDP_UUID_BASIC_PRINTING,           "BasicPrint"   }, /* BPP */
+	{ SDP_UUID_PRINTING_STATUS,          "PrintStatus"  }, /* BPP */
+	{ SDP_UUID_HUMAN_INTERFACE_DEVICE,   "HID"          }, /* HID */
+	{ SDP_UUID_HARDCOPY_CABLE_REPLACE,   "HCRP"         }, /* HCRP */
+	{ SDP_UUID_HCR_PRINT,                "HCRPrint"     }, /* HCRP */
+	{ SDP_UUID_HCR_SCAN,                 "HCRScan"      }, /* HCRP */
+	{ SDP_UUID_COMMON_ISDN_ACCESS,       "CIP"          }, /* CIP */
+	{ SDP_UUID_UDI_MT,                   "UDI MT"       }, /* UDI */
+	{ SDP_UUID_UDI_TA,                   "UDI TA"       }, /* UDI */
+	{ SDP_UUID_AUDIO_VIDEO,              "AudioVideo"   }, /* VCP */
+	{ SDP_UUID_SIM_ACCESS,               "SAP"          }, /* SAP */
+	{ SDP_UUID_PHONEBOOK_ACCESS_PCE,     "PBAP PCE"     }, /* PBAP */
+	{ SDP_UUID_PHONEBOOK_ACCESS_PSE,     "PBAP PSE"     }, /* PBAP */
+	{ SDP_UUID_PHONEBOOK_ACCESS,         "PBAP"         }, /* PBAP */
+	{ SDP_UUID_PNP_INFORMATION,          "PNPInfo"      },
+	{ SDP_UUID_GENERIC_NETWORKING,       "Networking"   },
+	{ SDP_UUID_GENERIC_FILE_TRANSFER,    "FileTrnsf"    },
+	{ SDP_UUID_GENERIC_AUDIO,            "Audio"        },
+	{ SDP_UUID_GENERIC_TELEPHONY,        "Telephony"    },
+	{ SDP_UUID_UPNP_SERVICE,             "UPNP"         }, /* ESDP */
+	{ SDP_UUID_UPNP_IP_SERVICE,          "UPNP IP"      }, /* ESDP */
+	{ SDP_UUID_ESDP_UPNP_IP_PAN,         "UPNP PAN"     }, /* ESDP */
+	{ SDP_UUID_ESDP_UPNP_IP_LAP,         "UPNP LAP"     }, /* ESDP */
+	{ SDP_UUID_ESDP_UPNP_L2CAP,          "UPNP L2CAP"   }, /* ESDP */
+	{ SDP_UUID_VIDEO_SOURCE,             "VideoSource"  }, /* VDP */
+	{ SDP_UUID_VIDEO_SINK,               "VideoSink"    }, /* VDP */
+	{ SDP_UUID_VIDEO_DISTRIBUTION,       "VideoDist"    }, /* VDP */
+	{ SDP_UUID_APPLE_AGENT,              "AppleAgent"   },
+};
+
+#define SDP_UUID_NAM_LOOKUP_TABLE_SIZE \
+	(sizeof(sdp_uuid_nam_lookup_table)/sizeof(sdp_uuid_nam_lookup_table_t))
+
+/* AttrID name lookup table */
+typedef struct {
+	int   attr_id;
+	char* name;
+} sdp_attr_id_nam_lookup_table_t;
+
+static sdp_attr_id_nam_lookup_table_t sdp_attr_id_nam_lookup_table[] = {
+	{ SDP_ATTR_ID_SERVICE_RECORD_HANDLE,             "SrvRecHndl"         },
+	{ SDP_ATTR_ID_SERVICE_CLASS_ID_LIST,             "SrvClassIDList"     },
+	{ SDP_ATTR_ID_SERVICE_RECORD_STATE,              "SrvRecState"        },
+	{ SDP_ATTR_ID_SERVICE_SERVICE_ID,                "SrvID"              },
+	{ SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST,          "ProtocolDescList"   },
+	{ SDP_ATTR_ID_BROWSE_GROUP_LIST,                 "BrwGrpList"         },
+	{ SDP_ATTR_ID_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,   "LangBaseAttrIDList" },
+	{ SDP_ATTR_ID_SERVICE_INFO_TIME_TO_LIVE,         "SrvInfoTimeToLive"  },
+	{ SDP_ATTR_ID_SERVICE_AVAILABILITY,              "SrvAvail"           },
+	{ SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, "BTProfileDescList"  },
+	{ SDP_ATTR_ID_DOCUMENTATION_URL,                 "DocURL"             },
+	{ SDP_ATTR_ID_CLIENT_EXECUTABLE_URL,             "ClientExeURL"       },
+	{ SDP_ATTR_ID_ICON_URL,                          "IconURL"            },
+	{ SDP_ATTR_ID_ADDITIONAL_PROTOCOL_DESC_LISTS,    "AdditionalProtocolDescLists" },
+	{ SDP_ATTR_ID_SERVICE_NAME,                      "SrvName"            },
+	{ SDP_ATTR_ID_SERVICE_DESCRIPTION,               "SrvDesc"            },
+	{ SDP_ATTR_ID_PROVIDER_NAME,                     "ProviderName"       },
+	{ SDP_ATTR_ID_VERSION_NUMBER_LIST,               "VersionNumList"     },
+	{ SDP_ATTR_ID_GROUP_ID,                          "GrpID"              },
+	{ SDP_ATTR_ID_SERVICE_DATABASE_STATE,            "SrvDBState"         },
+	{ SDP_ATTR_ID_SERVICE_VERSION,                   "SrvVersion"         },
+	{ SDP_ATTR_ID_SECURITY_DESCRIPTION,              "SecurityDescription"}, /* PAN */
+	{ SDP_ATTR_ID_SUPPORTED_DATA_STORES_LIST,        "SuppDataStoresList" }, /* Synchronization */
+	{ SDP_ATTR_ID_SUPPORTED_FORMATS_LIST,            "SuppFormatsList"    }, /* OBEX Object Push */
+	{ SDP_ATTR_ID_NET_ACCESS_TYPE,                   "NetAccessType"      }, /* PAN */
+	{ SDP_ATTR_ID_MAX_NET_ACCESS_RATE,               "MaxNetAccessRate"   }, /* PAN */
+	{ SDP_ATTR_ID_IPV4_SUBNET,                       "IPv4Subnet"         }, /* PAN */
+	{ SDP_ATTR_ID_IPV6_SUBNET,                       "IPv6Subnet"         }, /* PAN */
+	{ SDP_ATTR_ID_SUPPORTED_CAPABILITIES,            "SuppCapabilities"   }, /* Imaging */
+	{ SDP_ATTR_ID_SUPPORTED_FEATURES,                "SuppFeatures"       }, /* Imaging and Hansfree */
+	{ SDP_ATTR_ID_SUPPORTED_FUNCTIONS,               "SuppFunctions"      }, /* Imaging */
+	{ SDP_ATTR_ID_TOTAL_IMAGING_DATA_CAPACITY,       "SuppTotalCapacity"  }, /* Imaging */
+	{ SDP_ATTR_ID_SUPPORTED_REPOSITORIES,            "SuppRepositories"   }, /* PBAP */
+};
+
+#define SDP_ATTR_ID_NAM_LOOKUP_TABLE_SIZE \
+	(sizeof(sdp_attr_id_nam_lookup_table)/sizeof(sdp_attr_id_nam_lookup_table_t))
+
+char* get_uuid_name(int uuid)
+{
+	unsigned int i;
+
+	for (i = 0; i < SDP_UUID_NAM_LOOKUP_TABLE_SIZE; i++) {
+		if (sdp_uuid_nam_lookup_table[i].uuid == uuid)
+			return sdp_uuid_nam_lookup_table[i].name;
+	}
+
+	return 0;
+}
+
+static inline char* get_attr_id_name(int attr_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < SDP_ATTR_ID_NAM_LOOKUP_TABLE_SIZE; i++)
+		if (sdp_attr_id_nam_lookup_table[i].attr_id == attr_id)
+			return sdp_attr_id_nam_lookup_table[i].name;
+	return 0;
+}
+
+static inline uint8_t parse_de_hdr(struct frame *frm, int *n)
+{
+	uint8_t de_hdr = p_get_u8(frm);
+	uint8_t de_type = de_hdr >> 3;
+	uint8_t siz_idx = de_hdr & 0x07;
+
+	/* Get the number of bytes */
+	if (sdp_siz_idx_lookup_table[siz_idx].addl_bits) {
+		switch(sdp_siz_idx_lookup_table[siz_idx].num_bytes) {
+		case 1:
+			*n = p_get_u8(frm); break;
+		case 2:
+			*n = p_get_u16(frm); break;
+		case 4:
+			*n = p_get_u32(frm); break;
+		case 8:
+			*n = p_get_u64(frm); break;
+		}
+	} else
+		*n = sdp_siz_idx_lookup_table[siz_idx].num_bytes;
+
+	return de_type;
+}
+
+static inline void print_int(uint8_t de_type, int level, int n, struct frame *frm, uint16_t *psm, uint8_t *channel)
+{
+	uint64_t val, val2;
+
+	switch(de_type) {
+	case SDP_DE_UINT:
+		printf(" uint");
+		break;
+	case SDP_DE_INT:
+		printf(" int");
+		break;
+	case SDP_DE_BOOL:
+		printf(" bool");
+		break;
+	}
+
+	switch(n) {
+	case 1: /* 8-bit */
+		val = p_get_u8(frm);
+		if (channel && de_type == SDP_DE_UINT)
+			if (*channel == 0)
+				*channel = val;
+		break;
+	case 2: /* 16-bit */
+		val = p_get_u16(frm);
+		if (psm && de_type == SDP_DE_UINT)
+			if (*psm == 0)
+				*psm = val;
+		break;
+	case 4: /* 32-bit */
+		val = p_get_u32(frm);
+		break;
+	case 8: /* 64-bit */
+		val = p_get_u64(frm);
+		break;
+	case 16:/* 128-bit */
+		p_get_u128(frm, &val, &val2);
+		printf(" 0x%jx", val2);
+		if (val < 0x1000000000000000LL)
+			printf("0");
+		printf("%jx", val);
+		return;
+	default: /* syntax error */
+		printf(" err");
+		frm->ptr += n;
+		frm->len -= n;
+		return;
+	}
+
+	printf(" 0x%jx", val);
+}
+
+static inline void print_uuid(int n, struct frame *frm, uint16_t *psm, uint8_t *channel)
+{
+	uint32_t uuid = 0;
+	char* s;
+	int i;
+
+	switch(n) {
+	case 2: /* 16-bit UUID */
+		uuid = p_get_u16(frm);
+		s = "uuid-16";
+		break;
+	case 4: /* 32_bit UUID */
+		uuid = p_get_u32(frm);
+		s = "uuid-32";
+		break;
+	case 16: /* 128-bit UUID */
+		printf(" uuid-128 ");
+		for (i = 0; i < 16; i++) {
+			printf("%02x", ((unsigned char *) frm->ptr)[i]);
+			if (i == 3 || i == 5 || i == 7 || i == 9)
+				printf("-");
+		}
+		frm->ptr += 16;
+		frm->len -= 16;
+		return;
+	default: /* syntax error */
+		printf(" *err*");
+		frm->ptr += n;
+		frm->len -= n;
+		return;
+	}
+
+	if (psm && *psm > 0 && *psm != 0xffff) {
+		set_proto(frm->handle, *psm, 0, uuid);
+		*psm = 0xffff;
+	}
+
+	if (channel && *channel > 0 && *channel != 0xff) {
+		set_proto(frm->handle, *psm, *channel, uuid);
+		*channel = 0xff;
+	}
+
+	printf(" %s 0x%04x", s, uuid);
+	if ((s = get_uuid_name(uuid)))
+		printf(" (%s)", s);
+}
+
+static inline void print_string(int n, struct frame *frm, const char *name)
+{
+	int i, hex = 0;
+
+	for (i = 0; i < n; i++) {
+		if (i == (n - 1) && ((char *) frm->ptr)[i] == '\0')
+			break;
+
+		if (!isprint(((char *) frm->ptr)[i])) {
+			hex = 1;
+			break;
+		}
+	}
+
+	printf(" %s", name);
+	if (hex) {
+		for (i = 0; i < n; i++)
+			printf(" %02x", ((unsigned char *) frm->ptr)[i]);
+	} else {
+		printf(" \"");
+		for (i = 0; i < n; i++)
+			printf("%c", ((char *) frm->ptr)[i]);
+		printf("\"");
+	}
+
+	frm->ptr += n;
+	frm->len -= n;
+}
+
+static inline void print_de(int, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel);
+
+static inline void print_des(uint8_t de_type, int level, int n, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel)
+{
+	int len = frm->len;
+	while (len - (int) frm->len < n && (int) frm->len > 0)
+		print_de(level, frm, split, psm, channel);
+}
+
+static inline void print_de(int level, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel)
+{
+	int n = 0;
+	uint8_t de_type = parse_de_hdr(frm, &n);
+
+	switch (de_type) {
+	case SDP_DE_NULL:
+		printf(" null");
+		break;
+	case SDP_DE_UINT:
+	case SDP_DE_INT:
+	case SDP_DE_BOOL:
+		print_int(de_type, level, n, frm, psm, channel);
+		break;
+	case SDP_DE_UUID:
+		if (split) {
+			/* Split output by uuids.
+			 * Used for printing Protocol Desc List */
+			if (*split) {
+				printf("\n");
+				p_indent(level, NULL);
+			}
+			++*split;
+		}
+		print_uuid(n, frm, psm, channel);
+		break;
+	case SDP_DE_URL:
+	case SDP_DE_STRING:
+		print_string(n, frm, de_type == SDP_DE_URL? "url": "str");
+		break;
+	case SDP_DE_SEQ:
+		printf(" <");
+		print_des(de_type, level, n, frm, split, psm, channel);
+		printf(" >");
+		break;
+	case SDP_DE_ALT:
+		printf(" [");
+		print_des(de_type, level, n, frm, split, psm, channel);
+		printf(" ]");
+		break;
+	}
+}
+
+static inline void print_srv_srch_pat(int level, struct frame *frm)
+{
+	int len, n1 = 0, n2 = 0;
+
+	p_indent(level, frm);
+	printf("pat");
+
+	if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) {
+		len = frm->len;
+		while (len - (int) frm->len < n1 && (int) frm->len > 0) {
+			if (parse_de_hdr(frm, &n2) == SDP_DE_UUID) {
+				print_uuid(n2, frm, NULL, NULL);
+			} else {
+				printf("\nERROR: Unexpected syntax (UUID)\n");
+				raw_dump(level, frm);
+			}
+		}
+		printf("\n");
+	} else {
+		printf("\nERROR: Unexpected syntax (SEQ)\n");
+		raw_dump(level, frm);
+	}
+}
+
+static inline void print_attr_id_list(int level, struct frame *frm)
+{
+	uint16_t attr_id;
+	uint32_t attr_id_range;
+	int len, n1 = 0, n2 = 0;
+
+	p_indent(level, frm);
+	printf("aid(s)");
+
+	if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) {
+		len = frm->len;
+		while (len - (int) frm->len < n1 && (int) frm->len > 0) {
+			/* Print AttributeID */
+			if (parse_de_hdr(frm, &n2) == SDP_DE_UINT) {
+				char *name;
+				switch(n2) {
+				case 2:
+					attr_id = p_get_u16(frm);
+					name = get_attr_id_name(attr_id);
+					if (!name)
+						name = "unknown";
+					printf(" 0x%04x (%s)", attr_id, name);
+					break;
+				case 4:
+					attr_id_range = p_get_u32(frm);
+					printf(" 0x%04x - 0x%04x",
+							(attr_id_range >> 16),
+							(attr_id_range & 0xFFFF));
+					break;
+				}
+			} else {
+				printf("\nERROR: Unexpected syntax\n");
+				raw_dump(level, frm);
+			}
+		}
+		printf("\n");
+	} else {
+		printf("\nERROR: Unexpected syntax\n");
+		raw_dump(level, frm);
+	}
+}
+
+static inline void print_attr_list(int level, struct frame *frm)
+{
+	uint16_t attr_id, psm;
+	uint8_t channel;
+	int len, split, n1 = 0, n2 = 0;
+
+	if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) {
+		len = frm->len;
+		while (len - (int) frm->len < n1 && (int) frm->len > 0) {
+			/* Print AttributeID */
+			if (parse_de_hdr(frm, &n2) == SDP_DE_UINT && n2 == sizeof(attr_id)) {
+				char *name;
+				attr_id = p_get_u16(frm);
+				p_indent(level, 0);
+				name = get_attr_id_name(attr_id);
+				if (!name)
+					name = "unknown";
+				printf("aid 0x%04x (%s)\n", attr_id, name);
+				split = (attr_id != SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST);
+				psm = 0;
+				channel = 0;
+
+				/* Print AttributeValue */
+				p_indent(level + 1, 0);
+				print_de(level + 1, frm, split ? NULL: &split,
+					attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST ? &psm : NULL,
+					attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST ? &channel : NULL);
+				printf("\n");
+			} else {
+				printf("\nERROR: Unexpected syntax\n");
+				raw_dump(level, frm);
+				break;
+			}
+		}
+	} else {
+		printf("\nERROR: Unexpected syntax\n");
+		raw_dump(level, frm);
+	}
+}
+
+static inline void print_attr_lists(int level, struct frame *frm)
+{
+	int n = 0, cnt = 0;
+	int count = frm->len;
+
+	if (parse_de_hdr(frm, &n) == SDP_DE_SEQ) {
+		while (count - (int) frm->len < n && (int) frm->len > 0) {
+			p_indent(level, 0);
+			printf("record #%d\n", cnt++);
+			print_attr_list(level + 2, frm);
+		}
+	} else {
+		printf("\nERROR: Unexpected syntax\n");
+		raw_dump(level, frm);
+	}
+}
+
+static inline void print_cont_state(int level, unsigned char *buf)
+{
+	uint8_t cont = buf[0];
+	int i;
+
+	p_indent(level, 0);
+	printf("cont");
+	for (i = 0; i < cont + 1; i++)
+		printf(" %2.2X", buf[i]);
+	printf("\n");
+}
+
+static char *pid2str(uint8_t pid)
+{
+	switch (pid) {
+	case SDP_ERROR_RSP:
+		return "Error Rsp";
+	case SDP_SERVICE_SEARCH_REQ:
+		return "SS Req";
+	case SDP_SERVICE_SEARCH_RSP:
+		return "SS Rsp";
+	case SDP_SERVICE_ATTR_REQ:
+		return "SA Req";
+	case SDP_SERVICE_ATTR_RSP:
+		return "SA Rsp";
+	case SDP_SERVICE_SEARCH_ATTR_REQ:
+		return "SSA Req";
+	case SDP_SERVICE_SEARCH_ATTR_RSP:
+		return "SSA Rsp";
+	default:
+		return "Unknown";
+	}
+}
+
+#define FRAME_TABLE_SIZE 10
+
+static struct frame frame_table[FRAME_TABLE_SIZE];
+
+static int frame_add(struct frame *frm, int count)
+{
+	register struct frame *fr;
+	register unsigned char *data;
+	register int i, len = 0, pos = -1;
+
+	for (i = 0; i < FRAME_TABLE_SIZE; i++) {
+		if (frame_table[i].handle == frm->handle &&
+				frame_table[i].cid == frm->cid) {
+			pos = i;
+			len = frame_table[i].data_len;
+			break;
+		}
+		if (pos < 0 && !frame_table[i].handle)
+			pos = i;
+	}
+
+	if (pos < 0 || count <= 0)
+		return -EIO;
+
+	data = malloc(len + count);
+	if (!data)
+		return -ENOMEM;
+
+	fr = &frame_table[pos];
+
+	if (len > 0) {
+		memcpy(data, fr->data, len);
+		memcpy(data + len, frm->ptr, count);
+	} else
+		memcpy(data, frm->ptr, count);
+
+	if (fr->data)
+		free(fr->data);
+
+	fr->data       = data;
+	fr->data_len   = len + count;
+	fr->len        = fr->data_len;
+	fr->ptr        = fr->data;
+	fr->dev_id     = frm->dev_id;
+	fr->in         = frm->in;
+	fr->ts         = frm->ts;
+	fr->handle     = frm->handle;
+	fr->cid        = frm->cid;
+	fr->num        = frm->num;
+	fr->channel    = frm->channel;
+	fr->pppdump_fd = frm->pppdump_fd;
+	fr->audio_fd   = frm->audio_fd;
+
+	return pos;
+}
+
+static struct frame *frame_get(struct frame *frm, int count)
+{
+	register int pos;
+
+	pos = frame_add(frm, count);
+	if (pos < 0)
+		return frm;
+
+	frame_table[pos].handle = 0;
+
+	return &frame_table[pos];
+}
+
+void sdp_dump(int level, struct frame *frm)
+{
+	sdp_pdu_hdr *hdr = frm->ptr;
+	uint16_t tid = ntohs(hdr->tid);
+	uint16_t len = ntohs(hdr->len);
+	uint16_t total, count;
+	uint8_t cont;
+
+	frm->ptr += SDP_PDU_HDR_SIZE;
+	frm->len -= SDP_PDU_HDR_SIZE;
+
+	p_indent(level, frm);
+	printf("SDP %s: tid 0x%x len 0x%x\n", pid2str(hdr->pid), tid, len);
+
+	switch (hdr->pid) {
+	case SDP_ERROR_RSP:
+		p_indent(level + 1, frm);
+		printf("code 0x%x info ", p_get_u16(frm));
+		if (frm->len > 0)
+			hex_dump(0, frm, frm->len);
+		else
+			printf("none\n");
+		break;
+
+	case SDP_SERVICE_SEARCH_REQ:
+		/* Parse ServiceSearchPattern */
+		print_srv_srch_pat(level + 1, frm);
+
+		/* Parse MaximumServiceRecordCount */
+		p_indent(level + 1, frm);
+		printf("max %d\n", p_get_u16(frm));
+
+		/* Parse ContinuationState */
+		print_cont_state(level + 1, frm->ptr);
+		break;
+
+	case SDP_SERVICE_SEARCH_RSP:
+		/* Parse TotalServiceRecordCount */
+		total = p_get_u16(frm);
+
+		/* Parse CurrentServiceRecordCount */
+		count = p_get_u16(frm);
+		p_indent(level + 1, frm);
+		if (count < total)
+			printf("count %d of %d\n", count, total);
+		else
+			printf("count %d\n", count);
+
+		/* Parse service record handle(s) */
+		if (count > 0) {
+			int i;
+			p_indent(level + 1, frm);
+			printf("handle%s", count > 1 ? "s" : "");
+			for (i = 0; i < count; i++)
+				printf(" 0x%x", p_get_u32(frm));
+			printf("\n");
+		}
+
+		/* Parse ContinuationState */
+		print_cont_state(level + 1, frm->ptr);
+		break;
+
+	case SDP_SERVICE_ATTR_REQ:
+		/* Parse ServiceRecordHandle */
+		p_indent(level + 1, frm);
+		printf("handle 0x%x\n", p_get_u32(frm));
+
+		/* Parse MaximumAttributeByteCount */
+		p_indent(level + 1, frm);
+		printf("max %d\n", p_get_u16(frm));
+
+		/* Parse ServiceSearchPattern */
+		print_attr_id_list(level + 1, frm);
+
+		/* Parse ContinuationState */
+		print_cont_state(level + 1, frm->ptr);
+		break;
+
+	case SDP_SERVICE_ATTR_RSP:
+		/* Parse AttributeByteCount */
+		count = p_get_u16(frm);
+		p_indent(level + 1, frm);
+		printf("count %d\n", count);
+
+		/* Parse ContinuationState */
+		cont = *(unsigned char *)(frm->ptr + count);
+
+		if (cont == 0) {
+			/* Parse AttributeList */
+			print_attr_list(level + 1, frame_get(frm, count));
+		} else
+			frame_add(frm, count);
+
+		print_cont_state(level + 1, frm->ptr + count);
+		break;
+
+	case SDP_SERVICE_SEARCH_ATTR_REQ:
+		/* Parse ServiceSearchPattern */
+		print_srv_srch_pat(level + 1, frm);
+
+		/* Parse MaximumAttributeByteCount */
+		p_indent(level + 1, frm);
+		printf("max %d\n", p_get_u16(frm));
+
+		/* Parse AttributeList */
+		print_attr_id_list(level + 1, frm);
+
+		/* Parse ContinuationState */
+		print_cont_state(level + 1, frm->ptr);
+		break;
+
+	case SDP_SERVICE_SEARCH_ATTR_RSP:
+		/* Parse AttributeByteCount */
+		count = p_get_u16(frm);
+		p_indent(level + 1, frm);
+		printf("count %d\n", count);
+
+		/* Parse ContinuationState */
+		cont = *(unsigned char *)(frm->ptr + count);
+
+		if (cont == 0) {
+			/* Parse AttributeLists */
+			print_attr_lists(level + 1, frame_get(frm, count));
+		} else
+			frame_add(frm, count);
+
+		print_cont_state(level + 1, frm->ptr + count);
+		break;
+
+	default:
+		raw_dump(level + 1, frm);
+		break;
+	}
+}
diff --git a/bluez/repo/tools/parser/sdp.h b/bluez/repo/tools/parser/sdp.h
new file mode 100644
index 0000000..ed55a23
--- /dev/null
+++ b/bluez/repo/tools/parser/sdp.h
@@ -0,0 +1,161 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Ricky Yuen <ryuen@qualcomm.com>
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SDP_H
+#define __SDP_H
+
+/* Bluetooth assigned UUIDs for protocols */
+#define SDP_UUID_SDP                                   0x0001
+#define SDP_UUID_UDP                                   0x0002
+#define SDP_UUID_RFCOMM                                0x0003
+#define SDP_UUID_TCP                                   0x0004
+#define SDP_UUID_TCS_BIN                               0x0005
+#define SDP_UUID_TCS_AT                                0x0006
+#define SDP_UUID_OBEX                                  0x0008
+#define SDP_UUID_IP                                    0x0009
+#define SDP_UUID_FTP                                   0x000A
+#define SDP_UUID_HTTP                                  0x000C
+#define SDP_UUID_WSP                                   0x000E
+#define SDP_UUID_BNEP                                  0x000F /* PAN */
+#define SDP_UUID_HIDP                                  0x0011 /* HID */
+#define SDP_UUID_HARDCOPY_CONTROL_CHANNEL              0x0012 /* HCRP */
+#define SDP_UUID_HARDCOPY_DATA_CHANNEL                 0x0014 /* HCRP */
+#define SDP_UUID_HARDCOPY_NOTIFICATION                 0x0016 /* HCRP */
+#define SDP_UUID_AVCTP                                 0x0017 /* AVCTP */
+#define SDP_UUID_AVDTP                                 0x0019 /* AVDTP */
+#define SDP_UUID_CMTP                                  0x001B /* CIP */
+#define SDP_UUID_UDI_C_PLANE                           0x001D /* UDI */
+#define SDP_UUID_L2CAP                                 0x0100
+
+/* Bluetooth assigned UUIDs for Service Classes */
+#define SDP_UUID_SERVICE_DISCOVERY_SERVER              0x1000
+#define SDP_UUID_BROWSE_GROUP_DESCRIPTOR               0x1001
+#define SDP_UUID_PUBLIC_BROWSE_GROUP                   0x1002
+#define SDP_UUID_SERIAL_PORT                           0x1101
+#define SDP_UUID_LAN_ACCESS_PPP                        0x1102
+#define SDP_UUID_DIALUP_NETWORKING                     0x1103
+#define SDP_UUID_IR_MC_SYNC                            0x1104
+#define SDP_UUID_OBEX_OBJECT_PUSH                      0x1105
+#define SDP_UUID_OBEX_FILE_TRANSFER                    0x1106
+#define SDP_UUID_IR_MC_SYNC_COMMAND                    0x1107
+#define SDP_UUID_HEADSET                               0x1108
+#define SDP_UUID_CORDLESS_TELEPHONY                    0x1109
+#define SDP_UUID_AUDIO_SOURCE                          0x110a /* A2DP */
+#define SDP_UUID_AUDIO_SINK                            0x110b /* A2DP */
+#define SDP_UUID_AV_REMOTE_TARGET                      0x110c /* AVRCP */
+#define SDP_UUID_ADVANCED_AUDIO                        0x110d /* A2DP */
+#define SDP_UUID_AV_REMOTE                             0x110e /* AVRCP */
+#define SDP_UUID_AV_REMOTE_CONTROLLER                  0x110f /* AVRCP */
+#define SDP_UUID_INTERCOM                              0x1110
+#define SDP_UUID_FAX                                   0x1111
+#define SDP_UUID_HEADSET_AUDIO_GATEWAY                 0x1112
+#define SDP_UUID_WAP                                   0x1113
+#define SDP_UUID_WAP_CLIENT                            0x1114
+#define SDP_UUID_PANU                                  0x1115 /* PAN */
+#define SDP_UUID_NAP                                   0x1116 /* PAN */
+#define SDP_UUID_GN                                    0x1117 /* PAN */
+#define SDP_UUID_DIRECT_PRINTING                       0x1118 /* BPP */
+#define SDP_UUID_REFERENCE_PRINTING                    0x1119 /* BPP */
+#define SDP_UUID_IMAGING                               0x111a /* BIP */
+#define SDP_UUID_IMAGING_RESPONDER                     0x111b /* BIP */
+#define SDP_UUID_IMAGING_AUTOMATIC_ARCHIVE             0x111c /* BIP */
+#define SDP_UUID_IMAGING_REFERENCED_OBJECTS            0x111d /* BIP */
+#define SDP_UUID_HANDSFREE                             0x111e
+#define SDP_UUID_HANDSFREE_AUDIO_GATEWAY               0x111f
+#define SDP_UUID_DIRECT_PRINTING_REF_OBJS              0x1120 /* BPP */
+#define SDP_UUID_DIRECT_PRINTING_REFERENCE_OBJECTS     0x1120 /* BPP */
+#define SDP_UUID_REFLECTED_UI                          0x1121 /* BPP */
+#define SDP_UUID_BASIC_PRINTING                        0x1122 /* BPP */
+#define SDP_UUID_PRINTING_STATUS                       0x1123 /* BPP */
+#define SDP_UUID_HUMAN_INTERFACE_DEVICE                0x1124 /* HID */
+#define SDP_UUID_HARDCOPY_CABLE_REPLACE                0x1125 /* HCRP */
+#define SDP_UUID_HCR_PRINT                             0x1126 /* HCRP */
+#define SDP_UUID_HCR_SCAN                              0x1127 /* HCRP */
+#define SDP_UUID_COMMON_ISDN_ACCESS                    0x1128 /* CIP */
+#define SDP_UUID_UDI_MT                                0x112a /* UDI */
+#define SDP_UUID_UDI_TA                                0x112b /* UDI */
+#define SDP_UUID_AUDIO_VIDEO                           0x112c /* VCP */
+#define SDP_UUID_SIM_ACCESS                            0x112d /* SAP */
+#define SDP_UUID_PHONEBOOK_ACCESS_PCE                  0x112e /* PBAP */
+#define SDP_UUID_PHONEBOOK_ACCESS_PSE                  0x112f /* PBAP */
+#define SDP_UUID_PHONEBOOK_ACCESS                      0x1130 /* PBAP */
+#define SDP_UUID_PNP_INFORMATION                       0x1200
+#define SDP_UUID_GENERIC_NETWORKING                    0x1201
+#define SDP_UUID_GENERIC_FILE_TRANSFER                 0x1202
+#define SDP_UUID_GENERIC_AUDIO                         0x1203
+#define SDP_UUID_GENERIC_TELEPHONY                     0x1204
+#define SDP_UUID_UPNP_SERVICE                          0x1205 /* ESDP */
+#define SDP_UUID_UPNP_IP_SERVICE                       0x1206 /* ESDP */
+#define SDP_UUID_ESDP_UPNP_IP_PAN                      0x1300 /* ESDP */
+#define SDP_UUID_ESDP_UPNP_IP_LAP                      0x1301 /* ESDP */
+#define SDP_UUID_ESDP_UPNP_L2CAP                       0x1302 /* ESDP */
+#define SDP_UUID_VIDEO_SOURCE                          0x1303 /* VDP */
+#define SDP_UUID_VIDEO_SINK                            0x1304 /* VDP */
+#define SDP_UUID_VIDEO_DISTRIBUTION                    0x1305 /* VDP */
+#define SDP_UUID_APPLE_AGENT                           0x2112
+
+/* Bluetooth assigned numbers for Attribute IDs */
+#define SDP_ATTR_ID_SERVICE_RECORD_HANDLE              0x0000
+#define SDP_ATTR_ID_SERVICE_CLASS_ID_LIST              0x0001
+#define SDP_ATTR_ID_SERVICE_RECORD_STATE               0x0002
+#define SDP_ATTR_ID_SERVICE_SERVICE_ID                 0x0003
+#define SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST           0x0004
+#define SDP_ATTR_ID_BROWSE_GROUP_LIST                  0x0005
+#define SDP_ATTR_ID_LANGUAGE_BASE_ATTRIBUTE_ID_LIST    0x0006
+#define SDP_ATTR_ID_SERVICE_INFO_TIME_TO_LIVE          0x0007
+#define SDP_ATTR_ID_SERVICE_AVAILABILITY               0x0008
+#define SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST  0x0009
+#define SDP_ATTR_ID_DOCUMENTATION_URL                  0x000A
+#define SDP_ATTR_ID_CLIENT_EXECUTABLE_URL              0x000B
+#define SDP_ATTR_ID_ICON_URL                           0x000C
+#define SDP_ATTR_ID_ADDITIONAL_PROTOCOL_DESC_LISTS     0x000D
+#define SDP_ATTR_ID_SERVICE_NAME                       0x0100
+#define SDP_ATTR_ID_SERVICE_DESCRIPTION                0x0101
+#define SDP_ATTR_ID_PROVIDER_NAME                      0x0102
+#define SDP_ATTR_ID_VERSION_NUMBER_LIST                0x0200
+#define SDP_ATTR_ID_GROUP_ID                           0x0200
+#define SDP_ATTR_ID_SERVICE_DATABASE_STATE             0x0201
+#define SDP_ATTR_ID_SERVICE_VERSION                    0x0300
+
+#define SDP_ATTR_ID_EXTERNAL_NETWORK                   0x0301 /* Cordless Telephony */
+#define SDP_ATTR_ID_SUPPORTED_DATA_STORES_LIST         0x0301 /* Synchronization */
+#define SDP_ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL        0x0302 /* GAP */
+#define SDP_ATTR_ID_SUPPORTED_FORMATS_LIST             0x0303 /* OBEX Object Push */
+#define SDP_ATTR_ID_FAX_CLASS_1_SUPPORT                0x0302 /* Fax */
+#define SDP_ATTR_ID_FAX_CLASS_2_0_SUPPORT              0x0303
+#define SDP_ATTR_ID_FAX_CLASS_2_SUPPORT                0x0304
+#define SDP_ATTR_ID_AUDIO_FEEDBACK_SUPPORT             0x0305
+#define SDP_ATTR_ID_SECURITY_DESCRIPTION               0x030a /* PAN */
+#define SDP_ATTR_ID_NET_ACCESS_TYPE                    0x030b /* PAN */
+#define SDP_ATTR_ID_MAX_NET_ACCESS_RATE                0x030c /* PAN */
+#define SDP_ATTR_ID_IPV4_SUBNET                        0x030d /* PAN */
+#define SDP_ATTR_ID_IPV6_SUBNET                        0x030e /* PAN */
+
+#define SDP_ATTR_ID_SUPPORTED_CAPABILITIES             0x0310 /* Imaging */
+#define SDP_ATTR_ID_SUPPORTED_FEATURES                 0x0311 /* Imaging and Hansfree */
+#define SDP_ATTR_ID_SUPPORTED_FUNCTIONS                0x0312 /* Imaging */
+#define SDP_ATTR_ID_TOTAL_IMAGING_DATA_CAPACITY        0x0313 /* Imaging */
+#define SDP_ATTR_ID_SUPPORTED_REPOSITORIES             0x0314 /* PBAP */
+
+#endif /* __SDP_H */
diff --git a/bluez/repo/tools/parser/smp.c b/bluez/repo/tools/parser/smp.c
new file mode 100644
index 0000000..97f02fa
--- /dev/null
+++ b/bluez/repo/tools/parser/smp.c
@@ -0,0 +1,336 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation.
+ *
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser.h"
+
+/* SMP command codes */
+#define SMP_CMD_PAIRING_REQ	0x01
+#define SMP_CMD_PAIRING_RESP	0x02
+#define SMP_CMD_PAIRING_CONFIRM	0x03
+#define SMP_CMD_PAIRING_RANDOM	0x04
+#define SMP_CMD_PAIRING_FAILED	0x05
+#define SMP_CMD_ENCRYPT_INFO	0x06
+#define SMP_CMD_MASTER_IDENT	0x07
+#define SMP_CMD_IDENT_INFO	0X08
+#define SMP_CMD_IDENT_ADDR_INFO	0x09
+#define SMP_CMD_SIGN_INFO	0x0a
+#define SMP_CMD_SECURITY_REQ	0x0b
+
+/* IO Capabilities values */
+#define SMP_IO_DISPLAY_ONLY	0x00
+#define SMP_IO_DISPLAY_YESNO	0x01
+#define SMP_IO_KEYBOARD_ONLY	0x02
+#define SMP_IO_NO_INPUT_OUTPUT	0x03
+#define SMP_IO_KEYBOARD_DISPLAY	0x04
+
+/* OOB Data Present Values */
+#define SMP_OOB_NOT_PRESENT	0x00
+#define SMP_OOB_PRESENT		0x01
+
+#define SMP_DIST_ENC_KEY	0x01
+#define SMP_DIST_ID_KEY		0x02
+#define SMP_DIST_SIGN		0x04
+
+#define SMP_AUTH_NONE		0x00
+#define SMP_AUTH_BONDING	0x01
+#define SMP_AUTH_MITM		0x04
+
+#define SMP_REASON_PASSKEY_ENTRY_FAILED		0x01
+#define SMP_REASON_OOB_NOT_AVAIL		0x02
+#define SMP_REASON_AUTH_REQUIREMENTS		0x03
+#define SMP_REASON_CONFIRM_FAILED		0x04
+#define SMP_REASON_PAIRING_NOTSUPP		0x05
+#define SMP_REASON_ENC_KEY_SIZE			0x06
+#define SMP_REASON_CMD_NOTSUPP			0x07
+#define SMP_REASON_UNSPECIFIED			0x08
+#define SMP_REASON_REPEATED_ATTEMPTS		0x09
+
+static const char *smpcmd2str(uint8_t cmd)
+{
+	switch (cmd) {
+	case SMP_CMD_PAIRING_REQ:
+		return "Pairing Request";
+	case SMP_CMD_PAIRING_RESP:
+		return "Pairing Response";
+	case SMP_CMD_PAIRING_CONFIRM:
+		return "Pairing Confirm";
+	case SMP_CMD_PAIRING_RANDOM:
+		return "Pairing Random";
+	case SMP_CMD_PAIRING_FAILED:
+		return "Pairing Failed";
+	case SMP_CMD_ENCRYPT_INFO:
+		return "Encryption Information";
+	case SMP_CMD_MASTER_IDENT:
+		return "Master Identification";
+	case SMP_CMD_IDENT_INFO:
+		return "Identity Information";
+	case SMP_CMD_IDENT_ADDR_INFO:
+		return "Identity Address Information";
+	case SMP_CMD_SIGN_INFO:
+		return "Signing Information";
+	case SMP_CMD_SECURITY_REQ:
+		return "Security Request";
+	default:
+		return "Unknown";
+	}
+}
+
+static const char *smpio2str(uint8_t cap)
+{
+	switch(cap) {
+	case SMP_IO_DISPLAY_ONLY:
+		return "DisplayOnly";
+	case SMP_IO_DISPLAY_YESNO:
+		return "DisplayYesNo";
+	case SMP_IO_KEYBOARD_ONLY:
+		return "KeyboardOnly";
+	case SMP_IO_NO_INPUT_OUTPUT:
+		return "NoInputNoOutput";
+	case SMP_IO_KEYBOARD_DISPLAY:
+		return "KeyboardDisplay";
+	default:
+		return "Unkown";
+	}
+}
+
+static const char *smpreason2str(uint8_t reason)
+{
+	switch (reason) {
+	case SMP_REASON_PASSKEY_ENTRY_FAILED:
+		return "Passkey Entry Failed";
+	case SMP_REASON_OOB_NOT_AVAIL:
+		return "OOB Not Available";
+	case SMP_REASON_AUTH_REQUIREMENTS:
+		return "Authentication Requirements";
+	case SMP_REASON_CONFIRM_FAILED:
+		return "Confirm Value Failed";
+	case SMP_REASON_PAIRING_NOTSUPP:
+		return "Pairing Not Supported";
+	case SMP_REASON_ENC_KEY_SIZE:
+		return "Encryption Key Size";
+	case SMP_REASON_CMD_NOTSUPP:
+		return "Command Not Supported";
+	case SMP_REASON_UNSPECIFIED:
+		return "Unspecified Reason";
+	case SMP_REASON_REPEATED_ATTEMPTS:
+		return "Repeated Attempts";
+	default:
+		return "Unkown";
+	}
+}
+
+static void smp_cmd_pairing_dump(int level, struct frame *frm)
+{
+	uint8_t cap = p_get_u8(frm);
+	uint8_t oob = p_get_u8(frm);
+	uint8_t auth = p_get_u8(frm);
+	uint8_t key_size = p_get_u8(frm);
+	uint8_t int_dist = p_get_u8(frm);
+	uint8_t resp_dist = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("capability 0x%2.2x oob 0x%2.2x auth req 0x%2.2x\n", cap, oob,
+									auth);
+
+	p_indent(level , frm);
+	printf("max key size 0x%2.2x init key dist 0x%2.2x "
+		"resp key dist 0x%2.2x\n", key_size, int_dist, resp_dist);
+
+	p_indent(level , frm);
+	printf("Capability: %s (OOB data %s)\n", smpio2str(cap),
+				oob == 0x00 ? "not present" : "available");
+
+	p_indent(level , frm);
+	printf("Authentication: %s (%s)\n",
+			auth & SMP_AUTH_BONDING ? "Bonding" : "No Bonding",
+			auth & SMP_AUTH_MITM ? "MITM Protection" :
+			"No MITM Protection");
+
+	p_indent(level , frm);
+	printf("Initiator Key Distribution:  %s %s %s\n",
+			int_dist & SMP_DIST_ENC_KEY ? "LTK" : "",
+			int_dist & SMP_DIST_ID_KEY ? "IRK" : "",
+			int_dist & SMP_DIST_SIGN ? "CSRK" : "");
+
+	p_indent(level , frm);
+	printf("Responder Key Distribution:  %s %s %s\n",
+			resp_dist & SMP_DIST_ENC_KEY ? "LTK" : "",
+			resp_dist & SMP_DIST_ID_KEY ? "IRK" : "",
+			resp_dist & SMP_DIST_SIGN ? "CSRK" : "");
+}
+
+static void smp_cmd_pairing_confirm_dump(int level, struct frame *frm)
+{
+	int i;
+
+	p_indent(level, frm);
+	printf("key ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_pairing_random_dump(int level, struct frame *frm)
+{
+	int i;
+
+	p_indent(level, frm);
+	printf("random ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_pairing_failed_dump(int level, struct frame *frm)
+{
+	uint8_t reason = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("reason 0x%2.2x\n", reason);
+
+	p_indent(level, frm);
+	printf("Reason %s\n", smpreason2str(reason));
+}
+
+static void smp_cmd_encrypt_info_dump(int level, struct frame *frm)
+{
+	int i;
+
+	p_indent(level, frm);
+	printf("LTK ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_master_ident_dump(int level, struct frame *frm)
+{
+	uint16_t ediv = btohs(htons(p_get_u16(frm)));
+	int i;
+
+	p_indent(level, frm);
+	printf("EDIV 0x%4.4x ", ediv);
+
+	printf("Rand 0x");
+	for (i = 0; i < 8; i++)
+		printf("%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_ident_info_dump(int level, struct frame *frm)
+{
+	int i;
+
+	p_indent(level, frm);
+	printf("IRK ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_ident_addr_info_dump(int level, struct frame *frm)
+{
+	uint8_t type = p_get_u8(frm);
+	char addr[18];
+
+	p_indent(level, frm);
+	p_ba2str((bdaddr_t *) frm, addr);
+	printf("bdaddr %s (%s)\n", addr, type == 0x00 ? "Public" : "Random");
+}
+
+static void smp_cmd_sign_info_dump(int level, struct frame *frm)
+{
+	int i;
+
+	p_indent(level, frm);
+	printf("CSRK ");
+	for (i = 0; i < 16; i++)
+		printf("%2.2x", p_get_u8(frm));
+	printf("\n");
+}
+
+static void smp_cmd_security_req_dump(int level, struct frame *frm)
+{
+	uint8_t auth = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("auth req 0x%2.2x\n", auth);
+}
+
+void smp_dump(int level, struct frame *frm)
+{
+	uint8_t cmd;
+
+	cmd = p_get_u8(frm);
+
+	p_indent(level, frm);
+	printf("SMP: %s (0x%.2x)\n", smpcmd2str(cmd), cmd);
+
+	switch (cmd) {
+	case SMP_CMD_PAIRING_REQ:
+		smp_cmd_pairing_dump(level + 1, frm);
+		break;
+	case SMP_CMD_PAIRING_RESP:
+		smp_cmd_pairing_dump(level + 1, frm);
+		break;
+	case SMP_CMD_PAIRING_CONFIRM:
+		smp_cmd_pairing_confirm_dump(level + 1, frm);
+		break;
+	case SMP_CMD_PAIRING_RANDOM:
+		smp_cmd_pairing_random_dump(level + 1, frm);
+		break;
+	case SMP_CMD_PAIRING_FAILED:
+		smp_cmd_pairing_failed_dump(level + 1, frm);
+		break;
+	case SMP_CMD_ENCRYPT_INFO:
+		smp_cmd_encrypt_info_dump(level + 1, frm);
+		break;
+	case SMP_CMD_MASTER_IDENT:
+		smp_cmd_master_ident_dump(level + 1, frm);
+		break;
+	case SMP_CMD_IDENT_INFO:
+		smp_cmd_ident_info_dump(level + 1, frm);
+		break;
+	case SMP_CMD_IDENT_ADDR_INFO:
+		smp_cmd_ident_addr_info_dump(level + 1, frm);
+		break;
+	case SMP_CMD_SIGN_INFO:
+		smp_cmd_sign_info_dump(level + 1, frm);
+		break;
+	case SMP_CMD_SECURITY_REQ:
+		smp_cmd_security_req_dump(level + 1, frm);
+		break;
+	default:
+		raw_dump(level, frm);
+	}
+}
diff --git a/bluez/repo/tools/parser/tcpip.c b/bluez/repo/tools/parser/tcpip.c
new file mode 100644
index 0000000..6f2c3cb
--- /dev/null
+++ b/bluez/repo/tools/parser/tcpip.c
@@ -0,0 +1,140 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "parser.h"
+
+void arp_dump(int level, struct frame *frm)
+{
+	int i;
+	char buf[20];
+	struct sockaddr_in sai;
+	struct ether_arp *arp = (struct ether_arp *) frm->ptr;
+
+	printf("Src ");
+	for (i = 0; i < 5; i++)
+		printf("%02x:", arp->arp_sha[i]);
+	printf("%02x", arp->arp_sha[5]);
+	sai.sin_family = AF_INET;
+	memcpy(&sai.sin_addr, &arp->arp_spa, sizeof(sai.sin_addr));
+	getnameinfo((struct sockaddr *) &sai, sizeof(sai), buf, sizeof(buf),
+		    NULL, 0, NI_NUMERICHOST);
+	printf("(%s) ", buf);
+	printf("Tgt ");
+	for (i = 0; i < 5; i++)
+		printf("%02x:", arp->arp_tha[i]);
+	printf("%02x", arp->arp_tha[5]);
+	memcpy(&sai.sin_addr, &arp->arp_tpa, sizeof(sai.sin_addr));
+	getnameinfo((struct sockaddr *) &sai, sizeof(sai), buf, sizeof(buf),
+		    NULL, 0, NI_NUMERICHOST);
+	printf("(%s)\n", buf);
+	frm->ptr += sizeof(struct ether_arp);
+	frm->len -= sizeof(struct ether_arp);
+	raw_dump(level, frm);		// not needed.
+}
+
+void ip_dump(int level, struct frame *frm)
+{
+	char src[50], dst[50];
+	struct ip *ip = (struct ip *) (frm->ptr);
+	uint8_t proto;
+	int len;
+
+	if (ip->ip_v == 4) {
+		struct sockaddr_in sai;
+		proto = ip->ip_p;
+		len = ip->ip_hl << 2;
+		memset(&sai, 0, sizeof(sai));
+		sai.sin_family = AF_INET;
+		memcpy(&sai.sin_addr, &ip->ip_src, sizeof(struct in_addr));
+		getnameinfo((struct sockaddr *) &sai, sizeof(sai),
+			    src, sizeof(src), NULL, 0, NI_NUMERICHOST);
+		memcpy(&sai.sin_addr, &ip->ip_dst, sizeof(struct in_addr));
+		getnameinfo((struct sockaddr *) &sai, sizeof(sai),
+			    dst, sizeof(dst), NULL, 0, NI_NUMERICHOST);
+	} else if (ip->ip_v == 6) {
+		struct sockaddr_in6 sai6;
+		struct ip6_hdr *ip6 = (struct ip6_hdr *) ip;
+		proto = ip6->ip6_nxt;
+		len = sizeof(struct ip6_hdr);
+		memset(&sai6, 0, sizeof(sai6));
+		sai6.sin6_family = AF_INET6;
+		memcpy(&sai6.sin6_addr, &ip6->ip6_src, sizeof(struct in6_addr));
+		getnameinfo((struct sockaddr *) &sai6, sizeof(sai6),
+			    src, sizeof(src), NULL, 0, NI_NUMERICHOST);
+		memcpy(&sai6.sin6_addr, &ip6->ip6_dst, sizeof(struct in6_addr));
+		getnameinfo((struct sockaddr *) &sai6, sizeof(sai6),
+			    dst, sizeof(dst), NULL, 0, NI_NUMERICHOST);
+	} else {
+		raw_dump(level, frm);
+		return;
+	}
+
+	printf("src %s ", src);
+	printf("dst %s\n", dst);
+
+	frm->ptr += len;
+	frm->len -= len;
+	p_indent(++level, frm);
+
+	switch (proto) {
+	case IPPROTO_TCP:
+		printf("TCP:\n");
+		break;
+
+	case IPPROTO_UDP:
+		printf("UDP:\n");
+		break;
+
+	case IPPROTO_ICMP:
+		printf("ICMP:\n");
+		break;
+
+	case IPPROTO_ICMPV6:
+		printf("ICMPv6:\n");
+		break;
+
+	default:
+		printf("Unknown Protocol: 0x%02x\n", ip->ip_p);
+		break;
+	}
+
+	raw_dump(level, frm);
+}
diff --git a/bluez/repo/tools/rctest.1 b/bluez/repo/tools/rctest.1
new file mode 100644
index 0000000..dfedbef
--- /dev/null
+++ b/bluez/repo/tools/rctest.1
@@ -0,0 +1,90 @@
+.TH RCTEST 1 "Jul 6 2009" BlueZ ""
+.SH NAME
+rctest \- RFCOMM testing
+.SH SYNOPSIS
+.B rctest
+<\fImode\fR> [\fIoptions\fR] [\fIbdaddr\fR]
+
+.SH DESCRIPTION
+.LP
+.B
+rctest
+is used to test RFCOMM communications on the BlueZ stack
+
+.SH MODES
+.TP
+.B -r
+listen and receive
+.TP
+.B -w
+listen and send
+.TP
+.B -d
+listen and dump incoming data
+.TP
+.B -s
+connect and send
+.TP
+.B -u
+connect and receive
+.TP
+.B -n
+connect and be silent
+.TP
+.B -c
+connect, disconnect, connect, ...
+.TP
+.B -m
+multiple connects
+
+.SH OPTIONS
+.TP
+.BI -b\  bytes
+send/receive \fIbytes\fR bytes
+.TP
+.BI -i\  device
+select the specified \fIdevice\fR
+.TP
+.BI -P\  channel
+select the specified \fIchannel\fR
+.TP
+.BI -U\  uuid
+select the specified \fIuuid\fR
+.TP
+.BI -L\  seconds
+enable SO_LINGER options for \fIseconds\fR
+.TP
+.BI -W\  seconds
+enable deferred setup for \fIseconds\fR
+.TP
+.BI -B\  filename
+use data packets from \fIfilename\fR
+.TP
+.BI -N\  num
+send \fInum\fR frames
+.TP
+.BI -C\  num
+send \fInum\fR frames before delay (default: 1)
+.TP
+.BI -D\  milliseconds
+delay \fImilliseconds\fR after sending \fInum\fR frames (default: 0)
+.TP
+.B -A
+request authentication
+.TP
+.B -E
+request encryption
+.TP
+.B -S
+secure connection
+.TP
+.B -M
+become master
+.TP
+.B -T
+enable timestamps
+
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org> and Maxim Krasnyansky
+<maxk@qualcomm.com>, man page by Filippo Giunchedi <filippo@debian.org>
+.PP
diff --git a/bluez/repo/tools/rctest.c b/bluez/repo/tools/rctest.c
new file mode 100644
index 0000000..6d84e07
--- /dev/null
+++ b/bluez/repo/tools/rctest.c
@@ -0,0 +1,911 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/rfcomm.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "src/shared/util.h"
+
+/* Test modes */
+enum {
+	SEND,
+	RECV,
+	RECONNECT,
+	MULTY,
+	DUMP,
+	CONNECT,
+	CRECV,
+	LSEND,
+	AUTO,
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 127;
+static long num_frames = -1;
+
+/* Default number of consecutive frames before the delay */
+static int count = 1;
+
+/* Default delay after sending count number of frames */
+static unsigned long delay = 0;
+
+/* Default addr and channel */
+static bdaddr_t bdaddr;
+static bdaddr_t auto_bdaddr;
+static uint16_t uuid = 0x0000;
+static uint8_t channel = 10;
+
+static const char *filename = NULL;
+static const char *savefile = NULL;
+static int save_fd = -1;
+
+static int master = 0;
+static int auth = 0;
+static int encr = 0;
+static int secure = 0;
+static int socktype = SOCK_STREAM;
+static int linger = 0;
+static int timestamp = 0;
+static int defer_setup = 0;
+static int priority = -1;
+
+static float tv2fl(struct timeval tv)
+{
+	return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static uint8_t get_channel(const char *svr, uint16_t uuid)
+{
+	sdp_session_t *sdp;
+	sdp_list_t *srch, *attrs, *rsp, *protos;
+	uuid_t svclass;
+	uint16_t attr;
+	bdaddr_t dst;
+	uint8_t channel = 0;
+	int err;
+
+	str2ba(svr, &dst);
+
+	sdp = sdp_connect(&bdaddr, &dst, SDP_RETRY_IF_BUSY);
+	if (!sdp)
+		return 0;
+
+	sdp_uuid16_create(&svclass, uuid);
+	srch = sdp_list_append(NULL, &svclass);
+
+	attr = SDP_ATTR_PROTO_DESC_LIST;
+	attrs = sdp_list_append(NULL, &attr);
+
+	err = sdp_service_search_attr_req(sdp, srch,
+					SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+	if (err)
+		goto done;
+
+	for (; rsp; rsp = rsp->next) {
+		sdp_record_t *rec = (sdp_record_t *) rsp->data;
+
+		if (!sdp_get_access_protos(rec, &protos)) {
+			channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+			if (channel > 0)
+				break;
+		}
+	}
+
+	sdp_list_free(protos, NULL);
+
+done:
+	sdp_list_free(srch, NULL);
+	sdp_list_free(attrs, NULL);
+	sdp_close(sdp);
+
+	return channel;
+}
+
+static int do_connect(const char *svr)
+{
+	struct sockaddr_rc addr;
+	struct rfcomm_conninfo conn;
+	socklen_t optlen;
+	int sk, opt;
+
+	if (uuid != 0x0000)
+		channel = get_channel(svr, uuid);
+
+	if (channel == 0) {
+		syslog(LOG_ERR, "Can't get channel number");
+		return -1;
+	}
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		return -1;
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+
+	if (bacmp(&auto_bdaddr, BDADDR_ANY))
+		bacpy(&addr.rc_bdaddr, &auto_bdaddr);
+	else
+		bacpy(&addr.rc_bdaddr, &bdaddr);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+#if 0
+	/* Enable SO_TIMESTAMP */
+	if (timestamp) {
+		int t = 1;
+
+		if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+			syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+	}
+#endif
+
+	/* Enable SO_LINGER */
+	if (linger) {
+		struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+		if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+			syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+	}
+
+	/* Set link mode */
+	opt = 0;
+	if (master)
+		opt |= RFCOMM_LM_MASTER;
+	if (auth)
+		opt |= RFCOMM_LM_AUTH;
+	if (encr)
+		opt |= RFCOMM_LM_ENCRYPT;
+	if (secure)
+		opt |= RFCOMM_LM_SECURE;
+
+	if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+		syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Connect to remote device */
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	str2ba(svr, &addr.rc_bdaddr);
+	addr.rc_channel = channel;
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't connect: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Get connection information */
+	memset(&conn, 0, sizeof(conn));
+	optlen = sizeof(conn);
+
+	if (getsockopt(sk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
+							strerror(errno), errno);
+		//goto error;
+	}
+
+	if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &priority,
+						sizeof(priority)) < 0) {
+		syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	if (getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x, "
+			"priority %d]", conn.hci_handle, conn.dev_class[2],
+			conn.dev_class[1], conn.dev_class[0], opt);
+
+	return sk;
+
+error:
+	close(sk);
+	return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+	struct sockaddr_rc addr;
+	struct rfcomm_conninfo conn;
+	socklen_t optlen;
+	int sk, nsk, opt;
+	char ba[18];
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, &bdaddr);
+	addr.rc_channel = channel;
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Set link mode */
+	opt = 0;
+	if (master)
+		opt |= RFCOMM_LM_MASTER;
+	if (auth)
+		opt |= RFCOMM_LM_AUTH;
+	if (encr)
+		opt |= RFCOMM_LM_ENCRYPT;
+	if (secure)
+		opt |= RFCOMM_LM_SECURE;
+
+	if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+		syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Enable deferred setup */
+	opt = defer_setup;
+
+	if (opt && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+						&opt, sizeof(opt)) < 0) {
+		syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Listen for connections */
+	if (listen(sk, 10)) {
+		syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Check for socket address */
+	memset(&addr, 0, sizeof(addr));
+	optlen = sizeof(addr);
+
+	if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	channel = addr.rc_channel;
+
+	syslog(LOG_INFO, "Waiting for connection on channel %d ...", channel);
+
+	while (1) {
+		memset(&addr, 0, sizeof(addr));
+		optlen = sizeof(addr);
+
+		nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+		if (nsk < 0) {
+			syslog(LOG_ERR,"Accept failed: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+		if (fork()) {
+			/* Parent */
+			close(nsk);
+			continue;
+		}
+		/* Child */
+		close(sk);
+
+		/* Get connection information */
+		memset(&conn, 0, sizeof(conn));
+		optlen = sizeof(conn);
+
+		if (getsockopt(nsk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
+			syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
+							strerror(errno), errno);
+			//close(nsk);
+			//goto error;
+		}
+
+		if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY,
+					&priority, sizeof(priority)) < 0) {
+			syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+						strerror(errno), errno);
+			close(nsk);
+			goto error;
+		}
+
+		optlen = sizeof(priority);
+		if (getsockopt(nsk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+			syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+
+		ba2str(&addr.rc_bdaddr, ba);
+		syslog(LOG_INFO, "Connect from %s [handle %d, "
+				"class 0x%02x%02x%02x, priority %d]",
+				ba, conn.hci_handle, conn.dev_class[2],
+				conn.dev_class[1], conn.dev_class[0], opt);
+
+#if 0
+		/* Enable SO_TIMESTAMP */
+		if (timestamp) {
+			int t = 1;
+
+			if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+				syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+							strerror(errno), errno);
+				goto error;
+			}
+		}
+#endif
+
+		/* Enable SO_LINGER */
+		if (linger) {
+			struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+			if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+				syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+							strerror(errno), errno);
+				close(nsk);
+				goto error;
+			}
+		}
+
+		/* Handle deferred setup */
+		if (defer_setup) {
+			syslog(LOG_INFO, "Waiting for %d seconds",
+							abs(defer_setup) - 1);
+			sleep(abs(defer_setup) - 1);
+
+			if (defer_setup < 0) {
+				close(nsk);
+				goto error;
+			}
+		}
+
+		handler(nsk);
+
+		syslog(LOG_INFO, "Disconnect: %m");
+		exit(0);
+	}
+
+error:
+	close(sk);
+	exit(1);
+}
+
+static void dump_mode(int sk)
+{
+	int len;
+
+	syslog(LOG_INFO, "Receiving ...");
+	while ((len = read(sk, buf, data_size)) > 0)
+		syslog(LOG_INFO, "Received %d bytes", len);
+}
+
+static void save_mode(int sk)
+{
+	int len, ret;
+	char *b;
+
+	b = malloc(data_size);
+	if (!b) {
+		syslog(LOG_ERR, "Failed to open file to save recv data");
+		return;
+	}
+
+	syslog(LOG_INFO, "Receiving ...");
+	while ((len = read(sk, b, data_size)) > 0) {
+		ret = write(save_fd, b, len);
+		if (ret < 0)
+			goto done;
+	}
+
+done:
+	free(b);
+}
+
+static void recv_mode(int sk)
+{
+	struct timeval tv_beg, tv_end, tv_diff;
+	char ts[30];
+	long total;
+
+	syslog(LOG_INFO, "Receiving ...");
+
+	memset(ts, 0, sizeof(ts));
+
+	while (1) {
+		gettimeofday(&tv_beg,NULL);
+		total = 0;
+		while (total < data_size) {
+			//uint32_t sq;
+			//uint16_t l;
+			int r;
+
+			if ((r = recv(sk, buf, data_size, 0)) < 0) {
+				if (r < 0)
+					syslog(LOG_ERR, "Read failed: %s (%d)",
+							strerror(errno), errno);
+				return;
+			}
+
+			if (timestamp) {
+				struct timeval tv;
+
+				if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
+					timestamp = 0;
+					memset(ts, 0, sizeof(ts));
+				} else {
+					sprintf(ts, "[%ld.%ld] ",
+							tv.tv_sec, tv.tv_usec);
+				}
+			}
+
+#if 0
+			/* Check sequence */
+			sq = btohl(*(uint32_t *) buf);
+			if (seq != sq) {
+				syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
+				seq = sq;
+			}
+			seq++;
+
+			/* Check length */
+			l = btohs(*(uint16_t *) (buf + 4));
+			if (r != l) {
+				syslog(LOG_INFO, "size missmatch: %d -> %d", r, l);
+				continue;
+			}
+
+			/* Verify data */
+			for (i = 6; i < r; i++) {
+				if (buf[i] != 0x7f)
+					syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
+			}
+#endif
+			total += r;
+		}
+		gettimeofday(&tv_end,NULL);
+
+		timersub(&tv_end,&tv_beg,&tv_diff);
+
+		syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
+			tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+	}
+}
+
+static void do_send(int sk)
+{
+	uint32_t seq;
+	int i, fd, len;
+
+	syslog(LOG_INFO,"Sending ...");
+
+	if (filename) {
+		fd = open(filename, O_RDONLY);
+		if (fd < 0) {
+			syslog(LOG_ERR, "Open failed: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+		len = read(fd, buf, data_size);
+		send(sk, buf, len, 0);
+		close(fd);
+		return;
+	} else {
+		for (i = 6; i < data_size; i++)
+			buf[i] = 0x7f;
+	}
+
+	seq = 0;
+	while ((num_frames == -1) || (num_frames-- > 0)) {
+		put_le32(seq, buf);
+		put_le16(data_size, buf + 4);
+
+		seq++;
+
+		if (send(sk, buf, data_size, 0) <= 0) {
+			syslog(LOG_ERR, "Send failed: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		if (num_frames && delay && count && !(seq % count))
+			usleep(delay);
+	}
+}
+
+static void send_mode(int sk)
+{
+	do_send(sk);
+
+	syslog(LOG_INFO, "Closing channel ...");
+	if (shutdown(sk, SHUT_RDWR) < 0)
+		syslog(LOG_INFO, "Close failed: %m");
+	else
+		syslog(LOG_INFO, "Done");
+	close(sk);
+}
+
+static void reconnect_mode(char *svr)
+{
+	while(1) {
+		int sk = do_connect(svr);
+		close(sk);
+	}
+}
+
+static void multi_connect_mode(int argc, char *argv[])
+{
+	int i, n, sk;
+
+	while (1) {
+		for (n = 0; n < argc; n++) {
+			for (i = 0; i < count; i++) {
+				if (fork())
+					continue;
+
+				/* Child */
+				sk = do_connect(argv[n]);
+				usleep(500);
+				close(sk);
+				exit(0);
+			}
+		}
+		sleep(4);
+	}
+}
+
+static void automated_send_recv()
+{
+	int sk;
+	char device[18];
+
+	if (fork()) {
+		if (!savefile) {
+			/* do_listen() never returns */
+			do_listen(recv_mode);
+		}
+
+		save_fd = open(savefile, O_CREAT | O_WRONLY,
+						S_IRUSR | S_IWUSR);
+		if (save_fd < 0)
+			syslog(LOG_ERR, "Failed to open file to save data");
+
+		/* do_listen() never returns */
+		do_listen(save_mode);
+	} else {
+		ba2str(&bdaddr, device);
+
+		sk = do_connect(device);
+		if (sk < 0)
+			exit(1);
+		send_mode(sk);
+	}
+}
+
+static void sig_child_exit(int code)
+{
+	if (save_fd >= 0)
+		close(save_fd);
+
+	syslog(LOG_INFO, "Exit");
+	exit(0);
+}
+
+static void usage(void)
+{
+	printf("rctest - RFCOMM testing\n"
+		"Usage:\n");
+	printf("\trctest <mode> [options] [bdaddr]\n");
+	printf("Modes:\n"
+		"\t-r listen and receive\n"
+		"\t-w listen and send\n"
+		"\t-d listen and dump incoming data\n"
+		"\t-s connect and send\n"
+		"\t-u connect and receive\n"
+		"\t-n connect and be silent\n"
+		"\t-c connect, disconnect, connect, ...\n"
+		"\t-m multiple connects\n"
+		"\t-a automated test (receive hcix as parameter)\n");
+
+	printf("Options:\n"
+		"\t[-b bytes] [-i device] [-P channel] [-U uuid]\n"
+		"\t[-L seconds] enabled SO_LINGER option\n"
+		"\t[-W seconds] enable deferred setup\n"
+		"\t[-B filename] use data packets from file\n"
+		"\t[-O filename] save received data to file\n"
+		"\t[-N num] number of frames to send\n"
+		"\t[-C num] send num frames before delay (default = 1)\n"
+		"\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+		"\t[-Y priority] socket priority\n"
+		"\t[-A] request authentication\n"
+		"\t[-E] request encryption\n"
+		"\t[-S] secure connection\n"
+		"\t[-M] become master\n"
+		"\t[-T] enable timestamps\n");
+}
+
+int main(int argc, char *argv[])
+{
+	struct sigaction sa;
+	int opt, sk, mode = RECV, need_addr = 0;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+	bacpy(&auto_bdaddr, BDADDR_ANY);
+
+	while ((opt=getopt(argc,argv,"rdscuwmna:b:i:P:U:B:O:N:MAESL:W:C:D:Y:T")) != EOF) {
+		switch (opt) {
+		case 'r':
+			mode = RECV;
+			break;
+
+		case 's':
+			mode = SEND;
+			need_addr = 1;
+			break;
+
+		case 'w':
+			mode = LSEND;
+			break;
+
+		case 'u':
+			mode = CRECV;
+			need_addr = 1;
+			break;
+
+		case 'd':
+			mode = DUMP;
+			break;
+
+		case 'c':
+			mode = RECONNECT;
+			need_addr = 1;
+			break;
+
+		case 'n':
+			mode = CONNECT;
+			need_addr = 1;
+			break;
+
+		case 'm':
+			mode = MULTY;
+			need_addr = 1;
+			break;
+
+		case 'a':
+			mode = AUTO;
+
+			if (!strncasecmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &auto_bdaddr);
+			else
+				str2ba(optarg, &auto_bdaddr);
+			break;
+
+		case 'b':
+			data_size = atoi(optarg);
+			break;
+
+		case 'i':
+			if (!strncasecmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &bdaddr);
+			else
+				str2ba(optarg, &bdaddr);
+			break;
+
+		case 'P':
+			channel = atoi(optarg);
+			break;
+
+		case 'U':
+			if (!strcasecmp(optarg, "spp"))
+				uuid = SERIAL_PORT_SVCLASS_ID;
+			else if (!strncasecmp(optarg, "0x", 2))
+				uuid = strtoul(optarg + 2, NULL, 16);
+			else
+				uuid = atoi(optarg);
+			break;
+
+		case 'M':
+			master = 1;
+			break;
+
+		case 'A':
+			auth = 1;
+			break;
+
+		case 'E':
+			encr = 1;
+			break;
+
+		case 'S':
+			secure = 1;
+			break;
+
+		case 'L':
+			linger = atoi(optarg);
+			break;
+
+		case 'W':
+			defer_setup = atoi(optarg);
+			break;
+
+		case 'B':
+			filename = optarg;
+			break;
+
+		case 'O':
+			savefile = optarg;
+			break;
+
+		case 'N':
+			num_frames = atoi(optarg);
+			break;
+
+		case 'C':
+			count = atoi(optarg);
+			break;
+
+		case 'D':
+			delay = atoi(optarg) * 1000;
+			break;
+
+		case 'Y':
+			priority = atoi(optarg);
+			break;
+
+		case 'T':
+			timestamp = 1;
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	if (need_addr && !(argc - optind)) {
+		usage();
+		exit(1);
+	}
+
+	if (!(buf = malloc(data_size))) {
+		perror("Can't allocate data buffer");
+		exit(1);
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	if (mode == AUTO)
+		sa.sa_handler = sig_child_exit;
+	else
+		sa.sa_handler = SIG_IGN;
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sigaction(SIGCHLD, &sa, NULL);
+
+	openlog("rctest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+	switch (mode) {
+		case RECV:
+			do_listen(recv_mode);
+			break;
+
+		case CRECV:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			recv_mode(sk);
+			break;
+
+		case DUMP:
+			do_listen(dump_mode);
+			break;
+
+		case SEND:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			send_mode(sk);
+			break;
+
+		case LSEND:
+			do_listen(send_mode);
+			break;
+
+		case RECONNECT:
+			reconnect_mode(argv[optind]);
+			break;
+
+		case MULTY:
+			multi_connect_mode(argc - optind, argv + optind);
+			break;
+
+		case CONNECT:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			dump_mode(sk);
+			break;
+
+		case AUTO:
+			automated_send_recv();
+			break;
+	}
+
+	syslog(LOG_INFO, "Exit");
+
+	closelog();
+
+	return 0;
+}
diff --git a/bluez/repo/tools/rfcomm-tester.c b/bluez/repo/tools/rfcomm-tester.c
new file mode 100644
index 0000000..b20d70d
--- /dev/null
+++ b/bluez/repo/tools/rfcomm-tester.c
@@ -0,0 +1,765 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/rfcomm.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+
+struct test_data {
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	const void *test_data;
+	unsigned int io_id;
+	uint16_t conn_handle;
+};
+
+struct rfcomm_client_data {
+	uint8_t server_channel;
+	uint8_t client_channel;
+	int expected_connect_err;
+	const uint8_t *send_data;
+	const uint8_t *read_data;
+	uint16_t data_len;
+};
+
+struct rfcomm_server_data {
+	uint8_t server_channel;
+	uint8_t client_channel;
+	bool expected_status;
+	const uint8_t *send_data;
+	const uint8_t *read_data;
+	uint16_t data_len;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (data->io_id > 0) {
+		g_source_remove(data->io_id);
+		data->io_id = 0;
+	}
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	free(data);
+}
+
+static void client_connectable_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	switch (opcode) {
+	case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+		break;
+	default:
+		return;
+	}
+
+	tester_print("Client set connectable status 0x%02x", status);
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data);
+	bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void setup_powered_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_client_callback,
+			NULL, NULL);
+}
+
+static void setup_powered_server_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	tester_setup_complete();
+}
+
+static void setup_powered_server(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+				sizeof(param), param,
+				NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_server_callback,
+			NULL, NULL);
+}
+
+const struct rfcomm_client_data connect_success = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0c
+};
+
+const uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+
+const struct rfcomm_client_data connect_send_success = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0c,
+	.data_len = sizeof(data),
+	.send_data = data
+};
+
+const struct rfcomm_client_data connect_read_success = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0c,
+	.data_len = sizeof(data),
+	.read_data = data
+};
+
+const struct rfcomm_client_data connect_nval = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0e,
+	.expected_connect_err = -ECONNREFUSED
+};
+
+const struct rfcomm_server_data listen_success = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0c,
+	.expected_status = true
+};
+
+const struct rfcomm_server_data listen_send_success = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0c,
+	.expected_status = true,
+	.data_len = sizeof(data),
+	.send_data = data
+};
+
+const struct rfcomm_server_data listen_read_success = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0c,
+	.expected_status = true,
+	.data_len = sizeof(data),
+	.read_data = data
+};
+
+const struct rfcomm_server_data listen_nval = {
+	.server_channel = 0x0c,
+	.client_channel = 0x0e,
+	.expected_status = false
+};
+
+static void test_basic(const void *test_data)
+{
+	int sk;
+
+	sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+	if (sk < 0) {
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		tester_test_failed();
+		return;
+	}
+
+	close(sk);
+
+	tester_test_passed();
+}
+
+static int create_rfcomm_sock(bdaddr_t *address, uint8_t channel)
+{
+	int sk;
+	struct sockaddr_rc addr;
+
+	sk = socket(PF_BLUETOOTH, SOCK_STREAM | SOCK_NONBLOCK, BTPROTO_RFCOMM);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	addr.rc_channel = channel;
+	bacpy(&addr.rc_bdaddr, address);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(sk);
+		return -1;
+	}
+
+	return sk;
+}
+
+static int connect_rfcomm_sock(int sk, const bdaddr_t *bdaddr, uint8_t channel)
+{
+	struct sockaddr_rc addr;
+	int err;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, bdaddr);
+	addr.rc_channel = htobs(channel);
+
+	err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_client_data *cli = data->test_data;
+	int sk;
+	ssize_t ret;
+	char buf[248];
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	ret = read(sk, buf, cli->data_len);
+	if (cli->data_len != ret) {
+		tester_test_failed();
+		return false;
+	}
+
+	if (memcmp(cli->read_data, buf, cli->data_len))
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return false;
+}
+
+static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_client_data *cli = data->test_data;
+	socklen_t len = sizeof(int);
+	int sk, err, sk_err;
+
+	tester_print("Connected");
+
+	data->io_id = 0;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+		err = -errno;
+	else
+		err = -sk_err;
+
+	if (cli->expected_connect_err && err == cli->expected_connect_err) {
+		tester_test_passed();
+		return false;
+	}
+
+	if (cli->send_data) {
+		ssize_t ret;
+
+		tester_print("Writing %u bytes of data", cli->data_len);
+
+		ret = write(sk, cli->send_data, cli->data_len);
+		if (cli->data_len != ret) {
+			tester_warn("Failed to write %u bytes: %s (%d)",
+					cli->data_len, strerror(errno), errno);
+			tester_test_failed();
+		}
+
+		return false;
+	} else if (cli->read_data) {
+		g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
+		bthost_send_rfcomm_data(hciemu_client_get_host(data->hciemu),
+						data->conn_handle,
+						cli->client_channel,
+						cli->read_data, cli->data_len);
+		return false;
+	}
+
+	if (err < 0)
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return false;
+}
+
+static void client_hook_func(const void *data, uint16_t len,
+							void *user_data)
+{
+	struct test_data *test_data = tester_get_data();
+	const struct rfcomm_client_data *cli = test_data->test_data;
+	ssize_t ret;
+
+	tester_print("bthost received %u bytes of data", len);
+
+	if (cli->data_len != len) {
+		tester_test_failed();
+		return;
+	}
+
+	ret = memcmp(cli->send_data, data, len);
+	if (ret)
+		tester_test_failed();
+	else
+		tester_test_passed();
+}
+
+static void server_hook_func(const void *data, uint16_t len,
+							void *user_data)
+{
+	struct test_data *test_data = tester_get_data();
+	const struct rfcomm_server_data *srv = test_data->test_data;
+	ssize_t ret;
+
+	if (srv->data_len != len) {
+		tester_test_failed();
+		return;
+	}
+
+	ret = memcmp(srv->send_data, data, len);
+	if (ret)
+		tester_test_failed();
+	else
+		tester_test_passed();
+}
+
+static void rfcomm_connect_cb(uint16_t handle, uint16_t cid,
+						void *user_data, bool status)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_client_data *cli = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	if (cli->send_data)
+		bthost_add_rfcomm_chan_hook(bthost, handle,
+						cli->client_channel,
+						client_hook_func, NULL);
+	else if (cli->read_data)
+		data->conn_handle = handle;
+}
+
+static void test_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct rfcomm_client_data *cli = data->test_data;
+	const uint8_t *client_addr, *master_addr;
+	GIOChannel *io;
+	int sk;
+
+	bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL);
+	bthost_add_rfcomm_server(bthost, cli->server_channel,
+						rfcomm_connect_cb, NULL);
+
+	master_addr = hciemu_get_master_bdaddr(data->hciemu);
+	client_addr = hciemu_get_client_bdaddr(data->hciemu);
+
+	sk = create_rfcomm_sock((bdaddr_t *) master_addr, 0);
+
+	if (connect_rfcomm_sock(sk, (const bdaddr_t *) client_addr,
+					cli->client_channel) < 0) {
+		close(sk);
+		tester_test_failed();
+		return;
+	}
+
+	io = g_io_channel_unix_new(sk);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	data->io_id = g_io_add_watch(io, G_IO_OUT, rc_connect_cb, NULL);
+
+	g_io_channel_unref(io);
+
+	tester_print("Connect in progress %d", sk);
+}
+
+static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_server_data *srv = data->test_data;
+	char buf[1024];
+	ssize_t ret;
+	int sk;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	ret = read(sk, buf, srv->data_len);
+	if (ret != srv->data_len) {
+		tester_test_failed();
+		return false;
+	}
+
+	if (memcmp(buf, srv->read_data, srv->data_len))
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return false;
+}
+
+static gboolean rfcomm_listen_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_server_data *srv = data->test_data;
+	int sk, new_sk;
+
+	data->io_id = 0;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	new_sk = accept(sk, NULL, NULL);
+	if (new_sk < 0) {
+		tester_test_failed();
+		return false;
+	}
+
+	if (srv->send_data) {
+		ssize_t ret;
+
+		ret = write(new_sk, srv->send_data, srv->data_len);
+		if (ret != srv->data_len)
+			tester_test_failed();
+
+		close(new_sk);
+		return false;
+	} else if (srv->read_data) {
+		GIOChannel *new_io;
+
+		new_io = g_io_channel_unix_new(new_sk);
+		g_io_channel_set_close_on_unref(new_io, TRUE);
+
+		data->io_id = g_io_add_watch(new_io, G_IO_IN,
+						server_received_data, NULL);
+
+		g_io_channel_unref(new_io);
+		return false;
+	}
+
+	close(new_sk);
+
+	tester_test_passed();
+
+	return false;
+}
+
+static void connection_cb(uint16_t handle, uint16_t cid, void *user_data,
+								bool status)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_server_data *srv = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	if (srv->read_data) {
+		data->conn_handle = handle;
+		bthost_send_rfcomm_data(bthost, data->conn_handle,
+						srv->client_channel,
+						srv->read_data, srv->data_len);
+		return;
+	} else if (srv->data_len) {
+		return;
+	}
+
+	if (srv->expected_status == status)
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+static void client_new_conn(uint16_t handle, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_server_data *srv = data->test_data;
+	struct bthost *bthost;
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_add_rfcomm_chan_hook(bthost, handle, srv->client_channel,
+						server_hook_func, NULL);
+	bthost_connect_rfcomm(bthost, handle, srv->client_channel,
+						connection_cb, NULL);
+}
+
+static void test_server(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct rfcomm_server_data *srv = data->test_data;
+	const uint8_t *master_addr;
+	struct bthost *bthost;
+	GIOChannel *io;
+	int sk;
+
+	master_addr = hciemu_get_master_bdaddr(data->hciemu);
+
+	sk = create_rfcomm_sock((bdaddr_t *) master_addr, srv->server_channel);
+	if (sk < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	if (listen(sk, 5) < 0) {
+		tester_warn("listening on socket failed: %s (%u)",
+				strerror(errno), errno);
+		tester_test_failed();
+		close(sk);
+		return;
+	}
+
+	io = g_io_channel_unix_new(sk);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	data->io_id = g_io_add_watch(io, G_IO_IN, rfcomm_listen_cb, NULL);
+	g_io_channel_unref(io);
+
+	tester_print("Listening for connections");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_connect_cb(bthost, client_new_conn, data);
+
+	bthost_hci_connect(bthost, master_addr, BDADDR_BREDR);
+}
+
+#define test_rfcomm(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDR; \
+		user->test_data = data; \
+		user->io_id = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_rfcomm("Basic RFCOMM Socket - Success", NULL,
+					setup_powered_client, test_basic);
+	test_rfcomm("Basic RFCOMM Socket Client - Success", &connect_success,
+					setup_powered_client, test_connect);
+	test_rfcomm("Basic RFCOMM Socket Client - Write Success",
+				&connect_send_success, setup_powered_client,
+				test_connect);
+	test_rfcomm("Basic RFCOMM Socket Client - Read Success",
+				&connect_read_success, setup_powered_client,
+				test_connect);
+	test_rfcomm("Basic RFCOMM Socket Client - Conn Refused",
+			&connect_nval, setup_powered_client, test_connect);
+	test_rfcomm("Basic RFCOMM Socket Server - Success", &listen_success,
+					setup_powered_server, test_server);
+	test_rfcomm("Basic RFCOMM Socket Server - Write Success",
+				&listen_send_success, setup_powered_server,
+				test_server);
+	test_rfcomm("Basic RFCOMM Socket Server - Read Success",
+				&listen_read_success, setup_powered_server,
+				test_server);
+	test_rfcomm("Basic RFCOMM Socket Server - Conn Refused", &listen_nval,
+					setup_powered_server, test_server);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/rfcomm.1 b/bluez/repo/tools/rfcomm.1
new file mode 100644
index 0000000..a108609
--- /dev/null
+++ b/bluez/repo/tools/rfcomm.1
@@ -0,0 +1,113 @@
+.\"
+.\"	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH RFCOMM 1 "APRIL 28, 2002" "" ""
+
+.SH NAME
+rfcomm \- RFCOMM configuration utility
+.SH SYNOPSIS
+.BR "rfcomm
+[
+.I options
+] <
+.I command
+> <
+.I dev
+>
+.SH DESCRIPTION
+.B rfcomm
+is used to set up, maintain, and inspect the RFCOMM configuration
+of the Bluetooth subsystem in the Linux kernel. If no
+.B command
+is given, or if the option
+.B -a
+is used,
+.B rfcomm
+prints information about the configured RFCOMM devices.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -a
+Prints information about all configured RFCOMM devices.
+.TP
+.BI -r
+Switch TTY into raw mode (doesn't work with "bind").
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device hciX, which must be the name or the address of
+an installed Bluetooth device. If not specified, the command will be use the
+first available Bluetooth device.
+.TP
+.BI -A
+Enable authentification
+.TP
+.BI -E
+Enable encryption
+.TP
+.BI -S
+Secure connection
+.TP
+.BI -M
+Become the master of a piconet
+.TP
+.BI -L " <seconds>"
+Set linger timeout
+.SH COMMANDS
+.TP
+.BI show " <dev>"
+Display the information about the specified device.
+.TP
+.BI connect " <dev> [bdaddr] [channel]"
+Connect the RFCOMM device to the remote Bluetooth device on the
+specified channel. If no channel is specified, it will use the
+channel number 1. This command can be terminated with the key
+sequence CTRL-C.
+.TP
+.BI listen " <dev> [channel] [cmd]"
+Listen on a specified RFCOMM channel for incoming connections.
+If no channel is specified, it will use the channel number 1, but
+a channel must be specified before cmd. If cmd is given, it will be
+executed as soon as a client connects. When the child process
+terminates or the client disconnect, the command will terminate.
+Occurrences of {} in cmd will be replaced by the name of the device
+used by the connection. This command can be terminated with the key
+sequence CTRL-C.
+.TP
+.BI watch " <dev> [channel] [cmd]"
+Watch is identical to
+.B listen
+except that when the child process terminates or the client
+disconnect, the command will restart listening with the same
+parameters.
+.TP
+.BI bind " <dev> [bdaddr] [channel]"
+This binds the RFCOMM device to a remote Bluetooth device. The
+command does not establish a connection to the remote device, it
+only creates the binding. The connection will be established right
+after an application tries to open the RFCOMM device. If no channel
+number is specified, it uses the channel number 1.
+.TP
+.BI release " <dev>"
+This command releases a defined RFCOMM binding.
+
+If
+.B all
+is specified for the RFCOMM device, then all bindings will be removed.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/bluez/repo/tools/rfcomm.c b/bluez/repo/tools/rfcomm.c
new file mode 100644
index 0000000..809c240
--- /dev/null
+++ b/bluez/repo/tools/rfcomm.c
@@ -0,0 +1,787 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <termios.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/rfcomm.h"
+
+static int rfcomm_raw_tty = 0;
+static int auth = 0;
+static int encryption = 0;
+static int secure = 0;
+static int master = 0;
+static int linger = 0;
+
+static char *rfcomm_state[] = {
+	"unknown",
+	"connected",
+	"clean",
+	"bound",
+	"listening",
+	"connecting",
+	"connecting",
+	"config",
+	"disconnecting",
+	"closed"
+};
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+	return;
+}
+
+static void sig_term(int sig)
+{
+	__io_canceled = 1;
+}
+
+static char *rfcomm_flagstostr(uint32_t flags)
+{
+	static char str[100];
+	str[0] = 0;
+
+	strcat(str, "[");
+
+	if (flags & (1 << RFCOMM_REUSE_DLC))
+		strcat(str, "reuse-dlc ");
+
+	if (flags & (1 << RFCOMM_RELEASE_ONHUP))
+		strcat(str, "release-on-hup ");
+
+	if (flags & (1 << RFCOMM_TTY_ATTACHED))
+		strcat(str, "tty-attached");
+
+	strcat(str, "]");
+	return str;
+}
+
+static void print_dev_info(struct rfcomm_dev_info *di)
+{
+	char src[18], dst[18], addr[40];
+
+	ba2str(&di->src, src); ba2str(&di->dst, dst);
+
+	if (bacmp(&di->src, BDADDR_ANY) == 0)
+		sprintf(addr, "%s", dst);
+	else
+		sprintf(addr, "%s -> %s", src, dst);
+
+	printf("rfcomm%d: %s channel %d %s %s\n",
+		di->id, addr, di->channel,
+		rfcomm_state[di->state],
+		di->flags ? rfcomm_flagstostr(di->flags) : "");
+}
+
+static void print_dev_list(int ctl, int flags)
+{
+	struct rfcomm_dev_list_req *dl;
+	struct rfcomm_dev_info *di;
+	int i;
+
+	dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+	if (!dl) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	dl->dev_num = RFCOMM_MAX_DEV;
+	di = dl->dev_info;
+
+	if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+		perror("Can't get device list");
+		free(dl);
+		exit(1);
+	}
+
+	for (i = 0; i < dl->dev_num; i++)
+		print_dev_info(di + i);
+	free(dl);
+}
+
+static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	struct rfcomm_dev_req req;
+	int err;
+
+	memset(&req, 0, sizeof(req));
+	req.dev_id = dev;
+	req.flags = flags;
+	bacpy(&req.src, bdaddr);
+
+	if (argc < 2) {
+		fprintf(stderr, "Missing dev parameter");
+		return -EINVAL;
+	}
+
+	str2ba(argv[1], &req.dst);
+
+	if (argc > 2)
+		req.channel = atoi(argv[2]);
+	else
+		req.channel = 1;
+
+	err = ioctl(ctl, RFCOMMCREATEDEV, &req);
+	if (err == -1) {
+		err = -errno;
+
+		if (err == -EOPNOTSUPP)
+			fprintf(stderr, "RFCOMM TTY support not available\n");
+		else
+			perror("Can't create device");
+	}
+
+	return err;
+}
+
+static int release_dev(int ctl, int dev, uint32_t flags)
+{
+	struct rfcomm_dev_req req;
+	int err;
+
+	memset(&req, 0, sizeof(req));
+	req.dev_id = dev;
+
+	err = ioctl(ctl, RFCOMMRELEASEDEV, &req);
+	if (err < 0)
+		perror("Can't release device");
+
+	return err;
+}
+
+static int release_all(int ctl)
+{
+	struct rfcomm_dev_list_req *dl;
+	struct rfcomm_dev_info *di;
+	int i;
+
+	dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+	if (!dl) {
+		perror("Can't allocate memory");
+		exit(1);
+	}
+
+	dl->dev_num = RFCOMM_MAX_DEV;
+	di = dl->dev_info;
+
+	if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+		perror("Can't get device list");
+		free(dl);
+		exit(1);
+	}
+
+	for (i = 0; i < dl->dev_num; i++)
+		release_dev(ctl, (di + i)->id, 0);
+
+	free(dl);
+	return 0;
+}
+
+static void run_cmdline(struct pollfd *p, sigset_t *sigs, char *devname,
+			int argc, char **argv)
+{
+	int i;
+	pid_t pid;
+	char **cmdargv;
+
+	cmdargv = malloc((argc + 1) * sizeof(char *));
+	if (!cmdargv)
+		return;
+
+	for (i = 0; i < argc; i++)
+		cmdargv[i] = (strcmp(argv[i], "{}") == 0) ? devname : argv[i];
+	cmdargv[i] = NULL;
+
+	pid = fork();
+
+	switch (pid) {
+	case 0:
+		i = execvp(cmdargv[0], cmdargv);
+		fprintf(stderr, "Couldn't execute command %s (errno=%d:%s)\n",
+				cmdargv[0], errno, strerror(errno));
+		break;
+	case -1:
+		fprintf(stderr, "Couldn't fork to execute command %s\n",
+				cmdargv[0]);
+		break;
+	default:
+		while (1) {
+			int status;
+			pid_t child;
+			struct timespec ts;
+
+			child = waitpid(-1, &status, WNOHANG);
+			if (child == pid || (child < 0 && errno != EAGAIN))
+				break;
+
+			p->revents = 0;
+			ts.tv_sec  = 0;
+			ts.tv_nsec = 200;
+			if (ppoll(p, 1, &ts, sigs) || __io_canceled) {
+				kill(pid, SIGTERM);
+				waitpid(pid, &status, 0);
+				break;
+			}
+		}
+		break;
+	}
+
+	free(cmdargv);
+}
+
+static void cmd_connect(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	struct sockaddr_rc laddr, raddr;
+	struct rfcomm_dev_req req;
+	struct termios ti;
+	struct sigaction sa;
+	struct pollfd p;
+	sigset_t sigs;
+	socklen_t alen;
+	char dst[18], devname[MAXPATHLEN];
+	int sk, fd, try = 30;
+
+	laddr.rc_family = AF_BLUETOOTH;
+	bacpy(&laddr.rc_bdaddr, bdaddr);
+	laddr.rc_channel = 0;
+
+	if (argc < 2) {
+		fprintf(stderr, "Missing dev parameter");
+		return;
+	}
+
+	raddr.rc_family = AF_BLUETOOTH;
+	str2ba(argv[1], &raddr.rc_bdaddr);
+
+	if (argc > 2)
+		raddr.rc_channel = atoi(argv[2]);
+	else
+		raddr.rc_channel = 1;
+
+	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+	if (sk < 0) {
+		perror("Can't create RFCOMM socket");
+		return;
+	}
+
+	if (linger) {
+		struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+		if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+			perror("Can't set linger option");
+			return;
+		}
+	}
+
+	if (bind(sk, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+		perror("Can't bind RFCOMM socket");
+		close(sk);
+		return;
+	}
+
+	if (connect(sk, (struct sockaddr *) &raddr, sizeof(raddr)) < 0) {
+		perror("Can't connect RFCOMM socket");
+		close(sk);
+		return;
+	}
+
+	alen = sizeof(laddr);
+	if (getsockname(sk, (struct sockaddr *)&laddr, &alen) < 0) {
+		perror("Can't get RFCOMM socket name");
+		close(sk);
+		return;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.dev_id = dev;
+	req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+	bacpy(&req.src, &laddr.rc_bdaddr);
+	bacpy(&req.dst, &raddr.rc_bdaddr);
+	req.channel = raddr.rc_channel;
+
+	dev = ioctl(sk, RFCOMMCREATEDEV, &req);
+	if (dev < 0) {
+		perror("Can't create RFCOMM TTY");
+		close(sk);
+		return;
+	}
+
+	snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+	while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+		if (errno == EACCES) {
+			perror("Can't open RFCOMM device");
+			goto release;
+		}
+
+		snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+		if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+			if (try--) {
+				snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+				usleep(100 * 1000);
+				continue;
+			}
+			perror("Can't open RFCOMM device");
+			goto release;
+		}
+	}
+
+	if (rfcomm_raw_tty) {
+		tcflush(fd, TCIOFLUSH);
+
+		cfmakeraw(&ti);
+		tcsetattr(fd, TCSANOW, &ti);
+	}
+
+	close(sk);
+
+	ba2str(&req.dst, dst);
+	printf("Connected %s to %s on channel %d\n", devname, dst, req.channel);
+	printf("Press CTRL-C for hangup\n");
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &sa, NULL);
+	sigaction(SIGPIPE, &sa, NULL);
+
+	sa.sa_handler = sig_term;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	sa.sa_handler = sig_hup;
+	sigaction(SIGHUP, &sa, NULL);
+
+	sigfillset(&sigs);
+	sigdelset(&sigs, SIGCHLD);
+	sigdelset(&sigs, SIGPIPE);
+	sigdelset(&sigs, SIGTERM);
+	sigdelset(&sigs, SIGINT);
+	sigdelset(&sigs, SIGHUP);
+
+	p.fd = fd;
+	p.events = POLLERR | POLLHUP;
+
+	while (!__io_canceled) {
+		p.revents = 0;
+		if (ppoll(&p, 1, NULL, &sigs) > 0)
+			break;
+	}
+
+	printf("Disconnected\n");
+
+	close(fd);
+	return;
+
+release:
+	memset(&req, 0, sizeof(req));
+	req.dev_id = dev;
+	req.flags = (1 << RFCOMM_HANGUP_NOW);
+	ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+	close(sk);
+}
+
+static void cmd_listen(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	struct sockaddr_rc laddr, raddr;
+	struct rfcomm_dev_req req;
+	struct termios ti;
+	struct sigaction sa;
+	struct pollfd p;
+	sigset_t sigs;
+	socklen_t alen;
+	char dst[18], devname[MAXPATHLEN];
+	int sk, nsk, fd, lm, try = 30;
+
+	laddr.rc_family = AF_BLUETOOTH;
+	bacpy(&laddr.rc_bdaddr, bdaddr);
+	laddr.rc_channel = (argc < 2) ? 1 : atoi(argv[1]);
+
+	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+	if (sk < 0) {
+		perror("Can't create RFCOMM socket");
+		return;
+	}
+
+	lm = 0;
+	if (master)
+		lm |= RFCOMM_LM_MASTER;
+	if (auth)
+		lm |= RFCOMM_LM_AUTH;
+	if (encryption)
+		lm |= RFCOMM_LM_ENCRYPT;
+	if (secure)
+		lm |= RFCOMM_LM_SECURE;
+
+	if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+		perror("Can't set RFCOMM link mode");
+		close(sk);
+		return;
+	}
+
+	if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
+		perror("Can't bind RFCOMM socket");
+		close(sk);
+		return;
+	}
+
+	printf("Waiting for connection on channel %d\n", laddr.rc_channel);
+
+	listen(sk, 10);
+
+	alen = sizeof(raddr);
+	nsk = accept(sk, (struct sockaddr *) &raddr, &alen);
+
+	alen = sizeof(laddr);
+	if (getsockname(nsk, (struct sockaddr *)&laddr, &alen) < 0) {
+		perror("Can't get RFCOMM socket name");
+		close(nsk);
+		return;
+	}
+
+	if (linger) {
+		struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+		if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+			perror("Can't set linger option");
+			close(nsk);
+			return;
+		}
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.dev_id = dev;
+	req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+	bacpy(&req.src, &laddr.rc_bdaddr);
+	bacpy(&req.dst, &raddr.rc_bdaddr);
+	req.channel = raddr.rc_channel;
+
+	dev = ioctl(nsk, RFCOMMCREATEDEV, &req);
+	if (dev < 0) {
+		perror("Can't create RFCOMM TTY");
+		close(sk);
+		return;
+	}
+
+	snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+	while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+		if (errno == EACCES) {
+			perror("Can't open RFCOMM device");
+			goto release;
+		}
+
+		snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+		if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+			if (try--) {
+				snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+				usleep(100 * 1000);
+				continue;
+			}
+			perror("Can't open RFCOMM device");
+			goto release;
+		}
+	}
+
+	if (rfcomm_raw_tty) {
+		tcflush(fd, TCIOFLUSH);
+
+		cfmakeraw(&ti);
+		tcsetattr(fd, TCSANOW, &ti);
+	}
+
+	close(sk);
+	close(nsk);
+
+	ba2str(&req.dst, dst);
+	printf("Connection from %s to %s\n", dst, devname);
+	printf("Press CTRL-C for hangup\n");
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGCHLD, &sa, NULL);
+	sigaction(SIGPIPE, &sa, NULL);
+
+	sa.sa_handler = sig_term;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	sa.sa_handler = sig_hup;
+	sigaction(SIGHUP, &sa, NULL);
+
+	sigfillset(&sigs);
+	sigdelset(&sigs, SIGCHLD);
+	sigdelset(&sigs, SIGPIPE);
+	sigdelset(&sigs, SIGTERM);
+	sigdelset(&sigs, SIGINT);
+	sigdelset(&sigs, SIGHUP);
+
+	p.fd = fd;
+	p.events = POLLERR | POLLHUP;
+
+	if (argc <= 2) {
+		while (!__io_canceled) {
+			p.revents = 0;
+			if (ppoll(&p, 1, NULL, &sigs) > 0)
+				break;
+		}
+	} else
+		run_cmdline(&p, &sigs, devname, argc - 2, argv + 2);
+
+	sa.sa_handler = NULL;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	printf("Disconnected\n");
+
+	close(fd);
+	return;
+
+release:
+	memset(&req, 0, sizeof(req));
+	req.dev_id = dev;
+	req.flags = (1 << RFCOMM_HANGUP_NOW);
+	ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+	close(sk);
+}
+
+static void cmd_watch(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	while (!__io_canceled) {
+		cmd_listen(ctl, dev, bdaddr, argc, argv);
+		usleep(10000);
+	}
+}
+
+static void cmd_create(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	create_dev(ctl, dev, 0, bdaddr, argc, argv);
+}
+
+static void cmd_release(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	if (strcmp(argv[0], "all") == 0)
+		release_all(ctl);
+	else
+		release_dev(ctl, dev, 0);
+}
+
+static void cmd_show(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+	if (strcmp(argv[0], "all") == 0)
+		print_dev_list(ctl, 0);
+	else {
+		struct rfcomm_dev_info di = { .id = atoi(argv[0]) };
+		if (ioctl(ctl, RFCOMMGETDEVINFO, &di) < 0) {
+			perror("Get info failed");
+			exit(1);
+		}
+
+		print_dev_info(&di);
+	}
+}
+
+struct {
+	char *cmd;
+	char *alt;
+	void (*func)(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv);
+	char *opt;
+	char *doc;
+} command[] = {
+	{ "bind",    "create", cmd_create,  "<dev> <bdaddr> [channel]", "Bind device"    },
+	{ "release", "unbind", cmd_release, "<dev>",                    "Release device" },
+	{ "show",    "info",   cmd_show,    "<dev>",                    "Show device"    },
+	{ "connect", "conn",   cmd_connect, "<dev> <bdaddr> [channel]", "Connect device" },
+	{ "listen",  "server", cmd_listen,  "<dev> [channel [cmd]]",    "Listen"         },
+	{ "watch",   "watch",  cmd_watch,   "<dev> [channel [cmd]]",    "Watch"          },
+	{ NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+	int i;
+
+	printf("RFCOMM configuration utility ver %s\n", VERSION);
+
+	printf("Usage:\n"
+		"\trfcomm [options] <command> <dev>\n"
+		"\n");
+
+	printf("Options:\n"
+		"\t-i, --device [hciX|bdaddr]     Local HCI device or BD Address\n"
+		"\t-h, --help                     Display help\n"
+		"\t-r, --raw                      Switch TTY into raw mode\n"
+		"\t-A, --auth                     Enable authentication\n"
+		"\t-E, --encrypt                  Enable encryption\n"
+		"\t-S, --secure                   Secure connection\n"
+		"\t-M, --master                   Become the master of a piconet\n"
+		"\t-L, --linger [seconds]         Set linger timeout\n"
+		"\t-a                             Show all devices (default)\n"
+		"\n");
+
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-8s %-24s\t%s\n",
+			command[i].cmd,
+			command[i].opt ? command[i].opt : " ",
+			command[i].doc);
+	printf("\n");
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "device",	1, 0, 'i' },
+	{ "config",	1, 0, 'f' },
+	{ "raw",	0, 0, 'r' },
+	{ "auth",	0, 0, 'A' },
+	{ "encrypt",	0, 0, 'E' },
+	{ "secure",	0, 0, 'S' },
+	{ "master",	0, 0, 'M' },
+	{ "linger",	1, 0, 'L' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	bdaddr_t bdaddr;
+	int i, opt, ctl, dev_id, show_all = 0;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while ((opt = getopt_long(argc, argv, "+i:rahAESML:", main_options, NULL)) != -1) {
+		switch(opt) {
+		case 'i':
+			if (strncmp(optarg, "hci", 3) == 0)
+				hci_devba(atoi(optarg + 3), &bdaddr);
+			else
+				str2ba(optarg, &bdaddr);
+			break;
+
+		case 'r':
+			rfcomm_raw_tty = 1;
+			break;
+
+		case 'a':
+			show_all = 1;
+			break;
+
+		case 'h':
+			usage();
+			exit(0);
+
+		case 'A':
+			auth = 1;
+			break;
+
+		case 'E':
+			encryption = 1;
+			break;
+
+		case 'S':
+			secure = 1;
+			break;
+
+		case 'M':
+			master = 1;
+			break;
+
+		case 'L':
+			linger = atoi(optarg);
+			break;
+
+		default:
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 2) {
+		if (argc != 0) {
+			usage();
+			exit(1);
+		} else
+			show_all = 1;
+	}
+
+	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+	if (ctl < 0) {
+		perror("Can't open RFCOMM control socket");
+		exit(1);
+	}
+
+	if (show_all) {
+		print_dev_list(ctl, 0);
+		close(ctl);
+		exit(0);
+	}
+
+	if (strncmp(argv[1], "/dev/rfcomm", 11) == 0)
+		dev_id = atoi(argv[1] + 11);
+	else if (strncmp(argv[1], "rfcomm", 6) == 0)
+		dev_id = atoi(argv[1] + 6);
+	else
+		dev_id = atoi(argv[1]);
+
+	for (i = 0; command[i].cmd; i++) {
+		if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+			continue;
+		argc--;
+		argv++;
+		command[i].func(ctl, dev_id, &bdaddr, argc, argv);
+		close(ctl);
+		exit(0);
+	}
+
+	usage();
+
+	close(ctl);
+
+	return 0;
+}
diff --git a/bluez/repo/tools/sco-tester.c b/bluez/repo/tools/sco-tester.c
new file mode 100644
index 0000000..651fbe0
--- /dev/null
+++ b/bluez/repo/tools/sco-tester.c
@@ -0,0 +1,613 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sco.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+
+struct test_data {
+	const void *test_data;
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	unsigned int io_id;
+	bool disable_esco;
+};
+
+struct sco_client_data {
+	int expect_err;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(HCIEMU_TYPE_BREDRLE);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_print("New hciemu instance created");
+
+	if (data->disable_esco) {
+		uint8_t *features;
+
+		tester_print("Disabling eSCO packet type support");
+
+		features = hciemu_get_features(data->hciemu);
+		if (features)
+			features[3] &= ~0x80;
+	}
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	if (data->io_id > 0)
+		g_source_remove(data->io_id);
+
+	free(data);
+}
+
+#define test_sco_full(name, data, setup, func, _disable_esco) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
+		user->io_id = 0; \
+		user->test_data = data; \
+		user->disable_esco = _disable_esco; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+#define test_sco(name, data, setup, func) \
+	test_sco_full(name, data, setup, func, false)
+
+#define test_sco_11(name, data, setup, func) \
+	test_sco_full(name, data, setup, func, true)
+
+static const struct sco_client_data connect_success = {
+	.expect_err = 0
+};
+
+static const struct sco_client_data connect_failure = {
+	.expect_err = EOPNOTSUPP
+};
+
+static void client_connectable_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	if (opcode != BT_HCI_CMD_WRITE_SCAN_ENABLE)
+		return;
+
+	tester_print("Client set connectable status 0x%02x", status);
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data);
+	bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void setup_powered(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+					sizeof(param), param,
+					NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+					sizeof(param), param,
+					setup_powered_callback, NULL, NULL);
+}
+
+static void test_framework(const void *test_data)
+{
+	tester_test_passed();
+}
+
+static void test_socket(const void *test_data)
+{
+	int sk;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (sk < 0) {
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		tester_test_failed();
+		return;
+	}
+
+	close(sk);
+
+	tester_test_passed();
+}
+
+static void test_getsockopt(const void *test_data)
+{
+	int sk, err;
+	socklen_t len;
+	struct bt_voice voice;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (sk < 0) {
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		tester_test_failed();
+		return;
+	}
+
+	len = sizeof(voice);
+	memset(&voice, 0, len);
+
+	err = getsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &voice, &len);
+	if (err < 0) {
+		tester_warn("Can't get socket option : %s (%d)",
+							strerror(errno), errno);
+		tester_test_failed();
+		goto end;
+	}
+
+	if (voice.setting != BT_VOICE_CVSD_16BIT) {
+		tester_warn("Invalid voice setting");
+		tester_test_failed();
+		goto end;
+	}
+
+	tester_test_passed();
+
+end:
+	close(sk);
+}
+
+static void test_setsockopt(const void *test_data)
+{
+	int sk, err;
+	socklen_t len;
+	struct bt_voice voice;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (sk < 0) {
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		tester_test_failed();
+		goto end;
+	}
+
+
+	len = sizeof(voice);
+	memset(&voice, 0, len);
+
+	err = getsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &voice, &len);
+	if (err < 0) {
+		tester_warn("Can't get socket option : %s (%d)",
+							strerror(errno), errno);
+		tester_test_failed();
+		goto end;
+	}
+
+	if (voice.setting != BT_VOICE_CVSD_16BIT) {
+		tester_warn("Invalid voice setting");
+		tester_test_failed();
+		goto end;
+	}
+
+	memset(&voice, 0, sizeof(voice));
+	voice.setting = BT_VOICE_TRANSPARENT;
+
+	err = setsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice));
+	if (err < 0) {
+		tester_warn("Can't set socket option : %s (%d)",
+							strerror(errno), errno);
+		tester_test_failed();
+		goto end;
+	}
+
+	len = sizeof(voice);
+	memset(&voice, 0, len);
+
+	err = getsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &voice, &len);
+	if (err < 0) {
+		tester_warn("Can't get socket option : %s (%d)",
+							strerror(errno), errno);
+		tester_test_failed();
+		goto end;
+	}
+
+	if (voice.setting != BT_VOICE_TRANSPARENT) {
+		tester_warn("Invalid voice setting");
+		tester_test_failed();
+		goto end;
+	}
+
+	tester_test_passed();
+
+end:
+	close(sk);
+}
+
+static int create_sco_sock(struct test_data *data)
+{
+	const uint8_t *master_bdaddr;
+	struct sockaddr_sco addr;
+	int sk, err;
+
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK,
+								BTPROTO_SCO);
+	if (sk < 0) {
+		err = -errno;
+		tester_warn("Can't create socket: %s (%d)", strerror(errno),
+									errno);
+		return err;
+	}
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		return -ENODEV;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, (void *) master_bdaddr);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = -errno;
+		tester_warn("Can't bind socket: %s (%d)", strerror(errno),
+									errno);
+		close(sk);
+		return err;
+	}
+
+	return sk;
+}
+
+static int connect_sco_sock(struct test_data *data, int sk)
+{
+	const uint8_t *client_bdaddr;
+	struct sockaddr_sco addr;
+	int err;
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	if (!client_bdaddr) {
+		tester_warn("No client bdaddr");
+		return -ENODEV;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, (void *) client_bdaddr);
+
+	err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
+		err = -errno;
+		tester_warn("Can't connect socket: %s (%d)", strerror(errno),
+									errno);
+		return err;
+	}
+
+	return 0;
+}
+
+static gboolean sco_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct sco_client_data *scodata = data->test_data;
+	int err, sk_err, sk;
+	socklen_t len = sizeof(sk_err);
+
+	data->io_id = 0;
+
+	sk = g_io_channel_unix_get_fd(io);
+
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+		err = -errno;
+	else
+		err = -sk_err;
+
+	if (err < 0)
+		tester_warn("Connect failed: %s (%d)", strerror(-err), -err);
+	else
+		tester_print("Successfully connected");
+
+	if (-err != scodata->expect_err)
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+	return FALSE;
+}
+
+static void test_connect(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	GIOChannel *io;
+	int sk;
+
+	sk = create_sco_sock(data);
+	if (sk < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	if (connect_sco_sock(data, sk) < 0) {
+		close(sk);
+		tester_test_failed();
+		return;
+	}
+
+	io = g_io_channel_unix_new(sk);
+	g_io_channel_set_close_on_unref(io, TRUE);
+
+	data->io_id = g_io_add_watch(io, G_IO_OUT, sco_connect_cb, NULL);
+
+	g_io_channel_unref(io);
+
+	tester_print("Connect in progress");
+}
+
+static void test_connect_transp(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct sco_client_data *scodata = data->test_data;
+	int sk, err;
+	struct bt_voice voice;
+
+	sk = create_sco_sock(data);
+	if (sk < 0) {
+		tester_test_failed();
+		return;
+	}
+
+	memset(&voice, 0, sizeof(voice));
+	voice.setting = BT_VOICE_TRANSPARENT;
+
+	err = setsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice));
+	if (err < 0) {
+		tester_warn("Can't set socket option : %s (%d)",
+							strerror(errno), errno);
+		tester_test_failed();
+		goto end;
+	}
+
+	err = connect_sco_sock(data, sk);
+
+	tester_warn("Connect returned %s (%d), expected %s (%d)",
+			strerror(-err), -err,
+			strerror(scodata->expect_err), scodata->expect_err);
+
+	if (-err != scodata->expect_err)
+		tester_test_failed();
+	else
+		tester_test_passed();
+
+end:
+	close(sk);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_sco("Basic Framework - Success", NULL, setup_powered,
+							test_framework);
+
+	test_sco("Basic SCO Socket - Success", NULL, setup_powered,
+							test_socket);
+
+	test_sco("Basic SCO Get Socket Option - Success", NULL, setup_powered,
+							test_getsockopt);
+
+	test_sco("Basic SCO Set Socket Option - Success", NULL, setup_powered,
+							test_setsockopt);
+
+	test_sco("eSCO CVSD - Success", &connect_success, setup_powered,
+							test_connect);
+
+	test_sco("eSCO mSBC - Success", &connect_success, setup_powered,
+							test_connect_transp);
+
+	test_sco_11("SCO CVSD 1.1 - Success", &connect_success, setup_powered,
+							test_connect);
+
+	test_sco_11("SCO mSBC 1.1 - Failure", &connect_failure, setup_powered,
+							test_connect_transp);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/scotest.c b/bluez/repo/tools/scotest.c
new file mode 100644
index 0000000..596e403
--- /dev/null
+++ b/bluez/repo/tools/scotest.c
@@ -0,0 +1,521 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-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 <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sco.h"
+
+#include "src/shared/util.h"
+
+/* Test modes */
+enum {
+	SEND,
+	RECV,
+	RECONNECT,
+	MULTY,
+	DUMP,
+	CONNECT
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 672;
+
+static bdaddr_t bdaddr;
+
+static int defer_setup = 0;
+static int voice = 0;
+
+static float tv2fl(struct timeval tv)
+{
+	return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static int do_connect(char *svr)
+{
+	struct sockaddr_sco addr;
+	struct sco_conninfo conn;
+	socklen_t optlen;
+	int sk;
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		return -1;
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, &bdaddr);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	if (voice) {
+		struct bt_voice opts;
+
+		/* SCO voice setting */
+		memset(&opts, 0, sizeof(opts));
+		opts.setting = voice;
+		if (setsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) < 0) {
+			syslog(LOG_ERR,
+				"Can't set voice socket option: %s (%d)",
+				strerror(errno), errno);
+			goto error;
+		}
+	}
+
+	/* Connect to remote device */
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	str2ba(svr, &addr.sco_bdaddr);
+
+	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't connect: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Get connection information */
+	memset(&conn, 0, sizeof(conn));
+	optlen = sizeof(conn);
+
+	if (getsockopt(sk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
+		syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
+		conn.hci_handle,
+		conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+	return sk;
+
+error:
+	close(sk);
+	return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+	struct sockaddr_sco addr;
+	struct sco_conninfo conn;
+	socklen_t optlen;
+	int sk, nsk;
+	char ba[18];
+
+	/* Create socket */
+	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+	if (sk < 0) {
+		syslog(LOG_ERR, "Can't create socket: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	/* Bind to local address */
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, &bdaddr);
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Enable deferred setup */
+	if (defer_setup && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+				&defer_setup, sizeof(defer_setup)) < 0) {
+		syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	/* Listen for connections */
+	if (listen(sk, 10)) {
+		syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+							strerror(errno), errno);
+		goto error;
+	}
+
+	syslog(LOG_INFO,"Waiting for connection ...");
+
+	while (1) {
+		memset(&addr, 0, sizeof(addr));
+		optlen = sizeof(addr);
+
+		nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+		if (nsk < 0) {
+			syslog(LOG_ERR,"Accept failed: %s (%d)",
+							strerror(errno), errno);
+			goto error;
+		}
+		if (fork()) {
+			/* Parent */
+			close(nsk);
+			continue;
+		}
+		/* Child */
+		close(sk);
+
+		/* Get connection information */
+		memset(&conn, 0, sizeof(conn));
+		optlen = sizeof(conn);
+
+		if (getsockopt(nsk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
+			syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
+							strerror(errno), errno);
+			if (!defer_setup) {
+				close(nsk);
+				exit(1);
+			}
+		}
+
+		ba2str(&addr.sco_bdaddr, ba);
+		syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
+			ba, conn.hci_handle,
+			conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+		/* Handle deferred setup */
+		if (defer_setup) {
+			syslog(LOG_INFO, "Waiting for %d seconds",
+							abs(defer_setup) - 1);
+			sleep(abs(defer_setup) - 1);
+
+			if (defer_setup < 0) {
+				close(nsk);
+				exit(1);
+			}
+		}
+
+		handler(nsk);
+
+		syslog(LOG_INFO, "Disconnect");
+		exit(0);
+	}
+
+error:
+	close(sk);
+	exit(1);
+}
+
+static void dump_mode(int sk)
+{
+	struct bt_voice opts;
+	int len;
+
+	/* SCO voice setting */
+	memset(&opts, 0, sizeof(opts));
+	opts.setting = voice;
+	if (setsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) < 0)
+		syslog(LOG_ERR, "Can't set socket options: %s (%d)",
+							strerror(errno), errno);
+
+	if (defer_setup) {
+		len = read(sk, buf, data_size);
+		if (len < 0)
+			syslog(LOG_ERR, "Initial read error: %s (%d)",
+						strerror(errno), errno);
+		else
+			syslog(LOG_INFO, "Initial bytes %d", len);
+	}
+
+	syslog(LOG_INFO,"Receiving ...");
+	while ((len = read(sk, buf, data_size)) > 0)
+		syslog(LOG_INFO, "Recevied %d bytes", len);
+}
+
+static void recv_mode(int sk)
+{
+	struct timeval tv_beg,tv_end,tv_diff;
+	struct bt_voice opts;
+	long total;
+	int len;
+
+	/* SCO voice setting */
+	memset(&opts, 0, sizeof(opts));
+	opts.setting = voice;
+	if (setsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) < 0)
+		syslog(LOG_ERR, "Can't set socket options: %s (%d)",
+							strerror(errno), errno);
+
+	if (defer_setup) {
+		len = read(sk, buf, data_size);
+		if (len < 0)
+			syslog(LOG_ERR, "Initial read error: %s (%d)",
+						strerror(errno), errno);
+		else
+			syslog(LOG_INFO, "Initial bytes %d", len);
+	}
+
+	syslog(LOG_INFO, "Receiving ...");
+
+	while (1) {
+		gettimeofday(&tv_beg, NULL);
+		total = 0;
+		while (total < data_size) {
+			int r;
+			if ((r = recv(sk, buf, data_size, 0)) <= 0) {
+				if (r < 0)
+					syslog(LOG_ERR, "Read failed: %s (%d)",
+							strerror(errno), errno);
+				if (errno != ENOTCONN)
+					return;
+				r = 0;
+			}
+			total += r;
+		}
+		gettimeofday(&tv_end, NULL);
+
+		timersub(&tv_end, &tv_beg, &tv_diff);
+
+		syslog(LOG_INFO,"%ld bytes in %.2fm speed %.2f kb", total,
+			tv2fl(tv_diff) / 60.0,
+			(float)( total / tv2fl(tv_diff) ) / 1024.0 );
+	}
+}
+
+static void send_mode(char *svr)
+{
+	struct sco_options so;
+	socklen_t len;
+	uint32_t seq;
+	int i, sk;
+
+	if ((sk = do_connect(svr)) < 0) {
+		syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	len = sizeof(so);
+	if (getsockopt(sk, SOL_SCO, SCO_OPTIONS, &so, &len) < 0) {
+		syslog(LOG_ERR, "Can't get SCO options: %s (%d)",
+							strerror(errno), errno);
+		exit(1);
+	}
+
+	syslog(LOG_INFO,"Sending ...");
+
+	for (i = 6; i < so.mtu; i++)
+		buf[i] = 0x7f;
+
+	seq = 0;
+	while (1) {
+		put_le32(seq, buf);
+		put_le16(data_size, buf + 4);
+
+		seq++;
+
+		if (send(sk, buf, so.mtu, 0) <= 0) {
+			syslog(LOG_ERR, "Send failed: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		usleep(1);
+	}
+}
+
+static void reconnect_mode(char *svr)
+{
+	while (1) {
+		int sk;
+
+		if ((sk = do_connect(svr)) < 0) {
+			syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+							strerror(errno), errno);
+			exit(1);
+		}
+
+		close(sk);
+
+		sleep(5);
+	}
+}
+
+static void multy_connect_mode(char *svr)
+{
+	while (1) {
+		int i, sk;
+
+		for (i = 0; i < 10; i++){
+			if (fork())
+				continue;
+
+			/* Child */
+			sk = do_connect(svr);
+			if (sk < 0) {
+				syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+							strerror(errno), errno);
+			}
+			close(sk);
+			exit(0);
+		}
+
+		sleep(19);
+	}
+}
+
+static void usage(void)
+{
+	printf("scotest - SCO testing\n"
+		"Usage:\n");
+	printf("\tscotest <mode> [options] [bd_addr]\n");
+	printf("Modes:\n"
+		"\t-d dump (server)\n"
+		"\t-c reconnect (client)\n"
+		"\t-m multiple connects (client)\n"
+		"\t-r receive (server)\n"
+		"\t-s connect and send (client)\n"
+		"\t-n connect and be silent (client)\n"
+		"Options:\n"
+		"\t[-b bytes]\n"
+		"\t[-W seconds] enable deferred setup\n"
+		"\t[-V voice] select SCO voice setting (0x0060 cvsd, 0x0003 transparent)\n");
+}
+
+int main(int argc ,char *argv[])
+{
+	struct sigaction sa;
+	int opt, sk, mode = RECV;
+
+	while ((opt = getopt(argc, argv, "rdscmnb:W:V:")) != EOF) {
+		switch(opt) {
+		case 'r':
+			mode = RECV;
+			break;
+
+		case 's':
+			mode = SEND;
+			break;
+
+		case 'd':
+			mode = DUMP;
+			break;
+
+		case 'c':
+			mode = RECONNECT;
+			break;
+
+		case 'm':
+			mode = MULTY;
+			break;
+
+		case 'n':
+			mode = CONNECT;
+			break;
+
+		case 'b':
+			data_size = atoi(optarg);
+			break;
+
+		case 'W':
+			defer_setup = atoi(optarg);
+			break;
+
+		case 'V':
+			voice = strtol(optarg, NULL, 0);
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+	}
+
+	if (!(argc - optind) && (mode != RECV && mode != DUMP)) {
+		usage();
+		exit(1);
+	}
+
+	if (!(buf = malloc(data_size))) {
+		perror("Can't allocate data buffer");
+		exit(1);
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = SIG_IGN;
+	sa.sa_flags   = SA_NOCLDSTOP;
+	sigaction(SIGCHLD, &sa, NULL);
+
+	openlog("scotest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+	switch( mode ){
+		case RECV:
+			do_listen(recv_mode);
+			break;
+
+		case DUMP:
+			do_listen(dump_mode);
+			break;
+
+		case SEND:
+			send_mode(argv[optind]);
+			break;
+
+		case RECONNECT:
+			reconnect_mode(argv[optind]);
+			break;
+
+		case MULTY:
+			multy_connect_mode(argv[optind]);
+			break;
+
+		case CONNECT:
+			sk = do_connect(argv[optind]);
+			if (sk < 0)
+				exit(1);
+			dump_mode(sk);
+			break;
+	}
+
+	syslog(LOG_INFO, "Exit");
+
+	closelog();
+
+	return 0;
+}
diff --git a/bluez/repo/tools/sdptool.1 b/bluez/repo/tools/sdptool.1
new file mode 100644
index 0000000..ea95933
--- /dev/null
+++ b/bluez/repo/tools/sdptool.1
@@ -0,0 +1,132 @@
+.\" $Header$
+.\"
+.\"	transcript compatibility for postscript use.
+.\"
+.\"	synopsis:  .P! <file.ps>
+.\"
+.de P!
+.fl
+\!!1 setgray
+.fl
+\\&.\"
+.fl
+\!!0 setgray
+.fl			\" force out current output buffer
+\!!save /psv exch def currentpoint translate 0 0 moveto
+\!!/showpage{}def
+.fl			\" prolog
+.sy sed -e 's/^/!/' \\$1\" bring in postscript file
+\!!psv restore
+.
+.de pF
+.ie     \\*(f1 .ds f1 \\n(.f
+.el .ie \\*(f2 .ds f2 \\n(.f
+.el .ie \\*(f3 .ds f3 \\n(.f
+.el .ie \\*(f4 .ds f4 \\n(.f
+.el .tm ? font overflow
+.ft \\$1
+..
+.de fP
+.ie     !\\*(f4 \{\
+.	ft \\*(f4
+.	ds f4\"
+'	br \}
+.el .ie !\\*(f3 \{\
+.	ft \\*(f3
+.	ds f3\"
+'	br \}
+.el .ie !\\*(f2 \{\
+.	ft \\*(f2
+.	ds f2\"
+'	br \}
+.el .ie !\\*(f1 \{\
+.	ft \\*(f1
+.	ds f1\"
+'	br \}
+.el .tm ? font underflow
+..
+.ds f1\"
+.ds f2\"
+.ds f3\"
+.ds f4\"
+'\" t
+.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n
+.TH "sdptool" "1"
+.SH "NAME"
+sdptool \(em control and interrogate SDP servers
+.SH "SYNOPSIS"
+.PP
+\fBsdptool\fR [\fIoptions\fR]  {\fIcommand\fR}  [\fIcommand parameters\fR \&...]
+.SH "DESCRIPTION"
+.PP
+\fBsdptool\fR provides the interface for
+performing SDP queries on Bluetooth devices, and administering a
+local SDP database.
+.SH "COMMANDS"
+.PP
+The following commands are available.  In all cases \fBbdaddr\fR
+specifies the device to search or browse.  If \fIlocal\fP is used
+for \fBbdaddr\fP, then the local SDP database is searched.
+.PP
+Services are identified and manipulated with a 4-byte \fBrecord_handle\fP
+(NOT the service name).  To find a service's \fBrecord_handle\fP, look for the
+"Service RecHandle" line in the \fBsearch\fP or \fBbrowse\fP results
+.IP "\fBsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] service_name\fP" 10
+Search for services..
+.IP "" 10
+Known service names are DID, SP, DUN, LAN, FAX, OPUSH,
+FTP, HS, HF, HFAG, SAP, NAP, GN, PANU, HCRP, HID, CIP,
+A2SRC, A2SNK, AVRCT, AVRTG, UDIUE, UDITE and SYNCML.
+.IP "\fBbrowse [--tree] [--raw] [--xml] [bdaddr]\fP" 10
+Browse all available services on the device
+specified by a Bluetooth address as a parameter.
+.IP "\fBrecords [--tree] [--raw] [--xml] bdaddr\fP" 10
+Retrieve all possible service records.
+.IP "\fBadd [ --handle=N --channel=N ]\fP" 10
+Add a service to the local
+SDP database.
+.IP "" 10
+You can specify a handle for this record using
+the \fB--handle\fP option.
+.IP "" 10
+You can specify a channel to add the service on
+using the \fB--channel\fP option.
+.IP "" 10
+NOTE: Local adapters configuration will not be updated and this command should
+be used only for SDP testing.
+.IP "\fBdel record_handle\fP" 10
+Remove a service from the local
+SDP database.
+.IP "" 10
+NOTE: Local adapters configuration will not be updated and this command should
+be used only for SDP testing.
+.IP "\fBget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\fP" 10
+Retrieve a service from the local
+SDP database.
+.IP "\fBsetattr record_handle attrib_id attrib_value\fP" 10
+Set or add an attribute to an SDP record.
+
+.IP "\fBsetseq record_handle attrib_id attrib_values\fP" 10
+Set or add an attribute sequence to an
+SDP record.
+.SH "OPTIONS"
+.IP "\fB--help\fP" 10
+Displays help on using sdptool.
+
+.SH "EXAMPLES"
+.PP
+sdptool browse 00:80:98:24:15:6D
+.PP
+sdptool browse local
+.PP
+sdptool add DUN
+.PP
+sdptool del 0x10000
+.SH "BUGS"
+.PP
+Documentation needs improving.
+.SH "AUTHOR"
+.PP
+Maxim Krasnyansky <maxk@qualcomm.com>. Man page written
+by Edd Dumbill <ejad@debian.org>.
+.\" created by instant / docbook-to-man, Thu 15 Jan 2004, 21:01
diff --git a/bluez/repo/tools/sdptool.c b/bluez/repo/tools/sdptool.c
new file mode 100644
index 0000000..b1cbcfd
--- /dev/null
+++ b/bluez/repo/tools/sdptool.c
@@ -0,0 +1,4416 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *  Copyright (C) 2002-2003  Jean Tourrilhes <jt@hpl.hp.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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "src/sdp-xml.h"
+
+#ifndef APPLE_AGENT_SVCLASS_ID
+#define APPLE_AGENT_SVCLASS_ID 0x2112
+#endif
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
+#define N_ELEMENTS(x) (sizeof(x) / sizeof((x)[0]))
+
+/*
+ * Convert a string to a BDADDR, with a few "enhancements" - Jean II
+ */
+static int estr2ba(char *str, bdaddr_t *ba)
+{
+	/* Only trap "local", "any" is already dealt with */
+	if(!strcmp(str, "local")) {
+		bacpy(ba, BDADDR_LOCAL);
+		return 0;
+	}
+	return str2ba(str, ba);
+}
+
+#define DEFAULT_VIEW	0	/* Display only known attribute */
+#define TREE_VIEW	1	/* Display full attribute tree */
+#define RAW_VIEW	2	/* Display raw tree */
+#define XML_VIEW	3	/* Display xml tree */
+
+/* Pass args to the inquiry/search handler */
+struct search_context {
+	char		*svc;		/* Service */
+	uuid_t		group;		/* Browse group */
+	int		view;		/* View mode */
+	uint32_t	handle;		/* Service record handle */
+};
+
+typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg);
+
+static char UUID_str[MAX_LEN_UUID_STR];
+static bdaddr_t interface;
+
+/* Definition of attribute members */
+struct member_def {
+	char *name;
+};
+
+/* Definition of an attribute */
+struct attrib_def {
+	int			num;		/* Numeric ID - 16 bits */
+	char			*name;		/* User readable name */
+	struct member_def	*members;	/* Definition of attribute args */
+	int			member_max;	/* Max of attribute arg definitions */
+};
+
+/* Definition of a service or protocol */
+struct uuid_def {
+	int			num;		/* Numeric ID - 16 bits */
+	char			*name;		/* User readable name */
+	struct attrib_def	*attribs;	/* Specific attribute definitions */
+	int			attrib_max;	/* Max of attribute definitions */
+};
+
+/* Context information about current attribute */
+struct attrib_context {
+	struct uuid_def		*service;	/* Service UUID, if known */
+	struct attrib_def	*attrib;	/* Description of the attribute */
+	int			member_index;	/* Index of current attribute member */
+};
+
+/* Context information about the whole service */
+struct service_context {
+	struct uuid_def		*service;	/* Service UUID, if known */
+};
+
+/* Allow us to do nice formatting of the lists */
+static char *indent_spaces = "                                         ";
+
+/* ID of the service attribute.
+ * Most attributes after 0x200 are defined based on the service, so
+ * we need to find what is the service (which is messy) - Jean II */
+#define SERVICE_ATTR	0x1
+
+/* Definition of the optional arguments in protocol list */
+static struct member_def protocol_members[] = {
+	{ "Protocol"		},
+	{ "Channel/Port"	},
+	{ "Version"		},
+};
+
+/* Definition of the optional arguments in profile list */
+static struct member_def profile_members[] = {
+	{ "Profile"	},
+	{ "Version"	},
+};
+
+/* Definition of the optional arguments in Language list */
+static struct member_def language_members[] = {
+	{ "Code ISO639"		},
+	{ "Encoding"		},
+	{ "Base Offset"		},
+};
+
+/* Name of the various common attributes. See BT assigned numbers */
+static struct attrib_def attrib_names[] = {
+	{ 0x0, "ServiceRecordHandle", NULL, 0 },
+	{ 0x1, "ServiceClassIDList", NULL, 0 },
+	{ 0x2, "ServiceRecordState", NULL, 0 },
+	{ 0x3, "ServiceID", NULL, 0 },
+	{ 0x4, "ProtocolDescriptorList",
+		protocol_members, N_ELEMENTS(protocol_members) },
+	{ 0x5, "BrowseGroupList", NULL, 0 },
+	{ 0x6, "LanguageBaseAttributeIDList",
+		language_members, N_ELEMENTS(language_members) },
+	{ 0x7, "ServiceInfoTimeToLive", NULL, 0 },
+	{ 0x8, "ServiceAvailability", NULL, 0 },
+	{ 0x9, "BluetoothProfileDescriptorList",
+		profile_members, N_ELEMENTS(profile_members) },
+	{ 0xA, "DocumentationURL", NULL, 0 },
+	{ 0xB, "ClientExecutableURL", NULL, 0 },
+	{ 0xC, "IconURL", NULL, 0 },
+	{ 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 },
+	/* Definitions after that are tricky (per profile or offset) */
+};
+
+const int attrib_max = N_ELEMENTS(attrib_names);
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def sdp_attrib_names[] = {
+	{ 0x200, "VersionNumberList", NULL, 0 },
+	{ 0x201, "ServiceDatabaseState", NULL, 0 },
+};
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def browse_attrib_names[] = {
+	{ 0x200, "GroupID", NULL, 0 },
+};
+
+/* Name of the various Device ID attributes. See Device Id spec. */
+static struct attrib_def did_attrib_names[] = {
+	{ 0x200, "SpecificationID", NULL, 0 },
+	{ 0x201, "VendorID", NULL, 0 },
+	{ 0x202, "ProductID", NULL, 0 },
+	{ 0x203, "Version", NULL, 0 },
+	{ 0x204, "PrimaryRecord", NULL, 0 },
+	{ 0x205, "VendorIDSource", NULL, 0 },
+};
+
+/* Name of the various HID attributes. See HID spec. */
+static struct attrib_def hid_attrib_names[] = {
+	{ 0x200, "DeviceReleaseNum", NULL, 0 },
+	{ 0x201, "ParserVersion", NULL, 0 },
+	{ 0x202, "DeviceSubclass", NULL, 0 },
+	{ 0x203, "CountryCode", NULL, 0 },
+	{ 0x204, "VirtualCable", NULL, 0 },
+	{ 0x205, "ReconnectInitiate", NULL, 0 },
+	{ 0x206, "DescriptorList", NULL, 0 },
+	{ 0x207, "LangIDBaseList", NULL, 0 },
+	{ 0x208, "SDPDisable", NULL, 0 },
+	{ 0x209, "BatteryPower", NULL, 0 },
+	{ 0x20a, "RemoteWakeup", NULL, 0 },
+	{ 0x20b, "ProfileVersion", NULL, 0 },
+	{ 0x20c, "SupervisionTimeout", NULL, 0 },
+	{ 0x20d, "NormallyConnectable", NULL, 0 },
+	{ 0x20e, "BootDevice", NULL, 0 },
+};
+
+/* Name of the various PAN attributes. See BT assigned numbers */
+/* Note : those need to be double checked - Jean II */
+static struct attrib_def pan_attrib_names[] = {
+	{ 0x200, "IpSubnet", NULL, 0 },		/* Obsolete ??? */
+	{ 0x30A, "SecurityDescription", NULL, 0 },
+	{ 0x30B, "NetAccessType", NULL, 0 },
+	{ 0x30C, "MaxNetAccessrate", NULL, 0 },
+	{ 0x30D, "IPv4Subnet", NULL, 0 },
+	{ 0x30E, "IPv6Subnet", NULL, 0 },
+};
+
+/* Name of the various Generic-Audio attributes. See BT assigned numbers */
+/* Note : totally untested - Jean II */
+static struct attrib_def audio_attrib_names[] = {
+	{ 0x302, "Remote audio volume control", NULL, 0 },
+};
+
+/* Name of the various IrMCSync attributes. See BT assigned numbers */
+static struct attrib_def irmc_attrib_names[] = {
+	{ 0x0301, "SupportedDataStoresList", NULL, 0 },
+};
+
+/* Name of the various GOEP attributes. See BT assigned numbers */
+static struct attrib_def goep_attrib_names[] = {
+	{ 0x200, "GoepL2capPsm", NULL, 0 },
+};
+
+/* Name of the various PBAP attributes. See BT assigned numbers */
+static struct attrib_def pbap_attrib_names[] = {
+	{ 0x0314, "SupportedRepositories", NULL, 0 },
+	{ 0x0317, "PbapSupportedFeatures", NULL, 0 },
+};
+
+/* Name of the various MAS attributes. See BT assigned numbers */
+static struct attrib_def mas_attrib_names[] = {
+	{ 0x0315, "MASInstanceID", NULL, 0 },
+	{ 0x0316, "SupportedMessageTypes", NULL, 0 },
+	{ 0x0317, "MapSupportedFeatures", NULL, 0 },
+};
+
+/* Name of the various MNS attributes. See BT assigned numbers */
+static struct attrib_def mns_attrib_names[] = {
+	{ 0x0317, "MapSupportedFeatures", NULL, 0 },
+};
+
+/* Same for the UUIDs. See BT assigned numbers */
+static struct uuid_def uuid16_names[] = {
+	/* -- Protocols -- */
+	{ 0x0001, "SDP", NULL, 0 },
+	{ 0x0002, "UDP", NULL, 0 },
+	{ 0x0003, "RFCOMM", NULL, 0 },
+	{ 0x0004, "TCP", NULL, 0 },
+	{ 0x0005, "TCS-BIN", NULL, 0 },
+	{ 0x0006, "TCS-AT", NULL, 0 },
+	{ 0x0008, "OBEX", NULL, 0 },
+	{ 0x0009, "IP", NULL, 0 },
+	{ 0x000a, "FTP", NULL, 0 },
+	{ 0x000c, "HTTP", NULL, 0 },
+	{ 0x000e, "WSP", NULL, 0 },
+	{ 0x000f, "BNEP", NULL, 0 },
+	{ 0x0010, "UPnP/ESDP", NULL, 0 },
+	{ 0x0011, "HIDP", NULL, 0 },
+	{ 0x0012, "HardcopyControlChannel", NULL, 0 },
+	{ 0x0014, "HardcopyDataChannel", NULL, 0 },
+	{ 0x0016, "HardcopyNotification", NULL, 0 },
+	{ 0x0017, "AVCTP", NULL, 0 },
+	{ 0x0019, "AVDTP", NULL, 0 },
+	{ 0x001b, "CMTP", NULL, 0 },
+	{ 0x001d, "UDI_C-Plane", NULL, 0 },
+	{ 0x0100, "L2CAP", NULL, 0 },
+	/* -- Services -- */
+	{ 0x1000, "ServiceDiscoveryServerServiceClassID",
+		sdp_attrib_names, N_ELEMENTS(sdp_attrib_names) },
+	{ 0x1001, "BrowseGroupDescriptorServiceClassID",
+		browse_attrib_names, N_ELEMENTS(browse_attrib_names) },
+	{ 0x1002, "PublicBrowseGroup", NULL, 0 },
+	{ 0x1101, "SerialPort", NULL, 0 },
+	{ 0x1102, "LANAccessUsingPPP", NULL, 0 },
+	{ 0x1103, "DialupNetworking (DUN)", NULL, 0 },
+	{ 0x1104, "IrMCSync",
+		irmc_attrib_names, N_ELEMENTS(irmc_attrib_names) },
+	{ 0x1105, "OBEXObjectPush",
+		goep_attrib_names, N_ELEMENTS(goep_attrib_names) },
+	{ 0x1106, "OBEXFileTransfer",
+		goep_attrib_names, N_ELEMENTS(goep_attrib_names) },
+	{ 0x1107, "IrMCSyncCommand", NULL, 0 },
+	{ 0x1108, "Headset",
+		audio_attrib_names, N_ELEMENTS(audio_attrib_names) },
+	{ 0x1109, "CordlessTelephony", NULL, 0 },
+	{ 0x110a, "AudioSource", NULL, 0 },
+	{ 0x110b, "AudioSink", NULL, 0 },
+	{ 0x110c, "RemoteControlTarget", NULL, 0 },
+	{ 0x110d, "AdvancedAudio", NULL, 0 },
+	{ 0x110e, "RemoteControl", NULL, 0 },
+	{ 0x110f, "RemoteControlController", NULL, 0 },
+	{ 0x1110, "Intercom", NULL, 0 },
+	{ 0x1111, "Fax", NULL, 0 },
+	{ 0x1112, "HeadsetAudioGateway", NULL, 0 },
+	{ 0x1113, "WAP", NULL, 0 },
+	{ 0x1114, "WAP Client", NULL, 0 },
+	{ 0x1115, "PANU (PAN/BNEP)",
+		pan_attrib_names, N_ELEMENTS(pan_attrib_names) },
+	{ 0x1116, "NAP (PAN/BNEP)",
+		pan_attrib_names, N_ELEMENTS(pan_attrib_names) },
+	{ 0x1117, "GN (PAN/BNEP)",
+		pan_attrib_names, N_ELEMENTS(pan_attrib_names) },
+	{ 0x1118, "DirectPrinting (BPP)", NULL, 0 },
+	{ 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
+	{ 0x111a, "Imaging (BIP)", NULL, 0 },
+	{ 0x111b, "ImagingResponder (BIP)", NULL, 0 },
+	{ 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
+	{ 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
+	{ 0x111e, "Handsfree", NULL, 0 },
+	{ 0x111f, "HandsfreeAudioGateway", NULL, 0 },
+	{ 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
+	{ 0x1121, "ReflectedUI (BPP)", NULL, 0 },
+	{ 0x1122, "BasicPrinting (BPP)", NULL, 0 },
+	{ 0x1123, "PrintingStatus (BPP)", NULL, 0 },
+	{ 0x1124, "HumanInterfaceDeviceService (HID)",
+		hid_attrib_names, N_ELEMENTS(hid_attrib_names) },
+	{ 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 },
+	{ 0x1126, "HCR_Print (HCR)", NULL, 0 },
+	{ 0x1127, "HCR_Scan (HCR)", NULL, 0 },
+	{ 0x1128, "Common ISDN Access (CIP)", NULL, 0 },
+	{ 0x112a, "UDI-MT", NULL, 0 },
+	{ 0x112b, "UDI-TA", NULL, 0 },
+	{ 0x112c, "Audio/Video", NULL, 0 },
+	{ 0x112d, "SIM Access (SAP)", NULL, 0 },
+	{ 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 },
+	{ 0x112f, "Phonebook Access (PBAP) - PSE",
+		pbap_attrib_names, N_ELEMENTS(pbap_attrib_names) },
+	{ 0x1130, "Phonebook Access (PBAP)", NULL, 0 },
+	{ 0x1131, "Headset (HSP)", NULL, 0 },
+	{ 0x1132, "Message Access (MAP) - MAS",
+		mas_attrib_names, N_ELEMENTS(mas_attrib_names) },
+	{ 0x1133, "Message Access (MAP) - MNS",
+		mns_attrib_names, N_ELEMENTS(mns_attrib_names) },
+	{ 0x1134, "Message Access (MAP)", NULL, 0 },
+	/* ... */
+	{ 0x1200, "PnPInformation",
+		did_attrib_names, N_ELEMENTS(did_attrib_names) },
+	{ 0x1201, "GenericNetworking", NULL, 0 },
+	{ 0x1202, "GenericFileTransfer", NULL, 0 },
+	{ 0x1203, "GenericAudio",
+		audio_attrib_names, N_ELEMENTS(audio_attrib_names) },
+	{ 0x1204, "GenericTelephony", NULL, 0 },
+	/* ... */
+	{ 0x1303, "VideoSource", NULL, 0 },
+	{ 0x1304, "VideoSink", NULL, 0 },
+	{ 0x1305, "VideoDistribution", NULL, 0 },
+	{ 0x1400, "HDP", NULL, 0 },
+	{ 0x1401, "HDPSource", NULL, 0 },
+	{ 0x1402, "HDPSink", NULL, 0 },
+	{ 0x2112, "AppleAgent", NULL, 0 },
+};
+
+static const int uuid16_max = N_ELEMENTS(uuid16_names);
+
+static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int);
+
+/*
+ * Parse a UUID.
+ * The BT assigned numbers only list UUID16, so I'm not sure the
+ * other types will ever get used...
+ */
+static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent)
+{
+	if (uuid) {
+		if (uuid->type == SDP_UUID16) {
+			uint16_t uuidNum = uuid->value.uuid16;
+			struct uuid_def *uuidDef = NULL;
+			int i;
+
+			for (i = 0; i < uuid16_max; i++)
+				if (uuid16_names[i].num == uuidNum) {
+					uuidDef = &uuid16_names[i];
+					break;
+				}
+
+			/* Check if it's the service attribute */
+			if (context->attrib && context->attrib->num == SERVICE_ATTR) {
+				/* We got the service ID !!! */
+				context->service = uuidDef;
+			}
+
+			if (uuidDef)
+				printf("%.*sUUID16 : 0x%.4x - %s\n",
+					indent, indent_spaces, uuidNum, uuidDef->name);
+			else
+				printf("%.*sUUID16 : 0x%.4x\n",
+					indent, indent_spaces, uuidNum);
+		} else if (uuid->type == SDP_UUID32) {
+			struct uuid_def *uuidDef = NULL;
+			int i;
+
+			if (!(uuid->value.uuid32 & 0xffff0000)) {
+				uint16_t uuidNum = uuid->value.uuid32;
+				for (i = 0; i < uuid16_max; i++)
+					if (uuid16_names[i].num == uuidNum) {
+						uuidDef = &uuid16_names[i];
+						break;
+					}
+			}
+
+			if (uuidDef)
+				printf("%.*sUUID32 : 0x%.8x - %s\n",
+					indent, indent_spaces, uuid->value.uuid32, uuidDef->name);
+			else
+				printf("%.*sUUID32 : 0x%.8x\n",
+					indent, indent_spaces, uuid->value.uuid32);
+		} else if (uuid->type == SDP_UUID128) {
+			unsigned int data0;
+			unsigned short data1;
+			unsigned short data2;
+			unsigned short data3;
+			unsigned int data4;
+			unsigned short data5;
+
+			memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+			memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+			memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+			memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+			memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+			memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+			printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n",
+				indent, indent_spaces,
+				ntohl(data0), ntohs(data1), ntohs(data2),
+				ntohs(data3), ntohl(data4), ntohs(data5));
+		} else
+			printf("%.*sEnum type of UUID not set\n",
+				indent, indent_spaces);
+	} else
+		printf("%.*sNull passed to print UUID\n",
+				indent, indent_spaces);
+}
+
+/*
+ * Parse a sequence of data elements (i.e. a list)
+ */
+static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent)
+{
+	sdp_data_t *sdpdata = NULL;
+
+	sdpdata = pData;
+	if (sdpdata) {
+		context->member_index = 0;
+		do {
+			sdp_data_printf(sdpdata, context, indent + 2);
+			sdpdata = sdpdata->next;
+			context->member_index++;
+		} while (sdpdata);
+	} else {
+		printf("%.*sBroken dataseq link\n", indent, indent_spaces);
+	}
+}
+
+/*
+ * Parse a single data element (either in the attribute or in a data
+ * sequence).
+ */
+static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent)
+{
+	char *member_name = NULL;
+
+	/* Find member name. Almost black magic ;-) */
+	if (context && context->attrib && context->attrib->members &&
+			context->member_index < context->attrib->member_max) {
+		member_name = context->attrib->members[context->member_index].name;
+	}
+
+	switch (sdpdata->dtd) {
+	case SDP_DATA_NIL:
+		printf("%.*sNil\n", indent, indent_spaces);
+		break;
+	case SDP_BOOL:
+	case SDP_UINT8:
+	case SDP_UINT16:
+	case SDP_UINT32:
+	case SDP_UINT64:
+	case SDP_UINT128:
+	case SDP_INT8:
+	case SDP_INT16:
+	case SDP_INT32:
+	case SDP_INT64:
+	case SDP_INT128:
+		if (member_name) {
+			printf("%.*s%s (Integer) : 0x%x\n",
+				indent, indent_spaces, member_name, sdpdata->val.uint32);
+		} else {
+			printf("%.*sInteger : 0x%x\n", indent, indent_spaces,
+				sdpdata->val.uint32);
+		}
+		break;
+
+	case SDP_UUID16:
+	case SDP_UUID32:
+	case SDP_UUID128:
+		//printf("%.*sUUID\n", indent, indent_spaces);
+		sdp_uuid_printf(&sdpdata->val.uuid, context, indent);
+		break;
+
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+		if (sdpdata->unitSize > (int) strlen(sdpdata->val.str)) {
+			int i;
+			printf("%.*sData :", indent, indent_spaces);
+			for (i = 0; i < sdpdata->unitSize; i++)
+				printf(" %02x", (unsigned char) sdpdata->val.str[i]);
+			printf("\n");
+		} else
+			printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str);
+		break;
+
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		printf("%.*sData Sequence\n", indent, indent_spaces);
+		printf_dataseq(sdpdata->val.dataseq, context, indent);
+		break;
+
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		printf("%.*sData Sequence Alternates\n", indent, indent_spaces);
+		printf_dataseq(sdpdata->val.dataseq, context, indent);
+		break;
+	}
+}
+
+/*
+ * Parse a single attribute.
+ */
+static void print_tree_attr_func(void *value, void *userData)
+{
+	sdp_data_t *sdpdata = value;
+	uint16_t attrId;
+	struct service_context *service = (struct service_context *) userData;
+	struct attrib_context context;
+	struct attrib_def *attrDef = NULL;
+	int i;
+
+	if (!sdpdata)
+		return;
+
+	attrId = sdpdata->attrId;
+	/* Search amongst the generic attributes */
+	for (i = 0; i < attrib_max; i++)
+		if (attrib_names[i].num == attrId) {
+			attrDef = &attrib_names[i];
+			break;
+		}
+	/* Search amongst the specific attributes of this service */
+	if ((attrDef == NULL) && (service->service != NULL) &&
+				(service->service->attribs != NULL)) {
+		struct attrib_def *svc_attribs = service->service->attribs;
+		int		svc_attrib_max = service->service->attrib_max;
+		for (i = 0; i < svc_attrib_max; i++)
+			if (svc_attribs[i].num == attrId) {
+				attrDef = &svc_attribs[i];
+				break;
+			}
+	}
+
+	if (attrDef)
+		printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name);
+	else
+		printf("Attribute Identifier : 0x%x\n", attrId);
+	/* Build context */
+	context.service = service->service;
+	context.attrib = attrDef;
+	context.member_index = 0;
+	/* Parse attribute members */
+	sdp_data_printf(sdpdata, &context, 2);
+	/* Update service */
+	service->service = context.service;
+}
+
+/*
+ * Main entry point of this library. Parse a SDP record.
+ * We assume the record has already been read, parsed and cached
+ * locally. Jean II
+ */
+static void print_tree_attr(sdp_record_t *rec)
+{
+	if (rec && rec->attrlist) {
+		struct service_context service = { NULL };
+		sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
+	}
+}
+
+static void print_raw_data(sdp_data_t *data, int indent)
+{
+	struct uuid_def *def;
+	int i, hex;
+
+	if (!data)
+		return;
+
+	for (i = 0; i < indent; i++)
+		printf("\t");
+
+	switch (data->dtd) {
+	case SDP_DATA_NIL:
+		printf("NIL\n");
+		break;
+	case SDP_BOOL:
+		printf("Bool %s\n", data->val.uint8 ? "True" : "False");
+		break;
+	case SDP_UINT8:
+		printf("UINT8 0x%02x\n", data->val.uint8);
+		break;
+	case SDP_UINT16:
+		printf("UINT16 0x%04x\n", data->val.uint16);
+		break;
+	case SDP_UINT32:
+		printf("UINT32 0x%08x\n", data->val.uint32);
+		break;
+	case SDP_UINT64:
+		printf("UINT64 0x%016jx\n", data->val.uint64);
+		break;
+	case SDP_UINT128:
+		printf("UINT128 ...\n");
+		break;
+	case SDP_INT8:
+		printf("INT8 %d\n", data->val.int8);
+		break;
+	case SDP_INT16:
+		printf("INT16 %d\n", data->val.int16);
+		break;
+	case SDP_INT32:
+		printf("INT32 %d\n", data->val.int32);
+		break;
+	case SDP_INT64:
+		printf("INT64 %jd\n", data->val.int64);
+		break;
+	case SDP_INT128:
+		printf("INT128 ...\n");
+		break;
+	case SDP_UUID16:
+	case SDP_UUID32:
+	case SDP_UUID128:
+		switch (data->val.uuid.type) {
+		case SDP_UUID16:
+			def = NULL;
+			for (i = 0; i < uuid16_max; i++)
+				if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
+					def = &uuid16_names[i];
+					break;
+				}
+			if (def)
+				printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
+			else
+				printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
+			break;
+		case SDP_UUID32:
+			def = NULL;
+			if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
+				uint16_t value = data->val.uuid.value.uuid32;
+				for (i = 0; i < uuid16_max; i++)
+					if (uuid16_names[i].num == value) {
+						def = &uuid16_names[i];
+						break;
+					}
+			}
+			if (def)
+				printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
+			else
+				printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
+			break;
+		case SDP_UUID128:
+			printf("UUID128 ");
+			for (i = 0; i < 16; i++) {
+				switch (i) {
+				case 4:
+				case 6:
+				case 8:
+				case 10:
+					printf("-");
+					break;
+				}
+				printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
+			}
+			printf("\n");
+			break;
+		default:
+			printf("UUID type 0x%02x\n", data->val.uuid.type);
+			break;
+		}
+		break;
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+		hex = 0;
+		for (i = 0; i < data->unitSize; i++) {
+			if (i == (data->unitSize - 1) && data->val.str[i] == '\0')
+				break;
+			if (!isprint(data->val.str[i])) {
+				hex = 1;
+				break;
+			}
+		}
+		if (hex) {
+			printf("Data");
+			for (i = 0; i < data->unitSize; i++)
+				printf(" %02x", (unsigned char) data->val.str[i]);
+		} else {
+			printf("String ");
+			for (i = 0; i < data->unitSize; i++)
+				printf("%c", data->val.str[i]);
+		}
+		printf("\n");
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		printf("URL %s\n", data->val.str);
+		break;
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32:
+		printf("Sequence\n");
+		print_raw_data(data->val.dataseq, indent + 1);
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+		printf("Alternate\n");
+		print_raw_data(data->val.dataseq, indent + 1);
+		break;
+	default:
+		printf("Unknown type 0x%02x\n", data->dtd);
+		break;
+	}
+
+	print_raw_data(data->next, indent);
+}
+
+static void print_raw_attr_func(void *value, void *userData)
+{
+	sdp_data_t *data = (sdp_data_t *) value;
+	struct attrib_def *def = NULL;
+	int i;
+
+	if (!data)
+		return;
+
+	/* Search amongst the generic attributes */
+	for (i = 0; i < attrib_max; i++)
+		if (attrib_names[i].num == data->attrId) {
+			def = &attrib_names[i];
+			break;
+		}
+
+	if (def)
+		printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
+	else
+		printf("\tAttribute 0x%04x\n", data->attrId);
+
+	print_raw_data(data, 2);
+}
+
+static void print_raw_attr(sdp_record_t *rec)
+{
+	if (rec && rec->attrlist) {
+		printf("Sequence\n");
+		sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
+	}
+}
+
+/*
+ * Set attributes with single values in SDP record
+ * Jean II
+ */
+static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value)
+{
+	sdp_list_t *attrid_list;
+	uint32_t range = 0x0000ffff;
+	sdp_record_t *rec;
+	int ret;
+
+	/* Get the old SDP record */
+	attrid_list = sdp_list_append(NULL, &range);
+	rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+	sdp_list_free(attrid_list, NULL);
+
+	if (!rec) {
+		printf("Service get request failed.\n");
+		return -1;
+	}
+
+	/* Check the type of attribute */
+	if (!strncasecmp(value, "u0x", 3)) {
+		/* UUID16 */
+		uint16_t value_int = 0;
+		uuid_t value_uuid;
+		value_int = strtoul(value + 3, NULL, 16);
+		sdp_uuid16_create(&value_uuid, value_int);
+		printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n",
+			attrib, value_int, handle);
+
+		sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16);
+	} else if (!strncasecmp(value, "0x", 2)) {
+		/* Int */
+		uint32_t value_int;
+		value_int = strtoul(value + 2, NULL, 16);
+		printf("Adding attrib 0x%X int 0x%X to record 0x%X\n",
+			attrib, value_int, handle);
+
+		sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int);
+	} else {
+		/* String */
+		printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n",
+			attrib, value, handle);
+
+		/* Add/Update our attribute to the record */
+		sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value);
+	}
+
+	/* Update on the server */
+	ret = sdp_device_record_update(sess, &interface, rec);
+	if (ret < 0)
+		printf("Service Record update failed (%d).\n", errno);
+	sdp_record_free(rec);
+	return ret;
+}
+
+static struct option set_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *set_help =
+	"Usage:\n"
+	"\tget record_handle attrib_id attrib_value\n";
+
+/*
+ * Add an attribute to an existing SDP record on the local SDP server
+ */
+static int cmd_setattr(int argc, char **argv)
+{
+	int opt, status;
+	uint32_t handle;
+	uint16_t attrib;
+	sdp_session_t *sess;
+
+	for_each_opt(opt, set_options, NULL) {
+		switch(opt) {
+		default:
+			printf("%s", set_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 3) {
+		printf("%s", set_help);
+		return -1;
+	}
+
+	/* Convert command line args */
+	handle = strtoul(argv[0], NULL, 16);
+	attrib = strtoul(argv[1], NULL, 16);
+
+	/* Do it */
+	sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+	if (!sess)
+		return -1;
+
+	status = set_attrib(sess, handle, attrib, argv[2]);
+	sdp_close(sess);
+
+	return status;
+}
+
+/*
+ * We do only simple data sequences. Sequence of sequences is a pain ;-)
+ * Jean II
+ */
+static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv)
+{
+	sdp_list_t *attrid_list;
+	uint32_t range = 0x0000ffff;
+	sdp_record_t *rec;
+	sdp_data_t *pSequenceHolder = NULL;
+	void **dtdArray;
+	void **valueArray;
+	void **allocArray;
+	uint8_t uuid16 = SDP_UUID16;
+	uint8_t uint32 = SDP_UINT32;
+	uint8_t str8 = SDP_TEXT_STR8;
+	int i, ret = 0;
+
+	/* Get the old SDP record */
+	attrid_list = sdp_list_append(NULL, &range);
+	rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+	sdp_list_free(attrid_list, NULL);
+
+	if (!rec) {
+		printf("Service get request failed.\n");
+		return -1;
+	}
+
+	/* Create arrays */
+	dtdArray = malloc(argc * sizeof(void *));
+	valueArray = malloc(argc * sizeof(void *));
+	allocArray = malloc(argc * sizeof(void *));
+
+	if (!dtdArray || !valueArray || !allocArray) {
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	/* Loop on all args, add them in arrays */
+	for (i = 0; i < argc; i++) {
+		/* Check the type of attribute */
+		if (!strncasecmp(argv[i], "u0x", 3)) {
+			/* UUID16 */
+			uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16);
+			uuid_t *value_uuid = malloc(sizeof(uuid_t));
+			if (!value_uuid) {
+				ret = -ENOMEM;
+				goto cleanup;
+			}
+
+			allocArray[i] = value_uuid;
+			sdp_uuid16_create(value_uuid, value_int);
+
+			printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle);
+			dtdArray[i] = &uuid16;
+			valueArray[i] = &value_uuid->value.uuid16;
+		} else if (!strncasecmp(argv[i], "0x", 2)) {
+			/* Int */
+			uint32_t *value_int = malloc(sizeof(int));
+			if (!value_int) {
+				ret = -ENOMEM;
+				goto cleanup;
+			}
+
+			allocArray[i] = value_int;
+			*value_int = strtoul((argv[i]) + 2, NULL, 16);
+
+			printf("Adding int 0x%X to record 0x%X\n", *value_int, handle);
+			dtdArray[i] = &uint32;
+			valueArray[i] = value_int;
+		} else {
+			/* String */
+			printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle);
+			dtdArray[i] = &str8;
+			valueArray[i] = argv[i];
+		}
+	}
+
+	/* Add this sequence to the attrib list */
+	pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc);
+	if (pSequenceHolder) {
+		sdp_attr_replace(rec, attrib, pSequenceHolder);
+
+		/* Update on the server */
+		ret = sdp_device_record_update(session, &interface, rec);
+		if (ret < 0)
+			printf("Service Record update failed (%d).\n", errno);
+	} else
+		printf("Failed to create pSequenceHolder\n");
+
+cleanup:
+	if (ret == -ENOMEM)
+		printf("Memory allocation failed\n");
+
+	/* Cleanup */
+	for (i = 0; i < argc; i++)
+		if (allocArray)
+			free(allocArray[i]);
+
+	free(dtdArray);
+	free(valueArray);
+	free(allocArray);
+
+	sdp_record_free(rec);
+
+	return ret;
+}
+
+static struct option seq_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *seq_help =
+	"Usage:\n"
+	"\tget record_handle attrib_id attrib_values\n";
+
+/*
+ * Add an attribute sequence to an existing SDP record
+ * on the local SDP server
+ */
+static int cmd_setseq(int argc, char **argv)
+{
+	int opt, status;
+	uint32_t handle;
+	uint16_t attrib;
+	sdp_session_t *sess;
+
+	for_each_opt(opt, seq_options, NULL) {
+		switch(opt) {
+		default:
+			printf("%s", seq_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 3) {
+		printf("%s", seq_help);
+		return -1;
+	}
+
+	/* Convert command line args */
+	handle = strtoul(argv[0], NULL, 16);
+	attrib = strtoul(argv[1], NULL, 16);
+
+	argc -= 2;
+	argv += 2;
+
+	/* Do it */
+	sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+	if (!sess)
+		return -1;
+
+	status = set_attribseq(sess, handle, attrib, argc, argv);
+	sdp_close(sess);
+
+	return status;
+}
+
+static void print_service_class(void *value, void *userData)
+{
+	char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR];
+	uuid_t *uuid = (uuid_t *)value;
+
+	sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR);
+	sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR);
+	if (uuid->type != SDP_UUID128)
+		printf("  \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str);
+	else
+		printf("  UUID 128: %s\n", UUID_str);
+}
+
+static void print_service_desc(void *value, void *user)
+{
+	char str[MAX_LEN_PROTOCOL_UUID_STR];
+	sdp_data_t *p = (sdp_data_t *)value, *s;
+	int i = 0, proto = 0;
+
+	for (; p; p = p->next, i++) {
+		switch (p->dtd) {
+		case SDP_UUID16:
+		case SDP_UUID32:
+		case SDP_UUID128:
+			sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR);
+			sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str));
+			proto = sdp_uuid_to_proto(&p->val.uuid);
+			printf("  \"%s\" (0x%s)\n", str, UUID_str);
+			break;
+		case SDP_UINT8:
+			if (proto == RFCOMM_UUID)
+				printf("    Channel: %d\n", p->val.uint8);
+			else
+				printf("    uint8: 0x%02x\n", p->val.uint8);
+			break;
+		case SDP_UINT16:
+			if (proto == L2CAP_UUID) {
+				if (i == 1)
+					printf("    PSM: %d\n", p->val.uint16);
+				else
+					printf("    Version: 0x%04x\n", p->val.uint16);
+			} else if (proto == BNEP_UUID)
+				if (i == 1)
+					printf("    Version: 0x%04x\n", p->val.uint16);
+				else
+					printf("    uint16: 0x%04x\n", p->val.uint16);
+			else
+				printf("    uint16: 0x%04x\n", p->val.uint16);
+			break;
+		case SDP_SEQ16:
+			printf("    SEQ16:");
+			for (s = p->val.dataseq; s; s = s->next)
+				printf(" %x", s->val.uint16);
+			printf("\n");
+			break;
+		case SDP_SEQ8:
+			printf("    SEQ8:");
+			for (s = p->val.dataseq; s; s = s->next)
+				printf(" %x", s->val.uint8);
+			printf("\n");
+			break;
+		default:
+			printf("    FIXME: dtd=0%x\n", p->dtd);
+			break;
+		}
+	}
+}
+
+static void print_lang_attr(void *value, void *user)
+{
+	sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value;
+	printf("  code_ISO639: 0x%02x\n", lang->code_ISO639);
+	printf("  encoding:    0x%02x\n", lang->encoding);
+	printf("  base_offset: 0x%02x\n", lang->base_offset);
+}
+
+static void print_access_protos(void *value, void *userData)
+{
+	sdp_list_t *protDescSeq = (sdp_list_t *)value;
+	sdp_list_foreach(protDescSeq, print_service_desc, 0);
+}
+
+static void print_profile_desc(void *value, void *userData)
+{
+	sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value;
+	char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR];
+
+	sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR);
+	sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR);
+
+	printf("  \"%s\" (0x%s)\n", str, UUID_str);
+	if (desc->version)
+		printf("    Version: 0x%04x\n", desc->version);
+}
+
+/*
+ * Parse a SDP record in user friendly form.
+ */
+static void print_service_attr(sdp_record_t *rec)
+{
+	sdp_list_t *list = 0, *proto = 0;
+
+	sdp_record_print(rec);
+
+	printf("Service RecHandle: 0x%x\n", rec->handle);
+
+	if (sdp_get_service_classes(rec, &list) == 0) {
+		printf("Service Class ID List:\n");
+		sdp_list_foreach(list, print_service_class, 0);
+		sdp_list_free(list, free);
+	}
+	if (sdp_get_access_protos(rec, &proto) == 0) {
+		printf("Protocol Descriptor List:\n");
+		sdp_list_foreach(proto, print_access_protos, 0);
+		sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0);
+		sdp_list_free(proto, 0);
+	}
+	if (sdp_get_lang_attr(rec, &list) == 0) {
+		printf("Language Base Attr List:\n");
+		sdp_list_foreach(list, print_lang_attr, 0);
+		sdp_list_free(list, free);
+	}
+	if (sdp_get_profile_descs(rec, &list) == 0) {
+		printf("Profile Descriptor List:\n");
+		sdp_list_foreach(list, print_profile_desc, 0);
+		sdp_list_free(list, free);
+	}
+}
+
+/*
+ * Support for Service (de)registration
+ */
+typedef struct {
+	uint32_t handle;
+	char *name;
+	char *provider;
+	char *desc;
+	unsigned int class;
+	unsigned int profile;
+	uint16_t psm;
+	uint8_t channel;
+	uint8_t network;
+} svc_info_t;
+
+static int add_sp(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+	uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
+	sdp_profile_desc_t profile;
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 1;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &sp_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+	profile.version = 0x0100;
+	profiles = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, profiles);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_add_lang_attr(&record);
+
+	sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port");
+
+	sdp_set_url_attr(&record, "http://www.bluez.org/",
+			"http://www.bluez.org/", "http://www.bluez.org/");
+
+	sdp_set_service_id(&record, sp_uuid);
+	sdp_set_service_ttl(&record, 0xffff);
+	sdp_set_service_avail(&record, 0xff);
+	sdp_set_record_state(&record, 0x00001234);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Serial Port service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+	sdp_list_free(profiles, 0);
+
+	return ret;
+}
+
+static int add_dun(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
+	uuid_t rootu, dun, gn, l2cap, rfcomm;
+	sdp_profile_desc_t profile;
+	sdp_list_t *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 2;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &rootu);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &dun);
+	sdp_uuid16_create(&gn,  GENERIC_NETWORKING_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &gn);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Dial-Up Networking service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_fax(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel? si->channel : 3;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &fax_uuid);
+	sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &tel_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq  = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Fax", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+	printf("Fax service registered\n");
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+	return ret;
+}
+
+static int add_lan(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 4;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("LAN Access service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_headset(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 5;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Headset", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Headset service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_headset_ag(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 7;
+	sdp_data_t *channel;
+	uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Headset AG service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_handsfree(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 6;
+	uint16_t u16 = 0x31;
+	sdp_data_t *channel, *features;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+	profile.version = 0x0101;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	features = sdp_data_alloc(SDP_UINT16, &u16);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Handsfree", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Handsfree service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel : 7;
+	uint16_t u16 = 0x17;
+	sdp_data_t *channel, *features;
+	uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+	profile.version = 0x0105;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	features = sdp_data_alloc(SDP_UINT16, &u16);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Handsfree AG service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_simaccess(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t u8 = si->channel? si->channel : 8;
+	uint16_t u16 = 0x31;
+	sdp_data_t *channel, *features;
+	int ret = 0;
+
+	memset((void *)&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+	profile.version = 0x0101;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	features = sdp_data_alloc(SDP_UINT16, &u16);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "SIM Access", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("SIM Access service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_opush(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_record_t record;
+	uint8_t chan = si->channel ? si->channel : 9;
+	sdp_data_t *channel;
+	uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+	void *dtds[sizeof(formats)], *values[sizeof(formats)];
+	unsigned int i;
+	uint8_t dtd = SDP_UINT8;
+	sdp_data_t *sflist;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &opush_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &chan);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	for (i = 0; i < sizeof(formats); i++) {
+		dtds[i] = &dtd;
+		values[i] = &formats[i];
+	}
+	sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+	sdp_set_info_attr(&record, "OBEX Object Push", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("OBEX Object Push service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, NULL);
+
+	return ret;
+}
+
+static int add_pbap(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, pbap_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_record_t record;
+	uint8_t chan = si->channel ? si->channel : 19;
+	sdp_data_t *channel;
+	uint8_t formats[] = {0x01};
+	uint8_t dtd = SDP_UINT8;
+	sdp_data_t *sflist;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &pbap_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &chan);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sflist = sdp_data_alloc(dtd,formats);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
+
+	sdp_set_info_attr(&record, "OBEX Phonebook Access Server", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record,
+			SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("PBAP service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+
+	return ret;
+}
+
+static int add_map(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, map_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_record_t record;
+	uint8_t chan = si->channel ? si->channel : 17;
+	sdp_data_t *channel;
+	uint8_t msg_formats[] = {0x0f};
+	uint32_t supp_features[] = {0x0000007f};
+	uint8_t dtd_msg = SDP_UINT8, dtd_sf = SDP_UINT32;
+	sdp_data_t *smlist;
+	sdp_data_t *sflist;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&map_uuid, MAP_MSE_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &map_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &chan);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	smlist = sdp_data_alloc(dtd_msg, msg_formats);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, smlist);
+
+	sflist = sdp_data_alloc(dtd_sf, supp_features);
+	sdp_attr_add(&record, SDP_ATTR_MAP_SUPPORTED_FEATURES, sflist);
+
+	sdp_set_info_attr(&record, "OBEX Message Access Server", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record,
+			SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("MAP service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+
+	return ret;
+}
+
+static int add_ftp(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_record_t record;
+	uint8_t u8 = si->channel ? si->channel: 10;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &ftrn_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("OBEX File Transfer service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_directprint(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_record_t record;
+	uint8_t chan = si->channel ? si->channel : 12;
+	sdp_data_t *channel;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &opush_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &chan);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Direct Printing", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("Direct Printing service registered\n");
+
+end:
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_nap(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint16_t lp = 0x000f, ver = 0x0100;
+	sdp_data_t *psm, *version;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &ftrn_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+	proto[1] = sdp_list_append(0, &bnep_uuid);
+	version  = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+
+	{
+		uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 };
+		sdp_data_t *head, *pseq;
+		int p;
+
+		for (p = 0, head = NULL; p < 4; p++) {
+			sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+			head = sdp_seq_append(head, data);
+		}
+		pseq = sdp_data_alloc(SDP_SEQ16, head);
+		proto[1] = sdp_list_append(proto[1], pseq);
+	}
+
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Network Access Point Service", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("NAP service registered\n");
+
+end:
+	sdp_data_free(version);
+	sdp_data_free(psm);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_gn(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint16_t lp = 0x000f, ver = 0x0100;
+	sdp_data_t *psm, *version;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &ftrn_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+	proto[1] = sdp_list_append(0, &bnep_uuid);
+	version = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Group Network Service", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("GN service registered\n");
+
+end:
+	sdp_data_free(version);
+	sdp_data_free(psm);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_panu(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint16_t lp = 0x000f, ver = 0x0100;
+	sdp_data_t *psm, *version;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &ftrn_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(NULL, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+	sdp_list_free(pfseq, NULL);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep_uuid);
+	version = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "PAN User", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("PANU service registered\n");
+
+end:
+	sdp_data_free(version);
+	sdp_data_free(psm);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_hid_keyb(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+	unsigned int i;
+	uint8_t dtd = SDP_UINT16;
+	uint8_t dtd2 = SDP_UINT8;
+	uint8_t dtd_data = SDP_TEXT_STR8;
+	void *dtds[2];
+	void *values[2];
+	void *dtds2[2];
+	void *values2[2];
+	int leng[2];
+	uint8_t hid_spec_type = 0x22;
+	uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+	static const uint16_t ctrl = 0x11;
+	static const uint16_t intr = 0x13;
+	static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 };
+	static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 };
+	const uint8_t hid_spec[] = {
+		0x05, 0x01, // usage page
+		0x09, 0x06, // keyboard
+		0xa1, 0x01, // key codes
+		0x85, 0x01, // minimum
+		0x05, 0x07, // max
+		0x19, 0xe0, // logical min
+		0x29, 0xe7, // logical max
+		0x15, 0x00, // report size
+		0x25, 0x01, // report count
+		0x75, 0x01, // input data variable absolute
+		0x95, 0x08, // report count
+		0x81, 0x02, // report size
+		0x75, 0x08,
+		0x95, 0x01,
+		0x81, 0x01,
+		0x75, 0x01,
+		0x95, 0x05,
+		0x05, 0x08,
+		0x19, 0x01,
+		0x29, 0x05,
+		0x91, 0x02,
+		0x75, 0x03,
+		0x95, 0x01,
+		0x91, 0x01,
+		0x75, 0x08,
+		0x95, 0x06,
+		0x15, 0x00,
+		0x26, 0xff,
+		0x00, 0x05,
+		0x07, 0x19,
+		0x00, 0x2a,
+		0xff, 0x00,
+		0x81, 0x00,
+		0x75, 0x01,
+		0x95, 0x01,
+		0x15, 0x00,
+		0x25, 0x01,
+		0x05, 0x0c,
+		0x09, 0xb8,
+		0x81, 0x06,
+		0x09, 0xe2,
+		0x81, 0x06,
+		0x09, 0xe9,
+		0x81, 0x02,
+		0x09, 0xea,
+		0x81, 0x02,
+		0x75, 0x01,
+		0x95, 0x04,
+		0x81, 0x01,
+		0xc0         // end tag
+	};
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_add_lang_attr(&record);
+
+	sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &hidkb_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	/* protocols */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	/* additional protocols */
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &intr);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_add_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL);
+
+	for (i = 0; i < sizeof(hid_attr) / 2; i++)
+		sdp_attr_add_new(&record,
+					SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
+					SDP_UINT16, &hid_attr[i]);
+
+	dtds[0] = &dtd2;
+	values[0] = &hid_spec_type;
+	dtds[1] = &dtd_data;
+	values[1] = (uint8_t *) hid_spec;
+	leng[0] = 0;
+	leng[1] = sizeof(hid_spec);
+	hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+	hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+	sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+	for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+		dtds2[i] = &dtd;
+		values2[i] = &hid_attr_lang[i];
+	}
+
+	lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+	lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+	sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
+
+	for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
+		sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
+						SDP_UINT16, &hid_attr2[i + 1]);
+
+	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("HID keyboard service registered\n");
+
+	return 0;
+}
+
+static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+	unsigned int i;
+	uint8_t dtd = SDP_UINT16;
+	uint8_t dtd2 = SDP_UINT8;
+	uint8_t dtd_data = SDP_TEXT_STR8;
+	void *dtds[2];
+	void *values[2];
+	void *dtds2[2];
+	void *values2[2];
+	int leng[2];
+	uint8_t hid_spec_type = 0x22;
+	uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+	uint16_t ctrl = 0x11, intr = 0x13;
+	uint16_t hid_release = 0x0100, parser_version = 0x0111;
+	uint8_t subclass = 0x04, country = 0x33;
+	uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0;
+	uint8_t battery = 1, remote_wakeup = 1;
+	uint16_t profile_version = 0x0100, superv_timeout = 0x0c80;
+	uint8_t norm_connect = 0, boot_device = 0;
+	const uint8_t hid_spec[] = {
+		0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10,
+		0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95,
+		0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+		0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+		0xc0, 0x00
+	};
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &hid_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(NULL, profile);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &intr);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_add_access_protos(&record, aproto);
+
+	sdp_add_lang_attr(&record);
+
+	sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
+					"Nintendo", "Nintendo RVL-CNT-01");
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
+						SDP_UINT16, &hid_release);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION,
+						SDP_UINT16, &parser_version);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS,
+						SDP_UINT8, &subclass);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE,
+						SDP_UINT8, &country);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE,
+						SDP_BOOL, &virtual_cable);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE,
+						SDP_BOOL, &reconnect);
+
+	dtds[0] = &dtd2;
+	values[0] = &hid_spec_type;
+	dtds[1] = &dtd_data;
+	values[1] = (uint8_t *) hid_spec;
+	leng[0] = 0;
+	leng[1] = sizeof(hid_spec);
+	hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+	hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+	sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+	for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+		dtds2[i] = &dtd;
+		values2[i] = &hid_attr_lang[i];
+	}
+
+	lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+	lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+	sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE,
+						SDP_BOOL, &sdp_disable);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER,
+						SDP_BOOL, &battery);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP,
+						SDP_BOOL, &remote_wakeup);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION,
+						SDP_UINT16, &profile_version);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT,
+						SDP_UINT16, &superv_timeout);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE,
+						SDP_BOOL, &norm_connect);
+
+	sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE,
+						SDP_BOOL, &boot_device);
+
+	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("Wii-Mote service registered\n");
+
+	return 0;
+}
+
+static int add_cip(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, cmtp, cip;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint16_t psm = si->psm ? si->psm : 0x1001;
+	uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM
+	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&cip, CIP_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &cip);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	apseq = sdp_list_append(0, proto[0]);
+	proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm));
+	apseq = sdp_list_append(apseq, proto[0]);
+
+	sdp_uuid16_create(&cmtp, CMTP_UUID);
+	proto[1] = sdp_list_append(0, &cmtp);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_set_info_attr(&record, "Common ISDN Access", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("CIP service registered\n");
+
+end:
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_data_free(network);
+
+	return ret;
+}
+
+static int add_ctp(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, tcsbin, ctp;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document
+	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &ctp);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&tcsbin, TCS_BIN_UUID);
+	proto[1] = sdp_list_append(0, &tcsbin);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_set_info_attr(&record, "Cordless Telephony", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto end;
+	}
+
+	printf("CTP service registered\n");
+
+end:
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+	sdp_data_free(network);
+
+	return ret;
+}
+
+static int add_a2source(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avdtp, a2src;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	sdp_data_t *psm, *version;
+	uint16_t lp = 0x0019, ver = 0x0100;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &a2src);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avdtp, AVDTP_UUID);
+	proto[1] = sdp_list_append(0, &avdtp);
+	version = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Audio Source", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	printf("Audio source service registered\n");
+
+done:
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_a2sink(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avdtp, a2snk;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	sdp_data_t *psm, *version;
+	uint16_t lp = 0x0019, ver = 0x0100;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &a2snk);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avdtp, AVDTP_UUID);
+	proto[1] = sdp_list_append(0, &avdtp);
+	version = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Audio Sink", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	printf("Audio sink service registered\n");
+
+done:
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_avrct(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrct;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &avrct);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(0, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(&record, "AVRCP CT", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	printf("Remote control service registered\n");
+
+done:
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_avrtg(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, l2cap, avctp, avrtg;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t record;
+	sdp_data_t *psm, *version, *features;
+	uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+	int ret = 0;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &avrtg);
+	sdp_set_service_classes(&record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(&record, pfseq);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &lp);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&avctp, AVCTP_UUID);
+	proto[1] = sdp_list_append(0, &avctp);
+	version = sdp_data_alloc(SDP_UINT16, &ver);
+	proto[1] = sdp_list_append(proto[1], version);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	features = sdp_data_alloc(SDP_UINT16, &feat);
+	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+	sdp_set_info_attr(&record, "AVRCP TG", 0, 0);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	printf("Remote target service registered\n");
+
+done:
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	return ret;
+}
+
+static int add_udi_ue(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	uint8_t channel = si->channel ? si->channel: 18;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+	sdp_list_free(svclass, NULL);
+
+	sdp_set_info_attr(&record, "UDI UE", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("UDI UE service registered\n");
+
+	return 0;
+}
+
+static int add_udi_te(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	uint8_t channel = si->channel ? si->channel: 19;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+	sdp_list_free(svclass, NULL);
+
+	sdp_set_info_attr(&record, "UDI TE", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("UDI TE service registered\n");
+
+	return 0;
+}
+
+static unsigned char sr1_uuid[] = {	0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0,
+					0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 };
+
+static int add_sr1(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass;
+	uuid_t root_uuid, svclass_uuid;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("Toshiba Speech Recognition SR-1 service record registered\n");
+
+	return 0;
+}
+
+static unsigned char syncmls_uuid[] = {	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static unsigned char syncmlc_uuid[] = {	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static int add_syncml(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	uint8_t channel = si->channel ? si->channel: 15;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_set_info_attr(&record, "SyncML Client", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("SyncML Client service record registered\n");
+
+	return 0;
+}
+
+static unsigned char async_uuid[] = {	0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+					0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+static int add_activesync(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	uint8_t channel = si->channel ? si->channel: 21;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+	sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid128_create(&svclass_uuid, (void *) async_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("ActiveSync service record registered\n");
+
+	return 0;
+}
+
+static unsigned char hotsync_uuid[] = {	0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5,
+					0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C };
+
+static int add_hotsync(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	uint8_t channel = si->channel ? si->channel: 22;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+	sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("HotSync service record registered\n");
+
+	return 0;
+}
+
+static unsigned char palmos_uuid[] = {	0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51,
+					0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 };
+
+static int add_palmos(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass;
+	uuid_t root_uuid, svclass_uuid;
+	int err;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	err = sdp_device_record_register(session, &interface, &record,
+							SDP_RECORD_PERSIST);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass, NULL);
+
+	if (err < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("PalmOS service record registered\n");
+
+	return 0;
+}
+
+static unsigned char nokid_uuid[] = {	0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_nokiaid(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass;
+	uuid_t root_uuid, svclass_uuid;
+	uint16_t verid = 0x005f;
+	sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid);
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		sdp_data_free(version);
+		return -1;
+	}
+
+	printf("Nokia ID service record registered\n");
+
+	return 0;
+}
+
+static unsigned char pcsuite_uuid[] = {	0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_pcsuite(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+	uint8_t channel = si->channel ? si->channel: 14;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid);
+	svclass = sdp_list_append(NULL, &svclass_uuid);
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("Nokia PC Suite service registered\n");
+
+	return 0;
+}
+
+static unsigned char nftp_uuid[] = {	0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char nsyncml_uuid[] = {	0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char ngage_uuid[] = {	0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00,
+					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char apple_uuid[] = {	0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90,
+					0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 };
+
+static unsigned char iap_uuid[] = {	0x00, 0x00, 0x00, 0x00, 0xde, 0xca, 0xfa, 0xde,
+					0xde, 0xca, 0xde, 0xaf, 0xde, 0xca, 0xca, 0xfe };
+
+static int add_apple(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root;
+	uuid_t root_uuid;
+	uint32_t attr783 = 0x00000000;
+	uint32_t attr785 = 0x00000002;
+	uint16_t attr786 = 0x1234;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid);
+	sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini");
+	sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1");
+	sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783);
+	sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22");
+	sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785);
+	sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786);
+
+	sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("Apple attribute service registered\n");
+
+	return 0;
+}
+
+static int add_isync(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_list_t *root, *svclass, *proto;
+	uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid;
+	uint8_t channel = si->channel ? si->channel : 16;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto = sdp_list_append(proto, sdp_list_append(
+		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID);
+	svclass = sdp_list_append(NULL, &serial_uuid);
+
+	sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID);
+	svclass = sdp_list_append(svclass, &svclass_uuid);
+
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd.");
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	printf("Apple iSync service registered\n");
+
+	return 0;
+}
+
+static int add_semchla(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_record_t record;
+	sdp_profile_desc_t profile;
+	sdp_list_t *root, *svclass, *proto, *profiles;
+	uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid;
+	uint16_t psm = 0xf0f9;
+
+	memset(&record, 0, sizeof(record));
+	record.handle = si->handle;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto = sdp_list_append(NULL, sdp_list_append(
+		sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm)));
+
+	sdp_uuid32_create(&semchla_uuid, 0x8e770300);
+	proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid));
+
+	sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+	sdp_uuid32_create(&service_uuid, 0x8e771301);
+	svclass = sdp_list_append(NULL, &service_uuid);
+
+	sdp_set_service_classes(&record, svclass);
+
+	sdp_uuid32_create(&profile.uuid, 0x8e771302);	// Headset
+	//sdp_uuid32_create(&profile.uuid, 0x8e771303);	// Phone
+	profile.version = 0x0100;
+	profiles = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(&record, profiles);
+
+	sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL);
+
+	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+		printf("Service Record registration failed\n");
+		return -1;
+	}
+
+	/* SEMC High Level Authentication */
+	printf("SEMC HLA service registered\n");
+
+	return 0;
+}
+
+static int add_gatt(sdp_session_t *session, svc_info_t *si)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+	uuid_t root_uuid, proto_uuid, gatt_uuid, l2cap;
+	sdp_profile_desc_t profile;
+	sdp_record_t record;
+	sdp_data_t *psm, *sh, *eh;
+	uint16_t att_psm = 27, start = 0x0001, end = 0x000f;
+	int ret;
+
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = si->handle;
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+	sdp_list_free(root, NULL);
+
+	sdp_uuid16_create(&gatt_uuid, GENERIC_ATTRIB_SVCLASS_ID);
+	svclass_id = sdp_list_append(NULL, &gatt_uuid);
+	sdp_set_service_classes(&record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	sdp_uuid16_create(&profile.uuid, GENERIC_ATTRIB_PROFILE_ID);
+	profile.version = 0x0100;
+	profiles = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(&record, profiles);
+	sdp_list_free(profiles, NULL);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	psm = sdp_data_alloc(SDP_UINT16, &att_psm);
+	proto[0] = sdp_list_append(proto[0], psm);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&proto_uuid, ATT_UUID);
+	proto[1] = sdp_list_append(NULL, &proto_uuid);
+	sh = sdp_data_alloc(SDP_UINT16, &start);
+	proto[1] = sdp_list_append(proto[1], sh);
+	eh = sdp_data_alloc(SDP_UINT16, &end);
+	proto[1] = sdp_list_append(proto[1], eh);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_set_info_attr(&record, "Generic Attribute Profile", "BlueZ", NULL);
+
+	sdp_set_url_attr(&record, "http://www.bluez.org/",
+			"http://www.bluez.org/", "http://www.bluez.org/");
+
+	sdp_set_service_id(&record, gatt_uuid);
+
+	ret = sdp_device_record_register(session, &interface, &record,
+							SDP_RECORD_PERSIST);
+	if (ret < 0)
+		printf("Service Record registration failed\n");
+	else
+		printf("Generic Attribute Profile Service registered\n");
+
+	sdp_data_free(psm);
+	sdp_data_free(sh);
+	sdp_data_free(eh);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	return ret;
+}
+
+struct {
+	char		*name;
+	uint32_t	class;
+	int		(*add)(sdp_session_t *sess, svc_info_t *si);
+	unsigned char *uuid;
+} service[] = {
+	{ "DID",	PNP_INFO_SVCLASS_ID,		NULL,		},
+
+	{ "SP",		SERIAL_PORT_SVCLASS_ID,		add_sp		},
+	{ "DUN",	DIALUP_NET_SVCLASS_ID,		add_dun		},
+	{ "LAN",	LAN_ACCESS_SVCLASS_ID,		add_lan		},
+	{ "FAX",	FAX_SVCLASS_ID,			add_fax		},
+	{ "OPUSH",	OBEX_OBJPUSH_SVCLASS_ID,	add_opush	},
+	{ "FTP",	OBEX_FILETRANS_SVCLASS_ID,	add_ftp		},
+	{ "PRINT",	DIRECT_PRINTING_SVCLASS_ID,	add_directprint	},
+
+	{ "HS",		HEADSET_SVCLASS_ID,		add_headset	},
+	{ "HSAG",	HEADSET_AGW_SVCLASS_ID,		add_headset_ag	},
+	{ "HF",		HANDSFREE_SVCLASS_ID,		add_handsfree	},
+	{ "HFAG",	HANDSFREE_AGW_SVCLASS_ID,	add_handsfree_ag},
+	{ "SAP",	SAP_SVCLASS_ID,			add_simaccess	},
+	{ "PBAP",	PBAP_SVCLASS_ID,		add_pbap,	},
+
+	{ "MAP",	MAP_SVCLASS_ID,			add_map,	},
+	{ "NAP",	NAP_SVCLASS_ID,			add_nap		},
+	{ "GN",		GN_SVCLASS_ID,			add_gn		},
+	{ "PANU",	PANU_SVCLASS_ID,		add_panu	},
+
+	{ "HCRP",	HCR_SVCLASS_ID,			NULL		},
+	{ "HID",	HID_SVCLASS_ID,			NULL		},
+	{ "KEYB",	HID_SVCLASS_ID,			add_hid_keyb	},
+	{ "WIIMOTE",	HID_SVCLASS_ID,			add_hid_wiimote	},
+	{ "CIP",	CIP_SVCLASS_ID,			add_cip		},
+	{ "CTP",	CORDLESS_TELEPHONY_SVCLASS_ID,	add_ctp		},
+
+	{ "A2SRC",	AUDIO_SOURCE_SVCLASS_ID,	add_a2source	},
+	{ "A2SNK",	AUDIO_SINK_SVCLASS_ID,		add_a2sink	},
+	{ "AVRCT",	AV_REMOTE_SVCLASS_ID,		add_avrct	},
+	{ "AVRTG",	AV_REMOTE_TARGET_SVCLASS_ID,	add_avrtg	},
+
+	{ "UDIUE",	UDI_MT_SVCLASS_ID,		add_udi_ue	},
+	{ "UDITE",	UDI_TA_SVCLASS_ID,		add_udi_te	},
+
+	{ "SEMCHLA",	0x8e771301,			add_semchla	},
+
+	{ "SR1",	0,				add_sr1,	sr1_uuid	},
+	{ "SYNCML",	0,				add_syncml,	syncmlc_uuid	},
+	{ "SYNCMLSERV",	0,				NULL,		syncmls_uuid	},
+	{ "ACTIVESYNC",	0,				add_activesync,	async_uuid	},
+	{ "HOTSYNC",	0,				add_hotsync,	hotsync_uuid	},
+	{ "PALMOS",	0,				add_palmos,	palmos_uuid	},
+	{ "NOKID",	0,				add_nokiaid,	nokid_uuid	},
+	{ "PCSUITE",	0,				add_pcsuite,	pcsuite_uuid	},
+	{ "NFTP",	0,				NULL,		nftp_uuid	},
+	{ "NSYNCML",	0,				NULL,		nsyncml_uuid	},
+	{ "NGAGE",	0,				NULL,		ngage_uuid	},
+	{ "APPLE",	0,				add_apple,	apple_uuid	},
+	{ "IAP",	0,				NULL,		iap_uuid	},
+
+	{ "ISYNC",	APPLE_AGENT_SVCLASS_ID,		add_isync,	},
+	{ "GATT",	GENERIC_ATTRIB_SVCLASS_ID,	add_gatt,	},
+
+	{ 0 }
+};
+
+/* Add local service */
+static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
+{
+	sdp_session_t *sess;
+	int i, ret = -1;
+
+	if (!si->name)
+		return -1;
+
+	sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+	if (!sess)
+		return -1;
+
+	for (i = 0; service[i].name; i++)
+		if (!strcasecmp(service[i].name, si->name)) {
+			if (service[i].add)
+				ret = service[i].add(sess, si);
+			goto done;
+		}
+
+	printf("Unknown service name: %s\n", si->name);
+
+done:
+	free(si->name);
+	sdp_close(sess);
+
+	return ret;
+}
+
+static struct option add_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "handle",	1, 0, 'r' },
+	{ "psm",	1, 0, 'p' },
+	{ "channel",	1, 0, 'c' },
+	{ "network",	1, 0, 'n' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *add_help =
+	"Usage:\n"
+	"\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n";
+
+static int cmd_add(int argc, char **argv)
+{
+	svc_info_t si;
+	int opt;
+
+	memset(&si, 0, sizeof(si));
+	si.handle = 0xffffffff;
+
+	for_each_opt(opt, add_options, 0) {
+		switch (opt) {
+		case 'r':
+			if (strncasecmp(optarg, "0x", 2))
+				si.handle = atoi(optarg);
+			else
+				si.handle = strtol(optarg + 2, NULL, 16);
+			break;
+		case 'p':
+			if (strncasecmp(optarg, "0x", 2))
+				si.psm = atoi(optarg);
+			else
+				si.psm = strtol(optarg + 2, NULL, 16);
+			break;
+		case 'c':
+			if (strncasecmp(optarg, "0x", 2))
+				si.channel = atoi(optarg);
+			else
+				si.channel = strtol(optarg + 2, NULL, 16);
+			break;
+		case 'n':
+			if (strncasecmp(optarg, "0x", 2))
+				si.network = atoi(optarg);
+			else
+				si.network = strtol(optarg + 2, NULL, 16);
+			break;
+		default:
+			printf("%s", add_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		printf("%s", add_help);
+		return -1;
+	}
+
+	si.name = strdup(argv[0]);
+
+	return add_service(0, &si);
+}
+
+/* Delete local service */
+static int del_service(bdaddr_t *bdaddr, void *arg)
+{
+	uint32_t handle, range = 0x0000ffff;
+	sdp_list_t *attr;
+	sdp_session_t *sess;
+	sdp_record_t *rec;
+
+	if (!arg) {
+		printf("Record handle was not specified.\n");
+		return -1;
+	}
+
+	sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+	if (!sess) {
+		printf("No local SDP server!\n");
+		return -1;
+	}
+
+	handle = strtoul((char *)arg, 0, 16);
+	attr = sdp_list_append(0, &range);
+	rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr);
+	sdp_list_free(attr, 0);
+
+	if (!rec) {
+		printf("Service Record not found.\n");
+		sdp_close(sess);
+		return -1;
+	}
+
+	if (sdp_device_record_unregister(sess, &interface, rec)) {
+		printf("Failed to unregister service record: %s\n", strerror(errno));
+		sdp_close(sess);
+		return -1;
+	}
+
+	printf("Service Record deleted.\n");
+	sdp_close(sess);
+
+	return 0;
+}
+
+static struct option del_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *del_help =
+	"Usage:\n"
+	"\tdel record_handle\n";
+
+static int cmd_del(int argc, char **argv)
+{
+	int opt;
+
+	for_each_opt(opt, del_options, 0) {
+		switch (opt) {
+		default:
+			printf("%s", del_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		printf("%s", del_help);
+		return -1;
+	}
+
+	return del_service(NULL, argv[0]);
+}
+
+/*
+ * Perform an inquiry and search/browse all peer found.
+ */
+static void inquiry(handler_t handler, void *arg)
+{
+	inquiry_info ii[20];
+	uint8_t count = 0;
+	int i;
+
+	printf("Inquiring ...\n");
+	if (sdp_general_inquiry(ii, 20, 8, &count) < 0) {
+		printf("Inquiry failed\n");
+		return;
+	}
+
+	for (i = 0; i < count; i++)
+		handler(&ii[i].bdaddr, arg);
+}
+
+static void doprintf(void *data, const char *str)
+{
+	printf("%s", str);
+}
+
+/*
+ * Search for a specific SDP service
+ */
+static int do_search(bdaddr_t *bdaddr, struct search_context *context)
+{
+	sdp_list_t *attrid, *search, *seq, *next;
+	uint32_t range = 0x0000ffff;
+	char str[20];
+	sdp_session_t *sess;
+
+	if (!bdaddr) {
+		inquiry(do_search, context);
+		return 0;
+	}
+
+	sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+	ba2str(bdaddr, str);
+	if (!sess) {
+		printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+		return -1;
+	}
+
+	if (context->view != RAW_VIEW) {
+		if (context->svc)
+			printf("Searching for %s on %s ...\n", context->svc, str);
+		else
+			printf("Browsing %s ...\n", str);
+	}
+
+	attrid = sdp_list_append(0, &range);
+	search = sdp_list_append(0, &context->group);
+	if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) {
+		printf("Service Search failed: %s\n", strerror(errno));
+		sdp_list_free(attrid, 0);
+		sdp_list_free(search, 0);
+		sdp_close(sess);
+		return -1;
+	}
+	sdp_list_free(attrid, 0);
+	sdp_list_free(search, 0);
+
+	for (; seq; seq = next) {
+		sdp_record_t *rec = (sdp_record_t *) seq->data;
+		struct search_context sub_context;
+
+		switch (context->view) {
+		case DEFAULT_VIEW:
+			/* Display user friendly form */
+			print_service_attr(rec);
+			printf("\n");
+			break;
+		case TREE_VIEW:
+			/* Display full tree */
+			print_tree_attr(rec);
+			printf("\n");
+			break;
+		case XML_VIEW:
+			/* Display raw XML tree */
+			convert_sdp_record_to_xml(rec, 0, doprintf);
+			break;
+		default:
+			/* Display raw tree */
+			print_raw_attr(rec);
+			break;
+		}
+
+		/* Set the subcontext for browsing the sub tree */
+		memcpy(&sub_context, context, sizeof(struct search_context));
+
+		if (sdp_get_group_id(rec, &sub_context.group) != -1) {
+			/* Browse the next level down if not done */
+			if (sub_context.group.value.uuid16 != context->group.value.uuid16)
+				do_search(bdaddr, &sub_context);
+		}
+		next = seq->next;
+		free(seq);
+		sdp_record_free(rec);
+	}
+
+	sdp_close(sess);
+	return 0;
+}
+
+static struct option browse_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "tree",	0, 0, 't' },
+	{ "raw",	0, 0, 'r' },
+	{ "xml",	0, 0, 'x' },
+	{ "uuid",	1, 0, 'u' },
+	{ "l2cap",	0, 0, 'l' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *browse_help =
+	"Usage:\n"
+	"\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n";
+
+/*
+ * Browse the full SDP database (i.e. list all services starting from the
+ * root/top-level).
+ */
+static int cmd_browse(int argc, char **argv)
+{
+	struct search_context context;
+	int opt, num;
+
+	/* Initialise context */
+	memset(&context, '\0', sizeof(struct search_context));
+	/* We want to browse the top-level/root */
+	sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP);
+
+	for_each_opt(opt, browse_options, 0) {
+		switch (opt) {
+		case 't':
+			context.view = TREE_VIEW;
+			break;
+		case 'r':
+			context.view = RAW_VIEW;
+			break;
+		case 'x':
+			context.view = XML_VIEW;
+			break;
+		case 'u':
+			if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
+				printf("Invalid uuid %s\n", optarg);
+				return -1;
+			}
+			sdp_uuid16_create(&context.group, num);
+			break;
+		case 'l':
+			sdp_uuid16_create(&context.group, L2CAP_UUID);
+			break;
+		default:
+			printf("%s", browse_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc >= 1) {
+		bdaddr_t bdaddr;
+		estr2ba(argv[0], &bdaddr);
+		return do_search(&bdaddr, &context);
+	}
+
+	return do_search(NULL, &context);
+}
+
+static struct option search_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "bdaddr",	1, 0, 'b' },
+	{ "tree",	0, 0, 't' },
+	{ "raw",	0, 0, 'r' },
+	{ "xml",	0, 0, 'x' },
+	{ 0, 0, 0, 0}
+};
+
+static const char *search_help =
+	"Usage:\n"
+	"\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n"
+	"SERVICE is a name (string) or UUID (0x1002)\n";
+
+/*
+ * Search for a specific SDP service
+ *
+ * Note : we should support multiple services on the command line :
+ *          sdptool search 0x0100 0x000f 0x1002
+ * (this would search a service supporting both L2CAP and BNEP directly in
+ * the top level browse group)
+ */
+static int cmd_search(int argc, char **argv)
+{
+	struct search_context context;
+	unsigned char *uuid = NULL;
+	uint32_t class = 0;
+	bdaddr_t bdaddr;
+	int has_addr = 0;
+	int i;
+	int opt;
+
+	/* Initialise context */
+	memset(&context, '\0', sizeof(struct search_context));
+
+	for_each_opt(opt, search_options, 0) {
+		switch (opt) {
+		case 'b':
+			estr2ba(optarg, &bdaddr);
+			has_addr = 1;
+			break;
+		case 't':
+			context.view = TREE_VIEW;
+			break;
+		case 'r':
+			context.view = RAW_VIEW;
+			break;
+		case 'x':
+			context.view = XML_VIEW;
+			break;
+		default:
+			printf("%s", search_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		printf("%s", search_help);
+		return -1;
+	}
+
+	/* Note : we need to find a way to support search combining
+	 * multiple services */
+	context.svc = strdup(argv[0]);
+	if (!strncasecmp(context.svc, "0x", 2)) {
+		int num;
+		/* This is a UUID16, just convert to int */
+		sscanf(context.svc + 2, "%X", &num);
+		class = num;
+		printf("Class 0x%X\n", class);
+	} else {
+		/* Convert class name to an UUID */
+
+		for (i = 0; service[i].name; i++)
+			if (strcasecmp(context.svc, service[i].name) == 0) {
+				class = service[i].class;
+				uuid = service[i].uuid;
+				break;
+			}
+		if (!class && !uuid) {
+			printf("Unknown service %s\n", context.svc);
+			return -1;
+		}
+	}
+
+	if (class) {
+		if (class & 0xffff0000)
+			sdp_uuid32_create(&context.group, class);
+		else {
+			uint16_t class16 = class & 0xffff;
+			sdp_uuid16_create(&context.group, class16);
+		}
+	} else
+		sdp_uuid128_create(&context.group, uuid);
+
+	if (has_addr)
+		return do_search(&bdaddr, &context);
+
+	return do_search(NULL, &context);
+}
+
+/*
+ * Show how to get a specific SDP record by its handle.
+ * Not really useful to the user, just show how it can be done...
+ */
+static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite)
+{
+	sdp_list_t *attrid;
+	uint32_t range = 0x0000ffff;
+	sdp_record_t *rec;
+	sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+
+	if (!session) {
+		char str[20];
+		ba2str(bdaddr, str);
+		printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+		return -1;
+	}
+
+	attrid = sdp_list_append(0, &range);
+	rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid);
+	sdp_list_free(attrid, 0);
+	sdp_close(session);
+
+	if (!rec) {
+		if (!quite) {
+			printf("Service get request failed.\n");
+			return -1;
+		} else
+			return 0;
+	}
+
+	switch (context->view) {
+	case DEFAULT_VIEW:
+		/* Display user friendly form */
+		print_service_attr(rec);
+		printf("\n");
+		break;
+	case TREE_VIEW:
+		/* Display full tree */
+		print_tree_attr(rec);
+		printf("\n");
+		break;
+	case XML_VIEW:
+		/* Display raw XML tree */
+		convert_sdp_record_to_xml(rec, 0, doprintf);
+		break;
+	default:
+		/* Display raw tree */
+		print_raw_attr(rec);
+		break;
+	}
+
+	sdp_record_free(rec);
+	return 0;
+}
+
+static struct option records_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "tree",	0, 0, 't' },
+	{ "raw",	0, 0, 'r' },
+	{ "xml",	0, 0, 'x' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *records_help =
+	"Usage:\n"
+	"\trecords [--tree] [--raw] [--xml] bdaddr\n";
+
+/*
+ * Request possible SDP service records
+ */
+static int cmd_records(int argc, char **argv)
+{
+	struct search_context context;
+	uint32_t base[] = { 0x10000, 0x10300, 0x10500,
+				0x1002e, 0x110b, 0x90000, 0x2008000,
+					0x4000000, 0x100000, 0x1000000,
+						0x4f491100, 0x4f491200 };
+	bdaddr_t bdaddr;
+	unsigned int i, n, num = 32;
+	int opt, err = 0;
+
+	/* Initialise context */
+	memset(&context, '\0', sizeof(struct search_context));
+
+	for_each_opt(opt, records_options, 0) {
+		switch (opt) {
+		case 't':
+			context.view = TREE_VIEW;
+			break;
+		case 'r':
+			context.view = RAW_VIEW;
+			break;
+		case 'x':
+			context.view = XML_VIEW;
+			break;
+		default:
+			printf("%s", records_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		printf("%s", records_help);
+		return -1;
+	}
+
+	/* Convert command line parameters */
+	estr2ba(argv[0], &bdaddr);
+
+	for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
+		for (n = 0; n < num; n++) {
+			context.handle = base[i] + n;
+			err = get_service(&bdaddr, &context, 1);
+			if (err < 0)
+				return 0;
+		}
+
+	return 0;
+}
+
+static struct option get_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "bdaddr",	1, 0, 'b' },
+	{ "tree",	0, 0, 't' },
+	{ "raw",	0, 0, 'r' },
+	{ "xml",	0, 0, 'x' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *get_help =
+	"Usage:\n"
+	"\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n";
+
+/*
+ * Get a specific SDP record on the local SDP server
+ */
+static int cmd_get(int argc, char **argv)
+{
+	struct search_context context;
+	bdaddr_t bdaddr;
+	int has_addr = 0;
+	int opt;
+
+	/* Initialise context */
+	memset(&context, '\0', sizeof(struct search_context));
+
+	for_each_opt(opt, get_options, 0) {
+		switch (opt) {
+		case 'b':
+			estr2ba(optarg, &bdaddr);
+			has_addr = 1;
+			break;
+		case 't':
+			context.view = TREE_VIEW;
+			break;
+		case 'r':
+			context.view = RAW_VIEW;
+			break;
+		case 'x':
+			context.view = XML_VIEW;
+			break;
+		default:
+			printf("%s", get_help);
+			return -1;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1) {
+		printf("%s", get_help);
+		return -1;
+	}
+
+	/* Convert command line parameters */
+	context.handle = strtoul(argv[0], 0, 16);
+
+	return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0);
+}
+
+static struct {
+	char *cmd;
+	int (*func)(int argc, char **argv);
+	char *doc;
+} command[] = {
+	{ "search",  cmd_search,      "Search for a service"          },
+	{ "browse",  cmd_browse,      "Browse all available services" },
+	{ "records", cmd_records,     "Request all records"           },
+	{ "add",     cmd_add,         "Add local service"             },
+	{ "del",     cmd_del,         "Delete local service"          },
+	{ "get",     cmd_get,         "Get local service"             },
+	{ "setattr", cmd_setattr,     "Set/Add attribute to a SDP record"          },
+	{ "setseq",  cmd_setseq,      "Set/Add attribute sequence to a SDP record" },
+	{ 0, 0, 0 }
+};
+
+static void usage(void)
+{
+	int i, pos = 0;
+
+	printf("sdptool - SDP tool v%s\n", VERSION);
+	printf("Usage:\n"
+		"\tsdptool [options] <command> [command parameters]\n");
+	printf("Options:\n"
+		"\t-h\t\tDisplay help\n"
+		"\t-i\t\tSpecify source interface\n");
+
+	printf("Commands:\n");
+	for (i = 0; command[i].cmd; i++)
+		printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc);
+
+	printf("\nServices:\n\t");
+	for (i = 0; service[i].name; i++) {
+		printf("%s ", service[i].name);
+		pos += strlen(service[i].name) + 1;
+		if (pos > 60) {
+			printf("\n\t");
+			pos = 0;
+		}
+	}
+	printf("\n");
+}
+
+static struct option main_options[] = {
+	{ "help",	0, 0, 'h' },
+	{ "device",	1, 0, 'i' },
+	{ 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+	int i, opt;
+
+	bacpy(&interface, BDADDR_ANY);
+
+	while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+		switch(opt) {
+		case 'i':
+			if (!strncmp(optarg, "hci", 3))
+				hci_devba(atoi(optarg + 3), &interface);
+			else
+				str2ba(optarg, &interface);
+			break;
+
+		case 'h':
+			usage();
+			exit(0);
+
+		default:
+			exit(1);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (argc < 1) {
+		usage();
+		exit(1);
+	}
+
+	for (i = 0; command[i].cmd; i++)
+		if (strncmp(command[i].cmd, argv[0], 4) == 0)
+			return command[i].func(argc, argv);
+
+	return 1;
+}
diff --git a/bluez/repo/tools/seq2bseq.c b/bluez/repo/tools/seq2bseq.c
new file mode 100644
index 0000000..23f6c9e
--- /dev/null
+++ b/bluez/repo/tools/seq2bseq.c
@@ -0,0 +1,212 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  Intel Corporation
+ *
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+static int convert_line(int fd, const char *line)
+{
+	const char *ptr = line;
+	char str[3];
+	unsigned char val;
+
+	if (line[0] == '*' || line[0] == '\r' || line[0] == '\n')
+		return 0;
+
+	while (1) {
+		str[0] = *ptr++;
+		str[1] = *ptr++;
+		str[2] = '\0';
+
+		val = strtol(str, NULL, 16);
+
+		if (write(fd, &val, 1) < 0)
+			return -errno;
+
+		if (*ptr == '\r' || *ptr == '\n')
+			break;
+
+		while (*ptr == ' ')
+			ptr++;
+	}
+
+	return 0;
+}
+
+static void convert_file(const char *input_path, const char *output_path)
+{
+	size_t line_size = 1024;
+	char line_buffer[line_size];
+	char *path;
+	const char *ptr;
+	FILE *fp;
+	struct stat st;
+	off_t cur = 0;
+	int fd;
+
+	if (output_path) {
+		path = strdup(output_path);
+		if (!path) {
+			perror("Failed to allocate string");
+			return;
+		}
+	} else {
+		ptr = strrchr(input_path, '.');
+		if (ptr) {
+			path = malloc(ptr - input_path + 6);
+			if (!path) {
+				perror("Failed to allocate string");
+				return;
+			}
+			strncpy(path, input_path, ptr - input_path);
+			strcpy(path + (ptr - input_path), ".bseq");
+		} else {
+			if (asprintf(&path, "%s.bseq", input_path) < 0) {
+				perror("Failed to allocate string");
+				return;
+			}
+		}
+	}
+
+	printf("Converting %s to %s\n", input_path, path);
+
+	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+	free(path);
+
+	if (fd < 0) {
+		perror("Failed to create output file");
+		return;
+	}
+
+	if (stat(input_path, &st) < 0) {
+		fprintf(stderr, "Failed get file size\n");
+		close(fd);
+		return;
+	}
+
+	if (st.st_size == 0) {
+		fprintf(stderr, "Empty file\n");
+		close(fd);
+		return;
+	}
+
+	fp = fopen(input_path, "r");
+	if (!fp) {
+		fprintf(stderr, "Failed to open input file\n");
+		close(fd);
+		return;
+	}
+
+	while (1) {
+		char *str;
+		int err;
+
+		str = fgets(line_buffer, line_size - 1, fp);
+		if (!str)
+			break;
+
+		cur += strlen(str);
+
+		err = convert_line(fd, str);
+		if (err < 0) {
+			fprintf(stderr, "Failed to convert file (%s)\n",
+								strerror(-err));
+			break;
+		}
+	}
+
+	fclose(fp);
+
+	close(fd);
+}
+
+static void usage(void)
+{
+	printf("Intel Bluetooth firmware converter\n"
+		"Usage:\n");
+	printf("\tseq2bseq [options] <file>\n");
+	printf("Options:\n"
+		"\t-o, --output <file>    Provide firmware output file\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "output",  required_argument, NULL, 'o' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	const char *output_path = NULL;
+	int i;
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "o:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'o':
+			output_path = optarg;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (argc - optind < 1) {
+		fprintf(stderr, "No input firmware files provided\n");
+		return EXIT_FAILURE;
+	}
+
+	if (output_path && argc - optind > 1) {
+		fprintf(stderr, "Only single input firmware supported\n");
+		return EXIT_FAILURE;
+	}
+
+	for (i = optind; i < argc; i++)
+		convert_file(argv[i], output_path);
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/smp-tester.c b/bluez/repo/tools/smp-tester.c
new file mode 100644
index 0000000..d24c9b2
--- /dev/null
+++ b/bluez/repo/tools/smp-tester.c
@@ -0,0 +1,936 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/crypto.h"
+#include "src/shared/ecc.h"
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+
+#define SMP_CID 0x0006
+
+struct test_data {
+	const void *test_data;
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	unsigned int io_id;
+	uint8_t ia[6];
+	uint8_t ia_type;
+	uint8_t ra[6];
+	uint8_t ra_type;
+	bool out;
+	uint16_t handle;
+	size_t counter;
+	struct bt_crypto *crypto;
+	uint8_t tk[16];
+	uint8_t prnd[16];
+	uint8_t rrnd[16];
+	uint8_t pcnf[16];
+	uint8_t preq[7];
+	uint8_t prsp[7];
+	uint8_t ltk[16];
+	uint8_t remote_pk[64];
+	uint8_t local_pk[64];
+	uint8_t local_sk[32];
+	uint8_t dhkey[32];
+	int unmet_conditions;
+};
+
+struct smp_req_rsp {
+	const void *send;
+	uint16_t send_len;
+	const void *expect;
+	uint16_t expect_len;
+};
+
+struct smp_data {
+	const struct smp_req_rsp *req;
+	size_t req_count;
+	bool mitm;
+	uint16_t expect_hci_command;
+	const void *expect_hci_param;
+	uint8_t expect_hci_len;
+	const void * (*expect_hci_func)(uint8_t *len);
+	bool sc;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+					index_removed_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->crypto = bt_crypto_new();
+	if (!data->crypto) {
+		tester_warn("Failed to setup crypto");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		bt_crypto_unref(data->crypto);
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	if (data->io_id > 0) {
+		g_source_remove(data->io_id);
+		data->io_id = 0;
+	}
+
+	if (data->crypto) {
+		bt_crypto_unref(data->crypto);
+		data->crypto = NULL;
+	}
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	free(data);
+}
+
+static void test_add_condition(struct test_data *data)
+{
+	data->unmet_conditions++;
+
+	tester_print("Test condition added, total %d", data->unmet_conditions);
+}
+
+static void test_condition_complete(struct test_data *data)
+{
+	data->unmet_conditions--;
+
+	tester_print("Test condition complete, %d left",
+						data->unmet_conditions);
+
+	if (data->unmet_conditions > 0)
+		return;
+
+	tester_test_passed();
+}
+
+#define test_smp(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = calloc(1, sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
+		user->test_data = data; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+static const uint8_t smp_nval_req_1[] = { 0x0b, 0x00 };
+static const uint8_t smp_nval_req_1_rsp[] = { 0x05, 0x07 };
+
+static const struct smp_req_rsp nval_req_1[] = {
+	{ smp_nval_req_1, sizeof(smp_nval_req_1),
+			smp_nval_req_1_rsp, sizeof(smp_nval_req_1_rsp) },
+};
+
+static const struct smp_data smp_server_nval_req_1_test = {
+	.req = nval_req_1,
+	.req_count = G_N_ELEMENTS(nval_req_1),
+};
+
+static const uint8_t smp_nval_req_2[7] = { 0x01 };
+static const uint8_t smp_nval_req_2_rsp[] = { 0x05, 0x06 };
+
+static const struct smp_req_rsp srv_nval_req_1[] = {
+	{ smp_nval_req_2, sizeof(smp_nval_req_2),
+			smp_nval_req_2_rsp, sizeof(smp_nval_req_2_rsp) },
+};
+
+static const struct smp_data smp_server_nval_req_2_test = {
+	.req = srv_nval_req_1,
+	.req_count = G_N_ELEMENTS(srv_nval_req_1),
+};
+
+static const uint8_t smp_nval_req_3[] = { 0x01, 0xff };
+static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x0a };
+
+static const struct smp_req_rsp srv_nval_req_2[] = {
+	{ smp_nval_req_3, sizeof(smp_nval_req_3),
+			smp_nval_req_3_rsp, sizeof(smp_nval_req_3_rsp) },
+};
+
+static const struct smp_data smp_server_nval_req_3_test = {
+	.req = srv_nval_req_2,
+	.req_count = G_N_ELEMENTS(srv_nval_req_2),
+};
+
+static const uint8_t smp_basic_req_1[] = {	0x01,	/* Pairing Request */
+						0x03,	/* NoInputNoOutput */
+						0x00,	/* OOB Flag */
+						0x01,	/* Bonding - no MITM */
+						0x10,	/* Max key size */
+						0x05,	/* Init. key dist. */
+						0x05,	/* Rsp. key dist. */
+};
+static const uint8_t smp_basic_req_1_rsp[] = {	0x02,	/* Pairing Response */
+						0x03,	/* NoInputNoOutput */
+						0x00,	/* OOB Flag */
+						0x01,	/* Bonding - no MITM */
+						0x10,	/* Max key size */
+						0x05,	/* Init. key dist. */
+						0x05,	/* Rsp. key dist. */
+};
+
+static const uint8_t smp_confirm_req_1[17] = { 0x03 };
+static const uint8_t smp_random_req_1[17] = { 0x04 };
+
+static const struct smp_req_rsp srv_basic_req_1[] = {
+	{ smp_basic_req_1, sizeof(smp_basic_req_1),
+			smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp) },
+	{ smp_confirm_req_1, sizeof(smp_confirm_req_1),
+			smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+	{ smp_random_req_1, sizeof(smp_random_req_1),
+			smp_random_req_1, sizeof(smp_random_req_1) },
+};
+
+static const struct smp_data smp_server_basic_req_1_test = {
+	.req = srv_basic_req_1,
+	.req_count = G_N_ELEMENTS(srv_basic_req_1),
+};
+
+static const struct smp_req_rsp cli_basic_req_1[] = {
+	{ NULL, 0, smp_basic_req_1, sizeof(smp_basic_req_1) },
+	{ smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp),
+			smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+	{ smp_confirm_req_1, sizeof(smp_confirm_req_1),
+			smp_random_req_1, sizeof(smp_random_req_1) },
+	{ smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
+};
+
+static const struct smp_data smp_client_basic_req_1_test = {
+	.req = cli_basic_req_1,
+	.req_count = G_N_ELEMENTS(cli_basic_req_1),
+};
+
+static const uint8_t smp_basic_req_2[] = {	0x01,	/* Pairing Request */
+						0x04,	/* NoInputNoOutput */
+						0x00,	/* OOB Flag */
+						0x05,	/* Bonding - MITM */
+						0x10,	/* Max key size */
+						0x05,	/* Init. key dist. */
+						0x05,	/* Rsp. key dist. */
+};
+
+static const struct smp_req_rsp cli_basic_req_2[] = {
+	{ NULL, 0, smp_basic_req_2, sizeof(smp_basic_req_2) },
+	{ smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp),
+			smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+	{ smp_confirm_req_1, sizeof(smp_confirm_req_1),
+			smp_random_req_1, sizeof(smp_random_req_1) },
+	{ smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
+};
+
+static const struct smp_data smp_client_basic_req_2_test = {
+	.req = cli_basic_req_2,
+	.req_count = G_N_ELEMENTS(cli_basic_req_1),
+	.mitm = true,
+};
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+							const void *param,
+							void *user_data)
+{
+	const struct mgmt_ev_user_confirm_request *ev = param;
+	struct test_data *data = tester_get_data();
+	struct mgmt_cp_user_confirm_reply cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+	mgmt_reply(data->mgmt, MGMT_OP_USER_CONFIRM_REPLY,
+			data->mgmt_index, sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static const uint8_t smp_sc_req_1[] = {	0x01,	/* Pairing Request */
+					0x03,	/* NoInputNoOutput */
+					0x00,	/* OOB Flag */
+					0x09,	/* Bonding - no MITM, SC */
+					0x10,	/* Max key size */
+					0x0d,	/* Init. key dist. */
+					0x0d,	/* Rsp. key dist. */
+};
+
+static const struct smp_req_rsp cli_sc_req_1[] = {
+	{ NULL, 0, smp_sc_req_1, sizeof(smp_sc_req_1) },
+	{ smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp),
+			smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+	{ smp_confirm_req_1, sizeof(smp_confirm_req_1),
+			smp_random_req_1, sizeof(smp_random_req_1) },
+	{ smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
+};
+
+static const struct smp_data smp_client_sc_req_1_test = {
+	.req = cli_sc_req_1,
+	.req_count = G_N_ELEMENTS(cli_sc_req_1),
+	.sc = true,
+};
+
+static const uint8_t smp_sc_rsp_1[] = {	0x02,	/* Pairing Response */
+					0x03,	/* NoInputNoOutput */
+					0x00,	/* OOB Flag */
+					0x09,	/* Bonding - no MITM, SC */
+					0x10,	/* Max key size */
+					0x0d,	/* Init. key dist. */
+					0x0d,	/* Rsp. key dist. */
+};
+
+static const uint8_t smp_sc_pk[65] = { 0x0c };
+
+static const struct smp_req_rsp cli_sc_req_2[] = {
+	{ NULL, 0, smp_sc_req_1, sizeof(smp_sc_req_1) },
+	{ smp_sc_rsp_1, sizeof(smp_sc_rsp_1), smp_sc_pk, sizeof(smp_sc_pk) },
+	{ smp_sc_pk, sizeof(smp_sc_pk), NULL, 0 },
+	{ smp_confirm_req_1, sizeof(smp_confirm_req_1),
+				smp_random_req_1, sizeof(smp_random_req_1) },
+	{ smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
+};
+
+static const struct smp_data smp_client_sc_req_2_test = {
+	.req = cli_sc_req_2,
+	.req_count = G_N_ELEMENTS(cli_sc_req_2),
+	.sc = true,
+};
+
+static void client_connectable_complete(uint16_t opcode, uint8_t status,
+					const void *param, uint8_t len,
+					void *user_data)
+{
+	if (opcode != BT_HCI_CMD_LE_SET_ADV_ENABLE)
+		return;
+
+	tester_print("Client set connectable status 0x%02x", status);
+
+	if (status)
+		tester_setup_failed();
+	else
+		tester_setup_complete();
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost;
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data);
+	bthost_set_adv_enable(bthost, 0x01);
+}
+
+static void make_pk(struct test_data *data)
+{
+	if (!ecc_make_key(data->local_pk, data->local_sk)) {
+		tester_print("Failed to general local ECDH keypair");
+		tester_setup_failed();
+		return;
+	}
+}
+
+static void setup_powered_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct smp_data *smp = data->test_data;
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_SET_BONDABLE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	if (smp->sc) {
+		mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+		mgmt_send(data->mgmt, MGMT_OP_SET_SECURE_CONN,
+				data->mgmt_index, sizeof(param), param, NULL,
+				NULL, NULL);
+		make_pk(data);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_client_callback,
+			NULL, NULL);
+}
+
+static void pair_device_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_warn("Pairing failed: %s", mgmt_errstr(status));
+		return;
+	}
+
+	tester_print("Pairing succeedded");
+}
+
+static const void *get_pdu(const uint8_t *pdu)
+{
+	struct test_data *data = tester_get_data();
+	const struct smp_data *smp = data->test_data;
+	uint8_t opcode = pdu[0];
+	static uint8_t buf[65];
+
+	switch (opcode) {
+	case 0x01: /* Pairing Request */
+		memcpy(data->preq, pdu, sizeof(data->preq));
+		break;
+	case 0x02: /* Pairing Response */
+		memcpy(data->prsp, pdu, sizeof(data->prsp));
+		break;
+	case 0x03: /* Pairing Confirm */
+		buf[0] = pdu[0];
+		if (smp->sc)
+			bt_crypto_f4(data->crypto, data->local_pk,
+					data->remote_pk, data->prnd, 0,
+					&buf[1]);
+		else
+			bt_crypto_c1(data->crypto, data->tk, data->prnd,
+					data->prsp, data->preq, data->ia_type,
+					data->ia, data->ra_type, data->ra,
+					&buf[1]);
+		return buf;
+	case 0x04: /* Pairing Random */
+		buf[0] = pdu[0];
+		memcpy(&buf[1], data->prnd, 16);
+		return buf;
+	case 0x0c: /* Public Key */
+		buf[0] = pdu[0];
+		memcpy(&buf[1], data->local_pk, 64);
+		return buf;
+	default:
+		break;
+	}
+
+	return pdu;
+}
+
+static bool verify_random(const uint8_t rnd[16])
+{
+	struct test_data *data = tester_get_data();
+	uint8_t confirm[16];
+
+	if (!bt_crypto_c1(data->crypto, data->tk, data->rrnd, data->prsp,
+					data->preq, data->ia_type, data->ia,
+					data->ra_type, data->ra, confirm))
+		return false;
+
+	if (memcmp(data->pcnf, confirm, sizeof(data->pcnf) != 0)) {
+		tester_warn("Confirmation values don't match");
+		return false;
+	}
+
+	if (data->out) {
+		struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+		bt_crypto_s1(data->crypto, data->tk, data->rrnd, data->prnd,
+								data->ltk);
+		bthost_le_start_encrypt(bthost, data->handle, data->ltk);
+	} else {
+		bt_crypto_s1(data->crypto, data->tk, data->prnd, data->rrnd,
+								data->ltk);
+	}
+
+	return true;
+}
+
+static bool sc_random(struct test_data *test_data)
+{
+	return true;
+}
+
+static void smp_server(const void *data, uint16_t len, void *user_data)
+{
+	struct test_data *test_data = user_data;
+	struct bthost *bthost = hciemu_client_get_host(test_data->hciemu);
+	const struct smp_data *smp = test_data->test_data;
+	const struct smp_req_rsp *req;
+	uint8_t opcode;
+	const void *pdu;
+
+	if (len < 1) {
+		tester_warn("Received too small SMP PDU");
+		goto failed;
+	}
+
+	opcode = *((const uint8_t *) data);
+
+	tester_print("Received SMP opcode 0x%02x", opcode);
+
+	if (test_data->counter >= smp->req_count) {
+		test_condition_complete(test_data);
+		return;
+	}
+
+	req = &smp->req[test_data->counter++];
+	if (!req->expect)
+		goto next;
+
+	if (req->expect_len != len) {
+		tester_warn("Unexpected SMP PDU length (%u != %u)",
+							len, req->expect_len);
+		goto failed;
+	}
+
+	switch (opcode) {
+	case 0x01: /* Pairing Request */
+		memcpy(test_data->preq, data, sizeof(test_data->preq));
+		break;
+	case 0x02: /* Pairing Response */
+		memcpy(test_data->prsp, data, sizeof(test_data->prsp));
+		break;
+	case 0x03: /* Pairing Confirm */
+		memcpy(test_data->pcnf, data + 1, 16);
+		goto next;
+	case 0x04: /* Pairing Random */
+		memcpy(test_data->rrnd, data + 1, 16);
+		if (smp->sc) {
+			if (!sc_random(test_data))
+				goto failed;
+		} else {
+			if (!verify_random(data + 1))
+				goto failed;
+		}
+		goto next;
+	case 0x0c: /* Public Key */
+		memcpy(test_data->remote_pk, data + 1, 64);
+		ecdh_shared_secret(test_data->remote_pk, test_data->local_sk,
+							test_data->dhkey);
+		goto next;
+	default:
+		break;
+	}
+
+	if (memcmp(req->expect, data, len) != 0) {
+		tester_warn("Unexpected SMP PDU");
+		goto failed;
+	}
+
+next:
+	while (true) {
+		if (smp->req_count == test_data->counter) {
+			test_condition_complete(test_data);
+			break;
+		}
+
+		req = &smp->req[test_data->counter];
+
+		pdu = get_pdu(req->send);
+		bthost_send_cid(bthost, test_data->handle, SMP_CID, pdu,
+								req->send_len);
+		if (req->expect)
+			break;
+		else
+			test_data->counter++;
+	}
+
+	return;
+
+failed:
+	tester_test_failed();
+}
+
+static void command_hci_callback(uint16_t opcode, const void *param,
+					uint8_t length, void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct smp_data *smp = data->test_data;
+	const void *expect_hci_param = smp->expect_hci_param;
+	uint8_t expect_hci_len = smp->expect_hci_len;
+
+	tester_print("HCI Command 0x%04x length %u", opcode, length);
+
+	if (opcode != smp->expect_hci_command)
+		return;
+
+	if (smp->expect_hci_func)
+		expect_hci_param = smp->expect_hci_func(&expect_hci_len);
+
+	if (length != expect_hci_len) {
+		tester_warn("Invalid parameter size for HCI command");
+		tester_test_failed();
+		return;
+	}
+
+	if (memcmp(param, expect_hci_param, length) != 0) {
+		tester_warn("Unexpected HCI command parameter value");
+		tester_test_failed();
+		return;
+	}
+
+	test_condition_complete(data);
+}
+
+static void smp_new_conn(uint16_t handle, void *user_data)
+{
+	struct test_data *data = user_data;
+	const struct smp_data *smp = data->test_data;
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+	const struct smp_req_rsp *req;
+	const void *pdu;
+
+	tester_print("New SMP client connection with handle 0x%04x", handle);
+
+	data->handle = handle;
+
+	bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, data);
+
+	if (smp->req_count == data->counter)
+		return;
+
+	req = &smp->req[data->counter];
+
+	if (!req->send)
+		return;
+
+	tester_print("Sending SMP PDU");
+
+	pdu = get_pdu(req->send);
+	bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len);
+
+	if (!req->expect)
+		test_condition_complete(data);
+}
+
+static void init_bdaddr(struct test_data *data)
+{
+	const uint8_t *master_bdaddr, *client_bdaddr;
+
+	master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+	if (!master_bdaddr) {
+		tester_warn("No master bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+	if (!client_bdaddr) {
+		tester_warn("No client bdaddr");
+		tester_test_failed();
+		return;
+	}
+
+	data->ia_type = LE_PUBLIC_ADDRESS;
+	data->ra_type = LE_PUBLIC_ADDRESS;
+
+	if (data->out) {
+		memcpy(data->ia, client_bdaddr, sizeof(data->ia));
+		memcpy(data->ra, master_bdaddr, sizeof(data->ra));
+	} else {
+		memcpy(data->ia, master_bdaddr, sizeof(data->ia));
+		memcpy(data->ra, client_bdaddr, sizeof(data->ra));
+	}
+}
+
+static void test_client(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct smp_data *smp = data->test_data;
+	struct mgmt_cp_pair_device cp;
+	struct bthost *bthost;
+
+	init_bdaddr(data);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_connect_cb(bthost, smp_new_conn, data);
+	test_add_condition(data);
+
+	if (smp->expect_hci_command) {
+		tester_print("Registering HCI command callback");
+		hciemu_add_master_post_command_hook(data->hciemu,
+						command_hci_callback, data);
+		test_add_condition(data);
+	}
+
+	memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra));
+	cp.addr.type = BDADDR_LE_PUBLIC;
+	if (smp->mitm)
+		cp.io_cap = 0x04; /* KeyboardDisplay */
+	else
+		cp.io_cap = 0x03; /* NoInputNoOutput */
+
+	mgmt_send(data->mgmt, MGMT_OP_PAIR_DEVICE, data->mgmt_index,
+			sizeof(cp), &cp, pair_device_complete, NULL, NULL);
+
+	tester_print("Pairing in progress");
+}
+
+static void setup_powered_server_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	tester_setup_complete();
+}
+
+static void setup_powered_server(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct smp_data *smp = data->test_data;
+	unsigned char param[] = { 0x01 };
+
+	mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST,
+			data->mgmt_index, user_confirm_request_callback,
+			data, NULL);
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_SET_BONDABLE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
+				sizeof(param), param, NULL, NULL, NULL);
+	if (smp->sc) {
+		mgmt_send(data->mgmt, MGMT_OP_SET_SECURE_CONN,
+				data->mgmt_index, sizeof(param), param, NULL,
+				NULL, NULL);
+		make_pk(data);
+	}
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_server_callback,
+			NULL, NULL);
+}
+
+static void test_server(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct smp_data *smp = data->test_data;
+	struct bthost *bthost;
+
+	data->out = true;
+
+	init_bdaddr(data);
+
+	bthost = hciemu_client_get_host(data->hciemu);
+	bthost_set_connect_cb(bthost, smp_new_conn, data);
+	test_add_condition(data);
+
+	bthost_hci_connect(bthost, data->ra, BDADDR_LE_PUBLIC);
+
+	if (smp->expect_hci_command) {
+		tester_print("Registering HCI command callback");
+		hciemu_add_master_post_command_hook(data->hciemu,
+						command_hci_callback, data);
+		test_add_condition(data);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_smp("SMP Server - Basic Request 1",
+					&smp_server_basic_req_1_test,
+					setup_powered_server, test_server);
+	test_smp("SMP Server - Invalid Request 1",
+					&smp_server_nval_req_1_test,
+					setup_powered_server, test_server);
+	test_smp("SMP Server - Invalid Request 2",
+					&smp_server_nval_req_2_test,
+					setup_powered_server, test_server);
+	test_smp("SMP Server - Invalid Request 3",
+					&smp_server_nval_req_3_test,
+					setup_powered_server, test_server);
+
+	test_smp("SMP Client - Basic Request 1",
+					&smp_client_basic_req_1_test,
+					setup_powered_client, test_client);
+	test_smp("SMP Client - Basic Request 2",
+					&smp_client_basic_req_2_test,
+					setup_powered_client, test_client);
+
+	test_smp("SMP Client - SC Request 1",
+					&smp_client_sc_req_1_test,
+					setup_powered_client, test_client);
+	test_smp("SMP Client - SC Request 2",
+					&smp_client_sc_req_2_test,
+					setup_powered_client, test_client);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/test-runner.c b/bluez/repo/tools/test-runner.c
new file mode 100644
index 0000000..42c2c1a
--- /dev/null
+++ b/bluez/repo/tools/test-runner.c
@@ -0,0 +1,869 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "tools/hciattach.h"
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+#define CMDLINE_MAX 2048
+
+static const char *own_binary;
+static char **test_argv;
+static int test_argc;
+
+static bool run_auto = false;
+static bool start_dbus = false;
+static int num_devs = 0;
+static const char *qemu_binary = NULL;
+static const char *kernel_image = NULL;
+
+static const char *qemu_table[] = {
+	"qemu-system-x86_64",
+	"qemu-system-i386",
+	"/usr/bin/qemu-system-x86_64",
+	"/usr/bin/qemu-system-i386",
+	NULL
+};
+
+static const char *find_qemu(void)
+{
+	int i;
+
+	for (i = 0; qemu_table[i]; i++) {
+		struct stat st;
+
+		if (!stat(qemu_table[i], &st))
+			return qemu_table[i];
+	}
+
+	return NULL;
+}
+
+static const char *kernel_table[] = {
+	"bzImage",
+	"arch/x86/boot/bzImage",
+	"vmlinux",
+	"arch/x86/boot/vmlinux",
+	NULL
+};
+
+static const char *find_kernel(void)
+{
+	int i;
+
+	for (i = 0; kernel_table[i]; i++) {
+		struct stat st;
+
+		if (!stat(kernel_table[i], &st))
+			return kernel_table[i];
+	}
+
+	return NULL;
+}
+
+static const struct {
+	const char *target;
+	const char *linkpath;
+} dev_table[] = {
+	{ "/proc/self/fd",	"/dev/fd"	},
+	{ "/proc/self/fd/0",	"/dev/stdin"	},
+	{ "/proc/self/fd/1",	"/dev/stdout"	},
+	{ "/proc/self/fd/2",	"/dev/stderr"	},
+	{ }
+};
+
+static const struct {
+	const char *fstype;
+	const char *target;
+	const char *options;
+	unsigned long flags;
+} mount_table[] = {
+	{ "sysfs",    "/sys",     NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "proc",     "/proc",    NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV },
+	{ "devtmpfs", "/dev",     "mode=0755", MS_NOSUID|MS_STRICTATIME },
+	{ "devpts",   "/dev/pts", "mode=0620", MS_NOSUID|MS_NOEXEC },
+	{ "tmpfs",    "/dev/shm", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME },
+	{ "tmpfs",    "/run",     "mode=0755", MS_NOSUID|MS_NODEV|MS_STRICTATIME },
+	{ "tmpfs",    "/tmp",              NULL, 0 },
+	{ "debugfs",  "/sys/kernel/debug", NULL, 0 },
+	{ }
+};
+
+static const char *config_table[] = {
+	"/var/lib/bluetooth",
+	"/etc/bluetooth",
+	"/etc/dbus-1",
+	"/usr/share/dbus-1",
+	NULL
+};
+
+static void prepare_sandbox(void)
+{
+	int i;
+
+	for (i = 0; mount_table[i].fstype; i++) {
+		struct stat st;
+
+		if (lstat(mount_table[i].target, &st) < 0) {
+			printf("Creating %s\n", mount_table[i].target);
+			mkdir(mount_table[i].target, 0755);
+		}
+
+		printf("Mounting %s to %s\n", mount_table[i].fstype,
+						mount_table[i].target);
+
+		if (mount(mount_table[i].fstype,
+				mount_table[i].target,
+				mount_table[i].fstype,
+				mount_table[i].flags,
+				mount_table[i].options) < 0)
+			perror("Failed to mount filesystem");
+	}
+
+	for (i = 0; dev_table[i].target; i++) {
+		printf("Linking %s to %s\n", dev_table[i].linkpath,
+						dev_table[i].target);
+
+		if (symlink(dev_table[i].target, dev_table[i].linkpath) < 0)
+			perror("Failed to create device symlink");
+	}
+
+	printf("Creating new session group leader\n");
+	setsid();
+
+	printf("Setting controlling terminal\n");
+	ioctl(STDIN_FILENO, TIOCSCTTY, 1);
+
+	for (i = 0; config_table[i]; i++) {
+		printf("Creating %s\n", config_table[i]);
+
+		if (mount("tmpfs", config_table[i], "tmpfs",
+				MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+				"mode=0755") < 0)
+			perror("Failed to create filesystem");
+	}
+}
+
+static char *const qemu_argv[] = {
+	"",
+	"-nodefaults",
+	"-nodefconfig",
+	"-no-user-config",
+	"-monitor", "none",
+	"-display", "none",
+	"-machine", "type=q35,accel=kvm:tcg",
+	"-m", "192M",
+	"-nographic",
+	"-vga", "none",
+	"-net", "none",
+	"-balloon", "none",
+	"-no-acpi",
+	"-no-hpet",
+	"-no-reboot",
+	"-fsdev", "local,id=fsdev-root,path=/,readonly,security_model=none",
+	"-device", "virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root",
+	"-chardev", "stdio,id=chardev-serial0,signal=off",
+	"-device", "pci-serial,chardev=chardev-serial0",
+	NULL
+};
+
+static char *const qemu_envp[] = {
+	"HOME=/",
+	NULL
+};
+
+static void check_virtualization(void)
+{
+#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
+	uint32_t ecx;
+
+	__asm__ __volatile__("cpuid" : "=c" (ecx) : "a" (1) : "memory");
+
+	if (!!(ecx & (1 << 5)))
+		printf("Found support for Virtual Machine eXtensions\n");
+#endif
+}
+
+static void start_qemu(void)
+{
+	char cwd[PATH_MAX], initcmd[PATH_MAX], testargs[PATH_MAX];
+	char cmdline[CMDLINE_MAX];
+	char **argv;
+	int i, pos;
+
+	check_virtualization();
+
+	if (!getcwd(cwd, sizeof(cwd)))
+		strcat(cwd, "/");
+
+	if (own_binary[0] == '/')
+		snprintf(initcmd, sizeof(initcmd), "%s", own_binary);
+	else
+		snprintf(initcmd, sizeof(initcmd), "%s/%s", cwd, own_binary);
+
+	pos = snprintf(testargs, sizeof(testargs), "%s", test_argv[0]);
+
+	for (i = 1; i < test_argc; i++) {
+		int len = sizeof(testargs) - pos;
+		pos += snprintf(testargs + pos, len, " %s", test_argv[i]);
+	}
+
+	snprintf(cmdline, sizeof(cmdline),
+				"console=ttyS0,115200n8 earlyprintk=serial "
+				"rootfstype=9p "
+				"rootflags=trans=virtio,version=9p2000.L "
+				"acpi=off pci=noacpi noapic quiet ro init=%s "
+				"TESTHOME=%s TESTDBUS=%u TESTDEVS=%d "
+				"TESTAUTO=%u TESTARGS=\'%s\'", initcmd, cwd,
+				start_dbus, num_devs, run_auto, testargs);
+
+	argv = alloca(sizeof(qemu_argv) +
+				(sizeof(char *) * (4 + (num_devs * 4))));
+	memcpy(argv, qemu_argv, sizeof(qemu_argv));
+
+	pos = (sizeof(qemu_argv) / sizeof(char *)) - 1;
+
+	argv[0] = (char *) qemu_binary;
+
+	argv[pos++] = "-kernel";
+	argv[pos++] = (char *) kernel_image;
+	argv[pos++] = "-append";
+	argv[pos++] = (char *) cmdline;
+
+	for (i = 0; i < num_devs; i++) {
+		const char *path = "/tmp/bt-server-bredr";
+		char *chrdev, *serdev;
+
+		chrdev = alloca(32 + strlen(path));
+		sprintf(chrdev, "socket,path=%s,id=bt%d", path, i);
+
+		serdev = alloca(32);
+		sprintf(serdev, "pci-serial,chardev=bt%d", i);
+
+		argv[pos++] = "-chardev";
+		argv[pos++] = chrdev;
+		argv[pos++] = "-device";
+		argv[pos++] = serdev;
+	}
+
+	argv[pos] = NULL;
+
+	execve(argv[0], argv, qemu_envp);
+}
+
+static int open_serial(const char *path)
+{
+	struct termios ti;
+	int fd, saved_ldisc, ldisc = N_HCI;
+
+	fd = open(path, O_RDWR | O_NOCTTY);
+	if (fd < 0) {
+		perror("Failed to open serial port");
+		return -1;
+	}
+
+	if (tcflush(fd, TCIOFLUSH) < 0) {
+		perror("Failed to flush serial port");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) {
+		perror("Failed get serial line discipline");
+		close(fd);
+		return -1;
+	}
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+
+	ti.c_cflag |= (B115200 | CLOCAL | CREAD);
+
+	/* Set flow control */
+	ti.c_cflag |= CRTSCTS;
+
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		perror("Failed to set serial port settings");
+		close(fd);
+		return -1;
+	}
+
+	if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
+		perror("Failed set serial line discipline");
+		close(fd);
+		return -1;
+	}
+
+	printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc);
+
+	return fd;
+}
+
+static int attach_proto(const char *path, unsigned int proto,
+					unsigned int mandatory_flags,
+					unsigned int optional_flags)
+{
+	unsigned int flags = mandatory_flags | optional_flags;
+	int fd, dev_id;
+
+	fd = open_serial(path);
+	if (fd < 0)
+		return -1;
+
+	if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+		if (errno == EINVAL) {
+			if (ioctl(fd, HCIUARTSETFLAGS, mandatory_flags) < 0) {
+				perror("Failed to set mandatory flags");
+				close(fd);
+				return -1;
+			}
+		} else {
+			perror("Failed to set flags");
+			close(fd);
+			return -1;
+		}
+	}
+
+	if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) {
+		perror("Failed to set protocol");
+		close(fd);
+		return -1;
+	}
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE);
+	if (dev_id < 0) {
+		perror("Failed to get device id");
+		close(fd);
+		return -1;
+	}
+
+	printf("Device index %d attached\n", dev_id);
+
+	return fd;
+}
+
+static void create_dbus_system_conf(void)
+{
+	FILE *fp;
+
+	fp = fopen("/etc/dbus-1/system.conf", "we");
+	if (!fp)
+		return;
+
+	fputs("<!DOCTYPE busconfig PUBLIC "
+		"\"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN\" "
+		"\"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd\">\n", fp);
+	fputs("<busconfig>\n", fp);
+	fputs("<type>system</type>\n", fp);
+	fputs("<listen>unix:path=/run/dbus/system_bus_socket</listen>\n", fp);
+	fputs("<policy context=\"default\">\n", fp);
+	fputs("<allow user=\"*\"/>\n", fp);
+	fputs("<allow own=\"*\"/>\n", fp);
+	fputs("<allow send_type=\"method_call\"/>\n",fp);
+	fputs("<allow send_type=\"signal\"/>\n", fp);
+	fputs("<allow send_type=\"method_return\"/>\n", fp);
+	fputs("<allow send_type=\"error\"/>\n", fp);
+	fputs("<allow receive_type=\"method_call\"/>\n",fp);
+	fputs("<allow receive_type=\"signal\"/>\n", fp);
+	fputs("<allow receive_type=\"method_return\"/>\n", fp);
+	fputs("<allow receive_type=\"error\"/>\n", fp);
+	fputs("</policy>\n", fp);
+	fputs("</busconfig>\n", fp);
+
+	fclose(fp);
+
+	mkdir("/run/dbus", 0755);
+}
+
+static pid_t start_dbus_daemon(void)
+{
+	char *argv[3], *envp[1];
+	pid_t pid;
+	int i;
+
+	argv[0] = "/usr/bin/dbus-daemon";
+	argv[1] = "--system";
+	argv[2] = NULL;
+
+	envp[0] = NULL;
+
+	printf("Starting D-Bus daemon\n");
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return -1;
+	}
+
+	if (pid == 0) {
+		execve(argv[0], argv, envp);
+		exit(EXIT_SUCCESS);
+	}
+
+	printf("D-Bus daemon process %d created\n", pid);
+
+	for (i = 0; i < 20; i++) {
+		struct stat st;
+
+		if (!stat("/run/dbus/system_bus_socket", &st)) {
+			printf("Found D-Bus daemon socket\n");
+			break;
+		}
+
+		usleep(25 * 1000);
+	}
+
+	return pid;
+}
+
+static const char *daemon_table[] = {
+	"bluetoothd",
+	"src/bluetoothd",
+	"/usr/sbin/bluetoothd",
+	"/usr/libexec/bluetooth/bluetoothd",
+	NULL
+};
+
+static pid_t start_bluetooth_daemon(const char *home)
+{
+	const char *daemon = NULL;
+	char *argv[3], *envp[2];
+	pid_t pid;
+	int i;
+
+	if (chdir(home + 5) < 0) {
+		perror("Failed to change home directory for daemon");
+		return -1;
+	}
+
+	for (i = 0; daemon_table[i]; i++) {
+		struct stat st;
+
+		if (!stat(daemon_table[i], &st)) {
+			daemon = daemon_table[i];
+			break;
+		}
+	}
+
+	if (!daemon) {
+		fprintf(stderr, "Failed to locate Bluetooth daemon binary\n");
+		return -1;
+	}
+
+	printf("Using Bluetooth daemon %s\n", daemon);
+
+	argv[0] = (char *) daemon;
+	argv[1] = "--nodetach";
+	argv[2] = NULL;
+
+	envp[0] = "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket";
+	envp[1] = NULL;
+
+	printf("Starting Bluetooth daemon\n");
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return -1;
+	}
+
+	if (pid == 0) {
+		execve(argv[0], argv, envp);
+		exit(EXIT_SUCCESS);
+	}
+
+	printf("Bluetooth daemon process %d created\n", pid);
+
+	return pid;
+}
+
+static const char *test_table[] = {
+	"mgmt-tester",
+	"smp-tester",
+	"l2cap-tester",
+	"rfcomm-tester",
+	"sco-tester",
+	"bnep-tester",
+	"check-selftest",
+	"tools/mgmt-tester",
+	"tools/smp-tester",
+	"tools/l2cap-tester",
+	"tools/rfcomm-tester",
+	"tools/sco-tester",
+	"tools/bnep-tester",
+	"tools/check-selftest",
+	NULL
+};
+
+static void run_command(char *cmdname, char *home)
+{
+	char *argv[9], *envp[3];
+	int pos = 0, idx = 0;
+	int serial_fd;
+	pid_t pid, dbus_pid, daemon_pid;
+
+	if (num_devs) {
+		const char *node = "/dev/ttyS1";
+		unsigned int basic_flags, extra_flags;
+
+		printf("Attaching BR/EDR controller to %s\n", node);
+
+		basic_flags = (1 << HCI_UART_RESET_ON_INIT);
+		extra_flags = (1 << HCI_UART_VND_DETECT);
+
+		serial_fd = attach_proto(node, HCI_UART_H4, basic_flags,
+								extra_flags);
+	} else
+		serial_fd = -1;
+
+	if (start_dbus) {
+		create_dbus_system_conf();
+		dbus_pid = start_dbus_daemon();
+		daemon_pid = start_bluetooth_daemon(home);
+	} else {
+		dbus_pid = -1;
+		daemon_pid = -1;
+	}
+
+start_next:
+	if (run_auto) {
+		if (chdir(home + 5) < 0) {
+			perror("Failed to change home test directory");
+			return;
+		}
+
+		while (1) {
+			struct stat st;
+
+			if (!test_table[idx])
+				return;
+
+			if (!stat(test_table[idx], &st))
+				break;
+
+			idx++;
+		}
+
+		argv[0] = (char *) test_table[idx];
+		argv[1] = "-q";
+		argv[2] = NULL;
+	} else {
+		while (1) {
+			char *ptr;
+
+			ptr = strchr(cmdname, ' ');
+			if (!ptr) {
+				argv[pos++] = cmdname;
+				break;
+			}
+
+			*ptr = '\0';
+			argv[pos++] = cmdname;
+			if (pos > 8)
+				break;
+
+			cmdname = ptr + 1;
+		}
+
+		argv[pos] = NULL;
+	}
+
+	pos = 0;
+	envp[pos++] = "TERM=linux";
+	if (home)
+		envp[pos++] = home;
+	envp[pos] = NULL;
+
+	printf("Running command %s\n", argv[0]);
+
+	pid = fork();
+	if (pid < 0) {
+		perror("Failed to fork new process");
+		return;
+	}
+
+	if (pid == 0) {
+		if (home) {
+			printf("Changing into directory %s\n", home + 5);
+			if (chdir(home + 5) < 0)
+				perror("Failed to change directory");
+		}
+
+		execve(argv[0], argv, envp);
+		exit(EXIT_SUCCESS);
+	}
+
+	printf("New process %d created\n", pid);
+
+	while (1)  {
+		pid_t corpse;
+		int status;
+
+		corpse = waitpid(WAIT_ANY, &status, 0);
+		if (corpse < 0 || corpse == 0)
+			continue;
+
+		if (WIFEXITED(status))
+			printf("Process %d exited with status %d\n",
+						corpse, WEXITSTATUS(status));
+		else if (WIFSIGNALED(status))
+			printf("Process %d terminated with signal %d\n",
+						corpse, WTERMSIG(status));
+		else if (WIFSTOPPED(status))
+			printf("Process %d stopped with signal %d\n",
+						corpse, WSTOPSIG(status));
+		else if (WIFCONTINUED(status))
+			printf("Process %d continued\n", corpse);
+
+		if (corpse == dbus_pid) {
+			printf("D-Bus daemon terminated\n");
+			dbus_pid = -1;
+		}
+
+		if (corpse == daemon_pid) {
+			printf("Bluetooth daemon terminated\n");
+			daemon_pid = -1;
+		}
+
+		if (corpse == pid) {
+			if (!run_auto) {
+				if (daemon_pid > 0)
+					kill(daemon_pid, SIGTERM);
+				if (dbus_pid > 0)
+					kill(dbus_pid, SIGTERM);
+			}
+			break;
+		}
+	}
+
+	if (run_auto) {
+		idx++;
+		goto start_next;
+	}
+
+	if (serial_fd >= 0) {
+		close(serial_fd);
+		serial_fd = -1;
+	}
+}
+
+static void run_tests(void)
+{
+	char cmdline[CMDLINE_MAX], *ptr, *cmds, *home = NULL;
+	FILE *fp;
+
+	fp = fopen("/proc/cmdline", "re");
+	if (!fp) {
+		fprintf(stderr, "Failed to open kernel command line\n");
+		return;
+	}
+
+	ptr = fgets(cmdline, sizeof(cmdline), fp);
+	fclose(fp);
+
+	if (!ptr) {
+		fprintf(stderr, "Failed to read kernel command line\n");
+		return;
+	}
+
+	ptr = strstr(cmdline, "TESTARGS=");
+	if (!ptr) {
+		fprintf(stderr, "No test command section found\n");
+		return;
+	}
+
+	cmds = ptr + 10;
+	ptr = strchr(cmds, '\'');
+	if (!ptr) {
+		fprintf(stderr, "Malformed test command section\n");
+		return;
+	}
+
+	*ptr = '\0';
+
+	ptr = strstr(cmdline, "TESTAUTO=1");
+	if (ptr) {
+		printf("Automatic test execution requested\n");
+		run_auto= true;
+	}
+
+	ptr = strstr(cmdline, "TESTDEVS=1");
+	if (ptr) {
+		printf("Attachment of devices requested\n");
+		num_devs = 1;
+	}
+
+	ptr = strstr(cmdline, "TESTDBUS=1");
+	if (ptr) {
+		printf("D-Bus daemon requested\n");
+		start_dbus = true;
+	}
+
+	ptr = strstr(cmdline, "TESTHOME=");
+	if (ptr) {
+		home = ptr + 4;
+		ptr = strpbrk(home + 9, " \r\n");
+		if (ptr)
+			*ptr = '\0';
+	}
+
+	run_command(cmds, home);
+}
+
+static void usage(void)
+{
+	printf("test-runner - Automated test execution utility\n"
+		"Usage:\n");
+	printf("\ttest-runner [options] [--] <command> [args]\n");
+	printf("Options:\n"
+		"\t-a, --auto             Find tests and run them\n"
+		"\t-d, --dbus             Start D-Bus daemon\n"
+		"\t-u, --unix [path]      Provide serial device\n"
+		"\t-q, --qemu <path>      QEMU binary\n"
+		"\t-k, --kernel <image>   Kernel image (bzImage)\n"
+		"\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+	{ "all",     no_argument,       NULL, 'a' },
+	{ "auto",    no_argument,       NULL, 'a' },
+	{ "unix",    no_argument,       NULL, 'u' },
+	{ "dbus",    no_argument,       NULL, 'd' },
+	{ "qemu",    required_argument, NULL, 'q' },
+	{ "kernel",  required_argument, NULL, 'k' },
+	{ "version", no_argument,       NULL, 'v' },
+	{ "help",    no_argument,       NULL, 'h' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	if (getpid() == 1 && getppid() == 0) {
+		prepare_sandbox();
+		run_tests();
+
+		sync();
+		reboot(RB_AUTOBOOT);
+		return EXIT_SUCCESS;
+	}
+
+	for (;;) {
+		int opt;
+
+		opt = getopt_long(argc, argv, "audq:k:vh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'a':
+			run_auto = true;
+			break;
+		case 'u':
+			num_devs = 1;
+			break;
+		case 'd':
+			start_dbus = true;
+			break;
+		case 'q':
+			qemu_binary = optarg;
+			break;
+		case 'k':
+			kernel_image = optarg;
+			break;
+		case 'v':
+			printf("%s\n", VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage();
+			return EXIT_SUCCESS;
+		default:
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (run_auto) {
+		if (argc - optind > 0) {
+			fprintf(stderr, "Invalid command line parameters\n");
+			return EXIT_FAILURE;
+		}
+	} else {
+		if (argc - optind < 1) {
+			fprintf(stderr, "Failed to specify test command\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	own_binary = argv[0];
+	test_argv = argv + optind;
+	test_argc = argc - optind;
+
+	if (!qemu_binary) {
+		qemu_binary = find_qemu();
+		if (!qemu_binary) {
+			fprintf(stderr, "No default QEMU binary found\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!kernel_image) {
+		kernel_image = find_kernel();
+		if (!kernel_image) {
+			fprintf(stderr, "No default kernel image found\n");
+			return EXIT_FAILURE;
+		}
+	}
+
+	printf("Using QEMU binary %s\n", qemu_binary);
+	printf("Using kernel image %s\n", kernel_image);
+
+	start_qemu();
+
+	return EXIT_SUCCESS;
+}
diff --git a/bluez/repo/tools/ubcsp.c b/bluez/repo/tools/ubcsp.c
new file mode 100644
index 0000000..b3f883a
--- /dev/null
+++ b/bluez/repo/tools/ubcsp.c
@@ -0,0 +1,1180 @@
+/*

+ *

+ *  BlueZ - Bluetooth protocol stack for Linux

+ *

+ *  Copyright (C) 2000-2005  CSR Ltd.

+ *

+ *

+ *  Permission is hereby granted, free of charge, to any person obtaining

+ *  a copy of this software and associated documentation files (the

+ *  "Software"), to deal in the Software without restriction, including

+ *  without limitation the rights to use, copy, modify, merge, publish,

+ *  distribute, sublicense, and/or sell copies of the Software, and to

+ *  permit persons to whom the Software is furnished to do so, subject to

+ *  the following conditions:

+ *

+ *  The above copyright notice and this permission notice shall be included

+ *  in all copies or substantial portions of the Software.

+ *

+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

+ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

+ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

+ *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY

+ *  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,

+ *  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE

+ *  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ *

+ */

+

+#ifdef HAVE_CONFIG_H

+#include <config.h>

+#endif

+

+/*****************************************************************************/

+/*****************************************************************************/

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp,c                                                                 **/

+/**                                                                         **/

+/** MicroBCSP - a very low cost implementation of the BCSP protocol         **/

+/**                                                                         **/

+/*****************************************************************************/

+

+#include "ubcsp.h"

+

+#if SHOW_PACKET_ERRORS || SHOW_LE_STATES

+#include <stdio.h>

+#include <windows.h>

+#endif

+

+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc);

+static uint16 ubcsp_crc_reverse (uint16);

+

+/*****************************************************************************/

+/**                                                                         **/

+/** Constant Data - ROM                                                     **/

+/**                                                                         **/

+/*****************************************************************************/

+

+/* This is the storage for the link establishment messages */

+

+static const uint8 ubcsp_le_buffer[4][4] =

+	{

+		{ 0xDA, 0xDC, 0xED, 0xED },

+		{ 0xAC, 0xAF, 0xEF, 0xEE },

+		{ 0xAD, 0xEF, 0xAC, 0xED },

+		{ 0xDE, 0xAD, 0xD0, 0xD0 },

+	};

+

+/* These are the link establishment headers */

+/* The two version are for the CRC and non-CRC varients */

+

+#if UBCSP_CRC

+static const uint8 ubcsp_send_le_header[4] = 

+	{

+		0x40, 0x41, 0x00, 0x7E

+	};

+#else

+static const uint8 ubcsp_send_le_header[4] = 

+	{

+		0x00, 0x41, 0x00, 0xBE

+	};

+#endif

+

+/*****************************************************************************/

+/**                                                                         **/

+/** Static Data - RAM                                                       **/

+/**                                                                         **/

+/*****************************************************************************/

+

+/* This is the storage for all state data for ubcsp */

+

+static struct ubcsp_configuration ubcsp_config;

+

+/* This is the ACK packet header - this will be overwritten when

+   we create an ack packet */

+

+static uint8 ubcsp_send_ack_header[4] = 

+	{

+		0x00, 0x00, 0x00, 0x00

+	};

+

+/* This is the deslip lookup table */

+

+static const uint8 ubcsp_deslip[2] =

+	{

+		SLIP_FRAME, SLIP_ESCAPE,

+	};

+

+/* This is a state machine table for link establishment */

+

+static uint8 next_le_packet[16] =

+	{

+		ubcsp_le_sync,			// uninit

+		ubcsp_le_conf,			// init

+		ubcsp_le_none,			// active

+		ubcsp_le_none,

+		ubcsp_le_sync_resp,		// sync_resp

+		ubcsp_le_sync_resp,

+		ubcsp_le_none,

+		ubcsp_le_none,

+		ubcsp_le_none,			// conf_resp

+		ubcsp_le_conf_resp,

+		ubcsp_le_conf_resp,

+		ubcsp_le_none,

+	};

+

+/* This is the storage required for building send and crc data */

+

+static uint8 ubcsp_send_header[4];

+static uint8 ubcsp_send_crc[2];

+

+/* This is where the receive header is stored before the payload arrives */

+

+static uint8 ubcsp_receive_header[4];

+

+/*****************************************************************************/

+/**                                                                         **/

+/** Code - ROM or RAM                                                       **/

+/**                                                                         **/

+/*****************************************************************************/

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_initialize                                                        **/

+/**                                                                         **/

+/** This initializes the state of the ubcsp engine to a known values        **/

+/**                                                                         **/

+/*****************************************************************************/

+

+void ubcsp_initialize (void)

+{

+	ubcsp_config.ack_number = 0;

+	ubcsp_config.sequence_number = 0;

+	ubcsp_config.send_ptr = 0;

+	ubcsp_config.send_size = 0;

+	ubcsp_config.receive_index = -4;

+

+	ubcsp_config.delay = 0;

+

+#if SHOW_LE_STATES

+	printf ("Hello Link Uninitialized\n");

+#endif

+

+	ubcsp_config.link_establishment_state = ubcsp_le_uninitialized;

+	ubcsp_config.link_establishment_packet = ubcsp_le_sync;

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_send_packet                                                       **/

+/**                                                                         **/

+/** This sends a packet structure for sending to the ubcsp engine           **/

+/** This can only be called when the activity indication from ubcsp_poll    **/

+/** indicates that a packet can be sent with UBCSP_PACKET_SENT              **/

+/**                                                                         **/

+/*****************************************************************************/

+

+void ubcsp_send_packet (struct ubcsp_packet *send_packet)

+{

+	/* Initialize the send data to the packet we want to send */

+

+	ubcsp_config.send_packet = send_packet;

+

+	/* we cannot send the packet at the moment

+	   when we can at the moment, just set things to 0 */

+

+	ubcsp_config.send_size = 0;

+	ubcsp_config.send_ptr = 0;

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_receive_packet                                                    **/

+/**                                                                         **/

+/** This sends a packet structure for receiving to the ubcsp engine         **/

+/** This can only be called when the activity indication from ubcsp_poll    **/

+/** indicates that a packet can be sent with UBCSP_PACKET_RECEIVED          **/

+/**                                                                         **/

+/*****************************************************************************/

+

+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet)

+{

+	/* Initialize the receive data to the packet we want to receive */

+

+	ubcsp_config.receive_packet = receive_packet;

+

+	/* setup to receive the header first */

+

+	ubcsp_config.receive_index = -4;

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_calc_crc                                                          **/

+/**                                                                         **/

+/** Takes the next 8 bit value ch, and updates the crc with this value      **/

+/**                                                                         **/

+/*****************************************************************************/

+

+

+#ifdef UBCSP_CRC

+

+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc)

+{

+	/* Calculate the CRC using the above 16 entry lookup table */

+

+	static const uint16 crc_table[] =

+		{

+			0x0000, 0x1081, 0x2102, 0x3183,

+			0x4204, 0x5285, 0x6306, 0x7387,

+			0x8408, 0x9489, 0xa50a, 0xb58b,

+			0xc60c, 0xd68d, 0xe70e, 0xf78f

+		};

+

+	/* Do this four bits at a time - more code, less space */

+

+    crc = (crc >> 4) ^ crc_table[(crc ^ ch) & 0x000f];

+    crc = (crc >> 4) ^ crc_table[(crc ^ (ch >> 4)) & 0x000f];

+

+	return crc;

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_crc_reverse                                                       **/

+/**                                                                         **/

+/** Reserves the bits in crc and returns the new value                      **/

+/**                                                                         **/

+/*****************************************************************************/

+

+static uint16 ubcsp_crc_reverse (uint16 crc)

+{

+	int32

+		b,

+		rev;

+

+	/* Reserse the bits to compute the actual CRC value */

+

+	for (b = 0, rev=0; b < 16; b++)

+	{

+		rev = rev << 1;

+		rev |= (crc & 1);

+		crc = crc >> 1;

+	}

+

+	return rev;

+}

+

+#endif

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_put_slip_uart                                                     **/

+/**                                                                         **/

+/** Outputs a single octet to the uart                                      **/

+/** If the octet needs to be escaped, then output the escape value          **/

+/** and then store the second octet to be output later                      **/

+/**                                                                         **/

+/*****************************************************************************/

+

+static void ubcsp_put_slip_uart (uint8 ch)

+{

+	/* output a single UART octet */

+

+	/* If it needs to be escaped, then output the escape octet

+	   and set the send_slip_escape so that the next time we

+	   output the second octet for the escape correctly.

+	   This is done right at the top of ubcsp_poll */

+

+	if (ch == SLIP_FRAME)

+	{

+		put_uart (SLIP_ESCAPE);

+		ubcsp_config.send_slip_escape = SLIP_ESCAPE_FRAME;

+	}

+	else if (ch == SLIP_ESCAPE)

+	{

+		put_uart (SLIP_ESCAPE);

+		ubcsp_config.send_slip_escape = SLIP_ESCAPE_ESCAPE;

+	}

+	else

+	{

+		/* Not escaped, so just output octet */

+

+		put_uart (ch);

+	}

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_which_le_payload                                                  **/

+/**                                                                         **/

+/** Check the payload of this packet, and determine which of the four       **/

+/** link establishment packets this was.                                    **/

+/** Can return 5 if it is not a valid link establishment packet             **/

+/**                                                                         **/

+/*****************************************************************************/

+

+static uint32 ubcsp_which_le_payload (const uint8 *payload)

+{

+	static int32

+		octet,

+		loop;

+

+	/* Search through the various link establishment payloads to find

+	   which one we have received */

+

+	for (loop = 0; loop < 4; loop ++)

+	{

+		for (octet = 0; octet < 4; octet ++)

+		{

+			if (payload[octet] != ubcsp_le_buffer[loop][octet])

+			{

+				/* Bad match, just to loop again */

+				goto bad_match_loop;

+			}

+		}

+

+		/* All the octets matched, return the value */

+

+		return loop;

+

+		/* Jumps out of octet loop if we got a bad match */

+bad_match_loop:

+		{}

+	}

+

+	/* Non of the link establishment payloads matched - return invalid value */

+

+	return 5;

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_recevied_packet                                                   **/

+/**                                                                         **/

+/** This function is called when we have a SLIP END octet and a full        **/

+/** packet header and possibly data in the receive packet                   **/

+/**                                                                         **/

+/*****************************************************************************/

+

+static uint8 ubcsp_recevied_packet (void)

+{

+	static uint8

+		receive_crc,

+		receive_seq,

+		receive_ack,

+		activity;

+

+#if UBCSP_CRC

+	static int32

+		loop;

+

+	static uint16

+		crc;

+#endif

+

+	static uint16

+		length;

+

+	/* Keep track of what activity this received packet will cause */

+

+	activity = 0;

+

+	/*** Do all error checks that we can ***/

+

+	/* First check the header checksum */

+

+	if (((ubcsp_receive_header[0] + ubcsp_receive_header[1] + ubcsp_receive_header[2] + ubcsp_receive_header[3]) & 0xff) != 0xff)

+	{

+		/* Header Checksum Error */

+

+#if SHOW_PACKET_ERRORS

+		printf ("\n######################## Header Checksum Error %02X %02X %02X %02X\n",

+			ubcsp_receive_header[0],

+			ubcsp_receive_header[1],

+			ubcsp_receive_header[2],

+			ubcsp_receive_header[3]);

+#endif

+

+		/* If we have a header checksum error, send an ack in return

+		   this gets a packet to be resent as quickly as possible */

+

+		ubcsp_config.send_ack = 1;

+

+		return activity;

+	}

+

+	/* Decode the received packets header */

+

+	ubcsp_config.receive_packet->reliable = (ubcsp_receive_header[0] & 0x80) >> 7;

+

+	receive_crc = (ubcsp_receive_header[0] & 0x40) >> 6;

+	receive_ack = (ubcsp_receive_header[0] & 0x38) >> 3;

+	receive_seq = (ubcsp_receive_header[0] & 0x07);

+

+	ubcsp_config.receive_packet->channel = (ubcsp_receive_header[1] & 0x0f);

+

+	length =

+		((ubcsp_receive_header[1] & 0xf0) >> 4) |

+		(ubcsp_receive_header[2] << 4);

+

+#if SHOW_PACKET_ERRORS

+	if (ubcsp_config.receive_packet->reliable)

+	{

+		printf (" : %10d         Recv SEQ: %d ACK %d\n",

+			GetTickCount () % 100000,

+			receive_seq,

+			receive_ack);

+	}

+	else if (ubcsp_config.receive_packet->channel != 1)

+	{

+		printf (" : %10d          Recv        ACK %d\n",

+			GetTickCount () % 100000,

+			receive_ack);

+	}

+#endif

+

+	/* Check for length errors */

+

+#if UBCSP_CRC

+	if (receive_crc)

+	{

+		/* If this packet had a CRC, then the length of the payload 

+		   should be 2 less than the received size of the payload */

+

+		if (length + 2 != ubcsp_config.receive_index)

+		{

+			/* Slip Length Error */

+

+#if SHOW_PACKET_ERRORS

+			printf ("\n######################## Slip Length Error (With CRC) %d,%d\n", length, ubcsp_config.receive_index - 2);

+#endif

+

+			/* If we have a payload length error, send an ack in return

+			   this gets a packet to be resent as quickly as possible */

+

+			ubcsp_config.send_ack = 1;

+			return activity;

+		}

+

+		/* We have a CRC at the end of this packet */

+

+		ubcsp_config.receive_index -= 2;

+

+		/* Calculate the packet CRC */

+

+		crc = 0xffff;

+

+		/* CRC the packet header */

+

+		for (loop = 0; loop < 4; loop ++)

+		{

+			crc = ubcsp_calc_crc (ubcsp_receive_header[loop], crc);

+		}

+

+		/* CRC the packet payload - without the CRC bytes */

+

+		for (loop = 0; loop < ubcsp_config.receive_index; loop ++)

+		{

+			crc = ubcsp_calc_crc (ubcsp_config.receive_packet->payload[loop], crc);

+		}

+

+		/* Reverse the CRC */

+

+		crc = ubcsp_crc_reverse (crc);

+

+		/* Check the CRC is correct */

+

+		if

+		(

+			(((crc & 0xff00) >> 8) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index]) ||

+			((crc & 0xff) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index + 1])

+		)

+		{

+#if SHOW_PACKET_ERRORS

+			printf ("\n######################## CRC Error\n");

+#endif

+

+			/* If we have a packet crc error, send an ack in return

+			   this gets a packet to be resent as quickly as possible */

+

+			ubcsp_config.send_ack = 1;

+			return activity;

+		}

+	}

+	else

+	{

+#endif

+		/* No CRC present, so just check the length of payload with that received */

+

+		if (length != ubcsp_config.receive_index)

+		{

+			/* Slip Length Error */

+

+#if SHOW_PACKET_ERRORS

+			printf ("\n######################## Slip Length Error (No CRC) %d,%d\n", length, ubcsp_config.receive_index);

+#endif

+

+			/* If we have a payload length error, send an ack in return

+			   this gets a packet to be resent as quickly as possible */

+

+			ubcsp_config.send_ack = 1;

+			return activity;

+		}

+#if UBCSP_CRC

+	}

+#endif

+

+	/*** We have a fully formed packet having passed all data integrity checks ***/

+

+	/* Check if we have an ACK for the last packet we sent */

+

+	if (receive_ack != ubcsp_config.sequence_number)

+	{

+		/* Since we only have a window size of 1, if the ACK is not equal to SEQ

+		   then the packet was sent */

+

+		if

+		(

+			(ubcsp_config.send_packet) &&

+			(ubcsp_config.send_packet->reliable)

+		)

+		{

+			/* We had sent a reliable packet, so clear this packet

+			   Then increament the sequence number for the next packet */

+

+			ubcsp_config.send_packet = 0;

+			ubcsp_config.sequence_number ++;

+			ubcsp_config.delay = 0;

+

+			/* Notify the caller that we have SENT a packet */

+

+			activity |= UBCSP_PACKET_SENT;

+		}

+	}

+

+	/*** Now we can concentrate of the packet we have received ***/

+

+	/* Check for Link Establishment packets */

+

+	if (ubcsp_config.receive_packet->channel == 1)

+	{

+		/* Link Establishment */

+

+		ubcsp_config.delay = 0;

+

+		/* Find which link establishment packet this payload means

+		   This could return 5, meaning none */

+

+		switch (ubcsp_which_le_payload (ubcsp_config.receive_packet->payload))

+		{

+			case 0:

+			{

+				/* SYNC Recv'd */

+

+#if SHOW_LE_STATES

+				printf ("Recv SYNC\n");

+#endif

+

+				/* If we receive a SYNC, then we respond to it with a SYNC RESP

+				   but only if we are not active.

+				   If we are active, then we have a PEER RESET */

+

+				if (ubcsp_config.link_establishment_state < ubcsp_le_active)

+				{

+					ubcsp_config.link_establishment_resp = 1;

+				}

+				else

+				{

+					/* Peer reset !!!! */

+

+#if SHOW_LE_STATES

+					printf ("\n\n\n\n\nPEER RESET\n\n");

+#endif

+

+					/* Reinitialize the link */

+

+					ubcsp_initialize ();

+

+					/* Tell the host what has happened */

+

+					return UBCSP_PEER_RESET;

+				}

+				break;

+			}

+

+			case 1:

+			{

+				/* SYNC RESP Recv'd */

+

+#if SHOW_LE_STATES

+				printf ("Recv SYNC RESP\n");

+#endif

+

+				/* If we receive a SYNC RESP, push us into the initialized state */

+

+				if (ubcsp_config.link_establishment_state < ubcsp_le_initialized)

+				{

+#if SHOW_LE_STATES

+					printf ("Link Initialized\n");

+#endif

+					ubcsp_config.link_establishment_state = ubcsp_le_initialized;

+				}

+

+				break;

+			}

+

+			case 2:

+			{

+				/* CONF Recv'd */

+

+#if SHOW_LE_STATES

+				printf ("Recv CONF\n");

+#endif

+

+				/* If we receive a CONF, and we are initialized or active

+				   then respond with a CONF RESP */

+

+				if (ubcsp_config.link_establishment_state >= ubcsp_le_initialized)

+				{

+					ubcsp_config.link_establishment_resp = 2;

+				}

+

+				break;

+			}

+

+			case 3:

+			{

+				/* CONF RESP Recv'd */

+

+#if SHOW_LE_STATES

+				printf ("Recv CONF RESP\n");

+#endif

+

+				/* If we received a CONF RESP, then push us into the active state */

+

+				if (ubcsp_config.link_establishment_state < ubcsp_le_active)

+				{

+#if SHOW_LE_STATES

+					printf ("Link Active\n");

+#endif

+

+					ubcsp_config.link_establishment_state = ubcsp_le_active;

+					ubcsp_config.send_size = 0;

+

+					return activity | UBCSP_PACKET_SENT;

+				}

+

+				break;

+			}

+		}

+

+		/* We have finished processing Link Establishment packets */

+	}

+	else if (ubcsp_config.receive_index)

+	{

+		/* We have some payload data we need to process

+		   but only if we are active - otherwise, we just ignore it */

+

+		if (ubcsp_config.link_establishment_state == ubcsp_le_active)

+		{

+			if (ubcsp_config.receive_packet->reliable)

+			{

+				/* If the packet we've just received was reliable

+				   then send an ACK */

+

+				ubcsp_config.send_ack = 1;

+

+				/* We the sequence number we received is the same as 

+				   the last ACK we sent, then we have received a packet in sequence */

+

+				if (receive_seq == ubcsp_config.ack_number)

+				{

+					/* Increase the ACK number - which will be sent in the next ACK 

+					   or normal packet we send */

+

+					ubcsp_config.ack_number ++;

+

+					/* Set the values in the receive_packet structure, so the caller

+					   knows how much data we have */

+

+					ubcsp_config.receive_packet->length = length;

+					ubcsp_config.receive_packet = 0;

+

+					/* Tell the caller that we have received a packet, and that it

+					   will be ACK'ed */

+

+					activity |= UBCSP_PACKET_RECEIVED | UBCSP_PACKET_ACK;

+				}

+			}

+			else 

+			{

+				/* Set the values in the receive_packet structure, so the caller

+				   knows how much data we have */

+

+				ubcsp_config.receive_packet->length = length;

+				ubcsp_config.receive_packet = 0;

+

+				/* Tell the caller that we have received a packet */

+

+				activity |= UBCSP_PACKET_RECEIVED;

+			}

+		}
+	}
+
+	/* Just return any activity that occurred */
+
+	return activity;
+}
+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_setup_packet                                                      **/

+/**                                                                         **/

+/** This function is called to setup a packet to be sent                    **/

+/** This allows just a header, or a header and payload to be sent           **/

+/** It also allows the header checksum to be precalcuated                   **/

+/** or calculated here                                                      **/

+/** part1 is always 4 bytes                                                 **/

+/**                                                                         **/

+/*****************************************************************************/

+

+static void ubcsp_setup_packet (uint8 *part1, uint8 calc, uint8 *part2, uint16 len2)

+{

+	/* If we need to calculate the checksum, do that now */

+

+	if (calc)

+	{

+		part1[3] =

+			~(part1[0] + part1[1] + part1[2]);

+	}

+

+	/* Setup the header send pointer and size so we can clock this out */

+

+	ubcsp_config.send_ptr = part1;

+	ubcsp_config.send_size = 4;

+

+	/* Setup the payload send pointer and size */

+

+	ubcsp_config.next_send_ptr = part2;

+	ubcsp_config.next_send_size = len2;

+

+#if UBCSP_CRC

+	/* Initialize the crc as required */

+

+	ubcsp_config.send_crc = -1;

+

+	ubcsp_config.need_send_crc = 1;

+#endif

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_sent_packet                                                       **/

+/**                                                                         **/

+/** Called when we have finished sending a packet                           **/

+/** If this packet was unreliable, then notify caller, and clear the data   **/

+/**                                                                         **/

+/*****************************************************************************/

+

+static uint8 ubcsp_sent_packet (void)

+{

+	if (ubcsp_config.send_packet)

+	{

+		if (!ubcsp_config.send_packet->reliable)

+		{

+			/* We had a packet sent that was unreliable */

+

+			/* Forget about this packet */

+

+			ubcsp_config.send_packet = 0;

+

+			/* Notify caller that they can send another one */

+

+			return UBCSP_PACKET_SENT;

+		}

+	}

+

+	/* We didn't have a packet, or it was reliable

+	   Must wait for ACK before allowing another packet to be sent */

+

+	return 0;

+}

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_poll                                                              **/

+/**                                                                         **/

+/** This is the main function for ubcsp                                     **/

+/** It performs a number of tasks                                           **/

+/**                                                                         **/

+/** 1) Send another octet to the UART - escaping as required                **/

+/** 2) Setup the payload to be sent after the header has been sent          **/

+/** 3) Send the CRC for the packet if required                              **/

+/**                                                                         **/

+/** 4) Calculate the next Link Establishment State                          **/

+/** 5) Send a Link Establishment packet                                     **/

+/** 6) Send a normal packet if available                                    **/

+/** 7) Send an ACK packet if required                                       **/

+/**                                                                         **/

+/** 8) Receive octets from UART and deslip them as required                 **/

+/** 9) Place received octets into receive header or receive payload buffer  **/

+/** 10) Process received packet when SLIP_END is received                   **/

+/**                                                                         **/

+/** 11) Keep track of ability of caller to delay recalling                  **/

+/**                                                                         **/

+/*****************************************************************************/

+

+uint8 ubcsp_poll (uint8 *activity)

+{

+	uint8

+		delay = UBCSP_POLL_TIME_IMMEDIATE;

+

+	uint8

+		value;

+

+	/* Assume no activity to start with */

+

+	*activity = 0;

+

+	/* If we don't have to delay, then send something if we can */

+

+	if (!ubcsp_config.delay)

+	{

+		/* Do we have something we are sending to send */

+

+		if (ubcsp_config.send_size)

+		{

+			/* We have something to send so send it */

+

+			if (ubcsp_config.send_slip_escape)

+			{

+				/* Last time we send a SLIP_ESCAPE octet

+				   this time send the second escape code */

+

+				put_uart (ubcsp_config.send_slip_escape);

+

+				ubcsp_config.send_slip_escape = 0;

+			}

+			else

+			{

+#if UBCSP_CRC

+				/* get the value to send, and calculate CRC as we go */

+

+				value = *ubcsp_config.send_ptr ++;

+

+				ubcsp_config.send_crc = ubcsp_calc_crc (value, ubcsp_config.send_crc);

+

+				/* Output the octet */

+

+				ubcsp_put_slip_uart (value);

+#else

+				/* Just output the octet*/

+

+				ubcsp_put_slip_uart (*ubcsp_config.send_ptr ++);

+#endif

+			}

+

+			/* If we did output a SLIP_ESCAPE, then don't process the end of a block */

+

+			if ((!ubcsp_config.send_slip_escape) && ((ubcsp_config.send_size = ubcsp_config.send_size - 1) == 0))

+			{

+				/*** We are at the end of a block - either header or payload ***/

+

+				/* setup the next block */

+

+				ubcsp_config.send_ptr = ubcsp_config.next_send_ptr;

+				ubcsp_config.send_size = ubcsp_config.next_send_size;

+				ubcsp_config.next_send_ptr = 0;

+				ubcsp_config.next_send_size = 0;

+

+#if UBCSP_CRC

+				/* If we have no successor block

+				   then we might need to send the CRC */

+

+				if (!ubcsp_config.send_ptr)

+				{

+					if (ubcsp_config.need_send_crc)

+					{

+						/* reverse the CRC from what we computed along the way */

+

+						ubcsp_config.need_send_crc = 0;

+

+						ubcsp_config.send_crc = ubcsp_crc_reverse (ubcsp_config.send_crc);

+

+						/* Save in the send_crc buffer */

+

+						ubcsp_send_crc[0] = (uint8) (ubcsp_config.send_crc >> 8);

+						ubcsp_send_crc[1] = (uint8) ubcsp_config.send_crc;

+

+						/* Setup to send this buffer */

+

+						ubcsp_config.send_ptr = ubcsp_send_crc;

+						ubcsp_config.send_size = 2;

+					}

+					else

+					{

+						/* We don't need to send the crc

+						   either we just have, or this packet doesn't include it */

+

+						/* Output the end of FRAME marker */

+

+						put_uart (SLIP_FRAME);

+

+						/* Check if this is an unreliable packet */

+

+						*activity |= ubcsp_sent_packet ();

+

+						/* We've sent the packet, so don't need to have be called quickly soon */

+

+						delay = UBCSP_POLL_TIME_DELAY;

+					}

+				}

+#else

+				/* If we have no successor block

+				   then we might need to send the CRC */

+

+				if (!ubcsp_config.send_ptr)

+				{

+					/* Output the end of FRAME marker */

+

+					put_uart (SLIP_FRAME);

+

+					/* Check if this is an unreliable packet */

+

+					*activity |= ubcsp_sent_packet ();

+

+					/* We've sent the packet, so don't need to have be called quickly soon */

+

+					delay = UBCSP_POLL_TIME_DELAY;

+				}

+#endif

+			}

+		}

+		else if (ubcsp_config.link_establishment_packet == ubcsp_le_none)

+		{

+			/* We didn't have something to send

+			   AND we have no Link Establishment packet to send */

+

+			if (ubcsp_config.link_establishment_resp & 2)

+			{

+				/* Send the start of FRAME packet */

+

+				put_uart (SLIP_FRAME);

+

+				/* We did require a RESP packet - so setup the send */

+

+				ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_le_conf_resp], 4);

+

+				/* We have now "sent" this packet */

+

+				ubcsp_config.link_establishment_resp = 0;

+			}

+			else if (ubcsp_config.send_packet)

+			{

+				/* There is a packet ready to be sent */

+

+				/* Send the start of FRAME packet */

+

+				put_uart (SLIP_FRAME);

+

+				/* Encode up the packet header using ACK and SEQ numbers */

+

+				ubcsp_send_header[0] =

+					(ubcsp_config.send_packet->reliable << 7) |

+#if UBCSP_CRC

+					0x40 |	/* Always use CRC's */

+#endif

+					(ubcsp_config.ack_number << 3) | 

+					(ubcsp_config.sequence_number);

+

+				/* Encode up the packet header's channel and length */

+				ubcsp_send_header[1] =

+					(ubcsp_config.send_packet->channel & 0x0f) |

+					((ubcsp_config.send_packet->length << 4) & 0xf0);

+

+				ubcsp_send_header[2] =

+					(ubcsp_config.send_packet->length >> 4) & 0xff;

+

+				/* Let the ubcsp_setup_packet function calculate the header checksum */

+

+				ubcsp_setup_packet ((uint8*) ubcsp_send_header, 1, ubcsp_config.send_packet->payload, ubcsp_config.send_packet->length);

+

+				/* Don't need to send an ACK - we just place on in this packet */

+

+				ubcsp_config.send_ack = 0;

+				

+#if SHOW_PACKET_ERRORS

+				printf (" : %10d Send %d Ack %d\n",

+					GetTickCount () % 100000,

+					ubcsp_config.sequence_number,

+					ubcsp_config.ack_number);

+#endif

+			}

+			else if (ubcsp_config.send_ack)

+			{

+				/* Send the start of FRAME packet */

+

+				put_uart (SLIP_FRAME);

+

+#if SHOW_PACKET_ERRORS

+				printf (" : %10d Send ACK %d\n",

+					GetTickCount () % 100000,

+					ubcsp_config.ack_number);

+#endif

+

+				/* The ack packet is already computed apart from the first octet */

+

+				ubcsp_send_ack_header[0] =

+#if UBCSP_CRC

+					0x40 | 

+#endif

+					(ubcsp_config.ack_number << 3);

+

+				/* Let the ubcsp_setup_packet function calculate the header checksum */

+

+				ubcsp_setup_packet (ubcsp_send_ack_header, 1, 0, 0);

+

+				/* We've now sent the ack */

+

+				ubcsp_config.send_ack = 0;

+			}

+			else

+			{

+				/* We didn't have a Link Establishment response packet,

+				   a normal packet or an ACK packet to send */

+

+				delay = UBCSP_POLL_TIME_DELAY;

+			}

+		}

+		else

+		{

+#if SHOW_PACKET_ERRORS

+//			printf (" : %10d Send LE %d\n",

+//				GetTickCount () % 100000,

+//				ubcsp_config.link_establishment_packet);

+#endif

+

+			/* Send A Link Establishment Message */

+

+			put_uart (SLIP_FRAME);

+

+			/* Send the Link Establishment header followed by the 

+			   Link Establishment packet */

+

+			ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_config.link_establishment_packet], 4);

+

+			/* start sending immediately */

+

+			ubcsp_config.delay = 0;

+

+			/* workout what the next link establishment packet should be */

+

+			ubcsp_config.link_establishment_packet = next_le_packet[ubcsp_config.link_establishment_state + ubcsp_config.link_establishment_resp * 4];

+

+			/* We have now delt with any response packet that we needed */

+

+			ubcsp_config.link_establishment_resp = 0;

+

+			return 0;

+		}

+	}

+

+	/* We now need to receive any octets from the UART */

+

+	while ((ubcsp_config.receive_packet) && (get_uart (&value)))

+	{

+		/* If the last octet was SLIP_ESCAPE, then special processing is required */

+

+		if (ubcsp_config.receive_slip_escape)

+		{

+			/* WARNING - out of range values are not detected !!!

+			   This will probably be caught with the checksum or CRC check */

+

+			value = ubcsp_deslip[value - SLIP_ESCAPE_FRAME];

+

+			ubcsp_config.receive_slip_escape = 0;

+		}

+		else

+		{

+			/* Check for the SLIP_FRAME octet - must be start or end of packet */

+			if (value == SLIP_FRAME)

+			{

+				/* If we had a full header then we have a packet */

+

+				if (ubcsp_config.receive_index >= 0)

+				{

+					/* process the received packet */

+

+					*activity |= ubcsp_recevied_packet ();

+

+					if (*activity & UBCSP_PACKET_ACK)

+					{

+						/* We need to ACK this packet, then don't delay its sending */

+						ubcsp_config.delay = 0;

+					}

+				}

+

+				/* Setup to receive the next packet */

+

+				ubcsp_config.receive_index = -4;

+

+				/* Ok, next octet */

+

+				goto finished_receive;

+			}

+			else if (value == SLIP_ESCAPE)

+			{

+				/* If we receive a SLIP_ESCAPE,

+				   then remember to process the next special octet */

+

+				ubcsp_config.receive_slip_escape = 1;

+

+				goto finished_receive;

+			}

+		}

+

+		if (ubcsp_config.receive_index < 0)

+		{

+			/* We are still receiving the header */

+

+			ubcsp_receive_header[ubcsp_config.receive_index + 4] = value;

+

+			ubcsp_config.receive_index ++;

+		}

+		else if (ubcsp_config.receive_index < ubcsp_config.receive_packet->length)
+		{
+			/* We are receiving the payload */
+			/* We might stop coming here if we are receiving a
+			   packet which is longer than the receive_packet->length
+			   given by the host */
+
+			ubcsp_config.receive_packet->payload[ubcsp_config.receive_index] = value;

+

+			ubcsp_config.receive_index ++;

+		}

+

+finished_receive:

+		{

+		}

+	}

+

+	if (ubcsp_config.delay > 0)

+	{

+		/* We were delayed so delay some more

+		   this could be cancelled if we received something */

+

+		ubcsp_config.delay --;

+	}

+	else

+	{

+		/* We had no delay, so use the delay we just decided to us */

+

+		ubcsp_config.delay = delay;

+	}

+

+	/* Report the current delay to the user */

+

+	return ubcsp_config.delay;

+}

diff --git a/bluez/repo/tools/ubcsp.h b/bluez/repo/tools/ubcsp.h
new file mode 100644
index 0000000..6a74e9a
--- /dev/null
+++ b/bluez/repo/tools/ubcsp.h
@@ -0,0 +1,208 @@
+/*

+ *

+ *  BlueZ - Bluetooth protocol stack for Linux

+ *

+ *  Copyright (C) 2000-2005  CSR Ltd.

+ *

+ *

+ *  Permission is hereby granted, free of charge, to any person obtaining

+ *  a copy of this software and associated documentation files (the

+ *  "Software"), to deal in the Software without restriction, including

+ *  without limitation the rights to use, copy, modify, merge, publish,

+ *  distribute, sublicense, and/or sell copies of the Software, and to

+ *  permit persons to whom the Software is furnished to do so, subject to

+ *  the following conditions:

+ *

+ *  The above copyright notice and this permission notice shall be included

+ *  in all copies or substantial portions of the Software.

+ *

+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

+ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

+ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

+ *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY

+ *  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,

+ *  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE

+ *  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ *

+ */

+

+#ifndef UBCSP_INCLUDE_H

+#define UBCSP_INCLUDE_H

+

+/*****************************************************************************/

+/*****************************************************************************/

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp.h                                                                 **/

+/**                                                                         **/

+/** MicroBCSP - a very low cost implementation of the BCSP protocol         **/

+/**                                                                         **/

+/*****************************************************************************/

+

+/* If we wish to use CRC's, then change 0 to 1 in the next line */

+#define UBCSP_CRC 1

+

+/* Define some basic types - change these for your architecture */

+typedef unsigned char uint8;

+typedef unsigned short uint16;

+typedef unsigned int uint32;

+typedef signed char int8;

+typedef signed short int16;

+typedef signed int int32;

+

+/* The defines below require a printf function to be available */

+

+/* Do we want to show packet errors in debug output */

+#define SHOW_PACKET_ERRORS	0

+

+/* Do we want to show Link Establishment State transitions in debug output */

+#define SHOW_LE_STATES		0

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_packet                                                            **/

+/**                                                                         **/

+/** This is description of a bcsp packet for the upper layer                **/

+/**                                                                         **/

+/*****************************************************************************/

+

+struct ubcsp_packet

+{

+	uint8 channel;		/* Which Channel this packet is to/from */

+	uint8 reliable;		/* Is this packet reliable */

+	uint8 use_crc;		/* Does this packet use CRC data protection */

+	uint16 length;		/* What is the length of the payload data */

+	uint8 *payload;		/* The payload data itself - size of length */

+};

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_configuration                                                     **/

+/**                                                                         **/

+/** This is the main configuration of the ubcsp engine                      **/

+/** All state variables are stored in this structure                        **/

+/**                                                                         **/

+/*****************************************************************************/

+

+enum ubcsp_link_establishment_state

+{

+	ubcsp_le_uninitialized,

+	ubcsp_le_initialized,

+	ubcsp_le_active

+};

+

+enum ubcsp_link_establishment_packet

+{

+	ubcsp_le_sync,

+	ubcsp_le_sync_resp,

+	ubcsp_le_conf,

+	ubcsp_le_conf_resp,

+	ubcsp_le_none

+};

+

+struct ubcsp_configuration

+{

+	uint8 link_establishment_state;

+	uint8 link_establishment_resp;

+	uint8 link_establishment_packet;

+

+	uint8 sequence_number:3;

+	uint8 ack_number:3;

+	uint8 send_ack;

+	struct ubcsp_packet *send_packet;

+	struct ubcsp_packet *receive_packet;

+

+	uint8 receive_header_checksum;

+	uint8 receive_slip_escape;

+	int32 receive_index;

+

+	uint8 send_header_checksum;

+#ifdef UBCSP_CRC

+	uint8 need_send_crc;

+	uint16 send_crc;

+#endif

+	uint8 send_slip_escape;

+

+	uint8 *send_ptr;

+	int32 send_size;

+	uint8 *next_send_ptr;

+	int32 next_send_size;

+

+	int8 delay;

+};

+

+/*****************************************************************************/

+/**                                                                         **/

+/** ubcsp_poll sets activity from an OR of these flags                      **/

+/**                                                                         **/

+/*****************************************************************************/

+

+#define UBCSP_PACKET_SENT 0x01

+#define UBCSP_PACKET_RECEIVED 0x02

+#define UBCSP_PEER_RESET 0x04

+#define UBCSP_PACKET_ACK 0x08

+

+/*****************************************************************************/

+/**                                                                         **/

+/** This is the functional interface for ucbsp                              **/

+/**                                                                         **/

+/*****************************************************************************/

+

+void ubcsp_initialize (void);

+void ubcsp_send_packet (struct ubcsp_packet *send_packet);

+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet);

+uint8 ubcsp_poll (uint8 *activity);

+

+/*****************************************************************************/

+/**                                                                         **/

+/** Slip Escape Values                                                      **/

+/**                                                                         **/

+/*****************************************************************************/

+

+#define SLIP_FRAME 0xC0

+#define SLIP_ESCAPE 0xDB

+#define SLIP_ESCAPE_FRAME 0xDC

+#define SLIP_ESCAPE_ESCAPE 0xDD

+

+/*****************************************************************************/

+/*****************************************************************************/

+/*****************************************************************************/

+

+/*****************************************************************************/

+/**                                                                         **/

+/** These functions need to be linked into your system                      **/

+/**                                                                         **/

+/*****************************************************************************/

+

+/*****************************************************************************/

+/**                                                                         **/

+/** put_uart outputs a single octet over the UART Tx line                   **/

+/**                                                                         **/

+/*****************************************************************************/

+

+extern void put_uart (uint8);

+

+/*****************************************************************************/

+/**                                                                         **/

+/** get_uart receives a single octet over the UART Rx line                  **/

+/** if no octet is available, then this returns 0                           **/

+/** if an octet was read, then this is returned in the argument and         **/

+/**   the function returns 1                                                **/

+/**                                                                         **/

+/*****************************************************************************/

+

+extern uint8 get_uart (uint8 *);

+

+/*****************************************************************************/

+/**                                                                         **/

+/** These defines should be changed to your systems concept of 100ms        **/

+/**                                                                         **/

+/*****************************************************************************/

+

+#define UBCSP_POLL_TIME_IMMEDIATE   0

+#define UBCSP_POLL_TIME_DELAY       25

+

+/*****************************************************************************/

+/*****************************************************************************/

+/*****************************************************************************/

+#endif
diff --git a/bluez/repo/tools/update_compids.sh b/bluez/repo/tools/update_compids.sh
new file mode 100755
index 0000000..7c4cc12
--- /dev/null
+++ b/bluez/repo/tools/update_compids.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# Download the list of company IDs from bluetooth.org and generate a diff which
+# can be applied to source tree to update bt_compidtostr(). Usage:
+#
+# 1) ./tools/update_compids.sh | git apply -p0
+# 2) Inspect changes to make sure they are sane
+# 3) git commit -m "lib: Update list of company identifiers" lib/bluetooth.c
+#
+# Requires html2text: http://www.mbayer.de/html2text/
+#
+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
+
+echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
+
+path=specifications/assigned-numbers/company-identifiers
+# Use "iconv -c" to strip unwanted unicode characters
+curl https://www.bluetooth.com/$path | \
+    $scriptdir/tools/parse_companies.pl >> new.c
+
+if ! grep -q "return \"" new.c; then
+    echo "ERROR: could not parse company IDs from bluetooth.org" >&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
diff --git a/bluez/repo/tools/userchan-tester.c b/bluez/repo/tools/userchan-tester.c
new file mode 100644
index 0000000..8fb0888
--- /dev/null
+++ b/bluez/repo/tools/userchan-tester.c
@@ -0,0 +1,334 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014-2015  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+#include "emulator/hciemu.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hci.h"
+#include "src/shared/util.h"
+
+struct test_data {
+	struct mgmt *mgmt;
+	uint16_t mgmt_index;
+	struct hciemu *hciemu;
+	enum hciemu_type hciemu_type;
+	const void *test_data;
+	unsigned int remove_id;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	const struct mgmt_rp_read_info *rp = param;
+	char addr[18];
+	uint16_t manufacturer;
+	uint32_t supported_settings, current_settings;
+
+	tester_print("Read Info callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	ba2str(&rp->bdaddr, addr);
+	manufacturer = btohs(rp->manufacturer);
+	supported_settings = btohl(rp->supported_settings);
+	current_settings = btohl(rp->current_settings);
+
+	tester_print("  Address: %s", addr);
+	tester_print("  Version: 0x%02x", rp->version);
+	tester_print("  Manufacturer: 0x%04x", manufacturer);
+	tester_print("  Supported settings: 0x%08x", supported_settings);
+	tester_print("  Current settings: 0x%08x", current_settings);
+	tester_print("  Class: 0x%02x%02x%02x",
+			rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+	tester_print("  Name: %s", rp->name);
+	tester_print("  Short name: %s", rp->short_name);
+
+	if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Added callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (data->mgmt_index != MGMT_INDEX_NONE)
+		return;
+
+	data->mgmt_index = index;
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+					read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Index Removed callback");
+	tester_print("  Index: 0x%04x", index);
+
+	if (index != data->mgmt_index)
+		return;
+
+	if (data->remove_id) {
+		mgmt_unregister(data->mgmt, data->remove_id);
+		data->remove_id = 0;
+		tester_test_passed();
+		return;
+	}
+
+	mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+	mgmt_unref(data->mgmt);
+	data->mgmt = NULL;
+
+	tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+
+	tester_print("Read Index List callback");
+	tester_print("  Status: 0x%02x", status);
+
+	if (status || !param) {
+		tester_pre_setup_failed();
+		return;
+	}
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+					index_added_callback, NULL, NULL);
+
+	data->hciemu = hciemu_new(data->hciemu_type);
+	if (!data->hciemu) {
+		tester_warn("Failed to setup HCI emulation");
+		tester_pre_setup_failed();
+	}
+
+	tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	data->mgmt = mgmt_new_default();
+	if (!data->mgmt) {
+		tester_warn("Failed to setup management interface");
+		tester_pre_setup_failed();
+		return;
+	}
+
+	if (tester_use_debug())
+		mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+					read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+
+	mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, data->mgmt_index,
+					index_removed_callback,
+					NULL, NULL);
+
+	hciemu_unref(data->hciemu);
+	data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+	struct test_data *data = test_data;
+
+	free(data);
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered on");
+
+	tester_setup_complete();
+}
+
+static void setup_powered(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	unsigned char param[] = { 0x01 };
+
+	tester_print("Powering on controller");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, setup_powered_client_callback,
+			NULL, NULL);
+}
+
+static void toggle_powered(const void *test_data);
+
+static void toggle_powered_client_callback(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	bool power = PTR_TO_INT(user_data);
+
+	if (status != MGMT_STATUS_SUCCESS) {
+		tester_setup_failed();
+		return;
+	}
+
+	tester_print("Controller powered %s", power ? "on" : "off");
+
+	if (power)
+		toggle_powered(false);
+	else
+		tester_setup_complete();
+}
+
+static void toggle_powered(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	bool power = PTR_TO_INT(test_data);
+	unsigned char param[1];
+
+	param[0] = power ? 0x01 : 0x00;
+
+	tester_print("Powering %s controller", power ? "on" : "off");
+
+	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+			sizeof(param), param, toggle_powered_client_callback,
+			INT_TO_PTR(power), NULL);
+}
+
+static void test_open_success(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bt_hci *hci;
+
+	data->remove_id = mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED,
+					data->mgmt_index,
+					index_removed_callback,
+					NULL, NULL);
+
+	hci = bt_hci_new_user_channel(data->mgmt_index);
+	if (hci) {
+		bt_hci_unref(hci);
+		return;
+	}
+
+	mgmt_unregister(data->mgmt, data->remove_id);
+	data->remove_id = 0;
+
+	tester_test_failed();
+}
+
+static void test_open_failed(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bt_hci *hci;
+
+	hci = bt_hci_new_user_channel(data->mgmt_index);
+	if (!hci) {
+		tester_test_passed();
+		return;
+	}
+
+	bt_hci_unref(hci);
+	tester_test_failed();
+}
+
+#define test_user(name, data, setup, func) \
+	do { \
+		struct test_data *user; \
+		user = malloc(sizeof(struct test_data)); \
+		if (!user) \
+			break; \
+		user->hciemu_type = HCIEMU_TYPE_BREDR; \
+		user->mgmt_index = MGMT_INDEX_NONE; \
+		user->test_data = data; \
+		user->remove_id = 0; \
+		tester_add_full(name, data, \
+				test_pre_setup, setup, func, NULL, \
+				test_post_teardown, 2, user, test_data_free); \
+	} while (0)
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	test_user("User channel open - Success", NULL,
+					NULL, test_open_success);
+	test_user("User channel open - Failed", NULL,
+					setup_powered, test_open_failed);
+	test_user("User channel open - Power Toggle Success", INT_TO_PTR(true),
+					toggle_powered, test_open_success);
+
+	return tester_run();
+}
diff --git a/bluez/repo/tools/valgrind.supp b/bluez/repo/tools/valgrind.supp
new file mode 100644
index 0000000..9efb6f1
--- /dev/null
+++ b/bluez/repo/tools/valgrind.supp
@@ -0,0 +1,27 @@
+{
+   ecb_bind
+   Memcheck:Param
+   socketcall.bind(my_addr.sa_data)
+   fun:bind
+   fun:ecb_aes_setup
+}
+{
+   cmac_bind
+   Memcheck:Param
+   socketcall.bind(my_addr.sa_data)
+   fun:bind
+   fun:cmac_aes_setup
+}
+{
+   logging_open
+   Memcheck:Param
+   socketcall.bind(my_addr.rc_bdaddr)
+   fun:bind
+   fun:logging_open
+}
+{
+   bind
+   Memcheck:Param
+   socketcall.bind(my_addr.rc_channel)
+   fun:bind
+}
diff --git a/bluez/repo/unit/._test-avctp.c b/bluez/repo/unit/._test-avctp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-avctp.c
Binary files differ
diff --git a/bluez/repo/unit/._test-avdtp.c b/bluez/repo/unit/._test-avdtp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-avdtp.c
Binary files differ
diff --git a/bluez/repo/unit/._test-avrcp.c b/bluez/repo/unit/._test-avrcp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-avrcp.c
Binary files differ
diff --git a/bluez/repo/unit/._test-crc.c b/bluez/repo/unit/._test-crc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-crc.c
Binary files differ
diff --git a/bluez/repo/unit/._test-crypto.c b/bluez/repo/unit/._test-crypto.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-crypto.c
Binary files differ
diff --git a/bluez/repo/unit/._test-ecc.c b/bluez/repo/unit/._test-ecc.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-ecc.c
Binary files differ
diff --git a/bluez/repo/unit/._test-eir.c b/bluez/repo/unit/._test-eir.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-eir.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gatt.c b/bluez/repo/unit/._test-gatt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gatt.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gattrib.c b/bluez/repo/unit/._test-gattrib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gattrib.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gdbus-client.c b/bluez/repo/unit/._test-gdbus-client.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gdbus-client.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gobex-apparam.c b/bluez/repo/unit/._test-gobex-apparam.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gobex-apparam.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gobex-header.c b/bluez/repo/unit/._test-gobex-header.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gobex-header.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gobex-packet.c b/bluez/repo/unit/._test-gobex-packet.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gobex-packet.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gobex-transfer.c b/bluez/repo/unit/._test-gobex-transfer.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gobex-transfer.c
Binary files differ
diff --git a/bluez/repo/unit/._test-gobex.c b/bluez/repo/unit/._test-gobex.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-gobex.c
Binary files differ
diff --git a/bluez/repo/unit/._test-hfp.c b/bluez/repo/unit/._test-hfp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-hfp.c
Binary files differ
diff --git a/bluez/repo/unit/._test-hog.c b/bluez/repo/unit/._test-hog.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-hog.c
Binary files differ
diff --git a/bluez/repo/unit/._test-lib.c b/bluez/repo/unit/._test-lib.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-lib.c
Binary files differ
diff --git a/bluez/repo/unit/._test-mgmt.c b/bluez/repo/unit/._test-mgmt.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-mgmt.c
Binary files differ
diff --git a/bluez/repo/unit/._test-queue.c b/bluez/repo/unit/._test-queue.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-queue.c
Binary files differ
diff --git a/bluez/repo/unit/._test-ringbuf.c b/bluez/repo/unit/._test-ringbuf.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-ringbuf.c
Binary files differ
diff --git a/bluez/repo/unit/._test-sdp.c b/bluez/repo/unit/._test-sdp.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-sdp.c
Binary files differ
diff --git a/bluez/repo/unit/._test-textfile.c b/bluez/repo/unit/._test-textfile.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-textfile.c
Binary files differ
diff --git a/bluez/repo/unit/._test-uhid.c b/bluez/repo/unit/._test-uhid.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-uhid.c
Binary files differ
diff --git a/bluez/repo/unit/._test-uuid.c b/bluez/repo/unit/._test-uuid.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._test-uuid.c
Binary files differ
diff --git a/bluez/repo/unit/._util.c b/bluez/repo/unit/._util.c
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._util.c
Binary files differ
diff --git a/bluez/repo/unit/._util.h b/bluez/repo/unit/._util.h
new file mode 100644
index 0000000..3d90fc8
--- /dev/null
+++ b/bluez/repo/unit/._util.h
Binary files differ
diff --git a/bluez/repo/unit/test-avctp.c b/bluez/repo/unit/test-avctp.c
new file mode 100644
index 0000000..3bc3569
--- /dev/null
+++ b/bluez/repo/unit/test-avctp.c
@@ -0,0 +1,314 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/log.h"
+
+#include "android/avctp.h"
+
+struct test_pdu {
+	bool valid;
+	const uint8_t *data;
+	size_t size;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+};
+
+struct context {
+	struct avctp *session;
+	guint source;
+	guint process;
+	int fd;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define define_test(name, function, args...)				\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, function, NULL);		\
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	avctp_shutdown(context->session);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	destroy_context(context);
+
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+	util_hexdump('<', pdu->data, len, test_debug, "AVCTP: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context->process = 0;
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		g_print("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	util_hexdump('>', buf, len, test_debug, "AVCTP: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	context_process(context);
+
+	return TRUE;
+}
+
+static struct context *create_context(uint16_t version, gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->session = avctp_new(sv[0], 672, 672, version);
+	g_assert(context->session != NULL);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	return context;
+}
+
+static ssize_t handler(struct avctp *session,
+					uint8_t transaction, uint8_t *code,
+					uint8_t *subunit, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	g_assert_cmpint(transaction, ==, 0);
+	g_assert_cmpint(*code, ==, 0);
+	g_assert_cmpint(*subunit, ==, 0);
+	g_assert_cmpint(operand_count, ==, 0);
+
+	return operand_count;
+}
+
+static gboolean handler_response(struct avctp *session,
+					uint8_t code, uint8_t subunit,
+					uint8_t *operands, size_t operand_count,
+					void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert_cmpint(code, ==, 0x0a);
+	g_assert_cmpint(subunit, ==, 0);
+	g_assert_cmpint(operand_count, ==, 0);
+
+	return context_quit(context);
+}
+
+static void test_client(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	avctp_send_vendor_req(context->session, AVC_CTYPE_CONTROL, 0, NULL,
+						0, handler_response, context);
+}
+
+static void test_server(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	if (g_str_equal(context->data->test_name, "/TP/NFR/BV-03-C")) {
+		int ret;
+
+		ret = avctp_register_pdu_handler(context->session,
+					AVC_OP_VENDORDEP, handler, NULL);
+		g_assert_cmpint(ret, !=, 0);
+	}
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_dummy(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	context_quit(context);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	__btd_log_init("*", 0);
+
+	/* Connection Channel Management tests */
+
+	/*
+	 * Tests are checking that IUT is able to request establishing
+	 * channels, since we already have connection through socketpair
+	 * the tests are dummy.
+	 */
+	define_test("/TP/CCM/BV-01-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CCM/BV-02-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CCM/BV-03-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CCM/BV-04-C", test_dummy, raw_pdu(0x00));
+
+	/* Non-Fragmented Messages tests */
+
+	define_test("/TP/NFR/BV-01-C", test_client,
+				raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00));
+
+	define_test("/TP/NFR/BV-02-C", test_server,
+				raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00),
+				raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x00, 0x00));
+
+	define_test("/TP/NFR/BV-03-C", test_server,
+				raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00),
+				raw_pdu(0x02, 0x11, 0x0e, 0x00, 0x00, 0x00));
+
+	define_test("/TP/NFR/BV-04-C", test_client,
+				raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00),
+				raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x00, 0x00));
+
+	define_test("/TP/NFR/BI-01-C", test_server,
+				raw_pdu(0x00, 0xff, 0xff, 0x00, 0x00, 0x00),
+				raw_pdu(0x03, 0xff, 0xff));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-avdtp.c b/bluez/repo/unit/test-avdtp.c
new file mode 100644
index 0000000..dd8aed7
--- /dev/null
+++ b/bluez/repo/unit/test-avdtp.c
@@ -0,0 +1,1392 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/tester.h"
+#include "src/log.h"
+
+#include "android/avdtp.h"
+
+#define MAX_SEID 0x3E
+
+struct test_pdu {
+	bool valid;
+	bool fragmented;
+	const uint8_t *data;
+	size_t size;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...) \
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define frg_pdu(args...) \
+	{							\
+		.valid = true,					\
+		.fragmented = true,				\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define define_test(name, function, args...) \
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, function, NULL);		\
+	} while (0)
+
+struct context {
+	struct avdtp *session;
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct queue *lseps;
+	guint source;
+	guint process;
+	int fd;
+	int mtu;
+	gboolean pending_open;
+	gboolean pending_suspend;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+static void unregister_sep(void *data)
+{
+	struct avdtp_local_sep *sep = data;
+
+	/* Removed from the queue by caller */
+	avdtp_unregister_sep(NULL, sep);
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+	avdtp_unref(context->session);
+
+	test_free(context->data);
+	queue_destroy(context->lseps, unregister_sep);
+
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	destroy_context(context);
+
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+	util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-02-C"))
+		g_timeout_add_seconds(1, context_quit, context);
+
+	if (pdu->fragmented)
+		return send_pdu(user_data);
+
+	context->process = 0;
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean transport_open(struct avdtp_stream *stream)
+{
+	int fd;
+
+	fd = open("/dev/null", O_RDWR, 0);
+	if (fd < 0)
+		g_assert_not_reached();
+
+	return avdtp_stream_set_transport(stream, fd, 672, 672);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	util_hexdump('>', buf, len, test_debug, "AVDTP: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	if (context->pending_open) {
+		context->pending_open = FALSE;
+		g_assert(transport_open(context->stream));
+	}
+
+	if (context->pending_suspend) {
+		int ret;
+
+		context->pending_suspend = FALSE;
+		ret = avdtp_suspend(context->session, context->stream);
+		g_assert_cmpint(ret, ==, 0);
+	}
+
+	if (!pdu->fragmented)
+		context_process(context);
+
+	return TRUE;
+}
+
+static struct context *context_new(uint16_t version, uint16_t imtu,
+					uint16_t omtu, gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->lseps = queue_new();
+	g_assert(context->lseps);
+
+	context->session = avdtp_new(sv[0], imtu, omtu, version,
+								context->lseps);
+	g_assert(context->session != NULL);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	return context;
+}
+
+static struct context *create_context(uint16_t version, gconstpointer data)
+{
+	return context_new(version, 672, 672, data);
+}
+
+static gboolean sep_getcap_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data)
+{
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *codec_caps;
+	uint8_t cap[4] = { 0xff, 0xff, 2, 64 };
+
+	*caps = NULL;
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
+	codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec_caps->media_codec_type = 0x00;
+	memcpy(codec_caps->data, cap, sizeof(cap));
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+					sizeof(*codec_caps) + sizeof(cap));
+
+	*caps = g_slist_append(*caps, media_codec);
+	g_free(codec_caps);
+
+	return TRUE;
+}
+
+static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-18-C")) {
+		*err = 0xc0;
+		return FALSE;
+	}
+
+	context->pending_open = TRUE;
+	context->stream = stream;
+
+	return TRUE;
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						GSList *caps,
+						avdtp_set_configuration_cb cb,
+						void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-09-C"))
+		return FALSE;
+
+	cb(session, stream, NULL);
+
+	return TRUE;
+}
+
+static gboolean sep_start_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-21-C")) {
+		*err = 0xc0;
+		return FALSE;
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-25-C"))
+		context->pending_suspend = TRUE;
+
+	return TRUE;
+}
+
+static gboolean sep_close_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-24-C")) {
+		*err = 0xc0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean sep_suspend_ind(struct avdtp *session,
+						struct avdtp_local_sep *sep,
+						struct avdtp_stream *stream,
+						uint8_t *err,
+						void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-27-C")) {
+		*err = 0xc0;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+	.get_capability		= sep_getcap_ind,
+	.set_configuration	= sep_setconf_ind,
+	.open			= sep_open_ind,
+	.close			= sep_close_ind,
+	.start			= sep_start_ind,
+	.suspend		= sep_suspend_ind,
+};
+
+static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	struct context *context = user_data;
+	int ret;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-09-C")) {
+		context_quit(context);
+		return;
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-07-C")) {
+		g_assert(err != NULL);
+		g_assert_cmpint(avdtp_error_error_code(err), ==, 0x13);
+		context_quit(context);
+		return;
+	}
+
+	g_assert(err == NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-11-C") ||
+		g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-10-C"))
+		ret = avdtp_get_configuration(session, stream);
+	else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-23-C"))
+		ret = avdtp_abort(session, stream);
+	else
+		ret = avdtp_open(session, stream);
+
+	g_assert_cmpint(ret, ==, 0);
+}
+
+static void sep_getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-10-C")) {
+		g_assert(err != NULL);
+		g_assert_cmpint(avdtp_error_error_code(err), ==, 0x12);
+	} else
+		g_assert(err == NULL);
+
+	context_quit(context);
+}
+
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	int ret;
+
+	g_assert(err == NULL);
+
+	g_assert(transport_open(stream));
+
+	ret = avdtp_start(session, stream);
+	g_assert_cmpint(ret, ==, 0);
+}
+
+static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct context *context = user_data;
+	int ret;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-19-C") ||
+		g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-22-C")) {
+		g_assert(err != NULL);
+		g_assert_cmpint(avdtp_error_error_code(err), ==, 0x31);
+		context_quit(context);
+		return;
+	}
+
+	g_assert(err == NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-19-C"))
+		ret = avdtp_close(session, stream, FALSE);
+	else
+		ret = avdtp_suspend(session, stream);
+
+	g_assert_cmpint(ret, ==, 0);
+}
+
+static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-25-C")) {
+		g_assert(err != NULL);
+		g_assert_cmpint(avdtp_error_error_code(err), ==, 0x31);
+		context_quit(context);
+	}
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+	.set_configuration	= sep_setconf_cfm,
+	.get_configuration	= sep_getconf_cfm,
+	.open			= sep_open_cfm,
+	.start			= sep_start_cfm,
+	.suspend		= sep_suspend_cfm,
+};
+
+static void test_server(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SOURCE,
+					AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, &sep_ind, &sep_cfm,
+					context);
+	g_assert(sep);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_server_1_3(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SOURCE,
+					AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, NULL, context);
+	g_assert(sep);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_server_1_3_sink(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+					AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, NULL, context);
+	g_assert(sep);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_server_0_sep(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_server_seid(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+	unsigned int i;
+
+	for (i = 0; i < sizeof(int) * 8; i++) {
+		sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						0x00, TRUE, &sep_ind, NULL,
+						context);
+		g_assert(sep);
+	}
+
+	/* Now add (MAX_SEID + 1) SEP -> it shall fail */
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						0x00, TRUE, &sep_ind, NULL,
+						context);
+	g_assert(!sep);
+
+	context_quit(context);
+}
+
+static void test_server_seid_duplicate(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						0x00, TRUE, &sep_ind, NULL,
+						context);
+		g_assert(sep);
+	}
+
+	/* Remove 1st element */
+	sep = queue_peek_head(context->lseps);
+	g_assert(sep);
+
+	avdtp_unregister_sep(context->lseps, sep);
+
+	/* Now register new element */
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						0x00, TRUE, &sep_ind, NULL,
+						context);
+	g_assert(sep);
+
+	/* Check SEID ids with DISCOVER */
+
+	g_idle_add(send_pdu, context);
+}
+
+static gboolean sep_getcap_ind_frg(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					GSList **caps, uint8_t *err,
+					void *user_data)
+{
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_service_capability *content_protection;
+	struct avdtp_media_codec_capability *codec_caps;
+	uint8_t cap[4] = { 0xff, 0xff, 2, 64 };
+	uint8_t frg_cap[96] = {};
+
+	*caps = NULL;
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
+	codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec_caps->media_codec_type = 0x00;
+	memcpy(codec_caps->data, cap, sizeof(cap));
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+					sizeof(*codec_caps) + sizeof(cap));
+
+	*caps = g_slist_append(*caps, media_codec);
+	g_free(codec_caps);
+
+	content_protection = avdtp_service_cap_new(AVDTP_CONTENT_PROTECTION,
+						frg_cap, sizeof(frg_cap));
+	*caps = g_slist_append(*caps, content_protection);
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind_frg = {
+	.get_capability		= sep_getcap_ind_frg,
+};
+
+static void test_server_frg(gconstpointer data)
+{
+	struct context *context = context_new(0x0100, 48, 48, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SOURCE,
+						AVDTP_MEDIA_TYPE_AUDIO,
+						0x00, TRUE, &sep_ind_frg,
+						NULL, context);
+	g_assert(sep);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+	struct context *context = user_data;
+	struct avdtp_stream *stream;
+	struct avdtp_remote_sep *rsep;
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *cap;
+	GSList *caps;
+	uint8_t data[4] = { 0x21, 0x02, 2, 32 };
+	int ret;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-05-C") ||
+		g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-07-C") ||
+		g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-25-C"))
+		return;
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-01-C")) {
+		g_assert(err != NULL);
+		g_assert_cmpint(avdtp_error_error_code(err), ==, 0x01);
+		context_quit(context);
+		return;
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-04-C") ||
+		g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-32-C")) {
+		g_assert(err != NULL);
+		g_assert_cmpint(avdtp_error_error_code(err), ==, 0x11);
+		context_quit(context);
+		return;
+	}
+
+	g_assert(err == NULL);
+	g_assert_cmpint(g_slist_length(seps), !=, 0);
+
+	if (g_str_equal(context->data->test_name, "/TP/SIG/FRA/BV-02-C")) {
+		g_assert(err == NULL);
+		context_quit(context);
+		return;
+	}
+
+	rsep = avdtp_find_remote_sep(session, context->sep);
+	g_assert(rsep != NULL);
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	caps = g_slist_append(NULL, media_transport);
+
+	cap = g_malloc0(sizeof(*cap) + sizeof(data));
+	cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	cap->media_codec_type = 0x00;
+	memcpy(cap->data, data, sizeof(data));
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+						sizeof(*cap) + sizeof(data));
+
+	caps = g_slist_append(caps, media_codec);
+	g_free(cap);
+
+	ret = avdtp_set_configuration(session, rsep, context->sep, caps,
+								&stream);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_slist_free_full(caps, g_free);
+}
+
+static void test_client(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+					AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, NULL, &sep_cfm, context);
+
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+}
+
+static void test_client_1_3(gconstpointer data)
+{
+	struct context *context = create_context(0x0103, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+					AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, NULL, &sep_cfm, context);
+
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+}
+
+static void test_client_frg(gconstpointer data)
+{
+	struct context *context = context_new(0x0100, 48, 48, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+					AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, NULL, &sep_cfm, context);
+
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	__btd_log_init("*", 0);
+
+	/*
+	 * Stream Management Service
+	 *
+	 * To verify that the following procedures are implemented according to
+	 * their specification in AVDTP.
+	 */
+	define_test("/TP/SIG/SMG/BV-06-C-SEID-1", test_server_seid,
+			raw_pdu(0x00));
+	define_test("/TP/SIG/SMG/BV-06-C-SEID-2", test_server_seid_duplicate,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x08, 0x08, 0x04, 0x08));
+	define_test("/TP/SIG/SMG/BV-05-C", test_client,
+			raw_pdu(0x00, 0x01));
+	define_test("/TP/SIG/SMG/BV-06-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00));
+	define_test("/TP/SIG/SMG/BV-07-C", test_client,
+			raw_pdu(0x10, 0x01),
+			raw_pdu(0x12, 0x01, 0x04, 0x00),
+			raw_pdu(0x20, 0x02, 0x04));
+	define_test("/TP/SIG/SMG/BV-08-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40));
+	define_test("/TP/SIG/SMG/BV-09-C", test_client,
+			raw_pdu(0x30, 0x01),
+			raw_pdu(0x32, 0x01, 0x04, 0x00),
+			raw_pdu(0x40, 0x02, 0x04),
+			raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x52, 0x03));
+	define_test("/TP/SIG/SMG/BV-10-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03));
+	define_test("/TP/SIG/SMG/BV-11-C", test_client,
+			raw_pdu(0x60, 0x01),
+			raw_pdu(0x62, 0x01, 0x04, 0x00),
+			raw_pdu(0x70, 0x02, 0x04),
+			raw_pdu(0x72, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x80, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x82, 0x03),
+			raw_pdu(0x90, 0x04, 0x04));
+	define_test("/TP/SIG/SMG/BV-12-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x04, 0x04),
+			raw_pdu(0x32, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0x21, 0x02, 0x02, 0x20));
+	define_test("/TP/SIG/SMG/BV-15-C", test_client,
+			raw_pdu(0xa0, 0x01),
+			raw_pdu(0xa2, 0x01, 0x04, 0x00),
+			raw_pdu(0xb0, 0x02, 0x04),
+			raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0xc2, 0x03),
+			raw_pdu(0xd0, 0x06, 0x04));
+	define_test("/TP/SIG/SMG/BV-16-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06));
+	define_test("/TP/SIG/SMG/BV-17-C", test_client,
+			raw_pdu(0xe0, 0x01),
+			raw_pdu(0xe2, 0x01, 0x04, 0x00),
+			raw_pdu(0xf0, 0x02, 0x04),
+			raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x02, 0x03),
+			raw_pdu(0x10, 0x06, 0x04),
+			raw_pdu(0x12, 0x06),
+			raw_pdu(0x20, 0x07, 0x04));
+	define_test("/TP/SIG/SMG/BV-18-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07));
+	define_test("/TP/SIG/SMG/BV-19-C", test_client,
+			raw_pdu(0x30, 0x01),
+			raw_pdu(0x32, 0x01, 0x04, 0x00),
+			raw_pdu(0x40, 0x02, 0x04),
+			raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x52, 0x03),
+			raw_pdu(0x60, 0x06, 0x04),
+			raw_pdu(0x62, 0x06),
+			raw_pdu(0x70, 0x07, 0x04),
+			raw_pdu(0x72, 0x07),
+			raw_pdu(0x80, 0x08, 0x04));
+	define_test("/TP/SIG/SMG/BV-20-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0x50, 0x08, 0x04),
+			raw_pdu(0x52, 0x08));
+	define_test("/TP/SIG/SMG/BV-21-C", test_client,
+			raw_pdu(0x90, 0x01),
+			raw_pdu(0x92, 0x01, 0x04, 0x00),
+			raw_pdu(0xa0, 0x02, 0x04),
+			raw_pdu(0xa2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0xb0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0xb2, 0x03),
+			raw_pdu(0xc0, 0x06, 0x04),
+			raw_pdu(0xc2, 0x06),
+			raw_pdu(0xd0, 0x07, 0x04),
+			raw_pdu(0xd2, 0x07),
+			raw_pdu(0xe0, 0x09, 0x04));
+	define_test("/TP/SIG/SMG/BV-22-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0x50, 0x09, 0x04),
+			raw_pdu(0x52, 0x09));
+	define_test("/TP/SIG/SMG/BV-23-C", test_client,
+			raw_pdu(0xf0, 0x01),
+			raw_pdu(0xf2, 0x01, 0x04, 0x00),
+			raw_pdu(0x00, 0x02, 0x04),
+			raw_pdu(0x02, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x10, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x12, 0x03),
+			raw_pdu(0x20, 0x0a, 0x04));
+	define_test("/TP/SIG/SMG/BV-24-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x0a, 0x04),
+			raw_pdu(0x32, 0x0a));
+	define_test("/TP/SIG/SMG/BV-25-C", test_client_1_3,
+			raw_pdu(0x30, 0x01),
+			raw_pdu(0x32, 0x01, 0x04, 0x00),
+			raw_pdu(0x40, 0x0c, 0x04));
+	define_test("/TP/SIG/SMG/BV-26-C", test_server_1_3,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x0c, 0x04),
+			raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x08, 0x00));
+	define_test("/TP/SIG/SMG/BV-27-C", test_server_1_3,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40));
+	define_test("/TP/SIG/SMG/BV-28-C", test_client_1_3,
+			raw_pdu(0x50, 0x01),
+			raw_pdu(0x52, 0x01, 0x04, 0x00),
+			raw_pdu(0x60, 0x0c, 0x04),
+			raw_pdu(0x62, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x0f, 0x00),
+			raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+	define_test("/TP/SIG/SMG/BV-31-C", test_client_1_3,
+			raw_pdu(0x80, 0x01),
+			raw_pdu(0x82, 0x01, 0x04, 0x00),
+			raw_pdu(0x90, 0x0c, 0x04),
+			raw_pdu(0x92, 0x0c, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+				0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+			raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00));
+	define_test("/TP/SIG/SMG/BI-01-C", test_client,
+			raw_pdu(0xb0, 0x01),
+			raw_pdu(0xb3, 0x01, 0x01));
+	define_test("/TP/SIG/SMG/BI-02-C", test_server,
+			raw_pdu(0x01, 0x01));
+	define_test("/TP/SIG/SMG/BI-03-C", test_server_0_sep,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x03, 0x01, 0x19));
+	define_test("/TP/SIG/SMG/BI-04-C", test_client,
+			raw_pdu(0xc0, 0x01),
+			raw_pdu(0xc2, 0x01, 0x04, 0x00),
+			raw_pdu(0xd0, 0x02, 0x04),
+			raw_pdu(0xd3, 0x02, 0x11));
+	define_test("/TP/SIG/SMG/BI-05-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02),
+			raw_pdu(0x13, 0x02, 0x11));
+	define_test("/TP/SIG/SMG/BI-06-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x00),
+			raw_pdu(0x13, 0x02, 0x12));
+	define_test("/TP/SIG/SMG/BI-07-C", test_client,
+			raw_pdu(0xe0, 0x01),
+			raw_pdu(0xe2, 0x01, 0x04, 0x00),
+			raw_pdu(0xf0, 0x02, 0x04),
+			raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x03, 0x03, 0x00, 0x13));
+	define_test("/TP/SIG/SMG/BI-08-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x33, 0x03, 0x00, 0x13));
+	define_test("/TP/SIG/SMG/BI-09-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x23, 0x03, 0x00, 0x29));
+	define_test("/TP/SIG/SMG/BI-10-C", test_client,
+			raw_pdu(0x10, 0x01),
+			raw_pdu(0x12, 0x01, 0x04, 0x00),
+			raw_pdu(0x20, 0x02, 0x04),
+			raw_pdu(0x22, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x32, 0x03),
+			raw_pdu(0x40, 0x04, 0x04),
+			raw_pdu(0x43, 0x04, 0x12));
+	define_test("/TP/SIG/SMG/BI-11-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x04, 0x00),
+			raw_pdu(0x33, 0x04, 0x12));
+	define_test("/TP/SIG/SMG/BI-17-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x33, 0x06, 0x31));
+	define_test("/TP/SIG/SMG/BI-18-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x33, 0x06, 0xc0));
+	define_test("/TP/SIG/SMG/BI-19-C", test_client,
+			raw_pdu(0x50, 0x01),
+			raw_pdu(0x52, 0x01, 0x04, 0x00),
+			raw_pdu(0x60, 0x02, 0x04),
+			raw_pdu(0x62, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x72, 0x03),
+			raw_pdu(0x80, 0x06, 0x04),
+			raw_pdu(0x82, 0x06),
+			raw_pdu(0x90, 0x07, 0x04),
+			raw_pdu(0x93, 0x07, 0x04, 0x31));
+	define_test("/TP/SIG/SMG/BI-20-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x07, 0x04),
+			raw_pdu(0x33, 0x07, 0x04, 0x31));
+	define_test("/TP/SIG/SMG/BI-21-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x43, 0x07, 0x04, 0xc0));
+	define_test("/TP/SIG/SMG/BI-22-C", test_client,
+			raw_pdu(0xa0, 0x01),
+			raw_pdu(0xa2, 0x01, 0x04, 0x00),
+			raw_pdu(0xb0, 0x02, 0x04),
+			raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0xc2, 0x03),
+			raw_pdu(0xd0, 0x06, 0x04),
+			raw_pdu(0xd2, 0x06),
+			raw_pdu(0xe0, 0x07, 0x04),
+			raw_pdu(0xe3, 0x07, 0x04, 0x31));
+	define_test("/TP/SIG/SMG/BI-23-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x08, 0x00),
+			raw_pdu(0x43, 0x08, 0x12));
+	define_test("/TP/SIG/SMG/BI-24-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x08, 0x04),
+			raw_pdu(0x43, 0x08, 0xc0));
+	define_test("/TP/SIG/SMG/BI-25-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0xf0, 0x09, 0x04),
+			raw_pdu(0xf3, 0x09, 0x04, 0x31));
+	define_test("/TP/SIG/SMG/BI-26-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x09, 0x04),
+			raw_pdu(0x43, 0x09, 0x04, 0x31));
+	define_test("/TP/SIG/SMG/BI-27-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0x50, 0x09, 0x04),
+			raw_pdu(0x53, 0x09, 0x04, 0xc0));
+	define_test("/TP/SIG/SMG/BI-28-C", test_server,
+			raw_pdu(0x00, 0xff),
+			raw_pdu(0x01, 0x3f));
+	define_test("/TP/SIG/SMG/BI-30-C", test_client,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0xee, 0x01, 0x00, 0x01, 0x00, 0x07,
+				0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+	define_test("/TP/SIG/SMG/ESR04/BI-28-C", test_server,
+			raw_pdu(0x00, 0x3f),
+			raw_pdu(0x01, 0x3f));
+	define_test("/TP/SIG/SMG/BI-32-C", test_client_1_3,
+			raw_pdu(0x30, 0x01),
+			raw_pdu(0x32, 0x01, 0x04, 0x00),
+			raw_pdu(0x40, 0x0c, 0x04),
+			raw_pdu(0x43, 0x0c, 0x11));
+	define_test("/TP/SIG/SMG/BI-33-C", test_server_1_3,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x0c),
+			raw_pdu(0x13, 0x0c, 0x11));
+	define_test("/TP/SIG/SMG/BI-35-C", test_client_1_3,
+			raw_pdu(0x50, 0x01),
+			raw_pdu(0x52, 0x01, 0x04, 0x00),
+			raw_pdu(0x60, 0x0c, 0x04),
+			raw_pdu(0x62, 0x0c, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+				0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+			raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00));
+	define_test("/TP/SIG/SMG/BI-36-C", test_client_1_3,
+			raw_pdu(0x80, 0x01),
+			raw_pdu(0x82, 0x01, 0x04, 0x00),
+			raw_pdu(0x90, 0x0c, 0x04),
+			raw_pdu(0x92, 0x0c, 0xee, 0x01, 0x00, 0x01, 0x00, 0x07,
+				0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+
+	/*
+	 * Signaling Message Fragmentation Service
+	 *
+	 * verify that the IUT (INT and ACP) fragments the signaling messages
+	 * that cannot fit in a single L2CAP packet.
+	 */
+	define_test("/TP/SIG/FRA/BV-01-C", test_server_frg,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			frg_pdu(0x16, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00,
+				0x00, 0xff, 0xff, 0x02, 0x40, 0x04, 0x60, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00),
+			frg_pdu(0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00),
+			raw_pdu(0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00));
+	define_test("/TP/SIG/FRA/BV-02-C", test_client_frg,
+			raw_pdu(0xb0, 0x01),
+			raw_pdu(0xb2, 0x01, 0x04, 0x00),
+			raw_pdu(0xc0, 0x02, 0x04),
+			frg_pdu(0xc6, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00,
+				0x00, 0xff, 0xff, 0x02, 0x40, 0x04, 0x60, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00),
+			frg_pdu(0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00),
+			raw_pdu(0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00));
+
+	/*
+	 * Delay Reporting
+	 *
+	 * Verify that the stream management signaling procedure of delay
+	 * reporting is implemented according to its specification in AVDTP.
+	 */
+	define_test("/TP/SIG/SYN/BV-01-C", test_server_1_3_sink,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x08),
+			raw_pdu(0x10, 0x0c, 0x04),
+			raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x08, 0x00));
+	define_test("/TP/SIG/SYN/BV-02-C", test_client_1_3,
+			raw_pdu(0xd0, 0x01),
+			raw_pdu(0xd2, 0x01, 0x04, 0x00),
+			raw_pdu(0xe0, 0x0c, 0x04),
+			raw_pdu(0xe2, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x0f, 0x00, 0x08, 0x00),
+			raw_pdu(0xf0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00));
+	define_test("/TP/SIG/SYN/BV-03-C", test_server_1_3_sink,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x08),
+			raw_pdu(0x10, 0x0c, 0x04),
+			raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x00, 0x0d, 0x04, 0x00, 0x00));
+	define_test("/TP/SIG/SYN/BV-04-C", test_client_1_3,
+			raw_pdu(0x10, 0x01),
+			raw_pdu(0x12, 0x01, 0x04, 0x00),
+			raw_pdu(0x20, 0x0c, 0x04),
+			raw_pdu(0x22, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x0f, 0x00, 0x08, 0x00),
+			raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00),
+			raw_pdu(0x32, 0x03),
+			raw_pdu(0x40, 0x0d, 0x04, 0x00, 0x00));
+	define_test("/TP/SIG/SYN/BV-05-C", test_server_1_3,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x0c, 0x04),
+			raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x0d, 0x04, 0x00, 0x00),
+			raw_pdu(0x42, 0x0d));
+	define_test("/TP/SIG/SYN/BV-06-C", test_server_1_3,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x0c, 0x04),
+			raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+				0x00),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0x50, 0x0d, 0x04, 0x00, 0x00),
+			raw_pdu(0x52, 0x0d));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-avrcp.c b/bluez/repo/unit/test-avrcp.c
new file mode 100644
index 0000000..01307e6
--- /dev/null
+++ b/bluez/repo/unit/test-avrcp.c
@@ -0,0 +1,2099 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/log.h"
+#include "lib/bluetooth.h"
+
+#include "android/avctp.h"
+#include "android/avrcp-lib.h"
+
+struct test_pdu {
+	bool valid;
+	bool fragmented;
+	bool continuing;
+	bool browse;
+	const uint8_t *data;
+	size_t size;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+};
+
+struct context {
+	struct avrcp *session;
+	guint source;
+	guint browse_source;
+	guint process;
+	int fd;
+	int browse_fd;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define brs_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.browse = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define frg_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.fragmented = true,				\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define cont_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.continuing = true,				\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define define_test(name, function, args...)				\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, function, NULL);		\
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	if (context->browse_source > 0)
+		g_source_remove(context->browse_source);
+
+	avrcp_shutdown(context->session);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	destroy_context(context);
+
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (pdu->browse)
+		len = write(context->browse_fd, pdu->data, pdu->size);
+	else
+		len = write(context->fd, pdu->data, pdu->size);
+
+	util_hexdump('<', pdu->data, len, test_debug, "AVRCP: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	if (pdu->fragmented)
+		return send_pdu(user_data);
+
+	context->process = 0;
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(!pdu->browse);
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		tester_debug("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	if (g_test_verbose())
+		util_hexdump('>', buf, len, test_debug, "AVRCP: ");
+
+	if (!pdu->continuing)
+		g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	if (!pdu->fragmented)
+		context_process(context);
+
+	return TRUE;
+}
+
+static gboolean browse_test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(pdu->browse);
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->browse_source = 0;
+		tester_debug("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	util_hexdump('>', buf, len, test_debug, "AVRCP: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	if (!pdu->fragmented)
+		context_process(context);
+
+	return TRUE;
+}
+
+static struct context *create_context(uint16_t version, gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	/* Control channel setup */
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(!err);
+
+	context->session = avrcp_new(sv[0], 672, 672, version);
+	g_assert(context->session != NULL);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+
+	/* Browsing channel setup */
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(!err);
+
+	err = avrcp_connect_browsing(context->session, sv[0], 672, 672);
+	g_assert(!err);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->browse_source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				browse_test_handler, context);
+	g_assert(context->browse_source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->browse_fd = sv[1];
+
+	context->data = data;
+
+	return context;
+}
+
+static void test_dummy(gconstpointer data)
+{
+	struct context *context =  create_context(0x0100, data);
+
+	context_quit(context);
+}
+
+static bool handle_play(struct avrcp *session, bool pressed, void *user_data)
+{
+	return true;
+}
+
+static bool handle_volume_up(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	return true;
+}
+
+static bool handle_channel_up(struct avrcp *session, bool pressed,
+							void *user_data)
+{
+	return true;
+}
+
+static bool handle_select(struct avrcp *session, bool pressed, void *user_data)
+{
+	return true;
+}
+
+static bool handle_vendor_uniq(struct avrcp *session, bool pressed,
+								void *user_data)
+{
+	return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+		{ AVC_PLAY, handle_play },
+		{ AVC_VOLUME_UP, handle_volume_up },
+		{ AVC_CHANNEL_UP, handle_channel_up },
+		{ AVC_SELECT, handle_select },
+		{ AVC_VENDOR_UNIQUE, handle_vendor_uniq },
+		{ },
+};
+
+static int get_capabilities(struct avrcp *session, uint8_t transaction,
+							void *user_data)
+{
+	return -EINVAL;
+}
+
+static int list_attributes(struct avrcp *session, uint8_t transaction,
+							void *user_data)
+{
+	avrcp_list_player_attributes_rsp(session, transaction, 0, NULL);
+
+	return 0;
+}
+
+static int get_attribute_text(struct avrcp *session, uint8_t transaction,
+					uint8_t number, uint8_t *attrs,
+					void *user_data)
+{
+	const char *text[number];
+
+	if (number) {
+		memset(text, 0, number);
+		text[0] = "equalizer";
+	}
+
+	avrcp_get_player_attribute_text_rsp(session, transaction, number, attrs,
+									text);
+
+	return 0;
+}
+
+static int list_values(struct avrcp *session, uint8_t transaction,
+						uint8_t attr, void *user_data)
+{
+	avrcp_list_player_values_rsp(session, transaction, 0, NULL);
+
+	return -EINVAL;
+}
+
+static int get_value_text(struct avrcp *session, uint8_t transaction,
+				uint8_t attr, uint8_t number, uint8_t *values,
+				void *user_data)
+{
+	const char *text[number];
+
+	if (number) {
+		memset(text, 0, number);
+		text[0] = "on";
+	}
+
+	avrcp_get_player_values_text_rsp(session, transaction, number,
+								values, text);
+
+	return -EINVAL;
+}
+
+static int get_value(struct avrcp *session, uint8_t transaction,
+			uint8_t number, uint8_t *attrs, void *user_data)
+{
+	uint8_t values[number];
+
+	memset(values, 0, number);
+
+	avrcp_get_current_player_value_rsp(session, transaction, number, attrs,
+									values);
+
+	return 0;
+}
+
+static int set_value(struct avrcp *session, uint8_t transaction,
+			uint8_t number, uint8_t *attrs, uint8_t *values,
+			void *user_data)
+{
+	avrcp_set_player_value_rsp(session, transaction);
+
+	return 0;
+}
+
+static int get_play_status(struct avrcp *session, uint8_t transaction,
+							void *user_data)
+{
+	avrcp_get_play_status_rsp(session, transaction, 0xaaaaaaaa, 0xbbbbbbbb,
+									0x00);
+
+	return 0;
+}
+
+static int get_element_attributes(struct avrcp *session, uint8_t transaction,
+					uint64_t uid, uint8_t number,
+					uint32_t *attrs, void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_has_prefix(context->data->test_name, "/TP/RCR")) {
+		uint8_t params[1024];
+
+		memset(params, 0x00, sizeof(params) / 2);
+		memset(params + (sizeof(params) / 2), 0xff, sizeof(params) / 2);
+
+		avrcp_get_element_attrs_rsp(session, transaction, params,
+							sizeof(params));
+	} else
+		avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
+
+	return 0;
+}
+
+static int track_changed(struct avrcp *session, uint8_t transaction,
+					uint32_t interval, void *user_data)
+{
+	struct context *context = user_data;
+	uint64_t track;
+
+	if (g_str_equal(context->data->test_name, "/TP/NFY/BV-05-C") ||
+		g_str_equal(context->data->test_name, "/TP/NFY/BV-08-C"))
+		memset(&track, 0, sizeof(track));
+	else
+		memset(&track, 0xff, sizeof(track));
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+						AVRCP_EVENT_TRACK_CHANGED,
+						&track, sizeof(track));
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+						AVRCP_EVENT_TRACK_CHANGED,
+						&track, sizeof(track));
+
+	return 0;
+}
+
+static int settings_changed(struct avrcp *session, uint8_t transaction,
+					uint32_t interval, void *user_data)
+{
+	uint8_t settings[3];
+
+	settings[0] = 0x01;
+	settings[1] = 0x01;
+	settings[2] = 0x02;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+						AVRCP_EVENT_SETTINGS_CHANGED,
+						settings, sizeof(settings));
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+						AVRCP_EVENT_SETTINGS_CHANGED,
+						settings, sizeof(settings));
+
+	return 0;
+}
+
+static int available_players_changed(struct avrcp *session, uint8_t transaction,
+					uint32_t interval, void *user_data)
+{
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+					AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED,
+					NULL, 0);
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+					AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED,
+					NULL, 0);
+
+	return 0;
+}
+
+static int addressed_player_changed(struct avrcp *session, uint8_t transaction,
+					uint32_t interval, void *user_data)
+{
+	uint16_t player[2];
+
+	player[0] = 0x0001;
+	player[1] = 0x0001;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+					AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED,
+					player, sizeof(player));
+
+	player[0] = 0x0200;
+	player[1] = 0x0200;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+					AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED,
+					player, sizeof(player));
+
+	return 0;
+}
+
+static int uids_changed(struct avrcp *session, uint8_t transaction,
+					uint32_t interval, void *user_data)
+{
+	struct context *context = user_data;
+	uint16_t counter;
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-09-C"))
+		counter = 0x0000;
+	else
+		counter = 0x0001;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+						AVRCP_EVENT_UIDS_CHANGED,
+						&counter, sizeof(counter));
+
+	if (!g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-11-C") &&
+		!g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-05-C"))
+		return 0;
+
+	counter = 0x0200;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+						AVRCP_EVENT_UIDS_CHANGED,
+						&counter, sizeof(counter));
+
+	return 0;
+}
+
+static int now_playing_content_changed(struct avrcp *session,
+					uint8_t transaction, uint32_t interval,
+					void *user_data)
+{
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+					AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED,
+					NULL, 0);
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+					AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED,
+					NULL, 0);
+
+	return 0;
+}
+
+static int volume_changed(struct avrcp *session, uint8_t transaction,
+					uint32_t interval, void *user_data)
+{
+	uint8_t volume = 0x00;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+					AVRCP_EVENT_VOLUME_CHANGED,
+					&volume, sizeof(volume));
+
+	volume = 0x01;
+
+	avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+					AVRCP_EVENT_VOLUME_CHANGED,
+					&volume, sizeof(volume));
+
+	return 0;
+}
+
+static int register_notification(struct avrcp *session, uint8_t transaction,
+					uint8_t event, uint32_t interval,
+					void *user_data)
+{
+	switch (event) {
+	case AVRCP_EVENT_TRACK_CHANGED:
+		return track_changed(session, transaction, interval, user_data);
+	case AVRCP_EVENT_SETTINGS_CHANGED:
+		return settings_changed(session, transaction, interval,
+								user_data);
+	case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+		return available_players_changed(session, transaction, interval,
+								user_data);
+	case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+		return addressed_player_changed(session, transaction, interval,
+								user_data);
+	case AVRCP_EVENT_UIDS_CHANGED:
+		return uids_changed(session, transaction, interval, user_data);
+	case AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED:
+		return now_playing_content_changed(session, transaction,
+							interval, user_data);
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		return volume_changed(session, transaction,
+							interval, user_data);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int set_volume(struct avrcp *session, uint8_t transaction,
+					uint8_t volume, void *user_data)
+{
+	avrcp_set_volume_rsp(session, transaction, volume);
+
+	return 0;
+}
+
+static int set_addressed(struct avrcp *session, uint8_t transaction,
+						uint16_t id, void *user_data)
+{
+	struct context *context = user_data;
+	uint8_t status;
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BI-01-C"))
+		status = AVRCP_STATUS_INVALID_PLAYER_ID;
+	else
+		status = AVRCP_STATUS_SUCCESS;
+
+	avrcp_set_addressed_player_rsp(session, transaction, status);
+
+	return 0;
+}
+
+static int set_browsed(struct avrcp *session, uint8_t transaction,
+						uint16_t id, void *user_data)
+{
+	struct context *context = user_data;
+	const char *folders[1] = { "Filesystem" };
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BI-02-C"))
+		avrcp_set_browsed_player_rsp(session, transaction,
+						AVRCP_STATUS_INVALID_PLAYER_ID,
+						0, 0, 0, NULL);
+	else
+		avrcp_set_browsed_player_rsp(session, transaction,
+						AVRCP_STATUS_SUCCESS,
+						0xabcd, 0, 1, folders);
+
+	return 0;
+}
+
+static int get_folder_items(struct avrcp *session, uint8_t transaction,
+				uint8_t scope, uint32_t start, uint32_t end,
+				uint16_t number, uint32_t *attrs,
+				void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-02-C"))
+		return -ERANGE;
+
+	if (start > 1)
+		return -ERANGE;
+
+	avrcp_get_folder_items_rsp(session, transaction, AVRCP_STATUS_SUCCESS,
+						0xabcd, 0, NULL, NULL, NULL);
+
+	return 0;
+}
+
+static int change_path(struct avrcp *session, uint8_t transaction,
+					uint16_t counter, uint8_t direction,
+					uint64_t uid, void *user_data)
+{
+	if (!uid)
+		return -ENOTDIR;
+
+	avrcp_change_path_rsp(session, transaction, AVRCP_STATUS_SUCCESS, 0);
+
+	return 0;
+}
+
+static int get_item_attributes(struct avrcp *session, uint8_t transaction,
+					uint8_t scope, uint64_t uid,
+					uint16_t counter, uint8_t number,
+					uint32_t *attrs, void *user_data)
+{
+	struct context *context = user_data;
+	uint8_t status;
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-05-C"))
+		status = AVRCP_STATUS_UID_CHANGED;
+	else
+		status = AVRCP_STATUS_SUCCESS;
+
+	avrcp_get_item_attributes_rsp(session, transaction, status, 0, NULL,
+									NULL);
+
+	return 0;
+}
+
+static int play_item(struct avrcp *session, uint8_t transaction, uint8_t scope,
+			uint64_t uid, uint16_t counter, void *user_data)
+{
+	if (!uid)
+		return -ENOENT;
+
+	avrcp_play_item_rsp(session, transaction, AVRCP_STATUS_SUCCESS);
+
+	return 0;
+}
+
+static int search(struct avrcp *session, uint8_t transaction,
+					const char *string, void *user_data)
+{
+	avrcp_search_rsp(session, transaction, AVRCP_STATUS_SUCCESS, 0xaabb, 0);
+
+	return 0;
+}
+
+static int add_to_now_playing(struct avrcp *session, uint8_t transaction,
+				uint8_t scope, uint64_t uid, uint16_t counter,
+				void *user_data)
+{
+	if (!uid)
+		return -ENOENT;
+
+	avrcp_add_to_now_playing_rsp(session, transaction,
+							AVRCP_STATUS_SUCCESS);
+
+	return 0;
+}
+
+static const struct avrcp_control_ind control_ind = {
+	.get_capabilities = get_capabilities,
+	.list_attributes = list_attributes,
+	.get_attribute_text = get_attribute_text,
+	.list_values = list_values,
+	.get_value_text = get_value_text,
+	.get_value = get_value,
+	.set_value = set_value,
+	.get_play_status = get_play_status,
+	.get_element_attributes = get_element_attributes,
+	.register_notification = register_notification,
+	.set_volume = set_volume,
+	.set_addressed = set_addressed,
+	.set_browsed = set_browsed,
+	.get_folder_items = get_folder_items,
+	.change_path = change_path,
+	.get_item_attributes = get_item_attributes,
+	.play_item = play_item,
+	.search = search,
+	.add_to_now_playing = add_to_now_playing,
+};
+
+static void test_server(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	avrcp_set_passthrough_handlers(context->session, passthrough_handlers,
+								context);
+	avrcp_register_player(context->session, &control_ind, NULL, context);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void get_folder_items_rsp(struct avrcp *session, int err,
+					uint16_t counter, uint16_t number,
+					uint8_t *params, void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert_cmpint(err, ==, 0);
+	g_assert_cmpint(counter, ==, 0xabcd);
+	g_assert_cmpint(number, ==, 0);
+
+	context_quit(context);
+}
+
+static void set_volume_rsp(struct avrcp *session, int err, uint8_t volume,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert_cmpint(err, ==, 0);
+	g_assert_cmpint(volume, ==, 1);
+
+	context_quit(context);
+}
+
+static bool register_notification_rsp(struct avrcp *session, int err,
+					uint8_t code, uint8_t event,
+					void *params, void *user_data)
+{
+	struct context *context = user_data;
+	uint8_t *p = params;
+
+	g_assert_cmpint(err, ==, 0);
+
+	switch (event) {
+	case AVRCP_EVENT_VOLUME_CHANGED:
+		if (g_str_equal(context->data->test_name, "/TP/VLH/BV-03-C")) {
+			g_assert_cmpint(p[0], ==, 0);
+			break;
+		} else if (code == AVC_CTYPE_INTERIM) {
+			g_assert_cmpint(p[0], ==, 0);
+			return true;
+		}
+		g_assert_cmpint(p[0], ==, 1);
+		break;
+	}
+
+	context_quit(context);
+
+	return false;
+}
+
+static const struct avrcp_control_cfm control_cfm = {
+	.register_notification = register_notification_rsp,
+	.set_volume = set_volume_rsp,
+	.get_folder_items = get_folder_items_rsp,
+};
+
+static void test_client(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+
+	avrcp_register_player(context->session, NULL, &control_cfm, context);
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-C"))
+		avrcp_set_addressed_player(context->session, 0xabcd);
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BV-03-C"))
+		avrcp_set_browsed_player(context->session, 0xabcd);
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BV-06-C") ||
+		g_str_equal(context->data->test_name, "/TP/MPS/BV-08-C"))
+		avrcp_get_folder_items(context->session,
+					AVRCP_MEDIA_PLAYER_LIST, 0, 2, 0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-I"))
+		avrcp_get_folder_items(context->session,
+					AVRCP_MEDIA_PLAYER_LIST, 0, 2, 0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-01-C"))
+		avrcp_get_folder_items(context->session,
+					AVRCP_MEDIA_PLAYER_VFS, 0, 2, 0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-04-C"))
+		avrcp_change_path(context->session, 0x01, 0x01, 0xaabb);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-07-C"))
+		avrcp_get_item_attributes(context->session,
+					AVRCP_MEDIA_PLAYER_VFS, 0x01, 0xaabb,
+					0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-01-C"))
+		avrcp_search(context->session, "Country");
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-03-C"))
+		avrcp_get_folder_items(context->session, AVRCP_MEDIA_SEARCH,
+						0, 2, 0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-05-C"))
+		avrcp_get_item_attributes(context->session,
+					AVRCP_MEDIA_SEARCH, 0x01, 0xaabb,
+					0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-01-C"))
+		avrcp_play_item(context->session, AVRCP_MEDIA_NOW_PLAYING, 1,
+									1);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-03-C"))
+		avrcp_add_to_now_playing(context->session,
+					AVRCP_MEDIA_NOW_PLAYING, 0x01, 0xaabb);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-05-C"))
+		avrcp_get_folder_items(context->session,
+					AVRCP_MEDIA_NOW_PLAYING, 0, 2, 0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-08-C"))
+		avrcp_get_item_attributes(context->session,
+					AVRCP_MEDIA_NOW_PLAYING, 0x01, 0xaabb,
+					0, NULL);
+
+	if (g_str_equal(context->data->test_name, "/TP/CFG/BV-01-C"))
+		avrcp_get_capabilities(context->session, CAP_EVENTS_SUPPORTED);
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-01-C"))
+		avrcp_list_player_attributes(context->session);
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-03-C")) {
+		uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+						AVRCP_ATTRIBUTE_REPEAT_MODE };
+
+		avrcp_get_player_attribute_text(context->session, sizeof(attrs),
+									attrs);
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-05-C"))
+		avrcp_list_player_values(context->session,
+						AVRCP_ATTRIBUTE_EQUALIZER);
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-07-C")) {
+		uint8_t values[2] = { AVRCP_EQUALIZER_OFF, AVRCP_EQUALIZER_ON };
+
+		avrcp_get_player_value_text(context->session,
+						AVRCP_ATTRIBUTE_EQUALIZER,
+						sizeof(values), values);
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-09-C")) {
+		uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+						AVRCP_ATTRIBUTE_REPEAT_MODE };
+
+		avrcp_get_current_player_value(context->session, sizeof(attrs),
+									attrs);
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/PAS/BV-11-C")) {
+		uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+						AVRCP_ATTRIBUTE_REPEAT_MODE };
+		uint8_t values[2] = { 0xaa, 0xff };
+
+		avrcp_set_player_value(context->session, sizeof(attrs), attrs,
+								values);
+	}
+
+	if (g_str_equal(context->data->test_name, "/TP/MDI/BV-01-C"))
+		avrcp_get_play_status(context->session);
+
+	if (g_str_equal(context->data->test_name, "/TP/MDI/BV-03-C"))
+		avrcp_get_element_attributes(context->session);
+
+	if (g_str_equal(context->data->test_name, "/TP/NFY/BV-01-C"))
+		avrcp_register_notification(context->session,
+						AVRCP_EVENT_STATUS_CHANGED, 0);
+
+	if (g_str_equal(context->data->test_name, "/TP/BGN/BV-01-I"))
+		avrcp_send_passthrough(context->session, IEEEID_BTSIG,
+						AVC_VENDOR_NEXT_GROUP);
+
+	if (g_str_equal(context->data->test_name, "/TP/BGN/BV-02-I"))
+		avrcp_send_passthrough(context->session, IEEEID_BTSIG,
+						AVC_VENDOR_PREV_GROUP);
+
+	if (g_str_equal(context->data->test_name, "/TP/VLH/BV-01-C"))
+		avrcp_set_volume(context->session, 0x00);
+
+	if (g_str_equal(context->data->test_name, "/TP/VLH/BV-03-C"))
+		avrcp_register_notification(context->session,
+						AVRCP_EVENT_VOLUME_CHANGED, 0);
+
+	if (g_str_equal(context->data->test_name, "/TP/VLH/BI-03-C"))
+		avrcp_set_volume(context->session, 0x01);
+
+	if (g_str_equal(context->data->test_name, "/TP/VLH/BI-04-C"))
+		avrcp_register_notification(context->session,
+						AVRCP_EVENT_VOLUME_CHANGED, 0);
+
+	if (g_str_equal(context->data->test_name, "/TP/PTH/BV-01-C"))
+		avrcp_send_passthrough(context->session, 0, AVC_PLAY);
+
+	if (g_str_equal(context->data->test_name, "/TP/PTH/BV-02-C"))
+		avrcp_send_passthrough(context->session, 0, AVC_FAST_FORWARD);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	__btd_log_init("*", 0);
+
+	/* Media Player Selection Commands and Notifications */
+
+	/* SetAddressedPlayer - CT */
+	define_test("/TP/MPS/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x60, 0x00, 0x00,
+				0x02, 0xab, 0xcd));
+
+	/* SetAddressedPlayer - TG */
+	define_test("/TP/MPS/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ADDRESSED_PLAYER,
+				0x00, 0x00, 0x02, 0xab, 0xcd),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_STABLE,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_SET_ADDRESSED_PLAYER,
+				0x00, 0x00, 0x01, 0x04));
+
+	/* SetBrowsedPlayer - CT */
+	define_test("/TP/MPS/BV-03-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, 0x70, 0x00, 0x02,
+				0xab, 0xcd));
+
+	/* SetBrowsedPlayer - TG */
+	define_test("/TP/MPS/BV-04-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, 0x70, 0x00, 0x02,
+				0xab, 0xcd),
+			brs_pdu(0x02, 0x11, 0x0e, 0x70, 0x00, 0x16,
+				0x04, 0xab, 0xcd, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x6a, 0x01, 0x00, 0x0a,
+				0x46, 0x69, 0x6c, 0x65, 0x73, 0x79,
+				0x73, 0x74, 0x65, 0x6d));
+
+	/* AddressedPlayerChanged notification – TG */
+	define_test("/TP/MPS/BV-05-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0b,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0b,
+				0x00, 0x01, 0x00, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0b,
+				0x02, 0x00, 0x02, 0x00));
+
+	/* GetFolderItems - CT */
+	define_test("/TP/MPS/BV-06-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+	/* AvailablePlayersChanged Notification – TG */
+	define_test("/TP/MPS/BV-07-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0a,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x01, 0x0a),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x01, 0x0a));
+
+	/* GetFolderItems - CT */
+	define_test("/TP/MPS/BV-08-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00));
+
+	/* GetFolderItems - TG */
+	define_test("/TP/MPS/BV-09-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+	/* SetAddressedPlayer - TG */
+	define_test("/TP/MPS/BI-01-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ADDRESSED_PLAYER,
+				0x00, 0x00, 0x02, 0xab, 0xcd),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_STABLE,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_SET_ADDRESSED_PLAYER,
+				0x00, 0x00, 0x01, 0x11));
+
+	/* SetBrowsedPlayer - TG */
+	define_test("/TP/MPS/BI-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, 0x70, 0x00, 0x02,
+				0xab, 0xcd),
+			brs_pdu(0x02, 0x11, 0x0e, 0x70, 0x00, 0x01,
+				0x11));
+
+	/*
+	 * Media Content Navigation Commands and Notifications for Content
+	 * Browsing.
+	 */
+
+	/* GetFolderItems - Virtual FS - CT */
+	define_test("/TP/MCN/CB/BV-01-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00));
+
+	/* GetFolderItems - Virtual FS - TG */
+	define_test("/TP/MCN/CB/BV-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+	/* GetFolderItems - Virtual FS - TG */
+	define_test("/TP/MCN/CB/BV-03-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ADDRESSED_PLAYER,
+				0x00, 0x00, 0x02, 0xab, 0xcd),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_STABLE,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_SET_ADDRESSED_PLAYER,
+				0x00, 0x00, 0x01, 0x04),
+			brs_pdu(0x00, 0x11, 0x0e, 0x70, 0x00, 0x02,
+				0xab, 0xcd),
+			brs_pdu(0x02, 0x11, 0x0e, 0x70, 0x00, 0x16,
+				0x04, 0xab, 0xcd, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x6a, 0x01, 0x00, 0x0a,
+				0x46, 0x69, 0x6c, 0x65, 0x73, 0x79,
+				0x73, 0x74, 0x65, 0x6d),
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+	/* ChangePath - CT */
+	define_test("/TP/MCN/CB/BV-04-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x0b,
+				0xaa, 0xbb,		/* counter */
+				0x01,			/* direction */
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01	/* Folder UID */));
+
+	/* ChangePath - TG */
+	define_test("/TP/MCN/CB/BV-05-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x0b,
+				0xaa, 0xbb,		/* counter */
+				0x01,			/* direction */
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01	/* Folder UID */),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00));
+
+	/* ChangePath - TG */
+	define_test("/TP/MCN/CB/BV-06-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x0b,
+				0xaa, 0xbb,		/* counter */
+				0x00,			/* direction */
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01	/* Folder UID */),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00));
+
+	/* GetItemAttributes - CT */
+	define_test("/TP/MCN/CB/BV-07-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,	/* uuid */
+				0xaa, 0xbb,		/* counter */
+				0x00));			/* num attr */
+
+	/* GetItemAttributes - TG */
+	define_test("/TP/MCN/CB/BV-08-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,	/* uuid */
+				0xaa, 0xbb,		/* counter */
+				0x00),			/* num attr */
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x02, 0x04, 0x00));
+
+	/* UIDcounter - TG */
+	define_test("/TP/MCN/CB/BV-09-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0c,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x03, 0x0c,
+				0x00, 0x00));
+
+	/* UIDcounter - TG */
+	define_test("/TP/MCN/CB/BV-10-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0c,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x03, 0x0c,
+				0x00, 0x01));
+
+	/* UIDcounter - TG */
+	define_test("/TP/MCN/CB/BV-11-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0c,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x03, 0x0c,
+				0x00, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x03, 0x0c,
+				0x02, 0x00));
+
+	/* GetFolderItems - Virtual FS - TG */
+	define_test("/TP/MCN/CB/BI-01-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x01, /* start */
+				0x00, 0x00, 0x00, 0x00, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x01, 0x0b));
+
+	/* GetFolderItems - Virtual FS - TG */
+	define_test("/TP/MCN/CB/BI-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x01, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x01, 0x0b));
+
+	/* GetFolderItems - Virtual FS - TG */
+	define_test("/TP/MCN/CB/BI-03-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+				0x00, 0x00, 0x00, 0x02, /* start */
+				0x00, 0x00, 0x00, 0x03, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x01, 0x0b));
+
+	/* ChangePath - TG */
+	define_test("/TP/MCN/CB/BI-04-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x0b,
+				0xaa, 0xbb,		/* counter */
+				0x01,			/* direction */
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00	/* Folder UID */),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+				0x00, 0x01, 0x08));
+
+	/* UIDcounter - TG */
+	define_test("/TP/MCN/CB/BI-05-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0c,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x03, 0x0c,
+				0x00, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x03, 0x0c,
+				0x02, 0x00),
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,
+				0xaa, 0xbb,
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x01, 0x05));
+
+	/* Media Content Navigation Commands and Notifications for Search */
+
+	/* Search - CT */
+	define_test("/TP/MCN/SRC/BV-01-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_SEARCH,
+				0x00, 0x0b, 0x00, 0x6a,
+				0x00, 0x07,
+				0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79));
+
+	define_test("/TP/MCN/SRC/BV-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_SEARCH,
+				0x00, 0x0b, 0x00, 0x6a,
+				0x00, 0x07,
+				0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_SEARCH,
+				0x00, 0x07, 0x04,
+				0xaa, 0xbb,		/* counter */
+				0x00, 0x00, 0x00, 0x00));
+
+	/* GetFolderItems - CT */
+	define_test("/TP/MCN/SRC/BV-03-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_SEARCH,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00));
+
+	/* GetFolderItems - NowPlaying - TG */
+	define_test("/TP/MCN/SCR/BV-04-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_SEARCH,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+	/* GetItemAttributes - CT */
+	define_test("/TP/MCN/SRC/BV-05-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_SEARCH,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,	/* uuid */
+				0xaa, 0xbb,		/* counter */
+				0x00));			/* num attr */
+
+	/* GetItemAttributes - TG */
+	define_test("/TP/MCN/SRC/BV-06-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_SEARCH,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,	/* uid */
+				0xaa, 0xbb,		/* counter */
+				0x00),			/* num attr */
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x02, 0x04, 0x00));
+
+	/* Media Content Navigation Commands and Notifications for NowPlaying */
+
+	/* PlayItem - NowPlaying - CT */
+	define_test("/TP/MCN/NP/BV-01-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+				0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+				0x00, 0x01));
+
+	/* PlayItem - NowPlaying - TG */
+	define_test("/TP/MCN/NP/BV-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+				0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+				0x00, 0x01),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+				0x00, 0x01, 0x04));
+
+	/* AddToNowPlaying - NowPlaying - CT */
+	define_test("/TP/MCN/NP/BV-03-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+				0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01, /* uid */
+				0xaa, 0xbb));
+
+	/* AddToNowPlaying - NowPlaying - TG */
+	define_test("/TP/MCN/NP/BV-04-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+				0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01, /* uid */
+				0xaa, 0xbb),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+				0x00, 0x01, 0x04));
+
+	/* GetFolderItems - NowPlaying - CT */
+	define_test("/TP/MCN/NP/BV-05-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00));
+
+	/* GetFolderItems - NowPlaying - TG */
+	define_test("/TP/MCN/NP/BV-06-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+	/* NowPlayingContentChanged Notification – TG */
+	define_test("/TP/MCN/NP/BV-07-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x09,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x01, 0x09),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x01, 0x09));
+
+	/* GetItemAttributes - CT */
+	define_test("/TP/MCN/NP/BV-08-C", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,	/* uid */
+				0xaa, 0xbb,		/* counter */
+				0x00));			/* num attr */
+
+	/* GetItemAttributes - TG */
+	define_test("/TP/MCN/CB/BV-09-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x01,	/* uid */
+				0xaa, 0xbb,		/* counter */
+				0x00),			/* num attr */
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+				0x00, 0x02, 0x04, 0x00));
+
+	/* PlayItem - NowPlaying - TG */
+	define_test("/TP/MCN/NP/BI-01-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+				0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, /* uid */
+				0xaa, 0xbb),		/* counter */
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+				0x00, 0x01, 0x09));
+
+	/* AddToNowPlaying - NowPlaying - TG */
+	define_test("/TP/MCN/NP/BI-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+				0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+				0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, /* uid */
+				0xaa, 0xbb),		/* counter */
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+				0x00, 0x01, 0x09));
+
+	/* Media Player Selection IOP tests */
+
+	/* Listing of available media players */
+	define_test("/TP/MPS/BV-01-I", test_client,
+			brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+				0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+				0x00, 0x00, 0x00, 0x00, /* start */
+				0x00, 0x00, 0x00, 0x02, /* end */
+				0x00));
+
+	/* Connection Establishment for Browsing tests */
+
+	/*
+	 * Tests are checking connection establishment and release
+	 * for browsing channel. Since we are connected through socketpair
+	 * the tests are dummy
+	 */
+	define_test("/TP/CON/BV-01-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CON/BV-02-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CON/BV-03-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CON/BV-04-C", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CON/BV-05-C", test_dummy, raw_pdu(0x00));
+
+	/* Connection Establishment for Control tests */
+
+	/*
+	 * Tests are checking connection establishement and release
+	 * for control channel. Since we are connected through socketpair
+	 * the tests are dummy
+	 */
+	define_test("/TP/CEC/BV-01-I", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CEC/BV-02-I", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CRC/BV-01-I", test_dummy, raw_pdu(0x00));
+	define_test("/TP/CRC/BV-02-I", test_dummy, raw_pdu(0x00));
+
+	/* Information collection for control tests */
+
+	define_test("/TP/ICC/BV-01-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0xf8, 0x30,
+				0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0xf8, 0x30,
+				0x07, 0x48, 0xff, 0xff, 0xff));
+
+	define_test("/TP/ICC/BV-02-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0xf8, 0x31,
+				0x07, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0xf8, 0x31,
+				0x07, 0x48, 0xff, 0xff, 0xff));
+
+	define_test("/TP/PTT/BV-01-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+				0x44, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+				0x44, 0x00));
+
+	define_test("/TP/PTT/BV-02-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+				AVC_VOLUME_UP, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+				AVC_VOLUME_UP, 0x00));
+
+	define_test("/TP/PTT/BV-03-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+				AVC_CHANNEL_UP, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+				AVC_CHANNEL_UP, 0x00));
+
+	define_test("/TP/PTT/BV-04-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+				AVC_SELECT, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+				AVC_SELECT, 0x00));
+
+	define_test("/TP/PTT/BV-05-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+				AVC_PLAY, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+				AVC_PLAY, 0x00),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+				AVC_PLAY | 0x80, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+				AVC_PLAY | 0x80, 0x00));
+
+	/* Metadata transfer tests */
+
+	define_test("/TP/CFG/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+				0x01, 0x03));
+
+	define_test("/TP/CFG/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+				0x01, 0x02),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+				0x05, 0x02, 0x01, 0x00, 0x19, 0x58));
+
+	define_test("/TP/CFG/BI-01-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+				0x01, 0x7f),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58, 0x10,
+				0x00, 0x00, 0x01,
+				AVRCP_STATUS_INVALID_PARAM));
+
+	/* Player Application Settings tests */
+
+	define_test("/TP/PAS/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x11, 0x00, 0x00,
+				0x00));
+
+	define_test("/TP/PAS/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x11, 0x00, 0x00,
+				0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, 0x11, 0x00, 0x00,
+				0x01, 0x00));
+
+	define_test("/TP/PAS/BV-03-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+				0x00, 0x00, 0x03, 0x02,
+				AVRCP_ATTRIBUTE_EQUALIZER,
+				AVRCP_ATTRIBUTE_REPEAT_MODE));
+
+	define_test("/TP/PAS/BV-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+				0x00, 0x00, 0x02, 0x01, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+				0x00, 0x00, 0x0e, 0x01, 0x01, 0x00,
+				0x6a, 0x09, 0x65, 0x71, 0x75, 0x61,
+				0x6c, 0x69, 0x7a, 0x65, 0x72));
+
+	define_test("/TP/PAS/BV-05-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_LIST_PLAYER_VALUES,
+				0x00, 0x00, 0x01,
+				AVRCP_ATTRIBUTE_EQUALIZER));
+
+	define_test("/TP/PAS/BV-06-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_LIST_PLAYER_VALUES,
+				0x00, 0x00, 0x01, AVRCP_ATTRIBUTE_EQUALIZER),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_LIST_PLAYER_VALUES,
+				0x00, 0x00, 0x01, 0x00));
+
+	define_test("/TP/PAS/BV-07-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_VALUE_TEXT,
+				0x00, 0x00, 0x04,
+				AVRCP_ATTRIBUTE_EQUALIZER, 0x02,
+				AVRCP_EQUALIZER_OFF,
+				AVRCP_EQUALIZER_ON));
+
+	define_test("/TP/PAS/BV-08-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_VALUE_TEXT,
+				0x00, 0x00, 0x03, AVRCP_ATTRIBUTE_EQUALIZER,
+				0x01, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_VALUE_TEXT,
+				0x00, 0x00, 0x07, 0x01, 0x01, 0x00,
+				0x6a, 0x02, 0x6f, 0x6e));
+
+	define_test("/TP/PAS/BV-09-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_CURRENT_PLAYER_VALUE,
+				0x00, 0x00, 0x03, 0x02,
+				AVRCP_ATTRIBUTE_EQUALIZER,
+				AVRCP_ATTRIBUTE_REPEAT_MODE));
+
+	define_test("/TP/PAS/BV-10-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_CURRENT_PLAYER_VALUE,
+				0x00, 0x00, 0x03, 0x02,
+				AVRCP_ATTRIBUTE_EQUALIZER,
+				AVRCP_ATTRIBUTE_REPEAT_MODE),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_CURRENT_PLAYER_VALUE,
+				0x00, 0x00, 0x05, 0x02,
+				AVRCP_ATTRIBUTE_EQUALIZER, 0x00,
+				AVRCP_ATTRIBUTE_REPEAT_MODE, 0x00));
+
+	define_test("/TP/PAS/BV-11-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_SET_PLAYER_VALUE,
+				0x00, 0x00, 0x05, 0x02,
+				AVRCP_ATTRIBUTE_EQUALIZER, 0xaa,
+				AVRCP_ATTRIBUTE_REPEAT_MODE, 0xff));
+
+	/* Get player app setting attribute text invalid behavior - TG */
+	define_test("/TP/PAS/BI-01-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+				0x00, 0x00, 0x02, 0x01,
+				/* Invalid attribute id */
+				0x7f),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+				0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+	/* List player application setting values invalid behavior - TG */
+	define_test("/TP/PAS/BI-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_LIST_PLAYER_VALUES,
+				0x00, 0x00, 0x01,
+				/* Invalid attribute id */
+				0x7f),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_LIST_PLAYER_VALUES,
+				0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+	/* Get player application setting value text invalid behavior - TG */
+	define_test("/TP/PAS/BI-03-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_VALUE_TEXT,
+				0x00, 0x00, 0x03, AVRCP_ATTRIBUTE_EQUALIZER,
+				0x01,
+				/* Invalid setting value */
+				0x7f),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_GET_PLAYER_VALUE_TEXT,
+				0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+	/* Get current player application setting value invalid behavior - TG */
+	define_test("/TP/PAS/BI-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_GET_CURRENT_PLAYER_VALUE,
+				0x00, 0x00, 0x02, 0x01,
+				/* Invalid attribute */
+				0x7f),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_GET_CURRENT_PLAYER_VALUE,
+				0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+	/* Set player application setting value invalid behavior - TG */
+	define_test("/TP/PAS/BI-05-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				AVRCP_SET_PLAYER_VALUE,
+				0x00, 0x00, 0x03, 0x01,
+				AVRCP_ATTRIBUTE_REPEAT_MODE, 0x7f),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_SET_PLAYER_VALUE,
+				0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+	/* Media Information Commands */
+
+	/* Get play status - CT */
+	define_test("/TP/MDI/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS,
+				0x00, 0x00, 0x00));
+
+	/* Get play status - TG */
+	define_test("/TP/MDI/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS,
+				0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS,
+				0x00, 0x00, 0x09,
+				0xbb, 0xbb, 0xbb, 0xbb, /* duration */
+				0xaa, 0xaa, 0xaa, 0xaa, /* position */
+				0x00));
+
+	/* Get element attributes - CT */
+	define_test("/TP/MDI/BV-03-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
+
+	/* Get element attributes - TG */
+	define_test("/TP/MDI/BV-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x00));
+
+	/* Get element attributes - TG */
+	define_test("/TP/MDI/BV-05-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+				0x00, 0x00, 0x00, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x00));
+
+	/* Notification Commands */
+
+	/* Register notification - CT */
+	define_test("/TP/NFY/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, AVRCP_EVENT_STATUS_CHANGED,
+				0x00, 0x00, 0x00, 0x00));
+
+	/* Register notification - TG */
+	define_test("/TP/NFY/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff));
+
+	/* Register notification - TG */
+	define_test("/TP/NFY/BV-03-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05,
+				AVRCP_EVENT_SETTINGS_CHANGED,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x04,
+				AVRCP_EVENT_SETTINGS_CHANGED,
+				0x01, 0x01, 0x02),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x04,
+				AVRCP_EVENT_SETTINGS_CHANGED,
+				0x01, 0x01, 0x02));
+
+	/* Register notification - Track Changed - No Selected Track - TG */
+	define_test("/TP/NFY/BV-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff));
+
+	/* Register notification - Track Changed - Track Playing - TG */
+	define_test("/TP/NFY/BV-05-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00));
+
+	/* Register notification - Track Changed - Selected Track - TG */
+	define_test("/TP/NFY/BV-08-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x00));
+
+	/* Register notification - Register for events invalid behavior - TG */
+	define_test("/TP/NFY/BI-01-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05,
+				/* Invalid event id */
+				0xff,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+	/* Invalid commands */
+
+	/* Invalid PDU ID - TG */
+	define_test("/TP/INV/BI-01-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58,
+				/* Invalid PDU ID */
+				0xff,
+				0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+				0x48, 0x00, 0x00, 0x19, 0x58,
+				0xff, 0x00, 0x00, 0x01,
+				AVRCP_STATUS_INVALID_COMMAND));
+
+	/* Invalid PDU ID - Browsing TG */
+	define_test("/TP/INV/BI-02-C", test_server,
+			brs_pdu(0x00, 0x11, 0x0e, 0xff, 0x00, 0x00),
+			brs_pdu(0x02, 0x11, 0x0e, AVRCP_GENERAL_REJECT,
+				0x00, 0x01, AVRCP_STATUS_INVALID_COMMAND));
+
+	/* Next Group command transfer - CT */
+	define_test("/TP/BGN/BV-01-I", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH,
+				AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+				0x58, 0x00, AVC_VENDOR_NEXT_GROUP));
+
+	/* Next Group command transfer - TG */
+	define_test("/TP/BGN/BV-01-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH,
+				AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+				0x58, 0x00, AVC_VENDOR_NEXT_GROUP),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED,
+				0x48, AVC_OP_PASSTHROUGH,
+				AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+				0x58, 0x00, AVC_VENDOR_NEXT_GROUP));
+
+	/* Previous Group command transfer - CT */
+	define_test("/TP/BGN/BV-02-I", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH,
+				AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+				0x58, 0x00, AVC_VENDOR_PREV_GROUP));
+
+	/* Previous Group command transfer - TG */
+	define_test("/TP/BGN/BV-02-I", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH,
+				AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+				0x58, 0x00, AVC_VENDOR_PREV_GROUP),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED,
+				0x48, AVC_OP_PASSTHROUGH,
+				AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+				0x58, 0x00, AVC_VENDOR_PREV_GROUP));
+
+	/* Volume Level Handling */
+
+	/* Set absolute volume – CT */
+	define_test("/TP/VLH/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x00));
+
+	/* Set absolute volume – TG */
+	define_test("/TP/VLH/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x00));
+
+	/* NotifyVolumeChange - CT */
+	define_test("/TP/VLH/BV-03-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0d,
+				0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x02, 0x0d,
+				0x00));
+
+	/* NotifyVolumeChange - TG */
+	define_test("/TP/VLH/BV-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0d,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x02, 0x0d,
+				0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x02, 0x0d,
+				0x01));
+
+	/* Set absolute volume – TG */
+	define_test("/TP/VLH/BI-01-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x01));
+
+	/* Set absolute volume – TG */
+	define_test("/TP/VLH/BI-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x80),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x00));
+
+	/* Set Absolute Volume invalid behavior CT */
+	define_test("/TP/VLH/BI-03-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x01),
+			raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+				0x00, 0x00, 0x01, 0x81));
+
+	/* Set Absolute Volume invalid behavior CT */
+	define_test("/TP/VLH/BI-04-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x05, 0x0d,
+				0x00, 0x00, 0x00, 0x00),
+			frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x02, 0x0d,
+				0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+				0x00, 0x00, 0x02, 0x0d,
+				0x81));
+
+	/* PASS THROUGH Handling */
+
+	/* Press and release – CT */
+	define_test("/TP/PTH/BV-01-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_PLAY, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_PLAY),
+			raw_pdu(0x10, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_PLAY | 0x80, 0x00));
+
+	define_test("/TP/PTH/BV-02-C", test_client,
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_FAST_FORWARD, 0x00),
+			raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_FAST_FORWARD),
+			raw_pdu(0x10, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_FAST_FORWARD | 0x80,
+				0x00),
+			raw_pdu(0x12, 0x11, 0x0e, AVC_CTYPE_ACCEPTED, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_FAST_FORWARD | 0x80),
+			raw_pdu(0x20, 0x11, 0x0e, 0x00, 0x48,
+				AVC_OP_PASSTHROUGH, AVC_FAST_FORWARD, 0x00));
+
+	/* Request continuing response - TG */
+	define_test("/TP/RCR/BV-02-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x01, 0x01, 0xf9),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REQUEST_CONTINUING,
+				0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x02, 0x01, 0xf9),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_REQUEST_CONTINUING,
+				0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x03, 0x00, 0x0e));
+
+	/* Abort continuing response - TG */
+	define_test("/TP/RCR/BV-04-C", test_server,
+			raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+				0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+			cont_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+				0x01, 0x01, 0xf9),
+			raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
+				0x00, 0x00, 0x01, AVRCP_GET_ELEMENT_ATTRIBUTES),
+			raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x00,
+				0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
+				0x00, 0x00, 0x00));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-crc.c b/bluez/repo/unit/test-crc.c
new file mode 100644
index 0000000..791fa51
--- /dev/null
+++ b/bluez/repo/unit/test-crc.c
@@ -0,0 +1,193 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  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 "monitor/crc.h"
+#include "src/shared/tester.h"
+
+#include <glib.h>
+
+struct crc_data {
+	const void *packet;
+	size_t size;
+	uint32_t crc_init;
+};
+
+static const unsigned char packet_1[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x00, 0x17, 0x7e, 0x01,
+	0x00, 0xd0, 0x22, 0x00, 0x02, 0x01, 0x06, 0x03,
+	0x02, 0x0d, 0x18, 0x06, 0xff, 0x6b, 0x00, 0x03,
+	0x16, 0x52, 0x02, 0x0a, 0x00, 0xf4, 0x09, 0x92,
+};
+
+static const struct crc_data crc_1 = {
+	.packet = packet_1,
+	.size = sizeof(packet_1),
+};
+
+static const unsigned char packet_2[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x00, 0x17, 0x7e, 0x01,
+	0x00, 0xd0, 0x22, 0x00, 0x02, 0x01, 0x06, 0x03,
+	0x02, 0x0d, 0x18, 0x06, 0xff, 0x6b, 0x00, 0x03,
+	0x16, 0x54, 0x02, 0x0a, 0x00, 0x95, 0x5f, 0x14,
+};
+
+static const struct crc_data crc_2 = {
+	.packet = packet_2,
+	.size = sizeof(packet_2),
+};
+
+static const unsigned char packet_3[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x00, 0x17, 0x7e, 0x01,
+	0x00, 0xd0, 0x22, 0x00, 0x02, 0x01, 0x06, 0x03,
+	0x02, 0x0d, 0x18, 0x06, 0xff, 0x6b, 0x00, 0x03,
+	0x16, 0x55, 0x02, 0x0a, 0x00, 0x85, 0x66, 0x63,
+};
+
+static const struct crc_data crc_3 = {
+	.packet = packet_3,
+	.size = sizeof(packet_3),
+};
+
+static const unsigned char packet_4[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x00, 0x17, 0x7e, 0x01,
+	0x00, 0xd0, 0x22, 0x00, 0x02, 0x01, 0x06, 0x03,
+	0x02, 0x0d, 0x18, 0x06, 0xff, 0x6b, 0x00, 0x03,
+	0x16, 0x53, 0x02, 0x0a, 0x00, 0xe4, 0x30, 0xe5,
+};
+
+static const struct crc_data crc_4 = {
+	.packet = packet_4,
+	.size = sizeof(packet_4),
+};
+
+static const unsigned char packet_5[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x03, 0x0c, 0x46, 0x1c,
+	0xda, 0x72, 0x02, 0x00, 0x7e, 0x01, 0x00, 0xd0,
+	0x22, 0x00, 0x6e, 0xf4, 0x6f,
+};
+
+static const struct crc_data crc_5 = {
+	.packet = packet_5,
+	.size = sizeof(packet_5),
+};
+
+static const unsigned char packet_6[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x04, 0x17, 0x7e, 0x01,
+	0x00, 0xd0, 0x22, 0x00, 0x10, 0x09, 0x50, 0x6f,
+	0x6c, 0x61, 0x72, 0x20, 0x48, 0x37, 0x20, 0x30,
+	0x30, 0x30, 0x31, 0x37, 0x45, 0x0f, 0x8a, 0x65,
+};
+
+static const struct crc_data crc_6 = {
+	.packet = packet_6,
+	.size = sizeof(packet_6),
+};
+
+static const unsigned char packet_7[] = {
+	0xd6, 0xbe, 0x89, 0x8e, 0x05, 0x22, 0x46, 0x1c,
+	0xda, 0x72, 0x02, 0x00, 0x7e, 0x01, 0x00, 0xd0,
+	0x22, 0x00, 0x96, 0x83, 0x9a, 0xaf, 0xbe, 0x1d,
+	0x16, 0x03, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00,
+	0x2a, 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xa5,
+	0x77, 0x2d, 0x95,
+};
+
+static const struct crc_data crc_7 = {
+	.packet = packet_7,
+	.size = sizeof(packet_7),
+};
+
+static const unsigned char packet_8[] = {
+	0x96, 0x83, 0x9a, 0xaf, 0x01, 0x00, 0xc7, 0x15,
+	0x4d,
+};
+
+static const struct crc_data crc_8 = {
+	.packet = packet_8,
+	.size = sizeof(packet_8),
+	.crc_init = 0x161dbe,		/* from packet_7 = 0xbe 0x1d 0x16 */
+};
+
+static const unsigned char packet_9[] = {
+	0x96, 0x83, 0x9a, 0xaf, 0x06, 0x14, 0x10, 0x00,
+	0x04, 0x00, 0x09, 0x07, 0x10, 0x00, 0x10, 0x11,
+	0x00, 0x37, 0x2a, 0x13, 0x00, 0x02, 0x14, 0x00,
+	0x38, 0x2a, 0x73, 0x2a, 0xa3,
+};
+
+static const struct crc_data crc_9 = {
+	.packet = packet_9,
+	.size = sizeof(packet_9),
+	.crc_init = 0x161dbe,		/* from packet_7 = 0xbe 0x1d 0x16 */
+};
+
+static void test_crc(gconstpointer data)
+{
+	const struct crc_data *test_data = data;
+	const uint8_t *buf = test_data->packet + test_data->size - 3;
+	uint32_t crc_init, crc_value, crc, rev;
+
+	if (test_data->crc_init)
+		crc_init = crc24_bit_reverse(test_data->crc_init);
+	else
+		crc_init = crc24_bit_reverse(0x555555);
+
+	crc_value = buf[0] | buf[1] << 8 | buf[2] << 16;
+
+	crc = crc24_calculate(crc_init, test_data->packet + 4,
+						test_data->size - 7);
+
+	tester_debug("CRC: 0x%6.6x, Calculated: 0x%6.6x", crc_value, crc);
+
+	g_assert(crc_value == crc);
+
+	rev = crc24_reverse(crc_value, test_data->packet + 4,
+						test_data->size - 7);
+
+	tester_debug("Preset: 0x%6.6x, Calculated: 0x%6.6x", crc_init, rev);
+
+	g_assert(crc_init == rev);
+
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/crc/1", &crc_1, NULL, test_crc, NULL);
+	tester_add("/crc/2", &crc_2, NULL, test_crc, NULL);
+	tester_add("/crc/3", &crc_3, NULL, test_crc, NULL);
+	tester_add("/crc/4", &crc_4, NULL, test_crc, NULL);
+	tester_add("/crc/5", &crc_5, NULL, test_crc, NULL);
+	tester_add("/crc/6", &crc_6, NULL, test_crc, NULL);
+	tester_add("/crc/7", &crc_7, NULL, test_crc, NULL);
+	tester_add("/crc/8", &crc_8, NULL, test_crc, NULL);
+	tester_add("/crc/9", &crc_9, NULL, test_crc, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-crypto.c b/bluez/repo/unit/test-crypto.c
new file mode 100644
index 0000000..5fb1c64
--- /dev/null
+++ b/bluez/repo/unit/test-crypto.c
@@ -0,0 +1,166 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  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 "src/shared/crypto.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+#include <string.h>
+#include <glib.h>
+
+static struct bt_crypto *crypto;
+
+struct test_data {
+	const uint8_t *msg;
+	uint16_t msg_len;
+	const uint8_t *t;
+};
+
+static const uint8_t key[] = {
+	0x3c, 0x4f, 0xcf, 0x09, 0x88, 0x15, 0xf7, 0xab, 0xa6, 0xd2, 0xae, 0x28,
+	0x16, 0x15, 0x7e, 0x2b
+};
+
+static const uint8_t msg_1[] = { 0x00 };
+
+static const uint8_t t_msg_1[] = {
+	0x00, 0x00, 0x00, 0x00, 0xb3, 0xa8, 0x59, 0x41, 0x27, 0xeb, 0xc2, 0xc0
+};
+
+static const struct test_data test_data_1 = {
+	.msg = msg_1,
+	.msg_len = 0,
+	.t = t_msg_1
+};
+
+static const uint8_t msg_2[] = {
+	0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+	0x73, 0x93, 0x17, 0x2a
+
+};
+
+static const uint8_t t_msg_2[] = {
+	0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x74, 0xf4, 0x39, 0x2a, 0x23, 0x2a
+};
+
+static const struct test_data test_data_2 = {
+	.msg = msg_2,
+	.msg_len = 16,
+	.t = t_msg_2
+};
+
+static const uint8_t msg_3[] = {
+	0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+	0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+	0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+	0xa3, 0x5c, 0xe4, 0x11
+};
+
+static const uint8_t t_msg_3[12] = {
+	0x00, 0x00, 0x00, 0x00, 0xb7, 0xca, 0x94, 0xab, 0x87, 0xc7, 0x82, 0x18
+};
+
+static const struct test_data test_data_3 = {
+	.msg = msg_3,
+	.msg_len = 40,
+	.t = t_msg_3
+};
+
+static const uint8_t msg_4[] = {
+	0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
+	0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+	0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
+	0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+	0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
+	0xe6, 0x6c, 0x37, 0x10
+};
+
+static const uint8_t t_msg_4[12] = {
+	0x00, 0x00, 0x00, 0x00, 0x44, 0xe1, 0xe6, 0xce, 0x1d, 0xf5, 0x13, 0x68
+};
+
+static const struct test_data test_data_4 = {
+	.msg = msg_4,
+	.msg_len = 64,
+	.t = t_msg_4
+};
+
+static void print_debug(const char *str, void *user_data)
+{
+	tester_debug("%s", str);
+}
+
+static bool result_compare(const uint8_t exp[12], uint8_t res[12])
+{
+	int i;
+	for (i = 0; i < 12; i++)
+		if (exp[i] != res[i])
+			return false;
+
+	return true;
+}
+
+static void test_sign(gconstpointer data)
+{
+	uint8_t t[12];
+	const struct test_data *d = data;
+
+	memset(t, 0, 12);
+	if (!bt_crypto_sign_att(crypto, key, d->msg, d->msg_len, 0, t))
+		g_assert(true);
+
+	tester_debug("Result T:");
+	util_hexdump(' ', t, 12, print_debug, NULL);
+	tester_debug("Expected T:");
+	util_hexdump(' ', d->t, 12, print_debug, NULL);
+
+	g_assert(result_compare(d->t, t));
+
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	int exit_status;
+
+	crypto = bt_crypto_new();
+	if (!crypto)
+		return 0;
+
+	tester_init(&argc, &argv);
+
+	tester_add("/crypto/sign_att_1", &test_data_1, NULL, test_sign, NULL);
+	tester_add("/crypto/sign_att_2", &test_data_2, NULL, test_sign, NULL);
+	tester_add("/crypto/sign_att_3", &test_data_3, NULL, test_sign, NULL);
+	tester_add("/crypto/sign_att_4", &test_data_4, NULL, test_sign, NULL);
+
+	exit_status = tester_run();
+
+	bt_crypto_unref(crypto);
+
+	return exit_status;
+}
diff --git a/bluez/repo/unit/test-ecc.c b/bluez/repo/unit/test-ecc.c
new file mode 100644
index 0000000..9b48d0b
--- /dev/null
+++ b/bluez/repo/unit/test-ecc.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *  * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "src/shared/ecc.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+static void print_dump(const char *str, void *user_data)
+{
+	tester_debug("%s", str);
+}
+
+static void print_buf(const char *label, uint8_t *buf, size_t len)
+{
+	tester_debug("%s", label);
+
+	util_hexdump(' ', buf, len, print_dump, NULL);
+}
+
+#define PAIR_COUNT 200
+
+static void test_multi(const void *data)
+{
+	uint8_t public1[64], public2[64];
+	uint8_t private1[32], private2[32];
+	uint8_t shared1[32], shared2[32];
+	int i;
+
+	tester_debug("Testing %u random private key pairs", PAIR_COUNT);
+
+	for (i = 0; i < PAIR_COUNT; i++) {
+		ecc_make_key(public1, private1);
+		ecc_make_key(public2, private2);
+
+		ecdh_shared_secret(public1, private2, shared1);
+		ecdh_shared_secret(public2, private1, shared2);
+
+		if (memcmp(shared1, shared2, sizeof(shared1)) != 0) {
+			tester_debug("Shared secrets are not identical!\n");
+			print_buf("Shared secret 1 = ", shared1,
+							sizeof(shared1));
+			print_buf("Shared secret 2 = ", shared2,
+							sizeof(shared2));
+			print_buf("Private key 1 = ", private1,
+							sizeof(private1));
+			print_buf("Private key 2 = ", private2,
+							sizeof(private2));
+			g_assert_not_reached();
+		}
+	}
+
+	tester_test_passed();
+}
+
+static int test_sample(uint8_t priv_a[32], uint8_t priv_b[32],
+				uint8_t pub_a[64], uint8_t pub_b[64],
+				uint8_t dhkey[32])
+{
+	uint8_t dhkey_a[32], dhkey_b[32];
+	int fails = 0;
+
+	ecdh_shared_secret(pub_b, priv_a, dhkey_a);
+	ecdh_shared_secret(pub_a, priv_b, dhkey_b);
+
+	if (g_test_verbose()) {
+		print_buf("DHKey  ", dhkey, 32);
+		print_buf("DHKey A", dhkey_a, 32);
+		print_buf("DHKey B", dhkey_b, 32);
+	}
+
+	if (memcmp(dhkey_a, dhkey, 32)) {
+		tester_debug("DHKey A doesn't match!");
+		fails++;
+	} else {
+		tester_debug("DHKey A matches :)");
+	}
+
+	if (memcmp(dhkey_b, dhkey, 32)) {
+		tester_debug("DHKey B doesn't match!");
+		fails++;
+	} else {
+		tester_debug("DHKey B matches :)");
+	}
+
+	return fails;
+}
+
+static void test_sample_1(const void *data)
+{
+	uint8_t priv_a[32] = {	0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+				0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+				0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+				0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
+	};
+	uint8_t priv_b[32] = {	0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
+				0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
+				0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
+				0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55,
+	};
+	uint8_t pub_a[64] = {	0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+				0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+				0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+				0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+				0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+				0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+				0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+				0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+	};
+	uint8_t pub_b[64] = {	0x90, 0xa1, 0xaa, 0x2f, 0xb2, 0x77, 0x90, 0x55,
+				0x9f, 0xa6, 0x15, 0x86, 0xfd, 0x8a, 0xb5, 0x47,
+				0x00, 0x4c, 0x9e, 0xf1, 0x84, 0x22, 0x59, 0x09,
+				0x96, 0x1d, 0xaf, 0x1f, 0xf0, 0xf0, 0xa1, 0x1e,
+
+				0x4a, 0x21, 0xb1, 0x15, 0xf9, 0xaf, 0x89, 0x5f,
+				0x76, 0x36, 0x8e, 0xe2, 0x30, 0x11, 0x2d, 0x47,
+				0x60, 0x51, 0xb8, 0x9a, 0x3a, 0x70, 0x56, 0x73,
+				0x37, 0xad, 0x9d, 0x42, 0x3e, 0xf3, 0x55, 0x4c,
+	};
+	uint8_t dhkey[32] = {	0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
+				0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
+				0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
+				0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec,
+	};
+	int fails;
+
+	fails = test_sample(priv_a, priv_b, pub_a, pub_b, dhkey);
+
+	g_assert(fails == 0);
+
+	tester_test_passed();
+}
+
+static void test_sample_2(const void *data)
+{
+	uint8_t priv_a[32] = {	0x63, 0x76, 0x45, 0xd0, 0xf7, 0x73, 0xac, 0xb7,
+				0xff, 0xdd, 0x03, 0x72, 0xb9, 0x72, 0x85, 0xb4,
+				0x41, 0xb6, 0x5d, 0x0c, 0x5d, 0x54, 0x84, 0x60,
+				0x1a, 0xa3, 0x9a, 0x3c, 0x69, 0x16, 0xa5, 0x06,
+	};
+	uint8_t priv_b[32] = {	0xba, 0x30, 0x55, 0x50, 0x19, 0xa2, 0xca, 0xa3,
+				0xa5, 0x29, 0x08, 0xc6, 0xb5, 0x03, 0x88, 0x7e,
+				0x03, 0x2b, 0x50, 0x73, 0xd4, 0x2e, 0x50, 0x97,
+				0x64, 0xcd, 0x72, 0x0d, 0x67, 0xa0, 0x9a, 0x52,
+	};
+	uint8_t pub_a[64] = {	0xdd, 0x78, 0x5c, 0x74, 0x03, 0x9b, 0x7e, 0x98,
+				0xcb, 0x94, 0x87, 0x4a, 0xad, 0xfa, 0xf8, 0xd5,
+				0x43, 0x3e, 0x5c, 0xaf, 0xea, 0xb5, 0x4c, 0xf4,
+				0x9e, 0x80, 0x79, 0x57, 0x7b, 0xa4, 0x31, 0x2c,
+
+				0x4f, 0x5d, 0x71, 0x43, 0x77, 0x43, 0xf8, 0xea,
+				0xd4, 0x3e, 0xbd, 0x17, 0x91, 0x10, 0x21, 0xd0,
+				0x1f, 0x87, 0x43, 0x8e, 0x40, 0xe2, 0x52, 0xcd,
+				0xbe, 0xdf, 0x98, 0x38, 0x18, 0x12, 0x95, 0x91,
+	};
+	uint8_t pub_b[64] = {	0xcc, 0x00, 0x65, 0xe1, 0xf5, 0x6c, 0x0d, 0xcf,
+				0xec, 0x96, 0x47, 0x20, 0x66, 0xc9, 0xdb, 0x84,
+				0x81, 0x75, 0xa8, 0x4d, 0xc0, 0xdf, 0xc7, 0x9d,
+				0x1b, 0x3f, 0x3d, 0xf2, 0x3f, 0xe4, 0x65, 0xf4,
+
+				0x79, 0xb2, 0xec, 0xd8, 0xca, 0x55, 0xa1, 0xa8,
+				0x43, 0x4d, 0x6b, 0xca, 0x10, 0xb0, 0xc2, 0x01,
+				0xc2, 0x33, 0x4e, 0x16, 0x24, 0xc4, 0xef, 0xee,
+				0x99, 0xd8, 0xbb, 0xbc, 0x48, 0xd0, 0x01, 0x02,
+	};
+	uint8_t dhkey[32] = {	0x69, 0xeb, 0x21, 0x32, 0xf2, 0xc6, 0x05, 0x41,
+				0x60, 0x19, 0xcd, 0x5e, 0x94, 0xe1, 0xe6, 0x5f,
+				0x33, 0x07, 0xe3, 0x38, 0x4b, 0x68, 0xe5, 0x62,
+				0x3f, 0x88, 0x6d, 0x2f, 0x3a, 0x84, 0x85, 0xab,
+	};
+	int fails;
+
+	fails = test_sample(priv_a, priv_b, pub_a, pub_b, dhkey);
+
+	g_assert(fails == 0);
+
+	tester_test_passed();
+}
+
+static void test_sample_3(const void *data)
+{
+	uint8_t priv_a[32] = {	0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+				0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+				0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+				0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
+	};
+	uint8_t pub_a[64] = {	0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+				0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+				0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+				0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+				0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+				0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+				0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+				0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+	};
+	uint8_t dhkey[32] = {	0x2d, 0xab, 0x00, 0x48, 0xcb, 0xb3, 0x7b, 0xda,
+				0x55, 0x7b, 0x8b, 0x72, 0xa8, 0x57, 0x87, 0xc3,
+				0x87, 0x27, 0x99, 0x32, 0xfc, 0x79, 0x5f, 0xae,
+				0x7c, 0x1c, 0xf9, 0x49, 0xe6, 0xd7, 0xaa, 0x70,
+	};
+	int fails;
+
+	fails = test_sample(priv_a, priv_a, pub_a, pub_a, dhkey);
+
+	g_assert(fails == 0);
+
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/ecdh/multi", NULL, NULL, test_multi, NULL);
+
+	tester_add("/ecdh/sample/1", NULL, NULL, test_sample_1, NULL);
+	tester_add("/ecdh/sample/2", NULL, NULL, test_sample_2, NULL);
+	tester_add("/ecdh/sample/3", NULL, NULL, test_sample_3, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-eir.c b/bluez/repo/unit/test-eir.c
new file mode 100644
index 0000000..421d0db
--- /dev/null
+++ b/bluez/repo/unit/test-eir.c
@@ -0,0 +1,684 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  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 <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "src/shared/tester.h"
+#include "src/shared/util.h"
+#include "src/eir.h"
+
+struct test_data {
+	const void *eir_data;
+	size_t eir_size;
+	unsigned int flags;
+	const char *name;
+	bool name_complete;
+	int8_t tx_power;
+	const char **uuid;
+};
+
+static const unsigned char macbookair_data[] = {
+		0x17, 0x09, 0x4d, 0x61, 0x72, 0x63, 0x65, 0x6c,
+		0xe2, 0x80, 0x99, 0x73, 0x20, 0x4d, 0x61, 0x63,
+		0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x41, 0x69, 0x72,
+		0x11, 0x03, 0x12, 0x11, 0x0c, 0x11, 0x0a, 0x11,
+		0x1f, 0x11, 0x01, 0x11, 0x00, 0x10, 0x0a, 0x11,
+		0x17, 0x11, 0x11, 0xff, 0x4c, 0x00, 0x01, 0x4d,
+		0x61, 0x63, 0x42, 0x6f, 0x6f, 0x6b, 0x41, 0x69,
+		0x72, 0x33, 0x2c, 0x31, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *macbookair_uuid[] = {
+		"00001112-0000-1000-8000-00805f9b34fb",
+		"0000110c-0000-1000-8000-00805f9b34fb",
+		"0000110a-0000-1000-8000-00805f9b34fb",
+		"0000111f-0000-1000-8000-00805f9b34fb",
+		"00001101-0000-1000-8000-00805f9b34fb",
+		"00001000-0000-1000-8000-00805f9b34fb",
+		"0000110a-0000-1000-8000-00805f9b34fb",
+		"00001117-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data macbookair_test = {
+	.eir_data = macbookair_data,
+	.eir_size = sizeof(macbookair_data),
+	.name = "Marcel’s MacBook Air",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = macbookair_uuid,
+};
+
+static const unsigned char iphone5_data[] = {
+		0x14, 0x09, 0x4d, 0x61, 0x72, 0x63, 0x65, 0x6c,
+		0xe2, 0x80, 0x99, 0x73, 0x20, 0x69, 0x50, 0x68,
+		0x6f, 0x6e, 0x65, 0x20, 0x35, 0x0f, 0x03, 0x00,
+		0x12, 0x1f, 0x11, 0x2f, 0x11, 0x0a, 0x11, 0x0c,
+		0x11, 0x16, 0x11, 0x32, 0x11, 0x01, 0x05, 0x11,
+		0x07, 0xfe, 0xca, 0xca, 0xde, 0xaf, 0xde, 0xca,
+		0xde, 0xde, 0xfa, 0xca, 0xde, 0x00, 0x00, 0x00,
+		0x00, 0x27, 0xff, 0x00, 0x4c, 0x02, 0x24, 0x02,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *iphone5_uuid[] = {
+		"00001200-0000-1000-8000-00805f9b34fb",
+		"0000111f-0000-1000-8000-00805f9b34fb",
+		"0000112f-0000-1000-8000-00805f9b34fb",
+		"0000110a-0000-1000-8000-00805f9b34fb",
+		"0000110c-0000-1000-8000-00805f9b34fb",
+		"00001116-0000-1000-8000-00805f9b34fb",
+		"00001132-0000-1000-8000-00805f9b34fb",
+		"00000000-deca-fade-deca-deafdecacafe",
+		NULL
+};
+
+static const struct test_data iphone5_test = {
+	.eir_data = iphone5_data,
+	.eir_size = sizeof(iphone5_data),
+	.name = "Marcel’s iPhone 5",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = iphone5_uuid,
+};
+
+static const unsigned char ipadmini_data[] = {
+		0x13, 0x09, 0x4d, 0x61, 0x72, 0x63, 0x65, 0x6c,
+		0x27, 0x73, 0x20, 0x69, 0x50, 0x61, 0x64, 0x20,
+		0x6d, 0x69, 0x6e, 0x69, 0x0b, 0x03, 0x00, 0x12,
+		0x1f, 0x11, 0x0a, 0x11, 0x0c, 0x11, 0x32, 0x11,
+		0x01, 0x05, 0x11, 0x07, 0xfe, 0xca, 0xca, 0xde,
+		0xaf, 0xde, 0xca, 0xde, 0xde, 0xfa, 0xca, 0xde,
+		0x00, 0x00, 0x00, 0x00, 0x27, 0xff, 0x00, 0x4c,
+		0x02, 0x24, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *ipadmini_uuid[] = {
+		"00001200-0000-1000-8000-00805f9b34fb",
+		"0000111f-0000-1000-8000-00805f9b34fb",
+		"0000110a-0000-1000-8000-00805f9b34fb",
+		"0000110c-0000-1000-8000-00805f9b34fb",
+		"00001132-0000-1000-8000-00805f9b34fb",
+		"00000000-deca-fade-deca-deafdecacafe",
+		NULL
+};
+
+static const struct test_data ipadmini_test = {
+	.eir_data = ipadmini_data,
+	.eir_size = sizeof(ipadmini_data),
+	.name = "Marcel's iPad mini",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = ipadmini_uuid,
+};
+
+static const unsigned char gigaset_sl400h_data[] = {
+		0x0b, 0x03, 0x01, 0x11, 0x05, 0x11, 0x12, 0x11,
+		0x03, 0x12, 0x1f, 0x11, 0x10, 0x09, 0x4d, 0x61,
+		0x72, 0x63, 0x65, 0x6c, 0x27, 0x73, 0x20, 0x53,
+		0x4c, 0x34, 0x30, 0x30, 0x48, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *gigaset_sl400h_uuid[] = {
+		"00001101-0000-1000-8000-00805f9b34fb",
+		"00001105-0000-1000-8000-00805f9b34fb",
+		"00001112-0000-1000-8000-00805f9b34fb",
+		"00001203-0000-1000-8000-00805f9b34fb",
+		"0000111f-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data gigaset_sl400h_test = {
+	.eir_data = gigaset_sl400h_data,
+	.eir_size = sizeof(gigaset_sl400h_data),
+	.name = "Marcel's SL400H",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = gigaset_sl400h_uuid,
+};
+
+static const unsigned char gigaset_sl910_data[] = {
+		0x0b, 0x03, 0x01, 0x11, 0x05, 0x11, 0x12, 0x11,
+		0x03, 0x12, 0x1f, 0x11, 0x0f, 0x09, 0x4d, 0x61,
+		0x72, 0x63, 0x65, 0x6c, 0x27, 0x73, 0x20, 0x53,
+		0x4c, 0x39, 0x31, 0x30, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *gigaset_sl910_uuid[] = {
+		"00001101-0000-1000-8000-00805f9b34fb",
+		"00001105-0000-1000-8000-00805f9b34fb",
+		"00001112-0000-1000-8000-00805f9b34fb",
+		"00001203-0000-1000-8000-00805f9b34fb",
+		"0000111f-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data gigaset_sl910_test = {
+	.eir_data = gigaset_sl910_data,
+	.eir_size = sizeof(gigaset_sl910_data),
+	.name = "Marcel's SL910",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = gigaset_sl910_uuid,
+};
+
+static const unsigned char nokia_bh907_data[] = {
+		0x16, 0x09, 0x4e, 0x6f, 0x6b, 0x69, 0x61, 0x20,
+		0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+		0x20, 0x42, 0x48, 0x2d, 0x39, 0x30, 0x37, 0x02,
+		0x0a, 0x04, 0x0f, 0x02, 0x0d, 0x11, 0x0b, 0x11,
+		0x0e, 0x11, 0x0f, 0x11, 0x1e, 0x11, 0x08, 0x11,
+		0x31, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *nokia_bh907_uuid[] = {
+		"0000110d-0000-1000-8000-00805f9b34fb",
+		"0000110b-0000-1000-8000-00805f9b34fb",
+		"0000110e-0000-1000-8000-00805f9b34fb",
+		"0000110f-0000-1000-8000-00805f9b34fb",
+		"0000111e-0000-1000-8000-00805f9b34fb",
+		"00001108-0000-1000-8000-00805f9b34fb",
+		"00001131-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data nokia_bh907_test = {
+	.eir_data = nokia_bh907_data,
+	.eir_size = sizeof(nokia_bh907_data),
+	.name = "Nokia Reaction BH-907",
+	.name_complete = true,
+	.tx_power = 4,
+	.uuid = nokia_bh907_uuid,
+};
+
+static const unsigned char fuelband_data[] = {
+		0x0f, 0x09, 0x4e, 0x69, 0x6b, 0x65, 0x2b, 0x20,
+		0x46, 0x75, 0x65, 0x6c, 0x42, 0x61, 0x6e, 0x64,
+		0x11, 0x07, 0x00, 0x00, 0x00, 0x00, 0xde, 0xca,
+		0xfa, 0xde, 0xde, 0xca, 0xde, 0xaf, 0xde, 0xca,
+		0xca, 0xff, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const char *fuelband_uuid[] = {
+		"ffcacade-afde-cade-defa-cade00000000",
+		NULL
+};
+
+static const struct test_data fuelband_test = {
+	.eir_data = fuelband_data,
+	.eir_size = sizeof(fuelband_data),
+	.name = "Nike+ FuelBand",
+	.name_complete = true,
+	.tx_power = 0,
+	.uuid = fuelband_uuid,
+};
+
+static const unsigned char bluesc_data[] = {
+		0x02, 0x01, 0x06, 0x03, 0x02, 0x16, 0x18, 0x12,
+		0x09, 0x57, 0x61, 0x68, 0x6f, 0x6f, 0x20, 0x42,
+		0x6c, 0x75, 0x65, 0x53, 0x43, 0x20, 0x76, 0x31,
+		0x2e, 0x34,
+};
+
+static const char *bluesc_uuid[] = {
+		"00001816-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data bluesc_test = {
+	.eir_data = bluesc_data,
+	.eir_size = sizeof(bluesc_data),
+	.flags = 0x06,
+	.name = "Wahoo BlueSC v1.4",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = bluesc_uuid,
+};
+
+static const unsigned char wahoo_scale_data[] = {
+		0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x19, 0x11,
+		0x09, 0x57, 0x61, 0x68, 0x6f, 0x6f, 0x20, 0x53,
+		0x63, 0x61, 0x6c, 0x65, 0x20, 0x76, 0x31, 0x2e,
+		0x33, 0x05, 0xff, 0x00, 0x00, 0x00, 0x9c,
+};
+
+static const char *wahoo_scale_uuid[] = {
+		"00001901-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data wahoo_scale_test = {
+	.eir_data = wahoo_scale_data,
+	.eir_size = sizeof(wahoo_scale_data),
+	.flags = 0x06,
+	.name = "Wahoo Scale v1.3",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = wahoo_scale_uuid,
+};
+
+static const unsigned char mio_alpha_data[] = {
+		0x02, 0x01, 0x06, 0x03, 0x02, 0x0d, 0x18, 0x06,
+		0x09, 0x41, 0x4c, 0x50, 0x48, 0x41,
+};
+
+static const char *mio_alpha_uuid[] = {
+		"0000180d-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data mio_alpha_test = {
+	.eir_data = mio_alpha_data,
+	.eir_size = sizeof(mio_alpha_data),
+	.flags = 0x06,
+	.name = "ALPHA",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = mio_alpha_uuid,
+};
+
+static const unsigned char cookoo_data[] = {
+		0x02, 0x01, 0x05, 0x05, 0x02, 0x02, 0x18, 0x0a,
+		0x18, 0x0d, 0x09, 0x43, 0x4f, 0x4f, 0x4b, 0x4f,
+		0x4f, 0x20, 0x77, 0x61, 0x74, 0x63, 0x68,
+};
+
+static const char *cookoo_uuid[] = {
+		"00001802-0000-1000-8000-00805f9b34fb",
+		"0000180a-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data cookoo_test = {
+	.eir_data = cookoo_data,
+	.eir_size = sizeof(cookoo_data),
+	.flags = 0x05,
+	.name = "COOKOO watch",
+	.name_complete = true,
+	.tx_power = 127,
+	.uuid = cookoo_uuid,
+};
+
+static const unsigned char citizen_adv_data[] = {
+		0x02, 0x01, 0x05, 0x05, 0x12, 0x7f, 0x01, 0x8f,
+		0x01, 0x14, 0x09, 0x45, 0x63, 0x6f, 0x2d, 0x44,
+		0x72, 0x69, 0x76, 0x65, 0x20, 0x50, 0x72, 0x6f,
+		0x78, 0x69, 0x6d, 0x69, 0x74, 0x79,
+};
+
+static const struct test_data citizen_adv_test = {
+	.eir_data = citizen_adv_data,
+	.eir_size = sizeof(citizen_adv_data),
+	.flags = 0x05,
+	.name = "Eco-Drive Proximity",
+	.name_complete = true,
+	.tx_power = 127,
+};
+
+static const unsigned char citizen_scan_data[] = {
+		0x02, 0x0a, 0x00, 0x11, 0x07, 0x1b, 0xc5, 0xd5,
+		0xa5, 0x02, 0x00, 0x46, 0x9a, 0xe1, 0x11, 0xb7,
+		0x8d, 0x60, 0xb4, 0x45, 0x2d,
+};
+
+static const char *citizen_scan_uuid[] = {
+		"2d45b460-8db7-11e1-9a46-0002a5d5c51b",
+		NULL
+};
+
+static const struct test_data citizen_scan_test = {
+	.eir_data = citizen_scan_data,
+	.eir_size = sizeof(citizen_scan_data),
+	.tx_power = 0,
+	.uuid = citizen_scan_uuid,
+};
+
+static void test_basic(const void *data)
+{
+	struct eir_data eir;
+	unsigned char buf[HCI_MAX_EIR_LENGTH];
+
+	memset(buf, 0, sizeof(buf));
+	memset(&eir, 0, sizeof(eir));
+
+	eir_parse(&eir, buf, HCI_MAX_EIR_LENGTH);
+	g_assert(eir.services == NULL);
+	g_assert(eir.name == NULL);
+
+	eir_data_free(&eir);
+
+	tester_test_passed();
+}
+
+static void print_debug(const char *str, void *user_data)
+{
+	char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+static void test_parsing(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct eir_data eir;
+	GSList *list;
+
+	memset(&eir, 0, sizeof(eir));
+
+	eir_parse(&eir, test->eir_data, test->eir_size);
+
+	tester_debug("Flags: %d", eir.flags);
+	tester_debug("Name: %s", eir.name);
+	tester_debug("TX power: %d", eir.tx_power);
+
+	for (list = eir.services; list; list = list->next) {
+		char *uuid_str = list->data;
+
+		tester_debug("UUID: %s", uuid_str);
+	}
+
+	g_assert_cmpint(eir.flags, ==, test->flags);
+
+	if (test->name) {
+		g_assert_cmpstr(eir.name, ==, test->name);
+		g_assert(eir.name_complete == test->name_complete);
+	} else {
+		g_assert(eir.name == NULL);
+	}
+
+	g_assert(eir.tx_power == test->tx_power);
+
+	if (test->uuid) {
+		GSList *list;
+		int n = 0;
+
+		for (list = eir.services; list; list = list->next, n++) {
+			char *uuid_str = list->data;
+			g_assert(test->uuid[n]);
+			g_assert_cmpstr(test->uuid[n], ==, uuid_str);
+		}
+	} else {
+		g_assert(eir.services == NULL);
+	}
+
+	for (list = eir.msd_list; list; list = list->next) {
+		struct eir_msd *msd = list->data;
+
+		tester_debug("Manufacturer ID: 0x%04x", msd->company);
+		util_hexdump(' ', msd->data, msd->data_len, print_debug,
+							"Manufacturer Data:");
+	}
+
+	for (list = eir.sd_list; list; list = list->next) {
+		struct eir_sd *sd = list->data;
+
+		tester_debug("Service UUID: %s", sd->uuid);
+		util_hexdump(' ', sd->data, sd->data_len, print_debug,
+							"Service Data:");
+	}
+
+	eir_data_free(&eir);
+
+	tester_test_passed();
+}
+
+static const unsigned char gigaset_gtag_data[] = {
+		0x02, 0x01, 0x06, 0x0d, 0xff, 0x80, 0x01, 0x02,
+		0x15, 0x12, 0x34, 0x80, 0x91, 0xd0, 0xf2, 0xbb,
+		0xc5, 0x03, 0x02, 0x0f, 0x18, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const char *gigaset_gtag_uuid[] = {
+		"0000180f-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const struct test_data gigaset_gtag_test = {
+	.eir_data = gigaset_gtag_data,
+	.eir_size = sizeof(gigaset_gtag_data),
+	.flags = 0x06,
+	.tx_power = 127,
+	.uuid = gigaset_gtag_uuid,
+};
+
+static const char *uri_beacon_uuid[] = {
+		"0000fed8-0000-1000-8000-00805f9b34fb",
+		NULL
+};
+
+static const unsigned char uri_beacon_data[] = {
+		0x03, 0x03, 0xd8, 0xfe, 0x0c, 0x16, 0xd8, 0xfe, 0x00,
+		0x20, 0x00, 'b', 'l', 'u', 'e', 'z', 0x08
+};
+
+static const struct test_data uri_beacon_test = {
+	.eir_data = uri_beacon_data,
+	.eir_size = sizeof(uri_beacon_data),
+	.tx_power = 127,
+	.uuid = uri_beacon_uuid,
+};
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/eir/basic", NULL, NULL, test_basic, NULL);
+
+	tester_add("/eir/macbookair", &macbookair_test, NULL, test_parsing,
+									NULL);
+	tester_add("/eir/iphone5", &iphone5_test, NULL, test_parsing, NULL);
+	tester_add("/eir/ipadmini", &ipadmini_test, NULL, test_parsing, NULL);
+	tester_add("/eir/sl400h", &gigaset_sl400h_test, NULL, test_parsing,
+									NULL);
+	tester_add("/eir/sl910", &gigaset_sl910_test, NULL, test_parsing, NULL);
+	tester_add("/eir/bh907", &nokia_bh907_test, NULL, test_parsing, NULL);
+	tester_add("/eir/fuelband", &fuelband_test, NULL, test_parsing, NULL);
+	tester_add("/ad/bluesc", &bluesc_test, NULL, test_parsing, NULL);
+	tester_add("/ad/wahooscale", &wahoo_scale_test, NULL, test_parsing,
+									NULL);
+	tester_add("/ad/mioalpha", &mio_alpha_test, NULL, test_parsing, NULL);
+	tester_add("/ad/cookoo", &cookoo_test, NULL, test_parsing, NULL);
+	tester_add("/ad/citizen1", &citizen_adv_test, NULL, test_parsing, NULL);
+	tester_add("/ad/citizen2", &citizen_scan_test, NULL, test_parsing,
+									NULL);
+	tester_add("ad/g-tag", &gigaset_gtag_test, NULL, test_parsing, NULL);
+	tester_add("ad/uri-beacon", &uri_beacon_test, NULL, test_parsing, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-gatt.c b/bluez/repo/unit/test-gatt.c
new file mode 100644
index 0000000..326a32c
--- /dev/null
+++ b/bluez/repo/unit/test-gatt.c
@@ -0,0 +1,4429 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-helpers.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/tester.h"
+
+struct test_pdu {
+	bool valid;
+	const uint8_t *data;
+	size_t size;
+};
+
+enum context_type {
+	ATT,
+	CLIENT,
+	SERVER
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+	enum context_type context_type;
+	bt_uuid_t *uuid;
+	struct gatt_db *source_db;
+	const void *step;
+};
+
+struct context {
+	struct bt_gatt_client *client;
+	struct bt_gatt_server *server;
+	struct bt_att *att;
+	struct gatt_db *client_db;
+	struct gatt_db *server_db;
+	guint source;
+	guint process;
+	int fd;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+	struct bt_gatt_request *req;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define define_test(name, function, type, bt_uuid, db,			\
+		test_step, args...)					\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.context_type = type;				\
+		data.uuid = bt_uuid;					\
+		data.step = test_step;					\
+		data.source_db = db;					\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, function, NULL);		\
+	} while (0)
+
+#define define_test_att(name, function, bt_uuid, test_step, args...)	\
+	define_test(name, function, ATT, bt_uuid, NULL, test_step, args)
+
+#define define_test_client(name, function, source_db, test_step, args...)\
+	define_test(name, function, CLIENT, NULL, source_db, test_step, args)
+
+#define define_test_server(name, function, source_db, test_step, args...)\
+	define_test(name, function, SERVER, NULL, source_db, test_step, args)
+
+#define MTU_EXCHANGE_CLIENT_PDUS					\
+		raw_pdu(0x02, 0x00, 0x02),				\
+		raw_pdu(0x03, 0x00, 0x02)
+
+#define SERVICE_DATA_1_PDUS						\
+		MTU_EXCHANGE_CLIENT_PDUS,				\
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
+		raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),\
+		raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		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(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(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(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, 0x08, 0x00, 0x08, 0x00),			\
+		raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
+
+#define SERVICE_DATA_2_PDUS						\
+		MTU_EXCHANGE_CLIENT_PDUS,				\
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
+		raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x18),\
+		raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		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(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(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(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, 0x09, 0x00, 0x0a, 0x00),			\
+		raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
+
+#define SERVICE_DATA_3_PDUS						\
+		MTU_EXCHANGE_CLIENT_PDUS,				\
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x00, 0x01, 0x21, 0x01, 0x00, 0x18, \
+			0x00, 0x02, 0x00, 0x02, 0x01, 0x18),		\
+		raw_pdu(0x10, 0x01, 0x02, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x00, 0x03, 0x20, 0x03, 0x0d, 0x18),\
+		raw_pdu(0x10, 0x21, 0x03, 0xff, 0xff, 0x00, 0x28),	\
+		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(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(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(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, 0x03, 0x20, 0x03),			\
+		raw_pdu(0x05, 0x01, 0x20, 0x03, 0x02, 0x29)
+
+#define PRIMARY_DISC_SMALL_DB						\
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x17, 0xF0, 0x00, 0x18,	\
+				0xFF, 0xFF, 0xFF, 0xFF, 0x0a, 0x18)
+
+#define PRIMARY_DISC_LARGE_DB_1						\
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x01, 0x18,	\
+			0x20, 0x00, 0x29, 0x00, 0x0A, 0xA0,		\
+			0x30, 0x00, 0x32, 0x00, 0x0B, 0xA0),		\
+		raw_pdu(0x10, 0x33, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x40, 0x00, 0x46, 0x00, 0x00, 0x18,	\
+			0x50, 0x00, 0x52, 0x00, 0x0B, 0xA0,		\
+			0x60, 0x00, 0x6B, 0x00, 0x0B, 0xA0),		\
+		raw_pdu(0x10, 0x6C, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x70, 0x00, 0x76, 0x00, 0x0B, 0xA0,	\
+			0x80, 0x00, 0x85, 0x00, 0x0B, 0xA0),		\
+		raw_pdu(0x10, 0x86, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x14, 0x90, 0x00, 0x96, 0x00,		\
+			0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,	\
+			0x00, 0x00, 0x00, 0x00, 0x0C, 0xA0, 0x00, 0x00),\
+		raw_pdu(0x10, 0x97, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x06, 0xa0, 0x00, 0xb1, 0x00, 0x0f, 0xa0),\
+		raw_pdu(0x10, 0xb2, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x11, 0x14, 0xC0, 0x00, 0xDD, 0x00,		\
+			0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,	\
+			0x00, 0x00, 0x00, 0x00, 0x0C, 0xA0, 0x00, 0x00),\
+		raw_pdu(0x10, 0xde, 0x00, 0xff, 0xff, 0x00, 0x28),	\
+		raw_pdu(0x01, 0x10, 0xde, 0x00, 0x0a)
+
+#define SECONDARY_DISC_SMALL_DB						\
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),	\
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x0a, 0x18),\
+		raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x01, 0x28),	\
+		raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a)
+
+#define INCLUDE_DISC_SMALL_DB						\
+		raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x02, 0x28),	\
+		raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x0f, 0x00,	\
+			0x0a, 0x18),					\
+		raw_pdu(0x08, 0x12, 0xf0, 0x17, 0xf0, 0x02, 0x28),	\
+		raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a),			\
+		raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),	\
+		raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a)
+
+#define CHARACTERISTIC_DISC_SMALL_DB					\
+		raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28),	\
+		raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,	\
+			0x2a),						\
+		raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),	\
+		raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,	\
+			0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,	\
+			0x00, 0x00, 0x00, 0x09, 0xB0, 0x00, 0x00),	\
+		raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28),	\
+		raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01,	\
+			0x2a),						\
+		raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28),	\
+		raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a),			\
+		raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),	\
+		raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,	\
+			0x2a),						\
+		raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),	\
+		raw_pdu(0x01, 0x08, 0x03, 0x00, 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)
+
+#define SMALL_DB_DISCOVERY_PDUS						\
+		PRIMARY_DISC_SMALL_DB,					\
+		SECONDARY_DISC_SMALL_DB,				\
+		INCLUDE_DISC_SMALL_DB,					\
+		CHARACTERISTIC_DISC_SMALL_DB,				\
+		DESCRIPTOR_DISC_SMALL_DB
+
+
+#define SERVER_MTU_EXCHANGE_PDU raw_pdu(0x02, 0x17, 0x00)
+
+static bt_uuid_t uuid_16 = {
+	.type = BT_UUID16,
+	.value.u16 = 0x1800
+};
+
+static bt_uuid_t uuid_char_16 = {
+	.type = BT_UUID16,
+	.value.u16 = 0x2a0d
+};
+
+static bt_uuid_t uuid_128 = {
+	.type = BT_UUID128,
+	.value.u128.data = {0x00, 0x00, 0x18, 0x0d, 0x00, 0x00, 0x10, 0x00,
+				0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
+};
+
+static bt_uuid_t uuid_char_128 = {
+	.type = BT_UUID128,
+	.value.u128.data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
+};
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+typedef void (*test_step_t)(struct context *context);
+
+struct test_step {
+	test_step_t func;
+	test_step_t post_func;
+	uint16_t handle;
+	uint16_t end_handle;
+	uint8_t uuid[16];
+	uint8_t expected_att_ecode;
+	const uint8_t *value;
+	uint16_t length;
+};
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	if (context->req)
+		bt_gatt_request_unref(context->req);
+
+	bt_gatt_client_unref(context->client);
+	bt_gatt_server_unref(context->server);
+	gatt_db_unref(context->client_db);
+	gatt_db_unref(context->server_db);
+
+	if (context->att)
+		bt_att_unref(context->att);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	if (step && step->post_func)
+		step->post_func(context);
+
+	if (context->data->pdu_list[context->pdu_offset].valid)
+		tester_test_abort();
+	else
+		tester_test_passed();
+
+	destroy_context(context);
+
+	return FALSE;
+}
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+	util_hexdump('<', pdu->data, len, test_debug, "GATT: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context->process = 0;
+
+	pdu = &context->data->pdu_list[context->pdu_offset];
+	if (pdu->valid && (pdu->size == 0)) {
+		test_debug("(no action expected)", "GATT: ");
+		context->pdu_offset++;
+		return send_pdu(context);
+	}
+
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	/* Quit the context if we processed the last PDU */
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+	const struct test_pdu *pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		g_print("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	util_hexdump('>', buf, len, test_debug, "GATT: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	/* Empty client PDU means to trigger something out-of-band. */
+	pdu = &context->data->pdu_list[context->pdu_offset];
+
+	if (pdu->valid && (pdu->size == 0)) {
+		context->pdu_offset++;
+		test_debug("triggering server action", "Empty client pdu: ");
+		g_assert(step && step->func);
+		step->func(context);
+		return TRUE;
+	}
+
+	context_process(context);
+
+	return TRUE;
+}
+
+static void print_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+struct db_attribute_test_data {
+	struct gatt_db_attribute *match;
+	bool found;
+};
+
+static bool matching_desc_data(struct gatt_db_attribute *a,
+						struct gatt_db_attribute *b)
+{
+	uint16_t a_handle, b_handle;
+	const bt_uuid_t *a_uuid, *b_uuid;
+
+	a_handle = gatt_db_attribute_get_handle(a);
+	b_handle = gatt_db_attribute_get_handle(b);
+
+	a_uuid = gatt_db_attribute_get_type(a);
+	b_uuid = gatt_db_attribute_get_type(b);
+
+	return a_handle == b_handle && !bt_uuid_cmp(a_uuid, b_uuid);
+}
+
+static void find_matching_desc(struct gatt_db_attribute *source_desc_attr,
+								void *user_data)
+{
+	struct db_attribute_test_data *desc_test_data = user_data;
+
+	if (desc_test_data->found)
+		return;
+
+	desc_test_data->found = matching_desc_data(desc_test_data->match,
+							source_desc_attr);
+}
+
+static void match_descs(struct gatt_db_attribute *client_desc_attr,
+								void *user_data)
+{
+	struct gatt_db_attribute *source_char_attr = user_data;
+	struct db_attribute_test_data desc_test_data;
+
+	desc_test_data.match = client_desc_attr;
+	desc_test_data.found = false;
+
+	gatt_db_service_foreach_desc(source_char_attr, find_matching_desc,
+							&desc_test_data);
+
+	g_assert(desc_test_data.found);
+}
+
+static bool matching_char_data(struct gatt_db_attribute *a,
+						struct gatt_db_attribute *b)
+{
+	uint16_t a_handle, b_handle, a_value_handle, b_value_handle;
+	uint8_t a_properties, b_properties;
+	bt_uuid_t a_uuid, b_uuid;
+
+	gatt_db_attribute_get_char_data(a, &a_handle, &a_value_handle,
+							&a_properties, &a_uuid);
+	gatt_db_attribute_get_char_data(b, &b_handle, &b_value_handle,
+							&b_properties, &b_uuid);
+
+	return a_handle == b_handle && a_value_handle == b_value_handle &&
+						a_properties == b_properties &&
+						!bt_uuid_cmp(&a_uuid, &b_uuid);
+}
+
+static void find_matching_char(struct gatt_db_attribute *source_char_attr,
+								void *user_data)
+{
+	struct db_attribute_test_data *char_test_data = user_data;
+
+	if (char_test_data->found)
+		return;
+
+	if (matching_char_data(char_test_data->match, source_char_attr)) {
+
+		gatt_db_service_foreach_desc(char_test_data->match, match_descs,
+							source_char_attr);
+		char_test_data->found = true;
+	}
+}
+
+static void match_chars(struct gatt_db_attribute *client_char_attr,
+								void *user_data)
+{
+	struct gatt_db_attribute *source_serv_attr = user_data;
+	struct db_attribute_test_data char_test_data;
+
+	char_test_data.match = client_char_attr;
+	char_test_data.found = false;
+
+	gatt_db_service_foreach_char(source_serv_attr, find_matching_char,
+							&char_test_data);
+
+	g_assert(char_test_data.found);
+}
+
+static bool matching_service_data(struct gatt_db_attribute *a,
+						struct gatt_db_attribute *b)
+{
+	uint16_t a_start, b_start, a_end, b_end;
+	bool a_primary, b_primary;
+	bt_uuid_t a_uuid, b_uuid;
+
+	gatt_db_attribute_get_service_data(a, &a_start, &a_end, &a_primary,
+								&a_uuid);
+	gatt_db_attribute_get_service_data(b, &b_start, &b_end, &b_primary,
+								&b_uuid);
+
+	return a_start == b_start && a_end == b_end && a_primary == b_primary &&
+						!bt_uuid_cmp(&a_uuid, &b_uuid);
+}
+
+static void find_matching_service(struct gatt_db_attribute *source_serv_attr,
+								void *user_data)
+{
+	struct db_attribute_test_data *serv_test_data = user_data;
+
+	if (serv_test_data->found)
+		return;
+
+	if (matching_service_data(serv_test_data->match, source_serv_attr)) {
+		gatt_db_service_foreach_char(serv_test_data->match, match_chars,
+							source_serv_attr);
+		serv_test_data->found = true;
+	}
+}
+
+static void match_services(struct gatt_db_attribute *client_serv_attr,
+								void *user_data)
+{
+	struct gatt_db *source_db = user_data;
+	struct db_attribute_test_data serv_test_data;
+
+	serv_test_data.match = client_serv_attr;
+	serv_test_data.found = false;
+
+	gatt_db_foreach_service(source_db, NULL,
+					find_matching_service, &serv_test_data);
+
+	g_assert(serv_test_data.found);
+}
+
+static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert(success);
+
+	if (!context->data->source_db) {
+		context_quit(context);
+		return;
+	}
+
+	g_assert(context->client);
+	g_assert(context->client_db);
+
+	gatt_db_foreach_service(context->client_db, NULL, match_services,
+						context->data->source_db);
+
+	if (context->data->step) {
+		const struct test_step *step = context->data->step;
+
+		/* Auto elevate security for test that don't expect error */
+		if (!step->expected_att_ecode)
+			bt_att_set_security(context->att, BT_ATT_SECURITY_AUTO);
+
+		step->func(context);
+		return;
+	}
+
+	context_quit(context);
+}
+
+static struct context *create_context(uint16_t mtu, gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	const struct test_data *test_data = data;
+	GIOChannel *channel;
+	int err, sv[2];
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->att = bt_att_new(sv[0], false);
+	g_assert(context->att);
+
+	switch (test_data->context_type) {
+	case ATT:
+		bt_att_set_debug(context->att, print_debug, "bt_att:", NULL);
+
+		bt_gatt_exchange_mtu(context->att, mtu, NULL, NULL, NULL);
+		break;
+	case SERVER:
+		context->server_db = gatt_db_ref(test_data->source_db);
+		g_assert(context->server_db);
+
+		context->server = bt_gatt_server_new(context->server_db,
+							context->att, mtu);
+		g_assert(context->server);
+
+		bt_gatt_server_set_debug(context->server, print_debug,
+						"bt_gatt_server:", NULL);
+		break;
+	case CLIENT:
+		context->client_db = gatt_db_new();
+		g_assert(context->client_db);
+
+		context->client = bt_gatt_client_new(context->client_db,
+							context->att, mtu);
+		g_assert(context->client);
+
+		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);
+		break;
+	default:
+		break;
+	}
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	return context;
+}
+
+static void generic_search_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct context *context = user_data;
+
+	bt_gatt_request_unref(context->req);
+	context->req = NULL;
+
+	g_assert(success);
+
+	context_quit(context);
+}
+
+static void test_read_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	g_assert(att_ecode == step->expected_att_ecode);
+
+	if (success) {
+		g_assert(length == step->length);
+		g_assert(memcmp(value, step->value, length) == 0);
+	}
+
+	context_quit(context);
+}
+
+static void test_read(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_read_value(context->client, step->handle,
+						test_read_cb, context, NULL));
+}
+
+static const uint8_t read_data_1[] = {0x01, 0x02, 0x03};
+
+static const struct test_step test_read_1 = {
+	.handle = 0x0003,
+	.func = test_read,
+	.expected_att_ecode = 0,
+	.value = read_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_read_2 = {
+	.handle = 0x0000,
+	.func = test_read,
+	.expected_att_ecode = 0x01,
+};
+
+static const struct test_step test_read_3 = {
+	.handle = 0x0003,
+	.func = test_read,
+	.expected_att_ecode = 0x02,
+};
+
+static const struct test_step test_read_4 = {
+	.handle = 0x0003,
+	.func = test_read,
+	.expected_att_ecode = 0x08,
+};
+
+static const struct test_step test_read_5 = {
+	.handle = 0x0003,
+	.func = test_read,
+	.expected_att_ecode = 0x05,
+};
+
+static const struct test_step test_read_6 = {
+	.handle = 0x0003,
+	.func = test_read,
+	.expected_att_ecode = 0x0c,
+};
+
+static const struct test_step test_read_7 = {
+	.handle = 0x0004,
+	.func = test_read,
+	.expected_att_ecode = 0x00,
+	.value = read_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_read_8 = {
+	.handle = 0x0004,
+	.func = test_read,
+	.expected_att_ecode = 0x02,
+};
+
+static const struct test_step test_read_9 = {
+	.handle = 0x0004,
+	.func = test_read,
+	.expected_att_ecode = 0x08,
+};
+
+static const struct test_step test_read_10 = {
+	.handle = 0x0004,
+	.func = test_read,
+	.expected_att_ecode = 0x05,
+};
+
+static const struct test_step test_read_11 = {
+	.handle = 0x0004,
+	.func = test_read,
+	.expected_att_ecode = 0x0c,
+};
+
+static const struct test_step test_read_12 = {
+	.handle = 0x0003,
+	.func = test_read,
+	.expected_att_ecode = 0x80,
+};
+
+static void test_write_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	g_assert(att_ecode == step->expected_att_ecode);
+
+	context_quit(context);
+}
+
+static void test_write(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_write_value(context->client, step->handle,
+				step->value, step->length, test_write_cb,
+				context, NULL));
+}
+
+static const uint8_t write_data_1[] = {0x01, 0x02, 0x03};
+
+static const struct test_step test_write_1 = {
+	.handle = 0x0007,
+	.func = test_write,
+	.expected_att_ecode = 0,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_2 = {
+	.handle = 0x0000,
+	.func = test_write,
+	.expected_att_ecode = 0x01,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_3 = {
+	.handle = 0x0007,
+	.func = test_write,
+	.expected_att_ecode = 0x03,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_4 = {
+	.handle = 0x0007,
+	.func = test_write,
+	.expected_att_ecode = 0x08,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_5 = {
+	.handle = 0x0007,
+	.func = test_write,
+	.expected_att_ecode = 0x05,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_6 = {
+	.handle = 0x0007,
+	.func = test_write,
+	.expected_att_ecode = 0x0c,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_7 = {
+	.handle = 0x0008,
+	.func = test_write,
+	.expected_att_ecode = 0,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_8 = {
+	.handle = 0x0000,
+	.func = test_write,
+	.expected_att_ecode = 0x01,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_9 = {
+	.handle = 0x0008,
+	.func = test_write,
+	.expected_att_ecode = 0x03,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_10 = {
+	.handle = 0x0008,
+	.func = test_write,
+	.expected_att_ecode = 0x08,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_11 = {
+	.handle = 0x0008,
+	.func = test_write,
+	.expected_att_ecode = 0x05,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_write_12 = {
+	.handle = 0x0008,
+	.func = test_write,
+	.expected_att_ecode = 0x0c,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static void test_write_without_response(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_write_without_response(context->client,
+							step->handle,
+							false, step->value,
+							step->length));
+}
+
+static const struct test_step test_write_without_response_1 = {
+	.handle = 0x0007,
+	.func = test_write_without_response,
+	.expected_att_ecode = 0,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static bool local_counter(uint32_t *sign_cnt, void *user_data)
+{
+	static uint32_t cnt = 0;
+
+	*sign_cnt = cnt++;
+
+	return true;
+}
+
+static void test_signed_write(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+	uint8_t key[16] = {0xD8, 0x51, 0x59, 0x48, 0x45, 0x1F, 0xEA, 0x32, 0x0D,
+				0xC0, 0x5A, 0x2E, 0x88, 0x30, 0x81, 0x88 };
+
+	if (!bt_att_has_crypto(context->att)) {
+		context_quit(context);
+		return;
+	}
+
+	g_assert(bt_att_set_local_key(context->att, key, local_counter,
+								context));
+
+	g_assert(bt_gatt_client_write_without_response(context->client,
+							step->handle,
+							true, step->value,
+							step->length));
+}
+
+static const struct test_step test_signed_write_1 = {
+	.handle = 0x0007,
+	.func = test_signed_write,
+	.expected_att_ecode = 0,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static void test_signed_write_seclevel(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+	uint8_t key[16] = {0xD8, 0x51, 0x59, 0x48, 0x45, 0x1F, 0xEA, 0x32, 0x0D,
+				0xC0, 0x5A, 0x2E, 0x88, 0x30, 0x81, 0x88 };
+
+	g_assert(bt_att_set_local_key(context->att, key, local_counter,
+								context));
+
+	g_assert(bt_att_set_security(context->att, BT_ATT_SECURITY_MEDIUM));
+
+	g_assert(bt_gatt_client_write_without_response(context->client,
+							step->handle,
+							true, step->value,
+							step->length));
+}
+
+static const struct test_step test_signed_write_seclevel_1 = {
+	.handle = 0x0007,
+	.func = test_signed_write_seclevel,
+	.expected_att_ecode = 0,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static void test_long_write_cb(bool success, bool reliable_error,
+					uint8_t att_ecode, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	g_assert(att_ecode == step->expected_att_ecode);
+
+	context_quit(context);
+}
+
+static void test_long_write(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_write_long_value(context->client, false,
+				step->handle, 0, step->value,
+				step->length, test_long_write_cb,
+				context, NULL));
+}
+
+/* The maximum length of an attribute value shall be 512 octets. */
+static const uint8_t long_data_2[512] = { [0 ... 511] = 0xff };
+
+static const struct test_step test_long_write_1 = {
+	.handle = 0x0007,
+	.func = test_long_write,
+	.expected_att_ecode = 0,
+	.value = long_data_2,
+	.length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_write_2 = {
+	.handle = 0x0000,
+	.func = test_long_write,
+	.expected_att_ecode = 0x01,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_3 = {
+	.handle = 0x0003,
+	.func = test_long_write,
+	.expected_att_ecode = 0x03,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_4 = {
+	.handle = 0x0007,
+	.func = test_long_write,
+	.expected_att_ecode = 0x08,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_5 = {
+	.handle = 0x0007,
+	.func = test_long_write,
+	.expected_att_ecode = 0x05,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_6 = {
+	.handle = 0x0007,
+	.func = test_long_write,
+	.expected_att_ecode = 0x0c,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_7 = {
+	.handle = 0x0008,
+	.func = test_long_write,
+	.expected_att_ecode = 0,
+	.value = long_data_2,
+	.length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_write_8 = {
+	.handle = 0x0000,
+	.func = test_long_write,
+	.expected_att_ecode = 0x01,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_9 = {
+	.handle = 0x0008,
+	.func = test_long_write,
+	.expected_att_ecode = 0x03,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_10 = {
+	.handle = 0x0008,
+	.func = test_long_write,
+	.expected_att_ecode = 0x08,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_11 = {
+	.handle = 0x0008,
+	.func = test_long_write,
+	.expected_att_ecode = 0x05,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_write_12 = {
+	.handle = 0x0008,
+	.func = test_long_write,
+	.expected_att_ecode = 0x0c,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static void test_reliable_write_cb(bool success, bool reliable_error,
+					uint8_t att_ecode, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	g_assert(att_ecode == step->expected_att_ecode);
+
+	g_assert(!reliable_error);
+
+	context_quit(context);
+}
+
+static void test_reliable_write(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_write_long_value(context->client, true,
+				step->handle, 0, step->value,
+				step->length, test_reliable_write_cb,
+				context, NULL));
+}
+
+static const struct test_step test_reliable_write_1 = {
+	.handle = 0x0007,
+	.func = test_reliable_write,
+	.expected_att_ecode = 0,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_reliable_write_2 = {
+	.handle = 0x0000,
+	.func = test_reliable_write,
+	.expected_att_ecode = 0x01,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_reliable_write_3 = {
+	.handle = 0x0003,
+	.func = test_reliable_write,
+	.expected_att_ecode = 0x03,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_reliable_write_4 = {
+	.handle = 0x0007,
+	.func = test_reliable_write,
+	.expected_att_ecode = 0x08,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_reliable_write_5 = {
+	.handle = 0x0007,
+	.func = test_reliable_write,
+	.expected_att_ecode = 0x05,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_reliable_write_6 = {
+	.handle = 0x0007,
+	.func = test_reliable_write,
+	.expected_att_ecode = 0x0c,
+	.value = write_data_1,
+	.length = 0x03
+};
+
+static void att_write_cb(struct gatt_db_attribute *att, int err,
+								void *user_data)
+{
+	g_assert(!err);
+}
+
+static struct gatt_db_attribute *
+add_char_with_value(struct gatt_db_attribute *service_att, uint16_t handle,
+				bt_uuid_t *uuid, uint32_t att_permissions,
+				uint8_t char_properties, const void *value,
+				size_t len)
+{
+	struct gatt_db_attribute *attrib;
+
+	if (handle)
+		attrib = gatt_db_service_insert_characteristic(service_att,
+								handle, uuid,
+								att_permissions,
+								char_properties,
+								NULL, NULL,
+								NULL);
+	else
+		attrib = gatt_db_service_add_characteristic(service_att, uuid,
+								att_permissions,
+								char_properties,
+								NULL, NULL,
+								NULL);
+
+	g_assert(attrib != NULL);
+
+	gatt_db_attribute_write(attrib, 0, value, len, 0x00, NULL,
+							att_write_cb, NULL);
+
+	return attrib;
+}
+
+static struct gatt_db_attribute *
+add_desc_with_value(struct gatt_db_attribute *att, uint16_t handle,
+					bt_uuid_t *uuid, uint32_t att_perms,
+					const uint8_t *value, size_t len)
+{
+	struct gatt_db_attribute *desc_att;
+
+	if (handle)
+		desc_att = gatt_db_service_insert_descriptor(att, handle, uuid,
+							att_perms, NULL, NULL,
+							NULL);
+	else
+		desc_att = gatt_db_service_add_descriptor(att, uuid, att_perms,
+							NULL, NULL, NULL);
+
+	gatt_db_attribute_write(desc_att, 0, value, len, 0x00, NULL,
+							att_write_cb, NULL);
+
+	return desc_att;
+}
+
+enum gatt_type {
+	PRIMARY,
+	SECONDARY,
+	INCLUDE,
+	CHARACTERISTIC,
+	DESCRIPTOR
+};
+
+struct att_handle_spec {
+	uint16_t handle;
+	const char *uuid;
+	enum gatt_type type;
+	uint8_t char_properties;
+	uint32_t att_permissions;
+	const uint8_t *value;
+	size_t len;
+	bool valid;
+};
+
+#define PRIMARY_SERVICE(start_handle, srv_uuid, num_handles)	\
+	{							\
+		.valid = true,					\
+		.handle = start_handle,				\
+		.type = PRIMARY,				\
+		.uuid = srv_uuid,				\
+		.len = num_handles,				\
+	}
+
+#define SECONDARY_SERVICE(start_handle, srv_uuid, num_handles)	\
+	{							\
+		.valid = true,					\
+		.handle = start_handle,				\
+		.type = SECONDARY,				\
+		.uuid = srv_uuid,				\
+		.len = num_handles,				\
+	}
+
+#define INCLUDE(include_handle)				\
+	{						\
+		.valid = true,				\
+		.type = INCLUDE,			\
+		.handle = include_handle,		\
+	}
+
+#define STR(x) #x
+
+#define CHARACTERISTIC(chr_uuid, permissions, properties, bytes...)	\
+	{								\
+		.valid = true,						\
+		.type = CHARACTERISTIC,					\
+		.uuid = STR(chr_uuid),					\
+		.att_permissions = permissions,				\
+		.char_properties = properties,				\
+		.value = data(bytes),					\
+		.len = sizeof(data(bytes)),				\
+	}
+
+#define CHARACTERISTIC_STR(chr_uuid, permissions, properties, string)	\
+		{							\
+		.valid = true,						\
+		.type = CHARACTERISTIC,					\
+		.uuid = STR(chr_uuid),					\
+		.att_permissions = permissions,				\
+		.char_properties = properties,				\
+		.value = (uint8_t *)string,				\
+		.len = strlen(string),					\
+	}
+
+#define DESCRIPTOR(desc_uuid, permissions, bytes...)	\
+	{						\
+		.valid = true,				\
+		.type = DESCRIPTOR,			\
+		.uuid = STR(desc_uuid),			\
+		.att_permissions = permissions,		\
+		.value = data(bytes),			\
+		.len = sizeof(data(bytes)),		\
+	}
+
+#define DESCRIPTOR_STR(desc_uuid, permissions, string)	\
+	{						\
+		.valid = true,				\
+		.type = DESCRIPTOR,			\
+		.uuid = STR(desc_uuid),			\
+		.att_permissions = permissions,		\
+		.value = (uint8_t *)string,		\
+		.len = strlen(string),			\
+	}
+
+
+static struct gatt_db *make_db(const struct att_handle_spec *spec)
+{
+	struct gatt_db *db = gatt_db_new();
+	struct gatt_db_attribute *att, *include_att;
+	bt_uuid_t uuid;
+
+	att = include_att = NULL;
+
+	for (; spec->valid; spec++) {
+		switch (spec->type) {
+		case PRIMARY:
+		case SECONDARY:
+			bt_string_to_uuid(&uuid, spec->uuid);
+
+			if (att)
+				gatt_db_service_set_active(att, true);
+
+			att = gatt_db_insert_service(db, spec->handle, &uuid,
+					spec->type == PRIMARY, spec->len);
+			break;
+
+		case INCLUDE:
+			include_att = gatt_db_get_attribute(db, spec->handle);
+
+			gatt_db_service_add_included(att, include_att);
+			break;
+
+		case CHARACTERISTIC:
+			bt_string_to_uuid(&uuid, spec->uuid);
+
+			add_char_with_value(att, spec->handle, &uuid,
+							spec->att_permissions,
+							spec->char_properties,
+							spec->value, spec->len);
+
+			break;
+
+		case DESCRIPTOR:
+			bt_string_to_uuid(&uuid, spec->uuid);
+
+			add_desc_with_value(att, spec->handle, &uuid,
+							spec->att_permissions,
+							spec->value, spec->len);
+
+			break;
+		};
+	}
+
+	if (att)
+		gatt_db_service_set_active(att, true);
+
+	return db;
+}
+
+static struct gatt_db *make_service_data_1_db(void)
+{
+	const struct att_handle_spec specs[] = {
+		PRIMARY_SERVICE(0x0001, GATT_UUID, 4),
+		CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ, "BlueZ"),
+		DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+								"Device Name"),
+		PRIMARY_SERVICE(0x0005, HEART_RATE_UUID, 4),
+		CHARACTERISTIC_STR(GATT_CHARAC_MANUFACTURER_NAME_STRING,
+						BT_ATT_PERM_READ,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE, ""),
+		DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+							"Manufacturer Name"),
+		{ }
+	};
+
+	return make_db(specs);
+}
+
+#define CHARACTERISTIC_STR_AT(chr_handle, chr_uuid, permissions, properties, \
+								string) \
+	{								\
+		.valid = true,						\
+		.handle = chr_handle,					\
+		.type = CHARACTERISTIC,					\
+		.uuid = STR(chr_uuid),					\
+		.att_permissions = permissions,				\
+		.char_properties = properties,				\
+		.value = (uint8_t *)string,				\
+		.len = strlen(string),					\
+	}
+
+#define DESCRIPTOR_STR_AT(desc_handle, desc_uuid, permissions, string)	\
+	{								\
+		.valid = true,						\
+		.handle = desc_handle,					\
+		.type = DESCRIPTOR,					\
+		.uuid = STR(desc_uuid),					\
+		.att_permissions = permissions,				\
+		.value = (uint8_t *)string,				\
+		.len = strlen(string),					\
+	}
+
+static struct gatt_db *make_service_data_2_db(void)
+{
+	const struct att_handle_spec specs[] = {
+		PRIMARY_SERVICE(0x0001, GATT_UUID, 4),
+		CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ, "BlueZ"),
+		DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+								"Device Name"),
+		PRIMARY_SERVICE(0x0005, HEART_RATE_UUID, 6),
+		CHARACTERISTIC_STR_AT(0x0008,
+					GATT_CHARAC_MANUFACTURER_NAME_STRING,
+					BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_WRITE, ""),
+		DESCRIPTOR_STR_AT(0x000a, GATT_CHARAC_USER_DESC_UUID,
+					BT_ATT_PERM_READ, "Manufacturer Name"),
+		{ }
+	};
+
+	return make_db(specs);
+}
+
+#define CHARACTERISTIC_AT(chr_handle, chr_uuid, permissions, properties, \
+							bytes...)	\
+	{								\
+		.valid = true,						\
+		.handle = chr_handle,					\
+		.type = CHARACTERISTIC,					\
+		.uuid = STR(chr_uuid),					\
+		.att_permissions = permissions,				\
+		.char_properties = properties,				\
+		.value = data(bytes),					\
+		.len = sizeof(data(bytes)),				\
+	}
+
+#define DESCRIPTOR_AT(desc_handle, desc_uuid, permissions, bytes...)	\
+	{								\
+		.valid = true,						\
+		.handle = desc_handle,					\
+		.type = DESCRIPTOR,					\
+		.uuid = STR(desc_uuid),					\
+		.att_permissions = permissions,				\
+		.value = data(bytes),					\
+		.len = sizeof(data(bytes)),				\
+	}
+
+static struct gatt_db *make_service_data_3_db(void)
+{
+	const struct att_handle_spec specs[] = {
+		PRIMARY_SERVICE(0x0100, GAP_UUID, 0x0121 - 0x0100 + 1),
+		CHARACTERISTIC_STR_AT(0x0111, GATT_CHARAC_DEVICE_NAME,
+					BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ, "BlueZ"),
+		CHARACTERISTIC_AT(0x0121, GATT_CHARAC_APPEARANCE,
+					BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ, 0x00, 0x00),
+		PRIMARY_SERVICE(0x0200, GATT_UUID, 0x0200 - 0x0200 + 1),
+		PRIMARY_SERVICE(0x0300, HEART_RATE_UUID, 0x0320 - 0x0300 + 1),
+		CHARACTERISTIC_STR_AT(0x0311,
+					GATT_CHARAC_MANUFACTURER_NAME_STRING,
+					BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_WRITE, ""),
+		DESCRIPTOR_AT(0x0320, GATT_CLIENT_CHARAC_CFG_UUID,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					0x00, 0x00),
+		{ }
+	};
+
+	return make_db(specs);
+}
+
+/*
+ * Defined Test database 1:
+ * Tiny database fits into a single minimum sized-pdu.
+ * Satisfies requirements:
+ * 3. At least one primary service at the MAX handle
+ * 7. at least one service uuid with multiple instances
+ * 8. Some simple services, some with included services
+ * 9. an instance where handle of included service comes before the including
+ * service
+ * 11. Simple characteristics (no desc) and complex characteristics
+ *     (multiple descriptors)
+ * 12. Instances of complex chars with 16-bit and 128-bit uuids
+ *     (although not in scrambled order)
+ */
+
+static struct gatt_db *make_test_spec_small_db(void)
+{
+	const struct att_handle_spec specs[] = {
+		SECONDARY_SERVICE(0x0001, DEVICE_INFORMATION_UUID, 16),
+		CHARACTERISTIC_STR(GATT_CHARAC_MANUFACTURER_NAME_STRING,
+						BT_ATT_PERM_READ |
+						BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_NOTIFY |
+						BT_GATT_CHRC_PROP_INDICATE,
+						"BlueZ"),
+		DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ |
+						BT_ATT_PERM_WRITE, 0x00, 0x00),
+		DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+							"Manufacturer Name"),
+		PRIMARY_SERVICE(0xF010, GAP_UUID, 8),
+		INCLUDE(0x0001),
+		CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+							BT_GATT_CHRC_PROP_READ,
+							"BlueZ Unit Tester"),
+		CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					BT_GATT_CHRC_PROP_READ, 0x09),
+		CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ, 0x00, 0x00),
+		PRIMARY_SERVICE(0xFFFF, DEVICE_INFORMATION_UUID, 1),
+		{ }
+	};
+
+	return make_db(specs);
+}
+
+/*
+ * Defined Test database 2:
+ * Large Database with 128-bit services at the end
+ * Satisfies requirements:
+ * 4. at least one primary service without any include or characteristic
+ *    at the max handle.
+ * 6. at least one secondary service
+ * 7. at least one each of 16-bit and 128-bit UUID with multiple instances
+ * 8. some simple services, some some with included services
+ * 9. one instance where an included service comes before the including
+ * 10. one or more services with both 16-bit and 128-bit service UUIDs
+ * 11. simple and complex characteristics
+ * 12. complex chars with 16-bit and 128-bit uuids
+ */
+
+#define STRING_512BYTES "11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"11111222223333344444555556666677777888889999900000" \
+			"111112222233"
+
+static struct gatt_db *make_test_spec_large_db_1(void)
+{
+	const struct att_handle_spec specs[] = {
+		PRIMARY_SERVICE(0x0080, "a00b", 6),
+		CHARACTERISTIC(0xb008, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_WRITE,
+					0x08),
+		DESCRIPTOR(0xb015, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x01),
+		DESCRIPTOR(0xb016, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x02),
+		DESCRIPTOR(0xb017, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE |
+						BT_ATT_PERM_ENCRYPT, 0x03),
+
+		SECONDARY_SERVICE(0x0001, "a00d", 6),
+		INCLUDE(0x0080),
+		CHARACTERISTIC(0xb00c, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+									0x0C),
+		CHARACTERISTIC(0000b00b-0000-0000-0123-456789abcdef,
+				BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, 0x0B),
+
+		PRIMARY_SERVICE(0x0010, GATT_UUID, 4),
+		CHARACTERISTIC(GATT_CHARAC_SERVICE_CHANGED, BT_ATT_PERM_READ,
+						BT_GATT_CHRC_PROP_INDICATE,
+						0x01, 0x00, 0xFF, 0xFF),
+		DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					0x00, 0x00),
+
+		PRIMARY_SERVICE(0x0020, "a00a", 10),
+		INCLUDE(0x0001),
+		CHARACTERISTIC(0xb001, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+									0x01),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						STRING_512BYTES),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_WRITE,
+						"1111122222333334444455555"
+						"6666677777888889999900000"),
+		CHARACTERISTIC(0xb003, BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_WRITE, 0x03),
+
+		PRIMARY_SERVICE(0x0030, "a00b", 3),
+		CHARACTERISTIC(0xb007, BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_WRITE, 0x07),
+
+		PRIMARY_SERVICE(0x0040, GAP_UUID, 7),
+		CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ,
+					"Test Database"),
+		CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
+						BT_GATT_CHRC_PROP_READ, 17),
+		CHARACTERISTIC(GATT_CHARAC_PERIPHERAL_PREF_CONN,
+				BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+				0x64, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x07, 0xD0),
+
+		PRIMARY_SERVICE(0x0050, "a00b", 3),
+		CHARACTERISTIC(0xb006, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_WRITE |
+					BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP |
+					BT_GATT_CHRC_PROP_NOTIFY |
+					BT_GATT_CHRC_PROP_INDICATE, 0x06),
+
+		PRIMARY_SERVICE(0x0060, "a00b", 12),
+		CHARACTERISTIC(0xb004, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+			BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE, 0x04),
+		CHARACTERISTIC(0xb004, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+			BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE, 0x04),
+		DESCRIPTOR(GATT_SERVER_CHARAC_CFG_UUID,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					0x00, 0x00),
+		CHARACTERISTIC(0xb004, 0, 0, 0x04),
+		DESCRIPTOR(0xb012, 0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+				0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33,
+				0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11,
+				0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+				0x00, 0x11, 0x22, 0x33),
+		CHARACTERISTIC(0xb004, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+									0x04),
+		DESCRIPTOR(0xb012, BT_ATT_PERM_READ, 0x11, 0x22, 0x33, 0x44,
+				0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+				0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33),
+
+		PRIMARY_SERVICE(0x0070, "a00b", 7),
+		CHARACTERISTIC(0xb005, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE |
+						BT_GATT_CHRC_PROP_EXT_PROP,
+						0x05),
+		DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x03,
+									0x00),
+		DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					"ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+		DESCRIPTOR(GATT_CHARAC_FMT_UUID, 0x04, 0x00, 0x01, 0x30, 0x01,
+								0x11, 0x31),
+		DESCRIPTOR(0000d5d4-0000-0000-0123-456789abcdef,
+							BT_ATT_PERM_READ, 0x44),
+
+		/* 0x0080 service defined earlier, included in 0x0001 */
+
+		PRIMARY_SERVICE(0x0090, "0000a00c-0000-0000-0123-456789abcdef",
+									7),
+		INCLUDE(0x0001),
+		CHARACTERISTIC(0000b009-0000-0000-0123-456789abcdef,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					BT_GATT_CHRC_PROP_READ |
+					BT_GATT_CHRC_PROP_WRITE |
+					BT_GATT_CHRC_PROP_EXT_PROP, 0x09),
+		DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01,
+									0x00),
+		DESCRIPTOR(0000d9d2-0000-0000-0123-456789abcdef,
+				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x22),
+		DESCRIPTOR(0000d9d3-0000-0000-0123-456789abcdef,
+						BT_ATT_PERM_WRITE, 0x33),
+
+		PRIMARY_SERVICE(0x00a0, "a00f", 18),
+		CHARACTERISTIC_STR(0xb00e, BT_ATT_PERM_READ,
+					BT_GATT_CHRC_PROP_READ, "Length is "),
+		DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x19, 0x00,
+						0x00, 0x30, 0x01, 0x00, 0x00),
+		CHARACTERISTIC(0xb00f, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE, 0x65),
+		DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x04, 0x00,
+						0x01, 0x27, 0x01, 0x01, 0x00),
+		CHARACTERISTIC(0xb006, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						0x34, 0x12),
+		DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x06, 0x00,
+						0x10, 0x27, 0x01, 0x02, 0x00),
+		CHARACTERISTIC(0xb007, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						0x04, 0x03, 0x02, 0x01),
+		DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x08, 0x00,
+						0x17, 0x27, 0x01, 0x03, 0x00),
+		CHARACTERISTIC(0xb010, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+					0x65, 0x34, 0x12, 0x04, 0x03, 0x02,
+					0x01),
+		DESCRIPTOR(GATT_CHARAC_AGREG_FMT_UUID, BT_ATT_PERM_READ, 0xA6,
+						0x00, 0xa9, 0x00, 0xac, 0x00),
+		CHARACTERISTIC(0xb011, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_AUTH, 0x012),
+
+		PRIMARY_SERVICE(0x00C0, "0000a00c-0000-0000-0123-456789abcdef",
+									30),
+		CHARACTERISTIC(0xb00a, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+									0x0A),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						"111112222233333444445"),
+		DESCRIPTOR(0xb012, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						"2222233333444445555566"),
+		DESCRIPTOR(0xb013, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11,
+				0x22),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						"33333444445555566666777"),
+		DESCRIPTOR(0xb014, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11,
+				0x22, 0x33),
+		CHARACTERISTIC(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33),
+		DESCRIPTOR(0xb012, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+				0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33),
+		CHARACTERISTIC(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44),
+		DESCRIPTOR(0xb013, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+				0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33, 0x44),
+		CHARACTERISTIC(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55,
+						0x66, 0x77, 0x88, 0x99, 0x00,
+						0x11, 0x22, 0x33, 0x44, 0x55),
+		DESCRIPTOR(0xb014, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+				0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33, 0x44, 0x55),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						"1111122222333334444455555"
+						"666667777788888999"),
+		DESCRIPTOR(0xb012, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+				0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						"2222233333444445555566666"
+						"7777788888999990000"),
+		DESCRIPTOR(0xb013, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+				0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33, 0x44),
+		CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+						BT_GATT_CHRC_PROP_READ |
+						BT_GATT_CHRC_PROP_WRITE,
+						"3333344444555556666677777"
+						"88888999990000011111"),
+		DESCRIPTOR(0xb014, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+				0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+				0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+				0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+				0x11, 0x22, 0x33, 0x44, 0x55),
+		{ }
+	};
+
+	return make_db(specs);
+}
+
+static void test_client(gconstpointer data)
+{
+	create_context(512, data);
+}
+
+static void test_server(gconstpointer data)
+{
+	struct context *context = create_context(512, data);
+	ssize_t len;
+	const struct test_pdu pdu = SERVER_MTU_EXCHANGE_PDU;
+
+	len = write(context->fd, pdu.data, pdu.size);
+
+	g_assert_cmpint(len, ==, pdu.size);
+
+	util_hexdump('<', pdu.data, len, test_debug, "GATT: ");
+}
+
+static void test_search_primary(gconstpointer data)
+{
+	struct context *context = create_context(512, data);
+	const struct test_data *test_data = data;
+
+	context->req = bt_gatt_discover_all_primary_services(context->att,
+							test_data->uuid,
+							generic_search_cb,
+							context, NULL);
+}
+
+static void test_search_included(gconstpointer data)
+{
+	struct context *context = create_context(512, data);
+
+	context->req = bt_gatt_discover_included_services(context->att,
+							0x0001, 0xffff,
+							generic_search_cb,
+							context, NULL);
+}
+
+static void test_search_chars(gconstpointer data)
+{
+	struct context *context = create_context(512, data);
+
+	context->req = bt_gatt_discover_characteristics(context->att,
+							0x0010, 0x0020,
+							generic_search_cb,
+							context, NULL);
+	g_assert(context->req);
+}
+
+static void test_search_descs(gconstpointer data)
+{
+	struct context *context = create_context(512, data);
+
+	context->req = bt_gatt_discover_descriptors(context->att,
+							0x0013, 0x0016,
+							generic_search_cb,
+							context, NULL);
+	g_assert(context->req);
+}
+
+static const struct test_step test_read_by_type_1 = {
+	.handle = 0x0001,
+	.end_handle = 0xffff,
+	.expected_att_ecode = 0x0a,
+	.value = read_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_read_by_type_2 = {
+	.handle = 0x0001,
+	.end_handle = 0xffff,
+	.expected_att_ecode = 0x02,
+};
+
+static const struct test_step test_read_by_type_3 = {
+	.handle = 0x0001,
+	.end_handle = 0xffff,
+	.expected_att_ecode = 0x0a,
+};
+
+static const struct test_step test_read_by_type_4 = {
+	.handle = 0x0001,
+	.end_handle = 0xffff,
+	.expected_att_ecode = 0x08,
+};
+
+static const struct test_step test_read_by_type_5 = {
+	.handle = 0x0001,
+	.end_handle = 0xffff,
+	.expected_att_ecode = 0x05,
+};
+
+static const struct test_step test_read_by_type_6 = {
+	.handle = 0x0001,
+	.end_handle = 0xffff,
+	.expected_att_ecode = 0x0c,
+};
+
+static void multiple_read_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	g_assert_cmpint(att_ecode, ==, step->expected_att_ecode);
+
+	if (success) {
+		g_assert_cmpint(length, ==, step->length);
+		g_assert(memcmp(value, step->value, length) == 0);
+	}
+
+	context_quit(context);
+}
+
+static void test_multiple_read(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+	uint16_t handles[2];
+
+	handles[0] = step->handle;
+	handles[1] = step->end_handle;
+
+	g_assert(bt_gatt_client_read_multiple(context->client, handles, 2,
+						multiple_read_cb, context,
+						NULL));
+}
+
+static const struct test_step test_multiple_read_1 = {
+	.handle = 0x0003,
+	.end_handle = 0x0007,
+	.func = test_multiple_read,
+	.value = read_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_multiple_read_2 = {
+	.handle = 0x0003,
+	.end_handle = 0x0007,
+	.func = test_multiple_read,
+	.expected_att_ecode = 0x02
+};
+
+static const struct test_step test_multiple_read_3 = {
+	.handle = 0x0003,
+	.end_handle = 0x0007,
+	.func = test_multiple_read,
+	.expected_att_ecode = 0x01
+};
+
+static const struct test_step test_multiple_read_4 = {
+	.handle = 0x0003,
+	.end_handle = 0x0007,
+	.func = test_multiple_read,
+	.expected_att_ecode = 0x08
+};
+
+static const struct test_step test_multiple_read_5 = {
+	.handle = 0x0003,
+	.end_handle = 0x0007,
+	.func = test_multiple_read,
+	.expected_att_ecode = 0x05
+};
+
+static const struct test_step test_multiple_read_6 = {
+	.handle = 0x0003,
+	.end_handle = 0x0007,
+	.func = test_multiple_read,
+	.expected_att_ecode = 0x0c
+};
+
+static void read_by_type_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+	struct bt_gatt_iter iter;
+
+	g_assert(att_ecode == step->expected_att_ecode);
+
+	if (success) {
+		uint16_t length, handle;
+		const uint8_t *value;
+
+		g_assert(bt_gatt_iter_init(&iter, result));
+		g_assert(bt_gatt_iter_next_read_by_type(&iter, &handle, &length,
+								&value));
+		g_assert(length == step->length);
+		g_assert(!memcmp(value, step->value, length));
+
+		g_assert(!bt_gatt_iter_next_read_by_type(&iter, &handle,
+							&length, &value));
+	}
+
+	context_quit(context);
+}
+
+static void test_read_by_type(gconstpointer data)
+{
+	struct context *context = create_context(512, data);
+	const struct test_data *test_data = data;
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_read_by_type(context->att, step->handle,
+					step->end_handle, test_data->uuid,
+					read_by_type_cb, context, NULL));
+}
+
+static void test_long_read(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_read_long_value(context->client, step->handle,
+						0, multiple_read_cb, context,
+						NULL));
+}
+
+static const struct test_step test_long_read_1 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0,
+	.value = read_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_read_2 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0,
+	.value = long_data_2,
+	.length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_read_3 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0x02
+};
+
+static const struct test_step test_long_read_4 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0x07
+};
+
+static const struct test_step test_long_read_5 = {
+	.handle = 0x0000,
+	.func = test_long_read,
+	.expected_att_ecode = 0x01
+};
+
+static const struct test_step test_long_read_6 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0x08
+};
+
+static const struct test_step test_long_read_7 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0x05
+};
+
+static const struct test_step test_long_read_8 = {
+	.handle = 0x0003,
+	.func = test_long_read,
+	.expected_att_ecode = 0x0c
+};
+
+/* Descriptor test data's */
+
+static const struct test_step test_long_read_9 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0,
+	.value = read_data_1,
+	.length = 0x03
+};
+
+static const struct test_step test_long_read_10 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0,
+	.value = long_data_2,
+	.length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_read_11 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0x02
+};
+
+static const struct test_step test_long_read_12 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0x07
+};
+
+static const struct test_step test_long_read_13 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0x08
+};
+
+static const struct test_step test_long_read_14 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0x05
+};
+
+static const struct test_step test_long_read_15 = {
+	.handle = 0x0004,
+	.func = test_long_read,
+	.expected_att_ecode = 0x0c
+};
+
+static void notification_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	if (value_handle == step->handle) {
+		g_assert_cmpint(length, ==, step->length);
+
+		g_assert(memcmp(value, step->value, length) == 0);
+
+		context_quit(context);
+	}
+}
+
+static void notification_register_cb(uint16_t att_ecode, void *user_data)
+{
+	g_assert(!att_ecode);
+}
+
+static void test_notification(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_register_notify(context->client, step->handle,
+						notification_register_cb,
+						notification_cb, context,
+						NULL));
+}
+
+static const struct test_step test_notification_1 = {
+	.handle = 0x0003,
+	.func = test_notification,
+	.value = read_data_1,
+	.length = 0x03,
+};
+
+static void test_server_notification(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	bt_gatt_server_send_notification(context->server, step->handle,
+						step->value, step->length);
+}
+
+static const struct test_step test_notification_server_1 = {
+	.handle = 0x0003,
+	.func = test_server_notification,
+	.value = read_data_1,
+	.length = 0x03,
+};
+
+static uint8_t indication_received;
+
+static void test_indication_cb(void *user_data)
+{
+	struct context *context = user_data;
+
+	indication_received = 1;
+
+	context_quit(context);
+}
+
+static void test_server_indication_confirm(struct context *context)
+{
+	g_assert(indication_received == 1);
+}
+
+static void indication_cb(uint16_t value_handle, const uint8_t *value,
+					uint16_t length, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_step *step = context->data->step;
+
+	if (value_handle == step->handle) {
+		g_assert_cmpint(length, ==, step->length);
+
+		g_assert(memcmp(value, step->value, length) == 0);
+	}
+}
+
+static void test_indication(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	g_assert(bt_gatt_client_register_notify(context->client, step->handle,
+						notification_register_cb,
+						indication_cb, context,
+						NULL));
+}
+
+static const struct test_step test_indication_1 = {
+	.handle = 0x0003,
+	.func = test_indication,
+	.value = read_data_1,
+	.length = 0x03,
+};
+
+static void test_server_indication(struct context *context)
+{
+	const struct test_step *step = context->data->step;
+
+	bt_gatt_server_send_indication(context->server, step->handle,
+						step->value, step->length,
+						test_indication_cb,
+						context, NULL);
+}
+
+static const struct test_step test_indication_server_1 = {
+	.handle = 0x0003,
+	.func = test_server_indication,
+	.post_func = test_server_indication_confirm,
+	.value = read_data_1,
+	.length = 0x03,
+};
+
+int main(int argc, char *argv[])
+{
+	struct gatt_db *service_db_1, *service_db_2, *service_db_3;
+	struct gatt_db *ts_small_db, *ts_large_db_1;
+
+	tester_init(&argc, &argv);
+
+	service_db_1 = make_service_data_1_db();
+	service_db_2 = make_service_data_2_db();
+	service_db_3 = make_service_data_3_db();
+	ts_small_db = make_test_spec_small_db();
+	ts_large_db_1 = make_test_spec_large_db_1();
+
+	/*
+	 * Server Configuration
+	 *
+	 * The test group objective is to verify Generic Attribute Profile
+	 * Server Configuration.
+	 */
+
+	define_test_client("/TP/GAC/CL/BV-01-C", test_client, NULL, NULL,
+						raw_pdu(0x02, 0x00, 0x02));
+
+	define_test_server("/TP/GAC/SR/BV-01-C", test_server, service_db_1,
+					NULL,
+					raw_pdu(0x03, 0x00, 0x02));
+
+	/*
+	 * Discovery
+	 *
+	 * The test group objective is to verify Generic Attribute Profile
+	 * Discovery of Services and Service Characteristics.
+	 */
+	define_test_att("/TP/GAD/CL/BV-01-C", test_search_primary, NULL, NULL,
+			raw_pdu(0x02, 0x00, 0x02),
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+			raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x00, 0x18,
+					0x20, 0x00, 0x29, 0x00, 0xb0, 0x68,
+					0x30, 0x00, 0x32, 0x00, 0x19, 0x18),
+			raw_pdu(0x10, 0x33, 0x00, 0xff, 0xff, 0x00, 0x28),
+			raw_pdu(0x11, 0x14, 0x90, 0x00, 0x96, 0x00, 0xef, 0xcd,
+					0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
+					0x00, 0x00, 0x00, 0x00, 0x85, 0x60,
+					0x00, 0x00),
+			raw_pdu(0x10, 0x97, 0x00, 0xff, 0xff, 0x00, 0x28),
+			raw_pdu(0x01, 0x10, 0x97, 0x00, 0x0a));
+
+	define_test_att("/TP/GAD/CL/BV-01-C-small", test_search_primary, NULL,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			PRIMARY_DISC_SMALL_DB);
+
+	define_test_server("/TP/GAD/SR/BV-01-C", test_server, service_db_1,
+			NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+			raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18,
+					0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),
+			raw_pdu(0x10, 0x06, 0x00, 0xff, 0xff, 0x00, 0x28),
+			raw_pdu(0x01, 0x10, 0x06, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-01-C-small", test_server, ts_small_db,
+			NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			PRIMARY_DISC_SMALL_DB);
+
+	define_test_server("/TP/GAD/SR/BV-01-C-large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			PRIMARY_DISC_LARGE_DB_1);
+
+	define_test_att("/TP/GAD/CL/BV-02-C-1", test_search_primary, &uuid_16,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00,
+					0x18),
+			raw_pdu(0x07, 0x01, 0x00, 0x07, 0x00),
+			raw_pdu(0x06, 0x08, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00,
+					0x18),
+			raw_pdu(0x01, 0x06, 0x08, 0x00, 0x0a));
+
+	define_test_att("/TP/GAD/CL/BV-02-C-2", test_search_primary, &uuid_128,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0xfb,
+					0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00,
+					0x80, 0x00, 0x10, 0x00, 0x00, 0x0d,
+					0x18, 0x00, 0x00),
+			raw_pdu(0x07, 0x10, 0x00, 0x17, 0x00),
+			raw_pdu(0x06, 0x18, 0x00, 0xff, 0xff, 0x00, 0x28, 0xfb,
+					0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00,
+					0x80, 0x00, 0x10, 0x00, 0x00, 0x0d,
+					0x18, 0x00, 0x00),
+			raw_pdu(0x01, 0x06, 0x18, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-02-C/exists-16/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00,
+				0x18),
+			raw_pdu(0x07, 0x10, 0xf0, 0x17, 0xf0),
+			raw_pdu(0x06, 0x18, 0xf0, 0xff, 0xff, 0x00, 0x28, 0x00,
+				0x18),
+			raw_pdu(0x01, 0x06, 0x18, 0xf0, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-02-C/exists-16/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0b,
+				0xa0),
+			raw_pdu(0x07, 0x30, 0x00, 0x32, 0x00, 0x50, 0x00, 0x52,
+				0x00, 0x60, 0x00, 0x6b, 0x00, 0x70, 0x00, 0x76,
+				0x00, 0x80, 0x00, 0x85, 0x00),
+			raw_pdu(0x06, 0x86, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0b,
+				0xa0),
+			raw_pdu(0x01, 0x06, 0x86, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-02-C/missing-16/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x01,
+				0x18),
+			raw_pdu(0x01, 0x06, 0x01, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-02-C/missing-16/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0f,
+				0xf0),
+			raw_pdu(0x01, 0x06, 0x01, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-02-C/exists-128/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0xef,
+				0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+				0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00),
+			raw_pdu(0x07, 0x90, 0x00, 0x96, 0x00, 0xc0, 0x00, 0xdd,
+				0x00),
+			raw_pdu(0x06, 0xde, 0x00, 0xff, 0xff, 0x00, 0x28, 0xef,
+				0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+				0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00),
+			raw_pdu(0x01, 0x06, 0xde, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-02-C/missing-128/large-1",
+			test_server, ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0xff,
+				0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+				0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00),
+			raw_pdu(0x01, 0x06, 0x01, 0x00, 0x0a));
+
+	define_test_att("/TP/GAD/CL/BV-03-C", test_search_included, NULL,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x09, 0x08, 0x02, 0x00, 0x10, 0x00, 0x1f, 0x00,
+					0x0f, 0x18),
+			raw_pdu(0x08, 0x03, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x09, 0x06, 0x03, 0x00, 0x20, 0x00, 0x2f, 0x00,
+					0x04, 0x00, 0x30, 0x00, 0x3f, 0x00),
+			raw_pdu(0x0a, 0x20, 0x00),
+			raw_pdu(0x0b, 0x00, 0x00, 0x3e, 0x39, 0x00, 0x00, 0x00,
+					0x00, 0x01, 0x23, 0x45, 0x67, 0x89,
+					0xab, 0xcd, 0xef),
+			raw_pdu(0x0a, 0x30, 0x00),
+			raw_pdu(0x0b, 0x00, 0x00, 0x3b, 0x39, 0x00, 0x00, 0x00,
+					0x00, 0x01, 0x23, 0x45, 0x67, 0x89,
+					0xab, 0xcd, 0xef),
+			raw_pdu(0x08, 0x05, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x09, 0x08, 0x05, 0x00, 0x40, 0x00, 0x4f, 0x00,
+								0x0a, 0x18),
+			raw_pdu(0x08, 0x06, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-03-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x10, 0x00,
+					0x0a, 0x18),
+			raw_pdu(0x08, 0x12, 0xf0, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-03-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x09, 0x08, 0x02, 0x00, 0x80, 0x00, 0x85, 0x00,
+				0x0b, 0xa0, 0x21, 0x00, 0x01, 0x00, 0x06, 0x00,
+				0x0d, 0xa0),
+			raw_pdu(0x08, 0x22, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x09, 0x08, 0x91, 0x00, 0x01, 0x00, 0x06, 0x00,
+				0x0d, 0xa0),
+			raw_pdu(0x08, 0x92, 0x00, 0xff, 0xff, 0x02, 0x28),
+			raw_pdu(0x01, 0x08, 0x92, 0x00, 0x0a));
+
+	define_test_att("/TP/GAD/CL/BV-04-C", test_search_chars, NULL,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x10, 0x00, 0x20, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x11, 0x00, 0x02, 0x12, 0x00, 0x25,
+					0x2a),
+			raw_pdu(0x08, 0x12, 0x00, 0x20, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x15, 0x13, 0x00, 0x02, 0x14, 0x00, 0x85,
+					0x00, 0xef, 0xcd, 0xab, 0x89, 0x67,
+					0x45, 0x23, 0x01, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00),
+			raw_pdu(0x08, 0x14, 0x00, 0x20, 0x00, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x12, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-04-C/small/1", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
+					0x2a),
+			raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+					0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
+					0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
+					0xb0, 0x00, 0x00),
+			raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01,
+					0x2a),
+			raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-04-C/small/2", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+					0x2a),
+			raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-04-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x20, 0x00, 0x29, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x22, 0x00, 0x02, 0x23, 0x00, 0x01,
+				0xb0, 0x24, 0x00, 0x0a, 0x25, 0x00, 0x02, 0xb0,
+				0x26, 0x00, 0x08, 0x27, 0x00, 0x02, 0xb0),
+			raw_pdu(0x08, 0x27, 0x00, 0x29, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x28, 0x00, 0x08, 0x29, 0x00, 0x03,
+				0xb0),
+			raw_pdu(0x08, 0x29, 0x00, 0x29, 0x00, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x29, 0x00, 0x0a));
+
+	define_test_att("/TP/GAD/CL/BV-05-C", test_search_chars, NULL,
+			NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x10, 0x00, 0x20, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x11, 0x00, 0x02, 0x12, 0x00, 0x25,
+					0x2a),
+			raw_pdu(0x08, 0x12, 0x00, 0x20, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x15, 0x13, 0x00, 0x02, 0x14, 0x00, 0x85,
+					0x00, 0xef, 0xcd, 0xab, 0x89, 0x67,
+					0x45, 0x23, 0x01, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00),
+			raw_pdu(0x08, 0x14, 0x00, 0x20, 0x00, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x12, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-05-C/small/1", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
+					0x2a),
+			raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+					0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
+					0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
+					0xb0, 0x00, 0x00),
+			raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01,
+					0x2a),
+			raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-05-C/small/2", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+					0x2a),
+			raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
+
+	define_test_server("/TP/GAD/SR/BV-05-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x20, 0x00, 0x29, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x22, 0x00, 0x02, 0x23, 0x00, 0x01,
+				0xb0, 0x24, 0x00, 0x0a, 0x25, 0x00, 0x02, 0xb0,
+				0x26, 0x00, 0x08, 0x27, 0x00, 0x02, 0xb0),
+			raw_pdu(0x08, 0x27, 0x00, 0x29, 0x00, 0x03, 0x28),
+			raw_pdu(0x09, 0x07, 0x28, 0x00, 0x08, 0x29, 0x00, 0x03,
+				0xb0),
+			raw_pdu(0x08, 0x29, 0x00, 0x29, 0x00, 0x03, 0x28),
+			raw_pdu(0x01, 0x08, 0x29, 0x00, 0x0a));
+
+	define_test_att("/TP/GAD/CL/BV-06-C", test_search_descs, NULL, NULL,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x04, 0x13, 0x00, 0x16, 0x00),
+			raw_pdu(0x05, 0x01, 0x13, 0x00, 0x02, 0x29, 0x14, 0x00,
+					0x03, 0x29),
+			raw_pdu(0x04, 0x15, 0x00, 0x16, 0x00),
+			raw_pdu(0x05, 0x01, 0x15, 0x00, 0x04, 0x29, 0x16, 0x00,
+					0x05, 0x29));
+
+	define_test_client("/TP/GAD/CL/BV-06-C/client-1", test_client,
+			service_db_1, NULL,
+			SERVICE_DATA_1_PDUS);
+
+	define_test_client("/TP/GAD/CL/BV-06-C/client-2", test_client,
+			service_db_2, NULL,
+			SERVICE_DATA_2_PDUS);
+
+	define_test_client("/TP/GAD/CL/BV-06-C/client-3", test_client,
+			service_db_3, NULL,
+			SERVICE_DATA_3_PDUS);
+
+	define_test_server("/TP/GAD/SR/BV-06-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x04, 0x04, 0x00, 0x05, 0x00),
+			raw_pdu(0x05, 0x01, 0x04, 0x00, 0x02, 0x29, 0x05, 0x00,
+					0x01, 0x29));
+
+	define_test_server("/TP/GAD/SR/BV-06-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x04, 0x73, 0x00, 0x76, 0x00),
+			raw_pdu(0x05, 0x01, 0x73, 0x00, 0x00, 0x29, 0x74, 0x00,
+				0x01, 0x29, 0x75, 0x00, 0x04, 0x29),
+			raw_pdu(0x04, 0x76, 0x00, 0x76, 0x00),
+			raw_pdu(0x05, 0x02, 0x76, 0x00, 0xef, 0xcd, 0xab, 0x89,
+				0x67, 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00,
+				0xd4, 0xd5, 0x00, 0x00));
+
+	define_test_client("/TP/GAR/CL/BV-01-C", test_client, service_db_1,
+			&test_read_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-01-C", test_client, service_db_1,
+			&test_read_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x00, 0x00),
+			raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
+
+	define_test_client("/TP/GAR/CL/BI-02-C", test_client, service_db_1,
+			&test_read_3,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x02));
+
+	define_test_client("/TP/GAR/CL/BI-03-C", test_client, service_db_1,
+			&test_read_4,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08));
+
+	define_test_client("/TP/GAR/CL/BI-04-C", test_client, service_db_1,
+			&test_read_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05));
+
+	define_test_client("/TP/GAR/CL/BI-04-C/auto", test_client, service_db_1,
+			&test_read_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05),
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-05-C", test_client, service_db_1,
+			&test_read_6,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x0c));
+
+	define_test_server("/TP/GAR/SR/BV-01-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x0b, 0x42, 0x6c, 0x75, 0x65, 0x5a));
+
+	define_test_server("/TP/GAR/SR/BV-01-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0a, 0xc4, 0x00),
+			raw_pdu(0x0b, '1', '1', '1', '1', '1', '2', '2', '2',
+				'2', '2', '3', '3', '3', '3', '3', '4', '4',
+				'4', '4', '4', '5'),
+			raw_pdu(0x0a, 0xca, 0x00),
+			raw_pdu(0x0b, '3', '3', '3', '3', '3', '4', '4', '4',
+				'4', '4', '5', '5', '5', '5', '5', '6', '6',
+				'6', '6', '6', '7', '7'));
+
+	define_test_server("/TP/GAR/SR/BI-02-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0a, 0x00, 0x00),
+			raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
+
+	define_test_server("/TP/GAR/SR/BI-02-C/large", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0a, 0x0f, 0xf0),
+			raw_pdu(0x01, 0x0a, 0x0f, 0xf0, 0x01));
+
+	define_test_att("/TP/GAR/CL/BV-03-C-1", test_read_by_type,
+			&uuid_char_16, &test_read_by_type_1,
+			raw_pdu(0x02, 0x00, 0x02),
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x09, 0x05, 0x0a, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x08, 0x0b, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+
+	define_test_att("/TP/GAR/CL/BV-03-C-2", test_read_by_type,
+			&uuid_char_128, &test_read_by_type_1,
+			raw_pdu(0x02, 0x00, 0x02),
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0f, 0x0e, 0x0d,
+					0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
+					0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+					0x00),
+			raw_pdu(0x09, 0x05, 0x0a, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x08, 0x0b, 0x00, 0xff, 0xff, 0x0f, 0x0e, 0x0d,
+					0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
+					0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+					0x00),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+
+	define_test_att("/TP/GAR/CL/BI-06-C", test_read_by_type, &uuid_char_16,
+			&test_read_by_type_2,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x02));
+
+	define_test_att("/TP/GAR/CL/BI-07-C", test_read_by_type, &uuid_char_16,
+			&test_read_by_type_3,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+
+	define_test_att("/TP/GAR/CL/BI-09-C", test_read_by_type, &uuid_char_16,
+			&test_read_by_type_4,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x08));
+
+	define_test_att("/TP/GAR/CL/BI-10-C", test_read_by_type, &uuid_char_16,
+			&test_read_by_type_5,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x05));
+
+	define_test_att("/TP/GAR/CL/BI-11-C", test_read_by_type, &uuid_char_16,
+			&test_read_by_type_6,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
+			raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0c));
+
+	define_test_server("/TP/GAR/SR/BV-03-C/small", test_server, ts_small_db,
+			NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xef, 0xcd, 0xab,
+					0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+					0x00, 0x00, 0x00, 0x09, 0xB0, 0x00,
+					0x00),
+			raw_pdu(0x09, 0x03, 0x15, 0xF0, 0x09),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x2a),
+			raw_pdu(0x09, 0x04, 0x17, 0xF0, 0x00, 0x00));
+
+	define_test_server("/TP/GAR/SR/BV-03-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xef, 0xcd, 0xab,
+					0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+					0x00, 0x00, 0x00, 0xd4, 0xd5, 0x00,
+					0x00),
+			raw_pdu(0x09, 0x03, 0x76, 0x00, 0x44),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x02, 0xB0),
+			raw_pdu(0x09, 0x15, 0x25, 0x00, '1', '1', '1', '1', '1',
+				'2', '2', '2', '2', '2', '3', '3', '3', '3',
+				'3', '4', '4', '4', '4'));
+
+	define_test_server("/TP/GAR/SR/BI-06-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0xB0),
+			raw_pdu(0x01, 0x08, 0x32, 0x00, 0x02));
+
+	define_test_server("/TP/GAR/SR/BI-07-C/small", test_server, ts_small_db,
+			NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xF0, 0x0F),
+			raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a));
+
+	define_test_server("/TP/GAR/SR/BI-07-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xF0, 0x0F),
+			raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a));
+
+	define_test_server("/TP/GAR/SR/BI-08-C/small", test_server, ts_small_db,
+			NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28),
+			raw_pdu(0x01, 0x08, 0x02, 0x00, 0x01));
+
+	define_test_server("/TP/GAR/SR/BI-08-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x08, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28),
+			raw_pdu(0x01, 0x08, 0x02, 0x00, 0x01));
+
+	define_test_server("/TP/GAR/SR/BV-04-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xD3, 0x00, 0x00, 0x00),
+			raw_pdu(0x0D, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+				0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22),
+			raw_pdu(0x0C, 0xD3, 0x00, 0x16, 0x00),
+			raw_pdu(0x0D, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+				0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44),
+			raw_pdu(0x0C, 0xD3, 0x00, 0x2C, 0x00),
+			raw_pdu(0x0D, 0x55),
+			raw_pdu(0x0C, 0xD3, 0x00, 0x2D, 0x00),
+			raw_pdu(0x0D));
+
+	define_test_server("/TP/GAR/SR/BI-12-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0x27, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0C, 0x27, 0x00, 0x02));
+
+	define_test_server("/TP/GAR/SR/BI-13-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0x13, 0xF0, 0xF0, 0x00),
+			raw_pdu(0x01, 0x0C, 0x13, 0xF0, 0x07));
+
+	define_test_server("/TP/GAR/SR/BI-13-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xD3, 0x00, 0xF0, 0x00),
+			raw_pdu(0x01, 0x0C, 0xD3, 0x00, 0x07));
+
+	define_test_server("/TP/GAR/SR/BI-14-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+			raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+	define_test_server("/TP/GAR/SR/BI-14-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+			raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+	define_test_client("/TP/GAR/CL/BV-04-C", test_client, service_db_1,
+			&test_long_read_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BV-04-C/512B", test_client, service_db_1,
+			&test_long_read_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff),
+			raw_pdu(0x0c, 0x03, 0x00, 0xff, 0x01),
+			raw_pdu(0x0d, 0xff));
+
+	define_test_client("/TP/GAR/CL/BV-05-C", test_client, service_db_1,
+			&test_multiple_read_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x0f, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-12-C", test_client, service_db_1,
+			&test_long_read_3,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x02));
+
+	define_test_client("/TP/GAR/CL/BI-13-C", test_client, service_db_1,
+			&test_long_read_4,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x07));
+
+	define_test_client("/TP/GAR/CL/BI-14-C", test_client, service_db_1,
+			&test_long_read_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01));
+
+	define_test_client("/TP/GAR/CL/BI-15-C", test_client, service_db_1,
+			&test_long_read_6,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x08));
+
+	define_test_client("/TP/GAR/CL/BI-16-C", test_client, service_db_1,
+			&test_long_read_7,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05));
+
+	define_test_client("/TP/GAR/CL/BI-16-C/auto", test_client, service_db_1,
+			&test_long_read_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05),
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-17-C", test_client, service_db_1,
+			&test_long_read_8,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x0c));
+
+	define_test_client("/TP/GAR/CL/BI-18-C", test_client, service_db_1,
+			&test_multiple_read_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x02));
+
+	define_test_client("/TP/GAR/CL/BI-19-C", test_client, service_db_1,
+			&test_multiple_read_3,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x01));
+
+	define_test_client("/TP/GAR/CL/BI-20-C", test_client, service_db_1,
+			&test_multiple_read_4,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x08));
+
+	define_test_client("/TP/GAR/CL/BI-21-C", test_client, service_db_1,
+			&test_multiple_read_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x05));
+
+	define_test_client("/TP/GAR/CL/BI-21-C/auto", test_client, service_db_1,
+			&test_multiple_read_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x05),
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x0f, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-22-C", test_client, service_db_1,
+			&test_multiple_read_6,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
+			raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x0c));
+
+	define_test_server("/TP/GAR/SR/BV-05-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0e, 0x15, 0xF0, 0x03, 0x00),
+			raw_pdu(0x0f, 0x09, 'B', 'l', 'u', 'e', 'Z'));
+
+	define_test_server("/TP/GAR/SR/BV-05-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0e, 0x44, 0x00, 0x06, 0x00, 0xC4, 0x00),
+			raw_pdu(0x0f, 0x11, 0x0B, '1', '1', '1', '1', '1', '2',
+				'2', '2', '2', '2', '3', '3', '3', '3', '3',
+				'4', '4', '4', '4', '4'));
+
+	define_test_server("/TP/GAR/SR/BI-18-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0e, 0x44, 0x00, 0x06, 0x00, 0x27, 0x00),
+			raw_pdu(0x01, 0x0e, 0x27, 0x00, 0x02));
+
+	define_test_server("/TP/GAR/SR/BI-19-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0e, 0x15, 0xF0, 0xF0, 0x0F),
+			raw_pdu(0x01, 0x0e, 0xF0, 0x0F, 0x01));
+
+	define_test_server("/TP/GAR/SR/BI-19-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0e, 0x44, 0x00, 0xF0, 0x0F),
+			raw_pdu(0x01, 0x0e, 0xF0, 0x0F, 0x01));
+
+	define_test_server("/TP/GAR/SR/BV-06-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0A, 0x05, 0x00),
+			raw_pdu(0x0B, 'M', 'a', 'n', 'u', 'f', 'a', 'c', 't',
+				'u', 'r', 'e', 'r', ' ', 'N', 'a', 'm', 'e'));
+
+	define_test_server("/TP/GAR/SR/BV-06-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0A, 0xD4, 0x00),
+			raw_pdu(0x0B, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90,
+				0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34));
+
+	define_test_server("/TP/GAR/SR/BI-23-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0A, 0x96, 0x00),
+			raw_pdu(0x01, 0x0A, 0x96, 0x00, 0x02));
+
+	define_test_server("/TP/GAR/SR/BI-24-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0A, 0xF0, 0x0F),
+			raw_pdu(0x01, 0x0A, 0xF0, 0x0F, 0x01));
+
+	define_test_server("/TP/GAR/SR/BI-24-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0A, 0xF0, 0x0F),
+			raw_pdu(0x01, 0x0A, 0xF0, 0x0F, 0x01));
+
+	define_test_server("/TP/GAR/SR/BV-07-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xD1, 0x00, 0x00, 0x00),
+			raw_pdu(0x0D, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90,
+				0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34),
+			raw_pdu(0x0C, 0xD1, 0x00, 0x16, 0x00),
+			raw_pdu(0x0D, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44),
+			raw_pdu(0x0C, 0xD1, 0x00, 0x2C, 0x00),
+			raw_pdu(0x0D));
+
+	define_test_server("/TP/GAR/SR/BV-08-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xCE, 0x00, 0x00, 0x00),
+			raw_pdu(0x0D, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90,
+				0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34),
+			raw_pdu(0x0C, 0xCE, 0x00, 0x16, 0x00),
+			raw_pdu(0x0D, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+				0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+				0x88, 0x99, 0x00, 0x11, 0x22, 0x33),
+			raw_pdu(0x0C, 0xCE, 0x00, 0x2B, 0x00),
+			raw_pdu(0x0D));
+
+	define_test_server("/TP/GAR/SR/BI-28-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0x96, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0C, 0x96, 0x00, 0x02));
+
+	define_test_server("/TP/GAR/SR/BI-29-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0x05, 0x00, 0xF0, 0x00),
+			raw_pdu(0x01, 0x0C, 0x05, 0x00, 0x07));
+
+	define_test_server("/TP/GAR/SR/BI-29-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xCE, 0x00, 0xF0, 0x00),
+			raw_pdu(0x01, 0x0C, 0xCE, 0x00, 0x07));
+
+	define_test_server("/TP/GAR/SR/BI-30-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+			raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+	define_test_server("/TP/GAR/SR/BI-30-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+			raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+	define_test_client("/TP/GAN/CL/BV-01-C", test_client, ts_small_db,
+			&test_notification_1,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			SMALL_DB_DISCOVERY_PDUS,
+			raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
+			raw_pdu(0x13),
+			raw_pdu(),
+			raw_pdu(0x1B, 0x03, 0x00, 0x01, 0x02, 0x03));
+
+	define_test_server("/TP/GAN/SR/BV-01-C", test_server, ts_small_db,
+			&test_notification_server_1,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+			raw_pdu(0x13),
+			raw_pdu(),
+			raw_pdu(0x1B, 0x03, 0x00, 0x01, 0x02, 0x03));
+
+	define_test_server("/TP/GAI/SR/BV-01-C", test_server, ts_small_db,
+			&test_indication_server_1,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x04, 0x00, 0x02, 0x00),
+			raw_pdu(0x13),
+			raw_pdu(),
+			raw_pdu(0x1D, 0x03, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x1E));
+
+	define_test_client("/TP/GAI/CL/BV-01-C", test_client, ts_small_db,
+			&test_indication_1,
+			MTU_EXCHANGE_CLIENT_PDUS,
+			SMALL_DB_DISCOVERY_PDUS,
+			raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
+			raw_pdu(0x13),
+			raw_pdu(),
+			raw_pdu(0x1D, 0x03, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x1E));
+
+	define_test_client("/TP/GAR/CL/BV-06-C", test_client, service_db_1,
+			&test_read_7,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-23-C", test_client, service_db_1,
+			&test_read_8,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x02));
+
+	define_test_client("/TP/GAR/CL/BI-24-C", test_client, service_db_1,
+			&test_read_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x00, 0x00),
+			raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
+
+	define_test_client("/TP/GAR/CL/BI-25-C", test_client, service_db_1,
+			&test_read_9,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x08));
+
+	define_test_client("/TP/GAR/CL/BI-26-C", test_client, service_db_1,
+			&test_read_10,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05));
+
+	define_test_client("/TP/GAR/CL/BI-26-C/auto", test_client, service_db_1,
+			&test_read_7,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05),
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-27-C", test_client, service_db_1,
+			&test_read_11,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x04, 0x00),
+			raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x0c));
+
+	define_test_client("/TP/GAR/CL/BV-07-C", test_client, service_db_1,
+			&test_long_read_9,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BV-07-C/512B", test_client, service_db_1,
+			&test_long_read_10,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff),
+			raw_pdu(0x0c, 0x04, 0x00, 0xff, 0x01),
+			raw_pdu(0x0d, 0xff));
+
+	define_test_client("/TP/GAR/CL/BI-28-C", test_client, service_db_1,
+			&test_long_read_11,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x02));
+
+	define_test_client("/TP/GAR/CL/BI-29-C", test_client, service_db_1,
+			&test_long_read_12,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x07));
+
+	define_test_client("/TP/GAR/CL/BI-30-C", test_client, service_db_1,
+			&test_long_read_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01));
+
+	define_test_client("/TP/GAR/CL/BI-31-C", test_client, service_db_1,
+			&test_long_read_13,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x08));
+
+	define_test_client("/TP/GAR/CL/BI-32-C", test_client, service_db_1,
+			&test_long_read_14,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05));
+
+	define_test_client("/TP/GAR/CL/BI-32-C/auto", test_client, service_db_1,
+			&test_long_read_9,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05),
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x0d, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAR/CL/BI-33-C", test_client, service_db_1,
+			&test_long_read_15,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+			raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x0c));
+
+	define_test_client("/TP/GAR/CL/BI-34-C", test_client, service_db_1,
+			&test_read_12,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x80));
+
+	define_test_client("/TP/GAR/CL/BI-35-C", test_client, service_db_1,
+			&test_read_12,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x80));
+
+	define_test_client("/TP/GAW/CL/BV-01-C", test_client, service_db_1,
+			&test_write_without_response_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x52, 0x07, 0x00, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAW/CL/BV-02-C", test_client, service_db_1,
+			&test_signed_write_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0xd2, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00,
+				0x00, 0x00, 0x31, 0x1f, 0x0a, 0xcd, 0x1c, 0x3a,
+				0x5b, 0x0a));
+
+	define_test_client("/TP/GAW/CL/BV-02-C/seclevel", test_client,
+			service_db_1, &test_signed_write_seclevel_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x52, 0x07, 0x00, 0x01, 0x02, 0x03));
+
+	define_test_client("/TP/GAW/CL/BV-03-C", test_client, service_db_1,
+			&test_write_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_client("/TP/GAW/CL/BI-02-C", test_client, service_db_1,
+			&test_write_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+	define_test_client("/TP/GAW/CL/BI-03-C", test_client, service_db_1,
+			&test_write_3,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x07, 0x00, 0x03));
+
+	define_test_client("/TP/GAW/CL/BI-04-C", test_client, service_db_1,
+			&test_write_4,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x07, 0x00, 0x08));
+
+	define_test_client("/TP/GAW/CL/BI-05-C", test_client, service_db_1,
+			&test_write_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x07, 0x00, 0x05));
+
+	define_test_client("/TP/GAW/CL/BI-05-C/auto", test_client, service_db_1,
+			&test_write_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x07, 0x00, 0x05),
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_client("/TP/GAW/CL/BI-06-C", test_client, service_db_1,
+			&test_write_6,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x07, 0x00, 0x0c));
+
+	define_test_server("/TP/GAW/SR/BV-07-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19),
+			raw_pdu(0x0a, 0x03, 0x00),
+			raw_pdu(0x0b, 0x42, 0x6c, 0x75, 0x65, 0x5a));
+
+	define_test_server("/TP/GAW/SR/BV-07-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19),
+			raw_pdu(0x0a, 0xc4, 0x00),
+			raw_pdu(0x0b, '1', '1', '1', '1', '1', '2', '2', '2',
+				'2', '2', '3', '3', '3', '3', '3', '4', '4',
+				'4', '4', '4', '5'));
+
+	define_test_server("/TP/GAW/SR/BV-03-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x03, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_server("/TP/GAW/SR/BV-03-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x82, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_server("/TP/GAW/SR/BI-02-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-02-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x0f, 0xf0, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x0f, 0xf0, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-03-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x13, 0xf0, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x13, 0xf0, 0x03));
+
+	define_test_server("/TP/GAW/SR/BI-03-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x04, 0x00, 0x03));
+
+	define_test_client("/TP/GAW/CL/BV-05-C", test_client, service_db_1,
+			&test_long_write_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff),
+			raw_pdu(0x17, 0x07, 0x00, 0x00, 0x00,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff),
+			raw_pdu(0x16, 0x07, 0x00, 0xfb, 0x01,
+				0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x17, 0x07, 0x00, 0xfb, 0x01,
+				0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-07-C", test_client, service_db_1,
+			&test_long_write_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-08-C", test_client, service_db_1,
+			&test_long_write_3,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x03, 0x00, 0x03),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-11-C", test_client, service_db_1,
+			&test_long_write_4,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x08),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-12-C", test_client, service_db_1,
+			&test_long_write_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x05),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-13-C", test_client, service_db_1,
+			&test_long_write_6,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x0c),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-05-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x16, 0x03, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x17, 0x03, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-05-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x16, 0x82, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x17, 0x82, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BI-07-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-07-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-08-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x05, 0x00, 0x03));
+
+	define_test_server("/TP/GAW/SR/BI-08-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x73, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x73, 0x00, 0x03));
+
+	define_test_client("/TP/GAW/CL/BV-06-C", test_client, service_db_1,
+			&test_reliable_write_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-14-C", test_client, service_db_1,
+			&test_reliable_write_2,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-15-C", test_client, service_db_1,
+			&test_reliable_write_3,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x03, 0x00, 0x03),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-17-C", test_client, service_db_1,
+			&test_reliable_write_4,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x08),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-18-C", test_client, service_db_1,
+			&test_reliable_write_5,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x05),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-18-C/auto", test_client, service_db_1,
+			&test_reliable_write_1,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x05),
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-19-C", test_client, service_db_1,
+			&test_reliable_write_6,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x07, 0x00, 0x0c),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-06-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-06-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-10-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x16, 0x15, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x15, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x16, 0x03, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x17, 0x03, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x16, 0x15, 0xf0, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x17, 0x15, 0xf0, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-10-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x16, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x17, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x16, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x17, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x16, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x17, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BI-14-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-14-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-15-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x05, 0x00, 0x03));
+
+	define_test_server("/TP/GAW/SR/BI-15-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x73, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x73, 0x00, 0x03));
+
+	define_test_client("/TP/GAW/CL/BV-08-C", test_client, service_db_1,
+			&test_write_7,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_client("/TP/GAW/CL/BI-20-C", test_client, service_db_1,
+			&test_write_8,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+	define_test_client("/TP/GAW/CL/BI-21-C", test_client, service_db_1,
+			&test_write_9,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x08, 0x00, 0x03));
+
+	define_test_client("/TP/GAW/CL/BI-22-C", test_client, service_db_1,
+			&test_write_10,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x08, 0x00, 0x08));
+
+	define_test_client("/TP/GAW/CL/BI-23-C", test_client, service_db_1,
+			&test_write_11,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x08, 0x00, 0x05));
+
+	define_test_client("/TP/GAW/CL/BI-23-C/auto", test_client, service_db_1,
+			&test_write_7,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x08, 0x00, 0x05),
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_client("/TP/GAW/CL/BI-24-C", test_client, service_db_1,
+			&test_write_12,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x08, 0x00, 0x0c));
+
+	define_test_server("/TP/GAW/SR/BV-08-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_server("/TP/GAW/SR/BV-08-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x83, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x13));
+
+	define_test_server("/TP/GAW/SR/BI-20-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-20-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x0f, 0xf0, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x0f, 0xf0, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-21-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x13, 0xf0, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x13, 0xf0, 0x03));
+
+	define_test_server("/TP/GAW/SR/BI-21-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x12, 0x04, 0x00, 0x03));
+
+	define_test_client("/TP/GAW/CL/BV-09-C", test_client, service_db_1,
+			&test_long_write_7,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x08, 0x00, 0x00, 0x00,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff),
+			raw_pdu(0x17, 0x08, 0x00, 0x00, 0x00,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff),
+			raw_pdu(0x16, 0x08, 0x00, 0xfb, 0x01,
+				0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x17, 0x08, 0x00, 0xfb, 0x01,
+				0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-25-C", test_client, service_db_1,
+			&test_long_write_8,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-26-C", test_client, service_db_1,
+			&test_long_write_9,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x08, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x08, 0x00, 0x03),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-29-C", test_client, service_db_1,
+			&test_long_write_10,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x08, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x08, 0x00, 0x08),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-30-C", test_client, service_db_1,
+			&test_long_write_11,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x08, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x08, 0x00, 0x05),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_client("/TP/GAW/CL/BI-31-C", test_client, service_db_1,
+			&test_long_write_12,
+			SERVICE_DATA_1_PDUS,
+			raw_pdu(0x16, 0x08, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+			raw_pdu(0x01, 0x16, 0x08, 0x00, 0x0c),
+			raw_pdu(0x18, 0x00),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-09-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x17, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x16, 0x04, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x17, 0x04, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BV-09-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x17, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x16, 0x83, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x17, 0x83, 0x00, 0x3f, 0x00, 0xff),
+			raw_pdu(0x18, 0x01),
+			raw_pdu(0x19));
+
+	define_test_server("/TP/GAW/SR/BI-25-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-25-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+	define_test_server("/TP/GAW/SR/BI-26-C/small", test_server,
+			ts_small_db, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x13, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x13, 0xf0, 0x03));
+
+	define_test_server("/TP/GAW/SR/BI-26-C/large-1", test_server,
+			ts_large_db_1, NULL,
+			raw_pdu(0x03, 0x00, 0x02),
+			raw_pdu(0x16, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+			raw_pdu(0x01, 0x16, 0x04, 0x00, 0x03));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-gattrib.c b/bluez/repo/unit/test-gattrib.c
new file mode 100644
index 0000000..416e596
--- /dev/null
+++ b/bluez/repo/unit/test-gattrib.c
@@ -0,0 +1,565 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google, 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 <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/log.h"
+
+#define DEFAULT_MTU 23
+
+#define data(args...) ((const unsigned char[]) { args })
+
+struct test_pdu {
+	bool valid;
+	bool sent;
+	bool received;
+	const uint8_t *data;
+	size_t size;
+};
+
+#define pdu(args...)				\
+	{					\
+		.valid = true,			\
+		.sent = false,			\
+		.received = false,		\
+		.data = data(args),		\
+		.size = sizeof(data(args)),	\
+	}
+
+struct context {
+	GMainLoop *main_loop;
+	GIOChannel *att_io;
+	GIOChannel *server_io;
+	GAttrib *att;
+};
+
+static void setup_context(struct context *cxt, gconstpointer data)
+{
+	int err, sv[2];
+
+	cxt->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(cxt->main_loop != NULL);
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	cxt->att_io = g_io_channel_unix_new(sv[0]);
+	g_assert(cxt->att_io != NULL);
+
+	g_io_channel_set_close_on_unref(cxt->att_io, TRUE);
+
+	cxt->server_io = g_io_channel_unix_new(sv[1]);
+	g_assert(cxt->server_io != NULL);
+
+	g_io_channel_set_close_on_unref(cxt->server_io, TRUE);
+	g_io_channel_set_encoding(cxt->server_io, NULL, NULL);
+	g_io_channel_set_buffered(cxt->server_io, FALSE);
+
+	cxt->att = g_attrib_new(cxt->att_io, DEFAULT_MTU, false);
+	g_assert(cxt->att != NULL);
+}
+
+static void teardown_context(struct context *cxt, gconstpointer data)
+{
+	if (cxt->att)
+		g_attrib_unref(cxt->att);
+
+	g_io_channel_unref(cxt->server_io);
+
+	g_io_channel_unref(cxt->att_io);
+
+	g_main_loop_unref(cxt->main_loop);
+}
+
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%s%s\n", prefix, str);
+}
+
+static void destroy_canary_increment(gpointer data)
+{
+	int *canary = data;
+	(*canary)++;
+}
+
+static void test_refcount(struct context *cxt, gconstpointer unused)
+{
+	GAttrib *extra_ref;
+	int destroy_canary = 0;
+
+	g_attrib_set_destroy_function(cxt->att, destroy_canary_increment,
+							       &destroy_canary);
+
+	extra_ref = g_attrib_ref(cxt->att);
+
+	g_assert(extra_ref == cxt->att);
+
+	g_assert(destroy_canary == 0);
+
+	g_attrib_unref(extra_ref);
+
+	g_assert(destroy_canary == 0);
+
+	g_attrib_unref(cxt->att);
+
+	g_assert(destroy_canary == 1);
+
+	/* Avoid a double-free from the teardown function */
+	cxt->att = NULL;
+}
+
+static void test_get_channel(struct context *cxt, gconstpointer unused)
+{
+	GIOChannel *chan;
+
+	chan = g_attrib_get_channel(cxt->att);
+
+	g_assert(chan == cxt->att_io);
+}
+
+struct expect_response {
+	struct test_pdu expect;
+	struct test_pdu respond;
+	GSourceFunc receive_cb;
+	gpointer user_data;
+};
+
+static gboolean test_client(GIOChannel *channel, GIOCondition cond,
+								  gpointer data)
+{
+	struct expect_response *cr = data;
+	int fd;
+	uint8_t buf[256];
+	ssize_t len;
+	int cmp;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+	g_assert_cmpint(len, ==, cr->expect.size);
+
+	if (g_test_verbose())
+		util_hexdump('?', cr->expect.data,  cr->expect.size,
+						   test_debug, "test_client: ");
+
+	cmp = memcmp(cr->expect.data, buf, len);
+
+	g_assert(cmp == 0);
+
+	cr->expect.received = true;
+
+	if (cr->receive_cb != NULL)
+		cr->receive_cb(cr->user_data);
+
+	if (cr->respond.valid) {
+		if (g_test_verbose())
+			util_hexdump('<', cr->respond.data, cr->respond.size,
+						   test_debug, "test_client: ");
+		len = write(fd, cr->respond.data, cr->respond.size);
+
+		g_assert_cmpint(len, ==, cr->respond.size);
+
+		cr->respond.sent = true;
+	}
+
+	return TRUE;
+}
+
+struct result_data {
+	guint8 status;
+	guint8 *pdu;
+	guint16 len;
+	GSourceFunc complete_cb;
+	gpointer user_data;
+};
+
+static void result_canary(guint8 status, const guint8 *pdu, guint16 len,
+								gpointer data)
+{
+	struct result_data *result = data;
+
+	result->status = status;
+	result->pdu = g_malloc0(len);
+	memcpy(result->pdu, pdu, len);
+	result->len = len;
+
+	if (g_test_verbose())
+		util_hexdump('<', pdu, len, test_debug, "result_canary: ");
+
+	if (result->complete_cb != NULL)
+		result->complete_cb(result->user_data);
+}
+
+static gboolean context_stop_main_loop(gpointer user_data)
+{
+	struct context *cxt = user_data;
+
+	g_main_loop_quit(cxt->main_loop);
+	return FALSE;
+}
+
+static void test_send(struct context *cxt, gconstpointer unused)
+{
+	int cmp;
+	struct result_data results;
+	struct expect_response data = {
+		.expect = pdu(0x02, 0x00, 0x02),
+		.respond = pdu(0x03, 0x02, 0x03, 0x04),
+		.receive_cb = NULL,
+		.user_data = NULL,
+	};
+
+	g_io_add_watch(cxt->server_io, G_IO_IN | G_IO_HUP | G_IO_ERR |
+						G_IO_NVAL, test_client, &data);
+
+	results.complete_cb = context_stop_main_loop;
+	results.user_data = cxt;
+
+	g_attrib_send(cxt->att, 0, data.expect.data, data.expect.size,
+				      result_canary, (gpointer) &results, NULL);
+
+	g_main_loop_run(cxt->main_loop);
+
+	g_assert(results.pdu != NULL);
+
+	g_assert_cmpint(results.len, ==, data.respond.size);
+
+	cmp = memcmp(results.pdu, data.respond.data, results.len);
+
+	g_assert(cmp == 0);
+
+	g_free(results.pdu);
+}
+
+struct event_info {
+	struct context *context;
+	int event_id;
+};
+
+static gboolean cancel_existing_attrib_event(gpointer user_data)
+{
+	struct event_info *info = user_data;
+	gboolean canceled;
+
+	canceled = g_attrib_cancel(info->context->att, info->event_id);
+
+	g_assert(canceled);
+
+	g_idle_add(context_stop_main_loop, info->context);
+
+	return FALSE;
+}
+
+static void test_cancel(struct context *cxt, gconstpointer unused)
+{
+	gboolean canceled;
+	struct result_data results;
+	struct event_info info;
+	struct expect_response data = {
+		.expect = pdu(0x02, 0x00, 0x02),
+		.respond = pdu(0x03, 0x02, 0x03, 0x04),
+	};
+
+	g_io_add_watch(cxt->server_io,
+				      G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							    test_client, &data);
+
+	results.pdu = NULL;
+
+	info.context = cxt;
+	info.event_id = g_attrib_send(cxt->att, 0, data.expect.data,
+						data.expect.size, result_canary,
+								&results, NULL);
+
+	data.receive_cb = cancel_existing_attrib_event;
+	data.user_data = &info;
+
+	g_main_loop_run(cxt->main_loop);
+
+	g_assert(results.pdu == NULL);
+
+	results.pdu = NULL;
+	data.expect.received = false;
+	data.respond.sent = false;
+
+	info.event_id = g_attrib_send(cxt->att, 0, data.expect.data,
+						data.expect.size, result_canary,
+								&results, NULL);
+
+	canceled = g_attrib_cancel(cxt->att, info.event_id);
+	g_assert(canceled);
+
+	g_idle_add(context_stop_main_loop, info.context);
+
+	g_main_loop_run(cxt->main_loop);
+
+	g_assert(!data.expect.received);
+	g_assert(!data.respond.sent);
+	g_assert(results.pdu == NULL);
+
+	/* Invalid ID */
+	canceled = g_attrib_cancel(cxt->att, 42);
+	g_assert(!canceled);
+}
+
+static void send_test_pdus(gpointer context, struct test_pdu *pdus)
+{
+	struct context *cxt = context;
+	size_t len;
+	int fd;
+	struct test_pdu *cur_pdu;
+
+	fd = g_io_channel_unix_get_fd(cxt->server_io);
+
+	for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++)
+		cur_pdu->sent = false;
+
+	for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++) {
+		if (g_test_verbose())
+			util_hexdump('>', cur_pdu->data, cur_pdu->size,
+						test_debug, "send_test_pdus: ");
+		len = write(fd, cur_pdu->data, cur_pdu->size);
+		g_assert_cmpint(len, ==, cur_pdu->size);
+		cur_pdu->sent = true;
+	}
+
+	g_idle_add(context_stop_main_loop, cxt);
+	g_main_loop_run(cxt->main_loop);
+}
+
+#define PDU_MTU_RESP pdu(ATT_OP_MTU_RESP, 0x17)
+#define PDU_FIND_INFO_REQ pdu(ATT_OP_FIND_INFO_REQ, 0x01, 0x00, 0xFF, 0xFF)
+#define PDU_NO_ATT_ERR pdu(ATT_OP_ERROR, ATT_OP_FIND_INFO_REQ, 0x00, 0x00, 0x0A)
+#define PDU_IND_NODATA pdu(ATT_OP_HANDLE_IND, 0x01, 0x00)
+#define PDU_INVALID_IND pdu(ATT_OP_HANDLE_IND, 0x14)
+#define PDU_IND_DATA pdu(ATT_OP_HANDLE_IND, 0x14, 0x00, 0x01)
+
+struct expect_test_data {
+	struct test_pdu *expected;
+	GAttrib *att;
+};
+
+static void notify_canary_expect(const guint8 *pdu, guint16 len, gpointer data)
+{
+	struct expect_test_data *expect = data;
+	struct test_pdu *expected = expect->expected;
+	int cmp;
+
+	if (g_test_verbose())
+		util_hexdump('<', pdu, len, test_debug,
+						      "notify_canary_expect: ");
+
+	while (expected->valid && expected->received)
+		expected++;
+
+	g_assert(expected->valid);
+
+	if (g_test_verbose())
+		util_hexdump('?', expected->data, expected->size, test_debug,
+						      "notify_canary_expect: ");
+
+	g_assert_cmpint(expected->size, ==, len);
+
+	cmp = memcmp(pdu, expected->data, expected->size);
+
+	g_assert(cmp == 0);
+
+	expected->received = true;
+
+	if (pdu[0] == ATT_OP_FIND_INFO_REQ) {
+		struct test_pdu no_attributes = PDU_NO_ATT_ERR;
+		int reqid;
+
+		reqid = g_attrib_send(expect->att, 0, no_attributes.data,
+					  no_attributes.size, NULL, NULL, NULL);
+		g_assert(reqid != 0);
+	}
+}
+
+static void test_register(struct context *cxt, gconstpointer user_data)
+{
+	guint reg_id;
+	gboolean canceled;
+	struct test_pdu pdus[] = {
+		/*
+		 * Unmatched PDU opcode
+		 * Unmatched handle (GATTRIB_ALL_REQS) */
+		PDU_FIND_INFO_REQ,
+		/*
+		 * Matched PDU opcode
+		 * Unmatched handle (GATTRIB_ALL_HANDLES) */
+		PDU_IND_NODATA,
+		/*
+		 * Matched PDU opcode
+		 * Invalid length? */
+		PDU_INVALID_IND,
+		/*
+		 * Matched PDU opcode
+		 * Matched handle */
+		PDU_IND_DATA,
+		{ },
+	};
+	struct test_pdu req_pdus[] = { PDU_FIND_INFO_REQ, { } };
+	struct test_pdu all_ind_pdus[] = {
+		PDU_IND_NODATA,
+		PDU_INVALID_IND,
+		PDU_IND_DATA,
+		{ },
+	};
+	struct test_pdu followed_ind_pdus[] = { PDU_IND_DATA, { } };
+	struct test_pdu *current_pdu;
+	struct expect_test_data expect;
+
+	expect.att = cxt->att;
+
+	/*
+	 * Without registering anything, should be able to ignore everything but
+	 * an unexpected response. */
+	send_test_pdus(cxt, pdus);
+
+	if (g_test_verbose())
+		g_print("ALL_REQS, ALL_HANDLES\r\n");
+
+	expect.expected = req_pdus;
+	reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_REQS,
+				      GATTRIB_ALL_HANDLES, notify_canary_expect,
+								 &expect, NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	canceled = g_attrib_unregister(cxt->att, reg_id);
+
+	g_assert(canceled);
+
+	for (current_pdu = req_pdus; current_pdu->valid; current_pdu++)
+		g_assert(current_pdu->received);
+
+	if (g_test_verbose())
+		g_print("IND, ALL_HANDLES\r\n");
+
+	expect.expected = all_ind_pdus;
+	reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND,
+				      GATTRIB_ALL_HANDLES, notify_canary_expect,
+								 &expect, NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	canceled = g_attrib_unregister(cxt->att, reg_id);
+
+	g_assert(canceled);
+
+	for (current_pdu = all_ind_pdus; current_pdu->valid; current_pdu++)
+		g_assert(current_pdu->received);
+
+	if (g_test_verbose())
+		g_print("IND, 0x0014\r\n");
+
+	expect.expected = followed_ind_pdus;
+	reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND, 0x0014,
+					notify_canary_expect, &expect, NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	canceled = g_attrib_unregister(cxt->att, reg_id);
+
+	g_assert(canceled);
+
+	for (current_pdu = followed_ind_pdus; current_pdu->valid; current_pdu++)
+		g_assert(current_pdu->received);
+
+	canceled = g_attrib_unregister(cxt->att, reg_id);
+
+	g_assert(!canceled);
+}
+
+static void test_buffers(struct context *cxt, gconstpointer unused)
+{
+	size_t buflen;
+	uint8_t *buf;
+	gboolean success;
+
+	buf = g_attrib_get_buffer(cxt->att, &buflen);
+	g_assert(buf != 0);
+	g_assert_cmpint(buflen, ==, DEFAULT_MTU);
+
+	success = g_attrib_set_mtu(cxt->att, 5);
+	g_assert(!success);
+
+	success = g_attrib_set_mtu(cxt->att, 255);
+	g_assert(success);
+
+	buf = g_attrib_get_buffer(cxt->att, &buflen);
+	g_assert(buf != 0);
+	g_assert_cmpint(buflen, ==, 255);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__btd_log_init("*", 0);
+
+	/*
+	 * Test the GAttrib API behavior
+	 */
+	g_test_add("/gattrib/refcount", struct context, NULL, setup_context,
+					      test_refcount, teardown_context);
+	g_test_add("/gattrib/get_channel", struct context, NULL, setup_context,
+					    test_get_channel, teardown_context);
+	g_test_add("/gattrib/send", struct context, NULL, setup_context,
+						   test_send, teardown_context);
+	g_test_add("/gattrib/cancel", struct context, NULL, setup_context,
+						 test_cancel, teardown_context);
+	g_test_add("/gattrib/register", struct context, NULL, setup_context,
+					       test_register, teardown_context);
+	g_test_add("/gattrib/buffers", struct context, NULL, setup_context,
+						test_buffers, teardown_context);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-gdbus-client.c b/bluez/repo/unit/test-gdbus-client.c
new file mode 100644
index 0000000..dd17c00
--- /dev/null
+++ b/bluez/repo/unit/test-gdbus-client.c
@@ -0,0 +1,997 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  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>
+
+#include "gdbus/gdbus.h"
+
+#include "src/shared/tester.h"
+
+#define SERVICE_NAME "org.bluez.unit.test-gdbus-client"
+#define SERVICE_NAME1 "org.bluez.unit.test-gdbus-client1"
+#define SERVICE_PATH "/org/bluez/unit/test_gdbus_client"
+
+struct context {
+	DBusConnection *dbus_conn;
+	GDBusClient *dbus_client;
+	GDBusProxy *proxy;
+	void *data;
+	gboolean client_ready;
+	guint timeout_source;
+};
+
+static const GDBusMethodTable methods[] = {
+	{ }
+};
+
+static const GDBusSignalTable signals[] = {
+	{ }
+};
+
+static const GDBusPropertyTable properties[] = {
+	{ }
+};
+
+static struct context *create_context(void)
+{
+	struct context *context = g_new0(struct context, 1);
+	DBusError err;
+
+	dbus_error_init(&err);
+
+	context->dbus_conn = g_dbus_setup_private(DBUS_BUS_SESSION,
+							SERVICE_NAME, &err);
+	if (context->dbus_conn == NULL) {
+		if (dbus_error_is_set(&err)) {
+			tester_debug("D-Bus setup failed: %s", err.message);
+			dbus_error_free(&err);
+		}
+
+		g_free(context);
+		tester_test_abort();
+		return NULL;
+	}
+
+	/* Avoid D-Bus library calling _exit() before next test finishes. */
+	dbus_connection_set_exit_on_disconnect(context->dbus_conn, FALSE);
+
+	g_dbus_attach_object_manager(context->dbus_conn);
+	context->client_ready = FALSE;
+
+	return context;
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context == NULL)
+		return;
+
+	tester_test_passed();
+
+	if (context->timeout_source > 0)
+		g_source_remove(context->timeout_source);
+
+	g_dbus_detach_object_manager(context->dbus_conn);
+
+	g_dbus_unregister_interface(context->dbus_conn,
+					SERVICE_PATH, SERVICE_NAME);
+
+	dbus_connection_flush(context->dbus_conn);
+	dbus_connection_close(context->dbus_conn);
+	dbus_connection_unref(context->dbus_conn);
+
+	g_free(context->data);
+	g_free(context);
+}
+
+static gboolean timeout_handler(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	tester_debug("timeout triggered");
+
+	context->timeout_source = 0;
+
+	g_dbus_client_unref(context->dbus_client);
+
+	return FALSE;
+}
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+	struct context *context = user_data;
+
+	tester_debug("service connected");
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+	struct context *context = user_data;
+
+	tester_debug("service disconnected");
+
+	destroy_context(context);
+}
+
+static void simple_client(const void *data)
+{
+	struct context *context = create_context();
+
+	if (context == NULL)
+		return;
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_connect_watch(context->dbus_client,
+						connect_handler, context);
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+}
+
+static void client_connect_disconnect(const void *data)
+{
+	struct context *context = create_context();
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, properties, NULL, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_connect_watch(context->dbus_client,
+						connect_handler, context);
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+
+	context->timeout_source = g_timeout_add_seconds(10, timeout_handler,
+								context);
+}
+
+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 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 gboolean get_dict(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	DBusMessageIter dict;
+	const char *string = "value";
+	dbus_bool_t boolean = TRUE;
+
+	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, "String", DBUS_TYPE_STRING, &string);
+	dict_append_entry(&dict, "Boolean", DBUS_TYPE_BOOLEAN, &boolean);
+
+	dbus_message_iter_close_container(iter, &dict);
+
+	return TRUE;
+}
+
+static void proxy_get_dict(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter, dict, var1, var2, entry1, entry2;
+	const char *string;
+	dbus_bool_t boolean;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "Dict", &iter));
+	g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY);
+
+	dbus_message_iter_recurse(&iter, &dict);
+	g_assert(dbus_message_iter_get_arg_type(&dict) ==
+							DBUS_TYPE_DICT_ENTRY);
+
+	dbus_message_iter_recurse(&dict, &entry1);
+	g_assert(dbus_message_iter_get_arg_type(&entry1) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(&entry1, &string);
+	g_assert(g_strcmp0(string, "String") == 0);
+
+	dbus_message_iter_next(&entry1);
+	g_assert(dbus_message_iter_get_arg_type(&entry1) == DBUS_TYPE_VARIANT);
+
+	dbus_message_iter_recurse(&entry1, &var1);
+	g_assert(dbus_message_iter_get_arg_type(&var1) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(&var1, &string);
+	g_assert(g_strcmp0(string, "value") == 0);
+
+	dbus_message_iter_next(&dict);
+	g_assert(dbus_message_iter_get_arg_type(&dict) ==
+							DBUS_TYPE_DICT_ENTRY);
+
+	dbus_message_iter_recurse(&dict, &entry2);
+	g_assert(dbus_message_iter_get_arg_type(&entry2) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(&entry2, &string);
+	g_assert(g_strcmp0(string, "Boolean") == 0);
+
+	dbus_message_iter_next(&entry2);
+	g_assert(dbus_message_iter_get_arg_type(&entry2) == DBUS_TYPE_VARIANT);
+
+	dbus_message_iter_recurse(&entry2, &var2);
+	g_assert(dbus_message_iter_get_arg_type(&var2) == DBUS_TYPE_BOOLEAN);
+
+	dbus_message_iter_get_basic(&var2, &boolean);
+	g_assert(boolean == TRUE);
+
+	dbus_message_iter_next(&dict);
+	g_assert(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_INVALID);
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static void client_get_dict_property(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable dict_properties[] = {
+		{ "Dict", "a{sv}", get_dict },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, dict_properties,
+				NULL, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+	g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_get_dict,
+						NULL, NULL, context);
+}
+
+static void proxy_get_string(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+	const char *string;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "String", &iter));
+	g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
+
+	if (context->proxy) {
+		g_assert(context->proxy == proxy);
+		g_dbus_proxy_unref(context->proxy);
+	}
+
+	dbus_message_iter_get_basic(&iter, &string);
+	g_assert_cmpstr(string, ==, "value");
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static gboolean get_string(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct context *context = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &context->data);
+
+	return TRUE;
+}
+
+static void client_get_string_property(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	context->data = g_strdup("value");
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+	g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_get_string,
+						NULL, NULL, context);
+}
+
+static void proxy_get_boolean(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+	dbus_bool_t value;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "Boolean", &iter));
+	g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BOOLEAN);
+
+	dbus_message_iter_get_basic(&iter, &value);
+	g_assert(value == TRUE);
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static gboolean get_boolean(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = TRUE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static void client_get_boolean_property(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable boolean_properties[] = {
+		{ "Boolean", "b", get_boolean },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, boolean_properties,
+				NULL, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+						proxy_get_boolean,
+						NULL, NULL, context);
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+}
+
+static void proxy_get_array(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter, entry;
+	const char *value1, *value2;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "Array", &iter));
+	g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY);
+
+	dbus_message_iter_recurse(&iter, &entry);
+	g_assert(dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(&entry, &value1);
+	g_assert(g_strcmp0(value1, "value1") == 0);
+
+	dbus_message_iter_next(&entry);
+	g_assert(dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(&entry, &value2);
+	g_assert(g_strcmp0(value2, "value2") == 0);
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static gboolean get_array(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	const char *value[2] = { "value1", "value2" };
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+	dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &value[0]);
+	dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &value[1]);
+
+	dbus_message_iter_close_container(iter, &array);
+
+	return TRUE;
+}
+
+static void client_get_array_property(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable array_properties[] = {
+		{ "Array", "as", get_array },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, array_properties,
+				NULL, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_get_array,
+						NULL, NULL, context);
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+}
+
+static void proxy_get_uint64(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+	guint64 value;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "Number", &iter));
+	g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT64);
+
+	dbus_message_iter_get_basic(&iter, &value);
+	g_assert(value == G_MAXUINT64);
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static gboolean get_uint64(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	guint64 value = G_MAXUINT64;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &value);
+
+	return TRUE;
+}
+
+static void client_get_uint64_property(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable uint64_properties[] = {
+		{ "Number", "t", get_uint64 },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, uint64_properties,
+				NULL, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+						proxy_get_uint64,
+						NULL, NULL, context);
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+}
+
+static void property_set_success(const DBusError *err, void *user_data)
+{
+	g_assert(!dbus_error_is_set(err));
+}
+
+static void proxy_set_string(GDBusProxy *proxy, void *user_data)
+{
+	DBusMessageIter iter;
+	const char *string;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "String", &iter));
+	g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(&iter, &string);
+	g_assert(g_strcmp0(string, "value") == 0);
+
+	string = "value1";
+	g_assert(g_dbus_proxy_set_property_basic(proxy, "String",
+					DBUS_TYPE_STRING, &string,
+					property_set_success, user_data,
+					NULL));
+}
+
+static void property_string_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct context *context = user_data;
+	const char *string;
+
+	tester_debug("property %s changed", name);
+
+	g_assert(g_strcmp0(name, "String") == 0);
+	g_assert(dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(iter, &string);
+	g_assert(g_strcmp0(string, "value1") == 0);
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static void set_string(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct context *context = data;
+	const char *string;
+
+	g_assert(dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING);
+
+	dbus_message_iter_get_basic(iter, &string);
+	g_assert(g_strcmp0(string, "value1") == 0);
+
+	g_free(context->data);
+	context->data = g_strdup(string);
+
+	g_dbus_emit_property_changed(context->dbus_conn, SERVICE_PATH,
+						SERVICE_NAME, "String");
+
+	g_dbus_pending_property_success(id);
+}
+
+static void client_set_string_property(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, set_string },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	context->data = g_strdup("value");
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+	g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_set_string,
+						NULL, property_string_changed,
+						context);
+}
+
+static gboolean string_exists(const GDBusPropertyTable *property, void *data)
+{
+	struct context *context = data;
+
+	return context->data != NULL;
+}
+
+static gboolean timeout_test(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	tester_debug("timeout triggered");
+
+	context->timeout_source = 0;
+
+	g_assert_not_reached();
+
+	return FALSE;
+}
+
+static gboolean emit_string_change(void *user_data)
+{
+	struct context *context = user_data;
+
+	context->data = g_strdup("value1");
+
+	g_dbus_emit_property_changed(context->dbus_conn, SERVICE_PATH,
+						SERVICE_NAME, "String");
+
+	context->timeout_source = g_timeout_add_seconds(2, timeout_test,
+								context);
+
+	return FALSE;
+}
+
+static void proxy_string_changed(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusMessageIter iter;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(!g_dbus_proxy_get_property(proxy, "String", &iter));
+
+	g_idle_add(emit_string_change, context);
+}
+
+static void client_string_changed(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, NULL, string_exists },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+						proxy_string_changed, NULL,
+						property_string_changed,
+						context);
+}
+
+static void property_check_order(const DBusError *err, void *user_data)
+{
+	struct context *context = user_data;
+	GDBusProxy *proxy = context->proxy;
+	DBusMessageIter iter;
+	const char *string;
+
+	g_assert(!dbus_error_is_set(err));
+
+	g_assert(g_dbus_proxy_get_property(proxy, "String", &iter));
+
+	dbus_message_iter_get_basic(&iter, &string);
+	g_assert(g_strcmp0(string, "value1") == 0);
+
+	g_dbus_client_unref(context->dbus_client);
+}
+
+static void proxy_check_order(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	const char *string;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	context->proxy = proxy;
+	string = "value1";
+	g_assert(g_dbus_proxy_set_property_basic(proxy, "String",
+					DBUS_TYPE_STRING, &string,
+					property_check_order, context,
+					NULL));
+}
+
+static void client_check_order(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, set_string, string_exists },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	context->data = g_strdup("value");
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+						proxy_check_order, NULL, NULL,
+						context);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+
+	tester_debug("proxy removed");
+
+	destroy_context(context);
+}
+
+static void proxy_set_removed(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_set_removed_watch(proxy, proxy_removed, context));
+
+	context->timeout_source = g_timeout_add_seconds(2, timeout_test,
+								context);
+
+	g_dbus_unregister_interface(context->dbus_conn, SERVICE_PATH,
+								SERVICE_NAME);
+}
+
+static void client_proxy_removed(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, set_string, string_exists },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+						proxy_set_removed, NULL, NULL,
+						context);
+}
+
+static void client_no_object_manager(const void *data)
+{
+	struct context *context = create_context();
+	DBusMessageIter iter;
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, set_string, string_exists },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	context->data = g_strdup("value");
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new_full(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH,
+						NULL);
+
+	g_dbus_client_set_disconnect_watch(context->dbus_client,
+						disconnect_handler, context);
+
+	context->proxy = g_dbus_proxy_new(context->dbus_client, SERVICE_PATH,
+								SERVICE_NAME);
+
+	g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_get_string,
+						NULL, NULL, context);
+
+	g_assert(!g_dbus_proxy_get_property(context->proxy, "String", &iter));
+}
+
+static void proxy_force_disconnect(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+	DBusConnection *conn = context->data;
+
+	tester_debug("proxy %s found", g_dbus_proxy_get_interface(proxy));
+
+	g_assert(g_dbus_proxy_set_removed_watch(proxy, proxy_removed, context));
+
+	context->timeout_source = g_timeout_add_seconds(2, timeout_test,
+								context);
+
+	g_dbus_detach_object_manager(conn);
+
+	g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+
+	dbus_connection_flush(conn);
+	dbus_connection_close(conn);
+	dbus_connection_unref(conn);
+	context->data = NULL;
+}
+
+static void client_force_disconnect(const void *data)
+{
+	struct context *context = create_context();
+	DBusConnection *conn;
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, set_string, string_exists },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	conn = g_dbus_setup_private(DBUS_BUS_SESSION, SERVICE_NAME1, NULL);
+	g_assert(conn != NULL);
+
+	/* Avoid D-Bus library calling _exit() before next test finishes. */
+	dbus_connection_set_exit_on_disconnect(conn, FALSE);
+	g_dbus_attach_object_manager(conn);
+	context->data = conn;
+
+	g_dbus_register_interface(conn, SERVICE_PATH, SERVICE_NAME1,
+					methods, signals, string_properties,
+					context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME1, SERVICE_PATH);
+
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+					proxy_force_disconnect, NULL, NULL,
+					context);
+}
+
+static void client_ready_watch(GDBusClient *client, void *user_data)
+{
+	struct context *context = user_data;
+
+	context->client_ready = TRUE;
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	struct context *context = user_data;
+
+	/*
+	 * Proxy added callback should not be called after Client ready
+	 * watch. Ready means that all objects has been reported to the
+	 * upper-layer.
+	 */
+	g_assert(context->client_ready == FALSE);
+
+	destroy_context(context);
+}
+
+static void client_ready(const void *data)
+{
+	struct context *context = create_context();
+	static const GDBusPropertyTable string_properties[] = {
+		{ "String", "s", get_string, set_string, string_exists },
+		{ },
+	};
+
+	if (context == NULL)
+		return;
+
+	g_dbus_register_interface(context->dbus_conn,
+				SERVICE_PATH, SERVICE_NAME,
+				methods, signals, string_properties,
+				context, NULL);
+
+	context->dbus_client = g_dbus_client_new(context->dbus_conn,
+						SERVICE_NAME, SERVICE_PATH);
+
+	g_dbus_client_set_ready_watch(context->dbus_client, client_ready_watch,
+								context);
+	g_dbus_client_set_proxy_handlers(context->dbus_client,
+						proxy_added, NULL, NULL, context);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/gdbus/simple_client", NULL, NULL, simple_client, NULL);
+
+	tester_add("/gdbus/client_connect_disconnect", NULL, NULL,
+					client_connect_disconnect, NULL);
+
+	tester_add("/gdbus/client_get_string_property", NULL, NULL,
+					client_get_string_property, NULL);
+
+	tester_add("/gdbus/client_get_boolean_property", NULL, NULL,
+					client_get_boolean_property, NULL);
+
+	tester_add("/gdbus/client_get_uint64_property", NULL, NULL,
+					client_get_uint64_property, NULL);
+
+	tester_add("/gdbus/client_get_array_property", NULL, NULL,
+					client_get_array_property, NULL);
+
+	tester_add("/gdbus/client_get_dict_property", NULL, NULL,
+					client_get_dict_property, NULL);
+
+	tester_add("/gdbus/client_set_string_property", NULL, NULL,
+					client_set_string_property, NULL);
+
+	tester_add("/gdbus/client_string_changed", NULL, NULL,
+					client_string_changed, NULL);
+
+	tester_add("/gdbus/client_check_order", NULL, NULL, client_check_order,
+					NULL);
+
+	tester_add("/gdbus/client_proxy_removed", NULL, NULL,
+					client_proxy_removed, NULL);
+
+	tester_add("/gdbus/client_no_object_manager", NULL, NULL,
+					client_no_object_manager, NULL);
+
+	tester_add("/gdbus/client_force_disconnect", NULL, NULL,
+					client_force_disconnect, NULL);
+
+	tester_add("/gdbus/client_ready", NULL, NULL, client_ready, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-gobex-apparam.c b/bluez/repo/unit/test-gobex-apparam.c
new file mode 100644
index 0000000..7541c49
--- /dev/null
+++ b/bluez/repo/unit/test-gobex-apparam.c
@@ -0,0 +1,428 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2012  Intel Corporation.
+ *
+ *  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 <stdint.h>
+#include <string.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "util.h"
+
+#define TAG_U8 0x00
+#define TAG_U16 0x01
+#define TAG_U32 0x02
+#define TAG_U64 0x03
+#define TAG_STRING 0x04
+#define TAG_BYTES 0x05
+
+static uint8_t tag_nval_short[] = { TAG_U8 };
+static uint8_t tag_nval_data[] = { TAG_U8, 0x01 };
+static uint8_t tag_nval2_short[] = { TAG_U8, 0x01, 0x1, TAG_U16 };
+static uint8_t tag_nval2_data[] = { TAG_U8, 0x01, 0x1, TAG_U16, 0x02 };
+static uint8_t tag_uint8[] = { TAG_U8, 0x01, 0x01 };
+static uint8_t tag_uint16[] = { TAG_U16, 0x02, 0x01, 0x02 };
+static uint8_t tag_uint32[] = { TAG_U32, 0x04, 0x01, 0x02, 0x03, 0x04 };
+static uint8_t tag_uint64[] = { TAG_U64, 0x08, 0x01, 0x02, 0x03, 0x04,
+						0x05, 0x06, 0x07, 0x08 };
+static uint8_t tag_string[] = { TAG_STRING, 0x04, 'A', 'B', 'C', '\0' };
+static uint8_t tag_bytes[257] = { TAG_BYTES, 0xFF };
+static uint8_t tag_multi[] = { TAG_U8, 0x01, 0x01,
+				TAG_U16, 0x02, 0x01, 0x02,
+				TAG_U32, 0x04, 0x01, 0x02, 0x03, 0x04,
+				TAG_U64, 0x08, 0x01, 0x02, 0x03, 0x04,
+						0x05, 0x06, 0x07, 0x08,
+				TAG_STRING, 0x04, 'A', 'B', 'C', '\0' };
+
+
+static GObexApparam *parse_and_decode(const void *data, gsize size)
+{
+	GObexApparam *apparam;
+	guint8 encoded[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_decode(data, size);
+
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, encoded, sizeof(encoded));
+
+	assert_memequal(data, size, encoded, len);
+
+	return apparam;
+}
+
+static void test_apparam_nval_short(void)
+{
+	GObexApparam *apparam;
+
+	apparam = g_obex_apparam_decode(tag_nval_short,
+						sizeof(tag_nval_short));
+
+	g_assert(apparam == NULL);
+}
+
+static void test_apparam_nval_data(void)
+{
+	GObexApparam *apparam;
+
+	apparam = g_obex_apparam_decode(tag_nval_data,
+						sizeof(tag_nval_data));
+
+	g_assert(apparam == NULL);
+}
+
+static void test_apparam_nval2_short(void)
+{
+	GObexApparam *apparam;
+
+	apparam = g_obex_apparam_decode(tag_nval2_short,
+						sizeof(tag_nval2_short));
+
+	g_assert(apparam == NULL);
+}
+
+static void test_apparam_nval2_data(void)
+{
+	GObexApparam *apparam;
+
+	apparam = g_obex_apparam_decode(tag_nval2_data,
+						sizeof(tag_nval2_data));
+
+	g_assert(apparam == NULL);
+}
+
+static void test_apparam_get_uint8(void)
+{
+	GObexApparam *apparam;
+	guint8 data;
+	gboolean ret;
+
+	apparam = parse_and_decode(tag_uint8, sizeof(tag_uint8));
+
+	ret = g_obex_apparam_get_uint8(apparam, TAG_U8, &data);
+
+	g_assert(ret == TRUE);
+	g_assert(data == 0x01);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_get_uint16(void)
+{
+	GObexApparam *apparam;
+	uint16_t data;
+	gboolean ret;
+
+	apparam = parse_and_decode(tag_uint16, sizeof(tag_uint16));
+
+	ret = g_obex_apparam_get_uint16(apparam, TAG_U16, &data);
+
+	g_assert(ret == TRUE);
+	g_assert(data == 0x0102);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_get_uint32(void)
+{
+	GObexApparam *apparam;
+	uint32_t data;
+	gboolean ret;
+
+	apparam = parse_and_decode(tag_uint32, sizeof(tag_uint32));
+
+	ret = g_obex_apparam_get_uint32(apparam, TAG_U32, &data);
+
+	g_assert(ret == TRUE);
+	g_assert(data == 0x01020304);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_get_uint64(void)
+{
+	GObexApparam *apparam;
+	uint64_t data;
+	gboolean ret;
+
+	apparam = parse_and_decode(tag_uint64, sizeof(tag_uint64));
+
+	ret = g_obex_apparam_get_uint64(apparam, TAG_U64, &data);
+
+	g_assert(ret == TRUE);
+	g_assert(data == 0x0102030405060708);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_get_string(void)
+{
+	GObexApparam *apparam;
+	char *string;
+
+	apparam = parse_and_decode(tag_string, sizeof(tag_string));
+
+	string = g_obex_apparam_get_string(apparam, TAG_STRING);
+
+	g_assert(string != NULL);
+	g_assert_cmpstr(string, ==, "ABC");
+
+	g_free(string);
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_get_bytes(void)
+{
+	GObexApparam *apparam;
+	const uint8_t *data;
+	gsize len;
+	gboolean ret;
+
+	apparam = parse_and_decode(tag_bytes, sizeof(tag_bytes));
+
+	ret = g_obex_apparam_get_bytes(apparam, TAG_BYTES, &data, &len);
+
+	g_assert(ret == TRUE);
+	assert_memequal(tag_bytes + 2, sizeof(tag_bytes) - 2, data, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_get_multi(void)
+{
+	GObexApparam *apparam;
+	char *string;
+	uint8_t data8;
+	uint16_t data16;
+	uint32_t data32;
+	uint64_t data64;
+	gboolean ret;
+
+	apparam = g_obex_apparam_decode(tag_multi, sizeof(tag_multi));
+
+	g_assert(apparam != NULL);
+
+	ret = g_obex_apparam_get_uint8(apparam, TAG_U8, &data8);
+
+	g_assert(ret == TRUE);
+	g_assert(data8 == 0x01);
+
+	ret = g_obex_apparam_get_uint16(apparam, TAG_U16, &data16);
+
+	g_assert(ret == TRUE);
+	g_assert(data16 == 0x0102);
+
+	ret = g_obex_apparam_get_uint32(apparam, TAG_U32, &data32);
+
+	g_assert(ret == TRUE);
+	g_assert(data32 == 0x01020304);
+
+	ret = g_obex_apparam_get_uint64(apparam, TAG_U64, &data64);
+
+	g_assert(ret == TRUE);
+	g_assert(data64 == 0x0102030405060708);
+
+	string = g_obex_apparam_get_string(apparam, TAG_STRING);
+
+	g_assert(string != NULL);
+	g_assert_cmpstr(string, ==, "ABC");
+
+	g_free(string);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_uint8(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_uint8(NULL, TAG_U8, 0x01);
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	assert_memequal(tag_uint8, sizeof(tag_uint8), buf, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_uint16(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_uint16(NULL, TAG_U16, 0x0102);
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	assert_memequal(tag_uint16, sizeof(tag_uint16), buf, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_uint32(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_uint32(NULL, TAG_U32, 0x01020304);
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	assert_memequal(tag_uint32, sizeof(tag_uint32), buf, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_uint64(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_uint64(NULL, TAG_U64, 0x0102030405060708);
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	assert_memequal(tag_uint64, sizeof(tag_uint64), buf, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_string(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_string(NULL, TAG_STRING, "ABC");
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	assert_memequal(tag_string, sizeof(tag_string), buf, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_bytes(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_bytes(NULL, TAG_BYTES, tag_bytes + 2, 255);
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+	assert_memequal(tag_bytes, sizeof(tag_bytes), buf, len);
+
+	g_obex_apparam_free(apparam);
+}
+
+static void test_apparam_set_multi(void)
+{
+	GObexApparam *apparam;
+	guint8 buf[1024];
+	gsize len;
+
+	apparam = g_obex_apparam_set_uint8(NULL, TAG_U8, 0x01);
+
+	g_assert(apparam != NULL);
+
+	apparam = g_obex_apparam_set_uint16(apparam, TAG_U16, 0x0102);
+
+	g_assert(apparam != NULL);
+
+	apparam = g_obex_apparam_set_uint32(apparam, TAG_U32, 0x01020304);
+
+	g_assert(apparam != NULL);
+
+	apparam = g_obex_apparam_set_uint64(apparam, TAG_U64,
+							0x0102030405060708);
+
+	g_assert(apparam != NULL);
+
+	apparam = g_obex_apparam_set_string(apparam, TAG_STRING, "ABC");
+
+	g_assert(apparam != NULL);
+
+	len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
+
+	g_assert_cmpuint(len, ==, sizeof(tag_multi));
+
+	g_obex_apparam_free(apparam);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/gobex/test_apparam_nval_short",
+						test_apparam_nval_short);
+	g_test_add_func("/gobex/test_apparam_nval_data",
+						test_apparam_nval_data);
+
+	g_test_add_func("/gobex/test_apparam_nval2_short",
+						test_apparam_nval2_short);
+	g_test_add_func("/gobex/test_apparam_nval2_data",
+						test_apparam_nval2_data);
+
+	g_test_add_func("/gobex/test_apparam_get_uint8",
+						test_apparam_get_uint8);
+	g_test_add_func("/gobex/test_apparam_get_uint16",
+						test_apparam_get_uint16);
+	g_test_add_func("/gobex/test_apparam_get_uint32",
+						test_apparam_get_uint32);
+	g_test_add_func("/gobex/test_apparam_get_uint64",
+						test_apparam_get_uint64);
+	g_test_add_func("/gobex/test_apparam_get_string",
+						test_apparam_get_string);
+	g_test_add_func("/gobex/test_apparam_get_bytes",
+						test_apparam_get_bytes);
+	g_test_add_func("/gobex/test_apparam_get_multi",
+						test_apparam_get_multi);
+
+	g_test_add_func("/gobex/test_apparam_set_uint8",
+						test_apparam_set_uint8);
+	g_test_add_func("/gobex/test_apparam_set_uint16",
+						test_apparam_set_uint16);
+	g_test_add_func("/gobex/test_apparam_set_uint32",
+						test_apparam_set_uint32);
+	g_test_add_func("/gobex/test_apparam_set_uint64",
+						test_apparam_set_uint64);
+	g_test_add_func("/gobex/test_apparam_set_string",
+						test_apparam_set_string);
+	g_test_add_func("/gobex/test_apparam_set_bytes",
+						test_apparam_set_bytes);
+	g_test_add_func("/gobex/test_apparam_set_multi",
+						test_apparam_set_multi);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-gobex-header.c b/bluez/repo/unit/test-gobex-header.c
new file mode 100644
index 0000000..8705892
--- /dev/null
+++ b/bluez/repo/unit/test-gobex-header.c
@@ -0,0 +1,574 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <string.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-header.h"
+
+#include "util.h"
+
+static uint8_t hdr_connid[] = { G_OBEX_HDR_CONNECTION, 1, 2, 3, 4 };
+static uint8_t hdr_name_empty[] = { G_OBEX_HDR_NAME, 0x00, 0x03 };
+static uint8_t hdr_name_ascii[] = { G_OBEX_HDR_NAME, 0x00, 0x0b,
+				0x00, 'f', 0x00, 'o', 0x00, 'o',
+				0x00, 0x00 };
+static uint8_t hdr_name_umlaut[] = { G_OBEX_HDR_NAME, 0x00, 0x0b,
+				0x00, 0xe5, 0x00, 0xe4, 0x00, 0xf6,
+				0x00, 0x00 };
+static uint8_t hdr_body[] = { G_OBEX_HDR_BODY, 0x00, 0x07, 1, 2, 3, 4 };
+static uint8_t hdr_actionid[] = { G_OBEX_HDR_ACTION, 0xab };
+
+static uint8_t hdr_uint32_nval[] = { G_OBEX_HDR_CONNECTION, 1, 2 };
+static uint8_t hdr_unicode_nval_short[] = { G_OBEX_HDR_NAME, 0x12, 0x34,
+						0x00, 'a', 0x00, 'b',
+						0x00, 0x00 };
+static uint8_t hdr_unicode_nval_data[] = { G_OBEX_HDR_NAME, 0x00, 0x01,
+						0x00, 'a', 0x00, 'b' };
+static uint8_t hdr_bytes_nval_short[] = { G_OBEX_HDR_BODY, 0xab, 0xcd,
+						0x01, 0x02, 0x03 };
+static uint8_t hdr_bytes_nval_data[] = { G_OBEX_HDR_BODY, 0xab };
+static uint8_t hdr_bytes_nval_len[] = { G_OBEX_HDR_BODY, 0x00, 0x00 };
+static uint8_t hdr_apparam[] = { G_OBEX_HDR_APPARAM, 0x00, 0x09, 0x00, 0x04,
+						0x01, 0x02, 0x03, 0x04 };
+
+static void test_header_name_empty(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024];
+	size_t len;
+
+	header = g_obex_header_new_unicode(G_OBEX_HDR_NAME, "");
+
+	g_assert(header != NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_name_empty, sizeof(hdr_name_empty), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_name_ascii(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024];
+	size_t len;
+
+	header = g_obex_header_new_unicode(G_OBEX_HDR_NAME, "foo");
+
+	g_assert(header != NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_name_ascii, sizeof(hdr_name_ascii), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_name_umlaut(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024];
+	size_t len;
+
+	header = g_obex_header_new_unicode(G_OBEX_HDR_NAME, "åäö");
+
+	g_assert(header != NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_name_umlaut, sizeof(hdr_name_umlaut), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_bytes(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024], data[] = { 1, 2, 3, 4 };
+	size_t len;
+
+	header = g_obex_header_new_bytes(G_OBEX_HDR_BODY, data, sizeof(data));
+	g_assert(header != NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_body, sizeof(hdr_body), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_apparam(void)
+{
+	GObexHeader *header;
+	GObexApparam *apparam;
+	uint8_t buf[1024];
+	size_t len;
+
+	apparam = g_obex_apparam_set_uint32(NULL, 0, 0x01020304);
+	g_assert(apparam != NULL);
+
+	header = g_obex_header_new_apparam(apparam);
+	g_assert(header != NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_apparam, sizeof(hdr_apparam), buf, len);
+
+	g_obex_apparam_free(apparam);
+	g_obex_header_free(header);
+}
+
+static void test_header_uint8(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024];
+	size_t len;
+
+	header = g_obex_header_new_uint8(G_OBEX_HDR_ACTION, 0xab);
+
+	g_assert(header != NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_actionid, sizeof(hdr_actionid), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_uint32(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024];
+	size_t len;
+
+	header = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, 0x01020304);
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_connid, sizeof(hdr_connid), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static GObexHeader *parse_and_encode(uint8_t *buf, size_t buf_len)
+{
+	GObexHeader *header;
+	uint8_t encoded[1024];
+	size_t len;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(buf, buf_len, G_OBEX_DATA_REF, &len,
+									&err);
+	g_assert_no_error(err);
+	g_assert_cmpuint(len, ==, buf_len);
+
+	len = g_obex_header_encode(header, encoded, sizeof(encoded));
+
+	assert_memequal(buf, buf_len, encoded, len);
+
+	return header;
+}
+
+static void test_header_encode_connid(void)
+{
+	GObexHeader *header;
+	gboolean ret;
+	guint32 val;
+
+	header = parse_and_encode(hdr_connid, sizeof(hdr_connid));
+
+	ret = g_obex_header_get_uint32(header, &val);
+
+	g_assert(ret == TRUE);
+	g_assert(val == 0x01020304);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_encode_name_ascii(void)
+{
+	GObexHeader *header;
+	const char *str;
+	gboolean ret;
+
+	header = parse_and_encode(hdr_name_ascii, sizeof(hdr_name_ascii));
+
+	ret = g_obex_header_get_unicode(header, &str);
+
+	g_assert(ret == TRUE);
+	g_assert_cmpstr(str, ==, "foo");
+
+	g_obex_header_free(header);
+}
+
+static void test_header_encode_name_umlaut(void)
+{
+	GObexHeader *header;
+	const char *str;
+	gboolean ret;
+
+	header = parse_and_encode(hdr_name_umlaut, sizeof(hdr_name_umlaut));
+
+	ret = g_obex_header_get_unicode(header, &str);
+
+	g_assert(ret == TRUE);
+	g_assert_cmpstr(str, ==, "åäö");
+
+	g_obex_header_free(header);
+}
+
+static void test_header_encode_name_empty(void)
+{
+	GObexHeader *header;
+	const char *str;
+	gboolean ret;
+
+	header = parse_and_encode(hdr_name_empty, sizeof(hdr_name_empty));
+
+	ret = g_obex_header_get_unicode(header, &str);
+
+	g_assert(ret == TRUE);
+	g_assert_cmpstr(str, ==, "");
+
+	g_obex_header_free(header);
+}
+
+static void test_header_encode_body(void)
+{
+	GObexHeader *header;
+	guint8 expected[] = { 1, 2, 3, 4};
+	const guint8 *buf;
+	size_t len;
+	gboolean ret;
+
+	header = parse_and_encode(hdr_body, sizeof(hdr_body));
+
+	ret = g_obex_header_get_bytes(header, &buf, &len);
+
+	g_assert(ret == TRUE);
+	assert_memequal(expected, sizeof(expected), buf, len);
+
+	g_obex_header_free(header);
+}
+
+static void test_header_encode_apparam(void)
+{
+	GObexHeader *header;
+	GObexApparam *apparam;
+	gboolean ret;
+	guint32 data;
+
+	header = parse_and_encode(hdr_apparam, sizeof(hdr_apparam));
+
+	apparam = g_obex_header_get_apparam(header);
+	g_assert(apparam != NULL);
+
+	ret = g_obex_apparam_get_uint32(apparam, 0x00, &data);
+	g_assert(ret == TRUE);
+	g_assert(data == 0x01020304);
+
+	g_obex_apparam_free(apparam);
+	g_obex_header_free(header);
+}
+
+static void test_header_encode_actionid(void)
+{
+	GObexHeader *header;
+	gboolean ret;
+	guint8 val;
+
+	header = parse_and_encode(hdr_actionid, sizeof(hdr_actionid));
+
+	ret = g_obex_header_get_uint8(header, &val);
+
+	g_assert(ret == TRUE);
+	g_assert_cmpuint(val, ==, 0xab);
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_connid(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_connid, sizeof(hdr_connid),
+					G_OBEX_DATA_REF, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_connid));
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_name_ascii(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_name_ascii, sizeof(hdr_name_ascii),
+					G_OBEX_DATA_REF, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_name_ascii));
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_name_empty(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_name_empty, sizeof(hdr_name_empty),
+					G_OBEX_DATA_REF, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_name_empty));
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_name_umlaut(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_name_umlaut, sizeof(hdr_name_umlaut),
+					G_OBEX_DATA_REF, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_name_umlaut));
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_body(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_body, sizeof(hdr_body),
+					G_OBEX_DATA_COPY, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_body_extdata(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_body, sizeof(hdr_body),
+					G_OBEX_DATA_REF, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+
+	g_obex_header_free(header);
+}
+
+static void test_decode_header_actionid(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(hdr_actionid, sizeof(hdr_actionid),
+					G_OBEX_DATA_REF, &parsed, &err);
+	g_assert_no_error(err);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_actionid));
+
+	g_obex_header_free(header);
+}
+
+static void decode_header_nval(uint8_t *buf, size_t len)
+{
+	GObexHeader *header;
+	size_t parsed;
+	GError *err = NULL;
+
+	header = g_obex_header_decode(buf, len, G_OBEX_DATA_REF, &parsed,
+									&err);
+	g_assert_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR);
+	g_assert(header == NULL);
+	g_error_free(err);
+}
+
+static void test_decode_header_uint32_nval(void)
+{
+	decode_header_nval(hdr_uint32_nval, sizeof(hdr_uint32_nval));
+}
+
+static void test_decode_header_unicode_nval_short(void)
+{
+	decode_header_nval(hdr_unicode_nval_short,
+					sizeof(hdr_unicode_nval_short));
+}
+
+static void test_decode_header_unicode_nval_data(void)
+{
+	decode_header_nval(hdr_unicode_nval_data,
+					sizeof(hdr_unicode_nval_data));
+}
+
+static void test_decode_header_bytes_nval_short(void)
+{
+	decode_header_nval(hdr_bytes_nval_short, sizeof(hdr_bytes_nval_short));
+}
+
+static void test_decode_header_bytes_nval_data(void)
+{
+	decode_header_nval(hdr_bytes_nval_data, sizeof(hdr_bytes_nval_data));
+}
+
+static void test_decode_header_bytes_nval_len(void)
+{
+	decode_header_nval(hdr_bytes_nval_len, sizeof(hdr_bytes_nval_len));
+}
+
+static void test_decode_header_multi(void)
+{
+	GObexHeader *header;
+	GByteArray *buf;
+	size_t parsed;
+	GError *err = NULL;
+
+	buf = g_byte_array_sized_new(sizeof(hdr_connid) +
+					sizeof(hdr_name_ascii) +
+					sizeof(hdr_actionid) +
+					sizeof(hdr_body));
+
+	g_byte_array_append(buf, hdr_connid, sizeof(hdr_connid));
+	g_byte_array_append(buf, hdr_name_ascii, sizeof(hdr_name_ascii));
+	g_byte_array_append(buf, hdr_actionid, sizeof(hdr_actionid));
+	g_byte_array_append(buf, hdr_body, sizeof(hdr_body));
+
+	header = g_obex_header_decode(buf->data, buf->len, G_OBEX_DATA_REF,
+								&parsed, &err);
+	g_assert_no_error(err);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_connid));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	header = g_obex_header_decode(buf->data, buf->len, G_OBEX_DATA_REF,
+								&parsed, &err);
+	g_assert_no_error(err);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_name_ascii));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	header = g_obex_header_decode(buf->data, buf->len, G_OBEX_DATA_REF,
+								&parsed, &err);
+	g_assert_no_error(err);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_actionid));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	header = g_obex_header_decode(buf->data, buf->len, G_OBEX_DATA_REF,
+								&parsed, &err);
+	g_assert_no_error(err);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	g_byte_array_unref(buf);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/gobex/test_decode_header_connid",
+						test_decode_header_connid);
+	g_test_add_func("/gobex/test_decode_header_name_empty",
+					test_decode_header_name_empty);
+	g_test_add_func("/gobex/test_decode_header_name_ascii",
+					test_decode_header_name_ascii);
+	g_test_add_func("/gobex/test_decode_header_name_umlaut",
+					test_decode_header_name_umlaut);
+	g_test_add_func("/gobex/test_decode_header_body",
+						test_decode_header_body);
+	g_test_add_func("/gobex/test_decode_header_body_extdata",
+					test_decode_header_body_extdata);
+	g_test_add_func("/gobex/test_decode_header_actionid",
+						test_decode_header_actionid);
+	g_test_add_func("/gobex/test_decode_header_multi",
+						test_decode_header_multi);
+
+	g_test_add_func("/gobex/test_decode_header_uint32_nval",
+					test_decode_header_uint32_nval);
+	g_test_add_func("/gobex/test_decode_header_unicode_nval_short",
+					test_decode_header_unicode_nval_short);
+	g_test_add_func("/gobex/test_decode_header_unicode_nval_data",
+					test_decode_header_unicode_nval_data);
+	g_test_add_func("/gobex/test_decode_header_bytes_nval_short",
+					test_decode_header_bytes_nval_short);
+	g_test_add_func("/gobex/test_decode_header_bytes_nval_data",
+					test_decode_header_bytes_nval_data);
+	g_test_add_func("/gobex/test_decode_header_bytes_nval_len",
+					test_decode_header_bytes_nval_len);
+
+	g_test_add_func("/gobex/test_header_encode_connid",
+						test_header_encode_connid);
+	g_test_add_func("/gobex/test_header_encode_name_empty",
+					test_header_encode_name_empty);
+	g_test_add_func("/gobex/test_header_encode_name_ascii",
+					test_header_encode_name_ascii);
+	g_test_add_func("/gobex/test_header_encode_name_umlaut",
+					test_header_encode_name_umlaut);
+	g_test_add_func("/gobex/test_header_encode_body",
+						test_header_encode_body);
+	g_test_add_func("/gobex/test_header_encode_actionid",
+						test_header_encode_actionid);
+	g_test_add_func("/gobex/test_header_encode_apparam",
+						test_header_encode_apparam);
+
+	g_test_add_func("/gobex/test_header_name_empty",
+						test_header_name_empty);
+	g_test_add_func("/gobex/test_header_name_ascii",
+						test_header_name_ascii);
+	g_test_add_func("/gobex/test_header_name_umlaut",
+						test_header_name_umlaut);
+	g_test_add_func("/gobex/test_header_bytes", test_header_bytes);
+	g_test_add_func("/gobex/test_header_uint8", test_header_uint8);
+	g_test_add_func("/gobex/test_header_uint32", test_header_uint32);
+	g_test_add_func("/gobex/test_header_apparam", test_header_apparam);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-gobex-packet.c b/bluez/repo/unit/test-gobex-packet.c
new file mode 100644
index 0000000..0d62460
--- /dev/null
+++ b/bluez/repo/unit/test-gobex-packet.c
@@ -0,0 +1,260 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gobex/gobex.h"
+#include "gobex/gobex-packet.h"
+
+#include "util.h"
+
+static uint8_t pkt_connect[] = { G_OBEX_OP_CONNECT, 0x00, 0x0c,
+					0x10, 0x00, 0x10, 0x00,
+					G_OBEX_HDR_TARGET,
+						0x00, 0x05, 0xab, 0xcd };
+static uint8_t pkt_put_action[] = { G_OBEX_OP_PUT, 0x00, 0x05,
+					G_OBEX_HDR_ACTION, 0xab };
+static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
+					G_OBEX_HDR_BODY, 0x00, 0x07,
+					1, 2, 3, 4 };
+static uint8_t pkt_put[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
+
+static uint8_t pkt_nval_len[] = { G_OBEX_OP_PUT, 0xab, 0xcd, 0x12 };
+
+static guint8 pkt_put_long[] = { G_OBEX_OP_PUT, 0x00, 0x32,
+	G_OBEX_HDR_CONNECTION, 0x01, 0x02, 0x03, 0x04,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
+	G_OBEX_HDR_ACTION, 0xab,
+	G_OBEX_HDR_BODY, 0x00, 0x08,
+	0, 1, 2, 3, 4 };
+
+static void test_pkt(void)
+{
+	GObexPacket *pkt;
+
+	pkt = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_INVALID);
+
+	g_assert(pkt != NULL);
+
+	g_obex_packet_free(pkt);
+}
+
+static void test_decode_pkt(void)
+{
+	GObexPacket *pkt;
+	GError *err = NULL;
+
+	pkt = g_obex_packet_decode(pkt_put, sizeof(pkt_put), 0,
+						G_OBEX_DATA_REF, &err);
+	g_assert_no_error(err);
+
+	g_obex_packet_free(pkt);
+}
+
+static void test_decode_pkt_header(void)
+{
+	GObexPacket *pkt;
+	GObexHeader *header;
+	GError *err = NULL;
+	gboolean ret;
+	guint8 val;
+
+	pkt = g_obex_packet_decode(pkt_put_action, sizeof(pkt_put_action),
+						0, G_OBEX_DATA_REF, &err);
+	g_assert_no_error(err);
+
+	header = g_obex_packet_get_header(pkt, G_OBEX_HDR_ACTION);
+	g_assert(header != NULL);
+
+	ret = g_obex_header_get_uint8(header, &val);
+	g_assert(ret == TRUE);
+	g_assert(val == 0xab);
+
+	g_obex_packet_free(pkt);
+}
+
+static void test_decode_connect(void)
+{
+	GObexPacket *pkt;
+	GObexHeader *header;
+	GError *err = NULL;
+	gboolean ret;
+	const guint8 *buf;
+	guint8 target[] = { 0xab, 0xcd };
+	gsize len;
+
+	pkt = g_obex_packet_decode(pkt_connect, sizeof(pkt_connect),
+						4, G_OBEX_DATA_REF, &err);
+	g_assert_no_error(err);
+	g_assert(pkt != NULL);
+
+	header = g_obex_packet_get_header(pkt, G_OBEX_HDR_TARGET);
+	g_assert(header != NULL);
+
+	ret = g_obex_header_get_bytes(header, &buf, &len);
+	g_assert(ret == TRUE);
+	assert_memequal(target, sizeof(target), buf, len);
+
+	g_obex_packet_free(pkt);
+}
+
+static void test_decode_nval(void)
+{
+	GObexPacket *pkt;
+	GError *err = NULL;
+
+	pkt = g_obex_packet_decode(pkt_nval_len, sizeof(pkt_nval_len), 0,
+						G_OBEX_DATA_REF, &err);
+	g_assert_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR);
+	g_assert(pkt == NULL);
+
+	g_error_free(err);
+}
+
+static void test_decode_encode(void)
+{
+	GObexPacket *pkt;
+	GError *err = NULL;
+	uint8_t buf[255];
+	gssize len;
+
+	pkt = g_obex_packet_decode(pkt_put_action, sizeof(pkt_put_action),
+						0, G_OBEX_DATA_REF, &err);
+	g_assert_no_error(err);
+
+	len = g_obex_packet_encode(pkt, buf, sizeof(buf));
+	if (len < 0) {
+		g_printerr("Encoding failed: %s\n", g_strerror(-len));
+		g_assert_not_reached();
+	}
+
+	assert_memequal(pkt_put_action, sizeof(pkt_put_action), buf, len);
+
+	g_obex_packet_free(pkt);
+}
+
+static gssize get_body_data(void *buf, gsize len, gpointer user_data)
+{
+	uint8_t data[] = { 1, 2, 3, 4 };
+
+	memcpy(buf, data, sizeof(data));
+
+	return sizeof(data);
+}
+
+static void test_encode_on_demand(void)
+{
+	GObexPacket *pkt;
+	uint8_t buf[255];
+	gssize len;
+
+	pkt = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
+	g_obex_packet_add_body(pkt, get_body_data, NULL);
+
+	len = g_obex_packet_encode(pkt, buf, sizeof(buf));
+	if (len < 0) {
+		g_printerr("Encoding failed: %s\n", g_strerror(-len));
+		g_assert_not_reached();
+	}
+
+	assert_memequal(pkt_put_body, sizeof(pkt_put_body), buf, len);
+
+	g_obex_packet_free(pkt);
+}
+
+static gssize get_body_data_fail(void *buf, gsize len, gpointer user_data)
+{
+	return -EIO;
+}
+
+static void test_encode_on_demand_fail(void)
+{
+	GObexPacket *pkt;
+	uint8_t buf[255];
+	gssize len;
+
+	pkt = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
+	g_obex_packet_add_body(pkt, get_body_data_fail, NULL);
+
+	len = g_obex_packet_encode(pkt, buf, sizeof(buf));
+
+	g_assert_cmpint(len, ==, -EIO);
+
+	g_obex_packet_free(pkt);
+}
+
+static void test_create_args(void)
+{
+	GObexPacket *pkt;
+	guint8 buf[255], body[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
+	gssize len;
+
+	pkt = g_obex_packet_new(G_OBEX_OP_PUT, FALSE,
+			G_OBEX_HDR_CONNECTION, 0x01020304,
+			G_OBEX_HDR_TYPE, "foo/bar", strlen("foo/bar") + 1,
+			G_OBEX_HDR_NAME, "file.txt",
+			G_OBEX_HDR_ACTION, 0xab,
+			G_OBEX_HDR_BODY, body, sizeof(body),
+			G_OBEX_HDR_INVALID);
+
+	g_assert(pkt != NULL);
+
+	len = g_obex_packet_encode(pkt, buf, sizeof(buf));
+	g_assert(len > 0);
+
+	assert_memequal(pkt_put_long, sizeof(pkt_put_long), buf, len);
+
+	g_obex_packet_free(pkt);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/gobex/test_pkt", test_pkt);
+	g_test_add_func("/gobex/test_decode_pkt", test_decode_pkt);
+	g_test_add_func("/gobex/test_decode_pkt_header",
+						test_decode_pkt_header);
+	g_test_add_func("/gobex/test_decode_connect",
+						test_decode_connect);
+
+	g_test_add_func("/gobex/test_decode_nval", test_decode_nval);
+
+	g_test_add_func("/gobex/test_encode_pkt", test_decode_encode);
+
+	g_test_add_func("/gobex/test_encode_on_demand", test_encode_on_demand);
+	g_test_add_func("/gobex/test_encode_on_demand_fail",
+						test_encode_on_demand_fail);
+
+	g_test_add_func("/gobex/test_create_args", test_create_args);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-gobex-transfer.c b/bluez/repo/unit/test-gobex-transfer.c
new file mode 100644
index 0000000..6807c9f
--- /dev/null
+++ b/bluez/repo/unit/test-gobex-transfer.c
@@ -0,0 +1,2411 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+
+#include "gobex/gobex.h"
+
+#include "util.h"
+
+#define FINAL_BIT 0x80
+#define RANDOM_PACKETS 4
+
+static guint8 put_req_first[] = { G_OBEX_OP_PUT, 0x00, 0x30,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
+	G_OBEX_HDR_BODY, 0x00, 0x0d,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+static guint8 put_req_first_srm[] = { G_OBEX_OP_PUT, 0x00, 0x32,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
+	G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
+	G_OBEX_HDR_BODY, 0x00, 0x0d,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+static guint8 put_req_zero[255] = { G_OBEX_OP_PUT, 0x00, 0xff,
+	G_OBEX_HDR_BODY, 0x00, 0xfc };
+
+static guint8 put_req_last[] = { G_OBEX_OP_PUT | FINAL_BIT, 0x00, 0x06,
+					G_OBEX_HDR_BODY_END, 0x00, 0x03 };
+
+static guint8 abort_req[] = { G_OBEX_OP_ABORT | FINAL_BIT, 0x00, 0x03 };
+
+static guint8 put_rsp_first[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+								0x00, 0x03 };
+static guint8 put_rsp_first_srm[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+							0x00, 0x05,
+							G_OBEX_HDR_SRM, 0x01 };
+static guint8 put_rsp_first_srm_wait[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+							0x00, 0x07,
+							G_OBEX_HDR_SRM, 0x01,
+							G_OBEX_HDR_SRMP, 0x01 };
+static guint8 put_rsp_last[] = { G_OBEX_RSP_SUCCESS | FINAL_BIT, 0x00, 0x03 };
+
+static guint8 get_req_first[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x23,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
+
+static guint8 get_req_first_app[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x2a,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
+	G_OBEX_HDR_APPARAM, 0x00, 0x07,
+	0, 1, 2, 3  };
+
+static guint8 get_req_first_srm[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x25,
+	G_OBEX_HDR_SRM, 0x01,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
+
+static guint8 get_req_first_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x27,
+	G_OBEX_HDR_SRM, 0x01,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
+	G_OBEX_HDR_SRMP, 0x01 };
+
+static guint8 get_req_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x05,
+	G_OBEX_HDR_SRMP, 0x01 };
+
+static guint8 get_req_last[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x03, };
+
+static guint8 get_rsp_first_app[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0x0A,
+					G_OBEX_HDR_APPARAM, 0x00, 0x07,
+					0, 1, 2, 3 };
+static guint8 get_rsp_first[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0x10,
+					G_OBEX_HDR_BODY, 0x00, 0x0d,
+					0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+static guint8 get_rsp_first_srm[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0x12,
+					G_OBEX_HDR_SRM, 0x01,
+					G_OBEX_HDR_BODY, 0x00, 0x0d,
+					0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+static guint8 get_rsp_first_srm_wait_next[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+					0x00, 0x14,
+					G_OBEX_HDR_SRM, 0x01,
+					G_OBEX_HDR_SRMP, 0x02,
+					G_OBEX_HDR_BODY, 0x00, 0x0d,
+					0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+static guint8 get_rsp_srm_wait[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+					0x00, 0x03 };
+static guint8 get_rsp_zero[255] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0xff,
+					G_OBEX_HDR_BODY, 0x00, 0xfc };
+static guint8 get_rsp_zero_wait_next[255] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+					0x00, 0xff,
+					G_OBEX_HDR_SRMP, 0x02,
+					G_OBEX_HDR_BODY, 0x00, 0xfa };
+static guint8 get_rsp_last[] = { G_OBEX_RSP_SUCCESS | FINAL_BIT, 0x00, 0x06,
+					G_OBEX_HDR_BODY_END, 0x00, 0x03 };
+
+static guint8 conn_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT, 0x00, 0x07,
+					0x10, 0x00, 0x10, 0x00 };
+static guint8 conn_rsp[] = { G_OBEX_RSP_SUCCESS | FINAL_BIT, 0x00, 0x0c,
+					0x10, 0x00, 0x10, 0x00,
+					G_OBEX_HDR_CONNECTION, 0x00, 0x00,
+					0x00, 0x01 };
+
+static guint8 conn_req_srm[] = { G_OBEX_OP_CONNECT | FINAL_BIT, 0x00, 0x09,
+					0x10, 0x00, 0x10, 0x00,
+					G_OBEX_HDR_SRM, 0x02 };
+static guint8 conn_rsp_srm[] = { G_OBEX_RSP_SUCCESS | FINAL_BIT, 0x00, 0x0e,
+					0x10, 0x00, 0x10, 0x00,
+					G_OBEX_HDR_CONNECTION, 0x00, 0x00,
+					0x00, 0x01,
+					G_OBEX_HDR_SRM, 0x01 };
+
+static guint8 unavailable_rsp[] = { G_OBEX_RSP_SERVICE_UNAVAILABLE | FINAL_BIT,
+					0x00, 0x03 };
+
+static guint8 conn_get_req_first[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x28,
+	G_OBEX_HDR_CONNECTION, 0x00, 0x00, 0x00, 0x01,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
+
+static guint8 conn_get_req_wrg[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x28,
+	G_OBEX_HDR_CONNECTION, 0x00, 0x00, 0x00, 0xFF,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
+
+static guint8 conn_put_req_first[] = { G_OBEX_OP_PUT, 0x00, 0x35,
+	G_OBEX_HDR_CONNECTION, 0x00, 0x00, 0x00, 0x01,
+	G_OBEX_HDR_TYPE, 0x00, 0x0b,
+	'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
+	G_OBEX_HDR_NAME, 0x00, 0x15,
+	0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
+	G_OBEX_HDR_BODY, 0x00, 0x0d,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+static guint8 hdr_type[] = "foo/bar";
+static guint8 hdr_app[] = { 0, 1, 2, 3 };
+static guint8 body_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL)
+		d->err = g_error_copy(err);
+
+	g_main_loop_quit(d->mainloop);
+}
+
+static gboolean resume_obex(gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (!g_main_loop_is_running(d->mainloop))
+		return FALSE;
+
+	g_obex_resume(d->obex);
+
+	return FALSE;
+}
+
+static gssize provide_seq(void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	int fd;
+	gssize ret;
+
+	if (d->count == RANDOM_PACKETS - 1)
+		return 0;
+
+	fd = open("/dev/urandom", O_RDONLY | O_NOCTTY, 0);
+	if (fd < 0) {
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"open(/dev/urandom): %s", strerror(errno));
+		g_main_loop_quit(d->mainloop);
+		return -1;
+	}
+
+	ret = read(fd, buf, len);
+	close(fd);
+	return ret;
+}
+
+static gssize provide_eagain(void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (d->count > 0)
+		return 0;
+
+	if (len < sizeof(body_data)) {
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Got data request for only %zu bytes", len);
+		g_main_loop_quit(d->mainloop);
+		return -1;
+	}
+
+	if (d->provide_delay > 0) {
+		g_timeout_add(d->provide_delay, resume_obex, d);
+		d->provide_delay = 0;
+		return -EAGAIN;
+	}
+
+	memcpy(buf, body_data, sizeof(body_data));
+
+	return sizeof(body_data);
+}
+
+static gssize provide_data(void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (d->total > 0)
+		return 0;
+
+	if (len < sizeof(body_data)) {
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Got data request for only %zu bytes", len);
+		g_main_loop_quit(d->mainloop);
+		return -1;
+	}
+
+	memcpy(buf, body_data, sizeof(body_data));
+
+	if (d->provide_delay > 0) {
+		g_obex_suspend(d->obex);
+		g_timeout_add(d->provide_delay, resume_obex, d);
+	}
+
+	d->total += sizeof(body_data);
+
+	return sizeof(body_data);
+}
+
+static void test_put_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_req_first, sizeof(put_req_first) },
+				{ put_req_last, sizeof(put_req_last) } }, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_put_req(obex, provide_data, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static gboolean rcv_data(const void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (len != sizeof(body_data))
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected byte count %zu", len);
+
+	if (memcmp(buf, body_data, sizeof(body_data)) != 0) {
+		dump_bufs(body_data, sizeof(body_data), buf, len);
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected byte count %zu", len);
+	}
+
+	return TRUE;
+}
+
+static void handle_put(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_PUT) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_put_rsp(obex, req, rcv_data, transfer_complete, d, &d->err,
+							G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_put_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } }, {
+				{ put_req_last, sizeof(put_req_last) },
+				{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put, &d);
+
+	g_io_channel_write_chars(io, (char *) put_req_first,
+					sizeof(put_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static gboolean rcv_seq(const void *buf, gsize len, gpointer user_data)
+{
+	return TRUE;
+}
+
+static void handle_put_seq(GObex *obex, GObexPacket *req,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_PUT) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_put_rsp(obex, req, rcv_seq, transfer_complete, d,
+						&d->err, G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_stream_put_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } }, {
+				{ put_req_zero, sizeof(put_req_zero) },
+				{ put_req_zero, sizeof(put_req_zero) },
+				{ put_req_last, sizeof(put_req_last) },
+				{ NULL, -1 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put_seq,
+									&d);
+
+	g_io_channel_write_chars(io, (char *) put_req_first,
+					sizeof(put_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS - 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static gboolean cancel_transfer(gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (d->id > 0)
+		g_obex_cancel_transfer(d->id, transfer_complete, user_data);
+
+	return FALSE;
+}
+
+static gssize abort_data(void *buf, gsize len, gpointer user_data)
+{
+	g_idle_add_full(G_PRIORITY_HIGH, cancel_transfer, user_data, NULL);
+	return provide_data(buf, len, user_data);
+}
+
+static void test_stream_put_req_abort(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_req_first, sizeof(put_req_first) },
+				{ abort_req, sizeof(abort_req) } }, {
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	d.id = g_obex_put_req(obex, abort_data, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_error(d.err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED);
+	g_error_free(d.err);
+}
+
+static void test_stream_put_rsp_abort(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } }, {
+				{ put_req_zero, sizeof(put_req_zero) },
+				{ abort_req, sizeof(abort_req) },
+				{ NULL, -1 },
+				{ NULL, -1 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put_seq, &d);
+
+	g_io_channel_write_chars(io, (char *) put_req_first,
+					sizeof(put_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS - 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_error(d.err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED);
+	g_error_free(d.err);
+}
+
+static void handle_put_seq_wait(GObex *obex, GObexPacket *req,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_PUT) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_put_rsp(obex, req, rcv_seq, transfer_complete, d,
+					&d->err,
+					G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
+					G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_packet_put_rsp_wait(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+		{ put_rsp_first_srm_wait, sizeof(put_rsp_first_srm_wait) },
+		{ put_rsp_first, sizeof(put_rsp_first) },
+		{ NULL, -1 },
+		{ put_rsp_last, sizeof(put_rsp_last) } }, {
+		{ put_req_zero, sizeof(put_req_zero) },
+		{ put_req_zero, sizeof(put_req_zero) },
+		{ put_req_last, sizeof(put_req_last) },
+		{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT,
+						handle_put_seq_wait, &d);
+
+	g_io_channel_write_chars(io, (char *) put_req_first_srm,
+					sizeof(put_req_first_srm), NULL,
+					&d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_put_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ put_rsp_first_srm, sizeof(put_rsp_first_srm) },
+			{ NULL, -1 },
+			{ NULL, -1 },
+			{ put_rsp_last, sizeof(put_rsp_last) } }, {
+			{ put_req_zero, sizeof(put_req_zero) },
+			{ put_req_zero, sizeof(put_req_zero) },
+			{ put_req_last, sizeof(put_req_last) },
+			{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put_seq, &d);
+
+	g_io_channel_write_chars(io, (char *) put_req_first_srm,
+					sizeof(put_req_first_srm), NULL,
+					&d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_get_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ get_req_first, sizeof(get_req_first) },
+				{ get_req_last, sizeof(get_req_last) } }, {
+				{ get_rsp_first, sizeof(get_rsp_first) },
+				{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_data, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_stream_get_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ get_req_first, sizeof(get_req_first) },
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ get_req_last, sizeof(get_req_last) } }, {
+				{ get_rsp_first, sizeof(get_rsp_first) },
+				{ get_rsp_zero, sizeof(get_rsp_zero) },
+				{ get_rsp_zero, sizeof(get_rsp_zero) },
+				{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_get_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ get_req_first_srm, sizeof(get_req_first_srm) },
+			{ NULL, -1 },
+			{ NULL, -1 },
+			{ get_req_last, sizeof(get_req_last) } }, {
+			{ get_rsp_first_srm, sizeof(get_rsp_first_srm) },
+			{ get_rsp_zero, sizeof(get_rsp_zero) },
+			{ get_rsp_zero, sizeof(get_rsp_zero) },
+			{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_get_req_wait(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+		{ get_req_first_srm_wait, sizeof(get_req_first_srm_wait) },
+		{ get_req_last, sizeof(get_req_last) },
+		{ NULL, -1 },
+		{ get_req_last, sizeof(get_req_last) } }, {
+		{ get_rsp_first_srm, sizeof(get_rsp_first_srm) },
+		{ get_rsp_zero, sizeof(get_rsp_zero) },
+		{ get_rsp_zero, sizeof(get_rsp_zero) },
+		{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static gboolean rcv_seq_delay(const void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (d->provide_delay > 0) {
+		g_obex_suspend(d->obex);
+		g_timeout_add_seconds(d->provide_delay, resume_obex, d);
+		d->provide_delay = 0;
+	}
+
+	return TRUE;
+}
+
+static void test_packet_get_req_suspend_resume(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+		{ get_req_first_srm, sizeof(get_req_first_srm) },
+		{ get_req_srm_wait, sizeof(get_req_srm_wait) },
+		{ get_req_srm_wait, sizeof(get_req_srm_wait) },
+		{ get_req_last, sizeof(get_req_last) } }, {
+		{ get_rsp_first_srm, sizeof(get_rsp_first_srm) },
+		{ get_rsp_srm_wait, sizeof(get_rsp_srm_wait) },
+		{ get_rsp_srm_wait, sizeof(get_rsp_srm_wait) },
+		{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+	d.obex = obex;
+	d.provide_delay = 1;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_seq_delay, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 4);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_get_req_wait_next(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+		{ get_req_first_srm, sizeof(get_req_first_srm) },
+		{ get_req_last, sizeof(get_req_last) },
+		{ get_req_last, sizeof(get_req_last) },
+		{ get_req_last, sizeof(get_req_last) } }, {
+		{ get_rsp_first_srm_wait_next,
+		sizeof(get_rsp_first_srm_wait_next) },
+		{ get_rsp_zero_wait_next, sizeof(get_rsp_zero_wait_next) },
+		{ get_rsp_zero_wait_next, sizeof(get_rsp_zero_wait_next) },
+		{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_get_req_app(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ get_req_first_app, sizeof(get_req_first_app) },
+			{ get_req_last, sizeof(get_req_last) },
+			{ get_req_last, sizeof(get_req_last) } }, {
+			{ get_rsp_first_app, sizeof(get_rsp_first_app) },
+			{ get_rsp_first, sizeof(get_rsp_first) },
+			{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_data, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_APPARAM, hdr_app, sizeof(hdr_app),
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 3);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void handle_get_eagain(GObex *obex, GObexPacket *req,
+						gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_GET) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_get_rsp(obex, provide_eagain, transfer_complete, d,
+						&d->err, G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void handle_get(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_GET) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_get_rsp(obex, provide_data, transfer_complete, d, &d->err,
+							G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_stream_put_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ put_req_last, sizeof(put_req_last) } }, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_put_req(obex, provide_seq, transfer_complete, &d, &d.err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "random.bin",
+					G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static gssize provide_seq_delay(void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	int fd;
+	gssize ret;
+
+	if (d->provide_delay > 0) {
+		g_obex_suspend(d->obex);
+		g_timeout_add_seconds(d->provide_delay, resume_obex, d);
+		d->provide_delay = 0;
+	}
+
+	if (d->count == RANDOM_PACKETS - 1)
+		return 0;
+
+	fd = open("/dev/urandom", O_RDONLY | O_NOCTTY, 0);
+	if (fd < 0) {
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"open(/dev/urandom): %s", strerror(errno));
+		g_main_loop_quit(d->mainloop);
+		return -1;
+	}
+
+	ret = read(fd, buf, len);
+	close(fd);
+	return ret;
+}
+
+static void test_packet_put_req_suspend_resume(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ NULL, 0 },
+			{ NULL, 0 },
+			{ NULL, 0 },
+			{ put_req_last, sizeof(put_req_last) } }, {
+			{ put_rsp_first_srm, sizeof(put_rsp_first_srm) },
+			{ NULL, 0 },
+			{ NULL, 0 },
+			{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+	d.obex = obex;
+	d.provide_delay = 1;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	g_obex_put_req(obex, provide_seq_delay, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "random.bin",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_put_req_wait(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+		{ NULL, 0 },
+		{ NULL, 0 },
+		{ NULL, 0 },
+		{ put_req_last, sizeof(put_req_last) } }, {
+		{ put_rsp_first_srm_wait, sizeof(put_rsp_first_srm_wait) },
+		{ put_rsp_first, sizeof(put_rsp_first) },
+		{ NULL, 0 },
+		{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_put_req(obex, provide_seq, transfer_complete, &d, &d.err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "random.bin",
+					G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_put_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ NULL, 0 },
+			{ NULL, 0 },
+			{ NULL, 0 },
+			{ put_req_last, sizeof(put_req_last) } }, {
+			{ put_rsp_first_srm, sizeof(put_rsp_first_srm) },
+			{ NULL, 0 },
+			{ NULL, 0 },
+			{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_put_req(obex, provide_seq, transfer_complete, &d, &d.err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "random.bin",
+					G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_put_req_eagain(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_req_first, sizeof(put_req_first) },
+				{ put_req_last, sizeof(put_req_last) } }, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+	d.provide_delay = 200;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_put_req(obex, provide_eagain, transfer_complete, &d, &d.err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "file.txt",
+					G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_get_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ get_rsp_first, sizeof(get_rsp_first) },
+				{ get_rsp_last, sizeof(get_rsp_last) } }, {
+				{ get_req_last, sizeof(get_req_last) },
+				{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get, &d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first,
+					sizeof(get_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void handle_get_seq(GObex *obex, GObexPacket *req,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_GET) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_get_rsp(obex, provide_seq, transfer_complete, d,
+						&d->err, G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_stream_get_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ get_rsp_last, sizeof(get_rsp_last) } }, {
+				{ get_req_last, sizeof(get_req_last) },
+				{ get_req_last, sizeof(get_req_last) },
+				{ get_req_last, sizeof(get_req_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_seq, &d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first,
+					sizeof(get_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS - 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_packet_get_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ get_rsp_last, sizeof(get_rsp_last) } }, {
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ get_req_last, sizeof(get_req_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_seq, &d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first_srm,
+					sizeof(get_req_first_srm), NULL,
+					&d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS - 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void handle_get_seq_srm_wait(GObex *obex, GObexPacket *req,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_GET) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_get_rsp(obex, provide_seq, transfer_complete, d,
+					&d->err,
+					G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
+					G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_packet_get_rsp_wait(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ get_rsp_last, sizeof(get_rsp_last) } }, {
+				{ get_req_last, sizeof(get_req_last) },
+				{ NULL, 0 },
+				{ get_req_last, sizeof(get_req_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET,
+					handle_get_seq_srm_wait, &d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first_srm,
+					sizeof(get_req_first_srm), NULL,
+					&d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS - 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void handle_get_app(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	GObexPacket *rsp;
+
+	if (op != G_OBEX_OP_GET) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	g_obex_add_request_function(d->obex, G_OBEX_OP_GET, handle_get, d);
+
+	rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE,
+				G_OBEX_HDR_APPARAM, hdr_app, sizeof(hdr_app),
+				G_OBEX_HDR_INVALID);
+
+	if (g_obex_send(d->obex, rsp, NULL) == FALSE)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_get_rsp_app(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ get_rsp_first_app, sizeof(get_rsp_first_app) },
+			{ get_rsp_first, sizeof(get_rsp_first) },
+			{ get_rsp_last, sizeof(get_rsp_last) } }, {
+			{ get_req_first, sizeof(get_req_first) },
+			{ get_req_last, sizeof(get_req_last) },
+			{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_app, &d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first_app,
+					sizeof(get_req_first_app), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_put_req_delay(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_req_first, sizeof(put_req_first) },
+				{ put_req_last, sizeof(put_req_last) } }, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+	d.provide_delay = 200;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_put_req(obex, provide_data, transfer_complete, &d, &d.err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "file.txt",
+					G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_get_rsp_delay(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ get_rsp_first, sizeof(get_rsp_first) },
+				{ get_rsp_last, sizeof(get_rsp_last) } }, {
+				{ get_req_last, sizeof(get_req_last) },
+				{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+	d.provide_delay = 200;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get, &d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first,
+					sizeof(get_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static gboolean rcv_data_delay(const void *buf, gsize len, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (len != sizeof(body_data))
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected byte count %zu", len);
+
+	if (memcmp(buf, body_data, sizeof(body_data)) != 0) {
+		dump_bufs(body_data, sizeof(body_data), buf, len);
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected byte count %zu", len);
+	}
+
+	if (d->provide_delay > 0) {
+		g_obex_suspend(d->obex);
+		g_timeout_add(d->provide_delay, resume_obex, d);
+	}
+
+	return TRUE;
+}
+
+static void handle_put_delay(GObex *obex, GObexPacket *req, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	guint id;
+
+	if (op != G_OBEX_OP_PUT) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	id = g_obex_put_rsp(obex, req, rcv_data_delay, transfer_complete, d,
+						&d->err, G_OBEX_HDR_INVALID);
+	if (id == 0)
+		g_main_loop_quit(d->mainloop);
+}
+
+static void test_put_rsp_delay(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } }, {
+				{ put_req_last, sizeof(put_req_last) },
+				{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+	d.provide_delay = 200;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put_delay, &d);
+
+	g_io_channel_write_chars(io, (char *) put_req_first,
+					sizeof(put_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_get_req_delay(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ get_req_first, sizeof(get_req_first) },
+				{ get_req_last, sizeof(get_req_last) } }, {
+				{ get_rsp_first, sizeof(get_rsp_first) },
+				{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+	d.provide_delay = 200;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_get_req(obex, rcv_data_delay, transfer_complete, &d, &d.err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_get_rsp_eagain(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ get_rsp_first, sizeof(get_rsp_first) },
+				{ get_rsp_last, sizeof(get_rsp_last) } }, {
+				{ get_req_last, sizeof(get_req_last) },
+				{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+	d.provide_delay = 200;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_eagain,
+									&d);
+
+	g_io_channel_write_chars(io, (char *) get_req_first,
+					sizeof(get_req_first), NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void conn_complete(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL)
+		d->err = g_error_copy(err);
+
+	g_main_loop_quit(d->mainloop);
+}
+
+static void test_conn_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ conn_req, sizeof(conn_req) } }, {
+				{ conn_rsp, sizeof(conn_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, conn_complete, &d, &d.err, G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void handle_conn_rsp(GObex *obex, GObexPacket *req,
+						gpointer user_data)
+{
+	struct test_data *d = user_data;
+	guint8 op = g_obex_packet_get_operation(req, NULL);
+	GObexPacket *rsp;
+
+	if (op != G_OBEX_OP_CONNECT) {
+		d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Unexpected opcode 0x%02x", op);
+		g_main_loop_quit(d->mainloop);
+		return;
+	}
+
+	rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE,
+						G_OBEX_HDR_CONNECTION, 1,
+						G_OBEX_HDR_INVALID);
+	g_obex_send(obex, rsp, &d->err);
+}
+
+static void test_conn_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ conn_rsp, sizeof(conn_rsp) } }, {
+			{ NULL, -1 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT,
+						handle_conn_rsp, &d);
+
+	g_io_channel_write_chars(io, (char *) conn_req, sizeof(conn_req),
+								NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	if (!d.io_completed)
+		g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void conn_complete_get_req(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL) {
+		d->err = g_error_copy(err);
+		g_main_loop_quit(d->mainloop);
+	}
+
+	g_obex_get_req(obex, rcv_data, transfer_complete, d, &d->err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+}
+
+static void test_conn_get_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ conn_req, sizeof(conn_req) },
+			{ conn_get_req_first, sizeof(conn_get_req_first) },
+			{ get_req_last, sizeof(get_req_last) }}, {
+			{ conn_rsp, sizeof(conn_rsp) } ,
+			{ get_rsp_first, sizeof(get_rsp_first) },
+			{ get_rsp_last, sizeof(get_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, conn_complete_get_req, &d, &d.err,
+							G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 3);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_conn_get_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ conn_rsp, sizeof(conn_rsp) },
+			{ get_rsp_first, sizeof(get_rsp_first) },
+			{ get_rsp_last, sizeof(get_rsp_last) } }, {
+			{ conn_get_req_first, sizeof(conn_get_req_first) },
+			{ get_req_last, sizeof(get_req_last) },
+			{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT,
+						handle_conn_rsp, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_GET,
+						handle_get, &d);
+
+	g_io_channel_write_chars(io, (char *) conn_req, sizeof(conn_req),
+								NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void conn_complete_put_req(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL) {
+		d->err = g_error_copy(err);
+		g_main_loop_quit(d->mainloop);
+	}
+
+	g_obex_put_req(obex, provide_data, transfer_complete, d, &d->err,
+				G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+				G_OBEX_HDR_NAME, "file.txt",
+				G_OBEX_HDR_INVALID);
+}
+
+static void test_conn_put_req(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ conn_req, sizeof(conn_req) },
+			{ conn_put_req_first, sizeof(conn_put_req_first) },
+			{ put_req_last, sizeof(put_req_last) }}, {
+			{ conn_rsp, sizeof(conn_rsp) } ,
+			{ put_rsp_first, sizeof(put_rsp_first) },
+			{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, conn_complete_put_req, &d, &d.err,
+							G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 3);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_conn_put_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ conn_rsp, sizeof(conn_rsp) },
+			{ put_rsp_first, sizeof(put_rsp_first) },
+			{ put_rsp_last, sizeof(put_rsp_last) } }, {
+			{ conn_put_req_first, sizeof(conn_put_req_first) },
+			{ put_req_last, sizeof(put_req_last) },
+			{ NULL, 0 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT,
+						handle_conn_rsp, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_PUT,
+						handle_put, &d);
+
+	g_io_channel_write_chars(io, (char *) conn_req, sizeof(conn_req),
+								NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_conn_get_wrg_rsp(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ conn_rsp, sizeof(conn_rsp) },
+			{ unavailable_rsp, sizeof(unavailable_rsp) } }, {
+			{ conn_get_req_wrg, sizeof(conn_get_req_wrg) },
+			{ NULL, -1 } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT,
+						handle_conn_rsp, &d);
+
+	g_io_channel_write_chars(io, (char *) conn_req, sizeof(conn_req),
+								NULL, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	if (!d.io_completed)
+		g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void conn_complete_put_req_seq(GObex *obex, GError *err,
+					GObexPacket *rsp, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL) {
+		d->err = g_error_copy(err);
+		g_main_loop_quit(d->mainloop);
+	}
+
+	g_obex_put_req(obex, provide_seq, transfer_complete, d, &d->err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "random.bin",
+					G_OBEX_HDR_INVALID);
+}
+
+static void test_conn_put_req_seq(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ conn_req, sizeof(conn_req) } ,
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ put_req_last, sizeof(put_req_last) } }, {
+				{ conn_rsp, sizeof(conn_rsp) } ,
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_first, sizeof(put_rsp_first) },
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, conn_complete_put_req_seq, &d, &d.err,
+							G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void conn_complete_put_req_seq_srm(GObex *obex, GError *err,
+					GObexPacket *rsp, gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL) {
+		d->err = g_error_copy(err);
+		g_main_loop_quit(d->mainloop);
+	}
+
+	g_obex_put_req(obex, provide_seq, transfer_complete, d, &d->err,
+					G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+					G_OBEX_HDR_NAME, "random.bin",
+					G_OBEX_HDR_INVALID);
+}
+
+static void test_conn_put_req_seq_srm(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ conn_req_srm, sizeof(conn_req_srm) } ,
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ put_req_last, sizeof(put_req_last) } }, {
+				{ conn_rsp_srm, sizeof(conn_rsp_srm) } ,
+				{ NULL, 0 },
+				{ NULL, 0 },
+				{ put_rsp_last, sizeof(put_rsp_last) } } };
+
+	create_endpoints(&obex, &io, SOCK_SEQPACKET);
+	d.obex = obex;
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, conn_complete_put_req_seq_srm, &d, &d.err,
+					G_OBEX_HDR_SRM, G_OBEX_SRM_INDICATE,
+					G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/gobex/test_conn_req", test_conn_req);
+	g_test_add_func("/gobex/test_conn_rsp", test_conn_rsp);
+
+	g_test_add_func("/gobex/test_put_req", test_put_req);
+	g_test_add_func("/gobex/test_put_rsp", test_put_rsp);
+
+	g_test_add_func("/gobex/test_get_req", test_get_req);
+	g_test_add_func("/gobex/test_get_rsp", test_get_rsp);
+
+	g_test_add_func("/gobex/test_get_req_app", test_get_req_app);
+	g_test_add_func("/gobex/test_get_rsp_app", test_get_rsp_app);
+
+	g_test_add_func("/gobex/test_put_req_delay", test_put_req_delay);
+	g_test_add_func("/gobex/test_put_rsp_delay", test_put_rsp_delay);
+
+	g_test_add_func("/gobex/test_get_req_delay", test_get_req_delay);
+	g_test_add_func("/gobex/test_get_rsp_delay", test_get_rsp_delay);
+
+	g_test_add_func("/gobex/test_put_req_eagain", test_put_req_eagain);
+	g_test_add_func("/gobex/test_get_req_eagain", test_get_rsp_eagain);
+
+	g_test_add_func("/gobex/test_stream_put_req", test_stream_put_req);
+	g_test_add_func("/gobex/test_stream_put_rsp", test_stream_put_rsp);
+
+	g_test_add_func("/gobex/test_stream_put_req_abort",
+						test_stream_put_req_abort);
+	g_test_add_func("/gobex/test_stream_put_rsp_abort",
+						test_stream_put_rsp_abort);
+
+	g_test_add_func("/gobex/test_stream_get_req", test_stream_get_req);
+	g_test_add_func("/gobex/test_stream_get_rsp", test_stream_get_rsp);
+
+	g_test_add_func("/gobex/test_conn_get_req", test_conn_get_req);
+	g_test_add_func("/gobex/test_conn_get_rsp", test_conn_get_rsp);
+
+	g_test_add_func("/gobex/test_conn_put_req", test_conn_put_req);
+	g_test_add_func("/gobex/test_conn_put_rsp", test_conn_put_rsp);
+
+	g_test_add_func("/gobex/test_conn_get_wrg_rsp", test_conn_get_wrg_rsp);
+
+	g_test_add_func("/gobex/test_conn_put_req_seq",
+						test_conn_put_req_seq);
+
+	g_test_add_func("/gobex/test_packet_put_req", test_packet_put_req);
+	g_test_add_func("/gobex/test_packet_put_req_wait",
+						test_packet_put_req_wait);
+	g_test_add_func("/gobex/test_packet_put_req_suspend_resume",
+					test_packet_put_req_suspend_resume);
+
+	g_test_add_func("/gobex/test_packet_put_rsp", test_packet_put_rsp);
+	g_test_add_func("/gobex/test_packet_put_rsp_wait",
+						test_packet_put_rsp_wait);
+
+	g_test_add_func("/gobex/test_packet_get_rsp", test_packet_get_rsp);
+	g_test_add_func("/gobex/test_packet_get_rsp_wait",
+						test_packet_get_rsp_wait);
+
+	g_test_add_func("/gobex/test_packet_get_req", test_packet_get_req);
+	g_test_add_func("/gobex/test_packet_get_req_wait",
+						test_packet_get_req_wait);
+	g_test_add_func("/gobex/test_packet_get_req_suspend_resume",
+					test_packet_get_req_suspend_resume);
+
+	g_test_add_func("/gobex/test_packet_get_req_wait_next",
+						test_packet_get_req_wait_next);
+
+	g_test_add_func("/gobex/test_conn_put_req_seq_srm",
+						test_conn_put_req_seq_srm);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-gobex.c b/bluez/repo/unit/test-gobex.c
new file mode 100644
index 0000000..66531a2
--- /dev/null
+++ b/bluez/repo/unit/test-gobex.c
@@ -0,0 +1,1340 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "gobex/gobex.h"
+
+#include "util.h"
+
+#define FINAL_BIT 0x80
+
+static GMainLoop *mainloop = NULL;
+
+static uint8_t pkt_connect_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT,
+					0x00, 0x07, 0x10, 0x00, 0x10, 0x00 };
+static uint8_t pkt_connect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07,
+					0x10, 0x00, 0x10, 0x00 };
+
+static uint8_t pkt_disconnect_req[] = { G_OBEX_OP_DISCONNECT | FINAL_BIT,
+					0x00, 0x03 };
+static uint8_t pkt_disconnect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x03 };
+
+static uint8_t pkt_unauth_rsp[] = { 0x41 | FINAL_BIT, 0x00, 0x1c,
+					0x10, 0x00, 0x10, 0x00, 0x4d, 0x00,
+					0x15, 0x00, 0x10, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					0x00 };
+static uint8_t pkt_auth_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT, 0x00, 0x1c,
+					0x10, 0x00, 0x10, 0x00, 0x4e, 0x00,
+					0x15, 0x00, 0x10, 0x5a, 0xd4, 0x93,
+					0x93, 0xba, 0x4a, 0xf8, 0xac, 0xce,
+					0x7f, 0x5b, 0x1a, 0x05, 0x38, 0x74,
+					0x24 };
+static uint8_t pkt_auth_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07,
+					0x10, 0x00, 0x10, 0x00 };
+
+static uint8_t pkt_setpath_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT, 0x00, 0x10,
+					0x02, 0x00,
+					G_OBEX_HDR_NAME, 0x00, 0x0b,
+					0, 'd', 0, 'i', 0, 'r', 0, 0 };
+static uint8_t pkt_setpath_up_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT,
+					0x00, 0x05, 0x03, 0x00 };
+static uint8_t pkt_setpath_up_down_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT,
+					0x00, 0x10, 0x03, 0x00,
+					G_OBEX_HDR_NAME, 0x00, 0x0b,
+					0, 'd', 0, 'i', 0, 'r', 0, 0 };
+static uint8_t pkt_success_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x03 };
+
+static uint8_t pkt_mkdir_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT, 0x00, 0x10,
+					0x00, 0x00,
+					G_OBEX_HDR_NAME, 0x00, 0x0b,
+					0, 'd', 0, 'i', 0, 'r', 0, 0 };
+
+static uint8_t pkt_delete_req[] = { G_OBEX_OP_PUT | FINAL_BIT, 0x00, 0x16,
+		G_OBEX_HDR_NAME, 0x00, 0x13,
+		0, 'f', 0, 'o', 0, 'o', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
+
+static uint8_t pkt_copy_req[] = { G_OBEX_OP_ACTION | FINAL_BIT, 0x00, 0x1b,
+					G_OBEX_HDR_ACTION, 0x00,
+					G_OBEX_HDR_NAME, 0x00, 0x0b,
+					0, 'f', 0, 'o', 0, 'o', 0, 0,
+					G_OBEX_HDR_DESTNAME, 0x00, 0x0b,
+					0, 'b', 0, 'a', 0, 'r', 0, 0 };
+static uint8_t pkt_move_req[] = { G_OBEX_OP_ACTION | FINAL_BIT, 0x00, 0x1b,
+					G_OBEX_HDR_ACTION, 0x01,
+					G_OBEX_HDR_NAME, 0x00, 0x0b,
+					0, 'f', 0, 'o', 0, 'o', 0, 0,
+					G_OBEX_HDR_DESTNAME, 0x00, 0x0b,
+					0, 'b', 0, 'a', 0, 'r', 0, 0 };
+
+static uint8_t pkt_nval_connect_rsp[] = { 0x10 | FINAL_BIT, 0x00, 0x05,
+					0x10, 0x00, };
+static uint8_t pkt_abort_rsp[] = { 0x90, 0x00, 0x03 };
+static uint8_t pkt_nval_short_rsp[] = { 0x10 | FINAL_BIT, 0x12 };
+static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
+					G_OBEX_HDR_BODY, 0x00, 0x07,
+					1, 2, 3, 4 };
+
+static gboolean timeout(gpointer user_data)
+{
+	GError **err = user_data;
+
+	if (!g_main_loop_is_running(mainloop))
+		return FALSE;
+
+	g_set_error(err, TEST_ERROR, TEST_ERROR_TIMEOUT, "Timed out");
+
+	g_main_loop_quit(mainloop);
+
+	return FALSE;
+}
+
+static void connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	guint8 rsp_code;
+	gboolean final;
+	GError **test_err = user_data;
+
+	if (err != NULL) {
+		g_assert(*test_err == NULL);
+		*test_err = g_error_copy(err);
+		goto done;
+	}
+
+	rsp_code = g_obex_packet_get_operation(rsp, &final);
+	if (rsp_code != 0x20) {
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Unexpected response 0x%02x", rsp_code);
+		goto done;
+	}
+
+	if (!final) {
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Connect response didn't have final bit");
+		goto done;
+	}
+
+done:
+	g_main_loop_quit(mainloop);
+}
+
+static void nval_connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	GError **test_err = user_data;
+
+	if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR))
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Did not get expected parse error");
+
+	g_main_loop_quit(mainloop);
+}
+
+static void timeout_rsp(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	GError **test_err = user_data;
+
+	if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_TIMEOUT))
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Did not get expected timeout error");
+
+	g_main_loop_quit(mainloop);
+}
+
+static gboolean recv_and_send(GIOChannel *io, void *data, gsize len,
+								GError **err)
+{
+	gsize bytes_written, rbytes;
+	char buf[255];
+	GIOStatus status;
+
+	status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+	if (status != G_IO_STATUS_NORMAL) {
+		g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"read failed with status %d", status);
+		return FALSE;
+	}
+
+	if (data == NULL)
+		return TRUE;
+
+	g_io_channel_write_chars(io, data, len, &bytes_written, NULL);
+	if (bytes_written != len) {
+		g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+						"Unable to write to socket");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean send_connect_rsp(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	GError **err = user_data;
+
+	if (!recv_and_send(io, pkt_connect_rsp, sizeof(pkt_connect_rsp), err))
+		g_main_loop_quit(mainloop);
+
+	return FALSE;
+}
+
+static gboolean send_nval_connect_rsp(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	GError **err = user_data;
+
+	if (!recv_and_send(io, pkt_nval_connect_rsp,
+					sizeof(pkt_nval_connect_rsp), err))
+		g_main_loop_quit(mainloop);
+
+	return FALSE;
+}
+
+static gboolean send_nval_short_rsp(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	GError **err = user_data;
+
+	if (!recv_and_send(io, pkt_nval_short_rsp,
+					sizeof(pkt_nval_short_rsp), err))
+		g_main_loop_quit(mainloop);
+
+	return FALSE;
+}
+
+static gboolean send_nothing(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	GError **err = user_data;
+
+	if (!recv_and_send(io, NULL, 0, err))
+		g_main_loop_quit(mainloop);
+
+	return FALSE;
+}
+
+static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
+				GIOFunc send_rsp_func, int req_timeout,
+				int transport_type)
+{
+	GError *gerr = NULL;
+	GIOChannel *io;
+	GIOCondition cond;
+	guint timer_id, test_time;
+	GObex *obex;
+
+	create_endpoints(&obex, &io, transport_type);
+
+	g_obex_send_req(obex, req, req_timeout, rsp_func, &gerr, &gerr);
+	g_assert_no_error(gerr);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	g_io_add_watch(io, cond, send_rsp_func, &gerr);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	if (req_timeout > 0)
+		test_time = req_timeout + 1;
+	else
+		test_time = 1;
+
+	timer_id = g_timeout_add_seconds(test_time, timeout, &gerr);
+
+	g_main_loop_run(mainloop);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_obex_unref(obex);
+
+	g_assert_no_error(gerr);
+}
+
+static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
+					int req_timeout, int transport_type)
+{
+	GObexPacket *req;
+	guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
+
+	req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE, G_OBEX_HDR_INVALID);
+	g_assert(req != NULL);
+
+	g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
+							G_OBEX_DATA_REF);
+
+	send_req(req, rsp_func, send_rsp_func, req_timeout, transport_type);
+}
+
+static void test_send_connect_req_stream(void)
+{
+	send_connect(connect_rsp, send_connect_rsp, -1, SOCK_STREAM);
+}
+
+static void test_send_connect_req_pkt(void)
+{
+	send_connect(connect_rsp, send_connect_rsp, -1, SOCK_SEQPACKET);
+}
+
+static void test_send_nval_connect_req_stream(void)
+{
+	send_connect(nval_connect_rsp, send_nval_connect_rsp, -1, SOCK_STREAM);
+}
+
+static void test_send_nval_connect_req_pkt(void)
+{
+	send_connect(nval_connect_rsp, send_nval_connect_rsp, -1,
+							SOCK_SEQPACKET);
+}
+
+static void test_send_nval_connect_req_short_pkt(void)
+{
+	send_connect(nval_connect_rsp, send_nval_short_rsp, -1,
+							SOCK_SEQPACKET);
+}
+
+static void test_send_connect_req_timeout_stream(void)
+{
+	send_connect(timeout_rsp, send_nothing, 0, SOCK_STREAM);
+}
+
+static void test_send_connect_req_timeout_pkt(void)
+{
+	send_connect(timeout_rsp, send_nothing, 0, SOCK_SEQPACKET);
+}
+
+struct req_info {
+	GObex *obex;
+	guint id;
+	GError *err;
+};
+
+static void req_done(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct req_info *r = user_data;
+
+	if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED))
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Did not get expected cancelled error");
+
+	g_main_loop_quit(mainloop);
+}
+
+static void test_cancel_req_immediate(void)
+{
+	GObexPacket *req;
+	struct req_info r;
+	gboolean ret;
+
+	create_endpoints(&r.obex, NULL, SOCK_STREAM);
+
+	r.err = NULL;
+
+	req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_INVALID);
+	r.id = g_obex_send_req(r.obex, req, -1, req_done, &r, &r.err);
+	g_assert_no_error(r.err);
+	g_assert(r.id != 0);
+
+	ret = g_obex_cancel_req(r.obex, r.id, FALSE);
+	g_assert(ret == TRUE);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(mainloop);
+
+	g_assert_no_error(r.err);
+
+	g_obex_unref(r.obex);
+	g_main_loop_unref(mainloop);
+}
+
+static gboolean cancel_server(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct req_info *r = user_data;
+	GIOStatus status;
+	gsize bytes_written, rbytes;
+	char buf[255];
+
+	status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+	if (status != G_IO_STATUS_NORMAL) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Reading data failed with status %d", status);
+		goto failed;
+	}
+
+	if (rbytes < 3) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Not enough data from socket");
+		goto failed;
+	}
+
+	if ((uint8_t) buf[0] == (G_OBEX_OP_PUT | FINAL_BIT)) {
+		if (!g_obex_cancel_req(r->obex, r->id, FALSE)) {
+			g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Cancelling request failed");
+			goto failed;
+		}
+		return TRUE;
+	}
+
+	if ((uint8_t) buf[0] != (G_OBEX_OP_ABORT | FINAL_BIT)) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Neither Put nor Abort packet received");
+		goto failed;
+	}
+
+	g_io_channel_write_chars(io, (char *) pkt_abort_rsp,
+				sizeof(pkt_abort_rsp), &bytes_written, NULL);
+	if (bytes_written != sizeof(pkt_abort_rsp)) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+						"Unable to write to socket");
+		goto failed;
+	}
+
+	return TRUE;
+
+failed:
+	g_main_loop_quit(mainloop);
+	return FALSE;
+}
+
+static void test_cancel_req_delay(int transport_type)
+{
+	GIOChannel *io;
+	guint io_id, timer_id;
+	struct req_info r;
+	GObexPacket *req;
+	GIOCondition cond;
+
+	create_endpoints(&r.obex, &io, transport_type);
+
+	r.err = NULL;
+
+	req = g_obex_packet_new(G_OBEX_OP_PUT, TRUE, G_OBEX_HDR_INVALID);
+	r.id = g_obex_send_req(r.obex, req, -1, req_done, &r, &r.err);
+	g_assert_no_error(r.err);
+	g_assert(r.id != 0);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, cancel_server, &r);
+
+	timer_id = g_timeout_add_seconds(2, timeout, &r.err);
+
+	g_main_loop_run(mainloop);
+
+	g_assert_no_error(r.err);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(r.obex);
+	g_main_loop_unref(mainloop);
+}
+
+static void test_cancel_req_delay_stream(void)
+{
+	test_cancel_req_delay(SOCK_STREAM);
+}
+
+static void test_cancel_req_delay_pkt(void)
+{
+	test_cancel_req_delay(SOCK_SEQPACKET);
+}
+
+struct rcv_buf_info {
+	GError *err;
+	const guint8 *buf;
+	gsize len;
+	gboolean completed;
+};
+
+static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+	struct rcv_buf_info *r = user_data;
+	GIOStatus status;
+	gsize rbytes;
+	char buf[255];
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Unexpected condition %d on socket", cond);
+		goto done;
+	}
+
+	status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+	if (status != G_IO_STATUS_NORMAL) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Reading data failed with status %d", status);
+		goto done;
+	}
+
+	if (rbytes != r->len) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Got %zu bytes instead of %zu",
+				rbytes, sizeof(pkt_connect_req));
+		dump_bufs(r->buf, r->len, buf, rbytes);
+		goto done;
+	}
+
+	if (memcmp(buf, r->buf, rbytes) != 0) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Mismatch with received data");
+		dump_bufs(r->buf, r->len, buf, rbytes);
+		goto done;
+	}
+
+done:
+	g_main_loop_quit(mainloop);
+	r->completed = TRUE;
+	return FALSE;
+}
+
+static void test_send_connect(int transport_type)
+{
+	guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
+	struct rcv_buf_info r;
+	GIOChannel *io;
+	GIOCondition cond;
+	GObexPacket *req;
+	guint io_id, timer_id;
+	GObex *obex;
+
+	create_endpoints(&obex, &io, transport_type);
+
+	memset(&r, 0, sizeof(r));
+	r.buf = pkt_connect_req;
+	r.len = sizeof(pkt_connect_req);
+
+	req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE, G_OBEX_HDR_INVALID);
+	g_assert(req != NULL);
+
+	g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
+							G_OBEX_DATA_REF);
+	g_obex_send(obex, req, &r.err);
+	g_assert_no_error(r.err);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, rcv_data, &r);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, timeout, &r.err);
+
+	g_main_loop_run(mainloop);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	if (!r.completed)
+		g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(r.err);
+}
+
+static void test_send_connect_stream(void)
+{
+	test_send_connect(SOCK_STREAM);
+}
+
+static void test_send_connect_pkt(void)
+{
+	test_send_connect(SOCK_SEQPACKET);
+}
+
+static void unexpected_disconn(GObex *obex, GError *err, gpointer user_data)
+{
+	GError **test_err = user_data;
+
+	if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR))
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Didn't get parse error as expected");
+
+	g_main_loop_quit(mainloop);
+}
+
+static void test_recv_unexpected(void)
+{
+	GError *err = NULL;
+	GObexPacket *req;
+	GIOChannel *io;
+	guint timer_id;
+	GObex *obex;
+	guint8 buf[255];
+	gssize len;
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	g_obex_set_disconnect_function(obex, unexpected_disconn, &err);
+
+	req = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE, G_OBEX_HDR_INVALID);
+	len = g_obex_packet_encode(req, buf, sizeof(buf));
+	g_obex_packet_free(req);
+	g_assert_cmpint(len, >=, 0);
+
+	g_io_channel_write_chars(io, (char *) buf, len, NULL, &err);
+	g_assert_no_error(err);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, timeout, &err);
+
+	g_main_loop_run(mainloop);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_obex_unref(obex);
+
+	g_assert_no_error(err);
+}
+
+static gssize get_body_data(void *buf, gsize len, gpointer user_data)
+{
+	uint8_t data[] = { 1, 2, 3, 4 };
+
+	memcpy(buf, data, sizeof(data));
+
+	return sizeof(data);
+}
+
+static gssize get_body_data_fail(void *buf, gsize len, gpointer user_data)
+{
+	g_main_loop_quit(mainloop);
+	return -1;
+}
+
+static void test_send_on_demand(int transport_type, GObexDataProducer func)
+{
+	struct rcv_buf_info r;
+	GIOChannel *io;
+	GIOCondition cond;
+	GObexPacket *req;
+	guint io_id, timer_id;
+	GObex *obex;
+
+	create_endpoints(&obex, &io, transport_type);
+
+	memset(&r, 0, sizeof(r));
+	r.buf = pkt_put_body;
+	r.len = sizeof(pkt_put_body);
+
+	req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
+	g_obex_packet_add_body(req, func, &r);
+
+	g_obex_send(obex, req, &r.err);
+	g_assert_no_error(r.err);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, rcv_data, &r);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, timeout, &r.err);
+
+	g_main_loop_run(mainloop);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	if (!r.completed)
+		g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(r.err);
+}
+
+static void test_send_on_demand_stream(void)
+{
+	test_send_on_demand(SOCK_STREAM, get_body_data);
+}
+
+static void test_send_on_demand_pkt(void)
+{
+	test_send_on_demand(SOCK_SEQPACKET, get_body_data);
+}
+
+static void test_send_on_demand_fail_stream(void)
+{
+	test_send_on_demand(SOCK_STREAM, get_body_data_fail);
+}
+
+static void test_send_on_demand_fail_pkt(void)
+{
+	test_send_on_demand(SOCK_SEQPACKET, get_body_data_fail);
+}
+
+static void handle_connect_req(GObex *obex, GObexPacket *req,
+							gpointer user_data)
+{
+	GError **test_err = user_data;
+
+	if (g_obex_packet_get_operation(req, NULL) != G_OBEX_OP_CONNECT)
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+						"Unexpected operation");
+	g_main_loop_quit(mainloop);
+
+}
+
+static void handle_connect_err(GObex *obex, GError *err, gpointer user_data)
+{
+	GError **test_err = user_data;
+
+	g_main_loop_quit(mainloop);
+
+	if (err != NULL)
+		*test_err = g_error_copy(err);
+	else
+		*test_err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Disconnected");
+}
+
+static void recv_connect(int transport_type)
+{
+	GError *gerr = NULL;
+	guint timer_id;
+	GObex *obex;
+	GIOChannel *io;
+	GIOStatus status;
+	gsize bytes_written;
+
+	create_endpoints(&obex, &io, transport_type);
+
+	g_obex_add_request_function(obex, G_OBEX_OP_CONNECT,
+						handle_connect_req, &gerr);
+	g_obex_set_disconnect_function(obex, handle_connect_err, &gerr);
+
+	status = g_io_channel_write_chars(io, (char *) pkt_connect_req,
+						sizeof(pkt_connect_req),
+						&bytes_written, NULL);
+	g_assert_cmpint(status, ==, G_IO_STATUS_NORMAL);
+	g_assert_cmpuint(bytes_written, ==, sizeof(pkt_connect_req));
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, timeout, &gerr);
+
+	g_main_loop_run(mainloop);
+
+	g_source_remove(timer_id);
+	g_obex_unref(obex);
+	g_io_channel_unref(io);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	g_assert_no_error(gerr);
+}
+
+static void test_recv_connect_stream(void)
+{
+	recv_connect(SOCK_STREAM);
+}
+
+static void test_recv_connect_pkt(void)
+{
+	recv_connect(SOCK_SEQPACKET);
+}
+
+static void disconn_ev(GObex *obex, GError *err, gpointer user_data)
+{
+	GError **test_err = user_data;
+
+	if (!g_error_matches(err, G_OBEX_ERROR, G_OBEX_ERROR_DISCONNECTED))
+		g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Did not get expected disconnect error");
+
+	g_main_loop_quit(mainloop);
+}
+
+static void test_disconnect(void)
+{
+	GError *gerr = NULL;
+	guint timer_id;
+	GObex *obex;
+	GIOChannel *io;
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	g_obex_set_disconnect_function(obex, disconn_ev, &gerr);
+
+	timer_id = g_timeout_add_seconds(1, timeout, &gerr);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	g_io_channel_shutdown(io, FALSE, NULL);
+
+	g_main_loop_run(mainloop);
+
+	g_assert_no_error(gerr);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_obex_unref(obex);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+}
+
+static void test_ref_unref(void)
+{
+	GObex *obex;
+
+	obex = create_gobex(STDIN_FILENO, G_OBEX_TRANSPORT_STREAM, FALSE);
+
+	g_assert(obex != NULL);
+
+	obex = g_obex_ref(obex);
+
+	g_obex_unref(obex);
+	g_obex_unref(obex);
+}
+
+static void test_basic(void)
+{
+	GObex *obex;
+
+	obex = create_gobex(STDIN_FILENO, G_OBEX_TRANSPORT_STREAM, FALSE);
+
+	g_assert(obex != NULL);
+
+	g_obex_unref(obex);
+}
+
+static void test_null_io(void)
+{
+	GObex *obex;
+
+	obex = g_obex_new(NULL, 0, -1, -1);
+
+	g_assert(obex == NULL);
+}
+
+static void req_complete(GObex *obex, GError *err, GObexPacket *rsp,
+							gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (err != NULL)
+		d->err = g_error_copy(err);
+
+	g_main_loop_quit(d->mainloop);
+}
+
+static void test_connect(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ pkt_connect_req, sizeof(pkt_connect_req) } }, {
+				{ pkt_connect_rsp, sizeof(pkt_connect_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_obex_disconnect(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_disconnect_req, sizeof(pkt_disconnect_req) } }, {
+			{ pkt_disconnect_rsp, sizeof(pkt_disconnect_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_disconnect(obex, req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_auth(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ pkt_connect_req, sizeof(pkt_connect_req) },
+				{ pkt_auth_req, sizeof(pkt_auth_req) } }, {
+				{ pkt_unauth_rsp, sizeof(pkt_unauth_rsp) },
+				{ pkt_auth_rsp, sizeof(pkt_auth_rsp) } },
+				};
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_auth_fail(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+				{ pkt_connect_req, sizeof(pkt_connect_req) },
+				{ pkt_auth_req, sizeof(pkt_auth_req) } }, {
+				{ pkt_unauth_rsp, sizeof(pkt_unauth_rsp) },
+				{ pkt_unauth_rsp, sizeof(pkt_unauth_rsp) } },
+				};
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 2);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_setpath(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_setpath_req, sizeof(pkt_setpath_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_setpath(obex, "dir", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_setpath_up(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_setpath_up_req, sizeof(pkt_setpath_up_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_setpath(obex, "..", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_setpath_up_down(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_setpath_up_down_req,
+					sizeof(pkt_setpath_up_down_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_setpath(obex, "../dir", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_mkdir(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_mkdir_req, sizeof(pkt_mkdir_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_mkdir(obex, "dir", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_delete(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_delete_req, sizeof(pkt_delete_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_delete(obex, "foo.txt", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_copy(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_copy_req, sizeof(pkt_copy_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_copy(obex, "foo", "bar", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+static void test_move(void)
+{
+	GIOChannel *io;
+	GIOCondition cond;
+	guint io_id, timer_id;
+	GObex *obex;
+	struct test_data d = { 0, NULL, {
+			{ pkt_move_req, sizeof(pkt_move_req) } }, {
+			{ pkt_success_rsp, sizeof(pkt_success_rsp) } } };
+
+	create_endpoints(&obex, &io, SOCK_STREAM);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+	d.mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+	g_obex_move(obex, "foo", "bar", req_complete, &d, &d.err);
+	g_assert_no_error(d.err);
+
+	g_main_loop_run(d.mainloop);
+
+	g_assert_cmpuint(d.count, ==, 1);
+
+	g_main_loop_unref(d.mainloop);
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(d.err);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/gobex/null_io", test_null_io);
+	g_test_add_func("/gobex/basic", test_basic);
+	g_test_add_func("/gobex/ref_unref", test_ref_unref);
+
+	g_test_add_func("/gobex/test_disconnect", test_disconnect);
+
+	g_test_add_func("/gobex/test_recv_connect_stream",
+						test_recv_connect_stream);
+	g_test_add_func("/gobex/test_recv_connect_pkt",
+						test_recv_connect_pkt);
+	g_test_add_func("/gobex/test_send_connect_stream",
+						test_send_connect_stream);
+	g_test_add_func("/gobex/test_send_connect_pkt",
+						test_send_connect_pkt);
+	g_test_add_func("/gobex/test_recv_unexpected",
+						test_recv_unexpected);
+	g_test_add_func("/gobex/test_send_on_demand_stream",
+						test_send_on_demand_stream);
+	g_test_add_func("/gobex/test_send_on_demand_pkt",
+						test_send_on_demand_pkt);
+	g_test_add_func("/gobex/test_send_on_demand_fail_stream",
+					test_send_on_demand_fail_stream);
+	g_test_add_func("/gobex/test_send_on_demand_fail_pkt",
+					test_send_on_demand_fail_pkt);
+	g_test_add_func("/gobex/test_send_connect_req_stream",
+					test_send_connect_req_stream);
+	g_test_add_func("/gobex/test_send_connect_req_pkt",
+					test_send_connect_req_pkt);
+	g_test_add_func("/gobex/test_send_nval_connect_req_stream",
+					test_send_nval_connect_req_stream);
+	g_test_add_func("/gobex/test_send_nval_connect_req_pkt",
+					test_send_nval_connect_req_pkt);
+	g_test_add_func("/gobex/test_send_nval_connect_req_short_pkt",
+					test_send_nval_connect_req_short_pkt);
+	g_test_add_func("/gobex/test_send_connect_req_timeout_stream",
+					test_send_connect_req_timeout_stream);
+	g_test_add_func("/gobex/test_send_connect_req_timeout_pkt",
+					test_send_connect_req_timeout_pkt);
+
+
+	g_test_add_func("/gobex/test_cancel_req_immediate",
+					test_cancel_req_immediate);
+	g_test_add_func("/gobex/test_cancel_req_delay_stream",
+					test_cancel_req_delay_stream);
+	g_test_add_func("/gobex/test_cancel_req_delay_pkt",
+					test_cancel_req_delay_pkt);
+
+	g_test_add_func("/gobex/test_connect", test_connect);
+	g_test_add_func("/gobex/test_obex_disconnect", test_obex_disconnect);
+	g_test_add_func("/gobex/test_auth", test_auth);
+	g_test_add_func("/gobex/test_auth_fail", test_auth_fail);
+
+	g_test_add_func("/gobex/test_setpath", test_setpath);
+	g_test_add_func("/gobex/test_setpath_up", test_setpath_up);
+	g_test_add_func("/gobex/test_setpath_up_down", test_setpath_up_down);
+
+	g_test_add_func("/gobex/test_mkdir", test_mkdir);
+
+	g_test_add_func("/gobex/test_delete", test_delete);
+
+	g_test_add_func("/gobex/test_copy", test_copy);
+	g_test_add_func("/gobex/test_move", test_move);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-hfp.c b/bluez/repo/unit/test-hfp.c
new file mode 100644
index 0000000..f2b9622
--- /dev/null
+++ b/bluez/repo/unit/test-hfp.c
@@ -0,0 +1,861 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <sys/socket.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include "src/shared/hfp.h"
+#include "src/shared/tester.h"
+
+struct context {
+	guint watch_id;
+	int fd_server;
+	int fd_client;
+	struct hfp_gw *hfp;
+	struct hfp_hf *hfp_hf;
+	const struct test_data *data;
+	unsigned int pdu_offset;
+};
+
+struct test_pdu {
+	bool valid;
+	const uint8_t *data;
+	size_t size;
+	enum hfp_gw_cmd_type type;
+	bool fragmented;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+	hfp_result_func_t result_func;
+	hfp_response_func_t response_func;
+	hfp_hf_result_func_t hf_result_func;
+	GIOFunc test_handler;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define data_end()						\
+	{							\
+		.valid = false,					\
+	}
+
+#define type_pdu(cmd_type, args...)				\
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+		.type = cmd_type,				\
+	}
+
+#define frg_pdu(args...)					\
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+		.fragmented = true,				\
+	}
+
+#define define_test(name, function, result_function, args...)		\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		data.result_func = result_function;			\
+		tester_add(name, &data, NULL, function, NULL);		\
+		data.test_handler = test_handler;			\
+	} while (0)
+
+#define define_hf_test(name, function, result_func, response_function,	\
+								args...)\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		data.hf_result_func = result_func;			\
+		data.response_func = response_function;			\
+		tester_add(name, &data, NULL, function, NULL);		\
+		data.test_handler = test_hf_handler;			\
+	} while (0)
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->watch_id)
+		g_source_remove(context->watch_id);
+
+	test_free(context->data);
+
+	if (context->hfp)
+		hfp_gw_unref(context->hfp);
+
+	if (context->hfp_hf)
+		hfp_hf_unref(context->hfp_hf);
+
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context == NULL)
+		return FALSE;
+
+	destroy_context(context);
+	tester_test_passed();
+	return FALSE;
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(!pdu->valid);
+	context->watch_id = 0;
+
+	context_quit(context);
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+	if (!pdu || !pdu->valid)
+		return FALSE;
+
+	len = write(context->fd_server, pdu->data, pdu->size);
+	g_assert_cmpint(len, ==, pdu->size);
+
+	pdu = &context->data->pdu_list[context->pdu_offset];
+	if (pdu->fragmented)
+		g_idle_add(send_pdu, context);
+
+	return FALSE;
+}
+
+static gboolean test_hf_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	gchar buf[60];
+	gsize bytes_read;
+	GError *error = NULL;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto done;
+
+	/* dummy read */
+	g_io_channel_read_chars(channel, buf, 60, &bytes_read, &error);
+
+	send_pdu(context);
+
+	return TRUE;
+
+done:
+	context->watch_id = 0;
+
+	context_quit(context);
+
+	return FALSE;
+}
+
+static void cmd_handler(const char *command, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned int cmd_len = strlen(command);
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(cmd_len == pdu->size);
+	g_assert(!memcmp(command, pdu->data, cmd_len));
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void prefix_handler(struct hfp_context *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(type == pdu->type);
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+	const struct test_data *d = data;
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->watch_id = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				d->test_handler, context);
+
+	g_assert(context->watch_id > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd_server = sv[1];
+	context->fd_client = sv[0];
+	context->data = data;
+
+	return context;
+}
+
+static void test_init(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp = hfp_gw_new(context->fd_client);
+
+	g_assert(context->hfp);
+	g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
+
+	hfp_gw_unref(context->hfp);
+	context->hfp = NULL;
+
+	context_quit(context);
+}
+
+static void test_command_handler(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	const struct test_pdu *pdu;
+	ssize_t len;
+	bool ret;
+
+	context->hfp = hfp_gw_new(context->fd_client);
+	g_assert(context->hfp);
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	ret = hfp_gw_set_close_on_unref(context->hfp, true);
+	g_assert(ret);
+
+	ret = hfp_gw_set_command_handler(context->hfp, cmd_handler,
+								context, NULL);
+	g_assert(ret);
+
+	len = write(context->fd_server, pdu->data, pdu->size);
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context_quit(context);
+}
+
+static void test_register(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	const struct test_pdu *pdu;
+	ssize_t len;
+	bool ret;
+
+	context->hfp = hfp_gw_new(context->fd_client);
+	g_assert(context->hfp);
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	ret = hfp_gw_set_close_on_unref(context->hfp, true);
+	g_assert(ret);
+
+	if (context->data->result_func) {
+		ret = hfp_gw_register(context->hfp, context->data->result_func,
+					(char *)pdu->data, context, NULL);
+		g_assert(ret);
+	}
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd_server, pdu->data, pdu->size);
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context_quit(context);
+}
+
+static void test_fragmented(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp = hfp_gw_new(context->fd_client);
+	g_assert(context->hfp);
+
+	ret = hfp_gw_set_close_on_unref(context->hfp, true);
+	g_assert(ret);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_send_and_close(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp = hfp_gw_new(context->fd_client);
+	g_assert(context->hfp);
+
+	ret = hfp_gw_set_close_on_unref(context->hfp, true);
+	g_assert(ret);
+
+	send_pdu(context);
+
+	hfp_gw_unref(context->hfp);
+	context->hfp = NULL;
+
+	context_quit(context);
+}
+
+static void check_ustring_1(struct hfp_context *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned int i = 3, j = 0;
+	char str[10];
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(type == pdu->type);
+
+	g_assert(hfp_context_get_unquoted_string(result, str, sizeof(str)));
+
+	while (context->data->pdu_list[1].data[i] != '\r') {
+		g_assert(j < sizeof(str));
+		g_assert(str[j] == context->data->pdu_list[1].data[i]);
+
+		i++;
+		j++;
+	}
+
+	g_assert(str[j] == '\0');
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_ustring_2(struct hfp_context *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	char str[10];
+
+	memset(str, 'X', sizeof(str));
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(type == pdu->type);
+
+	g_assert(!hfp_context_get_unquoted_string(result, str, 3));
+
+	g_assert(str[3] == 'X');
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_string_1(struct hfp_context *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned int i = 4, j = 0;
+	char str[10];
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(type == pdu->type);
+
+	g_assert(hfp_context_get_string(result, str, sizeof(str)));
+
+	while (context->data->pdu_list[1].data[i] != '\"') {
+		g_assert(j < sizeof(str));
+		g_assert(str[j] == context->data->pdu_list[1].data[i]);
+
+		i++;
+		j++;
+	}
+
+	g_assert(context->data->pdu_list[1].data[i] == '\"');
+	g_assert(str[j] == '\0');
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_string_2(struct hfp_context *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	char str[10];
+
+	memset(str, 'X', sizeof(str));
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(type == pdu->type);
+
+	g_assert(!hfp_context_get_string(result, str, 3));
+
+	g_assert(str[3] == 'X');
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_string_3(struct hfp_context *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	g_assert(type == pdu->type);
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void test_hf_init(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+	g_assert(hfp_hf_set_close_on_unref(context->hfp_hf, true));
+
+	hfp_hf_unref(context->hfp_hf);
+	context->hfp_hf = NULL;
+
+	context_quit(context);
+}
+
+static bool unsolicited_resp = false;
+
+static void hf_unsolicited_resp_cb(struct hfp_context *context,
+							void *user_data) {
+	unsolicited_resp = true;
+}
+
+static void hf_response_with_data(enum hfp_result res,
+							enum hfp_error cme_err,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert(unsolicited_resp);
+	unsolicited_resp = false;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_cme_error_response_cb(enum hfp_result res,
+							enum hfp_error cme_err,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert_cmpint(res, ==, HFP_RESULT_CME_ERROR);
+	g_assert_cmpint(cme_err, ==, 30);
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_response_cb(enum hfp_result res, enum hfp_error cme_err,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void test_hf_send_command(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	const struct test_pdu *pdu;
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	if (context->data->response_func) {
+		if (context->data->hf_result_func) {
+			pdu = &context->data->pdu_list[context->pdu_offset++];
+
+			ret = hfp_hf_register(context->hfp_hf,
+						context->data->hf_result_func,
+						(char *)pdu->data,
+						NULL, NULL);
+			g_assert(ret);
+		}
+
+		pdu = &context->data->pdu_list[context->pdu_offset++];
+
+		ret = hfp_hf_send_command(context->hfp_hf,
+						context->data->response_func,
+						context, (char *)pdu->data);
+		g_assert(ret);
+	}
+
+	context_quit(context);
+}
+static void hf_chld_result_handler(struct hfp_context *hf_context,
+							void *user_data)
+{
+	struct context *context = user_data;
+	char str[3];
+
+	g_assert(hf_context);
+	g_assert(hfp_context_get_unquoted_string(hf_context, str,
+							sizeof(str)));
+	g_assert_cmpstr(str, ==, "1");
+	g_assert(hfp_context_get_unquoted_string(hf_context, str,
+							sizeof(str)));
+	g_assert_cmpstr(str, ==, "2x");
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_chld_skip_field(struct hfp_context *hf_context,
+							void *user_data)
+{
+	struct context *context = user_data;
+	char str[3];
+
+	g_assert(hf_context);
+
+	hfp_context_skip_field(hf_context);
+
+	g_assert(hfp_context_get_unquoted_string(hf_context, str,
+								sizeof(str)));
+	g_assert_cmpstr(str, ==, "2x");
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_clcc_result_handler(struct hfp_context *hf_context,
+							void *user_data)
+{
+	struct context *context = user_data;
+	char name[10];
+	uint32_t val1, val2;
+
+	g_assert(hf_context);
+	g_assert(hfp_context_open_container(hf_context));
+	g_assert(hfp_context_get_string(hf_context, name, sizeof(name)));
+	g_assert_cmpstr(name, ==, "call");
+	g_assert(hfp_context_open_container(hf_context));
+	g_assert(hfp_context_get_number(hf_context, &val1));
+	g_assert_cmpint(val1, ==, 0);
+	g_assert(hfp_context_get_number(hf_context, &val1));
+	g_assert_cmpint(val1, ==, 1);
+	g_assert(hfp_context_close_container(hf_context));
+	g_assert(hfp_context_close_container(hf_context));
+
+	g_assert(hfp_context_open_container(hf_context));
+	g_assert(hfp_context_get_string(hf_context, name, sizeof(name)));
+	g_assert_cmpstr(name, ==, "callsetup");
+	g_assert(hfp_context_open_container(hf_context));
+	g_assert(hfp_context_get_range(hf_context, &val1, &val2));
+	g_assert_cmpint(val1, ==, 0);
+	g_assert_cmpint(val2, ==, 3);
+	g_assert(hfp_context_close_container(hf_context));
+	g_assert(hfp_context_close_container(hf_context));
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_result_handler(struct hfp_context *result,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void test_hf_unsolicited(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	if (context->data->hf_result_func) {
+		const struct test_pdu *pdu;
+
+		pdu = &context->data->pdu_list[context->pdu_offset++];
+
+		ret = hfp_hf_register(context->hfp_hf,
+						context->data->hf_result_func,
+						(char *)pdu->data, context,
+						NULL);
+		g_assert(ret);
+	}
+
+	send_pdu(context);
+}
+
+static void test_hf_robustness(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	send_pdu(context);
+
+	hfp_hf_unref(context->hfp_hf);
+	context->hfp_hf = NULL;
+
+	context_quit(context);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	define_test("/hfp/test_init", test_init, NULL, data_end());
+	define_test("/hfp/test_cmd_handler_1", test_command_handler, NULL,
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\r'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F'),
+			data_end());
+	define_test("/hfp/test_cmd_handler_2", test_command_handler, NULL,
+			raw_pdu('A', 'T', 'D', '1', '2', '3', '4', '\r'),
+			raw_pdu('A', 'T', 'D', '1', '2', '3', '4'),
+			data_end());
+	define_test("/hfp/test_register_1", test_register, prefix_handler,
+			raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_COMMAND, 0),
+			data_end());
+	define_test("/hfp/test_register_2", test_register, prefix_handler,
+			raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '=', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_register_3", test_register, prefix_handler,
+			raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '?', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_READ, 0),
+			data_end());
+	define_test("/hfp/test_register_4", test_register, prefix_handler,
+			raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '=', '?',
+									'\r'),
+			type_pdu(HFP_GW_CMD_TYPE_TEST, 0),
+			data_end());
+	define_test("/hfp/test_register_5", test_register, prefix_handler,
+			raw_pdu('D', '\0'),
+			raw_pdu('A', 'T', 'D', '1', '2', '3', '4', '5', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_fragmented_1", test_fragmented, NULL,
+			frg_pdu('A'), frg_pdu('T'), frg_pdu('+'), frg_pdu('B'),
+			frg_pdu('R'), frg_pdu('S'), frg_pdu('F'), frg_pdu('\r'),
+			data_end());
+	define_test("/hfp/test_ustring_1", test_register, check_ustring_1,
+			raw_pdu('D', '\0'),
+			raw_pdu('A', 'T', 'D', '0', '1', '2', '3', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_ustring_2", test_register, check_ustring_2,
+			raw_pdu('D', '\0'),
+			raw_pdu('A', 'T', 'D', '0', '1', '2', '3', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_string_1", test_register, check_string_1,
+			raw_pdu('D', '\0'),
+			raw_pdu('A', 'T', 'D', '\"', '0', '1', '2', '3', '\"',
+									'\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_string_2", test_register, check_string_2,
+			raw_pdu('D', '\0'),
+			raw_pdu('A', 'T', 'D', '\"', '0', '1', '2', '3', '\"',
+									'\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_corrupted_1", test_register, check_string_3,
+			raw_pdu('D', '\0'),
+			raw_pdu('\r', 'A', 'T', 'D', '\"', '0', '1', '2', '3',
+								'\"', '\r'),
+			type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+			data_end());
+	define_test("/hfp/test_empty", test_send_and_close, NULL,
+			raw_pdu('\r'),
+			data_end());
+	define_hf_test("/hfp_hf/test_init", test_hf_init, NULL, NULL,
+			data_end());
+	define_hf_test("/hfp_hf/test_send_command_1", test_hf_send_command,
+			NULL, hf_response_cb,
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_send_command_2", test_hf_send_command,
+			hf_unsolicited_resp_cb,
+			hf_response_with_data,
+			raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\0'),
+			frg_pdu('\r', '\n', '+', 'B', 'R', 'S', 'F', '\r',
+									'\n'),
+			frg_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_send_command_3", test_hf_send_command,
+			NULL, hf_cme_error_response_cb,
+			raw_pdu('A', 'T', '+', 'C', 'H', 'L', 'D', '=',
+								'1', '\0'),
+			frg_pdu('\r', '\n', '+', 'C', 'M', 'E', ' ', 'E'),
+			frg_pdu('R', 'R', 'O', 'R', ':', '3', '0', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_unsolicited_1", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C'),
+			frg_pdu('C', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_unsolicited_2", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1'),
+			frg_pdu(',', '3', ',', '0', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_unsolicited_3", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r'), frg_pdu('\n'), frg_pdu('+'),
+			frg_pdu('C'), frg_pdu('L'), frg_pdu('C'), frg_pdu('C'),
+			frg_pdu(':'), frg_pdu('1'), frg_pdu(','), frg_pdu('3'),
+			frg_pdu(','), frg_pdu('0'), frg_pdu('\r'),
+			frg_pdu('\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_corrupted_1", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', 'X', '\r', '\n'),
+			frg_pdu('+', 'C', 'L', 'C', 'C', ':', '1', ',', '3'),
+			frg_pdu(',', '0', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_corrupted_2", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_empty", test_hf_robustness, NULL, NULL,
+			raw_pdu('\r'), data_end());
+
+	define_hf_test("/hfp_hf/test_unknown", test_hf_robustness, NULL, NULL,
+			raw_pdu('\r', '\n', 'B', 'R', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_context_parser_1", test_hf_unsolicited,
+			hf_clcc_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('+', 'C', 'L', 'C', 'C', ':'),
+			frg_pdu('(', '\"', 'c', 'a', 'l', 'l', '\"'),
+			frg_pdu('(', '0', ',', '1', ')', ')', ','),
+			frg_pdu('(', '\"', 'c', 'a', 'l', 'l', 's', 'e', 't'),
+			frg_pdu('u', 'p', '\"', ',', '(', '0', '-', '3', ')'),
+			frg_pdu(')', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_context_parser_2", test_hf_unsolicited,
+			hf_chld_result_handler, NULL,
+			raw_pdu('+', 'C', 'H', 'L', 'D', '\0'),
+			frg_pdu('+', 'C', 'H', 'L', 'D', ':'),
+			frg_pdu('1', ',', '2', 'x', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_context_skip_field", test_hf_unsolicited,
+			hf_chld_skip_field, NULL,
+			raw_pdu('+', 'C', 'H', 'L', 'D', '\0'),
+			frg_pdu('+', 'C', 'H', 'L', 'D', ':'),
+			frg_pdu('1', ',', '2', 'x', '\r', '\n'),
+			data_end());
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-hog.c b/bluez/repo/unit/test-hog.c
new file mode 100644
index 0000000..9f026e5
--- /dev/null
+++ b/bluez/repo/unit/test-hog.c
@@ -0,0 +1,440 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+#include "attrib/gattrib.h"
+
+#include "profiles/input/hog-lib.h"
+
+struct test_pdu {
+	bool valid;
+	const uint8_t *data;
+	size_t size;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+};
+
+struct context {
+	GAttrib *attrib;
+	struct bt_hog *hog;
+	guint source;
+	guint process;
+	int fd;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)    \
+{      \
+	.valid = true,		\
+	.data = data(args), \
+	.size = sizeof(data(args)),\
+}
+
+#define false_pdu()	\
+{						\
+		.valid = false, \
+}
+
+#define define_test(name, function, args...)      \
+	do {    \
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};		\
+		static struct test_data data;      \
+		data.test_name = g_strdup(name);   \
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, function, NULL);     \
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s", prefix, str);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	bt_hog_unref(context->hog);
+
+	g_attrib_unref(context->attrib);
+
+	g_free(context);
+
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+	util_hexdump('<', pdu->data, len, test_debug, "hog: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context->process = 0;
+
+	if (!context->data->pdu_list[context->pdu_offset].valid)
+		context_quit(context);
+
+	return FALSE;
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	unsigned char buf[512];
+	const struct test_pdu *pdu;
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		g_print("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	util_hexdump('>', buf, len, test_debug, "hog: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	context->process = g_idle_add(send_pdu, context);
+
+	return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context;
+	GIOChannel *channel, *att_io;
+	int err, sv[2], fd;
+	char name[] = "bluez-hog";
+	uint16_t vendor = 0x0002;
+	uint16_t product = 0x0001;
+	uint16_t version = 0x0001;
+
+	context = g_new0(struct context, 1);
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	att_io = g_io_channel_unix_new(sv[0]);
+
+	g_io_channel_set_close_on_unref(att_io, TRUE);
+
+	context->attrib = g_attrib_new(att_io, 23, false);
+	g_assert(context->attrib);
+
+	g_io_channel_unref(att_io);
+
+	fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
+	g_assert(fd > 0);
+
+	context->hog = bt_hog_new(fd, name, vendor, product, version, NULL);
+	g_assert(context->hog);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	return context;
+}
+
+static void test_hog(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	g_assert(bt_hog_attach(context->hog, context->attrib));
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	define_test("/TP/HGRF/RH/BV-01-I", test_hog,
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x12,
+			0x18, 0x05, 0x00, 0x08, 0x00, 0x12, 0x18),
+		raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
+		raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+			0x4b, 0x2a),
+		raw_pdu(0x08, 0x01, 0x00, 0x04, 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, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x07, 0x00, 0x02, 0x08, 0x00,
+			0x4b, 0x2a),
+		raw_pdu(0x0a, 0x04, 0x00),
+		raw_pdu(0x0b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+			0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+			0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+			0x15, 0x16),
+		raw_pdu(0x0a, 0x08, 0x00),
+		raw_pdu(0x0b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+			0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+			0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+			0x15, 0x16),
+		raw_pdu(0x0c, 0x04, 0x00, 0x16, 0x00),
+		raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+			0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+			0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+			0x15, 0x16),
+		raw_pdu(0x0c, 0x08, 0x00, 0x16, 0x00),
+		raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+			0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+			0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+			0x15, 0x16),
+		raw_pdu(0x0c, 0x04, 0x00, 0x2c, 0x00),
+		raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+			0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+			0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13),
+		raw_pdu(0x0c, 0x08, 0x00, 0x2c, 0x00),
+		raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+			0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+			0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13));
+
+	define_test("/TP/HGRF/RH/BV-08-I", test_hog,
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+			0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+		raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+		raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x03, 0x00, 0x0a, 0x04, 0x00,
+			0x4d, 0x2a),
+		raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+		raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+		raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x08, 0x00, 0x0a, 0x09, 0x00,
+			0x4d, 0x2a),
+		raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+		raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+		raw_pdu(0x0a, 0x04, 0x00),
+		raw_pdu(0x0b, 0xee, 0xee, 0xff, 0xff),
+		raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+		raw_pdu(0x05, 0x01, 0x05, 0x00, 0x08, 0x29),
+		raw_pdu(0x0a, 0x09, 0x00),
+		raw_pdu(0x0b, 0xff, 0xff, 0xee, 0xee),
+		raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+		raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x08, 0x29),
+		raw_pdu(0x0a, 0x05, 0x00),
+		raw_pdu(0x0b, 0x01, 0x03),
+		raw_pdu(0x0a, 0x0a, 0x00),
+		raw_pdu(0x0b, 0x02, 0x03));
+
+	define_test("/TP/HGRF/RH/BV-09-I", test_hog,
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x12,
+			0x18, 0x05, 0x00, 0x08, 0x00, 0x12, 0x18),
+		raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
+		raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+			0x4a, 0x2a),
+		raw_pdu(0x08, 0x01, 0x00, 0x04, 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, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x07, 0x00, 0x02, 0x08, 0x00,
+			0x4a, 0x2a),
+		raw_pdu(0x0a, 0x04, 0x00),
+		raw_pdu(0x0b, 0x01, 0x11, 0x00, 0x01),
+		raw_pdu(0x0a, 0x08, 0x00),
+		raw_pdu(0x0b, 0x01, 0x11, 0x00, 0x01));
+
+	define_test("/TP/HGRF/RH/BV-06-I", test_hog,
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+			0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+		raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+		raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x03, 0x00, 0x0a, 0x04, 0x00,
+			0x4d, 0x2a),
+		raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+		raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+		raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x08, 0x00, 0x0a, 0x09, 0x00,
+			0x4d, 0x2a),
+		raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+		raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+		raw_pdu(0x0a, 0x04, 0x00),
+		raw_pdu(0x0b, 0xee, 0xee, 0xff, 0xff),
+		raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+		raw_pdu(0x05, 0x01, 0x05, 0x00, 0x08, 0x29),
+		raw_pdu(0x0a, 0x09, 0x00),
+		raw_pdu(0x0b, 0xff, 0xff, 0xee, 0xee),
+		raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+		raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x08, 0x29),
+		raw_pdu(0x0a, 0x05, 0x00),
+		raw_pdu(0x0b, 0x01, 0x02),
+		raw_pdu(0x0a, 0x0a, 0x00),
+		raw_pdu(0x0b, 0x02, 0x02));
+
+	define_test("/TP/HGCF/RH/BV-01-I", test_hog,
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x06, 0x00, 0x12,
+			0x18, 0x07, 0x00, 0x0c, 0x00, 0x12, 0x18),
+		raw_pdu(0x10, 0x0d, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x01, 0x10, 0x0d, 0x00, 0x0a),
+		raw_pdu(0x08, 0x01, 0x00, 0x06, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x03, 0x00, 0x1a, 0x04, 0x00,
+			0x4d, 0x2a),
+		raw_pdu(0x08, 0x01, 0x00, 0x06, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+		raw_pdu(0x08, 0x07, 0x00, 0x0c, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
+		raw_pdu(0x08, 0x07, 0x00, 0x0c, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x09, 0x00, 0x1a, 0x0a, 0x00,
+			0x4d, 0x2a),
+		raw_pdu(0x08, 0x04, 0x00, 0x06, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+		raw_pdu(0x08, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x0a, 0x00, 0x0a),
+		raw_pdu(0x0a, 0x04, 0x00),
+		raw_pdu(0x0b, 0xed, 0x00),
+		raw_pdu(0x04, 0x05, 0x00, 0x06, 0x00),
+		raw_pdu(0x05, 0x01, 0x05, 0x00, 0x02, 0x29,
+			0x06, 0x00, 0x08, 0x29),
+		raw_pdu(0x0a, 0x0a, 0x00),
+		raw_pdu(0x0b, 0xed, 0x00),
+		raw_pdu(0x04, 0x0b, 0x00, 0x0c, 0x00),
+		raw_pdu(0x05, 0x01, 0x0b, 0x00, 0x02, 0x29,
+			0x0c, 0x00, 0x08, 0x29),
+		raw_pdu(0x0a, 0x06, 0x00),
+		raw_pdu(0x0b, 0x01, 0x01),
+		raw_pdu(0x0a, 0x0c, 0x00),
+		raw_pdu(0x0b, 0x02, 0x01),
+		raw_pdu(0x0a, 0x05, 0x00),
+		raw_pdu(0x0b, 0x00, 0x00),
+		raw_pdu(0x0a, 0x0b, 0x00),
+		raw_pdu(0x0b, 0x00, 0x00),
+		raw_pdu(0x12, 0x05, 0x00, 0x01, 0x00),
+		raw_pdu(0x13),
+		raw_pdu(0x12, 0x0b, 0x00, 0x01, 0x00),
+		raw_pdu(0x13));
+
+	define_test("/TP/HGRF/RH/BV-02-I", test_hog,
+		raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+			0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+		raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+		raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+		raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+			0x4b, 0x2a),
+		raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+		raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+		raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+		raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+		raw_pdu(0x09, 0x07, 0x08, 0x00, 0x02, 0x09, 0x00,
+			0x4b, 0x2a),
+		raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+		raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+		raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+		raw_pdu(0x0a, 0x04, 0x00),
+		raw_pdu(0x0b, 0x01, 0x02, 0x03),
+		raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+		raw_pdu(0x05, 0x01, 0x05, 0x00, 0x07, 0x29),
+		raw_pdu(0x0a, 0x09, 0x00),
+		raw_pdu(0x0b, 0x01, 0x02, 0x03),
+		raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+		raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x07, 0x29),
+		raw_pdu(0x0a, 0x05, 0x00),
+		raw_pdu(0x0b, 0x19, 0x2a),
+		raw_pdu(0x0a, 0x0a, 0x00),
+		raw_pdu(0x0b, 0x19, 0x2a));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-lib.c b/bluez/repo/unit/test-lib.c
new file mode 100644
index 0000000..bd4c5ee
--- /dev/null
+++ b/bluez/repo/unit/test-lib.c
@@ -0,0 +1,508 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013  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 <glib.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+static void test_ntoh64(const void *data)
+{
+	uint64_t test = 0x123456789abcdef;
+
+	g_assert(ntoh64(test) == be64toh(test));
+	g_assert(ntoh64(test) == be64_to_cpu(test));
+	tester_test_passed();
+}
+
+static void test_hton64(const void *data)
+{
+	uint64_t test = 0x123456789abcdef;
+
+	g_assert(hton64(test) == htobe64(test));
+	g_assert(hton64(test) == cpu_to_be64(test));
+	tester_test_passed();
+}
+
+static void test_sdp_get_access_protos_valid(const void *data)
+{
+	sdp_record_t *rec;
+	sdp_list_t *aproto, *apseq, *proto[2];
+	const uint8_t u8 = 1;
+	uuid_t l2cap, rfcomm;
+	sdp_data_t *channel;
+	int err;
+
+	rec = sdp_record_alloc();
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(rec, aproto);
+	sdp_set_add_access_protos(rec, aproto);
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	err = sdp_get_access_protos(rec, &aproto);
+	g_assert(err == 0);
+	sdp_list_foreach(aproto, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(aproto, NULL);
+
+	err = sdp_get_add_access_protos(rec, &aproto);
+	g_assert(err == 0);
+	sdp_list_foreach(aproto, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(aproto, NULL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_access_protos_nodata(const void *data)
+{
+	sdp_record_t *rec;
+	sdp_list_t *aproto;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	err = sdp_get_access_protos(rec, &aproto);
+	g_assert(err == -1 && errno == ENODATA);
+
+	err = sdp_get_add_access_protos(rec, &aproto);
+	g_assert(err == -1 && errno == ENODATA);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_access_protos_invalid_dtd1(const void *tdata)
+{
+	const uint32_t u32 = 0xdeadbeeb;
+	sdp_record_t *rec;
+	sdp_list_t *aproto;
+	sdp_data_t *data;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	data = sdp_data_alloc(SDP_UINT32, &u32);
+	g_assert(data != NULL);
+	sdp_attr_replace(rec, SDP_ATTR_PROTO_DESC_LIST, data);
+
+	err = sdp_get_access_protos(rec, &aproto);
+	g_assert(err == -1 && errno == EINVAL);
+
+	data = sdp_data_alloc(SDP_UINT32, &u32);
+	g_assert(data != NULL);
+	sdp_attr_replace(rec, SDP_ATTR_ADD_PROTO_DESC_LIST, data);
+
+	err = sdp_get_add_access_protos(rec, &aproto);
+	g_assert(err == -1 && errno == EINVAL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_access_protos_invalid_dtd2(const void *tdata)
+{
+	uint8_t dtd = SDP_UINT8, u8 = 0xff;
+	void *dtds = &dtd, *values = &u8;
+	sdp_record_t *rec;
+	sdp_list_t *aproto;
+	sdp_data_t *data;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	data = sdp_seq_alloc(&dtds, &values, 1);
+	g_assert(data != NULL);
+	sdp_attr_replace(rec, SDP_ATTR_PROTO_DESC_LIST, data);
+
+	err = sdp_get_access_protos(rec, &aproto);
+	g_assert(err == -1 && errno == EINVAL);
+
+	data = sdp_seq_alloc(&dtds, &values, 1);
+	g_assert(data != NULL);
+	sdp_attr_replace(rec, SDP_ATTR_ADD_PROTO_DESC_LIST, data);
+
+	err = sdp_get_add_access_protos(rec, &aproto);
+	g_assert(err == -1 && errno == EINVAL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_lang_attr_valid(const void *data)
+{
+	sdp_record_t *rec;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+	sdp_add_lang_attr(rec);
+
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == 0);
+
+	sdp_list_free(list, free);
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_lang_attr_nodata(const void *data)
+{
+	sdp_record_t *rec;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == -1 && errno == ENODATA);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_lang_attr_invalid_dtd(const void *tdata)
+{
+	uint8_t dtd1 = SDP_UINT16, dtd2 = SDP_UINT32;
+	uint32_t u32 = 0xdeadbeeb;
+	uint16_t u16 = 0x1234;
+	void *dtds1[] = { &dtd1, &dtd2, &dtd2 };
+	void *values1[] = { &u16, &u32, &u32 };
+	void *dtds2[] = { &dtd1, &dtd1, &dtd2 };
+	void *values2[] = { &u16, &u16, &u32 };
+	sdp_record_t *rec;
+	sdp_data_t *data;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	/* UINT32 */
+	data = sdp_data_alloc(SDP_UINT32, &u32);
+	g_assert(data != NULL);
+	sdp_attr_add(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, data);
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(UINT32) */
+	data = sdp_seq_alloc(&dtds1[1], &values1[1], 1);
+	sdp_attr_replace(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, data);
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(UINT16, UINT16) */
+	data = sdp_seq_alloc(dtds2, values2, 2);
+	sdp_attr_replace(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, data);
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(UINT16, UINT32, UINT32) */
+	data = sdp_seq_alloc(dtds1, values1, 3);
+	sdp_attr_replace(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, data);
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(UINT16, UINT16, UINT32) */
+	data = sdp_seq_alloc(dtds2, values2, 3);
+	sdp_attr_replace(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, data);
+	err = sdp_get_lang_attr(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_profile_descs_valid(const void *data)
+{
+	sdp_profile_desc_t profile;
+	sdp_record_t *rec;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	sdp_uuid16_create(&profile.uuid, NAP_PROFILE_ID);
+	profile.version = 0x0100;
+	list = sdp_list_append(NULL, &profile);
+	err = sdp_set_profile_descs(rec, list);
+	sdp_list_free(list, NULL);
+	g_assert(err == 0);
+
+	list = NULL;
+	err = sdp_get_profile_descs(rec, &list);
+	sdp_list_free(list, free);
+	g_assert(err == 0 && list != NULL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_profile_descs_nodata(const void *data)
+{
+	sdp_record_t *rec;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == ENODATA);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_profile_descs_invalid_dtd(const void *tdata)
+{
+	uint8_t dtd1 = SDP_UUID16, dtd2 = SDP_UINT32;
+	uint32_t u32 = 0xdeadbeeb;
+	uint16_t u16 = 0x1234;
+	void *dtds[1], *values[1];
+	void *dtds2[] = { &dtd1, &dtd2 };
+	void *values2[] = { &u16, &u32 };
+	sdp_record_t *rec;
+	sdp_data_t *data;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	/* UINT32 */
+	data = sdp_data_alloc(SDP_UINT32, &u32);
+	g_assert(data != NULL);
+	sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8() */
+	data = sdp_seq_alloc(NULL, NULL, 0);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(UINT32) */
+	data = sdp_seq_alloc(&dtds2[1], &values2[1], 1);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(SEQ8()) */
+	data = sdp_seq_alloc(NULL, NULL, 0);
+	dtds[0] = &data->dtd;
+	values[0] = data;
+	data = sdp_seq_alloc(dtds, values, 1);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(SEQ8(UINT32)) */
+	data = sdp_seq_alloc(&dtds2[1], &values2[1], 1);
+	dtds[0] = &data->dtd;
+	values[0] = data;
+	data = sdp_seq_alloc(dtds, values, 1);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(SEQ8(UUID16)) */
+	data = sdp_seq_alloc(dtds2, values2, 1);
+	dtds[0] = &data->dtd;
+	values[0] = data;
+	data = sdp_seq_alloc(dtds, values, 1);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* SEQ8(SEQ8(UUID16, UINT32)) */
+	data = sdp_seq_alloc(dtds2, values2, 2);
+	dtds[0] = &data->dtd;
+	values[0] = data;
+	data = sdp_seq_alloc(dtds, values, 1);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_profile_descs_workaround(const void *tdata)
+{
+	uint8_t dtd1 = SDP_UUID16, dtd2 = SDP_UINT16, dtd3 = SDP_UINT32;
+	uint16_t u16 = 0x1234;
+	uint32_t u32 = 0xdeadbeeb;
+	void *dtds[] = { &dtd1, &dtd2 };
+	void *values[] = { &u16, &u16 };
+	void *dtds2[] = { &dtd1, &dtd3 };
+	void *values2[] = { &u16, &u32 };
+	sdp_record_t *rec;
+	sdp_data_t *data;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	/* SEQ8(UUID16) */
+	data = sdp_seq_alloc(dtds, values, 1);
+	sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	list = NULL;
+	err = sdp_get_profile_descs(rec, &list);
+	sdp_list_free(list, free);
+	g_assert(err == 0 && list != NULL);
+
+	/* SEQ8(UUID16, UINT16) */
+	data = sdp_seq_alloc(dtds, values, 2);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	list = NULL;
+	err = sdp_get_profile_descs(rec, &list);
+	sdp_list_free(list, free);
+	g_assert(err == 0 && list != NULL);
+
+	/* SEQ8(UUID16) */
+	data = sdp_seq_alloc(dtds, values, 1);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	list = NULL;
+	err = sdp_get_profile_descs(rec, &list);
+	sdp_list_free(list, free);
+	g_assert(err == 0 && list != NULL);
+
+	/* SEQ8(UUID16, UINT32) */
+	data = sdp_seq_alloc(dtds2, values2, 2);
+	sdp_attr_replace(rec, SDP_ATTR_PFILE_DESC_LIST, data);
+	list = NULL;
+	err = sdp_get_profile_descs(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+static void test_sdp_get_server_ver(const void *tdata)
+{
+	uint16_t u16 = 0x1234;
+	uint32_t u32 = 0xdeadbeeb;
+	uint8_t dtd1 = SDP_UINT16, dtd2 = SDP_UINT32;
+	void *dtds1[] = { &dtd1 };
+	void *values1[] = { &u16 };
+	void *dtds2[] = { &dtd2 };
+	void *values2[] = { &u32 };
+	sdp_record_t *rec;
+	sdp_data_t *data;
+	sdp_list_t *list;
+	int err;
+
+	rec = sdp_record_alloc();
+
+	err = sdp_get_server_ver(rec, &list);
+	g_assert(err == -1 && errno == ENODATA);
+
+	/* Valid DTD */
+	data = sdp_seq_alloc(dtds1, values1, 1);
+	sdp_attr_add(rec, SDP_ATTR_VERSION_NUM_LIST, data);
+	err = sdp_get_server_ver(rec, &list);
+	g_assert(err == 0 && list != NULL);
+	sdp_list_free(list, NULL);
+
+	/* Invalid: UINT32 */
+	data = sdp_data_alloc(SDP_UINT32, &u32);
+	sdp_attr_replace(rec, SDP_ATTR_VERSION_NUM_LIST, data);
+	err = sdp_get_server_ver(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* Invalid: SEQ8() */
+	data = sdp_seq_alloc(NULL, NULL, 0);
+	sdp_attr_replace(rec, SDP_ATTR_VERSION_NUM_LIST, data);
+	err = sdp_get_server_ver(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	/* Invalid: SEQ8(UINT32) */
+	data = sdp_seq_alloc(dtds2, values2, 1);
+	sdp_attr_replace(rec, SDP_ATTR_VERSION_NUM_LIST, data);
+	err = sdp_get_server_ver(rec, &list);
+	g_assert(err == -1 && errno == EINVAL);
+
+	sdp_record_free(rec);
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/lib/ntoh64", NULL, NULL, test_ntoh64, NULL);
+	tester_add("/lib/hton64", NULL, NULL, test_hton64, NULL);
+
+	tester_add("/lib/sdp_get_access_protos/valid", NULL, NULL,
+				test_sdp_get_access_protos_valid, NULL);
+	tester_add("/lib/sdp_get_access_protos/nodata", NULL, NULL,
+				test_sdp_get_access_protos_nodata, NULL);
+	tester_add("/lib/sdp_get_access_protos/invalid_dtd1", NULL, NULL,
+				test_sdp_get_access_protos_invalid_dtd1, NULL);
+	tester_add("/lib/sdp_get_access_protos/invalid_dtd2", NULL, NULL,
+				test_sdp_get_access_protos_invalid_dtd2, NULL);
+
+	tester_add("/lib/sdp_get_lang_attr/valid", NULL, NULL,
+					test_sdp_get_lang_attr_valid, NULL);
+	tester_add("/lib/sdp_get_lang_attr/nodata", NULL, NULL,
+					test_sdp_get_lang_attr_nodata, NULL);
+	tester_add("/lib/sdp_get_lang_attr/invalid_dtd", NULL, NULL,
+				test_sdp_get_lang_attr_invalid_dtd, NULL);
+
+	tester_add("/lib/sdp_get_profile_descs/valid", NULL, NULL,
+					test_sdp_get_profile_descs_valid, NULL);
+	tester_add("/lib/sdp_get_profile_descs/nodata", NULL, NULL,
+				test_sdp_get_profile_descs_nodata, NULL);
+	tester_add("/lib/sdp_get_profile_descs/invalid_dtd", NULL, NULL,
+				test_sdp_get_profile_descs_invalid_dtd, NULL);
+	/* Test for workaround commented on sdp_get_profile_descs() */
+	tester_add("/lib/sdp_get_profile_descs/workaround", NULL, NULL,
+				test_sdp_get_profile_descs_workaround, NULL);
+
+	tester_add("/lib/sdp_get_server_ver", NULL, NULL,
+					test_sdp_get_server_ver, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-mgmt.c b/bluez/repo/unit/test-mgmt.c
new file mode 100644
index 0000000..c67678b
--- /dev/null
+++ b/bluez/repo/unit/test-mgmt.c
@@ -0,0 +1,455 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/mgmt.h"
+
+struct context {
+	GMainLoop *main_loop;
+	int fd;
+	struct mgmt *mgmt_client;
+	guint server_source;
+	GList *handler_list;
+};
+
+enum action {
+	ACTION_PASSED,
+	ACTION_IGNORE,
+	ACTION_RESPOND,
+};
+
+struct handler {
+	const void *cmd_data;
+	uint16_t cmd_size;
+	const void *rsp_data;
+	uint16_t rsp_size;
+	uint8_t rsp_status;
+	bool match_prefix;
+	enum action action;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%s%s\n", prefix, str);
+}
+
+static void context_quit(struct context *context)
+{
+	g_main_loop_quit(context->main_loop);
+}
+
+static void check_actions(struct context *context, int fd,
+					const void *data, uint16_t size)
+{
+	GList *list;
+
+	for (list = g_list_first(context->handler_list); list;
+						list = g_list_next(list)) {
+		struct handler *handler = list->data;
+		int ret;
+
+		if (handler->match_prefix) {
+			if (size < handler->cmd_size)
+				continue;
+		} else {
+			if (size != handler->cmd_size)
+				continue;
+		}
+
+		if (memcmp(data, handler->cmd_data, handler->cmd_size))
+			continue;
+
+		switch (handler->action) {
+		case ACTION_PASSED:
+			context_quit(context);
+			return;
+		case ACTION_RESPOND:
+			ret = write(fd, handler->rsp_data, handler->rsp_size);
+			g_assert(ret >= 0);
+			return;
+		case ACTION_IGNORE:
+			return;
+		}
+	}
+
+	g_test_message("Command not handled\n");
+	g_assert_not_reached();
+}
+
+static gboolean server_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	unsigned char buf[512];
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	result = read(fd, buf, sizeof(buf));
+	if (result < 0)
+		return FALSE;
+
+	check_actions(context, fd, buf, result);
+
+	return TRUE;
+}
+
+static struct context *create_context(void)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	context->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(context->main_loop);
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->fd = sv[0];
+	channel = g_io_channel_unix_new(sv[0]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->server_source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				server_handler, context);
+	g_assert(context->server_source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->mgmt_client = mgmt_new(sv[1]);
+	g_assert(context->mgmt_client);
+
+	if (g_test_verbose() == TRUE)
+		mgmt_set_debug(context->mgmt_client,
+					mgmt_debug, "mgmt: ", NULL);
+
+	mgmt_set_close_on_unref(context->mgmt_client, true);
+
+	return context;
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	g_list_free_full(context->handler_list, g_free);
+
+	g_source_remove(context->server_source);
+
+	mgmt_unref(context->mgmt_client);
+
+	g_main_loop_unref(context->main_loop);
+
+	g_free(context);
+}
+
+static void add_action(struct context *context,
+				const void *cmd_data, uint16_t cmd_size,
+				const void *rsp_data, uint16_t rsp_size,
+				uint8_t rsp_status, bool match_prefix,
+				enum action action)
+{
+	struct handler *handler = g_new0(struct handler, 1);
+
+	handler->cmd_data = cmd_data;
+	handler->cmd_size = cmd_size;
+	handler->rsp_data = rsp_data;
+	handler->rsp_size = rsp_size;
+	handler->rsp_status = rsp_status;
+	handler->match_prefix = match_prefix;
+	handler->action = action;
+
+	context->handler_list = g_list_append(context->handler_list, handler);
+}
+
+struct command_test_data {
+	uint16_t opcode;
+	uint16_t index;
+	uint16_t length;
+	const void *param;
+	const void *cmd_data;
+	uint16_t cmd_size;
+	const void *rsp_data;
+	uint16_t rsp_size;
+	uint8_t rsp_status;
+};
+
+static const unsigned char read_version_command[] =
+				{ 0x01, 0x00, 0xff, 0xff, 0x00, 0x00 };
+static const unsigned char read_version_response[] =
+				{ 0x01, 0x00, 0xff, 0xff, 0x06, 0x00,
+				0x01, 0x00, 0x00, 0x01, 0x06, 0x00 };
+
+static const struct command_test_data command_test_1 = {
+	.opcode = MGMT_OP_READ_VERSION,
+	.index = MGMT_INDEX_NONE,
+	.cmd_data = read_version_command,
+	.cmd_size = sizeof(read_version_command),
+	.rsp_data = read_version_response,
+	.rsp_size = sizeof(read_version_response),
+	.rsp_status = MGMT_STATUS_SUCCESS,
+};
+
+static const unsigned char read_info_command[] =
+				{ 0x04, 0x00, 0x00, 0x02, 0x00, 0x00 };
+
+static const struct command_test_data command_test_2 = {
+	.opcode = MGMT_OP_READ_INFO,
+	.index = 512,
+	.cmd_data = read_info_command,
+	.cmd_size = sizeof(read_info_command),
+};
+
+static const unsigned char invalid_index_response[] =
+				{ 0x02, 0x00, 0xff, 0xff, 0x03, 0x00,
+				0x01, 0x00, 0x11 };
+
+static const struct command_test_data command_test_3 = {
+	.opcode = MGMT_OP_READ_VERSION,
+	.index = MGMT_INDEX_NONE,
+	.cmd_data = read_version_command,
+	.cmd_size = sizeof(read_version_command),
+	.rsp_data = invalid_index_response,
+	.rsp_size = sizeof(invalid_index_response),
+	.rsp_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const unsigned char event_index_added[] =
+				{ 0x04, 0x00, 0x01, 0x00, 0x00, 0x00 };
+
+static const struct command_test_data event_test_1 = {
+	.opcode = MGMT_EV_INDEX_ADDED,
+	.index = MGMT_INDEX_NONE,
+	.cmd_data = event_index_added,
+	.cmd_size = sizeof(event_index_added),
+};
+
+static void test_command(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	add_action(context, test->cmd_data, test->cmd_size,
+			test->rsp_data, test->rsp_size, test->rsp_status,
+			false, ACTION_PASSED);
+
+	mgmt_send(context->mgmt_client, test->opcode, test->index,
+					test->length, test->param,
+						NULL ,NULL, NULL);
+
+	execute_context(context);
+}
+
+static void response_cb(uint8_t status, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct context *context = user_data;
+	struct handler *handler = context->handler_list->data;
+
+	g_assert_cmpint(status, ==, handler->rsp_status);
+
+	context_quit(context);
+}
+
+static void test_response(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	add_action(context, test->cmd_data, test->cmd_size,
+			test->rsp_data, test->rsp_size, test->rsp_status,
+			false, ACTION_RESPOND);
+
+	mgmt_send(context->mgmt_client, test->opcode, test->index,
+					test->length, test->param,
+					response_cb, context, NULL);
+
+	execute_context(context);
+}
+
+static void event_cb(uint16_t index, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	if (g_test_verbose())
+		printf("Event received\n");
+
+	context_quit(context);
+}
+
+static void test_event(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+						event_cb, context, NULL);
+
+	g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
+								test->cmd_size);
+
+	execute_context(context);
+}
+
+static void test_event2(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+						event_cb, context, NULL);
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+						event_cb, context, NULL);
+
+	g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
+								test->cmd_size);
+
+	execute_context(context);
+}
+
+static void unregister_all_cb(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct context *context = user_data;
+
+	mgmt_unregister_all(context->mgmt_client);
+
+	context_quit(context);
+}
+
+static void test_unregister_all(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+					unregister_all_cb, context, NULL);
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+						event_cb, context, NULL);
+
+	g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
+								test->cmd_size);
+
+	execute_context(context);
+}
+
+static void unregister_index_cb(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct context *context = user_data;
+
+	mgmt_unregister_index(context->mgmt_client, index);
+
+	context_quit(context);
+}
+
+static void destroy_cb(uint16_t index, uint16_t length, const void *param,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	mgmt_unref(context->mgmt_client);
+	context->mgmt_client = NULL;
+
+	context_quit(context);
+}
+
+static void test_unregister_index(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+					unregister_index_cb, context, NULL);
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+						event_cb, context, NULL);
+
+	g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
+								test->cmd_size);
+
+	execute_context(context);
+}
+
+static void test_destroy(gconstpointer data)
+{
+	const struct command_test_data *test = data;
+	struct context *context = create_context();
+
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+					destroy_cb, context, NULL);
+	mgmt_register(context->mgmt_client, test->opcode, test->index,
+						event_cb, context, NULL);
+
+	g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
+								test->cmd_size);
+
+	execute_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_data_func("/mgmt/command/1", &command_test_1, test_command);
+	g_test_add_data_func("/mgmt/command/2", &command_test_2, test_command);
+
+	g_test_add_data_func("/mgmt/response/1", &command_test_1,
+								test_response);
+	g_test_add_data_func("/mgmt/response/2", &command_test_3,
+								test_response);
+
+	g_test_add_data_func("/mgmt/event/1", &event_test_1, test_event);
+	g_test_add_data_func("/mgmt/event/2", &event_test_1, test_event2);
+
+	g_test_add_data_func("/mgmt/unregister/1", &event_test_1,
+							test_unregister_all);
+	g_test_add_data_func("/mgmt/unregister/2", &event_test_1,
+							test_unregister_index);
+
+	g_test_add_data_func("/mgmt/destroy/1", &event_test_1, test_destroy);
+
+	return g_test_run();
+}
diff --git a/bluez/repo/unit/test-queue.c b/bluez/repo/unit/test-queue.c
new file mode 100644
index 0000000..d912a64
--- /dev/null
+++ b/bluez/repo/unit/test-queue.c
@@ -0,0 +1,281 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/tester.h"
+
+static void test_basic(const void *data)
+{
+	struct queue *queue;
+	unsigned int n, i;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	for (n = 0; n < 1024; n++) {
+		for (i = 1; i < n + 2; i++)
+			queue_push_tail(queue, UINT_TO_PTR(i));
+
+		g_assert(queue_length(queue) == n + 1);
+
+		for (i = 1; i < n + 2; i++) {
+			void *ptr;
+
+			ptr = queue_pop_head(queue);
+			g_assert(ptr != NULL);
+			g_assert(i == PTR_TO_UINT(ptr));
+		}
+
+		g_assert(queue_isempty(queue) == true);
+	}
+
+	queue_destroy(queue, NULL);
+	tester_test_passed();
+}
+
+static void foreach_destroy(void *data, void *user_data)
+{
+	struct queue *queue = user_data;
+
+	queue_destroy(queue, NULL);
+}
+
+static void test_foreach_destroy(const void *data)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	queue_push_tail(queue, UINT_TO_PTR(1));
+	queue_push_tail(queue, UINT_TO_PTR(2));
+
+	queue_foreach(queue, foreach_destroy, queue);
+	tester_test_passed();
+}
+
+static void foreach_remove(void *data, void *user_data)
+{
+	struct queue *queue = user_data;
+
+	g_assert(queue_remove(queue, data));
+}
+
+static void test_foreach_remove(const void *data)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	queue_push_tail(queue, UINT_TO_PTR(1));
+	queue_push_tail(queue, UINT_TO_PTR(2));
+
+	queue_foreach(queue, foreach_remove, queue);
+	queue_destroy(queue, NULL);
+	tester_test_passed();
+}
+
+static void foreach_remove_all(void *data, void *user_data)
+{
+	struct queue *queue = user_data;
+
+	queue_remove_all(queue, NULL, NULL, NULL);
+}
+
+static void test_foreach_remove_all(const void *data)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	queue_push_tail(queue, UINT_TO_PTR(1));
+	queue_push_tail(queue, UINT_TO_PTR(2));
+
+	queue_foreach(queue, foreach_remove_all, queue);
+	queue_destroy(queue, NULL);
+	tester_test_passed();
+}
+
+static void foreach_remove_backward(void *data, void *user_data)
+{
+	struct queue *queue = user_data;
+
+	queue_remove(queue, UINT_TO_PTR(2));
+	queue_remove(queue, UINT_TO_PTR(1));
+}
+
+static void test_foreach_remove_backward(const void *data)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	queue_push_tail(queue, UINT_TO_PTR(1));
+	queue_push_tail(queue, UINT_TO_PTR(2));
+
+	queue_foreach(queue, foreach_remove_backward, queue);
+	queue_destroy(queue, NULL);
+	tester_test_passed();
+}
+
+static struct queue *static_queue;
+
+static void destroy_remove(void *user_data)
+{
+	queue_remove(static_queue, user_data);
+}
+
+static void test_destroy_remove(const void *data)
+{
+	static_queue = queue_new();
+
+	g_assert(static_queue != NULL);
+
+	queue_push_tail(static_queue, UINT_TO_PTR(1));
+	queue_push_tail(static_queue, UINT_TO_PTR(2));
+
+	queue_destroy(static_queue, destroy_remove);
+	tester_test_passed();
+}
+
+static void test_push_after(const void *data)
+{
+	struct queue *queue;
+	unsigned int len, i;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	/*
+	 * Pre-populate queue. Initial elements are:
+	 *   [ NULL, 2, 5 ]
+	 */
+	g_assert(queue_push_tail(queue, NULL));
+	g_assert(queue_push_tail(queue, UINT_TO_PTR(2)));
+	g_assert(queue_push_tail(queue, UINT_TO_PTR(5)));
+	g_assert(queue_length(queue) == 3);
+
+	/* Invalid insertion */
+	g_assert(!queue_push_after(queue, UINT_TO_PTR(6), UINT_TO_PTR(1)));
+
+	/* Valid insertions */
+	g_assert(queue_push_after(queue, NULL, UINT_TO_PTR(1)));
+	g_assert(queue_push_after(queue, UINT_TO_PTR(2), UINT_TO_PTR(3)));
+	g_assert(queue_push_after(queue, UINT_TO_PTR(3), UINT_TO_PTR(4)));
+	g_assert(queue_push_after(queue, UINT_TO_PTR(5), UINT_TO_PTR(6)));
+
+	g_assert(queue_peek_head(queue) == NULL);
+	g_assert(queue_peek_tail(queue) == UINT_TO_PTR(6));
+
+	/*
+	 * Queue should contain 7 elements:
+	 *   [ NULL, 1, 2, 3, 4, 5, 6 ]
+	 */
+	len = queue_length(queue);
+	g_assert(len == 7);
+
+	for (i = 0; i < 7; i++)
+		g_assert(queue_pop_head(queue) == UINT_TO_PTR(i));
+
+	/* Test with identical elements */
+	g_assert(queue_push_head(queue, UINT_TO_PTR(1)));
+	g_assert(queue_push_head(queue, UINT_TO_PTR(1)));
+	g_assert(queue_push_head(queue, UINT_TO_PTR(1)));
+	g_assert(queue_push_after(queue, UINT_TO_PTR(1), UINT_TO_PTR(0)));
+
+	g_assert(queue_pop_head(queue) == UINT_TO_PTR(1));
+	g_assert(queue_pop_head(queue) == UINT_TO_PTR(0));
+	g_assert(queue_pop_head(queue) == UINT_TO_PTR(1));
+	g_assert(queue_pop_head(queue) == UINT_TO_PTR(1));
+
+	queue_destroy(queue, NULL);
+	tester_test_passed();
+}
+
+static bool match_int(const void *a, const void *b)
+{
+	int i = PTR_TO_INT(a);
+	int j = PTR_TO_INT(b);
+
+	return i == j;
+}
+
+static bool match_ptr(const void *a, const void *b)
+{
+	return a == b;
+}
+
+static void test_remove_all(const void *data)
+{
+	struct queue *queue;
+
+	queue = queue_new();
+	g_assert(queue != NULL);
+
+	g_assert(queue_push_tail(queue, INT_TO_PTR(10)));
+
+	g_assert(queue_remove_all(queue, match_int, INT_TO_PTR(10), NULL) == 1);
+	g_assert(queue_isempty(queue));
+
+	g_assert(queue_push_tail(queue, NULL));
+	g_assert(queue_remove_all(queue, match_ptr, NULL, NULL) == 1);
+	g_assert(queue_isempty(queue));
+
+	g_assert(queue_push_tail(queue, UINT_TO_PTR(0)));
+	g_assert(queue_remove_all(queue, match_int, UINT_TO_PTR(0), NULL) == 1);
+	g_assert(queue_isempty(queue));
+
+	queue_destroy(queue, NULL);
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/queue/basic", NULL, NULL, test_basic, NULL);
+	tester_add("/queue/foreach_destroy", NULL, NULL,
+						test_foreach_destroy, NULL);
+	tester_add("/queue/foreach_remove",  NULL, NULL,
+						test_foreach_remove, NULL);
+	tester_add("/queue/foreach_remove_all",  NULL, NULL,
+						test_foreach_remove_all, NULL);
+	tester_add("/queue/foreach_remove_backward", NULL, NULL,
+					test_foreach_remove_backward, NULL);
+	tester_add("/queue/destroy_remove",  NULL, NULL,
+						test_destroy_remove, NULL);
+	tester_add("/queue/push_after",  NULL, NULL, test_push_after, NULL);
+	tester_add("/queue/remove_all",  NULL, NULL, test_remove_all, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-ringbuf.c b/bluez/repo/unit/test-ringbuf.c
new file mode 100644
index 0000000..a97524e
--- /dev/null
+++ b/bluez/repo/unit/test-ringbuf.c
@@ -0,0 +1,157 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include "src/shared/ringbuf.h"
+#include "src/shared/tester.h"
+
+static unsigned int nlpo2(unsigned int x)
+{
+	x--;
+	x |= (x >> 1);
+	x |= (x >> 2);
+	x |= (x >> 4);
+	x |= (x >> 8);
+	x |= (x >> 16);
+	return x + 1;
+}
+
+static unsigned int fls(unsigned int x)
+{
+	return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+static unsigned int align_power2(unsigned int u)
+{
+	return 1 << fls(u - 1);
+}
+
+static void test_power2(const void *data)
+{
+	size_t i;
+
+	for (i = 1; i < 1000000; i++) {
+		size_t size1, size2, size3 = 1;
+
+		size1 = nlpo2(i);
+		size2 = align_power2(i);
+
+		/* Find the next power of two */
+		while (size3 < i && size3 < SIZE_MAX)
+			size3 <<= 1;
+
+		tester_debug("%zu -> size1=%zu size2=%zu size3=%zu\n",
+						i, size1, size2, size3);
+
+		g_assert(size1 == size2);
+		g_assert(size2 == size3);
+		g_assert(size3 == size1);
+	}
+
+	tester_test_passed();
+}
+
+static void test_alloc(const void *data)
+{
+	int i;
+
+	for (i = 2; i < 10000; i++) {
+		struct ringbuf *rb;
+
+		tester_debug("Iteration %i\n", i);
+
+		rb = ringbuf_new(i);
+		g_assert(rb != NULL);
+
+		g_assert(ringbuf_capacity(rb) == ringbuf_avail(rb));
+
+		ringbuf_free(rb);
+	}
+
+	tester_test_passed();
+}
+
+static void test_printf(const void *data)
+{
+	static size_t rb_size = 500;
+	static size_t rb_capa = 512;
+	struct ringbuf *rb;
+	int i;
+
+	rb = ringbuf_new(rb_size);
+	g_assert(rb != NULL);
+	g_assert(ringbuf_capacity(rb) == rb_capa);
+
+	for (i = 0; i < 10000; i++) {
+		size_t len, count = i % rb_capa;
+		char *str, *ptr;
+
+		if (!count)
+			continue;
+
+		tester_debug("Iteration %i\n", i);
+
+		len = asprintf(&str, "%*c", (int) count, 'x');
+		g_assert(len == count);
+
+		len = ringbuf_printf(rb, "%s", str);
+		g_assert(len == count);
+		g_assert(ringbuf_len(rb) == count);
+		g_assert(ringbuf_avail(rb) == rb_capa - len);
+
+		ptr = ringbuf_peek(rb, 0, &len);
+		g_assert(ptr != NULL);
+		g_assert(len == count);
+		g_assert(strncmp(str, ptr, len) == 0);
+
+		len = ringbuf_drain(rb, count);
+		g_assert(len == count);
+		g_assert(ringbuf_len(rb) == 0);
+		g_assert(ringbuf_avail(rb) == rb_capa);
+
+		free(str);
+	}
+
+	ringbuf_free(rb);
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/ringbuf/power2", NULL, NULL, test_power2, NULL);
+	tester_add("/ringbuf/alloc", NULL, NULL, test_alloc, NULL);
+	tester_add("/ringbuf/printf", NULL, NULL, test_printf, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-sdp.c b/bluez/repo/unit/test-sdp.c
new file mode 100644
index 0000000..ac921a9
--- /dev/null
+++ b/bluez/repo/unit/test-sdp.c
@@ -0,0 +1,2814 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/log.h"
+#include "src/sdpd.h"
+
+struct sdp_pdu {
+	bool valid;
+	const void *raw_data;
+	size_t raw_size;
+	uint8_t cont_len;
+};
+
+struct test_data {
+	int mtu;
+	struct sdp_pdu *pdu_list;
+};
+
+#define raw_data(args...) ((const unsigned char[]) { args })
+#define build_u128(args...) ((const uint128_t) { .data = { args } })
+
+#define raw_pdu(args...) \
+	{							\
+		.valid = true,					\
+		.raw_data = raw_data(args),			\
+		.raw_size = sizeof(raw_data(args)),		\
+	}
+
+#define raw_pdu_cont(cont, args...) \
+	{							\
+		.valid = true,					\
+		.raw_data = raw_data(args),			\
+		.raw_size = sizeof(raw_data(args)),		\
+		.cont_len = cont,				\
+	}
+
+#define define_test(name, _mtu, args...) \
+	do {								\
+		const struct sdp_pdu pdus[] = {				\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.mtu = _mtu;					\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, test_sdp, NULL);		\
+	} while (0)
+
+#define define_ss(name, args...) define_test("/TP/SERVER/SS/" name, 48, args)
+#define define_sa(name, args...) define_test("/TP/SERVER/SA/" name, 48, args)
+#define define_ssa(name, args...) define_test("/TP/SERVER/SSA/" name, 48, args)
+#define define_brw(name, args...) define_test("/TP/SERVER/BRW/" name, 672, args)
+
+/* SDP Data Element (DE) tests */
+struct test_data_de {
+	const void *input_data;
+	size_t input_size;
+	sdp_data_t expected;
+};
+
+#define exp_data(_dtd, val_type, val_data) \
+	((const sdp_data_t) {			\
+		.dtd = _dtd,			\
+		.val.val_type = val_data,	\
+	})
+
+#define define_test_de_attr(name, input, exp) \
+	do {								\
+		static struct test_data_de data;			\
+		data.input_data = input;				\
+		data.input_size = sizeof(input);			\
+		data.expected = exp;					\
+		tester_add("/sdp/DE/ATTR/" name, &data,	NULL,		\
+					test_sdp_de_attr, NULL);	\
+	} while (0)
+
+struct context {
+	guint server_source;
+	guint client_source;
+	int fd;
+	uint8_t cont_data[16];
+	uint8_t cont_size;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+static void sdp_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s\n", prefix, str);
+}
+
+static void destroy_context(struct context *context)
+{
+	sdp_svcdb_collect_all(context->fd);
+	sdp_svcdb_reset();
+
+	g_source_remove(context->server_source);
+	g_source_remove(context->client_source);
+
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+	if (context == NULL)
+		return FALSE;
+
+	destroy_context(context);
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean server_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	sdp_pdu_hdr_t hdr;
+	void *buf;
+	size_t size;
+	ssize_t len;
+	int fd;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		sdp_svcdb_collect_all(fd);
+		return FALSE;
+	}
+
+	len = recv(fd, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
+	if (len != sizeof(sdp_pdu_hdr_t)) {
+		sdp_svcdb_collect_all(fd);
+		return FALSE;
+	}
+
+	size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
+
+	buf = malloc(size);
+	if (!buf)
+		return TRUE;
+
+	len = recv(fd, buf, size, 0);
+	if (len <= 0) {
+		sdp_svcdb_collect_all(fd);
+		free(buf);
+		return FALSE;
+	}
+
+	util_hexdump('<', buf, len, sdp_debug, "SDP: ");
+
+	handle_internal_request(fd, context->data->mtu, buf, len);
+
+	return TRUE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct sdp_pdu *req_pdu;
+	uint16_t pdu_len;
+	unsigned char *buf;
+	ssize_t len;
+
+	req_pdu = &context->data->pdu_list[context->pdu_offset];
+
+	pdu_len = req_pdu->raw_size + context->cont_size;
+
+	buf = g_malloc0(pdu_len);
+
+	memcpy(buf, req_pdu->raw_data, req_pdu->raw_size);
+
+	if (context->cont_size > 0)
+		memcpy(buf + req_pdu->raw_size, context->cont_data,
+							context->cont_size);
+
+	len = write(context->fd, buf, pdu_len);
+
+	g_free(buf);
+
+	g_assert(len == pdu_len);
+
+	return FALSE;
+}
+
+static void context_increment(struct context *context)
+{
+	context->pdu_offset += 2;
+
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	g_idle_add(send_pdu, context);
+}
+
+static gboolean client_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct sdp_pdu *rsp_pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	rsp_pdu = &context->data->pdu_list[context->pdu_offset + 1];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+	if (len < 0)
+		return FALSE;
+
+	util_hexdump('>', buf, len, sdp_debug, "SDP: ");
+
+	g_assert(len > 0);
+	g_assert((size_t) len == rsp_pdu->raw_size + rsp_pdu->cont_len);
+
+	g_assert(memcmp(buf, rsp_pdu->raw_data,	rsp_pdu->raw_size) == 0);
+
+	if (rsp_pdu->cont_len > 0)
+		memcpy(context->cont_data, buf + rsp_pdu->raw_size,
+							rsp_pdu->cont_len);
+
+	context->cont_size = rsp_pdu->cont_len;
+
+	context_increment(context);
+
+	return TRUE;
+}
+
+static void update_db_timestamp(void)
+{
+}
+
+static void register_serial_port(void)
+{
+	sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+	uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
+	sdp_profile_desc_t profile;
+	uint8_t u8 = 1;
+	sdp_data_t *sdp_data, *channel;
+	sdp_record_t *record = sdp_record_alloc();
+
+	record->handle = sdp_next_handle();
+
+	sdp_record_add(BDADDR_ANY, record);
+	sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+	sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, 0);
+
+	sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &sp_uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, 0);
+
+	sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+	profile.version = 0x0100;
+	profiles = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(record, profiles);
+	sdp_list_free(profiles, 0);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_add_lang_attr(record);
+
+	sdp_set_info_attr(record, "Serial Port", "BlueZ", "COM Port");
+
+	sdp_set_url_attr(record, "http://www.bluez.org/",
+			"http://www.bluez.org/", "http://www.bluez.org/");
+
+	sdp_set_service_id(record, sp_uuid);
+	sdp_set_service_ttl(record, 0xffff);
+	sdp_set_service_avail(record, 0xff);
+	sdp_set_record_state(record, 0x00001234);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	update_db_timestamp();
+}
+
+static void register_object_push(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	uint8_t chan = 9;
+	sdp_data_t *channel;
+	uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+	void *dtds[sizeof(formats)], *values[sizeof(formats)];
+	unsigned int i;
+	uint8_t dtd = SDP_UINT8;
+	sdp_data_t *sdp_data, *sflist;
+	sdp_record_t *record = sdp_record_alloc();
+
+	record->handle = sdp_next_handle();
+
+	sdp_record_add(BDADDR_ANY, record);
+	sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+	sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &opush_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &chan);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	for (i = 0; i < sizeof(formats); i++) {
+		dtds[i] = &dtd;
+		values[i] = &formats[i];
+	}
+	sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+	sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+	sdp_set_info_attr(record, "OBEX Object Push", 0, 0);
+
+	sdp_data_free(channel);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	update_db_timestamp();
+}
+
+static void register_hid_keyboard(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+	unsigned int i;
+	uint8_t dtd = SDP_UINT16;
+	uint8_t dtd2 = SDP_UINT8;
+	uint8_t dtd_data = SDP_TEXT_STR8;
+	void *dtds[2];
+	void *values[2];
+	void *dtds2[2];
+	void *values2[2];
+	int leng[2];
+	uint8_t hid_spec_type = 0x22;
+	uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+	static const uint16_t ctrl = 0x11;
+	static const uint16_t intr = 0x13;
+	static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d,
+								0x01, 0x01 };
+	static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40,
+								0x01, 0x01 };
+	const uint8_t hid_spec[] = {
+		0x05, 0x01, // usage page
+		0x09, 0x06, // keyboard
+		0xa1, 0x01, // key codes
+		0x85, 0x01, // minimum
+		0x05, 0x07, // max
+		0x19, 0xe0, // logical min
+		0x29, 0xe7, // logical max
+		0x15, 0x00, // report size
+		0x25, 0x01, // report count
+		0x75, 0x01, // input data variable absolute
+		0x95, 0x08, // report count
+		0x81, 0x02, // report size
+		0x75, 0x08,
+		0x95, 0x01,
+		0x81, 0x01,
+		0x75, 0x01,
+		0x95, 0x05,
+		0x05, 0x08,
+		0x19, 0x01,
+		0x29, 0x05,
+		0x91, 0x02,
+		0x75, 0x03,
+		0x95, 0x01,
+		0x91, 0x01,
+		0x75, 0x08,
+		0x95, 0x06,
+		0x15, 0x00,
+		0x26, 0xff,
+		0x00, 0x05,
+		0x07, 0x19,
+		0x00, 0x2a,
+		0xff, 0x00,
+		0x81, 0x00,
+		0x75, 0x01,
+		0x95, 0x01,
+		0x15, 0x00,
+		0x25, 0x01,
+		0x05, 0x0c,
+		0x09, 0xb8,
+		0x81, 0x06,
+		0x09, 0xe2,
+		0x81, 0x06,
+		0x09, 0xe9,
+		0x81, 0x02,
+		0x09, 0xea,
+		0x81, 0x02,
+		0x75, 0x01,
+		0x95, 0x04,
+		0x81, 0x01,
+		0xc0         // end tag
+	};
+	sdp_data_t *sdp_data;
+	sdp_record_t *record = sdp_record_alloc();
+
+	record->handle = sdp_next_handle();
+
+	sdp_record_add(BDADDR_ANY, record);
+	sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+	sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+	sdp_list_free(root, 0);
+
+	sdp_add_lang_attr(record);
+
+	sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &hidkb_uuid);
+	sdp_set_service_classes(record, svclass_id);
+	sdp_list_free(svclass_id, 0);
+
+	sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, profile);
+	sdp_set_profile_descs(record, pfseq);
+	sdp_list_free(pfseq, 0);
+
+	/* protocols */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_data_free(psm);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	/* additional protocols */
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &intr);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_add_access_protos(record, aproto);
+
+	sdp_data_free(psm);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	sdp_set_info_attr(record, "HID Keyboard", NULL, NULL);
+
+	for (i = 0; i < sizeof(hid_attr) / 2; i++)
+		sdp_attr_add_new(record,
+				SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
+				SDP_UINT16, &hid_attr[i]);
+
+	dtds[0] = &dtd2;
+	values[0] = &hid_spec_type;
+	dtds[1] = &dtd_data;
+	values[1] = (uint8_t *) hid_spec;
+	leng[0] = 0;
+	leng[1] = sizeof(hid_spec);
+	hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+	hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+	sdp_attr_add(record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+	for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+		dtds2[i] = &dtd;
+		values2[i] = &hid_attr_lang[i];
+	}
+
+	lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+	lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+	sdp_attr_add(record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+	sdp_attr_add_new(record, SDP_ATTR_HID_SDP_DISABLE,
+						SDP_UINT16, &hid_attr2[0]);
+
+	for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
+		sdp_attr_add_new(record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
+						SDP_UINT16, &hid_attr2[i + 1]);
+
+	update_db_timestamp();
+}
+
+static void register_file_transfer(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *aproto, *proto[3];
+	uint8_t u8 = 10;
+	sdp_data_t *sdp_data, *channel;
+	sdp_record_t *record = sdp_record_alloc();
+
+	record->handle = sdp_next_handle();
+
+	sdp_record_add(BDADDR_ANY, record);
+	sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+	sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &ftrn_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
+	profile[0].version = 0x0100;
+	pfseq = sdp_list_append(0, &profile[0]);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &u8);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(0, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "OBEX File Transfer", 0, 0);
+
+	sdp_data_free(channel);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+	sdp_list_free(pfseq, 0);
+	sdp_list_free(proto[0], 0);
+	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto[2], 0);
+	sdp_list_free(apseq, 0);
+	sdp_list_free(aproto, 0);
+
+	update_db_timestamp();
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	channel = g_io_channel_unix_new(sv[0]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->server_source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				server_handler, context);
+	g_assert(context->server_source > 0);
+
+	g_io_channel_unref(channel);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->client_source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				client_handler, context);
+	g_assert(context->client_source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	set_fixed_db_timestamp(0x496f0654);
+
+	register_public_browse_group();
+	register_server_service();
+
+	register_serial_port();
+	register_object_push();
+	register_hid_keyboard();
+	register_file_transfer();
+	register_file_transfer();
+	register_file_transfer();
+	register_file_transfer();
+	register_file_transfer();
+
+	return context;
+}
+
+static void test_sdp(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	g_idle_add(send_pdu, context);
+}
+
+static void test_sdp_de_attr(gconstpointer data)
+{
+	const struct test_data_de *test = data;
+	uint128_t u128;
+	sdp_data_t *d;
+	int size = 0;
+
+	d = sdp_extract_attr(test->input_data, test->input_size, &size, NULL);
+	g_assert(d != NULL);
+	g_assert_cmpuint(test->input_size, ==, size);
+	g_assert_cmpuint(test->expected.dtd, ==, d->dtd);
+
+	tester_debug("DTD=0x%02x\n", d->dtd);
+
+	switch (d->dtd) {
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+		g_assert_cmpstr(test->expected.val.str, ==, d->val.str);
+		break;
+	case SDP_DATA_NIL:
+	case SDP_UINT8:
+		g_assert_cmpuint(test->expected.val.uint8, ==, d->val.uint8);
+		break;
+	case SDP_UINT16:
+		g_assert_cmpuint(test->expected.val.uint16, ==, d->val.uint16);
+		break;
+	case SDP_UINT32:
+		g_assert_cmpuint(test->expected.val.uint32, ==, d->val.uint32);
+		break;
+	case SDP_UINT64:
+		g_assert_cmpuint(test->expected.val.uint64, ==, d->val.uint64);
+		break;
+	case SDP_BOOL:
+	case SDP_INT8:
+		g_assert_cmpuint(test->expected.val.int8, ==, d->val.int8);
+		break;
+	case SDP_INT16:
+		g_assert_cmpuint(test->expected.val.int16, ==, d->val.int16);
+		break;
+	case SDP_INT32:
+		g_assert_cmpuint(test->expected.val.int32, ==, d->val.int32);
+		break;
+	case SDP_INT64:
+		g_assert_cmpuint(test->expected.val.int64, ==, d->val.int64);
+		break;
+	case SDP_UINT128:
+	case SDP_INT128:
+		/* Expected bytes are in network order */
+		hton128(&d->val.uint128, &u128);
+		g_assert(memcmp(&test->expected.val.uint128, &u128,
+						sizeof(uint128_t)) == 0);
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	sdp_data_free(d);
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	__btd_log_init("*", 0);
+
+	/*
+	 * Service Search Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * existing service(s).
+	 */
+	define_ss("BV-01-C/UUID-16",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x01, 0x00));
+	define_ss("BV-01-C/UUID-32",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0a, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00));
+	define_ss("BV-01-C/UUID-128",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x01, 0x00));
+
+	/*
+	 * Service Search Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * existing service(s), using continuation state.
+	 */
+	define_ss("BV-03-C/UUID-16",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x03, 0x19,
+			0x01, 0x00, 0xff, 0xff, 0x00),
+		raw_pdu_cont(8, 0x03, 0x00, 0x01, 0x00, 0x29, 0x00, 0x08, 0x00,
+				0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+				0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+				0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+				0x05, 0x00, 0x01, 0x00, 0x06, 0x08),
+		raw_pdu_cont(8, 0x02, 0x00, 0x02, 0x00, 0x10, 0x35, 0x03, 0x19,
+				0x01, 0x00, 0xff, 0xff, 0x08),
+		raw_pdu(0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0x08, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x07, 0x00));
+	define_ss("BV-03-C/UUID-32",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0a, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00),
+		raw_pdu_cont(8, 0x03, 0x00, 0x01, 0x00, 0x29, 0x00, 0x08, 0x00,
+				0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+				0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+				0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+				0x05, 0x00, 0x01, 0x00, 0x06, 0x08),
+		raw_pdu_cont(8, 0x02, 0x00, 0x02, 0x00, 0x12, 0x35, 0x05, 0x1a,
+				0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x08),
+		raw_pdu(0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0x08, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x07, 0x00));
+	define_ss("BV-03-C/UUID-128",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x00),
+		raw_pdu_cont(8, 0x03, 0x00, 0x01, 0x00, 0x29, 0x00, 0x08, 0x00,
+				0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+				0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+				0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+				0x05, 0x00, 0x01, 0x00, 0x06, 0x08),
+		raw_pdu_cont(8, 0x02, 0x00, 0x02, 0x00, 0x1e, 0x35, 0x11, 0x1c,
+				0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+				0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+				0xff, 0xff, 0x08),
+		raw_pdu(0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0x08, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x07, 0x00));
+
+	/*
+	 * Service Search Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * no existing service(s).
+	 */
+	define_ss("BV-04-C/UUID-16",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x03, 0x19,
+			0xff, 0xff, 0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00,
+			0x00, 0x00));
+	define_ss("BV-04-C/UUID-128",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00,
+			0x00, 0x00));
+	define_ss("BV-04-C/UUID-32",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0a, 0x35, 0x05, 0x1a,
+			0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00,
+			0x00, 0x00));
+
+	/*
+	 * Service Search Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * existing service(s), using invalid PDU size.
+	 */
+	define_ss("BI-01-C/UUID-16",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x01, 0x00, 0x00, 0x05, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+	define_ss("BI-01-C/UUID-32",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+	define_ss("BI-01-C/UUID-128",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x05, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+
+	/*
+	 * Service Search Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * existing service(s), using invalid request syntax.
+	 */
+	define_ss("BI-02-C/UUID-16",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x06, 0x35, 0x03, 0x19,
+			0x01, 0x00, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+	define_ss("BI-02-C/UUID-32",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x01, 0x00, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+	define_ss("BI-02-C/UUID-128",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x14, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with attribute(s).
+	 */
+	define_sa("BV-01-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x0a, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the existing
+	 * Attribute(s) using ContinuationState.
+	 */
+	define_sa("BV-03-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x07, 0x35, 0x06, 0x09, 0x00, 0x00,
+			0x09, 0x00, 0x01, 0x00),
+		raw_pdu_cont(8, 0x05, 0x00, 0x01, 0x00, 0x12, 0x00, 0x07, 0x35,
+				0x10, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x08),
+		raw_pdu_cont(8, 0x04, 0x00, 0x02, 0x00, 0x17, 0x00, 0x01, 0x00,
+				0x00, 0x00, 0x07, 0x35, 0x06, 0x09, 0x00, 0x00,
+				0x09, 0x00, 0x01, 0x08),
+		raw_pdu_cont(8, 0x05, 0x00, 0x02, 0x00, 0x12, 0x00, 0x07, 0x01,
+				0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x08),
+		raw_pdu_cont(8, 0x04, 0x00, 0x03, 0x00, 0x17, 0x00, 0x01, 0x00,
+				0x00, 0x00, 0x07, 0x35, 0x06, 0x09, 0x00, 0x00,
+				0x09, 0x00, 0x01, 0x08),
+		raw_pdu(0x05, 0x00, 0x03, 0x00, 0x07, 0x00, 0x04, 0x03,
+			0x19, 0x11, 0x01, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * ServiceID attribute.
+	 */
+	define_sa("BV-04-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x19, 0x35, 0x03, 0x09, 0x00, 0x03,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x08, 0x35,
+			0x06, 0x09, 0x00, 0x03, 0x19, 0x11, 0x01, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * ProtocolDescriptorList attribute.
+	 */
+	define_sa("BV-05-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x04,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x18, 0x35,
+			0x16, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35, 0x03,
+			0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00, 0x03,
+			0x08, 0x09, 0x35, 0x03, 0x19, 0x00, 0x08, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the
+	 * ServiceRecordState attribute.
+	 */
+	define_sa("BV-06-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x0d, 0x35, 0x03, 0x09, 0x00, 0x02,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x12,
+			0x34, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the
+	 * ServiceInfoTime attribute.
+	 */
+	define_sa("BV-07-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x0d, 0x35, 0x03, 0x09, 0x00, 0x07,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x07, 0x0a, 0x00, 0x00, 0xff,
+			0xff, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * BrowseGroupList attribute.
+	 */
+	define_sa("BV-08-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x0a, 0x35, 0x03, 0x09, 0x00, 0x05,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x05, 0x35, 0x03, 0x19, 0x10,
+			0x02, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * LanguageBaseAttributeIdList attribute.
+	 */
+	define_sa("BV-09-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x13, 0x35, 0x03, 0x09, 0x00, 0x06,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x13, 0x00, 0x10, 0x35,
+			0x0e, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65,
+			0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * ServiceAvailability attribute.
+	 */
+	define_sa("BV-10-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x0a, 0x35, 0x03, 0x09, 0x00, 0x08,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x07, 0x35,
+			0x05, 0x09, 0x00, 0x08, 0x08, 0xff, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * IconURL attribute.
+	 */
+	define_sa("BV-11-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x1f, 0x35, 0x03, 0x09, 0x00, 0x0c,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1c, 0x35,
+			0x1a, 0x09, 0x00, 0x0c, 0x45, 0x15, 0x68, 0x74,
+			0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+			0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a, 0x2e, 0x6f,
+			0x72, 0x67, 0x2f, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * ServiceName attribute.
+	 */
+	define_sa("BV-12-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x16, 0x35, 0x03, 0x09, 0x00, 0x06,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x13, 0x00, 0x10, 0x35,
+			0x0e, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65,
+			0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x15, 0x35, 0x03, 0x09, 0x01, 0x00,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x09, 0x01, 0x00, 0x25, 0x0b, 0x53, 0x65,
+			0x72, 0x69, 0x61, 0x6c, 0x20, 0x50, 0x6f, 0x72,
+			0x74, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * ServiceDescription attribute.
+	 */
+	define_sa("BV-13-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x16, 0x35, 0x03, 0x09, 0x00, 0x06,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x13, 0x00, 0x10, 0x35,
+			0x0e, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65,
+			0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x12, 0x35, 0x03, 0x09, 0x01, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x12, 0x00, 0x0f, 0x35,
+			0x0d, 0x09, 0x01, 0x01, 0x25, 0x08, 0x43, 0x4f,
+			0x4d, 0x20, 0x50, 0x6f, 0x72, 0x74, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * ProviderName attribute.
+	 */
+	define_sa("BV-14-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x16, 0x35, 0x03, 0x09, 0x00, 0x06,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x13, 0x00, 0x10, 0x35,
+			0x0e, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65,
+			0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x0f, 0x35, 0x03, 0x09, 0x01, 0x02,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x09, 0x01, 0x02, 0x25, 0x05, 0x42, 0x6c,
+			0x75, 0x65, 0x5a, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * VersionNumberList attribute.
+	 */
+	define_sa("BV-15-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x00, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x0d, 0x35, 0x03, 0x09, 0x02, 0x00,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x02, 0x00, 0x35, 0x03, 0x09, 0x01,
+			0x00, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the
+	 * ServiceDatabaseState attribute.
+	 */
+	define_sa("BV-16-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x00, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x0d, 0x35, 0x03, 0x09, 0x02, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x02, 0x01, 0x0a, 0x49, 0x6f, 0x06,
+			0x54, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the
+	 * BluetoothProfileDescriptorList attribute.
+	 */
+	define_sa("BV-17-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x0f, 0x35, 0x03, 0x09, 0x00, 0x09,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x12, 0x00, 0x0f, 0x35,
+			0x0d, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06,
+			0x19, 0x11, 0x05, 0x09, 0x01, 0x00, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the
+	 * DocumentationURL attribute.
+	 */
+	define_sa("BV-18-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x1f, 0x35, 0x03, 0x09, 0x00, 0x0a,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1c, 0x35,
+			0x1a, 0x09, 0x00, 0x0a, 0x45, 0x15, 0x68, 0x74,
+			0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+			0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a, 0x2e, 0x6f,
+			0x72, 0x67, 0x2f, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with the
+	 * ClientExecutableURL attribute.
+	 */
+	define_sa("BV-19-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x1f, 0x35, 0x03, 0x09, 0x00, 0x0b,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1c, 0x35,
+			0x1a, 0x09, 0x00, 0x0b, 0x45, 0x15, 0x68, 0x74,
+			0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+			0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a, 0x2e, 0x6f,
+			0x72, 0x67, 0x2f, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for non-existing Attribute.
+	 */
+	define_sa("BV-20-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x07, 0x35, 0x03, 0x09, 0xff, 0xff,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify that the IUT is able to respond with an
+	 * AdditionalProtocolDescriptorList attribute.
+	 */
+	define_sa("BV-21-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x24, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00),
+		raw_pdu(0x04, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x0d,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x02, 0x00, 0x19, 0x00, 0x16, 0x35,
+			0x14, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, 0x0d,
+			0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13,
+			0x35, 0x03, 0x19, 0x00, 0x11, 0x00));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Attribute, using invalid ServiceRecordHandle.
+	 */
+	define_sa("BI-01-C",
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0xff, 0xff, 0xff,
+			0xff, 0x00, 0x07, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Attribute, using invalid request syntax.
+	 */
+	define_sa("BI-02-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x01, 0x00,
+			0x00, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+
+	/*
+	 * Service Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Attribute, using invalid PDU-Size.
+	 */
+	define_sa("BI-03-C",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x01, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x00, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x11, 0x00, 0x01, 0x00,
+			0x00, 0x00, 0x07, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for non-existing Service, existing Attribute using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-01-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0xff, 0xff, 0x00, 0x0a, 0x35, 0x03, 0x09, 0x00,
+			0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+	define_ssa("BV-01-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0xff, 0xff, 0xff, 0xff, 0x00, 0x0a, 0x35, 0x03,
+			0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+	define_ssa("BV-01-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x00, 0x0a, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service, non-existing Attribute using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-02-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0x00, 0x0a, 0x35, 0x03, 0x09, 0xff,
+			0xff, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+	define_ssa("BV-02-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x0a, 0x35, 0x03,
+			0x09, 0xff, 0xff, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+	define_ssa("BV-02-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x0a, 0x35, 0x03, 0x09, 0xff, 0xff, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for non-existing Service, non-existing Attribute using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-03-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0xff, 0xff, 0x00, 0x0a, 0x35, 0x03, 0x09, 0xff,
+			0xff, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+	define_ssa("BV-03-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0xff, 0xff, 0xff, 0xff, 0x00, 0x0a, 0x35, 0x03,
+			0x09, 0xff, 0xff, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+	define_ssa("BV-03-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x00, 0x0a, 0x35, 0x03, 0x09, 0xff, 0xff, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x05, 0x00, 0x02, 0x35,
+			0x00, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute(s) using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-04-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0x00, 0x11, 0x35, 0x03, 0x09, 0x00,
+			0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x05, 0x00));
+	define_ssa("BV-04-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x11, 0x35, 0x03,
+			0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x05, 0x00));
+	define_ssa("BV-04-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x11, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x05, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Attributes, using Continuation State and
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-06-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x10, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0x00, 0x07, 0x35, 0x06, 0x09, 0x00,
+			0x00, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu_cont(8, 0x07, 0x00, 0x01, 0x00, 0x12, 0x00, 0x07, 0x35,
+				0x12, 0x35, 0x10, 0x09, 0x00, 0x00, 0x08),
+		raw_pdu_cont(8, 0x06, 0x00, 0x02, 0x00, 0x18, 0x35, 0x03, 0x19,
+				0x11, 0x05, 0x00, 0x07, 0x35, 0x06, 0x09, 0x00,
+				0x00, 0x09, 0x00, 0x01, 0x08),
+		raw_pdu_cont(8, 0x07, 0x00, 0x02, 0x00, 0x12, 0x00, 0x07, 0x0a,
+				0x00, 0x01, 0x00, 0x01, 0x09, 0x00, 0x08),
+		raw_pdu_cont(8, 0x06, 0x00, 0x03, 0x00, 0x18, 0x35, 0x03, 0x19,
+				0x11, 0x05, 0x00, 0x07, 0x35, 0x06, 0x09, 0x00,
+				0x00, 0x09, 0x00, 0x01, 0x08),
+		raw_pdu(0x07, 0x00, 0x03, 0x00, 0x09, 0x00, 0x06, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x00));
+	define_ssa("BV-06-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x12, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x07, 0x35, 0x06,
+			0x09, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu_cont(8, 0x07, 0x00, 0x01, 0x00, 0x12, 0x00, 0x07, 0x35,
+				0x12, 0x35, 0x10, 0x09, 0x00, 0x00, 0x08),
+		raw_pdu_cont(8, 0x06, 0x00, 0x02, 0x00, 0x1a, 0x35, 0x05, 0x1a,
+				0x00, 0x00, 0x11, 0x05, 0x00, 0x07, 0x35, 0x06,
+				0x09, 0x00, 0x00, 0x09, 0x00, 0x01, 0x08),
+		raw_pdu_cont(8, 0x07, 0x00, 0x02, 0x00, 0x12, 0x00, 0x07, 0x0a,
+				0x00, 0x01, 0x00, 0x01, 0x09, 0x00, 0x08),
+		raw_pdu_cont(8, 0x06, 0x00, 0x03, 0x00, 0x1a, 0x35, 0x05, 0x1a,
+				0x00, 0x00, 0x11, 0x05, 0x00, 0x07, 0x35, 0x06,
+				0x09, 0x00, 0x00, 0x09, 0x00, 0x01, 0x08),
+		raw_pdu(0x07, 0x00, 0x03, 0x00, 0x09, 0x00, 0x06, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x00));
+	define_ssa("BV-06-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1e, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x07, 0x35, 0x06, 0x09, 0x00, 0x00, 0x09,
+			0x00, 0x01, 0x00),
+		raw_pdu_cont(8, 0x07, 0x00, 0x01, 0x00, 0x12, 0x00, 0x07, 0x35,
+				0x12, 0x35, 0x10, 0x09, 0x00, 0x00, 0x08),
+		raw_pdu_cont(8, 0x06, 0x00, 0x02, 0x00, 0x26, 0x35, 0x11, 0x1c,
+				0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+				0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+				0x00, 0x07, 0x35, 0x06, 0x09, 0x00, 0x00, 0x09,
+				0x00, 0x01, 0x08),
+		raw_pdu_cont(8, 0x07, 0x00, 0x02, 0x00, 0x12, 0x00, 0x07, 0x0a,
+				0x00, 0x01, 0x00, 0x01, 0x09, 0x00, 0x08),
+		raw_pdu_cont(8, 0x06, 0x00, 0x03, 0x00, 0x26, 0x35, 0x11, 0x1c,
+				0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+				0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+				0x00, 0x07, 0x35, 0x06, 0x09, 0x00, 0x00, 0x09,
+				0x00, 0x01, 0x08),
+		raw_pdu(0x07, 0x00, 0x03, 0x00, 0x09, 0x00, 0x06, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceRecordState
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-07-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x13, 0x35, 0x03, 0x09, 0x00,
+			0x02, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x02, 0x0a, 0x00,
+			0x00, 0x12, 0x34, 0x00));
+	define_ssa("BV-07-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x13, 0x35, 0x03,
+			0x09, 0x00, 0x02, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x02, 0x0a, 0x00,
+			0x00, 0x12, 0x34, 0x00));
+	define_ssa("BV-07-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x13, 0x35, 0x03, 0x09, 0x00, 0x02, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x02, 0x0a, 0x00,
+			0x00, 0x12, 0x34, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceDataBaseState
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-08-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x10, 0x00, 0x00, 0x13, 0x35, 0x03, 0x09, 0x02,
+			0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x02, 0x01, 0x0a, 0x49,
+			0x6f, 0x06, 0x54, 0x00));
+	define_ssa("BV-08-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x35, 0x03,
+			0x09, 0x02, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x02, 0x01, 0x0a, 0x49,
+			0x6f, 0x06, 0x54, 0x00));
+	define_ssa("BV-08-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x13, 0x35, 0x03, 0x09, 0x02, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x02, 0x01, 0x0a, 0x49,
+			0x6f, 0x06, 0x54, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceInfoTimeToLive
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-09-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x13, 0x35, 0x03, 0x09, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x07, 0x0a, 0x00,
+			0x00, 0xff, 0xff, 0x00));
+	define_ssa("BV-09-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x13, 0x35, 0x03,
+			0x09, 0x00, 0x07, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x07, 0x0a, 0x00,
+			0x00, 0xff, 0xff, 0x00));
+	define_ssa("BV-09-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x13, 0x35, 0x03, 0x09, 0x00, 0x07, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x07, 0x0a, 0x00,
+			0x00, 0xff, 0xff, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceID using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-10-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x1e, 0x35, 0x03, 0x09, 0x00,
+			0x03, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x35, 0x06, 0x09, 0x00, 0x03, 0x19, 0x11,
+			0x01, 0x00));
+	define_ssa("BV-10-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x1e, 0x35, 0x03,
+			0x09, 0x00, 0x03, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x35, 0x06, 0x09, 0x00, 0x03, 0x19, 0x11,
+			0x01, 0x00));
+	define_ssa("BV-10-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x1e, 0x35, 0x03, 0x09, 0x00, 0x03, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x35, 0x06, 0x09, 0x00, 0x03, 0x19, 0x11,
+			0x01, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ProtocolDescriptorList
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-11-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x04, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x1a, 0x35,
+			0x18, 0x35, 0x16, 0x09, 0x00, 0x04, 0x35, 0x11,
+			0x35, 0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19,
+			0x00, 0x03, 0x08, 0x09, 0x35, 0x03, 0x19, 0x00,
+			0x08, 0x00));
+	define_ssa("BV-11-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x05, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x04, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x1a, 0x35,
+			0x18, 0x35, 0x16, 0x09, 0x00, 0x04, 0x35, 0x11,
+			0x35, 0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19,
+			0x00, 0x03, 0x08, 0x09, 0x35, 0x03, 0x19, 0x00,
+			0x08, 0x00));
+	define_ssa("BV-11-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x04, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x1a, 0x35,
+			0x18, 0x35, 0x16, 0x09, 0x00, 0x04, 0x35, 0x11,
+			0x35, 0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19,
+			0x00, 0x03, 0x08, 0x09, 0x35, 0x03, 0x19, 0x00,
+			0x08, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute BrowseGroupList
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-12-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x05, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x05, 0x35, 0x03,
+			0x19, 0x10, 0x02, 0x00));
+	define_ssa("BV-12-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x05, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x05, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x05, 0x35, 0x03,
+			0x19, 0x10, 0x02, 0x00));
+	define_ssa("BV-12-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x05, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x00, 0x05, 0x35, 0x03,
+			0x19, 0x10, 0x02, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute LanguageBaseAttributeIdList
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-13-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x18, 0x35, 0x03, 0x09, 0x00,
+			0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00));
+	define_ssa("BV-13-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x18, 0x35, 0x03,
+			0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00));
+	define_ssa("BV-13-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x18, 0x35, 0x03, 0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceAvailability
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-14-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x0f, 0x35, 0x03, 0x09, 0x00,
+			0x08, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x09, 0x35,
+			0x07, 0x35, 0x05, 0x09, 0x00, 0x08, 0x08, 0xff,
+			0x00));
+	define_ssa("BV-14-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x0f, 0x35, 0x03,
+			0x09, 0x00, 0x08, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x09, 0x35,
+			0x07, 0x35, 0x05, 0x09, 0x00, 0x08, 0x08, 0xff,
+			0x00));
+	define_ssa("BV-14-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x0f, 0x35, 0x03, 0x09, 0x00, 0x08, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x09, 0x35,
+			0x07, 0x35, 0x05, 0x09, 0x00, 0x08, 0x08, 0xff,
+			0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute IconURL using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-15-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x24, 0x35, 0x03, 0x09, 0x00,
+			0x0c, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0c, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+	define_ssa("BV-15-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x24, 0x35, 0x03,
+			0x09, 0x00, 0x0c, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0c, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+	define_ssa("BV-15-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x24, 0x35, 0x03, 0x09, 0x00, 0x0c, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0c, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceName using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-16-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x1b, 0x35, 0x03, 0x09, 0x00,
+			0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x1d, 0x35, 0x03, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x17, 0x00, 0x14, 0x35,
+			0x12, 0x35, 0x10, 0x09, 0x01, 0x00, 0x25, 0x0b,
+			0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x50,
+			0x6f, 0x72, 0x74, 0x00));
+	define_ssa("BV-16-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x1b, 0x35, 0x03,
+			0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x1d, 0x35, 0x03,
+			0x09, 0x01, 0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x17, 0x00, 0x14, 0x35,
+			0x12, 0x35, 0x10, 0x09, 0x01, 0x00, 0x25, 0x0b,
+			0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x50,
+			0x6f, 0x72, 0x74, 0x00));
+	define_ssa("BV-16-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x1b, 0x35, 0x03, 0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x1d, 0x35, 0x03, 0x09, 0x01, 0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x17, 0x00, 0x14, 0x35,
+			0x12, 0x35, 0x10, 0x09, 0x01, 0x00, 0x25, 0x0b,
+			0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x50,
+			0x6f, 0x72, 0x74, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ServiceDescription
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-17-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x1b, 0x35, 0x03, 0x09, 0x00,
+			0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x1a, 0x35, 0x03, 0x09, 0x01,
+			0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x14, 0x00, 0x11, 0x35,
+			0x0f, 0x35, 0x0d, 0x09, 0x01, 0x01, 0x25, 0x08,
+			0x43, 0x4f, 0x4d, 0x20, 0x50, 0x6f, 0x72, 0x74,
+			0x00));
+	define_ssa("BV-17-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x1b, 0x35, 0x03,
+			0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x1a, 0x35, 0x03,
+			0x09, 0x01, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x14, 0x00, 0x11, 0x35,
+			0x0f, 0x35, 0x0d, 0x09, 0x01, 0x01, 0x25, 0x08,
+			0x43, 0x4f, 0x4d, 0x20, 0x50, 0x6f, 0x72, 0x74,
+			0x00));
+	define_ssa("BV-17-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x1b, 0x35, 0x03, 0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x1a, 0x35, 0x03, 0x09, 0x01, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x14, 0x00, 0x11, 0x35,
+			0x0f, 0x35, 0x0d, 0x09, 0x01, 0x01, 0x25, 0x08,
+			0x43, 0x4f, 0x4d, 0x20, 0x50, 0x6f, 0x72, 0x74,
+			0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ProviderName using
+	 * ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-18-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x1b, 0x35, 0x03, 0x09, 0x00,
+			0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x17, 0x35, 0x03, 0x09, 0x01,
+			0x02, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x11, 0x00, 0x0e, 0x35,
+			0x0c, 0x35, 0x0a, 0x09, 0x01, 0x02, 0x25, 0x05,
+			0x42, 0x6c, 0x75, 0x65, 0x5a, 0x00));
+	define_ssa("BV-18-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x1b, 0x35, 0x03,
+			0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x17, 0x35, 0x03,
+			0x09, 0x01, 0x02, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x11, 0x00, 0x0e, 0x35,
+			0x0c, 0x35, 0x0a, 0x09, 0x01, 0x02, 0x25, 0x05,
+			0x42, 0x6c, 0x75, 0x65, 0x5a, 0x00));
+	define_ssa("BV-18-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x1b, 0x35, 0x03, 0x09, 0x00, 0x06, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x15, 0x00, 0x12, 0x35,
+			0x10, 0x35, 0x0e, 0x09, 0x00, 0x06, 0x35, 0x09,
+			0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01,
+			0x00, 0x00),
+		raw_pdu(0x06, 0x00, 0x02, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x17, 0x35, 0x03, 0x09, 0x01, 0x02, 0x00),
+		raw_pdu(0x07, 0x00, 0x02, 0x00, 0x11, 0x00, 0x0e, 0x35,
+			0x0c, 0x35, 0x0a, 0x09, 0x01, 0x02, 0x25, 0x05,
+			0x42, 0x6c, 0x75, 0x65, 0x5a, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute VersionNumberList
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-19-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x10, 0x00, 0x00, 0x12, 0x35, 0x03, 0x09, 0x02,
+			0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x02, 0x00, 0x35, 0x03,
+			0x09, 0x01, 0x00, 0x00));
+	define_ssa("BV-19-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x35, 0x03,
+			0x09, 0x02, 0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x02, 0x00, 0x35, 0x03,
+			0x09, 0x01, 0x00, 0x00));
+	define_ssa("BV-19-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x12, 0x35, 0x03, 0x09, 0x02, 0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x0c, 0x35,
+			0x0a, 0x35, 0x08, 0x09, 0x02, 0x00, 0x35, 0x03,
+			0x09, 0x01, 0x00, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * existing Service(s) and Attribute BluetoothProfileDescriptorList
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-20-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x09, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x14, 0x00, 0x11, 0x35,
+			0x0f, 0x35, 0x0d, 0x09, 0x00, 0x09, 0x35, 0x08,
+			0x35, 0x06, 0x19, 0x11, 0x05, 0x09, 0x01, 0x00,
+			0x00));
+	define_ssa("BV-20-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x05, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x09, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x14, 0x00, 0x11, 0x35,
+			0x0f, 0x35, 0x0d, 0x09, 0x00, 0x09, 0x35, 0x08,
+			0x35, 0x06, 0x19, 0x11, 0x05, 0x09, 0x01, 0x00,
+			0x00));
+	define_ssa("BV-20-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x09, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x14, 0x00, 0x11, 0x35,
+			0x0f, 0x35, 0x0d, 0x09, 0x00, 0x09, 0x35, 0x08,
+			0x35, 0x06, 0x19, 0x11, 0x05, 0x09, 0x01, 0x00,
+			0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute DocumentationURL
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-21-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x24, 0x35, 0x03, 0x09, 0x00,
+			0x0a, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0a, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+	define_ssa("BV-21-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x24, 0x35, 0x03,
+			0x09, 0x00, 0x0a, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0a, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+	define_ssa("BV-21-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x24, 0x35, 0x03, 0x09, 0x00, 0x0a, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0a, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Service(s) and Attribute ClientExecutableURL
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-22-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x01, 0x00, 0x24, 0x35, 0x03, 0x09, 0x00,
+			0x0b, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0b, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+	define_ssa("BV-22-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x24, 0x35, 0x03,
+			0x09, 0x00, 0x0b, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0b, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+	define_ssa("BV-22-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x00, 0x24, 0x35, 0x03, 0x09, 0x00, 0x0b, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x1e, 0x35,
+			0x1c, 0x35, 0x1a, 0x09, 0x00, 0x0b, 0x45, 0x15,
+			0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+			0x77, 0x77, 0x2e, 0x62, 0x6c, 0x75, 0x65, 0x7a,
+			0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching for
+	 * existing Service(s) and Attribute AdditionalProtocolDescriptorList
+	 * using ServiceSearchAttributeRequest.
+	 */
+	define_ssa("BV-23-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x11, 0x24, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x0d, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x18, 0x35,
+			0x16, 0x35, 0x14, 0x09, 0x00, 0x0d, 0x35, 0x0f,
+			0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09,
+			0x00, 0x13, 0x35, 0x03, 0x19, 0x00, 0x11, 0x00));
+	define_ssa("BV-23-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x11, 0x24, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x0d, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x18, 0x35,
+			0x16, 0x35, 0x14, 0x09, 0x00, 0x0d, 0x35, 0x0f,
+			0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09,
+			0x00, 0x13, 0x35, 0x03, 0x19, 0x00, 0x11, 0x00));
+	define_ssa("BV-23-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x11, 0x24, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x0d, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x18, 0x35,
+			0x16, 0x35, 0x14, 0x09, 0x00, 0x0d, 0x35, 0x0f,
+			0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09,
+			0x00, 0x13, 0x35, 0x03, 0x19, 0x00, 0x11, 0x00));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Attribute, using invalid request syntax,
+	 * using the ServiceSearchAttributeRequest PDU.
+	 */
+	define_ssa("BI-01-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0b, 0x35, 0x03, 0x19,
+			0x01, 0x00, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+	define_ssa("BI-01-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x01, 0x00, 0x35, 0x03, 0x09, 0x00,
+			0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+	define_ssa("BI-01-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x19, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03));
+
+	/*
+	 * Service Search Attribute Request
+	 *
+	 * Verify the correct behaviour of the IUT when searching
+	 * for existing Attribute, using invalid PDU-size, using the
+	 * ServiceSearchAttributeRequest PDU.
+	 */
+	define_ssa("BI-02-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x12, 0x35, 0x03, 0x19,
+			0x01, 0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+	define_ssa("BI-02-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x14, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+	define_ssa("BI-02-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x20, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04));
+
+	/*
+	 * Service Browse
+	 *
+	 * Verify that the IUT behave correct using SDP_ServiceSearchRequest
+	 * and SDP_ServiceAttributeRequest for Service Browse.
+	 */
+	define_brw("BV-01-C/UUID-16",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x03, 0x19,
+			0x10, 0x02, 0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x25, 0x00, 0x08, 0x00,
+			0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+			0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+			0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x24, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x03, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x04, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x06, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x07, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x03, 0x19,
+			0x10, 0x01, 0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x00, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x02, 0x00,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x35,
+			0x06, 0x09, 0x02, 0x00, 0x19, 0x10, 0x02, 0x00),
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x08, 0x35, 0x03, 0x19,
+			0x10, 0x02, 0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x25, 0x00, 0x08, 0x00,
+			0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+			0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+			0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x24, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x03, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x04, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x06, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x07, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00));
+	define_brw("BV-01-C/UUID-32",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0a, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x02, 0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x25, 0x00, 0x08, 0x00,
+			0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+			0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+			0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x24, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x03, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x04, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x06, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x07, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0a, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x01, 0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x00, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x02, 0x00,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x35,
+			0x06, 0x09, 0x02, 0x00, 0x19, 0x10, 0x02, 0x00),
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x0a, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x02, 0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x25, 0x00, 0x08, 0x00,
+			0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+			0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+			0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x24, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x03, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x04, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x06, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x07, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00));
+	define_brw("BV-01-C/UUID-128",
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x25, 0x00, 0x08, 0x00,
+			0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+			0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+			0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x24, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x03, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x04, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x06, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x07, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x00, 0x00, 0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x02, 0x00,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x35,
+			0x06, 0x09, 0x02, 0x00, 0x19, 0x10, 0x02, 0x00),
+		raw_pdu(0x02, 0x00, 0x01, 0x00, 0x16, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x00),
+		raw_pdu(0x03, 0x00, 0x01, 0x00, 0x25, 0x00, 0x08, 0x00,
+			0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+			0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+			0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+			0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00,
+			0x07, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x00, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x01, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x05, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x24, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x03, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x04, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x05, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x06, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x04, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00,
+			0x07, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01,
+			0x00),
+		raw_pdu(0x05, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00));
+
+	/*
+	 * Service Browse
+	 *
+	 * Verify that the IUT behave correct using
+	 * SDP_ServiceSearchAttributeRequest for Service Browse.
+	 */
+	define_brw("BV-02-C/UUID-16",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x10, 0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x55, 0x00, 0x52, 0x35,
+			0x50, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x01, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x06, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x06, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x06, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x10, 0x01, 0xff, 0xff, 0x35, 0x03, 0x09, 0x02,
+			0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x35, 0x06, 0x09, 0x02, 0x00, 0x19, 0x10,
+			0x02, 0x00),
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0d, 0x35, 0x03, 0x19,
+			0x10, 0x02, 0xff, 0xff, 0x35, 0x03, 0x09, 0x00,
+			0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x55, 0x00, 0x52, 0x35,
+			0x50, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x01, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x06, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x06, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x06, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00));
+	define_brw("BV-02-C/UUID-32",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x02, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x55, 0x00, 0x52, 0x35,
+			0x50, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x01, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x06, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x06, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x06, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x01, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x02, 0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x35, 0x06, 0x09, 0x02, 0x00, 0x19, 0x10,
+			0x02, 0x00),
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x0f, 0x35, 0x05, 0x1a,
+			0x00, 0x00, 0x10, 0x02, 0xff, 0xff, 0x35, 0x03,
+			0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x55, 0x00, 0x52, 0x35,
+			0x50, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x01, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x06, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x06, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x06, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00));
+	define_brw("BV-02-C/UUID-128",
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x55, 0x00, 0x52, 0x35,
+			0x50, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x01, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x06, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x06, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x06, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00),
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x02, 0x00, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x0a, 0x35,
+			0x08, 0x35, 0x06, 0x09, 0x02, 0x00, 0x19, 0x10,
+			0x02, 0x00),
+		raw_pdu(0x06, 0x00, 0x01, 0x00, 0x1b, 0x35, 0x11, 0x1c,
+			0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+			0xff, 0xff, 0x35, 0x03, 0x09, 0x00, 0x01, 0x00),
+		raw_pdu(0x07, 0x00, 0x01, 0x00, 0x55, 0x00, 0x52, 0x35,
+			0x50, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x01, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x05, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x35, 0x08, 0x09, 0x00, 0x01, 0x35, 0x03,
+			0x19, 0x11, 0x06, 0x35, 0x08, 0x09, 0x00, 0x01,
+			0x35, 0x03, 0x19, 0x11, 0x06, 0x35, 0x08, 0x09,
+			0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x06, 0x35,
+			0x08, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11,
+			0x06, 0x00));
+
+	/*
+	 * SDP Data Element (DE) tests
+	 *
+	 * Test extraction of valid DEs supported by sdp_extract_attr().
+	 */
+	define_test_de_attr("TEXT_STR8/empty",
+			raw_data(0x25, 0x00),
+			exp_data(SDP_TEXT_STR8, str, ""));
+	define_test_de_attr("TEXT_STR8",
+			raw_data(0x25, 0x04, 0x41, 0x42, 0x43, 0x44),
+			exp_data(SDP_TEXT_STR8, str, "ABCD"));
+	define_test_de_attr("TEXT_STR16",
+			raw_data(0x26, 0x00, 0x04, 0x41, 0x42, 0x43, 0x44),
+			exp_data(SDP_TEXT_STR16, str, "ABCD"));
+	define_test_de_attr("URL_STR8",
+			raw_data(0x45, 0x15, 0x68, 0x74, 0x74, 0x70, 0x3a,
+				0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x62, 0x6c,
+				0x75, 0x65, 0x7a, 0x2e, 0x6f, 0x72, 0x67,
+				0x2f),
+			exp_data(SDP_URL_STR8, str, "http://www.bluez.org/"));
+	define_test_de_attr("URL_STR16",
+			raw_data(0x46, 0x00, 0x15, 0x68, 0x74, 0x74, 0x70,
+				0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x62,
+				0x6c, 0x75, 0x65, 0x7a, 0x2e, 0x6f, 0x72, 0x67,
+				0x2f),
+			exp_data(SDP_URL_STR16, str, "http://www.bluez.org/"));
+	define_test_de_attr("NIL",
+			raw_data(0x00),
+			exp_data(SDP_DATA_NIL, uint8, 0));
+	define_test_de_attr("UINT8",
+			raw_data(0x08, 0xff),
+			exp_data(SDP_UINT8, uint8, UINT8_MAX));
+	define_test_de_attr("INT8",
+			raw_data(0x10, 0x80),
+			exp_data(SDP_INT8, int8, INT8_MIN));
+	define_test_de_attr("BOOL",
+			raw_data(0x28, 0x01),
+			exp_data(SDP_BOOL, int8, 1));
+	define_test_de_attr("UINT16",
+			raw_data(0x09, 0xff, 0xff),
+			exp_data(SDP_UINT16, uint16, UINT16_MAX));
+	define_test_de_attr("INT16",
+			raw_data(0x11, 0x80, 0x00),
+			exp_data(SDP_INT16, int16, INT16_MIN));
+	define_test_de_attr("UINT32",
+			raw_data(0x0A, 0xff, 0xff, 0xff, 0xff),
+			exp_data(SDP_UINT32, uint32, UINT32_MAX));
+	define_test_de_attr("INT32",
+			raw_data(0x12, 0x80, 0x00, 0x00, 0x00),
+			exp_data(SDP_INT32, int32, INT32_MIN));
+	define_test_de_attr("UINT64",
+			raw_data(0x0B, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+									0xff),
+			exp_data(SDP_UINT64, uint64, UINT64_MAX));
+	define_test_de_attr("INT64",
+			raw_data(0x13, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+									0x00),
+			exp_data(SDP_INT64, int64, INT64_MIN));
+	/* UINT128/INT128 are just byte arrays parsed as uint128_t */
+	define_test_de_attr("UINT128",
+			raw_data(0x0C, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+					0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+					0xff, 0xff, 0xff),
+			exp_data(SDP_UINT128, uint128,
+				build_u128(0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+						0xff, 0xff, 0xff, 0xff, 0xff,
+						0xff, 0xff, 0xff, 0xff, 0xff)));
+	define_test_de_attr("INT128",
+			raw_data(0x14, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00),
+			exp_data(SDP_INT128, uint128,
+				build_u128(0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+						0x00, 0x00, 0x00, 0x00, 0x00,
+						0x00, 0x00, 0x00, 0x00, 0x00)));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-textfile.c b/bluez/repo/unit/test-textfile.c
new file mode 100644
index 0000000..5250f98
--- /dev/null
+++ b/bluez/repo/unit/test-textfile.c
@@ -0,0 +1,274 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "src/textfile.h"
+#include "src/shared/tester.h"
+
+static const char test_pathname[] = "/tmp/textfile";
+
+static void util_create_empty(void)
+{
+	int fd;
+
+	fd = creat(test_pathname, 0644);
+	if (fd < 0)
+		return;
+
+	if (ftruncate(fd, 0) < 0)
+		goto done;
+
+done:
+	close(fd);
+}
+
+static void util_create_pagesize(void)
+{
+	char value[512];
+	unsigned int i;
+	int fd, size;
+
+	size = getpagesize();
+	if (size < 0)
+		return;
+
+	fd = creat(test_pathname, 0644);
+	if (fd < 0)
+		return;
+
+	if (ftruncate(fd, 0) < 0)
+		goto done;
+
+	memset(value, 0, sizeof(value));
+	for (i = 0; i < (size / sizeof(value)); i++) {
+		if (write(fd, value, sizeof(value)) < 0)
+			break;
+	}
+
+done:
+	close(fd);
+}
+
+static void test_pagesize(const void *data)
+{
+	char key[18], *str;
+	int size;
+
+	size = getpagesize();
+	g_assert(size >= 4096);
+
+	tester_debug("System uses a page size of %d bytes\n", size);
+
+	util_create_pagesize();
+
+	sprintf(key, "11:11:11:11:11:11");
+	str = textfile_get(test_pathname, key);
+
+	tester_debug("%s\n", str);
+
+	g_assert(str == NULL);
+	tester_test_passed();
+}
+
+static void test_delete(const void *data)
+{
+	char key[18], value[512], *str;
+
+	util_create_empty();
+
+	sprintf(key, "00:00:00:00:00:00");
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	memset(value, 0, sizeof(value));
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	str = textfile_get(test_pathname, key);
+	g_assert(str != NULL);
+
+	tester_debug("%s\n", str);
+
+	g_free(str);
+	tester_test_passed();
+}
+
+static void test_overwrite(const void *data)
+{
+	char key[18], value[512], *str;
+
+	util_create_empty();
+
+	sprintf(key, "00:00:00:00:00:00");
+	memset(value, 0, sizeof(value));
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	snprintf(value, sizeof(value), "Test");
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	str = textfile_get(test_pathname, key);
+
+	tester_debug("%s\n", str);
+
+	g_assert(str == NULL);
+	tester_test_passed();
+}
+
+static void check_entry(char *key, char *value, void *data)
+{
+	unsigned int max = GPOINTER_TO_UINT(data);
+	unsigned int len;
+
+	len = strtol(key + 16, NULL, 16);
+	if (len == 1)
+		len = max;
+
+	if (g_test_verbose())
+		g_print("%s %s\n", key, value);
+
+	g_assert(strlen(value) == len);
+}
+
+static void test_multiple(const void *data)
+{
+	char key[18], value[512], *str;
+	unsigned int i, j, max = 10;
+
+	util_create_empty();
+
+	for (i = 1; i < max + 1; i++) {
+		sprintf(key, "00:00:00:00:00:%02X", i);
+
+		memset(value, 0, sizeof(value));
+		for (j = 0; j < i; j++)
+			value[j] = 'x';
+
+		g_assert(textfile_put(test_pathname, key, value) == 0);
+
+		str = textfile_get(test_pathname, key);
+
+		tester_debug("%s %s\n", key, str);
+
+		g_assert(str != NULL);
+		g_assert(strcmp(str, value) == 0);
+
+		free(str);
+	}
+
+	sprintf(key, "00:00:00:00:00:%02X", max);
+
+	memset(value, 0, sizeof(value));
+	for (j = 0; j < max; j++)
+		value[j] = 'y';
+
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	str = textfile_get(test_pathname, key);
+
+	tester_debug("%s %s\n", key, str);
+
+	g_assert(str != NULL);
+	g_assert(strcmp(str, value) == 0);
+
+	free(str);
+
+	sprintf(key, "00:00:00:00:00:%02X", 1);
+
+	memset(value, 0, sizeof(value));
+	for (j = 0; j < max; j++)
+		value[j] = 'z';
+
+	g_assert(textfile_put(test_pathname, key, value) == 0);
+
+	str = textfile_get(test_pathname, key);
+
+	tester_debug("%s %s\n", key, str);
+
+	g_assert(str != NULL);
+	g_assert(strcmp(str, value) == 0);
+
+	free(str);
+
+	for (i = 1; i < max + 1; i++) {
+		sprintf(key, "00:00:00:00:00:%02X", i);
+		str = textfile_get(test_pathname, key);
+
+		tester_debug("%s %s\n", key, str);
+
+		g_assert(str != NULL);
+
+		if (i == 1)
+			g_assert(strlen(str) == max);
+		else
+			g_assert(strlen(str) == i);
+
+		g_free(str);
+	}
+
+	sprintf(key, "00:00:00:00:00:%02X", 2);
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	sprintf(key, "00:00:00:00:00:%02X", max - 3);
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	textfile_foreach(test_pathname, check_entry, GUINT_TO_POINTER(max));
+
+	sprintf(key, "00:00:00:00:00:%02X", 1);
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	sprintf(key, "00:00:00:00:00:%02X", max);
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	sprintf(key, "00:00:00:00:00:%02X", max + 1);
+	g_assert(textfile_del(test_pathname, key) == 0);
+
+	textfile_foreach(test_pathname, check_entry, GUINT_TO_POINTER(max));
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	tester_add("/textfile/pagesize", NULL, NULL, test_pagesize, NULL);
+	tester_add("/textfile/delete", NULL, NULL, test_delete, NULL);
+	tester_add("/textfile/overwrite", NULL, NULL, test_overwrite, NULL);
+	tester_add("/textfile/multiple", NULL, NULL, test_multiple, NULL);
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-uhid.c b/bluez/repo/unit/test-uhid.c
new file mode 100644
index 0000000..320cd54
--- /dev/null
+++ b/bluez/repo/unit/test-uhid.c
@@ -0,0 +1,299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This 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 <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/uhid.h"
+#include "src/shared/util.h"
+
+#include "src/shared/tester.h"
+
+struct test_pdu {
+	bool valid;
+	const uint8_t *data;
+	size_t size;
+};
+
+struct test_data {
+	char *test_name;
+	struct test_pdu *pdu_list;
+};
+
+struct context {
+	struct bt_uhid *uhid;
+	guint source;
+	guint process;
+	int fd;
+	unsigned int pdu_offset;
+	const struct test_data *data;
+};
+
+#define event(args...)						\
+	{							\
+		.valid = true,					\
+		.data = (void *) args,				\
+		.size = sizeof(*args),				\
+	}
+
+#define define_test(name, function, args...)				\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_memdup(pdus, sizeof(pdus));		\
+		tester_add(name, &data, NULL, function, NULL);		\
+	} while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	tester_debug("%s%s\n", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+	const struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
+static void destroy_context(struct context *context)
+{
+	if (context->source > 0)
+		g_source_remove(context->source);
+
+	bt_uhid_unref(context->uhid);
+
+	test_free(context->data);
+	g_free(context);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+	struct context *context = user_data;
+
+	if (context == NULL)
+		return FALSE;
+
+	if (context->process > 0)
+		g_source_remove(context->process);
+
+	destroy_context(context);
+	tester_test_passed();
+
+	return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+
+	util_hexdump('<', pdu->data, len, test_debug, "uHID: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	context->process = 0;
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[sizeof(struct uhid_event)];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		context->source = 0;
+		g_print("%s: cond %x\n", __func__, cond);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	util_hexdump('>', buf, len, test_debug, "uHID: ");
+
+	g_assert_cmpint(len, ==, pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	context_process(context);
+
+	return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->uhid = bt_uhid_new(sv[0]);
+	g_assert(context->uhid != NULL);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+	context->data = data;
+
+	return context;
+}
+
+static const struct uhid_event ev_create = {
+	.type = UHID_CREATE,
+};
+
+static const struct uhid_event ev_destroy = {
+	.type = UHID_DESTROY,
+};
+
+static const struct uhid_event ev_feature_answer = {
+	.type = UHID_FEATURE_ANSWER,
+};
+
+static const struct uhid_event ev_input = {
+	.type = UHID_INPUT,
+};
+
+static const struct uhid_event ev_output = {
+	.type = UHID_OUTPUT,
+};
+
+static const struct uhid_event ev_feature = {
+	.type = UHID_FEATURE,
+};
+
+static void test_client(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	if (g_str_equal(context->data->test_name, "/uhid/command/create"))
+		bt_uhid_send(context->uhid, &ev_create);
+
+	if (g_str_equal(context->data->test_name, "/uhid/command/destroy"))
+		bt_uhid_send(context->uhid, &ev_destroy);
+
+	if (g_str_equal(context->data->test_name,
+						"/uhid/command/feature_answer"))
+		bt_uhid_send(context->uhid, &ev_feature_answer);
+
+	if (g_str_equal(context->data->test_name, "/uhid/command/input"))
+		bt_uhid_send(context->uhid, &ev_input);
+
+	context_quit(context);
+}
+
+static void handle_output(struct uhid_event *ev, void *user_data)
+{
+	g_assert_cmpint(ev->type, ==, UHID_OUTPUT);
+
+	context_quit(user_data);
+}
+
+static void handle_feature(struct uhid_event *ev, void *user_data)
+{
+	g_assert_cmpint(ev->type, ==, UHID_FEATURE);
+
+	context_quit(user_data);
+}
+
+static void test_server(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	bt_uhid_register(context->uhid, UHID_OUTPUT, handle_output, context);
+	bt_uhid_register(context->uhid, UHID_FEATURE, handle_feature, context);
+
+	g_idle_add(send_pdu, context);
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	define_test("/uhid/command/create", test_client, event(&ev_create));
+	define_test("/uhid/command/destroy", test_client, event(&ev_destroy));
+	define_test("/uhid/command/feature_answer", test_client,
+						event(&ev_feature_answer));
+	define_test("/uhid/command/input", test_client, event(&ev_input));
+
+	define_test("/uhid/event/output", test_server, event(&ev_output));
+	define_test("/uhid/event/feature", test_server, event(&ev_feature));
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/test-uuid.c b/bluez/repo/unit/test-uuid.c
new file mode 100644
index 0000000..7c6789e
--- /dev/null
+++ b/bluez/repo/unit/test-uuid.c
@@ -0,0 +1,281 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  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>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/tester.h"
+
+struct uuid_test_data {
+	const char *str;
+	uint16_t val16;
+	uint32_t val32;
+	unsigned char *binary;
+	uint8_t type;
+	const char *str128;
+	unsigned char *binary128;
+};
+
+static unsigned char uuid_base_binary[] = {
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static struct uuid_test_data uuid_base = {
+	.str = "0000",
+	.val16 = 0x0000,
+	.type = BT_UUID16,
+	.str128 = "00000000-0000-1000-8000-00805f9b34fb",
+	.binary128 = uuid_base_binary,
+};
+
+static unsigned char uuid_sixteen_binary[] = {
+			0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static struct uuid_test_data uuid_sixteen1 = {
+	.str = "0x1234",
+	.val16 = 0x1234,
+	.type = BT_UUID16,
+	.str128 = "00001234-0000-1000-8000-00805F9B34FB",
+	.binary128 = uuid_sixteen_binary,
+};
+
+static struct uuid_test_data uuid_sixteen2 = {
+	.str = "1234",
+	.val16 = 0x1234,
+	.type = BT_UUID16,
+	.str128 = "00001234-0000-1000-8000-00805F9B34FB",
+	.binary128 = uuid_sixteen_binary,
+};
+
+static unsigned char uuid_32_binary[] = {
+			0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static struct uuid_test_data uuid_32_1 = {
+	.str = "0x12345678",
+	.val32 = 0x12345678,
+	.type = BT_UUID32,
+        .str128 = "12345678-0000-1000-8000-00805F9B34FB",
+	.binary128 = uuid_32_binary,
+};
+
+static struct uuid_test_data uuid_32_2 = {
+	.str = "12345678",
+	.val32 = 0x12345678,
+	.type = BT_UUID32,
+	.str128 = "12345678-0000-1000-8000-00805F9B34FB",
+	.binary128 = uuid_32_binary,
+};
+
+static unsigned char uuid_128_binary[] = {
+			0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+			0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static struct uuid_test_data uuid_128 = {
+	.str = "F0000000-0000-1000-8000-00805f9b34fb",
+	.binary = uuid_128_binary,
+	.type = BT_UUID128,
+	.str128 = "F0000000-0000-1000-8000-00805f9b34fb",
+	.binary128 = uuid_128_binary,
+};
+
+static void test_uuid(gconstpointer data)
+{
+	const struct uuid_test_data *test_data = data;
+	bt_uuid_t uuid;
+
+	g_assert(bt_string_to_uuid(&uuid, test_data->str) == 0);
+	g_assert(uuid.type == test_data->type);
+
+	switch (uuid.type) {
+	case BT_UUID16:
+		g_assert(uuid.value.u16 == test_data->val16);
+		break;
+	case BT_UUID32:
+		g_assert(uuid.value.u32 == test_data->val32);
+		break;
+	case BT_UUID128:
+		/*
+		 * No matter the system type: 128-bit UUID should use
+		 * big-endian (human readable format).
+		 */
+		g_assert(memcmp(&uuid.value.u128, test_data->binary, 16) == 0);
+		break;
+	case BT_UUID_UNSPEC:
+	default:
+		tester_test_passed();
+		return;
+        }
+
+	tester_test_passed();
+}
+
+static void test_str(gconstpointer data)
+{
+	const struct uuid_test_data *test_data = data;
+	const char *str;
+	char buf[128];
+	bt_uuid_t uuid;
+
+	if (g_str_has_prefix(test_data->str, "0x") == TRUE)
+		str = test_data->str + 2;
+	else
+		str = test_data->str;
+
+	g_assert(bt_string_to_uuid(&uuid, test_data->str) == 0);
+
+	bt_uuid_to_string(&uuid, buf, sizeof(buf));
+	g_assert(bt_uuid_strcmp(buf, str) == 0);
+
+	switch (test_data->type) {
+	case BT_UUID16:
+		bt_uuid16_create(&uuid, test_data->val16);
+		break;
+	case BT_UUID32:
+		bt_uuid32_create(&uuid, test_data->val32);
+		break;
+	default:
+		tester_test_passed();
+		return;
+	}
+
+	bt_uuid_to_string(&uuid, buf, sizeof(buf));
+	g_assert(bt_uuid_strcmp(buf, str) == 0);
+	tester_test_passed();
+}
+
+static void test_cmp(gconstpointer data)
+{
+	const struct uuid_test_data *test_data = data;
+	bt_uuid_t uuid1, uuid2;
+
+	g_assert(bt_string_to_uuid(&uuid1, test_data->str) == 0);
+	g_assert(bt_string_to_uuid(&uuid2, test_data->str128) == 0);
+
+	g_assert(bt_uuid_cmp(&uuid1, &uuid2) == 0);
+	tester_test_passed();
+}
+
+static const struct uuid_test_data compress[] = {
+	{
+		.str = "00001234-0000-1000-8000-00805f9b34fb",
+		.type = BT_UUID16,
+		.val16 = 0x1234,
+	}, {
+		.str = "0000FFFF-0000-1000-8000-00805f9b34fb",
+		.type = BT_UUID16,
+		.val16 = 0xFFFF,
+	}, {
+		.str = "0000FFFF-0000-1000-8000-00805F9B34FB",
+		.type = BT_UUID16,
+		.val16 = 0xFFFF,
+	}, {
+		.str = "F0000000-0000-1000-8000-00805f9b34fb",
+		.type = BT_UUID128,
+		.binary = uuid_128_binary,
+	},
+};
+
+static const char *malformed[] = {
+	"0",
+	"01",
+	"012",
+	"xxxx",
+	"xxxxx",
+	"0xxxxx",
+	"0123456",
+	"012g4567",
+	"012345678",
+	"0x234567u9",
+	"01234567890",
+	"00001234-0000-1000-8000-00805F9B34F",
+	"00001234-0000-1000-8000 00805F9B34FB",
+	"00001234-0000-1000-8000-00805F9B34FBC",
+	"00001234-0000-1000-800G-00805F9B34FB",
+	NULL,
+};
+
+static void test_malformed(gconstpointer data)
+{
+	const char *str = data;
+	bt_uuid_t uuid;
+
+	g_assert(bt_string_to_uuid(&uuid, str) != 0);
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	size_t i;
+
+	tester_init(&argc, &argv);
+
+	tester_add("/uuid/base", &uuid_base, NULL, test_uuid, NULL);
+	tester_add("/uuid/base/str", &uuid_base, NULL, test_str, NULL);
+	tester_add("/uuid/base/cmp", &uuid_base, NULL, test_cmp, NULL);
+
+	tester_add("/uuid/sixteen1", &uuid_sixteen1, NULL, test_uuid, NULL);
+	tester_add("/uuid/sixteen1/str", &uuid_sixteen1, NULL, test_str, NULL);
+	tester_add("/uuid/sixteen1/cmp", &uuid_sixteen1, NULL, test_cmp, NULL);
+
+	tester_add("/uuid/sixteen2", &uuid_sixteen2, NULL, test_uuid, NULL);
+	tester_add("/uuid/sixteen2/str", &uuid_sixteen2, NULL, test_str, NULL);
+	tester_add("/uuid/sixteen2/cmp", &uuid_sixteen2, NULL, test_cmp, NULL);
+
+	tester_add("/uuid/thirtytwo1", &uuid_32_1, NULL, test_uuid, NULL);
+	tester_add("/uuid/thirtytwo1/str", &uuid_32_1, NULL, test_str, NULL);
+	tester_add("/uuid/thirtytwo1/cmp", &uuid_32_1, NULL, test_cmp, NULL);
+
+	tester_add("/uuid/thirtytwo2", &uuid_32_2, NULL, test_uuid, NULL);
+	tester_add("/uuid/thritytwo2/str", &uuid_32_2, NULL, test_str, NULL);
+	tester_add("/uuid/thirtytwo2/cmp", &uuid_32_2, NULL, test_cmp, NULL);
+
+	tester_add("/uuid/onetwentyeight", &uuid_128, NULL, test_uuid, NULL);
+	tester_add("/uuid/onetwentyeight/str", &uuid_128, NULL, test_str, NULL);
+	tester_add("/uuid/onetwentyeight/cmp", &uuid_128, NULL, test_cmp, NULL);
+
+	for (i = 0; malformed[i]; i++) {
+		char *testpath;
+
+		testpath = g_strdup_printf("/uuid/malformed/%s", malformed[i]);
+		tester_add(testpath, malformed[i], NULL, test_malformed, NULL);
+		g_free(testpath);
+	}
+
+	for (i = 0; i < (sizeof(compress) / sizeof(compress[0])); i++) {
+		char *testpath;
+
+		testpath = g_strdup_printf("/uuid/compress/%s",
+							compress[i].str);
+		tester_add(testpath, compress + i, NULL, test_uuid, NULL);
+		g_free(testpath);
+	}
+
+	return tester_run();
+}
diff --git a/bluez/repo/unit/util.c b/bluez/repo/unit/util.c
new file mode 100644
index 0000000..8e3115f
--- /dev/null
+++ b/bluez/repo/unit/util.c
@@ -0,0 +1,200 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gobex/gobex.h"
+
+#include "util.h"
+
+GQuark test_error_quark(void)
+{
+	return g_quark_from_static_string("test-error-quark");
+}
+
+static void dump_bytes(const uint8_t *buf, size_t buf_len)
+{
+	size_t i;
+
+	for (i = 0; i < buf_len; i++)
+		g_printerr("%02x ", buf[i]);
+
+	g_printerr("\n");
+}
+
+void dump_bufs(const void *mem1, size_t len1, const void *mem2, size_t len2)
+{
+	g_printerr("\nExpected: ");
+	dump_bytes(mem1, len1);
+	g_printerr("Got:      ");
+	dump_bytes(mem2, len2);
+}
+
+void assert_memequal(const void *mem1, size_t len1,
+						const void *mem2, size_t len2)
+{
+	if (len1 == len2 && memcmp(mem1, mem2, len1) == 0)
+		return;
+
+	dump_bufs(mem1, len1, mem2, len2);
+
+	g_assert(0);
+}
+
+GObex *create_gobex(int fd, GObexTransportType transport_type,
+						gboolean close_on_unref)
+{
+	GIOChannel *io;
+	GObex *obex;
+
+	io = g_io_channel_unix_new(fd);
+	g_assert(io != NULL);
+
+	g_io_channel_set_close_on_unref(io, close_on_unref);
+
+	obex = g_obex_new(io, transport_type, -1, -1);
+	g_io_channel_unref(io);
+
+	return obex;
+}
+
+void create_endpoints(GObex **obex, GIOChannel **io, int sock_type)
+{
+	GObexTransportType transport_type;
+	int sv[2];
+
+	if (socketpair(AF_UNIX, sock_type | SOCK_NONBLOCK, 0, sv) < 0) {
+		g_printerr("socketpair: %s", strerror(errno));
+		abort();
+	}
+
+	if (sock_type == SOCK_STREAM)
+		transport_type = G_OBEX_TRANSPORT_STREAM;
+	else
+		transport_type = G_OBEX_TRANSPORT_PACKET;
+
+	*obex = create_gobex(sv[0], transport_type, TRUE);
+	g_assert(*obex != NULL);
+
+	if (io == NULL) {
+		close(sv[1]);
+		return;
+	}
+
+	*io = g_io_channel_unix_new(sv[1]);
+	g_assert(*io != NULL);
+
+	g_io_channel_set_encoding(*io, NULL, NULL);
+	g_io_channel_set_buffered(*io, FALSE);
+	g_io_channel_set_close_on_unref(*io, TRUE);
+}
+
+gboolean test_timeout(gpointer user_data)
+{
+	struct test_data *d = user_data;
+
+	if (!g_main_loop_is_running(d->mainloop))
+		return FALSE;
+
+	d->err = g_error_new(TEST_ERROR, TEST_ERROR_TIMEOUT, "Timed out");
+
+	g_main_loop_quit(d->mainloop);
+
+	return FALSE;
+}
+
+gboolean test_io_cb(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+	struct test_data *d = user_data;
+	GIOStatus status;
+	gsize bytes_written, rbytes, send_buf_len, expect_len;
+	char buf[65535];
+	const char *send_buf, *expect;
+
+	expect = d->recv[d->count].data;
+	expect_len = d->recv[d->count].len;
+	send_buf = d->send[d->count].data;
+	send_buf_len = d->send[d->count].len;
+
+	d->count++;
+
+	if (!(cond & G_IO_IN))
+		goto send;
+
+	status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+	if (status != G_IO_STATUS_NORMAL) {
+		g_print("io_cb count %u\n", d->count);
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Reading data failed with status %d", status);
+		goto failed;
+	}
+
+	if (rbytes < expect_len) {
+		g_print("io_cb count %u\n", d->count);
+		dump_bufs(expect, expect_len, buf, rbytes);
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Not enough data from socket");
+		goto failed;
+	}
+
+	if (memcmp(buf, expect, expect_len) != 0) {
+		g_print("io_cb count %u\n", d->count);
+		dump_bufs(expect, expect_len, buf, rbytes);
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+					"Received data is not correct");
+		goto failed;
+	}
+
+send:
+	if ((gssize) send_buf_len < 0)
+		goto failed;
+
+	g_io_channel_write_chars(io, send_buf, send_buf_len, &bytes_written,
+									NULL);
+	if (bytes_written != send_buf_len) {
+		g_print("io_cb count %u\n", d->count);
+		g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+						"Unable to write to socket");
+		goto failed;
+	}
+
+	if (d->recv[d->count].len < 0 || (gssize) expect_len < 0)
+		return test_io_cb(io, G_IO_OUT, user_data);
+
+	return TRUE;
+
+failed:
+	g_main_loop_quit(d->mainloop);
+	d->io_completed = TRUE;
+	return FALSE;
+}
diff --git a/bluez/repo/unit/util.h b/bluez/repo/unit/util.h
new file mode 100644
index 0000000..6783c52
--- /dev/null
+++ b/bluez/repo/unit/util.h
@@ -0,0 +1,57 @@
+/*
+ *
+ *  OBEX library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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
+ *
+ */
+
+enum {
+	TEST_ERROR_TIMEOUT,
+	TEST_ERROR_UNEXPECTED,
+};
+
+struct test_buf {
+	const void *data;
+	gssize len;
+};
+
+struct test_data {
+	guint count;
+	GError *err;
+	struct test_buf recv[4];
+	struct test_buf send[4];
+	guint provide_delay;
+	GObex *obex;
+	guint id;
+	gsize total;
+	GMainLoop *mainloop;
+	gboolean io_completed;
+};
+
+#define TEST_ERROR test_error_quark()
+GQuark test_error_quark(void);
+
+void dump_bufs(const void *mem1, size_t len1, const void *mem2, size_t len2);
+void assert_memequal(const void *mem1, size_t len1,
+						const void *mem2, size_t len2);
+
+GObex *create_gobex(int fd, GObexTransportType transport_type,
+						gboolean close_on_unref);
+void create_endpoints(GObex **obex, GIOChannel **io, int sock_type);
+
+gboolean test_io_cb(GIOChannel *io, GIOCondition cond, gpointer user_data);
+gboolean test_timeout(gpointer user_data);